summaryrefslogtreecommitdiff
path: root/drivers/media/video
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video')
-rw-r--r--drivers/media/video/Kconfig1224
-rw-r--r--drivers/media/video/Makefile220
-rw-r--r--drivers/media/video/adp1653.c488
-rw-r--r--drivers/media/video/adv7170.c410
-rw-r--r--drivers/media/video/adv7175.c460
-rw-r--r--drivers/media/video/adv7180.c451
-rw-r--r--drivers/media/video/adv7183.c699
-rw-r--r--drivers/media/video/adv7183_regs.h107
-rw-r--r--drivers/media/video/adv7343.c478
-rw-r--r--drivers/media/video/adv7343_regs.h181
-rw-r--r--drivers/media/video/ak881x.c359
-rw-r--r--drivers/media/video/aptina-pll.c174
-rw-r--r--drivers/media/video/aptina-pll.h56
-rw-r--r--drivers/media/video/arv.c880
-rw-r--r--drivers/media/video/as3645a.c888
-rw-r--r--drivers/media/video/atmel-isi.c1109
-rw-r--r--drivers/media/video/au0828/Kconfig17
-rw-r--r--drivers/media/video/au0828/Makefile9
-rw-r--r--drivers/media/video/au0828/au0828-cards.c337
-rw-r--r--drivers/media/video/au0828/au0828-cards.h27
-rw-r--r--drivers/media/video/au0828/au0828-core.c295
-rw-r--r--drivers/media/video/au0828/au0828-dvb.c439
-rw-r--r--drivers/media/video/au0828/au0828-i2c.c390
-rw-r--r--drivers/media/video/au0828/au0828-reg.h65
-rw-r--r--drivers/media/video/au0828/au0828-vbi.c138
-rw-r--r--drivers/media/video/au0828/au0828-video.c1993
-rw-r--r--drivers/media/video/au0828/au0828.h302
-rw-r--r--drivers/media/video/blackfin/Kconfig10
-rw-r--r--drivers/media/video/blackfin/Makefile2
-rw-r--r--drivers/media/video/blackfin/bfin_capture.c1059
-rw-r--r--drivers/media/video/blackfin/ppi.c271
-rw-r--r--drivers/media/video/bt819.c517
-rw-r--r--drivers/media/video/bt856.c273
-rw-r--r--drivers/media/video/bt866.c243
-rw-r--r--drivers/media/video/bt8xx/Kconfig27
-rw-r--r--drivers/media/video/bt8xx/Makefile13
-rw-r--r--drivers/media/video/bt8xx/bt848.h369
-rw-r--r--drivers/media/video/bt8xx/bttv-audio-hook.c382
-rw-r--r--drivers/media/video/bt8xx/bttv-audio-hook.h23
-rw-r--r--drivers/media/video/bt8xx/bttv-cards.c4958
-rw-r--r--drivers/media/video/bt8xx/bttv-driver.c4631
-rw-r--r--drivers/media/video/bt8xx/bttv-gpio.c189
-rw-r--r--drivers/media/video/bt8xx/bttv-i2c.c397
-rw-r--r--drivers/media/video/bt8xx/bttv-if.c121
-rw-r--r--drivers/media/video/bt8xx/bttv-input.c589
-rw-r--r--drivers/media/video/bt8xx/bttv-risc.c909
-rw-r--r--drivers/media/video/bt8xx/bttv-vbi.c459
-rw-r--r--drivers/media/video/bt8xx/bttv.h377
-rw-r--r--drivers/media/video/bt8xx/bttvp.h534
-rw-r--r--drivers/media/video/btcx-risc.c260
-rw-r--r--drivers/media/video/btcx-risc.h34
-rw-r--r--drivers/media/video/bw-qcam.c1094
-rw-r--r--drivers/media/video/c-qcam.c891
-rwxr-xr-xdrivers/media/video/cmoscam.c99
-rwxr-xr-xdrivers/media/video/cmoscam.h77
-rw-r--r--drivers/media/video/cpia2/Kconfig9
-rw-r--r--drivers/media/video/cpia2/Makefile3
-rw-r--r--drivers/media/video/cpia2/cpia2.h489
-rw-r--r--drivers/media/video/cpia2/cpia2_core.c2493
-rw-r--r--drivers/media/video/cpia2/cpia2_registers.h476
-rw-r--r--drivers/media/video/cpia2/cpia2_usb.c915
-rw-r--r--drivers/media/video/cpia2/cpia2_v4l.c1578
-rw-r--r--drivers/media/video/cpia2/cpia2dev.h50
-rw-r--r--drivers/media/video/cs5345.c252
-rw-r--r--drivers/media/video/cs53l32a.c251
-rw-r--r--drivers/media/video/cs8420.h50
-rw-r--r--drivers/media/video/cx18/Kconfig35
-rw-r--r--drivers/media/video/cx18/Makefile13
-rw-r--r--drivers/media/video/cx18/cx18-alsa-main.c294
-rw-r--r--drivers/media/video/cx18/cx18-alsa-mixer.c175
-rw-r--r--drivers/media/video/cx18/cx18-alsa-mixer.h23
-rw-r--r--drivers/media/video/cx18/cx18-alsa-pcm.c360
-rw-r--r--drivers/media/video/cx18/cx18-alsa-pcm.h27
-rw-r--r--drivers/media/video/cx18/cx18-alsa.h75
-rw-r--r--drivers/media/video/cx18/cx18-audio.c92
-rw-r--r--drivers/media/video/cx18/cx18-audio.h24
-rw-r--r--drivers/media/video/cx18/cx18-av-audio.c471
-rw-r--r--drivers/media/video/cx18/cx18-av-core.c1401
-rw-r--r--drivers/media/video/cx18/cx18-av-core.h391
-rw-r--r--drivers/media/video/cx18/cx18-av-firmware.c223
-rw-r--r--drivers/media/video/cx18/cx18-av-vbi.c311
-rw-r--r--drivers/media/video/cx18/cx18-cards.c638
-rw-r--r--drivers/media/video/cx18/cx18-cards.h157
-rw-r--r--drivers/media/video/cx18/cx18-controls.c131
-rw-r--r--drivers/media/video/cx18/cx18-controls.h24
-rw-r--r--drivers/media/video/cx18/cx18-driver.c1359
-rw-r--r--drivers/media/video/cx18/cx18-driver.h730
-rw-r--r--drivers/media/video/cx18/cx18-dvb.c605
-rw-r--r--drivers/media/video/cx18/cx18-dvb.h25
-rw-r--r--drivers/media/video/cx18/cx18-fileops.c881
-rw-r--r--drivers/media/video/cx18/cx18-fileops.h41
-rw-r--r--drivers/media/video/cx18/cx18-firmware.c448
-rw-r--r--drivers/media/video/cx18/cx18-firmware.h25
-rw-r--r--drivers/media/video/cx18/cx18-gpio.c347
-rw-r--r--drivers/media/video/cx18/cx18-gpio.h34
-rw-r--r--drivers/media/video/cx18/cx18-i2c.c330
-rw-r--r--drivers/media/video/cx18/cx18-i2c.h29
-rw-r--r--drivers/media/video/cx18/cx18-io.c97
-rw-r--r--drivers/media/video/cx18/cx18-io.h191
-rw-r--r--drivers/media/video/cx18/cx18-ioctl.c1212
-rw-r--r--drivers/media/video/cx18/cx18-ioctl.h33
-rw-r--r--drivers/media/video/cx18/cx18-irq.c81
-rw-r--r--drivers/media/video/cx18/cx18-irq.h35
-rw-r--r--drivers/media/video/cx18/cx18-mailbox.c869
-rw-r--r--drivers/media/video/cx18/cx18-mailbox.h95
-rw-r--r--drivers/media/video/cx18/cx18-queue.c443
-rw-r--r--drivers/media/video/cx18/cx18-queue.h98
-rw-r--r--drivers/media/video/cx18/cx18-scb.c122
-rw-r--r--drivers/media/video/cx18/cx18-scb.h280
-rw-r--r--drivers/media/video/cx18/cx18-streams.c1063
-rw-r--r--drivers/media/video/cx18/cx18-streams.h62
-rw-r--r--drivers/media/video/cx18/cx18-vbi.c277
-rw-r--r--drivers/media/video/cx18/cx18-vbi.h26
-rw-r--r--drivers/media/video/cx18/cx18-version.h28
-rw-r--r--drivers/media/video/cx18/cx18-video.c32
-rw-r--r--drivers/media/video/cx18/cx18-video.h22
-rw-r--r--drivers/media/video/cx18/cx23418.h492
-rw-r--r--drivers/media/video/cx231xx/Kconfig51
-rw-r--r--drivers/media/video/cx231xx/Makefile16
-rw-r--r--drivers/media/video/cx231xx/cx231xx-417.c2191
-rw-r--r--drivers/media/video/cx231xx/cx231xx-audio.c776
-rw-r--r--drivers/media/video/cx231xx/cx231xx-avcore.c3123
-rw-r--r--drivers/media/video/cx231xx/cx231xx-cards.c1381
-rw-r--r--drivers/media/video/cx231xx/cx231xx-conf-reg.h495
-rw-r--r--drivers/media/video/cx231xx/cx231xx-core.c1742
-rw-r--r--drivers/media/video/cx231xx/cx231xx-dif.h3178
-rw-r--r--drivers/media/video/cx231xx/cx231xx-dvb.c796
-rw-r--r--drivers/media/video/cx231xx/cx231xx-i2c.c536
-rw-r--r--drivers/media/video/cx231xx/cx231xx-input.c119
-rw-r--r--drivers/media/video/cx231xx/cx231xx-pcb-cfg.c795
-rw-r--r--drivers/media/video/cx231xx/cx231xx-pcb-cfg.h231
-rw-r--r--drivers/media/video/cx231xx/cx231xx-reg.h1564
-rw-r--r--drivers/media/video/cx231xx/cx231xx-vbi.c714
-rw-r--r--drivers/media/video/cx231xx/cx231xx-vbi.h65
-rw-r--r--drivers/media/video/cx231xx/cx231xx-video.c2657
-rw-r--r--drivers/media/video/cx231xx/cx231xx.h1014
-rw-r--r--drivers/media/video/cx2341x.c1726
-rw-r--r--drivers/media/video/cx23885/Kconfig46
-rw-r--r--drivers/media/video/cx23885/Makefile15
-rw-r--r--drivers/media/video/cx23885/altera-ci.c837
-rw-r--r--drivers/media/video/cx23885/altera-ci.h100
-rw-r--r--drivers/media/video/cx23885/cimax2.c536
-rw-r--r--drivers/media/video/cx23885/cimax2.h47
-rw-r--r--drivers/media/video/cx23885/cx23885-417.c1788
-rw-r--r--drivers/media/video/cx23885/cx23885-alsa.c535
-rw-r--r--drivers/media/video/cx23885/cx23885-av.c35
-rw-r--r--drivers/media/video/cx23885/cx23885-av.h27
-rw-r--r--drivers/media/video/cx23885/cx23885-cards.c1602
-rw-r--r--drivers/media/video/cx23885/cx23885-core.c2227
-rw-r--r--drivers/media/video/cx23885/cx23885-dvb.c1344
-rw-r--r--drivers/media/video/cx23885/cx23885-f300.c177
-rw-r--r--drivers/media/video/cx23885/cx23885-f300.h2
-rw-r--r--drivers/media/video/cx23885/cx23885-i2c.c402
-rw-r--r--drivers/media/video/cx23885/cx23885-input.c356
-rw-r--r--drivers/media/video/cx23885/cx23885-input.h30
-rw-r--r--drivers/media/video/cx23885/cx23885-ioctl.c208
-rw-r--r--drivers/media/video/cx23885/cx23885-ioctl.h39
-rw-r--r--drivers/media/video/cx23885/cx23885-ir.c117
-rw-r--r--drivers/media/video/cx23885/cx23885-ir.h31
-rw-r--r--drivers/media/video/cx23885/cx23885-reg.h452
-rw-r--r--drivers/media/video/cx23885/cx23885-vbi.c295
-rw-r--r--drivers/media/video/cx23885/cx23885-video.c1919
-rw-r--r--drivers/media/video/cx23885/cx23885.h653
-rw-r--r--drivers/media/video/cx23885/cx23888-ir.c1273
-rw-r--r--drivers/media/video/cx23885/cx23888-ir.h28
-rw-r--r--drivers/media/video/cx23885/netup-eeprom.c107
-rw-r--r--drivers/media/video/cx23885/netup-eeprom.h42
-rw-r--r--drivers/media/video/cx23885/netup-init.c125
-rw-r--r--drivers/media/video/cx23885/netup-init.h25
-rw-r--r--drivers/media/video/cx25821/Kconfig34
-rw-r--r--drivers/media/video/cx25821/Makefile13
-rw-r--r--drivers/media/video/cx25821/cx25821-alsa.c786
-rw-r--r--drivers/media/video/cx25821/cx25821-audio-upstream.c779
-rw-r--r--drivers/media/video/cx25821/cx25821-audio-upstream.h62
-rw-r--r--drivers/media/video/cx25821/cx25821-audio.h62
-rw-r--r--drivers/media/video/cx25821/cx25821-biffuncs.h45
-rw-r--r--drivers/media/video/cx25821/cx25821-cards.c72
-rw-r--r--drivers/media/video/cx25821/cx25821-core.c1515
-rw-r--r--drivers/media/video/cx25821/cx25821-gpio.c98
-rw-r--r--drivers/media/video/cx25821/cx25821-i2c.c423
-rw-r--r--drivers/media/video/cx25821/cx25821-medusa-defines.h42
-rw-r--r--drivers/media/video/cx25821/cx25821-medusa-reg.h455
-rw-r--r--drivers/media/video/cx25821/cx25821-medusa-video.c790
-rw-r--r--drivers/media/video/cx25821/cx25821-medusa-video.h49
-rw-r--r--drivers/media/video/cx25821/cx25821-reg.h1592
-rw-r--r--drivers/media/video/cx25821/cx25821-sram.h261
-rw-r--r--drivers/media/video/cx25821/cx25821-video-upstream-ch2.c803
-rw-r--r--drivers/media/video/cx25821/cx25821-video-upstream-ch2.h138
-rw-r--r--drivers/media/video/cx25821/cx25821-video-upstream.c857
-rw-r--r--drivers/media/video/cx25821/cx25821-video-upstream.h139
-rw-r--r--drivers/media/video/cx25821/cx25821-video.c2011
-rw-r--r--drivers/media/video/cx25821/cx25821-video.h188
-rw-r--r--drivers/media/video/cx25821/cx25821.h617
-rw-r--r--drivers/media/video/cx25840/Kconfig8
-rw-r--r--drivers/media/video/cx25840/Makefile6
-rw-r--r--drivers/media/video/cx25840/cx25840-audio.c571
-rw-r--r--drivers/media/video/cx25840/cx25840-core.c5304
-rw-r--r--drivers/media/video/cx25840/cx25840-core.h137
-rw-r--r--drivers/media/video/cx25840/cx25840-firmware.c166
-rw-r--r--drivers/media/video/cx25840/cx25840-ir.c1285
-rw-r--r--drivers/media/video/cx25840/cx25840-vbi.c256
-rw-r--r--drivers/media/video/cx88/Kconfig86
-rw-r--r--drivers/media/video/cx88/Makefile16
-rw-r--r--drivers/media/video/cx88/cx88-alsa.c992
-rw-r--r--drivers/media/video/cx88/cx88-blackbird.c1385
-rw-r--r--drivers/media/video/cx88/cx88-cards.c3791
-rw-r--r--drivers/media/video/cx88/cx88-core.c1126
-rw-r--r--drivers/media/video/cx88/cx88-dsp.c322
-rw-r--r--drivers/media/video/cx88/cx88-dvb.c1778
-rw-r--r--drivers/media/video/cx88/cx88-i2c.c184
-rw-r--r--drivers/media/video/cx88/cx88-input.c635
-rw-r--r--drivers/media/video/cx88/cx88-mpeg.c929
-rw-r--r--drivers/media/video/cx88/cx88-reg.h836
-rw-r--r--drivers/media/video/cx88/cx88-tvaudio.c1059
-rw-r--r--drivers/media/video/cx88/cx88-vbi.c245
-rw-r--r--drivers/media/video/cx88/cx88-video.c2194
-rw-r--r--drivers/media/video/cx88/cx88-vp3054-i2c.c159
-rw-r--r--drivers/media/video/cx88/cx88-vp3054-i2c.h41
-rw-r--r--drivers/media/video/cx88/cx88.h732
-rw-r--r--drivers/media/video/davinci/Kconfig116
-rw-r--r--drivers/media/video/davinci/Makefile20
-rw-r--r--drivers/media/video/davinci/ccdc_hw_device.h110
-rw-r--r--drivers/media/video/davinci/dm355_ccdc.c1072
-rw-r--r--drivers/media/video/davinci/dm355_ccdc_regs.h310
-rw-r--r--drivers/media/video/davinci/dm644x_ccdc.c1081
-rw-r--r--drivers/media/video/davinci/dm644x_ccdc_regs.h153
-rw-r--r--drivers/media/video/davinci/isif.c1162
-rw-r--r--drivers/media/video/davinci/isif_regs.h269
-rw-r--r--drivers/media/video/davinci/vpbe.c886
-rw-r--r--drivers/media/video/davinci/vpbe_display.c1824
-rw-r--r--drivers/media/video/davinci/vpbe_osd.c1605
-rw-r--r--drivers/media/video/davinci/vpbe_osd_regs.h364
-rw-r--r--drivers/media/video/davinci/vpbe_venc.c706
-rw-r--r--drivers/media/video/davinci/vpbe_venc_regs.h177
-rw-r--r--drivers/media/video/davinci/vpfe_capture.c2079
-rw-r--r--drivers/media/video/davinci/vpif.c473
-rw-r--r--drivers/media/video/davinci/vpif.h643
-rw-r--r--drivers/media/video/davinci/vpif_capture.c2412
-rw-r--r--drivers/media/video/davinci/vpif_capture.h162
-rw-r--r--drivers/media/video/davinci/vpif_display.c1912
-rw-r--r--drivers/media/video/davinci/vpif_display.h172
-rw-r--r--drivers/media/video/davinci/vpss.c482
-rw-r--r--drivers/media/video/em28xx/Kconfig58
-rw-r--r--drivers/media/video/em28xx/Makefile16
-rw-r--r--drivers/media/video/em28xx/em28xx-audio.c748
-rw-r--r--drivers/media/video/em28xx/em28xx-cards.c3490
-rw-r--r--drivers/media/video/em28xx/em28xx-core.c1237
-rw-r--r--drivers/media/video/em28xx/em28xx-dvb.c1109
-rw-r--r--drivers/media/video/em28xx/em28xx-i2c.c570
-rw-r--r--drivers/media/video/em28xx/em28xx-input.c563
-rw-r--r--drivers/media/video/em28xx/em28xx-reg.h275
-rw-r--r--drivers/media/video/em28xx/em28xx-vbi.c145
-rw-r--r--drivers/media/video/em28xx/em28xx-video.c2605
-rw-r--r--drivers/media/video/em28xx/em28xx.h865
-rw-r--r--drivers/media/video/et61x251/Kconfig18
-rw-r--r--drivers/media/video/et61x251/Makefile4
-rw-r--r--drivers/media/video/et61x251/et61x251.h213
-rw-r--r--drivers/media/video/et61x251/et61x251_core.c2683
-rw-r--r--drivers/media/video/et61x251/et61x251_sensor.h108
-rw-r--r--drivers/media/video/et61x251/et61x251_tas5130d1b.c143
-rw-r--r--drivers/media/video/fsl-viu.c1669
-rw-r--r--drivers/media/video/gspca/Kconfig425
-rw-r--r--drivers/media/video/gspca/Makefile93
-rw-r--r--drivers/media/video/gspca/autogain_functions.h179
-rw-r--r--drivers/media/video/gspca/benq.c294
-rw-r--r--drivers/media/video/gspca/conex.c1070
-rw-r--r--drivers/media/video/gspca/cpia1.c2135
-rw-r--r--drivers/media/video/gspca/etoms.c898
-rw-r--r--drivers/media/video/gspca/finepix.c293
-rw-r--r--drivers/media/video/gspca/gl860/Kconfig8
-rw-r--r--drivers/media/video/gspca/gl860/Makefile10
-rw-r--r--drivers/media/video/gspca/gl860/gl860-mi1320.c536
-rw-r--r--drivers/media/video/gspca/gl860/gl860-mi2020.c733
-rw-r--r--drivers/media/video/gspca/gl860/gl860-ov2640.c489
-rw-r--r--drivers/media/video/gspca/gl860/gl860-ov9655.c336
-rw-r--r--drivers/media/video/gspca/gl860/gl860.c721
-rw-r--r--drivers/media/video/gspca/gl860/gl860.h105
-rw-r--r--drivers/media/video/gspca/gspca.c2613
-rw-r--r--drivers/media/video/gspca/gspca.h237
-rw-r--r--drivers/media/video/gspca/jeilinj.c585
-rw-r--r--drivers/media/video/gspca/jl2005bcd.c554
-rw-r--r--drivers/media/video/gspca/jpeg.h168
-rw-r--r--drivers/media/video/gspca/kinect.c416
-rw-r--r--drivers/media/video/gspca/konica.c634
-rw-r--r--drivers/media/video/gspca/m5602/Kconfig11
-rw-r--r--drivers/media/video/gspca/m5602/Makefile11
-rw-r--r--drivers/media/video/gspca/m5602/m5602_bridge.h163
-rw-r--r--drivers/media/video/gspca/m5602/m5602_core.c423
-rw-r--r--drivers/media/video/gspca/m5602/m5602_mt9m111.c647
-rw-r--r--drivers/media/video/gspca/m5602/m5602_mt9m111.h271
-rw-r--r--drivers/media/video/gspca/m5602/m5602_ov7660.c488
-rw-r--r--drivers/media/video/gspca/m5602/m5602_ov7660.h260
-rw-r--r--drivers/media/video/gspca/m5602/m5602_ov9650.c881
-rw-r--r--drivers/media/video/gspca/m5602/m5602_ov9650.h307
-rw-r--r--drivers/media/video/gspca/m5602/m5602_po1030.c763
-rw-r--r--drivers/media/video/gspca/m5602/m5602_po1030.h272
-rw-r--r--drivers/media/video/gspca/m5602/m5602_s5k4aa.c726
-rw-r--r--drivers/media/video/gspca/m5602/m5602_s5k4aa.h283
-rw-r--r--drivers/media/video/gspca/m5602/m5602_s5k83a.c605
-rw-r--r--drivers/media/video/gspca/m5602/m5602_s5k83a.h193
-rw-r--r--drivers/media/video/gspca/m5602/m5602_sensor.h70
-rw-r--r--drivers/media/video/gspca/mars.c519
-rw-r--r--drivers/media/video/gspca/mr97310a.c1262
-rw-r--r--drivers/media/video/gspca/nw80x.c2137
-rw-r--r--drivers/media/video/gspca/ov519.c5061
-rw-r--r--drivers/media/video/gspca/ov534.c1536
-rw-r--r--drivers/media/video/gspca/ov534_9.c1605
-rw-r--r--drivers/media/video/gspca/pac207.c572
-rw-r--r--drivers/media/video/gspca/pac7302.c971
-rw-r--r--drivers/media/video/gspca/pac7311.c871
-rw-r--r--drivers/media/video/gspca/pac_common.h134
-rw-r--r--drivers/media/video/gspca/se401.c777
-rw-r--r--drivers/media/video/gspca/se401.h90
-rw-r--r--drivers/media/video/gspca/sn9c2028.c740
-rw-r--r--drivers/media/video/gspca/sn9c2028.h51
-rw-r--r--drivers/media/video/gspca/sn9c20x.c2463
-rw-r--r--drivers/media/video/gspca/sonixb.c1533
-rw-r--r--drivers/media/video/gspca/sonixj.c3208
-rw-r--r--drivers/media/video/gspca/spca1528.c593
-rw-r--r--drivers/media/video/gspca/spca500.c1095
-rw-r--r--drivers/media/video/gspca/spca501.c2191
-rw-r--r--drivers/media/video/gspca/spca505.c818
-rw-r--r--drivers/media/video/gspca/spca506.c717
-rw-r--r--drivers/media/video/gspca/spca508.c1547
-rw-r--r--drivers/media/video/gspca/spca561.c1109
-rw-r--r--drivers/media/video/gspca/sq905.c435
-rw-r--r--drivers/media/video/gspca/sq905c.c342
-rw-r--r--drivers/media/video/gspca/sq930x.c1200
-rw-r--r--drivers/media/video/gspca/stk014.c522
-rw-r--r--drivers/media/video/gspca/stv0680.c358
-rw-r--r--drivers/media/video/gspca/stv06xx/Kconfig9
-rw-r--r--drivers/media/video/gspca/stv06xx/Makefile10
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx.c621
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx.h119
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c613
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h207
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c575
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h152
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_sensor.h87
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_st6422.c403
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_st6422.h52
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c374
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h259
-rw-r--r--drivers/media/video/gspca/sunplus.c1214
-rw-r--r--drivers/media/video/gspca/t613.c1466
-rw-r--r--drivers/media/video/gspca/topro.c4978
-rw-r--r--drivers/media/video/gspca/tv8532.c421
-rw-r--r--drivers/media/video/gspca/vc032x.c4233
-rw-r--r--drivers/media/video/gspca/vicam.c372
-rw-r--r--drivers/media/video/gspca/w996Xcf.c560
-rw-r--r--drivers/media/video/gspca/xirlink_cit.c3326
-rw-r--r--drivers/media/video/gspca/zc3xx-reg.h251
-rw-r--r--drivers/media/video/gspca/zc3xx.c7186
-rw-r--r--drivers/media/video/hdpvr/Kconfig10
-rw-r--r--drivers/media/video/hdpvr/Makefile7
-rw-r--r--drivers/media/video/hdpvr/hdpvr-control.c198
-rw-r--r--drivers/media/video/hdpvr/hdpvr-core.c472
-rw-r--r--drivers/media/video/hdpvr/hdpvr-i2c.c231
-rw-r--r--drivers/media/video/hdpvr/hdpvr-video.c1289
-rw-r--r--drivers/media/video/hdpvr/hdpvr.h317
-rw-r--r--drivers/media/video/hexium_gemini.c533
-rw-r--r--drivers/media/video/hexium_orion.c502
-rw-r--r--drivers/media/video/ibmmpeg2.h94
-rw-r--r--drivers/media/video/imx074.c475
-rw-r--r--drivers/media/video/indycam.c390
-rw-r--r--drivers/media/video/indycam.h93
-rw-r--r--drivers/media/video/ir-kbd-i2c.c489
-rw-r--r--drivers/media/video/ivtv/Kconfig45
-rw-r--r--drivers/media/video/ivtv/Makefile14
-rw-r--r--drivers/media/video/ivtv/ivtv-cards.c1370
-rw-r--r--drivers/media/video/ivtv/ivtv-cards.h309
-rw-r--r--drivers/media/video/ivtv/ivtv-controls.c163
-rw-r--r--drivers/media/video/ivtv/ivtv-controls.h28
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.c1498
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.h839
-rw-r--r--drivers/media/video/ivtv/ivtv-fileops.c1036
-rw-r--r--drivers/media/video/ivtv/ivtv-fileops.h44
-rw-r--r--drivers/media/video/ivtv/ivtv-firmware.c398
-rw-r--r--drivers/media/video/ivtv/ivtv-firmware.h31
-rw-r--r--drivers/media/video/ivtv/ivtv-gpio.c374
-rw-r--r--drivers/media/video/ivtv/ivtv-gpio.h29
-rw-r--r--drivers/media/video/ivtv/ivtv-i2c.c760
-rw-r--r--drivers/media/video/ivtv/ivtv-i2c.h32
-rw-r--r--drivers/media/video/ivtv/ivtv-ioctl.c1913
-rw-r--r--drivers/media/video/ivtv/ivtv-ioctl.h36
-rw-r--r--drivers/media/video/ivtv/ivtv-irq.c1038
-rw-r--r--drivers/media/video/ivtv/ivtv-irq.h53
-rw-r--r--drivers/media/video/ivtv/ivtv-mailbox.c387
-rw-r--r--drivers/media/video/ivtv/ivtv-mailbox.h35
-rw-r--r--drivers/media/video/ivtv/ivtv-queue.c297
-rw-r--r--drivers/media/video/ivtv/ivtv-queue.h96
-rw-r--r--drivers/media/video/ivtv/ivtv-routing.c119
-rw-r--r--drivers/media/video/ivtv/ivtv-routing.h27
-rw-r--r--drivers/media/video/ivtv/ivtv-streams.c1014
-rw-r--r--drivers/media/video/ivtv/ivtv-streams.h37
-rw-r--r--drivers/media/video/ivtv/ivtv-udma.c234
-rw-r--r--drivers/media/video/ivtv/ivtv-udma.h48
-rw-r--r--drivers/media/video/ivtv/ivtv-vbi.c549
-rw-r--r--drivers/media/video/ivtv/ivtv-vbi.h34
-rw-r--r--drivers/media/video/ivtv/ivtv-version.h26
-rw-r--r--drivers/media/video/ivtv/ivtv-yuv.c1296
-rw-r--r--drivers/media/video/ivtv/ivtv-yuv.h44
-rw-r--r--drivers/media/video/ivtv/ivtvfb.c1315
-rw-r--r--drivers/media/video/ks0127.c724
-rw-r--r--drivers/media/video/ks0127.h51
-rw-r--r--drivers/media/video/m52790.c216
-rw-r--r--drivers/media/video/m5mols/Kconfig5
-rw-r--r--drivers/media/video/m5mols/Makefile3
-rw-r--r--drivers/media/video/m5mols/m5mols.h299
-rw-r--r--drivers/media/video/m5mols/m5mols_capture.c160
-rw-r--r--drivers/media/video/m5mols/m5mols_controls.c299
-rw-r--r--drivers/media/video/m5mols/m5mols_core.c1065
-rw-r--r--drivers/media/video/m5mols/m5mols_reg.h361
-rw-r--r--drivers/media/video/marvell-ccic/Kconfig23
-rw-r--r--drivers/media/video/marvell-ccic/Makefile6
-rw-r--r--drivers/media/video/marvell-ccic/cafe-driver.c654
-rw-r--r--drivers/media/video/marvell-ccic/mcam-core.c1879
-rw-r--r--drivers/media/video/marvell-ccic/mcam-core.h322
-rw-r--r--drivers/media/video/marvell-ccic/mmp-driver.c380
-rw-r--r--drivers/media/video/mem2mem_testdev.c1050
-rw-r--r--drivers/media/video/meye.c1964
-rw-r--r--drivers/media/video/meye.h324
-rw-r--r--drivers/media/video/msp3400-driver.c892
-rw-r--r--drivers/media/video/msp3400-driver.h137
-rw-r--r--drivers/media/video/msp3400-kthreads.c1165
-rw-r--r--drivers/media/video/mt9m001.c737
-rw-r--r--drivers/media/video/mt9m032.c869
-rw-r--r--drivers/media/video/mt9m111.c1015
-rw-r--r--drivers/media/video/mt9p031.c939
-rw-r--r--drivers/media/video/mt9t001.c824
-rw-r--r--drivers/media/video/mt9t031.c857
-rw-r--r--drivers/media/video/mt9t112.c1124
-rw-r--r--drivers/media/video/mt9v011.c712
-rw-r--r--drivers/media/video/mt9v022.c879
-rw-r--r--drivers/media/video/mt9v032.c763
-rw-r--r--drivers/media/video/mx1_camera.c898
-rw-r--r--drivers/media/video/mx2_camera.c1856
-rw-r--r--drivers/media/video/mx2_emmaprp.c1008
-rw-r--r--drivers/media/video/mx3_camera.c1295
-rw-r--r--drivers/media/video/mxb.c879
-rw-r--r--drivers/media/video/mxb.h42
-rw-r--r--drivers/media/video/noon010pc30.c851
-rw-r--r--drivers/media/video/omap/Kconfig14
-rw-r--r--drivers/media/video/omap/Makefile8
-rw-r--r--drivers/media/video/omap/omap_vout.c2289
-rw-r--r--drivers/media/video/omap/omap_vout_vrfb.c390
-rw-r--r--drivers/media/video/omap/omap_vout_vrfb.h40
-rw-r--r--drivers/media/video/omap/omap_voutdef.h225
-rw-r--r--drivers/media/video/omap/omap_voutlib.c339
-rw-r--r--drivers/media/video/omap/omap_voutlib.h36
-rw-r--r--drivers/media/video/omap1_camera.c1725
-rw-r--r--drivers/media/video/omap24xxcam-dma.c601
-rw-r--r--drivers/media/video/omap24xxcam.c1882
-rw-r--r--drivers/media/video/omap24xxcam.h593
-rw-r--r--drivers/media/video/omap3isp/Makefile11
-rw-r--r--drivers/media/video/omap3isp/cfa_coef_table.h61
-rw-r--r--drivers/media/video/omap3isp/gamma_table.h90
-rw-r--r--drivers/media/video/omap3isp/isp.c2235
-rw-r--r--drivers/media/video/omap3isp/isp.h355
-rw-r--r--drivers/media/video/omap3isp/ispccdc.c2315
-rw-r--r--drivers/media/video/omap3isp/ispccdc.h217
-rw-r--r--drivers/media/video/omap3isp/ispccp2.c1193
-rw-r--r--drivers/media/video/omap3isp/ispccp2.h98
-rw-r--r--drivers/media/video/omap3isp/ispcsi2.c1318
-rw-r--r--drivers/media/video/omap3isp/ispcsi2.h166
-rw-r--r--drivers/media/video/omap3isp/ispcsiphy.c247
-rw-r--r--drivers/media/video/omap3isp/ispcsiphy.h74
-rw-r--r--drivers/media/video/omap3isp/isph3a.h117
-rw-r--r--drivers/media/video/omap3isp/isph3a_aewb.c374
-rw-r--r--drivers/media/video/omap3isp/isph3a_af.c429
-rw-r--r--drivers/media/video/omap3isp/isphist.c520
-rw-r--r--drivers/media/video/omap3isp/isphist.h40
-rw-r--r--drivers/media/video/omap3isp/isppreview.c2227
-rw-r--r--drivers/media/video/omap3isp/isppreview.h207
-rw-r--r--drivers/media/video/omap3isp/ispqueue.c1157
-rw-r--r--drivers/media/video/omap3isp/ispqueue.h187
-rw-r--r--drivers/media/video/omap3isp/ispreg.h1586
-rw-r--r--drivers/media/video/omap3isp/ispresizer.c1737
-rw-r--r--drivers/media/video/omap3isp/ispresizer.h146
-rw-r--r--drivers/media/video/omap3isp/ispstat.c1102
-rw-r--r--drivers/media/video/omap3isp/ispstat.h169
-rw-r--r--drivers/media/video/omap3isp/ispvideo.c1361
-rw-r--r--drivers/media/video/omap3isp/ispvideo.h209
-rw-r--r--drivers/media/video/omap3isp/luma_enhance_table.h42
-rw-r--r--drivers/media/video/omap3isp/noise_filter_table.h30
-rw-r--r--drivers/media/video/ov2640.c1110
-rw-r--r--drivers/media/video/ov5642.c1075
-rw-r--r--drivers/media/video/ov6650.c1053
-rw-r--r--drivers/media/video/ov7670.c1586
-rw-r--r--drivers/media/video/ov772x.c1130
-rw-r--r--drivers/media/video/ov9640.c745
-rw-r--r--drivers/media/video/ov9640.h207
-rw-r--r--drivers/media/video/ov9740.c1005
-rw-r--r--drivers/media/video/pms.c1144
-rw-r--r--drivers/media/video/pvrusb2/Kconfig66
-rw-r--r--drivers/media/video/pvrusb2/Makefile22
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-audio.c96
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-audio.h37
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-context.c431
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-context.h94
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c95
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-cs53l32a.h48
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-ctrl.c609
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-ctrl.h122
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c166
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h52
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-debug.h69
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-debugifc.c335
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-debugifc.h52
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-devattr.c569
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-devattr.h199
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-dvb.c435
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-dvb.h41
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-eeprom.c164
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-eeprom.h39
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-encoder.c552
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-encoder.h42
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h72
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h408
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.c5331
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.h367
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-i2c-core.c698
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-i2c-core.h40
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-io.c695
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-io.h102
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-ioread.c512
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-ioread.h48
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-main.c182
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-std.c411
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-std.h59
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-sysfs.c861
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-sysfs.h46
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-util.h62
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-v4l2.c1364
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-v4l2.h39
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-video-v4l.c114
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-video-v4l.h48
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-wm8775.c70
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-wm8775.h52
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2.h42
-rw-r--r--drivers/media/video/pwc/Kconfig48
-rw-r--r--drivers/media/video/pwc/Makefile4
-rw-r--r--drivers/media/video/pwc/philips.txt236
-rw-r--r--drivers/media/video/pwc/pwc-ctrl.c553
-rw-r--r--drivers/media/video/pwc/pwc-dec1.c32
-rw-r--r--drivers/media/video/pwc/pwc-dec1.h39
-rw-r--r--drivers/media/video/pwc/pwc-dec23.c691
-rw-r--r--drivers/media/video/pwc/pwc-dec23.h61
-rw-r--r--drivers/media/video/pwc/pwc-if.c1243
-rw-r--r--drivers/media/video/pwc/pwc-kiara.c892
-rw-r--r--drivers/media/video/pwc/pwc-kiara.h48
-rw-r--r--drivers/media/video/pwc/pwc-misc.c93
-rw-r--r--drivers/media/video/pwc/pwc-nala.h66
-rw-r--r--drivers/media/video/pwc/pwc-timon.c1448
-rw-r--r--drivers/media/video/pwc/pwc-timon.h63
-rw-r--r--drivers/media/video/pwc/pwc-uncompress.c107
-rw-r--r--drivers/media/video/pwc/pwc-v4l.c1169
-rw-r--r--drivers/media/video/pwc/pwc.h395
-rw-r--r--drivers/media/video/pxa_camera.c1861
-rw-r--r--drivers/media/video/rj54n1cb0c.c1414
-rw-r--r--drivers/media/video/s2255drv.c2689
-rw-r--r--drivers/media/video/s5k6aa.c1670
-rw-r--r--drivers/media/video/s5p-fimc/Makefile5
-rw-r--r--drivers/media/video/s5p-fimc/fimc-capture.c1583
-rw-r--r--drivers/media/video/s5p-fimc/fimc-core.c2045
-rw-r--r--drivers/media/video/s5p-fimc/fimc-core.h826
-rw-r--r--drivers/media/video/s5p-fimc/fimc-mdevice.c862
-rw-r--r--drivers/media/video/s5p-fimc/fimc-mdevice.h118
-rw-r--r--drivers/media/video/s5p-fimc/fimc-reg.c723
-rw-r--r--drivers/media/video/s5p-fimc/mipi-csis.c729
-rw-r--r--drivers/media/video/s5p-fimc/mipi-csis.h25
-rw-r--r--drivers/media/video/s5p-fimc/regs-fimc.h301
-rw-r--r--drivers/media/video/s5p-g2d/Makefile3
-rw-r--r--drivers/media/video/s5p-g2d/g2d-hw.c109
-rw-r--r--drivers/media/video/s5p-g2d/g2d-regs.h115
-rw-r--r--drivers/media/video/s5p-g2d/g2d.c848
-rw-r--r--drivers/media/video/s5p-g2d/g2d.h87
-rw-r--r--drivers/media/video/s5p-jpeg/Makefile2
-rw-r--r--drivers/media/video/s5p-jpeg/jpeg-core.c1563
-rw-r--r--drivers/media/video/s5p-jpeg/jpeg-core.h152
-rw-r--r--drivers/media/video/s5p-jpeg/jpeg-hw.h357
-rw-r--r--drivers/media/video/s5p-jpeg/jpeg-regs.h170
-rw-r--r--drivers/media/video/s5p-mfc/Makefile5
-rw-r--r--drivers/media/video/s5p-mfc/regs-mfc.h413
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc.c1252
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_cmd.c120
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_cmd.h30
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_common.h572
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c343
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_ctrl.h29
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_debug.h48
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_dec.c1037
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_dec.h23
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_enc.c1830
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_enc.h23
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_intr.c92
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_intr.h26
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_opr.c1397
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_opr.h91
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_pm.c137
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_pm.h24
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_shm.c47
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_shm.h91
-rw-r--r--drivers/media/video/s5p-tv/Kconfig86
-rw-r--r--drivers/media/video/s5p-tv/Makefile19
-rw-r--r--drivers/media/video/s5p-tv/hdmi_drv.c1043
-rw-r--r--drivers/media/video/s5p-tv/hdmiphy_drv.c178
-rw-r--r--drivers/media/video/s5p-tv/mixer.h364
-rw-r--r--drivers/media/video/s5p-tv/mixer_drv.c487
-rw-r--r--drivers/media/video/s5p-tv/mixer_grp_layer.c270
-rw-r--r--drivers/media/video/s5p-tv/mixer_reg.c548
-rw-r--r--drivers/media/video/s5p-tv/mixer_video.c1108
-rw-r--r--drivers/media/video/s5p-tv/mixer_vp_layer.c241
-rw-r--r--drivers/media/video/s5p-tv/regs-hdmi.h145
-rw-r--r--drivers/media/video/s5p-tv/regs-mixer.h122
-rw-r--r--drivers/media/video/s5p-tv/regs-sdo.h63
-rw-r--r--drivers/media/video/s5p-tv/regs-vp.h88
-rw-r--r--drivers/media/video/s5p-tv/sdo_drv.c452
-rw-r--r--drivers/media/video/s5p-tv/sii9234_drv.c432
-rw-r--r--drivers/media/video/saa6588.c542
-rw-r--r--drivers/media/video/saa7110.c494
-rw-r--r--drivers/media/video/saa7115.c1727
-rw-r--r--drivers/media/video/saa711x_regs.h549
-rw-r--r--drivers/media/video/saa7121.h132
-rw-r--r--drivers/media/video/saa7127.c855
-rw-r--r--drivers/media/video/saa7134/Kconfig64
-rw-r--r--drivers/media/video/saa7134/Makefile16
-rw-r--r--drivers/media/video/saa7134/saa6752hs.c1012
-rw-r--r--drivers/media/video/saa7134/saa7134-alsa.c1209
-rw-r--r--drivers/media/video/saa7134/saa7134-cards.c7981
-rw-r--r--drivers/media/video/saa7134/saa7134-core.c1368
-rw-r--r--drivers/media/video/saa7134/saa7134-dvb.c1897
-rw-r--r--drivers/media/video/saa7134/saa7134-empress.c590
-rw-r--r--drivers/media/video/saa7134/saa7134-i2c.c435
-rw-r--r--drivers/media/video/saa7134/saa7134-input.c1038
-rw-r--r--drivers/media/video/saa7134/saa7134-reg.h378
-rw-r--r--drivers/media/video/saa7134/saa7134-ts.c327
-rw-r--r--drivers/media/video/saa7134/saa7134-tvaudio.c1087
-rw-r--r--drivers/media/video/saa7134/saa7134-vbi.c255
-rw-r--r--drivers/media/video/saa7134/saa7134-video.c2661
-rw-r--r--drivers/media/video/saa7134/saa7134.h855
-rw-r--r--drivers/media/video/saa7146.h112
-rw-r--r--drivers/media/video/saa7146reg.h283
-rw-r--r--drivers/media/video/saa7164/Kconfig18
-rw-r--r--drivers/media/video/saa7164/Makefile12
-rw-r--r--drivers/media/video/saa7164/saa7164-api.c1533
-rw-r--r--drivers/media/video/saa7164/saa7164-buffer.c322
-rw-r--r--drivers/media/video/saa7164/saa7164-bus.c475
-rw-r--r--drivers/media/video/saa7164/saa7164-cards.c773
-rw-r--r--drivers/media/video/saa7164/saa7164-cmd.c589
-rw-r--r--drivers/media/video/saa7164/saa7164-core.c1526
-rw-r--r--drivers/media/video/saa7164/saa7164-dvb.c556
-rw-r--r--drivers/media/video/saa7164/saa7164-encoder.c1500
-rw-r--r--drivers/media/video/saa7164/saa7164-fw.c613
-rw-r--r--drivers/media/video/saa7164/saa7164-i2c.c141
-rw-r--r--drivers/media/video/saa7164/saa7164-reg.h219
-rw-r--r--drivers/media/video/saa7164/saa7164-types.h442
-rw-r--r--drivers/media/video/saa7164/saa7164-vbi.c1374
-rw-r--r--drivers/media/video/saa7164/saa7164.h624
-rw-r--r--drivers/media/video/saa717x.c1378
-rw-r--r--drivers/media/video/saa7185.c377
-rw-r--r--drivers/media/video/saa7191.c659
-rw-r--r--drivers/media/video/saa7191.h245
-rw-r--r--drivers/media/video/sh_mobile_ceu_camera.c2319
-rw-r--r--drivers/media/video/sh_mobile_csi2.c398
-rw-r--r--drivers/media/video/sh_vou.c1493
-rw-r--r--drivers/media/video/sn9c102/Kconfig14
-rw-r--r--drivers/media/video/sn9c102/Makefile15
-rw-r--r--drivers/media/video/sn9c102/sn9c102.h211
-rw-r--r--drivers/media/video/sn9c102/sn9c102_config.h86
-rw-r--r--drivers/media/video/sn9c102/sn9c102_core.c3423
-rw-r--r--drivers/media/video/sn9c102/sn9c102_devtable.h147
-rw-r--r--drivers/media/video/sn9c102/sn9c102_hv7131d.c264
-rw-r--r--drivers/media/video/sn9c102/sn9c102_hv7131r.c363
-rw-r--r--drivers/media/video/sn9c102/sn9c102_mi0343.c352
-rw-r--r--drivers/media/video/sn9c102/sn9c102_mi0360.c453
-rw-r--r--drivers/media/video/sn9c102/sn9c102_mt9v111.c260
-rw-r--r--drivers/media/video/sn9c102/sn9c102_ov7630.c626
-rw-r--r--drivers/media/video/sn9c102/sn9c102_ov7660.c538
-rw-r--r--drivers/media/video/sn9c102/sn9c102_pas106b.c302
-rw-r--r--drivers/media/video/sn9c102/sn9c102_pas202bcb.c335
-rw-r--r--drivers/media/video/sn9c102/sn9c102_sensor.h307
-rw-r--r--drivers/media/video/sn9c102/sn9c102_tas5110c1b.c154
-rw-r--r--drivers/media/video/sn9c102/sn9c102_tas5110d.c119
-rw-r--r--drivers/media/video/sn9c102/sn9c102_tas5130d1b.c165
-rw-r--r--drivers/media/video/soc_camera.c1537
-rw-r--r--drivers/media/video/soc_camera_platform.c197
-rw-r--r--drivers/media/video/soc_mediabus.c433
-rw-r--r--drivers/media/video/sr030pc30.c871
-rw-r--r--drivers/media/video/stk-sensor.c595
-rw-r--r--drivers/media/video/stk-webcam.c1380
-rw-r--r--drivers/media/video/stk-webcam.h134
-rw-r--r--drivers/media/video/tcm825x.c937
-rw-r--r--drivers/media/video/tcm825x.h200
-rw-r--r--drivers/media/video/tda7432.c485
-rw-r--r--drivers/media/video/tda9840.c211
-rw-r--r--drivers/media/video/tea6415c.c187
-rw-r--r--drivers/media/video/tea6415c.h27
-rw-r--r--drivers/media/video/tea6420.c169
-rw-r--r--drivers/media/video/tea6420.h24
-rw-r--r--drivers/media/video/ths7303.c140
-rw-r--r--drivers/media/video/timblogiw.c880
-rw-r--r--drivers/media/video/tlg2300/Kconfig16
-rw-r--r--drivers/media/video/tlg2300/Makefile9
-rw-r--r--drivers/media/video/tlg2300/pd-alsa.c332
-rw-r--r--drivers/media/video/tlg2300/pd-common.h281
-rw-r--r--drivers/media/video/tlg2300/pd-dvb.c596
-rw-r--r--drivers/media/video/tlg2300/pd-main.c534
-rw-r--r--drivers/media/video/tlg2300/pd-radio.c421
-rw-r--r--drivers/media/video/tlg2300/pd-video.c1669
-rw-r--r--drivers/media/video/tlg2300/vendorcmds.h243
-rw-r--r--drivers/media/video/tlv320aic23b.c230
-rw-r--r--drivers/media/video/tm6000/Kconfig33
-rw-r--r--drivers/media/video/tm6000/Makefile15
-rw-r--r--drivers/media/video/tm6000/tm6000-alsa.c528
-rw-r--r--drivers/media/video/tm6000/tm6000-cards.c1413
-rw-r--r--drivers/media/video/tm6000/tm6000-core.c935
-rw-r--r--drivers/media/video/tm6000/tm6000-dvb.c467
-rw-r--r--drivers/media/video/tm6000/tm6000-i2c.c334
-rw-r--r--drivers/media/video/tm6000/tm6000-input.c499
-rw-r--r--drivers/media/video/tm6000/tm6000-regs.h600
-rw-r--r--drivers/media/video/tm6000/tm6000-stds.c640
-rw-r--r--drivers/media/video/tm6000/tm6000-usb-isoc.h50
-rw-r--r--drivers/media/video/tm6000/tm6000-video.c1822
-rw-r--r--drivers/media/video/tm6000/tm6000.h401
-rw-r--r--drivers/media/video/tuner-core.c1336
-rw-r--r--drivers/media/video/tvaudio.c2081
-rw-r--r--drivers/media/video/tveeprom.c792
-rw-r--r--drivers/media/video/tvp514x.c1166
-rw-r--r--drivers/media/video/tvp514x_regs.h287
-rw-r--r--drivers/media/video/tvp5150.c1242
-rw-r--r--drivers/media/video/tvp5150_reg.h139
-rw-r--r--drivers/media/video/tvp7002.c1072
-rw-r--r--drivers/media/video/tvp7002_reg.h150
-rw-r--r--drivers/media/video/tw9910.c958
-rw-r--r--drivers/media/video/upd64031a.c274
-rw-r--r--drivers/media/video/upd64083.c246
-rw-r--r--drivers/media/video/usbvision/Kconfig12
-rw-r--r--drivers/media/video/usbvision/Makefile6
-rw-r--r--drivers/media/video/usbvision/usbvision-cards.c1133
-rw-r--r--drivers/media/video/usbvision/usbvision-cards.h69
-rw-r--r--drivers/media/video/usbvision/usbvision-core.c2520
-rw-r--r--drivers/media/video/usbvision/usbvision-i2c.c456
-rw-r--r--drivers/media/video/usbvision/usbvision-video.c1686
-rw-r--r--drivers/media/video/usbvision/usbvision.h535
-rw-r--r--drivers/media/video/uvc/Kconfig18
-rw-r--r--drivers/media/video/uvc/Makefile6
-rw-r--r--drivers/media/video/uvc/uvc_ctrl.c1934
-rw-r--r--drivers/media/video/uvc/uvc_debugfs.c136
-rw-r--r--drivers/media/video/uvc/uvc_driver.c2448
-rw-r--r--drivers/media/video/uvc/uvc_entity.c126
-rw-r--r--drivers/media/video/uvc/uvc_isight.c137
-rw-r--r--drivers/media/video/uvc/uvc_queue.c376
-rw-r--r--drivers/media/video/uvc/uvc_status.c237
-rw-r--r--drivers/media/video/uvc/uvc_v4l2.c1291
-rw-r--r--drivers/media/video/uvc/uvc_video.c1857
-rw-r--r--drivers/media/video/uvc/uvcvideo.h697
-rw-r--r--drivers/media/video/v4l2-common.c623
-rw-r--r--drivers/media/video/v4l2-compat-ioctl32.c1042
-rw-r--r--drivers/media/video/v4l2-ctrls.c2442
-rw-r--r--drivers/media/video/v4l2-dev.c803
-rw-r--r--drivers/media/video/v4l2-device.c280
-rw-r--r--drivers/media/video/v4l2-event.c320
-rw-r--r--drivers/media/video/v4l2-fh.c120
-rw-r--r--drivers/media/video/v4l2-int-device.c164
-rw-r--r--drivers/media/video/v4l2-ioctl.c2490
-rw-r--r--drivers/media/video/v4l2-mem2mem.c631
-rw-r--r--drivers/media/video/v4l2-subdev.c351
-rw-r--r--drivers/media/video/via-camera.c1503
-rw-r--r--drivers/media/video/via-camera.h93
-rw-r--r--drivers/media/video/videobuf-core.c1204
-rw-r--r--drivers/media/video/videobuf-dma-contig.c400
-rw-r--r--drivers/media/video/videobuf-dma-sg.c633
-rw-r--r--drivers/media/video/videobuf-dvb.c404
-rw-r--r--drivers/media/video/videobuf-vmalloc.c349
-rw-r--r--drivers/media/video/videobuf2-core.c2102
-rw-r--r--drivers/media/video/videobuf2-dma-contig.c186
-rw-r--r--drivers/media/video/videobuf2-dma-sg.c283
-rw-r--r--drivers/media/video/videobuf2-memops.c227
-rw-r--r--drivers/media/video/videobuf2-vmalloc.c222
-rw-r--r--drivers/media/video/vino.c4349
-rw-r--r--drivers/media/video/vino.h138
-rw-r--r--drivers/media/video/vivi.c1400
-rw-r--r--drivers/media/video/vp27smpx.c211
-rw-r--r--drivers/media/video/vpx3220.c591
-rw-r--r--drivers/media/video/vs6624.c928
-rw-r--r--drivers/media/video/vs6624_regs.h337
-rw-r--r--drivers/media/video/w9966.c993
-rw-r--r--drivers/media/video/wm8739.c294
-rw-r--r--drivers/media/video/wm8775.c342
-rwxr-xr-xdrivers/media/video/wmt/Kconfig19
-rwxr-xr-xdrivers/media/video/wmt/Makefile13
-rwxr-xr-xdrivers/media/video/wmt/encode/Kconfig15
-rwxr-xr-xdrivers/media/video/wmt/encode/Makefile11
-rwxr-xr-xdrivers/media/video/wmt/encode/jpeg/Kbuild3
-rwxr-xr-xdrivers/media/video/wmt_v4l2/Kconfig6
-rwxr-xr-xdrivers/media/video/wmt_v4l2/Makefile13
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/Kconfig13
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/Makefile49
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/bf3703/bf3703.c326
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/bf3703/bf3703.h269
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/bf3920/bf3920.c330
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/bf3920/bf3920.h514
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/bf3a03/bf3a03.c335
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/bf3a03/bf3a03.h391
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/cmos-subdev.c155
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/cmos-subdev.h108
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/flash/flash.c54
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/flash/flash.h36
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/flash/flash_eup2471.c131
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/flash/flash_eup3618.c124
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/flash/flash_gpio.c103
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/flash/flash_ktd231.c156
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/flash/flash_sgm3780.c207
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0307/gc0307-twv82222z2-f10.h624
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0307/gc0307.c371
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0307/gc0307.h567
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-7303.h450
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-7304.h546
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-781a.h448
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-gsg5-001gt7204-g3.h451
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-gsg5-001gt7204-g3_qx.h451
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-gt7302-gmt.h609
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-jfs0672-clw-gt7305.h481
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-kyt-110_v1.0.h446
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-kyt-173_v1.0.h446
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-mrs-gt7305_v1.h452
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-mrs-gt7305_v1_qx.h452
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-syx-gt7206.h446
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-yb474-gt7303.h465
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-zh_tvbox.h447
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-zyhb-twd8802.h447
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0308/gc0308.c378
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0308/gc0308.h530
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0308/gc0308_7300_hd.h424
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0308/gc0308_8335c.h424
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0308/gc0308_8353.h457
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0309/gc0309-zyhb0309.h725
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0309/gc0309.c346
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0309/gc0309.h737
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0311/gc0311.c224
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0311/gc0311.h444
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0328/gc0328-gt7302-gmt.h686
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0328/gc0328-wave7_cold.h606
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0328/gc0328-wave7_warm.h605
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0328/gc0328.c346
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0328/gc0328.h604
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0328/gc0328_kyt111_v7.h604
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0328/gc0328_ra474a_v7.h624
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0329/gc0329-3nod.h550
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0329/gc0329-3nod_TL070VA5.h560
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0329/gc0329-jmt781.h496
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0329/gc0329-kyt-174-v7.h992
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0329/gc0329-zh1426.h530
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0329/gc0329.c345
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0329/gc0329.h530
-rw-r--r--drivers/media/video/wmt_v4l2/sensors/gc0329/gc0329_shuttle.h560
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc0329/gc0329_style_s729.h530
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc2015/gc2015.c225
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc2015/gc2015.h601
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-3nod.h1024
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-781a.h1027
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-8335c.h899
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-forfun-v91.h1105
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-htr_t75v.h1040
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-jmt781.h954
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-kyt110_v7.h1042
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-mid8-wms8368.h1025
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-rb352a_v7.h914
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-rb357a_v7.h914
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-shutter.c441
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-tvboxh2-ori.h1064
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-v7_maz7z233.h1105
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-xhb_gbeffz2743l.h1023
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-zh1426.h954
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-zh1547a-tp785.h1030
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc2035/gc2035.c390
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc2035/gc2035.h1105
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc2035/gc2035_pid10_hl_pc1089.h1158
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc2035/gc2035_pid13_hl_pc1388.h1152
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc2155/gc2155.c388
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/gc2155/gc2155.h1074
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/hi253/hi253-bk-wave7.h2147
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/hi253/hi253-jfs0672-clw-gt7305.h2159
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/hi253/hi253-mrs-gt7305_v1.h2133
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/hi253/hi253-mrs-wave7.h2147
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/hi253/hi253-syx-gt7206.h2159
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/hi253/hi253-wave7.h2128
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/hi253/hi253.c333
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/hi253/hi253.h2124
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/hi257/hi257.c333
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/hi257/hi257.h2195
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/hi257/hi257_kyt162_v7.h2195
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/nmi5625/Makefile4
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/nmi5625/nmidrv_cmos.c158
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/nmi5625/nmidrv_cmos.h219
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/nmi5625/nmidrv_custom_wmt.h70
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/nmi5625/nmidrv_i2c.c484
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/nmi5625/nmidrv_i2c.h14
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/nmi5625/nmidrv_kernel.c435
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/nmi5625/nmidrv_kernel.h42
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/nt99141/nt99141.c448
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/nt99141/nt99141.h537
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/ov2640/ov2640.h66
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/ov2643/ov2643.c329
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/ov2643/ov2643.h380
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/ov2659/ov2659.c346
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/ov2659/ov2659.h1197
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/ov3640/ov3640.h3620
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/ov3660/ov3660.c209
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/ov3660/ov3660.h598
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/ov5640/ov5640-mid8-wms8368.h1296
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/ov5640/ov5640.c572
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/ov5640/ov5640.h1296
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/ov5640/ov5640_firmware.h4095
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/ov7675/ov7675.c321
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/ov7675/ov7675.h258
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/ov9740/ov9740.c348
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/ov9740/ov9740.h688
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/s5k5ca/s5k5ca.c374
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/s5k5ca/s5k5ca.h3288
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/sid130b/sid130b.c328
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/sid130b/sid130b.h957
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/siv120d/siv120d.c332
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/siv120d/siv120d.h485
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/siv121d/siv121d-kyt-093-du.h1128
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/siv121d/siv121d.c337
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/siv121d/siv121d.h1128
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/sp0718/sp0718-gt7302-gmt.h992
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/sp0718/sp0718.c344
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/sp0718/sp0718.h990
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/sp0838/sp0838.c326
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/sp0838/sp0838.h788
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/sp0a19/sp0a19.c344
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/sp0a19/sp0a19.h667
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/sp2518/sp2518.c326
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/sp2518/sp2518.h1221
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/template/template.h219
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/wmt-cmos.c1264
-rwxr-xr-xdrivers/media/video/wmt_v4l2/sensors/wmt-cmos.h0
-rwxr-xr-xdrivers/media/video/wmt_v4l2/wmt-vid.c820
-rwxr-xr-xdrivers/media/video/wmt_v4l2/wmt-vid.h168
-rwxr-xr-xdrivers/media/video/wmt_v4l2/wmt-vidreg.h87
-rw-r--r--drivers/media/video/zoran/Kconfig74
-rw-r--r--drivers/media/video/zoran/Makefile6
-rw-r--r--drivers/media/video/zoran/videocodec.c407
-rw-r--r--drivers/media/video/zoran/videocodec.h353
-rw-r--r--drivers/media/video/zoran/zoran.h401
-rw-r--r--drivers/media/video/zoran/zoran_card.c1524
-rw-r--r--drivers/media/video/zoran/zoran_card.h54
-rw-r--r--drivers/media/video/zoran/zoran_device.c1642
-rw-r--r--drivers/media/video/zoran/zoran_device.h95
-rw-r--r--drivers/media/video/zoran/zoran_driver.c3076
-rw-r--r--drivers/media/video/zoran/zoran_procfs.c225
-rw-r--r--drivers/media/video/zoran/zoran_procfs.h36
-rw-r--r--drivers/media/video/zoran/zr36016.c524
-rw-r--r--drivers/media/video/zoran/zr36016.h111
-rw-r--r--drivers/media/video/zoran/zr36050.c900
-rw-r--r--drivers/media/video/zoran/zr36050.h184
-rw-r--r--drivers/media/video/zoran/zr36057.h168
-rw-r--r--drivers/media/video/zoran/zr36060.c1010
-rw-r--r--drivers/media/video/zoran/zr36060.h220
-rw-r--r--drivers/media/video/zr364xx.c1703
963 files changed, 623302 insertions, 0 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
new file mode 100644
index 00000000..ce1e7ba9
--- /dev/null
+++ b/drivers/media/video/Kconfig
@@ -0,0 +1,1224 @@
+#
+# Generic video config states
+#
+
+config VIDEO_V4L2
+ tristate
+ depends on VIDEO_DEV && VIDEO_V4L2_COMMON
+ default VIDEO_DEV && VIDEO_V4L2_COMMON
+
+config VIDEOBUF_GEN
+ tristate
+
+config VIDEOBUF_DMA_SG
+ depends on HAS_DMA
+ select VIDEOBUF_GEN
+ tristate
+
+config VIDEOBUF_VMALLOC
+ select VIDEOBUF_GEN
+ tristate
+
+config VIDEOBUF_DMA_CONTIG
+ depends on HAS_DMA
+ select VIDEOBUF_GEN
+ tristate
+
+config VIDEOBUF_DVB
+ tristate
+ select VIDEOBUF_GEN
+
+config VIDEO_BTCX
+ depends on PCI
+ tristate
+
+config VIDEO_TVEEPROM
+ tristate
+ depends on I2C
+
+config VIDEO_TUNER
+ tristate
+ depends on MEDIA_TUNER
+
+config V4L2_MEM2MEM_DEV
+ tristate
+ depends on VIDEOBUF2_CORE
+
+config VIDEOBUF2_CORE
+ tristate
+
+config VIDEOBUF2_MEMOPS
+ tristate
+
+config VIDEOBUF2_DMA_CONTIG
+ select VIDEOBUF2_CORE
+ select VIDEOBUF2_MEMOPS
+ tristate
+
+config VIDEOBUF2_VMALLOC
+ select VIDEOBUF2_CORE
+ select VIDEOBUF2_MEMOPS
+ tristate
+
+
+config VIDEOBUF2_DMA_SG
+ #depends on HAS_DMA
+ select VIDEOBUF2_CORE
+ select VIDEOBUF2_MEMOPS
+ tristate
+#
+# Multimedia Video device configuration
+#
+
+menuconfig VIDEO_CAPTURE_DRIVERS
+ bool "Video capture adapters"
+ depends on VIDEO_V4L2
+ default y
+ ---help---
+ Say Y here to enable selecting the video adapters for
+ webcams, analog TV, and hybrid analog/digital TV.
+ Some of those devices also supports FM radio.
+
+if VIDEO_CAPTURE_DRIVERS && VIDEO_V4L2
+
+config VIDEO_ADV_DEBUG
+ bool "Enable advanced debug functionality"
+ default n
+ ---help---
+ Say Y here to enable advanced debugging functionality on some
+ V4L devices.
+ In doubt, say N.
+
+config VIDEO_FIXED_MINOR_RANGES
+ bool "Enable old-style fixed minor ranges for video devices"
+ default n
+ ---help---
+ Say Y here to enable the old-style fixed-range minor assignments.
+ Only useful if you rely on the old behavior and use mknod instead of udev.
+
+ When in doubt, say N.
+
+config VIDEO_HELPER_CHIPS_AUTO
+ bool "Autoselect pertinent encoders/decoders and other helper chips"
+ default y if !EXPERT
+ ---help---
+ Most video cards may require additional modules to encode or
+ decode audio/video standards. This option will autoselect
+ all pertinent modules to each selected video module.
+
+ Unselect this only if you know exactly what you are doing, since
+ it may break support on some boards.
+
+ In doubt, say Y.
+
+config VIDEO_IR_I2C
+ tristate "I2C module for IR" if !VIDEO_HELPER_CHIPS_AUTO
+ depends on I2C && RC_CORE
+ default y
+ ---help---
+ Most boards have an IR chip directly connected via GPIO. However,
+ some video boards have the IR connected via I2C bus.
+
+ If your board doesn't have an I2C IR chip, you may disable this
+ option.
+
+ In doubt, say Y.
+
+#
+# Encoder / Decoder module configuration
+#
+
+menu "Encoders, decoders, sensors and other helper chips"
+ visible if !VIDEO_HELPER_CHIPS_AUTO
+
+comment "Audio decoders, processors and mixers"
+
+config VIDEO_TVAUDIO
+ tristate "Simple audio decoder chips"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for several audio decoder chips found on some bt8xx boards:
+ Philips: tda9840, tda9873h, tda9874h/a, tda9850, tda985x, tea6300,
+ tea6320, tea6420, tda8425, ta8874z.
+ Microchip: pic16c54 based design on ProVideo PV951 board.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tvaudio.
+
+config VIDEO_TDA7432
+ tristate "Philips TDA7432 audio processor"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for tda7432 audio decoder chip found on some bt8xx boards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tda7432.
+
+config VIDEO_TDA9840
+ tristate "Philips TDA9840 audio processor"
+ depends on I2C
+ ---help---
+ Support for tda9840 audio decoder chip found on some Zoran boards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tda9840.
+
+config VIDEO_TEA6415C
+ tristate "Philips TEA6415C audio processor"
+ depends on I2C
+ ---help---
+ Support for tea6415c audio decoder chip found on some bt8xx boards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tea6415c.
+
+config VIDEO_TEA6420
+ tristate "Philips TEA6420 audio processor"
+ depends on I2C
+ ---help---
+ Support for tea6420 audio decoder chip found on some bt8xx boards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tea6420.
+
+config VIDEO_MSP3400
+ tristate "Micronas MSP34xx audio decoders"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Micronas MSP34xx series of audio decoders.
+
+ To compile this driver as a module, choose M here: the
+ module will be called msp3400.
+
+config VIDEO_CS5345
+ tristate "Cirrus Logic CS5345 audio ADC"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Cirrus Logic CS5345 24-bit, 192 kHz
+ stereo A/D converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cs5345.
+
+config VIDEO_CS53L32A
+ tristate "Cirrus Logic CS53L32A audio ADC"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Cirrus Logic CS53L32A low voltage
+ stereo A/D converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cs53l32a.
+
+config VIDEO_TLV320AIC23B
+ tristate "Texas Instruments TLV320AIC23B audio codec"
+ depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ ---help---
+ Support for the Texas Instruments TLV320AIC23B audio codec.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tlv320aic23b.
+
+config VIDEO_WM8775
+ tristate "Wolfson Microelectronics WM8775 audio ADC with input mixer"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Wolfson Microelectronics WM8775 high
+ performance stereo A/D Converter with a 4 channel input mixer.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wm8775.
+
+config VIDEO_WM8739
+ tristate "Wolfson Microelectronics WM8739 stereo audio ADC"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Wolfson Microelectronics WM8739
+ stereo A/D Converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wm8739.
+
+config VIDEO_VP27SMPX
+ tristate "Panasonic VP27s internal MPX"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the internal MPX of the Panasonic VP27s tuner.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vp27smpx.
+
+comment "RDS decoders"
+
+config VIDEO_SAA6588
+ tristate "SAA6588 Radio Chip RDS decoder support"
+ depends on VIDEO_V4L2 && I2C
+
+ help
+ Support for this Radio Data System (RDS) decoder. This allows
+ seeing radio station identification transmitted using this
+ standard.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa6588.
+
+comment "Video decoders"
+
+config VIDEO_ADV7180
+ tristate "Analog Devices ADV7180 decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Analog Devices ADV7180 video decoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called adv7180.
+
+config VIDEO_ADV7183
+ tristate "Analog Devices ADV7183 decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ V4l2 subdevice driver for the Analog Devices
+ ADV7183 video decoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called adv7183.
+
+config VIDEO_BT819
+ tristate "BT819A VideoStream decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for BT819A video decoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bt819.
+
+config VIDEO_BT856
+ tristate "BT856 VideoStream decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for BT856 video decoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bt856.
+
+config VIDEO_BT866
+ tristate "BT866 VideoStream decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for BT866 video decoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bt866.
+
+config VIDEO_KS0127
+ tristate "KS0127 video decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for KS0127 video decoder.
+
+ This chip is used on AverMedia AVS6EYES Zoran-based MJPEG
+ cards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ks0127.
+
+config VIDEO_SAA7110
+ tristate "Philips SAA7110 video decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Philips SAA7110 video decoders.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7110.
+
+config VIDEO_SAA711X
+ tristate "Philips SAA7111/3/4/5 video decoders"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Philips SAA7111/3/4/5 video decoders.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7115.
+
+config VIDEO_SAA7191
+ tristate "Philips SAA7191 video decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Philips SAA7191 video decoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7191.
+
+config VIDEO_TVP514X
+ tristate "Texas Instruments TVP514x video decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the TI TVP5146/47
+ decoder. It is currently working with the TI OMAP3 camera
+ controller.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tvp514x.
+
+config VIDEO_TVP5150
+ tristate "Texas Instruments TVP5150 video decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Texas Instruments TVP5150 video decoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tvp5150.
+
+config VIDEO_TVP7002
+ tristate "Texas Instruments TVP7002 video decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Texas Instruments TVP7002 video decoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tvp7002.
+
+config VIDEO_VPX3220
+ tristate "vpx3220a, vpx3216b & vpx3214c video decoders"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for VPX322x video decoders.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vpx3220.
+
+comment "Video and audio decoders"
+
+config VIDEO_SAA717X
+ tristate "Philips SAA7171/3/4 audio/video decoders"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Philips SAA7171/3/4 audio/video decoders.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa717x.
+
+source "drivers/media/video/cx25840/Kconfig"
+
+comment "MPEG video encoders"
+
+config VIDEO_CX2341X
+ tristate "Conexant CX2341x MPEG encoders"
+ depends on VIDEO_V4L2 && VIDEO_V4L2_COMMON
+ ---help---
+ Support for the Conexant CX23416 MPEG encoders
+ and CX23415 MPEG encoder/decoders.
+
+ This module currently supports the encoding functions only.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cx2341x.
+
+comment "Video encoders"
+
+config VIDEO_SAA7127
+ tristate "Philips SAA7127/9 digital video encoders"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Philips SAA7127/9 digital video encoders.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7127.
+
+config VIDEO_SAA7185
+ tristate "Philips SAA7185 video encoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Philips SAA7185 video encoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7185.
+
+config VIDEO_ADV7170
+ tristate "Analog Devices ADV7170 video encoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Analog Devices ADV7170 video encoder driver
+
+ To compile this driver as a module, choose M here: the
+ module will be called adv7170.
+
+config VIDEO_ADV7175
+ tristate "Analog Devices ADV7175 video encoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Analog Devices ADV7175 video encoder driver
+
+ To compile this driver as a module, choose M here: the
+ module will be called adv7175.
+
+config VIDEO_ADV7343
+ tristate "ADV7343 video encoder"
+ depends on I2C
+ help
+ Support for Analog Devices I2C bus based ADV7343 encoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called adv7343.
+
+config VIDEO_AK881X
+ tristate "AK8813/AK8814 video encoders"
+ depends on I2C
+ help
+ Video output driver for AKM AK8813 and AK8814 TV encoders
+
+comment "Camera sensor devices"
+
+config VIDEO_APTINA_PLL
+ tristate
+
+config VIDEO_OV7670
+ tristate "OmniVision OV7670 sensor support"
+ depends on I2C && VIDEO_V4L2
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the OmniVision
+ OV7670 VGA camera. It currently only works with the M88ALP01
+ controller.
+
+config VIDEO_VS6624
+ tristate "ST VS6624 sensor support"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the ST VS6624
+ camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vs6624.
+
+config VIDEO_MT9M032
+ tristate "MT9M032 camera sensor support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ select VIDEO_APTINA_PLL
+ ---help---
+ This driver supports MT9M032 camera sensors from Aptina, monochrome
+ models only.
+
+config VIDEO_MT9P031
+ tristate "Aptina MT9P031 support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ select VIDEO_APTINA_PLL
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the Aptina
+ (Micron) mt9p031 5 Mpixel camera.
+
+config VIDEO_MT9T001
+ tristate "Aptina MT9T001 support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the Aptina
+ (Micron) mt0t001 3 Mpixel camera.
+
+config VIDEO_MT9V011
+ tristate "Micron mt9v011 sensor support"
+ depends on I2C && VIDEO_V4L2
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the Micron
+ mt0v011 1.3 Mpixel camera. It currently only works with the
+ em28xx driver.
+
+config VIDEO_MT9V032
+ tristate "Micron MT9V032 sensor support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the Micron
+ MT9V032 752x480 CMOS sensor.
+
+config VIDEO_TCM825X
+ tristate "TCM825x camera sensor support"
+ depends on I2C && VIDEO_V4L2
+ ---help---
+ This is a driver for the Toshiba TCM825x VGA camera sensor.
+ It is used for example in Nokia N800.
+
+config VIDEO_SR030PC30
+ tristate "Siliconfile SR030PC30 sensor support"
+ depends on I2C && VIDEO_V4L2
+ ---help---
+ This driver supports SR030PC30 VGA camera from Siliconfile
+
+config VIDEO_NOON010PC30
+ tristate "Siliconfile NOON010PC30 sensor support"
+ depends on I2C && VIDEO_V4L2 && EXPERIMENTAL && VIDEO_V4L2_SUBDEV_API
+ ---help---
+ This driver supports NOON010PC30 CIF camera from Siliconfile
+
+source "drivers/media/video/m5mols/Kconfig"
+
+config VIDEO_S5K6AA
+ tristate "Samsung S5K6AAFX sensor support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ ---help---
+ This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M
+ camera sensor with an embedded SoC image signal processor.
+
+comment "Flash devices"
+
+config VIDEO_ADP1653
+ tristate "ADP1653 flash support"
+ depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
+ ---help---
+ This is a driver for the ADP1653 flash controller. It is used for
+ example in Nokia N900.
+
+config VIDEO_AS3645A
+ tristate "AS3645A flash driver support"
+ depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
+ ---help---
+ This is a driver for the AS3645A and LM3555 flash controllers. It has
+ build in control for flash, torch and indicator LEDs.
+
+comment "Video improvement chips"
+
+config VIDEO_UPD64031A
+ tristate "NEC Electronics uPD64031A Ghost Reduction"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the NEC Electronics uPD64031A Ghost Reduction
+ video chip. It is most often found in NTSC TV cards made for
+ Japan and is used to reduce the 'ghosting' effect that can
+ be present in analog TV broadcasts.
+
+ To compile this driver as a module, choose M here: the
+ module will be called upd64031a.
+
+config VIDEO_UPD64083
+ tristate "NEC Electronics uPD64083 3-Dimensional Y/C separation"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the NEC Electronics uPD64083 3-Dimensional Y/C
+ separation video chip. It is used to improve the quality of
+ the colors of a composite signal.
+
+ To compile this driver as a module, choose M here: the
+ module will be called upd64083.
+
+comment "Miscelaneous helper chips"
+
+config VIDEO_THS7303
+ tristate "THS7303 Video Amplifier"
+ depends on I2C
+ help
+ Support for TI THS7303 video amplifier
+
+ To compile this driver as a module, choose M here: the
+ module will be called ths7303.
+
+config VIDEO_M52790
+ tristate "Mitsubishi M52790 A/V switch"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Mitsubishi M52790 A/V switch.
+
+ To compile this driver as a module, choose M here: the
+ module will be called m52790.
+
+endmenu # encoder / decoder chips
+
+config VIDEO_VIVI
+ tristate "Virtual Video Driver"
+ depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64
+ depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE
+ select FONT_8x16
+ select VIDEOBUF2_VMALLOC
+ default n
+ ---help---
+ Enables a virtual video driver. This device shows a color bar
+ and a timestamp, as a real device would generate by using V4L2
+ api.
+ Say Y here if you want to test video apps or debug V4L devices.
+ In doubt, say N.
+
+#
+# USB Multimedia device configuration
+#
+
+menuconfig V4L_USB_DRIVERS
+ bool "V4L USB devices"
+ depends on USB
+ default y
+
+if V4L_USB_DRIVERS
+
+source "drivers/media/video/uvc/Kconfig"
+
+source "drivers/media/video/gspca/Kconfig"
+
+source "drivers/media/video/pvrusb2/Kconfig"
+
+source "drivers/media/video/hdpvr/Kconfig"
+
+source "drivers/media/video/em28xx/Kconfig"
+
+source "drivers/media/video/tlg2300/Kconfig"
+
+source "drivers/media/video/cx231xx/Kconfig"
+
+source "drivers/media/video/tm6000/Kconfig"
+
+source "drivers/media/video/usbvision/Kconfig"
+
+source "drivers/media/video/et61x251/Kconfig"
+
+source "drivers/media/video/sn9c102/Kconfig"
+
+source "drivers/media/video/pwc/Kconfig"
+
+source "drivers/media/video/cpia2/Kconfig"
+
+config USB_ZR364XX
+ tristate "USB ZR364XX Camera support"
+ depends on VIDEO_V4L2
+ select VIDEOBUF_GEN
+ select VIDEOBUF_VMALLOC
+ ---help---
+ Say Y here if you want to connect this type of camera to your
+ computer's USB port.
+ See <file:Documentation/video4linux/zr364xx.txt> for more info
+ and list of supported cameras.
+
+ To compile this driver as a module, choose M here: the
+ module will be called zr364xx.
+
+config USB_STKWEBCAM
+ tristate "USB Syntek DC1125 Camera support"
+ depends on VIDEO_V4L2 && EXPERIMENTAL
+ ---help---
+ Say Y here if you want to use this type of camera.
+ Supported devices are typically found in some Asus laptops,
+ with USB id 174f:a311 and 05e1:0501. Other Syntek cameras
+ may be supported by the stk11xx driver, from which this is
+ derived, see <http://sourceforge.net/projects/syntekdriver/>
+
+ To compile this driver as a module, choose M here: the
+ module will be called stkwebcam.
+
+config USB_S2255
+ tristate "USB Sensoray 2255 video capture device"
+ depends on VIDEO_V4L2
+ select VIDEOBUF_VMALLOC
+ default n
+ help
+ Say Y here if you want support for the Sensoray 2255 USB device.
+ This driver can be compiled as a module, called s2255drv.
+
+endif # V4L_USB_DRIVERS
+
+#
+# PCI drivers configuration
+#
+
+menuconfig V4L_PCI_DRIVERS
+ bool "V4L PCI(e) devices"
+ depends on PCI
+ default y
+ ---help---
+ Say Y here to enable support for these PCI(e) drivers.
+
+if V4L_PCI_DRIVERS
+
+source "drivers/media/video/au0828/Kconfig"
+
+source "drivers/media/video/bt8xx/Kconfig"
+
+source "drivers/media/video/cx18/Kconfig"
+
+source "drivers/media/video/cx23885/Kconfig"
+
+source "drivers/media/video/cx25821/Kconfig"
+
+source "drivers/media/video/cx88/Kconfig"
+
+config VIDEO_HEXIUM_GEMINI
+ tristate "Hexium Gemini frame grabber"
+ depends on PCI && VIDEO_V4L2 && I2C
+ select VIDEO_SAA7146_VV
+ ---help---
+ This is a video4linux driver for the Hexium Gemini frame
+ grabber card by Hexium. Please note that the Gemini Dual
+ card is *not* fully supported.
+
+ To compile this driver as a module, choose M here: the
+ module will be called hexium_gemini.
+
+config VIDEO_HEXIUM_ORION
+ tristate "Hexium HV-PCI6 and Orion frame grabber"
+ depends on PCI && VIDEO_V4L2 && I2C
+ select VIDEO_SAA7146_VV
+ ---help---
+ This is a video4linux driver for the Hexium HV-PCI6 and
+ Orion frame grabber cards by Hexium.
+
+ To compile this driver as a module, choose M here: the
+ module will be called hexium_orion.
+
+source "drivers/media/video/ivtv/Kconfig"
+
+config VIDEO_MEYE
+ tristate "Sony Vaio Picturebook Motion Eye Video For Linux"
+ depends on PCI && SONY_LAPTOP && VIDEO_V4L2
+ ---help---
+ This is the video4linux driver for the Motion Eye camera found
+ in the Vaio Picturebook laptops. Please read the material in
+ <file:Documentation/video4linux/meye.txt> for more information.
+
+ If you say Y or M here, you need to say Y or M to "Sony Laptop
+ Extras" in the misc device section.
+
+ To compile this driver as a module, choose M here: the
+ module will be called meye.
+
+config VIDEO_MXB
+ tristate "Siemens-Nixdorf 'Multimedia eXtension Board'"
+ depends on PCI && VIDEO_V4L2 && I2C
+ select VIDEO_SAA7146_VV
+ select VIDEO_TUNER
+ select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_TDA9840 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_TEA6415C if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_TEA6420 if VIDEO_HELPER_CHIPS_AUTO
+ ---help---
+ This is a video4linux driver for the 'Multimedia eXtension Board'
+ TV card by Siemens-Nixdorf.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mxb.
+
+source "drivers/media/video/saa7134/Kconfig"
+
+source "drivers/media/video/saa7164/Kconfig"
+
+source "drivers/media/video/zoran/Kconfig"
+
+endif # V4L_PCI_DRIVERS
+
+#
+# ISA & parallel port drivers configuration
+#
+
+menuconfig V4L_ISA_PARPORT_DRIVERS
+ bool "V4L ISA and parallel port devices"
+ depends on ISA || PARPORT
+ default n
+ ---help---
+ Say Y here to enable support for these ISA and parallel port drivers.
+
+if V4L_ISA_PARPORT_DRIVERS
+
+config VIDEO_BWQCAM
+ tristate "Quickcam BW Video For Linux"
+ depends on PARPORT && VIDEO_V4L2
+ help
+ Say Y have if you the black and white version of the QuickCam
+ camera. See the next option for the color version.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bw-qcam.
+
+config VIDEO_CQCAM
+ tristate "QuickCam Colour Video For Linux"
+ depends on PARPORT && VIDEO_V4L2
+ help
+ This is the video4linux driver for the colour version of the
+ Connectix QuickCam. If you have one of these cameras, say Y here,
+ otherwise say N. This driver does not work with the original
+ monochrome QuickCam, QuickCam VC or QuickClip. It is also available
+ as a module (c-qcam).
+ Read <file:Documentation/video4linux/CQcam.txt> for more information.
+
+config VIDEO_PMS
+ tristate "Mediavision Pro Movie Studio Video For Linux"
+ depends on ISA && VIDEO_V4L2
+ help
+ Say Y if you have the ISA Mediavision Pro Movie Studio
+ capture card.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pms.
+
+config VIDEO_W9966
+ tristate "W9966CF Webcam (FlyCam Supra and others) Video For Linux"
+ depends on PARPORT_1284 && PARPORT && VIDEO_V4L2
+ help
+ Video4linux driver for Winbond's w9966 based Webcams.
+ Currently tested with the LifeView FlyCam Supra.
+ If you have one of these cameras, say Y here
+ otherwise say N.
+ This driver is also available as a module (w9966).
+
+ Check out <file:Documentation/video4linux/w9966.txt> for more
+ information.
+
+endif # V4L_ISA_PARPORT_DRIVERS
+
+menuconfig V4L_PLATFORM_DRIVERS
+ bool "V4L platform devices"
+ default n
+ ---help---
+ Say Y here to enable support for platform-specific V4L drivers.
+
+if V4L_PLATFORM_DRIVERS
+
+source "drivers/media/video/marvell-ccic/Kconfig"
+
+config VIDEO_VIA_CAMERA
+ tristate "VIAFB camera controller support"
+ depends on FB_VIA
+ select VIDEOBUF_DMA_SG
+ select VIDEO_OV7670
+ help
+ Driver support for the integrated camera controller in VIA
+ Chrome9 chipsets. Currently only tested on OLPC xo-1.5 systems
+ with ov7670 sensors.
+
+#
+# Platform multimedia device configuration
+#
+
+source "drivers/media/video/davinci/Kconfig"
+
+source "drivers/media/video/omap/Kconfig"
+
+source "drivers/media/video/blackfin/Kconfig"
+
+config VIDEO_SH_VOU
+ tristate "SuperH VOU video output driver"
+ depends on VIDEO_DEV && ARCH_SHMOBILE
+ select VIDEOBUF_DMA_CONTIG
+ help
+ Support for the Video Output Unit (VOU) on SuperH SoCs.
+
+config VIDEO_VIU
+ tristate "Freescale VIU Video Driver"
+ depends on VIDEO_V4L2 && PPC_MPC512x
+ select VIDEOBUF_DMA_CONTIG
+ default y
+ ---help---
+ Support for Freescale VIU video driver. This device captures
+ video data, or overlays video on DIU frame buffer.
+
+ Say Y here if you want to enable VIU device on MPC5121e Rev2+.
+ In doubt, say N.
+
+config VIDEO_TIMBERDALE
+ tristate "Support for timberdale Video In/LogiWIN"
+ depends on VIDEO_V4L2 && I2C && DMADEVICES
+ select DMA_ENGINE
+ select TIMB_DMA
+ select VIDEO_ADV7180
+ select VIDEOBUF_DMA_CONTIG
+ ---help---
+ Add support for the Video In peripherial of the timberdale FPGA.
+
+config VIDEO_VINO
+ tristate "SGI Vino Video For Linux"
+ depends on I2C && SGI_IP22 && VIDEO_V4L2
+ select VIDEO_SAA7191 if VIDEO_HELPER_CHIPS_AUTO
+ help
+ Say Y here to build in support for the Vino video input system found
+ on SGI Indy machines.
+
+config VIDEO_M32R_AR
+ tristate "AR devices"
+ depends on M32R && VIDEO_V4L2
+ ---help---
+ This is a video4linux driver for the Renesas AR (Artificial Retina)
+ camera module.
+
+config VIDEO_M32R_AR_M64278
+ tristate "AR device with color module M64278(VGA)"
+ depends on PLAT_M32700UT
+ select VIDEO_M32R_AR
+ ---help---
+ This is a video4linux driver for the Renesas AR (Artificial
+ Retina) with M64278E-800 camera module.
+ This module supports VGA(640x480 pixels) resolutions.
+
+ To compile this driver as a module, choose M here: the
+ module will be called arv.
+
+config VIDEO_OMAP3
+ tristate "OMAP 3 Camera support (EXPERIMENTAL)"
+ depends on OMAP_IOVMM && VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3 && EXPERIMENTAL
+ ---help---
+ Driver for an OMAP 3 camera controller.
+
+config VIDEO_OMAP3_DEBUG
+ bool "OMAP 3 Camera debug messages"
+ depends on VIDEO_OMAP3
+ ---help---
+ Enable debug messages on OMAP 3 camera controller driver.
+
+config SOC_CAMERA
+ tristate "SoC camera support"
+ depends on VIDEO_V4L2 && HAS_DMA && I2C
+ select VIDEOBUF_GEN
+ select VIDEOBUF2_CORE
+ help
+ SoC Camera is a common API to several cameras, not connecting
+ over a bus like PCI or USB. For example some i2c camera connected
+ directly to the data bus of an SoC.
+
+config SOC_CAMERA_IMX074
+ tristate "imx074 support"
+ depends on SOC_CAMERA && I2C
+ help
+ This driver supports IMX074 cameras from Sony
+
+config SOC_CAMERA_MT9M001
+ tristate "mt9m001 support"
+ depends on SOC_CAMERA && I2C
+ select GPIO_PCA953X if MT9M001_PCA9536_SWITCH
+ help
+ This driver supports MT9M001 cameras from Micron, monochrome
+ and colour models.
+
+config SOC_CAMERA_MT9M111
+ tristate "mt9m111, mt9m112 and mt9m131 support"
+ depends on SOC_CAMERA && I2C
+ help
+ This driver supports MT9M111, MT9M112 and MT9M131 cameras from
+ Micron/Aptina
+
+config SOC_CAMERA_MT9T031
+ tristate "mt9t031 support"
+ depends on SOC_CAMERA && I2C
+ help
+ This driver supports MT9T031 cameras from Micron.
+
+config SOC_CAMERA_MT9T112
+ tristate "mt9t112 support"
+ depends on SOC_CAMERA && I2C
+ help
+ This driver supports MT9T112 cameras from Aptina.
+
+config SOC_CAMERA_MT9V022
+ tristate "mt9v022 support"
+ depends on SOC_CAMERA && I2C
+ select GPIO_PCA953X if MT9V022_PCA9536_SWITCH
+ help
+ This driver supports MT9V022 cameras from Micron
+
+config SOC_CAMERA_RJ54N1
+ tristate "rj54n1cb0c support"
+ depends on SOC_CAMERA && I2C
+ help
+ This is a rj54n1cb0c video driver
+
+config SOC_CAMERA_TW9910
+ tristate "tw9910 support"
+ depends on SOC_CAMERA && I2C
+ help
+ This is a tw9910 video driver
+
+config SOC_CAMERA_PLATFORM
+ tristate "platform camera support"
+ depends on SOC_CAMERA
+ help
+ This is a generic SoC camera platform driver, useful for testing
+
+config SOC_CAMERA_OV2640
+ tristate "ov2640 camera support"
+ depends on SOC_CAMERA && I2C
+ help
+ This is a ov2640 camera driver
+
+config SOC_CAMERA_OV5642
+ tristate "ov5642 camera support"
+ depends on SOC_CAMERA && I2C
+ help
+ This is a V4L2 camera driver for the OmniVision OV5642 sensor
+
+config SOC_CAMERA_OV6650
+ tristate "ov6650 sensor support"
+ depends on SOC_CAMERA && I2C
+ ---help---
+ This is a V4L2 SoC camera driver for the OmniVision OV6650 sensor
+
+config SOC_CAMERA_OV772X
+ tristate "ov772x camera support"
+ depends on SOC_CAMERA && I2C
+ help
+ This is a ov772x camera driver
+
+config SOC_CAMERA_OV9640
+ tristate "ov9640 camera support"
+ depends on SOC_CAMERA && I2C
+ help
+ This is a ov9640 camera driver
+
+config SOC_CAMERA_OV9740
+ tristate "ov9740 camera support"
+ depends on SOC_CAMERA && I2C
+ help
+ This is a ov9740 camera driver
+
+config MX1_VIDEO
+ bool
+
+config VIDEO_MX1
+ tristate "i.MX1/i.MXL CMOS Sensor Interface driver"
+ depends on VIDEO_DEV && ARCH_MX1 && SOC_CAMERA
+ select FIQ
+ select VIDEOBUF_DMA_CONTIG
+ select MX1_VIDEO
+ ---help---
+ This is a v4l2 driver for the i.MX1/i.MXL CMOS Sensor Interface
+
+config MX3_VIDEO
+ bool
+
+config VIDEO_MX3
+ tristate "i.MX3x Camera Sensor Interface driver"
+ depends on VIDEO_DEV && MX3_IPU && SOC_CAMERA
+ select VIDEOBUF2_DMA_CONTIG
+ select MX3_VIDEO
+ ---help---
+ This is a v4l2 driver for the i.MX3x Camera Sensor Interface
+
+config VIDEO_PXA27x
+ tristate "PXA27x Quick Capture Interface driver"
+ depends on VIDEO_DEV && PXA27x && SOC_CAMERA
+ select VIDEOBUF_DMA_SG
+ ---help---
+ This is a v4l2 driver for the PXA27x Quick Capture Interface
+
+config VIDEO_SH_MOBILE_CSI2
+ tristate "SuperH Mobile MIPI CSI-2 Interface driver"
+ depends on VIDEO_DEV && SOC_CAMERA && HAVE_CLK
+ ---help---
+ This is a v4l2 driver for the SuperH MIPI CSI-2 Interface
+
+config VIDEO_SH_MOBILE_CEU
+ tristate "SuperH Mobile CEU Interface driver"
+ depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA && HAVE_CLK
+ select VIDEOBUF2_DMA_CONTIG
+ ---help---
+ This is a v4l2 driver for the SuperH Mobile CEU Interface
+
+config VIDEO_OMAP1
+ tristate "OMAP1 Camera Interface driver"
+ depends on VIDEO_DEV && ARCH_OMAP1 && SOC_CAMERA
+ select VIDEOBUF_DMA_CONTIG
+ select VIDEOBUF_DMA_SG
+ ---help---
+ This is a v4l2 driver for the TI OMAP1 camera interface
+
+config VIDEO_OMAP2
+ tristate "OMAP2 Camera Capture Interface driver"
+ depends on VIDEO_DEV && ARCH_OMAP2
+ select VIDEOBUF_DMA_SG
+ ---help---
+ This is a v4l2 driver for the TI OMAP2 camera capture interface
+
+config VIDEO_MX2_HOSTSUPPORT
+ bool
+
+config VIDEO_MX2
+ tristate "i.MX27/i.MX25 Camera Sensor Interface driver"
+ depends on VIDEO_DEV && SOC_CAMERA && (MACH_MX27 || ARCH_MX25)
+ select VIDEOBUF2_DMA_CONTIG
+ select VIDEO_MX2_HOSTSUPPORT
+ ---help---
+ This is a v4l2 driver for the i.MX27 and the i.MX25 Camera Sensor
+ Interface
+
+config VIDEO_SAMSUNG_S5P_FIMC
+ tristate "Samsung S5P and EXYNOS4 camera interface driver (EXPERIMENTAL)"
+ depends on VIDEO_V4L2 && I2C && PLAT_S5P && PM_RUNTIME && \
+ VIDEO_V4L2_SUBDEV_API && EXPERIMENTAL
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ ---help---
+ This is a v4l2 driver for Samsung S5P and EXYNOS4 camera
+ host interface and video postprocessor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called s5p-fimc.
+
+config VIDEO_ATMEL_ISI
+ tristate "ATMEL Image Sensor Interface (ISI) support"
+ depends on VIDEO_DEV && SOC_CAMERA && ARCH_AT91
+ select VIDEOBUF2_DMA_CONTIG
+ ---help---
+ This module makes the ATMEL Image Sensor Interface available
+ as a v4l2 device.
+
+config VIDEO_S5P_MIPI_CSIS
+ tristate "Samsung S5P and EXYNOS4 MIPI CSI receiver driver"
+ depends on VIDEO_V4L2 && PM_RUNTIME && PLAT_S5P
+ depends on VIDEO_V4L2_SUBDEV_API && REGULATOR
+ ---help---
+ This is a v4l2 driver for Samsung S5P/EXYNOS4 MIPI-CSI receiver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called s5p-csis.
+
+source "drivers/media/video/s5p-tv/Kconfig"
+
+endif # V4L_PLATFORM_DRIVERS
+endif # VIDEO_CAPTURE_DRIVERS
+
+menuconfig V4L_MEM2MEM_DRIVERS
+ bool "Memory-to-memory multimedia devices"
+ depends on VIDEO_V4L2
+ default n
+ ---help---
+ Say Y here to enable selecting drivers for V4L devices that
+ use system memory for both source and destination buffers, as opposed
+ to capture and output drivers, which use memory buffers for just
+ one of those.
+
+if V4L_MEM2MEM_DRIVERS
+
+config VIDEO_MEM2MEM_TESTDEV
+ tristate "Virtual test device for mem2mem framework"
+ depends on VIDEO_DEV && VIDEO_V4L2
+ select VIDEOBUF2_VMALLOC
+ select V4L2_MEM2MEM_DEV
+ default n
+ ---help---
+ This is a virtual test device for the memory-to-memory driver
+ framework.
+
+config VIDEO_SAMSUNG_S5P_G2D
+ tristate "Samsung S5P and EXYNOS4 G2D 2d graphics accelerator driver"
+ depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ default n
+ ---help---
+ This is a v4l2 driver for Samsung S5P and EXYNOS4 G2D
+ 2d graphics accelerator.
+
+config VIDEO_SAMSUNG_S5P_JPEG
+ tristate "Samsung S5P/Exynos4 JPEG codec driver (EXPERIMENTAL)"
+ depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P && EXPERIMENTAL
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ ---help---
+ This is a v4l2 driver for Samsung S5P and EXYNOS4 JPEG codec
+
+config VIDEO_SAMSUNG_S5P_MFC
+ tristate "Samsung S5P MFC 5.1 Video Codec"
+ depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P
+ select VIDEOBUF2_DMA_CONTIG
+ default n
+ help
+ MFC 5.1 driver for V4L2.
+
+config VIDEO_MX2_EMMAPRP
+ tristate "MX2 eMMa-PrP support"
+ depends on VIDEO_DEV && VIDEO_V4L2 && SOC_IMX27
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ MX2X chips have a PrP that can be used to process buffers from
+ memory to memory. Operations include resizing and format
+ conversion.
+
+endif # V4L_MEM2MEM_DRIVERS
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
new file mode 100644
index 00000000..30d61a71
--- /dev/null
+++ b/drivers/media/video/Makefile
@@ -0,0 +1,220 @@
+#
+# Makefile for the video capture/playback device drivers.
+#
+
+tuner-objs := tuner-core.o
+
+msp3400-objs := msp3400-driver.o msp3400-kthreads.o
+
+stkwebcam-objs := stk-webcam.o stk-sensor.o
+
+omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o
+
+videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \
+ v4l2-event.o v4l2-ctrls.o v4l2-subdev.o
+ifeq ($(CONFIG_COMPAT),y)
+ videodev-objs += v4l2-compat-ioctl32.o
+endif
+
+# V4L2 core modules
+
+obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-int-device.o
+obj-$(CONFIG_VIDEO_V4L2_COMMON) += v4l2-common.o
+
+# Helper modules
+
+obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o
+
+# All i2c modules must come first:
+
+obj-$(CONFIG_VIDEO_TUNER) += tuner.o
+obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o
+obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o
+obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o
+obj-$(CONFIG_VIDEO_TDA9840) += tda9840.o
+obj-$(CONFIG_VIDEO_TEA6415C) += tea6415c.o
+obj-$(CONFIG_VIDEO_TEA6420) += tea6420.o
+obj-$(CONFIG_VIDEO_SAA7110) += saa7110.o
+obj-$(CONFIG_VIDEO_SAA711X) += saa7115.o
+obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o
+obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o
+obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o
+obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o
+obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o
+obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o
+obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o
+obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o
+obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o
+obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o
+obj-$(CONFIG_VIDEO_VS6624) += vs6624.o
+obj-$(CONFIG_VIDEO_BT819) += bt819.o
+obj-$(CONFIG_VIDEO_BT856) += bt856.o
+obj-$(CONFIG_VIDEO_BT866) += bt866.o
+obj-$(CONFIG_VIDEO_KS0127) += ks0127.o
+obj-$(CONFIG_VIDEO_THS7303) += ths7303.o
+obj-$(CONFIG_VIDEO_VINO) += indycam.o
+obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o
+obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o
+obj-$(CONFIG_VIDEO_TVP7002) += tvp7002.o
+obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o
+obj-$(CONFIG_VIDEO_CS5345) += cs5345.o
+obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o
+obj-$(CONFIG_VIDEO_M52790) += m52790.o
+obj-$(CONFIG_VIDEO_TLV320AIC23B) += tlv320aic23b.o
+obj-$(CONFIG_VIDEO_WM8775) += wm8775.o
+obj-$(CONFIG_VIDEO_WM8739) += wm8739.o
+obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o
+obj-$(CONFIG_VIDEO_CX25840) += cx25840/
+obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o
+obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
+obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
+obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o
+obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
+obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o
+obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o
+obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o
+obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
+obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
+obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o
+obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o
+obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/
+obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o
+obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o
+obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o
+
+obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o
+obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o
+obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o
+obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o
+obj-$(CONFIG_SOC_CAMERA_MT9T112) += mt9t112.o
+obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o
+obj-$(CONFIG_SOC_CAMERA_OV2640) += ov2640.o
+obj-$(CONFIG_SOC_CAMERA_OV5642) += ov5642.o
+obj-$(CONFIG_SOC_CAMERA_OV6650) += ov6650.o
+obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o
+obj-$(CONFIG_SOC_CAMERA_OV9640) += ov9640.o
+obj-$(CONFIG_SOC_CAMERA_OV9740) += ov9740.o
+obj-$(CONFIG_SOC_CAMERA_RJ54N1) += rj54n1cb0c.o
+obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o
+
+# And now the v4l2 drivers:
+
+obj-$(CONFIG_VIDEO_BT848) += bt8xx/
+obj-$(CONFIG_VIDEO_ZORAN) += zoran/
+obj-$(CONFIG_VIDEO_CQCAM) += c-qcam.o
+obj-$(CONFIG_VIDEO_BWQCAM) += bw-qcam.o
+obj-$(CONFIG_VIDEO_W9966) += w9966.o
+obj-$(CONFIG_VIDEO_PMS) += pms.o
+obj-$(CONFIG_VIDEO_VINO) += vino.o
+obj-$(CONFIG_VIDEO_MEYE) += meye.o
+obj-$(CONFIG_VIDEO_SAA7134) += saa7134/
+obj-$(CONFIG_VIDEO_CX88) += cx88/
+obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
+obj-$(CONFIG_VIDEO_TLG2300) += tlg2300/
+obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/
+obj-$(CONFIG_VIDEO_CX25821) += cx25821/
+obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
+obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/
+obj-$(CONFIG_VIDEO_CPIA2) += cpia2/
+obj-$(CONFIG_VIDEO_TM6000) += tm6000/
+obj-$(CONFIG_VIDEO_MXB) += mxb.o
+obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o
+obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o
+obj-$(CONFIG_VIDEO_TIMBERDALE) += timblogiw.o
+
+obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
+obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o
+obj-$(CONFIG_VIDEOBUF_DMA_CONTIG) += videobuf-dma-contig.o
+obj-$(CONFIG_VIDEOBUF_VMALLOC) += videobuf-vmalloc.o
+obj-$(CONFIG_VIDEOBUF_DVB) += videobuf-dvb.o
+obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o
+
+obj-$(CONFIG_VIDEOBUF2_CORE) += videobuf2-core.o
+obj-$(CONFIG_VIDEOBUF2_MEMOPS) += videobuf2-memops.o
+obj-$(CONFIG_VIDEOBUF2_VMALLOC) += videobuf2-vmalloc.o
+obj-$(CONFIG_VIDEOBUF2_DMA_CONTIG) += videobuf2-dma-contig.o
+obj-$(CONFIG_VIDEOBUF2_DMA_SG) += videobuf2-dma-sg.o
+
+obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
+
+obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o
+
+obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o
+
+#g_wmt_cmoscam-objs += cmoscam.o
+#obj-$(CONFIG_WMT_V4L2_CMOS) += g_wmt_cmoscam.o
+
+#g_wmt_tv-decoder-objs += tv-decoder.o
+#obj-$(CONFIG_WMT_V4L2_TV_DECODER) += g_wmt_tv-decoder.o
+
+obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/
+obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/
+
+obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o
+
+obj-$(CONFIG_VIDEO_OMAP3) += omap3isp/
+
+obj-$(CONFIG_USB_ZR364XX) += zr364xx.o
+obj-$(CONFIG_USB_STKWEBCAM) += stkwebcam.o
+
+obj-$(CONFIG_USB_SN9C102) += sn9c102/
+obj-$(CONFIG_USB_ET61X251) += et61x251/
+obj-$(CONFIG_USB_PWC) += pwc/
+obj-$(CONFIG_USB_GSPCA) += gspca/
+
+obj-$(CONFIG_VIDEO_HDPVR) += hdpvr/
+
+obj-$(CONFIG_USB_S2255) += s2255drv.o
+
+obj-$(CONFIG_VIDEO_IVTV) += ivtv/
+obj-$(CONFIG_VIDEO_CX18) += cx18/
+
+obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o
+obj-$(CONFIG_VIDEO_VIVI) += vivi.o
+obj-$(CONFIG_VIDEO_MEM2MEM_TESTDEV) += mem2mem_testdev.o
+obj-$(CONFIG_VIDEO_CX23885) += cx23885/
+
+obj-$(CONFIG_VIDEO_AK881X) += ak881x.o
+
+obj-$(CONFIG_VIDEO_OMAP2) += omap2cam.o
+obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o
+obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o
+# soc-camera host drivers have to be linked after camera drivers
+obj-$(CONFIG_VIDEO_MX1) += mx1_camera.o
+obj-$(CONFIG_VIDEO_MX2) += mx2_camera.o
+obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o
+obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o
+obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2) += sh_mobile_csi2.o
+obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o
+obj-$(CONFIG_VIDEO_OMAP1) += omap1_camera.o
+obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel-isi.o
+
+obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o
+
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc/
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) += s5p-mfc/
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV) += s5p-tv/
+
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D) += s5p-g2d/
+
+obj-$(CONFIG_BLACKFIN) += blackfin/
+
+obj-$(CONFIG_ARCH_DAVINCI) += davinci/
+
+obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o
+
+obj-$(CONFIG_VIDEO_AU0828) += au0828/
+
+obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/
+obj-$(CONFIG_VIDEO_SAA7164) += saa7164/
+
+obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o
+
+obj-y += davinci/
+
+obj-$(CONFIG_ARCH_OMAP) += omap/
+
+ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
+ccflags-y += -I$(srctree)/drivers/media/dvb/frontends
+ccflags-y += -I$(srctree)/drivers/media/common/tuners
diff --git a/drivers/media/video/adp1653.c b/drivers/media/video/adp1653.c
new file mode 100644
index 00000000..5b045b4a
--- /dev/null
+++ b/drivers/media/video/adp1653.c
@@ -0,0 +1,488 @@
+/*
+ * drivers/media/video/adp1653.c
+ *
+ * Copyright (C) 2008--2011 Nokia Corporation
+ *
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ *
+ * Contributors:
+ * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ * Tuukka Toivonen <tuukkat76@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * TODO:
+ * - fault interrupt handling
+ * - hardware strobe
+ * - power doesn't need to be ON if all lights are off
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <media/adp1653.h>
+#include <media/v4l2-device.h>
+
+#define TIMEOUT_MAX 820000
+#define TIMEOUT_STEP 54600
+#define TIMEOUT_MIN (TIMEOUT_MAX - ADP1653_REG_CONFIG_TMR_SET_MAX \
+ * TIMEOUT_STEP)
+#define TIMEOUT_US_TO_CODE(t) ((TIMEOUT_MAX + (TIMEOUT_STEP / 2) - (t)) \
+ / TIMEOUT_STEP)
+#define TIMEOUT_CODE_TO_US(c) (TIMEOUT_MAX - (c) * TIMEOUT_STEP)
+
+/* Write values into ADP1653 registers. */
+static int adp1653_update_hw(struct adp1653_flash *flash)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+ u8 out_sel;
+ u8 config = 0;
+ int rval;
+
+ out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG(
+ flash->indicator_intensity->val)
+ << ADP1653_REG_OUT_SEL_ILED_SHIFT;
+
+ switch (flash->led_mode->val) {
+ case V4L2_FLASH_LED_MODE_NONE:
+ break;
+ case V4L2_FLASH_LED_MODE_FLASH:
+ /* Flash mode, light on with strobe, duration from timer */
+ config = ADP1653_REG_CONFIG_TMR_CFG;
+ config |= TIMEOUT_US_TO_CODE(flash->flash_timeout->val)
+ << ADP1653_REG_CONFIG_TMR_SET_SHIFT;
+ break;
+ case V4L2_FLASH_LED_MODE_TORCH:
+ /* Torch mode, light immediately on, duration indefinite */
+ out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG(
+ flash->torch_intensity->val)
+ << ADP1653_REG_OUT_SEL_HPLED_SHIFT;
+ break;
+ }
+
+ rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel);
+ if (rval < 0)
+ return rval;
+
+ rval = i2c_smbus_write_byte_data(client, ADP1653_REG_CONFIG, config);
+ if (rval < 0)
+ return rval;
+
+ return 0;
+}
+
+static int adp1653_get_fault(struct adp1653_flash *flash)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+ int fault;
+ int rval;
+
+ fault = i2c_smbus_read_byte_data(client, ADP1653_REG_FAULT);
+ if (IS_ERR_VALUE(fault))
+ return fault;
+
+ flash->fault |= fault;
+
+ if (!flash->fault)
+ return 0;
+
+ /* Clear faults. */
+ rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0);
+ if (IS_ERR_VALUE(rval))
+ return rval;
+
+ flash->led_mode->val = V4L2_FLASH_LED_MODE_NONE;
+
+ rval = adp1653_update_hw(flash);
+ if (IS_ERR_VALUE(rval))
+ return rval;
+
+ return flash->fault;
+}
+
+static int adp1653_strobe(struct adp1653_flash *flash, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+ u8 out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG(
+ flash->indicator_intensity->val)
+ << ADP1653_REG_OUT_SEL_ILED_SHIFT;
+ int rval;
+
+ if (flash->led_mode->val != V4L2_FLASH_LED_MODE_FLASH)
+ return -EBUSY;
+
+ if (!enable)
+ return i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL,
+ out_sel);
+
+ out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG(
+ flash->flash_intensity->val)
+ << ADP1653_REG_OUT_SEL_HPLED_SHIFT;
+ rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel);
+ if (rval)
+ return rval;
+
+ /* Software strobe using i2c */
+ rval = i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE,
+ ADP1653_REG_SW_STROBE_SW_STROBE);
+ if (rval)
+ return rval;
+ return i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE, 0);
+}
+
+/* --------------------------------------------------------------------------
+ * V4L2 controls
+ */
+
+static int adp1653_get_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct adp1653_flash *flash =
+ container_of(ctrl->handler, struct adp1653_flash, ctrls);
+ int rval;
+
+ rval = adp1653_get_fault(flash);
+ if (IS_ERR_VALUE(rval))
+ return rval;
+
+ ctrl->cur.val = 0;
+
+ if (flash->fault & ADP1653_REG_FAULT_FLT_SCP)
+ ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT;
+ if (flash->fault & ADP1653_REG_FAULT_FLT_OT)
+ ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE;
+ if (flash->fault & ADP1653_REG_FAULT_FLT_TMR)
+ ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT;
+ if (flash->fault & ADP1653_REG_FAULT_FLT_OV)
+ ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE;
+
+ flash->fault = 0;
+
+ return 0;
+}
+
+static int adp1653_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct adp1653_flash *flash =
+ container_of(ctrl->handler, struct adp1653_flash, ctrls);
+ int rval;
+
+ rval = adp1653_get_fault(flash);
+ if (IS_ERR_VALUE(rval))
+ return rval;
+ if ((rval & (ADP1653_REG_FAULT_FLT_SCP |
+ ADP1653_REG_FAULT_FLT_OT |
+ ADP1653_REG_FAULT_FLT_OV)) &&
+ (ctrl->id == V4L2_CID_FLASH_STROBE ||
+ ctrl->id == V4L2_CID_FLASH_TORCH_INTENSITY ||
+ ctrl->id == V4L2_CID_FLASH_LED_MODE))
+ return -EBUSY;
+
+ switch (ctrl->id) {
+ case V4L2_CID_FLASH_STROBE:
+ return adp1653_strobe(flash, 1);
+ case V4L2_CID_FLASH_STROBE_STOP:
+ return adp1653_strobe(flash, 0);
+ }
+
+ return adp1653_update_hw(flash);
+}
+
+static const struct v4l2_ctrl_ops adp1653_ctrl_ops = {
+ .g_volatile_ctrl = adp1653_get_ctrl,
+ .s_ctrl = adp1653_set_ctrl,
+};
+
+static int adp1653_init_controls(struct adp1653_flash *flash)
+{
+ struct v4l2_ctrl *fault;
+
+ v4l2_ctrl_handler_init(&flash->ctrls, 9);
+
+ flash->led_mode =
+ v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops,
+ V4L2_CID_FLASH_LED_MODE,
+ V4L2_FLASH_LED_MODE_TORCH, ~0x7, 0);
+ v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops,
+ V4L2_CID_FLASH_STROBE_SOURCE,
+ V4L2_FLASH_STROBE_SOURCE_SOFTWARE, ~0x1, 0);
+ v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+ V4L2_CID_FLASH_STROBE, 0, 0, 0, 0);
+ v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+ V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0);
+ flash->flash_timeout =
+ v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+ V4L2_CID_FLASH_TIMEOUT, TIMEOUT_MIN,
+ flash->platform_data->max_flash_timeout,
+ TIMEOUT_STEP,
+ flash->platform_data->max_flash_timeout);
+ flash->flash_intensity =
+ v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+ V4L2_CID_FLASH_INTENSITY,
+ ADP1653_FLASH_INTENSITY_MIN,
+ flash->platform_data->max_flash_intensity,
+ 1, flash->platform_data->max_flash_intensity);
+ flash->torch_intensity =
+ v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+ V4L2_CID_FLASH_TORCH_INTENSITY,
+ ADP1653_TORCH_INTENSITY_MIN,
+ flash->platform_data->max_torch_intensity,
+ ADP1653_FLASH_INTENSITY_STEP,
+ flash->platform_data->max_torch_intensity);
+ flash->indicator_intensity =
+ v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+ V4L2_CID_FLASH_INDICATOR_INTENSITY,
+ ADP1653_INDICATOR_INTENSITY_MIN,
+ flash->platform_data->max_indicator_intensity,
+ ADP1653_INDICATOR_INTENSITY_STEP,
+ ADP1653_INDICATOR_INTENSITY_MIN);
+ fault = v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+ V4L2_CID_FLASH_FAULT, 0,
+ V4L2_FLASH_FAULT_OVER_VOLTAGE
+ | V4L2_FLASH_FAULT_OVER_TEMPERATURE
+ | V4L2_FLASH_FAULT_SHORT_CIRCUIT, 0, 0);
+
+ if (flash->ctrls.error)
+ return flash->ctrls.error;
+
+ fault->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ flash->subdev.ctrl_handler = &flash->ctrls;
+ return 0;
+}
+
+/* --------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+static int
+adp1653_init_device(struct adp1653_flash *flash)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+ int rval;
+
+ /* Clear FAULT register by writing zero to OUT_SEL */
+ rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0);
+ if (rval < 0) {
+ dev_err(&client->dev, "failed writing fault register\n");
+ return -EIO;
+ }
+
+ mutex_lock(&flash->ctrls.lock);
+ /* Reset faults before reading new ones. */
+ flash->fault = 0;
+ rval = adp1653_get_fault(flash);
+ mutex_unlock(&flash->ctrls.lock);
+ if (rval > 0) {
+ dev_err(&client->dev, "faults detected: 0x%1.1x\n", rval);
+ return -EIO;
+ }
+
+ mutex_lock(&flash->ctrls.lock);
+ rval = adp1653_update_hw(flash);
+ mutex_unlock(&flash->ctrls.lock);
+ if (rval) {
+ dev_err(&client->dev,
+ "adp1653_update_hw failed at %s\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int
+__adp1653_set_power(struct adp1653_flash *flash, int on)
+{
+ int ret;
+
+ ret = flash->platform_data->power(&flash->subdev, on);
+ if (ret < 0)
+ return ret;
+
+ if (!on)
+ return 0;
+
+ ret = adp1653_init_device(flash);
+ if (ret < 0)
+ flash->platform_data->power(&flash->subdev, 0);
+
+ return ret;
+}
+
+static int
+adp1653_set_power(struct v4l2_subdev *subdev, int on)
+{
+ struct adp1653_flash *flash = to_adp1653_flash(subdev);
+ int ret = 0;
+
+ mutex_lock(&flash->power_lock);
+
+ /* If the power count is modified from 0 to != 0 or from != 0 to 0,
+ * update the power state.
+ */
+ if (flash->power_count == !on) {
+ ret = __adp1653_set_power(flash, !!on);
+ if (ret < 0)
+ goto done;
+ }
+
+ /* Update the power count. */
+ flash->power_count += on ? 1 : -1;
+ WARN_ON(flash->power_count < 0);
+
+done:
+ mutex_unlock(&flash->power_lock);
+ return ret;
+}
+
+static int adp1653_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ return adp1653_set_power(sd, 1);
+}
+
+static int adp1653_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ return adp1653_set_power(sd, 0);
+}
+
+static const struct v4l2_subdev_core_ops adp1653_core_ops = {
+ .s_power = adp1653_set_power,
+};
+
+static const struct v4l2_subdev_ops adp1653_ops = {
+ .core = &adp1653_core_ops,
+};
+
+static const struct v4l2_subdev_internal_ops adp1653_internal_ops = {
+ .open = adp1653_open,
+ .close = adp1653_close,
+};
+
+/* --------------------------------------------------------------------------
+ * I2C driver
+ */
+#ifdef CONFIG_PM
+
+static int adp1653_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct adp1653_flash *flash = to_adp1653_flash(subdev);
+
+ if (!flash->power_count)
+ return 0;
+
+ return __adp1653_set_power(flash, 0);
+}
+
+static int adp1653_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct adp1653_flash *flash = to_adp1653_flash(subdev);
+
+ if (!flash->power_count)
+ return 0;
+
+ return __adp1653_set_power(flash, 1);
+}
+
+#else
+
+#define adp1653_suspend NULL
+#define adp1653_resume NULL
+
+#endif /* CONFIG_PM */
+
+static int adp1653_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct adp1653_flash *flash;
+ int ret;
+
+ /* we couldn't work without platform data */
+ if (client->dev.platform_data == NULL)
+ return -ENODEV;
+
+ flash = kzalloc(sizeof(*flash), GFP_KERNEL);
+ if (flash == NULL)
+ return -ENOMEM;
+
+ flash->platform_data = client->dev.platform_data;
+
+ mutex_init(&flash->power_lock);
+
+ v4l2_i2c_subdev_init(&flash->subdev, client, &adp1653_ops);
+ flash->subdev.internal_ops = &adp1653_internal_ops;
+ flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ ret = adp1653_init_controls(flash);
+ if (ret)
+ goto free_and_quit;
+
+ ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0);
+ if (ret < 0)
+ goto free_and_quit;
+
+ flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
+
+ return 0;
+
+free_and_quit:
+ v4l2_ctrl_handler_free(&flash->ctrls);
+ kfree(flash);
+ return ret;
+}
+
+static int __exit adp1653_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct adp1653_flash *flash = to_adp1653_flash(subdev);
+
+ v4l2_device_unregister_subdev(&flash->subdev);
+ v4l2_ctrl_handler_free(&flash->ctrls);
+ media_entity_cleanup(&flash->subdev.entity);
+ kfree(flash);
+ return 0;
+}
+
+static const struct i2c_device_id adp1653_id_table[] = {
+ { ADP1653_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adp1653_id_table);
+
+static struct dev_pm_ops adp1653_pm_ops = {
+ .suspend = adp1653_suspend,
+ .resume = adp1653_resume,
+};
+
+static struct i2c_driver adp1653_i2c_driver = {
+ .driver = {
+ .name = ADP1653_NAME,
+ .pm = &adp1653_pm_ops,
+ },
+ .probe = adp1653_probe,
+ .remove = __exit_p(adp1653_remove),
+ .id_table = adp1653_id_table,
+};
+
+module_i2c_driver(adp1653_i2c_driver);
+
+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
+MODULE_DESCRIPTION("Analog Devices ADP1653 LED flash driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/adv7170.c b/drivers/media/video/adv7170.c
new file mode 100644
index 00000000..6bc01fb9
--- /dev/null
+++ b/drivers/media/video/adv7170.c
@@ -0,0 +1,410 @@
+/*
+ * adv7170 - adv7170, adv7171 video encoder driver version 0.0.1
+ *
+ * Copyright (C) 2002 Maxim Yevtyushkin <max@linuxmedialabs.com>
+ *
+ * Based on adv7176 driver by:
+ *
+ * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+ * Copyright (C) 1999 Wolfgang Scherr <scherr@net4you.net>
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ * - some corrections for Pinnacle Systems Inc. DC10plus card.
+ *
+ * Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * - moved over to linux>=2.4.x i2c protocol (1/1/2003)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/ioctl.h>
+#include <asm/uaccess.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+
+MODULE_DESCRIPTION("Analog Devices ADV7170 video encoder driver");
+MODULE_AUTHOR("Maxim Yevtyushkin");
+MODULE_LICENSE("GPL");
+
+
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+/* ----------------------------------------------------------------------- */
+
+struct adv7170 {
+ struct v4l2_subdev sd;
+ unsigned char reg[128];
+
+ v4l2_std_id norm;
+ int input;
+};
+
+static inline struct adv7170 *to_adv7170(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct adv7170, sd);
+}
+
+static char *inputs[] = { "pass_through", "play_back" };
+
+static enum v4l2_mbus_pixelcode adv7170_codes[] = {
+ V4L2_MBUS_FMT_UYVY8_2X8,
+ V4L2_MBUS_FMT_UYVY8_1X16,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static inline int adv7170_write(struct v4l2_subdev *sd, u8 reg, u8 value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct adv7170 *encoder = to_adv7170(sd);
+
+ encoder->reg[reg] = value;
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static inline int adv7170_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int adv7170_write_block(struct v4l2_subdev *sd,
+ const u8 *data, unsigned int len)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct adv7170 *encoder = to_adv7170(sd);
+ int ret = -1;
+ u8 reg;
+
+ /* the adv7170 has an autoincrement function, use it if
+ * the adapter understands raw I2C */
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ /* do raw I2C, not smbus compatible */
+ u8 block_data[32];
+ int block_len;
+
+ while (len >= 2) {
+ block_len = 0;
+ block_data[block_len++] = reg = data[0];
+ do {
+ block_data[block_len++] =
+ encoder->reg[reg++] = data[1];
+ len -= 2;
+ data += 2;
+ } while (len >= 2 && data[0] == reg && block_len < 32);
+ ret = i2c_master_send(client, block_data, block_len);
+ if (ret < 0)
+ break;
+ }
+ } else {
+ /* do some slow I2C emulation kind of thing */
+ while (len >= 2) {
+ reg = *data++;
+ ret = adv7170_write(sd, reg, *data++);
+ if (ret < 0)
+ break;
+ len -= 2;
+ }
+ }
+ return ret;
+}
+
+/* ----------------------------------------------------------------------- */
+
+#define TR0MODE 0x4c
+#define TR0RST 0x80
+
+#define TR1CAPT 0x00
+#define TR1PLAY 0x00
+
+static const unsigned char init_NTSC[] = {
+ 0x00, 0x10, /* MR0 */
+ 0x01, 0x20, /* MR1 */
+ 0x02, 0x0e, /* MR2 RTC control: bits 2 and 1 */
+ 0x03, 0x80, /* MR3 */
+ 0x04, 0x30, /* MR4 */
+ 0x05, 0x00, /* Reserved */
+ 0x06, 0x00, /* Reserved */
+ 0x07, TR0MODE, /* TM0 */
+ 0x08, TR1CAPT, /* TM1 */
+ 0x09, 0x16, /* Fsc0 */
+ 0x0a, 0x7c, /* Fsc1 */
+ 0x0b, 0xf0, /* Fsc2 */
+ 0x0c, 0x21, /* Fsc3 */
+ 0x0d, 0x00, /* Subcarrier Phase */
+ 0x0e, 0x00, /* Closed Capt. Ext 0 */
+ 0x0f, 0x00, /* Closed Capt. Ext 1 */
+ 0x10, 0x00, /* Closed Capt. 0 */
+ 0x11, 0x00, /* Closed Capt. 1 */
+ 0x12, 0x00, /* Pedestal Ctl 0 */
+ 0x13, 0x00, /* Pedestal Ctl 1 */
+ 0x14, 0x00, /* Pedestal Ctl 2 */
+ 0x15, 0x00, /* Pedestal Ctl 3 */
+ 0x16, 0x00, /* CGMS_WSS_0 */
+ 0x17, 0x00, /* CGMS_WSS_1 */
+ 0x18, 0x00, /* CGMS_WSS_2 */
+ 0x19, 0x00, /* Teletext Ctl */
+};
+
+static const unsigned char init_PAL[] = {
+ 0x00, 0x71, /* MR0 */
+ 0x01, 0x20, /* MR1 */
+ 0x02, 0x0e, /* MR2 RTC control: bits 2 and 1 */
+ 0x03, 0x80, /* MR3 */
+ 0x04, 0x30, /* MR4 */
+ 0x05, 0x00, /* Reserved */
+ 0x06, 0x00, /* Reserved */
+ 0x07, TR0MODE, /* TM0 */
+ 0x08, TR1CAPT, /* TM1 */
+ 0x09, 0xcb, /* Fsc0 */
+ 0x0a, 0x8a, /* Fsc1 */
+ 0x0b, 0x09, /* Fsc2 */
+ 0x0c, 0x2a, /* Fsc3 */
+ 0x0d, 0x00, /* Subcarrier Phase */
+ 0x0e, 0x00, /* Closed Capt. Ext 0 */
+ 0x0f, 0x00, /* Closed Capt. Ext 1 */
+ 0x10, 0x00, /* Closed Capt. 0 */
+ 0x11, 0x00, /* Closed Capt. 1 */
+ 0x12, 0x00, /* Pedestal Ctl 0 */
+ 0x13, 0x00, /* Pedestal Ctl 1 */
+ 0x14, 0x00, /* Pedestal Ctl 2 */
+ 0x15, 0x00, /* Pedestal Ctl 3 */
+ 0x16, 0x00, /* CGMS_WSS_0 */
+ 0x17, 0x00, /* CGMS_WSS_1 */
+ 0x18, 0x00, /* CGMS_WSS_2 */
+ 0x19, 0x00, /* Teletext Ctl */
+};
+
+
+static int adv7170_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct adv7170 *encoder = to_adv7170(sd);
+
+ v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std);
+
+ if (std & V4L2_STD_NTSC) {
+ adv7170_write_block(sd, init_NTSC, sizeof(init_NTSC));
+ if (encoder->input == 0)
+ adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */
+ adv7170_write(sd, 0x07, TR0MODE | TR0RST);
+ adv7170_write(sd, 0x07, TR0MODE);
+ } else if (std & V4L2_STD_PAL) {
+ adv7170_write_block(sd, init_PAL, sizeof(init_PAL));
+ if (encoder->input == 0)
+ adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */
+ adv7170_write(sd, 0x07, TR0MODE | TR0RST);
+ adv7170_write(sd, 0x07, TR0MODE);
+ } else {
+ v4l2_dbg(1, debug, sd, "illegal norm: %llx\n",
+ (unsigned long long)std);
+ return -EINVAL;
+ }
+ v4l2_dbg(1, debug, sd, "switched to %llx\n", (unsigned long long)std);
+ encoder->norm = std;
+ return 0;
+}
+
+static int adv7170_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct adv7170 *encoder = to_adv7170(sd);
+
+ /* RJ: input = 0: input is from decoder
+ input = 1: input is from ZR36060
+ input = 2: color bar */
+
+ v4l2_dbg(1, debug, sd, "set input from %s\n",
+ input == 0 ? "decoder" : "ZR36060");
+
+ switch (input) {
+ case 0:
+ adv7170_write(sd, 0x01, 0x20);
+ adv7170_write(sd, 0x08, TR1CAPT); /* TR1 */
+ adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */
+ adv7170_write(sd, 0x07, TR0MODE | TR0RST);
+ adv7170_write(sd, 0x07, TR0MODE);
+ /* udelay(10); */
+ break;
+
+ case 1:
+ adv7170_write(sd, 0x01, 0x00);
+ adv7170_write(sd, 0x08, TR1PLAY); /* TR1 */
+ adv7170_write(sd, 0x02, 0x08);
+ adv7170_write(sd, 0x07, TR0MODE | TR0RST);
+ adv7170_write(sd, 0x07, TR0MODE);
+ /* udelay(10); */
+ break;
+
+ default:
+ v4l2_dbg(1, debug, sd, "illegal input: %d\n", input);
+ return -EINVAL;
+ }
+ v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[input]);
+ encoder->input = input;
+ return 0;
+}
+
+static int adv7170_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= ARRAY_SIZE(adv7170_codes))
+ return -EINVAL;
+
+ *code = adv7170_codes[index];
+ return 0;
+}
+
+static int adv7170_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ u8 val = adv7170_read(sd, 0x7);
+
+ if ((val & 0x40) == (1 << 6))
+ mf->code = V4L2_MBUS_FMT_UYVY8_1X16;
+ else
+ mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
+
+ mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ mf->width = 0;
+ mf->height = 0;
+ mf->field = V4L2_FIELD_ANY;
+
+ return 0;
+}
+
+static int adv7170_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ u8 val = adv7170_read(sd, 0x7);
+ int ret;
+
+ switch (mf->code) {
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ val &= ~0x40;
+ break;
+
+ case V4L2_MBUS_FMT_UYVY8_1X16:
+ val |= 0x40;
+ break;
+
+ default:
+ v4l2_dbg(1, debug, sd,
+ "illegal v4l2_mbus_framefmt code: %d\n", mf->code);
+ return -EINVAL;
+ }
+
+ ret = adv7170_write(sd, 0x7, val);
+
+ return ret;
+}
+
+static int adv7170_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7170, 0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops adv7170_core_ops = {
+ .g_chip_ident = adv7170_g_chip_ident,
+};
+
+static const struct v4l2_subdev_video_ops adv7170_video_ops = {
+ .s_std_output = adv7170_s_std_output,
+ .s_routing = adv7170_s_routing,
+ .s_mbus_fmt = adv7170_s_fmt,
+ .g_mbus_fmt = adv7170_g_fmt,
+ .enum_mbus_fmt = adv7170_enum_fmt,
+};
+
+static const struct v4l2_subdev_ops adv7170_ops = {
+ .core = &adv7170_core_ops,
+ .video = &adv7170_video_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int adv7170_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct adv7170 *encoder;
+ struct v4l2_subdev *sd;
+ int i;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ encoder = kzalloc(sizeof(struct adv7170), GFP_KERNEL);
+ if (encoder == NULL)
+ return -ENOMEM;
+ sd = &encoder->sd;
+ v4l2_i2c_subdev_init(sd, client, &adv7170_ops);
+ encoder->norm = V4L2_STD_NTSC;
+ encoder->input = 0;
+
+ i = adv7170_write_block(sd, init_NTSC, sizeof(init_NTSC));
+ if (i >= 0) {
+ i = adv7170_write(sd, 0x07, TR0MODE | TR0RST);
+ i = adv7170_write(sd, 0x07, TR0MODE);
+ i = adv7170_read(sd, 0x12);
+ v4l2_dbg(1, debug, sd, "revision %d\n", i & 1);
+ }
+ if (i < 0)
+ v4l2_dbg(1, debug, sd, "init error 0x%x\n", i);
+ return 0;
+}
+
+static int adv7170_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_adv7170(sd));
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct i2c_device_id adv7170_id[] = {
+ { "adv7170", 0 },
+ { "adv7171", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adv7170_id);
+
+static struct i2c_driver adv7170_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "adv7170",
+ },
+ .probe = adv7170_probe,
+ .remove = adv7170_remove,
+ .id_table = adv7170_id,
+};
+
+module_i2c_driver(adv7170_driver);
diff --git a/drivers/media/video/adv7175.c b/drivers/media/video/adv7175.c
new file mode 100644
index 00000000..c7640fab
--- /dev/null
+++ b/drivers/media/video/adv7175.c
@@ -0,0 +1,460 @@
+/*
+ * adv7175 - adv7175a video encoder driver version 0.0.3
+ *
+ * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+ * Copyright (C) 1999 Wolfgang Scherr <scherr@net4you.net>
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ * - some corrections for Pinnacle Systems Inc. DC10plus card.
+ *
+ * Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * - moved over to linux>=2.4.x i2c protocol (9/9/2002)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/ioctl.h>
+#include <asm/uaccess.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+
+MODULE_DESCRIPTION("Analog Devices ADV7175 video encoder driver");
+MODULE_AUTHOR("Dave Perks");
+MODULE_LICENSE("GPL");
+
+#define I2C_ADV7175 0xd4
+#define I2C_ADV7176 0x54
+
+
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+/* ----------------------------------------------------------------------- */
+
+struct adv7175 {
+ struct v4l2_subdev sd;
+ v4l2_std_id norm;
+ int input;
+};
+
+static inline struct adv7175 *to_adv7175(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct adv7175, sd);
+}
+
+static char *inputs[] = { "pass_through", "play_back", "color_bar" };
+
+static enum v4l2_mbus_pixelcode adv7175_codes[] = {
+ V4L2_MBUS_FMT_UYVY8_2X8,
+ V4L2_MBUS_FMT_UYVY8_1X16,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static inline int adv7175_write(struct v4l2_subdev *sd, u8 reg, u8 value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static inline int adv7175_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int adv7175_write_block(struct v4l2_subdev *sd,
+ const u8 *data, unsigned int len)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret = -1;
+ u8 reg;
+
+ /* the adv7175 has an autoincrement function, use it if
+ * the adapter understands raw I2C */
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ /* do raw I2C, not smbus compatible */
+ u8 block_data[32];
+ int block_len;
+
+ while (len >= 2) {
+ block_len = 0;
+ block_data[block_len++] = reg = data[0];
+ do {
+ block_data[block_len++] = data[1];
+ reg++;
+ len -= 2;
+ data += 2;
+ } while (len >= 2 && data[0] == reg && block_len < 32);
+ ret = i2c_master_send(client, block_data, block_len);
+ if (ret < 0)
+ break;
+ }
+ } else {
+ /* do some slow I2C emulation kind of thing */
+ while (len >= 2) {
+ reg = *data++;
+ ret = adv7175_write(sd, reg, *data++);
+ if (ret < 0)
+ break;
+ len -= 2;
+ }
+ }
+
+ return ret;
+}
+
+static void set_subcarrier_freq(struct v4l2_subdev *sd, int pass_through)
+{
+ /* for some reason pass_through NTSC needs
+ * a different sub-carrier freq to remain stable. */
+ if (pass_through)
+ adv7175_write(sd, 0x02, 0x00);
+ else
+ adv7175_write(sd, 0x02, 0x55);
+
+ adv7175_write(sd, 0x03, 0x55);
+ adv7175_write(sd, 0x04, 0x55);
+ adv7175_write(sd, 0x05, 0x25);
+}
+
+/* ----------------------------------------------------------------------- */
+/* Output filter: S-Video Composite */
+
+#define MR050 0x11 /* 0x09 */
+#define MR060 0x14 /* 0x0c */
+
+/* ----------------------------------------------------------------------- */
+
+#define TR0MODE 0x46
+#define TR0RST 0x80
+
+#define TR1CAPT 0x80
+#define TR1PLAY 0x00
+
+static const unsigned char init_common[] = {
+
+ 0x00, MR050, /* MR0, PAL enabled */
+ 0x01, 0x00, /* MR1 */
+ 0x02, 0x0c, /* subc. freq. */
+ 0x03, 0x8c, /* subc. freq. */
+ 0x04, 0x79, /* subc. freq. */
+ 0x05, 0x26, /* subc. freq. */
+ 0x06, 0x40, /* subc. phase */
+
+ 0x07, TR0MODE, /* TR0, 16bit */
+ 0x08, 0x21, /* */
+ 0x09, 0x00, /* */
+ 0x0a, 0x00, /* */
+ 0x0b, 0x00, /* */
+ 0x0c, TR1CAPT, /* TR1 */
+ 0x0d, 0x4f, /* MR2 */
+ 0x0e, 0x00, /* */
+ 0x0f, 0x00, /* */
+ 0x10, 0x00, /* */
+ 0x11, 0x00, /* */
+};
+
+static const unsigned char init_pal[] = {
+ 0x00, MR050, /* MR0, PAL enabled */
+ 0x01, 0x00, /* MR1 */
+ 0x02, 0x0c, /* subc. freq. */
+ 0x03, 0x8c, /* subc. freq. */
+ 0x04, 0x79, /* subc. freq. */
+ 0x05, 0x26, /* subc. freq. */
+ 0x06, 0x40, /* subc. phase */
+};
+
+static const unsigned char init_ntsc[] = {
+ 0x00, MR060, /* MR0, NTSC enabled */
+ 0x01, 0x00, /* MR1 */
+ 0x02, 0x55, /* subc. freq. */
+ 0x03, 0x55, /* subc. freq. */
+ 0x04, 0x55, /* subc. freq. */
+ 0x05, 0x25, /* subc. freq. */
+ 0x06, 0x1a, /* subc. phase */
+};
+
+static int adv7175_init(struct v4l2_subdev *sd, u32 val)
+{
+ /* This is just for testing!!! */
+ adv7175_write_block(sd, init_common, sizeof(init_common));
+ adv7175_write(sd, 0x07, TR0MODE | TR0RST);
+ adv7175_write(sd, 0x07, TR0MODE);
+ return 0;
+}
+
+static int adv7175_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct adv7175 *encoder = to_adv7175(sd);
+
+ if (std & V4L2_STD_NTSC) {
+ adv7175_write_block(sd, init_ntsc, sizeof(init_ntsc));
+ if (encoder->input == 0)
+ adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */
+ adv7175_write(sd, 0x07, TR0MODE | TR0RST);
+ adv7175_write(sd, 0x07, TR0MODE);
+ } else if (std & V4L2_STD_PAL) {
+ adv7175_write_block(sd, init_pal, sizeof(init_pal));
+ if (encoder->input == 0)
+ adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */
+ adv7175_write(sd, 0x07, TR0MODE | TR0RST);
+ adv7175_write(sd, 0x07, TR0MODE);
+ } else if (std & V4L2_STD_SECAM) {
+ /* This is an attempt to convert
+ * SECAM->PAL (typically it does not work
+ * due to genlock: when decoder is in SECAM
+ * and encoder in in PAL the subcarrier can
+ * not be syncronized with horizontal
+ * quency) */
+ adv7175_write_block(sd, init_pal, sizeof(init_pal));
+ if (encoder->input == 0)
+ adv7175_write(sd, 0x0d, 0x49); /* Disable genlock */
+ adv7175_write(sd, 0x07, TR0MODE | TR0RST);
+ adv7175_write(sd, 0x07, TR0MODE);
+ } else {
+ v4l2_dbg(1, debug, sd, "illegal norm: %llx\n",
+ (unsigned long long)std);
+ return -EINVAL;
+ }
+ v4l2_dbg(1, debug, sd, "switched to %llx\n", (unsigned long long)std);
+ encoder->norm = std;
+ return 0;
+}
+
+static int adv7175_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct adv7175 *encoder = to_adv7175(sd);
+
+ /* RJ: input = 0: input is from decoder
+ input = 1: input is from ZR36060
+ input = 2: color bar */
+
+ switch (input) {
+ case 0:
+ adv7175_write(sd, 0x01, 0x00);
+
+ if (encoder->norm & V4L2_STD_NTSC)
+ set_subcarrier_freq(sd, 1);
+
+ adv7175_write(sd, 0x0c, TR1CAPT); /* TR1 */
+ if (encoder->norm & V4L2_STD_SECAM)
+ adv7175_write(sd, 0x0d, 0x49); /* Disable genlock */
+ else
+ adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */
+ adv7175_write(sd, 0x07, TR0MODE | TR0RST);
+ adv7175_write(sd, 0x07, TR0MODE);
+ /*udelay(10);*/
+ break;
+
+ case 1:
+ adv7175_write(sd, 0x01, 0x00);
+
+ if (encoder->norm & V4L2_STD_NTSC)
+ set_subcarrier_freq(sd, 0);
+
+ adv7175_write(sd, 0x0c, TR1PLAY); /* TR1 */
+ adv7175_write(sd, 0x0d, 0x49);
+ adv7175_write(sd, 0x07, TR0MODE | TR0RST);
+ adv7175_write(sd, 0x07, TR0MODE);
+ /* udelay(10); */
+ break;
+
+ case 2:
+ adv7175_write(sd, 0x01, 0x80);
+
+ if (encoder->norm & V4L2_STD_NTSC)
+ set_subcarrier_freq(sd, 0);
+
+ adv7175_write(sd, 0x0d, 0x49);
+ adv7175_write(sd, 0x07, TR0MODE | TR0RST);
+ adv7175_write(sd, 0x07, TR0MODE);
+ /* udelay(10); */
+ break;
+
+ default:
+ v4l2_dbg(1, debug, sd, "illegal input: %d\n", input);
+ return -EINVAL;
+ }
+ v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[input]);
+ encoder->input = input;
+ return 0;
+}
+
+static int adv7175_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= ARRAY_SIZE(adv7175_codes))
+ return -EINVAL;
+
+ *code = adv7175_codes[index];
+ return 0;
+}
+
+static int adv7175_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ u8 val = adv7175_read(sd, 0x7);
+
+ if ((val & 0x40) == (1 << 6))
+ mf->code = V4L2_MBUS_FMT_UYVY8_1X16;
+ else
+ mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
+
+ mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ mf->width = 0;
+ mf->height = 0;
+ mf->field = V4L2_FIELD_ANY;
+
+ return 0;
+}
+
+static int adv7175_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ u8 val = adv7175_read(sd, 0x7);
+ int ret;
+
+ switch (mf->code) {
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ val &= ~0x40;
+ break;
+
+ case V4L2_MBUS_FMT_UYVY8_1X16:
+ val |= 0x40;
+ break;
+
+ default:
+ v4l2_dbg(1, debug, sd,
+ "illegal v4l2_mbus_framefmt code: %d\n", mf->code);
+ return -EINVAL;
+ }
+
+ ret = adv7175_write(sd, 0x7, val);
+
+ return ret;
+}
+
+static int adv7175_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7175, 0);
+}
+
+static int adv7175_s_power(struct v4l2_subdev *sd, int on)
+{
+ if (on)
+ adv7175_write(sd, 0x01, 0x00);
+ else
+ adv7175_write(sd, 0x01, 0x78);
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops adv7175_core_ops = {
+ .g_chip_ident = adv7175_g_chip_ident,
+ .init = adv7175_init,
+ .s_power = adv7175_s_power,
+};
+
+static const struct v4l2_subdev_video_ops adv7175_video_ops = {
+ .s_std_output = adv7175_s_std_output,
+ .s_routing = adv7175_s_routing,
+ .s_mbus_fmt = adv7175_s_fmt,
+ .g_mbus_fmt = adv7175_g_fmt,
+ .enum_mbus_fmt = adv7175_enum_fmt,
+};
+
+static const struct v4l2_subdev_ops adv7175_ops = {
+ .core = &adv7175_core_ops,
+ .video = &adv7175_video_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int adv7175_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int i;
+ struct adv7175 *encoder;
+ struct v4l2_subdev *sd;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ encoder = kzalloc(sizeof(struct adv7175), GFP_KERNEL);
+ if (encoder == NULL)
+ return -ENOMEM;
+ sd = &encoder->sd;
+ v4l2_i2c_subdev_init(sd, client, &adv7175_ops);
+ encoder->norm = V4L2_STD_NTSC;
+ encoder->input = 0;
+
+ i = adv7175_write_block(sd, init_common, sizeof(init_common));
+ if (i >= 0) {
+ i = adv7175_write(sd, 0x07, TR0MODE | TR0RST);
+ i = adv7175_write(sd, 0x07, TR0MODE);
+ i = adv7175_read(sd, 0x12);
+ v4l2_dbg(1, debug, sd, "revision %d\n", i & 1);
+ }
+ if (i < 0)
+ v4l2_dbg(1, debug, sd, "init error 0x%x\n", i);
+ return 0;
+}
+
+static int adv7175_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_adv7175(sd));
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct i2c_device_id adv7175_id[] = {
+ { "adv7175", 0 },
+ { "adv7176", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adv7175_id);
+
+static struct i2c_driver adv7175_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "adv7175",
+ },
+ .probe = adv7175_probe,
+ .remove = adv7175_remove,
+ .id_table = adv7175_id,
+};
+
+module_i2c_driver(adv7175_driver);
diff --git a/drivers/media/video/adv7180.c b/drivers/media/video/adv7180.c
new file mode 100644
index 00000000..b8b6c4b0
--- /dev/null
+++ b/drivers/media/video/adv7180.c
@@ -0,0 +1,451 @@
+/*
+ * adv7180.c Analog Devices ADV7180 video decoder driver
+ * Copyright (c) 2009 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <media/v4l2-ioctl.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <linux/mutex.h>
+
+#define DRIVER_NAME "adv7180"
+
+#define ADV7180_INPUT_CONTROL_REG 0x00
+#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM 0x00
+#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM_PED 0x10
+#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_J_SECAM 0x20
+#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_M_SECAM 0x30
+#define ADV7180_INPUT_CONTROL_NTSC_J 0x40
+#define ADV7180_INPUT_CONTROL_NTSC_M 0x50
+#define ADV7180_INPUT_CONTROL_PAL60 0x60
+#define ADV7180_INPUT_CONTROL_NTSC_443 0x70
+#define ADV7180_INPUT_CONTROL_PAL_BG 0x80
+#define ADV7180_INPUT_CONTROL_PAL_N 0x90
+#define ADV7180_INPUT_CONTROL_PAL_M 0xa0
+#define ADV7180_INPUT_CONTROL_PAL_M_PED 0xb0
+#define ADV7180_INPUT_CONTROL_PAL_COMB_N 0xc0
+#define ADV7180_INPUT_CONTROL_PAL_COMB_N_PED 0xd0
+#define ADV7180_INPUT_CONTROL_PAL_SECAM 0xe0
+#define ADV7180_INPUT_CONTROL_PAL_SECAM_PED 0xf0
+
+#define ADV7180_EXTENDED_OUTPUT_CONTROL_REG 0x04
+#define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS 0xC5
+
+#define ADV7180_AUTODETECT_ENABLE_REG 0x07
+#define ADV7180_AUTODETECT_DEFAULT 0x7f
+
+#define ADV7180_ADI_CTRL_REG 0x0e
+#define ADV7180_ADI_CTRL_IRQ_SPACE 0x20
+
+#define ADV7180_STATUS1_REG 0x10
+#define ADV7180_STATUS1_IN_LOCK 0x01
+#define ADV7180_STATUS1_AUTOD_MASK 0x70
+#define ADV7180_STATUS1_AUTOD_NTSM_M_J 0x00
+#define ADV7180_STATUS1_AUTOD_NTSC_4_43 0x10
+#define ADV7180_STATUS1_AUTOD_PAL_M 0x20
+#define ADV7180_STATUS1_AUTOD_PAL_60 0x30
+#define ADV7180_STATUS1_AUTOD_PAL_B_G 0x40
+#define ADV7180_STATUS1_AUTOD_SECAM 0x50
+#define ADV7180_STATUS1_AUTOD_PAL_COMB 0x60
+#define ADV7180_STATUS1_AUTOD_SECAM_525 0x70
+
+#define ADV7180_IDENT_REG 0x11
+#define ADV7180_ID_7180 0x18
+
+#define ADV7180_ICONF1_ADI 0x40
+#define ADV7180_ICONF1_ACTIVE_LOW 0x01
+#define ADV7180_ICONF1_PSYNC_ONLY 0x10
+#define ADV7180_ICONF1_ACTIVE_TO_CLR 0xC0
+
+#define ADV7180_IRQ1_LOCK 0x01
+#define ADV7180_IRQ1_UNLOCK 0x02
+#define ADV7180_ISR1_ADI 0x42
+#define ADV7180_ICR1_ADI 0x43
+#define ADV7180_IMR1_ADI 0x44
+#define ADV7180_IMR2_ADI 0x48
+#define ADV7180_IRQ3_AD_CHANGE 0x08
+#define ADV7180_ISR3_ADI 0x4A
+#define ADV7180_ICR3_ADI 0x4B
+#define ADV7180_IMR3_ADI 0x4C
+#define ADV7180_IMR4_ADI 0x50
+
+struct adv7180_state {
+ struct v4l2_subdev sd;
+ struct work_struct work;
+ struct mutex mutex; /* mutual excl. when accessing chip */
+ int irq;
+ v4l2_std_id curr_norm;
+ bool autodetect;
+};
+
+static v4l2_std_id adv7180_std_to_v4l2(u8 status1)
+{
+ switch (status1 & ADV7180_STATUS1_AUTOD_MASK) {
+ case ADV7180_STATUS1_AUTOD_NTSM_M_J:
+ return V4L2_STD_NTSC;
+ case ADV7180_STATUS1_AUTOD_NTSC_4_43:
+ return V4L2_STD_NTSC_443;
+ case ADV7180_STATUS1_AUTOD_PAL_M:
+ return V4L2_STD_PAL_M;
+ case ADV7180_STATUS1_AUTOD_PAL_60:
+ return V4L2_STD_PAL_60;
+ case ADV7180_STATUS1_AUTOD_PAL_B_G:
+ return V4L2_STD_PAL;
+ case ADV7180_STATUS1_AUTOD_SECAM:
+ return V4L2_STD_SECAM;
+ case ADV7180_STATUS1_AUTOD_PAL_COMB:
+ return V4L2_STD_PAL_Nc | V4L2_STD_PAL_N;
+ case ADV7180_STATUS1_AUTOD_SECAM_525:
+ return V4L2_STD_SECAM;
+ default:
+ return V4L2_STD_UNKNOWN;
+ }
+}
+
+static int v4l2_std_to_adv7180(v4l2_std_id std)
+{
+ if (std == V4L2_STD_PAL_60)
+ return ADV7180_INPUT_CONTROL_PAL60;
+ if (std == V4L2_STD_NTSC_443)
+ return ADV7180_INPUT_CONTROL_NTSC_443;
+ if (std == V4L2_STD_PAL_N)
+ return ADV7180_INPUT_CONTROL_PAL_N;
+ if (std == V4L2_STD_PAL_M)
+ return ADV7180_INPUT_CONTROL_PAL_M;
+ if (std == V4L2_STD_PAL_Nc)
+ return ADV7180_INPUT_CONTROL_PAL_COMB_N;
+
+ if (std & V4L2_STD_PAL)
+ return ADV7180_INPUT_CONTROL_PAL_BG;
+ if (std & V4L2_STD_NTSC)
+ return ADV7180_INPUT_CONTROL_NTSC_M;
+ if (std & V4L2_STD_SECAM)
+ return ADV7180_INPUT_CONTROL_PAL_SECAM;
+
+ return -EINVAL;
+}
+
+static u32 adv7180_status_to_v4l2(u8 status1)
+{
+ if (!(status1 & ADV7180_STATUS1_IN_LOCK))
+ return V4L2_IN_ST_NO_SIGNAL;
+
+ return 0;
+}
+
+static int __adv7180_status(struct i2c_client *client, u32 *status,
+ v4l2_std_id *std)
+{
+ int status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG);
+
+ if (status1 < 0)
+ return status1;
+
+ if (status)
+ *status = adv7180_status_to_v4l2(status1);
+ if (std)
+ *std = adv7180_std_to_v4l2(status1);
+
+ return 0;
+}
+
+static inline struct adv7180_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct adv7180_state, sd);
+}
+
+static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ struct adv7180_state *state = to_state(sd);
+ int err = mutex_lock_interruptible(&state->mutex);
+ if (err)
+ return err;
+
+ /* when we are interrupt driven we know the state */
+ if (!state->autodetect || state->irq > 0)
+ *std = state->curr_norm;
+ else
+ err = __adv7180_status(v4l2_get_subdevdata(sd), NULL, std);
+
+ mutex_unlock(&state->mutex);
+ return err;
+}
+
+static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+ struct adv7180_state *state = to_state(sd);
+ int ret = mutex_lock_interruptible(&state->mutex);
+ if (ret)
+ return ret;
+
+ ret = __adv7180_status(v4l2_get_subdevdata(sd), status, NULL);
+ mutex_unlock(&state->mutex);
+ return ret;
+}
+
+static int adv7180_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7180, 0);
+}
+
+static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct adv7180_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret = mutex_lock_interruptible(&state->mutex);
+ if (ret)
+ return ret;
+
+ /* all standards -> autodetect */
+ if (std == V4L2_STD_ALL) {
+ ret = i2c_smbus_write_byte_data(client,
+ ADV7180_INPUT_CONTROL_REG,
+ ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM);
+ if (ret < 0)
+ goto out;
+
+ __adv7180_status(client, NULL, &state->curr_norm);
+ state->autodetect = true;
+ } else {
+ ret = v4l2_std_to_adv7180(std);
+ if (ret < 0)
+ goto out;
+
+ ret = i2c_smbus_write_byte_data(client,
+ ADV7180_INPUT_CONTROL_REG, ret);
+ if (ret < 0)
+ goto out;
+
+ state->curr_norm = std;
+ state->autodetect = false;
+ }
+ ret = 0;
+out:
+ mutex_unlock(&state->mutex);
+ return ret;
+}
+
+static const struct v4l2_subdev_video_ops adv7180_video_ops = {
+ .querystd = adv7180_querystd,
+ .g_input_status = adv7180_g_input_status,
+};
+
+static const struct v4l2_subdev_core_ops adv7180_core_ops = {
+ .g_chip_ident = adv7180_g_chip_ident,
+ .s_std = adv7180_s_std,
+};
+
+static const struct v4l2_subdev_ops adv7180_ops = {
+ .core = &adv7180_core_ops,
+ .video = &adv7180_video_ops,
+};
+
+static void adv7180_work(struct work_struct *work)
+{
+ struct adv7180_state *state = container_of(work, struct adv7180_state,
+ work);
+ struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
+ u8 isr3;
+
+ mutex_lock(&state->mutex);
+ i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
+ ADV7180_ADI_CTRL_IRQ_SPACE);
+ isr3 = i2c_smbus_read_byte_data(client, ADV7180_ISR3_ADI);
+ /* clear */
+ i2c_smbus_write_byte_data(client, ADV7180_ICR3_ADI, isr3);
+ i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, 0);
+
+ if (isr3 & ADV7180_IRQ3_AD_CHANGE && state->autodetect)
+ __adv7180_status(client, NULL, &state->curr_norm);
+ mutex_unlock(&state->mutex);
+
+ enable_irq(state->irq);
+}
+
+static irqreturn_t adv7180_irq(int irq, void *devid)
+{
+ struct adv7180_state *state = devid;
+
+ schedule_work(&state->work);
+
+ disable_irq_nosync(state->irq);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Generic i2c probe
+ * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
+ */
+
+static __devinit int adv7180_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct adv7180_state *state;
+ struct v4l2_subdev *sd;
+ int ret;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ v4l_info(client, "chip found @ 0x%02x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ state = kzalloc(sizeof(struct adv7180_state), GFP_KERNEL);
+ if (state == NULL) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ state->irq = client->irq;
+ INIT_WORK(&state->work, adv7180_work);
+ mutex_init(&state->mutex);
+ state->autodetect = true;
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
+
+ /* Initialize adv7180 */
+ /* Enable autodetection */
+ ret = i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG,
+ ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM);
+ if (ret < 0)
+ goto err_unreg_subdev;
+
+ ret = i2c_smbus_write_byte_data(client, ADV7180_AUTODETECT_ENABLE_REG,
+ ADV7180_AUTODETECT_DEFAULT);
+ if (ret < 0)
+ goto err_unreg_subdev;
+
+ /* ITU-R BT.656-4 compatible */
+ ret = i2c_smbus_write_byte_data(client,
+ ADV7180_EXTENDED_OUTPUT_CONTROL_REG,
+ ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS);
+ if (ret < 0)
+ goto err_unreg_subdev;
+
+ /* read current norm */
+ __adv7180_status(client, NULL, &state->curr_norm);
+
+ /* register for interrupts */
+ if (state->irq > 0) {
+ ret = request_irq(state->irq, adv7180_irq, 0, DRIVER_NAME,
+ state);
+ if (ret)
+ goto err_unreg_subdev;
+
+ ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
+ ADV7180_ADI_CTRL_IRQ_SPACE);
+ if (ret < 0)
+ goto err_unreg_subdev;
+
+ /* config the Interrupt pin to be active low */
+ ret = i2c_smbus_write_byte_data(client, ADV7180_ICONF1_ADI,
+ ADV7180_ICONF1_ACTIVE_LOW | ADV7180_ICONF1_PSYNC_ONLY);
+ if (ret < 0)
+ goto err_unreg_subdev;
+
+ ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0);
+ if (ret < 0)
+ goto err_unreg_subdev;
+
+ ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0);
+ if (ret < 0)
+ goto err_unreg_subdev;
+
+ /* enable AD change interrupts interrupts */
+ ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI,
+ ADV7180_IRQ3_AD_CHANGE);
+ if (ret < 0)
+ goto err_unreg_subdev;
+
+ ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0);
+ if (ret < 0)
+ goto err_unreg_subdev;
+
+ ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
+ 0);
+ if (ret < 0)
+ goto err_unreg_subdev;
+ }
+
+ return 0;
+
+err_unreg_subdev:
+ mutex_destroy(&state->mutex);
+ v4l2_device_unregister_subdev(sd);
+ kfree(state);
+err:
+ printk(KERN_ERR DRIVER_NAME ": Failed to probe: %d\n", ret);
+ return ret;
+}
+
+static __devexit int adv7180_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct adv7180_state *state = to_state(sd);
+
+ if (state->irq > 0) {
+ free_irq(client->irq, state);
+ if (cancel_work_sync(&state->work)) {
+ /*
+ * Work was pending, therefore we need to enable
+ * IRQ here to balance the disable_irq() done in the
+ * interrupt handler.
+ */
+ enable_irq(state->irq);
+ }
+ }
+
+ mutex_destroy(&state->mutex);
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
+ return 0;
+}
+
+static const struct i2c_device_id adv7180_id[] = {
+ {DRIVER_NAME, 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, adv7180_id);
+
+static struct i2c_driver adv7180_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRIVER_NAME,
+ },
+ .probe = adv7180_probe,
+ .remove = __devexit_p(adv7180_remove),
+ .id_table = adv7180_id,
+};
+
+module_i2c_driver(adv7180_driver);
+
+MODULE_DESCRIPTION("Analog Devices ADV7180 video decoder driver");
+MODULE_AUTHOR("Mocean Laboratories");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/adv7183.c b/drivers/media/video/adv7183.c
new file mode 100644
index 00000000..e1d4c89d
--- /dev/null
+++ b/drivers/media/video/adv7183.c
@@ -0,0 +1,699 @@
+/*
+ * adv7183.c Analog Devices ADV7183 video decoder driver
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+#include <media/adv7183.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+
+#include "adv7183_regs.h"
+
+struct adv7183 {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+
+ v4l2_std_id std; /* Current set standard */
+ u32 input;
+ u32 output;
+ unsigned reset_pin;
+ unsigned oe_pin;
+ struct v4l2_mbus_framefmt fmt;
+};
+
+/* EXAMPLES USING 27 MHz CLOCK
+ * Mode 1 CVBS Input (Composite Video on AIN5)
+ * All standards are supported through autodetect, 8-bit, 4:2:2, ITU-R BT.656 output on P15 to P8.
+ */
+static const unsigned char adv7183_init_regs[] = {
+ ADV7183_IN_CTRL, 0x04, /* CVBS input on AIN5 */
+ ADV7183_DIGI_CLAMP_CTRL_1, 0x00, /* Slow down digital clamps */
+ ADV7183_SHAP_FILT_CTRL, 0x41, /* Set CSFM to SH1 */
+ ADV7183_ADC_CTRL, 0x16, /* Power down ADC 1 and ADC 2 */
+ ADV7183_CTI_DNR_CTRL_4, 0x04, /* Set DNR threshold to 4 for flat response */
+ /* ADI recommended programming sequence */
+ ADV7183_ADI_CTRL, 0x80,
+ ADV7183_CTI_DNR_CTRL_4, 0x20,
+ 0x52, 0x18,
+ 0x58, 0xED,
+ 0x77, 0xC5,
+ 0x7C, 0x93,
+ 0x7D, 0x00,
+ 0xD0, 0x48,
+ 0xD5, 0xA0,
+ 0xD7, 0xEA,
+ ADV7183_SD_SATURATION_CR, 0x3E,
+ ADV7183_PAL_V_END, 0x3E,
+ ADV7183_PAL_F_TOGGLE, 0x0F,
+ ADV7183_ADI_CTRL, 0x00,
+};
+
+static inline struct adv7183 *to_adv7183(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct adv7183, sd);
+}
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct adv7183, hdl)->sd;
+}
+
+static inline int adv7183_read(struct v4l2_subdev *sd, unsigned char reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static inline int adv7183_write(struct v4l2_subdev *sd, unsigned char reg,
+ unsigned char value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static int adv7183_writeregs(struct v4l2_subdev *sd,
+ const unsigned char *regs, unsigned int num)
+{
+ unsigned char reg, data;
+ unsigned int cnt = 0;
+
+ if (num & 0x1) {
+ v4l2_err(sd, "invalid regs array\n");
+ return -1;
+ }
+
+ while (cnt < num) {
+ reg = *regs++;
+ data = *regs++;
+ cnt += 2;
+
+ adv7183_write(sd, reg, data);
+ }
+ return 0;
+}
+
+static int adv7183_log_status(struct v4l2_subdev *sd)
+{
+ struct adv7183 *decoder = to_adv7183(sd);
+
+ v4l2_info(sd, "adv7183: Input control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_IN_CTRL));
+ v4l2_info(sd, "adv7183: Video selection = 0x%02x\n",
+ adv7183_read(sd, ADV7183_VD_SEL));
+ v4l2_info(sd, "adv7183: Output control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_OUT_CTRL));
+ v4l2_info(sd, "adv7183: Extended output control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_EXT_OUT_CTRL));
+ v4l2_info(sd, "adv7183: Autodetect enable = 0x%02x\n",
+ adv7183_read(sd, ADV7183_AUTO_DET_EN));
+ v4l2_info(sd, "adv7183: Contrast = 0x%02x\n",
+ adv7183_read(sd, ADV7183_CONTRAST));
+ v4l2_info(sd, "adv7183: Brightness = 0x%02x\n",
+ adv7183_read(sd, ADV7183_BRIGHTNESS));
+ v4l2_info(sd, "adv7183: Hue = 0x%02x\n",
+ adv7183_read(sd, ADV7183_HUE));
+ v4l2_info(sd, "adv7183: Default value Y = 0x%02x\n",
+ adv7183_read(sd, ADV7183_DEF_Y));
+ v4l2_info(sd, "adv7183: Default value C = 0x%02x\n",
+ adv7183_read(sd, ADV7183_DEF_C));
+ v4l2_info(sd, "adv7183: ADI control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_ADI_CTRL));
+ v4l2_info(sd, "adv7183: Power Management = 0x%02x\n",
+ adv7183_read(sd, ADV7183_POW_MANAGE));
+ v4l2_info(sd, "adv7183: Status 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n",
+ adv7183_read(sd, ADV7183_STATUS_1),
+ adv7183_read(sd, ADV7183_STATUS_2),
+ adv7183_read(sd, ADV7183_STATUS_3));
+ v4l2_info(sd, "adv7183: Ident = 0x%02x\n",
+ adv7183_read(sd, ADV7183_IDENT));
+ v4l2_info(sd, "adv7183: Analog clamp control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_ANAL_CLAMP_CTRL));
+ v4l2_info(sd, "adv7183: Digital clamp control 1 = 0x%02x\n",
+ adv7183_read(sd, ADV7183_DIGI_CLAMP_CTRL_1));
+ v4l2_info(sd, "adv7183: Shaping filter control 1 and 2 = 0x%02x 0x%02x\n",
+ adv7183_read(sd, ADV7183_SHAP_FILT_CTRL),
+ adv7183_read(sd, ADV7183_SHAP_FILT_CTRL_2));
+ v4l2_info(sd, "adv7183: Comb filter control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_COMB_FILT_CTRL));
+ v4l2_info(sd, "adv7183: ADI control 2 = 0x%02x\n",
+ adv7183_read(sd, ADV7183_ADI_CTRL_2));
+ v4l2_info(sd, "adv7183: Pixel delay control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_PIX_DELAY_CTRL));
+ v4l2_info(sd, "adv7183: Misc gain control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_MISC_GAIN_CTRL));
+ v4l2_info(sd, "adv7183: AGC mode control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_AGC_MODE_CTRL));
+ v4l2_info(sd, "adv7183: Chroma gain control 1 and 2 = 0x%02x 0x%02x\n",
+ adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_1),
+ adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_2));
+ v4l2_info(sd, "adv7183: Luma gain control 1 and 2 = 0x%02x 0x%02x\n",
+ adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_1),
+ adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_2));
+ v4l2_info(sd, "adv7183: Vsync field control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n",
+ adv7183_read(sd, ADV7183_VS_FIELD_CTRL_1),
+ adv7183_read(sd, ADV7183_VS_FIELD_CTRL_2),
+ adv7183_read(sd, ADV7183_VS_FIELD_CTRL_3));
+ v4l2_info(sd, "adv7183: Hsync positon control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n",
+ adv7183_read(sd, ADV7183_HS_POS_CTRL_1),
+ adv7183_read(sd, ADV7183_HS_POS_CTRL_2),
+ adv7183_read(sd, ADV7183_HS_POS_CTRL_3));
+ v4l2_info(sd, "adv7183: Polarity = 0x%02x\n",
+ adv7183_read(sd, ADV7183_POLARITY));
+ v4l2_info(sd, "adv7183: ADC control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_ADC_CTRL));
+ v4l2_info(sd, "adv7183: SD offset Cb and Cr = 0x%02x 0x%02x\n",
+ adv7183_read(sd, ADV7183_SD_OFFSET_CB),
+ adv7183_read(sd, ADV7183_SD_OFFSET_CR));
+ v4l2_info(sd, "adv7183: SD saturation Cb and Cr = 0x%02x 0x%02x\n",
+ adv7183_read(sd, ADV7183_SD_SATURATION_CB),
+ adv7183_read(sd, ADV7183_SD_SATURATION_CR));
+ v4l2_info(sd, "adv7183: Drive strength = 0x%02x\n",
+ adv7183_read(sd, ADV7183_DRIVE_STR));
+ v4l2_ctrl_handler_log_status(&decoder->hdl, sd->name);
+ return 0;
+}
+
+static int adv7183_g_std(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ struct adv7183 *decoder = to_adv7183(sd);
+
+ *std = decoder->std;
+ return 0;
+}
+
+static int adv7183_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct adv7183 *decoder = to_adv7183(sd);
+ int reg;
+
+ reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF;
+ if (std == V4L2_STD_PAL_60)
+ reg |= 0x60;
+ else if (std == V4L2_STD_NTSC_443)
+ reg |= 0x70;
+ else if (std == V4L2_STD_PAL_N)
+ reg |= 0x90;
+ else if (std == V4L2_STD_PAL_M)
+ reg |= 0xA0;
+ else if (std == V4L2_STD_PAL_Nc)
+ reg |= 0xC0;
+ else if (std & V4L2_STD_PAL)
+ reg |= 0x80;
+ else if (std & V4L2_STD_NTSC)
+ reg |= 0x50;
+ else if (std & V4L2_STD_SECAM)
+ reg |= 0xE0;
+ else
+ return -EINVAL;
+ adv7183_write(sd, ADV7183_IN_CTRL, reg);
+
+ decoder->std = std;
+
+ return 0;
+}
+
+static int adv7183_reset(struct v4l2_subdev *sd, u32 val)
+{
+ int reg;
+
+ reg = adv7183_read(sd, ADV7183_POW_MANAGE) | 0x80;
+ adv7183_write(sd, ADV7183_POW_MANAGE, reg);
+ /* wait 5ms before any further i2c writes are performed */
+ usleep_range(5000, 10000);
+ return 0;
+}
+
+static int adv7183_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct adv7183 *decoder = to_adv7183(sd);
+ int reg;
+
+ if ((input > ADV7183_COMPONENT1) || (output > ADV7183_16BIT_OUT))
+ return -EINVAL;
+
+ if (input != decoder->input) {
+ decoder->input = input;
+ reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF0;
+ switch (input) {
+ case ADV7183_COMPOSITE1:
+ reg |= 0x1;
+ break;
+ case ADV7183_COMPOSITE2:
+ reg |= 0x2;
+ break;
+ case ADV7183_COMPOSITE3:
+ reg |= 0x3;
+ break;
+ case ADV7183_COMPOSITE4:
+ reg |= 0x4;
+ break;
+ case ADV7183_COMPOSITE5:
+ reg |= 0x5;
+ break;
+ case ADV7183_COMPOSITE6:
+ reg |= 0xB;
+ break;
+ case ADV7183_COMPOSITE7:
+ reg |= 0xC;
+ break;
+ case ADV7183_COMPOSITE8:
+ reg |= 0xD;
+ break;
+ case ADV7183_COMPOSITE9:
+ reg |= 0xE;
+ break;
+ case ADV7183_COMPOSITE10:
+ reg |= 0xF;
+ break;
+ case ADV7183_SVIDEO0:
+ reg |= 0x6;
+ break;
+ case ADV7183_SVIDEO1:
+ reg |= 0x7;
+ break;
+ case ADV7183_SVIDEO2:
+ reg |= 0x8;
+ break;
+ case ADV7183_COMPONENT0:
+ reg |= 0x9;
+ break;
+ case ADV7183_COMPONENT1:
+ reg |= 0xA;
+ break;
+ default:
+ break;
+ }
+ adv7183_write(sd, ADV7183_IN_CTRL, reg);
+ }
+
+ if (output != decoder->output) {
+ decoder->output = output;
+ reg = adv7183_read(sd, ADV7183_OUT_CTRL) & 0xC0;
+ switch (output) {
+ case ADV7183_16BIT_OUT:
+ reg |= 0x9;
+ break;
+ default:
+ reg |= 0xC;
+ break;
+ }
+ adv7183_write(sd, ADV7183_OUT_CTRL, reg);
+ }
+
+ return 0;
+}
+
+static int adv7183_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ int val = ctrl->val;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ if (val < 0)
+ val = 127 - val;
+ adv7183_write(sd, ADV7183_BRIGHTNESS, val);
+ break;
+ case V4L2_CID_CONTRAST:
+ adv7183_write(sd, ADV7183_CONTRAST, val);
+ break;
+ case V4L2_CID_SATURATION:
+ adv7183_write(sd, ADV7183_SD_SATURATION_CB, val >> 8);
+ adv7183_write(sd, ADV7183_SD_SATURATION_CR, (val & 0xFF));
+ break;
+ case V4L2_CID_HUE:
+ adv7183_write(sd, ADV7183_SD_OFFSET_CB, val >> 8);
+ adv7183_write(sd, ADV7183_SD_OFFSET_CR, (val & 0xFF));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int adv7183_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ struct adv7183 *decoder = to_adv7183(sd);
+ int reg;
+
+ /* enable autodetection block */
+ reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF;
+ adv7183_write(sd, ADV7183_IN_CTRL, reg);
+
+ /* wait autodetection switch */
+ mdelay(10);
+
+ /* get autodetection result */
+ reg = adv7183_read(sd, ADV7183_STATUS_1);
+ switch ((reg >> 0x4) & 0x7) {
+ case 0:
+ *std = V4L2_STD_NTSC;
+ break;
+ case 1:
+ *std = V4L2_STD_NTSC_443;
+ break;
+ case 2:
+ *std = V4L2_STD_PAL_M;
+ break;
+ case 3:
+ *std = V4L2_STD_PAL_60;
+ break;
+ case 4:
+ *std = V4L2_STD_PAL;
+ break;
+ case 5:
+ *std = V4L2_STD_SECAM;
+ break;
+ case 6:
+ *std = V4L2_STD_PAL_Nc;
+ break;
+ case 7:
+ *std = V4L2_STD_SECAM;
+ break;
+ default:
+ *std = V4L2_STD_UNKNOWN;
+ break;
+ }
+
+ /* after std detection, write back user set std */
+ adv7183_s_std(sd, decoder->std);
+ return 0;
+}
+
+static int adv7183_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+ int reg;
+
+ *status = V4L2_IN_ST_NO_SIGNAL;
+ reg = adv7183_read(sd, ADV7183_STATUS_1);
+ if (reg < 0)
+ return reg;
+ if (reg & 0x1)
+ *status = 0;
+ return 0;
+}
+
+static int adv7183_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index > 0)
+ return -EINVAL;
+
+ *code = V4L2_MBUS_FMT_UYVY8_2X8;
+ return 0;
+}
+
+static int adv7183_try_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct adv7183 *decoder = to_adv7183(sd);
+
+ fmt->code = V4L2_MBUS_FMT_UYVY8_2X8;
+ fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ if (decoder->std & V4L2_STD_525_60) {
+ fmt->field = V4L2_FIELD_SEQ_TB;
+ fmt->width = 720;
+ fmt->height = 480;
+ } else {
+ fmt->field = V4L2_FIELD_SEQ_BT;
+ fmt->width = 720;
+ fmt->height = 576;
+ }
+ return 0;
+}
+
+static int adv7183_s_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct adv7183 *decoder = to_adv7183(sd);
+
+ adv7183_try_mbus_fmt(sd, fmt);
+ decoder->fmt = *fmt;
+ return 0;
+}
+
+static int adv7183_g_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct adv7183 *decoder = to_adv7183(sd);
+
+ *fmt = decoder->fmt;
+ return 0;
+}
+
+static int adv7183_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct adv7183 *decoder = to_adv7183(sd);
+
+ if (enable)
+ gpio_direction_output(decoder->oe_pin, 0);
+ else
+ gpio_direction_output(decoder->oe_pin, 1);
+ udelay(1);
+ return 0;
+}
+
+static int adv7183_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ int rev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ /* 0x11 for adv7183, 0x13 for adv7183b */
+ rev = adv7183_read(sd, ADV7183_IDENT);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7183, rev);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int adv7183_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = adv7183_read(sd, reg->reg & 0xff);
+ reg->size = 1;
+ return 0;
+}
+
+static int adv7183_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ adv7183_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ return 0;
+}
+#endif
+
+static const struct v4l2_ctrl_ops adv7183_ctrl_ops = {
+ .s_ctrl = adv7183_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops adv7183_core_ops = {
+ .log_status = adv7183_log_status,
+ .g_std = adv7183_g_std,
+ .s_std = adv7183_s_std,
+ .reset = adv7183_reset,
+ .g_chip_ident = adv7183_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = adv7183_g_register,
+ .s_register = adv7183_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops adv7183_video_ops = {
+ .s_routing = adv7183_s_routing,
+ .querystd = adv7183_querystd,
+ .g_input_status = adv7183_g_input_status,
+ .enum_mbus_fmt = adv7183_enum_mbus_fmt,
+ .try_mbus_fmt = adv7183_try_mbus_fmt,
+ .s_mbus_fmt = adv7183_s_mbus_fmt,
+ .g_mbus_fmt = adv7183_g_mbus_fmt,
+ .s_stream = adv7183_s_stream,
+};
+
+static const struct v4l2_subdev_ops adv7183_ops = {
+ .core = &adv7183_core_ops,
+ .video = &adv7183_video_ops,
+};
+
+static int adv7183_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct adv7183 *decoder;
+ struct v4l2_subdev *sd;
+ struct v4l2_ctrl_handler *hdl;
+ int ret;
+ struct v4l2_mbus_framefmt fmt;
+ const unsigned *pin_array;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ v4l_info(client, "chip found @ 0x%02x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ pin_array = client->dev.platform_data;
+ if (pin_array == NULL)
+ return -EINVAL;
+
+ decoder = kzalloc(sizeof(struct adv7183), GFP_KERNEL);
+ if (decoder == NULL)
+ return -ENOMEM;
+
+ decoder->reset_pin = pin_array[0];
+ decoder->oe_pin = pin_array[1];
+
+ if (gpio_request(decoder->reset_pin, "ADV7183 Reset")) {
+ v4l_err(client, "failed to request GPIO %d\n", decoder->reset_pin);
+ ret = -EBUSY;
+ goto err_free_decoder;
+ }
+
+ if (gpio_request(decoder->oe_pin, "ADV7183 Output Enable")) {
+ v4l_err(client, "failed to request GPIO %d\n", decoder->oe_pin);
+ ret = -EBUSY;
+ goto err_free_reset;
+ }
+
+ sd = &decoder->sd;
+ v4l2_i2c_subdev_init(sd, client, &adv7183_ops);
+
+ hdl = &decoder->hdl;
+ v4l2_ctrl_handler_init(hdl, 4);
+ v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 0xFF, 1, 0x80);
+ v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 0xFFFF, 1, 0x8080);
+ v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
+ V4L2_CID_HUE, 0, 0xFFFF, 1, 0x8080);
+ /* hook the control handler into the driver */
+ sd->ctrl_handler = hdl;
+ if (hdl->error) {
+ ret = hdl->error;
+
+ v4l2_ctrl_handler_free(hdl);
+ goto err_free_oe;
+ }
+
+ /* v4l2 doesn't support an autodetect standard, pick PAL as default */
+ decoder->std = V4L2_STD_PAL;
+ decoder->input = ADV7183_COMPOSITE4;
+ decoder->output = ADV7183_8BIT_OUT;
+
+ gpio_direction_output(decoder->oe_pin, 1);
+ /* reset chip */
+ gpio_direction_output(decoder->reset_pin, 0);
+ /* reset pulse width at least 5ms */
+ mdelay(10);
+ gpio_direction_output(decoder->reset_pin, 1);
+ /* wait 5ms before any further i2c writes are performed */
+ mdelay(5);
+
+ adv7183_writeregs(sd, adv7183_init_regs, ARRAY_SIZE(adv7183_init_regs));
+ adv7183_s_std(sd, decoder->std);
+ fmt.width = 720;
+ fmt.height = 576;
+ adv7183_s_mbus_fmt(sd, &fmt);
+
+ /* initialize the hardware to the default control values */
+ ret = v4l2_ctrl_handler_setup(hdl);
+ if (ret) {
+ v4l2_ctrl_handler_free(hdl);
+ goto err_free_oe;
+ }
+
+ return 0;
+err_free_oe:
+ gpio_free(decoder->oe_pin);
+err_free_reset:
+ gpio_free(decoder->reset_pin);
+err_free_decoder:
+ kfree(decoder);
+ return ret;
+}
+
+static int adv7183_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct adv7183 *decoder = to_adv7183(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+ gpio_free(decoder->oe_pin);
+ gpio_free(decoder->reset_pin);
+ kfree(decoder);
+ return 0;
+}
+
+static const struct i2c_device_id adv7183_id[] = {
+ {"adv7183", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, adv7183_id);
+
+static struct i2c_driver adv7183_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "adv7183",
+ },
+ .probe = adv7183_probe,
+ .remove = __devexit_p(adv7183_remove),
+ .id_table = adv7183_id,
+};
+
+static __init int adv7183_init(void)
+{
+ return i2c_add_driver(&adv7183_driver);
+}
+
+static __exit void adv7183_exit(void)
+{
+ i2c_del_driver(&adv7183_driver);
+}
+
+module_init(adv7183_init);
+module_exit(adv7183_exit);
+
+MODULE_DESCRIPTION("Analog Devices ADV7183 video decoder driver");
+MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/adv7183_regs.h b/drivers/media/video/adv7183_regs.h
new file mode 100644
index 00000000..4a5b7d21
--- /dev/null
+++ b/drivers/media/video/adv7183_regs.h
@@ -0,0 +1,107 @@
+/*
+ * adv7183 - Analog Devices ADV7183 video decoder registers
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _ADV7183_REGS_H_
+#define _ADV7183_REGS_H_
+
+#define ADV7183_IN_CTRL 0x00 /* Input control */
+#define ADV7183_VD_SEL 0x01 /* Video selection */
+#define ADV7183_OUT_CTRL 0x03 /* Output control */
+#define ADV7183_EXT_OUT_CTRL 0x04 /* Extended output control */
+#define ADV7183_AUTO_DET_EN 0x07 /* Autodetect enable */
+#define ADV7183_CONTRAST 0x08 /* Contrast */
+#define ADV7183_BRIGHTNESS 0x0A /* Brightness */
+#define ADV7183_HUE 0x0B /* Hue */
+#define ADV7183_DEF_Y 0x0C /* Default value Y */
+#define ADV7183_DEF_C 0x0D /* Default value C */
+#define ADV7183_ADI_CTRL 0x0E /* ADI control */
+#define ADV7183_POW_MANAGE 0x0F /* Power Management */
+#define ADV7183_STATUS_1 0x10 /* Status 1 */
+#define ADV7183_IDENT 0x11 /* Ident */
+#define ADV7183_STATUS_2 0x12 /* Status 2 */
+#define ADV7183_STATUS_3 0x13 /* Status 3 */
+#define ADV7183_ANAL_CLAMP_CTRL 0x14 /* Analog clamp control */
+#define ADV7183_DIGI_CLAMP_CTRL_1 0x15 /* Digital clamp control 1 */
+#define ADV7183_SHAP_FILT_CTRL 0x17 /* Shaping filter control */
+#define ADV7183_SHAP_FILT_CTRL_2 0x18 /* Shaping filter control 2 */
+#define ADV7183_COMB_FILT_CTRL 0x19 /* Comb filter control */
+#define ADV7183_ADI_CTRL_2 0x1D /* ADI control 2 */
+#define ADV7183_PIX_DELAY_CTRL 0x27 /* Pixel delay control */
+#define ADV7183_MISC_GAIN_CTRL 0x2B /* Misc gain control */
+#define ADV7183_AGC_MODE_CTRL 0x2C /* AGC mode control */
+#define ADV7183_CHRO_GAIN_CTRL_1 0x2D /* Chroma gain control 1 */
+#define ADV7183_CHRO_GAIN_CTRL_2 0x2E /* Chroma gain control 2 */
+#define ADV7183_LUMA_GAIN_CTRL_1 0x2F /* Luma gain control 1 */
+#define ADV7183_LUMA_GAIN_CTRL_2 0x30 /* Luma gain control 2 */
+#define ADV7183_VS_FIELD_CTRL_1 0x31 /* Vsync field control 1 */
+#define ADV7183_VS_FIELD_CTRL_2 0x32 /* Vsync field control 2 */
+#define ADV7183_VS_FIELD_CTRL_3 0x33 /* Vsync field control 3 */
+#define ADV7183_HS_POS_CTRL_1 0x34 /* Hsync positon control 1 */
+#define ADV7183_HS_POS_CTRL_2 0x35 /* Hsync positon control 2 */
+#define ADV7183_HS_POS_CTRL_3 0x36 /* Hsync positon control 3 */
+#define ADV7183_POLARITY 0x37 /* Polarity */
+#define ADV7183_NTSC_COMB_CTRL 0x38 /* NTSC comb control */
+#define ADV7183_PAL_COMB_CTRL 0x39 /* PAL comb control */
+#define ADV7183_ADC_CTRL 0x3A /* ADC control */
+#define ADV7183_MAN_WIN_CTRL 0x3D /* Manual window control */
+#define ADV7183_RESAMPLE_CTRL 0x41 /* Resample control */
+#define ADV7183_GEMSTAR_CTRL_1 0x48 /* Gemstar ctrl 1 */
+#define ADV7183_GEMSTAR_CTRL_2 0x49 /* Gemstar ctrl 2 */
+#define ADV7183_GEMSTAR_CTRL_3 0x4A /* Gemstar ctrl 3 */
+#define ADV7183_GEMSTAR_CTRL_4 0x4B /* Gemstar ctrl 4 */
+#define ADV7183_GEMSTAR_CTRL_5 0x4C /* Gemstar ctrl 5 */
+#define ADV7183_CTI_DNR_CTRL_1 0x4D /* CTI DNR ctrl 1 */
+#define ADV7183_CTI_DNR_CTRL_2 0x4E /* CTI DNR ctrl 2 */
+#define ADV7183_CTI_DNR_CTRL_4 0x50 /* CTI DNR ctrl 4 */
+#define ADV7183_LOCK_CNT 0x51 /* Lock count */
+#define ADV7183_FREE_LINE_LEN 0x8F /* Free-Run line length 1 */
+#define ADV7183_VBI_INFO 0x90 /* VBI info */
+#define ADV7183_WSS_1 0x91 /* WSS 1 */
+#define ADV7183_WSS_2 0x92 /* WSS 2 */
+#define ADV7183_EDTV_1 0x93 /* EDTV 1 */
+#define ADV7183_EDTV_2 0x94 /* EDTV 2 */
+#define ADV7183_EDTV_3 0x95 /* EDTV 3 */
+#define ADV7183_CGMS_1 0x96 /* CGMS 1 */
+#define ADV7183_CGMS_2 0x97 /* CGMS 2 */
+#define ADV7183_CGMS_3 0x98 /* CGMS 3 */
+#define ADV7183_CCAP_1 0x99 /* CCAP 1 */
+#define ADV7183_CCAP_2 0x9A /* CCAP 2 */
+#define ADV7183_LETTERBOX_1 0x9B /* Letterbox 1 */
+#define ADV7183_LETTERBOX_2 0x9C /* Letterbox 2 */
+#define ADV7183_LETTERBOX_3 0x9D /* Letterbox 3 */
+#define ADV7183_CRC_EN 0xB2 /* CRC enable */
+#define ADV7183_ADC_SWITCH_1 0xC3 /* ADC switch 1 */
+#define ADV7183_ADC_SWITCH_2 0xC4 /* ADC swithc 2 */
+#define ADV7183_LETTERBOX_CTRL_1 0xDC /* Letterbox control 1 */
+#define ADV7183_LETTERBOX_CTRL_2 0xDD /* Letterbox control 2 */
+#define ADV7183_SD_OFFSET_CB 0xE1 /* SD offset Cb */
+#define ADV7183_SD_OFFSET_CR 0xE2 /* SD offset Cr */
+#define ADV7183_SD_SATURATION_CB 0xE3 /* SD saturation Cb */
+#define ADV7183_SD_SATURATION_CR 0xE4 /* SD saturation Cr */
+#define ADV7183_NTSC_V_BEGIN 0xE5 /* NTSC V bit begin */
+#define ADV7183_NTSC_V_END 0xE6 /* NTSC V bit end */
+#define ADV7183_NTSC_F_TOGGLE 0xE7 /* NTSC F bit toggle */
+#define ADV7183_PAL_V_BEGIN 0xE8 /* PAL V bit begin */
+#define ADV7183_PAL_V_END 0xE9 /* PAL V bit end */
+#define ADV7183_PAL_F_TOGGLE 0xEA /* PAL F bit toggle */
+#define ADV7183_DRIVE_STR 0xF4 /* Drive strength */
+#define ADV7183_IF_COMP_CTRL 0xF8 /* IF comp control */
+#define ADV7183_VS_MODE_CTRL 0xF9 /* VS mode control */
+
+#endif
diff --git a/drivers/media/video/adv7343.c b/drivers/media/video/adv7343.c
new file mode 100644
index 00000000..119b6040
--- /dev/null
+++ b/drivers/media/video/adv7343.c
@@ -0,0 +1,478 @@
+/*
+ * adv7343 - ADV7343 Video Encoder Driver
+ *
+ * The encoder hardware does not support SECAM.
+ *
+ * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed .as is. WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/videodev2.h>
+#include <linux/uaccess.h>
+
+#include <media/adv7343.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+
+#include "adv7343_regs.h"
+
+MODULE_DESCRIPTION("ADV7343 video encoder driver");
+MODULE_LICENSE("GPL");
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level 0-1");
+
+struct adv7343_state {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+ u8 reg00;
+ u8 reg01;
+ u8 reg02;
+ u8 reg35;
+ u8 reg80;
+ u8 reg82;
+ u32 output;
+ v4l2_std_id std;
+};
+
+static inline struct adv7343_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct adv7343_state, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct adv7343_state, hdl)->sd;
+}
+
+static inline int adv7343_write(struct v4l2_subdev *sd, u8 reg, u8 value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static const u8 adv7343_init_reg_val[] = {
+ ADV7343_SOFT_RESET, ADV7343_SOFT_RESET_DEFAULT,
+ ADV7343_POWER_MODE_REG, ADV7343_POWER_MODE_REG_DEFAULT,
+
+ ADV7343_HD_MODE_REG1, ADV7343_HD_MODE_REG1_DEFAULT,
+ ADV7343_HD_MODE_REG2, ADV7343_HD_MODE_REG2_DEFAULT,
+ ADV7343_HD_MODE_REG3, ADV7343_HD_MODE_REG3_DEFAULT,
+ ADV7343_HD_MODE_REG4, ADV7343_HD_MODE_REG4_DEFAULT,
+ ADV7343_HD_MODE_REG5, ADV7343_HD_MODE_REG5_DEFAULT,
+ ADV7343_HD_MODE_REG6, ADV7343_HD_MODE_REG6_DEFAULT,
+ ADV7343_HD_MODE_REG7, ADV7343_HD_MODE_REG7_DEFAULT,
+
+ ADV7343_SD_MODE_REG1, ADV7343_SD_MODE_REG1_DEFAULT,
+ ADV7343_SD_MODE_REG2, ADV7343_SD_MODE_REG2_DEFAULT,
+ ADV7343_SD_MODE_REG3, ADV7343_SD_MODE_REG3_DEFAULT,
+ ADV7343_SD_MODE_REG4, ADV7343_SD_MODE_REG4_DEFAULT,
+ ADV7343_SD_MODE_REG5, ADV7343_SD_MODE_REG5_DEFAULT,
+ ADV7343_SD_MODE_REG6, ADV7343_SD_MODE_REG6_DEFAULT,
+ ADV7343_SD_MODE_REG7, ADV7343_SD_MODE_REG7_DEFAULT,
+ ADV7343_SD_MODE_REG8, ADV7343_SD_MODE_REG8_DEFAULT,
+
+ ADV7343_SD_HUE_REG, ADV7343_SD_HUE_REG_DEFAULT,
+ ADV7343_SD_CGMS_WSS0, ADV7343_SD_CGMS_WSS0_DEFAULT,
+ ADV7343_SD_BRIGHTNESS_WSS, ADV7343_SD_BRIGHTNESS_WSS_DEFAULT,
+};
+
+/*
+ * 2^32
+ * FSC(reg) = FSC (HZ) * --------
+ * 27000000
+ */
+static const struct adv7343_std_info stdinfo[] = {
+ {
+ /* FSC(Hz) = 3,579,545.45 Hz */
+ SD_STD_NTSC, 569408542, V4L2_STD_NTSC,
+ }, {
+ /* FSC(Hz) = 3,575,611.00 Hz */
+ SD_STD_PAL_M, 568782678, V4L2_STD_PAL_M,
+ }, {
+ /* FSC(Hz) = 3,582,056.00 */
+ SD_STD_PAL_N, 569807903, V4L2_STD_PAL_Nc,
+ }, {
+ /* FSC(Hz) = 4,433,618.75 Hz */
+ SD_STD_PAL_N, 705268427, V4L2_STD_PAL_N,
+ }, {
+ /* FSC(Hz) = 4,433,618.75 Hz */
+ SD_STD_PAL_BDGHI, 705268427, V4L2_STD_PAL,
+ }, {
+ /* FSC(Hz) = 4,433,618.75 Hz */
+ SD_STD_NTSC, 705268427, V4L2_STD_NTSC_443,
+ }, {
+ /* FSC(Hz) = 4,433,618.75 Hz */
+ SD_STD_PAL_M, 705268427, V4L2_STD_PAL_60,
+ },
+};
+
+static int adv7343_setstd(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct adv7343_state *state = to_state(sd);
+ struct adv7343_std_info *std_info;
+ int output_idx, num_std;
+ char *fsc_ptr;
+ u8 reg, val;
+ int err = 0;
+ int i = 0;
+
+ output_idx = state->output;
+
+ std_info = (struct adv7343_std_info *)stdinfo;
+ num_std = ARRAY_SIZE(stdinfo);
+
+ for (i = 0; i < num_std; i++) {
+ if (std_info[i].stdid & std)
+ break;
+ }
+
+ if (i == num_std) {
+ v4l2_dbg(1, debug, sd,
+ "Invalid std or std is not supported: %llx\n",
+ (unsigned long long)std);
+ return -EINVAL;
+ }
+
+ /* Set the standard */
+ val = state->reg80 & (~(SD_STD_MASK));
+ val |= std_info[i].standard_val3;
+ err = adv7343_write(sd, ADV7343_SD_MODE_REG1, val);
+ if (err < 0)
+ goto setstd_exit;
+
+ state->reg80 = val;
+
+ /* Configure the input mode register */
+ val = state->reg01 & (~((u8) INPUT_MODE_MASK));
+ val |= SD_INPUT_MODE;
+ err = adv7343_write(sd, ADV7343_MODE_SELECT_REG, val);
+ if (err < 0)
+ goto setstd_exit;
+
+ state->reg01 = val;
+
+ /* Program the sub carrier frequency registers */
+ fsc_ptr = (unsigned char *)&std_info[i].fsc_val;
+ reg = ADV7343_FSC_REG0;
+ for (i = 0; i < 4; i++, reg++, fsc_ptr++) {
+ err = adv7343_write(sd, reg, *fsc_ptr);
+ if (err < 0)
+ goto setstd_exit;
+ }
+
+ val = state->reg80;
+
+ /* Filter settings */
+ if (std & (V4L2_STD_NTSC | V4L2_STD_NTSC_443))
+ val &= 0x03;
+ else if (std & ~V4L2_STD_SECAM)
+ val |= 0x04;
+
+ err = adv7343_write(sd, ADV7343_SD_MODE_REG1, val);
+ if (err < 0)
+ goto setstd_exit;
+
+ state->reg80 = val;
+
+setstd_exit:
+ if (err != 0)
+ v4l2_err(sd, "Error setting std, write failed\n");
+
+ return err;
+}
+
+static int adv7343_setoutput(struct v4l2_subdev *sd, u32 output_type)
+{
+ struct adv7343_state *state = to_state(sd);
+ unsigned char val;
+ int err = 0;
+
+ if (output_type > ADV7343_SVIDEO_ID) {
+ v4l2_dbg(1, debug, sd,
+ "Invalid output type or output type not supported:%d\n",
+ output_type);
+ return -EINVAL;
+ }
+
+ /* Enable Appropriate DAC */
+ val = state->reg00 & 0x03;
+
+ if (output_type == ADV7343_COMPOSITE_ID)
+ val |= ADV7343_COMPOSITE_POWER_VALUE;
+ else if (output_type == ADV7343_COMPONENT_ID)
+ val |= ADV7343_COMPONENT_POWER_VALUE;
+ else
+ val |= ADV7343_SVIDEO_POWER_VALUE;
+
+ err = adv7343_write(sd, ADV7343_POWER_MODE_REG, val);
+ if (err < 0)
+ goto setoutput_exit;
+
+ state->reg00 = val;
+
+ /* Enable YUV output */
+ val = state->reg02 | YUV_OUTPUT_SELECT;
+ err = adv7343_write(sd, ADV7343_MODE_REG0, val);
+ if (err < 0)
+ goto setoutput_exit;
+
+ state->reg02 = val;
+
+ /* configure SD DAC Output 2 and SD DAC Output 1 bit to zero */
+ val = state->reg82 & (SD_DAC_1_DI & SD_DAC_2_DI);
+ err = adv7343_write(sd, ADV7343_SD_MODE_REG2, val);
+ if (err < 0)
+ goto setoutput_exit;
+
+ state->reg82 = val;
+
+ /* configure ED/HD Color DAC Swap and ED/HD RGB Input Enable bit to
+ * zero */
+ val = state->reg35 & (HD_RGB_INPUT_DI & HD_DAC_SWAP_DI);
+ err = adv7343_write(sd, ADV7343_HD_MODE_REG6, val);
+ if (err < 0)
+ goto setoutput_exit;
+
+ state->reg35 = val;
+
+setoutput_exit:
+ if (err != 0)
+ v4l2_err(sd, "Error setting output, write failed\n");
+
+ return err;
+}
+
+static int adv7343_log_status(struct v4l2_subdev *sd)
+{
+ struct adv7343_state *state = to_state(sd);
+
+ v4l2_info(sd, "Standard: %llx\n", (unsigned long long)state->std);
+ v4l2_info(sd, "Output: %s\n", (state->output == 0) ? "Composite" :
+ ((state->output == 1) ? "Component" : "S-Video"));
+ return 0;
+}
+
+static int adv7343_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ return adv7343_write(sd, ADV7343_SD_BRIGHTNESS_WSS,
+ ctrl->val);
+
+ case V4L2_CID_HUE:
+ return adv7343_write(sd, ADV7343_SD_HUE_REG, ctrl->val);
+
+ case V4L2_CID_GAIN:
+ return adv7343_write(sd, ADV7343_DAC2_OUTPUT_LEVEL, ctrl->val);
+ }
+ return -EINVAL;
+}
+
+static int adv7343_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7343, 0);
+}
+
+static const struct v4l2_ctrl_ops adv7343_ctrl_ops = {
+ .s_ctrl = adv7343_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops adv7343_core_ops = {
+ .log_status = adv7343_log_status,
+ .g_chip_ident = adv7343_g_chip_ident,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+};
+
+static int adv7343_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct adv7343_state *state = to_state(sd);
+ int err = 0;
+
+ if (state->std == std)
+ return 0;
+
+ err = adv7343_setstd(sd, std);
+ if (!err)
+ state->std = std;
+
+ return err;
+}
+
+static int adv7343_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct adv7343_state *state = to_state(sd);
+ int err = 0;
+
+ if (state->output == output)
+ return 0;
+
+ err = adv7343_setoutput(sd, output);
+ if (!err)
+ state->output = output;
+
+ return err;
+}
+
+static const struct v4l2_subdev_video_ops adv7343_video_ops = {
+ .s_std_output = adv7343_s_std_output,
+ .s_routing = adv7343_s_routing,
+};
+
+static const struct v4l2_subdev_ops adv7343_ops = {
+ .core = &adv7343_core_ops,
+ .video = &adv7343_video_ops,
+};
+
+static int adv7343_initialize(struct v4l2_subdev *sd)
+{
+ struct adv7343_state *state = to_state(sd);
+ int err = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(adv7343_init_reg_val); i += 2) {
+
+ err = adv7343_write(sd, adv7343_init_reg_val[i],
+ adv7343_init_reg_val[i+1]);
+ if (err) {
+ v4l2_err(sd, "Error initializing\n");
+ return err;
+ }
+ }
+
+ /* Configure for default video standard */
+ err = adv7343_setoutput(sd, state->output);
+ if (err < 0) {
+ v4l2_err(sd, "Error setting output during init\n");
+ return -EINVAL;
+ }
+
+ err = adv7343_setstd(sd, state->std);
+ if (err < 0) {
+ v4l2_err(sd, "Error setting std during init\n");
+ return -EINVAL;
+ }
+
+ return err;
+}
+
+static int adv7343_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct adv7343_state *state;
+ int err;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ state = kzalloc(sizeof(struct adv7343_state), GFP_KERNEL);
+ if (state == NULL)
+ return -ENOMEM;
+
+ state->reg00 = 0x80;
+ state->reg01 = 0x00;
+ state->reg02 = 0x20;
+ state->reg35 = 0x00;
+ state->reg80 = ADV7343_SD_MODE_REG1_DEFAULT;
+ state->reg82 = ADV7343_SD_MODE_REG2_DEFAULT;
+
+ state->output = ADV7343_COMPOSITE_ID;
+ state->std = V4L2_STD_NTSC;
+
+ v4l2_i2c_subdev_init(&state->sd, client, &adv7343_ops);
+
+ v4l2_ctrl_handler_init(&state->hdl, 2);
+ v4l2_ctrl_new_std(&state->hdl, &adv7343_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, ADV7343_BRIGHTNESS_MIN,
+ ADV7343_BRIGHTNESS_MAX, 1,
+ ADV7343_BRIGHTNESS_DEF);
+ v4l2_ctrl_new_std(&state->hdl, &adv7343_ctrl_ops,
+ V4L2_CID_HUE, ADV7343_HUE_MIN,
+ ADV7343_HUE_MAX, 1,
+ ADV7343_HUE_DEF);
+ v4l2_ctrl_new_std(&state->hdl, &adv7343_ctrl_ops,
+ V4L2_CID_GAIN, ADV7343_GAIN_MIN,
+ ADV7343_GAIN_MAX, 1,
+ ADV7343_GAIN_DEF);
+ state->sd.ctrl_handler = &state->hdl;
+ if (state->hdl.error) {
+ int err = state->hdl.error;
+
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ return err;
+ }
+ v4l2_ctrl_handler_setup(&state->hdl);
+
+ err = adv7343_initialize(&state->sd);
+ if (err) {
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ }
+ return err;
+}
+
+static int adv7343_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct adv7343_state *state = to_state(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+
+ return 0;
+}
+
+static const struct i2c_device_id adv7343_id[] = {
+ {"adv7343", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, adv7343_id);
+
+static struct i2c_driver adv7343_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "adv7343",
+ },
+ .probe = adv7343_probe,
+ .remove = adv7343_remove,
+ .id_table = adv7343_id,
+};
+
+module_i2c_driver(adv7343_driver);
diff --git a/drivers/media/video/adv7343_regs.h b/drivers/media/video/adv7343_regs.h
new file mode 100644
index 00000000..44660676
--- /dev/null
+++ b/drivers/media/video/adv7343_regs.h
@@ -0,0 +1,181 @@
+/*
+ * ADV7343 encoder related structure and register definitions
+ *
+ * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed .as is. WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef ADV7343_REG_H
+#define ADV7343_REGS_H
+
+struct adv7343_std_info {
+ u32 standard_val3;
+ u32 fsc_val;
+ v4l2_std_id stdid;
+};
+
+/* Register offset macros */
+#define ADV7343_POWER_MODE_REG (0x00)
+#define ADV7343_MODE_SELECT_REG (0x01)
+#define ADV7343_MODE_REG0 (0x02)
+
+#define ADV7343_DAC2_OUTPUT_LEVEL (0x0b)
+
+#define ADV7343_SOFT_RESET (0x17)
+
+#define ADV7343_HD_MODE_REG1 (0x30)
+#define ADV7343_HD_MODE_REG2 (0x31)
+#define ADV7343_HD_MODE_REG3 (0x32)
+#define ADV7343_HD_MODE_REG4 (0x33)
+#define ADV7343_HD_MODE_REG5 (0x34)
+#define ADV7343_HD_MODE_REG6 (0x35)
+
+#define ADV7343_HD_MODE_REG7 (0x39)
+
+#define ADV7343_SD_MODE_REG1 (0x80)
+#define ADV7343_SD_MODE_REG2 (0x82)
+#define ADV7343_SD_MODE_REG3 (0x83)
+#define ADV7343_SD_MODE_REG4 (0x84)
+#define ADV7343_SD_MODE_REG5 (0x86)
+#define ADV7343_SD_MODE_REG6 (0x87)
+#define ADV7343_SD_MODE_REG7 (0x88)
+#define ADV7343_SD_MODE_REG8 (0x89)
+
+#define ADV7343_FSC_REG0 (0x8C)
+#define ADV7343_FSC_REG1 (0x8D)
+#define ADV7343_FSC_REG2 (0x8E)
+#define ADV7343_FSC_REG3 (0x8F)
+
+#define ADV7343_SD_CGMS_WSS0 (0x99)
+
+#define ADV7343_SD_HUE_REG (0xA0)
+#define ADV7343_SD_BRIGHTNESS_WSS (0xA1)
+
+/* Default values for the registers */
+#define ADV7343_POWER_MODE_REG_DEFAULT (0x10)
+#define ADV7343_HD_MODE_REG1_DEFAULT (0x3C) /* Changed Default
+ 720p EAVSAV code*/
+#define ADV7343_HD_MODE_REG2_DEFAULT (0x01) /* Changed Pixel data
+ valid */
+#define ADV7343_HD_MODE_REG3_DEFAULT (0x00) /* Color delay 0 clks */
+#define ADV7343_HD_MODE_REG4_DEFAULT (0xE8) /* Changed */
+#define ADV7343_HD_MODE_REG5_DEFAULT (0x08)
+#define ADV7343_HD_MODE_REG6_DEFAULT (0x00)
+#define ADV7343_HD_MODE_REG7_DEFAULT (0x00)
+#define ADV7343_SD_MODE_REG8_DEFAULT (0x00)
+#define ADV7343_SOFT_RESET_DEFAULT (0x02)
+#define ADV7343_COMPOSITE_POWER_VALUE (0x80)
+#define ADV7343_COMPONENT_POWER_VALUE (0x1C)
+#define ADV7343_SVIDEO_POWER_VALUE (0x60)
+#define ADV7343_SD_HUE_REG_DEFAULT (127)
+#define ADV7343_SD_BRIGHTNESS_WSS_DEFAULT (0x03)
+
+#define ADV7343_SD_CGMS_WSS0_DEFAULT (0x10)
+
+#define ADV7343_SD_MODE_REG1_DEFAULT (0x00)
+#define ADV7343_SD_MODE_REG2_DEFAULT (0xC9)
+#define ADV7343_SD_MODE_REG3_DEFAULT (0x10)
+#define ADV7343_SD_MODE_REG4_DEFAULT (0x01)
+#define ADV7343_SD_MODE_REG5_DEFAULT (0x02)
+#define ADV7343_SD_MODE_REG6_DEFAULT (0x0C)
+#define ADV7343_SD_MODE_REG7_DEFAULT (0x04)
+#define ADV7343_SD_MODE_REG8_DEFAULT (0x00)
+
+/* Bit masks for Mode Select Register */
+#define INPUT_MODE_MASK (0x70)
+#define SD_INPUT_MODE (0x00)
+#define HD_720P_INPUT_MODE (0x10)
+#define HD_1080I_INPUT_MODE (0x10)
+
+/* Bit masks for Mode Register 0 */
+#define TEST_PATTERN_BLACK_BAR_EN (0x04)
+#define YUV_OUTPUT_SELECT (0x20)
+#define RGB_OUTPUT_SELECT (0xDF)
+
+/* Bit masks for DAC output levels */
+#define DAC_OUTPUT_LEVEL_MASK (0xFF)
+
+/* Bit masks for soft reset register */
+#define SOFT_RESET (0x02)
+
+/* Bit masks for HD Mode Register 1 */
+#define OUTPUT_STD_MASK (0x03)
+#define OUTPUT_STD_SHIFT (0)
+#define OUTPUT_STD_EIA0_2 (0x00)
+#define OUTPUT_STD_EIA0_1 (0x01)
+#define OUTPUT_STD_FULL (0x02)
+#define EMBEDDED_SYNC (0x04)
+#define EXTERNAL_SYNC (0xFB)
+#define STD_MODE_SHIFT (3)
+#define STD_MODE_MASK (0x1F)
+#define STD_MODE_720P (0x05)
+#define STD_MODE_720P_25 (0x08)
+#define STD_MODE_720P_30 (0x07)
+#define STD_MODE_720P_50 (0x06)
+#define STD_MODE_1080I (0x0D)
+#define STD_MODE_1080I_25fps (0x0E)
+#define STD_MODE_1080P_24 (0x12)
+#define STD_MODE_1080P_25 (0x10)
+#define STD_MODE_1080P_30 (0x0F)
+#define STD_MODE_525P (0x00)
+#define STD_MODE_625P (0x03)
+
+/* Bit masks for SD Mode Register 1 */
+#define SD_STD_MASK (0x03)
+#define SD_STD_NTSC (0x00)
+#define SD_STD_PAL_BDGHI (0x01)
+#define SD_STD_PAL_M (0x02)
+#define SD_STD_PAL_N (0x03)
+#define SD_LUMA_FLTR_MASK (0x7)
+#define SD_LUMA_FLTR_SHIFT (0x2)
+#define SD_CHROMA_FLTR_MASK (0x7)
+#define SD_CHROMA_FLTR_SHIFT (0x5)
+
+/* Bit masks for SD Mode Register 2 */
+#define SD_PBPR_SSAF_EN (0x01)
+#define SD_PBPR_SSAF_DI (0xFE)
+#define SD_DAC_1_DI (0xFD)
+#define SD_DAC_2_DI (0xFB)
+#define SD_PEDESTAL_EN (0x08)
+#define SD_PEDESTAL_DI (0xF7)
+#define SD_SQUARE_PIXEL_EN (0x10)
+#define SD_SQUARE_PIXEL_DI (0xEF)
+#define SD_PIXEL_DATA_VALID (0x40)
+#define SD_ACTIVE_EDGE_EN (0x80)
+#define SD_ACTIVE_EDGE_DI (0x7F)
+
+/* Bit masks for HD Mode Register 6 */
+#define HD_RGB_INPUT_EN (0x02)
+#define HD_RGB_INPUT_DI (0xFD)
+#define HD_PBPR_SYNC_EN (0x04)
+#define HD_PBPR_SYNC_DI (0xFB)
+#define HD_DAC_SWAP_EN (0x08)
+#define HD_DAC_SWAP_DI (0xF7)
+#define HD_GAMMA_CURVE_A (0xEF)
+#define HD_GAMMA_CURVE_B (0x10)
+#define HD_GAMMA_EN (0x20)
+#define HD_GAMMA_DI (0xDF)
+#define HD_ADPT_FLTR_MODEB (0x40)
+#define HD_ADPT_FLTR_MODEA (0xBF)
+#define HD_ADPT_FLTR_EN (0x80)
+#define HD_ADPT_FLTR_DI (0x7F)
+
+#define ADV7343_BRIGHTNESS_MAX (127)
+#define ADV7343_BRIGHTNESS_MIN (0)
+#define ADV7343_BRIGHTNESS_DEF (3)
+#define ADV7343_HUE_MAX (255)
+#define ADV7343_HUE_MIN (0)
+#define ADV7343_HUE_DEF (127)
+#define ADV7343_GAIN_MAX (64)
+#define ADV7343_GAIN_MIN (-64)
+#define ADV7343_GAIN_DEF (0)
+
+#endif
diff --git a/drivers/media/video/ak881x.c b/drivers/media/video/ak881x.c
new file mode 100644
index 00000000..ba674656
--- /dev/null
+++ b/drivers/media/video/ak881x.c
@@ -0,0 +1,359 @@
+/*
+ * Driver for AK8813 / AK8814 TV-ecoders from Asahi Kasei Microsystems Co., Ltd. (AKM)
+ *
+ * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/module.h>
+
+#include <media/ak881x.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+
+#define AK881X_INTERFACE_MODE 0
+#define AK881X_VIDEO_PROCESS1 1
+#define AK881X_VIDEO_PROCESS2 2
+#define AK881X_VIDEO_PROCESS3 3
+#define AK881X_DAC_MODE 5
+#define AK881X_STATUS 0x24
+#define AK881X_DEVICE_ID 0x25
+#define AK881X_DEVICE_REVISION 0x26
+
+struct ak881x {
+ struct v4l2_subdev subdev;
+ struct ak881x_pdata *pdata;
+ unsigned int lines;
+ int id; /* DEVICE_ID code V4L2_IDENT_AK881X code from v4l2-chip-ident.h */
+ char revision; /* DEVICE_REVISION content */
+};
+
+static int reg_read(struct i2c_client *client, const u8 reg)
+{
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int reg_write(struct i2c_client *client, const u8 reg,
+ const u8 data)
+{
+ return i2c_smbus_write_byte_data(client, reg, data);
+}
+
+static int reg_set(struct i2c_client *client, const u8 reg,
+ const u8 data, u8 mask)
+{
+ int ret = reg_read(client, reg);
+ if (ret < 0)
+ return ret;
+ return reg_write(client, reg, (ret & ~mask) | (data & mask));
+}
+
+static struct ak881x *to_ak881x(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct ak881x, subdev);
+}
+
+static int ak881x_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ak881x *ak881x = to_ak881x(client);
+
+ if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+
+ if (id->match.addr != client->addr)
+ return -ENODEV;
+
+ id->ident = ak881x->id;
+ id->revision = ak881x->revision;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ak881x_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x26)
+ return -EINVAL;
+
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ reg->val = reg_read(client, reg->reg);
+
+ if (reg->val > 0xffff)
+ return -EIO;
+
+ return 0;
+}
+
+static int ak881x_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x26)
+ return -EINVAL;
+
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ if (reg_write(client, reg->reg, reg->val) < 0)
+ return -EIO;
+
+ return 0;
+}
+#endif
+
+static int ak881x_try_g_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ak881x *ak881x = to_ak881x(client);
+
+ v4l_bound_align_image(&mf->width, 0, 720, 2,
+ &mf->height, 0, ak881x->lines, 1, 0);
+ mf->field = V4L2_FIELD_INTERLACED;
+ mf->code = V4L2_MBUS_FMT_YUYV8_2X8;
+ mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+ return 0;
+}
+
+static int ak881x_s_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ if (mf->field != V4L2_FIELD_INTERLACED ||
+ mf->code != V4L2_MBUS_FMT_YUYV8_2X8)
+ return -EINVAL;
+
+ return ak881x_try_g_mbus_fmt(sd, mf);
+}
+
+static int ak881x_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index)
+ return -EINVAL;
+
+ *code = V4L2_MBUS_FMT_YUYV8_2X8;
+ return 0;
+}
+
+static int ak881x_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ak881x *ak881x = to_ak881x(client);
+
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ a->bounds.width = 720;
+ a->bounds.height = ak881x->lines;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int ak881x_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ak881x *ak881x = to_ak881x(client);
+ u8 vp1;
+
+ if (std == V4L2_STD_NTSC_443) {
+ vp1 = 3;
+ ak881x->lines = 480;
+ } else if (std == V4L2_STD_PAL_M) {
+ vp1 = 5;
+ ak881x->lines = 480;
+ } else if (std == V4L2_STD_PAL_60) {
+ vp1 = 7;
+ ak881x->lines = 480;
+ } else if (std && !(std & ~V4L2_STD_PAL)) {
+ vp1 = 0xf;
+ ak881x->lines = 576;
+ } else if (std && !(std & ~V4L2_STD_NTSC)) {
+ vp1 = 0;
+ ak881x->lines = 480;
+ } else {
+ /* No SECAM or PAL_N/Nc supported */
+ return -EINVAL;
+ }
+
+ reg_set(client, AK881X_VIDEO_PROCESS1, vp1, 0xf);
+
+ return 0;
+}
+
+static int ak881x_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ak881x *ak881x = to_ak881x(client);
+
+ if (enable) {
+ u8 dac;
+ /* For colour-bar testing set bit 6 of AK881X_VIDEO_PROCESS1 */
+ /* Default: composite output */
+ if (ak881x->pdata->flags & AK881X_COMPONENT)
+ dac = 3;
+ else
+ dac = 4;
+ /* Turn on the DAC(s) */
+ reg_write(client, AK881X_DAC_MODE, dac);
+ dev_dbg(&client->dev, "chip status 0x%x\n",
+ reg_read(client, AK881X_STATUS));
+ } else {
+ /* ...and clear bit 6 of AK881X_VIDEO_PROCESS1 here */
+ reg_write(client, AK881X_DAC_MODE, 0);
+ dev_dbg(&client->dev, "chip status 0x%x\n",
+ reg_read(client, AK881X_STATUS));
+ }
+
+ return 0;
+}
+
+static struct v4l2_subdev_core_ops ak881x_subdev_core_ops = {
+ .g_chip_ident = ak881x_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = ak881x_g_register,
+ .s_register = ak881x_s_register,
+#endif
+};
+
+static struct v4l2_subdev_video_ops ak881x_subdev_video_ops = {
+ .s_mbus_fmt = ak881x_s_mbus_fmt,
+ .g_mbus_fmt = ak881x_try_g_mbus_fmt,
+ .try_mbus_fmt = ak881x_try_g_mbus_fmt,
+ .cropcap = ak881x_cropcap,
+ .enum_mbus_fmt = ak881x_enum_mbus_fmt,
+ .s_std_output = ak881x_s_std_output,
+ .s_stream = ak881x_s_stream,
+};
+
+static struct v4l2_subdev_ops ak881x_subdev_ops = {
+ .core = &ak881x_subdev_core_ops,
+ .video = &ak881x_subdev_video_ops,
+};
+
+static int ak881x_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct ak881x *ak881x;
+ u8 ifmode, data;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_warn(&adapter->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
+ return -EIO;
+ }
+
+ ak881x = kzalloc(sizeof(struct ak881x), GFP_KERNEL);
+ if (!ak881x)
+ return -ENOMEM;
+
+ v4l2_i2c_subdev_init(&ak881x->subdev, client, &ak881x_subdev_ops);
+
+ data = reg_read(client, AK881X_DEVICE_ID);
+
+ switch (data) {
+ case 0x13:
+ ak881x->id = V4L2_IDENT_AK8813;
+ break;
+ case 0x14:
+ ak881x->id = V4L2_IDENT_AK8814;
+ break;
+ default:
+ dev_err(&client->dev,
+ "No ak881x chip detected, register read %x\n", data);
+ kfree(ak881x);
+ return -ENODEV;
+ }
+
+ ak881x->revision = reg_read(client, AK881X_DEVICE_REVISION);
+ ak881x->pdata = client->dev.platform_data;
+
+ if (ak881x->pdata) {
+ if (ak881x->pdata->flags & AK881X_FIELD)
+ ifmode = 4;
+ else
+ ifmode = 0;
+
+ switch (ak881x->pdata->flags & AK881X_IF_MODE_MASK) {
+ case AK881X_IF_MODE_BT656:
+ ifmode |= 1;
+ break;
+ case AK881X_IF_MODE_MASTER:
+ ifmode |= 2;
+ break;
+ case AK881X_IF_MODE_SLAVE:
+ default:
+ break;
+ }
+
+ dev_dbg(&client->dev, "IF mode %x\n", ifmode);
+
+ /*
+ * "Line Blanking No." seems to be the same as the number of
+ * "black" lines on, e.g., SuperH VOU, whose default value of 20
+ * "incidentally" matches ak881x' default
+ */
+ reg_write(client, AK881X_INTERFACE_MODE, ifmode | (20 << 3));
+ }
+
+ /* Hardware default: NTSC-M */
+ ak881x->lines = 480;
+
+ dev_info(&client->dev, "Detected an ak881x chip ID %x, revision %x\n",
+ data, ak881x->revision);
+
+ return 0;
+}
+
+static int ak881x_remove(struct i2c_client *client)
+{
+ struct ak881x *ak881x = to_ak881x(client);
+
+ v4l2_device_unregister_subdev(&ak881x->subdev);
+ kfree(ak881x);
+
+ return 0;
+}
+
+static const struct i2c_device_id ak881x_id[] = {
+ { "ak8813", 0 },
+ { "ak8814", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ak881x_id);
+
+static struct i2c_driver ak881x_i2c_driver = {
+ .driver = {
+ .name = "ak881x",
+ },
+ .probe = ak881x_probe,
+ .remove = ak881x_remove,
+ .id_table = ak881x_id,
+};
+
+module_i2c_driver(ak881x_i2c_driver);
+
+MODULE_DESCRIPTION("TV-output driver for ak8813/ak8814");
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/aptina-pll.c b/drivers/media/video/aptina-pll.c
new file mode 100644
index 00000000..0bd3813b
--- /dev/null
+++ b/drivers/media/video/aptina-pll.c
@@ -0,0 +1,174 @@
+/*
+ * Aptina Sensor PLL Configuration
+ *
+ * Copyright (C) 2012 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/device.h>
+#include <linux/gcd.h>
+#include <linux/kernel.h>
+#include <linux/lcm.h>
+#include <linux/module.h>
+
+#include "aptina-pll.h"
+
+int aptina_pll_calculate(struct device *dev,
+ const struct aptina_pll_limits *limits,
+ struct aptina_pll *pll)
+{
+ unsigned int mf_min;
+ unsigned int mf_max;
+ unsigned int p1_min;
+ unsigned int p1_max;
+ unsigned int p1;
+ unsigned int div;
+
+ dev_dbg(dev, "PLL: ext clock %u pix clock %u\n",
+ pll->ext_clock, pll->pix_clock);
+
+ if (pll->ext_clock < limits->ext_clock_min ||
+ pll->ext_clock > limits->ext_clock_max) {
+ dev_err(dev, "pll: invalid external clock frequency.\n");
+ return -EINVAL;
+ }
+
+ if (pll->pix_clock == 0 || pll->pix_clock > limits->pix_clock_max) {
+ dev_err(dev, "pll: invalid pixel clock frequency.\n");
+ return -EINVAL;
+ }
+
+ /* Compute the multiplier M and combined N*P1 divisor. */
+ div = gcd(pll->pix_clock, pll->ext_clock);
+ pll->m = pll->pix_clock / div;
+ div = pll->ext_clock / div;
+
+ /* We now have the smallest M and N*P1 values that will result in the
+ * desired pixel clock frequency, but they might be out of the valid
+ * range. Compute the factor by which we should multiply them given the
+ * following constraints:
+ *
+ * - minimum/maximum multiplier
+ * - minimum/maximum multiplier output clock frequency assuming the
+ * minimum/maximum N value
+ * - minimum/maximum combined N*P1 divisor
+ */
+ mf_min = DIV_ROUND_UP(limits->m_min, pll->m);
+ mf_min = max(mf_min, limits->out_clock_min /
+ (pll->ext_clock / limits->n_min * pll->m));
+ mf_min = max(mf_min, limits->n_min * limits->p1_min / div);
+ mf_max = limits->m_max / pll->m;
+ mf_max = min(mf_max, limits->out_clock_max /
+ (pll->ext_clock / limits->n_max * pll->m));
+ mf_max = min(mf_max, DIV_ROUND_UP(limits->n_max * limits->p1_max, div));
+
+ dev_dbg(dev, "pll: mf min %u max %u\n", mf_min, mf_max);
+ if (mf_min > mf_max) {
+ dev_err(dev, "pll: no valid combined N*P1 divisor.\n");
+ return -EINVAL;
+ }
+
+ /*
+ * We're looking for the highest acceptable P1 value for which a
+ * multiplier factor MF exists that fulfills the following conditions:
+ *
+ * 1. p1 is in the [p1_min, p1_max] range given by the limits and is
+ * even
+ * 2. mf is in the [mf_min, mf_max] range computed above
+ * 3. div * mf is a multiple of p1, in order to compute
+ * n = div * mf / p1
+ * m = pll->m * mf
+ * 4. the internal clock frequency, given by ext_clock / n, is in the
+ * [int_clock_min, int_clock_max] range given by the limits
+ * 5. the output clock frequency, given by ext_clock / n * m, is in the
+ * [out_clock_min, out_clock_max] range given by the limits
+ *
+ * The first naive approach is to iterate over all p1 values acceptable
+ * according to (1) and all mf values acceptable according to (2), and
+ * stop at the first combination that fulfills (3), (4) and (5). This
+ * has a O(n^2) complexity.
+ *
+ * Instead of iterating over all mf values in the [mf_min, mf_max] range
+ * we can compute the mf increment between two acceptable values
+ * according to (3) with
+ *
+ * mf_inc = p1 / gcd(div, p1) (6)
+ *
+ * and round the minimum up to the nearest multiple of mf_inc. This will
+ * restrict the number of mf values to be checked.
+ *
+ * Furthermore, conditions (4) and (5) only restrict the range of
+ * acceptable p1 and mf values by modifying the minimum and maximum
+ * limits. (5) can be expressed as
+ *
+ * ext_clock / (div * mf / p1) * m * mf >= out_clock_min
+ * ext_clock / (div * mf / p1) * m * mf <= out_clock_max
+ *
+ * or
+ *
+ * p1 >= out_clock_min * div / (ext_clock * m) (7)
+ * p1 <= out_clock_max * div / (ext_clock * m)
+ *
+ * Similarly, (4) can be expressed as
+ *
+ * mf >= ext_clock * p1 / (int_clock_max * div) (8)
+ * mf <= ext_clock * p1 / (int_clock_min * div)
+ *
+ * We can thus iterate over the restricted p1 range defined by the
+ * combination of (1) and (7), and then compute the restricted mf range
+ * defined by the combination of (2), (6) and (8). If the resulting mf
+ * range is not empty, any value in the mf range is acceptable. We thus
+ * select the mf lwoer bound and the corresponding p1 value.
+ */
+ if (limits->p1_min == 0) {
+ dev_err(dev, "pll: P1 minimum value must be >0.\n");
+ return -EINVAL;
+ }
+
+ p1_min = max(limits->p1_min, DIV_ROUND_UP(limits->out_clock_min * div,
+ pll->ext_clock * pll->m));
+ p1_max = min(limits->p1_max, limits->out_clock_max * div /
+ (pll->ext_clock * pll->m));
+
+ for (p1 = p1_max & ~1; p1 >= p1_min; p1 -= 2) {
+ unsigned int mf_inc = p1 / gcd(div, p1);
+ unsigned int mf_high;
+ unsigned int mf_low;
+
+ mf_low = max(roundup(mf_min, mf_inc),
+ DIV_ROUND_UP(pll->ext_clock * p1,
+ limits->int_clock_max * div));
+ mf_high = min(mf_max, pll->ext_clock * p1 /
+ (limits->int_clock_min * div));
+
+ if (mf_low > mf_high)
+ continue;
+
+ pll->n = div * mf_low / p1;
+ pll->m *= mf_low;
+ pll->p1 = p1;
+ dev_dbg(dev, "PLL: N %u M %u P1 %u\n", pll->n, pll->m, pll->p1);
+ return 0;
+ }
+
+ dev_err(dev, "pll: no valid N and P1 divisors found.\n");
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(aptina_pll_calculate);
+
+MODULE_DESCRIPTION("Aptina PLL Helpers");
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/aptina-pll.h b/drivers/media/video/aptina-pll.h
new file mode 100644
index 00000000..b370e341
--- /dev/null
+++ b/drivers/media/video/aptina-pll.h
@@ -0,0 +1,56 @@
+/*
+ * Aptina Sensor PLL Configuration
+ *
+ * Copyright (C) 2012 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef __APTINA_PLL_H
+#define __APTINA_PLL_H
+
+struct aptina_pll {
+ unsigned int ext_clock;
+ unsigned int pix_clock;
+
+ unsigned int n;
+ unsigned int m;
+ unsigned int p1;
+};
+
+struct aptina_pll_limits {
+ unsigned int ext_clock_min;
+ unsigned int ext_clock_max;
+ unsigned int int_clock_min;
+ unsigned int int_clock_max;
+ unsigned int out_clock_min;
+ unsigned int out_clock_max;
+ unsigned int pix_clock_max;
+
+ unsigned int n_min;
+ unsigned int n_max;
+ unsigned int m_min;
+ unsigned int m_max;
+ unsigned int p1_min;
+ unsigned int p1_max;
+};
+
+struct device;
+
+int aptina_pll_calculate(struct device *dev,
+ const struct aptina_pll_limits *limits,
+ struct aptina_pll *pll);
+
+#endif /* __APTINA_PLL_H */
diff --git a/drivers/media/video/arv.c b/drivers/media/video/arv.c
new file mode 100644
index 00000000..b6ed44ae
--- /dev/null
+++ b/drivers/media/video/arv.c
@@ -0,0 +1,880 @@
+/*
+ * Colour AR M64278(VGA) driver for Video4Linux
+ *
+ * Copyright (C) 2003 Takeo Takahashi <takahashi.takeo@renesas.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Some code is taken from AR driver sample program for M3T-M32700UT.
+ *
+ * AR driver sample (M32R SDK):
+ * Copyright (c) 2003 RENESAS TECHNOROGY CORPORATION
+ * AND RENESAS SOLUTIONS CORPORATION
+ * All Rights Reserved.
+ *
+ * 2003-09-01: Support w3cam by Takeo Takahashi
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <linux/mutex.h>
+
+#include <asm/uaccess.h>
+#include <asm/m32r.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/byteorder.h>
+
+#if 0
+#define DEBUG(n, args...) printk(KERN_INFO args)
+#define CHECK_LOST 1
+#else
+#define DEBUG(n, args...)
+#define CHECK_LOST 0
+#endif
+
+/*
+ * USE_INT is always 0, interrupt mode is not available
+ * on linux due to lack of speed
+ */
+#define USE_INT 0 /* Don't modify */
+
+#define VERSION "0.0.5"
+
+#define ar_inl(addr) inl((unsigned long)(addr))
+#define ar_outl(val, addr) outl((unsigned long)(val), (unsigned long)(addr))
+
+extern struct cpuinfo_m32r boot_cpu_data;
+
+/*
+ * CCD pixel size
+ * Note that M32700UT does not support CIF mode, but QVGA is
+ * supported by M32700UT hardware using VGA mode of AR LSI.
+ *
+ * Supported: VGA (Normal mode, Interlace mode)
+ * QVGA (Always Interlace mode of VGA)
+ *
+ */
+#define AR_WIDTH_VGA 640
+#define AR_HEIGHT_VGA 480
+#define AR_WIDTH_QVGA 320
+#define AR_HEIGHT_QVGA 240
+#define MIN_AR_WIDTH AR_WIDTH_QVGA
+#define MIN_AR_HEIGHT AR_HEIGHT_QVGA
+#define MAX_AR_WIDTH AR_WIDTH_VGA
+#define MAX_AR_HEIGHT AR_HEIGHT_VGA
+
+/* bits & bytes per pixel */
+#define AR_BITS_PER_PIXEL 16
+#define AR_BYTES_PER_PIXEL (AR_BITS_PER_PIXEL / 8)
+
+/* line buffer size */
+#define AR_LINE_BYTES_VGA (AR_WIDTH_VGA * AR_BYTES_PER_PIXEL)
+#define AR_LINE_BYTES_QVGA (AR_WIDTH_QVGA * AR_BYTES_PER_PIXEL)
+#define MAX_AR_LINE_BYTES AR_LINE_BYTES_VGA
+
+/* frame size & type */
+#define AR_FRAME_BYTES_VGA \
+ (AR_WIDTH_VGA * AR_HEIGHT_VGA * AR_BYTES_PER_PIXEL)
+#define AR_FRAME_BYTES_QVGA \
+ (AR_WIDTH_QVGA * AR_HEIGHT_QVGA * AR_BYTES_PER_PIXEL)
+#define MAX_AR_FRAME_BYTES \
+ (MAX_AR_WIDTH * MAX_AR_HEIGHT * AR_BYTES_PER_PIXEL)
+
+#define AR_MAX_FRAME 15
+
+/* capture size */
+#define AR_SIZE_VGA 0
+#define AR_SIZE_QVGA 1
+
+/* capture mode */
+#define AR_MODE_INTERLACE 0
+#define AR_MODE_NORMAL 1
+
+struct ar {
+ struct v4l2_device v4l2_dev;
+ struct video_device vdev;
+ unsigned int start_capture; /* duaring capture in INT. mode. */
+#if USE_INT
+ unsigned char *line_buff; /* DMA line buffer */
+#endif
+ unsigned char *frame[MAX_AR_HEIGHT]; /* frame data */
+ short size; /* capture size */
+ short mode; /* capture mode */
+ int width, height;
+ int frame_bytes, line_bytes;
+ wait_queue_head_t wait;
+ struct mutex lock;
+};
+
+static struct ar ardev;
+
+static int video_nr = -1; /* video device number (first free) */
+static unsigned char yuv[MAX_AR_FRAME_BYTES];
+
+/* module parameters */
+/* default frequency */
+#define DEFAULT_FREQ 50 /* 50 or 75 (MHz) is available as BCLK */
+static int freq = DEFAULT_FREQ; /* BCLK: available 50 or 70 (MHz) */
+static int vga; /* default mode(0:QVGA mode, other:VGA mode) */
+static int vga_interlace; /* 0 is normal mode for, else interlace mode */
+module_param(freq, int, 0);
+module_param(vga, int, 0);
+module_param(vga_interlace, int, 0);
+
+static void wait_for_vsync(void)
+{
+ while (ar_inl(ARVCR0) & ARVCR0_VDS) /* wait for VSYNC */
+ cpu_relax();
+ while (!(ar_inl(ARVCR0) & ARVCR0_VDS)) /* wait for VSYNC */
+ cpu_relax();
+}
+
+static void wait_acknowledge(void)
+{
+ int i;
+
+ for (i = 0; i < 1000; i++)
+ cpu_relax();
+ while (ar_inl(PLDI2CSTS) & PLDI2CSTS_NOACK)
+ cpu_relax();
+}
+
+/*******************************************************************
+ * I2C functions
+ *******************************************************************/
+static void iic(int n, unsigned long addr, unsigned long data1, unsigned long data2,
+ unsigned long data3)
+{
+ int i;
+
+ /* Slave Address */
+ ar_outl(addr, PLDI2CDATA);
+ wait_for_vsync();
+
+ /* Start */
+ ar_outl(1, PLDI2CCND);
+ wait_acknowledge();
+
+ /* Transfer data 1 */
+ ar_outl(data1, PLDI2CDATA);
+ wait_for_vsync();
+ ar_outl(PLDI2CSTEN_STEN, PLDI2CSTEN);
+ wait_acknowledge();
+
+ /* Transfer data 2 */
+ ar_outl(data2, PLDI2CDATA);
+ wait_for_vsync();
+ ar_outl(PLDI2CSTEN_STEN, PLDI2CSTEN);
+ wait_acknowledge();
+
+ if (n == 3) {
+ /* Transfer data 3 */
+ ar_outl(data3, PLDI2CDATA);
+ wait_for_vsync();
+ ar_outl(PLDI2CSTEN_STEN, PLDI2CSTEN);
+ wait_acknowledge();
+ }
+
+ /* Stop */
+ for (i = 0; i < 100; i++)
+ cpu_relax();
+ ar_outl(2, PLDI2CCND);
+ ar_outl(2, PLDI2CCND);
+
+ while (ar_inl(PLDI2CSTS) & PLDI2CSTS_BB)
+ cpu_relax();
+}
+
+
+static void init_iic(void)
+{
+ DEBUG(1, "init_iic:\n");
+
+ /*
+ * ICU Setting (iic)
+ */
+ /* I2C Setting */
+ ar_outl(0x0, PLDI2CCR); /* I2CCR Disable */
+ ar_outl(0x0300, PLDI2CMOD); /* I2CMOD ACK/8b-data/7b-addr/auto */
+ ar_outl(0x1, PLDI2CACK); /* I2CACK ACK */
+
+ /* I2C CLK */
+ /* 50MH-100k */
+ if (freq == 75)
+ ar_outl(369, PLDI2CFREQ); /* BCLK = 75MHz */
+ else if (freq == 50)
+ ar_outl(244, PLDI2CFREQ); /* BCLK = 50MHz */
+ else
+ ar_outl(244, PLDI2CFREQ); /* default: BCLK = 50MHz */
+ ar_outl(0x1, PLDI2CCR); /* I2CCR Enable */
+}
+
+/**************************************************************************
+ *
+ * Video4Linux Interface functions
+ *
+ **************************************************************************/
+
+static inline void disable_dma(void)
+{
+ ar_outl(0x8000, M32R_DMAEN_PORTL); /* disable DMA0 */
+}
+
+static inline void enable_dma(void)
+{
+ ar_outl(0x8080, M32R_DMAEN_PORTL); /* enable DMA0 */
+}
+
+static inline void clear_dma_status(void)
+{
+ ar_outl(0x8000, M32R_DMAEDET_PORTL); /* clear status */
+}
+
+static void wait_for_vertical_sync(struct ar *ar, int exp_line)
+{
+#if CHECK_LOST
+ int tmout = 10000; /* FIXME */
+ int l;
+
+ /*
+ * check HCOUNT because we cannot check vertical sync.
+ */
+ for (; tmout >= 0; tmout--) {
+ l = ar_inl(ARVHCOUNT);
+ if (l == exp_line)
+ break;
+ }
+ if (tmout < 0)
+ v4l2_err(&ar->v4l2_dev, "lost %d -> %d\n", exp_line, l);
+#else
+ while (ar_inl(ARVHCOUNT) != exp_line)
+ cpu_relax();
+#endif
+}
+
+static ssize_t ar_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+ struct ar *ar = video_drvdata(file);
+ long ret = ar->frame_bytes; /* return read bytes */
+ unsigned long arvcr1 = 0;
+ unsigned long flags;
+ unsigned char *p;
+ int h, w;
+ unsigned char *py, *pu, *pv;
+#if !USE_INT
+ int l;
+#endif
+
+ DEBUG(1, "ar_read()\n");
+
+ if (ar->size == AR_SIZE_QVGA)
+ arvcr1 |= ARVCR1_QVGA;
+ if (ar->mode == AR_MODE_NORMAL)
+ arvcr1 |= ARVCR1_NORMAL;
+
+ mutex_lock(&ar->lock);
+
+#if USE_INT
+ local_irq_save(flags);
+ disable_dma();
+ ar_outl(0xa1871300, M32R_DMA0CR0_PORTL);
+ ar_outl(0x01000000, M32R_DMA0CR1_PORTL);
+
+ /* set AR FIFO address as source(BSEL5) */
+ ar_outl(ARDATA32, M32R_DMA0CSA_PORTL);
+ ar_outl(ARDATA32, M32R_DMA0RSA_PORTL);
+ ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL); /* destination addr. */
+ ar_outl(ar->line_buff, M32R_DMA0RDA_PORTL); /* reload address */
+ ar_outl(ar->line_bytes, M32R_DMA0CBCUT_PORTL); /* byte count (bytes) */
+ ar_outl(ar->line_bytes, M32R_DMA0RBCUT_PORTL); /* reload count (bytes) */
+
+ /*
+ * Okay, kick AR LSI to invoke an interrupt
+ */
+ ar->start_capture = 0;
+ ar_outl(arvcr1 | ARVCR1_HIEN, ARVCR1);
+ local_irq_restore(flags);
+ /* .... AR interrupts .... */
+ interruptible_sleep_on(&ar->wait);
+ if (signal_pending(current)) {
+ printk(KERN_ERR "arv: interrupted while get frame data.\n");
+ ret = -EINTR;
+ goto out_up;
+ }
+#else /* ! USE_INT */
+ /* polling */
+ ar_outl(arvcr1, ARVCR1);
+ disable_dma();
+ ar_outl(0x8000, M32R_DMAEDET_PORTL);
+ ar_outl(0xa0861300, M32R_DMA0CR0_PORTL);
+ ar_outl(0x01000000, M32R_DMA0CR1_PORTL);
+ ar_outl(ARDATA32, M32R_DMA0CSA_PORTL);
+ ar_outl(ARDATA32, M32R_DMA0RSA_PORTL);
+ ar_outl(ar->line_bytes, M32R_DMA0CBCUT_PORTL);
+ ar_outl(ar->line_bytes, M32R_DMA0RBCUT_PORTL);
+
+ local_irq_save(flags);
+ while (ar_inl(ARVHCOUNT) != 0) /* wait for 0 */
+ cpu_relax();
+ if (ar->mode == AR_MODE_INTERLACE && ar->size == AR_SIZE_VGA) {
+ for (h = 0; h < ar->height; h++) {
+ wait_for_vertical_sync(ar, h);
+ if (h < (AR_HEIGHT_VGA/2))
+ l = h << 1;
+ else
+ l = (((h - (AR_HEIGHT_VGA/2)) << 1) + 1);
+ ar_outl(virt_to_phys(ar->frame[l]), M32R_DMA0CDA_PORTL);
+ enable_dma();
+ while (!(ar_inl(M32R_DMAEDET_PORTL) & 0x8000))
+ cpu_relax();
+ disable_dma();
+ clear_dma_status();
+ ar_outl(0xa0861300, M32R_DMA0CR0_PORTL);
+ }
+ } else {
+ for (h = 0; h < ar->height; h++) {
+ wait_for_vertical_sync(ar, h);
+ ar_outl(virt_to_phys(ar->frame[h]), M32R_DMA0CDA_PORTL);
+ enable_dma();
+ while (!(ar_inl(M32R_DMAEDET_PORTL) & 0x8000))
+ cpu_relax();
+ disable_dma();
+ clear_dma_status();
+ ar_outl(0xa0861300, M32R_DMA0CR0_PORTL);
+ }
+ }
+ local_irq_restore(flags);
+#endif /* ! USE_INT */
+
+ /*
+ * convert YUV422 to YUV422P
+ * +--------------------+
+ * | Y0,Y1,... |
+ * | ..............Yn |
+ * +--------------------+
+ * | U0,U1,........Un |
+ * +--------------------+
+ * | V0,V1,........Vn |
+ * +--------------------+
+ */
+ py = yuv;
+ pu = py + (ar->frame_bytes / 2);
+ pv = pu + (ar->frame_bytes / 4);
+ for (h = 0; h < ar->height; h++) {
+ p = ar->frame[h];
+ for (w = 0; w < ar->line_bytes; w += 4) {
+ *py++ = *p++;
+ *pu++ = *p++;
+ *py++ = *p++;
+ *pv++ = *p++;
+ }
+ }
+ if (copy_to_user(buf, yuv, ar->frame_bytes)) {
+ v4l2_err(&ar->v4l2_dev, "failed while copy_to_user yuv.\n");
+ ret = -EFAULT;
+ goto out_up;
+ }
+ DEBUG(1, "ret = %d\n", ret);
+out_up:
+ mutex_unlock(&ar->lock);
+ return ret;
+}
+
+static int ar_querycap(struct file *file, void *priv,
+ struct v4l2_capability *vcap)
+{
+ struct ar *ar = video_drvdata(file);
+
+ strlcpy(vcap->driver, ar->vdev.name, sizeof(vcap->driver));
+ strlcpy(vcap->card, "Colour AR VGA", sizeof(vcap->card));
+ strlcpy(vcap->bus_info, "Platform", sizeof(vcap->bus_info));
+ vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
+ return 0;
+}
+
+static int ar_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
+{
+ if (vin->index > 0)
+ return -EINVAL;
+ strlcpy(vin->name, "Camera", sizeof(vin->name));
+ vin->type = V4L2_INPUT_TYPE_CAMERA;
+ vin->audioset = 0;
+ vin->tuner = 0;
+ vin->std = V4L2_STD_ALL;
+ vin->status = 0;
+ return 0;
+}
+
+static int ar_g_input(struct file *file, void *fh, unsigned int *inp)
+{
+ *inp = 0;
+ return 0;
+}
+
+static int ar_s_input(struct file *file, void *fh, unsigned int inp)
+{
+ return inp ? -EINVAL : 0;
+}
+
+static int ar_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct ar *ar = video_drvdata(file);
+ struct v4l2_pix_format *pix = &fmt->fmt.pix;
+
+ pix->width = ar->width;
+ pix->height = ar->height;
+ pix->pixelformat = V4L2_PIX_FMT_YUV422P;
+ pix->field = (ar->mode == AR_MODE_NORMAL) ? V4L2_FIELD_NONE : V4L2_FIELD_INTERLACED;
+ pix->bytesperline = ar->width;
+ pix->sizeimage = 2 * ar->width * ar->height;
+ /* Just a guess */
+ pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ return 0;
+}
+
+static int ar_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct ar *ar = video_drvdata(file);
+ struct v4l2_pix_format *pix = &fmt->fmt.pix;
+
+ if (pix->height <= AR_HEIGHT_QVGA || pix->width <= AR_WIDTH_QVGA) {
+ pix->height = AR_HEIGHT_QVGA;
+ pix->width = AR_WIDTH_QVGA;
+ pix->field = V4L2_FIELD_INTERLACED;
+ } else {
+ pix->height = AR_HEIGHT_VGA;
+ pix->width = AR_WIDTH_VGA;
+ pix->field = vga_interlace ? V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE;
+ }
+ pix->pixelformat = V4L2_PIX_FMT_YUV422P;
+ pix->bytesperline = ar->width;
+ pix->sizeimage = 2 * ar->width * ar->height;
+ /* Just a guess */
+ pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ return 0;
+}
+
+static int ar_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct ar *ar = video_drvdata(file);
+ struct v4l2_pix_format *pix = &fmt->fmt.pix;
+ int ret = ar_try_fmt_vid_cap(file, fh, fmt);
+
+ if (ret)
+ return ret;
+ mutex_lock(&ar->lock);
+ ar->width = pix->width;
+ ar->height = pix->height;
+ if (ar->width == AR_WIDTH_VGA) {
+ ar->size = AR_SIZE_VGA;
+ ar->frame_bytes = AR_FRAME_BYTES_VGA;
+ ar->line_bytes = AR_LINE_BYTES_VGA;
+ if (vga_interlace)
+ ar->mode = AR_MODE_INTERLACE;
+ else
+ ar->mode = AR_MODE_NORMAL;
+ } else {
+ ar->size = AR_SIZE_QVGA;
+ ar->frame_bytes = AR_FRAME_BYTES_QVGA;
+ ar->line_bytes = AR_LINE_BYTES_QVGA;
+ ar->mode = AR_MODE_INTERLACE;
+ }
+ /* Ok we figured out what to use from our wide choice */
+ mutex_unlock(&ar->lock);
+ return 0;
+}
+
+static int ar_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
+{
+ static struct v4l2_fmtdesc formats[] = {
+ { 0, 0, 0,
+ "YUV 4:2:2 Planar", V4L2_PIX_FMT_YUV422P,
+ { 0, 0, 0, 0 }
+ },
+ };
+ enum v4l2_buf_type type = fmt->type;
+
+ if (fmt->index > 0)
+ return -EINVAL;
+
+ *fmt = formats[fmt->index];
+ fmt->type = type;
+ return 0;
+}
+
+#if USE_INT
+/*
+ * Interrupt handler
+ */
+static void ar_interrupt(int irq, void *dev)
+{
+ struct ar *ar = dev;
+ unsigned int line_count;
+ unsigned int line_number;
+ unsigned int arvcr1;
+
+ line_count = ar_inl(ARVHCOUNT); /* line number */
+ if (ar->mode == AR_MODE_INTERLACE && ar->size == AR_SIZE_VGA) {
+ /* operations for interlace mode */
+ if (line_count < (AR_HEIGHT_VGA / 2)) /* even line */
+ line_number = (line_count << 1);
+ else /* odd line */
+ line_number =
+ (((line_count - (AR_HEIGHT_VGA / 2)) << 1) + 1);
+ } else {
+ line_number = line_count;
+ }
+
+ if (line_number == 0) {
+ /*
+ * It is an interrupt for line 0.
+ * we have to start capture.
+ */
+ disable_dma();
+#if 0
+ ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL); /* needless? */
+#endif
+ memcpy(ar->frame[0], ar->line_buff, ar->line_bytes);
+#if 0
+ ar_outl(0xa1861300, M32R_DMA0CR0_PORTL);
+#endif
+ enable_dma();
+ ar->start_capture = 1; /* during capture */
+ return;
+ }
+
+ if (ar->start_capture == 1 && line_number <= (ar->height - 1)) {
+ disable_dma();
+ memcpy(ar->frame[line_number], ar->line_buff, ar->line_bytes);
+
+ /*
+ * if captured all line of a frame, disable AR interrupt
+ * and wake a process up.
+ */
+ if (line_number == (ar->height - 1)) { /* end of line */
+
+ ar->start_capture = 0;
+
+ /* disable AR interrupt request */
+ arvcr1 = ar_inl(ARVCR1);
+ arvcr1 &= ~ARVCR1_HIEN; /* clear int. flag */
+ ar_outl(arvcr1, ARVCR1); /* disable */
+ wake_up_interruptible(&ar->wait);
+ } else {
+#if 0
+ ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL);
+ ar_outl(0xa1861300, M32R_DMA0CR0_PORTL);
+#endif
+ enable_dma();
+ }
+ }
+}
+#endif
+
+/*
+ * ar_initialize()
+ * ar_initialize() is called by video_register_device() and
+ * initializes AR LSI and peripherals.
+ *
+ * -1 is returned in all failures.
+ * 0 is returned in success.
+ *
+ */
+static int ar_initialize(struct ar *ar)
+{
+ unsigned long cr = 0;
+ int i, found = 0;
+
+ DEBUG(1, "ar_initialize:\n");
+
+ /*
+ * initialize AR LSI
+ */
+ ar_outl(0, ARVCR0); /* assert reset of AR LSI */
+ for (i = 0; i < 0x18; i++) /* wait for over 10 cycles @ 27MHz */
+ cpu_relax();
+ ar_outl(ARVCR0_RST, ARVCR0); /* negate reset of AR LSI (enable) */
+ for (i = 0; i < 0x40d; i++) /* wait for over 420 cycles @ 27MHz */
+ cpu_relax();
+
+ /* AR uses INT3 of CPU as interrupt pin. */
+ ar_outl(ARINTSEL_INT3, ARINTSEL);
+
+ if (ar->size == AR_SIZE_QVGA)
+ cr |= ARVCR1_QVGA;
+ if (ar->mode == AR_MODE_NORMAL)
+ cr |= ARVCR1_NORMAL;
+ ar_outl(cr, ARVCR1);
+
+ /*
+ * Initialize IIC so that CPU can communicate with AR LSI,
+ * and send boot commands to AR LSI.
+ */
+ init_iic();
+
+ for (i = 0; i < 0x100000; i++) { /* > 0xa1d10, 56ms */
+ if ((ar_inl(ARVCR0) & ARVCR0_VDS)) { /* VSYNC */
+ found = 1;
+ break;
+ }
+ }
+
+ if (found == 0)
+ return -ENODEV;
+
+ v4l2_info(&ar->v4l2_dev, "Initializing ");
+
+ iic(2, 0x78, 0x11, 0x01, 0x00); /* start */
+ iic(3, 0x78, 0x12, 0x00, 0x06);
+ iic(3, 0x78, 0x12, 0x12, 0x30);
+ iic(3, 0x78, 0x12, 0x15, 0x58);
+ iic(3, 0x78, 0x12, 0x17, 0x30);
+ printk(KERN_CONT ".");
+ iic(3, 0x78, 0x12, 0x1a, 0x97);
+ iic(3, 0x78, 0x12, 0x1b, 0xff);
+ iic(3, 0x78, 0x12, 0x1c, 0xff);
+ iic(3, 0x78, 0x12, 0x26, 0x10);
+ iic(3, 0x78, 0x12, 0x27, 0x00);
+ printk(KERN_CONT ".");
+ iic(2, 0x78, 0x34, 0x02, 0x00);
+ iic(2, 0x78, 0x7a, 0x10, 0x00);
+ iic(2, 0x78, 0x80, 0x39, 0x00);
+ iic(2, 0x78, 0x81, 0xe6, 0x00);
+ iic(2, 0x78, 0x8d, 0x00, 0x00);
+ printk(KERN_CONT ".");
+ iic(2, 0x78, 0x8e, 0x0c, 0x00);
+ iic(2, 0x78, 0x8f, 0x00, 0x00);
+#if 0
+ iic(2, 0x78, 0x90, 0x00, 0x00); /* AWB on=1 off=0 */
+#endif
+ iic(2, 0x78, 0x93, 0x01, 0x00);
+ iic(2, 0x78, 0x94, 0xcd, 0x00);
+ iic(2, 0x78, 0x95, 0x00, 0x00);
+ printk(KERN_CONT ".");
+ iic(2, 0x78, 0x96, 0xa0, 0x00);
+ iic(2, 0x78, 0x97, 0x00, 0x00);
+ iic(2, 0x78, 0x98, 0x60, 0x00);
+ iic(2, 0x78, 0x99, 0x01, 0x00);
+ iic(2, 0x78, 0x9a, 0x19, 0x00);
+ printk(KERN_CONT ".");
+ iic(2, 0x78, 0x9b, 0x02, 0x00);
+ iic(2, 0x78, 0x9c, 0xe8, 0x00);
+ iic(2, 0x78, 0x9d, 0x02, 0x00);
+ iic(2, 0x78, 0x9e, 0x2e, 0x00);
+ iic(2, 0x78, 0xb8, 0x78, 0x00);
+ iic(2, 0x78, 0xba, 0x05, 0x00);
+#if 0
+ iic(2, 0x78, 0x83, 0x8c, 0x00); /* brightness */
+#endif
+ printk(KERN_CONT ".");
+
+ /* color correction */
+ iic(3, 0x78, 0x49, 0x00, 0x95); /* a */
+ iic(3, 0x78, 0x49, 0x01, 0x96); /* b */
+ iic(3, 0x78, 0x49, 0x03, 0x85); /* c */
+ iic(3, 0x78, 0x49, 0x04, 0x97); /* d */
+ iic(3, 0x78, 0x49, 0x02, 0x7e); /* e(Lo) */
+ iic(3, 0x78, 0x49, 0x05, 0xa4); /* f(Lo) */
+ iic(3, 0x78, 0x49, 0x06, 0x04); /* e(Hi) */
+ iic(3, 0x78, 0x49, 0x07, 0x04); /* e(Hi) */
+ iic(2, 0x78, 0x48, 0x01, 0x00); /* on=1 off=0 */
+
+ printk(KERN_CONT ".");
+ iic(2, 0x78, 0x11, 0x00, 0x00); /* end */
+ printk(KERN_CONT " done\n");
+ return 0;
+}
+
+
+/****************************************************************************
+ *
+ * Video4Linux Module functions
+ *
+ ****************************************************************************/
+
+static const struct v4l2_file_operations ar_fops = {
+ .owner = THIS_MODULE,
+ .read = ar_read,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops ar_ioctl_ops = {
+ .vidioc_querycap = ar_querycap,
+ .vidioc_g_input = ar_g_input,
+ .vidioc_s_input = ar_s_input,
+ .vidioc_enum_input = ar_enum_input,
+ .vidioc_enum_fmt_vid_cap = ar_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = ar_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = ar_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = ar_try_fmt_vid_cap,
+};
+
+#define ALIGN4(x) ((((int)(x)) & 0x3) == 0)
+
+static int __init ar_init(void)
+{
+ struct ar *ar;
+ struct v4l2_device *v4l2_dev;
+ int ret;
+ int i;
+
+ ar = &ardev;
+ v4l2_dev = &ar->v4l2_dev;
+ strlcpy(v4l2_dev->name, "arv", sizeof(v4l2_dev->name));
+ v4l2_info(v4l2_dev, "Colour AR VGA driver %s\n", VERSION);
+
+ ret = v4l2_device_register(NULL, v4l2_dev);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
+ return ret;
+ }
+ ret = -EIO;
+
+#if USE_INT
+ /* allocate a DMA buffer for 1 line. */
+ ar->line_buff = kmalloc(MAX_AR_LINE_BYTES, GFP_KERNEL | GFP_DMA);
+ if (ar->line_buff == NULL || !ALIGN4(ar->line_buff)) {
+ v4l2_err(v4l2_dev, "buffer allocation failed for DMA.\n");
+ ret = -ENOMEM;
+ goto out_end;
+ }
+#endif
+ /* allocate buffers for a frame */
+ for (i = 0; i < MAX_AR_HEIGHT; i++) {
+ ar->frame[i] = kmalloc(MAX_AR_LINE_BYTES, GFP_KERNEL);
+ if (ar->frame[i] == NULL || !ALIGN4(ar->frame[i])) {
+ v4l2_err(v4l2_dev, "buffer allocation failed for frame.\n");
+ ret = -ENOMEM;
+ goto out_line_buff;
+ }
+ }
+
+ strlcpy(ar->vdev.name, "Colour AR VGA", sizeof(ar->vdev.name));
+ ar->vdev.v4l2_dev = v4l2_dev;
+ ar->vdev.fops = &ar_fops;
+ ar->vdev.ioctl_ops = &ar_ioctl_ops;
+ ar->vdev.release = video_device_release_empty;
+ video_set_drvdata(&ar->vdev, ar);
+
+ if (vga) {
+ ar->width = AR_WIDTH_VGA;
+ ar->height = AR_HEIGHT_VGA;
+ ar->size = AR_SIZE_VGA;
+ ar->frame_bytes = AR_FRAME_BYTES_VGA;
+ ar->line_bytes = AR_LINE_BYTES_VGA;
+ if (vga_interlace)
+ ar->mode = AR_MODE_INTERLACE;
+ else
+ ar->mode = AR_MODE_NORMAL;
+ } else {
+ ar->width = AR_WIDTH_QVGA;
+ ar->height = AR_HEIGHT_QVGA;
+ ar->size = AR_SIZE_QVGA;
+ ar->frame_bytes = AR_FRAME_BYTES_QVGA;
+ ar->line_bytes = AR_LINE_BYTES_QVGA;
+ ar->mode = AR_MODE_INTERLACE;
+ }
+ mutex_init(&ar->lock);
+ init_waitqueue_head(&ar->wait);
+
+#if USE_INT
+ if (request_irq(M32R_IRQ_INT3, ar_interrupt, 0, "arv", ar)) {
+ v4l2_err("request_irq(%d) failed.\n", M32R_IRQ_INT3);
+ ret = -EIO;
+ goto out_irq;
+ }
+#endif
+
+ if (ar_initialize(ar) != 0) {
+ v4l2_err(v4l2_dev, "M64278 not found.\n");
+ ret = -ENODEV;
+ goto out_dev;
+ }
+
+ /*
+ * ok, we can initialize h/w according to parameters,
+ * so register video device as a frame grabber type.
+ * device is named "video[0-64]".
+ * video_register_device() initializes h/w using ar_initialize().
+ */
+ if (video_register_device(&ar->vdev, VFL_TYPE_GRABBER, video_nr) != 0) {
+ /* return -1, -ENFILE(full) or others */
+ v4l2_err(v4l2_dev, "register video (Colour AR) failed.\n");
+ ret = -ENODEV;
+ goto out_dev;
+ }
+
+ v4l2_info(v4l2_dev, "%s: Found M64278 VGA (IRQ %d, Freq %dMHz).\n",
+ video_device_node_name(&ar->vdev), M32R_IRQ_INT3, freq);
+
+ return 0;
+
+out_dev:
+#if USE_INT
+ free_irq(M32R_IRQ_INT3, ar);
+
+out_irq:
+#endif
+ for (i = 0; i < MAX_AR_HEIGHT; i++)
+ kfree(ar->frame[i]);
+
+out_line_buff:
+#if USE_INT
+ kfree(ar->line_buff);
+
+out_end:
+#endif
+ v4l2_device_unregister(&ar->v4l2_dev);
+ return ret;
+}
+
+
+static int __init ar_init_module(void)
+{
+ freq = (boot_cpu_data.bus_clock / 1000000);
+ printk(KERN_INFO "arv: Bus clock %d\n", freq);
+ if (freq != 50 && freq != 75)
+ freq = DEFAULT_FREQ;
+ return ar_init();
+}
+
+static void __exit ar_cleanup_module(void)
+{
+ struct ar *ar;
+ int i;
+
+ ar = &ardev;
+ video_unregister_device(&ar->vdev);
+#if USE_INT
+ free_irq(M32R_IRQ_INT3, ar);
+#endif
+ for (i = 0; i < MAX_AR_HEIGHT; i++)
+ kfree(ar->frame[i]);
+#if USE_INT
+ kfree(ar->line_buff);
+#endif
+ v4l2_device_unregister(&ar->v4l2_dev);
+}
+
+module_init(ar_init_module);
+module_exit(ar_cleanup_module);
+
+MODULE_AUTHOR("Takeo Takahashi <takahashi.takeo@renesas.com>");
+MODULE_DESCRIPTION("Colour AR M64278(VGA) for Video4Linux");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(VERSION);
diff --git a/drivers/media/video/as3645a.c b/drivers/media/video/as3645a.c
new file mode 100644
index 00000000..7a3371f0
--- /dev/null
+++ b/drivers/media/video/as3645a.c
@@ -0,0 +1,888 @@
+/*
+ * drivers/media/video/as3645a.c - AS3645A and LM3555 flash controllers driver
+ *
+ * Copyright (C) 2008-2011 Nokia Corporation
+ * Copyright (c) 2011, Intel Corporation.
+ *
+ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * TODO:
+ * - Check hardware FSTROBE control when sensor driver add support for this
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+#include <media/as3645a.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+
+#define AS_TIMER_MS_TO_CODE(t) (((t) - 100) / 50)
+#define AS_TIMER_CODE_TO_MS(c) (50 * (c) + 100)
+
+/* Register definitions */
+
+/* Read-only Design info register: Reset state: xxxx 0001 */
+#define AS_DESIGN_INFO_REG 0x00
+#define AS_DESIGN_INFO_FACTORY(x) (((x) >> 4))
+#define AS_DESIGN_INFO_MODEL(x) ((x) & 0x0f)
+
+/* Read-only Version control register: Reset state: 0000 0000
+ * for first engineering samples
+ */
+#define AS_VERSION_CONTROL_REG 0x01
+#define AS_VERSION_CONTROL_RFU(x) (((x) >> 4))
+#define AS_VERSION_CONTROL_VERSION(x) ((x) & 0x0f)
+
+/* Read / Write (Indicator and timer register): Reset state: 0000 1111 */
+#define AS_INDICATOR_AND_TIMER_REG 0x02
+#define AS_INDICATOR_AND_TIMER_TIMEOUT_SHIFT 0
+#define AS_INDICATOR_AND_TIMER_VREF_SHIFT 4
+#define AS_INDICATOR_AND_TIMER_INDICATOR_SHIFT 6
+
+/* Read / Write (Current set register): Reset state: 0110 1001 */
+#define AS_CURRENT_SET_REG 0x03
+#define AS_CURRENT_ASSIST_LIGHT_SHIFT 0
+#define AS_CURRENT_LED_DET_ON (1 << 3)
+#define AS_CURRENT_FLASH_CURRENT_SHIFT 4
+
+/* Read / Write (Control register): Reset state: 1011 0100 */
+#define AS_CONTROL_REG 0x04
+#define AS_CONTROL_MODE_SETTING_SHIFT 0
+#define AS_CONTROL_STROBE_ON (1 << 2)
+#define AS_CONTROL_OUT_ON (1 << 3)
+#define AS_CONTROL_EXT_TORCH_ON (1 << 4)
+#define AS_CONTROL_STROBE_TYPE_EDGE (0 << 5)
+#define AS_CONTROL_STROBE_TYPE_LEVEL (1 << 5)
+#define AS_CONTROL_COIL_PEAK_SHIFT 6
+
+/* Read only (D3 is read / write) (Fault and info): Reset state: 0000 x000 */
+#define AS_FAULT_INFO_REG 0x05
+#define AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT (1 << 1)
+#define AS_FAULT_INFO_INDICATOR_LED (1 << 2)
+#define AS_FAULT_INFO_LED_AMOUNT (1 << 3)
+#define AS_FAULT_INFO_TIMEOUT (1 << 4)
+#define AS_FAULT_INFO_OVER_TEMPERATURE (1 << 5)
+#define AS_FAULT_INFO_SHORT_CIRCUIT (1 << 6)
+#define AS_FAULT_INFO_OVER_VOLTAGE (1 << 7)
+
+/* Boost register */
+#define AS_BOOST_REG 0x0d
+#define AS_BOOST_CURRENT_DISABLE (0 << 0)
+#define AS_BOOST_CURRENT_ENABLE (1 << 0)
+
+/* Password register is used to unlock boost register writing */
+#define AS_PASSWORD_REG 0x0f
+#define AS_PASSWORD_UNLOCK_VALUE 0x55
+
+enum as_mode {
+ AS_MODE_EXT_TORCH = 0 << AS_CONTROL_MODE_SETTING_SHIFT,
+ AS_MODE_INDICATOR = 1 << AS_CONTROL_MODE_SETTING_SHIFT,
+ AS_MODE_ASSIST = 2 << AS_CONTROL_MODE_SETTING_SHIFT,
+ AS_MODE_FLASH = 3 << AS_CONTROL_MODE_SETTING_SHIFT,
+};
+
+/*
+ * struct as3645a
+ *
+ * @subdev: V4L2 subdev
+ * @pdata: Flash platform data
+ * @power_lock: Protects power_count
+ * @power_count: Power reference count
+ * @led_mode: V4L2 flash LED mode
+ * @timeout: Flash timeout in microseconds
+ * @flash_current: Flash current (0=200mA ... 15=500mA). Maximum
+ * values are 400mA for two LEDs and 500mA for one LED.
+ * @assist_current: Torch/Assist light current (0=20mA, 1=40mA ... 7=160mA)
+ * @indicator_current: Indicator LED current (0=0mA, 1=2.5mA ... 4=10mA)
+ * @strobe_source: Flash strobe source (software or external)
+ */
+struct as3645a {
+ struct v4l2_subdev subdev;
+ const struct as3645a_platform_data *pdata;
+
+ struct mutex power_lock;
+ int power_count;
+
+ /* Controls */
+ struct v4l2_ctrl_handler ctrls;
+
+ enum v4l2_flash_led_mode led_mode;
+ unsigned int timeout;
+ u8 flash_current;
+ u8 assist_current;
+ u8 indicator_current;
+ enum v4l2_flash_strobe_source strobe_source;
+};
+
+#define to_as3645a(sd) container_of(sd, struct as3645a, subdev)
+
+/* Return negative errno else zero on success */
+static int as3645a_write(struct as3645a *flash, u8 addr, u8 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+ int rval;
+
+ rval = i2c_smbus_write_byte_data(client, addr, val);
+
+ dev_dbg(&client->dev, "Write Addr:%02X Val:%02X %s\n", addr, val,
+ rval < 0 ? "fail" : "ok");
+
+ return rval;
+}
+
+/* Return negative errno else a data byte received from the device. */
+static int as3645a_read(struct as3645a *flash, u8 addr)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+ int rval;
+
+ rval = i2c_smbus_read_byte_data(client, addr);
+
+ dev_dbg(&client->dev, "Read Addr:%02X Val:%02X %s\n", addr, rval,
+ rval < 0 ? "fail" : "ok");
+
+ return rval;
+}
+
+/* -----------------------------------------------------------------------------
+ * Hardware configuration and trigger
+ */
+
+/*
+ * as3645a_set_config - Set flash configuration registers
+ * @flash: The flash
+ *
+ * Configure the hardware with flash, assist and indicator currents, as well as
+ * flash timeout.
+ *
+ * Return 0 on success, or a negative error code if an I2C communication error
+ * occurred.
+ */
+static int as3645a_set_config(struct as3645a *flash)
+{
+ int ret;
+ u8 val;
+
+ val = (flash->flash_current << AS_CURRENT_FLASH_CURRENT_SHIFT)
+ | (flash->assist_current << AS_CURRENT_ASSIST_LIGHT_SHIFT)
+ | AS_CURRENT_LED_DET_ON;
+
+ ret = as3645a_write(flash, AS_CURRENT_SET_REG, val);
+ if (ret < 0)
+ return ret;
+
+ val = AS_TIMER_MS_TO_CODE(flash->timeout / 1000)
+ << AS_INDICATOR_AND_TIMER_TIMEOUT_SHIFT;
+
+ val |= (flash->pdata->vref << AS_INDICATOR_AND_TIMER_VREF_SHIFT)
+ | ((flash->indicator_current ? flash->indicator_current - 1 : 0)
+ << AS_INDICATOR_AND_TIMER_INDICATOR_SHIFT);
+
+ return as3645a_write(flash, AS_INDICATOR_AND_TIMER_REG, val);
+}
+
+/*
+ * as3645a_set_control - Set flash control register
+ * @flash: The flash
+ * @mode: Desired output mode
+ * @on: Desired output state
+ *
+ * Configure the hardware with output mode and state.
+ *
+ * Return 0 on success, or a negative error code if an I2C communication error
+ * occurred.
+ */
+static int
+as3645a_set_control(struct as3645a *flash, enum as_mode mode, bool on)
+{
+ u8 reg;
+
+ /* Configure output parameters and operation mode. */
+ reg = (flash->pdata->peak << AS_CONTROL_COIL_PEAK_SHIFT)
+ | (on ? AS_CONTROL_OUT_ON : 0)
+ | mode;
+
+ if (flash->led_mode == V4L2_FLASH_LED_MODE_FLASH &&
+ flash->strobe_source == V4L2_FLASH_STROBE_SOURCE_EXTERNAL) {
+ reg |= AS_CONTROL_STROBE_TYPE_LEVEL
+ | AS_CONTROL_STROBE_ON;
+ }
+
+ return as3645a_write(flash, AS_CONTROL_REG, reg);
+}
+
+/*
+ * as3645a_set_output - Configure output and operation mode
+ * @flash: Flash controller
+ * @strobe: Strobe the flash (only valid in flash mode)
+ *
+ * Turn the LEDs output on/off and set the operation mode based on the current
+ * parameters.
+ *
+ * The AS3645A can't control the indicator LED independently of the flash/torch
+ * LED. If the flash controller is in V4L2_FLASH_LED_MODE_NONE mode, set the
+ * chip to indicator mode. Otherwise set it to assist light (torch) or flash
+ * mode.
+ *
+ * In indicator and assist modes, turn the output on/off based on the indicator
+ * and torch currents. In software strobe flash mode, turn the output on/off
+ * based on the strobe parameter.
+ */
+static int as3645a_set_output(struct as3645a *flash, bool strobe)
+{
+ enum as_mode mode;
+ bool on;
+
+ switch (flash->led_mode) {
+ case V4L2_FLASH_LED_MODE_NONE:
+ on = flash->indicator_current != 0;
+ mode = AS_MODE_INDICATOR;
+ break;
+ case V4L2_FLASH_LED_MODE_TORCH:
+ on = true;
+ mode = AS_MODE_ASSIST;
+ break;
+ case V4L2_FLASH_LED_MODE_FLASH:
+ on = strobe;
+ mode = AS_MODE_FLASH;
+ break;
+ default:
+ BUG();
+ }
+
+ /* Configure output parameters and operation mode. */
+ return as3645a_set_control(flash, mode, on);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 controls
+ */
+
+static int as3645a_is_active(struct as3645a *flash)
+{
+ int ret;
+
+ ret = as3645a_read(flash, AS_CONTROL_REG);
+ return ret < 0 ? ret : !!(ret & AS_CONTROL_OUT_ON);
+}
+
+static int as3645a_read_fault(struct as3645a *flash)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+ int rval;
+
+ /* NOTE: reading register clear fault status */
+ rval = as3645a_read(flash, AS_FAULT_INFO_REG);
+ if (rval < 0)
+ return rval;
+
+ if (rval & AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT)
+ dev_dbg(&client->dev, "Inductor Peak limit fault\n");
+
+ if (rval & AS_FAULT_INFO_INDICATOR_LED)
+ dev_dbg(&client->dev, "Indicator LED fault: "
+ "Short circuit or open loop\n");
+
+ dev_dbg(&client->dev, "%u connected LEDs\n",
+ rval & AS_FAULT_INFO_LED_AMOUNT ? 2 : 1);
+
+ if (rval & AS_FAULT_INFO_TIMEOUT)
+ dev_dbg(&client->dev, "Timeout fault\n");
+
+ if (rval & AS_FAULT_INFO_OVER_TEMPERATURE)
+ dev_dbg(&client->dev, "Over temperature fault\n");
+
+ if (rval & AS_FAULT_INFO_SHORT_CIRCUIT)
+ dev_dbg(&client->dev, "Short circuit fault\n");
+
+ if (rval & AS_FAULT_INFO_OVER_VOLTAGE)
+ dev_dbg(&client->dev, "Over voltage fault: "
+ "Indicates missing capacitor or open connection\n");
+
+ return rval;
+}
+
+static int as3645a_get_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct as3645a *flash =
+ container_of(ctrl->handler, struct as3645a, ctrls);
+ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+ int value;
+
+ switch (ctrl->id) {
+ case V4L2_CID_FLASH_FAULT:
+ value = as3645a_read_fault(flash);
+ if (value < 0)
+ return value;
+
+ ctrl->cur.val = 0;
+ if (value & AS_FAULT_INFO_SHORT_CIRCUIT)
+ ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT;
+ if (value & AS_FAULT_INFO_OVER_TEMPERATURE)
+ ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE;
+ if (value & AS_FAULT_INFO_TIMEOUT)
+ ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT;
+ if (value & AS_FAULT_INFO_OVER_VOLTAGE)
+ ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE;
+ if (value & AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT)
+ ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_CURRENT;
+ if (value & AS_FAULT_INFO_INDICATOR_LED)
+ ctrl->cur.val |= V4L2_FLASH_FAULT_INDICATOR;
+ break;
+
+ case V4L2_CID_FLASH_STROBE_STATUS:
+ if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) {
+ ctrl->cur.val = 0;
+ break;
+ }
+
+ value = as3645a_is_active(flash);
+ if (value < 0)
+ return value;
+
+ ctrl->cur.val = value;
+ break;
+ }
+
+ dev_dbg(&client->dev, "G_CTRL %08x:%d\n", ctrl->id, ctrl->cur.val);
+
+ return 0;
+}
+
+static int as3645a_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct as3645a *flash =
+ container_of(ctrl->handler, struct as3645a, ctrls);
+ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+ int ret;
+
+ dev_dbg(&client->dev, "S_CTRL %08x:%d\n", ctrl->id, ctrl->val);
+
+ /* If a control that doesn't apply to the current mode is modified,
+ * we store the value and return immediately. The setting will be
+ * applied when the LED mode is changed. Otherwise we apply the setting
+ * immediately.
+ */
+
+ switch (ctrl->id) {
+ case V4L2_CID_FLASH_LED_MODE:
+ if (flash->indicator_current)
+ return -EBUSY;
+
+ ret = as3645a_set_config(flash);
+ if (ret < 0)
+ return ret;
+
+ flash->led_mode = ctrl->val;
+ return as3645a_set_output(flash, false);
+
+ case V4L2_CID_FLASH_STROBE_SOURCE:
+ flash->strobe_source = ctrl->val;
+
+ /* Applies to flash mode only. */
+ if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH)
+ break;
+
+ return as3645a_set_output(flash, false);
+
+ case V4L2_CID_FLASH_STROBE:
+ if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH)
+ return -EBUSY;
+
+ return as3645a_set_output(flash, true);
+
+ case V4L2_CID_FLASH_STROBE_STOP:
+ if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH)
+ return -EBUSY;
+
+ return as3645a_set_output(flash, false);
+
+ case V4L2_CID_FLASH_TIMEOUT:
+ flash->timeout = ctrl->val;
+
+ /* Applies to flash mode only. */
+ if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH)
+ break;
+
+ return as3645a_set_config(flash);
+
+ case V4L2_CID_FLASH_INTENSITY:
+ flash->flash_current = (ctrl->val - AS3645A_FLASH_INTENSITY_MIN)
+ / AS3645A_FLASH_INTENSITY_STEP;
+
+ /* Applies to flash mode only. */
+ if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH)
+ break;
+
+ return as3645a_set_config(flash);
+
+ case V4L2_CID_FLASH_TORCH_INTENSITY:
+ flash->assist_current =
+ (ctrl->val - AS3645A_TORCH_INTENSITY_MIN)
+ / AS3645A_TORCH_INTENSITY_STEP;
+
+ /* Applies to torch mode only. */
+ if (flash->led_mode != V4L2_FLASH_LED_MODE_TORCH)
+ break;
+
+ return as3645a_set_config(flash);
+
+ case V4L2_CID_FLASH_INDICATOR_INTENSITY:
+ if (flash->led_mode != V4L2_FLASH_LED_MODE_NONE)
+ return -EBUSY;
+
+ flash->indicator_current =
+ (ctrl->val - AS3645A_INDICATOR_INTENSITY_MIN)
+ / AS3645A_INDICATOR_INTENSITY_STEP;
+
+ ret = as3645a_set_config(flash);
+ if (ret < 0)
+ return ret;
+
+ if ((ctrl->val == 0) == (ctrl->cur.val == 0))
+ break;
+
+ return as3645a_set_output(flash, false);
+ }
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops as3645a_ctrl_ops = {
+ .g_volatile_ctrl = as3645a_get_ctrl,
+ .s_ctrl = as3645a_set_ctrl,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev core operations
+ */
+
+/* Put device into know state. */
+static int as3645a_setup(struct as3645a *flash)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+ int ret;
+
+ /* clear errors */
+ ret = as3645a_read(flash, AS_FAULT_INFO_REG);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(&client->dev, "Fault info: %02x\n", ret);
+
+ ret = as3645a_set_config(flash);
+ if (ret < 0)
+ return ret;
+
+ ret = as3645a_set_output(flash, false);
+ if (ret < 0)
+ return ret;
+
+ /* read status */
+ ret = as3645a_read_fault(flash);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(&client->dev, "AS_INDICATOR_AND_TIMER_REG: %02x\n",
+ as3645a_read(flash, AS_INDICATOR_AND_TIMER_REG));
+ dev_dbg(&client->dev, "AS_CURRENT_SET_REG: %02x\n",
+ as3645a_read(flash, AS_CURRENT_SET_REG));
+ dev_dbg(&client->dev, "AS_CONTROL_REG: %02x\n",
+ as3645a_read(flash, AS_CONTROL_REG));
+
+ return ret & ~AS_FAULT_INFO_LED_AMOUNT ? -EIO : 0;
+}
+
+static int __as3645a_set_power(struct as3645a *flash, int on)
+{
+ int ret;
+
+ if (!on)
+ as3645a_set_control(flash, AS_MODE_EXT_TORCH, false);
+
+ if (flash->pdata->set_power) {
+ ret = flash->pdata->set_power(&flash->subdev, on);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (!on)
+ return 0;
+
+ ret = as3645a_setup(flash);
+ if (ret < 0) {
+ if (flash->pdata->set_power)
+ flash->pdata->set_power(&flash->subdev, 0);
+ }
+
+ return ret;
+}
+
+static int as3645a_set_power(struct v4l2_subdev *sd, int on)
+{
+ struct as3645a *flash = to_as3645a(sd);
+ int ret = 0;
+
+ mutex_lock(&flash->power_lock);
+
+ if (flash->power_count == !on) {
+ ret = __as3645a_set_power(flash, !!on);
+ if (ret < 0)
+ goto done;
+ }
+
+ flash->power_count += on ? 1 : -1;
+ WARN_ON(flash->power_count < 0);
+
+done:
+ mutex_unlock(&flash->power_lock);
+ return ret;
+}
+
+static int as3645a_registered(struct v4l2_subdev *sd)
+{
+ struct as3645a *flash = to_as3645a(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int rval, man, model, rfu, version;
+ const char *vendor;
+
+ /* Power up the flash driver and read manufacturer ID, model ID, RFU
+ * and version.
+ */
+ rval = as3645a_set_power(&flash->subdev, 1);
+ if (rval < 0)
+ return rval;
+
+ rval = as3645a_read(flash, AS_DESIGN_INFO_REG);
+ if (rval < 0)
+ goto power_off;
+
+ man = AS_DESIGN_INFO_FACTORY(rval);
+ model = AS_DESIGN_INFO_MODEL(rval);
+
+ rval = as3645a_read(flash, AS_VERSION_CONTROL_REG);
+ if (rval < 0)
+ goto power_off;
+
+ rfu = AS_VERSION_CONTROL_RFU(rval);
+ version = AS_VERSION_CONTROL_VERSION(rval);
+
+ /* Verify the chip model and version. */
+ if (model != 0x01 || rfu != 0x00) {
+ dev_err(&client->dev, "AS3645A not detected "
+ "(model %d rfu %d)\n", model, rfu);
+ rval = -ENODEV;
+ goto power_off;
+ }
+
+ switch (man) {
+ case 1:
+ vendor = "AMS, Austria Micro Systems";
+ break;
+ case 2:
+ vendor = "ADI, Analog Devices Inc.";
+ break;
+ case 3:
+ vendor = "NSC, National Semiconductor";
+ break;
+ case 4:
+ vendor = "NXP";
+ break;
+ case 5:
+ vendor = "TI, Texas Instrument";
+ break;
+ default:
+ vendor = "Unknown";
+ }
+
+ dev_info(&client->dev, "Chip vendor: %s (%d) Version: %d\n", vendor,
+ man, version);
+
+ rval = as3645a_write(flash, AS_PASSWORD_REG, AS_PASSWORD_UNLOCK_VALUE);
+ if (rval < 0)
+ goto power_off;
+
+ rval = as3645a_write(flash, AS_BOOST_REG, AS_BOOST_CURRENT_DISABLE);
+ if (rval < 0)
+ goto power_off;
+
+ /* Setup default values. This makes sure that the chip is in a known
+ * state, in case the power rail can't be controlled.
+ */
+ rval = as3645a_setup(flash);
+
+power_off:
+ as3645a_set_power(&flash->subdev, 0);
+
+ return rval;
+}
+
+static int as3645a_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ return as3645a_set_power(sd, 1);
+}
+
+static int as3645a_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ return as3645a_set_power(sd, 0);
+}
+
+static const struct v4l2_subdev_core_ops as3645a_core_ops = {
+ .s_power = as3645a_set_power,
+};
+
+static const struct v4l2_subdev_ops as3645a_ops = {
+ .core = &as3645a_core_ops,
+};
+
+static const struct v4l2_subdev_internal_ops as3645a_internal_ops = {
+ .registered = as3645a_registered,
+ .open = as3645a_open,
+ .close = as3645a_close,
+};
+
+/* -----------------------------------------------------------------------------
+ * I2C driver
+ */
+#ifdef CONFIG_PM
+
+static int as3645a_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct as3645a *flash = to_as3645a(subdev);
+ int rval;
+
+ if (flash->power_count == 0)
+ return 0;
+
+ rval = __as3645a_set_power(flash, 0);
+
+ dev_dbg(&client->dev, "Suspend %s\n", rval < 0 ? "failed" : "ok");
+
+ return rval;
+}
+
+static int as3645a_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct as3645a *flash = to_as3645a(subdev);
+ int rval;
+
+ if (flash->power_count == 0)
+ return 0;
+
+ rval = __as3645a_set_power(flash, 1);
+
+ dev_dbg(&client->dev, "Resume %s\n", rval < 0 ? "fail" : "ok");
+
+ return rval;
+}
+
+#else
+
+#define as3645a_suspend NULL
+#define as3645a_resume NULL
+
+#endif /* CONFIG_PM */
+
+/*
+ * as3645a_init_controls - Create controls
+ * @flash: The flash
+ *
+ * The number of LEDs reported in platform data is used to compute default
+ * limits. Parameters passed through platform data can override those limits.
+ */
+static int as3645a_init_controls(struct as3645a *flash)
+{
+ const struct as3645a_platform_data *pdata = flash->pdata;
+ struct v4l2_ctrl *ctrl;
+ int maximum;
+
+ v4l2_ctrl_handler_init(&flash->ctrls, 10);
+
+ /* V4L2_CID_FLASH_LED_MODE */
+ v4l2_ctrl_new_std_menu(&flash->ctrls, &as3645a_ctrl_ops,
+ V4L2_CID_FLASH_LED_MODE, 2, ~7,
+ V4L2_FLASH_LED_MODE_NONE);
+
+ /* V4L2_CID_FLASH_STROBE_SOURCE */
+ v4l2_ctrl_new_std_menu(&flash->ctrls, &as3645a_ctrl_ops,
+ V4L2_CID_FLASH_STROBE_SOURCE,
+ pdata->ext_strobe ? 1 : 0,
+ pdata->ext_strobe ? ~3 : ~1,
+ V4L2_FLASH_STROBE_SOURCE_SOFTWARE);
+
+ flash->strobe_source = V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
+
+ /* V4L2_CID_FLASH_STROBE */
+ v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
+ V4L2_CID_FLASH_STROBE, 0, 0, 0, 0);
+
+ /* V4L2_CID_FLASH_STROBE_STOP */
+ v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
+ V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0);
+
+ /* V4L2_CID_FLASH_STROBE_STATUS */
+ ctrl = v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
+ V4L2_CID_FLASH_STROBE_STATUS, 0, 1, 1, 1);
+ if (ctrl != NULL)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ /* V4L2_CID_FLASH_TIMEOUT */
+ maximum = pdata->timeout_max;
+
+ v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
+ V4L2_CID_FLASH_TIMEOUT, AS3645A_FLASH_TIMEOUT_MIN,
+ maximum, AS3645A_FLASH_TIMEOUT_STEP, maximum);
+
+ flash->timeout = maximum;
+
+ /* V4L2_CID_FLASH_INTENSITY */
+ maximum = pdata->flash_max_current;
+
+ v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
+ V4L2_CID_FLASH_INTENSITY, AS3645A_FLASH_INTENSITY_MIN,
+ maximum, AS3645A_FLASH_INTENSITY_STEP, maximum);
+
+ flash->flash_current = (maximum - AS3645A_FLASH_INTENSITY_MIN)
+ / AS3645A_FLASH_INTENSITY_STEP;
+
+ /* V4L2_CID_FLASH_TORCH_INTENSITY */
+ maximum = pdata->torch_max_current;
+
+ v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
+ V4L2_CID_FLASH_TORCH_INTENSITY,
+ AS3645A_TORCH_INTENSITY_MIN, maximum,
+ AS3645A_TORCH_INTENSITY_STEP,
+ AS3645A_TORCH_INTENSITY_MIN);
+
+ flash->assist_current = 0;
+
+ /* V4L2_CID_FLASH_INDICATOR_INTENSITY */
+ v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
+ V4L2_CID_FLASH_INDICATOR_INTENSITY,
+ AS3645A_INDICATOR_INTENSITY_MIN,
+ AS3645A_INDICATOR_INTENSITY_MAX,
+ AS3645A_INDICATOR_INTENSITY_STEP,
+ AS3645A_INDICATOR_INTENSITY_MIN);
+
+ flash->indicator_current = 0;
+
+ /* V4L2_CID_FLASH_FAULT */
+ ctrl = v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
+ V4L2_CID_FLASH_FAULT, 0,
+ V4L2_FLASH_FAULT_OVER_VOLTAGE |
+ V4L2_FLASH_FAULT_TIMEOUT |
+ V4L2_FLASH_FAULT_OVER_TEMPERATURE |
+ V4L2_FLASH_FAULT_SHORT_CIRCUIT, 0, 0);
+ if (ctrl != NULL)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ flash->subdev.ctrl_handler = &flash->ctrls;
+
+ return flash->ctrls.error;
+}
+
+static int as3645a_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct as3645a *flash;
+ int ret;
+
+ if (client->dev.platform_data == NULL)
+ return -ENODEV;
+
+ flash = kzalloc(sizeof(*flash), GFP_KERNEL);
+ if (flash == NULL)
+ return -ENOMEM;
+
+ flash->pdata = client->dev.platform_data;
+
+ v4l2_i2c_subdev_init(&flash->subdev, client, &as3645a_ops);
+ flash->subdev.internal_ops = &as3645a_internal_ops;
+ flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ ret = as3645a_init_controls(flash);
+ if (ret < 0)
+ goto done;
+
+ ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0);
+ if (ret < 0)
+ goto done;
+
+ flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
+
+ mutex_init(&flash->power_lock);
+
+ flash->led_mode = V4L2_FLASH_LED_MODE_NONE;
+
+done:
+ if (ret < 0) {
+ v4l2_ctrl_handler_free(&flash->ctrls);
+ kfree(flash);
+ }
+
+ return ret;
+}
+
+static int __exit as3645a_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct as3645a *flash = to_as3645a(subdev);
+
+ v4l2_device_unregister_subdev(subdev);
+ v4l2_ctrl_handler_free(&flash->ctrls);
+ media_entity_cleanup(&flash->subdev.entity);
+ mutex_destroy(&flash->power_lock);
+ kfree(flash);
+
+ return 0;
+}
+
+static const struct i2c_device_id as3645a_id_table[] = {
+ { AS3645A_NAME, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, as3645a_id_table);
+
+static const struct dev_pm_ops as3645a_pm_ops = {
+ .suspend = as3645a_suspend,
+ .resume = as3645a_resume,
+};
+
+static struct i2c_driver as3645a_i2c_driver = {
+ .driver = {
+ .name = AS3645A_NAME,
+ .pm = &as3645a_pm_ops,
+ },
+ .probe = as3645a_probe,
+ .remove = __exit_p(as3645a_remove),
+ .id_table = as3645a_id_table,
+};
+
+module_i2c_driver(as3645a_i2c_driver);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("LED flash driver for AS3645A, LM3555 and their clones");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/atmel-isi.c b/drivers/media/video/atmel-isi.c
new file mode 100644
index 00000000..ec3f6a06
--- /dev/null
+++ b/drivers/media/video/atmel-isi.c
@@ -0,0 +1,1109 @@
+/*
+ * Copyright (c) 2011 Atmel Corporation
+ * Josh Wu, <josh.wu@atmel.com>
+ *
+ * Based on previous work by Lars Haring, <lars.haring@atmel.com>
+ * and Sedji Gaouaou
+ * Based on the bttv driver for Bt848 with respective copyright holders
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <media/atmel-isi.h>
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
+#include <media/videobuf2-dma-contig.h>
+
+#define MAX_BUFFER_NUM 32
+#define MAX_SUPPORT_WIDTH 2048
+#define MAX_SUPPORT_HEIGHT 2048
+#define VID_LIMIT_BYTES (16 * 1024 * 1024)
+#define MIN_FRAME_RATE 15
+#define FRAME_INTERVAL_MILLI_SEC (1000 / MIN_FRAME_RATE)
+
+/* ISI states */
+enum {
+ ISI_STATE_IDLE = 0,
+ ISI_STATE_READY,
+ ISI_STATE_WAIT_SOF,
+};
+
+/* Frame buffer descriptor */
+struct fbd {
+ /* Physical address of the frame buffer */
+ u32 fb_address;
+ /* DMA Control Register(only in HISI2) */
+ u32 dma_ctrl;
+ /* Physical address of the next fbd */
+ u32 next_fbd_address;
+};
+
+static void set_dma_ctrl(struct fbd *fb_desc, u32 ctrl)
+{
+ fb_desc->dma_ctrl = ctrl;
+}
+
+struct isi_dma_desc {
+ struct list_head list;
+ struct fbd *p_fbd;
+ u32 fbd_phys;
+};
+
+/* Frame buffer data */
+struct frame_buffer {
+ struct vb2_buffer vb;
+ struct isi_dma_desc *p_dma_desc;
+ struct list_head list;
+};
+
+struct atmel_isi {
+ /* Protects the access of variables shared with the ISR */
+ spinlock_t lock;
+ void __iomem *regs;
+
+ int sequence;
+ /* State of the ISI module in capturing mode */
+ int state;
+
+ /* Wait queue for waiting for SOF */
+ wait_queue_head_t vsync_wq;
+
+ struct vb2_alloc_ctx *alloc_ctx;
+
+ /* Allocate descriptors for dma buffer use */
+ struct fbd *p_fb_descriptors;
+ u32 fb_descriptors_phys;
+ struct list_head dma_desc_head;
+ struct isi_dma_desc dma_desc[MAX_BUFFER_NUM];
+
+ struct completion complete;
+ /* ISI peripherial clock */
+ struct clk *pclk;
+ /* ISI_MCK, feed to camera sensor to generate pixel clock */
+ struct clk *mck;
+ unsigned int irq;
+
+ struct isi_platform_data *pdata;
+ u16 width_flags; /* max 12 bits */
+
+ struct list_head video_buffer_list;
+ struct frame_buffer *active;
+
+ struct soc_camera_device *icd;
+ struct soc_camera_host soc_host;
+};
+
+static void isi_writel(struct atmel_isi *isi, u32 reg, u32 val)
+{
+ writel(val, isi->regs + reg);
+}
+static u32 isi_readl(struct atmel_isi *isi, u32 reg)
+{
+ return readl(isi->regs + reg);
+}
+
+static int configure_geometry(struct atmel_isi *isi, u32 width,
+ u32 height, enum v4l2_mbus_pixelcode code)
+{
+ u32 cfg2, cr;
+
+ switch (code) {
+ /* YUV, including grey */
+ case V4L2_MBUS_FMT_Y8_1X8:
+ cr = ISI_CFG2_GRAYSCALE;
+ break;
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ cr = ISI_CFG2_YCC_SWAP_MODE_3;
+ break;
+ case V4L2_MBUS_FMT_VYUY8_2X8:
+ cr = ISI_CFG2_YCC_SWAP_MODE_2;
+ break;
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ cr = ISI_CFG2_YCC_SWAP_MODE_1;
+ break;
+ case V4L2_MBUS_FMT_YVYU8_2X8:
+ cr = ISI_CFG2_YCC_SWAP_DEFAULT;
+ break;
+ /* RGB, TODO */
+ default:
+ return -EINVAL;
+ }
+
+ isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS);
+
+ cfg2 = isi_readl(isi, ISI_CFG2);
+ cfg2 |= cr;
+ /* Set width */
+ cfg2 &= ~(ISI_CFG2_IM_HSIZE_MASK);
+ cfg2 |= ((width - 1) << ISI_CFG2_IM_HSIZE_OFFSET) &
+ ISI_CFG2_IM_HSIZE_MASK;
+ /* Set height */
+ cfg2 &= ~(ISI_CFG2_IM_VSIZE_MASK);
+ cfg2 |= ((height - 1) << ISI_CFG2_IM_VSIZE_OFFSET)
+ & ISI_CFG2_IM_VSIZE_MASK;
+ isi_writel(isi, ISI_CFG2, cfg2);
+
+ return 0;
+}
+
+static irqreturn_t atmel_isi_handle_streaming(struct atmel_isi *isi)
+{
+ if (isi->active) {
+ struct vb2_buffer *vb = &isi->active->vb;
+ struct frame_buffer *buf = isi->active;
+
+ list_del_init(&buf->list);
+ do_gettimeofday(&vb->v4l2_buf.timestamp);
+ vb->v4l2_buf.sequence = isi->sequence++;
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ }
+
+ if (list_empty(&isi->video_buffer_list)) {
+ isi->active = NULL;
+ } else {
+ /* start next dma frame. */
+ isi->active = list_entry(isi->video_buffer_list.next,
+ struct frame_buffer, list);
+ isi_writel(isi, ISI_DMA_C_DSCR,
+ isi->active->p_dma_desc->fbd_phys);
+ isi_writel(isi, ISI_DMA_C_CTRL,
+ ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE);
+ isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_C_CH);
+ }
+ return IRQ_HANDLED;
+}
+
+/* ISI interrupt service routine */
+static irqreturn_t isi_interrupt(int irq, void *dev_id)
+{
+ struct atmel_isi *isi = dev_id;
+ u32 status, mask, pending;
+ irqreturn_t ret = IRQ_NONE;
+
+ spin_lock(&isi->lock);
+
+ status = isi_readl(isi, ISI_STATUS);
+ mask = isi_readl(isi, ISI_INTMASK);
+ pending = status & mask;
+
+ if (pending & ISI_CTRL_SRST) {
+ complete(&isi->complete);
+ isi_writel(isi, ISI_INTDIS, ISI_CTRL_SRST);
+ ret = IRQ_HANDLED;
+ } else if (pending & ISI_CTRL_DIS) {
+ complete(&isi->complete);
+ isi_writel(isi, ISI_INTDIS, ISI_CTRL_DIS);
+ ret = IRQ_HANDLED;
+ } else {
+ if ((pending & ISI_SR_VSYNC) &&
+ (isi->state == ISI_STATE_IDLE)) {
+ isi->state = ISI_STATE_READY;
+ wake_up_interruptible(&isi->vsync_wq);
+ ret = IRQ_HANDLED;
+ }
+ if (likely(pending & ISI_SR_CXFR_DONE))
+ ret = atmel_isi_handle_streaming(isi);
+ }
+
+ spin_unlock(&isi->lock);
+ return ret;
+}
+
+#define WAIT_ISI_RESET 1
+#define WAIT_ISI_DISABLE 0
+static int atmel_isi_wait_status(struct atmel_isi *isi, int wait_reset)
+{
+ unsigned long timeout;
+ /*
+ * The reset or disable will only succeed if we have a
+ * pixel clock from the camera.
+ */
+ init_completion(&isi->complete);
+
+ if (wait_reset) {
+ isi_writel(isi, ISI_INTEN, ISI_CTRL_SRST);
+ isi_writel(isi, ISI_CTRL, ISI_CTRL_SRST);
+ } else {
+ isi_writel(isi, ISI_INTEN, ISI_CTRL_DIS);
+ isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS);
+ }
+
+ timeout = wait_for_completion_timeout(&isi->complete,
+ msecs_to_jiffies(100));
+ if (timeout == 0)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------
+ Videobuf operations
+ ------------------------------------------------------------------*/
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct atmel_isi *isi = ici->priv;
+ unsigned long size;
+ int ret, bytes_per_line;
+
+ /* Reset ISI */
+ ret = atmel_isi_wait_status(isi, WAIT_ISI_RESET);
+ if (ret < 0) {
+ dev_err(icd->parent, "Reset ISI timed out\n");
+ return ret;
+ }
+ /* Disable all interrupts */
+ isi_writel(isi, ISI_INTDIS, ~0UL);
+
+ bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+
+ if (bytes_per_line < 0)
+ return bytes_per_line;
+
+ size = bytes_per_line * icd->user_height;
+
+ if (!*nbuffers || *nbuffers > MAX_BUFFER_NUM)
+ *nbuffers = MAX_BUFFER_NUM;
+
+ if (size * *nbuffers > VID_LIMIT_BYTES)
+ *nbuffers = VID_LIMIT_BYTES / size;
+
+ *nplanes = 1;
+ sizes[0] = size;
+ alloc_ctxs[0] = isi->alloc_ctx;
+
+ isi->sequence = 0;
+ isi->active = NULL;
+
+ dev_dbg(icd->parent, "%s, count=%d, size=%ld\n", __func__,
+ *nbuffers, size);
+
+ return 0;
+}
+
+static int buffer_init(struct vb2_buffer *vb)
+{
+ struct frame_buffer *buf = container_of(vb, struct frame_buffer, vb);
+
+ buf->p_dma_desc = NULL;
+ INIT_LIST_HEAD(&buf->list);
+
+ return 0;
+}
+
+static int buffer_prepare(struct vb2_buffer *vb)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct frame_buffer *buf = container_of(vb, struct frame_buffer, vb);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct atmel_isi *isi = ici->priv;
+ unsigned long size;
+ struct isi_dma_desc *desc;
+ int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+
+ if (bytes_per_line < 0)
+ return bytes_per_line;
+
+ size = bytes_per_line * icd->user_height;
+
+ if (vb2_plane_size(vb, 0) < size) {
+ dev_err(icd->parent, "%s data will not fit into plane (%lu < %lu)\n",
+ __func__, vb2_plane_size(vb, 0), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(&buf->vb, 0, size);
+
+ if (!buf->p_dma_desc) {
+ if (list_empty(&isi->dma_desc_head)) {
+ dev_err(icd->parent, "Not enough dma descriptors.\n");
+ return -EINVAL;
+ } else {
+ /* Get an available descriptor */
+ desc = list_entry(isi->dma_desc_head.next,
+ struct isi_dma_desc, list);
+ /* Delete the descriptor since now it is used */
+ list_del_init(&desc->list);
+
+ /* Initialize the dma descriptor */
+ desc->p_fbd->fb_address =
+ vb2_dma_contig_plane_dma_addr(vb, 0);
+ desc->p_fbd->next_fbd_address = 0;
+ set_dma_ctrl(desc->p_fbd, ISI_DMA_CTRL_WB);
+
+ buf->p_dma_desc = desc;
+ }
+ }
+ return 0;
+}
+
+static void buffer_cleanup(struct vb2_buffer *vb)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct atmel_isi *isi = ici->priv;
+ struct frame_buffer *buf = container_of(vb, struct frame_buffer, vb);
+
+ /* This descriptor is available now and we add to head list */
+ if (buf->p_dma_desc)
+ list_add(&buf->p_dma_desc->list, &isi->dma_desc_head);
+}
+
+static void start_dma(struct atmel_isi *isi, struct frame_buffer *buffer)
+{
+ u32 ctrl, cfg1;
+
+ cfg1 = isi_readl(isi, ISI_CFG1);
+ /* Enable irq: cxfr for the codec path, pxfr for the preview path */
+ isi_writel(isi, ISI_INTEN,
+ ISI_SR_CXFR_DONE | ISI_SR_PXFR_DONE);
+
+ /* Check if already in a frame */
+ if (isi_readl(isi, ISI_STATUS) & ISI_CTRL_CDC) {
+ dev_err(isi->icd->parent, "Already in frame handling.\n");
+ return;
+ }
+
+ isi_writel(isi, ISI_DMA_C_DSCR, buffer->p_dma_desc->fbd_phys);
+ isi_writel(isi, ISI_DMA_C_CTRL, ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE);
+ isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_C_CH);
+
+ /* Enable linked list */
+ cfg1 |= isi->pdata->frate | ISI_CFG1_DISCR;
+
+ /* Enable codec path and ISI */
+ ctrl = ISI_CTRL_CDC | ISI_CTRL_EN;
+ isi_writel(isi, ISI_CTRL, ctrl);
+ isi_writel(isi, ISI_CFG1, cfg1);
+}
+
+static void buffer_queue(struct vb2_buffer *vb)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct atmel_isi *isi = ici->priv;
+ struct frame_buffer *buf = container_of(vb, struct frame_buffer, vb);
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&isi->lock, flags);
+ list_add_tail(&buf->list, &isi->video_buffer_list);
+
+ if (isi->active == NULL) {
+ isi->active = buf;
+ if (vb2_is_streaming(vb->vb2_queue))
+ start_dma(isi, buf);
+ }
+ spin_unlock_irqrestore(&isi->lock, flags);
+}
+
+static int start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct atmel_isi *isi = ici->priv;
+
+ u32 sr = 0;
+ int ret;
+
+ spin_lock_irq(&isi->lock);
+ isi->state = ISI_STATE_IDLE;
+ /* Clear any pending SOF interrupt */
+ sr = isi_readl(isi, ISI_STATUS);
+ /* Enable VSYNC interrupt for SOF */
+ isi_writel(isi, ISI_INTEN, ISI_SR_VSYNC);
+ isi_writel(isi, ISI_CTRL, ISI_CTRL_EN);
+ spin_unlock_irq(&isi->lock);
+
+ dev_dbg(icd->parent, "Waiting for SOF\n");
+ ret = wait_event_interruptible(isi->vsync_wq,
+ isi->state != ISI_STATE_IDLE);
+ if (ret)
+ goto err;
+
+ if (isi->state != ISI_STATE_READY) {
+ ret = -EIO;
+ goto err;
+ }
+
+ spin_lock_irq(&isi->lock);
+ isi->state = ISI_STATE_WAIT_SOF;
+ isi_writel(isi, ISI_INTDIS, ISI_SR_VSYNC);
+ if (count)
+ start_dma(isi, isi->active);
+ spin_unlock_irq(&isi->lock);
+
+ return 0;
+err:
+ isi->active = NULL;
+ isi->sequence = 0;
+ INIT_LIST_HEAD(&isi->video_buffer_list);
+ return ret;
+}
+
+/* abort streaming and wait for last buffer */
+static int stop_streaming(struct vb2_queue *vq)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct atmel_isi *isi = ici->priv;
+ struct frame_buffer *buf, *node;
+ int ret = 0;
+ unsigned long timeout;
+
+ spin_lock_irq(&isi->lock);
+ isi->active = NULL;
+ /* Release all active buffers */
+ list_for_each_entry_safe(buf, node, &isi->video_buffer_list, list) {
+ list_del_init(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+ spin_unlock_irq(&isi->lock);
+
+ timeout = jiffies + FRAME_INTERVAL_MILLI_SEC * HZ;
+ /* Wait until the end of the current frame. */
+ while ((isi_readl(isi, ISI_STATUS) & ISI_CTRL_CDC) &&
+ time_before(jiffies, timeout))
+ msleep(1);
+
+ if (time_after(jiffies, timeout)) {
+ dev_err(icd->parent,
+ "Timeout waiting for finishing codec request\n");
+ return -ETIMEDOUT;
+ }
+
+ /* Disable interrupts */
+ isi_writel(isi, ISI_INTDIS,
+ ISI_SR_CXFR_DONE | ISI_SR_PXFR_DONE);
+
+ /* Disable ISI and wait for it is done */
+ ret = atmel_isi_wait_status(isi, WAIT_ISI_DISABLE);
+ if (ret < 0)
+ dev_err(icd->parent, "Disable ISI timed out\n");
+
+ return ret;
+}
+
+static struct vb2_ops isi_video_qops = {
+ .queue_setup = queue_setup,
+ .buf_init = buffer_init,
+ .buf_prepare = buffer_prepare,
+ .buf_cleanup = buffer_cleanup,
+ .buf_queue = buffer_queue,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
+ .wait_prepare = soc_camera_unlock,
+ .wait_finish = soc_camera_lock,
+};
+
+/* ------------------------------------------------------------------
+ SOC camera operations for the device
+ ------------------------------------------------------------------*/
+static int isi_camera_init_videobuf(struct vb2_queue *q,
+ struct soc_camera_device *icd)
+{
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP;
+ q->drv_priv = icd;
+ q->buf_struct_size = sizeof(struct frame_buffer);
+ q->ops = &isi_video_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+
+ return vb2_queue_init(q);
+}
+
+static int isi_camera_set_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct atmel_isi *isi = ici->priv;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_mbus_framefmt mf;
+ int ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+ if (!xlate) {
+ dev_warn(icd->parent, "Format %x not found\n",
+ pix->pixelformat);
+ return -EINVAL;
+ }
+
+ dev_dbg(icd->parent, "Plan to set format %dx%d\n",
+ pix->width, pix->height);
+
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
+
+ ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ if (mf.code != xlate->code)
+ return -EINVAL;
+
+ ret = configure_geometry(isi, pix->width, pix->height, xlate->code);
+ if (ret < 0)
+ return ret;
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->field = mf.field;
+ pix->colorspace = mf.colorspace;
+ icd->current_fmt = xlate;
+
+ dev_dbg(icd->parent, "Finally set format %dx%d\n",
+ pix->width, pix->height);
+
+ return ret;
+}
+
+static int isi_camera_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_mbus_framefmt mf;
+ u32 pixfmt = pix->pixelformat;
+ int ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (pixfmt && !xlate) {
+ dev_warn(icd->parent, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+
+ /* limit to Atmel ISI hardware capabilities */
+ if (pix->height > MAX_SUPPORT_HEIGHT)
+ pix->height = MAX_SUPPORT_HEIGHT;
+ if (pix->width > MAX_SUPPORT_WIDTH)
+ pix->width = MAX_SUPPORT_WIDTH;
+
+ /* limit to sensor capabilities */
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
+
+ ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->colorspace = mf.colorspace;
+
+ switch (mf.field) {
+ case V4L2_FIELD_ANY:
+ pix->field = V4L2_FIELD_NONE;
+ break;
+ case V4L2_FIELD_NONE:
+ break;
+ default:
+ dev_err(icd->parent, "Field type %d unsupported.\n",
+ mf.field);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct soc_mbus_pixelfmt isi_camera_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .name = "Packed YUV422 16 bit",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+};
+
+/* This will be corrected as we get more formats */
+static bool isi_camera_packing_supported(const struct soc_mbus_pixelfmt *fmt)
+{
+ return fmt->packing == SOC_MBUS_PACKING_NONE ||
+ (fmt->bits_per_sample == 8 &&
+ fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) ||
+ (fmt->bits_per_sample > 8 &&
+ fmt->packing == SOC_MBUS_PACKING_EXTEND16);
+}
+
+#define ISI_BUS_PARAM (V4L2_MBUS_MASTER | \
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_HSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_VSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_PCLK_SAMPLE_RISING | \
+ V4L2_MBUS_PCLK_SAMPLE_FALLING | \
+ V4L2_MBUS_DATA_ACTIVE_HIGH)
+
+static int isi_camera_try_bus_param(struct soc_camera_device *icd,
+ unsigned char buswidth)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct atmel_isi *isi = ici->priv;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long common_flags;
+ int ret;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg,
+ ISI_BUS_PARAM);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%x\n",
+ cfg.flags, ISI_BUS_PARAM);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ }
+
+ if ((1 << (buswidth - 1)) & isi->width_flags)
+ return 0;
+ return -EINVAL;
+}
+
+
+static int isi_camera_get_formats(struct soc_camera_device *icd,
+ unsigned int idx,
+ struct soc_camera_format_xlate *xlate)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ int formats = 0, ret;
+ /* sensor format */
+ enum v4l2_mbus_pixelcode code;
+ /* soc camera host format */
+ const struct soc_mbus_pixelfmt *fmt;
+
+ ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
+ if (ret < 0)
+ /* No more formats */
+ return 0;
+
+ fmt = soc_mbus_get_fmtdesc(code);
+ if (!fmt) {
+ dev_err(icd->parent,
+ "Invalid format code #%u: %d\n", idx, code);
+ return 0;
+ }
+
+ /* This also checks support for the requested bits-per-sample */
+ ret = isi_camera_try_bus_param(icd, fmt->bits_per_sample);
+ if (ret < 0) {
+ dev_err(icd->parent,
+ "Fail to try the bus parameters.\n");
+ return 0;
+ }
+
+ switch (code) {
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ case V4L2_MBUS_FMT_VYUY8_2X8:
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ case V4L2_MBUS_FMT_YVYU8_2X8:
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = &isi_camera_formats[0];
+ xlate->code = code;
+ xlate++;
+ dev_dbg(icd->parent, "Providing format %s using code %d\n",
+ isi_camera_formats[0].name, code);
+ }
+ break;
+ default:
+ if (!isi_camera_packing_supported(fmt))
+ return 0;
+ if (xlate)
+ dev_dbg(icd->parent,
+ "Providing format %s in pass-through mode\n",
+ fmt->name);
+ }
+
+ /* Generic pass-through */
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = fmt;
+ xlate->code = code;
+ xlate++;
+ }
+
+ return formats;
+}
+
+/* Called with .video_lock held */
+static int isi_camera_add_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct atmel_isi *isi = ici->priv;
+ int ret;
+
+ if (isi->icd)
+ return -EBUSY;
+
+ ret = clk_enable(isi->pclk);
+ if (ret)
+ return ret;
+
+ ret = clk_enable(isi->mck);
+ if (ret) {
+ clk_disable(isi->pclk);
+ return ret;
+ }
+
+ isi->icd = icd;
+ dev_dbg(icd->parent, "Atmel ISI Camera driver attached to camera %d\n",
+ icd->devnum);
+ return 0;
+}
+/* Called with .video_lock held */
+static void isi_camera_remove_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct atmel_isi *isi = ici->priv;
+
+ BUG_ON(icd != isi->icd);
+
+ clk_disable(isi->mck);
+ clk_disable(isi->pclk);
+ isi->icd = NULL;
+
+ dev_dbg(icd->parent, "Atmel ISI Camera driver detached from camera %d\n",
+ icd->devnum);
+}
+
+static unsigned int isi_camera_poll(struct file *file, poll_table *pt)
+{
+ struct soc_camera_device *icd = file->private_data;
+
+ return vb2_poll(&icd->vb2_vidq, file, pt);
+}
+
+static int isi_camera_querycap(struct soc_camera_host *ici,
+ struct v4l2_capability *cap)
+{
+ strcpy(cap->driver, "atmel-isi");
+ strcpy(cap->card, "Atmel Image Sensor Interface");
+ cap->capabilities = (V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_STREAMING);
+ return 0;
+}
+
+static int isi_camera_set_bus_param(struct soc_camera_device *icd)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct atmel_isi *isi = ici->priv;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long common_flags;
+ int ret;
+ u32 cfg1 = 0;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg,
+ ISI_BUS_PARAM);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%x\n",
+ cfg.flags, ISI_BUS_PARAM);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = ISI_BUS_PARAM;
+ }
+ dev_dbg(icd->parent, "Flags cam: 0x%x host: 0x%x common: 0x%lx\n",
+ cfg.flags, ISI_BUS_PARAM, common_flags);
+
+ /* Make choises, based on platform preferences */
+ if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
+ if (isi->pdata->hsync_act_low)
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
+ else
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
+ }
+
+ if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
+ if (isi->pdata->vsync_act_low)
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
+ else
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
+ }
+
+ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+ (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
+ if (isi->pdata->pclk_act_falling)
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
+ else
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
+ }
+
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n",
+ common_flags, ret);
+ return ret;
+ }
+
+ /* set bus param for ISI */
+ if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ cfg1 |= ISI_CFG1_HSYNC_POL_ACTIVE_LOW;
+ if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ cfg1 |= ISI_CFG1_VSYNC_POL_ACTIVE_LOW;
+ if (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
+ cfg1 |= ISI_CFG1_PIXCLK_POL_ACTIVE_FALLING;
+
+ if (isi->pdata->has_emb_sync)
+ cfg1 |= ISI_CFG1_EMB_SYNC;
+ if (isi->pdata->full_mode)
+ cfg1 |= ISI_CFG1_FULL_MODE;
+
+ isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS);
+ isi_writel(isi, ISI_CFG1, cfg1);
+
+ return 0;
+}
+
+static struct soc_camera_host_ops isi_soc_camera_host_ops = {
+ .owner = THIS_MODULE,
+ .add = isi_camera_add_device,
+ .remove = isi_camera_remove_device,
+ .set_fmt = isi_camera_set_fmt,
+ .try_fmt = isi_camera_try_fmt,
+ .get_formats = isi_camera_get_formats,
+ .init_videobuf2 = isi_camera_init_videobuf,
+ .poll = isi_camera_poll,
+ .querycap = isi_camera_querycap,
+ .set_bus_param = isi_camera_set_bus_param,
+};
+
+/* -----------------------------------------------------------------------*/
+static int __devexit atmel_isi_remove(struct platform_device *pdev)
+{
+ struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+ struct atmel_isi *isi = container_of(soc_host,
+ struct atmel_isi, soc_host);
+
+ free_irq(isi->irq, isi);
+ soc_camera_host_unregister(soc_host);
+ vb2_dma_contig_cleanup_ctx(isi->alloc_ctx);
+ dma_free_coherent(&pdev->dev,
+ sizeof(struct fbd) * MAX_BUFFER_NUM,
+ isi->p_fb_descriptors,
+ isi->fb_descriptors_phys);
+
+ iounmap(isi->regs);
+ clk_unprepare(isi->mck);
+ clk_put(isi->mck);
+ clk_unprepare(isi->pclk);
+ clk_put(isi->pclk);
+ kfree(isi);
+
+ return 0;
+}
+
+static int __devinit atmel_isi_probe(struct platform_device *pdev)
+{
+ unsigned int irq;
+ struct atmel_isi *isi;
+ struct clk *pclk;
+ struct resource *regs;
+ int ret, i;
+ struct device *dev = &pdev->dev;
+ struct soc_camera_host *soc_host;
+ struct isi_platform_data *pdata;
+
+ pdata = dev->platform_data;
+ if (!pdata || !pdata->data_width_flags || !pdata->mck_hz) {
+ dev_err(&pdev->dev,
+ "No config available for Atmel ISI\n");
+ return -EINVAL;
+ }
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!regs)
+ return -ENXIO;
+
+ pclk = clk_get(&pdev->dev, "isi_clk");
+ if (IS_ERR(pclk))
+ return PTR_ERR(pclk);
+
+ ret = clk_prepare(pclk);
+ if (ret)
+ goto err_clk_prepare_pclk;
+
+ isi = kzalloc(sizeof(struct atmel_isi), GFP_KERNEL);
+ if (!isi) {
+ ret = -ENOMEM;
+ dev_err(&pdev->dev, "Can't allocate interface!\n");
+ goto err_alloc_isi;
+ }
+
+ isi->pclk = pclk;
+ isi->pdata = pdata;
+ isi->active = NULL;
+ spin_lock_init(&isi->lock);
+ init_waitqueue_head(&isi->vsync_wq);
+ INIT_LIST_HEAD(&isi->video_buffer_list);
+ INIT_LIST_HEAD(&isi->dma_desc_head);
+
+ /* Get ISI_MCK, provided by programmable clock or external clock */
+ isi->mck = clk_get(dev, "isi_mck");
+ if (IS_ERR(isi->mck)) {
+ dev_err(dev, "Failed to get isi_mck\n");
+ ret = PTR_ERR(isi->mck);
+ goto err_clk_get;
+ }
+
+ ret = clk_prepare(isi->mck);
+ if (ret)
+ goto err_clk_prepare_mck;
+
+ /* Set ISI_MCK's frequency, it should be faster than pixel clock */
+ ret = clk_set_rate(isi->mck, pdata->mck_hz);
+ if (ret < 0)
+ goto err_set_mck_rate;
+
+ isi->p_fb_descriptors = dma_alloc_coherent(&pdev->dev,
+ sizeof(struct fbd) * MAX_BUFFER_NUM,
+ &isi->fb_descriptors_phys,
+ GFP_KERNEL);
+ if (!isi->p_fb_descriptors) {
+ ret = -ENOMEM;
+ dev_err(&pdev->dev, "Can't allocate descriptors!\n");
+ goto err_alloc_descriptors;
+ }
+
+ for (i = 0; i < MAX_BUFFER_NUM; i++) {
+ isi->dma_desc[i].p_fbd = isi->p_fb_descriptors + i;
+ isi->dma_desc[i].fbd_phys = isi->fb_descriptors_phys +
+ i * sizeof(struct fbd);
+ list_add(&isi->dma_desc[i].list, &isi->dma_desc_head);
+ }
+
+ isi->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(isi->alloc_ctx)) {
+ ret = PTR_ERR(isi->alloc_ctx);
+ goto err_alloc_ctx;
+ }
+
+ isi->regs = ioremap(regs->start, resource_size(regs));
+ if (!isi->regs) {
+ ret = -ENOMEM;
+ goto err_ioremap;
+ }
+
+ if (pdata->data_width_flags & ISI_DATAWIDTH_8)
+ isi->width_flags = 1 << 7;
+ if (pdata->data_width_flags & ISI_DATAWIDTH_10)
+ isi->width_flags |= 1 << 9;
+
+ isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ ret = irq;
+ goto err_req_irq;
+ }
+
+ ret = request_irq(irq, isi_interrupt, 0, "isi", isi);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to request irq %d\n", irq);
+ goto err_req_irq;
+ }
+ isi->irq = irq;
+
+ soc_host = &isi->soc_host;
+ soc_host->drv_name = "isi-camera";
+ soc_host->ops = &isi_soc_camera_host_ops;
+ soc_host->priv = isi;
+ soc_host->v4l2_dev.dev = &pdev->dev;
+ soc_host->nr = pdev->id;
+
+ ret = soc_camera_host_register(soc_host);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to register soc camera host\n");
+ goto err_register_soc_camera_host;
+ }
+ return 0;
+
+err_register_soc_camera_host:
+ free_irq(isi->irq, isi);
+err_req_irq:
+ iounmap(isi->regs);
+err_ioremap:
+ vb2_dma_contig_cleanup_ctx(isi->alloc_ctx);
+err_alloc_ctx:
+ dma_free_coherent(&pdev->dev,
+ sizeof(struct fbd) * MAX_BUFFER_NUM,
+ isi->p_fb_descriptors,
+ isi->fb_descriptors_phys);
+err_alloc_descriptors:
+err_set_mck_rate:
+ clk_unprepare(isi->mck);
+err_clk_prepare_mck:
+ clk_put(isi->mck);
+err_clk_get:
+ kfree(isi);
+err_alloc_isi:
+ clk_unprepare(pclk);
+err_clk_prepare_pclk:
+ clk_put(pclk);
+
+ return ret;
+}
+
+static struct platform_driver atmel_isi_driver = {
+ .probe = atmel_isi_probe,
+ .remove = __devexit_p(atmel_isi_remove),
+ .driver = {
+ .name = "atmel_isi",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init atmel_isi_init_module(void)
+{
+ return platform_driver_probe(&atmel_isi_driver, &atmel_isi_probe);
+}
+
+static void __exit atmel_isi_exit(void)
+{
+ platform_driver_unregister(&atmel_isi_driver);
+}
+module_init(atmel_isi_init_module);
+module_exit(atmel_isi_exit);
+
+MODULE_AUTHOR("Josh Wu <josh.wu@atmel.com>");
+MODULE_DESCRIPTION("The V4L2 driver for Atmel Linux");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("video");
diff --git a/drivers/media/video/au0828/Kconfig b/drivers/media/video/au0828/Kconfig
new file mode 100644
index 00000000..81ba9d9d
--- /dev/null
+++ b/drivers/media/video/au0828/Kconfig
@@ -0,0 +1,17 @@
+
+config VIDEO_AU0828
+ tristate "Auvitek AU0828 support"
+ depends on I2C && INPUT && DVB_CORE && USB && VIDEO_V4L2
+ depends on DVB_CAPTURE_DRIVERS
+ select I2C_ALGOBIT
+ select VIDEO_TVEEPROM
+ select VIDEOBUF_VMALLOC
+ select DVB_AU8522 if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE
+ select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE
+ select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE
+ ---help---
+ This is a video4linux driver for Auvitek's USB device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called au0828
diff --git a/drivers/media/video/au0828/Makefile b/drivers/media/video/au0828/Makefile
new file mode 100644
index 00000000..bd22223f
--- /dev/null
+++ b/drivers/media/video/au0828/Makefile
@@ -0,0 +1,9 @@
+au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o au0828-video.o au0828-vbi.o
+
+obj-$(CONFIG_VIDEO_AU0828) += au0828.o
+
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
+
+ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/video/au0828/au0828-cards.c b/drivers/media/video/au0828/au0828-cards.c
new file mode 100644
index 00000000..1c6015a0
--- /dev/null
+++ b/drivers/media/video/au0828/au0828-cards.c
@@ -0,0 +1,337 @@
+/*
+ * Driver for the Auvitek USB bridge
+ *
+ * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "au0828.h"
+#include "au0828-cards.h"
+#include "au8522.h"
+#include "media/tuner.h"
+#include "media/v4l2-common.h"
+
+void hvr950q_cs5340_audio(void *priv, int enable)
+{
+ /* Because the HVR-950q shares an i2s bus between the cs5340 and the
+ au8522, we need to hold cs5340 in reset when using the au8522 */
+ struct au0828_dev *dev = priv;
+ if (enable == 1)
+ au0828_set(dev, REG_000, 0x10);
+ else
+ au0828_clear(dev, REG_000, 0x10);
+}
+
+struct au0828_board au0828_boards[] = {
+ [AU0828_BOARD_UNKNOWN] = {
+ .name = "Unknown board",
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [AU0828_BOARD_HAUPPAUGE_HVR850] = {
+ .name = "Hauppauge HVR850",
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0x61,
+ .i2c_clk_divider = AU0828_I2C_CLK_30KHZ,
+ .input = {
+ {
+ .type = AU0828_VMUX_TELEVISION,
+ .vmux = AU8522_COMPOSITE_CH4_SIF,
+ .amux = AU8522_AUDIO_SIF,
+ },
+ {
+ .type = AU0828_VMUX_COMPOSITE,
+ .vmux = AU8522_COMPOSITE_CH1,
+ .amux = AU8522_AUDIO_NONE,
+ .audio_setup = hvr950q_cs5340_audio,
+ },
+ {
+ .type = AU0828_VMUX_SVIDEO,
+ .vmux = AU8522_SVIDEO_CH13,
+ .amux = AU8522_AUDIO_NONE,
+ .audio_setup = hvr950q_cs5340_audio,
+ },
+ },
+ },
+ [AU0828_BOARD_HAUPPAUGE_HVR950Q] = {
+ .name = "Hauppauge HVR950Q",
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0x61,
+ /* The au0828 hardware i2c implementation does not properly
+ support the xc5000's i2c clock stretching. So we need to
+ lower the clock frequency enough where the 15us clock
+ stretch fits inside of a normal clock cycle, or else the
+ au0828 fails to set the STOP bit. A 30 KHz clock puts the
+ clock pulse width at 18us */
+ .i2c_clk_divider = AU0828_I2C_CLK_30KHZ,
+ .input = {
+ {
+ .type = AU0828_VMUX_TELEVISION,
+ .vmux = AU8522_COMPOSITE_CH4_SIF,
+ .amux = AU8522_AUDIO_SIF,
+ },
+ {
+ .type = AU0828_VMUX_COMPOSITE,
+ .vmux = AU8522_COMPOSITE_CH1,
+ .amux = AU8522_AUDIO_NONE,
+ .audio_setup = hvr950q_cs5340_audio,
+ },
+ {
+ .type = AU0828_VMUX_SVIDEO,
+ .vmux = AU8522_SVIDEO_CH13,
+ .amux = AU8522_AUDIO_NONE,
+ .audio_setup = hvr950q_cs5340_audio,
+ },
+ },
+ },
+ [AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL] = {
+ .name = "Hauppauge HVR950Q rev xxF8",
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
+ },
+ [AU0828_BOARD_DVICO_FUSIONHDTV7] = {
+ .name = "DViCO FusionHDTV USB",
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
+ },
+ [AU0828_BOARD_HAUPPAUGE_WOODBURY] = {
+ .name = "Hauppauge Woodbury",
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
+ },
+};
+
+/* Tuner callback function for au0828 boards. Currently only needed
+ * for HVR1500Q, which has an xc5000 tuner.
+ */
+int au0828_tuner_callback(void *priv, int component, int command, int arg)
+{
+ struct au0828_dev *dev = priv;
+
+ dprintk(1, "%s()\n", __func__);
+
+ switch (dev->boardnr) {
+ case AU0828_BOARD_HAUPPAUGE_HVR850:
+ case AU0828_BOARD_HAUPPAUGE_HVR950Q:
+ case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
+ case AU0828_BOARD_DVICO_FUSIONHDTV7:
+ if (command == 0) {
+ /* Tuner Reset Command from xc5000 */
+ /* Drive the tuner into reset and out */
+ au0828_clear(dev, REG_001, 2);
+ mdelay(10);
+ au0828_set(dev, REG_001, 2);
+ mdelay(10);
+ return 0;
+ } else {
+ printk(KERN_ERR
+ "%s(): Unknown command.\n", __func__);
+ return -EINVAL;
+ }
+ break;
+ }
+
+ return 0; /* Should never be here */
+}
+
+static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data)
+{
+ struct tveeprom tv;
+
+ tveeprom_hauppauge_analog(&dev->i2c_client, &tv, eeprom_data);
+ dev->board.tuner_type = tv.tuner_type;
+
+ /* Make sure we support the board model */
+ switch (tv.model) {
+ case 72000: /* WinTV-HVR950q (Retail, IR, ATSC/QAM */
+ case 72001: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */
+ case 72101: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */
+ case 72201: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */
+ case 72211: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */
+ case 72221: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */
+ case 72231: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */
+ case 72241: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */
+ case 72251: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */
+ case 72261: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */
+ case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and analog video */
+ case 72500: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */
+ break;
+ default:
+ printk(KERN_WARNING "%s: warning: "
+ "unknown hauppauge model #%d\n", __func__, tv.model);
+ break;
+ }
+
+ printk(KERN_INFO "%s: hauppauge eeprom: model=%d\n",
+ __func__, tv.model);
+}
+
+void au0828_card_setup(struct au0828_dev *dev)
+{
+ static u8 eeprom[256];
+ struct tuner_setup tun_setup;
+ struct v4l2_subdev *sd;
+ unsigned int mode_mask = T_ANALOG_TV;
+
+ dprintk(1, "%s()\n", __func__);
+
+ memcpy(&dev->board, &au0828_boards[dev->boardnr], sizeof(dev->board));
+
+ if (dev->i2c_rc == 0) {
+ dev->i2c_client.addr = 0xa0 >> 1;
+ tveeprom_read(&dev->i2c_client, eeprom, sizeof(eeprom));
+ }
+
+ switch (dev->boardnr) {
+ case AU0828_BOARD_HAUPPAUGE_HVR850:
+ case AU0828_BOARD_HAUPPAUGE_HVR950Q:
+ case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
+ case AU0828_BOARD_HAUPPAUGE_WOODBURY:
+ if (dev->i2c_rc == 0)
+ hauppauge_eeprom(dev, eeprom+0xa0);
+ break;
+ }
+
+ if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) {
+ /* Load the analog demodulator driver (note this would need to
+ be abstracted out if we ever need to support a different
+ demod) */
+ sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+ "au8522", 0x8e >> 1, NULL);
+ if (sd == NULL)
+ printk(KERN_ERR "analog subdev registration failed\n");
+ }
+
+ /* Setup tuners */
+ if (dev->board.tuner_type != TUNER_ABSENT) {
+ /* Load the tuner module, which does the attach */
+ sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+ "tuner", dev->board.tuner_addr, NULL);
+ if (sd == NULL)
+ printk(KERN_ERR "tuner subdev registration fail\n");
+
+ tun_setup.mode_mask = mode_mask;
+ tun_setup.type = dev->board.tuner_type;
+ tun_setup.addr = dev->board.tuner_addr;
+ tun_setup.tuner_callback = au0828_tuner_callback;
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr,
+ &tun_setup);
+ }
+}
+
+/*
+ * The bridge has between 8 and 12 gpios.
+ * Regs 1 and 0 deal with output enables.
+ * Regs 3 and 2 deal with direction.
+ */
+void au0828_gpio_setup(struct au0828_dev *dev)
+{
+ dprintk(1, "%s()\n", __func__);
+
+ switch (dev->boardnr) {
+ case AU0828_BOARD_HAUPPAUGE_HVR850:
+ case AU0828_BOARD_HAUPPAUGE_HVR950Q:
+ case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
+ case AU0828_BOARD_HAUPPAUGE_WOODBURY:
+ /* GPIO's
+ * 4 - CS5340
+ * 5 - AU8522 Demodulator
+ * 6 - eeprom W/P
+ * 7 - power supply
+ * 9 - XC5000 Tuner
+ */
+
+ /* Into reset */
+ au0828_write(dev, REG_003, 0x02);
+ au0828_write(dev, REG_002, 0x80 | 0x20 | 0x10);
+ au0828_write(dev, REG_001, 0x0);
+ au0828_write(dev, REG_000, 0x0);
+ msleep(100);
+
+ /* Out of reset (leave the cs5340 in reset until needed) */
+ au0828_write(dev, REG_003, 0x02);
+ au0828_write(dev, REG_001, 0x02);
+ au0828_write(dev, REG_002, 0x80 | 0x20 | 0x10);
+ au0828_write(dev, REG_000, 0x80 | 0x40 | 0x20);
+
+ msleep(250);
+ break;
+ case AU0828_BOARD_DVICO_FUSIONHDTV7:
+ /* GPIO's
+ * 6 - ?
+ * 8 - AU8522 Demodulator
+ * 9 - XC5000 Tuner
+ */
+
+ /* Into reset */
+ au0828_write(dev, REG_003, 0x02);
+ au0828_write(dev, REG_002, 0xa0);
+ au0828_write(dev, REG_001, 0x0);
+ au0828_write(dev, REG_000, 0x0);
+ msleep(100);
+
+ /* Out of reset */
+ au0828_write(dev, REG_003, 0x02);
+ au0828_write(dev, REG_002, 0xa0);
+ au0828_write(dev, REG_001, 0x02);
+ au0828_write(dev, REG_000, 0xa0);
+ msleep(250);
+ break;
+ }
+}
+
+/* table of devices that work with this driver */
+struct usb_device_id au0828_usb_id_table[] = {
+ { USB_DEVICE(0x2040, 0x7200),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
+ { USB_DEVICE(0x2040, 0x7240),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR850 },
+ { USB_DEVICE(0x0fe9, 0xd620),
+ .driver_info = AU0828_BOARD_DVICO_FUSIONHDTV7 },
+ { USB_DEVICE(0x2040, 0x7210),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
+ { USB_DEVICE(0x2040, 0x7217),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
+ { USB_DEVICE(0x2040, 0x721b),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
+ { USB_DEVICE(0x2040, 0x721e),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
+ { USB_DEVICE(0x2040, 0x721f),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
+ { USB_DEVICE(0x2040, 0x7280),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
+ { USB_DEVICE(0x0fd9, 0x0008),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
+ { USB_DEVICE(0x2040, 0x7201),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL },
+ { USB_DEVICE(0x2040, 0x7211),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL },
+ { USB_DEVICE(0x2040, 0x7281),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL },
+ { USB_DEVICE(0x2040, 0x8200),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_WOODBURY },
+ { USB_DEVICE(0x2040, 0x7260),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
+ { USB_DEVICE(0x2040, 0x7213),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
+ { },
+};
+
+MODULE_DEVICE_TABLE(usb, au0828_usb_id_table);
diff --git a/drivers/media/video/au0828/au0828-cards.h b/drivers/media/video/au0828/au0828-cards.h
new file mode 100644
index 00000000..48a1882c
--- /dev/null
+++ b/drivers/media/video/au0828/au0828-cards.h
@@ -0,0 +1,27 @@
+/*
+ * Driver for the Auvitek USB bridge
+ *
+ * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define AU0828_BOARD_UNKNOWN 0
+#define AU0828_BOARD_HAUPPAUGE_HVR950Q 1
+#define AU0828_BOARD_HAUPPAUGE_HVR850 2
+#define AU0828_BOARD_DVICO_FUSIONHDTV7 3
+#define AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL 4
+#define AU0828_BOARD_HAUPPAUGE_WOODBURY 5
diff --git a/drivers/media/video/au0828/au0828-core.c b/drivers/media/video/au0828/au0828-core.c
new file mode 100644
index 00000000..1e4ce506
--- /dev/null
+++ b/drivers/media/video/au0828/au0828-core.c
@@ -0,0 +1,295 @@
+/*
+ * Driver for the Auvitek USB bridge
+ *
+ * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <linux/mutex.h>
+
+#include "au0828.h"
+
+/*
+ * 1 = General debug messages
+ * 2 = USB handling
+ * 4 = I2C related
+ * 8 = Bridge related
+ */
+int au0828_debug;
+module_param_named(debug, au0828_debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable debug messages");
+
+static unsigned int disable_usb_speed_check;
+module_param(disable_usb_speed_check, int, 0444);
+MODULE_PARM_DESC(disable_usb_speed_check,
+ "override min bandwidth requirement of 480M bps");
+
+#define _AU0828_BULKPIPE 0x03
+#define _BULKPIPESIZE 0xffff
+
+static int send_control_msg(struct au0828_dev *dev, u16 request, u32 value,
+ u16 index, unsigned char *cp, u16 size);
+static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value,
+ u16 index, unsigned char *cp, u16 size);
+
+/* USB Direction */
+#define CMD_REQUEST_IN 0x00
+#define CMD_REQUEST_OUT 0x01
+
+u32 au0828_readreg(struct au0828_dev *dev, u16 reg)
+{
+ recv_control_msg(dev, CMD_REQUEST_IN, 0, reg, dev->ctrlmsg, 1);
+ dprintk(8, "%s(0x%04x) = 0x%02x\n", __func__, reg, dev->ctrlmsg[0]);
+ return dev->ctrlmsg[0];
+}
+
+u32 au0828_writereg(struct au0828_dev *dev, u16 reg, u32 val)
+{
+ dprintk(8, "%s(0x%04x, 0x%02x)\n", __func__, reg, val);
+ return send_control_msg(dev, CMD_REQUEST_OUT, val, reg,
+ dev->ctrlmsg, 0);
+}
+
+static void cmd_msg_dump(struct au0828_dev *dev)
+{
+ int i;
+
+ for (i = 0; i < sizeof(dev->ctrlmsg); i += 16)
+ dprintk(2, "%s() %02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ __func__,
+ dev->ctrlmsg[i+0], dev->ctrlmsg[i+1],
+ dev->ctrlmsg[i+2], dev->ctrlmsg[i+3],
+ dev->ctrlmsg[i+4], dev->ctrlmsg[i+5],
+ dev->ctrlmsg[i+6], dev->ctrlmsg[i+7],
+ dev->ctrlmsg[i+8], dev->ctrlmsg[i+9],
+ dev->ctrlmsg[i+10], dev->ctrlmsg[i+11],
+ dev->ctrlmsg[i+12], dev->ctrlmsg[i+13],
+ dev->ctrlmsg[i+14], dev->ctrlmsg[i+15]);
+}
+
+static int send_control_msg(struct au0828_dev *dev, u16 request, u32 value,
+ u16 index, unsigned char *cp, u16 size)
+{
+ int status = -ENODEV;
+ mutex_lock(&dev->mutex);
+ if (dev->usbdev) {
+
+ /* cp must be memory that has been allocated by kmalloc */
+ status = usb_control_msg(dev->usbdev,
+ usb_sndctrlpipe(dev->usbdev, 0),
+ request,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE,
+ value, index,
+ cp, size, 1000);
+
+ status = min(status, 0);
+
+ if (status < 0) {
+ printk(KERN_ERR "%s() Failed sending control message, error %d.\n",
+ __func__, status);
+ }
+
+ }
+ mutex_unlock(&dev->mutex);
+ return status;
+}
+
+static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value,
+ u16 index, unsigned char *cp, u16 size)
+{
+ int status = -ENODEV;
+ mutex_lock(&dev->mutex);
+ if (dev->usbdev) {
+
+ memset(dev->ctrlmsg, 0, sizeof(dev->ctrlmsg));
+
+ /* cp must be memory that has been allocated by kmalloc */
+ status = usb_control_msg(dev->usbdev,
+ usb_rcvctrlpipe(dev->usbdev, 0),
+ request,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index,
+ cp, size, 1000);
+
+ status = min(status, 0);
+
+ if (status < 0) {
+ printk(KERN_ERR "%s() Failed receiving control message, error %d.\n",
+ __func__, status);
+ } else
+ cmd_msg_dump(dev);
+ }
+ mutex_unlock(&dev->mutex);
+ return status;
+}
+
+static void au0828_usb_disconnect(struct usb_interface *interface)
+{
+ struct au0828_dev *dev = usb_get_intfdata(interface);
+
+ dprintk(1, "%s()\n", __func__);
+
+ /* Digital TV */
+ au0828_dvb_unregister(dev);
+
+ if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED)
+ au0828_analog_unregister(dev);
+
+ /* I2C */
+ au0828_i2c_unregister(dev);
+
+ v4l2_device_unregister(&dev->v4l2_dev);
+
+ usb_set_intfdata(interface, NULL);
+
+ mutex_lock(&dev->mutex);
+ dev->usbdev = NULL;
+ mutex_unlock(&dev->mutex);
+
+ kfree(dev);
+
+}
+
+static int au0828_usb_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ int ifnum, retval;
+ struct au0828_dev *dev;
+ struct usb_device *usbdev = interface_to_usbdev(interface);
+
+ ifnum = interface->altsetting->desc.bInterfaceNumber;
+
+ if (ifnum != 0)
+ return -ENODEV;
+
+ dprintk(1, "%s() vendor id 0x%x device id 0x%x ifnum:%d\n", __func__,
+ le16_to_cpu(usbdev->descriptor.idVendor),
+ le16_to_cpu(usbdev->descriptor.idProduct),
+ ifnum);
+
+ /*
+ * Make sure we have 480 Mbps of bandwidth, otherwise things like
+ * video stream wouldn't likely work, since 12 Mbps is generally
+ * not enough even for most Digital TV streams.
+ */
+ if (usbdev->speed != USB_SPEED_HIGH && disable_usb_speed_check == 0) {
+ printk(KERN_ERR "au0828: Device initialization failed.\n");
+ printk(KERN_ERR "au0828: Device must be connected to a "
+ "high-speed USB 2.0 port.\n");
+ return -ENODEV;
+ }
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL) {
+ printk(KERN_ERR "%s() Unable to allocate memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ mutex_init(&dev->mutex);
+ mutex_init(&dev->dvb.lock);
+ dev->usbdev = usbdev;
+ dev->boardnr = id->driver_info;
+
+ /* Create the v4l2_device */
+ retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev);
+ if (retval) {
+ printk(KERN_ERR "%s() v4l2_device_register failed\n",
+ __func__);
+ kfree(dev);
+ return -EIO;
+ }
+
+ /* Power Up the bridge */
+ au0828_write(dev, REG_600, 1 << 4);
+
+ /* Bring up the GPIO's and supporting devices */
+ au0828_gpio_setup(dev);
+
+ /* I2C */
+ au0828_i2c_register(dev);
+
+ /* Setup */
+ au0828_card_setup(dev);
+
+ /* Analog TV */
+ if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED)
+ au0828_analog_register(dev, interface);
+
+ /* Digital TV */
+ au0828_dvb_register(dev);
+
+ /* Store the pointer to the au0828_dev so it can be accessed in
+ au0828_usb_disconnect */
+ usb_set_intfdata(interface, dev);
+
+ printk(KERN_INFO "Registered device AU0828 [%s]\n",
+ dev->board.name == NULL ? "Unset" : dev->board.name);
+
+ return 0;
+}
+
+static struct usb_driver au0828_usb_driver = {
+ .name = DRIVER_NAME,
+ .probe = au0828_usb_probe,
+ .disconnect = au0828_usb_disconnect,
+ .id_table = au0828_usb_id_table,
+};
+
+static int __init au0828_init(void)
+{
+ int ret;
+
+ if (au0828_debug & 1)
+ printk(KERN_INFO "%s() Debugging is enabled\n", __func__);
+
+ if (au0828_debug & 2)
+ printk(KERN_INFO "%s() USB Debugging is enabled\n", __func__);
+
+ if (au0828_debug & 4)
+ printk(KERN_INFO "%s() I2C Debugging is enabled\n", __func__);
+
+ if (au0828_debug & 8)
+ printk(KERN_INFO "%s() Bridge Debugging is enabled\n",
+ __func__);
+
+ printk(KERN_INFO "au0828 driver loaded\n");
+
+ ret = usb_register(&au0828_usb_driver);
+ if (ret)
+ printk(KERN_ERR "usb_register failed, error = %d\n", ret);
+
+ return ret;
+}
+
+static void __exit au0828_exit(void)
+{
+ usb_deregister(&au0828_usb_driver);
+}
+
+module_init(au0828_init);
+module_exit(au0828_exit);
+
+MODULE_DESCRIPTION("Driver for Auvitek AU0828 based products");
+MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.2");
diff --git a/drivers/media/video/au0828/au0828-dvb.c b/drivers/media/video/au0828/au0828-dvb.c
new file mode 100644
index 00000000..51821674
--- /dev/null
+++ b/drivers/media/video/au0828/au0828-dvb.c
@@ -0,0 +1,439 @@
+/*
+ * Driver for the Auvitek USB bridge
+ *
+ * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/suspend.h>
+#include <media/v4l2-common.h>
+
+#include "au0828.h"
+#include "au8522.h"
+#include "xc5000.h"
+#include "mxl5007t.h"
+#include "tda18271.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define _AU0828_BULKPIPE 0x83
+#define _BULKPIPESIZE 0xe522
+
+static u8 hauppauge_hvr950q_led_states[] = {
+ 0x00, /* off */
+ 0x02, /* yellow */
+ 0x04, /* green */
+};
+
+static struct au8522_led_config hauppauge_hvr950q_led_cfg = {
+ .gpio_output = 0x00e0,
+ .gpio_output_enable = 0x6006,
+ .gpio_output_disable = 0x0660,
+
+ .gpio_leds = 0x00e2,
+ .led_states = hauppauge_hvr950q_led_states,
+ .num_led_states = sizeof(hauppauge_hvr950q_led_states),
+
+ .vsb8_strong = 20 /* dB */ * 10,
+ .qam64_strong = 25 /* dB */ * 10,
+ .qam256_strong = 32 /* dB */ * 10,
+};
+
+static struct au8522_config hauppauge_hvr950q_config = {
+ .demod_address = 0x8e >> 1,
+ .status_mode = AU8522_DEMODLOCKING,
+ .qam_if = AU8522_IF_6MHZ,
+ .vsb_if = AU8522_IF_6MHZ,
+ .led_cfg = &hauppauge_hvr950q_led_cfg,
+};
+
+static struct au8522_config fusionhdtv7usb_config = {
+ .demod_address = 0x8e >> 1,
+ .status_mode = AU8522_DEMODLOCKING,
+ .qam_if = AU8522_IF_6MHZ,
+ .vsb_if = AU8522_IF_6MHZ,
+};
+
+static struct au8522_config hauppauge_woodbury_config = {
+ .demod_address = 0x8e >> 1,
+ .status_mode = AU8522_DEMODLOCKING,
+ .qam_if = AU8522_IF_4MHZ,
+ .vsb_if = AU8522_IF_3_25MHZ,
+};
+
+static struct xc5000_config hauppauge_hvr950q_tunerconfig = {
+ .i2c_address = 0x61,
+ .if_khz = 6000,
+};
+
+static struct mxl5007t_config mxl5007t_hvr950q_config = {
+ .xtal_freq_hz = MxL_XTAL_24_MHZ,
+ .if_freq_hz = MxL_IF_6_MHZ,
+};
+
+static struct tda18271_config hauppauge_woodbury_tunerconfig = {
+ .gate = TDA18271_GATE_DIGITAL,
+};
+
+/*-------------------------------------------------------------------*/
+static void urb_completion(struct urb *purb)
+{
+ struct au0828_dev *dev = purb->context;
+ int ptype = usb_pipetype(purb->pipe);
+
+ dprintk(2, "%s()\n", __func__);
+
+ if (!dev)
+ return;
+
+ if (dev->urb_streaming == 0)
+ return;
+
+ if (ptype != PIPE_BULK) {
+ printk(KERN_ERR "%s() Unsupported URB type %d\n",
+ __func__, ptype);
+ return;
+ }
+
+ /* Feed the transport payload into the kernel demux */
+ dvb_dmx_swfilter_packets(&dev->dvb.demux,
+ purb->transfer_buffer, purb->actual_length / 188);
+
+ /* Clean the buffer before we requeue */
+ memset(purb->transfer_buffer, 0, URB_BUFSIZE);
+
+ /* Requeue URB */
+ usb_submit_urb(purb, GFP_ATOMIC);
+}
+
+static int stop_urb_transfer(struct au0828_dev *dev)
+{
+ int i;
+
+ dprintk(2, "%s()\n", __func__);
+
+ for (i = 0; i < URB_COUNT; i++) {
+ usb_kill_urb(dev->urbs[i]);
+ kfree(dev->urbs[i]->transfer_buffer);
+ usb_free_urb(dev->urbs[i]);
+ }
+
+ dev->urb_streaming = 0;
+
+ return 0;
+}
+
+static int start_urb_transfer(struct au0828_dev *dev)
+{
+ struct urb *purb;
+ int i, ret = -ENOMEM;
+
+ dprintk(2, "%s()\n", __func__);
+
+ if (dev->urb_streaming) {
+ dprintk(2, "%s: bulk xfer already running!\n", __func__);
+ return 0;
+ }
+
+ for (i = 0; i < URB_COUNT; i++) {
+
+ dev->urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->urbs[i])
+ goto err;
+
+ purb = dev->urbs[i];
+
+ purb->transfer_buffer = kzalloc(URB_BUFSIZE, GFP_KERNEL);
+ if (!purb->transfer_buffer) {
+ usb_free_urb(purb);
+ dev->urbs[i] = NULL;
+ goto err;
+ }
+
+ purb->status = -EINPROGRESS;
+ usb_fill_bulk_urb(purb,
+ dev->usbdev,
+ usb_rcvbulkpipe(dev->usbdev,
+ _AU0828_BULKPIPE),
+ purb->transfer_buffer,
+ URB_BUFSIZE,
+ urb_completion,
+ dev);
+
+ }
+
+ for (i = 0; i < URB_COUNT; i++) {
+ ret = usb_submit_urb(dev->urbs[i], GFP_ATOMIC);
+ if (ret != 0) {
+ stop_urb_transfer(dev);
+ printk(KERN_ERR "%s: failed urb submission, "
+ "err = %d\n", __func__, ret);
+ return ret;
+ }
+ }
+
+ dev->urb_streaming = 1;
+ ret = 0;
+
+err:
+ return ret;
+}
+
+static int au0828_dvb_start_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct au0828_dev *dev = (struct au0828_dev *) demux->priv;
+ struct au0828_dvb *dvb = &dev->dvb;
+ int ret = 0;
+
+ dprintk(1, "%s()\n", __func__);
+
+ if (!demux->dmx.frontend)
+ return -EINVAL;
+
+ if (dvb) {
+ mutex_lock(&dvb->lock);
+ if (dvb->feeding++ == 0) {
+ /* Start transport */
+ au0828_write(dev, 0x608, 0x90);
+ au0828_write(dev, 0x609, 0x72);
+ au0828_write(dev, 0x60a, 0x71);
+ au0828_write(dev, 0x60b, 0x01);
+ ret = start_urb_transfer(dev);
+ }
+ mutex_unlock(&dvb->lock);
+ }
+
+ return ret;
+}
+
+static int au0828_dvb_stop_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct au0828_dev *dev = (struct au0828_dev *) demux->priv;
+ struct au0828_dvb *dvb = &dev->dvb;
+ int ret = 0;
+
+ dprintk(1, "%s()\n", __func__);
+
+ if (dvb) {
+ mutex_lock(&dvb->lock);
+ if (--dvb->feeding == 0) {
+ /* Stop transport */
+ au0828_write(dev, 0x608, 0x00);
+ au0828_write(dev, 0x609, 0x00);
+ au0828_write(dev, 0x60a, 0x00);
+ au0828_write(dev, 0x60b, 0x00);
+ ret = stop_urb_transfer(dev);
+ }
+ mutex_unlock(&dvb->lock);
+ }
+
+ return ret;
+}
+
+static int dvb_register(struct au0828_dev *dev)
+{
+ struct au0828_dvb *dvb = &dev->dvb;
+ int result;
+
+ dprintk(1, "%s()\n", __func__);
+
+ /* register adapter */
+ result = dvb_register_adapter(&dvb->adapter, DRIVER_NAME, THIS_MODULE,
+ &dev->usbdev->dev, adapter_nr);
+ if (result < 0) {
+ printk(KERN_ERR "%s: dvb_register_adapter failed "
+ "(errno = %d)\n", DRIVER_NAME, result);
+ goto fail_adapter;
+ }
+ dvb->adapter.priv = dev;
+
+ /* register frontend */
+ result = dvb_register_frontend(&dvb->adapter, dvb->frontend);
+ if (result < 0) {
+ printk(KERN_ERR "%s: dvb_register_frontend failed "
+ "(errno = %d)\n", DRIVER_NAME, result);
+ goto fail_frontend;
+ }
+
+ /* register demux stuff */
+ dvb->demux.dmx.capabilities =
+ DMX_TS_FILTERING | DMX_SECTION_FILTERING |
+ DMX_MEMORY_BASED_FILTERING;
+ dvb->demux.priv = dev;
+ dvb->demux.filternum = 256;
+ dvb->demux.feednum = 256;
+ dvb->demux.start_feed = au0828_dvb_start_feed;
+ dvb->demux.stop_feed = au0828_dvb_stop_feed;
+ result = dvb_dmx_init(&dvb->demux);
+ if (result < 0) {
+ printk(KERN_ERR "%s: dvb_dmx_init failed (errno = %d)\n",
+ DRIVER_NAME, result);
+ goto fail_dmx;
+ }
+
+ dvb->dmxdev.filternum = 256;
+ dvb->dmxdev.demux = &dvb->demux.dmx;
+ dvb->dmxdev.capabilities = 0;
+ result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter);
+ if (result < 0) {
+ printk(KERN_ERR "%s: dvb_dmxdev_init failed (errno = %d)\n",
+ DRIVER_NAME, result);
+ goto fail_dmxdev;
+ }
+
+ dvb->fe_hw.source = DMX_FRONTEND_0;
+ result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+ if (result < 0) {
+ printk(KERN_ERR "%s: add_frontend failed "
+ "(DMX_FRONTEND_0, errno = %d)\n", DRIVER_NAME, result);
+ goto fail_fe_hw;
+ }
+
+ dvb->fe_mem.source = DMX_MEMORY_FE;
+ result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+ if (result < 0) {
+ printk(KERN_ERR "%s: add_frontend failed "
+ "(DMX_MEMORY_FE, errno = %d)\n", DRIVER_NAME, result);
+ goto fail_fe_mem;
+ }
+
+ result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+ if (result < 0) {
+ printk(KERN_ERR "%s: connect_frontend failed (errno = %d)\n",
+ DRIVER_NAME, result);
+ goto fail_fe_conn;
+ }
+
+ /* register network adapter */
+ dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx);
+ return 0;
+
+fail_fe_conn:
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+fail_fe_mem:
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+fail_fe_hw:
+ dvb_dmxdev_release(&dvb->dmxdev);
+fail_dmxdev:
+ dvb_dmx_release(&dvb->demux);
+fail_dmx:
+ dvb_unregister_frontend(dvb->frontend);
+fail_frontend:
+ dvb_frontend_detach(dvb->frontend);
+ dvb_unregister_adapter(&dvb->adapter);
+fail_adapter:
+ return result;
+}
+
+void au0828_dvb_unregister(struct au0828_dev *dev)
+{
+ struct au0828_dvb *dvb = &dev->dvb;
+
+ dprintk(1, "%s()\n", __func__);
+
+ if (dvb->frontend == NULL)
+ return;
+
+ dvb_net_release(&dvb->net);
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+ dvb_dmxdev_release(&dvb->dmxdev);
+ dvb_dmx_release(&dvb->demux);
+ dvb_unregister_frontend(dvb->frontend);
+ dvb_frontend_detach(dvb->frontend);
+ dvb_unregister_adapter(&dvb->adapter);
+}
+
+/* All the DVB attach calls go here, this function get's modified
+ * for each new card. No other function in this file needs
+ * to change.
+ */
+int au0828_dvb_register(struct au0828_dev *dev)
+{
+ struct au0828_dvb *dvb = &dev->dvb;
+ int ret;
+
+ dprintk(1, "%s()\n", __func__);
+
+ /* init frontend */
+ switch (dev->boardnr) {
+ case AU0828_BOARD_HAUPPAUGE_HVR850:
+ case AU0828_BOARD_HAUPPAUGE_HVR950Q:
+ dvb->frontend = dvb_attach(au8522_attach,
+ &hauppauge_hvr950q_config,
+ &dev->i2c_adap);
+ if (dvb->frontend != NULL)
+ dvb_attach(xc5000_attach, dvb->frontend, &dev->i2c_adap,
+ &hauppauge_hvr950q_tunerconfig);
+ break;
+ case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
+ dvb->frontend = dvb_attach(au8522_attach,
+ &hauppauge_hvr950q_config,
+ &dev->i2c_adap);
+ if (dvb->frontend != NULL)
+ dvb_attach(mxl5007t_attach, dvb->frontend,
+ &dev->i2c_adap, 0x60,
+ &mxl5007t_hvr950q_config);
+ break;
+ case AU0828_BOARD_HAUPPAUGE_WOODBURY:
+ dvb->frontend = dvb_attach(au8522_attach,
+ &hauppauge_woodbury_config,
+ &dev->i2c_adap);
+ if (dvb->frontend != NULL)
+ dvb_attach(tda18271_attach, dvb->frontend,
+ 0x60, &dev->i2c_adap,
+ &hauppauge_woodbury_tunerconfig);
+ break;
+ case AU0828_BOARD_DVICO_FUSIONHDTV7:
+ dvb->frontend = dvb_attach(au8522_attach,
+ &fusionhdtv7usb_config,
+ &dev->i2c_adap);
+ if (dvb->frontend != NULL) {
+ dvb_attach(xc5000_attach, dvb->frontend,
+ &dev->i2c_adap,
+ &hauppauge_hvr950q_tunerconfig);
+ }
+ break;
+ default:
+ printk(KERN_WARNING "The frontend of your DVB/ATSC card "
+ "isn't supported yet\n");
+ break;
+ }
+ if (NULL == dvb->frontend) {
+ printk(KERN_ERR "%s() Frontend initialization failed\n",
+ __func__);
+ return -1;
+ }
+ /* define general-purpose callback pointer */
+ dvb->frontend->callback = au0828_tuner_callback;
+
+ /* register everything */
+ ret = dvb_register(dev);
+ if (ret < 0) {
+ if (dvb->frontend->ops.release)
+ dvb->frontend->ops.release(dvb->frontend);
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/drivers/media/video/au0828/au0828-i2c.c b/drivers/media/video/au0828/au0828-i2c.c
new file mode 100644
index 00000000..05c299fa
--- /dev/null
+++ b/drivers/media/video/au0828/au0828-i2c.c
@@ -0,0 +1,390 @@
+/*
+ * Driver for the Auvitek AU0828 USB bridge
+ *
+ * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include "au0828.h"
+
+#include <media/v4l2-common.h>
+
+static int i2c_scan;
+module_param(i2c_scan, int, 0444);
+MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time");
+
+#define I2C_WAIT_DELAY 512
+#define I2C_WAIT_RETRY 64
+
+static inline int i2c_slave_did_write_ack(struct i2c_adapter *i2c_adap)
+{
+ struct au0828_dev *dev = i2c_adap->algo_data;
+ return au0828_read(dev, AU0828_I2C_STATUS_201) &
+ AU0828_I2C_STATUS_NO_WRITE_ACK ? 0 : 1;
+}
+
+static inline int i2c_slave_did_read_ack(struct i2c_adapter *i2c_adap)
+{
+ struct au0828_dev *dev = i2c_adap->algo_data;
+ return au0828_read(dev, AU0828_I2C_STATUS_201) &
+ AU0828_I2C_STATUS_NO_READ_ACK ? 0 : 1;
+}
+
+static int i2c_wait_read_ack(struct i2c_adapter *i2c_adap)
+{
+ int count;
+
+ for (count = 0; count < I2C_WAIT_RETRY; count++) {
+ if (!i2c_slave_did_read_ack(i2c_adap))
+ break;
+ udelay(I2C_WAIT_DELAY);
+ }
+
+ if (I2C_WAIT_RETRY == count)
+ return 0;
+
+ return 1;
+}
+
+static inline int i2c_is_read_busy(struct i2c_adapter *i2c_adap)
+{
+ struct au0828_dev *dev = i2c_adap->algo_data;
+ return au0828_read(dev, AU0828_I2C_STATUS_201) &
+ AU0828_I2C_STATUS_READ_DONE ? 0 : 1;
+}
+
+static int i2c_wait_read_done(struct i2c_adapter *i2c_adap)
+{
+ int count;
+
+ for (count = 0; count < I2C_WAIT_RETRY; count++) {
+ if (!i2c_is_read_busy(i2c_adap))
+ break;
+ udelay(I2C_WAIT_DELAY);
+ }
+
+ if (I2C_WAIT_RETRY == count)
+ return 0;
+
+ return 1;
+}
+
+static inline int i2c_is_write_done(struct i2c_adapter *i2c_adap)
+{
+ struct au0828_dev *dev = i2c_adap->algo_data;
+ return au0828_read(dev, AU0828_I2C_STATUS_201) &
+ AU0828_I2C_STATUS_WRITE_DONE ? 1 : 0;
+}
+
+static int i2c_wait_write_done(struct i2c_adapter *i2c_adap)
+{
+ int count;
+
+ for (count = 0; count < I2C_WAIT_RETRY; count++) {
+ if (i2c_is_write_done(i2c_adap))
+ break;
+ udelay(I2C_WAIT_DELAY);
+ }
+
+ if (I2C_WAIT_RETRY == count)
+ return 0;
+
+ return 1;
+}
+
+static inline int i2c_is_busy(struct i2c_adapter *i2c_adap)
+{
+ struct au0828_dev *dev = i2c_adap->algo_data;
+ return au0828_read(dev, AU0828_I2C_STATUS_201) &
+ AU0828_I2C_STATUS_BUSY ? 1 : 0;
+}
+
+static int i2c_wait_done(struct i2c_adapter *i2c_adap)
+{
+ int count;
+
+ for (count = 0; count < I2C_WAIT_RETRY; count++) {
+ if (!i2c_is_busy(i2c_adap))
+ break;
+ udelay(I2C_WAIT_DELAY);
+ }
+
+ if (I2C_WAIT_RETRY == count)
+ return 0;
+
+ return 1;
+}
+
+/* FIXME: Implement join handling correctly */
+static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
+ const struct i2c_msg *msg, int joined_rlen)
+{
+ int i, strobe = 0;
+ struct au0828_dev *dev = i2c_adap->algo_data;
+
+ dprintk(4, "%s()\n", __func__);
+
+ au0828_write(dev, AU0828_I2C_MULTIBYTE_MODE_2FF, 0x01);
+
+ /* Set the I2C clock */
+ au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202,
+ dev->board.i2c_clk_divider);
+
+ /* Hardware needs 8 bit addresses */
+ au0828_write(dev, AU0828_I2C_DEST_ADDR_203, msg->addr << 1);
+
+ dprintk(4, "SEND: %02x\n", msg->addr);
+
+ /* Deal with i2c_scan */
+ if (msg->len == 0) {
+ /* The analog tuner detection code makes use of the SMBUS_QUICK
+ message (which involves a zero length i2c write). To avoid
+ checking the status register when we didn't strobe out any
+ actual bytes to the bus, just do a read check. This is
+ consistent with how I saw i2c device checking done in the
+ USB trace of the Windows driver */
+ au0828_write(dev, AU0828_I2C_TRIGGER_200,
+ AU0828_I2C_TRIGGER_READ);
+
+ if (!i2c_wait_done(i2c_adap))
+ return -EIO;
+
+ if (i2c_wait_read_ack(i2c_adap))
+ return -EIO;
+
+ return 0;
+ }
+
+ for (i = 0; i < msg->len;) {
+
+ dprintk(4, " %02x\n", msg->buf[i]);
+
+ au0828_write(dev, AU0828_I2C_WRITE_FIFO_205, msg->buf[i]);
+
+ strobe++;
+ i++;
+
+ if ((strobe >= 4) || (i >= msg->len)) {
+
+ /* Strobe the byte into the bus */
+ if (i < msg->len)
+ au0828_write(dev, AU0828_I2C_TRIGGER_200,
+ AU0828_I2C_TRIGGER_WRITE |
+ AU0828_I2C_TRIGGER_HOLD);
+ else
+ au0828_write(dev, AU0828_I2C_TRIGGER_200,
+ AU0828_I2C_TRIGGER_WRITE);
+
+ /* Reset strobe trigger */
+ strobe = 0;
+
+ if (!i2c_wait_write_done(i2c_adap))
+ return -EIO;
+
+ }
+
+ }
+ if (!i2c_wait_done(i2c_adap))
+ return -EIO;
+
+ dprintk(4, "\n");
+
+ return msg->len;
+}
+
+/* FIXME: Implement join handling correctly */
+static int i2c_readbytes(struct i2c_adapter *i2c_adap,
+ const struct i2c_msg *msg, int joined)
+{
+ struct au0828_dev *dev = i2c_adap->algo_data;
+ int i;
+
+ dprintk(4, "%s()\n", __func__);
+
+ au0828_write(dev, AU0828_I2C_MULTIBYTE_MODE_2FF, 0x01);
+
+ /* Set the I2C clock */
+ au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202,
+ dev->board.i2c_clk_divider);
+
+ /* Hardware needs 8 bit addresses */
+ au0828_write(dev, AU0828_I2C_DEST_ADDR_203, msg->addr << 1);
+
+ dprintk(4, " RECV:\n");
+
+ /* Deal with i2c_scan */
+ if (msg->len == 0) {
+ au0828_write(dev, AU0828_I2C_TRIGGER_200,
+ AU0828_I2C_TRIGGER_READ);
+
+ if (i2c_wait_read_ack(i2c_adap))
+ return -EIO;
+ return 0;
+ }
+
+ for (i = 0; i < msg->len;) {
+
+ i++;
+
+ if (i < msg->len)
+ au0828_write(dev, AU0828_I2C_TRIGGER_200,
+ AU0828_I2C_TRIGGER_READ |
+ AU0828_I2C_TRIGGER_HOLD);
+ else
+ au0828_write(dev, AU0828_I2C_TRIGGER_200,
+ AU0828_I2C_TRIGGER_READ);
+
+ if (!i2c_wait_read_done(i2c_adap))
+ return -EIO;
+
+ msg->buf[i-1] = au0828_read(dev, AU0828_I2C_READ_FIFO_209) &
+ 0xff;
+
+ dprintk(4, " %02x\n", msg->buf[i-1]);
+ }
+ if (!i2c_wait_done(i2c_adap))
+ return -EIO;
+
+ dprintk(4, "\n");
+
+ return msg->len;
+}
+
+static int i2c_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg *msgs, int num)
+{
+ int i, retval = 0;
+
+ dprintk(4, "%s(num = %d)\n", __func__, num);
+
+ for (i = 0; i < num; i++) {
+ dprintk(4, "%s(num = %d) addr = 0x%02x len = 0x%x\n",
+ __func__, num, msgs[i].addr, msgs[i].len);
+ if (msgs[i].flags & I2C_M_RD) {
+ /* read */
+ retval = i2c_readbytes(i2c_adap, &msgs[i], 0);
+ } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) &&
+ msgs[i].addr == msgs[i + 1].addr) {
+ /* write then read from same address */
+ retval = i2c_sendbytes(i2c_adap, &msgs[i],
+ msgs[i + 1].len);
+ if (retval < 0)
+ goto err;
+ i++;
+ retval = i2c_readbytes(i2c_adap, &msgs[i], 1);
+ } else {
+ /* write */
+ retval = i2c_sendbytes(i2c_adap, &msgs[i], 0);
+ }
+ if (retval < 0)
+ goto err;
+ }
+ return num;
+
+err:
+ return retval;
+}
+
+static u32 au0828_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm au0828_i2c_algo_template = {
+ .master_xfer = i2c_xfer,
+ .functionality = au0828_functionality,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static struct i2c_adapter au0828_i2c_adap_template = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .algo = &au0828_i2c_algo_template,
+};
+
+static struct i2c_client au0828_i2c_client_template = {
+ .name = "au0828 internal",
+};
+
+static char *i2c_devs[128] = {
+ [0x8e >> 1] = "au8522",
+ [0xa0 >> 1] = "eeprom",
+ [0xc2 >> 1] = "tuner/xc5000",
+};
+
+static void do_i2c_scan(char *name, struct i2c_client *c)
+{
+ unsigned char buf;
+ int i, rc;
+
+ for (i = 0; i < 128; i++) {
+ c->addr = i;
+ rc = i2c_master_recv(c, &buf, 0);
+ if (rc < 0)
+ continue;
+ printk(KERN_INFO "%s: i2c scan: found device @ 0x%x [%s]\n",
+ name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
+ }
+}
+
+/* init + register i2c adapter */
+int au0828_i2c_register(struct au0828_dev *dev)
+{
+ dprintk(1, "%s()\n", __func__);
+
+ memcpy(&dev->i2c_adap, &au0828_i2c_adap_template,
+ sizeof(dev->i2c_adap));
+ memcpy(&dev->i2c_algo, &au0828_i2c_algo_template,
+ sizeof(dev->i2c_algo));
+ memcpy(&dev->i2c_client, &au0828_i2c_client_template,
+ sizeof(dev->i2c_client));
+
+ dev->i2c_adap.dev.parent = &dev->usbdev->dev;
+
+ strlcpy(dev->i2c_adap.name, DRIVER_NAME,
+ sizeof(dev->i2c_adap.name));
+
+ dev->i2c_adap.algo = &dev->i2c_algo;
+ dev->i2c_adap.algo_data = dev;
+ i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev);
+ i2c_add_adapter(&dev->i2c_adap);
+
+ dev->i2c_client.adapter = &dev->i2c_adap;
+
+ if (0 == dev->i2c_rc) {
+ printk(KERN_INFO "%s: i2c bus registered\n", DRIVER_NAME);
+ if (i2c_scan)
+ do_i2c_scan(DRIVER_NAME, &dev->i2c_client);
+ } else
+ printk(KERN_INFO "%s: i2c bus register FAILED\n", DRIVER_NAME);
+
+ return dev->i2c_rc;
+}
+
+int au0828_i2c_unregister(struct au0828_dev *dev)
+{
+ i2c_del_adapter(&dev->i2c_adap);
+ return 0;
+}
+
diff --git a/drivers/media/video/au0828/au0828-reg.h b/drivers/media/video/au0828/au0828-reg.h
new file mode 100644
index 00000000..c39f3d2b
--- /dev/null
+++ b/drivers/media/video/au0828/au0828-reg.h
@@ -0,0 +1,65 @@
+/*
+ * Driver for the Auvitek USB bridge
+ *
+ * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* We'll start to rename these registers once we have a better
+ * understanding of their meaning.
+ */
+#define REG_000 0x000
+#define REG_001 0x001
+#define REG_002 0x002
+#define REG_003 0x003
+
+#define AU0828_SENSORCTRL_100 0x100
+#define AU0828_SENSORCTRL_VBI_103 0x103
+
+/* I2C registers */
+#define AU0828_I2C_TRIGGER_200 0x200
+#define AU0828_I2C_STATUS_201 0x201
+#define AU0828_I2C_CLK_DIVIDER_202 0x202
+#define AU0828_I2C_DEST_ADDR_203 0x203
+#define AU0828_I2C_WRITE_FIFO_205 0x205
+#define AU0828_I2C_READ_FIFO_209 0x209
+#define AU0828_I2C_MULTIBYTE_MODE_2FF 0x2ff
+
+/* Audio registers */
+#define AU0828_AUDIOCTRL_50C 0x50C
+
+#define REG_600 0x600
+
+/*********************************************************************/
+/* Here are constants for values associated with the above registers */
+
+/* I2C Trigger (Reg 0x200) */
+#define AU0828_I2C_TRIGGER_WRITE 0x01
+#define AU0828_I2C_TRIGGER_READ 0x20
+#define AU0828_I2C_TRIGGER_HOLD 0x40
+
+/* I2C Status (Reg 0x201) */
+#define AU0828_I2C_STATUS_READ_DONE 0x01
+#define AU0828_I2C_STATUS_NO_READ_ACK 0x02
+#define AU0828_I2C_STATUS_WRITE_DONE 0x04
+#define AU0828_I2C_STATUS_NO_WRITE_ACK 0x08
+#define AU0828_I2C_STATUS_BUSY 0x10
+
+/* I2C Clock Divider (Reg 0x202) */
+#define AU0828_I2C_CLK_250KHZ 0x07
+#define AU0828_I2C_CLK_100KHZ 0x14
+#define AU0828_I2C_CLK_30KHZ 0x40
diff --git a/drivers/media/video/au0828/au0828-vbi.c b/drivers/media/video/au0828/au0828-vbi.c
new file mode 100644
index 00000000..63f59307
--- /dev/null
+++ b/drivers/media/video/au0828/au0828-vbi.c
@@ -0,0 +1,138 @@
+/*
+ au0828-vbi.c - VBI driver for au0828
+
+ Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
+
+ This work was sponsored by GetWellNetwork Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+
+#include "au0828.h"
+
+static unsigned int vbibufs = 5;
+module_param(vbibufs, int, 0644);
+MODULE_PARM_DESC(vbibufs, "number of vbi buffers, range 2-32");
+
+/* ------------------------------------------------------------------ */
+
+static void
+free_buffer(struct videobuf_queue *vq, struct au0828_buffer *buf)
+{
+ struct au0828_fh *fh = vq->priv_data;
+ struct au0828_dev *dev = fh->dev;
+ unsigned long flags = 0;
+ if (in_interrupt())
+ BUG();
+
+ /* We used to wait for the buffer to finish here, but this didn't work
+ because, as we were keeping the state as VIDEOBUF_QUEUED,
+ videobuf_queue_cancel marked it as finished for us.
+ (Also, it could wedge forever if the hardware was misconfigured.)
+
+ This should be safe; by the time we get here, the buffer isn't
+ queued anymore. If we ever start marking the buffers as
+ VIDEOBUF_ACTIVE, it won't be, though.
+ */
+ spin_lock_irqsave(&dev->slock, flags);
+ if (dev->isoc_ctl.vbi_buf == buf)
+ dev->isoc_ctl.vbi_buf = NULL;
+ spin_unlock_irqrestore(&dev->slock, flags);
+
+ videobuf_vmalloc_free(&buf->vb);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int
+vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+ struct au0828_fh *fh = q->priv_data;
+ struct au0828_dev *dev = fh->dev;
+
+ *size = dev->vbi_width * dev->vbi_height * 2;
+
+ if (0 == *count)
+ *count = vbibufs;
+ if (*count < 2)
+ *count = 2;
+ if (*count > 32)
+ *count = 32;
+ return 0;
+}
+
+static int
+vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct au0828_fh *fh = q->priv_data;
+ struct au0828_dev *dev = fh->dev;
+ struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb);
+ int rc = 0;
+
+ buf->vb.size = dev->vbi_width * dev->vbi_height * 2;
+
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+
+ buf->vb.width = dev->vbi_width;
+ buf->vb.height = dev->vbi_height;
+ buf->vb.field = field;
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ rc = videobuf_iolock(q, &buf->vb, NULL);
+ if (rc < 0)
+ goto fail;
+ }
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+fail:
+ free_buffer(q, buf);
+ return rc;
+}
+
+static void
+vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct au0828_buffer *buf = container_of(vb,
+ struct au0828_buffer,
+ vb);
+ struct au0828_fh *fh = vq->priv_data;
+ struct au0828_dev *dev = fh->dev;
+ struct au0828_dmaqueue *vbiq = &dev->vbiq;
+
+ buf->vb.state = VIDEOBUF_QUEUED;
+ list_add_tail(&buf->vb.queue, &vbiq->active);
+}
+
+static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb);
+ free_buffer(q, buf);
+}
+
+struct videobuf_queue_ops au0828_vbi_qops = {
+ .buf_setup = vbi_setup,
+ .buf_prepare = vbi_prepare,
+ .buf_queue = vbi_queue,
+ .buf_release = vbi_release,
+};
diff --git a/drivers/media/video/au0828/au0828-video.c b/drivers/media/video/au0828/au0828-video.c
new file mode 100644
index 00000000..0b3e481f
--- /dev/null
+++ b/drivers/media/video/au0828/au0828-video.c
@@ -0,0 +1,1993 @@
+/*
+ * Auvitek AU0828 USB Bridge (Analog video support)
+ *
+ * Copyright (C) 2009 Devin Heitmueller <dheitmueller@linuxtv.org>
+ * Copyright (C) 2005-2008 Auvitek International, Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * As published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/* Developer Notes:
+ *
+ * VBI support is not yet working
+ * The hardware scaler supported is unimplemented
+ * AC97 audio support is unimplemented (only i2s audio mode)
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/suspend.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/tuner.h>
+#include "au0828.h"
+#include "au0828-reg.h"
+
+static DEFINE_MUTEX(au0828_sysfs_lock);
+
+/* ------------------------------------------------------------------
+ Videobuf operations
+ ------------------------------------------------------------------*/
+
+static unsigned int isoc_debug;
+module_param(isoc_debug, int, 0644);
+MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]");
+
+#define au0828_isocdbg(fmt, arg...) \
+do {\
+ if (isoc_debug) { \
+ printk(KERN_INFO "au0828 %s :"fmt, \
+ __func__ , ##arg); \
+ } \
+ } while (0)
+
+static inline void print_err_status(struct au0828_dev *dev,
+ int packet, int status)
+{
+ char *errmsg = "Unknown";
+
+ switch (status) {
+ case -ENOENT:
+ errmsg = "unlinked synchronuously";
+ break;
+ case -ECONNRESET:
+ errmsg = "unlinked asynchronuously";
+ break;
+ case -ENOSR:
+ errmsg = "Buffer error (overrun)";
+ break;
+ case -EPIPE:
+ errmsg = "Stalled (device not responding)";
+ break;
+ case -EOVERFLOW:
+ errmsg = "Babble (bad cable?)";
+ break;
+ case -EPROTO:
+ errmsg = "Bit-stuff error (bad cable?)";
+ break;
+ case -EILSEQ:
+ errmsg = "CRC/Timeout (could be anything)";
+ break;
+ case -ETIME:
+ errmsg = "Device does not respond";
+ break;
+ }
+ if (packet < 0) {
+ au0828_isocdbg("URB status %d [%s].\n", status, errmsg);
+ } else {
+ au0828_isocdbg("URB packet %d, status %d [%s].\n",
+ packet, status, errmsg);
+ }
+}
+
+static int check_dev(struct au0828_dev *dev)
+{
+ if (dev->dev_state & DEV_DISCONNECTED) {
+ printk(KERN_INFO "v4l2 ioctl: device not present\n");
+ return -ENODEV;
+ }
+
+ if (dev->dev_state & DEV_MISCONFIGURED) {
+ printk(KERN_INFO "v4l2 ioctl: device is misconfigured; "
+ "close and open it again\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+/*
+ * IRQ callback, called by URB callback
+ */
+static void au0828_irq_callback(struct urb *urb)
+{
+ struct au0828_dmaqueue *dma_q = urb->context;
+ struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vidq);
+ unsigned long flags = 0;
+ int rc, i;
+
+ switch (urb->status) {
+ case 0: /* success */
+ case -ETIMEDOUT: /* NAK */
+ break;
+ case -ECONNRESET: /* kill */
+ case -ENOENT:
+ case -ESHUTDOWN:
+ au0828_isocdbg("au0828_irq_callback called: status kill\n");
+ return;
+ default: /* unknown error */
+ au0828_isocdbg("urb completition error %d.\n", urb->status);
+ break;
+ }
+
+ /* Copy data from URB */
+ spin_lock_irqsave(&dev->slock, flags);
+ rc = dev->isoc_ctl.isoc_copy(dev, urb);
+ spin_unlock_irqrestore(&dev->slock, flags);
+
+ /* Reset urb buffers */
+ for (i = 0; i < urb->number_of_packets; i++) {
+ urb->iso_frame_desc[i].status = 0;
+ urb->iso_frame_desc[i].actual_length = 0;
+ }
+ urb->status = 0;
+
+ urb->status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (urb->status) {
+ au0828_isocdbg("urb resubmit failed (error=%i)\n",
+ urb->status);
+ }
+}
+
+/*
+ * Stop and Deallocate URBs
+ */
+void au0828_uninit_isoc(struct au0828_dev *dev)
+{
+ struct urb *urb;
+ int i;
+
+ au0828_isocdbg("au0828: called au0828_uninit_isoc\n");
+
+ dev->isoc_ctl.nfields = -1;
+ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
+ urb = dev->isoc_ctl.urb[i];
+ if (urb) {
+ if (!irqs_disabled())
+ usb_kill_urb(urb);
+ else
+ usb_unlink_urb(urb);
+
+ if (dev->isoc_ctl.transfer_buffer[i]) {
+ usb_free_coherent(dev->usbdev,
+ urb->transfer_buffer_length,
+ dev->isoc_ctl.transfer_buffer[i],
+ urb->transfer_dma);
+ }
+ usb_free_urb(urb);
+ dev->isoc_ctl.urb[i] = NULL;
+ }
+ dev->isoc_ctl.transfer_buffer[i] = NULL;
+ }
+
+ kfree(dev->isoc_ctl.urb);
+ kfree(dev->isoc_ctl.transfer_buffer);
+
+ dev->isoc_ctl.urb = NULL;
+ dev->isoc_ctl.transfer_buffer = NULL;
+ dev->isoc_ctl.num_bufs = 0;
+}
+
+/*
+ * Allocate URBs and start IRQ
+ */
+int au0828_init_isoc(struct au0828_dev *dev, int max_packets,
+ int num_bufs, int max_pkt_size,
+ int (*isoc_copy) (struct au0828_dev *dev, struct urb *urb))
+{
+ struct au0828_dmaqueue *dma_q = &dev->vidq;
+ int i;
+ int sb_size, pipe;
+ struct urb *urb;
+ int j, k;
+ int rc;
+
+ au0828_isocdbg("au0828: called au0828_prepare_isoc\n");
+
+ /* De-allocates all pending stuff */
+ au0828_uninit_isoc(dev);
+
+ dev->isoc_ctl.isoc_copy = isoc_copy;
+ dev->isoc_ctl.num_bufs = num_bufs;
+
+ dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL);
+ if (!dev->isoc_ctl.urb) {
+ au0828_isocdbg("cannot alloc memory for usb buffers\n");
+ return -ENOMEM;
+ }
+
+ dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs,
+ GFP_KERNEL);
+ if (!dev->isoc_ctl.transfer_buffer) {
+ au0828_isocdbg("cannot allocate memory for usb transfer\n");
+ kfree(dev->isoc_ctl.urb);
+ return -ENOMEM;
+ }
+
+ dev->isoc_ctl.max_pkt_size = max_pkt_size;
+ dev->isoc_ctl.buf = NULL;
+
+ sb_size = max_packets * dev->isoc_ctl.max_pkt_size;
+
+ /* allocate urbs and transfer buffers */
+ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
+ urb = usb_alloc_urb(max_packets, GFP_KERNEL);
+ if (!urb) {
+ au0828_isocdbg("cannot alloc isoc_ctl.urb %i\n", i);
+ au0828_uninit_isoc(dev);
+ return -ENOMEM;
+ }
+ dev->isoc_ctl.urb[i] = urb;
+
+ dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->usbdev,
+ sb_size, GFP_KERNEL, &urb->transfer_dma);
+ if (!dev->isoc_ctl.transfer_buffer[i]) {
+ printk("unable to allocate %i bytes for transfer"
+ " buffer %i%s\n",
+ sb_size, i,
+ in_interrupt() ? " while in int" : "");
+ au0828_uninit_isoc(dev);
+ return -ENOMEM;
+ }
+ memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size);
+
+ pipe = usb_rcvisocpipe(dev->usbdev,
+ dev->isoc_in_endpointaddr),
+
+ usb_fill_int_urb(urb, dev->usbdev, pipe,
+ dev->isoc_ctl.transfer_buffer[i], sb_size,
+ au0828_irq_callback, dma_q, 1);
+
+ urb->number_of_packets = max_packets;
+ urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+
+ k = 0;
+ for (j = 0; j < max_packets; j++) {
+ urb->iso_frame_desc[j].offset = k;
+ urb->iso_frame_desc[j].length =
+ dev->isoc_ctl.max_pkt_size;
+ k += dev->isoc_ctl.max_pkt_size;
+ }
+ }
+
+ init_waitqueue_head(&dma_q->wq);
+
+ /* submit urbs and enables IRQ */
+ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
+ rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC);
+ if (rc) {
+ au0828_isocdbg("submit of urb %i failed (error=%i)\n",
+ i, rc);
+ au0828_uninit_isoc(dev);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Announces that a buffer were filled and request the next
+ */
+static inline void buffer_filled(struct au0828_dev *dev,
+ struct au0828_dmaqueue *dma_q,
+ struct au0828_buffer *buf)
+{
+ /* Advice that buffer was filled */
+ au0828_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i);
+
+ buf->vb.state = VIDEOBUF_DONE;
+ buf->vb.field_count++;
+ do_gettimeofday(&buf->vb.ts);
+
+ dev->isoc_ctl.buf = NULL;
+
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+}
+
+static inline void vbi_buffer_filled(struct au0828_dev *dev,
+ struct au0828_dmaqueue *dma_q,
+ struct au0828_buffer *buf)
+{
+ /* Advice that buffer was filled */
+ au0828_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i);
+
+ buf->vb.state = VIDEOBUF_DONE;
+ buf->vb.field_count++;
+ do_gettimeofday(&buf->vb.ts);
+
+ dev->isoc_ctl.vbi_buf = NULL;
+
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+}
+
+/*
+ * Identify the buffer header type and properly handles
+ */
+static void au0828_copy_video(struct au0828_dev *dev,
+ struct au0828_dmaqueue *dma_q,
+ struct au0828_buffer *buf,
+ unsigned char *p,
+ unsigned char *outp, unsigned long len)
+{
+ void *fieldstart, *startwrite, *startread;
+ int linesdone, currlinedone, offset, lencopy, remain;
+ int bytesperline = dev->width << 1; /* Assumes 16-bit depth @@@@ */
+
+ if (len == 0)
+ return;
+
+ if (dma_q->pos + len > buf->vb.size)
+ len = buf->vb.size - dma_q->pos;
+
+ startread = p;
+ remain = len;
+
+ /* Interlaces frame */
+ if (buf->top_field)
+ fieldstart = outp;
+ else
+ fieldstart = outp + bytesperline;
+
+ linesdone = dma_q->pos / bytesperline;
+ currlinedone = dma_q->pos % bytesperline;
+ offset = linesdone * bytesperline * 2 + currlinedone;
+ startwrite = fieldstart + offset;
+ lencopy = bytesperline - currlinedone;
+ lencopy = lencopy > remain ? remain : lencopy;
+
+ if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) {
+ au0828_isocdbg("Overflow of %zi bytes past buffer end (1)\n",
+ ((char *)startwrite + lencopy) -
+ ((char *)outp + buf->vb.size));
+ remain = (char *)outp + buf->vb.size - (char *)startwrite;
+ lencopy = remain;
+ }
+ if (lencopy <= 0)
+ return;
+ memcpy(startwrite, startread, lencopy);
+
+ remain -= lencopy;
+
+ while (remain > 0) {
+ startwrite += lencopy + bytesperline;
+ startread += lencopy;
+ if (bytesperline > remain)
+ lencopy = remain;
+ else
+ lencopy = bytesperline;
+
+ if ((char *)startwrite + lencopy > (char *)outp +
+ buf->vb.size) {
+ au0828_isocdbg("Overflow %zi bytes past buf end (2)\n",
+ ((char *)startwrite + lencopy) -
+ ((char *)outp + buf->vb.size));
+ lencopy = remain = (char *)outp + buf->vb.size -
+ (char *)startwrite;
+ }
+ if (lencopy <= 0)
+ break;
+
+ memcpy(startwrite, startread, lencopy);
+
+ remain -= lencopy;
+ }
+
+ if (offset > 1440) {
+ /* We have enough data to check for greenscreen */
+ if (outp[0] < 0x60 && outp[1440] < 0x60)
+ dev->greenscreen_detected = 1;
+ }
+
+ dma_q->pos += len;
+}
+
+/*
+ * video-buf generic routine to get the next available buffer
+ */
+static inline void get_next_buf(struct au0828_dmaqueue *dma_q,
+ struct au0828_buffer **buf)
+{
+ struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vidq);
+
+ if (list_empty(&dma_q->active)) {
+ au0828_isocdbg("No active queue to serve\n");
+ dev->isoc_ctl.buf = NULL;
+ *buf = NULL;
+ return;
+ }
+
+ /* Get the next buffer */
+ *buf = list_entry(dma_q->active.next, struct au0828_buffer, vb.queue);
+ dev->isoc_ctl.buf = *buf;
+
+ return;
+}
+
+static void au0828_copy_vbi(struct au0828_dev *dev,
+ struct au0828_dmaqueue *dma_q,
+ struct au0828_buffer *buf,
+ unsigned char *p,
+ unsigned char *outp, unsigned long len)
+{
+ unsigned char *startwrite, *startread;
+ int bytesperline;
+ int i, j = 0;
+
+ if (dev == NULL) {
+ au0828_isocdbg("dev is null\n");
+ return;
+ }
+
+ if (dma_q == NULL) {
+ au0828_isocdbg("dma_q is null\n");
+ return;
+ }
+ if (buf == NULL)
+ return;
+ if (p == NULL) {
+ au0828_isocdbg("p is null\n");
+ return;
+ }
+ if (outp == NULL) {
+ au0828_isocdbg("outp is null\n");
+ return;
+ }
+
+ bytesperline = dev->vbi_width;
+
+ if (dma_q->pos + len > buf->vb.size)
+ len = buf->vb.size - dma_q->pos;
+
+ startread = p;
+ startwrite = outp + (dma_q->pos / 2);
+
+ /* Make sure the bottom field populates the second half of the frame */
+ if (buf->top_field == 0)
+ startwrite += bytesperline * dev->vbi_height;
+
+ for (i = 0; i < len; i += 2)
+ startwrite[j++] = startread[i+1];
+
+ dma_q->pos += len;
+}
+
+
+/*
+ * video-buf generic routine to get the next available VBI buffer
+ */
+static inline void vbi_get_next_buf(struct au0828_dmaqueue *dma_q,
+ struct au0828_buffer **buf)
+{
+ struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vbiq);
+ char *outp;
+
+ if (list_empty(&dma_q->active)) {
+ au0828_isocdbg("No active queue to serve\n");
+ dev->isoc_ctl.vbi_buf = NULL;
+ *buf = NULL;
+ return;
+ }
+
+ /* Get the next buffer */
+ *buf = list_entry(dma_q->active.next, struct au0828_buffer, vb.queue);
+ /* Cleans up buffer - Useful for testing for frame/URB loss */
+ outp = videobuf_to_vmalloc(&(*buf)->vb);
+ memset(outp, 0x00, (*buf)->vb.size);
+
+ dev->isoc_ctl.vbi_buf = *buf;
+
+ return;
+}
+
+/*
+ * Controls the isoc copy of each urb packet
+ */
+static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb)
+{
+ struct au0828_buffer *buf;
+ struct au0828_buffer *vbi_buf;
+ struct au0828_dmaqueue *dma_q = urb->context;
+ struct au0828_dmaqueue *vbi_dma_q = &dev->vbiq;
+ unsigned char *outp = NULL;
+ unsigned char *vbioutp = NULL;
+ int i, len = 0, rc = 1;
+ unsigned char *p;
+ unsigned char fbyte;
+ unsigned int vbi_field_size;
+ unsigned int remain, lencopy;
+
+ if (!dev)
+ return 0;
+
+ if ((dev->dev_state & DEV_DISCONNECTED) ||
+ (dev->dev_state & DEV_MISCONFIGURED))
+ return 0;
+
+ if (urb->status < 0) {
+ print_err_status(dev, -1, urb->status);
+ if (urb->status == -ENOENT)
+ return 0;
+ }
+
+ buf = dev->isoc_ctl.buf;
+ if (buf != NULL)
+ outp = videobuf_to_vmalloc(&buf->vb);
+
+ vbi_buf = dev->isoc_ctl.vbi_buf;
+ if (vbi_buf != NULL)
+ vbioutp = videobuf_to_vmalloc(&vbi_buf->vb);
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ int status = urb->iso_frame_desc[i].status;
+
+ if (status < 0) {
+ print_err_status(dev, i, status);
+ if (urb->iso_frame_desc[i].status != -EPROTO)
+ continue;
+ }
+
+ if (urb->iso_frame_desc[i].actual_length <= 0)
+ continue;
+
+ if (urb->iso_frame_desc[i].actual_length >
+ dev->max_pkt_size) {
+ au0828_isocdbg("packet bigger than packet size");
+ continue;
+ }
+
+ p = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+ fbyte = p[0];
+ len = urb->iso_frame_desc[i].actual_length - 4;
+ p += 4;
+
+ if (fbyte & 0x80) {
+ len -= 4;
+ p += 4;
+ au0828_isocdbg("Video frame %s\n",
+ (fbyte & 0x40) ? "odd" : "even");
+ if (fbyte & 0x40) {
+ /* VBI */
+ if (vbi_buf != NULL)
+ vbi_buffer_filled(dev,
+ vbi_dma_q,
+ vbi_buf);
+ vbi_get_next_buf(vbi_dma_q, &vbi_buf);
+ if (vbi_buf == NULL)
+ vbioutp = NULL;
+ else
+ vbioutp = videobuf_to_vmalloc(
+ &vbi_buf->vb);
+
+ /* Video */
+ if (buf != NULL)
+ buffer_filled(dev, dma_q, buf);
+ get_next_buf(dma_q, &buf);
+ if (buf == NULL)
+ outp = NULL;
+ else
+ outp = videobuf_to_vmalloc(&buf->vb);
+
+ /* As long as isoc traffic is arriving, keep
+ resetting the timer */
+ if (dev->vid_timeout_running)
+ mod_timer(&dev->vid_timeout,
+ jiffies + (HZ / 10));
+ if (dev->vbi_timeout_running)
+ mod_timer(&dev->vbi_timeout,
+ jiffies + (HZ / 10));
+ }
+
+ if (buf != NULL) {
+ if (fbyte & 0x40)
+ buf->top_field = 1;
+ else
+ buf->top_field = 0;
+ }
+
+ if (vbi_buf != NULL) {
+ if (fbyte & 0x40)
+ vbi_buf->top_field = 1;
+ else
+ vbi_buf->top_field = 0;
+ }
+
+ dev->vbi_read = 0;
+ vbi_dma_q->pos = 0;
+ dma_q->pos = 0;
+ }
+
+ vbi_field_size = dev->vbi_width * dev->vbi_height * 2;
+ if (dev->vbi_read < vbi_field_size) {
+ remain = vbi_field_size - dev->vbi_read;
+ if (len < remain)
+ lencopy = len;
+ else
+ lencopy = remain;
+
+ if (vbi_buf != NULL)
+ au0828_copy_vbi(dev, vbi_dma_q, vbi_buf, p,
+ vbioutp, len);
+
+ len -= lencopy;
+ p += lencopy;
+ dev->vbi_read += lencopy;
+ }
+
+ if (dev->vbi_read >= vbi_field_size && buf != NULL)
+ au0828_copy_video(dev, dma_q, buf, p, outp, len);
+ }
+ return rc;
+}
+
+static int
+buffer_setup(struct videobuf_queue *vq, unsigned int *count,
+ unsigned int *size)
+{
+ struct au0828_fh *fh = vq->priv_data;
+ *size = (fh->dev->width * fh->dev->height * 16 + 7) >> 3;
+
+ if (0 == *count)
+ *count = AU0828_DEF_BUF;
+
+ if (*count < AU0828_MIN_BUF)
+ *count = AU0828_MIN_BUF;
+ return 0;
+}
+
+/* This is called *without* dev->slock held; please keep it that way */
+static void free_buffer(struct videobuf_queue *vq, struct au0828_buffer *buf)
+{
+ struct au0828_fh *fh = vq->priv_data;
+ struct au0828_dev *dev = fh->dev;
+ unsigned long flags = 0;
+ if (in_interrupt())
+ BUG();
+
+ /* We used to wait for the buffer to finish here, but this didn't work
+ because, as we were keeping the state as VIDEOBUF_QUEUED,
+ videobuf_queue_cancel marked it as finished for us.
+ (Also, it could wedge forever if the hardware was misconfigured.)
+
+ This should be safe; by the time we get here, the buffer isn't
+ queued anymore. If we ever start marking the buffers as
+ VIDEOBUF_ACTIVE, it won't be, though.
+ */
+ spin_lock_irqsave(&dev->slock, flags);
+ if (dev->isoc_ctl.buf == buf)
+ dev->isoc_ctl.buf = NULL;
+ spin_unlock_irqrestore(&dev->slock, flags);
+
+ videobuf_vmalloc_free(&buf->vb);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int
+buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct au0828_fh *fh = vq->priv_data;
+ struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb);
+ struct au0828_dev *dev = fh->dev;
+ int rc = 0, urb_init = 0;
+
+ buf->vb.size = (fh->dev->width * fh->dev->height * 16 + 7) >> 3;
+
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+
+ buf->vb.width = dev->width;
+ buf->vb.height = dev->height;
+ buf->vb.field = field;
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ rc = videobuf_iolock(vq, &buf->vb, NULL);
+ if (rc < 0) {
+ printk(KERN_INFO "videobuf_iolock failed\n");
+ goto fail;
+ }
+ }
+
+ if (!dev->isoc_ctl.num_bufs)
+ urb_init = 1;
+
+ if (urb_init) {
+ rc = au0828_init_isoc(dev, AU0828_ISO_PACKETS_PER_URB,
+ AU0828_MAX_ISO_BUFS, dev->max_pkt_size,
+ au0828_isoc_copy);
+ if (rc < 0) {
+ printk(KERN_INFO "au0828_init_isoc failed\n");
+ goto fail;
+ }
+ }
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+fail:
+ free_buffer(vq, buf);
+ return rc;
+}
+
+static void
+buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct au0828_buffer *buf = container_of(vb,
+ struct au0828_buffer,
+ vb);
+ struct au0828_fh *fh = vq->priv_data;
+ struct au0828_dev *dev = fh->dev;
+ struct au0828_dmaqueue *vidq = &dev->vidq;
+
+ buf->vb.state = VIDEOBUF_QUEUED;
+ list_add_tail(&buf->vb.queue, &vidq->active);
+}
+
+static void buffer_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct au0828_buffer *buf = container_of(vb,
+ struct au0828_buffer,
+ vb);
+
+ free_buffer(vq, buf);
+}
+
+static struct videobuf_queue_ops au0828_video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/* ------------------------------------------------------------------
+ V4L2 interface
+ ------------------------------------------------------------------*/
+
+static int au0828_i2s_init(struct au0828_dev *dev)
+{
+ /* Enable i2s mode */
+ au0828_writereg(dev, AU0828_AUDIOCTRL_50C, 0x01);
+ return 0;
+}
+
+/*
+ * Auvitek au0828 analog stream enable
+ * Please set interface0 to AS5 before enable the stream
+ */
+int au0828_analog_stream_enable(struct au0828_dev *d)
+{
+ dprintk(1, "au0828_analog_stream_enable called\n");
+ au0828_writereg(d, AU0828_SENSORCTRL_VBI_103, 0x00);
+ au0828_writereg(d, 0x106, 0x00);
+ /* set x position */
+ au0828_writereg(d, 0x110, 0x00);
+ au0828_writereg(d, 0x111, 0x00);
+ au0828_writereg(d, 0x114, 0xa0);
+ au0828_writereg(d, 0x115, 0x05);
+ /* set y position */
+ au0828_writereg(d, 0x112, 0x00);
+ au0828_writereg(d, 0x113, 0x00);
+ au0828_writereg(d, 0x116, 0xf2);
+ au0828_writereg(d, 0x117, 0x00);
+ au0828_writereg(d, AU0828_SENSORCTRL_100, 0xb3);
+
+ return 0;
+}
+
+int au0828_analog_stream_disable(struct au0828_dev *d)
+{
+ dprintk(1, "au0828_analog_stream_disable called\n");
+ au0828_writereg(d, AU0828_SENSORCTRL_100, 0x0);
+ return 0;
+}
+
+void au0828_analog_stream_reset(struct au0828_dev *dev)
+{
+ dprintk(1, "au0828_analog_stream_reset called\n");
+ au0828_writereg(dev, AU0828_SENSORCTRL_100, 0x0);
+ mdelay(30);
+ au0828_writereg(dev, AU0828_SENSORCTRL_100, 0xb3);
+}
+
+/*
+ * Some operations needs to stop current streaming
+ */
+static int au0828_stream_interrupt(struct au0828_dev *dev)
+{
+ int ret = 0;
+
+ dev->stream_state = STREAM_INTERRUPT;
+ if (dev->dev_state == DEV_DISCONNECTED)
+ return -ENODEV;
+ else if (ret) {
+ dev->dev_state = DEV_MISCONFIGURED;
+ dprintk(1, "%s device is misconfigured!\n", __func__);
+ return ret;
+ }
+ return 0;
+}
+
+/*
+ * au0828_release_resources
+ * unregister v4l2 devices
+ */
+void au0828_analog_unregister(struct au0828_dev *dev)
+{
+ dprintk(1, "au0828_release_resources called\n");
+ mutex_lock(&au0828_sysfs_lock);
+
+ if (dev->vdev)
+ video_unregister_device(dev->vdev);
+ if (dev->vbi_dev)
+ video_unregister_device(dev->vbi_dev);
+
+ mutex_unlock(&au0828_sysfs_lock);
+}
+
+
+/* Usage lock check functions */
+static int res_get(struct au0828_fh *fh, unsigned int bit)
+{
+ struct au0828_dev *dev = fh->dev;
+
+ if (fh->resources & bit)
+ /* have it already allocated */
+ return 1;
+
+ /* is it free? */
+ mutex_lock(&dev->lock);
+ if (dev->resources & bit) {
+ /* no, someone else uses it */
+ mutex_unlock(&dev->lock);
+ return 0;
+ }
+ /* it's free, grab it */
+ fh->resources |= bit;
+ dev->resources |= bit;
+ dprintk(1, "res: get %d\n", bit);
+ mutex_unlock(&dev->lock);
+ return 1;
+}
+
+static int res_check(struct au0828_fh *fh, unsigned int bit)
+{
+ return fh->resources & bit;
+}
+
+static int res_locked(struct au0828_dev *dev, unsigned int bit)
+{
+ return dev->resources & bit;
+}
+
+static void res_free(struct au0828_fh *fh, unsigned int bits)
+{
+ struct au0828_dev *dev = fh->dev;
+
+ BUG_ON((fh->resources & bits) != bits);
+
+ mutex_lock(&dev->lock);
+ fh->resources &= ~bits;
+ dev->resources &= ~bits;
+ dprintk(1, "res: put %d\n", bits);
+ mutex_unlock(&dev->lock);
+}
+
+static int get_ressource(struct au0828_fh *fh)
+{
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return AU0828_RESOURCE_VIDEO;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ return AU0828_RESOURCE_VBI;
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+/* This function ensures that video frames continue to be delivered even if
+ the ITU-656 input isn't receiving any data (thereby preventing applications
+ such as tvtime from hanging) */
+void au0828_vid_buffer_timeout(unsigned long data)
+{
+ struct au0828_dev *dev = (struct au0828_dev *) data;
+ struct au0828_dmaqueue *dma_q = &dev->vidq;
+ struct au0828_buffer *buf;
+ unsigned char *vid_data;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&dev->slock, flags);
+
+ buf = dev->isoc_ctl.buf;
+ if (buf != NULL) {
+ vid_data = videobuf_to_vmalloc(&buf->vb);
+ memset(vid_data, 0x00, buf->vb.size); /* Blank green frame */
+ buffer_filled(dev, dma_q, buf);
+ }
+ get_next_buf(dma_q, &buf);
+
+ if (dev->vid_timeout_running == 1)
+ mod_timer(&dev->vid_timeout, jiffies + (HZ / 10));
+
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+void au0828_vbi_buffer_timeout(unsigned long data)
+{
+ struct au0828_dev *dev = (struct au0828_dev *) data;
+ struct au0828_dmaqueue *dma_q = &dev->vbiq;
+ struct au0828_buffer *buf;
+ unsigned char *vbi_data;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&dev->slock, flags);
+
+ buf = dev->isoc_ctl.vbi_buf;
+ if (buf != NULL) {
+ vbi_data = videobuf_to_vmalloc(&buf->vb);
+ memset(vbi_data, 0x00, buf->vb.size);
+ vbi_buffer_filled(dev, dma_q, buf);
+ }
+ vbi_get_next_buf(dma_q, &buf);
+
+ if (dev->vbi_timeout_running == 1)
+ mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10));
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+
+static int au0828_v4l2_open(struct file *filp)
+{
+ int ret = 0;
+ struct video_device *vdev = video_devdata(filp);
+ struct au0828_dev *dev = video_drvdata(filp);
+ struct au0828_fh *fh;
+ int type;
+
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ break;
+ case VFL_TYPE_VBI:
+ type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ fh = kzalloc(sizeof(struct au0828_fh), GFP_KERNEL);
+ if (NULL == fh) {
+ dprintk(1, "Failed allocate au0828_fh struct!\n");
+ return -ENOMEM;
+ }
+
+ fh->type = type;
+ fh->dev = dev;
+ filp->private_data = fh;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
+ /* set au0828 interface0 to AS5 here again */
+ ret = usb_set_interface(dev->usbdev, 0, 5);
+ if (ret < 0) {
+ printk(KERN_INFO "Au0828 can't set alternate to 5!\n");
+ return -EBUSY;
+ }
+ dev->width = NTSC_STD_W;
+ dev->height = NTSC_STD_H;
+ dev->frame_size = dev->width * dev->height * 2;
+ dev->field_size = dev->width * dev->height;
+ dev->bytesperline = dev->width * 2;
+
+ au0828_analog_stream_enable(dev);
+ au0828_analog_stream_reset(dev);
+
+ /* If we were doing ac97 instead of i2s, it would go here...*/
+ au0828_i2s_init(dev);
+
+ dev->stream_state = STREAM_OFF;
+ dev->dev_state |= DEV_INITIALIZED;
+ }
+
+ dev->users++;
+
+ videobuf_queue_vmalloc_init(&fh->vb_vidq, &au0828_video_qops,
+ NULL, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct au0828_buffer), fh, NULL);
+
+ /* VBI Setup */
+ dev->vbi_width = 720;
+ dev->vbi_height = 1;
+ videobuf_queue_vmalloc_init(&fh->vb_vbiq, &au0828_vbi_qops,
+ NULL, &dev->slock,
+ V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_FIELD_SEQ_TB,
+ sizeof(struct au0828_buffer), fh, NULL);
+
+ return ret;
+}
+
+static int au0828_v4l2_close(struct file *filp)
+{
+ int ret;
+ struct au0828_fh *fh = filp->private_data;
+ struct au0828_dev *dev = fh->dev;
+
+ if (res_check(fh, AU0828_RESOURCE_VIDEO)) {
+ /* Cancel timeout thread in case they didn't call streamoff */
+ dev->vid_timeout_running = 0;
+ del_timer_sync(&dev->vid_timeout);
+
+ videobuf_stop(&fh->vb_vidq);
+ res_free(fh, AU0828_RESOURCE_VIDEO);
+ }
+
+ if (res_check(fh, AU0828_RESOURCE_VBI)) {
+ /* Cancel timeout thread in case they didn't call streamoff */
+ dev->vbi_timeout_running = 0;
+ del_timer_sync(&dev->vbi_timeout);
+
+ videobuf_stop(&fh->vb_vbiq);
+ res_free(fh, AU0828_RESOURCE_VBI);
+ }
+
+ if (dev->users == 1) {
+ if (dev->dev_state & DEV_DISCONNECTED) {
+ au0828_analog_unregister(dev);
+ kfree(dev);
+ return 0;
+ }
+
+ au0828_analog_stream_disable(dev);
+
+ au0828_uninit_isoc(dev);
+
+ /* Save some power by putting tuner to sleep */
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0);
+
+ /* When close the device, set the usb intf0 into alt0 to free
+ USB bandwidth */
+ ret = usb_set_interface(dev->usbdev, 0, 0);
+ if (ret < 0)
+ printk(KERN_INFO "Au0828 can't set alternate to 0!\n");
+ }
+
+ videobuf_mmap_free(&fh->vb_vidq);
+ videobuf_mmap_free(&fh->vb_vbiq);
+ kfree(fh);
+ dev->users--;
+ wake_up_interruptible_nr(&dev->open, 1);
+ return 0;
+}
+
+static ssize_t au0828_v4l2_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct au0828_fh *fh = filp->private_data;
+ struct au0828_dev *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (res_locked(dev, AU0828_RESOURCE_VIDEO))
+ return -EBUSY;
+
+ return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0,
+ filp->f_flags & O_NONBLOCK);
+ }
+
+ if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ if (!res_get(fh, AU0828_RESOURCE_VBI))
+ return -EBUSY;
+
+ if (dev->vbi_timeout_running == 0) {
+ /* Handle case where caller tries to read without
+ calling streamon first */
+ dev->vbi_timeout_running = 1;
+ mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10));
+ }
+
+ return videobuf_read_stream(&fh->vb_vbiq, buf, count, pos, 0,
+ filp->f_flags & O_NONBLOCK);
+ }
+
+ return 0;
+}
+
+static unsigned int au0828_v4l2_poll(struct file *filp, poll_table *wait)
+{
+ struct au0828_fh *fh = filp->private_data;
+ struct au0828_dev *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (!res_get(fh, AU0828_RESOURCE_VIDEO))
+ return POLLERR;
+ return videobuf_poll_stream(filp, &fh->vb_vidq, wait);
+ } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ if (!res_get(fh, AU0828_RESOURCE_VBI))
+ return POLLERR;
+ return videobuf_poll_stream(filp, &fh->vb_vbiq, wait);
+ } else {
+ return POLLERR;
+ }
+}
+
+static int au0828_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct au0828_fh *fh = filp->private_data;
+ struct au0828_dev *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ rc = videobuf_mmap_mapper(&fh->vb_vidq, vma);
+ else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ rc = videobuf_mmap_mapper(&fh->vb_vbiq, vma);
+
+ return rc;
+}
+
+static int au0828_set_format(struct au0828_dev *dev, unsigned int cmd,
+ struct v4l2_format *format)
+{
+ int ret;
+ int width = format->fmt.pix.width;
+ int height = format->fmt.pix.height;
+
+ if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ /* If they are demanding a format other than the one we support,
+ bail out (tvtime asks for UYVY and then retries with YUYV) */
+ if (format->fmt.pix.pixelformat != V4L2_PIX_FMT_UYVY)
+ return -EINVAL;
+
+ /* format->fmt.pix.width only support 720 and height 480 */
+ if (width != 720)
+ width = 720;
+ if (height != 480)
+ height = 480;
+
+ format->fmt.pix.width = width;
+ format->fmt.pix.height = height;
+ format->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
+ format->fmt.pix.bytesperline = width * 2;
+ format->fmt.pix.sizeimage = width * height * 2;
+ format->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ format->fmt.pix.field = V4L2_FIELD_INTERLACED;
+
+ if (cmd == VIDIOC_TRY_FMT)
+ return 0;
+
+ /* maybe set new image format, driver current only support 720*480 */
+ dev->width = width;
+ dev->height = height;
+ dev->frame_size = width * height * 2;
+ dev->field_size = width * height;
+ dev->bytesperline = width * 2;
+
+ if (dev->stream_state == STREAM_ON) {
+ dprintk(1, "VIDIOC_SET_FMT: interrupting stream!\n");
+ ret = au0828_stream_interrupt(dev);
+ if (ret != 0) {
+ dprintk(1, "error interrupting video stream!\n");
+ return ret;
+ }
+ }
+
+ /* set au0828 interface0 to AS5 here again */
+ ret = usb_set_interface(dev->usbdev, 0, 5);
+ if (ret < 0) {
+ printk(KERN_INFO "Au0828 can't set alt setting to 5!\n");
+ return -EBUSY;
+ }
+
+ au0828_analog_stream_enable(dev);
+
+ return 0;
+}
+
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, qc);
+ if (qc->type)
+ return 0;
+ else
+ return -EINVAL;
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+
+ strlcpy(cap->driver, "au0828", sizeof(cap->driver));
+ strlcpy(cap->card, dev->board.name, sizeof(cap->card));
+ strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info));
+
+ /*set the device capabilities */
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VBI_CAPTURE |
+ V4L2_CAP_AUDIO |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING |
+ V4L2_CAP_TUNER;
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index)
+ return -EINVAL;
+
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ strcpy(f->description, "Packed YUV2");
+
+ f->flags = 0;
+ f->pixelformat = V4L2_PIX_FMT_UYVY;
+
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+
+ f->fmt.pix.width = dev->width;
+ f->fmt.pix.height = dev->height;
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
+ f->fmt.pix.bytesperline = dev->bytesperline;
+ f->fmt.pix.sizeimage = dev->frame_size;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; /* NTSC/PAL */
+ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+
+ return au0828_set_format(dev, VIDIOC_TRY_FMT, f);
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ mutex_lock(&dev->lock);
+
+ if (videobuf_queue_is_busy(&fh->vb_vidq)) {
+ printk(KERN_INFO "%s queue busy\n", __func__);
+ rc = -EBUSY;
+ goto out;
+ }
+
+ rc = au0828_set_format(dev, VIDIOC_S_FMT, f);
+out:
+ mutex_unlock(&dev->lock);
+ return rc;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * norm)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+
+ /* FIXME: when we support something other than NTSC, we are going to
+ have to make the au0828 bridge adjust the size of its capture
+ buffer, which is currently hardcoded at 720x480 */
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, *norm);
+ return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *input)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ unsigned int tmp;
+
+ static const char *inames[] = {
+ [AU0828_VMUX_UNDEFINED] = "Undefined",
+ [AU0828_VMUX_COMPOSITE] = "Composite",
+ [AU0828_VMUX_SVIDEO] = "S-Video",
+ [AU0828_VMUX_CABLE] = "Cable TV",
+ [AU0828_VMUX_TELEVISION] = "Television",
+ [AU0828_VMUX_DVB] = "DVB",
+ [AU0828_VMUX_DEBUG] = "tv debug"
+ };
+
+ tmp = input->index;
+
+ if (tmp >= AU0828_MAX_INPUT)
+ return -EINVAL;
+ if (AUVI_INPUT(tmp).type == 0)
+ return -EINVAL;
+
+ input->index = tmp;
+ strcpy(input->name, inames[AUVI_INPUT(tmp).type]);
+ if ((AUVI_INPUT(tmp).type == AU0828_VMUX_TELEVISION) ||
+ (AUVI_INPUT(tmp).type == AU0828_VMUX_CABLE))
+ input->type |= V4L2_INPUT_TYPE_TUNER;
+ else
+ input->type |= V4L2_INPUT_TYPE_CAMERA;
+
+ input->std = dev->vdev->tvnorms;
+
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ *i = dev->ctrl_input;
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int index)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ int i;
+
+ dprintk(1, "VIDIOC_S_INPUT in function %s, input=%d\n", __func__,
+ index);
+ if (index >= AU0828_MAX_INPUT)
+ return -EINVAL;
+ if (AUVI_INPUT(index).type == 0)
+ return -EINVAL;
+ dev->ctrl_input = index;
+
+ switch (AUVI_INPUT(index).type) {
+ case AU0828_VMUX_SVIDEO:
+ dev->input_type = AU0828_VMUX_SVIDEO;
+ break;
+ case AU0828_VMUX_COMPOSITE:
+ dev->input_type = AU0828_VMUX_COMPOSITE;
+ break;
+ case AU0828_VMUX_TELEVISION:
+ dev->input_type = AU0828_VMUX_TELEVISION;
+ break;
+ default:
+ dprintk(1, "VIDIOC_S_INPUT unknown input type set [%d]\n",
+ AUVI_INPUT(index).type);
+ break;
+ }
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing,
+ AUVI_INPUT(index).vmux, 0, 0);
+
+ for (i = 0; i < AU0828_MAX_INPUT; i++) {
+ int enable = 0;
+ if (AUVI_INPUT(i).audio_setup == NULL)
+ continue;
+
+ if (i == index)
+ enable = 1;
+ else
+ enable = 0;
+ if (enable) {
+ (AUVI_INPUT(i).audio_setup)(dev, enable);
+ } else {
+ /* Make sure we leave it turned on if some
+ other input is routed to this callback */
+ if ((AUVI_INPUT(i).audio_setup) !=
+ ((AUVI_INPUT(index).audio_setup))) {
+ (AUVI_INPUT(i).audio_setup)(dev, enable);
+ }
+ }
+ }
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing,
+ AUVI_INPUT(index).amux, 0, 0);
+ return 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ unsigned int index = a->index;
+
+ if (a->index > 1)
+ return -EINVAL;
+
+ index = dev->ctrl_ainput;
+ if (index == 0)
+ strcpy(a->name, "Television");
+ else
+ strcpy(a->name, "Line in");
+
+ a->capability = V4L2_AUDCAP_STEREO;
+ a->index = index;
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ if (a->index != dev->ctrl_ainput)
+ return -EINVAL;
+ return 0;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl);
+ return 0;
+
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl);
+ return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+
+ if (t->index != 0)
+ return -EINVAL;
+
+ strcpy(t->name, "Auvitek tuner");
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t);
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+
+ if (t->index != 0)
+ return -EINVAL;
+
+ t->type = V4L2_TUNER_ANALOG_TV;
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t);
+ dprintk(1, "VIDIOC_S_TUNER: signal = %x, afc = %x\n", t->signal,
+ t->afc);
+ return 0;
+
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *freq)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+
+ freq->type = V4L2_TUNER_ANALOG_TV;
+ freq->frequency = dev->ctrl_freq;
+ return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *freq)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+
+ if (freq->tuner != 0)
+ return -EINVAL;
+ if (freq->type != V4L2_TUNER_ANALOG_TV)
+ return -EINVAL;
+
+ dev->ctrl_freq = freq->frequency;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, freq);
+
+ au0828_analog_stream_reset(dev);
+
+ return 0;
+}
+
+
+/* RAW VBI ioctls */
+
+static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
+ struct v4l2_format *format)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+
+ format->fmt.vbi.samples_per_line = dev->vbi_width;
+ format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+ format->fmt.vbi.offset = 0;
+ format->fmt.vbi.flags = 0;
+ format->fmt.vbi.sampling_rate = 6750000 * 4 / 2;
+
+ format->fmt.vbi.count[0] = dev->vbi_height;
+ format->fmt.vbi.count[1] = dev->vbi_height;
+ format->fmt.vbi.start[0] = 21;
+ format->fmt.vbi.start[1] = 284;
+
+ return 0;
+}
+
+static int vidioc_g_chip_ident(struct file *file, void *priv,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+
+ if (v4l2_chip_match_host(&chip->match)) {
+ chip->ident = V4L2_IDENT_AU0828;
+ return 0;
+ }
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_chip_ident, chip);
+ if (chip->ident == V4L2_IDENT_NONE)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int vidioc_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cc)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+
+ if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ cc->bounds.left = 0;
+ cc->bounds.top = 0;
+ cc->bounds.width = dev->width;
+ cc->bounds.height = dev->height;
+
+ cc->defrect = cc->bounds;
+
+ cc->pixelaspect.numerator = 54;
+ cc->pixelaspect.denominator = 59;
+
+ return 0;
+}
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ int rc = -EINVAL;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (unlikely(type != fh->type))
+ return -EINVAL;
+
+ dprintk(1, "vidioc_streamon fh=%p t=%d fh->res=%d dev->res=%d\n",
+ fh, type, fh->resources, dev->resources);
+
+ if (unlikely(!res_get(fh, get_ressource(fh))))
+ return -EBUSY;
+
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ au0828_analog_stream_enable(dev);
+ v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1);
+ }
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ rc = videobuf_streamon(&fh->vb_vidq);
+ dev->vid_timeout_running = 1;
+ mod_timer(&dev->vid_timeout, jiffies + (HZ / 10));
+ } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ rc = videobuf_streamon(&fh->vb_vbiq);
+ dev->vbi_timeout_running = 1;
+ mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10));
+ }
+
+ return rc;
+}
+
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ int rc;
+ int i;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)
+ return -EINVAL;
+ if (type != fh->type)
+ return -EINVAL;
+
+ dprintk(1, "vidioc_streamoff fh=%p t=%d fh->res=%d dev->res=%d\n",
+ fh, type, fh->resources, dev->resources);
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ dev->vid_timeout_running = 0;
+ del_timer_sync(&dev->vid_timeout);
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
+ rc = au0828_stream_interrupt(dev);
+ if (rc != 0)
+ return rc;
+
+ for (i = 0; i < AU0828_MAX_INPUT; i++) {
+ if (AUVI_INPUT(i).audio_setup == NULL)
+ continue;
+ (AUVI_INPUT(i).audio_setup)(dev, 0);
+ }
+
+ videobuf_streamoff(&fh->vb_vidq);
+ res_free(fh, AU0828_RESOURCE_VIDEO);
+ } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ dev->vbi_timeout_running = 0;
+ del_timer_sync(&dev->vbi_timeout);
+
+ videobuf_streamoff(&fh->vb_vbiq);
+ res_free(fh, AU0828_RESOURCE_VBI);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vidioc_g_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+
+ switch (reg->match.type) {
+ case V4L2_CHIP_MATCH_I2C_DRIVER:
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int vidioc_s_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+
+ switch (reg->match.type) {
+ case V4L2_CHIP_MATCH_I2C_DRIVER:
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+#endif
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *rb)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ rc = videobuf_reqbufs(&fh->vb_vidq, rb);
+ else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ rc = videobuf_reqbufs(&fh->vb_vbiq, rb);
+
+ return rc;
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ rc = videobuf_querybuf(&fh->vb_vidq, b);
+ else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ rc = videobuf_querybuf(&fh->vb_vbiq, b);
+
+ return rc;
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ rc = videobuf_qbuf(&fh->vb_vidq, b);
+ else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ rc = videobuf_qbuf(&fh->vb_vbiq, b);
+
+ return rc;
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ /* Workaround for a bug in the au0828 hardware design that sometimes
+ results in the colorspace being inverted */
+ if (dev->greenscreen_detected == 1) {
+ dprintk(1, "Detected green frame. Resetting stream...\n");
+ au0828_analog_stream_reset(dev);
+ dev->greenscreen_detected = 0;
+ }
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ rc = videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK);
+ else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ rc = videobuf_dqbuf(&fh->vb_vbiq, b, file->f_flags & O_NONBLOCK);
+
+ return rc;
+}
+
+static struct v4l2_file_operations au0828_v4l_fops = {
+ .owner = THIS_MODULE,
+ .open = au0828_v4l2_open,
+ .release = au0828_v4l2_close,
+ .read = au0828_v4l2_read,
+ .poll = au0828_v4l2_poll,
+ .mmap = au0828_v4l2_mmap,
+ .ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops video_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
+ .vidioc_s_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_cropcap = vidioc_cropcap,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+#endif
+ .vidioc_g_chip_ident = vidioc_g_chip_ident,
+};
+
+static const struct video_device au0828_video_template = {
+ .fops = &au0828_v4l_fops,
+ .release = video_device_release,
+ .ioctl_ops = &video_ioctl_ops,
+ .tvnorms = V4L2_STD_NTSC_M,
+ .current_norm = V4L2_STD_NTSC_M,
+};
+
+/**************************************************************************/
+
+int au0828_analog_register(struct au0828_dev *dev,
+ struct usb_interface *interface)
+{
+ int retval = -ENOMEM;
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int i;
+
+ dprintk(1, "au0828_analog_register called!\n");
+
+ /* set au0828 usb interface0 to as5 */
+ retval = usb_set_interface(dev->usbdev,
+ interface->cur_altsetting->desc.bInterfaceNumber, 5);
+ if (retval != 0) {
+ printk(KERN_INFO "Failure setting usb interface0 to as5\n");
+ return retval;
+ }
+
+ /* Figure out which endpoint has the isoc interface */
+ iface_desc = interface->cur_altsetting;
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
+ endpoint = &iface_desc->endpoint[i].desc;
+ if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+ == USB_DIR_IN) &&
+ ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ == USB_ENDPOINT_XFER_ISOC)) {
+
+ /* we find our isoc in endpoint */
+ u16 tmp = le16_to_cpu(endpoint->wMaxPacketSize);
+ dev->max_pkt_size = (tmp & 0x07ff) *
+ (((tmp & 0x1800) >> 11) + 1);
+ dev->isoc_in_endpointaddr = endpoint->bEndpointAddress;
+ }
+ }
+ if (!(dev->isoc_in_endpointaddr)) {
+ printk(KERN_INFO "Could not locate isoc endpoint\n");
+ kfree(dev);
+ return -ENODEV;
+ }
+
+ init_waitqueue_head(&dev->open);
+ spin_lock_init(&dev->slock);
+ mutex_init(&dev->lock);
+
+ /* init video dma queues */
+ INIT_LIST_HEAD(&dev->vidq.active);
+ INIT_LIST_HEAD(&dev->vidq.queued);
+ INIT_LIST_HEAD(&dev->vbiq.active);
+ INIT_LIST_HEAD(&dev->vbiq.queued);
+
+ dev->vid_timeout.function = au0828_vid_buffer_timeout;
+ dev->vid_timeout.data = (unsigned long) dev;
+ init_timer(&dev->vid_timeout);
+
+ dev->vbi_timeout.function = au0828_vbi_buffer_timeout;
+ dev->vbi_timeout.data = (unsigned long) dev;
+ init_timer(&dev->vbi_timeout);
+
+ dev->width = NTSC_STD_W;
+ dev->height = NTSC_STD_H;
+ dev->field_size = dev->width * dev->height;
+ dev->frame_size = dev->field_size << 1;
+ dev->bytesperline = dev->width << 1;
+ dev->ctrl_ainput = 0;
+
+ /* allocate and fill v4l2 video struct */
+ dev->vdev = video_device_alloc();
+ if (NULL == dev->vdev) {
+ dprintk(1, "Can't allocate video_device.\n");
+ return -ENOMEM;
+ }
+
+ /* allocate the VBI struct */
+ dev->vbi_dev = video_device_alloc();
+ if (NULL == dev->vbi_dev) {
+ dprintk(1, "Can't allocate vbi_device.\n");
+ kfree(dev->vdev);
+ return -ENOMEM;
+ }
+
+ /* Fill the video capture device struct */
+ *dev->vdev = au0828_video_template;
+ dev->vdev->parent = &dev->usbdev->dev;
+ strcpy(dev->vdev->name, "au0828a video");
+
+ /* Setup the VBI device */
+ *dev->vbi_dev = au0828_video_template;
+ dev->vbi_dev->parent = &dev->usbdev->dev;
+ strcpy(dev->vbi_dev->name, "au0828a vbi");
+
+ /* Register the v4l2 device */
+ video_set_drvdata(dev->vdev, dev);
+ retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER, -1);
+ if (retval != 0) {
+ dprintk(1, "unable to register video device (error = %d).\n",
+ retval);
+ video_device_release(dev->vdev);
+ return -ENODEV;
+ }
+
+ /* Register the vbi device */
+ video_set_drvdata(dev->vbi_dev, dev);
+ retval = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, -1);
+ if (retval != 0) {
+ dprintk(1, "unable to register vbi device (error = %d).\n",
+ retval);
+ video_device_release(dev->vbi_dev);
+ video_device_release(dev->vdev);
+ return -ENODEV;
+ }
+
+ dprintk(1, "%s completed!\n", __func__);
+
+ return 0;
+}
+
diff --git a/drivers/media/video/au0828/au0828.h b/drivers/media/video/au0828/au0828.h
new file mode 100644
index 00000000..9cde3532
--- /dev/null
+++ b/drivers/media/video/au0828/au0828.h
@@ -0,0 +1,302 @@
+/*
+ * Driver for the Auvitek AU0828 USB bridge
+ *
+ * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <media/tveeprom.h>
+
+/* Analog */
+#include <linux/videodev2.h>
+#include <media/videobuf-vmalloc.h>
+#include <media/v4l2-device.h>
+
+/* DVB */
+#include "demux.h"
+#include "dmxdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+#include "dvbdev.h"
+
+#include "au0828-reg.h"
+#include "au0828-cards.h"
+
+#define DRIVER_NAME "au0828"
+#define URB_COUNT 16
+#define URB_BUFSIZE (0xe522)
+
+/* Analog constants */
+#define NTSC_STD_W 720
+#define NTSC_STD_H 480
+
+#define AU0828_INTERLACED_DEFAULT 1
+#define V4L2_CID_PRIVATE_SHARPNESS (V4L2_CID_PRIVATE_BASE + 0)
+
+/* Defination for AU0828 USB transfer */
+#define AU0828_MAX_ISO_BUFS 12 /* maybe resize this value in the future */
+#define AU0828_ISO_PACKETS_PER_URB 128
+
+#define AU0828_MIN_BUF 4
+#define AU0828_DEF_BUF 8
+
+#define AU0828_MAX_INPUT 4
+
+/* au0828 resource types (used for res_get/res_lock etc */
+#define AU0828_RESOURCE_VIDEO 0x01
+#define AU0828_RESOURCE_VBI 0x02
+
+enum au0828_itype {
+ AU0828_VMUX_UNDEFINED = 0,
+ AU0828_VMUX_COMPOSITE,
+ AU0828_VMUX_SVIDEO,
+ AU0828_VMUX_CABLE,
+ AU0828_VMUX_TELEVISION,
+ AU0828_VMUX_DVB,
+ AU0828_VMUX_DEBUG
+};
+
+struct au0828_input {
+ enum au0828_itype type;
+ unsigned int vmux;
+ unsigned int amux;
+ void (*audio_setup) (void *priv, int enable);
+};
+
+struct au0828_board {
+ char *name;
+ unsigned int tuner_type;
+ unsigned char tuner_addr;
+ unsigned char i2c_clk_divider;
+ struct au0828_input input[AU0828_MAX_INPUT];
+
+};
+
+struct au0828_dvb {
+ struct mutex lock;
+ struct dvb_adapter adapter;
+ struct dvb_frontend *frontend;
+ struct dvb_demux demux;
+ struct dmxdev dmxdev;
+ struct dmx_frontend fe_hw;
+ struct dmx_frontend fe_mem;
+ struct dvb_net net;
+ int feeding;
+};
+
+enum au0828_stream_state {
+ STREAM_OFF,
+ STREAM_INTERRUPT,
+ STREAM_ON
+};
+
+#define AUVI_INPUT(nr) (dev->board.input[nr])
+
+/* device state */
+enum au0828_dev_state {
+ DEV_INITIALIZED = 0x01,
+ DEV_DISCONNECTED = 0x02,
+ DEV_MISCONFIGURED = 0x04
+};
+
+struct au0828_fh {
+ struct au0828_dev *dev;
+ unsigned int resources;
+
+ struct videobuf_queue vb_vidq;
+ struct videobuf_queue vb_vbiq;
+ enum v4l2_buf_type type;
+};
+
+struct au0828_usb_isoc_ctl {
+ /* max packet size of isoc transaction */
+ int max_pkt_size;
+
+ /* number of allocated urbs */
+ int num_bufs;
+
+ /* urb for isoc transfers */
+ struct urb **urb;
+
+ /* transfer buffers for isoc transfer */
+ char **transfer_buffer;
+
+ /* Last buffer command and region */
+ u8 cmd;
+ int pos, size, pktsize;
+
+ /* Last field: ODD or EVEN? */
+ int field;
+
+ /* Stores incomplete commands */
+ u32 tmp_buf;
+ int tmp_buf_len;
+
+ /* Stores already requested buffers */
+ struct au0828_buffer *buf;
+ struct au0828_buffer *vbi_buf;
+
+ /* Stores the number of received fields */
+ int nfields;
+
+ /* isoc urb callback */
+ int (*isoc_copy) (struct au0828_dev *dev, struct urb *urb);
+
+};
+
+/* buffer for one video frame */
+struct au0828_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+
+ struct list_head frame;
+ int top_field;
+ int receiving;
+};
+
+struct au0828_dmaqueue {
+ struct list_head active;
+ struct list_head queued;
+
+ wait_queue_head_t wq;
+
+ /* Counters to control buffer fill */
+ int pos;
+};
+
+struct au0828_dev {
+ struct mutex mutex;
+ struct usb_device *usbdev;
+ int boardnr;
+ struct au0828_board board;
+ u8 ctrlmsg[64];
+
+ /* I2C */
+ struct i2c_adapter i2c_adap;
+ struct i2c_algorithm i2c_algo;
+ struct i2c_client i2c_client;
+ u32 i2c_rc;
+
+ /* Digital */
+ struct au0828_dvb dvb;
+
+ /* Analog */
+ struct v4l2_device v4l2_dev;
+ int users;
+ unsigned int resources; /* resources in use */
+ struct video_device *vdev;
+ struct video_device *vbi_dev;
+ struct timer_list vid_timeout;
+ int vid_timeout_running;
+ struct timer_list vbi_timeout;
+ int vbi_timeout_running;
+ int width;
+ int height;
+ int vbi_width;
+ int vbi_height;
+ u32 vbi_read;
+ u32 field_size;
+ u32 frame_size;
+ u32 bytesperline;
+ int type;
+ u8 ctrl_ainput;
+ __u8 isoc_in_endpointaddr;
+ u8 isoc_init_ok;
+ int greenscreen_detected;
+ unsigned int frame_count;
+ int ctrl_freq;
+ int input_type;
+ unsigned int ctrl_input;
+ enum au0828_dev_state dev_state;
+ enum au0828_stream_state stream_state;
+ wait_queue_head_t open;
+
+ struct mutex lock;
+
+ /* Isoc control struct */
+ struct au0828_dmaqueue vidq;
+ struct au0828_dmaqueue vbiq;
+ struct au0828_usb_isoc_ctl isoc_ctl;
+ spinlock_t slock;
+
+ /* usb transfer */
+ int alt; /* alternate */
+ int max_pkt_size; /* max packet size of isoc transaction */
+ int num_alt; /* Number of alternative settings */
+ unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */
+ struct urb *urb[AU0828_MAX_ISO_BUFS]; /* urb for isoc transfers */
+ char *transfer_buffer[AU0828_MAX_ISO_BUFS];/* transfer buffers for isoc
+ transfer */
+
+ /* USB / URB Related */
+ int urb_streaming;
+ struct urb *urbs[URB_COUNT];
+};
+
+/* ----------------------------------------------------------- */
+#define au0828_read(dev, reg) au0828_readreg(dev, reg)
+#define au0828_write(dev, reg, value) au0828_writereg(dev, reg, value)
+#define au0828_andor(dev, reg, mask, value) \
+ au0828_writereg(dev, reg, \
+ (au0828_readreg(dev, reg) & ~(mask)) | ((value) & (mask)))
+
+#define au0828_set(dev, reg, bit) au0828_andor(dev, (reg), (bit), (bit))
+#define au0828_clear(dev, reg, bit) au0828_andor(dev, (reg), (bit), 0)
+
+/* ----------------------------------------------------------- */
+/* au0828-core.c */
+extern u32 au0828_read(struct au0828_dev *dev, u16 reg);
+extern u32 au0828_write(struct au0828_dev *dev, u16 reg, u32 val);
+extern int au0828_debug;
+
+/* ----------------------------------------------------------- */
+/* au0828-cards.c */
+extern struct au0828_board au0828_boards[];
+extern struct usb_device_id au0828_usb_id_table[];
+extern void au0828_gpio_setup(struct au0828_dev *dev);
+extern int au0828_tuner_callback(void *priv, int component,
+ int command, int arg);
+extern void au0828_card_setup(struct au0828_dev *dev);
+
+/* ----------------------------------------------------------- */
+/* au0828-i2c.c */
+extern int au0828_i2c_register(struct au0828_dev *dev);
+extern int au0828_i2c_unregister(struct au0828_dev *dev);
+
+/* ----------------------------------------------------------- */
+/* au0828-video.c */
+int au0828_analog_register(struct au0828_dev *dev,
+ struct usb_interface *interface);
+int au0828_analog_stream_disable(struct au0828_dev *d);
+void au0828_analog_unregister(struct au0828_dev *dev);
+
+/* ----------------------------------------------------------- */
+/* au0828-dvb.c */
+extern int au0828_dvb_register(struct au0828_dev *dev);
+extern void au0828_dvb_unregister(struct au0828_dev *dev);
+
+/* au0828-vbi.c */
+extern struct videobuf_queue_ops au0828_vbi_qops;
+
+#define dprintk(level, fmt, arg...)\
+ do { if (au0828_debug & level)\
+ printk(KERN_DEBUG DRIVER_NAME "/0: " fmt, ## arg);\
+ } while (0)
diff --git a/drivers/media/video/blackfin/Kconfig b/drivers/media/video/blackfin/Kconfig
new file mode 100644
index 00000000..ecd53237
--- /dev/null
+++ b/drivers/media/video/blackfin/Kconfig
@@ -0,0 +1,10 @@
+config VIDEO_BLACKFIN_CAPTURE
+ tristate "Blackfin Video Capture Driver"
+ depends on VIDEO_V4L2 && BLACKFIN && I2C
+ select VIDEOBUF2_DMA_CONTIG
+ help
+ V4L2 bridge driver for Blackfin video capture device.
+ Choose PPI or EPPI as its interface.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bfin_video_capture.
diff --git a/drivers/media/video/blackfin/Makefile b/drivers/media/video/blackfin/Makefile
new file mode 100644
index 00000000..aa3a0a21
--- /dev/null
+++ b/drivers/media/video/blackfin/Makefile
@@ -0,0 +1,2 @@
+bfin_video_capture-objs := bfin_capture.o ppi.o
+obj-$(CONFIG_VIDEO_BLACKFIN_CAPTURE) += bfin_video_capture.o
diff --git a/drivers/media/video/blackfin/bfin_capture.c b/drivers/media/video/blackfin/bfin_capture.c
new file mode 100644
index 00000000..514fcf74
--- /dev/null
+++ b/drivers/media/video/blackfin/bfin_capture.c
@@ -0,0 +1,1059 @@
+/*
+ * Analog Devices video capture driver
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/types.h>
+
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include <asm/dma.h>
+
+#include <media/blackfin/bfin_capture.h>
+#include <media/blackfin/ppi.h>
+
+#define CAPTURE_DRV_NAME "bfin_capture"
+#define BCAP_MIN_NUM_BUF 2
+
+struct bcap_format {
+ char *desc;
+ u32 pixelformat;
+ enum v4l2_mbus_pixelcode mbus_code;
+ int bpp; /* bits per pixel */
+};
+
+struct bcap_buffer {
+ struct vb2_buffer vb;
+ struct list_head list;
+};
+
+struct bcap_device {
+ /* capture device instance */
+ struct v4l2_device v4l2_dev;
+ /* v4l2 control handler */
+ struct v4l2_ctrl_handler ctrl_handler;
+ /* device node data */
+ struct video_device *video_dev;
+ /* sub device instance */
+ struct v4l2_subdev *sd;
+ /* capture config */
+ struct bfin_capture_config *cfg;
+ /* ppi interface */
+ struct ppi_if *ppi;
+ /* current input */
+ unsigned int cur_input;
+ /* current selected standard */
+ v4l2_std_id std;
+ /* used to store pixel format */
+ struct v4l2_pix_format fmt;
+ /* bits per pixel*/
+ int bpp;
+ /* used to store sensor supported format */
+ struct bcap_format *sensor_formats;
+ /* number of sensor formats array */
+ int num_sensor_formats;
+ /* pointing to current video buffer */
+ struct bcap_buffer *cur_frm;
+ /* pointing to next video buffer */
+ struct bcap_buffer *next_frm;
+ /* buffer queue used in videobuf2 */
+ struct vb2_queue buffer_queue;
+ /* allocator-specific contexts for each plane */
+ struct vb2_alloc_ctx *alloc_ctx;
+ /* queue of filled frames */
+ struct list_head dma_queue;
+ /* used in videobuf2 callback */
+ spinlock_t lock;
+ /* used to access capture device */
+ struct mutex mutex;
+ /* used to wait ppi to complete one transfer */
+ struct completion comp;
+ /* prepare to stop */
+ bool stop;
+};
+
+struct bcap_fh {
+ struct v4l2_fh fh;
+ /* indicates whether this file handle is doing IO */
+ bool io_allowed;
+};
+
+static const struct bcap_format bcap_formats[] = {
+ {
+ .desc = "YCbCr 4:2:2 Interleaved UYVY",
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .bpp = 16,
+ },
+ {
+ .desc = "YCbCr 4:2:2 Interleaved YUYV",
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .bpp = 16,
+ },
+ {
+ .desc = "RGB 565",
+ .pixelformat = V4L2_PIX_FMT_RGB565,
+ .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE,
+ .bpp = 16,
+ },
+ {
+ .desc = "RGB 444",
+ .pixelformat = V4L2_PIX_FMT_RGB444,
+ .mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE,
+ .bpp = 16,
+ },
+
+};
+#define BCAP_MAX_FMTS ARRAY_SIZE(bcap_formats)
+
+static irqreturn_t bcap_isr(int irq, void *dev_id);
+
+static struct bcap_buffer *to_bcap_vb(struct vb2_buffer *vb)
+{
+ return container_of(vb, struct bcap_buffer, vb);
+}
+
+static int bcap_init_sensor_formats(struct bcap_device *bcap_dev)
+{
+ enum v4l2_mbus_pixelcode code;
+ struct bcap_format *sf;
+ unsigned int num_formats = 0;
+ int i, j;
+
+ while (!v4l2_subdev_call(bcap_dev->sd, video,
+ enum_mbus_fmt, num_formats, &code))
+ num_formats++;
+ if (!num_formats)
+ return -ENXIO;
+
+ sf = kzalloc(num_formats * sizeof(*sf), GFP_KERNEL);
+ if (!sf)
+ return -ENOMEM;
+
+ for (i = 0; i < num_formats; i++) {
+ v4l2_subdev_call(bcap_dev->sd, video,
+ enum_mbus_fmt, i, &code);
+ for (j = 0; j < BCAP_MAX_FMTS; j++)
+ if (code == bcap_formats[j].mbus_code)
+ break;
+ if (j == BCAP_MAX_FMTS) {
+ /* we don't allow this sensor working with our bridge */
+ kfree(sf);
+ return -EINVAL;
+ }
+ sf[i] = bcap_formats[j];
+ }
+ bcap_dev->sensor_formats = sf;
+ bcap_dev->num_sensor_formats = num_formats;
+ return 0;
+}
+
+static void bcap_free_sensor_formats(struct bcap_device *bcap_dev)
+{
+ bcap_dev->num_sensor_formats = 0;
+ kfree(bcap_dev->sensor_formats);
+ bcap_dev->sensor_formats = NULL;
+}
+
+static int bcap_open(struct file *file)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct video_device *vfd = bcap_dev->video_dev;
+ struct bcap_fh *bcap_fh;
+
+ if (!bcap_dev->sd) {
+ v4l2_err(&bcap_dev->v4l2_dev, "No sub device registered\n");
+ return -ENODEV;
+ }
+
+ bcap_fh = kzalloc(sizeof(*bcap_fh), GFP_KERNEL);
+ if (!bcap_fh) {
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "unable to allocate memory for file handle object\n");
+ return -ENOMEM;
+ }
+
+ v4l2_fh_init(&bcap_fh->fh, vfd);
+
+ /* store pointer to v4l2_fh in private_data member of file */
+ file->private_data = &bcap_fh->fh;
+ v4l2_fh_add(&bcap_fh->fh);
+ bcap_fh->io_allowed = false;
+ return 0;
+}
+
+static int bcap_release(struct file *file)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct v4l2_fh *fh = file->private_data;
+ struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
+
+ /* if this instance is doing IO */
+ if (bcap_fh->io_allowed)
+ vb2_queue_release(&bcap_dev->buffer_queue);
+
+ file->private_data = NULL;
+ v4l2_fh_del(&bcap_fh->fh);
+ v4l2_fh_exit(&bcap_fh->fh);
+ kfree(bcap_fh);
+ return 0;
+}
+
+static int bcap_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ return vb2_mmap(&bcap_dev->buffer_queue, vma);
+}
+
+#ifndef CONFIG_MMU
+static unsigned long bcap_get_unmapped_area(struct file *file,
+ unsigned long addr,
+ unsigned long len,
+ unsigned long pgoff,
+ unsigned long flags)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ return vb2_get_unmapped_area(&bcap_dev->buffer_queue,
+ addr,
+ len,
+ pgoff,
+ flags);
+}
+#endif
+
+static unsigned int bcap_poll(struct file *file, poll_table *wait)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ return vb2_poll(&bcap_dev->buffer_queue, file, wait);
+}
+
+static int bcap_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
+
+ if (*nbuffers < BCAP_MIN_NUM_BUF)
+ *nbuffers = BCAP_MIN_NUM_BUF;
+
+ *nplanes = 1;
+ sizes[0] = bcap_dev->fmt.sizeimage;
+ alloc_ctxs[0] = bcap_dev->alloc_ctx;
+
+ return 0;
+}
+
+static int bcap_buffer_init(struct vb2_buffer *vb)
+{
+ struct bcap_buffer *buf = to_bcap_vb(vb);
+
+ INIT_LIST_HEAD(&buf->list);
+ return 0;
+}
+
+static int bcap_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct bcap_buffer *buf = to_bcap_vb(vb);
+ unsigned long size;
+
+ size = bcap_dev->fmt.sizeimage;
+ if (vb2_plane_size(vb, 0) < size) {
+ v4l2_err(&bcap_dev->v4l2_dev, "buffer too small (%lu < %lu)\n",
+ vb2_plane_size(vb, 0), size);
+ return -EINVAL;
+ }
+ vb2_set_plane_payload(&buf->vb, 0, size);
+
+ return 0;
+}
+
+static void bcap_buffer_queue(struct vb2_buffer *vb)
+{
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct bcap_buffer *buf = to_bcap_vb(vb);
+ unsigned long flags;
+
+ spin_lock_irqsave(&bcap_dev->lock, flags);
+ list_add_tail(&buf->list, &bcap_dev->dma_queue);
+ spin_unlock_irqrestore(&bcap_dev->lock, flags);
+}
+
+static void bcap_buffer_cleanup(struct vb2_buffer *vb)
+{
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct bcap_buffer *buf = to_bcap_vb(vb);
+ unsigned long flags;
+
+ spin_lock_irqsave(&bcap_dev->lock, flags);
+ list_del_init(&buf->list);
+ spin_unlock_irqrestore(&bcap_dev->lock, flags);
+}
+
+static void bcap_lock(struct vb2_queue *vq)
+{
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
+ mutex_lock(&bcap_dev->mutex);
+}
+
+static void bcap_unlock(struct vb2_queue *vq)
+{
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
+ mutex_unlock(&bcap_dev->mutex);
+}
+
+static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
+ struct ppi_if *ppi = bcap_dev->ppi;
+ struct ppi_params params;
+ int ret;
+
+ /* enable streamon on the sub device */
+ ret = v4l2_subdev_call(bcap_dev->sd, video, s_stream, 1);
+ if (ret && (ret != -ENOIOCTLCMD)) {
+ v4l2_err(&bcap_dev->v4l2_dev, "stream on failed in subdev\n");
+ return ret;
+ }
+
+ /* set ppi params */
+ params.width = bcap_dev->fmt.width;
+ params.height = bcap_dev->fmt.height;
+ params.bpp = bcap_dev->bpp;
+ params.ppi_control = bcap_dev->cfg->ppi_control;
+ params.int_mask = bcap_dev->cfg->int_mask;
+ params.blank_clocks = bcap_dev->cfg->blank_clocks;
+ ret = ppi->ops->set_params(ppi, &params);
+ if (ret < 0) {
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "Error in setting ppi params\n");
+ return ret;
+ }
+
+ /* attach ppi DMA irq handler */
+ ret = ppi->ops->attach_irq(ppi, bcap_isr);
+ if (ret < 0) {
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "Error in attaching interrupt handler\n");
+ return ret;
+ }
+
+ INIT_COMPLETION(bcap_dev->comp);
+ bcap_dev->stop = false;
+ return 0;
+}
+
+static int bcap_stop_streaming(struct vb2_queue *vq)
+{
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
+ struct ppi_if *ppi = bcap_dev->ppi;
+ int ret;
+
+ if (!vb2_is_streaming(vq))
+ return 0;
+
+ bcap_dev->stop = true;
+ wait_for_completion(&bcap_dev->comp);
+ ppi->ops->stop(ppi);
+ ppi->ops->detach_irq(ppi);
+ ret = v4l2_subdev_call(bcap_dev->sd, video, s_stream, 0);
+ if (ret && (ret != -ENOIOCTLCMD))
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "stream off failed in subdev\n");
+
+ /* release all active buffers */
+ while (!list_empty(&bcap_dev->dma_queue)) {
+ bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next,
+ struct bcap_buffer, list);
+ list_del(&bcap_dev->next_frm->list);
+ vb2_buffer_done(&bcap_dev->next_frm->vb, VB2_BUF_STATE_ERROR);
+ }
+ return 0;
+}
+
+static struct vb2_ops bcap_video_qops = {
+ .queue_setup = bcap_queue_setup,
+ .buf_init = bcap_buffer_init,
+ .buf_prepare = bcap_buffer_prepare,
+ .buf_cleanup = bcap_buffer_cleanup,
+ .buf_queue = bcap_buffer_queue,
+ .wait_prepare = bcap_unlock,
+ .wait_finish = bcap_lock,
+ .start_streaming = bcap_start_streaming,
+ .stop_streaming = bcap_stop_streaming,
+};
+
+static int bcap_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *req_buf)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct vb2_queue *vq = &bcap_dev->buffer_queue;
+ struct v4l2_fh *fh = file->private_data;
+ struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
+
+ if (vb2_is_busy(vq))
+ return -EBUSY;
+
+ bcap_fh->io_allowed = true;
+
+ return vb2_reqbufs(vq, req_buf);
+}
+
+static int bcap_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ return vb2_querybuf(&bcap_dev->buffer_queue, buf);
+}
+
+static int bcap_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct v4l2_fh *fh = file->private_data;
+ struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
+
+ if (!bcap_fh->io_allowed)
+ return -EBUSY;
+
+ return vb2_qbuf(&bcap_dev->buffer_queue, buf);
+}
+
+static int bcap_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct v4l2_fh *fh = file->private_data;
+ struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
+
+ if (!bcap_fh->io_allowed)
+ return -EBUSY;
+
+ return vb2_dqbuf(&bcap_dev->buffer_queue,
+ buf, file->f_flags & O_NONBLOCK);
+}
+
+static irqreturn_t bcap_isr(int irq, void *dev_id)
+{
+ struct ppi_if *ppi = dev_id;
+ struct bcap_device *bcap_dev = ppi->priv;
+ struct timeval timevalue;
+ struct vb2_buffer *vb = &bcap_dev->cur_frm->vb;
+ dma_addr_t addr;
+
+ spin_lock(&bcap_dev->lock);
+
+ if (bcap_dev->cur_frm != bcap_dev->next_frm) {
+ do_gettimeofday(&timevalue);
+ vb->v4l2_buf.timestamp = timevalue;
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ bcap_dev->cur_frm = bcap_dev->next_frm;
+ }
+
+ ppi->ops->stop(ppi);
+
+ if (bcap_dev->stop) {
+ complete(&bcap_dev->comp);
+ } else {
+ if (!list_empty(&bcap_dev->dma_queue)) {
+ bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next,
+ struct bcap_buffer, list);
+ list_del(&bcap_dev->next_frm->list);
+ addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->next_frm->vb, 0);
+ ppi->ops->update_addr(ppi, (unsigned long)addr);
+ }
+ ppi->ops->start(ppi);
+ }
+
+ spin_unlock(&bcap_dev->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int bcap_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type buf_type)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct bcap_fh *fh = file->private_data;
+ struct ppi_if *ppi = bcap_dev->ppi;
+ dma_addr_t addr;
+ int ret;
+
+ if (!fh->io_allowed)
+ return -EBUSY;
+
+ /* call streamon to start streaming in videobuf */
+ ret = vb2_streamon(&bcap_dev->buffer_queue, buf_type);
+ if (ret)
+ return ret;
+
+ /* if dma queue is empty, return error */
+ if (list_empty(&bcap_dev->dma_queue)) {
+ v4l2_err(&bcap_dev->v4l2_dev, "dma queue is empty\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* get the next frame from the dma queue */
+ bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next,
+ struct bcap_buffer, list);
+ bcap_dev->cur_frm = bcap_dev->next_frm;
+ /* remove buffer from the dma queue */
+ list_del(&bcap_dev->cur_frm->list);
+ addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->cur_frm->vb, 0);
+ /* update DMA address */
+ ppi->ops->update_addr(ppi, (unsigned long)addr);
+ /* enable ppi */
+ ppi->ops->start(ppi);
+
+ return 0;
+err:
+ vb2_streamoff(&bcap_dev->buffer_queue, buf_type);
+ return ret;
+}
+
+static int bcap_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type buf_type)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct bcap_fh *fh = file->private_data;
+
+ if (!fh->io_allowed)
+ return -EBUSY;
+
+ return vb2_streamoff(&bcap_dev->buffer_queue, buf_type);
+}
+
+static int bcap_querystd(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ return v4l2_subdev_call(bcap_dev->sd, video, querystd, std);
+}
+
+static int bcap_g_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ *std = bcap_dev->std;
+ return 0;
+}
+
+static int bcap_s_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ int ret;
+
+ if (vb2_is_busy(&bcap_dev->buffer_queue))
+ return -EBUSY;
+
+ ret = v4l2_subdev_call(bcap_dev->sd, core, s_std, *std);
+ if (ret < 0)
+ return ret;
+
+ bcap_dev->std = *std;
+ return 0;
+}
+
+static int bcap_enum_input(struct file *file, void *priv,
+ struct v4l2_input *input)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct bfin_capture_config *config = bcap_dev->cfg;
+ int ret;
+ u32 status;
+
+ if (input->index >= config->num_inputs)
+ return -EINVAL;
+
+ *input = config->inputs[input->index];
+ /* get input status */
+ ret = v4l2_subdev_call(bcap_dev->sd, video, g_input_status, &status);
+ if (!ret)
+ input->status = status;
+ return 0;
+}
+
+static int bcap_g_input(struct file *file, void *priv, unsigned int *index)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ *index = bcap_dev->cur_input;
+ return 0;
+}
+
+static int bcap_s_input(struct file *file, void *priv, unsigned int index)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct bfin_capture_config *config = bcap_dev->cfg;
+ struct bcap_route *route;
+ int ret;
+
+ if (vb2_is_busy(&bcap_dev->buffer_queue))
+ return -EBUSY;
+
+ if (index >= config->num_inputs)
+ return -EINVAL;
+
+ route = &config->routes[index];
+ ret = v4l2_subdev_call(bcap_dev->sd, video, s_routing,
+ route->input, route->output, 0);
+ if ((ret < 0) && (ret != -ENOIOCTLCMD)) {
+ v4l2_err(&bcap_dev->v4l2_dev, "Failed to set input\n");
+ return ret;
+ }
+ bcap_dev->cur_input = index;
+ return 0;
+}
+
+static int bcap_try_format(struct bcap_device *bcap,
+ struct v4l2_pix_format *pixfmt,
+ enum v4l2_mbus_pixelcode *mbus_code,
+ int *bpp)
+{
+ struct bcap_format *sf = bcap->sensor_formats;
+ struct bcap_format *fmt = NULL;
+ struct v4l2_mbus_framefmt mbus_fmt;
+ int ret, i;
+
+ for (i = 0; i < bcap->num_sensor_formats; i++) {
+ fmt = &sf[i];
+ if (pixfmt->pixelformat == fmt->pixelformat)
+ break;
+ }
+ if (i == bcap->num_sensor_formats)
+ fmt = &sf[0];
+
+ if (mbus_code)
+ *mbus_code = fmt->mbus_code;
+ if (bpp)
+ *bpp = fmt->bpp;
+ v4l2_fill_mbus_format(&mbus_fmt, pixfmt, fmt->mbus_code);
+ ret = v4l2_subdev_call(bcap->sd, video,
+ try_mbus_fmt, &mbus_fmt);
+ if (ret < 0)
+ return ret;
+ v4l2_fill_pix_format(pixfmt, &mbus_fmt);
+ pixfmt->bytesperline = pixfmt->width * fmt->bpp / 8;
+ pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
+ return 0;
+}
+
+static int bcap_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *fmt)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct bcap_format *sf = bcap_dev->sensor_formats;
+
+ if (fmt->index >= bcap_dev->num_sensor_formats)
+ return -EINVAL;
+
+ fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ strlcpy(fmt->description,
+ sf[fmt->index].desc,
+ sizeof(fmt->description));
+ fmt->pixelformat = sf[fmt->index].pixelformat;
+ return 0;
+}
+
+static int bcap_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+
+ return bcap_try_format(bcap_dev, pixfmt, NULL, NULL);
+}
+
+static int bcap_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ fmt->fmt.pix = bcap_dev->fmt;
+ return 0;
+}
+
+static int bcap_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct v4l2_mbus_framefmt mbus_fmt;
+ enum v4l2_mbus_pixelcode mbus_code;
+ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+ int ret, bpp;
+
+ if (vb2_is_busy(&bcap_dev->buffer_queue))
+ return -EBUSY;
+
+ /* see if format works */
+ ret = bcap_try_format(bcap_dev, pixfmt, &mbus_code, &bpp);
+ if (ret < 0)
+ return ret;
+
+ v4l2_fill_mbus_format(&mbus_fmt, pixfmt, mbus_code);
+ ret = v4l2_subdev_call(bcap_dev->sd, video, s_mbus_fmt, &mbus_fmt);
+ if (ret < 0)
+ return ret;
+ bcap_dev->fmt = *pixfmt;
+ bcap_dev->bpp = bpp;
+ return 0;
+}
+
+static int bcap_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver));
+ strlcpy(cap->bus_info, "Blackfin Platform", sizeof(cap->bus_info));
+ strlcpy(cap->card, bcap_dev->cfg->card_name, sizeof(cap->card));
+ return 0;
+}
+
+static int bcap_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ return v4l2_subdev_call(bcap_dev->sd, video, g_parm, a);
+}
+
+static int bcap_s_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ return v4l2_subdev_call(bcap_dev->sd, video, s_parm, a);
+}
+
+static int bcap_g_chip_ident(struct file *file, void *priv,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+ if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
+ chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+
+ return v4l2_subdev_call(bcap_dev->sd, core,
+ g_chip_ident, chip);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int bcap_dbg_g_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ return v4l2_subdev_call(bcap_dev->sd, core,
+ g_register, reg);
+}
+
+static int bcap_dbg_s_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ return v4l2_subdev_call(bcap_dev->sd, core,
+ s_register, reg);
+}
+#endif
+
+static int bcap_log_status(struct file *file, void *priv)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ /* status for sub devices */
+ v4l2_device_call_all(&bcap_dev->v4l2_dev, 0, core, log_status);
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops bcap_ioctl_ops = {
+ .vidioc_querycap = bcap_querycap,
+ .vidioc_g_fmt_vid_cap = bcap_g_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_cap = bcap_enum_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = bcap_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = bcap_try_fmt_vid_cap,
+ .vidioc_enum_input = bcap_enum_input,
+ .vidioc_g_input = bcap_g_input,
+ .vidioc_s_input = bcap_s_input,
+ .vidioc_querystd = bcap_querystd,
+ .vidioc_s_std = bcap_s_std,
+ .vidioc_g_std = bcap_g_std,
+ .vidioc_reqbufs = bcap_reqbufs,
+ .vidioc_querybuf = bcap_querybuf,
+ .vidioc_qbuf = bcap_qbuf,
+ .vidioc_dqbuf = bcap_dqbuf,
+ .vidioc_streamon = bcap_streamon,
+ .vidioc_streamoff = bcap_streamoff,
+ .vidioc_g_parm = bcap_g_parm,
+ .vidioc_s_parm = bcap_s_parm,
+ .vidioc_g_chip_ident = bcap_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = bcap_dbg_g_register,
+ .vidioc_s_register = bcap_dbg_s_register,
+#endif
+ .vidioc_log_status = bcap_log_status,
+};
+
+static struct v4l2_file_operations bcap_fops = {
+ .owner = THIS_MODULE,
+ .open = bcap_open,
+ .release = bcap_release,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = bcap_mmap,
+#ifndef CONFIG_MMU
+ .get_unmapped_area = bcap_get_unmapped_area,
+#endif
+ .poll = bcap_poll
+};
+
+static int __devinit bcap_probe(struct platform_device *pdev)
+{
+ struct bcap_device *bcap_dev;
+ struct video_device *vfd;
+ struct i2c_adapter *i2c_adap;
+ struct bfin_capture_config *config;
+ struct vb2_queue *q;
+ int ret;
+
+ config = pdev->dev.platform_data;
+ if (!config) {
+ v4l2_err(pdev->dev.driver, "Unable to get board config\n");
+ return -ENODEV;
+ }
+
+ bcap_dev = kzalloc(sizeof(*bcap_dev), GFP_KERNEL);
+ if (!bcap_dev) {
+ v4l2_err(pdev->dev.driver, "Unable to alloc bcap_dev\n");
+ return -ENOMEM;
+ }
+
+ bcap_dev->cfg = config;
+
+ bcap_dev->ppi = ppi_create_instance(config->ppi_info);
+ if (!bcap_dev->ppi) {
+ v4l2_err(pdev->dev.driver, "Unable to create ppi\n");
+ ret = -ENODEV;
+ goto err_free_dev;
+ }
+ bcap_dev->ppi->priv = bcap_dev;
+
+ bcap_dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(bcap_dev->alloc_ctx)) {
+ ret = PTR_ERR(bcap_dev->alloc_ctx);
+ goto err_free_ppi;
+ }
+
+ vfd = video_device_alloc();
+ if (!vfd) {
+ ret = -ENOMEM;
+ v4l2_err(pdev->dev.driver, "Unable to alloc video device\n");
+ goto err_cleanup_ctx;
+ }
+
+ /* initialize field of video device */
+ vfd->release = video_device_release;
+ vfd->fops = &bcap_fops;
+ vfd->ioctl_ops = &bcap_ioctl_ops;
+ vfd->tvnorms = 0;
+ vfd->v4l2_dev = &bcap_dev->v4l2_dev;
+ set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
+ strncpy(vfd->name, CAPTURE_DRV_NAME, sizeof(vfd->name));
+ bcap_dev->video_dev = vfd;
+
+ ret = v4l2_device_register(&pdev->dev, &bcap_dev->v4l2_dev);
+ if (ret) {
+ v4l2_err(pdev->dev.driver,
+ "Unable to register v4l2 device\n");
+ goto err_release_vdev;
+ }
+ v4l2_info(&bcap_dev->v4l2_dev, "v4l2 device registered\n");
+
+ bcap_dev->v4l2_dev.ctrl_handler = &bcap_dev->ctrl_handler;
+ ret = v4l2_ctrl_handler_init(&bcap_dev->ctrl_handler, 0);
+ if (ret) {
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "Unable to init control handler\n");
+ goto err_unreg_v4l2;
+ }
+
+ spin_lock_init(&bcap_dev->lock);
+ /* initialize queue */
+ q = &bcap_dev->buffer_queue;
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP;
+ q->drv_priv = bcap_dev;
+ q->buf_struct_size = sizeof(struct bcap_buffer);
+ q->ops = &bcap_video_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+
+ vb2_queue_init(q);
+
+ mutex_init(&bcap_dev->mutex);
+ init_completion(&bcap_dev->comp);
+
+ /* init video dma queues */
+ INIT_LIST_HEAD(&bcap_dev->dma_queue);
+
+ vfd->lock = &bcap_dev->mutex;
+
+ /* register video device */
+ ret = video_register_device(bcap_dev->video_dev, VFL_TYPE_GRABBER, -1);
+ if (ret) {
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "Unable to register video device\n");
+ goto err_free_handler;
+ }
+ video_set_drvdata(bcap_dev->video_dev, bcap_dev);
+ v4l2_info(&bcap_dev->v4l2_dev, "video device registered as: %s\n",
+ video_device_node_name(vfd));
+
+ /* load up the subdevice */
+ i2c_adap = i2c_get_adapter(config->i2c_adapter_id);
+ if (!i2c_adap) {
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "Unable to find i2c adapter\n");
+ goto err_unreg_vdev;
+
+ }
+ bcap_dev->sd = v4l2_i2c_new_subdev_board(&bcap_dev->v4l2_dev,
+ i2c_adap,
+ &config->board_info,
+ NULL);
+ if (bcap_dev->sd) {
+ int i;
+ /* update tvnorms from the sub devices */
+ for (i = 0; i < config->num_inputs; i++)
+ vfd->tvnorms |= config->inputs[i].std;
+ } else {
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "Unable to register sub device\n");
+ goto err_unreg_vdev;
+ }
+
+ v4l2_info(&bcap_dev->v4l2_dev, "v4l2 sub device registered\n");
+
+ /* now we can probe the default state */
+ if (vfd->tvnorms) {
+ v4l2_std_id std;
+ ret = v4l2_subdev_call(bcap_dev->sd, core, g_std, &std);
+ if (ret) {
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "Unable to get std\n");
+ goto err_unreg_vdev;
+ }
+ bcap_dev->std = std;
+ }
+ ret = bcap_init_sensor_formats(bcap_dev);
+ if (ret) {
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "Unable to create sensor formats table\n");
+ goto err_unreg_vdev;
+ }
+ return 0;
+err_unreg_vdev:
+ video_unregister_device(bcap_dev->video_dev);
+ bcap_dev->video_dev = NULL;
+err_free_handler:
+ v4l2_ctrl_handler_free(&bcap_dev->ctrl_handler);
+err_unreg_v4l2:
+ v4l2_device_unregister(&bcap_dev->v4l2_dev);
+err_release_vdev:
+ if (bcap_dev->video_dev)
+ video_device_release(bcap_dev->video_dev);
+err_cleanup_ctx:
+ vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx);
+err_free_ppi:
+ ppi_delete_instance(bcap_dev->ppi);
+err_free_dev:
+ kfree(bcap_dev);
+ return ret;
+}
+
+static int __devexit bcap_remove(struct platform_device *pdev)
+{
+ struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
+ struct bcap_device *bcap_dev = container_of(v4l2_dev,
+ struct bcap_device, v4l2_dev);
+
+ bcap_free_sensor_formats(bcap_dev);
+ video_unregister_device(bcap_dev->video_dev);
+ v4l2_ctrl_handler_free(&bcap_dev->ctrl_handler);
+ v4l2_device_unregister(v4l2_dev);
+ vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx);
+ ppi_delete_instance(bcap_dev->ppi);
+ kfree(bcap_dev);
+ return 0;
+}
+
+static struct platform_driver bcap_driver = {
+ .driver = {
+ .name = CAPTURE_DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = bcap_probe,
+ .remove = __devexit_p(bcap_remove),
+};
+
+static __init int bcap_init(void)
+{
+ return platform_driver_register(&bcap_driver);
+}
+
+static __exit void bcap_exit(void)
+{
+ platform_driver_unregister(&bcap_driver);
+}
+
+module_init(bcap_init);
+module_exit(bcap_exit);
+
+MODULE_DESCRIPTION("Analog Devices blackfin video capture driver");
+MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/blackfin/ppi.c b/drivers/media/video/blackfin/ppi.c
new file mode 100644
index 00000000..d2959218
--- /dev/null
+++ b/drivers/media/video/blackfin/ppi.c
@@ -0,0 +1,271 @@
+/*
+ * ppi.c Analog Devices Parallel Peripheral Interface driver
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/slab.h>
+
+#include <asm/bfin_ppi.h>
+#include <asm/blackfin.h>
+#include <asm/cacheflush.h>
+#include <asm/dma.h>
+#include <asm/portmux.h>
+
+#include <media/blackfin/ppi.h>
+
+static int ppi_attach_irq(struct ppi_if *ppi, irq_handler_t handler);
+static void ppi_detach_irq(struct ppi_if *ppi);
+static int ppi_start(struct ppi_if *ppi);
+static int ppi_stop(struct ppi_if *ppi);
+static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params);
+static void ppi_update_addr(struct ppi_if *ppi, unsigned long addr);
+
+static const struct ppi_ops ppi_ops = {
+ .attach_irq = ppi_attach_irq,
+ .detach_irq = ppi_detach_irq,
+ .start = ppi_start,
+ .stop = ppi_stop,
+ .set_params = ppi_set_params,
+ .update_addr = ppi_update_addr,
+};
+
+static irqreturn_t ppi_irq_err(int irq, void *dev_id)
+{
+ struct ppi_if *ppi = dev_id;
+ const struct ppi_info *info = ppi->info;
+
+ switch (info->type) {
+ case PPI_TYPE_PPI:
+ {
+ struct bfin_ppi_regs *reg = info->base;
+ unsigned short status;
+
+ /* register on bf561 is cleared when read
+ * others are W1C
+ */
+ status = bfin_read16(&reg->status);
+ bfin_write16(&reg->status, 0xff00);
+ break;
+ }
+ case PPI_TYPE_EPPI:
+ {
+ struct bfin_eppi_regs *reg = info->base;
+ bfin_write16(&reg->status, 0xffff);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int ppi_attach_irq(struct ppi_if *ppi, irq_handler_t handler)
+{
+ const struct ppi_info *info = ppi->info;
+ int ret;
+
+ ret = request_dma(info->dma_ch, "PPI_DMA");
+
+ if (ret) {
+ pr_err("Unable to allocate DMA channel for PPI\n");
+ return ret;
+ }
+ set_dma_callback(info->dma_ch, handler, ppi);
+
+ if (ppi->err_int) {
+ ret = request_irq(info->irq_err, ppi_irq_err, 0, "PPI ERROR", ppi);
+ if (ret) {
+ pr_err("Unable to allocate IRQ for PPI\n");
+ free_dma(info->dma_ch);
+ }
+ }
+ return ret;
+}
+
+static void ppi_detach_irq(struct ppi_if *ppi)
+{
+ const struct ppi_info *info = ppi->info;
+
+ if (ppi->err_int)
+ free_irq(info->irq_err, ppi);
+ free_dma(info->dma_ch);
+}
+
+static int ppi_start(struct ppi_if *ppi)
+{
+ const struct ppi_info *info = ppi->info;
+
+ /* enable DMA */
+ enable_dma(info->dma_ch);
+
+ /* enable PPI */
+ ppi->ppi_control |= PORT_EN;
+ switch (info->type) {
+ case PPI_TYPE_PPI:
+ {
+ struct bfin_ppi_regs *reg = info->base;
+ bfin_write16(&reg->control, ppi->ppi_control);
+ break;
+ }
+ case PPI_TYPE_EPPI:
+ {
+ struct bfin_eppi_regs *reg = info->base;
+ bfin_write32(&reg->control, ppi->ppi_control);
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ SSYNC();
+ return 0;
+}
+
+static int ppi_stop(struct ppi_if *ppi)
+{
+ const struct ppi_info *info = ppi->info;
+
+ /* disable PPI */
+ ppi->ppi_control &= ~PORT_EN;
+ switch (info->type) {
+ case PPI_TYPE_PPI:
+ {
+ struct bfin_ppi_regs *reg = info->base;
+ bfin_write16(&reg->control, ppi->ppi_control);
+ break;
+ }
+ case PPI_TYPE_EPPI:
+ {
+ struct bfin_eppi_regs *reg = info->base;
+ bfin_write32(&reg->control, ppi->ppi_control);
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ /* disable DMA */
+ clear_dma_irqstat(info->dma_ch);
+ disable_dma(info->dma_ch);
+
+ SSYNC();
+ return 0;
+}
+
+static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params)
+{
+ const struct ppi_info *info = ppi->info;
+ int dma32 = 0;
+ int dma_config, bytes_per_line, lines_per_frame;
+
+ bytes_per_line = params->width * params->bpp / 8;
+ lines_per_frame = params->height;
+ if (params->int_mask == 0xFFFFFFFF)
+ ppi->err_int = false;
+ else
+ ppi->err_int = true;
+
+ dma_config = (DMA_FLOW_STOP | WNR | RESTART | DMA2D | DI_EN);
+ ppi->ppi_control = params->ppi_control & ~PORT_EN;
+ switch (info->type) {
+ case PPI_TYPE_PPI:
+ {
+ struct bfin_ppi_regs *reg = info->base;
+
+ if (params->ppi_control & DMA32)
+ dma32 = 1;
+
+ bfin_write16(&reg->control, ppi->ppi_control);
+ bfin_write16(&reg->count, bytes_per_line - 1);
+ bfin_write16(&reg->frame, lines_per_frame);
+ break;
+ }
+ case PPI_TYPE_EPPI:
+ {
+ struct bfin_eppi_regs *reg = info->base;
+
+ if ((params->ppi_control & PACK_EN)
+ || (params->ppi_control & 0x38000) > DLEN_16)
+ dma32 = 1;
+
+ bfin_write32(&reg->control, ppi->ppi_control);
+ bfin_write16(&reg->line, bytes_per_line + params->blank_clocks);
+ bfin_write16(&reg->frame, lines_per_frame);
+ bfin_write16(&reg->hdelay, 0);
+ bfin_write16(&reg->vdelay, 0);
+ bfin_write16(&reg->hcount, bytes_per_line);
+ bfin_write16(&reg->vcount, lines_per_frame);
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ if (dma32) {
+ dma_config |= WDSIZE_32;
+ set_dma_x_count(info->dma_ch, bytes_per_line >> 2);
+ set_dma_x_modify(info->dma_ch, 4);
+ set_dma_y_modify(info->dma_ch, 4);
+ } else {
+ dma_config |= WDSIZE_16;
+ set_dma_x_count(info->dma_ch, bytes_per_line >> 1);
+ set_dma_x_modify(info->dma_ch, 2);
+ set_dma_y_modify(info->dma_ch, 2);
+ }
+ set_dma_y_count(info->dma_ch, lines_per_frame);
+ set_dma_config(info->dma_ch, dma_config);
+
+ SSYNC();
+ return 0;
+}
+
+static void ppi_update_addr(struct ppi_if *ppi, unsigned long addr)
+{
+ set_dma_start_addr(ppi->info->dma_ch, addr);
+}
+
+struct ppi_if *ppi_create_instance(const struct ppi_info *info)
+{
+ struct ppi_if *ppi;
+
+ if (!info || !info->pin_req)
+ return NULL;
+
+ if (peripheral_request_list(info->pin_req, KBUILD_MODNAME)) {
+ pr_err("request peripheral failed\n");
+ return NULL;
+ }
+
+ ppi = kzalloc(sizeof(*ppi), GFP_KERNEL);
+ if (!ppi) {
+ peripheral_free_list(info->pin_req);
+ pr_err("unable to allocate memory for ppi handle\n");
+ return NULL;
+ }
+ ppi->ops = &ppi_ops;
+ ppi->info = info;
+
+ pr_info("ppi probe success\n");
+ return ppi;
+}
+
+void ppi_delete_instance(struct ppi_if *ppi)
+{
+ peripheral_free_list(ppi->info->pin_req);
+ kfree(ppi);
+}
diff --git a/drivers/media/video/bt819.c b/drivers/media/video/bt819.c
new file mode 100644
index 00000000..377bf05b
--- /dev/null
+++ b/drivers/media/video/bt819.c
@@ -0,0 +1,517 @@
+/*
+ * bt819 - BT819A VideoStream Decoder (Rockwell Part)
+ *
+ * Copyright (C) 1999 Mike Bernson <mike@mlb.org>
+ * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+ *
+ * Modifications for LML33/DC10plus unified driver
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ *
+ * Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * - moved over to linux>=2.4.x i2c protocol (9/9/2002)
+ *
+ * This code was modify/ported from the saa7111 driver written
+ * by Dave Perks.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+#include <media/bt819.h>
+
+MODULE_DESCRIPTION("Brooktree-819 video decoder driver");
+MODULE_AUTHOR("Mike Bernson & Dave Perks");
+MODULE_LICENSE("GPL");
+
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+
+/* ----------------------------------------------------------------------- */
+
+struct bt819 {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+ unsigned char reg[32];
+
+ v4l2_std_id norm;
+ int ident;
+ int input;
+ int enable;
+};
+
+static inline struct bt819 *to_bt819(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct bt819, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct bt819, hdl)->sd;
+}
+
+struct timing {
+ int hactive;
+ int hdelay;
+ int vactive;
+ int vdelay;
+ int hscale;
+ int vscale;
+};
+
+/* for values, see the bt819 datasheet */
+static struct timing timing_data[] = {
+ {864 - 24, 20, 625 - 2, 1, 0x0504, 0x0000},
+ {858 - 24, 20, 525 - 2, 1, 0x00f8, 0x0000},
+};
+
+/* ----------------------------------------------------------------------- */
+
+static inline int bt819_write(struct bt819 *decoder, u8 reg, u8 value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd);
+
+ decoder->reg[reg] = value;
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static inline int bt819_setbit(struct bt819 *decoder, u8 reg, u8 bit, u8 value)
+{
+ return bt819_write(decoder, reg,
+ (decoder->reg[reg] & ~(1 << bit)) | (value ? (1 << bit) : 0));
+}
+
+static int bt819_write_block(struct bt819 *decoder, const u8 *data, unsigned int len)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd);
+ int ret = -1;
+ u8 reg;
+
+ /* the bt819 has an autoincrement function, use it if
+ * the adapter understands raw I2C */
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ /* do raw I2C, not smbus compatible */
+ u8 block_data[32];
+ int block_len;
+
+ while (len >= 2) {
+ block_len = 0;
+ block_data[block_len++] = reg = data[0];
+ do {
+ block_data[block_len++] =
+ decoder->reg[reg++] = data[1];
+ len -= 2;
+ data += 2;
+ } while (len >= 2 && data[0] == reg && block_len < 32);
+ ret = i2c_master_send(client, block_data, block_len);
+ if (ret < 0)
+ break;
+ }
+ } else {
+ /* do some slow I2C emulation kind of thing */
+ while (len >= 2) {
+ reg = *data++;
+ ret = bt819_write(decoder, reg, *data++);
+ if (ret < 0)
+ break;
+ len -= 2;
+ }
+ }
+
+ return ret;
+}
+
+static inline int bt819_read(struct bt819 *decoder, u8 reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd);
+
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int bt819_init(struct v4l2_subdev *sd)
+{
+ static unsigned char init[] = {
+ /*0x1f, 0x00,*/ /* Reset */
+ 0x01, 0x59, /* 0x01 input format */
+ 0x02, 0x00, /* 0x02 temporal decimation */
+ 0x03, 0x12, /* 0x03 Cropping msb */
+ 0x04, 0x16, /* 0x04 Vertical Delay, lsb */
+ 0x05, 0xe0, /* 0x05 Vertical Active lsb */
+ 0x06, 0x80, /* 0x06 Horizontal Delay lsb */
+ 0x07, 0xd0, /* 0x07 Horizontal Active lsb */
+ 0x08, 0x00, /* 0x08 Horizontal Scaling msb */
+ 0x09, 0xf8, /* 0x09 Horizontal Scaling lsb */
+ 0x0a, 0x00, /* 0x0a Brightness control */
+ 0x0b, 0x30, /* 0x0b Miscellaneous control */
+ 0x0c, 0xd8, /* 0x0c Luma Gain lsb */
+ 0x0d, 0xfe, /* 0x0d Chroma Gain (U) lsb */
+ 0x0e, 0xb4, /* 0x0e Chroma Gain (V) msb */
+ 0x0f, 0x00, /* 0x0f Hue control */
+ 0x12, 0x04, /* 0x12 Output Format */
+ 0x13, 0x20, /* 0x13 Vertial Scaling msb 0x00
+ chroma comb OFF, line drop scaling, interlace scaling
+ BUG? Why does turning the chroma comb on fuck up color?
+ Bug in the bt819 stepping on my board?
+ */
+ 0x14, 0x00, /* 0x14 Vertial Scaling lsb */
+ 0x16, 0x07, /* 0x16 Video Timing Polarity
+ ACTIVE=active low
+ FIELD: high=odd,
+ vreset=active high,
+ hreset=active high */
+ 0x18, 0x68, /* 0x18 AGC Delay */
+ 0x19, 0x5d, /* 0x19 Burst Gate Delay */
+ 0x1a, 0x80, /* 0x1a ADC Interface */
+ };
+
+ struct bt819 *decoder = to_bt819(sd);
+ struct timing *timing = &timing_data[(decoder->norm & V4L2_STD_525_60) ? 1 : 0];
+
+ init[0x03 * 2 - 1] =
+ (((timing->vdelay >> 8) & 0x03) << 6) |
+ (((timing->vactive >> 8) & 0x03) << 4) |
+ (((timing->hdelay >> 8) & 0x03) << 2) |
+ ((timing->hactive >> 8) & 0x03);
+ init[0x04 * 2 - 1] = timing->vdelay & 0xff;
+ init[0x05 * 2 - 1] = timing->vactive & 0xff;
+ init[0x06 * 2 - 1] = timing->hdelay & 0xff;
+ init[0x07 * 2 - 1] = timing->hactive & 0xff;
+ init[0x08 * 2 - 1] = timing->hscale >> 8;
+ init[0x09 * 2 - 1] = timing->hscale & 0xff;
+ /* 0x15 in array is address 0x19 */
+ init[0x15 * 2 - 1] = (decoder->norm & V4L2_STD_625_50) ? 115 : 93; /* Chroma burst delay */
+ /* reset */
+ bt819_write(decoder, 0x1f, 0x00);
+ mdelay(1);
+
+ /* init */
+ return bt819_write_block(decoder, init, sizeof(init));
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int bt819_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd)
+{
+ struct bt819 *decoder = to_bt819(sd);
+ int status = bt819_read(decoder, 0x00);
+ int res = V4L2_IN_ST_NO_SIGNAL;
+ v4l2_std_id std;
+
+ if ((status & 0x80))
+ res = 0;
+
+ if ((status & 0x10))
+ std = V4L2_STD_PAL;
+ else
+ std = V4L2_STD_NTSC;
+ if (pstd)
+ *pstd = std;
+ if (pstatus)
+ *pstatus = res;
+
+ v4l2_dbg(1, debug, sd, "get status %x\n", status);
+ return 0;
+}
+
+static int bt819_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ return bt819_status(sd, NULL, std);
+}
+
+static int bt819_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+ return bt819_status(sd, status, NULL);
+}
+
+static int bt819_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct bt819 *decoder = to_bt819(sd);
+ struct timing *timing = NULL;
+
+ v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std);
+
+ if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL)
+ v4l2_err(sd, "no notify found!\n");
+
+ if (std & V4L2_STD_NTSC) {
+ v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL);
+ bt819_setbit(decoder, 0x01, 0, 1);
+ bt819_setbit(decoder, 0x01, 1, 0);
+ bt819_setbit(decoder, 0x01, 5, 0);
+ bt819_write(decoder, 0x18, 0x68);
+ bt819_write(decoder, 0x19, 0x5d);
+ /* bt819_setbit(decoder, 0x1a, 5, 1); */
+ timing = &timing_data[1];
+ } else if (std & V4L2_STD_PAL) {
+ v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL);
+ bt819_setbit(decoder, 0x01, 0, 1);
+ bt819_setbit(decoder, 0x01, 1, 1);
+ bt819_setbit(decoder, 0x01, 5, 1);
+ bt819_write(decoder, 0x18, 0x7f);
+ bt819_write(decoder, 0x19, 0x72);
+ /* bt819_setbit(decoder, 0x1a, 5, 0); */
+ timing = &timing_data[0];
+ } else {
+ v4l2_dbg(1, debug, sd, "unsupported norm %llx\n",
+ (unsigned long long)std);
+ return -EINVAL;
+ }
+ bt819_write(decoder, 0x03,
+ (((timing->vdelay >> 8) & 0x03) << 6) |
+ (((timing->vactive >> 8) & 0x03) << 4) |
+ (((timing->hdelay >> 8) & 0x03) << 2) |
+ ((timing->hactive >> 8) & 0x03));
+ bt819_write(decoder, 0x04, timing->vdelay & 0xff);
+ bt819_write(decoder, 0x05, timing->vactive & 0xff);
+ bt819_write(decoder, 0x06, timing->hdelay & 0xff);
+ bt819_write(decoder, 0x07, timing->hactive & 0xff);
+ bt819_write(decoder, 0x08, (timing->hscale >> 8) & 0xff);
+ bt819_write(decoder, 0x09, timing->hscale & 0xff);
+ decoder->norm = std;
+ v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, NULL);
+ return 0;
+}
+
+static int bt819_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct bt819 *decoder = to_bt819(sd);
+
+ v4l2_dbg(1, debug, sd, "set input %x\n", input);
+
+ if (input > 7)
+ return -EINVAL;
+
+ if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL)
+ v4l2_err(sd, "no notify found!\n");
+
+ if (decoder->input != input) {
+ v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL);
+ decoder->input = input;
+ /* select mode */
+ if (decoder->input == 0) {
+ bt819_setbit(decoder, 0x0b, 6, 0);
+ bt819_setbit(decoder, 0x1a, 1, 1);
+ } else {
+ bt819_setbit(decoder, 0x0b, 6, 1);
+ bt819_setbit(decoder, 0x1a, 1, 0);
+ }
+ v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, NULL);
+ }
+ return 0;
+}
+
+static int bt819_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct bt819 *decoder = to_bt819(sd);
+
+ v4l2_dbg(1, debug, sd, "enable output %x\n", enable);
+
+ if (decoder->enable != enable) {
+ decoder->enable = enable;
+ bt819_setbit(decoder, 0x16, 7, !enable);
+ }
+ return 0;
+}
+
+static int bt819_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ struct bt819 *decoder = to_bt819(sd);
+ int temp;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ bt819_write(decoder, 0x0a, ctrl->val);
+ break;
+
+ case V4L2_CID_CONTRAST:
+ bt819_write(decoder, 0x0c, ctrl->val & 0xff);
+ bt819_setbit(decoder, 0x0b, 2, ((ctrl->val >> 8) & 0x01));
+ break;
+
+ case V4L2_CID_SATURATION:
+ bt819_write(decoder, 0x0d, (ctrl->val >> 7) & 0xff);
+ bt819_setbit(decoder, 0x0b, 1, ((ctrl->val >> 15) & 0x01));
+
+ /* Ratio between U gain and V gain must stay the same as
+ the ratio between the default U and V gain values. */
+ temp = (ctrl->val * 180) / 254;
+ bt819_write(decoder, 0x0e, (temp >> 7) & 0xff);
+ bt819_setbit(decoder, 0x0b, 0, (temp >> 15) & 0x01);
+ break;
+
+ case V4L2_CID_HUE:
+ bt819_write(decoder, 0x0f, ctrl->val);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int bt819_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct bt819 *decoder = to_bt819(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, decoder->ident, 0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_ctrl_ops bt819_ctrl_ops = {
+ .s_ctrl = bt819_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops bt819_core_ops = {
+ .g_chip_ident = bt819_g_chip_ident,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+ .s_std = bt819_s_std,
+};
+
+static const struct v4l2_subdev_video_ops bt819_video_ops = {
+ .s_routing = bt819_s_routing,
+ .s_stream = bt819_s_stream,
+ .querystd = bt819_querystd,
+ .g_input_status = bt819_g_input_status,
+};
+
+static const struct v4l2_subdev_ops bt819_ops = {
+ .core = &bt819_core_ops,
+ .video = &bt819_video_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int bt819_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int i, ver;
+ struct bt819 *decoder;
+ struct v4l2_subdev *sd;
+ const char *name;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ decoder = kzalloc(sizeof(struct bt819), GFP_KERNEL);
+ if (decoder == NULL)
+ return -ENOMEM;
+ sd = &decoder->sd;
+ v4l2_i2c_subdev_init(sd, client, &bt819_ops);
+
+ ver = bt819_read(decoder, 0x17);
+ switch (ver & 0xf0) {
+ case 0x70:
+ name = "bt819a";
+ decoder->ident = V4L2_IDENT_BT819A;
+ break;
+ case 0x60:
+ name = "bt817a";
+ decoder->ident = V4L2_IDENT_BT817A;
+ break;
+ case 0x20:
+ name = "bt815a";
+ decoder->ident = V4L2_IDENT_BT815A;
+ break;
+ default:
+ v4l2_dbg(1, debug, sd,
+ "unknown chip version 0x%02x\n", ver);
+ return -ENODEV;
+ }
+
+ v4l_info(client, "%s found @ 0x%x (%s)\n", name,
+ client->addr << 1, client->adapter->name);
+
+ decoder->norm = V4L2_STD_NTSC;
+ decoder->input = 0;
+ decoder->enable = 1;
+
+ i = bt819_init(sd);
+ if (i < 0)
+ v4l2_dbg(1, debug, sd, "init status %d\n", i);
+
+ v4l2_ctrl_handler_init(&decoder->hdl, 4);
+ v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
+ v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 511, 1, 0xd8);
+ v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 511, 1, 0xfe);
+ v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops,
+ V4L2_CID_HUE, -128, 127, 1, 0);
+ sd->ctrl_handler = &decoder->hdl;
+ if (decoder->hdl.error) {
+ int err = decoder->hdl.error;
+
+ v4l2_ctrl_handler_free(&decoder->hdl);
+ kfree(decoder);
+ return err;
+ }
+ v4l2_ctrl_handler_setup(&decoder->hdl);
+ return 0;
+}
+
+static int bt819_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct bt819 *decoder = to_bt819(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&decoder->hdl);
+ kfree(decoder);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct i2c_device_id bt819_id[] = {
+ { "bt819a", 0 },
+ { "bt817a", 0 },
+ { "bt815a", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, bt819_id);
+
+static struct i2c_driver bt819_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "bt819",
+ },
+ .probe = bt819_probe,
+ .remove = bt819_remove,
+ .id_table = bt819_id,
+};
+
+module_i2c_driver(bt819_driver);
diff --git a/drivers/media/video/bt856.c b/drivers/media/video/bt856.c
new file mode 100644
index 00000000..7e5bd365
--- /dev/null
+++ b/drivers/media/video/bt856.c
@@ -0,0 +1,273 @@
+/*
+ * bt856 - BT856A Digital Video Encoder (Rockwell Part)
+ *
+ * Copyright (C) 1999 Mike Bernson <mike@mlb.org>
+ * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+ *
+ * Modifications for LML33/DC10plus unified driver
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ *
+ * This code was modify/ported from the saa7111 driver written
+ * by Dave Perks.
+ *
+ * Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * - moved over to linux>=2.4.x i2c protocol (9/9/2002)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/ioctl.h>
+#include <asm/uaccess.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+
+MODULE_DESCRIPTION("Brooktree-856A video encoder driver");
+MODULE_AUTHOR("Mike Bernson & Dave Perks");
+MODULE_LICENSE("GPL");
+
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+
+/* ----------------------------------------------------------------------- */
+
+#define BT856_REG_OFFSET 0xDA
+#define BT856_NR_REG 6
+
+struct bt856 {
+ struct v4l2_subdev sd;
+ unsigned char reg[BT856_NR_REG];
+
+ v4l2_std_id norm;
+};
+
+static inline struct bt856 *to_bt856(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct bt856, sd);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static inline int bt856_write(struct bt856 *encoder, u8 reg, u8 value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&encoder->sd);
+
+ encoder->reg[reg - BT856_REG_OFFSET] = value;
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static inline int bt856_setbit(struct bt856 *encoder, u8 reg, u8 bit, u8 value)
+{
+ return bt856_write(encoder, reg,
+ (encoder->reg[reg - BT856_REG_OFFSET] & ~(1 << bit)) |
+ (value ? (1 << bit) : 0));
+}
+
+static void bt856_dump(struct bt856 *encoder)
+{
+ int i;
+
+ v4l2_info(&encoder->sd, "register dump:\n");
+ for (i = 0; i < BT856_NR_REG; i += 2)
+ printk(KERN_CONT " %02x", encoder->reg[i]);
+ printk(KERN_CONT "\n");
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int bt856_init(struct v4l2_subdev *sd, u32 arg)
+{
+ struct bt856 *encoder = to_bt856(sd);
+
+ /* This is just for testing!!! */
+ v4l2_dbg(1, debug, sd, "init\n");
+ bt856_write(encoder, 0xdc, 0x18);
+ bt856_write(encoder, 0xda, 0);
+ bt856_write(encoder, 0xde, 0);
+
+ bt856_setbit(encoder, 0xdc, 3, 1);
+ /*bt856_setbit(encoder, 0xdc, 6, 0);*/
+ bt856_setbit(encoder, 0xdc, 4, 1);
+
+ if (encoder->norm & V4L2_STD_NTSC)
+ bt856_setbit(encoder, 0xdc, 2, 0);
+ else
+ bt856_setbit(encoder, 0xdc, 2, 1);
+
+ bt856_setbit(encoder, 0xdc, 1, 1);
+ bt856_setbit(encoder, 0xde, 4, 0);
+ bt856_setbit(encoder, 0xde, 3, 1);
+ if (debug != 0)
+ bt856_dump(encoder);
+ return 0;
+}
+
+static int bt856_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct bt856 *encoder = to_bt856(sd);
+
+ v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std);
+
+ if (std & V4L2_STD_NTSC) {
+ bt856_setbit(encoder, 0xdc, 2, 0);
+ } else if (std & V4L2_STD_PAL) {
+ bt856_setbit(encoder, 0xdc, 2, 1);
+ bt856_setbit(encoder, 0xda, 0, 0);
+ /*bt856_setbit(encoder, 0xda, 0, 1);*/
+ } else {
+ return -EINVAL;
+ }
+ encoder->norm = std;
+ if (debug != 0)
+ bt856_dump(encoder);
+ return 0;
+}
+
+static int bt856_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct bt856 *encoder = to_bt856(sd);
+
+ v4l2_dbg(1, debug, sd, "set input %d\n", input);
+
+ /* We only have video bus.
+ * input= 0: input is from bt819
+ * input= 1: input is from ZR36060 */
+ switch (input) {
+ case 0:
+ bt856_setbit(encoder, 0xde, 4, 0);
+ bt856_setbit(encoder, 0xde, 3, 1);
+ bt856_setbit(encoder, 0xdc, 3, 1);
+ bt856_setbit(encoder, 0xdc, 6, 0);
+ break;
+ case 1:
+ bt856_setbit(encoder, 0xde, 4, 0);
+ bt856_setbit(encoder, 0xde, 3, 1);
+ bt856_setbit(encoder, 0xdc, 3, 1);
+ bt856_setbit(encoder, 0xdc, 6, 1);
+ break;
+ case 2: /* Color bar */
+ bt856_setbit(encoder, 0xdc, 3, 0);
+ bt856_setbit(encoder, 0xde, 4, 1);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (debug != 0)
+ bt856_dump(encoder);
+ return 0;
+}
+
+static int bt856_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_BT856, 0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops bt856_core_ops = {
+ .g_chip_ident = bt856_g_chip_ident,
+ .init = bt856_init,
+};
+
+static const struct v4l2_subdev_video_ops bt856_video_ops = {
+ .s_std_output = bt856_s_std_output,
+ .s_routing = bt856_s_routing,
+};
+
+static const struct v4l2_subdev_ops bt856_ops = {
+ .core = &bt856_core_ops,
+ .video = &bt856_video_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int bt856_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct bt856 *encoder;
+ struct v4l2_subdev *sd;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ encoder = kzalloc(sizeof(struct bt856), GFP_KERNEL);
+ if (encoder == NULL)
+ return -ENOMEM;
+ sd = &encoder->sd;
+ v4l2_i2c_subdev_init(sd, client, &bt856_ops);
+ encoder->norm = V4L2_STD_NTSC;
+
+ bt856_write(encoder, 0xdc, 0x18);
+ bt856_write(encoder, 0xda, 0);
+ bt856_write(encoder, 0xde, 0);
+
+ bt856_setbit(encoder, 0xdc, 3, 1);
+ /*bt856_setbit(encoder, 0xdc, 6, 0);*/
+ bt856_setbit(encoder, 0xdc, 4, 1);
+
+ if (encoder->norm & V4L2_STD_NTSC)
+ bt856_setbit(encoder, 0xdc, 2, 0);
+ else
+ bt856_setbit(encoder, 0xdc, 2, 1);
+
+ bt856_setbit(encoder, 0xdc, 1, 1);
+ bt856_setbit(encoder, 0xde, 4, 0);
+ bt856_setbit(encoder, 0xde, 3, 1);
+
+ if (debug != 0)
+ bt856_dump(encoder);
+ return 0;
+}
+
+static int bt856_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_bt856(sd));
+ return 0;
+}
+
+static const struct i2c_device_id bt856_id[] = {
+ { "bt856", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, bt856_id);
+
+static struct i2c_driver bt856_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "bt856",
+ },
+ .probe = bt856_probe,
+ .remove = bt856_remove,
+ .id_table = bt856_id,
+};
+
+module_i2c_driver(bt856_driver);
diff --git a/drivers/media/video/bt866.c b/drivers/media/video/bt866.c
new file mode 100644
index 00000000..905320b6
--- /dev/null
+++ b/drivers/media/video/bt866.c
@@ -0,0 +1,243 @@
+/*
+ bt866 - BT866 Digital Video Encoder (Rockwell Part)
+
+ Copyright (C) 1999 Mike Bernson <mike@mlb.org>
+ Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+
+ Modifications for LML33/DC10plus unified driver
+ Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+
+ This code was modify/ported from the saa7111 driver written
+ by Dave Perks.
+
+ This code was adapted for the bt866 by Christer Weinigel and ported
+ to 2.6 by Martin Samuelsson.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/ioctl.h>
+#include <asm/uaccess.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+
+MODULE_DESCRIPTION("Brooktree-866 video encoder driver");
+MODULE_AUTHOR("Mike Bernson & Dave Perks");
+MODULE_LICENSE("GPL");
+
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+
+/* ----------------------------------------------------------------------- */
+
+struct bt866 {
+ struct v4l2_subdev sd;
+ u8 reg[256];
+};
+
+static inline struct bt866 *to_bt866(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct bt866, sd);
+}
+
+static int bt866_write(struct bt866 *encoder, u8 subaddr, u8 data)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&encoder->sd);
+ u8 buffer[2];
+ int err;
+
+ buffer[0] = subaddr;
+ buffer[1] = data;
+
+ encoder->reg[subaddr] = data;
+
+ v4l_dbg(1, debug, client, "write 0x%02x = 0x%02x\n", subaddr, data);
+
+ for (err = 0; err < 3;) {
+ if (i2c_master_send(client, buffer, 2) == 2)
+ break;
+ err++;
+ v4l_warn(client, "error #%d writing to 0x%02x\n",
+ err, subaddr);
+ schedule_timeout_interruptible(msecs_to_jiffies(100));
+ }
+ if (err == 3) {
+ v4l_warn(client, "giving up\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int bt866_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std);
+
+ /* Only PAL supported by this driver at the moment! */
+ if (!(std & V4L2_STD_NTSC))
+ return -EINVAL;
+ return 0;
+}
+
+static int bt866_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ static const __u8 init[] = {
+ 0xc8, 0xcc, /* CRSCALE */
+ 0xca, 0x91, /* CBSCALE */
+ 0xcc, 0x24, /* YC16 | OSDNUM */
+ 0xda, 0x00, /* */
+ 0xdc, 0x24, /* SETMODE | PAL */
+ 0xde, 0x02, /* EACTIVE */
+
+ /* overlay colors */
+ 0x70, 0xEB, 0x90, 0x80, 0xB0, 0x80, /* white */
+ 0x72, 0xA2, 0x92, 0x8E, 0xB2, 0x2C, /* yellow */
+ 0x74, 0x83, 0x94, 0x2C, 0xB4, 0x9C, /* cyan */
+ 0x76, 0x70, 0x96, 0x3A, 0xB6, 0x48, /* green */
+ 0x78, 0x54, 0x98, 0xC6, 0xB8, 0xB8, /* magenta */
+ 0x7A, 0x41, 0x9A, 0xD4, 0xBA, 0x64, /* red */
+ 0x7C, 0x23, 0x9C, 0x72, 0xBC, 0xD4, /* blue */
+ 0x7E, 0x10, 0x9E, 0x80, 0xBE, 0x80, /* black */
+
+ 0x60, 0xEB, 0x80, 0x80, 0xc0, 0x80, /* white */
+ 0x62, 0xA2, 0x82, 0x8E, 0xc2, 0x2C, /* yellow */
+ 0x64, 0x83, 0x84, 0x2C, 0xc4, 0x9C, /* cyan */
+ 0x66, 0x70, 0x86, 0x3A, 0xc6, 0x48, /* green */
+ 0x68, 0x54, 0x88, 0xC6, 0xc8, 0xB8, /* magenta */
+ 0x6A, 0x41, 0x8A, 0xD4, 0xcA, 0x64, /* red */
+ 0x6C, 0x23, 0x8C, 0x72, 0xcC, 0xD4, /* blue */
+ 0x6E, 0x10, 0x8E, 0x80, 0xcE, 0x80, /* black */
+ };
+ struct bt866 *encoder = to_bt866(sd);
+ u8 val;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(init) / 2; i += 2)
+ bt866_write(encoder, init[i], init[i+1]);
+
+ val = encoder->reg[0xdc];
+
+ if (input == 0)
+ val |= 0x40; /* CBSWAP */
+ else
+ val &= ~0x40; /* !CBSWAP */
+
+ bt866_write(encoder, 0xdc, val);
+
+ val = encoder->reg[0xcc];
+ if (input == 2)
+ val |= 0x01; /* OSDBAR */
+ else
+ val &= ~0x01; /* !OSDBAR */
+ bt866_write(encoder, 0xcc, val);
+
+ v4l2_dbg(1, debug, sd, "set input %d\n", input);
+
+ switch (input) {
+ case 0:
+ case 1:
+ case 2:
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+#if 0
+/* Code to setup square pixels, might be of some use in the future,
+ but is currently unused. */
+ val = encoder->reg[0xdc];
+ if (*iarg)
+ val |= 1; /* SQUARE */
+ else
+ val &= ~1; /* !SQUARE */
+ bt866_write(client, 0xdc, val);
+#endif
+
+static int bt866_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_BT866, 0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops bt866_core_ops = {
+ .g_chip_ident = bt866_g_chip_ident,
+};
+
+static const struct v4l2_subdev_video_ops bt866_video_ops = {
+ .s_std_output = bt866_s_std_output,
+ .s_routing = bt866_s_routing,
+};
+
+static const struct v4l2_subdev_ops bt866_ops = {
+ .core = &bt866_core_ops,
+ .video = &bt866_video_ops,
+};
+
+static int bt866_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct bt866 *encoder;
+ struct v4l2_subdev *sd;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ encoder = kzalloc(sizeof(*encoder), GFP_KERNEL);
+ if (encoder == NULL)
+ return -ENOMEM;
+ sd = &encoder->sd;
+ v4l2_i2c_subdev_init(sd, client, &bt866_ops);
+ return 0;
+}
+
+static int bt866_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_bt866(sd));
+ return 0;
+}
+
+static const struct i2c_device_id bt866_id[] = {
+ { "bt866", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, bt866_id);
+
+static struct i2c_driver bt866_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "bt866",
+ },
+ .probe = bt866_probe,
+ .remove = bt866_remove,
+ .id_table = bt866_id,
+};
+
+module_i2c_driver(bt866_driver);
diff --git a/drivers/media/video/bt8xx/Kconfig b/drivers/media/video/bt8xx/Kconfig
new file mode 100644
index 00000000..7da5c2e1
--- /dev/null
+++ b/drivers/media/video/bt8xx/Kconfig
@@ -0,0 +1,27 @@
+config VIDEO_BT848
+ tristate "BT848 Video For Linux"
+ depends on VIDEO_DEV && PCI && I2C && VIDEO_V4L2
+ select I2C_ALGOBIT
+ select VIDEO_BTCX
+ select VIDEOBUF_DMA_SG
+ depends on RC_CORE
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_TVAUDIO if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_TDA7432 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_SAA6588 if VIDEO_HELPER_CHIPS_AUTO
+ ---help---
+ Support for BT848 based frame grabber/overlay boards. This includes
+ the Miro, Hauppauge and STB boards. Please read the material in
+ <file:Documentation/video4linux/bttv/> for more information.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bttv.
+
+config VIDEO_BT848_DVB
+ bool "DVB/ATSC Support for bt878 based TV cards"
+ depends on VIDEO_BT848 && DVB_CORE
+ select DVB_BT8XX
+ ---help---
+ This adds support for DVB/ATSC cards based on the BT878 chip.
diff --git a/drivers/media/video/bt8xx/Makefile b/drivers/media/video/bt8xx/Makefile
new file mode 100644
index 00000000..3f9a2b22
--- /dev/null
+++ b/drivers/media/video/bt8xx/Makefile
@@ -0,0 +1,13 @@
+#
+# Makefile for the video capture/playback device drivers.
+#
+
+bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o \
+ bttv-risc.o bttv-vbi.o bttv-i2c.o bttv-gpio.o \
+ bttv-input.o bttv-audio-hook.o
+
+obj-$(CONFIG_VIDEO_BT848) += bttv.o
+
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
diff --git a/drivers/media/video/bt8xx/bt848.h b/drivers/media/video/bt8xx/bt848.h
new file mode 100644
index 00000000..c37e6acf
--- /dev/null
+++ b/drivers/media/video/bt8xx/bt848.h
@@ -0,0 +1,369 @@
+/*
+ bt848.h - Bt848 register offsets
+
+ Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _BT848_H_
+#define _BT848_H_
+
+#ifndef PCI_VENDOR_ID_BROOKTREE
+#define PCI_VENDOR_ID_BROOKTREE 0x109e
+#endif
+#ifndef PCI_DEVICE_ID_BT848
+#define PCI_DEVICE_ID_BT848 0x350
+#endif
+#ifndef PCI_DEVICE_ID_BT849
+#define PCI_DEVICE_ID_BT849 0x351
+#endif
+#ifndef PCI_DEVICE_ID_FUSION879
+#define PCI_DEVICE_ID_FUSION879 0x36c
+#endif
+
+#ifndef PCI_DEVICE_ID_BT878
+#define PCI_DEVICE_ID_BT878 0x36e
+#endif
+#ifndef PCI_DEVICE_ID_BT879
+#define PCI_DEVICE_ID_BT879 0x36f
+#endif
+
+/* Brooktree 848 registers */
+
+#define BT848_DSTATUS 0x000
+#define BT848_DSTATUS_PRES (1<<7)
+#define BT848_DSTATUS_HLOC (1<<6)
+#define BT848_DSTATUS_FIELD (1<<5)
+#define BT848_DSTATUS_NUML (1<<4)
+#define BT848_DSTATUS_CSEL (1<<3)
+#define BT848_DSTATUS_PLOCK (1<<2)
+#define BT848_DSTATUS_LOF (1<<1)
+#define BT848_DSTATUS_COF (1<<0)
+
+#define BT848_IFORM 0x004
+#define BT848_IFORM_HACTIVE (1<<7)
+#define BT848_IFORM_MUXSEL (3<<5)
+#define BT848_IFORM_MUX0 (2<<5)
+#define BT848_IFORM_MUX1 (3<<5)
+#define BT848_IFORM_MUX2 (1<<5)
+#define BT848_IFORM_XTSEL (3<<3)
+#define BT848_IFORM_XT0 (1<<3)
+#define BT848_IFORM_XT1 (2<<3)
+#define BT848_IFORM_XTAUTO (3<<3)
+#define BT848_IFORM_XTBOTH (3<<3)
+#define BT848_IFORM_NTSC 1
+#define BT848_IFORM_NTSC_J 2
+#define BT848_IFORM_PAL_BDGHI 3
+#define BT848_IFORM_PAL_M 4
+#define BT848_IFORM_PAL_N 5
+#define BT848_IFORM_SECAM 6
+#define BT848_IFORM_PAL_NC 7
+#define BT848_IFORM_AUTO 0
+#define BT848_IFORM_NORM 7
+
+#define BT848_TDEC 0x008
+#define BT848_TDEC_DEC_FIELD (1<<7)
+#define BT848_TDEC_FLDALIGN (1<<6)
+#define BT848_TDEC_DEC_RAT (0x1f)
+
+#define BT848_E_CROP 0x00C
+#define BT848_O_CROP 0x08C
+
+#define BT848_E_VDELAY_LO 0x010
+#define BT848_O_VDELAY_LO 0x090
+
+#define BT848_E_VACTIVE_LO 0x014
+#define BT848_O_VACTIVE_LO 0x094
+
+#define BT848_E_HDELAY_LO 0x018
+#define BT848_O_HDELAY_LO 0x098
+
+#define BT848_E_HACTIVE_LO 0x01C
+#define BT848_O_HACTIVE_LO 0x09C
+
+#define BT848_E_HSCALE_HI 0x020
+#define BT848_O_HSCALE_HI 0x0A0
+
+#define BT848_E_HSCALE_LO 0x024
+#define BT848_O_HSCALE_LO 0x0A4
+
+#define BT848_BRIGHT 0x028
+
+#define BT848_E_CONTROL 0x02C
+#define BT848_O_CONTROL 0x0AC
+#define BT848_CONTROL_LNOTCH (1<<7)
+#define BT848_CONTROL_COMP (1<<6)
+#define BT848_CONTROL_LDEC (1<<5)
+#define BT848_CONTROL_CBSENSE (1<<4)
+#define BT848_CONTROL_CON_MSB (1<<2)
+#define BT848_CONTROL_SAT_U_MSB (1<<1)
+#define BT848_CONTROL_SAT_V_MSB (1<<0)
+
+#define BT848_CONTRAST_LO 0x030
+#define BT848_SAT_U_LO 0x034
+#define BT848_SAT_V_LO 0x038
+#define BT848_HUE 0x03C
+
+#define BT848_E_SCLOOP 0x040
+#define BT848_O_SCLOOP 0x0C0
+#define BT848_SCLOOP_CAGC (1<<6)
+#define BT848_SCLOOP_CKILL (1<<5)
+#define BT848_SCLOOP_HFILT_AUTO (0<<3)
+#define BT848_SCLOOP_HFILT_CIF (1<<3)
+#define BT848_SCLOOP_HFILT_QCIF (2<<3)
+#define BT848_SCLOOP_HFILT_ICON (3<<3)
+
+#define BT848_SCLOOP_PEAK (1<<7)
+#define BT848_SCLOOP_HFILT_MINP (1<<3)
+#define BT848_SCLOOP_HFILT_MEDP (2<<3)
+#define BT848_SCLOOP_HFILT_MAXP (3<<3)
+
+
+#define BT848_OFORM 0x048
+#define BT848_OFORM_RANGE (1<<7)
+#define BT848_OFORM_CORE0 (0<<5)
+#define BT848_OFORM_CORE8 (1<<5)
+#define BT848_OFORM_CORE16 (2<<5)
+#define BT848_OFORM_CORE32 (3<<5)
+
+#define BT848_E_VSCALE_HI 0x04C
+#define BT848_O_VSCALE_HI 0x0CC
+#define BT848_VSCALE_YCOMB (1<<7)
+#define BT848_VSCALE_COMB (1<<6)
+#define BT848_VSCALE_INT (1<<5)
+#define BT848_VSCALE_HI 15
+
+#define BT848_E_VSCALE_LO 0x050
+#define BT848_O_VSCALE_LO 0x0D0
+#define BT848_TEST 0x054
+#define BT848_ADELAY 0x060
+#define BT848_BDELAY 0x064
+
+#define BT848_ADC 0x068
+#define BT848_ADC_RESERVED (2<<6)
+#define BT848_ADC_SYNC_T (1<<5)
+#define BT848_ADC_AGC_EN (1<<4)
+#define BT848_ADC_CLK_SLEEP (1<<3)
+#define BT848_ADC_Y_SLEEP (1<<2)
+#define BT848_ADC_C_SLEEP (1<<1)
+#define BT848_ADC_CRUSH (1<<0)
+
+#define BT848_WC_UP 0x044
+#define BT848_WC_DOWN 0x078
+
+#define BT848_E_VTC 0x06C
+#define BT848_O_VTC 0x0EC
+#define BT848_VTC_HSFMT (1<<7)
+#define BT848_VTC_VFILT_2TAP 0
+#define BT848_VTC_VFILT_3TAP 1
+#define BT848_VTC_VFILT_4TAP 2
+#define BT848_VTC_VFILT_5TAP 3
+
+#define BT848_SRESET 0x07C
+
+#define BT848_COLOR_FMT 0x0D4
+#define BT848_COLOR_FMT_O_RGB32 (0<<4)
+#define BT848_COLOR_FMT_O_RGB24 (1<<4)
+#define BT848_COLOR_FMT_O_RGB16 (2<<4)
+#define BT848_COLOR_FMT_O_RGB15 (3<<4)
+#define BT848_COLOR_FMT_O_YUY2 (4<<4)
+#define BT848_COLOR_FMT_O_BtYUV (5<<4)
+#define BT848_COLOR_FMT_O_Y8 (6<<4)
+#define BT848_COLOR_FMT_O_RGB8 (7<<4)
+#define BT848_COLOR_FMT_O_YCrCb422 (8<<4)
+#define BT848_COLOR_FMT_O_YCrCb411 (9<<4)
+#define BT848_COLOR_FMT_O_RAW (14<<4)
+#define BT848_COLOR_FMT_E_RGB32 0
+#define BT848_COLOR_FMT_E_RGB24 1
+#define BT848_COLOR_FMT_E_RGB16 2
+#define BT848_COLOR_FMT_E_RGB15 3
+#define BT848_COLOR_FMT_E_YUY2 4
+#define BT848_COLOR_FMT_E_BtYUV 5
+#define BT848_COLOR_FMT_E_Y8 6
+#define BT848_COLOR_FMT_E_RGB8 7
+#define BT848_COLOR_FMT_E_YCrCb422 8
+#define BT848_COLOR_FMT_E_YCrCb411 9
+#define BT848_COLOR_FMT_E_RAW 14
+
+#define BT848_COLOR_FMT_RGB32 0x00
+#define BT848_COLOR_FMT_RGB24 0x11
+#define BT848_COLOR_FMT_RGB16 0x22
+#define BT848_COLOR_FMT_RGB15 0x33
+#define BT848_COLOR_FMT_YUY2 0x44
+#define BT848_COLOR_FMT_BtYUV 0x55
+#define BT848_COLOR_FMT_Y8 0x66
+#define BT848_COLOR_FMT_RGB8 0x77
+#define BT848_COLOR_FMT_YCrCb422 0x88
+#define BT848_COLOR_FMT_YCrCb411 0x99
+#define BT848_COLOR_FMT_RAW 0xee
+
+#define BT848_VTOTAL_LO 0xB0
+#define BT848_VTOTAL_HI 0xB4
+
+#define BT848_COLOR_CTL 0x0D8
+#define BT848_COLOR_CTL_EXT_FRMRATE (1<<7)
+#define BT848_COLOR_CTL_COLOR_BARS (1<<6)
+#define BT848_COLOR_CTL_RGB_DED (1<<5)
+#define BT848_COLOR_CTL_GAMMA (1<<4)
+#define BT848_COLOR_CTL_WSWAP_ODD (1<<3)
+#define BT848_COLOR_CTL_WSWAP_EVEN (1<<2)
+#define BT848_COLOR_CTL_BSWAP_ODD (1<<1)
+#define BT848_COLOR_CTL_BSWAP_EVEN (1<<0)
+
+#define BT848_CAP_CTL 0x0DC
+#define BT848_CAP_CTL_DITH_FRAME (1<<4)
+#define BT848_CAP_CTL_CAPTURE_VBI_ODD (1<<3)
+#define BT848_CAP_CTL_CAPTURE_VBI_EVEN (1<<2)
+#define BT848_CAP_CTL_CAPTURE_ODD (1<<1)
+#define BT848_CAP_CTL_CAPTURE_EVEN (1<<0)
+
+#define BT848_VBI_PACK_SIZE 0x0E0
+
+#define BT848_VBI_PACK_DEL 0x0E4
+#define BT848_VBI_PACK_DEL_VBI_HDELAY 0xfc
+#define BT848_VBI_PACK_DEL_EXT_FRAME 2
+#define BT848_VBI_PACK_DEL_VBI_PKT_HI 1
+
+
+#define BT848_INT_STAT 0x100
+#define BT848_INT_MASK 0x104
+
+#define BT848_INT_ETBF (1<<23)
+
+#define BT848_INT_RISCS (0xf<<28)
+#define BT848_INT_RISC_EN (1<<27)
+#define BT848_INT_RACK (1<<25)
+#define BT848_INT_FIELD (1<<24)
+#define BT848_INT_SCERR (1<<19)
+#define BT848_INT_OCERR (1<<18)
+#define BT848_INT_PABORT (1<<17)
+#define BT848_INT_RIPERR (1<<16)
+#define BT848_INT_PPERR (1<<15)
+#define BT848_INT_FDSR (1<<14)
+#define BT848_INT_FTRGT (1<<13)
+#define BT848_INT_FBUS (1<<12)
+#define BT848_INT_RISCI (1<<11)
+#define BT848_INT_GPINT (1<<9)
+#define BT848_INT_I2CDONE (1<<8)
+#define BT848_INT_VPRES (1<<5)
+#define BT848_INT_HLOCK (1<<4)
+#define BT848_INT_OFLOW (1<<3)
+#define BT848_INT_HSYNC (1<<2)
+#define BT848_INT_VSYNC (1<<1)
+#define BT848_INT_FMTCHG (1<<0)
+
+
+#define BT848_GPIO_DMA_CTL 0x10C
+#define BT848_GPIO_DMA_CTL_GPINTC (1<<15)
+#define BT848_GPIO_DMA_CTL_GPINTI (1<<14)
+#define BT848_GPIO_DMA_CTL_GPWEC (1<<13)
+#define BT848_GPIO_DMA_CTL_GPIOMODE (3<<11)
+#define BT848_GPIO_DMA_CTL_GPCLKMODE (1<<10)
+#define BT848_GPIO_DMA_CTL_PLTP23_4 (0<<6)
+#define BT848_GPIO_DMA_CTL_PLTP23_8 (1<<6)
+#define BT848_GPIO_DMA_CTL_PLTP23_16 (2<<6)
+#define BT848_GPIO_DMA_CTL_PLTP23_32 (3<<6)
+#define BT848_GPIO_DMA_CTL_PLTP1_4 (0<<4)
+#define BT848_GPIO_DMA_CTL_PLTP1_8 (1<<4)
+#define BT848_GPIO_DMA_CTL_PLTP1_16 (2<<4)
+#define BT848_GPIO_DMA_CTL_PLTP1_32 (3<<4)
+#define BT848_GPIO_DMA_CTL_PKTP_4 (0<<2)
+#define BT848_GPIO_DMA_CTL_PKTP_8 (1<<2)
+#define BT848_GPIO_DMA_CTL_PKTP_16 (2<<2)
+#define BT848_GPIO_DMA_CTL_PKTP_32 (3<<2)
+#define BT848_GPIO_DMA_CTL_RISC_ENABLE (1<<1)
+#define BT848_GPIO_DMA_CTL_FIFO_ENABLE (1<<0)
+
+#define BT848_I2C 0x110
+#define BT878_I2C_MODE (1<<7)
+#define BT878_I2C_RATE (1<<6)
+#define BT878_I2C_NOSTOP (1<<5)
+#define BT878_I2C_NOSTART (1<<4)
+#define BT848_I2C_DIV (0xf<<4)
+#define BT848_I2C_SYNC (1<<3)
+#define BT848_I2C_W3B (1<<2)
+#define BT848_I2C_SCL (1<<1)
+#define BT848_I2C_SDA (1<<0)
+
+#define BT848_RISC_STRT_ADD 0x114
+#define BT848_GPIO_OUT_EN 0x118
+#define BT848_GPIO_REG_INP 0x11C
+#define BT848_RISC_COUNT 0x120
+#define BT848_GPIO_DATA 0x200
+
+
+/* Bt848 RISC commands */
+
+/* only for the SYNC RISC command */
+#define BT848_FIFO_STATUS_FM1 0x06
+#define BT848_FIFO_STATUS_FM3 0x0e
+#define BT848_FIFO_STATUS_SOL 0x02
+#define BT848_FIFO_STATUS_EOL4 0x01
+#define BT848_FIFO_STATUS_EOL3 0x0d
+#define BT848_FIFO_STATUS_EOL2 0x09
+#define BT848_FIFO_STATUS_EOL1 0x05
+#define BT848_FIFO_STATUS_VRE 0x04
+#define BT848_FIFO_STATUS_VRO 0x0c
+#define BT848_FIFO_STATUS_PXV 0x00
+
+#define BT848_RISC_RESYNC (1<<15)
+
+/* WRITE and SKIP */
+/* disable which bytes of each DWORD */
+#define BT848_RISC_BYTE0 (1U<<12)
+#define BT848_RISC_BYTE1 (1U<<13)
+#define BT848_RISC_BYTE2 (1U<<14)
+#define BT848_RISC_BYTE3 (1U<<15)
+#define BT848_RISC_BYTE_ALL (0x0fU<<12)
+#define BT848_RISC_BYTE_NONE 0
+/* cause RISCI */
+#define BT848_RISC_IRQ (1U<<24)
+/* RISC command is last one in this line */
+#define BT848_RISC_EOL (1U<<26)
+/* RISC command is first one in this line */
+#define BT848_RISC_SOL (1U<<27)
+
+#define BT848_RISC_WRITE (0x01U<<28)
+#define BT848_RISC_SKIP (0x02U<<28)
+#define BT848_RISC_WRITEC (0x05U<<28)
+#define BT848_RISC_JUMP (0x07U<<28)
+#define BT848_RISC_SYNC (0x08U<<28)
+
+#define BT848_RISC_WRITE123 (0x09U<<28)
+#define BT848_RISC_SKIP123 (0x0aU<<28)
+#define BT848_RISC_WRITE1S23 (0x0bU<<28)
+
+
+/* Bt848A and higher only !! */
+#define BT848_TGLB 0x080
+#define BT848_TGCTRL 0x084
+#define BT848_FCAP 0x0E8
+#define BT848_PLL_F_LO 0x0F0
+#define BT848_PLL_F_HI 0x0F4
+
+#define BT848_PLL_XCI 0x0F8
+#define BT848_PLL_X (1<<7)
+#define BT848_PLL_C (1<<6)
+
+#define BT848_DVSIF 0x0FC
+
+/* Bt878 register */
+
+#define BT878_DEVCTRL 0x40
+#define BT878_EN_TBFX 0x02
+#define BT878_EN_VSFX 0x04
+
+#endif
diff --git a/drivers/media/video/bt8xx/bttv-audio-hook.c b/drivers/media/video/bt8xx/bttv-audio-hook.c
new file mode 100644
index 00000000..2364d165
--- /dev/null
+++ b/drivers/media/video/bt8xx/bttv-audio-hook.c
@@ -0,0 +1,382 @@
+/*
+ * Handlers for board audio hooks, splitted from bttv-cards
+ *
+ * Copyright (c) 2006 Mauro Carvalho Chehab (mchehab@infradead.org)
+ * This code is placed under the terms of the GNU General Public License
+ */
+
+#include "bttv-audio-hook.h"
+
+#include <linux/delay.h>
+
+/* ----------------------------------------------------------------------- */
+/* winview */
+
+void winview_volume(struct bttv *btv, __u16 volume)
+{
+ /* PT2254A programming Jon Tombs, jon@gte.esi.us.es */
+ int bits_out, loops, vol, data;
+
+ /* 32 levels logarithmic */
+ vol = 32 - ((volume>>11));
+ /* units */
+ bits_out = (PT2254_DBS_IN_2>>(vol%5));
+ /* tens */
+ bits_out |= (PT2254_DBS_IN_10>>(vol/5));
+ bits_out |= PT2254_L_CHANNEL | PT2254_R_CHANNEL;
+ data = gpio_read();
+ data &= ~(WINVIEW_PT2254_CLK| WINVIEW_PT2254_DATA|
+ WINVIEW_PT2254_STROBE);
+ for (loops = 17; loops >= 0 ; loops--) {
+ if (bits_out & (1<<loops))
+ data |= WINVIEW_PT2254_DATA;
+ else
+ data &= ~WINVIEW_PT2254_DATA;
+ gpio_write(data);
+ udelay(5);
+ data |= WINVIEW_PT2254_CLK;
+ gpio_write(data);
+ udelay(5);
+ data &= ~WINVIEW_PT2254_CLK;
+ gpio_write(data);
+ }
+ data |= WINVIEW_PT2254_STROBE;
+ data &= ~WINVIEW_PT2254_DATA;
+ gpio_write(data);
+ udelay(10);
+ data &= ~WINVIEW_PT2254_STROBE;
+ gpio_write(data);
+}
+
+/* ----------------------------------------------------------------------- */
+/* mono/stereo control for various cards (which don't use i2c chips but */
+/* connect something to the GPIO pins */
+
+void gvbctv3pci_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned int con = 0;
+
+ if (set) {
+ gpio_inout(0x300, 0x300);
+ if (t->audmode & V4L2_TUNER_MODE_LANG1)
+ con = 0x000;
+ if (t->audmode & V4L2_TUNER_MODE_LANG2)
+ con = 0x300;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO)
+ con = 0x200;
+/* if (t->audmode & V4L2_TUNER_MODE_MONO)
+ * con = 0x100; */
+ gpio_bits(0x300, con);
+ } else {
+ t->audmode = V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
+
+void gvbctv5pci_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned int val, con;
+
+ if (btv->radio_user)
+ return;
+
+ val = gpio_read();
+ if (set) {
+ con = 0x000;
+ if (t->audmode & V4L2_TUNER_MODE_LANG2) {
+ if (t->audmode & V4L2_TUNER_MODE_LANG1) {
+ /* LANG1 + LANG2 */
+ con = 0x100;
+ }
+ else {
+ /* LANG2 */
+ con = 0x300;
+ }
+ }
+ if (con != (val & 0x300)) {
+ gpio_bits(0x300, con);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"gvbctv5pci");
+ }
+ } else {
+ switch (val & 0x70) {
+ case 0x10:
+ t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ break;
+ case 0x30:
+ t->rxsubchans = V4L2_TUNER_SUB_LANG2;
+ break;
+ case 0x50:
+ t->rxsubchans = V4L2_TUNER_SUB_LANG1;
+ break;
+ case 0x60:
+ t->rxsubchans = V4L2_TUNER_SUB_STEREO;
+ break;
+ case 0x70:
+ t->rxsubchans = V4L2_TUNER_SUB_MONO;
+ break;
+ default:
+ t->rxsubchans = V4L2_TUNER_SUB_MONO |
+ V4L2_TUNER_SUB_STEREO |
+ V4L2_TUNER_SUB_LANG1 |
+ V4L2_TUNER_SUB_LANG2;
+ }
+ t->audmode = V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
+
+/*
+ * Mario Medina Nussbaum <medisoft@alohabbs.org.mx>
+ * I discover that on BT848_GPIO_DATA address a byte 0xcce enable stereo,
+ * 0xdde enables mono and 0xccd enables sap
+ *
+ * Petr Vandrovec <VANDROVE@vc.cvut.cz>
+ * P.S.: At least mask in line above is wrong - GPIO pins 3,2 select
+ * input/output sound connection, so both must be set for output mode.
+ *
+ * Looks like it's needed only for the "tvphone", the "tvphone 98"
+ * handles this with a tda9840
+ *
+ */
+
+void avermedia_tvphone_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ int val = 0;
+
+ if (set) {
+ if (t->audmode & V4L2_TUNER_MODE_LANG2) /* SAP */
+ val = 0x02;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO)
+ val = 0x01;
+ if (val) {
+ gpio_bits(0x03,val);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"avermedia");
+ }
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1;
+ return;
+ }
+}
+
+
+void avermedia_tv_stereo_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ int val = 0;
+
+ if (set) {
+ if (t->audmode & V4L2_TUNER_MODE_LANG2) /* SAP */
+ val = 0x01;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO) /* STEREO */
+ val = 0x02;
+ btaor(val, ~0x03, BT848_GPIO_DATA);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"avermedia");
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ return;
+ }
+}
+
+/* Lifetec 9415 handling */
+
+void lt9415_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ int val = 0;
+
+ if (gpio_read() & 0x4000) {
+ t->audmode = V4L2_TUNER_MODE_MONO;
+ return;
+ }
+
+ if (set) {
+ if (t->audmode & V4L2_TUNER_MODE_LANG2) /* A2 SAP */
+ val = 0x0080;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO) /* A2 stereo */
+ val = 0x0880;
+ if ((t->audmode & V4L2_TUNER_MODE_LANG1) ||
+ (t->audmode & V4L2_TUNER_MODE_MONO))
+ val = 0;
+ gpio_bits(0x0880, val);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"lt9415");
+ } else {
+ /* autodetect doesn't work with this card :-( */
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ return;
+ }
+}
+
+/* TDA9821 on TerraTV+ Bt848, Bt878 */
+void terratv_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned int con = 0;
+
+ if (set) {
+ gpio_inout(0x180000,0x180000);
+ if (t->audmode & V4L2_TUNER_MODE_LANG2)
+ con = 0x080000;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO)
+ con = 0x180000;
+ gpio_bits(0x180000, con);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"terratv");
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
+
+
+void winfast2000_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned long val = 0;
+
+ if (set) {
+ /*btor (0xc32000, BT848_GPIO_OUT_EN);*/
+ if (t->audmode & V4L2_TUNER_MODE_MONO) /* Mono */
+ val = 0x420000;
+ if (t->audmode & V4L2_TUNER_MODE_LANG1) /* Mono */
+ val = 0x420000;
+ if (t->audmode & V4L2_TUNER_MODE_LANG2) /* SAP */
+ val = 0x410000;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO) /* Stereo */
+ val = 0x020000;
+ if (val) {
+ gpio_bits(0x430000, val);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"winfast2000");
+ }
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
+
+/*
+ * Dariusz Kowalewski <darekk@automex.pl>
+ * sound control for Prolink PV-BT878P+9B (PixelView PlayTV Pro FM+NICAM
+ * revision 9B has on-board TDA9874A sound decoder).
+ *
+ * Note: There are card variants without tda9874a. Forcing the "stereo sound route"
+ * will mute this cards.
+ */
+void pvbt878p9b_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned int val = 0;
+
+ if (btv->radio_user)
+ return;
+
+ if (set) {
+ if (t->audmode & V4L2_TUNER_MODE_MONO) {
+ val = 0x01;
+ }
+ if ((t->audmode & (V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2))
+ || (t->audmode & V4L2_TUNER_MODE_STEREO)) {
+ val = 0x02;
+ }
+ if (val) {
+ gpio_bits(0x03,val);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"pvbt878p9b");
+ }
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
+
+/*
+ * Dariusz Kowalewski <darekk@automex.pl>
+ * sound control for FlyVideo 2000S (with tda9874 decoder)
+ * based on pvbt878p9b_audio() - this is not tested, please fix!!!
+ */
+void fv2000s_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned int val = 0xffff;
+
+ if (btv->radio_user)
+ return;
+
+ if (set) {
+ if (t->audmode & V4L2_TUNER_MODE_MONO) {
+ val = 0x0000;
+ }
+ if ((t->audmode & (V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2))
+ || (t->audmode & V4L2_TUNER_MODE_STEREO)) {
+ val = 0x1080; /*-dk-???: 0x0880, 0x0080, 0x1800 ... */
+ }
+ if (val != 0xffff) {
+ gpio_bits(0x1800, val);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"fv2000s");
+ }
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
+
+/*
+ * sound control for Canopus WinDVR PCI
+ * Masaki Suzuki <masaki@btree.org>
+ */
+void windvr_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned long val = 0;
+
+ if (set) {
+ if (t->audmode & V4L2_TUNER_MODE_MONO)
+ val = 0x040000;
+ if (t->audmode & V4L2_TUNER_MODE_LANG1)
+ val = 0;
+ if (t->audmode & V4L2_TUNER_MODE_LANG2)
+ val = 0x100000;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO)
+ val = 0;
+ if (val) {
+ gpio_bits(0x140000, val);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"windvr");
+ }
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
+
+/*
+ * sound control for AD-TVK503
+ * Hiroshi Takekawa <sian@big.or.jp>
+ */
+void adtvk503_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned int con = 0xffffff;
+
+ /* btaor(0x1e0000, ~0x1e0000, BT848_GPIO_OUT_EN); */
+
+ if (set) {
+ /* btor(***, BT848_GPIO_OUT_EN); */
+ if (t->audmode & V4L2_TUNER_MODE_LANG1)
+ con = 0x00000000;
+ if (t->audmode & V4L2_TUNER_MODE_LANG2)
+ con = 0x00180000;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO)
+ con = 0x00000000;
+ if (t->audmode & V4L2_TUNER_MODE_MONO)
+ con = 0x00060000;
+ if (con != 0xffffff) {
+ gpio_bits(0x1e0000,con);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv, "adtvk503");
+ }
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
diff --git a/drivers/media/video/bt8xx/bttv-audio-hook.h b/drivers/media/video/bt8xx/bttv-audio-hook.h
new file mode 100644
index 00000000..159d07ad
--- /dev/null
+++ b/drivers/media/video/bt8xx/bttv-audio-hook.h
@@ -0,0 +1,23 @@
+/*
+ * Handlers for board audio hooks, splitted from bttv-cards
+ *
+ * Copyright (c) 2006 Mauro Carvalho Chehab (mchehab@infradead.org)
+ * This code is placed under the terms of the GNU General Public License
+ */
+
+#include "bttvp.h"
+
+void winview_volume (struct bttv *btv, __u16 volume);
+
+void lt9415_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void avermedia_tvphone_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void avermedia_tv_stereo_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void terratv_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void gvbctv3pci_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void gvbctv5pci_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void winfast2000_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void pvbt878p9b_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void fv2000s_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void windvr_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void adtvk503_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+
diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c
new file mode 100644
index 00000000..ff2933ab
--- /dev/null
+++ b/drivers/media/video/bt8xx/bttv-cards.c
@@ -0,0 +1,4958 @@
+/*
+
+ bttv-cards.c
+
+ this file has configuration informations - card-specific stuff
+ like the big tvcards array for the most part
+
+ Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
+ & Marcus Metzler (mocm@thp.uni-koeln.de)
+ (c) 1999-2001 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/firmware.h>
+#include <net/checksum.h>
+
+#include <asm/unaligned.h>
+#include <asm/io.h>
+
+#include "bttvp.h"
+#include <media/v4l2-common.h>
+#include <media/tvaudio.h>
+#include "bttv-audio-hook.h"
+
+/* fwd decl */
+static void boot_msp34xx(struct bttv *btv, int pin);
+static void hauppauge_eeprom(struct bttv *btv);
+static void avermedia_eeprom(struct bttv *btv);
+static void osprey_eeprom(struct bttv *btv, const u8 ee[256]);
+static void modtec_eeprom(struct bttv *btv);
+static void init_PXC200(struct bttv *btv);
+static void init_RTV24(struct bttv *btv);
+
+static void rv605_muxsel(struct bttv *btv, unsigned int input);
+static void eagle_muxsel(struct bttv *btv, unsigned int input);
+static void xguard_muxsel(struct bttv *btv, unsigned int input);
+static void ivc120_muxsel(struct bttv *btv, unsigned int input);
+static void gvc1100_muxsel(struct bttv *btv, unsigned int input);
+
+static void PXC200_muxsel(struct bttv *btv, unsigned int input);
+
+static void picolo_tetra_muxsel(struct bttv *btv, unsigned int input);
+static void picolo_tetra_init(struct bttv *btv);
+
+static void tibetCS16_muxsel(struct bttv *btv, unsigned int input);
+static void tibetCS16_init(struct bttv *btv);
+
+static void kodicom4400r_muxsel(struct bttv *btv, unsigned int input);
+static void kodicom4400r_init(struct bttv *btv);
+
+static void sigmaSLC_muxsel(struct bttv *btv, unsigned int input);
+static void sigmaSQ_muxsel(struct bttv *btv, unsigned int input);
+
+static void geovision_muxsel(struct bttv *btv, unsigned int input);
+
+static void phytec_muxsel(struct bttv *btv, unsigned int input);
+
+static void gv800s_muxsel(struct bttv *btv, unsigned int input);
+static void gv800s_init(struct bttv *btv);
+
+static void td3116_muxsel(struct bttv *btv, unsigned int input);
+
+static int terratec_active_radio_upgrade(struct bttv *btv);
+static int tea5757_read(struct bttv *btv);
+static int tea5757_write(struct bttv *btv, int value);
+static void identify_by_eeprom(struct bttv *btv,
+ unsigned char eeprom_data[256]);
+static int __devinit pvr_boot(struct bttv *btv);
+
+/* config variables */
+static unsigned int triton1;
+static unsigned int vsfx;
+static unsigned int latency = UNSET;
+int no_overlay=-1;
+
+static unsigned int card[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET };
+static unsigned int pll[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET };
+static unsigned int tuner[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET };
+static unsigned int svhs[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET };
+static unsigned int remote[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET };
+static unsigned int audiodev[BTTV_MAX];
+static unsigned int saa6588[BTTV_MAX];
+static struct bttv *master[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = NULL };
+static unsigned int autoload = UNSET;
+static unsigned int gpiomask = UNSET;
+static unsigned int audioall = UNSET;
+static unsigned int audiomux[5] = { [ 0 ... 4 ] = UNSET };
+
+/* insmod options */
+module_param(triton1, int, 0444);
+module_param(vsfx, int, 0444);
+module_param(no_overlay, int, 0444);
+module_param(latency, int, 0444);
+module_param(gpiomask, int, 0444);
+module_param(audioall, int, 0444);
+module_param(autoload, int, 0444);
+
+module_param_array(card, int, NULL, 0444);
+module_param_array(pll, int, NULL, 0444);
+module_param_array(tuner, int, NULL, 0444);
+module_param_array(svhs, int, NULL, 0444);
+module_param_array(remote, int, NULL, 0444);
+module_param_array(audiodev, int, NULL, 0444);
+module_param_array(audiomux, int, NULL, 0444);
+
+MODULE_PARM_DESC(triton1,"set ETBF pci config bit "
+ "[enable bug compatibility for triton1 + others]");
+MODULE_PARM_DESC(vsfx,"set VSFX pci config bit "
+ "[yet another chipset flaw workaround]");
+MODULE_PARM_DESC(latency,"pci latency timer");
+MODULE_PARM_DESC(card,"specify TV/grabber card model, see CARDLIST file for a list");
+MODULE_PARM_DESC(pll,"specify installed crystal (0=none, 28=28 MHz, 35=35 MHz)");
+MODULE_PARM_DESC(tuner,"specify installed tuner type");
+MODULE_PARM_DESC(autoload, "obsolete option, please do not use anymore");
+MODULE_PARM_DESC(audiodev, "specify audio device:\n"
+ "\t\t-1 = no audio\n"
+ "\t\t 0 = autodetect (default)\n"
+ "\t\t 1 = msp3400\n"
+ "\t\t 2 = tda7432\n"
+ "\t\t 3 = tvaudio");
+MODULE_PARM_DESC(saa6588, "if 1, then load the saa6588 RDS module, default (0) is to use the card definition.");
+MODULE_PARM_DESC(no_overlay,"allow override overlay default (0 disables, 1 enables)"
+ " [some VIA/SIS chipsets are known to have problem with overlay]");
+
+/* ----------------------------------------------------------------------- */
+/* list of card IDs for bt878+ cards */
+
+static struct CARD {
+ unsigned id;
+ int cardnr;
+ char *name;
+} cards[] __devinitdata = {
+ { 0x13eb0070, BTTV_BOARD_HAUPPAUGE878, "Hauppauge WinTV" },
+ { 0x39000070, BTTV_BOARD_HAUPPAUGE878, "Hauppauge WinTV-D" },
+ { 0x45000070, BTTV_BOARD_HAUPPAUGEPVR, "Hauppauge WinTV/PVR" },
+ { 0xff000070, BTTV_BOARD_OSPREY1x0, "Osprey-100" },
+ { 0xff010070, BTTV_BOARD_OSPREY2x0_SVID,"Osprey-200" },
+ { 0xff020070, BTTV_BOARD_OSPREY500, "Osprey-500" },
+ { 0xff030070, BTTV_BOARD_OSPREY2000, "Osprey-2000" },
+ { 0xff040070, BTTV_BOARD_OSPREY540, "Osprey-540" },
+ { 0xff070070, BTTV_BOARD_OSPREY440, "Osprey-440" },
+
+ { 0x00011002, BTTV_BOARD_ATI_TVWONDER, "ATI TV Wonder" },
+ { 0x00031002, BTTV_BOARD_ATI_TVWONDERVE,"ATI TV Wonder/VE" },
+
+ { 0x6606107d, BTTV_BOARD_WINFAST2000, "Leadtek WinFast TV 2000" },
+ { 0x6607107d, BTTV_BOARD_WINFASTVC100, "Leadtek WinFast VC 100" },
+ { 0x6609107d, BTTV_BOARD_WINFAST2000, "Leadtek TV 2000 XP" },
+ { 0x263610b4, BTTV_BOARD_STB2, "STB TV PCI FM, Gateway P/N 6000704" },
+ { 0x264510b4, BTTV_BOARD_STB2, "STB TV PCI FM, Gateway P/N 6000704" },
+ { 0x402010fc, BTTV_BOARD_GVBCTV3PCI, "I-O Data Co. GV-BCTV3/PCI" },
+ { 0x405010fc, BTTV_BOARD_GVBCTV4PCI, "I-O Data Co. GV-BCTV4/PCI" },
+ { 0x407010fc, BTTV_BOARD_GVBCTV5PCI, "I-O Data Co. GV-BCTV5/PCI" },
+ { 0xd01810fc, BTTV_BOARD_GVBCTV5PCI, "I-O Data Co. GV-BCTV5/PCI" },
+
+ { 0x001211bd, BTTV_BOARD_PINNACLE, "Pinnacle PCTV" },
+ /* some cards ship with byteswapped IDs ... */
+ { 0x1200bd11, BTTV_BOARD_PINNACLE, "Pinnacle PCTV [bswap]" },
+ { 0xff00bd11, BTTV_BOARD_PINNACLE, "Pinnacle PCTV [bswap]" },
+ /* this seems to happen as well ... */
+ { 0xff1211bd, BTTV_BOARD_PINNACLE, "Pinnacle PCTV" },
+
+ { 0x3000121a, BTTV_BOARD_VOODOOTV_200, "3Dfx VoodooTV 200" },
+ { 0x263710b4, BTTV_BOARD_VOODOOTV_FM, "3Dfx VoodooTV FM" },
+ { 0x3060121a, BTTV_BOARD_STB2, "3Dfx VoodooTV 100/ STB OEM" },
+
+ { 0x3000144f, BTTV_BOARD_MAGICTVIEW063, "(Askey Magic/others) TView99 CPH06x" },
+ { 0xa005144f, BTTV_BOARD_MAGICTVIEW063, "CPH06X TView99-Card" },
+ { 0x3002144f, BTTV_BOARD_MAGICTVIEW061, "(Askey Magic/others) TView99 CPH05x" },
+ { 0x3005144f, BTTV_BOARD_MAGICTVIEW061, "(Askey Magic/others) TView99 CPH061/06L (T1/LC)" },
+ { 0x5000144f, BTTV_BOARD_MAGICTVIEW061, "Askey CPH050" },
+ { 0x300014ff, BTTV_BOARD_MAGICTVIEW061, "TView 99 (CPH061)" },
+ { 0x300214ff, BTTV_BOARD_PHOEBE_TVMAS, "Phoebe TV Master (CPH060)" },
+
+ { 0x00011461, BTTV_BOARD_AVPHONE98, "AVerMedia TVPhone98" },
+ { 0x00021461, BTTV_BOARD_AVERMEDIA98, "AVermedia TVCapture 98" },
+ { 0x00031461, BTTV_BOARD_AVPHONE98, "AVerMedia TVPhone98" },
+ { 0x00041461, BTTV_BOARD_AVERMEDIA98, "AVerMedia TVCapture 98" },
+ { 0x03001461, BTTV_BOARD_AVERMEDIA98, "VDOMATE TV TUNER CARD" },
+
+ { 0x1117153b, BTTV_BOARD_TERRATVALUE, "Terratec TValue (Philips PAL B/G)" },
+ { 0x1118153b, BTTV_BOARD_TERRATVALUE, "Terratec TValue (Temic PAL B/G)" },
+ { 0x1119153b, BTTV_BOARD_TERRATVALUE, "Terratec TValue (Philips PAL I)" },
+ { 0x111a153b, BTTV_BOARD_TERRATVALUE, "Terratec TValue (Temic PAL I)" },
+
+ { 0x1123153b, BTTV_BOARD_TERRATVRADIO, "Terratec TV Radio+" },
+ { 0x1127153b, BTTV_BOARD_TERRATV, "Terratec TV+ (V1.05)" },
+ /* clashes with FlyVideo
+ *{ 0x18521852, BTTV_BOARD_TERRATV, "Terratec TV+ (V1.10)" }, */
+ { 0x1134153b, BTTV_BOARD_TERRATVALUE, "Terratec TValue (LR102)" },
+ { 0x1135153b, BTTV_BOARD_TERRATVALUER, "Terratec TValue Radio" }, /* LR102 */
+ { 0x5018153b, BTTV_BOARD_TERRATVALUE, "Terratec TValue" }, /* ?? */
+ { 0xff3b153b, BTTV_BOARD_TERRATVALUER, "Terratec TValue Radio" }, /* ?? */
+
+ { 0x400015b0, BTTV_BOARD_ZOLTRIX_GENIE, "Zoltrix Genie TV" },
+ { 0x400a15b0, BTTV_BOARD_ZOLTRIX_GENIE, "Zoltrix Genie TV" },
+ { 0x400d15b0, BTTV_BOARD_ZOLTRIX_GENIE, "Zoltrix Genie TV / Radio" },
+ { 0x401015b0, BTTV_BOARD_ZOLTRIX_GENIE, "Zoltrix Genie TV / Radio" },
+ { 0x401615b0, BTTV_BOARD_ZOLTRIX_GENIE, "Zoltrix Genie TV / Radio" },
+
+ { 0x1430aa00, BTTV_BOARD_PV143, "Provideo PV143A" },
+ { 0x1431aa00, BTTV_BOARD_PV143, "Provideo PV143B" },
+ { 0x1432aa00, BTTV_BOARD_PV143, "Provideo PV143C" },
+ { 0x1433aa00, BTTV_BOARD_PV143, "Provideo PV143D" },
+ { 0x1433aa03, BTTV_BOARD_PV143, "Security Eyes" },
+
+ { 0x1460aa00, BTTV_BOARD_PV150, "Provideo PV150A-1" },
+ { 0x1461aa01, BTTV_BOARD_PV150, "Provideo PV150A-2" },
+ { 0x1462aa02, BTTV_BOARD_PV150, "Provideo PV150A-3" },
+ { 0x1463aa03, BTTV_BOARD_PV150, "Provideo PV150A-4" },
+
+ { 0x1464aa04, BTTV_BOARD_PV150, "Provideo PV150B-1" },
+ { 0x1465aa05, BTTV_BOARD_PV150, "Provideo PV150B-2" },
+ { 0x1466aa06, BTTV_BOARD_PV150, "Provideo PV150B-3" },
+ { 0x1467aa07, BTTV_BOARD_PV150, "Provideo PV150B-4" },
+
+ { 0xa132ff00, BTTV_BOARD_IVC100, "IVC-100" },
+ { 0xa1550000, BTTV_BOARD_IVC200, "IVC-200" },
+ { 0xa1550001, BTTV_BOARD_IVC200, "IVC-200" },
+ { 0xa1550002, BTTV_BOARD_IVC200, "IVC-200" },
+ { 0xa1550003, BTTV_BOARD_IVC200, "IVC-200" },
+ { 0xa1550100, BTTV_BOARD_IVC200, "IVC-200G" },
+ { 0xa1550101, BTTV_BOARD_IVC200, "IVC-200G" },
+ { 0xa1550102, BTTV_BOARD_IVC200, "IVC-200G" },
+ { 0xa1550103, BTTV_BOARD_IVC200, "IVC-200G" },
+ { 0xa1550800, BTTV_BOARD_IVC200, "IVC-200" },
+ { 0xa1550801, BTTV_BOARD_IVC200, "IVC-200" },
+ { 0xa1550802, BTTV_BOARD_IVC200, "IVC-200" },
+ { 0xa1550803, BTTV_BOARD_IVC200, "IVC-200" },
+ { 0xa182ff00, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff01, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff02, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff03, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff04, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff05, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff06, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff07, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff08, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff09, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff0a, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff0b, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff0c, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff0d, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff0e, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff0f, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xf0500000, BTTV_BOARD_IVCE8784, "IVCE-8784" },
+ { 0xf0500001, BTTV_BOARD_IVCE8784, "IVCE-8784" },
+ { 0xf0500002, BTTV_BOARD_IVCE8784, "IVCE-8784" },
+ { 0xf0500003, BTTV_BOARD_IVCE8784, "IVCE-8784" },
+
+ { 0x41424344, BTTV_BOARD_GRANDTEC, "GrandTec Multi Capture" },
+ { 0x01020304, BTTV_BOARD_XGUARD, "Grandtec Grand X-Guard" },
+
+ { 0x18501851, BTTV_BOARD_CHRONOS_VS2, "FlyVideo 98 (LR50)/ Chronos Video Shuttle II" },
+ { 0xa0501851, BTTV_BOARD_CHRONOS_VS2, "FlyVideo 98 (LR50)/ Chronos Video Shuttle II" },
+ { 0x18511851, BTTV_BOARD_FLYVIDEO98EZ, "FlyVideo 98EZ (LR51)/ CyberMail AV" },
+ { 0x18521852, BTTV_BOARD_TYPHOON_TVIEW, "FlyVideo 98FM (LR50)/ Typhoon TView TV/FM Tuner" },
+ { 0x41a0a051, BTTV_BOARD_FLYVIDEO_98FM, "Lifeview FlyVideo 98 LR50 Rev Q" },
+ { 0x18501f7f, BTTV_BOARD_FLYVIDEO_98, "Lifeview Flyvideo 98" },
+
+ { 0x010115cb, BTTV_BOARD_GMV1, "AG GMV1" },
+ { 0x010114c7, BTTV_BOARD_MODTEC_205, "Modular Technology MM201/MM202/MM205/MM210/MM215 PCTV" },
+
+ { 0x10b42636, BTTV_BOARD_HAUPPAUGE878, "STB ???" },
+ { 0x217d6606, BTTV_BOARD_WINFAST2000, "Leadtek WinFast TV 2000" },
+ { 0xfff6f6ff, BTTV_BOARD_WINFAST2000, "Leadtek WinFast TV 2000" },
+ { 0x03116000, BTTV_BOARD_SENSORAY311_611, "Sensoray 311" },
+ { 0x06116000, BTTV_BOARD_SENSORAY311_611, "Sensoray 611" },
+ { 0x00790e11, BTTV_BOARD_WINDVR, "Canopus WinDVR PCI" },
+ { 0xa0fca1a0, BTTV_BOARD_ZOLTRIX, "Face to Face Tvmax" },
+ { 0x82b2aa6a, BTTV_BOARD_SIMUS_GVC1100, "SIMUS GVC1100" },
+ { 0x146caa0c, BTTV_BOARD_PV951, "ituner spectra8" },
+ { 0x200a1295, BTTV_BOARD_PXC200, "ImageNation PXC200A" },
+
+ { 0x40111554, BTTV_BOARD_PV_BT878P_9B, "Prolink Pixelview PV-BT" },
+ { 0x17de0a01, BTTV_BOARD_KWORLD, "Mecer TV/FM/Video Tuner" },
+
+ { 0x01051805, BTTV_BOARD_PICOLO_TETRA_CHIP, "Picolo Tetra Chip #1" },
+ { 0x01061805, BTTV_BOARD_PICOLO_TETRA_CHIP, "Picolo Tetra Chip #2" },
+ { 0x01071805, BTTV_BOARD_PICOLO_TETRA_CHIP, "Picolo Tetra Chip #3" },
+ { 0x01081805, BTTV_BOARD_PICOLO_TETRA_CHIP, "Picolo Tetra Chip #4" },
+
+ { 0x15409511, BTTV_BOARD_ACORP_Y878F, "Acorp Y878F" },
+
+ { 0x53534149, BTTV_BOARD_SSAI_SECURITY, "SSAI Security Video Interface" },
+ { 0x5353414a, BTTV_BOARD_SSAI_ULTRASOUND, "SSAI Ultrasound Video Interface" },
+
+ /* likely broken, vendor id doesn't match the other magic views ...
+ * { 0xa0fca04f, BTTV_BOARD_MAGICTVIEW063, "Guillemot Maxi TV Video 3" }, */
+
+ /* Duplicate PCI ID, reconfigure for this board during the eeprom read.
+ * { 0x13eb0070, BTTV_BOARD_HAUPPAUGE_IMPACTVCB, "Hauppauge ImpactVCB" }, */
+
+ { 0x109e036e, BTTV_BOARD_CONCEPTRONIC_CTVFMI2, "Conceptronic CTVFMi v2"},
+
+ /* DVB cards (using pci function .1 for mpeg data xfer) */
+ { 0x001c11bd, BTTV_BOARD_PINNACLESAT, "Pinnacle PCTV Sat" },
+ { 0x01010071, BTTV_BOARD_NEBULA_DIGITV, "Nebula Electronics DigiTV" },
+ { 0x20007063, BTTV_BOARD_PC_HDTV, "pcHDTV HD-2000 TV"},
+ { 0x002611bd, BTTV_BOARD_TWINHAN_DST, "Pinnacle PCTV SAT CI" },
+ { 0x00011822, BTTV_BOARD_TWINHAN_DST, "Twinhan VisionPlus DVB" },
+ { 0xfc00270f, BTTV_BOARD_TWINHAN_DST, "ChainTech digitop DST-1000 DVB-S" },
+ { 0x07711461, BTTV_BOARD_AVDVBT_771, "AVermedia AverTV DVB-T 771" },
+ { 0x07611461, BTTV_BOARD_AVDVBT_761, "AverMedia AverTV DVB-T 761" },
+ { 0xdb1018ac, BTTV_BOARD_DVICO_DVBT_LITE, "DViCO FusionHDTV DVB-T Lite" },
+ { 0xdb1118ac, BTTV_BOARD_DVICO_DVBT_LITE, "Ultraview DVB-T Lite" },
+ { 0xd50018ac, BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE, "DViCO FusionHDTV 5 Lite" },
+ { 0x00261822, BTTV_BOARD_TWINHAN_DST, "DNTV Live! Mini "},
+ { 0xd200dbc0, BTTV_BOARD_DVICO_FUSIONHDTV_2, "DViCO FusionHDTV 2" },
+ { 0x763c008a, BTTV_BOARD_GEOVISION_GV600, "GeoVision GV-600" },
+ { 0x18011000, BTTV_BOARD_ENLTV_FM_2, "Encore ENL TV-FM-2" },
+ { 0x763d800a, BTTV_BOARD_GEOVISION_GV800S, "GeoVision GV-800(S) (master)" },
+ { 0x763d800b, BTTV_BOARD_GEOVISION_GV800S_SL, "GeoVision GV-800(S) (slave)" },
+ { 0x763d800c, BTTV_BOARD_GEOVISION_GV800S_SL, "GeoVision GV-800(S) (slave)" },
+ { 0x763d800d, BTTV_BOARD_GEOVISION_GV800S_SL, "GeoVision GV-800(S) (slave)" },
+
+ { 0x15401830, BTTV_BOARD_PV183, "Provideo PV183-1" },
+ { 0x15401831, BTTV_BOARD_PV183, "Provideo PV183-2" },
+ { 0x15401832, BTTV_BOARD_PV183, "Provideo PV183-3" },
+ { 0x15401833, BTTV_BOARD_PV183, "Provideo PV183-4" },
+ { 0x15401834, BTTV_BOARD_PV183, "Provideo PV183-5" },
+ { 0x15401835, BTTV_BOARD_PV183, "Provideo PV183-6" },
+ { 0x15401836, BTTV_BOARD_PV183, "Provideo PV183-7" },
+ { 0x15401837, BTTV_BOARD_PV183, "Provideo PV183-8" },
+ { 0x3116f200, BTTV_BOARD_TVT_TD3116, "Tongwei Video Technology TD-3116" },
+
+ { 0, -1, NULL }
+};
+
+/* ----------------------------------------------------------------------- */
+/* array with description for bt848 / bt878 tv/grabber cards */
+
+struct tvcard bttv_tvcards[] = {
+ /* ---- card 0x00 ---------------------------------- */
+ [BTTV_BOARD_UNKNOWN] = {
+ .name = " *** UNKNOWN/GENERIC *** ",
+ .video_inputs = 4,
+ .svhs = 2,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_MIRO] = {
+ .name = "MIRO PCTV",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 15,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 2, 0, 0, 0 },
+ .gpiomute = 10,
+ .needs_tvaudio = 1,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_HAUPPAUGE] = {
+ .name = "Hauppauge (bt848)",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 7,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 1, 2, 3 },
+ .gpiomute = 4,
+ .needs_tvaudio = 1,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_STB] = {
+ .name = "STB, Gateway P/N 6000699 (bt848)",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 7,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 4, 0, 2, 3 },
+ .gpiomute = 1,
+ .no_msp34xx = 1,
+ .needs_tvaudio = 1,
+ .tuner_type = TUNER_PHILIPS_NTSC,
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .has_radio = 1,
+ },
+
+ /* ---- card 0x04 ---------------------------------- */
+ [BTTV_BOARD_INTEL] = {
+ .name = "Intel Create and Share PCI/ Smart Video Recorder III",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = 2,
+ .gpiomask = 0,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0 },
+ .needs_tvaudio = 0,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_DIAMOND] = {
+ .name = "Diamond DTV2000",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 3,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = { 0, 1, 0, 1 },
+ .gpiomute = 3,
+ .needs_tvaudio = 1,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_AVERMEDIA] = {
+ .name = "AVerMedia TVPhone",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 3,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomask = 0x0f,
+ .gpiomux = { 0x0c, 0x04, 0x08, 0x04 },
+ /* 0x04 for some cards ?? */
+ .needs_tvaudio = 1,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .audio_mode_gpio= avermedia_tvphone_audio,
+ .has_remote = 1,
+ },
+ [BTTV_BOARD_MATRIX_VISION] = {
+ .name = "MATRIX-Vision MV-Delta",
+ .video_inputs = 5,
+ /* .audio_inputs= 1, */
+ .svhs = 3,
+ .gpiomask = 0,
+ .muxsel = MUXSEL(2, 3, 1, 0, 0),
+ .gpiomux = { 0 },
+ .needs_tvaudio = 1,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x08 ---------------------------------- */
+ [BTTV_BOARD_FLYVIDEO] = {
+ .name = "Lifeview FlyVideo II (Bt848) LR26 / MAXI TV Video PCI2 LR26",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xc00,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0xc00, 0x800, 0x400 },
+ .gpiomute = 0xc00,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_TURBOTV] = {
+ .name = "IMS/IXmicro TurboTV",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 3,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 1, 1, 2, 3 },
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+ .tuner_type = TUNER_TEMIC_PAL,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_HAUPPAUGE878] = {
+ .name = "Hauppauge (bt878)",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x0f, /* old: 7 */
+ .muxsel = MUXSEL(2, 0, 1, 1),
+ .gpiomux = { 0, 1, 2, 3 },
+ .gpiomute = 4,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_MIROPRO] = {
+ .name = "MIRO PCTV pro",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x3014f,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x20001,0x10001, 0, 0 },
+ .gpiomute = 10,
+ .needs_tvaudio = 1,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x0c ---------------------------------- */
+ [BTTV_BOARD_ADSTECH_TV] = {
+ .name = "ADS Technologies Channel Surfer TV (bt848)",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 15,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 13, 14, 11, 7 },
+ .needs_tvaudio = 1,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_AVERMEDIA98] = {
+ .name = "AVerMedia TVCapture 98",
+ .video_inputs = 3,
+ /* .audio_inputs= 4, */
+ .svhs = 2,
+ .gpiomask = 15,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 13, 14, 11, 7 },
+ .needs_tvaudio = 1,
+ .msp34xx_alt = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .audio_mode_gpio= avermedia_tv_stereo_audio,
+ .no_gpioirq = 1,
+ },
+ [BTTV_BOARD_VHX] = {
+ .name = "Aimslab Video Highway Xtreme (VHX)",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 7,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 2, 1, 3 }, /* old: {0, 1, 2, 3, 4} */
+ .gpiomute = 4,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_ZOLTRIX] = {
+ .name = "Zoltrix TV-Max",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 15,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0, 1, 0 },
+ .gpiomute = 10,
+ .needs_tvaudio = 1,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x10 ---------------------------------- */
+ [BTTV_BOARD_PIXVIEWPLAYTV] = {
+ .name = "Prolink Pixelview PlayTV (bt878)",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x01fe00,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ /* 2003-10-20 by "Anton A. Arapov" <arapov@mail.ru> */
+ .gpiomux = { 0x001e00, 0, 0x018000, 0x014000 },
+ .gpiomute = 0x002000,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_WINVIEW_601] = {
+ .name = "Leadtek WinView 601",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x8300f8,
+ .muxsel = MUXSEL(2, 3, 1, 1, 0),
+ .gpiomux = { 0x4fa007,0xcfa007,0xcfa007,0xcfa007 },
+ .gpiomute = 0xcfa007,
+ .needs_tvaudio = 1,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .volume_gpio = winview_volume,
+ .has_radio = 1,
+ },
+ [BTTV_BOARD_AVEC_INTERCAP] = {
+ .name = "AVEC Intercapture",
+ .video_inputs = 3,
+ /* .audio_inputs= 2, */
+ .svhs = 2,
+ .gpiomask = 0,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 1, 0, 0, 0 },
+ .needs_tvaudio = 1,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_LIFE_FLYKIT] = {
+ .name = "Lifeview FlyVideo II EZ /FlyKit LR38 Bt848 (capture only)",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0x8dff00,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0 },
+ .no_msp34xx = 1,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x14 ---------------------------------- */
+ [BTTV_BOARD_CEI_RAFFLES] = {
+ .name = "CEI Raffles Card",
+ .video_inputs = 3,
+ /* .audio_inputs= 3, */
+ .svhs = 2,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_CONFERENCETV] = {
+ .name = "Lifeview FlyVideo 98/ Lucky Star Image World ConferenceTV LR50",
+ .video_inputs = 4,
+ /* .audio_inputs= 2, tuner, line in */
+ .svhs = 2,
+ .gpiomask = 0x1800,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0x800, 0x1000, 0x1000 },
+ .gpiomute = 0x1800,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL_I,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_PHOEBE_TVMAS] = {
+ .name = "Askey CPH050/ Phoebe Tv Master + FM",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xc00,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 1, 0x800, 0x400 },
+ .gpiomute = 0xc00,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_MODTEC_205] = {
+ .name = "Modular Technology MM201/MM202/MM205/MM210/MM215 PCTV, bt878",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = NO_SVHS,
+ .has_dig_in = 1,
+ .gpiomask = 7,
+ .muxsel = MUXSEL(2, 3, 0), /* input 2 is digital */
+ /* .digital_mode= DIGITAL_MODE_CAMERA, */
+ .gpiomux = { 0, 0, 0, 0 },
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ALPS_TSBB5_PAL_I,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x18 ---------------------------------- */
+ [BTTV_BOARD_MAGICTVIEW061] = {
+ .name = "Askey CPH05X/06X (bt878) [many vendors]",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xe00,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = {0x400, 0x400, 0x400, 0x400 },
+ .gpiomute = 0xc00,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .has_remote = 1,
+ },
+ [BTTV_BOARD_VOBIS_BOOSTAR] = {
+ .name = "Terratec TerraTV+ Version 1.0 (Bt848)/ Terra TValue Version 1.0/ Vobis TV-Boostar",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x1f0fff,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x20000, 0x30000, 0x10000, 0 },
+ .gpiomute = 0x40000,
+ .needs_tvaudio = 0,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .audio_mode_gpio= terratv_audio,
+ },
+ [BTTV_BOARD_HAUPPAUG_WCAM] = {
+ .name = "Hauppauge WinCam newer (bt878)",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 3,
+ .gpiomask = 7,
+ .muxsel = MUXSEL(2, 0, 1, 1),
+ .gpiomux = { 0, 1, 2, 3 },
+ .gpiomute = 4,
+ .needs_tvaudio = 1,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_MAXI] = {
+ .name = "Lifeview FlyVideo 98/ MAXI TV Video PCI2 LR50",
+ .video_inputs = 4,
+ /* .audio_inputs= 2, */
+ .svhs = 2,
+ .gpiomask = 0x1800,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0x800, 0x1000, 0x1000 },
+ .gpiomute = 0x1800,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_SECAM,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x1c ---------------------------------- */
+ [BTTV_BOARD_TERRATV] = {
+ .name = "Terratec TerraTV+ Version 1.1 (bt878)",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x1f0fff,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x20000, 0x30000, 0x10000, 0x00000 },
+ .gpiomute = 0x40000,
+ .needs_tvaudio = 0,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .audio_mode_gpio= terratv_audio,
+ /* GPIO wiring:
+ External 20 pin connector (for Active Radio Upgrade board)
+ gpio00: i2c-sda
+ gpio01: i2c-scl
+ gpio02: om5610-data
+ gpio03: om5610-clk
+ gpio04: om5610-wre
+ gpio05: om5610-stereo
+ gpio06: rds6588-davn
+ gpio07: Pin 7 n.c.
+ gpio08: nIOW
+ gpio09+10: nIOR, nSEL ?? (bt878)
+ gpio09: nIOR (bt848)
+ gpio10: nSEL (bt848)
+ Sound Routing:
+ gpio16: u2-A0 (1st 4052bt)
+ gpio17: u2-A1
+ gpio18: u2-nEN
+ gpio19: u4-A0 (2nd 4052)
+ gpio20: u4-A1
+ u4-nEN - GND
+ Btspy:
+ 00000 : Cdrom (internal audio input)
+ 10000 : ext. Video audio input
+ 20000 : TV Mono
+ a0000 : TV Mono/2
+ 1a0000 : TV Stereo
+ 30000 : Radio
+ 40000 : Mute
+ */
+
+ },
+ [BTTV_BOARD_PXC200] = {
+ /* Jannik Fritsch <jannik@techfak.uni-bielefeld.de> */
+ .name = "Imagenation PXC200",
+ .video_inputs = 5,
+ /* .audio_inputs= 1, */
+ .svhs = 1, /* was: 4 */
+ .gpiomask = 0,
+ .muxsel = MUXSEL(2, 3, 1, 0, 0),
+ .gpiomux = { 0 },
+ .needs_tvaudio = 1,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .muxsel_hook = PXC200_muxsel,
+
+ },
+ [BTTV_BOARD_FLYVIDEO_98] = {
+ .name = "Lifeview FlyVideo 98 LR50",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x1800, /* 0x8dfe00 */
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0x0800, 0x1000, 0x1000 },
+ .gpiomute = 0x1800,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_IPROTV] = {
+ .name = "Formac iProTV, Formac ProTV I (bt848)",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 3,
+ .gpiomask = 1,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 1, 0, 0, 0 },
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x20 ---------------------------------- */
+ [BTTV_BOARD_INTEL_C_S_PCI] = {
+ .name = "Intel Create and Share PCI/ Smart Video Recorder III",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = 2,
+ .gpiomask = 0,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0 },
+ .needs_tvaudio = 0,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_TERRATVALUE] = {
+ .name = "Terratec TerraTValue Version Bt878",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xffff00,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x500, 0, 0x300, 0x900 },
+ .gpiomute = 0x900,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_WINFAST2000] = {
+ .name = "Leadtek WinFast 2000/ WinFast 2000 XP",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ /* TV, CVid, SVid, CVid over SVid connector */
+ .muxsel = MUXSEL(2, 3, 1, 1, 0),
+ /* Alexander Varakin <avarakin@hotmail.com> [stereo version] */
+ .gpiomask = 0xb33000,
+ .gpiomux = { 0x122000,0x1000,0x0000,0x620000 },
+ .gpiomute = 0x800000,
+ /* Audio Routing for "WinFast 2000 XP" (no tv stereo !)
+ gpio23 -- hef4052:nEnable (0x800000)
+ gpio12 -- hef4052:A1
+ gpio13 -- hef4052:A0
+ 0x0000: external audio
+ 0x1000: FM
+ 0x2000: TV
+ 0x3000: n.c.
+ Note: There exists another variant "Winfast 2000" with tv stereo !?
+ Note: eeprom only contains FF and pci subsystem id 107d:6606
+ */
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+ .has_radio = 1,
+ .tuner_type = TUNER_PHILIPS_PAL, /* default for now, gpio reads BFFF06 for Pal bg+dk */
+ .tuner_addr = ADDR_UNSET,
+ .audio_mode_gpio= winfast2000_audio,
+ .has_remote = 1,
+ },
+ [BTTV_BOARD_CHRONOS_VS2] = {
+ .name = "Lifeview FlyVideo 98 LR50 / Chronos Video Shuttle II",
+ .video_inputs = 4,
+ /* .audio_inputs= 3, */
+ .svhs = 2,
+ .gpiomask = 0x1800,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0x800, 0x1000, 0x1000 },
+ .gpiomute = 0x1800,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x24 ---------------------------------- */
+ [BTTV_BOARD_TYPHOON_TVIEW] = {
+ .name = "Lifeview FlyVideo 98FM LR50 / Typhoon TView TV/FM Tuner",
+ .video_inputs = 4,
+ /* .audio_inputs= 3, */
+ .svhs = 2,
+ .gpiomask = 0x1800,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0x800, 0x1000, 0x1000 },
+ .gpiomute = 0x1800,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .has_radio = 1,
+ },
+ [BTTV_BOARD_PXELVWPLTVPRO] = {
+ .name = "Prolink PixelView PlayTV pro",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xff,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x21, 0x20, 0x24, 0x2c },
+ .gpiomute = 0x29,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_MAGICTVIEW063] = {
+ .name = "Askey CPH06X TView99",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x551e00,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = { 0x551400, 0x551200, 0, 0 },
+ .gpiomute = 0x551c00,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL_I,
+ .tuner_addr = ADDR_UNSET,
+ .has_remote = 1,
+ },
+ [BTTV_BOARD_PINNACLE] = {
+ .name = "Pinnacle PCTV Studio/Rave",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x03000F,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 2, 0xd0001, 0, 0 },
+ .gpiomute = 1,
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x28 ---------------------------------- */
+ [BTTV_BOARD_STB2] = {
+ .name = "STB TV PCI FM, Gateway P/N 6000704 (bt878), 3Dfx VoodooTV 100",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 7,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 4, 0, 2, 3 },
+ .gpiomute = 1,
+ .no_msp34xx = 1,
+ .needs_tvaudio = 1,
+ .tuner_type = TUNER_PHILIPS_NTSC,
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .has_radio = 1,
+ },
+ [BTTV_BOARD_AVPHONE98] = {
+ .name = "AVerMedia TVPhone 98",
+ .video_inputs = 3,
+ /* .audio_inputs= 4, */
+ .svhs = 2,
+ .gpiomask = 15,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 13, 4, 11, 7 },
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .has_radio = 1,
+ .audio_mode_gpio= avermedia_tvphone_audio,
+ },
+ [BTTV_BOARD_PV951] = {
+ .name = "ProVideo PV951", /* pic16c54 */
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0, 0, 0},
+ .needs_tvaudio = 1,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL_I,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_ONAIR_TV] = {
+ .name = "Little OnAir TV",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xe00b,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0xff9ff6, 0xff9ff6, 0xff1ff7, 0 },
+ .gpiomute = 0xff3ffc,
+ .no_msp34xx = 1,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x2c ---------------------------------- */
+ [BTTV_BOARD_SIGMA_TVII_FM] = {
+ .name = "Sigma TVII-FM",
+ .video_inputs = 2,
+ /* .audio_inputs= 1, */
+ .svhs = NO_SVHS,
+ .gpiomask = 3,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 1, 1, 0, 2 },
+ .gpiomute = 3,
+ .no_msp34xx = 1,
+ .pll = PLL_NONE,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_MATRIX_VISION2] = {
+ .name = "MATRIX-Vision MV-Delta 2",
+ .video_inputs = 5,
+ /* .audio_inputs= 1, */
+ .svhs = 3,
+ .gpiomask = 0,
+ .muxsel = MUXSEL(2, 3, 1, 0, 0),
+ .gpiomux = { 0 },
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_ZOLTRIX_GENIE] = {
+ .name = "Zoltrix Genie TV/FM",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xbcf03f,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0xbc803f, 0xbc903f, 0xbcb03f, 0 },
+ .gpiomute = 0xbcb03f,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_TEMIC_4039FR5_NTSC,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_TERRATVRADIO] = {
+ .name = "Terratec TV/Radio+",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x70000,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x20000, 0x30000, 0x10000, 0 },
+ .gpiomute = 0x40000,
+ .needs_tvaudio = 1,
+ .no_msp34xx = 1,
+ .pll = PLL_35,
+ .tuner_type = TUNER_PHILIPS_PAL_I,
+ .tuner_addr = ADDR_UNSET,
+ .has_radio = 1,
+ },
+
+ /* ---- card 0x30 ---------------------------------- */
+ [BTTV_BOARD_DYNALINK] = {
+ .name = "Askey CPH03x/ Dynalink Magic TView",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 15,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = {2,0,0,0 },
+ .gpiomute = 1,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_GVBCTV3PCI] = {
+ .name = "IODATA GV-BCTV3/PCI",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x010f00,
+ .muxsel = MUXSEL(2, 3, 0, 0),
+ .gpiomux = {0x10000, 0, 0x10000, 0 },
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ALPS_TSHC6_NTSC,
+ .tuner_addr = ADDR_UNSET,
+ .audio_mode_gpio= gvbctv3pci_audio,
+ },
+ [BTTV_BOARD_PXELVWPLTVPAK] = {
+ .name = "Prolink PV-BT878P+4E / PixelView PlayTV PAK / Lenco MXTV-9578 CP",
+ .video_inputs = 5,
+ /* .audio_inputs= 1, */
+ .svhs = 3,
+ .has_dig_in = 1,
+ .gpiomask = 0xAA0000,
+ .muxsel = MUXSEL(2, 3, 1, 1, 0), /* in 4 is digital */
+ /* .digital_mode= DIGITAL_MODE_CAMERA, */
+ .gpiomux = { 0x20000, 0, 0x80000, 0x80000 },
+ .gpiomute = 0xa8000,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL_I,
+ .tuner_addr = ADDR_UNSET,
+ .has_remote = 1,
+ /* GPIO wiring: (different from Rev.4C !)
+ GPIO17: U4.A0 (first hef4052bt)
+ GPIO19: U4.A1
+ GPIO20: U5.A1 (second hef4052bt)
+ GPIO21: U4.nEN
+ GPIO22: BT832 Reset Line
+ GPIO23: A5,A0, U5,nEN
+ Note: At i2c=0x8a is a Bt832 chip, which changes to 0x88 after being reset via GPIO22
+ */
+ },
+ [BTTV_BOARD_EAGLE] = {
+ .name = "Eagle Wireless Capricorn2 (bt878A)",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 7,
+ .muxsel = MUXSEL(2, 0, 1, 1),
+ .gpiomux = { 0, 1, 2, 3 },
+ .gpiomute = 4,
+ .pll = PLL_28,
+ .tuner_type = UNSET /* TUNER_ALPS_TMDH2_NTSC */,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x34 ---------------------------------- */
+ [BTTV_BOARD_PINNACLEPRO] = {
+ /* David Härdeman <david@2gen.com> */
+ .name = "Pinnacle PCTV Studio Pro",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 3,
+ .gpiomask = 0x03000F,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 1, 0xd0001, 0, 0 },
+ .gpiomute = 10,
+ /* sound path (5 sources):
+ MUX1 (mask 0x03), Enable Pin 0x08 (0=enable, 1=disable)
+ 0= ext. Audio IN
+ 1= from MUX2
+ 2= Mono TV sound from Tuner
+ 3= not connected
+ MUX2 (mask 0x30000):
+ 0,2,3= from MSP34xx
+ 1= FM stereo Radio from Tuner */
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_TVIEW_RDS_FM] = {
+ /* Claas Langbehn <claas@bigfoot.com>,
+ Sven Grothklags <sven@upb.de> */
+ .name = "Typhoon TView RDS + FM Stereo / KNC1 TV Station RDS",
+ .video_inputs = 4,
+ /* .audio_inputs= 3, */
+ .svhs = 2,
+ .gpiomask = 0x1c,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0, 0x10, 8 },
+ .gpiomute = 4,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .has_radio = 1,
+ },
+ [BTTV_BOARD_LIFETEC_9415] = {
+ /* Tim Röstermundt <rosterm@uni-muenster.de>
+ in de.comp.os.unix.linux.hardware:
+ options bttv card=0 pll=1 radio=1 gpiomask=0x18e0
+ gpiomux =0x44c71f,0x44d71f,0,0x44d71f,0x44dfff
+ options tuner type=5 */
+ .name = "Lifeview FlyVideo 2000 /FlyVideo A2/ Lifetec LT 9415 TV [LR90]",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x18e0,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x0000,0x0800,0x1000,0x1000 },
+ .gpiomute = 0x18e0,
+ /* For cards with tda9820/tda9821:
+ 0x0000: Tuner normal stereo
+ 0x0080: Tuner A2 SAP (second audio program = Zweikanalton)
+ 0x0880: Tuner A2 stereo */
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_BESTBUY_EASYTV] = {
+ /* Miguel Angel Alvarez <maacruz@navegalia.com>
+ old Easy TV BT848 version (model CPH031) */
+ .name = "Askey CPH031/ BESTBUY Easy TV",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xF,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = { 2, 0, 0, 0 },
+ .gpiomute = 10,
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+ .tuner_type = TUNER_TEMIC_PAL,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x38 ---------------------------------- */
+ [BTTV_BOARD_FLYVIDEO_98FM] = {
+ /* Gordon Heydon <gjheydon@bigfoot.com ('98) */
+ .name = "Lifeview FlyVideo 98FM LR50",
+ .video_inputs = 4,
+ /* .audio_inputs= 3, */
+ .svhs = 2,
+ .gpiomask = 0x1800,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0x800, 0x1000, 0x1000 },
+ .gpiomute = 0x1800,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ },
+ /* This is the ultimate cheapo capture card
+ * just a BT848A on a small PCB!
+ * Steve Hosgood <steve@equiinet.com> */
+ [BTTV_BOARD_GRANDTEC] = {
+ .name = "GrandTec 'Grand Video Capture' (Bt848)",
+ .video_inputs = 2,
+ /* .audio_inputs= 0, */
+ .svhs = 1,
+ .gpiomask = 0,
+ .muxsel = MUXSEL(3, 1),
+ .gpiomux = { 0 },
+ .needs_tvaudio = 0,
+ .no_msp34xx = 1,
+ .pll = PLL_35,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_ASKEY_CPH060] = {
+ /* Daniel Herrington <daniel.herrington@home.com> */
+ .name = "Askey CPH060/ Phoebe TV Master Only (No FM)",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xe00,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x400, 0x400, 0x400, 0x400 },
+ .gpiomute = 0x800,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_TEMIC_4036FY5_NTSC,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_ASKEY_CPH03X] = {
+ /* Matti Mottus <mottus@physic.ut.ee> */
+ .name = "Askey CPH03x TV Capturer",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x03000F,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = { 2, 0, 0, 0 },
+ .gpiomute = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_TEMIC_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .has_remote = 1,
+ },
+
+ /* ---- card 0x3c ---------------------------------- */
+ [BTTV_BOARD_MM100PCTV] = {
+ /* Philip Blundell <philb@gnu.org> */
+ .name = "Modular Technology MM100PCTV",
+ .video_inputs = 2,
+ /* .audio_inputs= 2, */
+ .svhs = NO_SVHS,
+ .gpiomask = 11,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 2, 0, 0, 1 },
+ .gpiomute = 8,
+ .pll = PLL_35,
+ .tuner_type = TUNER_TEMIC_PAL,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_GMV1] = {
+ /* Adrian Cox <adrian@humboldt.co.uk */
+ .name = "AG Electronics GMV1",
+ .video_inputs = 2,
+ /* .audio_inputs= 0, */
+ .svhs = 1,
+ .gpiomask = 0xF,
+ .muxsel = MUXSEL(2, 2),
+ .gpiomux = { },
+ .no_msp34xx = 1,
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_BESTBUY_EASYTV2] = {
+ /* Miguel Angel Alvarez <maacruz@navegalia.com>
+ new Easy TV BT878 version (model CPH061)
+ special thanks to Informatica Mieres for providing the card */
+ .name = "Askey CPH061/ BESTBUY Easy TV (bt878)",
+ .video_inputs = 3,
+ /* .audio_inputs= 2, */
+ .svhs = 2,
+ .gpiomask = 0xFF,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = { 1, 0, 4, 4 },
+ .gpiomute = 9,
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_ATI_TVWONDER] = {
+ /* Lukas Gebauer <geby@volny.cz> */
+ .name = "ATI TV-Wonder",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xf03f,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = { 0xbffe, 0, 0xbfff, 0 },
+ .gpiomute = 0xbffe,
+ .pll = PLL_28,
+ .tuner_type = TUNER_TEMIC_4006FN5_MULTI_PAL,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x40 ---------------------------------- */
+ [BTTV_BOARD_ATI_TVWONDERVE] = {
+ /* Lukas Gebauer <geby@volny.cz> */
+ .name = "ATI TV-Wonder VE",
+ .video_inputs = 2,
+ /* .audio_inputs= 1, */
+ .svhs = NO_SVHS,
+ .gpiomask = 1,
+ .muxsel = MUXSEL(2, 3, 0, 1),
+ .gpiomux = { 0, 0, 1, 0 },
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_TEMIC_4006FN5_MULTI_PAL,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_FLYVIDEO2000] = {
+ /* DeeJay <deejay@westel900.net (2000S) */
+ .name = "Lifeview FlyVideo 2000S LR90",
+ .video_inputs = 3,
+ /* .audio_inputs= 3, */
+ .svhs = 2,
+ .gpiomask = 0x18e0,
+ .muxsel = MUXSEL(2, 3, 0, 1),
+ /* Radio changed from 1e80 to 0x800 to make
+ FlyVideo2000S in .hu happy (gm)*/
+ /* -dk-???: set mute=0x1800 for tda9874h daughterboard */
+ .gpiomux = { 0x0000,0x0800,0x1000,0x1000 },
+ .gpiomute = 0x1800,
+ .audio_mode_gpio= fv2000s_audio,
+ .no_msp34xx = 1,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_TERRATVALUER] = {
+ .name = "Terratec TValueRadio",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xffff00,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x500, 0x500, 0x300, 0x900 },
+ .gpiomute = 0x900,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .has_radio = 1,
+ },
+ [BTTV_BOARD_GVBCTV4PCI] = {
+ /* TANAKA Kei <peg00625@nifty.com> */
+ .name = "IODATA GV-BCTV4/PCI",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x010f00,
+ .muxsel = MUXSEL(2, 3, 0, 0),
+ .gpiomux = {0x10000, 0, 0x10000, 0 },
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_SHARP_2U5JF5540_NTSC,
+ .tuner_addr = ADDR_UNSET,
+ .audio_mode_gpio= gvbctv3pci_audio,
+ },
+
+ /* ---- card 0x44 ---------------------------------- */
+ [BTTV_BOARD_VOODOOTV_FM] = {
+ .name = "3Dfx VoodooTV FM (Euro)",
+ /* try "insmod msp3400 simple=0" if you have
+ * sound problems with this card. */
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0x4f8a00,
+ /* 0x100000: 1=MSP enabled (0=disable again)
+ * 0x010000: Connected to "S0" on tda9880 (0=Pal/BG, 1=NTSC) */
+ .gpiomux = {0x947fff, 0x987fff,0x947fff,0x947fff },
+ .gpiomute = 0x947fff,
+ /* tvtuner, radio, external,internal, mute, stereo
+ * tuner, Composit, SVid, Composit-on-Svid-adapter */
+ .muxsel = MUXSEL(2, 3, 0, 1),
+ .tuner_type = TUNER_MT2032,
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .has_radio = 1,
+ },
+ [BTTV_BOARD_VOODOOTV_200] = {
+ .name = "VoodooTV 200 (USA)",
+ /* try "insmod msp3400 simple=0" if you have
+ * sound problems with this card. */
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0x4f8a00,
+ /* 0x100000: 1=MSP enabled (0=disable again)
+ * 0x010000: Connected to "S0" on tda9880 (0=Pal/BG, 1=NTSC) */
+ .gpiomux = {0x947fff, 0x987fff,0x947fff,0x947fff },
+ .gpiomute = 0x947fff,
+ /* tvtuner, radio, external,internal, mute, stereo
+ * tuner, Composit, SVid, Composit-on-Svid-adapter */
+ .muxsel = MUXSEL(2, 3, 0, 1),
+ .tuner_type = TUNER_MT2032,
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .has_radio = 1,
+ },
+ [BTTV_BOARD_AIMMS] = {
+ /* Philip Blundell <pb@nexus.co.uk> */
+ .name = "Active Imaging AIMMS",
+ .video_inputs = 1,
+ /* .audio_inputs= 0, */
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .muxsel = MUXSEL(2),
+ .gpiomask = 0
+ },
+ [BTTV_BOARD_PV_BT878P_PLUS] = {
+ /* Tomasz Pyra <hellfire@sedez.iq.pl> */
+ .name = "Prolink Pixelview PV-BT878P+ (Rev.4C,8E)",
+ .video_inputs = 3,
+ /* .audio_inputs= 4, */
+ .svhs = 2,
+ .gpiomask = 15,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0, 11, 7 }, /* TV and Radio with same GPIO ! */
+ .gpiomute = 13,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_LG_PAL_I_FM,
+ .tuner_addr = ADDR_UNSET,
+ .has_remote = 1,
+ /* GPIO wiring:
+ GPIO0: U4.A0 (hef4052bt)
+ GPIO1: U4.A1
+ GPIO2: U4.A1 (second hef4052bt)
+ GPIO3: U4.nEN, U5.A0, A5.nEN
+ GPIO8-15: vrd866b ?
+ */
+ },
+ [BTTV_BOARD_FLYVIDEO98EZ] = {
+ .name = "Lifeview FlyVideo 98EZ (capture only) LR51",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = 2,
+ /* AV1, AV2, SVHS, CVid adapter on SVHS */
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .pll = PLL_28,
+ .no_msp34xx = 1,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x48 ---------------------------------- */
+ [BTTV_BOARD_PV_BT878P_9B] = {
+ /* Dariusz Kowalewski <darekk@automex.pl> */
+ .name = "Prolink Pixelview PV-BT878P+9B (PlayTV Pro rev.9B FM+NICAM)",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x3f,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x01, 0x00, 0x03, 0x03 },
+ .gpiomute = 0x09,
+ .needs_tvaudio = 1,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .audio_mode_gpio= pvbt878p9b_audio, /* Note: not all cards have stereo */
+ .has_radio = 1, /* Note: not all cards have radio */
+ .has_remote = 1,
+ /* GPIO wiring:
+ GPIO0: A0 hef4052
+ GPIO1: A1 hef4052
+ GPIO3: nEN hef4052
+ GPIO8-15: vrd866b
+ GPIO20,22,23: R30,R29,R28
+ */
+ },
+ [BTTV_BOARD_SENSORAY311_611] = {
+ /* Clay Kunz <ckunz@mail.arc.nasa.gov> */
+ /* you must jumper JP5 for the 311 card (PC/104+) to work */
+ .name = "Sensoray 311/611",
+ .video_inputs = 5,
+ /* .audio_inputs= 0, */
+ .svhs = 4,
+ .gpiomask = 0,
+ .muxsel = MUXSEL(2, 3, 1, 0, 0),
+ .gpiomux = { 0 },
+ .needs_tvaudio = 0,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_RV605] = {
+ /* Miguel Freitas <miguel@cetuc.puc-rio.br> */
+ .name = "RemoteVision MX (RV605)",
+ .video_inputs = 16,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0x00,
+ .gpiomask2 = 0x07ff,
+ .muxsel = MUXSEL(3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3),
+ .no_msp34xx = 1,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .muxsel_hook = rv605_muxsel,
+ },
+ [BTTV_BOARD_POWERCLR_MTV878] = {
+ .name = "Powercolor MTV878/ MTV878R/ MTV878F",
+ .video_inputs = 3,
+ /* .audio_inputs= 2, */
+ .svhs = 2,
+ .gpiomask = 0x1C800F, /* Bit0-2: Audio select, 8-12:remote control 14:remote valid 15:remote reset */
+ .muxsel = MUXSEL(2, 1, 1),
+ .gpiomux = { 0, 1, 2, 2 },
+ .gpiomute = 4,
+ .needs_tvaudio = 0,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .has_radio = 1,
+ },
+
+ /* ---- card 0x4c ---------------------------------- */
+ [BTTV_BOARD_WINDVR] = {
+ /* Masaki Suzuki <masaki@btree.org> */
+ .name = "Canopus WinDVR PCI (COMPAQ Presario 3524JP, 5112JP)",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x140007,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 1, 2, 3 },
+ .gpiomute = 4,
+ .tuner_type = TUNER_PHILIPS_NTSC,
+ .tuner_addr = ADDR_UNSET,
+ .audio_mode_gpio= windvr_audio,
+ },
+ [BTTV_BOARD_GRANDTEC_MULTI] = {
+ .name = "GrandTec Multi Capture Card (Bt878)",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = { 0 },
+ .needs_tvaudio = 0,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_KWORLD] = {
+ .name = "Jetway TV/Capture JW-TV878-FBK, Kworld KW-TV878RF",
+ .video_inputs = 4,
+ /* .audio_inputs= 3, */
+ .svhs = 2,
+ .gpiomask = 7,
+ /* Tuner, SVid, SVHS, SVid to SVHS connector */
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0, 4, 4 },/* Yes, this tuner uses the same audio output for TV and FM radio!
+ * This card lacks external Audio In, so we mute it on Ext. & Int.
+ * The PCB can take a sbx1637/sbx1673, wiring unknown.
+ * This card lacks PCI subsystem ID, sigh.
+ * gpiomux =1: lower volume, 2+3: mute
+ * btwincap uses 0x80000/0x80003
+ */
+ .gpiomute = 4,
+ .needs_tvaudio = 0,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ /* Samsung TCPA9095PC27A (BG+DK), philips compatible, w/FM, stereo and
+ radio signal strength indicators work fine. */
+ .has_radio = 1,
+ /* GPIO Info:
+ GPIO0,1: HEF4052 A0,A1
+ GPIO2: HEF4052 nENABLE
+ GPIO3-7: n.c.
+ GPIO8-13: IRDC357 data0-5 (data6 n.c. ?) [chip not present on my card]
+ GPIO14,15: ??
+ GPIO16-21: n.c.
+ GPIO22,23: ??
+ ?? : mtu8b56ep microcontroller for IR (GPIO wiring unknown)*/
+ },
+ [BTTV_BOARD_DSP_TCVIDEO] = {
+ /* Arthur Tetzlaff-Deas, DSP Design Ltd <software@dspdesign.com> */
+ .name = "DSP Design TCVIDEO",
+ .video_inputs = 4,
+ .svhs = NO_SVHS,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x50 ---------------------------------- */
+ [BTTV_BOARD_HAUPPAUGEPVR] = {
+ .name = "Hauppauge WinTV PVR",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .muxsel = MUXSEL(2, 0, 1, 1),
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+
+ .gpiomask = 7,
+ .gpiomux = {7},
+ },
+ [BTTV_BOARD_GVBCTV5PCI] = {
+ .name = "IODATA GV-BCTV5/PCI",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x0f0f80,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = {0x030000, 0x010000, 0, 0 },
+ .gpiomute = 0x020000,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .tuner_addr = ADDR_UNSET,
+ .audio_mode_gpio= gvbctv5pci_audio,
+ .has_radio = 1,
+ },
+ [BTTV_BOARD_OSPREY1x0] = {
+ .name = "Osprey 100/150 (878)", /* 0x1(2|3)-45C6-C1 */
+ .video_inputs = 4, /* id-inputs-clock */
+ /* .audio_inputs= 0, */
+ .svhs = 3,
+ .muxsel = MUXSEL(3, 2, 0, 1),
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ },
+ [BTTV_BOARD_OSPREY1x0_848] = {
+ .name = "Osprey 100/150 (848)", /* 0x04-54C0-C1 & older boards */
+ .video_inputs = 3,
+ /* .audio_inputs= 0, */
+ .svhs = 2,
+ .muxsel = MUXSEL(2, 3, 1),
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ },
+
+ /* ---- card 0x54 ---------------------------------- */
+ [BTTV_BOARD_OSPREY101_848] = {
+ .name = "Osprey 101 (848)", /* 0x05-40C0-C1 */
+ .video_inputs = 2,
+ /* .audio_inputs= 0, */
+ .svhs = 1,
+ .muxsel = MUXSEL(3, 1),
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ },
+ [BTTV_BOARD_OSPREY1x1] = {
+ .name = "Osprey 101/151", /* 0x1(4|5)-0004-C4 */
+ .video_inputs = 1,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .muxsel = MUXSEL(0),
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ },
+ [BTTV_BOARD_OSPREY1x1_SVID] = {
+ .name = "Osprey 101/151 w/ svid", /* 0x(16|17|20)-00C4-C1 */
+ .video_inputs = 2,
+ /* .audio_inputs= 0, */
+ .svhs = 1,
+ .muxsel = MUXSEL(0, 1),
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ },
+ [BTTV_BOARD_OSPREY2xx] = {
+ .name = "Osprey 200/201/250/251", /* 0x1(8|9|E|F)-0004-C4 */
+ .video_inputs = 1,
+ /* .audio_inputs= 1, */
+ .svhs = NO_SVHS,
+ .muxsel = MUXSEL(0),
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ },
+
+ /* ---- card 0x58 ---------------------------------- */
+ [BTTV_BOARD_OSPREY2x0_SVID] = {
+ .name = "Osprey 200/250", /* 0x1(A|B)-00C4-C1 */
+ .video_inputs = 2,
+ /* .audio_inputs= 1, */
+ .svhs = 1,
+ .muxsel = MUXSEL(0, 1),
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ },
+ [BTTV_BOARD_OSPREY2x0] = {
+ .name = "Osprey 210/220/230", /* 0x1(A|B)-04C0-C1 */
+ .video_inputs = 2,
+ /* .audio_inputs= 1, */
+ .svhs = 1,
+ .muxsel = MUXSEL(2, 3),
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ },
+ [BTTV_BOARD_OSPREY500] = {
+ .name = "Osprey 500", /* 500 */
+ .video_inputs = 2,
+ /* .audio_inputs= 1, */
+ .svhs = 1,
+ .muxsel = MUXSEL(2, 3),
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ },
+ [BTTV_BOARD_OSPREY540] = {
+ .name = "Osprey 540", /* 540 */
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ },
+
+ /* ---- card 0x5C ---------------------------------- */
+ [BTTV_BOARD_OSPREY2000] = {
+ .name = "Osprey 2000", /* 2000 */
+ .video_inputs = 2,
+ /* .audio_inputs= 1, */
+ .svhs = 1,
+ .muxsel = MUXSEL(2, 3),
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1, /* must avoid, conflicts with the bt860 */
+ },
+ [BTTV_BOARD_IDS_EAGLE] = {
+ /* M G Berberich <berberic@forwiss.uni-passau.de> */
+ .name = "IDS Eagle",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .svhs = NO_SVHS,
+ .gpiomask = 0,
+ .muxsel = MUXSEL(2, 2, 2, 2),
+ .muxsel_hook = eagle_muxsel,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ },
+ [BTTV_BOARD_PINNACLESAT] = {
+ .name = "Pinnacle PCTV Sat",
+ .video_inputs = 2,
+ /* .audio_inputs= 0, */
+ .svhs = 1,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .muxsel = MUXSEL(3, 1),
+ .pll = PLL_28,
+ .no_gpioirq = 1,
+ .has_dvb = 1,
+ },
+ [BTTV_BOARD_FORMAC_PROTV] = {
+ .name = "Formac ProTV II (bt878)",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 3,
+ .gpiomask = 2,
+ /* TV, Comp1, Composite over SVID con, SVID */
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 2, 2, 0, 0 },
+ .pll = PLL_28,
+ .has_radio = 1,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ /* sound routing:
+ GPIO=0x00,0x01,0x03: mute (?)
+ 0x02: both TV and radio (tuner: FM1216/I)
+ The card has onboard audio connectors labeled "cdrom" and "board",
+ not soldered here, though unknown wiring.
+ Card lacks: external audio in, pci subsystem id.
+ */
+ },
+
+ /* ---- card 0x60 ---------------------------------- */
+ [BTTV_BOARD_MACHTV] = {
+ .name = "MachTV",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = NO_SVHS,
+ .gpiomask = 7,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 1, 2, 3},
+ .gpiomute = 4,
+ .needs_tvaudio = 1,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ },
+ [BTTV_BOARD_EURESYS_PICOLO] = {
+ .name = "Euresys Picolo",
+ .video_inputs = 3,
+ /* .audio_inputs= 0, */
+ .svhs = 2,
+ .gpiomask = 0,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .muxsel = MUXSEL(2, 0, 1),
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_PV150] = {
+ /* Luc Van Hoeylandt <luc@e-magic.be> */
+ .name = "ProVideo PV150", /* 0x4f */
+ .video_inputs = 2,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0,
+ .muxsel = MUXSEL(2, 3),
+ .gpiomux = { 0 },
+ .needs_tvaudio = 0,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_AD_TVK503] = {
+ /* Hiroshi Takekawa <sian@big.or.jp> */
+ /* This card lacks subsystem ID */
+ .name = "AD-TVK503", /* 0x63 */
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x001e8007,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ /* Tuner, Radio, external, internal, off, on */
+ .gpiomux = { 0x08, 0x0f, 0x0a, 0x08 },
+ .gpiomute = 0x0f,
+ .needs_tvaudio = 0,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_NTSC,
+ .tuner_addr = ADDR_UNSET,
+ .audio_mode_gpio= adtvk503_audio,
+ },
+
+ /* ---- card 0x64 ---------------------------------- */
+ [BTTV_BOARD_HERCULES_SM_TV] = {
+ .name = "Hercules Smart TV Stereo",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x00,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .needs_tvaudio = 1,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ /* Notes:
+ - card lacks subsystem ID
+ - stereo variant w/ daughter board with tda9874a @0xb0
+ - Audio Routing:
+ always from tda9874 independent of GPIO (?)
+ external line in: unknown
+ - Other chips: em78p156elp @ 0x96 (probably IR remote control)
+ hef4053 (instead 4052) for unknown function
+ */
+ },
+ [BTTV_BOARD_PACETV] = {
+ .name = "Pace TV & Radio Card",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ /* Tuner, CVid, SVid, CVid over SVid connector */
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomask = 0,
+ .no_tda7432 = 1,
+ .tuner_type = TUNER_PHILIPS_PAL_I,
+ .tuner_addr = ADDR_UNSET,
+ .has_radio = 1,
+ .pll = PLL_28,
+ /* Bt878, Bt832, FI1246 tuner; no pci subsystem id
+ only internal line out: (4pin header) RGGL
+ Radio must be decoded by msp3410d (not routed through)*/
+ /*
+ .digital_mode = DIGITAL_MODE_CAMERA, todo!
+ */
+ },
+ [BTTV_BOARD_IVC200] = {
+ /* Chris Willing <chris@vislab.usyd.edu.au> */
+ .name = "IVC-200",
+ .video_inputs = 1,
+ /* .audio_inputs= 0, */
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .svhs = NO_SVHS,
+ .gpiomask = 0xdf,
+ .muxsel = MUXSEL(2),
+ .pll = PLL_28,
+ },
+ [BTTV_BOARD_IVCE8784] = {
+ .name = "IVCE-8784",
+ .video_inputs = 1,
+ /* .audio_inputs= 0, */
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .svhs = NO_SVHS,
+ .gpiomask = 0xdf,
+ .muxsel = MUXSEL(2),
+ .pll = PLL_28,
+ },
+ [BTTV_BOARD_XGUARD] = {
+ .name = "Grand X-Guard / Trust 814PCI",
+ .video_inputs = 16,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .gpiomask2 = 0xff,
+ .muxsel = MUXSEL(2,2,2,2, 3,3,3,3, 1,1,1,1, 0,0,0,0),
+ .muxsel_hook = xguard_muxsel,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .pll = PLL_28,
+ },
+
+ /* ---- card 0x68 ---------------------------------- */
+ [BTTV_BOARD_NEBULA_DIGITV] = {
+ .name = "Nebula Electronics DigiTV",
+ .video_inputs = 1,
+ .svhs = NO_SVHS,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .has_dvb = 1,
+ .has_remote = 1,
+ .gpiomask = 0x1b,
+ .no_gpioirq = 1,
+ },
+ [BTTV_BOARD_PV143] = {
+ /* Jorge Boncompte - DTI2 <jorge@dti2.net> */
+ .name = "ProVideo PV143",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = { 0 },
+ .needs_tvaudio = 0,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_VD009X1_VD011_MINIDIN] = {
+ /* M.Klahr@phytec.de */
+ .name = "PHYTEC VD-009-X1 VD-011 MiniDIN (bt878)",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = 3,
+ .gpiomask = 0x00,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_VD009X1_VD011_COMBI] = {
+ .name = "PHYTEC VD-009-X1 VD-011 Combi (bt878)",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = 3,
+ .gpiomask = 0x00,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x6c ---------------------------------- */
+ [BTTV_BOARD_VD009_MINIDIN] = {
+ .name = "PHYTEC VD-009 MiniDIN (bt878)",
+ .video_inputs = 10,
+ /* .audio_inputs= 0, */
+ .svhs = 9,
+ .gpiomask = 0x00,
+ .gpiomask2 = 0x03, /* used for external vodeo mux */
+ .muxsel = MUXSEL(2, 2, 2, 2, 3, 3, 3, 3, 1, 0),
+ .muxsel_hook = phytec_muxsel,
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_VD009_COMBI] = {
+ .name = "PHYTEC VD-009 Combi (bt878)",
+ .video_inputs = 10,
+ /* .audio_inputs= 0, */
+ .svhs = 9,
+ .gpiomask = 0x00,
+ .gpiomask2 = 0x03, /* used for external vodeo mux */
+ .muxsel = MUXSEL(2, 2, 2, 2, 3, 3, 3, 3, 1, 1),
+ .muxsel_hook = phytec_muxsel,
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_IVC100] = {
+ .name = "IVC-100",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .svhs = NO_SVHS,
+ .gpiomask = 0xdf,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .pll = PLL_28,
+ },
+ [BTTV_BOARD_IVC120] = {
+ /* IVC-120G - Alan Garfield <alan@fromorbit.com> */
+ .name = "IVC-120G",
+ .video_inputs = 16,
+ /* .audio_inputs= 0, */
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .svhs = NO_SVHS, /* card has no svhs */
+ .needs_tvaudio = 0,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .gpiomask = 0x00,
+ .muxsel = MUXSEL(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
+ .muxsel_hook = ivc120_muxsel,
+ .pll = PLL_28,
+ },
+
+ /* ---- card 0x70 ---------------------------------- */
+ [BTTV_BOARD_PC_HDTV] = {
+ .name = "pcHDTV HD-2000 TV",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .tuner_type = TUNER_PHILIPS_FCV1236D,
+ .tuner_addr = ADDR_UNSET,
+ .has_dvb = 1,
+ },
+ [BTTV_BOARD_TWINHAN_DST] = {
+ .name = "Twinhan DST + clones",
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .no_video = 1,
+ .has_dvb = 1,
+ },
+ [BTTV_BOARD_WINFASTVC100] = {
+ .name = "Winfast VC100",
+ .video_inputs = 3,
+ /* .audio_inputs= 0, */
+ .svhs = 1,
+ /* Vid In, SVid In, Vid over SVid in connector */
+ .muxsel = MUXSEL(3, 1, 1, 3),
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ },
+ [BTTV_BOARD_TEV560] = {
+ .name = "Teppro TEV-560/InterVision IV-560",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 3,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 1, 1, 1, 1 },
+ .needs_tvaudio = 1,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_35,
+ },
+
+ /* ---- card 0x74 ---------------------------------- */
+ [BTTV_BOARD_SIMUS_GVC1100] = {
+ .name = "SIMUS GVC1100",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .muxsel = MUXSEL(2, 2, 2, 2),
+ .gpiomask = 0x3F,
+ .muxsel_hook = gvc1100_muxsel,
+ },
+ [BTTV_BOARD_NGSTV_PLUS] = {
+ /* Carlos Silva r3pek@r3pek.homelinux.org || card 0x75 */
+ .name = "NGS NGSTV+",
+ .video_inputs = 3,
+ .svhs = 2,
+ .gpiomask = 0x008007,
+ .muxsel = MUXSEL(2, 3, 0, 0),
+ .gpiomux = { 0, 0, 0, 0 },
+ .gpiomute = 0x000003,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .has_remote = 1,
+ },
+ [BTTV_BOARD_LMLBT4] = {
+ /* http://linuxmedialabs.com */
+ .name = "LMLBT4",
+ .video_inputs = 4, /* IN1,IN2,IN3,IN4 */
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .needs_tvaudio = 0,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_TEKRAM_M205] = {
+ /* Helmroos Harri <harri.helmroos@pp.inet.fi> */
+ .name = "Tekram M205 PRO",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .svhs = 2,
+ .needs_tvaudio = 0,
+ .gpiomask = 0x68,
+ .muxsel = MUXSEL(2, 3, 1),
+ .gpiomux = { 0x68, 0x68, 0x61, 0x61 },
+ .pll = PLL_28,
+ },
+
+ /* ---- card 0x78 ---------------------------------- */
+ [BTTV_BOARD_CONTVFMI] = {
+ /* Javier Cendan Ares <jcendan@lycos.es> */
+ /* bt878 TV + FM without subsystem ID */
+ .name = "Conceptronic CONTVFMi",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x008007,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 1, 2, 2 },
+ .gpiomute = 3,
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .has_remote = 1,
+ .has_radio = 1,
+ },
+ [BTTV_BOARD_PICOLO_TETRA_CHIP] = {
+ /*Eric DEBIEF <debief@telemsa.com>*/
+ /*EURESYS Picolo Tetra : 4 Conexant Fusion 878A, no audio, video input set with analog multiplexers GPIO controlled*/
+ /* adds picolo_tetra_muxsel(), picolo_tetra_init(), the following declaration strucure, and #define BTTV_BOARD_PICOLO_TETRA_CHIP*/
+ /*0x79 in bttv.h*/
+ .name = "Euresys Picolo Tetra",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0,
+ .gpiomask2 = 0x3C<<16,/*Set the GPIO[18]->GPIO[21] as output pin.==> drive the video inputs through analog multiplexers*/
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ /*878A input is always MUX0, see above.*/
+ .muxsel = MUXSEL(2, 2, 2, 2),
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .pll = PLL_28,
+ .needs_tvaudio = 0,
+ .muxsel_hook = picolo_tetra_muxsel,/*Required as it doesn't follow the classic input selection policy*/
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_SPIRIT_TV] = {
+ /* Spirit TV Tuner from http://spiritmodems.com.au */
+ /* Stafford Goodsell <surge@goliath.homeunix.org> */
+ .name = "Spirit TV Tuner",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x0000000f,
+ .muxsel = MUXSEL(2, 1, 1),
+ .gpiomux = { 0x02, 0x00, 0x00, 0x00 },
+ .tuner_type = TUNER_TEMIC_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ },
+ [BTTV_BOARD_AVDVBT_771] = {
+ /* Wolfram Joost <wojo@frokaschwei.de> */
+ .name = "AVerMedia AVerTV DVB-T 771",
+ .video_inputs = 2,
+ .svhs = 1,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .muxsel = MUXSEL(3, 3),
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .pll = PLL_28,
+ .has_dvb = 1,
+ .no_gpioirq = 1,
+ .has_remote = 1,
+ },
+ /* ---- card 0x7c ---------------------------------- */
+ [BTTV_BOARD_AVDVBT_761] = {
+ /* Matt Jesson <dvb@jesson.eclipse.co.uk> */
+ /* Based on the Nebula card data - added remote and new card number - BTTV_BOARD_AVDVBT_761, see also ir-kbd-gpio.c */
+ .name = "AverMedia AverTV DVB-T 761",
+ .video_inputs = 2,
+ .svhs = 1,
+ .muxsel = MUXSEL(3, 1, 2, 0), /* Comp0, S-Video, ?, ? */
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .has_dvb = 1,
+ .no_gpioirq = 1,
+ .has_remote = 1,
+ },
+ [BTTV_BOARD_MATRIX_VISIONSQ] = {
+ /* andre.schwarz@matrix-vision.de */
+ .name = "MATRIX Vision Sigma-SQ",
+ .video_inputs = 16,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0x0,
+ .muxsel = MUXSEL(2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3),
+ .muxsel_hook = sigmaSQ_muxsel,
+ .gpiomux = { 0 },
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_MATRIX_VISIONSLC] = {
+ /* andre.schwarz@matrix-vision.de */
+ .name = "MATRIX Vision Sigma-SLC",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0x0,
+ .muxsel = MUXSEL(2, 2, 2, 2),
+ .muxsel_hook = sigmaSLC_muxsel,
+ .gpiomux = { 0 },
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ /* BTTV_BOARD_APAC_VIEWCOMP */
+ [BTTV_BOARD_APAC_VIEWCOMP] = {
+ /* Attila Kondoros <attila.kondoros@chello.hu> */
+ /* bt878 TV + FM 0x00000000 subsystem ID */
+ .name = "APAC Viewcomp 878(AMAX)",
+ .video_inputs = 2,
+ /* .audio_inputs= 1, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0xFF,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 2, 0, 0, 0 },
+ .gpiomute = 10,
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .has_remote = 1, /* miniremote works, see ir-kbd-gpio.c */
+ .has_radio = 1, /* not every card has radio */
+ },
+
+ /* ---- card 0x80 ---------------------------------- */
+ [BTTV_BOARD_DVICO_DVBT_LITE] = {
+ /* Chris Pascoe <c.pascoe@itee.uq.edu.au> */
+ .name = "DViCO FusionHDTV DVB-T Lite",
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .pll = PLL_28,
+ .no_video = 1,
+ .has_dvb = 1,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_VGEAR_MYVCD] = {
+ /* Steven <photon38@pchome.com.tw> */
+ .name = "V-Gear MyVCD",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x3f,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = {0x31, 0x31, 0x31, 0x31 },
+ .gpiomute = 0x31,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .tuner_addr = ADDR_UNSET,
+ .has_radio = 0,
+ },
+ [BTTV_BOARD_SUPER_TV] = {
+ /* Rick C <cryptdragoon@gmail.com> */
+ .name = "Super TV Tuner",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .tuner_type = TUNER_PHILIPS_NTSC,
+ .tuner_addr = ADDR_UNSET,
+ .gpiomask = 0x008007,
+ .gpiomux = { 0, 0x000001,0,0 },
+ .needs_tvaudio = 1,
+ .has_radio = 1,
+ },
+ [BTTV_BOARD_TIBET_CS16] = {
+ /* Chris Fanning <video4linux@haydon.net> */
+ .name = "Tibet Systems 'Progress DVR' CS16",
+ .video_inputs = 16,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .muxsel = MUXSEL(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2),
+ .pll = PLL_28,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .muxsel_hook = tibetCS16_muxsel,
+ },
+ [BTTV_BOARD_KODICOM_4400R] = {
+ /* Bill Brack <wbrack@mmm.com.hk> */
+ /*
+ * Note that, because of the card's wiring, the "master"
+ * BT878A chip (i.e. the one which controls the analog switch
+ * and must use this card type) is the 2nd one detected. The
+ * other 3 chips should use card type 0x85, whose description
+ * follows this one. There is a EEPROM on the card (which is
+ * connected to the I2C of one of those other chips), but is
+ * not currently handled. There is also a facility for a
+ * "monitor", which is also not currently implemented.
+ */
+ .name = "Kodicom 4400R (master)",
+ .video_inputs = 16,
+ /* .audio_inputs= 0, */
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .svhs = NO_SVHS,
+ /* GPIO bits 0-9 used for analog switch:
+ * 00 - 03: camera selector
+ * 04 - 06: channel (controller) selector
+ * 07: data (1->on, 0->off)
+ * 08: strobe
+ * 09: reset
+ * bit 16 is input from sync separator for the channel
+ */
+ .gpiomask = 0x0003ff,
+ .no_gpioirq = 1,
+ .muxsel = MUXSEL(3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3),
+ .pll = PLL_28,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .muxsel_hook = kodicom4400r_muxsel,
+ },
+ [BTTV_BOARD_KODICOM_4400R_SL] = {
+ /* Bill Brack <wbrack@mmm.com.hk> */
+ /* Note that, for reasons unknown, the "master" BT878A chip (i.e. the
+ * one which controls the analog switch, and must use the card type)
+ * is the 2nd one detected. The other 3 chips should use this card
+ * type
+ */
+ .name = "Kodicom 4400R (slave)",
+ .video_inputs = 16,
+ /* .audio_inputs= 0, */
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .svhs = NO_SVHS,
+ .gpiomask = 0x010000,
+ .no_gpioirq = 1,
+ .muxsel = MUXSEL(3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3),
+ .pll = PLL_28,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .muxsel_hook = kodicom4400r_muxsel,
+ },
+ /* ---- card 0x86---------------------------------- */
+ [BTTV_BOARD_ADLINK_RTV24] = {
+ /* Michael Henson <mhenson@clarityvi.com> */
+ /* Adlink RTV24 with special unlock codes */
+ .name = "Adlink RTV24",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ },
+ /* ---- card 0x87---------------------------------- */
+ [BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE] = {
+ /* Michael Krufky <mkrufky@m1k.net> */
+ .name = "DViCO FusionHDTV 5 Lite",
+ .tuner_type = TUNER_LG_TDVS_H06XF, /* TDVS-H064F */
+ .tuner_addr = ADDR_UNSET,
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .muxsel = MUXSEL(2, 3, 1),
+ .gpiomask = 0x00e00007,
+ .gpiomux = { 0x00400005, 0, 0x00000001, 0 },
+ .gpiomute = 0x00c00007,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .has_dvb = 1,
+ },
+ /* ---- card 0x88---------------------------------- */
+ [BTTV_BOARD_ACORP_Y878F] = {
+ /* Mauro Carvalho Chehab <mchehab@infradead.org> */
+ .name = "Acorp Y878F",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x01fe00,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x001e00, 0, 0x018000, 0x014000 },
+ .gpiomute = 0x002000,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_YMEC_TVF66T5_B_DFF,
+ .tuner_addr = 0xc1 >>1,
+ .has_radio = 1,
+ },
+ /* ---- card 0x89 ---------------------------------- */
+ [BTTV_BOARD_CONCEPTRONIC_CTVFMI2] = {
+ .name = "Conceptronic CTVFMi v2",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x001c0007,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 1, 2, 2 },
+ .gpiomute = 3,
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+ .tuner_type = TUNER_TENA_9533_DI,
+ .tuner_addr = ADDR_UNSET,
+ .has_remote = 1,
+ .has_radio = 1,
+ },
+ /* ---- card 0x8a ---------------------------------- */
+ [BTTV_BOARD_PV_BT878P_2E] = {
+ .name = "Prolink Pixelview PV-BT878P+ (Rev.2E)",
+ .video_inputs = 5,
+ /* .audio_inputs= 1, */
+ .svhs = 3,
+ .has_dig_in = 1,
+ .gpiomask = 0x01fe00,
+ .muxsel = MUXSEL(2, 3, 1, 1, 0), /* in 4 is digital */
+ /* .digital_mode= DIGITAL_MODE_CAMERA, */
+ .gpiomux = { 0x00400, 0x10400, 0x04400, 0x80000 },
+ .gpiomute = 0x12400,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_LG_PAL_FM,
+ .tuner_addr = ADDR_UNSET,
+ .has_remote = 1,
+ },
+ /* ---- card 0x8b ---------------------------------- */
+ [BTTV_BOARD_PV_M4900] = {
+ /* Sérgio Fortier <sergiofortier@yahoo.com.br> */
+ .name = "Prolink PixelView PlayTV MPEG2 PV-M4900",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x3f,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x21, 0x20, 0x24, 0x2c },
+ .gpiomute = 0x29,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_YMEC_TVF_5533MF,
+ .tuner_addr = ADDR_UNSET,
+ .has_radio = 1,
+ .has_remote = 1,
+ },
+ /* ---- card 0x8c ---------------------------------- */
+ /* Has four Bt878 chips behind a PCI bridge, each chip has:
+ one external BNC composite input (mux 2)
+ three internal composite inputs (unknown muxes)
+ an 18-bit stereo A/D (CS5331A), which has:
+ one external stereo unblanced (RCA) audio connection
+ one (or 3?) internal stereo balanced (XLR) audio connection
+ input is selected via gpio to a 14052B mux
+ (mask=0x300, unbal=0x000, bal=0x100, ??=0x200,0x300)
+ gain is controlled via an X9221A chip on the I2C bus @0x28
+ sample rate is controlled via gpio to an MK1413S
+ (mask=0x3, 32kHz=0x0, 44.1kHz=0x1, 48kHz=0x2, ??=0x3)
+ There is neither a tuner nor an svideo input. */
+ [BTTV_BOARD_OSPREY440] = {
+ .name = "Osprey 440",
+ .video_inputs = 4,
+ /* .audio_inputs= 2, */
+ .svhs = NO_SVHS,
+ .muxsel = MUXSEL(2, 3, 0, 1), /* 3,0,1 are guesses */
+ .gpiomask = 0x303,
+ .gpiomute = 0x000, /* int + 32kHz */
+ .gpiomux = { 0, 0, 0x000, 0x100},
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ },
+ /* ---- card 0x8d ---------------------------------- */
+ [BTTV_BOARD_ASOUND_SKYEYE] = {
+ .name = "Asound Skyeye PCTV",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 15,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 2, 0, 0, 0 },
+ .gpiomute = 1,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_NTSC,
+ .tuner_addr = ADDR_UNSET,
+ },
+ /* ---- card 0x8e ---------------------------------- */
+ [BTTV_BOARD_SABRENT_TVFM] = {
+ .name = "Sabrent TV-FM (bttv version)",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x108007,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 100000, 100002, 100002, 100000 },
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_TNF_5335MF,
+ .tuner_addr = ADDR_UNSET,
+ .has_radio = 1,
+ },
+ /* ---- card 0x8f ---------------------------------- */
+ [BTTV_BOARD_HAUPPAUGE_IMPACTVCB] = {
+ .name = "Hauppauge ImpactVCB (bt878)",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0x0f, /* old: 7 */
+ .muxsel = MUXSEL(0, 1, 3, 2), /* Composite 0-3 */
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_MACHTV_MAGICTV] = {
+ /* Julian Calaby <julian.calaby@gmail.com>
+ * Slightly different from original MachTV definition (0x60)
+
+ * FIXME: RegSpy says gpiomask should be "0x001c800f", but it
+ * stuffs up remote chip. Bug is a pin on the jaecs is not set
+ * properly (methinks) causing no keyup bits being set */
+
+ .name = "MagicTV", /* rebranded MachTV */
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 7,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 1, 2, 3 },
+ .gpiomute = 4,
+ .tuner_type = TUNER_TEMIC_4009FR5_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .has_radio = 1,
+ .has_remote = 1,
+ },
+ [BTTV_BOARD_SSAI_SECURITY] = {
+ .name = "SSAI Security Video Interface",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .muxsel = MUXSEL(0, 1, 2, 3),
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_SSAI_ULTRASOUND] = {
+ .name = "SSAI Ultrasound Video Interface",
+ .video_inputs = 2,
+ /* .audio_inputs= 0, */
+ .svhs = 1,
+ .muxsel = MUXSEL(2, 0, 1, 3),
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ /* ---- card 0x94---------------------------------- */
+ [BTTV_BOARD_DVICO_FUSIONHDTV_2] = {
+ .name = "DViCO FusionHDTV 2",
+ .tuner_type = TUNER_PHILIPS_FCV1236D,
+ .tuner_addr = ADDR_UNSET,
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .muxsel = MUXSEL(2, 3, 1),
+ .gpiomask = 0x00e00007,
+ .gpiomux = { 0x00400005, 0, 0x00000001, 0 },
+ .gpiomute = 0x00c00007,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ },
+ /* ---- card 0x95---------------------------------- */
+ [BTTV_BOARD_TYPHOON_TVTUNERPCI] = {
+ .name = "Typhoon TV-Tuner PCI (50684)",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x3014f,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x20001,0x10001, 0, 0 },
+ .gpiomute = 10,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL_I,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_GEOVISION_GV600] = {
+ /* emhn@usb.ve */
+ .name = "Geovision GV-600",
+ .video_inputs = 16,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0x0,
+ .muxsel = MUXSEL(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2),
+ .muxsel_hook = geovision_muxsel,
+ .gpiomux = { 0 },
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_KOZUMI_KTV_01C] = {
+ /* Mauro Lacy <mauro@lacy.com.ar>
+ * Based on MagicTV and Conceptronic CONTVFMi */
+
+ .name = "Kozumi KTV-01C",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x008007,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 1, 2, 2 }, /* CONTVFMi */
+ .gpiomute = 3, /* CONTVFMi */
+ .needs_tvaudio = 0,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* TCL MK3 */
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .has_radio = 1,
+ .has_remote = 1,
+ },
+ [BTTV_BOARD_ENLTV_FM_2] = {
+ /* Encore TV Tuner Pro ENL TV-FM-2
+ Mauro Carvalho Chehab <mchehab@infradead.org */
+ .name = "Encore ENL TV-FM-2",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ /* bit 6 -> IR disabled
+ bit 18/17 = 00 -> mute
+ 01 -> enable external audio input
+ 10 -> internal audio input (mono?)
+ 11 -> internal audio input
+ */
+ .gpiomask = 0x060040,
+ .muxsel = MUXSEL(2, 3, 3),
+ .gpiomux = { 0x60000, 0x60000, 0x20000, 0x20000 },
+ .gpiomute = 0,
+ .tuner_type = TUNER_TCL_MF02GIP_5N,
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .has_radio = 1,
+ .has_remote = 1,
+ },
+ [BTTV_BOARD_VD012] = {
+ /* D.Heer@Phytec.de */
+ .name = "PHYTEC VD-012 (bt878)",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0x00,
+ .muxsel = MUXSEL(0, 2, 3, 1),
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_VD012_X1] = {
+ /* D.Heer@Phytec.de */
+ .name = "PHYTEC VD-012-X1 (bt878)",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = 3,
+ .gpiomask = 0x00,
+ .muxsel = MUXSEL(2, 3, 1),
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_VD012_X2] = {
+ /* D.Heer@Phytec.de */
+ .name = "PHYTEC VD-012-X2 (bt878)",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = 3,
+ .gpiomask = 0x00,
+ .muxsel = MUXSEL(3, 2, 1),
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_GEOVISION_GV800S] = {
+ /* Bruno Christo <bchristo@inf.ufsm.br>
+ *
+ * GeoVision GV-800(S) has 4 Conexant Fusion 878A:
+ * 1 audio input per BT878A = 4 audio inputs
+ * 4 video inputs per BT878A = 16 video inputs
+ * This is the first BT878A chip of the GV-800(S). It's the
+ * "master" chip and it controls the video inputs through an
+ * analog multiplexer (a CD22M3494) via some GPIO pins. The
+ * slaves should use card type 0x9e (following this one).
+ * There is a EEPROM on the card which is currently not handled.
+ * The audio input is not working yet.
+ */
+ .name = "Geovision GV-800(S) (master)",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .svhs = NO_SVHS,
+ .gpiomask = 0xf107f,
+ .no_gpioirq = 1,
+ .muxsel = MUXSEL(2, 2, 2, 2),
+ .pll = PLL_28,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .muxsel_hook = gv800s_muxsel,
+ },
+ [BTTV_BOARD_GEOVISION_GV800S_SL] = {
+ /* Bruno Christo <bchristo@inf.ufsm.br>
+ *
+ * GeoVision GV-800(S) has 4 Conexant Fusion 878A:
+ * 1 audio input per BT878A = 4 audio inputs
+ * 4 video inputs per BT878A = 16 video inputs
+ * The 3 other BT878A chips are "slave" chips of the GV-800(S)
+ * and should use this card type.
+ * The audio input is not working yet.
+ */
+ .name = "Geovision GV-800(S) (slave)",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .svhs = NO_SVHS,
+ .gpiomask = 0x00,
+ .no_gpioirq = 1,
+ .muxsel = MUXSEL(2, 2, 2, 2),
+ .pll = PLL_28,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .muxsel_hook = gv800s_muxsel,
+ },
+ [BTTV_BOARD_PV183] = {
+ .name = "ProVideo PV183", /* 0x9f */
+ .video_inputs = 2,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0,
+ .muxsel = MUXSEL(2, 3),
+ .gpiomux = { 0 },
+ .needs_tvaudio = 0,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_TVT_TD3116] = {
+ .name = "Tongwei Video Technology TD-3116",
+ .video_inputs = 16,
+ .gpiomask = 0xc00ff,
+ .muxsel = MUXSEL(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2),
+ .muxsel_hook = td3116_muxsel,
+ .svhs = NO_SVHS,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ },
+};
+
+static const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards);
+
+/* ----------------------------------------------------------------------- */
+
+static unsigned char eeprom_data[256];
+
+/*
+ * identify card
+ */
+void __devinit bttv_idcard(struct bttv *btv)
+{
+ unsigned int gpiobits;
+ int i,type;
+
+ /* read PCI subsystem ID */
+ btv->cardid = btv->c.pci->subsystem_device << 16;
+ btv->cardid |= btv->c.pci->subsystem_vendor;
+
+ if (0 != btv->cardid && 0xffffffff != btv->cardid) {
+ /* look for the card */
+ for (type = -1, i = 0; cards[i].id != 0; i++)
+ if (cards[i].id == btv->cardid)
+ type = i;
+
+ if (type != -1) {
+ /* found it */
+ pr_info("%d: detected: %s [card=%d], PCI subsystem ID is %04x:%04x\n",
+ btv->c.nr, cards[type].name, cards[type].cardnr,
+ btv->cardid & 0xffff,
+ (btv->cardid >> 16) & 0xffff);
+ btv->c.type = cards[type].cardnr;
+ } else {
+ /* 404 */
+ pr_info("%d: subsystem: %04x:%04x (UNKNOWN)\n",
+ btv->c.nr, btv->cardid & 0xffff,
+ (btv->cardid >> 16) & 0xffff);
+ pr_debug("please mail id, board name and the correct card= insmod option to linux-media@vger.kernel.org\n");
+ }
+ }
+
+ /* let the user override the autodetected type */
+ if (card[btv->c.nr] < bttv_num_tvcards)
+ btv->c.type=card[btv->c.nr];
+
+ /* print which card config we are using */
+ pr_info("%d: using: %s [card=%d,%s]\n",
+ btv->c.nr, bttv_tvcards[btv->c.type].name, btv->c.type,
+ card[btv->c.nr] < bttv_num_tvcards
+ ? "insmod option" : "autodetected");
+
+ /* overwrite gpio stuff ?? */
+ if (UNSET == audioall && UNSET == audiomux[0])
+ return;
+
+ if (UNSET != audiomux[0]) {
+ gpiobits = 0;
+ for (i = 0; i < ARRAY_SIZE(bttv_tvcards->gpiomux); i++) {
+ bttv_tvcards[btv->c.type].gpiomux[i] = audiomux[i];
+ gpiobits |= audiomux[i];
+ }
+ } else {
+ gpiobits = audioall;
+ for (i = 0; i < ARRAY_SIZE(bttv_tvcards->gpiomux); i++) {
+ bttv_tvcards[btv->c.type].gpiomux[i] = audioall;
+ }
+ }
+ bttv_tvcards[btv->c.type].gpiomask = (UNSET != gpiomask) ? gpiomask : gpiobits;
+ pr_info("%d: gpio config override: mask=0x%x, mux=",
+ btv->c.nr, bttv_tvcards[btv->c.type].gpiomask);
+ for (i = 0; i < ARRAY_SIZE(bttv_tvcards->gpiomux); i++) {
+ pr_cont("%s0x%x",
+ i ? "," : "", bttv_tvcards[btv->c.type].gpiomux[i]);
+ }
+ pr_cont("\n");
+}
+
+/*
+ * (most) board specific initialisations goes here
+ */
+
+/* Some Modular Technology cards have an eeprom, but no subsystem ID */
+static void identify_by_eeprom(struct bttv *btv, unsigned char eeprom_data[256])
+{
+ int type = -1;
+
+ if (0 == strncmp(eeprom_data,"GET MM20xPCTV",13))
+ type = BTTV_BOARD_MODTEC_205;
+ else if (0 == strncmp(eeprom_data+20,"Picolo",7))
+ type = BTTV_BOARD_EURESYS_PICOLO;
+ else if (eeprom_data[0] == 0x84 && eeprom_data[2]== 0)
+ type = BTTV_BOARD_HAUPPAUGE; /* old bt848 */
+
+ if (-1 != type) {
+ btv->c.type = type;
+ pr_info("%d: detected by eeprom: %s [card=%d]\n",
+ btv->c.nr, bttv_tvcards[btv->c.type].name, btv->c.type);
+ }
+}
+
+static void flyvideo_gpio(struct bttv *btv)
+{
+ int gpio, has_remote, has_radio, is_capture_only;
+ int is_lr90, has_tda9820_tda9821;
+ int tuner_type = UNSET, ttype;
+
+ gpio_inout(0xffffff, 0);
+ udelay(8); /* without this we would see the 0x1800 mask */
+ gpio = gpio_read();
+ /* FIXME: must restore OUR_EN ??? */
+
+ /* all cards provide GPIO info, some have an additional eeprom
+ * LR50: GPIO coding can be found lower right CP1 .. CP9
+ * CP9=GPIO23 .. CP1=GPIO15; when OPEN, the corresponding GPIO reads 1.
+ * GPIO14-12: n.c.
+ * LR90: GP9=GPIO23 .. GP1=GPIO15 (right above the bt878)
+
+ * lowest 3 bytes are remote control codes (no handshake needed)
+ * xxxFFF: No remote control chip soldered
+ * xxxF00(LR26/LR50), xxxFE0(LR90): Remote control chip (LVA001 or CF45) soldered
+ * Note: Some bits are Audio_Mask !
+ */
+ ttype = (gpio & 0x0f0000) >> 16;
+ switch (ttype) {
+ case 0x0:
+ tuner_type = 2; /* NTSC, e.g. TPI8NSR11P */
+ break;
+ case 0x2:
+ tuner_type = 39; /* LG NTSC (newer TAPC series) TAPC-H701P */
+ break;
+ case 0x4:
+ tuner_type = 5; /* Philips PAL TPI8PSB02P, TPI8PSB12P, TPI8PSB12D or FI1216, FM1216 */
+ break;
+ case 0x6:
+ tuner_type = 37; /* LG PAL (newer TAPC series) TAPC-G702P */
+ break;
+ case 0xC:
+ tuner_type = 3; /* Philips SECAM(+PAL) FQ1216ME or FI1216MF */
+ break;
+ default:
+ pr_info("%d: FlyVideo_gpio: unknown tuner type\n", btv->c.nr);
+ break;
+ }
+
+ has_remote = gpio & 0x800000;
+ has_radio = gpio & 0x400000;
+ /* unknown 0x200000;
+ * unknown2 0x100000; */
+ is_capture_only = !(gpio & 0x008000); /* GPIO15 */
+ has_tda9820_tda9821 = !(gpio & 0x004000);
+ is_lr90 = !(gpio & 0x002000); /* else LR26/LR50 (LR38/LR51 f. capture only) */
+ /*
+ * gpio & 0x001000 output bit for audio routing */
+
+ if (is_capture_only)
+ tuner_type = TUNER_ABSENT; /* No tuner present */
+
+ pr_info("%d: FlyVideo Radio=%s RemoteControl=%s Tuner=%d gpio=0x%06x\n",
+ btv->c.nr, has_radio ? "yes" : "no",
+ has_remote ? "yes" : "no", tuner_type, gpio);
+ pr_info("%d: FlyVideo LR90=%s tda9821/tda9820=%s capture_only=%s\n",
+ btv->c.nr, is_lr90 ? "yes" : "no",
+ has_tda9820_tda9821 ? "yes" : "no",
+ is_capture_only ? "yes" : "no");
+
+ if (tuner_type != UNSET) /* only set if known tuner autodetected, else let insmod option through */
+ btv->tuner_type = tuner_type;
+ btv->has_radio = has_radio;
+
+ /* LR90 Audio Routing is done by 2 hef4052, so Audio_Mask has 4 bits: 0x001c80
+ * LR26/LR50 only has 1 hef4052, Audio_Mask 0x000c00
+ * Audio options: from tuner, from tda9821/tda9821(mono,stereo,sap), from tda9874, ext., mute */
+ if (has_tda9820_tda9821)
+ btv->audio_mode_gpio = lt9415_audio;
+ /* todo: if(has_tda9874) btv->audio_mode_gpio = fv2000s_audio; */
+}
+
+static int miro_tunermap[] = { 0,6,2,3, 4,5,6,0, 3,0,4,5, 5,2,16,1,
+ 14,2,17,1, 4,1,4,3, 1,2,16,1, 4,4,4,4 };
+static int miro_fmtuner[] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1,
+ 1,1,1,1, 1,1,1,0, 0,0,0,0, 0,1,0,0 };
+
+static void miro_pinnacle_gpio(struct bttv *btv)
+{
+ int id,msp,gpio;
+ char *info;
+
+ gpio_inout(0xffffff, 0);
+ gpio = gpio_read();
+ id = ((gpio>>10) & 63) -1;
+ msp = bttv_I2CRead(btv, I2C_ADDR_MSP3400, "MSP34xx");
+ if (id < 32) {
+ btv->tuner_type = miro_tunermap[id];
+ if (0 == (gpio & 0x20)) {
+ btv->has_radio = 1;
+ if (!miro_fmtuner[id]) {
+ btv->has_matchbox = 1;
+ btv->mbox_we = (1<<6);
+ btv->mbox_most = (1<<7);
+ btv->mbox_clk = (1<<8);
+ btv->mbox_data = (1<<9);
+ btv->mbox_mask = (1<<6)|(1<<7)|(1<<8)|(1<<9);
+ }
+ } else {
+ btv->has_radio = 0;
+ }
+ if (-1 != msp) {
+ if (btv->c.type == BTTV_BOARD_MIRO)
+ btv->c.type = BTTV_BOARD_MIROPRO;
+ if (btv->c.type == BTTV_BOARD_PINNACLE)
+ btv->c.type = BTTV_BOARD_PINNACLEPRO;
+ }
+ pr_info("%d: miro: id=%d tuner=%d radio=%s stereo=%s\n",
+ btv->c.nr, id+1, btv->tuner_type,
+ !btv->has_radio ? "no" :
+ (btv->has_matchbox ? "matchbox" : "fmtuner"),
+ (-1 == msp) ? "no" : "yes");
+ } else {
+ /* new cards with microtune tuner */
+ id = 63 - id;
+ btv->has_radio = 0;
+ switch (id) {
+ case 1:
+ info = "PAL / mono";
+ btv->tda9887_conf = TDA9887_INTERCARRIER;
+ break;
+ case 2:
+ info = "PAL+SECAM / stereo";
+ btv->has_radio = 1;
+ btv->tda9887_conf = TDA9887_QSS;
+ break;
+ case 3:
+ info = "NTSC / stereo";
+ btv->has_radio = 1;
+ btv->tda9887_conf = TDA9887_QSS;
+ break;
+ case 4:
+ info = "PAL+SECAM / mono";
+ btv->tda9887_conf = TDA9887_QSS;
+ break;
+ case 5:
+ info = "NTSC / mono";
+ btv->tda9887_conf = TDA9887_INTERCARRIER;
+ break;
+ case 6:
+ info = "NTSC / stereo";
+ btv->tda9887_conf = TDA9887_INTERCARRIER;
+ break;
+ case 7:
+ info = "PAL / stereo";
+ btv->tda9887_conf = TDA9887_INTERCARRIER;
+ break;
+ default:
+ info = "oops: unknown card";
+ break;
+ }
+ if (-1 != msp)
+ btv->c.type = BTTV_BOARD_PINNACLEPRO;
+ pr_info("%d: pinnacle/mt: id=%d info=\"%s\" radio=%s\n",
+ btv->c.nr, id, info, btv->has_radio ? "yes" : "no");
+ btv->tuner_type = TUNER_MT2032;
+ }
+}
+
+/* GPIO21 L: Buffer aktiv, H: Buffer inaktiv */
+#define LM1882_SYNC_DRIVE 0x200000L
+
+static void init_ids_eagle(struct bttv *btv)
+{
+ gpio_inout(0xffffff,0xFFFF37);
+ gpio_write(0x200020);
+
+ /* flash strobe inverter ?! */
+ gpio_write(0x200024);
+
+ /* switch sync drive off */
+ gpio_bits(LM1882_SYNC_DRIVE,LM1882_SYNC_DRIVE);
+
+ /* set BT848 muxel to 2 */
+ btaor((2)<<5, ~(2<<5), BT848_IFORM);
+}
+
+/* Muxsel helper for the IDS Eagle.
+ * the eagles does not use the standard muxsel-bits but
+ * has its own multiplexer */
+static void eagle_muxsel(struct bttv *btv, unsigned int input)
+{
+ gpio_bits(3, input & 3);
+
+ /* composite */
+ /* set chroma ADC to sleep */
+ btor(BT848_ADC_C_SLEEP, BT848_ADC);
+ /* set to composite video */
+ btand(~BT848_CONTROL_COMP, BT848_E_CONTROL);
+ btand(~BT848_CONTROL_COMP, BT848_O_CONTROL);
+
+ /* switch sync drive off */
+ gpio_bits(LM1882_SYNC_DRIVE,LM1882_SYNC_DRIVE);
+}
+
+static void gvc1100_muxsel(struct bttv *btv, unsigned int input)
+{
+ static const int masks[] = {0x30, 0x01, 0x12, 0x23};
+ gpio_write(masks[input%4]);
+}
+
+/* LMLBT4x initialization - to allow access to GPIO bits for sensors input and
+ alarms output
+
+ GPIObit | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+ assignment | TI | O3|INx| O2| O1|IN4|IN3|IN2|IN1| | |
+
+ IN - sensor inputs, INx - sensor inputs and TI XORed together
+ O1,O2,O3 - alarm outputs (relays)
+
+ OUT ENABLE 1 1 0 . 1 1 0 0 . 0 0 0 0 = 0x6C0
+
+*/
+
+static void init_lmlbt4x(struct bttv *btv)
+{
+ pr_debug("LMLBT4x init\n");
+ btwrite(0x000000, BT848_GPIO_REG_INP);
+ gpio_inout(0xffffff, 0x0006C0);
+ gpio_write(0x000000);
+}
+
+static void sigmaSQ_muxsel(struct bttv *btv, unsigned int input)
+{
+ unsigned int inmux = input % 8;
+ gpio_inout( 0xf, 0xf );
+ gpio_bits( 0xf, inmux );
+}
+
+static void sigmaSLC_muxsel(struct bttv *btv, unsigned int input)
+{
+ unsigned int inmux = input % 4;
+ gpio_inout( 3<<9, 3<<9 );
+ gpio_bits( 3<<9, inmux<<9 );
+}
+
+static void geovision_muxsel(struct bttv *btv, unsigned int input)
+{
+ unsigned int inmux = input % 16;
+ gpio_inout(0xf, 0xf);
+ gpio_bits(0xf, inmux);
+}
+
+/*
+ * The TD3116 has 2 74HC4051 muxes wired to the MUX0 input of a bt878.
+ * The first 74HC4051 has the lower 8 inputs, the second one the higher 8.
+ * The muxes are controlled via a 74HC373 latch which is connected to
+ * GPIOs 0-7. GPIO 18 is connected to the LE signal of the latch.
+ * Q0 of the latch is connected to the Enable (~E) input of the first
+ * 74HC4051. Q1 - Q3 are connected to S0 - S2 of the same 74HC4051.
+ * Q4 - Q7 are connected to the second 74HC4051 in the same way.
+ */
+
+static void td3116_latch_value(struct bttv *btv, u32 value)
+{
+ gpio_bits((1<<18) | 0xff, value);
+ gpio_bits((1<<18) | 0xff, (1<<18) | value);
+ udelay(1);
+ gpio_bits((1<<18) | 0xff, value);
+}
+
+static void td3116_muxsel(struct bttv *btv, unsigned int input)
+{
+ u32 value;
+ u32 highbit;
+
+ highbit = (input & 0x8) >> 3 ;
+
+ /* Disable outputs and set value in the mux */
+ value = 0x11; /* Disable outputs */
+ value |= ((input & 0x7) << 1) << (4 * highbit);
+ td3116_latch_value(btv, value);
+
+ /* Enable the correct output */
+ value &= ~0x11;
+ value |= ((highbit ^ 0x1) << 4) | highbit;
+ td3116_latch_value(btv, value);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void bttv_reset_audio(struct bttv *btv)
+{
+ /*
+ * BT878A has a audio-reset register.
+ * 1. This register is an audio reset function but it is in
+ * function-0 (video capture) address space.
+ * 2. It is enough to do this once per power-up of the card.
+ * 3. There is a typo in the Conexant doc -- it is not at
+ * 0x5B, but at 0x058. (B is an odd-number, obviously a typo!).
+ * --//Shrikumar 030609
+ */
+ if (btv->id != 878)
+ return;
+
+ if (bttv_debug)
+ pr_debug("%d: BT878A ARESET\n", btv->c.nr);
+ btwrite((1<<7), 0x058);
+ udelay(10);
+ btwrite( 0, 0x058);
+}
+
+/* initialization part one -- before registering i2c bus */
+void __devinit bttv_init_card1(struct bttv *btv)
+{
+ switch (btv->c.type) {
+ case BTTV_BOARD_HAUPPAUGE:
+ case BTTV_BOARD_HAUPPAUGE878:
+ boot_msp34xx(btv,5);
+ break;
+ case BTTV_BOARD_VOODOOTV_200:
+ case BTTV_BOARD_VOODOOTV_FM:
+ boot_msp34xx(btv,20);
+ break;
+ case BTTV_BOARD_AVERMEDIA98:
+ boot_msp34xx(btv,11);
+ break;
+ case BTTV_BOARD_HAUPPAUGEPVR:
+ pvr_boot(btv);
+ break;
+ case BTTV_BOARD_TWINHAN_DST:
+ case BTTV_BOARD_AVDVBT_771:
+ case BTTV_BOARD_PINNACLESAT:
+ btv->use_i2c_hw = 1;
+ break;
+ case BTTV_BOARD_ADLINK_RTV24:
+ init_RTV24( btv );
+ break;
+
+ }
+ if (!bttv_tvcards[btv->c.type].has_dvb)
+ bttv_reset_audio(btv);
+}
+
+/* initialization part two -- after registering i2c bus */
+void __devinit bttv_init_card2(struct bttv *btv)
+{
+ btv->tuner_type = UNSET;
+
+ if (BTTV_BOARD_UNKNOWN == btv->c.type) {
+ bttv_readee(btv,eeprom_data,0xa0);
+ identify_by_eeprom(btv,eeprom_data);
+ }
+
+ switch (btv->c.type) {
+ case BTTV_BOARD_MIRO:
+ case BTTV_BOARD_MIROPRO:
+ case BTTV_BOARD_PINNACLE:
+ case BTTV_BOARD_PINNACLEPRO:
+ /* miro/pinnacle */
+ miro_pinnacle_gpio(btv);
+ break;
+ case BTTV_BOARD_FLYVIDEO_98:
+ case BTTV_BOARD_MAXI:
+ case BTTV_BOARD_LIFE_FLYKIT:
+ case BTTV_BOARD_FLYVIDEO:
+ case BTTV_BOARD_TYPHOON_TVIEW:
+ case BTTV_BOARD_CHRONOS_VS2:
+ case BTTV_BOARD_FLYVIDEO_98FM:
+ case BTTV_BOARD_FLYVIDEO2000:
+ case BTTV_BOARD_FLYVIDEO98EZ:
+ case BTTV_BOARD_CONFERENCETV:
+ case BTTV_BOARD_LIFETEC_9415:
+ flyvideo_gpio(btv);
+ break;
+ case BTTV_BOARD_HAUPPAUGE:
+ case BTTV_BOARD_HAUPPAUGE878:
+ case BTTV_BOARD_HAUPPAUGEPVR:
+ /* pick up some config infos from the eeprom */
+ bttv_readee(btv,eeprom_data,0xa0);
+ hauppauge_eeprom(btv);
+ break;
+ case BTTV_BOARD_AVERMEDIA98:
+ case BTTV_BOARD_AVPHONE98:
+ bttv_readee(btv,eeprom_data,0xa0);
+ avermedia_eeprom(btv);
+ break;
+ case BTTV_BOARD_PXC200:
+ init_PXC200(btv);
+ break;
+ case BTTV_BOARD_PICOLO_TETRA_CHIP:
+ picolo_tetra_init(btv);
+ break;
+ case BTTV_BOARD_VHX:
+ btv->has_radio = 1;
+ btv->has_matchbox = 1;
+ btv->mbox_we = 0x20;
+ btv->mbox_most = 0;
+ btv->mbox_clk = 0x08;
+ btv->mbox_data = 0x10;
+ btv->mbox_mask = 0x38;
+ break;
+ case BTTV_BOARD_VOBIS_BOOSTAR:
+ case BTTV_BOARD_TERRATV:
+ terratec_active_radio_upgrade(btv);
+ break;
+ case BTTV_BOARD_MAGICTVIEW061:
+ if (btv->cardid == 0x3002144f) {
+ btv->has_radio=1;
+ pr_info("%d: radio detected by subsystem id (CPH05x)\n",
+ btv->c.nr);
+ }
+ break;
+ case BTTV_BOARD_STB2:
+ if (btv->cardid == 0x3060121a) {
+ /* Fix up entry for 3DFX VoodooTV 100,
+ which is an OEM STB card variant. */
+ btv->has_radio=0;
+ btv->tuner_type=TUNER_TEMIC_NTSC;
+ }
+ break;
+ case BTTV_BOARD_OSPREY1x0:
+ case BTTV_BOARD_OSPREY1x0_848:
+ case BTTV_BOARD_OSPREY101_848:
+ case BTTV_BOARD_OSPREY1x1:
+ case BTTV_BOARD_OSPREY1x1_SVID:
+ case BTTV_BOARD_OSPREY2xx:
+ case BTTV_BOARD_OSPREY2x0_SVID:
+ case BTTV_BOARD_OSPREY2x0:
+ case BTTV_BOARD_OSPREY440:
+ case BTTV_BOARD_OSPREY500:
+ case BTTV_BOARD_OSPREY540:
+ case BTTV_BOARD_OSPREY2000:
+ bttv_readee(btv,eeprom_data,0xa0);
+ osprey_eeprom(btv, eeprom_data);
+ break;
+ case BTTV_BOARD_IDS_EAGLE:
+ init_ids_eagle(btv);
+ break;
+ case BTTV_BOARD_MODTEC_205:
+ bttv_readee(btv,eeprom_data,0xa0);
+ modtec_eeprom(btv);
+ break;
+ case BTTV_BOARD_LMLBT4:
+ init_lmlbt4x(btv);
+ break;
+ case BTTV_BOARD_TIBET_CS16:
+ tibetCS16_init(btv);
+ break;
+ case BTTV_BOARD_KODICOM_4400R:
+ kodicom4400r_init(btv);
+ break;
+ case BTTV_BOARD_GEOVISION_GV800S:
+ gv800s_init(btv);
+ break;
+ }
+
+ /* pll configuration */
+ if (!(btv->id==848 && btv->revision==0x11)) {
+ /* defaults from card list */
+ if (PLL_28 == bttv_tvcards[btv->c.type].pll) {
+ btv->pll.pll_ifreq=28636363;
+ btv->pll.pll_crystal=BT848_IFORM_XT0;
+ }
+ if (PLL_35 == bttv_tvcards[btv->c.type].pll) {
+ btv->pll.pll_ifreq=35468950;
+ btv->pll.pll_crystal=BT848_IFORM_XT1;
+ }
+ /* insmod options can override */
+ switch (pll[btv->c.nr]) {
+ case 0: /* none */
+ btv->pll.pll_crystal = 0;
+ btv->pll.pll_ifreq = 0;
+ btv->pll.pll_ofreq = 0;
+ break;
+ case 1: /* 28 MHz */
+ case 28:
+ btv->pll.pll_ifreq = 28636363;
+ btv->pll.pll_ofreq = 0;
+ btv->pll.pll_crystal = BT848_IFORM_XT0;
+ break;
+ case 2: /* 35 MHz */
+ case 35:
+ btv->pll.pll_ifreq = 35468950;
+ btv->pll.pll_ofreq = 0;
+ btv->pll.pll_crystal = BT848_IFORM_XT1;
+ break;
+ }
+ }
+ btv->pll.pll_current = -1;
+
+ /* tuner configuration (from card list / autodetect / insmod option) */
+ if (UNSET != bttv_tvcards[btv->c.type].tuner_type)
+ if (UNSET == btv->tuner_type)
+ btv->tuner_type = bttv_tvcards[btv->c.type].tuner_type;
+ if (UNSET != tuner[btv->c.nr])
+ btv->tuner_type = tuner[btv->c.nr];
+
+ if (btv->tuner_type == TUNER_ABSENT)
+ pr_info("%d: tuner absent\n", btv->c.nr);
+ else if (btv->tuner_type == UNSET)
+ pr_warn("%d: tuner type unset\n", btv->c.nr);
+ else
+ pr_info("%d: tuner type=%d\n", btv->c.nr, btv->tuner_type);
+
+ if (autoload != UNSET) {
+ pr_warn("%d: the autoload option is obsolete\n", btv->c.nr);
+ pr_warn("%d: use option msp3400, tda7432 or tvaudio to override which audio module should be used\n",
+ btv->c.nr);
+ }
+
+ if (UNSET == btv->tuner_type)
+ btv->tuner_type = TUNER_ABSENT;
+
+ btv->dig = bttv_tvcards[btv->c.type].has_dig_in ?
+ bttv_tvcards[btv->c.type].video_inputs - 1 : UNSET;
+ btv->svhs = bttv_tvcards[btv->c.type].svhs == NO_SVHS ?
+ UNSET : bttv_tvcards[btv->c.type].svhs;
+ if (svhs[btv->c.nr] != UNSET)
+ btv->svhs = svhs[btv->c.nr];
+ if (remote[btv->c.nr] != UNSET)
+ btv->has_remote = remote[btv->c.nr];
+
+ if (bttv_tvcards[btv->c.type].has_radio)
+ btv->has_radio = 1;
+ if (bttv_tvcards[btv->c.type].has_remote)
+ btv->has_remote = 1;
+ if (!bttv_tvcards[btv->c.type].no_gpioirq)
+ btv->gpioirq = 1;
+ if (bttv_tvcards[btv->c.type].volume_gpio)
+ btv->volume_gpio = bttv_tvcards[btv->c.type].volume_gpio;
+ if (bttv_tvcards[btv->c.type].audio_mode_gpio)
+ btv->audio_mode_gpio = bttv_tvcards[btv->c.type].audio_mode_gpio;
+
+ if (btv->tuner_type == TUNER_ABSENT)
+ return; /* no tuner or related drivers to load */
+
+ if (btv->has_saa6588 || saa6588[btv->c.nr]) {
+ /* Probe for RDS receiver chip */
+ static const unsigned short addrs[] = {
+ 0x20 >> 1,
+ 0x22 >> 1,
+ I2C_CLIENT_END
+ };
+ struct v4l2_subdev *sd;
+
+ sd = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+ &btv->c.i2c_adap, "saa6588", 0, addrs);
+ btv->has_saa6588 = (sd != NULL);
+ }
+
+ /* try to detect audio/fader chips */
+
+ /* First check if the user specified the audio chip via a module
+ option. */
+
+ switch (audiodev[btv->c.nr]) {
+ case -1:
+ return; /* do not load any audio module */
+
+ case 0: /* autodetect */
+ break;
+
+ case 1: {
+ /* The user specified that we should probe for msp3400 */
+ static const unsigned short addrs[] = {
+ I2C_ADDR_MSP3400 >> 1,
+ I2C_ADDR_MSP3400_ALT >> 1,
+ I2C_CLIENT_END
+ };
+
+ btv->sd_msp34xx = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+ &btv->c.i2c_adap, "msp3400", 0, addrs);
+ if (btv->sd_msp34xx)
+ return;
+ goto no_audio;
+ }
+
+ case 2: {
+ /* The user specified that we should probe for tda7432 */
+ static const unsigned short addrs[] = {
+ I2C_ADDR_TDA7432 >> 1,
+ I2C_CLIENT_END
+ };
+
+ if (v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+ &btv->c.i2c_adap, "tda7432", 0, addrs))
+ return;
+ goto no_audio;
+ }
+
+ case 3: {
+ /* The user specified that we should probe for tvaudio */
+ btv->sd_tvaudio = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+ &btv->c.i2c_adap, "tvaudio", 0, tvaudio_addrs());
+ if (btv->sd_tvaudio)
+ return;
+ goto no_audio;
+ }
+
+ default:
+ pr_warn("%d: unknown audiodev value!\n", btv->c.nr);
+ return;
+ }
+
+ /* There were no overrides, so now we try to discover this through the
+ card definition */
+
+ /* probe for msp3400 first: this driver can detect whether or not
+ it really is a msp3400, so it will return NULL when the device
+ found is really something else (e.g. a tea6300). */
+ if (!bttv_tvcards[btv->c.type].no_msp34xx) {
+ btv->sd_msp34xx = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+ &btv->c.i2c_adap, "msp3400",
+ 0, I2C_ADDRS(I2C_ADDR_MSP3400 >> 1));
+ } else if (bttv_tvcards[btv->c.type].msp34xx_alt) {
+ btv->sd_msp34xx = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+ &btv->c.i2c_adap, "msp3400",
+ 0, I2C_ADDRS(I2C_ADDR_MSP3400_ALT >> 1));
+ }
+
+ /* If we found a msp34xx, then we're done. */
+ if (btv->sd_msp34xx)
+ return;
+
+ /* it might also be a tda7432. */
+ if (!bttv_tvcards[btv->c.type].no_tda7432) {
+ static const unsigned short addrs[] = {
+ I2C_ADDR_TDA7432 >> 1,
+ I2C_CLIENT_END
+ };
+
+ if (v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+ &btv->c.i2c_adap, "tda7432", 0, addrs))
+ return;
+ }
+
+ /* Now see if we can find one of the tvaudio devices. */
+ btv->sd_tvaudio = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+ &btv->c.i2c_adap, "tvaudio", 0, tvaudio_addrs());
+ if (btv->sd_tvaudio)
+ return;
+
+no_audio:
+ pr_warn("%d: audio absent, no audio device found!\n", btv->c.nr);
+}
+
+
+/* initialize the tuner */
+void __devinit bttv_init_tuner(struct bttv *btv)
+{
+ int addr = ADDR_UNSET;
+
+ if (ADDR_UNSET != bttv_tvcards[btv->c.type].tuner_addr)
+ addr = bttv_tvcards[btv->c.type].tuner_addr;
+
+ if (btv->tuner_type != TUNER_ABSENT) {
+ struct tuner_setup tun_setup;
+
+ /* Load tuner module before issuing tuner config call! */
+ if (bttv_tvcards[btv->c.type].has_radio)
+ v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+ &btv->c.i2c_adap, "tuner",
+ 0, v4l2_i2c_tuner_addrs(ADDRS_RADIO));
+ v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+ &btv->c.i2c_adap, "tuner",
+ 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
+ v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+ &btv->c.i2c_adap, "tuner",
+ 0, v4l2_i2c_tuner_addrs(ADDRS_TV_WITH_DEMOD));
+
+ tun_setup.mode_mask = T_ANALOG_TV;
+ tun_setup.type = btv->tuner_type;
+ tun_setup.addr = addr;
+
+ if (bttv_tvcards[btv->c.type].has_radio)
+ tun_setup.mode_mask |= T_RADIO;
+
+ bttv_call_all(btv, tuner, s_type_addr, &tun_setup);
+ }
+
+ if (btv->tda9887_conf) {
+ struct v4l2_priv_tun_config tda9887_cfg;
+
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &btv->tda9887_conf;
+
+ bttv_call_all(btv, tuner, s_config, &tda9887_cfg);
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void modtec_eeprom(struct bttv *btv)
+{
+ if( strncmp(&(eeprom_data[0x1e]),"Temic 4066 FY5",14) ==0) {
+ btv->tuner_type=TUNER_TEMIC_4066FY5_PAL_I;
+ pr_info("%d: Modtec: Tuner autodetected by eeprom: %s\n",
+ btv->c.nr, &eeprom_data[0x1e]);
+ } else if (strncmp(&(eeprom_data[0x1e]),"Alps TSBB5",10) ==0) {
+ btv->tuner_type=TUNER_ALPS_TSBB5_PAL_I;
+ pr_info("%d: Modtec: Tuner autodetected by eeprom: %s\n",
+ btv->c.nr, &eeprom_data[0x1e]);
+ } else if (strncmp(&(eeprom_data[0x1e]),"Philips FM1246",14) ==0) {
+ btv->tuner_type=TUNER_PHILIPS_NTSC;
+ pr_info("%d: Modtec: Tuner autodetected by eeprom: %s\n",
+ btv->c.nr, &eeprom_data[0x1e]);
+ } else {
+ pr_info("%d: Modtec: Unknown TunerString: %s\n",
+ btv->c.nr, &eeprom_data[0x1e]);
+ }
+}
+
+static void __devinit hauppauge_eeprom(struct bttv *btv)
+{
+ struct tveeprom tv;
+
+ tveeprom_hauppauge_analog(&btv->i2c_client, &tv, eeprom_data);
+ btv->tuner_type = tv.tuner_type;
+ btv->has_radio = tv.has_radio;
+
+ pr_info("%d: Hauppauge eeprom indicates model#%d\n",
+ btv->c.nr, tv.model);
+
+ /*
+ * Some of the 878 boards have duplicate PCI IDs. Switch the board
+ * type based on model #.
+ */
+ if(tv.model == 64900) {
+ pr_info("%d: Switching board type from %s to %s\n",
+ btv->c.nr,
+ bttv_tvcards[btv->c.type].name,
+ bttv_tvcards[BTTV_BOARD_HAUPPAUGE_IMPACTVCB].name);
+ btv->c.type = BTTV_BOARD_HAUPPAUGE_IMPACTVCB;
+ }
+}
+
+static int terratec_active_radio_upgrade(struct bttv *btv)
+{
+ int freq;
+
+ btv->has_radio = 1;
+ btv->has_matchbox = 1;
+ btv->mbox_we = 0x10;
+ btv->mbox_most = 0x20;
+ btv->mbox_clk = 0x08;
+ btv->mbox_data = 0x04;
+ btv->mbox_mask = 0x3c;
+
+ btv->mbox_iow = 1 << 8;
+ btv->mbox_ior = 1 << 9;
+ btv->mbox_csel = 1 << 10;
+
+ freq=88000/62.5;
+ tea5757_write(btv, 5 * freq + 0x358); /* write 0x1ed8 */
+ if (0x1ed8 == tea5757_read(btv)) {
+ pr_info("%d: Terratec Active Radio Upgrade found\n", btv->c.nr);
+ btv->has_radio = 1;
+ btv->has_saa6588 = 1;
+ btv->has_matchbox = 1;
+ } else {
+ btv->has_radio = 0;
+ btv->has_matchbox = 0;
+ }
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+/*
+ * minimal bootstrap for the WinTV/PVR -- upload altera firmware.
+ *
+ * The hcwamc.rbf firmware file is on the Hauppauge driver CD. Have
+ * a look at Pvr/pvr45xxx.EXE (self-extracting zip archive, can be
+ * unpacked with unzip).
+ */
+#define PVR_GPIO_DELAY 10
+
+#define BTTV_ALT_DATA 0x000001
+#define BTTV_ALT_DCLK 0x100000
+#define BTTV_ALT_NCONFIG 0x800000
+
+static int __devinit pvr_altera_load(struct bttv *btv, const u8 *micro,
+ u32 microlen)
+{
+ u32 n;
+ u8 bits;
+ int i;
+
+ gpio_inout(0xffffff,BTTV_ALT_DATA|BTTV_ALT_DCLK|BTTV_ALT_NCONFIG);
+ gpio_write(0);
+ udelay(PVR_GPIO_DELAY);
+
+ gpio_write(BTTV_ALT_NCONFIG);
+ udelay(PVR_GPIO_DELAY);
+
+ for (n = 0; n < microlen; n++) {
+ bits = micro[n];
+ for (i = 0 ; i < 8 ; i++) {
+ gpio_bits(BTTV_ALT_DCLK,0);
+ if (bits & 0x01)
+ gpio_bits(BTTV_ALT_DATA,BTTV_ALT_DATA);
+ else
+ gpio_bits(BTTV_ALT_DATA,0);
+ gpio_bits(BTTV_ALT_DCLK,BTTV_ALT_DCLK);
+ bits >>= 1;
+ }
+ }
+ gpio_bits(BTTV_ALT_DCLK,0);
+ udelay(PVR_GPIO_DELAY);
+
+ /* begin Altera init loop (Not necessary,but doesn't hurt) */
+ for (i = 0 ; i < 30 ; i++) {
+ gpio_bits(BTTV_ALT_DCLK,0);
+ gpio_bits(BTTV_ALT_DCLK,BTTV_ALT_DCLK);
+ }
+ gpio_bits(BTTV_ALT_DCLK,0);
+ return 0;
+}
+
+static int __devinit pvr_boot(struct bttv *btv)
+{
+ const struct firmware *fw_entry;
+ int rc;
+
+ rc = request_firmware(&fw_entry, "hcwamc.rbf", &btv->c.pci->dev);
+ if (rc != 0) {
+ pr_warn("%d: no altera firmware [via hotplug]\n", btv->c.nr);
+ return rc;
+ }
+ rc = pvr_altera_load(btv, fw_entry->data, fw_entry->size);
+ pr_info("%d: altera firmware upload %s\n",
+ btv->c.nr, (rc < 0) ? "failed" : "ok");
+ release_firmware(fw_entry);
+ return rc;
+}
+
+/* ----------------------------------------------------------------------- */
+/* some osprey specific stuff */
+
+static void __devinit osprey_eeprom(struct bttv *btv, const u8 ee[256])
+{
+ int i;
+ u32 serial = 0;
+ int cardid = -1;
+
+ /* This code will nevery actually get called in this case.... */
+ if (btv->c.type == BTTV_BOARD_UNKNOWN) {
+ /* this might be an antique... check for MMAC label in eeprom */
+ if (!strncmp(ee, "MMAC", 4)) {
+ u8 checksum = 0;
+ for (i = 0; i < 21; i++)
+ checksum += ee[i];
+ if (checksum != ee[21])
+ return;
+ cardid = BTTV_BOARD_OSPREY1x0_848;
+ for (i = 12; i < 21; i++)
+ serial *= 10, serial += ee[i] - '0';
+ }
+ } else {
+ unsigned short type;
+
+ for (i = 4*16; i < 8*16; i += 16) {
+ u16 checksum = ip_compute_csum(ee + i, 16);
+
+ if ((checksum&0xff) + (checksum>>8) == 0xff)
+ break;
+ }
+ if (i >= 8*16)
+ return;
+ ee += i;
+
+ /* found a valid descriptor */
+ type = get_unaligned_be16((__be16 *)(ee+4));
+
+ switch(type) {
+ /* 848 based */
+ case 0x0004:
+ cardid = BTTV_BOARD_OSPREY1x0_848;
+ break;
+ case 0x0005:
+ cardid = BTTV_BOARD_OSPREY101_848;
+ break;
+
+ /* 878 based */
+ case 0x0012:
+ case 0x0013:
+ cardid = BTTV_BOARD_OSPREY1x0;
+ break;
+ case 0x0014:
+ case 0x0015:
+ cardid = BTTV_BOARD_OSPREY1x1;
+ break;
+ case 0x0016:
+ case 0x0017:
+ case 0x0020:
+ cardid = BTTV_BOARD_OSPREY1x1_SVID;
+ break;
+ case 0x0018:
+ case 0x0019:
+ case 0x001E:
+ case 0x001F:
+ cardid = BTTV_BOARD_OSPREY2xx;
+ break;
+ case 0x001A:
+ case 0x001B:
+ cardid = BTTV_BOARD_OSPREY2x0_SVID;
+ break;
+ case 0x0040:
+ cardid = BTTV_BOARD_OSPREY500;
+ break;
+ case 0x0050:
+ case 0x0056:
+ cardid = BTTV_BOARD_OSPREY540;
+ /* bttv_osprey_540_init(btv); */
+ break;
+ case 0x0060:
+ case 0x0070:
+ case 0x00A0:
+ cardid = BTTV_BOARD_OSPREY2x0;
+ /* enable output on select control lines */
+ gpio_inout(0xffffff,0x000303);
+ break;
+ case 0x00D8:
+ cardid = BTTV_BOARD_OSPREY440;
+ break;
+ default:
+ /* unknown...leave generic, but get serial # */
+ pr_info("%d: osprey eeprom: unknown card type 0x%04x\n",
+ btv->c.nr, type);
+ break;
+ }
+ serial = get_unaligned_be32((__be32 *)(ee+6));
+ }
+
+ pr_info("%d: osprey eeprom: card=%d '%s' serial=%u\n",
+ btv->c.nr, cardid,
+ cardid > 0 ? bttv_tvcards[cardid].name : "Unknown", serial);
+
+ if (cardid<0 || btv->c.type == cardid)
+ return;
+
+ /* card type isn't set correctly */
+ if (card[btv->c.nr] < bttv_num_tvcards) {
+ pr_warn("%d: osprey eeprom: Not overriding user specified card type\n",
+ btv->c.nr);
+ } else {
+ pr_info("%d: osprey eeprom: Changing card type from %d to %d\n",
+ btv->c.nr, btv->c.type, cardid);
+ btv->c.type = cardid;
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+/* AVermedia specific stuff, from bktr_card.c */
+
+static int tuner_0_table[] = {
+ TUNER_PHILIPS_NTSC, TUNER_PHILIPS_PAL /* PAL-BG*/,
+ TUNER_PHILIPS_PAL, TUNER_PHILIPS_PAL /* PAL-I*/,
+ TUNER_PHILIPS_PAL, TUNER_PHILIPS_PAL,
+ TUNER_PHILIPS_SECAM, TUNER_PHILIPS_SECAM,
+ TUNER_PHILIPS_SECAM, TUNER_PHILIPS_PAL,
+ TUNER_PHILIPS_FM1216ME_MK3 };
+
+static int tuner_1_table[] = {
+ TUNER_TEMIC_NTSC, TUNER_TEMIC_PAL,
+ TUNER_TEMIC_PAL, TUNER_TEMIC_PAL,
+ TUNER_TEMIC_PAL, TUNER_TEMIC_PAL,
+ TUNER_TEMIC_4012FY5, TUNER_TEMIC_4012FY5, /* TUNER_TEMIC_SECAM */
+ TUNER_TEMIC_4012FY5, TUNER_TEMIC_PAL};
+
+static void __devinit avermedia_eeprom(struct bttv *btv)
+{
+ int tuner_make, tuner_tv_fm, tuner_format, tuner_type = 0;
+
+ tuner_make = (eeprom_data[0x41] & 0x7);
+ tuner_tv_fm = (eeprom_data[0x41] & 0x18) >> 3;
+ tuner_format = (eeprom_data[0x42] & 0xf0) >> 4;
+ btv->has_remote = (eeprom_data[0x42] & 0x01);
+
+ if (tuner_make == 0 || tuner_make == 2)
+ if (tuner_format <= 0x0a)
+ tuner_type = tuner_0_table[tuner_format];
+ if (tuner_make == 1)
+ if (tuner_format <= 9)
+ tuner_type = tuner_1_table[tuner_format];
+
+ if (tuner_make == 4)
+ if (tuner_format == 0x09)
+ tuner_type = TUNER_LG_NTSC_NEW_TAPC; /* TAPC-G702P */
+
+ pr_info("%d: Avermedia eeprom[0x%02x%02x]: tuner=",
+ btv->c.nr, eeprom_data[0x41], eeprom_data[0x42]);
+ if (tuner_type) {
+ btv->tuner_type = tuner_type;
+ pr_cont("%d", tuner_type);
+ } else
+ pr_cont("Unknown type");
+ pr_cont(" radio:%s remote control:%s\n",
+ tuner_tv_fm ? "yes" : "no",
+ btv->has_remote ? "yes" : "no");
+}
+
+/*
+ * For Voodoo TV/FM and Voodoo 200. These cards' tuners use a TDA9880
+ * analog demod, which is not I2C controlled like the newer and more common
+ * TDA9887 series. Instead is has two tri-state input pins, S0 and S1,
+ * that control the IF for the video and audio. Apparently, bttv GPIO
+ * 0x10000 is connected to S0. S0 low selects a 38.9 MHz VIF for B/G/D/K/I
+ * (i.e., PAL) while high selects 45.75 MHz for M/N (i.e., NTSC).
+ */
+u32 bttv_tda9880_setnorm(struct bttv *btv, u32 gpiobits)
+{
+
+ if (btv->audio == TVAUDIO_INPUT_TUNER) {
+ if (bttv_tvnorms[btv->tvnorm].v4l2_id & V4L2_STD_MN)
+ gpiobits |= 0x10000;
+ else
+ gpiobits &= ~0x10000;
+ }
+
+ gpio_bits(bttv_tvcards[btv->c.type].gpiomask, gpiobits);
+ return gpiobits;
+}
+
+
+/*
+ * reset/enable the MSP on some Hauppauge cards
+ * Thanks to Kyösti Mälkki (kmalkki@cc.hut.fi)!
+ *
+ * Hauppauge: pin 5
+ * Voodoo: pin 20
+ */
+static void __devinit boot_msp34xx(struct bttv *btv, int pin)
+{
+ int mask = (1 << pin);
+
+ gpio_inout(mask,mask);
+ gpio_bits(mask,0);
+ mdelay(2);
+ udelay(500);
+ gpio_bits(mask,mask);
+
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"msp34xx");
+ if (bttv_verbose)
+ pr_info("%d: Hauppauge/Voodoo msp34xx: reset line init [%d]\n",
+ btv->c.nr, pin);
+}
+
+/* ----------------------------------------------------------------------- */
+/* Imagenation L-Model PXC200 Framegrabber */
+/* This is basically the same procedure as
+ * used by Alessandro Rubini in his pxc200
+ * driver, but using BTTV functions */
+
+static void __devinit init_PXC200(struct bttv *btv)
+{
+ static int vals[] __devinitdata = { 0x08, 0x09, 0x0a, 0x0b, 0x0d, 0x0d,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x00 };
+ unsigned int i;
+ int tmp;
+ u32 val;
+
+ /* Initialise GPIO-connevted stuff */
+ gpio_inout(0xffffff, (1<<13));
+ gpio_write(0);
+ udelay(3);
+ gpio_write(1<<13);
+ /* GPIO inputs are pulled up, so no need to drive
+ * reset pin any longer */
+ gpio_bits(0xffffff, 0);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"pxc200");
+
+ /* we could/should try and reset/control the AD pots? but
+ right now we simply turned off the crushing. Without
+ this the AGC drifts drifts
+ remember the EN is reverse logic -->
+ setting BT848_ADC_AGC_EN disable the AGC
+ tboult@eecs.lehigh.edu
+ */
+
+ btwrite(BT848_ADC_RESERVED|BT848_ADC_AGC_EN, BT848_ADC);
+
+ /* Initialise MAX517 DAC */
+ pr_info("Setting DAC reference voltage level ...\n");
+ bttv_I2CWrite(btv,0x5E,0,0x80,1);
+
+ /* Initialise 12C508 PIC */
+ /* The I2CWrite and I2CRead commmands are actually to the
+ * same chips - but the R/W bit is included in the address
+ * argument so the numbers are different */
+
+
+ pr_info("Initialising 12C508 PIC chip ...\n");
+
+ /* First of all, enable the clock line. This is used in the PXC200-F */
+ val = btread(BT848_GPIO_DMA_CTL);
+ val |= BT848_GPIO_DMA_CTL_GPCLKMODE;
+ btwrite(val, BT848_GPIO_DMA_CTL);
+
+ /* Then, push to 0 the reset pin long enough to reset the *
+ * device same as above for the reset line, but not the same
+ * value sent to the GPIO-connected stuff
+ * which one is the good one? */
+ gpio_inout(0xffffff,(1<<2));
+ gpio_write(0);
+ udelay(10);
+ gpio_write(1<<2);
+
+ for (i = 0; i < ARRAY_SIZE(vals); i++) {
+ tmp=bttv_I2CWrite(btv,0x1E,0,vals[i],1);
+ if (tmp != -1) {
+ pr_info("I2C Write(%2.2x) = %i\nI2C Read () = %2.2x\n\n",
+ vals[i],tmp,bttv_I2CRead(btv,0x1F,NULL));
+ }
+ }
+
+ pr_info("PXC200 Initialised\n");
+}
+
+
+
+/* ----------------------------------------------------------------------- */
+/*
+ * The Adlink RTV-24 (aka Angelo) has some special initialisation to unlock
+ * it. This apparently involves the following procedure for each 878 chip:
+ *
+ * 1) write 0x00C3FEFF to the GPIO_OUT_EN register
+ *
+ * 2) write to GPIO_DATA
+ * - 0x0E
+ * - sleep 1ms
+ * - 0x10 + 0x0E
+ * - sleep 10ms
+ * - 0x0E
+ * read from GPIO_DATA into buf (uint_32)
+ * - if ( data>>18 & 0x01 != 0) || ( buf>>19 & 0x01 != 1 )
+ * error. ERROR_CPLD_Check_Failed stop.
+ *
+ * 3) write to GPIO_DATA
+ * - write 0x4400 + 0x0E
+ * - sleep 10ms
+ * - write 0x4410 + 0x0E
+ * - sleep 1ms
+ * - write 0x0E
+ * read from GPIO_DATA into buf (uint_32)
+ * - if ( buf>>18 & 0x01 ) || ( buf>>19 & 0x01 != 0 )
+ * error. ERROR_CPLD_Check_Failed.
+ */
+/* ----------------------------------------------------------------------- */
+static void
+init_RTV24 (struct bttv *btv)
+{
+ uint32_t dataRead = 0;
+ long watchdog_value = 0x0E;
+
+ pr_info("%d: Adlink RTV-24 initialisation in progress ...\n",
+ btv->c.nr);
+
+ btwrite (0x00c3feff, BT848_GPIO_OUT_EN);
+
+ btwrite (0 + watchdog_value, BT848_GPIO_DATA);
+ msleep (1);
+ btwrite (0x10 + watchdog_value, BT848_GPIO_DATA);
+ msleep (10);
+ btwrite (0 + watchdog_value, BT848_GPIO_DATA);
+
+ dataRead = btread (BT848_GPIO_DATA);
+
+ if ((((dataRead >> 18) & 0x01) != 0) || (((dataRead >> 19) & 0x01) != 1)) {
+ pr_info("%d: Adlink RTV-24 initialisation(1) ERROR_CPLD_Check_Failed (read %d)\n",
+ btv->c.nr, dataRead);
+ }
+
+ btwrite (0x4400 + watchdog_value, BT848_GPIO_DATA);
+ msleep (10);
+ btwrite (0x4410 + watchdog_value, BT848_GPIO_DATA);
+ msleep (1);
+ btwrite (watchdog_value, BT848_GPIO_DATA);
+ msleep (1);
+ dataRead = btread (BT848_GPIO_DATA);
+
+ if ((((dataRead >> 18) & 0x01) != 0) || (((dataRead >> 19) & 0x01) != 0)) {
+ pr_info("%d: Adlink RTV-24 initialisation(2) ERROR_CPLD_Check_Failed (read %d)\n",
+ btv->c.nr, dataRead);
+
+ return;
+ }
+
+ pr_info("%d: Adlink RTV-24 initialisation complete\n", btv->c.nr);
+}
+
+
+
+/* ----------------------------------------------------------------------- */
+/* Miro Pro radio stuff -- the tea5757 is connected to some GPIO ports */
+/*
+ * Copyright (c) 1999 Csaba Halasz <qgehali@uni-miskolc.hu>
+ * This code is placed under the terms of the GNU General Public License
+ *
+ * Brutally hacked by Dan Sheridan <dan.sheridan@contact.org.uk> djs52 8/3/00
+ */
+
+static void bus_low(struct bttv *btv, int bit)
+{
+ if (btv->mbox_ior) {
+ gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
+ btv->mbox_ior | btv->mbox_iow | btv->mbox_csel);
+ udelay(5);
+ }
+
+ gpio_bits(bit,0);
+ udelay(5);
+
+ if (btv->mbox_ior) {
+ gpio_bits(btv->mbox_iow | btv->mbox_csel, 0);
+ udelay(5);
+ }
+}
+
+static void bus_high(struct bttv *btv, int bit)
+{
+ if (btv->mbox_ior) {
+ gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
+ btv->mbox_ior | btv->mbox_iow | btv->mbox_csel);
+ udelay(5);
+ }
+
+ gpio_bits(bit,bit);
+ udelay(5);
+
+ if (btv->mbox_ior) {
+ gpio_bits(btv->mbox_iow | btv->mbox_csel, 0);
+ udelay(5);
+ }
+}
+
+static int bus_in(struct bttv *btv, int bit)
+{
+ if (btv->mbox_ior) {
+ gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
+ btv->mbox_ior | btv->mbox_iow | btv->mbox_csel);
+ udelay(5);
+
+ gpio_bits(btv->mbox_iow | btv->mbox_csel, 0);
+ udelay(5);
+ }
+ return gpio_read() & (bit);
+}
+
+/* TEA5757 register bits */
+#define TEA_FREQ 0:14
+#define TEA_BUFFER 15:15
+
+#define TEA_SIGNAL_STRENGTH 16:17
+
+#define TEA_PORT1 18:18
+#define TEA_PORT0 19:19
+
+#define TEA_BAND 20:21
+#define TEA_BAND_FM 0
+#define TEA_BAND_MW 1
+#define TEA_BAND_LW 2
+#define TEA_BAND_SW 3
+
+#define TEA_MONO 22:22
+#define TEA_ALLOW_STEREO 0
+#define TEA_FORCE_MONO 1
+
+#define TEA_SEARCH_DIRECTION 23:23
+#define TEA_SEARCH_DOWN 0
+#define TEA_SEARCH_UP 1
+
+#define TEA_STATUS 24:24
+#define TEA_STATUS_TUNED 0
+#define TEA_STATUS_SEARCHING 1
+
+/* Low-level stuff */
+static int tea5757_read(struct bttv *btv)
+{
+ unsigned long timeout;
+ int value = 0;
+ int i;
+
+ /* better safe than sorry */
+ gpio_inout(btv->mbox_mask, btv->mbox_clk | btv->mbox_we);
+
+ if (btv->mbox_ior) {
+ gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
+ btv->mbox_ior | btv->mbox_iow | btv->mbox_csel);
+ udelay(5);
+ }
+
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"tea5757 read");
+
+ bus_low(btv,btv->mbox_we);
+ bus_low(btv,btv->mbox_clk);
+
+ udelay(10);
+ timeout= jiffies + msecs_to_jiffies(1000);
+
+ /* wait for DATA line to go low; error if it doesn't */
+ while (bus_in(btv,btv->mbox_data) && time_before(jiffies, timeout))
+ schedule();
+ if (bus_in(btv,btv->mbox_data)) {
+ pr_warn("%d: tea5757: read timeout\n", btv->c.nr);
+ return -1;
+ }
+
+ dprintk("%d: tea5757:", btv->c.nr);
+ for (i = 0; i < 24; i++) {
+ udelay(5);
+ bus_high(btv,btv->mbox_clk);
+ udelay(5);
+ dprintk_cont("%c",
+ bus_in(btv, btv->mbox_most) == 0 ? 'T' : '-');
+ bus_low(btv,btv->mbox_clk);
+ value <<= 1;
+ value |= (bus_in(btv,btv->mbox_data) == 0)?0:1; /* MSB first */
+ dprintk_cont("%c",
+ bus_in(btv, btv->mbox_most) == 0 ? 'S' : 'M');
+ }
+ dprintk_cont("\n");
+ dprintk("%d: tea5757: read 0x%X\n", btv->c.nr, value);
+ return value;
+}
+
+static int tea5757_write(struct bttv *btv, int value)
+{
+ int i;
+ int reg = value;
+
+ gpio_inout(btv->mbox_mask, btv->mbox_clk | btv->mbox_we | btv->mbox_data);
+
+ if (btv->mbox_ior) {
+ gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
+ btv->mbox_ior | btv->mbox_iow | btv->mbox_csel);
+ udelay(5);
+ }
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"tea5757 write");
+
+ dprintk("%d: tea5757: write 0x%X\n", btv->c.nr, value);
+ bus_low(btv,btv->mbox_clk);
+ bus_high(btv,btv->mbox_we);
+ for (i = 0; i < 25; i++) {
+ if (reg & 0x1000000)
+ bus_high(btv,btv->mbox_data);
+ else
+ bus_low(btv,btv->mbox_data);
+ reg <<= 1;
+ bus_high(btv,btv->mbox_clk);
+ udelay(10);
+ bus_low(btv,btv->mbox_clk);
+ udelay(10);
+ }
+ bus_low(btv,btv->mbox_we); /* unmute !!! */
+ return 0;
+}
+
+void tea5757_set_freq(struct bttv *btv, unsigned short freq)
+{
+ dprintk("tea5757_set_freq %d\n",freq);
+ tea5757_write(btv, 5 * freq + 0x358); /* add 10.7MHz (see docs) */
+}
+
+/* RemoteVision MX (rv605) muxsel helper [Miguel Freitas]
+ *
+ * This is needed because rv605 don't use a normal multiplex, but a crosspoint
+ * switch instead (CD22M3494E). This IC can have multiple active connections
+ * between Xn (input) and Yn (output) pins. We need to clear any existing
+ * connection prior to establish a new one, pulsing the STROBE pin.
+ *
+ * The board hardwire Y0 (xpoint) to MUX1 and MUXOUT to Yin.
+ * GPIO pins are wired as:
+ * GPIO[0:3] - AX[0:3] (xpoint) - P1[0:3] (microcontroller)
+ * GPIO[4:6] - AY[0:2] (xpoint) - P1[4:6] (microcontroller)
+ * GPIO[7] - DATA (xpoint) - P1[7] (microcontroller)
+ * GPIO[8] - - P3[5] (microcontroller)
+ * GPIO[9] - RESET (xpoint) - P3[6] (microcontroller)
+ * GPIO[10] - STROBE (xpoint) - P3[7] (microcontroller)
+ * GPINTR - - P3[4] (microcontroller)
+ *
+ * The microcontroller is a 80C32 like. It should be possible to change xpoint
+ * configuration either directly (as we are doing) or using the microcontroller
+ * which is also wired to I2C interface. I have no further info on the
+ * microcontroller features, one would need to disassembly the firmware.
+ * note: the vendor refused to give any information on this product, all
+ * that stuff was found using a multimeter! :)
+ */
+static void rv605_muxsel(struct bttv *btv, unsigned int input)
+{
+ static const u8 muxgpio[] = { 0x3, 0x1, 0x2, 0x4, 0xf, 0x7, 0xe, 0x0,
+ 0xd, 0xb, 0xc, 0x6, 0x9, 0x5, 0x8, 0xa };
+
+ gpio_bits(0x07f, muxgpio[input]);
+
+ /* reset all conections */
+ gpio_bits(0x200,0x200);
+ mdelay(1);
+ gpio_bits(0x200,0x000);
+ mdelay(1);
+
+ /* create a new connection */
+ gpio_bits(0x480,0x480);
+ mdelay(1);
+ gpio_bits(0x480,0x080);
+ mdelay(1);
+}
+
+/* Tibet Systems 'Progress DVR' CS16 muxsel helper [Chris Fanning]
+ *
+ * The CS16 (available on eBay cheap) is a PCI board with four Fusion
+ * 878A chips, a PCI bridge, an Atmel microcontroller, four sync separator
+ * chips, ten eight input analog multiplexors, a not chip and a few
+ * other components.
+ *
+ * 16 inputs on a secondary bracket are provided and can be selected
+ * from each of the four capture chips. Two of the eight input
+ * multiplexors are used to select from any of the 16 input signals.
+ *
+ * Unsupported hardware capabilities:
+ * . A video output monitor on the secondary bracket can be selected from
+ * one of the 878A chips.
+ * . Another passthrough but I haven't spent any time investigating it.
+ * . Digital I/O (logic level connected to GPIO) is available from an
+ * onboard header.
+ *
+ * The on chip input mux should always be set to 2.
+ * GPIO[16:19] - Video input selection
+ * GPIO[0:3] - Video output monitor select (only available from one 878A)
+ * GPIO[?:?] - Digital I/O.
+ *
+ * There is an ATMEL microcontroller with an 8031 core on board. I have not
+ * determined what function (if any) it provides. With the microcontroller
+ * and sync separator chips a guess is that it might have to do with video
+ * switching and maybe some digital I/O.
+ */
+static void tibetCS16_muxsel(struct bttv *btv, unsigned int input)
+{
+ /* video mux */
+ gpio_bits(0x0f0000, input << 16);
+}
+
+static void tibetCS16_init(struct bttv *btv)
+{
+ /* enable gpio bits, mask obtained via btSpy */
+ gpio_inout(0xffffff, 0x0f7fff);
+ gpio_write(0x0f7fff);
+}
+
+/*
+ * The following routines for the Kodicom-4400r get a little mind-twisting.
+ * There is a "master" controller and three "slave" controllers, together
+ * an analog switch which connects any of 16 cameras to any of the BT87A's.
+ * The analog switch is controlled by the "master", but the detection order
+ * of the four BT878A chips is in an order which I just don't understand.
+ * The "master" is actually the second controller to be detected. The
+ * logic on the board uses logical numbers for the 4 controllers, but
+ * those numbers are different from the detection sequence. When working
+ * with the analog switch, we need to "map" from the detection sequence
+ * over to the board's logical controller number. This mapping sequence
+ * is {3, 0, 2, 1}, i.e. the first controller to be detected is logical
+ * unit 3, the second (which is the master) is logical unit 0, etc.
+ * We need to maintain the status of the analog switch (which of the 16
+ * cameras is connected to which of the 4 controllers). Rather than
+ * add to the bttv structure for this, we use the data reserved for
+ * the mbox (unused for this card type).
+ */
+
+/*
+ * First a routine to set the analog switch, which controls which camera
+ * is routed to which controller. The switch comprises an X-address
+ * (gpio bits 0-3, representing the camera, ranging from 0-15), and a
+ * Y-address (gpio bits 4-6, representing the controller, ranging from 0-3).
+ * A data value (gpio bit 7) of '1' enables the switch, and '0' disables
+ * the switch. A STROBE bit (gpio bit 8) latches the data value into the
+ * specified address. The idea is to set the address and data, then bring
+ * STROBE high, and finally bring STROBE back to low.
+ */
+static void kodicom4400r_write(struct bttv *btv,
+ unsigned char xaddr,
+ unsigned char yaddr,
+ unsigned char data) {
+ unsigned int udata;
+
+ udata = (data << 7) | ((yaddr&3) << 4) | (xaddr&0xf);
+ gpio_bits(0x1ff, udata); /* write ADDR and DAT */
+ gpio_bits(0x1ff, udata | (1 << 8)); /* strobe high */
+ gpio_bits(0x1ff, udata); /* strobe low */
+}
+
+/*
+ * Next the mux select. Both the "master" and "slave" 'cards' (controllers)
+ * use this routine. The routine finds the "master" for the card, maps
+ * the controller number from the detected position over to the logical
+ * number, writes the appropriate data to the analog switch, and housekeeps
+ * the local copy of the switch information. The parameter 'input' is the
+ * requested camera number (0 - 15).
+ */
+static void kodicom4400r_muxsel(struct bttv *btv, unsigned int input)
+{
+ char *sw_status;
+ int xaddr, yaddr;
+ struct bttv *mctlr;
+ static unsigned char map[4] = {3, 0, 2, 1};
+
+ mctlr = master[btv->c.nr];
+ if (mctlr == NULL) { /* ignore if master not yet detected */
+ return;
+ }
+ yaddr = (btv->c.nr - mctlr->c.nr + 1) & 3; /* the '&' is for safety */
+ yaddr = map[yaddr];
+ sw_status = (char *)(&mctlr->mbox_we);
+ xaddr = input & 0xf;
+ /* Check if the controller/camera pair has changed, else ignore */
+ if (sw_status[yaddr] != xaddr)
+ {
+ /* "open" the old switch, "close" the new one, save the new */
+ kodicom4400r_write(mctlr, sw_status[yaddr], yaddr, 0);
+ sw_status[yaddr] = xaddr;
+ kodicom4400r_write(mctlr, xaddr, yaddr, 1);
+ }
+}
+
+/*
+ * During initialisation, we need to reset the analog switch. We
+ * also preset the switch to map the 4 connectors on the card to the
+ * *user's* (see above description of kodicom4400r_muxsel) channels
+ * 0 through 3
+ */
+static void kodicom4400r_init(struct bttv *btv)
+{
+ char *sw_status = (char *)(&btv->mbox_we);
+ int ix;
+
+ gpio_inout(0x0003ff, 0x0003ff);
+ gpio_write(1 << 9); /* reset MUX */
+ gpio_write(0);
+ /* Preset camera 0 to the 4 controllers */
+ for (ix = 0; ix < 4; ix++) {
+ sw_status[ix] = ix;
+ kodicom4400r_write(btv, ix, ix, 1);
+ }
+ /*
+ * Since this is the "master", we need to set up the
+ * other three controller chips' pointers to this structure
+ * for later use in the muxsel routine.
+ */
+ if ((btv->c.nr<1) || (btv->c.nr>BTTV_MAX-3))
+ return;
+ master[btv->c.nr-1] = btv;
+ master[btv->c.nr] = btv;
+ master[btv->c.nr+1] = btv;
+ master[btv->c.nr+2] = btv;
+}
+
+/* The Grandtec X-Guard framegrabber card uses two Dual 4-channel
+ * video multiplexers to provide up to 16 video inputs. These
+ * multiplexers are controlled by the lower 8 GPIO pins of the
+ * bt878. The multiplexers probably Pericom PI5V331Q or similar.
+
+ * xxx0 is pin xxx of multiplexer U5,
+ * yyy1 is pin yyy of multiplexer U2
+ */
+#define ENA0 0x01
+#define ENB0 0x02
+#define ENA1 0x04
+#define ENB1 0x08
+
+#define IN10 0x10
+#define IN00 0x20
+#define IN11 0x40
+#define IN01 0x80
+
+static void xguard_muxsel(struct bttv *btv, unsigned int input)
+{
+ static const int masks[] = {
+ ENB0, ENB0|IN00, ENB0|IN10, ENB0|IN00|IN10,
+ ENA0, ENA0|IN00, ENA0|IN10, ENA0|IN00|IN10,
+ ENB1, ENB1|IN01, ENB1|IN11, ENB1|IN01|IN11,
+ ENA1, ENA1|IN01, ENA1|IN11, ENA1|IN01|IN11,
+ };
+ gpio_write(masks[input%16]);
+}
+static void picolo_tetra_init(struct bttv *btv)
+{
+ /*This is the video input redirection fonctionality : I DID NOT USED IT. */
+ btwrite (0x08<<16,BT848_GPIO_DATA);/*GPIO[19] [==> 4053 B+C] set to 1 */
+ btwrite (0x04<<16,BT848_GPIO_DATA);/*GPIO[18] [==> 4053 A] set to 1*/
+}
+static void picolo_tetra_muxsel (struct bttv* btv, unsigned int input)
+{
+
+ dprintk("%d : picolo_tetra_muxsel => input = %d\n", btv->c.nr, input);
+ /*Just set the right path in the analog multiplexers : channel 1 -> 4 ==> Analog Mux ==> MUX0*/
+ /*GPIO[20]&GPIO[21] used to choose the right input*/
+ btwrite (input<<20,BT848_GPIO_DATA);
+
+}
+
+/*
+ * ivc120_muxsel [Added by Alan Garfield <alan@fromorbit.com>]
+ *
+ * The IVC120G security card has 4 i2c controlled TDA8540 matrix
+ * swichers to provide 16 channels to MUX0. The TDA8540's have
+ * 4 independent outputs and as such the IVC120G also has the
+ * optional "Monitor Out" bus. This allows the card to be looking
+ * at one input while the monitor is looking at another.
+ *
+ * Since I've couldn't be bothered figuring out how to add an
+ * independent muxsel for the monitor bus, I've just set it to
+ * whatever the card is looking at.
+ *
+ * OUT0 of the TDA8540's is connected to MUX0 (0x03)
+ * OUT1 of the TDA8540's is connected to "Monitor Out" (0x0C)
+ *
+ * TDA8540_ALT3 IN0-3 = Channel 13 - 16 (0x03)
+ * TDA8540_ALT4 IN0-3 = Channel 1 - 4 (0x03)
+ * TDA8540_ALT5 IN0-3 = Channel 5 - 8 (0x03)
+ * TDA8540_ALT6 IN0-3 = Channel 9 - 12 (0x03)
+ *
+ */
+
+/* All 7 possible sub-ids for the TDA8540 Matrix Switcher */
+#define I2C_TDA8540 0x90
+#define I2C_TDA8540_ALT1 0x92
+#define I2C_TDA8540_ALT2 0x94
+#define I2C_TDA8540_ALT3 0x96
+#define I2C_TDA8540_ALT4 0x98
+#define I2C_TDA8540_ALT5 0x9a
+#define I2C_TDA8540_ALT6 0x9c
+
+static void ivc120_muxsel(struct bttv *btv, unsigned int input)
+{
+ /* Simple maths */
+ int key = input % 4;
+ int matrix = input / 4;
+
+ dprintk("%d: ivc120_muxsel: Input - %02d | TDA - %02d | In - %02d\n",
+ btv->c.nr, input, matrix, key);
+
+ /* Handles the input selection on the TDA8540's */
+ bttv_I2CWrite(btv, I2C_TDA8540_ALT3, 0x00,
+ ((matrix == 3) ? (key | key << 2) : 0x00), 1);
+ bttv_I2CWrite(btv, I2C_TDA8540_ALT4, 0x00,
+ ((matrix == 0) ? (key | key << 2) : 0x00), 1);
+ bttv_I2CWrite(btv, I2C_TDA8540_ALT5, 0x00,
+ ((matrix == 1) ? (key | key << 2) : 0x00), 1);
+ bttv_I2CWrite(btv, I2C_TDA8540_ALT6, 0x00,
+ ((matrix == 2) ? (key | key << 2) : 0x00), 1);
+
+ /* Handles the output enables on the TDA8540's */
+ bttv_I2CWrite(btv, I2C_TDA8540_ALT3, 0x02,
+ ((matrix == 3) ? 0x03 : 0x00), 1); /* 13 - 16 */
+ bttv_I2CWrite(btv, I2C_TDA8540_ALT4, 0x02,
+ ((matrix == 0) ? 0x03 : 0x00), 1); /* 1-4 */
+ bttv_I2CWrite(btv, I2C_TDA8540_ALT5, 0x02,
+ ((matrix == 1) ? 0x03 : 0x00), 1); /* 5-8 */
+ bttv_I2CWrite(btv, I2C_TDA8540_ALT6, 0x02,
+ ((matrix == 2) ? 0x03 : 0x00), 1); /* 9-12 */
+
+ /* 878's MUX0 is already selected for input via muxsel values */
+}
+
+
+/* PXC200 muxsel helper
+ * luke@syseng.anu.edu.au
+ * another transplant
+ * from Alessandro Rubini (rubini@linux.it)
+ *
+ * There are 4 kinds of cards:
+ * PXC200L which is bt848
+ * PXC200F which is bt848 with PIC controlling mux
+ * PXC200AL which is bt878
+ * PXC200AF which is bt878 with PIC controlling mux
+ */
+#define PX_CFG_PXC200F 0x01
+#define PX_FLAG_PXC200A 0x00001000 /* a pxc200A is bt-878 based */
+#define PX_I2C_PIC 0x0f
+#define PX_PXC200A_CARDID 0x200a1295
+#define PX_I2C_CMD_CFG 0x00
+
+static void PXC200_muxsel(struct bttv *btv, unsigned int input)
+{
+ int rc;
+ long mux;
+ int bitmask;
+ unsigned char buf[2];
+
+ /* Read PIC config to determine if this is a PXC200F */
+ /* PX_I2C_CMD_CFG*/
+ buf[0]=0;
+ buf[1]=0;
+ rc=bttv_I2CWrite(btv,(PX_I2C_PIC<<1),buf[0],buf[1],1);
+ if (rc) {
+ pr_debug("%d: PXC200_muxsel: pic cfg write failed:%d\n",
+ btv->c.nr, rc);
+ /* not PXC ? do nothing */
+ return;
+ }
+
+ rc=bttv_I2CRead(btv,(PX_I2C_PIC<<1),NULL);
+ if (!(rc & PX_CFG_PXC200F)) {
+ pr_debug("%d: PXC200_muxsel: not PXC200F rc:%d\n",
+ btv->c.nr, rc);
+ return;
+ }
+
+
+ /* The multiplexer in the 200F is handled by the GPIO port */
+ /* get correct mapping between inputs */
+ /* mux = bttv_tvcards[btv->type].muxsel[input] & 3; */
+ /* ** not needed!? */
+ mux = input;
+
+ /* make sure output pins are enabled */
+ /* bitmask=0x30f; */
+ bitmask=0x302;
+ /* check whether we have a PXC200A */
+ if (btv->cardid == PX_PXC200A_CARDID) {
+ bitmask ^= 0x180; /* use 7 and 9, not 8 and 9 */
+ bitmask |= 7<<4; /* the DAC */
+ }
+ btwrite(bitmask, BT848_GPIO_OUT_EN);
+
+ bitmask = btread(BT848_GPIO_DATA);
+ if (btv->cardid == PX_PXC200A_CARDID)
+ bitmask = (bitmask & ~0x280) | ((mux & 2) << 8) | ((mux & 1) << 7);
+ else /* older device */
+ bitmask = (bitmask & ~0x300) | ((mux & 3) << 8);
+ btwrite(bitmask,BT848_GPIO_DATA);
+
+ /*
+ * Was "to be safe, set the bt848 to input 0"
+ * Actually, since it's ok at load time, better not messing
+ * with these bits (on PXC200AF you need to set mux 2 here)
+ *
+ * needed because bttv-driver sets mux before calling this function
+ */
+ if (btv->cardid == PX_PXC200A_CARDID)
+ btaor(2<<5, ~BT848_IFORM_MUXSEL, BT848_IFORM);
+ else /* older device */
+ btand(~BT848_IFORM_MUXSEL,BT848_IFORM);
+
+ pr_debug("%d: setting input channel to:%d\n", btv->c.nr, (int)mux);
+}
+
+static void phytec_muxsel(struct bttv *btv, unsigned int input)
+{
+ unsigned int mux = input % 4;
+
+ if (input == btv->svhs)
+ mux = 0;
+
+ gpio_bits(0x3, mux);
+}
+
+/*
+ * GeoVision GV-800(S) functions
+ * Bruno Christo <bchristo@inf.ufsm.br>
+*/
+
+/* This is a function to control the analog switch, which determines which
+ * camera is routed to which controller. The switch comprises an X-address
+ * (gpio bits 0-3, representing the camera, ranging from 0-15), and a
+ * Y-address (gpio bits 4-6, representing the controller, ranging from 0-3).
+ * A data value (gpio bit 18) of '1' enables the switch, and '0' disables
+ * the switch. A STROBE bit (gpio bit 17) latches the data value into the
+ * specified address. There is also a chip select (gpio bit 16).
+ * The idea is to set the address and chip select together, bring
+ * STROBE high, write the data, and finally bring STROBE back to low.
+ */
+static void gv800s_write(struct bttv *btv,
+ unsigned char xaddr,
+ unsigned char yaddr,
+ unsigned char data) {
+ /* On the "master" 878A:
+ * GPIO bits 0-9 are used for the analog switch:
+ * 00 - 03: camera selector
+ * 04 - 06: 878A (controller) selector
+ * 16: cselect
+ * 17: strobe
+ * 18: data (1->on, 0->off)
+ * 19: reset
+ */
+ const u32 ADDRESS = ((xaddr&0xf) | (yaddr&3)<<4);
+ const u32 CSELECT = 1<<16;
+ const u32 STROBE = 1<<17;
+ const u32 DATA = data<<18;
+
+ gpio_bits(0x1007f, ADDRESS | CSELECT); /* write ADDRESS and CSELECT */
+ gpio_bits(0x20000, STROBE); /* STROBE high */
+ gpio_bits(0x40000, DATA); /* write DATA */
+ gpio_bits(0x20000, ~STROBE); /* STROBE low */
+}
+
+/*
+ * GeoVision GV-800(S) muxsel
+ *
+ * Each of the 4 cards (controllers) use this function.
+ * The controller using this function selects the input through the GPIO pins
+ * of the "master" card. A pointer to this card is stored in master[btv->c.nr].
+ *
+ * The parameter 'input' is the requested camera number (0-4) on the controller.
+ * The map array has the address of each input. Note that the addresses in the
+ * array are in the sequence the original GeoVision driver uses, that is, set
+ * every controller to input 0, then to input 1, 2, 3, repeat. This means that
+ * the physical "camera 1" connector corresponds to controller 0 input 0,
+ * "camera 2" corresponds to controller 1 input 0, and so on.
+ *
+ * After getting the input address, the function then writes the appropriate
+ * data to the analog switch, and housekeeps the local copy of the switch
+ * information.
+ */
+static void gv800s_muxsel(struct bttv *btv, unsigned int input)
+{
+ struct bttv *mctlr;
+ char *sw_status;
+ int xaddr, yaddr;
+ static unsigned int map[4][4] = { { 0x0, 0x4, 0xa, 0x6 },
+ { 0x1, 0x5, 0xb, 0x7 },
+ { 0x2, 0x8, 0xc, 0xe },
+ { 0x3, 0x9, 0xd, 0xf } };
+ input = input%4;
+ mctlr = master[btv->c.nr];
+ if (mctlr == NULL) {
+ /* do nothing until the "master" is detected */
+ return;
+ }
+ yaddr = (btv->c.nr - mctlr->c.nr) & 3;
+ sw_status = (char *)(&mctlr->mbox_we);
+ xaddr = map[yaddr][input] & 0xf;
+
+ /* Check if the controller/camera pair has changed, ignore otherwise */
+ if (sw_status[yaddr] != xaddr) {
+ /* disable the old switch, enable the new one and save status */
+ gv800s_write(mctlr, sw_status[yaddr], yaddr, 0);
+ sw_status[yaddr] = xaddr;
+ gv800s_write(mctlr, xaddr, yaddr, 1);
+ }
+}
+
+/* GeoVision GV-800(S) "master" chip init */
+static void gv800s_init(struct bttv *btv)
+{
+ char *sw_status = (char *)(&btv->mbox_we);
+ int ix;
+
+ gpio_inout(0xf107f, 0xf107f);
+ gpio_write(1<<19); /* reset the analog MUX */
+ gpio_write(0);
+
+ /* Preset camera 0 to the 4 controllers */
+ for (ix = 0; ix < 4; ix++) {
+ sw_status[ix] = ix;
+ gv800s_write(btv, ix, ix, 1);
+ }
+
+ /* Inputs on the "master" controller need this brightness fix */
+ bttv_I2CWrite(btv, 0x18, 0x5, 0x90, 1);
+
+ if (btv->c.nr > BTTV_MAX-4)
+ return;
+ /*
+ * Store the "master" controller pointer in the master
+ * array for later use in the muxsel function.
+ */
+ master[btv->c.nr] = btv;
+ master[btv->c.nr+1] = btv;
+ master[btv->c.nr+2] = btv;
+ master[btv->c.nr+3] = btv;
+}
+
+/* ----------------------------------------------------------------------- */
+/* motherboard chipset specific stuff */
+
+void __init bttv_check_chipset(void)
+{
+ int pcipci_fail = 0;
+ struct pci_dev *dev = NULL;
+
+ if (pci_pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL)) /* should check if target is AGP */
+ pcipci_fail = 1;
+ if (pci_pci_problems & (PCIPCI_TRITON|PCIPCI_NATOMA|PCIPCI_VIAETBF))
+ triton1 = 1;
+ if (pci_pci_problems & PCIPCI_VSFX)
+ vsfx = 1;
+#ifdef PCIPCI_ALIMAGIK
+ if (pci_pci_problems & PCIPCI_ALIMAGIK)
+ latency = 0x0A;
+#endif
+
+
+ /* print warnings about any quirks found */
+ if (triton1)
+ pr_info("Host bridge needs ETBF enabled\n");
+ if (vsfx)
+ pr_info("Host bridge needs VSFX enabled\n");
+ if (pcipci_fail) {
+ pr_info("bttv and your chipset may not work together\n");
+ if (!no_overlay) {
+ pr_info("overlay will be disabled\n");
+ no_overlay = 1;
+ } else {
+ pr_info("overlay forced. Use this option at your own risk.\n");
+ }
+ }
+ if (UNSET != latency)
+ pr_info("pci latency fixup [%d]\n", latency);
+ while ((dev = pci_get_device(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_82441, dev))) {
+ unsigned char b;
+ pci_read_config_byte(dev, 0x53, &b);
+ if (bttv_debug)
+ pr_info("Host bridge: 82441FX Natoma, bufcon=0x%02x\n",
+ b);
+ }
+}
+
+int __devinit bttv_handle_chipset(struct bttv *btv)
+{
+ unsigned char command;
+
+ if (!triton1 && !vsfx && UNSET == latency)
+ return 0;
+
+ if (bttv_verbose) {
+ if (triton1)
+ pr_info("%d: enabling ETBF (430FX/VP3 compatibility)\n",
+ btv->c.nr);
+ if (vsfx && btv->id >= 878)
+ pr_info("%d: enabling VSFX\n", btv->c.nr);
+ if (UNSET != latency)
+ pr_info("%d: setting pci timer to %d\n",
+ btv->c.nr, latency);
+ }
+
+ if (btv->id < 878) {
+ /* bt848 (mis)uses a bit in the irq mask for etbf */
+ if (triton1)
+ btv->triton1 = BT848_INT_ETBF;
+ } else {
+ /* bt878 has a bit in the pci config space for it */
+ pci_read_config_byte(btv->c.pci, BT878_DEVCTRL, &command);
+ if (triton1)
+ command |= BT878_EN_TBFX;
+ if (vsfx)
+ command |= BT878_EN_VSFX;
+ pci_write_config_byte(btv->c.pci, BT878_DEVCTRL, command);
+ }
+ if (UNSET != latency)
+ pci_write_config_byte(btv->c.pci, PCI_LATENCY_TIMER, latency);
+ return 0;
+}
+
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c
new file mode 100644
index 00000000..e581b37b
--- /dev/null
+++ b/drivers/media/video/bt8xx/bttv-driver.c
@@ -0,0 +1,4631 @@
+/*
+
+ bttv - Bt848 frame grabber driver
+
+ Copyright (C) 1996,97,98 Ralph Metzler <rjkm@thp.uni-koeln.de>
+ & Marcus Metzler <mocm@thp.uni-koeln.de>
+ (c) 1999-2002 Gerd Knorr <kraxel@bytesex.org>
+
+ some v4l2 code lines are taken from Justin's bttv2 driver which is
+ (c) 2000 Justin Schoeman <justin@suntiger.ee.up.ac.za>
+
+ V4L1 removal from:
+ (c) 2005-2006 Nickolay V. Shmyrev <nshmyrev@yandex.ru>
+
+ Fixes to be fully V4L2 compliant by
+ (c) 2006 Mauro Carvalho Chehab <mchehab@infradead.org>
+
+ Cropping and overscan support
+ Copyright (C) 2005, 2006 Michael H. Schimek <mschimek@gmx.at>
+ Sponsored by OPQ Systems AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/kdev_t.h>
+#include "bttvp.h"
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/tvaudio.h>
+#include <media/msp3400.h>
+
+#include <linux/dma-mapping.h>
+
+#include <asm/io.h>
+#include <asm/byteorder.h>
+
+#include <media/saa6588.h>
+
+#define BTTV_VERSION "0.9.19"
+
+unsigned int bttv_num; /* number of Bt848s in use */
+struct bttv *bttvs[BTTV_MAX];
+
+unsigned int bttv_debug;
+unsigned int bttv_verbose = 1;
+unsigned int bttv_gpio;
+
+/* config variables */
+#ifdef __BIG_ENDIAN
+static unsigned int bigendian=1;
+#else
+static unsigned int bigendian;
+#endif
+static unsigned int radio[BTTV_MAX];
+static unsigned int irq_debug;
+static unsigned int gbuffers = 8;
+static unsigned int gbufsize = 0x208000;
+static unsigned int reset_crop = 1;
+
+static int video_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
+static int radio_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
+static int vbi_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
+static int debug_latency;
+static int disable_ir;
+
+static unsigned int fdsr;
+
+/* options */
+static unsigned int combfilter;
+static unsigned int lumafilter;
+static unsigned int automute = 1;
+static unsigned int chroma_agc;
+static unsigned int adc_crush = 1;
+static unsigned int whitecrush_upper = 0xCF;
+static unsigned int whitecrush_lower = 0x7F;
+static unsigned int vcr_hack;
+static unsigned int irq_iswitch;
+static unsigned int uv_ratio = 50;
+static unsigned int full_luma_range;
+static unsigned int coring;
+
+/* API features (turn on/off stuff for testing) */
+static unsigned int v4l2 = 1;
+
+/* insmod args */
+module_param(bttv_verbose, int, 0644);
+module_param(bttv_gpio, int, 0644);
+module_param(bttv_debug, int, 0644);
+module_param(irq_debug, int, 0644);
+module_param(debug_latency, int, 0644);
+module_param(disable_ir, int, 0444);
+
+module_param(fdsr, int, 0444);
+module_param(gbuffers, int, 0444);
+module_param(gbufsize, int, 0444);
+module_param(reset_crop, int, 0444);
+
+module_param(v4l2, int, 0644);
+module_param(bigendian, int, 0644);
+module_param(irq_iswitch, int, 0644);
+module_param(combfilter, int, 0444);
+module_param(lumafilter, int, 0444);
+module_param(automute, int, 0444);
+module_param(chroma_agc, int, 0444);
+module_param(adc_crush, int, 0444);
+module_param(whitecrush_upper, int, 0444);
+module_param(whitecrush_lower, int, 0444);
+module_param(vcr_hack, int, 0444);
+module_param(uv_ratio, int, 0444);
+module_param(full_luma_range, int, 0444);
+module_param(coring, int, 0444);
+
+module_param_array(radio, int, NULL, 0444);
+module_param_array(video_nr, int, NULL, 0444);
+module_param_array(radio_nr, int, NULL, 0444);
+module_param_array(vbi_nr, int, NULL, 0444);
+
+MODULE_PARM_DESC(radio,"The TV card supports radio, default is 0 (no)");
+MODULE_PARM_DESC(bigendian,"byte order of the framebuffer, default is native endian");
+MODULE_PARM_DESC(bttv_verbose,"verbose startup messages, default is 1 (yes)");
+MODULE_PARM_DESC(bttv_gpio,"log gpio changes, default is 0 (no)");
+MODULE_PARM_DESC(bttv_debug,"debug messages, default is 0 (no)");
+MODULE_PARM_DESC(irq_debug,"irq handler debug messages, default is 0 (no)");
+MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
+MODULE_PARM_DESC(gbuffers,"number of capture buffers. range 2-32, default 8");
+MODULE_PARM_DESC(gbufsize,"size of the capture buffers, default is 0x208000");
+MODULE_PARM_DESC(reset_crop,"reset cropping parameters at open(), default "
+ "is 1 (yes) for compatibility with older applications");
+MODULE_PARM_DESC(automute,"mute audio on bad/missing video signal, default is 1 (yes)");
+MODULE_PARM_DESC(chroma_agc,"enables the AGC of chroma signal, default is 0 (no)");
+MODULE_PARM_DESC(adc_crush,"enables the luminance ADC crush, default is 1 (yes)");
+MODULE_PARM_DESC(whitecrush_upper,"sets the white crush upper value, default is 207");
+MODULE_PARM_DESC(whitecrush_lower,"sets the white crush lower value, default is 127");
+MODULE_PARM_DESC(vcr_hack,"enables the VCR hack (improves synch on poor VCR tapes), default is 0 (no)");
+MODULE_PARM_DESC(irq_iswitch,"switch inputs in irq handler");
+MODULE_PARM_DESC(uv_ratio,"ratio between u and v gains, default is 50");
+MODULE_PARM_DESC(full_luma_range,"use the full luma range, default is 0 (no)");
+MODULE_PARM_DESC(coring,"set the luma coring level, default is 0 (no)");
+MODULE_PARM_DESC(video_nr, "video device numbers");
+MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
+MODULE_PARM_DESC(radio_nr, "radio device numbers");
+
+MODULE_DESCRIPTION("bttv - v4l/v4l2 driver module for bt848/878 based cards");
+MODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(BTTV_VERSION);
+
+/* ----------------------------------------------------------------------- */
+/* sysfs */
+
+static ssize_t show_card(struct device *cd,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vfd = container_of(cd, struct video_device, dev);
+ struct bttv *btv = video_get_drvdata(vfd);
+ return sprintf(buf, "%d\n", btv ? btv->c.type : UNSET);
+}
+static DEVICE_ATTR(card, S_IRUGO, show_card, NULL);
+
+/* ----------------------------------------------------------------------- */
+/* dvb auto-load setup */
+#if defined(CONFIG_MODULES) && defined(MODULE)
+static void request_module_async(struct work_struct *work)
+{
+ request_module("dvb-bt8xx");
+}
+
+static void request_modules(struct bttv *dev)
+{
+ INIT_WORK(&dev->request_module_wk, request_module_async);
+ schedule_work(&dev->request_module_wk);
+}
+
+static void flush_request_modules(struct bttv *dev)
+{
+ flush_work_sync(&dev->request_module_wk);
+}
+#else
+#define request_modules(dev)
+#define flush_request_modules(dev)
+#endif /* CONFIG_MODULES */
+
+
+/* ----------------------------------------------------------------------- */
+/* static data */
+
+/* special timing tables from conexant... */
+static u8 SRAM_Table[][60] =
+{
+ /* PAL digital input over GPIO[7:0] */
+ {
+ 45, // 45 bytes following
+ 0x36,0x11,0x01,0x00,0x90,0x02,0x05,0x10,0x04,0x16,
+ 0x12,0x05,0x11,0x00,0x04,0x12,0xC0,0x00,0x31,0x00,
+ 0x06,0x51,0x08,0x03,0x89,0x08,0x07,0xC0,0x44,0x00,
+ 0x81,0x01,0x01,0xA9,0x0D,0x02,0x02,0x50,0x03,0x37,
+ 0x37,0x00,0xAF,0x21,0x00
+ },
+ /* NTSC digital input over GPIO[7:0] */
+ {
+ 51, // 51 bytes following
+ 0x0C,0xC0,0x00,0x00,0x90,0x02,0x03,0x10,0x03,0x06,
+ 0x10,0x04,0x12,0x12,0x05,0x02,0x13,0x04,0x19,0x00,
+ 0x04,0x39,0x00,0x06,0x59,0x08,0x03,0x83,0x08,0x07,
+ 0x03,0x50,0x00,0xC0,0x40,0x00,0x86,0x01,0x01,0xA6,
+ 0x0D,0x02,0x03,0x11,0x01,0x05,0x37,0x00,0xAC,0x21,
+ 0x00,
+ },
+ // TGB_NTSC392 // quartzsight
+ // This table has been modified to be used for Fusion Rev D
+ {
+ 0x2A, // size of table = 42
+ 0x06, 0x08, 0x04, 0x0a, 0xc0, 0x00, 0x18, 0x08, 0x03, 0x24,
+ 0x08, 0x07, 0x02, 0x90, 0x02, 0x08, 0x10, 0x04, 0x0c, 0x10,
+ 0x05, 0x2c, 0x11, 0x04, 0x55, 0x48, 0x00, 0x05, 0x50, 0x00,
+ 0xbf, 0x0c, 0x02, 0x2f, 0x3d, 0x00, 0x2f, 0x3f, 0x00, 0xc3,
+ 0x20, 0x00
+ }
+};
+
+/* minhdelayx1 first video pixel we can capture on a line and
+ hdelayx1 start of active video, both relative to rising edge of
+ /HRESET pulse (0H) in 1 / fCLKx1.
+ swidth width of active video and
+ totalwidth total line width, both in 1 / fCLKx1.
+ sqwidth total line width in square pixels.
+ vdelay start of active video in 2 * field lines relative to
+ trailing edge of /VRESET pulse (VDELAY register).
+ sheight height of active video in 2 * field lines.
+ videostart0 ITU-R frame line number of the line corresponding
+ to vdelay in the first field. */
+#define CROPCAP(minhdelayx1, hdelayx1, swidth, totalwidth, sqwidth, \
+ vdelay, sheight, videostart0) \
+ .cropcap.bounds.left = minhdelayx1, \
+ /* * 2 because vertically we count field lines times two, */ \
+ /* e.g. 23 * 2 to 23 * 2 + 576 in PAL-BGHI defrect. */ \
+ .cropcap.bounds.top = (videostart0) * 2 - (vdelay) + MIN_VDELAY, \
+ /* 4 is a safety margin at the end of the line. */ \
+ .cropcap.bounds.width = (totalwidth) - (minhdelayx1) - 4, \
+ .cropcap.bounds.height = (sheight) + (vdelay) - MIN_VDELAY, \
+ .cropcap.defrect.left = hdelayx1, \
+ .cropcap.defrect.top = (videostart0) * 2, \
+ .cropcap.defrect.width = swidth, \
+ .cropcap.defrect.height = sheight, \
+ .cropcap.pixelaspect.numerator = totalwidth, \
+ .cropcap.pixelaspect.denominator = sqwidth,
+
+const struct bttv_tvnorm bttv_tvnorms[] = {
+ /* PAL-BDGHI */
+ /* max. active video is actually 922, but 924 is divisible by 4 and 3! */
+ /* actually, max active PAL with HSCALE=0 is 948, NTSC is 768 - nil */
+ {
+ .v4l2_id = V4L2_STD_PAL,
+ .name = "PAL",
+ .Fsc = 35468950,
+ .swidth = 924,
+ .sheight = 576,
+ .totalwidth = 1135,
+ .adelay = 0x7f,
+ .bdelay = 0x72,
+ .iform = (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
+ .scaledtwidth = 1135,
+ .hdelayx1 = 186,
+ .hactivex1 = 924,
+ .vdelay = 0x20,
+ .vbipack = 255, /* min (2048 / 4, 0x1ff) & 0xff */
+ .sram = 0,
+ /* ITU-R frame line number of the first VBI line
+ we can capture, of the first and second field.
+ The last line is determined by cropcap.bounds. */
+ .vbistart = { 7, 320 },
+ CROPCAP(/* minhdelayx1 */ 68,
+ /* hdelayx1 */ 186,
+ /* Should be (768 * 1135 + 944 / 2) / 944.
+ cropcap.defrect is used for image width
+ checks, so we keep the old value 924. */
+ /* swidth */ 924,
+ /* totalwidth */ 1135,
+ /* sqwidth */ 944,
+ /* vdelay */ 0x20,
+ /* sheight */ 576,
+ /* videostart0 */ 23)
+ /* bt878 (and bt848?) can capture another
+ line below active video. */
+ .cropcap.bounds.height = (576 + 2) + 0x20 - 2,
+ },{
+ .v4l2_id = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR,
+ .name = "NTSC",
+ .Fsc = 28636363,
+ .swidth = 768,
+ .sheight = 480,
+ .totalwidth = 910,
+ .adelay = 0x68,
+ .bdelay = 0x5d,
+ .iform = (BT848_IFORM_NTSC|BT848_IFORM_XT0),
+ .scaledtwidth = 910,
+ .hdelayx1 = 128,
+ .hactivex1 = 910,
+ .vdelay = 0x1a,
+ .vbipack = 144, /* min (1600 / 4, 0x1ff) & 0xff */
+ .sram = 1,
+ .vbistart = { 10, 273 },
+ CROPCAP(/* minhdelayx1 */ 68,
+ /* hdelayx1 */ 128,
+ /* Should be (640 * 910 + 780 / 2) / 780? */
+ /* swidth */ 768,
+ /* totalwidth */ 910,
+ /* sqwidth */ 780,
+ /* vdelay */ 0x1a,
+ /* sheight */ 480,
+ /* videostart0 */ 23)
+ },{
+ .v4l2_id = V4L2_STD_SECAM,
+ .name = "SECAM",
+ .Fsc = 35468950,
+ .swidth = 924,
+ .sheight = 576,
+ .totalwidth = 1135,
+ .adelay = 0x7f,
+ .bdelay = 0xb0,
+ .iform = (BT848_IFORM_SECAM|BT848_IFORM_XT1),
+ .scaledtwidth = 1135,
+ .hdelayx1 = 186,
+ .hactivex1 = 922,
+ .vdelay = 0x20,
+ .vbipack = 255,
+ .sram = 0, /* like PAL, correct? */
+ .vbistart = { 7, 320 },
+ CROPCAP(/* minhdelayx1 */ 68,
+ /* hdelayx1 */ 186,
+ /* swidth */ 924,
+ /* totalwidth */ 1135,
+ /* sqwidth */ 944,
+ /* vdelay */ 0x20,
+ /* sheight */ 576,
+ /* videostart0 */ 23)
+ },{
+ .v4l2_id = V4L2_STD_PAL_Nc,
+ .name = "PAL-Nc",
+ .Fsc = 28636363,
+ .swidth = 640,
+ .sheight = 576,
+ .totalwidth = 910,
+ .adelay = 0x68,
+ .bdelay = 0x5d,
+ .iform = (BT848_IFORM_PAL_NC|BT848_IFORM_XT0),
+ .scaledtwidth = 780,
+ .hdelayx1 = 130,
+ .hactivex1 = 734,
+ .vdelay = 0x1a,
+ .vbipack = 144,
+ .sram = -1,
+ .vbistart = { 7, 320 },
+ CROPCAP(/* minhdelayx1 */ 68,
+ /* hdelayx1 */ 130,
+ /* swidth */ (640 * 910 + 780 / 2) / 780,
+ /* totalwidth */ 910,
+ /* sqwidth */ 780,
+ /* vdelay */ 0x1a,
+ /* sheight */ 576,
+ /* videostart0 */ 23)
+ },{
+ .v4l2_id = V4L2_STD_PAL_M,
+ .name = "PAL-M",
+ .Fsc = 28636363,
+ .swidth = 640,
+ .sheight = 480,
+ .totalwidth = 910,
+ .adelay = 0x68,
+ .bdelay = 0x5d,
+ .iform = (BT848_IFORM_PAL_M|BT848_IFORM_XT0),
+ .scaledtwidth = 780,
+ .hdelayx1 = 135,
+ .hactivex1 = 754,
+ .vdelay = 0x1a,
+ .vbipack = 144,
+ .sram = -1,
+ .vbistart = { 10, 273 },
+ CROPCAP(/* minhdelayx1 */ 68,
+ /* hdelayx1 */ 135,
+ /* swidth */ (640 * 910 + 780 / 2) / 780,
+ /* totalwidth */ 910,
+ /* sqwidth */ 780,
+ /* vdelay */ 0x1a,
+ /* sheight */ 480,
+ /* videostart0 */ 23)
+ },{
+ .v4l2_id = V4L2_STD_PAL_N,
+ .name = "PAL-N",
+ .Fsc = 35468950,
+ .swidth = 768,
+ .sheight = 576,
+ .totalwidth = 1135,
+ .adelay = 0x7f,
+ .bdelay = 0x72,
+ .iform = (BT848_IFORM_PAL_N|BT848_IFORM_XT1),
+ .scaledtwidth = 944,
+ .hdelayx1 = 186,
+ .hactivex1 = 922,
+ .vdelay = 0x20,
+ .vbipack = 144,
+ .sram = -1,
+ .vbistart = { 7, 320 },
+ CROPCAP(/* minhdelayx1 */ 68,
+ /* hdelayx1 */ 186,
+ /* swidth */ (768 * 1135 + 944 / 2) / 944,
+ /* totalwidth */ 1135,
+ /* sqwidth */ 944,
+ /* vdelay */ 0x20,
+ /* sheight */ 576,
+ /* videostart0 */ 23)
+ },{
+ .v4l2_id = V4L2_STD_NTSC_M_JP,
+ .name = "NTSC-JP",
+ .Fsc = 28636363,
+ .swidth = 640,
+ .sheight = 480,
+ .totalwidth = 910,
+ .adelay = 0x68,
+ .bdelay = 0x5d,
+ .iform = (BT848_IFORM_NTSC_J|BT848_IFORM_XT0),
+ .scaledtwidth = 780,
+ .hdelayx1 = 135,
+ .hactivex1 = 754,
+ .vdelay = 0x16,
+ .vbipack = 144,
+ .sram = -1,
+ .vbistart = { 10, 273 },
+ CROPCAP(/* minhdelayx1 */ 68,
+ /* hdelayx1 */ 135,
+ /* swidth */ (640 * 910 + 780 / 2) / 780,
+ /* totalwidth */ 910,
+ /* sqwidth */ 780,
+ /* vdelay */ 0x16,
+ /* sheight */ 480,
+ /* videostart0 */ 23)
+ },{
+ /* that one hopefully works with the strange timing
+ * which video recorders produce when playing a NTSC
+ * tape on a PAL TV ... */
+ .v4l2_id = V4L2_STD_PAL_60,
+ .name = "PAL-60",
+ .Fsc = 35468950,
+ .swidth = 924,
+ .sheight = 480,
+ .totalwidth = 1135,
+ .adelay = 0x7f,
+ .bdelay = 0x72,
+ .iform = (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
+ .scaledtwidth = 1135,
+ .hdelayx1 = 186,
+ .hactivex1 = 924,
+ .vdelay = 0x1a,
+ .vbipack = 255,
+ .vtotal = 524,
+ .sram = -1,
+ .vbistart = { 10, 273 },
+ CROPCAP(/* minhdelayx1 */ 68,
+ /* hdelayx1 */ 186,
+ /* swidth */ 924,
+ /* totalwidth */ 1135,
+ /* sqwidth */ 944,
+ /* vdelay */ 0x1a,
+ /* sheight */ 480,
+ /* videostart0 */ 23)
+ }
+};
+static const unsigned int BTTV_TVNORMS = ARRAY_SIZE(bttv_tvnorms);
+
+/* ----------------------------------------------------------------------- */
+/* bttv format list
+ packed pixel formats must come first */
+static const struct bttv_format formats[] = {
+ {
+ .name = "8 bpp, gray",
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .btformat = BT848_COLOR_FMT_Y8,
+ .depth = 8,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "8 bpp, dithered color",
+ .fourcc = V4L2_PIX_FMT_HI240,
+ .btformat = BT848_COLOR_FMT_RGB8,
+ .depth = 8,
+ .flags = FORMAT_FLAGS_PACKED | FORMAT_FLAGS_DITHER,
+ },{
+ .name = "15 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .btformat = BT848_COLOR_FMT_RGB15,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "15 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB555X,
+ .btformat = BT848_COLOR_FMT_RGB15,
+ .btswap = 0x03, /* byteswap */
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "16 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .btformat = BT848_COLOR_FMT_RGB16,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "16 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+ .btformat = BT848_COLOR_FMT_RGB16,
+ .btswap = 0x03, /* byteswap */
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "24 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .btformat = BT848_COLOR_FMT_RGB24,
+ .depth = 24,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "32 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_BGR32,
+ .btformat = BT848_COLOR_FMT_RGB32,
+ .depth = 32,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "32 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .btformat = BT848_COLOR_FMT_RGB32,
+ .btswap = 0x0f, /* byte+word swap */
+ .depth = 32,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .btformat = BT848_COLOR_FMT_YUY2,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .btformat = BT848_COLOR_FMT_YUY2,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "4:2:2, packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .btformat = BT848_COLOR_FMT_YUY2,
+ .btswap = 0x03, /* byteswap */
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "4:2:2, planar, Y-Cb-Cr",
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .btformat = BT848_COLOR_FMT_YCrCb422,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PLANAR,
+ .hshift = 1,
+ .vshift = 0,
+ },{
+ .name = "4:2:0, planar, Y-Cb-Cr",
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .btformat = BT848_COLOR_FMT_YCrCb422,
+ .depth = 12,
+ .flags = FORMAT_FLAGS_PLANAR,
+ .hshift = 1,
+ .vshift = 1,
+ },{
+ .name = "4:2:0, planar, Y-Cr-Cb",
+ .fourcc = V4L2_PIX_FMT_YVU420,
+ .btformat = BT848_COLOR_FMT_YCrCb422,
+ .depth = 12,
+ .flags = FORMAT_FLAGS_PLANAR | FORMAT_FLAGS_CrCb,
+ .hshift = 1,
+ .vshift = 1,
+ },{
+ .name = "4:1:1, planar, Y-Cb-Cr",
+ .fourcc = V4L2_PIX_FMT_YUV411P,
+ .btformat = BT848_COLOR_FMT_YCrCb411,
+ .depth = 12,
+ .flags = FORMAT_FLAGS_PLANAR,
+ .hshift = 2,
+ .vshift = 0,
+ },{
+ .name = "4:1:0, planar, Y-Cb-Cr",
+ .fourcc = V4L2_PIX_FMT_YUV410,
+ .btformat = BT848_COLOR_FMT_YCrCb411,
+ .depth = 9,
+ .flags = FORMAT_FLAGS_PLANAR,
+ .hshift = 2,
+ .vshift = 2,
+ },{
+ .name = "4:1:0, planar, Y-Cr-Cb",
+ .fourcc = V4L2_PIX_FMT_YVU410,
+ .btformat = BT848_COLOR_FMT_YCrCb411,
+ .depth = 9,
+ .flags = FORMAT_FLAGS_PLANAR | FORMAT_FLAGS_CrCb,
+ .hshift = 2,
+ .vshift = 2,
+ },{
+ .name = "raw scanlines",
+ .fourcc = -1,
+ .btformat = BT848_COLOR_FMT_RAW,
+ .depth = 8,
+ .flags = FORMAT_FLAGS_RAW,
+ }
+};
+static const unsigned int FORMATS = ARRAY_SIZE(formats);
+
+/* ----------------------------------------------------------------------- */
+
+#define V4L2_CID_PRIVATE_CHROMA_AGC (V4L2_CID_PRIVATE_BASE + 0)
+#define V4L2_CID_PRIVATE_COMBFILTER (V4L2_CID_PRIVATE_BASE + 1)
+#define V4L2_CID_PRIVATE_AUTOMUTE (V4L2_CID_PRIVATE_BASE + 2)
+#define V4L2_CID_PRIVATE_LUMAFILTER (V4L2_CID_PRIVATE_BASE + 3)
+#define V4L2_CID_PRIVATE_AGC_CRUSH (V4L2_CID_PRIVATE_BASE + 4)
+#define V4L2_CID_PRIVATE_VCR_HACK (V4L2_CID_PRIVATE_BASE + 5)
+#define V4L2_CID_PRIVATE_WHITECRUSH_UPPER (V4L2_CID_PRIVATE_BASE + 6)
+#define V4L2_CID_PRIVATE_WHITECRUSH_LOWER (V4L2_CID_PRIVATE_BASE + 7)
+#define V4L2_CID_PRIVATE_UV_RATIO (V4L2_CID_PRIVATE_BASE + 8)
+#define V4L2_CID_PRIVATE_FULL_LUMA_RANGE (V4L2_CID_PRIVATE_BASE + 9)
+#define V4L2_CID_PRIVATE_CORING (V4L2_CID_PRIVATE_BASE + 10)
+#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 11)
+
+static const struct v4l2_queryctrl no_ctl = {
+ .name = "42",
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+};
+static const struct v4l2_queryctrl bttv_ctls[] = {
+ /* --- video --- */
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 256,
+ .default_value = 32768,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_CONTRAST,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 128,
+ .default_value = 32768,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_SATURATION,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 128,
+ .default_value = 32768,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_HUE,
+ .name = "Hue",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 256,
+ .default_value = 32768,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ /* --- audio --- */
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .name = "Volume",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 65535/100,
+ .default_value = 65535,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_AUDIO_BALANCE,
+ .name = "Balance",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 65535/100,
+ .default_value = 32768,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_AUDIO_BASS,
+ .name = "Bass",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 65535/100,
+ .default_value = 32768,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_AUDIO_TREBLE,
+ .name = "Treble",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 65535/100,
+ .default_value = 32768,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ /* --- private --- */
+ {
+ .id = V4L2_CID_PRIVATE_CHROMA_AGC,
+ .name = "chroma agc",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_PRIVATE_COMBFILTER,
+ .name = "combfilter",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_PRIVATE_AUTOMUTE,
+ .name = "automute",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_PRIVATE_LUMAFILTER,
+ .name = "luma decimation filter",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_PRIVATE_AGC_CRUSH,
+ .name = "agc crush",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_PRIVATE_VCR_HACK,
+ .name = "vcr hack",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_PRIVATE_WHITECRUSH_UPPER,
+ .name = "whitecrush upper",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 0xCF,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_PRIVATE_WHITECRUSH_LOWER,
+ .name = "whitecrush lower",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 0x7F,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_PRIVATE_UV_RATIO,
+ .name = "uv ratio",
+ .minimum = 0,
+ .maximum = 100,
+ .step = 1,
+ .default_value = 50,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_PRIVATE_FULL_LUMA_RANGE,
+ .name = "full luma range",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_PRIVATE_CORING,
+ .name = "coring",
+ .minimum = 0,
+ .maximum = 3,
+ .step = 1,
+ .default_value = 0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ }
+
+
+
+};
+
+static const struct v4l2_queryctrl *ctrl_by_id(int id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bttv_ctls); i++)
+ if (bttv_ctls[i].id == id)
+ return bttv_ctls+i;
+
+ return NULL;
+}
+
+/* ----------------------------------------------------------------------- */
+/* resource management */
+
+/*
+ RESOURCE_ allocated by freed by
+
+ VIDEO_READ bttv_read 1) bttv_read 2)
+
+ VIDEO_STREAM VIDIOC_STREAMON VIDIOC_STREAMOFF
+ VIDIOC_QBUF 1) bttv_release
+ VIDIOCMCAPTURE 1)
+
+ OVERLAY VIDIOCCAPTURE on VIDIOCCAPTURE off
+ VIDIOC_OVERLAY on VIDIOC_OVERLAY off
+ 3) bttv_release
+
+ VBI VIDIOC_STREAMON VIDIOC_STREAMOFF
+ VIDIOC_QBUF 1) bttv_release
+ bttv_read, bttv_poll 1) 4)
+
+ 1) The resource must be allocated when we enter buffer prepare functions
+ and remain allocated while buffers are in the DMA queue.
+ 2) This is a single frame read.
+ 3) VIDIOC_S_FBUF and VIDIOC_S_FMT (OVERLAY) still work when
+ RESOURCE_OVERLAY is allocated.
+ 4) This is a continuous read, implies VIDIOC_STREAMON.
+
+ Note this driver permits video input and standard changes regardless if
+ resources are allocated.
+*/
+
+#define VBI_RESOURCES (RESOURCE_VBI)
+#define VIDEO_RESOURCES (RESOURCE_VIDEO_READ | \
+ RESOURCE_VIDEO_STREAM | \
+ RESOURCE_OVERLAY)
+
+static
+int check_alloc_btres_lock(struct bttv *btv, struct bttv_fh *fh, int bit)
+{
+ int xbits; /* mutual exclusive resources */
+
+ if (fh->resources & bit)
+ /* have it already allocated */
+ return 1;
+
+ xbits = bit;
+ if (bit & (RESOURCE_VIDEO_READ | RESOURCE_VIDEO_STREAM))
+ xbits |= RESOURCE_VIDEO_READ | RESOURCE_VIDEO_STREAM;
+
+ /* is it free? */
+ if (btv->resources & xbits) {
+ /* no, someone else uses it */
+ goto fail;
+ }
+
+ if ((bit & VIDEO_RESOURCES)
+ && 0 == (btv->resources & VIDEO_RESOURCES)) {
+ /* Do crop - use current, don't - use default parameters. */
+ __s32 top = btv->crop[!!fh->do_crop].rect.top;
+
+ if (btv->vbi_end > top)
+ goto fail;
+
+ /* We cannot capture the same line as video and VBI data.
+ Claim scan lines crop[].rect.top to bottom. */
+ btv->crop_start = top;
+ } else if (bit & VBI_RESOURCES) {
+ __s32 end = fh->vbi_fmt.end;
+
+ if (end > btv->crop_start)
+ goto fail;
+
+ /* Claim scan lines above fh->vbi_fmt.end. */
+ btv->vbi_end = end;
+ }
+
+ /* it's free, grab it */
+ fh->resources |= bit;
+ btv->resources |= bit;
+ return 1;
+
+ fail:
+ return 0;
+}
+
+static
+int check_btres(struct bttv_fh *fh, int bit)
+{
+ return (fh->resources & bit);
+}
+
+static
+int locked_btres(struct bttv *btv, int bit)
+{
+ return (btv->resources & bit);
+}
+
+/* Call with btv->lock down. */
+static void
+disclaim_vbi_lines(struct bttv *btv)
+{
+ btv->vbi_end = 0;
+}
+
+/* Call with btv->lock down. */
+static void
+disclaim_video_lines(struct bttv *btv)
+{
+ const struct bttv_tvnorm *tvnorm;
+ u8 crop;
+
+ tvnorm = &bttv_tvnorms[btv->tvnorm];
+ btv->crop_start = tvnorm->cropcap.bounds.top
+ + tvnorm->cropcap.bounds.height;
+
+ /* VBI capturing ends at VDELAY, start of video capturing, no
+ matter how many lines the VBI RISC program expects. When video
+ capturing is off, it shall no longer "preempt" VBI capturing,
+ so we set VDELAY to maximum. */
+ crop = btread(BT848_E_CROP) | 0xc0;
+ btwrite(crop, BT848_E_CROP);
+ btwrite(0xfe, BT848_E_VDELAY_LO);
+ btwrite(crop, BT848_O_CROP);
+ btwrite(0xfe, BT848_O_VDELAY_LO);
+}
+
+static
+void free_btres_lock(struct bttv *btv, struct bttv_fh *fh, int bits)
+{
+ if ((fh->resources & bits) != bits) {
+ /* trying to free resources not allocated by us ... */
+ pr_err("BUG! (btres)\n");
+ }
+ fh->resources &= ~bits;
+ btv->resources &= ~bits;
+
+ bits = btv->resources;
+
+ if (0 == (bits & VIDEO_RESOURCES))
+ disclaim_video_lines(btv);
+
+ if (0 == (bits & VBI_RESOURCES))
+ disclaim_vbi_lines(btv);
+}
+
+/* ----------------------------------------------------------------------- */
+/* If Bt848a or Bt849, use PLL for PAL/SECAM and crystal for NTSC */
+
+/* Frequency = (F_input / PLL_X) * PLL_I.PLL_F/PLL_C
+ PLL_X = Reference pre-divider (0=1, 1=2)
+ PLL_C = Post divider (0=6, 1=4)
+ PLL_I = Integer input
+ PLL_F = Fractional input
+
+ F_input = 28.636363 MHz:
+ PAL (CLKx2 = 35.46895 MHz): PLL_X = 1, PLL_I = 0x0E, PLL_F = 0xDCF9, PLL_C = 0
+*/
+
+static void set_pll_freq(struct bttv *btv, unsigned int fin, unsigned int fout)
+{
+ unsigned char fl, fh, fi;
+
+ /* prevent overflows */
+ fin/=4;
+ fout/=4;
+
+ fout*=12;
+ fi=fout/fin;
+
+ fout=(fout%fin)*256;
+ fh=fout/fin;
+
+ fout=(fout%fin)*256;
+ fl=fout/fin;
+
+ btwrite(fl, BT848_PLL_F_LO);
+ btwrite(fh, BT848_PLL_F_HI);
+ btwrite(fi|BT848_PLL_X, BT848_PLL_XCI);
+}
+
+static void set_pll(struct bttv *btv)
+{
+ int i;
+
+ if (!btv->pll.pll_crystal)
+ return;
+
+ if (btv->pll.pll_ofreq == btv->pll.pll_current) {
+ dprintk("%d: PLL: no change required\n", btv->c.nr);
+ return;
+ }
+
+ if (btv->pll.pll_ifreq == btv->pll.pll_ofreq) {
+ /* no PLL needed */
+ if (btv->pll.pll_current == 0)
+ return;
+ if (bttv_verbose)
+ pr_info("%d: PLL can sleep, using XTAL (%d)\n",
+ btv->c.nr, btv->pll.pll_ifreq);
+ btwrite(0x00,BT848_TGCTRL);
+ btwrite(0x00,BT848_PLL_XCI);
+ btv->pll.pll_current = 0;
+ return;
+ }
+
+ if (bttv_verbose)
+ pr_info("%d: Setting PLL: %d => %d (needs up to 100ms)\n",
+ btv->c.nr,
+ btv->pll.pll_ifreq, btv->pll.pll_ofreq);
+ set_pll_freq(btv, btv->pll.pll_ifreq, btv->pll.pll_ofreq);
+
+ for (i=0; i<10; i++) {
+ /* Let other people run while the PLL stabilizes */
+ msleep(10);
+
+ if (btread(BT848_DSTATUS) & BT848_DSTATUS_PLOCK) {
+ btwrite(0,BT848_DSTATUS);
+ } else {
+ btwrite(0x08,BT848_TGCTRL);
+ btv->pll.pll_current = btv->pll.pll_ofreq;
+ if (bttv_verbose)
+ pr_info("PLL set ok\n");
+ return;
+ }
+ }
+ btv->pll.pll_current = -1;
+ if (bttv_verbose)
+ pr_info("Setting PLL failed\n");
+ return;
+}
+
+/* used to switch between the bt848's analog/digital video capture modes */
+static void bt848A_set_timing(struct bttv *btv)
+{
+ int i, len;
+ int table_idx = bttv_tvnorms[btv->tvnorm].sram;
+ int fsc = bttv_tvnorms[btv->tvnorm].Fsc;
+
+ if (btv->input == btv->dig) {
+ dprintk("%d: load digital timing table (table_idx=%d)\n",
+ btv->c.nr,table_idx);
+
+ /* timing change...reset timing generator address */
+ btwrite(0x00, BT848_TGCTRL);
+ btwrite(0x02, BT848_TGCTRL);
+ btwrite(0x00, BT848_TGCTRL);
+
+ len=SRAM_Table[table_idx][0];
+ for(i = 1; i <= len; i++)
+ btwrite(SRAM_Table[table_idx][i],BT848_TGLB);
+ btv->pll.pll_ofreq = 27000000;
+
+ set_pll(btv);
+ btwrite(0x11, BT848_TGCTRL);
+ btwrite(0x41, BT848_DVSIF);
+ } else {
+ btv->pll.pll_ofreq = fsc;
+ set_pll(btv);
+ btwrite(0x0, BT848_DVSIF);
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void bt848_bright(struct bttv *btv, int bright)
+{
+ int value;
+
+ // printk("set bright: %d\n", bright); // DEBUG
+ btv->bright = bright;
+
+ /* We want -128 to 127 we get 0-65535 */
+ value = (bright >> 8) - 128;
+ btwrite(value & 0xff, BT848_BRIGHT);
+}
+
+static void bt848_hue(struct bttv *btv, int hue)
+{
+ int value;
+
+ btv->hue = hue;
+
+ /* -128 to 127 */
+ value = (hue >> 8) - 128;
+ btwrite(value & 0xff, BT848_HUE);
+}
+
+static void bt848_contrast(struct bttv *btv, int cont)
+{
+ int value,hibit;
+
+ btv->contrast = cont;
+
+ /* 0-511 */
+ value = (cont >> 7);
+ hibit = (value >> 6) & 4;
+ btwrite(value & 0xff, BT848_CONTRAST_LO);
+ btaor(hibit, ~4, BT848_E_CONTROL);
+ btaor(hibit, ~4, BT848_O_CONTROL);
+}
+
+static void bt848_sat(struct bttv *btv, int color)
+{
+ int val_u,val_v,hibits;
+
+ btv->saturation = color;
+
+ /* 0-511 for the color */
+ val_u = ((color * btv->opt_uv_ratio) / 50) >> 7;
+ val_v = (((color * (100 - btv->opt_uv_ratio) / 50) >>7)*180L)/254;
+ hibits = (val_u >> 7) & 2;
+ hibits |= (val_v >> 8) & 1;
+ btwrite(val_u & 0xff, BT848_SAT_U_LO);
+ btwrite(val_v & 0xff, BT848_SAT_V_LO);
+ btaor(hibits, ~3, BT848_E_CONTROL);
+ btaor(hibits, ~3, BT848_O_CONTROL);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int
+video_mux(struct bttv *btv, unsigned int input)
+{
+ int mux,mask2;
+
+ if (input >= bttv_tvcards[btv->c.type].video_inputs)
+ return -EINVAL;
+
+ /* needed by RemoteVideo MX */
+ mask2 = bttv_tvcards[btv->c.type].gpiomask2;
+ if (mask2)
+ gpio_inout(mask2,mask2);
+
+ if (input == btv->svhs) {
+ btor(BT848_CONTROL_COMP, BT848_E_CONTROL);
+ btor(BT848_CONTROL_COMP, BT848_O_CONTROL);
+ } else {
+ btand(~BT848_CONTROL_COMP, BT848_E_CONTROL);
+ btand(~BT848_CONTROL_COMP, BT848_O_CONTROL);
+ }
+ mux = bttv_muxsel(btv, input);
+ btaor(mux<<5, ~(3<<5), BT848_IFORM);
+ dprintk("%d: video mux: input=%d mux=%d\n", btv->c.nr, input, mux);
+
+ /* card specific hook */
+ if(bttv_tvcards[btv->c.type].muxsel_hook)
+ bttv_tvcards[btv->c.type].muxsel_hook (btv, input);
+ return 0;
+}
+
+static char *audio_modes[] = {
+ "audio: tuner", "audio: radio", "audio: extern",
+ "audio: intern", "audio: mute"
+};
+
+static int
+audio_mux(struct bttv *btv, int input, int mute)
+{
+ int gpio_val, signal;
+ struct v4l2_control ctrl;
+
+ gpio_inout(bttv_tvcards[btv->c.type].gpiomask,
+ bttv_tvcards[btv->c.type].gpiomask);
+ signal = btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC;
+
+ btv->mute = mute;
+ btv->audio = input;
+
+ /* automute */
+ mute = mute || (btv->opt_automute && !signal && !btv->radio_user);
+
+ if (mute)
+ gpio_val = bttv_tvcards[btv->c.type].gpiomute;
+ else
+ gpio_val = bttv_tvcards[btv->c.type].gpiomux[input];
+
+ switch (btv->c.type) {
+ case BTTV_BOARD_VOODOOTV_FM:
+ case BTTV_BOARD_VOODOOTV_200:
+ gpio_val = bttv_tda9880_setnorm(btv, gpio_val);
+ break;
+
+ default:
+ gpio_bits(bttv_tvcards[btv->c.type].gpiomask, gpio_val);
+ }
+
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv, audio_modes[mute ? 4 : input]);
+ if (in_interrupt())
+ return 0;
+
+ ctrl.id = V4L2_CID_AUDIO_MUTE;
+ ctrl.value = btv->mute;
+ bttv_call_all(btv, core, s_ctrl, &ctrl);
+ if (btv->sd_msp34xx) {
+ u32 in;
+
+ /* Note: the inputs tuner/radio/extern/intern are translated
+ to msp routings. This assumes common behavior for all msp3400
+ based TV cards. When this assumption fails, then the
+ specific MSP routing must be added to the card table.
+ For now this is sufficient. */
+ switch (input) {
+ case TVAUDIO_INPUT_RADIO:
+ in = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
+ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
+ break;
+ case TVAUDIO_INPUT_EXTERN:
+ in = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1,
+ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
+ break;
+ case TVAUDIO_INPUT_INTERN:
+ /* Yes, this is the same input as for RADIO. I doubt
+ if this is ever used. The only board with an INTERN
+ input is the BTTV_BOARD_AVERMEDIA98. I wonder how
+ that was tested. My guess is that the whole INTERN
+ input does not work. */
+ in = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
+ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
+ break;
+ case TVAUDIO_INPUT_TUNER:
+ default:
+ /* This is the only card that uses TUNER2, and afaik,
+ is the only difference between the VOODOOTV_FM
+ and VOODOOTV_200 */
+ if (btv->c.type == BTTV_BOARD_VOODOOTV_200)
+ in = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER2, \
+ MSP_DSP_IN_TUNER, MSP_DSP_IN_TUNER);
+ else
+ in = MSP_INPUT_DEFAULT;
+ break;
+ }
+ v4l2_subdev_call(btv->sd_msp34xx, audio, s_routing,
+ in, MSP_OUTPUT_DEFAULT, 0);
+ }
+ if (btv->sd_tvaudio) {
+ v4l2_subdev_call(btv->sd_tvaudio, audio, s_routing,
+ input, 0, 0);
+ }
+ return 0;
+}
+
+static inline int
+audio_mute(struct bttv *btv, int mute)
+{
+ return audio_mux(btv, btv->audio, mute);
+}
+
+static inline int
+audio_input(struct bttv *btv, int input)
+{
+ return audio_mux(btv, input, btv->mute);
+}
+
+static void
+bttv_crop_calc_limits(struct bttv_crop *c)
+{
+ /* Scale factor min. 1:1, max. 16:1. Min. image size
+ 48 x 32. Scaled width must be a multiple of 4. */
+
+ if (1) {
+ /* For bug compatibility with VIDIOCGCAP and image
+ size checks in earlier driver versions. */
+ c->min_scaled_width = 48;
+ c->min_scaled_height = 32;
+ } else {
+ c->min_scaled_width =
+ (max(48, c->rect.width >> 4) + 3) & ~3;
+ c->min_scaled_height =
+ max(32, c->rect.height >> 4);
+ }
+
+ c->max_scaled_width = c->rect.width & ~3;
+ c->max_scaled_height = c->rect.height;
+}
+
+static void
+bttv_crop_reset(struct bttv_crop *c, unsigned int norm)
+{
+ c->rect = bttv_tvnorms[norm].cropcap.defrect;
+ bttv_crop_calc_limits(c);
+}
+
+/* Call with btv->lock down. */
+static int
+set_tvnorm(struct bttv *btv, unsigned int norm)
+{
+ const struct bttv_tvnorm *tvnorm;
+ v4l2_std_id id;
+
+ BUG_ON(norm >= BTTV_TVNORMS);
+ BUG_ON(btv->tvnorm >= BTTV_TVNORMS);
+
+ tvnorm = &bttv_tvnorms[norm];
+
+ if (memcmp(&bttv_tvnorms[btv->tvnorm].cropcap, &tvnorm->cropcap,
+ sizeof (tvnorm->cropcap))) {
+ bttv_crop_reset(&btv->crop[0], norm);
+ btv->crop[1] = btv->crop[0]; /* current = default */
+
+ if (0 == (btv->resources & VIDEO_RESOURCES)) {
+ btv->crop_start = tvnorm->cropcap.bounds.top
+ + tvnorm->cropcap.bounds.height;
+ }
+ }
+
+ btv->tvnorm = norm;
+
+ btwrite(tvnorm->adelay, BT848_ADELAY);
+ btwrite(tvnorm->bdelay, BT848_BDELAY);
+ btaor(tvnorm->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH),
+ BT848_IFORM);
+ btwrite(tvnorm->vbipack, BT848_VBI_PACK_SIZE);
+ btwrite(1, BT848_VBI_PACK_DEL);
+ bt848A_set_timing(btv);
+
+ switch (btv->c.type) {
+ case BTTV_BOARD_VOODOOTV_FM:
+ case BTTV_BOARD_VOODOOTV_200:
+ bttv_tda9880_setnorm(btv, gpio_read());
+ break;
+ }
+ id = tvnorm->v4l2_id;
+ bttv_call_all(btv, core, s_std, id);
+
+ return 0;
+}
+
+/* Call with btv->lock down. */
+static void
+set_input(struct bttv *btv, unsigned int input, unsigned int norm)
+{
+ unsigned long flags;
+
+ btv->input = input;
+ if (irq_iswitch) {
+ spin_lock_irqsave(&btv->s_lock,flags);
+ if (btv->curr.frame_irq) {
+ /* active capture -> delayed input switch */
+ btv->new_input = input;
+ } else {
+ video_mux(btv,input);
+ }
+ spin_unlock_irqrestore(&btv->s_lock,flags);
+ } else {
+ video_mux(btv,input);
+ }
+ audio_input(btv, (btv->tuner_type != TUNER_ABSENT && input == 0) ?
+ TVAUDIO_INPUT_TUNER : TVAUDIO_INPUT_EXTERN);
+ set_tvnorm(btv, norm);
+}
+
+static void init_irqreg(struct bttv *btv)
+{
+ /* clear status */
+ btwrite(0xfffffUL, BT848_INT_STAT);
+
+ if (bttv_tvcards[btv->c.type].no_video) {
+ /* i2c only */
+ btwrite(BT848_INT_I2CDONE,
+ BT848_INT_MASK);
+ } else {
+ /* full video */
+ btwrite((btv->triton1) |
+ (btv->gpioirq ? BT848_INT_GPINT : 0) |
+ BT848_INT_SCERR |
+ (fdsr ? BT848_INT_FDSR : 0) |
+ BT848_INT_RISCI | BT848_INT_OCERR |
+ BT848_INT_FMTCHG|BT848_INT_HLOCK|
+ BT848_INT_I2CDONE,
+ BT848_INT_MASK);
+ }
+}
+
+static void init_bt848(struct bttv *btv)
+{
+ int val;
+
+ if (bttv_tvcards[btv->c.type].no_video) {
+ /* very basic init only */
+ init_irqreg(btv);
+ return;
+ }
+
+ btwrite(0x00, BT848_CAP_CTL);
+ btwrite(BT848_COLOR_CTL_GAMMA, BT848_COLOR_CTL);
+ btwrite(BT848_IFORM_XTAUTO | BT848_IFORM_AUTO, BT848_IFORM);
+
+ /* set planar and packed mode trigger points and */
+ /* set rising edge of inverted GPINTR pin as irq trigger */
+ btwrite(BT848_GPIO_DMA_CTL_PKTP_32|
+ BT848_GPIO_DMA_CTL_PLTP1_16|
+ BT848_GPIO_DMA_CTL_PLTP23_16|
+ BT848_GPIO_DMA_CTL_GPINTC|
+ BT848_GPIO_DMA_CTL_GPINTI,
+ BT848_GPIO_DMA_CTL);
+
+ val = btv->opt_chroma_agc ? BT848_SCLOOP_CAGC : 0;
+ btwrite(val, BT848_E_SCLOOP);
+ btwrite(val, BT848_O_SCLOOP);
+
+ btwrite(0x20, BT848_E_VSCALE_HI);
+ btwrite(0x20, BT848_O_VSCALE_HI);
+ btwrite(BT848_ADC_RESERVED | (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0),
+ BT848_ADC);
+
+ btwrite(whitecrush_upper, BT848_WC_UP);
+ btwrite(whitecrush_lower, BT848_WC_DOWN);
+
+ if (btv->opt_lumafilter) {
+ btwrite(0, BT848_E_CONTROL);
+ btwrite(0, BT848_O_CONTROL);
+ } else {
+ btwrite(BT848_CONTROL_LDEC, BT848_E_CONTROL);
+ btwrite(BT848_CONTROL_LDEC, BT848_O_CONTROL);
+ }
+
+ bt848_bright(btv, btv->bright);
+ bt848_hue(btv, btv->hue);
+ bt848_contrast(btv, btv->contrast);
+ bt848_sat(btv, btv->saturation);
+
+ /* interrupt */
+ init_irqreg(btv);
+}
+
+static void bttv_reinit_bt848(struct bttv *btv)
+{
+ unsigned long flags;
+
+ if (bttv_verbose)
+ pr_info("%d: reset, reinitialize\n", btv->c.nr);
+ spin_lock_irqsave(&btv->s_lock,flags);
+ btv->errors=0;
+ bttv_set_dma(btv,0);
+ spin_unlock_irqrestore(&btv->s_lock,flags);
+
+ init_bt848(btv);
+ btv->pll.pll_current = -1;
+ set_input(btv, btv->input, btv->tvnorm);
+}
+
+static int bttv_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *c)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ c->value = btv->bright;
+ break;
+ case V4L2_CID_HUE:
+ c->value = btv->hue;
+ break;
+ case V4L2_CID_CONTRAST:
+ c->value = btv->contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ c->value = btv->saturation;
+ break;
+
+ case V4L2_CID_AUDIO_MUTE:
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ bttv_call_all(btv, core, g_ctrl, c);
+ break;
+
+ case V4L2_CID_PRIVATE_CHROMA_AGC:
+ c->value = btv->opt_chroma_agc;
+ break;
+ case V4L2_CID_PRIVATE_COMBFILTER:
+ c->value = btv->opt_combfilter;
+ break;
+ case V4L2_CID_PRIVATE_LUMAFILTER:
+ c->value = btv->opt_lumafilter;
+ break;
+ case V4L2_CID_PRIVATE_AUTOMUTE:
+ c->value = btv->opt_automute;
+ break;
+ case V4L2_CID_PRIVATE_AGC_CRUSH:
+ c->value = btv->opt_adc_crush;
+ break;
+ case V4L2_CID_PRIVATE_VCR_HACK:
+ c->value = btv->opt_vcr_hack;
+ break;
+ case V4L2_CID_PRIVATE_WHITECRUSH_UPPER:
+ c->value = btv->opt_whitecrush_upper;
+ break;
+ case V4L2_CID_PRIVATE_WHITECRUSH_LOWER:
+ c->value = btv->opt_whitecrush_lower;
+ break;
+ case V4L2_CID_PRIVATE_UV_RATIO:
+ c->value = btv->opt_uv_ratio;
+ break;
+ case V4L2_CID_PRIVATE_FULL_LUMA_RANGE:
+ c->value = btv->opt_full_luma_range;
+ break;
+ case V4L2_CID_PRIVATE_CORING:
+ c->value = btv->opt_coring;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int bttv_s_ctrl(struct file *file, void *f,
+ struct v4l2_control *c)
+{
+ int err;
+ int val;
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ err = v4l2_prio_check(&btv->prio, fh->prio);
+ if (0 != err)
+ return err;
+
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ bt848_bright(btv, c->value);
+ break;
+ case V4L2_CID_HUE:
+ bt848_hue(btv, c->value);
+ break;
+ case V4L2_CID_CONTRAST:
+ bt848_contrast(btv, c->value);
+ break;
+ case V4L2_CID_SATURATION:
+ bt848_sat(btv, c->value);
+ break;
+ case V4L2_CID_AUDIO_MUTE:
+ audio_mute(btv, c->value);
+ /* fall through */
+ case V4L2_CID_AUDIO_VOLUME:
+ if (btv->volume_gpio)
+ btv->volume_gpio(btv, c->value);
+
+ bttv_call_all(btv, core, s_ctrl, c);
+ break;
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ bttv_call_all(btv, core, s_ctrl, c);
+ break;
+
+ case V4L2_CID_PRIVATE_CHROMA_AGC:
+ btv->opt_chroma_agc = c->value;
+ val = btv->opt_chroma_agc ? BT848_SCLOOP_CAGC : 0;
+ btwrite(val, BT848_E_SCLOOP);
+ btwrite(val, BT848_O_SCLOOP);
+ break;
+ case V4L2_CID_PRIVATE_COMBFILTER:
+ btv->opt_combfilter = c->value;
+ break;
+ case V4L2_CID_PRIVATE_LUMAFILTER:
+ btv->opt_lumafilter = c->value;
+ if (btv->opt_lumafilter) {
+ btand(~BT848_CONTROL_LDEC, BT848_E_CONTROL);
+ btand(~BT848_CONTROL_LDEC, BT848_O_CONTROL);
+ } else {
+ btor(BT848_CONTROL_LDEC, BT848_E_CONTROL);
+ btor(BT848_CONTROL_LDEC, BT848_O_CONTROL);
+ }
+ break;
+ case V4L2_CID_PRIVATE_AUTOMUTE:
+ btv->opt_automute = c->value;
+ break;
+ case V4L2_CID_PRIVATE_AGC_CRUSH:
+ btv->opt_adc_crush = c->value;
+ btwrite(BT848_ADC_RESERVED |
+ (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0),
+ BT848_ADC);
+ break;
+ case V4L2_CID_PRIVATE_VCR_HACK:
+ btv->opt_vcr_hack = c->value;
+ break;
+ case V4L2_CID_PRIVATE_WHITECRUSH_UPPER:
+ btv->opt_whitecrush_upper = c->value;
+ btwrite(c->value, BT848_WC_UP);
+ break;
+ case V4L2_CID_PRIVATE_WHITECRUSH_LOWER:
+ btv->opt_whitecrush_lower = c->value;
+ btwrite(c->value, BT848_WC_DOWN);
+ break;
+ case V4L2_CID_PRIVATE_UV_RATIO:
+ btv->opt_uv_ratio = c->value;
+ bt848_sat(btv, btv->saturation);
+ break;
+ case V4L2_CID_PRIVATE_FULL_LUMA_RANGE:
+ btv->opt_full_luma_range = c->value;
+ btaor((c->value<<7), ~BT848_OFORM_RANGE, BT848_OFORM);
+ break;
+ case V4L2_CID_PRIVATE_CORING:
+ btv->opt_coring = c->value;
+ btaor((c->value<<5), ~BT848_OFORM_CORE32, BT848_OFORM);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+void bttv_gpio_tracking(struct bttv *btv, char *comment)
+{
+ unsigned int outbits, data;
+ outbits = btread(BT848_GPIO_OUT_EN);
+ data = btread(BT848_GPIO_DATA);
+ pr_debug("%d: gpio: en=%08x, out=%08x in=%08x [%s]\n",
+ btv->c.nr, outbits, data & outbits, data & ~outbits, comment);
+}
+
+static void bttv_field_count(struct bttv *btv)
+{
+ int need_count = 0;
+
+ if (btv->users)
+ need_count++;
+
+ if (need_count) {
+ /* start field counter */
+ btor(BT848_INT_VSYNC,BT848_INT_MASK);
+ } else {
+ /* stop field counter */
+ btand(~BT848_INT_VSYNC,BT848_INT_MASK);
+ btv->field_count = 0;
+ }
+}
+
+static const struct bttv_format*
+format_by_fourcc(int fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < FORMATS; i++) {
+ if (-1 == formats[i].fourcc)
+ continue;
+ if (formats[i].fourcc == fourcc)
+ return formats+i;
+ }
+ return NULL;
+}
+
+/* ----------------------------------------------------------------------- */
+/* misc helpers */
+
+static int
+bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh,
+ struct bttv_buffer *new)
+{
+ struct bttv_buffer *old;
+ unsigned long flags;
+ int retval = 0;
+
+ dprintk("switch_overlay: enter [new=%p]\n", new);
+ if (new)
+ new->vb.state = VIDEOBUF_DONE;
+ spin_lock_irqsave(&btv->s_lock,flags);
+ old = btv->screen;
+ btv->screen = new;
+ btv->loop_irq |= 1;
+ bttv_set_dma(btv, 0x03);
+ spin_unlock_irqrestore(&btv->s_lock,flags);
+ if (NULL != old) {
+ dprintk("switch_overlay: old=%p state is %d\n",
+ old, old->vb.state);
+ bttv_dma_free(&fh->cap,btv, old);
+ kfree(old);
+ }
+ if (NULL == new)
+ free_btres_lock(btv,fh,RESOURCE_OVERLAY);
+ dprintk("switch_overlay: done\n");
+ return retval;
+}
+
+/* ----------------------------------------------------------------------- */
+/* video4linux (1) interface */
+
+static int bttv_prepare_buffer(struct videobuf_queue *q,struct bttv *btv,
+ struct bttv_buffer *buf,
+ const struct bttv_format *fmt,
+ unsigned int width, unsigned int height,
+ enum v4l2_field field)
+{
+ struct bttv_fh *fh = q->priv_data;
+ int redo_dma_risc = 0;
+ struct bttv_crop c;
+ int norm;
+ int rc;
+
+ /* check settings */
+ if (NULL == fmt)
+ return -EINVAL;
+ if (fmt->btformat == BT848_COLOR_FMT_RAW) {
+ width = RAW_BPL;
+ height = RAW_LINES*2;
+ if (width*height > buf->vb.bsize)
+ return -EINVAL;
+ buf->vb.size = buf->vb.bsize;
+
+ /* Make sure tvnorm and vbi_end remain consistent
+ until we're done. */
+
+ norm = btv->tvnorm;
+
+ /* In this mode capturing always starts at defrect.top
+ (default VDELAY), ignoring cropping parameters. */
+ if (btv->vbi_end > bttv_tvnorms[norm].cropcap.defrect.top) {
+ return -EINVAL;
+ }
+
+ c.rect = bttv_tvnorms[norm].cropcap.defrect;
+ } else {
+ norm = btv->tvnorm;
+ c = btv->crop[!!fh->do_crop];
+
+ if (width < c.min_scaled_width ||
+ width > c.max_scaled_width ||
+ height < c.min_scaled_height)
+ return -EINVAL;
+
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ case V4L2_FIELD_ALTERNATE:
+ /* btv->crop counts frame lines. Max. scale
+ factor is 16:1 for frames, 8:1 for fields. */
+ if (height * 2 > c.max_scaled_height)
+ return -EINVAL;
+ break;
+
+ default:
+ if (height > c.max_scaled_height)
+ return -EINVAL;
+ break;
+ }
+
+ buf->vb.size = (width * height * fmt->depth) >> 3;
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+ }
+
+ /* alloc + fill struct bttv_buffer (if changed) */
+ if (buf->vb.width != width || buf->vb.height != height ||
+ buf->vb.field != field ||
+ buf->tvnorm != norm || buf->fmt != fmt ||
+ buf->crop.top != c.rect.top ||
+ buf->crop.left != c.rect.left ||
+ buf->crop.width != c.rect.width ||
+ buf->crop.height != c.rect.height) {
+ buf->vb.width = width;
+ buf->vb.height = height;
+ buf->vb.field = field;
+ buf->tvnorm = norm;
+ buf->fmt = fmt;
+ buf->crop = c.rect;
+ redo_dma_risc = 1;
+ }
+
+ /* alloc risc memory */
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ redo_dma_risc = 1;
+ if (0 != (rc = videobuf_iolock(q,&buf->vb,&btv->fbuf)))
+ goto fail;
+ }
+
+ if (redo_dma_risc)
+ if (0 != (rc = bttv_buffer_risc(btv,buf)))
+ goto fail;
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+ fail:
+ bttv_dma_free(q,btv,buf);
+ return rc;
+}
+
+static int
+buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+ struct bttv_fh *fh = q->priv_data;
+
+ *size = fh->fmt->depth*fh->width*fh->height >> 3;
+ if (0 == *count)
+ *count = gbuffers;
+ if (*size * *count > gbuffers * gbufsize)
+ *count = (gbuffers * gbufsize) / *size;
+ return 0;
+}
+
+static int
+buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
+ struct bttv_fh *fh = q->priv_data;
+
+ return bttv_prepare_buffer(q,fh->btv, buf, fh->fmt,
+ fh->width, fh->height, field);
+}
+
+static void
+buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
+ struct bttv_fh *fh = q->priv_data;
+ struct bttv *btv = fh->btv;
+
+ buf->vb.state = VIDEOBUF_QUEUED;
+ list_add_tail(&buf->vb.queue,&btv->capture);
+ if (!btv->curr.frame_irq) {
+ btv->loop_irq |= 1;
+ bttv_set_dma(btv, 0x03);
+ }
+}
+
+static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
+ struct bttv_fh *fh = q->priv_data;
+
+ bttv_dma_free(q,fh->btv,buf);
+}
+
+static struct videobuf_queue_ops bttv_video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+static int bttv_s_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ unsigned int i;
+ int err;
+
+ err = v4l2_prio_check(&btv->prio, fh->prio);
+ if (err)
+ goto err;
+
+ for (i = 0; i < BTTV_TVNORMS; i++)
+ if (*id & bttv_tvnorms[i].v4l2_id)
+ break;
+ if (i == BTTV_TVNORMS) {
+ err = -EINVAL;
+ goto err;
+ }
+
+ set_tvnorm(btv, i);
+
+err:
+
+ return err;
+}
+
+static int bttv_querystd(struct file *file, void *f, v4l2_std_id *id)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ if (btread(BT848_DSTATUS) & BT848_DSTATUS_NUML)
+ *id = V4L2_STD_625_50;
+ else
+ *id = V4L2_STD_525_60;
+ return 0;
+}
+
+static int bttv_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int rc = 0;
+
+ if (i->index >= bttv_tvcards[btv->c.type].video_inputs) {
+ rc = -EINVAL;
+ goto err;
+ }
+
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ i->audioset = 1;
+
+ if (btv->tuner_type != TUNER_ABSENT && i->index == 0) {
+ sprintf(i->name, "Television");
+ i->type = V4L2_INPUT_TYPE_TUNER;
+ i->tuner = 0;
+ } else if (i->index == btv->svhs) {
+ sprintf(i->name, "S-Video");
+ } else {
+ sprintf(i->name, "Composite%d", i->index);
+ }
+
+ if (i->index == btv->input) {
+ __u32 dstatus = btread(BT848_DSTATUS);
+ if (0 == (dstatus & BT848_DSTATUS_PRES))
+ i->status |= V4L2_IN_ST_NO_SIGNAL;
+ if (0 == (dstatus & BT848_DSTATUS_HLOC))
+ i->status |= V4L2_IN_ST_NO_H_LOCK;
+ }
+
+ i->std = BTTV_NORMS;
+
+err:
+
+ return rc;
+}
+
+static int bttv_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ *i = btv->input;
+
+ return 0;
+}
+
+static int bttv_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ int err;
+
+ err = v4l2_prio_check(&btv->prio, fh->prio);
+ if (unlikely(err))
+ goto err;
+
+ if (i > bttv_tvcards[btv->c.type].video_inputs) {
+ err = -EINVAL;
+ goto err;
+ }
+
+ set_input(btv, i, btv->tvnorm);
+
+err:
+ return 0;
+}
+
+static int bttv_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int err;
+
+ if (unlikely(0 != t->index))
+ return -EINVAL;
+
+ if (unlikely(btv->tuner_type == TUNER_ABSENT)) {
+ err = -EINVAL;
+ goto err;
+ }
+
+ err = v4l2_prio_check(&btv->prio, fh->prio);
+ if (unlikely(err))
+ goto err;
+
+ bttv_call_all(btv, tuner, s_tuner, t);
+
+ if (btv->audio_mode_gpio)
+ btv->audio_mode_gpio(btv, t, 1);
+
+err:
+
+ return 0;
+}
+
+static int bttv_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ f->type = btv->radio_user ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ f->frequency = btv->freq;
+
+ return 0;
+}
+
+static int bttv_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int err;
+
+ if (unlikely(f->tuner != 0))
+ return -EINVAL;
+
+ err = v4l2_prio_check(&btv->prio, fh->prio);
+ if (unlikely(err))
+ goto err;
+
+ if (unlikely(f->type != (btv->radio_user
+ ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV))) {
+ err = -EINVAL;
+ goto err;
+ }
+ btv->freq = f->frequency;
+ bttv_call_all(btv, tuner, s_frequency, f);
+ if (btv->has_matchbox && btv->radio_user)
+ tea5757_set_freq(btv, btv->freq);
+err:
+
+ return 0;
+}
+
+static int bttv_log_status(struct file *file, void *f)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ bttv_call_all(btv, core, log_status);
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int bttv_g_register(struct file *file, void *f,
+ struct v4l2_dbg_register *reg)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+
+ /* bt848 has a 12-bit register space */
+ reg->reg &= 0xfff;
+ reg->val = btread(reg->reg);
+ reg->size = 1;
+
+ return 0;
+}
+
+static int bttv_s_register(struct file *file, void *f,
+ struct v4l2_dbg_register *reg)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+
+ /* bt848 has a 12-bit register space */
+ reg->reg &= 0xfff;
+ btwrite(reg->val, reg->reg);
+
+ return 0;
+}
+#endif
+
+/* Given cropping boundaries b and the scaled width and height of a
+ single field or frame, which must not exceed hardware limits, this
+ function adjusts the cropping parameters c. */
+static void
+bttv_crop_adjust (struct bttv_crop * c,
+ const struct v4l2_rect * b,
+ __s32 width,
+ __s32 height,
+ enum v4l2_field field)
+{
+ __s32 frame_height = height << !V4L2_FIELD_HAS_BOTH(field);
+ __s32 max_left;
+ __s32 max_top;
+
+ if (width < c->min_scaled_width) {
+ /* Max. hor. scale factor 16:1. */
+ c->rect.width = width * 16;
+ } else if (width > c->max_scaled_width) {
+ /* Min. hor. scale factor 1:1. */
+ c->rect.width = width;
+
+ max_left = b->left + b->width - width;
+ max_left = min(max_left, (__s32) MAX_HDELAY);
+ if (c->rect.left > max_left)
+ c->rect.left = max_left;
+ }
+
+ if (height < c->min_scaled_height) {
+ /* Max. vert. scale factor 16:1, single fields 8:1. */
+ c->rect.height = height * 16;
+ } else if (frame_height > c->max_scaled_height) {
+ /* Min. vert. scale factor 1:1.
+ Top and height count field lines times two. */
+ c->rect.height = (frame_height + 1) & ~1;
+
+ max_top = b->top + b->height - c->rect.height;
+ if (c->rect.top > max_top)
+ c->rect.top = max_top;
+ }
+
+ bttv_crop_calc_limits(c);
+}
+
+/* Returns an error if scaling to a frame or single field with the given
+ width and height is not possible with the current cropping parameters
+ and width aligned according to width_mask. If adjust_size is TRUE the
+ function may adjust the width and/or height instead, rounding width
+ to (width + width_bias) & width_mask. If adjust_crop is TRUE it may
+ also adjust the current cropping parameters to get closer to the
+ desired image size. */
+static int
+limit_scaled_size_lock (struct bttv_fh * fh,
+ __s32 * width,
+ __s32 * height,
+ enum v4l2_field field,
+ unsigned int width_mask,
+ unsigned int width_bias,
+ int adjust_size,
+ int adjust_crop)
+{
+ struct bttv *btv = fh->btv;
+ const struct v4l2_rect *b;
+ struct bttv_crop *c;
+ __s32 min_width;
+ __s32 min_height;
+ __s32 max_width;
+ __s32 max_height;
+ int rc;
+
+ BUG_ON((int) width_mask >= 0 ||
+ width_bias >= (unsigned int) -width_mask);
+
+ /* Make sure tvnorm, vbi_end and the current cropping parameters
+ remain consistent until we're done. */
+
+ b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds;
+
+ /* Do crop - use current, don't - use default parameters. */
+ c = &btv->crop[!!fh->do_crop];
+
+ if (fh->do_crop
+ && adjust_size
+ && adjust_crop
+ && !locked_btres(btv, VIDEO_RESOURCES)) {
+ min_width = 48;
+ min_height = 32;
+
+ /* We cannot scale up. When the scaled image is larger
+ than crop.rect we adjust the crop.rect as required
+ by the V4L2 spec, hence cropcap.bounds are our limit. */
+ max_width = min(b->width, (__s32) MAX_HACTIVE);
+ max_height = b->height;
+
+ /* We cannot capture the same line as video and VBI data.
+ Note btv->vbi_end is really a minimum, see
+ bttv_vbi_try_fmt(). */
+ if (btv->vbi_end > b->top) {
+ max_height -= btv->vbi_end - b->top;
+ rc = -EBUSY;
+ if (min_height > max_height)
+ goto fail;
+ }
+ } else {
+ rc = -EBUSY;
+ if (btv->vbi_end > c->rect.top)
+ goto fail;
+
+ min_width = c->min_scaled_width;
+ min_height = c->min_scaled_height;
+ max_width = c->max_scaled_width;
+ max_height = c->max_scaled_height;
+
+ adjust_crop = 0;
+ }
+
+ min_width = (min_width - width_mask - 1) & width_mask;
+ max_width = max_width & width_mask;
+
+ /* Max. scale factor is 16:1 for frames, 8:1 for fields. */
+ min_height = min_height;
+ /* Min. scale factor is 1:1. */
+ max_height >>= !V4L2_FIELD_HAS_BOTH(field);
+
+ if (adjust_size) {
+ *width = clamp(*width, min_width, max_width);
+ *height = clamp(*height, min_height, max_height);
+
+ /* Round after clamping to avoid overflow. */
+ *width = (*width + width_bias) & width_mask;
+
+ if (adjust_crop) {
+ bttv_crop_adjust(c, b, *width, *height, field);
+
+ if (btv->vbi_end > c->rect.top) {
+ /* Move the crop window out of the way. */
+ c->rect.top = btv->vbi_end;
+ }
+ }
+ } else {
+ rc = -EINVAL;
+ if (*width < min_width ||
+ *height < min_height ||
+ *width > max_width ||
+ *height > max_height ||
+ 0 != (*width & ~width_mask))
+ goto fail;
+ }
+
+ rc = 0; /* success */
+
+ fail:
+
+ return rc;
+}
+
+/* Returns an error if the given overlay window dimensions are not
+ possible with the current cropping parameters. If adjust_size is
+ TRUE the function may adjust the window width and/or height
+ instead, however it always rounds the horizontal position and
+ width as btcx_align() does. If adjust_crop is TRUE the function
+ may also adjust the current cropping parameters to get closer
+ to the desired window size. */
+static int
+verify_window_lock (struct bttv_fh * fh,
+ struct v4l2_window * win,
+ int adjust_size,
+ int adjust_crop)
+{
+ enum v4l2_field field;
+ unsigned int width_mask;
+ int rc;
+
+ if (win->w.width < 48 || win->w.height < 32)
+ return -EINVAL;
+ if (win->clipcount > 2048)
+ return -EINVAL;
+
+ field = win->field;
+
+ if (V4L2_FIELD_ANY == field) {
+ __s32 height2;
+
+ height2 = fh->btv->crop[!!fh->do_crop].rect.height >> 1;
+ field = (win->w.height > height2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_TOP;
+ }
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* 4-byte alignment. */
+ if (NULL == fh->ovfmt)
+ return -EINVAL;
+ width_mask = ~0;
+ switch (fh->ovfmt->depth) {
+ case 8:
+ case 24:
+ width_mask = ~3;
+ break;
+ case 16:
+ width_mask = ~1;
+ break;
+ case 32:
+ break;
+ default:
+ BUG();
+ }
+
+ win->w.width -= win->w.left & ~width_mask;
+ win->w.left = (win->w.left - width_mask - 1) & width_mask;
+
+ rc = limit_scaled_size_lock(fh, &win->w.width, &win->w.height,
+ field, width_mask,
+ /* width_bias: round down */ 0,
+ adjust_size, adjust_crop);
+ if (0 != rc)
+ return rc;
+
+ win->field = field;
+ return 0;
+}
+
+static int setup_window_lock(struct bttv_fh *fh, struct bttv *btv,
+ struct v4l2_window *win, int fixup)
+{
+ struct v4l2_clip *clips = NULL;
+ int n,size,retval = 0;
+
+ if (NULL == fh->ovfmt)
+ return -EINVAL;
+ if (!(fh->ovfmt->flags & FORMAT_FLAGS_PACKED))
+ return -EINVAL;
+ retval = verify_window_lock(fh, win,
+ /* adjust_size */ fixup,
+ /* adjust_crop */ fixup);
+ if (0 != retval)
+ return retval;
+
+ /* copy clips -- luckily v4l1 + v4l2 are binary
+ compatible here ...*/
+ n = win->clipcount;
+ size = sizeof(*clips)*(n+4);
+ clips = kmalloc(size,GFP_KERNEL);
+ if (NULL == clips)
+ return -ENOMEM;
+ if (n > 0) {
+ if (copy_from_user(clips,win->clips,sizeof(struct v4l2_clip)*n)) {
+ kfree(clips);
+ return -EFAULT;
+ }
+ }
+
+ /* clip against screen */
+ if (NULL != btv->fbuf.base)
+ n = btcx_screen_clips(btv->fbuf.fmt.width, btv->fbuf.fmt.height,
+ &win->w, clips, n);
+ btcx_sort_clips(clips,n);
+
+ /* 4-byte alignments */
+ switch (fh->ovfmt->depth) {
+ case 8:
+ case 24:
+ btcx_align(&win->w, clips, n, 3);
+ break;
+ case 16:
+ btcx_align(&win->w, clips, n, 1);
+ break;
+ case 32:
+ /* no alignment fixups needed */
+ break;
+ default:
+ BUG();
+ }
+
+ kfree(fh->ov.clips);
+ fh->ov.clips = clips;
+ fh->ov.nclips = n;
+
+ fh->ov.w = win->w;
+ fh->ov.field = win->field;
+ fh->ov.setup_ok = 1;
+
+ btv->init.ov.w.width = win->w.width;
+ btv->init.ov.w.height = win->w.height;
+ btv->init.ov.field = win->field;
+
+ /* update overlay if needed */
+ retval = 0;
+ if (check_btres(fh, RESOURCE_OVERLAY)) {
+ struct bttv_buffer *new;
+
+ new = videobuf_sg_alloc(sizeof(*new));
+ new->crop = btv->crop[!!fh->do_crop].rect;
+ bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
+ retval = bttv_switch_overlay(btv,fh,new);
+ }
+ return retval;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct videobuf_queue* bttv_queue(struct bttv_fh *fh)
+{
+ struct videobuf_queue* q = NULL;
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ q = &fh->cap;
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ q = &fh->vbi;
+ break;
+ default:
+ BUG();
+ }
+ return q;
+}
+
+static int bttv_resource(struct bttv_fh *fh)
+{
+ int res = 0;
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ res = RESOURCE_VIDEO_STREAM;
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ res = RESOURCE_VBI;
+ break;
+ default:
+ BUG();
+ }
+ return res;
+}
+
+static int bttv_switch_type(struct bttv_fh *fh, enum v4l2_buf_type type)
+{
+ struct videobuf_queue *q = bttv_queue(fh);
+ int res = bttv_resource(fh);
+
+ if (check_btres(fh,res))
+ return -EBUSY;
+ if (videobuf_queue_is_busy(q))
+ return -EBUSY;
+ fh->type = type;
+ return 0;
+}
+
+static void
+pix_format_set_size (struct v4l2_pix_format * f,
+ const struct bttv_format * fmt,
+ unsigned int width,
+ unsigned int height)
+{
+ f->width = width;
+ f->height = height;
+
+ if (fmt->flags & FORMAT_FLAGS_PLANAR) {
+ f->bytesperline = width; /* Y plane */
+ f->sizeimage = (width * height * fmt->depth) >> 3;
+ } else {
+ f->bytesperline = (width * fmt->depth) >> 3;
+ f->sizeimage = height * f->bytesperline;
+ }
+}
+
+static int bttv_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct bttv_fh *fh = priv;
+
+ pix_format_set_size(&f->fmt.pix, fh->fmt,
+ fh->width, fh->height);
+ f->fmt.pix.field = fh->cap.field;
+ f->fmt.pix.pixelformat = fh->fmt->fourcc;
+
+ return 0;
+}
+
+static int bttv_g_fmt_vid_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct bttv_fh *fh = priv;
+
+ f->fmt.win.w = fh->ov.w;
+ f->fmt.win.field = fh->ov.field;
+
+ return 0;
+}
+
+static int bttv_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ const struct bttv_format *fmt;
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ enum v4l2_field field;
+ __s32 width, height;
+ int rc;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+
+ field = f->fmt.pix.field;
+
+ if (V4L2_FIELD_ANY == field) {
+ __s32 height2;
+
+ height2 = btv->crop[!!fh->do_crop].rect.height >> 1;
+ field = (f->fmt.pix.height > height2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_BOTTOM;
+ }
+
+ if (V4L2_FIELD_SEQ_BT == field)
+ field = V4L2_FIELD_SEQ_TB;
+
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ case V4L2_FIELD_ALTERNATE:
+ case V4L2_FIELD_INTERLACED:
+ break;
+ case V4L2_FIELD_SEQ_TB:
+ if (fmt->flags & FORMAT_FLAGS_PLANAR)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ width = f->fmt.pix.width;
+ height = f->fmt.pix.height;
+
+ rc = limit_scaled_size_lock(fh, &width, &height, field,
+ /* width_mask: 4 pixels */ ~3,
+ /* width_bias: nearest */ 2,
+ /* adjust_size */ 1,
+ /* adjust_crop */ 0);
+ if (0 != rc)
+ return rc;
+
+ /* update data for the application */
+ f->fmt.pix.field = field;
+ pix_format_set_size(&f->fmt.pix, fmt, width, height);
+
+ return 0;
+}
+
+static int bttv_try_fmt_vid_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct bttv_fh *fh = priv;
+
+ return verify_window_lock(fh, &f->fmt.win,
+ /* adjust_size */ 1,
+ /* adjust_crop */ 0);
+}
+
+static int bttv_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ int retval;
+ const struct bttv_format *fmt;
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ __s32 width, height;
+ enum v4l2_field field;
+
+ retval = bttv_switch_type(fh, f->type);
+ if (0 != retval)
+ return retval;
+
+ retval = bttv_try_fmt_vid_cap(file, priv, f);
+ if (0 != retval)
+ return retval;
+
+ width = f->fmt.pix.width;
+ height = f->fmt.pix.height;
+ field = f->fmt.pix.field;
+
+ retval = limit_scaled_size_lock(fh, &width, &height, f->fmt.pix.field,
+ /* width_mask: 4 pixels */ ~3,
+ /* width_bias: nearest */ 2,
+ /* adjust_size */ 1,
+ /* adjust_crop */ 1);
+ if (0 != retval)
+ return retval;
+
+ f->fmt.pix.field = field;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+
+ /* update our state informations */
+ fh->fmt = fmt;
+ fh->cap.field = f->fmt.pix.field;
+ fh->cap.last = V4L2_FIELD_NONE;
+ fh->width = f->fmt.pix.width;
+ fh->height = f->fmt.pix.height;
+ btv->init.fmt = fmt;
+ btv->init.width = f->fmt.pix.width;
+ btv->init.height = f->fmt.pix.height;
+
+ return 0;
+}
+
+static int bttv_s_fmt_vid_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ if (no_overlay > 0) {
+ pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+ return -EINVAL;
+ }
+
+ return setup_window_lock(fh, btv, &f->fmt.win, 1);
+}
+
+static int bttv_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ if (0 == v4l2)
+ return -EINVAL;
+
+ strlcpy(cap->driver, "bttv", sizeof(cap->driver));
+ strlcpy(cap->card, btv->video_dev->name, sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
+ "PCI:%s", pci_name(btv->c.pci));
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VBI_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+ if (no_overlay <= 0)
+ cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY;
+
+ /*
+ * No need to lock here: those vars are initialized during board
+ * probe and remains untouched during the rest of the driver lifecycle
+ */
+ if (btv->has_saa6588)
+ cap->capabilities |= V4L2_CAP_RDS_CAPTURE;
+ if (btv->tuner_type != TUNER_ABSENT)
+ cap->capabilities |= V4L2_CAP_TUNER;
+ return 0;
+}
+
+static int bttv_enum_fmt_cap_ovr(struct v4l2_fmtdesc *f)
+{
+ int index = -1, i;
+
+ for (i = 0; i < FORMATS; i++) {
+ if (formats[i].fourcc != -1)
+ index++;
+ if ((unsigned int)index == f->index)
+ break;
+ }
+ if (FORMATS == i)
+ return -EINVAL;
+
+ f->pixelformat = formats[i].fourcc;
+ strlcpy(f->description, formats[i].name, sizeof(f->description));
+
+ return i;
+}
+
+static int bttv_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ int rc = bttv_enum_fmt_cap_ovr(f);
+
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+static int bttv_enum_fmt_vid_overlay(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ int rc;
+
+ if (no_overlay > 0) {
+ pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+ return -EINVAL;
+ }
+
+ rc = bttv_enum_fmt_cap_ovr(f);
+
+ if (rc < 0)
+ return rc;
+
+ if (!(formats[rc].flags & FORMAT_FLAGS_PACKED))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int bttv_g_fbuf(struct file *file, void *f,
+ struct v4l2_framebuffer *fb)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ *fb = btv->fbuf;
+ fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+ if (fh->ovfmt)
+ fb->fmt.pixelformat = fh->ovfmt->fourcc;
+ return 0;
+}
+
+static int bttv_overlay(struct file *file, void *f, unsigned int on)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+ struct bttv_buffer *new;
+ int retval = 0;
+
+ if (on) {
+ /* verify args */
+ if (unlikely(!btv->fbuf.base)) {
+ return -EINVAL;
+ }
+ if (unlikely(!fh->ov.setup_ok)) {
+ dprintk("%d: overlay: !setup_ok\n", btv->c.nr);
+ retval = -EINVAL;
+ }
+ if (retval)
+ return retval;
+ }
+
+ if (!check_alloc_btres_lock(btv, fh, RESOURCE_OVERLAY))
+ return -EBUSY;
+
+ if (on) {
+ fh->ov.tvnorm = btv->tvnorm;
+ new = videobuf_sg_alloc(sizeof(*new));
+ new->crop = btv->crop[!!fh->do_crop].rect;
+ bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
+ } else {
+ new = NULL;
+ }
+
+ /* switch over */
+ retval = bttv_switch_overlay(btv, fh, new);
+ return retval;
+}
+
+static int bttv_s_fbuf(struct file *file, void *f,
+ struct v4l2_framebuffer *fb)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+ const struct bttv_format *fmt;
+ int retval;
+
+ if (!capable(CAP_SYS_ADMIN) &&
+ !capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ /* check args */
+ fmt = format_by_fourcc(fb->fmt.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+ if (0 == (fmt->flags & FORMAT_FLAGS_PACKED))
+ return -EINVAL;
+
+ retval = -EINVAL;
+ if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
+ __s32 width = fb->fmt.width;
+ __s32 height = fb->fmt.height;
+
+ retval = limit_scaled_size_lock(fh, &width, &height,
+ V4L2_FIELD_INTERLACED,
+ /* width_mask */ ~3,
+ /* width_bias */ 2,
+ /* adjust_size */ 0,
+ /* adjust_crop */ 0);
+ if (0 != retval)
+ return retval;
+ }
+
+ /* ok, accept it */
+ btv->fbuf.base = fb->base;
+ btv->fbuf.fmt.width = fb->fmt.width;
+ btv->fbuf.fmt.height = fb->fmt.height;
+ if (0 != fb->fmt.bytesperline)
+ btv->fbuf.fmt.bytesperline = fb->fmt.bytesperline;
+ else
+ btv->fbuf.fmt.bytesperline = btv->fbuf.fmt.width*fmt->depth/8;
+
+ retval = 0;
+ fh->ovfmt = fmt;
+ btv->init.ovfmt = fmt;
+ if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
+ fh->ov.w.left = 0;
+ fh->ov.w.top = 0;
+ fh->ov.w.width = fb->fmt.width;
+ fh->ov.w.height = fb->fmt.height;
+ btv->init.ov.w.width = fb->fmt.width;
+ btv->init.ov.w.height = fb->fmt.height;
+ kfree(fh->ov.clips);
+ fh->ov.clips = NULL;
+ fh->ov.nclips = 0;
+
+ if (check_btres(fh, RESOURCE_OVERLAY)) {
+ struct bttv_buffer *new;
+
+ new = videobuf_sg_alloc(sizeof(*new));
+ new->crop = btv->crop[!!fh->do_crop].rect;
+ bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
+ retval = bttv_switch_overlay(btv, fh, new);
+ }
+ }
+ return retval;
+}
+
+static int bttv_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct bttv_fh *fh = priv;
+ return videobuf_reqbufs(bttv_queue(fh), p);
+}
+
+static int bttv_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct bttv_fh *fh = priv;
+ return videobuf_querybuf(bttv_queue(fh), b);
+}
+
+static int bttv_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int res = bttv_resource(fh);
+
+ if (!check_alloc_btres_lock(btv, fh, res))
+ return -EBUSY;
+
+ return videobuf_qbuf(bttv_queue(fh), b);
+}
+
+static int bttv_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct bttv_fh *fh = priv;
+ return videobuf_dqbuf(bttv_queue(fh), b,
+ file->f_flags & O_NONBLOCK);
+}
+
+static int bttv_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int res = bttv_resource(fh);
+
+ if (!check_alloc_btres_lock(btv, fh, res))
+ return -EBUSY;
+ return videobuf_streamon(bttv_queue(fh));
+}
+
+
+static int bttv_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int retval;
+ int res = bttv_resource(fh);
+
+
+ retval = videobuf_streamoff(bttv_queue(fh));
+ if (retval < 0)
+ return retval;
+ free_btres_lock(btv, fh, res);
+ return 0;
+}
+
+static int bttv_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ const struct v4l2_queryctrl *ctrl;
+
+ if ((c->id < V4L2_CID_BASE ||
+ c->id >= V4L2_CID_LASTP1) &&
+ (c->id < V4L2_CID_PRIVATE_BASE ||
+ c->id >= V4L2_CID_PRIVATE_LASTP1))
+ return -EINVAL;
+
+ if (!btv->volume_gpio && (c->id == V4L2_CID_AUDIO_VOLUME))
+ *c = no_ctl;
+ else {
+ ctrl = ctrl_by_id(c->id);
+
+ *c = (NULL != ctrl) ? *ctrl : no_ctl;
+ }
+
+ return 0;
+}
+
+static int bttv_g_parm(struct file *file, void *f,
+ struct v4l2_streamparm *parm)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ v4l2_video_std_frame_period(bttv_tvnorms[btv->tvnorm].v4l2_id,
+ &parm->parm.capture.timeperframe);
+
+ return 0;
+}
+
+static int bttv_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ if (btv->tuner_type == TUNER_ABSENT)
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+
+ t->rxsubchans = V4L2_TUNER_SUB_MONO;
+ bttv_call_all(btv, tuner, g_tuner, t);
+ strcpy(t->name, "Television");
+ t->capability = V4L2_TUNER_CAP_NORM;
+ t->type = V4L2_TUNER_ANALOG_TV;
+ if (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)
+ t->signal = 0xffff;
+
+ if (btv->audio_mode_gpio)
+ btv->audio_mode_gpio(btv, t, 0);
+
+ return 0;
+}
+
+static int bttv_g_priority(struct file *file, void *f, enum v4l2_priority *p)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ *p = v4l2_prio_max(&btv->prio);
+
+ return 0;
+}
+
+static int bttv_s_priority(struct file *file, void *f,
+ enum v4l2_priority prio)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+ int rc;
+
+ rc = v4l2_prio_change(&btv->prio, &fh->prio, prio);
+
+ return rc;
+}
+
+static int bttv_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cap)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
+
+ *cap = bttv_tvnorms[btv->tvnorm].cropcap;
+
+ return 0;
+}
+
+static int bttv_g_crop(struct file *file, void *f, struct v4l2_crop *crop)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
+
+ /* No fh->do_crop = 1; because btv->crop[1] may be
+ inconsistent with fh->width or fh->height and apps
+ do not expect a change here. */
+
+ crop->c = btv->crop[!!fh->do_crop].rect;
+
+ return 0;
+}
+
+static int bttv_s_crop(struct file *file, void *f, struct v4l2_crop *crop)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+ const struct v4l2_rect *b;
+ int retval;
+ struct bttv_crop c;
+ __s32 b_left;
+ __s32 b_top;
+ __s32 b_right;
+ __s32 b_bottom;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
+
+ /* Make sure tvnorm, vbi_end and the current cropping
+ parameters remain consistent until we're done. Note
+ read() may change vbi_end in check_alloc_btres_lock(). */
+ retval = v4l2_prio_check(&btv->prio, fh->prio);
+ if (0 != retval) {
+ return retval;
+ }
+
+ retval = -EBUSY;
+
+ if (locked_btres(fh->btv, VIDEO_RESOURCES)) {
+ return retval;
+ }
+
+ b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds;
+
+ b_left = b->left;
+ b_right = b_left + b->width;
+ b_bottom = b->top + b->height;
+
+ b_top = max(b->top, btv->vbi_end);
+ if (b_top + 32 >= b_bottom) {
+ return retval;
+ }
+
+ /* Min. scaled size 48 x 32. */
+ c.rect.left = clamp(crop->c.left, b_left, b_right - 48);
+ c.rect.left = min(c.rect.left, (__s32) MAX_HDELAY);
+
+ c.rect.width = clamp(crop->c.width,
+ 48, b_right - c.rect.left);
+
+ c.rect.top = clamp(crop->c.top, b_top, b_bottom - 32);
+ /* Top and height must be a multiple of two. */
+ c.rect.top = (c.rect.top + 1) & ~1;
+
+ c.rect.height = clamp(crop->c.height,
+ 32, b_bottom - c.rect.top);
+ c.rect.height = (c.rect.height + 1) & ~1;
+
+ bttv_crop_calc_limits(&c);
+
+ btv->crop[1] = c;
+
+ fh->do_crop = 1;
+
+ if (fh->width < c.min_scaled_width) {
+ fh->width = c.min_scaled_width;
+ btv->init.width = c.min_scaled_width;
+ } else if (fh->width > c.max_scaled_width) {
+ fh->width = c.max_scaled_width;
+ btv->init.width = c.max_scaled_width;
+ }
+
+ if (fh->height < c.min_scaled_height) {
+ fh->height = c.min_scaled_height;
+ btv->init.height = c.min_scaled_height;
+ } else if (fh->height > c.max_scaled_height) {
+ fh->height = c.max_scaled_height;
+ btv->init.height = c.max_scaled_height;
+ }
+
+ return 0;
+}
+
+static int bttv_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ if (unlikely(a->index))
+ return -EINVAL;
+
+ strcpy(a->name, "audio");
+ return 0;
+}
+
+static int bttv_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ if (unlikely(a->index))
+ return -EINVAL;
+
+ return 0;
+}
+
+static ssize_t bttv_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct bttv_fh *fh = file->private_data;
+ int retval = 0;
+
+ if (fh->btv->errors)
+ bttv_reinit_bt848(fh->btv);
+ dprintk("%d: read count=%d type=%s\n",
+ fh->btv->c.nr, (int)count, v4l2_type_names[fh->type]);
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (!check_alloc_btres_lock(fh->btv, fh, RESOURCE_VIDEO_READ)) {
+ /* VIDEO_READ in use by another fh,
+ or VIDEO_STREAM by any fh. */
+ return -EBUSY;
+ }
+ retval = videobuf_read_one(&fh->cap, data, count, ppos,
+ file->f_flags & O_NONBLOCK);
+ free_btres_lock(fh->btv, fh, RESOURCE_VIDEO_READ);
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ if (!check_alloc_btres_lock(fh->btv,fh,RESOURCE_VBI))
+ return -EBUSY;
+ retval = videobuf_read_stream(&fh->vbi, data, count, ppos, 1,
+ file->f_flags & O_NONBLOCK);
+ break;
+ default:
+ BUG();
+ }
+ return retval;
+}
+
+static unsigned int bttv_poll(struct file *file, poll_table *wait)
+{
+ struct bttv_fh *fh = file->private_data;
+ struct bttv_buffer *buf;
+ enum v4l2_field field;
+ unsigned int rc = POLLERR;
+
+ if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
+ if (!check_alloc_btres_lock(fh->btv,fh,RESOURCE_VBI))
+ return POLLERR;
+ return videobuf_poll_stream(file, &fh->vbi, wait);
+ }
+
+ if (check_btres(fh,RESOURCE_VIDEO_STREAM)) {
+ /* streaming capture */
+ if (list_empty(&fh->cap.stream))
+ goto err;
+ buf = list_entry(fh->cap.stream.next,struct bttv_buffer,vb.stream);
+ } else {
+ /* read() capture */
+ if (NULL == fh->cap.read_buf) {
+ /* need to capture a new frame */
+ if (locked_btres(fh->btv,RESOURCE_VIDEO_STREAM))
+ goto err;
+ fh->cap.read_buf = videobuf_sg_alloc(fh->cap.msize);
+ if (NULL == fh->cap.read_buf)
+ goto err;
+ fh->cap.read_buf->memory = V4L2_MEMORY_USERPTR;
+ field = videobuf_next_field(&fh->cap);
+ if (0 != fh->cap.ops->buf_prepare(&fh->cap,fh->cap.read_buf,field)) {
+ kfree (fh->cap.read_buf);
+ fh->cap.read_buf = NULL;
+ goto err;
+ }
+ fh->cap.ops->buf_queue(&fh->cap,fh->cap.read_buf);
+ fh->cap.read_off = 0;
+ }
+ buf = (struct bttv_buffer*)fh->cap.read_buf;
+ }
+
+ poll_wait(file, &buf->vb.done, wait);
+ if (buf->vb.state == VIDEOBUF_DONE ||
+ buf->vb.state == VIDEOBUF_ERROR)
+ rc = POLLIN|POLLRDNORM;
+ else
+ rc = 0;
+err:
+ return rc;
+}
+
+static int bttv_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct bttv *btv = video_drvdata(file);
+ struct bttv_fh *fh;
+ enum v4l2_buf_type type = 0;
+
+ dprintk("open dev=%s\n", video_device_node_name(vdev));
+
+ if (vdev->vfl_type == VFL_TYPE_GRABBER) {
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ } else if (vdev->vfl_type == VFL_TYPE_VBI) {
+ type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ } else {
+ WARN_ON(1);
+ return -ENODEV;
+ }
+
+ dprintk("%d: open called (type=%s)\n",
+ btv->c.nr, v4l2_type_names[type]);
+
+ /* allocate per filehandle data */
+ fh = kmalloc(sizeof(*fh), GFP_KERNEL);
+ if (unlikely(!fh))
+ return -ENOMEM;
+ file->private_data = fh;
+
+ *fh = btv->init;
+
+ fh->type = type;
+ fh->ov.setup_ok = 0;
+
+ v4l2_prio_open(&btv->prio, &fh->prio);
+
+ videobuf_queue_sg_init(&fh->cap, &bttv_video_qops,
+ &btv->c.pci->dev, &btv->s_lock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct bttv_buffer),
+ fh, &btv->lock);
+ videobuf_queue_sg_init(&fh->vbi, &bttv_vbi_qops,
+ &btv->c.pci->dev, &btv->s_lock,
+ V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_FIELD_SEQ_TB,
+ sizeof(struct bttv_buffer),
+ fh, &btv->lock);
+ set_tvnorm(btv,btv->tvnorm);
+ set_input(btv, btv->input, btv->tvnorm);
+
+ btv->users++;
+
+ /* The V4L2 spec requires one global set of cropping parameters
+ which only change on request. These are stored in btv->crop[1].
+ However for compatibility with V4L apps and cropping unaware
+ V4L2 apps we now reset the cropping parameters as seen through
+ this fh, which is to say VIDIOC_G_CROP and scaling limit checks
+ will use btv->crop[0], the default cropping parameters for the
+ current video standard, and VIDIOC_S_FMT will not implicitely
+ change the cropping parameters until VIDIOC_S_CROP has been
+ called. */
+ fh->do_crop = !reset_crop; /* module parameter */
+
+ /* Likewise there should be one global set of VBI capture
+ parameters, but for compatibility with V4L apps and earlier
+ driver versions each fh has its own parameters. */
+ bttv_vbi_fmt_reset(&fh->vbi_fmt, btv->tvnorm);
+
+ bttv_field_count(btv);
+ return 0;
+}
+
+static int bttv_release(struct file *file)
+{
+ struct bttv_fh *fh = file->private_data;
+ struct bttv *btv = fh->btv;
+
+ /* turn off overlay */
+ if (check_btres(fh, RESOURCE_OVERLAY))
+ bttv_switch_overlay(btv,fh,NULL);
+
+ /* stop video capture */
+ if (check_btres(fh, RESOURCE_VIDEO_STREAM)) {
+ videobuf_streamoff(&fh->cap);
+ free_btres_lock(btv,fh,RESOURCE_VIDEO_STREAM);
+ }
+ if (fh->cap.read_buf) {
+ buffer_release(&fh->cap,fh->cap.read_buf);
+ kfree(fh->cap.read_buf);
+ }
+ if (check_btres(fh, RESOURCE_VIDEO_READ)) {
+ free_btres_lock(btv, fh, RESOURCE_VIDEO_READ);
+ }
+
+ /* stop vbi capture */
+ if (check_btres(fh, RESOURCE_VBI)) {
+ videobuf_stop(&fh->vbi);
+ free_btres_lock(btv,fh,RESOURCE_VBI);
+ }
+
+ /* free stuff */
+
+ videobuf_mmap_free(&fh->cap);
+ videobuf_mmap_free(&fh->vbi);
+ v4l2_prio_close(&btv->prio, fh->prio);
+ file->private_data = NULL;
+ kfree(fh);
+
+ btv->users--;
+ bttv_field_count(btv);
+
+ if (!btv->users)
+ audio_mute(btv, 1);
+
+ return 0;
+}
+
+static int
+bttv_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct bttv_fh *fh = file->private_data;
+
+ dprintk("%d: mmap type=%s 0x%lx+%ld\n",
+ fh->btv->c.nr, v4l2_type_names[fh->type],
+ vma->vm_start, vma->vm_end - vma->vm_start);
+ return videobuf_mmap_mapper(bttv_queue(fh),vma);
+}
+
+static const struct v4l2_file_operations bttv_fops =
+{
+ .owner = THIS_MODULE,
+ .open = bttv_open,
+ .release = bttv_release,
+ .unlocked_ioctl = video_ioctl2,
+ .read = bttv_read,
+ .mmap = bttv_mmap,
+ .poll = bttv_poll,
+};
+
+static const struct v4l2_ioctl_ops bttv_ioctl_ops = {
+ .vidioc_querycap = bttv_querycap,
+ .vidioc_enum_fmt_vid_cap = bttv_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = bttv_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = bttv_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = bttv_s_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_overlay = bttv_enum_fmt_vid_overlay,
+ .vidioc_g_fmt_vid_overlay = bttv_g_fmt_vid_overlay,
+ .vidioc_try_fmt_vid_overlay = bttv_try_fmt_vid_overlay,
+ .vidioc_s_fmt_vid_overlay = bttv_s_fmt_vid_overlay,
+ .vidioc_g_fmt_vbi_cap = bttv_g_fmt_vbi_cap,
+ .vidioc_try_fmt_vbi_cap = bttv_try_fmt_vbi_cap,
+ .vidioc_s_fmt_vbi_cap = bttv_s_fmt_vbi_cap,
+ .vidioc_g_audio = bttv_g_audio,
+ .vidioc_s_audio = bttv_s_audio,
+ .vidioc_cropcap = bttv_cropcap,
+ .vidioc_reqbufs = bttv_reqbufs,
+ .vidioc_querybuf = bttv_querybuf,
+ .vidioc_qbuf = bttv_qbuf,
+ .vidioc_dqbuf = bttv_dqbuf,
+ .vidioc_s_std = bttv_s_std,
+ .vidioc_enum_input = bttv_enum_input,
+ .vidioc_g_input = bttv_g_input,
+ .vidioc_s_input = bttv_s_input,
+ .vidioc_queryctrl = bttv_queryctrl,
+ .vidioc_g_ctrl = bttv_g_ctrl,
+ .vidioc_s_ctrl = bttv_s_ctrl,
+ .vidioc_streamon = bttv_streamon,
+ .vidioc_streamoff = bttv_streamoff,
+ .vidioc_g_tuner = bttv_g_tuner,
+ .vidioc_s_tuner = bttv_s_tuner,
+ .vidioc_g_crop = bttv_g_crop,
+ .vidioc_s_crop = bttv_s_crop,
+ .vidioc_g_fbuf = bttv_g_fbuf,
+ .vidioc_s_fbuf = bttv_s_fbuf,
+ .vidioc_overlay = bttv_overlay,
+ .vidioc_g_priority = bttv_g_priority,
+ .vidioc_s_priority = bttv_s_priority,
+ .vidioc_g_parm = bttv_g_parm,
+ .vidioc_g_frequency = bttv_g_frequency,
+ .vidioc_s_frequency = bttv_s_frequency,
+ .vidioc_log_status = bttv_log_status,
+ .vidioc_querystd = bttv_querystd,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = bttv_g_register,
+ .vidioc_s_register = bttv_s_register,
+#endif
+};
+
+static struct video_device bttv_video_template = {
+ .fops = &bttv_fops,
+ .ioctl_ops = &bttv_ioctl_ops,
+ .tvnorms = BTTV_NORMS,
+ .current_norm = V4L2_STD_PAL,
+};
+
+/* ----------------------------------------------------------------------- */
+/* radio interface */
+
+static int radio_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct bttv *btv = video_drvdata(file);
+ struct bttv_fh *fh;
+
+ dprintk("open dev=%s\n", video_device_node_name(vdev));
+
+ dprintk("%d: open called (radio)\n", btv->c.nr);
+
+ /* allocate per filehandle data */
+ fh = kmalloc(sizeof(*fh), GFP_KERNEL);
+ if (unlikely(!fh))
+ return -ENOMEM;
+ file->private_data = fh;
+ *fh = btv->init;
+
+ v4l2_prio_open(&btv->prio, &fh->prio);
+
+ btv->radio_user++;
+
+ bttv_call_all(btv, tuner, s_radio);
+ audio_input(btv,TVAUDIO_INPUT_RADIO);
+
+ return 0;
+}
+
+static int radio_release(struct file *file)
+{
+ struct bttv_fh *fh = file->private_data;
+ struct bttv *btv = fh->btv;
+ struct saa6588_command cmd;
+
+ v4l2_prio_close(&btv->prio, fh->prio);
+ file->private_data = NULL;
+ kfree(fh);
+
+ btv->radio_user--;
+
+ bttv_call_all(btv, core, ioctl, SAA6588_CMD_CLOSE, &cmd);
+
+ return 0;
+}
+
+static int radio_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ strcpy(cap->driver, "bttv");
+ strlcpy(cap->card, btv->radio_dev->name, sizeof(cap->card));
+ sprintf(cap->bus_info, "PCI:%s", pci_name(btv->c.pci));
+ cap->capabilities = V4L2_CAP_TUNER;
+
+ return 0;
+}
+
+static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ if (btv->tuner_type == TUNER_ABSENT)
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+ strcpy(t->name, "Radio");
+ t->type = V4L2_TUNER_RADIO;
+
+ bttv_call_all(btv, tuner, g_tuner, t);
+
+ if (btv->audio_mode_gpio)
+ btv->audio_mode_gpio(btv, t, 0);
+
+ return 0;
+}
+
+static int radio_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
+
+ strcpy(i->name, "Radio");
+ i->type = V4L2_INPUT_TYPE_TUNER;
+
+ return 0;
+}
+
+static int radio_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ if (unlikely(a->index))
+ return -EINVAL;
+
+ strcpy(a->name, "Radio");
+
+ return 0;
+}
+
+static int radio_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ bttv_call_all(btv, tuner, s_tuner, t);
+ return 0;
+}
+
+static int radio_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ if (unlikely(a->index))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int radio_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ if (unlikely(i))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
+ return 0;
+}
+
+static int radio_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ const struct v4l2_queryctrl *ctrl;
+
+ if (c->id < V4L2_CID_BASE ||
+ c->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+
+ if (c->id == V4L2_CID_AUDIO_MUTE) {
+ ctrl = ctrl_by_id(c->id);
+ *c = *ctrl;
+ } else
+ *c = no_ctl;
+
+ return 0;
+}
+
+static int radio_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static ssize_t radio_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct bttv_fh *fh = file->private_data;
+ struct bttv *btv = fh->btv;
+ struct saa6588_command cmd;
+ cmd.block_count = count/3;
+ cmd.buffer = data;
+ cmd.instance = file;
+ cmd.result = -ENODEV;
+
+ bttv_call_all(btv, core, ioctl, SAA6588_CMD_READ, &cmd);
+
+ return cmd.result;
+}
+
+static unsigned int radio_poll(struct file *file, poll_table *wait)
+{
+ struct bttv_fh *fh = file->private_data;
+ struct bttv *btv = fh->btv;
+ struct saa6588_command cmd;
+ cmd.instance = file;
+ cmd.event_list = wait;
+ cmd.result = -ENODEV;
+ bttv_call_all(btv, core, ioctl, SAA6588_CMD_POLL, &cmd);
+
+ return cmd.result;
+}
+
+static const struct v4l2_file_operations radio_fops =
+{
+ .owner = THIS_MODULE,
+ .open = radio_open,
+ .read = radio_read,
+ .release = radio_release,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = radio_poll,
+};
+
+static const struct v4l2_ioctl_ops radio_ioctl_ops = {
+ .vidioc_querycap = radio_querycap,
+ .vidioc_g_tuner = radio_g_tuner,
+ .vidioc_enum_input = radio_enum_input,
+ .vidioc_g_audio = radio_g_audio,
+ .vidioc_s_tuner = radio_s_tuner,
+ .vidioc_s_audio = radio_s_audio,
+ .vidioc_s_input = radio_s_input,
+ .vidioc_s_std = radio_s_std,
+ .vidioc_queryctrl = radio_queryctrl,
+ .vidioc_g_input = radio_g_input,
+ .vidioc_g_ctrl = bttv_g_ctrl,
+ .vidioc_s_ctrl = bttv_s_ctrl,
+ .vidioc_g_frequency = bttv_g_frequency,
+ .vidioc_s_frequency = bttv_s_frequency,
+};
+
+static struct video_device radio_template = {
+ .fops = &radio_fops,
+ .ioctl_ops = &radio_ioctl_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+/* some debug code */
+
+static int bttv_risc_decode(u32 risc)
+{
+ static char *instr[16] = {
+ [ BT848_RISC_WRITE >> 28 ] = "write",
+ [ BT848_RISC_SKIP >> 28 ] = "skip",
+ [ BT848_RISC_WRITEC >> 28 ] = "writec",
+ [ BT848_RISC_JUMP >> 28 ] = "jump",
+ [ BT848_RISC_SYNC >> 28 ] = "sync",
+ [ BT848_RISC_WRITE123 >> 28 ] = "write123",
+ [ BT848_RISC_SKIP123 >> 28 ] = "skip123",
+ [ BT848_RISC_WRITE1S23 >> 28 ] = "write1s23",
+ };
+ static int incr[16] = {
+ [ BT848_RISC_WRITE >> 28 ] = 2,
+ [ BT848_RISC_JUMP >> 28 ] = 2,
+ [ BT848_RISC_SYNC >> 28 ] = 2,
+ [ BT848_RISC_WRITE123 >> 28 ] = 5,
+ [ BT848_RISC_SKIP123 >> 28 ] = 2,
+ [ BT848_RISC_WRITE1S23 >> 28 ] = 3,
+ };
+ static char *bits[] = {
+ "be0", "be1", "be2", "be3/resync",
+ "set0", "set1", "set2", "set3",
+ "clr0", "clr1", "clr2", "clr3",
+ "irq", "res", "eol", "sol",
+ };
+ int i;
+
+ pr_cont("0x%08x [ %s", risc,
+ instr[risc >> 28] ? instr[risc >> 28] : "INVALID");
+ for (i = ARRAY_SIZE(bits)-1; i >= 0; i--)
+ if (risc & (1 << (i + 12)))
+ pr_cont(" %s", bits[i]);
+ pr_cont(" count=%d ]\n", risc & 0xfff);
+ return incr[risc >> 28] ? incr[risc >> 28] : 1;
+}
+
+static void bttv_risc_disasm(struct bttv *btv,
+ struct btcx_riscmem *risc)
+{
+ unsigned int i,j,n;
+
+ pr_info("%s: risc disasm: %p [dma=0x%08lx]\n",
+ btv->c.v4l2_dev.name, risc->cpu, (unsigned long)risc->dma);
+ for (i = 0; i < (risc->size >> 2); i += n) {
+ pr_info("%s: 0x%lx: ",
+ btv->c.v4l2_dev.name,
+ (unsigned long)(risc->dma + (i<<2)));
+ n = bttv_risc_decode(le32_to_cpu(risc->cpu[i]));
+ for (j = 1; j < n; j++)
+ pr_info("%s: 0x%lx: 0x%08x [ arg #%d ]\n",
+ btv->c.v4l2_dev.name,
+ (unsigned long)(risc->dma + ((i+j)<<2)),
+ risc->cpu[i+j], j);
+ if (0 == risc->cpu[i])
+ break;
+ }
+}
+
+static void bttv_print_riscaddr(struct bttv *btv)
+{
+ pr_info(" main: %08llx\n", (unsigned long long)btv->main.dma);
+ pr_info(" vbi : o=%08llx e=%08llx\n",
+ btv->cvbi ? (unsigned long long)btv->cvbi->top.dma : 0,
+ btv->cvbi ? (unsigned long long)btv->cvbi->bottom.dma : 0);
+ pr_info(" cap : o=%08llx e=%08llx\n",
+ btv->curr.top
+ ? (unsigned long long)btv->curr.top->top.dma : 0,
+ btv->curr.bottom
+ ? (unsigned long long)btv->curr.bottom->bottom.dma : 0);
+ pr_info(" scr : o=%08llx e=%08llx\n",
+ btv->screen ? (unsigned long long)btv->screen->top.dma : 0,
+ btv->screen ? (unsigned long long)btv->screen->bottom.dma : 0);
+ bttv_risc_disasm(btv, &btv->main);
+}
+
+/* ----------------------------------------------------------------------- */
+/* irq handler */
+
+static char *irq_name[] = {
+ "FMTCHG", // format change detected (525 vs. 625)
+ "VSYNC", // vertical sync (new field)
+ "HSYNC", // horizontal sync
+ "OFLOW", // chroma/luma AGC overflow
+ "HLOCK", // horizontal lock changed
+ "VPRES", // video presence changed
+ "6", "7",
+ "I2CDONE", // hw irc operation finished
+ "GPINT", // gpio port triggered irq
+ "10",
+ "RISCI", // risc instruction triggered irq
+ "FBUS", // pixel data fifo dropped data (high pci bus latencies)
+ "FTRGT", // pixel data fifo overrun
+ "FDSR", // fifo data stream resyncronisation
+ "PPERR", // parity error (data transfer)
+ "RIPERR", // parity error (read risc instructions)
+ "PABORT", // pci abort
+ "OCERR", // risc instruction error
+ "SCERR", // syncronisation error
+};
+
+static void bttv_print_irqbits(u32 print, u32 mark)
+{
+ unsigned int i;
+
+ pr_cont("bits:");
+ for (i = 0; i < ARRAY_SIZE(irq_name); i++) {
+ if (print & (1 << i))
+ pr_cont(" %s", irq_name[i]);
+ if (mark & (1 << i))
+ pr_cont("*");
+ }
+}
+
+static void bttv_irq_debug_low_latency(struct bttv *btv, u32 rc)
+{
+ pr_warn("%d: irq: skipped frame [main=%lx,o_vbi=%lx,o_field=%lx,rc=%lx]\n",
+ btv->c.nr,
+ (unsigned long)btv->main.dma,
+ (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_VBI+1]),
+ (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_FIELD+1]),
+ (unsigned long)rc);
+
+ if (0 == (btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC)) {
+ pr_notice("%d: Oh, there (temporarily?) is no input signal. "
+ "Ok, then this is harmless, don't worry ;)\n",
+ btv->c.nr);
+ return;
+ }
+ pr_notice("%d: Uhm. Looks like we have unusual high IRQ latencies\n",
+ btv->c.nr);
+ pr_notice("%d: Lets try to catch the culpit red-handed ...\n",
+ btv->c.nr);
+ dump_stack();
+}
+
+static int
+bttv_irq_next_video(struct bttv *btv, struct bttv_buffer_set *set)
+{
+ struct bttv_buffer *item;
+
+ memset(set,0,sizeof(*set));
+
+ /* capture request ? */
+ if (!list_empty(&btv->capture)) {
+ set->frame_irq = 1;
+ item = list_entry(btv->capture.next, struct bttv_buffer, vb.queue);
+ if (V4L2_FIELD_HAS_TOP(item->vb.field))
+ set->top = item;
+ if (V4L2_FIELD_HAS_BOTTOM(item->vb.field))
+ set->bottom = item;
+
+ /* capture request for other field ? */
+ if (!V4L2_FIELD_HAS_BOTH(item->vb.field) &&
+ (item->vb.queue.next != &btv->capture)) {
+ item = list_entry(item->vb.queue.next, struct bttv_buffer, vb.queue);
+ /* Mike Isely <isely@pobox.com> - Only check
+ * and set up the bottom field in the logic
+ * below. Don't ever do the top field. This
+ * of course means that if we set up the
+ * bottom field in the above code that we'll
+ * actually skip a field. But that's OK.
+ * Having processed only a single buffer this
+ * time, then the next time around the first
+ * available buffer should be for a top field.
+ * That will then cause us here to set up a
+ * top then a bottom field in the normal way.
+ * The alternative to this understanding is
+ * that we set up the second available buffer
+ * as a top field, but that's out of order
+ * since this driver always processes the top
+ * field first - the effect will be the two
+ * buffers being returned in the wrong order,
+ * with the second buffer also being delayed
+ * by one field time (owing to the fifo nature
+ * of videobuf). Worse still, we'll be stuck
+ * doing fields out of order now every time
+ * until something else causes a field to be
+ * dropped. By effectively forcing a field to
+ * drop this way then we always get back into
+ * sync within a single frame time. (Out of
+ * order fields can screw up deinterlacing
+ * algorithms.) */
+ if (!V4L2_FIELD_HAS_BOTH(item->vb.field)) {
+ if (NULL == set->bottom &&
+ V4L2_FIELD_BOTTOM == item->vb.field) {
+ set->bottom = item;
+ }
+ if (NULL != set->top && NULL != set->bottom)
+ set->top_irq = 2;
+ }
+ }
+ }
+
+ /* screen overlay ? */
+ if (NULL != btv->screen) {
+ if (V4L2_FIELD_HAS_BOTH(btv->screen->vb.field)) {
+ if (NULL == set->top && NULL == set->bottom) {
+ set->top = btv->screen;
+ set->bottom = btv->screen;
+ }
+ } else {
+ if (V4L2_FIELD_TOP == btv->screen->vb.field &&
+ NULL == set->top) {
+ set->top = btv->screen;
+ }
+ if (V4L2_FIELD_BOTTOM == btv->screen->vb.field &&
+ NULL == set->bottom) {
+ set->bottom = btv->screen;
+ }
+ }
+ }
+
+ dprintk("%d: next set: top=%p bottom=%p [screen=%p,irq=%d,%d]\n",
+ btv->c.nr, set->top, set->bottom,
+ btv->screen, set->frame_irq, set->top_irq);
+ return 0;
+}
+
+static void
+bttv_irq_wakeup_video(struct bttv *btv, struct bttv_buffer_set *wakeup,
+ struct bttv_buffer_set *curr, unsigned int state)
+{
+ struct timeval ts;
+
+ do_gettimeofday(&ts);
+
+ if (wakeup->top == wakeup->bottom) {
+ if (NULL != wakeup->top && curr->top != wakeup->top) {
+ if (irq_debug > 1)
+ pr_debug("%d: wakeup: both=%p\n",
+ btv->c.nr, wakeup->top);
+ wakeup->top->vb.ts = ts;
+ wakeup->top->vb.field_count = btv->field_count;
+ wakeup->top->vb.state = state;
+ wake_up(&wakeup->top->vb.done);
+ }
+ } else {
+ if (NULL != wakeup->top && curr->top != wakeup->top) {
+ if (irq_debug > 1)
+ pr_debug("%d: wakeup: top=%p\n",
+ btv->c.nr, wakeup->top);
+ wakeup->top->vb.ts = ts;
+ wakeup->top->vb.field_count = btv->field_count;
+ wakeup->top->vb.state = state;
+ wake_up(&wakeup->top->vb.done);
+ }
+ if (NULL != wakeup->bottom && curr->bottom != wakeup->bottom) {
+ if (irq_debug > 1)
+ pr_debug("%d: wakeup: bottom=%p\n",
+ btv->c.nr, wakeup->bottom);
+ wakeup->bottom->vb.ts = ts;
+ wakeup->bottom->vb.field_count = btv->field_count;
+ wakeup->bottom->vb.state = state;
+ wake_up(&wakeup->bottom->vb.done);
+ }
+ }
+}
+
+static void
+bttv_irq_wakeup_vbi(struct bttv *btv, struct bttv_buffer *wakeup,
+ unsigned int state)
+{
+ struct timeval ts;
+
+ if (NULL == wakeup)
+ return;
+
+ do_gettimeofday(&ts);
+ wakeup->vb.ts = ts;
+ wakeup->vb.field_count = btv->field_count;
+ wakeup->vb.state = state;
+ wake_up(&wakeup->vb.done);
+}
+
+static void bttv_irq_timeout(unsigned long data)
+{
+ struct bttv *btv = (struct bttv *)data;
+ struct bttv_buffer_set old,new;
+ struct bttv_buffer *ovbi;
+ struct bttv_buffer *item;
+ unsigned long flags;
+
+ if (bttv_verbose) {
+ pr_info("%d: timeout: drop=%d irq=%d/%d, risc=%08x, ",
+ btv->c.nr, btv->framedrop, btv->irq_me, btv->irq_total,
+ btread(BT848_RISC_COUNT));
+ bttv_print_irqbits(btread(BT848_INT_STAT),0);
+ pr_cont("\n");
+ }
+
+ spin_lock_irqsave(&btv->s_lock,flags);
+
+ /* deactivate stuff */
+ memset(&new,0,sizeof(new));
+ old = btv->curr;
+ ovbi = btv->cvbi;
+ btv->curr = new;
+ btv->cvbi = NULL;
+ btv->loop_irq = 0;
+ bttv_buffer_activate_video(btv, &new);
+ bttv_buffer_activate_vbi(btv, NULL);
+ bttv_set_dma(btv, 0);
+
+ /* wake up */
+ bttv_irq_wakeup_video(btv, &old, &new, VIDEOBUF_ERROR);
+ bttv_irq_wakeup_vbi(btv, ovbi, VIDEOBUF_ERROR);
+
+ /* cancel all outstanding capture / vbi requests */
+ while (!list_empty(&btv->capture)) {
+ item = list_entry(btv->capture.next, struct bttv_buffer, vb.queue);
+ list_del(&item->vb.queue);
+ item->vb.state = VIDEOBUF_ERROR;
+ wake_up(&item->vb.done);
+ }
+ while (!list_empty(&btv->vcapture)) {
+ item = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue);
+ list_del(&item->vb.queue);
+ item->vb.state = VIDEOBUF_ERROR;
+ wake_up(&item->vb.done);
+ }
+
+ btv->errors++;
+ spin_unlock_irqrestore(&btv->s_lock,flags);
+}
+
+static void
+bttv_irq_wakeup_top(struct bttv *btv)
+{
+ struct bttv_buffer *wakeup = btv->curr.top;
+
+ if (NULL == wakeup)
+ return;
+
+ spin_lock(&btv->s_lock);
+ btv->curr.top_irq = 0;
+ btv->curr.top = NULL;
+ bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0);
+
+ do_gettimeofday(&wakeup->vb.ts);
+ wakeup->vb.field_count = btv->field_count;
+ wakeup->vb.state = VIDEOBUF_DONE;
+ wake_up(&wakeup->vb.done);
+ spin_unlock(&btv->s_lock);
+}
+
+static inline int is_active(struct btcx_riscmem *risc, u32 rc)
+{
+ if (rc < risc->dma)
+ return 0;
+ if (rc > risc->dma + risc->size)
+ return 0;
+ return 1;
+}
+
+static void
+bttv_irq_switch_video(struct bttv *btv)
+{
+ struct bttv_buffer_set new;
+ struct bttv_buffer_set old;
+ dma_addr_t rc;
+
+ spin_lock(&btv->s_lock);
+
+ /* new buffer set */
+ bttv_irq_next_video(btv, &new);
+ rc = btread(BT848_RISC_COUNT);
+ if ((btv->curr.top && is_active(&btv->curr.top->top, rc)) ||
+ (btv->curr.bottom && is_active(&btv->curr.bottom->bottom, rc))) {
+ btv->framedrop++;
+ if (debug_latency)
+ bttv_irq_debug_low_latency(btv, rc);
+ spin_unlock(&btv->s_lock);
+ return;
+ }
+
+ /* switch over */
+ old = btv->curr;
+ btv->curr = new;
+ btv->loop_irq &= ~1;
+ bttv_buffer_activate_video(btv, &new);
+ bttv_set_dma(btv, 0);
+
+ /* switch input */
+ if (UNSET != btv->new_input) {
+ video_mux(btv,btv->new_input);
+ btv->new_input = UNSET;
+ }
+
+ /* wake up finished buffers */
+ bttv_irq_wakeup_video(btv, &old, &new, VIDEOBUF_DONE);
+ spin_unlock(&btv->s_lock);
+}
+
+static void
+bttv_irq_switch_vbi(struct bttv *btv)
+{
+ struct bttv_buffer *new = NULL;
+ struct bttv_buffer *old;
+ u32 rc;
+
+ spin_lock(&btv->s_lock);
+
+ if (!list_empty(&btv->vcapture))
+ new = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue);
+ old = btv->cvbi;
+
+ rc = btread(BT848_RISC_COUNT);
+ if (NULL != old && (is_active(&old->top, rc) ||
+ is_active(&old->bottom, rc))) {
+ btv->framedrop++;
+ if (debug_latency)
+ bttv_irq_debug_low_latency(btv, rc);
+ spin_unlock(&btv->s_lock);
+ return;
+ }
+
+ /* switch */
+ btv->cvbi = new;
+ btv->loop_irq &= ~4;
+ bttv_buffer_activate_vbi(btv, new);
+ bttv_set_dma(btv, 0);
+
+ bttv_irq_wakeup_vbi(btv, old, VIDEOBUF_DONE);
+ spin_unlock(&btv->s_lock);
+}
+
+static irqreturn_t bttv_irq(int irq, void *dev_id)
+{
+ u32 stat,astat;
+ u32 dstat;
+ int count;
+ struct bttv *btv;
+ int handled = 0;
+
+ btv=(struct bttv *)dev_id;
+
+ count=0;
+ while (1) {
+ /* get/clear interrupt status bits */
+ stat=btread(BT848_INT_STAT);
+ astat=stat&btread(BT848_INT_MASK);
+ if (!astat)
+ break;
+ handled = 1;
+ btwrite(stat,BT848_INT_STAT);
+
+ /* get device status bits */
+ dstat=btread(BT848_DSTATUS);
+
+ if (irq_debug) {
+ pr_debug("%d: irq loop=%d fc=%d riscs=%x, riscc=%08x, ",
+ btv->c.nr, count, btv->field_count,
+ stat>>28, btread(BT848_RISC_COUNT));
+ bttv_print_irqbits(stat,astat);
+ if (stat & BT848_INT_HLOCK)
+ pr_cont(" HLOC => %s",
+ dstat & BT848_DSTATUS_HLOC
+ ? "yes" : "no");
+ if (stat & BT848_INT_VPRES)
+ pr_cont(" PRES => %s",
+ dstat & BT848_DSTATUS_PRES
+ ? "yes" : "no");
+ if (stat & BT848_INT_FMTCHG)
+ pr_cont(" NUML => %s",
+ dstat & BT848_DSTATUS_NUML
+ ? "625" : "525");
+ pr_cont("\n");
+ }
+
+ if (astat&BT848_INT_VSYNC)
+ btv->field_count++;
+
+ if ((astat & BT848_INT_GPINT) && btv->remote) {
+ bttv_input_irq(btv);
+ }
+
+ if (astat & BT848_INT_I2CDONE) {
+ btv->i2c_done = stat;
+ wake_up(&btv->i2c_queue);
+ }
+
+ if ((astat & BT848_INT_RISCI) && (stat & (4<<28)))
+ bttv_irq_switch_vbi(btv);
+
+ if ((astat & BT848_INT_RISCI) && (stat & (2<<28)))
+ bttv_irq_wakeup_top(btv);
+
+ if ((astat & BT848_INT_RISCI) && (stat & (1<<28)))
+ bttv_irq_switch_video(btv);
+
+ if ((astat & BT848_INT_HLOCK) && btv->opt_automute)
+ audio_mute(btv, btv->mute); /* trigger automute */
+
+ if (astat & (BT848_INT_SCERR|BT848_INT_OCERR)) {
+ pr_info("%d: %s%s @ %08x,",
+ btv->c.nr,
+ (astat & BT848_INT_SCERR) ? "SCERR" : "",
+ (astat & BT848_INT_OCERR) ? "OCERR" : "",
+ btread(BT848_RISC_COUNT));
+ bttv_print_irqbits(stat,astat);
+ pr_cont("\n");
+ if (bttv_debug)
+ bttv_print_riscaddr(btv);
+ }
+ if (fdsr && astat & BT848_INT_FDSR) {
+ pr_info("%d: FDSR @ %08x\n",
+ btv->c.nr, btread(BT848_RISC_COUNT));
+ if (bttv_debug)
+ bttv_print_riscaddr(btv);
+ }
+
+ count++;
+ if (count > 4) {
+
+ if (count > 8 || !(astat & BT848_INT_GPINT)) {
+ btwrite(0, BT848_INT_MASK);
+
+ pr_err("%d: IRQ lockup, cleared int mask [",
+ btv->c.nr);
+ } else {
+ pr_err("%d: IRQ lockup, clearing GPINT from int mask [",
+ btv->c.nr);
+
+ btwrite(btread(BT848_INT_MASK) & (-1 ^ BT848_INT_GPINT),
+ BT848_INT_MASK);
+ };
+
+ bttv_print_irqbits(stat,astat);
+
+ pr_cont("]\n");
+ }
+ }
+ btv->irq_total++;
+ if (handled)
+ btv->irq_me++;
+ return IRQ_RETVAL(handled);
+}
+
+
+/* ----------------------------------------------------------------------- */
+/* initialitation */
+
+static struct video_device *vdev_init(struct bttv *btv,
+ const struct video_device *template,
+ const char *type_name)
+{
+ struct video_device *vfd;
+
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+ *vfd = *template;
+ vfd->v4l2_dev = &btv->c.v4l2_dev;
+ vfd->release = video_device_release;
+ vfd->debug = bttv_debug;
+ video_set_drvdata(vfd, btv);
+ snprintf(vfd->name, sizeof(vfd->name), "BT%d%s %s (%s)",
+ btv->id, (btv->id==848 && btv->revision==0x12) ? "A" : "",
+ type_name, bttv_tvcards[btv->c.type].name);
+ return vfd;
+}
+
+static void bttv_unregister_video(struct bttv *btv)
+{
+ if (btv->video_dev) {
+ if (video_is_registered(btv->video_dev))
+ video_unregister_device(btv->video_dev);
+ else
+ video_device_release(btv->video_dev);
+ btv->video_dev = NULL;
+ }
+ if (btv->vbi_dev) {
+ if (video_is_registered(btv->vbi_dev))
+ video_unregister_device(btv->vbi_dev);
+ else
+ video_device_release(btv->vbi_dev);
+ btv->vbi_dev = NULL;
+ }
+ if (btv->radio_dev) {
+ if (video_is_registered(btv->radio_dev))
+ video_unregister_device(btv->radio_dev);
+ else
+ video_device_release(btv->radio_dev);
+ btv->radio_dev = NULL;
+ }
+}
+
+/* register video4linux devices */
+static int __devinit bttv_register_video(struct bttv *btv)
+{
+ if (no_overlay > 0)
+ pr_notice("Overlay support disabled\n");
+
+ /* video */
+ btv->video_dev = vdev_init(btv, &bttv_video_template, "video");
+
+ if (NULL == btv->video_dev)
+ goto err;
+ if (video_register_device(btv->video_dev, VFL_TYPE_GRABBER,
+ video_nr[btv->c.nr]) < 0)
+ goto err;
+ pr_info("%d: registered device %s\n",
+ btv->c.nr, video_device_node_name(btv->video_dev));
+ if (device_create_file(&btv->video_dev->dev,
+ &dev_attr_card)<0) {
+ pr_err("%d: device_create_file 'card' failed\n", btv->c.nr);
+ goto err;
+ }
+
+ /* vbi */
+ btv->vbi_dev = vdev_init(btv, &bttv_video_template, "vbi");
+
+ if (NULL == btv->vbi_dev)
+ goto err;
+ if (video_register_device(btv->vbi_dev, VFL_TYPE_VBI,
+ vbi_nr[btv->c.nr]) < 0)
+ goto err;
+ pr_info("%d: registered device %s\n",
+ btv->c.nr, video_device_node_name(btv->vbi_dev));
+
+ if (!btv->has_radio)
+ return 0;
+ /* radio */
+ btv->radio_dev = vdev_init(btv, &radio_template, "radio");
+ if (NULL == btv->radio_dev)
+ goto err;
+ if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO,
+ radio_nr[btv->c.nr]) < 0)
+ goto err;
+ pr_info("%d: registered device %s\n",
+ btv->c.nr, video_device_node_name(btv->radio_dev));
+
+ /* all done */
+ return 0;
+
+ err:
+ bttv_unregister_video(btv);
+ return -1;
+}
+
+
+/* on OpenFirmware machines (PowerMac at least), PCI memory cycle */
+/* response on cards with no firmware is not enabled by OF */
+static void pci_set_command(struct pci_dev *dev)
+{
+#if defined(__powerpc__)
+ unsigned int cmd;
+
+ pci_read_config_dword(dev, PCI_COMMAND, &cmd);
+ cmd = (cmd | PCI_COMMAND_MEMORY );
+ pci_write_config_dword(dev, PCI_COMMAND, cmd);
+#endif
+}
+
+static int __devinit bttv_probe(struct pci_dev *dev,
+ const struct pci_device_id *pci_id)
+{
+ int result;
+ unsigned char lat;
+ struct bttv *btv;
+
+ if (bttv_num == BTTV_MAX)
+ return -ENOMEM;
+ pr_info("Bt8xx card found (%d)\n", bttv_num);
+ bttvs[bttv_num] = btv = kzalloc(sizeof(*btv), GFP_KERNEL);
+ if (btv == NULL) {
+ pr_err("out of memory\n");
+ return -ENOMEM;
+ }
+ btv->c.nr = bttv_num;
+ snprintf(btv->c.v4l2_dev.name, sizeof(btv->c.v4l2_dev.name),
+ "bttv%d", btv->c.nr);
+
+ /* initialize structs / fill in defaults */
+ mutex_init(&btv->lock);
+ spin_lock_init(&btv->s_lock);
+ spin_lock_init(&btv->gpio_lock);
+ init_waitqueue_head(&btv->i2c_queue);
+ INIT_LIST_HEAD(&btv->c.subs);
+ INIT_LIST_HEAD(&btv->capture);
+ INIT_LIST_HEAD(&btv->vcapture);
+ v4l2_prio_init(&btv->prio);
+
+ init_timer(&btv->timeout);
+ btv->timeout.function = bttv_irq_timeout;
+ btv->timeout.data = (unsigned long)btv;
+
+ btv->i2c_rc = -1;
+ btv->tuner_type = UNSET;
+ btv->new_input = UNSET;
+ btv->has_radio=radio[btv->c.nr];
+
+ /* pci stuff (init, get irq/mmio, ... */
+ btv->c.pci = dev;
+ btv->id = dev->device;
+ if (pci_enable_device(dev)) {
+ pr_warn("%d: Can't enable device\n", btv->c.nr);
+ return -EIO;
+ }
+ if (pci_set_dma_mask(dev, DMA_BIT_MASK(32))) {
+ pr_warn("%d: No suitable DMA available\n", btv->c.nr);
+ return -EIO;
+ }
+ if (!request_mem_region(pci_resource_start(dev,0),
+ pci_resource_len(dev,0),
+ btv->c.v4l2_dev.name)) {
+ pr_warn("%d: can't request iomem (0x%llx)\n",
+ btv->c.nr,
+ (unsigned long long)pci_resource_start(dev, 0));
+ return -EBUSY;
+ }
+ pci_set_master(dev);
+ pci_set_command(dev);
+
+ result = v4l2_device_register(&dev->dev, &btv->c.v4l2_dev);
+ if (result < 0) {
+ pr_warn("%d: v4l2_device_register() failed\n", btv->c.nr);
+ goto fail0;
+ }
+
+ btv->revision = dev->revision;
+ pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
+ pr_info("%d: Bt%d (rev %d) at %s, irq: %d, latency: %d, mmio: 0x%llx\n",
+ bttv_num, btv->id, btv->revision, pci_name(dev),
+ btv->c.pci->irq, lat,
+ (unsigned long long)pci_resource_start(dev, 0));
+ schedule();
+
+ btv->bt848_mmio = ioremap(pci_resource_start(dev, 0), 0x1000);
+ if (NULL == btv->bt848_mmio) {
+ pr_err("%d: ioremap() failed\n", btv->c.nr);
+ result = -EIO;
+ goto fail1;
+ }
+
+ /* identify card */
+ bttv_idcard(btv);
+
+ /* disable irqs, register irq handler */
+ btwrite(0, BT848_INT_MASK);
+ result = request_irq(btv->c.pci->irq, bttv_irq,
+ IRQF_SHARED | IRQF_DISABLED, btv->c.v4l2_dev.name, (void *)btv);
+ if (result < 0) {
+ pr_err("%d: can't get IRQ %d\n",
+ bttv_num, btv->c.pci->irq);
+ goto fail1;
+ }
+
+ if (0 != bttv_handle_chipset(btv)) {
+ result = -EIO;
+ goto fail2;
+ }
+
+ /* init options from insmod args */
+ btv->opt_combfilter = combfilter;
+ btv->opt_lumafilter = lumafilter;
+ btv->opt_automute = automute;
+ btv->opt_chroma_agc = chroma_agc;
+ btv->opt_adc_crush = adc_crush;
+ btv->opt_vcr_hack = vcr_hack;
+ btv->opt_whitecrush_upper = whitecrush_upper;
+ btv->opt_whitecrush_lower = whitecrush_lower;
+ btv->opt_uv_ratio = uv_ratio;
+ btv->opt_full_luma_range = full_luma_range;
+ btv->opt_coring = coring;
+
+ /* fill struct bttv with some useful defaults */
+ btv->init.btv = btv;
+ btv->init.ov.w.width = 320;
+ btv->init.ov.w.height = 240;
+ btv->init.fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
+ btv->init.width = 320;
+ btv->init.height = 240;
+ btv->input = 0;
+
+ /* initialize hardware */
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"pre-init");
+
+ bttv_risc_init_main(btv);
+ init_bt848(btv);
+
+ /* gpio */
+ btwrite(0x00, BT848_GPIO_REG_INP);
+ btwrite(0x00, BT848_GPIO_OUT_EN);
+ if (bttv_verbose)
+ bttv_gpio_tracking(btv,"init");
+
+ /* needs to be done before i2c is registered */
+ bttv_init_card1(btv);
+
+ /* register i2c + gpio */
+ init_bttv_i2c(btv);
+
+ /* some card-specific stuff (needs working i2c) */
+ bttv_init_card2(btv);
+ bttv_init_tuner(btv);
+ init_irqreg(btv);
+
+ /* register video4linux + input */
+ if (!bttv_tvcards[btv->c.type].no_video) {
+ bttv_register_video(btv);
+ bt848_bright(btv,32768);
+ bt848_contrast(btv,32768);
+ bt848_hue(btv,32768);
+ bt848_sat(btv,32768);
+ audio_mute(btv, 1);
+ set_input(btv, 0, btv->tvnorm);
+ bttv_crop_reset(&btv->crop[0], btv->tvnorm);
+ btv->crop[1] = btv->crop[0]; /* current = default */
+ disclaim_vbi_lines(btv);
+ disclaim_video_lines(btv);
+ }
+
+ /* add subdevices and autoload dvb-bt8xx if needed */
+ if (bttv_tvcards[btv->c.type].has_dvb) {
+ bttv_sub_add_device(&btv->c, "dvb");
+ request_modules(btv);
+ }
+
+ if (!disable_ir) {
+ init_bttv_i2c_ir(btv);
+ bttv_input_init(btv);
+ }
+
+ /* everything is fine */
+ bttv_num++;
+ return 0;
+
+fail2:
+ free_irq(btv->c.pci->irq,btv);
+
+fail1:
+ v4l2_device_unregister(&btv->c.v4l2_dev);
+
+fail0:
+ if (btv->bt848_mmio)
+ iounmap(btv->bt848_mmio);
+ release_mem_region(pci_resource_start(btv->c.pci,0),
+ pci_resource_len(btv->c.pci,0));
+ return result;
+}
+
+static void __devexit bttv_remove(struct pci_dev *pci_dev)
+{
+ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+ struct bttv *btv = to_bttv(v4l2_dev);
+
+ if (bttv_verbose)
+ pr_info("%d: unloading\n", btv->c.nr);
+
+ if (bttv_tvcards[btv->c.type].has_dvb)
+ flush_request_modules(btv);
+
+ /* shutdown everything (DMA+IRQs) */
+ btand(~15, BT848_GPIO_DMA_CTL);
+ btwrite(0, BT848_INT_MASK);
+ btwrite(~0x0, BT848_INT_STAT);
+ btwrite(0x0, BT848_GPIO_OUT_EN);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"cleanup");
+
+ /* tell gpio modules we are leaving ... */
+ btv->shutdown=1;
+ bttv_input_fini(btv);
+ bttv_sub_del_devices(&btv->c);
+
+ /* unregister i2c_bus + input */
+ fini_bttv_i2c(btv);
+
+ /* unregister video4linux */
+ bttv_unregister_video(btv);
+
+ /* free allocated memory */
+ btcx_riscmem_free(btv->c.pci,&btv->main);
+
+ /* free ressources */
+ free_irq(btv->c.pci->irq,btv);
+ iounmap(btv->bt848_mmio);
+ release_mem_region(pci_resource_start(btv->c.pci,0),
+ pci_resource_len(btv->c.pci,0));
+
+ v4l2_device_unregister(&btv->c.v4l2_dev);
+ bttvs[btv->c.nr] = NULL;
+ kfree(btv);
+
+ return;
+}
+
+#ifdef CONFIG_PM
+static int bttv_suspend(struct pci_dev *pci_dev, pm_message_t state)
+{
+ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+ struct bttv *btv = to_bttv(v4l2_dev);
+ struct bttv_buffer_set idle;
+ unsigned long flags;
+
+ dprintk("%d: suspend %d\n", btv->c.nr, state.event);
+
+ /* stop dma + irqs */
+ spin_lock_irqsave(&btv->s_lock,flags);
+ memset(&idle, 0, sizeof(idle));
+ btv->state.video = btv->curr;
+ btv->state.vbi = btv->cvbi;
+ btv->state.loop_irq = btv->loop_irq;
+ btv->curr = idle;
+ btv->loop_irq = 0;
+ bttv_buffer_activate_video(btv, &idle);
+ bttv_buffer_activate_vbi(btv, NULL);
+ bttv_set_dma(btv, 0);
+ btwrite(0, BT848_INT_MASK);
+ spin_unlock_irqrestore(&btv->s_lock,flags);
+
+ /* save bt878 state */
+ btv->state.gpio_enable = btread(BT848_GPIO_OUT_EN);
+ btv->state.gpio_data = gpio_read();
+
+ /* save pci state */
+ pci_save_state(pci_dev);
+ if (0 != pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state))) {
+ pci_disable_device(pci_dev);
+ btv->state.disabled = 1;
+ }
+ return 0;
+}
+
+static int bttv_resume(struct pci_dev *pci_dev)
+{
+ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+ struct bttv *btv = to_bttv(v4l2_dev);
+ unsigned long flags;
+ int err;
+
+ dprintk("%d: resume\n", btv->c.nr);
+
+ /* restore pci state */
+ if (btv->state.disabled) {
+ err=pci_enable_device(pci_dev);
+ if (err) {
+ pr_warn("%d: Can't enable device\n", btv->c.nr);
+ return err;
+ }
+ btv->state.disabled = 0;
+ }
+ err=pci_set_power_state(pci_dev, PCI_D0);
+ if (err) {
+ pci_disable_device(pci_dev);
+ pr_warn("%d: Can't enable device\n", btv->c.nr);
+ btv->state.disabled = 1;
+ return err;
+ }
+
+ pci_restore_state(pci_dev);
+
+ /* restore bt878 state */
+ bttv_reinit_bt848(btv);
+ gpio_inout(0xffffff, btv->state.gpio_enable);
+ gpio_write(btv->state.gpio_data);
+
+ /* restart dma */
+ spin_lock_irqsave(&btv->s_lock,flags);
+ btv->curr = btv->state.video;
+ btv->cvbi = btv->state.vbi;
+ btv->loop_irq = btv->state.loop_irq;
+ bttv_buffer_activate_video(btv, &btv->curr);
+ bttv_buffer_activate_vbi(btv, btv->cvbi);
+ bttv_set_dma(btv, 0);
+ spin_unlock_irqrestore(&btv->s_lock,flags);
+ return 0;
+}
+#endif
+
+static struct pci_device_id bttv_pci_tbl[] = {
+ {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT848), 0},
+ {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT849), 0},
+ {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT878), 0},
+ {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT879), 0},
+ {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_FUSION879), 0},
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, bttv_pci_tbl);
+
+static struct pci_driver bttv_pci_driver = {
+ .name = "bttv",
+ .id_table = bttv_pci_tbl,
+ .probe = bttv_probe,
+ .remove = __devexit_p(bttv_remove),
+#ifdef CONFIG_PM
+ .suspend = bttv_suspend,
+ .resume = bttv_resume,
+#endif
+};
+
+static int __init bttv_init_module(void)
+{
+ int ret;
+
+ bttv_num = 0;
+
+ pr_info("driver version %s loaded\n", BTTV_VERSION);
+ if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME)
+ gbuffers = 2;
+ if (gbufsize > BTTV_MAX_FBUF)
+ gbufsize = BTTV_MAX_FBUF;
+ gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK;
+ if (bttv_verbose)
+ pr_info("using %d buffers with %dk (%d pages) each for capture\n",
+ gbuffers, gbufsize >> 10, gbufsize >> PAGE_SHIFT);
+
+ bttv_check_chipset();
+
+ ret = bus_register(&bttv_sub_bus_type);
+ if (ret < 0) {
+ pr_warn("bus_register error: %d\n", ret);
+ return ret;
+ }
+ ret = pci_register_driver(&bttv_pci_driver);
+ if (ret < 0)
+ bus_unregister(&bttv_sub_bus_type);
+
+ return ret;
+}
+
+static void __exit bttv_cleanup_module(void)
+{
+ pci_unregister_driver(&bttv_pci_driver);
+ bus_unregister(&bttv_sub_bus_type);
+}
+
+module_init(bttv_init_module);
+module_exit(bttv_cleanup_module);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/bt8xx/bttv-gpio.c b/drivers/media/video/bt8xx/bttv-gpio.c
new file mode 100644
index 00000000..922e8233
--- /dev/null
+++ b/drivers/media/video/bt8xx/bttv-gpio.c
@@ -0,0 +1,189 @@
+/*
+
+ bttv-gpio.c -- gpio sub drivers
+
+ sysfs-based sub driver interface for bttv
+ mainly intended for gpio access
+
+
+ Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
+ & Marcus Metzler (mocm@thp.uni-koeln.de)
+ (c) 1999-2003 Gerd Knorr <kraxel@bytesex.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+
+#include "bttvp.h"
+
+/* ----------------------------------------------------------------------- */
+/* internal: the bttv "bus" */
+
+static int bttv_sub_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct bttv_sub_driver *sub = to_bttv_sub_drv(drv);
+ int len = strlen(sub->wanted);
+
+ if (0 == strncmp(dev_name(dev), sub->wanted, len))
+ return 1;
+ return 0;
+}
+
+static int bttv_sub_probe(struct device *dev)
+{
+ struct bttv_sub_device *sdev = to_bttv_sub_dev(dev);
+ struct bttv_sub_driver *sub = to_bttv_sub_drv(dev->driver);
+
+ return sub->probe ? sub->probe(sdev) : -ENODEV;
+}
+
+static int bttv_sub_remove(struct device *dev)
+{
+ struct bttv_sub_device *sdev = to_bttv_sub_dev(dev);
+ struct bttv_sub_driver *sub = to_bttv_sub_drv(dev->driver);
+
+ if (sub->remove)
+ sub->remove(sdev);
+ return 0;
+}
+
+struct bus_type bttv_sub_bus_type = {
+ .name = "bttv-sub",
+ .match = &bttv_sub_bus_match,
+ .probe = bttv_sub_probe,
+ .remove = bttv_sub_remove,
+};
+
+static void release_sub_device(struct device *dev)
+{
+ struct bttv_sub_device *sub = to_bttv_sub_dev(dev);
+ kfree(sub);
+}
+
+int bttv_sub_add_device(struct bttv_core *core, char *name)
+{
+ struct bttv_sub_device *sub;
+ int err;
+
+ sub = kzalloc(sizeof(*sub),GFP_KERNEL);
+ if (NULL == sub)
+ return -ENOMEM;
+
+ sub->core = core;
+ sub->dev.parent = &core->pci->dev;
+ sub->dev.bus = &bttv_sub_bus_type;
+ sub->dev.release = release_sub_device;
+ dev_set_name(&sub->dev, "%s%d", name, core->nr);
+
+ err = device_register(&sub->dev);
+ if (0 != err) {
+ kfree(sub);
+ return err;
+ }
+ pr_info("%d: add subdevice \"%s\"\n", core->nr, dev_name(&sub->dev));
+ list_add_tail(&sub->list,&core->subs);
+ return 0;
+}
+
+int bttv_sub_del_devices(struct bttv_core *core)
+{
+ struct bttv_sub_device *sub, *save;
+
+ list_for_each_entry_safe(sub, save, &core->subs, list) {
+ list_del(&sub->list);
+ device_unregister(&sub->dev);
+ }
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+/* external: sub-driver register/unregister */
+
+int bttv_sub_register(struct bttv_sub_driver *sub, char *wanted)
+{
+ sub->drv.bus = &bttv_sub_bus_type;
+ snprintf(sub->wanted,sizeof(sub->wanted),"%s",wanted);
+ return driver_register(&sub->drv);
+}
+EXPORT_SYMBOL(bttv_sub_register);
+
+int bttv_sub_unregister(struct bttv_sub_driver *sub)
+{
+ driver_unregister(&sub->drv);
+ return 0;
+}
+EXPORT_SYMBOL(bttv_sub_unregister);
+
+/* ----------------------------------------------------------------------- */
+/* external: gpio access functions */
+
+void bttv_gpio_inout(struct bttv_core *core, u32 mask, u32 outbits)
+{
+ struct bttv *btv = container_of(core, struct bttv, c);
+ unsigned long flags;
+ u32 data;
+
+ spin_lock_irqsave(&btv->gpio_lock,flags);
+ data = btread(BT848_GPIO_OUT_EN);
+ data = data & ~mask;
+ data = data | (mask & outbits);
+ btwrite(data,BT848_GPIO_OUT_EN);
+ spin_unlock_irqrestore(&btv->gpio_lock,flags);
+}
+
+u32 bttv_gpio_read(struct bttv_core *core)
+{
+ struct bttv *btv = container_of(core, struct bttv, c);
+ u32 value;
+
+ value = btread(BT848_GPIO_DATA);
+ return value;
+}
+
+void bttv_gpio_write(struct bttv_core *core, u32 value)
+{
+ struct bttv *btv = container_of(core, struct bttv, c);
+
+ btwrite(value,BT848_GPIO_DATA);
+}
+
+void bttv_gpio_bits(struct bttv_core *core, u32 mask, u32 bits)
+{
+ struct bttv *btv = container_of(core, struct bttv, c);
+ unsigned long flags;
+ u32 data;
+
+ spin_lock_irqsave(&btv->gpio_lock,flags);
+ data = btread(BT848_GPIO_DATA);
+ data = data & ~mask;
+ data = data | (mask & bits);
+ btwrite(data,BT848_GPIO_DATA);
+ spin_unlock_irqrestore(&btv->gpio_lock,flags);
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/bt8xx/bttv-i2c.c b/drivers/media/video/bt8xx/bttv-i2c.c
new file mode 100644
index 00000000..580c8e68
--- /dev/null
+++ b/drivers/media/video/bt8xx/bttv-i2c.c
@@ -0,0 +1,397 @@
+/*
+
+ bttv-i2c.c -- all the i2c code is here
+
+ bttv - Bt848 frame grabber driver
+
+ Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
+ & Marcus Metzler (mocm@thp.uni-koeln.de)
+ (c) 1999-2003 Gerd Knorr <kraxel@bytesex.org>
+
+ (c) 2005 Mauro Carvalho Chehab <mchehab@infradead.org>
+ - Multituner support and i2c address binding
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include "bttvp.h"
+#include <media/v4l2-common.h>
+#include <linux/jiffies.h>
+#include <asm/io.h>
+
+static int i2c_debug;
+static int i2c_hw;
+static int i2c_scan;
+module_param(i2c_debug, int, 0644);
+MODULE_PARM_DESC(i2c_debug, "configure i2c debug level");
+module_param(i2c_hw, int, 0444);
+MODULE_PARM_DESC(i2c_hw,"force use of hardware i2c support, "
+ "instead of software bitbang");
+module_param(i2c_scan, int, 0444);
+MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time");
+
+static unsigned int i2c_udelay = 5;
+module_param(i2c_udelay, int, 0444);
+MODULE_PARM_DESC(i2c_udelay,"soft i2c delay at insmod time, in usecs "
+ "(should be 5 or higher). Lower value means higher bus speed.");
+
+/* ----------------------------------------------------------------------- */
+/* I2C functions - bitbanging adapter (software i2c) */
+
+static void bttv_bit_setscl(void *data, int state)
+{
+ struct bttv *btv = (struct bttv*)data;
+
+ if (state)
+ btv->i2c_state |= 0x02;
+ else
+ btv->i2c_state &= ~0x02;
+ btwrite(btv->i2c_state, BT848_I2C);
+ btread(BT848_I2C);
+}
+
+static void bttv_bit_setsda(void *data, int state)
+{
+ struct bttv *btv = (struct bttv*)data;
+
+ if (state)
+ btv->i2c_state |= 0x01;
+ else
+ btv->i2c_state &= ~0x01;
+ btwrite(btv->i2c_state, BT848_I2C);
+ btread(BT848_I2C);
+}
+
+static int bttv_bit_getscl(void *data)
+{
+ struct bttv *btv = (struct bttv*)data;
+ int state;
+
+ state = btread(BT848_I2C) & 0x02 ? 1 : 0;
+ return state;
+}
+
+static int bttv_bit_getsda(void *data)
+{
+ struct bttv *btv = (struct bttv*)data;
+ int state;
+
+ state = btread(BT848_I2C) & 0x01;
+ return state;
+}
+
+static struct i2c_algo_bit_data __devinitdata bttv_i2c_algo_bit_template = {
+ .setsda = bttv_bit_setsda,
+ .setscl = bttv_bit_setscl,
+ .getsda = bttv_bit_getsda,
+ .getscl = bttv_bit_getscl,
+ .udelay = 16,
+ .timeout = 200,
+};
+
+/* ----------------------------------------------------------------------- */
+/* I2C functions - hardware i2c */
+
+static u32 functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_EMUL;
+}
+
+static int
+bttv_i2c_wait_done(struct bttv *btv)
+{
+ int rc = 0;
+
+ /* timeout */
+ if (wait_event_interruptible_timeout(btv->i2c_queue,
+ btv->i2c_done, msecs_to_jiffies(85)) == -ERESTARTSYS)
+ rc = -EIO;
+
+ if (btv->i2c_done & BT848_INT_RACK)
+ rc = 1;
+ btv->i2c_done = 0;
+ return rc;
+}
+
+#define I2C_HW (BT878_I2C_MODE | BT848_I2C_SYNC |\
+ BT848_I2C_SCL | BT848_I2C_SDA)
+
+static int
+bttv_i2c_sendbytes(struct bttv *btv, const struct i2c_msg *msg, int last)
+{
+ u32 xmit;
+ int retval,cnt;
+
+ /* sanity checks */
+ if (0 == msg->len)
+ return -EINVAL;
+
+ /* start, address + first byte */
+ xmit = (msg->addr << 25) | (msg->buf[0] << 16) | I2C_HW;
+ if (msg->len > 1 || !last)
+ xmit |= BT878_I2C_NOSTOP;
+ btwrite(xmit, BT848_I2C);
+ retval = bttv_i2c_wait_done(btv);
+ if (retval < 0)
+ goto err;
+ if (retval == 0)
+ goto eio;
+ if (i2c_debug) {
+ pr_cont(" <W %02x %02x", msg->addr << 1, msg->buf[0]);
+ }
+
+ for (cnt = 1; cnt < msg->len; cnt++ ) {
+ /* following bytes */
+ xmit = (msg->buf[cnt] << 24) | I2C_HW | BT878_I2C_NOSTART;
+ if (cnt < msg->len-1 || !last)
+ xmit |= BT878_I2C_NOSTOP;
+ btwrite(xmit, BT848_I2C);
+ retval = bttv_i2c_wait_done(btv);
+ if (retval < 0)
+ goto err;
+ if (retval == 0)
+ goto eio;
+ if (i2c_debug)
+ pr_cont(" %02x", msg->buf[cnt]);
+ }
+ if (!(xmit & BT878_I2C_NOSTOP))
+ pr_cont(">\n");
+ return msg->len;
+
+ eio:
+ retval = -EIO;
+ err:
+ if (i2c_debug)
+ pr_cont(" ERR: %d\n",retval);
+ return retval;
+}
+
+static int
+bttv_i2c_readbytes(struct bttv *btv, const struct i2c_msg *msg, int last)
+{
+ u32 xmit;
+ u32 cnt;
+ int retval;
+
+ for (cnt = 0; cnt < msg->len; cnt++) {
+ xmit = (msg->addr << 25) | (1 << 24) | I2C_HW;
+ if (cnt < msg->len-1)
+ xmit |= BT848_I2C_W3B;
+ if (cnt < msg->len-1 || !last)
+ xmit |= BT878_I2C_NOSTOP;
+ if (cnt)
+ xmit |= BT878_I2C_NOSTART;
+
+ if (i2c_debug) {
+ if (!(xmit & BT878_I2C_NOSTART))
+ pr_cont(" <R %02x", (msg->addr << 1) +1);
+ }
+
+ btwrite(xmit, BT848_I2C);
+ retval = bttv_i2c_wait_done(btv);
+ if (retval < 0)
+ goto err;
+ if (retval == 0)
+ goto eio;
+ msg->buf[cnt] = ((u32)btread(BT848_I2C) >> 8) & 0xff;
+ if (i2c_debug) {
+ pr_cont(" =%02x", msg->buf[cnt]);
+ }
+ if (i2c_debug && !(xmit & BT878_I2C_NOSTOP))
+ pr_cont(" >\n");
+ }
+
+
+ return msg->len;
+
+ eio:
+ retval = -EIO;
+ err:
+ if (i2c_debug)
+ pr_cont(" ERR: %d\n",retval);
+ return retval;
+}
+
+static int bttv_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
+{
+ struct v4l2_device *v4l2_dev = i2c_get_adapdata(i2c_adap);
+ struct bttv *btv = to_bttv(v4l2_dev);
+ int retval = 0;
+ int i;
+
+ if (i2c_debug)
+ pr_debug("bt-i2c:");
+
+ btwrite(BT848_INT_I2CDONE|BT848_INT_RACK, BT848_INT_STAT);
+ for (i = 0 ; i < num; i++) {
+ if (msgs[i].flags & I2C_M_RD) {
+ /* read */
+ retval = bttv_i2c_readbytes(btv, &msgs[i], i+1 == num);
+ if (retval < 0)
+ goto err;
+ } else {
+ /* write */
+ retval = bttv_i2c_sendbytes(btv, &msgs[i], i+1 == num);
+ if (retval < 0)
+ goto err;
+ }
+ }
+ return num;
+
+ err:
+ return retval;
+}
+
+static const struct i2c_algorithm bttv_algo = {
+ .master_xfer = bttv_i2c_xfer,
+ .functionality = functionality,
+};
+
+/* ----------------------------------------------------------------------- */
+/* I2C functions - common stuff */
+
+/* read I2C */
+int bttv_I2CRead(struct bttv *btv, unsigned char addr, char *probe_for)
+{
+ unsigned char buffer = 0;
+
+ if (0 != btv->i2c_rc)
+ return -1;
+ if (bttv_verbose && NULL != probe_for)
+ pr_info("%d: i2c: checking for %s @ 0x%02x... ",
+ btv->c.nr, probe_for, addr);
+ btv->i2c_client.addr = addr >> 1;
+ if (1 != i2c_master_recv(&btv->i2c_client, &buffer, 1)) {
+ if (NULL != probe_for) {
+ if (bttv_verbose)
+ pr_cont("not found\n");
+ } else
+ pr_warn("%d: i2c read 0x%x: error\n",
+ btv->c.nr, addr);
+ return -1;
+ }
+ if (bttv_verbose && NULL != probe_for)
+ pr_cont("found\n");
+ return buffer;
+}
+
+/* write I2C */
+int bttv_I2CWrite(struct bttv *btv, unsigned char addr, unsigned char b1,
+ unsigned char b2, int both)
+{
+ unsigned char buffer[2];
+ int bytes = both ? 2 : 1;
+
+ if (0 != btv->i2c_rc)
+ return -1;
+ btv->i2c_client.addr = addr >> 1;
+ buffer[0] = b1;
+ buffer[1] = b2;
+ if (bytes != i2c_master_send(&btv->i2c_client, buffer, bytes))
+ return -1;
+ return 0;
+}
+
+/* read EEPROM content */
+void __devinit bttv_readee(struct bttv *btv, unsigned char *eedata, int addr)
+{
+ memset(eedata, 0, 256);
+ if (0 != btv->i2c_rc)
+ return;
+ btv->i2c_client.addr = addr >> 1;
+ tveeprom_read(&btv->i2c_client, eedata, 256);
+}
+
+static char *i2c_devs[128] = {
+ [ 0x1c >> 1 ] = "lgdt330x",
+ [ 0x30 >> 1 ] = "IR (hauppauge)",
+ [ 0x80 >> 1 ] = "msp34xx",
+ [ 0x86 >> 1 ] = "tda9887",
+ [ 0xa0 >> 1 ] = "eeprom",
+ [ 0xc0 >> 1 ] = "tuner (analog)",
+ [ 0xc2 >> 1 ] = "tuner (analog)",
+};
+
+static void do_i2c_scan(char *name, struct i2c_client *c)
+{
+ unsigned char buf;
+ int i,rc;
+
+ for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) {
+ c->addr = i;
+ rc = i2c_master_recv(c,&buf,0);
+ if (rc < 0)
+ continue;
+ pr_info("%s: i2c scan: found device @ 0x%x [%s]\n",
+ name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
+ }
+}
+
+/* init + register i2c adapter */
+int __devinit init_bttv_i2c(struct bttv *btv)
+{
+ strlcpy(btv->i2c_client.name, "bttv internal", I2C_NAME_SIZE);
+
+ if (i2c_hw)
+ btv->use_i2c_hw = 1;
+ if (btv->use_i2c_hw) {
+ /* bt878 */
+ strlcpy(btv->c.i2c_adap.name, "bt878",
+ sizeof(btv->c.i2c_adap.name));
+ btv->c.i2c_adap.algo = &bttv_algo;
+ } else {
+ /* bt848 */
+ /* Prevents usage of invalid delay values */
+ if (i2c_udelay<5)
+ i2c_udelay=5;
+
+ strlcpy(btv->c.i2c_adap.name, "bttv",
+ sizeof(btv->c.i2c_adap.name));
+ memcpy(&btv->i2c_algo, &bttv_i2c_algo_bit_template,
+ sizeof(bttv_i2c_algo_bit_template));
+ btv->i2c_algo.udelay = i2c_udelay;
+ btv->i2c_algo.data = btv;
+ btv->c.i2c_adap.algo_data = &btv->i2c_algo;
+ }
+ btv->c.i2c_adap.owner = THIS_MODULE;
+
+ btv->c.i2c_adap.dev.parent = &btv->c.pci->dev;
+ snprintf(btv->c.i2c_adap.name, sizeof(btv->c.i2c_adap.name),
+ "bt%d #%d [%s]", btv->id, btv->c.nr,
+ btv->use_i2c_hw ? "hw" : "sw");
+
+ i2c_set_adapdata(&btv->c.i2c_adap, &btv->c.v4l2_dev);
+ btv->i2c_client.adapter = &btv->c.i2c_adap;
+
+
+ if (btv->use_i2c_hw) {
+ btv->i2c_rc = i2c_add_adapter(&btv->c.i2c_adap);
+ } else {
+ bttv_bit_setscl(btv,1);
+ bttv_bit_setsda(btv,1);
+ btv->i2c_rc = i2c_bit_add_bus(&btv->c.i2c_adap);
+ }
+ if (0 == btv->i2c_rc && i2c_scan)
+ do_i2c_scan(btv->c.v4l2_dev.name, &btv->i2c_client);
+
+ return btv->i2c_rc;
+}
diff --git a/drivers/media/video/bt8xx/bttv-if.c b/drivers/media/video/bt8xx/bttv-if.c
new file mode 100644
index 00000000..a6a540dc
--- /dev/null
+++ b/drivers/media/video/bt8xx/bttv-if.c
@@ -0,0 +1,121 @@
+/*
+
+ bttv-if.c -- old gpio interface to other kernel modules
+ don't use in new code, will go away in 2.7
+ have a look at bttv-gpio.c instead.
+
+ bttv - Bt848 frame grabber driver
+
+ Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
+ & Marcus Metzler (mocm@thp.uni-koeln.de)
+ (c) 1999-2003 Gerd Knorr <kraxel@bytesex.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+#include "bttvp.h"
+
+EXPORT_SYMBOL(bttv_get_pcidev);
+EXPORT_SYMBOL(bttv_gpio_enable);
+EXPORT_SYMBOL(bttv_read_gpio);
+EXPORT_SYMBOL(bttv_write_gpio);
+
+/* ----------------------------------------------------------------------- */
+/* Exported functions - for other modules which want to access the */
+/* gpio ports (IR for example) */
+/* see bttv.h for comments */
+
+struct pci_dev* bttv_get_pcidev(unsigned int card)
+{
+ if (card >= bttv_num)
+ return NULL;
+ if (!bttvs[card])
+ return NULL;
+
+ return bttvs[card]->c.pci;
+}
+
+
+int bttv_gpio_enable(unsigned int card, unsigned long mask, unsigned long data)
+{
+ struct bttv *btv;
+
+ if (card >= bttv_num) {
+ return -EINVAL;
+ }
+
+ btv = bttvs[card];
+ if (!btv)
+ return -ENODEV;
+
+ gpio_inout(mask,data);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"extern enable");
+ return 0;
+}
+
+int bttv_read_gpio(unsigned int card, unsigned long *data)
+{
+ struct bttv *btv;
+
+ if (card >= bttv_num) {
+ return -EINVAL;
+ }
+
+ btv = bttvs[card];
+ if (!btv)
+ return -ENODEV;
+
+ if(btv->shutdown) {
+ return -ENODEV;
+ }
+
+/* prior setting BT848_GPIO_REG_INP is (probably) not needed
+ because we set direct input on init */
+ *data = gpio_read();
+ return 0;
+}
+
+int bttv_write_gpio(unsigned int card, unsigned long mask, unsigned long data)
+{
+ struct bttv *btv;
+
+ if (card >= bttv_num) {
+ return -EINVAL;
+ }
+
+ btv = bttvs[card];
+ if (!btv)
+ return -ENODEV;
+
+/* prior setting BT848_GPIO_REG_INP is (probably) not needed
+ because direct input is set on init */
+ gpio_bits(mask,data);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"extern write");
+ return 0;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c
new file mode 100644
index 00000000..ef4c7cd4
--- /dev/null
+++ b/drivers/media/video/bt8xx/bttv-input.c
@@ -0,0 +1,589 @@
+/*
+ *
+ * Copyright (c) 2003 Gerd Knorr
+ * Copyright (c) 2003 Pavel Machek
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+
+#include "bttv.h"
+#include "bttvp.h"
+
+
+static int ir_debug;
+module_param(ir_debug, int, 0644);
+
+static int ir_rc5_remote_gap = 885;
+module_param(ir_rc5_remote_gap, int, 0644);
+
+#undef dprintk
+#define dprintk(fmt, ...) \
+do { \
+ if (ir_debug >= 1) \
+ pr_info(fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define DEVNAME "bttv-input"
+
+#define MODULE_NAME "bttv"
+
+/* ---------------------------------------------------------------------- */
+
+static void ir_handle_key(struct bttv *btv)
+{
+ struct bttv_ir *ir = btv->remote;
+ u32 gpio,data;
+
+ /* read gpio value */
+ gpio = bttv_gpio_read(&btv->c);
+ if (ir->polling) {
+ if (ir->last_gpio == gpio)
+ return;
+ ir->last_gpio = gpio;
+ }
+
+ /* extract data */
+ data = ir_extract_bits(gpio, ir->mask_keycode);
+ dprintk("irq gpio=0x%x code=%d | %s%s%s\n",
+ gpio, data,
+ ir->polling ? "poll" : "irq",
+ (gpio & ir->mask_keydown) ? " down" : "",
+ (gpio & ir->mask_keyup) ? " up" : "");
+
+ if ((ir->mask_keydown && (gpio & ir->mask_keydown)) ||
+ (ir->mask_keyup && !(gpio & ir->mask_keyup))) {
+ rc_keydown_notimeout(ir->dev, data, 0);
+ } else {
+ /* HACK: Probably, ir->mask_keydown is missing
+ for this board */
+ if (btv->c.type == BTTV_BOARD_WINFAST2000)
+ rc_keydown_notimeout(ir->dev, data, 0);
+
+ rc_keyup(ir->dev);
+ }
+}
+
+static void ir_enltv_handle_key(struct bttv *btv)
+{
+ struct bttv_ir *ir = btv->remote;
+ u32 gpio, data, keyup;
+
+ /* read gpio value */
+ gpio = bttv_gpio_read(&btv->c);
+
+ /* extract data */
+ data = ir_extract_bits(gpio, ir->mask_keycode);
+
+ /* Check if it is keyup */
+ keyup = (gpio & ir->mask_keyup) ? 1 << 31 : 0;
+
+ if ((ir->last_gpio & 0x7f) != data) {
+ dprintk("gpio=0x%x code=%d | %s\n",
+ gpio, data,
+ (gpio & ir->mask_keyup) ? " up" : "up/down");
+
+ rc_keydown_notimeout(ir->dev, data, 0);
+ if (keyup)
+ rc_keyup(ir->dev);
+ } else {
+ if ((ir->last_gpio & 1 << 31) == keyup)
+ return;
+
+ dprintk("(cnt) gpio=0x%x code=%d | %s\n",
+ gpio, data,
+ (gpio & ir->mask_keyup) ? " up" : "down");
+
+ if (keyup)
+ rc_keyup(ir->dev);
+ else
+ rc_keydown_notimeout(ir->dev, data, 0);
+ }
+
+ ir->last_gpio = data | keyup;
+}
+
+static int bttv_rc5_irq(struct bttv *btv);
+
+void bttv_input_irq(struct bttv *btv)
+{
+ struct bttv_ir *ir = btv->remote;
+
+ if (ir->rc5_gpio)
+ bttv_rc5_irq(btv);
+ else if (!ir->polling)
+ ir_handle_key(btv);
+}
+
+static void bttv_input_timer(unsigned long data)
+{
+ struct bttv *btv = (struct bttv*)data;
+ struct bttv_ir *ir = btv->remote;
+
+ if (btv->c.type == BTTV_BOARD_ENLTV_FM_2)
+ ir_enltv_handle_key(btv);
+ else
+ ir_handle_key(btv);
+ mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling));
+}
+
+/*
+ * FIXME: Nebula digi uses the legacy way to decode RC5, instead of relying
+ * on the rc-core way. As we need to be sure that both IRQ transitions are
+ * properly triggered, Better to touch it only with this hardware for
+ * testing.
+ */
+
+#define RC5_START(x) (((x) >> 12) & 3)
+#define RC5_TOGGLE(x) (((x) >> 11) & 1)
+#define RC5_ADDR(x) (((x) >> 6) & 31)
+#define RC5_INSTR(x) ((x) & 63)
+
+/* decode raw bit pattern to RC5 code */
+static u32 bttv_rc5_decode(unsigned int code)
+{
+ unsigned int org_code = code;
+ unsigned int pair;
+ unsigned int rc5 = 0;
+ int i;
+
+ for (i = 0; i < 14; ++i) {
+ pair = code & 0x3;
+ code >>= 2;
+
+ rc5 <<= 1;
+ switch (pair) {
+ case 0:
+ case 2:
+ break;
+ case 1:
+ rc5 |= 1;
+ break;
+ case 3:
+ dprintk("rc5_decode(%x) bad code\n",
+ org_code);
+ return 0;
+ }
+ }
+ dprintk("code=%x, rc5=%x, start=%x, toggle=%x, address=%x, "
+ "instr=%x\n", rc5, org_code, RC5_START(rc5),
+ RC5_TOGGLE(rc5), RC5_ADDR(rc5), RC5_INSTR(rc5));
+ return rc5;
+}
+
+static void bttv_rc5_timer_end(unsigned long data)
+{
+ struct bttv_ir *ir = (struct bttv_ir *)data;
+ struct timeval tv;
+ u32 gap;
+ u32 rc5 = 0;
+
+ /* get time */
+ do_gettimeofday(&tv);
+
+ /* avoid overflow with gap >1s */
+ if (tv.tv_sec - ir->base_time.tv_sec > 1) {
+ gap = 200000;
+ } else {
+ gap = 1000000 * (tv.tv_sec - ir->base_time.tv_sec) +
+ tv.tv_usec - ir->base_time.tv_usec;
+ }
+
+ /* signal we're ready to start a new code */
+ ir->active = false;
+
+ /* Allow some timer jitter (RC5 is ~24ms anyway so this is ok) */
+ if (gap < 28000) {
+ dprintk("spurious timer_end\n");
+ return;
+ }
+
+ if (ir->last_bit < 20) {
+ /* ignore spurious codes (caused by light/other remotes) */
+ dprintk("short code: %x\n", ir->code);
+ } else {
+ ir->code = (ir->code << ir->shift_by) | 1;
+ rc5 = bttv_rc5_decode(ir->code);
+
+ /* two start bits? */
+ if (RC5_START(rc5) != ir->start) {
+ pr_info(DEVNAME ":"
+ " rc5 start bits invalid: %u\n", RC5_START(rc5));
+
+ /* right address? */
+ } else if (RC5_ADDR(rc5) == ir->addr) {
+ u32 toggle = RC5_TOGGLE(rc5);
+ u32 instr = RC5_INSTR(rc5);
+
+ /* Good code */
+ rc_keydown(ir->dev, instr, toggle);
+ dprintk("instruction %x, toggle %x\n",
+ instr, toggle);
+ }
+ }
+}
+
+static int bttv_rc5_irq(struct bttv *btv)
+{
+ struct bttv_ir *ir = btv->remote;
+ struct timeval tv;
+ u32 gpio;
+ u32 gap;
+ unsigned long current_jiffies;
+
+ /* read gpio port */
+ gpio = bttv_gpio_read(&btv->c);
+
+ /* get time of bit */
+ current_jiffies = jiffies;
+ do_gettimeofday(&tv);
+
+ /* avoid overflow with gap >1s */
+ if (tv.tv_sec - ir->base_time.tv_sec > 1) {
+ gap = 200000;
+ } else {
+ gap = 1000000 * (tv.tv_sec - ir->base_time.tv_sec) +
+ tv.tv_usec - ir->base_time.tv_usec;
+ }
+
+ dprintk("RC5 IRQ: gap %d us for %s\n",
+ gap, (gpio & 0x20) ? "mark" : "space");
+
+ /* remote IRQ? */
+ if (!(gpio & 0x20))
+ return 0;
+
+ /* active code => add bit */
+ if (ir->active) {
+ /* only if in the code (otherwise spurious IRQ or timer
+ late) */
+ if (ir->last_bit < 28) {
+ ir->last_bit = (gap - ir_rc5_remote_gap / 2) /
+ ir_rc5_remote_gap;
+ ir->code |= 1 << ir->last_bit;
+ }
+ /* starting new code */
+ } else {
+ ir->active = true;
+ ir->code = 0;
+ ir->base_time = tv;
+ ir->last_bit = 0;
+
+ mod_timer(&ir->timer, current_jiffies + msecs_to_jiffies(30));
+ }
+
+ /* toggle GPIO pin 4 to reset the irq */
+ bttv_gpio_write(&btv->c, gpio & ~(1 << 4));
+ bttv_gpio_write(&btv->c, gpio | (1 << 4));
+ return 1;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void bttv_ir_start(struct bttv *btv, struct bttv_ir *ir)
+{
+ if (ir->polling) {
+ setup_timer(&ir->timer, bttv_input_timer, (unsigned long)btv);
+ ir->timer.expires = jiffies + msecs_to_jiffies(1000);
+ add_timer(&ir->timer);
+ } else if (ir->rc5_gpio) {
+ /* set timer_end for code completion */
+ setup_timer(&ir->timer, bttv_rc5_timer_end, (unsigned long)ir);
+ ir->shift_by = 1;
+ ir->start = 3;
+ ir->addr = 0x0;
+ ir->rc5_remote_gap = ir_rc5_remote_gap;
+ }
+}
+
+static void bttv_ir_stop(struct bttv *btv)
+{
+ if (btv->remote->polling)
+ del_timer_sync(&btv->remote->timer);
+
+ if (btv->remote->rc5_gpio) {
+ u32 gpio;
+
+ del_timer_sync(&btv->remote->timer);
+
+ gpio = bttv_gpio_read(&btv->c);
+ bttv_gpio_write(&btv->c, gpio & ~(1 << 4));
+ }
+}
+
+/*
+ * Get_key functions used by I2C remotes
+ */
+
+static int get_key_pv951(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+ unsigned char b;
+
+ /* poll IR chip */
+ if (1 != i2c_master_recv(ir->c, &b, 1)) {
+ dprintk("read error\n");
+ return -EIO;
+ }
+
+ /* ignore 0xaa */
+ if (b==0xaa)
+ return 0;
+ dprintk("key %02x\n", b);
+
+ /*
+ * NOTE:
+ * lirc_i2c maps the pv951 code as:
+ * addr = 0x61D6
+ * cmd = bit_reverse (b)
+ * So, it seems that this device uses NEC extended
+ * I decided to not fix the table, due to two reasons:
+ * 1) Without the actual device, this is only a guess;
+ * 2) As the addr is not reported via I2C, nor can be changed,
+ * the device is bound to the vendor-provided RC.
+ */
+
+ *ir_key = b;
+ *ir_raw = b;
+ return 1;
+}
+
+/* Instantiate the I2C IR receiver device, if present */
+void __devinit init_bttv_i2c_ir(struct bttv *btv)
+{
+ const unsigned short addr_list[] = {
+ 0x1a, 0x18, 0x64, 0x30, 0x71,
+ I2C_CLIENT_END
+ };
+ struct i2c_board_info info;
+
+ if (0 != btv->i2c_rc)
+ return;
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ memset(&btv->init_data, 0, sizeof(btv->init_data));
+ strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
+
+ switch (btv->c.type) {
+ case BTTV_BOARD_PV951:
+ btv->init_data.name = "PV951";
+ btv->init_data.get_key = get_key_pv951;
+ btv->init_data.ir_codes = RC_MAP_PV951;
+ info.addr = 0x4b;
+ break;
+ default:
+ /*
+ * The external IR receiver is at i2c address 0x34 (0x35 for
+ * reads). Future Hauppauge cards will have an internal
+ * receiver at 0x30 (0x31 for reads). In theory, both can be
+ * fitted, and Hauppauge suggest an external overrides an
+ * internal.
+ * That's why we probe 0x1a (~0x34) first. CB
+ */
+
+ i2c_new_probed_device(&btv->c.i2c_adap, &info, addr_list, NULL);
+ return;
+ }
+
+ if (btv->init_data.name)
+ info.platform_data = &btv->init_data;
+ i2c_new_device(&btv->c.i2c_adap, &info);
+
+ return;
+}
+
+int __devexit fini_bttv_i2c(struct bttv *btv)
+{
+ if (0 != btv->i2c_rc)
+ return 0;
+
+ return i2c_del_adapter(&btv->c.i2c_adap);
+}
+
+int bttv_input_init(struct bttv *btv)
+{
+ struct bttv_ir *ir;
+ char *ir_codes = NULL;
+ struct rc_dev *rc;
+ int err = -ENOMEM;
+
+ if (!btv->has_remote)
+ return -ENODEV;
+
+ ir = kzalloc(sizeof(*ir),GFP_KERNEL);
+ rc = rc_allocate_device();
+ if (!ir || !rc)
+ goto err_out_free;
+
+ /* detect & configure */
+ switch (btv->c.type) {
+ case BTTV_BOARD_AVERMEDIA:
+ case BTTV_BOARD_AVPHONE98:
+ case BTTV_BOARD_AVERMEDIA98:
+ ir_codes = RC_MAP_AVERMEDIA;
+ ir->mask_keycode = 0xf88000;
+ ir->mask_keydown = 0x010000;
+ ir->polling = 50; // ms
+ break;
+
+ case BTTV_BOARD_AVDVBT_761:
+ case BTTV_BOARD_AVDVBT_771:
+ ir_codes = RC_MAP_AVERMEDIA_DVBT;
+ ir->mask_keycode = 0x0f00c0;
+ ir->mask_keydown = 0x000020;
+ ir->polling = 50; // ms
+ break;
+
+ case BTTV_BOARD_PXELVWPLTVPAK:
+ ir_codes = RC_MAP_PIXELVIEW;
+ ir->mask_keycode = 0x003e00;
+ ir->mask_keyup = 0x010000;
+ ir->polling = 50; // ms
+ break;
+ case BTTV_BOARD_PV_M4900:
+ case BTTV_BOARD_PV_BT878P_9B:
+ case BTTV_BOARD_PV_BT878P_PLUS:
+ ir_codes = RC_MAP_PIXELVIEW;
+ ir->mask_keycode = 0x001f00;
+ ir->mask_keyup = 0x008000;
+ ir->polling = 50; // ms
+ break;
+
+ case BTTV_BOARD_WINFAST2000:
+ ir_codes = RC_MAP_WINFAST;
+ ir->mask_keycode = 0x1f8;
+ break;
+ case BTTV_BOARD_MAGICTVIEW061:
+ case BTTV_BOARD_MAGICTVIEW063:
+ ir_codes = RC_MAP_WINFAST;
+ ir->mask_keycode = 0x0008e000;
+ ir->mask_keydown = 0x00200000;
+ break;
+ case BTTV_BOARD_APAC_VIEWCOMP:
+ ir_codes = RC_MAP_APAC_VIEWCOMP;
+ ir->mask_keycode = 0x001f00;
+ ir->mask_keyup = 0x008000;
+ ir->polling = 50; // ms
+ break;
+ case BTTV_BOARD_ASKEY_CPH03X:
+ case BTTV_BOARD_CONCEPTRONIC_CTVFMI2:
+ case BTTV_BOARD_CONTVFMI:
+ ir_codes = RC_MAP_PIXELVIEW;
+ ir->mask_keycode = 0x001F00;
+ ir->mask_keyup = 0x006000;
+ ir->polling = 50; // ms
+ break;
+ case BTTV_BOARD_NEBULA_DIGITV:
+ ir_codes = RC_MAP_NEBULA;
+ ir->rc5_gpio = true;
+ break;
+ case BTTV_BOARD_MACHTV_MAGICTV:
+ ir_codes = RC_MAP_APAC_VIEWCOMP;
+ ir->mask_keycode = 0x001F00;
+ ir->mask_keyup = 0x004000;
+ ir->polling = 50; /* ms */
+ break;
+ case BTTV_BOARD_KOZUMI_KTV_01C:
+ ir_codes = RC_MAP_PCTV_SEDNA;
+ ir->mask_keycode = 0x001f00;
+ ir->mask_keyup = 0x006000;
+ ir->polling = 50; /* ms */
+ break;
+ case BTTV_BOARD_ENLTV_FM_2:
+ ir_codes = RC_MAP_ENCORE_ENLTV2;
+ ir->mask_keycode = 0x00fd00;
+ ir->mask_keyup = 0x000080;
+ ir->polling = 1; /* ms */
+ ir->last_gpio = ir_extract_bits(bttv_gpio_read(&btv->c),
+ ir->mask_keycode);
+ break;
+ }
+ if (NULL == ir_codes) {
+ dprintk("Ooops: IR config error [card=%d]\n", btv->c.type);
+ err = -ENODEV;
+ goto err_out_free;
+ }
+
+ if (ir->rc5_gpio) {
+ u32 gpio;
+ /* enable remote irq */
+ bttv_gpio_inout(&btv->c, (1 << 4), 1 << 4);
+ gpio = bttv_gpio_read(&btv->c);
+ bttv_gpio_write(&btv->c, gpio & ~(1 << 4));
+ bttv_gpio_write(&btv->c, gpio | (1 << 4));
+ } else {
+ /* init hardware-specific stuff */
+ bttv_gpio_inout(&btv->c, ir->mask_keycode | ir->mask_keydown, 0);
+ }
+
+ /* init input device */
+ ir->dev = rc;
+
+ snprintf(ir->name, sizeof(ir->name), "bttv IR (card=%d)",
+ btv->c.type);
+ snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0",
+ pci_name(btv->c.pci));
+
+ rc->input_name = ir->name;
+ rc->input_phys = ir->phys;
+ rc->input_id.bustype = BUS_PCI;
+ rc->input_id.version = 1;
+ if (btv->c.pci->subsystem_vendor) {
+ rc->input_id.vendor = btv->c.pci->subsystem_vendor;
+ rc->input_id.product = btv->c.pci->subsystem_device;
+ } else {
+ rc->input_id.vendor = btv->c.pci->vendor;
+ rc->input_id.product = btv->c.pci->device;
+ }
+ rc->dev.parent = &btv->c.pci->dev;
+ rc->map_name = ir_codes;
+ rc->driver_name = MODULE_NAME;
+
+ btv->remote = ir;
+ bttv_ir_start(btv, ir);
+
+ /* all done */
+ err = rc_register_device(rc);
+ if (err)
+ goto err_out_stop;
+
+ return 0;
+
+ err_out_stop:
+ bttv_ir_stop(btv);
+ btv->remote = NULL;
+ err_out_free:
+ rc_free_device(rc);
+ kfree(ir);
+ return err;
+}
+
+void bttv_input_fini(struct bttv *btv)
+{
+ if (btv->remote == NULL)
+ return;
+
+ bttv_ir_stop(btv);
+ rc_unregister_device(btv->remote->dev);
+ kfree(btv->remote);
+ btv->remote = NULL;
+}
diff --git a/drivers/media/video/bt8xx/bttv-risc.c b/drivers/media/video/bt8xx/bttv-risc.c
new file mode 100644
index 00000000..82cc47d2
--- /dev/null
+++ b/drivers/media/video/bt8xx/bttv-risc.c
@@ -0,0 +1,909 @@
+/*
+
+ bttv-risc.c -- interfaces to other kernel modules
+
+ bttv risc code handling
+ - memory management
+ - generation
+
+ (c) 2000-2003 Gerd Knorr <kraxel@bytesex.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <media/v4l2-ioctl.h>
+
+#include "bttvp.h"
+
+#define VCR_HACK_LINES 4
+
+/* ---------------------------------------------------------- */
+/* risc code generators */
+
+int
+bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc,
+ struct scatterlist *sglist,
+ unsigned int offset, unsigned int bpl,
+ unsigned int padding, unsigned int skip_lines,
+ unsigned int store_lines)
+{
+ u32 instructions,line,todo;
+ struct scatterlist *sg;
+ __le32 *rp;
+ int rc;
+
+ /* estimate risc mem: worst case is one write per page border +
+ one write per scan line + sync + jump (all 2 dwords). padding
+ can cause next bpl to start close to a page border. First DMA
+ region may be smaller than PAGE_SIZE */
+ instructions = skip_lines * 4;
+ instructions += (1 + ((bpl + padding) * store_lines)
+ / PAGE_SIZE + store_lines) * 8;
+ instructions += 2 * 8;
+ if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,instructions)) < 0)
+ return rc;
+
+ /* sync instruction */
+ rp = risc->cpu;
+ *(rp++) = cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
+ *(rp++) = cpu_to_le32(0);
+
+ while (skip_lines-- > 0) {
+ *(rp++) = cpu_to_le32(BT848_RISC_SKIP | BT848_RISC_SOL |
+ BT848_RISC_EOL | bpl);
+ }
+
+ /* scan lines */
+ sg = sglist;
+ for (line = 0; line < store_lines; line++) {
+ if ((btv->opt_vcr_hack) &&
+ (line >= (store_lines - VCR_HACK_LINES)))
+ continue;
+ while (offset && offset >= sg_dma_len(sg)) {
+ offset -= sg_dma_len(sg);
+ sg++;
+ }
+ if (bpl <= sg_dma_len(sg)-offset) {
+ /* fits into current chunk */
+ *(rp++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL|
+ BT848_RISC_EOL|bpl);
+ *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
+ offset+=bpl;
+ } else {
+ /* scanline needs to be splitted */
+ todo = bpl;
+ *(rp++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL|
+ (sg_dma_len(sg)-offset));
+ *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
+ todo -= (sg_dma_len(sg)-offset);
+ offset = 0;
+ sg++;
+ while (todo > sg_dma_len(sg)) {
+ *(rp++)=cpu_to_le32(BT848_RISC_WRITE|
+ sg_dma_len(sg));
+ *(rp++)=cpu_to_le32(sg_dma_address(sg));
+ todo -= sg_dma_len(sg);
+ sg++;
+ }
+ *(rp++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_EOL|
+ todo);
+ *(rp++)=cpu_to_le32(sg_dma_address(sg));
+ offset += todo;
+ }
+ offset += padding;
+ }
+
+ /* save pointer to jmp instruction address */
+ risc->jmp = rp;
+ BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size);
+ return 0;
+}
+
+static int
+bttv_risc_planar(struct bttv *btv, struct btcx_riscmem *risc,
+ struct scatterlist *sglist,
+ unsigned int yoffset, unsigned int ybpl,
+ unsigned int ypadding, unsigned int ylines,
+ unsigned int uoffset, unsigned int voffset,
+ unsigned int hshift, unsigned int vshift,
+ unsigned int cpadding)
+{
+ unsigned int instructions,line,todo,ylen,chroma;
+ __le32 *rp;
+ u32 ri;
+ struct scatterlist *ysg;
+ struct scatterlist *usg;
+ struct scatterlist *vsg;
+ int topfield = (0 == yoffset);
+ int rc;
+
+ /* estimate risc mem: worst case is one write per page border +
+ one write per scan line (5 dwords)
+ plus sync + jump (2 dwords) */
+ instructions = ((3 + (ybpl + ypadding) * ylines * 2)
+ / PAGE_SIZE) + ylines;
+ instructions += 2;
+ if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,instructions*4*5)) < 0)
+ return rc;
+
+ /* sync instruction */
+ rp = risc->cpu;
+ *(rp++) = cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM3);
+ *(rp++) = cpu_to_le32(0);
+
+ /* scan lines */
+ ysg = sglist;
+ usg = sglist;
+ vsg = sglist;
+ for (line = 0; line < ylines; line++) {
+ if ((btv->opt_vcr_hack) &&
+ (line >= (ylines - VCR_HACK_LINES)))
+ continue;
+ switch (vshift) {
+ case 0:
+ chroma = 1;
+ break;
+ case 1:
+ if (topfield)
+ chroma = ((line & 1) == 0);
+ else
+ chroma = ((line & 1) == 1);
+ break;
+ case 2:
+ if (topfield)
+ chroma = ((line & 3) == 0);
+ else
+ chroma = ((line & 3) == 2);
+ break;
+ default:
+ chroma = 0;
+ break;
+ }
+
+ for (todo = ybpl; todo > 0; todo -= ylen) {
+ /* go to next sg entry if needed */
+ while (yoffset && yoffset >= sg_dma_len(ysg)) {
+ yoffset -= sg_dma_len(ysg);
+ ysg++;
+ }
+ while (uoffset && uoffset >= sg_dma_len(usg)) {
+ uoffset -= sg_dma_len(usg);
+ usg++;
+ }
+ while (voffset && voffset >= sg_dma_len(vsg)) {
+ voffset -= sg_dma_len(vsg);
+ vsg++;
+ }
+
+ /* calculate max number of bytes we can write */
+ ylen = todo;
+ if (yoffset + ylen > sg_dma_len(ysg))
+ ylen = sg_dma_len(ysg) - yoffset;
+ if (chroma) {
+ if (uoffset + (ylen>>hshift) > sg_dma_len(usg))
+ ylen = (sg_dma_len(usg) - uoffset) << hshift;
+ if (voffset + (ylen>>hshift) > sg_dma_len(vsg))
+ ylen = (sg_dma_len(vsg) - voffset) << hshift;
+ ri = BT848_RISC_WRITE123;
+ } else {
+ ri = BT848_RISC_WRITE1S23;
+ }
+ if (ybpl == todo)
+ ri |= BT848_RISC_SOL;
+ if (ylen == todo)
+ ri |= BT848_RISC_EOL;
+
+ /* write risc instruction */
+ *(rp++)=cpu_to_le32(ri | ylen);
+ *(rp++)=cpu_to_le32(((ylen >> hshift) << 16) |
+ (ylen >> hshift));
+ *(rp++)=cpu_to_le32(sg_dma_address(ysg)+yoffset);
+ yoffset += ylen;
+ if (chroma) {
+ *(rp++)=cpu_to_le32(sg_dma_address(usg)+uoffset);
+ uoffset += ylen >> hshift;
+ *(rp++)=cpu_to_le32(sg_dma_address(vsg)+voffset);
+ voffset += ylen >> hshift;
+ }
+ }
+ yoffset += ypadding;
+ if (chroma) {
+ uoffset += cpadding;
+ voffset += cpadding;
+ }
+ }
+
+ /* save pointer to jmp instruction address */
+ risc->jmp = rp;
+ BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size);
+ return 0;
+}
+
+static int
+bttv_risc_overlay(struct bttv *btv, struct btcx_riscmem *risc,
+ const struct bttv_format *fmt, struct bttv_overlay *ov,
+ int skip_even, int skip_odd)
+{
+ int dwords, rc, line, maxy, start, end;
+ unsigned skip, nskips;
+ struct btcx_skiplist *skips;
+ __le32 *rp;
+ u32 ri,ra;
+ u32 addr;
+
+ /* skip list for window clipping */
+ if (NULL == (skips = kmalloc(sizeof(*skips) * ov->nclips,GFP_KERNEL)))
+ return -ENOMEM;
+
+ /* estimate risc mem: worst case is (1.5*clip+1) * lines instructions
+ + sync + jump (all 2 dwords) */
+ dwords = (3 * ov->nclips + 2) *
+ ((skip_even || skip_odd) ? (ov->w.height+1)>>1 : ov->w.height);
+ dwords += 4;
+ if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,dwords*4)) < 0) {
+ kfree(skips);
+ return rc;
+ }
+
+ /* sync instruction */
+ rp = risc->cpu;
+ *(rp++) = cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
+ *(rp++) = cpu_to_le32(0);
+
+ addr = (unsigned long)btv->fbuf.base;
+ addr += btv->fbuf.fmt.bytesperline * ov->w.top;
+ addr += (fmt->depth >> 3) * ov->w.left;
+
+ /* scan lines */
+ for (maxy = -1, line = 0; line < ov->w.height;
+ line++, addr += btv->fbuf.fmt.bytesperline) {
+ if ((btv->opt_vcr_hack) &&
+ (line >= (ov->w.height - VCR_HACK_LINES)))
+ continue;
+ if ((line%2) == 0 && skip_even)
+ continue;
+ if ((line%2) == 1 && skip_odd)
+ continue;
+
+ /* calculate clipping */
+ if (line > maxy)
+ btcx_calc_skips(line, ov->w.width, &maxy,
+ skips, &nskips, ov->clips, ov->nclips);
+
+ /* write out risc code */
+ for (start = 0, skip = 0; start < ov->w.width; start = end) {
+ if (skip >= nskips) {
+ ri = BT848_RISC_WRITE;
+ end = ov->w.width;
+ } else if (start < skips[skip].start) {
+ ri = BT848_RISC_WRITE;
+ end = skips[skip].start;
+ } else {
+ ri = BT848_RISC_SKIP;
+ end = skips[skip].end;
+ skip++;
+ }
+ if (BT848_RISC_WRITE == ri)
+ ra = addr + (fmt->depth>>3)*start;
+ else
+ ra = 0;
+
+ if (0 == start)
+ ri |= BT848_RISC_SOL;
+ if (ov->w.width == end)
+ ri |= BT848_RISC_EOL;
+ ri |= (fmt->depth>>3) * (end-start);
+
+ *(rp++)=cpu_to_le32(ri);
+ if (0 != ra)
+ *(rp++)=cpu_to_le32(ra);
+ }
+ }
+
+ /* save pointer to jmp instruction address */
+ risc->jmp = rp;
+ BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size);
+ kfree(skips);
+ return 0;
+}
+
+/* ---------------------------------------------------------- */
+
+static void
+bttv_calc_geo_old(struct bttv *btv, struct bttv_geometry *geo,
+ int width, int height, int interleaved,
+ const struct bttv_tvnorm *tvnorm)
+{
+ u32 xsf, sr;
+ int vdelay;
+
+ int swidth = tvnorm->swidth;
+ int totalwidth = tvnorm->totalwidth;
+ int scaledtwidth = tvnorm->scaledtwidth;
+
+ if (btv->input == btv->dig) {
+ swidth = 720;
+ totalwidth = 858;
+ scaledtwidth = 858;
+ }
+
+ vdelay = tvnorm->vdelay;
+
+ xsf = (width*scaledtwidth)/swidth;
+ geo->hscale = ((totalwidth*4096UL)/xsf-4096);
+ geo->hdelay = tvnorm->hdelayx1;
+ geo->hdelay = (geo->hdelay*width)/swidth;
+ geo->hdelay &= 0x3fe;
+ sr = ((tvnorm->sheight >> (interleaved?0:1))*512)/height - 512;
+ geo->vscale = (0x10000UL-sr) & 0x1fff;
+ geo->crop = ((width>>8)&0x03) | ((geo->hdelay>>6)&0x0c) |
+ ((tvnorm->sheight>>4)&0x30) | ((vdelay>>2)&0xc0);
+ geo->vscale |= interleaved ? (BT848_VSCALE_INT<<8) : 0;
+ geo->vdelay = vdelay;
+ geo->width = width;
+ geo->sheight = tvnorm->sheight;
+ geo->vtotal = tvnorm->vtotal;
+
+ if (btv->opt_combfilter) {
+ geo->vtc = (width < 193) ? 2 : ((width < 385) ? 1 : 0);
+ geo->comb = (width < 769) ? 1 : 0;
+ } else {
+ geo->vtc = 0;
+ geo->comb = 0;
+ }
+}
+
+static void
+bttv_calc_geo (struct bttv * btv,
+ struct bttv_geometry * geo,
+ unsigned int width,
+ unsigned int height,
+ int both_fields,
+ const struct bttv_tvnorm * tvnorm,
+ const struct v4l2_rect * crop)
+{
+ unsigned int c_width;
+ unsigned int c_height;
+ u32 sr;
+
+ if ((crop->left == tvnorm->cropcap.defrect.left
+ && crop->top == tvnorm->cropcap.defrect.top
+ && crop->width == tvnorm->cropcap.defrect.width
+ && crop->height == tvnorm->cropcap.defrect.height
+ && width <= tvnorm->swidth /* see PAL-Nc et al */)
+ || btv->input == btv->dig) {
+ bttv_calc_geo_old(btv, geo, width, height,
+ both_fields, tvnorm);
+ return;
+ }
+
+ /* For bug compatibility the image size checks permit scale
+ factors > 16. See bttv_crop_calc_limits(). */
+ c_width = min((unsigned int) crop->width, width * 16);
+ c_height = min((unsigned int) crop->height, height * 16);
+
+ geo->width = width;
+ geo->hscale = (c_width * 4096U + (width >> 1)) / width - 4096;
+ /* Even to store Cb first, odd for Cr. */
+ geo->hdelay = ((crop->left * width + c_width) / c_width) & ~1;
+
+ geo->sheight = c_height;
+ geo->vdelay = crop->top - tvnorm->cropcap.bounds.top + MIN_VDELAY;
+ sr = c_height >> !both_fields;
+ sr = (sr * 512U + (height >> 1)) / height - 512;
+ geo->vscale = (0x10000UL - sr) & 0x1fff;
+ geo->vscale |= both_fields ? (BT848_VSCALE_INT << 8) : 0;
+ geo->vtotal = tvnorm->vtotal;
+
+ geo->crop = (((geo->width >> 8) & 0x03) |
+ ((geo->hdelay >> 6) & 0x0c) |
+ ((geo->sheight >> 4) & 0x30) |
+ ((geo->vdelay >> 2) & 0xc0));
+
+ if (btv->opt_combfilter) {
+ geo->vtc = (width < 193) ? 2 : ((width < 385) ? 1 : 0);
+ geo->comb = (width < 769) ? 1 : 0;
+ } else {
+ geo->vtc = 0;
+ geo->comb = 0;
+ }
+}
+
+static void
+bttv_apply_geo(struct bttv *btv, struct bttv_geometry *geo, int odd)
+{
+ int off = odd ? 0x80 : 0x00;
+
+ if (geo->comb)
+ btor(BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off);
+ else
+ btand(~BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off);
+
+ btwrite(geo->vtc, BT848_E_VTC+off);
+ btwrite(geo->hscale >> 8, BT848_E_HSCALE_HI+off);
+ btwrite(geo->hscale & 0xff, BT848_E_HSCALE_LO+off);
+ btaor((geo->vscale>>8), 0xe0, BT848_E_VSCALE_HI+off);
+ btwrite(geo->vscale & 0xff, BT848_E_VSCALE_LO+off);
+ btwrite(geo->width & 0xff, BT848_E_HACTIVE_LO+off);
+ btwrite(geo->hdelay & 0xff, BT848_E_HDELAY_LO+off);
+ btwrite(geo->sheight & 0xff, BT848_E_VACTIVE_LO+off);
+ btwrite(geo->vdelay & 0xff, BT848_E_VDELAY_LO+off);
+ btwrite(geo->crop, BT848_E_CROP+off);
+ btwrite(geo->vtotal>>8, BT848_VTOTAL_HI);
+ btwrite(geo->vtotal & 0xff, BT848_VTOTAL_LO);
+}
+
+/* ---------------------------------------------------------- */
+/* risc group / risc main loop / dma management */
+
+void
+bttv_set_dma(struct bttv *btv, int override)
+{
+ unsigned long cmd;
+ int capctl;
+
+ btv->cap_ctl = 0;
+ if (NULL != btv->curr.top) btv->cap_ctl |= 0x02;
+ if (NULL != btv->curr.bottom) btv->cap_ctl |= 0x01;
+ if (NULL != btv->cvbi) btv->cap_ctl |= 0x0c;
+
+ capctl = 0;
+ capctl |= (btv->cap_ctl & 0x03) ? 0x03 : 0x00; /* capture */
+ capctl |= (btv->cap_ctl & 0x0c) ? 0x0c : 0x00; /* vbi data */
+ capctl |= override;
+
+ d2printk("%d: capctl=%x lirq=%d top=%08llx/%08llx even=%08llx/%08llx\n",
+ btv->c.nr,capctl,btv->loop_irq,
+ btv->cvbi ? (unsigned long long)btv->cvbi->top.dma : 0,
+ btv->curr.top ? (unsigned long long)btv->curr.top->top.dma : 0,
+ btv->cvbi ? (unsigned long long)btv->cvbi->bottom.dma : 0,
+ btv->curr.bottom ? (unsigned long long)btv->curr.bottom->bottom.dma : 0);
+
+ cmd = BT848_RISC_JUMP;
+ if (btv->loop_irq) {
+ cmd |= BT848_RISC_IRQ;
+ cmd |= (btv->loop_irq & 0x0f) << 16;
+ cmd |= (~btv->loop_irq & 0x0f) << 20;
+ }
+ if (btv->curr.frame_irq || btv->loop_irq || btv->cvbi) {
+ mod_timer(&btv->timeout, jiffies+BTTV_TIMEOUT);
+ } else {
+ del_timer(&btv->timeout);
+ }
+ btv->main.cpu[RISC_SLOT_LOOP] = cpu_to_le32(cmd);
+
+ btaor(capctl, ~0x0f, BT848_CAP_CTL);
+ if (capctl) {
+ if (btv->dma_on)
+ return;
+ btwrite(btv->main.dma, BT848_RISC_STRT_ADD);
+ btor(3, BT848_GPIO_DMA_CTL);
+ btv->dma_on = 1;
+ } else {
+ if (!btv->dma_on)
+ return;
+ btand(~3, BT848_GPIO_DMA_CTL);
+ btv->dma_on = 0;
+ }
+ return;
+}
+
+int
+bttv_risc_init_main(struct bttv *btv)
+{
+ int rc;
+
+ if ((rc = btcx_riscmem_alloc(btv->c.pci,&btv->main,PAGE_SIZE)) < 0)
+ return rc;
+ dprintk("%d: risc main @ %08llx\n",
+ btv->c.nr, (unsigned long long)btv->main.dma);
+
+ btv->main.cpu[0] = cpu_to_le32(BT848_RISC_SYNC | BT848_RISC_RESYNC |
+ BT848_FIFO_STATUS_VRE);
+ btv->main.cpu[1] = cpu_to_le32(0);
+ btv->main.cpu[2] = cpu_to_le32(BT848_RISC_JUMP);
+ btv->main.cpu[3] = cpu_to_le32(btv->main.dma + (4<<2));
+
+ /* top field */
+ btv->main.cpu[4] = cpu_to_le32(BT848_RISC_JUMP);
+ btv->main.cpu[5] = cpu_to_le32(btv->main.dma + (6<<2));
+ btv->main.cpu[6] = cpu_to_le32(BT848_RISC_JUMP);
+ btv->main.cpu[7] = cpu_to_le32(btv->main.dma + (8<<2));
+
+ btv->main.cpu[8] = cpu_to_le32(BT848_RISC_SYNC | BT848_RISC_RESYNC |
+ BT848_FIFO_STATUS_VRO);
+ btv->main.cpu[9] = cpu_to_le32(0);
+
+ /* bottom field */
+ btv->main.cpu[10] = cpu_to_le32(BT848_RISC_JUMP);
+ btv->main.cpu[11] = cpu_to_le32(btv->main.dma + (12<<2));
+ btv->main.cpu[12] = cpu_to_le32(BT848_RISC_JUMP);
+ btv->main.cpu[13] = cpu_to_le32(btv->main.dma + (14<<2));
+
+ /* jump back to top field */
+ btv->main.cpu[14] = cpu_to_le32(BT848_RISC_JUMP);
+ btv->main.cpu[15] = cpu_to_le32(btv->main.dma + (0<<2));
+
+ return 0;
+}
+
+int
+bttv_risc_hook(struct bttv *btv, int slot, struct btcx_riscmem *risc,
+ int irqflags)
+{
+ unsigned long cmd;
+ unsigned long next = btv->main.dma + ((slot+2) << 2);
+
+ if (NULL == risc) {
+ d2printk("%d: risc=%p slot[%d]=NULL\n", btv->c.nr, risc, slot);
+ btv->main.cpu[slot+1] = cpu_to_le32(next);
+ } else {
+ d2printk("%d: risc=%p slot[%d]=%08llx irq=%d\n",
+ btv->c.nr, risc, slot,
+ (unsigned long long)risc->dma, irqflags);
+ cmd = BT848_RISC_JUMP;
+ if (irqflags) {
+ cmd |= BT848_RISC_IRQ;
+ cmd |= (irqflags & 0x0f) << 16;
+ cmd |= (~irqflags & 0x0f) << 20;
+ }
+ risc->jmp[0] = cpu_to_le32(cmd);
+ risc->jmp[1] = cpu_to_le32(next);
+ btv->main.cpu[slot+1] = cpu_to_le32(risc->dma);
+ }
+ return 0;
+}
+
+void
+bttv_dma_free(struct videobuf_queue *q,struct bttv *btv, struct bttv_buffer *buf)
+{
+ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+
+ BUG_ON(in_interrupt());
+ videobuf_waiton(q, &buf->vb, 0, 0);
+ videobuf_dma_unmap(q->dev, dma);
+ videobuf_dma_free(dma);
+ btcx_riscmem_free(btv->c.pci,&buf->bottom);
+ btcx_riscmem_free(btv->c.pci,&buf->top);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+int
+bttv_buffer_activate_vbi(struct bttv *btv,
+ struct bttv_buffer *vbi)
+{
+ struct btcx_riscmem *top;
+ struct btcx_riscmem *bottom;
+ int top_irq_flags;
+ int bottom_irq_flags;
+
+ top = NULL;
+ bottom = NULL;
+ top_irq_flags = 0;
+ bottom_irq_flags = 0;
+
+ if (vbi) {
+ unsigned int crop, vdelay;
+
+ vbi->vb.state = VIDEOBUF_ACTIVE;
+ list_del(&vbi->vb.queue);
+
+ /* VDELAY is start of video, end of VBI capturing. */
+ crop = btread(BT848_E_CROP);
+ vdelay = btread(BT848_E_VDELAY_LO) + ((crop & 0xc0) << 2);
+
+ if (vbi->geo.vdelay > vdelay) {
+ vdelay = vbi->geo.vdelay & 0xfe;
+ crop = (crop & 0x3f) | ((vbi->geo.vdelay >> 2) & 0xc0);
+
+ btwrite(vdelay, BT848_E_VDELAY_LO);
+ btwrite(crop, BT848_E_CROP);
+ btwrite(vdelay, BT848_O_VDELAY_LO);
+ btwrite(crop, BT848_O_CROP);
+ }
+
+ if (vbi->vbi_count[0] > 0) {
+ top = &vbi->top;
+ top_irq_flags = 4;
+ }
+
+ if (vbi->vbi_count[1] > 0) {
+ top_irq_flags = 0;
+ bottom = &vbi->bottom;
+ bottom_irq_flags = 4;
+ }
+ }
+
+ bttv_risc_hook(btv, RISC_SLOT_O_VBI, top, top_irq_flags);
+ bttv_risc_hook(btv, RISC_SLOT_E_VBI, bottom, bottom_irq_flags);
+
+ return 0;
+}
+
+int
+bttv_buffer_activate_video(struct bttv *btv,
+ struct bttv_buffer_set *set)
+{
+ /* video capture */
+ if (NULL != set->top && NULL != set->bottom) {
+ if (set->top == set->bottom) {
+ set->top->vb.state = VIDEOBUF_ACTIVE;
+ if (set->top->vb.queue.next)
+ list_del(&set->top->vb.queue);
+ } else {
+ set->top->vb.state = VIDEOBUF_ACTIVE;
+ set->bottom->vb.state = VIDEOBUF_ACTIVE;
+ if (set->top->vb.queue.next)
+ list_del(&set->top->vb.queue);
+ if (set->bottom->vb.queue.next)
+ list_del(&set->bottom->vb.queue);
+ }
+ bttv_apply_geo(btv, &set->top->geo, 1);
+ bttv_apply_geo(btv, &set->bottom->geo,0);
+ bttv_risc_hook(btv, RISC_SLOT_O_FIELD, &set->top->top,
+ set->top_irq);
+ bttv_risc_hook(btv, RISC_SLOT_E_FIELD, &set->bottom->bottom,
+ set->frame_irq);
+ btaor((set->top->btformat & 0xf0) | (set->bottom->btformat & 0x0f),
+ ~0xff, BT848_COLOR_FMT);
+ btaor((set->top->btswap & 0x0a) | (set->bottom->btswap & 0x05),
+ ~0x0f, BT848_COLOR_CTL);
+ } else if (NULL != set->top) {
+ set->top->vb.state = VIDEOBUF_ACTIVE;
+ if (set->top->vb.queue.next)
+ list_del(&set->top->vb.queue);
+ bttv_apply_geo(btv, &set->top->geo,1);
+ bttv_apply_geo(btv, &set->top->geo,0);
+ bttv_risc_hook(btv, RISC_SLOT_O_FIELD, &set->top->top,
+ set->frame_irq);
+ bttv_risc_hook(btv, RISC_SLOT_E_FIELD, NULL, 0);
+ btaor(set->top->btformat & 0xff, ~0xff, BT848_COLOR_FMT);
+ btaor(set->top->btswap & 0x0f, ~0x0f, BT848_COLOR_CTL);
+ } else if (NULL != set->bottom) {
+ set->bottom->vb.state = VIDEOBUF_ACTIVE;
+ if (set->bottom->vb.queue.next)
+ list_del(&set->bottom->vb.queue);
+ bttv_apply_geo(btv, &set->bottom->geo,1);
+ bttv_apply_geo(btv, &set->bottom->geo,0);
+ bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0);
+ bttv_risc_hook(btv, RISC_SLOT_E_FIELD, &set->bottom->bottom,
+ set->frame_irq);
+ btaor(set->bottom->btformat & 0xff, ~0xff, BT848_COLOR_FMT);
+ btaor(set->bottom->btswap & 0x0f, ~0x0f, BT848_COLOR_CTL);
+ } else {
+ bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0);
+ bttv_risc_hook(btv, RISC_SLOT_E_FIELD, NULL, 0);
+ }
+ return 0;
+}
+
+/* ---------------------------------------------------------- */
+
+/* calculate geometry, build risc code */
+int
+bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf)
+{
+ const struct bttv_tvnorm *tvnorm = bttv_tvnorms + buf->tvnorm;
+ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+
+ dprintk("%d: buffer field: %s format: %s size: %dx%d\n",
+ btv->c.nr, v4l2_field_names[buf->vb.field],
+ buf->fmt->name, buf->vb.width, buf->vb.height);
+
+ /* packed pixel modes */
+ if (buf->fmt->flags & FORMAT_FLAGS_PACKED) {
+ int bpl = (buf->fmt->depth >> 3) * buf->vb.width;
+ int bpf = bpl * (buf->vb.height >> 1);
+
+ bttv_calc_geo(btv,&buf->geo,buf->vb.width,buf->vb.height,
+ V4L2_FIELD_HAS_BOTH(buf->vb.field),
+ tvnorm,&buf->crop);
+
+ switch (buf->vb.field) {
+ case V4L2_FIELD_TOP:
+ bttv_risc_packed(btv,&buf->top,dma->sglist,
+ /* offset */ 0,bpl,
+ /* padding */ 0,/* skip_lines */ 0,
+ buf->vb.height);
+ break;
+ case V4L2_FIELD_BOTTOM:
+ bttv_risc_packed(btv,&buf->bottom,dma->sglist,
+ 0,bpl,0,0,buf->vb.height);
+ break;
+ case V4L2_FIELD_INTERLACED:
+ bttv_risc_packed(btv,&buf->top,dma->sglist,
+ 0,bpl,bpl,0,buf->vb.height >> 1);
+ bttv_risc_packed(btv,&buf->bottom,dma->sglist,
+ bpl,bpl,bpl,0,buf->vb.height >> 1);
+ break;
+ case V4L2_FIELD_SEQ_TB:
+ bttv_risc_packed(btv,&buf->top,dma->sglist,
+ 0,bpl,0,0,buf->vb.height >> 1);
+ bttv_risc_packed(btv,&buf->bottom,dma->sglist,
+ bpf,bpl,0,0,buf->vb.height >> 1);
+ break;
+ default:
+ BUG();
+ }
+ }
+
+ /* planar modes */
+ if (buf->fmt->flags & FORMAT_FLAGS_PLANAR) {
+ int uoffset, voffset;
+ int ypadding, cpadding, lines;
+
+ /* calculate chroma offsets */
+ uoffset = buf->vb.width * buf->vb.height;
+ voffset = buf->vb.width * buf->vb.height;
+ if (buf->fmt->flags & FORMAT_FLAGS_CrCb) {
+ /* Y-Cr-Cb plane order */
+ uoffset >>= buf->fmt->hshift;
+ uoffset >>= buf->fmt->vshift;
+ uoffset += voffset;
+ } else {
+ /* Y-Cb-Cr plane order */
+ voffset >>= buf->fmt->hshift;
+ voffset >>= buf->fmt->vshift;
+ voffset += uoffset;
+ }
+
+ switch (buf->vb.field) {
+ case V4L2_FIELD_TOP:
+ bttv_calc_geo(btv,&buf->geo,buf->vb.width,
+ buf->vb.height,/* both_fields */ 0,
+ tvnorm,&buf->crop);
+ bttv_risc_planar(btv, &buf->top, dma->sglist,
+ 0,buf->vb.width,0,buf->vb.height,
+ uoffset,voffset,buf->fmt->hshift,
+ buf->fmt->vshift,0);
+ break;
+ case V4L2_FIELD_BOTTOM:
+ bttv_calc_geo(btv,&buf->geo,buf->vb.width,
+ buf->vb.height,0,
+ tvnorm,&buf->crop);
+ bttv_risc_planar(btv, &buf->bottom, dma->sglist,
+ 0,buf->vb.width,0,buf->vb.height,
+ uoffset,voffset,buf->fmt->hshift,
+ buf->fmt->vshift,0);
+ break;
+ case V4L2_FIELD_INTERLACED:
+ bttv_calc_geo(btv,&buf->geo,buf->vb.width,
+ buf->vb.height,1,
+ tvnorm,&buf->crop);
+ lines = buf->vb.height >> 1;
+ ypadding = buf->vb.width;
+ cpadding = buf->vb.width >> buf->fmt->hshift;
+ bttv_risc_planar(btv,&buf->top,
+ dma->sglist,
+ 0,buf->vb.width,ypadding,lines,
+ uoffset,voffset,
+ buf->fmt->hshift,
+ buf->fmt->vshift,
+ cpadding);
+ bttv_risc_planar(btv,&buf->bottom,
+ dma->sglist,
+ ypadding,buf->vb.width,ypadding,lines,
+ uoffset+cpadding,
+ voffset+cpadding,
+ buf->fmt->hshift,
+ buf->fmt->vshift,
+ cpadding);
+ break;
+ case V4L2_FIELD_SEQ_TB:
+ bttv_calc_geo(btv,&buf->geo,buf->vb.width,
+ buf->vb.height,1,
+ tvnorm,&buf->crop);
+ lines = buf->vb.height >> 1;
+ ypadding = buf->vb.width;
+ cpadding = buf->vb.width >> buf->fmt->hshift;
+ bttv_risc_planar(btv,&buf->top,
+ dma->sglist,
+ 0,buf->vb.width,0,lines,
+ uoffset >> 1,
+ voffset >> 1,
+ buf->fmt->hshift,
+ buf->fmt->vshift,
+ 0);
+ bttv_risc_planar(btv,&buf->bottom,
+ dma->sglist,
+ lines * ypadding,buf->vb.width,0,lines,
+ lines * ypadding + (uoffset >> 1),
+ lines * ypadding + (voffset >> 1),
+ buf->fmt->hshift,
+ buf->fmt->vshift,
+ 0);
+ break;
+ default:
+ BUG();
+ }
+ }
+
+ /* raw data */
+ if (buf->fmt->flags & FORMAT_FLAGS_RAW) {
+ /* build risc code */
+ buf->vb.field = V4L2_FIELD_SEQ_TB;
+ bttv_calc_geo(btv,&buf->geo,tvnorm->swidth,tvnorm->sheight,
+ 1,tvnorm,&buf->crop);
+ bttv_risc_packed(btv, &buf->top, dma->sglist,
+ /* offset */ 0, RAW_BPL, /* padding */ 0,
+ /* skip_lines */ 0, RAW_LINES);
+ bttv_risc_packed(btv, &buf->bottom, dma->sglist,
+ buf->vb.size/2 , RAW_BPL, 0, 0, RAW_LINES);
+ }
+
+ /* copy format info */
+ buf->btformat = buf->fmt->btformat;
+ buf->btswap = buf->fmt->btswap;
+ return 0;
+}
+
+/* ---------------------------------------------------------- */
+
+/* calculate geometry, build risc code */
+int
+bttv_overlay_risc(struct bttv *btv,
+ struct bttv_overlay *ov,
+ const struct bttv_format *fmt,
+ struct bttv_buffer *buf)
+{
+ /* check interleave, bottom+top fields */
+ dprintk("%d: overlay fields: %s format: %s size: %dx%d\n",
+ btv->c.nr, v4l2_field_names[buf->vb.field],
+ fmt->name, ov->w.width, ov->w.height);
+
+ /* calculate geometry */
+ bttv_calc_geo(btv,&buf->geo,ov->w.width,ov->w.height,
+ V4L2_FIELD_HAS_BOTH(ov->field),
+ &bttv_tvnorms[ov->tvnorm],&buf->crop);
+
+ /* build risc code */
+ switch (ov->field) {
+ case V4L2_FIELD_TOP:
+ bttv_risc_overlay(btv, &buf->top, fmt, ov, 0, 0);
+ break;
+ case V4L2_FIELD_BOTTOM:
+ bttv_risc_overlay(btv, &buf->bottom, fmt, ov, 0, 0);
+ break;
+ case V4L2_FIELD_INTERLACED:
+ bttv_risc_overlay(btv, &buf->top, fmt, ov, 0, 1);
+ bttv_risc_overlay(btv, &buf->bottom, fmt, ov, 1, 0);
+ break;
+ default:
+ BUG();
+ }
+
+ /* copy format info */
+ buf->btformat = fmt->btformat;
+ buf->btswap = fmt->btswap;
+ buf->vb.field = ov->field;
+ return 0;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/bt8xx/bttv-vbi.c b/drivers/media/video/bt8xx/bttv-vbi.c
new file mode 100644
index 00000000..b433267d
--- /dev/null
+++ b/drivers/media/video/bt8xx/bttv-vbi.c
@@ -0,0 +1,459 @@
+/*
+
+ bttv - Bt848 frame grabber driver
+ vbi interface
+
+ (c) 2002 Gerd Knorr <kraxel@bytesex.org>
+
+ Copyright (C) 2005, 2006 Michael H. Schimek <mschimek@gmx.at>
+ Sponsored by OPQ Systems AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/kdev_t.h>
+#include <media/v4l2-ioctl.h>
+#include <asm/io.h>
+#include "bttvp.h"
+
+/* Offset from line sync pulse leading edge (0H) to start of VBI capture,
+ in fCLKx2 pixels. According to the datasheet, VBI capture starts
+ VBI_HDELAY fCLKx1 pixels from the tailing edgeof /HRESET, and /HRESET
+ is 64 fCLKx1 pixels wide. VBI_HDELAY is set to 0, so this should be
+ (64 + 0) * 2 = 128 fCLKx2 pixels. But it's not! The datasheet is
+ Just Plain Wrong. The real value appears to be different for
+ different revisions of the bt8x8 chips, and to be affected by the
+ horizontal scaling factor. Experimentally, the value is measured
+ to be about 244. */
+#define VBI_OFFSET 244
+
+/* 2048 for compatibility with earlier driver versions. The driver
+ really stores 1024 + tvnorm->vbipack * 4 samples per line in the
+ buffer. Note tvnorm->vbipack is <= 0xFF (limit of VBIPACK_LO + HI
+ is 0x1FF DWORDs) and VBI read()s store a frame counter in the last
+ four bytes of the VBI image. */
+#define VBI_BPL 2048
+
+/* Compatibility. */
+#define VBI_DEFLINES 16
+
+static unsigned int vbibufs = 4;
+static unsigned int vbi_debug;
+
+module_param(vbibufs, int, 0444);
+module_param(vbi_debug, int, 0644);
+MODULE_PARM_DESC(vbibufs,"number of vbi buffers, range 2-32, default 4");
+MODULE_PARM_DESC(vbi_debug,"vbi code debug messages, default is 0 (no)");
+
+#ifdef dprintk
+# undef dprintk
+#endif
+#define dprintk(fmt, ...) \
+do { \
+ if (vbi_debug) \
+ pr_debug("%d: " fmt, btv->c.nr, ##__VA_ARGS__); \
+} while (0)
+
+#define IMAGE_SIZE(fmt) \
+ (((fmt)->count[0] + (fmt)->count[1]) * (fmt)->samples_per_line)
+
+/* ----------------------------------------------------------------------- */
+/* vbi risc code + mm */
+
+static int vbi_buffer_setup(struct videobuf_queue *q,
+ unsigned int *count, unsigned int *size)
+{
+ struct bttv_fh *fh = q->priv_data;
+ struct bttv *btv = fh->btv;
+
+ if (0 == *count)
+ *count = vbibufs;
+
+ *size = IMAGE_SIZE(&fh->vbi_fmt.fmt);
+
+ dprintk("setup: samples=%u start=%d,%d count=%u,%u\n",
+ fh->vbi_fmt.fmt.samples_per_line,
+ fh->vbi_fmt.fmt.start[0],
+ fh->vbi_fmt.fmt.start[1],
+ fh->vbi_fmt.fmt.count[0],
+ fh->vbi_fmt.fmt.count[1]);
+
+ return 0;
+}
+
+static int vbi_buffer_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct bttv_fh *fh = q->priv_data;
+ struct bttv *btv = fh->btv;
+ struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
+ const struct bttv_tvnorm *tvnorm;
+ unsigned int skip_lines0, skip_lines1, min_vdelay;
+ int redo_dma_risc;
+ int rc;
+
+ buf->vb.size = IMAGE_SIZE(&fh->vbi_fmt.fmt);
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+
+ tvnorm = fh->vbi_fmt.tvnorm;
+
+ /* There's no VBI_VDELAY register, RISC must skip the lines
+ we don't want. With default parameters we skip zero lines
+ as earlier driver versions did. The driver permits video
+ standard changes while capturing, so we use vbi_fmt.tvnorm
+ instead of btv->tvnorm to skip zero lines after video
+ standard changes as well. */
+
+ skip_lines0 = 0;
+ skip_lines1 = 0;
+
+ if (fh->vbi_fmt.fmt.count[0] > 0)
+ skip_lines0 = max(0, (fh->vbi_fmt.fmt.start[0]
+ - tvnorm->vbistart[0]));
+ if (fh->vbi_fmt.fmt.count[1] > 0)
+ skip_lines1 = max(0, (fh->vbi_fmt.fmt.start[1]
+ - tvnorm->vbistart[1]));
+
+ redo_dma_risc = 0;
+
+ if (buf->vbi_skip[0] != skip_lines0 ||
+ buf->vbi_skip[1] != skip_lines1 ||
+ buf->vbi_count[0] != fh->vbi_fmt.fmt.count[0] ||
+ buf->vbi_count[1] != fh->vbi_fmt.fmt.count[1]) {
+ buf->vbi_skip[0] = skip_lines0;
+ buf->vbi_skip[1] = skip_lines1;
+ buf->vbi_count[0] = fh->vbi_fmt.fmt.count[0];
+ buf->vbi_count[1] = fh->vbi_fmt.fmt.count[1];
+ redo_dma_risc = 1;
+ }
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ redo_dma_risc = 1;
+ if (0 != (rc = videobuf_iolock(q, &buf->vb, NULL)))
+ goto fail;
+ }
+
+ if (redo_dma_risc) {
+ unsigned int bpl, padding, offset;
+ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+
+ bpl = 2044; /* max. vbipack */
+ padding = VBI_BPL - bpl;
+
+ if (fh->vbi_fmt.fmt.count[0] > 0) {
+ rc = bttv_risc_packed(btv, &buf->top,
+ dma->sglist,
+ /* offset */ 0, bpl,
+ padding, skip_lines0,
+ fh->vbi_fmt.fmt.count[0]);
+ if (0 != rc)
+ goto fail;
+ }
+
+ if (fh->vbi_fmt.fmt.count[1] > 0) {
+ offset = fh->vbi_fmt.fmt.count[0] * VBI_BPL;
+
+ rc = bttv_risc_packed(btv, &buf->bottom,
+ dma->sglist,
+ offset, bpl,
+ padding, skip_lines1,
+ fh->vbi_fmt.fmt.count[1]);
+ if (0 != rc)
+ goto fail;
+ }
+ }
+
+ /* VBI capturing ends at VDELAY, start of video capturing,
+ no matter where the RISC program ends. VDELAY minimum is 2,
+ bounds.top is the corresponding first field line number
+ times two. VDELAY counts half field lines. */
+ min_vdelay = MIN_VDELAY;
+ if (fh->vbi_fmt.end >= tvnorm->cropcap.bounds.top)
+ min_vdelay += fh->vbi_fmt.end - tvnorm->cropcap.bounds.top;
+
+ /* For bttv_buffer_activate_vbi(). */
+ buf->geo.vdelay = min_vdelay;
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ buf->vb.field = field;
+ dprintk("buf prepare %p: top=%p bottom=%p field=%s\n",
+ vb, &buf->top, &buf->bottom,
+ v4l2_field_names[buf->vb.field]);
+ return 0;
+
+ fail:
+ bttv_dma_free(q,btv,buf);
+ return rc;
+}
+
+static void
+vbi_buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct bttv_fh *fh = q->priv_data;
+ struct bttv *btv = fh->btv;
+ struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
+
+ dprintk("queue %p\n",vb);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ list_add_tail(&buf->vb.queue,&btv->vcapture);
+ if (NULL == btv->cvbi) {
+ fh->btv->loop_irq |= 4;
+ bttv_set_dma(btv,0x0c);
+ }
+}
+
+static void vbi_buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct bttv_fh *fh = q->priv_data;
+ struct bttv *btv = fh->btv;
+ struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
+
+ dprintk("free %p\n",vb);
+ bttv_dma_free(q,fh->btv,buf);
+}
+
+struct videobuf_queue_ops bttv_vbi_qops = {
+ .buf_setup = vbi_buffer_setup,
+ .buf_prepare = vbi_buffer_prepare,
+ .buf_queue = vbi_buffer_queue,
+ .buf_release = vbi_buffer_release,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int try_fmt(struct v4l2_vbi_format *f, const struct bttv_tvnorm *tvnorm,
+ __s32 crop_start)
+{
+ __s32 min_start, max_start, max_end, f2_offset;
+ unsigned int i;
+
+ /* For compatibility with earlier driver versions we must pretend
+ the VBI and video capture window may overlap. In reality RISC
+ magic aborts VBI capturing at the first line of video capturing,
+ leaving the rest of the buffer unchanged, usually all zero.
+ VBI capturing must always start before video capturing. >> 1
+ because cropping counts field lines times two. */
+ min_start = tvnorm->vbistart[0];
+ max_start = (crop_start >> 1) - 1;
+ max_end = (tvnorm->cropcap.bounds.top
+ + tvnorm->cropcap.bounds.height) >> 1;
+
+ if (min_start > max_start)
+ return -EBUSY;
+
+ BUG_ON(max_start >= max_end);
+
+ f->sampling_rate = tvnorm->Fsc;
+ f->samples_per_line = VBI_BPL;
+ f->sample_format = V4L2_PIX_FMT_GREY;
+ f->offset = VBI_OFFSET;
+
+ f2_offset = tvnorm->vbistart[1] - tvnorm->vbistart[0];
+
+ for (i = 0; i < 2; ++i) {
+ if (0 == f->count[i]) {
+ /* No data from this field. We leave f->start[i]
+ alone because VIDIOCSVBIFMT is w/o and EINVALs
+ when a driver does not support exactly the
+ requested parameters. */
+ } else {
+ s64 start, count;
+
+ start = clamp(f->start[i], min_start, max_start);
+ /* s64 to prevent overflow. */
+ count = (s64) f->start[i] + f->count[i] - start;
+ f->start[i] = start;
+ f->count[i] = clamp(count, (s64) 1,
+ max_end - start);
+ }
+
+ min_start += f2_offset;
+ max_start += f2_offset;
+ max_end += f2_offset;
+ }
+
+ if (0 == (f->count[0] | f->count[1])) {
+ /* As in earlier driver versions. */
+ f->start[0] = tvnorm->vbistart[0];
+ f->start[1] = tvnorm->vbistart[1];
+ f->count[0] = 1;
+ f->count[1] = 1;
+ }
+
+ f->flags = 0;
+
+ f->reserved[0] = 0;
+ f->reserved[1] = 0;
+
+ return 0;
+}
+
+int bttv_try_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+ const struct bttv_tvnorm *tvnorm;
+ __s32 crop_start;
+
+ mutex_lock(&btv->lock);
+
+ tvnorm = &bttv_tvnorms[btv->tvnorm];
+ crop_start = btv->crop_start;
+
+ mutex_unlock(&btv->lock);
+
+ return try_fmt(&frt->fmt.vbi, tvnorm, crop_start);
+}
+
+
+int bttv_s_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+ const struct bttv_tvnorm *tvnorm;
+ __s32 start1, end;
+ int rc;
+
+ mutex_lock(&btv->lock);
+
+ rc = -EBUSY;
+ if (fh->resources & RESOURCE_VBI)
+ goto fail;
+
+ tvnorm = &bttv_tvnorms[btv->tvnorm];
+
+ rc = try_fmt(&frt->fmt.vbi, tvnorm, btv->crop_start);
+ if (0 != rc)
+ goto fail;
+
+ start1 = frt->fmt.vbi.start[1] - tvnorm->vbistart[1] +
+ tvnorm->vbistart[0];
+
+ /* First possible line of video capturing. Should be
+ max(f->start[0] + f->count[0], start1 + f->count[1]) * 2
+ when capturing both fields. But for compatibility we must
+ pretend the VBI and video capture window may overlap,
+ so end = start + 1, the lowest possible value, times two
+ because vbi_fmt.end counts field lines times two. */
+ end = max(frt->fmt.vbi.start[0], start1) * 2 + 2;
+
+ mutex_lock(&fh->vbi.vb_lock);
+
+ fh->vbi_fmt.fmt = frt->fmt.vbi;
+ fh->vbi_fmt.tvnorm = tvnorm;
+ fh->vbi_fmt.end = end;
+
+ mutex_unlock(&fh->vbi.vb_lock);
+
+ rc = 0;
+
+ fail:
+ mutex_unlock(&btv->lock);
+
+ return rc;
+}
+
+
+int bttv_g_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt)
+{
+ struct bttv_fh *fh = f;
+ const struct bttv_tvnorm *tvnorm;
+
+ frt->fmt.vbi = fh->vbi_fmt.fmt;
+
+ tvnorm = &bttv_tvnorms[fh->btv->tvnorm];
+
+ if (tvnorm != fh->vbi_fmt.tvnorm) {
+ __s32 max_end;
+ unsigned int i;
+
+ /* As in vbi_buffer_prepare() this imitates the
+ behaviour of earlier driver versions after video
+ standard changes, with default parameters anyway. */
+
+ max_end = (tvnorm->cropcap.bounds.top
+ + tvnorm->cropcap.bounds.height) >> 1;
+
+ frt->fmt.vbi.sampling_rate = tvnorm->Fsc;
+
+ for (i = 0; i < 2; ++i) {
+ __s32 new_start;
+
+ new_start = frt->fmt.vbi.start[i]
+ + tvnorm->vbistart[i]
+ - fh->vbi_fmt.tvnorm->vbistart[i];
+
+ frt->fmt.vbi.start[i] = min(new_start, max_end - 1);
+ frt->fmt.vbi.count[i] =
+ min((__s32) frt->fmt.vbi.count[i],
+ max_end - frt->fmt.vbi.start[i]);
+
+ max_end += tvnorm->vbistart[1]
+ - tvnorm->vbistart[0];
+ }
+ }
+ return 0;
+}
+
+void bttv_vbi_fmt_reset(struct bttv_vbi_fmt *f, unsigned int norm)
+{
+ const struct bttv_tvnorm *tvnorm;
+ unsigned int real_samples_per_line;
+ unsigned int real_count;
+
+ tvnorm = &bttv_tvnorms[norm];
+
+ f->fmt.sampling_rate = tvnorm->Fsc;
+ f->fmt.samples_per_line = VBI_BPL;
+ f->fmt.sample_format = V4L2_PIX_FMT_GREY;
+ f->fmt.offset = VBI_OFFSET;
+ f->fmt.start[0] = tvnorm->vbistart[0];
+ f->fmt.start[1] = tvnorm->vbistart[1];
+ f->fmt.count[0] = VBI_DEFLINES;
+ f->fmt.count[1] = VBI_DEFLINES;
+ f->fmt.flags = 0;
+ f->fmt.reserved[0] = 0;
+ f->fmt.reserved[1] = 0;
+
+ /* For compatibility the buffer size must be 2 * VBI_DEFLINES *
+ VBI_BPL regardless of the current video standard. */
+ real_samples_per_line = 1024 + tvnorm->vbipack * 4;
+ real_count = ((tvnorm->cropcap.defrect.top >> 1)
+ - tvnorm->vbistart[0]);
+
+ BUG_ON(real_samples_per_line > VBI_BPL);
+ BUG_ON(real_count > VBI_DEFLINES);
+
+ f->tvnorm = tvnorm;
+
+ /* See bttv_vbi_fmt_set(). */
+ f->end = tvnorm->vbistart[0] * 2 + 2;
+}
+
+/* ----------------------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/bt8xx/bttv.h b/drivers/media/video/bt8xx/bttv.h
new file mode 100644
index 00000000..c5171619
--- /dev/null
+++ b/drivers/media/video/bt8xx/bttv.h
@@ -0,0 +1,377 @@
+/*
+ *
+ * bttv - Bt848 frame grabber driver
+ *
+ * card ID's and external interfaces of the bttv driver
+ * basically stuff needed by other drivers (i2c, lirc, ...)
+ * and is supported not to change much over time.
+ *
+ * Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de)
+ * (c) 1999,2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+ *
+ */
+
+#ifndef _BTTV_H_
+#define _BTTV_H_
+
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+#include <media/v4l2-device.h>
+#include <media/i2c-addr.h>
+#include <media/tuner.h>
+
+/* ---------------------------------------------------------- */
+/* exported by bttv-cards.c */
+
+#define BTTV_BOARD_UNKNOWN 0x00
+#define BTTV_BOARD_MIRO 0x01
+#define BTTV_BOARD_HAUPPAUGE 0x02
+#define BTTV_BOARD_STB 0x03
+#define BTTV_BOARD_INTEL 0x04
+#define BTTV_BOARD_DIAMOND 0x05
+#define BTTV_BOARD_AVERMEDIA 0x06
+#define BTTV_BOARD_MATRIX_VISION 0x07
+#define BTTV_BOARD_FLYVIDEO 0x08
+#define BTTV_BOARD_TURBOTV 0x09
+#define BTTV_BOARD_HAUPPAUGE878 0x0a
+#define BTTV_BOARD_MIROPRO 0x0b
+#define BTTV_BOARD_ADSTECH_TV 0x0c
+#define BTTV_BOARD_AVERMEDIA98 0x0d
+#define BTTV_BOARD_VHX 0x0e
+#define BTTV_BOARD_ZOLTRIX 0x0f
+#define BTTV_BOARD_PIXVIEWPLAYTV 0x10
+#define BTTV_BOARD_WINVIEW_601 0x11
+#define BTTV_BOARD_AVEC_INTERCAP 0x12
+#define BTTV_BOARD_LIFE_FLYKIT 0x13
+#define BTTV_BOARD_CEI_RAFFLES 0x14
+#define BTTV_BOARD_CONFERENCETV 0x15
+#define BTTV_BOARD_PHOEBE_TVMAS 0x16
+#define BTTV_BOARD_MODTEC_205 0x17
+#define BTTV_BOARD_MAGICTVIEW061 0x18
+#define BTTV_BOARD_VOBIS_BOOSTAR 0x19
+#define BTTV_BOARD_HAUPPAUG_WCAM 0x1a
+#define BTTV_BOARD_MAXI 0x1b
+#define BTTV_BOARD_TERRATV 0x1c
+#define BTTV_BOARD_PXC200 0x1d
+#define BTTV_BOARD_FLYVIDEO_98 0x1e
+#define BTTV_BOARD_IPROTV 0x1f
+#define BTTV_BOARD_INTEL_C_S_PCI 0x20
+#define BTTV_BOARD_TERRATVALUE 0x21
+#define BTTV_BOARD_WINFAST2000 0x22
+#define BTTV_BOARD_CHRONOS_VS2 0x23
+#define BTTV_BOARD_TYPHOON_TVIEW 0x24
+#define BTTV_BOARD_PXELVWPLTVPRO 0x25
+#define BTTV_BOARD_MAGICTVIEW063 0x26
+#define BTTV_BOARD_PINNACLE 0x27
+#define BTTV_BOARD_STB2 0x28
+#define BTTV_BOARD_AVPHONE98 0x29
+#define BTTV_BOARD_PV951 0x2a
+#define BTTV_BOARD_ONAIR_TV 0x2b
+#define BTTV_BOARD_SIGMA_TVII_FM 0x2c
+#define BTTV_BOARD_MATRIX_VISION2 0x2d
+#define BTTV_BOARD_ZOLTRIX_GENIE 0x2e
+#define BTTV_BOARD_TERRATVRADIO 0x2f
+#define BTTV_BOARD_DYNALINK 0x30
+#define BTTV_BOARD_GVBCTV3PCI 0x31
+#define BTTV_BOARD_PXELVWPLTVPAK 0x32
+#define BTTV_BOARD_EAGLE 0x33
+#define BTTV_BOARD_PINNACLEPRO 0x34
+#define BTTV_BOARD_TVIEW_RDS_FM 0x35
+#define BTTV_BOARD_LIFETEC_9415 0x36
+#define BTTV_BOARD_BESTBUY_EASYTV 0x37
+#define BTTV_BOARD_FLYVIDEO_98FM 0x38
+#define BTTV_BOARD_GRANDTEC 0x39
+#define BTTV_BOARD_ASKEY_CPH060 0x3a
+#define BTTV_BOARD_ASKEY_CPH03X 0x3b
+#define BTTV_BOARD_MM100PCTV 0x3c
+#define BTTV_BOARD_GMV1 0x3d
+#define BTTV_BOARD_BESTBUY_EASYTV2 0x3e
+#define BTTV_BOARD_ATI_TVWONDER 0x3f
+#define BTTV_BOARD_ATI_TVWONDERVE 0x40
+#define BTTV_BOARD_FLYVIDEO2000 0x41
+#define BTTV_BOARD_TERRATVALUER 0x42
+#define BTTV_BOARD_GVBCTV4PCI 0x43
+#define BTTV_BOARD_VOODOOTV_FM 0x44
+#define BTTV_BOARD_AIMMS 0x45
+#define BTTV_BOARD_PV_BT878P_PLUS 0x46
+#define BTTV_BOARD_FLYVIDEO98EZ 0x47
+#define BTTV_BOARD_PV_BT878P_9B 0x48
+#define BTTV_BOARD_SENSORAY311_611 0x49
+#define BTTV_BOARD_RV605 0x4a
+#define BTTV_BOARD_POWERCLR_MTV878 0x4b
+#define BTTV_BOARD_WINDVR 0x4c
+#define BTTV_BOARD_GRANDTEC_MULTI 0x4d
+#define BTTV_BOARD_KWORLD 0x4e
+#define BTTV_BOARD_DSP_TCVIDEO 0x4f
+#define BTTV_BOARD_HAUPPAUGEPVR 0x50
+#define BTTV_BOARD_GVBCTV5PCI 0x51
+#define BTTV_BOARD_OSPREY1x0 0x52
+#define BTTV_BOARD_OSPREY1x0_848 0x53
+#define BTTV_BOARD_OSPREY101_848 0x54
+#define BTTV_BOARD_OSPREY1x1 0x55
+#define BTTV_BOARD_OSPREY1x1_SVID 0x56
+#define BTTV_BOARD_OSPREY2xx 0x57
+#define BTTV_BOARD_OSPREY2x0_SVID 0x58
+#define BTTV_BOARD_OSPREY2x0 0x59
+#define BTTV_BOARD_OSPREY500 0x5a
+#define BTTV_BOARD_OSPREY540 0x5b
+#define BTTV_BOARD_OSPREY2000 0x5c
+#define BTTV_BOARD_IDS_EAGLE 0x5d
+#define BTTV_BOARD_PINNACLESAT 0x5e
+#define BTTV_BOARD_FORMAC_PROTV 0x5f
+#define BTTV_BOARD_MACHTV 0x60
+#define BTTV_BOARD_EURESYS_PICOLO 0x61
+#define BTTV_BOARD_PV150 0x62
+#define BTTV_BOARD_AD_TVK503 0x63
+#define BTTV_BOARD_HERCULES_SM_TV 0x64
+#define BTTV_BOARD_PACETV 0x65
+#define BTTV_BOARD_IVC200 0x66
+#define BTTV_BOARD_XGUARD 0x67
+#define BTTV_BOARD_NEBULA_DIGITV 0x68
+#define BTTV_BOARD_PV143 0x69
+#define BTTV_BOARD_VD009X1_VD011_MINIDIN 0x6a
+#define BTTV_BOARD_VD009X1_VD011_COMBI 0x6b
+#define BTTV_BOARD_VD009_MINIDIN 0x6c
+#define BTTV_BOARD_VD009_COMBI 0x6d
+#define BTTV_BOARD_IVC100 0x6e
+#define BTTV_BOARD_IVC120 0x6f
+#define BTTV_BOARD_PC_HDTV 0x70
+#define BTTV_BOARD_TWINHAN_DST 0x71
+#define BTTV_BOARD_WINFASTVC100 0x72
+#define BTTV_BOARD_TEV560 0x73
+#define BTTV_BOARD_SIMUS_GVC1100 0x74
+#define BTTV_BOARD_NGSTV_PLUS 0x75
+#define BTTV_BOARD_LMLBT4 0x76
+#define BTTV_BOARD_TEKRAM_M205 0x77
+#define BTTV_BOARD_CONTVFMI 0x78
+#define BTTV_BOARD_PICOLO_TETRA_CHIP 0x79
+#define BTTV_BOARD_SPIRIT_TV 0x7a
+#define BTTV_BOARD_AVDVBT_771 0x7b
+#define BTTV_BOARD_AVDVBT_761 0x7c
+#define BTTV_BOARD_MATRIX_VISIONSQ 0x7d
+#define BTTV_BOARD_MATRIX_VISIONSLC 0x7e
+#define BTTV_BOARD_APAC_VIEWCOMP 0x7f
+#define BTTV_BOARD_DVICO_DVBT_LITE 0x80
+#define BTTV_BOARD_VGEAR_MYVCD 0x81
+#define BTTV_BOARD_SUPER_TV 0x82
+#define BTTV_BOARD_TIBET_CS16 0x83
+#define BTTV_BOARD_KODICOM_4400R 0x84
+#define BTTV_BOARD_KODICOM_4400R_SL 0x85
+#define BTTV_BOARD_ADLINK_RTV24 0x86
+#define BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE 0x87
+#define BTTV_BOARD_ACORP_Y878F 0x88
+#define BTTV_BOARD_CONCEPTRONIC_CTVFMI2 0x89
+#define BTTV_BOARD_PV_BT878P_2E 0x8a
+#define BTTV_BOARD_PV_M4900 0x8b
+#define BTTV_BOARD_OSPREY440 0x8c
+#define BTTV_BOARD_ASOUND_SKYEYE 0x8d
+#define BTTV_BOARD_SABRENT_TVFM 0x8e
+#define BTTV_BOARD_HAUPPAUGE_IMPACTVCB 0x8f
+#define BTTV_BOARD_MACHTV_MAGICTV 0x90
+#define BTTV_BOARD_SSAI_SECURITY 0x91
+#define BTTV_BOARD_SSAI_ULTRASOUND 0x92
+#define BTTV_BOARD_VOODOOTV_200 0x93
+#define BTTV_BOARD_DVICO_FUSIONHDTV_2 0x94
+#define BTTV_BOARD_TYPHOON_TVTUNERPCI 0x95
+#define BTTV_BOARD_GEOVISION_GV600 0x96
+#define BTTV_BOARD_KOZUMI_KTV_01C 0x97
+#define BTTV_BOARD_ENLTV_FM_2 0x98
+#define BTTV_BOARD_VD012 0x99
+#define BTTV_BOARD_VD012_X1 0x9a
+#define BTTV_BOARD_VD012_X2 0x9b
+#define BTTV_BOARD_IVCE8784 0x9c
+#define BTTV_BOARD_GEOVISION_GV800S 0x9d
+#define BTTV_BOARD_GEOVISION_GV800S_SL 0x9e
+#define BTTV_BOARD_PV183 0x9f
+#define BTTV_BOARD_TVT_TD3116 0xa0
+
+
+/* more card-specific defines */
+#define PT2254_L_CHANNEL 0x10
+#define PT2254_R_CHANNEL 0x08
+#define PT2254_DBS_IN_2 0x400
+#define PT2254_DBS_IN_10 0x20000
+#define WINVIEW_PT2254_CLK 0x40
+#define WINVIEW_PT2254_DATA 0x20
+#define WINVIEW_PT2254_STROBE 0x80
+
+struct bttv_core {
+ /* device structs */
+ struct v4l2_device v4l2_dev;
+ struct pci_dev *pci;
+ struct i2c_adapter i2c_adap;
+ struct list_head subs; /* struct bttv_sub_device */
+
+ /* device config */
+ unsigned int nr; /* dev nr (for printk("bttv%d: ..."); */
+ unsigned int type; /* card type (pointer into tvcards[]) */
+};
+
+struct bttv;
+
+struct tvcard {
+ char *name;
+ void (*volume_gpio)(struct bttv *btv, __u16 volume);
+ void (*audio_mode_gpio)(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+ void (*muxsel_hook)(struct bttv *btv, unsigned int input);
+
+ /* MUX bits for each input, two bits per input starting with the LSB */
+ u32 muxsel; /* Use MUXSEL() to set */
+
+ u32 gpiomask;
+ u32 gpiomux[4]; /* Tuner, Radio, external, internal */
+ u32 gpiomute; /* GPIO mute setting */
+ u32 gpiomask2; /* GPIO MUX mask */
+
+ unsigned int tuner_type;
+ u8 tuner_addr;
+ u8 video_inputs; /* Number of inputs */
+ unsigned int svhs:4; /* Which input is s-video */
+#define NO_SVHS 15
+ unsigned int pll:2;
+#define PLL_NONE 0
+#define PLL_28 1
+#define PLL_35 2
+
+ /* i2c audio flags */
+ unsigned int no_msp34xx:1;
+ unsigned int no_tda7432:1;
+ unsigned int needs_tvaudio:1;
+ unsigned int msp34xx_alt:1;
+ /* Note: currently no card definition needs to mark the presence
+ of a RDS saa6588 chip. If this is ever needed, then add a new
+ 'has_saa6588' bit here. */
+
+ unsigned int no_video:1; /* video pci function is unused */
+ unsigned int has_dvb:1;
+ unsigned int has_remote:1;
+ unsigned int has_radio:1;
+ unsigned int has_dig_in:1; /* Has digital input (always last input) */
+ unsigned int no_gpioirq:1;
+};
+
+extern struct tvcard bttv_tvcards[];
+
+/*
+ * This bit of cpp voodoo is used to create a macro with a variable number of
+ * arguments (1 to 16). It will pack each argument into a word two bits at a
+ * time. It can't be a function because it needs to be compile time constant to
+ * initialize structures. Since each argument must fit in two bits, it's ok
+ * that they are changed to octal. One should not use hex number, macros, or
+ * anything else with this macro. Just use plain integers from 0 to 3.
+ */
+#define _MUXSELf(a) 0##a << 30
+#define _MUXSELe(a, b...) 0##a << 28 | _MUXSELf(b)
+#define _MUXSELd(a, b...) 0##a << 26 | _MUXSELe(b)
+#define _MUXSELc(a, b...) 0##a << 24 | _MUXSELd(b)
+#define _MUXSELb(a, b...) 0##a << 22 | _MUXSELc(b)
+#define _MUXSELa(a, b...) 0##a << 20 | _MUXSELb(b)
+#define _MUXSEL9(a, b...) 0##a << 18 | _MUXSELa(b)
+#define _MUXSEL8(a, b...) 0##a << 16 | _MUXSEL9(b)
+#define _MUXSEL7(a, b...) 0##a << 14 | _MUXSEL8(b)
+#define _MUXSEL6(a, b...) 0##a << 12 | _MUXSEL7(b)
+#define _MUXSEL5(a, b...) 0##a << 10 | _MUXSEL6(b)
+#define _MUXSEL4(a, b...) 0##a << 8 | _MUXSEL5(b)
+#define _MUXSEL3(a, b...) 0##a << 6 | _MUXSEL4(b)
+#define _MUXSEL2(a, b...) 0##a << 4 | _MUXSEL3(b)
+#define _MUXSEL1(a, b...) 0##a << 2 | _MUXSEL2(b)
+#define MUXSEL(a, b...) (a | _MUXSEL1(b))
+
+/* identification / initialization of the card */
+extern void bttv_idcard(struct bttv *btv);
+extern void bttv_init_card1(struct bttv *btv);
+extern void bttv_init_card2(struct bttv *btv);
+extern void bttv_init_tuner(struct bttv *btv);
+
+/* card-specific funtions */
+extern void tea5757_set_freq(struct bttv *btv, unsigned short freq);
+extern u32 bttv_tda9880_setnorm(struct bttv *btv, u32 gpiobits);
+
+/* extra tweaks for some chipsets */
+extern void bttv_check_chipset(void);
+extern int bttv_handle_chipset(struct bttv *btv);
+
+/* ---------------------------------------------------------- */
+/* exported by bttv-if.c */
+
+/* this obsolete -- please use the sysfs-based
+ interface below for new code */
+
+extern struct pci_dev* bttv_get_pcidev(unsigned int card);
+
+/* sets GPOE register (BT848_GPIO_OUT_EN) to new value:
+ data | (current_GPOE_value & ~mask)
+ returns negative value if error occurred
+*/
+extern int bttv_gpio_enable(unsigned int card,
+ unsigned long mask, unsigned long data);
+
+/* fills data with GPDATA register contents
+ returns negative value if error occurred
+*/
+extern int bttv_read_gpio(unsigned int card, unsigned long *data);
+
+/* sets GPDATA register to new value:
+ (data & mask) | (current_GPDATA_value & ~mask)
+ returns negative value if error occurred
+*/
+extern int bttv_write_gpio(unsigned int card,
+ unsigned long mask, unsigned long data);
+
+
+
+
+/* ---------------------------------------------------------- */
+/* sysfs/driver-moded based gpio access interface */
+
+struct bttv_sub_device {
+ struct device dev;
+ struct bttv_core *core;
+ struct list_head list;
+};
+#define to_bttv_sub_dev(x) container_of((x), struct bttv_sub_device, dev)
+
+struct bttv_sub_driver {
+ struct device_driver drv;
+ char wanted[20];
+ int (*probe)(struct bttv_sub_device *sub);
+ void (*remove)(struct bttv_sub_device *sub);
+};
+#define to_bttv_sub_drv(x) container_of((x), struct bttv_sub_driver, drv)
+
+int bttv_sub_register(struct bttv_sub_driver *drv, char *wanted);
+int bttv_sub_unregister(struct bttv_sub_driver *drv);
+
+/* gpio access functions */
+void bttv_gpio_inout(struct bttv_core *core, u32 mask, u32 outbits);
+u32 bttv_gpio_read(struct bttv_core *core);
+void bttv_gpio_write(struct bttv_core *core, u32 value);
+void bttv_gpio_bits(struct bttv_core *core, u32 mask, u32 bits);
+
+#define gpio_inout(mask,bits) bttv_gpio_inout(&btv->c, mask, bits)
+#define gpio_read() bttv_gpio_read(&btv->c)
+#define gpio_write(value) bttv_gpio_write(&btv->c, value)
+#define gpio_bits(mask,bits) bttv_gpio_bits(&btv->c, mask, bits)
+
+
+/* ---------------------------------------------------------- */
+/* i2c */
+
+#define bttv_call_all(btv, o, f, args...) \
+ v4l2_device_call_all(&btv->c.v4l2_dev, 0, o, f, ##args)
+
+extern int bttv_I2CRead(struct bttv *btv, unsigned char addr, char *probe_for);
+extern int bttv_I2CWrite(struct bttv *btv, unsigned char addr, unsigned char b1,
+ unsigned char b2, int both);
+extern void bttv_readee(struct bttv *btv, unsigned char *eedata, int addr);
+
+extern int bttv_input_init(struct bttv *dev);
+extern void bttv_input_fini(struct bttv *dev);
+extern void bttv_input_irq(struct bttv *dev);
+
+#endif /* _BTTV_H_ */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/bt8xx/bttvp.h b/drivers/media/video/bt8xx/bttvp.h
new file mode 100644
index 00000000..db943a8d
--- /dev/null
+++ b/drivers/media/video/bt8xx/bttvp.h
@@ -0,0 +1,534 @@
+/*
+
+ bttv - Bt848 frame grabber driver
+
+ bttv's *private* header file -- nobody other than bttv itself
+ should ever include this file.
+
+ (c) 2000-2002 Gerd Knorr <kraxel@bytesex.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _BTTVP_H_
+#define _BTTVP_H_
+
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/pci.h>
+#include <linux/input.h>
+#include <linux/mutex.h>
+#include <linux/scatterlist.h>
+#include <asm/io.h>
+#include <media/v4l2-common.h>
+#include <linux/device.h>
+#include <media/videobuf-dma-sg.h>
+#include <media/tveeprom.h>
+#include <media/rc-core.h>
+#include <media/ir-kbd-i2c.h>
+
+#include "bt848.h"
+#include "bttv.h"
+#include "btcx-risc.h"
+
+#ifdef __KERNEL__
+
+#define FORMAT_FLAGS_DITHER 0x01
+#define FORMAT_FLAGS_PACKED 0x02
+#define FORMAT_FLAGS_PLANAR 0x04
+#define FORMAT_FLAGS_RAW 0x08
+#define FORMAT_FLAGS_CrCb 0x10
+
+#define RISC_SLOT_O_VBI 4
+#define RISC_SLOT_O_FIELD 6
+#define RISC_SLOT_E_VBI 10
+#define RISC_SLOT_E_FIELD 12
+#define RISC_SLOT_LOOP 14
+
+#define RESOURCE_OVERLAY 1
+#define RESOURCE_VIDEO_STREAM 2
+#define RESOURCE_VBI 4
+#define RESOURCE_VIDEO_READ 8
+
+#define RAW_LINES 640
+#define RAW_BPL 1024
+
+#define UNSET (-1U)
+
+/* Min. value in VDELAY register. */
+#define MIN_VDELAY 2
+/* Even to get Cb first, odd for Cr. */
+#define MAX_HDELAY (0x3FF & -2)
+/* Limits scaled width, which must be a multiple of 4. */
+#define MAX_HACTIVE (0x3FF & -4)
+
+#define BTTV_NORMS (\
+ V4L2_STD_PAL | V4L2_STD_PAL_N | \
+ V4L2_STD_PAL_Nc | V4L2_STD_SECAM | \
+ V4L2_STD_NTSC | V4L2_STD_PAL_M | \
+ V4L2_STD_PAL_60)
+/* ---------------------------------------------------------- */
+
+struct bttv_tvnorm {
+ int v4l2_id;
+ char *name;
+ u32 Fsc;
+ u16 swidth, sheight; /* scaled standard width, height */
+ u16 totalwidth;
+ u8 adelay, bdelay, iform;
+ u32 scaledtwidth;
+ u16 hdelayx1, hactivex1;
+ u16 vdelay;
+ u8 vbipack;
+ u16 vtotal;
+ int sram;
+ /* ITU-R frame line number of the first VBI line we can
+ capture, of the first and second field. The last possible line
+ is determined by cropcap.bounds. */
+ u16 vbistart[2];
+ /* Horizontally this counts fCLKx1 samples following the leading
+ edge of the horizontal sync pulse, vertically ITU-R frame line
+ numbers of the first field times two (2, 4, 6, ... 524 or 624). */
+ struct v4l2_cropcap cropcap;
+};
+extern const struct bttv_tvnorm bttv_tvnorms[];
+
+struct bttv_format {
+ char *name;
+ int fourcc; /* video4linux 2 */
+ int btformat; /* BT848_COLOR_FMT_* */
+ int btswap; /* BT848_COLOR_CTL_* */
+ int depth; /* bit/pixel */
+ int flags;
+ int hshift,vshift; /* for planar modes */
+};
+
+struct bttv_ir {
+ struct rc_dev *dev;
+ struct timer_list timer;
+
+ char name[32];
+ char phys[32];
+
+ /* Usual gpio signalling */
+ u32 mask_keycode;
+ u32 mask_keydown;
+ u32 mask_keyup;
+ u32 polling;
+ u32 last_gpio;
+ int shift_by;
+ int start; // What should RC5_START() be
+ int addr; // What RC5_ADDR() should be.
+ int rc5_remote_gap;
+
+ /* RC5 gpio */
+ bool rc5_gpio; /* Is RC5 legacy GPIO enabled? */
+ u32 last_bit; /* last raw bit seen */
+ u32 code; /* raw code under construction */
+ struct timeval base_time; /* time of last seen code */
+ bool active; /* building raw code */
+};
+
+
+/* ---------------------------------------------------------- */
+
+struct bttv_geometry {
+ u8 vtc,crop,comb;
+ u16 width,hscale,hdelay;
+ u16 sheight,vscale,vdelay,vtotal;
+};
+
+struct bttv_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+
+ /* bttv specific */
+ const struct bttv_format *fmt;
+ unsigned int tvnorm;
+ int btformat;
+ int btswap;
+ struct bttv_geometry geo;
+ struct btcx_riscmem top;
+ struct btcx_riscmem bottom;
+ struct v4l2_rect crop;
+ unsigned int vbi_skip[2];
+ unsigned int vbi_count[2];
+};
+
+struct bttv_buffer_set {
+ struct bttv_buffer *top; /* top field buffer */
+ struct bttv_buffer *bottom; /* bottom field buffer */
+ unsigned int top_irq;
+ unsigned int frame_irq;
+};
+
+struct bttv_overlay {
+ unsigned int tvnorm;
+ struct v4l2_rect w;
+ enum v4l2_field field;
+ struct v4l2_clip *clips;
+ int nclips;
+ int setup_ok;
+};
+
+struct bttv_vbi_fmt {
+ struct v4l2_vbi_format fmt;
+
+ /* fmt.start[] and count[] refer to this video standard. */
+ const struct bttv_tvnorm *tvnorm;
+
+ /* Earliest possible start of video capturing with this
+ v4l2_vbi_format, in struct bttv_crop.rect units. */
+ __s32 end;
+};
+
+/* bttv-vbi.c */
+void bttv_vbi_fmt_reset(struct bttv_vbi_fmt *f, unsigned int norm);
+
+struct bttv_crop {
+ /* A cropping rectangle in struct bttv_tvnorm.cropcap units. */
+ struct v4l2_rect rect;
+
+ /* Scaled image size limits with this crop rect. Divide
+ max_height, but not min_height, by two when capturing
+ single fields. See also bttv_crop_reset() and
+ bttv_crop_adjust() in bttv-driver.c. */
+ __s32 min_scaled_width;
+ __s32 min_scaled_height;
+ __s32 max_scaled_width;
+ __s32 max_scaled_height;
+};
+
+struct bttv_fh {
+ struct bttv *btv;
+ int resources;
+#ifdef VIDIOC_G_PRIORITY
+ enum v4l2_priority prio;
+#endif
+ enum v4l2_buf_type type;
+
+ /* video capture */
+ struct videobuf_queue cap;
+ const struct bttv_format *fmt;
+ int width;
+ int height;
+
+ /* video overlay */
+ const struct bttv_format *ovfmt;
+ struct bttv_overlay ov;
+
+ /* Application called VIDIOC_S_CROP. */
+ int do_crop;
+
+ /* vbi capture */
+ struct videobuf_queue vbi;
+ /* Current VBI capture window as seen through this fh (cannot
+ be global for compatibility with earlier drivers). Protected
+ by struct bttv.lock and struct bttv_fh.vbi.lock. */
+ struct bttv_vbi_fmt vbi_fmt;
+};
+
+/* ---------------------------------------------------------- */
+/* bttv-risc.c */
+
+/* risc code generators - capture */
+int bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc,
+ struct scatterlist *sglist,
+ unsigned int offset, unsigned int bpl,
+ unsigned int pitch, unsigned int skip_lines,
+ unsigned int store_lines);
+
+/* control dma register + risc main loop */
+void bttv_set_dma(struct bttv *btv, int override);
+int bttv_risc_init_main(struct bttv *btv);
+int bttv_risc_hook(struct bttv *btv, int slot, struct btcx_riscmem *risc,
+ int irqflags);
+
+/* capture buffer handling */
+int bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf);
+int bttv_buffer_activate_video(struct bttv *btv,
+ struct bttv_buffer_set *set);
+int bttv_buffer_activate_vbi(struct bttv *btv,
+ struct bttv_buffer *vbi);
+void bttv_dma_free(struct videobuf_queue *q, struct bttv *btv,
+ struct bttv_buffer *buf);
+
+/* overlay handling */
+int bttv_overlay_risc(struct bttv *btv, struct bttv_overlay *ov,
+ const struct bttv_format *fmt,
+ struct bttv_buffer *buf);
+
+
+/* ---------------------------------------------------------- */
+/* bttv-vbi.c */
+
+int bttv_try_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f);
+int bttv_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f);
+int bttv_s_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f);
+
+extern struct videobuf_queue_ops bttv_vbi_qops;
+
+/* ---------------------------------------------------------- */
+/* bttv-gpio.c */
+
+extern struct bus_type bttv_sub_bus_type;
+int bttv_sub_add_device(struct bttv_core *core, char *name);
+int bttv_sub_del_devices(struct bttv_core *core);
+
+/* ---------------------------------------------------------- */
+/* bttv-cards.c */
+
+extern int no_overlay;
+
+/* ---------------------------------------------------------- */
+/* bttv-input.c */
+
+extern void init_bttv_i2c_ir(struct bttv *btv);
+extern int fini_bttv_i2c(struct bttv *btv);
+
+/* ---------------------------------------------------------- */
+/* bttv-driver.c */
+
+/* insmod options */
+extern unsigned int bttv_verbose;
+extern unsigned int bttv_debug;
+extern unsigned int bttv_gpio;
+extern void bttv_gpio_tracking(struct bttv *btv, char *comment);
+extern int init_bttv_i2c(struct bttv *btv);
+
+#define dprintk(fmt, ...) \
+do { \
+ if (bttv_debug >= 1) \
+ pr_debug(fmt, ##__VA_ARGS__); \
+} while (0)
+#define dprintk_cont(fmt, ...) \
+do { \
+ if (bttv_debug >= 1) \
+ pr_cont(fmt, ##__VA_ARGS__); \
+} while (0)
+#define d2printk(fmt, ...) \
+do { \
+ if (bttv_debug >= 2) \
+ printk(fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define BTTV_MAX_FBUF 0x208000
+#define BTTV_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */
+#define BTTV_FREE_IDLE msecs_to_jiffies(1000) /* one second */
+
+
+struct bttv_pll_info {
+ unsigned int pll_ifreq; /* PLL input frequency */
+ unsigned int pll_ofreq; /* PLL output frequency */
+ unsigned int pll_crystal; /* Crystal used for input */
+ unsigned int pll_current; /* Currently programmed ofreq */
+};
+
+/* for gpio-connected remote control */
+struct bttv_input {
+ struct input_dev *dev;
+ char name[32];
+ char phys[32];
+ u32 mask_keycode;
+ u32 mask_keydown;
+};
+
+struct bttv_suspend_state {
+ u32 gpio_enable;
+ u32 gpio_data;
+ int disabled;
+ int loop_irq;
+ struct bttv_buffer_set video;
+ struct bttv_buffer *vbi;
+};
+
+struct bttv {
+ struct bttv_core c;
+
+ /* pci device config */
+ unsigned short id;
+ unsigned char revision;
+ unsigned char __iomem *bt848_mmio; /* pointer to mmio */
+
+ /* card configuration info */
+ unsigned int cardid; /* pci subsystem id (bt878 based ones) */
+ unsigned int tuner_type; /* tuner chip type */
+ unsigned int tda9887_conf;
+ unsigned int svhs, dig;
+ unsigned int has_saa6588:1;
+ struct bttv_pll_info pll;
+ int triton1;
+ int gpioirq;
+
+ int use_i2c_hw;
+
+ /* old gpio interface */
+ int shutdown;
+
+ void (*volume_gpio)(struct bttv *btv, __u16 volume);
+ void (*audio_mode_gpio)(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+
+ /* new gpio interface */
+ spinlock_t gpio_lock;
+
+ /* i2c layer */
+ struct i2c_algo_bit_data i2c_algo;
+ struct i2c_client i2c_client;
+ int i2c_state, i2c_rc;
+ int i2c_done;
+ wait_queue_head_t i2c_queue;
+ struct v4l2_subdev *sd_msp34xx;
+ struct v4l2_subdev *sd_tvaudio;
+
+ /* video4linux (1) */
+ struct video_device *video_dev;
+ struct video_device *radio_dev;
+ struct video_device *vbi_dev;
+
+ /* infrared remote */
+ int has_remote;
+ struct bttv_ir *remote;
+
+ /* I2C remote data */
+ struct IR_i2c_init_data init_data;
+
+ /* locking */
+ spinlock_t s_lock;
+ struct mutex lock;
+ int resources;
+#ifdef VIDIOC_G_PRIORITY
+ struct v4l2_prio_state prio;
+#endif
+
+ /* video state */
+ unsigned int input;
+ unsigned int audio;
+ unsigned int mute;
+ unsigned long freq;
+ unsigned int tvnorm;
+ int hue, contrast, bright, saturation;
+ struct v4l2_framebuffer fbuf;
+ unsigned int field_count;
+
+ /* various options */
+ int opt_combfilter;
+ int opt_lumafilter;
+ int opt_automute;
+ int opt_chroma_agc;
+ int opt_adc_crush;
+ int opt_vcr_hack;
+ int opt_whitecrush_upper;
+ int opt_whitecrush_lower;
+ int opt_uv_ratio;
+ int opt_full_luma_range;
+ int opt_coring;
+
+ /* radio data/state */
+ int has_radio;
+ int radio_user;
+
+ /* miro/pinnacle + Aimslab VHX
+ philips matchbox (tea5757 radio tuner) support */
+ int has_matchbox;
+ int mbox_we;
+ int mbox_data;
+ int mbox_clk;
+ int mbox_most;
+ int mbox_mask;
+
+ /* ISA stuff (Terratec Active Radio Upgrade) */
+ int mbox_ior;
+ int mbox_iow;
+ int mbox_csel;
+
+ /* risc memory management data
+ - must acquire s_lock before changing these
+ - only the irq handler is supported to touch top + bottom + vcurr */
+ struct btcx_riscmem main;
+ struct bttv_buffer *screen; /* overlay */
+ struct list_head capture; /* video capture queue */
+ struct list_head vcapture; /* vbi capture queue */
+ struct bttv_buffer_set curr; /* active buffers */
+ struct bttv_buffer *cvbi; /* active vbi buffer */
+ int loop_irq;
+ int new_input;
+
+ unsigned long cap_ctl;
+ unsigned long dma_on;
+ struct timer_list timeout;
+ struct bttv_suspend_state state;
+
+ /* stats */
+ unsigned int errors;
+ unsigned int framedrop;
+ unsigned int irq_total;
+ unsigned int irq_me;
+
+ unsigned int users;
+ struct bttv_fh init;
+
+ /* used to make dvb-bt8xx autoloadable */
+ struct work_struct request_module_wk;
+
+ /* Default (0) and current (1) video capturing and overlay
+ cropping parameters in bttv_tvnorm.cropcap units. Protected
+ by bttv.lock. */
+ struct bttv_crop crop[2];
+
+ /* Earliest possible start of video capturing in
+ bttv_tvnorm.cropcap line units. Set by check_alloc_btres()
+ and free_btres(). Protected by bttv.lock. */
+ __s32 vbi_end;
+
+ /* Latest possible end of VBI capturing (= crop[x].rect.top when
+ VIDEO_RESOURCES are locked). Set by check_alloc_btres()
+ and free_btres(). Protected by bttv.lock. */
+ __s32 crop_start;
+};
+
+static inline struct bttv *to_bttv(struct v4l2_device *v4l2_dev)
+{
+ return container_of(v4l2_dev, struct bttv, c.v4l2_dev);
+}
+
+/* our devices */
+#define BTTV_MAX 32
+extern unsigned int bttv_num;
+extern struct bttv *bttvs[BTTV_MAX];
+
+static inline unsigned int bttv_muxsel(const struct bttv *btv,
+ unsigned int input)
+{
+ return (bttv_tvcards[btv->c.type].muxsel >> (input * 2)) & 3;
+}
+
+#endif
+
+#define btwrite(dat,adr) writel((dat), btv->bt848_mmio+(adr))
+#define btread(adr) readl(btv->bt848_mmio+(adr))
+
+#define btand(dat,adr) btwrite((dat) & btread(adr), adr)
+#define btor(dat,adr) btwrite((dat) | btread(adr), adr)
+#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr)
+
+#endif /* _BTTVP_H_ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/btcx-risc.c b/drivers/media/video/btcx-risc.c
new file mode 100644
index 00000000..ac1b2687
--- /dev/null
+++ b/drivers/media/video/btcx-risc.c
@@ -0,0 +1,260 @@
+/*
+
+ btcx-risc.c
+
+ bt848/bt878/cx2388x risc code generator.
+
+ (c) 2000-03 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/videodev2.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+
+#include "btcx-risc.h"
+
+MODULE_DESCRIPTION("some code shared by bttv and cx88xx drivers");
+MODULE_AUTHOR("Gerd Knorr");
+MODULE_LICENSE("GPL");
+
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug,"debug messages, default is 0 (no)");
+
+/* ---------------------------------------------------------- */
+/* allocate/free risc memory */
+
+static int memcnt;
+
+void btcx_riscmem_free(struct pci_dev *pci,
+ struct btcx_riscmem *risc)
+{
+ if (NULL == risc->cpu)
+ return;
+ if (debug) {
+ memcnt--;
+ printk("btcx: riscmem free [%d] dma=%lx\n",
+ memcnt, (unsigned long)risc->dma);
+ }
+ pci_free_consistent(pci, risc->size, risc->cpu, risc->dma);
+ memset(risc,0,sizeof(*risc));
+}
+
+int btcx_riscmem_alloc(struct pci_dev *pci,
+ struct btcx_riscmem *risc,
+ unsigned int size)
+{
+ __le32 *cpu;
+ dma_addr_t dma = 0;
+
+ if (NULL != risc->cpu && risc->size < size)
+ btcx_riscmem_free(pci,risc);
+ if (NULL == risc->cpu) {
+ cpu = pci_alloc_consistent(pci, size, &dma);
+ if (NULL == cpu)
+ return -ENOMEM;
+ risc->cpu = cpu;
+ risc->dma = dma;
+ risc->size = size;
+ if (debug) {
+ memcnt++;
+ printk("btcx: riscmem alloc [%d] dma=%lx cpu=%p size=%d\n",
+ memcnt, (unsigned long)dma, cpu, size);
+ }
+ }
+ memset(risc->cpu,0,risc->size);
+ return 0;
+}
+
+/* ---------------------------------------------------------- */
+/* screen overlay helpers */
+
+int
+btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win,
+ struct v4l2_clip *clips, unsigned int n)
+{
+ if (win->left < 0) {
+ /* left */
+ clips[n].c.left = 0;
+ clips[n].c.top = 0;
+ clips[n].c.width = -win->left;
+ clips[n].c.height = win->height;
+ n++;
+ }
+ if (win->left + win->width > swidth) {
+ /* right */
+ clips[n].c.left = swidth - win->left;
+ clips[n].c.top = 0;
+ clips[n].c.width = win->width - clips[n].c.left;
+ clips[n].c.height = win->height;
+ n++;
+ }
+ if (win->top < 0) {
+ /* top */
+ clips[n].c.left = 0;
+ clips[n].c.top = 0;
+ clips[n].c.width = win->width;
+ clips[n].c.height = -win->top;
+ n++;
+ }
+ if (win->top + win->height > sheight) {
+ /* bottom */
+ clips[n].c.left = 0;
+ clips[n].c.top = sheight - win->top;
+ clips[n].c.width = win->width;
+ clips[n].c.height = win->height - clips[n].c.top;
+ n++;
+ }
+ return n;
+}
+
+int
+btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, unsigned int n, int mask)
+{
+ s32 nx,nw,dx;
+ unsigned int i;
+
+ /* fixup window */
+ nx = (win->left + mask) & ~mask;
+ nw = (win->width) & ~mask;
+ if (nx + nw > win->left + win->width)
+ nw -= mask+1;
+ dx = nx - win->left;
+ win->left = nx;
+ win->width = nw;
+ if (debug)
+ printk(KERN_DEBUG "btcx: window align %dx%d+%d+%d [dx=%d]\n",
+ win->width, win->height, win->left, win->top, dx);
+
+ /* fixup clips */
+ for (i = 0; i < n; i++) {
+ nx = (clips[i].c.left-dx) & ~mask;
+ nw = (clips[i].c.width) & ~mask;
+ if (nx + nw < clips[i].c.left-dx + clips[i].c.width)
+ nw += mask+1;
+ clips[i].c.left = nx;
+ clips[i].c.width = nw;
+ if (debug)
+ printk(KERN_DEBUG "btcx: clip align %dx%d+%d+%d\n",
+ clips[i].c.width, clips[i].c.height,
+ clips[i].c.left, clips[i].c.top);
+ }
+ return 0;
+}
+
+void
+btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips)
+{
+ struct v4l2_clip swap;
+ int i,j,n;
+
+ if (nclips < 2)
+ return;
+ for (i = nclips-2; i >= 0; i--) {
+ for (n = 0, j = 0; j <= i; j++) {
+ if (clips[j].c.left > clips[j+1].c.left) {
+ swap = clips[j];
+ clips[j] = clips[j+1];
+ clips[j+1] = swap;
+ n++;
+ }
+ }
+ if (0 == n)
+ break;
+ }
+}
+
+void
+btcx_calc_skips(int line, int width, int *maxy,
+ struct btcx_skiplist *skips, unsigned int *nskips,
+ const struct v4l2_clip *clips, unsigned int nclips)
+{
+ unsigned int clip,skip;
+ int end, maxline;
+
+ skip=0;
+ maxline = 9999;
+ for (clip = 0; clip < nclips; clip++) {
+
+ /* sanity checks */
+ if (clips[clip].c.left + clips[clip].c.width <= 0)
+ continue;
+ if (clips[clip].c.left > (signed)width)
+ break;
+
+ /* vertical range */
+ if (line > clips[clip].c.top+clips[clip].c.height-1)
+ continue;
+ if (line < clips[clip].c.top) {
+ if (maxline > clips[clip].c.top-1)
+ maxline = clips[clip].c.top-1;
+ continue;
+ }
+ if (maxline > clips[clip].c.top+clips[clip].c.height-1)
+ maxline = clips[clip].c.top+clips[clip].c.height-1;
+
+ /* horizontal range */
+ if (0 == skip || clips[clip].c.left > skips[skip-1].end) {
+ /* new one */
+ skips[skip].start = clips[clip].c.left;
+ if (skips[skip].start < 0)
+ skips[skip].start = 0;
+ skips[skip].end = clips[clip].c.left + clips[clip].c.width;
+ if (skips[skip].end > width)
+ skips[skip].end = width;
+ skip++;
+ } else {
+ /* overlaps -- expand last one */
+ end = clips[clip].c.left + clips[clip].c.width;
+ if (skips[skip-1].end < end)
+ skips[skip-1].end = end;
+ if (skips[skip-1].end > width)
+ skips[skip-1].end = width;
+ }
+ }
+ *nskips = skip;
+ *maxy = maxline;
+
+ if (debug) {
+ printk(KERN_DEBUG "btcx: skips line %d-%d:",line,maxline);
+ for (skip = 0; skip < *nskips; skip++) {
+ printk(" %d-%d",skips[skip].start,skips[skip].end);
+ }
+ printk("\n");
+ }
+}
+
+/* ---------------------------------------------------------- */
+
+EXPORT_SYMBOL(btcx_riscmem_alloc);
+EXPORT_SYMBOL(btcx_riscmem_free);
+
+EXPORT_SYMBOL(btcx_screen_clips);
+EXPORT_SYMBOL(btcx_align);
+EXPORT_SYMBOL(btcx_sort_clips);
+EXPORT_SYMBOL(btcx_calc_skips);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/btcx-risc.h b/drivers/media/video/btcx-risc.h
new file mode 100644
index 00000000..f8bc6e8e
--- /dev/null
+++ b/drivers/media/video/btcx-risc.h
@@ -0,0 +1,34 @@
+/*
+ */
+struct btcx_riscmem {
+ unsigned int size;
+ __le32 *cpu;
+ __le32 *jmp;
+ dma_addr_t dma;
+};
+
+struct btcx_skiplist {
+ int start;
+ int end;
+};
+
+int btcx_riscmem_alloc(struct pci_dev *pci,
+ struct btcx_riscmem *risc,
+ unsigned int size);
+void btcx_riscmem_free(struct pci_dev *pci,
+ struct btcx_riscmem *risc);
+
+int btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win,
+ struct v4l2_clip *clips, unsigned int n);
+int btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips,
+ unsigned int n, int mask);
+void btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips);
+void btcx_calc_skips(int line, int width, int *maxy,
+ struct btcx_skiplist *skips, unsigned int *nskips,
+ const struct v4l2_clip *clips, unsigned int nclips);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/bw-qcam.c b/drivers/media/video/bw-qcam.c
new file mode 100644
index 00000000..f09df9df
--- /dev/null
+++ b/drivers/media/video/bw-qcam.c
@@ -0,0 +1,1094 @@
+/*
+ * QuickCam Driver For Video4Linux.
+ *
+ * Video4Linux conversion work by Alan Cox.
+ * Parport compatibility by Phil Blundell.
+ * Busy loop avoidance by Mark Cooke.
+ *
+ * Module parameters:
+ *
+ * maxpoll=<1 - 5000>
+ *
+ * When polling the QuickCam for a response, busy-wait for a
+ * maximum of this many loops. The default of 250 gives little
+ * impact on interactive response.
+ *
+ * NOTE: If this parameter is set too high, the processor
+ * will busy wait until this loop times out, and then
+ * slowly poll for a further 5 seconds before failing
+ * the transaction. You have been warned.
+ *
+ * yieldlines=<1 - 250>
+ *
+ * When acquiring a frame from the camera, the data gathering
+ * loop will yield back to the scheduler after completing
+ * this many lines. The default of 4 provides a trade-off
+ * between increased frame acquisition time and impact on
+ * interactive response.
+ */
+
+/* qcam-lib.c -- Library for programming with the Connectix QuickCam.
+ * See the included documentation for usage instructions and details
+ * of the protocol involved. */
+
+
+/* Version 0.5, August 4, 1996 */
+/* Version 0.7, August 27, 1996 */
+/* Version 0.9, November 17, 1996 */
+
+
+/******************************************************************
+
+Copyright (C) 1996 by Scott Laird
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL SCOTT LAIRD BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/parport.h>
+#include <linux/sched.h>
+#include <linux/videodev2.h>
+#include <linux/mutex.h>
+#include <asm/uaccess.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+
+/* One from column A... */
+#define QC_NOTSET 0
+#define QC_UNIDIR 1
+#define QC_BIDIR 2
+#define QC_SERIAL 3
+
+/* ... and one from column B */
+#define QC_ANY 0x00
+#define QC_FORCE_UNIDIR 0x10
+#define QC_FORCE_BIDIR 0x20
+#define QC_FORCE_SERIAL 0x30
+/* in the port_mode member */
+
+#define QC_MODE_MASK 0x07
+#define QC_FORCE_MASK 0x70
+
+#define MAX_HEIGHT 243
+#define MAX_WIDTH 336
+
+/* Bit fields for status flags */
+#define QC_PARAM_CHANGE 0x01 /* Camera status change has occurred */
+
+struct qcam {
+ struct v4l2_device v4l2_dev;
+ struct video_device vdev;
+ struct pardevice *pdev;
+ struct parport *pport;
+ struct mutex lock;
+ int width, height;
+ int bpp;
+ int mode;
+ int contrast, brightness, whitebal;
+ int port_mode;
+ int transfer_scale;
+ int top, left;
+ int status;
+ unsigned int saved_bits;
+ unsigned long in_use;
+};
+
+static unsigned int maxpoll = 250; /* Maximum busy-loop count for qcam I/O */
+static unsigned int yieldlines = 4; /* Yield after this many during capture */
+static int video_nr = -1;
+static unsigned int force_init; /* Whether to probe aggressively */
+
+module_param(maxpoll, int, 0);
+module_param(yieldlines, int, 0);
+module_param(video_nr, int, 0);
+
+/* Set force_init=1 to avoid detection by polling status register and
+ * immediately attempt to initialize qcam */
+module_param(force_init, int, 0);
+
+#define MAX_CAMS 4
+static struct qcam *qcams[MAX_CAMS];
+static unsigned int num_cams;
+
+static inline int read_lpstatus(struct qcam *q)
+{
+ return parport_read_status(q->pport);
+}
+
+static inline int read_lpdata(struct qcam *q)
+{
+ return parport_read_data(q->pport);
+}
+
+static inline void write_lpdata(struct qcam *q, int d)
+{
+ parport_write_data(q->pport, d);
+}
+
+static void write_lpcontrol(struct qcam *q, int d)
+{
+ if (d & 0x20) {
+ /* Set bidirectional mode to reverse (data in) */
+ parport_data_reverse(q->pport);
+ } else {
+ /* Set bidirectional mode to forward (data out) */
+ parport_data_forward(q->pport);
+ }
+
+ /* Now issue the regular port command, but strip out the
+ * direction flag */
+ d &= ~0x20;
+ parport_write_control(q->pport, d);
+}
+
+
+/* qc_waithand busy-waits for a handshake signal from the QuickCam.
+ * Almost all communication with the camera requires handshaking. */
+
+static int qc_waithand(struct qcam *q, int val)
+{
+ int status;
+ int runs = 0;
+
+ if (val) {
+ while (!((status = read_lpstatus(q)) & 8)) {
+ /* 1000 is enough spins on the I/O for all normal
+ cases, at that point we start to poll slowly
+ until the camera wakes up. However, we are
+ busy blocked until the camera responds, so
+ setting it lower is much better for interactive
+ response. */
+
+ if (runs++ > maxpoll)
+ msleep_interruptible(5);
+ if (runs > (maxpoll + 1000)) /* 5 seconds */
+ return -1;
+ }
+ } else {
+ while (((status = read_lpstatus(q)) & 8)) {
+ /* 1000 is enough spins on the I/O for all normal
+ cases, at that point we start to poll slowly
+ until the camera wakes up. However, we are
+ busy blocked until the camera responds, so
+ setting it lower is much better for interactive
+ response. */
+
+ if (runs++ > maxpoll)
+ msleep_interruptible(5);
+ if (runs++ > (maxpoll + 1000)) /* 5 seconds */
+ return -1;
+ }
+ }
+
+ return status;
+}
+
+/* Waithand2 is used when the qcam is in bidirectional mode, and the
+ * handshaking signal is CamRdy2 (bit 0 of data reg) instead of CamRdy1
+ * (bit 3 of status register). It also returns the last value read,
+ * since this data is useful. */
+
+static unsigned int qc_waithand2(struct qcam *q, int val)
+{
+ unsigned int status;
+ int runs = 0;
+
+ do {
+ status = read_lpdata(q);
+ /* 1000 is enough spins on the I/O for all normal
+ cases, at that point we start to poll slowly
+ until the camera wakes up. However, we are
+ busy blocked until the camera responds, so
+ setting it lower is much better for interactive
+ response. */
+
+ if (runs++ > maxpoll)
+ msleep_interruptible(5);
+ if (runs++ > (maxpoll + 1000)) /* 5 seconds */
+ return 0;
+ } while ((status & 1) != val);
+
+ return status;
+}
+
+/* qc_command is probably a bit of a misnomer -- it's used to send
+ * bytes *to* the camera. Generally, these bytes are either commands
+ * or arguments to commands, so the name fits, but it still bugs me a
+ * bit. See the documentation for a list of commands. */
+
+static int qc_command(struct qcam *q, int command)
+{
+ int n1, n2;
+ int cmd;
+
+ write_lpdata(q, command);
+ write_lpcontrol(q, 6);
+
+ n1 = qc_waithand(q, 1);
+
+ write_lpcontrol(q, 0xe);
+ n2 = qc_waithand(q, 0);
+
+ cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
+ return cmd;
+}
+
+static int qc_readparam(struct qcam *q)
+{
+ int n1, n2;
+ int cmd;
+
+ write_lpcontrol(q, 6);
+ n1 = qc_waithand(q, 1);
+
+ write_lpcontrol(q, 0xe);
+ n2 = qc_waithand(q, 0);
+
+ cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
+ return cmd;
+}
+
+
+/* Try to detect a QuickCam. It appears to flash the upper 4 bits of
+ the status register at 5-10 Hz. This is only used in the autoprobe
+ code. Be aware that this isn't the way Connectix detects the
+ camera (they send a reset and try to handshake), but this should be
+ almost completely safe, while their method screws up my printer if
+ I plug it in before the camera. */
+
+static int qc_detect(struct qcam *q)
+{
+ int reg, lastreg;
+ int count = 0;
+ int i;
+
+ if (force_init)
+ return 1;
+
+ lastreg = reg = read_lpstatus(q) & 0xf0;
+
+ for (i = 0; i < 500; i++) {
+ reg = read_lpstatus(q) & 0xf0;
+ if (reg != lastreg)
+ count++;
+ lastreg = reg;
+ mdelay(2);
+ }
+
+
+#if 0
+ /* Force camera detection during testing. Sometimes the camera
+ won't be flashing these bits. Possibly unloading the module
+ in the middle of a grab? Or some timeout condition?
+ I've seen this parameter as low as 19 on my 450Mhz box - mpc */
+ printk(KERN_DEBUG "Debugging: QCam detection counter <30-200 counts as detected>: %d\n", count);
+ return 1;
+#endif
+
+ /* Be (even more) liberal in what you accept... */
+
+ if (count > 20 && count < 400) {
+ return 1; /* found */
+ } else {
+ printk(KERN_ERR "No Quickcam found on port %s\n",
+ q->pport->name);
+ printk(KERN_DEBUG "Quickcam detection counter: %u\n", count);
+ return 0; /* not found */
+ }
+}
+
+/* Decide which scan mode to use. There's no real requirement that
+ * the scanmode match the resolution in q->height and q-> width -- the
+ * camera takes the picture at the resolution specified in the
+ * "scanmode" and then returns the image at the resolution specified
+ * with the resolution commands. If the scan is bigger than the
+ * requested resolution, the upper-left hand corner of the scan is
+ * returned. If the scan is smaller, then the rest of the image
+ * returned contains garbage. */
+
+static int qc_setscanmode(struct qcam *q)
+{
+ int old_mode = q->mode;
+
+ switch (q->transfer_scale) {
+ case 1:
+ q->mode = 0;
+ break;
+ case 2:
+ q->mode = 4;
+ break;
+ case 4:
+ q->mode = 8;
+ break;
+ }
+
+ switch (q->bpp) {
+ case 4:
+ break;
+ case 6:
+ q->mode += 2;
+ break;
+ }
+
+ switch (q->port_mode & QC_MODE_MASK) {
+ case QC_BIDIR:
+ q->mode += 1;
+ break;
+ case QC_NOTSET:
+ case QC_UNIDIR:
+ break;
+ }
+
+ if (q->mode != old_mode)
+ q->status |= QC_PARAM_CHANGE;
+
+ return 0;
+}
+
+
+/* Reset the QuickCam. This uses the same sequence the Windows
+ * QuickPic program uses. Someone with a bi-directional port should
+ * check that bi-directional mode is detected right, and then
+ * implement bi-directional mode in qc_readbyte(). */
+
+static void qc_reset(struct qcam *q)
+{
+ switch (q->port_mode & QC_FORCE_MASK) {
+ case QC_FORCE_UNIDIR:
+ q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR;
+ break;
+
+ case QC_FORCE_BIDIR:
+ q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR;
+ break;
+
+ case QC_ANY:
+ write_lpcontrol(q, 0x20);
+ write_lpdata(q, 0x75);
+
+ if (read_lpdata(q) != 0x75)
+ q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR;
+ else
+ q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR;
+ break;
+ }
+
+ write_lpcontrol(q, 0xb);
+ udelay(250);
+ write_lpcontrol(q, 0xe);
+ qc_setscanmode(q); /* in case port_mode changed */
+}
+
+
+
+/* Reset the QuickCam and program for brightness, contrast,
+ * white-balance, and resolution. */
+
+static void qc_set(struct qcam *q)
+{
+ int val;
+ int val2;
+
+ qc_reset(q);
+
+ /* Set the brightness. Yes, this is repetitive, but it works.
+ * Shorter versions seem to fail subtly. Feel free to try :-). */
+ /* I think the problem was in qc_command, not here -- bls */
+
+ qc_command(q, 0xb);
+ qc_command(q, q->brightness);
+
+ val = q->height / q->transfer_scale;
+ qc_command(q, 0x11);
+ qc_command(q, val);
+ if ((q->port_mode & QC_MODE_MASK) == QC_UNIDIR && q->bpp == 6) {
+ /* The normal "transfers per line" calculation doesn't seem to work
+ as expected here (and yet it works fine in qc_scan). No idea
+ why this case is the odd man out. Fortunately, Laird's original
+ working version gives me a good way to guess at working values.
+ -- bls */
+ val = q->width;
+ val2 = q->transfer_scale * 4;
+ } else {
+ val = q->width * q->bpp;
+ val2 = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) *
+ q->transfer_scale;
+ }
+ val = DIV_ROUND_UP(val, val2);
+ qc_command(q, 0x13);
+ qc_command(q, val);
+
+ /* Setting top and left -- bls */
+ qc_command(q, 0xd);
+ qc_command(q, q->top);
+ qc_command(q, 0xf);
+ qc_command(q, q->left / 2);
+
+ qc_command(q, 0x19);
+ qc_command(q, q->contrast);
+ qc_command(q, 0x1f);
+ qc_command(q, q->whitebal);
+
+ /* Clear flag that we must update the grabbing parameters on the camera
+ before we grab the next frame */
+ q->status &= (~QC_PARAM_CHANGE);
+}
+
+/* Qc_readbytes reads some bytes from the QC and puts them in
+ the supplied buffer. It returns the number of bytes read,
+ or -1 on error. */
+
+static inline int qc_readbytes(struct qcam *q, char buffer[])
+{
+ int ret = 1;
+ unsigned int hi, lo;
+ unsigned int hi2, lo2;
+ static int state;
+
+ if (buffer == NULL) {
+ state = 0;
+ return 0;
+ }
+
+ switch (q->port_mode & QC_MODE_MASK) {
+ case QC_BIDIR: /* Bi-directional Port */
+ write_lpcontrol(q, 0x26);
+ lo = (qc_waithand2(q, 1) >> 1);
+ hi = (read_lpstatus(q) >> 3) & 0x1f;
+ write_lpcontrol(q, 0x2e);
+ lo2 = (qc_waithand2(q, 0) >> 1);
+ hi2 = (read_lpstatus(q) >> 3) & 0x1f;
+ switch (q->bpp) {
+ case 4:
+ buffer[0] = lo & 0xf;
+ buffer[1] = ((lo & 0x70) >> 4) | ((hi & 1) << 3);
+ buffer[2] = (hi & 0x1e) >> 1;
+ buffer[3] = lo2 & 0xf;
+ buffer[4] = ((lo2 & 0x70) >> 4) | ((hi2 & 1) << 3);
+ buffer[5] = (hi2 & 0x1e) >> 1;
+ ret = 6;
+ break;
+ case 6:
+ buffer[0] = lo & 0x3f;
+ buffer[1] = ((lo & 0x40) >> 6) | (hi << 1);
+ buffer[2] = lo2 & 0x3f;
+ buffer[3] = ((lo2 & 0x40) >> 6) | (hi2 << 1);
+ ret = 4;
+ break;
+ }
+ break;
+
+ case QC_UNIDIR: /* Unidirectional Port */
+ write_lpcontrol(q, 6);
+ lo = (qc_waithand(q, 1) & 0xf0) >> 4;
+ write_lpcontrol(q, 0xe);
+ hi = (qc_waithand(q, 0) & 0xf0) >> 4;
+
+ switch (q->bpp) {
+ case 4:
+ buffer[0] = lo;
+ buffer[1] = hi;
+ ret = 2;
+ break;
+ case 6:
+ switch (state) {
+ case 0:
+ buffer[0] = (lo << 2) | ((hi & 0xc) >> 2);
+ q->saved_bits = (hi & 3) << 4;
+ state = 1;
+ ret = 1;
+ break;
+ case 1:
+ buffer[0] = lo | q->saved_bits;
+ q->saved_bits = hi << 2;
+ state = 2;
+ ret = 1;
+ break;
+ case 2:
+ buffer[0] = ((lo & 0xc) >> 2) | q->saved_bits;
+ buffer[1] = ((lo & 3) << 4) | hi;
+ state = 0;
+ ret = 2;
+ break;
+ }
+ break;
+ }
+ break;
+ }
+ return ret;
+}
+
+/* requests a scan from the camera. It sends the correct instructions
+ * to the camera and then reads back the correct number of bytes. In
+ * previous versions of this routine the return structure contained
+ * the raw output from the camera, and there was a 'qc_convertscan'
+ * function that converted that to a useful format. In version 0.3 I
+ * rolled qc_convertscan into qc_scan and now I only return the
+ * converted scan. The format is just an one-dimensional array of
+ * characters, one for each pixel, with 0=black up to n=white, where
+ * n=2^(bit depth)-1. Ask me for more details if you don't understand
+ * this. */
+
+static long qc_capture(struct qcam *q, char __user *buf, unsigned long len)
+{
+ int i, j, k, yield;
+ int bytes;
+ int linestotrans, transperline;
+ int divisor;
+ int pixels_per_line;
+ int pixels_read = 0;
+ int got = 0;
+ char buffer[6];
+ int shift = 8 - q->bpp;
+ char invert;
+
+ if (q->mode == -1)
+ return -ENXIO;
+
+ qc_command(q, 0x7);
+ qc_command(q, q->mode);
+
+ if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) {
+ write_lpcontrol(q, 0x2e); /* turn port around */
+ write_lpcontrol(q, 0x26);
+ qc_waithand(q, 1);
+ write_lpcontrol(q, 0x2e);
+ qc_waithand(q, 0);
+ }
+
+ /* strange -- should be 15:63 below, but 4bpp is odd */
+ invert = (q->bpp == 4) ? 16 : 63;
+
+ linestotrans = q->height / q->transfer_scale;
+ pixels_per_line = q->width / q->transfer_scale;
+ transperline = q->width * q->bpp;
+ divisor = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) *
+ q->transfer_scale;
+ transperline = DIV_ROUND_UP(transperline, divisor);
+
+ for (i = 0, yield = yieldlines; i < linestotrans; i++) {
+ for (pixels_read = j = 0; j < transperline; j++) {
+ bytes = qc_readbytes(q, buffer);
+ for (k = 0; k < bytes && (pixels_read + k) < pixels_per_line; k++) {
+ int o;
+ if (buffer[k] == 0 && invert == 16) {
+ /* 4bpp is odd (again) -- inverter is 16, not 15, but output
+ must be 0-15 -- bls */
+ buffer[k] = 16;
+ }
+ o = i * pixels_per_line + pixels_read + k;
+ if (o < len) {
+ got++;
+ put_user((invert - buffer[k]) << shift, buf + o);
+ }
+ }
+ pixels_read += bytes;
+ }
+ qc_readbytes(q, NULL); /* reset state machine */
+
+ /* Grabbing an entire frame from the quickcam is a lengthy
+ process. We don't (usually) want to busy-block the
+ processor for the entire frame. yieldlines is a module
+ parameter. If we yield every line, the minimum frame
+ time will be 240 / 200 = 1.2 seconds. The compile-time
+ default is to yield every 4 lines. */
+ if (i >= yield) {
+ msleep_interruptible(5);
+ yield = i + yieldlines;
+ }
+ }
+
+ if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) {
+ write_lpcontrol(q, 2);
+ write_lpcontrol(q, 6);
+ udelay(3);
+ write_lpcontrol(q, 0xe);
+ }
+ if (got < len)
+ return got;
+ return len;
+}
+
+/*
+ * Video4linux interfacing
+ */
+
+static int qcam_querycap(struct file *file, void *priv,
+ struct v4l2_capability *vcap)
+{
+ struct qcam *qcam = video_drvdata(file);
+
+ strlcpy(vcap->driver, qcam->v4l2_dev.name, sizeof(vcap->driver));
+ strlcpy(vcap->card, "B&W Quickcam", sizeof(vcap->card));
+ strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info));
+ vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
+ return 0;
+}
+
+static int qcam_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
+{
+ if (vin->index > 0)
+ return -EINVAL;
+ strlcpy(vin->name, "Camera", sizeof(vin->name));
+ vin->type = V4L2_INPUT_TYPE_CAMERA;
+ vin->audioset = 0;
+ vin->tuner = 0;
+ vin->std = 0;
+ vin->status = 0;
+ return 0;
+}
+
+static int qcam_g_input(struct file *file, void *fh, unsigned int *inp)
+{
+ *inp = 0;
+ return 0;
+}
+
+static int qcam_s_input(struct file *file, void *fh, unsigned int inp)
+{
+ return (inp > 0) ? -EINVAL : 0;
+}
+
+static int qcam_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 180);
+ case V4L2_CID_CONTRAST:
+ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 192);
+ case V4L2_CID_GAMMA:
+ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 105);
+ }
+ return -EINVAL;
+}
+
+static int qcam_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct qcam *qcam = video_drvdata(file);
+ int ret = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = qcam->brightness;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->value = qcam->contrast;
+ break;
+ case V4L2_CID_GAMMA:
+ ctrl->value = qcam->whitebal;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int qcam_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct qcam *qcam = video_drvdata(file);
+ int ret = 0;
+
+ mutex_lock(&qcam->lock);
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ qcam->brightness = ctrl->value;
+ break;
+ case V4L2_CID_CONTRAST:
+ qcam->contrast = ctrl->value;
+ break;
+ case V4L2_CID_GAMMA:
+ qcam->whitebal = ctrl->value;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ if (ret == 0) {
+ qc_setscanmode(qcam);
+ qcam->status |= QC_PARAM_CHANGE;
+ }
+ mutex_unlock(&qcam->lock);
+ return ret;
+}
+
+static int qcam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct qcam *qcam = video_drvdata(file);
+ struct v4l2_pix_format *pix = &fmt->fmt.pix;
+
+ pix->width = qcam->width / qcam->transfer_scale;
+ pix->height = qcam->height / qcam->transfer_scale;
+ pix->pixelformat = (qcam->bpp == 4) ? V4L2_PIX_FMT_Y4 : V4L2_PIX_FMT_Y6;
+ pix->field = V4L2_FIELD_NONE;
+ pix->bytesperline = qcam->width;
+ pix->sizeimage = qcam->width * qcam->height;
+ /* Just a guess */
+ pix->colorspace = V4L2_COLORSPACE_SRGB;
+ return 0;
+}
+
+static int qcam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct v4l2_pix_format *pix = &fmt->fmt.pix;
+
+ if (pix->height <= 60 || pix->width <= 80) {
+ pix->height = 60;
+ pix->width = 80;
+ } else if (pix->height <= 120 || pix->width <= 160) {
+ pix->height = 120;
+ pix->width = 160;
+ } else {
+ pix->height = 240;
+ pix->width = 320;
+ }
+ if (pix->pixelformat != V4L2_PIX_FMT_Y4 &&
+ pix->pixelformat != V4L2_PIX_FMT_Y6)
+ pix->pixelformat = V4L2_PIX_FMT_Y4;
+ pix->field = V4L2_FIELD_NONE;
+ pix->bytesperline = pix->width;
+ pix->sizeimage = pix->width * pix->height;
+ /* Just a guess */
+ pix->colorspace = V4L2_COLORSPACE_SRGB;
+ return 0;
+}
+
+static int qcam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct qcam *qcam = video_drvdata(file);
+ struct v4l2_pix_format *pix = &fmt->fmt.pix;
+ int ret = qcam_try_fmt_vid_cap(file, fh, fmt);
+
+ if (ret)
+ return ret;
+ qcam->width = 320;
+ qcam->height = 240;
+ if (pix->height == 60)
+ qcam->transfer_scale = 4;
+ else if (pix->height == 120)
+ qcam->transfer_scale = 2;
+ else
+ qcam->transfer_scale = 1;
+ if (pix->pixelformat == V4L2_PIX_FMT_Y6)
+ qcam->bpp = 6;
+ else
+ qcam->bpp = 4;
+
+ mutex_lock(&qcam->lock);
+ qc_setscanmode(qcam);
+ /* We must update the camera before we grab. We could
+ just have changed the grab size */
+ qcam->status |= QC_PARAM_CHANGE;
+ mutex_unlock(&qcam->lock);
+ return 0;
+}
+
+static int qcam_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
+{
+ static struct v4l2_fmtdesc formats[] = {
+ { 0, 0, 0,
+ "4-Bit Monochrome", V4L2_PIX_FMT_Y4,
+ { 0, 0, 0, 0 }
+ },
+ { 0, 0, 0,
+ "6-Bit Monochrome", V4L2_PIX_FMT_Y6,
+ { 0, 0, 0, 0 }
+ },
+ };
+ enum v4l2_buf_type type = fmt->type;
+
+ if (fmt->index > 1)
+ return -EINVAL;
+
+ *fmt = formats[fmt->index];
+ fmt->type = type;
+ return 0;
+}
+
+static ssize_t qcam_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct qcam *qcam = video_drvdata(file);
+ int len;
+ parport_claim_or_block(qcam->pdev);
+
+ mutex_lock(&qcam->lock);
+
+ qc_reset(qcam);
+
+ /* Update the camera parameters if we need to */
+ if (qcam->status & QC_PARAM_CHANGE)
+ qc_set(qcam);
+
+ len = qc_capture(qcam, buf, count);
+
+ mutex_unlock(&qcam->lock);
+
+ parport_release(qcam->pdev);
+ return len;
+}
+
+static const struct v4l2_file_operations qcam_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = video_ioctl2,
+ .read = qcam_read,
+};
+
+static const struct v4l2_ioctl_ops qcam_ioctl_ops = {
+ .vidioc_querycap = qcam_querycap,
+ .vidioc_g_input = qcam_g_input,
+ .vidioc_s_input = qcam_s_input,
+ .vidioc_enum_input = qcam_enum_input,
+ .vidioc_queryctrl = qcam_queryctrl,
+ .vidioc_g_ctrl = qcam_g_ctrl,
+ .vidioc_s_ctrl = qcam_s_ctrl,
+ .vidioc_enum_fmt_vid_cap = qcam_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = qcam_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = qcam_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = qcam_try_fmt_vid_cap,
+};
+
+/* Initialize the QuickCam driver control structure. This is where
+ * defaults are set for people who don't have a config file.*/
+
+static struct qcam *qcam_init(struct parport *port)
+{
+ struct qcam *qcam;
+ struct v4l2_device *v4l2_dev;
+
+ qcam = kzalloc(sizeof(struct qcam), GFP_KERNEL);
+ if (qcam == NULL)
+ return NULL;
+
+ v4l2_dev = &qcam->v4l2_dev;
+ strlcpy(v4l2_dev->name, "bw-qcam", sizeof(v4l2_dev->name));
+
+ if (v4l2_device_register(NULL, v4l2_dev) < 0) {
+ v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
+ kfree(qcam);
+ return NULL;
+ }
+
+ qcam->pport = port;
+ qcam->pdev = parport_register_device(port, "bw-qcam", NULL, NULL,
+ NULL, 0, NULL);
+ if (qcam->pdev == NULL) {
+ v4l2_err(v4l2_dev, "couldn't register for %s.\n", port->name);
+ kfree(qcam);
+ return NULL;
+ }
+
+ strlcpy(qcam->vdev.name, "Connectix QuickCam", sizeof(qcam->vdev.name));
+ qcam->vdev.v4l2_dev = v4l2_dev;
+ qcam->vdev.fops = &qcam_fops;
+ qcam->vdev.ioctl_ops = &qcam_ioctl_ops;
+ qcam->vdev.release = video_device_release_empty;
+ video_set_drvdata(&qcam->vdev, qcam);
+
+ mutex_init(&qcam->lock);
+
+ qcam->port_mode = (QC_ANY | QC_NOTSET);
+ qcam->width = 320;
+ qcam->height = 240;
+ qcam->bpp = 4;
+ qcam->transfer_scale = 2;
+ qcam->contrast = 192;
+ qcam->brightness = 180;
+ qcam->whitebal = 105;
+ qcam->top = 1;
+ qcam->left = 14;
+ qcam->mode = -1;
+ qcam->status = QC_PARAM_CHANGE;
+ return qcam;
+}
+
+static int qc_calibrate(struct qcam *q)
+{
+ /*
+ * Bugfix by Hanno Mueller hmueller@kabel.de, Mai 21 96
+ * The white balance is an individual value for each
+ * quickcam.
+ */
+
+ int value;
+ int count = 0;
+
+ qc_command(q, 27); /* AutoAdjustOffset */
+ qc_command(q, 0); /* Dummy Parameter, ignored by the camera */
+
+ /* GetOffset (33) will read 255 until autocalibration */
+ /* is finished. After that, a value of 1-254 will be */
+ /* returned. */
+
+ do {
+ qc_command(q, 33);
+ value = qc_readparam(q);
+ mdelay(1);
+ schedule();
+ count++;
+ } while (value == 0xff && count < 2048);
+
+ q->whitebal = value;
+ return value;
+}
+
+static int init_bwqcam(struct parport *port)
+{
+ struct qcam *qcam;
+
+ if (num_cams == MAX_CAMS) {
+ printk(KERN_ERR "Too many Quickcams (max %d)\n", MAX_CAMS);
+ return -ENOSPC;
+ }
+
+ qcam = qcam_init(port);
+ if (qcam == NULL)
+ return -ENODEV;
+
+ parport_claim_or_block(qcam->pdev);
+
+ qc_reset(qcam);
+
+ if (qc_detect(qcam) == 0) {
+ parport_release(qcam->pdev);
+ parport_unregister_device(qcam->pdev);
+ kfree(qcam);
+ return -ENODEV;
+ }
+ qc_calibrate(qcam);
+
+ parport_release(qcam->pdev);
+
+ v4l2_info(&qcam->v4l2_dev, "Connectix Quickcam on %s\n", qcam->pport->name);
+
+ if (video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
+ parport_unregister_device(qcam->pdev);
+ kfree(qcam);
+ return -ENODEV;
+ }
+
+ qcams[num_cams++] = qcam;
+
+ return 0;
+}
+
+static void close_bwqcam(struct qcam *qcam)
+{
+ video_unregister_device(&qcam->vdev);
+ parport_unregister_device(qcam->pdev);
+ kfree(qcam);
+}
+
+/* The parport parameter controls which parports will be scanned.
+ * Scanning all parports causes some printers to print a garbage page.
+ * -- March 14, 1999 Billy Donahue <billy@escape.com> */
+#ifdef MODULE
+static char *parport[MAX_CAMS] = { NULL, };
+module_param_array(parport, charp, NULL, 0);
+#endif
+
+static int accept_bwqcam(struct parport *port)
+{
+#ifdef MODULE
+ int n;
+
+ if (parport[0] && strncmp(parport[0], "auto", 4) != 0) {
+ /* user gave parport parameters */
+ for (n = 0; n < MAX_CAMS && parport[n]; n++) {
+ char *ep;
+ unsigned long r;
+ r = simple_strtoul(parport[n], &ep, 0);
+ if (ep == parport[n]) {
+ printk(KERN_ERR
+ "bw-qcam: bad port specifier \"%s\"\n",
+ parport[n]);
+ continue;
+ }
+ if (r == port->number)
+ return 1;
+ }
+ return 0;
+ }
+#endif
+ return 1;
+}
+
+static void bwqcam_attach(struct parport *port)
+{
+ if (accept_bwqcam(port))
+ init_bwqcam(port);
+}
+
+static void bwqcam_detach(struct parport *port)
+{
+ int i;
+ for (i = 0; i < num_cams; i++) {
+ struct qcam *qcam = qcams[i];
+ if (qcam && qcam->pdev->port == port) {
+ qcams[i] = NULL;
+ close_bwqcam(qcam);
+ }
+ }
+}
+
+static struct parport_driver bwqcam_driver = {
+ .name = "bw-qcam",
+ .attach = bwqcam_attach,
+ .detach = bwqcam_detach,
+};
+
+static void __exit exit_bw_qcams(void)
+{
+ parport_unregister_driver(&bwqcam_driver);
+}
+
+static int __init init_bw_qcams(void)
+{
+#ifdef MODULE
+ /* Do some sanity checks on the module parameters. */
+ if (maxpoll > 5000) {
+ printk(KERN_INFO "Connectix Quickcam max-poll was above 5000. Using 5000.\n");
+ maxpoll = 5000;
+ }
+
+ if (yieldlines < 1) {
+ printk(KERN_INFO "Connectix Quickcam yieldlines was less than 1. Using 1.\n");
+ yieldlines = 1;
+ }
+#endif
+ return parport_register_driver(&bwqcam_driver);
+}
+
+module_init(init_bw_qcams);
+module_exit(exit_bw_qcams);
+
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.3");
diff --git a/drivers/media/video/c-qcam.c b/drivers/media/video/c-qcam.c
new file mode 100644
index 00000000..fda32f52
--- /dev/null
+++ b/drivers/media/video/c-qcam.c
@@ -0,0 +1,891 @@
+/*
+ * Video4Linux Colour QuickCam driver
+ * Copyright 1997-2000 Philip Blundell <philb@gnu.org>
+ *
+ * Module parameters:
+ *
+ * parport=auto -- probe all parports (default)
+ * parport=0 -- parport0 becomes qcam1
+ * parport=2,0,1 -- parports 2,0,1 are tried in that order
+ *
+ * probe=0 -- do no probing, assume camera is present
+ * probe=1 -- use IEEE-1284 autoprobe data only (default)
+ * probe=2 -- probe aggressively for cameras
+ *
+ * force_rgb=1 -- force data format to RGB (default is BGR)
+ *
+ * The parport parameter controls which parports will be scanned.
+ * Scanning all parports causes some printers to print a garbage page.
+ * -- March 14, 1999 Billy Donahue <billy@escape.com>
+ *
+ * Fixed data format to BGR, added force_rgb parameter. Added missing
+ * parport_unregister_driver() on module removal.
+ * -- May 28, 2000 Claudio Matsuoka <claudio@conectiva.com>
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/parport.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/jiffies.h>
+#include <linux/videodev2.h>
+#include <asm/uaccess.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+
+struct qcam {
+ struct v4l2_device v4l2_dev;
+ struct video_device vdev;
+ struct pardevice *pdev;
+ struct parport *pport;
+ int width, height;
+ int ccd_width, ccd_height;
+ int mode;
+ int contrast, brightness, whitebal;
+ int top, left;
+ unsigned int bidirectional;
+ struct mutex lock;
+};
+
+/* cameras maximum */
+#define MAX_CAMS 4
+
+/* The three possible QuickCam modes */
+#define QC_MILLIONS 0x18
+#define QC_BILLIONS 0x10
+#define QC_THOUSANDS 0x08 /* with VIDEC compression (not supported) */
+
+/* The three possible decimations */
+#define QC_DECIMATION_1 0
+#define QC_DECIMATION_2 2
+#define QC_DECIMATION_4 4
+
+#define BANNER "Colour QuickCam for Video4Linux v0.06"
+
+static int parport[MAX_CAMS] = { [1 ... MAX_CAMS-1] = -1 };
+static int probe = 2;
+static bool force_rgb;
+static int video_nr = -1;
+
+/* FIXME: parport=auto would never have worked, surely? --RR */
+MODULE_PARM_DESC(parport, "parport=<auto|n[,n]...> for port detection method\n"
+ "probe=<0|1|2> for camera detection method\n"
+ "force_rgb=<0|1> for RGB data format (default BGR)");
+module_param_array(parport, int, NULL, 0);
+module_param(probe, int, 0);
+module_param(force_rgb, bool, 0);
+module_param(video_nr, int, 0);
+
+static struct qcam *qcams[MAX_CAMS];
+static unsigned int num_cams;
+
+static inline void qcam_set_ack(struct qcam *qcam, unsigned int i)
+{
+ /* note: the QC specs refer to the PCAck pin by voltage, not
+ software level. PC ports have builtin inverters. */
+ parport_frob_control(qcam->pport, 8, i ? 8 : 0);
+}
+
+static inline unsigned int qcam_ready1(struct qcam *qcam)
+{
+ return (parport_read_status(qcam->pport) & 0x8) ? 1 : 0;
+}
+
+static inline unsigned int qcam_ready2(struct qcam *qcam)
+{
+ return (parport_read_data(qcam->pport) & 0x1) ? 1 : 0;
+}
+
+static unsigned int qcam_await_ready1(struct qcam *qcam, int value)
+{
+ struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
+ unsigned long oldjiffies = jiffies;
+ unsigned int i;
+
+ for (oldjiffies = jiffies;
+ time_before(jiffies, oldjiffies + msecs_to_jiffies(40));)
+ if (qcam_ready1(qcam) == value)
+ return 0;
+
+ /* If the camera didn't respond within 1/25 second, poll slowly
+ for a while. */
+ for (i = 0; i < 50; i++) {
+ if (qcam_ready1(qcam) == value)
+ return 0;
+ msleep_interruptible(100);
+ }
+
+ /* Probably somebody pulled the plug out. Not much we can do. */
+ v4l2_err(v4l2_dev, "ready1 timeout (%d) %x %x\n", value,
+ parport_read_status(qcam->pport),
+ parport_read_control(qcam->pport));
+ return 1;
+}
+
+static unsigned int qcam_await_ready2(struct qcam *qcam, int value)
+{
+ struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
+ unsigned long oldjiffies = jiffies;
+ unsigned int i;
+
+ for (oldjiffies = jiffies;
+ time_before(jiffies, oldjiffies + msecs_to_jiffies(40));)
+ if (qcam_ready2(qcam) == value)
+ return 0;
+
+ /* If the camera didn't respond within 1/25 second, poll slowly
+ for a while. */
+ for (i = 0; i < 50; i++) {
+ if (qcam_ready2(qcam) == value)
+ return 0;
+ msleep_interruptible(100);
+ }
+
+ /* Probably somebody pulled the plug out. Not much we can do. */
+ v4l2_err(v4l2_dev, "ready2 timeout (%d) %x %x %x\n", value,
+ parport_read_status(qcam->pport),
+ parport_read_control(qcam->pport),
+ parport_read_data(qcam->pport));
+ return 1;
+}
+
+static int qcam_read_data(struct qcam *qcam)
+{
+ unsigned int idata;
+
+ qcam_set_ack(qcam, 0);
+ if (qcam_await_ready1(qcam, 1))
+ return -1;
+ idata = parport_read_status(qcam->pport) & 0xf0;
+ qcam_set_ack(qcam, 1);
+ if (qcam_await_ready1(qcam, 0))
+ return -1;
+ idata |= parport_read_status(qcam->pport) >> 4;
+ return idata;
+}
+
+static int qcam_write_data(struct qcam *qcam, unsigned int data)
+{
+ struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
+ unsigned int idata;
+
+ parport_write_data(qcam->pport, data);
+ idata = qcam_read_data(qcam);
+ if (data != idata) {
+ v4l2_warn(v4l2_dev, "sent %x but received %x\n", data,
+ idata);
+ return 1;
+ }
+ return 0;
+}
+
+static inline int qcam_set(struct qcam *qcam, unsigned int cmd, unsigned int data)
+{
+ if (qcam_write_data(qcam, cmd))
+ return -1;
+ if (qcam_write_data(qcam, data))
+ return -1;
+ return 0;
+}
+
+static inline int qcam_get(struct qcam *qcam, unsigned int cmd)
+{
+ if (qcam_write_data(qcam, cmd))
+ return -1;
+ return qcam_read_data(qcam);
+}
+
+static int qc_detect(struct qcam *qcam)
+{
+ unsigned int stat, ostat, i, count = 0;
+
+ /* The probe routine below is not very reliable. The IEEE-1284
+ probe takes precedence. */
+ /* XXX Currently parport provides no way to distinguish between
+ "the IEEE probe was not done" and "the probe was done, but
+ no device was found". Fix this one day. */
+ if (qcam->pport->probe_info[0].class == PARPORT_CLASS_MEDIA
+ && qcam->pport->probe_info[0].model
+ && !strcmp(qcam->pdev->port->probe_info[0].model,
+ "Color QuickCam 2.0")) {
+ printk(KERN_DEBUG "QuickCam: Found by IEEE1284 probe.\n");
+ return 1;
+ }
+
+ if (probe < 2)
+ return 0;
+
+ parport_write_control(qcam->pport, 0xc);
+
+ /* look for a heartbeat */
+ ostat = stat = parport_read_status(qcam->pport);
+ for (i = 0; i < 250; i++) {
+ mdelay(1);
+ stat = parport_read_status(qcam->pport);
+ if (ostat != stat) {
+ if (++count >= 3)
+ return 1;
+ ostat = stat;
+ }
+ }
+
+ /* Reset the camera and try again */
+ parport_write_control(qcam->pport, 0xc);
+ parport_write_control(qcam->pport, 0x8);
+ mdelay(1);
+ parport_write_control(qcam->pport, 0xc);
+ mdelay(1);
+ count = 0;
+
+ ostat = stat = parport_read_status(qcam->pport);
+ for (i = 0; i < 250; i++) {
+ mdelay(1);
+ stat = parport_read_status(qcam->pport);
+ if (ostat != stat) {
+ if (++count >= 3)
+ return 1;
+ ostat = stat;
+ }
+ }
+
+ /* no (or flatline) camera, give up */
+ return 0;
+}
+
+static void qc_reset(struct qcam *qcam)
+{
+ parport_write_control(qcam->pport, 0xc);
+ parport_write_control(qcam->pport, 0x8);
+ mdelay(1);
+ parport_write_control(qcam->pport, 0xc);
+ mdelay(1);
+}
+
+/* Reset the QuickCam and program for brightness, contrast,
+ * white-balance, and resolution. */
+
+static void qc_setup(struct qcam *qcam)
+{
+ qc_reset(qcam);
+
+ /* Set the brightness. */
+ qcam_set(qcam, 11, qcam->brightness);
+
+ /* Set the height and width. These refer to the actual
+ CCD area *before* applying the selected decimation. */
+ qcam_set(qcam, 17, qcam->ccd_height);
+ qcam_set(qcam, 19, qcam->ccd_width / 2);
+
+ /* Set top and left. */
+ qcam_set(qcam, 0xd, qcam->top);
+ qcam_set(qcam, 0xf, qcam->left);
+
+ /* Set contrast and white balance. */
+ qcam_set(qcam, 0x19, qcam->contrast);
+ qcam_set(qcam, 0x1f, qcam->whitebal);
+
+ /* Set the speed. */
+ qcam_set(qcam, 45, 2);
+}
+
+/* Read some bytes from the camera and put them in the buffer.
+ nbytes should be a multiple of 3, because bidirectional mode gives
+ us three bytes at a time. */
+
+static unsigned int qcam_read_bytes(struct qcam *qcam, unsigned char *buf, unsigned int nbytes)
+{
+ unsigned int bytes = 0;
+
+ qcam_set_ack(qcam, 0);
+ if (qcam->bidirectional) {
+ /* It's a bidirectional port */
+ while (bytes < nbytes) {
+ unsigned int lo1, hi1, lo2, hi2;
+ unsigned char r, g, b;
+
+ if (qcam_await_ready2(qcam, 1))
+ return bytes;
+ lo1 = parport_read_data(qcam->pport) >> 1;
+ hi1 = ((parport_read_status(qcam->pport) >> 3) & 0x1f) ^ 0x10;
+ qcam_set_ack(qcam, 1);
+ if (qcam_await_ready2(qcam, 0))
+ return bytes;
+ lo2 = parport_read_data(qcam->pport) >> 1;
+ hi2 = ((parport_read_status(qcam->pport) >> 3) & 0x1f) ^ 0x10;
+ qcam_set_ack(qcam, 0);
+ r = lo1 | ((hi1 & 1) << 7);
+ g = ((hi1 & 0x1e) << 3) | ((hi2 & 0x1e) >> 1);
+ b = lo2 | ((hi2 & 1) << 7);
+ if (force_rgb) {
+ buf[bytes++] = r;
+ buf[bytes++] = g;
+ buf[bytes++] = b;
+ } else {
+ buf[bytes++] = b;
+ buf[bytes++] = g;
+ buf[bytes++] = r;
+ }
+ }
+ } else {
+ /* It's a unidirectional port */
+ int i = 0, n = bytes;
+ unsigned char rgb[3];
+
+ while (bytes < nbytes) {
+ unsigned int hi, lo;
+
+ if (qcam_await_ready1(qcam, 1))
+ return bytes;
+ hi = (parport_read_status(qcam->pport) & 0xf0);
+ qcam_set_ack(qcam, 1);
+ if (qcam_await_ready1(qcam, 0))
+ return bytes;
+ lo = (parport_read_status(qcam->pport) & 0xf0);
+ qcam_set_ack(qcam, 0);
+ /* flip some bits */
+ rgb[(i = bytes++ % 3)] = (hi | (lo >> 4)) ^ 0x88;
+ if (i >= 2) {
+get_fragment:
+ if (force_rgb) {
+ buf[n++] = rgb[0];
+ buf[n++] = rgb[1];
+ buf[n++] = rgb[2];
+ } else {
+ buf[n++] = rgb[2];
+ buf[n++] = rgb[1];
+ buf[n++] = rgb[0];
+ }
+ }
+ }
+ if (i) {
+ i = 0;
+ goto get_fragment;
+ }
+ }
+ return bytes;
+}
+
+#define BUFSZ 150
+
+static long qc_capture(struct qcam *qcam, char __user *buf, unsigned long len)
+{
+ struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
+ unsigned lines, pixelsperline, bitsperxfer;
+ unsigned int is_bi_dir = qcam->bidirectional;
+ size_t wantlen, outptr = 0;
+ char tmpbuf[BUFSZ];
+
+ if (!access_ok(VERIFY_WRITE, buf, len))
+ return -EFAULT;
+
+ /* Wait for camera to become ready */
+ for (;;) {
+ int i = qcam_get(qcam, 41);
+
+ if (i == -1) {
+ qc_setup(qcam);
+ return -EIO;
+ }
+ if ((i & 0x80) == 0)
+ break;
+ schedule();
+ }
+
+ if (qcam_set(qcam, 7, (qcam->mode | (is_bi_dir ? 1 : 0)) + 1))
+ return -EIO;
+
+ lines = qcam->height;
+ pixelsperline = qcam->width;
+ bitsperxfer = (is_bi_dir) ? 24 : 8;
+
+ if (is_bi_dir) {
+ /* Turn the port around */
+ parport_data_reverse(qcam->pport);
+ mdelay(3);
+ qcam_set_ack(qcam, 0);
+ if (qcam_await_ready1(qcam, 1)) {
+ qc_setup(qcam);
+ return -EIO;
+ }
+ qcam_set_ack(qcam, 1);
+ if (qcam_await_ready1(qcam, 0)) {
+ qc_setup(qcam);
+ return -EIO;
+ }
+ }
+
+ wantlen = lines * pixelsperline * 24 / 8;
+
+ while (wantlen) {
+ size_t t, s;
+
+ s = (wantlen > BUFSZ) ? BUFSZ : wantlen;
+ t = qcam_read_bytes(qcam, tmpbuf, s);
+ if (outptr < len) {
+ size_t sz = len - outptr;
+
+ if (sz > t)
+ sz = t;
+ if (__copy_to_user(buf + outptr, tmpbuf, sz))
+ break;
+ outptr += sz;
+ }
+ wantlen -= t;
+ if (t < s)
+ break;
+ cond_resched();
+ }
+
+ len = outptr;
+
+ if (wantlen) {
+ v4l2_err(v4l2_dev, "short read.\n");
+ if (is_bi_dir)
+ parport_data_forward(qcam->pport);
+ qc_setup(qcam);
+ return len;
+ }
+
+ if (is_bi_dir) {
+ int l;
+
+ do {
+ l = qcam_read_bytes(qcam, tmpbuf, 3);
+ cond_resched();
+ } while (l && (tmpbuf[0] == 0x7e || tmpbuf[1] == 0x7e || tmpbuf[2] == 0x7e));
+ if (force_rgb) {
+ if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf)
+ v4l2_err(v4l2_dev, "bad EOF\n");
+ } else {
+ if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe)
+ v4l2_err(v4l2_dev, "bad EOF\n");
+ }
+ qcam_set_ack(qcam, 0);
+ if (qcam_await_ready1(qcam, 1)) {
+ v4l2_err(v4l2_dev, "no ack after EOF\n");
+ parport_data_forward(qcam->pport);
+ qc_setup(qcam);
+ return len;
+ }
+ parport_data_forward(qcam->pport);
+ mdelay(3);
+ qcam_set_ack(qcam, 1);
+ if (qcam_await_ready1(qcam, 0)) {
+ v4l2_err(v4l2_dev, "no ack to port turnaround\n");
+ qc_setup(qcam);
+ return len;
+ }
+ } else {
+ int l;
+
+ do {
+ l = qcam_read_bytes(qcam, tmpbuf, 1);
+ cond_resched();
+ } while (l && tmpbuf[0] == 0x7e);
+ l = qcam_read_bytes(qcam, tmpbuf + 1, 2);
+ if (force_rgb) {
+ if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf)
+ v4l2_err(v4l2_dev, "bad EOF\n");
+ } else {
+ if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe)
+ v4l2_err(v4l2_dev, "bad EOF\n");
+ }
+ }
+
+ qcam_write_data(qcam, 0);
+ return len;
+}
+
+/*
+ * Video4linux interfacing
+ */
+
+static int qcam_querycap(struct file *file, void *priv,
+ struct v4l2_capability *vcap)
+{
+ struct qcam *qcam = video_drvdata(file);
+
+ strlcpy(vcap->driver, qcam->v4l2_dev.name, sizeof(vcap->driver));
+ strlcpy(vcap->card, "Color Quickcam", sizeof(vcap->card));
+ strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info));
+ vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
+ return 0;
+}
+
+static int qcam_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
+{
+ if (vin->index > 0)
+ return -EINVAL;
+ strlcpy(vin->name, "Camera", sizeof(vin->name));
+ vin->type = V4L2_INPUT_TYPE_CAMERA;
+ vin->audioset = 0;
+ vin->tuner = 0;
+ vin->std = 0;
+ vin->status = 0;
+ return 0;
+}
+
+static int qcam_g_input(struct file *file, void *fh, unsigned int *inp)
+{
+ *inp = 0;
+ return 0;
+}
+
+static int qcam_s_input(struct file *file, void *fh, unsigned int inp)
+{
+ return (inp > 0) ? -EINVAL : 0;
+}
+
+static int qcam_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 240);
+ case V4L2_CID_CONTRAST:
+ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 192);
+ case V4L2_CID_GAMMA:
+ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
+ }
+ return -EINVAL;
+}
+
+static int qcam_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct qcam *qcam = video_drvdata(file);
+ int ret = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = qcam->brightness;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->value = qcam->contrast;
+ break;
+ case V4L2_CID_GAMMA:
+ ctrl->value = qcam->whitebal;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int qcam_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct qcam *qcam = video_drvdata(file);
+ int ret = 0;
+
+ mutex_lock(&qcam->lock);
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ qcam->brightness = ctrl->value;
+ break;
+ case V4L2_CID_CONTRAST:
+ qcam->contrast = ctrl->value;
+ break;
+ case V4L2_CID_GAMMA:
+ qcam->whitebal = ctrl->value;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ if (ret == 0) {
+ parport_claim_or_block(qcam->pdev);
+ qc_setup(qcam);
+ parport_release(qcam->pdev);
+ }
+ mutex_unlock(&qcam->lock);
+ return ret;
+}
+
+static int qcam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct qcam *qcam = video_drvdata(file);
+ struct v4l2_pix_format *pix = &fmt->fmt.pix;
+
+ pix->width = qcam->width;
+ pix->height = qcam->height;
+ pix->pixelformat = V4L2_PIX_FMT_RGB24;
+ pix->field = V4L2_FIELD_NONE;
+ pix->bytesperline = 3 * qcam->width;
+ pix->sizeimage = 3 * qcam->width * qcam->height;
+ /* Just a guess */
+ pix->colorspace = V4L2_COLORSPACE_SRGB;
+ return 0;
+}
+
+static int qcam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct v4l2_pix_format *pix = &fmt->fmt.pix;
+
+ if (pix->height < 60 || pix->width < 80) {
+ pix->height = 60;
+ pix->width = 80;
+ } else if (pix->height < 120 || pix->width < 160) {
+ pix->height = 120;
+ pix->width = 160;
+ } else {
+ pix->height = 240;
+ pix->width = 320;
+ }
+ pix->pixelformat = V4L2_PIX_FMT_RGB24;
+ pix->field = V4L2_FIELD_NONE;
+ pix->bytesperline = 3 * pix->width;
+ pix->sizeimage = 3 * pix->width * pix->height;
+ /* Just a guess */
+ pix->colorspace = V4L2_COLORSPACE_SRGB;
+ return 0;
+}
+
+static int qcam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct qcam *qcam = video_drvdata(file);
+ struct v4l2_pix_format *pix = &fmt->fmt.pix;
+ int ret = qcam_try_fmt_vid_cap(file, fh, fmt);
+
+ if (ret)
+ return ret;
+ switch (pix->height) {
+ case 60:
+ qcam->mode = QC_DECIMATION_4;
+ break;
+ case 120:
+ qcam->mode = QC_DECIMATION_2;
+ break;
+ default:
+ qcam->mode = QC_DECIMATION_1;
+ break;
+ }
+
+ mutex_lock(&qcam->lock);
+ qcam->mode |= QC_MILLIONS;
+ qcam->height = pix->height;
+ qcam->width = pix->width;
+ parport_claim_or_block(qcam->pdev);
+ qc_setup(qcam);
+ parport_release(qcam->pdev);
+ mutex_unlock(&qcam->lock);
+ return 0;
+}
+
+static int qcam_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
+{
+ static struct v4l2_fmtdesc formats[] = {
+ { 0, 0, 0,
+ "RGB 8:8:8", V4L2_PIX_FMT_RGB24,
+ { 0, 0, 0, 0 }
+ },
+ };
+ enum v4l2_buf_type type = fmt->type;
+
+ if (fmt->index > 0)
+ return -EINVAL;
+
+ *fmt = formats[fmt->index];
+ fmt->type = type;
+ return 0;
+}
+
+static ssize_t qcam_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct qcam *qcam = video_drvdata(file);
+ int len;
+
+ mutex_lock(&qcam->lock);
+ parport_claim_or_block(qcam->pdev);
+ /* Probably should have a semaphore against multiple users */
+ len = qc_capture(qcam, buf, count);
+ parport_release(qcam->pdev);
+ mutex_unlock(&qcam->lock);
+ return len;
+}
+
+static const struct v4l2_file_operations qcam_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = video_ioctl2,
+ .read = qcam_read,
+};
+
+static const struct v4l2_ioctl_ops qcam_ioctl_ops = {
+ .vidioc_querycap = qcam_querycap,
+ .vidioc_g_input = qcam_g_input,
+ .vidioc_s_input = qcam_s_input,
+ .vidioc_enum_input = qcam_enum_input,
+ .vidioc_queryctrl = qcam_queryctrl,
+ .vidioc_g_ctrl = qcam_g_ctrl,
+ .vidioc_s_ctrl = qcam_s_ctrl,
+ .vidioc_enum_fmt_vid_cap = qcam_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = qcam_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = qcam_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = qcam_try_fmt_vid_cap,
+};
+
+/* Initialize the QuickCam driver control structure. */
+
+static struct qcam *qcam_init(struct parport *port)
+{
+ struct qcam *qcam;
+ struct v4l2_device *v4l2_dev;
+
+ qcam = kzalloc(sizeof(*qcam), GFP_KERNEL);
+ if (qcam == NULL)
+ return NULL;
+
+ v4l2_dev = &qcam->v4l2_dev;
+ strlcpy(v4l2_dev->name, "c-qcam", sizeof(v4l2_dev->name));
+
+ if (v4l2_device_register(NULL, v4l2_dev) < 0) {
+ v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
+ kfree(qcam);
+ return NULL;
+ }
+
+ qcam->pport = port;
+ qcam->pdev = parport_register_device(port, "c-qcam", NULL, NULL,
+ NULL, 0, NULL);
+
+ qcam->bidirectional = (qcam->pport->modes & PARPORT_MODE_TRISTATE) ? 1 : 0;
+
+ if (qcam->pdev == NULL) {
+ v4l2_err(v4l2_dev, "couldn't register for %s.\n", port->name);
+ kfree(qcam);
+ return NULL;
+ }
+
+ strlcpy(qcam->vdev.name, "Colour QuickCam", sizeof(qcam->vdev.name));
+ qcam->vdev.v4l2_dev = v4l2_dev;
+ qcam->vdev.fops = &qcam_fops;
+ qcam->vdev.ioctl_ops = &qcam_ioctl_ops;
+ qcam->vdev.release = video_device_release_empty;
+ video_set_drvdata(&qcam->vdev, qcam);
+
+ mutex_init(&qcam->lock);
+ qcam->width = qcam->ccd_width = 320;
+ qcam->height = qcam->ccd_height = 240;
+ qcam->mode = QC_MILLIONS | QC_DECIMATION_1;
+ qcam->contrast = 192;
+ qcam->brightness = 240;
+ qcam->whitebal = 128;
+ qcam->top = 1;
+ qcam->left = 14;
+ return qcam;
+}
+
+static int init_cqcam(struct parport *port)
+{
+ struct qcam *qcam;
+ struct v4l2_device *v4l2_dev;
+
+ if (parport[0] != -1) {
+ /* The user gave specific instructions */
+ int i, found = 0;
+
+ for (i = 0; i < MAX_CAMS && parport[i] != -1; i++) {
+ if (parport[0] == port->number)
+ found = 1;
+ }
+ if (!found)
+ return -ENODEV;
+ }
+
+ if (num_cams == MAX_CAMS)
+ return -ENOSPC;
+
+ qcam = qcam_init(port);
+ if (qcam == NULL)
+ return -ENODEV;
+
+ v4l2_dev = &qcam->v4l2_dev;
+
+ parport_claim_or_block(qcam->pdev);
+
+ qc_reset(qcam);
+
+ if (probe && qc_detect(qcam) == 0) {
+ parport_release(qcam->pdev);
+ parport_unregister_device(qcam->pdev);
+ kfree(qcam);
+ return -ENODEV;
+ }
+
+ qc_setup(qcam);
+
+ parport_release(qcam->pdev);
+
+ if (video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
+ v4l2_err(v4l2_dev, "Unable to register Colour QuickCam on %s\n",
+ qcam->pport->name);
+ parport_unregister_device(qcam->pdev);
+ kfree(qcam);
+ return -ENODEV;
+ }
+
+ v4l2_info(v4l2_dev, "%s: Colour QuickCam found on %s\n",
+ video_device_node_name(&qcam->vdev), qcam->pport->name);
+
+ qcams[num_cams++] = qcam;
+
+ return 0;
+}
+
+static void close_cqcam(struct qcam *qcam)
+{
+ video_unregister_device(&qcam->vdev);
+ parport_unregister_device(qcam->pdev);
+ kfree(qcam);
+}
+
+static void cq_attach(struct parport *port)
+{
+ init_cqcam(port);
+}
+
+static void cq_detach(struct parport *port)
+{
+ /* Write this some day. */
+}
+
+static struct parport_driver cqcam_driver = {
+ .name = "cqcam",
+ .attach = cq_attach,
+ .detach = cq_detach,
+};
+
+static int __init cqcam_init(void)
+{
+ printk(KERN_INFO BANNER "\n");
+
+ return parport_register_driver(&cqcam_driver);
+}
+
+static void __exit cqcam_cleanup(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < num_cams; i++)
+ close_cqcam(qcams[i]);
+
+ parport_unregister_driver(&cqcam_driver);
+}
+
+MODULE_AUTHOR("Philip Blundell <philb@gnu.org>");
+MODULE_DESCRIPTION(BANNER);
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.4");
+
+module_init(cqcam_init);
+module_exit(cqcam_cleanup);
diff --git a/drivers/media/video/cmoscam.c b/drivers/media/video/cmoscam.c
new file mode 100755
index 00000000..fad74f68
--- /dev/null
+++ b/drivers/media/video/cmoscam.c
@@ -0,0 +1,99 @@
+ /*++
+ * linux/drivers/media/video/cmoscam.c
+ * WonderMedia v4l cmos device driver
+*
+ * Copyright c 2010 WonderMedia Technologies, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+*
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+--*/
+
+#include "cmoscam.h"
+
+static int video_nr = -1; /* /dev/videoN, -1 for autodetect */
+
+static const struct v4l2_ioctl_ops cmos_ioctl_ops = {
+ .vidioc_querycap = cmos_cam_querycap,
+ .vidioc_enum_input = cmos_cam_enum_input,
+ .vidioc_g_input = cmos_cam_g_input,
+ .vidioc_s_input = cmos_cam_s_input,
+ .vidioc_s_std = cmos_cam_s_std,
+ .vidioc_reqbufs = cmos_cam_reqbufs,
+ .vidioc_querybuf = cmos_cam_querybuf,
+ .vidioc_qbuf = cmos_cam_qbuf,
+ .vidioc_dqbuf = cmos_cam_dqbuf,
+ .vidioc_streamon = cmos_cam_streamon,
+ .vidioc_streamoff = cmos_cam_streamoff,
+ .vidioc_enum_fmt_vid_cap = cmos_cam_enum_fmt_cap,
+ .vidioc_g_fmt_vid_cap = cmos_cam_g_fmt_cap,
+ .vidioc_s_fmt_vid_cap = cmos_cam_s_fmt_cap,
+ .vidioc_try_fmt_vid_cap = cmos_cam_try_fmt_cap,
+ .vidioc_queryctrl = cmos_cam_queryctrl,
+ .vidioc_s_ctrl = cmos_cam_s_ctrl,
+ .vidioc_g_ctrl = cmos_cam_g_ctrl,
+};
+
+static const struct v4l2_file_operations cmos_cam_fops = {
+ .owner = THIS_MODULE,
+ .open = cmos_cam_open,
+ .release = cmos_cam_release,
+#if !defined(LINUX_VERSION_CODE)
+ #include <linux/version.h>
+#endif
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,32)
+ .ioctl = video_ioctl2,
+#else
+ .unlocked_ioctl = video_ioctl2,
+#endif
+ .mmap = cmos_cam_mmap,
+};
+
+struct video_device cmos_cam = {
+ .name = "cmos_cam",
+ .fops = &cmos_cam_fops,
+ .ioctl_ops = &cmos_ioctl_ops,
+ .release = video_device_release,
+ .tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM,
+ .minor = -1
+};
+
+
+static int __init cmos_cam_init(void)
+{
+ int ret;
+
+ ret = video_register_device(&cmos_cam, VFL_TYPE_GRABBER, video_nr);
+ if (ret < 0)
+ printk(KERN_ERR "WonderMedia CMOS camera register failed\n");
+ else
+ printk(KERN_ERR "WonderMedia CMOS camera register OK \n");
+
+
+ return ret;
+}
+
+static void __exit cmos_cam_exit(void)
+{
+ video_unregister_device(&cmos_cam);
+}
+
+module_init(cmos_cam_init);
+module_exit(cmos_cam_exit);
+
+MODULE_DESCRIPTION("WonderMedia CMOS Camera for ARM SoC");
+MODULE_AUTHOR("WonderMedia Technologies, Inc.");
+MODULE_LICENSE("Dual BSD/GPL");
+
diff --git a/drivers/media/video/cmoscam.h b/drivers/media/video/cmoscam.h
new file mode 100755
index 00000000..82d6640c
--- /dev/null
+++ b/drivers/media/video/cmoscam.h
@@ -0,0 +1,77 @@
+ /*++
+ * linux/drivers/media/video/cmoscam.h
+ * WonderMedia v4l cmos device driver
+*
+ * Copyright c 2010 WonderMedia Technologies, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+*
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+--*/
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/videodev2.h>
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+#include <linux/videodev.h>
+#endif
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+
+
+
+extern int cmos_cam_open ( struct file *);
+extern int cmos_cam_release (struct file *);
+extern ssize_t cmos_cam_read(struct file *, char __user *, size_t, loff_t *);
+extern int cmos_cam_mmap (struct file *, struct vm_area_struct *);
+extern unsigned int cmos_cam_poll (struct file *, struct poll_table_struct *);
+extern int cmos_cam_ioctl (struct inode *, struct file *, unsigned int, unsigned long);
+extern long cmos_cam_compat_ioctl (struct file *, unsigned int, unsigned long);
+
+extern int cmos_cam_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap);
+extern int cmos_cam_cropcap(struct file *file, void *__fh,
+ struct v4l2_cropcap *cropcap);
+
+extern int cmos_cam_enum_fmt_cap(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f);
+extern int cmos_cam_g_fmt_cap(struct file *file, void *fh,
+ struct v4l2_format *f);
+extern int cmos_cam_try_fmt_cap(struct file *file, void *fh,
+ struct v4l2_format *f);
+extern int cmos_cam_s_fmt_cap(struct file *file, void *fh,
+ struct v4l2_format *f);
+extern int cmos_cam_reqbufs(struct file *file, void *fh,
+ struct v4l2_requestbuffers *b);
+extern int cmos_cam_querybuf(struct file *file, void *fh,
+ struct v4l2_buffer *b);
+extern int cmos_cam_qbuf(struct file *file, void *fh,
+ struct v4l2_buffer *b);
+extern int cmos_cam_dqbuf(struct file *file, void *fh,
+ struct v4l2_buffer *b);
+extern int cmos_cam_s_std(struct file *file, void *fh, v4l2_std_id *norm);
+extern int cmos_cam_enum_input(struct file *file, void *fh,
+ struct v4l2_input *inp);
+extern int cmos_cam_g_input(struct file *file, void *fh, unsigned int *i);
+extern int cmos_cam_s_input(struct file *file, void *fh, unsigned int i);
+extern int cmos_cam_queryctrl(struct file *file, void *fh,
+ struct v4l2_queryctrl *a);
+extern int cmos_cam_g_ctrl(struct file *file, void *fh, struct v4l2_control *a);
+extern int cmos_cam_s_ctrl(struct file *file, void *fh, struct v4l2_control *a);
+extern int cmos_cam_streamon(struct file *file, void *fh, enum v4l2_buf_type i);
+extern int cmos_cam_streamoff(struct file *file, void *fh,
+ enum v4l2_buf_type i);
+
diff --git a/drivers/media/video/cpia2/Kconfig b/drivers/media/video/cpia2/Kconfig
new file mode 100644
index 00000000..66e9283f
--- /dev/null
+++ b/drivers/media/video/cpia2/Kconfig
@@ -0,0 +1,9 @@
+config VIDEO_CPIA2
+ tristate "CPiA2 Video For Linux"
+ depends on VIDEO_DEV && USB && VIDEO_V4L2
+ ---help---
+ This is the video4linux driver for cameras based on Vision's CPiA2
+ (Colour Processor Interface ASIC), such as the Digital Blue QX5
+ Microscope. If you have one of these cameras, say Y here
+
+ This driver is also available as a module (cpia2).
diff --git a/drivers/media/video/cpia2/Makefile b/drivers/media/video/cpia2/Makefile
new file mode 100644
index 00000000..828cf1b1
--- /dev/null
+++ b/drivers/media/video/cpia2/Makefile
@@ -0,0 +1,3 @@
+cpia2-objs := cpia2_v4l.o cpia2_usb.o cpia2_core.o
+
+obj-$(CONFIG_VIDEO_CPIA2) += cpia2.o
diff --git a/drivers/media/video/cpia2/cpia2.h b/drivers/media/video/cpia2/cpia2.h
new file mode 100644
index 00000000..ab252188
--- /dev/null
+++ b/drivers/media/video/cpia2/cpia2.h
@@ -0,0 +1,489 @@
+/****************************************************************************
+ *
+ * Filename: cpia2.h
+ *
+ * Copyright 2001, STMicrolectronics, Inc.
+ *
+ * Contact: steve.miller@st.com
+ *
+ * Description:
+ * This is a USB driver for CPiA2 based video cameras.
+ *
+ * This driver is modelled on the cpia usb driver by
+ * Jochen Scharrlach and Johannes Erdfeldt.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************************/
+
+#ifndef __CPIA2_H__
+#define __CPIA2_H__
+
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <linux/usb.h>
+#include <linux/poll.h>
+
+#include "cpia2dev.h"
+#include "cpia2_registers.h"
+
+/* define for verbose debug output */
+//#define _CPIA2_DEBUG_
+
+/***
+ * Image defines
+ ***/
+
+/* Misc constants */
+#define ALLOW_CORRUPT 0 /* Causes collater to discard checksum */
+
+/* USB Transfer mode */
+#define XFER_ISOC 0
+#define XFER_BULK 1
+
+/* USB Alternates */
+#define USBIF_CMDONLY 0
+#define USBIF_BULK 1
+#define USBIF_ISO_1 2 /* 128 bytes/ms */
+#define USBIF_ISO_2 3 /* 384 bytes/ms */
+#define USBIF_ISO_3 4 /* 640 bytes/ms */
+#define USBIF_ISO_4 5 /* 768 bytes/ms */
+#define USBIF_ISO_5 6 /* 896 bytes/ms */
+#define USBIF_ISO_6 7 /* 1023 bytes/ms */
+
+/* Flicker Modes */
+#define NEVER_FLICKER 0
+#define ANTI_FLICKER_ON 1
+#define FLICKER_60 60
+#define FLICKER_50 50
+
+/* Debug flags */
+#define DEBUG_NONE 0
+#define DEBUG_REG 0x00000001
+#define DEBUG_DUMP_PATCH 0x00000002
+#define DEBUG_DUMP_REGS 0x00000004
+
+/***
+ * Video frame sizes
+ ***/
+enum {
+ VIDEOSIZE_VGA = 0, /* 640x480 */
+ VIDEOSIZE_CIF, /* 352x288 */
+ VIDEOSIZE_QVGA, /* 320x240 */
+ VIDEOSIZE_QCIF, /* 176x144 */
+ VIDEOSIZE_288_216,
+ VIDEOSIZE_256_192,
+ VIDEOSIZE_224_168,
+ VIDEOSIZE_192_144,
+};
+
+#define STV_IMAGE_CIF_ROWS 288
+#define STV_IMAGE_CIF_COLS 352
+
+#define STV_IMAGE_QCIF_ROWS 144
+#define STV_IMAGE_QCIF_COLS 176
+
+#define STV_IMAGE_VGA_ROWS 480
+#define STV_IMAGE_VGA_COLS 640
+
+#define STV_IMAGE_QVGA_ROWS 240
+#define STV_IMAGE_QVGA_COLS 320
+
+#define JPEG_MARKER_COM (1<<6) /* Comment segment */
+
+/***
+ * Enums
+ ***/
+/* Sensor types available with cpia2 asics */
+enum sensors {
+ CPIA2_SENSOR_410,
+ CPIA2_SENSOR_500
+};
+
+/* Asic types available in the CPiA2 architecture */
+#define CPIA2_ASIC_672 0x67
+
+/* Device types (stv672, stv676, etc) */
+#define DEVICE_STV_672 0x0001
+#define DEVICE_STV_676 0x0002
+
+enum frame_status {
+ FRAME_EMPTY,
+ FRAME_READING, /* In the process of being grabbed into */
+ FRAME_READY, /* Ready to be read */
+ FRAME_ERROR,
+};
+
+/***
+ * Register access (for USB request byte)
+ ***/
+enum {
+ CAMERAACCESS_SYSTEM = 0,
+ CAMERAACCESS_VC,
+ CAMERAACCESS_VP,
+ CAMERAACCESS_IDATA
+};
+
+#define CAMERAACCESS_TYPE_BLOCK 0x00
+#define CAMERAACCESS_TYPE_RANDOM 0x04
+#define CAMERAACCESS_TYPE_MASK 0x08
+#define CAMERAACCESS_TYPE_REPEAT 0x0C
+
+#define TRANSFER_READ 0
+#define TRANSFER_WRITE 1
+
+#define DEFAULT_ALT USBIF_ISO_6
+#define DEFAULT_BRIGHTNESS 0x46
+#define DEFAULT_CONTRAST 0x93
+#define DEFAULT_SATURATION 0x7f
+#define DEFAULT_TARGET_KB 0x30
+
+/* Power state */
+#define HI_POWER_MODE CPIA2_SYSTEM_CONTROL_HIGH_POWER
+#define LO_POWER_MODE CPIA2_SYSTEM_CONTROL_LOW_POWER
+
+
+/********
+ * Commands
+ *******/
+enum {
+ CPIA2_CMD_NONE = 0,
+ CPIA2_CMD_GET_VERSION,
+ CPIA2_CMD_GET_PNP_ID,
+ CPIA2_CMD_GET_ASIC_TYPE,
+ CPIA2_CMD_GET_SENSOR,
+ CPIA2_CMD_GET_VP_DEVICE,
+ CPIA2_CMD_GET_VP_BRIGHTNESS,
+ CPIA2_CMD_SET_VP_BRIGHTNESS,
+ CPIA2_CMD_GET_CONTRAST,
+ CPIA2_CMD_SET_CONTRAST,
+ CPIA2_CMD_GET_VP_SATURATION,
+ CPIA2_CMD_SET_VP_SATURATION,
+ CPIA2_CMD_GET_VP_GPIO_DIRECTION,
+ CPIA2_CMD_SET_VP_GPIO_DIRECTION,
+ CPIA2_CMD_GET_VP_GPIO_DATA,
+ CPIA2_CMD_SET_VP_GPIO_DATA,
+ CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION,
+ CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION,
+ CPIA2_CMD_GET_VC_MP_GPIO_DATA,
+ CPIA2_CMD_SET_VC_MP_GPIO_DATA,
+ CPIA2_CMD_ENABLE_PACKET_CTRL,
+ CPIA2_CMD_GET_FLICKER_MODES,
+ CPIA2_CMD_SET_FLICKER_MODES,
+ CPIA2_CMD_RESET_FIFO, /* clear fifo and enable stream block */
+ CPIA2_CMD_SET_HI_POWER,
+ CPIA2_CMD_SET_LOW_POWER,
+ CPIA2_CMD_CLEAR_V2W_ERR,
+ CPIA2_CMD_SET_USER_MODE,
+ CPIA2_CMD_GET_USER_MODE,
+ CPIA2_CMD_FRAMERATE_REQ,
+ CPIA2_CMD_SET_COMPRESSION_STATE,
+ CPIA2_CMD_GET_WAKEUP,
+ CPIA2_CMD_SET_WAKEUP,
+ CPIA2_CMD_GET_PW_CONTROL,
+ CPIA2_CMD_SET_PW_CONTROL,
+ CPIA2_CMD_GET_SYSTEM_CTRL,
+ CPIA2_CMD_SET_SYSTEM_CTRL,
+ CPIA2_CMD_GET_VP_SYSTEM_STATE,
+ CPIA2_CMD_GET_VP_SYSTEM_CTRL,
+ CPIA2_CMD_SET_VP_SYSTEM_CTRL,
+ CPIA2_CMD_GET_VP_EXP_MODES,
+ CPIA2_CMD_SET_VP_EXP_MODES,
+ CPIA2_CMD_GET_DEVICE_CONFIG,
+ CPIA2_CMD_SET_DEVICE_CONFIG,
+ CPIA2_CMD_SET_SERIAL_ADDR,
+ CPIA2_CMD_SET_SENSOR_CR1,
+ CPIA2_CMD_GET_VC_CONTROL,
+ CPIA2_CMD_SET_VC_CONTROL,
+ CPIA2_CMD_SET_TARGET_KB,
+ CPIA2_CMD_SET_DEF_JPEG_OPT,
+ CPIA2_CMD_REHASH_VP4,
+ CPIA2_CMD_GET_USER_EFFECTS,
+ CPIA2_CMD_SET_USER_EFFECTS
+};
+
+enum user_cmd {
+ COMMAND_NONE = 0x00000001,
+ COMMAND_SET_FPS = 0x00000002,
+ COMMAND_SET_COLOR_PARAMS = 0x00000004,
+ COMMAND_GET_COLOR_PARAMS = 0x00000008,
+ COMMAND_SET_FORMAT = 0x00000010, /* size, etc */
+ COMMAND_SET_FLICKER = 0x00000020
+};
+
+/***
+ * Some defines specific to the 676 chip
+ ***/
+#define CAMACC_CIF 0x01
+#define CAMACC_VGA 0x02
+#define CAMACC_QCIF 0x04
+#define CAMACC_QVGA 0x08
+
+
+struct cpia2_register {
+ u8 index;
+ u8 value;
+};
+
+struct cpia2_reg_mask {
+ u8 index;
+ u8 and_mask;
+ u8 or_mask;
+ u8 fill;
+};
+
+struct cpia2_command {
+ u32 command;
+ u8 req_mode; /* (Block or random) | registerBank */
+ u8 reg_count;
+ u8 direction;
+ u8 start;
+ union reg_types {
+ struct cpia2_register registers[32];
+ struct cpia2_reg_mask masks[16];
+ u8 block_data[64];
+ u8 *patch_data; /* points to function defined block */
+ } buffer;
+};
+
+struct camera_params {
+ struct {
+ u8 firmware_revision_hi; /* For system register set (bank 0) */
+ u8 firmware_revision_lo;
+ u8 asic_id; /* Video Compressor set (bank 1) */
+ u8 asic_rev;
+ u8 vp_device_hi; /* Video Processor set (bank 2) */
+ u8 vp_device_lo;
+ u8 sensor_flags;
+ u8 sensor_rev;
+ } version;
+
+ struct {
+ u32 device_type; /* enumerated from vendor/product ids.
+ * Currently, either STV_672 or STV_676 */
+ u16 vendor;
+ u16 product;
+ u16 device_revision;
+ } pnp_id;
+
+ struct {
+ u8 brightness; /* CPIA2_VP_EXPOSURE_TARGET */
+ u8 contrast; /* Note: this is CPIA2_VP_YRANGE */
+ u8 saturation; /* CPIA2_VP_SATURATION */
+ } color_params;
+
+ struct {
+ u8 cam_register;
+ u8 flicker_mode_req; /* 1 if flicker on, else never flicker */
+ int mains_frequency;
+ } flicker_control;
+
+ struct {
+ u8 jpeg_options;
+ u8 creep_period;
+ u8 user_squeeze;
+ u8 inhibit_htables;
+ } compression;
+
+ struct {
+ u8 ohsize; /* output image size */
+ u8 ovsize;
+ u8 hcrop; /* cropping start_pos/4 */
+ u8 vcrop;
+ u8 hphase; /* scaling registers */
+ u8 vphase;
+ u8 hispan;
+ u8 vispan;
+ u8 hicrop;
+ u8 vicrop;
+ u8 hifraction;
+ u8 vifraction;
+ } image_size;
+
+ struct {
+ int width; /* actual window width */
+ int height; /* actual window height */
+ } roi;
+
+ struct {
+ u8 video_mode;
+ u8 frame_rate;
+ u8 video_size; /* Not a register, just a convenience for cropped sizes */
+ u8 gpio_direction;
+ u8 gpio_data;
+ u8 system_ctrl;
+ u8 system_state;
+ u8 lowlight_boost; /* Bool: 0 = off, 1 = on */
+ u8 device_config;
+ u8 exposure_modes;
+ u8 user_effects;
+ } vp_params;
+
+ struct {
+ u8 pw_control;
+ u8 wakeup;
+ u8 vc_control;
+ u8 vc_mp_direction;
+ u8 vc_mp_data;
+ u8 target_kb;
+ } vc_params;
+
+ struct {
+ u8 power_mode;
+ u8 system_ctrl;
+ u8 stream_mode; /* This is the current alternate for usb drivers */
+ u8 allow_corrupt;
+ } camera_state;
+};
+
+#define NUM_SBUF 2
+
+struct cpia2_sbuf {
+ char *data;
+ struct urb *urb;
+};
+
+struct framebuf {
+ struct timeval timestamp;
+ unsigned long seq;
+ int num;
+ int length;
+ int max_length;
+ volatile enum frame_status status;
+ u8 *data;
+ struct framebuf *next;
+};
+
+struct cpia2_fh {
+ enum v4l2_priority prio;
+ u8 mmapped;
+};
+
+struct camera_data {
+ /* locks */
+ struct mutex v4l2_lock; /* serialize file operations */
+ struct v4l2_prio_state prio;
+
+ /* camera status */
+ volatile int present; /* Is the camera still present? */
+ int open_count; /* # of process that have camera open */
+ int first_image_seen;
+ u8 mains_freq; /* for flicker control */
+ enum sensors sensor_type;
+ u8 flush;
+ u8 mmapped;
+ int streaming; /* 0 = no, 1 = yes */
+ int xfer_mode; /* XFER_BULK or XFER_ISOC */
+ struct camera_params params; /* camera settings */
+
+ /* v4l */
+ int video_size; /* VIDEO_SIZE_ */
+ struct video_device *vdev; /* v4l videodev */
+ u32 width;
+ u32 height; /* Its size */
+ __u32 pixelformat; /* Format fourcc */
+
+ /* USB */
+ struct usb_device *dev;
+ unsigned char iface;
+ unsigned int cur_alt;
+ unsigned int old_alt;
+ struct cpia2_sbuf sbuf[NUM_SBUF]; /* Double buffering */
+
+ wait_queue_head_t wq_stream;
+
+ /* Buffering */
+ u32 frame_size;
+ int num_frames;
+ unsigned long frame_count;
+ u8 *frame_buffer; /* frame buffer data */
+ struct framebuf *buffers;
+ struct framebuf * volatile curbuff;
+ struct framebuf *workbuff;
+
+ /* MJPEG Extension */
+ int APPn; /* Number of APP segment to be written, must be 0..15 */
+ int APP_len; /* Length of data in JPEG APPn segment */
+ char APP_data[60]; /* Data in the JPEG APPn segment. */
+
+ int COM_len; /* Length of data in JPEG COM segment */
+ char COM_data[60]; /* Data in JPEG COM segment */
+};
+
+/* v4l */
+int cpia2_register_camera(struct camera_data *cam);
+void cpia2_unregister_camera(struct camera_data *cam);
+
+/* core */
+int cpia2_reset_camera(struct camera_data *cam);
+int cpia2_set_low_power(struct camera_data *cam);
+void cpia2_dbg_dump_registers(struct camera_data *cam);
+int cpia2_match_video_size(int width, int height);
+void cpia2_set_camera_state(struct camera_data *cam);
+void cpia2_save_camera_state(struct camera_data *cam);
+void cpia2_set_color_params(struct camera_data *cam);
+void cpia2_set_brightness(struct camera_data *cam, unsigned char value);
+void cpia2_set_contrast(struct camera_data *cam, unsigned char value);
+void cpia2_set_saturation(struct camera_data *cam, unsigned char value);
+int cpia2_set_flicker_mode(struct camera_data *cam, int mode);
+void cpia2_set_format(struct camera_data *cam);
+int cpia2_send_command(struct camera_data *cam, struct cpia2_command *cmd);
+int cpia2_do_command(struct camera_data *cam,
+ unsigned int command,
+ unsigned char direction, unsigned char param);
+struct camera_data *cpia2_init_camera_struct(void);
+int cpia2_init_camera(struct camera_data *cam);
+int cpia2_allocate_buffers(struct camera_data *cam);
+void cpia2_free_buffers(struct camera_data *cam);
+long cpia2_read(struct camera_data *cam,
+ char __user *buf, unsigned long count, int noblock);
+unsigned int cpia2_poll(struct camera_data *cam,
+ struct file *filp, poll_table *wait);
+int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma);
+void cpia2_set_property_flip(struct camera_data *cam, int prop_val);
+void cpia2_set_property_mirror(struct camera_data *cam, int prop_val);
+int cpia2_set_target_kb(struct camera_data *cam, unsigned char value);
+int cpia2_set_gpio(struct camera_data *cam, unsigned char setting);
+int cpia2_set_fps(struct camera_data *cam, int framerate);
+
+/* usb */
+int cpia2_usb_init(void);
+void cpia2_usb_cleanup(void);
+int cpia2_usb_transfer_cmd(struct camera_data *cam, void *registers,
+ u8 request, u8 start, u8 count, u8 direction);
+int cpia2_usb_stream_start(struct camera_data *cam, unsigned int alternate);
+int cpia2_usb_stream_stop(struct camera_data *cam);
+int cpia2_usb_stream_pause(struct camera_data *cam);
+int cpia2_usb_stream_resume(struct camera_data *cam);
+int cpia2_usb_change_streaming_alternate(struct camera_data *cam,
+ unsigned int alt);
+
+
+/* ----------------------- debug functions ---------------------- */
+#ifdef _CPIA2_DEBUG_
+#define ALOG(lev, fmt, args...) printk(lev "%s:%d %s(): " fmt, __FILE__, __LINE__, __func__, ## args)
+#define LOG(fmt, args...) ALOG(KERN_INFO, fmt, ## args)
+#define ERR(fmt, args...) ALOG(KERN_ERR, fmt, ## args)
+#define DBG(fmt, args...) ALOG(KERN_DEBUG, fmt, ## args)
+#else
+#define ALOG(fmt,args...) printk(fmt,##args)
+#define LOG(fmt,args...) ALOG(KERN_INFO "cpia2: "fmt,##args)
+#define ERR(fmt,args...) ALOG(KERN_ERR "cpia2: "fmt,##args)
+#define DBG(fmn,args...) do {} while(0)
+#endif
+/* No function or lineno, for shorter lines */
+#define KINFO(fmt, args...) printk(KERN_INFO fmt,##args)
+
+#endif
diff --git a/drivers/media/video/cpia2/cpia2_core.c b/drivers/media/video/cpia2/cpia2_core.c
new file mode 100644
index 00000000..ee91e295
--- /dev/null
+++ b/drivers/media/video/cpia2/cpia2_core.c
@@ -0,0 +1,2493 @@
+/****************************************************************************
+ *
+ * Filename: cpia2_core.c
+ *
+ * Copyright 2001, STMicrolectronics, Inc.
+ * Contact: steve.miller@st.com
+ *
+ * Description:
+ * This is a USB driver for CPia2 based video cameras.
+ * The infrastructure of this driver is based on the cpia usb driver by
+ * Jochen Scharrlach and Johannes Erdfeldt.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Stripped of 2.4 stuff ready for main kernel submit by
+ * Alan Cox <alan@lxorguk.ukuu.org.uk>
+ *
+ ****************************************************************************/
+
+#include "cpia2.h"
+
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/firmware.h>
+
+/* #define _CPIA2_DEBUG_ */
+
+#ifdef _CPIA2_DEBUG_
+
+static const char *block_name[] = {
+ "System",
+ "VC",
+ "VP",
+ "IDATA"
+};
+#endif
+
+static unsigned int debugs_on; /* default 0 - DEBUG_REG */
+
+
+/******************************************************************************
+ *
+ * Forward Declarations
+ *
+ *****************************************************************************/
+static int apply_vp_patch(struct camera_data *cam);
+static int set_default_user_mode(struct camera_data *cam);
+static int set_vw_size(struct camera_data *cam, int size);
+static int configure_sensor(struct camera_data *cam,
+ int reqwidth, int reqheight);
+static int config_sensor_410(struct camera_data *cam,
+ int reqwidth, int reqheight);
+static int config_sensor_500(struct camera_data *cam,
+ int reqwidth, int reqheight);
+static int set_all_properties(struct camera_data *cam);
+static void get_color_params(struct camera_data *cam);
+static void wake_system(struct camera_data *cam);
+static void set_lowlight_boost(struct camera_data *cam);
+static void reset_camera_struct(struct camera_data *cam);
+static int cpia2_set_high_power(struct camera_data *cam);
+
+/* Here we want the physical address of the memory.
+ * This is used when initializing the contents of the
+ * area and marking the pages as reserved.
+ */
+static inline unsigned long kvirt_to_pa(unsigned long adr)
+{
+ unsigned long kva, ret;
+
+ kva = (unsigned long) page_address(vmalloc_to_page((void *)adr));
+ kva |= adr & (PAGE_SIZE-1); /* restore the offset */
+ ret = __pa(kva);
+ return ret;
+}
+
+static void *rvmalloc(unsigned long size)
+{
+ void *mem;
+ unsigned long adr;
+
+ /* Round it off to PAGE_SIZE */
+ size = PAGE_ALIGN(size);
+
+ mem = vmalloc_32(size);
+ if (!mem)
+ return NULL;
+
+ memset(mem, 0, size); /* Clear the ram out, no junk to the user */
+ adr = (unsigned long) mem;
+
+ while ((long)size > 0) {
+ SetPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ return mem;
+}
+
+static void rvfree(void *mem, unsigned long size)
+{
+ unsigned long adr;
+
+ if (!mem)
+ return;
+
+ size = PAGE_ALIGN(size);
+
+ adr = (unsigned long) mem;
+ while ((long)size > 0) {
+ ClearPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ vfree(mem);
+}
+
+/******************************************************************************
+ *
+ * cpia2_do_command
+ *
+ * Send an arbitrary command to the camera. For commands that read from
+ * the camera, copy the buffers into the proper param structures.
+ *****************************************************************************/
+int cpia2_do_command(struct camera_data *cam,
+ u32 command, u8 direction, u8 param)
+{
+ int retval = 0;
+ struct cpia2_command cmd;
+ unsigned int device = cam->params.pnp_id.device_type;
+
+ cmd.command = command;
+ cmd.reg_count = 2; /* default */
+ cmd.direction = direction;
+
+ /***
+ * Set up the command.
+ ***/
+ switch (command) {
+ case CPIA2_CMD_GET_VERSION:
+ cmd.req_mode =
+ CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM;
+ cmd.start = CPIA2_SYSTEM_DEVICE_HI;
+ break;
+ case CPIA2_CMD_GET_PNP_ID:
+ cmd.req_mode =
+ CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM;
+ cmd.reg_count = 8;
+ cmd.start = CPIA2_SYSTEM_DESCRIP_VID_HI;
+ break;
+ case CPIA2_CMD_GET_ASIC_TYPE:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
+ cmd.start = CPIA2_VC_ASIC_ID;
+ break;
+ case CPIA2_CMD_GET_SENSOR:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.start = CPIA2_VP_SENSOR_FLAGS;
+ break;
+ case CPIA2_CMD_GET_VP_DEVICE:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.start = CPIA2_VP_DEVICEH;
+ break;
+ case CPIA2_CMD_SET_VP_BRIGHTNESS:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_VP_BRIGHTNESS:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ if (device == DEVICE_STV_672)
+ cmd.start = CPIA2_VP4_EXPOSURE_TARGET;
+ else
+ cmd.start = CPIA2_VP5_EXPOSURE_TARGET;
+ break;
+ case CPIA2_CMD_SET_CONTRAST:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_CONTRAST:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VP_YRANGE;
+ break;
+ case CPIA2_CMD_SET_VP_SATURATION:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_VP_SATURATION:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ if (device == DEVICE_STV_672)
+ cmd.start = CPIA2_VP_SATURATION;
+ else
+ cmd.start = CPIA2_VP5_MCUVSATURATION;
+ break;
+ case CPIA2_CMD_SET_VP_GPIO_DATA:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_VP_GPIO_DATA:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VP_GPIO_DATA;
+ break;
+ case CPIA2_CMD_SET_VP_GPIO_DIRECTION:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_VP_GPIO_DIRECTION:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VP_GPIO_DIRECTION;
+ break;
+ case CPIA2_CMD_SET_VC_MP_GPIO_DATA:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_VC_MP_GPIO_DATA:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VC_MP_DATA;
+ break;
+ case CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VC_MP_DIR;
+ break;
+ case CPIA2_CMD_ENABLE_PACKET_CTRL:
+ cmd.req_mode =
+ CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM;
+ cmd.start = CPIA2_SYSTEM_INT_PACKET_CTRL;
+ cmd.reg_count = 1;
+ cmd.buffer.block_data[0] = param;
+ break;
+ case CPIA2_CMD_SET_FLICKER_MODES:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_FLICKER_MODES:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VP_FLICKER_MODES;
+ break;
+ case CPIA2_CMD_RESET_FIFO: /* clear fifo and enable stream block */
+ cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC;
+ cmd.reg_count = 2;
+ cmd.start = 0;
+ cmd.buffer.registers[0].index = CPIA2_VC_ST_CTRL;
+ cmd.buffer.registers[0].value = CPIA2_VC_ST_CTRL_SRC_VC |
+ CPIA2_VC_ST_CTRL_DST_USB | CPIA2_VC_ST_CTRL_EOF_DETECT;
+ cmd.buffer.registers[1].index = CPIA2_VC_ST_CTRL;
+ cmd.buffer.registers[1].value = CPIA2_VC_ST_CTRL_SRC_VC |
+ CPIA2_VC_ST_CTRL_DST_USB |
+ CPIA2_VC_ST_CTRL_EOF_DETECT |
+ CPIA2_VC_ST_CTRL_FIFO_ENABLE;
+ break;
+ case CPIA2_CMD_SET_HI_POWER:
+ cmd.req_mode =
+ CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_SYSTEM;
+ cmd.reg_count = 2;
+ cmd.buffer.registers[0].index =
+ CPIA2_SYSTEM_SYSTEM_CONTROL;
+ cmd.buffer.registers[1].index =
+ CPIA2_SYSTEM_SYSTEM_CONTROL;
+ cmd.buffer.registers[0].value = CPIA2_SYSTEM_CONTROL_CLEAR_ERR;
+ cmd.buffer.registers[1].value =
+ CPIA2_SYSTEM_CONTROL_HIGH_POWER;
+ break;
+ case CPIA2_CMD_SET_LOW_POWER:
+ cmd.req_mode =
+ CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_SYSTEM_SYSTEM_CONTROL;
+ cmd.buffer.block_data[0] = 0;
+ break;
+ case CPIA2_CMD_CLEAR_V2W_ERR:
+ cmd.req_mode =
+ CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_SYSTEM_SYSTEM_CONTROL;
+ cmd.buffer.block_data[0] = CPIA2_SYSTEM_CONTROL_CLEAR_ERR;
+ break;
+ case CPIA2_CMD_SET_USER_MODE: /* Then fall through */
+ cmd.buffer.block_data[0] = param;
+ case CPIA2_CMD_GET_USER_MODE:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ if (device == DEVICE_STV_672)
+ cmd.start = CPIA2_VP4_USER_MODE;
+ else
+ cmd.start = CPIA2_VP5_USER_MODE;
+ break;
+ case CPIA2_CMD_FRAMERATE_REQ:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ if (device == DEVICE_STV_672)
+ cmd.start = CPIA2_VP4_FRAMERATE_REQUEST;
+ else
+ cmd.start = CPIA2_VP5_FRAMERATE_REQUEST;
+ cmd.buffer.block_data[0] = param;
+ break;
+ case CPIA2_CMD_SET_WAKEUP:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_WAKEUP:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VC_WAKEUP;
+ break;
+ case CPIA2_CMD_SET_PW_CONTROL:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_PW_CONTROL:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VC_PW_CTRL;
+ break;
+ case CPIA2_CMD_GET_VP_SYSTEM_STATE:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VP_SYSTEMSTATE;
+ break;
+ case CPIA2_CMD_SET_SYSTEM_CTRL:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_SYSTEM_CTRL:
+ cmd.req_mode =
+ CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_SYSTEM_SYSTEM_CONTROL;
+ break;
+ case CPIA2_CMD_SET_VP_SYSTEM_CTRL:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_VP_SYSTEM_CTRL:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VP_SYSTEMCTRL;
+ break;
+ case CPIA2_CMD_SET_VP_EXP_MODES:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_VP_EXP_MODES:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VP_EXPOSURE_MODES;
+ break;
+ case CPIA2_CMD_SET_DEVICE_CONFIG:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_DEVICE_CONFIG:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VP_DEVICE_CONFIG;
+ break;
+ case CPIA2_CMD_SET_SERIAL_ADDR:
+ cmd.buffer.block_data[0] = param;
+ cmd.req_mode =
+ CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_SYSTEM_VP_SERIAL_ADDR;
+ break;
+ case CPIA2_CMD_SET_SENSOR_CR1:
+ cmd.buffer.block_data[0] = param;
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_SENSOR_CR1;
+ break;
+ case CPIA2_CMD_SET_VC_CONTROL:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_VC_CONTROL:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VC_VC_CTRL;
+ break;
+ case CPIA2_CMD_SET_TARGET_KB:
+ cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC;
+ cmd.reg_count = 1;
+ cmd.buffer.registers[0].index = CPIA2_VC_VC_TARGET_KB;
+ cmd.buffer.registers[0].value = param;
+ break;
+ case CPIA2_CMD_SET_DEF_JPEG_OPT:
+ cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC;
+ cmd.reg_count = 4;
+ cmd.buffer.registers[0].index = CPIA2_VC_VC_JPEG_OPT;
+ cmd.buffer.registers[0].value =
+ CPIA2_VC_VC_JPEG_OPT_DOUBLE_SQUEEZE;
+ cmd.buffer.registers[1].index = CPIA2_VC_VC_USER_SQUEEZE;
+ cmd.buffer.registers[1].value = 20;
+ cmd.buffer.registers[2].index = CPIA2_VC_VC_CREEP_PERIOD;
+ cmd.buffer.registers[2].value = 2;
+ cmd.buffer.registers[3].index = CPIA2_VC_VC_JPEG_OPT;
+ cmd.buffer.registers[3].value = CPIA2_VC_VC_JPEG_OPT_DEFAULT;
+ break;
+ case CPIA2_CMD_REHASH_VP4:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VP_REHASH_VALUES;
+ cmd.buffer.block_data[0] = param;
+ break;
+ case CPIA2_CMD_SET_USER_EFFECTS: /* Note: Be careful with this as
+ this register can also affect
+ flicker modes */
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_USER_EFFECTS:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ if (device == DEVICE_STV_672)
+ cmd.start = CPIA2_VP4_USER_EFFECTS;
+ else
+ cmd.start = CPIA2_VP5_USER_EFFECTS;
+ break;
+ default:
+ LOG("DoCommand received invalid command\n");
+ return -EINVAL;
+ }
+
+ retval = cpia2_send_command(cam, &cmd);
+ if (retval) {
+ return retval;
+ }
+
+ /***
+ * Now copy any results from a read into the appropriate param struct.
+ ***/
+ switch (command) {
+ case CPIA2_CMD_GET_VERSION:
+ cam->params.version.firmware_revision_hi =
+ cmd.buffer.block_data[0];
+ cam->params.version.firmware_revision_lo =
+ cmd.buffer.block_data[1];
+ break;
+ case CPIA2_CMD_GET_PNP_ID:
+ cam->params.pnp_id.vendor = (cmd.buffer.block_data[0] << 8) |
+ cmd.buffer.block_data[1];
+ cam->params.pnp_id.product = (cmd.buffer.block_data[2] << 8) |
+ cmd.buffer.block_data[3];
+ cam->params.pnp_id.device_revision =
+ (cmd.buffer.block_data[4] << 8) |
+ cmd.buffer.block_data[5];
+ if (cam->params.pnp_id.vendor == 0x553) {
+ if (cam->params.pnp_id.product == 0x100) {
+ cam->params.pnp_id.device_type = DEVICE_STV_672;
+ } else if (cam->params.pnp_id.product == 0x140 ||
+ cam->params.pnp_id.product == 0x151) {
+ cam->params.pnp_id.device_type = DEVICE_STV_676;
+ }
+ }
+ break;
+ case CPIA2_CMD_GET_ASIC_TYPE:
+ cam->params.version.asic_id = cmd.buffer.block_data[0];
+ cam->params.version.asic_rev = cmd.buffer.block_data[1];
+ break;
+ case CPIA2_CMD_GET_SENSOR:
+ cam->params.version.sensor_flags = cmd.buffer.block_data[0];
+ cam->params.version.sensor_rev = cmd.buffer.block_data[1];
+ break;
+ case CPIA2_CMD_GET_VP_DEVICE:
+ cam->params.version.vp_device_hi = cmd.buffer.block_data[0];
+ cam->params.version.vp_device_lo = cmd.buffer.block_data[1];
+ break;
+ case CPIA2_CMD_GET_VP_BRIGHTNESS:
+ cam->params.color_params.brightness = cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_CONTRAST:
+ cam->params.color_params.contrast = cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_VP_SATURATION:
+ cam->params.color_params.saturation = cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_VP_GPIO_DATA:
+ cam->params.vp_params.gpio_data = cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_VP_GPIO_DIRECTION:
+ cam->params.vp_params.gpio_direction = cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION:
+ cam->params.vc_params.vc_mp_direction =cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_VC_MP_GPIO_DATA:
+ cam->params.vc_params.vc_mp_data = cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_FLICKER_MODES:
+ cam->params.flicker_control.cam_register =
+ cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_WAKEUP:
+ cam->params.vc_params.wakeup = cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_PW_CONTROL:
+ cam->params.vc_params.pw_control = cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_SYSTEM_CTRL:
+ cam->params.camera_state.system_ctrl = cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_VP_SYSTEM_STATE:
+ cam->params.vp_params.system_state = cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_VP_SYSTEM_CTRL:
+ cam->params.vp_params.system_ctrl = cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_VP_EXP_MODES:
+ cam->params.vp_params.exposure_modes = cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_DEVICE_CONFIG:
+ cam->params.vp_params.device_config = cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_VC_CONTROL:
+ cam->params.vc_params.vc_control = cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_USER_MODE:
+ cam->params.vp_params.video_mode = cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_USER_EFFECTS:
+ cam->params.vp_params.user_effects = cmd.buffer.block_data[0];
+ break;
+ default:
+ break;
+ }
+ return retval;
+}
+
+/******************************************************************************
+ *
+ * cpia2_send_command
+ *
+ *****************************************************************************/
+
+#define DIR(cmd) ((cmd->direction == TRANSFER_WRITE) ? "Write" : "Read")
+#define BINDEX(cmd) (cmd->req_mode & 0x03)
+
+int cpia2_send_command(struct camera_data *cam, struct cpia2_command *cmd)
+{
+ u8 count;
+ u8 start;
+ u8 *buffer;
+ int retval;
+
+ switch (cmd->req_mode & 0x0c) {
+ case CAMERAACCESS_TYPE_RANDOM:
+ count = cmd->reg_count * sizeof(struct cpia2_register);
+ start = 0;
+ buffer = (u8 *) & cmd->buffer;
+ if (debugs_on & DEBUG_REG)
+ DBG("%s Random: Register block %s\n", DIR(cmd),
+ block_name[BINDEX(cmd)]);
+ break;
+ case CAMERAACCESS_TYPE_BLOCK:
+ count = cmd->reg_count;
+ start = cmd->start;
+ buffer = cmd->buffer.block_data;
+ if (debugs_on & DEBUG_REG)
+ DBG("%s Block: Register block %s\n", DIR(cmd),
+ block_name[BINDEX(cmd)]);
+ break;
+ case CAMERAACCESS_TYPE_MASK:
+ count = cmd->reg_count * sizeof(struct cpia2_reg_mask);
+ start = 0;
+ buffer = (u8 *) & cmd->buffer;
+ if (debugs_on & DEBUG_REG)
+ DBG("%s Mask: Register block %s\n", DIR(cmd),
+ block_name[BINDEX(cmd)]);
+ break;
+ case CAMERAACCESS_TYPE_REPEAT: /* For patch blocks only */
+ count = cmd->reg_count;
+ start = cmd->start;
+ buffer = cmd->buffer.block_data;
+ if (debugs_on & DEBUG_REG)
+ DBG("%s Repeat: Register block %s\n", DIR(cmd),
+ block_name[BINDEX(cmd)]);
+ break;
+ default:
+ LOG("%s: invalid request mode\n",__func__);
+ return -EINVAL;
+ }
+
+ retval = cpia2_usb_transfer_cmd(cam,
+ buffer,
+ cmd->req_mode,
+ start, count, cmd->direction);
+#ifdef _CPIA2_DEBUG_
+ if (debugs_on & DEBUG_REG) {
+ int i;
+ for (i = 0; i < cmd->reg_count; i++) {
+ if((cmd->req_mode & 0x0c) == CAMERAACCESS_TYPE_BLOCK)
+ KINFO("%s Block: [0x%02X] = 0x%02X\n",
+ DIR(cmd), start + i, buffer[i]);
+ if((cmd->req_mode & 0x0c) == CAMERAACCESS_TYPE_RANDOM)
+ KINFO("%s Random: [0x%02X] = 0x%02X\n",
+ DIR(cmd), cmd->buffer.registers[i].index,
+ cmd->buffer.registers[i].value);
+ }
+ }
+#endif
+
+ return retval;
+};
+
+/*************
+ * Functions to implement camera functionality
+ *************/
+/******************************************************************************
+ *
+ * cpia2_get_version_info
+ *
+ *****************************************************************************/
+static void cpia2_get_version_info(struct camera_data *cam)
+{
+ cpia2_do_command(cam, CPIA2_CMD_GET_VERSION, TRANSFER_READ, 0);
+ cpia2_do_command(cam, CPIA2_CMD_GET_PNP_ID, TRANSFER_READ, 0);
+ cpia2_do_command(cam, CPIA2_CMD_GET_ASIC_TYPE, TRANSFER_READ, 0);
+ cpia2_do_command(cam, CPIA2_CMD_GET_SENSOR, TRANSFER_READ, 0);
+ cpia2_do_command(cam, CPIA2_CMD_GET_VP_DEVICE, TRANSFER_READ, 0);
+}
+
+/******************************************************************************
+ *
+ * cpia2_reset_camera
+ *
+ * Called at least during the open process, sets up initial params.
+ *****************************************************************************/
+int cpia2_reset_camera(struct camera_data *cam)
+{
+ u8 tmp_reg;
+ int retval = 0;
+ int i;
+ struct cpia2_command cmd;
+
+ /***
+ * VC setup
+ ***/
+ retval = configure_sensor(cam,
+ cam->params.roi.width,
+ cam->params.roi.height);
+ if (retval < 0) {
+ ERR("Couldn't configure sensor, error=%d\n", retval);
+ return retval;
+ }
+
+ /* Clear FIFO and route/enable stream block */
+ cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC;
+ cmd.direction = TRANSFER_WRITE;
+ cmd.reg_count = 2;
+ cmd.buffer.registers[0].index = CPIA2_VC_ST_CTRL;
+ cmd.buffer.registers[0].value = CPIA2_VC_ST_CTRL_SRC_VC |
+ CPIA2_VC_ST_CTRL_DST_USB | CPIA2_VC_ST_CTRL_EOF_DETECT;
+ cmd.buffer.registers[1].index = CPIA2_VC_ST_CTRL;
+ cmd.buffer.registers[1].value = CPIA2_VC_ST_CTRL_SRC_VC |
+ CPIA2_VC_ST_CTRL_DST_USB |
+ CPIA2_VC_ST_CTRL_EOF_DETECT | CPIA2_VC_ST_CTRL_FIFO_ENABLE;
+
+ cpia2_send_command(cam, &cmd);
+
+ cpia2_set_high_power(cam);
+
+ if (cam->params.pnp_id.device_type == DEVICE_STV_672) {
+ /* Enable button notification */
+ cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_SYSTEM;
+ cmd.buffer.registers[0].index = CPIA2_SYSTEM_INT_PACKET_CTRL;
+ cmd.buffer.registers[0].value =
+ CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_SW_XX;
+ cmd.reg_count = 1;
+ cpia2_send_command(cam, &cmd);
+ }
+
+ schedule_timeout_interruptible(msecs_to_jiffies(100));
+
+ if (cam->params.pnp_id.device_type == DEVICE_STV_672)
+ retval = apply_vp_patch(cam);
+
+ /* wait for vp to go to sleep */
+ schedule_timeout_interruptible(msecs_to_jiffies(100));
+
+ /***
+ * If this is a 676, apply VP5 fixes before we start streaming
+ ***/
+ if (cam->params.pnp_id.device_type == DEVICE_STV_676) {
+ cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VP;
+
+ /* The following writes improve the picture */
+ cmd.buffer.registers[0].index = CPIA2_VP5_MYBLACK_LEVEL;
+ cmd.buffer.registers[0].value = 0; /* reduce from the default
+ * rec 601 pedestal of 16 */
+ cmd.buffer.registers[1].index = CPIA2_VP5_MCYRANGE;
+ cmd.buffer.registers[1].value = 0x92; /* increase from 100% to
+ * (256/256 - 31) to fill
+ * available range */
+ cmd.buffer.registers[2].index = CPIA2_VP5_MYCEILING;
+ cmd.buffer.registers[2].value = 0xFF; /* Increase from the
+ * default rec 601 ceiling
+ * of 240 */
+ cmd.buffer.registers[3].index = CPIA2_VP5_MCUVSATURATION;
+ cmd.buffer.registers[3].value = 0xFF; /* Increase from the rec
+ * 601 100% level (128)
+ * to 145-192 */
+ cmd.buffer.registers[4].index = CPIA2_VP5_ANTIFLKRSETUP;
+ cmd.buffer.registers[4].value = 0x80; /* Inhibit the
+ * anti-flicker */
+
+ /* The following 4 writes are a fix to allow QVGA to work at 30 fps */
+ cmd.buffer.registers[5].index = CPIA2_VP_RAM_ADDR_H;
+ cmd.buffer.registers[5].value = 0x01;
+ cmd.buffer.registers[6].index = CPIA2_VP_RAM_ADDR_L;
+ cmd.buffer.registers[6].value = 0xE3;
+ cmd.buffer.registers[7].index = CPIA2_VP_RAM_DATA;
+ cmd.buffer.registers[7].value = 0x02;
+ cmd.buffer.registers[8].index = CPIA2_VP_RAM_DATA;
+ cmd.buffer.registers[8].value = 0xFC;
+
+ cmd.direction = TRANSFER_WRITE;
+ cmd.reg_count = 9;
+
+ cpia2_send_command(cam, &cmd);
+ }
+
+ /* Activate all settings and start the data stream */
+ /* Set user mode */
+ set_default_user_mode(cam);
+
+ /* Give VP time to wake up */
+ schedule_timeout_interruptible(msecs_to_jiffies(100));
+
+ set_all_properties(cam);
+
+ cpia2_do_command(cam, CPIA2_CMD_GET_USER_MODE, TRANSFER_READ, 0);
+ DBG("After SetAllProperties(cam), user mode is 0x%0X\n",
+ cam->params.vp_params.video_mode);
+
+ /***
+ * Set audio regulator off. This and the code to set the compresison
+ * state are too complex to form a CPIA2_CMD_, and seem to be somewhat
+ * intertwined. This stuff came straight from the windows driver.
+ ***/
+ /* Turn AutoExposure off in VP and enable the serial bridge to the sensor */
+ cpia2_do_command(cam, CPIA2_CMD_GET_VP_SYSTEM_CTRL, TRANSFER_READ, 0);
+ tmp_reg = cam->params.vp_params.system_ctrl;
+ cmd.buffer.registers[0].value = tmp_reg &
+ (tmp_reg & (CPIA2_VP_SYSTEMCTRL_HK_CONTROL ^ 0xFF));
+
+ cpia2_do_command(cam, CPIA2_CMD_GET_DEVICE_CONFIG, TRANSFER_READ, 0);
+ cmd.buffer.registers[1].value = cam->params.vp_params.device_config |
+ CPIA2_VP_DEVICE_CONFIG_SERIAL_BRIDGE;
+ cmd.buffer.registers[0].index = CPIA2_VP_SYSTEMCTRL;
+ cmd.buffer.registers[1].index = CPIA2_VP_DEVICE_CONFIG;
+ cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VP;
+ cmd.reg_count = 2;
+ cmd.direction = TRANSFER_WRITE;
+ cmd.start = 0;
+ cpia2_send_command(cam, &cmd);
+
+ /* Set the correct I2C address in the CPiA-2 system register */
+ cpia2_do_command(cam,
+ CPIA2_CMD_SET_SERIAL_ADDR,
+ TRANSFER_WRITE,
+ CPIA2_SYSTEM_VP_SERIAL_ADDR_SENSOR);
+
+ /* Now have sensor access - set bit to turn the audio regulator off */
+ cpia2_do_command(cam,
+ CPIA2_CMD_SET_SENSOR_CR1,
+ TRANSFER_WRITE, CPIA2_SENSOR_CR1_DOWN_AUDIO_REGULATOR);
+
+ /* Set the correct I2C address in the CPiA-2 system register */
+ if (cam->params.pnp_id.device_type == DEVICE_STV_672)
+ cpia2_do_command(cam,
+ CPIA2_CMD_SET_SERIAL_ADDR,
+ TRANSFER_WRITE,
+ CPIA2_SYSTEM_VP_SERIAL_ADDR_VP); // 0x88
+ else
+ cpia2_do_command(cam,
+ CPIA2_CMD_SET_SERIAL_ADDR,
+ TRANSFER_WRITE,
+ CPIA2_SYSTEM_VP_SERIAL_ADDR_676_VP); // 0x8a
+
+ /* increase signal drive strength */
+ if (cam->params.pnp_id.device_type == DEVICE_STV_676)
+ cpia2_do_command(cam,
+ CPIA2_CMD_SET_VP_EXP_MODES,
+ TRANSFER_WRITE,
+ CPIA2_VP_EXPOSURE_MODES_COMPILE_EXP);
+
+ /* Start autoexposure */
+ cpia2_do_command(cam, CPIA2_CMD_GET_DEVICE_CONFIG, TRANSFER_READ, 0);
+ cmd.buffer.registers[0].value = cam->params.vp_params.device_config &
+ (CPIA2_VP_DEVICE_CONFIG_SERIAL_BRIDGE ^ 0xFF);
+
+ cpia2_do_command(cam, CPIA2_CMD_GET_VP_SYSTEM_CTRL, TRANSFER_READ, 0);
+ cmd.buffer.registers[1].value =
+ cam->params.vp_params.system_ctrl | CPIA2_VP_SYSTEMCTRL_HK_CONTROL;
+
+ cmd.buffer.registers[0].index = CPIA2_VP_DEVICE_CONFIG;
+ cmd.buffer.registers[1].index = CPIA2_VP_SYSTEMCTRL;
+ cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VP;
+ cmd.reg_count = 2;
+ cmd.direction = TRANSFER_WRITE;
+
+ cpia2_send_command(cam, &cmd);
+
+ /* Set compression state */
+ cpia2_do_command(cam, CPIA2_CMD_GET_VC_CONTROL, TRANSFER_READ, 0);
+ if (cam->params.compression.inhibit_htables) {
+ tmp_reg = cam->params.vc_params.vc_control |
+ CPIA2_VC_VC_CTRL_INHIBIT_H_TABLES;
+ } else {
+ tmp_reg = cam->params.vc_params.vc_control &
+ ~CPIA2_VC_VC_CTRL_INHIBIT_H_TABLES;
+ }
+ cpia2_do_command(cam, CPIA2_CMD_SET_VC_CONTROL, TRANSFER_WRITE,tmp_reg);
+
+ /* Set target size (kb) on vc */
+ cpia2_do_command(cam, CPIA2_CMD_SET_TARGET_KB,
+ TRANSFER_WRITE, cam->params.vc_params.target_kb);
+
+ /* Wiggle VC Reset */
+ /***
+ * First read and wait a bit.
+ ***/
+ for (i = 0; i < 50; i++) {
+ cpia2_do_command(cam, CPIA2_CMD_GET_PW_CONTROL,
+ TRANSFER_READ, 0);
+ }
+
+ tmp_reg = cam->params.vc_params.pw_control;
+ tmp_reg &= ~CPIA2_VC_PW_CTRL_VC_RESET_N;
+
+ cpia2_do_command(cam, CPIA2_CMD_SET_PW_CONTROL, TRANSFER_WRITE,tmp_reg);
+
+ tmp_reg |= CPIA2_VC_PW_CTRL_VC_RESET_N;
+ cpia2_do_command(cam, CPIA2_CMD_SET_PW_CONTROL, TRANSFER_WRITE,tmp_reg);
+
+ cpia2_do_command(cam, CPIA2_CMD_SET_DEF_JPEG_OPT, TRANSFER_WRITE, 0);
+
+ cpia2_do_command(cam, CPIA2_CMD_GET_USER_MODE, TRANSFER_READ, 0);
+ DBG("After VC RESET, user mode is 0x%0X\n",
+ cam->params.vp_params.video_mode);
+
+ return retval;
+}
+
+/******************************************************************************
+ *
+ * cpia2_set_high_power
+ *
+ *****************************************************************************/
+static int cpia2_set_high_power(struct camera_data *cam)
+{
+ int i;
+ for (i = 0; i <= 50; i++) {
+ /* Read system status */
+ cpia2_do_command(cam,CPIA2_CMD_GET_SYSTEM_CTRL,TRANSFER_READ,0);
+
+ /* If there is an error, clear it */
+ if(cam->params.camera_state.system_ctrl &
+ CPIA2_SYSTEM_CONTROL_V2W_ERR)
+ cpia2_do_command(cam, CPIA2_CMD_CLEAR_V2W_ERR,
+ TRANSFER_WRITE, 0);
+
+ /* Try to set high power mode */
+ cpia2_do_command(cam, CPIA2_CMD_SET_SYSTEM_CTRL,
+ TRANSFER_WRITE, 1);
+
+ /* Try to read something in VP to check if everything is awake */
+ cpia2_do_command(cam, CPIA2_CMD_GET_VP_SYSTEM_STATE,
+ TRANSFER_READ, 0);
+ if (cam->params.vp_params.system_state &
+ CPIA2_VP_SYSTEMSTATE_HK_ALIVE) {
+ break;
+ } else if (i == 50) {
+ cam->params.camera_state.power_mode = LO_POWER_MODE;
+ ERR("Camera did not wake up\n");
+ return -EIO;
+ }
+ }
+
+ DBG("System now in high power state\n");
+ cam->params.camera_state.power_mode = HI_POWER_MODE;
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * cpia2_set_low_power
+ *
+ *****************************************************************************/
+int cpia2_set_low_power(struct camera_data *cam)
+{
+ cam->params.camera_state.power_mode = LO_POWER_MODE;
+ cpia2_do_command(cam, CPIA2_CMD_SET_SYSTEM_CTRL, TRANSFER_WRITE, 0);
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * apply_vp_patch
+ *
+ *****************************************************************************/
+static int cpia2_send_onebyte_command(struct camera_data *cam,
+ struct cpia2_command *cmd,
+ u8 start, u8 datum)
+{
+ cmd->buffer.block_data[0] = datum;
+ cmd->start = start;
+ cmd->reg_count = 1;
+ return cpia2_send_command(cam, cmd);
+}
+
+static int apply_vp_patch(struct camera_data *cam)
+{
+ const struct firmware *fw;
+ const char fw_name[] = "cpia2/stv0672_vp4.bin";
+ int i, ret;
+ struct cpia2_command cmd;
+
+ ret = request_firmware(&fw, fw_name, &cam->dev->dev);
+ if (ret) {
+ printk(KERN_ERR "cpia2: failed to load VP patch \"%s\"\n",
+ fw_name);
+ return ret;
+ }
+
+ cmd.req_mode = CAMERAACCESS_TYPE_REPEAT | CAMERAACCESS_VP;
+ cmd.direction = TRANSFER_WRITE;
+
+ /* First send the start address... */
+ cpia2_send_onebyte_command(cam, &cmd, 0x0A, fw->data[0]); /* hi */
+ cpia2_send_onebyte_command(cam, &cmd, 0x0B, fw->data[1]); /* lo */
+
+ /* ... followed by the data payload */
+ for (i = 2; i < fw->size; i += 64) {
+ cmd.start = 0x0C; /* Data */
+ cmd.reg_count = min_t(int, 64, fw->size - i);
+ memcpy(cmd.buffer.block_data, &fw->data[i], cmd.reg_count);
+ cpia2_send_command(cam, &cmd);
+ }
+
+ /* Next send the start address... */
+ cpia2_send_onebyte_command(cam, &cmd, 0x0A, fw->data[0]); /* hi */
+ cpia2_send_onebyte_command(cam, &cmd, 0x0B, fw->data[1]); /* lo */
+
+ /* ... followed by the 'goto' command */
+ cpia2_send_onebyte_command(cam, &cmd, 0x0D, 1);
+
+ release_firmware(fw);
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * set_default_user_mode
+ *
+ *****************************************************************************/
+static int set_default_user_mode(struct camera_data *cam)
+{
+ unsigned char user_mode;
+ unsigned char frame_rate;
+ int width = cam->params.roi.width;
+ int height = cam->params.roi.height;
+
+ switch (cam->params.version.sensor_flags) {
+ case CPIA2_VP_SENSOR_FLAGS_404:
+ case CPIA2_VP_SENSOR_FLAGS_407:
+ case CPIA2_VP_SENSOR_FLAGS_409:
+ case CPIA2_VP_SENSOR_FLAGS_410:
+ if ((width > STV_IMAGE_QCIF_COLS)
+ || (height > STV_IMAGE_QCIF_ROWS)) {
+ user_mode = CPIA2_VP_USER_MODE_CIF;
+ } else {
+ user_mode = CPIA2_VP_USER_MODE_QCIFDS;
+ }
+ frame_rate = CPIA2_VP_FRAMERATE_30;
+ break;
+ case CPIA2_VP_SENSOR_FLAGS_500:
+ if ((width > STV_IMAGE_CIF_COLS)
+ || (height > STV_IMAGE_CIF_ROWS)) {
+ user_mode = CPIA2_VP_USER_MODE_VGA;
+ } else {
+ user_mode = CPIA2_VP_USER_MODE_QVGADS;
+ }
+ if (cam->params.pnp_id.device_type == DEVICE_STV_672)
+ frame_rate = CPIA2_VP_FRAMERATE_15;
+ else
+ frame_rate = CPIA2_VP_FRAMERATE_30;
+ break;
+ default:
+ LOG("%s: Invalid sensor flag value 0x%0X\n",__func__,
+ cam->params.version.sensor_flags);
+ return -EINVAL;
+ }
+
+ DBG("Sensor flag = 0x%0x, user mode = 0x%0x, frame rate = 0x%X\n",
+ cam->params.version.sensor_flags, user_mode, frame_rate);
+ cpia2_do_command(cam, CPIA2_CMD_SET_USER_MODE, TRANSFER_WRITE,
+ user_mode);
+ if(cam->params.vp_params.frame_rate > 0 &&
+ frame_rate > cam->params.vp_params.frame_rate)
+ frame_rate = cam->params.vp_params.frame_rate;
+
+ cpia2_set_fps(cam, frame_rate);
+
+// if (cam->params.pnp_id.device_type == DEVICE_STV_676)
+// cpia2_do_command(cam,
+// CPIA2_CMD_SET_VP_SYSTEM_CTRL,
+// TRANSFER_WRITE,
+// CPIA2_VP_SYSTEMCTRL_HK_CONTROL |
+// CPIA2_VP_SYSTEMCTRL_POWER_CONTROL);
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * cpia2_match_video_size
+ *
+ * return the best match, where 'best' is as always
+ * the largest that is not bigger than what is requested.
+ *****************************************************************************/
+int cpia2_match_video_size(int width, int height)
+{
+ if (width >= STV_IMAGE_VGA_COLS && height >= STV_IMAGE_VGA_ROWS)
+ return VIDEOSIZE_VGA;
+
+ if (width >= STV_IMAGE_CIF_COLS && height >= STV_IMAGE_CIF_ROWS)
+ return VIDEOSIZE_CIF;
+
+ if (width >= STV_IMAGE_QVGA_COLS && height >= STV_IMAGE_QVGA_ROWS)
+ return VIDEOSIZE_QVGA;
+
+ if (width >= 288 && height >= 216)
+ return VIDEOSIZE_288_216;
+
+ if (width >= 256 && height >= 192)
+ return VIDEOSIZE_256_192;
+
+ if (width >= 224 && height >= 168)
+ return VIDEOSIZE_224_168;
+
+ if (width >= 192 && height >= 144)
+ return VIDEOSIZE_192_144;
+
+ if (width >= STV_IMAGE_QCIF_COLS && height >= STV_IMAGE_QCIF_ROWS)
+ return VIDEOSIZE_QCIF;
+
+ return -1;
+}
+
+/******************************************************************************
+ *
+ * SetVideoSize
+ *
+ *****************************************************************************/
+static int set_vw_size(struct camera_data *cam, int size)
+{
+ int retval = 0;
+
+ cam->params.vp_params.video_size = size;
+
+ switch (size) {
+ case VIDEOSIZE_VGA:
+ DBG("Setting size to VGA\n");
+ cam->params.roi.width = STV_IMAGE_VGA_COLS;
+ cam->params.roi.height = STV_IMAGE_VGA_ROWS;
+ cam->width = STV_IMAGE_VGA_COLS;
+ cam->height = STV_IMAGE_VGA_ROWS;
+ break;
+ case VIDEOSIZE_CIF:
+ DBG("Setting size to CIF\n");
+ cam->params.roi.width = STV_IMAGE_CIF_COLS;
+ cam->params.roi.height = STV_IMAGE_CIF_ROWS;
+ cam->width = STV_IMAGE_CIF_COLS;
+ cam->height = STV_IMAGE_CIF_ROWS;
+ break;
+ case VIDEOSIZE_QVGA:
+ DBG("Setting size to QVGA\n");
+ cam->params.roi.width = STV_IMAGE_QVGA_COLS;
+ cam->params.roi.height = STV_IMAGE_QVGA_ROWS;
+ cam->width = STV_IMAGE_QVGA_COLS;
+ cam->height = STV_IMAGE_QVGA_ROWS;
+ break;
+ case VIDEOSIZE_288_216:
+ cam->params.roi.width = 288;
+ cam->params.roi.height = 216;
+ cam->width = 288;
+ cam->height = 216;
+ break;
+ case VIDEOSIZE_256_192:
+ cam->width = 256;
+ cam->height = 192;
+ cam->params.roi.width = 256;
+ cam->params.roi.height = 192;
+ break;
+ case VIDEOSIZE_224_168:
+ cam->width = 224;
+ cam->height = 168;
+ cam->params.roi.width = 224;
+ cam->params.roi.height = 168;
+ break;
+ case VIDEOSIZE_192_144:
+ cam->width = 192;
+ cam->height = 144;
+ cam->params.roi.width = 192;
+ cam->params.roi.height = 144;
+ break;
+ case VIDEOSIZE_QCIF:
+ DBG("Setting size to QCIF\n");
+ cam->params.roi.width = STV_IMAGE_QCIF_COLS;
+ cam->params.roi.height = STV_IMAGE_QCIF_ROWS;
+ cam->width = STV_IMAGE_QCIF_COLS;
+ cam->height = STV_IMAGE_QCIF_ROWS;
+ break;
+ default:
+ retval = -EINVAL;
+ }
+ return retval;
+}
+
+/******************************************************************************
+ *
+ * configure_sensor
+ *
+ *****************************************************************************/
+static int configure_sensor(struct camera_data *cam,
+ int req_width, int req_height)
+{
+ int retval;
+
+ switch (cam->params.version.sensor_flags) {
+ case CPIA2_VP_SENSOR_FLAGS_404:
+ case CPIA2_VP_SENSOR_FLAGS_407:
+ case CPIA2_VP_SENSOR_FLAGS_409:
+ case CPIA2_VP_SENSOR_FLAGS_410:
+ retval = config_sensor_410(cam, req_width, req_height);
+ break;
+ case CPIA2_VP_SENSOR_FLAGS_500:
+ retval = config_sensor_500(cam, req_width, req_height);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return retval;
+}
+
+/******************************************************************************
+ *
+ * config_sensor_410
+ *
+ *****************************************************************************/
+static int config_sensor_410(struct camera_data *cam,
+ int req_width, int req_height)
+{
+ struct cpia2_command cmd;
+ int i = 0;
+ int image_size;
+ int image_type;
+ int width = req_width;
+ int height = req_height;
+
+ /***
+ * Make sure size doesn't exceed CIF.
+ ***/
+ if (width > STV_IMAGE_CIF_COLS)
+ width = STV_IMAGE_CIF_COLS;
+ if (height > STV_IMAGE_CIF_ROWS)
+ height = STV_IMAGE_CIF_ROWS;
+
+ image_size = cpia2_match_video_size(width, height);
+
+ DBG("Config 410: width = %d, height = %d\n", width, height);
+ DBG("Image size returned is %d\n", image_size);
+ if (image_size >= 0) {
+ set_vw_size(cam, image_size);
+ width = cam->params.roi.width;
+ height = cam->params.roi.height;
+
+ DBG("After set_vw_size(), width = %d, height = %d\n",
+ width, height);
+ if (width <= 176 && height <= 144) {
+ DBG("image type = VIDEOSIZE_QCIF\n");
+ image_type = VIDEOSIZE_QCIF;
+ }
+ else if (width <= 320 && height <= 240) {
+ DBG("image type = VIDEOSIZE_QVGA\n");
+ image_type = VIDEOSIZE_QVGA;
+ }
+ else {
+ DBG("image type = VIDEOSIZE_CIF\n");
+ image_type = VIDEOSIZE_CIF;
+ }
+ } else {
+ ERR("ConfigSensor410 failed\n");
+ return -EINVAL;
+ }
+
+ cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC;
+ cmd.direction = TRANSFER_WRITE;
+
+ /* VC Format */
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_FORMAT;
+ if (image_type == VIDEOSIZE_CIF) {
+ cmd.buffer.registers[i++].value =
+ (u8) (CPIA2_VC_VC_FORMAT_UFIRST |
+ CPIA2_VC_VC_FORMAT_SHORTLINE);
+ } else {
+ cmd.buffer.registers[i++].value =
+ (u8) CPIA2_VC_VC_FORMAT_UFIRST;
+ }
+
+ /* VC Clocks */
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_CLOCKS;
+ if (image_type == VIDEOSIZE_QCIF) {
+ if (cam->params.pnp_id.device_type == DEVICE_STV_672) {
+ cmd.buffer.registers[i++].value=
+ (u8)(CPIA2_VC_VC_672_CLOCKS_CIF_DIV_BY_3 |
+ CPIA2_VC_VC_672_CLOCKS_SCALING |
+ CPIA2_VC_VC_CLOCKS_LOGDIV2);
+ DBG("VC_Clocks (0xc4) should be B\n");
+ }
+ else {
+ cmd.buffer.registers[i++].value=
+ (u8)(CPIA2_VC_VC_676_CLOCKS_CIF_DIV_BY_3 |
+ CPIA2_VC_VC_CLOCKS_LOGDIV2);
+ }
+ } else {
+ if (cam->params.pnp_id.device_type == DEVICE_STV_672) {
+ cmd.buffer.registers[i++].value =
+ (u8) (CPIA2_VC_VC_672_CLOCKS_CIF_DIV_BY_3 |
+ CPIA2_VC_VC_CLOCKS_LOGDIV0);
+ }
+ else {
+ cmd.buffer.registers[i++].value =
+ (u8) (CPIA2_VC_VC_676_CLOCKS_CIF_DIV_BY_3 |
+ CPIA2_VC_VC_676_CLOCKS_SCALING |
+ CPIA2_VC_VC_CLOCKS_LOGDIV0);
+ }
+ }
+ DBG("VC_Clocks (0xc4) = 0x%0X\n", cmd.buffer.registers[i-1].value);
+
+ /* Input reqWidth from VC */
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_IHSIZE_LO;
+ if (image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value =
+ (u8) (STV_IMAGE_QCIF_COLS / 4);
+ else
+ cmd.buffer.registers[i++].value =
+ (u8) (STV_IMAGE_CIF_COLS / 4);
+
+ /* Timings */
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_HI;
+ if (image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value = (u8) 0;
+ else
+ cmd.buffer.registers[i++].value = (u8) 1;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_LO;
+ if (image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value = (u8) 208;
+ else
+ cmd.buffer.registers[i++].value = (u8) 160;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_HI;
+ if (image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value = (u8) 0;
+ else
+ cmd.buffer.registers[i++].value = (u8) 1;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_LO;
+ if (image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value = (u8) 160;
+ else
+ cmd.buffer.registers[i++].value = (u8) 64;
+
+ /* Output Image Size */
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_OHSIZE;
+ cmd.buffer.registers[i++].value = cam->params.roi.width / 4;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_OVSIZE;
+ cmd.buffer.registers[i++].value = cam->params.roi.height / 4;
+
+ /* Cropping */
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_HCROP;
+ if (image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value =
+ (u8) (((STV_IMAGE_QCIF_COLS / 4) - (width / 4)) / 2);
+ else
+ cmd.buffer.registers[i++].value =
+ (u8) (((STV_IMAGE_CIF_COLS / 4) - (width / 4)) / 2);
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_VCROP;
+ if (image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value =
+ (u8) (((STV_IMAGE_QCIF_ROWS / 4) - (height / 4)) / 2);
+ else
+ cmd.buffer.registers[i++].value =
+ (u8) (((STV_IMAGE_CIF_ROWS / 4) - (height / 4)) / 2);
+
+ /* Scaling registers (defaults) */
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_HPHASE;
+ cmd.buffer.registers[i++].value = (u8) 0;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_VPHASE;
+ cmd.buffer.registers[i++].value = (u8) 0;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_HISPAN;
+ cmd.buffer.registers[i++].value = (u8) 31;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_VISPAN;
+ cmd.buffer.registers[i++].value = (u8) 31;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_HICROP;
+ cmd.buffer.registers[i++].value = (u8) 0;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_VICROP;
+ cmd.buffer.registers[i++].value = (u8) 0;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_HFRACT;
+ cmd.buffer.registers[i++].value = (u8) 0x81; /* = 8/1 = 8 (HIBYTE/LOBYTE) */
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_VFRACT;
+ cmd.buffer.registers[i++].value = (u8) 0x81; /* = 8/1 = 8 (HIBYTE/LOBYTE) */
+
+ cmd.reg_count = i;
+
+ cpia2_send_command(cam, &cmd);
+
+ return i;
+}
+
+
+/******************************************************************************
+ *
+ * config_sensor_500(cam)
+ *
+ *****************************************************************************/
+static int config_sensor_500(struct camera_data *cam,
+ int req_width, int req_height)
+{
+ struct cpia2_command cmd;
+ int i = 0;
+ int image_size = VIDEOSIZE_CIF;
+ int image_type = VIDEOSIZE_VGA;
+ int width = req_width;
+ int height = req_height;
+ unsigned int device = cam->params.pnp_id.device_type;
+
+ image_size = cpia2_match_video_size(width, height);
+
+ if (width > STV_IMAGE_CIF_COLS || height > STV_IMAGE_CIF_ROWS)
+ image_type = VIDEOSIZE_VGA;
+ else if (width > STV_IMAGE_QVGA_COLS || height > STV_IMAGE_QVGA_ROWS)
+ image_type = VIDEOSIZE_CIF;
+ else if (width > STV_IMAGE_QCIF_COLS || height > STV_IMAGE_QCIF_ROWS)
+ image_type = VIDEOSIZE_QVGA;
+ else
+ image_type = VIDEOSIZE_QCIF;
+
+ if (image_size >= 0) {
+ set_vw_size(cam, image_size);
+ width = cam->params.roi.width;
+ height = cam->params.roi.height;
+ } else {
+ ERR("ConfigSensor500 failed\n");
+ return -EINVAL;
+ }
+
+ DBG("image_size = %d, width = %d, height = %d, type = %d\n",
+ image_size, width, height, image_type);
+
+ cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC;
+ cmd.direction = TRANSFER_WRITE;
+ i = 0;
+
+ /* VC Format */
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_FORMAT;
+ cmd.buffer.registers[i].value = (u8) CPIA2_VC_VC_FORMAT_UFIRST;
+ if (image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i].value |= (u8) CPIA2_VC_VC_FORMAT_DECIMATING;
+ i++;
+
+ /* VC Clocks */
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_CLOCKS;
+ if (device == DEVICE_STV_672) {
+ if (image_type == VIDEOSIZE_VGA)
+ cmd.buffer.registers[i].value =
+ (u8)CPIA2_VC_VC_CLOCKS_LOGDIV1;
+ else
+ cmd.buffer.registers[i].value =
+ (u8)(CPIA2_VC_VC_672_CLOCKS_SCALING |
+ CPIA2_VC_VC_CLOCKS_LOGDIV3);
+ } else {
+ if (image_type == VIDEOSIZE_VGA)
+ cmd.buffer.registers[i].value =
+ (u8)CPIA2_VC_VC_CLOCKS_LOGDIV0;
+ else
+ cmd.buffer.registers[i].value =
+ (u8)(CPIA2_VC_VC_676_CLOCKS_SCALING |
+ CPIA2_VC_VC_CLOCKS_LOGDIV2);
+ }
+ i++;
+
+ DBG("VC_CLOCKS = 0x%X\n", cmd.buffer.registers[i-1].value);
+
+ /* Input width from VP */
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_IHSIZE_LO;
+ if (image_type == VIDEOSIZE_VGA)
+ cmd.buffer.registers[i].value =
+ (u8) (STV_IMAGE_VGA_COLS / 4);
+ else
+ cmd.buffer.registers[i].value =
+ (u8) (STV_IMAGE_QVGA_COLS / 4);
+ i++;
+ DBG("Input width = %d\n", cmd.buffer.registers[i-1].value);
+
+ /* Timings */
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_HI;
+ if (image_type == VIDEOSIZE_VGA)
+ cmd.buffer.registers[i++].value = (u8) 2;
+ else
+ cmd.buffer.registers[i++].value = (u8) 1;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_LO;
+ if (image_type == VIDEOSIZE_VGA)
+ cmd.buffer.registers[i++].value = (u8) 250;
+ else if (image_type == VIDEOSIZE_QVGA)
+ cmd.buffer.registers[i++].value = (u8) 125;
+ else
+ cmd.buffer.registers[i++].value = (u8) 160;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_HI;
+ if (image_type == VIDEOSIZE_VGA)
+ cmd.buffer.registers[i++].value = (u8) 2;
+ else
+ cmd.buffer.registers[i++].value = (u8) 1;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_LO;
+ if (image_type == VIDEOSIZE_VGA)
+ cmd.buffer.registers[i++].value = (u8) 12;
+ else if (image_type == VIDEOSIZE_QVGA)
+ cmd.buffer.registers[i++].value = (u8) 64;
+ else
+ cmd.buffer.registers[i++].value = (u8) 6;
+
+ /* Output Image Size */
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_OHSIZE;
+ if (image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value = STV_IMAGE_CIF_COLS / 4;
+ else
+ cmd.buffer.registers[i++].value = width / 4;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_OVSIZE;
+ if (image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value = STV_IMAGE_CIF_ROWS / 4;
+ else
+ cmd.buffer.registers[i++].value = height / 4;
+
+ /* Cropping */
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_HCROP;
+ if (image_type == VIDEOSIZE_VGA)
+ cmd.buffer.registers[i++].value =
+ (u8) (((STV_IMAGE_VGA_COLS / 4) - (width / 4)) / 2);
+ else if (image_type == VIDEOSIZE_QVGA)
+ cmd.buffer.registers[i++].value =
+ (u8) (((STV_IMAGE_QVGA_COLS / 4) - (width / 4)) / 2);
+ else if (image_type == VIDEOSIZE_CIF)
+ cmd.buffer.registers[i++].value =
+ (u8) (((STV_IMAGE_CIF_COLS / 4) - (width / 4)) / 2);
+ else /*if (image_type == VIDEOSIZE_QCIF)*/
+ cmd.buffer.registers[i++].value =
+ (u8) (((STV_IMAGE_QCIF_COLS / 4) - (width / 4)) / 2);
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_VCROP;
+ if (image_type == VIDEOSIZE_VGA)
+ cmd.buffer.registers[i++].value =
+ (u8) (((STV_IMAGE_VGA_ROWS / 4) - (height / 4)) / 2);
+ else if (image_type == VIDEOSIZE_QVGA)
+ cmd.buffer.registers[i++].value =
+ (u8) (((STV_IMAGE_QVGA_ROWS / 4) - (height / 4)) / 2);
+ else if (image_type == VIDEOSIZE_CIF)
+ cmd.buffer.registers[i++].value =
+ (u8) (((STV_IMAGE_CIF_ROWS / 4) - (height / 4)) / 2);
+ else /*if (image_type == VIDEOSIZE_QCIF)*/
+ cmd.buffer.registers[i++].value =
+ (u8) (((STV_IMAGE_QCIF_ROWS / 4) - (height / 4)) / 2);
+
+ /* Scaling registers (defaults) */
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_HPHASE;
+ if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value = (u8) 36;
+ else
+ cmd.buffer.registers[i++].value = (u8) 0;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_VPHASE;
+ if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value = (u8) 32;
+ else
+ cmd.buffer.registers[i++].value = (u8) 0;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_HISPAN;
+ if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value = (u8) 26;
+ else
+ cmd.buffer.registers[i++].value = (u8) 31;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_VISPAN;
+ if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value = (u8) 21;
+ else
+ cmd.buffer.registers[i++].value = (u8) 31;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_HICROP;
+ cmd.buffer.registers[i++].value = (u8) 0;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_VICROP;
+ cmd.buffer.registers[i++].value = (u8) 0;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_HFRACT;
+ if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value = (u8) 0x2B; /* 2/11 */
+ else
+ cmd.buffer.registers[i++].value = (u8) 0x81; /* 8/1 */
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_VFRACT;
+ if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value = (u8) 0x13; /* 1/3 */
+ else
+ cmd.buffer.registers[i++].value = (u8) 0x81; /* 8/1 */
+
+ cmd.reg_count = i;
+
+ cpia2_send_command(cam, &cmd);
+
+ return i;
+}
+
+
+/******************************************************************************
+ *
+ * setallproperties
+ *
+ * This sets all user changeable properties to the values in cam->params.
+ *****************************************************************************/
+static int set_all_properties(struct camera_data *cam)
+{
+ /**
+ * Don't set target_kb here, it will be set later.
+ * framerate and user_mode were already set (set_default_user_mode).
+ **/
+
+ cpia2_set_color_params(cam);
+
+ cpia2_usb_change_streaming_alternate(cam,
+ cam->params.camera_state.stream_mode);
+
+ cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE,
+ cam->params.vp_params.user_effects);
+
+ cpia2_set_flicker_mode(cam,
+ cam->params.flicker_control.flicker_mode_req);
+
+ cpia2_do_command(cam,
+ CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION,
+ TRANSFER_WRITE, cam->params.vp_params.gpio_direction);
+ cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DATA, TRANSFER_WRITE,
+ cam->params.vp_params.gpio_data);
+
+ wake_system(cam);
+
+ set_lowlight_boost(cam);
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * cpia2_save_camera_state
+ *
+ *****************************************************************************/
+void cpia2_save_camera_state(struct camera_data *cam)
+{
+ get_color_params(cam);
+ cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, TRANSFER_READ, 0);
+ cpia2_do_command(cam, CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION, TRANSFER_READ,
+ 0);
+ cpia2_do_command(cam, CPIA2_CMD_GET_VC_MP_GPIO_DATA, TRANSFER_READ, 0);
+ /* Don't get framerate or target_kb. Trust the values we already have */
+}
+
+/******************************************************************************
+ *
+ * get_color_params
+ *
+ *****************************************************************************/
+static void get_color_params(struct camera_data *cam)
+{
+ cpia2_do_command(cam, CPIA2_CMD_GET_VP_BRIGHTNESS, TRANSFER_READ, 0);
+ cpia2_do_command(cam, CPIA2_CMD_GET_VP_SATURATION, TRANSFER_READ, 0);
+ cpia2_do_command(cam, CPIA2_CMD_GET_CONTRAST, TRANSFER_READ, 0);
+}
+
+/******************************************************************************
+ *
+ * cpia2_set_color_params
+ *
+ *****************************************************************************/
+void cpia2_set_color_params(struct camera_data *cam)
+{
+ DBG("Setting color params\n");
+ cpia2_set_brightness(cam, cam->params.color_params.brightness);
+ cpia2_set_contrast(cam, cam->params.color_params.contrast);
+ cpia2_set_saturation(cam, cam->params.color_params.saturation);
+}
+
+/******************************************************************************
+ *
+ * cpia2_set_flicker_mode
+ *
+ *****************************************************************************/
+int cpia2_set_flicker_mode(struct camera_data *cam, int mode)
+{
+ unsigned char cam_reg;
+ int err = 0;
+
+ if(cam->params.pnp_id.device_type != DEVICE_STV_672)
+ return -EINVAL;
+
+ /* Set the appropriate bits in FLICKER_MODES, preserving the rest */
+ if((err = cpia2_do_command(cam, CPIA2_CMD_GET_FLICKER_MODES,
+ TRANSFER_READ, 0)))
+ return err;
+ cam_reg = cam->params.flicker_control.cam_register;
+
+ switch(mode) {
+ case NEVER_FLICKER:
+ cam_reg |= CPIA2_VP_FLICKER_MODES_NEVER_FLICKER;
+ cam_reg &= ~CPIA2_VP_FLICKER_MODES_50HZ;
+ break;
+ case FLICKER_60:
+ cam_reg &= ~CPIA2_VP_FLICKER_MODES_NEVER_FLICKER;
+ cam_reg &= ~CPIA2_VP_FLICKER_MODES_50HZ;
+ break;
+ case FLICKER_50:
+ cam_reg &= ~CPIA2_VP_FLICKER_MODES_NEVER_FLICKER;
+ cam_reg |= CPIA2_VP_FLICKER_MODES_50HZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if((err = cpia2_do_command(cam, CPIA2_CMD_SET_FLICKER_MODES,
+ TRANSFER_WRITE, cam_reg)))
+ return err;
+
+ /* Set the appropriate bits in EXP_MODES, preserving the rest */
+ if((err = cpia2_do_command(cam, CPIA2_CMD_GET_VP_EXP_MODES,
+ TRANSFER_READ, 0)))
+ return err;
+ cam_reg = cam->params.vp_params.exposure_modes;
+
+ if (mode == NEVER_FLICKER) {
+ cam_reg |= CPIA2_VP_EXPOSURE_MODES_INHIBIT_FLICKER;
+ } else {
+ cam_reg &= ~CPIA2_VP_EXPOSURE_MODES_INHIBIT_FLICKER;
+ }
+
+ if((err = cpia2_do_command(cam, CPIA2_CMD_SET_VP_EXP_MODES,
+ TRANSFER_WRITE, cam_reg)))
+ return err;
+
+ if((err = cpia2_do_command(cam, CPIA2_CMD_REHASH_VP4,
+ TRANSFER_WRITE, 1)))
+ return err;
+
+ switch(mode) {
+ case NEVER_FLICKER:
+ cam->params.flicker_control.flicker_mode_req = mode;
+ break;
+ case FLICKER_60:
+ cam->params.flicker_control.flicker_mode_req = mode;
+ cam->params.flicker_control.mains_frequency = 60;
+ break;
+ case FLICKER_50:
+ cam->params.flicker_control.flicker_mode_req = mode;
+ cam->params.flicker_control.mains_frequency = 50;
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ return err;
+}
+
+/******************************************************************************
+ *
+ * cpia2_set_property_flip
+ *
+ *****************************************************************************/
+void cpia2_set_property_flip(struct camera_data *cam, int prop_val)
+{
+ unsigned char cam_reg;
+
+ cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, TRANSFER_READ, 0);
+ cam_reg = cam->params.vp_params.user_effects;
+
+ if (prop_val)
+ {
+ cam_reg |= CPIA2_VP_USER_EFFECTS_FLIP;
+ }
+ else
+ {
+ cam_reg &= ~CPIA2_VP_USER_EFFECTS_FLIP;
+ }
+ cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE,
+ cam_reg);
+}
+
+/******************************************************************************
+ *
+ * cpia2_set_property_mirror
+ *
+ *****************************************************************************/
+void cpia2_set_property_mirror(struct camera_data *cam, int prop_val)
+{
+ unsigned char cam_reg;
+
+ cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, TRANSFER_READ, 0);
+ cam_reg = cam->params.vp_params.user_effects;
+
+ if (prop_val)
+ {
+ cam_reg |= CPIA2_VP_USER_EFFECTS_MIRROR;
+ }
+ else
+ {
+ cam_reg &= ~CPIA2_VP_USER_EFFECTS_MIRROR;
+ }
+ cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE,
+ cam_reg);
+}
+
+/******************************************************************************
+ *
+ * set_target_kb
+ *
+ * The new Target KB is set in cam->params.vc_params.target_kb and
+ * activates on reset.
+ *****************************************************************************/
+
+int cpia2_set_target_kb(struct camera_data *cam, unsigned char value)
+{
+ DBG("Requested target_kb = %d\n", value);
+ if (value != cam->params.vc_params.target_kb) {
+
+ cpia2_usb_stream_pause(cam);
+
+ /* reset camera for new target_kb */
+ cam->params.vc_params.target_kb = value;
+ cpia2_reset_camera(cam);
+
+ cpia2_usb_stream_resume(cam);
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * cpia2_set_gpio
+ *
+ *****************************************************************************/
+int cpia2_set_gpio(struct camera_data *cam, unsigned char setting)
+{
+ int ret;
+
+ /* Set the microport direction (register 0x90, should be defined
+ * already) to 1 (user output), and set the microport data (0x91) to
+ * the value in the ioctl argument.
+ */
+
+ ret = cpia2_do_command(cam,
+ CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION,
+ CPIA2_VC_MP_DIR_OUTPUT,
+ 255);
+ if (ret < 0)
+ return ret;
+ cam->params.vp_params.gpio_direction = 255;
+
+ ret = cpia2_do_command(cam,
+ CPIA2_CMD_SET_VC_MP_GPIO_DATA,
+ CPIA2_VC_MP_DIR_OUTPUT,
+ setting);
+ if (ret < 0)
+ return ret;
+ cam->params.vp_params.gpio_data = setting;
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * cpia2_set_fps
+ *
+ *****************************************************************************/
+int cpia2_set_fps(struct camera_data *cam, int framerate)
+{
+ int retval;
+
+ switch(framerate) {
+ case CPIA2_VP_FRAMERATE_30:
+ case CPIA2_VP_FRAMERATE_25:
+ if(cam->params.pnp_id.device_type == DEVICE_STV_672 &&
+ cam->params.version.sensor_flags ==
+ CPIA2_VP_SENSOR_FLAGS_500) {
+ return -EINVAL;
+ }
+ /* Fall through */
+ case CPIA2_VP_FRAMERATE_15:
+ case CPIA2_VP_FRAMERATE_12_5:
+ case CPIA2_VP_FRAMERATE_7_5:
+ case CPIA2_VP_FRAMERATE_6_25:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (cam->params.pnp_id.device_type == DEVICE_STV_672 &&
+ framerate == CPIA2_VP_FRAMERATE_15)
+ framerate = 0; /* Work around bug in VP4 */
+
+ retval = cpia2_do_command(cam,
+ CPIA2_CMD_FRAMERATE_REQ,
+ TRANSFER_WRITE,
+ framerate);
+
+ if(retval == 0)
+ cam->params.vp_params.frame_rate = framerate;
+
+ return retval;
+}
+
+/******************************************************************************
+ *
+ * cpia2_set_brightness
+ *
+ *****************************************************************************/
+void cpia2_set_brightness(struct camera_data *cam, unsigned char value)
+{
+ /***
+ * Don't let the register be set to zero - bug in VP4 - flash of full
+ * brightness
+ ***/
+ if (cam->params.pnp_id.device_type == DEVICE_STV_672 && value == 0)
+ value++;
+ DBG("Setting brightness to %d (0x%0x)\n", value, value);
+ cpia2_do_command(cam,CPIA2_CMD_SET_VP_BRIGHTNESS, TRANSFER_WRITE,value);
+}
+
+/******************************************************************************
+ *
+ * cpia2_set_contrast
+ *
+ *****************************************************************************/
+void cpia2_set_contrast(struct camera_data *cam, unsigned char value)
+{
+ DBG("Setting contrast to %d (0x%0x)\n", value, value);
+ cam->params.color_params.contrast = value;
+ cpia2_do_command(cam, CPIA2_CMD_SET_CONTRAST, TRANSFER_WRITE, value);
+}
+
+/******************************************************************************
+ *
+ * cpia2_set_saturation
+ *
+ *****************************************************************************/
+void cpia2_set_saturation(struct camera_data *cam, unsigned char value)
+{
+ DBG("Setting saturation to %d (0x%0x)\n", value, value);
+ cam->params.color_params.saturation = value;
+ cpia2_do_command(cam,CPIA2_CMD_SET_VP_SATURATION, TRANSFER_WRITE,value);
+}
+
+/******************************************************************************
+ *
+ * wake_system
+ *
+ *****************************************************************************/
+static void wake_system(struct camera_data *cam)
+{
+ cpia2_do_command(cam, CPIA2_CMD_SET_WAKEUP, TRANSFER_WRITE, 0);
+}
+
+/******************************************************************************
+ *
+ * set_lowlight_boost
+ *
+ * Valid for STV500 sensor only
+ *****************************************************************************/
+static void set_lowlight_boost(struct camera_data *cam)
+{
+ struct cpia2_command cmd;
+
+ if (cam->params.pnp_id.device_type != DEVICE_STV_672 ||
+ cam->params.version.sensor_flags != CPIA2_VP_SENSOR_FLAGS_500)
+ return;
+
+ cmd.direction = TRANSFER_WRITE;
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 3;
+ cmd.start = CPIA2_VP_RAM_ADDR_H;
+
+ cmd.buffer.block_data[0] = 0; /* High byte of address to write to */
+ cmd.buffer.block_data[1] = 0x59; /* Low byte of address to write to */
+ cmd.buffer.block_data[2] = 0; /* High byte of data to write */
+
+ cpia2_send_command(cam, &cmd);
+
+ if (cam->params.vp_params.lowlight_boost) {
+ cmd.buffer.block_data[0] = 0x02; /* Low byte data to write */
+ } else {
+ cmd.buffer.block_data[0] = 0x06;
+ }
+ cmd.start = CPIA2_VP_RAM_DATA;
+ cmd.reg_count = 1;
+ cpia2_send_command(cam, &cmd);
+
+ /* Rehash the VP4 values */
+ cpia2_do_command(cam, CPIA2_CMD_REHASH_VP4, TRANSFER_WRITE, 1);
+}
+
+/******************************************************************************
+ *
+ * cpia2_set_format
+ *
+ * Assumes that new size is already set in param struct.
+ *****************************************************************************/
+void cpia2_set_format(struct camera_data *cam)
+{
+ cam->flush = true;
+
+ cpia2_usb_stream_pause(cam);
+
+ /* reset camera to new size */
+ cpia2_set_low_power(cam);
+ cpia2_reset_camera(cam);
+ cam->flush = false;
+
+ cpia2_dbg_dump_registers(cam);
+
+ cpia2_usb_stream_resume(cam);
+}
+
+/******************************************************************************
+ *
+ * cpia2_dbg_dump_registers
+ *
+ *****************************************************************************/
+void cpia2_dbg_dump_registers(struct camera_data *cam)
+{
+#ifdef _CPIA2_DEBUG_
+ struct cpia2_command cmd;
+
+ if (!(debugs_on & DEBUG_DUMP_REGS))
+ return;
+
+ cmd.direction = TRANSFER_READ;
+
+ /* Start with bank 0 (SYSTEM) */
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM;
+ cmd.reg_count = 3;
+ cmd.start = 0;
+ cpia2_send_command(cam, &cmd);
+ printk(KERN_DEBUG "System Device Hi = 0x%X\n",
+ cmd.buffer.block_data[0]);
+ printk(KERN_DEBUG "System Device Lo = 0x%X\n",
+ cmd.buffer.block_data[1]);
+ printk(KERN_DEBUG "System_system control = 0x%X\n",
+ cmd.buffer.block_data[2]);
+
+ /* Bank 1 (VC) */
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
+ cmd.reg_count = 4;
+ cmd.start = 0x80;
+ cpia2_send_command(cam, &cmd);
+ printk(KERN_DEBUG "ASIC_ID = 0x%X\n",
+ cmd.buffer.block_data[0]);
+ printk(KERN_DEBUG "ASIC_REV = 0x%X\n",
+ cmd.buffer.block_data[1]);
+ printk(KERN_DEBUG "PW_CONTRL = 0x%X\n",
+ cmd.buffer.block_data[2]);
+ printk(KERN_DEBUG "WAKEUP = 0x%X\n",
+ cmd.buffer.block_data[3]);
+
+ cmd.start = 0xA0; /* ST_CTRL */
+ cmd.reg_count = 1;
+ cpia2_send_command(cam, &cmd);
+ printk(KERN_DEBUG "Stream ctrl = 0x%X\n",
+ cmd.buffer.block_data[0]);
+
+ cmd.start = 0xA4; /* Stream status */
+ cpia2_send_command(cam, &cmd);
+ printk(KERN_DEBUG "Stream status = 0x%X\n",
+ cmd.buffer.block_data[0]);
+
+ cmd.start = 0xA8; /* USB status */
+ cmd.reg_count = 3;
+ cpia2_send_command(cam, &cmd);
+ printk(KERN_DEBUG "USB_CTRL = 0x%X\n",
+ cmd.buffer.block_data[0]);
+ printk(KERN_DEBUG "USB_STRM = 0x%X\n",
+ cmd.buffer.block_data[1]);
+ printk(KERN_DEBUG "USB_STATUS = 0x%X\n",
+ cmd.buffer.block_data[2]);
+
+ cmd.start = 0xAF; /* USB settings */
+ cmd.reg_count = 1;
+ cpia2_send_command(cam, &cmd);
+ printk(KERN_DEBUG "USB settings = 0x%X\n",
+ cmd.buffer.block_data[0]);
+
+ cmd.start = 0xC0; /* VC stuff */
+ cmd.reg_count = 26;
+ cpia2_send_command(cam, &cmd);
+ printk(KERN_DEBUG "VC Control = 0x%0X\n",
+ cmd.buffer.block_data[0]);
+ printk(KERN_DEBUG "VC Format = 0x%0X\n",
+ cmd.buffer.block_data[3]);
+ printk(KERN_DEBUG "VC Clocks = 0x%0X\n",
+ cmd.buffer.block_data[4]);
+ printk(KERN_DEBUG "VC IHSize = 0x%0X\n",
+ cmd.buffer.block_data[5]);
+ printk(KERN_DEBUG "VC Xlim Hi = 0x%0X\n",
+ cmd.buffer.block_data[6]);
+ printk(KERN_DEBUG "VC XLim Lo = 0x%0X\n",
+ cmd.buffer.block_data[7]);
+ printk(KERN_DEBUG "VC YLim Hi = 0x%0X\n",
+ cmd.buffer.block_data[8]);
+ printk(KERN_DEBUG "VC YLim Lo = 0x%0X\n",
+ cmd.buffer.block_data[9]);
+ printk(KERN_DEBUG "VC OHSize = 0x%0X\n",
+ cmd.buffer.block_data[10]);
+ printk(KERN_DEBUG "VC OVSize = 0x%0X\n",
+ cmd.buffer.block_data[11]);
+ printk(KERN_DEBUG "VC HCrop = 0x%0X\n",
+ cmd.buffer.block_data[12]);
+ printk(KERN_DEBUG "VC VCrop = 0x%0X\n",
+ cmd.buffer.block_data[13]);
+ printk(KERN_DEBUG "VC HPhase = 0x%0X\n",
+ cmd.buffer.block_data[14]);
+ printk(KERN_DEBUG "VC VPhase = 0x%0X\n",
+ cmd.buffer.block_data[15]);
+ printk(KERN_DEBUG "VC HIspan = 0x%0X\n",
+ cmd.buffer.block_data[16]);
+ printk(KERN_DEBUG "VC VIspan = 0x%0X\n",
+ cmd.buffer.block_data[17]);
+ printk(KERN_DEBUG "VC HiCrop = 0x%0X\n",
+ cmd.buffer.block_data[18]);
+ printk(KERN_DEBUG "VC ViCrop = 0x%0X\n",
+ cmd.buffer.block_data[19]);
+ printk(KERN_DEBUG "VC HiFract = 0x%0X\n",
+ cmd.buffer.block_data[20]);
+ printk(KERN_DEBUG "VC ViFract = 0x%0X\n",
+ cmd.buffer.block_data[21]);
+ printk(KERN_DEBUG "VC JPeg Opt = 0x%0X\n",
+ cmd.buffer.block_data[22]);
+ printk(KERN_DEBUG "VC Creep Per = 0x%0X\n",
+ cmd.buffer.block_data[23]);
+ printk(KERN_DEBUG "VC User Sq. = 0x%0X\n",
+ cmd.buffer.block_data[24]);
+ printk(KERN_DEBUG "VC Target KB = 0x%0X\n",
+ cmd.buffer.block_data[25]);
+
+ /*** VP ***/
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 14;
+ cmd.start = 0;
+ cpia2_send_command(cam, &cmd);
+
+ printk(KERN_DEBUG "VP Dev Hi = 0x%0X\n",
+ cmd.buffer.block_data[0]);
+ printk(KERN_DEBUG "VP Dev Lo = 0x%0X\n",
+ cmd.buffer.block_data[1]);
+ printk(KERN_DEBUG "VP Sys State = 0x%0X\n",
+ cmd.buffer.block_data[2]);
+ printk(KERN_DEBUG "VP Sys Ctrl = 0x%0X\n",
+ cmd.buffer.block_data[3]);
+ printk(KERN_DEBUG "VP Sensor flg = 0x%0X\n",
+ cmd.buffer.block_data[5]);
+ printk(KERN_DEBUG "VP Sensor Rev = 0x%0X\n",
+ cmd.buffer.block_data[6]);
+ printk(KERN_DEBUG "VP Dev Config = 0x%0X\n",
+ cmd.buffer.block_data[7]);
+ printk(KERN_DEBUG "VP GPIO_DIR = 0x%0X\n",
+ cmd.buffer.block_data[8]);
+ printk(KERN_DEBUG "VP GPIO_DATA = 0x%0X\n",
+ cmd.buffer.block_data[9]);
+ printk(KERN_DEBUG "VP Ram ADDR H = 0x%0X\n",
+ cmd.buffer.block_data[10]);
+ printk(KERN_DEBUG "VP Ram ADDR L = 0x%0X\n",
+ cmd.buffer.block_data[11]);
+ printk(KERN_DEBUG "VP RAM Data = 0x%0X\n",
+ cmd.buffer.block_data[12]);
+ printk(KERN_DEBUG "Do Call = 0x%0X\n",
+ cmd.buffer.block_data[13]);
+
+ if (cam->params.pnp_id.device_type == DEVICE_STV_672) {
+ cmd.reg_count = 9;
+ cmd.start = 0x0E;
+ cpia2_send_command(cam, &cmd);
+ printk(KERN_DEBUG "VP Clock Ctrl = 0x%0X\n",
+ cmd.buffer.block_data[0]);
+ printk(KERN_DEBUG "VP Patch Rev = 0x%0X\n",
+ cmd.buffer.block_data[1]);
+ printk(KERN_DEBUG "VP Vid Mode = 0x%0X\n",
+ cmd.buffer.block_data[2]);
+ printk(KERN_DEBUG "VP Framerate = 0x%0X\n",
+ cmd.buffer.block_data[3]);
+ printk(KERN_DEBUG "VP UserEffect = 0x%0X\n",
+ cmd.buffer.block_data[4]);
+ printk(KERN_DEBUG "VP White Bal = 0x%0X\n",
+ cmd.buffer.block_data[5]);
+ printk(KERN_DEBUG "VP WB thresh = 0x%0X\n",
+ cmd.buffer.block_data[6]);
+ printk(KERN_DEBUG "VP Exp Modes = 0x%0X\n",
+ cmd.buffer.block_data[7]);
+ printk(KERN_DEBUG "VP Exp Target = 0x%0X\n",
+ cmd.buffer.block_data[8]);
+
+ cmd.reg_count = 1;
+ cmd.start = 0x1B;
+ cpia2_send_command(cam, &cmd);
+ printk(KERN_DEBUG "VP FlickerMds = 0x%0X\n",
+ cmd.buffer.block_data[0]);
+ } else {
+ cmd.reg_count = 8 ;
+ cmd.start = 0x0E;
+ cpia2_send_command(cam, &cmd);
+ printk(KERN_DEBUG "VP Clock Ctrl = 0x%0X\n",
+ cmd.buffer.block_data[0]);
+ printk(KERN_DEBUG "VP Patch Rev = 0x%0X\n",
+ cmd.buffer.block_data[1]);
+ printk(KERN_DEBUG "VP Vid Mode = 0x%0X\n",
+ cmd.buffer.block_data[5]);
+ printk(KERN_DEBUG "VP Framerate = 0x%0X\n",
+ cmd.buffer.block_data[6]);
+ printk(KERN_DEBUG "VP UserEffect = 0x%0X\n",
+ cmd.buffer.block_data[7]);
+
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VP5_EXPOSURE_TARGET;
+ cpia2_send_command(cam, &cmd);
+ printk(KERN_DEBUG "VP5 Exp Target= 0x%0X\n",
+ cmd.buffer.block_data[0]);
+
+ cmd.reg_count = 4;
+ cmd.start = 0x3A;
+ cpia2_send_command(cam, &cmd);
+ printk(KERN_DEBUG "VP5 MY Black = 0x%0X\n",
+ cmd.buffer.block_data[0]);
+ printk(KERN_DEBUG "VP5 MCY Range = 0x%0X\n",
+ cmd.buffer.block_data[1]);
+ printk(KERN_DEBUG "VP5 MYCEILING = 0x%0X\n",
+ cmd.buffer.block_data[2]);
+ printk(KERN_DEBUG "VP5 MCUV Sat = 0x%0X\n",
+ cmd.buffer.block_data[3]);
+ }
+#endif
+}
+
+/******************************************************************************
+ *
+ * reset_camera_struct
+ *
+ * Sets all values to the defaults
+ *****************************************************************************/
+static void reset_camera_struct(struct camera_data *cam)
+{
+ /***
+ * The following parameter values are the defaults from the register map.
+ ***/
+ cam->params.color_params.brightness = DEFAULT_BRIGHTNESS;
+ cam->params.color_params.contrast = DEFAULT_CONTRAST;
+ cam->params.color_params.saturation = DEFAULT_SATURATION;
+ cam->params.vp_params.lowlight_boost = 0;
+
+ /* FlickerModes */
+ cam->params.flicker_control.flicker_mode_req = NEVER_FLICKER;
+ cam->params.flicker_control.mains_frequency = 60;
+
+ /* jpeg params */
+ cam->params.compression.jpeg_options = CPIA2_VC_VC_JPEG_OPT_DEFAULT;
+ cam->params.compression.creep_period = 2;
+ cam->params.compression.user_squeeze = 20;
+ cam->params.compression.inhibit_htables = false;
+
+ /* gpio params */
+ cam->params.vp_params.gpio_direction = 0; /* write, the default safe mode */
+ cam->params.vp_params.gpio_data = 0;
+
+ /* Target kb params */
+ cam->params.vc_params.target_kb = DEFAULT_TARGET_KB;
+
+ /***
+ * Set Sensor FPS as fast as possible.
+ ***/
+ if(cam->params.pnp_id.device_type == DEVICE_STV_672) {
+ if(cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500)
+ cam->params.vp_params.frame_rate = CPIA2_VP_FRAMERATE_15;
+ else
+ cam->params.vp_params.frame_rate = CPIA2_VP_FRAMERATE_30;
+ } else {
+ cam->params.vp_params.frame_rate = CPIA2_VP_FRAMERATE_30;
+ }
+
+ /***
+ * Set default video mode as large as possible :
+ * for vga sensor set to vga, for cif sensor set to CIF.
+ ***/
+ if (cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) {
+ cam->sensor_type = CPIA2_SENSOR_500;
+ cam->video_size = VIDEOSIZE_VGA;
+ cam->params.roi.width = STV_IMAGE_VGA_COLS;
+ cam->params.roi.height = STV_IMAGE_VGA_ROWS;
+ } else {
+ cam->sensor_type = CPIA2_SENSOR_410;
+ cam->video_size = VIDEOSIZE_CIF;
+ cam->params.roi.width = STV_IMAGE_CIF_COLS;
+ cam->params.roi.height = STV_IMAGE_CIF_ROWS;
+ }
+
+ cam->width = cam->params.roi.width;
+ cam->height = cam->params.roi.height;
+}
+
+/******************************************************************************
+ *
+ * cpia2_init_camera_struct
+ *
+ * Initializes camera struct, does not call reset to fill in defaults.
+ *****************************************************************************/
+struct camera_data *cpia2_init_camera_struct(void)
+{
+ struct camera_data *cam;
+
+ cam = kzalloc(sizeof(*cam), GFP_KERNEL);
+
+ if (!cam) {
+ ERR("couldn't kmalloc cpia2 struct\n");
+ return NULL;
+ }
+
+
+ cam->present = 1;
+ mutex_init(&cam->v4l2_lock);
+ init_waitqueue_head(&cam->wq_stream);
+
+ return cam;
+}
+
+/******************************************************************************
+ *
+ * cpia2_init_camera
+ *
+ * Initializes camera.
+ *****************************************************************************/
+int cpia2_init_camera(struct camera_data *cam)
+{
+ DBG("Start\n");
+
+ cam->mmapped = false;
+
+ /* Get sensor and asic types before reset. */
+ cpia2_set_high_power(cam);
+ cpia2_get_version_info(cam);
+ if (cam->params.version.asic_id != CPIA2_ASIC_672) {
+ ERR("Device IO error (asicID has incorrect value of 0x%X\n",
+ cam->params.version.asic_id);
+ return -ENODEV;
+ }
+
+ /* Set GPIO direction and data to a safe state. */
+ cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION,
+ TRANSFER_WRITE, 0);
+ cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DATA,
+ TRANSFER_WRITE, 0);
+
+ /* resetting struct requires version info for sensor and asic types */
+ reset_camera_struct(cam);
+
+ cpia2_set_low_power(cam);
+
+ DBG("End\n");
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * cpia2_allocate_buffers
+ *
+ *****************************************************************************/
+int cpia2_allocate_buffers(struct camera_data *cam)
+{
+ int i;
+
+ if(!cam->buffers) {
+ u32 size = cam->num_frames*sizeof(struct framebuf);
+ cam->buffers = kmalloc(size, GFP_KERNEL);
+ if(!cam->buffers) {
+ ERR("couldn't kmalloc frame buffer structures\n");
+ return -ENOMEM;
+ }
+ }
+
+ if(!cam->frame_buffer) {
+ cam->frame_buffer = rvmalloc(cam->frame_size*cam->num_frames);
+ if (!cam->frame_buffer) {
+ ERR("couldn't vmalloc frame buffer data area\n");
+ kfree(cam->buffers);
+ cam->buffers = NULL;
+ return -ENOMEM;
+ }
+ }
+
+ for(i=0; i<cam->num_frames-1; ++i) {
+ cam->buffers[i].next = &cam->buffers[i+1];
+ cam->buffers[i].data = cam->frame_buffer +i*cam->frame_size;
+ cam->buffers[i].status = FRAME_EMPTY;
+ cam->buffers[i].length = 0;
+ cam->buffers[i].max_length = 0;
+ cam->buffers[i].num = i;
+ }
+ cam->buffers[i].next = cam->buffers;
+ cam->buffers[i].data = cam->frame_buffer +i*cam->frame_size;
+ cam->buffers[i].status = FRAME_EMPTY;
+ cam->buffers[i].length = 0;
+ cam->buffers[i].max_length = 0;
+ cam->buffers[i].num = i;
+ cam->curbuff = cam->buffers;
+ cam->workbuff = cam->curbuff->next;
+ DBG("buffers=%p, curbuff=%p, workbuff=%p\n", cam->buffers, cam->curbuff,
+ cam->workbuff);
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * cpia2_free_buffers
+ *
+ *****************************************************************************/
+void cpia2_free_buffers(struct camera_data *cam)
+{
+ if(cam->buffers) {
+ kfree(cam->buffers);
+ cam->buffers = NULL;
+ }
+ if(cam->frame_buffer) {
+ rvfree(cam->frame_buffer, cam->frame_size*cam->num_frames);
+ cam->frame_buffer = NULL;
+ }
+}
+
+/******************************************************************************
+ *
+ * cpia2_read
+ *
+ *****************************************************************************/
+long cpia2_read(struct camera_data *cam,
+ char __user *buf, unsigned long count, int noblock)
+{
+ struct framebuf *frame;
+
+ if (!count)
+ return 0;
+
+ if (!buf) {
+ ERR("%s: buffer NULL\n",__func__);
+ return -EINVAL;
+ }
+
+ if (!cam) {
+ ERR("%s: Internal error, camera_data NULL!\n",__func__);
+ return -EINVAL;
+ }
+
+ if (!cam->present) {
+ LOG("%s: camera removed\n",__func__);
+ return 0; /* EOF */
+ }
+
+ if (!cam->streaming) {
+ /* Start streaming */
+ cpia2_usb_stream_start(cam,
+ cam->params.camera_state.stream_mode);
+ }
+
+ /* Copy cam->curbuff in case it changes while we're processing */
+ frame = cam->curbuff;
+ if (noblock && frame->status != FRAME_READY) {
+ return -EAGAIN;
+ }
+
+ if (frame->status != FRAME_READY) {
+ mutex_unlock(&cam->v4l2_lock);
+ wait_event_interruptible(cam->wq_stream,
+ !cam->present ||
+ (frame = cam->curbuff)->status == FRAME_READY);
+ mutex_lock(&cam->v4l2_lock);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ if (!cam->present)
+ return 0;
+ }
+
+ /* copy data to user space */
+ if (frame->length > count)
+ return -EFAULT;
+ if (copy_to_user(buf, frame->data, frame->length))
+ return -EFAULT;
+
+ count = frame->length;
+
+ frame->status = FRAME_EMPTY;
+
+ return count;
+}
+
+/******************************************************************************
+ *
+ * cpia2_poll
+ *
+ *****************************************************************************/
+unsigned int cpia2_poll(struct camera_data *cam, struct file *filp,
+ poll_table *wait)
+{
+ unsigned int status=0;
+
+ if (!cam) {
+ ERR("%s: Internal error, camera_data not found!\n",__func__);
+ return POLLERR;
+ }
+
+ if (!cam->present)
+ return POLLHUP;
+
+ if(!cam->streaming) {
+ /* Start streaming */
+ cpia2_usb_stream_start(cam,
+ cam->params.camera_state.stream_mode);
+ }
+
+ poll_wait(filp, &cam->wq_stream, wait);
+
+ if(!cam->present)
+ status = POLLHUP;
+ else if(cam->curbuff->status == FRAME_READY)
+ status = POLLIN | POLLRDNORM;
+
+ return status;
+}
+
+/******************************************************************************
+ *
+ * cpia2_remap_buffer
+ *
+ *****************************************************************************/
+int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma)
+{
+ const char *adr = (const char *)vma->vm_start;
+ unsigned long size = vma->vm_end-vma->vm_start;
+ unsigned long start_offset = vma->vm_pgoff << PAGE_SHIFT;
+ unsigned long start = (unsigned long) adr;
+ unsigned long page, pos;
+
+ if (!cam)
+ return -ENODEV;
+
+ DBG("mmap offset:%ld size:%ld\n", start_offset, size);
+
+ if (!cam->present)
+ return -ENODEV;
+
+ if (size > cam->frame_size*cam->num_frames ||
+ (start_offset % cam->frame_size) != 0 ||
+ (start_offset+size > cam->frame_size*cam->num_frames))
+ return -EINVAL;
+
+ pos = ((unsigned long) (cam->frame_buffer)) + start_offset;
+ while (size > 0) {
+ page = kvirt_to_pa(pos);
+ if (remap_pfn_range(vma, start, page >> PAGE_SHIFT, PAGE_SIZE, PAGE_SHARED))
+ return -EAGAIN;
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size -= PAGE_SIZE;
+ else
+ size = 0;
+ }
+
+ cam->mmapped = true;
+ return 0;
+}
diff --git a/drivers/media/video/cpia2/cpia2_registers.h b/drivers/media/video/cpia2/cpia2_registers.h
new file mode 100644
index 00000000..3bbec514
--- /dev/null
+++ b/drivers/media/video/cpia2/cpia2_registers.h
@@ -0,0 +1,476 @@
+/****************************************************************************
+ *
+ * Filename: cpia2registers.h
+ *
+ * Copyright 2001, STMicrolectronics, Inc.
+ *
+ * Description:
+ * Definitions for the CPia2 register set
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************************/
+
+#ifndef CPIA2_REGISTER_HEADER
+#define CPIA2_REGISTER_HEADER
+
+/***
+ * System register set (Bank 0)
+ ***/
+#define CPIA2_SYSTEM_DEVICE_HI 0x00
+#define CPIA2_SYSTEM_DEVICE_LO 0x01
+
+#define CPIA2_SYSTEM_SYSTEM_CONTROL 0x02
+#define CPIA2_SYSTEM_CONTROL_LOW_POWER 0x00
+#define CPIA2_SYSTEM_CONTROL_HIGH_POWER 0x01
+#define CPIA2_SYSTEM_CONTROL_SUSPEND 0x02
+#define CPIA2_SYSTEM_CONTROL_V2W_ERR 0x10
+#define CPIA2_SYSTEM_CONTROL_RB_ERR 0x10
+#define CPIA2_SYSTEM_CONTROL_CLEAR_ERR 0x80
+
+#define CPIA2_SYSTEM_INT_PACKET_CTRL 0x04
+#define CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_SW_XX 0x01
+#define CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_EOF 0x02
+#define CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_INT1 0x04
+
+#define CPIA2_SYSTEM_CACHE_CTRL 0x05
+#define CPIA2_SYSTEM_CACHE_CTRL_CACHE_RESET 0x01
+#define CPIA2_SYSTEM_CACHE_CTRL_CACHE_FLUSH 0x02
+
+#define CPIA2_SYSTEM_SERIAL_CTRL 0x06
+#define CPIA2_SYSTEM_SERIAL_CTRL_NULL_CMD 0x00
+#define CPIA2_SYSTEM_SERIAL_CTRL_START_CMD 0x01
+#define CPIA2_SYSTEM_SERIAL_CTRL_STOP_CMD 0x02
+#define CPIA2_SYSTEM_SERIAL_CTRL_WRITE_CMD 0x03
+#define CPIA2_SYSTEM_SERIAL_CTRL_READ_ACK_CMD 0x04
+#define CPIA2_SYSTEM_SERIAL_CTRL_READ_NACK_CMD 0x05
+
+#define CPIA2_SYSTEM_SERIAL_DATA 0x07
+
+#define CPIA2_SYSTEM_VP_SERIAL_ADDR 0x08
+
+/***
+ * I2C addresses for various devices in CPiA2
+ ***/
+#define CPIA2_SYSTEM_VP_SERIAL_ADDR_SENSOR 0x20
+#define CPIA2_SYSTEM_VP_SERIAL_ADDR_VP 0x88
+#define CPIA2_SYSTEM_VP_SERIAL_ADDR_676_VP 0x8A
+
+#define CPIA2_SYSTEM_SPARE_REG1 0x09
+#define CPIA2_SYSTEM_SPARE_REG2 0x0A
+#define CPIA2_SYSTEM_SPARE_REG3 0x0B
+
+#define CPIA2_SYSTEM_MC_PORT_0 0x0C
+#define CPIA2_SYSTEM_MC_PORT_1 0x0D
+#define CPIA2_SYSTEM_MC_PORT_2 0x0E
+#define CPIA2_SYSTEM_MC_PORT_3 0x0F
+
+#define CPIA2_SYSTEM_STATUS_PKT 0x20
+#define CPIA2_SYSTEM_STATUS_PKT_END 0x27
+
+#define CPIA2_SYSTEM_DESCRIP_VID_HI 0x30
+#define CPIA2_SYSTEM_DESCRIP_VID_LO 0x31
+#define CPIA2_SYSTEM_DESCRIP_PID_HI 0x32
+#define CPIA2_SYSTEM_DESCRIP_PID_LO 0x33
+
+#define CPIA2_SYSTEM_FW_VERSION_HI 0x34
+#define CPIA2_SYSTEM_FW_VERSION_LO 0x35
+
+#define CPIA2_SYSTEM_CACHE_START_INDEX 0x80
+#define CPIA2_SYSTEM_CACHE_MAX_WRITES 0x10
+
+/***
+ * VC register set (Bank 1)
+ ***/
+#define CPIA2_VC_ASIC_ID 0x80
+
+#define CPIA2_VC_ASIC_REV 0x81
+
+#define CPIA2_VC_PW_CTRL 0x82
+#define CPIA2_VC_PW_CTRL_COLDSTART 0x01
+#define CPIA2_VC_PW_CTRL_CP_CLK_EN 0x02
+#define CPIA2_VC_PW_CTRL_VP_RESET_N 0x04
+#define CPIA2_VC_PW_CTRL_VC_CLK_EN 0x08
+#define CPIA2_VC_PW_CTRL_VC_RESET_N 0x10
+#define CPIA2_VC_PW_CTRL_GOTO_SUSPEND 0x20
+#define CPIA2_VC_PW_CTRL_UDC_SUSPEND 0x40
+#define CPIA2_VC_PW_CTRL_PWR_DOWN 0x80
+
+#define CPIA2_VC_WAKEUP 0x83
+#define CPIA2_VC_WAKEUP_SW_ENABLE 0x01
+#define CPIA2_VC_WAKEUP_XX_ENABLE 0x02
+#define CPIA2_VC_WAKEUP_SW_ATWAKEUP 0x04
+#define CPIA2_VC_WAKEUP_XX_ATWAKEUP 0x08
+
+#define CPIA2_VC_CLOCK_CTRL 0x84
+#define CPIA2_VC_CLOCK_CTRL_TESTUP72 0x01
+
+#define CPIA2_VC_INT_ENABLE 0x88
+#define CPIA2_VC_INT_ENABLE_XX_IE 0x01
+#define CPIA2_VC_INT_ENABLE_SW_IE 0x02
+#define CPIA2_VC_INT_ENABLE_VC_IE 0x04
+#define CPIA2_VC_INT_ENABLE_USBDATA_IE 0x08
+#define CPIA2_VC_INT_ENABLE_USBSETUP_IE 0x10
+#define CPIA2_VC_INT_ENABLE_USBCFG_IE 0x20
+
+#define CPIA2_VC_INT_FLAG 0x89
+#define CPIA2_VC_INT_ENABLE_XX_FLAG 0x01
+#define CPIA2_VC_INT_ENABLE_SW_FLAG 0x02
+#define CPIA2_VC_INT_ENABLE_VC_FLAG 0x04
+#define CPIA2_VC_INT_ENABLE_USBDATA_FLAG 0x08
+#define CPIA2_VC_INT_ENABLE_USBSETUP_FLAG 0x10
+#define CPIA2_VC_INT_ENABLE_USBCFG_FLAG 0x20
+#define CPIA2_VC_INT_ENABLE_SET_RESET_BIT 0x80
+
+#define CPIA2_VC_INT_STATE 0x8A
+#define CPIA2_VC_INT_STATE_XX_STATE 0x01
+#define CPIA2_VC_INT_STATE_SW_STATE 0x02
+
+#define CPIA2_VC_MP_DIR 0x90
+#define CPIA2_VC_MP_DIR_INPUT 0x00
+#define CPIA2_VC_MP_DIR_OUTPUT 0x01
+
+#define CPIA2_VC_MP_DATA 0x91
+
+#define CPIA2_VC_DP_CTRL 0x98
+#define CPIA2_VC_DP_CTRL_MODE_0 0x00
+#define CPIA2_VC_DP_CTRL_MODE_A 0x01
+#define CPIA2_VC_DP_CTRL_MODE_B 0x02
+#define CPIA2_VC_DP_CTRL_MODE_C 0x03
+#define CPIA2_VC_DP_CTRL_FAKE_FST 0x04
+
+#define CPIA2_VC_AD_CTRL 0x99
+#define CPIA2_VC_AD_CTRL_SRC_0 0x00
+#define CPIA2_VC_AD_CTRL_SRC_DIGI_A 0x01
+#define CPIA2_VC_AD_CTRL_SRC_REG 0x02
+#define CPIA2_VC_AD_CTRL_DST_USB 0x00
+#define CPIA2_VC_AD_CTRL_DST_REG 0x04
+
+#define CPIA2_VC_AD_TEST_IN 0x9B
+
+#define CPIA2_VC_AD_TEST_OUT 0x9C
+
+#define CPIA2_VC_AD_STATUS 0x9D
+#define CPIA2_VC_AD_STATUS_EMPTY 0x01
+#define CPIA2_VC_AD_STATUS_FULL 0x02
+
+#define CPIA2_VC_DP_DATA 0x9E
+
+#define CPIA2_VC_ST_CTRL 0xA0
+#define CPIA2_VC_ST_CTRL_SRC_VC 0x00
+#define CPIA2_VC_ST_CTRL_SRC_DP 0x01
+#define CPIA2_VC_ST_CTRL_SRC_REG 0x02
+
+#define CPIA2_VC_ST_CTRL_RAW_SELECT 0x04
+
+#define CPIA2_VC_ST_CTRL_DST_USB 0x00
+#define CPIA2_VC_ST_CTRL_DST_DP 0x08
+#define CPIA2_VC_ST_CTRL_DST_REG 0x10
+
+#define CPIA2_VC_ST_CTRL_FIFO_ENABLE 0x20
+#define CPIA2_VC_ST_CTRL_EOF_DETECT 0x40
+
+#define CPIA2_VC_ST_TEST 0xA1
+#define CPIA2_VC_ST_TEST_MODE_MANUAL 0x00
+#define CPIA2_VC_ST_TEST_MODE_INCREMENT 0x02
+
+#define CPIA2_VC_ST_TEST_AUTO_FILL 0x08
+
+#define CPIA2_VC_ST_TEST_REPEAT_FIFO 0x10
+
+#define CPIA2_VC_ST_TEST_IN 0xA2
+
+#define CPIA2_VC_ST_TEST_OUT 0xA3
+
+#define CPIA2_VC_ST_STATUS 0xA4
+#define CPIA2_VC_ST_STATUS_EMPTY 0x01
+#define CPIA2_VC_ST_STATUS_FULL 0x02
+
+#define CPIA2_VC_ST_FRAME_DETECT_1 0xA5
+
+#define CPIA2_VC_ST_FRAME_DETECT_2 0xA6
+
+#define CPIA2_VC_USB_CTRL 0xA8
+#define CPIA2_VC_USB_CTRL_CMD_STALLED 0x01
+#define CPIA2_VC_USB_CTRL_CMD_READY 0x02
+#define CPIA2_VC_USB_CTRL_CMD_STATUS 0x04
+#define CPIA2_VC_USB_CTRL_CMD_STATUS_DIR 0x08
+#define CPIA2_VC_USB_CTRL_CMD_NO_CLASH 0x10
+#define CPIA2_VC_USB_CTRL_CMD_MICRO_ACCESS 0x80
+
+#define CPIA2_VC_USB_STRM 0xA9
+#define CPIA2_VC_USB_STRM_ISO_ENABLE 0x01
+#define CPIA2_VC_USB_STRM_BLK_ENABLE 0x02
+#define CPIA2_VC_USB_STRM_INT_ENABLE 0x04
+#define CPIA2_VC_USB_STRM_AUD_ENABLE 0x08
+
+#define CPIA2_VC_USB_STATUS 0xAA
+#define CPIA2_VC_USB_STATUS_CMD_IN_PROGRESS 0x01
+#define CPIA2_VC_USB_STATUS_CMD_STATUS_STALL 0x02
+#define CPIA2_VC_USB_STATUS_CMD_HANDSHAKE 0x04
+#define CPIA2_VC_USB_STATUS_CMD_OVERRIDE 0x08
+#define CPIA2_VC_USB_STATUS_CMD_FIFO_BUSY 0x10
+#define CPIA2_VC_USB_STATUS_BULK_REPEAT_TXN 0x20
+#define CPIA2_VC_USB_STATUS_CONFIG_DONE 0x40
+#define CPIA2_VC_USB_STATUS_USB_SUSPEND 0x80
+
+#define CPIA2_VC_USB_CMDW 0xAB
+
+#define CPIA2_VC_USB_DATARW 0xAC
+
+#define CPIA2_VC_USB_INFO 0xAD
+
+#define CPIA2_VC_USB_CONFIG 0xAE
+
+#define CPIA2_VC_USB_SETTINGS 0xAF
+#define CPIA2_VC_USB_SETTINGS_CONFIG_MASK 0x03
+#define CPIA2_VC_USB_SETTINGS_INTERFACE_MASK 0x0C
+#define CPIA2_VC_USB_SETTINGS_ALTERNATE_MASK 0x70
+
+#define CPIA2_VC_USB_ISOLIM 0xB0
+
+#define CPIA2_VC_USB_ISOFAILS 0xB1
+
+#define CPIA2_VC_USB_ISOMAXPKTHI 0xB2
+
+#define CPIA2_VC_USB_ISOMAXPKTLO 0xB3
+
+#define CPIA2_VC_V2W_CTRL 0xB8
+#define CPIA2_VC_V2W_SELECT 0x01
+
+#define CPIA2_VC_V2W_SCL 0xB9
+
+#define CPIA2_VC_V2W_SDA 0xBA
+
+#define CPIA2_VC_VC_CTRL 0xC0
+#define CPIA2_VC_VC_CTRL_RUN 0x01
+#define CPIA2_VC_VC_CTRL_SINGLESHOT 0x02
+#define CPIA2_VC_VC_CTRL_IDLING 0x04
+#define CPIA2_VC_VC_CTRL_INHIBIT_H_TABLES 0x10
+#define CPIA2_VC_VC_CTRL_INHIBIT_Q_TABLES 0x20
+#define CPIA2_VC_VC_CTRL_INHIBIT_PRIVATE 0x40
+
+#define CPIA2_VC_VC_RESTART_IVAL_HI 0xC1
+
+#define CPIA2_VC_VC_RESTART_IVAL_LO 0xC2
+
+#define CPIA2_VC_VC_FORMAT 0xC3
+#define CPIA2_VC_VC_FORMAT_UFIRST 0x01
+#define CPIA2_VC_VC_FORMAT_MONO 0x02
+#define CPIA2_VC_VC_FORMAT_DECIMATING 0x04
+#define CPIA2_VC_VC_FORMAT_SHORTLINE 0x08
+#define CPIA2_VC_VC_FORMAT_SELFTEST 0x10
+
+#define CPIA2_VC_VC_CLOCKS 0xC4
+#define CPIA2_VC_VC_CLOCKS_CLKDIV_MASK 0x03
+#define CPIA2_VC_VC_672_CLOCKS_CIF_DIV_BY_3 0x04
+#define CPIA2_VC_VC_672_CLOCKS_SCALING 0x08
+#define CPIA2_VC_VC_CLOCKS_LOGDIV0 0x00
+#define CPIA2_VC_VC_CLOCKS_LOGDIV1 0x01
+#define CPIA2_VC_VC_CLOCKS_LOGDIV2 0x02
+#define CPIA2_VC_VC_CLOCKS_LOGDIV3 0x03
+#define CPIA2_VC_VC_676_CLOCKS_CIF_DIV_BY_3 0x08
+#define CPIA2_VC_VC_676_CLOCKS_SCALING 0x10
+
+#define CPIA2_VC_VC_IHSIZE_LO 0xC5
+
+#define CPIA2_VC_VC_XLIM_HI 0xC6
+
+#define CPIA2_VC_VC_XLIM_LO 0xC7
+
+#define CPIA2_VC_VC_YLIM_HI 0xC8
+
+#define CPIA2_VC_VC_YLIM_LO 0xC9
+
+#define CPIA2_VC_VC_OHSIZE 0xCA
+
+#define CPIA2_VC_VC_OVSIZE 0xCB
+
+#define CPIA2_VC_VC_HCROP 0xCC
+
+#define CPIA2_VC_VC_VCROP 0xCD
+
+#define CPIA2_VC_VC_HPHASE 0xCE
+
+#define CPIA2_VC_VC_VPHASE 0xCF
+
+#define CPIA2_VC_VC_HISPAN 0xD0
+
+#define CPIA2_VC_VC_VISPAN 0xD1
+
+#define CPIA2_VC_VC_HICROP 0xD2
+
+#define CPIA2_VC_VC_VICROP 0xD3
+
+#define CPIA2_VC_VC_HFRACT 0xD4
+#define CPIA2_VC_VC_HFRACT_DEN_MASK 0x0F
+#define CPIA2_VC_VC_HFRACT_NUM_MASK 0xF0
+
+#define CPIA2_VC_VC_VFRACT 0xD5
+#define CPIA2_VC_VC_VFRACT_DEN_MASK 0x0F
+#define CPIA2_VC_VC_VFRACT_NUM_MASK 0xF0
+
+#define CPIA2_VC_VC_JPEG_OPT 0xD6
+#define CPIA2_VC_VC_JPEG_OPT_DOUBLE_SQUEEZE 0x01
+#define CPIA2_VC_VC_JPEG_OPT_NO_DC_AUTO_SQUEEZE 0x02
+#define CPIA2_VC_VC_JPEG_OPT_AUTO_SQUEEZE 0x04
+#define CPIA2_VC_VC_JPEG_OPT_DEFAULT (CPIA2_VC_VC_JPEG_OPT_DOUBLE_SQUEEZE|\
+ CPIA2_VC_VC_JPEG_OPT_AUTO_SQUEEZE)
+
+
+#define CPIA2_VC_VC_CREEP_PERIOD 0xD7
+#define CPIA2_VC_VC_USER_SQUEEZE 0xD8
+#define CPIA2_VC_VC_TARGET_KB 0xD9
+
+#define CPIA2_VC_VC_AUTO_SQUEEZE 0xE6
+
+
+/***
+ * VP register set (Bank 2)
+ ***/
+#define CPIA2_VP_DEVICEH 0
+#define CPIA2_VP_DEVICEL 1
+
+#define CPIA2_VP_SYSTEMSTATE 0x02
+#define CPIA2_VP_SYSTEMSTATE_HK_ALIVE 0x01
+
+#define CPIA2_VP_SYSTEMCTRL 0x03
+#define CPIA2_VP_SYSTEMCTRL_REQ_CLEAR_ERROR 0x80
+#define CPIA2_VP_SYSTEMCTRL_POWER_DOWN_PLL 0x20
+#define CPIA2_VP_SYSTEMCTRL_REQ_SUSPEND_STATE 0x10
+#define CPIA2_VP_SYSTEMCTRL_REQ_SERIAL_WAKEUP 0x08
+#define CPIA2_VP_SYSTEMCTRL_REQ_AUTOLOAD 0x04
+#define CPIA2_VP_SYSTEMCTRL_HK_CONTROL 0x02
+#define CPIA2_VP_SYSTEMCTRL_POWER_CONTROL 0x01
+
+#define CPIA2_VP_SENSOR_FLAGS 0x05
+#define CPIA2_VP_SENSOR_FLAGS_404 0x01
+#define CPIA2_VP_SENSOR_FLAGS_407 0x02
+#define CPIA2_VP_SENSOR_FLAGS_409 0x04
+#define CPIA2_VP_SENSOR_FLAGS_410 0x08
+#define CPIA2_VP_SENSOR_FLAGS_500 0x10
+
+#define CPIA2_VP_SENSOR_REV 0x06
+
+#define CPIA2_VP_DEVICE_CONFIG 0x07
+#define CPIA2_VP_DEVICE_CONFIG_SERIAL_BRIDGE 0x01
+
+#define CPIA2_VP_GPIO_DIRECTION 0x08
+#define CPIA2_VP_GPIO_READ 0xFF
+#define CPIA2_VP_GPIO_WRITE 0x00
+
+#define CPIA2_VP_GPIO_DATA 0x09
+
+#define CPIA2_VP_RAM_ADDR_H 0x0A
+#define CPIA2_VP_RAM_ADDR_L 0x0B
+#define CPIA2_VP_RAM_DATA 0x0C
+
+#define CPIA2_VP_PATCH_REV 0x0F
+
+#define CPIA2_VP4_USER_MODE 0x10
+#define CPIA2_VP5_USER_MODE 0x13
+#define CPIA2_VP_USER_MODE_CIF 0x01
+#define CPIA2_VP_USER_MODE_QCIFDS 0x02
+#define CPIA2_VP_USER_MODE_QCIFPTC 0x04
+#define CPIA2_VP_USER_MODE_QVGADS 0x08
+#define CPIA2_VP_USER_MODE_QVGAPTC 0x10
+#define CPIA2_VP_USER_MODE_VGA 0x20
+
+#define CPIA2_VP4_FRAMERATE_REQUEST 0x11
+#define CPIA2_VP5_FRAMERATE_REQUEST 0x14
+#define CPIA2_VP_FRAMERATE_60 0x80
+#define CPIA2_VP_FRAMERATE_50 0x40
+#define CPIA2_VP_FRAMERATE_30 0x20
+#define CPIA2_VP_FRAMERATE_25 0x10
+#define CPIA2_VP_FRAMERATE_15 0x08
+#define CPIA2_VP_FRAMERATE_12_5 0x04
+#define CPIA2_VP_FRAMERATE_7_5 0x02
+#define CPIA2_VP_FRAMERATE_6_25 0x01
+
+#define CPIA2_VP4_USER_EFFECTS 0x12
+#define CPIA2_VP5_USER_EFFECTS 0x15
+#define CPIA2_VP_USER_EFFECTS_COLBARS 0x01
+#define CPIA2_VP_USER_EFFECTS_COLBARS_GRAD 0x02
+#define CPIA2_VP_USER_EFFECTS_MIRROR 0x04
+#define CPIA2_VP_USER_EFFECTS_FLIP 0x40 // VP5 only
+
+/* NOTE: CPIA2_VP_EXPOSURE_MODES shares the same register as VP5 User
+ * Effects */
+#define CPIA2_VP_EXPOSURE_MODES 0x15
+#define CPIA2_VP_EXPOSURE_MODES_INHIBIT_FLICKER 0x20
+#define CPIA2_VP_EXPOSURE_MODES_COMPILE_EXP 0x10
+
+#define CPIA2_VP4_EXPOSURE_TARGET 0x16 // VP4
+#define CPIA2_VP5_EXPOSURE_TARGET 0x20 // VP5
+
+#define CPIA2_VP_FLICKER_MODES 0x1B
+#define CPIA2_VP_FLICKER_MODES_50HZ 0x80
+#define CPIA2_VP_FLICKER_MODES_CUSTOM_FLT_FFREQ 0x40
+#define CPIA2_VP_FLICKER_MODES_NEVER_FLICKER 0x20
+#define CPIA2_VP_FLICKER_MODES_INHIBIT_RUB 0x10
+#define CPIA2_VP_FLICKER_MODES_ADJUST_LINE_FREQ 0x08
+#define CPIA2_VP_FLICKER_MODES_CUSTOM_INT_FFREQ 0x04
+
+#define CPIA2_VP_UMISC 0x1D
+#define CPIA2_VP_UMISC_FORCE_MONO 0x80
+#define CPIA2_VP_UMISC_FORCE_ID_MASK 0x40
+#define CPIA2_VP_UMISC_INHIBIT_AUTO_FGS 0x20
+#define CPIA2_VP_UMISC_INHIBIT_AUTO_DIMS 0x08
+#define CPIA2_VP_UMISC_OPT_FOR_SENSOR_DS 0x04
+#define CPIA2_VP_UMISC_INHIBIT_AUTO_MODE_INT 0x02
+
+#define CPIA2_VP5_ANTIFLKRSETUP 0x22 //34
+
+#define CPIA2_VP_INTERPOLATION 0x24
+#define CPIA2_VP_INTERPOLATION_EVEN_FIRST 0x40
+#define CPIA2_VP_INTERPOLATION_HJOG 0x20
+#define CPIA2_VP_INTERPOLATION_VJOG 0x10
+
+#define CPIA2_VP_GAMMA 0x25
+#define CPIA2_VP_DEFAULT_GAMMA 0x10
+
+#define CPIA2_VP_YRANGE 0x26
+
+#define CPIA2_VP_SATURATION 0x27
+
+#define CPIA2_VP5_MYBLACK_LEVEL 0x3A //58
+#define CPIA2_VP5_MCYRANGE 0x3B //59
+#define CPIA2_VP5_MYCEILING 0x3C //60
+#define CPIA2_VP5_MCUVSATURATION 0x3D //61
+
+
+#define CPIA2_VP_REHASH_VALUES 0x60
+
+
+/***
+ * Common sensor registers
+ ***/
+#define CPIA2_SENSOR_DEVICE_H 0x00
+#define CPIA2_SENSOR_DEVICE_L 0x01
+
+#define CPIA2_SENSOR_DATA_FORMAT 0x16
+#define CPIA2_SENSOR_DATA_FORMAT_HMIRROR 0x08
+#define CPIA2_SENSOR_DATA_FORMAT_VMIRROR 0x10
+
+#define CPIA2_SENSOR_CR1 0x76
+#define CPIA2_SENSOR_CR1_STAND_BY 0x01
+#define CPIA2_SENSOR_CR1_DOWN_RAMP_GEN 0x02
+#define CPIA2_SENSOR_CR1_DOWN_COLUMN_ADC 0x04
+#define CPIA2_SENSOR_CR1_DOWN_CAB_REGULATOR 0x08
+#define CPIA2_SENSOR_CR1_DOWN_AUDIO_REGULATOR 0x10
+#define CPIA2_SENSOR_CR1_DOWN_VRT_AMP 0x20
+#define CPIA2_SENSOR_CR1_DOWN_BAND_GAP 0x40
+
+#endif
diff --git a/drivers/media/video/cpia2/cpia2_usb.c b/drivers/media/video/cpia2/cpia2_usb.c
new file mode 100644
index 00000000..59c797c1
--- /dev/null
+++ b/drivers/media/video/cpia2/cpia2_usb.c
@@ -0,0 +1,915 @@
+/****************************************************************************
+ *
+ * Filename: cpia2_usb.c
+ *
+ * Copyright 2001, STMicrolectronics, Inc.
+ * Contact: steve.miller@st.com
+ *
+ * Description:
+ * This is a USB driver for CPia2 based video cameras.
+ * The infrastructure of this driver is based on the cpia usb driver by
+ * Jochen Scharrlach and Johannes Erdfeldt.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Stripped of 2.4 stuff ready for main kernel submit by
+ * Alan Cox <alan@lxorguk.ukuu.org.uk>
+ ****************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/module.h>
+
+#include "cpia2.h"
+
+static int frame_sizes[] = {
+ 0, // USBIF_CMDONLY
+ 0, // USBIF_BULK
+ 128, // USBIF_ISO_1
+ 384, // USBIF_ISO_2
+ 640, // USBIF_ISO_3
+ 768, // USBIF_ISO_4
+ 896, // USBIF_ISO_5
+ 1023, // USBIF_ISO_6
+};
+
+#define FRAMES_PER_DESC 10
+#define FRAME_SIZE_PER_DESC frame_sizes[cam->cur_alt]
+
+static void process_frame(struct camera_data *cam);
+static void cpia2_usb_complete(struct urb *urb);
+static int cpia2_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id);
+static void cpia2_usb_disconnect(struct usb_interface *intf);
+
+static void free_sbufs(struct camera_data *cam);
+static void add_APPn(struct camera_data *cam);
+static void add_COM(struct camera_data *cam);
+static int submit_urbs(struct camera_data *cam);
+static int set_alternate(struct camera_data *cam, unsigned int alt);
+static int configure_transfer_mode(struct camera_data *cam, unsigned int alt);
+
+static struct usb_device_id cpia2_id_table[] = {
+ {USB_DEVICE(0x0553, 0x0100)},
+ {USB_DEVICE(0x0553, 0x0140)},
+ {USB_DEVICE(0x0553, 0x0151)}, /* STV0676 */
+ {} /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, cpia2_id_table);
+
+static struct usb_driver cpia2_driver = {
+ .name = "cpia2",
+ .probe = cpia2_usb_probe,
+ .disconnect = cpia2_usb_disconnect,
+ .id_table = cpia2_id_table
+};
+
+
+/******************************************************************************
+ *
+ * process_frame
+ *
+ *****************************************************************************/
+static void process_frame(struct camera_data *cam)
+{
+ static int frame_count;
+
+ unsigned char *inbuff = cam->workbuff->data;
+
+ DBG("Processing frame #%d, current:%d\n",
+ cam->workbuff->num, cam->curbuff->num);
+
+ if(cam->workbuff->length > cam->workbuff->max_length)
+ cam->workbuff->max_length = cam->workbuff->length;
+
+ if ((inbuff[0] == 0xFF) && (inbuff[1] == 0xD8)) {
+ frame_count++;
+ } else {
+ cam->workbuff->status = FRAME_ERROR;
+ DBG("Start of frame not found\n");
+ return;
+ }
+
+ /***
+ * Now the output buffer should have a JPEG image in it.
+ ***/
+ if(!cam->first_image_seen) {
+ /* Always skip the first image after streaming
+ * starts. It is almost certainly corrupt. */
+ cam->first_image_seen = 1;
+ cam->workbuff->status = FRAME_EMPTY;
+ return;
+ }
+ if (cam->workbuff->length > 3) {
+ if(cam->mmapped &&
+ cam->workbuff->length < cam->workbuff->max_length) {
+ /* No junk in the buffers */
+ memset(cam->workbuff->data+cam->workbuff->length,
+ 0, cam->workbuff->max_length-
+ cam->workbuff->length);
+ }
+ cam->workbuff->max_length = cam->workbuff->length;
+ cam->workbuff->status = FRAME_READY;
+
+ if(!cam->mmapped && cam->num_frames > 2) {
+ /* During normal reading, the most recent
+ * frame will be read. If the current frame
+ * hasn't started reading yet, it will never
+ * be read, so mark it empty. If the buffer is
+ * mmapped, or we have few buffers, we need to
+ * wait for the user to free the buffer.
+ *
+ * NOTE: This is not entirely foolproof with 3
+ * buffers, but it would take an EXTREMELY
+ * overloaded system to cause problems (possible
+ * image data corruption). Basically, it would
+ * need to take more time to execute cpia2_read
+ * than it would for the camera to send
+ * cam->num_frames-2 frames before problems
+ * could occur.
+ */
+ cam->curbuff->status = FRAME_EMPTY;
+ }
+ cam->curbuff = cam->workbuff;
+ cam->workbuff = cam->workbuff->next;
+ DBG("Changed buffers, work:%d, current:%d\n",
+ cam->workbuff->num, cam->curbuff->num);
+ return;
+ } else {
+ DBG("Not enough data for an image.\n");
+ }
+
+ cam->workbuff->status = FRAME_ERROR;
+ return;
+}
+
+/******************************************************************************
+ *
+ * add_APPn
+ *
+ * Adds a user specified APPn record
+ *****************************************************************************/
+static void add_APPn(struct camera_data *cam)
+{
+ if(cam->APP_len > 0) {
+ cam->workbuff->data[cam->workbuff->length++] = 0xFF;
+ cam->workbuff->data[cam->workbuff->length++] = 0xE0+cam->APPn;
+ cam->workbuff->data[cam->workbuff->length++] = 0;
+ cam->workbuff->data[cam->workbuff->length++] = cam->APP_len+2;
+ memcpy(cam->workbuff->data+cam->workbuff->length,
+ cam->APP_data, cam->APP_len);
+ cam->workbuff->length += cam->APP_len;
+ }
+}
+
+/******************************************************************************
+ *
+ * add_COM
+ *
+ * Adds a user specified COM record
+ *****************************************************************************/
+static void add_COM(struct camera_data *cam)
+{
+ if(cam->COM_len > 0) {
+ cam->workbuff->data[cam->workbuff->length++] = 0xFF;
+ cam->workbuff->data[cam->workbuff->length++] = 0xFE;
+ cam->workbuff->data[cam->workbuff->length++] = 0;
+ cam->workbuff->data[cam->workbuff->length++] = cam->COM_len+2;
+ memcpy(cam->workbuff->data+cam->workbuff->length,
+ cam->COM_data, cam->COM_len);
+ cam->workbuff->length += cam->COM_len;
+ }
+}
+
+/******************************************************************************
+ *
+ * cpia2_usb_complete
+ *
+ * callback when incoming packet is received
+ *****************************************************************************/
+static void cpia2_usb_complete(struct urb *urb)
+{
+ int i;
+ unsigned char *cdata;
+ static int frame_ready = false;
+ struct camera_data *cam = (struct camera_data *) urb->context;
+
+ if (urb->status!=0) {
+ if (!(urb->status == -ENOENT ||
+ urb->status == -ECONNRESET ||
+ urb->status == -ESHUTDOWN))
+ {
+ DBG("urb->status = %d!\n", urb->status);
+ }
+ DBG("Stopping streaming\n");
+ return;
+ }
+
+ if (!cam->streaming || !cam->present || cam->open_count == 0) {
+ LOG("Will now stop the streaming: streaming = %d, "
+ "present=%d, open_count=%d\n",
+ cam->streaming, cam->present, cam->open_count);
+ return;
+ }
+
+ /***
+ * Packet collater
+ ***/
+ //DBG("Collating %d packets\n", urb->number_of_packets);
+ for (i = 0; i < urb->number_of_packets; i++) {
+ u16 checksum, iso_checksum;
+ int j;
+ int n = urb->iso_frame_desc[i].actual_length;
+ int st = urb->iso_frame_desc[i].status;
+
+ if(cam->workbuff->status == FRAME_READY) {
+ struct framebuf *ptr;
+ /* Try to find an available buffer */
+ DBG("workbuff full, searching\n");
+ for (ptr = cam->workbuff->next;
+ ptr != cam->workbuff;
+ ptr = ptr->next)
+ {
+ if (ptr->status == FRAME_EMPTY) {
+ ptr->status = FRAME_READING;
+ ptr->length = 0;
+ break;
+ }
+ }
+ if (ptr == cam->workbuff)
+ break; /* No READING or EMPTY buffers left */
+
+ cam->workbuff = ptr;
+ }
+
+ if (cam->workbuff->status == FRAME_EMPTY ||
+ cam->workbuff->status == FRAME_ERROR) {
+ cam->workbuff->status = FRAME_READING;
+ cam->workbuff->length = 0;
+ }
+
+ //DBG(" Packet %d length = %d, status = %d\n", i, n, st);
+ cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+
+ if (st) {
+ LOG("cpia2 data error: [%d] len=%d, status = %d\n",
+ i, n, st);
+ if(!ALLOW_CORRUPT)
+ cam->workbuff->status = FRAME_ERROR;
+ continue;
+ }
+
+ if(n<=2)
+ continue;
+
+ checksum = 0;
+ for(j=0; j<n-2; ++j)
+ checksum += cdata[j];
+ iso_checksum = cdata[j] + cdata[j+1]*256;
+ if(checksum != iso_checksum) {
+ LOG("checksum mismatch: [%d] len=%d, calculated = %x, checksum = %x\n",
+ i, n, (int)checksum, (int)iso_checksum);
+ if(!ALLOW_CORRUPT) {
+ cam->workbuff->status = FRAME_ERROR;
+ continue;
+ }
+ }
+ n -= 2;
+
+ if(cam->workbuff->status != FRAME_READING) {
+ if((0xFF == cdata[0] && 0xD8 == cdata[1]) ||
+ (0xD8 == cdata[0] && 0xFF == cdata[1] &&
+ 0 != cdata[2])) {
+ /* frame is skipped, but increment total
+ * frame count anyway */
+ cam->frame_count++;
+ }
+ DBG("workbuff not reading, status=%d\n",
+ cam->workbuff->status);
+ continue;
+ }
+
+ if (cam->frame_size < cam->workbuff->length + n) {
+ ERR("buffer overflow! length: %d, n: %d\n",
+ cam->workbuff->length, n);
+ cam->workbuff->status = FRAME_ERROR;
+ if(cam->workbuff->length > cam->workbuff->max_length)
+ cam->workbuff->max_length =
+ cam->workbuff->length;
+ continue;
+ }
+
+ if (cam->workbuff->length == 0) {
+ int data_offset;
+ if ((0xD8 == cdata[0]) && (0xFF == cdata[1])) {
+ data_offset = 1;
+ } else if((0xFF == cdata[0]) && (0xD8 == cdata[1])
+ && (0xFF == cdata[2])) {
+ data_offset = 2;
+ } else {
+ DBG("Ignoring packet, not beginning!\n");
+ continue;
+ }
+ DBG("Start of frame pattern found\n");
+ do_gettimeofday(&cam->workbuff->timestamp);
+ cam->workbuff->seq = cam->frame_count++;
+ cam->workbuff->data[0] = 0xFF;
+ cam->workbuff->data[1] = 0xD8;
+ cam->workbuff->length = 2;
+ add_APPn(cam);
+ add_COM(cam);
+ memcpy(cam->workbuff->data+cam->workbuff->length,
+ cdata+data_offset, n-data_offset);
+ cam->workbuff->length += n-data_offset;
+ } else if (cam->workbuff->length > 0) {
+ memcpy(cam->workbuff->data + cam->workbuff->length,
+ cdata, n);
+ cam->workbuff->length += n;
+ }
+
+ if ((cam->workbuff->length >= 3) &&
+ (cam->workbuff->data[cam->workbuff->length - 3] == 0xFF) &&
+ (cam->workbuff->data[cam->workbuff->length - 2] == 0xD9) &&
+ (cam->workbuff->data[cam->workbuff->length - 1] == 0xFF)) {
+ frame_ready = true;
+ cam->workbuff->data[cam->workbuff->length - 1] = 0;
+ cam->workbuff->length -= 1;
+ } else if ((cam->workbuff->length >= 2) &&
+ (cam->workbuff->data[cam->workbuff->length - 2] == 0xFF) &&
+ (cam->workbuff->data[cam->workbuff->length - 1] == 0xD9)) {
+ frame_ready = true;
+ }
+
+ if (frame_ready) {
+ DBG("Workbuff image size = %d\n",cam->workbuff->length);
+ process_frame(cam);
+
+ frame_ready = false;
+
+ if (waitqueue_active(&cam->wq_stream))
+ wake_up_interruptible(&cam->wq_stream);
+ }
+ }
+
+ if(cam->streaming) {
+ /* resubmit */
+ urb->dev = cam->dev;
+ if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0)
+ ERR("%s: usb_submit_urb ret %d!\n", __func__, i);
+ }
+}
+
+/******************************************************************************
+ *
+ * configure_transfer_mode
+ *
+ *****************************************************************************/
+static int configure_transfer_mode(struct camera_data *cam, unsigned int alt)
+{
+ static unsigned char iso_regs[8][4] = {
+ {0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00},
+ {0xB9, 0x00, 0x00, 0x7E},
+ {0xB9, 0x00, 0x01, 0x7E},
+ {0xB9, 0x00, 0x02, 0x7E},
+ {0xB9, 0x00, 0x02, 0xFE},
+ {0xB9, 0x00, 0x03, 0x7E},
+ {0xB9, 0x00, 0x03, 0xFD}
+ };
+ struct cpia2_command cmd;
+ unsigned char reg;
+
+ if(!cam->present)
+ return -ENODEV;
+
+ /***
+ * Write the isoc registers according to the alternate selected
+ ***/
+ cmd.direction = TRANSFER_WRITE;
+ cmd.buffer.block_data[0] = iso_regs[alt][0];
+ cmd.buffer.block_data[1] = iso_regs[alt][1];
+ cmd.buffer.block_data[2] = iso_regs[alt][2];
+ cmd.buffer.block_data[3] = iso_regs[alt][3];
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
+ cmd.start = CPIA2_VC_USB_ISOLIM;
+ cmd.reg_count = 4;
+ cpia2_send_command(cam, &cmd);
+
+ /***
+ * Enable relevant streams before starting polling.
+ * First read USB Stream Config Register.
+ ***/
+ cmd.direction = TRANSFER_READ;
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
+ cmd.start = CPIA2_VC_USB_STRM;
+ cmd.reg_count = 1;
+ cpia2_send_command(cam, &cmd);
+ reg = cmd.buffer.block_data[0];
+
+ /* Clear iso, bulk, and int */
+ reg &= ~(CPIA2_VC_USB_STRM_BLK_ENABLE |
+ CPIA2_VC_USB_STRM_ISO_ENABLE |
+ CPIA2_VC_USB_STRM_INT_ENABLE);
+
+ if (alt == USBIF_BULK) {
+ DBG("Enabling bulk xfer\n");
+ reg |= CPIA2_VC_USB_STRM_BLK_ENABLE; /* Enable Bulk */
+ cam->xfer_mode = XFER_BULK;
+ } else if (alt >= USBIF_ISO_1) {
+ DBG("Enabling ISOC xfer\n");
+ reg |= CPIA2_VC_USB_STRM_ISO_ENABLE;
+ cam->xfer_mode = XFER_ISOC;
+ }
+
+ cmd.buffer.block_data[0] = reg;
+ cmd.direction = TRANSFER_WRITE;
+ cmd.start = CPIA2_VC_USB_STRM;
+ cmd.reg_count = 1;
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
+ cpia2_send_command(cam, &cmd);
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * cpia2_usb_change_streaming_alternate
+ *
+ *****************************************************************************/
+int cpia2_usb_change_streaming_alternate(struct camera_data *cam,
+ unsigned int alt)
+{
+ int ret = 0;
+
+ if(alt < USBIF_ISO_1 || alt > USBIF_ISO_6)
+ return -EINVAL;
+
+ if(alt == cam->params.camera_state.stream_mode)
+ return 0;
+
+ cpia2_usb_stream_pause(cam);
+
+ configure_transfer_mode(cam, alt);
+
+ cam->params.camera_state.stream_mode = alt;
+
+ /* Reset the camera to prevent image quality degradation */
+ cpia2_reset_camera(cam);
+
+ cpia2_usb_stream_resume(cam);
+
+ return ret;
+}
+
+/******************************************************************************
+ *
+ * set_alternate
+ *
+ *****************************************************************************/
+static int set_alternate(struct camera_data *cam, unsigned int alt)
+{
+ int ret = 0;
+
+ if(alt == cam->cur_alt)
+ return 0;
+
+ if (cam->cur_alt != USBIF_CMDONLY) {
+ DBG("Changing from alt %d to %d\n", cam->cur_alt, USBIF_CMDONLY);
+ ret = usb_set_interface(cam->dev, cam->iface, USBIF_CMDONLY);
+ if (ret != 0)
+ return ret;
+ }
+ if (alt != USBIF_CMDONLY) {
+ DBG("Changing from alt %d to %d\n", USBIF_CMDONLY, alt);
+ ret = usb_set_interface(cam->dev, cam->iface, alt);
+ if (ret != 0)
+ return ret;
+ }
+
+ cam->old_alt = cam->cur_alt;
+ cam->cur_alt = alt;
+
+ return ret;
+}
+
+/******************************************************************************
+ *
+ * free_sbufs
+ *
+ * Free all cam->sbuf[]. All non-NULL .data and .urb members that are non-NULL
+ * are assumed to be allocated. Non-NULL .urb members are also assumed to be
+ * submitted (and must therefore be killed before they are freed).
+ *****************************************************************************/
+static void free_sbufs(struct camera_data *cam)
+{
+ int i;
+
+ for (i = 0; i < NUM_SBUF; i++) {
+ if(cam->sbuf[i].urb) {
+ usb_kill_urb(cam->sbuf[i].urb);
+ usb_free_urb(cam->sbuf[i].urb);
+ cam->sbuf[i].urb = NULL;
+ }
+ if(cam->sbuf[i].data) {
+ kfree(cam->sbuf[i].data);
+ cam->sbuf[i].data = NULL;
+ }
+ }
+}
+
+/*******
+* Convenience functions
+*******/
+/****************************************************************************
+ *
+ * write_packet
+ *
+ ***************************************************************************/
+static int write_packet(struct usb_device *udev,
+ u8 request, u8 * registers, u16 start, size_t size)
+{
+ if (!registers || size <= 0)
+ return -EINVAL;
+
+ return usb_control_msg(udev,
+ usb_sndctrlpipe(udev, 0),
+ request,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ start, /* value */
+ 0, /* index */
+ registers, /* buffer */
+ size,
+ HZ);
+}
+
+/****************************************************************************
+ *
+ * read_packet
+ *
+ ***************************************************************************/
+static int read_packet(struct usb_device *udev,
+ u8 request, u8 * registers, u16 start, size_t size)
+{
+ if (!registers || size <= 0)
+ return -EINVAL;
+
+ return usb_control_msg(udev,
+ usb_rcvctrlpipe(udev, 0),
+ request,
+ USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_DEVICE,
+ start, /* value */
+ 0, /* index */
+ registers, /* buffer */
+ size,
+ HZ);
+}
+
+/******************************************************************************
+ *
+ * cpia2_usb_transfer_cmd
+ *
+ *****************************************************************************/
+int cpia2_usb_transfer_cmd(struct camera_data *cam,
+ void *registers,
+ u8 request, u8 start, u8 count, u8 direction)
+{
+ int err = 0;
+ struct usb_device *udev = cam->dev;
+
+ if (!udev) {
+ ERR("%s: Internal driver error: udev is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!registers) {
+ ERR("%s: Internal driver error: register array is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (direction == TRANSFER_READ) {
+ err = read_packet(udev, request, (u8 *)registers, start, count);
+ if (err > 0)
+ err = 0;
+ } else if (direction == TRANSFER_WRITE) {
+ err =write_packet(udev, request, (u8 *)registers, start, count);
+ if (err < 0) {
+ LOG("Control message failed, err val = %d\n", err);
+ LOG("Message: request = 0x%0X, start = 0x%0X\n",
+ request, start);
+ LOG("Message: count = %d, register[0] = 0x%0X\n",
+ count, ((unsigned char *) registers)[0]);
+ } else
+ err=0;
+ } else {
+ LOG("Unexpected first byte of direction: %d\n",
+ direction);
+ return -EINVAL;
+ }
+
+ if(err != 0)
+ LOG("Unexpected error: %d\n", err);
+ return err;
+}
+
+
+/******************************************************************************
+ *
+ * submit_urbs
+ *
+ *****************************************************************************/
+static int submit_urbs(struct camera_data *cam)
+{
+ struct urb *urb;
+ int fx, err, i, j;
+
+ for(i=0; i<NUM_SBUF; ++i) {
+ if (cam->sbuf[i].data)
+ continue;
+ cam->sbuf[i].data =
+ kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL);
+ if (!cam->sbuf[i].data) {
+ while (--i >= 0) {
+ kfree(cam->sbuf[i].data);
+ cam->sbuf[i].data = NULL;
+ }
+ return -ENOMEM;
+ }
+ }
+
+ /* We double buffer the Isoc lists, and also know the polling
+ * interval is every frame (1 == (1 << (bInterval -1))).
+ */
+ for(i=0; i<NUM_SBUF; ++i) {
+ if(cam->sbuf[i].urb) {
+ continue;
+ }
+ urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL);
+ if (!urb) {
+ ERR("%s: usb_alloc_urb error!\n", __func__);
+ for (j = 0; j < i; j++)
+ usb_free_urb(cam->sbuf[j].urb);
+ return -ENOMEM;
+ }
+
+ cam->sbuf[i].urb = urb;
+ urb->dev = cam->dev;
+ urb->context = cam;
+ urb->pipe = usb_rcvisocpipe(cam->dev, 1 /*ISOC endpoint*/);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->transfer_buffer = cam->sbuf[i].data;
+ urb->complete = cpia2_usb_complete;
+ urb->number_of_packets = FRAMES_PER_DESC;
+ urb->interval = 1;
+ urb->transfer_buffer_length =
+ FRAME_SIZE_PER_DESC * FRAMES_PER_DESC;
+
+ for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
+ urb->iso_frame_desc[fx].offset =
+ FRAME_SIZE_PER_DESC * fx;
+ urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC;
+ }
+ }
+
+
+ /* Queue the ISO urbs, and resubmit in the completion handler */
+ for(i=0; i<NUM_SBUF; ++i) {
+ err = usb_submit_urb(cam->sbuf[i].urb, GFP_KERNEL);
+ if (err) {
+ ERR("usb_submit_urb[%d]() = %d\n", i, err);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * cpia2_usb_stream_start
+ *
+ *****************************************************************************/
+int cpia2_usb_stream_start(struct camera_data *cam, unsigned int alternate)
+{
+ int ret;
+ int old_alt;
+
+ if(cam->streaming)
+ return 0;
+
+ if (cam->flush) {
+ int i;
+ DBG("Flushing buffers\n");
+ for(i=0; i<cam->num_frames; ++i) {
+ cam->buffers[i].status = FRAME_EMPTY;
+ cam->buffers[i].length = 0;
+ }
+ cam->curbuff = &cam->buffers[0];
+ cam->workbuff = cam->curbuff->next;
+ cam->flush = false;
+ }
+
+ old_alt = cam->params.camera_state.stream_mode;
+ cam->params.camera_state.stream_mode = 0;
+ ret = cpia2_usb_change_streaming_alternate(cam, alternate);
+ if (ret < 0) {
+ int ret2;
+ ERR("cpia2_usb_change_streaming_alternate() = %d!\n", ret);
+ cam->params.camera_state.stream_mode = old_alt;
+ ret2 = set_alternate(cam, USBIF_CMDONLY);
+ if (ret2 < 0) {
+ ERR("cpia2_usb_change_streaming_alternate(%d) =%d has already "
+ "failed. Then tried to call "
+ "set_alternate(USBIF_CMDONLY) = %d.\n",
+ alternate, ret, ret2);
+ }
+ } else {
+ cam->frame_count = 0;
+ cam->streaming = 1;
+ ret = cpia2_usb_stream_resume(cam);
+ }
+ return ret;
+}
+
+/******************************************************************************
+ *
+ * cpia2_usb_stream_pause
+ *
+ *****************************************************************************/
+int cpia2_usb_stream_pause(struct camera_data *cam)
+{
+ int ret = 0;
+ if(cam->streaming) {
+ ret = set_alternate(cam, USBIF_CMDONLY);
+ free_sbufs(cam);
+ }
+ return ret;
+}
+
+/******************************************************************************
+ *
+ * cpia2_usb_stream_resume
+ *
+ *****************************************************************************/
+int cpia2_usb_stream_resume(struct camera_data *cam)
+{
+ int ret = 0;
+ if(cam->streaming) {
+ cam->first_image_seen = 0;
+ ret = set_alternate(cam, cam->params.camera_state.stream_mode);
+ if(ret == 0) {
+ ret = submit_urbs(cam);
+ }
+ }
+ return ret;
+}
+
+/******************************************************************************
+ *
+ * cpia2_usb_stream_stop
+ *
+ *****************************************************************************/
+int cpia2_usb_stream_stop(struct camera_data *cam)
+{
+ int ret;
+ ret = cpia2_usb_stream_pause(cam);
+ cam->streaming = 0;
+ configure_transfer_mode(cam, 0);
+ return ret;
+}
+
+/******************************************************************************
+ *
+ * cpia2_usb_probe
+ *
+ * Probe and initialize.
+ *****************************************************************************/
+static int cpia2_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct usb_interface_descriptor *interface;
+ struct camera_data *cam;
+ int ret;
+
+ /* A multi-config CPiA2 camera? */
+ if (udev->descriptor.bNumConfigurations != 1)
+ return -ENODEV;
+ interface = &intf->cur_altsetting->desc;
+
+ /* If we get to this point, we found a CPiA2 camera */
+ LOG("CPiA2 USB camera found\n");
+
+ if((cam = cpia2_init_camera_struct()) == NULL)
+ return -ENOMEM;
+
+ cam->dev = udev;
+ cam->iface = interface->bInterfaceNumber;
+
+ ret = set_alternate(cam, USBIF_CMDONLY);
+ if (ret < 0) {
+ ERR("%s: usb_set_interface error (ret = %d)\n", __func__, ret);
+ kfree(cam);
+ return ret;
+ }
+
+ if ((ret = cpia2_register_camera(cam)) < 0) {
+ ERR("%s: Failed to register cpia2 camera (ret = %d)\n", __func__, ret);
+ kfree(cam);
+ return ret;
+ }
+
+
+ if((ret = cpia2_init_camera(cam)) < 0) {
+ ERR("%s: failed to initialize cpia2 camera (ret = %d)\n", __func__, ret);
+ cpia2_unregister_camera(cam);
+ kfree(cam);
+ return ret;
+ }
+ LOG(" CPiA Version: %d.%02d (%d.%d)\n",
+ cam->params.version.firmware_revision_hi,
+ cam->params.version.firmware_revision_lo,
+ cam->params.version.asic_id,
+ cam->params.version.asic_rev);
+ LOG(" CPiA PnP-ID: %04x:%04x:%04x\n",
+ cam->params.pnp_id.vendor,
+ cam->params.pnp_id.product,
+ cam->params.pnp_id.device_revision);
+ LOG(" SensorID: %d.(version %d)\n",
+ cam->params.version.sensor_flags,
+ cam->params.version.sensor_rev);
+
+ usb_set_intfdata(intf, cam);
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * cpia2_disconnect
+ *
+ *****************************************************************************/
+static void cpia2_usb_disconnect(struct usb_interface *intf)
+{
+ struct camera_data *cam = usb_get_intfdata(intf);
+ usb_set_intfdata(intf, NULL);
+ cam->present = 0;
+
+ DBG("Stopping stream\n");
+ cpia2_usb_stream_stop(cam);
+
+ DBG("Unregistering camera\n");
+ cpia2_unregister_camera(cam);
+
+ if(cam->buffers) {
+ DBG("Wakeup waiting processes\n");
+ cam->curbuff->status = FRAME_READY;
+ cam->curbuff->length = 0;
+ if (waitqueue_active(&cam->wq_stream))
+ wake_up_interruptible(&cam->wq_stream);
+ }
+
+ DBG("Releasing interface\n");
+ usb_driver_release_interface(&cpia2_driver, intf);
+
+ if (cam->open_count == 0) {
+ DBG("Freeing camera structure\n");
+ kfree(cam);
+ }
+
+ LOG("CPiA2 camera disconnected.\n");
+}
+
+
+/******************************************************************************
+ *
+ * usb_cpia2_init
+ *
+ *****************************************************************************/
+int cpia2_usb_init(void)
+{
+ return usb_register(&cpia2_driver);
+}
+
+/******************************************************************************
+ *
+ * usb_cpia_cleanup
+ *
+ *****************************************************************************/
+void cpia2_usb_cleanup(void)
+{
+ schedule_timeout(2 * HZ);
+ usb_deregister(&cpia2_driver);
+}
diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c
new file mode 100644
index 00000000..077eb1db
--- /dev/null
+++ b/drivers/media/video/cpia2/cpia2_v4l.c
@@ -0,0 +1,1578 @@
+/****************************************************************************
+ *
+ * Filename: cpia2_v4l.c
+ *
+ * Copyright 2001, STMicrolectronics, Inc.
+ * Contact: steve.miller@st.com
+ * Copyright 2001,2005, Scott J. Bertin <scottbertin@yahoo.com>
+ *
+ * Description:
+ * This is a USB driver for CPia2 based video cameras.
+ * The infrastructure of this driver is based on the cpia usb driver by
+ * Jochen Scharrlach and Johannes Erdfeldt.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Stripped of 2.4 stuff ready for main kernel submit by
+ * Alan Cox <alan@lxorguk.ukuu.org.uk>
+ ****************************************************************************/
+
+#define CPIA_VERSION "3.0.1"
+
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/videodev2.h>
+#include <linux/stringify.h>
+#include <media/v4l2-ioctl.h>
+
+#include "cpia2.h"
+#include "cpia2dev.h"
+
+static int video_nr = -1;
+module_param(video_nr, int, 0);
+MODULE_PARM_DESC(video_nr,"video device to register (0=/dev/video0, etc)");
+
+static int buffer_size = 68*1024;
+module_param(buffer_size, int, 0);
+MODULE_PARM_DESC(buffer_size, "Size for each frame buffer in bytes (default 68k)");
+
+static int num_buffers = 3;
+module_param(num_buffers, int, 0);
+MODULE_PARM_DESC(num_buffers, "Number of frame buffers (1-"
+ __stringify(VIDEO_MAX_FRAME) ", default 3)");
+
+static int alternate = DEFAULT_ALT;
+module_param(alternate, int, 0);
+MODULE_PARM_DESC(alternate, "USB Alternate (" __stringify(USBIF_ISO_1) "-"
+ __stringify(USBIF_ISO_6) ", default "
+ __stringify(DEFAULT_ALT) ")");
+
+static int flicker_freq = 60;
+module_param(flicker_freq, int, 0);
+MODULE_PARM_DESC(flicker_freq, "Flicker frequency (" __stringify(50) "or"
+ __stringify(60) ", default "
+ __stringify(60) ")");
+
+static int flicker_mode = NEVER_FLICKER;
+module_param(flicker_mode, int, 0);
+MODULE_PARM_DESC(flicker_mode,
+ "Flicker supression (" __stringify(NEVER_FLICKER) "or"
+ __stringify(ANTI_FLICKER_ON) ", default "
+ __stringify(NEVER_FLICKER) ")");
+
+MODULE_AUTHOR("Steve Miller (STMicroelectronics) <steve.miller@st.com>");
+MODULE_DESCRIPTION("V4L-driver for STMicroelectronics CPiA2 based cameras");
+MODULE_SUPPORTED_DEVICE("video");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(CPIA_VERSION);
+
+#define ABOUT "V4L-Driver for Vision CPiA2 based cameras"
+
+struct control_menu_info {
+ int value;
+ char name[32];
+};
+
+static struct control_menu_info framerate_controls[] =
+{
+ { CPIA2_VP_FRAMERATE_6_25, "6.25 fps" },
+ { CPIA2_VP_FRAMERATE_7_5, "7.5 fps" },
+ { CPIA2_VP_FRAMERATE_12_5, "12.5 fps" },
+ { CPIA2_VP_FRAMERATE_15, "15 fps" },
+ { CPIA2_VP_FRAMERATE_25, "25 fps" },
+ { CPIA2_VP_FRAMERATE_30, "30 fps" },
+};
+#define NUM_FRAMERATE_CONTROLS (ARRAY_SIZE(framerate_controls))
+
+static struct control_menu_info flicker_controls[] =
+{
+ { NEVER_FLICKER, "Off" },
+ { FLICKER_50, "50 Hz" },
+ { FLICKER_60, "60 Hz" },
+};
+#define NUM_FLICKER_CONTROLS (ARRAY_SIZE(flicker_controls))
+
+static struct control_menu_info lights_controls[] =
+{
+ { 0, "Off" },
+ { 64, "Top" },
+ { 128, "Bottom" },
+ { 192, "Both" },
+};
+#define NUM_LIGHTS_CONTROLS (ARRAY_SIZE(lights_controls))
+#define GPIO_LIGHTS_MASK 192
+
+static struct v4l2_queryctrl controls[] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = DEFAULT_BRIGHTNESS,
+ },
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = DEFAULT_CONTRAST,
+ },
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = DEFAULT_SATURATION,
+ },
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mirror Horizontally",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Flip Vertically",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = CPIA2_CID_TARGET_KB,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Target KB",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = DEFAULT_TARGET_KB,
+ },
+ {
+ .id = CPIA2_CID_GPIO,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "GPIO",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = CPIA2_CID_FLICKER_MODE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Flicker Reduction",
+ .minimum = 0,
+ .maximum = NUM_FLICKER_CONTROLS-1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = CPIA2_CID_FRAMERATE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Framerate",
+ .minimum = 0,
+ .maximum = NUM_FRAMERATE_CONTROLS-1,
+ .step = 1,
+ .default_value = NUM_FRAMERATE_CONTROLS-1,
+ },
+ {
+ .id = CPIA2_CID_USB_ALT,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "USB Alternate",
+ .minimum = USBIF_ISO_1,
+ .maximum = USBIF_ISO_6,
+ .step = 1,
+ .default_value = DEFAULT_ALT,
+ },
+ {
+ .id = CPIA2_CID_LIGHTS,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Lights",
+ .minimum = 0,
+ .maximum = NUM_LIGHTS_CONTROLS-1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = CPIA2_CID_RESET_CAMERA,
+ .type = V4L2_CTRL_TYPE_BUTTON,
+ .name = "Reset Camera",
+ .minimum = 0,
+ .maximum = 0,
+ .step = 0,
+ .default_value = 0,
+ },
+};
+#define NUM_CONTROLS (ARRAY_SIZE(controls))
+
+
+/******************************************************************************
+ *
+ * cpia2_open
+ *
+ *****************************************************************************/
+static int cpia2_open(struct file *file)
+{
+ struct camera_data *cam = video_drvdata(file);
+ struct cpia2_fh *fh;
+
+ if (!cam) {
+ ERR("Internal error, camera_data not found!\n");
+ return -ENODEV;
+ }
+
+ if (!cam->present)
+ return -ENODEV;
+
+ if (cam->open_count == 0) {
+ if (cpia2_allocate_buffers(cam))
+ return -ENOMEM;
+
+ /* reset the camera */
+ if (cpia2_reset_camera(cam) < 0)
+ return -EIO;
+
+ cam->APP_len = 0;
+ cam->COM_len = 0;
+ }
+
+ fh = kmalloc(sizeof(*fh), GFP_KERNEL);
+ if (!fh)
+ return -ENOMEM;
+ file->private_data = fh;
+ fh->prio = V4L2_PRIORITY_UNSET;
+ v4l2_prio_open(&cam->prio, &fh->prio);
+ fh->mmapped = 0;
+
+ ++cam->open_count;
+
+ cpia2_dbg_dump_registers(cam);
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * cpia2_close
+ *
+ *****************************************************************************/
+static int cpia2_close(struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ struct camera_data *cam = video_get_drvdata(dev);
+ struct cpia2_fh *fh = file->private_data;
+
+ if (cam->present &&
+ (cam->open_count == 1 || fh->prio == V4L2_PRIORITY_RECORD)) {
+ cpia2_usb_stream_stop(cam);
+
+ if (cam->open_count == 1) {
+ /* save camera state for later open */
+ cpia2_save_camera_state(cam);
+
+ cpia2_set_low_power(cam);
+ cpia2_free_buffers(cam);
+ }
+ }
+
+ if (fh->mmapped)
+ cam->mmapped = 0;
+ v4l2_prio_close(&cam->prio, fh->prio);
+ file->private_data = NULL;
+ kfree(fh);
+
+ if (--cam->open_count == 0) {
+ cpia2_free_buffers(cam);
+ if (!cam->present) {
+ video_unregister_device(dev);
+ kfree(cam);
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * cpia2_v4l_read
+ *
+ *****************************************************************************/
+static ssize_t cpia2_v4l_read(struct file *file, char __user *buf, size_t count,
+ loff_t *off)
+{
+ struct camera_data *cam = video_drvdata(file);
+ int noblock = file->f_flags&O_NONBLOCK;
+
+ struct cpia2_fh *fh = file->private_data;
+
+ if(!cam)
+ return -EINVAL;
+
+ /* Priority check */
+ if(fh->prio != V4L2_PRIORITY_RECORD) {
+ return -EBUSY;
+ }
+
+ return cpia2_read(cam, buf, count, noblock);
+}
+
+
+/******************************************************************************
+ *
+ * cpia2_v4l_poll
+ *
+ *****************************************************************************/
+static unsigned int cpia2_v4l_poll(struct file *filp, struct poll_table_struct *wait)
+{
+ struct camera_data *cam = video_drvdata(filp);
+ struct cpia2_fh *fh = filp->private_data;
+
+ if(!cam)
+ return POLLERR;
+
+ /* Priority check */
+ if(fh->prio != V4L2_PRIORITY_RECORD) {
+ return POLLERR;
+ }
+
+ return cpia2_poll(cam, filp, wait);
+}
+
+
+static int sync(struct camera_data *cam, int frame_nr)
+{
+ struct framebuf *frame = &cam->buffers[frame_nr];
+
+ while (1) {
+ if (frame->status == FRAME_READY)
+ return 0;
+
+ if (!cam->streaming) {
+ frame->status = FRAME_READY;
+ frame->length = 0;
+ return 0;
+ }
+
+ mutex_unlock(&cam->v4l2_lock);
+ wait_event_interruptible(cam->wq_stream,
+ !cam->streaming ||
+ frame->status == FRAME_READY);
+ mutex_lock(&cam->v4l2_lock);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ if(!cam->present)
+ return -ENOTTY;
+ }
+}
+
+/******************************************************************************
+ *
+ * ioctl_set_gpio
+ *
+ *****************************************************************************/
+
+static long cpia2_default(struct file *file, void *fh, bool valid_prio,
+ int cmd, void *arg)
+{
+ struct camera_data *cam = video_drvdata(file);
+ __u32 gpio_val;
+
+ if (cmd != CPIA2_CID_GPIO)
+ return -EINVAL;
+
+ gpio_val = *(__u32*) arg;
+
+ if (gpio_val &~ 0xFFU)
+ return -EINVAL;
+
+ return cpia2_set_gpio(cam, (unsigned char)gpio_val);
+}
+
+/******************************************************************************
+ *
+ * ioctl_querycap
+ *
+ * V4L2 device capabilities
+ *
+ *****************************************************************************/
+
+static int cpia2_querycap(struct file *file, void *fh, struct v4l2_capability *vc)
+{
+ struct camera_data *cam = video_drvdata(file);
+
+ strcpy(vc->driver, "cpia2");
+
+ if (cam->params.pnp_id.product == 0x151)
+ strcpy(vc->card, "QX5 Microscope");
+ else
+ strcpy(vc->card, "CPiA2 Camera");
+ switch (cam->params.pnp_id.device_type) {
+ case DEVICE_STV_672:
+ strcat(vc->card, " (672/");
+ break;
+ case DEVICE_STV_676:
+ strcat(vc->card, " (676/");
+ break;
+ default:
+ strcat(vc->card, " (XXX/");
+ break;
+ }
+ switch (cam->params.version.sensor_flags) {
+ case CPIA2_VP_SENSOR_FLAGS_404:
+ strcat(vc->card, "404)");
+ break;
+ case CPIA2_VP_SENSOR_FLAGS_407:
+ strcat(vc->card, "407)");
+ break;
+ case CPIA2_VP_SENSOR_FLAGS_409:
+ strcat(vc->card, "409)");
+ break;
+ case CPIA2_VP_SENSOR_FLAGS_410:
+ strcat(vc->card, "410)");
+ break;
+ case CPIA2_VP_SENSOR_FLAGS_500:
+ strcat(vc->card, "500)");
+ break;
+ default:
+ strcat(vc->card, "XXX)");
+ break;
+ }
+
+ if (usb_make_path(cam->dev, vc->bus_info, sizeof(vc->bus_info)) <0)
+ memset(vc->bus_info,0, sizeof(vc->bus_info));
+
+ vc->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * ioctl_input
+ *
+ * V4L2 input get/set/enumerate
+ *
+ *****************************************************************************/
+
+static int cpia2_enum_input(struct file *file, void *fh, struct v4l2_input *i)
+{
+ if (i->index)
+ return -EINVAL;
+ strcpy(i->name, "Camera");
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ return 0;
+}
+
+static int cpia2_g_input(struct file *file, void *fh, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int cpia2_s_input(struct file *file, void *fh, unsigned int i)
+{
+ return i ? -EINVAL : 0;
+}
+
+/******************************************************************************
+ *
+ * ioctl_enum_fmt
+ *
+ * V4L2 format enumerate
+ *
+ *****************************************************************************/
+
+static int cpia2_enum_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ int index = f->index;
+
+ if (index < 0 || index > 1)
+ return -EINVAL;
+
+ memset(f, 0, sizeof(*f));
+ f->index = index;
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ f->flags = V4L2_FMT_FLAG_COMPRESSED;
+ switch(index) {
+ case 0:
+ strcpy(f->description, "MJPEG");
+ f->pixelformat = V4L2_PIX_FMT_MJPEG;
+ break;
+ case 1:
+ strcpy(f->description, "JPEG");
+ f->pixelformat = V4L2_PIX_FMT_JPEG;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * ioctl_try_fmt
+ *
+ * V4L2 format try
+ *
+ *****************************************************************************/
+
+static int cpia2_try_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct camera_data *cam = video_drvdata(file);
+
+ if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG &&
+ f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG)
+ return -EINVAL;
+
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage = cam->frame_size;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
+ f->fmt.pix.priv = 0;
+
+ switch (cpia2_match_video_size(f->fmt.pix.width, f->fmt.pix.height)) {
+ case VIDEOSIZE_VGA:
+ f->fmt.pix.width = 640;
+ f->fmt.pix.height = 480;
+ break;
+ case VIDEOSIZE_CIF:
+ f->fmt.pix.width = 352;
+ f->fmt.pix.height = 288;
+ break;
+ case VIDEOSIZE_QVGA:
+ f->fmt.pix.width = 320;
+ f->fmt.pix.height = 240;
+ break;
+ case VIDEOSIZE_288_216:
+ f->fmt.pix.width = 288;
+ f->fmt.pix.height = 216;
+ break;
+ case VIDEOSIZE_256_192:
+ f->fmt.pix.width = 256;
+ f->fmt.pix.height = 192;
+ break;
+ case VIDEOSIZE_224_168:
+ f->fmt.pix.width = 224;
+ f->fmt.pix.height = 168;
+ break;
+ case VIDEOSIZE_192_144:
+ f->fmt.pix.width = 192;
+ f->fmt.pix.height = 144;
+ break;
+ case VIDEOSIZE_QCIF:
+ default:
+ f->fmt.pix.width = 176;
+ f->fmt.pix.height = 144;
+ break;
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * ioctl_set_fmt
+ *
+ * V4L2 format set
+ *
+ *****************************************************************************/
+
+static int cpia2_s_fmt_vid_cap(struct file *file, void *_fh,
+ struct v4l2_format *f)
+{
+ struct camera_data *cam = video_drvdata(file);
+ struct cpia2_fh *fh = _fh;
+ int err, frame;
+
+ err = v4l2_prio_check(&cam->prio, fh->prio);
+ if (err)
+ return err;
+ err = cpia2_try_fmt_vid_cap(file, _fh, f);
+ if(err != 0)
+ return err;
+
+ /* Ensure that only this process can change the format. */
+ err = v4l2_prio_change(&cam->prio, &fh->prio, V4L2_PRIORITY_RECORD);
+ if(err != 0) {
+ return err;
+ }
+
+ cam->pixelformat = f->fmt.pix.pixelformat;
+
+ /* NOTE: This should be set to 1 for MJPEG, but some apps don't handle
+ * the missing Huffman table properly. */
+ cam->params.compression.inhibit_htables = 0;
+ /*f->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG;*/
+
+ /* we set the video window to something smaller or equal to what
+ * is requested by the user???
+ */
+ DBG("Requested width = %d, height = %d\n",
+ f->fmt.pix.width, f->fmt.pix.height);
+ if (f->fmt.pix.width != cam->width ||
+ f->fmt.pix.height != cam->height) {
+ cam->width = f->fmt.pix.width;
+ cam->height = f->fmt.pix.height;
+ cam->params.roi.width = f->fmt.pix.width;
+ cam->params.roi.height = f->fmt.pix.height;
+ cpia2_set_format(cam);
+ }
+
+ for (frame = 0; frame < cam->num_frames; ++frame) {
+ if (cam->buffers[frame].status == FRAME_READING)
+ if ((err = sync(cam, frame)) < 0)
+ return err;
+
+ cam->buffers[frame].status = FRAME_EMPTY;
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * ioctl_get_fmt
+ *
+ * V4L2 format get
+ *
+ *****************************************************************************/
+
+static int cpia2_g_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct camera_data *cam = video_drvdata(file);
+
+ f->fmt.pix.width = cam->width;
+ f->fmt.pix.height = cam->height;
+ f->fmt.pix.pixelformat = cam->pixelformat;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage = cam->frame_size;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
+ f->fmt.pix.priv = 0;
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * ioctl_cropcap
+ *
+ * V4L2 query cropping capabilities
+ * NOTE: cropping is currently disabled
+ *
+ *****************************************************************************/
+
+static int cpia2_cropcap(struct file *file, void *fh, struct v4l2_cropcap *c)
+{
+ struct camera_data *cam = video_drvdata(file);
+
+ if (c->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ c->bounds.left = 0;
+ c->bounds.top = 0;
+ c->bounds.width = cam->width;
+ c->bounds.height = cam->height;
+ c->defrect.left = 0;
+ c->defrect.top = 0;
+ c->defrect.width = cam->width;
+ c->defrect.height = cam->height;
+ c->pixelaspect.numerator = 1;
+ c->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * ioctl_queryctrl
+ *
+ * V4L2 query possible control variables
+ *
+ *****************************************************************************/
+
+static int cpia2_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *c)
+{
+ struct camera_data *cam = video_drvdata(file);
+ int i;
+
+ for(i=0; i<NUM_CONTROLS; ++i) {
+ if(c->id == controls[i].id) {
+ memcpy(c, controls+i, sizeof(*c));
+ break;
+ }
+ }
+
+ if(i == NUM_CONTROLS)
+ return -EINVAL;
+
+ /* Some devices have additional limitations */
+ switch(c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ /***
+ * Don't let the register be set to zero - bug in VP4
+ * flash of full brightness
+ ***/
+ if (cam->params.pnp_id.device_type == DEVICE_STV_672)
+ c->minimum = 1;
+ break;
+ case V4L2_CID_VFLIP:
+ // VP5 Only
+ if(cam->params.pnp_id.device_type == DEVICE_STV_672)
+ c->flags |= V4L2_CTRL_FLAG_DISABLED;
+ break;
+ case CPIA2_CID_FRAMERATE:
+ if(cam->params.pnp_id.device_type == DEVICE_STV_672 &&
+ cam->params.version.sensor_flags==CPIA2_VP_SENSOR_FLAGS_500){
+ // Maximum 15fps
+ for(i=0; i<c->maximum; ++i) {
+ if(framerate_controls[i].value ==
+ CPIA2_VP_FRAMERATE_15) {
+ c->maximum = i;
+ c->default_value = i;
+ }
+ }
+ }
+ break;
+ case CPIA2_CID_FLICKER_MODE:
+ // Flicker control only valid for 672.
+ if(cam->params.pnp_id.device_type != DEVICE_STV_672)
+ c->flags |= V4L2_CTRL_FLAG_DISABLED;
+ break;
+ case CPIA2_CID_LIGHTS:
+ // Light control only valid for the QX5 Microscope.
+ if(cam->params.pnp_id.product != 0x151)
+ c->flags |= V4L2_CTRL_FLAG_DISABLED;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * ioctl_querymenu
+ *
+ * V4L2 query possible control variables
+ *
+ *****************************************************************************/
+
+static int cpia2_querymenu(struct file *file, void *fh, struct v4l2_querymenu *m)
+{
+ struct camera_data *cam = video_drvdata(file);
+
+ switch(m->id) {
+ case CPIA2_CID_FLICKER_MODE:
+ if (m->index >= NUM_FLICKER_CONTROLS)
+ return -EINVAL;
+
+ strcpy(m->name, flicker_controls[m->index].name);
+ break;
+ case CPIA2_CID_FRAMERATE:
+ {
+ int maximum = NUM_FRAMERATE_CONTROLS - 1;
+ if(cam->params.pnp_id.device_type == DEVICE_STV_672 &&
+ cam->params.version.sensor_flags==CPIA2_VP_SENSOR_FLAGS_500){
+ // Maximum 15fps
+ int i;
+ for(i=0; i<maximum; ++i) {
+ if(framerate_controls[i].value ==
+ CPIA2_VP_FRAMERATE_15)
+ maximum = i;
+ }
+ }
+ if (m->index > maximum)
+ return -EINVAL;
+
+ strcpy(m->name, framerate_controls[m->index].name);
+ break;
+ }
+ case CPIA2_CID_LIGHTS:
+ if (m->index >= NUM_LIGHTS_CONTROLS)
+ return -EINVAL;
+
+ strcpy(m->name, lights_controls[m->index].name);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * ioctl_g_ctrl
+ *
+ * V4L2 get the value of a control variable
+ *
+ *****************************************************************************/
+
+static int cpia2_g_ctrl(struct file *file, void *fh, struct v4l2_control *c)
+{
+ struct camera_data *cam = video_drvdata(file);
+
+ switch(c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ cpia2_do_command(cam, CPIA2_CMD_GET_VP_BRIGHTNESS,
+ TRANSFER_READ, 0);
+ c->value = cam->params.color_params.brightness;
+ break;
+ case V4L2_CID_CONTRAST:
+ cpia2_do_command(cam, CPIA2_CMD_GET_CONTRAST,
+ TRANSFER_READ, 0);
+ c->value = cam->params.color_params.contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ cpia2_do_command(cam, CPIA2_CMD_GET_VP_SATURATION,
+ TRANSFER_READ, 0);
+ c->value = cam->params.color_params.saturation;
+ break;
+ case V4L2_CID_HFLIP:
+ cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS,
+ TRANSFER_READ, 0);
+ c->value = (cam->params.vp_params.user_effects &
+ CPIA2_VP_USER_EFFECTS_MIRROR) != 0;
+ break;
+ case V4L2_CID_VFLIP:
+ cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS,
+ TRANSFER_READ, 0);
+ c->value = (cam->params.vp_params.user_effects &
+ CPIA2_VP_USER_EFFECTS_FLIP) != 0;
+ break;
+ case CPIA2_CID_TARGET_KB:
+ c->value = cam->params.vc_params.target_kb;
+ break;
+ case CPIA2_CID_GPIO:
+ cpia2_do_command(cam, CPIA2_CMD_GET_VP_GPIO_DATA,
+ TRANSFER_READ, 0);
+ c->value = cam->params.vp_params.gpio_data;
+ break;
+ case CPIA2_CID_FLICKER_MODE:
+ {
+ int i, mode;
+ cpia2_do_command(cam, CPIA2_CMD_GET_FLICKER_MODES,
+ TRANSFER_READ, 0);
+ if(cam->params.flicker_control.cam_register &
+ CPIA2_VP_FLICKER_MODES_NEVER_FLICKER) {
+ mode = NEVER_FLICKER;
+ } else {
+ if(cam->params.flicker_control.cam_register &
+ CPIA2_VP_FLICKER_MODES_50HZ) {
+ mode = FLICKER_50;
+ } else {
+ mode = FLICKER_60;
+ }
+ }
+ for(i=0; i<NUM_FLICKER_CONTROLS; i++) {
+ if(flicker_controls[i].value == mode) {
+ c->value = i;
+ break;
+ }
+ }
+ if(i == NUM_FLICKER_CONTROLS)
+ return -EINVAL;
+ break;
+ }
+ case CPIA2_CID_FRAMERATE:
+ {
+ int maximum = NUM_FRAMERATE_CONTROLS - 1;
+ int i;
+ for(i=0; i<= maximum; i++) {
+ if(cam->params.vp_params.frame_rate ==
+ framerate_controls[i].value)
+ break;
+ }
+ if(i > maximum)
+ return -EINVAL;
+ c->value = i;
+ break;
+ }
+ case CPIA2_CID_USB_ALT:
+ c->value = cam->params.camera_state.stream_mode;
+ break;
+ case CPIA2_CID_LIGHTS:
+ {
+ int i;
+ cpia2_do_command(cam, CPIA2_CMD_GET_VP_GPIO_DATA,
+ TRANSFER_READ, 0);
+ for(i=0; i<NUM_LIGHTS_CONTROLS; i++) {
+ if((cam->params.vp_params.gpio_data&GPIO_LIGHTS_MASK) ==
+ lights_controls[i].value) {
+ break;
+ }
+ }
+ if(i == NUM_LIGHTS_CONTROLS)
+ return -EINVAL;
+ c->value = i;
+ break;
+ }
+ case CPIA2_CID_RESET_CAMERA:
+ return -EINVAL;
+ default:
+ return -EINVAL;
+ }
+
+ DBG("Get control id:%d, value:%d\n", c->id, c->value);
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * ioctl_s_ctrl
+ *
+ * V4L2 set the value of a control variable
+ *
+ *****************************************************************************/
+
+static int cpia2_s_ctrl(struct file *file, void *fh, struct v4l2_control *c)
+{
+ struct camera_data *cam = video_drvdata(file);
+ int i;
+ int retval = 0;
+
+ DBG("Set control id:%d, value:%d\n", c->id, c->value);
+
+ /* Check that the value is in range */
+ for(i=0; i<NUM_CONTROLS; i++) {
+ if(c->id == controls[i].id) {
+ if(c->value < controls[i].minimum ||
+ c->value > controls[i].maximum) {
+ return -EINVAL;
+ }
+ break;
+ }
+ }
+ if(i == NUM_CONTROLS)
+ return -EINVAL;
+
+ switch(c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ cpia2_set_brightness(cam, c->value);
+ break;
+ case V4L2_CID_CONTRAST:
+ cpia2_set_contrast(cam, c->value);
+ break;
+ case V4L2_CID_SATURATION:
+ cpia2_set_saturation(cam, c->value);
+ break;
+ case V4L2_CID_HFLIP:
+ cpia2_set_property_mirror(cam, c->value);
+ break;
+ case V4L2_CID_VFLIP:
+ cpia2_set_property_flip(cam, c->value);
+ break;
+ case CPIA2_CID_TARGET_KB:
+ retval = cpia2_set_target_kb(cam, c->value);
+ break;
+ case CPIA2_CID_GPIO:
+ retval = cpia2_set_gpio(cam, c->value);
+ break;
+ case CPIA2_CID_FLICKER_MODE:
+ retval = cpia2_set_flicker_mode(cam,
+ flicker_controls[c->value].value);
+ break;
+ case CPIA2_CID_FRAMERATE:
+ retval = cpia2_set_fps(cam, framerate_controls[c->value].value);
+ break;
+ case CPIA2_CID_USB_ALT:
+ retval = cpia2_usb_change_streaming_alternate(cam, c->value);
+ break;
+ case CPIA2_CID_LIGHTS:
+ retval = cpia2_set_gpio(cam, lights_controls[c->value].value);
+ break;
+ case CPIA2_CID_RESET_CAMERA:
+ cpia2_usb_stream_pause(cam);
+ cpia2_reset_camera(cam);
+ cpia2_usb_stream_resume(cam);
+ break;
+ default:
+ retval = -EINVAL;
+ }
+
+ return retval;
+}
+
+/******************************************************************************
+ *
+ * ioctl_g_jpegcomp
+ *
+ * V4L2 get the JPEG compression parameters
+ *
+ *****************************************************************************/
+
+static int cpia2_g_jpegcomp(struct file *file, void *fh, struct v4l2_jpegcompression *parms)
+{
+ struct camera_data *cam = video_drvdata(file);
+
+ memset(parms, 0, sizeof(*parms));
+
+ parms->quality = 80; // TODO: Can this be made meaningful?
+
+ parms->jpeg_markers = V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI;
+ if(!cam->params.compression.inhibit_htables) {
+ parms->jpeg_markers |= V4L2_JPEG_MARKER_DHT;
+ }
+
+ parms->APPn = cam->APPn;
+ parms->APP_len = cam->APP_len;
+ if(cam->APP_len > 0) {
+ memcpy(parms->APP_data, cam->APP_data, cam->APP_len);
+ parms->jpeg_markers |= V4L2_JPEG_MARKER_APP;
+ }
+
+ parms->COM_len = cam->COM_len;
+ if(cam->COM_len > 0) {
+ memcpy(parms->COM_data, cam->COM_data, cam->COM_len);
+ parms->jpeg_markers |= JPEG_MARKER_COM;
+ }
+
+ DBG("G_JPEGCOMP APP_len:%d COM_len:%d\n",
+ parms->APP_len, parms->COM_len);
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * ioctl_s_jpegcomp
+ *
+ * V4L2 set the JPEG compression parameters
+ * NOTE: quality and some jpeg_markers are ignored.
+ *
+ *****************************************************************************/
+
+static int cpia2_s_jpegcomp(struct file *file, void *fh, struct v4l2_jpegcompression *parms)
+{
+ struct camera_data *cam = video_drvdata(file);
+
+ DBG("S_JPEGCOMP APP_len:%d COM_len:%d\n",
+ parms->APP_len, parms->COM_len);
+
+ cam->params.compression.inhibit_htables =
+ !(parms->jpeg_markers & V4L2_JPEG_MARKER_DHT);
+
+ if(parms->APP_len != 0) {
+ if(parms->APP_len > 0 &&
+ parms->APP_len <= sizeof(cam->APP_data) &&
+ parms->APPn >= 0 && parms->APPn <= 15) {
+ cam->APPn = parms->APPn;
+ cam->APP_len = parms->APP_len;
+ memcpy(cam->APP_data, parms->APP_data, parms->APP_len);
+ } else {
+ LOG("Bad APPn Params n=%d len=%d\n",
+ parms->APPn, parms->APP_len);
+ return -EINVAL;
+ }
+ } else {
+ cam->APP_len = 0;
+ }
+
+ if(parms->COM_len != 0) {
+ if(parms->COM_len > 0 &&
+ parms->COM_len <= sizeof(cam->COM_data)) {
+ cam->COM_len = parms->COM_len;
+ memcpy(cam->COM_data, parms->COM_data, parms->COM_len);
+ } else {
+ LOG("Bad COM_len=%d\n", parms->COM_len);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * ioctl_reqbufs
+ *
+ * V4L2 Initiate memory mapping.
+ * NOTE: The user's request is ignored. For now the buffers are fixed.
+ *
+ *****************************************************************************/
+
+static int cpia2_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *req)
+{
+ struct camera_data *cam = video_drvdata(file);
+
+ if(req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ req->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+ DBG("REQBUFS requested:%d returning:%d\n", req->count, cam->num_frames);
+ req->count = cam->num_frames;
+ memset(&req->reserved, 0, sizeof(req->reserved));
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * ioctl_querybuf
+ *
+ * V4L2 Query memory buffer status.
+ *
+ *****************************************************************************/
+
+static int cpia2_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+ struct camera_data *cam = video_drvdata(file);
+
+ if(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ buf->index > cam->num_frames)
+ return -EINVAL;
+
+ buf->m.offset = cam->buffers[buf->index].data - cam->frame_buffer;
+ buf->length = cam->frame_size;
+
+ buf->memory = V4L2_MEMORY_MMAP;
+
+ if(cam->mmapped)
+ buf->flags = V4L2_BUF_FLAG_MAPPED;
+ else
+ buf->flags = 0;
+
+ switch (cam->buffers[buf->index].status) {
+ case FRAME_EMPTY:
+ case FRAME_ERROR:
+ case FRAME_READING:
+ buf->bytesused = 0;
+ buf->flags = V4L2_BUF_FLAG_QUEUED;
+ break;
+ case FRAME_READY:
+ buf->bytesused = cam->buffers[buf->index].length;
+ buf->timestamp = cam->buffers[buf->index].timestamp;
+ buf->sequence = cam->buffers[buf->index].seq;
+ buf->flags = V4L2_BUF_FLAG_DONE;
+ break;
+ }
+
+ DBG("QUERYBUF index:%d offset:%d flags:%d seq:%d bytesused:%d\n",
+ buf->index, buf->m.offset, buf->flags, buf->sequence,
+ buf->bytesused);
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * ioctl_qbuf
+ *
+ * V4L2 User is freeing buffer
+ *
+ *****************************************************************************/
+
+static int cpia2_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+ struct camera_data *cam = video_drvdata(file);
+
+ if(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ buf->memory != V4L2_MEMORY_MMAP ||
+ buf->index > cam->num_frames)
+ return -EINVAL;
+
+ DBG("QBUF #%d\n", buf->index);
+
+ if(cam->buffers[buf->index].status == FRAME_READY)
+ cam->buffers[buf->index].status = FRAME_EMPTY;
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * find_earliest_filled_buffer
+ *
+ * Helper for ioctl_dqbuf. Find the next ready buffer.
+ *
+ *****************************************************************************/
+
+static int find_earliest_filled_buffer(struct camera_data *cam)
+{
+ int i;
+ int found = -1;
+ for (i=0; i<cam->num_frames; i++) {
+ if(cam->buffers[i].status == FRAME_READY) {
+ if(found < 0) {
+ found = i;
+ } else {
+ /* find which buffer is earlier */
+ struct timeval *tv1, *tv2;
+ tv1 = &cam->buffers[i].timestamp;
+ tv2 = &cam->buffers[found].timestamp;
+ if(tv1->tv_sec < tv2->tv_sec ||
+ (tv1->tv_sec == tv2->tv_sec &&
+ tv1->tv_usec < tv2->tv_usec))
+ found = i;
+ }
+ }
+ }
+ return found;
+}
+
+/******************************************************************************
+ *
+ * ioctl_dqbuf
+ *
+ * V4L2 User is asking for a filled buffer.
+ *
+ *****************************************************************************/
+
+static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+ struct camera_data *cam = video_drvdata(file);
+ int frame;
+
+ if(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ buf->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+ frame = find_earliest_filled_buffer(cam);
+
+ if(frame < 0 && file->f_flags&O_NONBLOCK)
+ return -EAGAIN;
+
+ if(frame < 0) {
+ /* Wait for a frame to become available */
+ struct framebuf *cb=cam->curbuff;
+ mutex_unlock(&cam->v4l2_lock);
+ wait_event_interruptible(cam->wq_stream,
+ !cam->present ||
+ (cb=cam->curbuff)->status == FRAME_READY);
+ mutex_lock(&cam->v4l2_lock);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ if(!cam->present)
+ return -ENOTTY;
+ frame = cb->num;
+ }
+
+
+ buf->index = frame;
+ buf->bytesused = cam->buffers[buf->index].length;
+ buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE;
+ buf->field = V4L2_FIELD_NONE;
+ buf->timestamp = cam->buffers[buf->index].timestamp;
+ buf->sequence = cam->buffers[buf->index].seq;
+ buf->m.offset = cam->buffers[buf->index].data - cam->frame_buffer;
+ buf->length = cam->frame_size;
+ buf->input = 0;
+ buf->reserved = 0;
+ memset(&buf->timecode, 0, sizeof(buf->timecode));
+
+ DBG("DQBUF #%d status:%d seq:%d length:%d\n", buf->index,
+ cam->buffers[buf->index].status, buf->sequence, buf->bytesused);
+
+ return 0;
+}
+
+static int cpia2_g_priority(struct file *file, void *_fh, enum v4l2_priority *p)
+{
+ struct cpia2_fh *fh = _fh;
+
+ *p = fh->prio;
+ return 0;
+}
+
+static int cpia2_s_priority(struct file *file, void *_fh, enum v4l2_priority prio)
+{
+ struct camera_data *cam = video_drvdata(file);
+ struct cpia2_fh *fh = _fh;
+
+ if (cam->streaming && prio != fh->prio &&
+ fh->prio == V4L2_PRIORITY_RECORD)
+ /* Can't drop record priority while streaming */
+ return -EBUSY;
+
+ if (prio == V4L2_PRIORITY_RECORD && prio != fh->prio &&
+ v4l2_prio_max(&cam->prio) == V4L2_PRIORITY_RECORD)
+ /* Only one program can record at a time */
+ return -EBUSY;
+ return v4l2_prio_change(&cam->prio, &fh->prio, prio);
+}
+
+static int cpia2_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
+{
+ struct camera_data *cam = video_drvdata(file);
+
+ DBG("VIDIOC_STREAMON, streaming=%d\n", cam->streaming);
+ if (!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (!cam->streaming)
+ return cpia2_usb_stream_start(cam,
+ cam->params.camera_state.stream_mode);
+ return -EINVAL;
+}
+
+static int cpia2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
+{
+ struct camera_data *cam = video_drvdata(file);
+
+ DBG("VIDIOC_STREAMOFF, streaming=%d\n", cam->streaming);
+ if (!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (cam->streaming)
+ return cpia2_usb_stream_stop(cam);
+ return -EINVAL;
+}
+
+/******************************************************************************
+ *
+ * cpia2_mmap
+ *
+ *****************************************************************************/
+static int cpia2_mmap(struct file *file, struct vm_area_struct *area)
+{
+ struct camera_data *cam = video_drvdata(file);
+ int retval;
+
+ /* Priority check */
+ struct cpia2_fh *fh = file->private_data;
+ if(fh->prio != V4L2_PRIORITY_RECORD) {
+ return -EBUSY;
+ }
+
+ retval = cpia2_remap_buffer(cam, area);
+
+ if(!retval)
+ fh->mmapped = 1;
+ return retval;
+}
+
+/******************************************************************************
+ *
+ * reset_camera_struct_v4l
+ *
+ * Sets all values to the defaults
+ *****************************************************************************/
+static void reset_camera_struct_v4l(struct camera_data *cam)
+{
+ cam->width = cam->params.roi.width;
+ cam->height = cam->params.roi.height;
+
+ cam->frame_size = buffer_size;
+ cam->num_frames = num_buffers;
+
+ /* FlickerModes */
+ cam->params.flicker_control.flicker_mode_req = flicker_mode;
+ cam->params.flicker_control.mains_frequency = flicker_freq;
+
+ /* streamMode */
+ cam->params.camera_state.stream_mode = alternate;
+
+ cam->pixelformat = V4L2_PIX_FMT_JPEG;
+ v4l2_prio_init(&cam->prio);
+}
+
+static const struct v4l2_ioctl_ops cpia2_ioctl_ops = {
+ .vidioc_querycap = cpia2_querycap,
+ .vidioc_enum_input = cpia2_enum_input,
+ .vidioc_g_input = cpia2_g_input,
+ .vidioc_s_input = cpia2_s_input,
+ .vidioc_enum_fmt_vid_cap = cpia2_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = cpia2_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = cpia2_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = cpia2_try_fmt_vid_cap,
+ .vidioc_queryctrl = cpia2_queryctrl,
+ .vidioc_querymenu = cpia2_querymenu,
+ .vidioc_g_ctrl = cpia2_g_ctrl,
+ .vidioc_s_ctrl = cpia2_s_ctrl,
+ .vidioc_g_jpegcomp = cpia2_g_jpegcomp,
+ .vidioc_s_jpegcomp = cpia2_s_jpegcomp,
+ .vidioc_cropcap = cpia2_cropcap,
+ .vidioc_reqbufs = cpia2_reqbufs,
+ .vidioc_querybuf = cpia2_querybuf,
+ .vidioc_qbuf = cpia2_qbuf,
+ .vidioc_dqbuf = cpia2_dqbuf,
+ .vidioc_streamon = cpia2_streamon,
+ .vidioc_streamoff = cpia2_streamoff,
+ .vidioc_g_priority = cpia2_g_priority,
+ .vidioc_s_priority = cpia2_s_priority,
+ .vidioc_default = cpia2_default,
+};
+
+/***
+ * The v4l video device structure initialized for this device
+ ***/
+static const struct v4l2_file_operations cpia2_fops = {
+ .owner = THIS_MODULE,
+ .open = cpia2_open,
+ .release = cpia2_close,
+ .read = cpia2_v4l_read,
+ .poll = cpia2_v4l_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = cpia2_mmap,
+};
+
+static struct video_device cpia2_template = {
+ /* I could not find any place for the old .initialize initializer?? */
+ .name = "CPiA2 Camera",
+ .fops = &cpia2_fops,
+ .ioctl_ops = &cpia2_ioctl_ops,
+ .release = video_device_release,
+};
+
+/******************************************************************************
+ *
+ * cpia2_register_camera
+ *
+ *****************************************************************************/
+int cpia2_register_camera(struct camera_data *cam)
+{
+ cam->vdev = video_device_alloc();
+ if(!cam->vdev)
+ return -ENOMEM;
+
+ memcpy(cam->vdev, &cpia2_template, sizeof(cpia2_template));
+ video_set_drvdata(cam->vdev, cam);
+ cam->vdev->lock = &cam->v4l2_lock;
+
+ reset_camera_struct_v4l(cam);
+
+ /* register v4l device */
+ if (video_register_device(cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
+ ERR("video_register_device failed\n");
+ video_device_release(cam->vdev);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * cpia2_unregister_camera
+ *
+ *****************************************************************************/
+void cpia2_unregister_camera(struct camera_data *cam)
+{
+ if (!cam->open_count) {
+ video_unregister_device(cam->vdev);
+ } else {
+ LOG("%s removed while open, deferring "
+ "video_unregister_device\n",
+ video_device_node_name(cam->vdev));
+ }
+}
+
+/******************************************************************************
+ *
+ * check_parameters
+ *
+ * Make sure that all user-supplied parameters are sensible
+ *****************************************************************************/
+static void __init check_parameters(void)
+{
+ if(buffer_size < PAGE_SIZE) {
+ buffer_size = PAGE_SIZE;
+ LOG("buffer_size too small, setting to %d\n", buffer_size);
+ } else if(buffer_size > 1024*1024) {
+ /* arbitrary upper limiit */
+ buffer_size = 1024*1024;
+ LOG("buffer_size ridiculously large, setting to %d\n",
+ buffer_size);
+ } else {
+ buffer_size += PAGE_SIZE-1;
+ buffer_size &= ~(PAGE_SIZE-1);
+ }
+
+ if(num_buffers < 1) {
+ num_buffers = 1;
+ LOG("num_buffers too small, setting to %d\n", num_buffers);
+ } else if(num_buffers > VIDEO_MAX_FRAME) {
+ num_buffers = VIDEO_MAX_FRAME;
+ LOG("num_buffers too large, setting to %d\n", num_buffers);
+ }
+
+ if(alternate < USBIF_ISO_1 || alternate > USBIF_ISO_6) {
+ alternate = DEFAULT_ALT;
+ LOG("alternate specified is invalid, using %d\n", alternate);
+ }
+
+ if (flicker_mode != NEVER_FLICKER && flicker_mode != ANTI_FLICKER_ON) {
+ flicker_mode = NEVER_FLICKER;
+ LOG("Flicker mode specified is invalid, using %d\n",
+ flicker_mode);
+ }
+
+ if (flicker_freq != FLICKER_50 && flicker_freq != FLICKER_60) {
+ flicker_freq = FLICKER_60;
+ LOG("Flicker mode specified is invalid, using %d\n",
+ flicker_freq);
+ }
+
+ if(video_nr < -1 || video_nr > 64) {
+ video_nr = -1;
+ LOG("invalid video_nr specified, must be -1 to 64\n");
+ }
+
+ DBG("Using %d buffers, each %d bytes, alternate=%d\n",
+ num_buffers, buffer_size, alternate);
+}
+
+/************ Module Stuff ***************/
+
+
+/******************************************************************************
+ *
+ * cpia2_init/module_init
+ *
+ *****************************************************************************/
+static int __init cpia2_init(void)
+{
+ LOG("%s v%s\n",
+ ABOUT, CPIA_VERSION);
+ check_parameters();
+ cpia2_usb_init();
+ return 0;
+}
+
+
+/******************************************************************************
+ *
+ * cpia2_exit/module_exit
+ *
+ *****************************************************************************/
+static void __exit cpia2_exit(void)
+{
+ cpia2_usb_cleanup();
+ schedule_timeout(2 * HZ);
+}
+
+module_init(cpia2_init);
+module_exit(cpia2_exit);
diff --git a/drivers/media/video/cpia2/cpia2dev.h b/drivers/media/video/cpia2/cpia2dev.h
new file mode 100644
index 00000000..f66691fe
--- /dev/null
+++ b/drivers/media/video/cpia2/cpia2dev.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+ *
+ * Filename: cpia2dev.h
+ *
+ * Copyright 2001, STMicrolectronics, Inc.
+ *
+ * Contact: steve.miller@st.com
+ *
+ * Description:
+ * This file provides definitions for applications wanting to use the
+ * cpia2 driver beyond the generic v4l capabilities.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************************/
+
+#ifndef CPIA2_DEV_HEADER
+#define CPIA2_DEV_HEADER
+
+#include <linux/videodev2.h>
+
+/***
+ * The following defines are ioctl numbers based on video4linux private ioctls,
+ * which can range from 192 (BASE_VIDIOCPRIVATE) to 255. All of these take int
+ * args
+ */
+#define CPIA2_IOC_SET_GPIO _IOW('v', BASE_VIDIOC_PRIVATE + 17, __u32)
+
+/* V4L2 driver specific controls */
+#define CPIA2_CID_TARGET_KB (V4L2_CID_PRIVATE_BASE+0)
+#define CPIA2_CID_GPIO (V4L2_CID_PRIVATE_BASE+1)
+#define CPIA2_CID_FLICKER_MODE (V4L2_CID_PRIVATE_BASE+2)
+#define CPIA2_CID_FRAMERATE (V4L2_CID_PRIVATE_BASE+3)
+#define CPIA2_CID_USB_ALT (V4L2_CID_PRIVATE_BASE+4)
+#define CPIA2_CID_LIGHTS (V4L2_CID_PRIVATE_BASE+5)
+#define CPIA2_CID_RESET_CAMERA (V4L2_CID_PRIVATE_BASE+6)
+
+#endif
diff --git a/drivers/media/video/cs5345.c b/drivers/media/video/cs5345.c
new file mode 100644
index 00000000..c8581e26
--- /dev/null
+++ b/drivers/media/video/cs5345.c
@@ -0,0 +1,252 @@
+/*
+ * cs5345 Cirrus Logic 24-bit, 192 kHz Stereo Audio ADC
+ * Copyright (C) 2007 Hans Verkuil
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+
+MODULE_DESCRIPTION("i2c device driver for cs5345 Audio ADC");
+MODULE_AUTHOR("Hans Verkuil");
+MODULE_LICENSE("GPL");
+
+static bool debug;
+
+module_param(debug, bool, 0644);
+
+MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On");
+
+struct cs5345_state {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+};
+
+static inline struct cs5345_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct cs5345_state, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct cs5345_state, hdl)->sd;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static inline int cs5345_write(struct v4l2_subdev *sd, u8 reg, u8 value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static inline int cs5345_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int cs5345_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ if ((input & 0xf) > 6) {
+ v4l2_err(sd, "Invalid input %d.\n", input);
+ return -EINVAL;
+ }
+ cs5345_write(sd, 0x09, input & 0xf);
+ cs5345_write(sd, 0x05, input & 0xf0);
+ return 0;
+}
+
+static int cs5345_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ cs5345_write(sd, 0x04, ctrl->val ? 0x80 : 0);
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+ cs5345_write(sd, 0x07, ((u8)ctrl->val) & 0x3f);
+ cs5345_write(sd, 0x08, ((u8)ctrl->val) & 0x3f);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int cs5345_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->size = 1;
+ reg->val = cs5345_read(sd, reg->reg & 0x1f);
+ return 0;
+}
+
+static int cs5345_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ cs5345_write(sd, reg->reg & 0x1f, reg->val & 0xff);
+ return 0;
+}
+#endif
+
+static int cs5345_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_CS5345, 0);
+}
+
+static int cs5345_log_status(struct v4l2_subdev *sd)
+{
+ u8 v = cs5345_read(sd, 0x09) & 7;
+ u8 m = cs5345_read(sd, 0x04);
+ int vol = cs5345_read(sd, 0x08) & 0x3f;
+
+ v4l2_info(sd, "Input: %d%s\n", v,
+ (m & 0x80) ? " (muted)" : "");
+ if (vol >= 32)
+ vol = vol - 64;
+ v4l2_info(sd, "Volume: %d dB\n", vol);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_ctrl_ops cs5345_ctrl_ops = {
+ .s_ctrl = cs5345_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops cs5345_core_ops = {
+ .log_status = cs5345_log_status,
+ .g_chip_ident = cs5345_g_chip_ident,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = cs5345_g_register,
+ .s_register = cs5345_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_audio_ops cs5345_audio_ops = {
+ .s_routing = cs5345_s_routing,
+};
+
+static const struct v4l2_subdev_ops cs5345_ops = {
+ .core = &cs5345_core_ops,
+ .audio = &cs5345_audio_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int cs5345_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct cs5345_state *state;
+ struct v4l2_subdev *sd;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ state = kzalloc(sizeof(struct cs5345_state), GFP_KERNEL);
+ if (state == NULL)
+ return -ENOMEM;
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &cs5345_ops);
+
+ v4l2_ctrl_handler_init(&state->hdl, 2);
+ v4l2_ctrl_new_std(&state->hdl, &cs5345_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&state->hdl, &cs5345_ctrl_ops,
+ V4L2_CID_AUDIO_VOLUME, -24, 24, 1, 0);
+ sd->ctrl_handler = &state->hdl;
+ if (state->hdl.error) {
+ int err = state->hdl.error;
+
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ return err;
+ }
+ /* set volume/mute */
+ v4l2_ctrl_handler_setup(&state->hdl);
+
+ cs5345_write(sd, 0x02, 0x00);
+ cs5345_write(sd, 0x04, 0x01);
+ cs5345_write(sd, 0x09, 0x01);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int cs5345_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct cs5345_state *state = to_state(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct i2c_device_id cs5345_id[] = {
+ { "cs5345", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cs5345_id);
+
+static struct i2c_driver cs5345_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "cs5345",
+ },
+ .probe = cs5345_probe,
+ .remove = cs5345_remove,
+ .id_table = cs5345_id,
+};
+
+module_i2c_driver(cs5345_driver);
diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c
new file mode 100644
index 00000000..b2939122
--- /dev/null
+++ b/drivers/media/video/cs53l32a.c
@@ -0,0 +1,251 @@
+/*
+ * cs53l32a (Adaptec AVC-2010 and AVC-2410) i2c ivtv driver.
+ * Copyright (C) 2005 Martin Vaughan
+ *
+ * Audio source switching for Adaptec AVC-2410 added by Trev Jackson
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/ioctl.h>
+#include <asm/uaccess.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+
+MODULE_DESCRIPTION("i2c device driver for cs53l32a Audio ADC");
+MODULE_AUTHOR("Martin Vaughan");
+MODULE_LICENSE("GPL");
+
+static bool debug;
+
+module_param(debug, bool, 0644);
+
+MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On");
+
+
+struct cs53l32a_state {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+};
+
+static inline struct cs53l32a_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct cs53l32a_state, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct cs53l32a_state, hdl)->sd;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int cs53l32a_write(struct v4l2_subdev *sd, u8 reg, u8 value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static int cs53l32a_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int cs53l32a_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ /* There are 2 physical inputs, but the second input can be
+ placed in two modes, the first mode bypasses the PGA (gain),
+ the second goes through the PGA. Hence there are three
+ possible inputs to choose from. */
+ if (input > 2) {
+ v4l2_err(sd, "Invalid input %d.\n", input);
+ return -EINVAL;
+ }
+ cs53l32a_write(sd, 0x01, 0x01 + (input << 4));
+ return 0;
+}
+
+static int cs53l32a_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ cs53l32a_write(sd, 0x03, ctrl->val ? 0xf0 : 0x30);
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+ cs53l32a_write(sd, 0x04, (u8)ctrl->val);
+ cs53l32a_write(sd, 0x05, (u8)ctrl->val);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int cs53l32a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client,
+ chip, V4L2_IDENT_CS53l32A, 0);
+}
+
+static int cs53l32a_log_status(struct v4l2_subdev *sd)
+{
+ struct cs53l32a_state *state = to_state(sd);
+ u8 v = cs53l32a_read(sd, 0x01);
+
+ v4l2_info(sd, "Input: %d\n", (v >> 4) & 3);
+ v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_ctrl_ops cs53l32a_ctrl_ops = {
+ .s_ctrl = cs53l32a_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops cs53l32a_core_ops = {
+ .log_status = cs53l32a_log_status,
+ .g_chip_ident = cs53l32a_g_chip_ident,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+};
+
+static const struct v4l2_subdev_audio_ops cs53l32a_audio_ops = {
+ .s_routing = cs53l32a_s_routing,
+};
+
+static const struct v4l2_subdev_ops cs53l32a_ops = {
+ .core = &cs53l32a_core_ops,
+ .audio = &cs53l32a_audio_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+/* i2c implementation */
+
+/*
+ * Generic i2c probe
+ * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
+ */
+
+static int cs53l32a_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct cs53l32a_state *state;
+ struct v4l2_subdev *sd;
+ int i;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ if (!id)
+ strlcpy(client->name, "cs53l32a", sizeof(client->name));
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ state = kzalloc(sizeof(struct cs53l32a_state), GFP_KERNEL);
+ if (state == NULL)
+ return -ENOMEM;
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &cs53l32a_ops);
+
+ for (i = 1; i <= 7; i++) {
+ u8 v = cs53l32a_read(sd, i);
+
+ v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v);
+ }
+
+ v4l2_ctrl_handler_init(&state->hdl, 2);
+ v4l2_ctrl_new_std(&state->hdl, &cs53l32a_ctrl_ops,
+ V4L2_CID_AUDIO_VOLUME, -96, 12, 1, 0);
+ v4l2_ctrl_new_std(&state->hdl, &cs53l32a_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+ sd->ctrl_handler = &state->hdl;
+ if (state->hdl.error) {
+ int err = state->hdl.error;
+
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ return err;
+ }
+
+ /* Set cs53l32a internal register for Adaptec 2010/2410 setup */
+
+ cs53l32a_write(sd, 0x01, 0x21);
+ cs53l32a_write(sd, 0x02, 0x29);
+ cs53l32a_write(sd, 0x03, 0x30);
+ cs53l32a_write(sd, 0x04, 0x00);
+ cs53l32a_write(sd, 0x05, 0x00);
+ cs53l32a_write(sd, 0x06, 0x00);
+ cs53l32a_write(sd, 0x07, 0x00);
+
+ /* Display results, should be 0x21,0x29,0x30,0x00,0x00,0x00,0x00 */
+
+ for (i = 1; i <= 7; i++) {
+ u8 v = cs53l32a_read(sd, i);
+
+ v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v);
+ }
+ return 0;
+}
+
+static int cs53l32a_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct cs53l32a_state *state = to_state(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ return 0;
+}
+
+static const struct i2c_device_id cs53l32a_id[] = {
+ { "cs53l32a", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cs53l32a_id);
+
+static struct i2c_driver cs53l32a_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "cs53l32a",
+ },
+ .probe = cs53l32a_probe,
+ .remove = cs53l32a_remove,
+ .id_table = cs53l32a_id,
+};
+
+module_i2c_driver(cs53l32a_driver);
diff --git a/drivers/media/video/cs8420.h b/drivers/media/video/cs8420.h
new file mode 100644
index 00000000..621c0c66
--- /dev/null
+++ b/drivers/media/video/cs8420.h
@@ -0,0 +1,50 @@
+/* cs8420.h - cs8420 initializations
+ Copyright (C) 1999 Nathan Laredo (laredo@gnu.org)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+#ifndef __CS8420_H__
+#define __CS8420_H__
+
+/* Initialization Sequence */
+
+static __u8 init8420[] = {
+ 1, 0x01, 2, 0x02, 3, 0x00, 4, 0x46,
+ 5, 0x24, 6, 0x84, 18, 0x18, 19, 0x13,
+};
+
+#define INIT8420LEN (sizeof(init8420)/2)
+
+static __u8 mode8420pro[] = { /* professional output mode */
+ 32, 0xa1, 33, 0x00, 34, 0x00, 35, 0x00,
+ 36, 0x00, 37, 0x00, 38, 0x00, 39, 0x00,
+ 40, 0x00, 41, 0x00, 42, 0x00, 43, 0x00,
+ 44, 0x00, 45, 0x00, 46, 0x00, 47, 0x00,
+ 48, 0x00, 49, 0x00, 50, 0x00, 51, 0x00,
+ 52, 0x00, 53, 0x00, 54, 0x00, 55, 0x00,
+};
+#define MODE8420LEN (sizeof(mode8420pro)/2)
+
+static __u8 mode8420con[] = { /* consumer output mode */
+ 32, 0x20, 33, 0x00, 34, 0x00, 35, 0x48,
+ 36, 0x00, 37, 0x00, 38, 0x00, 39, 0x00,
+ 40, 0x00, 41, 0x00, 42, 0x00, 43, 0x00,
+ 44, 0x00, 45, 0x00, 46, 0x00, 47, 0x00,
+ 48, 0x00, 49, 0x00, 50, 0x00, 51, 0x00,
+ 52, 0x00, 53, 0x00, 54, 0x00, 55, 0x00,
+};
+
+#endif
diff --git a/drivers/media/video/cx18/Kconfig b/drivers/media/video/cx18/Kconfig
new file mode 100644
index 00000000..53b3c770
--- /dev/null
+++ b/drivers/media/video/cx18/Kconfig
@@ -0,0 +1,35 @@
+config VIDEO_CX18
+ tristate "Conexant cx23418 MPEG encoder support"
+ depends on VIDEO_V4L2 && DVB_CORE && PCI && I2C && EXPERIMENTAL
+ select I2C_ALGOBIT
+ select VIDEOBUF_VMALLOC
+ depends on RC_CORE
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ select VIDEO_CX2341X
+ select VIDEO_CS5345
+ select DVB_S5H1409 if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE
+ select DVB_S5H1411 if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE
+ select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE
+ ---help---
+ This is a video4linux driver for Conexant cx23418 based
+ PCI combo video recorder devices.
+
+ This is used in devices such as the Hauppauge HVR-1600
+ cards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cx18.
+
+config VIDEO_CX18_ALSA
+ tristate "Conexant 23418 DMA audio support"
+ depends on VIDEO_CX18 && SND && EXPERIMENTAL
+ select SND_PCM
+ ---help---
+ This is a video4linux driver for direct (DMA) audio on
+ Conexant 23418 based TV cards using ALSA.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cx18-alsa.
diff --git a/drivers/media/video/cx18/Makefile b/drivers/media/video/cx18/Makefile
new file mode 100644
index 00000000..a86bab58
--- /dev/null
+++ b/drivers/media/video/cx18/Makefile
@@ -0,0 +1,13 @@
+cx18-objs := cx18-driver.o cx18-cards.o cx18-i2c.o cx18-firmware.o cx18-gpio.o \
+ cx18-queue.o cx18-streams.o cx18-fileops.o cx18-ioctl.o cx18-controls.o \
+ cx18-mailbox.o cx18-vbi.o cx18-audio.o cx18-video.o cx18-irq.o \
+ cx18-av-core.o cx18-av-audio.o cx18-av-firmware.o cx18-av-vbi.o cx18-scb.o \
+ cx18-dvb.o cx18-io.o
+cx18-alsa-objs := cx18-alsa-main.o cx18-alsa-pcm.o
+
+obj-$(CONFIG_VIDEO_CX18) += cx18.o
+obj-$(CONFIG_VIDEO_CX18_ALSA) += cx18-alsa.o
+
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/common/tuners
diff --git a/drivers/media/video/cx18/cx18-alsa-main.c b/drivers/media/video/cx18/cx18-alsa-main.c
new file mode 100644
index 00000000..e118361c
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-alsa-main.c
@@ -0,0 +1,294 @@
+/*
+ * ALSA interface to cx18 PCM capture streams
+ *
+ * Copyright (C) 2009 Andy Walls <awalls@md.metrocast.net>
+ * Copyright (C) 2009 Devin Heitmueller <dheitmueller@kernellabs.com>
+ *
+ * Portions of this work were sponsored by ONELAN Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+
+#include <media/v4l2-device.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+
+#include "cx18-driver.h"
+#include "cx18-version.h"
+#include "cx18-alsa.h"
+#include "cx18-alsa-mixer.h"
+#include "cx18-alsa-pcm.h"
+
+int cx18_alsa_debug;
+
+#define CX18_DEBUG_ALSA_INFO(fmt, arg...) \
+ do { \
+ if (cx18_alsa_debug & 2) \
+ printk(KERN_INFO "%s: " fmt, "cx18-alsa", ## arg); \
+ } while (0);
+
+module_param_named(debug, cx18_alsa_debug, int, 0644);
+MODULE_PARM_DESC(debug,
+ "Debug level (bitmask). Default: 0\n"
+ "\t\t\t 1/0x0001: warning\n"
+ "\t\t\t 2/0x0002: info\n");
+
+MODULE_AUTHOR("Andy Walls");
+MODULE_DESCRIPTION("CX23418 ALSA Interface");
+MODULE_SUPPORTED_DEVICE("CX23418 MPEG2 encoder");
+MODULE_LICENSE("GPL");
+
+MODULE_VERSION(CX18_VERSION);
+
+static inline
+struct snd_cx18_card *to_snd_cx18_card(struct v4l2_device *v4l2_dev)
+{
+ return to_cx18(v4l2_dev)->alsa;
+}
+
+static inline
+struct snd_cx18_card *p_to_snd_cx18_card(struct v4l2_device **v4l2_dev)
+{
+ return container_of(v4l2_dev, struct snd_cx18_card, v4l2_dev);
+}
+
+static void snd_cx18_card_free(struct snd_cx18_card *cxsc)
+{
+ if (cxsc == NULL)
+ return;
+
+ if (cxsc->v4l2_dev != NULL)
+ to_cx18(cxsc->v4l2_dev)->alsa = NULL;
+
+ /* FIXME - take any other stopping actions needed */
+
+ kfree(cxsc);
+}
+
+static void snd_cx18_card_private_free(struct snd_card *sc)
+{
+ if (sc == NULL)
+ return;
+ snd_cx18_card_free(sc->private_data);
+ sc->private_data = NULL;
+ sc->private_free = NULL;
+}
+
+static int snd_cx18_card_create(struct v4l2_device *v4l2_dev,
+ struct snd_card *sc,
+ struct snd_cx18_card **cxsc)
+{
+ *cxsc = kzalloc(sizeof(struct snd_cx18_card), GFP_KERNEL);
+ if (*cxsc == NULL)
+ return -ENOMEM;
+
+ (*cxsc)->v4l2_dev = v4l2_dev;
+ (*cxsc)->sc = sc;
+
+ sc->private_data = *cxsc;
+ sc->private_free = snd_cx18_card_private_free;
+
+ return 0;
+}
+
+static int snd_cx18_card_set_names(struct snd_cx18_card *cxsc)
+{
+ struct cx18 *cx = to_cx18(cxsc->v4l2_dev);
+ struct snd_card *sc = cxsc->sc;
+
+ /* sc->driver is used by alsa-lib's configurator: simple, unique */
+ strlcpy(sc->driver, "CX23418", sizeof(sc->driver));
+
+ /* sc->shortname is a symlink in /proc/asound: CX18-M -> cardN */
+ snprintf(sc->shortname, sizeof(sc->shortname), "CX18-%d",
+ cx->instance);
+
+ /* sc->longname is read from /proc/asound/cards */
+ snprintf(sc->longname, sizeof(sc->longname),
+ "CX23418 #%d %s TV/FM Radio/Line-In Capture",
+ cx->instance, cx->card_name);
+
+ return 0;
+}
+
+static int snd_cx18_init(struct v4l2_device *v4l2_dev)
+{
+ struct cx18 *cx = to_cx18(v4l2_dev);
+ struct snd_card *sc = NULL;
+ struct snd_cx18_card *cxsc;
+ int ret;
+
+ /* Numbrs steps from "Writing an ALSA Driver" by Takashi Iwai */
+
+ /* (1) Check and increment the device index */
+ /* This is a no-op for us. We'll use the cx->instance */
+
+ /* (2) Create a card instance */
+ ret = snd_card_create(SNDRV_DEFAULT_IDX1, /* use first available id */
+ SNDRV_DEFAULT_STR1, /* xid from end of shortname*/
+ THIS_MODULE, 0, &sc);
+ if (ret) {
+ CX18_ALSA_ERR("%s: snd_card_create() failed with err %d\n",
+ __func__, ret);
+ goto err_exit;
+ }
+
+ /* (3) Create a main component */
+ ret = snd_cx18_card_create(v4l2_dev, sc, &cxsc);
+ if (ret) {
+ CX18_ALSA_ERR("%s: snd_cx18_card_create() failed with err %d\n",
+ __func__, ret);
+ goto err_exit_free;
+ }
+
+ /* (4) Set the driver ID and name strings */
+ snd_cx18_card_set_names(cxsc);
+
+
+ ret = snd_cx18_pcm_create(cxsc);
+ if (ret) {
+ CX18_ALSA_ERR("%s: snd_cx18_pcm_create() failed with err %d\n",
+ __func__, ret);
+ goto err_exit_free;
+ }
+ /* FIXME - proc files */
+
+ /* (7) Set the driver data and return 0 */
+ /* We do this out of normal order for PCI drivers to avoid races */
+ cx->alsa = cxsc;
+
+ /* (6) Register the card instance */
+ ret = snd_card_register(sc);
+ if (ret) {
+ cx->alsa = NULL;
+ CX18_ALSA_ERR("%s: snd_card_register() failed with err %d\n",
+ __func__, ret);
+ goto err_exit_free;
+ }
+
+ return 0;
+
+err_exit_free:
+ if (sc != NULL)
+ snd_card_free(sc);
+ kfree(cxsc);
+err_exit:
+ return ret;
+}
+
+int cx18_alsa_load(struct cx18 *cx)
+{
+ struct v4l2_device *v4l2_dev = &cx->v4l2_dev;
+ struct cx18_stream *s;
+
+ if (v4l2_dev == NULL) {
+ printk(KERN_ERR "cx18-alsa: %s: struct v4l2_device * is NULL\n",
+ __func__);
+ return 0;
+ }
+
+ cx = to_cx18(v4l2_dev);
+ if (cx == NULL) {
+ printk(KERN_ERR "cx18-alsa cx is NULL\n");
+ return 0;
+ }
+
+ s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM];
+ if (s->video_dev == NULL) {
+ CX18_DEBUG_ALSA_INFO("%s: PCM stream for card is disabled - "
+ "skipping\n", __func__);
+ return 0;
+ }
+
+ if (cx->alsa != NULL) {
+ CX18_ALSA_ERR("%s: struct snd_cx18_card * already exists\n",
+ __func__);
+ return 0;
+ }
+
+ if (snd_cx18_init(v4l2_dev)) {
+ CX18_ALSA_ERR("%s: failed to create struct snd_cx18_card\n",
+ __func__);
+ } else {
+ CX18_DEBUG_ALSA_INFO("%s: created cx18 ALSA interface instance "
+ "\n", __func__);
+ }
+ return 0;
+}
+
+static int __init cx18_alsa_init(void)
+{
+ printk(KERN_INFO "cx18-alsa: module loading...\n");
+ cx18_ext_init = &cx18_alsa_load;
+ return 0;
+}
+
+static void __exit snd_cx18_exit(struct snd_cx18_card *cxsc)
+{
+ struct cx18 *cx = to_cx18(cxsc->v4l2_dev);
+
+ /* FIXME - pointer checks & shutdown cxsc */
+
+ snd_card_free(cxsc->sc);
+ cx->alsa = NULL;
+}
+
+static int __exit cx18_alsa_exit_callback(struct device *dev, void *data)
+{
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
+ struct snd_cx18_card *cxsc;
+
+ if (v4l2_dev == NULL) {
+ printk(KERN_ERR "cx18-alsa: %s: struct v4l2_device * is NULL\n",
+ __func__);
+ return 0;
+ }
+
+ cxsc = to_snd_cx18_card(v4l2_dev);
+ if (cxsc == NULL) {
+ CX18_ALSA_WARN("%s: struct snd_cx18_card * is NULL\n",
+ __func__);
+ return 0;
+ }
+
+ snd_cx18_exit(cxsc);
+ return 0;
+}
+
+static void __exit cx18_alsa_exit(void)
+{
+ struct device_driver *drv;
+ int ret;
+
+ printk(KERN_INFO "cx18-alsa: module unloading...\n");
+
+ drv = driver_find("cx18", &pci_bus_type);
+ ret = driver_for_each_device(drv, NULL, NULL, cx18_alsa_exit_callback);
+
+ cx18_ext_init = NULL;
+ printk(KERN_INFO "cx18-alsa: module unload complete\n");
+}
+
+module_init(cx18_alsa_init);
+module_exit(cx18_alsa_exit);
diff --git a/drivers/media/video/cx18/cx18-alsa-mixer.c b/drivers/media/video/cx18/cx18-alsa-mixer.c
new file mode 100644
index 00000000..341bddc0
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-alsa-mixer.c
@@ -0,0 +1,175 @@
+/*
+ * ALSA mixer controls for the
+ * ALSA interface to cx18 PCM capture streams
+ *
+ * Copyright (C) 2009 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-device.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+
+#include "cx18-alsa.h"
+#include "cx18-driver.h"
+
+/*
+ * Note the cx18-av-core volume scale is funny, due to the alignment of the
+ * scale with another chip's range:
+ *
+ * v4l2_control value /512 indicated dB actual dB reg 0x8d4
+ * 0x0000 - 0x01ff 0 -119 -96 228
+ * 0x0200 - 0x02ff 1 -118 -96 228
+ * ...
+ * 0x2c00 - 0x2dff 22 -97 -96 228
+ * 0x2e00 - 0x2fff 23 -96 -96 228
+ * 0x3000 - 0x31ff 24 -95 -95 226
+ * ...
+ * 0xee00 - 0xefff 119 0 0 36
+ * ...
+ * 0xfe00 - 0xffff 127 +8 +8 20
+ */
+static inline int dB_to_cx18_av_vol(int dB)
+{
+ if (dB < -96)
+ dB = -96;
+ else if (dB > 8)
+ dB = 8;
+ return (dB + 119) << 9;
+}
+
+static inline int cx18_av_vol_to_dB(int v)
+{
+ if (v < (23 << 9))
+ v = (23 << 9);
+ else if (v > (127 << 9))
+ v = (127 << 9);
+ return (v >> 9) - 119;
+}
+
+static int snd_cx18_mixer_tv_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ /* We're already translating values, just keep this control in dB */
+ uinfo->value.integer.min = -96;
+ uinfo->value.integer.max = 8;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static int snd_cx18_mixer_tv_vol_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uctl)
+{
+ struct snd_cx18_card *cxsc = snd_kcontrol_chip(kctl);
+ struct cx18 *cx = to_cx18(cxsc->v4l2_dev);
+ struct v4l2_control vctrl;
+ int ret;
+
+ vctrl.id = V4L2_CID_AUDIO_VOLUME;
+ vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]);
+
+ snd_cx18_lock(cxsc);
+ ret = v4l2_subdev_call(cx->sd_av, core, g_ctrl, &vctrl);
+ snd_cx18_unlock(cxsc);
+
+ if (!ret)
+ uctl->value.integer.value[0] = cx18_av_vol_to_dB(vctrl.value);
+ return ret;
+}
+
+static int snd_cx18_mixer_tv_vol_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uctl)
+{
+ struct snd_cx18_card *cxsc = snd_kcontrol_chip(kctl);
+ struct cx18 *cx = to_cx18(cxsc->v4l2_dev);
+ struct v4l2_control vctrl;
+ int ret;
+
+ vctrl.id = V4L2_CID_AUDIO_VOLUME;
+ vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]);
+
+ snd_cx18_lock(cxsc);
+
+ /* Fetch current state */
+ ret = v4l2_subdev_call(cx->sd_av, core, g_ctrl, &vctrl);
+
+ if (ret ||
+ (cx18_av_vol_to_dB(vctrl.value) != uctl->value.integer.value[0])) {
+
+ /* Set, if needed */
+ vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]);
+ ret = v4l2_subdev_call(cx->sd_av, core, s_ctrl, &vctrl);
+ if (!ret)
+ ret = 1; /* Indicate control was changed w/o error */
+ }
+ snd_cx18_unlock(cxsc);
+
+ return ret;
+}
+
+
+/* This is a bit of overkill, the slider is already in dB internally */
+static DECLARE_TLV_DB_SCALE(snd_cx18_mixer_tv_vol_db_scale, -9600, 100, 0);
+
+static struct snd_kcontrol_new snd_cx18_mixer_tv_vol __initdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog TV Capture Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .info = snd_cx18_mixer_tv_volume_info,
+ .get = snd_cx18_mixer_tv_volume_get,
+ .put = snd_cx18_mixer_tv_volume_put,
+ .tlv.p = snd_cx18_mixer_tv_vol_db_scale
+};
+
+/* FIXME - add mute switch and balance, bass, treble sliders:
+ V4L2_CID_AUDIO_MUTE
+
+ V4L2_CID_AUDIO_BALANCE
+
+ V4L2_CID_AUDIO_BASS
+ V4L2_CID_AUDIO_TREBLE
+*/
+
+/* FIXME - add stereo, lang1, lang2, mono menu */
+/* FIXME - add CS5345 I2S volume for HVR-1600 */
+
+int __init snd_cx18_mixer_create(struct snd_cx18_card *cxsc)
+{
+ struct v4l2_device *v4l2_dev = cxsc->v4l2_dev;
+ struct snd_card *sc = cxsc->sc;
+ int ret;
+
+ strlcpy(sc->mixername, "CX23418 Mixer", sizeof(sc->mixername));
+
+ ret = snd_ctl_add(sc, snd_ctl_new1(snd_cx18_mixer_tv_vol, cxsc));
+ if (ret) {
+ CX18_ALSA_WARN("%s: failed to add %s control, err %d\n",
+ __func__, snd_cx18_mixer_tv_vol.name, ret);
+ }
+ return ret;
+}
diff --git a/drivers/media/video/cx18/cx18-alsa-mixer.h b/drivers/media/video/cx18/cx18-alsa-mixer.h
new file mode 100644
index 00000000..ec923879
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-alsa-mixer.h
@@ -0,0 +1,23 @@
+/*
+ * ALSA mixer controls for the
+ * ALSA interface to cx18 PCM capture streams
+ *
+ * Copyright (C) 2009 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+int __init snd_cx18_mixer_create(struct snd_cx18_card *cxsc);
diff --git a/drivers/media/video/cx18/cx18-alsa-pcm.c b/drivers/media/video/cx18/cx18-alsa-pcm.c
new file mode 100644
index 00000000..82d195be
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-alsa-pcm.c
@@ -0,0 +1,360 @@
+/*
+ * ALSA PCM device for the
+ * ALSA interface to cx18 PCM capture streams
+ *
+ * Copyright (C) 2009 Andy Walls <awalls@md.metrocast.net>
+ * Copyright (C) 2009 Devin Heitmueller <dheitmueller@kernellabs.com>
+ *
+ * Portions of this work were sponsored by ONELAN Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+
+#include <media/v4l2-device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+#include "cx18-driver.h"
+#include "cx18-queue.h"
+#include "cx18-streams.h"
+#include "cx18-fileops.h"
+#include "cx18-alsa.h"
+
+static unsigned int pcm_debug;
+module_param(pcm_debug, int, 0644);
+MODULE_PARM_DESC(pcm_debug, "enable debug messages for pcm");
+
+#define dprintk(fmt, arg...) do { \
+ if (pcm_debug) \
+ printk(KERN_INFO "cx18-alsa-pcm %s: " fmt, \
+ __func__, ##arg); \
+ } while (0)
+
+static struct snd_pcm_hardware snd_cx18_hw_capture = {
+ .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID,
+
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+
+ .rates = SNDRV_PCM_RATE_48000,
+
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */
+ .period_bytes_min = 64, /* 12544/2, */
+ .period_bytes_max = 12544,
+ .periods_min = 2,
+ .periods_max = 98, /* 12544, */
+};
+
+void cx18_alsa_announce_pcm_data(struct snd_cx18_card *cxsc, u8 *pcm_data,
+ size_t num_bytes)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ unsigned int oldptr;
+ unsigned int stride;
+ int period_elapsed = 0;
+ int length;
+
+ dprintk("cx18 alsa announce ptr=%p data=%p num_bytes=%zd\n", cxsc,
+ pcm_data, num_bytes);
+
+ substream = cxsc->capture_pcm_substream;
+ if (substream == NULL) {
+ dprintk("substream was NULL\n");
+ return;
+ }
+
+ runtime = substream->runtime;
+ if (runtime == NULL) {
+ dprintk("runtime was NULL\n");
+ return;
+ }
+
+ stride = runtime->frame_bits >> 3;
+ if (stride == 0) {
+ dprintk("stride is zero\n");
+ return;
+ }
+
+ length = num_bytes / stride;
+ if (length == 0) {
+ dprintk("%s: length was zero\n", __func__);
+ return;
+ }
+
+ if (runtime->dma_area == NULL) {
+ dprintk("dma area was NULL - ignoring\n");
+ return;
+ }
+
+ oldptr = cxsc->hwptr_done_capture;
+ if (oldptr + length >= runtime->buffer_size) {
+ unsigned int cnt =
+ runtime->buffer_size - oldptr;
+ memcpy(runtime->dma_area + oldptr * stride, pcm_data,
+ cnt * stride);
+ memcpy(runtime->dma_area, pcm_data + cnt * stride,
+ length * stride - cnt * stride);
+ } else {
+ memcpy(runtime->dma_area + oldptr * stride, pcm_data,
+ length * stride);
+ }
+ snd_pcm_stream_lock(substream);
+
+ cxsc->hwptr_done_capture += length;
+ if (cxsc->hwptr_done_capture >=
+ runtime->buffer_size)
+ cxsc->hwptr_done_capture -=
+ runtime->buffer_size;
+
+ cxsc->capture_transfer_done += length;
+ if (cxsc->capture_transfer_done >=
+ runtime->period_size) {
+ cxsc->capture_transfer_done -=
+ runtime->period_size;
+ period_elapsed = 1;
+ }
+
+ snd_pcm_stream_unlock(substream);
+
+ if (period_elapsed)
+ snd_pcm_period_elapsed(substream);
+}
+
+static int snd_cx18_pcm_capture_open(struct snd_pcm_substream *substream)
+{
+ struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct v4l2_device *v4l2_dev = cxsc->v4l2_dev;
+ struct cx18 *cx = to_cx18(v4l2_dev);
+ struct cx18_stream *s;
+ struct cx18_open_id item;
+ int ret;
+
+ /* Instruct the cx18 to start sending packets */
+ snd_cx18_lock(cxsc);
+ s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM];
+
+ item.cx = cx;
+ item.type = s->type;
+ item.open_id = cx->open_id++;
+
+ /* See if the stream is available */
+ if (cx18_claim_stream(&item, item.type)) {
+ /* No, it's already in use */
+ snd_cx18_unlock(cxsc);
+ return -EBUSY;
+ }
+
+ if (test_bit(CX18_F_S_STREAMOFF, &s->s_flags) ||
+ test_and_set_bit(CX18_F_S_STREAMING, &s->s_flags)) {
+ /* We're already streaming. No additional action required */
+ snd_cx18_unlock(cxsc);
+ return 0;
+ }
+
+
+ runtime->hw = snd_cx18_hw_capture;
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ cxsc->capture_pcm_substream = substream;
+ runtime->private_data = cx;
+
+ cx->pcm_announce_callback = cx18_alsa_announce_pcm_data;
+
+ /* Not currently streaming, so start it up */
+ set_bit(CX18_F_S_STREAMING, &s->s_flags);
+ ret = cx18_start_v4l2_encode_stream(s);
+ snd_cx18_unlock(cxsc);
+
+ return 0;
+}
+
+static int snd_cx18_pcm_capture_close(struct snd_pcm_substream *substream)
+{
+ struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
+ struct v4l2_device *v4l2_dev = cxsc->v4l2_dev;
+ struct cx18 *cx = to_cx18(v4l2_dev);
+ struct cx18_stream *s;
+ int ret;
+
+ /* Instruct the cx18 to stop sending packets */
+ snd_cx18_lock(cxsc);
+ s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM];
+ ret = cx18_stop_v4l2_encode_stream(s, 0);
+ clear_bit(CX18_F_S_STREAMING, &s->s_flags);
+
+ cx18_release_stream(s);
+
+ cx->pcm_announce_callback = NULL;
+ snd_cx18_unlock(cxsc);
+
+ return 0;
+}
+
+static int snd_cx18_pcm_ioctl(struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
+ int ret;
+
+ snd_cx18_lock(cxsc);
+ ret = snd_pcm_lib_ioctl(substream, cmd, arg);
+ snd_cx18_unlock(cxsc);
+ return ret;
+}
+
+
+static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
+ size_t size)
+{
+ struct snd_pcm_runtime *runtime = subs->runtime;
+
+ dprintk("Allocating vbuffer\n");
+ if (runtime->dma_area) {
+ if (runtime->dma_bytes > size)
+ return 0;
+
+ vfree(runtime->dma_area);
+ }
+ runtime->dma_area = vmalloc(size);
+ if (!runtime->dma_area)
+ return -ENOMEM;
+
+ runtime->dma_bytes = size;
+
+ return 0;
+}
+
+static int snd_cx18_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ int ret;
+
+ dprintk("%s called\n", __func__);
+
+ ret = snd_pcm_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(params));
+ return 0;
+}
+
+static int snd_cx18_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
+ unsigned long flags;
+
+ spin_lock_irqsave(&cxsc->slock, flags);
+ if (substream->runtime->dma_area) {
+ dprintk("freeing pcm capture region\n");
+ vfree(substream->runtime->dma_area);
+ substream->runtime->dma_area = NULL;
+ }
+ spin_unlock_irqrestore(&cxsc->slock, flags);
+
+ return 0;
+}
+
+static int snd_cx18_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
+
+ cxsc->hwptr_done_capture = 0;
+ cxsc->capture_transfer_done = 0;
+
+ return 0;
+}
+
+static int snd_cx18_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ return 0;
+}
+
+static
+snd_pcm_uframes_t snd_cx18_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ unsigned long flags;
+ snd_pcm_uframes_t hwptr_done;
+ struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
+
+ spin_lock_irqsave(&cxsc->slock, flags);
+ hwptr_done = cxsc->hwptr_done_capture;
+ spin_unlock_irqrestore(&cxsc->slock, flags);
+
+ return hwptr_done;
+}
+
+static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
+ unsigned long offset)
+{
+ void *pageptr = subs->runtime->dma_area + offset;
+
+ return vmalloc_to_page(pageptr);
+}
+
+static struct snd_pcm_ops snd_cx18_pcm_capture_ops = {
+ .open = snd_cx18_pcm_capture_open,
+ .close = snd_cx18_pcm_capture_close,
+ .ioctl = snd_cx18_pcm_ioctl,
+ .hw_params = snd_cx18_pcm_hw_params,
+ .hw_free = snd_cx18_pcm_hw_free,
+ .prepare = snd_cx18_pcm_prepare,
+ .trigger = snd_cx18_pcm_trigger,
+ .pointer = snd_cx18_pcm_pointer,
+ .page = snd_pcm_get_vmalloc_page,
+};
+
+int snd_cx18_pcm_create(struct snd_cx18_card *cxsc)
+{
+ struct snd_pcm *sp;
+ struct snd_card *sc = cxsc->sc;
+ struct v4l2_device *v4l2_dev = cxsc->v4l2_dev;
+ struct cx18 *cx = to_cx18(v4l2_dev);
+ int ret;
+
+ ret = snd_pcm_new(sc, "CX23418 PCM",
+ 0, /* PCM device 0, the only one for this card */
+ 0, /* 0 playback substreams */
+ 1, /* 1 capture substream */
+ &sp);
+ if (ret) {
+ CX18_ALSA_ERR("%s: snd_cx18_pcm_create() failed with err %d\n",
+ __func__, ret);
+ goto err_exit;
+ }
+
+ spin_lock_init(&cxsc->slock);
+
+ snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE,
+ &snd_cx18_pcm_capture_ops);
+ sp->info_flags = 0;
+ sp->private_data = cxsc;
+ strlcpy(sp->name, cx->card_name, sizeof(sp->name));
+
+ return 0;
+
+err_exit:
+ return ret;
+}
diff --git a/drivers/media/video/cx18/cx18-alsa-pcm.h b/drivers/media/video/cx18/cx18-alsa-pcm.h
new file mode 100644
index 00000000..d26e51f9
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-alsa-pcm.h
@@ -0,0 +1,27 @@
+/*
+ * ALSA PCM device for the
+ * ALSA interface to cx18 PCM capture streams
+ *
+ * Copyright (C) 2009 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+int __init snd_cx18_pcm_create(struct snd_cx18_card *cxsc);
+
+/* Used by cx18-mailbox to announce the PCM data to the module */
+void cx18_alsa_announce_pcm_data(struct snd_cx18_card *card, u8 *pcm_data,
+ size_t num_bytes);
diff --git a/drivers/media/video/cx18/cx18-alsa.h b/drivers/media/video/cx18/cx18-alsa.h
new file mode 100644
index 00000000..447da374
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-alsa.h
@@ -0,0 +1,75 @@
+/*
+ * ALSA interface to cx18 PCM capture streams
+ *
+ * Copyright (C) 2009 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+struct snd_card;
+
+struct snd_cx18_card {
+ struct v4l2_device *v4l2_dev;
+ struct snd_card *sc;
+ unsigned int capture_transfer_done;
+ unsigned int hwptr_done_capture;
+ struct snd_pcm_substream *capture_pcm_substream;
+ spinlock_t slock;
+};
+
+extern int cx18_alsa_debug;
+
+/*
+ * File operations that manipulate the encoder or video or audio subdevices
+ * need to be serialized. Use the same lock we use for v4l2 file ops.
+ */
+static inline void snd_cx18_lock(struct snd_cx18_card *cxsc)
+{
+ struct cx18 *cx = to_cx18(cxsc->v4l2_dev);
+ mutex_lock(&cx->serialize_lock);
+}
+
+static inline void snd_cx18_unlock(struct snd_cx18_card *cxsc)
+{
+ struct cx18 *cx = to_cx18(cxsc->v4l2_dev);
+ mutex_unlock(&cx->serialize_lock);
+}
+
+#define CX18_ALSA_DBGFLG_WARN (1 << 0)
+#define CX18_ALSA_DBGFLG_WARN (1 << 0)
+#define CX18_ALSA_DBGFLG_INFO (1 << 1)
+
+#define CX18_ALSA_DEBUG(x, type, fmt, args...) \
+ do { \
+ if ((x) & cx18_alsa_debug) \
+ printk(KERN_INFO "%s-alsa: " type ": " fmt, \
+ v4l2_dev->name , ## args); \
+ } while (0)
+
+#define CX18_ALSA_DEBUG_WARN(fmt, args...) \
+ CX18_ALSA_DEBUG(CX18_ALSA_DBGFLG_WARN, "warning", fmt , ## args)
+
+#define CX18_ALSA_DEBUG_INFO(fmt, args...) \
+ CX18_ALSA_DEBUG(CX18_ALSA_DBGFLG_INFO, "info", fmt , ## args)
+
+#define CX18_ALSA_ERR(fmt, args...) \
+ printk(KERN_ERR "%s-alsa: " fmt, v4l2_dev->name , ## args)
+
+#define CX18_ALSA_WARN(fmt, args...) \
+ printk(KERN_WARNING "%s-alsa: " fmt, v4l2_dev->name , ## args)
+
+#define CX18_ALSA_INFO(fmt, args...) \
+ printk(KERN_INFO "%s-alsa: " fmt, v4l2_dev->name , ## args)
diff --git a/drivers/media/video/cx18/cx18-audio.c b/drivers/media/video/cx18/cx18-audio.c
new file mode 100644
index 00000000..35268923
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-audio.c
@@ -0,0 +1,92 @@
+/*
+ * cx18 audio-related functions
+ *
+ * Derived from ivtv-audio.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-cards.h"
+#include "cx18-audio.h"
+
+#define CX18_AUDIO_ENABLE 0xc72014
+#define CX18_AI1_MUX_MASK 0x30
+#define CX18_AI1_MUX_I2S1 0x00
+#define CX18_AI1_MUX_I2S2 0x10
+#define CX18_AI1_MUX_843_I2S 0x20
+
+/* Selects the audio input and output according to the current
+ settings. */
+int cx18_audio_set_io(struct cx18 *cx)
+{
+ const struct cx18_card_audio_input *in;
+ u32 u, v;
+ int err;
+
+ /* Determine which input to use */
+ if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags))
+ in = &cx->card->radio_input;
+ else
+ in = &cx->card->audio_inputs[cx->audio_input];
+
+ /* handle muxer chips */
+ v4l2_subdev_call(cx->sd_extmux, audio, s_routing,
+ (u32) in->muxer_input, 0, 0);
+
+ err = cx18_call_hw_err(cx, cx->card->hw_audio_ctrl,
+ audio, s_routing, in->audio_input, 0, 0);
+ if (err)
+ return err;
+
+ /* FIXME - this internal mux should be abstracted to a subdev */
+ u = cx18_read_reg(cx, CX18_AUDIO_ENABLE);
+ v = u & ~CX18_AI1_MUX_MASK;
+ switch (in->audio_input) {
+ case CX18_AV_AUDIO_SERIAL1:
+ v |= CX18_AI1_MUX_I2S1;
+ break;
+ case CX18_AV_AUDIO_SERIAL2:
+ v |= CX18_AI1_MUX_I2S2;
+ break;
+ default:
+ v |= CX18_AI1_MUX_843_I2S;
+ break;
+ }
+ if (v == u) {
+ /* force a toggle of some AI1 MUX control bits */
+ u &= ~CX18_AI1_MUX_MASK;
+ switch (in->audio_input) {
+ case CX18_AV_AUDIO_SERIAL1:
+ u |= CX18_AI1_MUX_843_I2S;
+ break;
+ case CX18_AV_AUDIO_SERIAL2:
+ u |= CX18_AI1_MUX_843_I2S;
+ break;
+ default:
+ u |= CX18_AI1_MUX_I2S1;
+ break;
+ }
+ cx18_write_reg_expect(cx, u | 0xb00, CX18_AUDIO_ENABLE,
+ u, CX18_AI1_MUX_MASK);
+ }
+ cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE,
+ v, CX18_AI1_MUX_MASK);
+ return 0;
+}
diff --git a/drivers/media/video/cx18/cx18-audio.h b/drivers/media/video/cx18/cx18-audio.h
new file mode 100644
index 00000000..2731d29b
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-audio.h
@@ -0,0 +1,24 @@
+/*
+ * cx18 audio-related functions
+ *
+ * Derived from ivtv-audio.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+int cx18_audio_set_io(struct cx18 *cx);
diff --git a/drivers/media/video/cx18/cx18-av-audio.c b/drivers/media/video/cx18/cx18-av-audio.c
new file mode 100644
index 00000000..4a24ffb1
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-av-audio.c
@@ -0,0 +1,471 @@
+/*
+ * cx18 ADEC audio functions
+ *
+ * Derived from cx25840-audio.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "cx18-driver.h"
+
+static int set_audclk_freq(struct cx18 *cx, u32 freq)
+{
+ struct cx18_av_state *state = &cx->av_state;
+
+ if (freq != 32000 && freq != 44100 && freq != 48000)
+ return -EINVAL;
+
+ /*
+ * The PLL parameters are based on the external crystal frequency that
+ * would ideally be:
+ *
+ * NTSC Color subcarrier freq * 8 =
+ * 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz
+ *
+ * The accidents of history and rationale that explain from where this
+ * combination of magic numbers originate can be found in:
+ *
+ * [1] Abrahams, I. C., "Choice of Chrominance Subcarrier Frequency in
+ * the NTSC Standards", Proceedings of the I-R-E, January 1954, pp 79-80
+ *
+ * [2] Abrahams, I. C., "The 'Frequency Interleaving' Principle in the
+ * NTSC Standards", Proceedings of the I-R-E, January 1954, pp 81-83
+ *
+ * As Mike Bradley has rightly pointed out, it's not the exact crystal
+ * frequency that matters, only that all parts of the driver and
+ * firmware are using the same value (close to the ideal value).
+ *
+ * Since I have a strong suspicion that, if the firmware ever assumes a
+ * crystal value at all, it will assume 28.636360 MHz, the crystal
+ * freq used in calculations in this driver will be:
+ *
+ * xtal_freq = 28.636360 MHz
+ *
+ * an error of less than 0.13 ppm which is way, way better than any off
+ * the shelf crystal will have for accuracy anyway.
+ *
+ * Below I aim to run the PLLs' VCOs near 400 MHz to minimze error.
+ *
+ * Many thanks to Jeff Campbell and Mike Bradley for their extensive
+ * investigation, experimentation, testing, and suggested solutions of
+ * of audio/video sync problems with SVideo and CVBS captures.
+ */
+
+ if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
+ switch (freq) {
+ case 32000:
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x20
+ */
+ cx18_av_write4(cx, 0x108, 0x200d040f);
+
+ /* VID_PLL Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+ cx18_av_write4(cx, 0x10c, 0x002be2fe);
+
+ /* AUX_PLL Fraction = 0x176740c */
+ /* xtal * 0xd.bb3a060/0x20 = 32000 * 384: 393 MHz p-pd*/
+ cx18_av_write4(cx, 0x110, 0x0176740c);
+
+ /* src3/4/6_ctl */
+ /* 0x1.f77f = (4 * xtal/8*2/455) / 32000 */
+ cx18_av_write4(cx, 0x900, 0x0801f77f);
+ cx18_av_write4(cx, 0x904, 0x0801f77f);
+ cx18_av_write4(cx, 0x90c, 0x0801f77f);
+
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x20 */
+ cx18_av_write(cx, 0x127, 0x60);
+
+ /* AUD_COUNT = 0x2fff = 8 samples * 4 * 384 - 1 */
+ cx18_av_write4(cx, 0x12c, 0x11202fff);
+
+ /*
+ * EN_AV_LOCK = 0
+ * VID_COUNT = 0x0d2ef8 = 107999.000 * 8 =
+ * ((8 samples/32,000) * (13,500,000 * 8) * 4 - 1) * 8
+ */
+ cx18_av_write4(cx, 0x128, 0xa00d2ef8);
+ break;
+
+ case 44100:
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x18
+ */
+ cx18_av_write4(cx, 0x108, 0x180e040f);
+
+ /* VID_PLL Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+ cx18_av_write4(cx, 0x10c, 0x002be2fe);
+
+ /* AUX_PLL Fraction = 0x062a1f2 */
+ /* xtal * 0xe.3150f90/0x18 = 44100 * 384: 406 MHz p-pd*/
+ cx18_av_write4(cx, 0x110, 0x0062a1f2);
+
+ /* src3/4/6_ctl */
+ /* 0x1.6d59 = (4 * xtal/8*2/455) / 44100 */
+ cx18_av_write4(cx, 0x900, 0x08016d59);
+ cx18_av_write4(cx, 0x904, 0x08016d59);
+ cx18_av_write4(cx, 0x90c, 0x08016d59);
+
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x18 */
+ cx18_av_write(cx, 0x127, 0x58);
+
+ /* AUD_COUNT = 0x92ff = 49 samples * 2 * 384 - 1 */
+ cx18_av_write4(cx, 0x12c, 0x112092ff);
+
+ /*
+ * EN_AV_LOCK = 0
+ * VID_COUNT = 0x1d4bf8 = 239999.000 * 8 =
+ * ((49 samples/44,100) * (13,500,000 * 8) * 2 - 1) * 8
+ */
+ cx18_av_write4(cx, 0x128, 0xa01d4bf8);
+ break;
+
+ case 48000:
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x16
+ */
+ cx18_av_write4(cx, 0x108, 0x160e040f);
+
+ /* VID_PLL Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+ cx18_av_write4(cx, 0x10c, 0x002be2fe);
+
+ /* AUX_PLL Fraction = 0x05227ad */
+ /* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz p-pd*/
+ cx18_av_write4(cx, 0x110, 0x005227ad);
+
+ /* src3/4/6_ctl */
+ /* 0x1.4faa = (4 * xtal/8*2/455) / 48000 */
+ cx18_av_write4(cx, 0x900, 0x08014faa);
+ cx18_av_write4(cx, 0x904, 0x08014faa);
+ cx18_av_write4(cx, 0x90c, 0x08014faa);
+
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */
+ cx18_av_write(cx, 0x127, 0x56);
+
+ /* AUD_COUNT = 0x5fff = 4 samples * 16 * 384 - 1 */
+ cx18_av_write4(cx, 0x12c, 0x11205fff);
+
+ /*
+ * EN_AV_LOCK = 0
+ * VID_COUNT = 0x1193f8 = 143999.000 * 8 =
+ * ((4 samples/48,000) * (13,500,000 * 8) * 16 - 1) * 8
+ */
+ cx18_av_write4(cx, 0x128, 0xa01193f8);
+ break;
+ }
+ } else {
+ switch (freq) {
+ case 32000:
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x30
+ */
+ cx18_av_write4(cx, 0x108, 0x300d040f);
+
+ /* VID_PLL Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+ cx18_av_write4(cx, 0x10c, 0x002be2fe);
+
+ /* AUX_PLL Fraction = 0x176740c */
+ /* xtal * 0xd.bb3a060/0x30 = 32000 * 256: 393 MHz p-pd*/
+ cx18_av_write4(cx, 0x110, 0x0176740c);
+
+ /* src1_ctl */
+ /* 0x1.0000 = 32000/32000 */
+ cx18_av_write4(cx, 0x8f8, 0x08010000);
+
+ /* src3/4/6_ctl */
+ /* 0x2.0000 = 2 * (32000/32000) */
+ cx18_av_write4(cx, 0x900, 0x08020000);
+ cx18_av_write4(cx, 0x904, 0x08020000);
+ cx18_av_write4(cx, 0x90c, 0x08020000);
+
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x30 */
+ cx18_av_write(cx, 0x127, 0x70);
+
+ /* AUD_COUNT = 0x1fff = 8 samples * 4 * 256 - 1 */
+ cx18_av_write4(cx, 0x12c, 0x11201fff);
+
+ /*
+ * EN_AV_LOCK = 0
+ * VID_COUNT = 0x0d2ef8 = 107999.000 * 8 =
+ * ((8 samples/32,000) * (13,500,000 * 8) * 4 - 1) * 8
+ */
+ cx18_av_write4(cx, 0x128, 0xa00d2ef8);
+ break;
+
+ case 44100:
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x24
+ */
+ cx18_av_write4(cx, 0x108, 0x240e040f);
+
+ /* VID_PLL Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+ cx18_av_write4(cx, 0x10c, 0x002be2fe);
+
+ /* AUX_PLL Fraction = 0x062a1f2 */
+ /* xtal * 0xe.3150f90/0x24 = 44100 * 256: 406 MHz p-pd*/
+ cx18_av_write4(cx, 0x110, 0x0062a1f2);
+
+ /* src1_ctl */
+ /* 0x1.60cd = 44100/32000 */
+ cx18_av_write4(cx, 0x8f8, 0x080160cd);
+
+ /* src3/4/6_ctl */
+ /* 0x1.7385 = 2 * (32000/44100) */
+ cx18_av_write4(cx, 0x900, 0x08017385);
+ cx18_av_write4(cx, 0x904, 0x08017385);
+ cx18_av_write4(cx, 0x90c, 0x08017385);
+
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x24 */
+ cx18_av_write(cx, 0x127, 0x64);
+
+ /* AUD_COUNT = 0x61ff = 49 samples * 2 * 256 - 1 */
+ cx18_av_write4(cx, 0x12c, 0x112061ff);
+
+ /*
+ * EN_AV_LOCK = 0
+ * VID_COUNT = 0x1d4bf8 = 239999.000 * 8 =
+ * ((49 samples/44,100) * (13,500,000 * 8) * 2 - 1) * 8
+ */
+ cx18_av_write4(cx, 0x128, 0xa01d4bf8);
+ break;
+
+ case 48000:
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x20
+ */
+ cx18_av_write4(cx, 0x108, 0x200d040f);
+
+ /* VID_PLL Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+ cx18_av_write4(cx, 0x10c, 0x002be2fe);
+
+ /* AUX_PLL Fraction = 0x176740c */
+ /* xtal * 0xd.bb3a060/0x20 = 48000 * 256: 393 MHz p-pd*/
+ cx18_av_write4(cx, 0x110, 0x0176740c);
+
+ /* src1_ctl */
+ /* 0x1.8000 = 48000/32000 */
+ cx18_av_write4(cx, 0x8f8, 0x08018000);
+
+ /* src3/4/6_ctl */
+ /* 0x1.5555 = 2 * (32000/48000) */
+ cx18_av_write4(cx, 0x900, 0x08015555);
+ cx18_av_write4(cx, 0x904, 0x08015555);
+ cx18_av_write4(cx, 0x90c, 0x08015555);
+
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x20 */
+ cx18_av_write(cx, 0x127, 0x60);
+
+ /* AUD_COUNT = 0x3fff = 4 samples * 16 * 256 - 1 */
+ cx18_av_write4(cx, 0x12c, 0x11203fff);
+
+ /*
+ * EN_AV_LOCK = 0
+ * VID_COUNT = 0x1193f8 = 143999.000 * 8 =
+ * ((4 samples/48,000) * (13,500,000 * 8) * 16 - 1) * 8
+ */
+ cx18_av_write4(cx, 0x128, 0xa01193f8);
+ break;
+ }
+ }
+
+ state->audclk_freq = freq;
+
+ return 0;
+}
+
+void cx18_av_audio_set_path(struct cx18 *cx)
+{
+ struct cx18_av_state *state = &cx->av_state;
+ u8 v;
+
+ /* stop microcontroller */
+ v = cx18_av_read(cx, 0x803) & ~0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
+
+ /* assert soft reset */
+ v = cx18_av_read(cx, 0x810) | 0x01;
+ cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
+
+ /* Mute everything to prevent the PFFT! */
+ cx18_av_write(cx, 0x8d3, 0x1f);
+
+ if (state->aud_input <= CX18_AV_AUDIO_SERIAL2) {
+ /* Set Path1 to Serial Audio Input */
+ cx18_av_write4(cx, 0x8d0, 0x01011012);
+
+ /* The microcontroller should not be started for the
+ * non-tuner inputs: autodetection is specific for
+ * TV audio. */
+ } else {
+ /* Set Path1 to Analog Demod Main Channel */
+ cx18_av_write4(cx, 0x8d0, 0x1f063870);
+ }
+
+ set_audclk_freq(cx, state->audclk_freq);
+
+ /* deassert soft reset */
+ v = cx18_av_read(cx, 0x810) & ~0x01;
+ cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
+
+ if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
+ /* When the microcontroller detects the
+ * audio format, it will unmute the lines */
+ v = cx18_av_read(cx, 0x803) | 0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
+ }
+}
+
+static void set_volume(struct cx18 *cx, int volume)
+{
+ /* First convert the volume to msp3400 values (0-127) */
+ int vol = volume >> 9;
+ /* now scale it up to cx18_av values
+ * -114dB to -96dB maps to 0
+ * this should be 19, but in my testing that was 4dB too loud */
+ if (vol <= 23)
+ vol = 0;
+ else
+ vol -= 23;
+
+ /* PATH1_VOLUME */
+ cx18_av_write(cx, 0x8d4, 228 - (vol * 2));
+}
+
+static void set_bass(struct cx18 *cx, int bass)
+{
+ /* PATH1_EQ_BASS_VOL */
+ cx18_av_and_or(cx, 0x8d9, ~0x3f, 48 - (bass * 48 / 0xffff));
+}
+
+static void set_treble(struct cx18 *cx, int treble)
+{
+ /* PATH1_EQ_TREBLE_VOL */
+ cx18_av_and_or(cx, 0x8db, ~0x3f, 48 - (treble * 48 / 0xffff));
+}
+
+static void set_balance(struct cx18 *cx, int balance)
+{
+ int bal = balance >> 8;
+ if (bal > 0x80) {
+ /* PATH1_BAL_LEFT */
+ cx18_av_and_or(cx, 0x8d5, 0x7f, 0x80);
+ /* PATH1_BAL_LEVEL */
+ cx18_av_and_or(cx, 0x8d5, ~0x7f, bal & 0x7f);
+ } else {
+ /* PATH1_BAL_LEFT */
+ cx18_av_and_or(cx, 0x8d5, 0x7f, 0x00);
+ /* PATH1_BAL_LEVEL */
+ cx18_av_and_or(cx, 0x8d5, ~0x7f, 0x80 - bal);
+ }
+}
+
+static void set_mute(struct cx18 *cx, int mute)
+{
+ struct cx18_av_state *state = &cx->av_state;
+ u8 v;
+
+ if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
+ /* Must turn off microcontroller in order to mute sound.
+ * Not sure if this is the best method, but it does work.
+ * If the microcontroller is running, then it will undo any
+ * changes to the mute register. */
+ v = cx18_av_read(cx, 0x803);
+ if (mute) {
+ /* disable microcontroller */
+ v &= ~0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
+ cx18_av_write(cx, 0x8d3, 0x1f);
+ } else {
+ /* enable microcontroller */
+ v |= 0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
+ }
+ } else {
+ /* SRC1_MUTE_EN */
+ cx18_av_and_or(cx, 0x8d3, ~0x2, mute ? 0x02 : 0x00);
+ }
+}
+
+int cx18_av_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ struct cx18_av_state *state = &cx->av_state;
+ int retval;
+ u8 v;
+
+ if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
+ v = cx18_av_read(cx, 0x803) & ~0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
+ cx18_av_write(cx, 0x8d3, 0x1f);
+ }
+ v = cx18_av_read(cx, 0x810) | 0x1;
+ cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
+
+ retval = set_audclk_freq(cx, freq);
+
+ v = cx18_av_read(cx, 0x810) & ~0x1;
+ cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
+ if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
+ v = cx18_av_read(cx, 0x803) | 0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
+ }
+ return retval;
+}
+
+static int cx18_av_audio_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ set_volume(cx, ctrl->val);
+ break;
+ case V4L2_CID_AUDIO_BASS:
+ set_bass(cx, ctrl->val);
+ break;
+ case V4L2_CID_AUDIO_TREBLE:
+ set_treble(cx, ctrl->val);
+ break;
+ case V4L2_CID_AUDIO_BALANCE:
+ set_balance(cx, ctrl->val);
+ break;
+ case V4L2_CID_AUDIO_MUTE:
+ set_mute(cx, ctrl->val);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+const struct v4l2_ctrl_ops cx18_av_audio_ctrl_ops = {
+ .s_ctrl = cx18_av_audio_s_ctrl,
+};
diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c
new file mode 100644
index 00000000..f164b7f6
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-av-core.c
@@ -0,0 +1,1401 @@
+/*
+ * cx18 ADEC audio functions
+ *
+ * Derived from cx25840-core.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <media/v4l2-chip-ident.h>
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-cards.h"
+
+int cx18_av_write(struct cx18 *cx, u16 addr, u8 value)
+{
+ u32 reg = 0xc40000 + (addr & ~3);
+ u32 mask = 0xff;
+ int shift = (addr & 3) * 8;
+ u32 x = cx18_read_reg(cx, reg);
+
+ x = (x & ~(mask << shift)) | ((u32)value << shift);
+ cx18_write_reg(cx, x, reg);
+ return 0;
+}
+
+int cx18_av_write_expect(struct cx18 *cx, u16 addr, u8 value, u8 eval, u8 mask)
+{
+ u32 reg = 0xc40000 + (addr & ~3);
+ int shift = (addr & 3) * 8;
+ u32 x = cx18_read_reg(cx, reg);
+
+ x = (x & ~((u32)0xff << shift)) | ((u32)value << shift);
+ cx18_write_reg_expect(cx, x, reg,
+ ((u32)eval << shift), ((u32)mask << shift));
+ return 0;
+}
+
+int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value)
+{
+ cx18_write_reg(cx, value, 0xc40000 + addr);
+ return 0;
+}
+
+int
+cx18_av_write4_expect(struct cx18 *cx, u16 addr, u32 value, u32 eval, u32 mask)
+{
+ cx18_write_reg_expect(cx, value, 0xc40000 + addr, eval, mask);
+ return 0;
+}
+
+int cx18_av_write4_noretry(struct cx18 *cx, u16 addr, u32 value)
+{
+ cx18_write_reg_noretry(cx, value, 0xc40000 + addr);
+ return 0;
+}
+
+u8 cx18_av_read(struct cx18 *cx, u16 addr)
+{
+ u32 x = cx18_read_reg(cx, 0xc40000 + (addr & ~3));
+ int shift = (addr & 3) * 8;
+
+ return (x >> shift) & 0xff;
+}
+
+u32 cx18_av_read4(struct cx18 *cx, u16 addr)
+{
+ return cx18_read_reg(cx, 0xc40000 + addr);
+}
+
+int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned and_mask,
+ u8 or_value)
+{
+ return cx18_av_write(cx, addr,
+ (cx18_av_read(cx, addr) & and_mask) |
+ or_value);
+}
+
+int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 and_mask,
+ u32 or_value)
+{
+ return cx18_av_write4(cx, addr,
+ (cx18_av_read4(cx, addr) & and_mask) |
+ or_value);
+}
+
+static void cx18_av_init(struct cx18 *cx)
+{
+ /*
+ * The crystal freq used in calculations in this driver will be
+ * 28.636360 MHz.
+ * Aim to run the PLLs' VCOs near 400 MHz to minimze errors.
+ */
+
+ /*
+ * VDCLK Integer = 0x0f, Post Divider = 0x04
+ * AIMCLK Integer = 0x0e, Post Divider = 0x16
+ */
+ cx18_av_write4(cx, CXADEC_PLL_CTRL1, 0x160e040f);
+
+ /* VDCLK Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz before post divide */
+ cx18_av_write4(cx, CXADEC_VID_PLL_FRAC, 0x002be2fe);
+
+ /* AIMCLK Fraction = 0x05227ad */
+ /* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz pre post-div*/
+ cx18_av_write4(cx, CXADEC_AUX_PLL_FRAC, 0x005227ad);
+
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */
+ cx18_av_write(cx, CXADEC_I2S_MCLK, 0x56);
+}
+
+static void cx18_av_initialize(struct v4l2_subdev *sd)
+{
+ struct cx18_av_state *state = to_cx18_av_state(sd);
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ int default_volume;
+ u32 v;
+
+ cx18_av_loadfw(cx);
+ /* Stop 8051 code execution */
+ cx18_av_write4_expect(cx, CXADEC_DL_CTL, 0x03000000,
+ 0x03000000, 0x13000000);
+
+ /* initallize the PLL by toggling sleep bit */
+ v = cx18_av_read4(cx, CXADEC_HOST_REG1);
+ /* enable sleep mode - register appears to be read only... */
+ cx18_av_write4_expect(cx, CXADEC_HOST_REG1, v | 1, v, 0xfffe);
+ /* disable sleep mode */
+ cx18_av_write4_expect(cx, CXADEC_HOST_REG1, v & 0xfffe,
+ v & 0xfffe, 0xffff);
+
+ /* initialize DLLs */
+ v = cx18_av_read4(cx, CXADEC_DLL1_DIAG_CTRL) & 0xE1FFFEFF;
+ /* disable FLD */
+ cx18_av_write4(cx, CXADEC_DLL1_DIAG_CTRL, v);
+ /* enable FLD */
+ cx18_av_write4(cx, CXADEC_DLL1_DIAG_CTRL, v | 0x10000100);
+
+ v = cx18_av_read4(cx, CXADEC_DLL2_DIAG_CTRL) & 0xE1FFFEFF;
+ /* disable FLD */
+ cx18_av_write4(cx, CXADEC_DLL2_DIAG_CTRL, v);
+ /* enable FLD */
+ cx18_av_write4(cx, CXADEC_DLL2_DIAG_CTRL, v | 0x06000100);
+
+ /* set analog bias currents. Set Vreg to 1.20V. */
+ cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL1, 0x000A1802);
+
+ v = cx18_av_read4(cx, CXADEC_AFE_DIAG_CTRL3) | 1;
+ /* enable TUNE_FIL_RST */
+ cx18_av_write4_expect(cx, CXADEC_AFE_DIAG_CTRL3, v, v, 0x03009F0F);
+ /* disable TUNE_FIL_RST */
+ cx18_av_write4_expect(cx, CXADEC_AFE_DIAG_CTRL3,
+ v & 0xFFFFFFFE, v & 0xFFFFFFFE, 0x03009F0F);
+
+ /* enable 656 output */
+ cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x040C00);
+
+ /* video output drive strength */
+ cx18_av_and_or4(cx, CXADEC_PIN_CTRL2, ~0, 0x2);
+
+ /* reset video */
+ cx18_av_write4(cx, CXADEC_SOFT_RST_CTRL, 0x8000);
+ cx18_av_write4(cx, CXADEC_SOFT_RST_CTRL, 0);
+
+ /*
+ * Disable Video Auto-config of the Analog Front End and Video PLL.
+ *
+ * Since we only use BT.656 pixel mode, which works for both 525 and 625
+ * line systems, it's just easier for us to set registers
+ * 0x102 (CXADEC_CHIP_CTRL), 0x104-0x106 (CXADEC_AFE_CTRL),
+ * 0x108-0x109 (CXADEC_PLL_CTRL1), and 0x10c-0x10f (CXADEC_VID_PLL_FRAC)
+ * ourselves, than to run around cleaning up after the auto-config.
+ *
+ * (Note: my CX23418 chip doesn't seem to let the ACFG_DIS bit
+ * get set to 1, but OTOH, it doesn't seem to do AFE and VID PLL
+ * autoconfig either.)
+ *
+ * As a default, also turn off Dual mode for ADC2 and set ADC2 to CH3.
+ */
+ cx18_av_and_or4(cx, CXADEC_CHIP_CTRL, 0xFFFBFFFF, 0x00120000);
+
+ /* Setup the Video and and Aux/Audio PLLs */
+ cx18_av_init(cx);
+
+ /* set video to auto-detect */
+ /* Clear bits 11-12 to enable slow locking mode. Set autodetect mode */
+ /* set the comb notch = 1 */
+ cx18_av_and_or4(cx, CXADEC_MODE_CTRL, 0xFFF7E7F0, 0x02040800);
+
+ /* Enable wtw_en in CRUSH_CTRL (Set bit 22) */
+ /* Enable maj_sel in CRUSH_CTRL (Set bit 20) */
+ cx18_av_and_or4(cx, CXADEC_CRUSH_CTRL, ~0, 0x00500000);
+
+ /* Set VGA_TRACK_RANGE to 0x20 */
+ cx18_av_and_or4(cx, CXADEC_DFE_CTRL2, 0xFFFF00FF, 0x00002000);
+
+ /*
+ * Initial VBI setup
+ * VIP-1.1, 10 bit mode, enable Raw, disable sliced,
+ * don't clamp raw samples when codes are in use, 1 byte user D-words,
+ * IDID0 has line #, RP code V bit transition on VBLANK, data during
+ * blanking intervals
+ */
+ cx18_av_write4(cx, CXADEC_OUT_CTRL1, 0x4013252e);
+
+ /* Set the video input.
+ The setting in MODE_CTRL gets lost when we do the above setup */
+ /* EncSetSignalStd(dwDevNum, pEnc->dwSigStd); */
+ /* EncSetVideoInput(dwDevNum, pEnc->VidIndSelection); */
+
+ /*
+ * Analog Front End (AFE)
+ * Default to luma on ch1/ADC1, chroma on ch2/ADC2, SIF on ch3/ADC2
+ * bypass_ch[1-3] use filter
+ * droop_comp_ch[1-3] disable
+ * clamp_en_ch[1-3] disable
+ * aud_in_sel ADC2
+ * luma_in_sel ADC1
+ * chroma_in_sel ADC2
+ * clamp_sel_ch[2-3] midcode
+ * clamp_sel_ch1 video decoder
+ * vga_sel_ch3 audio decoder
+ * vga_sel_ch[1-2] video decoder
+ * half_bw_ch[1-3] disable
+ * +12db_ch[1-3] disable
+ */
+ cx18_av_and_or4(cx, CXADEC_AFE_CTRL, 0xFF000000, 0x00005D00);
+
+/* if(dwEnable && dw3DCombAvailable) { */
+/* CxDevWrReg(CXADEC_SRC_COMB_CFG, 0x7728021F); */
+/* } else { */
+/* CxDevWrReg(CXADEC_SRC_COMB_CFG, 0x6628021F); */
+/* } */
+ cx18_av_write4(cx, CXADEC_SRC_COMB_CFG, 0x6628021F);
+ default_volume = cx18_av_read(cx, 0x8d4);
+ /*
+ * Enforce the legacy volume scale mapping limits to avoid
+ * -ERANGE errors when initializing the volume control
+ */
+ if (default_volume > 228) {
+ /* Bottom out at -96 dB, v4l2 vol range 0x2e00-0x2fff */
+ default_volume = 228;
+ cx18_av_write(cx, 0x8d4, 228);
+ } else if (default_volume < 20) {
+ /* Top out at + 8 dB, v4l2 vol range 0xfe00-0xffff */
+ default_volume = 20;
+ cx18_av_write(cx, 0x8d4, 20);
+ }
+ default_volume = (((228 - default_volume) >> 1) + 23) << 9;
+ state->volume->cur.val = state->volume->default_value = default_volume;
+ v4l2_ctrl_handler_setup(&state->hdl);
+}
+
+static int cx18_av_reset(struct v4l2_subdev *sd, u32 val)
+{
+ cx18_av_initialize(sd);
+ return 0;
+}
+
+static int cx18_av_load_fw(struct v4l2_subdev *sd)
+{
+ struct cx18_av_state *state = to_cx18_av_state(sd);
+
+ if (!state->is_initialized) {
+ /* initialize on first use */
+ state->is_initialized = 1;
+ cx18_av_initialize(sd);
+ }
+ return 0;
+}
+
+void cx18_av_std_setup(struct cx18 *cx)
+{
+ struct cx18_av_state *state = &cx->av_state;
+ struct v4l2_subdev *sd = &state->sd;
+ v4l2_std_id std = state->std;
+
+ /*
+ * Video ADC crystal clock to pixel clock SRC decimation ratio
+ * 28.636360 MHz/13.5 Mpps * 256 = 0x21f.07b
+ */
+ const int src_decimation = 0x21f;
+
+ int hblank, hactive, burst, vblank, vactive, sc;
+ int vblank656;
+ int luma_lpf, uv_lpf, comb;
+ u32 pll_int, pll_frac, pll_post;
+
+ /* datasheet startup, step 8d */
+ if (std & ~V4L2_STD_NTSC)
+ cx18_av_write(cx, 0x49f, 0x11);
+ else
+ cx18_av_write(cx, 0x49f, 0x14);
+
+ /*
+ * Note: At the end of a field, there are 3 sets of half line duration
+ * (double horizontal rate) pulses:
+ *
+ * 5 (625) or 6 (525) half-lines to blank for the vertical retrace
+ * 5 (625) or 6 (525) vertical sync pulses of half line duration
+ * 5 (625) or 6 (525) half-lines of equalization pulses
+ */
+ if (std & V4L2_STD_625_50) {
+ /*
+ * The following relationships of half line counts should hold:
+ * 625 = vblank656 + vactive
+ * 10 = vblank656 - vblank = vsync pulses + equalization pulses
+ *
+ * vblank656: half lines after line 625/mid-313 of blanked video
+ * vblank: half lines, after line 5/317, of blanked video
+ * vactive: half lines of active video +
+ * 5 half lines after the end of active video
+ *
+ * As far as I can tell:
+ * vblank656 starts counting from the falling edge of the first
+ * vsync pulse (start of line 1 or mid-313)
+ * vblank starts counting from the after the 5 vsync pulses and
+ * 5 or 4 equalization pulses (start of line 6 or 318)
+ *
+ * For 625 line systems the driver will extract VBI information
+ * from lines 6-23 and lines 318-335 (but the slicer can only
+ * handle 17 lines, not the 18 in the vblank region).
+ * In addition, we need vblank656 and vblank to be one whole
+ * line longer, to cover line 24 and 336, so the SAV/EAV RP
+ * codes get generated such that the encoder can actually
+ * extract line 23 & 335 (WSS). We'll lose 1 line in each field
+ * at the top of the screen.
+ *
+ * It appears the 5 half lines that happen after active
+ * video must be included in vactive (579 instead of 574),
+ * otherwise the colors get badly displayed in various regions
+ * of the screen. I guess the chroma comb filter gets confused
+ * without them (at least when a PVR-350 is the PAL source).
+ */
+ vblank656 = 48; /* lines 1 - 24 & 313 - 336 */
+ vblank = 38; /* lines 6 - 24 & 318 - 336 */
+ vactive = 579; /* lines 24 - 313 & 337 - 626 */
+
+ /*
+ * For a 13.5 Mpps clock and 15,625 Hz line rate, a line is
+ * is 864 pixels = 720 active + 144 blanking. ITU-R BT.601
+ * specifies 12 luma clock periods or ~ 0.9 * 13.5 Mpps after
+ * the end of active video to start a horizontal line, so that
+ * leaves 132 pixels of hblank to ignore.
+ */
+ hblank = 132;
+ hactive = 720;
+
+ /*
+ * Burst gate delay (for 625 line systems)
+ * Hsync leading edge to color burst rise = 5.6 us
+ * Color burst width = 2.25 us
+ * Gate width = 4 pixel clocks
+ * (5.6 us + 2.25/2 us) * 13.5 Mpps + 4/2 clocks = 92.79 clocks
+ */
+ burst = 93;
+ luma_lpf = 2;
+ if (std & V4L2_STD_PAL) {
+ uv_lpf = 1;
+ comb = 0x20;
+ /* sc = 4433618.75 * src_decimation/28636360 * 2^13 */
+ sc = 688700;
+ } else if (std == V4L2_STD_PAL_Nc) {
+ uv_lpf = 1;
+ comb = 0x20;
+ /* sc = 3582056.25 * src_decimation/28636360 * 2^13 */
+ sc = 556422;
+ } else { /* SECAM */
+ uv_lpf = 0;
+ comb = 0;
+ /* (fr + fb)/2 = (4406260 + 4250000)/2 = 4328130 */
+ /* sc = 4328130 * src_decimation/28636360 * 2^13 */
+ sc = 672314;
+ }
+ } else {
+ /*
+ * The following relationships of half line counts should hold:
+ * 525 = prevsync + vblank656 + vactive
+ * 12 = vblank656 - vblank = vsync pulses + equalization pulses
+ *
+ * prevsync: 6 half-lines before the vsync pulses
+ * vblank656: half lines, after line 3/mid-266, of blanked video
+ * vblank: half lines, after line 9/272, of blanked video
+ * vactive: half lines of active video
+ *
+ * As far as I can tell:
+ * vblank656 starts counting from the falling edge of the first
+ * vsync pulse (start of line 4 or mid-266)
+ * vblank starts counting from the after the 6 vsync pulses and
+ * 6 or 5 equalization pulses (start of line 10 or 272)
+ *
+ * For 525 line systems the driver will extract VBI information
+ * from lines 10-21 and lines 273-284.
+ */
+ vblank656 = 38; /* lines 4 - 22 & 266 - 284 */
+ vblank = 26; /* lines 10 - 22 & 272 - 284 */
+ vactive = 481; /* lines 23 - 263 & 285 - 525 */
+
+ /*
+ * For a 13.5 Mpps clock and 15,734.26 Hz line rate, a line is
+ * is 858 pixels = 720 active + 138 blanking. The Hsync leading
+ * edge should happen 1.2 us * 13.5 Mpps ~= 16 pixels after the
+ * end of active video, leaving 122 pixels of hblank to ignore
+ * before active video starts.
+ */
+ hactive = 720;
+ hblank = 122;
+ luma_lpf = 1;
+ uv_lpf = 1;
+
+ /*
+ * Burst gate delay (for 525 line systems)
+ * Hsync leading edge to color burst rise = 5.3 us
+ * Color burst width = 2.5 us
+ * Gate width = 4 pixel clocks
+ * (5.3 us + 2.5/2 us) * 13.5 Mpps + 4/2 clocks = 90.425 clocks
+ */
+ if (std == V4L2_STD_PAL_60) {
+ burst = 90;
+ luma_lpf = 2;
+ comb = 0x20;
+ /* sc = 4433618.75 * src_decimation/28636360 * 2^13 */
+ sc = 688700;
+ } else if (std == V4L2_STD_PAL_M) {
+ /* The 97 needs to be verified against PAL-M timings */
+ burst = 97;
+ comb = 0x20;
+ /* sc = 3575611.49 * src_decimation/28636360 * 2^13 */
+ sc = 555421;
+ } else {
+ burst = 90;
+ comb = 0x66;
+ /* sc = 3579545.45.. * src_decimation/28636360 * 2^13 */
+ sc = 556032;
+ }
+ }
+
+ /* DEBUG: Displays configured PLL frequency */
+ pll_int = cx18_av_read(cx, 0x108);
+ pll_frac = cx18_av_read4(cx, 0x10c) & 0x1ffffff;
+ pll_post = cx18_av_read(cx, 0x109);
+ CX18_DEBUG_INFO_DEV(sd, "PLL regs = int: %u, frac: %u, post: %u\n",
+ pll_int, pll_frac, pll_post);
+
+ if (pll_post) {
+ int fsc, pll;
+ u64 tmp;
+
+ pll = (28636360L * ((((u64)pll_int) << 25) + pll_frac)) >> 25;
+ pll /= pll_post;
+ CX18_DEBUG_INFO_DEV(sd, "Video PLL = %d.%06d MHz\n",
+ pll / 1000000, pll % 1000000);
+ CX18_DEBUG_INFO_DEV(sd, "Pixel rate = %d.%06d Mpixel/sec\n",
+ pll / 8000000, (pll / 8) % 1000000);
+
+ CX18_DEBUG_INFO_DEV(sd, "ADC XTAL/pixel clock decimation ratio "
+ "= %d.%03d\n", src_decimation / 256,
+ ((src_decimation % 256) * 1000) / 256);
+
+ tmp = 28636360 * (u64) sc;
+ do_div(tmp, src_decimation);
+ fsc = tmp >> 13;
+ CX18_DEBUG_INFO_DEV(sd,
+ "Chroma sub-carrier initial freq = %d.%06d "
+ "MHz\n", fsc / 1000000, fsc % 1000000);
+
+ CX18_DEBUG_INFO_DEV(sd, "hblank %i, hactive %i, vblank %i, "
+ "vactive %i, vblank656 %i, src_dec %i, "
+ "burst 0x%02x, luma_lpf %i, uv_lpf %i, "
+ "comb 0x%02x, sc 0x%06x\n",
+ hblank, hactive, vblank, vactive, vblank656,
+ src_decimation, burst, luma_lpf, uv_lpf,
+ comb, sc);
+ }
+
+ /* Sets horizontal blanking delay and active lines */
+ cx18_av_write(cx, 0x470, hblank);
+ cx18_av_write(cx, 0x471, 0xff & (((hblank >> 8) & 0x3) |
+ (hactive << 4)));
+ cx18_av_write(cx, 0x472, hactive >> 4);
+
+ /* Sets burst gate delay */
+ cx18_av_write(cx, 0x473, burst);
+
+ /* Sets vertical blanking delay and active duration */
+ cx18_av_write(cx, 0x474, vblank);
+ cx18_av_write(cx, 0x475, 0xff & (((vblank >> 8) & 0x3) |
+ (vactive << 4)));
+ cx18_av_write(cx, 0x476, vactive >> 4);
+ cx18_av_write(cx, 0x477, vblank656);
+
+ /* Sets src decimation rate */
+ cx18_av_write(cx, 0x478, 0xff & src_decimation);
+ cx18_av_write(cx, 0x479, 0xff & (src_decimation >> 8));
+
+ /* Sets Luma and UV Low pass filters */
+ cx18_av_write(cx, 0x47a, luma_lpf << 6 | ((uv_lpf << 4) & 0x30));
+
+ /* Enables comb filters */
+ cx18_av_write(cx, 0x47b, comb);
+
+ /* Sets SC Step*/
+ cx18_av_write(cx, 0x47c, sc);
+ cx18_av_write(cx, 0x47d, 0xff & sc >> 8);
+ cx18_av_write(cx, 0x47e, 0xff & sc >> 16);
+
+ if (std & V4L2_STD_625_50) {
+ state->slicer_line_delay = 1;
+ state->slicer_line_offset = (6 + state->slicer_line_delay - 2);
+ } else {
+ state->slicer_line_delay = 0;
+ state->slicer_line_offset = (10 + state->slicer_line_delay - 2);
+ }
+ cx18_av_write(cx, 0x47f, state->slicer_line_delay);
+}
+
+static void input_change(struct cx18 *cx)
+{
+ struct cx18_av_state *state = &cx->av_state;
+ v4l2_std_id std = state->std;
+ u8 v;
+
+ /* Follow step 8c and 8d of section 3.16 in the cx18_av datasheet */
+ cx18_av_write(cx, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11);
+ cx18_av_and_or(cx, 0x401, ~0x60, 0);
+ cx18_av_and_or(cx, 0x401, ~0x60, 0x60);
+
+ if (std & V4L2_STD_525_60) {
+ if (std == V4L2_STD_NTSC_M_JP) {
+ /* Japan uses EIAJ audio standard */
+ cx18_av_write_expect(cx, 0x808, 0xf7, 0xf7, 0xff);
+ cx18_av_write_expect(cx, 0x80b, 0x02, 0x02, 0x3f);
+ } else if (std == V4L2_STD_NTSC_M_KR) {
+ /* South Korea uses A2 audio standard */
+ cx18_av_write_expect(cx, 0x808, 0xf8, 0xf8, 0xff);
+ cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f);
+ } else {
+ /* Others use the BTSC audio standard */
+ cx18_av_write_expect(cx, 0x808, 0xf6, 0xf6, 0xff);
+ cx18_av_write_expect(cx, 0x80b, 0x01, 0x01, 0x3f);
+ }
+ } else if (std & V4L2_STD_PAL) {
+ /* Follow tuner change procedure for PAL */
+ cx18_av_write_expect(cx, 0x808, 0xff, 0xff, 0xff);
+ cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f);
+ } else if (std & V4L2_STD_SECAM) {
+ /* Select autodetect for SECAM */
+ cx18_av_write_expect(cx, 0x808, 0xff, 0xff, 0xff);
+ cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f);
+ }
+
+ v = cx18_av_read(cx, 0x803);
+ if (v & 0x10) {
+ /* restart audio decoder microcontroller */
+ v &= ~0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
+ v |= 0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
+ }
+}
+
+static int cx18_av_s_frequency(struct v4l2_subdev *sd,
+ struct v4l2_frequency *freq)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ input_change(cx);
+ return 0;
+}
+
+static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
+ enum cx18_av_audio_input aud_input)
+{
+ struct cx18_av_state *state = &cx->av_state;
+ struct v4l2_subdev *sd = &state->sd;
+
+ enum analog_signal_type {
+ NONE, CVBS, Y, C, SIF, Pb, Pr
+ } ch[3] = {NONE, NONE, NONE};
+
+ u8 afe_mux_cfg;
+ u8 adc2_cfg;
+ u8 input_mode;
+ u32 afe_cfg;
+ int i;
+
+ CX18_DEBUG_INFO_DEV(sd, "decoder set video input %d, audio input %d\n",
+ vid_input, aud_input);
+
+ if (vid_input >= CX18_AV_COMPOSITE1 &&
+ vid_input <= CX18_AV_COMPOSITE8) {
+ afe_mux_cfg = 0xf0 + (vid_input - CX18_AV_COMPOSITE1);
+ ch[0] = CVBS;
+ input_mode = 0x0;
+ } else if (vid_input >= CX18_AV_COMPONENT_LUMA1) {
+ int luma = vid_input & 0xf000;
+ int r_chroma = vid_input & 0xf0000;
+ int b_chroma = vid_input & 0xf00000;
+
+ if ((vid_input & ~0xfff000) ||
+ luma < CX18_AV_COMPONENT_LUMA1 ||
+ luma > CX18_AV_COMPONENT_LUMA8 ||
+ r_chroma < CX18_AV_COMPONENT_R_CHROMA4 ||
+ r_chroma > CX18_AV_COMPONENT_R_CHROMA6 ||
+ b_chroma < CX18_AV_COMPONENT_B_CHROMA7 ||
+ b_chroma > CX18_AV_COMPONENT_B_CHROMA8) {
+ CX18_ERR_DEV(sd, "0x%06x is not a valid video input!\n",
+ vid_input);
+ return -EINVAL;
+ }
+ afe_mux_cfg = (luma - CX18_AV_COMPONENT_LUMA1) >> 12;
+ ch[0] = Y;
+ afe_mux_cfg |= (r_chroma - CX18_AV_COMPONENT_R_CHROMA4) >> 12;
+ ch[1] = Pr;
+ afe_mux_cfg |= (b_chroma - CX18_AV_COMPONENT_B_CHROMA7) >> 14;
+ ch[2] = Pb;
+ input_mode = 0x6;
+ } else {
+ int luma = vid_input & 0xf0;
+ int chroma = vid_input & 0xf00;
+
+ if ((vid_input & ~0xff0) ||
+ luma < CX18_AV_SVIDEO_LUMA1 ||
+ luma > CX18_AV_SVIDEO_LUMA8 ||
+ chroma < CX18_AV_SVIDEO_CHROMA4 ||
+ chroma > CX18_AV_SVIDEO_CHROMA8) {
+ CX18_ERR_DEV(sd, "0x%06x is not a valid video input!\n",
+ vid_input);
+ return -EINVAL;
+ }
+ afe_mux_cfg = 0xf0 + ((luma - CX18_AV_SVIDEO_LUMA1) >> 4);
+ ch[0] = Y;
+ if (chroma >= CX18_AV_SVIDEO_CHROMA7) {
+ afe_mux_cfg &= 0x3f;
+ afe_mux_cfg |= (chroma - CX18_AV_SVIDEO_CHROMA7) >> 2;
+ ch[2] = C;
+ } else {
+ afe_mux_cfg &= 0xcf;
+ afe_mux_cfg |= (chroma - CX18_AV_SVIDEO_CHROMA4) >> 4;
+ ch[1] = C;
+ }
+ input_mode = 0x2;
+ }
+
+ switch (aud_input) {
+ case CX18_AV_AUDIO_SERIAL1:
+ case CX18_AV_AUDIO_SERIAL2:
+ /* do nothing, use serial audio input */
+ break;
+ case CX18_AV_AUDIO4:
+ afe_mux_cfg &= ~0x30;
+ ch[1] = SIF;
+ break;
+ case CX18_AV_AUDIO5:
+ afe_mux_cfg = (afe_mux_cfg & ~0x30) | 0x10;
+ ch[1] = SIF;
+ break;
+ case CX18_AV_AUDIO6:
+ afe_mux_cfg = (afe_mux_cfg & ~0x30) | 0x20;
+ ch[1] = SIF;
+ break;
+ case CX18_AV_AUDIO7:
+ afe_mux_cfg &= ~0xc0;
+ ch[2] = SIF;
+ break;
+ case CX18_AV_AUDIO8:
+ afe_mux_cfg = (afe_mux_cfg & ~0xc0) | 0x40;
+ ch[2] = SIF;
+ break;
+
+ default:
+ CX18_ERR_DEV(sd, "0x%04x is not a valid audio input!\n",
+ aud_input);
+ return -EINVAL;
+ }
+
+ /* Set up analog front end multiplexers */
+ cx18_av_write_expect(cx, 0x103, afe_mux_cfg, afe_mux_cfg, 0xf7);
+ /* Set INPUT_MODE to Composite, S-Video, or Component */
+ cx18_av_and_or(cx, 0x401, ~0x6, input_mode);
+
+ /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */
+ adc2_cfg = cx18_av_read(cx, 0x102);
+ if (ch[2] == NONE)
+ adc2_cfg &= ~0x2; /* No sig on CH3, set ADC2 to CH2 for input */
+ else
+ adc2_cfg |= 0x2; /* Signal on CH3, set ADC2 to CH3 for input */
+
+ /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */
+ if (ch[1] != NONE && ch[2] != NONE)
+ adc2_cfg |= 0x4; /* Set dual mode */
+ else
+ adc2_cfg &= ~0x4; /* Clear dual mode */
+ cx18_av_write_expect(cx, 0x102, adc2_cfg, adc2_cfg, 0x17);
+
+ /* Configure the analog front end */
+ afe_cfg = cx18_av_read4(cx, CXADEC_AFE_CTRL);
+ afe_cfg &= 0xff000000;
+ afe_cfg |= 0x00005000; /* CHROMA_IN, AUD_IN: ADC2; LUMA_IN: ADC1 */
+ if (ch[1] != NONE && ch[2] != NONE)
+ afe_cfg |= 0x00000030; /* half_bw_ch[2-3] since in dual mode */
+
+ for (i = 0; i < 3; i++) {
+ switch (ch[i]) {
+ default:
+ case NONE:
+ /* CLAMP_SEL = Fixed to midcode clamp level */
+ afe_cfg |= (0x00000200 << i);
+ break;
+ case CVBS:
+ case Y:
+ if (i > 0)
+ afe_cfg |= 0x00002000; /* LUMA_IN_SEL: ADC2 */
+ break;
+ case C:
+ case Pb:
+ case Pr:
+ /* CLAMP_SEL = Fixed to midcode clamp level */
+ afe_cfg |= (0x00000200 << i);
+ if (i == 0 && ch[i] == C)
+ afe_cfg &= ~0x00001000; /* CHROMA_IN_SEL ADC1 */
+ break;
+ case SIF:
+ /*
+ * VGA_GAIN_SEL = Audio Decoder
+ * CLAMP_SEL = Fixed to midcode clamp level
+ */
+ afe_cfg |= (0x00000240 << i);
+ if (i == 0)
+ afe_cfg &= ~0x00004000; /* AUD_IN_SEL ADC1 */
+ break;
+ }
+ }
+
+ cx18_av_write4(cx, CXADEC_AFE_CTRL, afe_cfg);
+
+ state->vid_input = vid_input;
+ state->aud_input = aud_input;
+ cx18_av_audio_set_path(cx);
+ input_change(cx);
+ return 0;
+}
+
+static int cx18_av_s_video_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct cx18_av_state *state = to_cx18_av_state(sd);
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ return set_input(cx, input, state->aud_input);
+}
+
+static int cx18_av_s_audio_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct cx18_av_state *state = to_cx18_av_state(sd);
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ return set_input(cx, state->vid_input, input);
+}
+
+static int cx18_av_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct cx18_av_state *state = to_cx18_av_state(sd);
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ u8 vpres;
+ u8 mode;
+ int val = 0;
+
+ if (state->radio)
+ return 0;
+
+ vpres = cx18_av_read(cx, 0x40e) & 0x20;
+ vt->signal = vpres ? 0xffff : 0x0;
+
+ vt->capability |=
+ V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
+ V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
+
+ mode = cx18_av_read(cx, 0x804);
+
+ /* get rxsubchans and audmode */
+ if ((mode & 0xf) == 1)
+ val |= V4L2_TUNER_SUB_STEREO;
+ else
+ val |= V4L2_TUNER_SUB_MONO;
+
+ if (mode == 2 || mode == 4)
+ val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+
+ if (mode & 0x10)
+ val |= V4L2_TUNER_SUB_SAP;
+
+ vt->rxsubchans = val;
+ vt->audmode = state->audmode;
+ return 0;
+}
+
+static int cx18_av_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct cx18_av_state *state = to_cx18_av_state(sd);
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ u8 v;
+
+ if (state->radio)
+ return 0;
+
+ v = cx18_av_read(cx, 0x809);
+ v &= ~0xf;
+
+ switch (vt->audmode) {
+ case V4L2_TUNER_MODE_MONO:
+ /* mono -> mono
+ stereo -> mono
+ bilingual -> lang1 */
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ case V4L2_TUNER_MODE_LANG1:
+ /* mono -> mono
+ stereo -> stereo
+ bilingual -> lang1 */
+ v |= 0x4;
+ break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ /* mono -> mono
+ stereo -> stereo
+ bilingual -> lang1/lang2 */
+ v |= 0x7;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ /* mono -> mono
+ stereo -> stereo
+ bilingual -> lang2 */
+ v |= 0x1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ cx18_av_write_expect(cx, 0x809, v, v, 0xff);
+ state->audmode = vt->audmode;
+ return 0;
+}
+
+static int cx18_av_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
+{
+ struct cx18_av_state *state = to_cx18_av_state(sd);
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+ u8 fmt = 0; /* zero is autodetect */
+ u8 pal_m = 0;
+
+ if (state->radio == 0 && state->std == norm)
+ return 0;
+
+ state->radio = 0;
+ state->std = norm;
+
+ /* First tests should be against specific std */
+ if (state->std == V4L2_STD_NTSC_M_JP) {
+ fmt = 0x2;
+ } else if (state->std == V4L2_STD_NTSC_443) {
+ fmt = 0x3;
+ } else if (state->std == V4L2_STD_PAL_M) {
+ pal_m = 1;
+ fmt = 0x5;
+ } else if (state->std == V4L2_STD_PAL_N) {
+ fmt = 0x6;
+ } else if (state->std == V4L2_STD_PAL_Nc) {
+ fmt = 0x7;
+ } else if (state->std == V4L2_STD_PAL_60) {
+ fmt = 0x8;
+ } else {
+ /* Then, test against generic ones */
+ if (state->std & V4L2_STD_NTSC)
+ fmt = 0x1;
+ else if (state->std & V4L2_STD_PAL)
+ fmt = 0x4;
+ else if (state->std & V4L2_STD_SECAM)
+ fmt = 0xc;
+ }
+
+ CX18_DEBUG_INFO_DEV(sd, "changing video std to fmt %i\n", fmt);
+
+ /* Follow step 9 of section 3.16 in the cx18_av datasheet.
+ Without this PAL may display a vertical ghosting effect.
+ This happens for example with the Yuan MPC622. */
+ if (fmt >= 4 && fmt < 8) {
+ /* Set format to NTSC-M */
+ cx18_av_and_or(cx, 0x400, ~0xf, 1);
+ /* Turn off LCOMB */
+ cx18_av_and_or(cx, 0x47b, ~6, 0);
+ }
+ cx18_av_and_or(cx, 0x400, ~0x2f, fmt | 0x20);
+ cx18_av_and_or(cx, 0x403, ~0x3, pal_m);
+ cx18_av_std_setup(cx);
+ input_change(cx);
+ return 0;
+}
+
+static int cx18_av_s_radio(struct v4l2_subdev *sd)
+{
+ struct cx18_av_state *state = to_cx18_av_state(sd);
+ state->radio = 1;
+ return 0;
+}
+
+static int cx18_av_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ cx18_av_write(cx, 0x414, ctrl->val - 128);
+ break;
+
+ case V4L2_CID_CONTRAST:
+ cx18_av_write(cx, 0x415, ctrl->val << 1);
+ break;
+
+ case V4L2_CID_SATURATION:
+ cx18_av_write(cx, 0x420, ctrl->val << 1);
+ cx18_av_write(cx, 0x421, ctrl->val << 1);
+ break;
+
+ case V4L2_CID_HUE:
+ cx18_av_write(cx, 0x422, ctrl->val);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cx18_av_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt)
+{
+ struct cx18_av_state *state = to_cx18_av_state(sd);
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ int HSC, VSC, Vsrc, Hsrc, filter, Vlines;
+ int is_50Hz = !(state->std & V4L2_STD_525_60);
+
+ if (fmt->code != V4L2_MBUS_FMT_FIXED)
+ return -EINVAL;
+
+ fmt->field = V4L2_FIELD_INTERLACED;
+ fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+ Vsrc = (cx18_av_read(cx, 0x476) & 0x3f) << 4;
+ Vsrc |= (cx18_av_read(cx, 0x475) & 0xf0) >> 4;
+
+ Hsrc = (cx18_av_read(cx, 0x472) & 0x3f) << 4;
+ Hsrc |= (cx18_av_read(cx, 0x471) & 0xf0) >> 4;
+
+ /*
+ * This adjustment reflects the excess of vactive, set in
+ * cx18_av_std_setup(), above standard values:
+ *
+ * 480 + 1 for 60 Hz systems
+ * 576 + 3 for 50 Hz systems
+ */
+ Vlines = fmt->height + (is_50Hz ? 3 : 1);
+
+ /*
+ * Invalid height and width scaling requests are:
+ * 1. width less than 1/16 of the source width
+ * 2. width greater than the source width
+ * 3. height less than 1/8 of the source height
+ * 4. height greater than the source height
+ */
+ if ((fmt->width * 16 < Hsrc) || (Hsrc < fmt->width) ||
+ (Vlines * 8 < Vsrc) || (Vsrc < Vlines)) {
+ CX18_ERR_DEV(sd, "%dx%d is not a valid size!\n",
+ fmt->width, fmt->height);
+ return -ERANGE;
+ }
+
+ HSC = (Hsrc * (1 << 20)) / fmt->width - (1 << 20);
+ VSC = (1 << 16) - (Vsrc * (1 << 9) / Vlines - (1 << 9));
+ VSC &= 0x1fff;
+
+ if (fmt->width >= 385)
+ filter = 0;
+ else if (fmt->width > 192)
+ filter = 1;
+ else if (fmt->width > 96)
+ filter = 2;
+ else
+ filter = 3;
+
+ CX18_DEBUG_INFO_DEV(sd,
+ "decoder set size %dx%d -> scale %ux%u\n",
+ fmt->width, fmt->height, HSC, VSC);
+
+ /* HSCALE=HSC */
+ cx18_av_write(cx, 0x418, HSC & 0xff);
+ cx18_av_write(cx, 0x419, (HSC >> 8) & 0xff);
+ cx18_av_write(cx, 0x41a, HSC >> 16);
+ /* VSCALE=VSC */
+ cx18_av_write(cx, 0x41c, VSC & 0xff);
+ cx18_av_write(cx, 0x41d, VSC >> 8);
+ /* VS_INTRLACE=1 VFILT=filter */
+ cx18_av_write(cx, 0x41e, 0x8 | filter);
+ return 0;
+}
+
+static int cx18_av_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+ CX18_DEBUG_INFO_DEV(sd, "%s output\n", enable ? "enable" : "disable");
+ if (enable) {
+ cx18_av_write(cx, 0x115, 0x8c);
+ cx18_av_write(cx, 0x116, 0x07);
+ } else {
+ cx18_av_write(cx, 0x115, 0x00);
+ cx18_av_write(cx, 0x116, 0x00);
+ }
+ return 0;
+}
+
+static void log_video_status(struct cx18 *cx)
+{
+ static const char *const fmt_strs[] = {
+ "0x0",
+ "NTSC-M", "NTSC-J", "NTSC-4.43",
+ "PAL-BDGHI", "PAL-M", "PAL-N", "PAL-Nc", "PAL-60",
+ "0x9", "0xA", "0xB",
+ "SECAM",
+ "0xD", "0xE", "0xF"
+ };
+
+ struct cx18_av_state *state = &cx->av_state;
+ struct v4l2_subdev *sd = &state->sd;
+ u8 vidfmt_sel = cx18_av_read(cx, 0x400) & 0xf;
+ u8 gen_stat1 = cx18_av_read(cx, 0x40d);
+ u8 gen_stat2 = cx18_av_read(cx, 0x40e);
+ int vid_input = state->vid_input;
+
+ CX18_INFO_DEV(sd, "Video signal: %spresent\n",
+ (gen_stat2 & 0x20) ? "" : "not ");
+ CX18_INFO_DEV(sd, "Detected format: %s\n",
+ fmt_strs[gen_stat1 & 0xf]);
+
+ CX18_INFO_DEV(sd, "Specified standard: %s\n",
+ vidfmt_sel ? fmt_strs[vidfmt_sel]
+ : "automatic detection");
+
+ if (vid_input >= CX18_AV_COMPOSITE1 &&
+ vid_input <= CX18_AV_COMPOSITE8) {
+ CX18_INFO_DEV(sd, "Specified video input: Composite %d\n",
+ vid_input - CX18_AV_COMPOSITE1 + 1);
+ } else {
+ CX18_INFO_DEV(sd, "Specified video input: "
+ "S-Video (Luma In%d, Chroma In%d)\n",
+ (vid_input & 0xf0) >> 4,
+ (vid_input & 0xf00) >> 8);
+ }
+
+ CX18_INFO_DEV(sd, "Specified audioclock freq: %d Hz\n",
+ state->audclk_freq);
+}
+
+static void log_audio_status(struct cx18 *cx)
+{
+ struct cx18_av_state *state = &cx->av_state;
+ struct v4l2_subdev *sd = &state->sd;
+ u8 download_ctl = cx18_av_read(cx, 0x803);
+ u8 mod_det_stat0 = cx18_av_read(cx, 0x804);
+ u8 mod_det_stat1 = cx18_av_read(cx, 0x805);
+ u8 audio_config = cx18_av_read(cx, 0x808);
+ u8 pref_mode = cx18_av_read(cx, 0x809);
+ u8 afc0 = cx18_av_read(cx, 0x80b);
+ u8 mute_ctl = cx18_av_read(cx, 0x8d3);
+ int aud_input = state->aud_input;
+ char *p;
+
+ switch (mod_det_stat0) {
+ case 0x00: p = "mono"; break;
+ case 0x01: p = "stereo"; break;
+ case 0x02: p = "dual"; break;
+ case 0x04: p = "tri"; break;
+ case 0x10: p = "mono with SAP"; break;
+ case 0x11: p = "stereo with SAP"; break;
+ case 0x12: p = "dual with SAP"; break;
+ case 0x14: p = "tri with SAP"; break;
+ case 0xfe: p = "forced mode"; break;
+ default: p = "not defined"; break;
+ }
+ CX18_INFO_DEV(sd, "Detected audio mode: %s\n", p);
+
+ switch (mod_det_stat1) {
+ case 0x00: p = "not defined"; break;
+ case 0x01: p = "EIAJ"; break;
+ case 0x02: p = "A2-M"; break;
+ case 0x03: p = "A2-BG"; break;
+ case 0x04: p = "A2-DK1"; break;
+ case 0x05: p = "A2-DK2"; break;
+ case 0x06: p = "A2-DK3"; break;
+ case 0x07: p = "A1 (6.0 MHz FM Mono)"; break;
+ case 0x08: p = "AM-L"; break;
+ case 0x09: p = "NICAM-BG"; break;
+ case 0x0a: p = "NICAM-DK"; break;
+ case 0x0b: p = "NICAM-I"; break;
+ case 0x0c: p = "NICAM-L"; break;
+ case 0x0d: p = "BTSC/EIAJ/A2-M Mono (4.5 MHz FMMono)"; break;
+ case 0x0e: p = "IF FM Radio"; break;
+ case 0x0f: p = "BTSC"; break;
+ case 0x10: p = "detected chrominance"; break;
+ case 0xfd: p = "unknown audio standard"; break;
+ case 0xfe: p = "forced audio standard"; break;
+ case 0xff: p = "no detected audio standard"; break;
+ default: p = "not defined"; break;
+ }
+ CX18_INFO_DEV(sd, "Detected audio standard: %s\n", p);
+ CX18_INFO_DEV(sd, "Audio muted: %s\n",
+ (mute_ctl & 0x2) ? "yes" : "no");
+ CX18_INFO_DEV(sd, "Audio microcontroller: %s\n",
+ (download_ctl & 0x10) ? "running" : "stopped");
+
+ switch (audio_config >> 4) {
+ case 0x00: p = "undefined"; break;
+ case 0x01: p = "BTSC"; break;
+ case 0x02: p = "EIAJ"; break;
+ case 0x03: p = "A2-M"; break;
+ case 0x04: p = "A2-BG"; break;
+ case 0x05: p = "A2-DK1"; break;
+ case 0x06: p = "A2-DK2"; break;
+ case 0x07: p = "A2-DK3"; break;
+ case 0x08: p = "A1 (6.0 MHz FM Mono)"; break;
+ case 0x09: p = "AM-L"; break;
+ case 0x0a: p = "NICAM-BG"; break;
+ case 0x0b: p = "NICAM-DK"; break;
+ case 0x0c: p = "NICAM-I"; break;
+ case 0x0d: p = "NICAM-L"; break;
+ case 0x0e: p = "FM radio"; break;
+ case 0x0f: p = "automatic detection"; break;
+ default: p = "undefined"; break;
+ }
+ CX18_INFO_DEV(sd, "Configured audio standard: %s\n", p);
+
+ if ((audio_config >> 4) < 0xF) {
+ switch (audio_config & 0xF) {
+ case 0x00: p = "MONO1 (LANGUAGE A/Mono L+R channel for BTSC, EIAJ, A2)"; break;
+ case 0x01: p = "MONO2 (LANGUAGE B)"; break;
+ case 0x02: p = "MONO3 (STEREO forced MONO)"; break;
+ case 0x03: p = "MONO4 (NICAM ANALOG-Language C/Analog Fallback)"; break;
+ case 0x04: p = "STEREO"; break;
+ case 0x05: p = "DUAL1 (AC)"; break;
+ case 0x06: p = "DUAL2 (BC)"; break;
+ case 0x07: p = "DUAL3 (AB)"; break;
+ default: p = "undefined";
+ }
+ CX18_INFO_DEV(sd, "Configured audio mode: %s\n", p);
+ } else {
+ switch (audio_config & 0xF) {
+ case 0x00: p = "BG"; break;
+ case 0x01: p = "DK1"; break;
+ case 0x02: p = "DK2"; break;
+ case 0x03: p = "DK3"; break;
+ case 0x04: p = "I"; break;
+ case 0x05: p = "L"; break;
+ case 0x06: p = "BTSC"; break;
+ case 0x07: p = "EIAJ"; break;
+ case 0x08: p = "A2-M"; break;
+ case 0x09: p = "FM Radio (4.5 MHz)"; break;
+ case 0x0a: p = "FM Radio (5.5 MHz)"; break;
+ case 0x0b: p = "S-Video"; break;
+ case 0x0f: p = "automatic standard and mode detection"; break;
+ default: p = "undefined"; break;
+ }
+ CX18_INFO_DEV(sd, "Configured audio system: %s\n", p);
+ }
+
+ if (aud_input)
+ CX18_INFO_DEV(sd, "Specified audio input: Tuner (In%d)\n",
+ aud_input);
+ else
+ CX18_INFO_DEV(sd, "Specified audio input: External\n");
+
+ switch (pref_mode & 0xf) {
+ case 0: p = "mono/language A"; break;
+ case 1: p = "language B"; break;
+ case 2: p = "language C"; break;
+ case 3: p = "analog fallback"; break;
+ case 4: p = "stereo"; break;
+ case 5: p = "language AC"; break;
+ case 6: p = "language BC"; break;
+ case 7: p = "language AB"; break;
+ default: p = "undefined"; break;
+ }
+ CX18_INFO_DEV(sd, "Preferred audio mode: %s\n", p);
+
+ if ((audio_config & 0xf) == 0xf) {
+ switch ((afc0 >> 3) & 0x1) {
+ case 0: p = "system DK"; break;
+ case 1: p = "system L"; break;
+ }
+ CX18_INFO_DEV(sd, "Selected 65 MHz format: %s\n", p);
+
+ switch (afc0 & 0x7) {
+ case 0: p = "Chroma"; break;
+ case 1: p = "BTSC"; break;
+ case 2: p = "EIAJ"; break;
+ case 3: p = "A2-M"; break;
+ case 4: p = "autodetect"; break;
+ default: p = "undefined"; break;
+ }
+ CX18_INFO_DEV(sd, "Selected 45 MHz format: %s\n", p);
+ }
+}
+
+static int cx18_av_log_status(struct v4l2_subdev *sd)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ log_video_status(cx);
+ log_audio_status(cx);
+ return 0;
+}
+
+static inline int cx18_av_dbg_match(const struct v4l2_dbg_match *match)
+{
+ return match->type == V4L2_CHIP_MATCH_HOST && match->addr == 1;
+}
+
+static int cx18_av_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct cx18_av_state *state = to_cx18_av_state(sd);
+
+ if (cx18_av_dbg_match(&chip->match)) {
+ chip->ident = state->id;
+ chip->revision = state->rev;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int cx18_av_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+ if (!cx18_av_dbg_match(&reg->match))
+ return -EINVAL;
+ if ((reg->reg & 0x3) != 0)
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->size = 4;
+ reg->val = cx18_av_read4(cx, reg->reg & 0x00000ffc);
+ return 0;
+}
+
+static int cx18_av_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+ if (!cx18_av_dbg_match(&reg->match))
+ return -EINVAL;
+ if ((reg->reg & 0x3) != 0)
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ cx18_av_write4(cx, reg->reg & 0x00000ffc, reg->val);
+ return 0;
+}
+#endif
+
+static const struct v4l2_ctrl_ops cx18_av_ctrl_ops = {
+ .s_ctrl = cx18_av_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops cx18_av_general_ops = {
+ .g_chip_ident = cx18_av_g_chip_ident,
+ .log_status = cx18_av_log_status,
+ .load_fw = cx18_av_load_fw,
+ .reset = cx18_av_reset,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+ .s_std = cx18_av_s_std,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = cx18_av_g_register,
+ .s_register = cx18_av_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_tuner_ops cx18_av_tuner_ops = {
+ .s_radio = cx18_av_s_radio,
+ .s_frequency = cx18_av_s_frequency,
+ .g_tuner = cx18_av_g_tuner,
+ .s_tuner = cx18_av_s_tuner,
+};
+
+static const struct v4l2_subdev_audio_ops cx18_av_audio_ops = {
+ .s_clock_freq = cx18_av_s_clock_freq,
+ .s_routing = cx18_av_s_audio_routing,
+};
+
+static const struct v4l2_subdev_video_ops cx18_av_video_ops = {
+ .s_routing = cx18_av_s_video_routing,
+ .s_stream = cx18_av_s_stream,
+ .s_mbus_fmt = cx18_av_s_mbus_fmt,
+};
+
+static const struct v4l2_subdev_vbi_ops cx18_av_vbi_ops = {
+ .decode_vbi_line = cx18_av_decode_vbi_line,
+ .g_sliced_fmt = cx18_av_g_sliced_fmt,
+ .s_sliced_fmt = cx18_av_s_sliced_fmt,
+ .s_raw_fmt = cx18_av_s_raw_fmt,
+};
+
+static const struct v4l2_subdev_ops cx18_av_ops = {
+ .core = &cx18_av_general_ops,
+ .tuner = &cx18_av_tuner_ops,
+ .audio = &cx18_av_audio_ops,
+ .video = &cx18_av_video_ops,
+ .vbi = &cx18_av_vbi_ops,
+};
+
+int cx18_av_probe(struct cx18 *cx)
+{
+ struct cx18_av_state *state = &cx->av_state;
+ struct v4l2_subdev *sd;
+ int err;
+
+ state->rev = cx18_av_read4(cx, CXADEC_CHIP_CTRL) & 0xffff;
+ state->id = ((state->rev >> 4) == CXADEC_CHIP_TYPE_MAKO)
+ ? V4L2_IDENT_CX23418_843 : V4L2_IDENT_UNKNOWN;
+
+ state->vid_input = CX18_AV_COMPOSITE7;
+ state->aud_input = CX18_AV_AUDIO8;
+ state->audclk_freq = 48000;
+ state->audmode = V4L2_TUNER_MODE_LANG1;
+ state->slicer_line_delay = 0;
+ state->slicer_line_offset = (10 + state->slicer_line_delay - 2);
+
+ sd = &state->sd;
+ v4l2_subdev_init(sd, &cx18_av_ops);
+ v4l2_set_subdevdata(sd, cx);
+ snprintf(sd->name, sizeof(sd->name),
+ "%s %03x", cx->v4l2_dev.name, (state->rev >> 4));
+ sd->grp_id = CX18_HW_418_AV;
+ v4l2_ctrl_handler_init(&state->hdl, 9);
+ v4l2_ctrl_new_std(&state->hdl, &cx18_av_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(&state->hdl, &cx18_av_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(&state->hdl, &cx18_av_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(&state->hdl, &cx18_av_ctrl_ops,
+ V4L2_CID_HUE, -128, 127, 1, 0);
+
+ state->volume = v4l2_ctrl_new_std(&state->hdl,
+ &cx18_av_audio_ctrl_ops, V4L2_CID_AUDIO_VOLUME,
+ 0, 65535, 65535 / 100, 0);
+ v4l2_ctrl_new_std(&state->hdl,
+ &cx18_av_audio_ctrl_ops, V4L2_CID_AUDIO_MUTE,
+ 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&state->hdl, &cx18_av_audio_ctrl_ops,
+ V4L2_CID_AUDIO_BALANCE,
+ 0, 65535, 65535 / 100, 32768);
+ v4l2_ctrl_new_std(&state->hdl, &cx18_av_audio_ctrl_ops,
+ V4L2_CID_AUDIO_BASS,
+ 0, 65535, 65535 / 100, 32768);
+ v4l2_ctrl_new_std(&state->hdl, &cx18_av_audio_ctrl_ops,
+ V4L2_CID_AUDIO_TREBLE,
+ 0, 65535, 65535 / 100, 32768);
+ sd->ctrl_handler = &state->hdl;
+ if (state->hdl.error) {
+ int err = state->hdl.error;
+
+ v4l2_ctrl_handler_free(&state->hdl);
+ return err;
+ }
+ err = v4l2_device_register_subdev(&cx->v4l2_dev, sd);
+ if (err)
+ v4l2_ctrl_handler_free(&state->hdl);
+ else
+ cx18_av_init(cx);
+ return err;
+}
diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h
new file mode 100644
index 00000000..e9c69d9c
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-av-core.h
@@ -0,0 +1,391 @@
+/*
+ * cx18 ADEC header
+ *
+ * Derived from cx25840-core.h
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef _CX18_AV_CORE_H_
+#define _CX18_AV_CORE_H_
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+
+struct cx18;
+
+enum cx18_av_video_input {
+ /* Composite video inputs In1-In8 */
+ CX18_AV_COMPOSITE1 = 1,
+ CX18_AV_COMPOSITE2,
+ CX18_AV_COMPOSITE3,
+ CX18_AV_COMPOSITE4,
+ CX18_AV_COMPOSITE5,
+ CX18_AV_COMPOSITE6,
+ CX18_AV_COMPOSITE7,
+ CX18_AV_COMPOSITE8,
+
+ /* S-Video inputs consist of one luma input (In1-In8) ORed with one
+ chroma input (In5-In8) */
+ CX18_AV_SVIDEO_LUMA1 = 0x10,
+ CX18_AV_SVIDEO_LUMA2 = 0x20,
+ CX18_AV_SVIDEO_LUMA3 = 0x30,
+ CX18_AV_SVIDEO_LUMA4 = 0x40,
+ CX18_AV_SVIDEO_LUMA5 = 0x50,
+ CX18_AV_SVIDEO_LUMA6 = 0x60,
+ CX18_AV_SVIDEO_LUMA7 = 0x70,
+ CX18_AV_SVIDEO_LUMA8 = 0x80,
+ CX18_AV_SVIDEO_CHROMA4 = 0x400,
+ CX18_AV_SVIDEO_CHROMA5 = 0x500,
+ CX18_AV_SVIDEO_CHROMA6 = 0x600,
+ CX18_AV_SVIDEO_CHROMA7 = 0x700,
+ CX18_AV_SVIDEO_CHROMA8 = 0x800,
+
+ /* S-Video aliases for common luma/chroma combinations */
+ CX18_AV_SVIDEO1 = 0x510,
+ CX18_AV_SVIDEO2 = 0x620,
+ CX18_AV_SVIDEO3 = 0x730,
+ CX18_AV_SVIDEO4 = 0x840,
+
+ /* Component Video inputs consist of one luma input (In1-In8) ORed
+ with a red chroma (In4-In6) and blue chroma input (In7-In8) */
+ CX18_AV_COMPONENT_LUMA1 = 0x1000,
+ CX18_AV_COMPONENT_LUMA2 = 0x2000,
+ CX18_AV_COMPONENT_LUMA3 = 0x3000,
+ CX18_AV_COMPONENT_LUMA4 = 0x4000,
+ CX18_AV_COMPONENT_LUMA5 = 0x5000,
+ CX18_AV_COMPONENT_LUMA6 = 0x6000,
+ CX18_AV_COMPONENT_LUMA7 = 0x7000,
+ CX18_AV_COMPONENT_LUMA8 = 0x8000,
+ CX18_AV_COMPONENT_R_CHROMA4 = 0x40000,
+ CX18_AV_COMPONENT_R_CHROMA5 = 0x50000,
+ CX18_AV_COMPONENT_R_CHROMA6 = 0x60000,
+ CX18_AV_COMPONENT_B_CHROMA7 = 0x700000,
+ CX18_AV_COMPONENT_B_CHROMA8 = 0x800000,
+
+ /* Component Video aliases for common combinations */
+ CX18_AV_COMPONENT1 = 0x861000,
+};
+
+enum cx18_av_audio_input {
+ /* Audio inputs: serial or In4-In8 */
+ CX18_AV_AUDIO_SERIAL1,
+ CX18_AV_AUDIO_SERIAL2,
+ CX18_AV_AUDIO4 = 4,
+ CX18_AV_AUDIO5,
+ CX18_AV_AUDIO6,
+ CX18_AV_AUDIO7,
+ CX18_AV_AUDIO8,
+};
+
+struct cx18_av_state {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+ struct v4l2_ctrl *volume;
+ int radio;
+ v4l2_std_id std;
+ enum cx18_av_video_input vid_input;
+ enum cx18_av_audio_input aud_input;
+ u32 audclk_freq;
+ int audmode;
+ u32 id;
+ u32 rev;
+ int is_initialized;
+
+ /*
+ * The VBI slicer starts operating and counting lines, beginning at
+ * slicer line count of 1, at D lines after the deassertion of VRESET.
+ * This staring field line, S, is 6 (& 319) or 10 (& 273) for 625 or 525
+ * line systems respectively. Sliced ancillary data captured on VBI
+ * slicer line M is inserted after the VBI slicer is done with line M,
+ * when VBI slicer line count is N = M+1. Thus when the VBI slicer
+ * reports a VBI slicer line number with ancillary data, the IDID0 byte
+ * indicates VBI slicer line N. The actual field line that the captured
+ * data comes from is
+ *
+ * L = M+(S+D-1) = N-1+(S+D-1) = N + (S+D-2).
+ *
+ * L is the line in the field, not frame, from which the VBI data came.
+ * N is the line reported by the slicer in the ancillary data.
+ * D is the slicer_line_delay value programmed into register 0x47f.
+ * S is 6 for 625 line systems or 10 for 525 line systems
+ * (S+D-2) is the slicer_line_offset used to convert slicer reported
+ * line counts to actual field lines.
+ */
+ int slicer_line_delay;
+ int slicer_line_offset;
+};
+
+
+/* Registers */
+#define CXADEC_CHIP_TYPE_TIGER 0x837
+#define CXADEC_CHIP_TYPE_MAKO 0x843
+
+#define CXADEC_HOST_REG1 0x000
+#define CXADEC_HOST_REG2 0x001
+
+#define CXADEC_CHIP_CTRL 0x100
+#define CXADEC_AFE_CTRL 0x104
+#define CXADEC_PLL_CTRL1 0x108
+#define CXADEC_VID_PLL_FRAC 0x10C
+#define CXADEC_AUX_PLL_FRAC 0x110
+#define CXADEC_PIN_CTRL1 0x114
+#define CXADEC_PIN_CTRL2 0x118
+#define CXADEC_PIN_CFG1 0x11C
+#define CXADEC_PIN_CFG2 0x120
+
+#define CXADEC_PIN_CFG3 0x124
+#define CXADEC_I2S_MCLK 0x127
+
+#define CXADEC_AUD_LOCK1 0x128
+#define CXADEC_AUD_LOCK2 0x12C
+#define CXADEC_POWER_CTRL 0x130
+#define CXADEC_AFE_DIAG_CTRL1 0x134
+#define CXADEC_AFE_DIAG_CTRL2 0x138
+#define CXADEC_AFE_DIAG_CTRL3 0x13C
+#define CXADEC_PLL_DIAG_CTRL 0x140
+#define CXADEC_TEST_CTRL1 0x144
+#define CXADEC_TEST_CTRL2 0x148
+#define CXADEC_BIST_STAT 0x14C
+#define CXADEC_DLL1_DIAG_CTRL 0x158
+#define CXADEC_DLL2_DIAG_CTRL 0x15C
+
+/* IR registers */
+#define CXADEC_IR_CTRL_REG 0x200
+#define CXADEC_IR_TXCLK_REG 0x204
+#define CXADEC_IR_RXCLK_REG 0x208
+#define CXADEC_IR_CDUTY_REG 0x20C
+#define CXADEC_IR_STAT_REG 0x210
+#define CXADEC_IR_IRQEN_REG 0x214
+#define CXADEC_IR_FILTER_REG 0x218
+#define CXADEC_IR_FIFO_REG 0x21C
+
+/* Video Registers */
+#define CXADEC_MODE_CTRL 0x400
+#define CXADEC_OUT_CTRL1 0x404
+#define CXADEC_OUT_CTRL2 0x408
+#define CXADEC_GEN_STAT 0x40C
+#define CXADEC_INT_STAT_MASK 0x410
+#define CXADEC_LUMA_CTRL 0x414
+
+#define CXADEC_BRIGHTNESS_CTRL_BYTE 0x414
+#define CXADEC_CONTRAST_CTRL_BYTE 0x415
+#define CXADEC_LUMA_CTRL_BYTE_3 0x416
+
+#define CXADEC_HSCALE_CTRL 0x418
+#define CXADEC_VSCALE_CTRL 0x41C
+
+#define CXADEC_CHROMA_CTRL 0x420
+
+#define CXADEC_USAT_CTRL_BYTE 0x420
+#define CXADEC_VSAT_CTRL_BYTE 0x421
+#define CXADEC_HUE_CTRL_BYTE 0x422
+
+#define CXADEC_VBI_LINE_CTRL1 0x424
+#define CXADEC_VBI_LINE_CTRL2 0x428
+#define CXADEC_VBI_LINE_CTRL3 0x42C
+#define CXADEC_VBI_LINE_CTRL4 0x430
+#define CXADEC_VBI_LINE_CTRL5 0x434
+#define CXADEC_VBI_FC_CFG 0x438
+#define CXADEC_VBI_MISC_CFG1 0x43C
+#define CXADEC_VBI_MISC_CFG2 0x440
+#define CXADEC_VBI_PAY1 0x444
+#define CXADEC_VBI_PAY2 0x448
+#define CXADEC_VBI_CUST1_CFG1 0x44C
+#define CXADEC_VBI_CUST1_CFG2 0x450
+#define CXADEC_VBI_CUST1_CFG3 0x454
+#define CXADEC_VBI_CUST2_CFG1 0x458
+#define CXADEC_VBI_CUST2_CFG2 0x45C
+#define CXADEC_VBI_CUST2_CFG3 0x460
+#define CXADEC_VBI_CUST3_CFG1 0x464
+#define CXADEC_VBI_CUST3_CFG2 0x468
+#define CXADEC_VBI_CUST3_CFG3 0x46C
+#define CXADEC_HORIZ_TIM_CTRL 0x470
+#define CXADEC_VERT_TIM_CTRL 0x474
+#define CXADEC_SRC_COMB_CFG 0x478
+#define CXADEC_CHROMA_VBIOFF_CFG 0x47C
+#define CXADEC_FIELD_COUNT 0x480
+#define CXADEC_MISC_TIM_CTRL 0x484
+#define CXADEC_DFE_CTRL1 0x488
+#define CXADEC_DFE_CTRL2 0x48C
+#define CXADEC_DFE_CTRL3 0x490
+#define CXADEC_PLL_CTRL2 0x494
+#define CXADEC_HTL_CTRL 0x498
+#define CXADEC_COMB_CTRL 0x49C
+#define CXADEC_CRUSH_CTRL 0x4A0
+#define CXADEC_SOFT_RST_CTRL 0x4A4
+#define CXADEC_MV_DT_CTRL2 0x4A8
+#define CXADEC_MV_DT_CTRL3 0x4AC
+#define CXADEC_MISC_DIAG_CTRL 0x4B8
+
+#define CXADEC_DL_CTL 0x800
+#define CXADEC_DL_CTL_ADDRESS_LOW 0x800 /* Byte 1 in DL_CTL */
+#define CXADEC_DL_CTL_ADDRESS_HIGH 0x801 /* Byte 2 in DL_CTL */
+#define CXADEC_DL_CTL_DATA 0x802 /* Byte 3 in DL_CTL */
+#define CXADEC_DL_CTL_CONTROL 0x803 /* Byte 4 in DL_CTL */
+
+#define CXADEC_STD_DET_STATUS 0x804
+
+#define CXADEC_STD_DET_CTL 0x808
+#define CXADEC_STD_DET_CTL_AUD_CTL 0x808 /* Byte 1 in STD_DET_CTL */
+#define CXADEC_STD_DET_CTL_PREF_MODE 0x809 /* Byte 2 in STD_DET_CTL */
+
+#define CXADEC_DW8051_INT 0x80C
+#define CXADEC_GENERAL_CTL 0x810
+#define CXADEC_AAGC_CTL 0x814
+#define CXADEC_IF_SRC_CTL 0x818
+#define CXADEC_ANLOG_DEMOD_CTL 0x81C
+#define CXADEC_ROT_FREQ_CTL 0x820
+#define CXADEC_FM1_CTL 0x824
+#define CXADEC_PDF_CTL 0x828
+#define CXADEC_DFT1_CTL1 0x82C
+#define CXADEC_DFT1_CTL2 0x830
+#define CXADEC_DFT_STATUS 0x834
+#define CXADEC_DFT2_CTL1 0x838
+#define CXADEC_DFT2_CTL2 0x83C
+#define CXADEC_DFT2_STATUS 0x840
+#define CXADEC_DFT3_CTL1 0x844
+#define CXADEC_DFT3_CTL2 0x848
+#define CXADEC_DFT3_STATUS 0x84C
+#define CXADEC_DFT4_CTL1 0x850
+#define CXADEC_DFT4_CTL2 0x854
+#define CXADEC_DFT4_STATUS 0x858
+#define CXADEC_AM_MTS_DET 0x85C
+#define CXADEC_ANALOG_MUX_CTL 0x860
+#define CXADEC_DIG_PLL_CTL1 0x864
+#define CXADEC_DIG_PLL_CTL2 0x868
+#define CXADEC_DIG_PLL_CTL3 0x86C
+#define CXADEC_DIG_PLL_CTL4 0x870
+#define CXADEC_DIG_PLL_CTL5 0x874
+#define CXADEC_DEEMPH_GAIN_CTL 0x878
+#define CXADEC_DEEMPH_COEF1 0x87C
+#define CXADEC_DEEMPH_COEF2 0x880
+#define CXADEC_DBX1_CTL1 0x884
+#define CXADEC_DBX1_CTL2 0x888
+#define CXADEC_DBX1_STATUS 0x88C
+#define CXADEC_DBX2_CTL1 0x890
+#define CXADEC_DBX2_CTL2 0x894
+#define CXADEC_DBX2_STATUS 0x898
+#define CXADEC_AM_FM_DIFF 0x89C
+
+/* NICAM registers go here */
+#define CXADEC_NICAM_STATUS 0x8C8
+#define CXADEC_DEMATRIX_CTL 0x8CC
+
+#define CXADEC_PATH1_CTL1 0x8D0
+#define CXADEC_PATH1_VOL_CTL 0x8D4
+#define CXADEC_PATH1_EQ_CTL 0x8D8
+#define CXADEC_PATH1_SC_CTL 0x8DC
+
+#define CXADEC_PATH2_CTL1 0x8E0
+#define CXADEC_PATH2_VOL_CTL 0x8E4
+#define CXADEC_PATH2_EQ_CTL 0x8E8
+#define CXADEC_PATH2_SC_CTL 0x8EC
+
+#define CXADEC_SRC_CTL 0x8F0
+#define CXADEC_SRC_LF_COEF 0x8F4
+#define CXADEC_SRC1_CTL 0x8F8
+#define CXADEC_SRC2_CTL 0x8FC
+#define CXADEC_SRC3_CTL 0x900
+#define CXADEC_SRC4_CTL 0x904
+#define CXADEC_SRC5_CTL 0x908
+#define CXADEC_SRC6_CTL 0x90C
+
+#define CXADEC_BASEBAND_OUT_SEL 0x910
+#define CXADEC_I2S_IN_CTL 0x914
+#define CXADEC_I2S_OUT_CTL 0x918
+#define CXADEC_AC97_CTL 0x91C
+#define CXADEC_QAM_PDF 0x920
+#define CXADEC_QAM_CONST_DEC 0x924
+#define CXADEC_QAM_ROTATOR_FREQ 0x948
+
+/* Bit definitions / settings used in Mako Audio */
+#define CXADEC_PREF_MODE_MONO_LANGA 0
+#define CXADEC_PREF_MODE_MONO_LANGB 1
+#define CXADEC_PREF_MODE_MONO_LANGC 2
+#define CXADEC_PREF_MODE_FALLBACK 3
+#define CXADEC_PREF_MODE_STEREO 4
+#define CXADEC_PREF_MODE_DUAL_LANG_AC 5
+#define CXADEC_PREF_MODE_DUAL_LANG_BC 6
+#define CXADEC_PREF_MODE_DUAL_LANG_AB 7
+
+
+#define CXADEC_DETECT_STEREO 1
+#define CXADEC_DETECT_DUAL 2
+#define CXADEC_DETECT_TRI 4
+#define CXADEC_DETECT_SAP 0x10
+#define CXADEC_DETECT_NO_SIGNAL 0xFF
+
+#define CXADEC_SELECT_AUDIO_STANDARD_BG 0xF0 /* NICAM BG and A2 BG */
+#define CXADEC_SELECT_AUDIO_STANDARD_DK1 0xF1 /* NICAM DK and A2 DK */
+#define CXADEC_SELECT_AUDIO_STANDARD_DK2 0xF2
+#define CXADEC_SELECT_AUDIO_STANDARD_DK3 0xF3
+#define CXADEC_SELECT_AUDIO_STANDARD_I 0xF4 /* NICAM I and A1 */
+#define CXADEC_SELECT_AUDIO_STANDARD_L 0xF5 /* NICAM L and System L AM */
+#define CXADEC_SELECT_AUDIO_STANDARD_BTSC 0xF6
+#define CXADEC_SELECT_AUDIO_STANDARD_EIAJ 0xF7
+#define CXADEC_SELECT_AUDIO_STANDARD_A2_M 0xF8 /* A2 M */
+#define CXADEC_SELECT_AUDIO_STANDARD_FM 0xF9 /* FM radio */
+#define CXADEC_SELECT_AUDIO_STANDARD_AUTO 0xFF /* Auto detect */
+
+static inline struct cx18_av_state *to_cx18_av_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct cx18_av_state, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct cx18_av_state, hdl)->sd;
+}
+
+/* ----------------------------------------------------------------------- */
+/* cx18_av-core.c */
+int cx18_av_write(struct cx18 *cx, u16 addr, u8 value);
+int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value);
+int cx18_av_write4_noretry(struct cx18 *cx, u16 addr, u32 value);
+int cx18_av_write_expect(struct cx18 *cx, u16 addr, u8 value, u8 eval, u8 mask);
+int cx18_av_write4_expect(struct cx18 *cx, u16 addr, u32 value, u32 eval,
+ u32 mask);
+u8 cx18_av_read(struct cx18 *cx, u16 addr);
+u32 cx18_av_read4(struct cx18 *cx, u16 addr);
+int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned mask, u8 value);
+int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 mask, u32 value);
+void cx18_av_std_setup(struct cx18 *cx);
+
+int cx18_av_probe(struct cx18 *cx);
+
+/* ----------------------------------------------------------------------- */
+/* cx18_av-firmware.c */
+int cx18_av_loadfw(struct cx18 *cx);
+
+/* ----------------------------------------------------------------------- */
+/* cx18_av-audio.c */
+int cx18_av_s_clock_freq(struct v4l2_subdev *sd, u32 freq);
+void cx18_av_audio_set_path(struct cx18 *cx);
+extern const struct v4l2_ctrl_ops cx18_av_audio_ctrl_ops;
+
+/* ----------------------------------------------------------------------- */
+/* cx18_av-vbi.c */
+int cx18_av_decode_vbi_line(struct v4l2_subdev *sd,
+ struct v4l2_decode_vbi_line *vbi);
+int cx18_av_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt);
+int cx18_av_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt);
+int cx18_av_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt);
+
+#endif
diff --git a/drivers/media/video/cx18/cx18-av-firmware.c b/drivers/media/video/cx18/cx18-av-firmware.c
new file mode 100644
index 00000000..280aa4d2
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-av-firmware.c
@@ -0,0 +1,223 @@
+/*
+ * cx18 ADEC firmware functions
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include <linux/firmware.h>
+
+#define CX18_AUDIO_ENABLE 0xc72014
+#define CX18_AI1_MUX_MASK 0x30
+#define CX18_AI1_MUX_I2S1 0x00
+#define CX18_AI1_MUX_I2S2 0x10
+#define CX18_AI1_MUX_843_I2S 0x20
+#define CX18_AI1_MUX_INVALID 0x30
+
+#define FWFILE "v4l-cx23418-dig.fw"
+
+static int cx18_av_verifyfw(struct cx18 *cx, const struct firmware *fw)
+{
+ struct v4l2_subdev *sd = &cx->av_state.sd;
+ int ret = 0;
+ const u8 *data;
+ u32 size;
+ int addr;
+ u32 expected, dl_control;
+
+ /* Ensure we put the 8051 in reset and enable firmware upload mode */
+ dl_control = cx18_av_read4(cx, CXADEC_DL_CTL);
+ do {
+ dl_control &= 0x00ffffff;
+ dl_control |= 0x0f000000;
+ cx18_av_write4_noretry(cx, CXADEC_DL_CTL, dl_control);
+ dl_control = cx18_av_read4(cx, CXADEC_DL_CTL);
+ } while ((dl_control & 0xff000000) != 0x0f000000);
+
+ /* Read and auto increment until at address 0x0000 */
+ while (dl_control & 0x3fff)
+ dl_control = cx18_av_read4(cx, CXADEC_DL_CTL);
+
+ data = fw->data;
+ size = fw->size;
+ for (addr = 0; addr < size; addr++) {
+ dl_control &= 0xffff3fff; /* ignore top 2 bits of address */
+ expected = 0x0f000000 | ((u32)data[addr] << 16) | addr;
+ if (expected != dl_control) {
+ CX18_ERR_DEV(sd, "verification of %s firmware load "
+ "failed: expected %#010x got %#010x\n",
+ FWFILE, expected, dl_control);
+ ret = -EIO;
+ break;
+ }
+ dl_control = cx18_av_read4(cx, CXADEC_DL_CTL);
+ }
+ if (ret == 0)
+ CX18_INFO_DEV(sd, "verified load of %s firmware (%d bytes)\n",
+ FWFILE, size);
+ return ret;
+}
+
+int cx18_av_loadfw(struct cx18 *cx)
+{
+ struct v4l2_subdev *sd = &cx->av_state.sd;
+ const struct firmware *fw = NULL;
+ u32 size;
+ u32 u, v;
+ const u8 *ptr;
+ int i;
+ int retries1 = 0;
+
+ if (request_firmware(&fw, FWFILE, &cx->pci_dev->dev) != 0) {
+ CX18_ERR_DEV(sd, "unable to open firmware %s\n", FWFILE);
+ return -EINVAL;
+ }
+
+ /* The firmware load often has byte errors, so allow for several
+ retries, both at byte level and at the firmware load level. */
+ while (retries1 < 5) {
+ cx18_av_write4_expect(cx, CXADEC_CHIP_CTRL, 0x00010000,
+ 0x00008430, 0xffffffff); /* cx25843 */
+ cx18_av_write_expect(cx, CXADEC_STD_DET_CTL, 0xf6, 0xf6, 0xff);
+
+ /* Reset the Mako core, Register is alias of CXADEC_CHIP_CTRL */
+ cx18_av_write4_expect(cx, 0x8100, 0x00010000,
+ 0x00008430, 0xffffffff); /* cx25843 */
+
+ /* Put the 8051 in reset and enable firmware upload */
+ cx18_av_write4_noretry(cx, CXADEC_DL_CTL, 0x0F000000);
+
+ ptr = fw->data;
+ size = fw->size;
+
+ for (i = 0; i < size; i++) {
+ u32 dl_control = 0x0F000000 | i | ((u32)ptr[i] << 16);
+ u32 value = 0;
+ int retries2;
+ int unrec_err = 0;
+
+ for (retries2 = 0; retries2 < CX18_MAX_MMIO_WR_RETRIES;
+ retries2++) {
+ cx18_av_write4_noretry(cx, CXADEC_DL_CTL,
+ dl_control);
+ udelay(10);
+ value = cx18_av_read4(cx, CXADEC_DL_CTL);
+ if (value == dl_control)
+ break;
+ /* Check if we can correct the byte by changing
+ the address. We can only write the lower
+ address byte of the address. */
+ if ((value & 0x3F00) != (dl_control & 0x3F00)) {
+ unrec_err = 1;
+ break;
+ }
+ }
+ if (unrec_err || retries2 >= CX18_MAX_MMIO_WR_RETRIES)
+ break;
+ }
+ if (i == size)
+ break;
+ retries1++;
+ }
+ if (retries1 >= 5) {
+ CX18_ERR_DEV(sd, "unable to load firmware %s\n", FWFILE);
+ release_firmware(fw);
+ return -EIO;
+ }
+
+ cx18_av_write4_expect(cx, CXADEC_DL_CTL,
+ 0x03000000 | fw->size, 0x03000000, 0x13000000);
+
+ CX18_INFO_DEV(sd, "loaded %s firmware (%d bytes)\n", FWFILE, size);
+
+ if (cx18_av_verifyfw(cx, fw) == 0)
+ cx18_av_write4_expect(cx, CXADEC_DL_CTL,
+ 0x13000000 | fw->size, 0x13000000, 0x13000000);
+
+ /* Output to the 416 */
+ cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x78000);
+
+ /* Audio input control 1 set to Sony mode */
+ /* Audio output input 2 is 0 for slave operation input */
+ /* 0xC4000914[5]: 0 = left sample on WS=0, 1 = left sample on WS=1 */
+ /* 0xC4000914[7]: 0 = Philips mode, 1 = Sony mode (1st SCK rising edge
+ after WS transition for first bit of audio word. */
+ cx18_av_write4(cx, CXADEC_I2S_IN_CTL, 0x000000A0);
+
+ /* Audio output control 1 is set to Sony mode */
+ /* Audio output control 2 is set to 1 for master mode */
+ /* 0xC4000918[5]: 0 = left sample on WS=0, 1 = left sample on WS=1 */
+ /* 0xC4000918[7]: 0 = Philips mode, 1 = Sony mode (1st SCK rising edge
+ after WS transition for first bit of audio word. */
+ /* 0xC4000918[8]: 0 = slave operation, 1 = master (SCK_OUT and WS_OUT
+ are generated) */
+ cx18_av_write4(cx, CXADEC_I2S_OUT_CTL, 0x000001A0);
+
+ /* set alt I2s master clock to /0x16 and enable alt divider i2s
+ passthrough */
+ cx18_av_write4(cx, CXADEC_PIN_CFG3, 0x5600B687);
+
+ cx18_av_write4_expect(cx, CXADEC_STD_DET_CTL, 0x000000F6, 0x000000F6,
+ 0x3F00FFFF);
+ /* CxDevWrReg(CXADEC_STD_DET_CTL, 0x000000FF); */
+
+ /* Set bit 0 in register 0x9CC to signify that this is MiniMe. */
+ /* Register 0x09CC is defined by the Merlin firmware, and doesn't
+ have a name in the spec. */
+ cx18_av_write4(cx, 0x09CC, 1);
+
+ v = cx18_read_reg(cx, CX18_AUDIO_ENABLE);
+ /* If bit 11 is 1, clear bit 10 */
+ if (v & 0x800)
+ cx18_write_reg_expect(cx, v & 0xFFFFFBFF, CX18_AUDIO_ENABLE,
+ 0, 0x400);
+
+ /* Toggle the AI1 MUX */
+ v = cx18_read_reg(cx, CX18_AUDIO_ENABLE);
+ u = v & CX18_AI1_MUX_MASK;
+ v &= ~CX18_AI1_MUX_MASK;
+ if (u == CX18_AI1_MUX_843_I2S || u == CX18_AI1_MUX_INVALID) {
+ /* Switch to I2S1 */
+ v |= CX18_AI1_MUX_I2S1;
+ cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE,
+ v, CX18_AI1_MUX_MASK);
+ /* Switch back to the A/V decoder core I2S output */
+ v = (v & ~CX18_AI1_MUX_MASK) | CX18_AI1_MUX_843_I2S;
+ } else {
+ /* Switch to the A/V decoder core I2S output */
+ v |= CX18_AI1_MUX_843_I2S;
+ cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE,
+ v, CX18_AI1_MUX_MASK);
+ /* Switch back to I2S1 or I2S2 */
+ v = (v & ~CX18_AI1_MUX_MASK) | u;
+ }
+ cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE,
+ v, CX18_AI1_MUX_MASK);
+
+ /* Enable WW auto audio standard detection */
+ v = cx18_av_read4(cx, CXADEC_STD_DET_CTL);
+ v |= 0xFF; /* Auto by default */
+ v |= 0x400; /* Stereo by default */
+ v |= 0x14000000;
+ cx18_av_write4_expect(cx, CXADEC_STD_DET_CTL, v, v, 0x3F00FFFF);
+
+ release_firmware(fw);
+ return 0;
+}
diff --git a/drivers/media/video/cx18/cx18-av-vbi.c b/drivers/media/video/cx18/cx18-av-vbi.c
new file mode 100644
index 00000000..baa36fbc
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-av-vbi.c
@@ -0,0 +1,311 @@
+/*
+ * cx18 ADEC VBI functions
+ *
+ * Derived from cx25840-vbi.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+
+#include "cx18-driver.h"
+
+/*
+ * For sliced VBI output, we set up to use VIP-1.1, 8-bit mode,
+ * NN counts 1 byte Dwords, an IDID with the VBI line # in it.
+ * Thus, according to the VIP-2 Spec, our VBI ancillary data lines
+ * (should!) look like:
+ * 4 byte EAV code: 0xff 0x00 0x00 0xRP
+ * unknown number of possible idle bytes
+ * 3 byte Anc data preamble: 0x00 0xff 0xff
+ * 1 byte data identifier: ne010iii (parity bits, 010, DID bits)
+ * 1 byte secondary data id: nessssss (parity bits, SDID bits)
+ * 1 byte data word count: necccccc (parity bits, NN Dword count)
+ * 2 byte Internal DID: VBI-line-# 0x80
+ * NN data bytes
+ * 1 byte checksum
+ * Fill bytes needed to fil out to 4*NN bytes of payload
+ *
+ * The RP codes for EAVs when in VIP-1.1 mode, not in raw mode, &
+ * in the vertical blanking interval are:
+ * 0xb0 (Task 0 VerticalBlank HorizontalBlank 0 0 0 0)
+ * 0xf0 (Task EvenField VerticalBlank HorizontalBlank 0 0 0 0)
+ *
+ * Since the V bit is only allowed to toggle in the EAV RP code, just
+ * before the first active region line and for active lines, they are:
+ * 0x90 (Task 0 0 HorizontalBlank 0 0 0 0)
+ * 0xd0 (Task EvenField 0 HorizontalBlank 0 0 0 0)
+ *
+ * The user application DID bytes we care about are:
+ * 0x91 (1 0 010 0 !ActiveLine AncDataPresent)
+ * 0x55 (0 1 010 2ndField !ActiveLine AncDataPresent)
+ *
+ */
+static const u8 sliced_vbi_did[2] = { 0x91, 0x55 };
+
+struct vbi_anc_data {
+ /* u8 eav[4]; */
+ /* u8 idle[]; Variable number of idle bytes */
+ u8 preamble[3];
+ u8 did;
+ u8 sdid;
+ u8 data_count;
+ u8 idid[2];
+ u8 payload[1]; /* data_count of payload */
+ /* u8 checksum; */
+ /* u8 fill[]; Variable number of fill bytes */
+};
+
+static int odd_parity(u8 c)
+{
+ c ^= (c >> 4);
+ c ^= (c >> 2);
+ c ^= (c >> 1);
+
+ return c & 1;
+}
+
+static int decode_vps(u8 *dst, u8 *p)
+{
+ static const u8 biphase_tbl[] = {
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
+ 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
+ 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
+ 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
+ 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
+ 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87,
+ 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3,
+ 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85,
+ 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1,
+ 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
+ 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
+ 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
+ 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
+ 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86,
+ 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2,
+ 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84,
+ 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0,
+ 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
+ 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
+ 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
+ 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
+ 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ };
+
+ u8 c, err = 0;
+ int i;
+
+ for (i = 0; i < 2 * 13; i += 2) {
+ err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]];
+ c = (biphase_tbl[p[i + 1]] & 0xf) |
+ ((biphase_tbl[p[i]] & 0xf) << 4);
+ dst[i / 2] = c;
+ }
+
+ return err & 0xf0;
+}
+
+int cx18_av_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ struct cx18_av_state *state = &cx->av_state;
+ static const u16 lcr2vbi[] = {
+ 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */
+ 0, V4L2_SLICED_WSS_625, 0, /* 4 */
+ V4L2_SLICED_CAPTION_525, /* 6 */
+ 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */
+ 0, 0, 0, 0
+ };
+ int is_pal = !(state->std & V4L2_STD_525_60);
+ int i;
+
+ memset(svbi, 0, sizeof(*svbi));
+ /* we're done if raw VBI is active */
+ if ((cx18_av_read(cx, 0x404) & 0x10) == 0)
+ return 0;
+
+ if (is_pal) {
+ for (i = 7; i <= 23; i++) {
+ u8 v = cx18_av_read(cx, 0x424 + i - 7);
+
+ svbi->service_lines[0][i] = lcr2vbi[v >> 4];
+ svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
+ svbi->service_set |= svbi->service_lines[0][i] |
+ svbi->service_lines[1][i];
+ }
+ } else {
+ for (i = 10; i <= 21; i++) {
+ u8 v = cx18_av_read(cx, 0x424 + i - 10);
+
+ svbi->service_lines[0][i] = lcr2vbi[v >> 4];
+ svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
+ svbi->service_set |= svbi->service_lines[0][i] |
+ svbi->service_lines[1][i];
+ }
+ }
+ return 0;
+}
+
+int cx18_av_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ struct cx18_av_state *state = &cx->av_state;
+
+ /* Setup standard */
+ cx18_av_std_setup(cx);
+
+ /* VBI Offset */
+ cx18_av_write(cx, 0x47f, state->slicer_line_delay);
+ cx18_av_write(cx, 0x404, 0x2e);
+ return 0;
+}
+
+int cx18_av_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ struct cx18_av_state *state = &cx->av_state;
+ int is_pal = !(state->std & V4L2_STD_525_60);
+ int i, x;
+ u8 lcr[24];
+
+ for (x = 0; x <= 23; x++)
+ lcr[x] = 0x00;
+
+ /* Setup standard */
+ cx18_av_std_setup(cx);
+
+ /* Sliced VBI */
+ cx18_av_write(cx, 0x404, 0x32); /* Ancillary data */
+ cx18_av_write(cx, 0x406, 0x13);
+ cx18_av_write(cx, 0x47f, state->slicer_line_delay);
+
+ /* Force impossible lines to 0 */
+ if (is_pal) {
+ for (i = 0; i <= 6; i++)
+ svbi->service_lines[0][i] =
+ svbi->service_lines[1][i] = 0;
+ } else {
+ for (i = 0; i <= 9; i++)
+ svbi->service_lines[0][i] =
+ svbi->service_lines[1][i] = 0;
+
+ for (i = 22; i <= 23; i++)
+ svbi->service_lines[0][i] =
+ svbi->service_lines[1][i] = 0;
+ }
+
+ /* Build register values for requested service lines */
+ for (i = 7; i <= 23; i++) {
+ for (x = 0; x <= 1; x++) {
+ switch (svbi->service_lines[1-x][i]) {
+ case V4L2_SLICED_TELETEXT_B:
+ lcr[i] |= 1 << (4 * x);
+ break;
+ case V4L2_SLICED_WSS_625:
+ lcr[i] |= 4 << (4 * x);
+ break;
+ case V4L2_SLICED_CAPTION_525:
+ lcr[i] |= 6 << (4 * x);
+ break;
+ case V4L2_SLICED_VPS:
+ lcr[i] |= 9 << (4 * x);
+ break;
+ }
+ }
+ }
+
+ if (is_pal) {
+ for (x = 1, i = 0x424; i <= 0x434; i++, x++)
+ cx18_av_write(cx, i, lcr[6 + x]);
+ } else {
+ for (x = 1, i = 0x424; i <= 0x430; i++, x++)
+ cx18_av_write(cx, i, lcr[9 + x]);
+ for (i = 0x431; i <= 0x434; i++)
+ cx18_av_write(cx, i, 0);
+ }
+
+ cx18_av_write(cx, 0x43c, 0x16);
+ /* Should match vblank set in cx18_av_std_setup() */
+ cx18_av_write(cx, 0x474, is_pal ? 38 : 26);
+ return 0;
+}
+
+int cx18_av_decode_vbi_line(struct v4l2_subdev *sd,
+ struct v4l2_decode_vbi_line *vbi)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ struct cx18_av_state *state = &cx->av_state;
+ struct vbi_anc_data *anc = (struct vbi_anc_data *)vbi->p;
+ u8 *p;
+ int did, sdid, l, err = 0;
+
+ /*
+ * Check for the ancillary data header for sliced VBI
+ */
+ if (anc->preamble[0] ||
+ anc->preamble[1] != 0xff || anc->preamble[2] != 0xff ||
+ (anc->did != sliced_vbi_did[0] &&
+ anc->did != sliced_vbi_did[1])) {
+ vbi->line = vbi->type = 0;
+ return 0;
+ }
+
+ did = anc->did;
+ sdid = anc->sdid & 0xf;
+ l = anc->idid[0] & 0x3f;
+ l += state->slicer_line_offset;
+ p = anc->payload;
+
+ /* Decode the SDID set by the slicer */
+ switch (sdid) {
+ case 1:
+ sdid = V4L2_SLICED_TELETEXT_B;
+ break;
+ case 4:
+ sdid = V4L2_SLICED_WSS_625;
+ break;
+ case 6:
+ sdid = V4L2_SLICED_CAPTION_525;
+ err = !odd_parity(p[0]) || !odd_parity(p[1]);
+ break;
+ case 9:
+ sdid = V4L2_SLICED_VPS;
+ if (decode_vps(p, p) != 0)
+ err = 1;
+ break;
+ default:
+ sdid = 0;
+ err = 1;
+ break;
+ }
+
+ vbi->type = err ? 0 : sdid;
+ vbi->line = err ? 0 : l;
+ vbi->is_second_field = err ? 0 : (did == sliced_vbi_did[1]);
+ vbi->p = p;
+ return 0;
+}
diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c
new file mode 100644
index 00000000..c07c849b
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-cards.c
@@ -0,0 +1,638 @@
+/*
+ * cx18 functions to query card hardware
+ *
+ * Derived from ivtv-cards.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include "cx18-driver.h"
+#include "cx18-cards.h"
+#include "cx18-av-core.h"
+#include "cx18-i2c.h"
+#include <media/cs5345.h>
+
+#define V4L2_STD_PAL_SECAM (V4L2_STD_PAL|V4L2_STD_SECAM)
+
+/********************** card configuration *******************************/
+
+/* usual i2c tuner addresses to probe */
+static struct cx18_card_tuner_i2c cx18_i2c_std = {
+ .radio = { I2C_CLIENT_END },
+ .demod = { 0x43, I2C_CLIENT_END },
+ .tv = { 0x61, 0x60, I2C_CLIENT_END },
+};
+
+/*
+ * usual i2c tuner addresses to probe with additional demod address for
+ * an NXP TDA8295 at 0x42 (N.B. it can possibly be at 0x4b or 0x4c too).
+ */
+static struct cx18_card_tuner_i2c cx18_i2c_nxp = {
+ .radio = { I2C_CLIENT_END },
+ .demod = { 0x42, 0x43, I2C_CLIENT_END },
+ .tv = { 0x61, 0x60, I2C_CLIENT_END },
+};
+
+/* Please add new PCI IDs to: http://pci-ids.ucw.cz/
+ This keeps the PCI ID database up to date. Note that the entries
+ must be added under vendor 0x4444 (Conexant) as subsystem IDs.
+ New vendor IDs should still be added to the vendor ID list. */
+
+/* Hauppauge HVR-1600 cards */
+
+/* Note: for Hauppauge cards the tveeprom information is used instead
+ of PCI IDs */
+static const struct cx18_card cx18_card_hvr1600_esmt = {
+ .type = CX18_CARD_HVR_1600_ESMT,
+ .name = "Hauppauge HVR-1600",
+ .comment = "Simultaneous Digital and Analog TV capture supported\n",
+ .v4l2_capabilities = CX18_CAP_ENCODER,
+ .hw_audio_ctrl = CX18_HW_418_AV,
+ .hw_muxer = CX18_HW_CS5345,
+ .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER |
+ CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL |
+ CX18_HW_Z8F0811_IR_HAUP,
+ .video_inputs = {
+ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 },
+ { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 },
+ { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE3 },
+ { CX18_CARD_INPUT_SVIDEO2, 2, CX18_AV_SVIDEO2 },
+ { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE4 },
+ },
+ .audio_inputs = {
+ { CX18_CARD_INPUT_AUD_TUNER,
+ CX18_AV_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 },
+ { CX18_CARD_INPUT_LINE_IN1,
+ CX18_AV_AUDIO_SERIAL1, CS5345_IN_2 },
+ { CX18_CARD_INPUT_LINE_IN2,
+ CX18_AV_AUDIO_SERIAL1, CS5345_IN_3 },
+ },
+ .radio_input = { CX18_CARD_INPUT_AUD_TUNER,
+ CX18_AV_AUDIO_SERIAL1, CS5345_IN_4 },
+ .ddr = {
+ /* ESMT M13S128324A-5B memory */
+ .chip_config = 0x003,
+ .refresh = 0x30c,
+ .timing1 = 0x44220e82,
+ .timing2 = 0x08,
+ .tune_lane = 0,
+ .initial_emrs = 0,
+ },
+ .gpio_init.initial_value = 0x3001,
+ .gpio_init.direction = 0x3001,
+ .gpio_i2c_slave_reset = {
+ .active_lo_mask = 0x3001,
+ .msecs_asserted = 10,
+ .msecs_recovery = 40,
+ .ir_reset_mask = 0x0001,
+ },
+ .i2c = &cx18_i2c_std,
+};
+
+static const struct cx18_card cx18_card_hvr1600_s5h1411 = {
+ .type = CX18_CARD_HVR_1600_S5H1411,
+ .name = "Hauppauge HVR-1600",
+ .comment = "Simultaneous Digital and Analog TV capture supported\n",
+ .v4l2_capabilities = CX18_CAP_ENCODER,
+ .hw_audio_ctrl = CX18_HW_418_AV,
+ .hw_muxer = CX18_HW_CS5345,
+ .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER |
+ CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL |
+ CX18_HW_Z8F0811_IR_HAUP,
+ .video_inputs = {
+ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 },
+ { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 },
+ { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE3 },
+ { CX18_CARD_INPUT_SVIDEO2, 2, CX18_AV_SVIDEO2 },
+ { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE4 },
+ },
+ .audio_inputs = {
+ { CX18_CARD_INPUT_AUD_TUNER,
+ CX18_AV_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 },
+ { CX18_CARD_INPUT_LINE_IN1,
+ CX18_AV_AUDIO_SERIAL1, CS5345_IN_2 },
+ { CX18_CARD_INPUT_LINE_IN2,
+ CX18_AV_AUDIO_SERIAL1, CS5345_IN_3 },
+ },
+ .radio_input = { CX18_CARD_INPUT_AUD_TUNER,
+ CX18_AV_AUDIO_SERIAL1, CS5345_IN_4 },
+ .ddr = {
+ /* ESMT M13S128324A-5B memory */
+ .chip_config = 0x003,
+ .refresh = 0x30c,
+ .timing1 = 0x44220e82,
+ .timing2 = 0x08,
+ .tune_lane = 0,
+ .initial_emrs = 0,
+ },
+ .gpio_init.initial_value = 0x3801,
+ .gpio_init.direction = 0x3801,
+ .gpio_i2c_slave_reset = {
+ .active_lo_mask = 0x3801,
+ .msecs_asserted = 10,
+ .msecs_recovery = 40,
+ .ir_reset_mask = 0x0001,
+ },
+ .i2c = &cx18_i2c_nxp,
+};
+
+static const struct cx18_card cx18_card_hvr1600_samsung = {
+ .type = CX18_CARD_HVR_1600_SAMSUNG,
+ .name = "Hauppauge HVR-1600 (Preproduction)",
+ .comment = "Simultaneous Digital and Analog TV capture supported\n",
+ .v4l2_capabilities = CX18_CAP_ENCODER,
+ .hw_audio_ctrl = CX18_HW_418_AV,
+ .hw_muxer = CX18_HW_CS5345,
+ .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER |
+ CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL |
+ CX18_HW_Z8F0811_IR_HAUP,
+ .video_inputs = {
+ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 },
+ { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 },
+ { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE3 },
+ { CX18_CARD_INPUT_SVIDEO2, 2, CX18_AV_SVIDEO2 },
+ { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE4 },
+ },
+ .audio_inputs = {
+ { CX18_CARD_INPUT_AUD_TUNER,
+ CX18_AV_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 },
+ { CX18_CARD_INPUT_LINE_IN1,
+ CX18_AV_AUDIO_SERIAL1, CS5345_IN_2 },
+ { CX18_CARD_INPUT_LINE_IN2,
+ CX18_AV_AUDIO_SERIAL1, CS5345_IN_3 },
+ },
+ .radio_input = { CX18_CARD_INPUT_AUD_TUNER,
+ CX18_AV_AUDIO_SERIAL1, CS5345_IN_4 },
+ .ddr = {
+ /* Samsung K4D263238G-VC33 memory */
+ .chip_config = 0x003,
+ .refresh = 0x30c,
+ .timing1 = 0x23230b73,
+ .timing2 = 0x08,
+ .tune_lane = 0,
+ .initial_emrs = 2,
+ },
+ .gpio_init.initial_value = 0x3001,
+ .gpio_init.direction = 0x3001,
+ .gpio_i2c_slave_reset = {
+ .active_lo_mask = 0x3001,
+ .msecs_asserted = 10,
+ .msecs_recovery = 40,
+ .ir_reset_mask = 0x0001,
+ },
+ .i2c = &cx18_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Compro VideoMate H900: note that this card is analog only! */
+
+static const struct cx18_card_pci_info cx18_pci_h900[] = {
+ { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_COMPRO, 0xe100 },
+ { 0, 0, 0 }
+};
+
+static const struct cx18_card cx18_card_h900 = {
+ .type = CX18_CARD_COMPRO_H900,
+ .name = "Compro VideoMate H900",
+ .comment = "Analog TV capture supported\n",
+ .v4l2_capabilities = CX18_CAP_ENCODER,
+ .hw_audio_ctrl = CX18_HW_418_AV,
+ .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_RESET_CTRL,
+ .video_inputs = {
+ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 },
+ { CX18_CARD_INPUT_SVIDEO1, 1,
+ CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 },
+ { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 },
+ },
+ .audio_inputs = {
+ { CX18_CARD_INPUT_AUD_TUNER,
+ CX18_AV_AUDIO5, 0 },
+ { CX18_CARD_INPUT_LINE_IN1,
+ CX18_AV_AUDIO_SERIAL1, 0 },
+ },
+ .radio_input = { CX18_CARD_INPUT_AUD_TUNER,
+ CX18_AV_AUDIO_SERIAL1, 0 },
+ .tuners = {
+ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+ },
+ .ddr = {
+ /* EtronTech EM6A9160TS-5G memory */
+ .chip_config = 0x50003,
+ .refresh = 0x753,
+ .timing1 = 0x24330e84,
+ .timing2 = 0x1f,
+ .tune_lane = 0,
+ .initial_emrs = 0,
+ },
+ .xceive_pin = 15,
+ .pci_list = cx18_pci_h900,
+ .i2c = &cx18_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan MPC718: not working at the moment! */
+
+static const struct cx18_card_pci_info cx18_pci_mpc718[] = {
+ { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_YUAN, 0x0718 },
+ { 0, 0, 0 }
+};
+
+static const struct cx18_card cx18_card_mpc718 = {
+ .type = CX18_CARD_YUAN_MPC718,
+ .name = "Yuan MPC718 MiniPCI DVB-T/Analog",
+ .comment = "Experimenters needed for device to work well.\n"
+ "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n",
+ .v4l2_capabilities = CX18_CAP_ENCODER,
+ .hw_audio_ctrl = CX18_HW_418_AV,
+ .hw_muxer = CX18_HW_GPIO_MUX,
+ .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER |
+ CX18_HW_GPIO_MUX | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL,
+ .video_inputs = {
+ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 },
+ { CX18_CARD_INPUT_SVIDEO1, 1,
+ CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 },
+ { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 },
+ { CX18_CARD_INPUT_SVIDEO2, 2,
+ CX18_AV_SVIDEO_LUMA7 | CX18_AV_SVIDEO_CHROMA8 },
+ { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE6 },
+ },
+ .audio_inputs = {
+ { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 },
+ { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 1 },
+ { CX18_CARD_INPUT_LINE_IN2, CX18_AV_AUDIO_SERIAL2, 1 },
+ },
+ .tuners = {
+ /* XC3028 tuner */
+ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+ },
+ /* FIXME - the FM radio is just a guess and driver doesn't use SIF */
+ .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 2 },
+ .ddr = {
+ /* Hynix HY5DU283222B DDR RAM */
+ .chip_config = 0x303,
+ .refresh = 0x3bd,
+ .timing1 = 0x36320966,
+ .timing2 = 0x1f,
+ .tune_lane = 0,
+ .initial_emrs = 2,
+ },
+ .gpio_init.initial_value = 0x1,
+ .gpio_init.direction = 0x3,
+ /* FIXME - these GPIO's are just guesses */
+ .gpio_audio_input = { .mask = 0x3,
+ .tuner = 0x1,
+ .linein = 0x3,
+ .radio = 0x1 },
+ .xceive_pin = 0,
+ .pci_list = cx18_pci_mpc718,
+ .i2c = &cx18_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* GoTView PCI */
+
+static const struct cx18_card_pci_info cx18_pci_gotview_dvd3[] = {
+ { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_GOTVIEW, 0x3343 },
+ { 0, 0, 0 }
+};
+
+static const struct cx18_card cx18_card_gotview_dvd3 = {
+ .type = CX18_CARD_GOTVIEW_PCI_DVD3,
+ .name = "GoTView PCI DVD3 Hybrid",
+ .comment = "Experimenters needed for device to work well.\n"
+ "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n",
+ .v4l2_capabilities = CX18_CAP_ENCODER,
+ .hw_audio_ctrl = CX18_HW_418_AV,
+ .hw_muxer = CX18_HW_GPIO_MUX,
+ .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER |
+ CX18_HW_GPIO_MUX | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL,
+ .video_inputs = {
+ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 },
+ { CX18_CARD_INPUT_SVIDEO1, 1,
+ CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 },
+ { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 },
+ { CX18_CARD_INPUT_SVIDEO2, 2,
+ CX18_AV_SVIDEO_LUMA7 | CX18_AV_SVIDEO_CHROMA8 },
+ { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE6 },
+ },
+ .audio_inputs = {
+ { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 },
+ { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 1 },
+ { CX18_CARD_INPUT_LINE_IN2, CX18_AV_AUDIO_SERIAL2, 1 },
+ },
+ .tuners = {
+ /* XC3028 tuner */
+ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+ },
+ /* FIXME - the FM radio is just a guess and driver doesn't use SIF */
+ .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 2 },
+ .ddr = {
+ /* Hynix HY5DU283222B DDR RAM */
+ .chip_config = 0x303,
+ .refresh = 0x3bd,
+ .timing1 = 0x36320966,
+ .timing2 = 0x1f,
+ .tune_lane = 0,
+ .initial_emrs = 2,
+ },
+ .gpio_init.initial_value = 0x1,
+ .gpio_init.direction = 0x3,
+
+ .gpio_audio_input = { .mask = 0x3,
+ .tuner = 0x1,
+ .linein = 0x2,
+ .radio = 0x1 },
+ .xceive_pin = 0,
+ .pci_list = cx18_pci_gotview_dvd3,
+ .i2c = &cx18_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Conexant Raptor PAL/SECAM: note that this card is analog only! */
+
+static const struct cx18_card_pci_info cx18_pci_cnxt_raptor_pal[] = {
+ { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_CONEXANT, 0x0009 },
+ { 0, 0, 0 }
+};
+
+static const struct cx18_card cx18_card_cnxt_raptor_pal = {
+ .type = CX18_CARD_CNXT_RAPTOR_PAL,
+ .name = "Conexant Raptor PAL/SECAM",
+ .comment = "Analog TV capture supported\n",
+ .v4l2_capabilities = CX18_CAP_ENCODER,
+ .hw_audio_ctrl = CX18_HW_418_AV,
+ .hw_muxer = CX18_HW_GPIO_MUX,
+ .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_MUX,
+ .video_inputs = {
+ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 },
+ { CX18_CARD_INPUT_SVIDEO1, 1,
+ CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 },
+ { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 },
+ { CX18_CARD_INPUT_SVIDEO2, 2,
+ CX18_AV_SVIDEO_LUMA7 | CX18_AV_SVIDEO_CHROMA8 },
+ { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE6 },
+ },
+ .audio_inputs = {
+ { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 },
+ { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 1 },
+ { CX18_CARD_INPUT_LINE_IN2, CX18_AV_AUDIO_SERIAL2, 1 },
+ },
+ .tuners = {
+ { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+ },
+ .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO_SERIAL1, 2 },
+ .ddr = {
+ /* MT 46V16M16 memory */
+ .chip_config = 0x50306,
+ .refresh = 0x753,
+ .timing1 = 0x33220953,
+ .timing2 = 0x09,
+ .tune_lane = 0,
+ .initial_emrs = 0,
+ },
+ .gpio_init.initial_value = 0x1002,
+ .gpio_init.direction = 0xf002,
+ .gpio_audio_input = { .mask = 0xf002,
+ .tuner = 0x1002, /* LED D1 Tuner AF */
+ .linein = 0x2000, /* LED D2 Line In 1 */
+ .radio = 0x4002 }, /* LED D3 Tuner AF */
+ .pci_list = cx18_pci_cnxt_raptor_pal,
+ .i2c = &cx18_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Toshiba Qosmio laptop internal DVB-T/Analog Hybrid Tuner */
+
+static const struct cx18_card_pci_info cx18_pci_toshiba_qosmio_dvbt[] = {
+ { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_TOSHIBA, 0x0110 },
+ { 0, 0, 0 }
+};
+
+static const struct cx18_card cx18_card_toshiba_qosmio_dvbt = {
+ .type = CX18_CARD_TOSHIBA_QOSMIO_DVBT,
+ .name = "Toshiba Qosmio DVB-T/Analog",
+ .comment = "Experimenters and photos needed for device to work well.\n"
+ "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n",
+ .v4l2_capabilities = CX18_CAP_ENCODER,
+ .hw_audio_ctrl = CX18_HW_418_AV,
+ .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_RESET_CTRL,
+ .video_inputs = {
+ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE6 },
+ { CX18_CARD_INPUT_SVIDEO1, 1,
+ CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 },
+ { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 },
+ },
+ .audio_inputs = {
+ { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 },
+ { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 1 },
+ },
+ .tuners = {
+ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+ },
+ .ddr = {
+ .chip_config = 0x202,
+ .refresh = 0x3bb,
+ .timing1 = 0x33320a63,
+ .timing2 = 0x0a,
+ .tune_lane = 0,
+ .initial_emrs = 0x42,
+ },
+ .xceive_pin = 15,
+ .pci_list = cx18_pci_toshiba_qosmio_dvbt,
+ .i2c = &cx18_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Leadtek WinFast PVR2100 */
+
+static const struct cx18_card_pci_info cx18_pci_leadtek_pvr2100[] = {
+ { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_LEADTEK, 0x6f27 }, /* PVR2100 */
+ { 0, 0, 0 }
+};
+
+static const struct cx18_card cx18_card_leadtek_pvr2100 = {
+ .type = CX18_CARD_LEADTEK_PVR2100,
+ .name = "Leadtek WinFast PVR2100",
+ .comment = "Experimenters and photos needed for device to work well.\n"
+ "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n",
+ .v4l2_capabilities = CX18_CAP_ENCODER,
+ .hw_audio_ctrl = CX18_HW_418_AV,
+ .hw_muxer = CX18_HW_GPIO_MUX,
+ .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_MUX |
+ CX18_HW_GPIO_RESET_CTRL,
+ .video_inputs = {
+ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 },
+ { CX18_CARD_INPUT_SVIDEO1, 1,
+ CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 },
+ { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE7 },
+ { CX18_CARD_INPUT_COMPONENT1, 1, CX18_AV_COMPONENT1 },
+ },
+ .audio_inputs = {
+ { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 },
+ { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 1 },
+ },
+ .tuners = {
+ /* XC2028 tuner */
+ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+ },
+ .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 2 },
+ .ddr = {
+ /* Pointer to proper DDR config values provided by Terry Wu */
+ .chip_config = 0x303,
+ .refresh = 0x3bb,
+ .timing1 = 0x24220e83,
+ .timing2 = 0x1f,
+ .tune_lane = 0,
+ .initial_emrs = 0x2,
+ },
+ .gpio_init.initial_value = 0x6,
+ .gpio_init.direction = 0x7,
+ .gpio_audio_input = { .mask = 0x7,
+ .tuner = 0x6, .linein = 0x2, .radio = 0x2 },
+ .xceive_pin = 1,
+ .pci_list = cx18_pci_leadtek_pvr2100,
+ .i2c = &cx18_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Leadtek WinFast DVR3100 H */
+
+static const struct cx18_card_pci_info cx18_pci_leadtek_dvr3100h[] = {
+ { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_LEADTEK, 0x6690 }, /* DVR3100 H */
+ { 0, 0, 0 }
+};
+
+static const struct cx18_card cx18_card_leadtek_dvr3100h = {
+ .type = CX18_CARD_LEADTEK_DVR3100H,
+ .name = "Leadtek WinFast DVR3100 H",
+ .comment = "Simultaneous DVB-T and Analog capture supported,\n"
+ "\texcept when capturing Analog from the antenna input.\n",
+ .v4l2_capabilities = CX18_CAP_ENCODER,
+ .hw_audio_ctrl = CX18_HW_418_AV,
+ .hw_muxer = CX18_HW_GPIO_MUX,
+ .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_MUX |
+ CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL,
+ .video_inputs = {
+ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 },
+ { CX18_CARD_INPUT_SVIDEO1, 1,
+ CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 },
+ { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE7 },
+ { CX18_CARD_INPUT_COMPONENT1, 1, CX18_AV_COMPONENT1 },
+ },
+ .audio_inputs = {
+ { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 },
+ { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 1 },
+ },
+ .tuners = {
+ /* XC3028 tuner */
+ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+ },
+ .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 2 },
+ .ddr = {
+ /* Pointer to proper DDR config values provided by Terry Wu */
+ .chip_config = 0x303,
+ .refresh = 0x3bb,
+ .timing1 = 0x24220e83,
+ .timing2 = 0x1f,
+ .tune_lane = 0,
+ .initial_emrs = 0x2,
+ },
+ .gpio_init.initial_value = 0x6,
+ .gpio_init.direction = 0x7,
+ .gpio_audio_input = { .mask = 0x7,
+ .tuner = 0x6, .linein = 0x2, .radio = 0x2 },
+ .xceive_pin = 1,
+ .pci_list = cx18_pci_leadtek_dvr3100h,
+ .i2c = &cx18_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+static const struct cx18_card *cx18_card_list[] = {
+ &cx18_card_hvr1600_esmt,
+ &cx18_card_hvr1600_samsung,
+ &cx18_card_h900,
+ &cx18_card_mpc718,
+ &cx18_card_cnxt_raptor_pal,
+ &cx18_card_toshiba_qosmio_dvbt,
+ &cx18_card_leadtek_pvr2100,
+ &cx18_card_leadtek_dvr3100h,
+ &cx18_card_gotview_dvd3,
+ &cx18_card_hvr1600_s5h1411
+};
+
+const struct cx18_card *cx18_get_card(u16 index)
+{
+ if (index >= ARRAY_SIZE(cx18_card_list))
+ return NULL;
+ return cx18_card_list[index];
+}
+
+int cx18_get_input(struct cx18 *cx, u16 index, struct v4l2_input *input)
+{
+ const struct cx18_card_video_input *card_input =
+ cx->card->video_inputs + index;
+ static const char * const input_strs[] = {
+ "Tuner 1",
+ "S-Video 1",
+ "S-Video 2",
+ "Composite 1",
+ "Composite 2",
+ "Component 1"
+ };
+
+ if (index >= cx->nof_inputs)
+ return -EINVAL;
+ input->index = index;
+ strlcpy(input->name, input_strs[card_input->video_type - 1],
+ sizeof(input->name));
+ input->type = (card_input->video_type == CX18_CARD_INPUT_VID_TUNER ?
+ V4L2_INPUT_TYPE_TUNER : V4L2_INPUT_TYPE_CAMERA);
+ input->audioset = (1 << cx->nof_audio_inputs) - 1;
+ input->std = (input->type == V4L2_INPUT_TYPE_TUNER) ?
+ cx->tuner_std : V4L2_STD_ALL;
+ return 0;
+}
+
+int cx18_get_audio_input(struct cx18 *cx, u16 index, struct v4l2_audio *audio)
+{
+ const struct cx18_card_audio_input *aud_input =
+ cx->card->audio_inputs + index;
+ static const char * const input_strs[] = {
+ "Tuner 1",
+ "Line In 1",
+ "Line In 2"
+ };
+
+ memset(audio, 0, sizeof(*audio));
+ if (index >= cx->nof_audio_inputs)
+ return -EINVAL;
+ strlcpy(audio->name, input_strs[aud_input->audio_type - 1],
+ sizeof(audio->name));
+ audio->index = index;
+ audio->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+}
diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h
new file mode 100644
index 00000000..add7391e
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-cards.h
@@ -0,0 +1,157 @@
+/*
+ * cx18 functions to query card hardware
+ *
+ * Derived from ivtv-cards.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* hardware flags */
+#define CX18_HW_TUNER (1 << 0)
+#define CX18_HW_TVEEPROM (1 << 1)
+#define CX18_HW_CS5345 (1 << 2)
+#define CX18_HW_DVB (1 << 3)
+#define CX18_HW_418_AV (1 << 4)
+#define CX18_HW_GPIO_MUX (1 << 5)
+#define CX18_HW_GPIO_RESET_CTRL (1 << 6)
+#define CX18_HW_Z8F0811_IR_TX_HAUP (1 << 7)
+#define CX18_HW_Z8F0811_IR_RX_HAUP (1 << 8)
+#define CX18_HW_Z8F0811_IR_HAUP (CX18_HW_Z8F0811_IR_RX_HAUP | \
+ CX18_HW_Z8F0811_IR_TX_HAUP)
+
+#define CX18_HW_IR_ANY (CX18_HW_Z8F0811_IR_RX_HAUP | \
+ CX18_HW_Z8F0811_IR_TX_HAUP)
+
+/* video inputs */
+#define CX18_CARD_INPUT_VID_TUNER 1
+#define CX18_CARD_INPUT_SVIDEO1 2
+#define CX18_CARD_INPUT_SVIDEO2 3
+#define CX18_CARD_INPUT_COMPOSITE1 4
+#define CX18_CARD_INPUT_COMPOSITE2 5
+#define CX18_CARD_INPUT_COMPONENT1 6
+
+/* audio inputs */
+#define CX18_CARD_INPUT_AUD_TUNER 1
+#define CX18_CARD_INPUT_LINE_IN1 2
+#define CX18_CARD_INPUT_LINE_IN2 3
+
+#define CX18_CARD_MAX_VIDEO_INPUTS 6
+#define CX18_CARD_MAX_AUDIO_INPUTS 3
+#define CX18_CARD_MAX_TUNERS 2
+
+/* V4L2 capability aliases */
+#define CX18_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \
+ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | \
+ V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE)
+
+struct cx18_card_video_input {
+ u8 video_type; /* video input type */
+ u8 audio_index; /* index in cx18_card_audio_input array */
+ u32 video_input; /* hardware video input */
+};
+
+struct cx18_card_audio_input {
+ u8 audio_type; /* audio input type */
+ u32 audio_input; /* hardware audio input */
+ u16 muxer_input; /* hardware muxer input for boards with a
+ multiplexer chip */
+};
+
+struct cx18_card_pci_info {
+ u16 device;
+ u16 subsystem_vendor;
+ u16 subsystem_device;
+};
+
+/* GPIO definitions */
+
+/* The mask is the set of bits used by the operation */
+
+struct cx18_gpio_init { /* set initial GPIO DIR and OUT values */
+ u32 direction; /* DIR setting. Leave to 0 if no init is needed */
+ u32 initial_value;
+};
+
+struct cx18_gpio_i2c_slave_reset {
+ u32 active_lo_mask; /* GPIO outputs that reset i2c chips when low */
+ u32 active_hi_mask; /* GPIO outputs that reset i2c chips when high */
+ int msecs_asserted; /* time period reset must remain asserted */
+ int msecs_recovery; /* time after deassert for chips to be ready */
+ u32 ir_reset_mask; /* GPIO to reset the Zilog Z8F0811 IR contoller */
+};
+
+struct cx18_gpio_audio_input { /* select tuner/line in input */
+ u32 mask; /* leave to 0 if not supported */
+ u32 tuner;
+ u32 linein;
+ u32 radio;
+};
+
+struct cx18_card_tuner {
+ v4l2_std_id std; /* standard for which the tuner is suitable */
+ int tuner; /* tuner ID (from tuner.h) */
+};
+
+struct cx18_card_tuner_i2c {
+ unsigned short radio[2];/* radio tuner i2c address to probe */
+ unsigned short demod[3];/* demodulator i2c address to probe */
+ unsigned short tv[4]; /* tv tuner i2c addresses to probe */
+};
+
+struct cx18_ddr { /* DDR config data */
+ u32 chip_config;
+ u32 refresh;
+ u32 timing1;
+ u32 timing2;
+ u32 tune_lane;
+ u32 initial_emrs;
+};
+
+/* for card information/parameters */
+struct cx18_card {
+ int type;
+ char *name;
+ char *comment;
+ u32 v4l2_capabilities;
+ u32 hw_audio_ctrl; /* hardware used for the V4L2 controls (only
+ 1 dev allowed currently) */
+ u32 hw_muxer; /* hardware used to multiplex audio input */
+ u32 hw_all; /* all hardware used by the board */
+ struct cx18_card_video_input video_inputs[CX18_CARD_MAX_VIDEO_INPUTS];
+ struct cx18_card_audio_input audio_inputs[CX18_CARD_MAX_AUDIO_INPUTS];
+ struct cx18_card_audio_input radio_input;
+
+ /* GPIO card-specific settings */
+ u8 xceive_pin; /* XCeive tuner GPIO reset pin */
+ struct cx18_gpio_init gpio_init;
+ struct cx18_gpio_i2c_slave_reset gpio_i2c_slave_reset;
+ struct cx18_gpio_audio_input gpio_audio_input;
+
+ struct cx18_card_tuner tuners[CX18_CARD_MAX_TUNERS];
+ struct cx18_card_tuner_i2c *i2c;
+
+ struct cx18_ddr ddr;
+
+ /* list of device and subsystem vendor/devices that
+ correspond to this card type. */
+ const struct cx18_card_pci_info *pci_list;
+};
+
+int cx18_get_input(struct cx18 *cx, u16 index, struct v4l2_input *input);
+int cx18_get_audio_input(struct cx18 *cx, u16 index, struct v4l2_audio *input);
+const struct cx18_card *cx18_get_card(u16 index);
diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c
new file mode 100644
index 00000000..282a3d29
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-controls.c
@@ -0,0 +1,131 @@
+/*
+ * cx18 ioctl control functions
+ *
+ * Derived from ivtv-controls.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include "cx18-driver.h"
+#include "cx18-cards.h"
+#include "cx18-ioctl.h"
+#include "cx18-audio.h"
+#include "cx18-mailbox.h"
+#include "cx18-controls.h"
+
+static int cx18_s_stream_vbi_fmt(struct cx2341x_handler *cxhdl, u32 fmt)
+{
+ struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl);
+ int type = cxhdl->stream_type->val;
+
+ if (atomic_read(&cx->ana_capturing) > 0)
+ return -EBUSY;
+
+ if (fmt != V4L2_MPEG_STREAM_VBI_FMT_IVTV ||
+ !(type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS ||
+ type == V4L2_MPEG_STREAM_TYPE_MPEG2_DVD ||
+ type == V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD)) {
+ /* Only IVTV fmt VBI insertion & only MPEG-2 PS type streams */
+ cx->vbi.insert_mpeg = V4L2_MPEG_STREAM_VBI_FMT_NONE;
+ CX18_DEBUG_INFO("disabled insertion of sliced VBI data into "
+ "the MPEG stream\n");
+ return 0;
+ }
+
+ /* Allocate sliced VBI buffers if needed. */
+ if (cx->vbi.sliced_mpeg_data[0] == NULL) {
+ int i;
+
+ for (i = 0; i < CX18_VBI_FRAMES; i++) {
+ cx->vbi.sliced_mpeg_data[i] =
+ kmalloc(CX18_SLICED_MPEG_DATA_BUFSZ, GFP_KERNEL);
+ if (cx->vbi.sliced_mpeg_data[i] == NULL) {
+ while (--i >= 0) {
+ kfree(cx->vbi.sliced_mpeg_data[i]);
+ cx->vbi.sliced_mpeg_data[i] = NULL;
+ }
+ cx->vbi.insert_mpeg =
+ V4L2_MPEG_STREAM_VBI_FMT_NONE;
+ CX18_WARN("Unable to allocate buffers for "
+ "sliced VBI data insertion\n");
+ return -ENOMEM;
+ }
+ }
+ }
+
+ cx->vbi.insert_mpeg = fmt;
+ CX18_DEBUG_INFO("enabled insertion of sliced VBI data into the MPEG PS,"
+ "when sliced VBI is enabled\n");
+
+ /*
+ * If our current settings have no lines set for capture, store a valid,
+ * default set of service lines to capture, in our current settings.
+ */
+ if (cx18_get_service_set(cx->vbi.sliced_in) == 0) {
+ if (cx->is_60hz)
+ cx->vbi.sliced_in->service_set =
+ V4L2_SLICED_CAPTION_525;
+ else
+ cx->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625;
+ cx18_expand_service_set(cx->vbi.sliced_in, cx->is_50hz);
+ }
+ return 0;
+}
+
+static int cx18_s_video_encoding(struct cx2341x_handler *cxhdl, u32 val)
+{
+ struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl);
+ int is_mpeg1 = val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
+ struct v4l2_mbus_framefmt fmt;
+
+ /* fix videodecoder resolution */
+ fmt.width = cxhdl->width / (is_mpeg1 ? 2 : 1);
+ fmt.height = cxhdl->height;
+ fmt.code = V4L2_MBUS_FMT_FIXED;
+ v4l2_subdev_call(cx->sd_av, video, s_mbus_fmt, &fmt);
+ return 0;
+}
+
+static int cx18_s_audio_sampling_freq(struct cx2341x_handler *cxhdl, u32 idx)
+{
+ static const u32 freqs[3] = { 44100, 48000, 32000 };
+ struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl);
+
+ /* The audio clock of the digitizer must match the codec sample
+ rate otherwise you get some very strange effects. */
+ if (idx < ARRAY_SIZE(freqs))
+ cx18_call_all(cx, audio, s_clock_freq, freqs[idx]);
+ return 0;
+}
+
+static int cx18_s_audio_mode(struct cx2341x_handler *cxhdl, u32 val)
+{
+ struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl);
+
+ cx->dualwatch_stereo_mode = val;
+ return 0;
+}
+
+struct cx2341x_handler_ops cx18_cxhdl_ops = {
+ .s_audio_mode = cx18_s_audio_mode,
+ .s_audio_sampling_freq = cx18_s_audio_sampling_freq,
+ .s_video_encoding = cx18_s_video_encoding,
+ .s_stream_vbi_fmt = cx18_s_stream_vbi_fmt,
+};
diff --git a/drivers/media/video/cx18/cx18-controls.h b/drivers/media/video/cx18/cx18-controls.h
new file mode 100644
index 00000000..cb5dfc7b
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-controls.h
@@ -0,0 +1,24 @@
+/*
+ * cx18 ioctl control functions
+ *
+ * Derived from ivtv-controls.h
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+extern struct cx2341x_handler_ops cx18_cxhdl_ops;
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c
new file mode 100644
index 00000000..b55d57cc
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-driver.c
@@ -0,0 +1,1359 @@
+/*
+ * cx18 driver initialization and card probing
+ *
+ * Derived from ivtv-driver.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-version.h"
+#include "cx18-cards.h"
+#include "cx18-i2c.h"
+#include "cx18-irq.h"
+#include "cx18-gpio.h"
+#include "cx18-firmware.h"
+#include "cx18-queue.h"
+#include "cx18-streams.h"
+#include "cx18-av-core.h"
+#include "cx18-scb.h"
+#include "cx18-mailbox.h"
+#include "cx18-ioctl.h"
+#include "cx18-controls.h"
+#include "tuner-xc2028.h"
+#include <linux/dma-mapping.h>
+#include <media/tveeprom.h>
+
+/* If you have already X v4l cards, then set this to X. This way
+ the device numbers stay matched. Example: you have a WinTV card
+ without radio and a Compro H900 with. Normally this would give a
+ video1 device together with a radio0 device for the Compro. By
+ setting this to 1 you ensure that radio0 is now also radio1. */
+int cx18_first_minor;
+
+/* Callback for registering extensions */
+int (*cx18_ext_init)(struct cx18 *);
+EXPORT_SYMBOL(cx18_ext_init);
+
+/* add your revision and whatnot here */
+static struct pci_device_id cx18_pci_tbl[] __devinitdata = {
+ {PCI_VENDOR_ID_CX, PCI_DEVICE_ID_CX23418,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, cx18_pci_tbl);
+
+static atomic_t cx18_instance = ATOMIC_INIT(0);
+
+/* Parameter declarations */
+static int cardtype[CX18_MAX_CARDS];
+static int tuner[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 };
+static int radio[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 };
+static unsigned cardtype_c = 1;
+static unsigned tuner_c = 1;
+static unsigned radio_c = 1;
+static char pal[] = "--";
+static char secam[] = "--";
+static char ntsc[] = "-";
+
+/* Buffers */
+static int enc_ts_buffers = CX18_DEFAULT_ENC_TS_BUFFERS;
+static int enc_mpg_buffers = CX18_DEFAULT_ENC_MPG_BUFFERS;
+static int enc_idx_buffers = CX18_DEFAULT_ENC_IDX_BUFFERS;
+static int enc_yuv_buffers = CX18_DEFAULT_ENC_YUV_BUFFERS;
+static int enc_vbi_buffers = CX18_DEFAULT_ENC_VBI_BUFFERS;
+static int enc_pcm_buffers = CX18_DEFAULT_ENC_PCM_BUFFERS;
+
+static int enc_ts_bufsize = CX18_DEFAULT_ENC_TS_BUFSIZE;
+static int enc_mpg_bufsize = CX18_DEFAULT_ENC_MPG_BUFSIZE;
+static int enc_idx_bufsize = CX18_DEFAULT_ENC_IDX_BUFSIZE;
+static int enc_yuv_bufsize = CX18_DEFAULT_ENC_YUV_BUFSIZE;
+static int enc_pcm_bufsize = CX18_DEFAULT_ENC_PCM_BUFSIZE;
+
+static int enc_ts_bufs = -1;
+static int enc_mpg_bufs = -1;
+static int enc_idx_bufs = CX18_MAX_FW_MDLS_PER_STREAM;
+static int enc_yuv_bufs = -1;
+static int enc_vbi_bufs = -1;
+static int enc_pcm_bufs = -1;
+
+
+static int cx18_pci_latency = 1;
+
+static int mmio_ndelay;
+static int retry_mmio = 1;
+
+int cx18_debug;
+
+module_param_array(tuner, int, &tuner_c, 0644);
+module_param_array(radio, int, &radio_c, 0644);
+module_param_array(cardtype, int, &cardtype_c, 0644);
+module_param_string(pal, pal, sizeof(pal), 0644);
+module_param_string(secam, secam, sizeof(secam), 0644);
+module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
+module_param_named(debug, cx18_debug, int, 0644);
+module_param(mmio_ndelay, int, 0644);
+module_param(retry_mmio, int, 0644);
+module_param(cx18_pci_latency, int, 0644);
+module_param(cx18_first_minor, int, 0644);
+
+module_param(enc_ts_buffers, int, 0644);
+module_param(enc_mpg_buffers, int, 0644);
+module_param(enc_idx_buffers, int, 0644);
+module_param(enc_yuv_buffers, int, 0644);
+module_param(enc_vbi_buffers, int, 0644);
+module_param(enc_pcm_buffers, int, 0644);
+
+module_param(enc_ts_bufsize, int, 0644);
+module_param(enc_mpg_bufsize, int, 0644);
+module_param(enc_idx_bufsize, int, 0644);
+module_param(enc_yuv_bufsize, int, 0644);
+module_param(enc_pcm_bufsize, int, 0644);
+
+module_param(enc_ts_bufs, int, 0644);
+module_param(enc_mpg_bufs, int, 0644);
+module_param(enc_idx_bufs, int, 0644);
+module_param(enc_yuv_bufs, int, 0644);
+module_param(enc_vbi_bufs, int, 0644);
+module_param(enc_pcm_bufs, int, 0644);
+
+MODULE_PARM_DESC(tuner, "Tuner type selection,\n"
+ "\t\t\tsee tuner.h for values");
+MODULE_PARM_DESC(radio,
+ "Enable or disable the radio. Use only if autodetection\n"
+ "\t\t\tfails. 0 = disable, 1 = enable");
+MODULE_PARM_DESC(cardtype,
+ "Only use this option if your card is not detected properly.\n"
+ "\t\tSpecify card type:\n"
+ "\t\t\t 1 = Hauppauge HVR 1600 (ESMT memory)\n"
+ "\t\t\t 2 = Hauppauge HVR 1600 (Samsung memory)\n"
+ "\t\t\t 3 = Compro VideoMate H900\n"
+ "\t\t\t 4 = Yuan MPC718\n"
+ "\t\t\t 5 = Conexant Raptor PAL/SECAM\n"
+ "\t\t\t 6 = Toshiba Qosmio DVB-T/Analog\n"
+ "\t\t\t 7 = Leadtek WinFast PVR2100\n"
+ "\t\t\t 8 = Leadtek WinFast DVR3100 H\n"
+ "\t\t\t 9 = GoTView PCI DVD3 Hybrid\n"
+ "\t\t\t 10 = Hauppauge HVR 1600 (S5H1411)\n"
+ "\t\t\t 0 = Autodetect (default)\n"
+ "\t\t\t-1 = Ignore this card\n\t\t");
+MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60");
+MODULE_PARM_DESC(secam, "Set SECAM standard: B, G, H, D, K, L, LC");
+MODULE_PARM_DESC(ntsc, "Set NTSC standard: M, J, K");
+MODULE_PARM_DESC(debug,
+ "Debug level (bitmask). Default: 0\n"
+ "\t\t\t 1/0x0001: warning\n"
+ "\t\t\t 2/0x0002: info\n"
+ "\t\t\t 4/0x0004: mailbox\n"
+ "\t\t\t 8/0x0008: dma\n"
+ "\t\t\t 16/0x0010: ioctl\n"
+ "\t\t\t 32/0x0020: file\n"
+ "\t\t\t 64/0x0040: i2c\n"
+ "\t\t\t128/0x0080: irq\n"
+ "\t\t\t256/0x0100: high volume\n");
+MODULE_PARM_DESC(cx18_pci_latency,
+ "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n"
+ "\t\t\tDefault: Yes");
+MODULE_PARM_DESC(retry_mmio,
+ "(Deprecated) MMIO writes are now always checked and retried\n"
+ "\t\t\tEffectively: 1 [Yes]");
+MODULE_PARM_DESC(mmio_ndelay,
+ "(Deprecated) MMIO accesses are now never purposely delayed\n"
+ "\t\t\tEffectively: 0 ns");
+MODULE_PARM_DESC(enc_ts_buffers,
+ "Encoder TS buffer memory (MB). (enc_ts_bufs can override)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFFERS));
+MODULE_PARM_DESC(enc_ts_bufsize,
+ "Size of an encoder TS buffer (kB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFSIZE));
+MODULE_PARM_DESC(enc_ts_bufs,
+ "Number of encoder TS buffers\n"
+ "\t\t\tDefault is computed from other enc_ts_* parameters");
+MODULE_PARM_DESC(enc_mpg_buffers,
+ "Encoder MPG buffer memory (MB). (enc_mpg_bufs can override)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS));
+MODULE_PARM_DESC(enc_mpg_bufsize,
+ "Size of an encoder MPG buffer (kB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFSIZE));
+MODULE_PARM_DESC(enc_mpg_bufs,
+ "Number of encoder MPG buffers\n"
+ "\t\t\tDefault is computed from other enc_mpg_* parameters");
+MODULE_PARM_DESC(enc_idx_buffers,
+ "(Deprecated) Encoder IDX buffer memory (MB)\n"
+ "\t\t\tIgnored, except 0 disables IDX buffer allocations\n"
+ "\t\t\tDefault: 1 [Enabled]");
+MODULE_PARM_DESC(enc_idx_bufsize,
+ "Size of an encoder IDX buffer (kB)\n"
+ "\t\t\tAllowed values are multiples of 1.5 kB rounded up\n"
+ "\t\t\t(multiples of size required for 64 index entries)\n"
+ "\t\t\tDefault: 2");
+MODULE_PARM_DESC(enc_idx_bufs,
+ "Number of encoder IDX buffers\n"
+ "\t\t\tDefault: " __stringify(CX18_MAX_FW_MDLS_PER_STREAM));
+MODULE_PARM_DESC(enc_yuv_buffers,
+ "Encoder YUV buffer memory (MB). (enc_yuv_bufs can override)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS));
+MODULE_PARM_DESC(enc_yuv_bufsize,
+ "Size of an encoder YUV buffer (kB)\n"
+ "\t\t\tAllowed values are multiples of 33.75 kB rounded up\n"
+ "\t\t\t(multiples of size required for 32 screen lines)\n"
+ "\t\t\tDefault: 102");
+MODULE_PARM_DESC(enc_yuv_bufs,
+ "Number of encoder YUV buffers\n"
+ "\t\t\tDefault is computed from other enc_yuv_* parameters");
+MODULE_PARM_DESC(enc_vbi_buffers,
+ "Encoder VBI buffer memory (MB). (enc_vbi_bufs can override)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_VBI_BUFFERS));
+MODULE_PARM_DESC(enc_vbi_bufs,
+ "Number of encoder VBI buffers\n"
+ "\t\t\tDefault is computed from enc_vbi_buffers");
+MODULE_PARM_DESC(enc_pcm_buffers,
+ "Encoder PCM buffer memory (MB). (enc_pcm_bufs can override)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFFERS));
+MODULE_PARM_DESC(enc_pcm_bufsize,
+ "Size of an encoder PCM buffer (kB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFSIZE));
+MODULE_PARM_DESC(enc_pcm_bufs,
+ "Number of encoder PCM buffers\n"
+ "\t\t\tDefault is computed from other enc_pcm_* parameters");
+
+MODULE_PARM_DESC(cx18_first_minor,
+ "Set device node number assigned to first card");
+
+MODULE_AUTHOR("Hans Verkuil");
+MODULE_DESCRIPTION("CX23418 driver");
+MODULE_SUPPORTED_DEVICE("CX23418 MPEG2 encoder");
+MODULE_LICENSE("GPL");
+
+MODULE_VERSION(CX18_VERSION);
+
+#if defined(CONFIG_MODULES) && defined(MODULE)
+static void request_module_async(struct work_struct *work)
+{
+ struct cx18 *dev = container_of(work, struct cx18, request_module_wk);
+
+ /* Make sure cx18-alsa module is loaded */
+ request_module("cx18-alsa");
+
+ /* Initialize cx18-alsa for this instance of the cx18 device */
+ if (cx18_ext_init != NULL)
+ cx18_ext_init(dev);
+}
+
+static void request_modules(struct cx18 *dev)
+{
+ INIT_WORK(&dev->request_module_wk, request_module_async);
+ schedule_work(&dev->request_module_wk);
+}
+
+static void flush_request_modules(struct cx18 *dev)
+{
+ flush_work_sync(&dev->request_module_wk);
+}
+#else
+#define request_modules(dev)
+#define flush_request_modules(dev)
+#endif /* CONFIG_MODULES */
+
+/* Generic utility functions */
+int cx18_msleep_timeout(unsigned int msecs, int intr)
+{
+ long int timeout = msecs_to_jiffies(msecs);
+ int sig;
+
+ do {
+ set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
+ timeout = schedule_timeout(timeout);
+ sig = intr ? signal_pending(current) : 0;
+ } while (!sig && timeout);
+ return sig;
+}
+
+/* Release ioremapped memory */
+static void cx18_iounmap(struct cx18 *cx)
+{
+ if (cx == NULL)
+ return;
+
+ /* Release io memory */
+ if (cx->enc_mem != NULL) {
+ CX18_DEBUG_INFO("releasing enc_mem\n");
+ iounmap(cx->enc_mem);
+ cx->enc_mem = NULL;
+ }
+}
+
+static void cx18_eeprom_dump(struct cx18 *cx, unsigned char *eedata, int len)
+{
+ int i;
+
+ CX18_INFO("eeprom dump:\n");
+ for (i = 0; i < len; i++) {
+ if (0 == (i % 16))
+ CX18_INFO("eeprom %02x:", i);
+ printk(KERN_CONT " %02x", eedata[i]);
+ if (15 == (i % 16))
+ printk(KERN_CONT "\n");
+ }
+}
+
+/* Hauppauge card? get values from tveeprom */
+void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv)
+{
+ struct i2c_client c;
+ u8 eedata[256];
+
+ memset(&c, 0, sizeof(c));
+ strlcpy(c.name, "cx18 tveeprom tmp", sizeof(c.name));
+ c.adapter = &cx->i2c_adap[0];
+ c.addr = 0xA0 >> 1;
+
+ memset(tv, 0, sizeof(*tv));
+ if (tveeprom_read(&c, eedata, sizeof(eedata)))
+ return;
+
+ switch (cx->card->type) {
+ case CX18_CARD_HVR_1600_ESMT:
+ case CX18_CARD_HVR_1600_SAMSUNG:
+ case CX18_CARD_HVR_1600_S5H1411:
+ tveeprom_hauppauge_analog(&c, tv, eedata);
+ break;
+ case CX18_CARD_YUAN_MPC718:
+ case CX18_CARD_GOTVIEW_PCI_DVD3:
+ tv->model = 0x718;
+ cx18_eeprom_dump(cx, eedata, sizeof(eedata));
+ CX18_INFO("eeprom PCI ID: %02x%02x:%02x%02x\n",
+ eedata[2], eedata[1], eedata[4], eedata[3]);
+ break;
+ default:
+ tv->model = 0xffffffff;
+ cx18_eeprom_dump(cx, eedata, sizeof(eedata));
+ break;
+ }
+}
+
+static void cx18_process_eeprom(struct cx18 *cx)
+{
+ struct tveeprom tv;
+
+ cx18_read_eeprom(cx, &tv);
+
+ /* Many thanks to Steven Toth from Hauppauge for providing the
+ model numbers */
+ /* Note: the Samsung memory models cannot be reliably determined
+ from the model number. Use the cardtype module option if you
+ have one of these preproduction models. */
+ switch (tv.model) {
+ case 74301: /* Retail models */
+ case 74321:
+ case 74351: /* OEM models */
+ case 74361:
+ /* Digital side is s5h1411/tda18271 */
+ cx->card = cx18_get_card(CX18_CARD_HVR_1600_S5H1411);
+ break;
+ case 74021: /* Retail models */
+ case 74031:
+ case 74041:
+ case 74141:
+ case 74541: /* OEM models */
+ case 74551:
+ case 74591:
+ case 74651:
+ case 74691:
+ case 74751:
+ case 74891:
+ /* Digital side is s5h1409/mxl5005s */
+ cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+ break;
+ case 0x718:
+ return;
+ case 0xffffffff:
+ CX18_INFO("Unknown EEPROM encoding\n");
+ return;
+ case 0:
+ CX18_ERR("Invalid EEPROM\n");
+ return;
+ default:
+ CX18_ERR("Unknown model %d, defaulting to original HVR-1600 "
+ "(cardtype=1)\n", tv.model);
+ cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+ break;
+ }
+
+ cx->v4l2_cap = cx->card->v4l2_capabilities;
+ cx->card_name = cx->card->name;
+ cx->card_i2c = cx->card->i2c;
+
+ CX18_INFO("Autodetected %s\n", cx->card_name);
+
+ if (tv.tuner_type == TUNER_ABSENT)
+ CX18_ERR("tveeprom cannot autodetect tuner!\n");
+
+ if (cx->options.tuner == -1)
+ cx->options.tuner = tv.tuner_type;
+ if (cx->options.radio == -1)
+ cx->options.radio = (tv.has_radio != 0);
+
+ if (cx->std != 0)
+ /* user specified tuner standard */
+ return;
+
+ /* autodetect tuner standard */
+#define TVEEPROM_TUNER_FORMAT_ALL (V4L2_STD_B | V4L2_STD_GH | \
+ V4L2_STD_MN | \
+ V4L2_STD_PAL_I | \
+ V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC | \
+ V4L2_STD_DK)
+ if ((tv.tuner_formats & TVEEPROM_TUNER_FORMAT_ALL)
+ == TVEEPROM_TUNER_FORMAT_ALL) {
+ CX18_DEBUG_INFO("Worldwide tuner detected\n");
+ cx->std = V4L2_STD_ALL;
+ } else if (tv.tuner_formats & V4L2_STD_PAL) {
+ CX18_DEBUG_INFO("PAL tuner detected\n");
+ cx->std |= V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
+ } else if (tv.tuner_formats & V4L2_STD_NTSC) {
+ CX18_DEBUG_INFO("NTSC tuner detected\n");
+ cx->std |= V4L2_STD_NTSC_M;
+ } else if (tv.tuner_formats & V4L2_STD_SECAM) {
+ CX18_DEBUG_INFO("SECAM tuner detected\n");
+ cx->std |= V4L2_STD_SECAM_L;
+ } else {
+ CX18_INFO("No tuner detected, default to NTSC-M\n");
+ cx->std |= V4L2_STD_NTSC_M;
+ }
+}
+
+static v4l2_std_id cx18_parse_std(struct cx18 *cx)
+{
+ switch (pal[0]) {
+ case '6':
+ return V4L2_STD_PAL_60;
+ case 'b':
+ case 'B':
+ case 'g':
+ case 'G':
+ return V4L2_STD_PAL_BG;
+ case 'h':
+ case 'H':
+ return V4L2_STD_PAL_H;
+ case 'n':
+ case 'N':
+ if (pal[1] == 'c' || pal[1] == 'C')
+ return V4L2_STD_PAL_Nc;
+ return V4L2_STD_PAL_N;
+ case 'i':
+ case 'I':
+ return V4L2_STD_PAL_I;
+ case 'd':
+ case 'D':
+ case 'k':
+ case 'K':
+ return V4L2_STD_PAL_DK;
+ case 'M':
+ case 'm':
+ return V4L2_STD_PAL_M;
+ case '-':
+ break;
+ default:
+ CX18_WARN("pal= argument not recognised\n");
+ return 0;
+ }
+
+ switch (secam[0]) {
+ case 'b':
+ case 'B':
+ case 'g':
+ case 'G':
+ case 'h':
+ case 'H':
+ return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
+ case 'd':
+ case 'D':
+ case 'k':
+ case 'K':
+ return V4L2_STD_SECAM_DK;
+ case 'l':
+ case 'L':
+ if (secam[1] == 'C' || secam[1] == 'c')
+ return V4L2_STD_SECAM_LC;
+ return V4L2_STD_SECAM_L;
+ case '-':
+ break;
+ default:
+ CX18_WARN("secam= argument not recognised\n");
+ return 0;
+ }
+
+ switch (ntsc[0]) {
+ case 'm':
+ case 'M':
+ return V4L2_STD_NTSC_M;
+ case 'j':
+ case 'J':
+ return V4L2_STD_NTSC_M_JP;
+ case 'k':
+ case 'K':
+ return V4L2_STD_NTSC_M_KR;
+ case '-':
+ break;
+ default:
+ CX18_WARN("ntsc= argument not recognised\n");
+ return 0;
+ }
+
+ /* no match found */
+ return 0;
+}
+
+static void cx18_process_options(struct cx18 *cx)
+{
+ int i, j;
+
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_TS] = enc_ts_buffers;
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers;
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_buffers;
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers;
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers;
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers;
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control only */
+
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_TS] = enc_ts_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control, no data */
+
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_TS] = enc_ts_bufsize;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufsize;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufsize;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufsize;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_active_samples * 36;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufsize;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control no data */
+
+ /* Ensure stream_buffers & stream_buf_size are valid */
+ for (i = 0; i < CX18_MAX_STREAMS; i++) {
+ if (cx->stream_buffers[i] == 0 || /* User said 0 buffers */
+ cx->options.megabytes[i] <= 0 || /* User said 0 MB total */
+ cx->stream_buf_size[i] <= 0) { /* User said buf size 0 */
+ cx->options.megabytes[i] = 0;
+ cx->stream_buffers[i] = 0;
+ cx->stream_buf_size[i] = 0;
+ continue;
+ }
+ /*
+ * YUV is a special case where the stream_buf_size needs to be
+ * an integral multiple of 33.75 kB (storage for 32 screens
+ * lines to maintain alignment in case of lost buffers).
+ *
+ * IDX is a special case where the stream_buf_size should be
+ * an integral multiple of 1.5 kB (storage for 64 index entries
+ * to maintain alignment in case of lost buffers).
+ *
+ */
+ if (i == CX18_ENC_STREAM_TYPE_YUV) {
+ cx->stream_buf_size[i] *= 1024;
+ cx->stream_buf_size[i] -=
+ (cx->stream_buf_size[i] % CX18_UNIT_ENC_YUV_BUFSIZE);
+
+ if (cx->stream_buf_size[i] < CX18_UNIT_ENC_YUV_BUFSIZE)
+ cx->stream_buf_size[i] =
+ CX18_UNIT_ENC_YUV_BUFSIZE;
+ } else if (i == CX18_ENC_STREAM_TYPE_IDX) {
+ cx->stream_buf_size[i] *= 1024;
+ cx->stream_buf_size[i] -=
+ (cx->stream_buf_size[i] % CX18_UNIT_ENC_IDX_BUFSIZE);
+
+ if (cx->stream_buf_size[i] < CX18_UNIT_ENC_IDX_BUFSIZE)
+ cx->stream_buf_size[i] =
+ CX18_UNIT_ENC_IDX_BUFSIZE;
+ }
+ /*
+ * YUV and IDX are special cases where the stream_buf_size is
+ * now in bytes.
+ * VBI is a special case where the stream_buf_size is fixed
+ * and already in bytes
+ */
+ if (i == CX18_ENC_STREAM_TYPE_VBI ||
+ i == CX18_ENC_STREAM_TYPE_YUV ||
+ i == CX18_ENC_STREAM_TYPE_IDX) {
+ if (cx->stream_buffers[i] < 0) {
+ cx->stream_buffers[i] =
+ cx->options.megabytes[i] * 1024 * 1024
+ / cx->stream_buf_size[i];
+ } else {
+ /* N.B. This might round down to 0 */
+ cx->options.megabytes[i] =
+ cx->stream_buffers[i]
+ * cx->stream_buf_size[i]/(1024 * 1024);
+ }
+ } else {
+ /* All other streams have stream_buf_size in kB here */
+ if (cx->stream_buffers[i] < 0) {
+ cx->stream_buffers[i] =
+ cx->options.megabytes[i] * 1024
+ / cx->stream_buf_size[i];
+ } else {
+ /* N.B. This might round down to 0 */
+ cx->options.megabytes[i] =
+ cx->stream_buffers[i]
+ * cx->stream_buf_size[i] / 1024;
+ }
+ /* convert from kB to bytes */
+ cx->stream_buf_size[i] *= 1024;
+ }
+ CX18_DEBUG_INFO("Stream type %d options: %d MB, %d buffers, "
+ "%d bytes\n", i, cx->options.megabytes[i],
+ cx->stream_buffers[i], cx->stream_buf_size[i]);
+ }
+
+ cx->options.cardtype = cardtype[cx->instance];
+ cx->options.tuner = tuner[cx->instance];
+ cx->options.radio = radio[cx->instance];
+
+ cx->std = cx18_parse_std(cx);
+ if (cx->options.cardtype == -1) {
+ CX18_INFO("Ignore card\n");
+ return;
+ }
+ cx->card = cx18_get_card(cx->options.cardtype - 1);
+ if (cx->card)
+ CX18_INFO("User specified %s card\n", cx->card->name);
+ else if (cx->options.cardtype != 0)
+ CX18_ERR("Unknown user specified type, trying to autodetect card\n");
+ if (cx->card == NULL) {
+ if (cx->pci_dev->subsystem_vendor == CX18_PCI_ID_HAUPPAUGE) {
+ cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+ CX18_INFO("Autodetected Hauppauge card\n");
+ }
+ }
+ if (cx->card == NULL) {
+ for (i = 0; (cx->card = cx18_get_card(i)); i++) {
+ if (cx->card->pci_list == NULL)
+ continue;
+ for (j = 0; cx->card->pci_list[j].device; j++) {
+ if (cx->pci_dev->device !=
+ cx->card->pci_list[j].device)
+ continue;
+ if (cx->pci_dev->subsystem_vendor !=
+ cx->card->pci_list[j].subsystem_vendor)
+ continue;
+ if (cx->pci_dev->subsystem_device !=
+ cx->card->pci_list[j].subsystem_device)
+ continue;
+ CX18_INFO("Autodetected %s card\n", cx->card->name);
+ goto done;
+ }
+ }
+ }
+done:
+
+ if (cx->card == NULL) {
+ cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+ CX18_ERR("Unknown card: vendor/device: [%04x:%04x]\n",
+ cx->pci_dev->vendor, cx->pci_dev->device);
+ CX18_ERR(" subsystem vendor/device: [%04x:%04x]\n",
+ cx->pci_dev->subsystem_vendor,
+ cx->pci_dev->subsystem_device);
+ CX18_ERR("Defaulting to %s card\n", cx->card->name);
+ CX18_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n");
+ CX18_ERR("card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)\n");
+ CX18_ERR("Prefix your subject line with [UNKNOWN CX18 CARD].\n");
+ }
+ cx->v4l2_cap = cx->card->v4l2_capabilities;
+ cx->card_name = cx->card->name;
+ cx->card_i2c = cx->card->i2c;
+}
+
+static int __devinit cx18_create_in_workq(struct cx18 *cx)
+{
+ snprintf(cx->in_workq_name, sizeof(cx->in_workq_name), "%s-in",
+ cx->v4l2_dev.name);
+ cx->in_work_queue = alloc_ordered_workqueue(cx->in_workq_name, 0);
+ if (cx->in_work_queue == NULL) {
+ CX18_ERR("Unable to create incoming mailbox handler thread\n");
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void __devinit cx18_init_in_work_orders(struct cx18 *cx)
+{
+ int i;
+ for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++) {
+ cx->in_work_order[i].cx = cx;
+ cx->in_work_order[i].str = cx->epu_debug_str;
+ INIT_WORK(&cx->in_work_order[i].work, cx18_in_work_handler);
+ }
+}
+
+/* Precondition: the cx18 structure has been memset to 0. Only
+ the dev and instance fields have been filled in.
+ No assumptions on the card type may be made here (see cx18_init_struct2
+ for that).
+ */
+static int __devinit cx18_init_struct1(struct cx18 *cx)
+{
+ int ret;
+
+ cx->base_addr = pci_resource_start(cx->pci_dev, 0);
+
+ mutex_init(&cx->serialize_lock);
+ mutex_init(&cx->gpio_lock);
+ mutex_init(&cx->epu2apu_mb_lock);
+ mutex_init(&cx->epu2cpu_mb_lock);
+
+ ret = cx18_create_in_workq(cx);
+ if (ret)
+ return ret;
+
+ cx18_init_in_work_orders(cx);
+
+ /* start counting open_id at 1 */
+ cx->open_id = 1;
+
+ /* Initial settings */
+ cx->cxhdl.port = CX2341X_PORT_MEMORY;
+ cx->cxhdl.capabilities = CX2341X_CAP_HAS_TS | CX2341X_CAP_HAS_SLICED_VBI;
+ cx->cxhdl.ops = &cx18_cxhdl_ops;
+ cx->cxhdl.func = cx18_api_func;
+ cx->cxhdl.priv = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];
+ ret = cx2341x_handler_init(&cx->cxhdl, 50);
+ if (ret)
+ return ret;
+ cx->v4l2_dev.ctrl_handler = &cx->cxhdl.hdl;
+
+ cx->temporal_strength = cx->cxhdl.video_temporal_filter->cur.val;
+ cx->spatial_strength = cx->cxhdl.video_spatial_filter->cur.val;
+ cx->filter_mode = cx->cxhdl.video_spatial_filter_mode->cur.val |
+ (cx->cxhdl.video_temporal_filter_mode->cur.val << 1) |
+ (cx->cxhdl.video_median_filter_type->cur.val << 2);
+
+ init_waitqueue_head(&cx->cap_w);
+ init_waitqueue_head(&cx->mb_apu_waitq);
+ init_waitqueue_head(&cx->mb_cpu_waitq);
+ init_waitqueue_head(&cx->dma_waitq);
+
+ /* VBI */
+ cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced;
+
+ /* IVTV style VBI insertion into MPEG streams */
+ INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_buf.list);
+ INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.list);
+ INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.buf_list);
+ list_add(&cx->vbi.sliced_mpeg_buf.list,
+ &cx->vbi.sliced_mpeg_mdl.buf_list);
+ return 0;
+}
+
+/* Second initialization part. Here the card type has been
+ autodetected. */
+static void __devinit cx18_init_struct2(struct cx18 *cx)
+{
+ int i;
+
+ for (i = 0; i < CX18_CARD_MAX_VIDEO_INPUTS; i++)
+ if (cx->card->video_inputs[i].video_type == 0)
+ break;
+ cx->nof_inputs = i;
+ for (i = 0; i < CX18_CARD_MAX_AUDIO_INPUTS; i++)
+ if (cx->card->audio_inputs[i].audio_type == 0)
+ break;
+ cx->nof_audio_inputs = i;
+
+ /* Find tuner input */
+ for (i = 0; i < cx->nof_inputs; i++) {
+ if (cx->card->video_inputs[i].video_type ==
+ CX18_CARD_INPUT_VID_TUNER)
+ break;
+ }
+ if (i == cx->nof_inputs)
+ i = 0;
+ cx->active_input = i;
+ cx->audio_input = cx->card->video_inputs[i].audio_index;
+}
+
+static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_id)
+{
+ u16 cmd;
+ unsigned char pci_latency;
+
+ CX18_DEBUG_INFO("Enabling pci device\n");
+
+ if (pci_enable_device(pci_dev)) {
+ CX18_ERR("Can't enable device %d!\n", cx->instance);
+ return -EIO;
+ }
+ if (pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32))) {
+ CX18_ERR("No suitable DMA available, card %d\n", cx->instance);
+ return -EIO;
+ }
+ if (!request_mem_region(cx->base_addr, CX18_MEM_SIZE, "cx18 encoder")) {
+ CX18_ERR("Cannot request encoder memory region, card %d\n",
+ cx->instance);
+ return -EIO;
+ }
+
+ /* Enable bus mastering and memory mapped IO for the CX23418 */
+ pci_read_config_word(pci_dev, PCI_COMMAND, &cmd);
+ cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
+ pci_write_config_word(pci_dev, PCI_COMMAND, cmd);
+
+ cx->card_rev = pci_dev->revision;
+ pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &pci_latency);
+
+ if (pci_latency < 64 && cx18_pci_latency) {
+ CX18_INFO("Unreasonably low latency timer, "
+ "setting to 64 (was %d)\n", pci_latency);
+ pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, 64);
+ pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &pci_latency);
+ }
+
+ CX18_DEBUG_INFO("cx%d (rev %d) at %02x:%02x.%x, "
+ "irq: %d, latency: %d, memory: 0x%lx\n",
+ cx->pci_dev->device, cx->card_rev, pci_dev->bus->number,
+ PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn),
+ cx->pci_dev->irq, pci_latency, (unsigned long)cx->base_addr);
+
+ return 0;
+}
+
+static void cx18_init_subdevs(struct cx18 *cx)
+{
+ u32 hw = cx->card->hw_all;
+ u32 device;
+ int i;
+
+ for (i = 0, device = 1; i < 32; i++, device <<= 1) {
+
+ if (!(device & hw))
+ continue;
+
+ switch (device) {
+ case CX18_HW_DVB:
+ case CX18_HW_TVEEPROM:
+ /* These subordinate devices do not use probing */
+ cx->hw_flags |= device;
+ break;
+ case CX18_HW_418_AV:
+ /* The A/V decoder gets probed earlier to set PLLs */
+ /* Just note that the card uses it (i.e. has analog) */
+ cx->hw_flags |= device;
+ break;
+ case CX18_HW_GPIO_RESET_CTRL:
+ /*
+ * The Reset Controller gets probed and added to
+ * hw_flags earlier for i2c adapter/bus initialization
+ */
+ break;
+ case CX18_HW_GPIO_MUX:
+ if (cx18_gpio_register(cx, device) == 0)
+ cx->hw_flags |= device;
+ break;
+ default:
+ if (cx18_i2c_register(cx, i) == 0)
+ cx->hw_flags |= device;
+ break;
+ }
+ }
+
+ if (cx->hw_flags & CX18_HW_418_AV)
+ cx->sd_av = cx18_find_hw(cx, CX18_HW_418_AV);
+
+ if (cx->card->hw_muxer != 0)
+ cx->sd_extmux = cx18_find_hw(cx, cx->card->hw_muxer);
+}
+
+static int __devinit cx18_probe(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_id)
+{
+ int retval = 0;
+ int i;
+ u32 devtype;
+ struct cx18 *cx;
+
+ /* FIXME - module parameter arrays constrain max instances */
+ i = atomic_inc_return(&cx18_instance) - 1;
+ if (i >= CX18_MAX_CARDS) {
+ printk(KERN_ERR "cx18: cannot manage card %d, driver has a "
+ "limit of 0 - %d\n", i, CX18_MAX_CARDS - 1);
+ return -ENOMEM;
+ }
+
+ cx = kzalloc(sizeof(struct cx18), GFP_ATOMIC);
+ if (cx == NULL) {
+ printk(KERN_ERR "cx18: cannot manage card %d, out of memory\n",
+ i);
+ return -ENOMEM;
+ }
+ cx->pci_dev = pci_dev;
+ cx->instance = i;
+
+ retval = v4l2_device_register(&pci_dev->dev, &cx->v4l2_dev);
+ if (retval) {
+ printk(KERN_ERR "cx18: v4l2_device_register of card %d failed"
+ "\n", cx->instance);
+ kfree(cx);
+ return retval;
+ }
+ snprintf(cx->v4l2_dev.name, sizeof(cx->v4l2_dev.name), "cx18-%d",
+ cx->instance);
+ CX18_INFO("Initializing card %d\n", cx->instance);
+
+ cx18_process_options(cx);
+ if (cx->options.cardtype == -1) {
+ retval = -ENODEV;
+ goto err;
+ }
+
+ retval = cx18_init_struct1(cx);
+ if (retval)
+ goto err;
+
+ CX18_DEBUG_INFO("base addr: 0x%08x\n", cx->base_addr);
+
+ /* PCI Device Setup */
+ retval = cx18_setup_pci(cx, pci_dev, pci_id);
+ if (retval != 0)
+ goto free_workqueues;
+
+ /* map io memory */
+ CX18_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n",
+ cx->base_addr + CX18_MEM_OFFSET, CX18_MEM_SIZE);
+ cx->enc_mem = ioremap_nocache(cx->base_addr + CX18_MEM_OFFSET,
+ CX18_MEM_SIZE);
+ if (!cx->enc_mem) {
+ CX18_ERR("ioremap failed. Can't get a window into CX23418 "
+ "memory and register space\n");
+ CX18_ERR("Each capture card with a CX23418 needs 64 MB of "
+ "vmalloc address space for the window\n");
+ CX18_ERR("Check the output of 'grep Vmalloc /proc/meminfo'\n");
+ CX18_ERR("Use the vmalloc= kernel command line option to set "
+ "VmallocTotal to a larger value\n");
+ retval = -ENOMEM;
+ goto free_mem;
+ }
+ cx->reg_mem = cx->enc_mem + CX18_REG_OFFSET;
+ devtype = cx18_read_reg(cx, 0xC72028);
+ switch (devtype & 0xff000000) {
+ case 0xff000000:
+ CX18_INFO("cx23418 revision %08x (A)\n", devtype);
+ break;
+ case 0x01000000:
+ CX18_INFO("cx23418 revision %08x (B)\n", devtype);
+ break;
+ default:
+ CX18_INFO("cx23418 revision %08x (Unknown)\n", devtype);
+ break;
+ }
+
+ cx18_init_power(cx, 1);
+ cx18_init_memory(cx);
+
+ cx->scb = (struct cx18_scb __iomem *)(cx->enc_mem + SCB_OFFSET);
+ cx18_init_scb(cx);
+
+ cx18_gpio_init(cx);
+
+ /* Initialize integrated A/V decoder early to set PLLs, just in case */
+ retval = cx18_av_probe(cx);
+ if (retval) {
+ CX18_ERR("Could not register A/V decoder subdevice\n");
+ goto free_map;
+ }
+
+ /* Initialize GPIO Reset Controller to do chip resets during i2c init */
+ if (cx->card->hw_all & CX18_HW_GPIO_RESET_CTRL) {
+ if (cx18_gpio_register(cx, CX18_HW_GPIO_RESET_CTRL) != 0)
+ CX18_WARN("Could not register GPIO reset controller"
+ "subdevice; proceeding anyway.\n");
+ else
+ cx->hw_flags |= CX18_HW_GPIO_RESET_CTRL;
+ }
+
+ /* active i2c */
+ CX18_DEBUG_INFO("activating i2c...\n");
+ retval = init_cx18_i2c(cx);
+ if (retval) {
+ CX18_ERR("Could not initialize i2c\n");
+ goto free_map;
+ }
+
+ if (cx->card->hw_all & CX18_HW_TVEEPROM) {
+ /* Based on the model number the cardtype may be changed.
+ The PCI IDs are not always reliable. */
+ const struct cx18_card *orig_card = cx->card;
+ cx18_process_eeprom(cx);
+
+ if (cx->card != orig_card) {
+ /* Changed the cardtype; re-reset the I2C chips */
+ cx18_gpio_init(cx);
+ cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL,
+ core, reset, (u32) CX18_GPIO_RESET_I2C);
+ }
+ }
+ if (cx->card->comment)
+ CX18_INFO("%s", cx->card->comment);
+ if (cx->card->v4l2_capabilities == 0) {
+ retval = -ENODEV;
+ goto free_i2c;
+ }
+ cx18_init_memory(cx);
+ cx18_init_scb(cx);
+
+ /* Register IRQ */
+ retval = request_irq(cx->pci_dev->irq, cx18_irq_handler,
+ IRQF_SHARED | IRQF_DISABLED,
+ cx->v4l2_dev.name, (void *)cx);
+ if (retval) {
+ CX18_ERR("Failed to register irq %d\n", retval);
+ goto free_i2c;
+ }
+
+ if (cx->std == 0)
+ cx->std = V4L2_STD_NTSC_M;
+
+ if (cx->options.tuner == -1) {
+ for (i = 0; i < CX18_CARD_MAX_TUNERS; i++) {
+ if ((cx->std & cx->card->tuners[i].std) == 0)
+ continue;
+ cx->options.tuner = cx->card->tuners[i].tuner;
+ break;
+ }
+ }
+ /* if no tuner was found, then pick the first tuner in the card list */
+ if (cx->options.tuner == -1 && cx->card->tuners[0].std) {
+ cx->std = cx->card->tuners[0].std;
+ if (cx->std & V4L2_STD_PAL)
+ cx->std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
+ else if (cx->std & V4L2_STD_NTSC)
+ cx->std = V4L2_STD_NTSC_M;
+ else if (cx->std & V4L2_STD_SECAM)
+ cx->std = V4L2_STD_SECAM_L;
+ cx->options.tuner = cx->card->tuners[0].tuner;
+ }
+ if (cx->options.radio == -1)
+ cx->options.radio = (cx->card->radio_input.audio_type != 0);
+
+ /* The card is now fully identified, continue with card-specific
+ initialization. */
+ cx18_init_struct2(cx);
+
+ cx18_init_subdevs(cx);
+
+ if (cx->std & V4L2_STD_525_60)
+ cx->is_60hz = 1;
+ else
+ cx->is_50hz = 1;
+
+ cx2341x_handler_set_50hz(&cx->cxhdl, !cx->is_60hz);
+
+ if (cx->options.radio > 0)
+ cx->v4l2_cap |= V4L2_CAP_RADIO;
+
+ if (cx->options.tuner > -1) {
+ struct tuner_setup setup;
+
+ setup.addr = ADDR_UNSET;
+ setup.type = cx->options.tuner;
+ setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */
+ if (cx->options.radio > 0)
+ setup.mode_mask |= T_RADIO;
+ setup.tuner_callback = (setup.type == TUNER_XC2028) ?
+ cx18_reset_tuner_gpio : NULL;
+ cx18_call_all(cx, tuner, s_type_addr, &setup);
+ if (setup.type == TUNER_XC2028) {
+ static struct xc2028_ctrl ctrl = {
+ .fname = XC2028_DEFAULT_FIRMWARE,
+ .max_len = 64,
+ };
+ struct v4l2_priv_tun_config cfg = {
+ .tuner = cx->options.tuner,
+ .priv = &ctrl,
+ };
+ cx18_call_all(cx, tuner, s_config, &cfg);
+ }
+ }
+
+ /* The tuner is fixed to the standard. The other inputs (e.g. S-Video)
+ are not. */
+ cx->tuner_std = cx->std;
+ if (cx->std == V4L2_STD_ALL)
+ cx->std = V4L2_STD_NTSC_M;
+
+ retval = cx18_streams_setup(cx);
+ if (retval) {
+ CX18_ERR("Error %d setting up streams\n", retval);
+ goto free_irq;
+ }
+ retval = cx18_streams_register(cx);
+ if (retval) {
+ CX18_ERR("Error %d registering devices\n", retval);
+ goto free_streams;
+ }
+
+ CX18_INFO("Initialized card: %s\n", cx->card_name);
+
+ /* Load cx18 submodules (cx18-alsa) */
+ request_modules(cx);
+ return 0;
+
+free_streams:
+ cx18_streams_cleanup(cx, 1);
+free_irq:
+ free_irq(cx->pci_dev->irq, (void *)cx);
+free_i2c:
+ exit_cx18_i2c(cx);
+free_map:
+ cx18_iounmap(cx);
+free_mem:
+ release_mem_region(cx->base_addr, CX18_MEM_SIZE);
+free_workqueues:
+ destroy_workqueue(cx->in_work_queue);
+err:
+ if (retval == 0)
+ retval = -ENODEV;
+ CX18_ERR("Error %d on initialization\n", retval);
+
+ v4l2_device_unregister(&cx->v4l2_dev);
+ kfree(cx);
+ return retval;
+}
+
+int cx18_init_on_first_open(struct cx18 *cx)
+{
+ int video_input;
+ int fw_retry_count = 3;
+ struct v4l2_frequency vf;
+ struct cx18_open_id fh;
+ v4l2_std_id std;
+
+ fh.cx = cx;
+
+ if (test_bit(CX18_F_I_FAILED, &cx->i_flags))
+ return -ENXIO;
+
+ if (test_and_set_bit(CX18_F_I_INITED, &cx->i_flags))
+ return 0;
+
+ while (--fw_retry_count > 0) {
+ /* load firmware */
+ if (cx18_firmware_init(cx) == 0)
+ break;
+ if (fw_retry_count > 1)
+ CX18_WARN("Retry loading firmware\n");
+ }
+
+ if (fw_retry_count == 0) {
+ set_bit(CX18_F_I_FAILED, &cx->i_flags);
+ return -ENXIO;
+ }
+ set_bit(CX18_F_I_LOADED_FW, &cx->i_flags);
+
+ /*
+ * Init the firmware twice to work around a silicon bug
+ * with the digital TS.
+ *
+ * The second firmware load requires us to normalize the APU state,
+ * or the audio for the first analog capture will be badly incorrect.
+ *
+ * I can't seem to call APU_RESETAI and have it succeed without the
+ * APU capturing audio, so we start and stop it here to do the reset
+ */
+
+ /* MPEG Encoding, 224 kbps, MPEG Layer II, 48 ksps */
+ cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0);
+ cx18_vapi(cx, CX18_APU_RESETAI, 0);
+ cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG);
+
+ fw_retry_count = 3;
+ while (--fw_retry_count > 0) {
+ /* load firmware */
+ if (cx18_firmware_init(cx) == 0)
+ break;
+ if (fw_retry_count > 1)
+ CX18_WARN("Retry loading firmware\n");
+ }
+
+ if (fw_retry_count == 0) {
+ set_bit(CX18_F_I_FAILED, &cx->i_flags);
+ return -ENXIO;
+ }
+
+ /*
+ * The second firmware load requires us to normalize the APU state,
+ * or the audio for the first analog capture will be badly incorrect.
+ *
+ * I can't seem to call APU_RESETAI and have it succeed without the
+ * APU capturing audio, so we start and stop it here to do the reset
+ */
+
+ /* MPEG Encoding, 224 kbps, MPEG Layer II, 48 ksps */
+ cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0);
+ cx18_vapi(cx, CX18_APU_RESETAI, 0);
+ cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG);
+
+ /* Init the A/V decoder, if it hasn't been already */
+ v4l2_subdev_call(cx->sd_av, core, load_fw);
+
+ vf.tuner = 0;
+ vf.type = V4L2_TUNER_ANALOG_TV;
+ vf.frequency = 6400; /* the tuner 'baseline' frequency */
+
+ /* Set initial frequency. For PAL/SECAM broadcasts no
+ 'default' channel exists AFAIK. */
+ if (cx->std == V4L2_STD_NTSC_M_JP)
+ vf.frequency = 1460; /* ch. 1 91250*16/1000 */
+ else if (cx->std & V4L2_STD_NTSC_M)
+ vf.frequency = 1076; /* ch. 4 67250*16/1000 */
+
+ video_input = cx->active_input;
+ cx->active_input++; /* Force update of input */
+ cx18_s_input(NULL, &fh, video_input);
+
+ /* Let the VIDIOC_S_STD ioctl do all the work, keeps the code
+ in one place. */
+ cx->std++; /* Force full standard initialization */
+ std = (cx->tuner_std == V4L2_STD_ALL) ? V4L2_STD_NTSC_M : cx->tuner_std;
+ cx18_s_std(NULL, &fh, &std);
+ cx18_s_frequency(NULL, &fh, &vf);
+ return 0;
+}
+
+static void cx18_cancel_in_work_orders(struct cx18 *cx)
+{
+ int i;
+ for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++)
+ cancel_work_sync(&cx->in_work_order[i].work);
+}
+
+static void cx18_cancel_out_work_orders(struct cx18 *cx)
+{
+ int i;
+ for (i = 0; i < CX18_MAX_STREAMS; i++)
+ if (&cx->streams[i].video_dev != NULL)
+ cancel_work_sync(&cx->streams[i].out_work_order);
+}
+
+static void cx18_remove(struct pci_dev *pci_dev)
+{
+ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+ struct cx18 *cx = to_cx18(v4l2_dev);
+ int i;
+
+ CX18_DEBUG_INFO("Removing Card\n");
+
+ flush_request_modules(cx);
+
+ /* Stop all captures */
+ CX18_DEBUG_INFO("Stopping all streams\n");
+ if (atomic_read(&cx->tot_capturing) > 0)
+ cx18_stop_all_captures(cx);
+
+ /* Stop interrupts that cause incoming work to be queued */
+ cx18_sw1_irq_disable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
+
+ /* Incoming work can cause outgoing work, so clean up incoming first */
+ cx18_cancel_in_work_orders(cx);
+ cx18_cancel_out_work_orders(cx);
+
+ /* Stop ack interrupts that may have been needed for work to finish */
+ cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
+
+ cx18_halt_firmware(cx);
+
+ destroy_workqueue(cx->in_work_queue);
+
+ cx18_streams_cleanup(cx, 1);
+
+ exit_cx18_i2c(cx);
+
+ free_irq(cx->pci_dev->irq, (void *)cx);
+
+ cx18_iounmap(cx);
+
+ release_mem_region(cx->base_addr, CX18_MEM_SIZE);
+
+ pci_disable_device(cx->pci_dev);
+
+ if (cx->vbi.sliced_mpeg_data[0] != NULL)
+ for (i = 0; i < CX18_VBI_FRAMES; i++)
+ kfree(cx->vbi.sliced_mpeg_data[i]);
+
+ v4l2_ctrl_handler_free(&cx->av_state.hdl);
+
+ CX18_INFO("Removed %s\n", cx->card_name);
+
+ v4l2_device_unregister(v4l2_dev);
+ kfree(cx);
+}
+
+
+/* define a pci_driver for card detection */
+static struct pci_driver cx18_pci_driver = {
+ .name = "cx18",
+ .id_table = cx18_pci_tbl,
+ .probe = cx18_probe,
+ .remove = cx18_remove,
+};
+
+static int __init module_start(void)
+{
+ printk(KERN_INFO "cx18: Start initialization, version %s\n",
+ CX18_VERSION);
+
+ /* Validate parameters */
+ if (cx18_first_minor < 0 || cx18_first_minor >= CX18_MAX_CARDS) {
+ printk(KERN_ERR "cx18: Exiting, cx18_first_minor must be between 0 and %d\n",
+ CX18_MAX_CARDS - 1);
+ return -1;
+ }
+
+ if (cx18_debug < 0 || cx18_debug > 511) {
+ cx18_debug = 0;
+ printk(KERN_INFO "cx18: Debug value must be >= 0 and <= 511!\n");
+ }
+
+ if (pci_register_driver(&cx18_pci_driver)) {
+ printk(KERN_ERR "cx18: Error detecting PCI card\n");
+ return -ENODEV;
+ }
+ printk(KERN_INFO "cx18: End initialization\n");
+ return 0;
+}
+
+static void __exit module_cleanup(void)
+{
+ pci_unregister_driver(&cx18_pci_driver);
+}
+
+module_init(module_start);
+module_exit(module_cleanup);
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h
new file mode 100644
index 00000000..7a37e0ee
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-driver.h
@@ -0,0 +1,730 @@
+/*
+ * cx18 driver internal defines and structures
+ *
+ * Derived from ivtv-driver.h
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#ifndef CX18_DRIVER_H
+#define CX18_DRIVER_H
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/list.h>
+#include <linux/unistd.h>
+#include <linux/pagemap.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <asm/byteorder.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+#include <media/tuner.h>
+#include <media/ir-kbd-i2c.h>
+#include "cx18-mailbox.h"
+#include "cx18-av-core.h"
+#include "cx23418.h"
+
+/* DVB */
+#include "demux.h"
+#include "dmxdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+#include "dvbdev.h"
+
+/* Videobuf / YUV support */
+#include <media/videobuf-core.h>
+#include <media/videobuf-vmalloc.h>
+
+#ifndef CONFIG_PCI
+# error "This driver requires kernel PCI support."
+#endif
+
+#define CX18_MEM_OFFSET 0x00000000
+#define CX18_MEM_SIZE 0x04000000
+#define CX18_REG_OFFSET 0x02000000
+
+/* Maximum cx18 driver instances. */
+#define CX18_MAX_CARDS 32
+
+/* Supported cards */
+#define CX18_CARD_HVR_1600_ESMT 0 /* Hauppauge HVR 1600 (ESMT memory) */
+#define CX18_CARD_HVR_1600_SAMSUNG 1 /* Hauppauge HVR 1600 (Samsung memory) */
+#define CX18_CARD_COMPRO_H900 2 /* Compro VideoMate H900 */
+#define CX18_CARD_YUAN_MPC718 3 /* Yuan MPC718 */
+#define CX18_CARD_CNXT_RAPTOR_PAL 4 /* Conexant Raptor PAL */
+#define CX18_CARD_TOSHIBA_QOSMIO_DVBT 5 /* Toshiba Qosmio Interal DVB-T/Analog*/
+#define CX18_CARD_LEADTEK_PVR2100 6 /* Leadtek WinFast PVR2100 */
+#define CX18_CARD_LEADTEK_DVR3100H 7 /* Leadtek WinFast DVR3100 H */
+#define CX18_CARD_GOTVIEW_PCI_DVD3 8 /* GoTView PCI DVD3 Hybrid */
+#define CX18_CARD_HVR_1600_S5H1411 9 /* Hauppauge HVR 1600 s5h1411/tda18271*/
+#define CX18_CARD_LAST 9
+
+#define CX18_ENC_STREAM_TYPE_MPG 0
+#define CX18_ENC_STREAM_TYPE_TS 1
+#define CX18_ENC_STREAM_TYPE_YUV 2
+#define CX18_ENC_STREAM_TYPE_VBI 3
+#define CX18_ENC_STREAM_TYPE_PCM 4
+#define CX18_ENC_STREAM_TYPE_IDX 5
+#define CX18_ENC_STREAM_TYPE_RAD 6
+#define CX18_MAX_STREAMS 7
+
+/* system vendor and device IDs */
+#define PCI_VENDOR_ID_CX 0x14f1
+#define PCI_DEVICE_ID_CX23418 0x5b7a
+
+/* subsystem vendor ID */
+#define CX18_PCI_ID_HAUPPAUGE 0x0070
+#define CX18_PCI_ID_COMPRO 0x185b
+#define CX18_PCI_ID_YUAN 0x12ab
+#define CX18_PCI_ID_CONEXANT 0x14f1
+#define CX18_PCI_ID_TOSHIBA 0x1179
+#define CX18_PCI_ID_LEADTEK 0x107D
+#define CX18_PCI_ID_GOTVIEW 0x5854
+
+/* ======================================================================== */
+/* ========================== START USER SETTABLE DMA VARIABLES =========== */
+/* ======================================================================== */
+
+/* DMA Buffers, Default size in MB allocated */
+#define CX18_DEFAULT_ENC_TS_BUFFERS 1
+#define CX18_DEFAULT_ENC_MPG_BUFFERS 2
+#define CX18_DEFAULT_ENC_IDX_BUFFERS 1
+#define CX18_DEFAULT_ENC_YUV_BUFFERS 2
+#define CX18_DEFAULT_ENC_VBI_BUFFERS 1
+#define CX18_DEFAULT_ENC_PCM_BUFFERS 1
+
+/* Maximum firmware DMA buffers per stream */
+#define CX18_MAX_FW_MDLS_PER_STREAM 63
+
+/* YUV buffer sizes in bytes to ensure integer # of frames per buffer */
+#define CX18_UNIT_ENC_YUV_BUFSIZE (720 * 32 * 3 / 2) /* bytes */
+#define CX18_625_LINE_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 576/32)
+#define CX18_525_LINE_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 480/32)
+
+/* IDX buffer size should be a multiple of the index entry size from the chip */
+struct cx18_enc_idx_entry {
+ __le32 length;
+ __le32 offset_low;
+ __le32 offset_high;
+ __le32 flags;
+ __le32 pts_low;
+ __le32 pts_high;
+} __attribute__ ((packed));
+#define CX18_UNIT_ENC_IDX_BUFSIZE \
+ (sizeof(struct cx18_enc_idx_entry) * V4L2_ENC_IDX_ENTRIES)
+
+/* DMA buffer, default size in kB allocated */
+#define CX18_DEFAULT_ENC_TS_BUFSIZE 32
+#define CX18_DEFAULT_ENC_MPG_BUFSIZE 32
+#define CX18_DEFAULT_ENC_IDX_BUFSIZE (CX18_UNIT_ENC_IDX_BUFSIZE * 1 / 1024 + 1)
+#define CX18_DEFAULT_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 3 / 1024 + 1)
+#define CX18_DEFAULT_ENC_PCM_BUFSIZE 4
+
+/* i2c stuff */
+#define I2C_CLIENTS_MAX 16
+
+/* debugging */
+
+/* Flag to turn on high volume debugging */
+#define CX18_DBGFLG_WARN (1 << 0)
+#define CX18_DBGFLG_INFO (1 << 1)
+#define CX18_DBGFLG_API (1 << 2)
+#define CX18_DBGFLG_DMA (1 << 3)
+#define CX18_DBGFLG_IOCTL (1 << 4)
+#define CX18_DBGFLG_FILE (1 << 5)
+#define CX18_DBGFLG_I2C (1 << 6)
+#define CX18_DBGFLG_IRQ (1 << 7)
+/* Flag to turn on high volume debugging */
+#define CX18_DBGFLG_HIGHVOL (1 << 8)
+
+/* NOTE: extra space before comma in 'fmt , ## args' is required for
+ gcc-2.95, otherwise it won't compile. */
+#define CX18_DEBUG(x, type, fmt, args...) \
+ do { \
+ if ((x) & cx18_debug) \
+ v4l2_info(&cx->v4l2_dev, " " type ": " fmt , ## args); \
+ } while (0)
+#define CX18_DEBUG_WARN(fmt, args...) CX18_DEBUG(CX18_DBGFLG_WARN, "warning", fmt , ## args)
+#define CX18_DEBUG_INFO(fmt, args...) CX18_DEBUG(CX18_DBGFLG_INFO, "info", fmt , ## args)
+#define CX18_DEBUG_API(fmt, args...) CX18_DEBUG(CX18_DBGFLG_API, "api", fmt , ## args)
+#define CX18_DEBUG_DMA(fmt, args...) CX18_DEBUG(CX18_DBGFLG_DMA, "dma", fmt , ## args)
+#define CX18_DEBUG_IOCTL(fmt, args...) CX18_DEBUG(CX18_DBGFLG_IOCTL, "ioctl", fmt , ## args)
+#define CX18_DEBUG_FILE(fmt, args...) CX18_DEBUG(CX18_DBGFLG_FILE, "file", fmt , ## args)
+#define CX18_DEBUG_I2C(fmt, args...) CX18_DEBUG(CX18_DBGFLG_I2C, "i2c", fmt , ## args)
+#define CX18_DEBUG_IRQ(fmt, args...) CX18_DEBUG(CX18_DBGFLG_IRQ, "irq", fmt , ## args)
+
+#define CX18_DEBUG_HIGH_VOL(x, type, fmt, args...) \
+ do { \
+ if (((x) & cx18_debug) && (cx18_debug & CX18_DBGFLG_HIGHVOL)) \
+ v4l2_info(&cx->v4l2_dev, " " type ": " fmt , ## args); \
+ } while (0)
+#define CX18_DEBUG_HI_WARN(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_WARN, "warning", fmt , ## args)
+#define CX18_DEBUG_HI_INFO(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_INFO, "info", fmt , ## args)
+#define CX18_DEBUG_HI_API(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_API, "api", fmt , ## args)
+#define CX18_DEBUG_HI_DMA(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_DMA, "dma", fmt , ## args)
+#define CX18_DEBUG_HI_IOCTL(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_IOCTL, "ioctl", fmt , ## args)
+#define CX18_DEBUG_HI_FILE(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_FILE, "file", fmt , ## args)
+#define CX18_DEBUG_HI_I2C(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_I2C, "i2c", fmt , ## args)
+#define CX18_DEBUG_HI_IRQ(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_IRQ, "irq", fmt , ## args)
+
+/* Standard kernel messages */
+#define CX18_ERR(fmt, args...) v4l2_err(&cx->v4l2_dev, fmt , ## args)
+#define CX18_WARN(fmt, args...) v4l2_warn(&cx->v4l2_dev, fmt , ## args)
+#define CX18_INFO(fmt, args...) v4l2_info(&cx->v4l2_dev, fmt , ## args)
+
+/* Messages for internal subdevs to use */
+#define CX18_DEBUG_DEV(x, dev, type, fmt, args...) \
+ do { \
+ if ((x) & cx18_debug) \
+ v4l2_info(dev, " " type ": " fmt , ## args); \
+ } while (0)
+#define CX18_DEBUG_WARN_DEV(dev, fmt, args...) \
+ CX18_DEBUG_DEV(CX18_DBGFLG_WARN, dev, "warning", fmt , ## args)
+#define CX18_DEBUG_INFO_DEV(dev, fmt, args...) \
+ CX18_DEBUG_DEV(CX18_DBGFLG_INFO, dev, "info", fmt , ## args)
+#define CX18_DEBUG_API_DEV(dev, fmt, args...) \
+ CX18_DEBUG_DEV(CX18_DBGFLG_API, dev, "api", fmt , ## args)
+#define CX18_DEBUG_DMA_DEV(dev, fmt, args...) \
+ CX18_DEBUG_DEV(CX18_DBGFLG_DMA, dev, "dma", fmt , ## args)
+#define CX18_DEBUG_IOCTL_DEV(dev, fmt, args...) \
+ CX18_DEBUG_DEV(CX18_DBGFLG_IOCTL, dev, "ioctl", fmt , ## args)
+#define CX18_DEBUG_FILE_DEV(dev, fmt, args...) \
+ CX18_DEBUG_DEV(CX18_DBGFLG_FILE, dev, "file", fmt , ## args)
+#define CX18_DEBUG_I2C_DEV(dev, fmt, args...) \
+ CX18_DEBUG_DEV(CX18_DBGFLG_I2C, dev, "i2c", fmt , ## args)
+#define CX18_DEBUG_IRQ_DEV(dev, fmt, args...) \
+ CX18_DEBUG_DEV(CX18_DBGFLG_IRQ, dev, "irq", fmt , ## args)
+
+#define CX18_DEBUG_HIGH_VOL_DEV(x, dev, type, fmt, args...) \
+ do { \
+ if (((x) & cx18_debug) && (cx18_debug & CX18_DBGFLG_HIGHVOL)) \
+ v4l2_info(dev, " " type ": " fmt , ## args); \
+ } while (0)
+#define CX18_DEBUG_HI_WARN_DEV(dev, fmt, args...) \
+ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_WARN, dev, "warning", fmt , ## args)
+#define CX18_DEBUG_HI_INFO_DEV(dev, fmt, args...) \
+ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_INFO, dev, "info", fmt , ## args)
+#define CX18_DEBUG_HI_API_DEV(dev, fmt, args...) \
+ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_API, dev, "api", fmt , ## args)
+#define CX18_DEBUG_HI_DMA_DEV(dev, fmt, args...) \
+ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_DMA, dev, "dma", fmt , ## args)
+#define CX18_DEBUG_HI_IOCTL_DEV(dev, fmt, args...) \
+ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_IOCTL, dev, "ioctl", fmt , ## args)
+#define CX18_DEBUG_HI_FILE_DEV(dev, fmt, args...) \
+ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_FILE, dev, "file", fmt , ## args)
+#define CX18_DEBUG_HI_I2C_DEV(dev, fmt, args...) \
+ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_I2C, dev, "i2c", fmt , ## args)
+#define CX18_DEBUG_HI_IRQ_DEV(dev, fmt, args...) \
+ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_IRQ, dev, "irq", fmt , ## args)
+
+#define CX18_ERR_DEV(dev, fmt, args...) v4l2_err(dev, fmt , ## args)
+#define CX18_WARN_DEV(dev, fmt, args...) v4l2_warn(dev, fmt , ## args)
+#define CX18_INFO_DEV(dev, fmt, args...) v4l2_info(dev, fmt , ## args)
+
+extern int cx18_debug;
+
+struct cx18_options {
+ int megabytes[CX18_MAX_STREAMS]; /* Size in megabytes of each stream */
+ int cardtype; /* force card type on load */
+ int tuner; /* set tuner on load */
+ int radio; /* enable/disable radio */
+};
+
+/* per-mdl bit flags */
+#define CX18_F_M_NEED_SWAP 0 /* mdl buffer data must be endianess swapped */
+
+/* per-stream, s_flags */
+#define CX18_F_S_CLAIMED 3 /* this stream is claimed */
+#define CX18_F_S_STREAMING 4 /* the fw is decoding/encoding this stream */
+#define CX18_F_S_INTERNAL_USE 5 /* this stream is used internally (sliced VBI processing) */
+#define CX18_F_S_STREAMOFF 7 /* signal end of stream EOS */
+#define CX18_F_S_APPL_IO 8 /* this stream is used read/written by an application */
+#define CX18_F_S_STOPPING 9 /* telling the fw to stop capturing */
+
+/* per-cx18, i_flags */
+#define CX18_F_I_LOADED_FW 0 /* Loaded firmware 1st time */
+#define CX18_F_I_EOS 4 /* End of encoder stream */
+#define CX18_F_I_RADIO_USER 5 /* radio tuner is selected */
+#define CX18_F_I_ENC_PAUSED 13 /* the encoder is paused */
+#define CX18_F_I_INITED 21 /* set after first open */
+#define CX18_F_I_FAILED 22 /* set if first open failed */
+
+/* These are the VBI types as they appear in the embedded VBI private packets. */
+#define CX18_SLICED_TYPE_TELETEXT_B (1)
+#define CX18_SLICED_TYPE_CAPTION_525 (4)
+#define CX18_SLICED_TYPE_WSS_625 (5)
+#define CX18_SLICED_TYPE_VPS (7)
+
+/**
+ * list_entry_is_past_end - check if a previous loop cursor is off list end
+ * @pos: the type * previously used as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Check if the entry's list_head is the head of the list, thus it's not a
+ * real entry but was the loop cursor that walked past the end
+ */
+#define list_entry_is_past_end(pos, head, member) \
+ (&pos->member == (head))
+
+struct cx18_buffer {
+ struct list_head list;
+ dma_addr_t dma_handle;
+ char *buf;
+
+ u32 bytesused;
+ u32 readpos;
+};
+
+struct cx18_mdl {
+ struct list_head list;
+ u32 id; /* index into cx->scb->cpu_mdl[] of 1st cx18_mdl_ent */
+
+ unsigned int skipped;
+ unsigned long m_flags;
+
+ struct list_head buf_list;
+ struct cx18_buffer *curr_buf; /* current buffer in list for reading */
+
+ u32 bytesused;
+ u32 readpos;
+};
+
+struct cx18_queue {
+ struct list_head list;
+ atomic_t depth;
+ u32 bytesused;
+ spinlock_t lock;
+};
+
+struct cx18_stream; /* forward reference */
+
+struct cx18_dvb {
+ struct cx18_stream *stream;
+ struct dmx_frontend hw_frontend;
+ struct dmx_frontend mem_frontend;
+ struct dmxdev dmxdev;
+ struct dvb_adapter dvb_adapter;
+ struct dvb_demux demux;
+ struct dvb_frontend *fe;
+ struct dvb_net dvbnet;
+ int enabled;
+ int feeding;
+ struct mutex feedlock;
+};
+
+struct cx18; /* forward reference */
+struct cx18_scb; /* forward reference */
+
+
+#define CX18_MAX_MDL_ACKS 2
+#define CX18_MAX_IN_WORK_ORDERS (CX18_MAX_FW_MDLS_PER_STREAM + 7)
+/* CPU_DE_RELEASE_MDL can burst CX18_MAX_FW_MDLS_PER_STREAM orders in a group */
+
+#define CX18_F_EWO_MB_STALE_UPON_RECEIPT 0x1
+#define CX18_F_EWO_MB_STALE_WHILE_PROC 0x2
+#define CX18_F_EWO_MB_STALE \
+ (CX18_F_EWO_MB_STALE_UPON_RECEIPT | CX18_F_EWO_MB_STALE_WHILE_PROC)
+
+struct cx18_in_work_order {
+ struct work_struct work;
+ atomic_t pending;
+ struct cx18 *cx;
+ unsigned long flags;
+ int rpu;
+ struct cx18_mailbox mb;
+ struct cx18_mdl_ack mdl_ack[CX18_MAX_MDL_ACKS];
+ char *str;
+};
+
+#define CX18_INVALID_TASK_HANDLE 0xffffffff
+
+struct cx18_stream {
+ /* These first five fields are always set, even if the stream
+ is not actually created. */
+ struct video_device *video_dev; /* NULL when stream not created */
+ struct cx18_dvb *dvb; /* DVB / Digital Transport */
+ struct cx18 *cx; /* for ease of use */
+ const char *name; /* name of the stream */
+ int type; /* stream type */
+ u32 handle; /* task handle */
+ unsigned int mdl_base_idx;
+
+ u32 id;
+ unsigned long s_flags; /* status flags, see above */
+ int dma; /* can be PCI_DMA_TODEVICE,
+ PCI_DMA_FROMDEVICE or
+ PCI_DMA_NONE */
+ wait_queue_head_t waitq;
+
+ /* Buffers */
+ struct list_head buf_pool; /* buffers not attached to an MDL */
+ u32 buffers; /* total buffers owned by this stream */
+ u32 buf_size; /* size in bytes of a single buffer */
+
+ /* MDL sizes - all stream MDLs are the same size */
+ u32 bufs_per_mdl;
+ u32 mdl_size; /* total bytes in all buffers in a mdl */
+
+ /* MDL Queues */
+ struct cx18_queue q_free; /* free - in rotation, not committed */
+ struct cx18_queue q_busy; /* busy - in use by firmware */
+ struct cx18_queue q_full; /* full - data for user apps */
+ struct cx18_queue q_idle; /* idle - not in rotation */
+
+ struct work_struct out_work_order;
+
+ /* Videobuf for YUV video */
+ u32 pixelformat;
+ u32 vb_bytes_per_frame;
+ struct list_head vb_capture; /* video capture queue */
+ spinlock_t vb_lock;
+ struct timer_list vb_timeout;
+
+ struct videobuf_queue vbuf_q;
+ spinlock_t vbuf_q_lock; /* Protect vbuf_q */
+ enum v4l2_buf_type vb_type;
+};
+
+struct cx18_videobuf_buffer {
+ /* Common video buffer sub-system struct */
+ struct videobuf_buffer vb;
+ v4l2_std_id tvnorm; /* selected tv norm */
+ u32 bytes_used;
+};
+
+struct cx18_open_id {
+ struct v4l2_fh fh;
+ u32 open_id;
+ int type;
+ struct cx18 *cx;
+};
+
+static inline struct cx18_open_id *fh2id(struct v4l2_fh *fh)
+{
+ return container_of(fh, struct cx18_open_id, fh);
+}
+
+static inline struct cx18_open_id *file2id(struct file *file)
+{
+ return fh2id(file->private_data);
+}
+
+/* forward declaration of struct defined in cx18-cards.h */
+struct cx18_card;
+
+/*
+ * A note about "sliced" VBI data as implemented in this driver:
+ *
+ * Currently we collect the sliced VBI in the form of Ancillary Data
+ * packets, inserted by the AV core decoder/digitizer/slicer in the
+ * horizontal blanking region of the VBI lines, in "raw" mode as far as
+ * the Encoder is concerned. We don't ever tell the Encoder itself
+ * to provide sliced VBI. (AV Core: sliced mode - Encoder: raw mode)
+ *
+ * We then process the ancillary data ourselves to send the sliced data
+ * to the user application directly or build up MPEG-2 private stream 1
+ * packets to splice into (only!) MPEG-2 PS streams for the user app.
+ *
+ * (That's how ivtv essentially does it.)
+ *
+ * The Encoder should be able to extract certain sliced VBI data for
+ * us and provide it in a separate stream or splice it into any type of
+ * MPEG PS or TS stream, but this isn't implemented yet.
+ */
+
+/*
+ * Number of "raw" VBI samples per horizontal line we tell the Encoder to
+ * grab from the decoder/digitizer/slicer output for raw or sliced VBI.
+ * It depends on the pixel clock and the horiz rate:
+ *
+ * (1/Fh)*(2*Fp) = Samples/line
+ * = 4 bytes EAV + Anc data in hblank + 4 bytes SAV + active samples
+ *
+ * Sliced VBI data is sent as ancillary data during horizontal blanking
+ * Raw VBI is sent as active video samples during vertcal blanking
+ *
+ * We use a BT.656 pxiel clock of 13.5 MHz and a BT.656 active line
+ * length of 720 pixels @ 4:2:2 sampling. Thus...
+ *
+ * For systems that use a 15.734 kHz horizontal rate, such as
+ * NTSC-M, PAL-M, PAL-60, and other 60 Hz/525 line systems, we have:
+ *
+ * (1/15.734 kHz) * 2 * 13.5 MHz = 1716 samples/line =
+ * 4 bytes SAV + 268 bytes anc data + 4 bytes SAV + 1440 active samples
+ *
+ * For systems that use a 15.625 kHz horizontal rate, such as
+ * PAL-B/G/H, PAL-I, SECAM-L and other 50 Hz/625 line systems, we have:
+ *
+ * (1/15.625 kHz) * 2 * 13.5 MHz = 1728 samples/line =
+ * 4 bytes SAV + 280 bytes anc data + 4 bytes SAV + 1440 active samples
+ */
+static const u32 vbi_active_samples = 1444; /* 4 byte SAV + 720 Y + 720 U/V */
+static const u32 vbi_hblank_samples_60Hz = 272; /* 4 byte EAV + 268 anc/fill */
+static const u32 vbi_hblank_samples_50Hz = 284; /* 4 byte EAV + 280 anc/fill */
+
+#define CX18_VBI_FRAMES 32
+
+struct vbi_info {
+ /* Current state of v4l2 VBI settings for this device */
+ struct v4l2_format in;
+ struct v4l2_sliced_vbi_format *sliced_in; /* pointer to in.fmt.sliced */
+ u32 count; /* Count of VBI data lines: 60 Hz: 12 or 50 Hz: 18 */
+ u32 start[2]; /* First VBI data line per field: 10 & 273 or 6 & 318 */
+
+ u32 frame; /* Count of VBI buffers/frames received from Encoder */
+
+ /*
+ * Vars for creation and insertion of MPEG Private Stream 1 packets
+ * of sliced VBI data into an MPEG PS
+ */
+
+ /* Boolean: create and insert Private Stream 1 packets into the PS */
+ int insert_mpeg;
+
+ /*
+ * Buffer for the maximum of 2 * 18 * packet_size sliced VBI lines.
+ * Used in cx18-vbi.c only for collecting sliced data, and as a source
+ * during conversion of sliced VBI data into MPEG Priv Stream 1 packets.
+ * We don't need to save state here, but the array may have been a bit
+ * too big (2304 bytes) to alloc from the stack.
+ */
+ struct v4l2_sliced_vbi_data sliced_data[36];
+
+ /*
+ * A ring buffer of driver-generated MPEG-2 PS
+ * Program Pack/Private Stream 1 packets for sliced VBI data insertion
+ * into the MPEG PS stream.
+ *
+ * In each sliced_mpeg_data[] buffer is:
+ * 16 byte MPEG-2 PS Program Pack Header
+ * 16 byte MPEG-2 Private Stream 1 PES Header
+ * 4 byte magic number: "itv0" or "ITV0"
+ * 4 byte first field line mask, if "itv0"
+ * 4 byte second field line mask, if "itv0"
+ * 36 lines, if "ITV0"; or <36 lines, if "itv0"; of sliced VBI data
+ *
+ * Each line in the payload is
+ * 1 byte line header derived from the SDID (WSS, CC, VPS, etc.)
+ * 42 bytes of line data
+ *
+ * That's a maximum 1552 bytes of payload in the Private Stream 1 packet
+ * which is the payload size a PVR-350 (CX23415) MPEG decoder will
+ * accept for VBI data. So, including the headers, it's a maximum 1584
+ * bytes total.
+ */
+#define CX18_SLICED_MPEG_DATA_MAXSZ 1584
+ /* copy_vbi_buf() needs 8 temp bytes on the end for the worst case */
+#define CX18_SLICED_MPEG_DATA_BUFSZ (CX18_SLICED_MPEG_DATA_MAXSZ+8)
+ u8 *sliced_mpeg_data[CX18_VBI_FRAMES];
+ u32 sliced_mpeg_size[CX18_VBI_FRAMES];
+
+ /* Count of Program Pack/Program Stream 1 packets inserted into PS */
+ u32 inserted_frame;
+
+ /*
+ * A dummy driver stream transfer mdl & buffer with a copy of the next
+ * sliced_mpeg_data[] buffer for output to userland apps.
+ * Only used in cx18-fileops.c, but its state needs to persist at times.
+ */
+ struct cx18_mdl sliced_mpeg_mdl;
+ struct cx18_buffer sliced_mpeg_buf;
+};
+
+/* Per cx23418, per I2C bus private algo callback data */
+struct cx18_i2c_algo_callback_data {
+ struct cx18 *cx;
+ int bus_index; /* 0 or 1 for the cx23418's 1st or 2nd I2C bus */
+};
+
+#define CX18_MAX_MMIO_WR_RETRIES 10
+
+/* Struct to hold info about cx18 cards */
+struct cx18 {
+ int instance;
+ struct pci_dev *pci_dev;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_subdev *sd_av; /* A/V decoder/digitizer sub-device */
+ struct v4l2_subdev *sd_extmux; /* External multiplexer sub-dev */
+
+ const struct cx18_card *card; /* card information */
+ const char *card_name; /* full name of the card */
+ const struct cx18_card_tuner_i2c *card_i2c; /* i2c addresses to probe for tuner */
+ u8 is_50hz;
+ u8 is_60hz;
+ u8 nof_inputs; /* number of video inputs */
+ u8 nof_audio_inputs; /* number of audio inputs */
+ u32 v4l2_cap; /* V4L2 capabilities of card */
+ u32 hw_flags; /* Hardware description of the board */
+ unsigned int free_mdl_idx;
+ struct cx18_scb __iomem *scb; /* pointer to SCB */
+ struct mutex epu2apu_mb_lock; /* protect driver to chip mailbox in SCB*/
+ struct mutex epu2cpu_mb_lock; /* protect driver to chip mailbox in SCB*/
+
+ struct cx18_av_state av_state;
+
+ /* codec settings */
+ struct cx2341x_handler cxhdl;
+ u32 filter_mode;
+ u32 temporal_strength;
+ u32 spatial_strength;
+
+ /* dualwatch */
+ unsigned long dualwatch_jiffies;
+ u32 dualwatch_stereo_mode;
+
+ struct mutex serialize_lock; /* mutex used to serialize open/close/start/stop/ioctl operations */
+ struct cx18_options options; /* User options */
+ int stream_buffers[CX18_MAX_STREAMS]; /* # of buffers for each stream */
+ int stream_buf_size[CX18_MAX_STREAMS]; /* Stream buffer size */
+ struct cx18_stream streams[CX18_MAX_STREAMS]; /* Stream data */
+ struct snd_cx18_card *alsa; /* ALSA interface for PCM capture stream */
+ void (*pcm_announce_callback)(struct snd_cx18_card *card, u8 *pcm_data,
+ size_t num_bytes);
+
+ unsigned long i_flags; /* global cx18 flags */
+ atomic_t ana_capturing; /* count number of active analog capture streams */
+ atomic_t tot_capturing; /* total count number of active capture streams */
+ int search_pack_header;
+
+ int open_id; /* incremented each time an open occurs, used as
+ unique ID. Starts at 1, so 0 can be used as
+ uninitialized value in the stream->id. */
+
+ u32 base_addr;
+
+ u8 card_rev;
+ void __iomem *enc_mem, *reg_mem;
+
+ struct vbi_info vbi;
+
+ u64 mpg_data_received;
+ u64 vbi_data_inserted;
+
+ wait_queue_head_t mb_apu_waitq;
+ wait_queue_head_t mb_cpu_waitq;
+ wait_queue_head_t cap_w;
+ /* when the current DMA is finished this queue is woken up */
+ wait_queue_head_t dma_waitq;
+
+ u32 sw1_irq_mask;
+ u32 sw2_irq_mask;
+ u32 hw2_irq_mask;
+
+ struct workqueue_struct *in_work_queue;
+ char in_workq_name[11]; /* "cx18-NN-in" */
+ struct cx18_in_work_order in_work_order[CX18_MAX_IN_WORK_ORDERS];
+ char epu_debug_str[256]; /* CX18_EPU_DEBUG is rare: use shared space */
+
+ /* i2c */
+ struct i2c_adapter i2c_adap[2];
+ struct i2c_algo_bit_data i2c_algo[2];
+ struct cx18_i2c_algo_callback_data i2c_algo_cb_data[2];
+
+ struct IR_i2c_init_data ir_i2c_init_data;
+
+ /* gpio */
+ u32 gpio_dir;
+ u32 gpio_val;
+ struct mutex gpio_lock;
+ struct v4l2_subdev sd_gpiomux;
+ struct v4l2_subdev sd_resetctrl;
+
+ /* v4l2 and User settings */
+
+ /* codec settings */
+ u32 audio_input;
+ u32 active_input;
+ v4l2_std_id std;
+ v4l2_std_id tuner_std; /* The norm of the tuner (fixed) */
+
+ /* Used for cx18-alsa module loading */
+ struct work_struct request_module_wk;
+};
+
+static inline struct cx18 *to_cx18(struct v4l2_device *v4l2_dev)
+{
+ return container_of(v4l2_dev, struct cx18, v4l2_dev);
+}
+
+/* cx18 extensions to be loaded */
+extern int (*cx18_ext_init)(struct cx18 *);
+
+/* Globals */
+extern int cx18_first_minor;
+
+/*==============Prototypes==================*/
+
+/* Return non-zero if a signal is pending */
+int cx18_msleep_timeout(unsigned int msecs, int intr);
+
+/* Read Hauppauge eeprom */
+struct tveeprom; /* forward reference */
+void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv);
+
+/* First-open initialization: load firmware, etc. */
+int cx18_init_on_first_open(struct cx18 *cx);
+
+/* Test if the current VBI mode is raw (1) or sliced (0) */
+static inline int cx18_raw_vbi(const struct cx18 *cx)
+{
+ return cx->vbi.in.type == V4L2_BUF_TYPE_VBI_CAPTURE;
+}
+
+/* Call the specified callback for all subdevs with a grp_id bit matching the
+ * mask in hw (if 0, then match them all). Ignore any errors. */
+#define cx18_call_hw(cx, hw, o, f, args...) \
+ do { \
+ struct v4l2_subdev *__sd; \
+ __v4l2_device_call_subdevs_p(&(cx)->v4l2_dev, __sd, \
+ !(hw) || (__sd->grp_id & (hw)), o, f , ##args); \
+ } while (0)
+
+#define cx18_call_all(cx, o, f, args...) cx18_call_hw(cx, 0, o, f , ##args)
+
+/* Call the specified callback for all subdevs with a grp_id bit matching the
+ * mask in hw (if 0, then match them all). If the callback returns an error
+ * other than 0 or -ENOIOCTLCMD, then return with that error code. */
+#define cx18_call_hw_err(cx, hw, o, f, args...) \
+({ \
+ struct v4l2_subdev *__sd; \
+ __v4l2_device_call_subdevs_until_err_p(&(cx)->v4l2_dev, \
+ __sd, !(hw) || (__sd->grp_id & (hw)), o, f, \
+ ##args); \
+})
+
+#define cx18_call_all_err(cx, o, f, args...) \
+ cx18_call_hw_err(cx, 0, o, f , ##args)
+
+#endif /* CX18_DRIVER_H */
diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c
new file mode 100644
index 00000000..f41922bd
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-dvb.c
@@ -0,0 +1,605 @@
+/*
+ * cx18 functions for DVB support
+ *
+ * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "cx18-version.h"
+#include "cx18-dvb.h"
+#include "cx18-io.h"
+#include "cx18-queue.h"
+#include "cx18-streams.h"
+#include "cx18-cards.h"
+#include "cx18-gpio.h"
+#include "s5h1409.h"
+#include "mxl5005s.h"
+#include "s5h1411.h"
+#include "tda18271.h"
+#include "zl10353.h"
+
+#include <linux/firmware.h>
+#include "mt352.h"
+#include "mt352_priv.h"
+#include "tuner-xc2028.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define CX18_REG_DMUX_NUM_PORT_0_CONTROL 0xd5a000
+#define CX18_CLOCK_ENABLE2 0xc71024
+#define CX18_DMUX_CLK_MASK 0x0080
+
+/*
+ * CX18_CARD_HVR_1600_ESMT
+ * CX18_CARD_HVR_1600_SAMSUNG
+ */
+
+static struct mxl5005s_config hauppauge_hvr1600_tuner = {
+ .i2c_address = 0xC6 >> 1,
+ .if_freq = IF_FREQ_5380000HZ,
+ .xtal_freq = CRYSTAL_FREQ_16000000HZ,
+ .agc_mode = MXL_SINGLE_AGC,
+ .tracking_filter = MXL_TF_C_H,
+ .rssi_enable = MXL_RSSI_ENABLE,
+ .cap_select = MXL_CAP_SEL_ENABLE,
+ .div_out = MXL_DIV_OUT_4,
+ .clock_out = MXL_CLOCK_OUT_DISABLE,
+ .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
+ .top = MXL5005S_TOP_25P2,
+ .mod_mode = MXL_DIGITAL_MODE,
+ .if_mode = MXL_ZERO_IF,
+ .qam_gain = 0x02,
+ .AgcMasterByte = 0x00,
+};
+
+static struct s5h1409_config hauppauge_hvr1600_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_SERIAL_OUTPUT,
+ .gpio = S5H1409_GPIO_ON,
+ .qam_if = 44000,
+ .inversion = S5H1409_INVERSION_OFF,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+ .hvr1600_opt = S5H1409_HVR1600_OPTIMIZE
+};
+
+/*
+ * CX18_CARD_HVR_1600_S5H1411
+ */
+static struct s5h1411_config hcw_s5h1411_config = {
+ .output_mode = S5H1411_SERIAL_OUTPUT,
+ .gpio = S5H1411_GPIO_OFF,
+ .vsb_if = S5H1411_IF_44000,
+ .qam_if = S5H1411_IF_4000,
+ .inversion = S5H1411_INVERSION_ON,
+ .status_mode = S5H1411_DEMODLOCKING,
+ .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct tda18271_std_map hauppauge_tda18271_std_map = {
+ .atsc_6 = { .if_freq = 5380, .agc_mode = 3, .std = 3,
+ .if_lvl = 6, .rfagc_top = 0x37 },
+ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0,
+ .if_lvl = 6, .rfagc_top = 0x37 },
+};
+
+static struct tda18271_config hauppauge_tda18271_config = {
+ .std_map = &hauppauge_tda18271_std_map,
+ .gate = TDA18271_GATE_DIGITAL,
+ .output_opt = TDA18271_OUTPUT_LT_OFF,
+};
+
+/*
+ * CX18_CARD_LEADTEK_DVR3100H
+ */
+/* Information/confirmation of proper config values provided by Terry Wu */
+static struct zl10353_config leadtek_dvr3100h_demod = {
+ .demod_address = 0x1e >> 1, /* Datasheet suggested straps */
+ .if2 = 45600, /* 4.560 MHz IF from the XC3028 */
+ .parallel_ts = 1, /* Not a serial TS */
+ .no_tuner = 1, /* XC3028 is not behind the gate */
+ .disable_i2c_gate_ctrl = 1, /* Disable the I2C gate */
+};
+
+/*
+ * CX18_CARD_YUAN_MPC718
+ */
+/*
+ * Due to
+ *
+ * 1. an absence of information on how to prgram the MT352
+ * 2. the Linux mt352 module pushing MT352 initialzation off onto us here
+ *
+ * We have to use an init sequence that *you* must extract from the Windows
+ * driver (yuanrap.sys) and which we load as a firmware.
+ *
+ * If someone can provide me with a Zarlink MT352 (Intel CE6352?) Design Manual
+ * with chip programming details, then I can remove this annoyance.
+ */
+static int yuan_mpc718_mt352_reqfw(struct cx18_stream *stream,
+ const struct firmware **fw)
+{
+ struct cx18 *cx = stream->cx;
+ const char *fn = "dvb-cx18-mpc718-mt352.fw";
+ int ret;
+
+ ret = request_firmware(fw, fn, &cx->pci_dev->dev);
+ if (ret)
+ CX18_ERR("Unable to open firmware file %s\n", fn);
+ else {
+ size_t sz = (*fw)->size;
+ if (sz < 2 || sz > 64 || (sz % 2) != 0) {
+ CX18_ERR("Firmware %s has a bad size: %lu bytes\n",
+ fn, (unsigned long) sz);
+ ret = -EILSEQ;
+ release_firmware(*fw);
+ *fw = NULL;
+ }
+ }
+
+ if (ret) {
+ CX18_ERR("The MPC718 board variant with the MT352 DVB-T"
+ "demodualtor will not work without it\n");
+ CX18_ERR("Run 'linux/Documentation/dvb/get_dvb_firmware "
+ "mpc718' if you need the firmware\n");
+ }
+ return ret;
+}
+
+static int yuan_mpc718_mt352_init(struct dvb_frontend *fe)
+{
+ struct cx18_dvb *dvb = container_of(fe->dvb,
+ struct cx18_dvb, dvb_adapter);
+ struct cx18_stream *stream = dvb->stream;
+ const struct firmware *fw = NULL;
+ int ret;
+ int i;
+ u8 buf[3];
+
+ ret = yuan_mpc718_mt352_reqfw(stream, &fw);
+ if (ret)
+ return ret;
+
+ /* Loop through all the register-value pairs in the firmware file */
+ for (i = 0; i < fw->size; i += 2) {
+ buf[0] = fw->data[i];
+ /* Intercept a few registers we want to set ourselves */
+ switch (buf[0]) {
+ case TRL_NOMINAL_RATE_0:
+ /* Set our custom OFDM bandwidth in the case below */
+ break;
+ case TRL_NOMINAL_RATE_1:
+ /* 6 MHz: 64/7 * 6/8 / 20.48 * 2^16 = 0x55b6.db6 */
+ /* 7 MHz: 64/7 * 7/8 / 20.48 * 2^16 = 0x6400 */
+ /* 8 MHz: 64/7 * 8/8 / 20.48 * 2^16 = 0x7249.249 */
+ buf[1] = 0x72;
+ buf[2] = 0x49;
+ mt352_write(fe, buf, 3);
+ break;
+ case INPUT_FREQ_0:
+ /* Set our custom IF in the case below */
+ break;
+ case INPUT_FREQ_1:
+ /* 4.56 MHz IF: (20.48 - 4.56)/20.48 * 2^14 = 0x31c0 */
+ buf[1] = 0x31;
+ buf[2] = 0xc0;
+ mt352_write(fe, buf, 3);
+ break;
+ default:
+ /* Pass through the register-value pair from the fw */
+ buf[1] = fw->data[i+1];
+ mt352_write(fe, buf, 2);
+ break;
+ }
+ }
+
+ buf[0] = (u8) TUNER_GO;
+ buf[1] = 0x01; /* Go */
+ mt352_write(fe, buf, 2);
+ release_firmware(fw);
+ return 0;
+}
+
+static struct mt352_config yuan_mpc718_mt352_demod = {
+ .demod_address = 0x1e >> 1,
+ .adc_clock = 20480, /* 20.480 MHz */
+ .if2 = 4560, /* 4.560 MHz */
+ .no_tuner = 1, /* XC3028 is not behind the gate */
+ .demod_init = yuan_mpc718_mt352_init,
+};
+
+static struct zl10353_config yuan_mpc718_zl10353_demod = {
+ .demod_address = 0x1e >> 1, /* Datasheet suggested straps */
+ .if2 = 45600, /* 4.560 MHz IF from the XC3028 */
+ .parallel_ts = 1, /* Not a serial TS */
+ .no_tuner = 1, /* XC3028 is not behind the gate */
+ .disable_i2c_gate_ctrl = 1, /* Disable the I2C gate */
+};
+
+static struct zl10353_config gotview_dvd3_zl10353_demod = {
+ .demod_address = 0x1e >> 1, /* Datasheet suggested straps */
+ .if2 = 45600, /* 4.560 MHz IF from the XC3028 */
+ .parallel_ts = 1, /* Not a serial TS */
+ .no_tuner = 1, /* XC3028 is not behind the gate */
+ .disable_i2c_gate_ctrl = 1, /* Disable the I2C gate */
+};
+
+static int dvb_register(struct cx18_stream *stream);
+
+/* Kernel DVB framework calls this when the feed needs to start.
+ * The CX18 framework should enable the transport DMA handling
+ * and queue processing.
+ */
+static int cx18_dvb_start_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct cx18_stream *stream = (struct cx18_stream *) demux->priv;
+ struct cx18 *cx;
+ int ret;
+ u32 v;
+
+ if (!stream)
+ return -EINVAL;
+
+ cx = stream->cx;
+ CX18_DEBUG_INFO("Start feed: pid = 0x%x index = %d\n",
+ feed->pid, feed->index);
+
+ mutex_lock(&cx->serialize_lock);
+ ret = cx18_init_on_first_open(cx);
+ mutex_unlock(&cx->serialize_lock);
+ if (ret) {
+ CX18_ERR("Failed to initialize firmware starting DVB feed\n");
+ return ret;
+ }
+ ret = -EINVAL;
+
+ switch (cx->card->type) {
+ case CX18_CARD_HVR_1600_ESMT:
+ case CX18_CARD_HVR_1600_SAMSUNG:
+ case CX18_CARD_HVR_1600_S5H1411:
+ v = cx18_read_reg(cx, CX18_REG_DMUX_NUM_PORT_0_CONTROL);
+ v |= 0x00400000; /* Serial Mode */
+ v |= 0x00002000; /* Data Length - Byte */
+ v |= 0x00010000; /* Error - Polarity */
+ v |= 0x00020000; /* Error - Passthru */
+ v |= 0x000c0000; /* Error - Ignore */
+ cx18_write_reg(cx, v, CX18_REG_DMUX_NUM_PORT_0_CONTROL);
+ break;
+
+ case CX18_CARD_LEADTEK_DVR3100H:
+ case CX18_CARD_YUAN_MPC718:
+ case CX18_CARD_GOTVIEW_PCI_DVD3:
+ default:
+ /* Assumption - Parallel transport - Signalling
+ * undefined or default.
+ */
+ break;
+ }
+
+ if (!demux->dmx.frontend)
+ return -EINVAL;
+
+ mutex_lock(&stream->dvb->feedlock);
+ if (stream->dvb->feeding++ == 0) {
+ CX18_DEBUG_INFO("Starting Transport DMA\n");
+ mutex_lock(&cx->serialize_lock);
+ set_bit(CX18_F_S_STREAMING, &stream->s_flags);
+ ret = cx18_start_v4l2_encode_stream(stream);
+ if (ret < 0) {
+ CX18_DEBUG_INFO("Failed to start Transport DMA\n");
+ stream->dvb->feeding--;
+ if (stream->dvb->feeding == 0)
+ clear_bit(CX18_F_S_STREAMING, &stream->s_flags);
+ }
+ mutex_unlock(&cx->serialize_lock);
+ } else
+ ret = 0;
+ mutex_unlock(&stream->dvb->feedlock);
+
+ return ret;
+}
+
+/* Kernel DVB framework calls this when the feed needs to stop. */
+static int cx18_dvb_stop_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct cx18_stream *stream = (struct cx18_stream *)demux->priv;
+ struct cx18 *cx;
+ int ret = -EINVAL;
+
+ if (stream) {
+ cx = stream->cx;
+ CX18_DEBUG_INFO("Stop feed: pid = 0x%x index = %d\n",
+ feed->pid, feed->index);
+
+ mutex_lock(&stream->dvb->feedlock);
+ if (--stream->dvb->feeding == 0) {
+ CX18_DEBUG_INFO("Stopping Transport DMA\n");
+ mutex_lock(&cx->serialize_lock);
+ ret = cx18_stop_v4l2_encode_stream(stream, 0);
+ mutex_unlock(&cx->serialize_lock);
+ } else
+ ret = 0;
+ mutex_unlock(&stream->dvb->feedlock);
+ }
+
+ return ret;
+}
+
+int cx18_dvb_register(struct cx18_stream *stream)
+{
+ struct cx18 *cx = stream->cx;
+ struct cx18_dvb *dvb = stream->dvb;
+ struct dvb_adapter *dvb_adapter;
+ struct dvb_demux *dvbdemux;
+ struct dmx_demux *dmx;
+ int ret;
+
+ if (!dvb)
+ return -EINVAL;
+
+ dvb->enabled = 0;
+ dvb->stream = stream;
+
+ ret = dvb_register_adapter(&dvb->dvb_adapter,
+ CX18_DRIVER_NAME,
+ THIS_MODULE, &cx->pci_dev->dev, adapter_nr);
+ if (ret < 0)
+ goto err_out;
+
+ dvb_adapter = &dvb->dvb_adapter;
+
+ dvbdemux = &dvb->demux;
+
+ dvbdemux->priv = (void *)stream;
+
+ dvbdemux->filternum = 256;
+ dvbdemux->feednum = 256;
+ dvbdemux->start_feed = cx18_dvb_start_feed;
+ dvbdemux->stop_feed = cx18_dvb_stop_feed;
+ dvbdemux->dmx.capabilities = (DMX_TS_FILTERING |
+ DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING);
+ ret = dvb_dmx_init(dvbdemux);
+ if (ret < 0)
+ goto err_dvb_unregister_adapter;
+
+ dmx = &dvbdemux->dmx;
+
+ dvb->hw_frontend.source = DMX_FRONTEND_0;
+ dvb->mem_frontend.source = DMX_MEMORY_FE;
+ dvb->dmxdev.filternum = 256;
+ dvb->dmxdev.demux = dmx;
+
+ ret = dvb_dmxdev_init(&dvb->dmxdev, dvb_adapter);
+ if (ret < 0)
+ goto err_dvb_dmx_release;
+
+ ret = dmx->add_frontend(dmx, &dvb->hw_frontend);
+ if (ret < 0)
+ goto err_dvb_dmxdev_release;
+
+ ret = dmx->add_frontend(dmx, &dvb->mem_frontend);
+ if (ret < 0)
+ goto err_remove_hw_frontend;
+
+ ret = dmx->connect_frontend(dmx, &dvb->hw_frontend);
+ if (ret < 0)
+ goto err_remove_mem_frontend;
+
+ ret = dvb_register(stream);
+ if (ret < 0)
+ goto err_disconnect_frontend;
+
+ dvb_net_init(dvb_adapter, &dvb->dvbnet, dmx);
+
+ CX18_INFO("DVB Frontend registered\n");
+ CX18_INFO("Registered DVB adapter%d for %s (%d x %d.%02d kB)\n",
+ stream->dvb->dvb_adapter.num, stream->name,
+ stream->buffers, stream->buf_size/1024,
+ (stream->buf_size * 100 / 1024) % 100);
+
+ mutex_init(&dvb->feedlock);
+ dvb->enabled = 1;
+ return ret;
+
+err_disconnect_frontend:
+ dmx->disconnect_frontend(dmx);
+err_remove_mem_frontend:
+ dmx->remove_frontend(dmx, &dvb->mem_frontend);
+err_remove_hw_frontend:
+ dmx->remove_frontend(dmx, &dvb->hw_frontend);
+err_dvb_dmxdev_release:
+ dvb_dmxdev_release(&dvb->dmxdev);
+err_dvb_dmx_release:
+ dvb_dmx_release(dvbdemux);
+err_dvb_unregister_adapter:
+ dvb_unregister_adapter(dvb_adapter);
+err_out:
+ return ret;
+}
+
+void cx18_dvb_unregister(struct cx18_stream *stream)
+{
+ struct cx18 *cx = stream->cx;
+ struct cx18_dvb *dvb = stream->dvb;
+ struct dvb_adapter *dvb_adapter;
+ struct dvb_demux *dvbdemux;
+ struct dmx_demux *dmx;
+
+ CX18_INFO("unregister DVB\n");
+
+ if (dvb == NULL || !dvb->enabled)
+ return;
+
+ dvb_adapter = &dvb->dvb_adapter;
+ dvbdemux = &dvb->demux;
+ dmx = &dvbdemux->dmx;
+
+ dmx->close(dmx);
+ dvb_net_release(&dvb->dvbnet);
+ dmx->remove_frontend(dmx, &dvb->mem_frontend);
+ dmx->remove_frontend(dmx, &dvb->hw_frontend);
+ dvb_dmxdev_release(&dvb->dmxdev);
+ dvb_dmx_release(dvbdemux);
+ dvb_unregister_frontend(dvb->fe);
+ dvb_frontend_detach(dvb->fe);
+ dvb_unregister_adapter(dvb_adapter);
+}
+
+/* All the DVB attach calls go here, this function get's modified
+ * for each new card. cx18_dvb_start_feed() will also need changes.
+ */
+static int dvb_register(struct cx18_stream *stream)
+{
+ struct cx18_dvb *dvb = stream->dvb;
+ struct cx18 *cx = stream->cx;
+ int ret = 0;
+
+ switch (cx->card->type) {
+ case CX18_CARD_HVR_1600_ESMT:
+ case CX18_CARD_HVR_1600_SAMSUNG:
+ dvb->fe = dvb_attach(s5h1409_attach,
+ &hauppauge_hvr1600_config,
+ &cx->i2c_adap[0]);
+ if (dvb->fe != NULL) {
+ dvb_attach(mxl5005s_attach, dvb->fe,
+ &cx->i2c_adap[0],
+ &hauppauge_hvr1600_tuner);
+ ret = 0;
+ }
+ break;
+ case CX18_CARD_HVR_1600_S5H1411:
+ dvb->fe = dvb_attach(s5h1411_attach,
+ &hcw_s5h1411_config,
+ &cx->i2c_adap[0]);
+ if (dvb->fe != NULL)
+ dvb_attach(tda18271_attach, dvb->fe,
+ 0x60, &cx->i2c_adap[0],
+ &hauppauge_tda18271_config);
+ break;
+ case CX18_CARD_LEADTEK_DVR3100H:
+ dvb->fe = dvb_attach(zl10353_attach,
+ &leadtek_dvr3100h_demod,
+ &cx->i2c_adap[1]);
+ if (dvb->fe != NULL) {
+ struct dvb_frontend *fe;
+ struct xc2028_config cfg = {
+ .i2c_adap = &cx->i2c_adap[1],
+ .i2c_addr = 0xc2 >> 1,
+ .ctrl = NULL,
+ };
+ static struct xc2028_ctrl ctrl = {
+ .fname = XC2028_DEFAULT_FIRMWARE,
+ .max_len = 64,
+ .demod = XC3028_FE_ZARLINK456,
+ .type = XC2028_AUTO,
+ };
+
+ fe = dvb_attach(xc2028_attach, dvb->fe, &cfg);
+ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+ fe->ops.tuner_ops.set_config(fe, &ctrl);
+ }
+ break;
+ case CX18_CARD_YUAN_MPC718:
+ /*
+ * TODO
+ * Apparently, these cards also could instead have a
+ * DiBcom demod supported by one of the db7000 drivers
+ */
+ dvb->fe = dvb_attach(mt352_attach,
+ &yuan_mpc718_mt352_demod,
+ &cx->i2c_adap[1]);
+ if (dvb->fe == NULL)
+ dvb->fe = dvb_attach(zl10353_attach,
+ &yuan_mpc718_zl10353_demod,
+ &cx->i2c_adap[1]);
+ if (dvb->fe != NULL) {
+ struct dvb_frontend *fe;
+ struct xc2028_config cfg = {
+ .i2c_adap = &cx->i2c_adap[1],
+ .i2c_addr = 0xc2 >> 1,
+ .ctrl = NULL,
+ };
+ static struct xc2028_ctrl ctrl = {
+ .fname = XC2028_DEFAULT_FIRMWARE,
+ .max_len = 64,
+ .demod = XC3028_FE_ZARLINK456,
+ .type = XC2028_AUTO,
+ };
+
+ fe = dvb_attach(xc2028_attach, dvb->fe, &cfg);
+ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+ fe->ops.tuner_ops.set_config(fe, &ctrl);
+ }
+ break;
+ case CX18_CARD_GOTVIEW_PCI_DVD3:
+ dvb->fe = dvb_attach(zl10353_attach,
+ &gotview_dvd3_zl10353_demod,
+ &cx->i2c_adap[1]);
+ if (dvb->fe != NULL) {
+ struct dvb_frontend *fe;
+ struct xc2028_config cfg = {
+ .i2c_adap = &cx->i2c_adap[1],
+ .i2c_addr = 0xc2 >> 1,
+ .ctrl = NULL,
+ };
+ static struct xc2028_ctrl ctrl = {
+ .fname = XC2028_DEFAULT_FIRMWARE,
+ .max_len = 64,
+ .demod = XC3028_FE_ZARLINK456,
+ .type = XC2028_AUTO,
+ };
+
+ fe = dvb_attach(xc2028_attach, dvb->fe, &cfg);
+ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+ fe->ops.tuner_ops.set_config(fe, &ctrl);
+ }
+ break;
+ default:
+ /* No Digital Tv Support */
+ break;
+ }
+
+ if (dvb->fe == NULL) {
+ CX18_ERR("frontend initialization failed\n");
+ return -1;
+ }
+
+ dvb->fe->callback = cx18_reset_tuner_gpio;
+
+ ret = dvb_register_frontend(&dvb->dvb_adapter, dvb->fe);
+ if (ret < 0) {
+ if (dvb->fe->ops.release)
+ dvb->fe->ops.release(dvb->fe);
+ return ret;
+ }
+
+ /*
+ * The firmware seems to enable the TS DMUX clock
+ * under various circumstances. However, since we know we
+ * might use it, let's just turn it on ourselves here.
+ */
+ cx18_write_reg_expect(cx,
+ (CX18_DMUX_CLK_MASK << 16) | CX18_DMUX_CLK_MASK,
+ CX18_CLOCK_ENABLE2,
+ CX18_DMUX_CLK_MASK,
+ (CX18_DMUX_CLK_MASK << 16) | CX18_DMUX_CLK_MASK);
+
+ return ret;
+}
diff --git a/drivers/media/video/cx18/cx18-dvb.h b/drivers/media/video/cx18/cx18-dvb.h
new file mode 100644
index 00000000..bf8d8f6f
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-dvb.h
@@ -0,0 +1,25 @@
+/*
+ * cx18 functions for DVB support
+ *
+ * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "cx18-driver.h"
+
+int cx18_dvb_register(struct cx18_stream *stream);
+void cx18_dvb_unregister(struct cx18_stream *stream);
diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c
new file mode 100644
index 00000000..4bfd865a
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-fileops.c
@@ -0,0 +1,881 @@
+/*
+ * cx18 file operation functions
+ *
+ * Derived from ivtv-fileops.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include "cx18-driver.h"
+#include "cx18-fileops.h"
+#include "cx18-i2c.h"
+#include "cx18-queue.h"
+#include "cx18-vbi.h"
+#include "cx18-audio.h"
+#include "cx18-mailbox.h"
+#include "cx18-scb.h"
+#include "cx18-streams.h"
+#include "cx18-controls.h"
+#include "cx18-ioctl.h"
+#include "cx18-cards.h"
+
+/* This function tries to claim the stream for a specific file descriptor.
+ If no one else is using this stream then the stream is claimed and
+ associated VBI and IDX streams are also automatically claimed.
+ Possible error returns: -EBUSY if someone else has claimed
+ the stream or 0 on success. */
+int cx18_claim_stream(struct cx18_open_id *id, int type)
+{
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[type];
+ struct cx18_stream *s_assoc;
+
+ /* Nothing should ever try to directly claim the IDX stream */
+ if (type == CX18_ENC_STREAM_TYPE_IDX) {
+ CX18_WARN("MPEG Index stream cannot be claimed "
+ "directly, but something tried.\n");
+ return -EINVAL;
+ }
+
+ if (test_and_set_bit(CX18_F_S_CLAIMED, &s->s_flags)) {
+ /* someone already claimed this stream */
+ if (s->id == id->open_id) {
+ /* yes, this file descriptor did. So that's OK. */
+ return 0;
+ }
+ if (s->id == -1 && type == CX18_ENC_STREAM_TYPE_VBI) {
+ /* VBI is handled already internally, now also assign
+ the file descriptor to this stream for external
+ reading of the stream. */
+ s->id = id->open_id;
+ CX18_DEBUG_INFO("Start Read VBI\n");
+ return 0;
+ }
+ /* someone else is using this stream already */
+ CX18_DEBUG_INFO("Stream %d is busy\n", type);
+ return -EBUSY;
+ }
+ s->id = id->open_id;
+
+ /*
+ * CX18_ENC_STREAM_TYPE_MPG needs to claim:
+ * CX18_ENC_STREAM_TYPE_VBI, if VBI insertion is on for sliced VBI, or
+ * CX18_ENC_STREAM_TYPE_IDX, if VBI insertion is off for sliced VBI
+ * (We don't yet fix up MPEG Index entries for our inserted packets).
+ *
+ * For all other streams we're done.
+ */
+ if (type != CX18_ENC_STREAM_TYPE_MPG)
+ return 0;
+
+ s_assoc = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
+ if (cx->vbi.insert_mpeg && !cx18_raw_vbi(cx))
+ s_assoc = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
+ else if (!cx18_stream_enabled(s_assoc))
+ return 0;
+
+ set_bit(CX18_F_S_CLAIMED, &s_assoc->s_flags);
+
+ /* mark that it is used internally */
+ set_bit(CX18_F_S_INTERNAL_USE, &s_assoc->s_flags);
+ return 0;
+}
+EXPORT_SYMBOL(cx18_claim_stream);
+
+/* This function releases a previously claimed stream. It will take into
+ account associated VBI streams. */
+void cx18_release_stream(struct cx18_stream *s)
+{
+ struct cx18 *cx = s->cx;
+ struct cx18_stream *s_assoc;
+
+ s->id = -1;
+ if (s->type == CX18_ENC_STREAM_TYPE_IDX) {
+ /*
+ * The IDX stream is only used internally, and can
+ * only be indirectly unclaimed by unclaiming the MPG stream.
+ */
+ return;
+ }
+
+ if (s->type == CX18_ENC_STREAM_TYPE_VBI &&
+ test_bit(CX18_F_S_INTERNAL_USE, &s->s_flags)) {
+ /* this stream is still in use internally */
+ return;
+ }
+ if (!test_and_clear_bit(CX18_F_S_CLAIMED, &s->s_flags)) {
+ CX18_DEBUG_WARN("Release stream %s not in use!\n", s->name);
+ return;
+ }
+
+ cx18_flush_queues(s);
+
+ /*
+ * CX18_ENC_STREAM_TYPE_MPG needs to release the
+ * CX18_ENC_STREAM_TYPE_VBI and/or CX18_ENC_STREAM_TYPE_IDX streams.
+ *
+ * For all other streams we're done.
+ */
+ if (s->type != CX18_ENC_STREAM_TYPE_MPG)
+ return;
+
+ /* Unclaim the associated MPEG Index stream */
+ s_assoc = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
+ if (test_and_clear_bit(CX18_F_S_INTERNAL_USE, &s_assoc->s_flags)) {
+ clear_bit(CX18_F_S_CLAIMED, &s_assoc->s_flags);
+ cx18_flush_queues(s_assoc);
+ }
+
+ /* Unclaim the associated VBI stream */
+ s_assoc = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
+ if (test_and_clear_bit(CX18_F_S_INTERNAL_USE, &s_assoc->s_flags)) {
+ if (s_assoc->id == -1) {
+ /*
+ * The VBI stream is not still claimed by a file
+ * descriptor, so completely unclaim it.
+ */
+ clear_bit(CX18_F_S_CLAIMED, &s_assoc->s_flags);
+ cx18_flush_queues(s_assoc);
+ }
+ }
+}
+EXPORT_SYMBOL(cx18_release_stream);
+
+static void cx18_dualwatch(struct cx18 *cx)
+{
+ struct v4l2_tuner vt;
+ u32 new_stereo_mode;
+ const u32 dual = 0x0200;
+
+ new_stereo_mode = v4l2_ctrl_g_ctrl(cx->cxhdl.audio_mode);
+ memset(&vt, 0, sizeof(vt));
+ cx18_call_all(cx, tuner, g_tuner, &vt);
+ if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 &&
+ (vt.rxsubchans & V4L2_TUNER_SUB_LANG2))
+ new_stereo_mode = dual;
+
+ if (new_stereo_mode == cx->dualwatch_stereo_mode)
+ return;
+
+ CX18_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x.\n",
+ cx->dualwatch_stereo_mode, new_stereo_mode);
+ if (v4l2_ctrl_s_ctrl(cx->cxhdl.audio_mode, new_stereo_mode))
+ CX18_DEBUG_INFO("dualwatch: changing stereo flag failed\n");
+}
+
+
+static struct cx18_mdl *cx18_get_mdl(struct cx18_stream *s, int non_block,
+ int *err)
+{
+ struct cx18 *cx = s->cx;
+ struct cx18_stream *s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
+ struct cx18_mdl *mdl;
+ DEFINE_WAIT(wait);
+
+ *err = 0;
+ while (1) {
+ if (s->type == CX18_ENC_STREAM_TYPE_MPG) {
+ /* Process pending program updates and VBI data */
+ if (time_after(jiffies, cx->dualwatch_jiffies + msecs_to_jiffies(1000))) {
+ cx->dualwatch_jiffies = jiffies;
+ cx18_dualwatch(cx);
+ }
+ if (test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
+ !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) {
+ while ((mdl = cx18_dequeue(s_vbi,
+ &s_vbi->q_full))) {
+ /* byteswap and process VBI data */
+ cx18_process_vbi_data(cx, mdl,
+ s_vbi->type);
+ cx18_stream_put_mdl_fw(s_vbi, mdl);
+ }
+ }
+ mdl = &cx->vbi.sliced_mpeg_mdl;
+ if (mdl->readpos != mdl->bytesused)
+ return mdl;
+ }
+
+ /* do we have new data? */
+ mdl = cx18_dequeue(s, &s->q_full);
+ if (mdl) {
+ if (!test_and_clear_bit(CX18_F_M_NEED_SWAP,
+ &mdl->m_flags))
+ return mdl;
+ if (s->type == CX18_ENC_STREAM_TYPE_MPG)
+ /* byteswap MPG data */
+ cx18_mdl_swap(mdl);
+ else {
+ /* byteswap and process VBI data */
+ cx18_process_vbi_data(cx, mdl, s->type);
+ }
+ return mdl;
+ }
+
+ /* return if end of stream */
+ if (!test_bit(CX18_F_S_STREAMING, &s->s_flags)) {
+ CX18_DEBUG_INFO("EOS %s\n", s->name);
+ return NULL;
+ }
+
+ /* return if file was opened with O_NONBLOCK */
+ if (non_block) {
+ *err = -EAGAIN;
+ return NULL;
+ }
+
+ /* wait for more data to arrive */
+ prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE);
+ /* New buffers might have become available before we were added
+ to the waitqueue */
+ if (!atomic_read(&s->q_full.depth))
+ schedule();
+ finish_wait(&s->waitq, &wait);
+ if (signal_pending(current)) {
+ /* return if a signal was received */
+ CX18_DEBUG_INFO("User stopped %s\n", s->name);
+ *err = -EINTR;
+ return NULL;
+ }
+ }
+}
+
+static void cx18_setup_sliced_vbi_mdl(struct cx18 *cx)
+{
+ struct cx18_mdl *mdl = &cx->vbi.sliced_mpeg_mdl;
+ struct cx18_buffer *buf = &cx->vbi.sliced_mpeg_buf;
+ int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES;
+
+ buf->buf = cx->vbi.sliced_mpeg_data[idx];
+ buf->bytesused = cx->vbi.sliced_mpeg_size[idx];
+ buf->readpos = 0;
+
+ mdl->curr_buf = NULL;
+ mdl->bytesused = cx->vbi.sliced_mpeg_size[idx];
+ mdl->readpos = 0;
+}
+
+static size_t cx18_copy_buf_to_user(struct cx18_stream *s,
+ struct cx18_buffer *buf, char __user *ubuf, size_t ucount, bool *stop)
+{
+ struct cx18 *cx = s->cx;
+ size_t len = buf->bytesused - buf->readpos;
+
+ *stop = false;
+ if (len > ucount)
+ len = ucount;
+ if (cx->vbi.insert_mpeg && s->type == CX18_ENC_STREAM_TYPE_MPG &&
+ !cx18_raw_vbi(cx) && buf != &cx->vbi.sliced_mpeg_buf) {
+ /*
+ * Try to find a good splice point in the PS, just before
+ * an MPEG-2 Program Pack start code, and provide only
+ * up to that point to the user, so it's easy to insert VBI data
+ * the next time around.
+ *
+ * This will not work for an MPEG-2 TS and has only been
+ * verified by analysis to work for an MPEG-2 PS. Helen Buus
+ * pointed out this works for the CX23416 MPEG-2 DVD compatible
+ * stream, and research indicates both the MPEG 2 SVCD and DVD
+ * stream types use an MPEG-2 PS container.
+ */
+ /*
+ * An MPEG-2 Program Stream (PS) is a series of
+ * MPEG-2 Program Packs terminated by an
+ * MPEG Program End Code after the last Program Pack.
+ * A Program Pack may hold a PS System Header packet and any
+ * number of Program Elementary Stream (PES) Packets
+ */
+ const char *start = buf->buf + buf->readpos;
+ const char *p = start + 1;
+ const u8 *q;
+ u8 ch = cx->search_pack_header ? 0xba : 0xe0;
+ int stuffing, i;
+
+ while (start + len > p) {
+ /* Scan for a 0 to find a potential MPEG-2 start code */
+ q = memchr(p, 0, start + len - p);
+ if (q == NULL)
+ break;
+ p = q + 1;
+ /*
+ * Keep looking if not a
+ * MPEG-2 Pack header start code: 0x00 0x00 0x01 0xba
+ * or MPEG-2 video PES start code: 0x00 0x00 0x01 0xe0
+ */
+ if ((char *)q + 15 >= buf->buf + buf->bytesused ||
+ q[1] != 0 || q[2] != 1 || q[3] != ch)
+ continue;
+
+ /* If expecting the primary video PES */
+ if (!cx->search_pack_header) {
+ /* Continue if it couldn't be a PES packet */
+ if ((q[6] & 0xc0) != 0x80)
+ continue;
+ /* Check if a PTS or PTS & DTS follow */
+ if (((q[7] & 0xc0) == 0x80 && /* PTS only */
+ (q[9] & 0xf0) == 0x20) || /* PTS only */
+ ((q[7] & 0xc0) == 0xc0 && /* PTS & DTS */
+ (q[9] & 0xf0) == 0x30)) { /* DTS follows */
+ /* Assume we found the video PES hdr */
+ ch = 0xba; /* next want a Program Pack*/
+ cx->search_pack_header = 1;
+ p = q + 9; /* Skip this video PES hdr */
+ }
+ continue;
+ }
+
+ /* We may have found a Program Pack start code */
+
+ /* Get the count of stuffing bytes & verify them */
+ stuffing = q[13] & 7;
+ /* all stuffing bytes must be 0xff */
+ for (i = 0; i < stuffing; i++)
+ if (q[14 + i] != 0xff)
+ break;
+ if (i == stuffing && /* right number of stuffing bytes*/
+ (q[4] & 0xc4) == 0x44 && /* marker check */
+ (q[12] & 3) == 3 && /* marker check */
+ q[14 + stuffing] == 0 && /* PES Pack or Sys Hdr */
+ q[15 + stuffing] == 0 &&
+ q[16 + stuffing] == 1) {
+ /* We declare we actually found a Program Pack*/
+ cx->search_pack_header = 0; /* expect vid PES */
+ len = (char *)q - start;
+ cx18_setup_sliced_vbi_mdl(cx);
+ *stop = true;
+ break;
+ }
+ }
+ }
+ if (copy_to_user(ubuf, (u8 *)buf->buf + buf->readpos, len)) {
+ CX18_DEBUG_WARN("copy %zd bytes to user failed for %s\n",
+ len, s->name);
+ return -EFAULT;
+ }
+ buf->readpos += len;
+ if (s->type == CX18_ENC_STREAM_TYPE_MPG &&
+ buf != &cx->vbi.sliced_mpeg_buf)
+ cx->mpg_data_received += len;
+ return len;
+}
+
+static size_t cx18_copy_mdl_to_user(struct cx18_stream *s,
+ struct cx18_mdl *mdl, char __user *ubuf, size_t ucount)
+{
+ size_t tot_written = 0;
+ int rc;
+ bool stop = false;
+
+ if (mdl->curr_buf == NULL)
+ mdl->curr_buf = list_first_entry(&mdl->buf_list,
+ struct cx18_buffer, list);
+
+ if (list_entry_is_past_end(mdl->curr_buf, &mdl->buf_list, list)) {
+ /*
+ * For some reason we've exhausted the buffers, but the MDL
+ * object still said some data was unread.
+ * Fix that and bail out.
+ */
+ mdl->readpos = mdl->bytesused;
+ return 0;
+ }
+
+ list_for_each_entry_from(mdl->curr_buf, &mdl->buf_list, list) {
+
+ if (mdl->curr_buf->readpos >= mdl->curr_buf->bytesused)
+ continue;
+
+ rc = cx18_copy_buf_to_user(s, mdl->curr_buf, ubuf + tot_written,
+ ucount - tot_written, &stop);
+ if (rc < 0)
+ return rc;
+ mdl->readpos += rc;
+ tot_written += rc;
+
+ if (stop || /* Forced stopping point for VBI insertion */
+ tot_written >= ucount || /* Reader request statisfied */
+ mdl->curr_buf->readpos < mdl->curr_buf->bytesused ||
+ mdl->readpos >= mdl->bytesused) /* MDL buffers drained */
+ break;
+ }
+ return tot_written;
+}
+
+static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf,
+ size_t tot_count, int non_block)
+{
+ struct cx18 *cx = s->cx;
+ size_t tot_written = 0;
+ int single_frame = 0;
+
+ if (atomic_read(&cx->ana_capturing) == 0 && s->id == -1) {
+ /* shouldn't happen */
+ CX18_DEBUG_WARN("Stream %s not initialized before read\n",
+ s->name);
+ return -EIO;
+ }
+
+ /* Each VBI buffer is one frame, the v4l2 API says that for VBI the
+ frames should arrive one-by-one, so make sure we never output more
+ than one VBI frame at a time */
+ if (s->type == CX18_ENC_STREAM_TYPE_VBI && !cx18_raw_vbi(cx))
+ single_frame = 1;
+
+ for (;;) {
+ struct cx18_mdl *mdl;
+ int rc;
+
+ mdl = cx18_get_mdl(s, non_block, &rc);
+ /* if there is no data available... */
+ if (mdl == NULL) {
+ /* if we got data, then return that regardless */
+ if (tot_written)
+ break;
+ /* EOS condition */
+ if (rc == 0) {
+ clear_bit(CX18_F_S_STREAMOFF, &s->s_flags);
+ clear_bit(CX18_F_S_APPL_IO, &s->s_flags);
+ cx18_release_stream(s);
+ }
+ /* set errno */
+ return rc;
+ }
+
+ rc = cx18_copy_mdl_to_user(s, mdl, ubuf + tot_written,
+ tot_count - tot_written);
+
+ if (mdl != &cx->vbi.sliced_mpeg_mdl) {
+ if (mdl->readpos == mdl->bytesused)
+ cx18_stream_put_mdl_fw(s, mdl);
+ else
+ cx18_push(s, mdl, &s->q_full);
+ } else if (mdl->readpos == mdl->bytesused) {
+ int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES;
+
+ cx->vbi.sliced_mpeg_size[idx] = 0;
+ cx->vbi.inserted_frame++;
+ cx->vbi_data_inserted += mdl->bytesused;
+ }
+ if (rc < 0)
+ return rc;
+ tot_written += rc;
+
+ if (tot_written == tot_count || single_frame)
+ break;
+ }
+ return tot_written;
+}
+
+static ssize_t cx18_read_pos(struct cx18_stream *s, char __user *ubuf,
+ size_t count, loff_t *pos, int non_block)
+{
+ ssize_t rc = count ? cx18_read(s, ubuf, count, non_block) : 0;
+ struct cx18 *cx = s->cx;
+
+ CX18_DEBUG_HI_FILE("read %zd from %s, got %zd\n", count, s->name, rc);
+ if (rc > 0)
+ pos += rc;
+ return rc;
+}
+
+int cx18_start_capture(struct cx18_open_id *id)
+{
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[id->type];
+ struct cx18_stream *s_vbi;
+ struct cx18_stream *s_idx;
+
+ if (s->type == CX18_ENC_STREAM_TYPE_RAD) {
+ /* you cannot read from these stream types. */
+ return -EPERM;
+ }
+
+ /* Try to claim this stream. */
+ if (cx18_claim_stream(id, s->type))
+ return -EBUSY;
+
+ /* If capture is already in progress, then we also have to
+ do nothing extra. */
+ if (test_bit(CX18_F_S_STREAMOFF, &s->s_flags) ||
+ test_and_set_bit(CX18_F_S_STREAMING, &s->s_flags)) {
+ set_bit(CX18_F_S_APPL_IO, &s->s_flags);
+ return 0;
+ }
+
+ /* Start associated VBI or IDX stream capture if required */
+ s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
+ s_idx = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
+ if (s->type == CX18_ENC_STREAM_TYPE_MPG) {
+ /*
+ * The VBI and IDX streams should have been claimed
+ * automatically, if for internal use, when the MPG stream was
+ * claimed. We only need to start these streams capturing.
+ */
+ if (test_bit(CX18_F_S_INTERNAL_USE, &s_idx->s_flags) &&
+ !test_and_set_bit(CX18_F_S_STREAMING, &s_idx->s_flags)) {
+ if (cx18_start_v4l2_encode_stream(s_idx)) {
+ CX18_DEBUG_WARN("IDX capture start failed\n");
+ clear_bit(CX18_F_S_STREAMING, &s_idx->s_flags);
+ goto start_failed;
+ }
+ CX18_DEBUG_INFO("IDX capture started\n");
+ }
+ if (test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
+ !test_and_set_bit(CX18_F_S_STREAMING, &s_vbi->s_flags)) {
+ if (cx18_start_v4l2_encode_stream(s_vbi)) {
+ CX18_DEBUG_WARN("VBI capture start failed\n");
+ clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags);
+ goto start_failed;
+ }
+ CX18_DEBUG_INFO("VBI insertion started\n");
+ }
+ }
+
+ /* Tell the card to start capturing */
+ if (!cx18_start_v4l2_encode_stream(s)) {
+ /* We're done */
+ set_bit(CX18_F_S_APPL_IO, &s->s_flags);
+ /* Resume a possibly paused encoder */
+ if (test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
+ cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, s->handle);
+ return 0;
+ }
+
+start_failed:
+ CX18_DEBUG_WARN("Failed to start capturing for stream %s\n", s->name);
+
+ /*
+ * The associated VBI and IDX streams for internal use are released
+ * automatically when the MPG stream is released. We only need to stop
+ * the associated stream.
+ */
+ if (s->type == CX18_ENC_STREAM_TYPE_MPG) {
+ /* Stop the IDX stream which is always for internal use */
+ if (test_bit(CX18_F_S_STREAMING, &s_idx->s_flags)) {
+ cx18_stop_v4l2_encode_stream(s_idx, 0);
+ clear_bit(CX18_F_S_STREAMING, &s_idx->s_flags);
+ }
+ /* Stop the VBI stream, if only running for internal use */
+ if (test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags) &&
+ !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) {
+ cx18_stop_v4l2_encode_stream(s_vbi, 0);
+ clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags);
+ }
+ }
+ clear_bit(CX18_F_S_STREAMING, &s->s_flags);
+ cx18_release_stream(s); /* Also releases associated streams */
+ return -EIO;
+}
+
+ssize_t cx18_v4l2_read(struct file *filp, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct cx18_open_id *id = file2id(filp);
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[id->type];
+ int rc;
+
+ CX18_DEBUG_HI_FILE("read %zd bytes from %s\n", count, s->name);
+
+ mutex_lock(&cx->serialize_lock);
+ rc = cx18_start_capture(id);
+ mutex_unlock(&cx->serialize_lock);
+ if (rc)
+ return rc;
+
+ if ((s->vb_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (id->type == CX18_ENC_STREAM_TYPE_YUV)) {
+ return videobuf_read_stream(&s->vbuf_q, buf, count, pos, 0,
+ filp->f_flags & O_NONBLOCK);
+ }
+
+ return cx18_read_pos(s, buf, count, pos, filp->f_flags & O_NONBLOCK);
+}
+
+unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait)
+{
+ struct cx18_open_id *id = file2id(filp);
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[id->type];
+ int eof = test_bit(CX18_F_S_STREAMOFF, &s->s_flags);
+
+ /* Start a capture if there is none */
+ if (!eof && !test_bit(CX18_F_S_STREAMING, &s->s_flags)) {
+ int rc;
+
+ mutex_lock(&cx->serialize_lock);
+ rc = cx18_start_capture(id);
+ mutex_unlock(&cx->serialize_lock);
+ if (rc) {
+ CX18_DEBUG_INFO("Could not start capture for %s (%d)\n",
+ s->name, rc);
+ return POLLERR;
+ }
+ CX18_DEBUG_FILE("Encoder poll started capture\n");
+ }
+
+ if ((s->vb_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (id->type == CX18_ENC_STREAM_TYPE_YUV)) {
+ int videobuf_poll = videobuf_poll_stream(filp, &s->vbuf_q, wait);
+ if (eof && videobuf_poll == POLLERR)
+ return POLLHUP;
+ else
+ return videobuf_poll;
+ }
+
+ /* add stream's waitq to the poll list */
+ CX18_DEBUG_HI_FILE("Encoder poll\n");
+ poll_wait(filp, &s->waitq, wait);
+
+ if (atomic_read(&s->q_full.depth))
+ return POLLIN | POLLRDNORM;
+ if (eof)
+ return POLLHUP;
+ return 0;
+}
+
+int cx18_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct cx18_open_id *id = file->private_data;
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[id->type];
+ int eof = test_bit(CX18_F_S_STREAMOFF, &s->s_flags);
+
+ if ((s->vb_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (id->type == CX18_ENC_STREAM_TYPE_YUV)) {
+
+ /* Start a capture if there is none */
+ if (!eof && !test_bit(CX18_F_S_STREAMING, &s->s_flags)) {
+ int rc;
+
+ mutex_lock(&cx->serialize_lock);
+ rc = cx18_start_capture(id);
+ mutex_unlock(&cx->serialize_lock);
+ if (rc) {
+ CX18_DEBUG_INFO(
+ "Could not start capture for %s (%d)\n",
+ s->name, rc);
+ return -EINVAL;
+ }
+ CX18_DEBUG_FILE("Encoder mmap started capture\n");
+ }
+
+ return videobuf_mmap_mapper(&s->vbuf_q, vma);
+ }
+
+ return -EINVAL;
+}
+
+void cx18_vb_timeout(unsigned long data)
+{
+ struct cx18_stream *s = (struct cx18_stream *)data;
+ struct cx18_videobuf_buffer *buf;
+ unsigned long flags;
+
+ /* Return all of the buffers in error state, so the vbi/vid inode
+ * can return from blocking.
+ */
+ spin_lock_irqsave(&s->vb_lock, flags);
+ while (!list_empty(&s->vb_capture)) {
+ buf = list_entry(s->vb_capture.next,
+ struct cx18_videobuf_buffer, vb.queue);
+ list_del(&buf->vb.queue);
+ buf->vb.state = VIDEOBUF_ERROR;
+ wake_up(&buf->vb.done);
+ }
+ spin_unlock_irqrestore(&s->vb_lock, flags);
+}
+
+void cx18_stop_capture(struct cx18_open_id *id, int gop_end)
+{
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[id->type];
+ struct cx18_stream *s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
+ struct cx18_stream *s_idx = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
+
+ CX18_DEBUG_IOCTL("close() of %s\n", s->name);
+
+ /* 'Unclaim' this stream */
+
+ /* Stop capturing */
+ if (test_bit(CX18_F_S_STREAMING, &s->s_flags)) {
+ CX18_DEBUG_INFO("close stopping capture\n");
+ if (id->type == CX18_ENC_STREAM_TYPE_MPG) {
+ /* Stop internal use associated VBI and IDX streams */
+ if (test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags) &&
+ !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) {
+ CX18_DEBUG_INFO("close stopping embedded VBI "
+ "capture\n");
+ cx18_stop_v4l2_encode_stream(s_vbi, 0);
+ }
+ if (test_bit(CX18_F_S_STREAMING, &s_idx->s_flags)) {
+ CX18_DEBUG_INFO("close stopping IDX capture\n");
+ cx18_stop_v4l2_encode_stream(s_idx, 0);
+ }
+ }
+ if (id->type == CX18_ENC_STREAM_TYPE_VBI &&
+ test_bit(CX18_F_S_INTERNAL_USE, &s->s_flags))
+ /* Also used internally, don't stop capturing */
+ s->id = -1;
+ else
+ cx18_stop_v4l2_encode_stream(s, gop_end);
+ }
+ if (!gop_end) {
+ clear_bit(CX18_F_S_APPL_IO, &s->s_flags);
+ clear_bit(CX18_F_S_STREAMOFF, &s->s_flags);
+ cx18_release_stream(s);
+ }
+}
+
+int cx18_v4l2_close(struct file *filp)
+{
+ struct v4l2_fh *fh = filp->private_data;
+ struct cx18_open_id *id = fh2id(fh);
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[id->type];
+
+ CX18_DEBUG_IOCTL("close() of %s\n", s->name);
+
+ mutex_lock(&cx->serialize_lock);
+ /* Stop radio */
+ if (id->type == CX18_ENC_STREAM_TYPE_RAD &&
+ v4l2_fh_is_singular_file(filp)) {
+ /* Closing radio device, return to TV mode */
+ cx18_mute(cx);
+ /* Mark that the radio is no longer in use */
+ clear_bit(CX18_F_I_RADIO_USER, &cx->i_flags);
+ /* Switch tuner to TV */
+ cx18_call_all(cx, core, s_std, cx->std);
+ /* Select correct audio input (i.e. TV tuner or Line in) */
+ cx18_audio_set_io(cx);
+ if (atomic_read(&cx->ana_capturing) > 0) {
+ /* Undo video mute */
+ cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle,
+ (v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute) |
+ (v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute_yuv) << 8)));
+ }
+ /* Done! Unmute and continue. */
+ cx18_unmute(cx);
+ }
+
+ v4l2_fh_del(fh);
+ v4l2_fh_exit(fh);
+
+ /* 'Unclaim' this stream */
+ if (s->id == id->open_id)
+ cx18_stop_capture(id, 0);
+ kfree(id);
+ mutex_unlock(&cx->serialize_lock);
+ return 0;
+}
+
+static int cx18_serialized_open(struct cx18_stream *s, struct file *filp)
+{
+ struct cx18 *cx = s->cx;
+ struct cx18_open_id *item;
+
+ CX18_DEBUG_FILE("open %s\n", s->name);
+
+ /* Allocate memory */
+ item = kzalloc(sizeof(struct cx18_open_id), GFP_KERNEL);
+ if (NULL == item) {
+ CX18_DEBUG_WARN("nomem on v4l2 open\n");
+ return -ENOMEM;
+ }
+ v4l2_fh_init(&item->fh, s->video_dev);
+
+ item->cx = cx;
+ item->type = s->type;
+
+ item->open_id = cx->open_id++;
+ filp->private_data = &item->fh;
+ v4l2_fh_add(&item->fh);
+
+ if (item->type == CX18_ENC_STREAM_TYPE_RAD &&
+ v4l2_fh_is_singular_file(filp)) {
+ if (!test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) {
+ if (atomic_read(&cx->ana_capturing) > 0) {
+ /* switching to radio while capture is
+ in progress is not polite */
+ v4l2_fh_del(&item->fh);
+ v4l2_fh_exit(&item->fh);
+ kfree(item);
+ return -EBUSY;
+ }
+ }
+
+ /* Mark that the radio is being used. */
+ set_bit(CX18_F_I_RADIO_USER, &cx->i_flags);
+ /* We have the radio */
+ cx18_mute(cx);
+ /* Switch tuner to radio */
+ cx18_call_all(cx, tuner, s_radio);
+ /* Select the correct audio input (i.e. radio tuner) */
+ cx18_audio_set_io(cx);
+ /* Done! Unmute and continue. */
+ cx18_unmute(cx);
+ }
+ return 0;
+}
+
+int cx18_v4l2_open(struct file *filp)
+{
+ int res;
+ struct video_device *video_dev = video_devdata(filp);
+ struct cx18_stream *s = video_get_drvdata(video_dev);
+ struct cx18 *cx = s->cx;
+
+ mutex_lock(&cx->serialize_lock);
+ if (cx18_init_on_first_open(cx)) {
+ CX18_ERR("Failed to initialize on %s\n",
+ video_device_node_name(video_dev));
+ mutex_unlock(&cx->serialize_lock);
+ return -ENXIO;
+ }
+ res = cx18_serialized_open(s, filp);
+ mutex_unlock(&cx->serialize_lock);
+ return res;
+}
+
+void cx18_mute(struct cx18 *cx)
+{
+ u32 h;
+ if (atomic_read(&cx->ana_capturing)) {
+ h = cx18_find_handle(cx);
+ if (h != CX18_INVALID_TASK_HANDLE)
+ cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2, h, 1);
+ else
+ CX18_ERR("Can't find valid task handle for mute\n");
+ }
+ CX18_DEBUG_INFO("Mute\n");
+}
+
+void cx18_unmute(struct cx18 *cx)
+{
+ u32 h;
+ if (atomic_read(&cx->ana_capturing)) {
+ h = cx18_find_handle(cx);
+ if (h != CX18_INVALID_TASK_HANDLE) {
+ cx18_msleep_timeout(100, 0);
+ cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, h, 12);
+ cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2, h, 0);
+ } else
+ CX18_ERR("Can't find valid task handle for unmute\n");
+ }
+ CX18_DEBUG_INFO("Unmute\n");
+}
diff --git a/drivers/media/video/cx18/cx18-fileops.h b/drivers/media/video/cx18/cx18-fileops.h
new file mode 100644
index 00000000..b9e5110a
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-fileops.h
@@ -0,0 +1,41 @@
+/*
+ * cx18 file operation functions
+ *
+ * Derived from ivtv-fileops.h
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+/* Testing/Debugging */
+int cx18_v4l2_open(struct file *filp);
+ssize_t cx18_v4l2_read(struct file *filp, char __user *buf, size_t count,
+ loff_t *pos);
+ssize_t cx18_v4l2_write(struct file *filp, const char __user *buf, size_t count,
+ loff_t *pos);
+int cx18_v4l2_close(struct file *filp);
+unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait);
+int cx18_start_capture(struct cx18_open_id *id);
+void cx18_stop_capture(struct cx18_open_id *id, int gop_end);
+void cx18_mute(struct cx18 *cx);
+void cx18_unmute(struct cx18 *cx);
+int cx18_v4l2_mmap(struct file *file, struct vm_area_struct *vma);
+void cx18_vb_timeout(unsigned long data);
+
+/* Shared with cx18-alsa module */
+int cx18_claim_stream(struct cx18_open_id *id, int type);
+void cx18_release_stream(struct cx18_stream *s);
diff --git a/drivers/media/video/cx18/cx18-firmware.c b/drivers/media/video/cx18/cx18-firmware.c
new file mode 100644
index 00000000..1b3fb502
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-firmware.c
@@ -0,0 +1,448 @@
+/*
+ * cx18 firmware functions
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-scb.h"
+#include "cx18-irq.h"
+#include "cx18-firmware.h"
+#include "cx18-cards.h"
+#include <linux/firmware.h>
+
+#define CX18_PROC_SOFT_RESET 0xc70010
+#define CX18_DDR_SOFT_RESET 0xc70014
+#define CX18_CLOCK_SELECT1 0xc71000
+#define CX18_CLOCK_SELECT2 0xc71004
+#define CX18_HALF_CLOCK_SELECT1 0xc71008
+#define CX18_HALF_CLOCK_SELECT2 0xc7100C
+#define CX18_CLOCK_POLARITY1 0xc71010
+#define CX18_CLOCK_POLARITY2 0xc71014
+#define CX18_ADD_DELAY_ENABLE1 0xc71018
+#define CX18_ADD_DELAY_ENABLE2 0xc7101C
+#define CX18_CLOCK_ENABLE1 0xc71020
+#define CX18_CLOCK_ENABLE2 0xc71024
+
+#define CX18_REG_BUS_TIMEOUT_EN 0xc72024
+
+#define CX18_FAST_CLOCK_PLL_INT 0xc78000
+#define CX18_FAST_CLOCK_PLL_FRAC 0xc78004
+#define CX18_FAST_CLOCK_PLL_POST 0xc78008
+#define CX18_FAST_CLOCK_PLL_PRESCALE 0xc7800C
+#define CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH 0xc78010
+
+#define CX18_SLOW_CLOCK_PLL_INT 0xc78014
+#define CX18_SLOW_CLOCK_PLL_FRAC 0xc78018
+#define CX18_SLOW_CLOCK_PLL_POST 0xc7801C
+#define CX18_MPEG_CLOCK_PLL_INT 0xc78040
+#define CX18_MPEG_CLOCK_PLL_FRAC 0xc78044
+#define CX18_MPEG_CLOCK_PLL_POST 0xc78048
+#define CX18_PLL_POWER_DOWN 0xc78088
+#define CX18_SW1_INT_STATUS 0xc73104
+#define CX18_SW1_INT_ENABLE_PCI 0xc7311C
+#define CX18_SW2_INT_SET 0xc73140
+#define CX18_SW2_INT_STATUS 0xc73144
+#define CX18_ADEC_CONTROL 0xc78120
+
+#define CX18_DDR_REQUEST_ENABLE 0xc80000
+#define CX18_DDR_CHIP_CONFIG 0xc80004
+#define CX18_DDR_REFRESH 0xc80008
+#define CX18_DDR_TIMING1 0xc8000C
+#define CX18_DDR_TIMING2 0xc80010
+#define CX18_DDR_POWER_REG 0xc8001C
+
+#define CX18_DDR_TUNE_LANE 0xc80048
+#define CX18_DDR_INITIAL_EMRS 0xc80054
+#define CX18_DDR_MB_PER_ROW_7 0xc8009C
+#define CX18_DDR_BASE_63_ADDR 0xc804FC
+
+#define CX18_WMB_CLIENT02 0xc90108
+#define CX18_WMB_CLIENT05 0xc90114
+#define CX18_WMB_CLIENT06 0xc90118
+#define CX18_WMB_CLIENT07 0xc9011C
+#define CX18_WMB_CLIENT08 0xc90120
+#define CX18_WMB_CLIENT09 0xc90124
+#define CX18_WMB_CLIENT10 0xc90128
+#define CX18_WMB_CLIENT11 0xc9012C
+#define CX18_WMB_CLIENT12 0xc90130
+#define CX18_WMB_CLIENT13 0xc90134
+#define CX18_WMB_CLIENT14 0xc90138
+
+#define CX18_DSP0_INTERRUPT_MASK 0xd0004C
+
+#define APU_ROM_SYNC1 0x6D676553 /* "mgeS" */
+#define APU_ROM_SYNC2 0x72646548 /* "rdeH" */
+
+struct cx18_apu_rom_seghdr {
+ u32 sync1;
+ u32 sync2;
+ u32 addr;
+ u32 size;
+};
+
+static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx)
+{
+ const struct firmware *fw = NULL;
+ int i, j;
+ unsigned size;
+ u32 __iomem *dst = (u32 __iomem *)mem;
+ const u32 *src;
+
+ if (request_firmware(&fw, fn, &cx->pci_dev->dev)) {
+ CX18_ERR("Unable to open firmware %s\n", fn);
+ CX18_ERR("Did you put the firmware in the hotplug firmware directory?\n");
+ return -ENOMEM;
+ }
+
+ src = (const u32 *)fw->data;
+
+ for (i = 0; i < fw->size; i += 4096) {
+ cx18_setup_page(cx, i);
+ for (j = i; j < fw->size && j < i + 4096; j += 4) {
+ /* no need for endianness conversion on the ppc */
+ cx18_raw_writel(cx, *src, dst);
+ if (cx18_raw_readl(cx, dst) != *src) {
+ CX18_ERR("Mismatch at offset %x\n", i);
+ release_firmware(fw);
+ cx18_setup_page(cx, 0);
+ return -EIO;
+ }
+ dst++;
+ src++;
+ }
+ }
+ if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags))
+ CX18_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size);
+ size = fw->size;
+ release_firmware(fw);
+ cx18_setup_page(cx, SCB_OFFSET);
+ return size;
+}
+
+static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx,
+ u32 *entry_addr)
+{
+ const struct firmware *fw = NULL;
+ int i, j;
+ unsigned size;
+ const u32 *src;
+ struct cx18_apu_rom_seghdr seghdr;
+ const u8 *vers;
+ u32 offset = 0;
+ u32 apu_version = 0;
+ int sz;
+
+ if (request_firmware(&fw, fn, &cx->pci_dev->dev)) {
+ CX18_ERR("unable to open firmware %s\n", fn);
+ CX18_ERR("did you put the firmware in the hotplug firmware directory?\n");
+ cx18_setup_page(cx, 0);
+ return -ENOMEM;
+ }
+
+ *entry_addr = 0;
+ src = (const u32 *)fw->data;
+ vers = fw->data + sizeof(seghdr);
+ sz = fw->size;
+
+ apu_version = (vers[0] << 24) | (vers[4] << 16) | vers[32];
+ while (offset + sizeof(seghdr) < fw->size) {
+ /* TODO: byteswapping */
+ memcpy(&seghdr, src + offset / 4, sizeof(seghdr));
+ offset += sizeof(seghdr);
+ if (seghdr.sync1 != APU_ROM_SYNC1 ||
+ seghdr.sync2 != APU_ROM_SYNC2) {
+ offset += seghdr.size;
+ continue;
+ }
+ CX18_DEBUG_INFO("load segment %x-%x\n", seghdr.addr,
+ seghdr.addr + seghdr.size - 1);
+ if (*entry_addr == 0)
+ *entry_addr = seghdr.addr;
+ if (offset + seghdr.size > sz)
+ break;
+ for (i = 0; i < seghdr.size; i += 4096) {
+ cx18_setup_page(cx, seghdr.addr + i);
+ for (j = i; j < seghdr.size && j < i + 4096; j += 4) {
+ /* no need for endianness conversion on the ppc */
+ cx18_raw_writel(cx, src[(offset + j) / 4],
+ dst + seghdr.addr + j);
+ if (cx18_raw_readl(cx, dst + seghdr.addr + j)
+ != src[(offset + j) / 4]) {
+ CX18_ERR("Mismatch at offset %x\n",
+ offset + j);
+ release_firmware(fw);
+ cx18_setup_page(cx, 0);
+ return -EIO;
+ }
+ }
+ }
+ offset += seghdr.size;
+ }
+ if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags))
+ CX18_INFO("loaded %s firmware V%08x (%zd bytes)\n",
+ fn, apu_version, fw->size);
+ size = fw->size;
+ release_firmware(fw);
+ cx18_setup_page(cx, 0);
+ return size;
+}
+
+void cx18_halt_firmware(struct cx18 *cx)
+{
+ CX18_DEBUG_INFO("Preparing for firmware halt.\n");
+ cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET,
+ 0x0000000F, 0x000F000F);
+ cx18_write_reg_expect(cx, 0x00020002, CX18_ADEC_CONTROL,
+ 0x00000002, 0x00020002);
+}
+
+void cx18_init_power(struct cx18 *cx, int lowpwr)
+{
+ /* power-down Spare and AOM PLLs */
+ /* power-up fast, slow and mpeg PLLs */
+ cx18_write_reg(cx, 0x00000008, CX18_PLL_POWER_DOWN);
+
+ /* ADEC out of sleep */
+ cx18_write_reg_expect(cx, 0x00020000, CX18_ADEC_CONTROL,
+ 0x00000000, 0x00020002);
+
+ /*
+ * The PLL parameters are based on the external crystal frequency that
+ * would ideally be:
+ *
+ * NTSC Color subcarrier freq * 8 =
+ * 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz
+ *
+ * The accidents of history and rationale that explain from where this
+ * combination of magic numbers originate can be found in:
+ *
+ * [1] Abrahams, I. C., "Choice of Chrominance Subcarrier Frequency in
+ * the NTSC Standards", Proceedings of the I-R-E, January 1954, pp 79-80
+ *
+ * [2] Abrahams, I. C., "The 'Frequency Interleaving' Principle in the
+ * NTSC Standards", Proceedings of the I-R-E, January 1954, pp 81-83
+ *
+ * As Mike Bradley has rightly pointed out, it's not the exact crystal
+ * frequency that matters, only that all parts of the driver and
+ * firmware are using the same value (close to the ideal value).
+ *
+ * Since I have a strong suspicion that, if the firmware ever assumes a
+ * crystal value at all, it will assume 28.636360 MHz, the crystal
+ * freq used in calculations in this driver will be:
+ *
+ * xtal_freq = 28.636360 MHz
+ *
+ * an error of less than 0.13 ppm which is way, way better than any off
+ * the shelf crystal will have for accuracy anyway.
+ *
+ * Below I aim to run the PLLs' VCOs near 400 MHz to minimze errors.
+ *
+ * Many thanks to Jeff Campbell and Mike Bradley for their extensive
+ * investigation, experimentation, testing, and suggested solutions of
+ * of audio/video sync problems with SVideo and CVBS captures.
+ */
+
+ /* the fast clock is at 200/245 MHz */
+ /* 1 * xtal_freq * 0x0d.f7df9b8 / 2 = 200 MHz: 400 MHz pre post-divide*/
+ /* 1 * xtal_freq * 0x11.1c71eb8 / 2 = 245 MHz: 490 MHz pre post-divide*/
+ cx18_write_reg(cx, lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT);
+ cx18_write_reg(cx, lowpwr ? 0x1EFBF37 : 0x038E3D7,
+ CX18_FAST_CLOCK_PLL_FRAC);
+
+ cx18_write_reg(cx, 2, CX18_FAST_CLOCK_PLL_POST);
+ cx18_write_reg(cx, 1, CX18_FAST_CLOCK_PLL_PRESCALE);
+ cx18_write_reg(cx, 4, CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH);
+
+ /* set slow clock to 125/120 MHz */
+ /* xtal_freq * 0x0d.1861a20 / 3 = 125 MHz: 375 MHz before post-divide */
+ /* xtal_freq * 0x0c.92493f8 / 3 = 120 MHz: 360 MHz before post-divide */
+ cx18_write_reg(cx, lowpwr ? 0xD : 0xC, CX18_SLOW_CLOCK_PLL_INT);
+ cx18_write_reg(cx, lowpwr ? 0x30C344 : 0x124927F,
+ CX18_SLOW_CLOCK_PLL_FRAC);
+ cx18_write_reg(cx, 3, CX18_SLOW_CLOCK_PLL_POST);
+
+ /* mpeg clock pll 54MHz */
+ /* xtal_freq * 0xf.15f17f0 / 8 = 54 MHz: 432 MHz before post-divide */
+ cx18_write_reg(cx, 0xF, CX18_MPEG_CLOCK_PLL_INT);
+ cx18_write_reg(cx, 0x2BE2FE, CX18_MPEG_CLOCK_PLL_FRAC);
+ cx18_write_reg(cx, 8, CX18_MPEG_CLOCK_PLL_POST);
+
+ /* Defaults */
+ /* APU = SC or SC/2 = 125/62.5 */
+ /* EPU = SC = 125 */
+ /* DDR = FC = 180 */
+ /* ENC = SC = 125 */
+ /* AI1 = SC = 125 */
+ /* VIM2 = disabled */
+ /* PCI = FC/2 = 90 */
+ /* AI2 = disabled */
+ /* DEMUX = disabled */
+ /* AO = SC/2 = 62.5 */
+ /* SER = 54MHz */
+ /* VFC = disabled */
+ /* USB = disabled */
+
+ if (lowpwr) {
+ cx18_write_reg_expect(cx, 0xFFFF0020, CX18_CLOCK_SELECT1,
+ 0x00000020, 0xFFFFFFFF);
+ cx18_write_reg_expect(cx, 0xFFFF0004, CX18_CLOCK_SELECT2,
+ 0x00000004, 0xFFFFFFFF);
+ } else {
+ /* This doesn't explicitly set every clock select */
+ cx18_write_reg_expect(cx, 0x00060004, CX18_CLOCK_SELECT1,
+ 0x00000004, 0x00060006);
+ cx18_write_reg_expect(cx, 0x00060006, CX18_CLOCK_SELECT2,
+ 0x00000006, 0x00060006);
+ }
+
+ cx18_write_reg_expect(cx, 0xFFFF0002, CX18_HALF_CLOCK_SELECT1,
+ 0x00000002, 0xFFFFFFFF);
+ cx18_write_reg_expect(cx, 0xFFFF0104, CX18_HALF_CLOCK_SELECT2,
+ 0x00000104, 0xFFFFFFFF);
+ cx18_write_reg_expect(cx, 0xFFFF9026, CX18_CLOCK_ENABLE1,
+ 0x00009026, 0xFFFFFFFF);
+ cx18_write_reg_expect(cx, 0xFFFF3105, CX18_CLOCK_ENABLE2,
+ 0x00003105, 0xFFFFFFFF);
+}
+
+void cx18_init_memory(struct cx18 *cx)
+{
+ cx18_msleep_timeout(10, 0);
+ cx18_write_reg_expect(cx, 0x00010000, CX18_DDR_SOFT_RESET,
+ 0x00000000, 0x00010001);
+ cx18_msleep_timeout(10, 0);
+
+ cx18_write_reg(cx, cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG);
+
+ cx18_msleep_timeout(10, 0);
+
+ cx18_write_reg(cx, cx->card->ddr.refresh, CX18_DDR_REFRESH);
+ cx18_write_reg(cx, cx->card->ddr.timing1, CX18_DDR_TIMING1);
+ cx18_write_reg(cx, cx->card->ddr.timing2, CX18_DDR_TIMING2);
+
+ cx18_msleep_timeout(10, 0);
+
+ /* Initialize DQS pad time */
+ cx18_write_reg(cx, cx->card->ddr.tune_lane, CX18_DDR_TUNE_LANE);
+ cx18_write_reg(cx, cx->card->ddr.initial_emrs, CX18_DDR_INITIAL_EMRS);
+
+ cx18_msleep_timeout(10, 0);
+
+ cx18_write_reg_expect(cx, 0x00020000, CX18_DDR_SOFT_RESET,
+ 0x00000000, 0x00020002);
+ cx18_msleep_timeout(10, 0);
+
+ /* use power-down mode when idle */
+ cx18_write_reg(cx, 0x00000010, CX18_DDR_POWER_REG);
+
+ cx18_write_reg_expect(cx, 0x00010001, CX18_REG_BUS_TIMEOUT_EN,
+ 0x00000001, 0x00010001);
+
+ cx18_write_reg(cx, 0x48, CX18_DDR_MB_PER_ROW_7);
+ cx18_write_reg(cx, 0xE0000, CX18_DDR_BASE_63_ADDR);
+
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT02); /* AO */
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT09); /* AI2 */
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT05); /* VIM1 */
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT06); /* AI1 */
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT07); /* 3D comb */
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT10); /* ME */
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT12); /* ENC */
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT13); /* PK */
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT11); /* RC */
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT14); /* AVO */
+}
+
+int cx18_firmware_init(struct cx18 *cx)
+{
+ u32 fw_entry_addr;
+ int sz, retries;
+ u32 api_args[MAX_MB_ARGUMENTS];
+
+ /* Allow chip to control CLKRUN */
+ cx18_write_reg(cx, 0x5, CX18_DSP0_INTERRUPT_MASK);
+
+ /* Stop the firmware */
+ cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET,
+ 0x0000000F, 0x000F000F);
+
+ cx18_msleep_timeout(1, 0);
+
+ /* If the CPU is still running */
+ if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) == 0) {
+ CX18_ERR("%s: couldn't stop CPU to load firmware\n", __func__);
+ return -EIO;
+ }
+
+ cx18_sw1_irq_enable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
+ cx18_sw2_irq_enable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
+
+ sz = load_cpu_fw_direct("v4l-cx23418-cpu.fw", cx->enc_mem, cx);
+ if (sz <= 0)
+ return sz;
+
+ /* The SCB & IPC area *must* be correct before starting the firmwares */
+ cx18_init_scb(cx);
+
+ fw_entry_addr = 0;
+ sz = load_apu_fw_direct("v4l-cx23418-apu.fw", cx->enc_mem, cx,
+ &fw_entry_addr);
+ if (sz <= 0)
+ return sz;
+
+ /* Start the CPU. The CPU will take care of the APU for us. */
+ cx18_write_reg_expect(cx, 0x00080000, CX18_PROC_SOFT_RESET,
+ 0x00000000, 0x00080008);
+
+ /* Wait up to 500 ms for the APU to come out of reset */
+ for (retries = 0;
+ retries < 50 && (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1;
+ retries++)
+ cx18_msleep_timeout(10, 0);
+
+ cx18_msleep_timeout(200, 0);
+
+ if (retries == 50 &&
+ (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1) {
+ CX18_ERR("Could not start the CPU\n");
+ return -EIO;
+ }
+
+ /*
+ * The CPU had once before set up to receive an interrupt for it's
+ * outgoing IRQ_CPU_TO_EPU_ACK to us. If it ever does this, we get an
+ * interrupt when it sends us an ack, but by the time we process it,
+ * that flag in the SW2 status register has been cleared by the CPU
+ * firmware. We'll prevent that not so useful condition from happening
+ * by clearing the CPU's interrupt enables for Ack IRQ's we want to
+ * process.
+ */
+ cx18_sw2_irq_disable_cpu(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
+
+ /* Try a benign command to see if the CPU is alive and well */
+ sz = cx18_vapi_result(cx, api_args, CX18_CPU_DEBUG_PEEK32, 1, 0);
+ if (sz < 0)
+ return sz;
+
+ /* initialize GPIO */
+ cx18_write_reg_expect(cx, 0x14001400, 0xc78110, 0x00001400, 0x14001400);
+ return 0;
+}
diff --git a/drivers/media/video/cx18/cx18-firmware.h b/drivers/media/video/cx18/cx18-firmware.h
new file mode 100644
index 00000000..38d4c05e
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-firmware.h
@@ -0,0 +1,25 @@
+/*
+ * cx18 firmware functions
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+int cx18_firmware_init(struct cx18 *cx);
+void cx18_halt_firmware(struct cx18 *cx);
+void cx18_init_memory(struct cx18 *cx);
+void cx18_init_power(struct cx18 *cx, int lowpwr);
diff --git a/drivers/media/video/cx18/cx18-gpio.c b/drivers/media/video/cx18/cx18-gpio.c
new file mode 100644
index 00000000..5374aeb0
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-gpio.c
@@ -0,0 +1,347 @@
+/*
+ * cx18 gpio functions
+ *
+ * Derived from ivtv-gpio.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-cards.h"
+#include "cx18-gpio.h"
+#include "tuner-xc2028.h"
+
+/********************* GPIO stuffs *********************/
+
+/* GPIO registers */
+#define CX18_REG_GPIO_IN 0xc72010
+#define CX18_REG_GPIO_OUT1 0xc78100
+#define CX18_REG_GPIO_DIR1 0xc78108
+#define CX18_REG_GPIO_OUT2 0xc78104
+#define CX18_REG_GPIO_DIR2 0xc7810c
+
+/*
+ * HVR-1600 GPIO pins, courtesy of Hauppauge:
+ *
+ * gpio0: zilog ir process reset pin
+ * gpio1: zilog programming pin (you should never use this)
+ * gpio12: cx24227 reset pin
+ * gpio13: cs5345 reset pin
+*/
+
+/*
+ * File scope utility functions
+ */
+static void gpio_write(struct cx18 *cx)
+{
+ u32 dir_lo = cx->gpio_dir & 0xffff;
+ u32 val_lo = cx->gpio_val & 0xffff;
+ u32 dir_hi = cx->gpio_dir >> 16;
+ u32 val_hi = cx->gpio_val >> 16;
+
+ cx18_write_reg_expect(cx, dir_lo << 16,
+ CX18_REG_GPIO_DIR1, ~dir_lo, dir_lo);
+ cx18_write_reg_expect(cx, (dir_lo << 16) | val_lo,
+ CX18_REG_GPIO_OUT1, val_lo, dir_lo);
+ cx18_write_reg_expect(cx, dir_hi << 16,
+ CX18_REG_GPIO_DIR2, ~dir_hi, dir_hi);
+ cx18_write_reg_expect(cx, (dir_hi << 16) | val_hi,
+ CX18_REG_GPIO_OUT2, val_hi, dir_hi);
+}
+
+static void gpio_update(struct cx18 *cx, u32 mask, u32 data)
+{
+ if (mask == 0)
+ return;
+
+ mutex_lock(&cx->gpio_lock);
+ cx->gpio_val = (cx->gpio_val & ~mask) | (data & mask);
+ gpio_write(cx);
+ mutex_unlock(&cx->gpio_lock);
+}
+
+static void gpio_reset_seq(struct cx18 *cx, u32 active_lo, u32 active_hi,
+ unsigned int assert_msecs,
+ unsigned int recovery_msecs)
+{
+ u32 mask;
+
+ mask = active_lo | active_hi;
+ if (mask == 0)
+ return;
+
+ /*
+ * Assuming that active_hi and active_lo are a subsets of the bits in
+ * gpio_dir. Also assumes that active_lo and active_hi don't overlap
+ * in any bit position
+ */
+
+ /* Assert */
+ gpio_update(cx, mask, ~active_lo);
+ schedule_timeout_uninterruptible(msecs_to_jiffies(assert_msecs));
+
+ /* Deassert */
+ gpio_update(cx, mask, ~active_hi);
+ schedule_timeout_uninterruptible(msecs_to_jiffies(recovery_msecs));
+}
+
+/*
+ * GPIO Multiplexer - logical device
+ */
+static int gpiomux_log_status(struct v4l2_subdev *sd)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+ mutex_lock(&cx->gpio_lock);
+ CX18_INFO_DEV(sd, "GPIO: direction 0x%08x, value 0x%08x\n",
+ cx->gpio_dir, cx->gpio_val);
+ mutex_unlock(&cx->gpio_lock);
+ return 0;
+}
+
+static int gpiomux_s_radio(struct v4l2_subdev *sd)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+ /*
+ * FIXME - work out the cx->active/audio_input mess - this is
+ * intended to handle the switch to radio mode and set the
+ * audio routing, but we need to update the state in cx
+ */
+ gpio_update(cx, cx->card->gpio_audio_input.mask,
+ cx->card->gpio_audio_input.radio);
+ return 0;
+}
+
+static int gpiomux_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ u32 data;
+
+ switch (cx->card->audio_inputs[cx->audio_input].muxer_input) {
+ case 1:
+ data = cx->card->gpio_audio_input.linein;
+ break;
+ case 0:
+ data = cx->card->gpio_audio_input.tuner;
+ break;
+ default:
+ /*
+ * FIXME - work out the cx->active/audio_input mess - this is
+ * intended to handle the switch from radio mode and set the
+ * audio routing, but we need to update the state in cx
+ */
+ data = cx->card->gpio_audio_input.tuner;
+ break;
+ }
+ gpio_update(cx, cx->card->gpio_audio_input.mask, data);
+ return 0;
+}
+
+static int gpiomux_s_audio_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ u32 data;
+
+ switch (input) {
+ case 0:
+ data = cx->card->gpio_audio_input.tuner;
+ break;
+ case 1:
+ data = cx->card->gpio_audio_input.linein;
+ break;
+ case 2:
+ data = cx->card->gpio_audio_input.radio;
+ break;
+ default:
+ return -EINVAL;
+ }
+ gpio_update(cx, cx->card->gpio_audio_input.mask, data);
+ return 0;
+}
+
+static const struct v4l2_subdev_core_ops gpiomux_core_ops = {
+ .log_status = gpiomux_log_status,
+ .s_std = gpiomux_s_std,
+};
+
+static const struct v4l2_subdev_tuner_ops gpiomux_tuner_ops = {
+ .s_radio = gpiomux_s_radio,
+};
+
+static const struct v4l2_subdev_audio_ops gpiomux_audio_ops = {
+ .s_routing = gpiomux_s_audio_routing,
+};
+
+static const struct v4l2_subdev_ops gpiomux_ops = {
+ .core = &gpiomux_core_ops,
+ .tuner = &gpiomux_tuner_ops,
+ .audio = &gpiomux_audio_ops,
+};
+
+/*
+ * GPIO Reset Controller - logical device
+ */
+static int resetctrl_log_status(struct v4l2_subdev *sd)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+ mutex_lock(&cx->gpio_lock);
+ CX18_INFO_DEV(sd, "GPIO: direction 0x%08x, value 0x%08x\n",
+ cx->gpio_dir, cx->gpio_val);
+ mutex_unlock(&cx->gpio_lock);
+ return 0;
+}
+
+static int resetctrl_reset(struct v4l2_subdev *sd, u32 val)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ const struct cx18_gpio_i2c_slave_reset *p;
+
+ p = &cx->card->gpio_i2c_slave_reset;
+ switch (val) {
+ case CX18_GPIO_RESET_I2C:
+ gpio_reset_seq(cx, p->active_lo_mask, p->active_hi_mask,
+ p->msecs_asserted, p->msecs_recovery);
+ break;
+ case CX18_GPIO_RESET_Z8F0811:
+ /*
+ * Assert timing for the Z8F0811 on HVR-1600 boards:
+ * 1. Assert RESET for min of 4 clock cycles at 18.432 MHz to
+ * initiate
+ * 2. Reset then takes 66 WDT cycles at 10 kHz + 16 xtal clock
+ * cycles (6,601,085 nanoseconds ~= 7 milliseconds)
+ * 3. DBG pin must be high before chip exits reset for normal
+ * operation. DBG is open drain and hopefully pulled high
+ * since we don't normally drive it (GPIO 1?) for the
+ * HVR-1600
+ * 4. Z8F0811 won't exit reset until RESET is deasserted
+ * 5. Zilog comes out of reset, loads reset vector address and
+ * executes from there. Required recovery delay unknown.
+ */
+ gpio_reset_seq(cx, p->ir_reset_mask, 0,
+ p->msecs_asserted, p->msecs_recovery);
+ break;
+ case CX18_GPIO_RESET_XC2028:
+ if (cx->card->tuners[0].tuner == TUNER_XC2028)
+ gpio_reset_seq(cx, (1 << cx->card->xceive_pin), 0,
+ 1, 1);
+ break;
+ }
+ return 0;
+}
+
+static const struct v4l2_subdev_core_ops resetctrl_core_ops = {
+ .log_status = resetctrl_log_status,
+ .reset = resetctrl_reset,
+};
+
+static const struct v4l2_subdev_ops resetctrl_ops = {
+ .core = &resetctrl_core_ops,
+};
+
+/*
+ * External entry points
+ */
+void cx18_gpio_init(struct cx18 *cx)
+{
+ mutex_lock(&cx->gpio_lock);
+ cx->gpio_dir = cx->card->gpio_init.direction;
+ cx->gpio_val = cx->card->gpio_init.initial_value;
+
+ if (cx->card->tuners[0].tuner == TUNER_XC2028) {
+ cx->gpio_dir |= 1 << cx->card->xceive_pin;
+ cx->gpio_val |= 1 << cx->card->xceive_pin;
+ }
+
+ if (cx->gpio_dir == 0) {
+ mutex_unlock(&cx->gpio_lock);
+ return;
+ }
+
+ CX18_DEBUG_INFO("GPIO initial dir: %08x/%08x out: %08x/%08x\n",
+ cx18_read_reg(cx, CX18_REG_GPIO_DIR1),
+ cx18_read_reg(cx, CX18_REG_GPIO_DIR2),
+ cx18_read_reg(cx, CX18_REG_GPIO_OUT1),
+ cx18_read_reg(cx, CX18_REG_GPIO_OUT2));
+
+ gpio_write(cx);
+ mutex_unlock(&cx->gpio_lock);
+}
+
+int cx18_gpio_register(struct cx18 *cx, u32 hw)
+{
+ struct v4l2_subdev *sd;
+ const struct v4l2_subdev_ops *ops;
+ char *str;
+
+ switch (hw) {
+ case CX18_HW_GPIO_MUX:
+ sd = &cx->sd_gpiomux;
+ ops = &gpiomux_ops;
+ str = "gpio-mux";
+ break;
+ case CX18_HW_GPIO_RESET_CTRL:
+ sd = &cx->sd_resetctrl;
+ ops = &resetctrl_ops;
+ str = "gpio-reset-ctrl";
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ v4l2_subdev_init(sd, ops);
+ v4l2_set_subdevdata(sd, cx);
+ snprintf(sd->name, sizeof(sd->name), "%s %s", cx->v4l2_dev.name, str);
+ sd->grp_id = hw;
+ return v4l2_device_register_subdev(&cx->v4l2_dev, sd);
+}
+
+void cx18_reset_ir_gpio(void *data)
+{
+ struct cx18 *cx = to_cx18((struct v4l2_device *)data);
+
+ if (cx->card->gpio_i2c_slave_reset.ir_reset_mask == 0)
+ return;
+
+ CX18_DEBUG_INFO("Resetting IR microcontroller\n");
+
+ v4l2_subdev_call(&cx->sd_resetctrl,
+ core, reset, CX18_GPIO_RESET_Z8F0811);
+}
+EXPORT_SYMBOL(cx18_reset_ir_gpio);
+/* This symbol is exported for use by lirc_pvr150 for the IR-blaster */
+
+/* Xceive tuner reset function */
+int cx18_reset_tuner_gpio(void *dev, int component, int cmd, int value)
+{
+ struct i2c_algo_bit_data *algo = dev;
+ struct cx18_i2c_algo_callback_data *cb_data = algo->data;
+ struct cx18 *cx = cb_data->cx;
+
+ if (cmd != XC2028_TUNER_RESET ||
+ cx->card->tuners[0].tuner != TUNER_XC2028)
+ return 0;
+
+ CX18_DEBUG_INFO("Resetting XCeive tuner\n");
+ return v4l2_subdev_call(&cx->sd_resetctrl,
+ core, reset, CX18_GPIO_RESET_XC2028);
+}
diff --git a/drivers/media/video/cx18/cx18-gpio.h b/drivers/media/video/cx18/cx18-gpio.h
new file mode 100644
index 00000000..4aea2ef8
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-gpio.h
@@ -0,0 +1,34 @@
+/*
+ * cx18 gpio functions
+ *
+ * Derived from ivtv-gpio.h
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+void cx18_gpio_init(struct cx18 *cx);
+int cx18_gpio_register(struct cx18 *cx, u32 hw);
+
+enum cx18_gpio_reset_type {
+ CX18_GPIO_RESET_I2C = 0,
+ CX18_GPIO_RESET_Z8F0811 = 1,
+ CX18_GPIO_RESET_XC2028 = 2,
+};
+
+void cx18_reset_ir_gpio(void *data);
+int cx18_reset_tuner_gpio(void *dev, int component, int cmd, int value);
diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c
new file mode 100644
index 00000000..51609d5c
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-i2c.c
@@ -0,0 +1,330 @@
+/*
+ * cx18 I2C functions
+ *
+ * Derived from ivtv-i2c.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-cards.h"
+#include "cx18-gpio.h"
+#include "cx18-i2c.h"
+#include "cx18-irq.h"
+
+#define CX18_REG_I2C_1_WR 0xf15000
+#define CX18_REG_I2C_1_RD 0xf15008
+#define CX18_REG_I2C_2_WR 0xf25100
+#define CX18_REG_I2C_2_RD 0xf25108
+
+#define SETSCL_BIT 0x0001
+#define SETSDL_BIT 0x0002
+#define GETSCL_BIT 0x0004
+#define GETSDL_BIT 0x0008
+
+#define CX18_CS5345_I2C_ADDR 0x4c
+#define CX18_Z8F0811_IR_TX_I2C_ADDR 0x70
+#define CX18_Z8F0811_IR_RX_I2C_ADDR 0x71
+
+/* This array should match the CX18_HW_ defines */
+static const u8 hw_addrs[] = {
+ 0, /* CX18_HW_TUNER */
+ 0, /* CX18_HW_TVEEPROM */
+ CX18_CS5345_I2C_ADDR, /* CX18_HW_CS5345 */
+ 0, /* CX18_HW_DVB */
+ 0, /* CX18_HW_418_AV */
+ 0, /* CX18_HW_GPIO_MUX */
+ 0, /* CX18_HW_GPIO_RESET_CTRL */
+ CX18_Z8F0811_IR_TX_I2C_ADDR, /* CX18_HW_Z8F0811_IR_TX_HAUP */
+ CX18_Z8F0811_IR_RX_I2C_ADDR, /* CX18_HW_Z8F0811_IR_RX_HAUP */
+};
+
+/* This array should match the CX18_HW_ defines */
+/* This might well become a card-specific array */
+static const u8 hw_bus[] = {
+ 1, /* CX18_HW_TUNER */
+ 0, /* CX18_HW_TVEEPROM */
+ 0, /* CX18_HW_CS5345 */
+ 0, /* CX18_HW_DVB */
+ 0, /* CX18_HW_418_AV */
+ 0, /* CX18_HW_GPIO_MUX */
+ 0, /* CX18_HW_GPIO_RESET_CTRL */
+ 0, /* CX18_HW_Z8F0811_IR_TX_HAUP */
+ 0, /* CX18_HW_Z8F0811_IR_RX_HAUP */
+};
+
+/* This array should match the CX18_HW_ defines */
+static const char * const hw_devicenames[] = {
+ "tuner",
+ "tveeprom",
+ "cs5345",
+ "cx23418_DTV",
+ "cx23418_AV",
+ "gpio_mux",
+ "gpio_reset_ctrl",
+ "ir_tx_z8f0811_haup",
+ "ir_rx_z8f0811_haup",
+};
+
+static int cx18_i2c_new_ir(struct cx18 *cx, struct i2c_adapter *adap, u32 hw,
+ const char *type, u8 addr)
+{
+ struct i2c_board_info info;
+ struct IR_i2c_init_data *init_data = &cx->ir_i2c_init_data;
+ unsigned short addr_list[2] = { addr, I2C_CLIENT_END };
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, type, I2C_NAME_SIZE);
+
+ /* Our default information for ir-kbd-i2c.c to use */
+ switch (hw) {
+ case CX18_HW_Z8F0811_IR_RX_HAUP:
+ init_data->ir_codes = RC_MAP_HAUPPAUGE;
+ init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR;
+ init_data->type = RC_TYPE_RC5;
+ init_data->name = cx->card_name;
+ info.platform_data = init_data;
+ break;
+ }
+
+ return i2c_new_probed_device(adap, &info, addr_list, NULL) == NULL ?
+ -1 : 0;
+}
+
+int cx18_i2c_register(struct cx18 *cx, unsigned idx)
+{
+ struct v4l2_subdev *sd;
+ int bus = hw_bus[idx];
+ struct i2c_adapter *adap = &cx->i2c_adap[bus];
+ const char *type = hw_devicenames[idx];
+ u32 hw = 1 << idx;
+
+ if (idx >= ARRAY_SIZE(hw_addrs))
+ return -1;
+
+ if (hw == CX18_HW_TUNER) {
+ /* special tuner group handling */
+ sd = v4l2_i2c_new_subdev(&cx->v4l2_dev,
+ adap, type, 0, cx->card_i2c->radio);
+ if (sd != NULL)
+ sd->grp_id = hw;
+ sd = v4l2_i2c_new_subdev(&cx->v4l2_dev,
+ adap, type, 0, cx->card_i2c->demod);
+ if (sd != NULL)
+ sd->grp_id = hw;
+ sd = v4l2_i2c_new_subdev(&cx->v4l2_dev,
+ adap, type, 0, cx->card_i2c->tv);
+ if (sd != NULL)
+ sd->grp_id = hw;
+ return sd != NULL ? 0 : -1;
+ }
+
+ if (hw & CX18_HW_IR_ANY)
+ return cx18_i2c_new_ir(cx, adap, hw, type, hw_addrs[idx]);
+
+ /* Is it not an I2C device or one we do not wish to register? */
+ if (!hw_addrs[idx])
+ return -1;
+
+ /* It's an I2C device other than an analog tuner or IR chip */
+ sd = v4l2_i2c_new_subdev(&cx->v4l2_dev, adap, type, hw_addrs[idx],
+ NULL);
+ if (sd != NULL)
+ sd->grp_id = hw;
+ return sd != NULL ? 0 : -1;
+}
+
+/* Find the first member of the subdev group id in hw */
+struct v4l2_subdev *cx18_find_hw(struct cx18 *cx, u32 hw)
+{
+ struct v4l2_subdev *result = NULL;
+ struct v4l2_subdev *sd;
+
+ spin_lock(&cx->v4l2_dev.lock);
+ v4l2_device_for_each_subdev(sd, &cx->v4l2_dev) {
+ if (sd->grp_id == hw) {
+ result = sd;
+ break;
+ }
+ }
+ spin_unlock(&cx->v4l2_dev.lock);
+ return result;
+}
+
+static void cx18_setscl(void *data, int state)
+{
+ struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
+ int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;
+ u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR;
+ u32 r = cx18_read_reg(cx, addr);
+
+ if (state)
+ cx18_write_reg(cx, r | SETSCL_BIT, addr);
+ else
+ cx18_write_reg(cx, r & ~SETSCL_BIT, addr);
+}
+
+static void cx18_setsda(void *data, int state)
+{
+ struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
+ int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;
+ u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR;
+ u32 r = cx18_read_reg(cx, addr);
+
+ if (state)
+ cx18_write_reg(cx, r | SETSDL_BIT, addr);
+ else
+ cx18_write_reg(cx, r & ~SETSDL_BIT, addr);
+}
+
+static int cx18_getscl(void *data)
+{
+ struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
+ int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;
+ u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD;
+
+ return cx18_read_reg(cx, addr) & GETSCL_BIT;
+}
+
+static int cx18_getsda(void *data)
+{
+ struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
+ int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;
+ u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD;
+
+ return cx18_read_reg(cx, addr) & GETSDL_BIT;
+}
+
+/* template for i2c-bit-algo */
+static struct i2c_adapter cx18_i2c_adap_template = {
+ .name = "cx18 i2c driver",
+ .algo = NULL, /* set by i2c-algo-bit */
+ .algo_data = NULL, /* filled from template */
+ .owner = THIS_MODULE,
+};
+
+#define CX18_SCL_PERIOD (10) /* usecs. 10 usec is period for a 100 KHz clock */
+#define CX18_ALGO_BIT_TIMEOUT (2) /* seconds */
+
+static struct i2c_algo_bit_data cx18_i2c_algo_template = {
+ .setsda = cx18_setsda,
+ .setscl = cx18_setscl,
+ .getsda = cx18_getsda,
+ .getscl = cx18_getscl,
+ .udelay = CX18_SCL_PERIOD/2, /* 1/2 clock period in usec*/
+ .timeout = CX18_ALGO_BIT_TIMEOUT*HZ /* jiffies */
+};
+
+/* init + register i2c adapter */
+int init_cx18_i2c(struct cx18 *cx)
+{
+ int i, err;
+ CX18_DEBUG_I2C("i2c init\n");
+
+ for (i = 0; i < 2; i++) {
+ /* Setup algorithm for adapter */
+ memcpy(&cx->i2c_algo[i], &cx18_i2c_algo_template,
+ sizeof(struct i2c_algo_bit_data));
+ cx->i2c_algo_cb_data[i].cx = cx;
+ cx->i2c_algo_cb_data[i].bus_index = i;
+ cx->i2c_algo[i].data = &cx->i2c_algo_cb_data[i];
+
+ /* Setup adapter */
+ memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template,
+ sizeof(struct i2c_adapter));
+ cx->i2c_adap[i].algo_data = &cx->i2c_algo[i];
+ sprintf(cx->i2c_adap[i].name + strlen(cx->i2c_adap[i].name),
+ " #%d-%d", cx->instance, i);
+ i2c_set_adapdata(&cx->i2c_adap[i], &cx->v4l2_dev);
+ cx->i2c_adap[i].dev.parent = &cx->pci_dev->dev;
+ }
+
+ if (cx18_read_reg(cx, CX18_REG_I2C_2_WR) != 0x0003c02f) {
+ /* Reset/Unreset I2C hardware block */
+ /* Clock select 220MHz */
+ cx18_write_reg_expect(cx, 0x10000000, 0xc71004,
+ 0x00000000, 0x10001000);
+ /* Clock Enable */
+ cx18_write_reg_expect(cx, 0x10001000, 0xc71024,
+ 0x00001000, 0x10001000);
+ }
+ /* courtesy of Steven Toth <stoth@hauppauge.com> */
+ cx18_write_reg_expect(cx, 0x00c00000, 0xc7001c, 0x00000000, 0x00c000c0);
+ mdelay(10);
+ cx18_write_reg_expect(cx, 0x00c000c0, 0xc7001c, 0x000000c0, 0x00c000c0);
+ mdelay(10);
+ cx18_write_reg_expect(cx, 0x00c00000, 0xc7001c, 0x00000000, 0x00c000c0);
+ mdelay(10);
+
+ /* Set to edge-triggered intrs. */
+ cx18_write_reg(cx, 0x00c00000, 0xc730c8);
+ /* Clear any stale intrs */
+ cx18_write_reg_expect(cx, HW2_I2C1_INT|HW2_I2C2_INT, HW2_INT_CLR_STATUS,
+ ~(HW2_I2C1_INT|HW2_I2C2_INT), HW2_I2C1_INT|HW2_I2C2_INT);
+
+ /* Hw I2C1 Clock Freq ~100kHz */
+ cx18_write_reg(cx, 0x00021c0f & ~4, CX18_REG_I2C_1_WR);
+ cx18_setscl(&cx->i2c_algo_cb_data[0], 1);
+ cx18_setsda(&cx->i2c_algo_cb_data[0], 1);
+
+ /* Hw I2C2 Clock Freq ~100kHz */
+ cx18_write_reg(cx, 0x00021c0f & ~4, CX18_REG_I2C_2_WR);
+ cx18_setscl(&cx->i2c_algo_cb_data[1], 1);
+ cx18_setsda(&cx->i2c_algo_cb_data[1], 1);
+
+ cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL,
+ core, reset, (u32) CX18_GPIO_RESET_I2C);
+
+ err = i2c_bit_add_bus(&cx->i2c_adap[0]);
+ if (err)
+ goto err;
+ err = i2c_bit_add_bus(&cx->i2c_adap[1]);
+ if (err)
+ goto err_del_bus_0;
+ return 0;
+
+ err_del_bus_0:
+ i2c_del_adapter(&cx->i2c_adap[0]);
+ err:
+ return err;
+}
+
+void exit_cx18_i2c(struct cx18 *cx)
+{
+ int i;
+ CX18_DEBUG_I2C("i2c exit\n");
+ cx18_write_reg(cx, cx18_read_reg(cx, CX18_REG_I2C_1_WR) | 4,
+ CX18_REG_I2C_1_WR);
+ cx18_write_reg(cx, cx18_read_reg(cx, CX18_REG_I2C_2_WR) | 4,
+ CX18_REG_I2C_2_WR);
+
+ for (i = 0; i < 2; i++) {
+ i2c_del_adapter(&cx->i2c_adap[i]);
+ }
+}
+
+/*
+ Hauppauge HVR1600 should have:
+ 32 cx24227
+ 98 unknown
+ a0 eeprom
+ c2 tuner
+ e? zilog ir
+ */
diff --git a/drivers/media/video/cx18/cx18-i2c.h b/drivers/media/video/cx18/cx18-i2c.h
new file mode 100644
index 00000000..1180fdc8
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-i2c.h
@@ -0,0 +1,29 @@
+/*
+ * cx18 I2C functions
+ *
+ * Derived from ivtv-i2c.h
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+int cx18_i2c_register(struct cx18 *cx, unsigned idx);
+struct v4l2_subdev *cx18_find_hw(struct cx18 *cx, u32 hw);
+
+/* init + register i2c adapter */
+int init_cx18_i2c(struct cx18 *cx);
+void exit_cx18_i2c(struct cx18 *cx);
diff --git a/drivers/media/video/cx18/cx18-io.c b/drivers/media/video/cx18/cx18-io.c
new file mode 100644
index 00000000..49b9dbd0
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-io.c
@@ -0,0 +1,97 @@
+/*
+ * cx18 driver PCI memory mapped IO access routines
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-irq.h"
+
+void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count)
+{
+ u8 __iomem *dst = addr;
+ u16 val2 = val | (val << 8);
+ u32 val4 = val2 | (val2 << 16);
+
+ /* Align writes on the CX23418's addresses */
+ if ((count > 0) && ((unsigned long)dst & 1)) {
+ cx18_writeb(cx, (u8) val, dst);
+ count--;
+ dst++;
+ }
+ if ((count > 1) && ((unsigned long)dst & 2)) {
+ cx18_writew(cx, val2, dst);
+ count -= 2;
+ dst += 2;
+ }
+ while (count > 3) {
+ cx18_writel(cx, val4, dst);
+ count -= 4;
+ dst += 4;
+ }
+ if (count > 1) {
+ cx18_writew(cx, val2, dst);
+ count -= 2;
+ dst += 2;
+ }
+ if (count > 0)
+ cx18_writeb(cx, (u8) val, dst);
+}
+
+void cx18_sw1_irq_enable(struct cx18 *cx, u32 val)
+{
+ cx18_write_reg_expect(cx, val, SW1_INT_STATUS, ~val, val);
+ cx->sw1_irq_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI) | val;
+ cx18_write_reg(cx, cx->sw1_irq_mask, SW1_INT_ENABLE_PCI);
+}
+
+void cx18_sw1_irq_disable(struct cx18 *cx, u32 val)
+{
+ cx->sw1_irq_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI) & ~val;
+ cx18_write_reg(cx, cx->sw1_irq_mask, SW1_INT_ENABLE_PCI);
+}
+
+void cx18_sw2_irq_enable(struct cx18 *cx, u32 val)
+{
+ cx18_write_reg_expect(cx, val, SW2_INT_STATUS, ~val, val);
+ cx->sw2_irq_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI) | val;
+ cx18_write_reg(cx, cx->sw2_irq_mask, SW2_INT_ENABLE_PCI);
+}
+
+void cx18_sw2_irq_disable(struct cx18 *cx, u32 val)
+{
+ cx->sw2_irq_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI) & ~val;
+ cx18_write_reg(cx, cx->sw2_irq_mask, SW2_INT_ENABLE_PCI);
+}
+
+void cx18_sw2_irq_disable_cpu(struct cx18 *cx, u32 val)
+{
+ u32 r;
+ r = cx18_read_reg(cx, SW2_INT_ENABLE_CPU);
+ cx18_write_reg(cx, r & ~val, SW2_INT_ENABLE_CPU);
+}
+
+void cx18_setup_page(struct cx18 *cx, u32 addr)
+{
+ u32 val;
+ val = cx18_read_reg(cx, 0xD000F8);
+ val = (val & ~0x1f00) | ((addr >> 17) & 0x1f00);
+ cx18_write_reg(cx, val, 0xD000F8);
+}
diff --git a/drivers/media/video/cx18/cx18-io.h b/drivers/media/video/cx18/cx18-io.h
new file mode 100644
index 00000000..18974d88
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-io.h
@@ -0,0 +1,191 @@
+/*
+ * cx18 driver PCI memory mapped IO access routines
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#ifndef CX18_IO_H
+#define CX18_IO_H
+
+#include "cx18-driver.h"
+
+/*
+ * Readback and retry of MMIO access for reliability:
+ * The concept was suggested by Steve Toth <stoth@linuxtv.org>.
+ * The implmentation is the fault of Andy Walls <awalls@md.metrocast.net>.
+ *
+ * *write* functions are implied to retry the mmio unless suffixed with _noretry
+ * *read* functions never retry the mmio (it never helps to do so)
+ */
+
+/* Non byteswapping memory mapped IO */
+static inline u32 cx18_raw_readl(struct cx18 *cx, const void __iomem *addr)
+{
+ return __raw_readl(addr);
+}
+
+static inline
+void cx18_raw_writel_noretry(struct cx18 *cx, u32 val, void __iomem *addr)
+{
+ __raw_writel(val, addr);
+}
+
+static inline void cx18_raw_writel(struct cx18 *cx, u32 val, void __iomem *addr)
+{
+ int i;
+ for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) {
+ cx18_raw_writel_noretry(cx, val, addr);
+ if (val == cx18_raw_readl(cx, addr))
+ break;
+ }
+}
+
+/* Normal memory mapped IO */
+static inline u32 cx18_readl(struct cx18 *cx, const void __iomem *addr)
+{
+ return readl(addr);
+}
+
+static inline
+void cx18_writel_noretry(struct cx18 *cx, u32 val, void __iomem *addr)
+{
+ writel(val, addr);
+}
+
+static inline void cx18_writel(struct cx18 *cx, u32 val, void __iomem *addr)
+{
+ int i;
+ for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) {
+ cx18_writel_noretry(cx, val, addr);
+ if (val == cx18_readl(cx, addr))
+ break;
+ }
+}
+
+static inline
+void cx18_writel_expect(struct cx18 *cx, u32 val, void __iomem *addr,
+ u32 eval, u32 mask)
+{
+ int i;
+ u32 r;
+ eval &= mask;
+ for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) {
+ cx18_writel_noretry(cx, val, addr);
+ r = cx18_readl(cx, addr);
+ if (r == 0xffffffff && eval != 0xffffffff)
+ continue;
+ if (eval == (r & mask))
+ break;
+ }
+}
+
+static inline u16 cx18_readw(struct cx18 *cx, const void __iomem *addr)
+{
+ return readw(addr);
+}
+
+static inline
+void cx18_writew_noretry(struct cx18 *cx, u16 val, void __iomem *addr)
+{
+ writew(val, addr);
+}
+
+static inline void cx18_writew(struct cx18 *cx, u16 val, void __iomem *addr)
+{
+ int i;
+ for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) {
+ cx18_writew_noretry(cx, val, addr);
+ if (val == cx18_readw(cx, addr))
+ break;
+ }
+}
+
+static inline u8 cx18_readb(struct cx18 *cx, const void __iomem *addr)
+{
+ return readb(addr);
+}
+
+static inline
+void cx18_writeb_noretry(struct cx18 *cx, u8 val, void __iomem *addr)
+{
+ writeb(val, addr);
+}
+
+static inline void cx18_writeb(struct cx18 *cx, u8 val, void __iomem *addr)
+{
+ int i;
+ for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) {
+ cx18_writeb_noretry(cx, val, addr);
+ if (val == cx18_readb(cx, addr))
+ break;
+ }
+}
+
+static inline
+void cx18_memcpy_fromio(struct cx18 *cx, void *to,
+ const void __iomem *from, unsigned int len)
+{
+ memcpy_fromio(to, from, len);
+}
+
+void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count);
+
+
+/* Access "register" region of CX23418 memory mapped I/O */
+static inline void cx18_write_reg_noretry(struct cx18 *cx, u32 val, u32 reg)
+{
+ cx18_writel_noretry(cx, val, cx->reg_mem + reg);
+}
+
+static inline void cx18_write_reg(struct cx18 *cx, u32 val, u32 reg)
+{
+ cx18_writel(cx, val, cx->reg_mem + reg);
+}
+
+static inline void cx18_write_reg_expect(struct cx18 *cx, u32 val, u32 reg,
+ u32 eval, u32 mask)
+{
+ cx18_writel_expect(cx, val, cx->reg_mem + reg, eval, mask);
+}
+
+static inline u32 cx18_read_reg(struct cx18 *cx, u32 reg)
+{
+ return cx18_readl(cx, cx->reg_mem + reg);
+}
+
+
+/* Access "encoder memory" region of CX23418 memory mapped I/O */
+static inline void cx18_write_enc(struct cx18 *cx, u32 val, u32 addr)
+{
+ cx18_writel(cx, val, cx->enc_mem + addr);
+}
+
+static inline u32 cx18_read_enc(struct cx18 *cx, u32 addr)
+{
+ return cx18_readl(cx, cx->enc_mem + addr);
+}
+
+void cx18_sw1_irq_enable(struct cx18 *cx, u32 val);
+void cx18_sw1_irq_disable(struct cx18 *cx, u32 val);
+void cx18_sw2_irq_enable(struct cx18 *cx, u32 val);
+void cx18_sw2_irq_disable(struct cx18 *cx, u32 val);
+void cx18_sw2_irq_disable_cpu(struct cx18 *cx, u32 val);
+void cx18_setup_page(struct cx18 *cx, u32 addr);
+
+#endif /* CX18_IO_H */
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c
new file mode 100644
index 00000000..be49f68d
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-ioctl.c
@@ -0,0 +1,1212 @@
+/*
+ * cx18 ioctl system call
+ *
+ * Derived from ivtv-ioctl.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-version.h"
+#include "cx18-mailbox.h"
+#include "cx18-i2c.h"
+#include "cx18-queue.h"
+#include "cx18-fileops.h"
+#include "cx18-vbi.h"
+#include "cx18-audio.h"
+#include "cx18-video.h"
+#include "cx18-streams.h"
+#include "cx18-ioctl.h"
+#include "cx18-gpio.h"
+#include "cx18-controls.h"
+#include "cx18-cards.h"
+#include "cx18-av-core.h"
+#include <media/tveeprom.h>
+#include <media/v4l2-chip-ident.h>
+
+u16 cx18_service2vbi(int type)
+{
+ switch (type) {
+ case V4L2_SLICED_TELETEXT_B:
+ return CX18_SLICED_TYPE_TELETEXT_B;
+ case V4L2_SLICED_CAPTION_525:
+ return CX18_SLICED_TYPE_CAPTION_525;
+ case V4L2_SLICED_WSS_625:
+ return CX18_SLICED_TYPE_WSS_625;
+ case V4L2_SLICED_VPS:
+ return CX18_SLICED_TYPE_VPS;
+ default:
+ return 0;
+ }
+}
+
+/* Check if VBI services are allowed on the (field, line) for the video std */
+static int valid_service_line(int field, int line, int is_pal)
+{
+ return (is_pal && line >= 6 &&
+ ((field == 0 && line <= 23) || (field == 1 && line <= 22))) ||
+ (!is_pal && line >= 10 && line < 22);
+}
+
+/*
+ * For a (field, line, std) and inbound potential set of services for that line,
+ * return the first valid service of those passed in the incoming set for that
+ * line in priority order:
+ * CC, VPS, or WSS over TELETEXT for well known lines
+ * TELETEXT, before VPS, before CC, before WSS, for other lines
+ */
+static u16 select_service_from_set(int field, int line, u16 set, int is_pal)
+{
+ u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525);
+ int i;
+
+ set = set & valid_set;
+ if (set == 0 || !valid_service_line(field, line, is_pal))
+ return 0;
+ if (!is_pal) {
+ if (line == 21 && (set & V4L2_SLICED_CAPTION_525))
+ return V4L2_SLICED_CAPTION_525;
+ } else {
+ if (line == 16 && field == 0 && (set & V4L2_SLICED_VPS))
+ return V4L2_SLICED_VPS;
+ if (line == 23 && field == 0 && (set & V4L2_SLICED_WSS_625))
+ return V4L2_SLICED_WSS_625;
+ if (line == 23)
+ return 0;
+ }
+ for (i = 0; i < 32; i++) {
+ if ((1 << i) & set)
+ return 1 << i;
+ }
+ return 0;
+}
+
+/*
+ * Expand the service_set of *fmt into valid service_lines for the std,
+ * and clear the passed in fmt->service_set
+ */
+void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
+{
+ u16 set = fmt->service_set;
+ int f, l;
+
+ fmt->service_set = 0;
+ for (f = 0; f < 2; f++) {
+ for (l = 0; l < 24; l++)
+ fmt->service_lines[f][l] = select_service_from_set(f, l, set, is_pal);
+ }
+}
+
+/*
+ * Sanitize the service_lines in *fmt per the video std, and return 1
+ * if any service_line is left as valid after santization
+ */
+static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
+{
+ int f, l;
+ u16 set = 0;
+
+ for (f = 0; f < 2; f++) {
+ for (l = 0; l < 24; l++) {
+ fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal);
+ set |= fmt->service_lines[f][l];
+ }
+ }
+ return set != 0;
+}
+
+/* Compute the service_set from the assumed valid service_lines of *fmt */
+u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt)
+{
+ int f, l;
+ u16 set = 0;
+
+ for (f = 0; f < 2; f++) {
+ for (l = 0; l < 24; l++)
+ set |= fmt->service_lines[f][l];
+ }
+ return set;
+}
+
+static int cx18_g_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+{
+ struct cx18_open_id *id = fh2id(fh);
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[id->type];
+ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+
+ pixfmt->width = cx->cxhdl.width;
+ pixfmt->height = cx->cxhdl.height;
+ pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ pixfmt->field = V4L2_FIELD_INTERLACED;
+ pixfmt->priv = 0;
+ if (id->type == CX18_ENC_STREAM_TYPE_YUV) {
+ pixfmt->pixelformat = s->pixelformat;
+ pixfmt->sizeimage = s->vb_bytes_per_frame;
+ pixfmt->bytesperline = 720;
+ } else {
+ pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;
+ pixfmt->sizeimage = 128 * 1024;
+ pixfmt->bytesperline = 0;
+ }
+ return 0;
+}
+
+static int cx18_g_fmt_vbi_cap(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+ struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi;
+
+ vbifmt->sampling_rate = 27000000;
+ vbifmt->offset = 248; /* FIXME - slightly wrong for both 50 & 60 Hz */
+ vbifmt->samples_per_line = vbi_active_samples - 4;
+ vbifmt->sample_format = V4L2_PIX_FMT_GREY;
+ vbifmt->start[0] = cx->vbi.start[0];
+ vbifmt->start[1] = cx->vbi.start[1];
+ vbifmt->count[0] = vbifmt->count[1] = cx->vbi.count;
+ vbifmt->flags = 0;
+ vbifmt->reserved[0] = 0;
+ vbifmt->reserved[1] = 0;
+ return 0;
+}
+
+static int cx18_g_fmt_sliced_vbi_cap(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+
+ /* sane, V4L2 spec compliant, defaults */
+ vbifmt->reserved[0] = 0;
+ vbifmt->reserved[1] = 0;
+ vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+ memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines));
+ vbifmt->service_set = 0;
+
+ /*
+ * Fetch the configured service_lines and total service_set from the
+ * digitizer/slicer. Note, cx18_av_vbi() wipes the passed in
+ * fmt->fmt.sliced under valid calling conditions
+ */
+ if (v4l2_subdev_call(cx->sd_av, vbi, g_sliced_fmt, &fmt->fmt.sliced))
+ return -EINVAL;
+
+ /* Ensure V4L2 spec compliant output */
+ vbifmt->reserved[0] = 0;
+ vbifmt->reserved[1] = 0;
+ vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+ vbifmt->service_set = cx18_get_service_set(vbifmt);
+ return 0;
+}
+
+static int cx18_try_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+{
+ struct cx18_open_id *id = fh2id(fh);
+ struct cx18 *cx = id->cx;
+ int w = fmt->fmt.pix.width;
+ int h = fmt->fmt.pix.height;
+ int min_h = 2;
+
+ w = min(w, 720);
+ w = max(w, 2);
+ if (id->type == CX18_ENC_STREAM_TYPE_YUV) {
+ /* YUV height must be a multiple of 32 */
+ h &= ~0x1f;
+ min_h = 32;
+ }
+ h = min(h, cx->is_50hz ? 576 : 480);
+ h = max(h, min_h);
+
+ fmt->fmt.pix.width = w;
+ fmt->fmt.pix.height = h;
+ return 0;
+}
+
+static int cx18_try_fmt_vbi_cap(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+{
+ return cx18_g_fmt_vbi_cap(file, fh, fmt);
+}
+
+static int cx18_try_fmt_sliced_vbi_cap(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+
+ vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+ vbifmt->reserved[0] = 0;
+ vbifmt->reserved[1] = 0;
+
+ /* If given a service set, expand it validly & clear passed in set */
+ if (vbifmt->service_set)
+ cx18_expand_service_set(vbifmt, cx->is_50hz);
+ /* Sanitize the service_lines, and compute the new set if any valid */
+ if (check_service_set(vbifmt, cx->is_50hz))
+ vbifmt->service_set = cx18_get_service_set(vbifmt);
+ return 0;
+}
+
+static int cx18_s_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+{
+ struct cx18_open_id *id = fh2id(fh);
+ struct cx18 *cx = id->cx;
+ struct v4l2_mbus_framefmt mbus_fmt;
+ struct cx18_stream *s = &cx->streams[id->type];
+ int ret;
+ int w, h;
+
+ ret = cx18_try_fmt_vid_cap(file, fh, fmt);
+ if (ret)
+ return ret;
+ w = fmt->fmt.pix.width;
+ h = fmt->fmt.pix.height;
+
+ if (cx->cxhdl.width == w && cx->cxhdl.height == h &&
+ s->pixelformat == fmt->fmt.pix.pixelformat)
+ return 0;
+
+ if (atomic_read(&cx->ana_capturing) > 0)
+ return -EBUSY;
+
+ s->pixelformat = fmt->fmt.pix.pixelformat;
+ /* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
+ UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */
+ if (s->pixelformat == V4L2_PIX_FMT_HM12)
+ s->vb_bytes_per_frame = h * 720 * 3 / 2;
+ else
+ s->vb_bytes_per_frame = h * 720 * 2;
+
+ mbus_fmt.width = cx->cxhdl.width = w;
+ mbus_fmt.height = cx->cxhdl.height = h;
+ mbus_fmt.code = V4L2_MBUS_FMT_FIXED;
+ v4l2_subdev_call(cx->sd_av, video, s_mbus_fmt, &mbus_fmt);
+ return cx18_g_fmt_vid_cap(file, fh, fmt);
+}
+
+static int cx18_s_fmt_vbi_cap(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+{
+ struct cx18_open_id *id = fh2id(fh);
+ struct cx18 *cx = id->cx;
+ int ret;
+
+ /*
+ * Changing the Encoder's Raw VBI parameters won't have any effect
+ * if any analog capture is ongoing
+ */
+ if (!cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0)
+ return -EBUSY;
+
+ /*
+ * Set the digitizer registers for raw active VBI.
+ * Note cx18_av_vbi_wipes out a lot of the passed in fmt under valid
+ * calling conditions
+ */
+ ret = v4l2_subdev_call(cx->sd_av, vbi, s_raw_fmt, &fmt->fmt.vbi);
+ if (ret)
+ return ret;
+
+ /* Store our new v4l2 (non-)sliced VBI state */
+ cx->vbi.sliced_in->service_set = 0;
+ cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
+
+ return cx18_g_fmt_vbi_cap(file, fh, fmt);
+}
+
+static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+{
+ struct cx18_open_id *id = fh2id(fh);
+ struct cx18 *cx = id->cx;
+ int ret;
+ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+
+ cx18_try_fmt_sliced_vbi_cap(file, fh, fmt);
+
+ /*
+ * Changing the Encoder's Raw VBI parameters won't have any effect
+ * if any analog capture is ongoing
+ */
+ if (cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0)
+ return -EBUSY;
+
+ /*
+ * Set the service_lines requested in the digitizer/slicer registers.
+ * Note, cx18_av_vbi() wipes some "impossible" service lines in the
+ * passed in fmt->fmt.sliced under valid calling conditions
+ */
+ ret = v4l2_subdev_call(cx->sd_av, vbi, s_sliced_fmt, &fmt->fmt.sliced);
+ if (ret)
+ return ret;
+ /* Store our current v4l2 sliced VBI settings */
+ cx->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
+ memcpy(cx->vbi.sliced_in, vbifmt, sizeof(*cx->vbi.sliced_in));
+ return 0;
+}
+
+static int cx18_g_chip_ident(struct file *file, void *fh,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+ int err = 0;
+
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+ switch (chip->match.type) {
+ case V4L2_CHIP_MATCH_HOST:
+ switch (chip->match.addr) {
+ case 0:
+ chip->ident = V4L2_IDENT_CX23418;
+ chip->revision = cx18_read_reg(cx, 0xC72028);
+ break;
+ case 1:
+ /*
+ * The A/V decoder is always present, but in the rare
+ * case that the card doesn't have analog, we don't
+ * use it. We find it w/o using the cx->sd_av pointer
+ */
+ cx18_call_hw(cx, CX18_HW_418_AV,
+ core, g_chip_ident, chip);
+ break;
+ default:
+ /*
+ * Could return ident = V4L2_IDENT_UNKNOWN if we had
+ * other host chips at higher addresses, but we don't
+ */
+ err = -EINVAL; /* per V4L2 spec */
+ break;
+ }
+ break;
+ case V4L2_CHIP_MATCH_I2C_DRIVER:
+ /* If needed, returns V4L2_IDENT_AMBIGUOUS without extra work */
+ cx18_call_all(cx, core, g_chip_ident, chip);
+ break;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ /*
+ * We could return V4L2_IDENT_UNKNOWN, but we don't do the work
+ * to look if a chip is at the address with no driver. That's a
+ * dangerous thing to do with EEPROMs anyway.
+ */
+ cx18_call_all(cx, core, g_chip_ident, chip);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ return err;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg)
+{
+ struct v4l2_dbg_register *regs = arg;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (regs->reg >= CX18_MEM_OFFSET + CX18_MEM_SIZE)
+ return -EINVAL;
+
+ regs->size = 4;
+ if (cmd == VIDIOC_DBG_S_REGISTER)
+ cx18_write_enc(cx, regs->val, regs->reg);
+ else
+ regs->val = cx18_read_enc(cx, regs->reg);
+ return 0;
+}
+
+static int cx18_g_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+
+ if (v4l2_chip_match_host(&reg->match))
+ return cx18_cxc(cx, VIDIOC_DBG_G_REGISTER, reg);
+ /* FIXME - errors shouldn't be ignored */
+ cx18_call_all(cx, core, g_register, reg);
+ return 0;
+}
+
+static int cx18_s_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+
+ if (v4l2_chip_match_host(&reg->match))
+ return cx18_cxc(cx, VIDIOC_DBG_S_REGISTER, reg);
+ /* FIXME - errors shouldn't be ignored */
+ cx18_call_all(cx, core, s_register, reg);
+ return 0;
+}
+#endif
+
+static int cx18_querycap(struct file *file, void *fh,
+ struct v4l2_capability *vcap)
+{
+ struct cx18_open_id *id = fh2id(fh);
+ struct cx18 *cx = id->cx;
+
+ strlcpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver));
+ strlcpy(vcap->card, cx->card_name, sizeof(vcap->card));
+ snprintf(vcap->bus_info, sizeof(vcap->bus_info),
+ "PCI:%s", pci_name(cx->pci_dev));
+ vcap->capabilities = cx->v4l2_cap; /* capabilities */
+ if (id->type == CX18_ENC_STREAM_TYPE_YUV)
+ vcap->capabilities |= V4L2_CAP_STREAMING;
+ return 0;
+}
+
+static int cx18_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+
+ return cx18_get_audio_input(cx, vin->index, vin);
+}
+
+static int cx18_g_audio(struct file *file, void *fh, struct v4l2_audio *vin)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+
+ vin->index = cx->audio_input;
+ return cx18_get_audio_input(cx, vin->index, vin);
+}
+
+static int cx18_s_audio(struct file *file, void *fh, struct v4l2_audio *vout)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+
+ if (vout->index >= cx->nof_audio_inputs)
+ return -EINVAL;
+ cx->audio_input = vout->index;
+ cx18_audio_set_io(cx);
+ return 0;
+}
+
+static int cx18_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+
+ /* set it to defaults from our table */
+ return cx18_get_input(cx, vin->index, vin);
+}
+
+static int cx18_cropcap(struct file *file, void *fh,
+ struct v4l2_cropcap *cropcap)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+
+ if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ cropcap->bounds.top = cropcap->bounds.left = 0;
+ cropcap->bounds.width = 720;
+ cropcap->bounds.height = cx->is_50hz ? 576 : 480;
+ cropcap->pixelaspect.numerator = cx->is_50hz ? 59 : 10;
+ cropcap->pixelaspect.denominator = cx->is_50hz ? 54 : 11;
+ cropcap->defrect = cropcap->bounds;
+ return 0;
+}
+
+static int cx18_s_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+{
+ struct cx18_open_id *id = fh2id(fh);
+ struct cx18 *cx = id->cx;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ CX18_DEBUG_WARN("VIDIOC_S_CROP not implemented\n");
+ return -EINVAL;
+}
+
+static int cx18_g_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ CX18_DEBUG_WARN("VIDIOC_G_CROP not implemented\n");
+ return -EINVAL;
+}
+
+static int cx18_enum_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_fmtdesc *fmt)
+{
+ static const struct v4l2_fmtdesc formats[] = {
+ { 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0,
+ "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12, { 0, 0, 0, 0 }
+ },
+ { 1, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FMT_FLAG_COMPRESSED,
+ "MPEG", V4L2_PIX_FMT_MPEG, { 0, 0, 0, 0 }
+ },
+ { 2, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0,
+ "UYVY 4:2:2", V4L2_PIX_FMT_UYVY, { 0, 0, 0, 0 }
+ },
+ };
+
+ if (fmt->index > ARRAY_SIZE(formats) - 1)
+ return -EINVAL;
+ *fmt = formats[fmt->index];
+ return 0;
+}
+
+static int cx18_g_input(struct file *file, void *fh, unsigned int *i)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+
+ *i = cx->active_input;
+ return 0;
+}
+
+int cx18_s_input(struct file *file, void *fh, unsigned int inp)
+{
+ struct cx18_open_id *id = fh2id(fh);
+ struct cx18 *cx = id->cx;
+
+ if (inp >= cx->nof_inputs)
+ return -EINVAL;
+
+ if (inp == cx->active_input) {
+ CX18_DEBUG_INFO("Input unchanged\n");
+ return 0;
+ }
+
+ CX18_DEBUG_INFO("Changing input from %d to %d\n",
+ cx->active_input, inp);
+
+ cx->active_input = inp;
+ /* Set the audio input to whatever is appropriate for the input type. */
+ cx->audio_input = cx->card->video_inputs[inp].audio_index;
+
+ /* prevent others from messing with the streams until
+ we're finished changing inputs. */
+ cx18_mute(cx);
+ cx18_video_set_io(cx);
+ cx18_audio_set_io(cx);
+ cx18_unmute(cx);
+ return 0;
+}
+
+static int cx18_g_frequency(struct file *file, void *fh,
+ struct v4l2_frequency *vf)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+
+ if (vf->tuner != 0)
+ return -EINVAL;
+
+ cx18_call_all(cx, tuner, g_frequency, vf);
+ return 0;
+}
+
+int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
+{
+ struct cx18_open_id *id = fh2id(fh);
+ struct cx18 *cx = id->cx;
+
+ if (vf->tuner != 0)
+ return -EINVAL;
+
+ cx18_mute(cx);
+ CX18_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency);
+ cx18_call_all(cx, tuner, s_frequency, vf);
+ cx18_unmute(cx);
+ return 0;
+}
+
+static int cx18_g_std(struct file *file, void *fh, v4l2_std_id *std)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+
+ *std = cx->std;
+ return 0;
+}
+
+int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std)
+{
+ struct cx18_open_id *id = fh2id(fh);
+ struct cx18 *cx = id->cx;
+
+ if ((*std & V4L2_STD_ALL) == 0)
+ return -EINVAL;
+
+ if (*std == cx->std)
+ return 0;
+
+ if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ||
+ atomic_read(&cx->ana_capturing) > 0) {
+ /* Switching standard would turn off the radio or mess
+ with already running streams, prevent that by
+ returning EBUSY. */
+ return -EBUSY;
+ }
+
+ cx->std = *std;
+ cx->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0;
+ cx->is_50hz = !cx->is_60hz;
+ cx2341x_handler_set_50hz(&cx->cxhdl, cx->is_50hz);
+ cx->cxhdl.width = 720;
+ cx->cxhdl.height = cx->is_50hz ? 576 : 480;
+ cx->vbi.count = cx->is_50hz ? 18 : 12;
+ cx->vbi.start[0] = cx->is_50hz ? 6 : 10;
+ cx->vbi.start[1] = cx->is_50hz ? 318 : 273;
+ CX18_DEBUG_INFO("Switching standard to %llx.\n",
+ (unsigned long long) cx->std);
+
+ /* Tuner */
+ cx18_call_all(cx, core, s_std, cx->std);
+ return 0;
+}
+
+static int cx18_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
+{
+ struct cx18_open_id *id = fh2id(fh);
+ struct cx18 *cx = id->cx;
+
+ if (vt->index != 0)
+ return -EINVAL;
+
+ cx18_call_all(cx, tuner, s_tuner, vt);
+ return 0;
+}
+
+static int cx18_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+
+ if (vt->index != 0)
+ return -EINVAL;
+
+ cx18_call_all(cx, tuner, g_tuner, vt);
+
+ if (vt->type == V4L2_TUNER_RADIO)
+ strlcpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name));
+ else
+ strlcpy(vt->name, "cx18 TV Tuner", sizeof(vt->name));
+ return 0;
+}
+
+static int cx18_g_sliced_vbi_cap(struct file *file, void *fh,
+ struct v4l2_sliced_vbi_cap *cap)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+ int set = cx->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525;
+ int f, l;
+
+ if (cap->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+ return -EINVAL;
+
+ cap->service_set = 0;
+ for (f = 0; f < 2; f++) {
+ for (l = 0; l < 24; l++) {
+ if (valid_service_line(f, l, cx->is_50hz)) {
+ /*
+ * We can find all v4l2 supported vbi services
+ * for the standard, on a valid line for the std
+ */
+ cap->service_lines[f][l] = set;
+ cap->service_set |= set;
+ } else
+ cap->service_lines[f][l] = 0;
+ }
+ }
+ for (f = 0; f < 3; f++)
+ cap->reserved[f] = 0;
+ return 0;
+}
+
+static int _cx18_process_idx_data(struct cx18_buffer *buf,
+ struct v4l2_enc_idx *idx)
+{
+ int consumed, remaining;
+ struct v4l2_enc_idx_entry *e_idx;
+ struct cx18_enc_idx_entry *e_buf;
+
+ /* Frame type lookup: 1=I, 2=P, 4=B */
+ const int mapping[8] = {
+ -1, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_P,
+ -1, V4L2_ENC_IDX_FRAME_B, -1, -1, -1
+ };
+
+ /*
+ * Assumption here is that a buf holds an integral number of
+ * struct cx18_enc_idx_entry objects and is properly aligned.
+ * This is enforced by the module options on IDX buffer sizes.
+ */
+ remaining = buf->bytesused - buf->readpos;
+ consumed = 0;
+ e_idx = &idx->entry[idx->entries];
+ e_buf = (struct cx18_enc_idx_entry *) &buf->buf[buf->readpos];
+
+ while (remaining >= sizeof(struct cx18_enc_idx_entry) &&
+ idx->entries < V4L2_ENC_IDX_ENTRIES) {
+
+ e_idx->offset = (((u64) le32_to_cpu(e_buf->offset_high)) << 32)
+ | le32_to_cpu(e_buf->offset_low);
+
+ e_idx->pts = (((u64) (le32_to_cpu(e_buf->pts_high) & 1)) << 32)
+ | le32_to_cpu(e_buf->pts_low);
+
+ e_idx->length = le32_to_cpu(e_buf->length);
+
+ e_idx->flags = mapping[le32_to_cpu(e_buf->flags) & 0x7];
+
+ e_idx->reserved[0] = 0;
+ e_idx->reserved[1] = 0;
+
+ idx->entries++;
+ e_idx = &idx->entry[idx->entries];
+ e_buf++;
+
+ remaining -= sizeof(struct cx18_enc_idx_entry);
+ consumed += sizeof(struct cx18_enc_idx_entry);
+ }
+
+ /* Swallow any partial entries at the end, if there are any */
+ if (remaining > 0 && remaining < sizeof(struct cx18_enc_idx_entry))
+ consumed += remaining;
+
+ buf->readpos += consumed;
+ return consumed;
+}
+
+static int cx18_process_idx_data(struct cx18_stream *s, struct cx18_mdl *mdl,
+ struct v4l2_enc_idx *idx)
+{
+ if (s->type != CX18_ENC_STREAM_TYPE_IDX)
+ return -EINVAL;
+
+ if (mdl->curr_buf == NULL)
+ mdl->curr_buf = list_first_entry(&mdl->buf_list,
+ struct cx18_buffer, list);
+
+ if (list_entry_is_past_end(mdl->curr_buf, &mdl->buf_list, list)) {
+ /*
+ * For some reason we've exhausted the buffers, but the MDL
+ * object still said some data was unread.
+ * Fix that and bail out.
+ */
+ mdl->readpos = mdl->bytesused;
+ return 0;
+ }
+
+ list_for_each_entry_from(mdl->curr_buf, &mdl->buf_list, list) {
+
+ /* Skip any empty buffers in the MDL */
+ if (mdl->curr_buf->readpos >= mdl->curr_buf->bytesused)
+ continue;
+
+ mdl->readpos += _cx18_process_idx_data(mdl->curr_buf, idx);
+
+ /* exit when MDL drained or request satisfied */
+ if (idx->entries >= V4L2_ENC_IDX_ENTRIES ||
+ mdl->curr_buf->readpos < mdl->curr_buf->bytesused ||
+ mdl->readpos >= mdl->bytesused)
+ break;
+ }
+ return 0;
+}
+
+static int cx18_g_enc_index(struct file *file, void *fh,
+ struct v4l2_enc_idx *idx)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+ struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
+ s32 tmp;
+ struct cx18_mdl *mdl;
+
+ if (!cx18_stream_enabled(s)) /* Module options inhibited IDX stream */
+ return -EINVAL;
+
+ /* Compute the best case number of entries we can buffer */
+ tmp = s->buffers -
+ s->bufs_per_mdl * CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN;
+ if (tmp <= 0)
+ tmp = 1;
+ tmp = tmp * s->buf_size / sizeof(struct cx18_enc_idx_entry);
+
+ /* Fill out the header of the return structure */
+ idx->entries = 0;
+ idx->entries_cap = tmp;
+ memset(idx->reserved, 0, sizeof(idx->reserved));
+
+ /* Pull IDX MDLs and buffers from q_full and populate the entries */
+ do {
+ mdl = cx18_dequeue(s, &s->q_full);
+ if (mdl == NULL) /* No more IDX data right now */
+ break;
+
+ /* Extract the Index entry data from the MDL and buffers */
+ cx18_process_idx_data(s, mdl, idx);
+ if (mdl->readpos < mdl->bytesused) {
+ /* We finished with data remaining, push the MDL back */
+ cx18_push(s, mdl, &s->q_full);
+ break;
+ }
+
+ /* We drained this MDL, schedule it to go to the firmware */
+ cx18_enqueue(s, mdl, &s->q_free);
+
+ } while (idx->entries < V4L2_ENC_IDX_ENTRIES);
+
+ /* Tell the work handler to send free IDX MDLs to the firmware */
+ cx18_stream_load_fw_queue(s);
+ return 0;
+}
+
+static struct videobuf_queue *cx18_vb_queue(struct cx18_open_id *id)
+{
+ struct videobuf_queue *q = NULL;
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[id->type];
+
+ switch (s->vb_type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ q = &s->vbuf_q;
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ break;
+ default:
+ break;
+ }
+ return q;
+}
+
+static int cx18_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct cx18_open_id *id = file->private_data;
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[id->type];
+
+ /* Start the hardware only if we're the video device */
+ if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
+ return -EINVAL;
+
+ if (id->type != CX18_ENC_STREAM_TYPE_YUV)
+ return -EINVAL;
+
+ /* Establish a buffer timeout */
+ mod_timer(&s->vb_timeout, msecs_to_jiffies(2000) + jiffies);
+
+ return videobuf_streamon(cx18_vb_queue(id));
+}
+
+static int cx18_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct cx18_open_id *id = file->private_data;
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[id->type];
+
+ /* Start the hardware only if we're the video device */
+ if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
+ return -EINVAL;
+
+ if (id->type != CX18_ENC_STREAM_TYPE_YUV)
+ return -EINVAL;
+
+ return videobuf_streamoff(cx18_vb_queue(id));
+}
+
+static int cx18_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *rb)
+{
+ struct cx18_open_id *id = file->private_data;
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[id->type];
+
+ if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
+ return -EINVAL;
+
+ return videobuf_reqbufs(cx18_vb_queue(id), rb);
+}
+
+static int cx18_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct cx18_open_id *id = file->private_data;
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[id->type];
+
+ if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
+ return -EINVAL;
+
+ return videobuf_querybuf(cx18_vb_queue(id), b);
+}
+
+static int cx18_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct cx18_open_id *id = file->private_data;
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[id->type];
+
+ if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
+ return -EINVAL;
+
+ return videobuf_qbuf(cx18_vb_queue(id), b);
+}
+
+static int cx18_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct cx18_open_id *id = file->private_data;
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[id->type];
+
+ if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
+ return -EINVAL;
+
+ return videobuf_dqbuf(cx18_vb_queue(id), b, file->f_flags & O_NONBLOCK);
+}
+
+static int cx18_encoder_cmd(struct file *file, void *fh,
+ struct v4l2_encoder_cmd *enc)
+{
+ struct cx18_open_id *id = fh2id(fh);
+ struct cx18 *cx = id->cx;
+ u32 h;
+
+ switch (enc->cmd) {
+ case V4L2_ENC_CMD_START:
+ CX18_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");
+ enc->flags = 0;
+ return cx18_start_capture(id);
+
+ case V4L2_ENC_CMD_STOP:
+ CX18_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");
+ enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
+ cx18_stop_capture(id,
+ enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END);
+ break;
+
+ case V4L2_ENC_CMD_PAUSE:
+ CX18_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");
+ enc->flags = 0;
+ if (!atomic_read(&cx->ana_capturing))
+ return -EPERM;
+ if (test_and_set_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
+ return 0;
+ h = cx18_find_handle(cx);
+ if (h == CX18_INVALID_TASK_HANDLE) {
+ CX18_ERR("Can't find valid task handle for "
+ "V4L2_ENC_CMD_PAUSE\n");
+ return -EBADFD;
+ }
+ cx18_mute(cx);
+ cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, h);
+ break;
+
+ case V4L2_ENC_CMD_RESUME:
+ CX18_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");
+ enc->flags = 0;
+ if (!atomic_read(&cx->ana_capturing))
+ return -EPERM;
+ if (!test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
+ return 0;
+ h = cx18_find_handle(cx);
+ if (h == CX18_INVALID_TASK_HANDLE) {
+ CX18_ERR("Can't find valid task handle for "
+ "V4L2_ENC_CMD_RESUME\n");
+ return -EBADFD;
+ }
+ cx18_vapi(cx, CX18_CPU_CAPTURE_RESUME, 1, h);
+ cx18_unmute(cx);
+ break;
+
+ default:
+ CX18_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cx18_try_encoder_cmd(struct file *file, void *fh,
+ struct v4l2_encoder_cmd *enc)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+
+ switch (enc->cmd) {
+ case V4L2_ENC_CMD_START:
+ CX18_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");
+ enc->flags = 0;
+ break;
+
+ case V4L2_ENC_CMD_STOP:
+ CX18_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");
+ enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
+ break;
+
+ case V4L2_ENC_CMD_PAUSE:
+ CX18_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");
+ enc->flags = 0;
+ break;
+
+ case V4L2_ENC_CMD_RESUME:
+ CX18_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");
+ enc->flags = 0;
+ break;
+
+ default:
+ CX18_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cx18_log_status(struct file *file, void *fh)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+ struct v4l2_input vidin;
+ struct v4l2_audio audin;
+ int i;
+
+ CX18_INFO("Version: %s Card: %s\n", CX18_VERSION, cx->card_name);
+ if (cx->hw_flags & CX18_HW_TVEEPROM) {
+ struct tveeprom tv;
+
+ cx18_read_eeprom(cx, &tv);
+ }
+ cx18_call_all(cx, core, log_status);
+ cx18_get_input(cx, cx->active_input, &vidin);
+ cx18_get_audio_input(cx, cx->audio_input, &audin);
+ CX18_INFO("Video Input: %s\n", vidin.name);
+ CX18_INFO("Audio Input: %s\n", audin.name);
+ mutex_lock(&cx->gpio_lock);
+ CX18_INFO("GPIO: direction 0x%08x, value 0x%08x\n",
+ cx->gpio_dir, cx->gpio_val);
+ mutex_unlock(&cx->gpio_lock);
+ CX18_INFO("Tuner: %s\n",
+ test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ? "Radio" : "TV");
+ v4l2_ctrl_handler_log_status(&cx->cxhdl.hdl, cx->v4l2_dev.name);
+ CX18_INFO("Status flags: 0x%08lx\n", cx->i_flags);
+ for (i = 0; i < CX18_MAX_STREAMS; i++) {
+ struct cx18_stream *s = &cx->streams[i];
+
+ if (s->video_dev == NULL || s->buffers == 0)
+ continue;
+ CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n",
+ s->name, s->s_flags,
+ atomic_read(&s->q_full.depth) * s->bufs_per_mdl * 100
+ / s->buffers,
+ (s->buffers * s->buf_size) / 1024, s->buffers);
+ }
+ CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n",
+ (long long)cx->mpg_data_received,
+ (long long)cx->vbi_data_inserted);
+ return 0;
+}
+
+static long cx18_default(struct file *file, void *fh, bool valid_prio,
+ int cmd, void *arg)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+
+ switch (cmd) {
+ case VIDIOC_INT_RESET: {
+ u32 val = *(u32 *)arg;
+
+ if ((val == 0) || (val & 0x01))
+ cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL, core, reset,
+ (u32) CX18_GPIO_RESET_Z8F0811);
+ break;
+ }
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+long cx18_v4l2_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct video_device *vfd = video_devdata(filp);
+ struct cx18_open_id *id = file2id(filp);
+ struct cx18 *cx = id->cx;
+ long res;
+
+ mutex_lock(&cx->serialize_lock);
+
+ if (cx18_debug & CX18_DBGFLG_IOCTL)
+ vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
+ res = video_ioctl2(filp, cmd, arg);
+ vfd->debug = 0;
+ mutex_unlock(&cx->serialize_lock);
+ return res;
+}
+
+static const struct v4l2_ioctl_ops cx18_ioctl_ops = {
+ .vidioc_querycap = cx18_querycap,
+ .vidioc_s_audio = cx18_s_audio,
+ .vidioc_g_audio = cx18_g_audio,
+ .vidioc_enumaudio = cx18_enumaudio,
+ .vidioc_enum_input = cx18_enum_input,
+ .vidioc_cropcap = cx18_cropcap,
+ .vidioc_s_crop = cx18_s_crop,
+ .vidioc_g_crop = cx18_g_crop,
+ .vidioc_g_input = cx18_g_input,
+ .vidioc_s_input = cx18_s_input,
+ .vidioc_g_frequency = cx18_g_frequency,
+ .vidioc_s_frequency = cx18_s_frequency,
+ .vidioc_s_tuner = cx18_s_tuner,
+ .vidioc_g_tuner = cx18_g_tuner,
+ .vidioc_g_enc_index = cx18_g_enc_index,
+ .vidioc_g_std = cx18_g_std,
+ .vidioc_s_std = cx18_s_std,
+ .vidioc_log_status = cx18_log_status,
+ .vidioc_enum_fmt_vid_cap = cx18_enum_fmt_vid_cap,
+ .vidioc_encoder_cmd = cx18_encoder_cmd,
+ .vidioc_try_encoder_cmd = cx18_try_encoder_cmd,
+ .vidioc_g_fmt_vid_cap = cx18_g_fmt_vid_cap,
+ .vidioc_g_fmt_vbi_cap = cx18_g_fmt_vbi_cap,
+ .vidioc_g_fmt_sliced_vbi_cap = cx18_g_fmt_sliced_vbi_cap,
+ .vidioc_s_fmt_vid_cap = cx18_s_fmt_vid_cap,
+ .vidioc_s_fmt_vbi_cap = cx18_s_fmt_vbi_cap,
+ .vidioc_s_fmt_sliced_vbi_cap = cx18_s_fmt_sliced_vbi_cap,
+ .vidioc_try_fmt_vid_cap = cx18_try_fmt_vid_cap,
+ .vidioc_try_fmt_vbi_cap = cx18_try_fmt_vbi_cap,
+ .vidioc_try_fmt_sliced_vbi_cap = cx18_try_fmt_sliced_vbi_cap,
+ .vidioc_g_sliced_vbi_cap = cx18_g_sliced_vbi_cap,
+ .vidioc_g_chip_ident = cx18_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = cx18_g_register,
+ .vidioc_s_register = cx18_s_register,
+#endif
+ .vidioc_default = cx18_default,
+ .vidioc_streamon = cx18_streamon,
+ .vidioc_streamoff = cx18_streamoff,
+ .vidioc_reqbufs = cx18_reqbufs,
+ .vidioc_querybuf = cx18_querybuf,
+ .vidioc_qbuf = cx18_qbuf,
+ .vidioc_dqbuf = cx18_dqbuf,
+};
+
+void cx18_set_funcs(struct video_device *vdev)
+{
+ vdev->ioctl_ops = &cx18_ioctl_ops;
+}
diff --git a/drivers/media/video/cx18/cx18-ioctl.h b/drivers/media/video/cx18/cx18-ioctl.h
new file mode 100644
index 00000000..dcb2559a
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-ioctl.h
@@ -0,0 +1,33 @@
+/*
+ * cx18 ioctl system call
+ *
+ * Derived from ivtv-ioctl.h
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+u16 cx18_service2vbi(int type);
+void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal);
+u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt);
+void cx18_set_funcs(struct video_device *vdev);
+int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std);
+int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf);
+int cx18_s_input(struct file *file, void *fh, unsigned int inp);
+long cx18_v4l2_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg);
diff --git a/drivers/media/video/cx18/cx18-irq.c b/drivers/media/video/cx18/cx18-irq.c
new file mode 100644
index 00000000..80edfe93
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-irq.c
@@ -0,0 +1,81 @@
+/*
+ * cx18 interrupt handling
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-irq.h"
+#include "cx18-mailbox.h"
+#include "cx18-scb.h"
+
+static void xpu_ack(struct cx18 *cx, u32 sw2)
+{
+ if (sw2 & IRQ_CPU_TO_EPU_ACK)
+ wake_up(&cx->mb_cpu_waitq);
+ if (sw2 & IRQ_APU_TO_EPU_ACK)
+ wake_up(&cx->mb_apu_waitq);
+}
+
+static void epu_cmd(struct cx18 *cx, u32 sw1)
+{
+ if (sw1 & IRQ_CPU_TO_EPU)
+ cx18_api_epu_cmd_irq(cx, CPU);
+ if (sw1 & IRQ_APU_TO_EPU)
+ cx18_api_epu_cmd_irq(cx, APU);
+}
+
+irqreturn_t cx18_irq_handler(int irq, void *dev_id)
+{
+ struct cx18 *cx = (struct cx18 *)dev_id;
+ u32 sw1, sw2, hw2;
+
+ sw1 = cx18_read_reg(cx, SW1_INT_STATUS) & cx->sw1_irq_mask;
+ sw2 = cx18_read_reg(cx, SW2_INT_STATUS) & cx->sw2_irq_mask;
+ hw2 = cx18_read_reg(cx, HW2_INT_CLR_STATUS) & cx->hw2_irq_mask;
+
+ if (sw1)
+ cx18_write_reg_expect(cx, sw1, SW1_INT_STATUS, ~sw1, sw1);
+ if (sw2)
+ cx18_write_reg_expect(cx, sw2, SW2_INT_STATUS, ~sw2, sw2);
+ if (hw2)
+ cx18_write_reg_expect(cx, hw2, HW2_INT_CLR_STATUS, ~hw2, hw2);
+
+ if (sw1 || sw2 || hw2)
+ CX18_DEBUG_HI_IRQ("received interrupts "
+ "SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2);
+
+ /*
+ * SW1 responses have to happen first. The sending XPU times out the
+ * incoming mailboxes on us rather rapidly.
+ */
+ if (sw1)
+ epu_cmd(cx, sw1);
+
+ /* To do: interrupt-based I2C handling
+ if (hw2 & (HW2_I2C1_INT|HW2_I2C2_INT)) {
+ }
+ */
+
+ if (sw2)
+ xpu_ack(cx, sw2);
+
+ return (sw1 || sw2 || hw2) ? IRQ_HANDLED : IRQ_NONE;
+}
diff --git a/drivers/media/video/cx18/cx18-irq.h b/drivers/media/video/cx18/cx18-irq.h
new file mode 100644
index 00000000..30e7eaf8
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-irq.h
@@ -0,0 +1,35 @@
+/*
+ * cx18 interrupt handling
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#define HW2_I2C1_INT (1 << 22)
+#define HW2_I2C2_INT (1 << 23)
+#define HW2_INT_CLR_STATUS 0xc730c4
+#define HW2_INT_MASK5_PCI 0xc730e4
+#define SW1_INT_SET 0xc73100
+#define SW1_INT_STATUS 0xc73104
+#define SW1_INT_ENABLE_PCI 0xc7311c
+#define SW2_INT_SET 0xc73140
+#define SW2_INT_STATUS 0xc73144
+#define SW2_INT_ENABLE_CPU 0xc73158
+#define SW2_INT_ENABLE_PCI 0xc7315c
+
+irqreturn_t cx18_irq_handler(int irq, void *dev_id);
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c
new file mode 100644
index 00000000..0c7796e7
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-mailbox.c
@@ -0,0 +1,869 @@
+/*
+ * cx18 mailbox functions
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include <stdarg.h>
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-scb.h"
+#include "cx18-irq.h"
+#include "cx18-mailbox.h"
+#include "cx18-queue.h"
+#include "cx18-streams.h"
+#include "cx18-alsa-pcm.h" /* FIXME make configurable */
+
+static const char *rpu_str[] = { "APU", "CPU", "EPU", "HPU" };
+
+#define API_FAST (1 << 2) /* Short timeout */
+#define API_SLOW (1 << 3) /* Additional 300ms timeout */
+
+struct cx18_api_info {
+ u32 cmd;
+ u8 flags; /* Flags, see above */
+ u8 rpu; /* Processing unit */
+ const char *name; /* The name of the command */
+};
+
+#define API_ENTRY(rpu, x, f) { (x), (f), (rpu), #x }
+
+static const struct cx18_api_info api_info[] = {
+ /* MPEG encoder API */
+ API_ENTRY(CPU, CX18_CPU_SET_CHANNEL_TYPE, 0),
+ API_ENTRY(CPU, CX18_EPU_DEBUG, 0),
+ API_ENTRY(CPU, CX18_CREATE_TASK, 0),
+ API_ENTRY(CPU, CX18_DESTROY_TASK, 0),
+ API_ENTRY(CPU, CX18_CPU_CAPTURE_START, API_SLOW),
+ API_ENTRY(CPU, CX18_CPU_CAPTURE_STOP, API_SLOW),
+ API_ENTRY(CPU, CX18_CPU_CAPTURE_PAUSE, 0),
+ API_ENTRY(CPU, CX18_CPU_CAPTURE_RESUME, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_CHANNEL_TYPE, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_STREAM_OUTPUT_TYPE, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_VIDEO_IN, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_VIDEO_RATE, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_VIDEO_RESOLUTION, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_FILTER_PARAM, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_SPATIAL_FILTER_TYPE, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_MEDIAN_CORING, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_INDEXTABLE, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_AUDIO_PARAMETERS, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_VIDEO_MUTE, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_AUDIO_MUTE, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_MISC_PARAMETERS, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_RAW_VBI_PARAM, API_SLOW),
+ API_ENTRY(CPU, CX18_CPU_SET_CAPTURE_LINE_NO, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_COPYRIGHT, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_AUDIO_PID, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_VIDEO_PID, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_VER_CROP_LINE, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_GOP_STRUCTURE, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_SCENE_CHANGE_DETECTION, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_ASPECT_RATIO, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_SKIP_INPUT_FRAME, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_SLICED_VBI_PARAM, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_USERDATA_PLACE_HOLDER, 0),
+ API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_VFC_PARAM, 0),
+ API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0),
+ API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST),
+ API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL, API_SLOW),
+ API_ENTRY(APU, CX18_APU_START, 0),
+ API_ENTRY(APU, CX18_APU_STOP, 0),
+ API_ENTRY(APU, CX18_APU_RESETAI, 0),
+ API_ENTRY(CPU, CX18_CPU_DEBUG_PEEK32, 0),
+ API_ENTRY(0, 0, 0),
+};
+
+static const struct cx18_api_info *find_api_info(u32 cmd)
+{
+ int i;
+
+ for (i = 0; api_info[i].cmd; i++)
+ if (api_info[i].cmd == cmd)
+ return &api_info[i];
+ return NULL;
+}
+
+/* Call with buf of n*11+1 bytes */
+static char *u32arr2hex(u32 data[], int n, char *buf)
+{
+ char *p;
+ int i;
+
+ for (i = 0, p = buf; i < n; i++, p += 11) {
+ /* kernel snprintf() appends '\0' always */
+ snprintf(p, 12, " %#010x", data[i]);
+ }
+ *p = '\0';
+ return buf;
+}
+
+static void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name)
+{
+ char argstr[MAX_MB_ARGUMENTS*11+1];
+
+ if (!(cx18_debug & CX18_DBGFLG_API))
+ return;
+
+ CX18_DEBUG_API("%s: req %#010x ack %#010x cmd %#010x err %#010x args%s"
+ "\n", name, mb->request, mb->ack, mb->cmd, mb->error,
+ u32arr2hex(mb->args, MAX_MB_ARGUMENTS, argstr));
+}
+
+
+/*
+ * Functions that run in a work_queue work handling context
+ */
+
+static void cx18_mdl_send_to_dvb(struct cx18_stream *s, struct cx18_mdl *mdl)
+{
+ struct cx18_buffer *buf;
+
+ if (s->dvb == NULL || !s->dvb->enabled || mdl->bytesused == 0)
+ return;
+
+ /* We ignore mdl and buf readpos accounting here - it doesn't matter */
+
+ /* The likely case */
+ if (list_is_singular(&mdl->buf_list)) {
+ buf = list_first_entry(&mdl->buf_list, struct cx18_buffer,
+ list);
+ if (buf->bytesused)
+ dvb_dmx_swfilter(&s->dvb->demux,
+ buf->buf, buf->bytesused);
+ return;
+ }
+
+ list_for_each_entry(buf, &mdl->buf_list, list) {
+ if (buf->bytesused == 0)
+ break;
+ dvb_dmx_swfilter(&s->dvb->demux, buf->buf, buf->bytesused);
+ }
+}
+
+static void cx18_mdl_send_to_videobuf(struct cx18_stream *s,
+ struct cx18_mdl *mdl)
+{
+ struct cx18_videobuf_buffer *vb_buf;
+ struct cx18_buffer *buf;
+ u8 *p;
+ u32 offset = 0;
+ int dispatch = 0;
+
+ if (mdl->bytesused == 0)
+ return;
+
+ /* Acquire a videobuf buffer, clone to and and release it */
+ spin_lock(&s->vb_lock);
+ if (list_empty(&s->vb_capture))
+ goto out;
+
+ vb_buf = list_first_entry(&s->vb_capture, struct cx18_videobuf_buffer,
+ vb.queue);
+
+ p = videobuf_to_vmalloc(&vb_buf->vb);
+ if (!p)
+ goto out;
+
+ offset = vb_buf->bytes_used;
+ list_for_each_entry(buf, &mdl->buf_list, list) {
+ if (buf->bytesused == 0)
+ break;
+
+ if ((offset + buf->bytesused) <= vb_buf->vb.bsize) {
+ memcpy(p + offset, buf->buf, buf->bytesused);
+ offset += buf->bytesused;
+ vb_buf->bytes_used += buf->bytesused;
+ }
+ }
+
+ /* If we've filled the buffer as per the callers res then dispatch it */
+ if (vb_buf->bytes_used >= s->vb_bytes_per_frame) {
+ dispatch = 1;
+ vb_buf->bytes_used = 0;
+ }
+
+ if (dispatch) {
+ vb_buf->vb.ts = ktime_to_timeval(ktime_get());
+ list_del(&vb_buf->vb.queue);
+ vb_buf->vb.state = VIDEOBUF_DONE;
+ wake_up(&vb_buf->vb.done);
+ }
+
+ mod_timer(&s->vb_timeout, msecs_to_jiffies(2000) + jiffies);
+
+out:
+ spin_unlock(&s->vb_lock);
+}
+
+static void cx18_mdl_send_to_alsa(struct cx18 *cx, struct cx18_stream *s,
+ struct cx18_mdl *mdl)
+{
+ struct cx18_buffer *buf;
+
+ if (mdl->bytesused == 0)
+ return;
+
+ /* We ignore mdl and buf readpos accounting here - it doesn't matter */
+
+ /* The likely case */
+ if (list_is_singular(&mdl->buf_list)) {
+ buf = list_first_entry(&mdl->buf_list, struct cx18_buffer,
+ list);
+ if (buf->bytesused)
+ cx->pcm_announce_callback(cx->alsa, buf->buf,
+ buf->bytesused);
+ return;
+ }
+
+ list_for_each_entry(buf, &mdl->buf_list, list) {
+ if (buf->bytesused == 0)
+ break;
+ cx->pcm_announce_callback(cx->alsa, buf->buf, buf->bytesused);
+ }
+}
+
+static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order)
+{
+ u32 handle, mdl_ack_count, id;
+ struct cx18_mailbox *mb;
+ struct cx18_mdl_ack *mdl_ack;
+ struct cx18_stream *s;
+ struct cx18_mdl *mdl;
+ int i;
+
+ mb = &order->mb;
+ handle = mb->args[0];
+ s = cx18_handle_to_stream(cx, handle);
+
+ if (s == NULL) {
+ CX18_WARN("Got DMA done notification for unknown/inactive"
+ " handle %d, %s mailbox seq no %d\n", handle,
+ (order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) ?
+ "stale" : "good", mb->request);
+ return;
+ }
+
+ mdl_ack_count = mb->args[2];
+ mdl_ack = order->mdl_ack;
+ for (i = 0; i < mdl_ack_count; i++, mdl_ack++) {
+ id = mdl_ack->id;
+ /*
+ * Simple integrity check for processing a stale (and possibly
+ * inconsistent mailbox): make sure the MDL id is in the
+ * valid range for the stream.
+ *
+ * We go through the trouble of dealing with stale mailboxes
+ * because most of the time, the mailbox data is still valid and
+ * unchanged (and in practice the firmware ping-pongs the
+ * two mdl_ack buffers so mdl_acks are not stale).
+ *
+ * There are occasions when we get a half changed mailbox,
+ * which this check catches for a handle & id mismatch. If the
+ * handle and id do correspond, the worst case is that we
+ * completely lost the old MDL, but pick up the new MDL
+ * early (but the new mdl_ack is guaranteed to be good in this
+ * case as the firmware wouldn't point us to a new mdl_ack until
+ * it's filled in).
+ *
+ * cx18_queue_get_mdl() will detect the lost MDLs
+ * and send them back to q_free for fw rotation eventually.
+ */
+ if ((order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) &&
+ !(id >= s->mdl_base_idx &&
+ id < (s->mdl_base_idx + s->buffers))) {
+ CX18_WARN("Fell behind! Ignoring stale mailbox with "
+ " inconsistent data. Lost MDL for mailbox "
+ "seq no %d\n", mb->request);
+ break;
+ }
+ mdl = cx18_queue_get_mdl(s, id, mdl_ack->data_used);
+
+ CX18_DEBUG_HI_DMA("DMA DONE for %s (MDL %d)\n", s->name, id);
+ if (mdl == NULL) {
+ CX18_WARN("Could not find MDL %d for stream %s\n",
+ id, s->name);
+ continue;
+ }
+
+ CX18_DEBUG_HI_DMA("%s recv bytesused = %d\n",
+ s->name, mdl->bytesused);
+
+ if (s->type == CX18_ENC_STREAM_TYPE_TS) {
+ cx18_mdl_send_to_dvb(s, mdl);
+ cx18_enqueue(s, mdl, &s->q_free);
+ } else if (s->type == CX18_ENC_STREAM_TYPE_PCM) {
+ /* Pass the data to cx18-alsa */
+ if (cx->pcm_announce_callback != NULL) {
+ cx18_mdl_send_to_alsa(cx, s, mdl);
+ cx18_enqueue(s, mdl, &s->q_free);
+ } else {
+ cx18_enqueue(s, mdl, &s->q_full);
+ }
+ } else if (s->type == CX18_ENC_STREAM_TYPE_YUV) {
+ cx18_mdl_send_to_videobuf(s, mdl);
+ cx18_enqueue(s, mdl, &s->q_free);
+ } else {
+ cx18_enqueue(s, mdl, &s->q_full);
+ if (s->type == CX18_ENC_STREAM_TYPE_IDX)
+ cx18_stream_rotate_idx_mdls(cx);
+ }
+ }
+ /* Put as many MDLs as possible back into fw use */
+ cx18_stream_load_fw_queue(s);
+
+ wake_up(&cx->dma_waitq);
+ if (s->id != -1)
+ wake_up(&s->waitq);
+}
+
+static void epu_debug(struct cx18 *cx, struct cx18_in_work_order *order)
+{
+ char *p;
+ char *str = order->str;
+
+ CX18_DEBUG_INFO("%x %s\n", order->mb.args[0], str);
+ p = strchr(str, '.');
+ if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags) && p && p > str)
+ CX18_INFO("FW version: %s\n", p - 1);
+}
+
+static void epu_cmd(struct cx18 *cx, struct cx18_in_work_order *order)
+{
+ switch (order->rpu) {
+ case CPU:
+ {
+ switch (order->mb.cmd) {
+ case CX18_EPU_DMA_DONE:
+ epu_dma_done(cx, order);
+ break;
+ case CX18_EPU_DEBUG:
+ epu_debug(cx, order);
+ break;
+ default:
+ CX18_WARN("Unknown CPU to EPU mailbox command %#0x\n",
+ order->mb.cmd);
+ break;
+ }
+ break;
+ }
+ case APU:
+ CX18_WARN("Unknown APU to EPU mailbox command %#0x\n",
+ order->mb.cmd);
+ break;
+ default:
+ break;
+ }
+}
+
+static
+void free_in_work_order(struct cx18 *cx, struct cx18_in_work_order *order)
+{
+ atomic_set(&order->pending, 0);
+}
+
+void cx18_in_work_handler(struct work_struct *work)
+{
+ struct cx18_in_work_order *order =
+ container_of(work, struct cx18_in_work_order, work);
+ struct cx18 *cx = order->cx;
+ epu_cmd(cx, order);
+ free_in_work_order(cx, order);
+}
+
+
+/*
+ * Functions that run in an interrupt handling context
+ */
+
+static void mb_ack_irq(struct cx18 *cx, struct cx18_in_work_order *order)
+{
+ struct cx18_mailbox __iomem *ack_mb;
+ u32 ack_irq, req;
+
+ switch (order->rpu) {
+ case APU:
+ ack_irq = IRQ_EPU_TO_APU_ACK;
+ ack_mb = &cx->scb->apu2epu_mb;
+ break;
+ case CPU:
+ ack_irq = IRQ_EPU_TO_CPU_ACK;
+ ack_mb = &cx->scb->cpu2epu_mb;
+ break;
+ default:
+ CX18_WARN("Unhandled RPU (%d) for command %x ack\n",
+ order->rpu, order->mb.cmd);
+ return;
+ }
+
+ req = order->mb.request;
+ /* Don't ack if the RPU has gotten impatient and timed us out */
+ if (req != cx18_readl(cx, &ack_mb->request) ||
+ req == cx18_readl(cx, &ack_mb->ack)) {
+ CX18_DEBUG_WARN("Possibly falling behind: %s self-ack'ed our "
+ "incoming %s to EPU mailbox (sequence no. %u) "
+ "while processing\n",
+ rpu_str[order->rpu], rpu_str[order->rpu], req);
+ order->flags |= CX18_F_EWO_MB_STALE_WHILE_PROC;
+ return;
+ }
+ cx18_writel(cx, req, &ack_mb->ack);
+ cx18_write_reg_expect(cx, ack_irq, SW2_INT_SET, ack_irq, ack_irq);
+ return;
+}
+
+static int epu_dma_done_irq(struct cx18 *cx, struct cx18_in_work_order *order)
+{
+ u32 handle, mdl_ack_offset, mdl_ack_count;
+ struct cx18_mailbox *mb;
+
+ mb = &order->mb;
+ handle = mb->args[0];
+ mdl_ack_offset = mb->args[1];
+ mdl_ack_count = mb->args[2];
+
+ if (handle == CX18_INVALID_TASK_HANDLE ||
+ mdl_ack_count == 0 || mdl_ack_count > CX18_MAX_MDL_ACKS) {
+ if ((order->flags & CX18_F_EWO_MB_STALE) == 0)
+ mb_ack_irq(cx, order);
+ return -1;
+ }
+
+ cx18_memcpy_fromio(cx, order->mdl_ack, cx->enc_mem + mdl_ack_offset,
+ sizeof(struct cx18_mdl_ack) * mdl_ack_count);
+
+ if ((order->flags & CX18_F_EWO_MB_STALE) == 0)
+ mb_ack_irq(cx, order);
+ return 1;
+}
+
+static
+int epu_debug_irq(struct cx18 *cx, struct cx18_in_work_order *order)
+{
+ u32 str_offset;
+ char *str = order->str;
+
+ str[0] = '\0';
+ str_offset = order->mb.args[1];
+ if (str_offset) {
+ cx18_setup_page(cx, str_offset);
+ cx18_memcpy_fromio(cx, str, cx->enc_mem + str_offset, 252);
+ str[252] = '\0';
+ cx18_setup_page(cx, SCB_OFFSET);
+ }
+
+ if ((order->flags & CX18_F_EWO_MB_STALE) == 0)
+ mb_ack_irq(cx, order);
+
+ return str_offset ? 1 : 0;
+}
+
+static inline
+int epu_cmd_irq(struct cx18 *cx, struct cx18_in_work_order *order)
+{
+ int ret = -1;
+
+ switch (order->rpu) {
+ case CPU:
+ {
+ switch (order->mb.cmd) {
+ case CX18_EPU_DMA_DONE:
+ ret = epu_dma_done_irq(cx, order);
+ break;
+ case CX18_EPU_DEBUG:
+ ret = epu_debug_irq(cx, order);
+ break;
+ default:
+ CX18_WARN("Unknown CPU to EPU mailbox command %#0x\n",
+ order->mb.cmd);
+ break;
+ }
+ break;
+ }
+ case APU:
+ CX18_WARN("Unknown APU to EPU mailbox command %#0x\n",
+ order->mb.cmd);
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+static inline
+struct cx18_in_work_order *alloc_in_work_order_irq(struct cx18 *cx)
+{
+ int i;
+ struct cx18_in_work_order *order = NULL;
+
+ for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++) {
+ /*
+ * We only need "pending" atomic to inspect its contents,
+ * and need not do a check and set because:
+ * 1. Any work handler thread only clears "pending" and only
+ * on one, particular work order at a time, per handler thread.
+ * 2. "pending" is only set here, and we're serialized because
+ * we're called in an IRQ handler context.
+ */
+ if (atomic_read(&cx->in_work_order[i].pending) == 0) {
+ order = &cx->in_work_order[i];
+ atomic_set(&order->pending, 1);
+ break;
+ }
+ }
+ return order;
+}
+
+void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu)
+{
+ struct cx18_mailbox __iomem *mb;
+ struct cx18_mailbox *order_mb;
+ struct cx18_in_work_order *order;
+ int submit;
+
+ switch (rpu) {
+ case CPU:
+ mb = &cx->scb->cpu2epu_mb;
+ break;
+ case APU:
+ mb = &cx->scb->apu2epu_mb;
+ break;
+ default:
+ return;
+ }
+
+ order = alloc_in_work_order_irq(cx);
+ if (order == NULL) {
+ CX18_WARN("Unable to find blank work order form to schedule "
+ "incoming mailbox command processing\n");
+ return;
+ }
+
+ order->flags = 0;
+ order->rpu = rpu;
+ order_mb = &order->mb;
+
+ /* mb->cmd and mb->args[0] through mb->args[2] */
+ cx18_memcpy_fromio(cx, &order_mb->cmd, &mb->cmd, 4 * sizeof(u32));
+ /* mb->request and mb->ack. N.B. we want to read mb->ack last */
+ cx18_memcpy_fromio(cx, &order_mb->request, &mb->request,
+ 2 * sizeof(u32));
+
+ if (order_mb->request == order_mb->ack) {
+ CX18_DEBUG_WARN("Possibly falling behind: %s self-ack'ed our "
+ "incoming %s to EPU mailbox (sequence no. %u)"
+ "\n",
+ rpu_str[rpu], rpu_str[rpu], order_mb->request);
+ if (cx18_debug & CX18_DBGFLG_WARN)
+ dump_mb(cx, order_mb, "incoming");
+ order->flags = CX18_F_EWO_MB_STALE_UPON_RECEIPT;
+ }
+
+ /*
+ * Individual EPU command processing is responsible for ack-ing
+ * a non-stale mailbox as soon as possible
+ */
+ submit = epu_cmd_irq(cx, order);
+ if (submit > 0) {
+ queue_work(cx->in_work_queue, &order->work);
+ }
+}
+
+
+/*
+ * Functions called from a non-interrupt, non work_queue context
+ */
+
+static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
+{
+ const struct cx18_api_info *info = find_api_info(cmd);
+ u32 state, irq, req, ack, err;
+ struct cx18_mailbox __iomem *mb;
+ u32 __iomem *xpu_state;
+ wait_queue_head_t *waitq;
+ struct mutex *mb_lock;
+ unsigned long int t0, timeout, ret;
+ int i;
+ char argstr[MAX_MB_ARGUMENTS*11+1];
+ DEFINE_WAIT(w);
+
+ if (info == NULL) {
+ CX18_WARN("unknown cmd %x\n", cmd);
+ return -EINVAL;
+ }
+
+ if (cx18_debug & CX18_DBGFLG_API) { /* only call u32arr2hex if needed */
+ if (cmd == CX18_CPU_DE_SET_MDL) {
+ if (cx18_debug & CX18_DBGFLG_HIGHVOL)
+ CX18_DEBUG_HI_API("%s\tcmd %#010x args%s\n",
+ info->name, cmd,
+ u32arr2hex(data, args, argstr));
+ } else
+ CX18_DEBUG_API("%s\tcmd %#010x args%s\n",
+ info->name, cmd,
+ u32arr2hex(data, args, argstr));
+ }
+
+ switch (info->rpu) {
+ case APU:
+ waitq = &cx->mb_apu_waitq;
+ mb_lock = &cx->epu2apu_mb_lock;
+ irq = IRQ_EPU_TO_APU;
+ mb = &cx->scb->epu2apu_mb;
+ xpu_state = &cx->scb->apu_state;
+ break;
+ case CPU:
+ waitq = &cx->mb_cpu_waitq;
+ mb_lock = &cx->epu2cpu_mb_lock;
+ irq = IRQ_EPU_TO_CPU;
+ mb = &cx->scb->epu2cpu_mb;
+ xpu_state = &cx->scb->cpu_state;
+ break;
+ default:
+ CX18_WARN("Unknown RPU (%d) for API call\n", info->rpu);
+ return -EINVAL;
+ }
+
+ mutex_lock(mb_lock);
+ /*
+ * Wait for an in-use mailbox to complete
+ *
+ * If the XPU is responding with Ack's, the mailbox shouldn't be in
+ * a busy state, since we serialize access to it on our end.
+ *
+ * If the wait for ack after sending a previous command was interrupted
+ * by a signal, we may get here and find a busy mailbox. After waiting,
+ * mark it "not busy" from our end, if the XPU hasn't ack'ed it still.
+ */
+ state = cx18_readl(cx, xpu_state);
+ req = cx18_readl(cx, &mb->request);
+ timeout = msecs_to_jiffies(10);
+ ret = wait_event_timeout(*waitq,
+ (ack = cx18_readl(cx, &mb->ack)) == req,
+ timeout);
+ if (req != ack) {
+ /* waited long enough, make the mbox "not busy" from our end */
+ cx18_writel(cx, req, &mb->ack);
+ CX18_ERR("mbox was found stuck busy when setting up for %s; "
+ "clearing busy and trying to proceed\n", info->name);
+ } else if (ret != timeout)
+ CX18_DEBUG_API("waited %u msecs for busy mbox to be acked\n",
+ jiffies_to_msecs(timeout-ret));
+
+ /* Build the outgoing mailbox */
+ req = ((req & 0xfffffffe) == 0xfffffffe) ? 1 : req + 1;
+
+ cx18_writel(cx, cmd, &mb->cmd);
+ for (i = 0; i < args; i++)
+ cx18_writel(cx, data[i], &mb->args[i]);
+ cx18_writel(cx, 0, &mb->error);
+ cx18_writel(cx, req, &mb->request);
+ cx18_writel(cx, req - 1, &mb->ack); /* ensure ack & req are distinct */
+
+ /*
+ * Notify the XPU and wait for it to send an Ack back
+ */
+ timeout = msecs_to_jiffies((info->flags & API_FAST) ? 10 : 20);
+
+ CX18_DEBUG_HI_IRQ("sending interrupt SW1: %x to send %s\n",
+ irq, info->name);
+
+ /* So we don't miss the wakeup, prepare to wait before notifying fw */
+ prepare_to_wait(waitq, &w, TASK_UNINTERRUPTIBLE);
+ cx18_write_reg_expect(cx, irq, SW1_INT_SET, irq, irq);
+
+ t0 = jiffies;
+ ack = cx18_readl(cx, &mb->ack);
+ if (ack != req) {
+ schedule_timeout(timeout);
+ ret = jiffies - t0;
+ ack = cx18_readl(cx, &mb->ack);
+ } else {
+ ret = jiffies - t0;
+ }
+
+ finish_wait(waitq, &w);
+
+ if (req != ack) {
+ mutex_unlock(mb_lock);
+ if (ret >= timeout) {
+ /* Timed out */
+ CX18_DEBUG_WARN("sending %s timed out waiting %d msecs "
+ "for RPU acknowledgement\n",
+ info->name, jiffies_to_msecs(ret));
+ } else {
+ CX18_DEBUG_WARN("woken up before mailbox ack was ready "
+ "after submitting %s to RPU. only "
+ "waited %d msecs on req %u but awakened"
+ " with unmatched ack %u\n",
+ info->name,
+ jiffies_to_msecs(ret),
+ req, ack);
+ }
+ return -EINVAL;
+ }
+
+ if (ret >= timeout)
+ CX18_DEBUG_WARN("failed to be awakened upon RPU acknowledgment "
+ "sending %s; timed out waiting %d msecs\n",
+ info->name, jiffies_to_msecs(ret));
+ else
+ CX18_DEBUG_HI_API("waited %u msecs for %s to be acked\n",
+ jiffies_to_msecs(ret), info->name);
+
+ /* Collect data returned by the XPU */
+ for (i = 0; i < MAX_MB_ARGUMENTS; i++)
+ data[i] = cx18_readl(cx, &mb->args[i]);
+ err = cx18_readl(cx, &mb->error);
+ mutex_unlock(mb_lock);
+
+ /*
+ * Wait for XPU to perform extra actions for the caller in some cases.
+ * e.g. CX18_CPU_DE_RELEASE_MDL will cause the CPU to send all MDLs
+ * back in a burst shortly thereafter
+ */
+ if (info->flags & API_SLOW)
+ cx18_msleep_timeout(300, 0);
+
+ if (err)
+ CX18_DEBUG_API("mailbox error %08x for command %s\n", err,
+ info->name);
+ return err ? -EIO : 0;
+}
+
+int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[])
+{
+ return cx18_api_call(cx, cmd, args, data);
+}
+
+static int cx18_set_filter_param(struct cx18_stream *s)
+{
+ struct cx18 *cx = s->cx;
+ u32 mode;
+ int ret;
+
+ mode = (cx->filter_mode & 1) ? 2 : (cx->spatial_strength ? 1 : 0);
+ ret = cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4,
+ s->handle, 1, mode, cx->spatial_strength);
+ mode = (cx->filter_mode & 2) ? 2 : (cx->temporal_strength ? 1 : 0);
+ ret = ret ? ret : cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4,
+ s->handle, 0, mode, cx->temporal_strength);
+ ret = ret ? ret : cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4,
+ s->handle, 2, cx->filter_mode >> 2, 0);
+ return ret;
+}
+
+int cx18_api_func(void *priv, u32 cmd, int in, int out,
+ u32 data[CX2341X_MBOX_MAX_DATA])
+{
+ struct cx18_stream *s = priv;
+ struct cx18 *cx = s->cx;
+
+ switch (cmd) {
+ case CX2341X_ENC_SET_OUTPUT_PORT:
+ return 0;
+ case CX2341X_ENC_SET_FRAME_RATE:
+ return cx18_vapi(cx, CX18_CPU_SET_VIDEO_IN, 6,
+ s->handle, 0, 0, 0, 0, data[0]);
+ case CX2341X_ENC_SET_FRAME_SIZE:
+ return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RESOLUTION, 3,
+ s->handle, data[1], data[0]);
+ case CX2341X_ENC_SET_STREAM_TYPE:
+ return cx18_vapi(cx, CX18_CPU_SET_STREAM_OUTPUT_TYPE, 2,
+ s->handle, data[0]);
+ case CX2341X_ENC_SET_ASPECT_RATIO:
+ return cx18_vapi(cx, CX18_CPU_SET_ASPECT_RATIO, 2,
+ s->handle, data[0]);
+
+ case CX2341X_ENC_SET_GOP_PROPERTIES:
+ return cx18_vapi(cx, CX18_CPU_SET_GOP_STRUCTURE, 3,
+ s->handle, data[0], data[1]);
+ case CX2341X_ENC_SET_GOP_CLOSURE:
+ return 0;
+ case CX2341X_ENC_SET_AUDIO_PROPERTIES:
+ return cx18_vapi(cx, CX18_CPU_SET_AUDIO_PARAMETERS, 2,
+ s->handle, data[0]);
+ case CX2341X_ENC_MUTE_AUDIO:
+ return cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2,
+ s->handle, data[0]);
+ case CX2341X_ENC_SET_BIT_RATE:
+ return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RATE, 5,
+ s->handle, data[0], data[1], data[2], data[3]);
+ case CX2341X_ENC_MUTE_VIDEO:
+ return cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2,
+ s->handle, data[0]);
+ case CX2341X_ENC_SET_FRAME_DROP_RATE:
+ return cx18_vapi(cx, CX18_CPU_SET_SKIP_INPUT_FRAME, 2,
+ s->handle, data[0]);
+ case CX2341X_ENC_MISC:
+ return cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 4,
+ s->handle, data[0], data[1], data[2]);
+ case CX2341X_ENC_SET_DNR_FILTER_MODE:
+ cx->filter_mode = (data[0] & 3) | (data[1] << 2);
+ return cx18_set_filter_param(s);
+ case CX2341X_ENC_SET_DNR_FILTER_PROPS:
+ cx->spatial_strength = data[0];
+ cx->temporal_strength = data[1];
+ return cx18_set_filter_param(s);
+ case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE:
+ return cx18_vapi(cx, CX18_CPU_SET_SPATIAL_FILTER_TYPE, 3,
+ s->handle, data[0], data[1]);
+ case CX2341X_ENC_SET_CORING_LEVELS:
+ return cx18_vapi(cx, CX18_CPU_SET_MEDIAN_CORING, 5,
+ s->handle, data[0], data[1], data[2], data[3]);
+ }
+ CX18_WARN("Unknown cmd %x\n", cmd);
+ return 0;
+}
+
+int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS],
+ u32 cmd, int args, ...)
+{
+ va_list ap;
+ int i;
+
+ va_start(ap, args);
+ for (i = 0; i < args; i++)
+ data[i] = va_arg(ap, u32);
+ va_end(ap);
+ return cx18_api(cx, cmd, args, data);
+}
+
+int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...)
+{
+ u32 data[MAX_MB_ARGUMENTS];
+ va_list ap;
+ int i;
+
+ if (cx == NULL) {
+ CX18_ERR("cx == NULL (cmd=%x)\n", cmd);
+ return 0;
+ }
+ if (args > MAX_MB_ARGUMENTS) {
+ CX18_ERR("args too big (cmd=%x)\n", cmd);
+ args = MAX_MB_ARGUMENTS;
+ }
+ va_start(ap, args);
+ for (i = 0; i < args; i++)
+ data[i] = va_arg(ap, u32);
+ va_end(ap);
+ return cx18_api(cx, cmd, args, data);
+}
diff --git a/drivers/media/video/cx18/cx18-mailbox.h b/drivers/media/video/cx18/cx18-mailbox.h
new file mode 100644
index 00000000..b63fdfaa
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-mailbox.h
@@ -0,0 +1,95 @@
+/*
+ * cx18 mailbox functions
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#ifndef _CX18_MAILBOX_H_
+#define _CX18_MAILBOX_H_
+
+/* mailbox max args */
+#define MAX_MB_ARGUMENTS 6
+/* compatibility, should be same as the define in cx2341x.h */
+#define CX2341X_MBOX_MAX_DATA 16
+
+#define MB_RESERVED_HANDLE_0 0
+#define MB_RESERVED_HANDLE_1 0xFFFFFFFF
+
+#define APU 0
+#define CPU 1
+#define EPU 2
+#define HPU 3
+
+struct cx18;
+
+/*
+ * This structure is used by CPU to provide completed MDL & buffers information.
+ * Its structure is dictated by the layout of the SCB, required by the
+ * firmware, but its definition needs to be here, instead of in cx18-scb.h,
+ * for mailbox work order scheduling
+ */
+struct cx18_mdl_ack {
+ u32 id; /* ID of a completed MDL */
+ u32 data_used; /* Total data filled in the MDL with 'id' */
+};
+
+/* The cx18_mailbox struct is the mailbox structure which is used for passing
+ messages between processors */
+struct cx18_mailbox {
+ /* The sender sets a handle in 'request' after he fills the command. The
+ 'request' should be different than 'ack'. The sender, also, generates
+ an interrupt on XPU2YPU_irq where XPU is the sender and YPU is the
+ receiver. */
+ u32 request;
+ /* The receiver detects a new command when 'req' is different than 'ack'.
+ He sets 'ack' to the same value as 'req' to clear the command. He, also,
+ generates an interrupt on YPU2XPU_irq where XPU is the sender and YPU
+ is the receiver. */
+ u32 ack;
+ u32 reserved[6];
+ /* 'cmd' identifies the command. The list of these commands are in
+ cx23418.h */
+ u32 cmd;
+ /* Each command can have up to 6 arguments */
+ u32 args[MAX_MB_ARGUMENTS];
+ /* The return code can be one of the codes in the file cx23418.h. If the
+ command is completed successfully, the error will be ERR_SYS_SUCCESS.
+ If it is pending, the code is ERR_SYS_PENDING. If it failed, the error
+ code would indicate the task from which the error originated and will
+ be one of the errors in cx23418.h. In that case, the following
+ applies ((error & 0xff) != 0).
+ If the command is pending, the return will be passed in a MB from the
+ receiver to the sender. 'req' will be returned in args[0] */
+ u32 error;
+};
+
+struct cx18_stream;
+
+int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[]);
+int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS], u32 cmd,
+ int args, ...);
+int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...);
+int cx18_api_func(void *priv, u32 cmd, int in, int out,
+ u32 data[CX2341X_MBOX_MAX_DATA]);
+
+void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu);
+
+void cx18_in_work_handler(struct work_struct *work);
+
+#endif
diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c
new file mode 100644
index 00000000..8884537b
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-queue.c
@@ -0,0 +1,443 @@
+/*
+ * cx18 buffer queues
+ *
+ * Derived from ivtv-queue.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include "cx18-driver.h"
+#include "cx18-queue.h"
+#include "cx18-streams.h"
+#include "cx18-scb.h"
+#include "cx18-io.h"
+
+void cx18_buf_swap(struct cx18_buffer *buf)
+{
+ int i;
+
+ for (i = 0; i < buf->bytesused; i += 4)
+ swab32s((u32 *)(buf->buf + i));
+}
+
+void _cx18_mdl_swap(struct cx18_mdl *mdl)
+{
+ struct cx18_buffer *buf;
+
+ list_for_each_entry(buf, &mdl->buf_list, list) {
+ if (buf->bytesused == 0)
+ break;
+ cx18_buf_swap(buf);
+ }
+}
+
+void cx18_queue_init(struct cx18_queue *q)
+{
+ INIT_LIST_HEAD(&q->list);
+ atomic_set(&q->depth, 0);
+ q->bytesused = 0;
+}
+
+struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl,
+ struct cx18_queue *q, int to_front)
+{
+ /* clear the mdl if it is not to be enqueued to the full queue */
+ if (q != &s->q_full) {
+ mdl->bytesused = 0;
+ mdl->readpos = 0;
+ mdl->m_flags = 0;
+ mdl->skipped = 0;
+ mdl->curr_buf = NULL;
+ }
+
+ /* q_busy is restricted to a max buffer count imposed by firmware */
+ if (q == &s->q_busy &&
+ atomic_read(&q->depth) >= CX18_MAX_FW_MDLS_PER_STREAM)
+ q = &s->q_free;
+
+ spin_lock(&q->lock);
+
+ if (to_front)
+ list_add(&mdl->list, &q->list); /* LIFO */
+ else
+ list_add_tail(&mdl->list, &q->list); /* FIFO */
+ q->bytesused += mdl->bytesused - mdl->readpos;
+ atomic_inc(&q->depth);
+
+ spin_unlock(&q->lock);
+ return q;
+}
+
+struct cx18_mdl *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q)
+{
+ struct cx18_mdl *mdl = NULL;
+
+ spin_lock(&q->lock);
+ if (!list_empty(&q->list)) {
+ mdl = list_first_entry(&q->list, struct cx18_mdl, list);
+ list_del_init(&mdl->list);
+ q->bytesused -= mdl->bytesused - mdl->readpos;
+ mdl->skipped = 0;
+ atomic_dec(&q->depth);
+ }
+ spin_unlock(&q->lock);
+ return mdl;
+}
+
+static void _cx18_mdl_update_bufs_for_cpu(struct cx18_stream *s,
+ struct cx18_mdl *mdl)
+{
+ struct cx18_buffer *buf;
+ u32 buf_size = s->buf_size;
+ u32 bytesused = mdl->bytesused;
+
+ list_for_each_entry(buf, &mdl->buf_list, list) {
+ buf->readpos = 0;
+ if (bytesused >= buf_size) {
+ buf->bytesused = buf_size;
+ bytesused -= buf_size;
+ } else {
+ buf->bytesused = bytesused;
+ bytesused = 0;
+ }
+ cx18_buf_sync_for_cpu(s, buf);
+ }
+}
+
+static inline void cx18_mdl_update_bufs_for_cpu(struct cx18_stream *s,
+ struct cx18_mdl *mdl)
+{
+ struct cx18_buffer *buf;
+
+ if (list_is_singular(&mdl->buf_list)) {
+ buf = list_first_entry(&mdl->buf_list, struct cx18_buffer,
+ list);
+ buf->bytesused = mdl->bytesused;
+ buf->readpos = 0;
+ cx18_buf_sync_for_cpu(s, buf);
+ } else {
+ _cx18_mdl_update_bufs_for_cpu(s, mdl);
+ }
+}
+
+struct cx18_mdl *cx18_queue_get_mdl(struct cx18_stream *s, u32 id,
+ u32 bytesused)
+{
+ struct cx18 *cx = s->cx;
+ struct cx18_mdl *mdl;
+ struct cx18_mdl *tmp;
+ struct cx18_mdl *ret = NULL;
+ LIST_HEAD(sweep_up);
+
+ /*
+ * We don't have to acquire multiple q locks here, because we are
+ * serialized by the single threaded work handler.
+ * MDLs from the firmware will thus remain in order as
+ * they are moved from q_busy to q_full or to the dvb ring buffer.
+ */
+ spin_lock(&s->q_busy.lock);
+ list_for_each_entry_safe(mdl, tmp, &s->q_busy.list, list) {
+ /*
+ * We should find what the firmware told us is done,
+ * right at the front of the queue. If we don't, we likely have
+ * missed an mdl done message from the firmware.
+ * Once we skip an mdl repeatedly, relative to the size of
+ * q_busy, we have high confidence we've missed it.
+ */
+ if (mdl->id != id) {
+ mdl->skipped++;
+ if (mdl->skipped >= atomic_read(&s->q_busy.depth)-1) {
+ /* mdl must have fallen out of rotation */
+ CX18_WARN("Skipped %s, MDL %d, %d "
+ "times - it must have dropped out of "
+ "rotation\n", s->name, mdl->id,
+ mdl->skipped);
+ /* Sweep it up to put it back into rotation */
+ list_move_tail(&mdl->list, &sweep_up);
+ atomic_dec(&s->q_busy.depth);
+ }
+ continue;
+ }
+ /*
+ * We pull the desired mdl off of the queue here. Something
+ * will have to put it back on a queue later.
+ */
+ list_del_init(&mdl->list);
+ atomic_dec(&s->q_busy.depth);
+ ret = mdl;
+ break;
+ }
+ spin_unlock(&s->q_busy.lock);
+
+ /*
+ * We found the mdl for which we were looking. Get it ready for
+ * the caller to put on q_full or in the dvb ring buffer.
+ */
+ if (ret != NULL) {
+ ret->bytesused = bytesused;
+ ret->skipped = 0;
+ /* 0'ed readpos, m_flags & curr_buf when mdl went on q_busy */
+ cx18_mdl_update_bufs_for_cpu(s, ret);
+ if (s->type != CX18_ENC_STREAM_TYPE_TS)
+ set_bit(CX18_F_M_NEED_SWAP, &ret->m_flags);
+ }
+
+ /* Put any mdls the firmware is ignoring back into normal rotation */
+ list_for_each_entry_safe(mdl, tmp, &sweep_up, list) {
+ list_del_init(&mdl->list);
+ cx18_enqueue(s, mdl, &s->q_free);
+ }
+ return ret;
+}
+
+/* Move all mdls of a queue, while flushing the mdl */
+static void cx18_queue_flush(struct cx18_stream *s,
+ struct cx18_queue *q_src, struct cx18_queue *q_dst)
+{
+ struct cx18_mdl *mdl;
+
+ /* It only makes sense to flush to q_free or q_idle */
+ if (q_src == q_dst || q_dst == &s->q_full || q_dst == &s->q_busy)
+ return;
+
+ spin_lock(&q_src->lock);
+ spin_lock(&q_dst->lock);
+ while (!list_empty(&q_src->list)) {
+ mdl = list_first_entry(&q_src->list, struct cx18_mdl, list);
+ list_move_tail(&mdl->list, &q_dst->list);
+ mdl->bytesused = 0;
+ mdl->readpos = 0;
+ mdl->m_flags = 0;
+ mdl->skipped = 0;
+ mdl->curr_buf = NULL;
+ atomic_inc(&q_dst->depth);
+ }
+ cx18_queue_init(q_src);
+ spin_unlock(&q_src->lock);
+ spin_unlock(&q_dst->lock);
+}
+
+void cx18_flush_queues(struct cx18_stream *s)
+{
+ cx18_queue_flush(s, &s->q_busy, &s->q_free);
+ cx18_queue_flush(s, &s->q_full, &s->q_free);
+}
+
+/*
+ * Note, s->buf_pool is not protected by a lock,
+ * the stream better not have *anything* going on when calling this
+ */
+void cx18_unload_queues(struct cx18_stream *s)
+{
+ struct cx18_queue *q_idle = &s->q_idle;
+ struct cx18_mdl *mdl;
+ struct cx18_buffer *buf;
+
+ /* Move all MDLS to q_idle */
+ cx18_queue_flush(s, &s->q_busy, q_idle);
+ cx18_queue_flush(s, &s->q_full, q_idle);
+ cx18_queue_flush(s, &s->q_free, q_idle);
+
+ /* Reset MDL id's and move all buffers back to the stream's buf_pool */
+ spin_lock(&q_idle->lock);
+ list_for_each_entry(mdl, &q_idle->list, list) {
+ while (!list_empty(&mdl->buf_list)) {
+ buf = list_first_entry(&mdl->buf_list,
+ struct cx18_buffer, list);
+ list_move_tail(&buf->list, &s->buf_pool);
+ buf->bytesused = 0;
+ buf->readpos = 0;
+ }
+ mdl->id = s->mdl_base_idx; /* reset id to a "safe" value */
+ /* all other mdl fields were cleared by cx18_queue_flush() */
+ }
+ spin_unlock(&q_idle->lock);
+}
+
+/*
+ * Note, s->buf_pool is not protected by a lock,
+ * the stream better not have *anything* going on when calling this
+ */
+void cx18_load_queues(struct cx18_stream *s)
+{
+ struct cx18 *cx = s->cx;
+ struct cx18_mdl *mdl;
+ struct cx18_buffer *buf;
+ int mdl_id;
+ int i;
+ u32 partial_buf_size;
+
+ /*
+ * Attach buffers to MDLs, give the MDLs ids, and add MDLs to q_free
+ * Excess MDLs are left on q_idle
+ * Excess buffers are left in buf_pool and/or on an MDL in q_idle
+ */
+ mdl_id = s->mdl_base_idx;
+ for (mdl = cx18_dequeue(s, &s->q_idle), i = s->bufs_per_mdl;
+ mdl != NULL && i == s->bufs_per_mdl;
+ mdl = cx18_dequeue(s, &s->q_idle)) {
+
+ mdl->id = mdl_id;
+
+ for (i = 0; i < s->bufs_per_mdl; i++) {
+ if (list_empty(&s->buf_pool))
+ break;
+
+ buf = list_first_entry(&s->buf_pool, struct cx18_buffer,
+ list);
+ list_move_tail(&buf->list, &mdl->buf_list);
+
+ /* update the firmware's MDL array with this buffer */
+ cx18_writel(cx, buf->dma_handle,
+ &cx->scb->cpu_mdl[mdl_id + i].paddr);
+ cx18_writel(cx, s->buf_size,
+ &cx->scb->cpu_mdl[mdl_id + i].length);
+ }
+
+ if (i == s->bufs_per_mdl) {
+ /*
+ * The encoder doesn't honor s->mdl_size. So in the
+ * case of a non-integral number of buffers to meet
+ * mdl_size, we lie about the size of the last buffer
+ * in the MDL to get the encoder to really only send
+ * us mdl_size bytes per MDL transfer.
+ */
+ partial_buf_size = s->mdl_size % s->buf_size;
+ if (partial_buf_size) {
+ cx18_writel(cx, partial_buf_size,
+ &cx->scb->cpu_mdl[mdl_id + i - 1].length);
+ }
+ cx18_enqueue(s, mdl, &s->q_free);
+ } else {
+ /* Not enough buffers for this MDL; we won't use it */
+ cx18_push(s, mdl, &s->q_idle);
+ }
+ mdl_id += i;
+ }
+}
+
+void _cx18_mdl_sync_for_device(struct cx18_stream *s, struct cx18_mdl *mdl)
+{
+ int dma = s->dma;
+ u32 buf_size = s->buf_size;
+ struct pci_dev *pci_dev = s->cx->pci_dev;
+ struct cx18_buffer *buf;
+
+ list_for_each_entry(buf, &mdl->buf_list, list)
+ pci_dma_sync_single_for_device(pci_dev, buf->dma_handle,
+ buf_size, dma);
+}
+
+int cx18_stream_alloc(struct cx18_stream *s)
+{
+ struct cx18 *cx = s->cx;
+ int i;
+
+ if (s->buffers == 0)
+ return 0;
+
+ CX18_DEBUG_INFO("Allocate %s stream: %d x %d buffers "
+ "(%d.%02d kB total)\n",
+ s->name, s->buffers, s->buf_size,
+ s->buffers * s->buf_size / 1024,
+ (s->buffers * s->buf_size * 100 / 1024) % 100);
+
+ if (((char __iomem *)&cx->scb->cpu_mdl[cx->free_mdl_idx + s->buffers] -
+ (char __iomem *)cx->scb) > SCB_RESERVED_SIZE) {
+ unsigned bufsz = (((char __iomem *)cx->scb) + SCB_RESERVED_SIZE -
+ ((char __iomem *)cx->scb->cpu_mdl));
+
+ CX18_ERR("Too many buffers, cannot fit in SCB area\n");
+ CX18_ERR("Max buffers = %zd\n",
+ bufsz / sizeof(struct cx18_mdl_ent));
+ return -ENOMEM;
+ }
+
+ s->mdl_base_idx = cx->free_mdl_idx;
+
+ /* allocate stream buffers and MDLs */
+ for (i = 0; i < s->buffers; i++) {
+ struct cx18_mdl *mdl;
+ struct cx18_buffer *buf;
+
+ /* 1 MDL per buffer to handle the worst & also default case */
+ mdl = kzalloc(sizeof(struct cx18_mdl), GFP_KERNEL|__GFP_NOWARN);
+ if (mdl == NULL)
+ break;
+
+ buf = kzalloc(sizeof(struct cx18_buffer),
+ GFP_KERNEL|__GFP_NOWARN);
+ if (buf == NULL) {
+ kfree(mdl);
+ break;
+ }
+
+ buf->buf = kmalloc(s->buf_size, GFP_KERNEL|__GFP_NOWARN);
+ if (buf->buf == NULL) {
+ kfree(mdl);
+ kfree(buf);
+ break;
+ }
+
+ INIT_LIST_HEAD(&mdl->list);
+ INIT_LIST_HEAD(&mdl->buf_list);
+ mdl->id = s->mdl_base_idx; /* a somewhat safe value */
+ cx18_enqueue(s, mdl, &s->q_idle);
+
+ INIT_LIST_HEAD(&buf->list);
+ buf->dma_handle = pci_map_single(s->cx->pci_dev,
+ buf->buf, s->buf_size, s->dma);
+ cx18_buf_sync_for_cpu(s, buf);
+ list_add_tail(&buf->list, &s->buf_pool);
+ }
+ if (i == s->buffers) {
+ cx->free_mdl_idx += s->buffers;
+ return 0;
+ }
+ CX18_ERR("Couldn't allocate buffers for %s stream\n", s->name);
+ cx18_stream_free(s);
+ return -ENOMEM;
+}
+
+void cx18_stream_free(struct cx18_stream *s)
+{
+ struct cx18_mdl *mdl;
+ struct cx18_buffer *buf;
+ struct cx18 *cx = s->cx;
+
+ CX18_DEBUG_INFO("Deallocating buffers for %s stream\n", s->name);
+
+ /* move all buffers to buf_pool and all MDLs to q_idle */
+ cx18_unload_queues(s);
+
+ /* empty q_idle */
+ while ((mdl = cx18_dequeue(s, &s->q_idle)))
+ kfree(mdl);
+
+ /* empty buf_pool */
+ while (!list_empty(&s->buf_pool)) {
+ buf = list_first_entry(&s->buf_pool, struct cx18_buffer, list);
+ list_del_init(&buf->list);
+
+ pci_unmap_single(s->cx->pci_dev, buf->dma_handle,
+ s->buf_size, s->dma);
+ kfree(buf->buf);
+ kfree(buf);
+ }
+}
diff --git a/drivers/media/video/cx18/cx18-queue.h b/drivers/media/video/cx18/cx18-queue.h
new file mode 100644
index 00000000..4201ddc1
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-queue.h
@@ -0,0 +1,98 @@
+/*
+ * cx18 buffer queues
+ *
+ * Derived from ivtv-queue.h
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#define CX18_DMA_UNMAPPED ((u32) -1)
+
+/* cx18_buffer utility functions */
+
+static inline void cx18_buf_sync_for_cpu(struct cx18_stream *s,
+ struct cx18_buffer *buf)
+{
+ pci_dma_sync_single_for_cpu(s->cx->pci_dev, buf->dma_handle,
+ s->buf_size, s->dma);
+}
+
+static inline void cx18_buf_sync_for_device(struct cx18_stream *s,
+ struct cx18_buffer *buf)
+{
+ pci_dma_sync_single_for_device(s->cx->pci_dev, buf->dma_handle,
+ s->buf_size, s->dma);
+}
+
+void _cx18_mdl_sync_for_device(struct cx18_stream *s, struct cx18_mdl *mdl);
+
+static inline void cx18_mdl_sync_for_device(struct cx18_stream *s,
+ struct cx18_mdl *mdl)
+{
+ if (list_is_singular(&mdl->buf_list))
+ cx18_buf_sync_for_device(s, list_first_entry(&mdl->buf_list,
+ struct cx18_buffer,
+ list));
+ else
+ _cx18_mdl_sync_for_device(s, mdl);
+}
+
+void cx18_buf_swap(struct cx18_buffer *buf);
+void _cx18_mdl_swap(struct cx18_mdl *mdl);
+
+static inline void cx18_mdl_swap(struct cx18_mdl *mdl)
+{
+ if (list_is_singular(&mdl->buf_list))
+ cx18_buf_swap(list_first_entry(&mdl->buf_list,
+ struct cx18_buffer, list));
+ else
+ _cx18_mdl_swap(mdl);
+}
+
+/* cx18_queue utility functions */
+struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl,
+ struct cx18_queue *q, int to_front);
+
+static inline
+struct cx18_queue *cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl,
+ struct cx18_queue *q)
+{
+ return _cx18_enqueue(s, mdl, q, 0); /* FIFO */
+}
+
+static inline
+struct cx18_queue *cx18_push(struct cx18_stream *s, struct cx18_mdl *mdl,
+ struct cx18_queue *q)
+{
+ return _cx18_enqueue(s, mdl, q, 1); /* LIFO */
+}
+
+void cx18_queue_init(struct cx18_queue *q);
+struct cx18_mdl *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q);
+struct cx18_mdl *cx18_queue_get_mdl(struct cx18_stream *s, u32 id,
+ u32 bytesused);
+void cx18_flush_queues(struct cx18_stream *s);
+
+/* queue MDL reconfiguration helpers */
+void cx18_unload_queues(struct cx18_stream *s);
+void cx18_load_queues(struct cx18_stream *s);
+
+/* cx18_stream utility functions */
+int cx18_stream_alloc(struct cx18_stream *s);
+void cx18_stream_free(struct cx18_stream *s);
diff --git a/drivers/media/video/cx18/cx18-scb.c b/drivers/media/video/cx18/cx18-scb.c
new file mode 100644
index 00000000..85cc5963
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-scb.c
@@ -0,0 +1,122 @@
+/*
+ * cx18 System Control Block initialization
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-scb.h"
+
+void cx18_init_scb(struct cx18 *cx)
+{
+ cx18_setup_page(cx, SCB_OFFSET);
+ cx18_memset_io(cx, cx->scb, 0, 0x10000);
+
+ cx18_writel(cx, IRQ_APU_TO_CPU, &cx->scb->apu2cpu_irq);
+ cx18_writel(cx, IRQ_CPU_TO_APU_ACK, &cx->scb->cpu2apu_irq_ack);
+ cx18_writel(cx, IRQ_HPU_TO_CPU, &cx->scb->hpu2cpu_irq);
+ cx18_writel(cx, IRQ_CPU_TO_HPU_ACK, &cx->scb->cpu2hpu_irq_ack);
+ cx18_writel(cx, IRQ_PPU_TO_CPU, &cx->scb->ppu2cpu_irq);
+ cx18_writel(cx, IRQ_CPU_TO_PPU_ACK, &cx->scb->cpu2ppu_irq_ack);
+ cx18_writel(cx, IRQ_EPU_TO_CPU, &cx->scb->epu2cpu_irq);
+ cx18_writel(cx, IRQ_CPU_TO_EPU_ACK, &cx->scb->cpu2epu_irq_ack);
+
+ cx18_writel(cx, IRQ_CPU_TO_APU, &cx->scb->cpu2apu_irq);
+ cx18_writel(cx, IRQ_APU_TO_CPU_ACK, &cx->scb->apu2cpu_irq_ack);
+ cx18_writel(cx, IRQ_HPU_TO_APU, &cx->scb->hpu2apu_irq);
+ cx18_writel(cx, IRQ_APU_TO_HPU_ACK, &cx->scb->apu2hpu_irq_ack);
+ cx18_writel(cx, IRQ_PPU_TO_APU, &cx->scb->ppu2apu_irq);
+ cx18_writel(cx, IRQ_APU_TO_PPU_ACK, &cx->scb->apu2ppu_irq_ack);
+ cx18_writel(cx, IRQ_EPU_TO_APU, &cx->scb->epu2apu_irq);
+ cx18_writel(cx, IRQ_APU_TO_EPU_ACK, &cx->scb->apu2epu_irq_ack);
+
+ cx18_writel(cx, IRQ_CPU_TO_HPU, &cx->scb->cpu2hpu_irq);
+ cx18_writel(cx, IRQ_HPU_TO_CPU_ACK, &cx->scb->hpu2cpu_irq_ack);
+ cx18_writel(cx, IRQ_APU_TO_HPU, &cx->scb->apu2hpu_irq);
+ cx18_writel(cx, IRQ_HPU_TO_APU_ACK, &cx->scb->hpu2apu_irq_ack);
+ cx18_writel(cx, IRQ_PPU_TO_HPU, &cx->scb->ppu2hpu_irq);
+ cx18_writel(cx, IRQ_HPU_TO_PPU_ACK, &cx->scb->hpu2ppu_irq_ack);
+ cx18_writel(cx, IRQ_EPU_TO_HPU, &cx->scb->epu2hpu_irq);
+ cx18_writel(cx, IRQ_HPU_TO_EPU_ACK, &cx->scb->hpu2epu_irq_ack);
+
+ cx18_writel(cx, IRQ_CPU_TO_PPU, &cx->scb->cpu2ppu_irq);
+ cx18_writel(cx, IRQ_PPU_TO_CPU_ACK, &cx->scb->ppu2cpu_irq_ack);
+ cx18_writel(cx, IRQ_APU_TO_PPU, &cx->scb->apu2ppu_irq);
+ cx18_writel(cx, IRQ_PPU_TO_APU_ACK, &cx->scb->ppu2apu_irq_ack);
+ cx18_writel(cx, IRQ_HPU_TO_PPU, &cx->scb->hpu2ppu_irq);
+ cx18_writel(cx, IRQ_PPU_TO_HPU_ACK, &cx->scb->ppu2hpu_irq_ack);
+ cx18_writel(cx, IRQ_EPU_TO_PPU, &cx->scb->epu2ppu_irq);
+ cx18_writel(cx, IRQ_PPU_TO_EPU_ACK, &cx->scb->ppu2epu_irq_ack);
+
+ cx18_writel(cx, IRQ_CPU_TO_EPU, &cx->scb->cpu2epu_irq);
+ cx18_writel(cx, IRQ_EPU_TO_CPU_ACK, &cx->scb->epu2cpu_irq_ack);
+ cx18_writel(cx, IRQ_APU_TO_EPU, &cx->scb->apu2epu_irq);
+ cx18_writel(cx, IRQ_EPU_TO_APU_ACK, &cx->scb->epu2apu_irq_ack);
+ cx18_writel(cx, IRQ_HPU_TO_EPU, &cx->scb->hpu2epu_irq);
+ cx18_writel(cx, IRQ_EPU_TO_HPU_ACK, &cx->scb->epu2hpu_irq_ack);
+ cx18_writel(cx, IRQ_PPU_TO_EPU, &cx->scb->ppu2epu_irq);
+ cx18_writel(cx, IRQ_EPU_TO_PPU_ACK, &cx->scb->epu2ppu_irq_ack);
+
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2cpu_mb),
+ &cx->scb->apu2cpu_mb_offset);
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2cpu_mb),
+ &cx->scb->hpu2cpu_mb_offset);
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2cpu_mb),
+ &cx->scb->ppu2cpu_mb_offset);
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2cpu_mb),
+ &cx->scb->epu2cpu_mb_offset);
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2apu_mb),
+ &cx->scb->cpu2apu_mb_offset);
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2apu_mb),
+ &cx->scb->hpu2apu_mb_offset);
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2apu_mb),
+ &cx->scb->ppu2apu_mb_offset);
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2apu_mb),
+ &cx->scb->epu2apu_mb_offset);
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2hpu_mb),
+ &cx->scb->cpu2hpu_mb_offset);
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2hpu_mb),
+ &cx->scb->apu2hpu_mb_offset);
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2hpu_mb),
+ &cx->scb->ppu2hpu_mb_offset);
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2hpu_mb),
+ &cx->scb->epu2hpu_mb_offset);
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2ppu_mb),
+ &cx->scb->cpu2ppu_mb_offset);
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2ppu_mb),
+ &cx->scb->apu2ppu_mb_offset);
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2ppu_mb),
+ &cx->scb->hpu2ppu_mb_offset);
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2ppu_mb),
+ &cx->scb->epu2ppu_mb_offset);
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2epu_mb),
+ &cx->scb->cpu2epu_mb_offset);
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2epu_mb),
+ &cx->scb->apu2epu_mb_offset);
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2epu_mb),
+ &cx->scb->hpu2epu_mb_offset);
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2epu_mb),
+ &cx->scb->ppu2epu_mb_offset);
+
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu_state),
+ &cx->scb->ipc_offset);
+
+ cx18_writel(cx, 1, &cx->scb->epu_state);
+}
diff --git a/drivers/media/video/cx18/cx18-scb.h b/drivers/media/video/cx18/cx18-scb.h
new file mode 100644
index 00000000..08877652
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-scb.h
@@ -0,0 +1,280 @@
+/*
+ * cx18 System Control Block initialization
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#ifndef CX18_SCB_H
+#define CX18_SCB_H
+
+#include "cx18-mailbox.h"
+
+/* NOTE: All ACK interrupts are in the SW2 register. All non-ACK interrupts
+ are in the SW1 register. */
+
+#define IRQ_APU_TO_CPU 0x00000001
+#define IRQ_CPU_TO_APU_ACK 0x00000001
+#define IRQ_HPU_TO_CPU 0x00000002
+#define IRQ_CPU_TO_HPU_ACK 0x00000002
+#define IRQ_PPU_TO_CPU 0x00000004
+#define IRQ_CPU_TO_PPU_ACK 0x00000004
+#define IRQ_EPU_TO_CPU 0x00000008
+#define IRQ_CPU_TO_EPU_ACK 0x00000008
+
+#define IRQ_CPU_TO_APU 0x00000010
+#define IRQ_APU_TO_CPU_ACK 0x00000010
+#define IRQ_HPU_TO_APU 0x00000020
+#define IRQ_APU_TO_HPU_ACK 0x00000020
+#define IRQ_PPU_TO_APU 0x00000040
+#define IRQ_APU_TO_PPU_ACK 0x00000040
+#define IRQ_EPU_TO_APU 0x00000080
+#define IRQ_APU_TO_EPU_ACK 0x00000080
+
+#define IRQ_CPU_TO_HPU 0x00000100
+#define IRQ_HPU_TO_CPU_ACK 0x00000100
+#define IRQ_APU_TO_HPU 0x00000200
+#define IRQ_HPU_TO_APU_ACK 0x00000200
+#define IRQ_PPU_TO_HPU 0x00000400
+#define IRQ_HPU_TO_PPU_ACK 0x00000400
+#define IRQ_EPU_TO_HPU 0x00000800
+#define IRQ_HPU_TO_EPU_ACK 0x00000800
+
+#define IRQ_CPU_TO_PPU 0x00001000
+#define IRQ_PPU_TO_CPU_ACK 0x00001000
+#define IRQ_APU_TO_PPU 0x00002000
+#define IRQ_PPU_TO_APU_ACK 0x00002000
+#define IRQ_HPU_TO_PPU 0x00004000
+#define IRQ_PPU_TO_HPU_ACK 0x00004000
+#define IRQ_EPU_TO_PPU 0x00008000
+#define IRQ_PPU_TO_EPU_ACK 0x00008000
+
+#define IRQ_CPU_TO_EPU 0x00010000
+#define IRQ_EPU_TO_CPU_ACK 0x00010000
+#define IRQ_APU_TO_EPU 0x00020000
+#define IRQ_EPU_TO_APU_ACK 0x00020000
+#define IRQ_HPU_TO_EPU 0x00040000
+#define IRQ_EPU_TO_HPU_ACK 0x00040000
+#define IRQ_PPU_TO_EPU 0x00080000
+#define IRQ_EPU_TO_PPU_ACK 0x00080000
+
+#define SCB_OFFSET 0xDC0000
+
+/* If Firmware uses fixed memory map, it shall not allocate the area
+ between SCB_OFFSET and SCB_OFFSET+SCB_RESERVED_SIZE-1 inclusive */
+#define SCB_RESERVED_SIZE 0x10000
+
+
+/* This structure is used by EPU to provide memory descriptors in its memory */
+struct cx18_mdl_ent {
+ u32 paddr; /* Physical address of a buffer segment */
+ u32 length; /* Length of the buffer segment */
+};
+
+struct cx18_scb {
+ /* These fields form the System Control Block which is used at boot time
+ for localizing the IPC data as well as the code positions for all
+ processors. The offsets are from the start of this struct. */
+
+ /* Offset where to find the Inter-Processor Communication data */
+ u32 ipc_offset;
+ u32 reserved01[7];
+ /* Offset where to find the start of the CPU code */
+ u32 cpu_code_offset;
+ u32 reserved02[3];
+ /* Offset where to find the start of the APU code */
+ u32 apu_code_offset;
+ u32 reserved03[3];
+ /* Offset where to find the start of the HPU code */
+ u32 hpu_code_offset;
+ u32 reserved04[3];
+ /* Offset where to find the start of the PPU code */
+ u32 ppu_code_offset;
+ u32 reserved05[3];
+
+ /* These fields form Inter-Processor Communication data which is used
+ by all processors to locate the information needed for communicating
+ with other processors */
+
+ /* Fields for CPU: */
+
+ /* bit 0: 1/0 processor ready/not ready. Set other bits to 0. */
+ u32 cpu_state;
+ u32 reserved1[7];
+ /* Offset to the mailbox used for sending commands from APU to CPU */
+ u32 apu2cpu_mb_offset;
+ /* Value to write to register SW1 register set (0xC7003100) after the
+ command is ready */
+ u32 apu2cpu_irq;
+ /* Value to write to register SW2 register set (0xC7003140) after the
+ command is cleared */
+ u32 cpu2apu_irq_ack;
+ u32 reserved2[13];
+
+ u32 hpu2cpu_mb_offset;
+ u32 hpu2cpu_irq;
+ u32 cpu2hpu_irq_ack;
+ u32 reserved3[13];
+
+ u32 ppu2cpu_mb_offset;
+ u32 ppu2cpu_irq;
+ u32 cpu2ppu_irq_ack;
+ u32 reserved4[13];
+
+ u32 epu2cpu_mb_offset;
+ u32 epu2cpu_irq;
+ u32 cpu2epu_irq_ack;
+ u32 reserved5[13];
+ u32 reserved6[8];
+
+ /* Fields for APU: */
+
+ u32 apu_state;
+ u32 reserved11[7];
+ u32 cpu2apu_mb_offset;
+ u32 cpu2apu_irq;
+ u32 apu2cpu_irq_ack;
+ u32 reserved12[13];
+
+ u32 hpu2apu_mb_offset;
+ u32 hpu2apu_irq;
+ u32 apu2hpu_irq_ack;
+ u32 reserved13[13];
+
+ u32 ppu2apu_mb_offset;
+ u32 ppu2apu_irq;
+ u32 apu2ppu_irq_ack;
+ u32 reserved14[13];
+
+ u32 epu2apu_mb_offset;
+ u32 epu2apu_irq;
+ u32 apu2epu_irq_ack;
+ u32 reserved15[13];
+ u32 reserved16[8];
+
+ /* Fields for HPU: */
+
+ u32 hpu_state;
+ u32 reserved21[7];
+ u32 cpu2hpu_mb_offset;
+ u32 cpu2hpu_irq;
+ u32 hpu2cpu_irq_ack;
+ u32 reserved22[13];
+
+ u32 apu2hpu_mb_offset;
+ u32 apu2hpu_irq;
+ u32 hpu2apu_irq_ack;
+ u32 reserved23[13];
+
+ u32 ppu2hpu_mb_offset;
+ u32 ppu2hpu_irq;
+ u32 hpu2ppu_irq_ack;
+ u32 reserved24[13];
+
+ u32 epu2hpu_mb_offset;
+ u32 epu2hpu_irq;
+ u32 hpu2epu_irq_ack;
+ u32 reserved25[13];
+ u32 reserved26[8];
+
+ /* Fields for PPU: */
+
+ u32 ppu_state;
+ u32 reserved31[7];
+ u32 cpu2ppu_mb_offset;
+ u32 cpu2ppu_irq;
+ u32 ppu2cpu_irq_ack;
+ u32 reserved32[13];
+
+ u32 apu2ppu_mb_offset;
+ u32 apu2ppu_irq;
+ u32 ppu2apu_irq_ack;
+ u32 reserved33[13];
+
+ u32 hpu2ppu_mb_offset;
+ u32 hpu2ppu_irq;
+ u32 ppu2hpu_irq_ack;
+ u32 reserved34[13];
+
+ u32 epu2ppu_mb_offset;
+ u32 epu2ppu_irq;
+ u32 ppu2epu_irq_ack;
+ u32 reserved35[13];
+ u32 reserved36[8];
+
+ /* Fields for EPU: */
+
+ u32 epu_state;
+ u32 reserved41[7];
+ u32 cpu2epu_mb_offset;
+ u32 cpu2epu_irq;
+ u32 epu2cpu_irq_ack;
+ u32 reserved42[13];
+
+ u32 apu2epu_mb_offset;
+ u32 apu2epu_irq;
+ u32 epu2apu_irq_ack;
+ u32 reserved43[13];
+
+ u32 hpu2epu_mb_offset;
+ u32 hpu2epu_irq;
+ u32 epu2hpu_irq_ack;
+ u32 reserved44[13];
+
+ u32 ppu2epu_mb_offset;
+ u32 ppu2epu_irq;
+ u32 epu2ppu_irq_ack;
+ u32 reserved45[13];
+ u32 reserved46[8];
+
+ u32 semaphores[8]; /* Semaphores */
+
+ u32 reserved50[32]; /* Reserved for future use */
+
+ struct cx18_mailbox apu2cpu_mb;
+ struct cx18_mailbox hpu2cpu_mb;
+ struct cx18_mailbox ppu2cpu_mb;
+ struct cx18_mailbox epu2cpu_mb;
+
+ struct cx18_mailbox cpu2apu_mb;
+ struct cx18_mailbox hpu2apu_mb;
+ struct cx18_mailbox ppu2apu_mb;
+ struct cx18_mailbox epu2apu_mb;
+
+ struct cx18_mailbox cpu2hpu_mb;
+ struct cx18_mailbox apu2hpu_mb;
+ struct cx18_mailbox ppu2hpu_mb;
+ struct cx18_mailbox epu2hpu_mb;
+
+ struct cx18_mailbox cpu2ppu_mb;
+ struct cx18_mailbox apu2ppu_mb;
+ struct cx18_mailbox hpu2ppu_mb;
+ struct cx18_mailbox epu2ppu_mb;
+
+ struct cx18_mailbox cpu2epu_mb;
+ struct cx18_mailbox apu2epu_mb;
+ struct cx18_mailbox hpu2epu_mb;
+ struct cx18_mailbox ppu2epu_mb;
+
+ struct cx18_mdl_ack cpu_mdl_ack[CX18_MAX_STREAMS][CX18_MAX_MDL_ACKS];
+ struct cx18_mdl_ent cpu_mdl[1];
+};
+
+void cx18_init_scb(struct cx18 *cx);
+
+#endif
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c
new file mode 100644
index 00000000..638cca15
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-streams.c
@@ -0,0 +1,1063 @@
+/*
+ * cx18 init/start/stop/exit stream functions
+ *
+ * Derived from ivtv-streams.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-fileops.h"
+#include "cx18-mailbox.h"
+#include "cx18-i2c.h"
+#include "cx18-queue.h"
+#include "cx18-ioctl.h"
+#include "cx18-streams.h"
+#include "cx18-cards.h"
+#include "cx18-scb.h"
+#include "cx18-dvb.h"
+
+#define CX18_DSP0_INTERRUPT_MASK 0xd0004C
+
+static struct v4l2_file_operations cx18_v4l2_enc_fops = {
+ .owner = THIS_MODULE,
+ .read = cx18_v4l2_read,
+ .open = cx18_v4l2_open,
+ /* FIXME change to video_ioctl2 if serialization lock can be removed */
+ .unlocked_ioctl = cx18_v4l2_ioctl,
+ .release = cx18_v4l2_close,
+ .poll = cx18_v4l2_enc_poll,
+ .mmap = cx18_v4l2_mmap,
+};
+
+/* offset from 0 to register ts v4l2 minors on */
+#define CX18_V4L2_ENC_TS_OFFSET 16
+/* offset from 0 to register pcm v4l2 minors on */
+#define CX18_V4L2_ENC_PCM_OFFSET 24
+/* offset from 0 to register yuv v4l2 minors on */
+#define CX18_V4L2_ENC_YUV_OFFSET 32
+
+static struct {
+ const char *name;
+ int vfl_type;
+ int num_offset;
+ int dma;
+ enum v4l2_buf_type buf_type;
+} cx18_stream_info[] = {
+ { /* CX18_ENC_STREAM_TYPE_MPG */
+ "encoder MPEG",
+ VFL_TYPE_GRABBER, 0,
+ PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ },
+ { /* CX18_ENC_STREAM_TYPE_TS */
+ "TS",
+ VFL_TYPE_GRABBER, -1,
+ PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ },
+ { /* CX18_ENC_STREAM_TYPE_YUV */
+ "encoder YUV",
+ VFL_TYPE_GRABBER, CX18_V4L2_ENC_YUV_OFFSET,
+ PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ },
+ { /* CX18_ENC_STREAM_TYPE_VBI */
+ "encoder VBI",
+ VFL_TYPE_VBI, 0,
+ PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VBI_CAPTURE,
+ },
+ { /* CX18_ENC_STREAM_TYPE_PCM */
+ "encoder PCM audio",
+ VFL_TYPE_GRABBER, CX18_V4L2_ENC_PCM_OFFSET,
+ PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_PRIVATE,
+ },
+ { /* CX18_ENC_STREAM_TYPE_IDX */
+ "encoder IDX",
+ VFL_TYPE_GRABBER, -1,
+ PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ },
+ { /* CX18_ENC_STREAM_TYPE_RAD */
+ "encoder radio",
+ VFL_TYPE_RADIO, 0,
+ PCI_DMA_NONE, V4L2_BUF_TYPE_PRIVATE,
+ },
+};
+
+
+void cx18_dma_free(struct videobuf_queue *q,
+ struct cx18_stream *s, struct cx18_videobuf_buffer *buf)
+{
+ videobuf_waiton(q, &buf->vb, 0, 0);
+ videobuf_vmalloc_free(&buf->vb);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int cx18_prepare_buffer(struct videobuf_queue *q,
+ struct cx18_stream *s,
+ struct cx18_videobuf_buffer *buf,
+ u32 pixelformat,
+ unsigned int width, unsigned int height,
+ enum v4l2_field field)
+{
+ struct cx18 *cx = s->cx;
+ int rc = 0;
+
+ /* check settings */
+ buf->bytes_used = 0;
+
+ if ((width < 48) || (height < 32))
+ return -EINVAL;
+
+ buf->vb.size = (width * height * 2);
+ if ((buf->vb.baddr != 0) && (buf->vb.bsize < buf->vb.size))
+ return -EINVAL;
+
+ /* alloc + fill struct (if changed) */
+ if (buf->vb.width != width || buf->vb.height != height ||
+ buf->vb.field != field || s->pixelformat != pixelformat ||
+ buf->tvnorm != cx->std) {
+
+ buf->vb.width = width;
+ buf->vb.height = height;
+ buf->vb.field = field;
+ buf->tvnorm = cx->std;
+ s->pixelformat = pixelformat;
+
+ /* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
+ UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */
+ if (s->pixelformat == V4L2_PIX_FMT_HM12)
+ s->vb_bytes_per_frame = height * 720 * 3 / 2;
+ else
+ s->vb_bytes_per_frame = height * 720 * 2;
+ cx18_dma_free(q, s, buf);
+ }
+
+ if ((buf->vb.baddr != 0) && (buf->vb.bsize < buf->vb.size))
+ return -EINVAL;
+
+ if (buf->vb.field == 0)
+ buf->vb.field = V4L2_FIELD_INTERLACED;
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ buf->vb.width = width;
+ buf->vb.height = height;
+ buf->vb.field = field;
+ buf->tvnorm = cx->std;
+ s->pixelformat = pixelformat;
+
+ /* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
+ UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */
+ if (s->pixelformat == V4L2_PIX_FMT_HM12)
+ s->vb_bytes_per_frame = height * 720 * 3 / 2;
+ else
+ s->vb_bytes_per_frame = height * 720 * 2;
+ rc = videobuf_iolock(q, &buf->vb, NULL);
+ if (rc != 0)
+ goto fail;
+ }
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+fail:
+ cx18_dma_free(q, s, buf);
+ return rc;
+
+}
+
+/* VB_MIN_BUFSIZE is lcm(1440 * 480, 1440 * 576)
+ 1440 is a single line of 4:2:2 YUV at 720 luma samples wide
+*/
+#define VB_MIN_BUFFERS 32
+#define VB_MIN_BUFSIZE 4147200
+
+static int buffer_setup(struct videobuf_queue *q,
+ unsigned int *count, unsigned int *size)
+{
+ struct cx18_stream *s = q->priv_data;
+ struct cx18 *cx = s->cx;
+
+ *size = 2 * cx->cxhdl.width * cx->cxhdl.height;
+ if (*count == 0)
+ *count = VB_MIN_BUFFERS;
+
+ while (*size * *count > VB_MIN_BUFFERS * VB_MIN_BUFSIZE)
+ (*count)--;
+
+ q->field = V4L2_FIELD_INTERLACED;
+ q->last = V4L2_FIELD_INTERLACED;
+
+ return 0;
+}
+
+static int buffer_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct cx18_videobuf_buffer *buf =
+ container_of(vb, struct cx18_videobuf_buffer, vb);
+ struct cx18_stream *s = q->priv_data;
+ struct cx18 *cx = s->cx;
+
+ return cx18_prepare_buffer(q, s, buf, s->pixelformat,
+ cx->cxhdl.width, cx->cxhdl.height, field);
+}
+
+static void buffer_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ struct cx18_videobuf_buffer *buf =
+ container_of(vb, struct cx18_videobuf_buffer, vb);
+ struct cx18_stream *s = q->priv_data;
+
+ cx18_dma_free(q, s, buf);
+}
+
+static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct cx18_videobuf_buffer *buf =
+ container_of(vb, struct cx18_videobuf_buffer, vb);
+ struct cx18_stream *s = q->priv_data;
+
+ buf->vb.state = VIDEOBUF_QUEUED;
+
+ list_add_tail(&buf->vb.queue, &s->vb_capture);
+}
+
+static struct videobuf_queue_ops cx18_videobuf_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+static void cx18_stream_init(struct cx18 *cx, int type)
+{
+ struct cx18_stream *s = &cx->streams[type];
+ struct video_device *video_dev = s->video_dev;
+
+ /* we need to keep video_dev, so restore it afterwards */
+ memset(s, 0, sizeof(*s));
+ s->video_dev = video_dev;
+
+ /* initialize cx18_stream fields */
+ s->dvb = NULL;
+ s->cx = cx;
+ s->type = type;
+ s->name = cx18_stream_info[type].name;
+ s->handle = CX18_INVALID_TASK_HANDLE;
+
+ s->dma = cx18_stream_info[type].dma;
+ s->buffers = cx->stream_buffers[type];
+ s->buf_size = cx->stream_buf_size[type];
+ INIT_LIST_HEAD(&s->buf_pool);
+ s->bufs_per_mdl = 1;
+ s->mdl_size = s->buf_size * s->bufs_per_mdl;
+
+ init_waitqueue_head(&s->waitq);
+ s->id = -1;
+ spin_lock_init(&s->q_free.lock);
+ cx18_queue_init(&s->q_free);
+ spin_lock_init(&s->q_busy.lock);
+ cx18_queue_init(&s->q_busy);
+ spin_lock_init(&s->q_full.lock);
+ cx18_queue_init(&s->q_full);
+ spin_lock_init(&s->q_idle.lock);
+ cx18_queue_init(&s->q_idle);
+
+ INIT_WORK(&s->out_work_order, cx18_out_work_handler);
+
+ INIT_LIST_HEAD(&s->vb_capture);
+ s->vb_timeout.function = cx18_vb_timeout;
+ s->vb_timeout.data = (unsigned long)s;
+ init_timer(&s->vb_timeout);
+ spin_lock_init(&s->vb_lock);
+ if (type == CX18_ENC_STREAM_TYPE_YUV) {
+ spin_lock_init(&s->vbuf_q_lock);
+
+ s->vb_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ videobuf_queue_vmalloc_init(&s->vbuf_q, &cx18_videobuf_qops,
+ &cx->pci_dev->dev, &s->vbuf_q_lock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct cx18_videobuf_buffer),
+ s, &cx->serialize_lock);
+
+ /* Assume the previous pixel default */
+ s->pixelformat = V4L2_PIX_FMT_HM12;
+ s->vb_bytes_per_frame = cx->cxhdl.height * 720 * 3 / 2;
+ }
+}
+
+static int cx18_prep_dev(struct cx18 *cx, int type)
+{
+ struct cx18_stream *s = &cx->streams[type];
+ u32 cap = cx->v4l2_cap;
+ int num_offset = cx18_stream_info[type].num_offset;
+ int num = cx->instance + cx18_first_minor + num_offset;
+
+ /*
+ * These five fields are always initialized.
+ * For analog capture related streams, if video_dev == NULL then the
+ * stream is not in use.
+ * For the TS stream, if dvb == NULL then the stream is not in use.
+ * In those cases no other fields but these four can be used.
+ */
+ s->video_dev = NULL;
+ s->dvb = NULL;
+ s->cx = cx;
+ s->type = type;
+ s->name = cx18_stream_info[type].name;
+
+ /* Check whether the radio is supported */
+ if (type == CX18_ENC_STREAM_TYPE_RAD && !(cap & V4L2_CAP_RADIO))
+ return 0;
+
+ /* Check whether VBI is supported */
+ if (type == CX18_ENC_STREAM_TYPE_VBI &&
+ !(cap & (V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE)))
+ return 0;
+
+ /* User explicitly selected 0 buffers for these streams, so don't
+ create them. */
+ if (cx18_stream_info[type].dma != PCI_DMA_NONE &&
+ cx->stream_buffers[type] == 0) {
+ CX18_INFO("Disabled %s device\n", cx18_stream_info[type].name);
+ return 0;
+ }
+
+ cx18_stream_init(cx, type);
+
+ /* Allocate the cx18_dvb struct only for the TS on cards with DTV */
+ if (type == CX18_ENC_STREAM_TYPE_TS) {
+ if (cx->card->hw_all & CX18_HW_DVB) {
+ s->dvb = kzalloc(sizeof(struct cx18_dvb), GFP_KERNEL);
+ if (s->dvb == NULL) {
+ CX18_ERR("Couldn't allocate cx18_dvb structure"
+ " for %s\n", s->name);
+ return -ENOMEM;
+ }
+ } else {
+ /* Don't need buffers for the TS, if there is no DVB */
+ s->buffers = 0;
+ }
+ }
+
+ if (num_offset == -1)
+ return 0;
+
+ /* allocate and initialize the v4l2 video device structure */
+ s->video_dev = video_device_alloc();
+ if (s->video_dev == NULL) {
+ CX18_ERR("Couldn't allocate v4l2 video_device for %s\n",
+ s->name);
+ return -ENOMEM;
+ }
+
+ snprintf(s->video_dev->name, sizeof(s->video_dev->name), "%s %s",
+ cx->v4l2_dev.name, s->name);
+
+ s->video_dev->num = num;
+ s->video_dev->v4l2_dev = &cx->v4l2_dev;
+ s->video_dev->fops = &cx18_v4l2_enc_fops;
+ s->video_dev->release = video_device_release;
+ s->video_dev->tvnorms = V4L2_STD_ALL;
+ set_bit(V4L2_FL_USE_FH_PRIO, &s->video_dev->flags);
+ cx18_set_funcs(s->video_dev);
+ return 0;
+}
+
+/* Initialize v4l2 variables and register v4l2 devices */
+int cx18_streams_setup(struct cx18 *cx)
+{
+ int type, ret;
+
+ /* Setup V4L2 Devices */
+ for (type = 0; type < CX18_MAX_STREAMS; type++) {
+ /* Prepare device */
+ ret = cx18_prep_dev(cx, type);
+ if (ret < 0)
+ break;
+
+ /* Allocate Stream */
+ ret = cx18_stream_alloc(&cx->streams[type]);
+ if (ret < 0)
+ break;
+ }
+ if (type == CX18_MAX_STREAMS)
+ return 0;
+
+ /* One or more streams could not be initialized. Clean 'em all up. */
+ cx18_streams_cleanup(cx, 0);
+ return ret;
+}
+
+static int cx18_reg_dev(struct cx18 *cx, int type)
+{
+ struct cx18_stream *s = &cx->streams[type];
+ int vfl_type = cx18_stream_info[type].vfl_type;
+ const char *name;
+ int num, ret;
+
+ if (type == CX18_ENC_STREAM_TYPE_TS && s->dvb != NULL) {
+ ret = cx18_dvb_register(s);
+ if (ret < 0) {
+ CX18_ERR("DVB failed to register\n");
+ return ret;
+ }
+ }
+
+ if (s->video_dev == NULL)
+ return 0;
+
+ num = s->video_dev->num;
+ /* card number + user defined offset + device offset */
+ if (type != CX18_ENC_STREAM_TYPE_MPG) {
+ struct cx18_stream *s_mpg = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];
+
+ if (s_mpg->video_dev)
+ num = s_mpg->video_dev->num
+ + cx18_stream_info[type].num_offset;
+ }
+ video_set_drvdata(s->video_dev, s);
+
+ /* Register device. First try the desired minor, then any free one. */
+ ret = video_register_device_no_warn(s->video_dev, vfl_type, num);
+ if (ret < 0) {
+ CX18_ERR("Couldn't register v4l2 device for %s (device node number %d)\n",
+ s->name, num);
+ video_device_release(s->video_dev);
+ s->video_dev = NULL;
+ return ret;
+ }
+
+ name = video_device_node_name(s->video_dev);
+
+ switch (vfl_type) {
+ case VFL_TYPE_GRABBER:
+ CX18_INFO("Registered device %s for %s (%d x %d.%02d kB)\n",
+ name, s->name, cx->stream_buffers[type],
+ cx->stream_buf_size[type] / 1024,
+ (cx->stream_buf_size[type] * 100 / 1024) % 100);
+ break;
+
+ case VFL_TYPE_RADIO:
+ CX18_INFO("Registered device %s for %s\n", name, s->name);
+ break;
+
+ case VFL_TYPE_VBI:
+ if (cx->stream_buffers[type])
+ CX18_INFO("Registered device %s for %s "
+ "(%d x %d bytes)\n",
+ name, s->name, cx->stream_buffers[type],
+ cx->stream_buf_size[type]);
+ else
+ CX18_INFO("Registered device %s for %s\n",
+ name, s->name);
+ break;
+ }
+
+ return 0;
+}
+
+/* Register v4l2 devices */
+int cx18_streams_register(struct cx18 *cx)
+{
+ int type;
+ int err;
+ int ret = 0;
+
+ /* Register V4L2 devices */
+ for (type = 0; type < CX18_MAX_STREAMS; type++) {
+ err = cx18_reg_dev(cx, type);
+ if (err && ret == 0)
+ ret = err;
+ }
+
+ if (ret == 0)
+ return 0;
+
+ /* One or more streams could not be initialized. Clean 'em all up. */
+ cx18_streams_cleanup(cx, 1);
+ return ret;
+}
+
+/* Unregister v4l2 devices */
+void cx18_streams_cleanup(struct cx18 *cx, int unregister)
+{
+ struct video_device *vdev;
+ int type;
+
+ /* Teardown all streams */
+ for (type = 0; type < CX18_MAX_STREAMS; type++) {
+
+ /* The TS has a cx18_dvb structure, not a video_device */
+ if (type == CX18_ENC_STREAM_TYPE_TS) {
+ if (cx->streams[type].dvb != NULL) {
+ if (unregister)
+ cx18_dvb_unregister(&cx->streams[type]);
+ kfree(cx->streams[type].dvb);
+ cx->streams[type].dvb = NULL;
+ cx18_stream_free(&cx->streams[type]);
+ }
+ continue;
+ }
+
+ /* No struct video_device, but can have buffers allocated */
+ if (type == CX18_ENC_STREAM_TYPE_IDX) {
+ /* If the module params didn't inhibit IDX ... */
+ if (cx->stream_buffers[type] != 0) {
+ cx->stream_buffers[type] = 0;
+ /*
+ * Before calling cx18_stream_free(),
+ * check if the IDX stream was actually set up.
+ * Needed, since the cx18_probe() error path
+ * exits through here as well as normal clean up
+ */
+ if (cx->streams[type].buffers != 0)
+ cx18_stream_free(&cx->streams[type]);
+ }
+ continue;
+ }
+
+ /* If struct video_device exists, can have buffers allocated */
+ vdev = cx->streams[type].video_dev;
+
+ cx->streams[type].video_dev = NULL;
+ if (vdev == NULL)
+ continue;
+
+ if (type == CX18_ENC_STREAM_TYPE_YUV)
+ videobuf_mmap_free(&cx->streams[type].vbuf_q);
+
+ cx18_stream_free(&cx->streams[type]);
+
+ /* Unregister or release device */
+ if (unregister)
+ video_unregister_device(vdev);
+ else
+ video_device_release(vdev);
+ }
+}
+
+static void cx18_vbi_setup(struct cx18_stream *s)
+{
+ struct cx18 *cx = s->cx;
+ int raw = cx18_raw_vbi(cx);
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ int lines;
+
+ if (cx->is_60hz) {
+ cx->vbi.count = 12;
+ cx->vbi.start[0] = 10;
+ cx->vbi.start[1] = 273;
+ } else { /* PAL/SECAM */
+ cx->vbi.count = 18;
+ cx->vbi.start[0] = 6;
+ cx->vbi.start[1] = 318;
+ }
+
+ /* setup VBI registers */
+ if (raw)
+ v4l2_subdev_call(cx->sd_av, vbi, s_raw_fmt, &cx->vbi.in.fmt.vbi);
+ else
+ v4l2_subdev_call(cx->sd_av, vbi, s_sliced_fmt, &cx->vbi.in.fmt.sliced);
+
+ /*
+ * Send the CX18_CPU_SET_RAW_VBI_PARAM API command to setup Encoder Raw
+ * VBI when the first analog capture channel starts, as once it starts
+ * (e.g. MPEG), we can't effect any change in the Encoder Raw VBI setup
+ * (i.e. for the VBI capture channels). We also send it for each
+ * analog capture channel anyway just to make sure we get the proper
+ * behavior
+ */
+ if (raw) {
+ lines = cx->vbi.count * 2;
+ } else {
+ /*
+ * For 525/60 systems, according to the VIP 2 & BT.656 std:
+ * The EAV RP code's Field bit toggles on line 4, a few lines
+ * after the Vertcal Blank bit has already toggled.
+ * Tell the encoder to capture 21-4+1=18 lines per field,
+ * since we want lines 10 through 21.
+ *
+ * For 625/50 systems, according to the VIP 2 & BT.656 std:
+ * The EAV RP code's Field bit toggles on line 1, a few lines
+ * after the Vertcal Blank bit has already toggled.
+ * (We've actually set the digitizer so that the Field bit
+ * toggles on line 2.) Tell the encoder to capture 23-2+1=22
+ * lines per field, since we want lines 6 through 23.
+ */
+ lines = cx->is_60hz ? (21 - 4 + 1) * 2 : (23 - 2 + 1) * 2;
+ }
+
+ data[0] = s->handle;
+ /* Lines per field */
+ data[1] = (lines / 2) | ((lines / 2) << 16);
+ /* bytes per line */
+ data[2] = (raw ? vbi_active_samples
+ : (cx->is_60hz ? vbi_hblank_samples_60Hz
+ : vbi_hblank_samples_50Hz));
+ /* Every X number of frames a VBI interrupt arrives
+ (frames as in 25 or 30 fps) */
+ data[3] = 1;
+ /*
+ * Set the SAV/EAV RP codes to look for as start/stop points
+ * when in VIP-1.1 mode
+ */
+ if (raw) {
+ /*
+ * Start codes for beginning of "active" line in vertical blank
+ * 0x20 ( VerticalBlank )
+ * 0x60 ( EvenField VerticalBlank )
+ */
+ data[4] = 0x20602060;
+ /*
+ * End codes for end of "active" raw lines and regular lines
+ * 0x30 ( VerticalBlank HorizontalBlank)
+ * 0x70 ( EvenField VerticalBlank HorizontalBlank)
+ * 0x90 (Task HorizontalBlank)
+ * 0xd0 (Task EvenField HorizontalBlank)
+ */
+ data[5] = 0x307090d0;
+ } else {
+ /*
+ * End codes for active video, we want data in the hblank region
+ * 0xb0 (Task 0 VerticalBlank HorizontalBlank)
+ * 0xf0 (Task EvenField VerticalBlank HorizontalBlank)
+ *
+ * Since the V bit is only allowed to toggle in the EAV RP code,
+ * just before the first active region line, these two
+ * are problematic:
+ * 0x90 (Task HorizontalBlank)
+ * 0xd0 (Task EvenField HorizontalBlank)
+ *
+ * We have set the digitzer such that we don't have to worry
+ * about these problem codes.
+ */
+ data[4] = 0xB0F0B0F0;
+ /*
+ * Start codes for beginning of active line in vertical blank
+ * 0xa0 (Task VerticalBlank )
+ * 0xe0 (Task EvenField VerticalBlank )
+ */
+ data[5] = 0xA0E0A0E0;
+ }
+
+ CX18_DEBUG_INFO("Setup VBI h: %d lines %x bpl %d fr %d %x %x\n",
+ data[0], data[1], data[2], data[3], data[4], data[5]);
+
+ cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data);
+}
+
+void cx18_stream_rotate_idx_mdls(struct cx18 *cx)
+{
+ struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
+ struct cx18_mdl *mdl;
+
+ if (!cx18_stream_enabled(s))
+ return;
+
+ /* Return if the firmware is not running low on MDLs */
+ if ((atomic_read(&s->q_free.depth) + atomic_read(&s->q_busy.depth)) >=
+ CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN)
+ return;
+
+ /* Return if there are no MDLs to rotate back to the firmware */
+ if (atomic_read(&s->q_full.depth) < 2)
+ return;
+
+ /*
+ * Take the oldest IDX MDL still holding data, and discard its index
+ * entries by scheduling the MDL to go back to the firmware
+ */
+ mdl = cx18_dequeue(s, &s->q_full);
+ if (mdl != NULL)
+ cx18_enqueue(s, mdl, &s->q_free);
+}
+
+static
+struct cx18_queue *_cx18_stream_put_mdl_fw(struct cx18_stream *s,
+ struct cx18_mdl *mdl)
+{
+ struct cx18 *cx = s->cx;
+ struct cx18_queue *q;
+
+ /* Don't give it to the firmware, if we're not running a capture */
+ if (s->handle == CX18_INVALID_TASK_HANDLE ||
+ test_bit(CX18_F_S_STOPPING, &s->s_flags) ||
+ !test_bit(CX18_F_S_STREAMING, &s->s_flags))
+ return cx18_enqueue(s, mdl, &s->q_free);
+
+ q = cx18_enqueue(s, mdl, &s->q_busy);
+ if (q != &s->q_busy)
+ return q; /* The firmware has the max MDLs it can handle */
+
+ cx18_mdl_sync_for_device(s, mdl);
+ cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
+ (void __iomem *) &cx->scb->cpu_mdl[mdl->id] - cx->enc_mem,
+ s->bufs_per_mdl, mdl->id, s->mdl_size);
+ return q;
+}
+
+static
+void _cx18_stream_load_fw_queue(struct cx18_stream *s)
+{
+ struct cx18_queue *q;
+ struct cx18_mdl *mdl;
+
+ if (atomic_read(&s->q_free.depth) == 0 ||
+ atomic_read(&s->q_busy.depth) >= CX18_MAX_FW_MDLS_PER_STREAM)
+ return;
+
+ /* Move from q_free to q_busy notifying the firmware, until the limit */
+ do {
+ mdl = cx18_dequeue(s, &s->q_free);
+ if (mdl == NULL)
+ break;
+ q = _cx18_stream_put_mdl_fw(s, mdl);
+ } while (atomic_read(&s->q_busy.depth) < CX18_MAX_FW_MDLS_PER_STREAM
+ && q == &s->q_busy);
+}
+
+void cx18_out_work_handler(struct work_struct *work)
+{
+ struct cx18_stream *s =
+ container_of(work, struct cx18_stream, out_work_order);
+
+ _cx18_stream_load_fw_queue(s);
+}
+
+static void cx18_stream_configure_mdls(struct cx18_stream *s)
+{
+ cx18_unload_queues(s);
+
+ switch (s->type) {
+ case CX18_ENC_STREAM_TYPE_YUV:
+ /*
+ * Height should be a multiple of 32 lines.
+ * Set the MDL size to the exact size needed for one frame.
+ * Use enough buffers per MDL to cover the MDL size
+ */
+ if (s->pixelformat == V4L2_PIX_FMT_HM12)
+ s->mdl_size = 720 * s->cx->cxhdl.height * 3 / 2;
+ else
+ s->mdl_size = 720 * s->cx->cxhdl.height * 2;
+ s->bufs_per_mdl = s->mdl_size / s->buf_size;
+ if (s->mdl_size % s->buf_size)
+ s->bufs_per_mdl++;
+ break;
+ case CX18_ENC_STREAM_TYPE_VBI:
+ s->bufs_per_mdl = 1;
+ if (cx18_raw_vbi(s->cx)) {
+ s->mdl_size = (s->cx->is_60hz ? 12 : 18)
+ * 2 * vbi_active_samples;
+ } else {
+ /*
+ * See comment in cx18_vbi_setup() below about the
+ * extra lines we capture in sliced VBI mode due to
+ * the lines on which EAV RP codes toggle.
+ */
+ s->mdl_size = s->cx->is_60hz
+ ? (21 - 4 + 1) * 2 * vbi_hblank_samples_60Hz
+ : (23 - 2 + 1) * 2 * vbi_hblank_samples_50Hz;
+ }
+ break;
+ default:
+ s->bufs_per_mdl = 1;
+ s->mdl_size = s->buf_size * s->bufs_per_mdl;
+ break;
+ }
+
+ cx18_load_queues(s);
+}
+
+int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
+{
+ u32 data[MAX_MB_ARGUMENTS];
+ struct cx18 *cx = s->cx;
+ int captype = 0;
+ struct cx18_stream *s_idx;
+
+ if (!cx18_stream_enabled(s))
+ return -EINVAL;
+
+ CX18_DEBUG_INFO("Start encoder stream %s\n", s->name);
+
+ switch (s->type) {
+ case CX18_ENC_STREAM_TYPE_MPG:
+ captype = CAPTURE_CHANNEL_TYPE_MPEG;
+ cx->mpg_data_received = cx->vbi_data_inserted = 0;
+ cx->dualwatch_jiffies = jiffies;
+ cx->dualwatch_stereo_mode = v4l2_ctrl_g_ctrl(cx->cxhdl.audio_mode);
+ cx->search_pack_header = 0;
+ break;
+
+ case CX18_ENC_STREAM_TYPE_IDX:
+ captype = CAPTURE_CHANNEL_TYPE_INDEX;
+ break;
+ case CX18_ENC_STREAM_TYPE_TS:
+ captype = CAPTURE_CHANNEL_TYPE_TS;
+ break;
+ case CX18_ENC_STREAM_TYPE_YUV:
+ captype = CAPTURE_CHANNEL_TYPE_YUV;
+ break;
+ case CX18_ENC_STREAM_TYPE_PCM:
+ captype = CAPTURE_CHANNEL_TYPE_PCM;
+ break;
+ case CX18_ENC_STREAM_TYPE_VBI:
+#ifdef CX18_ENCODER_PARSES_SLICED
+ captype = cx18_raw_vbi(cx) ?
+ CAPTURE_CHANNEL_TYPE_VBI : CAPTURE_CHANNEL_TYPE_SLICED_VBI;
+#else
+ /*
+ * Currently we set things up so that Sliced VBI from the
+ * digitizer is handled as Raw VBI by the encoder
+ */
+ captype = CAPTURE_CHANNEL_TYPE_VBI;
+#endif
+ cx->vbi.frame = 0;
+ cx->vbi.inserted_frame = 0;
+ memset(cx->vbi.sliced_mpeg_size,
+ 0, sizeof(cx->vbi.sliced_mpeg_size));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Clear Streamoff flags in case left from last capture */
+ clear_bit(CX18_F_S_STREAMOFF, &s->s_flags);
+
+ cx18_vapi_result(cx, data, CX18_CREATE_TASK, 1, CPU_CMD_MASK_CAPTURE);
+ s->handle = data[0];
+ cx18_vapi(cx, CX18_CPU_SET_CHANNEL_TYPE, 2, s->handle, captype);
+
+ /*
+ * For everything but CAPTURE_CHANNEL_TYPE_TS, play it safe and
+ * set up all the parameters, as it is not obvious which parameters the
+ * firmware shares across capture channel types and which it does not.
+ *
+ * Some of the cx18_vapi() calls below apply to only certain capture
+ * channel types. We're hoping there's no harm in calling most of them
+ * anyway, as long as the values are all consistent. Setting some
+ * shared parameters will have no effect once an analog capture channel
+ * has started streaming.
+ */
+ if (captype != CAPTURE_CHANNEL_TYPE_TS) {
+ cx18_vapi(cx, CX18_CPU_SET_VER_CROP_LINE, 2, s->handle, 0);
+ cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 3, 1);
+ cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 8, 0);
+ cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 4, 1);
+
+ /*
+ * Audio related reset according to
+ * Documentation/video4linux/cx2341x/fw-encoder-api.txt
+ */
+ if (atomic_read(&cx->ana_capturing) == 0)
+ cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2,
+ s->handle, 12);
+
+ /*
+ * Number of lines for Field 1 & Field 2 according to
+ * Documentation/video4linux/cx2341x/fw-encoder-api.txt
+ * Field 1 is 312 for 625 line systems in BT.656
+ * Field 2 is 313 for 625 line systems in BT.656
+ */
+ cx18_vapi(cx, CX18_CPU_SET_CAPTURE_LINE_NO, 3,
+ s->handle, 312, 313);
+
+ if (cx->v4l2_cap & V4L2_CAP_VBI_CAPTURE)
+ cx18_vbi_setup(s);
+
+ /*
+ * Select to receive I, P, and B frame index entries, if the
+ * index stream is enabled. Otherwise disable index entry
+ * generation.
+ */
+ s_idx = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
+ cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 2,
+ s->handle, cx18_stream_enabled(s_idx) ? 7 : 0);
+
+ /* Call out to the common CX2341x API setup for user controls */
+ cx->cxhdl.priv = s;
+ cx2341x_handler_setup(&cx->cxhdl);
+
+ /*
+ * When starting a capture and we're set for radio,
+ * ensure the video is muted, despite the user control.
+ */
+ if (!cx->cxhdl.video_mute &&
+ test_bit(CX18_F_I_RADIO_USER, &cx->i_flags))
+ cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle,
+ (v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute_yuv) << 8) | 1);
+
+ /* Enable the Video Format Converter for UYVY 4:2:2 support,
+ * rather than the default HM12 Macroblovk 4:2:0 support.
+ */
+ if (captype == CAPTURE_CHANNEL_TYPE_YUV) {
+ if (s->pixelformat == V4L2_PIX_FMT_UYVY)
+ cx18_vapi(cx, CX18_CPU_SET_VFC_PARAM, 2,
+ s->handle, 1);
+ else
+ /* If in doubt, default to HM12 */
+ cx18_vapi(cx, CX18_CPU_SET_VFC_PARAM, 2,
+ s->handle, 0);
+ }
+ }
+
+ if (atomic_read(&cx->tot_capturing) == 0) {
+ cx2341x_handler_set_busy(&cx->cxhdl, 1);
+ clear_bit(CX18_F_I_EOS, &cx->i_flags);
+ cx18_write_reg(cx, 7, CX18_DSP0_INTERRUPT_MASK);
+ }
+
+ cx18_vapi(cx, CX18_CPU_DE_SET_MDL_ACK, 3, s->handle,
+ (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][0] - cx->enc_mem,
+ (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem);
+
+ /* Init all the cpu_mdls for this stream */
+ cx18_stream_configure_mdls(s);
+ _cx18_stream_load_fw_queue(s);
+
+ /* begin_capture */
+ if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) {
+ CX18_DEBUG_WARN("Error starting capture!\n");
+ /* Ensure we're really not capturing before releasing MDLs */
+ set_bit(CX18_F_S_STOPPING, &s->s_flags);
+ if (s->type == CX18_ENC_STREAM_TYPE_MPG)
+ cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, 1);
+ else
+ cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle);
+ clear_bit(CX18_F_S_STREAMING, &s->s_flags);
+ /* FIXME - CX18_F_S_STREAMOFF as well? */
+ cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle);
+ cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
+ s->handle = CX18_INVALID_TASK_HANDLE;
+ clear_bit(CX18_F_S_STOPPING, &s->s_flags);
+ if (atomic_read(&cx->tot_capturing) == 0) {
+ set_bit(CX18_F_I_EOS, &cx->i_flags);
+ cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK);
+ }
+ return -EINVAL;
+ }
+
+ /* you're live! sit back and await interrupts :) */
+ if (captype != CAPTURE_CHANNEL_TYPE_TS)
+ atomic_inc(&cx->ana_capturing);
+ atomic_inc(&cx->tot_capturing);
+ return 0;
+}
+EXPORT_SYMBOL(cx18_start_v4l2_encode_stream);
+
+void cx18_stop_all_captures(struct cx18 *cx)
+{
+ int i;
+
+ for (i = CX18_MAX_STREAMS - 1; i >= 0; i--) {
+ struct cx18_stream *s = &cx->streams[i];
+
+ if (!cx18_stream_enabled(s))
+ continue;
+ if (test_bit(CX18_F_S_STREAMING, &s->s_flags))
+ cx18_stop_v4l2_encode_stream(s, 0);
+ }
+}
+
+int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end)
+{
+ struct cx18 *cx = s->cx;
+ unsigned long then;
+
+ if (!cx18_stream_enabled(s))
+ return -EINVAL;
+
+ /* This function assumes that you are allowed to stop the capture
+ and that we are actually capturing */
+
+ CX18_DEBUG_INFO("Stop Capture\n");
+
+ if (atomic_read(&cx->tot_capturing) == 0)
+ return 0;
+
+ set_bit(CX18_F_S_STOPPING, &s->s_flags);
+ if (s->type == CX18_ENC_STREAM_TYPE_MPG)
+ cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, !gop_end);
+ else
+ cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle);
+
+ then = jiffies;
+
+ if (s->type == CX18_ENC_STREAM_TYPE_MPG && gop_end) {
+ CX18_INFO("ignoring gop_end: not (yet?) supported by the firmware\n");
+ }
+
+ if (s->type != CX18_ENC_STREAM_TYPE_TS)
+ atomic_dec(&cx->ana_capturing);
+ atomic_dec(&cx->tot_capturing);
+
+ /* Clear capture and no-read bits */
+ clear_bit(CX18_F_S_STREAMING, &s->s_flags);
+
+ /* Tell the CX23418 it can't use our buffers anymore */
+ cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle);
+
+ cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
+ s->handle = CX18_INVALID_TASK_HANDLE;
+ clear_bit(CX18_F_S_STOPPING, &s->s_flags);
+
+ if (atomic_read(&cx->tot_capturing) > 0)
+ return 0;
+
+ cx2341x_handler_set_busy(&cx->cxhdl, 0);
+ cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK);
+ wake_up(&s->waitq);
+
+ return 0;
+}
+EXPORT_SYMBOL(cx18_stop_v4l2_encode_stream);
+
+u32 cx18_find_handle(struct cx18 *cx)
+{
+ int i;
+
+ /* find first available handle to be used for global settings */
+ for (i = 0; i < CX18_MAX_STREAMS; i++) {
+ struct cx18_stream *s = &cx->streams[i];
+
+ if (s->video_dev && (s->handle != CX18_INVALID_TASK_HANDLE))
+ return s->handle;
+ }
+ return CX18_INVALID_TASK_HANDLE;
+}
+
+struct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle)
+{
+ int i;
+ struct cx18_stream *s;
+
+ if (handle == CX18_INVALID_TASK_HANDLE)
+ return NULL;
+
+ for (i = 0; i < CX18_MAX_STREAMS; i++) {
+ s = &cx->streams[i];
+ if (s->handle != handle)
+ continue;
+ if (cx18_stream_enabled(s))
+ return s;
+ }
+ return NULL;
+}
diff --git a/drivers/media/video/cx18/cx18-streams.h b/drivers/media/video/cx18/cx18-streams.h
new file mode 100644
index 00000000..713b0e61
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-streams.h
@@ -0,0 +1,62 @@
+/*
+ * cx18 init/start/stop/exit stream functions
+ *
+ * Derived from ivtv-streams.h
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+u32 cx18_find_handle(struct cx18 *cx);
+struct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle);
+int cx18_streams_setup(struct cx18 *cx);
+int cx18_streams_register(struct cx18 *cx);
+void cx18_streams_cleanup(struct cx18 *cx, int unregister);
+
+#define CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN (3)
+void cx18_stream_rotate_idx_mdls(struct cx18 *cx);
+
+static inline bool cx18_stream_enabled(struct cx18_stream *s)
+{
+ return s->video_dev ||
+ (s->dvb && s->dvb->enabled) ||
+ (s->type == CX18_ENC_STREAM_TYPE_IDX &&
+ s->cx->stream_buffers[CX18_ENC_STREAM_TYPE_IDX] != 0);
+}
+
+/* Related to submission of mdls to firmware */
+static inline void cx18_stream_load_fw_queue(struct cx18_stream *s)
+{
+ schedule_work(&s->out_work_order);
+}
+
+static inline void cx18_stream_put_mdl_fw(struct cx18_stream *s,
+ struct cx18_mdl *mdl)
+{
+ /* Put mdl on q_free; the out work handler will move mdl(s) to q_busy */
+ cx18_enqueue(s, mdl, &s->q_free);
+ cx18_stream_load_fw_queue(s);
+}
+
+void cx18_out_work_handler(struct work_struct *work);
+
+/* Capture related */
+int cx18_start_v4l2_encode_stream(struct cx18_stream *s);
+int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end);
+
+void cx18_stop_all_captures(struct cx18 *cx);
diff --git a/drivers/media/video/cx18/cx18-vbi.c b/drivers/media/video/cx18/cx18-vbi.c
new file mode 100644
index 00000000..6d3121ff
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-vbi.c
@@ -0,0 +1,277 @@
+/*
+ * cx18 Vertical Blank Interval support functions
+ *
+ * Derived from ivtv-vbi.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include "cx18-driver.h"
+#include "cx18-vbi.h"
+#include "cx18-ioctl.h"
+#include "cx18-queue.h"
+
+/*
+ * Raster Reference/Protection (RP) bytes, used in Start/End Active
+ * Video codes emitted from the digitzer in VIP 1.x mode, that flag the start
+ * of VBI sample or VBI ancillary data regions in the digitial ratser line.
+ *
+ * Task FieldEven VerticalBlank HorizontalBlank 0 0 0 0
+ */
+static const u8 raw_vbi_sav_rp[2] = { 0x20, 0x60 }; /* __V_, _FV_ */
+static const u8 sliced_vbi_eav_rp[2] = { 0xb0, 0xf0 }; /* T_VH, TFVH */
+
+static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp)
+{
+ int line = 0;
+ int i;
+ u32 linemask[2] = { 0, 0 };
+ unsigned short size;
+ static const u8 mpeg_hdr_data[] = {
+ /* MPEG-2 Program Pack */
+ 0x00, 0x00, 0x01, 0xba, /* Prog Pack start code */
+ 0x44, 0x00, 0x0c, 0x66, 0x24, 0x01, /* SCR, SCR Ext, markers */
+ 0x01, 0xd1, 0xd3, /* Mux Rate, markers */
+ 0xfa, 0xff, 0xff, /* Res, Suff cnt, Stuff */
+ /* MPEG-2 Private Stream 1 PES Packet */
+ 0x00, 0x00, 0x01, 0xbd, /* Priv Stream 1 start */
+ 0x00, 0x1a, /* length */
+ 0x84, 0x80, 0x07, /* flags, hdr data len */
+ 0x21, 0x00, 0x5d, 0x63, 0xa7, /* PTS, markers */
+ 0xff, 0xff /* stuffing */
+ };
+ const int sd = sizeof(mpeg_hdr_data); /* start of vbi data */
+ int idx = cx->vbi.frame % CX18_VBI_FRAMES;
+ u8 *dst = &cx->vbi.sliced_mpeg_data[idx][0];
+
+ for (i = 0; i < lines; i++) {
+ struct v4l2_sliced_vbi_data *sdata = cx->vbi.sliced_data + i;
+ int f, l;
+
+ if (sdata->id == 0)
+ continue;
+
+ l = sdata->line - 6;
+ f = sdata->field;
+ if (f)
+ l += 18;
+ if (l < 32)
+ linemask[0] |= (1 << l);
+ else
+ linemask[1] |= (1 << (l - 32));
+ dst[sd + 12 + line * 43] = cx18_service2vbi(sdata->id);
+ memcpy(dst + sd + 12 + line * 43 + 1, sdata->data, 42);
+ line++;
+ }
+ memcpy(dst, mpeg_hdr_data, sizeof(mpeg_hdr_data));
+ if (line == 36) {
+ /* All lines are used, so there is no space for the linemask
+ (the max size of the VBI data is 36 * 43 + 4 bytes).
+ So in this case we use the magic number 'ITV0'. */
+ memcpy(dst + sd, "ITV0", 4);
+ memcpy(dst + sd + 4, dst + sd + 12, line * 43);
+ size = 4 + ((43 * line + 3) & ~3);
+ } else {
+ memcpy(dst + sd, "itv0", 4);
+ cpu_to_le32s(&linemask[0]);
+ cpu_to_le32s(&linemask[1]);
+ memcpy(dst + sd + 4, &linemask[0], 8);
+ size = 12 + ((43 * line + 3) & ~3);
+ }
+ dst[4+16] = (size + 10) >> 8;
+ dst[5+16] = (size + 10) & 0xff;
+ dst[9+16] = 0x21 | ((pts_stamp >> 29) & 0x6);
+ dst[10+16] = (pts_stamp >> 22) & 0xff;
+ dst[11+16] = 1 | ((pts_stamp >> 14) & 0xff);
+ dst[12+16] = (pts_stamp >> 7) & 0xff;
+ dst[13+16] = 1 | ((pts_stamp & 0x7f) << 1);
+ cx->vbi.sliced_mpeg_size[idx] = sd + size;
+}
+
+/* Compress raw VBI format, removes leading SAV codes and surplus space
+ after the frame. Returns new compressed size. */
+/* FIXME - this function ignores the input size. */
+static u32 compress_raw_buf(struct cx18 *cx, u8 *buf, u32 size, u32 hdr_size)
+{
+ u32 line_size = vbi_active_samples;
+ u32 lines = cx->vbi.count * 2;
+ u8 *q = buf;
+ u8 *p;
+ int i;
+
+ /* Skip the header */
+ buf += hdr_size;
+
+ for (i = 0; i < lines; i++) {
+ p = buf + i * line_size;
+
+ /* Look for SAV code */
+ if (p[0] != 0xff || p[1] || p[2] ||
+ (p[3] != raw_vbi_sav_rp[0] &&
+ p[3] != raw_vbi_sav_rp[1]))
+ break;
+ if (i == lines - 1) {
+ /* last line is hdr_size bytes short - extrapolate it */
+ memcpy(q, p + 4, line_size - 4 - hdr_size);
+ q += line_size - 4 - hdr_size;
+ p += line_size - hdr_size - 1;
+ memset(q, (int) *p, hdr_size);
+ } else {
+ memcpy(q, p + 4, line_size - 4);
+ q += line_size - 4;
+ }
+ }
+ return lines * (line_size - 4);
+}
+
+static u32 compress_sliced_buf(struct cx18 *cx, u8 *buf, u32 size,
+ const u32 hdr_size)
+{
+ struct v4l2_decode_vbi_line vbi;
+ int i;
+ u32 line = 0;
+ u32 line_size = cx->is_60hz ? vbi_hblank_samples_60Hz
+ : vbi_hblank_samples_50Hz;
+
+ /* find the first valid line */
+ for (i = hdr_size, buf += hdr_size; i < size; i++, buf++) {
+ if (buf[0] == 0xff && !buf[1] && !buf[2] &&
+ (buf[3] == sliced_vbi_eav_rp[0] ||
+ buf[3] == sliced_vbi_eav_rp[1]))
+ break;
+ }
+
+ /*
+ * The last line is short by hdr_size bytes, but for the remaining
+ * checks against size, we pretend that it is not, by counting the
+ * header bytes we knowingly skipped
+ */
+ size -= (i - hdr_size);
+ if (size < line_size)
+ return line;
+
+ for (i = 0; i < size / line_size; i++) {
+ u8 *p = buf + i * line_size;
+
+ /* Look for EAV code */
+ if (p[0] != 0xff || p[1] || p[2] ||
+ (p[3] != sliced_vbi_eav_rp[0] &&
+ p[3] != sliced_vbi_eav_rp[1]))
+ continue;
+ vbi.p = p + 4;
+ v4l2_subdev_call(cx->sd_av, vbi, decode_vbi_line, &vbi);
+ if (vbi.type) {
+ cx->vbi.sliced_data[line].id = vbi.type;
+ cx->vbi.sliced_data[line].field = vbi.is_second_field;
+ cx->vbi.sliced_data[line].line = vbi.line;
+ memcpy(cx->vbi.sliced_data[line].data, vbi.p, 42);
+ line++;
+ }
+ }
+ return line;
+}
+
+static void _cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf)
+{
+ /*
+ * The CX23418 provides a 12 byte header in its raw VBI buffers to us:
+ * 0x3fffffff [4 bytes of something] [4 byte presentation time stamp]
+ */
+ struct vbi_data_hdr {
+ __be32 magic;
+ __be32 unknown;
+ __be32 pts;
+ } *hdr = (struct vbi_data_hdr *) buf->buf;
+
+ u8 *p = (u8 *) buf->buf;
+ u32 size = buf->bytesused;
+ u32 pts;
+ int lines;
+
+ /*
+ * The CX23418 sends us data that is 32 bit little-endian swapped,
+ * but we want the raw VBI bytes in the order they were in the raster
+ * line. This has a side effect of making the header big endian
+ */
+ cx18_buf_swap(buf);
+
+ /* Raw VBI data */
+ if (cx18_raw_vbi(cx)) {
+
+ size = buf->bytesused =
+ compress_raw_buf(cx, p, size, sizeof(struct vbi_data_hdr));
+
+ /*
+ * Hack needed for compatibility with old VBI software.
+ * Write the frame # at the last 4 bytes of the frame
+ */
+ p += size - 4;
+ memcpy(p, &cx->vbi.frame, 4);
+ cx->vbi.frame++;
+ return;
+ }
+
+ /* Sliced VBI data with data insertion */
+
+ pts = (be32_to_cpu(hdr->magic) == 0x3fffffff) ? be32_to_cpu(hdr->pts)
+ : 0;
+
+ lines = compress_sliced_buf(cx, p, size, sizeof(struct vbi_data_hdr));
+
+ /* always return at least one empty line */
+ if (lines == 0) {
+ cx->vbi.sliced_data[0].id = 0;
+ cx->vbi.sliced_data[0].line = 0;
+ cx->vbi.sliced_data[0].field = 0;
+ lines = 1;
+ }
+ buf->bytesused = size = lines * sizeof(cx->vbi.sliced_data[0]);
+ memcpy(p, &cx->vbi.sliced_data[0], size);
+
+ if (cx->vbi.insert_mpeg)
+ copy_vbi_data(cx, lines, pts);
+ cx->vbi.frame++;
+}
+
+void cx18_process_vbi_data(struct cx18 *cx, struct cx18_mdl *mdl,
+ int streamtype)
+{
+ struct cx18_buffer *buf;
+ u32 orig_used;
+
+ if (streamtype != CX18_ENC_STREAM_TYPE_VBI)
+ return;
+
+ /*
+ * Big assumption here:
+ * Every buffer hooked to the MDL's buf_list is a complete VBI frame
+ * that ends at the end of the buffer.
+ *
+ * To assume anything else would make the code in this file
+ * more complex, or require extra memcpy()'s to make the
+ * buffers satisfy the above assumption. It's just simpler to set
+ * up the encoder buffer transfers to make the assumption true.
+ */
+ list_for_each_entry(buf, &mdl->buf_list, list) {
+ orig_used = buf->bytesused;
+ if (orig_used == 0)
+ break;
+ _cx18_process_vbi_data(cx, buf);
+ mdl->bytesused -= (orig_used - buf->bytesused);
+ }
+}
diff --git a/drivers/media/video/cx18/cx18-vbi.h b/drivers/media/video/cx18/cx18-vbi.h
new file mode 100644
index 00000000..b365cf4b
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-vbi.h
@@ -0,0 +1,26 @@
+/*
+ * cx18 Vertical Blank Interval support functions
+ *
+ * Derived from ivtv-vbi.h
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+void cx18_process_vbi_data(struct cx18 *cx, struct cx18_mdl *mdl,
+ int streamtype);
+int cx18_used_line(struct cx18 *cx, int line, int field);
diff --git a/drivers/media/video/cx18/cx18-version.h b/drivers/media/video/cx18/cx18-version.h
new file mode 100644
index 00000000..fed48b6b
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-version.h
@@ -0,0 +1,28 @@
+/*
+ * cx18 driver version information
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#ifndef CX18_VERSION_H
+#define CX18_VERSION_H
+
+#define CX18_DRIVER_NAME "cx18"
+#define CX18_VERSION "1.5.1"
+
+#endif
diff --git a/drivers/media/video/cx18/cx18-video.c b/drivers/media/video/cx18/cx18-video.c
new file mode 100644
index 00000000..6dc84aac
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-video.c
@@ -0,0 +1,32 @@
+/*
+ * cx18 video interface functions
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include "cx18-driver.h"
+#include "cx18-video.h"
+#include "cx18-cards.h"
+
+void cx18_video_set_io(struct cx18 *cx)
+{
+ int inp = cx->active_input;
+
+ v4l2_subdev_call(cx->sd_av, video, s_routing,
+ cx->card->video_inputs[inp].video_input, 0, 0);
+}
diff --git a/drivers/media/video/cx18/cx18-video.h b/drivers/media/video/cx18/cx18-video.h
new file mode 100644
index 00000000..529006a0
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-video.h
@@ -0,0 +1,22 @@
+/*
+ * cx18 video interface functions
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+void cx18_video_set_io(struct cx18 *cx);
diff --git a/drivers/media/video/cx18/cx23418.h b/drivers/media/video/cx18/cx23418.h
new file mode 100644
index 00000000..767a8d23
--- /dev/null
+++ b/drivers/media/video/cx18/cx23418.h
@@ -0,0 +1,492 @@
+/*
+ * cx18 header containing common defines.
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#ifndef CX23418_H
+#define CX23418_H
+
+#include <media/cx2341x.h>
+
+#define MGR_CMD_MASK 0x40000000
+/* The MSB of the command code indicates that this is the completion of a
+ command */
+#define MGR_CMD_MASK_ACK (MGR_CMD_MASK | 0x80000000)
+
+/* Description: This command creates a new instance of a certain task
+ IN[0] - Task ID. This is one of the XPU_CMD_MASK_YYY where XPU is
+ the processor on which the task YYY will be created
+ OUT[0] - Task handle. This handle is passed along with commands to
+ dispatch to the right instance of the task
+ ReturnCode - One of the ERR_SYS_... */
+#define CX18_CREATE_TASK (MGR_CMD_MASK | 0x0001)
+
+/* Description: This command destroys an instance of a task
+ IN[0] - Task handle. Hanlde of the task to destroy
+ ReturnCode - One of the ERR_SYS_... */
+#define CX18_DESTROY_TASK (MGR_CMD_MASK | 0x0002)
+
+/* All commands for CPU have the following mask set */
+#define CPU_CMD_MASK 0x20000000
+#define CPU_CMD_MASK_DEBUG (CPU_CMD_MASK | 0x00000000)
+#define CPU_CMD_MASK_ACK (CPU_CMD_MASK | 0x80000000)
+#define CPU_CMD_MASK_CAPTURE (CPU_CMD_MASK | 0x00020000)
+#define CPU_CMD_MASK_TS (CPU_CMD_MASK | 0x00040000)
+
+#define EPU_CMD_MASK 0x02000000
+#define EPU_CMD_MASK_DEBUG (EPU_CMD_MASK | 0x000000)
+#define EPU_CMD_MASK_DE (EPU_CMD_MASK | 0x040000)
+
+#define APU_CMD_MASK 0x10000000
+#define APU_CMD_MASK_ACK (APU_CMD_MASK | 0x80000000)
+
+#define CX18_APU_ENCODING_METHOD_MPEG (0 << 28)
+#define CX18_APU_ENCODING_METHOD_AC3 (1 << 28)
+
+/* Description: Command APU to start audio
+ IN[0] - audio parameters (same as CX18_CPU_SET_AUDIO_PARAMETERS?)
+ IN[1] - caller buffer address, or 0
+ ReturnCode - ??? */
+#define CX18_APU_START (APU_CMD_MASK | 0x01)
+
+/* Description: Command APU to stop audio
+ IN[0] - encoding method to stop
+ ReturnCode - ??? */
+#define CX18_APU_STOP (APU_CMD_MASK | 0x02)
+
+/* Description: Command APU to reset the AI
+ ReturnCode - ??? */
+#define CX18_APU_RESETAI (APU_CMD_MASK | 0x05)
+
+/* Description: This command indicates that a Memory Descriptor List has been
+ filled with the requested channel type
+ IN[0] - Task handle. Handle of the task
+ IN[1] - Offset of the MDL_ACK from the beginning of the local DDR.
+ IN[2] - Number of CNXT_MDL_ACK structures in the array pointed to by IN[1]
+ ReturnCode - One of the ERR_DE_... */
+#define CX18_EPU_DMA_DONE (EPU_CMD_MASK_DE | 0x0001)
+
+/* Something interesting happened
+ IN[0] - A value to log
+ IN[1] - An offset of a string in the MiniMe memory;
+ 0/zero/NULL means "I have nothing to say" */
+#define CX18_EPU_DEBUG (EPU_CMD_MASK_DEBUG | 0x0003)
+
+/* Reads memory/registers (32-bit)
+ IN[0] - Address
+ OUT[1] - Value */
+#define CX18_CPU_DEBUG_PEEK32 (CPU_CMD_MASK_DEBUG | 0x0003)
+
+/* Description: This command starts streaming with the set channel type
+ IN[0] - Task handle. Handle of the task to start
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_CAPTURE_START (CPU_CMD_MASK_CAPTURE | 0x0002)
+
+/* Description: This command stops streaming with the set channel type
+ IN[0] - Task handle. Handle of the task to stop
+ IN[1] - 0 = stop at end of GOP, 1 = stop at end of frame (MPEG only)
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_CAPTURE_STOP (CPU_CMD_MASK_CAPTURE | 0x0003)
+
+/* Description: This command pauses streaming with the set channel type
+ IN[0] - Task handle. Handle of the task to pause
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_CAPTURE_PAUSE (CPU_CMD_MASK_CAPTURE | 0x0007)
+
+/* Description: This command resumes streaming with the set channel type
+ IN[0] - Task handle. Handle of the task to resume
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_CAPTURE_RESUME (CPU_CMD_MASK_CAPTURE | 0x0008)
+
+#define CAPTURE_CHANNEL_TYPE_NONE 0
+#define CAPTURE_CHANNEL_TYPE_MPEG 1
+#define CAPTURE_CHANNEL_TYPE_INDEX 2
+#define CAPTURE_CHANNEL_TYPE_YUV 3
+#define CAPTURE_CHANNEL_TYPE_PCM 4
+#define CAPTURE_CHANNEL_TYPE_VBI 5
+#define CAPTURE_CHANNEL_TYPE_SLICED_VBI 6
+#define CAPTURE_CHANNEL_TYPE_TS 7
+#define CAPTURE_CHANNEL_TYPE_MAX 15
+
+/* Description: This command sets the channel type. This can only be done
+ when stopped.
+ IN[0] - Task handle. Handle of the task to start
+ IN[1] - Channel Type. See Below.
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_CHANNEL_TYPE (CPU_CMD_MASK_CAPTURE + 1)
+
+/* Description: Set stream output type
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - type
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_STREAM_OUTPUT_TYPE (CPU_CMD_MASK_CAPTURE | 0x0012)
+
+/* Description: Set video input resolution and frame rate
+ IN[0] - task handle
+ IN[1] - reserved
+ IN[2] - reserved
+ IN[3] - reserved
+ IN[4] - reserved
+ IN[5] - frame rate, 0 - 29.97f/s, 1 - 25f/s
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_VIDEO_IN (CPU_CMD_MASK_CAPTURE | 0x0004)
+
+/* Description: Set video frame rate
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - video bit rate mode
+ IN[2] - video average rate
+ IN[3] - video peak rate
+ IN[4] - system mux rate
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_VIDEO_RATE (CPU_CMD_MASK_CAPTURE | 0x0005)
+
+/* Description: Set video output resolution
+ IN[0] - task handle
+ IN[1] - horizontal size
+ IN[2] - vertical size
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_VIDEO_RESOLUTION (CPU_CMD_MASK_CAPTURE | 0x0006)
+
+/* Description: This command set filter parameters
+ IN[0] - Task handle. Handle of the task
+ IN[1] - type, 0 - temporal, 1 - spatial, 2 - median
+ IN[2] - mode, temporal/spatial: 0 - disable, 1 - static, 2 - dynamic
+ median: 0 = disable, 1 = horizontal, 2 = vertical,
+ 3 = horizontal/vertical, 4 = diagonal
+ IN[3] - strength, temporal 0 - 31, spatial 0 - 15
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_FILTER_PARAM (CPU_CMD_MASK_CAPTURE | 0x0009)
+
+/* Description: This command set spatial filter type
+ IN[0] - Task handle.
+ IN[1] - luma type: 0 = disable, 1 = 1D horizontal only, 2 = 1D vertical only,
+ 3 = 2D H/V separable, 4 = 2D symmetric non-separable
+ IN[2] - chroma type: 0 - disable, 1 = 1D horizontal
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_SPATIAL_FILTER_TYPE (CPU_CMD_MASK_CAPTURE | 0x000C)
+
+/* Description: This command set coring levels for median filter
+ IN[0] - Task handle.
+ IN[1] - luma_high
+ IN[2] - luma_low
+ IN[3] - chroma_high
+ IN[4] - chroma_low
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_MEDIAN_CORING (CPU_CMD_MASK_CAPTURE | 0x000E)
+
+/* Description: This command set the picture type mask for index file
+ IN[0] - Task handle (ignored by firmware)
+ IN[1] - 0 = disable index file output
+ 1 = output I picture
+ 2 = P picture
+ 4 = B picture
+ other = illegal */
+#define CX18_CPU_SET_INDEXTABLE (CPU_CMD_MASK_CAPTURE | 0x0010)
+
+/* Description: Set audio parameters
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - audio parameter
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_AUDIO_PARAMETERS (CPU_CMD_MASK_CAPTURE | 0x0011)
+
+/* Description: Set video mute
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - bit31-24: muteYvalue
+ bit23-16: muteUvalue
+ bit15-8: muteVvalue
+ bit0: 1:mute, 0: unmute
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_VIDEO_MUTE (CPU_CMD_MASK_CAPTURE | 0x0013)
+
+/* Description: Set audio mute
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - mute/unmute
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_AUDIO_MUTE (CPU_CMD_MASK_CAPTURE | 0x0014)
+
+/* Description: Set stream output type
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - subType
+ SET_INITIAL_SCR 1
+ SET_QUALITY_MODE 2
+ SET_VIM_PROTECT_MODE 3
+ SET_PTS_CORRECTION 4
+ SET_USB_FLUSH_MODE 5
+ SET_MERAQPAR_ENABLE 6
+ SET_NAV_PACK_INSERTION 7
+ SET_SCENE_CHANGE_ENABLE 8
+ IN[2] - parameter 1
+ IN[3] - parameter 2
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_MISC_PARAMETERS (CPU_CMD_MASK_CAPTURE | 0x0015)
+
+/* Description: Set raw VBI parameters
+ IN[0] - Task handle
+ IN[1] - No. of input lines per field:
+ bit[15:0]: field 1,
+ bit[31:16]: field 2
+ IN[2] - No. of input bytes per line
+ IN[3] - No. of output frames per transfer
+ IN[4] - start code
+ IN[5] - stop code
+ ReturnCode */
+#define CX18_CPU_SET_RAW_VBI_PARAM (CPU_CMD_MASK_CAPTURE | 0x0016)
+
+/* Description: Set capture line No.
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - height1
+ IN[2] - height2
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_CAPTURE_LINE_NO (CPU_CMD_MASK_CAPTURE | 0x0017)
+
+/* Description: Set copyright
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - copyright
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_COPYRIGHT (CPU_CMD_MASK_CAPTURE | 0x0018)
+
+/* Description: Set audio PID
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - PID
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_AUDIO_PID (CPU_CMD_MASK_CAPTURE | 0x0019)
+
+/* Description: Set video PID
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - PID
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_VIDEO_PID (CPU_CMD_MASK_CAPTURE | 0x001A)
+
+/* Description: Set Vertical Crop Line
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - Line
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_VER_CROP_LINE (CPU_CMD_MASK_CAPTURE | 0x001B)
+
+/* Description: Set COP structure
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - M
+ IN[2] - N
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_GOP_STRUCTURE (CPU_CMD_MASK_CAPTURE | 0x001C)
+
+/* Description: Set Scene Change Detection
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - scene change
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_SCENE_CHANGE_DETECTION (CPU_CMD_MASK_CAPTURE | 0x001D)
+
+/* Description: Set Aspect Ratio
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - AspectRatio
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_ASPECT_RATIO (CPU_CMD_MASK_CAPTURE | 0x001E)
+
+/* Description: Set Skip Input Frame
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - skip input frames
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_SKIP_INPUT_FRAME (CPU_CMD_MASK_CAPTURE | 0x001F)
+
+/* Description: Set sliced VBI parameters -
+ Note This API will only apply to MPEG and Sliced VBI Channels
+ IN[0] - Task handle
+ IN[1] - output type, 0 - CC, 1 - Moji, 2 - Teletext
+ IN[2] - start / stop line
+ bit[15:0] start line number
+ bit[31:16] stop line number
+ IN[3] - number of output frames per interrupt
+ IN[4] - VBI insertion mode
+ bit 0: output user data, 1 - enable
+ bit 1: output private stream, 1 - enable
+ bit 2: mux option, 0 - in GOP, 1 - in picture
+ bit[7:0] private stream ID
+ IN[5] - insertion period while mux option is in picture
+ ReturnCode - VBI data offset */
+#define CX18_CPU_SET_SLICED_VBI_PARAM (CPU_CMD_MASK_CAPTURE | 0x0020)
+
+/* Description: Set the user data place holder
+ IN[0] - type of data (0 for user)
+ IN[1] - Stuffing period
+ IN[2] - ID data size in word (less than 10)
+ IN[3] - Pointer to ID buffer */
+#define CX18_CPU_SET_USERDATA_PLACE_HOLDER (CPU_CMD_MASK_CAPTURE | 0x0021)
+
+
+/* Description:
+ In[0] Task Handle
+ return parameter:
+ Out[0] Reserved
+ Out[1] Video PTS bit[32:2] of last output video frame.
+ Out[2] Video PTS bit[ 1:0] of last output video frame.
+ Out[3] Hardware Video PTS counter bit[31:0],
+ these bits get incremented on every 90kHz clock tick.
+ Out[4] Hardware Video PTS counter bit32,
+ these bits get incremented on every 90kHz clock tick.
+ ReturnCode */
+#define CX18_CPU_GET_ENC_PTS (CPU_CMD_MASK_CAPTURE | 0x0022)
+
+/* Description: Set VFC parameters
+ IN[0] - task handle
+ IN[1] - VFC enable flag, 1 - enable, 0 - disable
+*/
+#define CX18_CPU_SET_VFC_PARAM (CPU_CMD_MASK_CAPTURE | 0x0023)
+
+/* Below is the list of commands related to the data exchange */
+#define CPU_CMD_MASK_DE (CPU_CMD_MASK | 0x040000)
+
+/* Description: This command provides the physical base address of the local
+ DDR as viewed by EPU
+ IN[0] - Physical offset where EPU has the local DDR mapped
+ ReturnCode - One of the ERR_DE_... */
+#define CPU_CMD_DE_SetBase (CPU_CMD_MASK_DE | 0x0001)
+
+/* Description: This command provides the offsets in the device memory where
+ the 2 cx18_mdl_ack blocks reside
+ IN[0] - Task handle. Handle of the task to start
+ IN[1] - Offset of the first cx18_mdl_ack from the beginning of the
+ local DDR.
+ IN[2] - Offset of the second cx18_mdl_ack from the beginning of the
+ local DDR.
+ ReturnCode - One of the ERR_DE_... */
+#define CX18_CPU_DE_SET_MDL_ACK (CPU_CMD_MASK_DE | 0x0002)
+
+/* Description: This command provides the offset to a Memory Descriptor List
+ IN[0] - Task handle. Handle of the task to start
+ IN[1] - Offset of the MDL from the beginning of the local DDR.
+ IN[2] - Number of cx18_mdl_ent structures in the array pointed to by IN[1]
+ IN[3] - Buffer ID
+ IN[4] - Total buffer length
+ ReturnCode - One of the ERR_DE_... */
+#define CX18_CPU_DE_SET_MDL (CPU_CMD_MASK_DE | 0x0005)
+
+/* Description: This command requests return of all current Memory
+ Descriptor Lists to the driver
+ IN[0] - Task handle. Handle of the task to start
+ ReturnCode - One of the ERR_DE_... */
+#define CX18_CPU_DE_RELEASE_MDL (CPU_CMD_MASK_DE | 0x0006)
+
+/* Description: This command signals the cpu that the dat buffer has been
+ consumed and ready for re-use.
+ IN[0] - Task handle. Handle of the task
+ IN[1] - Offset of the data block from the beginning of the local DDR.
+ IN[2] - Number of bytes in the data block
+ ReturnCode - One of the ERR_DE_... */
+/* #define CX18_CPU_DE_RELEASE_BUFFER (CPU_CMD_MASK_DE | 0x0007) */
+
+/* No Error / Success */
+#define CNXT_OK 0x000000
+
+/* Received unknown command */
+#define CXERR_UNK_CMD 0x000001
+
+/* First parameter in the command is invalid */
+#define CXERR_INVALID_PARAM1 0x000002
+
+/* Second parameter in the command is invalid */
+#define CXERR_INVALID_PARAM2 0x000003
+
+/* Device interface is not open/found */
+#define CXERR_DEV_NOT_FOUND 0x000004
+
+/* Requested function is not implemented/available */
+#define CXERR_NOTSUPPORTED 0x000005
+
+/* Invalid pointer is provided */
+#define CXERR_BADPTR 0x000006
+
+/* Unable to allocate memory */
+#define CXERR_NOMEM 0x000007
+
+/* Object/Link not found */
+#define CXERR_LINK 0x000008
+
+/* Device busy, command cannot be executed */
+#define CXERR_BUSY 0x000009
+
+/* File/device/handle is not open. */
+#define CXERR_NOT_OPEN 0x00000A
+
+/* Value is out of range */
+#define CXERR_OUTOFRANGE 0x00000B
+
+/* Buffer overflow */
+#define CXERR_OVERFLOW 0x00000C
+
+/* Version mismatch */
+#define CXERR_BADVER 0x00000D
+
+/* Operation timed out */
+#define CXERR_TIMEOUT 0x00000E
+
+/* Operation aborted */
+#define CXERR_ABORT 0x00000F
+
+/* Specified I2C device not found for read/write */
+#define CXERR_I2CDEV_NOTFOUND 0x000010
+
+/* Error in I2C data xfer (but I2C device is present) */
+#define CXERR_I2CDEV_XFERERR 0x000011
+
+/* Chanel changing component not ready */
+#define CXERR_CHANNELNOTREADY 0x000012
+
+/* PPU (Presensation/Decoder) mail box is corrupted */
+#define CXERR_PPU_MB_CORRUPT 0x000013
+
+/* CPU (Capture/Encoder) mail box is corrupted */
+#define CXERR_CPU_MB_CORRUPT 0x000014
+
+/* APU (Audio) mail box is corrupted */
+#define CXERR_APU_MB_CORRUPT 0x000015
+
+/* Unable to open file for reading */
+#define CXERR_FILE_OPEN_READ 0x000016
+
+/* Unable to open file for writing */
+#define CXERR_FILE_OPEN_WRITE 0x000017
+
+/* Unable to find the I2C section specified */
+#define CXERR_I2C_BADSECTION 0x000018
+
+/* Error in I2C data xfer (but I2C device is present) */
+#define CXERR_I2CDEV_DATALOW 0x000019
+
+/* Error in I2C data xfer (but I2C device is present) */
+#define CXERR_I2CDEV_CLOCKLOW 0x00001A
+
+/* No Interrupt received from HW (for I2C access) */
+#define CXERR_NO_HW_I2C_INTR 0x00001B
+
+/* RPU is not ready to accept commands! */
+#define CXERR_RPU_NOT_READY 0x00001C
+
+/* RPU is not ready to accept commands! */
+#define CXERR_RPU_NO_ACK 0x00001D
+
+/* The are no buffers ready. Try again soon! */
+#define CXERR_NODATA_AGAIN 0x00001E
+
+/* The stream is stopping. Function not allowed now! */
+#define CXERR_STOPPING_STATUS 0x00001F
+
+/* Trying to access hardware when the power is turned OFF */
+#define CXERR_DEVPOWER_OFF 0x000020
+
+#endif /* CX23418_H */
diff --git a/drivers/media/video/cx231xx/Kconfig b/drivers/media/video/cx231xx/Kconfig
new file mode 100644
index 00000000..446f692a
--- /dev/null
+++ b/drivers/media/video/cx231xx/Kconfig
@@ -0,0 +1,51 @@
+config VIDEO_CX231XX
+ tristate "Conexant cx231xx USB video capture support"
+ depends on VIDEO_DEV && I2C
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ depends on RC_CORE
+ select VIDEOBUF_VMALLOC
+ select VIDEO_CX25840
+ select VIDEO_CX2341X
+
+ ---help---
+ This is a video4linux driver for Conexant 231xx USB based TV cards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cx231xx
+
+config VIDEO_CX231XX_RC
+ bool "Conexant cx231xx Remote Controller additional support"
+ depends on RC_CORE
+ depends on VIDEO_CX231XX
+ default y
+ ---help---
+ cx231xx hardware has a builtin RX/TX support. However, a few
+ designs opted to not use it, but, instead, some other hardware.
+ This module enables the usage of those other hardware, like the
+ ones used with ISDB-T boards.
+
+ On most cases, all you need for IR is mceusb module.
+
+config VIDEO_CX231XX_ALSA
+ tristate "Conexant Cx231xx ALSA audio module"
+ depends on VIDEO_CX231XX && SND
+ select SND_PCM
+
+ ---help---
+ This is an ALSA driver for Cx231xx USB based TV cards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cx231xx-alsa
+
+config VIDEO_CX231XX_DVB
+ tristate "DVB/ATSC Support for Cx231xx based TV cards"
+ depends on VIDEO_CX231XX && DVB_CORE && DVB_CAPTURE_DRIVERS
+ select VIDEOBUF_DVB
+ select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE
+ select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE
+ select DVB_MB86A20S if !DVB_FE_CUSTOMISE
+
+ ---help---
+ This adds support for DVB cards based on the
+ Conexant cx231xx chips.
diff --git a/drivers/media/video/cx231xx/Makefile b/drivers/media/video/cx231xx/Makefile
new file mode 100644
index 00000000..b3348975
--- /dev/null
+++ b/drivers/media/video/cx231xx/Makefile
@@ -0,0 +1,16 @@
+cx231xx-y += cx231xx-video.o cx231xx-i2c.o cx231xx-cards.o cx231xx-core.o
+cx231xx-y += cx231xx-avcore.o cx231xx-417.o cx231xx-pcb-cfg.o cx231xx-vbi.o
+cx231xx-$(CONFIG_VIDEO_CX231XX_RC) += cx231xx-input.o
+
+cx231xx-alsa-objs := cx231xx-audio.o
+
+obj-$(CONFIG_VIDEO_CX231XX) += cx231xx.o
+obj-$(CONFIG_VIDEO_CX231XX_ALSA) += cx231xx-alsa.o
+obj-$(CONFIG_VIDEO_CX231XX_DVB) += cx231xx-dvb.o
+
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
+ccflags-y += -Idrivers/media/dvb/dvb-usb
+
diff --git a/drivers/media/video/cx231xx/cx231xx-417.c b/drivers/media/video/cx231xx/cx231xx-417.c
new file mode 100644
index 00000000..d4327dab
--- /dev/null
+++ b/drivers/media/video/cx231xx/cx231xx-417.c
@@ -0,0 +1,2191 @@
+/*
+ *
+ * Support for a cx23417 mpeg encoder via cx231xx host port.
+ *
+ * (c) 2004 Jelle Foks <jelle@foks.us>
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org>
+ * (c) 2008 Steven Toth <stoth@linuxtv.org>
+ * - CX23885/7/8 support
+ *
+ * Includes parts from the ivtv driver( http://ivtv.sourceforge.net/),
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/vmalloc.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/cx2341x.h>
+#include <linux/usb.h>
+
+#include "cx231xx.h"
+/*#include "cx23885-ioctl.h"*/
+
+#define CX231xx_FIRM_IMAGE_SIZE 376836
+#define CX231xx_FIRM_IMAGE_NAME "v4l-cx23885-enc.fw"
+
+/* for polaris ITVC */
+#define ITVC_WRITE_DIR 0x03FDFC00
+#define ITVC_READ_DIR 0x0001FC00
+
+#define MCI_MEMORY_DATA_BYTE0 0x00
+#define MCI_MEMORY_DATA_BYTE1 0x08
+#define MCI_MEMORY_DATA_BYTE2 0x10
+#define MCI_MEMORY_DATA_BYTE3 0x18
+
+#define MCI_MEMORY_ADDRESS_BYTE2 0x20
+#define MCI_MEMORY_ADDRESS_BYTE1 0x28
+#define MCI_MEMORY_ADDRESS_BYTE0 0x30
+
+#define MCI_REGISTER_DATA_BYTE0 0x40
+#define MCI_REGISTER_DATA_BYTE1 0x48
+#define MCI_REGISTER_DATA_BYTE2 0x50
+#define MCI_REGISTER_DATA_BYTE3 0x58
+
+#define MCI_REGISTER_ADDRESS_BYTE0 0x60
+#define MCI_REGISTER_ADDRESS_BYTE1 0x68
+
+#define MCI_REGISTER_MODE 0x70
+
+/* Read and write modes for polaris ITVC */
+#define MCI_MODE_REGISTER_READ 0x000
+#define MCI_MODE_REGISTER_WRITE 0x100
+#define MCI_MODE_MEMORY_READ 0x000
+#define MCI_MODE_MEMORY_WRITE 0x4000
+
+static unsigned int mpegbufs = 8;
+module_param(mpegbufs, int, 0644);
+MODULE_PARM_DESC(mpegbufs, "number of mpeg buffers, range 2-32");
+static unsigned int mpeglines = 128;
+module_param(mpeglines, int, 0644);
+MODULE_PARM_DESC(mpeglines, "number of lines in an MPEG buffer, range 2-32");
+static unsigned int mpeglinesize = 512;
+module_param(mpeglinesize, int, 0644);
+MODULE_PARM_DESC(mpeglinesize,
+ "number of bytes in each line of an MPEG buffer, range 512-1024");
+
+static unsigned int v4l_debug = 1;
+module_param(v4l_debug, int, 0644);
+MODULE_PARM_DESC(v4l_debug, "enable V4L debug messages");
+struct cx231xx_dmaqueue *dma_qq;
+#define dprintk(level, fmt, arg...)\
+ do { if (v4l_debug >= level) \
+ printk(KERN_INFO "%s: " fmt, \
+ (dev) ? dev->name : "cx231xx[?]", ## arg); \
+ } while (0)
+
+static struct cx231xx_tvnorm cx231xx_tvnorms[] = {
+ {
+ .name = "NTSC-M",
+ .id = V4L2_STD_NTSC_M,
+ }, {
+ .name = "NTSC-JP",
+ .id = V4L2_STD_NTSC_M_JP,
+ }, {
+ .name = "PAL-BG",
+ .id = V4L2_STD_PAL_BG,
+ }, {
+ .name = "PAL-DK",
+ .id = V4L2_STD_PAL_DK,
+ }, {
+ .name = "PAL-I",
+ .id = V4L2_STD_PAL_I,
+ }, {
+ .name = "PAL-M",
+ .id = V4L2_STD_PAL_M,
+ }, {
+ .name = "PAL-N",
+ .id = V4L2_STD_PAL_N,
+ }, {
+ .name = "PAL-Nc",
+ .id = V4L2_STD_PAL_Nc,
+ }, {
+ .name = "PAL-60",
+ .id = V4L2_STD_PAL_60,
+ }, {
+ .name = "SECAM-L",
+ .id = V4L2_STD_SECAM_L,
+ }, {
+ .name = "SECAM-DK",
+ .id = V4L2_STD_SECAM_DK,
+ }
+};
+
+/* ------------------------------------------------------------------ */
+enum cx231xx_capture_type {
+ CX231xx_MPEG_CAPTURE,
+ CX231xx_RAW_CAPTURE,
+ CX231xx_RAW_PASSTHRU_CAPTURE
+};
+enum cx231xx_capture_bits {
+ CX231xx_RAW_BITS_NONE = 0x00,
+ CX231xx_RAW_BITS_YUV_CAPTURE = 0x01,
+ CX231xx_RAW_BITS_PCM_CAPTURE = 0x02,
+ CX231xx_RAW_BITS_VBI_CAPTURE = 0x04,
+ CX231xx_RAW_BITS_PASSTHRU_CAPTURE = 0x08,
+ CX231xx_RAW_BITS_TO_HOST_CAPTURE = 0x10
+};
+enum cx231xx_capture_end {
+ CX231xx_END_AT_GOP, /* stop at the end of gop, generate irq */
+ CX231xx_END_NOW, /* stop immediately, no irq */
+};
+enum cx231xx_framerate {
+ CX231xx_FRAMERATE_NTSC_30, /* NTSC: 30fps */
+ CX231xx_FRAMERATE_PAL_25 /* PAL: 25fps */
+};
+enum cx231xx_stream_port {
+ CX231xx_OUTPUT_PORT_MEMORY,
+ CX231xx_OUTPUT_PORT_STREAMING,
+ CX231xx_OUTPUT_PORT_SERIAL
+};
+enum cx231xx_data_xfer_status {
+ CX231xx_MORE_BUFFERS_FOLLOW,
+ CX231xx_LAST_BUFFER,
+};
+enum cx231xx_picture_mask {
+ CX231xx_PICTURE_MASK_NONE,
+ CX231xx_PICTURE_MASK_I_FRAMES,
+ CX231xx_PICTURE_MASK_I_P_FRAMES = 0x3,
+ CX231xx_PICTURE_MASK_ALL_FRAMES = 0x7,
+};
+enum cx231xx_vbi_mode_bits {
+ CX231xx_VBI_BITS_SLICED,
+ CX231xx_VBI_BITS_RAW,
+};
+enum cx231xx_vbi_insertion_bits {
+ CX231xx_VBI_BITS_INSERT_IN_XTENSION_USR_DATA,
+ CX231xx_VBI_BITS_INSERT_IN_PRIVATE_PACKETS = 0x1 << 1,
+ CX231xx_VBI_BITS_SEPARATE_STREAM = 0x2 << 1,
+ CX231xx_VBI_BITS_SEPARATE_STREAM_USR_DATA = 0x4 << 1,
+ CX231xx_VBI_BITS_SEPARATE_STREAM_PRV_DATA = 0x5 << 1,
+};
+enum cx231xx_dma_unit {
+ CX231xx_DMA_BYTES,
+ CX231xx_DMA_FRAMES,
+};
+enum cx231xx_dma_transfer_status_bits {
+ CX231xx_DMA_TRANSFER_BITS_DONE = 0x01,
+ CX231xx_DMA_TRANSFER_BITS_ERROR = 0x04,
+ CX231xx_DMA_TRANSFER_BITS_LL_ERROR = 0x10,
+};
+enum cx231xx_pause {
+ CX231xx_PAUSE_ENCODING,
+ CX231xx_RESUME_ENCODING,
+};
+enum cx231xx_copyright {
+ CX231xx_COPYRIGHT_OFF,
+ CX231xx_COPYRIGHT_ON,
+};
+enum cx231xx_notification_type {
+ CX231xx_NOTIFICATION_REFRESH,
+};
+enum cx231xx_notification_status {
+ CX231xx_NOTIFICATION_OFF,
+ CX231xx_NOTIFICATION_ON,
+};
+enum cx231xx_notification_mailbox {
+ CX231xx_NOTIFICATION_NO_MAILBOX = -1,
+};
+enum cx231xx_field1_lines {
+ CX231xx_FIELD1_SAA7114 = 0x00EF, /* 239 */
+ CX231xx_FIELD1_SAA7115 = 0x00F0, /* 240 */
+ CX231xx_FIELD1_MICRONAS = 0x0105, /* 261 */
+};
+enum cx231xx_field2_lines {
+ CX231xx_FIELD2_SAA7114 = 0x00EF, /* 239 */
+ CX231xx_FIELD2_SAA7115 = 0x00F0, /* 240 */
+ CX231xx_FIELD2_MICRONAS = 0x0106, /* 262 */
+};
+enum cx231xx_custom_data_type {
+ CX231xx_CUSTOM_EXTENSION_USR_DATA,
+ CX231xx_CUSTOM_PRIVATE_PACKET,
+};
+enum cx231xx_mute {
+ CX231xx_UNMUTE,
+ CX231xx_MUTE,
+};
+enum cx231xx_mute_video_mask {
+ CX231xx_MUTE_VIDEO_V_MASK = 0x0000FF00,
+ CX231xx_MUTE_VIDEO_U_MASK = 0x00FF0000,
+ CX231xx_MUTE_VIDEO_Y_MASK = 0xFF000000,
+};
+enum cx231xx_mute_video_shift {
+ CX231xx_MUTE_VIDEO_V_SHIFT = 8,
+ CX231xx_MUTE_VIDEO_U_SHIFT = 16,
+ CX231xx_MUTE_VIDEO_Y_SHIFT = 24,
+};
+
+/* defines below are from ivtv-driver.h */
+#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF
+
+/* Firmware API commands */
+#define IVTV_API_STD_TIMEOUT 500
+
+/* Registers */
+/* IVTV_REG_OFFSET */
+#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8)
+#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC)
+#define IVTV_REG_SPU (0x9050)
+#define IVTV_REG_HW_BLOCKS (0x9054)
+#define IVTV_REG_VPU (0x9058)
+#define IVTV_REG_APU (0xA064)
+
+/*
+ * Bit definitions for MC417_RWD and MC417_OEN registers
+ *
+ * bits 31-16
+ *+-----------+
+ *| Reserved |
+ *|+-----------+
+ *| bit 15 bit 14 bit 13 bit 12 bit 11 bit 10 bit 9 bit 8
+ *|+-------+-------+-------+-------+-------+-------+-------+-------+
+ *|| MIWR# | MIRD# | MICS# |MIRDY# |MIADDR3|MIADDR2|MIADDR1|MIADDR0|
+ *|+-------+-------+-------+-------+-------+-------+-------+-------+
+ *| bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
+ *|+-------+-------+-------+-------+-------+-------+-------+-------+
+ *||MIDATA7|MIDATA6|MIDATA5|MIDATA4|MIDATA3|MIDATA2|MIDATA1|MIDATA0|
+ *|+-------+-------+-------+-------+-------+-------+-------+-------+
+ */
+#define MC417_MIWR 0x8000
+#define MC417_MIRD 0x4000
+#define MC417_MICS 0x2000
+#define MC417_MIRDY 0x1000
+#define MC417_MIADDR 0x0F00
+#define MC417_MIDATA 0x00FF
+
+
+/* Bit definitions for MC417_CTL register ****
+ *bits 31-6 bits 5-4 bit 3 bits 2-1 Bit 0
+ *+--------+-------------+--------+--------------+------------+
+ *|Reserved|MC417_SPD_CTL|Reserved|MC417_GPIO_SEL|UART_GPIO_EN|
+ *+--------+-------------+--------+--------------+------------+
+ */
+#define MC417_SPD_CTL(x) (((x) << 4) & 0x00000030)
+#define MC417_GPIO_SEL(x) (((x) << 1) & 0x00000006)
+#define MC417_UART_GPIO_EN 0x00000001
+
+/* Values for speed control */
+#define MC417_SPD_CTL_SLOW 0x1
+#define MC417_SPD_CTL_MEDIUM 0x0
+#define MC417_SPD_CTL_FAST 0x3 /* b'1x, but we use b'11 */
+
+/* Values for GPIO select */
+#define MC417_GPIO_SEL_GPIO3 0x3
+#define MC417_GPIO_SEL_GPIO2 0x2
+#define MC417_GPIO_SEL_GPIO1 0x1
+#define MC417_GPIO_SEL_GPIO0 0x0
+
+
+#define CX23417_GPIO_MASK 0xFC0003FF
+static int setITVCReg(struct cx231xx *dev, u32 gpio_direction, u32 value)
+{
+ int status = 0;
+ u32 _gpio_direction = 0;
+
+ _gpio_direction = _gpio_direction & CX23417_GPIO_MASK;
+ _gpio_direction = _gpio_direction|gpio_direction;
+ status = cx231xx_send_gpio_cmd(dev, _gpio_direction,
+ (u8 *)&value, 4, 0, 0);
+ return status;
+}
+static int getITVCReg(struct cx231xx *dev, u32 gpio_direction, u32 *pValue)
+{
+ int status = 0;
+ u32 _gpio_direction = 0;
+
+ _gpio_direction = _gpio_direction & CX23417_GPIO_MASK;
+ _gpio_direction = _gpio_direction|gpio_direction;
+
+ status = cx231xx_send_gpio_cmd(dev, _gpio_direction,
+ (u8 *)pValue, 4, 0, 1);
+ return status;
+}
+
+static int waitForMciComplete(struct cx231xx *dev)
+{
+ u32 gpio;
+ u32 gpio_driection = 0;
+ u8 count = 0;
+ getITVCReg(dev, gpio_driection, &gpio);
+
+ while (!(gpio&0x020000)) {
+ msleep(10);
+
+ getITVCReg(dev, gpio_driection, &gpio);
+
+ if (count++ > 100) {
+ dprintk(3, "ERROR: Timeout - gpio=%x\n", gpio);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int mc417_register_write(struct cx231xx *dev, u16 address, u32 value)
+{
+ u32 temp;
+ int status = 0;
+
+ temp = 0x82|MCI_REGISTER_DATA_BYTE0|((value&0x000000FF)<<8);
+ temp = temp<<10;
+ status = setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ if (status < 0)
+ return status;
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*write data byte 1;*/
+ temp = 0x82|MCI_REGISTER_DATA_BYTE1|(value&0x0000FF00);
+ temp = temp<<10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*write data byte 2;*/
+ temp = 0x82|MCI_REGISTER_DATA_BYTE2|((value&0x00FF0000)>>8);
+ temp = temp<<10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*write data byte 3;*/
+ temp = 0x82|MCI_REGISTER_DATA_BYTE3|((value&0xFF000000)>>16);
+ temp = temp<<10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*write address byte 0;*/
+ temp = 0x82|MCI_REGISTER_ADDRESS_BYTE0|((address&0x000000FF)<<8);
+ temp = temp<<10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*write address byte 1;*/
+ temp = 0x82|MCI_REGISTER_ADDRESS_BYTE1|(address&0x0000FF00);
+ temp = temp<<10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*Write that the mode is write.*/
+ temp = 0x82 | MCI_REGISTER_MODE | MCI_MODE_REGISTER_WRITE;
+ temp = temp<<10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ return waitForMciComplete(dev);
+}
+
+static int mc417_register_read(struct cx231xx *dev, u16 address, u32 *value)
+{
+ /*write address byte 0;*/
+ u32 temp;
+ u32 return_value = 0;
+ int ret = 0;
+
+ temp = 0x82 | MCI_REGISTER_ADDRESS_BYTE0 | ((address & 0x00FF) << 8);
+ temp = temp << 10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp | ((0x05) << 10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*write address byte 1;*/
+ temp = 0x82 | MCI_REGISTER_ADDRESS_BYTE1 | (address & 0xFF00);
+ temp = temp << 10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp | ((0x05) << 10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*write that the mode is read;*/
+ temp = 0x82 | MCI_REGISTER_MODE | MCI_MODE_REGISTER_READ;
+ temp = temp << 10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp | ((0x05) << 10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*wait for the MIRDY line to be asserted ,
+ signalling that the read is done;*/
+ ret = waitForMciComplete(dev);
+
+ /*switch the DATA- GPIO to input mode;*/
+
+ /*Read data byte 0;*/
+ temp = (0x82 | MCI_REGISTER_DATA_BYTE0) << 10;
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ temp = ((0x81 | MCI_REGISTER_DATA_BYTE0) << 10);
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ getITVCReg(dev, ITVC_READ_DIR, &temp);
+ return_value |= ((temp & 0x03FC0000) >> 18);
+ setITVCReg(dev, ITVC_READ_DIR, (0x87 << 10));
+
+ /* Read data byte 1;*/
+ temp = (0x82 | MCI_REGISTER_DATA_BYTE1) << 10;
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ temp = ((0x81 | MCI_REGISTER_DATA_BYTE1) << 10);
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ getITVCReg(dev, ITVC_READ_DIR, &temp);
+
+ return_value |= ((temp & 0x03FC0000) >> 10);
+ setITVCReg(dev, ITVC_READ_DIR, (0x87 << 10));
+
+ /*Read data byte 2;*/
+ temp = (0x82 | MCI_REGISTER_DATA_BYTE2) << 10;
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ temp = ((0x81 | MCI_REGISTER_DATA_BYTE2) << 10);
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ getITVCReg(dev, ITVC_READ_DIR, &temp);
+ return_value |= ((temp & 0x03FC0000) >> 2);
+ setITVCReg(dev, ITVC_READ_DIR, (0x87 << 10));
+
+ /*Read data byte 3;*/
+ temp = (0x82 | MCI_REGISTER_DATA_BYTE3) << 10;
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ temp = ((0x81 | MCI_REGISTER_DATA_BYTE3) << 10);
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ getITVCReg(dev, ITVC_READ_DIR, &temp);
+ return_value |= ((temp & 0x03FC0000) << 6);
+ setITVCReg(dev, ITVC_READ_DIR, (0x87 << 10));
+
+ *value = return_value;
+
+
+ return ret;
+}
+
+static int mc417_memory_write(struct cx231xx *dev, u32 address, u32 value)
+{
+ /*write data byte 0;*/
+
+ u32 temp;
+ int ret = 0;
+
+ temp = 0x82 | MCI_MEMORY_DATA_BYTE0|((value & 0x000000FF) << 8);
+ temp = temp << 10;
+ ret = setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ if (ret < 0)
+ return ret;
+ temp = temp | ((0x05) << 10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*write data byte 1;*/
+ temp = 0x82 | MCI_MEMORY_DATA_BYTE1 | (value & 0x0000FF00);
+ temp = temp << 10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp | ((0x05) << 10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*write data byte 2;*/
+ temp = 0x82|MCI_MEMORY_DATA_BYTE2|((value&0x00FF0000)>>8);
+ temp = temp<<10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*write data byte 3;*/
+ temp = 0x82|MCI_MEMORY_DATA_BYTE3|((value&0xFF000000)>>16);
+ temp = temp<<10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /* write address byte 2;*/
+ temp = 0x82|MCI_MEMORY_ADDRESS_BYTE2 | MCI_MODE_MEMORY_WRITE |
+ ((address & 0x003F0000)>>8);
+ temp = temp<<10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /* write address byte 1;*/
+ temp = 0x82|MCI_MEMORY_ADDRESS_BYTE1 | (address & 0xFF00);
+ temp = temp<<10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /* write address byte 0;*/
+ temp = 0x82|MCI_MEMORY_ADDRESS_BYTE0|((address & 0x00FF)<<8);
+ temp = temp<<10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*wait for MIRDY line;*/
+ waitForMciComplete(dev);
+
+ return 0;
+}
+
+static int mc417_memory_read(struct cx231xx *dev, u32 address, u32 *value)
+{
+ u32 temp = 0;
+ u32 return_value = 0;
+ int ret = 0;
+
+ /*write address byte 2;*/
+ temp = 0x82|MCI_MEMORY_ADDRESS_BYTE2 | MCI_MODE_MEMORY_READ |
+ ((address & 0x003F0000)>>8);
+ temp = temp<<10;
+ ret = setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ if (ret < 0)
+ return ret;
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*write address byte 1*/
+ temp = 0x82|MCI_MEMORY_ADDRESS_BYTE1 | (address & 0xFF00);
+ temp = temp<<10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*write address byte 0*/
+ temp = 0x82|MCI_MEMORY_ADDRESS_BYTE0 | ((address & 0x00FF)<<8);
+ temp = temp<<10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*Wait for MIRDY line*/
+ ret = waitForMciComplete(dev);
+
+
+ /*Read data byte 3;*/
+ temp = (0x82|MCI_MEMORY_DATA_BYTE3)<<10;
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ temp = ((0x81|MCI_MEMORY_DATA_BYTE3)<<10);
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ getITVCReg(dev, ITVC_READ_DIR, &temp);
+ return_value |= ((temp&0x03FC0000)<<6);
+ setITVCReg(dev, ITVC_READ_DIR, (0x87<<10));
+
+ /*Read data byte 2;*/
+ temp = (0x82|MCI_MEMORY_DATA_BYTE2)<<10;
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ temp = ((0x81|MCI_MEMORY_DATA_BYTE2)<<10);
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ getITVCReg(dev, ITVC_READ_DIR, &temp);
+ return_value |= ((temp&0x03FC0000)>>2);
+ setITVCReg(dev, ITVC_READ_DIR, (0x87<<10));
+
+ /* Read data byte 1;*/
+ temp = (0x82|MCI_MEMORY_DATA_BYTE1)<<10;
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ temp = ((0x81|MCI_MEMORY_DATA_BYTE1)<<10);
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ getITVCReg(dev, ITVC_READ_DIR, &temp);
+ return_value |= ((temp&0x03FC0000)>>10);
+ setITVCReg(dev, ITVC_READ_DIR, (0x87<<10));
+
+ /*Read data byte 0;*/
+ temp = (0x82|MCI_MEMORY_DATA_BYTE0)<<10;
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ temp = ((0x81|MCI_MEMORY_DATA_BYTE0)<<10);
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ getITVCReg(dev, ITVC_READ_DIR, &temp);
+ return_value |= ((temp&0x03FC0000)>>18);
+ setITVCReg(dev, ITVC_READ_DIR, (0x87<<10));
+
+ *value = return_value;
+ return ret;
+}
+
+/* ------------------------------------------------------------------ */
+
+/* MPEG encoder API */
+static char *cmd_to_str(int cmd)
+{
+ switch (cmd) {
+ case CX2341X_ENC_PING_FW:
+ return "PING_FW";
+ case CX2341X_ENC_START_CAPTURE:
+ return "START_CAPTURE";
+ case CX2341X_ENC_STOP_CAPTURE:
+ return "STOP_CAPTURE";
+ case CX2341X_ENC_SET_AUDIO_ID:
+ return "SET_AUDIO_ID";
+ case CX2341X_ENC_SET_VIDEO_ID:
+ return "SET_VIDEO_ID";
+ case CX2341X_ENC_SET_PCR_ID:
+ return "SET_PCR_PID";
+ case CX2341X_ENC_SET_FRAME_RATE:
+ return "SET_FRAME_RATE";
+ case CX2341X_ENC_SET_FRAME_SIZE:
+ return "SET_FRAME_SIZE";
+ case CX2341X_ENC_SET_BIT_RATE:
+ return "SET_BIT_RATE";
+ case CX2341X_ENC_SET_GOP_PROPERTIES:
+ return "SET_GOP_PROPERTIES";
+ case CX2341X_ENC_SET_ASPECT_RATIO:
+ return "SET_ASPECT_RATIO";
+ case CX2341X_ENC_SET_DNR_FILTER_MODE:
+ return "SET_DNR_FILTER_PROPS";
+ case CX2341X_ENC_SET_DNR_FILTER_PROPS:
+ return "SET_DNR_FILTER_PROPS";
+ case CX2341X_ENC_SET_CORING_LEVELS:
+ return "SET_CORING_LEVELS";
+ case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE:
+ return "SET_SPATIAL_FILTER_TYPE";
+ case CX2341X_ENC_SET_VBI_LINE:
+ return "SET_VBI_LINE";
+ case CX2341X_ENC_SET_STREAM_TYPE:
+ return "SET_STREAM_TYPE";
+ case CX2341X_ENC_SET_OUTPUT_PORT:
+ return "SET_OUTPUT_PORT";
+ case CX2341X_ENC_SET_AUDIO_PROPERTIES:
+ return "SET_AUDIO_PROPERTIES";
+ case CX2341X_ENC_HALT_FW:
+ return "HALT_FW";
+ case CX2341X_ENC_GET_VERSION:
+ return "GET_VERSION";
+ case CX2341X_ENC_SET_GOP_CLOSURE:
+ return "SET_GOP_CLOSURE";
+ case CX2341X_ENC_GET_SEQ_END:
+ return "GET_SEQ_END";
+ case CX2341X_ENC_SET_PGM_INDEX_INFO:
+ return "SET_PGM_INDEX_INFO";
+ case CX2341X_ENC_SET_VBI_CONFIG:
+ return "SET_VBI_CONFIG";
+ case CX2341X_ENC_SET_DMA_BLOCK_SIZE:
+ return "SET_DMA_BLOCK_SIZE";
+ case CX2341X_ENC_GET_PREV_DMA_INFO_MB_10:
+ return "GET_PREV_DMA_INFO_MB_10";
+ case CX2341X_ENC_GET_PREV_DMA_INFO_MB_9:
+ return "GET_PREV_DMA_INFO_MB_9";
+ case CX2341X_ENC_SCHED_DMA_TO_HOST:
+ return "SCHED_DMA_TO_HOST";
+ case CX2341X_ENC_INITIALIZE_INPUT:
+ return "INITIALIZE_INPUT";
+ case CX2341X_ENC_SET_FRAME_DROP_RATE:
+ return "SET_FRAME_DROP_RATE";
+ case CX2341X_ENC_PAUSE_ENCODER:
+ return "PAUSE_ENCODER";
+ case CX2341X_ENC_REFRESH_INPUT:
+ return "REFRESH_INPUT";
+ case CX2341X_ENC_SET_COPYRIGHT:
+ return "SET_COPYRIGHT";
+ case CX2341X_ENC_SET_EVENT_NOTIFICATION:
+ return "SET_EVENT_NOTIFICATION";
+ case CX2341X_ENC_SET_NUM_VSYNC_LINES:
+ return "SET_NUM_VSYNC_LINES";
+ case CX2341X_ENC_SET_PLACEHOLDER:
+ return "SET_PLACEHOLDER";
+ case CX2341X_ENC_MUTE_VIDEO:
+ return "MUTE_VIDEO";
+ case CX2341X_ENC_MUTE_AUDIO:
+ return "MUTE_AUDIO";
+ case CX2341X_ENC_MISC:
+ return "MISC";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static int cx231xx_mbox_func(void *priv,
+ u32 command,
+ int in,
+ int out,
+ u32 data[CX2341X_MBOX_MAX_DATA])
+{
+ struct cx231xx *dev = priv;
+ unsigned long timeout;
+ u32 value, flag, retval = 0;
+ int i;
+
+ dprintk(3, "%s: command(0x%X) = %s\n", __func__, command,
+ cmd_to_str(command));
+
+ /* this may not be 100% safe if we can't read any memory location
+ without side effects */
+ mc417_memory_read(dev, dev->cx23417_mailbox - 4, &value);
+ if (value != 0x12345678) {
+ dprintk(3,
+ "Firmware and/or mailbox pointer not initialized "
+ "or corrupted, signature = 0x%x, cmd = %s\n", value,
+ cmd_to_str(command));
+ return -1;
+ }
+
+ /* This read looks at 32 bits, but flag is only 8 bits.
+ * Seems we also bail if CMD or TIMEOUT bytes are set???
+ */
+ mc417_memory_read(dev, dev->cx23417_mailbox, &flag);
+ if (flag) {
+ dprintk(3, "ERROR: Mailbox appears to be in use "
+ "(%x), cmd = %s\n", flag, cmd_to_str(command));
+ return -1;
+ }
+
+ flag |= 1; /* tell 'em we're working on it */
+ mc417_memory_write(dev, dev->cx23417_mailbox, flag);
+
+ /* write command + args + fill remaining with zeros */
+ /* command code */
+ mc417_memory_write(dev, dev->cx23417_mailbox + 1, command);
+ mc417_memory_write(dev, dev->cx23417_mailbox + 3,
+ IVTV_API_STD_TIMEOUT); /* timeout */
+ for (i = 0; i < in; i++) {
+ mc417_memory_write(dev, dev->cx23417_mailbox + 4 + i, data[i]);
+ dprintk(3, "API Input %d = %d\n", i, data[i]);
+ }
+ for (; i < CX2341X_MBOX_MAX_DATA; i++)
+ mc417_memory_write(dev, dev->cx23417_mailbox + 4 + i, 0);
+
+ flag |= 3; /* tell 'em we're done writing */
+ mc417_memory_write(dev, dev->cx23417_mailbox, flag);
+
+ /* wait for firmware to handle the API command */
+ timeout = jiffies + msecs_to_jiffies(10);
+ for (;;) {
+ mc417_memory_read(dev, dev->cx23417_mailbox, &flag);
+ if (0 != (flag & 4))
+ break;
+ if (time_after(jiffies, timeout)) {
+ dprintk(3, "ERROR: API Mailbox timeout\n");
+ return -1;
+ }
+ udelay(10);
+ }
+
+ /* read output values */
+ for (i = 0; i < out; i++) {
+ mc417_memory_read(dev, dev->cx23417_mailbox + 4 + i, data + i);
+ dprintk(3, "API Output %d = %d\n", i, data[i]);
+ }
+
+ mc417_memory_read(dev, dev->cx23417_mailbox + 2, &retval);
+ dprintk(3, "API result = %d\n", retval);
+
+ flag = 0;
+ mc417_memory_write(dev, dev->cx23417_mailbox, flag);
+
+ return retval;
+}
+
+/* We don't need to call the API often, so using just one
+ * mailbox will probably suffice
+ */
+static int cx231xx_api_cmd(struct cx231xx *dev,
+ u32 command,
+ u32 inputcnt,
+ u32 outputcnt,
+ ...)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ va_list vargs;
+ int i, err;
+
+ dprintk(3, "%s() cmds = 0x%08x\n", __func__, command);
+
+ va_start(vargs, outputcnt);
+ for (i = 0; i < inputcnt; i++)
+ data[i] = va_arg(vargs, int);
+
+ err = cx231xx_mbox_func(dev, command, inputcnt, outputcnt, data);
+ for (i = 0; i < outputcnt; i++) {
+ int *vptr = va_arg(vargs, int *);
+ *vptr = data[i];
+ }
+ va_end(vargs);
+
+ return err;
+}
+
+static int cx231xx_find_mailbox(struct cx231xx *dev)
+{
+ u32 signature[4] = {
+ 0x12345678, 0x34567812, 0x56781234, 0x78123456
+ };
+ int signaturecnt = 0;
+ u32 value;
+ int i;
+ int ret = 0;
+
+ dprintk(2, "%s()\n", __func__);
+
+ for (i = 0; i < 0x100; i++) {/*CX231xx_FIRM_IMAGE_SIZE*/
+ ret = mc417_memory_read(dev, i, &value);
+ if (ret < 0)
+ return ret;
+ if (value == signature[signaturecnt])
+ signaturecnt++;
+ else
+ signaturecnt = 0;
+ if (4 == signaturecnt) {
+ dprintk(1, "Mailbox signature found at 0x%x\n", i+1);
+ return i+1;
+ }
+ }
+ dprintk(3, "Mailbox signature values not found!\n");
+ return -1;
+}
+
+static void mciWriteMemoryToGPIO(struct cx231xx *dev, u32 address, u32 value,
+ u32 *p_fw_image)
+{
+
+ u32 temp = 0;
+ int i = 0;
+
+ temp = 0x82|MCI_MEMORY_DATA_BYTE0|((value&0x000000FF)<<8);
+ temp = temp<<10;
+ *p_fw_image = temp;
+ p_fw_image++;
+ temp = temp|((0x05)<<10);
+ *p_fw_image = temp;
+ p_fw_image++;
+
+ /*write data byte 1;*/
+ temp = 0x82|MCI_MEMORY_DATA_BYTE1|(value&0x0000FF00);
+ temp = temp<<10;
+ *p_fw_image = temp;
+ p_fw_image++;
+ temp = temp|((0x05)<<10);
+ *p_fw_image = temp;
+ p_fw_image++;
+
+ /*write data byte 2;*/
+ temp = 0x82|MCI_MEMORY_DATA_BYTE2|((value&0x00FF0000)>>8);
+ temp = temp<<10;
+ *p_fw_image = temp;
+ p_fw_image++;
+ temp = temp|((0x05)<<10);
+ *p_fw_image = temp;
+ p_fw_image++;
+
+ /*write data byte 3;*/
+ temp = 0x82|MCI_MEMORY_DATA_BYTE3|((value&0xFF000000)>>16);
+ temp = temp<<10;
+ *p_fw_image = temp;
+ p_fw_image++;
+ temp = temp|((0x05)<<10);
+ *p_fw_image = temp;
+ p_fw_image++;
+
+ /* write address byte 2;*/
+ temp = 0x82|MCI_MEMORY_ADDRESS_BYTE2 | MCI_MODE_MEMORY_WRITE |
+ ((address & 0x003F0000)>>8);
+ temp = temp<<10;
+ *p_fw_image = temp;
+ p_fw_image++;
+ temp = temp|((0x05)<<10);
+ *p_fw_image = temp;
+ p_fw_image++;
+
+ /* write address byte 1;*/
+ temp = 0x82|MCI_MEMORY_ADDRESS_BYTE1 | (address & 0xFF00);
+ temp = temp<<10;
+ *p_fw_image = temp;
+ p_fw_image++;
+ temp = temp|((0x05)<<10);
+ *p_fw_image = temp;
+ p_fw_image++;
+
+ /* write address byte 0;*/
+ temp = 0x82|MCI_MEMORY_ADDRESS_BYTE0|((address & 0x00FF)<<8);
+ temp = temp<<10;
+ *p_fw_image = temp;
+ p_fw_image++;
+ temp = temp|((0x05)<<10);
+ *p_fw_image = temp;
+ p_fw_image++;
+
+ for (i = 0; i < 6; i++) {
+ *p_fw_image = 0xFFFFFFFF;
+ p_fw_image++;
+ }
+}
+
+
+static int cx231xx_load_firmware(struct cx231xx *dev)
+{
+ static const unsigned char magic[8] = {
+ 0xa7, 0x0d, 0x00, 0x00, 0x66, 0xbb, 0x55, 0xaa
+ };
+ const struct firmware *firmware;
+ int i, retval = 0;
+ u32 value = 0;
+ u32 gpio_output = 0;
+ /*u32 checksum = 0;*/
+ /*u32 *dataptr;*/
+ u32 transfer_size = 0;
+ u32 fw_data = 0;
+ u32 address = 0;
+ /*u32 current_fw[800];*/
+ u32 *p_current_fw, *p_fw;
+ u32 *p_fw_data;
+ int frame = 0;
+ u16 _buffer_size = 4096;
+ u8 *p_buffer;
+
+ p_current_fw = vmalloc(1884180 * 4);
+ p_fw = p_current_fw;
+ if (p_current_fw == NULL) {
+ dprintk(2, "FAIL!!!\n");
+ return -1;
+ }
+
+ p_buffer = vmalloc(4096);
+ if (p_buffer == NULL) {
+ dprintk(2, "FAIL!!!\n");
+ return -1;
+ }
+
+ dprintk(2, "%s()\n", __func__);
+
+ /* Save GPIO settings before reset of APU */
+ retval |= mc417_memory_read(dev, 0x9020, &gpio_output);
+ retval |= mc417_memory_read(dev, 0x900C, &value);
+
+ retval = mc417_register_write(dev,
+ IVTV_REG_VPU, 0xFFFFFFED);
+ retval |= mc417_register_write(dev,
+ IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST);
+ retval |= mc417_register_write(dev,
+ IVTV_REG_ENC_SDRAM_REFRESH, 0x80000800);
+ retval |= mc417_register_write(dev,
+ IVTV_REG_ENC_SDRAM_PRECHARGE, 0x1A);
+ retval |= mc417_register_write(dev,
+ IVTV_REG_APU, 0);
+
+ if (retval != 0) {
+ printk(KERN_ERR "%s: Error with mc417_register_write\n",
+ __func__);
+ return -1;
+ }
+
+ retval = request_firmware(&firmware, CX231xx_FIRM_IMAGE_NAME,
+ &dev->udev->dev);
+
+ if (retval != 0) {
+ printk(KERN_ERR
+ "ERROR: Hotplug firmware request failed (%s).\n",
+ CX231xx_FIRM_IMAGE_NAME);
+ printk(KERN_ERR "Please fix your hotplug setup, the board will "
+ "not work without firmware loaded!\n");
+ return -1;
+ }
+
+ if (firmware->size != CX231xx_FIRM_IMAGE_SIZE) {
+ printk(KERN_ERR "ERROR: Firmware size mismatch "
+ "(have %zd, expected %d)\n",
+ firmware->size, CX231xx_FIRM_IMAGE_SIZE);
+ release_firmware(firmware);
+ return -1;
+ }
+
+ if (0 != memcmp(firmware->data, magic, 8)) {
+ printk(KERN_ERR
+ "ERROR: Firmware magic mismatch, wrong file?\n");
+ release_firmware(firmware);
+ return -1;
+ }
+
+ initGPIO(dev);
+
+ /* transfer to the chip */
+ dprintk(2, "Loading firmware to GPIO...\n");
+ p_fw_data = (u32 *)firmware->data;
+ dprintk(2, "firmware->size=%zd\n", firmware->size);
+ for (transfer_size = 0; transfer_size < firmware->size;
+ transfer_size += 4) {
+ fw_data = *p_fw_data;
+
+ mciWriteMemoryToGPIO(dev, address, fw_data, p_current_fw);
+ address = address + 1;
+ p_current_fw += 20;
+ p_fw_data += 1;
+ }
+
+ /*download the firmware by ep5-out*/
+
+ for (frame = 0; frame < (int)(CX231xx_FIRM_IMAGE_SIZE*20/_buffer_size);
+ frame++) {
+ for (i = 0; i < _buffer_size; i++) {
+ *(p_buffer + i) = (u8)(*(p_fw + (frame * 128 * 8 + (i / 4))) & 0x000000FF);
+ i++;
+ *(p_buffer + i) = (u8)((*(p_fw + (frame * 128 * 8 + (i / 4))) & 0x0000FF00) >> 8);
+ i++;
+ *(p_buffer + i) = (u8)((*(p_fw + (frame * 128 * 8 + (i / 4))) & 0x00FF0000) >> 16);
+ i++;
+ *(p_buffer + i) = (u8)((*(p_fw + (frame * 128 * 8 + (i / 4))) & 0xFF000000) >> 24);
+ }
+ cx231xx_ep5_bulkout(dev, p_buffer, _buffer_size);
+ }
+
+ p_current_fw = p_fw;
+ vfree(p_current_fw);
+ p_current_fw = NULL;
+ uninitGPIO(dev);
+ release_firmware(firmware);
+ dprintk(1, "Firmware upload successful.\n");
+
+ retval |= mc417_register_write(dev, IVTV_REG_HW_BLOCKS,
+ IVTV_CMD_HW_BLOCKS_RST);
+ if (retval < 0) {
+ printk(KERN_ERR "%s: Error with mc417_register_write\n",
+ __func__);
+ return retval;
+ }
+ /* F/W power up disturbs the GPIOs, restore state */
+ retval |= mc417_register_write(dev, 0x9020, gpio_output);
+ retval |= mc417_register_write(dev, 0x900C, value);
+
+ retval |= mc417_register_read(dev, IVTV_REG_VPU, &value);
+ retval |= mc417_register_write(dev, IVTV_REG_VPU, value & 0xFFFFFFE8);
+
+ if (retval < 0) {
+ printk(KERN_ERR "%s: Error with mc417_register_write\n",
+ __func__);
+ return retval;
+ }
+ return 0;
+}
+
+static void cx231xx_417_check_encoder(struct cx231xx *dev)
+{
+ u32 status, seq;
+
+ status = 0;
+ seq = 0;
+ cx231xx_api_cmd(dev, CX2341X_ENC_GET_SEQ_END, 0, 2, &status, &seq);
+ dprintk(1, "%s() status = %d, seq = %d\n", __func__, status, seq);
+}
+
+static void cx231xx_codec_settings(struct cx231xx *dev)
+{
+ dprintk(1, "%s()\n", __func__);
+
+ /* assign frame size */
+ cx231xx_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0,
+ dev->ts1.height, dev->ts1.width);
+
+ dev->mpeg_params.width = dev->ts1.width;
+ dev->mpeg_params.height = dev->ts1.height;
+
+ cx2341x_update(dev, cx231xx_mbox_func, NULL, &dev->mpeg_params);
+
+ cx231xx_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 3, 1);
+ cx231xx_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 4, 1);
+}
+
+static int cx231xx_initialize_codec(struct cx231xx *dev)
+{
+ int version;
+ int retval;
+ u32 i, data[7];
+ u32 val = 0;
+
+ dprintk(1, "%s()\n", __func__);
+ cx231xx_disable656(dev);
+ retval = cx231xx_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */
+ if (retval < 0) {
+ dprintk(2, "%s() PING OK\n", __func__);
+ retval = cx231xx_load_firmware(dev);
+ if (retval < 0) {
+ printk(KERN_ERR "%s() f/w load failed\n", __func__);
+ return retval;
+ }
+ retval = cx231xx_find_mailbox(dev);
+ if (retval < 0) {
+ printk(KERN_ERR "%s() mailbox < 0, error\n",
+ __func__);
+ return -1;
+ }
+ dev->cx23417_mailbox = retval;
+ retval = cx231xx_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0);
+ if (retval < 0) {
+ printk(KERN_ERR
+ "ERROR: cx23417 firmware ping failed!\n");
+ return -1;
+ }
+ retval = cx231xx_api_cmd(dev, CX2341X_ENC_GET_VERSION, 0, 1,
+ &version);
+ if (retval < 0) {
+ printk(KERN_ERR "ERROR: cx23417 firmware get encoder :"
+ "version failed!\n");
+ return -1;
+ }
+ dprintk(1, "cx23417 firmware version is 0x%08x\n", version);
+ msleep(200);
+ }
+
+ for (i = 0; i < 1; i++) {
+ retval = mc417_register_read(dev, 0x20f8, &val);
+ dprintk(3, "***before enable656() VIM Capture Lines =%d ***\n",
+ val);
+ if (retval < 0)
+ return retval;
+ }
+
+ cx231xx_enable656(dev);
+ /* stop mpeg capture */
+ cx231xx_api_cmd(dev, CX2341X_ENC_STOP_CAPTURE,
+ 3, 0, 1, 3, 4);
+
+ cx231xx_codec_settings(dev);
+ msleep(60);
+
+/* cx231xx_api_cmd(dev, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, 0,
+ CX231xx_FIELD1_SAA7115, CX231xx_FIELD2_SAA7115);
+ cx231xx_api_cmd(dev, CX2341X_ENC_SET_PLACEHOLDER, 12, 0,
+ CX231xx_CUSTOM_EXTENSION_USR_DATA, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0);
+*/
+ /* Setup to capture VBI */
+ data[0] = 0x0001BD00;
+ data[1] = 1; /* frames per interrupt */
+ data[2] = 4; /* total bufs */
+ data[3] = 0x91559155; /* start codes */
+ data[4] = 0x206080C0; /* stop codes */
+ data[5] = 6; /* lines */
+ data[6] = 64; /* BPL */
+/*
+ cx231xx_api_cmd(dev, CX2341X_ENC_SET_VBI_CONFIG, 7, 0, data[0], data[1],
+ data[2], data[3], data[4], data[5], data[6]);
+
+ for (i = 2; i <= 24; i++) {
+ int valid;
+
+ valid = ((i >= 19) && (i <= 21));
+ cx231xx_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0, i,
+ valid, 0 , 0, 0);
+ cx231xx_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0,
+ i | 0x80000000, valid, 0, 0, 0);
+ }
+*/
+/* cx231xx_api_cmd(dev, CX2341X_ENC_MUTE_AUDIO, 1, 0, CX231xx_UNMUTE);
+ msleep(60);
+*/
+ /* initialize the video input */
+ retval = cx231xx_api_cmd(dev, CX2341X_ENC_INITIALIZE_INPUT, 0, 0);
+ if (retval < 0)
+ return retval;
+ msleep(60);
+
+ /* Enable VIP style pixel invalidation so we work with scaled mode */
+ mc417_memory_write(dev, 2120, 0x00000080);
+
+ /* start capturing to the host interface */
+ retval = cx231xx_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0,
+ CX231xx_MPEG_CAPTURE, CX231xx_RAW_BITS_NONE);
+ if (retval < 0)
+ return retval;
+ msleep(10);
+
+ for (i = 0; i < 1; i++) {
+ mc417_register_read(dev, 0x20f8, &val);
+ dprintk(3, "***VIM Capture Lines =%d ***\n", val);
+ }
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int bb_buf_setup(struct videobuf_queue *q,
+ unsigned int *count, unsigned int *size)
+{
+ struct cx231xx_fh *fh = q->priv_data;
+
+ fh->dev->ts1.ts_packet_size = mpeglinesize;
+ fh->dev->ts1.ts_packet_count = mpeglines;
+
+ *size = fh->dev->ts1.ts_packet_size * fh->dev->ts1.ts_packet_count;
+ *count = mpegbufs;
+
+ return 0;
+}
+static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf)
+{
+ struct cx231xx_fh *fh = vq->priv_data;
+ struct cx231xx *dev = fh->dev;
+ unsigned long flags = 0;
+
+ if (in_interrupt())
+ BUG();
+
+ spin_lock_irqsave(&dev->video_mode.slock, flags);
+ if (dev->USE_ISO) {
+ if (dev->video_mode.isoc_ctl.buf == buf)
+ dev->video_mode.isoc_ctl.buf = NULL;
+ } else {
+ if (dev->video_mode.bulk_ctl.buf == buf)
+ dev->video_mode.bulk_ctl.buf = NULL;
+ }
+ spin_unlock_irqrestore(&dev->video_mode.slock, flags);
+ videobuf_waiton(vq, &buf->vb, 0, 0);
+ videobuf_vmalloc_free(&buf->vb);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static void buffer_copy(struct cx231xx *dev, char *data, int len, struct urb *urb,
+ struct cx231xx_dmaqueue *dma_q)
+{
+ void *vbuf;
+ struct cx231xx_buffer *buf;
+ u32 tail_data = 0;
+ char *p_data;
+
+ if (dma_q->mpeg_buffer_done == 0) {
+ if (list_empty(&dma_q->active))
+ return;
+
+ buf = list_entry(dma_q->active.next,
+ struct cx231xx_buffer, vb.queue);
+ dev->video_mode.isoc_ctl.buf = buf;
+ dma_q->mpeg_buffer_done = 1;
+ }
+ /* Fill buffer */
+ buf = dev->video_mode.isoc_ctl.buf;
+ vbuf = videobuf_to_vmalloc(&buf->vb);
+
+ if ((dma_q->mpeg_buffer_completed+len) <
+ mpeglines*mpeglinesize) {
+ if (dma_q->add_ps_package_head ==
+ CX231XX_NEED_ADD_PS_PACKAGE_HEAD) {
+ memcpy(vbuf+dma_q->mpeg_buffer_completed,
+ dma_q->ps_head, 3);
+ dma_q->mpeg_buffer_completed =
+ dma_q->mpeg_buffer_completed + 3;
+ dma_q->add_ps_package_head =
+ CX231XX_NONEED_PS_PACKAGE_HEAD;
+ }
+ memcpy(vbuf+dma_q->mpeg_buffer_completed, data, len);
+ dma_q->mpeg_buffer_completed =
+ dma_q->mpeg_buffer_completed + len;
+ } else {
+ dma_q->mpeg_buffer_done = 0;
+
+ tail_data =
+ mpeglines*mpeglinesize - dma_q->mpeg_buffer_completed;
+ memcpy(vbuf+dma_q->mpeg_buffer_completed,
+ data, tail_data);
+
+ buf->vb.state = VIDEOBUF_DONE;
+ buf->vb.field_count++;
+ do_gettimeofday(&buf->vb.ts);
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+ dma_q->mpeg_buffer_completed = 0;
+
+ if (len - tail_data > 0) {
+ p_data = data + tail_data;
+ dma_q->left_data_count = len - tail_data;
+ memcpy(dma_q->p_left_data,
+ p_data, len - tail_data);
+ }
+
+ }
+
+ return;
+}
+
+static void buffer_filled(char *data, int len, struct urb *urb,
+ struct cx231xx_dmaqueue *dma_q)
+{
+ void *vbuf;
+ struct cx231xx_buffer *buf;
+
+ if (list_empty(&dma_q->active))
+ return;
+
+
+ buf = list_entry(dma_q->active.next,
+ struct cx231xx_buffer, vb.queue);
+
+
+ /* Fill buffer */
+ vbuf = videobuf_to_vmalloc(&buf->vb);
+ memcpy(vbuf, data, len);
+ buf->vb.state = VIDEOBUF_DONE;
+ buf->vb.field_count++;
+ do_gettimeofday(&buf->vb.ts);
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+
+ return;
+}
+static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb)
+{
+ struct cx231xx_dmaqueue *dma_q = urb->context;
+ unsigned char *p_buffer;
+ u32 buffer_size = 0;
+ u32 i = 0;
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ if (dma_q->left_data_count > 0) {
+ buffer_copy(dev, dma_q->p_left_data,
+ dma_q->left_data_count, urb, dma_q);
+ dma_q->mpeg_buffer_completed = dma_q->left_data_count;
+ dma_q->left_data_count = 0;
+ }
+
+ p_buffer = urb->transfer_buffer +
+ urb->iso_frame_desc[i].offset;
+ buffer_size = urb->iso_frame_desc[i].actual_length;
+
+ if (buffer_size > 0)
+ buffer_copy(dev, p_buffer, buffer_size, urb, dma_q);
+ }
+
+ return 0;
+}
+static inline int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb)
+{
+
+ /*char *outp;*/
+ /*struct cx231xx_buffer *buf;*/
+ struct cx231xx_dmaqueue *dma_q = urb->context;
+ unsigned char *p_buffer, *buffer;
+ u32 buffer_size = 0;
+
+ p_buffer = urb->transfer_buffer;
+ buffer_size = urb->actual_length;
+
+ buffer = kmalloc(buffer_size, GFP_ATOMIC);
+
+ memcpy(buffer, dma_q->ps_head, 3);
+ memcpy(buffer+3, p_buffer, buffer_size-3);
+ memcpy(dma_q->ps_head, p_buffer+buffer_size-3, 3);
+
+ p_buffer = buffer;
+ buffer_filled(p_buffer, buffer_size, urb, dma_q);
+
+ kfree(buffer);
+ return 0;
+}
+
+static int bb_buf_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb, enum v4l2_field field)
+{
+ struct cx231xx_fh *fh = q->priv_data;
+ struct cx231xx_buffer *buf =
+ container_of(vb, struct cx231xx_buffer, vb);
+ struct cx231xx *dev = fh->dev;
+ int rc = 0, urb_init = 0;
+ int size = fh->dev->ts1.ts_packet_size * fh->dev->ts1.ts_packet_count;
+
+ dma_qq = &dev->video_mode.vidq;
+
+ if (0 != buf->vb.baddr && buf->vb.bsize < size)
+ return -EINVAL;
+ buf->vb.width = fh->dev->ts1.ts_packet_size;
+ buf->vb.height = fh->dev->ts1.ts_packet_count;
+ buf->vb.size = size;
+ buf->vb.field = field;
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ rc = videobuf_iolock(q, &buf->vb, NULL);
+ if (rc < 0)
+ goto fail;
+ }
+
+ if (dev->USE_ISO) {
+ if (!dev->video_mode.isoc_ctl.num_bufs)
+ urb_init = 1;
+ } else {
+ if (!dev->video_mode.bulk_ctl.num_bufs)
+ urb_init = 1;
+ }
+ /*cx231xx_info("urb_init=%d dev->video_mode.max_pkt_size=%d\n",
+ urb_init, dev->video_mode.max_pkt_size);*/
+ dev->mode_tv = 1;
+
+ if (urb_init) {
+ rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
+ rc = cx231xx_unmute_audio(dev);
+ if (dev->USE_ISO) {
+ cx231xx_set_alt_setting(dev, INDEX_TS1, 4);
+ rc = cx231xx_init_isoc(dev, mpeglines,
+ mpegbufs,
+ dev->ts1_mode.max_pkt_size,
+ cx231xx_isoc_copy);
+ } else {
+ cx231xx_set_alt_setting(dev, INDEX_TS1, 0);
+ rc = cx231xx_init_bulk(dev, mpeglines,
+ mpegbufs,
+ dev->ts1_mode.max_pkt_size,
+ cx231xx_bulk_copy);
+ }
+ if (rc < 0)
+ goto fail;
+ }
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+fail:
+ free_buffer(q, buf);
+ return rc;
+}
+
+static void bb_buf_queue(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ struct cx231xx_fh *fh = q->priv_data;
+
+ struct cx231xx_buffer *buf =
+ container_of(vb, struct cx231xx_buffer, vb);
+ struct cx231xx *dev = fh->dev;
+ struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq;
+
+ buf->vb.state = VIDEOBUF_QUEUED;
+ list_add_tail(&buf->vb.queue, &vidq->active);
+
+}
+
+static void bb_buf_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ struct cx231xx_buffer *buf =
+ container_of(vb, struct cx231xx_buffer, vb);
+ /*struct cx231xx_fh *fh = q->priv_data;*/
+ /*struct cx231xx *dev = (struct cx231xx *)fh->dev;*/
+
+ free_buffer(q, buf);
+}
+
+static struct videobuf_queue_ops cx231xx_qops = {
+ .buf_setup = bb_buf_setup,
+ .buf_prepare = bb_buf_prepare,
+ .buf_queue = bb_buf_queue,
+ .buf_release = bb_buf_release,
+};
+
+/* ------------------------------------------------------------------ */
+
+static const u32 *ctrl_classes[] = {
+ cx2341x_mpeg_ctrls,
+ NULL
+};
+
+static int cx231xx_queryctrl(struct cx231xx *dev,
+ struct v4l2_queryctrl *qctrl)
+{
+ qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
+ if (qctrl->id == 0)
+ return -EINVAL;
+
+ /* MPEG V4L2 controls */
+ if (cx2341x_ctrl_query(&dev->mpeg_params, qctrl))
+ qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+
+ return 0;
+}
+
+static int cx231xx_querymenu(struct cx231xx *dev,
+ struct v4l2_querymenu *qmenu)
+{
+ struct v4l2_queryctrl qctrl;
+
+ qctrl.id = qmenu->id;
+ cx231xx_queryctrl(dev, &qctrl);
+ return v4l2_ctrl_query_menu(qmenu, &qctrl,
+ cx2341x_ctrl_get_menu(&dev->mpeg_params, qmenu->id));
+}
+
+static int vidioc_g_std(struct file *file, void *fh0, v4l2_std_id *norm)
+{
+ struct cx231xx_fh *fh = file->private_data;
+ struct cx231xx *dev = fh->dev;
+
+ *norm = dev->encodernorm.id;
+ return 0;
+}
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct cx231xx_fh *fh = file->private_data;
+ struct cx231xx *dev = fh->dev;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(cx231xx_tvnorms); i++)
+ if (*id & cx231xx_tvnorms[i].id)
+ break;
+ if (i == ARRAY_SIZE(cx231xx_tvnorms))
+ return -EINVAL;
+ dev->encodernorm = cx231xx_tvnorms[i];
+
+ if (dev->encodernorm.id & 0xb000) {
+ dprintk(3, "encodernorm set to NTSC\n");
+ dev->norm = V4L2_STD_NTSC;
+ dev->ts1.height = 480;
+ dev->mpeg_params.is_50hz = 0;
+ } else {
+ dprintk(3, "encodernorm set to PAL\n");
+ dev->norm = V4L2_STD_PAL_B;
+ dev->ts1.height = 576;
+ dev->mpeg_params.is_50hz = 1;
+ }
+ call_all(dev, core, s_std, dev->norm);
+ /* do mode control overrides */
+ cx231xx_do_mode_ctrl_overrides(dev);
+
+ dprintk(3, "exit vidioc_s_std() i=0x%x\n", i);
+ return 0;
+}
+static int vidioc_g_audio(struct file *file, void *fh,
+ struct v4l2_audio *a)
+{
+ struct v4l2_audio *vin = a;
+
+ int ret = -EINVAL;
+ if (vin->index > 0)
+ return ret;
+ strncpy(vin->name, "VideoGrabber Audio", 14);
+ vin->capability = V4L2_AUDCAP_STEREO;
+return 0;
+}
+static int vidioc_enumaudio(struct file *file, void *fh,
+ struct v4l2_audio *a)
+{
+ struct v4l2_audio *vin = a;
+
+ int ret = -EINVAL;
+
+ if (vin->index > 0)
+ return ret;
+ strncpy(vin->name, "VideoGrabber Audio", 14);
+ vin->capability = V4L2_AUDCAP_STEREO;
+
+
+return 0;
+}
+static const char *iname[] = {
+ [CX231XX_VMUX_COMPOSITE1] = "Composite1",
+ [CX231XX_VMUX_SVIDEO] = "S-Video",
+ [CX231XX_VMUX_TELEVISION] = "Television",
+ [CX231XX_VMUX_CABLE] = "Cable TV",
+ [CX231XX_VMUX_DVB] = "DVB",
+ [CX231XX_VMUX_DEBUG] = "for debug only",
+};
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct cx231xx_fh *fh = file->private_data;
+ struct cx231xx *dev = fh->dev;
+ struct cx231xx_input *input;
+ int n;
+ dprintk(3, "enter vidioc_enum_input()i->index=%d\n", i->index);
+
+ if (i->index >= 4)
+ return -EINVAL;
+
+
+ input = &cx231xx_boards[dev->model].input[i->index];
+
+ if (input->type == 0)
+ return -EINVAL;
+
+ /* FIXME
+ * strcpy(i->name, input->name); */
+
+ n = i->index;
+ strcpy(i->name, iname[INPUT(n)->type]);
+
+ if (input->type == CX231XX_VMUX_TELEVISION ||
+ input->type == CX231XX_VMUX_CABLE)
+ i->type = V4L2_INPUT_TYPE_TUNER;
+ else
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+
+
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct cx231xx_fh *fh = file->private_data;
+ struct cx231xx *dev = fh->dev;
+
+ dprintk(3, "enter vidioc_s_input() i=%d\n", i);
+
+ mutex_lock(&dev->lock);
+
+ video_mux(dev, i);
+
+ mutex_unlock(&dev->lock);
+
+ if (i >= 4)
+ return -EINVAL;
+ dev->input = i;
+ dprintk(3, "exit vidioc_s_input()\n");
+ return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+
+
+ return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctl)
+{
+ struct cx231xx_fh *fh = file->private_data;
+ struct cx231xx *dev = fh->dev;
+ dprintk(3, "enter vidioc_s_ctrl()\n");
+ /* Update the A/V core */
+ call_all(dev, core, s_ctrl, ctl);
+ dprintk(3, "exit vidioc_s_ctrl()\n");
+ return 0;
+}
+static struct v4l2_capability pvr_capability = {
+ .driver = "cx231xx",
+ .card = "VideoGrabber",
+ .bus_info = "usb",
+ .version = 1,
+ .capabilities = (V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO |
+ V4L2_CAP_STREAMING | V4L2_CAP_READWRITE),
+};
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+
+
+
+ memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability));
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+
+ if (f->index != 0)
+ return -EINVAL;
+
+ strlcpy(f->description, "MPEG", sizeof(f->description));
+ f->pixelformat = V4L2_PIX_FMT_MPEG;
+
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx231xx_fh *fh = file->private_data;
+ struct cx231xx *dev = fh->dev;
+ dprintk(3, "enter vidioc_g_fmt_vid_cap()\n");
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage =
+ dev->ts1.ts_packet_size * dev->ts1.ts_packet_count;
+ f->fmt.pix.colorspace = 0;
+ f->fmt.pix.width = dev->ts1.width;
+ f->fmt.pix.height = dev->ts1.height;
+ f->fmt.pix.field = fh->vidq.field;
+ dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n",
+ dev->ts1.width, dev->ts1.height, fh->vidq.field);
+ dprintk(3, "exit vidioc_g_fmt_vid_cap()\n");
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx231xx_fh *fh = file->private_data;
+ struct cx231xx *dev = fh->dev;
+ dprintk(3, "enter vidioc_try_fmt_vid_cap()\n");
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage =
+ dev->ts1.ts_packet_size * dev->ts1.ts_packet_count;
+ f->fmt.pix.colorspace = 0;
+ dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n",
+ dev->ts1.width, dev->ts1.height, fh->vidq.field);
+ dprintk(3, "exit vidioc_try_fmt_vid_cap()\n");
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+
+ return 0;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct cx231xx_fh *fh = file->private_data;
+
+ return videobuf_reqbufs(&fh->vidq, p);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct cx231xx_fh *fh = file->private_data;
+
+ return videobuf_querybuf(&fh->vidq, p);
+}
+
+static int vidioc_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct cx231xx_fh *fh = file->private_data;
+
+ return videobuf_qbuf(&fh->vidq, p);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct cx231xx_fh *fh = priv;
+
+ return videobuf_dqbuf(&fh->vidq, b, file->f_flags & O_NONBLOCK);
+}
+
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type i)
+{
+ struct cx231xx_fh *fh = file->private_data;
+
+ struct cx231xx *dev = fh->dev;
+ int rc = 0;
+ dprintk(3, "enter vidioc_streamon()\n");
+ cx231xx_set_alt_setting(dev, INDEX_TS1, 0);
+ rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
+ if (dev->USE_ISO)
+ rc = cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS,
+ CX231XX_NUM_BUFS,
+ dev->video_mode.max_pkt_size,
+ cx231xx_isoc_copy);
+ else {
+ rc = cx231xx_init_bulk(dev, 320,
+ 5,
+ dev->ts1_mode.max_pkt_size,
+ cx231xx_bulk_copy);
+ }
+ dprintk(3, "exit vidioc_streamon()\n");
+ return videobuf_streamon(&fh->vidq);
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct cx231xx_fh *fh = file->private_data;
+
+ return videobuf_streamoff(&fh->vidq);
+}
+
+static int vidioc_g_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *f)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ dprintk(3, "enter vidioc_g_ext_ctrls()\n");
+ if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+ return -EINVAL;
+ dprintk(3, "exit vidioc_g_ext_ctrls()\n");
+ return cx2341x_ext_ctrls(&dev->mpeg_params, 0, f, VIDIOC_G_EXT_CTRLS);
+}
+
+static int vidioc_s_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *f)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ struct cx2341x_mpeg_params p;
+ int err;
+ dprintk(3, "enter vidioc_s_ext_ctrls()\n");
+ if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+ return -EINVAL;
+
+ p = dev->mpeg_params;
+ err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_TRY_EXT_CTRLS);
+ if (err == 0) {
+ err = cx2341x_update(dev, cx231xx_mbox_func,
+ &dev->mpeg_params, &p);
+ dev->mpeg_params = p;
+ }
+
+ return err;
+
+
+return 0;
+}
+
+static int vidioc_try_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *f)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ struct cx2341x_mpeg_params p;
+ int err;
+ dprintk(3, "enter vidioc_try_ext_ctrls()\n");
+ if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+ return -EINVAL;
+
+ p = dev->mpeg_params;
+ err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_TRY_EXT_CTRLS);
+ dprintk(3, "exit vidioc_try_ext_ctrls() err=%d\n", err);
+ return err;
+}
+
+static int vidioc_log_status(struct file *file, void *priv)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ char name[32 + 2];
+
+ snprintf(name, sizeof(name), "%s/2", dev->name);
+ dprintk(3,
+ "%s/2: ============ START LOG STATUS ============\n",
+ dev->name);
+ call_all(dev, core, log_status);
+ cx2341x_log_status(&dev->mpeg_params, name);
+ dprintk(3,
+ "%s/2: ============= END LOG STATUS =============\n",
+ dev->name);
+ return 0;
+}
+
+static int vidioc_querymenu(struct file *file, void *priv,
+ struct v4l2_querymenu *a)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ dprintk(3, "enter vidioc_querymenu()\n");
+ dprintk(3, "exit vidioc_querymenu()\n");
+ return cx231xx_querymenu(dev, a);
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ dprintk(3, "enter vidioc_queryctrl()\n");
+ dprintk(3, "exit vidioc_queryctrl()\n");
+ return cx231xx_queryctrl(dev, c);
+}
+
+static int mpeg_open(struct file *file)
+{
+ int minor = video_devdata(file)->minor;
+ struct cx231xx *h, *dev = NULL;
+ /*struct list_head *list;*/
+ struct cx231xx_fh *fh;
+ /*u32 value = 0;*/
+
+ dprintk(2, "%s()\n", __func__);
+
+ list_for_each_entry(h, &cx231xx_devlist, devlist) {
+ if (h->v4l_device->minor == minor)
+ dev = h;
+ }
+
+ if (dev == NULL)
+ return -ENODEV;
+
+ mutex_lock(&dev->lock);
+
+ /* allocate + initialize per filehandle data */
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (NULL == fh) {
+ mutex_unlock(&dev->lock);
+ return -ENOMEM;
+ }
+
+ file->private_data = fh;
+ fh->dev = dev;
+
+
+ videobuf_queue_vmalloc_init(&fh->vidq, &cx231xx_qops,
+ NULL, &dev->video_mode.slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED,
+ sizeof(struct cx231xx_buffer), fh, NULL);
+/*
+ videobuf_queue_sg_init(&fh->vidq, &cx231xx_qops,
+ &dev->udev->dev, &dev->ts1.slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct cx231xx_buffer),
+ fh, NULL);
+*/
+
+
+ cx231xx_set_alt_setting(dev, INDEX_VANC, 1);
+ cx231xx_set_gpio_value(dev, 2, 0);
+
+ cx231xx_initialize_codec(dev);
+
+ mutex_unlock(&dev->lock);
+ cx231xx_start_TS1(dev);
+
+ return 0;
+}
+
+static int mpeg_release(struct file *file)
+{
+ struct cx231xx_fh *fh = file->private_data;
+ struct cx231xx *dev = fh->dev;
+
+ dprintk(3, "mpeg_release()! dev=0x%p\n", dev);
+
+ if (!dev) {
+ dprintk(3, "abort!!!\n");
+ return 0;
+ }
+
+ mutex_lock(&dev->lock);
+
+ cx231xx_stop_TS1(dev);
+
+ /* do this before setting alternate! */
+ if (dev->USE_ISO)
+ cx231xx_uninit_isoc(dev);
+ else
+ cx231xx_uninit_bulk(dev);
+ cx231xx_set_mode(dev, CX231XX_SUSPEND);
+
+ cx231xx_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0,
+ CX231xx_END_NOW, CX231xx_MPEG_CAPTURE,
+ CX231xx_RAW_BITS_NONE);
+
+ /* FIXME: Review this crap */
+ /* Shut device down on last close */
+ if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) {
+ if (atomic_dec_return(&dev->v4l_reader_count) == 0) {
+ /* stop mpeg capture */
+
+ msleep(500);
+ cx231xx_417_check_encoder(dev);
+
+ }
+ }
+
+ if (fh->vidq.streaming)
+ videobuf_streamoff(&fh->vidq);
+ if (fh->vidq.reading)
+ videobuf_read_stop(&fh->vidq);
+
+ videobuf_mmap_free(&fh->vidq);
+ file->private_data = NULL;
+ kfree(fh);
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static ssize_t mpeg_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct cx231xx_fh *fh = file->private_data;
+ struct cx231xx *dev = fh->dev;
+
+
+ /* Deal w/ A/V decoder * and mpeg encoder sync issues. */
+ /* Start mpeg encoder on first read. */
+ if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
+ if (atomic_inc_return(&dev->v4l_reader_count) == 1) {
+ if (cx231xx_initialize_codec(dev) < 0)
+ return -EINVAL;
+ }
+ }
+
+ return videobuf_read_stream(&fh->vidq, data, count, ppos, 0,
+ file->f_flags & O_NONBLOCK);
+}
+
+static unsigned int mpeg_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct cx231xx_fh *fh = file->private_data;
+ /*struct cx231xx *dev = fh->dev;*/
+
+ /*dprintk(2, "%s\n", __func__);*/
+
+ return videobuf_poll_stream(file, &fh->vidq, wait);
+}
+
+static int mpeg_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct cx231xx_fh *fh = file->private_data;
+ struct cx231xx *dev = fh->dev;
+
+ dprintk(2, "%s()\n", __func__);
+
+ return videobuf_mmap_mapper(&fh->vidq, vma);
+}
+
+static struct v4l2_file_operations mpeg_fops = {
+ .owner = THIS_MODULE,
+ .open = mpeg_open,
+ .release = mpeg_release,
+ .read = mpeg_read,
+ .poll = mpeg_poll,
+ .mmap = mpeg_mmap,
+ .ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_enumaudio = vidioc_enumaudio,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls,
+ .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls,
+ .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls,
+ .vidioc_log_status = vidioc_log_status,
+ .vidioc_querymenu = vidioc_querymenu,
+ .vidioc_queryctrl = vidioc_queryctrl,
+/* .vidioc_g_chip_ident = cx231xx_g_chip_ident,*/
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+/* .vidioc_g_register = cx231xx_g_register,*/
+/* .vidioc_s_register = cx231xx_s_register,*/
+#endif
+};
+
+static struct video_device cx231xx_mpeg_template = {
+ .name = "cx231xx",
+ .fops = &mpeg_fops,
+ .ioctl_ops = &mpeg_ioctl_ops,
+ .minor = -1,
+ .tvnorms = CX231xx_NORMS,
+ .current_norm = V4L2_STD_NTSC_M,
+};
+
+void cx231xx_417_unregister(struct cx231xx *dev)
+{
+ dprintk(1, "%s()\n", __func__);
+ dprintk(3, "%s()\n", __func__);
+
+ if (dev->v4l_device) {
+ if (-1 != dev->v4l_device->minor)
+ video_unregister_device(dev->v4l_device);
+ else
+ video_device_release(dev->v4l_device);
+ dev->v4l_device = NULL;
+ }
+}
+
+static struct video_device *cx231xx_video_dev_alloc(
+ struct cx231xx *dev,
+ struct usb_device *usbdev,
+ struct video_device *template,
+ char *type)
+{
+ struct video_device *vfd;
+
+ dprintk(1, "%s()\n", __func__);
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+ *vfd = *template;
+ vfd->minor = -1;
+ snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name,
+ type, cx231xx_boards[dev->model].name);
+
+ vfd->v4l2_dev = &dev->v4l2_dev;
+ vfd->release = video_device_release;
+
+ return vfd;
+
+}
+
+int cx231xx_417_register(struct cx231xx *dev)
+{
+ /* FIXME: Port1 hardcoded here */
+ int err = -ENODEV;
+ struct cx231xx_tsport *tsport = &dev->ts1;
+
+ dprintk(1, "%s()\n", __func__);
+
+ /* Set default TV standard */
+ dev->encodernorm = cx231xx_tvnorms[0];
+
+ if (dev->encodernorm.id & V4L2_STD_525_60)
+ tsport->height = 480;
+ else
+ tsport->height = 576;
+
+ tsport->width = 720;
+ cx2341x_fill_defaults(&dev->mpeg_params);
+ dev->norm = V4L2_STD_NTSC;
+
+ dev->mpeg_params.port = CX2341X_PORT_SERIAL;
+
+ /* Allocate and initialize V4L video device */
+ dev->v4l_device = cx231xx_video_dev_alloc(dev,
+ dev->udev, &cx231xx_mpeg_template, "mpeg");
+ err = video_register_device(dev->v4l_device,
+ VFL_TYPE_GRABBER, -1);
+ if (err < 0) {
+ dprintk(3, "%s: can't register mpeg device\n", dev->name);
+ return err;
+ }
+
+ dprintk(3, "%s: registered device video%d [mpeg]\n",
+ dev->name, dev->v4l_device->num);
+
+ return 0;
+}
diff --git a/drivers/media/video/cx231xx/cx231xx-audio.c b/drivers/media/video/cx231xx/cx231xx-audio.c
new file mode 100644
index 00000000..a2c2b7d3
--- /dev/null
+++ b/drivers/media/video/cx231xx/cx231xx-audio.c
@@ -0,0 +1,776 @@
+/*
+ * Conexant Cx231xx audio extension
+ *
+ * Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
+ * Based on em28xx driver
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/init.h>
+#include <linux/sound.h>
+#include <linux/spinlock.h>
+#include <linux/soundcard.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <media/v4l2-common.h>
+#include "cx231xx.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "activates debug info");
+
+#define dprintk(fmt, arg...) do { \
+ if (debug) \
+ printk(KERN_INFO "cx231xx-audio %s: " fmt, \
+ __func__, ##arg); \
+ } while (0)
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+
+static int cx231xx_isoc_audio_deinit(struct cx231xx *dev)
+{
+ int i;
+
+ dprintk("Stopping isoc\n");
+
+ for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
+ if (dev->adev.urb[i]) {
+ if (!irqs_disabled())
+ usb_kill_urb(dev->adev.urb[i]);
+ else
+ usb_unlink_urb(dev->adev.urb[i]);
+
+ usb_free_urb(dev->adev.urb[i]);
+ dev->adev.urb[i] = NULL;
+
+ kfree(dev->adev.transfer_buffer[i]);
+ dev->adev.transfer_buffer[i] = NULL;
+ }
+ }
+
+ return 0;
+}
+
+static int cx231xx_bulk_audio_deinit(struct cx231xx *dev)
+{
+ int i;
+
+ dprintk("Stopping bulk\n");
+
+ for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
+ if (dev->adev.urb[i]) {
+ if (!irqs_disabled())
+ usb_kill_urb(dev->adev.urb[i]);
+ else
+ usb_unlink_urb(dev->adev.urb[i]);
+
+ usb_free_urb(dev->adev.urb[i]);
+ dev->adev.urb[i] = NULL;
+
+ kfree(dev->adev.transfer_buffer[i]);
+ dev->adev.transfer_buffer[i] = NULL;
+ }
+ }
+
+ return 0;
+}
+
+static void cx231xx_audio_isocirq(struct urb *urb)
+{
+ struct cx231xx *dev = urb->context;
+ int i;
+ unsigned int oldptr;
+ int period_elapsed = 0;
+ int status;
+ unsigned char *cp;
+ unsigned int stride;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+
+ if (dev->state & DEV_DISCONNECTED)
+ return;
+
+ switch (urb->status) {
+ case 0: /* success */
+ case -ETIMEDOUT: /* NAK */
+ break;
+ case -ECONNRESET: /* kill */
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ default: /* error */
+ dprintk("urb completition error %d.\n", urb->status);
+ break;
+ }
+
+ if (atomic_read(&dev->stream_started) == 0)
+ return;
+
+ if (dev->adev.capture_pcm_substream) {
+ substream = dev->adev.capture_pcm_substream;
+ runtime = substream->runtime;
+ stride = runtime->frame_bits >> 3;
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ int length = urb->iso_frame_desc[i].actual_length /
+ stride;
+ cp = (unsigned char *)urb->transfer_buffer +
+ urb->iso_frame_desc[i].offset;
+
+ if (!length)
+ continue;
+
+ oldptr = dev->adev.hwptr_done_capture;
+ if (oldptr + length >= runtime->buffer_size) {
+ unsigned int cnt;
+
+ cnt = runtime->buffer_size - oldptr;
+ memcpy(runtime->dma_area + oldptr * stride, cp,
+ cnt * stride);
+ memcpy(runtime->dma_area, cp + cnt * stride,
+ length * stride - cnt * stride);
+ } else {
+ memcpy(runtime->dma_area + oldptr * stride, cp,
+ length * stride);
+ }
+
+ snd_pcm_stream_lock(substream);
+
+ dev->adev.hwptr_done_capture += length;
+ if (dev->adev.hwptr_done_capture >=
+ runtime->buffer_size)
+ dev->adev.hwptr_done_capture -=
+ runtime->buffer_size;
+
+ dev->adev.capture_transfer_done += length;
+ if (dev->adev.capture_transfer_done >=
+ runtime->period_size) {
+ dev->adev.capture_transfer_done -=
+ runtime->period_size;
+ period_elapsed = 1;
+ }
+ snd_pcm_stream_unlock(substream);
+ }
+ if (period_elapsed)
+ snd_pcm_period_elapsed(substream);
+ }
+ urb->status = 0;
+
+ status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (status < 0) {
+ cx231xx_errdev("resubmit of audio urb failed (error=%i)\n",
+ status);
+ }
+ return;
+}
+
+static void cx231xx_audio_bulkirq(struct urb *urb)
+{
+ struct cx231xx *dev = urb->context;
+ unsigned int oldptr;
+ int period_elapsed = 0;
+ int status;
+ unsigned char *cp;
+ unsigned int stride;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+
+ if (dev->state & DEV_DISCONNECTED)
+ return;
+
+ switch (urb->status) {
+ case 0: /* success */
+ case -ETIMEDOUT: /* NAK */
+ break;
+ case -ECONNRESET: /* kill */
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ default: /* error */
+ dprintk("urb completition error %d.\n", urb->status);
+ break;
+ }
+
+ if (atomic_read(&dev->stream_started) == 0)
+ return;
+
+ if (dev->adev.capture_pcm_substream) {
+ substream = dev->adev.capture_pcm_substream;
+ runtime = substream->runtime;
+ stride = runtime->frame_bits >> 3;
+
+ if (1) {
+ int length = urb->actual_length /
+ stride;
+ cp = (unsigned char *)urb->transfer_buffer;
+
+ oldptr = dev->adev.hwptr_done_capture;
+ if (oldptr + length >= runtime->buffer_size) {
+ unsigned int cnt;
+
+ cnt = runtime->buffer_size - oldptr;
+ memcpy(runtime->dma_area + oldptr * stride, cp,
+ cnt * stride);
+ memcpy(runtime->dma_area, cp + cnt * stride,
+ length * stride - cnt * stride);
+ } else {
+ memcpy(runtime->dma_area + oldptr * stride, cp,
+ length * stride);
+ }
+
+ snd_pcm_stream_lock(substream);
+
+ dev->adev.hwptr_done_capture += length;
+ if (dev->adev.hwptr_done_capture >=
+ runtime->buffer_size)
+ dev->adev.hwptr_done_capture -=
+ runtime->buffer_size;
+
+ dev->adev.capture_transfer_done += length;
+ if (dev->adev.capture_transfer_done >=
+ runtime->period_size) {
+ dev->adev.capture_transfer_done -=
+ runtime->period_size;
+ period_elapsed = 1;
+ }
+ snd_pcm_stream_unlock(substream);
+ }
+ if (period_elapsed)
+ snd_pcm_period_elapsed(substream);
+ }
+ urb->status = 0;
+
+ status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (status < 0) {
+ cx231xx_errdev("resubmit of audio urb failed (error=%i)\n",
+ status);
+ }
+ return;
+}
+
+static int cx231xx_init_audio_isoc(struct cx231xx *dev)
+{
+ int i, errCode;
+ int sb_size;
+
+ cx231xx_info("%s: Starting ISO AUDIO transfers\n", __func__);
+
+ if (dev->state & DEV_DISCONNECTED)
+ return -ENODEV;
+
+ sb_size = CX231XX_ISO_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size;
+
+ for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
+ struct urb *urb;
+ int j, k;
+
+ dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC);
+ if (!dev->adev.transfer_buffer[i])
+ return -ENOMEM;
+
+ memset(dev->adev.transfer_buffer[i], 0x80, sb_size);
+ urb = usb_alloc_urb(CX231XX_ISO_NUM_AUDIO_PACKETS, GFP_ATOMIC);
+ if (!urb) {
+ cx231xx_errdev("usb_alloc_urb failed!\n");
+ for (j = 0; j < i; j++) {
+ usb_free_urb(dev->adev.urb[j]);
+ kfree(dev->adev.transfer_buffer[j]);
+ }
+ return -ENOMEM;
+ }
+
+ urb->dev = dev->udev;
+ urb->context = dev;
+ urb->pipe = usb_rcvisocpipe(dev->udev,
+ dev->adev.end_point_addr);
+ urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+ urb->transfer_buffer = dev->adev.transfer_buffer[i];
+ urb->interval = 1;
+ urb->complete = cx231xx_audio_isocirq;
+ urb->number_of_packets = CX231XX_ISO_NUM_AUDIO_PACKETS;
+ urb->transfer_buffer_length = sb_size;
+
+ for (j = k = 0; j < CX231XX_ISO_NUM_AUDIO_PACKETS;
+ j++, k += dev->adev.max_pkt_size) {
+ urb->iso_frame_desc[j].offset = k;
+ urb->iso_frame_desc[j].length = dev->adev.max_pkt_size;
+ }
+ dev->adev.urb[i] = urb;
+ }
+
+ for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
+ errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC);
+ if (errCode < 0) {
+ cx231xx_isoc_audio_deinit(dev);
+ return errCode;
+ }
+ }
+
+ return errCode;
+}
+
+static int cx231xx_init_audio_bulk(struct cx231xx *dev)
+{
+ int i, errCode;
+ int sb_size;
+
+ cx231xx_info("%s: Starting BULK AUDIO transfers\n", __func__);
+
+ if (dev->state & DEV_DISCONNECTED)
+ return -ENODEV;
+
+ sb_size = CX231XX_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size;
+
+ for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
+ struct urb *urb;
+ int j;
+
+ dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC);
+ if (!dev->adev.transfer_buffer[i])
+ return -ENOMEM;
+
+ memset(dev->adev.transfer_buffer[i], 0x80, sb_size);
+ urb = usb_alloc_urb(CX231XX_NUM_AUDIO_PACKETS, GFP_ATOMIC);
+ if (!urb) {
+ cx231xx_errdev("usb_alloc_urb failed!\n");
+ for (j = 0; j < i; j++) {
+ usb_free_urb(dev->adev.urb[j]);
+ kfree(dev->adev.transfer_buffer[j]);
+ }
+ return -ENOMEM;
+ }
+
+ urb->dev = dev->udev;
+ urb->context = dev;
+ urb->pipe = usb_rcvbulkpipe(dev->udev,
+ dev->adev.end_point_addr);
+ urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+ urb->transfer_buffer = dev->adev.transfer_buffer[i];
+ urb->complete = cx231xx_audio_bulkirq;
+ urb->transfer_buffer_length = sb_size;
+
+ dev->adev.urb[i] = urb;
+
+ }
+
+ for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
+ errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC);
+ if (errCode < 0) {
+ cx231xx_bulk_audio_deinit(dev);
+ return errCode;
+ }
+ }
+
+ return errCode;
+}
+
+static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
+ size_t size)
+{
+ struct snd_pcm_runtime *runtime = subs->runtime;
+
+ dprintk("Allocating vbuffer\n");
+ if (runtime->dma_area) {
+ if (runtime->dma_bytes > size)
+ return 0;
+
+ vfree(runtime->dma_area);
+ }
+ runtime->dma_area = vmalloc(size);
+ if (!runtime->dma_area)
+ return -ENOMEM;
+
+ runtime->dma_bytes = size;
+
+ return 0;
+}
+
+static struct snd_pcm_hardware snd_cx231xx_hw_capture = {
+ .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID,
+
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT,
+
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */
+ .period_bytes_min = 64, /* 12544/2, */
+ .period_bytes_max = 12544,
+ .periods_min = 2,
+ .periods_max = 98, /* 12544, */
+};
+
+static int snd_cx231xx_capture_open(struct snd_pcm_substream *substream)
+{
+ struct cx231xx *dev = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int ret = 0;
+
+ dprintk("opening device and trying to acquire exclusive lock\n");
+
+ if (!dev) {
+ cx231xx_errdev("BUG: cx231xx can't find device struct."
+ " Can't proceed with open\n");
+ return -ENODEV;
+ }
+
+ if (dev->state & DEV_DISCONNECTED) {
+ cx231xx_errdev("Can't open. the device was removed.\n");
+ return -ENODEV;
+ }
+
+ /* Sets volume, mute, etc */
+ dev->mute = 0;
+
+ /* set alternate setting for audio interface */
+ /* 1 - 48000 samples per sec */
+ mutex_lock(&dev->lock);
+ if (dev->USE_ISO)
+ ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 1);
+ else
+ ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0);
+ mutex_unlock(&dev->lock);
+ if (ret < 0) {
+ cx231xx_errdev("failed to set alternate setting !\n");
+
+ return ret;
+ }
+
+ runtime->hw = snd_cx231xx_hw_capture;
+
+ mutex_lock(&dev->lock);
+ /* inform hardware to start streaming */
+ ret = cx231xx_capture_start(dev, 1, Audio);
+
+ dev->adev.users++;
+ mutex_unlock(&dev->lock);
+
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ dev->adev.capture_pcm_substream = substream;
+ runtime->private_data = dev;
+
+ return 0;
+}
+
+static int snd_cx231xx_pcm_close(struct snd_pcm_substream *substream)
+{
+ int ret;
+ struct cx231xx *dev = snd_pcm_substream_chip(substream);
+
+ dprintk("closing device\n");
+
+ /* inform hardware to stop streaming */
+ mutex_lock(&dev->lock);
+ ret = cx231xx_capture_start(dev, 0, Audio);
+
+ /* set alternate setting for audio interface */
+ /* 1 - 48000 samples per sec */
+ ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0);
+ if (ret < 0) {
+ cx231xx_errdev("failed to set alternate setting !\n");
+
+ mutex_unlock(&dev->lock);
+ return ret;
+ }
+
+ dev->mute = 1;
+ dev->adev.users--;
+ mutex_unlock(&dev->lock);
+
+ if (dev->adev.users == 0 && dev->adev.shutdown == 1) {
+ dprintk("audio users: %d\n", dev->adev.users);
+ dprintk("disabling audio stream!\n");
+ dev->adev.shutdown = 0;
+ dprintk("released lock\n");
+ if (atomic_read(&dev->stream_started) > 0) {
+ atomic_set(&dev->stream_started, 0);
+ schedule_work(&dev->wq_trigger);
+ }
+ }
+ return 0;
+}
+
+static int snd_cx231xx_hw_capture_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ unsigned int channels, rate, format;
+ int ret;
+
+ dprintk("Setting capture parameters\n");
+
+ ret = snd_pcm_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ format = params_format(hw_params);
+ rate = params_rate(hw_params);
+ channels = params_channels(hw_params);
+
+ /* TODO: set up cx231xx audio chip to deliver the correct audio format,
+ current default is 48000hz multiplexed => 96000hz mono
+ which shouldn't matter since analogue TV only supports mono */
+ return 0;
+}
+
+static int snd_cx231xx_hw_capture_free(struct snd_pcm_substream *substream)
+{
+ struct cx231xx *dev = snd_pcm_substream_chip(substream);
+
+ dprintk("Stop capture, if needed\n");
+
+ if (atomic_read(&dev->stream_started) > 0) {
+ atomic_set(&dev->stream_started, 0);
+ schedule_work(&dev->wq_trigger);
+ }
+
+ return 0;
+}
+
+static int snd_cx231xx_prepare(struct snd_pcm_substream *substream)
+{
+ struct cx231xx *dev = snd_pcm_substream_chip(substream);
+
+ dev->adev.hwptr_done_capture = 0;
+ dev->adev.capture_transfer_done = 0;
+
+ return 0;
+}
+
+static void audio_trigger(struct work_struct *work)
+{
+ struct cx231xx *dev = container_of(work, struct cx231xx, wq_trigger);
+
+ if (atomic_read(&dev->stream_started)) {
+ dprintk("starting capture");
+ if (is_fw_load(dev) == 0)
+ cx25840_call(dev, core, load_fw);
+ if (dev->USE_ISO)
+ cx231xx_init_audio_isoc(dev);
+ else
+ cx231xx_init_audio_bulk(dev);
+ } else {
+ dprintk("stopping capture");
+ cx231xx_isoc_audio_deinit(dev);
+ }
+}
+
+static int snd_cx231xx_capture_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct cx231xx *dev = snd_pcm_substream_chip(substream);
+ int retval;
+
+ if (dev->state & DEV_DISCONNECTED)
+ return -ENODEV;
+
+ spin_lock(&dev->adev.slock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ atomic_set(&dev->stream_started, 1);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ atomic_set(&dev->stream_started, 0);
+ break;
+ default:
+ retval = -EINVAL;
+ }
+ spin_unlock(&dev->adev.slock);
+
+ schedule_work(&dev->wq_trigger);
+
+ return 0;
+}
+
+static snd_pcm_uframes_t snd_cx231xx_capture_pointer(struct snd_pcm_substream
+ *substream)
+{
+ struct cx231xx *dev;
+ unsigned long flags;
+ snd_pcm_uframes_t hwptr_done;
+
+ dev = snd_pcm_substream_chip(substream);
+
+ spin_lock_irqsave(&dev->adev.slock, flags);
+ hwptr_done = dev->adev.hwptr_done_capture;
+ spin_unlock_irqrestore(&dev->adev.slock, flags);
+
+ return hwptr_done;
+}
+
+static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
+ unsigned long offset)
+{
+ void *pageptr = subs->runtime->dma_area + offset;
+
+ return vmalloc_to_page(pageptr);
+}
+
+static struct snd_pcm_ops snd_cx231xx_pcm_capture = {
+ .open = snd_cx231xx_capture_open,
+ .close = snd_cx231xx_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_cx231xx_hw_capture_params,
+ .hw_free = snd_cx231xx_hw_capture_free,
+ .prepare = snd_cx231xx_prepare,
+ .trigger = snd_cx231xx_capture_trigger,
+ .pointer = snd_cx231xx_capture_pointer,
+ .page = snd_pcm_get_vmalloc_page,
+};
+
+static int cx231xx_audio_init(struct cx231xx *dev)
+{
+ struct cx231xx_audio *adev = &dev->adev;
+ struct snd_pcm *pcm;
+ struct snd_card *card;
+ static int devnr;
+ int err;
+ struct usb_interface *uif;
+ int i, isoc_pipe = 0;
+
+ if (dev->has_alsa_audio != 1) {
+ /* This device does not support the extension (in this case
+ the device is expecting the snd-usb-audio module or
+ doesn't have analog audio support at all) */
+ return 0;
+ }
+
+ cx231xx_info("cx231xx-audio.c: probing for cx231xx "
+ "non standard usbaudio\n");
+
+ err = snd_card_create(index[devnr], "Cx231xx Audio", THIS_MODULE,
+ 0, &card);
+ if (err < 0)
+ return err;
+
+ spin_lock_init(&adev->slock);
+ err = snd_pcm_new(card, "Cx231xx Audio", 0, 0, 1, &pcm);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &snd_cx231xx_pcm_capture);
+ pcm->info_flags = 0;
+ pcm->private_data = dev;
+ strcpy(pcm->name, "Conexant cx231xx Capture");
+ snd_card_set_dev(card, &dev->udev->dev);
+ strcpy(card->driver, "Cx231xx-Audio");
+ strcpy(card->shortname, "Cx231xx Audio");
+ strcpy(card->longname, "Conexant cx231xx Audio");
+
+ INIT_WORK(&dev->wq_trigger, audio_trigger);
+
+ err = snd_card_register(card);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ adev->sndcard = card;
+ adev->udev = dev->udev;
+
+ /* compute alternate max packet sizes for Audio */
+ uif =
+ dev->udev->actconfig->interface[dev->current_pcb_config.
+ hs_config_info[0].interface_info.
+ audio_index + 1];
+
+ adev->end_point_addr =
+ le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc.
+ bEndpointAddress);
+
+ adev->num_alt = uif->num_altsetting;
+ cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
+ adev->end_point_addr, adev->num_alt);
+ adev->alt_max_pkt_size = kmalloc(32 * adev->num_alt, GFP_KERNEL);
+
+ if (adev->alt_max_pkt_size == NULL) {
+ cx231xx_errdev("out of memory!\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < adev->num_alt; i++) {
+ u16 tmp =
+ le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.
+ wMaxPacketSize);
+ adev->alt_max_pkt_size[i] =
+ (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
+ cx231xx_info("Alternate setting %i, max size= %i\n", i,
+ adev->alt_max_pkt_size[i]);
+ }
+
+ return 0;
+}
+
+static int cx231xx_audio_fini(struct cx231xx *dev)
+{
+ if (dev == NULL)
+ return 0;
+
+ if (dev->has_alsa_audio != 1) {
+ /* This device does not support the extension (in this case
+ the device is expecting the snd-usb-audio module or
+ doesn't have analog audio support at all) */
+ return 0;
+ }
+
+ if (dev->adev.sndcard) {
+ snd_card_free(dev->adev.sndcard);
+ kfree(dev->adev.alt_max_pkt_size);
+ dev->adev.sndcard = NULL;
+ }
+
+ return 0;
+}
+
+static struct cx231xx_ops audio_ops = {
+ .id = CX231XX_AUDIO,
+ .name = "Cx231xx Audio Extension",
+ .init = cx231xx_audio_init,
+ .fini = cx231xx_audio_fini,
+};
+
+static int __init cx231xx_alsa_register(void)
+{
+ return cx231xx_register_extension(&audio_ops);
+}
+
+static void __exit cx231xx_alsa_unregister(void)
+{
+ cx231xx_unregister_extension(&audio_ops);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Srinivasa Deevi <srinivasa.deevi@conexant.com>");
+MODULE_DESCRIPTION("Cx231xx Audio driver");
+
+module_init(cx231xx_alsa_register);
+module_exit(cx231xx_alsa_unregister);
diff --git a/drivers/media/video/cx231xx/cx231xx-avcore.c b/drivers/media/video/cx231xx/cx231xx-avcore.c
new file mode 100644
index 00000000..53ff26e7
--- /dev/null
+++ b/drivers/media/video/cx231xx/cx231xx-avcore.c
@@ -0,0 +1,3123 @@
+/*
+ cx231xx_avcore.c - driver for Conexant Cx23100/101/102
+ USB video capture devices
+
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
+
+ This program contains the specific code to control the avdecoder chip and
+ other related usb control functions for cx231xx based chipset.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/bitmap.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <media/tuner.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-chip-ident.h>
+
+#include "cx231xx.h"
+#include "cx231xx-dif.h"
+
+#define TUNER_MODE_FM_RADIO 0
+/******************************************************************************
+ -: BLOCK ARRANGEMENT :-
+ I2S block ----------------------|
+ [I2S audio] |
+ |
+ Analog Front End --> Direct IF -|-> Cx25840 --> Audio
+ [video & audio] | [Audio]
+ |
+ |-> Cx25840 --> Video
+ [Video]
+
+*******************************************************************************/
+/******************************************************************************
+ * VERVE REGISTER *
+ * *
+ ******************************************************************************/
+static int verve_write_byte(struct cx231xx *dev, u8 saddr, u8 data)
+{
+ return cx231xx_write_i2c_data(dev, VERVE_I2C_ADDRESS,
+ saddr, 1, data, 1);
+}
+
+static int verve_read_byte(struct cx231xx *dev, u8 saddr, u8 *data)
+{
+ int status;
+ u32 temp = 0;
+
+ status = cx231xx_read_i2c_data(dev, VERVE_I2C_ADDRESS,
+ saddr, 1, &temp, 1);
+ *data = (u8) temp;
+ return status;
+}
+void initGPIO(struct cx231xx *dev)
+{
+ u32 _gpio_direction = 0;
+ u32 value = 0;
+ u8 val = 0;
+
+ _gpio_direction = _gpio_direction & 0xFC0003FF;
+ _gpio_direction = _gpio_direction | 0x03FDFC00;
+ cx231xx_send_gpio_cmd(dev, _gpio_direction, (u8 *)&value, 4, 0, 0);
+
+ verve_read_byte(dev, 0x07, &val);
+ cx231xx_info(" verve_read_byte address0x07=0x%x\n", val);
+ verve_write_byte(dev, 0x07, 0xF4);
+ verve_read_byte(dev, 0x07, &val);
+ cx231xx_info(" verve_read_byte address0x07=0x%x\n", val);
+
+ cx231xx_capture_start(dev, 1, 2);
+
+ cx231xx_mode_register(dev, EP_MODE_SET, 0x0500FE00);
+ cx231xx_mode_register(dev, GBULK_BIT_EN, 0xFFFDFFFF);
+
+}
+void uninitGPIO(struct cx231xx *dev)
+{
+ u8 value[4] = { 0, 0, 0, 0 };
+
+ cx231xx_capture_start(dev, 0, 2);
+ verve_write_byte(dev, 0x07, 0x14);
+ cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ 0x68, value, 4);
+}
+
+/******************************************************************************
+ * A F E - B L O C K C O N T R O L functions *
+ * [ANALOG FRONT END] *
+ ******************************************************************************/
+static int afe_write_byte(struct cx231xx *dev, u16 saddr, u8 data)
+{
+ return cx231xx_write_i2c_data(dev, AFE_DEVICE_ADDRESS,
+ saddr, 2, data, 1);
+}
+
+static int afe_read_byte(struct cx231xx *dev, u16 saddr, u8 *data)
+{
+ int status;
+ u32 temp = 0;
+
+ status = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS,
+ saddr, 2, &temp, 1);
+ *data = (u8) temp;
+ return status;
+}
+
+int cx231xx_afe_init_super_block(struct cx231xx *dev, u32 ref_count)
+{
+ int status = 0;
+ u8 temp = 0;
+ u8 afe_power_status = 0;
+ int i = 0;
+
+ /* super block initialize */
+ temp = (u8) (ref_count & 0xff);
+ status = afe_write_byte(dev, SUP_BLK_TUNE2, temp);
+ if (status < 0)
+ return status;
+
+ status = afe_read_byte(dev, SUP_BLK_TUNE2, &afe_power_status);
+ if (status < 0)
+ return status;
+
+ temp = (u8) ((ref_count & 0x300) >> 8);
+ temp |= 0x40;
+ status = afe_write_byte(dev, SUP_BLK_TUNE1, temp);
+ if (status < 0)
+ return status;
+
+ status = afe_write_byte(dev, SUP_BLK_PLL2, 0x0f);
+ if (status < 0)
+ return status;
+
+ /* enable pll */
+ while (afe_power_status != 0x18) {
+ status = afe_write_byte(dev, SUP_BLK_PWRDN, 0x18);
+ if (status < 0) {
+ cx231xx_info(
+ ": Init Super Block failed in send cmd\n");
+ break;
+ }
+
+ status = afe_read_byte(dev, SUP_BLK_PWRDN, &afe_power_status);
+ afe_power_status &= 0xff;
+ if (status < 0) {
+ cx231xx_info(
+ ": Init Super Block failed in receive cmd\n");
+ break;
+ }
+ i++;
+ if (i == 10) {
+ cx231xx_info(
+ ": Init Super Block force break in loop !!!!\n");
+ status = -1;
+ break;
+ }
+ }
+
+ if (status < 0)
+ return status;
+
+ /* start tuning filter */
+ status = afe_write_byte(dev, SUP_BLK_TUNE3, 0x40);
+ if (status < 0)
+ return status;
+
+ msleep(5);
+
+ /* exit tuning */
+ status = afe_write_byte(dev, SUP_BLK_TUNE3, 0x00);
+
+ return status;
+}
+
+int cx231xx_afe_init_channels(struct cx231xx *dev)
+{
+ int status = 0;
+
+ /* power up all 3 channels, clear pd_buffer */
+ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1, 0x00);
+ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, 0x00);
+ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, 0x00);
+
+ /* Enable quantizer calibration */
+ status = afe_write_byte(dev, ADC_COM_QUANT, 0x02);
+
+ /* channel initialize, force modulator (fb) reset */
+ status = afe_write_byte(dev, ADC_FB_FRCRST_CH1, 0x17);
+ status = afe_write_byte(dev, ADC_FB_FRCRST_CH2, 0x17);
+ status = afe_write_byte(dev, ADC_FB_FRCRST_CH3, 0x17);
+
+ /* start quantilizer calibration */
+ status = afe_write_byte(dev, ADC_CAL_ATEST_CH1, 0x10);
+ status = afe_write_byte(dev, ADC_CAL_ATEST_CH2, 0x10);
+ status = afe_write_byte(dev, ADC_CAL_ATEST_CH3, 0x10);
+ msleep(5);
+
+ /* exit modulator (fb) reset */
+ status = afe_write_byte(dev, ADC_FB_FRCRST_CH1, 0x07);
+ status = afe_write_byte(dev, ADC_FB_FRCRST_CH2, 0x07);
+ status = afe_write_byte(dev, ADC_FB_FRCRST_CH3, 0x07);
+
+ /* enable the pre_clamp in each channel for single-ended input */
+ status = afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH1, 0xf0);
+ status = afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH2, 0xf0);
+ status = afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH3, 0xf0);
+
+ /* use diode instead of resistor, so set term_en to 0, res_en to 0 */
+ status = cx231xx_reg_mask_write(dev, AFE_DEVICE_ADDRESS, 8,
+ ADC_QGAIN_RES_TRM_CH1, 3, 7, 0x00);
+ status = cx231xx_reg_mask_write(dev, AFE_DEVICE_ADDRESS, 8,
+ ADC_QGAIN_RES_TRM_CH2, 3, 7, 0x00);
+ status = cx231xx_reg_mask_write(dev, AFE_DEVICE_ADDRESS, 8,
+ ADC_QGAIN_RES_TRM_CH3, 3, 7, 0x00);
+
+ /* dynamic element matching off */
+ status = afe_write_byte(dev, ADC_DCSERVO_DEM_CH1, 0x03);
+ status = afe_write_byte(dev, ADC_DCSERVO_DEM_CH2, 0x03);
+ status = afe_write_byte(dev, ADC_DCSERVO_DEM_CH3, 0x03);
+
+ return status;
+}
+
+int cx231xx_afe_setup_AFE_for_baseband(struct cx231xx *dev)
+{
+ u8 c_value = 0;
+ int status = 0;
+
+ status = afe_read_byte(dev, ADC_PWRDN_CLAMP_CH2, &c_value);
+ c_value &= (~(0x50));
+ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, c_value);
+
+ return status;
+}
+
+/*
+ The Analog Front End in Cx231xx has 3 channels. These
+ channels are used to share between different inputs
+ like tuner, s-video and composite inputs.
+
+ channel 1 ----- pin 1 to pin4(in reg is 1-4)
+ channel 2 ----- pin 5 to pin8(in reg is 5-8)
+ channel 3 ----- pin 9 to pin 12(in reg is 9-11)
+*/
+int cx231xx_afe_set_input_mux(struct cx231xx *dev, u32 input_mux)
+{
+ u8 ch1_setting = (u8) input_mux;
+ u8 ch2_setting = (u8) (input_mux >> 8);
+ u8 ch3_setting = (u8) (input_mux >> 16);
+ int status = 0;
+ u8 value = 0;
+
+ if (ch1_setting != 0) {
+ status = afe_read_byte(dev, ADC_INPUT_CH1, &value);
+ value &= ~INPUT_SEL_MASK;
+ value |= (ch1_setting - 1) << 4;
+ value &= 0xff;
+ status = afe_write_byte(dev, ADC_INPUT_CH1, value);
+ }
+
+ if (ch2_setting != 0) {
+ status = afe_read_byte(dev, ADC_INPUT_CH2, &value);
+ value &= ~INPUT_SEL_MASK;
+ value |= (ch2_setting - 1) << 4;
+ value &= 0xff;
+ status = afe_write_byte(dev, ADC_INPUT_CH2, value);
+ }
+
+ /* For ch3_setting, the value to put in the register is
+ 7 less than the input number */
+ if (ch3_setting != 0) {
+ status = afe_read_byte(dev, ADC_INPUT_CH3, &value);
+ value &= ~INPUT_SEL_MASK;
+ value |= (ch3_setting - 1) << 4;
+ value &= 0xff;
+ status = afe_write_byte(dev, ADC_INPUT_CH3, value);
+ }
+
+ return status;
+}
+
+int cx231xx_afe_set_mode(struct cx231xx *dev, enum AFE_MODE mode)
+{
+ int status = 0;
+
+ /*
+ * FIXME: We need to implement the AFE code for LOW IF and for HI IF.
+ * Currently, only baseband works.
+ */
+
+ switch (mode) {
+ case AFE_MODE_LOW_IF:
+ cx231xx_Setup_AFE_for_LowIF(dev);
+ break;
+ case AFE_MODE_BASEBAND:
+ status = cx231xx_afe_setup_AFE_for_baseband(dev);
+ break;
+ case AFE_MODE_EU_HI_IF:
+ /* SetupAFEforEuHiIF(); */
+ break;
+ case AFE_MODE_US_HI_IF:
+ /* SetupAFEforUsHiIF(); */
+ break;
+ case AFE_MODE_JAPAN_HI_IF:
+ /* SetupAFEforJapanHiIF(); */
+ break;
+ }
+
+ if ((mode != dev->afe_mode) &&
+ (dev->video_input == CX231XX_VMUX_TELEVISION))
+ status = cx231xx_afe_adjust_ref_count(dev,
+ CX231XX_VMUX_TELEVISION);
+
+ dev->afe_mode = mode;
+
+ return status;
+}
+
+int cx231xx_afe_update_power_control(struct cx231xx *dev,
+ enum AV_MODE avmode)
+{
+ u8 afe_power_status = 0;
+ int status = 0;
+
+ switch (dev->model) {
+ case CX231XX_BOARD_CNXT_CARRAERA:
+ case CX231XX_BOARD_CNXT_RDE_250:
+ case CX231XX_BOARD_CNXT_SHELBY:
+ case CX231XX_BOARD_CNXT_RDU_250:
+ case CX231XX_BOARD_CNXT_RDE_253S:
+ case CX231XX_BOARD_CNXT_RDU_253S:
+ case CX231XX_BOARD_CNXT_VIDEO_GRABBER:
+ case CX231XX_BOARD_HAUPPAUGE_EXETER:
+ case CX231XX_BOARD_HAUPPAUGE_USBLIVE2:
+ case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID:
+ case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL:
+ case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC:
+ if (avmode == POLARIS_AVMODE_ANALOGT_TV) {
+ while (afe_power_status != (FLD_PWRDN_TUNING_BIAS |
+ FLD_PWRDN_ENABLE_PLL)) {
+ status = afe_write_byte(dev, SUP_BLK_PWRDN,
+ FLD_PWRDN_TUNING_BIAS |
+ FLD_PWRDN_ENABLE_PLL);
+ status |= afe_read_byte(dev, SUP_BLK_PWRDN,
+ &afe_power_status);
+ if (status < 0)
+ break;
+ }
+
+ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1,
+ 0x00);
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2,
+ 0x00);
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3,
+ 0x00);
+ } else if (avmode == POLARIS_AVMODE_DIGITAL) {
+ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1,
+ 0x70);
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2,
+ 0x70);
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3,
+ 0x70);
+
+ status |= afe_read_byte(dev, SUP_BLK_PWRDN,
+ &afe_power_status);
+ afe_power_status |= FLD_PWRDN_PD_BANDGAP |
+ FLD_PWRDN_PD_BIAS |
+ FLD_PWRDN_PD_TUNECK;
+ status |= afe_write_byte(dev, SUP_BLK_PWRDN,
+ afe_power_status);
+ } else if (avmode == POLARIS_AVMODE_ENXTERNAL_AV) {
+ while (afe_power_status != (FLD_PWRDN_TUNING_BIAS |
+ FLD_PWRDN_ENABLE_PLL)) {
+ status = afe_write_byte(dev, SUP_BLK_PWRDN,
+ FLD_PWRDN_TUNING_BIAS |
+ FLD_PWRDN_ENABLE_PLL);
+ status |= afe_read_byte(dev, SUP_BLK_PWRDN,
+ &afe_power_status);
+ if (status < 0)
+ break;
+ }
+
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1,
+ 0x00);
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2,
+ 0x00);
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3,
+ 0x00);
+ } else {
+ cx231xx_info("Invalid AV mode input\n");
+ status = -1;
+ }
+ break;
+ default:
+ if (avmode == POLARIS_AVMODE_ANALOGT_TV) {
+ while (afe_power_status != (FLD_PWRDN_TUNING_BIAS |
+ FLD_PWRDN_ENABLE_PLL)) {
+ status = afe_write_byte(dev, SUP_BLK_PWRDN,
+ FLD_PWRDN_TUNING_BIAS |
+ FLD_PWRDN_ENABLE_PLL);
+ status |= afe_read_byte(dev, SUP_BLK_PWRDN,
+ &afe_power_status);
+ if (status < 0)
+ break;
+ }
+
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1,
+ 0x40);
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2,
+ 0x40);
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3,
+ 0x00);
+ } else if (avmode == POLARIS_AVMODE_DIGITAL) {
+ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1,
+ 0x70);
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2,
+ 0x70);
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3,
+ 0x70);
+
+ status |= afe_read_byte(dev, SUP_BLK_PWRDN,
+ &afe_power_status);
+ afe_power_status |= FLD_PWRDN_PD_BANDGAP |
+ FLD_PWRDN_PD_BIAS |
+ FLD_PWRDN_PD_TUNECK;
+ status |= afe_write_byte(dev, SUP_BLK_PWRDN,
+ afe_power_status);
+ } else if (avmode == POLARIS_AVMODE_ENXTERNAL_AV) {
+ while (afe_power_status != (FLD_PWRDN_TUNING_BIAS |
+ FLD_PWRDN_ENABLE_PLL)) {
+ status = afe_write_byte(dev, SUP_BLK_PWRDN,
+ FLD_PWRDN_TUNING_BIAS |
+ FLD_PWRDN_ENABLE_PLL);
+ status |= afe_read_byte(dev, SUP_BLK_PWRDN,
+ &afe_power_status);
+ if (status < 0)
+ break;
+ }
+
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1,
+ 0x00);
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2,
+ 0x00);
+ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3,
+ 0x40);
+ } else {
+ cx231xx_info("Invalid AV mode input\n");
+ status = -1;
+ }
+ } /* switch */
+
+ return status;
+}
+
+int cx231xx_afe_adjust_ref_count(struct cx231xx *dev, u32 video_input)
+{
+ u8 input_mode = 0;
+ u8 ntf_mode = 0;
+ int status = 0;
+
+ dev->video_input = video_input;
+
+ if (video_input == CX231XX_VMUX_TELEVISION) {
+ status = afe_read_byte(dev, ADC_INPUT_CH3, &input_mode);
+ status = afe_read_byte(dev, ADC_NTF_PRECLMP_EN_CH3,
+ &ntf_mode);
+ } else {
+ status = afe_read_byte(dev, ADC_INPUT_CH1, &input_mode);
+ status = afe_read_byte(dev, ADC_NTF_PRECLMP_EN_CH1,
+ &ntf_mode);
+ }
+
+ input_mode = (ntf_mode & 0x3) | ((input_mode & 0x6) << 1);
+
+ switch (input_mode) {
+ case SINGLE_ENDED:
+ dev->afe_ref_count = 0x23C;
+ break;
+ case LOW_IF:
+ dev->afe_ref_count = 0x24C;
+ break;
+ case EU_IF:
+ dev->afe_ref_count = 0x258;
+ break;
+ case US_IF:
+ dev->afe_ref_count = 0x260;
+ break;
+ default:
+ break;
+ }
+
+ status = cx231xx_afe_init_super_block(dev, dev->afe_ref_count);
+
+ return status;
+}
+
+/******************************************************************************
+ * V I D E O / A U D I O D E C O D E R C O N T R O L functions *
+ ******************************************************************************/
+static int vid_blk_write_byte(struct cx231xx *dev, u16 saddr, u8 data)
+{
+ return cx231xx_write_i2c_data(dev, VID_BLK_I2C_ADDRESS,
+ saddr, 2, data, 1);
+}
+
+static int vid_blk_read_byte(struct cx231xx *dev, u16 saddr, u8 *data)
+{
+ int status;
+ u32 temp = 0;
+
+ status = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
+ saddr, 2, &temp, 1);
+ *data = (u8) temp;
+ return status;
+}
+
+static int vid_blk_write_word(struct cx231xx *dev, u16 saddr, u32 data)
+{
+ return cx231xx_write_i2c_data(dev, VID_BLK_I2C_ADDRESS,
+ saddr, 2, data, 4);
+}
+
+static int vid_blk_read_word(struct cx231xx *dev, u16 saddr, u32 *data)
+{
+ return cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
+ saddr, 2, data, 4);
+}
+int cx231xx_check_fw(struct cx231xx *dev)
+{
+ u8 temp = 0;
+ int status = 0;
+ status = vid_blk_read_byte(dev, DL_CTL_ADDRESS_LOW, &temp);
+ if (status < 0)
+ return status;
+ else
+ return temp;
+
+}
+
+int cx231xx_set_video_input_mux(struct cx231xx *dev, u8 input)
+{
+ int status = 0;
+
+ switch (INPUT(input)->type) {
+ case CX231XX_VMUX_COMPOSITE1:
+ case CX231XX_VMUX_SVIDEO:
+ if ((dev->current_pcb_config.type == USB_BUS_POWER) &&
+ (dev->power_mode != POLARIS_AVMODE_ENXTERNAL_AV)) {
+ /* External AV */
+ status = cx231xx_set_power_mode(dev,
+ POLARIS_AVMODE_ENXTERNAL_AV);
+ if (status < 0) {
+ cx231xx_errdev("%s: set_power_mode : Failed to"
+ " set Power - errCode [%d]!\n",
+ __func__, status);
+ return status;
+ }
+ }
+ status = cx231xx_set_decoder_video_input(dev,
+ INPUT(input)->type,
+ INPUT(input)->vmux);
+ break;
+ case CX231XX_VMUX_TELEVISION:
+ case CX231XX_VMUX_CABLE:
+ if ((dev->current_pcb_config.type == USB_BUS_POWER) &&
+ (dev->power_mode != POLARIS_AVMODE_ANALOGT_TV)) {
+ /* Tuner */
+ status = cx231xx_set_power_mode(dev,
+ POLARIS_AVMODE_ANALOGT_TV);
+ if (status < 0) {
+ cx231xx_errdev("%s: set_power_mode:Failed"
+ " to set Power - errCode [%d]!\n",
+ __func__, status);
+ return status;
+ }
+ }
+ if (dev->tuner_type == TUNER_NXP_TDA18271)
+ status = cx231xx_set_decoder_video_input(dev,
+ CX231XX_VMUX_TELEVISION,
+ INPUT(input)->vmux);
+ else
+ status = cx231xx_set_decoder_video_input(dev,
+ CX231XX_VMUX_COMPOSITE1,
+ INPUT(input)->vmux);
+
+ break;
+ default:
+ cx231xx_errdev("%s: set_power_mode : Unknown Input %d !\n",
+ __func__, INPUT(input)->type);
+ break;
+ }
+
+ /* save the selection */
+ dev->video_input = input;
+
+ return status;
+}
+
+int cx231xx_set_decoder_video_input(struct cx231xx *dev,
+ u8 pin_type, u8 input)
+{
+ int status = 0;
+ u32 value = 0;
+
+ if (pin_type != dev->video_input) {
+ status = cx231xx_afe_adjust_ref_count(dev, pin_type);
+ if (status < 0) {
+ cx231xx_errdev("%s: adjust_ref_count :Failed to set"
+ "AFE input mux - errCode [%d]!\n",
+ __func__, status);
+ return status;
+ }
+ }
+
+ /* call afe block to set video inputs */
+ status = cx231xx_afe_set_input_mux(dev, input);
+ if (status < 0) {
+ cx231xx_errdev("%s: set_input_mux :Failed to set"
+ " AFE input mux - errCode [%d]!\n",
+ __func__, status);
+ return status;
+ }
+
+ switch (pin_type) {
+ case CX231XX_VMUX_COMPOSITE1:
+ status = vid_blk_read_word(dev, AFE_CTRL, &value);
+ value |= (0 << 13) | (1 << 4);
+ value &= ~(1 << 5);
+
+ /* set [24:23] [22:15] to 0 */
+ value &= (~(0x1ff8000));
+ /* set FUNC_MODE[24:23] = 2 IF_MOD[22:15] = 0 */
+ value |= 0x1000000;
+ status = vid_blk_write_word(dev, AFE_CTRL, value);
+
+ status = vid_blk_read_word(dev, OUT_CTRL1, &value);
+ value |= (1 << 7);
+ status = vid_blk_write_word(dev, OUT_CTRL1, value);
+
+ /* Set output mode */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ OUT_CTRL1,
+ FLD_OUT_MODE,
+ dev->board.output_mode);
+
+ /* Tell DIF object to go to baseband mode */
+ status = cx231xx_dif_set_standard(dev, DIF_USE_BASEBAND);
+ if (status < 0) {
+ cx231xx_errdev("%s: cx231xx_dif set to By pass"
+ " mode- errCode [%d]!\n",
+ __func__, status);
+ return status;
+ }
+
+ /* Read the DFE_CTRL1 register */
+ status = vid_blk_read_word(dev, DFE_CTRL1, &value);
+
+ /* enable the VBI_GATE_EN */
+ value |= FLD_VBI_GATE_EN;
+
+ /* Enable the auto-VGA enable */
+ value |= FLD_VGA_AUTO_EN;
+
+ /* Write it back */
+ status = vid_blk_write_word(dev, DFE_CTRL1, value);
+
+ /* Disable auto config of registers */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ MODE_CTRL, FLD_ACFG_DIS,
+ cx231xx_set_field(FLD_ACFG_DIS, 1));
+
+ /* Set CVBS input mode */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ MODE_CTRL, FLD_INPUT_MODE,
+ cx231xx_set_field(FLD_INPUT_MODE, INPUT_MODE_CVBS_0));
+ break;
+ case CX231XX_VMUX_SVIDEO:
+ /* Disable the use of DIF */
+
+ status = vid_blk_read_word(dev, AFE_CTRL, &value);
+
+ /* set [24:23] [22:15] to 0 */
+ value &= (~(0x1ff8000));
+ /* set FUNC_MODE[24:23] = 2
+ IF_MOD[22:15] = 0 DCR_BYP_CH2[4:4] = 1; */
+ value |= 0x1000010;
+ status = vid_blk_write_word(dev, AFE_CTRL, value);
+
+ /* Tell DIF object to go to baseband mode */
+ status = cx231xx_dif_set_standard(dev, DIF_USE_BASEBAND);
+ if (status < 0) {
+ cx231xx_errdev("%s: cx231xx_dif set to By pass"
+ " mode- errCode [%d]!\n",
+ __func__, status);
+ return status;
+ }
+
+ /* Read the DFE_CTRL1 register */
+ status = vid_blk_read_word(dev, DFE_CTRL1, &value);
+
+ /* enable the VBI_GATE_EN */
+ value |= FLD_VBI_GATE_EN;
+
+ /* Enable the auto-VGA enable */
+ value |= FLD_VGA_AUTO_EN;
+
+ /* Write it back */
+ status = vid_blk_write_word(dev, DFE_CTRL1, value);
+
+ /* Disable auto config of registers */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ MODE_CTRL, FLD_ACFG_DIS,
+ cx231xx_set_field(FLD_ACFG_DIS, 1));
+
+ /* Set YC input mode */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ MODE_CTRL,
+ FLD_INPUT_MODE,
+ cx231xx_set_field(FLD_INPUT_MODE, INPUT_MODE_YC_1));
+
+ /* Chroma to ADC2 */
+ status = vid_blk_read_word(dev, AFE_CTRL, &value);
+ value |= FLD_CHROMA_IN_SEL; /* set the chroma in select */
+
+ /* Clear VGA_SEL_CH2 and VGA_SEL_CH3 (bits 7 and 8)
+ This sets them to use video
+ rather than audio. Only one of the two will be in use. */
+ value &= ~(FLD_VGA_SEL_CH2 | FLD_VGA_SEL_CH3);
+
+ status = vid_blk_write_word(dev, AFE_CTRL, value);
+
+ status = cx231xx_afe_set_mode(dev, AFE_MODE_BASEBAND);
+ break;
+ case CX231XX_VMUX_TELEVISION:
+ case CX231XX_VMUX_CABLE:
+ default:
+ /* TODO: Test if this is also needed for xc2028/xc3028 */
+ if (dev->board.tuner_type == TUNER_XC5000) {
+ /* Disable the use of DIF */
+
+ status = vid_blk_read_word(dev, AFE_CTRL, &value);
+ value |= (0 << 13) | (1 << 4);
+ value &= ~(1 << 5);
+
+ /* set [24:23] [22:15] to 0 */
+ value &= (~(0x1FF8000));
+ /* set FUNC_MODE[24:23] = 2 IF_MOD[22:15] = 0 */
+ value |= 0x1000000;
+ status = vid_blk_write_word(dev, AFE_CTRL, value);
+
+ status = vid_blk_read_word(dev, OUT_CTRL1, &value);
+ value |= (1 << 7);
+ status = vid_blk_write_word(dev, OUT_CTRL1, value);
+
+ /* Set output mode */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ OUT_CTRL1, FLD_OUT_MODE,
+ dev->board.output_mode);
+
+ /* Tell DIF object to go to baseband mode */
+ status = cx231xx_dif_set_standard(dev,
+ DIF_USE_BASEBAND);
+ if (status < 0) {
+ cx231xx_errdev("%s: cx231xx_dif set to By pass"
+ " mode- errCode [%d]!\n",
+ __func__, status);
+ return status;
+ }
+
+ /* Read the DFE_CTRL1 register */
+ status = vid_blk_read_word(dev, DFE_CTRL1, &value);
+
+ /* enable the VBI_GATE_EN */
+ value |= FLD_VBI_GATE_EN;
+
+ /* Enable the auto-VGA enable */
+ value |= FLD_VGA_AUTO_EN;
+
+ /* Write it back */
+ status = vid_blk_write_word(dev, DFE_CTRL1, value);
+
+ /* Disable auto config of registers */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ MODE_CTRL, FLD_ACFG_DIS,
+ cx231xx_set_field(FLD_ACFG_DIS, 1));
+
+ /* Set CVBS input mode */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ MODE_CTRL, FLD_INPUT_MODE,
+ cx231xx_set_field(FLD_INPUT_MODE,
+ INPUT_MODE_CVBS_0));
+ } else {
+ /* Enable the DIF for the tuner */
+
+ /* Reinitialize the DIF */
+ status = cx231xx_dif_set_standard(dev, dev->norm);
+ if (status < 0) {
+ cx231xx_errdev("%s: cx231xx_dif set to By pass"
+ " mode- errCode [%d]!\n",
+ __func__, status);
+ return status;
+ }
+
+ /* Make sure bypass is cleared */
+ status = vid_blk_read_word(dev, DIF_MISC_CTRL, &value);
+
+ /* Clear the bypass bit */
+ value &= ~FLD_DIF_DIF_BYPASS;
+
+ /* Enable the use of the DIF block */
+ status = vid_blk_write_word(dev, DIF_MISC_CTRL, value);
+
+ /* Read the DFE_CTRL1 register */
+ status = vid_blk_read_word(dev, DFE_CTRL1, &value);
+
+ /* Disable the VBI_GATE_EN */
+ value &= ~FLD_VBI_GATE_EN;
+
+ /* Enable the auto-VGA enable, AGC, and
+ set the skip count to 2 */
+ value |= FLD_VGA_AUTO_EN | FLD_AGC_AUTO_EN | 0x00200000;
+
+ /* Write it back */
+ status = vid_blk_write_word(dev, DFE_CTRL1, value);
+
+ /* Wait until AGC locks up */
+ msleep(1);
+
+ /* Disable the auto-VGA enable AGC */
+ value &= ~(FLD_VGA_AUTO_EN);
+
+ /* Write it back */
+ status = vid_blk_write_word(dev, DFE_CTRL1, value);
+
+ /* Enable Polaris B0 AGC output */
+ status = vid_blk_read_word(dev, PIN_CTRL, &value);
+ value |= (FLD_OEF_AGC_RF) |
+ (FLD_OEF_AGC_IFVGA) |
+ (FLD_OEF_AGC_IF);
+ status = vid_blk_write_word(dev, PIN_CTRL, value);
+
+ /* Set output mode */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ OUT_CTRL1, FLD_OUT_MODE,
+ dev->board.output_mode);
+
+ /* Disable auto config of registers */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ MODE_CTRL, FLD_ACFG_DIS,
+ cx231xx_set_field(FLD_ACFG_DIS, 1));
+
+ /* Set CVBS input mode */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ MODE_CTRL, FLD_INPUT_MODE,
+ cx231xx_set_field(FLD_INPUT_MODE,
+ INPUT_MODE_CVBS_0));
+
+ /* Set some bits in AFE_CTRL so that channel 2 or 3
+ * is ready to receive audio */
+ /* Clear clamp for channels 2 and 3 (bit 16-17) */
+ /* Clear droop comp (bit 19-20) */
+ /* Set VGA_SEL (for audio control) (bit 7-8) */
+ status = vid_blk_read_word(dev, AFE_CTRL, &value);
+
+ /*Set Func mode:01-DIF 10-baseband 11-YUV*/
+ value &= (~(FLD_FUNC_MODE));
+ value |= 0x800000;
+
+ value |= FLD_VGA_SEL_CH3 | FLD_VGA_SEL_CH2;
+
+ status = vid_blk_write_word(dev, AFE_CTRL, value);
+
+ if (dev->tuner_type == TUNER_NXP_TDA18271) {
+ status = vid_blk_read_word(dev, PIN_CTRL,
+ &value);
+ status = vid_blk_write_word(dev, PIN_CTRL,
+ (value & 0xFFFFFFEF));
+ }
+
+ break;
+
+ }
+ break;
+ }
+
+ /* Set raw VBI mode */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ OUT_CTRL1, FLD_VBIHACTRAW_EN,
+ cx231xx_set_field(FLD_VBIHACTRAW_EN, 1));
+
+ status = vid_blk_read_word(dev, OUT_CTRL1, &value);
+ if (value & 0x02) {
+ value |= (1 << 19);
+ status = vid_blk_write_word(dev, OUT_CTRL1, value);
+ }
+
+ return status;
+}
+
+void cx231xx_enable656(struct cx231xx *dev)
+{
+ u8 temp = 0;
+ int status;
+ /*enable TS1 data[0:7] as output to export 656*/
+
+ status = vid_blk_write_byte(dev, TS1_PIN_CTL0, 0xFF);
+
+ /*enable TS1 clock as output to export 656*/
+
+ status = vid_blk_read_byte(dev, TS1_PIN_CTL1, &temp);
+ temp = temp|0x04;
+
+ status = vid_blk_write_byte(dev, TS1_PIN_CTL1, temp);
+
+}
+EXPORT_SYMBOL_GPL(cx231xx_enable656);
+
+void cx231xx_disable656(struct cx231xx *dev)
+{
+ u8 temp = 0;
+ int status;
+
+
+ status = vid_blk_write_byte(dev, TS1_PIN_CTL0, 0x00);
+
+ status = vid_blk_read_byte(dev, TS1_PIN_CTL1, &temp);
+ temp = temp&0xFB;
+
+ status = vid_blk_write_byte(dev, TS1_PIN_CTL1, temp);
+}
+EXPORT_SYMBOL_GPL(cx231xx_disable656);
+
+/*
+ * Handle any video-mode specific overrides that are different
+ * on a per video standards basis after touching the MODE_CTRL
+ * register which resets many values for autodetect
+ */
+int cx231xx_do_mode_ctrl_overrides(struct cx231xx *dev)
+{
+ int status = 0;
+
+ cx231xx_info("do_mode_ctrl_overrides : 0x%x\n",
+ (unsigned int)dev->norm);
+
+ /* Change the DFE_CTRL3 bp_percent to fix flagging */
+ status = vid_blk_write_word(dev, DFE_CTRL3, 0xCD3F0280);
+
+ if (dev->norm & (V4L2_STD_NTSC | V4L2_STD_PAL_M)) {
+ cx231xx_info("do_mode_ctrl_overrides NTSC\n");
+
+ /* Move the close caption lines out of active video,
+ adjust the active video start point */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ VERT_TIM_CTRL,
+ FLD_VBLANK_CNT, 0x18);
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ VERT_TIM_CTRL,
+ FLD_VACTIVE_CNT,
+ 0x1E7000);
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ VERT_TIM_CTRL,
+ FLD_V656BLANK_CNT,
+ 0x1C000000);
+
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ HORIZ_TIM_CTRL,
+ FLD_HBLANK_CNT,
+ cx231xx_set_field
+ (FLD_HBLANK_CNT, 0x79));
+
+ } else if (dev->norm & V4L2_STD_SECAM) {
+ cx231xx_info("do_mode_ctrl_overrides SECAM\n");
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ VERT_TIM_CTRL,
+ FLD_VBLANK_CNT, 0x20);
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ VERT_TIM_CTRL,
+ FLD_VACTIVE_CNT,
+ cx231xx_set_field
+ (FLD_VACTIVE_CNT,
+ 0x244));
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ VERT_TIM_CTRL,
+ FLD_V656BLANK_CNT,
+ cx231xx_set_field
+ (FLD_V656BLANK_CNT,
+ 0x24));
+ /* Adjust the active video horizontal start point */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ HORIZ_TIM_CTRL,
+ FLD_HBLANK_CNT,
+ cx231xx_set_field
+ (FLD_HBLANK_CNT, 0x85));
+ } else {
+ cx231xx_info("do_mode_ctrl_overrides PAL\n");
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ VERT_TIM_CTRL,
+ FLD_VBLANK_CNT, 0x20);
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ VERT_TIM_CTRL,
+ FLD_VACTIVE_CNT,
+ cx231xx_set_field
+ (FLD_VACTIVE_CNT,
+ 0x244));
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ VERT_TIM_CTRL,
+ FLD_V656BLANK_CNT,
+ cx231xx_set_field
+ (FLD_V656BLANK_CNT,
+ 0x24));
+ /* Adjust the active video horizontal start point */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ HORIZ_TIM_CTRL,
+ FLD_HBLANK_CNT,
+ cx231xx_set_field
+ (FLD_HBLANK_CNT, 0x85));
+
+ }
+
+ return status;
+}
+
+int cx231xx_unmute_audio(struct cx231xx *dev)
+{
+ return vid_blk_write_byte(dev, PATH1_VOL_CTL, 0x24);
+}
+EXPORT_SYMBOL_GPL(cx231xx_unmute_audio);
+
+int stopAudioFirmware(struct cx231xx *dev)
+{
+ return vid_blk_write_byte(dev, DL_CTL_CONTROL, 0x03);
+}
+
+int restartAudioFirmware(struct cx231xx *dev)
+{
+ return vid_blk_write_byte(dev, DL_CTL_CONTROL, 0x13);
+}
+
+int cx231xx_set_audio_input(struct cx231xx *dev, u8 input)
+{
+ int status = 0;
+ enum AUDIO_INPUT ainput = AUDIO_INPUT_LINE;
+
+ switch (INPUT(input)->amux) {
+ case CX231XX_AMUX_VIDEO:
+ ainput = AUDIO_INPUT_TUNER_TV;
+ break;
+ case CX231XX_AMUX_LINE_IN:
+ status = cx231xx_i2s_blk_set_audio_input(dev, input);
+ ainput = AUDIO_INPUT_LINE;
+ break;
+ default:
+ break;
+ }
+
+ status = cx231xx_set_audio_decoder_input(dev, ainput);
+
+ return status;
+}
+
+int cx231xx_set_audio_decoder_input(struct cx231xx *dev,
+ enum AUDIO_INPUT audio_input)
+{
+ u32 dwval;
+ int status;
+ u8 gen_ctrl;
+ u32 value = 0;
+
+ /* Put it in soft reset */
+ status = vid_blk_read_byte(dev, GENERAL_CTL, &gen_ctrl);
+ gen_ctrl |= 1;
+ status = vid_blk_write_byte(dev, GENERAL_CTL, gen_ctrl);
+
+ switch (audio_input) {
+ case AUDIO_INPUT_LINE:
+ /* setup AUD_IO control from Merlin paralle output */
+ value = cx231xx_set_field(FLD_AUD_CHAN1_SRC,
+ AUD_CHAN_SRC_PARALLEL);
+ status = vid_blk_write_word(dev, AUD_IO_CTRL, value);
+
+ /* setup input to Merlin, SRC2 connect to AC97
+ bypass upsample-by-2, slave mode, sony mode, left justify
+ adr 091c, dat 01000000 */
+ status = vid_blk_read_word(dev, AC97_CTL, &dwval);
+
+ status = vid_blk_write_word(dev, AC97_CTL,
+ (dwval | FLD_AC97_UP2X_BYPASS));
+
+ /* select the parallel1 and SRC3 */
+ status = vid_blk_write_word(dev, BAND_OUT_SEL,
+ cx231xx_set_field(FLD_SRC3_IN_SEL, 0x0) |
+ cx231xx_set_field(FLD_SRC3_CLK_SEL, 0x0) |
+ cx231xx_set_field(FLD_PARALLEL1_SRC_SEL, 0x0));
+
+ /* unmute all, AC97 in, independence mode
+ adr 08d0, data 0x00063073 */
+ status = vid_blk_write_word(dev, DL_CTL, 0x3000001);
+ status = vid_blk_write_word(dev, PATH1_CTL1, 0x00063073);
+
+ /* set AVC maximum threshold, adr 08d4, dat ffff0024 */
+ status = vid_blk_read_word(dev, PATH1_VOL_CTL, &dwval);
+ status = vid_blk_write_word(dev, PATH1_VOL_CTL,
+ (dwval | FLD_PATH1_AVC_THRESHOLD));
+
+ /* set SC maximum threshold, adr 08ec, dat ffffb3a3 */
+ status = vid_blk_read_word(dev, PATH1_SC_CTL, &dwval);
+ status = vid_blk_write_word(dev, PATH1_SC_CTL,
+ (dwval | FLD_PATH1_SC_THRESHOLD));
+ break;
+
+ case AUDIO_INPUT_TUNER_TV:
+ default:
+ status = stopAudioFirmware(dev);
+ /* Setup SRC sources and clocks */
+ status = vid_blk_write_word(dev, BAND_OUT_SEL,
+ cx231xx_set_field(FLD_SRC6_IN_SEL, 0x00) |
+ cx231xx_set_field(FLD_SRC6_CLK_SEL, 0x01) |
+ cx231xx_set_field(FLD_SRC5_IN_SEL, 0x00) |
+ cx231xx_set_field(FLD_SRC5_CLK_SEL, 0x02) |
+ cx231xx_set_field(FLD_SRC4_IN_SEL, 0x02) |
+ cx231xx_set_field(FLD_SRC4_CLK_SEL, 0x03) |
+ cx231xx_set_field(FLD_SRC3_IN_SEL, 0x00) |
+ cx231xx_set_field(FLD_SRC3_CLK_SEL, 0x00) |
+ cx231xx_set_field(FLD_BASEBAND_BYPASS_CTL, 0x00) |
+ cx231xx_set_field(FLD_AC97_SRC_SEL, 0x03) |
+ cx231xx_set_field(FLD_I2S_SRC_SEL, 0x00) |
+ cx231xx_set_field(FLD_PARALLEL2_SRC_SEL, 0x02) |
+ cx231xx_set_field(FLD_PARALLEL1_SRC_SEL, 0x01));
+
+ /* Setup the AUD_IO control */
+ status = vid_blk_write_word(dev, AUD_IO_CTRL,
+ cx231xx_set_field(FLD_I2S_PORT_DIR, 0x00) |
+ cx231xx_set_field(FLD_I2S_OUT_SRC, 0x00) |
+ cx231xx_set_field(FLD_AUD_CHAN3_SRC, 0x00) |
+ cx231xx_set_field(FLD_AUD_CHAN2_SRC, 0x00) |
+ cx231xx_set_field(FLD_AUD_CHAN1_SRC, 0x03));
+
+ status = vid_blk_write_word(dev, PATH1_CTL1, 0x1F063870);
+
+ /* setAudioStandard(_audio_standard); */
+ status = vid_blk_write_word(dev, PATH1_CTL1, 0x00063870);
+
+ status = restartAudioFirmware(dev);
+
+ switch (dev->board.tuner_type) {
+ case TUNER_XC5000:
+ /* SIF passthrough at 28.6363 MHz sample rate */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ CHIP_CTRL,
+ FLD_SIF_EN,
+ cx231xx_set_field(FLD_SIF_EN, 1));
+ break;
+ case TUNER_NXP_TDA18271:
+ /* Normal mode: SIF passthrough at 14.32 MHz */
+ status = cx231xx_read_modify_write_i2c_dword(dev,
+ VID_BLK_I2C_ADDRESS,
+ CHIP_CTRL,
+ FLD_SIF_EN,
+ cx231xx_set_field(FLD_SIF_EN, 0));
+ break;
+ default:
+ /* This is just a casual suggestion to people adding
+ new boards in case they use a tuner type we don't
+ currently know about */
+ printk(KERN_INFO "Unknown tuner type configuring SIF");
+ break;
+ }
+ break;
+
+ case AUDIO_INPUT_TUNER_FM:
+ /* use SIF for FM radio
+ setupFM();
+ setAudioStandard(_audio_standard);
+ */
+ break;
+
+ case AUDIO_INPUT_MUTE:
+ status = vid_blk_write_word(dev, PATH1_CTL1, 0x1F011012);
+ break;
+ }
+
+ /* Take it out of soft reset */
+ status = vid_blk_read_byte(dev, GENERAL_CTL, &gen_ctrl);
+ gen_ctrl &= ~1;
+ status = vid_blk_write_byte(dev, GENERAL_CTL, gen_ctrl);
+
+ return status;
+}
+
+/******************************************************************************
+ * C H I P Specific C O N T R O L functions *
+ ******************************************************************************/
+int cx231xx_init_ctrl_pin_status(struct cx231xx *dev)
+{
+ u32 value;
+ int status = 0;
+
+ status = vid_blk_read_word(dev, PIN_CTRL, &value);
+ value |= (~dev->board.ctl_pin_status_mask);
+ status = vid_blk_write_word(dev, PIN_CTRL, value);
+
+ return status;
+}
+
+int cx231xx_set_agc_analog_digital_mux_select(struct cx231xx *dev,
+ u8 analog_or_digital)
+{
+ int status = 0;
+
+ /* first set the direction to output */
+ status = cx231xx_set_gpio_direction(dev,
+ dev->board.
+ agc_analog_digital_select_gpio, 1);
+
+ /* 0 - demod ; 1 - Analog mode */
+ status = cx231xx_set_gpio_value(dev,
+ dev->board.agc_analog_digital_select_gpio,
+ analog_or_digital);
+
+ return status;
+}
+
+int cx231xx_enable_i2c_port_3(struct cx231xx *dev, bool is_port_3)
+{
+ u8 value[4] = { 0, 0, 0, 0 };
+ int status = 0;
+ bool current_is_port_3;
+
+ if (dev->board.dont_use_port_3)
+ is_port_3 = false;
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER,
+ PWR_CTL_EN, value, 4);
+ if (status < 0)
+ return status;
+
+ current_is_port_3 = value[0] & I2C_DEMOD_EN ? true : false;
+
+ /* Just return, if already using the right port */
+ if (current_is_port_3 == is_port_3)
+ return 0;
+
+ if (is_port_3)
+ value[0] |= I2C_DEMOD_EN;
+ else
+ value[0] &= ~I2C_DEMOD_EN;
+
+ cx231xx_info("Changing the i2c master port to %d\n",
+ is_port_3 ? 3 : 1);
+
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ PWR_CTL_EN, value, 4);
+
+ return status;
+
+}
+EXPORT_SYMBOL_GPL(cx231xx_enable_i2c_port_3);
+
+void update_HH_register_after_set_DIF(struct cx231xx *dev)
+{
+/*
+ u8 status = 0;
+ u32 value = 0;
+
+ vid_blk_write_word(dev, PIN_CTRL, 0xA0FFF82F);
+ vid_blk_write_word(dev, DIF_MISC_CTRL, 0x0A203F11);
+ vid_blk_write_word(dev, DIF_SRC_PHASE_INC, 0x1BEFBF06);
+
+ status = vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value);
+ vid_blk_write_word(dev, AFE_CTRL_C2HH_SRC_CTRL, 0x4485D390);
+ status = vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value);
+*/
+}
+
+void cx231xx_dump_HH_reg(struct cx231xx *dev)
+{
+ u8 status = 0;
+ u32 value = 0;
+ u16 i = 0;
+
+ value = 0x45005390;
+ status = vid_blk_write_word(dev, 0x104, value);
+
+ for (i = 0x100; i < 0x140; i++) {
+ status = vid_blk_read_word(dev, i, &value);
+ cx231xx_info("reg0x%x=0x%x\n", i, value);
+ i = i+3;
+ }
+
+ for (i = 0x300; i < 0x400; i++) {
+ status = vid_blk_read_word(dev, i, &value);
+ cx231xx_info("reg0x%x=0x%x\n", i, value);
+ i = i+3;
+ }
+
+ for (i = 0x400; i < 0x440; i++) {
+ status = vid_blk_read_word(dev, i, &value);
+ cx231xx_info("reg0x%x=0x%x\n", i, value);
+ i = i+3;
+ }
+
+ status = vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value);
+ cx231xx_info("AFE_CTRL_C2HH_SRC_CTRL=0x%x\n", value);
+ vid_blk_write_word(dev, AFE_CTRL_C2HH_SRC_CTRL, 0x4485D390);
+ status = vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value);
+ cx231xx_info("AFE_CTRL_C2HH_SRC_CTRL=0x%x\n", value);
+}
+
+void cx231xx_dump_SC_reg(struct cx231xx *dev)
+{
+ u8 value[4] = { 0, 0, 0, 0 };
+ int status = 0;
+ cx231xx_info("cx231xx_dump_SC_reg!\n");
+
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT,
+ value, 4);
+ cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", BOARD_CFG_STAT, value[0],
+ value[1], value[2], value[3]);
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS_MODE_REG,
+ value, 4);
+ cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS_MODE_REG, value[0],
+ value[1], value[2], value[3]);
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS1_CFG_REG,
+ value, 4);
+ cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS1_CFG_REG, value[0],
+ value[1], value[2], value[3]);
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS1_LENGTH_REG,
+ value, 4);
+ cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS1_LENGTH_REG, value[0],
+ value[1], value[2], value[3]);
+
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS2_CFG_REG,
+ value, 4);
+ cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS2_CFG_REG, value[0],
+ value[1], value[2], value[3]);
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS2_LENGTH_REG,
+ value, 4);
+ cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS2_LENGTH_REG, value[0],
+ value[1], value[2], value[3]);
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET,
+ value, 4);
+ cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", EP_MODE_SET, value[0],
+ value[1], value[2], value[3]);
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN1,
+ value, 4);
+ cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN1, value[0],
+ value[1], value[2], value[3]);
+
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN2,
+ value, 4);
+ cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN2, value[0],
+ value[1], value[2], value[3]);
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN3,
+ value, 4);
+ cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN3, value[0],
+ value[1], value[2], value[3]);
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK0,
+ value, 4);
+ cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK0, value[0],
+ value[1], value[2], value[3]);
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK1,
+ value, 4);
+ cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK1, value[0],
+ value[1], value[2], value[3]);
+
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK2,
+ value, 4);
+ cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK2, value[0],
+ value[1], value[2], value[3]);
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_GAIN,
+ value, 4);
+ cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_GAIN, value[0],
+ value[1], value[2], value[3]);
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_CAR_REG,
+ value, 4);
+ cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_CAR_REG, value[0],
+ value[1], value[2], value[3]);
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_OT_CFG1,
+ value, 4);
+ cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_OT_CFG1, value[0],
+ value[1], value[2], value[3]);
+
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_OT_CFG2,
+ value, 4);
+ cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_OT_CFG2, value[0],
+ value[1], value[2], value[3]);
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN,
+ value, 4);
+ cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", PWR_CTL_EN, value[0],
+ value[1], value[2], value[3]);
+
+
+}
+
+void cx231xx_Setup_AFE_for_LowIF(struct cx231xx *dev)
+
+{
+ u8 status = 0;
+ u8 value = 0;
+
+
+
+ status = afe_read_byte(dev, ADC_STATUS2_CH3, &value);
+ value = (value & 0xFE)|0x01;
+ status = afe_write_byte(dev, ADC_STATUS2_CH3, value);
+
+ status = afe_read_byte(dev, ADC_STATUS2_CH3, &value);
+ value = (value & 0xFE)|0x00;
+ status = afe_write_byte(dev, ADC_STATUS2_CH3, value);
+
+
+/*
+ config colibri to lo-if mode
+
+ FIXME: ntf_mode = 2'b00 by default. But set 0x1 would reduce
+ the diff IF input by half,
+
+ for low-if agc defect
+*/
+
+ status = afe_read_byte(dev, ADC_NTF_PRECLMP_EN_CH3, &value);
+ value = (value & 0xFC)|0x00;
+ status = afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH3, value);
+
+ status = afe_read_byte(dev, ADC_INPUT_CH3, &value);
+ value = (value & 0xF9)|0x02;
+ status = afe_write_byte(dev, ADC_INPUT_CH3, value);
+
+ status = afe_read_byte(dev, ADC_FB_FRCRST_CH3, &value);
+ value = (value & 0xFB)|0x04;
+ status = afe_write_byte(dev, ADC_FB_FRCRST_CH3, value);
+
+ status = afe_read_byte(dev, ADC_DCSERVO_DEM_CH3, &value);
+ value = (value & 0xFC)|0x03;
+ status = afe_write_byte(dev, ADC_DCSERVO_DEM_CH3, value);
+
+ status = afe_read_byte(dev, ADC_CTRL_DAC1_CH3, &value);
+ value = (value & 0xFB)|0x04;
+ status = afe_write_byte(dev, ADC_CTRL_DAC1_CH3, value);
+
+ status = afe_read_byte(dev, ADC_CTRL_DAC23_CH3, &value);
+ value = (value & 0xF8)|0x06;
+ status = afe_write_byte(dev, ADC_CTRL_DAC23_CH3, value);
+
+ status = afe_read_byte(dev, ADC_CTRL_DAC23_CH3, &value);
+ value = (value & 0x8F)|0x40;
+ status = afe_write_byte(dev, ADC_CTRL_DAC23_CH3, value);
+
+ status = afe_read_byte(dev, ADC_PWRDN_CLAMP_CH3, &value);
+ value = (value & 0xDF)|0x20;
+ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, value);
+}
+
+void cx231xx_set_Colibri_For_LowIF(struct cx231xx *dev, u32 if_freq,
+ u8 spectral_invert, u32 mode)
+{
+ u32 colibri_carrier_offset = 0;
+ u8 status = 0;
+ u32 func_mode = 0x01; /* Device has a DIF if this function is called */
+ u32 standard = 0;
+ u8 value[4] = { 0, 0, 0, 0 };
+
+ cx231xx_info("Enter cx231xx_set_Colibri_For_LowIF()\n");
+ value[0] = (u8) 0x6F;
+ value[1] = (u8) 0x6F;
+ value[2] = (u8) 0x6F;
+ value[3] = (u8) 0x6F;
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ PWR_CTL_EN, value, 4);
+
+ /*Set colibri for low IF*/
+ status = cx231xx_afe_set_mode(dev, AFE_MODE_LOW_IF);
+
+ /* Set C2HH for low IF operation.*/
+ standard = dev->norm;
+ status = cx231xx_dif_configure_C2HH_for_low_IF(dev, dev->active_mode,
+ func_mode, standard);
+
+ /* Get colibri offsets.*/
+ colibri_carrier_offset = cx231xx_Get_Colibri_CarrierOffset(mode,
+ standard);
+
+ cx231xx_info("colibri_carrier_offset=%d, standard=0x%x\n",
+ colibri_carrier_offset, standard);
+
+ /* Set the band Pass filter for DIF*/
+ cx231xx_set_DIF_bandpass(dev, (if_freq+colibri_carrier_offset),
+ spectral_invert, mode);
+}
+
+u32 cx231xx_Get_Colibri_CarrierOffset(u32 mode, u32 standerd)
+{
+ u32 colibri_carrier_offset = 0;
+
+ if (mode == TUNER_MODE_FM_RADIO) {
+ colibri_carrier_offset = 1100000;
+ } else if (standerd & (V4L2_STD_MN | V4L2_STD_NTSC_M_JP)) {
+ colibri_carrier_offset = 4832000; /*4.83MHz */
+ } else if (standerd & (V4L2_STD_PAL_B | V4L2_STD_PAL_G)) {
+ colibri_carrier_offset = 2700000; /*2.70MHz */
+ } else if (standerd & (V4L2_STD_PAL_D | V4L2_STD_PAL_I
+ | V4L2_STD_SECAM)) {
+ colibri_carrier_offset = 2100000; /*2.10MHz */
+ }
+
+ return colibri_carrier_offset;
+}
+
+void cx231xx_set_DIF_bandpass(struct cx231xx *dev, u32 if_freq,
+ u8 spectral_invert, u32 mode)
+{
+ unsigned long pll_freq_word;
+ int status = 0;
+ u32 dif_misc_ctrl_value = 0;
+ u64 pll_freq_u64 = 0;
+ u32 i = 0;
+
+ cx231xx_info("if_freq=%d;spectral_invert=0x%x;mode=0x%x\n",
+ if_freq, spectral_invert, mode);
+
+
+ if (mode == TUNER_MODE_FM_RADIO) {
+ pll_freq_word = 0x905A1CAC;
+ status = vid_blk_write_word(dev, DIF_PLL_FREQ_WORD, pll_freq_word);
+
+ } else /*KSPROPERTY_TUNER_MODE_TV*/{
+ /* Calculate the PLL frequency word based on the adjusted if_freq*/
+ pll_freq_word = if_freq;
+ pll_freq_u64 = (u64)pll_freq_word << 28L;
+ do_div(pll_freq_u64, 50000000);
+ pll_freq_word = (u32)pll_freq_u64;
+ /*pll_freq_word = 0x3463497;*/
+ status = vid_blk_write_word(dev, DIF_PLL_FREQ_WORD, pll_freq_word);
+
+ if (spectral_invert) {
+ if_freq -= 400000;
+ /* Enable Spectral Invert*/
+ status = vid_blk_read_word(dev, DIF_MISC_CTRL,
+ &dif_misc_ctrl_value);
+ dif_misc_ctrl_value = dif_misc_ctrl_value | 0x00200000;
+ status = vid_blk_write_word(dev, DIF_MISC_CTRL,
+ dif_misc_ctrl_value);
+ } else {
+ if_freq += 400000;
+ /* Disable Spectral Invert*/
+ status = vid_blk_read_word(dev, DIF_MISC_CTRL,
+ &dif_misc_ctrl_value);
+ dif_misc_ctrl_value = dif_misc_ctrl_value & 0xFFDFFFFF;
+ status = vid_blk_write_word(dev, DIF_MISC_CTRL,
+ dif_misc_ctrl_value);
+ }
+
+ if_freq = (if_freq/100000)*100000;
+
+ if (if_freq < 3000000)
+ if_freq = 3000000;
+
+ if (if_freq > 16000000)
+ if_freq = 16000000;
+ }
+
+ cx231xx_info("Enter IF=%zd\n",
+ sizeof(Dif_set_array)/sizeof(struct dif_settings));
+ for (i = 0; i < sizeof(Dif_set_array)/sizeof(struct dif_settings); i++) {
+ if (Dif_set_array[i].if_freq == if_freq) {
+ status = vid_blk_write_word(dev,
+ Dif_set_array[i].register_address, Dif_set_array[i].value);
+ }
+ }
+}
+
+/******************************************************************************
+ * D I F - B L O C K C O N T R O L functions *
+ ******************************************************************************/
+int cx231xx_dif_configure_C2HH_for_low_IF(struct cx231xx *dev, u32 mode,
+ u32 function_mode, u32 standard)
+{
+ int status = 0;
+
+
+ if (mode == V4L2_TUNER_RADIO) {
+ /* C2HH */
+ /* lo if big signal */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1);
+ /* FUNC_MODE = DIF */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 23, 24, function_mode);
+ /* IF_MODE */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xFF);
+ /* no inv */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1);
+ } else if (standard != DIF_USE_BASEBAND) {
+ if (standard & V4L2_STD_MN) {
+ /* lo if big signal */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1);
+ /* FUNC_MODE = DIF */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 23, 24,
+ function_mode);
+ /* IF_MODE */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xb);
+ /* no inv */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1);
+ /* 0x124, AUD_CHAN1_SRC = 0x3 */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AUD_IO_CTRL, 0, 31, 0x00000003);
+ } else if ((standard == V4L2_STD_PAL_I) |
+ (standard & V4L2_STD_PAL_D) |
+ (standard & V4L2_STD_SECAM)) {
+ /* C2HH setup */
+ /* lo if big signal */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1);
+ /* FUNC_MODE = DIF */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 23, 24,
+ function_mode);
+ /* IF_MODE */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xF);
+ /* no inv */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1);
+ } else {
+ /* default PAL BG */
+ /* C2HH setup */
+ /* lo if big signal */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1);
+ /* FUNC_MODE = DIF */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 23, 24,
+ function_mode);
+ /* IF_MODE */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xE);
+ /* no inv */
+ status = cx231xx_reg_mask_write(dev,
+ VID_BLK_I2C_ADDRESS, 32,
+ AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1);
+ }
+ }
+
+ return status;
+}
+
+int cx231xx_dif_set_standard(struct cx231xx *dev, u32 standard)
+{
+ int status = 0;
+ u32 dif_misc_ctrl_value = 0;
+ u32 func_mode = 0;
+
+ cx231xx_info("%s: setStandard to %x\n", __func__, standard);
+
+ status = vid_blk_read_word(dev, DIF_MISC_CTRL, &dif_misc_ctrl_value);
+ if (standard != DIF_USE_BASEBAND)
+ dev->norm = standard;
+
+ switch (dev->model) {
+ case CX231XX_BOARD_CNXT_CARRAERA:
+ case CX231XX_BOARD_CNXT_RDE_250:
+ case CX231XX_BOARD_CNXT_SHELBY:
+ case CX231XX_BOARD_CNXT_RDU_250:
+ case CX231XX_BOARD_CNXT_VIDEO_GRABBER:
+ case CX231XX_BOARD_HAUPPAUGE_EXETER:
+ func_mode = 0x03;
+ break;
+ case CX231XX_BOARD_CNXT_RDE_253S:
+ case CX231XX_BOARD_CNXT_RDU_253S:
+ case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL:
+ case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC:
+ func_mode = 0x01;
+ break;
+ default:
+ func_mode = 0x01;
+ }
+
+ status = cx231xx_dif_configure_C2HH_for_low_IF(dev, dev->active_mode,
+ func_mode, standard);
+
+ if (standard == DIF_USE_BASEBAND) { /* base band */
+ /* There is a different SRC_PHASE_INC value
+ for baseband vs. DIF */
+ status = vid_blk_write_word(dev, DIF_SRC_PHASE_INC, 0xDF7DF83);
+ status = vid_blk_read_word(dev, DIF_MISC_CTRL,
+ &dif_misc_ctrl_value);
+ dif_misc_ctrl_value |= FLD_DIF_DIF_BYPASS;
+ status = vid_blk_write_word(dev, DIF_MISC_CTRL,
+ dif_misc_ctrl_value);
+ } else if (standard & V4L2_STD_PAL_D) {
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_PLL_CTRL, 0, 31, 0x6503bc0c);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_PLL_CTRL1, 0, 31, 0xbd038c85);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_PLL_CTRL2, 0, 31, 0x1db4640a);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_PLL_CTRL3, 0, 31, 0x00008800);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_IF_REF, 0, 31, 0x444C1380);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_CTRL_IF, 0, 31, 0xDA302600);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_CTRL_INT, 0, 31, 0xDA261700);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_CTRL_RF, 0, 31, 0xDA262600);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_IF_INT_CURRENT, 0, 31,
+ 0x26001700);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_RF_CURRENT, 0, 31,
+ 0x00002660);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_VIDEO_AGC_CTRL, 0, 31,
+ 0x72500800);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_VID_AUD_OVERRIDE, 0, 31,
+ 0x27000100);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AV_SEP_CTRL, 0, 31, 0x3F3934EA);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_COMP_FLT_CTRL, 0, 31,
+ 0x00000000);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_SRC_PHASE_INC, 0, 31,
+ 0x1befbf06);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_SRC_GAIN_CONTROL, 0, 31,
+ 0x000035e8);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_RPT_VARIANCE, 0, 31, 0x00000000);
+ /* Save the Spec Inversion value */
+ dif_misc_ctrl_value &= FLD_DIF_SPEC_INV;
+ dif_misc_ctrl_value |= 0x3a023F11;
+ } else if (standard & V4L2_STD_PAL_I) {
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_PLL_CTRL, 0, 31, 0x6503bc0c);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_PLL_CTRL1, 0, 31, 0xbd038c85);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_PLL_CTRL2, 0, 31, 0x1db4640a);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_PLL_CTRL3, 0, 31, 0x00008800);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_IF_REF, 0, 31, 0x444C1380);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_CTRL_IF, 0, 31, 0xDA302600);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_CTRL_INT, 0, 31, 0xDA261700);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_CTRL_RF, 0, 31, 0xDA262600);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_IF_INT_CURRENT, 0, 31,
+ 0x26001700);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_RF_CURRENT, 0, 31,
+ 0x00002660);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_VIDEO_AGC_CTRL, 0, 31,
+ 0x72500800);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_VID_AUD_OVERRIDE, 0, 31,
+ 0x27000100);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AV_SEP_CTRL, 0, 31, 0x5F39A934);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_COMP_FLT_CTRL, 0, 31,
+ 0x00000000);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_SRC_PHASE_INC, 0, 31,
+ 0x1befbf06);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_SRC_GAIN_CONTROL, 0, 31,
+ 0x000035e8);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_RPT_VARIANCE, 0, 31, 0x00000000);
+ /* Save the Spec Inversion value */
+ dif_misc_ctrl_value &= FLD_DIF_SPEC_INV;
+ dif_misc_ctrl_value |= 0x3a033F11;
+ } else if (standard & V4L2_STD_PAL_M) {
+ /* improved Low Frequency Phase Noise */
+ status = vid_blk_write_word(dev, DIF_PLL_CTRL, 0xFF01FF0C);
+ status = vid_blk_write_word(dev, DIF_PLL_CTRL1, 0xbd038c85);
+ status = vid_blk_write_word(dev, DIF_PLL_CTRL2, 0x1db4640a);
+ status = vid_blk_write_word(dev, DIF_PLL_CTRL3, 0x00008800);
+ status = vid_blk_write_word(dev, DIF_AGC_IF_REF, 0x444C1380);
+ status = vid_blk_write_word(dev, DIF_AGC_IF_INT_CURRENT,
+ 0x26001700);
+ status = vid_blk_write_word(dev, DIF_AGC_RF_CURRENT,
+ 0x00002660);
+ status = vid_blk_write_word(dev, DIF_VIDEO_AGC_CTRL,
+ 0x72500800);
+ status = vid_blk_write_word(dev, DIF_VID_AUD_OVERRIDE,
+ 0x27000100);
+ status = vid_blk_write_word(dev, DIF_AV_SEP_CTRL, 0x012c405d);
+ status = vid_blk_write_word(dev, DIF_COMP_FLT_CTRL,
+ 0x009f50c1);
+ status = vid_blk_write_word(dev, DIF_SRC_PHASE_INC,
+ 0x1befbf06);
+ status = vid_blk_write_word(dev, DIF_SRC_GAIN_CONTROL,
+ 0x000035e8);
+ status = vid_blk_write_word(dev, DIF_SOFT_RST_CTRL_REVB,
+ 0x00000000);
+ /* Save the Spec Inversion value */
+ dif_misc_ctrl_value &= FLD_DIF_SPEC_INV;
+ dif_misc_ctrl_value |= 0x3A0A3F10;
+ } else if (standard & (V4L2_STD_PAL_N | V4L2_STD_PAL_Nc)) {
+ /* improved Low Frequency Phase Noise */
+ status = vid_blk_write_word(dev, DIF_PLL_CTRL, 0xFF01FF0C);
+ status = vid_blk_write_word(dev, DIF_PLL_CTRL1, 0xbd038c85);
+ status = vid_blk_write_word(dev, DIF_PLL_CTRL2, 0x1db4640a);
+ status = vid_blk_write_word(dev, DIF_PLL_CTRL3, 0x00008800);
+ status = vid_blk_write_word(dev, DIF_AGC_IF_REF, 0x444C1380);
+ status = vid_blk_write_word(dev, DIF_AGC_IF_INT_CURRENT,
+ 0x26001700);
+ status = vid_blk_write_word(dev, DIF_AGC_RF_CURRENT,
+ 0x00002660);
+ status = vid_blk_write_word(dev, DIF_VIDEO_AGC_CTRL,
+ 0x72500800);
+ status = vid_blk_write_word(dev, DIF_VID_AUD_OVERRIDE,
+ 0x27000100);
+ status = vid_blk_write_word(dev, DIF_AV_SEP_CTRL,
+ 0x012c405d);
+ status = vid_blk_write_word(dev, DIF_COMP_FLT_CTRL,
+ 0x009f50c1);
+ status = vid_blk_write_word(dev, DIF_SRC_PHASE_INC,
+ 0x1befbf06);
+ status = vid_blk_write_word(dev, DIF_SRC_GAIN_CONTROL,
+ 0x000035e8);
+ status = vid_blk_write_word(dev, DIF_SOFT_RST_CTRL_REVB,
+ 0x00000000);
+ /* Save the Spec Inversion value */
+ dif_misc_ctrl_value &= FLD_DIF_SPEC_INV;
+ dif_misc_ctrl_value = 0x3A093F10;
+ } else if (standard &
+ (V4L2_STD_SECAM_B | V4L2_STD_SECAM_D | V4L2_STD_SECAM_G |
+ V4L2_STD_SECAM_K | V4L2_STD_SECAM_K1)) {
+
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_PLL_CTRL, 0, 31, 0x6503bc0c);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_PLL_CTRL1, 0, 31, 0xbd038c85);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_PLL_CTRL2, 0, 31, 0x1db4640a);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_PLL_CTRL3, 0, 31, 0x00008800);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_IF_REF, 0, 31, 0x888C0380);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_CTRL_IF, 0, 31, 0xe0262600);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_CTRL_INT, 0, 31, 0xc2171700);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_CTRL_RF, 0, 31, 0xc2262600);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_IF_INT_CURRENT, 0, 31,
+ 0x26001700);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_RF_CURRENT, 0, 31,
+ 0x00002660);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_VID_AUD_OVERRIDE, 0, 31,
+ 0x27000100);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AV_SEP_CTRL, 0, 31, 0x3F3530ec);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_COMP_FLT_CTRL, 0, 31,
+ 0x00000000);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_SRC_PHASE_INC, 0, 31,
+ 0x1befbf06);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_SRC_GAIN_CONTROL, 0, 31,
+ 0x000035e8);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_RPT_VARIANCE, 0, 31, 0x00000000);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_VIDEO_AGC_CTRL, 0, 31,
+ 0xf4000000);
+
+ /* Save the Spec Inversion value */
+ dif_misc_ctrl_value &= FLD_DIF_SPEC_INV;
+ dif_misc_ctrl_value |= 0x3a023F11;
+ } else if (standard & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC)) {
+ /* Is it SECAM_L1? */
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_PLL_CTRL, 0, 31, 0x6503bc0c);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_PLL_CTRL1, 0, 31, 0xbd038c85);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_PLL_CTRL2, 0, 31, 0x1db4640a);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_PLL_CTRL3, 0, 31, 0x00008800);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_IF_REF, 0, 31, 0x888C0380);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_CTRL_IF, 0, 31, 0xe0262600);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_CTRL_INT, 0, 31, 0xc2171700);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_CTRL_RF, 0, 31, 0xc2262600);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_IF_INT_CURRENT, 0, 31,
+ 0x26001700);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_RF_CURRENT, 0, 31,
+ 0x00002660);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_VID_AUD_OVERRIDE, 0, 31,
+ 0x27000100);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AV_SEP_CTRL, 0, 31, 0x3F3530ec);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_COMP_FLT_CTRL, 0, 31,
+ 0x00000000);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_SRC_PHASE_INC, 0, 31,
+ 0x1befbf06);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_SRC_GAIN_CONTROL, 0, 31,
+ 0x000035e8);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_RPT_VARIANCE, 0, 31, 0x00000000);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_VIDEO_AGC_CTRL, 0, 31,
+ 0xf2560000);
+
+ /* Save the Spec Inversion value */
+ dif_misc_ctrl_value &= FLD_DIF_SPEC_INV;
+ dif_misc_ctrl_value |= 0x3a023F11;
+
+ } else if (standard & V4L2_STD_NTSC_M) {
+ /* V4L2_STD_NTSC_M (75 IRE Setup) Or
+ V4L2_STD_NTSC_M_JP (Japan, 0 IRE Setup) */
+
+ /* For NTSC the centre frequency of video coming out of
+ sidewinder is around 7.1MHz or 3.6MHz depending on the
+ spectral inversion. so for a non spectrally inverted channel
+ the pll freq word is 0x03420c49
+ */
+
+ status = vid_blk_write_word(dev, DIF_PLL_CTRL, 0x6503BC0C);
+ status = vid_blk_write_word(dev, DIF_PLL_CTRL1, 0xBD038C85);
+ status = vid_blk_write_word(dev, DIF_PLL_CTRL2, 0x1DB4640A);
+ status = vid_blk_write_word(dev, DIF_PLL_CTRL3, 0x00008800);
+ status = vid_blk_write_word(dev, DIF_AGC_IF_REF, 0x444C0380);
+ status = vid_blk_write_word(dev, DIF_AGC_IF_INT_CURRENT,
+ 0x26001700);
+ status = vid_blk_write_word(dev, DIF_AGC_RF_CURRENT,
+ 0x00002660);
+ status = vid_blk_write_word(dev, DIF_VIDEO_AGC_CTRL,
+ 0x04000800);
+ status = vid_blk_write_word(dev, DIF_VID_AUD_OVERRIDE,
+ 0x27000100);
+ status = vid_blk_write_word(dev, DIF_AV_SEP_CTRL, 0x01296e1f);
+
+ status = vid_blk_write_word(dev, DIF_COMP_FLT_CTRL,
+ 0x009f50c1);
+ status = vid_blk_write_word(dev, DIF_SRC_PHASE_INC,
+ 0x1befbf06);
+ status = vid_blk_write_word(dev, DIF_SRC_GAIN_CONTROL,
+ 0x000035e8);
+
+ status = vid_blk_write_word(dev, DIF_AGC_CTRL_IF, 0xC2262600);
+ status = vid_blk_write_word(dev, DIF_AGC_CTRL_INT,
+ 0xC2262600);
+ status = vid_blk_write_word(dev, DIF_AGC_CTRL_RF, 0xC2262600);
+
+ /* Save the Spec Inversion value */
+ dif_misc_ctrl_value &= FLD_DIF_SPEC_INV;
+ dif_misc_ctrl_value |= 0x3a003F10;
+ } else {
+ /* default PAL BG */
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_PLL_CTRL, 0, 31, 0x6503bc0c);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_PLL_CTRL1, 0, 31, 0xbd038c85);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_PLL_CTRL2, 0, 31, 0x1db4640a);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_PLL_CTRL3, 0, 31, 0x00008800);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_IF_REF, 0, 31, 0x444C1380);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_CTRL_IF, 0, 31, 0xDA302600);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_CTRL_INT, 0, 31, 0xDA261700);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_CTRL_RF, 0, 31, 0xDA262600);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_IF_INT_CURRENT, 0, 31,
+ 0x26001700);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AGC_RF_CURRENT, 0, 31,
+ 0x00002660);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_VIDEO_AGC_CTRL, 0, 31,
+ 0x72500800);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_VID_AUD_OVERRIDE, 0, 31,
+ 0x27000100);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_AV_SEP_CTRL, 0, 31, 0x3F3530EC);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_COMP_FLT_CTRL, 0, 31,
+ 0x00A653A8);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_SRC_PHASE_INC, 0, 31,
+ 0x1befbf06);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_SRC_GAIN_CONTROL, 0, 31,
+ 0x000035e8);
+ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
+ DIF_RPT_VARIANCE, 0, 31, 0x00000000);
+ /* Save the Spec Inversion value */
+ dif_misc_ctrl_value &= FLD_DIF_SPEC_INV;
+ dif_misc_ctrl_value |= 0x3a013F11;
+ }
+
+ /* The AGC values should be the same for all standards,
+ AUD_SRC_SEL[19] should always be disabled */
+ dif_misc_ctrl_value &= ~FLD_DIF_AUD_SRC_SEL;
+
+ /* It is still possible to get Set Standard calls even when we
+ are in FM mode.
+ This is done to override the value for FM. */
+ if (dev->active_mode == V4L2_TUNER_RADIO)
+ dif_misc_ctrl_value = 0x7a080000;
+
+ /* Write the calculated value for misc ontrol register */
+ status = vid_blk_write_word(dev, DIF_MISC_CTRL, dif_misc_ctrl_value);
+
+ return status;
+}
+
+int cx231xx_tuner_pre_channel_change(struct cx231xx *dev)
+{
+ int status = 0;
+ u32 dwval;
+
+ /* Set the RF and IF k_agc values to 3 */
+ status = vid_blk_read_word(dev, DIF_AGC_IF_REF, &dwval);
+ dwval &= ~(FLD_DIF_K_AGC_RF | FLD_DIF_K_AGC_IF);
+ dwval |= 0x33000000;
+
+ status = vid_blk_write_word(dev, DIF_AGC_IF_REF, dwval);
+
+ return status;
+}
+
+int cx231xx_tuner_post_channel_change(struct cx231xx *dev)
+{
+ int status = 0;
+ u32 dwval;
+ cx231xx_info("cx231xx_tuner_post_channel_change dev->tuner_type =0%d\n",
+ dev->tuner_type);
+ /* Set the RF and IF k_agc values to 4 for PAL/NTSC and 8 for
+ * SECAM L/B/D standards */
+ status = vid_blk_read_word(dev, DIF_AGC_IF_REF, &dwval);
+ dwval &= ~(FLD_DIF_K_AGC_RF | FLD_DIF_K_AGC_IF);
+
+ if (dev->norm & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_B |
+ V4L2_STD_SECAM_D)) {
+ if (dev->tuner_type == TUNER_NXP_TDA18271) {
+ dwval &= ~FLD_DIF_IF_REF;
+ dwval |= 0x88000300;
+ } else
+ dwval |= 0x88000000;
+ } else {
+ if (dev->tuner_type == TUNER_NXP_TDA18271) {
+ dwval &= ~FLD_DIF_IF_REF;
+ dwval |= 0xCC000300;
+ } else
+ dwval |= 0x44000000;
+ }
+
+ status = vid_blk_write_word(dev, DIF_AGC_IF_REF, dwval);
+
+ return status;
+}
+
+/******************************************************************************
+ * I 2 S - B L O C K C O N T R O L functions *
+ ******************************************************************************/
+int cx231xx_i2s_blk_initialize(struct cx231xx *dev)
+{
+ int status = 0;
+ u32 value;
+
+ status = cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
+ CH_PWR_CTRL1, 1, &value, 1);
+ /* enables clock to delta-sigma and decimation filter */
+ value |= 0x80;
+ status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
+ CH_PWR_CTRL1, 1, value, 1);
+ /* power up all channel */
+ status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
+ CH_PWR_CTRL2, 1, 0x00, 1);
+
+ return status;
+}
+
+int cx231xx_i2s_blk_update_power_control(struct cx231xx *dev,
+ enum AV_MODE avmode)
+{
+ int status = 0;
+ u32 value = 0;
+
+ if (avmode != POLARIS_AVMODE_ENXTERNAL_AV) {
+ status = cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
+ CH_PWR_CTRL2, 1, &value, 1);
+ value |= 0xfe;
+ status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
+ CH_PWR_CTRL2, 1, value, 1);
+ } else {
+ status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
+ CH_PWR_CTRL2, 1, 0x00, 1);
+ }
+
+ return status;
+}
+
+/* set i2s_blk for audio input types */
+int cx231xx_i2s_blk_set_audio_input(struct cx231xx *dev, u8 audio_input)
+{
+ int status = 0;
+
+ switch (audio_input) {
+ case CX231XX_AMUX_LINE_IN:
+ status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
+ CH_PWR_CTRL2, 1, 0x00, 1);
+ status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
+ CH_PWR_CTRL1, 1, 0x80, 1);
+ break;
+ case CX231XX_AMUX_VIDEO:
+ default:
+ break;
+ }
+
+ dev->ctl_ainput = audio_input;
+
+ return status;
+}
+
+/******************************************************************************
+ * P O W E R C O N T R O L functions *
+ ******************************************************************************/
+int cx231xx_set_power_mode(struct cx231xx *dev, enum AV_MODE mode)
+{
+ u8 value[4] = { 0, 0, 0, 0 };
+ u32 tmp = 0;
+ int status = 0;
+
+ if (dev->power_mode != mode)
+ dev->power_mode = mode;
+ else {
+ cx231xx_info(" setPowerMode::mode = %d, No Change req.\n",
+ mode);
+ return 0;
+ }
+
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, value,
+ 4);
+ if (status < 0)
+ return status;
+
+ tmp = *((u32 *) value);
+
+ switch (mode) {
+ case POLARIS_AVMODE_ENXTERNAL_AV:
+
+ tmp &= (~PWR_MODE_MASK);
+
+ tmp |= PWR_AV_EN;
+ value[0] = (u8) tmp;
+ value[1] = (u8) (tmp >> 8);
+ value[2] = (u8) (tmp >> 16);
+ value[3] = (u8) (tmp >> 24);
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ PWR_CTL_EN, value, 4);
+ msleep(PWR_SLEEP_INTERVAL);
+
+ tmp |= PWR_ISO_EN;
+ value[0] = (u8) tmp;
+ value[1] = (u8) (tmp >> 8);
+ value[2] = (u8) (tmp >> 16);
+ value[3] = (u8) (tmp >> 24);
+ status =
+ cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, PWR_CTL_EN,
+ value, 4);
+ msleep(PWR_SLEEP_INTERVAL);
+
+ tmp |= POLARIS_AVMODE_ENXTERNAL_AV;
+ value[0] = (u8) tmp;
+ value[1] = (u8) (tmp >> 8);
+ value[2] = (u8) (tmp >> 16);
+ value[3] = (u8) (tmp >> 24);
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ PWR_CTL_EN, value, 4);
+
+ /* reset state of xceive tuner */
+ dev->xc_fw_load_done = 0;
+ break;
+
+ case POLARIS_AVMODE_ANALOGT_TV:
+
+ tmp |= PWR_DEMOD_EN;
+ tmp |= (I2C_DEMOD_EN);
+ value[0] = (u8) tmp;
+ value[1] = (u8) (tmp >> 8);
+ value[2] = (u8) (tmp >> 16);
+ value[3] = (u8) (tmp >> 24);
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ PWR_CTL_EN, value, 4);
+ msleep(PWR_SLEEP_INTERVAL);
+
+ if (!(tmp & PWR_TUNER_EN)) {
+ tmp |= (PWR_TUNER_EN);
+ value[0] = (u8) tmp;
+ value[1] = (u8) (tmp >> 8);
+ value[2] = (u8) (tmp >> 16);
+ value[3] = (u8) (tmp >> 24);
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ PWR_CTL_EN, value, 4);
+ msleep(PWR_SLEEP_INTERVAL);
+ }
+
+ if (!(tmp & PWR_AV_EN)) {
+ tmp |= PWR_AV_EN;
+ value[0] = (u8) tmp;
+ value[1] = (u8) (tmp >> 8);
+ value[2] = (u8) (tmp >> 16);
+ value[3] = (u8) (tmp >> 24);
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ PWR_CTL_EN, value, 4);
+ msleep(PWR_SLEEP_INTERVAL);
+ }
+ if (!(tmp & PWR_ISO_EN)) {
+ tmp |= PWR_ISO_EN;
+ value[0] = (u8) tmp;
+ value[1] = (u8) (tmp >> 8);
+ value[2] = (u8) (tmp >> 16);
+ value[3] = (u8) (tmp >> 24);
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ PWR_CTL_EN, value, 4);
+ msleep(PWR_SLEEP_INTERVAL);
+ }
+
+ if (!(tmp & POLARIS_AVMODE_ANALOGT_TV)) {
+ tmp |= POLARIS_AVMODE_ANALOGT_TV;
+ value[0] = (u8) tmp;
+ value[1] = (u8) (tmp >> 8);
+ value[2] = (u8) (tmp >> 16);
+ value[3] = (u8) (tmp >> 24);
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ PWR_CTL_EN, value, 4);
+ msleep(PWR_SLEEP_INTERVAL);
+ }
+
+ if (dev->board.tuner_type != TUNER_ABSENT) {
+ /* Enable tuner */
+ cx231xx_enable_i2c_port_3(dev, true);
+
+ /* reset the Tuner */
+ if (dev->board.tuner_gpio)
+ cx231xx_gpio_set(dev, dev->board.tuner_gpio);
+
+ if (dev->cx231xx_reset_analog_tuner)
+ dev->cx231xx_reset_analog_tuner(dev);
+ }
+
+ break;
+
+ case POLARIS_AVMODE_DIGITAL:
+ if (!(tmp & PWR_TUNER_EN)) {
+ tmp |= (PWR_TUNER_EN);
+ value[0] = (u8) tmp;
+ value[1] = (u8) (tmp >> 8);
+ value[2] = (u8) (tmp >> 16);
+ value[3] = (u8) (tmp >> 24);
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ PWR_CTL_EN, value, 4);
+ msleep(PWR_SLEEP_INTERVAL);
+ }
+ if (!(tmp & PWR_AV_EN)) {
+ tmp |= PWR_AV_EN;
+ value[0] = (u8) tmp;
+ value[1] = (u8) (tmp >> 8);
+ value[2] = (u8) (tmp >> 16);
+ value[3] = (u8) (tmp >> 24);
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ PWR_CTL_EN, value, 4);
+ msleep(PWR_SLEEP_INTERVAL);
+ }
+ if (!(tmp & PWR_ISO_EN)) {
+ tmp |= PWR_ISO_EN;
+ value[0] = (u8) tmp;
+ value[1] = (u8) (tmp >> 8);
+ value[2] = (u8) (tmp >> 16);
+ value[3] = (u8) (tmp >> 24);
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ PWR_CTL_EN, value, 4);
+ msleep(PWR_SLEEP_INTERVAL);
+ }
+
+ tmp &= (~PWR_AV_MODE);
+ tmp |= POLARIS_AVMODE_DIGITAL | I2C_DEMOD_EN;
+ value[0] = (u8) tmp;
+ value[1] = (u8) (tmp >> 8);
+ value[2] = (u8) (tmp >> 16);
+ value[3] = (u8) (tmp >> 24);
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ PWR_CTL_EN, value, 4);
+ msleep(PWR_SLEEP_INTERVAL);
+
+ if (!(tmp & PWR_DEMOD_EN)) {
+ tmp |= PWR_DEMOD_EN;
+ value[0] = (u8) tmp;
+ value[1] = (u8) (tmp >> 8);
+ value[2] = (u8) (tmp >> 16);
+ value[3] = (u8) (tmp >> 24);
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ PWR_CTL_EN, value, 4);
+ msleep(PWR_SLEEP_INTERVAL);
+ }
+
+ if (dev->board.tuner_type != TUNER_ABSENT) {
+ /*
+ * Enable tuner
+ * Hauppauge Exeter seems to need to do something different!
+ */
+ if (dev->model == CX231XX_BOARD_HAUPPAUGE_EXETER)
+ cx231xx_enable_i2c_port_3(dev, false);
+ else
+ cx231xx_enable_i2c_port_3(dev, true);
+
+ /* reset the Tuner */
+ if (dev->board.tuner_gpio)
+ cx231xx_gpio_set(dev, dev->board.tuner_gpio);
+
+ if (dev->cx231xx_reset_analog_tuner)
+ dev->cx231xx_reset_analog_tuner(dev);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ msleep(PWR_SLEEP_INTERVAL);
+
+ /* For power saving, only enable Pwr_resetout_n
+ when digital TV is selected. */
+ if (mode == POLARIS_AVMODE_DIGITAL) {
+ tmp |= PWR_RESETOUT_EN;
+ value[0] = (u8) tmp;
+ value[1] = (u8) (tmp >> 8);
+ value[2] = (u8) (tmp >> 16);
+ value[3] = (u8) (tmp >> 24);
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ PWR_CTL_EN, value, 4);
+ msleep(PWR_SLEEP_INTERVAL);
+ }
+
+ /* update power control for afe */
+ status = cx231xx_afe_update_power_control(dev, mode);
+
+ /* update power control for i2s_blk */
+ status = cx231xx_i2s_blk_update_power_control(dev, mode);
+
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, value,
+ 4);
+
+ return status;
+}
+
+int cx231xx_power_suspend(struct cx231xx *dev)
+{
+ u8 value[4] = { 0, 0, 0, 0 };
+ u32 tmp = 0;
+ int status = 0;
+
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN,
+ value, 4);
+ if (status > 0)
+ return status;
+
+ tmp = *((u32 *) value);
+ tmp &= (~PWR_MODE_MASK);
+
+ value[0] = (u8) tmp;
+ value[1] = (u8) (tmp >> 8);
+ value[2] = (u8) (tmp >> 16);
+ value[3] = (u8) (tmp >> 24);
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, PWR_CTL_EN,
+ value, 4);
+
+ return status;
+}
+
+/******************************************************************************
+ * S T R E A M C O N T R O L functions *
+ ******************************************************************************/
+int cx231xx_start_stream(struct cx231xx *dev, u32 ep_mask)
+{
+ u8 value[4] = { 0x0, 0x0, 0x0, 0x0 };
+ u32 tmp = 0;
+ int status = 0;
+
+ cx231xx_info("cx231xx_start_stream():: ep_mask = %x\n", ep_mask);
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET,
+ value, 4);
+ if (status < 0)
+ return status;
+
+ tmp = *((u32 *) value);
+ tmp |= ep_mask;
+ value[0] = (u8) tmp;
+ value[1] = (u8) (tmp >> 8);
+ value[2] = (u8) (tmp >> 16);
+ value[3] = (u8) (tmp >> 24);
+
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, EP_MODE_SET,
+ value, 4);
+
+ return status;
+}
+
+int cx231xx_stop_stream(struct cx231xx *dev, u32 ep_mask)
+{
+ u8 value[4] = { 0x0, 0x0, 0x0, 0x0 };
+ u32 tmp = 0;
+ int status = 0;
+
+ cx231xx_info("cx231xx_stop_stream():: ep_mask = %x\n", ep_mask);
+ status =
+ cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET, value, 4);
+ if (status < 0)
+ return status;
+
+ tmp = *((u32 *) value);
+ tmp &= (~ep_mask);
+ value[0] = (u8) tmp;
+ value[1] = (u8) (tmp >> 8);
+ value[2] = (u8) (tmp >> 16);
+ value[3] = (u8) (tmp >> 24);
+
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, EP_MODE_SET,
+ value, 4);
+
+ return status;
+}
+
+int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type)
+{
+ int status = 0;
+ u32 value = 0;
+ u8 val[4] = { 0, 0, 0, 0 };
+
+ if (dev->udev->speed == USB_SPEED_HIGH) {
+ switch (media_type) {
+ case 81: /* audio */
+ cx231xx_info("%s: Audio enter HANC\n", __func__);
+ status =
+ cx231xx_mode_register(dev, TS_MODE_REG, 0x9300);
+ break;
+
+ case 2: /* vbi */
+ cx231xx_info("%s: set vanc registers\n", __func__);
+ status = cx231xx_mode_register(dev, TS_MODE_REG, 0x300);
+ break;
+
+ case 3: /* sliced cc */
+ cx231xx_info("%s: set hanc registers\n", __func__);
+ status =
+ cx231xx_mode_register(dev, TS_MODE_REG, 0x1300);
+ break;
+
+ case 0: /* video */
+ cx231xx_info("%s: set video registers\n", __func__);
+ status = cx231xx_mode_register(dev, TS_MODE_REG, 0x100);
+ break;
+
+ case 4: /* ts1 */
+ cx231xx_info("%s: set ts1 registers", __func__);
+
+ if (dev->board.has_417) {
+ cx231xx_info(" MPEG\n");
+ value &= 0xFFFFFFFC;
+ value |= 0x3;
+
+ status = cx231xx_mode_register(dev, TS_MODE_REG, value);
+
+ val[0] = 0x04;
+ val[1] = 0xA3;
+ val[2] = 0x3B;
+ val[3] = 0x00;
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ TS1_CFG_REG, val, 4);
+
+ val[0] = 0x00;
+ val[1] = 0x08;
+ val[2] = 0x00;
+ val[3] = 0x08;
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ TS1_LENGTH_REG, val, 4);
+
+ } else {
+ cx231xx_info(" BDA\n");
+ status = cx231xx_mode_register(dev, TS_MODE_REG, 0x101);
+ status = cx231xx_mode_register(dev, TS1_CFG_REG, 0x010);
+ }
+ break;
+
+ case 6: /* ts1 parallel mode */
+ cx231xx_info("%s: set ts1 parallel mode registers\n",
+ __func__);
+ status = cx231xx_mode_register(dev, TS_MODE_REG, 0x100);
+ status = cx231xx_mode_register(dev, TS1_CFG_REG, 0x400);
+ break;
+ }
+ } else {
+ status = cx231xx_mode_register(dev, TS_MODE_REG, 0x101);
+ }
+
+ return status;
+}
+
+int cx231xx_capture_start(struct cx231xx *dev, int start, u8 media_type)
+{
+ int rc = -1;
+ u32 ep_mask = -1;
+ struct pcb_config *pcb_config;
+
+ /* get EP for media type */
+ pcb_config = (struct pcb_config *)&dev->current_pcb_config;
+
+ if (pcb_config->config_num == 1) {
+ switch (media_type) {
+ case 0: /* Video */
+ ep_mask = ENABLE_EP4; /* ep4 [00:1000] */
+ break;
+ case 1: /* Audio */
+ ep_mask = ENABLE_EP3; /* ep3 [00:0100] */
+ break;
+ case 2: /* Vbi */
+ ep_mask = ENABLE_EP5; /* ep5 [01:0000] */
+ break;
+ case 3: /* Sliced_cc */
+ ep_mask = ENABLE_EP6; /* ep6 [10:0000] */
+ break;
+ case 4: /* ts1 */
+ case 6: /* ts1 parallel mode */
+ ep_mask = ENABLE_EP1; /* ep1 [00:0001] */
+ break;
+ case 5: /* ts2 */
+ ep_mask = ENABLE_EP2; /* ep2 [00:0010] */
+ break;
+ }
+
+ } else if (pcb_config->config_num > 1) {
+ switch (media_type) {
+ case 0: /* Video */
+ ep_mask = ENABLE_EP4; /* ep4 [00:1000] */
+ break;
+ case 1: /* Audio */
+ ep_mask = ENABLE_EP3; /* ep3 [00:0100] */
+ break;
+ case 2: /* Vbi */
+ ep_mask = ENABLE_EP5; /* ep5 [01:0000] */
+ break;
+ case 3: /* Sliced_cc */
+ ep_mask = ENABLE_EP6; /* ep6 [10:0000] */
+ break;
+ case 4: /* ts1 */
+ case 6: /* ts1 parallel mode */
+ ep_mask = ENABLE_EP1; /* ep1 [00:0001] */
+ break;
+ case 5: /* ts2 */
+ ep_mask = ENABLE_EP2; /* ep2 [00:0010] */
+ break;
+ }
+
+ }
+
+ if (start) {
+ rc = cx231xx_initialize_stream_xfer(dev, media_type);
+
+ if (rc < 0)
+ return rc;
+
+ /* enable video capture */
+ if (ep_mask > 0)
+ rc = cx231xx_start_stream(dev, ep_mask);
+ } else {
+ /* disable video capture */
+ if (ep_mask > 0)
+ rc = cx231xx_stop_stream(dev, ep_mask);
+ }
+
+ if (dev->mode == CX231XX_ANALOG_MODE)
+ ;/* do any in Analog mode */
+ else
+ ;/* do any in digital mode */
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(cx231xx_capture_start);
+
+/*****************************************************************************
+* G P I O B I T control functions *
+******************************************************************************/
+int cx231xx_set_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val)
+{
+ int status = 0;
+
+ status = cx231xx_send_gpio_cmd(dev, gpio_bit, gpio_val, 4, 0, 0);
+
+ return status;
+}
+
+int cx231xx_get_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val)
+{
+ int status = 0;
+
+ status = cx231xx_send_gpio_cmd(dev, gpio_bit, gpio_val, 4, 0, 1);
+
+ return status;
+}
+
+/*
+* cx231xx_set_gpio_direction
+* Sets the direction of the GPIO pin to input or output
+*
+* Parameters :
+* pin_number : The GPIO Pin number to program the direction for
+* from 0 to 31
+* pin_value : The Direction of the GPIO Pin under reference.
+* 0 = Input direction
+* 1 = Output direction
+*/
+int cx231xx_set_gpio_direction(struct cx231xx *dev,
+ int pin_number, int pin_value)
+{
+ int status = 0;
+ u32 value = 0;
+
+ /* Check for valid pin_number - if 32 , bail out */
+ if (pin_number >= 32)
+ return -EINVAL;
+
+ /* input */
+ if (pin_value == 0)
+ value = dev->gpio_dir & (~(1 << pin_number)); /* clear */
+ else
+ value = dev->gpio_dir | (1 << pin_number);
+
+ status = cx231xx_set_gpio_bit(dev, value, (u8 *) &dev->gpio_val);
+
+ /* cache the value for future */
+ dev->gpio_dir = value;
+
+ return status;
+}
+
+/*
+* cx231xx_set_gpio_value
+* Sets the value of the GPIO pin to Logic high or low. The Pin under
+* reference should ALREADY BE SET IN OUTPUT MODE !!!!!!!!!
+*
+* Parameters :
+* pin_number : The GPIO Pin number to program the direction for
+* pin_value : The value of the GPIO Pin under reference.
+* 0 = set it to 0
+* 1 = set it to 1
+*/
+int cx231xx_set_gpio_value(struct cx231xx *dev, int pin_number, int pin_value)
+{
+ int status = 0;
+ u32 value = 0;
+
+ /* Check for valid pin_number - if 0xFF , bail out */
+ if (pin_number >= 32)
+ return -EINVAL;
+
+ /* first do a sanity check - if the Pin is not output, make it output */
+ if ((dev->gpio_dir & (1 << pin_number)) == 0x00) {
+ /* It was in input mode */
+ value = dev->gpio_dir | (1 << pin_number);
+ dev->gpio_dir = value;
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir,
+ (u8 *) &dev->gpio_val);
+ value = 0;
+ }
+
+ if (pin_value == 0)
+ value = dev->gpio_val & (~(1 << pin_number));
+ else
+ value = dev->gpio_val | (1 << pin_number);
+
+ /* store the value */
+ dev->gpio_val = value;
+
+ /* toggle bit0 of GP_IO */
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
+
+ return status;
+}
+
+/*****************************************************************************
+* G P I O I2C related functions *
+******************************************************************************/
+int cx231xx_gpio_i2c_start(struct cx231xx *dev)
+{
+ int status = 0;
+
+ /* set SCL to output 1 ; set SDA to output 1 */
+ dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio;
+ dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio;
+ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio;
+ dev->gpio_val |= 1 << dev->board.tuner_sda_gpio;
+
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
+ if (status < 0)
+ return -EINVAL;
+
+ /* set SCL to output 1; set SDA to output 0 */
+ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio;
+ dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio);
+
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
+ if (status < 0)
+ return -EINVAL;
+
+ /* set SCL to output 0; set SDA to output 0 */
+ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
+ dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio);
+
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
+ if (status < 0)
+ return -EINVAL;
+
+ return status;
+}
+
+int cx231xx_gpio_i2c_end(struct cx231xx *dev)
+{
+ int status = 0;
+
+ /* set SCL to output 0; set SDA to output 0 */
+ dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio;
+ dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio;
+
+ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
+ dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio);
+
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
+ if (status < 0)
+ return -EINVAL;
+
+ /* set SCL to output 1; set SDA to output 0 */
+ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio;
+ dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio);
+
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
+ if (status < 0)
+ return -EINVAL;
+
+ /* set SCL to input ,release SCL cable control
+ set SDA to input ,release SDA cable control */
+ dev->gpio_dir &= ~(1 << dev->board.tuner_scl_gpio);
+ dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio);
+
+ status =
+ cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
+ if (status < 0)
+ return -EINVAL;
+
+ return status;
+}
+
+int cx231xx_gpio_i2c_write_byte(struct cx231xx *dev, u8 data)
+{
+ int status = 0;
+ u8 i;
+
+ /* set SCL to output ; set SDA to output */
+ dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio;
+ dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio;
+
+ for (i = 0; i < 8; i++) {
+ if (((data << i) & 0x80) == 0) {
+ /* set SCL to output 0; set SDA to output 0 */
+ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
+ dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio);
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir,
+ (u8 *)&dev->gpio_val);
+
+ /* set SCL to output 1; set SDA to output 0 */
+ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio;
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir,
+ (u8 *)&dev->gpio_val);
+
+ /* set SCL to output 0; set SDA to output 0 */
+ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir,
+ (u8 *)&dev->gpio_val);
+ } else {
+ /* set SCL to output 0; set SDA to output 1 */
+ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
+ dev->gpio_val |= 1 << dev->board.tuner_sda_gpio;
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir,
+ (u8 *)&dev->gpio_val);
+
+ /* set SCL to output 1; set SDA to output 1 */
+ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio;
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir,
+ (u8 *)&dev->gpio_val);
+
+ /* set SCL to output 0; set SDA to output 1 */
+ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir,
+ (u8 *)&dev->gpio_val);
+ }
+ }
+ return status;
+}
+
+int cx231xx_gpio_i2c_read_byte(struct cx231xx *dev, u8 *buf)
+{
+ u8 value = 0;
+ int status = 0;
+ u32 gpio_logic_value = 0;
+ u8 i;
+
+ /* read byte */
+ for (i = 0; i < 8; i++) { /* send write I2c addr */
+
+ /* set SCL to output 0; set SDA to input */
+ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir,
+ (u8 *)&dev->gpio_val);
+
+ /* set SCL to output 1; set SDA to input */
+ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio;
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir,
+ (u8 *)&dev->gpio_val);
+
+ /* get SDA data bit */
+ gpio_logic_value = dev->gpio_val;
+ status = cx231xx_get_gpio_bit(dev, dev->gpio_dir,
+ (u8 *)&dev->gpio_val);
+ if ((dev->gpio_val & (1 << dev->board.tuner_sda_gpio)) != 0)
+ value |= (1 << (8 - i - 1));
+
+ dev->gpio_val = gpio_logic_value;
+ }
+
+ /* set SCL to output 0,finish the read latest SCL signal.
+ !!!set SDA to input, never to modify SDA direction at
+ the same times */
+ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
+
+ /* store the value */
+ *buf = value & 0xff;
+
+ return status;
+}
+
+int cx231xx_gpio_i2c_read_ack(struct cx231xx *dev)
+{
+ int status = 0;
+ u32 gpio_logic_value = 0;
+ int nCnt = 10;
+ int nInit = nCnt;
+
+ /* clock stretch; set SCL to input; set SDA to input;
+ get SCL value till SCL = 1 */
+ dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio);
+ dev->gpio_dir &= ~(1 << dev->board.tuner_scl_gpio);
+
+ gpio_logic_value = dev->gpio_val;
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
+
+ do {
+ msleep(2);
+ status = cx231xx_get_gpio_bit(dev, dev->gpio_dir,
+ (u8 *)&dev->gpio_val);
+ nCnt--;
+ } while (((dev->gpio_val &
+ (1 << dev->board.tuner_scl_gpio)) == 0) &&
+ (nCnt > 0));
+
+ if (nCnt == 0)
+ cx231xx_info("No ACK after %d msec -GPIO I2C failed!",
+ nInit * 10);
+
+ /*
+ * readAck
+ * through clock stretch, slave has given a SCL signal,
+ * so the SDA data can be directly read.
+ */
+ status = cx231xx_get_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
+
+ if ((dev->gpio_val & 1 << dev->board.tuner_sda_gpio) == 0) {
+ dev->gpio_val = gpio_logic_value;
+ dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio);
+ status = 0;
+ } else {
+ dev->gpio_val = gpio_logic_value;
+ dev->gpio_val |= (1 << dev->board.tuner_sda_gpio);
+ }
+
+ /* read SDA end, set the SCL to output 0, after this operation,
+ SDA direction can be changed. */
+ dev->gpio_val = gpio_logic_value;
+ dev->gpio_dir |= (1 << dev->board.tuner_scl_gpio);
+ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
+
+ return status;
+}
+
+int cx231xx_gpio_i2c_write_ack(struct cx231xx *dev)
+{
+ int status = 0;
+
+ /* set SDA to ouput */
+ dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio;
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
+
+ /* set SCL = 0 (output); set SDA = 0 (output) */
+ dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio);
+ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
+
+ /* set SCL = 1 (output); set SDA = 0 (output) */
+ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio;
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
+
+ /* set SCL = 0 (output); set SDA = 0 (output) */
+ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
+
+ /* set SDA to input,and then the slave will read data from SDA. */
+ dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio);
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
+
+ return status;
+}
+
+int cx231xx_gpio_i2c_write_nak(struct cx231xx *dev)
+{
+ int status = 0;
+
+ /* set scl to output ; set sda to input */
+ dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio;
+ dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio);
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
+
+ /* set scl to output 0; set sda to input */
+ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
+
+ /* set scl to output 1; set sda to input */
+ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio;
+ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
+
+ return status;
+}
+
+/*****************************************************************************
+* G P I O I2C related functions *
+******************************************************************************/
+/* cx231xx_gpio_i2c_read
+ * Function to read data from gpio based I2C interface
+ */
+int cx231xx_gpio_i2c_read(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len)
+{
+ int status = 0;
+ int i = 0;
+
+ /* get the lock */
+ mutex_lock(&dev->gpio_i2c_lock);
+
+ /* start */
+ status = cx231xx_gpio_i2c_start(dev);
+
+ /* write dev_addr */
+ status = cx231xx_gpio_i2c_write_byte(dev, (dev_addr << 1) + 1);
+
+ /* readAck */
+ status = cx231xx_gpio_i2c_read_ack(dev);
+
+ /* read data */
+ for (i = 0; i < len; i++) {
+ /* read data */
+ buf[i] = 0;
+ status = cx231xx_gpio_i2c_read_byte(dev, &buf[i]);
+
+ if ((i + 1) != len) {
+ /* only do write ack if we more length */
+ status = cx231xx_gpio_i2c_write_ack(dev);
+ }
+ }
+
+ /* write NAK - inform reads are complete */
+ status = cx231xx_gpio_i2c_write_nak(dev);
+
+ /* write end */
+ status = cx231xx_gpio_i2c_end(dev);
+
+ /* release the lock */
+ mutex_unlock(&dev->gpio_i2c_lock);
+
+ return status;
+}
+
+/* cx231xx_gpio_i2c_write
+ * Function to write data to gpio based I2C interface
+ */
+int cx231xx_gpio_i2c_write(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len)
+{
+ int status = 0;
+ int i = 0;
+
+ /* get the lock */
+ mutex_lock(&dev->gpio_i2c_lock);
+
+ /* start */
+ status = cx231xx_gpio_i2c_start(dev);
+
+ /* write dev_addr */
+ status = cx231xx_gpio_i2c_write_byte(dev, dev_addr << 1);
+
+ /* read Ack */
+ status = cx231xx_gpio_i2c_read_ack(dev);
+
+ for (i = 0; i < len; i++) {
+ /* Write data */
+ status = cx231xx_gpio_i2c_write_byte(dev, buf[i]);
+
+ /* read Ack */
+ status = cx231xx_gpio_i2c_read_ack(dev);
+ }
+
+ /* write End */
+ status = cx231xx_gpio_i2c_end(dev);
+
+ /* release the lock */
+ mutex_unlock(&dev->gpio_i2c_lock);
+
+ return 0;
+}
diff --git a/drivers/media/video/cx231xx/cx231xx-cards.c b/drivers/media/video/cx231xx/cx231xx-cards.c
new file mode 100644
index 00000000..8ed460d6
--- /dev/null
+++ b/drivers/media/video/cx231xx/cx231xx-cards.c
@@ -0,0 +1,1381 @@
+/*
+ cx231xx-cards.c - driver for Conexant Cx23100/101/102
+ USB video capture devices
+
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
+ Based on em28xx driver
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/usb.h>
+#include <media/tuner.h>
+#include <media/tveeprom.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
+
+#include <media/cx25840.h>
+#include "dvb-usb-ids.h"
+#include "xc5000.h"
+#include "tda18271.h"
+
+#include "cx231xx.h"
+
+static int tuner = -1;
+module_param(tuner, int, 0444);
+MODULE_PARM_DESC(tuner, "tuner type");
+
+static int transfer_mode = 1;
+module_param(transfer_mode, int, 0444);
+MODULE_PARM_DESC(transfer_mode, "transfer mode (1-ISO or 0-BULK)");
+
+static unsigned int disable_ir;
+module_param(disable_ir, int, 0444);
+MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
+
+/* Bitmask marking allocated devices from 0 to CX231XX_MAXBOARDS */
+static unsigned long cx231xx_devused;
+
+/*
+ * Reset sequences for analog/digital modes
+ */
+
+static struct cx231xx_reg_seq RDE250_XCV_TUNER[] = {
+ {0x03, 0x01, 10},
+ {0x03, 0x00, 30},
+ {0x03, 0x01, 10},
+ {-1, -1, -1},
+};
+
+/*
+ * Board definitions
+ */
+struct cx231xx_board cx231xx_boards[] = {
+ [CX231XX_BOARD_UNKNOWN] = {
+ .name = "Unknown CX231xx video grabber",
+ .tuner_type = TUNER_ABSENT,
+ .input = {{
+ .type = CX231XX_VMUX_TELEVISION,
+ .vmux = CX231XX_VIN_3_1,
+ .amux = CX231XX_AMUX_VIDEO,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_COMPOSITE1,
+ .vmux = CX231XX_VIN_2_1,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_SVIDEO,
+ .vmux = CX231XX_VIN_1_1 |
+ (CX231XX_VIN_1_2 << 8) |
+ CX25840_SVIDEO_ON,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ }
+ },
+ },
+ [CX231XX_BOARD_CNXT_CARRAERA] = {
+ .name = "Conexant Hybrid TV - CARRAERA",
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0x61,
+ .tuner_gpio = RDE250_XCV_TUNER,
+ .tuner_sif_gpio = 0x05,
+ .tuner_scl_gpio = 0x1a,
+ .tuner_sda_gpio = 0x1b,
+ .decoder = CX231XX_AVDECODER,
+ .output_mode = OUT_MODE_VIP11,
+ .demod_xfer_mode = 0,
+ .ctl_pin_status_mask = 0xFFFFFFC4,
+ .agc_analog_digital_select_gpio = 0x0c,
+ .gpio_pin_status_mask = 0x4001000,
+ .tuner_i2c_master = 1,
+ .demod_i2c_master = 2,
+ .has_dvb = 1,
+ .demod_addr = 0x02,
+ .norm = V4L2_STD_PAL,
+
+ .input = {{
+ .type = CX231XX_VMUX_TELEVISION,
+ .vmux = CX231XX_VIN_3_1,
+ .amux = CX231XX_AMUX_VIDEO,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_COMPOSITE1,
+ .vmux = CX231XX_VIN_2_1,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_SVIDEO,
+ .vmux = CX231XX_VIN_1_1 |
+ (CX231XX_VIN_1_2 << 8) |
+ CX25840_SVIDEO_ON,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ }
+ },
+ },
+ [CX231XX_BOARD_CNXT_SHELBY] = {
+ .name = "Conexant Hybrid TV - SHELBY",
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0x61,
+ .tuner_gpio = RDE250_XCV_TUNER,
+ .tuner_sif_gpio = 0x05,
+ .tuner_scl_gpio = 0x1a,
+ .tuner_sda_gpio = 0x1b,
+ .decoder = CX231XX_AVDECODER,
+ .output_mode = OUT_MODE_VIP11,
+ .demod_xfer_mode = 0,
+ .ctl_pin_status_mask = 0xFFFFFFC4,
+ .agc_analog_digital_select_gpio = 0x0c,
+ .gpio_pin_status_mask = 0x4001000,
+ .tuner_i2c_master = 1,
+ .demod_i2c_master = 2,
+ .has_dvb = 1,
+ .demod_addr = 0x32,
+ .norm = V4L2_STD_NTSC,
+
+ .input = {{
+ .type = CX231XX_VMUX_TELEVISION,
+ .vmux = CX231XX_VIN_3_1,
+ .amux = CX231XX_AMUX_VIDEO,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_COMPOSITE1,
+ .vmux = CX231XX_VIN_2_1,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_SVIDEO,
+ .vmux = CX231XX_VIN_1_1 |
+ (CX231XX_VIN_1_2 << 8) |
+ CX25840_SVIDEO_ON,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ }
+ },
+ },
+ [CX231XX_BOARD_CNXT_RDE_253S] = {
+ .name = "Conexant Hybrid TV - RDE253S",
+ .tuner_type = TUNER_NXP_TDA18271,
+ .tuner_addr = 0x60,
+ .tuner_gpio = RDE250_XCV_TUNER,
+ .tuner_sif_gpio = 0x05,
+ .tuner_scl_gpio = 0x1a,
+ .tuner_sda_gpio = 0x1b,
+ .decoder = CX231XX_AVDECODER,
+ .output_mode = OUT_MODE_VIP11,
+ .demod_xfer_mode = 0,
+ .ctl_pin_status_mask = 0xFFFFFFC4,
+ .agc_analog_digital_select_gpio = 0x1c,
+ .gpio_pin_status_mask = 0x4001000,
+ .tuner_i2c_master = 1,
+ .demod_i2c_master = 2,
+ .has_dvb = 1,
+ .demod_addr = 0x02,
+ .norm = V4L2_STD_PAL,
+
+ .input = {{
+ .type = CX231XX_VMUX_TELEVISION,
+ .vmux = CX231XX_VIN_3_1,
+ .amux = CX231XX_AMUX_VIDEO,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_COMPOSITE1,
+ .vmux = CX231XX_VIN_2_1,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_SVIDEO,
+ .vmux = CX231XX_VIN_1_1 |
+ (CX231XX_VIN_1_2 << 8) |
+ CX25840_SVIDEO_ON,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ }
+ },
+ },
+
+ [CX231XX_BOARD_CNXT_RDU_253S] = {
+ .name = "Conexant Hybrid TV - RDU253S",
+ .tuner_type = TUNER_NXP_TDA18271,
+ .tuner_addr = 0x60,
+ .tuner_gpio = RDE250_XCV_TUNER,
+ .tuner_sif_gpio = 0x05,
+ .tuner_scl_gpio = 0x1a,
+ .tuner_sda_gpio = 0x1b,
+ .decoder = CX231XX_AVDECODER,
+ .output_mode = OUT_MODE_VIP11,
+ .demod_xfer_mode = 0,
+ .ctl_pin_status_mask = 0xFFFFFFC4,
+ .agc_analog_digital_select_gpio = 0x1c,
+ .gpio_pin_status_mask = 0x4001000,
+ .tuner_i2c_master = 1,
+ .demod_i2c_master = 2,
+ .has_dvb = 1,
+ .demod_addr = 0x02,
+ .norm = V4L2_STD_PAL,
+
+ .input = {{
+ .type = CX231XX_VMUX_TELEVISION,
+ .vmux = CX231XX_VIN_3_1,
+ .amux = CX231XX_AMUX_VIDEO,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_COMPOSITE1,
+ .vmux = CX231XX_VIN_2_1,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_SVIDEO,
+ .vmux = CX231XX_VIN_1_1 |
+ (CX231XX_VIN_1_2 << 8) |
+ CX25840_SVIDEO_ON,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ }
+ },
+ },
+ [CX231XX_BOARD_CNXT_VIDEO_GRABBER] = {
+ .name = "Conexant VIDEO GRABBER",
+ .tuner_type = TUNER_ABSENT,
+ .decoder = CX231XX_AVDECODER,
+ .output_mode = OUT_MODE_VIP11,
+ .ctl_pin_status_mask = 0xFFFFFFC4,
+ .agc_analog_digital_select_gpio = 0x1c,
+ .gpio_pin_status_mask = 0x4001000,
+ .norm = V4L2_STD_PAL,
+ .no_alt_vanc = 1,
+ .external_av = 1,
+ .has_417 = 1,
+
+ .input = {{
+ .type = CX231XX_VMUX_COMPOSITE1,
+ .vmux = CX231XX_VIN_2_1,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_SVIDEO,
+ .vmux = CX231XX_VIN_1_1 |
+ (CX231XX_VIN_1_2 << 8) |
+ CX25840_SVIDEO_ON,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ }
+ },
+ },
+ [CX231XX_BOARD_CNXT_RDE_250] = {
+ .name = "Conexant Hybrid TV - rde 250",
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0x61,
+ .tuner_gpio = RDE250_XCV_TUNER,
+ .tuner_sif_gpio = 0x05,
+ .tuner_scl_gpio = 0x1a,
+ .tuner_sda_gpio = 0x1b,
+ .decoder = CX231XX_AVDECODER,
+ .output_mode = OUT_MODE_VIP11,
+ .demod_xfer_mode = 0,
+ .ctl_pin_status_mask = 0xFFFFFFC4,
+ .agc_analog_digital_select_gpio = 0x0c,
+ .gpio_pin_status_mask = 0x4001000,
+ .tuner_i2c_master = 1,
+ .demod_i2c_master = 2,
+ .has_dvb = 1,
+ .demod_addr = 0x02,
+ .norm = V4L2_STD_PAL,
+
+ .input = {{
+ .type = CX231XX_VMUX_TELEVISION,
+ .vmux = CX231XX_VIN_2_1,
+ .amux = CX231XX_AMUX_VIDEO,
+ .gpio = NULL,
+ }
+ },
+ },
+ [CX231XX_BOARD_CNXT_RDU_250] = {
+ .name = "Conexant Hybrid TV - RDU 250",
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0x61,
+ .tuner_gpio = RDE250_XCV_TUNER,
+ .tuner_sif_gpio = 0x05,
+ .tuner_scl_gpio = 0x1a,
+ .tuner_sda_gpio = 0x1b,
+ .decoder = CX231XX_AVDECODER,
+ .output_mode = OUT_MODE_VIP11,
+ .demod_xfer_mode = 0,
+ .ctl_pin_status_mask = 0xFFFFFFC4,
+ .agc_analog_digital_select_gpio = 0x0c,
+ .gpio_pin_status_mask = 0x4001000,
+ .tuner_i2c_master = 1,
+ .demod_i2c_master = 2,
+ .has_dvb = 1,
+ .demod_addr = 0x32,
+ .norm = V4L2_STD_NTSC,
+
+ .input = {{
+ .type = CX231XX_VMUX_TELEVISION,
+ .vmux = CX231XX_VIN_2_1,
+ .amux = CX231XX_AMUX_VIDEO,
+ .gpio = NULL,
+ }
+ },
+ },
+ [CX231XX_BOARD_HAUPPAUGE_EXETER] = {
+ .name = "Hauppauge EXETER",
+ .tuner_type = TUNER_NXP_TDA18271,
+ .tuner_addr = 0x60,
+ .tuner_gpio = RDE250_XCV_TUNER,
+ .tuner_sif_gpio = 0x05,
+ .tuner_scl_gpio = 0x1a,
+ .tuner_sda_gpio = 0x1b,
+ .decoder = CX231XX_AVDECODER,
+ .output_mode = OUT_MODE_VIP11,
+ .demod_xfer_mode = 0,
+ .ctl_pin_status_mask = 0xFFFFFFC4,
+ .agc_analog_digital_select_gpio = 0x0c,
+ .gpio_pin_status_mask = 0x4001000,
+ .tuner_i2c_master = 1,
+ .demod_i2c_master = 2,
+ .has_dvb = 1,
+ .demod_addr = 0x0e,
+ .norm = V4L2_STD_NTSC,
+
+ .input = {{
+ .type = CX231XX_VMUX_TELEVISION,
+ .vmux = CX231XX_VIN_3_1,
+ .amux = CX231XX_AMUX_VIDEO,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_COMPOSITE1,
+ .vmux = CX231XX_VIN_2_1,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_SVIDEO,
+ .vmux = CX231XX_VIN_1_1 |
+ (CX231XX_VIN_1_2 << 8) |
+ CX25840_SVIDEO_ON,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ } },
+ },
+ [CX231XX_BOARD_HAUPPAUGE_USBLIVE2] = {
+ .name = "Hauppauge USB Live 2",
+ .tuner_type = TUNER_ABSENT,
+ .decoder = CX231XX_AVDECODER,
+ .output_mode = OUT_MODE_VIP11,
+ .demod_xfer_mode = 0,
+ .ctl_pin_status_mask = 0xFFFFFFC4,
+ .agc_analog_digital_select_gpio = 0x0c,
+ .gpio_pin_status_mask = 0x4001000,
+ .norm = V4L2_STD_NTSC,
+ .no_alt_vanc = 1,
+ .external_av = 1,
+ .dont_use_port_3 = 1,
+ .input = {{
+ .type = CX231XX_VMUX_COMPOSITE1,
+ .vmux = CX231XX_VIN_2_1,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_SVIDEO,
+ .vmux = CX231XX_VIN_1_1 |
+ (CX231XX_VIN_1_2 << 8) |
+ CX25840_SVIDEO_ON,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ } },
+ },
+ [CX231XX_BOARD_KWORLD_UB430_USB_HYBRID] = {
+ .name = "Kworld UB430 USB Hybrid",
+ .tuner_type = TUNER_NXP_TDA18271,
+ .tuner_addr = 0x60,
+ .decoder = CX231XX_AVDECODER,
+ .output_mode = OUT_MODE_VIP11,
+ .demod_xfer_mode = 0,
+ .ctl_pin_status_mask = 0xFFFFFFC4,
+ .agc_analog_digital_select_gpio = 0x11, /* According with PV cxPolaris.inf file */
+ .tuner_sif_gpio = -1,
+ .tuner_scl_gpio = -1,
+ .tuner_sda_gpio = -1,
+ .gpio_pin_status_mask = 0x4001000,
+ .tuner_i2c_master = 2,
+ .demod_i2c_master = 1,
+ .ir_i2c_master = 2,
+ .has_dvb = 1,
+ .demod_addr = 0x10,
+ .norm = V4L2_STD_PAL_M,
+ .input = {{
+ .type = CX231XX_VMUX_TELEVISION,
+ .vmux = CX231XX_VIN_3_1,
+ .amux = CX231XX_AMUX_VIDEO,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_COMPOSITE1,
+ .vmux = CX231XX_VIN_2_1,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_SVIDEO,
+ .vmux = CX231XX_VIN_1_1 |
+ (CX231XX_VIN_1_2 << 8) |
+ CX25840_SVIDEO_ON,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ } },
+ },
+ [CX231XX_BOARD_PV_PLAYTV_USB_HYBRID] = {
+ .name = "Pixelview PlayTV USB Hybrid",
+ .tuner_type = TUNER_NXP_TDA18271,
+ .tuner_addr = 0x60,
+ .decoder = CX231XX_AVDECODER,
+ .output_mode = OUT_MODE_VIP11,
+ .demod_xfer_mode = 0,
+ .ctl_pin_status_mask = 0xFFFFFFC4,
+ .agc_analog_digital_select_gpio = 0x00, /* According with PV cxPolaris.inf file */
+ .tuner_sif_gpio = -1,
+ .tuner_scl_gpio = -1,
+ .tuner_sda_gpio = -1,
+ .gpio_pin_status_mask = 0x4001000,
+ .tuner_i2c_master = 2,
+ .demod_i2c_master = 1,
+ .ir_i2c_master = 2,
+ .rc_map_name = RC_MAP_PIXELVIEW_002T,
+ .has_dvb = 1,
+ .demod_addr = 0x10,
+ .norm = V4L2_STD_PAL_M,
+ .input = {{
+ .type = CX231XX_VMUX_TELEVISION,
+ .vmux = CX231XX_VIN_3_1,
+ .amux = CX231XX_AMUX_VIDEO,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_COMPOSITE1,
+ .vmux = CX231XX_VIN_2_1,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_SVIDEO,
+ .vmux = CX231XX_VIN_1_1 |
+ (CX231XX_VIN_1_2 << 8) |
+ CX25840_SVIDEO_ON,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ } },
+ },
+ [CX231XX_BOARD_PV_XCAPTURE_USB] = {
+ .name = "Pixelview Xcapture USB",
+ .tuner_type = TUNER_ABSENT,
+ .decoder = CX231XX_AVDECODER,
+ .output_mode = OUT_MODE_VIP11,
+ .demod_xfer_mode = 0,
+ .ctl_pin_status_mask = 0xFFFFFFC4,
+ .agc_analog_digital_select_gpio = 0x0c,
+ .gpio_pin_status_mask = 0x4001000,
+ .norm = V4L2_STD_NTSC,
+ .no_alt_vanc = 1,
+ .external_av = 1,
+ .dont_use_port_3 = 1,
+
+ .input = {{
+ .type = CX231XX_VMUX_COMPOSITE1,
+ .vmux = CX231XX_VIN_2_1,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_SVIDEO,
+ .vmux = CX231XX_VIN_1_1 |
+ (CX231XX_VIN_1_2 << 8) |
+ CX25840_SVIDEO_ON,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ }
+ },
+ },
+
+ [CX231XX_BOARD_ICONBIT_U100] = {
+ .name = "Iconbit Analog Stick U100 FM",
+ .tuner_type = TUNER_ABSENT,
+ .decoder = CX231XX_AVDECODER,
+ .output_mode = OUT_MODE_VIP11,
+ .demod_xfer_mode = 0,
+ .ctl_pin_status_mask = 0xFFFFFFC4,
+ .agc_analog_digital_select_gpio = 0x1C,
+ .gpio_pin_status_mask = 0x4001000,
+
+ .input = {{
+ .type = CX231XX_VMUX_COMPOSITE1,
+ .vmux = CX231XX_VIN_2_1,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_SVIDEO,
+ .vmux = CX231XX_VIN_1_1 |
+ (CX231XX_VIN_1_2 << 8) |
+ CX25840_SVIDEO_ON,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ } },
+ },
+ [CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL] = {
+ .name = "Hauppauge WinTV USB2 FM (PAL)",
+ .tuner_type = TUNER_NXP_TDA18271,
+ .tuner_addr = 0x60,
+ .tuner_gpio = RDE250_XCV_TUNER,
+ .tuner_sif_gpio = 0x05,
+ .tuner_scl_gpio = 0x1a,
+ .tuner_sda_gpio = 0x1b,
+ .decoder = CX231XX_AVDECODER,
+ .output_mode = OUT_MODE_VIP11,
+ .ctl_pin_status_mask = 0xFFFFFFC4,
+ .agc_analog_digital_select_gpio = 0x0c,
+ .gpio_pin_status_mask = 0x4001000,
+ .tuner_i2c_master = 1,
+ .norm = V4L2_STD_PAL,
+
+ .input = {{
+ .type = CX231XX_VMUX_TELEVISION,
+ .vmux = CX231XX_VIN_3_1,
+ .amux = CX231XX_AMUX_VIDEO,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_COMPOSITE1,
+ .vmux = CX231XX_VIN_2_1,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_SVIDEO,
+ .vmux = CX231XX_VIN_1_1 |
+ (CX231XX_VIN_1_2 << 8) |
+ CX25840_SVIDEO_ON,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ } },
+ },
+ [CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC] = {
+ .name = "Hauppauge WinTV USB2 FM (NTSC)",
+ .tuner_type = TUNER_NXP_TDA18271,
+ .tuner_addr = 0x60,
+ .tuner_gpio = RDE250_XCV_TUNER,
+ .tuner_sif_gpio = 0x05,
+ .tuner_scl_gpio = 0x1a,
+ .tuner_sda_gpio = 0x1b,
+ .decoder = CX231XX_AVDECODER,
+ .output_mode = OUT_MODE_VIP11,
+ .ctl_pin_status_mask = 0xFFFFFFC4,
+ .agc_analog_digital_select_gpio = 0x0c,
+ .gpio_pin_status_mask = 0x4001000,
+ .tuner_i2c_master = 1,
+ .norm = V4L2_STD_NTSC,
+
+ .input = {{
+ .type = CX231XX_VMUX_TELEVISION,
+ .vmux = CX231XX_VIN_3_1,
+ .amux = CX231XX_AMUX_VIDEO,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_COMPOSITE1,
+ .vmux = CX231XX_VIN_2_1,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_SVIDEO,
+ .vmux = CX231XX_VIN_1_1 |
+ (CX231XX_VIN_1_2 << 8) |
+ CX25840_SVIDEO_ON,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ } },
+ },
+};
+const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards);
+
+/* table of devices that work with this driver */
+struct usb_device_id cx231xx_id_table[] = {
+ {USB_DEVICE(0x0572, 0x5A3C),
+ .driver_info = CX231XX_BOARD_UNKNOWN},
+ {USB_DEVICE(0x0572, 0x58A2),
+ .driver_info = CX231XX_BOARD_CNXT_CARRAERA},
+ {USB_DEVICE(0x0572, 0x58A1),
+ .driver_info = CX231XX_BOARD_CNXT_SHELBY},
+ {USB_DEVICE(0x0572, 0x58A4),
+ .driver_info = CX231XX_BOARD_CNXT_RDE_253S},
+ {USB_DEVICE(0x0572, 0x58A5),
+ .driver_info = CX231XX_BOARD_CNXT_RDU_253S},
+ {USB_DEVICE(0x0572, 0x58A6),
+ .driver_info = CX231XX_BOARD_CNXT_VIDEO_GRABBER},
+ {USB_DEVICE(0x0572, 0x589E),
+ .driver_info = CX231XX_BOARD_CNXT_RDE_250},
+ {USB_DEVICE(0x0572, 0x58A0),
+ .driver_info = CX231XX_BOARD_CNXT_RDU_250},
+ {USB_DEVICE(0x2040, 0xb110),
+ .driver_info = CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL},
+ {USB_DEVICE(0x2040, 0xb111),
+ .driver_info = CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC},
+ {USB_DEVICE(0x2040, 0xb120),
+ .driver_info = CX231XX_BOARD_HAUPPAUGE_EXETER},
+ {USB_DEVICE(0x2040, 0xb140),
+ .driver_info = CX231XX_BOARD_HAUPPAUGE_EXETER},
+ {USB_DEVICE(0x2040, 0xc200),
+ .driver_info = CX231XX_BOARD_HAUPPAUGE_USBLIVE2},
+ {USB_DEVICE_VER(USB_VID_PIXELVIEW, USB_PID_PIXELVIEW_SBTVD, 0x4000, 0x4001),
+ .driver_info = CX231XX_BOARD_PV_PLAYTV_USB_HYBRID},
+ {USB_DEVICE(USB_VID_PIXELVIEW, 0x5014),
+ .driver_info = CX231XX_BOARD_PV_XCAPTURE_USB},
+ {USB_DEVICE(0x1b80, 0xe424),
+ .driver_info = CX231XX_BOARD_KWORLD_UB430_USB_HYBRID},
+ {USB_DEVICE(0x1f4d, 0x0237),
+ .driver_info = CX231XX_BOARD_ICONBIT_U100},
+ {},
+};
+
+MODULE_DEVICE_TABLE(usb, cx231xx_id_table);
+
+/* cx231xx_tuner_callback
+ * will be used to reset XC5000 tuner using GPIO pin
+ */
+
+int cx231xx_tuner_callback(void *ptr, int component, int command, int arg)
+{
+ int rc = 0;
+ struct cx231xx *dev = ptr;
+
+ if (dev->tuner_type == TUNER_XC5000) {
+ if (command == XC5000_TUNER_RESET) {
+ cx231xx_info
+ ("Tuner CB: RESET: cmd %d : tuner type %d \n",
+ command, dev->tuner_type);
+ cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit,
+ 1);
+ msleep(10);
+ cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit,
+ 0);
+ msleep(330);
+ cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit,
+ 1);
+ msleep(10);
+ }
+ } else if (dev->tuner_type == TUNER_NXP_TDA18271) {
+ switch (command) {
+ case TDA18271_CALLBACK_CMD_AGC_ENABLE:
+ if (dev->model == CX231XX_BOARD_PV_PLAYTV_USB_HYBRID)
+ rc = cx231xx_set_agc_analog_digital_mux_select(dev, arg);
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ }
+ return rc;
+}
+EXPORT_SYMBOL_GPL(cx231xx_tuner_callback);
+
+void cx231xx_reset_out(struct cx231xx *dev)
+{
+ cx231xx_set_gpio_value(dev, CX23417_RESET, 1);
+ msleep(200);
+ cx231xx_set_gpio_value(dev, CX23417_RESET, 0);
+ msleep(200);
+ cx231xx_set_gpio_value(dev, CX23417_RESET, 1);
+}
+void cx231xx_enable_OSC(struct cx231xx *dev)
+{
+ cx231xx_set_gpio_value(dev, CX23417_OSC_EN, 1);
+}
+void cx231xx_sleep_s5h1432(struct cx231xx *dev)
+{
+ cx231xx_set_gpio_value(dev, SLEEP_S5H1432, 0);
+}
+
+static inline void cx231xx_set_model(struct cx231xx *dev)
+{
+ memcpy(&dev->board, &cx231xx_boards[dev->model], sizeof(dev->board));
+}
+
+/* Since cx231xx_pre_card_setup() requires a proper dev->model,
+ * this won't work for boards with generic PCI IDs
+ */
+void cx231xx_pre_card_setup(struct cx231xx *dev)
+{
+
+ cx231xx_set_model(dev);
+
+ cx231xx_info("Identified as %s (card=%d)\n",
+ dev->board.name, dev->model);
+
+ /* set the direction for GPIO pins */
+ if (dev->board.tuner_gpio) {
+ cx231xx_set_gpio_direction(dev, dev->board.tuner_gpio->bit, 1);
+ cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit, 1);
+ }
+ if (dev->board.tuner_sif_gpio >= 0)
+ cx231xx_set_gpio_direction(dev, dev->board.tuner_sif_gpio, 1);
+
+ /* request some modules if any required */
+
+ /* set the mode to Analog mode initially */
+ cx231xx_set_mode(dev, CX231XX_ANALOG_MODE);
+
+ /* Unlock device */
+ /* cx231xx_set_mode(dev, CX231XX_SUSPEND); */
+
+}
+
+static void cx231xx_config_tuner(struct cx231xx *dev)
+{
+ struct tuner_setup tun_setup;
+ struct v4l2_frequency f;
+
+ if (dev->tuner_type == TUNER_ABSENT)
+ return;
+
+ tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
+ tun_setup.type = dev->tuner_type;
+ tun_setup.addr = dev->tuner_addr;
+ tun_setup.tuner_callback = cx231xx_tuner_callback;
+
+ tuner_call(dev, tuner, s_type_addr, &tun_setup);
+
+#if 0
+ if (tun_setup.type == TUNER_XC5000) {
+ static struct xc2028_ctrl ctrl = {
+ .fname = XC5000_DEFAULT_FIRMWARE,
+ .max_len = 64,
+ .demod = 0;
+ };
+ struct v4l2_priv_tun_config cfg = {
+ .tuner = dev->tuner_type,
+ .priv = &ctrl,
+ };
+ tuner_call(dev, tuner, s_config, &cfg);
+ }
+#endif
+ /* configure tuner */
+ f.tuner = 0;
+ f.type = V4L2_TUNER_ANALOG_TV;
+ f.frequency = 9076; /* just a magic number */
+ dev->ctl_freq = f.frequency;
+ call_all(dev, tuner, s_frequency, &f);
+
+}
+
+void cx231xx_card_setup(struct cx231xx *dev)
+{
+
+ cx231xx_set_model(dev);
+
+ dev->tuner_type = cx231xx_boards[dev->model].tuner_type;
+ if (cx231xx_boards[dev->model].tuner_addr)
+ dev->tuner_addr = cx231xx_boards[dev->model].tuner_addr;
+
+ /* request some modules */
+ if (dev->board.decoder == CX231XX_AVDECODER) {
+ dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
+ &dev->i2c_bus[0].i2c_adap,
+ "cx25840", 0x88 >> 1, NULL);
+ if (dev->sd_cx25840 == NULL)
+ cx231xx_info("cx25840 subdev registration failure\n");
+ cx25840_call(dev, core, load_fw);
+
+ }
+
+ /* Initialize the tuner */
+ if (dev->board.tuner_type != TUNER_ABSENT) {
+ dev->sd_tuner = v4l2_i2c_new_subdev(&dev->v4l2_dev,
+ &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
+ "tuner",
+ dev->tuner_addr, NULL);
+ if (dev->sd_tuner == NULL)
+ cx231xx_info("tuner subdev registration failure\n");
+ else
+ cx231xx_config_tuner(dev);
+ }
+}
+
+/*
+ * cx231xx_config()
+ * inits registers with sane defaults
+ */
+int cx231xx_config(struct cx231xx *dev)
+{
+ /* TBD need to add cx231xx specific code */
+ dev->mute = 1; /* maybe not the right place... */
+ dev->volume = 0x1f;
+
+ return 0;
+}
+
+/*
+ * cx231xx_config_i2c()
+ * configure i2c attached devices
+ */
+void cx231xx_config_i2c(struct cx231xx *dev)
+{
+ /* u32 input = INPUT(dev->video_input)->vmux; */
+
+ call_all(dev, video, s_stream, 1);
+}
+
+/*
+ * cx231xx_realease_resources()
+ * unregisters the v4l2,i2c and usb devices
+ * called when the device gets disconected or at module unload
+*/
+void cx231xx_release_resources(struct cx231xx *dev)
+{
+ cx231xx_release_analog_resources(dev);
+
+ cx231xx_remove_from_devlist(dev);
+
+ cx231xx_ir_exit(dev);
+
+ /* Release I2C buses */
+ cx231xx_dev_uninit(dev);
+
+ /* delete v4l2 device */
+ v4l2_device_unregister(&dev->v4l2_dev);
+
+ usb_put_dev(dev->udev);
+
+ /* Mark device as unused */
+ clear_bit(dev->devno, &cx231xx_devused);
+
+ kfree(dev->video_mode.alt_max_pkt_size);
+ kfree(dev->vbi_mode.alt_max_pkt_size);
+ kfree(dev->sliced_cc_mode.alt_max_pkt_size);
+ kfree(dev->ts1_mode.alt_max_pkt_size);
+ kfree(dev);
+}
+
+/*
+ * cx231xx_init_dev()
+ * allocates and inits the device structs, registers i2c bus and v4l device
+ */
+static int cx231xx_init_dev(struct cx231xx *dev, struct usb_device *udev,
+ int minor)
+{
+ int retval = -ENOMEM;
+ int errCode;
+ unsigned int maxh, maxw;
+
+ dev->udev = udev;
+ mutex_init(&dev->lock);
+ mutex_init(&dev->ctrl_urb_lock);
+ mutex_init(&dev->gpio_i2c_lock);
+ mutex_init(&dev->i2c_lock);
+
+ spin_lock_init(&dev->video_mode.slock);
+ spin_lock_init(&dev->vbi_mode.slock);
+ spin_lock_init(&dev->sliced_cc_mode.slock);
+
+ init_waitqueue_head(&dev->open);
+ init_waitqueue_head(&dev->wait_frame);
+ init_waitqueue_head(&dev->wait_stream);
+
+ dev->cx231xx_read_ctrl_reg = cx231xx_read_ctrl_reg;
+ dev->cx231xx_write_ctrl_reg = cx231xx_write_ctrl_reg;
+ dev->cx231xx_send_usb_command = cx231xx_send_usb_command;
+ dev->cx231xx_gpio_i2c_read = cx231xx_gpio_i2c_read;
+ dev->cx231xx_gpio_i2c_write = cx231xx_gpio_i2c_write;
+
+ /* Query cx231xx to find what pcb config it is related to */
+ initialize_cx231xx(dev);
+
+ /*To workaround error number=-71 on EP0 for VideoGrabber,
+ need set alt here.*/
+ if (dev->model == CX231XX_BOARD_CNXT_VIDEO_GRABBER ||
+ dev->model == CX231XX_BOARD_HAUPPAUGE_USBLIVE2) {
+ cx231xx_set_alt_setting(dev, INDEX_VIDEO, 3);
+ cx231xx_set_alt_setting(dev, INDEX_VANC, 1);
+ }
+ /* Cx231xx pre card setup */
+ cx231xx_pre_card_setup(dev);
+
+ errCode = cx231xx_config(dev);
+ if (errCode) {
+ cx231xx_errdev("error configuring device\n");
+ return -ENOMEM;
+ }
+
+ /* set default norm */
+ dev->norm = dev->board.norm;
+
+ /* register i2c bus */
+ errCode = cx231xx_dev_init(dev);
+ if (errCode < 0) {
+ cx231xx_dev_uninit(dev);
+ cx231xx_errdev("%s: cx231xx_i2c_register - errCode [%d]!\n",
+ __func__, errCode);
+ return errCode;
+ }
+
+ /* Do board specific init */
+ cx231xx_card_setup(dev);
+
+ /* configure the device */
+ cx231xx_config_i2c(dev);
+
+ maxw = norm_maxw(dev);
+ maxh = norm_maxh(dev);
+
+ /* set default image size */
+ dev->width = maxw;
+ dev->height = maxh;
+ dev->interlaced = 0;
+ dev->video_input = 0;
+
+ errCode = cx231xx_config(dev);
+ if (errCode < 0) {
+ cx231xx_errdev("%s: cx231xx_config - errCode [%d]!\n",
+ __func__, errCode);
+ return errCode;
+ }
+
+ /* init video dma queues */
+ INIT_LIST_HEAD(&dev->video_mode.vidq.active);
+ INIT_LIST_HEAD(&dev->video_mode.vidq.queued);
+
+ /* init vbi dma queues */
+ INIT_LIST_HEAD(&dev->vbi_mode.vidq.active);
+ INIT_LIST_HEAD(&dev->vbi_mode.vidq.queued);
+
+ /* Reset other chips required if they are tied up with GPIO pins */
+ cx231xx_add_into_devlist(dev);
+
+ if (dev->board.has_417) {
+ printk(KERN_INFO "attach 417 %d\n", dev->model);
+ if (cx231xx_417_register(dev) < 0) {
+ printk(KERN_ERR
+ "%s() Failed to register 417 on VID_B\n",
+ __func__);
+ }
+ }
+
+ retval = cx231xx_register_analog_devices(dev);
+ if (retval < 0) {
+ cx231xx_release_resources(dev);
+ return retval;
+ }
+
+ cx231xx_ir_init(dev);
+
+ cx231xx_init_extension(dev);
+
+ return 0;
+}
+
+#if defined(CONFIG_MODULES) && defined(MODULE)
+static void request_module_async(struct work_struct *work)
+{
+ struct cx231xx *dev = container_of(work,
+ struct cx231xx, request_module_wk);
+
+ if (dev->has_alsa_audio)
+ request_module("cx231xx-alsa");
+
+ if (dev->board.has_dvb)
+ request_module("cx231xx-dvb");
+
+}
+
+static void request_modules(struct cx231xx *dev)
+{
+ INIT_WORK(&dev->request_module_wk, request_module_async);
+ schedule_work(&dev->request_module_wk);
+}
+
+static void flush_request_modules(struct cx231xx *dev)
+{
+ flush_work_sync(&dev->request_module_wk);
+}
+#else
+#define request_modules(dev)
+#define flush_request_modules(dev)
+#endif /* CONFIG_MODULES */
+
+/*
+ * cx231xx_usb_probe()
+ * checks for supported devices
+ */
+static int cx231xx_usb_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev;
+ struct usb_interface *uif;
+ struct cx231xx *dev = NULL;
+ int retval = -ENODEV;
+ int nr = 0, ifnum;
+ int i, isoc_pipe = 0;
+ char *speed;
+ char descr[255] = "";
+ struct usb_interface_assoc_descriptor *assoc_desc;
+
+ udev = usb_get_dev(interface_to_usbdev(interface));
+ ifnum = interface->altsetting[0].desc.bInterfaceNumber;
+
+ /*
+ * Interface number 0 - IR interface (handled by mceusb driver)
+ * Interface number 1 - AV interface (handled by this driver)
+ */
+ if (ifnum != 1)
+ return -ENODEV;
+
+ /* Check to see next free device and mark as used */
+ do {
+ nr = find_first_zero_bit(&cx231xx_devused, CX231XX_MAXBOARDS);
+ if (nr >= CX231XX_MAXBOARDS) {
+ /* No free device slots */
+ cx231xx_err(DRIVER_NAME ": Supports only %i devices.\n",
+ CX231XX_MAXBOARDS);
+ return -ENOMEM;
+ }
+ } while (test_and_set_bit(nr, &cx231xx_devused));
+
+ /* allocate memory for our device state and initialize it */
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL) {
+ cx231xx_err(DRIVER_NAME ": out of memory!\n");
+ clear_bit(nr, &cx231xx_devused);
+ return -ENOMEM;
+ }
+
+ snprintf(dev->name, 29, "cx231xx #%d", nr);
+ dev->devno = nr;
+ dev->model = id->driver_info;
+ dev->video_mode.alt = -1;
+
+ dev->interface_count++;
+ /* reset gpio dir and value */
+ dev->gpio_dir = 0;
+ dev->gpio_val = 0;
+ dev->xc_fw_load_done = 0;
+ dev->has_alsa_audio = 1;
+ dev->power_mode = -1;
+ atomic_set(&dev->devlist_count, 0);
+
+ /* 0 - vbi ; 1 -sliced cc mode */
+ dev->vbi_or_sliced_cc_mode = 0;
+
+ /* get maximum no.of IAD interfaces */
+ assoc_desc = udev->actconfig->intf_assoc[0];
+ dev->max_iad_interface_count = assoc_desc->bInterfaceCount;
+
+ /* init CIR module TBD */
+
+ /*mode_tv: digital=1 or analog=0*/
+ dev->mode_tv = 0;
+
+ dev->USE_ISO = transfer_mode;
+
+ switch (udev->speed) {
+ case USB_SPEED_LOW:
+ speed = "1.5";
+ break;
+ case USB_SPEED_UNKNOWN:
+ case USB_SPEED_FULL:
+ speed = "12";
+ break;
+ case USB_SPEED_HIGH:
+ speed = "480";
+ break;
+ default:
+ speed = "unknown";
+ }
+
+ if (udev->manufacturer)
+ strlcpy(descr, udev->manufacturer, sizeof(descr));
+
+ if (udev->product) {
+ if (*descr)
+ strlcat(descr, " ", sizeof(descr));
+ strlcat(descr, udev->product, sizeof(descr));
+ }
+ if (*descr)
+ strlcat(descr, " ", sizeof(descr));
+
+ cx231xx_info("New device %s@ %s Mbps "
+ "(%04x:%04x) with %d interfaces\n",
+ descr,
+ speed,
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct),
+ dev->max_iad_interface_count);
+
+ /* increment interface count */
+ dev->interface_count++;
+
+ /* get device number */
+ nr = dev->devno;
+
+ assoc_desc = udev->actconfig->intf_assoc[0];
+ if (assoc_desc->bFirstInterface != ifnum) {
+ cx231xx_err(DRIVER_NAME ": Not found "
+ "matching IAD interface\n");
+ clear_bit(dev->devno, &cx231xx_devused);
+ kfree(dev);
+ dev = NULL;
+ return -ENODEV;
+ }
+
+ cx231xx_info("registering interface %d\n", ifnum);
+
+ /* save our data pointer in this interface device */
+ usb_set_intfdata(interface, dev);
+
+ /*
+ * AV device initialization - only done at the last interface
+ */
+
+ /* Create v4l2 device */
+ retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev);
+ if (retval) {
+ cx231xx_errdev("v4l2_device_register failed\n");
+ clear_bit(dev->devno, &cx231xx_devused);
+ kfree(dev);
+ dev = NULL;
+ return -EIO;
+ }
+ /* allocate device struct */
+ retval = cx231xx_init_dev(dev, udev, nr);
+ if (retval) {
+ clear_bit(dev->devno, &cx231xx_devused);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ kfree(dev);
+ dev = NULL;
+ usb_set_intfdata(interface, NULL);
+
+ return retval;
+ }
+
+ /* compute alternate max packet sizes for video */
+ uif = udev->actconfig->interface[dev->current_pcb_config.
+ hs_config_info[0].interface_info.video_index + 1];
+
+ dev->video_mode.end_point_addr = le16_to_cpu(uif->altsetting[0].
+ endpoint[isoc_pipe].desc.bEndpointAddress);
+
+ dev->video_mode.num_alt = uif->num_altsetting;
+ cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
+ dev->video_mode.end_point_addr,
+ dev->video_mode.num_alt);
+ dev->video_mode.alt_max_pkt_size =
+ kmalloc(32 * dev->video_mode.num_alt, GFP_KERNEL);
+
+ if (dev->video_mode.alt_max_pkt_size == NULL) {
+ cx231xx_errdev("out of memory!\n");
+ clear_bit(dev->devno, &cx231xx_devused);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ kfree(dev);
+ dev = NULL;
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < dev->video_mode.num_alt; i++) {
+ u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].
+ desc.wMaxPacketSize);
+ dev->video_mode.alt_max_pkt_size[i] =
+ (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
+ cx231xx_info("Alternate setting %i, max size= %i\n", i,
+ dev->video_mode.alt_max_pkt_size[i]);
+ }
+
+ /* compute alternate max packet sizes for vbi */
+ uif = udev->actconfig->interface[dev->current_pcb_config.
+ hs_config_info[0].interface_info.
+ vanc_index + 1];
+
+ dev->vbi_mode.end_point_addr =
+ le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc.
+ bEndpointAddress);
+
+ dev->vbi_mode.num_alt = uif->num_altsetting;
+ cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
+ dev->vbi_mode.end_point_addr,
+ dev->vbi_mode.num_alt);
+ dev->vbi_mode.alt_max_pkt_size =
+ kmalloc(32 * dev->vbi_mode.num_alt, GFP_KERNEL);
+
+ if (dev->vbi_mode.alt_max_pkt_size == NULL) {
+ cx231xx_errdev("out of memory!\n");
+ clear_bit(dev->devno, &cx231xx_devused);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ kfree(dev);
+ dev = NULL;
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < dev->vbi_mode.num_alt; i++) {
+ u16 tmp =
+ le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].
+ desc.wMaxPacketSize);
+ dev->vbi_mode.alt_max_pkt_size[i] =
+ (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
+ cx231xx_info("Alternate setting %i, max size= %i\n", i,
+ dev->vbi_mode.alt_max_pkt_size[i]);
+ }
+
+ /* compute alternate max packet sizes for sliced CC */
+ uif = udev->actconfig->interface[dev->current_pcb_config.
+ hs_config_info[0].interface_info.
+ hanc_index + 1];
+
+ dev->sliced_cc_mode.end_point_addr =
+ le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc.
+ bEndpointAddress);
+
+ dev->sliced_cc_mode.num_alt = uif->num_altsetting;
+ cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
+ dev->sliced_cc_mode.end_point_addr,
+ dev->sliced_cc_mode.num_alt);
+ dev->sliced_cc_mode.alt_max_pkt_size =
+ kmalloc(32 * dev->sliced_cc_mode.num_alt, GFP_KERNEL);
+
+ if (dev->sliced_cc_mode.alt_max_pkt_size == NULL) {
+ cx231xx_errdev("out of memory!\n");
+ clear_bit(dev->devno, &cx231xx_devused);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ kfree(dev);
+ dev = NULL;
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < dev->sliced_cc_mode.num_alt; i++) {
+ u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].
+ desc.wMaxPacketSize);
+ dev->sliced_cc_mode.alt_max_pkt_size[i] =
+ (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
+ cx231xx_info("Alternate setting %i, max size= %i\n", i,
+ dev->sliced_cc_mode.alt_max_pkt_size[i]);
+ }
+
+ if (dev->current_pcb_config.ts1_source != 0xff) {
+ /* compute alternate max packet sizes for TS1 */
+ uif = udev->actconfig->interface[dev->current_pcb_config.
+ hs_config_info[0].
+ interface_info.
+ ts1_index + 1];
+
+ dev->ts1_mode.end_point_addr =
+ le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].
+ desc.bEndpointAddress);
+
+ dev->ts1_mode.num_alt = uif->num_altsetting;
+ cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
+ dev->ts1_mode.end_point_addr,
+ dev->ts1_mode.num_alt);
+ dev->ts1_mode.alt_max_pkt_size =
+ kmalloc(32 * dev->ts1_mode.num_alt, GFP_KERNEL);
+
+ if (dev->ts1_mode.alt_max_pkt_size == NULL) {
+ cx231xx_errdev("out of memory!\n");
+ clear_bit(dev->devno, &cx231xx_devused);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ kfree(dev);
+ dev = NULL;
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < dev->ts1_mode.num_alt; i++) {
+ u16 tmp = le16_to_cpu(uif->altsetting[i].
+ endpoint[isoc_pipe].desc.
+ wMaxPacketSize);
+ dev->ts1_mode.alt_max_pkt_size[i] =
+ (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
+ cx231xx_info("Alternate setting %i, max size= %i\n", i,
+ dev->ts1_mode.alt_max_pkt_size[i]);
+ }
+ }
+
+ if (dev->model == CX231XX_BOARD_CNXT_VIDEO_GRABBER) {
+ cx231xx_enable_OSC(dev);
+ cx231xx_reset_out(dev);
+ cx231xx_set_alt_setting(dev, INDEX_VIDEO, 3);
+ }
+
+ if (dev->model == CX231XX_BOARD_CNXT_RDE_253S)
+ cx231xx_sleep_s5h1432(dev);
+
+ /* load other modules required */
+ request_modules(dev);
+
+ return 0;
+}
+
+/*
+ * cx231xx_usb_disconnect()
+ * called when the device gets diconencted
+ * video device will be unregistered on v4l2_close in case it is still open
+ */
+static void cx231xx_usb_disconnect(struct usb_interface *interface)
+{
+ struct cx231xx *dev;
+
+ dev = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+
+ if (!dev)
+ return;
+
+ if (!dev->udev)
+ return;
+
+ dev->state |= DEV_DISCONNECTED;
+
+ flush_request_modules(dev);
+
+ /* wait until all current v4l2 io is finished then deallocate
+ resources */
+ mutex_lock(&dev->lock);
+
+ wake_up_interruptible_all(&dev->open);
+
+ if (dev->users) {
+ cx231xx_warn
+ ("device %s is open! Deregistration and memory "
+ "deallocation are deferred on close.\n",
+ video_device_node_name(dev->vdev));
+
+ /* Even having users, it is safe to remove the RC i2c driver */
+ cx231xx_ir_exit(dev);
+
+ if (dev->USE_ISO)
+ cx231xx_uninit_isoc(dev);
+ else
+ cx231xx_uninit_bulk(dev);
+ wake_up_interruptible(&dev->wait_frame);
+ wake_up_interruptible(&dev->wait_stream);
+ } else {
+ }
+
+ cx231xx_close_extension(dev);
+
+ mutex_unlock(&dev->lock);
+
+ if (!dev->users)
+ cx231xx_release_resources(dev);
+}
+
+static struct usb_driver cx231xx_usb_driver = {
+ .name = "cx231xx",
+ .probe = cx231xx_usb_probe,
+ .disconnect = cx231xx_usb_disconnect,
+ .id_table = cx231xx_id_table,
+};
+
+module_usb_driver(cx231xx_usb_driver);
diff --git a/drivers/media/video/cx231xx/cx231xx-conf-reg.h b/drivers/media/video/cx231xx/cx231xx-conf-reg.h
new file mode 100644
index 00000000..25593f21
--- /dev/null
+++ b/drivers/media/video/cx231xx/cx231xx-conf-reg.h
@@ -0,0 +1,495 @@
+/*
+ cx231xx_conf-reg.h - driver for Conexant Cx23100/101/102 USB
+ video capture devices
+
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _POLARIS_REG_H_
+#define _POLARIS_REG_H_
+
+#define BOARD_CFG_STAT 0x0
+#define TS_MODE_REG 0x4
+#define TS1_CFG_REG 0x8
+#define TS1_LENGTH_REG 0xc
+#define TS2_CFG_REG 0x10
+#define TS2_LENGTH_REG 0x14
+#define EP_MODE_SET 0x18
+#define CIR_PWR_PTN1 0x1c
+#define CIR_PWR_PTN2 0x20
+#define CIR_PWR_PTN3 0x24
+#define CIR_PWR_MASK0 0x28
+#define CIR_PWR_MASK1 0x2c
+#define CIR_PWR_MASK2 0x30
+#define CIR_GAIN 0x34
+#define CIR_CAR_REG 0x38
+#define CIR_OT_CFG1 0x40
+#define CIR_OT_CFG2 0x44
+#define GBULK_BIT_EN 0x68
+#define PWR_CTL_EN 0x74
+
+/* Polaris Endpoints capture mask for register EP_MODE_SET */
+#define ENABLE_EP1 0x01 /* Bit[0]=1 */
+#define ENABLE_EP2 0x02 /* Bit[1]=1 */
+#define ENABLE_EP3 0x04 /* Bit[2]=1 */
+#define ENABLE_EP4 0x08 /* Bit[3]=1 */
+#define ENABLE_EP5 0x10 /* Bit[4]=1 */
+#define ENABLE_EP6 0x20 /* Bit[5]=1 */
+
+/* Bit definition for register PWR_CTL_EN */
+#define PWR_MODE_MASK 0x17f
+#define PWR_AV_EN 0x08 /* bit3 */
+#define PWR_ISO_EN 0x40 /* bit6 */
+#define PWR_AV_MODE 0x30 /* bit4,5 */
+#define PWR_TUNER_EN 0x04 /* bit2 */
+#define PWR_DEMOD_EN 0x02 /* bit1 */
+#define I2C_DEMOD_EN 0x01 /* bit0 */
+#define PWR_RESETOUT_EN 0x100 /* bit8 */
+
+enum AV_MODE{
+ POLARIS_AVMODE_DEFAULT = 0,
+ POLARIS_AVMODE_DIGITAL = 0x10,
+ POLARIS_AVMODE_ANALOGT_TV = 0x20,
+ POLARIS_AVMODE_ENXTERNAL_AV = 0x30,
+
+};
+
+/* Colibri Registers */
+
+#define SINGLE_ENDED 0x0
+#define LOW_IF 0x4
+#define EU_IF 0x9
+#define US_IF 0xa
+
+#define SUP_BLK_TUNE1 0x00
+#define SUP_BLK_TUNE2 0x01
+#define SUP_BLK_TUNE3 0x02
+#define SUP_BLK_XTAL 0x03
+#define SUP_BLK_PLL1 0x04
+#define SUP_BLK_PLL2 0x05
+#define SUP_BLK_PLL3 0x06
+#define SUP_BLK_REF 0x07
+#define SUP_BLK_PWRDN 0x08
+#define SUP_BLK_TESTPAD 0x09
+#define ADC_COM_INT5_STAB_REF 0x0a
+#define ADC_COM_QUANT 0x0b
+#define ADC_COM_BIAS1 0x0c
+#define ADC_COM_BIAS2 0x0d
+#define ADC_COM_BIAS3 0x0e
+#define TESTBUS_CTRL 0x12
+
+#define FLD_PWRDN_TUNING_BIAS 0x10
+#define FLD_PWRDN_ENABLE_PLL 0x08
+#define FLD_PWRDN_PD_BANDGAP 0x04
+#define FLD_PWRDN_PD_BIAS 0x02
+#define FLD_PWRDN_PD_TUNECK 0x01
+
+
+#define ADC_STATUS_CH1 0x20
+#define ADC_STATUS_CH2 0x40
+#define ADC_STATUS_CH3 0x60
+
+#define ADC_STATUS2_CH1 0x21
+#define ADC_STATUS2_CH2 0x41
+#define ADC_STATUS2_CH3 0x61
+
+#define ADC_CAL_ATEST_CH1 0x22
+#define ADC_CAL_ATEST_CH2 0x42
+#define ADC_CAL_ATEST_CH3 0x62
+
+#define ADC_PWRDN_CLAMP_CH1 0x23
+#define ADC_PWRDN_CLAMP_CH2 0x43
+#define ADC_PWRDN_CLAMP_CH3 0x63
+
+#define ADC_CTRL_DAC23_CH1 0x24
+#define ADC_CTRL_DAC23_CH2 0x44
+#define ADC_CTRL_DAC23_CH3 0x64
+
+#define ADC_CTRL_DAC1_CH1 0x25
+#define ADC_CTRL_DAC1_CH2 0x45
+#define ADC_CTRL_DAC1_CH3 0x65
+
+#define ADC_DCSERVO_DEM_CH1 0x26
+#define ADC_DCSERVO_DEM_CH2 0x46
+#define ADC_DCSERVO_DEM_CH3 0x66
+
+#define ADC_FB_FRCRST_CH1 0x27
+#define ADC_FB_FRCRST_CH2 0x47
+#define ADC_FB_FRCRST_CH3 0x67
+
+#define ADC_INPUT_CH1 0x28
+#define ADC_INPUT_CH2 0x48
+#define ADC_INPUT_CH3 0x68
+#define INPUT_SEL_MASK 0x30 /* [5:4] in_sel */
+
+#define ADC_NTF_PRECLMP_EN_CH1 0x29
+#define ADC_NTF_PRECLMP_EN_CH2 0x49
+#define ADC_NTF_PRECLMP_EN_CH3 0x69
+
+#define ADC_QGAIN_RES_TRM_CH1 0x2a
+#define ADC_QGAIN_RES_TRM_CH2 0x4a
+#define ADC_QGAIN_RES_TRM_CH3 0x6a
+
+#define ADC_SOC_PRECLMP_TERM_CH1 0x2b
+#define ADC_SOC_PRECLMP_TERM_CH2 0x4b
+#define ADC_SOC_PRECLMP_TERM_CH3 0x6b
+
+#define TESTBUS_CTRL_CH1 0x32
+#define TESTBUS_CTRL_CH2 0x52
+#define TESTBUS_CTRL_CH3 0x72
+
+/******************************************************************************
+ * DIF registers *
+ ******************************************************************************/
+#define DIRECT_IF_REVB_BASE 0x00300
+
+/*****************************************************************************/
+#define DIF_PLL_FREQ_WORD (DIRECT_IF_REVB_BASE + 0x00000000)
+/*****************************************************************************/
+#define FLD_DIF_PLL_LOCK 0x80000000
+/* Reserved [30:29] */
+#define FLD_DIF_PLL_FREE_RUN 0x10000000
+#define FLD_DIF_PLL_FREQ 0x0fffffff
+
+/*****************************************************************************/
+#define DIF_PLL_CTRL (DIRECT_IF_REVB_BASE + 0x00000004)
+/*****************************************************************************/
+#define FLD_DIF_KD_PD 0xff000000
+/* Reserved [23:20] */
+#define FLD_DIF_KDS_PD 0x000f0000
+#define FLD_DIF_KI_PD 0x0000ff00
+/* Reserved [7:4] */
+#define FLD_DIF_KIS_PD 0x0000000f
+
+/*****************************************************************************/
+#define DIF_PLL_CTRL1 (DIRECT_IF_REVB_BASE + 0x00000008)
+/*****************************************************************************/
+#define FLD_DIF_KD_FD 0xff000000
+/* Reserved [23:20] */
+#define FLD_DIF_KDS_FD 0x000f0000
+#define FLD_DIF_KI_FD 0x0000ff00
+#define FLD_DIF_SIG_PROP_SZ 0x000000f0
+#define FLD_DIF_KIS_FD 0x0000000f
+
+/*****************************************************************************/
+#define DIF_PLL_CTRL2 (DIRECT_IF_REVB_BASE + 0x0000000c)
+/*****************************************************************************/
+#define FLD_DIF_PLL_AGC_REF 0xfff00000
+#define FLD_DIF_PLL_AGC_KI 0x000f0000
+/* Reserved [15] */
+#define FLD_DIF_FREQ_LIMIT 0x00007000
+#define FLD_DIF_K_FD 0x00000f00
+#define FLD_DIF_DOWNSMPL_FD 0x000000ff
+
+/*****************************************************************************/
+#define DIF_PLL_CTRL3 (DIRECT_IF_REVB_BASE + 0x00000010)
+/*****************************************************************************/
+/* Reserved [31:16] */
+#define FLD_DIF_PLL_AGC_EN 0x00008000
+/* Reserved [14:12] */
+#define FLD_DIF_PLL_MAN_GAIN 0x00000fff
+
+/*****************************************************************************/
+#define DIF_AGC_IF_REF (DIRECT_IF_REVB_BASE + 0x00000014)
+/*****************************************************************************/
+#define FLD_DIF_K_AGC_RF 0xf0000000
+#define FLD_DIF_K_AGC_IF 0x0f000000
+#define FLD_DIF_K_AGC_INT 0x00f00000
+/* Reserved [19:12] */
+#define FLD_DIF_IF_REF 0x00000fff
+
+/*****************************************************************************/
+#define DIF_AGC_CTRL_IF (DIRECT_IF_REVB_BASE + 0x00000018)
+/*****************************************************************************/
+#define FLD_DIF_IF_MAX 0xff000000
+#define FLD_DIF_IF_MIN 0x00ff0000
+#define FLD_DIF_IF_AGC 0x0000ffff
+
+/*****************************************************************************/
+#define DIF_AGC_CTRL_INT (DIRECT_IF_REVB_BASE + 0x0000001c)
+/*****************************************************************************/
+#define FLD_DIF_INT_MAX 0xff000000
+#define FLD_DIF_INT_MIN 0x00ff0000
+#define FLD_DIF_INT_AGC 0x0000ffff
+
+/*****************************************************************************/
+#define DIF_AGC_CTRL_RF (DIRECT_IF_REVB_BASE + 0x00000020)
+/*****************************************************************************/
+#define FLD_DIF_RF_MAX 0xff000000
+#define FLD_DIF_RF_MIN 0x00ff0000
+#define FLD_DIF_RF_AGC 0x0000ffff
+
+/*****************************************************************************/
+#define DIF_AGC_IF_INT_CURRENT (DIRECT_IF_REVB_BASE + 0x00000024)
+/*****************************************************************************/
+#define FLD_DIF_IF_AGC_IN 0xffff0000
+#define FLD_DIF_INT_AGC_IN 0x0000ffff
+
+/*****************************************************************************/
+#define DIF_AGC_RF_CURRENT (DIRECT_IF_REVB_BASE + 0x00000028)
+/*****************************************************************************/
+/* Reserved [31:16] */
+#define FLD_DIF_RF_AGC_IN 0x0000ffff
+
+/*****************************************************************************/
+#define DIF_VIDEO_AGC_CTRL (DIRECT_IF_REVB_BASE + 0x0000002c)
+/*****************************************************************************/
+#define FLD_DIF_AFD 0xc0000000
+#define FLD_DIF_K_VID_AGC 0x30000000
+#define FLD_DIF_LINE_LENGTH 0x0fff0000
+#define FLD_DIF_AGC_GAIN 0x0000ffff
+
+/*****************************************************************************/
+#define DIF_VID_AUD_OVERRIDE (DIRECT_IF_REVB_BASE + 0x00000030)
+/*****************************************************************************/
+#define FLD_DIF_AUDIO_AGC_OVERRIDE 0x80000000
+/* Reserved [30:30] */
+#define FLD_DIF_AUDIO_MAN_GAIN 0x3f000000
+/* Reserved [23:17] */
+#define FLD_DIF_VID_AGC_OVERRIDE 0x00010000
+#define FLD_DIF_VID_MAN_GAIN 0x0000ffff
+
+/*****************************************************************************/
+#define DIF_AV_SEP_CTRL (DIRECT_IF_REVB_BASE + 0x00000034)
+/*****************************************************************************/
+#define FLD_DIF_LPF_FREQ 0xc0000000
+#define FLD_DIF_AV_PHASE_INC 0x3f000000
+#define FLD_DIF_AUDIO_FREQ 0x00ffffff
+
+/*****************************************************************************/
+#define DIF_COMP_FLT_CTRL (DIRECT_IF_REVB_BASE + 0x00000038)
+/*****************************************************************************/
+/* Reserved [31:24] */
+#define FLD_DIF_IIR23_R2 0x00ff0000
+#define FLD_DIF_IIR23_R1 0x0000ff00
+#define FLD_DIF_IIR1_R1 0x000000ff
+
+/*****************************************************************************/
+#define DIF_MISC_CTRL (DIRECT_IF_REVB_BASE + 0x0000003c)
+/*****************************************************************************/
+#define FLD_DIF_DIF_BYPASS 0x80000000
+#define FLD_DIF_FM_NYQ_GAIN 0x40000000
+#define FLD_DIF_RF_AGC_ENA 0x20000000
+#define FLD_DIF_INT_AGC_ENA 0x10000000
+#define FLD_DIF_IF_AGC_ENA 0x08000000
+#define FLD_DIF_FORCE_RF_IF_LOCK 0x04000000
+#define FLD_DIF_VIDEO_AGC_ENA 0x02000000
+#define FLD_DIF_RF_AGC_INV 0x01000000
+#define FLD_DIF_INT_AGC_INV 0x00800000
+#define FLD_DIF_IF_AGC_INV 0x00400000
+#define FLD_DIF_SPEC_INV 0x00200000
+#define FLD_DIF_AUD_FULL_BW 0x00100000
+#define FLD_DIF_AUD_SRC_SEL 0x00080000
+/* Reserved [18] */
+#define FLD_DIF_IF_FREQ 0x00030000
+/* Reserved [15:14] */
+#define FLD_DIF_TIP_OFFSET 0x00003f00
+/* Reserved [7:5] */
+#define FLD_DIF_DITHER_ENA 0x00000010
+/* Reserved [3:1] */
+#define FLD_DIF_RF_IF_LOCK 0x00000001
+
+/*****************************************************************************/
+#define DIF_SRC_PHASE_INC (DIRECT_IF_REVB_BASE + 0x00000040)
+/*****************************************************************************/
+/* Reserved [31:29] */
+#define FLD_DIF_PHASE_INC 0x1fffffff
+
+/*****************************************************************************/
+#define DIF_SRC_GAIN_CONTROL (DIRECT_IF_REVB_BASE + 0x00000044)
+/*****************************************************************************/
+/* Reserved [31:16] */
+#define FLD_DIF_SRC_KI 0x0000ff00
+#define FLD_DIF_SRC_KD 0x000000ff
+
+/*****************************************************************************/
+#define DIF_BPF_COEFF01 (DIRECT_IF_REVB_BASE + 0x00000048)
+/*****************************************************************************/
+/* Reserved [31:19] */
+#define FLD_DIF_BPF_COEFF_0 0x00070000
+/* Reserved [15:4] */
+#define FLD_DIF_BPF_COEFF_1 0x0000000f
+
+/*****************************************************************************/
+#define DIF_BPF_COEFF23 (DIRECT_IF_REVB_BASE + 0x0000004c)
+/*****************************************************************************/
+/* Reserved [31:22] */
+#define FLD_DIF_BPF_COEFF_2 0x003f0000
+/* Reserved [15:7] */
+#define FLD_DIF_BPF_COEFF_3 0x0000007f
+
+/*****************************************************************************/
+#define DIF_BPF_COEFF45 (DIRECT_IF_REVB_BASE + 0x00000050)
+/*****************************************************************************/
+/* Reserved [31:24] */
+#define FLD_DIF_BPF_COEFF_4 0x00ff0000
+/* Reserved [15:8] */
+#define FLD_DIF_BPF_COEFF_5 0x000000ff
+
+/*****************************************************************************/
+#define DIF_BPF_COEFF67 (DIRECT_IF_REVB_BASE + 0x00000054)
+/*****************************************************************************/
+/* Reserved [31:25] */
+#define FLD_DIF_BPF_COEFF_6 0x01ff0000
+/* Reserved [15:9] */
+#define FLD_DIF_BPF_COEFF_7 0x000001ff
+
+/*****************************************************************************/
+#define DIF_BPF_COEFF89 (DIRECT_IF_REVB_BASE + 0x00000058)
+/*****************************************************************************/
+/* Reserved [31:26] */
+#define FLD_DIF_BPF_COEFF_8 0x03ff0000
+/* Reserved [15:10] */
+#define FLD_DIF_BPF_COEFF_9 0x000003ff
+
+/*****************************************************************************/
+#define DIF_BPF_COEFF1011 (DIRECT_IF_REVB_BASE + 0x0000005c)
+/*****************************************************************************/
+/* Reserved [31:27] */
+#define FLD_DIF_BPF_COEFF_10 0x07ff0000
+/* Reserved [15:11] */
+#define FLD_DIF_BPF_COEFF_11 0x000007ff
+
+/*****************************************************************************/
+#define DIF_BPF_COEFF1213 (DIRECT_IF_REVB_BASE + 0x00000060)
+/*****************************************************************************/
+/* Reserved [31:27] */
+#define FLD_DIF_BPF_COEFF_12 0x07ff0000
+/* Reserved [15:12] */
+#define FLD_DIF_BPF_COEFF_13 0x00000fff
+
+/*****************************************************************************/
+#define DIF_BPF_COEFF1415 (DIRECT_IF_REVB_BASE + 0x00000064)
+/*****************************************************************************/
+/* Reserved [31:28] */
+#define FLD_DIF_BPF_COEFF_14 0x0fff0000
+/* Reserved [15:12] */
+#define FLD_DIF_BPF_COEFF_15 0x00000fff
+
+/*****************************************************************************/
+#define DIF_BPF_COEFF1617 (DIRECT_IF_REVB_BASE + 0x00000068)
+/*****************************************************************************/
+/* Reserved [31:29] */
+#define FLD_DIF_BPF_COEFF_16 0x1fff0000
+/* Reserved [15:13] */
+#define FLD_DIF_BPF_COEFF_17 0x00001fff
+
+/*****************************************************************************/
+#define DIF_BPF_COEFF1819 (DIRECT_IF_REVB_BASE + 0x0000006c)
+/*****************************************************************************/
+/* Reserved [31:29] */
+#define FLD_DIF_BPF_COEFF_18 0x1fff0000
+/* Reserved [15:13] */
+#define FLD_DIF_BPF_COEFF_19 0x00001fff
+
+/*****************************************************************************/
+#define DIF_BPF_COEFF2021 (DIRECT_IF_REVB_BASE + 0x00000070)
+/*****************************************************************************/
+/* Reserved [31:29] */
+#define FLD_DIF_BPF_COEFF_20 0x1fff0000
+/* Reserved [15:14] */
+#define FLD_DIF_BPF_COEFF_21 0x00003fff
+
+/*****************************************************************************/
+#define DIF_BPF_COEFF2223 (DIRECT_IF_REVB_BASE + 0x00000074)
+/*****************************************************************************/
+/* Reserved [31:30] */
+#define FLD_DIF_BPF_COEFF_22 0x3fff0000
+/* Reserved [15:14] */
+#define FLD_DIF_BPF_COEFF_23 0x00003fff
+
+/*****************************************************************************/
+#define DIF_BPF_COEFF2425 (DIRECT_IF_REVB_BASE + 0x00000078)
+/*****************************************************************************/
+/* Reserved [31:30] */
+#define FLD_DIF_BPF_COEFF_24 0x3fff0000
+/* Reserved [15:14] */
+#define FLD_DIF_BPF_COEFF_25 0x00003fff
+
+/*****************************************************************************/
+#define DIF_BPF_COEFF2627 (DIRECT_IF_REVB_BASE + 0x0000007c)
+/*****************************************************************************/
+/* Reserved [31:30] */
+#define FLD_DIF_BPF_COEFF_26 0x3fff0000
+/* Reserved [15:14] */
+#define FLD_DIF_BPF_COEFF_27 0x00003fff
+
+/*****************************************************************************/
+#define DIF_BPF_COEFF2829 (DIRECT_IF_REVB_BASE + 0x00000080)
+/*****************************************************************************/
+/* Reserved [31:30] */
+#define FLD_DIF_BPF_COEFF_28 0x3fff0000
+/* Reserved [15:14] */
+#define FLD_DIF_BPF_COEFF_29 0x00003fff
+
+/*****************************************************************************/
+#define DIF_BPF_COEFF3031 (DIRECT_IF_REVB_BASE + 0x00000084)
+/*****************************************************************************/
+/* Reserved [31:30] */
+#define FLD_DIF_BPF_COEFF_30 0x3fff0000
+/* Reserved [15:14] */
+#define FLD_DIF_BPF_COEFF_31 0x00003fff
+
+/*****************************************************************************/
+#define DIF_BPF_COEFF3233 (DIRECT_IF_REVB_BASE + 0x00000088)
+/*****************************************************************************/
+/* Reserved [31:30] */
+#define FLD_DIF_BPF_COEFF_32 0x3fff0000
+/* Reserved [15:14] */
+#define FLD_DIF_BPF_COEFF_33 0x00003fff
+
+/*****************************************************************************/
+#define DIF_BPF_COEFF3435 (DIRECT_IF_REVB_BASE + 0x0000008c)
+/*****************************************************************************/
+/* Reserved [31:30] */
+#define FLD_DIF_BPF_COEFF_34 0x3fff0000
+/* Reserved [15:14] */
+#define FLD_DIF_BPF_COEFF_35 0x00003fff
+
+/*****************************************************************************/
+#define DIF_BPF_COEFF36 (DIRECT_IF_REVB_BASE + 0x00000090)
+/*****************************************************************************/
+/* Reserved [31:30] */
+#define FLD_DIF_BPF_COEFF_36 0x3fff0000
+/* Reserved [15:0] */
+
+/*****************************************************************************/
+#define DIF_RPT_VARIANCE (DIRECT_IF_REVB_BASE + 0x00000094)
+/*****************************************************************************/
+/* Reserved [31:20] */
+#define FLD_DIF_RPT_VARIANCE 0x000fffff
+
+/*****************************************************************************/
+#define DIF_SOFT_RST_CTRL_REVB (DIRECT_IF_REVB_BASE + 0x00000098)
+/*****************************************************************************/
+/* Reserved [31:8] */
+#define FLD_DIF_DIF_SOFT_RST 0x00000080
+#define FLD_DIF_DIF_REG_RST_MSK 0x00000040
+#define FLD_DIF_AGC_RST_MSK 0x00000020
+#define FLD_DIF_CMP_RST_MSK 0x00000010
+#define FLD_DIF_AVS_RST_MSK 0x00000008
+#define FLD_DIF_NYQ_RST_MSK 0x00000004
+#define FLD_DIF_DIF_SRC_RST_MSK 0x00000002
+#define FLD_DIF_PLL_RST_MSK 0x00000001
+
+/*****************************************************************************/
+#define DIF_PLL_FREQ_ERR (DIRECT_IF_REVB_BASE + 0x0000009c)
+/*****************************************************************************/
+/* Reserved [31:25] */
+#define FLD_DIF_CTL_IP 0x01ffffff
+
+#endif
diff --git a/drivers/media/video/cx231xx/cx231xx-core.c b/drivers/media/video/cx231xx/cx231xx-core.c
new file mode 100644
index 00000000..08dd930f
--- /dev/null
+++ b/drivers/media/video/cx231xx/cx231xx-core.c
@@ -0,0 +1,1742 @@
+/*
+ cx231xx-core.c - driver for Conexant Cx23100/101/102
+ USB video capture devices
+
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
+ Based on em28xx driver
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/vmalloc.h>
+#include <media/v4l2-common.h>
+#include <media/tuner.h>
+
+#include "cx231xx.h"
+#include "cx231xx-reg.h"
+
+/* #define ENABLE_DEBUG_ISOC_FRAMES */
+
+static unsigned int core_debug;
+module_param(core_debug, int, 0644);
+MODULE_PARM_DESC(core_debug, "enable debug messages [core]");
+
+#define cx231xx_coredbg(fmt, arg...) do {\
+ if (core_debug) \
+ printk(KERN_INFO "%s %s :"fmt, \
+ dev->name, __func__ , ##arg); } while (0)
+
+static unsigned int reg_debug;
+module_param(reg_debug, int, 0644);
+MODULE_PARM_DESC(reg_debug, "enable debug messages [URB reg]");
+
+static int alt = CX231XX_PINOUT;
+module_param(alt, int, 0644);
+MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint");
+
+#define cx231xx_isocdbg(fmt, arg...) do {\
+ if (core_debug) \
+ printk(KERN_INFO "%s %s :"fmt, \
+ dev->name, __func__ , ##arg); } while (0)
+
+/*****************************************************************
+* Device control list functions *
+******************************************************************/
+
+LIST_HEAD(cx231xx_devlist);
+static DEFINE_MUTEX(cx231xx_devlist_mutex);
+
+/*
+ * cx231xx_realease_resources()
+ * unregisters the v4l2,i2c and usb devices
+ * called when the device gets disconected or at module unload
+*/
+void cx231xx_remove_from_devlist(struct cx231xx *dev)
+{
+ if (dev == NULL)
+ return;
+ if (dev->udev == NULL)
+ return;
+
+ if (atomic_read(&dev->devlist_count) > 0) {
+ mutex_lock(&cx231xx_devlist_mutex);
+ list_del(&dev->devlist);
+ atomic_dec(&dev->devlist_count);
+ mutex_unlock(&cx231xx_devlist_mutex);
+ }
+};
+
+void cx231xx_add_into_devlist(struct cx231xx *dev)
+{
+ mutex_lock(&cx231xx_devlist_mutex);
+ list_add_tail(&dev->devlist, &cx231xx_devlist);
+ atomic_inc(&dev->devlist_count);
+ mutex_unlock(&cx231xx_devlist_mutex);
+};
+
+static LIST_HEAD(cx231xx_extension_devlist);
+
+int cx231xx_register_extension(struct cx231xx_ops *ops)
+{
+ struct cx231xx *dev = NULL;
+
+ mutex_lock(&cx231xx_devlist_mutex);
+ list_add_tail(&ops->next, &cx231xx_extension_devlist);
+ list_for_each_entry(dev, &cx231xx_devlist, devlist)
+ ops->init(dev);
+
+ printk(KERN_INFO DRIVER_NAME ": %s initialized\n", ops->name);
+ mutex_unlock(&cx231xx_devlist_mutex);
+ return 0;
+}
+EXPORT_SYMBOL(cx231xx_register_extension);
+
+void cx231xx_unregister_extension(struct cx231xx_ops *ops)
+{
+ struct cx231xx *dev = NULL;
+
+ mutex_lock(&cx231xx_devlist_mutex);
+ list_for_each_entry(dev, &cx231xx_devlist, devlist)
+ ops->fini(dev);
+
+
+ printk(KERN_INFO DRIVER_NAME ": %s removed\n", ops->name);
+ list_del(&ops->next);
+ mutex_unlock(&cx231xx_devlist_mutex);
+}
+EXPORT_SYMBOL(cx231xx_unregister_extension);
+
+void cx231xx_init_extension(struct cx231xx *dev)
+{
+ struct cx231xx_ops *ops = NULL;
+
+ mutex_lock(&cx231xx_devlist_mutex);
+ if (!list_empty(&cx231xx_extension_devlist)) {
+ list_for_each_entry(ops, &cx231xx_extension_devlist, next) {
+ if (ops->init)
+ ops->init(dev);
+ }
+ }
+ mutex_unlock(&cx231xx_devlist_mutex);
+}
+
+void cx231xx_close_extension(struct cx231xx *dev)
+{
+ struct cx231xx_ops *ops = NULL;
+
+ mutex_lock(&cx231xx_devlist_mutex);
+ if (!list_empty(&cx231xx_extension_devlist)) {
+ list_for_each_entry(ops, &cx231xx_extension_devlist, next) {
+ if (ops->fini)
+ ops->fini(dev);
+ }
+ }
+ mutex_unlock(&cx231xx_devlist_mutex);
+}
+
+/****************************************************************
+* U S B related functions *
+*****************************************************************/
+int cx231xx_send_usb_command(struct cx231xx_i2c *i2c_bus,
+ struct cx231xx_i2c_xfer_data *req_data)
+{
+ int status = 0;
+ struct cx231xx *dev = i2c_bus->dev;
+ struct VENDOR_REQUEST_IN ven_req;
+
+ u8 saddr_len = 0;
+ u8 _i2c_period = 0;
+ u8 _i2c_nostop = 0;
+ u8 _i2c_reserve = 0;
+
+ if (dev->state & DEV_DISCONNECTED)
+ return -ENODEV;
+
+ /* Get the I2C period, nostop and reserve parameters */
+ _i2c_period = i2c_bus->i2c_period;
+ _i2c_nostop = i2c_bus->i2c_nostop;
+ _i2c_reserve = i2c_bus->i2c_reserve;
+
+ saddr_len = req_data->saddr_len;
+
+ /* Set wValue */
+ if (saddr_len == 1) /* need check saddr_len == 0 */
+ ven_req.wValue =
+ req_data->
+ dev_addr << 9 | _i2c_period << 4 | saddr_len << 2 |
+ _i2c_nostop << 1 | I2C_SYNC | _i2c_reserve << 6;
+ else
+ ven_req.wValue =
+ req_data->
+ dev_addr << 9 | _i2c_period << 4 | saddr_len << 2 |
+ _i2c_nostop << 1 | I2C_SYNC | _i2c_reserve << 6;
+
+ /* set channel number */
+ if (req_data->direction & I2C_M_RD) {
+ /* channel number, for read,spec required channel_num +4 */
+ ven_req.bRequest = i2c_bus->nr + 4;
+ } else
+ ven_req.bRequest = i2c_bus->nr; /* channel number, */
+
+ /* set index value */
+ switch (saddr_len) {
+ case 0:
+ ven_req.wIndex = 0; /* need check */
+ break;
+ case 1:
+ ven_req.wIndex = (req_data->saddr_dat & 0xff);
+ break;
+ case 2:
+ ven_req.wIndex = req_data->saddr_dat;
+ break;
+ }
+
+ /* set wLength value */
+ ven_req.wLength = req_data->buf_size;
+
+ /* set bData value */
+ ven_req.bData = 0;
+
+ /* set the direction */
+ if (req_data->direction) {
+ ven_req.direction = USB_DIR_IN;
+ memset(req_data->p_buffer, 0x00, ven_req.wLength);
+ } else
+ ven_req.direction = USB_DIR_OUT;
+
+ /* set the buffer for read / write */
+ ven_req.pBuff = req_data->p_buffer;
+
+
+ /* call common vendor command request */
+ status = cx231xx_send_vendor_cmd(dev, &ven_req);
+ if (status < 0) {
+ cx231xx_info
+ ("UsbInterface::sendCommand, failed with status -%d\n",
+ status);
+ }
+
+ return status;
+}
+EXPORT_SYMBOL_GPL(cx231xx_send_usb_command);
+
+/*
+ * Sends/Receives URB control messages, assuring to use a kalloced buffer
+ * for all operations (dev->urb_buf), to avoid using stacked buffers, as
+ * they aren't safe for usage with USB, due to DMA restrictions.
+ * Also implements the debug code for control URB's.
+ */
+static int __usb_control_msg(struct cx231xx *dev, unsigned int pipe,
+ __u8 request, __u8 requesttype, __u16 value, __u16 index,
+ void *data, __u16 size, int timeout)
+{
+ int rc, i;
+
+ if (reg_debug) {
+ printk(KERN_DEBUG "%s: (pipe 0x%08x): "
+ "%s: %02x %02x %02x %02x %02x %02x %02x %02x ",
+ dev->name,
+ pipe,
+ (requesttype & USB_DIR_IN) ? "IN" : "OUT",
+ requesttype,
+ request,
+ value & 0xff, value >> 8,
+ index & 0xff, index >> 8,
+ size & 0xff, size >> 8);
+ if (!(requesttype & USB_DIR_IN)) {
+ printk(KERN_CONT ">>>");
+ for (i = 0; i < size; i++)
+ printk(KERN_CONT " %02x",
+ ((unsigned char *)data)[i]);
+ }
+ }
+
+ /* Do the real call to usb_control_msg */
+ mutex_lock(&dev->ctrl_urb_lock);
+ if (!(requesttype & USB_DIR_IN) && size)
+ memcpy(dev->urb_buf, data, size);
+ rc = usb_control_msg(dev->udev, pipe, request, requesttype, value,
+ index, dev->urb_buf, size, timeout);
+ if ((requesttype & USB_DIR_IN) && size)
+ memcpy(data, dev->urb_buf, size);
+ mutex_unlock(&dev->ctrl_urb_lock);
+
+ if (reg_debug) {
+ if (unlikely(rc < 0)) {
+ printk(KERN_CONT "FAILED!\n");
+ return rc;
+ }
+
+ if ((requesttype & USB_DIR_IN)) {
+ printk(KERN_CONT "<<<");
+ for (i = 0; i < size; i++)
+ printk(KERN_CONT " %02x",
+ ((unsigned char *)data)[i]);
+ }
+ printk(KERN_CONT "\n");
+ }
+
+ return rc;
+}
+
+
+/*
+ * cx231xx_read_ctrl_reg()
+ * reads data from the usb device specifying bRequest and wValue
+ */
+int cx231xx_read_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg,
+ char *buf, int len)
+{
+ u8 val = 0;
+ int ret;
+ int pipe = usb_rcvctrlpipe(dev->udev, 0);
+
+ if (dev->state & DEV_DISCONNECTED)
+ return -ENODEV;
+
+ if (len > URB_MAX_CTRL_SIZE)
+ return -EINVAL;
+
+ switch (len) {
+ case 1:
+ val = ENABLE_ONE_BYTE;
+ break;
+ case 2:
+ val = ENABLE_TWE_BYTE;
+ break;
+ case 3:
+ val = ENABLE_THREE_BYTE;
+ break;
+ case 4:
+ val = ENABLE_FOUR_BYTE;
+ break;
+ default:
+ val = 0xFF; /* invalid option */
+ }
+
+ if (val == 0xFF)
+ return -EINVAL;
+
+ ret = __usb_control_msg(dev, pipe, req,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ val, reg, buf, len, HZ);
+ return ret;
+}
+
+int cx231xx_send_vendor_cmd(struct cx231xx *dev,
+ struct VENDOR_REQUEST_IN *ven_req)
+{
+ int ret;
+ int pipe = 0;
+ int unsend_size = 0;
+ u8 *pdata;
+
+ if (dev->state & DEV_DISCONNECTED)
+ return -ENODEV;
+
+ if ((ven_req->wLength > URB_MAX_CTRL_SIZE))
+ return -EINVAL;
+
+ if (ven_req->direction)
+ pipe = usb_rcvctrlpipe(dev->udev, 0);
+ else
+ pipe = usb_sndctrlpipe(dev->udev, 0);
+
+ /*
+ * If the cx23102 read more than 4 bytes with i2c bus,
+ * need chop to 4 byte per request
+ */
+ if ((ven_req->wLength > 4) && ((ven_req->bRequest == 0x4) ||
+ (ven_req->bRequest == 0x5) ||
+ (ven_req->bRequest == 0x6))) {
+ unsend_size = 0;
+ pdata = ven_req->pBuff;
+
+
+ unsend_size = ven_req->wLength;
+
+ /* the first package */
+ ven_req->wValue = ven_req->wValue & 0xFFFB;
+ ven_req->wValue = (ven_req->wValue & 0xFFBD) | 0x2;
+ ret = __usb_control_msg(dev, pipe, ven_req->bRequest,
+ ven_req->direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ ven_req->wValue, ven_req->wIndex, pdata,
+ 0x0004, HZ);
+ unsend_size = unsend_size - 4;
+
+ /* the middle package */
+ ven_req->wValue = (ven_req->wValue & 0xFFBD) | 0x42;
+ while (unsend_size - 4 > 0) {
+ pdata = pdata + 4;
+ ret = __usb_control_msg(dev, pipe,
+ ven_req->bRequest,
+ ven_req->direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ ven_req->wValue, ven_req->wIndex, pdata,
+ 0x0004, HZ);
+ unsend_size = unsend_size - 4;
+ }
+
+ /* the last package */
+ ven_req->wValue = (ven_req->wValue & 0xFFBD) | 0x40;
+ pdata = pdata + 4;
+ ret = __usb_control_msg(dev, pipe, ven_req->bRequest,
+ ven_req->direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ ven_req->wValue, ven_req->wIndex, pdata,
+ unsend_size, HZ);
+ } else {
+ ret = __usb_control_msg(dev, pipe, ven_req->bRequest,
+ ven_req->direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ ven_req->wValue, ven_req->wIndex,
+ ven_req->pBuff, ven_req->wLength, HZ);
+ }
+
+ return ret;
+}
+
+/*
+ * cx231xx_write_ctrl_reg()
+ * sends data to the usb device, specifying bRequest
+ */
+int cx231xx_write_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg, char *buf,
+ int len)
+{
+ u8 val = 0;
+ int ret;
+ int pipe = usb_sndctrlpipe(dev->udev, 0);
+
+ if (dev->state & DEV_DISCONNECTED)
+ return -ENODEV;
+
+ if ((len < 1) || (len > URB_MAX_CTRL_SIZE))
+ return -EINVAL;
+
+ switch (len) {
+ case 1:
+ val = ENABLE_ONE_BYTE;
+ break;
+ case 2:
+ val = ENABLE_TWE_BYTE;
+ break;
+ case 3:
+ val = ENABLE_THREE_BYTE;
+ break;
+ case 4:
+ val = ENABLE_FOUR_BYTE;
+ break;
+ default:
+ val = 0xFF; /* invalid option */
+ }
+
+ if (val == 0xFF)
+ return -EINVAL;
+
+ if (reg_debug) {
+ int byte;
+
+ cx231xx_isocdbg("(pipe 0x%08x): "
+ "OUT: %02x %02x %02x %02x %02x %02x %02x %02x >>>",
+ pipe,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ req, 0, val, reg & 0xff,
+ reg >> 8, len & 0xff, len >> 8);
+
+ for (byte = 0; byte < len; byte++)
+ cx231xx_isocdbg(" %02x", (unsigned char)buf[byte]);
+ cx231xx_isocdbg("\n");
+ }
+
+ ret = __usb_control_msg(dev, pipe, req,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ val, reg, buf, len, HZ);
+
+ return ret;
+}
+
+/****************************************************************
+* USB Alternate Setting functions *
+*****************************************************************/
+
+int cx231xx_set_video_alternate(struct cx231xx *dev)
+{
+ int errCode, prev_alt = dev->video_mode.alt;
+ unsigned int min_pkt_size = dev->width * 2 + 4;
+ u32 usb_interface_index = 0;
+
+ /* When image size is bigger than a certain value,
+ the frame size should be increased, otherwise, only
+ green screen will be received.
+ */
+ if (dev->width * 2 * dev->height > 720 * 240 * 2)
+ min_pkt_size *= 2;
+
+ if (dev->width > 360) {
+ /* resolutions: 720,704,640 */
+ dev->video_mode.alt = 3;
+ } else if (dev->width > 180) {
+ /* resolutions: 360,352,320,240 */
+ dev->video_mode.alt = 2;
+ } else if (dev->width > 0) {
+ /* resolutions: 180,176,160,128,88 */
+ dev->video_mode.alt = 1;
+ } else {
+ /* Change to alt0 BULK to release USB bandwidth */
+ dev->video_mode.alt = 0;
+ }
+
+ if (dev->USE_ISO == 0)
+ dev->video_mode.alt = 0;
+
+ cx231xx_coredbg("dev->video_mode.alt= %d\n", dev->video_mode.alt);
+
+ /* Get the correct video interface Index */
+ usb_interface_index =
+ dev->current_pcb_config.hs_config_info[0].interface_info.
+ video_index + 1;
+
+ if (dev->video_mode.alt != prev_alt) {
+ cx231xx_coredbg("minimum isoc packet size: %u (alt=%d)\n",
+ min_pkt_size, dev->video_mode.alt);
+
+ if (dev->video_mode.alt_max_pkt_size != NULL)
+ dev->video_mode.max_pkt_size =
+ dev->video_mode.alt_max_pkt_size[dev->video_mode.alt];
+ cx231xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n",
+ dev->video_mode.alt,
+ dev->video_mode.max_pkt_size);
+ errCode =
+ usb_set_interface(dev->udev, usb_interface_index,
+ dev->video_mode.alt);
+ if (errCode < 0) {
+ cx231xx_errdev
+ ("cannot change alt number to %d (error=%i)\n",
+ dev->video_mode.alt, errCode);
+ return errCode;
+ }
+ }
+ return 0;
+}
+
+int cx231xx_set_alt_setting(struct cx231xx *dev, u8 index, u8 alt)
+{
+ int status = 0;
+ u32 usb_interface_index = 0;
+ u32 max_pkt_size = 0;
+
+ switch (index) {
+ case INDEX_TS1:
+ usb_interface_index =
+ dev->current_pcb_config.hs_config_info[0].interface_info.
+ ts1_index + 1;
+ dev->ts1_mode.alt = alt;
+ if (dev->ts1_mode.alt_max_pkt_size != NULL)
+ max_pkt_size = dev->ts1_mode.max_pkt_size =
+ dev->ts1_mode.alt_max_pkt_size[dev->ts1_mode.alt];
+ break;
+ case INDEX_TS2:
+ usb_interface_index =
+ dev->current_pcb_config.hs_config_info[0].interface_info.
+ ts2_index + 1;
+ break;
+ case INDEX_AUDIO:
+ usb_interface_index =
+ dev->current_pcb_config.hs_config_info[0].interface_info.
+ audio_index + 1;
+ dev->adev.alt = alt;
+ if (dev->adev.alt_max_pkt_size != NULL)
+ max_pkt_size = dev->adev.max_pkt_size =
+ dev->adev.alt_max_pkt_size[dev->adev.alt];
+ break;
+ case INDEX_VIDEO:
+ usb_interface_index =
+ dev->current_pcb_config.hs_config_info[0].interface_info.
+ video_index + 1;
+ dev->video_mode.alt = alt;
+ if (dev->video_mode.alt_max_pkt_size != NULL)
+ max_pkt_size = dev->video_mode.max_pkt_size =
+ dev->video_mode.alt_max_pkt_size[dev->video_mode.
+ alt];
+ break;
+ case INDEX_VANC:
+ if (dev->board.no_alt_vanc)
+ return 0;
+ usb_interface_index =
+ dev->current_pcb_config.hs_config_info[0].interface_info.
+ vanc_index + 1;
+ dev->vbi_mode.alt = alt;
+ if (dev->vbi_mode.alt_max_pkt_size != NULL)
+ max_pkt_size = dev->vbi_mode.max_pkt_size =
+ dev->vbi_mode.alt_max_pkt_size[dev->vbi_mode.alt];
+ break;
+ case INDEX_HANC:
+ usb_interface_index =
+ dev->current_pcb_config.hs_config_info[0].interface_info.
+ hanc_index + 1;
+ dev->sliced_cc_mode.alt = alt;
+ if (dev->sliced_cc_mode.alt_max_pkt_size != NULL)
+ max_pkt_size = dev->sliced_cc_mode.max_pkt_size =
+ dev->sliced_cc_mode.alt_max_pkt_size[dev->
+ sliced_cc_mode.
+ alt];
+ break;
+ default:
+ break;
+ }
+
+ if (alt > 0 && max_pkt_size == 0) {
+ cx231xx_errdev
+ ("can't change interface %d alt no. to %d: Max. Pkt size = 0\n",
+ usb_interface_index, alt);
+ /*To workaround error number=-71 on EP0 for videograbber,
+ need add following codes.*/
+ if (dev->board.no_alt_vanc)
+ return -1;
+ }
+
+ cx231xx_coredbg("setting alternate %d with wMaxPacketSize=%u,"
+ "Interface = %d\n", alt, max_pkt_size,
+ usb_interface_index);
+
+ if (usb_interface_index > 0) {
+ status = usb_set_interface(dev->udev, usb_interface_index, alt);
+ if (status < 0) {
+ cx231xx_errdev
+ ("can't change interface %d alt no. to %d (err=%i)\n",
+ usb_interface_index, alt, status);
+ return status;
+ }
+ }
+
+ return status;
+}
+EXPORT_SYMBOL_GPL(cx231xx_set_alt_setting);
+
+int cx231xx_gpio_set(struct cx231xx *dev, struct cx231xx_reg_seq *gpio)
+{
+ int rc = 0;
+
+ if (!gpio)
+ return rc;
+
+ /* Send GPIO reset sequences specified at board entry */
+ while (gpio->sleep >= 0) {
+ rc = cx231xx_set_gpio_value(dev, gpio->bit, gpio->val);
+ if (rc < 0)
+ return rc;
+
+ if (gpio->sleep > 0)
+ msleep(gpio->sleep);
+
+ gpio++;
+ }
+ return rc;
+}
+
+int cx231xx_demod_reset(struct cx231xx *dev)
+{
+
+ u8 status = 0;
+ u8 value[4] = { 0, 0, 0, 0 };
+
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN,
+ value, 4);
+
+ cx231xx_coredbg("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", PWR_CTL_EN,
+ value[0], value[1], value[2], value[3]);
+
+ cx231xx_coredbg("Enter cx231xx_demod_reset()\n");
+
+ value[1] = (u8) 0x3;
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ PWR_CTL_EN, value, 4);
+ msleep(10);
+
+ value[1] = (u8) 0x0;
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ PWR_CTL_EN, value, 4);
+ msleep(10);
+
+ value[1] = (u8) 0x3;
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ PWR_CTL_EN, value, 4);
+ msleep(10);
+
+
+
+ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN,
+ value, 4);
+
+ cx231xx_coredbg("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", PWR_CTL_EN,
+ value[0], value[1], value[2], value[3]);
+
+ return status;
+}
+EXPORT_SYMBOL_GPL(cx231xx_demod_reset);
+int is_fw_load(struct cx231xx *dev)
+{
+ return cx231xx_check_fw(dev);
+}
+EXPORT_SYMBOL_GPL(is_fw_load);
+
+int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode)
+{
+ int errCode = 0;
+
+ if (dev->mode == set_mode)
+ return 0;
+
+ if (set_mode == CX231XX_SUSPEND) {
+ /* Set the chip in power saving mode */
+ dev->mode = set_mode;
+ }
+
+ /* Resource is locked */
+ if (dev->mode != CX231XX_SUSPEND)
+ return -EINVAL;
+
+ dev->mode = set_mode;
+
+ if (dev->mode == CX231XX_DIGITAL_MODE)/* Set Digital power mode */ {
+ /* set AGC mode to Digital */
+ switch (dev->model) {
+ case CX231XX_BOARD_CNXT_CARRAERA:
+ case CX231XX_BOARD_CNXT_RDE_250:
+ case CX231XX_BOARD_CNXT_SHELBY:
+ case CX231XX_BOARD_CNXT_RDU_250:
+ errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 0);
+ break;
+ case CX231XX_BOARD_CNXT_RDE_253S:
+ case CX231XX_BOARD_CNXT_RDU_253S:
+ errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 1);
+ break;
+ case CX231XX_BOARD_HAUPPAUGE_EXETER:
+ errCode = cx231xx_set_power_mode(dev,
+ POLARIS_AVMODE_DIGITAL);
+ break;
+ default:
+ break;
+ }
+ } else/* Set Analog Power mode */ {
+ /* set AGC mode to Analog */
+ switch (dev->model) {
+ case CX231XX_BOARD_CNXT_CARRAERA:
+ case CX231XX_BOARD_CNXT_RDE_250:
+ case CX231XX_BOARD_CNXT_SHELBY:
+ case CX231XX_BOARD_CNXT_RDU_250:
+ errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 1);
+ break;
+ case CX231XX_BOARD_CNXT_RDE_253S:
+ case CX231XX_BOARD_CNXT_RDU_253S:
+ case CX231XX_BOARD_HAUPPAUGE_EXETER:
+ case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID:
+ case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL:
+ case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC:
+ errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 0);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cx231xx_set_mode);
+
+int cx231xx_ep5_bulkout(struct cx231xx *dev, u8 *firmware, u16 size)
+{
+ int errCode = 0;
+ int actlen, ret = -ENOMEM;
+ u32 *buffer;
+
+buffer = kzalloc(4096, GFP_KERNEL);
+ if (buffer == NULL) {
+ cx231xx_info("out of mem\n");
+ return -ENOMEM;
+ }
+ memcpy(&buffer[0], firmware, 4096);
+
+ ret = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 5),
+ buffer, 4096, &actlen, 2000);
+
+ if (ret)
+ cx231xx_info("bulk message failed: %d (%d/%d)", ret,
+ size, actlen);
+ else {
+ errCode = actlen != size ? -1 : 0;
+ }
+kfree(buffer);
+ return 0;
+}
+
+/*****************************************************************
+* URB Streaming functions *
+******************************************************************/
+
+/*
+ * IRQ callback, called by URB callback
+ */
+static void cx231xx_isoc_irq_callback(struct urb *urb)
+{
+ struct cx231xx_dmaqueue *dma_q = urb->context;
+ struct cx231xx_video_mode *vmode =
+ container_of(dma_q, struct cx231xx_video_mode, vidq);
+ struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode);
+ int rc, i;
+
+ switch (urb->status) {
+ case 0: /* success */
+ case -ETIMEDOUT: /* NAK */
+ break;
+ case -ECONNRESET: /* kill */
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ default: /* error */
+ cx231xx_isocdbg("urb completition error %d.\n", urb->status);
+ break;
+ }
+
+ /* Copy data from URB */
+ spin_lock(&dev->video_mode.slock);
+ rc = dev->video_mode.isoc_ctl.isoc_copy(dev, urb);
+ spin_unlock(&dev->video_mode.slock);
+
+ /* Reset urb buffers */
+ for (i = 0; i < urb->number_of_packets; i++) {
+ urb->iso_frame_desc[i].status = 0;
+ urb->iso_frame_desc[i].actual_length = 0;
+ }
+ urb->status = 0;
+
+ urb->status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (urb->status) {
+ cx231xx_isocdbg("urb resubmit failed (error=%i)\n",
+ urb->status);
+ }
+}
+/*****************************************************************
+* URB Streaming functions *
+******************************************************************/
+
+/*
+ * IRQ callback, called by URB callback
+ */
+static void cx231xx_bulk_irq_callback(struct urb *urb)
+{
+ struct cx231xx_dmaqueue *dma_q = urb->context;
+ struct cx231xx_video_mode *vmode =
+ container_of(dma_q, struct cx231xx_video_mode, vidq);
+ struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode);
+ int rc;
+
+ switch (urb->status) {
+ case 0: /* success */
+ case -ETIMEDOUT: /* NAK */
+ break;
+ case -ECONNRESET: /* kill */
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ default: /* error */
+ cx231xx_isocdbg("urb completition error %d.\n", urb->status);
+ break;
+ }
+
+ /* Copy data from URB */
+ spin_lock(&dev->video_mode.slock);
+ rc = dev->video_mode.bulk_ctl.bulk_copy(dev, urb);
+ spin_unlock(&dev->video_mode.slock);
+
+ /* Reset urb buffers */
+ urb->status = 0;
+
+ urb->status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (urb->status) {
+ cx231xx_isocdbg("urb resubmit failed (error=%i)\n",
+ urb->status);
+ }
+}
+/*
+ * Stop and Deallocate URBs
+ */
+void cx231xx_uninit_isoc(struct cx231xx *dev)
+{
+ struct cx231xx_dmaqueue *dma_q = &dev->video_mode.vidq;
+ struct urb *urb;
+ int i;
+
+ cx231xx_isocdbg("cx231xx: called cx231xx_uninit_isoc\n");
+
+ dev->video_mode.isoc_ctl.nfields = -1;
+ for (i = 0; i < dev->video_mode.isoc_ctl.num_bufs; i++) {
+ urb = dev->video_mode.isoc_ctl.urb[i];
+ if (urb) {
+ if (!irqs_disabled())
+ usb_kill_urb(urb);
+ else
+ usb_unlink_urb(urb);
+
+ if (dev->video_mode.isoc_ctl.transfer_buffer[i]) {
+ usb_free_coherent(dev->udev,
+ urb->transfer_buffer_length,
+ dev->video_mode.isoc_ctl.
+ transfer_buffer[i],
+ urb->transfer_dma);
+ }
+ usb_free_urb(urb);
+ dev->video_mode.isoc_ctl.urb[i] = NULL;
+ }
+ dev->video_mode.isoc_ctl.transfer_buffer[i] = NULL;
+ }
+
+ kfree(dev->video_mode.isoc_ctl.urb);
+ kfree(dev->video_mode.isoc_ctl.transfer_buffer);
+ kfree(dma_q->p_left_data);
+
+ dev->video_mode.isoc_ctl.urb = NULL;
+ dev->video_mode.isoc_ctl.transfer_buffer = NULL;
+ dev->video_mode.isoc_ctl.num_bufs = 0;
+ dma_q->p_left_data = NULL;
+
+ if (dev->mode_tv == 0)
+ cx231xx_capture_start(dev, 0, Raw_Video);
+ else
+ cx231xx_capture_start(dev, 0, TS1_serial_mode);
+
+
+}
+EXPORT_SYMBOL_GPL(cx231xx_uninit_isoc);
+
+/*
+ * Stop and Deallocate URBs
+ */
+void cx231xx_uninit_bulk(struct cx231xx *dev)
+{
+ struct urb *urb;
+ int i;
+
+ cx231xx_isocdbg("cx231xx: called cx231xx_uninit_bulk\n");
+
+ dev->video_mode.bulk_ctl.nfields = -1;
+ for (i = 0; i < dev->video_mode.bulk_ctl.num_bufs; i++) {
+ urb = dev->video_mode.bulk_ctl.urb[i];
+ if (urb) {
+ if (!irqs_disabled())
+ usb_kill_urb(urb);
+ else
+ usb_unlink_urb(urb);
+
+ if (dev->video_mode.bulk_ctl.transfer_buffer[i]) {
+ usb_free_coherent(dev->udev,
+ urb->transfer_buffer_length,
+ dev->video_mode.isoc_ctl.
+ transfer_buffer[i],
+ urb->transfer_dma);
+ }
+ usb_free_urb(urb);
+ dev->video_mode.bulk_ctl.urb[i] = NULL;
+ }
+ dev->video_mode.bulk_ctl.transfer_buffer[i] = NULL;
+ }
+
+ kfree(dev->video_mode.bulk_ctl.urb);
+ kfree(dev->video_mode.bulk_ctl.transfer_buffer);
+
+ dev->video_mode.bulk_ctl.urb = NULL;
+ dev->video_mode.bulk_ctl.transfer_buffer = NULL;
+ dev->video_mode.bulk_ctl.num_bufs = 0;
+
+ if (dev->mode_tv == 0)
+ cx231xx_capture_start(dev, 0, Raw_Video);
+ else
+ cx231xx_capture_start(dev, 0, TS1_serial_mode);
+
+
+}
+EXPORT_SYMBOL_GPL(cx231xx_uninit_bulk);
+
+/*
+ * Allocate URBs and start IRQ
+ */
+int cx231xx_init_isoc(struct cx231xx *dev, int max_packets,
+ int num_bufs, int max_pkt_size,
+ int (*isoc_copy) (struct cx231xx *dev, struct urb *urb))
+{
+ struct cx231xx_dmaqueue *dma_q = &dev->video_mode.vidq;
+ int i;
+ int sb_size, pipe;
+ struct urb *urb;
+ int j, k;
+ int rc;
+
+ /* De-allocates all pending stuff */
+ cx231xx_uninit_isoc(dev);
+
+ dma_q->p_left_data = kzalloc(4096, GFP_KERNEL);
+ if (dma_q->p_left_data == NULL) {
+ cx231xx_info("out of mem\n");
+ return -ENOMEM;
+ }
+
+
+
+ dev->video_mode.isoc_ctl.isoc_copy = isoc_copy;
+ dev->video_mode.isoc_ctl.num_bufs = num_bufs;
+ dma_q->pos = 0;
+ dma_q->is_partial_line = 0;
+ dma_q->last_sav = 0;
+ dma_q->current_field = -1;
+ dma_q->field1_done = 0;
+ dma_q->lines_per_field = dev->height / 2;
+ dma_q->bytes_left_in_line = dev->width << 1;
+ dma_q->lines_completed = 0;
+ dma_q->mpeg_buffer_done = 0;
+ dma_q->left_data_count = 0;
+ dma_q->mpeg_buffer_completed = 0;
+ dma_q->add_ps_package_head = CX231XX_NEED_ADD_PS_PACKAGE_HEAD;
+ dma_q->ps_head[0] = 0x00;
+ dma_q->ps_head[1] = 0x00;
+ dma_q->ps_head[2] = 0x01;
+ dma_q->ps_head[3] = 0xBA;
+ for (i = 0; i < 8; i++)
+ dma_q->partial_buf[i] = 0;
+
+ dev->video_mode.isoc_ctl.urb =
+ kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
+ if (!dev->video_mode.isoc_ctl.urb) {
+ cx231xx_errdev("cannot alloc memory for usb buffers\n");
+ return -ENOMEM;
+ }
+
+ dev->video_mode.isoc_ctl.transfer_buffer =
+ kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
+ if (!dev->video_mode.isoc_ctl.transfer_buffer) {
+ cx231xx_errdev("cannot allocate memory for usbtransfer\n");
+ kfree(dev->video_mode.isoc_ctl.urb);
+ return -ENOMEM;
+ }
+
+ dev->video_mode.isoc_ctl.max_pkt_size = max_pkt_size;
+ dev->video_mode.isoc_ctl.buf = NULL;
+
+ sb_size = max_packets * dev->video_mode.isoc_ctl.max_pkt_size;
+
+ if (dev->mode_tv == 1)
+ dev->video_mode.end_point_addr = 0x81;
+ else
+ dev->video_mode.end_point_addr = 0x84;
+
+
+ /* allocate urbs and transfer buffers */
+ for (i = 0; i < dev->video_mode.isoc_ctl.num_bufs; i++) {
+ urb = usb_alloc_urb(max_packets, GFP_KERNEL);
+ if (!urb) {
+ cx231xx_err("cannot alloc isoc_ctl.urb %i\n", i);
+ cx231xx_uninit_isoc(dev);
+ return -ENOMEM;
+ }
+ dev->video_mode.isoc_ctl.urb[i] = urb;
+
+ dev->video_mode.isoc_ctl.transfer_buffer[i] =
+ usb_alloc_coherent(dev->udev, sb_size, GFP_KERNEL,
+ &urb->transfer_dma);
+ if (!dev->video_mode.isoc_ctl.transfer_buffer[i]) {
+ cx231xx_err("unable to allocate %i bytes for transfer"
+ " buffer %i%s\n",
+ sb_size, i,
+ in_interrupt() ? " while in int" : "");
+ cx231xx_uninit_isoc(dev);
+ return -ENOMEM;
+ }
+ memset(dev->video_mode.isoc_ctl.transfer_buffer[i], 0, sb_size);
+
+ pipe =
+ usb_rcvisocpipe(dev->udev, dev->video_mode.end_point_addr);
+
+ usb_fill_int_urb(urb, dev->udev, pipe,
+ dev->video_mode.isoc_ctl.transfer_buffer[i],
+ sb_size, cx231xx_isoc_irq_callback, dma_q, 1);
+
+ urb->number_of_packets = max_packets;
+ urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+
+ k = 0;
+ for (j = 0; j < max_packets; j++) {
+ urb->iso_frame_desc[j].offset = k;
+ urb->iso_frame_desc[j].length =
+ dev->video_mode.isoc_ctl.max_pkt_size;
+ k += dev->video_mode.isoc_ctl.max_pkt_size;
+ }
+ }
+
+ init_waitqueue_head(&dma_q->wq);
+
+ /* submit urbs and enables IRQ */
+ for (i = 0; i < dev->video_mode.isoc_ctl.num_bufs; i++) {
+ rc = usb_submit_urb(dev->video_mode.isoc_ctl.urb[i],
+ GFP_ATOMIC);
+ if (rc) {
+ cx231xx_err("submit of urb %i failed (error=%i)\n", i,
+ rc);
+ cx231xx_uninit_isoc(dev);
+ return rc;
+ }
+ }
+
+ if (dev->mode_tv == 0)
+ cx231xx_capture_start(dev, 1, Raw_Video);
+ else
+ cx231xx_capture_start(dev, 1, TS1_serial_mode);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cx231xx_init_isoc);
+
+/*
+ * Allocate URBs and start IRQ
+ */
+int cx231xx_init_bulk(struct cx231xx *dev, int max_packets,
+ int num_bufs, int max_pkt_size,
+ int (*bulk_copy) (struct cx231xx *dev, struct urb *urb))
+{
+ struct cx231xx_dmaqueue *dma_q = &dev->video_mode.vidq;
+ int i;
+ int sb_size, pipe;
+ struct urb *urb;
+ int rc;
+
+ dev->video_input = dev->video_input > 2 ? 2 : dev->video_input;
+
+ cx231xx_coredbg("Setting Video mux to %d\n", dev->video_input);
+
+ video_mux(dev, dev->video_input);
+
+ /* De-allocates all pending stuff */
+ cx231xx_uninit_bulk(dev);
+
+ dev->video_mode.bulk_ctl.bulk_copy = bulk_copy;
+ dev->video_mode.bulk_ctl.num_bufs = num_bufs;
+ dma_q->pos = 0;
+ dma_q->is_partial_line = 0;
+ dma_q->last_sav = 0;
+ dma_q->current_field = -1;
+ dma_q->field1_done = 0;
+ dma_q->lines_per_field = dev->height / 2;
+ dma_q->bytes_left_in_line = dev->width << 1;
+ dma_q->lines_completed = 0;
+ dma_q->mpeg_buffer_done = 0;
+ dma_q->left_data_count = 0;
+ dma_q->mpeg_buffer_completed = 0;
+ dma_q->ps_head[0] = 0x00;
+ dma_q->ps_head[1] = 0x00;
+ dma_q->ps_head[2] = 0x01;
+ dma_q->ps_head[3] = 0xBA;
+ for (i = 0; i < 8; i++)
+ dma_q->partial_buf[i] = 0;
+
+ dev->video_mode.bulk_ctl.urb =
+ kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
+ if (!dev->video_mode.bulk_ctl.urb) {
+ cx231xx_errdev("cannot alloc memory for usb buffers\n");
+ return -ENOMEM;
+ }
+
+ dev->video_mode.bulk_ctl.transfer_buffer =
+ kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
+ if (!dev->video_mode.bulk_ctl.transfer_buffer) {
+ cx231xx_errdev("cannot allocate memory for usbtransfer\n");
+ kfree(dev->video_mode.bulk_ctl.urb);
+ return -ENOMEM;
+ }
+
+ dev->video_mode.bulk_ctl.max_pkt_size = max_pkt_size;
+ dev->video_mode.bulk_ctl.buf = NULL;
+
+ sb_size = max_packets * dev->video_mode.bulk_ctl.max_pkt_size;
+
+ if (dev->mode_tv == 1)
+ dev->video_mode.end_point_addr = 0x81;
+ else
+ dev->video_mode.end_point_addr = 0x84;
+
+
+ /* allocate urbs and transfer buffers */
+ for (i = 0; i < dev->video_mode.bulk_ctl.num_bufs; i++) {
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ cx231xx_err("cannot alloc bulk_ctl.urb %i\n", i);
+ cx231xx_uninit_bulk(dev);
+ return -ENOMEM;
+ }
+ dev->video_mode.bulk_ctl.urb[i] = urb;
+ urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+
+ dev->video_mode.bulk_ctl.transfer_buffer[i] =
+ usb_alloc_coherent(dev->udev, sb_size, GFP_KERNEL,
+ &urb->transfer_dma);
+ if (!dev->video_mode.bulk_ctl.transfer_buffer[i]) {
+ cx231xx_err("unable to allocate %i bytes for transfer"
+ " buffer %i%s\n",
+ sb_size, i,
+ in_interrupt() ? " while in int" : "");
+ cx231xx_uninit_bulk(dev);
+ return -ENOMEM;
+ }
+ memset(dev->video_mode.bulk_ctl.transfer_buffer[i], 0, sb_size);
+
+ pipe = usb_rcvbulkpipe(dev->udev,
+ dev->video_mode.end_point_addr);
+ usb_fill_bulk_urb(urb, dev->udev, pipe,
+ dev->video_mode.bulk_ctl.transfer_buffer[i],
+ sb_size, cx231xx_bulk_irq_callback, dma_q);
+ }
+
+ init_waitqueue_head(&dma_q->wq);
+
+ /* submit urbs and enables IRQ */
+ for (i = 0; i < dev->video_mode.bulk_ctl.num_bufs; i++) {
+ rc = usb_submit_urb(dev->video_mode.bulk_ctl.urb[i],
+ GFP_ATOMIC);
+ if (rc) {
+ cx231xx_err("submit of urb %i failed (error=%i)\n", i,
+ rc);
+ cx231xx_uninit_bulk(dev);
+ return rc;
+ }
+ }
+
+ if (dev->mode_tv == 0)
+ cx231xx_capture_start(dev, 1, Raw_Video);
+ else
+ cx231xx_capture_start(dev, 1, TS1_serial_mode);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cx231xx_init_bulk);
+void cx231xx_stop_TS1(struct cx231xx *dev)
+{
+ int status = 0;
+ u8 val[4] = { 0, 0, 0, 0 };
+
+ val[0] = 0x00;
+ val[1] = 0x03;
+ val[2] = 0x00;
+ val[3] = 0x00;
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ TS_MODE_REG, val, 4);
+
+ val[0] = 0x00;
+ val[1] = 0x70;
+ val[2] = 0x04;
+ val[3] = 0x00;
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ TS1_CFG_REG, val, 4);
+}
+/* EXPORT_SYMBOL_GPL(cx231xx_stop_TS1); */
+void cx231xx_start_TS1(struct cx231xx *dev)
+{
+ int status = 0;
+ u8 val[4] = { 0, 0, 0, 0 };
+
+ val[0] = 0x03;
+ val[1] = 0x03;
+ val[2] = 0x00;
+ val[3] = 0x00;
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ TS_MODE_REG, val, 4);
+
+ val[0] = 0x04;
+ val[1] = 0xA3;
+ val[2] = 0x3B;
+ val[3] = 0x00;
+ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ TS1_CFG_REG, val, 4);
+}
+/* EXPORT_SYMBOL_GPL(cx231xx_start_TS1); */
+/*****************************************************************
+* Device Init/UnInit functions *
+******************************************************************/
+int cx231xx_dev_init(struct cx231xx *dev)
+{
+ int errCode = 0;
+
+ /* Initialize I2C bus */
+
+ /* External Master 1 Bus */
+ dev->i2c_bus[0].nr = 0;
+ dev->i2c_bus[0].dev = dev;
+ dev->i2c_bus[0].i2c_period = I2C_SPEED_100K; /* 100 KHz */
+ dev->i2c_bus[0].i2c_nostop = 0;
+ dev->i2c_bus[0].i2c_reserve = 0;
+
+ /* External Master 2 Bus */
+ dev->i2c_bus[1].nr = 1;
+ dev->i2c_bus[1].dev = dev;
+ dev->i2c_bus[1].i2c_period = I2C_SPEED_100K; /* 100 KHz */
+ dev->i2c_bus[1].i2c_nostop = 0;
+ dev->i2c_bus[1].i2c_reserve = 0;
+
+ /* Internal Master 3 Bus */
+ dev->i2c_bus[2].nr = 2;
+ dev->i2c_bus[2].dev = dev;
+ dev->i2c_bus[2].i2c_period = I2C_SPEED_100K; /* 100kHz */
+ dev->i2c_bus[2].i2c_nostop = 0;
+ dev->i2c_bus[2].i2c_reserve = 0;
+
+ /* register I2C buses */
+ cx231xx_i2c_register(&dev->i2c_bus[0]);
+ cx231xx_i2c_register(&dev->i2c_bus[1]);
+ cx231xx_i2c_register(&dev->i2c_bus[2]);
+
+ /* init hardware */
+ /* Note : with out calling set power mode function,
+ afe can not be set up correctly */
+ if (dev->board.external_av) {
+ errCode = cx231xx_set_power_mode(dev,
+ POLARIS_AVMODE_ENXTERNAL_AV);
+ if (errCode < 0) {
+ cx231xx_errdev
+ ("%s: Failed to set Power - errCode [%d]!\n",
+ __func__, errCode);
+ return errCode;
+ }
+ } else {
+ errCode = cx231xx_set_power_mode(dev,
+ POLARIS_AVMODE_ANALOGT_TV);
+ if (errCode < 0) {
+ cx231xx_errdev
+ ("%s: Failed to set Power - errCode [%d]!\n",
+ __func__, errCode);
+ return errCode;
+ }
+ }
+
+ /* reset the Tuner, if it is a Xceive tuner */
+ if ((dev->board.tuner_type == TUNER_XC5000) ||
+ (dev->board.tuner_type == TUNER_XC2028))
+ cx231xx_gpio_set(dev, dev->board.tuner_gpio);
+
+ /* initialize Colibri block */
+ errCode = cx231xx_afe_init_super_block(dev, 0x23c);
+ if (errCode < 0) {
+ cx231xx_errdev
+ ("%s: cx231xx_afe init super block - errCode [%d]!\n",
+ __func__, errCode);
+ return errCode;
+ }
+ errCode = cx231xx_afe_init_channels(dev);
+ if (errCode < 0) {
+ cx231xx_errdev
+ ("%s: cx231xx_afe init channels - errCode [%d]!\n",
+ __func__, errCode);
+ return errCode;
+ }
+
+ /* Set DIF in By pass mode */
+ errCode = cx231xx_dif_set_standard(dev, DIF_USE_BASEBAND);
+ if (errCode < 0) {
+ cx231xx_errdev
+ ("%s: cx231xx_dif set to By pass mode - errCode [%d]!\n",
+ __func__, errCode);
+ return errCode;
+ }
+
+ /* I2S block related functions */
+ errCode = cx231xx_i2s_blk_initialize(dev);
+ if (errCode < 0) {
+ cx231xx_errdev
+ ("%s: cx231xx_i2s block initialize - errCode [%d]!\n",
+ __func__, errCode);
+ return errCode;
+ }
+
+ /* init control pins */
+ errCode = cx231xx_init_ctrl_pin_status(dev);
+ if (errCode < 0) {
+ cx231xx_errdev("%s: cx231xx_init ctrl pins - errCode [%d]!\n",
+ __func__, errCode);
+ return errCode;
+ }
+
+ /* set AGC mode to Analog */
+ switch (dev->model) {
+ case CX231XX_BOARD_CNXT_CARRAERA:
+ case CX231XX_BOARD_CNXT_RDE_250:
+ case CX231XX_BOARD_CNXT_SHELBY:
+ case CX231XX_BOARD_CNXT_RDU_250:
+ errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 1);
+ break;
+ case CX231XX_BOARD_CNXT_RDE_253S:
+ case CX231XX_BOARD_CNXT_RDU_253S:
+ case CX231XX_BOARD_HAUPPAUGE_EXETER:
+ case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID:
+ case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL:
+ case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC:
+ errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 0);
+ break;
+ default:
+ break;
+ }
+ if (errCode < 0) {
+ cx231xx_errdev
+ ("%s: cx231xx_AGC mode to Analog - errCode [%d]!\n",
+ __func__, errCode);
+ return errCode;
+ }
+
+ /* set all alternate settings to zero initially */
+ cx231xx_set_alt_setting(dev, INDEX_VIDEO, 0);
+ cx231xx_set_alt_setting(dev, INDEX_VANC, 0);
+ cx231xx_set_alt_setting(dev, INDEX_HANC, 0);
+ if (dev->board.has_dvb)
+ cx231xx_set_alt_setting(dev, INDEX_TS1, 0);
+
+ /* set the I2C master port to 3 on channel 1 */
+ errCode = cx231xx_enable_i2c_port_3(dev, true);
+
+ return errCode;
+}
+EXPORT_SYMBOL_GPL(cx231xx_dev_init);
+
+void cx231xx_dev_uninit(struct cx231xx *dev)
+{
+ /* Un Initialize I2C bus */
+ cx231xx_i2c_unregister(&dev->i2c_bus[2]);
+ cx231xx_i2c_unregister(&dev->i2c_bus[1]);
+ cx231xx_i2c_unregister(&dev->i2c_bus[0]);
+}
+EXPORT_SYMBOL_GPL(cx231xx_dev_uninit);
+
+/*****************************************************************
+* G P I O related functions *
+******************************************************************/
+int cx231xx_send_gpio_cmd(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val,
+ u8 len, u8 request, u8 direction)
+{
+ int status = 0;
+ struct VENDOR_REQUEST_IN ven_req;
+
+ /* Set wValue */
+ ven_req.wValue = (u16) (gpio_bit >> 16 & 0xffff);
+
+ /* set request */
+ if (!request) {
+ if (direction)
+ ven_req.bRequest = VRT_GET_GPIO; /* 0x8 gpio */
+ else
+ ven_req.bRequest = VRT_SET_GPIO; /* 0x9 gpio */
+ } else {
+ if (direction)
+ ven_req.bRequest = VRT_GET_GPIE; /* 0xa gpie */
+ else
+ ven_req.bRequest = VRT_SET_GPIE; /* 0xb gpie */
+ }
+
+ /* set index value */
+ ven_req.wIndex = (u16) (gpio_bit & 0xffff);
+
+ /* set wLength value */
+ ven_req.wLength = len;
+
+ /* set bData value */
+ ven_req.bData = 0;
+
+ /* set the buffer for read / write */
+ ven_req.pBuff = gpio_val;
+
+ /* set the direction */
+ if (direction) {
+ ven_req.direction = USB_DIR_IN;
+ memset(ven_req.pBuff, 0x00, ven_req.wLength);
+ } else
+ ven_req.direction = USB_DIR_OUT;
+
+
+ /* call common vendor command request */
+ status = cx231xx_send_vendor_cmd(dev, &ven_req);
+ if (status < 0) {
+ cx231xx_info
+ ("UsbInterface::sendCommand, failed with status -%d\n",
+ status);
+ }
+
+ return status;
+}
+EXPORT_SYMBOL_GPL(cx231xx_send_gpio_cmd);
+
+/*****************************************************************
+ * C O N T R O L - Register R E A D / W R I T E functions *
+ *****************************************************************/
+int cx231xx_mode_register(struct cx231xx *dev, u16 address, u32 mode)
+{
+ u8 value[4] = { 0x0, 0x0, 0x0, 0x0 };
+ u32 tmp = 0;
+ int status = 0;
+
+ status =
+ cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, address, value, 4);
+ if (status < 0)
+ return status;
+
+ tmp = *((u32 *) value);
+ tmp |= mode;
+
+ value[0] = (u8) tmp;
+ value[1] = (u8) (tmp >> 8);
+ value[2] = (u8) (tmp >> 16);
+ value[3] = (u8) (tmp >> 24);
+
+ status =
+ cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, address, value, 4);
+
+ return status;
+}
+
+/*****************************************************************
+ * I 2 C Internal C O N T R O L functions *
+ *****************************************************************/
+int cx231xx_read_i2c_master(struct cx231xx *dev, u8 dev_addr, u16 saddr,
+ u8 saddr_len, u32 *data, u8 data_len, int master)
+{
+ int status = 0;
+ struct cx231xx_i2c_xfer_data req_data;
+ u8 value[64] = "0";
+
+ if (saddr_len == 0)
+ saddr = 0;
+ else if (saddr_len == 1)
+ saddr &= 0xff;
+
+ /* prepare xfer_data struct */
+ req_data.dev_addr = dev_addr >> 1;
+ req_data.direction = I2C_M_RD;
+ req_data.saddr_len = saddr_len;
+ req_data.saddr_dat = saddr;
+ req_data.buf_size = data_len;
+ req_data.p_buffer = (u8 *) value;
+
+ /* usb send command */
+ if (master == 0)
+ status = dev->cx231xx_send_usb_command(&dev->i2c_bus[0],
+ &req_data);
+ else if (master == 1)
+ status = dev->cx231xx_send_usb_command(&dev->i2c_bus[1],
+ &req_data);
+ else if (master == 2)
+ status = dev->cx231xx_send_usb_command(&dev->i2c_bus[2],
+ &req_data);
+
+ if (status >= 0) {
+ /* Copy the data read back to main buffer */
+ if (data_len == 1)
+ *data = value[0];
+ else if (data_len == 4)
+ *data =
+ value[0] | value[1] << 8 | value[2] << 16 | value[3]
+ << 24;
+ else if (data_len > 4)
+ *data = value[saddr];
+ }
+
+ return status;
+}
+
+int cx231xx_write_i2c_master(struct cx231xx *dev, u8 dev_addr, u16 saddr,
+ u8 saddr_len, u32 data, u8 data_len, int master)
+{
+ int status = 0;
+ u8 value[4] = { 0, 0, 0, 0 };
+ struct cx231xx_i2c_xfer_data req_data;
+
+ value[0] = (u8) data;
+ value[1] = (u8) (data >> 8);
+ value[2] = (u8) (data >> 16);
+ value[3] = (u8) (data >> 24);
+
+ if (saddr_len == 0)
+ saddr = 0;
+ else if (saddr_len == 1)
+ saddr &= 0xff;
+
+ /* prepare xfer_data struct */
+ req_data.dev_addr = dev_addr >> 1;
+ req_data.direction = 0;
+ req_data.saddr_len = saddr_len;
+ req_data.saddr_dat = saddr;
+ req_data.buf_size = data_len;
+ req_data.p_buffer = value;
+
+ /* usb send command */
+ if (master == 0)
+ status = dev->cx231xx_send_usb_command(&dev->i2c_bus[0],
+ &req_data);
+ else if (master == 1)
+ status = dev->cx231xx_send_usb_command(&dev->i2c_bus[1],
+ &req_data);
+ else if (master == 2)
+ status = dev->cx231xx_send_usb_command(&dev->i2c_bus[2],
+ &req_data);
+
+ return status;
+}
+
+int cx231xx_read_i2c_data(struct cx231xx *dev, u8 dev_addr, u16 saddr,
+ u8 saddr_len, u32 *data, u8 data_len)
+{
+ int status = 0;
+ struct cx231xx_i2c_xfer_data req_data;
+ u8 value[4] = { 0, 0, 0, 0 };
+
+ if (saddr_len == 0)
+ saddr = 0;
+ else if (saddr_len == 1)
+ saddr &= 0xff;
+
+ /* prepare xfer_data struct */
+ req_data.dev_addr = dev_addr >> 1;
+ req_data.direction = I2C_M_RD;
+ req_data.saddr_len = saddr_len;
+ req_data.saddr_dat = saddr;
+ req_data.buf_size = data_len;
+ req_data.p_buffer = (u8 *) value;
+
+ /* usb send command */
+ status = dev->cx231xx_send_usb_command(&dev->i2c_bus[0], &req_data);
+
+ if (status >= 0) {
+ /* Copy the data read back to main buffer */
+ if (data_len == 1)
+ *data = value[0];
+ else
+ *data =
+ value[0] | value[1] << 8 | value[2] << 16 | value[3]
+ << 24;
+ }
+
+ return status;
+}
+
+int cx231xx_write_i2c_data(struct cx231xx *dev, u8 dev_addr, u16 saddr,
+ u8 saddr_len, u32 data, u8 data_len)
+{
+ int status = 0;
+ u8 value[4] = { 0, 0, 0, 0 };
+ struct cx231xx_i2c_xfer_data req_data;
+
+ value[0] = (u8) data;
+ value[1] = (u8) (data >> 8);
+ value[2] = (u8) (data >> 16);
+ value[3] = (u8) (data >> 24);
+
+ if (saddr_len == 0)
+ saddr = 0;
+ else if (saddr_len == 1)
+ saddr &= 0xff;
+
+ /* prepare xfer_data struct */
+ req_data.dev_addr = dev_addr >> 1;
+ req_data.direction = 0;
+ req_data.saddr_len = saddr_len;
+ req_data.saddr_dat = saddr;
+ req_data.buf_size = data_len;
+ req_data.p_buffer = value;
+
+ /* usb send command */
+ status = dev->cx231xx_send_usb_command(&dev->i2c_bus[0], &req_data);
+
+ return status;
+}
+
+int cx231xx_reg_mask_write(struct cx231xx *dev, u8 dev_addr, u8 size,
+ u16 register_address, u8 bit_start, u8 bit_end,
+ u32 value)
+{
+ int status = 0;
+ u32 tmp;
+ u32 mask = 0;
+ int i;
+
+ if (bit_start > (size - 1) || bit_end > (size - 1))
+ return -1;
+
+ if (size == 8) {
+ status =
+ cx231xx_read_i2c_data(dev, dev_addr, register_address, 2,
+ &tmp, 1);
+ } else {
+ status =
+ cx231xx_read_i2c_data(dev, dev_addr, register_address, 2,
+ &tmp, 4);
+ }
+
+ if (status < 0)
+ return status;
+
+ mask = 1 << bit_end;
+ for (i = bit_end; i > bit_start && i > 0; i--)
+ mask = mask + (1 << (i - 1));
+
+ value <<= bit_start;
+
+ if (size == 8) {
+ tmp &= ~mask;
+ tmp |= value;
+ tmp &= 0xff;
+ status =
+ cx231xx_write_i2c_data(dev, dev_addr, register_address, 2,
+ tmp, 1);
+ } else {
+ tmp &= ~mask;
+ tmp |= value;
+ status =
+ cx231xx_write_i2c_data(dev, dev_addr, register_address, 2,
+ tmp, 4);
+ }
+
+ return status;
+}
+
+int cx231xx_read_modify_write_i2c_dword(struct cx231xx *dev, u8 dev_addr,
+ u16 saddr, u32 mask, u32 value)
+{
+ u32 temp;
+ int status = 0;
+
+ status = cx231xx_read_i2c_data(dev, dev_addr, saddr, 2, &temp, 4);
+
+ if (status < 0)
+ return status;
+
+ temp &= ~mask;
+ temp |= value;
+
+ status = cx231xx_write_i2c_data(dev, dev_addr, saddr, 2, temp, 4);
+
+ return status;
+}
+
+u32 cx231xx_set_field(u32 field_mask, u32 data)
+{
+ u32 temp;
+
+ for (temp = field_mask; (temp & 1) == 0; temp >>= 1)
+ data <<= 1;
+
+ return data;
+}
diff --git a/drivers/media/video/cx231xx/cx231xx-dif.h b/drivers/media/video/cx231xx/cx231xx-dif.h
new file mode 100644
index 00000000..2b63c2f6
--- /dev/null
+++ b/drivers/media/video/cx231xx/cx231xx-dif.h
@@ -0,0 +1,3178 @@
+/*
+ * cx231xx-dif.h - driver for Conexant Cx23100/101/102 USB video capture devices
+ *
+ * Copyright {C} 2009 <Bill.Liu@conexant.com>
+ *
+ * This program is free software, you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program, if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _CX231XX_DIF_H
+#define _CX231XX_DIF_H
+
+#include "cx231xx-reg.h"
+
+struct dif_settings{
+ u32 if_freq;
+ u32 register_address;
+ u32 value;
+};
+
+static struct dif_settings Dif_set_array[] = {
+
+/*case 3000000:*/
+/* BEGIN - DIF BPF register values from 30_quant.dat*/
+{3000000, DIF_BPF_COEFF01, 0x00000002},
+{3000000, DIF_BPF_COEFF23, 0x00080012},
+{3000000, DIF_BPF_COEFF45, 0x001e0024},
+{3000000, DIF_BPF_COEFF67, 0x001bfff8},
+{3000000, DIF_BPF_COEFF89, 0xffb4ff50},
+{3000000, DIF_BPF_COEFF1011, 0xfed8fe68},
+{3000000, DIF_BPF_COEFF1213, 0xfe24fe34},
+{3000000, DIF_BPF_COEFF1415, 0xfebaffc7},
+{3000000, DIF_BPF_COEFF1617, 0x014d031f},
+{3000000, DIF_BPF_COEFF1819, 0x04f0065d},
+{3000000, DIF_BPF_COEFF2021, 0x07010688},
+{3000000, DIF_BPF_COEFF2223, 0x04c901d6},
+{3000000, DIF_BPF_COEFF2425, 0xfe00f9d3},
+{3000000, DIF_BPF_COEFF2627, 0xf600f342},
+{3000000, DIF_BPF_COEFF2829, 0xf235f337},
+{3000000, DIF_BPF_COEFF3031, 0xf64efb22},
+{3000000, DIF_BPF_COEFF3233, 0x0105070f},
+{3000000, DIF_BPF_COEFF3435, 0x0c460fce},
+{3000000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 30_quant.dat*/
+
+
+/*case 3100000:*/
+/* BEGIN - DIF BPF register values from 31_quant.dat*/
+{3100000, DIF_BPF_COEFF01, 0x00000001},
+{3100000, DIF_BPF_COEFF23, 0x00070012},
+{3100000, DIF_BPF_COEFF45, 0x00220032},
+{3100000, DIF_BPF_COEFF67, 0x00370026},
+{3100000, DIF_BPF_COEFF89, 0xfff0ff91},
+{3100000, DIF_BPF_COEFF1011, 0xff0efe7c},
+{3100000, DIF_BPF_COEFF1213, 0xfe01fdcc},
+{3100000, DIF_BPF_COEFF1415, 0xfe0afedb},
+{3100000, DIF_BPF_COEFF1617, 0x00440224},
+{3100000, DIF_BPF_COEFF1819, 0x0434060c},
+{3100000, DIF_BPF_COEFF2021, 0x0738074e},
+{3100000, DIF_BPF_COEFF2223, 0x06090361},
+{3100000, DIF_BPF_COEFF2425, 0xff99fb39},
+{3100000, DIF_BPF_COEFF2627, 0xf6fef3b6},
+{3100000, DIF_BPF_COEFF2829, 0xf21af2a5},
+{3100000, DIF_BPF_COEFF3031, 0xf573fa33},
+{3100000, DIF_BPF_COEFF3233, 0x0034067d},
+{3100000, DIF_BPF_COEFF3435, 0x0bfb0fb9},
+{3100000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 31_quant.dat*/
+
+
+/*case 3200000:*/
+/* BEGIN - DIF BPF register values from 32_quant.dat*/
+{3200000, DIF_BPF_COEFF01, 0x00000000},
+{3200000, DIF_BPF_COEFF23, 0x0004000e},
+{3200000, DIF_BPF_COEFF45, 0x00200038},
+{3200000, DIF_BPF_COEFF67, 0x004c004f},
+{3200000, DIF_BPF_COEFF89, 0x002fffdf},
+{3200000, DIF_BPF_COEFF1011, 0xff5cfeb6},
+{3200000, DIF_BPF_COEFF1213, 0xfe0dfd92},
+{3200000, DIF_BPF_COEFF1415, 0xfd7ffe03},
+{3200000, DIF_BPF_COEFF1617, 0xff36010a},
+{3200000, DIF_BPF_COEFF1819, 0x03410575},
+{3200000, DIF_BPF_COEFF2021, 0x072607d2},
+{3200000, DIF_BPF_COEFF2223, 0x071804d5},
+{3200000, DIF_BPF_COEFF2425, 0x0134fcb7},
+{3200000, DIF_BPF_COEFF2627, 0xf81ff451},
+{3200000, DIF_BPF_COEFF2829, 0xf223f22e},
+{3200000, DIF_BPF_COEFF3031, 0xf4a7f94b},
+{3200000, DIF_BPF_COEFF3233, 0xff6405e8},
+{3200000, DIF_BPF_COEFF3435, 0x0bae0fa4},
+{3200000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 32_quant.dat*/
+
+
+/*case 3300000:*/
+/* BEGIN - DIF BPF register values from 33_quant.dat*/
+{3300000, DIF_BPF_COEFF01, 0x0000ffff},
+{3300000, DIF_BPF_COEFF23, 0x00000008},
+{3300000, DIF_BPF_COEFF45, 0x001a0036},
+{3300000, DIF_BPF_COEFF67, 0x0056006d},
+{3300000, DIF_BPF_COEFF89, 0x00670030},
+{3300000, DIF_BPF_COEFF1011, 0xffbdff10},
+{3300000, DIF_BPF_COEFF1213, 0xfe46fd8d},
+{3300000, DIF_BPF_COEFF1415, 0xfd25fd4f},
+{3300000, DIF_BPF_COEFF1617, 0xfe35ffe0},
+{3300000, DIF_BPF_COEFF1819, 0x0224049f},
+{3300000, DIF_BPF_COEFF2021, 0x06c9080e},
+{3300000, DIF_BPF_COEFF2223, 0x07ef0627},
+{3300000, DIF_BPF_COEFF2425, 0x02c9fe45},
+{3300000, DIF_BPF_COEFF2627, 0xf961f513},
+{3300000, DIF_BPF_COEFF2829, 0xf250f1d2},
+{3300000, DIF_BPF_COEFF3031, 0xf3ecf869},
+{3300000, DIF_BPF_COEFF3233, 0xfe930552},
+{3300000, DIF_BPF_COEFF3435, 0x0b5f0f8f},
+{3300000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 33_quant.dat*/
+
+
+/*case 3400000:*/
+/* BEGIN - DIF BPF register values from 34_quant.dat*/
+{3400000, DIF_BPF_COEFF01, 0xfffffffe},
+{3400000, DIF_BPF_COEFF23, 0xfffd0001},
+{3400000, DIF_BPF_COEFF45, 0x000f002c},
+{3400000, DIF_BPF_COEFF67, 0x0054007d},
+{3400000, DIF_BPF_COEFF89, 0x0093007c},
+{3400000, DIF_BPF_COEFF1011, 0x0024ff82},
+{3400000, DIF_BPF_COEFF1213, 0xfea6fdbb},
+{3400000, DIF_BPF_COEFF1415, 0xfd03fcca},
+{3400000, DIF_BPF_COEFF1617, 0xfd51feb9},
+{3400000, DIF_BPF_COEFF1819, 0x00eb0392},
+{3400000, DIF_BPF_COEFF2021, 0x06270802},
+{3400000, DIF_BPF_COEFF2223, 0x08880750},
+{3400000, DIF_BPF_COEFF2425, 0x044dffdb},
+{3400000, DIF_BPF_COEFF2627, 0xfabdf5f8},
+{3400000, DIF_BPF_COEFF2829, 0xf2a0f193},
+{3400000, DIF_BPF_COEFF3031, 0xf342f78f},
+{3400000, DIF_BPF_COEFF3233, 0xfdc404b9},
+{3400000, DIF_BPF_COEFF3435, 0x0b0e0f78},
+{3400000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 34_quant.dat*/
+
+
+/*case 3500000:*/
+/* BEGIN - DIF BPF register values from 35_quant.dat*/
+{3500000, DIF_BPF_COEFF01, 0xfffffffd},
+{3500000, DIF_BPF_COEFF23, 0xfffafff9},
+{3500000, DIF_BPF_COEFF45, 0x0002001b},
+{3500000, DIF_BPF_COEFF67, 0x0046007d},
+{3500000, DIF_BPF_COEFF89, 0x00ad00ba},
+{3500000, DIF_BPF_COEFF1011, 0x00870000},
+{3500000, DIF_BPF_COEFF1213, 0xff26fe1a},
+{3500000, DIF_BPF_COEFF1415, 0xfd1bfc7e},
+{3500000, DIF_BPF_COEFF1617, 0xfc99fda4},
+{3500000, DIF_BPF_COEFF1819, 0xffa5025c},
+{3500000, DIF_BPF_COEFF2021, 0x054507ad},
+{3500000, DIF_BPF_COEFF2223, 0x08dd0847},
+{3500000, DIF_BPF_COEFF2425, 0x05b80172},
+{3500000, DIF_BPF_COEFF2627, 0xfc2ef6ff},
+{3500000, DIF_BPF_COEFF2829, 0xf313f170},
+{3500000, DIF_BPF_COEFF3031, 0xf2abf6bd},
+{3500000, DIF_BPF_COEFF3233, 0xfcf6041f},
+{3500000, DIF_BPF_COEFF3435, 0x0abc0f61},
+{3500000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 35_quant.dat*/
+
+
+/*case 3600000:*/
+/* BEGIN - DIF BPF register values from 36_quant.dat*/
+{3600000, DIF_BPF_COEFF01, 0xfffffffd},
+{3600000, DIF_BPF_COEFF23, 0xfff8fff3},
+{3600000, DIF_BPF_COEFF45, 0xfff50006},
+{3600000, DIF_BPF_COEFF67, 0x002f006c},
+{3600000, DIF_BPF_COEFF89, 0x00b200e3},
+{3600000, DIF_BPF_COEFF1011, 0x00dc007e},
+{3600000, DIF_BPF_COEFF1213, 0xffb9fea0},
+{3600000, DIF_BPF_COEFF1415, 0xfd6bfc71},
+{3600000, DIF_BPF_COEFF1617, 0xfc17fcb1},
+{3600000, DIF_BPF_COEFF1819, 0xfe65010b},
+{3600000, DIF_BPF_COEFF2021, 0x042d0713},
+{3600000, DIF_BPF_COEFF2223, 0x08ec0906},
+{3600000, DIF_BPF_COEFF2425, 0x07020302},
+{3600000, DIF_BPF_COEFF2627, 0xfdaff823},
+{3600000, DIF_BPF_COEFF2829, 0xf3a7f16a},
+{3600000, DIF_BPF_COEFF3031, 0xf228f5f5},
+{3600000, DIF_BPF_COEFF3233, 0xfc2a0384},
+{3600000, DIF_BPF_COEFF3435, 0x0a670f4a},
+{3600000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 36_quant.dat*/
+
+
+/*case 3700000:*/
+/* BEGIN - DIF BPF register values from 37_quant.dat*/
+{3700000, DIF_BPF_COEFF01, 0x0000fffd},
+{3700000, DIF_BPF_COEFF23, 0xfff7ffef},
+{3700000, DIF_BPF_COEFF45, 0xffe9fff1},
+{3700000, DIF_BPF_COEFF67, 0x0010004d},
+{3700000, DIF_BPF_COEFF89, 0x00a100f2},
+{3700000, DIF_BPF_COEFF1011, 0x011a00f0},
+{3700000, DIF_BPF_COEFF1213, 0x0053ff44},
+{3700000, DIF_BPF_COEFF1415, 0xfdedfca2},
+{3700000, DIF_BPF_COEFF1617, 0xfbd3fbef},
+{3700000, DIF_BPF_COEFF1819, 0xfd39ffae},
+{3700000, DIF_BPF_COEFF2021, 0x02ea0638},
+{3700000, DIF_BPF_COEFF2223, 0x08b50987},
+{3700000, DIF_BPF_COEFF2425, 0x08230483},
+{3700000, DIF_BPF_COEFF2627, 0xff39f960},
+{3700000, DIF_BPF_COEFF2829, 0xf45bf180},
+{3700000, DIF_BPF_COEFF3031, 0xf1b8f537},
+{3700000, DIF_BPF_COEFF3233, 0xfb6102e7},
+{3700000, DIF_BPF_COEFF3435, 0x0a110f32},
+{3700000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 37_quant.dat*/
+
+
+/*case 3800000:*/
+/* BEGIN - DIF BPF register values from 38_quant.dat*/
+{3800000, DIF_BPF_COEFF01, 0x0000fffe},
+{3800000, DIF_BPF_COEFF23, 0xfff9ffee},
+{3800000, DIF_BPF_COEFF45, 0xffe1ffdd},
+{3800000, DIF_BPF_COEFF67, 0xfff00024},
+{3800000, DIF_BPF_COEFF89, 0x007c00e5},
+{3800000, DIF_BPF_COEFF1011, 0x013a014a},
+{3800000, DIF_BPF_COEFF1213, 0x00e6fff8},
+{3800000, DIF_BPF_COEFF1415, 0xfe98fd0f},
+{3800000, DIF_BPF_COEFF1617, 0xfbd3fb67},
+{3800000, DIF_BPF_COEFF1819, 0xfc32fe54},
+{3800000, DIF_BPF_COEFF2021, 0x01880525},
+{3800000, DIF_BPF_COEFF2223, 0x083909c7},
+{3800000, DIF_BPF_COEFF2425, 0x091505ee},
+{3800000, DIF_BPF_COEFF2627, 0x00c7fab3},
+{3800000, DIF_BPF_COEFF2829, 0xf52df1b4},
+{3800000, DIF_BPF_COEFF3031, 0xf15df484},
+{3800000, DIF_BPF_COEFF3233, 0xfa9b0249},
+{3800000, DIF_BPF_COEFF3435, 0x09ba0f19},
+{3800000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 38_quant.dat*/
+
+
+/*case 3900000:*/
+/* BEGIN - DIF BPF register values from 39_quant.dat*/
+{3900000, DIF_BPF_COEFF01, 0x00000000},
+{3900000, DIF_BPF_COEFF23, 0xfffbfff0},
+{3900000, DIF_BPF_COEFF45, 0xffdeffcf},
+{3900000, DIF_BPF_COEFF67, 0xffd1fff6},
+{3900000, DIF_BPF_COEFF89, 0x004800be},
+{3900000, DIF_BPF_COEFF1011, 0x01390184},
+{3900000, DIF_BPF_COEFF1213, 0x016300ac},
+{3900000, DIF_BPF_COEFF1415, 0xff5efdb1},
+{3900000, DIF_BPF_COEFF1617, 0xfc17fb23},
+{3900000, DIF_BPF_COEFF1819, 0xfb5cfd0d},
+{3900000, DIF_BPF_COEFF2021, 0x001703e4},
+{3900000, DIF_BPF_COEFF2223, 0x077b09c4},
+{3900000, DIF_BPF_COEFF2425, 0x09d2073c},
+{3900000, DIF_BPF_COEFF2627, 0x0251fc18},
+{3900000, DIF_BPF_COEFF2829, 0xf61cf203},
+{3900000, DIF_BPF_COEFF3031, 0xf118f3dc},
+{3900000, DIF_BPF_COEFF3233, 0xf9d801aa},
+{3900000, DIF_BPF_COEFF3435, 0x09600eff},
+{3900000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 39_quant.dat*/
+
+
+/*case 4000000:*/
+/* BEGIN - DIF BPF register values from 40_quant.dat*/
+{4000000, DIF_BPF_COEFF01, 0x00000001},
+{4000000, DIF_BPF_COEFF23, 0xfffefff4},
+{4000000, DIF_BPF_COEFF45, 0xffe1ffc8},
+{4000000, DIF_BPF_COEFF67, 0xffbaffca},
+{4000000, DIF_BPF_COEFF89, 0x000b0082},
+{4000000, DIF_BPF_COEFF1011, 0x01170198},
+{4000000, DIF_BPF_COEFF1213, 0x01c10152},
+{4000000, DIF_BPF_COEFF1415, 0x0030fe7b},
+{4000000, DIF_BPF_COEFF1617, 0xfc99fb24},
+{4000000, DIF_BPF_COEFF1819, 0xfac3fbe9},
+{4000000, DIF_BPF_COEFF2021, 0xfea5027f},
+{4000000, DIF_BPF_COEFF2223, 0x0683097f},
+{4000000, DIF_BPF_COEFF2425, 0x0a560867},
+{4000000, DIF_BPF_COEFF2627, 0x03d2fd89},
+{4000000, DIF_BPF_COEFF2829, 0xf723f26f},
+{4000000, DIF_BPF_COEFF3031, 0xf0e8f341},
+{4000000, DIF_BPF_COEFF3233, 0xf919010a},
+{4000000, DIF_BPF_COEFF3435, 0x09060ee5},
+{4000000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 40_quant.dat*/
+
+
+/*case 4100000:*/
+/* BEGIN - DIF BPF register values from 41_quant.dat*/
+{4100000, DIF_BPF_COEFF01, 0x00010002},
+{4100000, DIF_BPF_COEFF23, 0x0002fffb},
+{4100000, DIF_BPF_COEFF45, 0xffe8ffca},
+{4100000, DIF_BPF_COEFF67, 0xffacffa4},
+{4100000, DIF_BPF_COEFF89, 0xffcd0036},
+{4100000, DIF_BPF_COEFF1011, 0x00d70184},
+{4100000, DIF_BPF_COEFF1213, 0x01f601dc},
+{4100000, DIF_BPF_COEFF1415, 0x00ffff60},
+{4100000, DIF_BPF_COEFF1617, 0xfd51fb6d},
+{4100000, DIF_BPF_COEFF1819, 0xfa6efaf5},
+{4100000, DIF_BPF_COEFF2021, 0xfd410103},
+{4100000, DIF_BPF_COEFF2223, 0x055708f9},
+{4100000, DIF_BPF_COEFF2425, 0x0a9e0969},
+{4100000, DIF_BPF_COEFF2627, 0x0543ff02},
+{4100000, DIF_BPF_COEFF2829, 0xf842f2f5},
+{4100000, DIF_BPF_COEFF3031, 0xf0cef2b2},
+{4100000, DIF_BPF_COEFF3233, 0xf85e006b},
+{4100000, DIF_BPF_COEFF3435, 0x08aa0ecb},
+{4100000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 41_quant.dat*/
+
+
+/*case 4200000:*/
+/* BEGIN - DIF BPF register values from 42_quant.dat*/
+{4200000, DIF_BPF_COEFF01, 0x00010003},
+{4200000, DIF_BPF_COEFF23, 0x00050003},
+{4200000, DIF_BPF_COEFF45, 0xfff3ffd3},
+{4200000, DIF_BPF_COEFF67, 0xffaaff8b},
+{4200000, DIF_BPF_COEFF89, 0xff95ffe5},
+{4200000, DIF_BPF_COEFF1011, 0x0080014a},
+{4200000, DIF_BPF_COEFF1213, 0x01fe023f},
+{4200000, DIF_BPF_COEFF1415, 0x01ba0050},
+{4200000, DIF_BPF_COEFF1617, 0xfe35fbf8},
+{4200000, DIF_BPF_COEFF1819, 0xfa62fa3b},
+{4200000, DIF_BPF_COEFF2021, 0xfbf9ff7e},
+{4200000, DIF_BPF_COEFF2223, 0x04010836},
+{4200000, DIF_BPF_COEFF2425, 0x0aa90a3d},
+{4200000, DIF_BPF_COEFF2627, 0x069f007f},
+{4200000, DIF_BPF_COEFF2829, 0xf975f395},
+{4200000, DIF_BPF_COEFF3031, 0xf0cbf231},
+{4200000, DIF_BPF_COEFF3233, 0xf7a9ffcb},
+{4200000, DIF_BPF_COEFF3435, 0x084c0eaf},
+{4200000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 42_quant.dat*/
+
+
+/*case 4300000:*/
+/* BEGIN - DIF BPF register values from 43_quant.dat*/
+{4300000, DIF_BPF_COEFF01, 0x00010003},
+{4300000, DIF_BPF_COEFF23, 0x0008000a},
+{4300000, DIF_BPF_COEFF45, 0x0000ffe4},
+{4300000, DIF_BPF_COEFF67, 0xffb4ff81},
+{4300000, DIF_BPF_COEFF89, 0xff6aff96},
+{4300000, DIF_BPF_COEFF1011, 0x001c00f0},
+{4300000, DIF_BPF_COEFF1213, 0x01d70271},
+{4300000, DIF_BPF_COEFF1415, 0x0254013b},
+{4300000, DIF_BPF_COEFF1617, 0xff36fcbd},
+{4300000, DIF_BPF_COEFF1819, 0xfa9ff9c5},
+{4300000, DIF_BPF_COEFF2021, 0xfadbfdfe},
+{4300000, DIF_BPF_COEFF2223, 0x028c073b},
+{4300000, DIF_BPF_COEFF2425, 0x0a750adf},
+{4300000, DIF_BPF_COEFF2627, 0x07e101fa},
+{4300000, DIF_BPF_COEFF2829, 0xfab8f44e},
+{4300000, DIF_BPF_COEFF3031, 0xf0ddf1be},
+{4300000, DIF_BPF_COEFF3233, 0xf6f9ff2b},
+{4300000, DIF_BPF_COEFF3435, 0x07ed0e94},
+{4300000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 43_quant.dat*/
+
+
+/*case 4400000:*/
+/* BEGIN - DIF BPF register values from 44_quant.dat*/
+{4400000, DIF_BPF_COEFF01, 0x00000003},
+{4400000, DIF_BPF_COEFF23, 0x0009000f},
+{4400000, DIF_BPF_COEFF45, 0x000efff8},
+{4400000, DIF_BPF_COEFF67, 0xffc9ff87},
+{4400000, DIF_BPF_COEFF89, 0xff52ff54},
+{4400000, DIF_BPF_COEFF1011, 0xffb5007e},
+{4400000, DIF_BPF_COEFF1213, 0x01860270},
+{4400000, DIF_BPF_COEFF1415, 0x02c00210},
+{4400000, DIF_BPF_COEFF1617, 0x0044fdb2},
+{4400000, DIF_BPF_COEFF1819, 0xfb22f997},
+{4400000, DIF_BPF_COEFF2021, 0xf9f2fc90},
+{4400000, DIF_BPF_COEFF2223, 0x0102060f},
+{4400000, DIF_BPF_COEFF2425, 0x0a050b4c},
+{4400000, DIF_BPF_COEFF2627, 0x0902036e},
+{4400000, DIF_BPF_COEFF2829, 0xfc0af51e},
+{4400000, DIF_BPF_COEFF3031, 0xf106f15a},
+{4400000, DIF_BPF_COEFF3233, 0xf64efe8b},
+{4400000, DIF_BPF_COEFF3435, 0x078d0e77},
+{4400000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 44_quant.dat*/
+
+
+/*case 4500000:*/
+/* BEGIN - DIF BPF register values from 45_quant.dat*/
+{4500000, DIF_BPF_COEFF01, 0x00000002},
+{4500000, DIF_BPF_COEFF23, 0x00080012},
+{4500000, DIF_BPF_COEFF45, 0x0019000e},
+{4500000, DIF_BPF_COEFF67, 0xffe5ff9e},
+{4500000, DIF_BPF_COEFF89, 0xff4fff25},
+{4500000, DIF_BPF_COEFF1011, 0xff560000},
+{4500000, DIF_BPF_COEFF1213, 0x0112023b},
+{4500000, DIF_BPF_COEFF1415, 0x02f702c0},
+{4500000, DIF_BPF_COEFF1617, 0x014dfec8},
+{4500000, DIF_BPF_COEFF1819, 0xfbe5f9b3},
+{4500000, DIF_BPF_COEFF2021, 0xf947fb41},
+{4500000, DIF_BPF_COEFF2223, 0xff7004b9},
+{4500000, DIF_BPF_COEFF2425, 0x095a0b81},
+{4500000, DIF_BPF_COEFF2627, 0x0a0004d8},
+{4500000, DIF_BPF_COEFF2829, 0xfd65f603},
+{4500000, DIF_BPF_COEFF3031, 0xf144f104},
+{4500000, DIF_BPF_COEFF3233, 0xf5aafdec},
+{4500000, DIF_BPF_COEFF3435, 0x072b0e5a},
+{4500000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 45_quant.dat*/
+
+
+/*case 4600000:*/
+/* BEGIN - DIF BPF register values from 46_quant.dat*/
+{4600000, DIF_BPF_COEFF01, 0x00000001},
+{4600000, DIF_BPF_COEFF23, 0x00060012},
+{4600000, DIF_BPF_COEFF45, 0x00200022},
+{4600000, DIF_BPF_COEFF67, 0x0005ffc1},
+{4600000, DIF_BPF_COEFF89, 0xff61ff10},
+{4600000, DIF_BPF_COEFF1011, 0xff09ff82},
+{4600000, DIF_BPF_COEFF1213, 0x008601d7},
+{4600000, DIF_BPF_COEFF1415, 0x02f50340},
+{4600000, DIF_BPF_COEFF1617, 0x0241fff0},
+{4600000, DIF_BPF_COEFF1819, 0xfcddfa19},
+{4600000, DIF_BPF_COEFF2021, 0xf8e2fa1e},
+{4600000, DIF_BPF_COEFF2223, 0xfde30343},
+{4600000, DIF_BPF_COEFF2425, 0x08790b7f},
+{4600000, DIF_BPF_COEFF2627, 0x0ad50631},
+{4600000, DIF_BPF_COEFF2829, 0xfec7f6fc},
+{4600000, DIF_BPF_COEFF3031, 0xf198f0bd},
+{4600000, DIF_BPF_COEFF3233, 0xf50dfd4e},
+{4600000, DIF_BPF_COEFF3435, 0x06c90e3d},
+{4600000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 46_quant.dat*/
+
+
+/*case 4700000:*/
+/* BEGIN - DIF BPF register values from 47_quant.dat*/
+{4700000, DIF_BPF_COEFF01, 0x0000ffff},
+{4700000, DIF_BPF_COEFF23, 0x0003000f},
+{4700000, DIF_BPF_COEFF45, 0x00220030},
+{4700000, DIF_BPF_COEFF67, 0x0025ffed},
+{4700000, DIF_BPF_COEFF89, 0xff87ff15},
+{4700000, DIF_BPF_COEFF1011, 0xfed6ff10},
+{4700000, DIF_BPF_COEFF1213, 0xffed014c},
+{4700000, DIF_BPF_COEFF1415, 0x02b90386},
+{4700000, DIF_BPF_COEFF1617, 0x03110119},
+{4700000, DIF_BPF_COEFF1819, 0xfdfefac4},
+{4700000, DIF_BPF_COEFF2021, 0xf8c6f92f},
+{4700000, DIF_BPF_COEFF2223, 0xfc6701b7},
+{4700000, DIF_BPF_COEFF2425, 0x07670b44},
+{4700000, DIF_BPF_COEFF2627, 0x0b7e0776},
+{4700000, DIF_BPF_COEFF2829, 0x002df807},
+{4700000, DIF_BPF_COEFF3031, 0xf200f086},
+{4700000, DIF_BPF_COEFF3233, 0xf477fcb1},
+{4700000, DIF_BPF_COEFF3435, 0x06650e1e},
+{4700000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 47_quant.dat*/
+
+
+/*case 4800000:*/
+/* BEGIN - DIF BPF register values from 48_quant.dat*/
+{4800000, DIF_BPF_COEFF01, 0xfffffffe},
+{4800000, DIF_BPF_COEFF23, 0xffff0009},
+{4800000, DIF_BPF_COEFF45, 0x001e0038},
+{4800000, DIF_BPF_COEFF67, 0x003f001b},
+{4800000, DIF_BPF_COEFF89, 0xffbcff36},
+{4800000, DIF_BPF_COEFF1011, 0xfec2feb6},
+{4800000, DIF_BPF_COEFF1213, 0xff5600a5},
+{4800000, DIF_BPF_COEFF1415, 0x0248038d},
+{4800000, DIF_BPF_COEFF1617, 0x03b00232},
+{4800000, DIF_BPF_COEFF1819, 0xff39fbab},
+{4800000, DIF_BPF_COEFF2021, 0xf8f4f87f},
+{4800000, DIF_BPF_COEFF2223, 0xfb060020},
+{4800000, DIF_BPF_COEFF2425, 0x062a0ad2},
+{4800000, DIF_BPF_COEFF2627, 0x0bf908a3},
+{4800000, DIF_BPF_COEFF2829, 0x0192f922},
+{4800000, DIF_BPF_COEFF3031, 0xf27df05e},
+{4800000, DIF_BPF_COEFF3233, 0xf3e8fc14},
+{4800000, DIF_BPF_COEFF3435, 0x06000e00},
+{4800000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 48_quant.dat*/
+
+
+/*case 4900000:*/
+/* BEGIN - DIF BPF register values from 49_quant.dat*/
+{4900000, DIF_BPF_COEFF01, 0xfffffffd},
+{4900000, DIF_BPF_COEFF23, 0xfffc0002},
+{4900000, DIF_BPF_COEFF45, 0x00160037},
+{4900000, DIF_BPF_COEFF67, 0x00510046},
+{4900000, DIF_BPF_COEFF89, 0xfff9ff6d},
+{4900000, DIF_BPF_COEFF1011, 0xfed0fe7c},
+{4900000, DIF_BPF_COEFF1213, 0xfecefff0},
+{4900000, DIF_BPF_COEFF1415, 0x01aa0356},
+{4900000, DIF_BPF_COEFF1617, 0x0413032b},
+{4900000, DIF_BPF_COEFF1819, 0x007ffcc5},
+{4900000, DIF_BPF_COEFF2021, 0xf96cf812},
+{4900000, DIF_BPF_COEFF2223, 0xf9cefe87},
+{4900000, DIF_BPF_COEFF2425, 0x04c90a2c},
+{4900000, DIF_BPF_COEFF2627, 0x0c4309b4},
+{4900000, DIF_BPF_COEFF2829, 0x02f3fa4a},
+{4900000, DIF_BPF_COEFF3031, 0xf30ef046},
+{4900000, DIF_BPF_COEFF3233, 0xf361fb7a},
+{4900000, DIF_BPF_COEFF3435, 0x059b0de0},
+{4900000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 49_quant.dat*/
+
+
+/*case 5000000:*/
+/* BEGIN - DIF BPF register values from 50_quant.dat*/
+{5000000, DIF_BPF_COEFF01, 0xfffffffd},
+{5000000, DIF_BPF_COEFF23, 0xfff9fffa},
+{5000000, DIF_BPF_COEFF45, 0x000a002d},
+{5000000, DIF_BPF_COEFF67, 0x00570067},
+{5000000, DIF_BPF_COEFF89, 0x0037ffb5},
+{5000000, DIF_BPF_COEFF1011, 0xfefffe68},
+{5000000, DIF_BPF_COEFF1213, 0xfe62ff3d},
+{5000000, DIF_BPF_COEFF1415, 0x00ec02e3},
+{5000000, DIF_BPF_COEFF1617, 0x043503f6},
+{5000000, DIF_BPF_COEFF1819, 0x01befe05},
+{5000000, DIF_BPF_COEFF2021, 0xfa27f7ee},
+{5000000, DIF_BPF_COEFF2223, 0xf8c6fcf8},
+{5000000, DIF_BPF_COEFF2425, 0x034c0954},
+{5000000, DIF_BPF_COEFF2627, 0x0c5c0aa4},
+{5000000, DIF_BPF_COEFF2829, 0x044cfb7e},
+{5000000, DIF_BPF_COEFF3031, 0xf3b1f03f},
+{5000000, DIF_BPF_COEFF3233, 0xf2e2fae1},
+{5000000, DIF_BPF_COEFF3435, 0x05340dc0},
+{5000000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 50_quant.dat*/
+
+
+/*case 5100000:*/
+/* BEGIN - DIF BPF register values from 51_quant.dat*/
+{5100000, DIF_BPF_COEFF01, 0x0000fffd},
+{5100000, DIF_BPF_COEFF23, 0xfff8fff4},
+{5100000, DIF_BPF_COEFF45, 0xfffd001e},
+{5100000, DIF_BPF_COEFF67, 0x0051007b},
+{5100000, DIF_BPF_COEFF89, 0x006e0006},
+{5100000, DIF_BPF_COEFF1011, 0xff48fe7c},
+{5100000, DIF_BPF_COEFF1213, 0xfe1bfe9a},
+{5100000, DIF_BPF_COEFF1415, 0x001d023e},
+{5100000, DIF_BPF_COEFF1617, 0x04130488},
+{5100000, DIF_BPF_COEFF1819, 0x02e6ff5b},
+{5100000, DIF_BPF_COEFF2021, 0xfb1ef812},
+{5100000, DIF_BPF_COEFF2223, 0xf7f7fb7f},
+{5100000, DIF_BPF_COEFF2425, 0x01bc084e},
+{5100000, DIF_BPF_COEFF2627, 0x0c430b72},
+{5100000, DIF_BPF_COEFF2829, 0x059afcba},
+{5100000, DIF_BPF_COEFF3031, 0xf467f046},
+{5100000, DIF_BPF_COEFF3233, 0xf26cfa4a},
+{5100000, DIF_BPF_COEFF3435, 0x04cd0da0},
+{5100000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 51_quant.dat*/
+
+
+/*case 5200000:*/
+/* BEGIN - DIF BPF register values from 52_quant.dat*/
+{5200000, DIF_BPF_COEFF01, 0x0000fffe},
+{5200000, DIF_BPF_COEFF23, 0xfff8ffef},
+{5200000, DIF_BPF_COEFF45, 0xfff00009},
+{5200000, DIF_BPF_COEFF67, 0x003f007f},
+{5200000, DIF_BPF_COEFF89, 0x00980056},
+{5200000, DIF_BPF_COEFF1011, 0xffa5feb6},
+{5200000, DIF_BPF_COEFF1213, 0xfe00fe15},
+{5200000, DIF_BPF_COEFF1415, 0xff4b0170},
+{5200000, DIF_BPF_COEFF1617, 0x03b004d7},
+{5200000, DIF_BPF_COEFF1819, 0x03e800b9},
+{5200000, DIF_BPF_COEFF2021, 0xfc48f87f},
+{5200000, DIF_BPF_COEFF2223, 0xf768fa23},
+{5200000, DIF_BPF_COEFF2425, 0x0022071f},
+{5200000, DIF_BPF_COEFF2627, 0x0bf90c1b},
+{5200000, DIF_BPF_COEFF2829, 0x06dafdfd},
+{5200000, DIF_BPF_COEFF3031, 0xf52df05e},
+{5200000, DIF_BPF_COEFF3233, 0xf1fef9b5},
+{5200000, DIF_BPF_COEFF3435, 0x04640d7f},
+{5200000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 52_quant.dat*/
+
+
+/*case 5300000:*/
+/* BEGIN - DIF BPF register values from 53_quant.dat*/
+{5300000, DIF_BPF_COEFF01, 0x0000ffff},
+{5300000, DIF_BPF_COEFF23, 0xfff9ffee},
+{5300000, DIF_BPF_COEFF45, 0xffe6fff3},
+{5300000, DIF_BPF_COEFF67, 0x00250072},
+{5300000, DIF_BPF_COEFF89, 0x00af009c},
+{5300000, DIF_BPF_COEFF1011, 0x000cff10},
+{5300000, DIF_BPF_COEFF1213, 0xfe13fdb8},
+{5300000, DIF_BPF_COEFF1415, 0xfe870089},
+{5300000, DIF_BPF_COEFF1617, 0x031104e1},
+{5300000, DIF_BPF_COEFF1819, 0x04b8020f},
+{5300000, DIF_BPF_COEFF2021, 0xfd98f92f},
+{5300000, DIF_BPF_COEFF2223, 0xf71df8f0},
+{5300000, DIF_BPF_COEFF2425, 0xfe8805ce},
+{5300000, DIF_BPF_COEFF2627, 0x0b7e0c9c},
+{5300000, DIF_BPF_COEFF2829, 0x0808ff44},
+{5300000, DIF_BPF_COEFF3031, 0xf603f086},
+{5300000, DIF_BPF_COEFF3233, 0xf19af922},
+{5300000, DIF_BPF_COEFF3435, 0x03fb0d5e},
+{5300000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 53_quant.dat*/
+
+
+/*case 5400000:*/
+/* BEGIN - DIF BPF register values from 54_quant.dat*/
+{5400000, DIF_BPF_COEFF01, 0x00000001},
+{5400000, DIF_BPF_COEFF23, 0xfffcffef},
+{5400000, DIF_BPF_COEFF45, 0xffe0ffe0},
+{5400000, DIF_BPF_COEFF67, 0x00050056},
+{5400000, DIF_BPF_COEFF89, 0x00b000d1},
+{5400000, DIF_BPF_COEFF1011, 0x0071ff82},
+{5400000, DIF_BPF_COEFF1213, 0xfe53fd8c},
+{5400000, DIF_BPF_COEFF1415, 0xfddfff99},
+{5400000, DIF_BPF_COEFF1617, 0x024104a3},
+{5400000, DIF_BPF_COEFF1819, 0x054a034d},
+{5400000, DIF_BPF_COEFF2021, 0xff01fa1e},
+{5400000, DIF_BPF_COEFF2223, 0xf717f7ed},
+{5400000, DIF_BPF_COEFF2425, 0xfcf50461},
+{5400000, DIF_BPF_COEFF2627, 0x0ad50cf4},
+{5400000, DIF_BPF_COEFF2829, 0x0921008d},
+{5400000, DIF_BPF_COEFF3031, 0xf6e7f0bd},
+{5400000, DIF_BPF_COEFF3233, 0xf13ff891},
+{5400000, DIF_BPF_COEFF3435, 0x03920d3b},
+{5400000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 54_quant.dat*/
+
+
+/*case 5500000:*/
+/* BEGIN - DIF BPF register values from 55_quant.dat*/
+{5500000, DIF_BPF_COEFF01, 0x00010002},
+{5500000, DIF_BPF_COEFF23, 0xfffffff3},
+{5500000, DIF_BPF_COEFF45, 0xffdeffd1},
+{5500000, DIF_BPF_COEFF67, 0xffe5002f},
+{5500000, DIF_BPF_COEFF89, 0x009c00ed},
+{5500000, DIF_BPF_COEFF1011, 0x00cb0000},
+{5500000, DIF_BPF_COEFF1213, 0xfebafd94},
+{5500000, DIF_BPF_COEFF1415, 0xfd61feb0},
+{5500000, DIF_BPF_COEFF1617, 0x014d0422},
+{5500000, DIF_BPF_COEFF1819, 0x05970464},
+{5500000, DIF_BPF_COEFF2021, 0x0074fb41},
+{5500000, DIF_BPF_COEFF2223, 0xf759f721},
+{5500000, DIF_BPF_COEFF2425, 0xfb7502de},
+{5500000, DIF_BPF_COEFF2627, 0x0a000d21},
+{5500000, DIF_BPF_COEFF2829, 0x0a2201d4},
+{5500000, DIF_BPF_COEFF3031, 0xf7d9f104},
+{5500000, DIF_BPF_COEFF3233, 0xf0edf804},
+{5500000, DIF_BPF_COEFF3435, 0x03280d19},
+{5500000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 55_quant.dat*/
+
+
+/*case 5600000:*/
+/* BEGIN - DIF BPF register values from 56_quant.dat*/
+{5600000, DIF_BPF_COEFF01, 0x00010003},
+{5600000, DIF_BPF_COEFF23, 0x0003fffa},
+{5600000, DIF_BPF_COEFF45, 0xffe3ffc9},
+{5600000, DIF_BPF_COEFF67, 0xffc90002},
+{5600000, DIF_BPF_COEFF89, 0x007500ef},
+{5600000, DIF_BPF_COEFF1011, 0x010e007e},
+{5600000, DIF_BPF_COEFF1213, 0xff3dfdcf},
+{5600000, DIF_BPF_COEFF1415, 0xfd16fddd},
+{5600000, DIF_BPF_COEFF1617, 0x00440365},
+{5600000, DIF_BPF_COEFF1819, 0x059b0548},
+{5600000, DIF_BPF_COEFF2021, 0x01e3fc90},
+{5600000, DIF_BPF_COEFF2223, 0xf7dff691},
+{5600000, DIF_BPF_COEFF2425, 0xfa0f014d},
+{5600000, DIF_BPF_COEFF2627, 0x09020d23},
+{5600000, DIF_BPF_COEFF2829, 0x0b0a0318},
+{5600000, DIF_BPF_COEFF3031, 0xf8d7f15a},
+{5600000, DIF_BPF_COEFF3233, 0xf0a5f779},
+{5600000, DIF_BPF_COEFF3435, 0x02bd0cf6},
+{5600000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 56_quant.dat*/
+
+
+/*case 5700000:*/
+/* BEGIN - DIF BPF register values from 57_quant.dat*/
+{5700000, DIF_BPF_COEFF01, 0x00010003},
+{5700000, DIF_BPF_COEFF23, 0x00060001},
+{5700000, DIF_BPF_COEFF45, 0xffecffc9},
+{5700000, DIF_BPF_COEFF67, 0xffb4ffd4},
+{5700000, DIF_BPF_COEFF89, 0x004000d5},
+{5700000, DIF_BPF_COEFF1011, 0x013600f0},
+{5700000, DIF_BPF_COEFF1213, 0xffd3fe39},
+{5700000, DIF_BPF_COEFF1415, 0xfd04fd31},
+{5700000, DIF_BPF_COEFF1617, 0xff360277},
+{5700000, DIF_BPF_COEFF1819, 0x055605ef},
+{5700000, DIF_BPF_COEFF2021, 0x033efdfe},
+{5700000, DIF_BPF_COEFF2223, 0xf8a5f642},
+{5700000, DIF_BPF_COEFF2425, 0xf8cbffb6},
+{5700000, DIF_BPF_COEFF2627, 0x07e10cfb},
+{5700000, DIF_BPF_COEFF2829, 0x0bd50456},
+{5700000, DIF_BPF_COEFF3031, 0xf9dff1be},
+{5700000, DIF_BPF_COEFF3233, 0xf067f6f2},
+{5700000, DIF_BPF_COEFF3435, 0x02520cd2},
+{5700000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 57_quant.dat*/
+
+
+/*case 5800000:*/
+/* BEGIN - DIF BPF register values from 58_quant.dat*/
+{5800000, DIF_BPF_COEFF01, 0x00000003},
+{5800000, DIF_BPF_COEFF23, 0x00080009},
+{5800000, DIF_BPF_COEFF45, 0xfff8ffd2},
+{5800000, DIF_BPF_COEFF67, 0xffaaffac},
+{5800000, DIF_BPF_COEFF89, 0x000200a3},
+{5800000, DIF_BPF_COEFF1011, 0x013c014a},
+{5800000, DIF_BPF_COEFF1213, 0x006dfec9},
+{5800000, DIF_BPF_COEFF1415, 0xfd2bfcb7},
+{5800000, DIF_BPF_COEFF1617, 0xfe350165},
+{5800000, DIF_BPF_COEFF1819, 0x04cb0651},
+{5800000, DIF_BPF_COEFF2021, 0x0477ff7e},
+{5800000, DIF_BPF_COEFF2223, 0xf9a5f635},
+{5800000, DIF_BPF_COEFF2425, 0xf7b1fe20},
+{5800000, DIF_BPF_COEFF2627, 0x069f0ca8},
+{5800000, DIF_BPF_COEFF2829, 0x0c81058b},
+{5800000, DIF_BPF_COEFF3031, 0xfaf0f231},
+{5800000, DIF_BPF_COEFF3233, 0xf033f66d},
+{5800000, DIF_BPF_COEFF3435, 0x01e60cae},
+{5800000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 58_quant.dat*/
+
+
+/*case 5900000:*/
+/* BEGIN - DIF BPF register values from 59_quant.dat*/
+{5900000, DIF_BPF_COEFF01, 0x00000002},
+{5900000, DIF_BPF_COEFF23, 0x0009000e},
+{5900000, DIF_BPF_COEFF45, 0x0005ffe1},
+{5900000, DIF_BPF_COEFF67, 0xffacff90},
+{5900000, DIF_BPF_COEFF89, 0xffc5005f},
+{5900000, DIF_BPF_COEFF1011, 0x01210184},
+{5900000, DIF_BPF_COEFF1213, 0x00fcff72},
+{5900000, DIF_BPF_COEFF1415, 0xfd8afc77},
+{5900000, DIF_BPF_COEFF1617, 0xfd51003f},
+{5900000, DIF_BPF_COEFF1819, 0x04020669},
+{5900000, DIF_BPF_COEFF2021, 0x05830103},
+{5900000, DIF_BPF_COEFF2223, 0xfad7f66b},
+{5900000, DIF_BPF_COEFF2425, 0xf6c8fc93},
+{5900000, DIF_BPF_COEFF2627, 0x05430c2b},
+{5900000, DIF_BPF_COEFF2829, 0x0d0d06b5},
+{5900000, DIF_BPF_COEFF3031, 0xfc08f2b2},
+{5900000, DIF_BPF_COEFF3233, 0xf00af5ec},
+{5900000, DIF_BPF_COEFF3435, 0x017b0c89},
+{5900000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 59_quant.dat*/
+
+
+/*case 6000000:*/
+/* BEGIN - DIF BPF register values from 60_quant.dat*/
+{6000000, DIF_BPF_COEFF01, 0x00000001},
+{6000000, DIF_BPF_COEFF23, 0x00070012},
+{6000000, DIF_BPF_COEFF45, 0x0012fff5},
+{6000000, DIF_BPF_COEFF67, 0xffbaff82},
+{6000000, DIF_BPF_COEFF89, 0xff8e000f},
+{6000000, DIF_BPF_COEFF1011, 0x00e80198},
+{6000000, DIF_BPF_COEFF1213, 0x01750028},
+{6000000, DIF_BPF_COEFF1415, 0xfe18fc75},
+{6000000, DIF_BPF_COEFF1617, 0xfc99ff15},
+{6000000, DIF_BPF_COEFF1819, 0x03050636},
+{6000000, DIF_BPF_COEFF2021, 0x0656027f},
+{6000000, DIF_BPF_COEFF2223, 0xfc32f6e2},
+{6000000, DIF_BPF_COEFF2425, 0xf614fb17},
+{6000000, DIF_BPF_COEFF2627, 0x03d20b87},
+{6000000, DIF_BPF_COEFF2829, 0x0d7707d2},
+{6000000, DIF_BPF_COEFF3031, 0xfd26f341},
+{6000000, DIF_BPF_COEFF3233, 0xefeaf56f},
+{6000000, DIF_BPF_COEFF3435, 0x010f0c64},
+{6000000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 60_quant.dat*/
+
+
+/*case 6100000:*/
+/* BEGIN - DIF BPF register values from 61_quant.dat*/
+{6100000, DIF_BPF_COEFF01, 0xffff0000},
+{6100000, DIF_BPF_COEFF23, 0x00050012},
+{6100000, DIF_BPF_COEFF45, 0x001c000b},
+{6100000, DIF_BPF_COEFF67, 0xffd1ff84},
+{6100000, DIF_BPF_COEFF89, 0xff66ffbe},
+{6100000, DIF_BPF_COEFF1011, 0x00960184},
+{6100000, DIF_BPF_COEFF1213, 0x01cd00da},
+{6100000, DIF_BPF_COEFF1415, 0xfeccfcb2},
+{6100000, DIF_BPF_COEFF1617, 0xfc17fdf9},
+{6100000, DIF_BPF_COEFF1819, 0x01e005bc},
+{6100000, DIF_BPF_COEFF2021, 0x06e703e4},
+{6100000, DIF_BPF_COEFF2223, 0xfdabf798},
+{6100000, DIF_BPF_COEFF2425, 0xf599f9b3},
+{6100000, DIF_BPF_COEFF2627, 0x02510abd},
+{6100000, DIF_BPF_COEFF2829, 0x0dbf08df},
+{6100000, DIF_BPF_COEFF3031, 0xfe48f3dc},
+{6100000, DIF_BPF_COEFF3233, 0xefd5f4f6},
+{6100000, DIF_BPF_COEFF3435, 0x00a20c3e},
+{6100000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 61_quant.dat*/
+
+
+/*case 6200000:*/
+/* BEGIN - DIF BPF register values from 62_quant.dat*/
+{6200000, DIF_BPF_COEFF01, 0xfffffffe},
+{6200000, DIF_BPF_COEFF23, 0x0002000f},
+{6200000, DIF_BPF_COEFF45, 0x0021001f},
+{6200000, DIF_BPF_COEFF67, 0xfff0ff97},
+{6200000, DIF_BPF_COEFF89, 0xff50ff74},
+{6200000, DIF_BPF_COEFF1011, 0x0034014a},
+{6200000, DIF_BPF_COEFF1213, 0x01fa0179},
+{6200000, DIF_BPF_COEFF1415, 0xff97fd2a},
+{6200000, DIF_BPF_COEFF1617, 0xfbd3fcfa},
+{6200000, DIF_BPF_COEFF1819, 0x00a304fe},
+{6200000, DIF_BPF_COEFF2021, 0x07310525},
+{6200000, DIF_BPF_COEFF2223, 0xff37f886},
+{6200000, DIF_BPF_COEFF2425, 0xf55cf86e},
+{6200000, DIF_BPF_COEFF2627, 0x00c709d0},
+{6200000, DIF_BPF_COEFF2829, 0x0de209db},
+{6200000, DIF_BPF_COEFF3031, 0xff6df484},
+{6200000, DIF_BPF_COEFF3233, 0xefcbf481},
+{6200000, DIF_BPF_COEFF3435, 0x00360c18},
+{6200000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 62_quant.dat*/
+
+
+/*case 6300000:*/
+/* BEGIN - DIF BPF register values from 63_quant.dat*/
+{6300000, DIF_BPF_COEFF01, 0xfffffffd},
+{6300000, DIF_BPF_COEFF23, 0xfffe000a},
+{6300000, DIF_BPF_COEFF45, 0x0021002f},
+{6300000, DIF_BPF_COEFF67, 0x0010ffb8},
+{6300000, DIF_BPF_COEFF89, 0xff50ff3b},
+{6300000, DIF_BPF_COEFF1011, 0xffcc00f0},
+{6300000, DIF_BPF_COEFF1213, 0x01fa01fa},
+{6300000, DIF_BPF_COEFF1415, 0x0069fdd4},
+{6300000, DIF_BPF_COEFF1617, 0xfbd3fc26},
+{6300000, DIF_BPF_COEFF1819, 0xff5d0407},
+{6300000, DIF_BPF_COEFF2021, 0x07310638},
+{6300000, DIF_BPF_COEFF2223, 0x00c9f9a8},
+{6300000, DIF_BPF_COEFF2425, 0xf55cf74e},
+{6300000, DIF_BPF_COEFF2627, 0xff3908c3},
+{6300000, DIF_BPF_COEFF2829, 0x0de20ac3},
+{6300000, DIF_BPF_COEFF3031, 0x0093f537},
+{6300000, DIF_BPF_COEFF3233, 0xefcbf410},
+{6300000, DIF_BPF_COEFF3435, 0xffca0bf2},
+{6300000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 63_quant.dat*/
+
+
+/*case 6400000:*/
+/* BEGIN - DIF BPF register values from 64_quant.dat*/
+{6400000, DIF_BPF_COEFF01, 0xfffffffd},
+{6400000, DIF_BPF_COEFF23, 0xfffb0003},
+{6400000, DIF_BPF_COEFF45, 0x001c0037},
+{6400000, DIF_BPF_COEFF67, 0x002fffe2},
+{6400000, DIF_BPF_COEFF89, 0xff66ff17},
+{6400000, DIF_BPF_COEFF1011, 0xff6a007e},
+{6400000, DIF_BPF_COEFF1213, 0x01cd0251},
+{6400000, DIF_BPF_COEFF1415, 0x0134fea5},
+{6400000, DIF_BPF_COEFF1617, 0xfc17fb8b},
+{6400000, DIF_BPF_COEFF1819, 0xfe2002e0},
+{6400000, DIF_BPF_COEFF2021, 0x06e70713},
+{6400000, DIF_BPF_COEFF2223, 0x0255faf5},
+{6400000, DIF_BPF_COEFF2425, 0xf599f658},
+{6400000, DIF_BPF_COEFF2627, 0xfdaf0799},
+{6400000, DIF_BPF_COEFF2829, 0x0dbf0b96},
+{6400000, DIF_BPF_COEFF3031, 0x01b8f5f5},
+{6400000, DIF_BPF_COEFF3233, 0xefd5f3a3},
+{6400000, DIF_BPF_COEFF3435, 0xff5e0bca},
+{6400000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 64_quant.dat*/
+
+
+/*case 6500000:*/
+/* BEGIN - DIF BPF register values from 65_quant.dat*/
+{6500000, DIF_BPF_COEFF01, 0x0000fffd},
+{6500000, DIF_BPF_COEFF23, 0xfff9fffb},
+{6500000, DIF_BPF_COEFF45, 0x00120037},
+{6500000, DIF_BPF_COEFF67, 0x00460010},
+{6500000, DIF_BPF_COEFF89, 0xff8eff0f},
+{6500000, DIF_BPF_COEFF1011, 0xff180000},
+{6500000, DIF_BPF_COEFF1213, 0x01750276},
+{6500000, DIF_BPF_COEFF1415, 0x01e8ff8d},
+{6500000, DIF_BPF_COEFF1617, 0xfc99fb31},
+{6500000, DIF_BPF_COEFF1819, 0xfcfb0198},
+{6500000, DIF_BPF_COEFF2021, 0x065607ad},
+{6500000, DIF_BPF_COEFF2223, 0x03cefc64},
+{6500000, DIF_BPF_COEFF2425, 0xf614f592},
+{6500000, DIF_BPF_COEFF2627, 0xfc2e0656},
+{6500000, DIF_BPF_COEFF2829, 0x0d770c52},
+{6500000, DIF_BPF_COEFF3031, 0x02daf6bd},
+{6500000, DIF_BPF_COEFF3233, 0xefeaf33b},
+{6500000, DIF_BPF_COEFF3435, 0xfef10ba3},
+{6500000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 65_quant.dat*/
+
+
+/*case 6600000:*/
+/* BEGIN - DIF BPF register values from 66_quant.dat*/
+{6600000, DIF_BPF_COEFF01, 0x0000fffe},
+{6600000, DIF_BPF_COEFF23, 0xfff7fff5},
+{6600000, DIF_BPF_COEFF45, 0x0005002f},
+{6600000, DIF_BPF_COEFF67, 0x0054003c},
+{6600000, DIF_BPF_COEFF89, 0xffc5ff22},
+{6600000, DIF_BPF_COEFF1011, 0xfedfff82},
+{6600000, DIF_BPF_COEFF1213, 0x00fc0267},
+{6600000, DIF_BPF_COEFF1415, 0x0276007e},
+{6600000, DIF_BPF_COEFF1617, 0xfd51fb1c},
+{6600000, DIF_BPF_COEFF1819, 0xfbfe003e},
+{6600000, DIF_BPF_COEFF2021, 0x05830802},
+{6600000, DIF_BPF_COEFF2223, 0x0529fdec},
+{6600000, DIF_BPF_COEFF2425, 0xf6c8f4fe},
+{6600000, DIF_BPF_COEFF2627, 0xfabd04ff},
+{6600000, DIF_BPF_COEFF2829, 0x0d0d0cf6},
+{6600000, DIF_BPF_COEFF3031, 0x03f8f78f},
+{6600000, DIF_BPF_COEFF3233, 0xf00af2d7},
+{6600000, DIF_BPF_COEFF3435, 0xfe850b7b},
+{6600000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 66_quant.dat*/
+
+
+/*case 6700000:*/
+/* BEGIN - DIF BPF register values from 67_quant.dat*/
+{6700000, DIF_BPF_COEFF01, 0x0000ffff},
+{6700000, DIF_BPF_COEFF23, 0xfff8fff0},
+{6700000, DIF_BPF_COEFF45, 0xfff80020},
+{6700000, DIF_BPF_COEFF67, 0x00560060},
+{6700000, DIF_BPF_COEFF89, 0x0002ff4e},
+{6700000, DIF_BPF_COEFF1011, 0xfec4ff10},
+{6700000, DIF_BPF_COEFF1213, 0x006d0225},
+{6700000, DIF_BPF_COEFF1415, 0x02d50166},
+{6700000, DIF_BPF_COEFF1617, 0xfe35fb4e},
+{6700000, DIF_BPF_COEFF1819, 0xfb35fee1},
+{6700000, DIF_BPF_COEFF2021, 0x0477080e},
+{6700000, DIF_BPF_COEFF2223, 0x065bff82},
+{6700000, DIF_BPF_COEFF2425, 0xf7b1f4a0},
+{6700000, DIF_BPF_COEFF2627, 0xf9610397},
+{6700000, DIF_BPF_COEFF2829, 0x0c810d80},
+{6700000, DIF_BPF_COEFF3031, 0x0510f869},
+{6700000, DIF_BPF_COEFF3233, 0xf033f278},
+{6700000, DIF_BPF_COEFF3435, 0xfe1a0b52},
+{6700000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 67_quant.dat*/
+
+
+/*case 6800000:*/
+/* BEGIN - DIF BPF register values from 68_quant.dat*/
+{6800000, DIF_BPF_COEFF01, 0x00010000},
+{6800000, DIF_BPF_COEFF23, 0xfffaffee},
+{6800000, DIF_BPF_COEFF45, 0xffec000c},
+{6800000, DIF_BPF_COEFF67, 0x004c0078},
+{6800000, DIF_BPF_COEFF89, 0x0040ff8e},
+{6800000, DIF_BPF_COEFF1011, 0xfecafeb6},
+{6800000, DIF_BPF_COEFF1213, 0xffd301b6},
+{6800000, DIF_BPF_COEFF1415, 0x02fc0235},
+{6800000, DIF_BPF_COEFF1617, 0xff36fbc5},
+{6800000, DIF_BPF_COEFF1819, 0xfaaafd90},
+{6800000, DIF_BPF_COEFF2021, 0x033e07d2},
+{6800000, DIF_BPF_COEFF2223, 0x075b011b},
+{6800000, DIF_BPF_COEFF2425, 0xf8cbf47a},
+{6800000, DIF_BPF_COEFF2627, 0xf81f0224},
+{6800000, DIF_BPF_COEFF2829, 0x0bd50def},
+{6800000, DIF_BPF_COEFF3031, 0x0621f94b},
+{6800000, DIF_BPF_COEFF3233, 0xf067f21e},
+{6800000, DIF_BPF_COEFF3435, 0xfdae0b29},
+{6800000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 68_quant.dat*/
+
+
+/*case 6900000:*/
+/* BEGIN - DIF BPF register values from 69_quant.dat*/
+{6900000, DIF_BPF_COEFF01, 0x00010001},
+{6900000, DIF_BPF_COEFF23, 0xfffdffef},
+{6900000, DIF_BPF_COEFF45, 0xffe3fff6},
+{6900000, DIF_BPF_COEFF67, 0x0037007f},
+{6900000, DIF_BPF_COEFF89, 0x0075ffdc},
+{6900000, DIF_BPF_COEFF1011, 0xfef2fe7c},
+{6900000, DIF_BPF_COEFF1213, 0xff3d0122},
+{6900000, DIF_BPF_COEFF1415, 0x02ea02dd},
+{6900000, DIF_BPF_COEFF1617, 0x0044fc79},
+{6900000, DIF_BPF_COEFF1819, 0xfa65fc5d},
+{6900000, DIF_BPF_COEFF2021, 0x01e3074e},
+{6900000, DIF_BPF_COEFF2223, 0x082102ad},
+{6900000, DIF_BPF_COEFF2425, 0xfa0ff48c},
+{6900000, DIF_BPF_COEFF2627, 0xf6fe00a9},
+{6900000, DIF_BPF_COEFF2829, 0x0b0a0e43},
+{6900000, DIF_BPF_COEFF3031, 0x0729fa33},
+{6900000, DIF_BPF_COEFF3233, 0xf0a5f1c9},
+{6900000, DIF_BPF_COEFF3435, 0xfd430b00},
+{6900000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 69_quant.dat*/
+
+
+/*case 7000000:*/
+/* BEGIN - DIF BPF register values from 70_quant.dat*/
+{7000000, DIF_BPF_COEFF01, 0x00010002},
+{7000000, DIF_BPF_COEFF23, 0x0001fff3},
+{7000000, DIF_BPF_COEFF45, 0xffdeffe2},
+{7000000, DIF_BPF_COEFF67, 0x001b0076},
+{7000000, DIF_BPF_COEFF89, 0x009c002d},
+{7000000, DIF_BPF_COEFF1011, 0xff35fe68},
+{7000000, DIF_BPF_COEFF1213, 0xfeba0076},
+{7000000, DIF_BPF_COEFF1415, 0x029f0352},
+{7000000, DIF_BPF_COEFF1617, 0x014dfd60},
+{7000000, DIF_BPF_COEFF1819, 0xfa69fb53},
+{7000000, DIF_BPF_COEFF2021, 0x00740688},
+{7000000, DIF_BPF_COEFF2223, 0x08a7042d},
+{7000000, DIF_BPF_COEFF2425, 0xfb75f4d6},
+{7000000, DIF_BPF_COEFF2627, 0xf600ff2d},
+{7000000, DIF_BPF_COEFF2829, 0x0a220e7a},
+{7000000, DIF_BPF_COEFF3031, 0x0827fb22},
+{7000000, DIF_BPF_COEFF3233, 0xf0edf17a},
+{7000000, DIF_BPF_COEFF3435, 0xfcd80ad6},
+{7000000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 70_quant.dat*/
+
+
+/*case 7100000:*/
+/* BEGIN - DIF BPF register values from 71_quant.dat*/
+{7100000, DIF_BPF_COEFF01, 0x00000003},
+{7100000, DIF_BPF_COEFF23, 0x0004fff9},
+{7100000, DIF_BPF_COEFF45, 0xffe0ffd2},
+{7100000, DIF_BPF_COEFF67, 0xfffb005e},
+{7100000, DIF_BPF_COEFF89, 0x00b0007a},
+{7100000, DIF_BPF_COEFF1011, 0xff8ffe7c},
+{7100000, DIF_BPF_COEFF1213, 0xfe53ffc1},
+{7100000, DIF_BPF_COEFF1415, 0x0221038c},
+{7100000, DIF_BPF_COEFF1617, 0x0241fe6e},
+{7100000, DIF_BPF_COEFF1819, 0xfab6fa80},
+{7100000, DIF_BPF_COEFF2021, 0xff010587},
+{7100000, DIF_BPF_COEFF2223, 0x08e90590},
+{7100000, DIF_BPF_COEFF2425, 0xfcf5f556},
+{7100000, DIF_BPF_COEFF2627, 0xf52bfdb3},
+{7100000, DIF_BPF_COEFF2829, 0x09210e95},
+{7100000, DIF_BPF_COEFF3031, 0x0919fc15},
+{7100000, DIF_BPF_COEFF3233, 0xf13ff12f},
+{7100000, DIF_BPF_COEFF3435, 0xfc6e0aab},
+{7100000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 71_quant.dat*/
+
+
+/*case 7200000:*/
+/* BEGIN - DIF BPF register values from 72_quant.dat*/
+{7200000, DIF_BPF_COEFF01, 0x00000003},
+{7200000, DIF_BPF_COEFF23, 0x00070000},
+{7200000, DIF_BPF_COEFF45, 0xffe6ffc9},
+{7200000, DIF_BPF_COEFF67, 0xffdb0039},
+{7200000, DIF_BPF_COEFF89, 0x00af00b8},
+{7200000, DIF_BPF_COEFF1011, 0xfff4feb6},
+{7200000, DIF_BPF_COEFF1213, 0xfe13ff10},
+{7200000, DIF_BPF_COEFF1415, 0x01790388},
+{7200000, DIF_BPF_COEFF1617, 0x0311ff92},
+{7200000, DIF_BPF_COEFF1819, 0xfb48f9ed},
+{7200000, DIF_BPF_COEFF2021, 0xfd980453},
+{7200000, DIF_BPF_COEFF2223, 0x08e306cd},
+{7200000, DIF_BPF_COEFF2425, 0xfe88f60a},
+{7200000, DIF_BPF_COEFF2627, 0xf482fc40},
+{7200000, DIF_BPF_COEFF2829, 0x08080e93},
+{7200000, DIF_BPF_COEFF3031, 0x09fdfd0c},
+{7200000, DIF_BPF_COEFF3233, 0xf19af0ea},
+{7200000, DIF_BPF_COEFF3435, 0xfc050a81},
+{7200000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 72_quant.dat*/
+
+
+/*case 7300000:*/
+/* BEGIN - DIF BPF register values from 73_quant.dat*/
+{7300000, DIF_BPF_COEFF01, 0x00000002},
+{7300000, DIF_BPF_COEFF23, 0x00080008},
+{7300000, DIF_BPF_COEFF45, 0xfff0ffc9},
+{7300000, DIF_BPF_COEFF67, 0xffc1000d},
+{7300000, DIF_BPF_COEFF89, 0x009800e2},
+{7300000, DIF_BPF_COEFF1011, 0x005bff10},
+{7300000, DIF_BPF_COEFF1213, 0xfe00fe74},
+{7300000, DIF_BPF_COEFF1415, 0x00b50345},
+{7300000, DIF_BPF_COEFF1617, 0x03b000bc},
+{7300000, DIF_BPF_COEFF1819, 0xfc18f9a1},
+{7300000, DIF_BPF_COEFF2021, 0xfc4802f9},
+{7300000, DIF_BPF_COEFF2223, 0x089807dc},
+{7300000, DIF_BPF_COEFF2425, 0x0022f6f0},
+{7300000, DIF_BPF_COEFF2627, 0xf407fada},
+{7300000, DIF_BPF_COEFF2829, 0x06da0e74},
+{7300000, DIF_BPF_COEFF3031, 0x0ad3fe06},
+{7300000, DIF_BPF_COEFF3233, 0xf1fef0ab},
+{7300000, DIF_BPF_COEFF3435, 0xfb9c0a55},
+{7300000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 73_quant.dat*/
+
+
+/*case 7400000:*/
+/* BEGIN - DIF BPF register values from 74_quant.dat*/
+{7400000, DIF_BPF_COEFF01, 0x00000001},
+{7400000, DIF_BPF_COEFF23, 0x0008000e},
+{7400000, DIF_BPF_COEFF45, 0xfffdffd0},
+{7400000, DIF_BPF_COEFF67, 0xffafffdf},
+{7400000, DIF_BPF_COEFF89, 0x006e00f2},
+{7400000, DIF_BPF_COEFF1011, 0x00b8ff82},
+{7400000, DIF_BPF_COEFF1213, 0xfe1bfdf8},
+{7400000, DIF_BPF_COEFF1415, 0xffe302c8},
+{7400000, DIF_BPF_COEFF1617, 0x041301dc},
+{7400000, DIF_BPF_COEFF1819, 0xfd1af99e},
+{7400000, DIF_BPF_COEFF2021, 0xfb1e0183},
+{7400000, DIF_BPF_COEFF2223, 0x080908b5},
+{7400000, DIF_BPF_COEFF2425, 0x01bcf801},
+{7400000, DIF_BPF_COEFF2627, 0xf3bdf985},
+{7400000, DIF_BPF_COEFF2829, 0x059a0e38},
+{7400000, DIF_BPF_COEFF3031, 0x0b99ff03},
+{7400000, DIF_BPF_COEFF3233, 0xf26cf071},
+{7400000, DIF_BPF_COEFF3435, 0xfb330a2a},
+{7400000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 74_quant.dat*/
+
+
+/*case 7500000:*/
+/* BEGIN - DIF BPF register values from 75_quant.dat*/
+{7500000, DIF_BPF_COEFF01, 0xffff0000},
+{7500000, DIF_BPF_COEFF23, 0x00070011},
+{7500000, DIF_BPF_COEFF45, 0x000affdf},
+{7500000, DIF_BPF_COEFF67, 0xffa9ffb5},
+{7500000, DIF_BPF_COEFF89, 0x003700e6},
+{7500000, DIF_BPF_COEFF1011, 0x01010000},
+{7500000, DIF_BPF_COEFF1213, 0xfe62fda8},
+{7500000, DIF_BPF_COEFF1415, 0xff140219},
+{7500000, DIF_BPF_COEFF1617, 0x043502e1},
+{7500000, DIF_BPF_COEFF1819, 0xfe42f9e6},
+{7500000, DIF_BPF_COEFF2021, 0xfa270000},
+{7500000, DIF_BPF_COEFF2223, 0x073a0953},
+{7500000, DIF_BPF_COEFF2425, 0x034cf939},
+{7500000, DIF_BPF_COEFF2627, 0xf3a4f845},
+{7500000, DIF_BPF_COEFF2829, 0x044c0de1},
+{7500000, DIF_BPF_COEFF3031, 0x0c4f0000},
+{7500000, DIF_BPF_COEFF3233, 0xf2e2f03c},
+{7500000, DIF_BPF_COEFF3435, 0xfacc09fe},
+{7500000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 75_quant.dat*/
+
+
+/*case 7600000:*/
+/* BEGIN - DIF BPF register values from 76_quant.dat*/
+{7600000, DIF_BPF_COEFF01, 0xffffffff},
+{7600000, DIF_BPF_COEFF23, 0x00040012},
+{7600000, DIF_BPF_COEFF45, 0x0016fff3},
+{7600000, DIF_BPF_COEFF67, 0xffafff95},
+{7600000, DIF_BPF_COEFF89, 0xfff900c0},
+{7600000, DIF_BPF_COEFF1011, 0x0130007e},
+{7600000, DIF_BPF_COEFF1213, 0xfecefd89},
+{7600000, DIF_BPF_COEFF1415, 0xfe560146},
+{7600000, DIF_BPF_COEFF1617, 0x041303bc},
+{7600000, DIF_BPF_COEFF1819, 0xff81fa76},
+{7600000, DIF_BPF_COEFF2021, 0xf96cfe7d},
+{7600000, DIF_BPF_COEFF2223, 0x063209b1},
+{7600000, DIF_BPF_COEFF2425, 0x04c9fa93},
+{7600000, DIF_BPF_COEFF2627, 0xf3bdf71e},
+{7600000, DIF_BPF_COEFF2829, 0x02f30d6e},
+{7600000, DIF_BPF_COEFF3031, 0x0cf200fd},
+{7600000, DIF_BPF_COEFF3233, 0xf361f00e},
+{7600000, DIF_BPF_COEFF3435, 0xfa6509d1},
+{7600000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 76_quant.dat*/
+
+
+/*case 7700000:*/
+/* BEGIN - DIF BPF register values from 77_quant.dat*/
+{7700000, DIF_BPF_COEFF01, 0xfffffffe},
+{7700000, DIF_BPF_COEFF23, 0x00010010},
+{7700000, DIF_BPF_COEFF45, 0x001e0008},
+{7700000, DIF_BPF_COEFF67, 0xffc1ff84},
+{7700000, DIF_BPF_COEFF89, 0xffbc0084},
+{7700000, DIF_BPF_COEFF1011, 0x013e00f0},
+{7700000, DIF_BPF_COEFF1213, 0xff56fd9f},
+{7700000, DIF_BPF_COEFF1415, 0xfdb8005c},
+{7700000, DIF_BPF_COEFF1617, 0x03b00460},
+{7700000, DIF_BPF_COEFF1819, 0x00c7fb45},
+{7700000, DIF_BPF_COEFF2021, 0xf8f4fd07},
+{7700000, DIF_BPF_COEFF2223, 0x04fa09ce},
+{7700000, DIF_BPF_COEFF2425, 0x062afc07},
+{7700000, DIF_BPF_COEFF2627, 0xf407f614},
+{7700000, DIF_BPF_COEFF2829, 0x01920ce0},
+{7700000, DIF_BPF_COEFF3031, 0x0d8301fa},
+{7700000, DIF_BPF_COEFF3233, 0xf3e8efe5},
+{7700000, DIF_BPF_COEFF3435, 0xfa0009a4},
+{7700000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 77_quant.dat*/
+
+
+/*case 7800000:*/
+/* BEGIN - DIF BPF register values from 78_quant.dat*/
+{7800000, DIF_BPF_COEFF01, 0x0000fffd},
+{7800000, DIF_BPF_COEFF23, 0xfffd000b},
+{7800000, DIF_BPF_COEFF45, 0x0022001d},
+{7800000, DIF_BPF_COEFF67, 0xffdbff82},
+{7800000, DIF_BPF_COEFF89, 0xff870039},
+{7800000, DIF_BPF_COEFF1011, 0x012a014a},
+{7800000, DIF_BPF_COEFF1213, 0xffedfde7},
+{7800000, DIF_BPF_COEFF1415, 0xfd47ff6b},
+{7800000, DIF_BPF_COEFF1617, 0x031104c6},
+{7800000, DIF_BPF_COEFF1819, 0x0202fc4c},
+{7800000, DIF_BPF_COEFF2021, 0xf8c6fbad},
+{7800000, DIF_BPF_COEFF2223, 0x039909a7},
+{7800000, DIF_BPF_COEFF2425, 0x0767fd8e},
+{7800000, DIF_BPF_COEFF2627, 0xf482f52b},
+{7800000, DIF_BPF_COEFF2829, 0x002d0c39},
+{7800000, DIF_BPF_COEFF3031, 0x0e0002f4},
+{7800000, DIF_BPF_COEFF3233, 0xf477efc2},
+{7800000, DIF_BPF_COEFF3435, 0xf99b0977},
+{7800000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 78_quant.dat*/
+
+
+/*case 7900000:*/
+/* BEGIN - DIF BPF register values from 79_quant.dat*/
+{7900000, DIF_BPF_COEFF01, 0x0000fffd},
+{7900000, DIF_BPF_COEFF23, 0xfffa0004},
+{7900000, DIF_BPF_COEFF45, 0x0020002d},
+{7900000, DIF_BPF_COEFF67, 0xfffbff91},
+{7900000, DIF_BPF_COEFF89, 0xff61ffe8},
+{7900000, DIF_BPF_COEFF1011, 0x00f70184},
+{7900000, DIF_BPF_COEFF1213, 0x0086fe5c},
+{7900000, DIF_BPF_COEFF1415, 0xfd0bfe85},
+{7900000, DIF_BPF_COEFF1617, 0x024104e5},
+{7900000, DIF_BPF_COEFF1819, 0x0323fd7d},
+{7900000, DIF_BPF_COEFF2021, 0xf8e2fa79},
+{7900000, DIF_BPF_COEFF2223, 0x021d093f},
+{7900000, DIF_BPF_COEFF2425, 0x0879ff22},
+{7900000, DIF_BPF_COEFF2627, 0xf52bf465},
+{7900000, DIF_BPF_COEFF2829, 0xfec70b79},
+{7900000, DIF_BPF_COEFF3031, 0x0e6803eb},
+{7900000, DIF_BPF_COEFF3233, 0xf50defa5},
+{7900000, DIF_BPF_COEFF3435, 0xf937094a},
+{7900000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 79_quant.dat*/
+
+
+/*case 8000000:*/
+/* BEGIN - DIF BPF register values from 80_quant.dat*/
+{8000000, DIF_BPF_COEFF01, 0x0000fffe},
+{8000000, DIF_BPF_COEFF23, 0xfff8fffd},
+{8000000, DIF_BPF_COEFF45, 0x00190036},
+{8000000, DIF_BPF_COEFF67, 0x001bffaf},
+{8000000, DIF_BPF_COEFF89, 0xff4fff99},
+{8000000, DIF_BPF_COEFF1011, 0x00aa0198},
+{8000000, DIF_BPF_COEFF1213, 0x0112fef3},
+{8000000, DIF_BPF_COEFF1415, 0xfd09fdb9},
+{8000000, DIF_BPF_COEFF1617, 0x014d04be},
+{8000000, DIF_BPF_COEFF1819, 0x041bfecc},
+{8000000, DIF_BPF_COEFF2021, 0xf947f978},
+{8000000, DIF_BPF_COEFF2223, 0x00900897},
+{8000000, DIF_BPF_COEFF2425, 0x095a00b9},
+{8000000, DIF_BPF_COEFF2627, 0xf600f3c5},
+{8000000, DIF_BPF_COEFF2829, 0xfd650aa3},
+{8000000, DIF_BPF_COEFF3031, 0x0ebc04de},
+{8000000, DIF_BPF_COEFF3233, 0xf5aaef8e},
+{8000000, DIF_BPF_COEFF3435, 0xf8d5091c},
+{8000000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 80_quant.dat*/
+
+
+/*case 8100000:*/
+/* BEGIN - DIF BPF register values from 81_quant.dat*/
+{8100000, DIF_BPF_COEFF01, 0x0000ffff},
+{8100000, DIF_BPF_COEFF23, 0xfff7fff6},
+{8100000, DIF_BPF_COEFF45, 0x000e0038},
+{8100000, DIF_BPF_COEFF67, 0x0037ffd7},
+{8100000, DIF_BPF_COEFF89, 0xff52ff56},
+{8100000, DIF_BPF_COEFF1011, 0x004b0184},
+{8100000, DIF_BPF_COEFF1213, 0x0186ffa1},
+{8100000, DIF_BPF_COEFF1415, 0xfd40fd16},
+{8100000, DIF_BPF_COEFF1617, 0x00440452},
+{8100000, DIF_BPF_COEFF1819, 0x04de0029},
+{8100000, DIF_BPF_COEFF2021, 0xf9f2f8b2},
+{8100000, DIF_BPF_COEFF2223, 0xfefe07b5},
+{8100000, DIF_BPF_COEFF2425, 0x0a05024d},
+{8100000, DIF_BPF_COEFF2627, 0xf6fef34d},
+{8100000, DIF_BPF_COEFF2829, 0xfc0a09b8},
+{8100000, DIF_BPF_COEFF3031, 0x0efa05cd},
+{8100000, DIF_BPF_COEFF3233, 0xf64eef7d},
+{8100000, DIF_BPF_COEFF3435, 0xf87308ed},
+{8100000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 81_quant.dat*/
+
+
+/*case 8200000:*/
+/* BEGIN - DIF BPF register values from 82_quant.dat*/
+{8200000, DIF_BPF_COEFF01, 0x00010000},
+{8200000, DIF_BPF_COEFF23, 0xfff8fff0},
+{8200000, DIF_BPF_COEFF45, 0x00000031},
+{8200000, DIF_BPF_COEFF67, 0x004c0005},
+{8200000, DIF_BPF_COEFF89, 0xff6aff27},
+{8200000, DIF_BPF_COEFF1011, 0xffe4014a},
+{8200000, DIF_BPF_COEFF1213, 0x01d70057},
+{8200000, DIF_BPF_COEFF1415, 0xfdacfca6},
+{8200000, DIF_BPF_COEFF1617, 0xff3603a7},
+{8200000, DIF_BPF_COEFF1819, 0x05610184},
+{8200000, DIF_BPF_COEFF2021, 0xfadbf82e},
+{8200000, DIF_BPF_COEFF2223, 0xfd74069f},
+{8200000, DIF_BPF_COEFF2425, 0x0a7503d6},
+{8200000, DIF_BPF_COEFF2627, 0xf81ff2ff},
+{8200000, DIF_BPF_COEFF2829, 0xfab808b9},
+{8200000, DIF_BPF_COEFF3031, 0x0f2306b5},
+{8200000, DIF_BPF_COEFF3233, 0xf6f9ef72},
+{8200000, DIF_BPF_COEFF3435, 0xf81308bf},
+{8200000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 82_quant.dat*/
+
+
+/*case 8300000:*/
+/* BEGIN - DIF BPF register values from 83_quant.dat*/
+{8300000, DIF_BPF_COEFF01, 0x00010001},
+{8300000, DIF_BPF_COEFF23, 0xfffbffee},
+{8300000, DIF_BPF_COEFF45, 0xfff30022},
+{8300000, DIF_BPF_COEFF67, 0x00560032},
+{8300000, DIF_BPF_COEFF89, 0xff95ff10},
+{8300000, DIF_BPF_COEFF1011, 0xff8000f0},
+{8300000, DIF_BPF_COEFF1213, 0x01fe0106},
+{8300000, DIF_BPF_COEFF1415, 0xfe46fc71},
+{8300000, DIF_BPF_COEFF1617, 0xfe3502c7},
+{8300000, DIF_BPF_COEFF1819, 0x059e02ce},
+{8300000, DIF_BPF_COEFF2021, 0xfbf9f7f2},
+{8300000, DIF_BPF_COEFF2223, 0xfbff055b},
+{8300000, DIF_BPF_COEFF2425, 0x0aa9054c},
+{8300000, DIF_BPF_COEFF2627, 0xf961f2db},
+{8300000, DIF_BPF_COEFF2829, 0xf97507aa},
+{8300000, DIF_BPF_COEFF3031, 0x0f350797},
+{8300000, DIF_BPF_COEFF3233, 0xf7a9ef6d},
+{8300000, DIF_BPF_COEFF3435, 0xf7b40890},
+{8300000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 83_quant.dat*/
+
+
+/*case 8400000:*/
+/* BEGIN - DIF BPF register values from 84_quant.dat*/
+{8400000, DIF_BPF_COEFF01, 0x00010002},
+{8400000, DIF_BPF_COEFF23, 0xfffeffee},
+{8400000, DIF_BPF_COEFF45, 0xffe8000f},
+{8400000, DIF_BPF_COEFF67, 0x00540058},
+{8400000, DIF_BPF_COEFF89, 0xffcdff14},
+{8400000, DIF_BPF_COEFF1011, 0xff29007e},
+{8400000, DIF_BPF_COEFF1213, 0x01f6019e},
+{8400000, DIF_BPF_COEFF1415, 0xff01fc7c},
+{8400000, DIF_BPF_COEFF1617, 0xfd5101bf},
+{8400000, DIF_BPF_COEFF1819, 0x059203f6},
+{8400000, DIF_BPF_COEFF2021, 0xfd41f7fe},
+{8400000, DIF_BPF_COEFF2223, 0xfaa903f3},
+{8400000, DIF_BPF_COEFF2425, 0x0a9e06a9},
+{8400000, DIF_BPF_COEFF2627, 0xfabdf2e2},
+{8400000, DIF_BPF_COEFF2829, 0xf842068b},
+{8400000, DIF_BPF_COEFF3031, 0x0f320871},
+{8400000, DIF_BPF_COEFF3233, 0xf85eef6e},
+{8400000, DIF_BPF_COEFF3435, 0xf7560860},
+{8400000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 84_quant.dat*/
+
+
+/*case 8500000:*/
+/* BEGIN - DIF BPF register values from 85_quant.dat*/
+{8500000, DIF_BPF_COEFF01, 0x00000003},
+{8500000, DIF_BPF_COEFF23, 0x0002fff2},
+{8500000, DIF_BPF_COEFF45, 0xffe1fff9},
+{8500000, DIF_BPF_COEFF67, 0x00460073},
+{8500000, DIF_BPF_COEFF89, 0x000bff34},
+{8500000, DIF_BPF_COEFF1011, 0xfee90000},
+{8500000, DIF_BPF_COEFF1213, 0x01c10215},
+{8500000, DIF_BPF_COEFF1415, 0xffd0fcc5},
+{8500000, DIF_BPF_COEFF1617, 0xfc99009d},
+{8500000, DIF_BPF_COEFF1819, 0x053d04f1},
+{8500000, DIF_BPF_COEFF2021, 0xfea5f853},
+{8500000, DIF_BPF_COEFF2223, 0xf97d0270},
+{8500000, DIF_BPF_COEFF2425, 0x0a5607e4},
+{8500000, DIF_BPF_COEFF2627, 0xfc2ef314},
+{8500000, DIF_BPF_COEFF2829, 0xf723055f},
+{8500000, DIF_BPF_COEFF3031, 0x0f180943},
+{8500000, DIF_BPF_COEFF3233, 0xf919ef75},
+{8500000, DIF_BPF_COEFF3435, 0xf6fa0830},
+{8500000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 85_quant.dat*/
+
+
+/*case 8600000:*/
+/* BEGIN - DIF BPF register values from 86_quant.dat*/
+{8600000, DIF_BPF_COEFF01, 0x00000003},
+{8600000, DIF_BPF_COEFF23, 0x0005fff8},
+{8600000, DIF_BPF_COEFF45, 0xffdeffe4},
+{8600000, DIF_BPF_COEFF67, 0x002f007f},
+{8600000, DIF_BPF_COEFF89, 0x0048ff6b},
+{8600000, DIF_BPF_COEFF1011, 0xfec7ff82},
+{8600000, DIF_BPF_COEFF1213, 0x0163025f},
+{8600000, DIF_BPF_COEFF1415, 0x00a2fd47},
+{8600000, DIF_BPF_COEFF1617, 0xfc17ff73},
+{8600000, DIF_BPF_COEFF1819, 0x04a405b2},
+{8600000, DIF_BPF_COEFF2021, 0x0017f8ed},
+{8600000, DIF_BPF_COEFF2223, 0xf88500dc},
+{8600000, DIF_BPF_COEFF2425, 0x09d208f9},
+{8600000, DIF_BPF_COEFF2627, 0xfdaff370},
+{8600000, DIF_BPF_COEFF2829, 0xf61c0429},
+{8600000, DIF_BPF_COEFF3031, 0x0ee80a0b},
+{8600000, DIF_BPF_COEFF3233, 0xf9d8ef82},
+{8600000, DIF_BPF_COEFF3435, 0xf6a00800},
+{8600000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 86_quant.dat*/
+
+
+/*case 8700000:*/
+/* BEGIN - DIF BPF register values from 87_quant.dat*/
+{8700000, DIF_BPF_COEFF01, 0x00000003},
+{8700000, DIF_BPF_COEFF23, 0x0007ffff},
+{8700000, DIF_BPF_COEFF45, 0xffe1ffd4},
+{8700000, DIF_BPF_COEFF67, 0x0010007a},
+{8700000, DIF_BPF_COEFF89, 0x007cffb2},
+{8700000, DIF_BPF_COEFF1011, 0xfec6ff10},
+{8700000, DIF_BPF_COEFF1213, 0x00e60277},
+{8700000, DIF_BPF_COEFF1415, 0x0168fdf9},
+{8700000, DIF_BPF_COEFF1617, 0xfbd3fe50},
+{8700000, DIF_BPF_COEFF1819, 0x03ce0631},
+{8700000, DIF_BPF_COEFF2021, 0x0188f9c8},
+{8700000, DIF_BPF_COEFF2223, 0xf7c7ff43},
+{8700000, DIF_BPF_COEFF2425, 0x091509e3},
+{8700000, DIF_BPF_COEFF2627, 0xff39f3f6},
+{8700000, DIF_BPF_COEFF2829, 0xf52d02ea},
+{8700000, DIF_BPF_COEFF3031, 0x0ea30ac9},
+{8700000, DIF_BPF_COEFF3233, 0xfa9bef95},
+{8700000, DIF_BPF_COEFF3435, 0xf64607d0},
+{8700000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 87_quant.dat*/
+
+
+/*case 8800000:*/
+/* BEGIN - DIF BPF register values from 88_quant.dat*/
+{8800000, DIF_BPF_COEFF01, 0x00000002},
+{8800000, DIF_BPF_COEFF23, 0x00090007},
+{8800000, DIF_BPF_COEFF45, 0xffe9ffca},
+{8800000, DIF_BPF_COEFF67, 0xfff00065},
+{8800000, DIF_BPF_COEFF89, 0x00a10003},
+{8800000, DIF_BPF_COEFF1011, 0xfee6feb6},
+{8800000, DIF_BPF_COEFF1213, 0x0053025b},
+{8800000, DIF_BPF_COEFF1415, 0x0213fed0},
+{8800000, DIF_BPF_COEFF1617, 0xfbd3fd46},
+{8800000, DIF_BPF_COEFF1819, 0x02c70668},
+{8800000, DIF_BPF_COEFF2021, 0x02eafadb},
+{8800000, DIF_BPF_COEFF2223, 0xf74bfdae},
+{8800000, DIF_BPF_COEFF2425, 0x08230a9c},
+{8800000, DIF_BPF_COEFF2627, 0x00c7f4a3},
+{8800000, DIF_BPF_COEFF2829, 0xf45b01a6},
+{8800000, DIF_BPF_COEFF3031, 0x0e480b7c},
+{8800000, DIF_BPF_COEFF3233, 0xfb61efae},
+{8800000, DIF_BPF_COEFF3435, 0xf5ef079f},
+{8800000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 88_quant.dat*/
+
+
+/*case 8900000:*/
+/* BEGIN - DIF BPF register values from 89_quant.dat*/
+{8900000, DIF_BPF_COEFF01, 0xffff0000},
+{8900000, DIF_BPF_COEFF23, 0x0008000d},
+{8900000, DIF_BPF_COEFF45, 0xfff5ffc8},
+{8900000, DIF_BPF_COEFF67, 0xffd10043},
+{8900000, DIF_BPF_COEFF89, 0x00b20053},
+{8900000, DIF_BPF_COEFF1011, 0xff24fe7c},
+{8900000, DIF_BPF_COEFF1213, 0xffb9020c},
+{8900000, DIF_BPF_COEFF1415, 0x0295ffbb},
+{8900000, DIF_BPF_COEFF1617, 0xfc17fc64},
+{8900000, DIF_BPF_COEFF1819, 0x019b0654},
+{8900000, DIF_BPF_COEFF2021, 0x042dfc1c},
+{8900000, DIF_BPF_COEFF2223, 0xf714fc2a},
+{8900000, DIF_BPF_COEFF2425, 0x07020b21},
+{8900000, DIF_BPF_COEFF2627, 0x0251f575},
+{8900000, DIF_BPF_COEFF2829, 0xf3a7005e},
+{8900000, DIF_BPF_COEFF3031, 0x0dd80c24},
+{8900000, DIF_BPF_COEFF3233, 0xfc2aefcd},
+{8900000, DIF_BPF_COEFF3435, 0xf599076e},
+{8900000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 89_quant.dat*/
+
+
+/*case 9000000:*/
+/* BEGIN - DIF BPF register values from 90_quant.dat*/
+{9000000, DIF_BPF_COEFF01, 0xffffffff},
+{9000000, DIF_BPF_COEFF23, 0x00060011},
+{9000000, DIF_BPF_COEFF45, 0x0002ffcf},
+{9000000, DIF_BPF_COEFF67, 0xffba0018},
+{9000000, DIF_BPF_COEFF89, 0x00ad009a},
+{9000000, DIF_BPF_COEFF1011, 0xff79fe68},
+{9000000, DIF_BPF_COEFF1213, 0xff260192},
+{9000000, DIF_BPF_COEFF1415, 0x02e500ab},
+{9000000, DIF_BPF_COEFF1617, 0xfc99fbb6},
+{9000000, DIF_BPF_COEFF1819, 0x005b05f7},
+{9000000, DIF_BPF_COEFF2021, 0x0545fd81},
+{9000000, DIF_BPF_COEFF2223, 0xf723fabf},
+{9000000, DIF_BPF_COEFF2425, 0x05b80b70},
+{9000000, DIF_BPF_COEFF2627, 0x03d2f669},
+{9000000, DIF_BPF_COEFF2829, 0xf313ff15},
+{9000000, DIF_BPF_COEFF3031, 0x0d550cbf},
+{9000000, DIF_BPF_COEFF3233, 0xfcf6eff2},
+{9000000, DIF_BPF_COEFF3435, 0xf544073d},
+{9000000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 90_quant.dat*/
+
+
+/*case 9100000:*/
+/* BEGIN - DIF BPF register values from 91_quant.dat*/
+{9100000, DIF_BPF_COEFF01, 0xfffffffe},
+{9100000, DIF_BPF_COEFF23, 0x00030012},
+{9100000, DIF_BPF_COEFF45, 0x000fffdd},
+{9100000, DIF_BPF_COEFF67, 0xffacffea},
+{9100000, DIF_BPF_COEFF89, 0x009300cf},
+{9100000, DIF_BPF_COEFF1011, 0xffdcfe7c},
+{9100000, DIF_BPF_COEFF1213, 0xfea600f7},
+{9100000, DIF_BPF_COEFF1415, 0x02fd0190},
+{9100000, DIF_BPF_COEFF1617, 0xfd51fb46},
+{9100000, DIF_BPF_COEFF1819, 0xff150554},
+{9100000, DIF_BPF_COEFF2021, 0x0627fefd},
+{9100000, DIF_BPF_COEFF2223, 0xf778f978},
+{9100000, DIF_BPF_COEFF2425, 0x044d0b87},
+{9100000, DIF_BPF_COEFF2627, 0x0543f77d},
+{9100000, DIF_BPF_COEFF2829, 0xf2a0fdcf},
+{9100000, DIF_BPF_COEFF3031, 0x0cbe0d4e},
+{9100000, DIF_BPF_COEFF3233, 0xfdc4f01d},
+{9100000, DIF_BPF_COEFF3435, 0xf4f2070b},
+{9100000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 91_quant.dat*/
+
+
+/*case 9200000:*/
+/* BEGIN - DIF BPF register values from 92_quant.dat*/
+{9200000, DIF_BPF_COEFF01, 0x0000fffd},
+{9200000, DIF_BPF_COEFF23, 0x00000010},
+{9200000, DIF_BPF_COEFF45, 0x001afff0},
+{9200000, DIF_BPF_COEFF67, 0xffaaffbf},
+{9200000, DIF_BPF_COEFF89, 0x006700ed},
+{9200000, DIF_BPF_COEFF1011, 0x0043feb6},
+{9200000, DIF_BPF_COEFF1213, 0xfe460047},
+{9200000, DIF_BPF_COEFF1415, 0x02db0258},
+{9200000, DIF_BPF_COEFF1617, 0xfe35fb1b},
+{9200000, DIF_BPF_COEFF1819, 0xfddc0473},
+{9200000, DIF_BPF_COEFF2021, 0x06c90082},
+{9200000, DIF_BPF_COEFF2223, 0xf811f85e},
+{9200000, DIF_BPF_COEFF2425, 0x02c90b66},
+{9200000, DIF_BPF_COEFF2627, 0x069ff8ad},
+{9200000, DIF_BPF_COEFF2829, 0xf250fc8d},
+{9200000, DIF_BPF_COEFF3031, 0x0c140dcf},
+{9200000, DIF_BPF_COEFF3233, 0xfe93f04d},
+{9200000, DIF_BPF_COEFF3435, 0xf4a106d9},
+{9200000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 92_quant.dat*/
+
+
+/*case 9300000:*/
+/* BEGIN - DIF BPF register values from 93_quant.dat*/
+{9300000, DIF_BPF_COEFF01, 0x0000fffd},
+{9300000, DIF_BPF_COEFF23, 0xfffc000c},
+{9300000, DIF_BPF_COEFF45, 0x00200006},
+{9300000, DIF_BPF_COEFF67, 0xffb4ff9c},
+{9300000, DIF_BPF_COEFF89, 0x002f00ef},
+{9300000, DIF_BPF_COEFF1011, 0x00a4ff10},
+{9300000, DIF_BPF_COEFF1213, 0xfe0dff92},
+{9300000, DIF_BPF_COEFF1415, 0x028102f7},
+{9300000, DIF_BPF_COEFF1617, 0xff36fb37},
+{9300000, DIF_BPF_COEFF1819, 0xfcbf035e},
+{9300000, DIF_BPF_COEFF2021, 0x07260202},
+{9300000, DIF_BPF_COEFF2223, 0xf8e8f778},
+{9300000, DIF_BPF_COEFF2425, 0x01340b0d},
+{9300000, DIF_BPF_COEFF2627, 0x07e1f9f4},
+{9300000, DIF_BPF_COEFF2829, 0xf223fb51},
+{9300000, DIF_BPF_COEFF3031, 0x0b590e42},
+{9300000, DIF_BPF_COEFF3233, 0xff64f083},
+{9300000, DIF_BPF_COEFF3435, 0xf45206a7},
+{9300000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 93_quant.dat*/
+
+
+/*case 9400000:*/
+/* BEGIN - DIF BPF register values from 94_quant.dat*/
+{9400000, DIF_BPF_COEFF01, 0x0000fffd},
+{9400000, DIF_BPF_COEFF23, 0xfff90005},
+{9400000, DIF_BPF_COEFF45, 0x0022001a},
+{9400000, DIF_BPF_COEFF67, 0xffc9ff86},
+{9400000, DIF_BPF_COEFF89, 0xfff000d7},
+{9400000, DIF_BPF_COEFF1011, 0x00f2ff82},
+{9400000, DIF_BPF_COEFF1213, 0xfe01fee5},
+{9400000, DIF_BPF_COEFF1415, 0x01f60362},
+{9400000, DIF_BPF_COEFF1617, 0x0044fb99},
+{9400000, DIF_BPF_COEFF1819, 0xfbcc0222},
+{9400000, DIF_BPF_COEFF2021, 0x07380370},
+{9400000, DIF_BPF_COEFF2223, 0xf9f7f6cc},
+{9400000, DIF_BPF_COEFF2425, 0xff990a7e},
+{9400000, DIF_BPF_COEFF2627, 0x0902fb50},
+{9400000, DIF_BPF_COEFF2829, 0xf21afa1f},
+{9400000, DIF_BPF_COEFF3031, 0x0a8d0ea6},
+{9400000, DIF_BPF_COEFF3233, 0x0034f0bf},
+{9400000, DIF_BPF_COEFF3435, 0xf4050675},
+{9400000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 94_quant.dat*/
+
+
+/*case 9500000:*/
+/* BEGIN - DIF BPF register values from 95_quant.dat*/
+{9500000, DIF_BPF_COEFF01, 0x0000fffe},
+{9500000, DIF_BPF_COEFF23, 0xfff8fffe},
+{9500000, DIF_BPF_COEFF45, 0x001e002b},
+{9500000, DIF_BPF_COEFF67, 0xffe5ff81},
+{9500000, DIF_BPF_COEFF89, 0xffb400a5},
+{9500000, DIF_BPF_COEFF1011, 0x01280000},
+{9500000, DIF_BPF_COEFF1213, 0xfe24fe50},
+{9500000, DIF_BPF_COEFF1415, 0x01460390},
+{9500000, DIF_BPF_COEFF1617, 0x014dfc3a},
+{9500000, DIF_BPF_COEFF1819, 0xfb1000ce},
+{9500000, DIF_BPF_COEFF2021, 0x070104bf},
+{9500000, DIF_BPF_COEFF2223, 0xfb37f65f},
+{9500000, DIF_BPF_COEFF2425, 0xfe0009bc},
+{9500000, DIF_BPF_COEFF2627, 0x0a00fcbb},
+{9500000, DIF_BPF_COEFF2829, 0xf235f8f8},
+{9500000, DIF_BPF_COEFF3031, 0x09b20efc},
+{9500000, DIF_BPF_COEFF3233, 0x0105f101},
+{9500000, DIF_BPF_COEFF3435, 0xf3ba0642},
+{9500000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 95_quant.dat*/
+
+
+/*case 9600000:*/
+/* BEGIN - DIF BPF register values from 96_quant.dat*/
+{9600000, DIF_BPF_COEFF01, 0x0001ffff},
+{9600000, DIF_BPF_COEFF23, 0xfff8fff7},
+{9600000, DIF_BPF_COEFF45, 0x00150036},
+{9600000, DIF_BPF_COEFF67, 0x0005ff8c},
+{9600000, DIF_BPF_COEFF89, 0xff810061},
+{9600000, DIF_BPF_COEFF1011, 0x013d007e},
+{9600000, DIF_BPF_COEFF1213, 0xfe71fddf},
+{9600000, DIF_BPF_COEFF1415, 0x007c0380},
+{9600000, DIF_BPF_COEFF1617, 0x0241fd13},
+{9600000, DIF_BPF_COEFF1819, 0xfa94ff70},
+{9600000, DIF_BPF_COEFF2021, 0x068005e2},
+{9600000, DIF_BPF_COEFF2223, 0xfc9bf633},
+{9600000, DIF_BPF_COEFF2425, 0xfc7308ca},
+{9600000, DIF_BPF_COEFF2627, 0x0ad5fe30},
+{9600000, DIF_BPF_COEFF2829, 0xf274f7e0},
+{9600000, DIF_BPF_COEFF3031, 0x08c90f43},
+{9600000, DIF_BPF_COEFF3233, 0x01d4f147},
+{9600000, DIF_BPF_COEFF3435, 0xf371060f},
+{9600000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 96_quant.dat*/
+
+
+/*case 9700000:*/
+/* BEGIN - DIF BPF register values from 97_quant.dat*/
+{9700000, DIF_BPF_COEFF01, 0x00010001},
+{9700000, DIF_BPF_COEFF23, 0xfff9fff1},
+{9700000, DIF_BPF_COEFF45, 0x00090038},
+{9700000, DIF_BPF_COEFF67, 0x0025ffa7},
+{9700000, DIF_BPF_COEFF89, 0xff5e0012},
+{9700000, DIF_BPF_COEFF1011, 0x013200f0},
+{9700000, DIF_BPF_COEFF1213, 0xfee3fd9b},
+{9700000, DIF_BPF_COEFF1415, 0xffaa0331},
+{9700000, DIF_BPF_COEFF1617, 0x0311fe15},
+{9700000, DIF_BPF_COEFF1819, 0xfa60fe18},
+{9700000, DIF_BPF_COEFF2021, 0x05bd06d1},
+{9700000, DIF_BPF_COEFF2223, 0xfe1bf64a},
+{9700000, DIF_BPF_COEFF2425, 0xfafa07ae},
+{9700000, DIF_BPF_COEFF2627, 0x0b7effab},
+{9700000, DIF_BPF_COEFF2829, 0xf2d5f6d7},
+{9700000, DIF_BPF_COEFF3031, 0x07d30f7a},
+{9700000, DIF_BPF_COEFF3233, 0x02a3f194},
+{9700000, DIF_BPF_COEFF3435, 0xf32905dc},
+{9700000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 97_quant.dat*/
+
+
+/*case 9800000:*/
+/* BEGIN - DIF BPF register values from 98_quant.dat*/
+{9800000, DIF_BPF_COEFF01, 0x00010002},
+{9800000, DIF_BPF_COEFF23, 0xfffcffee},
+{9800000, DIF_BPF_COEFF45, 0xfffb0032},
+{9800000, DIF_BPF_COEFF67, 0x003fffcd},
+{9800000, DIF_BPF_COEFF89, 0xff4effc1},
+{9800000, DIF_BPF_COEFF1011, 0x0106014a},
+{9800000, DIF_BPF_COEFF1213, 0xff6efd8a},
+{9800000, DIF_BPF_COEFF1415, 0xfedd02aa},
+{9800000, DIF_BPF_COEFF1617, 0x03b0ff34},
+{9800000, DIF_BPF_COEFF1819, 0xfa74fcd7},
+{9800000, DIF_BPF_COEFF2021, 0x04bf0781},
+{9800000, DIF_BPF_COEFF2223, 0xffaaf6a3},
+{9800000, DIF_BPF_COEFF2425, 0xf99e066b},
+{9800000, DIF_BPF_COEFF2627, 0x0bf90128},
+{9800000, DIF_BPF_COEFF2829, 0xf359f5e1},
+{9800000, DIF_BPF_COEFF3031, 0x06d20fa2},
+{9800000, DIF_BPF_COEFF3233, 0x0370f1e5},
+{9800000, DIF_BPF_COEFF3435, 0xf2e405a8},
+{9800000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 98_quant.dat*/
+
+
+/*case 9900000:*/
+/* BEGIN - DIF BPF register values from 99_quant.dat*/
+{9900000, DIF_BPF_COEFF01, 0x00000003},
+{9900000, DIF_BPF_COEFF23, 0xffffffee},
+{9900000, DIF_BPF_COEFF45, 0xffef0024},
+{9900000, DIF_BPF_COEFF67, 0x0051fffa},
+{9900000, DIF_BPF_COEFF89, 0xff54ff77},
+{9900000, DIF_BPF_COEFF1011, 0x00be0184},
+{9900000, DIF_BPF_COEFF1213, 0x0006fdad},
+{9900000, DIF_BPF_COEFF1415, 0xfe2701f3},
+{9900000, DIF_BPF_COEFF1617, 0x0413005e},
+{9900000, DIF_BPF_COEFF1819, 0xfad1fbba},
+{9900000, DIF_BPF_COEFF2021, 0x039007ee},
+{9900000, DIF_BPF_COEFF2223, 0x013bf73d},
+{9900000, DIF_BPF_COEFF2425, 0xf868050a},
+{9900000, DIF_BPF_COEFF2627, 0x0c4302a1},
+{9900000, DIF_BPF_COEFF2829, 0xf3fdf4fe},
+{9900000, DIF_BPF_COEFF3031, 0x05c70fba},
+{9900000, DIF_BPF_COEFF3233, 0x043bf23c},
+{9900000, DIF_BPF_COEFF3435, 0xf2a10575},
+{9900000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 99_quant.dat*/
+
+
+/*case 10000000:*/
+/* BEGIN - DIF BPF register values from 100_quant.dat*/
+{10000000, DIF_BPF_COEFF01, 0x00000003},
+{10000000, DIF_BPF_COEFF23, 0x0003fff1},
+{10000000, DIF_BPF_COEFF45, 0xffe50011},
+{10000000, DIF_BPF_COEFF67, 0x00570027},
+{10000000, DIF_BPF_COEFF89, 0xff70ff3c},
+{10000000, DIF_BPF_COEFF1011, 0x00620198},
+{10000000, DIF_BPF_COEFF1213, 0x009efe01},
+{10000000, DIF_BPF_COEFF1415, 0xfd95011a},
+{10000000, DIF_BPF_COEFF1617, 0x04350183},
+{10000000, DIF_BPF_COEFF1819, 0xfb71fad0},
+{10000000, DIF_BPF_COEFF2021, 0x023c0812},
+{10000000, DIF_BPF_COEFF2223, 0x02c3f811},
+{10000000, DIF_BPF_COEFF2425, 0xf75e0390},
+{10000000, DIF_BPF_COEFF2627, 0x0c5c0411},
+{10000000, DIF_BPF_COEFF2829, 0xf4c1f432},
+{10000000, DIF_BPF_COEFF3031, 0x04b30fc1},
+{10000000, DIF_BPF_COEFF3233, 0x0503f297},
+{10000000, DIF_BPF_COEFF3435, 0xf2610541},
+{10000000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 100_quant.dat*/
+
+
+/*case 10100000:*/
+/* BEGIN - DIF BPF register values from 101_quant.dat*/
+{10100000, DIF_BPF_COEFF01, 0x00000003},
+{10100000, DIF_BPF_COEFF23, 0x0006fff7},
+{10100000, DIF_BPF_COEFF45, 0xffdffffc},
+{10100000, DIF_BPF_COEFF67, 0x00510050},
+{10100000, DIF_BPF_COEFF89, 0xff9dff18},
+{10100000, DIF_BPF_COEFF1011, 0xfffc0184},
+{10100000, DIF_BPF_COEFF1213, 0x0128fe80},
+{10100000, DIF_BPF_COEFF1415, 0xfd32002e},
+{10100000, DIF_BPF_COEFF1617, 0x04130292},
+{10100000, DIF_BPF_COEFF1819, 0xfc4dfa21},
+{10100000, DIF_BPF_COEFF2021, 0x00d107ee},
+{10100000, DIF_BPF_COEFF2223, 0x0435f91c},
+{10100000, DIF_BPF_COEFF2425, 0xf6850205},
+{10100000, DIF_BPF_COEFF2627, 0x0c430573},
+{10100000, DIF_BPF_COEFF2829, 0xf5a1f37d},
+{10100000, DIF_BPF_COEFF3031, 0x03990fba},
+{10100000, DIF_BPF_COEFF3233, 0x05c7f2f8},
+{10100000, DIF_BPF_COEFF3435, 0xf222050d},
+{10100000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 101_quant.dat*/
+
+
+/*case 10200000:*/
+/* BEGIN - DIF BPF register values from 102_quant.dat*/
+{10200000, DIF_BPF_COEFF01, 0x00000002},
+{10200000, DIF_BPF_COEFF23, 0x0008fffe},
+{10200000, DIF_BPF_COEFF45, 0xffdfffe7},
+{10200000, DIF_BPF_COEFF67, 0x003f006e},
+{10200000, DIF_BPF_COEFF89, 0xffd6ff0f},
+{10200000, DIF_BPF_COEFF1011, 0xff96014a},
+{10200000, DIF_BPF_COEFF1213, 0x0197ff1f},
+{10200000, DIF_BPF_COEFF1415, 0xfd05ff3e},
+{10200000, DIF_BPF_COEFF1617, 0x03b0037c},
+{10200000, DIF_BPF_COEFF1819, 0xfd59f9b7},
+{10200000, DIF_BPF_COEFF2021, 0xff5d0781},
+{10200000, DIF_BPF_COEFF2223, 0x0585fa56},
+{10200000, DIF_BPF_COEFF2425, 0xf5e4006f},
+{10200000, DIF_BPF_COEFF2627, 0x0bf906c4},
+{10200000, DIF_BPF_COEFF2829, 0xf69df2e0},
+{10200000, DIF_BPF_COEFF3031, 0x02790fa2},
+{10200000, DIF_BPF_COEFF3233, 0x0688f35d},
+{10200000, DIF_BPF_COEFF3435, 0xf1e604d8},
+{10200000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 102_quant.dat*/
+
+
+/*case 10300000:*/
+/* BEGIN - DIF BPF register values from 103_quant.dat*/
+{10300000, DIF_BPF_COEFF01, 0xffff0001},
+{10300000, DIF_BPF_COEFF23, 0x00090005},
+{10300000, DIF_BPF_COEFF45, 0xffe4ffd6},
+{10300000, DIF_BPF_COEFF67, 0x0025007e},
+{10300000, DIF_BPF_COEFF89, 0x0014ff20},
+{10300000, DIF_BPF_COEFF1011, 0xff3c00f0},
+{10300000, DIF_BPF_COEFF1213, 0x01e1ffd0},
+{10300000, DIF_BPF_COEFF1415, 0xfd12fe5c},
+{10300000, DIF_BPF_COEFF1617, 0x03110433},
+{10300000, DIF_BPF_COEFF1819, 0xfe88f996},
+{10300000, DIF_BPF_COEFF2021, 0xfdf106d1},
+{10300000, DIF_BPF_COEFF2223, 0x06aafbb7},
+{10300000, DIF_BPF_COEFF2425, 0xf57efed8},
+{10300000, DIF_BPF_COEFF2627, 0x0b7e07ff},
+{10300000, DIF_BPF_COEFF2829, 0xf7b0f25e},
+{10300000, DIF_BPF_COEFF3031, 0x01560f7a},
+{10300000, DIF_BPF_COEFF3233, 0x0745f3c7},
+{10300000, DIF_BPF_COEFF3435, 0xf1ac04a4},
+{10300000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 103_quant.dat*/
+
+
+/*case 10400000:*/
+/* BEGIN - DIF BPF register values from 104_quant.dat*/
+{10400000, DIF_BPF_COEFF01, 0xffffffff},
+{10400000, DIF_BPF_COEFF23, 0x0008000c},
+{10400000, DIF_BPF_COEFF45, 0xffedffcb},
+{10400000, DIF_BPF_COEFF67, 0x0005007d},
+{10400000, DIF_BPF_COEFF89, 0x0050ff4c},
+{10400000, DIF_BPF_COEFF1011, 0xfef6007e},
+{10400000, DIF_BPF_COEFF1213, 0x01ff0086},
+{10400000, DIF_BPF_COEFF1415, 0xfd58fd97},
+{10400000, DIF_BPF_COEFF1617, 0x024104ad},
+{10400000, DIF_BPF_COEFF1819, 0xffcaf9c0},
+{10400000, DIF_BPF_COEFF2021, 0xfc9905e2},
+{10400000, DIF_BPF_COEFF2223, 0x079afd35},
+{10400000, DIF_BPF_COEFF2425, 0xf555fd46},
+{10400000, DIF_BPF_COEFF2627, 0x0ad50920},
+{10400000, DIF_BPF_COEFF2829, 0xf8d9f1f6},
+{10400000, DIF_BPF_COEFF3031, 0x00310f43},
+{10400000, DIF_BPF_COEFF3233, 0x07fdf435},
+{10400000, DIF_BPF_COEFF3435, 0xf174046f},
+{10400000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 104_quant.dat*/
+
+
+/*case 10500000:*/
+/* BEGIN - DIF BPF register values from 105_quant.dat*/
+{10500000, DIF_BPF_COEFF01, 0xfffffffe},
+{10500000, DIF_BPF_COEFF23, 0x00050011},
+{10500000, DIF_BPF_COEFF45, 0xfffaffc8},
+{10500000, DIF_BPF_COEFF67, 0xffe5006b},
+{10500000, DIF_BPF_COEFF89, 0x0082ff8c},
+{10500000, DIF_BPF_COEFF1011, 0xfecc0000},
+{10500000, DIF_BPF_COEFF1213, 0x01f00130},
+{10500000, DIF_BPF_COEFF1415, 0xfdd2fcfc},
+{10500000, DIF_BPF_COEFF1617, 0x014d04e3},
+{10500000, DIF_BPF_COEFF1819, 0x010efa32},
+{10500000, DIF_BPF_COEFF2021, 0xfb6404bf},
+{10500000, DIF_BPF_COEFF2223, 0x084efec5},
+{10500000, DIF_BPF_COEFF2425, 0xf569fbc2},
+{10500000, DIF_BPF_COEFF2627, 0x0a000a23},
+{10500000, DIF_BPF_COEFF2829, 0xfa15f1ab},
+{10500000, DIF_BPF_COEFF3031, 0xff0b0efc},
+{10500000, DIF_BPF_COEFF3233, 0x08b0f4a7},
+{10500000, DIF_BPF_COEFF3435, 0xf13f043a},
+{10500000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 105_quant.dat*/
+
+
+/*case 10600000:*/
+/* BEGIN - DIF BPF register values from 106_quant.dat*/
+{10600000, DIF_BPF_COEFF01, 0x0000fffd},
+{10600000, DIF_BPF_COEFF23, 0x00020012},
+{10600000, DIF_BPF_COEFF45, 0x0007ffcd},
+{10600000, DIF_BPF_COEFF67, 0xffc9004c},
+{10600000, DIF_BPF_COEFF89, 0x00a4ffd9},
+{10600000, DIF_BPF_COEFF1011, 0xfec3ff82},
+{10600000, DIF_BPF_COEFF1213, 0x01b401c1},
+{10600000, DIF_BPF_COEFF1415, 0xfe76fc97},
+{10600000, DIF_BPF_COEFF1617, 0x004404d2},
+{10600000, DIF_BPF_COEFF1819, 0x0245fae8},
+{10600000, DIF_BPF_COEFF2021, 0xfa5f0370},
+{10600000, DIF_BPF_COEFF2223, 0x08c1005f},
+{10600000, DIF_BPF_COEFF2425, 0xf5bcfa52},
+{10600000, DIF_BPF_COEFF2627, 0x09020b04},
+{10600000, DIF_BPF_COEFF2829, 0xfb60f17b},
+{10600000, DIF_BPF_COEFF3031, 0xfde70ea6},
+{10600000, DIF_BPF_COEFF3233, 0x095df51e},
+{10600000, DIF_BPF_COEFF3435, 0xf10c0405},
+{10600000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 106_quant.dat*/
+
+
+/*case 10700000:*/
+/* BEGIN - DIF BPF register values from 107_quant.dat*/
+{10700000, DIF_BPF_COEFF01, 0x0000fffd},
+{10700000, DIF_BPF_COEFF23, 0xffff0011},
+{10700000, DIF_BPF_COEFF45, 0x0014ffdb},
+{10700000, DIF_BPF_COEFF67, 0xffb40023},
+{10700000, DIF_BPF_COEFF89, 0x00b2002a},
+{10700000, DIF_BPF_COEFF1011, 0xfedbff10},
+{10700000, DIF_BPF_COEFF1213, 0x0150022d},
+{10700000, DIF_BPF_COEFF1415, 0xff38fc6f},
+{10700000, DIF_BPF_COEFF1617, 0xff36047b},
+{10700000, DIF_BPF_COEFF1819, 0x035efbda},
+{10700000, DIF_BPF_COEFF2021, 0xf9940202},
+{10700000, DIF_BPF_COEFF2223, 0x08ee01f5},
+{10700000, DIF_BPF_COEFF2425, 0xf649f8fe},
+{10700000, DIF_BPF_COEFF2627, 0x07e10bc2},
+{10700000, DIF_BPF_COEFF2829, 0xfcb6f169},
+{10700000, DIF_BPF_COEFF3031, 0xfcc60e42},
+{10700000, DIF_BPF_COEFF3233, 0x0a04f599},
+{10700000, DIF_BPF_COEFF3435, 0xf0db03d0},
+{10700000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 107_quant.dat*/
+
+
+/*case 10800000:*/
+/* BEGIN - DIF BPF register values from 108_quant.dat*/
+{10800000, DIF_BPF_COEFF01, 0x0000fffd},
+{10800000, DIF_BPF_COEFF23, 0xfffb000d},
+{10800000, DIF_BPF_COEFF45, 0x001dffed},
+{10800000, DIF_BPF_COEFF67, 0xffaafff5},
+{10800000, DIF_BPF_COEFF89, 0x00aa0077},
+{10800000, DIF_BPF_COEFF1011, 0xff13feb6},
+{10800000, DIF_BPF_COEFF1213, 0x00ce026b},
+{10800000, DIF_BPF_COEFF1415, 0x000afc85},
+{10800000, DIF_BPF_COEFF1617, 0xfe3503e3},
+{10800000, DIF_BPF_COEFF1819, 0x044cfcfb},
+{10800000, DIF_BPF_COEFF2021, 0xf90c0082},
+{10800000, DIF_BPF_COEFF2223, 0x08d5037f},
+{10800000, DIF_BPF_COEFF2425, 0xf710f7cc},
+{10800000, DIF_BPF_COEFF2627, 0x069f0c59},
+{10800000, DIF_BPF_COEFF2829, 0xfe16f173},
+{10800000, DIF_BPF_COEFF3031, 0xfbaa0dcf},
+{10800000, DIF_BPF_COEFF3233, 0x0aa5f617},
+{10800000, DIF_BPF_COEFF3435, 0xf0ad039b},
+{10800000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 108_quant.dat*/
+
+
+/*case 10900000:*/
+/* BEGIN - DIF BPF register values from 109_quant.dat*/
+{10900000, DIF_BPF_COEFF01, 0x0000fffe},
+{10900000, DIF_BPF_COEFF23, 0xfff90006},
+{10900000, DIF_BPF_COEFF45, 0x00210003},
+{10900000, DIF_BPF_COEFF67, 0xffacffc8},
+{10900000, DIF_BPF_COEFF89, 0x008e00b6},
+{10900000, DIF_BPF_COEFF1011, 0xff63fe7c},
+{10900000, DIF_BPF_COEFF1213, 0x003a0275},
+{10900000, DIF_BPF_COEFF1415, 0x00dafcda},
+{10900000, DIF_BPF_COEFF1617, 0xfd510313},
+{10900000, DIF_BPF_COEFF1819, 0x0501fe40},
+{10900000, DIF_BPF_COEFF2021, 0xf8cbfefd},
+{10900000, DIF_BPF_COEFF2223, 0x087604f0},
+{10900000, DIF_BPF_COEFF2425, 0xf80af6c2},
+{10900000, DIF_BPF_COEFF2627, 0x05430cc8},
+{10900000, DIF_BPF_COEFF2829, 0xff7af19a},
+{10900000, DIF_BPF_COEFF3031, 0xfa940d4e},
+{10900000, DIF_BPF_COEFF3233, 0x0b3ff699},
+{10900000, DIF_BPF_COEFF3435, 0xf0810365},
+{10900000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 109_quant.dat*/
+
+
+/*case 11000000:*/
+/* BEGIN - DIF BPF register values from 110_quant.dat*/
+{11000000, DIF_BPF_COEFF01, 0x0001ffff},
+{11000000, DIF_BPF_COEFF23, 0xfff8ffff},
+{11000000, DIF_BPF_COEFF45, 0x00210018},
+{11000000, DIF_BPF_COEFF67, 0xffbaffa3},
+{11000000, DIF_BPF_COEFF89, 0x006000e1},
+{11000000, DIF_BPF_COEFF1011, 0xffc4fe68},
+{11000000, DIF_BPF_COEFF1213, 0xffa0024b},
+{11000000, DIF_BPF_COEFF1415, 0x019afd66},
+{11000000, DIF_BPF_COEFF1617, 0xfc990216},
+{11000000, DIF_BPF_COEFF1819, 0x0575ff99},
+{11000000, DIF_BPF_COEFF2021, 0xf8d4fd81},
+{11000000, DIF_BPF_COEFF2223, 0x07d40640},
+{11000000, DIF_BPF_COEFF2425, 0xf932f5e6},
+{11000000, DIF_BPF_COEFF2627, 0x03d20d0d},
+{11000000, DIF_BPF_COEFF2829, 0x00dff1de},
+{11000000, DIF_BPF_COEFF3031, 0xf9860cbf},
+{11000000, DIF_BPF_COEFF3233, 0x0bd1f71e},
+{11000000, DIF_BPF_COEFF3435, 0xf058032f},
+{11000000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 110_quant.dat*/
+
+
+/*case 11100000:*/
+/* BEGIN - DIF BPF register values from 111_quant.dat*/
+{11100000, DIF_BPF_COEFF01, 0x00010000},
+{11100000, DIF_BPF_COEFF23, 0xfff8fff8},
+{11100000, DIF_BPF_COEFF45, 0x001b0029},
+{11100000, DIF_BPF_COEFF67, 0xffd1ff8a},
+{11100000, DIF_BPF_COEFF89, 0x002600f2},
+{11100000, DIF_BPF_COEFF1011, 0x002cfe7c},
+{11100000, DIF_BPF_COEFF1213, 0xff0f01f0},
+{11100000, DIF_BPF_COEFF1415, 0x023bfe20},
+{11100000, DIF_BPF_COEFF1617, 0xfc1700fa},
+{11100000, DIF_BPF_COEFF1819, 0x05a200f7},
+{11100000, DIF_BPF_COEFF2021, 0xf927fc1c},
+{11100000, DIF_BPF_COEFF2223, 0x06f40765},
+{11100000, DIF_BPF_COEFF2425, 0xfa82f53b},
+{11100000, DIF_BPF_COEFF2627, 0x02510d27},
+{11100000, DIF_BPF_COEFF2829, 0x0243f23d},
+{11100000, DIF_BPF_COEFF3031, 0xf8810c24},
+{11100000, DIF_BPF_COEFF3233, 0x0c5cf7a7},
+{11100000, DIF_BPF_COEFF3435, 0xf03102fa},
+{11100000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 111_quant.dat*/
+
+
+/*case 11200000:*/
+/* BEGIN - DIF BPF register values from 112_quant.dat*/
+{11200000, DIF_BPF_COEFF01, 0x00010002},
+{11200000, DIF_BPF_COEFF23, 0xfffafff2},
+{11200000, DIF_BPF_COEFF45, 0x00110035},
+{11200000, DIF_BPF_COEFF67, 0xfff0ff81},
+{11200000, DIF_BPF_COEFF89, 0xffe700e7},
+{11200000, DIF_BPF_COEFF1011, 0x008ffeb6},
+{11200000, DIF_BPF_COEFF1213, 0xfe94016d},
+{11200000, DIF_BPF_COEFF1415, 0x02b0fefb},
+{11200000, DIF_BPF_COEFF1617, 0xfbd3ffd1},
+{11200000, DIF_BPF_COEFF1819, 0x05850249},
+{11200000, DIF_BPF_COEFF2021, 0xf9c1fadb},
+{11200000, DIF_BPF_COEFF2223, 0x05de0858},
+{11200000, DIF_BPF_COEFF2425, 0xfbf2f4c4},
+{11200000, DIF_BPF_COEFF2627, 0x00c70d17},
+{11200000, DIF_BPF_COEFF2829, 0x03a0f2b8},
+{11200000, DIF_BPF_COEFF3031, 0xf7870b7c},
+{11200000, DIF_BPF_COEFF3233, 0x0cdff833},
+{11200000, DIF_BPF_COEFF3435, 0xf00d02c4},
+{11200000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 112_quant.dat*/
+
+
+/*case 11300000:*/
+/* BEGIN - DIF BPF register values from 113_quant.dat*/
+{11300000, DIF_BPF_COEFF01, 0x00000003},
+{11300000, DIF_BPF_COEFF23, 0xfffdffee},
+{11300000, DIF_BPF_COEFF45, 0x00040038},
+{11300000, DIF_BPF_COEFF67, 0x0010ff88},
+{11300000, DIF_BPF_COEFF89, 0xffac00c2},
+{11300000, DIF_BPF_COEFF1011, 0x00e2ff10},
+{11300000, DIF_BPF_COEFF1213, 0xfe3900cb},
+{11300000, DIF_BPF_COEFF1415, 0x02f1ffe9},
+{11300000, DIF_BPF_COEFF1617, 0xfbd3feaa},
+{11300000, DIF_BPF_COEFF1819, 0x05210381},
+{11300000, DIF_BPF_COEFF2021, 0xfa9cf9c8},
+{11300000, DIF_BPF_COEFF2223, 0x04990912},
+{11300000, DIF_BPF_COEFF2425, 0xfd7af484},
+{11300000, DIF_BPF_COEFF2627, 0xff390cdb},
+{11300000, DIF_BPF_COEFF2829, 0x04f4f34d},
+{11300000, DIF_BPF_COEFF3031, 0xf69a0ac9},
+{11300000, DIF_BPF_COEFF3233, 0x0d5af8c1},
+{11300000, DIF_BPF_COEFF3435, 0xefec028e},
+{11300000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 113_quant.dat*/
+
+
+/*case 11400000:*/
+/* BEGIN - DIF BPF register values from 114_quant.dat*/
+{11400000, DIF_BPF_COEFF01, 0x00000003},
+{11400000, DIF_BPF_COEFF23, 0x0000ffee},
+{11400000, DIF_BPF_COEFF45, 0xfff60033},
+{11400000, DIF_BPF_COEFF67, 0x002fff9f},
+{11400000, DIF_BPF_COEFF89, 0xff7b0087},
+{11400000, DIF_BPF_COEFF1011, 0x011eff82},
+{11400000, DIF_BPF_COEFF1213, 0xfe080018},
+{11400000, DIF_BPF_COEFF1415, 0x02f900d8},
+{11400000, DIF_BPF_COEFF1617, 0xfc17fd96},
+{11400000, DIF_BPF_COEFF1819, 0x04790490},
+{11400000, DIF_BPF_COEFF2021, 0xfbadf8ed},
+{11400000, DIF_BPF_COEFF2223, 0x032f098e},
+{11400000, DIF_BPF_COEFF2425, 0xff10f47d},
+{11400000, DIF_BPF_COEFF2627, 0xfdaf0c75},
+{11400000, DIF_BPF_COEFF2829, 0x063cf3fc},
+{11400000, DIF_BPF_COEFF3031, 0xf5ba0a0b},
+{11400000, DIF_BPF_COEFF3233, 0x0dccf952},
+{11400000, DIF_BPF_COEFF3435, 0xefcd0258},
+{11400000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 114_quant.dat*/
+
+
+/*case 11500000:*/
+/* BEGIN - DIF BPF register values from 115_quant.dat*/
+{11500000, DIF_BPF_COEFF01, 0x00000003},
+{11500000, DIF_BPF_COEFF23, 0x0004fff1},
+{11500000, DIF_BPF_COEFF45, 0xffea0026},
+{11500000, DIF_BPF_COEFF67, 0x0046ffc3},
+{11500000, DIF_BPF_COEFF89, 0xff5a003c},
+{11500000, DIF_BPF_COEFF1011, 0x013b0000},
+{11500000, DIF_BPF_COEFF1213, 0xfe04ff63},
+{11500000, DIF_BPF_COEFF1415, 0x02c801b8},
+{11500000, DIF_BPF_COEFF1617, 0xfc99fca6},
+{11500000, DIF_BPF_COEFF1819, 0x0397056a},
+{11500000, DIF_BPF_COEFF2021, 0xfcecf853},
+{11500000, DIF_BPF_COEFF2223, 0x01ad09c9},
+{11500000, DIF_BPF_COEFF2425, 0x00acf4ad},
+{11500000, DIF_BPF_COEFF2627, 0xfc2e0be7},
+{11500000, DIF_BPF_COEFF2829, 0x0773f4c2},
+{11500000, DIF_BPF_COEFF3031, 0xf4e90943},
+{11500000, DIF_BPF_COEFF3233, 0x0e35f9e6},
+{11500000, DIF_BPF_COEFF3435, 0xefb10221},
+{11500000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 115_quant.dat*/
+
+
+/*case 11600000:*/
+/* BEGIN - DIF BPF register values from 116_quant.dat*/
+{11600000, DIF_BPF_COEFF01, 0x00000002},
+{11600000, DIF_BPF_COEFF23, 0x0007fff6},
+{11600000, DIF_BPF_COEFF45, 0xffe20014},
+{11600000, DIF_BPF_COEFF67, 0x0054ffee},
+{11600000, DIF_BPF_COEFF89, 0xff4effeb},
+{11600000, DIF_BPF_COEFF1011, 0x0137007e},
+{11600000, DIF_BPF_COEFF1213, 0xfe2efebb},
+{11600000, DIF_BPF_COEFF1415, 0x0260027a},
+{11600000, DIF_BPF_COEFF1617, 0xfd51fbe6},
+{11600000, DIF_BPF_COEFF1819, 0x02870605},
+{11600000, DIF_BPF_COEFF2021, 0xfe4af7fe},
+{11600000, DIF_BPF_COEFF2223, 0x001d09c1},
+{11600000, DIF_BPF_COEFF2425, 0x0243f515},
+{11600000, DIF_BPF_COEFF2627, 0xfabd0b32},
+{11600000, DIF_BPF_COEFF2829, 0x0897f59e},
+{11600000, DIF_BPF_COEFF3031, 0xf4280871},
+{11600000, DIF_BPF_COEFF3233, 0x0e95fa7c},
+{11600000, DIF_BPF_COEFF3435, 0xef9701eb},
+{11600000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 116_quant.dat*/
+
+
+/*case 11700000:*/
+/* BEGIN - DIF BPF register values from 117_quant.dat*/
+{11700000, DIF_BPF_COEFF01, 0xffff0001},
+{11700000, DIF_BPF_COEFF23, 0x0008fffd},
+{11700000, DIF_BPF_COEFF45, 0xffdeffff},
+{11700000, DIF_BPF_COEFF67, 0x0056001d},
+{11700000, DIF_BPF_COEFF89, 0xff57ff9c},
+{11700000, DIF_BPF_COEFF1011, 0x011300f0},
+{11700000, DIF_BPF_COEFF1213, 0xfe82fe2e},
+{11700000, DIF_BPF_COEFF1415, 0x01ca0310},
+{11700000, DIF_BPF_COEFF1617, 0xfe35fb62},
+{11700000, DIF_BPF_COEFF1819, 0x0155065a},
+{11700000, DIF_BPF_COEFF2021, 0xffbaf7f2},
+{11700000, DIF_BPF_COEFF2223, 0xfe8c0977},
+{11700000, DIF_BPF_COEFF2425, 0x03cef5b2},
+{11700000, DIF_BPF_COEFF2627, 0xf9610a58},
+{11700000, DIF_BPF_COEFF2829, 0x09a5f68f},
+{11700000, DIF_BPF_COEFF3031, 0xf3790797},
+{11700000, DIF_BPF_COEFF3233, 0x0eebfb14},
+{11700000, DIF_BPF_COEFF3435, 0xef8001b5},
+{11700000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 117_quant.dat*/
+
+
+/*case 11800000:*/
+/* BEGIN - DIF BPF register values from 118_quant.dat*/
+{11800000, DIF_BPF_COEFF01, 0xffff0000},
+{11800000, DIF_BPF_COEFF23, 0x00080004},
+{11800000, DIF_BPF_COEFF45, 0xffe0ffe9},
+{11800000, DIF_BPF_COEFF67, 0x004c0047},
+{11800000, DIF_BPF_COEFF89, 0xff75ff58},
+{11800000, DIF_BPF_COEFF1011, 0x00d1014a},
+{11800000, DIF_BPF_COEFF1213, 0xfef9fdc8},
+{11800000, DIF_BPF_COEFF1415, 0x0111036f},
+{11800000, DIF_BPF_COEFF1617, 0xff36fb21},
+{11800000, DIF_BPF_COEFF1819, 0x00120665},
+{11800000, DIF_BPF_COEFF2021, 0x012df82e},
+{11800000, DIF_BPF_COEFF2223, 0xfd0708ec},
+{11800000, DIF_BPF_COEFF2425, 0x0542f682},
+{11800000, DIF_BPF_COEFF2627, 0xf81f095c},
+{11800000, DIF_BPF_COEFF2829, 0x0a9af792},
+{11800000, DIF_BPF_COEFF3031, 0xf2db06b5},
+{11800000, DIF_BPF_COEFF3233, 0x0f38fbad},
+{11800000, DIF_BPF_COEFF3435, 0xef6c017e},
+{11800000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 118_quant.dat*/
+
+
+/*case 11900000:*/
+/* BEGIN - DIF BPF register values from 119_quant.dat*/
+{11900000, DIF_BPF_COEFF01, 0xffffffff},
+{11900000, DIF_BPF_COEFF23, 0x0007000b},
+{11900000, DIF_BPF_COEFF45, 0xffe7ffd8},
+{11900000, DIF_BPF_COEFF67, 0x00370068},
+{11900000, DIF_BPF_COEFF89, 0xffa4ff28},
+{11900000, DIF_BPF_COEFF1011, 0x00790184},
+{11900000, DIF_BPF_COEFF1213, 0xff87fd91},
+{11900000, DIF_BPF_COEFF1415, 0x00430392},
+{11900000, DIF_BPF_COEFF1617, 0x0044fb26},
+{11900000, DIF_BPF_COEFF1819, 0xfece0626},
+{11900000, DIF_BPF_COEFF2021, 0x0294f8b2},
+{11900000, DIF_BPF_COEFF2223, 0xfb990825},
+{11900000, DIF_BPF_COEFF2425, 0x0698f77f},
+{11900000, DIF_BPF_COEFF2627, 0xf6fe0842},
+{11900000, DIF_BPF_COEFF2829, 0x0b73f8a7},
+{11900000, DIF_BPF_COEFF3031, 0xf25105cd},
+{11900000, DIF_BPF_COEFF3233, 0x0f7bfc48},
+{11900000, DIF_BPF_COEFF3435, 0xef5a0148},
+{11900000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 119_quant.dat*/
+
+
+/*case 12000000:*/
+/* BEGIN - DIF BPF register values from 120_quant.dat*/
+{12000000, DIF_BPF_COEFF01, 0x0000fffe},
+{12000000, DIF_BPF_COEFF23, 0x00050010},
+{12000000, DIF_BPF_COEFF45, 0xfff2ffcc},
+{12000000, DIF_BPF_COEFF67, 0x001b007b},
+{12000000, DIF_BPF_COEFF89, 0xffdfff10},
+{12000000, DIF_BPF_COEFF1011, 0x00140198},
+{12000000, DIF_BPF_COEFF1213, 0x0020fd8e},
+{12000000, DIF_BPF_COEFF1415, 0xff710375},
+{12000000, DIF_BPF_COEFF1617, 0x014dfb73},
+{12000000, DIF_BPF_COEFF1819, 0xfd9a059f},
+{12000000, DIF_BPF_COEFF2021, 0x03e0f978},
+{12000000, DIF_BPF_COEFF2223, 0xfa4e0726},
+{12000000, DIF_BPF_COEFF2425, 0x07c8f8a7},
+{12000000, DIF_BPF_COEFF2627, 0xf600070c},
+{12000000, DIF_BPF_COEFF2829, 0x0c2ff9c9},
+{12000000, DIF_BPF_COEFF3031, 0xf1db04de},
+{12000000, DIF_BPF_COEFF3233, 0x0fb4fce5},
+{12000000, DIF_BPF_COEFF3435, 0xef4b0111},
+{12000000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 120_quant.dat*/
+
+
+/*case 12100000:*/
+/* BEGIN - DIF BPF register values from 121_quant.dat*/
+{12100000, DIF_BPF_COEFF01, 0x0000fffd},
+{12100000, DIF_BPF_COEFF23, 0x00010012},
+{12100000, DIF_BPF_COEFF45, 0xffffffc8},
+{12100000, DIF_BPF_COEFF67, 0xfffb007e},
+{12100000, DIF_BPF_COEFF89, 0x001dff14},
+{12100000, DIF_BPF_COEFF1011, 0xffad0184},
+{12100000, DIF_BPF_COEFF1213, 0x00b7fdbe},
+{12100000, DIF_BPF_COEFF1415, 0xfea9031b},
+{12100000, DIF_BPF_COEFF1617, 0x0241fc01},
+{12100000, DIF_BPF_COEFF1819, 0xfc8504d6},
+{12100000, DIF_BPF_COEFF2021, 0x0504fa79},
+{12100000, DIF_BPF_COEFF2223, 0xf93005f6},
+{12100000, DIF_BPF_COEFF2425, 0x08caf9f2},
+{12100000, DIF_BPF_COEFF2627, 0xf52b05c0},
+{12100000, DIF_BPF_COEFF2829, 0x0ccbfaf9},
+{12100000, DIF_BPF_COEFF3031, 0xf17903eb},
+{12100000, DIF_BPF_COEFF3233, 0x0fe3fd83},
+{12100000, DIF_BPF_COEFF3435, 0xef3f00db},
+{12100000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 121_quant.dat*/
+
+
+/*case 12200000:*/
+/* BEGIN - DIF BPF register values from 122_quant.dat*/
+{12200000, DIF_BPF_COEFF01, 0x0000fffd},
+{12200000, DIF_BPF_COEFF23, 0xfffe0011},
+{12200000, DIF_BPF_COEFF45, 0x000cffcc},
+{12200000, DIF_BPF_COEFF67, 0xffdb0071},
+{12200000, DIF_BPF_COEFF89, 0x0058ff32},
+{12200000, DIF_BPF_COEFF1011, 0xff4f014a},
+{12200000, DIF_BPF_COEFF1213, 0x013cfe1f},
+{12200000, DIF_BPF_COEFF1415, 0xfdfb028a},
+{12200000, DIF_BPF_COEFF1617, 0x0311fcc9},
+{12200000, DIF_BPF_COEFF1819, 0xfb9d03d6},
+{12200000, DIF_BPF_COEFF2021, 0x05f4fbad},
+{12200000, DIF_BPF_COEFF2223, 0xf848049d},
+{12200000, DIF_BPF_COEFF2425, 0x0999fb5b},
+{12200000, DIF_BPF_COEFF2627, 0xf4820461},
+{12200000, DIF_BPF_COEFF2829, 0x0d46fc32},
+{12200000, DIF_BPF_COEFF3031, 0xf12d02f4},
+{12200000, DIF_BPF_COEFF3233, 0x1007fe21},
+{12200000, DIF_BPF_COEFF3435, 0xef3600a4},
+{12200000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 122_quant.dat*/
+
+
+/*case 12300000:*/
+/* BEGIN - DIF BPF register values from 123_quant.dat*/
+{12300000, DIF_BPF_COEFF01, 0x0000fffe},
+{12300000, DIF_BPF_COEFF23, 0xfffa000e},
+{12300000, DIF_BPF_COEFF45, 0x0017ffd9},
+{12300000, DIF_BPF_COEFF67, 0xffc10055},
+{12300000, DIF_BPF_COEFF89, 0x0088ff68},
+{12300000, DIF_BPF_COEFF1011, 0xff0400f0},
+{12300000, DIF_BPF_COEFF1213, 0x01a6fea7},
+{12300000, DIF_BPF_COEFF1415, 0xfd7501cc},
+{12300000, DIF_BPF_COEFF1617, 0x03b0fdc0},
+{12300000, DIF_BPF_COEFF1819, 0xfaef02a8},
+{12300000, DIF_BPF_COEFF2021, 0x06a7fd07},
+{12300000, DIF_BPF_COEFF2223, 0xf79d0326},
+{12300000, DIF_BPF_COEFF2425, 0x0a31fcda},
+{12300000, DIF_BPF_COEFF2627, 0xf40702f3},
+{12300000, DIF_BPF_COEFF2829, 0x0d9ffd72},
+{12300000, DIF_BPF_COEFF3031, 0xf0f601fa},
+{12300000, DIF_BPF_COEFF3233, 0x1021fec0},
+{12300000, DIF_BPF_COEFF3435, 0xef2f006d},
+{12300000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 123_quant.dat*/
+
+
+/*case 12400000:*/
+/* BEGIN - DIF BPF register values from 124_quant.dat*/
+{12400000, DIF_BPF_COEFF01, 0x0001ffff},
+{12400000, DIF_BPF_COEFF23, 0xfff80007},
+{12400000, DIF_BPF_COEFF45, 0x001fffeb},
+{12400000, DIF_BPF_COEFF67, 0xffaf002d},
+{12400000, DIF_BPF_COEFF89, 0x00a8ffb0},
+{12400000, DIF_BPF_COEFF1011, 0xfed3007e},
+{12400000, DIF_BPF_COEFF1213, 0x01e9ff4c},
+{12400000, DIF_BPF_COEFF1415, 0xfd2000ee},
+{12400000, DIF_BPF_COEFF1617, 0x0413fed8},
+{12400000, DIF_BPF_COEFF1819, 0xfa82015c},
+{12400000, DIF_BPF_COEFF2021, 0x0715fe7d},
+{12400000, DIF_BPF_COEFF2223, 0xf7340198},
+{12400000, DIF_BPF_COEFF2425, 0x0a8dfe69},
+{12400000, DIF_BPF_COEFF2627, 0xf3bd017c},
+{12400000, DIF_BPF_COEFF2829, 0x0dd5feb8},
+{12400000, DIF_BPF_COEFF3031, 0xf0d500fd},
+{12400000, DIF_BPF_COEFF3233, 0x1031ff60},
+{12400000, DIF_BPF_COEFF3435, 0xef2b0037},
+{12400000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 124_quant.dat*/
+
+
+/*case 12500000:*/
+/* BEGIN - DIF BPF register values from 125_quant.dat*/
+{12500000, DIF_BPF_COEFF01, 0x00010000},
+{12500000, DIF_BPF_COEFF23, 0xfff70000},
+{12500000, DIF_BPF_COEFF45, 0x00220000},
+{12500000, DIF_BPF_COEFF67, 0xffa90000},
+{12500000, DIF_BPF_COEFF89, 0x00b30000},
+{12500000, DIF_BPF_COEFF1011, 0xfec20000},
+{12500000, DIF_BPF_COEFF1213, 0x02000000},
+{12500000, DIF_BPF_COEFF1415, 0xfd030000},
+{12500000, DIF_BPF_COEFF1617, 0x04350000},
+{12500000, DIF_BPF_COEFF1819, 0xfa5e0000},
+{12500000, DIF_BPF_COEFF2021, 0x073b0000},
+{12500000, DIF_BPF_COEFF2223, 0xf7110000},
+{12500000, DIF_BPF_COEFF2425, 0x0aac0000},
+{12500000, DIF_BPF_COEFF2627, 0xf3a40000},
+{12500000, DIF_BPF_COEFF2829, 0x0de70000},
+{12500000, DIF_BPF_COEFF3031, 0xf0c90000},
+{12500000, DIF_BPF_COEFF3233, 0x10360000},
+{12500000, DIF_BPF_COEFF3435, 0xef290000},
+{12500000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 125_quant.dat*/
+
+
+/*case 12600000:*/
+/* BEGIN - DIF BPF register values from 126_quant.dat*/
+{12600000, DIF_BPF_COEFF01, 0x00010001},
+{12600000, DIF_BPF_COEFF23, 0xfff8fff9},
+{12600000, DIF_BPF_COEFF45, 0x001f0015},
+{12600000, DIF_BPF_COEFF67, 0xffafffd3},
+{12600000, DIF_BPF_COEFF89, 0x00a80050},
+{12600000, DIF_BPF_COEFF1011, 0xfed3ff82},
+{12600000, DIF_BPF_COEFF1213, 0x01e900b4},
+{12600000, DIF_BPF_COEFF1415, 0xfd20ff12},
+{12600000, DIF_BPF_COEFF1617, 0x04130128},
+{12600000, DIF_BPF_COEFF1819, 0xfa82fea4},
+{12600000, DIF_BPF_COEFF2021, 0x07150183},
+{12600000, DIF_BPF_COEFF2223, 0xf734fe68},
+{12600000, DIF_BPF_COEFF2425, 0x0a8d0197},
+{12600000, DIF_BPF_COEFF2627, 0xf3bdfe84},
+{12600000, DIF_BPF_COEFF2829, 0x0dd50148},
+{12600000, DIF_BPF_COEFF3031, 0xf0d5ff03},
+{12600000, DIF_BPF_COEFF3233, 0x103100a0},
+{12600000, DIF_BPF_COEFF3435, 0xef2bffc9},
+{12600000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 126_quant.dat*/
+
+
+/*case 12700000:*/
+/* BEGIN - DIF BPF register values from 127_quant.dat*/
+{12700000, DIF_BPF_COEFF01, 0x00000002},
+{12700000, DIF_BPF_COEFF23, 0xfffafff2},
+{12700000, DIF_BPF_COEFF45, 0x00170027},
+{12700000, DIF_BPF_COEFF67, 0xffc1ffab},
+{12700000, DIF_BPF_COEFF89, 0x00880098},
+{12700000, DIF_BPF_COEFF1011, 0xff04ff10},
+{12700000, DIF_BPF_COEFF1213, 0x01a60159},
+{12700000, DIF_BPF_COEFF1415, 0xfd75fe34},
+{12700000, DIF_BPF_COEFF1617, 0x03b00240},
+{12700000, DIF_BPF_COEFF1819, 0xfaeffd58},
+{12700000, DIF_BPF_COEFF2021, 0x06a702f9},
+{12700000, DIF_BPF_COEFF2223, 0xf79dfcda},
+{12700000, DIF_BPF_COEFF2425, 0x0a310326},
+{12700000, DIF_BPF_COEFF2627, 0xf407fd0d},
+{12700000, DIF_BPF_COEFF2829, 0x0d9f028e},
+{12700000, DIF_BPF_COEFF3031, 0xf0f6fe06},
+{12700000, DIF_BPF_COEFF3233, 0x10210140},
+{12700000, DIF_BPF_COEFF3435, 0xef2fff93},
+{12700000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 127_quant.dat*/
+
+
+/*case 12800000:*/
+/* BEGIN - DIF BPF register values from 128_quant.dat*/
+{12800000, DIF_BPF_COEFF01, 0x00000003},
+{12800000, DIF_BPF_COEFF23, 0xfffeffef},
+{12800000, DIF_BPF_COEFF45, 0x000c0034},
+{12800000, DIF_BPF_COEFF67, 0xffdbff8f},
+{12800000, DIF_BPF_COEFF89, 0x005800ce},
+{12800000, DIF_BPF_COEFF1011, 0xff4ffeb6},
+{12800000, DIF_BPF_COEFF1213, 0x013c01e1},
+{12800000, DIF_BPF_COEFF1415, 0xfdfbfd76},
+{12800000, DIF_BPF_COEFF1617, 0x03110337},
+{12800000, DIF_BPF_COEFF1819, 0xfb9dfc2a},
+{12800000, DIF_BPF_COEFF2021, 0x05f40453},
+{12800000, DIF_BPF_COEFF2223, 0xf848fb63},
+{12800000, DIF_BPF_COEFF2425, 0x099904a5},
+{12800000, DIF_BPF_COEFF2627, 0xf482fb9f},
+{12800000, DIF_BPF_COEFF2829, 0x0d4603ce},
+{12800000, DIF_BPF_COEFF3031, 0xf12dfd0c},
+{12800000, DIF_BPF_COEFF3233, 0x100701df},
+{12800000, DIF_BPF_COEFF3435, 0xef36ff5c},
+{12800000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 128_quant.dat*/
+
+
+/*case 12900000:*/
+/* BEGIN - DIF BPF register values from 129_quant.dat*/
+{12900000, DIF_BPF_COEFF01, 0x00000003},
+{12900000, DIF_BPF_COEFF23, 0x0001ffee},
+{12900000, DIF_BPF_COEFF45, 0xffff0038},
+{12900000, DIF_BPF_COEFF67, 0xfffbff82},
+{12900000, DIF_BPF_COEFF89, 0x001d00ec},
+{12900000, DIF_BPF_COEFF1011, 0xffadfe7c},
+{12900000, DIF_BPF_COEFF1213, 0x00b70242},
+{12900000, DIF_BPF_COEFF1415, 0xfea9fce5},
+{12900000, DIF_BPF_COEFF1617, 0x024103ff},
+{12900000, DIF_BPF_COEFF1819, 0xfc85fb2a},
+{12900000, DIF_BPF_COEFF2021, 0x05040587},
+{12900000, DIF_BPF_COEFF2223, 0xf930fa0a},
+{12900000, DIF_BPF_COEFF2425, 0x08ca060e},
+{12900000, DIF_BPF_COEFF2627, 0xf52bfa40},
+{12900000, DIF_BPF_COEFF2829, 0x0ccb0507},
+{12900000, DIF_BPF_COEFF3031, 0xf179fc15},
+{12900000, DIF_BPF_COEFF3233, 0x0fe3027d},
+{12900000, DIF_BPF_COEFF3435, 0xef3fff25},
+{12900000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 129_quant.dat*/
+
+
+/*case 113000000:*/
+/* BEGIN - DIF BPF register values from 130_quant.dat*/
+{13000000, DIF_BPF_COEFF01, 0x00000002},
+{13000000, DIF_BPF_COEFF23, 0x0005fff0},
+{13000000, DIF_BPF_COEFF45, 0xfff20034},
+{13000000, DIF_BPF_COEFF67, 0x001bff85},
+{13000000, DIF_BPF_COEFF89, 0xffdf00f0},
+{13000000, DIF_BPF_COEFF1011, 0x0014fe68},
+{13000000, DIF_BPF_COEFF1213, 0x00200272},
+{13000000, DIF_BPF_COEFF1415, 0xff71fc8b},
+{13000000, DIF_BPF_COEFF1617, 0x014d048d},
+{13000000, DIF_BPF_COEFF1819, 0xfd9afa61},
+{13000000, DIF_BPF_COEFF2021, 0x03e00688},
+{13000000, DIF_BPF_COEFF2223, 0xfa4ef8da},
+{13000000, DIF_BPF_COEFF2425, 0x07c80759},
+{13000000, DIF_BPF_COEFF2627, 0xf600f8f4},
+{13000000, DIF_BPF_COEFF2829, 0x0c2f0637},
+{13000000, DIF_BPF_COEFF3031, 0xf1dbfb22},
+{13000000, DIF_BPF_COEFF3233, 0x0fb4031b},
+{13000000, DIF_BPF_COEFF3435, 0xef4bfeef},
+{13000000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 130_quant.dat*/
+
+
+/*case 13100000:*/
+/* BEGIN - DIF BPF register values from 131_quant.dat*/
+{13100000, DIF_BPF_COEFF01, 0xffff0001},
+{13100000, DIF_BPF_COEFF23, 0x0007fff5},
+{13100000, DIF_BPF_COEFF45, 0xffe70028},
+{13100000, DIF_BPF_COEFF67, 0x0037ff98},
+{13100000, DIF_BPF_COEFF89, 0xffa400d8},
+{13100000, DIF_BPF_COEFF1011, 0x0079fe7c},
+{13100000, DIF_BPF_COEFF1213, 0xff87026f},
+{13100000, DIF_BPF_COEFF1415, 0x0043fc6e},
+{13100000, DIF_BPF_COEFF1617, 0x004404da},
+{13100000, DIF_BPF_COEFF1819, 0xfecef9da},
+{13100000, DIF_BPF_COEFF2021, 0x0294074e},
+{13100000, DIF_BPF_COEFF2223, 0xfb99f7db},
+{13100000, DIF_BPF_COEFF2425, 0x06980881},
+{13100000, DIF_BPF_COEFF2627, 0xf6fef7be},
+{13100000, DIF_BPF_COEFF2829, 0x0b730759},
+{13100000, DIF_BPF_COEFF3031, 0xf251fa33},
+{13100000, DIF_BPF_COEFF3233, 0x0f7b03b8},
+{13100000, DIF_BPF_COEFF3435, 0xef5afeb8},
+{13100000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 131_quant.dat*/
+
+
+/*case 13200000:*/
+/* BEGIN - DIF BPF register values from 132_quant.dat*/
+{13200000, DIF_BPF_COEFF01, 0xffff0000},
+{13200000, DIF_BPF_COEFF23, 0x0008fffc},
+{13200000, DIF_BPF_COEFF45, 0xffe00017},
+{13200000, DIF_BPF_COEFF67, 0x004cffb9},
+{13200000, DIF_BPF_COEFF89, 0xff7500a8},
+{13200000, DIF_BPF_COEFF1011, 0x00d1feb6},
+{13200000, DIF_BPF_COEFF1213, 0xfef90238},
+{13200000, DIF_BPF_COEFF1415, 0x0111fc91},
+{13200000, DIF_BPF_COEFF1617, 0xff3604df},
+{13200000, DIF_BPF_COEFF1819, 0x0012f99b},
+{13200000, DIF_BPF_COEFF2021, 0x012d07d2},
+{13200000, DIF_BPF_COEFF2223, 0xfd07f714},
+{13200000, DIF_BPF_COEFF2425, 0x0542097e},
+{13200000, DIF_BPF_COEFF2627, 0xf81ff6a4},
+{13200000, DIF_BPF_COEFF2829, 0x0a9a086e},
+{13200000, DIF_BPF_COEFF3031, 0xf2dbf94b},
+{13200000, DIF_BPF_COEFF3233, 0x0f380453},
+{13200000, DIF_BPF_COEFF3435, 0xef6cfe82},
+{13200000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 132_quant.dat*/
+
+
+/*case 13300000:*/
+/* BEGIN - DIF BPF register values from 133_quant.dat*/
+{13300000, DIF_BPF_COEFF01, 0xffffffff},
+{13300000, DIF_BPF_COEFF23, 0x00080003},
+{13300000, DIF_BPF_COEFF45, 0xffde0001},
+{13300000, DIF_BPF_COEFF67, 0x0056ffe3},
+{13300000, DIF_BPF_COEFF89, 0xff570064},
+{13300000, DIF_BPF_COEFF1011, 0x0113ff10},
+{13300000, DIF_BPF_COEFF1213, 0xfe8201d2},
+{13300000, DIF_BPF_COEFF1415, 0x01cafcf0},
+{13300000, DIF_BPF_COEFF1617, 0xfe35049e},
+{13300000, DIF_BPF_COEFF1819, 0x0155f9a6},
+{13300000, DIF_BPF_COEFF2021, 0xffba080e},
+{13300000, DIF_BPF_COEFF2223, 0xfe8cf689},
+{13300000, DIF_BPF_COEFF2425, 0x03ce0a4e},
+{13300000, DIF_BPF_COEFF2627, 0xf961f5a8},
+{13300000, DIF_BPF_COEFF2829, 0x09a50971},
+{13300000, DIF_BPF_COEFF3031, 0xf379f869},
+{13300000, DIF_BPF_COEFF3233, 0x0eeb04ec},
+{13300000, DIF_BPF_COEFF3435, 0xef80fe4b},
+{13300000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 133_quant.dat*/
+
+
+/*case 13400000:*/
+/* BEGIN - DIF BPF register values from 134_quant.dat*/
+{13400000, DIF_BPF_COEFF01, 0x0000fffe},
+{13400000, DIF_BPF_COEFF23, 0x0007000a},
+{13400000, DIF_BPF_COEFF45, 0xffe2ffec},
+{13400000, DIF_BPF_COEFF67, 0x00540012},
+{13400000, DIF_BPF_COEFF89, 0xff4e0015},
+{13400000, DIF_BPF_COEFF1011, 0x0137ff82},
+{13400000, DIF_BPF_COEFF1213, 0xfe2e0145},
+{13400000, DIF_BPF_COEFF1415, 0x0260fd86},
+{13400000, DIF_BPF_COEFF1617, 0xfd51041a},
+{13400000, DIF_BPF_COEFF1819, 0x0287f9fb},
+{13400000, DIF_BPF_COEFF2021, 0xfe4a0802},
+{13400000, DIF_BPF_COEFF2223, 0x001df63f},
+{13400000, DIF_BPF_COEFF2425, 0x02430aeb},
+{13400000, DIF_BPF_COEFF2627, 0xfabdf4ce},
+{13400000, DIF_BPF_COEFF2829, 0x08970a62},
+{13400000, DIF_BPF_COEFF3031, 0xf428f78f},
+{13400000, DIF_BPF_COEFF3233, 0x0e950584},
+{13400000, DIF_BPF_COEFF3435, 0xef97fe15},
+{13400000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 134_quant.dat*/
+
+
+/*case 13500000:*/
+/* BEGIN - DIF BPF register values from 135_quant.dat*/
+{13500000, DIF_BPF_COEFF01, 0x0000fffd},
+{13500000, DIF_BPF_COEFF23, 0x0004000f},
+{13500000, DIF_BPF_COEFF45, 0xffeaffda},
+{13500000, DIF_BPF_COEFF67, 0x0046003d},
+{13500000, DIF_BPF_COEFF89, 0xff5affc4},
+{13500000, DIF_BPF_COEFF1011, 0x013b0000},
+{13500000, DIF_BPF_COEFF1213, 0xfe04009d},
+{13500000, DIF_BPF_COEFF1415, 0x02c8fe48},
+{13500000, DIF_BPF_COEFF1617, 0xfc99035a},
+{13500000, DIF_BPF_COEFF1819, 0x0397fa96},
+{13500000, DIF_BPF_COEFF2021, 0xfcec07ad},
+{13500000, DIF_BPF_COEFF2223, 0x01adf637},
+{13500000, DIF_BPF_COEFF2425, 0x00ac0b53},
+{13500000, DIF_BPF_COEFF2627, 0xfc2ef419},
+{13500000, DIF_BPF_COEFF2829, 0x07730b3e},
+{13500000, DIF_BPF_COEFF3031, 0xf4e9f6bd},
+{13500000, DIF_BPF_COEFF3233, 0x0e35061a},
+{13500000, DIF_BPF_COEFF3435, 0xefb1fddf},
+{13500000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 135_quant.dat*/
+
+
+/*case 13600000:*/
+/* BEGIN - DIF BPF register values from 136_quant.dat*/
+{13600000, DIF_BPF_COEFF01, 0x0000fffd},
+{13600000, DIF_BPF_COEFF23, 0x00000012},
+{13600000, DIF_BPF_COEFF45, 0xfff6ffcd},
+{13600000, DIF_BPF_COEFF67, 0x002f0061},
+{13600000, DIF_BPF_COEFF89, 0xff7bff79},
+{13600000, DIF_BPF_COEFF1011, 0x011e007e},
+{13600000, DIF_BPF_COEFF1213, 0xfe08ffe8},
+{13600000, DIF_BPF_COEFF1415, 0x02f9ff28},
+{13600000, DIF_BPF_COEFF1617, 0xfc17026a},
+{13600000, DIF_BPF_COEFF1819, 0x0479fb70},
+{13600000, DIF_BPF_COEFF2021, 0xfbad0713},
+{13600000, DIF_BPF_COEFF2223, 0x032ff672},
+{13600000, DIF_BPF_COEFF2425, 0xff100b83},
+{13600000, DIF_BPF_COEFF2627, 0xfdaff38b},
+{13600000, DIF_BPF_COEFF2829, 0x063c0c04},
+{13600000, DIF_BPF_COEFF3031, 0xf5baf5f5},
+{13600000, DIF_BPF_COEFF3233, 0x0dcc06ae},
+{13600000, DIF_BPF_COEFF3435, 0xefcdfda8},
+{13600000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 136_quant.dat*/
+
+
+/*case 13700000:*/
+/* BEGIN - DIF BPF register values from 137_quant.dat*/
+{13700000, DIF_BPF_COEFF01, 0x0000fffd},
+{13700000, DIF_BPF_COEFF23, 0xfffd0012},
+{13700000, DIF_BPF_COEFF45, 0x0004ffc8},
+{13700000, DIF_BPF_COEFF67, 0x00100078},
+{13700000, DIF_BPF_COEFF89, 0xffacff3e},
+{13700000, DIF_BPF_COEFF1011, 0x00e200f0},
+{13700000, DIF_BPF_COEFF1213, 0xfe39ff35},
+{13700000, DIF_BPF_COEFF1415, 0x02f10017},
+{13700000, DIF_BPF_COEFF1617, 0xfbd30156},
+{13700000, DIF_BPF_COEFF1819, 0x0521fc7f},
+{13700000, DIF_BPF_COEFF2021, 0xfa9c0638},
+{13700000, DIF_BPF_COEFF2223, 0x0499f6ee},
+{13700000, DIF_BPF_COEFF2425, 0xfd7a0b7c},
+{13700000, DIF_BPF_COEFF2627, 0xff39f325},
+{13700000, DIF_BPF_COEFF2829, 0x04f40cb3},
+{13700000, DIF_BPF_COEFF3031, 0xf69af537},
+{13700000, DIF_BPF_COEFF3233, 0x0d5a073f},
+{13700000, DIF_BPF_COEFF3435, 0xefecfd72},
+{13700000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 137_quant.dat*/
+
+
+/*case 13800000:*/
+/* BEGIN - DIF BPF register values from 138_quant.dat*/
+{13800000, DIF_BPF_COEFF01, 0x0001fffe},
+{13800000, DIF_BPF_COEFF23, 0xfffa000e},
+{13800000, DIF_BPF_COEFF45, 0x0011ffcb},
+{13800000, DIF_BPF_COEFF67, 0xfff0007f},
+{13800000, DIF_BPF_COEFF89, 0xffe7ff19},
+{13800000, DIF_BPF_COEFF1011, 0x008f014a},
+{13800000, DIF_BPF_COEFF1213, 0xfe94fe93},
+{13800000, DIF_BPF_COEFF1415, 0x02b00105},
+{13800000, DIF_BPF_COEFF1617, 0xfbd3002f},
+{13800000, DIF_BPF_COEFF1819, 0x0585fdb7},
+{13800000, DIF_BPF_COEFF2021, 0xf9c10525},
+{13800000, DIF_BPF_COEFF2223, 0x05def7a8},
+{13800000, DIF_BPF_COEFF2425, 0xfbf20b3c},
+{13800000, DIF_BPF_COEFF2627, 0x00c7f2e9},
+{13800000, DIF_BPF_COEFF2829, 0x03a00d48},
+{13800000, DIF_BPF_COEFF3031, 0xf787f484},
+{13800000, DIF_BPF_COEFF3233, 0x0cdf07cd},
+{13800000, DIF_BPF_COEFF3435, 0xf00dfd3c},
+{13800000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 138_quant.dat*/
+
+
+/*case 13900000:*/
+/* BEGIN - DIF BPF register values from 139_quant.dat*/
+{13900000, DIF_BPF_COEFF01, 0x00010000},
+{13900000, DIF_BPF_COEFF23, 0xfff80008},
+{13900000, DIF_BPF_COEFF45, 0x001bffd7},
+{13900000, DIF_BPF_COEFF67, 0xffd10076},
+{13900000, DIF_BPF_COEFF89, 0x0026ff0e},
+{13900000, DIF_BPF_COEFF1011, 0x002c0184},
+{13900000, DIF_BPF_COEFF1213, 0xff0ffe10},
+{13900000, DIF_BPF_COEFF1415, 0x023b01e0},
+{13900000, DIF_BPF_COEFF1617, 0xfc17ff06},
+{13900000, DIF_BPF_COEFF1819, 0x05a2ff09},
+{13900000, DIF_BPF_COEFF2021, 0xf92703e4},
+{13900000, DIF_BPF_COEFF2223, 0x06f4f89b},
+{13900000, DIF_BPF_COEFF2425, 0xfa820ac5},
+{13900000, DIF_BPF_COEFF2627, 0x0251f2d9},
+{13900000, DIF_BPF_COEFF2829, 0x02430dc3},
+{13900000, DIF_BPF_COEFF3031, 0xf881f3dc},
+{13900000, DIF_BPF_COEFF3233, 0x0c5c0859},
+{13900000, DIF_BPF_COEFF3435, 0xf031fd06},
+{13900000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 139_quant.dat*/
+
+
+/*case 14000000:*/
+/* BEGIN - DIF BPF register values from 140_quant.dat*/
+{14000000, DIF_BPF_COEFF01, 0x00010001},
+{14000000, DIF_BPF_COEFF23, 0xfff80001},
+{14000000, DIF_BPF_COEFF45, 0x0021ffe8},
+{14000000, DIF_BPF_COEFF67, 0xffba005d},
+{14000000, DIF_BPF_COEFF89, 0x0060ff1f},
+{14000000, DIF_BPF_COEFF1011, 0xffc40198},
+{14000000, DIF_BPF_COEFF1213, 0xffa0fdb5},
+{14000000, DIF_BPF_COEFF1415, 0x019a029a},
+{14000000, DIF_BPF_COEFF1617, 0xfc99fdea},
+{14000000, DIF_BPF_COEFF1819, 0x05750067},
+{14000000, DIF_BPF_COEFF2021, 0xf8d4027f},
+{14000000, DIF_BPF_COEFF2223, 0x07d4f9c0},
+{14000000, DIF_BPF_COEFF2425, 0xf9320a1a},
+{14000000, DIF_BPF_COEFF2627, 0x03d2f2f3},
+{14000000, DIF_BPF_COEFF2829, 0x00df0e22},
+{14000000, DIF_BPF_COEFF3031, 0xf986f341},
+{14000000, DIF_BPF_COEFF3233, 0x0bd108e2},
+{14000000, DIF_BPF_COEFF3435, 0xf058fcd1},
+{14000000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 140_quant.dat*/
+
+
+/*case 14100000:*/
+/* BEGIN - DIF BPF register values from 141_quant.dat*/
+{14100000, DIF_BPF_COEFF01, 0x00000002},
+{14100000, DIF_BPF_COEFF23, 0xfff9fffa},
+{14100000, DIF_BPF_COEFF45, 0x0021fffd},
+{14100000, DIF_BPF_COEFF67, 0xffac0038},
+{14100000, DIF_BPF_COEFF89, 0x008eff4a},
+{14100000, DIF_BPF_COEFF1011, 0xff630184},
+{14100000, DIF_BPF_COEFF1213, 0x003afd8b},
+{14100000, DIF_BPF_COEFF1415, 0x00da0326},
+{14100000, DIF_BPF_COEFF1617, 0xfd51fced},
+{14100000, DIF_BPF_COEFF1819, 0x050101c0},
+{14100000, DIF_BPF_COEFF2021, 0xf8cb0103},
+{14100000, DIF_BPF_COEFF2223, 0x0876fb10},
+{14100000, DIF_BPF_COEFF2425, 0xf80a093e},
+{14100000, DIF_BPF_COEFF2627, 0x0543f338},
+{14100000, DIF_BPF_COEFF2829, 0xff7a0e66},
+{14100000, DIF_BPF_COEFF3031, 0xfa94f2b2},
+{14100000, DIF_BPF_COEFF3233, 0x0b3f0967},
+{14100000, DIF_BPF_COEFF3435, 0xf081fc9b},
+{14100000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 141_quant.dat*/
+
+
+/*case 14200000:*/
+/* BEGIN - DIF BPF register values from 142_quant.dat*/
+{14200000, DIF_BPF_COEFF01, 0x00000003},
+{14200000, DIF_BPF_COEFF23, 0xfffbfff3},
+{14200000, DIF_BPF_COEFF45, 0x001d0013},
+{14200000, DIF_BPF_COEFF67, 0xffaa000b},
+{14200000, DIF_BPF_COEFF89, 0x00aaff89},
+{14200000, DIF_BPF_COEFF1011, 0xff13014a},
+{14200000, DIF_BPF_COEFF1213, 0x00cefd95},
+{14200000, DIF_BPF_COEFF1415, 0x000a037b},
+{14200000, DIF_BPF_COEFF1617, 0xfe35fc1d},
+{14200000, DIF_BPF_COEFF1819, 0x044c0305},
+{14200000, DIF_BPF_COEFF2021, 0xf90cff7e},
+{14200000, DIF_BPF_COEFF2223, 0x08d5fc81},
+{14200000, DIF_BPF_COEFF2425, 0xf7100834},
+{14200000, DIF_BPF_COEFF2627, 0x069ff3a7},
+{14200000, DIF_BPF_COEFF2829, 0xfe160e8d},
+{14200000, DIF_BPF_COEFF3031, 0xfbaaf231},
+{14200000, DIF_BPF_COEFF3233, 0x0aa509e9},
+{14200000, DIF_BPF_COEFF3435, 0xf0adfc65},
+{14200000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 142_quant.dat*/
+
+
+/*case 14300000:*/
+/* BEGIN - DIF BPF register values from 143_quant.dat*/
+{14300000, DIF_BPF_COEFF01, 0x00000003},
+{14300000, DIF_BPF_COEFF23, 0xffffffef},
+{14300000, DIF_BPF_COEFF45, 0x00140025},
+{14300000, DIF_BPF_COEFF67, 0xffb4ffdd},
+{14300000, DIF_BPF_COEFF89, 0x00b2ffd6},
+{14300000, DIF_BPF_COEFF1011, 0xfedb00f0},
+{14300000, DIF_BPF_COEFF1213, 0x0150fdd3},
+{14300000, DIF_BPF_COEFF1415, 0xff380391},
+{14300000, DIF_BPF_COEFF1617, 0xff36fb85},
+{14300000, DIF_BPF_COEFF1819, 0x035e0426},
+{14300000, DIF_BPF_COEFF2021, 0xf994fdfe},
+{14300000, DIF_BPF_COEFF2223, 0x08eefe0b},
+{14300000, DIF_BPF_COEFF2425, 0xf6490702},
+{14300000, DIF_BPF_COEFF2627, 0x07e1f43e},
+{14300000, DIF_BPF_COEFF2829, 0xfcb60e97},
+{14300000, DIF_BPF_COEFF3031, 0xfcc6f1be},
+{14300000, DIF_BPF_COEFF3233, 0x0a040a67},
+{14300000, DIF_BPF_COEFF3435, 0xf0dbfc30},
+{14300000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 143_quant.dat*/
+
+
+/*case 14400000:*/
+/* BEGIN - DIF BPF register values from 144_quant.dat*/
+{14400000, DIF_BPF_COEFF01, 0x00000003},
+{14400000, DIF_BPF_COEFF23, 0x0002ffee},
+{14400000, DIF_BPF_COEFF45, 0x00070033},
+{14400000, DIF_BPF_COEFF67, 0xffc9ffb4},
+{14400000, DIF_BPF_COEFF89, 0x00a40027},
+{14400000, DIF_BPF_COEFF1011, 0xfec3007e},
+{14400000, DIF_BPF_COEFF1213, 0x01b4fe3f},
+{14400000, DIF_BPF_COEFF1415, 0xfe760369},
+{14400000, DIF_BPF_COEFF1617, 0x0044fb2e},
+{14400000, DIF_BPF_COEFF1819, 0x02450518},
+{14400000, DIF_BPF_COEFF2021, 0xfa5ffc90},
+{14400000, DIF_BPF_COEFF2223, 0x08c1ffa1},
+{14400000, DIF_BPF_COEFF2425, 0xf5bc05ae},
+{14400000, DIF_BPF_COEFF2627, 0x0902f4fc},
+{14400000, DIF_BPF_COEFF2829, 0xfb600e85},
+{14400000, DIF_BPF_COEFF3031, 0xfde7f15a},
+{14400000, DIF_BPF_COEFF3233, 0x095d0ae2},
+{14400000, DIF_BPF_COEFF3435, 0xf10cfbfb},
+{14400000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 144_quant.dat*/
+
+
+/*case 14500000:*/
+/* BEGIN - DIF BPF register values from 145_quant.dat*/
+{14500000, DIF_BPF_COEFF01, 0xffff0002},
+{14500000, DIF_BPF_COEFF23, 0x0005ffef},
+{14500000, DIF_BPF_COEFF45, 0xfffa0038},
+{14500000, DIF_BPF_COEFF67, 0xffe5ff95},
+{14500000, DIF_BPF_COEFF89, 0x00820074},
+{14500000, DIF_BPF_COEFF1011, 0xfecc0000},
+{14500000, DIF_BPF_COEFF1213, 0x01f0fed0},
+{14500000, DIF_BPF_COEFF1415, 0xfdd20304},
+{14500000, DIF_BPF_COEFF1617, 0x014dfb1d},
+{14500000, DIF_BPF_COEFF1819, 0x010e05ce},
+{14500000, DIF_BPF_COEFF2021, 0xfb64fb41},
+{14500000, DIF_BPF_COEFF2223, 0x084e013b},
+{14500000, DIF_BPF_COEFF2425, 0xf569043e},
+{14500000, DIF_BPF_COEFF2627, 0x0a00f5dd},
+{14500000, DIF_BPF_COEFF2829, 0xfa150e55},
+{14500000, DIF_BPF_COEFF3031, 0xff0bf104},
+{14500000, DIF_BPF_COEFF3233, 0x08b00b59},
+{14500000, DIF_BPF_COEFF3435, 0xf13ffbc6},
+{14500000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 145_quant.dat*/
+
+
+/*case 14600000:*/
+/* BEGIN - DIF BPF register values from 146_quant.dat*/
+{14600000, DIF_BPF_COEFF01, 0xffff0001},
+{14600000, DIF_BPF_COEFF23, 0x0008fff4},
+{14600000, DIF_BPF_COEFF45, 0xffed0035},
+{14600000, DIF_BPF_COEFF67, 0x0005ff83},
+{14600000, DIF_BPF_COEFF89, 0x005000b4},
+{14600000, DIF_BPF_COEFF1011, 0xfef6ff82},
+{14600000, DIF_BPF_COEFF1213, 0x01ffff7a},
+{14600000, DIF_BPF_COEFF1415, 0xfd580269},
+{14600000, DIF_BPF_COEFF1617, 0x0241fb53},
+{14600000, DIF_BPF_COEFF1819, 0xffca0640},
+{14600000, DIF_BPF_COEFF2021, 0xfc99fa1e},
+{14600000, DIF_BPF_COEFF2223, 0x079a02cb},
+{14600000, DIF_BPF_COEFF2425, 0xf55502ba},
+{14600000, DIF_BPF_COEFF2627, 0x0ad5f6e0},
+{14600000, DIF_BPF_COEFF2829, 0xf8d90e0a},
+{14600000, DIF_BPF_COEFF3031, 0x0031f0bd},
+{14600000, DIF_BPF_COEFF3233, 0x07fd0bcb},
+{14600000, DIF_BPF_COEFF3435, 0xf174fb91},
+{14600000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 146_quant.dat*/
+
+
+/*case 14700000:*/
+/* BEGIN - DIF BPF register values from 147_quant.dat*/
+{14700000, DIF_BPF_COEFF01, 0xffffffff},
+{14700000, DIF_BPF_COEFF23, 0x0009fffb},
+{14700000, DIF_BPF_COEFF45, 0xffe4002a},
+{14700000, DIF_BPF_COEFF67, 0x0025ff82},
+{14700000, DIF_BPF_COEFF89, 0x001400e0},
+{14700000, DIF_BPF_COEFF1011, 0xff3cff10},
+{14700000, DIF_BPF_COEFF1213, 0x01e10030},
+{14700000, DIF_BPF_COEFF1415, 0xfd1201a4},
+{14700000, DIF_BPF_COEFF1617, 0x0311fbcd},
+{14700000, DIF_BPF_COEFF1819, 0xfe88066a},
+{14700000, DIF_BPF_COEFF2021, 0xfdf1f92f},
+{14700000, DIF_BPF_COEFF2223, 0x06aa0449},
+{14700000, DIF_BPF_COEFF2425, 0xf57e0128},
+{14700000, DIF_BPF_COEFF2627, 0x0b7ef801},
+{14700000, DIF_BPF_COEFF2829, 0xf7b00da2},
+{14700000, DIF_BPF_COEFF3031, 0x0156f086},
+{14700000, DIF_BPF_COEFF3233, 0x07450c39},
+{14700000, DIF_BPF_COEFF3435, 0xf1acfb5c},
+{14700000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 147_quant.dat*/
+
+
+/*case 14800000:*/
+/* BEGIN - DIF BPF register values from 148_quant.dat*/
+{14800000, DIF_BPF_COEFF01, 0x0000fffe},
+{14800000, DIF_BPF_COEFF23, 0x00080002},
+{14800000, DIF_BPF_COEFF45, 0xffdf0019},
+{14800000, DIF_BPF_COEFF67, 0x003fff92},
+{14800000, DIF_BPF_COEFF89, 0xffd600f1},
+{14800000, DIF_BPF_COEFF1011, 0xff96feb6},
+{14800000, DIF_BPF_COEFF1213, 0x019700e1},
+{14800000, DIF_BPF_COEFF1415, 0xfd0500c2},
+{14800000, DIF_BPF_COEFF1617, 0x03b0fc84},
+{14800000, DIF_BPF_COEFF1819, 0xfd590649},
+{14800000, DIF_BPF_COEFF2021, 0xff5df87f},
+{14800000, DIF_BPF_COEFF2223, 0x058505aa},
+{14800000, DIF_BPF_COEFF2425, 0xf5e4ff91},
+{14800000, DIF_BPF_COEFF2627, 0x0bf9f93c},
+{14800000, DIF_BPF_COEFF2829, 0xf69d0d20},
+{14800000, DIF_BPF_COEFF3031, 0x0279f05e},
+{14800000, DIF_BPF_COEFF3233, 0x06880ca3},
+{14800000, DIF_BPF_COEFF3435, 0xf1e6fb28},
+{14800000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 148_quant.dat*/
+
+
+/*case 14900000:*/
+/* BEGIN - DIF BPF register values from 149_quant.dat*/
+{14900000, DIF_BPF_COEFF01, 0x0000fffd},
+{14900000, DIF_BPF_COEFF23, 0x00060009},
+{14900000, DIF_BPF_COEFF45, 0xffdf0004},
+{14900000, DIF_BPF_COEFF67, 0x0051ffb0},
+{14900000, DIF_BPF_COEFF89, 0xff9d00e8},
+{14900000, DIF_BPF_COEFF1011, 0xfffcfe7c},
+{14900000, DIF_BPF_COEFF1213, 0x01280180},
+{14900000, DIF_BPF_COEFF1415, 0xfd32ffd2},
+{14900000, DIF_BPF_COEFF1617, 0x0413fd6e},
+{14900000, DIF_BPF_COEFF1819, 0xfc4d05df},
+{14900000, DIF_BPF_COEFF2021, 0x00d1f812},
+{14900000, DIF_BPF_COEFF2223, 0x043506e4},
+{14900000, DIF_BPF_COEFF2425, 0xf685fdfb},
+{14900000, DIF_BPF_COEFF2627, 0x0c43fa8d},
+{14900000, DIF_BPF_COEFF2829, 0xf5a10c83},
+{14900000, DIF_BPF_COEFF3031, 0x0399f046},
+{14900000, DIF_BPF_COEFF3233, 0x05c70d08},
+{14900000, DIF_BPF_COEFF3435, 0xf222faf3},
+{14900000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 149_quant.dat*/
+
+
+/*case 15000000:*/
+/* BEGIN - DIF BPF register values from 150_quant.dat*/
+{15000000, DIF_BPF_COEFF01, 0x0000fffd},
+{15000000, DIF_BPF_COEFF23, 0x0003000f},
+{15000000, DIF_BPF_COEFF45, 0xffe5ffef},
+{15000000, DIF_BPF_COEFF67, 0x0057ffd9},
+{15000000, DIF_BPF_COEFF89, 0xff7000c4},
+{15000000, DIF_BPF_COEFF1011, 0x0062fe68},
+{15000000, DIF_BPF_COEFF1213, 0x009e01ff},
+{15000000, DIF_BPF_COEFF1415, 0xfd95fee6},
+{15000000, DIF_BPF_COEFF1617, 0x0435fe7d},
+{15000000, DIF_BPF_COEFF1819, 0xfb710530},
+{15000000, DIF_BPF_COEFF2021, 0x023cf7ee},
+{15000000, DIF_BPF_COEFF2223, 0x02c307ef},
+{15000000, DIF_BPF_COEFF2425, 0xf75efc70},
+{15000000, DIF_BPF_COEFF2627, 0x0c5cfbef},
+{15000000, DIF_BPF_COEFF2829, 0xf4c10bce},
+{15000000, DIF_BPF_COEFF3031, 0x04b3f03f},
+{15000000, DIF_BPF_COEFF3233, 0x05030d69},
+{15000000, DIF_BPF_COEFF3435, 0xf261fabf},
+{15000000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 150_quant.dat*/
+
+
+/*case 15100000:*/
+/* BEGIN - DIF BPF register values from 151_quant.dat*/
+{15100000, DIF_BPF_COEFF01, 0x0000fffd},
+{15100000, DIF_BPF_COEFF23, 0xffff0012},
+{15100000, DIF_BPF_COEFF45, 0xffefffdc},
+{15100000, DIF_BPF_COEFF67, 0x00510006},
+{15100000, DIF_BPF_COEFF89, 0xff540089},
+{15100000, DIF_BPF_COEFF1011, 0x00befe7c},
+{15100000, DIF_BPF_COEFF1213, 0x00060253},
+{15100000, DIF_BPF_COEFF1415, 0xfe27fe0d},
+{15100000, DIF_BPF_COEFF1617, 0x0413ffa2},
+{15100000, DIF_BPF_COEFF1819, 0xfad10446},
+{15100000, DIF_BPF_COEFF2021, 0x0390f812},
+{15100000, DIF_BPF_COEFF2223, 0x013b08c3},
+{15100000, DIF_BPF_COEFF2425, 0xf868faf6},
+{15100000, DIF_BPF_COEFF2627, 0x0c43fd5f},
+{15100000, DIF_BPF_COEFF2829, 0xf3fd0b02},
+{15100000, DIF_BPF_COEFF3031, 0x05c7f046},
+{15100000, DIF_BPF_COEFF3233, 0x043b0dc4},
+{15100000, DIF_BPF_COEFF3435, 0xf2a1fa8b},
+{15100000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 151_quant.dat*/
+
+
+/*case 15200000:*/
+/* BEGIN - DIF BPF register values from 152_quant.dat*/
+{15200000, DIF_BPF_COEFF01, 0x0001fffe},
+{15200000, DIF_BPF_COEFF23, 0xfffc0012},
+{15200000, DIF_BPF_COEFF45, 0xfffbffce},
+{15200000, DIF_BPF_COEFF67, 0x003f0033},
+{15200000, DIF_BPF_COEFF89, 0xff4e003f},
+{15200000, DIF_BPF_COEFF1011, 0x0106feb6},
+{15200000, DIF_BPF_COEFF1213, 0xff6e0276},
+{15200000, DIF_BPF_COEFF1415, 0xfeddfd56},
+{15200000, DIF_BPF_COEFF1617, 0x03b000cc},
+{15200000, DIF_BPF_COEFF1819, 0xfa740329},
+{15200000, DIF_BPF_COEFF2021, 0x04bff87f},
+{15200000, DIF_BPF_COEFF2223, 0xffaa095d},
+{15200000, DIF_BPF_COEFF2425, 0xf99ef995},
+{15200000, DIF_BPF_COEFF2627, 0x0bf9fed8},
+{15200000, DIF_BPF_COEFF2829, 0xf3590a1f},
+{15200000, DIF_BPF_COEFF3031, 0x06d2f05e},
+{15200000, DIF_BPF_COEFF3233, 0x03700e1b},
+{15200000, DIF_BPF_COEFF3435, 0xf2e4fa58},
+{15200000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 152_quant.dat*/
+
+
+/*case 115300000:*/
+/* BEGIN - DIF BPF register values from 153_quant.dat*/
+{15300000, DIF_BPF_COEFF01, 0x0001ffff},
+{15300000, DIF_BPF_COEFF23, 0xfff9000f},
+{15300000, DIF_BPF_COEFF45, 0x0009ffc8},
+{15300000, DIF_BPF_COEFF67, 0x00250059},
+{15300000, DIF_BPF_COEFF89, 0xff5effee},
+{15300000, DIF_BPF_COEFF1011, 0x0132ff10},
+{15300000, DIF_BPF_COEFF1213, 0xfee30265},
+{15300000, DIF_BPF_COEFF1415, 0xffaafccf},
+{15300000, DIF_BPF_COEFF1617, 0x031101eb},
+{15300000, DIF_BPF_COEFF1819, 0xfa6001e8},
+{15300000, DIF_BPF_COEFF2021, 0x05bdf92f},
+{15300000, DIF_BPF_COEFF2223, 0xfe1b09b6},
+{15300000, DIF_BPF_COEFF2425, 0xfafaf852},
+{15300000, DIF_BPF_COEFF2627, 0x0b7e0055},
+{15300000, DIF_BPF_COEFF2829, 0xf2d50929},
+{15300000, DIF_BPF_COEFF3031, 0x07d3f086},
+{15300000, DIF_BPF_COEFF3233, 0x02a30e6c},
+{15300000, DIF_BPF_COEFF3435, 0xf329fa24},
+{15300000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 153_quant.dat*/
+
+
+/*case 115400000:*/
+/* BEGIN - DIF BPF register values from 154_quant.dat*/
+{15400000, DIF_BPF_COEFF01, 0x00010001},
+{15400000, DIF_BPF_COEFF23, 0xfff80009},
+{15400000, DIF_BPF_COEFF45, 0x0015ffca},
+{15400000, DIF_BPF_COEFF67, 0x00050074},
+{15400000, DIF_BPF_COEFF89, 0xff81ff9f},
+{15400000, DIF_BPF_COEFF1011, 0x013dff82},
+{15400000, DIF_BPF_COEFF1213, 0xfe710221},
+{15400000, DIF_BPF_COEFF1415, 0x007cfc80},
+{15400000, DIF_BPF_COEFF1617, 0x024102ed},
+{15400000, DIF_BPF_COEFF1819, 0xfa940090},
+{15400000, DIF_BPF_COEFF2021, 0x0680fa1e},
+{15400000, DIF_BPF_COEFF2223, 0xfc9b09cd},
+{15400000, DIF_BPF_COEFF2425, 0xfc73f736},
+{15400000, DIF_BPF_COEFF2627, 0x0ad501d0},
+{15400000, DIF_BPF_COEFF2829, 0xf2740820},
+{15400000, DIF_BPF_COEFF3031, 0x08c9f0bd},
+{15400000, DIF_BPF_COEFF3233, 0x01d40eb9},
+{15400000, DIF_BPF_COEFF3435, 0xf371f9f1},
+{15400000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 154_quant.dat*/
+
+
+/*case 115500000:*/
+/* BEGIN - DIF BPF register values from 155_quant.dat*/
+{15500000, DIF_BPF_COEFF01, 0x00000002},
+{15500000, DIF_BPF_COEFF23, 0xfff80002},
+{15500000, DIF_BPF_COEFF45, 0x001effd5},
+{15500000, DIF_BPF_COEFF67, 0xffe5007f},
+{15500000, DIF_BPF_COEFF89, 0xffb4ff5b},
+{15500000, DIF_BPF_COEFF1011, 0x01280000},
+{15500000, DIF_BPF_COEFF1213, 0xfe2401b0},
+{15500000, DIF_BPF_COEFF1415, 0x0146fc70},
+{15500000, DIF_BPF_COEFF1617, 0x014d03c6},
+{15500000, DIF_BPF_COEFF1819, 0xfb10ff32},
+{15500000, DIF_BPF_COEFF2021, 0x0701fb41},
+{15500000, DIF_BPF_COEFF2223, 0xfb3709a1},
+{15500000, DIF_BPF_COEFF2425, 0xfe00f644},
+{15500000, DIF_BPF_COEFF2627, 0x0a000345},
+{15500000, DIF_BPF_COEFF2829, 0xf2350708},
+{15500000, DIF_BPF_COEFF3031, 0x09b2f104},
+{15500000, DIF_BPF_COEFF3233, 0x01050eff},
+{15500000, DIF_BPF_COEFF3435, 0xf3baf9be},
+{15500000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 155_quant.dat*/
+
+
+/*case 115600000:*/
+/* BEGIN - DIF BPF register values from 156_quant.dat*/
+{15600000, DIF_BPF_COEFF01, 0x00000003},
+{15600000, DIF_BPF_COEFF23, 0xfff9fffb},
+{15600000, DIF_BPF_COEFF45, 0x0022ffe6},
+{15600000, DIF_BPF_COEFF67, 0xffc9007a},
+{15600000, DIF_BPF_COEFF89, 0xfff0ff29},
+{15600000, DIF_BPF_COEFF1011, 0x00f2007e},
+{15600000, DIF_BPF_COEFF1213, 0xfe01011b},
+{15600000, DIF_BPF_COEFF1415, 0x01f6fc9e},
+{15600000, DIF_BPF_COEFF1617, 0x00440467},
+{15600000, DIF_BPF_COEFF1819, 0xfbccfdde},
+{15600000, DIF_BPF_COEFF2021, 0x0738fc90},
+{15600000, DIF_BPF_COEFF2223, 0xf9f70934},
+{15600000, DIF_BPF_COEFF2425, 0xff99f582},
+{15600000, DIF_BPF_COEFF2627, 0x090204b0},
+{15600000, DIF_BPF_COEFF2829, 0xf21a05e1},
+{15600000, DIF_BPF_COEFF3031, 0x0a8df15a},
+{15600000, DIF_BPF_COEFF3233, 0x00340f41},
+{15600000, DIF_BPF_COEFF3435, 0xf405f98b},
+{15600000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 156_quant.dat*/
+
+
+/*case 115700000:*/
+/* BEGIN - DIF BPF register values from 157_quant.dat*/
+{15700000, DIF_BPF_COEFF01, 0x00000003},
+{15700000, DIF_BPF_COEFF23, 0xfffcfff4},
+{15700000, DIF_BPF_COEFF45, 0x0020fffa},
+{15700000, DIF_BPF_COEFF67, 0xffb40064},
+{15700000, DIF_BPF_COEFF89, 0x002fff11},
+{15700000, DIF_BPF_COEFF1011, 0x00a400f0},
+{15700000, DIF_BPF_COEFF1213, 0xfe0d006e},
+{15700000, DIF_BPF_COEFF1415, 0x0281fd09},
+{15700000, DIF_BPF_COEFF1617, 0xff3604c9},
+{15700000, DIF_BPF_COEFF1819, 0xfcbffca2},
+{15700000, DIF_BPF_COEFF2021, 0x0726fdfe},
+{15700000, DIF_BPF_COEFF2223, 0xf8e80888},
+{15700000, DIF_BPF_COEFF2425, 0x0134f4f3},
+{15700000, DIF_BPF_COEFF2627, 0x07e1060c},
+{15700000, DIF_BPF_COEFF2829, 0xf22304af},
+{15700000, DIF_BPF_COEFF3031, 0x0b59f1be},
+{15700000, DIF_BPF_COEFF3233, 0xff640f7d},
+{15700000, DIF_BPF_COEFF3435, 0xf452f959},
+{15700000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 157_quant.dat*/
+
+
+/*case 115800000:*/
+/* BEGIN - DIF BPF register values from 158_quant.dat*/
+{15800000, DIF_BPF_COEFF01, 0x00000003},
+{15800000, DIF_BPF_COEFF23, 0x0000fff0},
+{15800000, DIF_BPF_COEFF45, 0x001a0010},
+{15800000, DIF_BPF_COEFF67, 0xffaa0041},
+{15800000, DIF_BPF_COEFF89, 0x0067ff13},
+{15800000, DIF_BPF_COEFF1011, 0x0043014a},
+{15800000, DIF_BPF_COEFF1213, 0xfe46ffb9},
+{15800000, DIF_BPF_COEFF1415, 0x02dbfda8},
+{15800000, DIF_BPF_COEFF1617, 0xfe3504e5},
+{15800000, DIF_BPF_COEFF1819, 0xfddcfb8d},
+{15800000, DIF_BPF_COEFF2021, 0x06c9ff7e},
+{15800000, DIF_BPF_COEFF2223, 0xf81107a2},
+{15800000, DIF_BPF_COEFF2425, 0x02c9f49a},
+{15800000, DIF_BPF_COEFF2627, 0x069f0753},
+{15800000, DIF_BPF_COEFF2829, 0xf2500373},
+{15800000, DIF_BPF_COEFF3031, 0x0c14f231},
+{15800000, DIF_BPF_COEFF3233, 0xfe930fb3},
+{15800000, DIF_BPF_COEFF3435, 0xf4a1f927},
+{15800000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 158_quant.dat*/
+
+
+/*case 115900000:*/
+/* BEGIN - DIF BPF register values from 159_quant.dat*/
+{15900000, DIF_BPF_COEFF01, 0xffff0002},
+{15900000, DIF_BPF_COEFF23, 0x0003ffee},
+{15900000, DIF_BPF_COEFF45, 0x000f0023},
+{15900000, DIF_BPF_COEFF67, 0xffac0016},
+{15900000, DIF_BPF_COEFF89, 0x0093ff31},
+{15900000, DIF_BPF_COEFF1011, 0xffdc0184},
+{15900000, DIF_BPF_COEFF1213, 0xfea6ff09},
+{15900000, DIF_BPF_COEFF1415, 0x02fdfe70},
+{15900000, DIF_BPF_COEFF1617, 0xfd5104ba},
+{15900000, DIF_BPF_COEFF1819, 0xff15faac},
+{15900000, DIF_BPF_COEFF2021, 0x06270103},
+{15900000, DIF_BPF_COEFF2223, 0xf7780688},
+{15900000, DIF_BPF_COEFF2425, 0x044df479},
+{15900000, DIF_BPF_COEFF2627, 0x05430883},
+{15900000, DIF_BPF_COEFF2829, 0xf2a00231},
+{15900000, DIF_BPF_COEFF3031, 0x0cbef2b2},
+{15900000, DIF_BPF_COEFF3233, 0xfdc40fe3},
+{15900000, DIF_BPF_COEFF3435, 0xf4f2f8f5},
+{15900000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 159_quant.dat*/
+
+
+/*case 116000000:*/
+/* BEGIN - DIF BPF register values from 160_quant.dat*/
+{16000000, DIF_BPF_COEFF01, 0xffff0001},
+{16000000, DIF_BPF_COEFF23, 0x0006ffef},
+{16000000, DIF_BPF_COEFF45, 0x00020031},
+{16000000, DIF_BPF_COEFF67, 0xffbaffe8},
+{16000000, DIF_BPF_COEFF89, 0x00adff66},
+{16000000, DIF_BPF_COEFF1011, 0xff790198},
+{16000000, DIF_BPF_COEFF1213, 0xff26fe6e},
+{16000000, DIF_BPF_COEFF1415, 0x02e5ff55},
+{16000000, DIF_BPF_COEFF1617, 0xfc99044a},
+{16000000, DIF_BPF_COEFF1819, 0x005bfa09},
+{16000000, DIF_BPF_COEFF2021, 0x0545027f},
+{16000000, DIF_BPF_COEFF2223, 0xf7230541},
+{16000000, DIF_BPF_COEFF2425, 0x05b8f490},
+{16000000, DIF_BPF_COEFF2627, 0x03d20997},
+{16000000, DIF_BPF_COEFF2829, 0xf31300eb},
+{16000000, DIF_BPF_COEFF3031, 0x0d55f341},
+{16000000, DIF_BPF_COEFF3233, 0xfcf6100e},
+{16000000, DIF_BPF_COEFF3435, 0xf544f8c3},
+{16000000, DIF_BPF_COEFF36, 0x110d0000},
+/* END - DIF BPF register values from 160_quant.dat*/
+};
+
+#endif
diff --git a/drivers/media/video/cx231xx/cx231xx-dvb.c b/drivers/media/video/cx231xx/cx231xx-dvb.c
new file mode 100644
index 00000000..7c4e360b
--- /dev/null
+++ b/drivers/media/video/cx231xx/cx231xx-dvb.c
@@ -0,0 +1,796 @@
+/*
+ DVB device driver for cx231xx
+
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
+ Based on em28xx driver
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#include "cx231xx.h"
+#include <media/v4l2-common.h>
+#include <media/videobuf-vmalloc.h>
+
+#include "xc5000.h"
+#include "s5h1432.h"
+#include "tda18271.h"
+#include "s5h1411.h"
+#include "lgdt3305.h"
+#include "mb86a20s.h"
+
+MODULE_DESCRIPTION("driver for cx231xx based DVB cards");
+MODULE_AUTHOR("Srinivasa Deevi <srinivasa.deevi@conexant.com>");
+MODULE_LICENSE("GPL");
+
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable debug messages [dvb]");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define dprintk(level, fmt, arg...) do { \
+if (debug >= level) \
+ printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->name, ## arg); \
+} while (0)
+
+#define CX231XX_DVB_NUM_BUFS 5
+#define CX231XX_DVB_MAX_PACKETSIZE 564
+#define CX231XX_DVB_MAX_PACKETS 64
+
+struct cx231xx_dvb {
+ struct dvb_frontend *frontend;
+
+ /* feed count management */
+ struct mutex lock;
+ int nfeeds;
+
+ /* general boilerplate stuff */
+ struct dvb_adapter adapter;
+ struct dvb_demux demux;
+ struct dmxdev dmxdev;
+ struct dmx_frontend fe_hw;
+ struct dmx_frontend fe_mem;
+ struct dvb_net net;
+};
+
+static struct s5h1432_config dvico_s5h1432_config = {
+ .output_mode = S5H1432_SERIAL_OUTPUT,
+ .gpio = S5H1432_GPIO_ON,
+ .qam_if = S5H1432_IF_4000,
+ .vsb_if = S5H1432_IF_4000,
+ .inversion = S5H1432_INVERSION_OFF,
+ .status_mode = S5H1432_DEMODLOCKING,
+ .mpeg_timing = S5H1432_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct tda18271_std_map cnxt_rde253s_tda18271_std_map = {
+ .dvbt_6 = { .if_freq = 4000, .agc_mode = 3, .std = 4,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+ .dvbt_7 = { .if_freq = 4000, .agc_mode = 3, .std = 5,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+ .dvbt_8 = { .if_freq = 4000, .agc_mode = 3, .std = 6,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+};
+
+static struct tda18271_std_map mb86a20s_tda18271_config = {
+ .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4,
+ .if_lvl = 7, .rfagc_top = 0x37, },
+};
+
+static struct tda18271_config cnxt_rde253s_tunerconfig = {
+ .std_map = &cnxt_rde253s_tda18271_std_map,
+ .gate = TDA18271_GATE_ANALOG,
+};
+
+static struct s5h1411_config tda18271_s5h1411_config = {
+ .output_mode = S5H1411_SERIAL_OUTPUT,
+ .gpio = S5H1411_GPIO_OFF,
+ .vsb_if = S5H1411_IF_3250,
+ .qam_if = S5H1411_IF_4000,
+ .inversion = S5H1411_INVERSION_ON,
+ .status_mode = S5H1411_DEMODLOCKING,
+ .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+static struct s5h1411_config xc5000_s5h1411_config = {
+ .output_mode = S5H1411_SERIAL_OUTPUT,
+ .gpio = S5H1411_GPIO_OFF,
+ .vsb_if = S5H1411_IF_3250,
+ .qam_if = S5H1411_IF_3250,
+ .inversion = S5H1411_INVERSION_OFF,
+ .status_mode = S5H1411_DEMODLOCKING,
+ .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct lgdt3305_config hcw_lgdt3305_config = {
+ .i2c_addr = 0x0e,
+ .mpeg_mode = LGDT3305_MPEG_SERIAL,
+ .tpclk_edge = LGDT3305_TPCLK_FALLING_EDGE,
+ .tpvalid_polarity = LGDT3305_TP_VALID_HIGH,
+ .deny_i2c_rptr = 1,
+ .spectral_inversion = 1,
+ .qam_if_khz = 4000,
+ .vsb_if_khz = 3250,
+};
+
+static struct tda18271_std_map hauppauge_tda18271_std_map = {
+ .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 4,
+ .if_lvl = 1, .rfagc_top = 0x58, },
+ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 5,
+ .if_lvl = 1, .rfagc_top = 0x58, },
+};
+
+static struct tda18271_config hcw_tda18271_config = {
+ .std_map = &hauppauge_tda18271_std_map,
+ .gate = TDA18271_GATE_DIGITAL,
+};
+
+static const struct mb86a20s_config pv_mb86a20s_config = {
+ .demod_address = 0x10,
+ .is_serial = true,
+};
+
+static struct tda18271_config pv_tda18271_config = {
+ .std_map = &mb86a20s_tda18271_config,
+ .gate = TDA18271_GATE_DIGITAL,
+ .small_i2c = TDA18271_03_BYTE_CHUNK_INIT,
+};
+
+static inline void print_err_status(struct cx231xx *dev, int packet, int status)
+{
+ char *errmsg = "Unknown";
+
+ switch (status) {
+ case -ENOENT:
+ errmsg = "unlinked synchronuously";
+ break;
+ case -ECONNRESET:
+ errmsg = "unlinked asynchronuously";
+ break;
+ case -ENOSR:
+ errmsg = "Buffer error (overrun)";
+ break;
+ case -EPIPE:
+ errmsg = "Stalled (device not responding)";
+ break;
+ case -EOVERFLOW:
+ errmsg = "Babble (bad cable?)";
+ break;
+ case -EPROTO:
+ errmsg = "Bit-stuff error (bad cable?)";
+ break;
+ case -EILSEQ:
+ errmsg = "CRC/Timeout (could be anything)";
+ break;
+ case -ETIME:
+ errmsg = "Device does not respond";
+ break;
+ }
+ if (packet < 0) {
+ dprintk(1, "URB status %d [%s].\n", status, errmsg);
+ } else {
+ dprintk(1, "URB packet %d, status %d [%s].\n",
+ packet, status, errmsg);
+ }
+}
+
+static inline int dvb_isoc_copy(struct cx231xx *dev, struct urb *urb)
+{
+ int i;
+
+ if (!dev)
+ return 0;
+
+ if (dev->state & DEV_DISCONNECTED)
+ return 0;
+
+ if (urb->status < 0) {
+ print_err_status(dev, -1, urb->status);
+ if (urb->status == -ENOENT)
+ return 0;
+ }
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ int status = urb->iso_frame_desc[i].status;
+
+ if (status < 0) {
+ print_err_status(dev, i, status);
+ if (urb->iso_frame_desc[i].status != -EPROTO)
+ continue;
+ }
+
+ dvb_dmx_swfilter(&dev->dvb->demux,
+ urb->transfer_buffer +
+ urb->iso_frame_desc[i].offset,
+ urb->iso_frame_desc[i].actual_length);
+ }
+
+ return 0;
+}
+
+static inline int dvb_bulk_copy(struct cx231xx *dev, struct urb *urb)
+{
+ if (!dev)
+ return 0;
+
+ if (dev->state & DEV_DISCONNECTED)
+ return 0;
+
+ if (urb->status < 0) {
+ print_err_status(dev, -1, urb->status);
+ if (urb->status == -ENOENT)
+ return 0;
+ }
+
+ /* Feed the transport payload into the kernel demux */
+ dvb_dmx_swfilter(&dev->dvb->demux,
+ urb->transfer_buffer, urb->actual_length);
+
+ return 0;
+}
+
+static int start_streaming(struct cx231xx_dvb *dvb)
+{
+ int rc;
+ struct cx231xx *dev = dvb->adapter.priv;
+
+ if (dev->USE_ISO) {
+ cx231xx_info("DVB transfer mode is ISO.\n");
+ mutex_lock(&dev->i2c_lock);
+ cx231xx_enable_i2c_port_3(dev, false);
+ cx231xx_set_alt_setting(dev, INDEX_TS1, 4);
+ cx231xx_enable_i2c_port_3(dev, true);
+ mutex_unlock(&dev->i2c_lock);
+ rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
+ if (rc < 0)
+ return rc;
+ dev->mode_tv = 1;
+ return cx231xx_init_isoc(dev, CX231XX_DVB_MAX_PACKETS,
+ CX231XX_DVB_NUM_BUFS,
+ dev->ts1_mode.max_pkt_size,
+ dvb_isoc_copy);
+ } else {
+ cx231xx_info("DVB transfer mode is BULK.\n");
+ cx231xx_set_alt_setting(dev, INDEX_TS1, 0);
+ rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
+ if (rc < 0)
+ return rc;
+ dev->mode_tv = 1;
+ return cx231xx_init_bulk(dev, CX231XX_DVB_MAX_PACKETS,
+ CX231XX_DVB_NUM_BUFS,
+ dev->ts1_mode.max_pkt_size,
+ dvb_bulk_copy);
+ }
+
+}
+
+static int stop_streaming(struct cx231xx_dvb *dvb)
+{
+ struct cx231xx *dev = dvb->adapter.priv;
+
+ if (dev->USE_ISO)
+ cx231xx_uninit_isoc(dev);
+ else
+ cx231xx_uninit_bulk(dev);
+
+ cx231xx_set_mode(dev, CX231XX_SUSPEND);
+
+ return 0;
+}
+
+static int start_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct cx231xx_dvb *dvb = demux->priv;
+ int rc, ret;
+
+ if (!demux->dmx.frontend)
+ return -EINVAL;
+
+ mutex_lock(&dvb->lock);
+ dvb->nfeeds++;
+ rc = dvb->nfeeds;
+
+ if (dvb->nfeeds == 1) {
+ ret = start_streaming(dvb);
+ if (ret < 0)
+ rc = ret;
+ }
+
+ mutex_unlock(&dvb->lock);
+ return rc;
+}
+
+static int stop_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct cx231xx_dvb *dvb = demux->priv;
+ int err = 0;
+
+ mutex_lock(&dvb->lock);
+ dvb->nfeeds--;
+
+ if (0 == dvb->nfeeds)
+ err = stop_streaming(dvb);
+
+ mutex_unlock(&dvb->lock);
+ return err;
+}
+
+/* ------------------------------------------------------------------ */
+static int cx231xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire)
+{
+ struct cx231xx *dev = fe->dvb->priv;
+
+ if (acquire)
+ return cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
+ else
+ return cx231xx_set_mode(dev, CX231XX_SUSPEND);
+}
+
+/* ------------------------------------------------------------------ */
+
+static struct xc5000_config cnxt_rde250_tunerconfig = {
+ .i2c_address = 0x61,
+ .if_khz = 4000,
+};
+static struct xc5000_config cnxt_rdu250_tunerconfig = {
+ .i2c_address = 0x61,
+ .if_khz = 3250,
+};
+
+/* ------------------------------------------------------------------ */
+#if 0
+static int attach_xc5000(u8 addr, struct cx231xx *dev)
+{
+
+ struct dvb_frontend *fe;
+ struct xc5000_config cfg;
+
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.i2c_adap = &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap;
+ cfg.i2c_addr = addr;
+
+ if (!dev->dvb->frontend) {
+ printk(KERN_ERR "%s/2: dvb frontend not attached. "
+ "Can't attach xc5000\n", dev->name);
+ return -EINVAL;
+ }
+
+ fe = dvb_attach(xc5000_attach, dev->dvb->frontend, &cfg);
+ if (!fe) {
+ printk(KERN_ERR "%s/2: xc5000 attach failed\n", dev->name);
+ dvb_frontend_detach(dev->dvb->frontend);
+ dev->dvb->frontend = NULL;
+ return -EINVAL;
+ }
+
+ printk(KERN_INFO "%s/2: xc5000 attached\n", dev->name);
+
+ return 0;
+}
+#endif
+
+int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq)
+{
+ int status = 0;
+
+ if ((dev->dvb != NULL) && (dev->dvb->frontend != NULL)) {
+
+ struct dvb_tuner_ops *dops = &dev->dvb->frontend->ops.tuner_ops;
+
+ if (dops->set_analog_params != NULL) {
+ struct analog_parameters params;
+
+ params.frequency = freq;
+ params.std = dev->norm;
+ params.mode = 0; /* 0- Air; 1 - cable */
+ /*params.audmode = ; */
+
+ /* Set the analog parameters to set the frequency */
+ dops->set_analog_params(dev->dvb->frontend, &params);
+ }
+
+ }
+
+ return status;
+}
+
+int cx231xx_reset_analog_tuner(struct cx231xx *dev)
+{
+ int status = 0;
+
+ if ((dev->dvb != NULL) && (dev->dvb->frontend != NULL)) {
+
+ struct dvb_tuner_ops *dops = &dev->dvb->frontend->ops.tuner_ops;
+
+ if (dops->init != NULL && !dev->xc_fw_load_done) {
+
+ cx231xx_info("Reloading firmware for XC5000\n");
+ status = dops->init(dev->dvb->frontend);
+ if (status == 0) {
+ dev->xc_fw_load_done = 1;
+ cx231xx_info
+ ("XC5000 firmware download completed\n");
+ } else {
+ dev->xc_fw_load_done = 0;
+ cx231xx_info
+ ("XC5000 firmware download failed !!!\n");
+ }
+ }
+
+ }
+
+ return status;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int register_dvb(struct cx231xx_dvb *dvb,
+ struct module *module,
+ struct cx231xx *dev, struct device *device)
+{
+ int result;
+
+ mutex_init(&dvb->lock);
+
+ /* register adapter */
+ result = dvb_register_adapter(&dvb->adapter, dev->name, module, device,
+ adapter_nr);
+ if (result < 0) {
+ printk(KERN_WARNING
+ "%s: dvb_register_adapter failed (errno = %d)\n",
+ dev->name, result);
+ goto fail_adapter;
+ }
+
+ /* Ensure all frontends negotiate bus access */
+ dvb->frontend->ops.ts_bus_ctrl = cx231xx_dvb_bus_ctrl;
+
+ dvb->adapter.priv = dev;
+
+ /* register frontend */
+ result = dvb_register_frontend(&dvb->adapter, dvb->frontend);
+ if (result < 0) {
+ printk(KERN_WARNING
+ "%s: dvb_register_frontend failed (errno = %d)\n",
+ dev->name, result);
+ goto fail_frontend;
+ }
+
+ /* register demux stuff */
+ dvb->demux.dmx.capabilities =
+ DMX_TS_FILTERING | DMX_SECTION_FILTERING |
+ DMX_MEMORY_BASED_FILTERING;
+ dvb->demux.priv = dvb;
+ dvb->demux.filternum = 256;
+ dvb->demux.feednum = 256;
+ dvb->demux.start_feed = start_feed;
+ dvb->demux.stop_feed = stop_feed;
+
+ result = dvb_dmx_init(&dvb->demux);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n",
+ dev->name, result);
+ goto fail_dmx;
+ }
+
+ dvb->dmxdev.filternum = 256;
+ dvb->dmxdev.demux = &dvb->demux.dmx;
+ dvb->dmxdev.capabilities = 0;
+ result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n",
+ dev->name, result);
+ goto fail_dmxdev;
+ }
+
+ dvb->fe_hw.source = DMX_FRONTEND_0;
+ result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+ if (result < 0) {
+ printk(KERN_WARNING
+ "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n",
+ dev->name, result);
+ goto fail_fe_hw;
+ }
+
+ dvb->fe_mem.source = DMX_MEMORY_FE;
+ result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+ if (result < 0) {
+ printk(KERN_WARNING
+ "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
+ dev->name, result);
+ goto fail_fe_mem;
+ }
+
+ result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+ if (result < 0) {
+ printk(KERN_WARNING
+ "%s: connect_frontend failed (errno = %d)\n", dev->name,
+ result);
+ goto fail_fe_conn;
+ }
+
+ /* register network adapter */
+ dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx);
+ return 0;
+
+fail_fe_conn:
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+fail_fe_mem:
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+fail_fe_hw:
+ dvb_dmxdev_release(&dvb->dmxdev);
+fail_dmxdev:
+ dvb_dmx_release(&dvb->demux);
+fail_dmx:
+ dvb_unregister_frontend(dvb->frontend);
+fail_frontend:
+ dvb_frontend_detach(dvb->frontend);
+ dvb_unregister_adapter(&dvb->adapter);
+fail_adapter:
+ return result;
+}
+
+static void unregister_dvb(struct cx231xx_dvb *dvb)
+{
+ dvb_net_release(&dvb->net);
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+ dvb_dmxdev_release(&dvb->dmxdev);
+ dvb_dmx_release(&dvb->demux);
+ dvb_unregister_frontend(dvb->frontend);
+ dvb_frontend_detach(dvb->frontend);
+ dvb_unregister_adapter(&dvb->adapter);
+}
+
+static int dvb_init(struct cx231xx *dev)
+{
+ int result = 0;
+ struct cx231xx_dvb *dvb;
+
+ if (!dev->board.has_dvb) {
+ /* This device does not support the extension */
+ return 0;
+ }
+
+ dvb = kzalloc(sizeof(struct cx231xx_dvb), GFP_KERNEL);
+
+ if (dvb == NULL) {
+ printk(KERN_INFO "cx231xx_dvb: memory allocation failed\n");
+ return -ENOMEM;
+ }
+ dev->dvb = dvb;
+ dev->cx231xx_set_analog_freq = cx231xx_set_analog_freq;
+ dev->cx231xx_reset_analog_tuner = cx231xx_reset_analog_tuner;
+
+ mutex_lock(&dev->lock);
+ cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
+ cx231xx_demod_reset(dev);
+ /* init frontend */
+ switch (dev->model) {
+ case CX231XX_BOARD_CNXT_CARRAERA:
+ case CX231XX_BOARD_CNXT_RDE_250:
+
+ dev->dvb->frontend = dvb_attach(s5h1432_attach,
+ &dvico_s5h1432_config,
+ &dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap);
+
+ if (dev->dvb->frontend == NULL) {
+ printk(DRIVER_NAME
+ ": Failed to attach s5h1432 front end\n");
+ result = -EINVAL;
+ goto out_free;
+ }
+
+ /* define general-purpose callback pointer */
+ dvb->frontend->callback = cx231xx_tuner_callback;
+
+ if (!dvb_attach(xc5000_attach, dev->dvb->frontend,
+ &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
+ &cnxt_rde250_tunerconfig)) {
+ result = -EINVAL;
+ goto out_free;
+ }
+
+ break;
+ case CX231XX_BOARD_CNXT_SHELBY:
+ case CX231XX_BOARD_CNXT_RDU_250:
+
+ dev->dvb->frontend = dvb_attach(s5h1411_attach,
+ &xc5000_s5h1411_config,
+ &dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap);
+
+ if (dev->dvb->frontend == NULL) {
+ printk(DRIVER_NAME
+ ": Failed to attach s5h1411 front end\n");
+ result = -EINVAL;
+ goto out_free;
+ }
+
+ /* define general-purpose callback pointer */
+ dvb->frontend->callback = cx231xx_tuner_callback;
+
+ if (!dvb_attach(xc5000_attach, dev->dvb->frontend,
+ &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
+ &cnxt_rdu250_tunerconfig)) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ break;
+ case CX231XX_BOARD_CNXT_RDE_253S:
+
+ dev->dvb->frontend = dvb_attach(s5h1432_attach,
+ &dvico_s5h1432_config,
+ &dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap);
+
+ if (dev->dvb->frontend == NULL) {
+ printk(DRIVER_NAME
+ ": Failed to attach s5h1432 front end\n");
+ result = -EINVAL;
+ goto out_free;
+ }
+
+ /* define general-purpose callback pointer */
+ dvb->frontend->callback = cx231xx_tuner_callback;
+
+ if (!dvb_attach(tda18271_attach, dev->dvb->frontend,
+ 0x60, &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
+ &cnxt_rde253s_tunerconfig)) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ break;
+ case CX231XX_BOARD_CNXT_RDU_253S:
+
+ dev->dvb->frontend = dvb_attach(s5h1411_attach,
+ &tda18271_s5h1411_config,
+ &dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap);
+
+ if (dev->dvb->frontend == NULL) {
+ printk(DRIVER_NAME
+ ": Failed to attach s5h1411 front end\n");
+ result = -EINVAL;
+ goto out_free;
+ }
+
+ /* define general-purpose callback pointer */
+ dvb->frontend->callback = cx231xx_tuner_callback;
+
+ if (!dvb_attach(tda18271_attach, dev->dvb->frontend,
+ 0x60, &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
+ &cnxt_rde253s_tunerconfig)) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ break;
+ case CX231XX_BOARD_HAUPPAUGE_EXETER:
+
+ printk(KERN_INFO "%s: looking for tuner / demod on i2c bus: %d\n",
+ __func__, i2c_adapter_id(&dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap));
+
+ dev->dvb->frontend = dvb_attach(lgdt3305_attach,
+ &hcw_lgdt3305_config,
+ &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap);
+
+ if (dev->dvb->frontend == NULL) {
+ printk(DRIVER_NAME
+ ": Failed to attach LG3305 front end\n");
+ result = -EINVAL;
+ goto out_free;
+ }
+
+ /* define general-purpose callback pointer */
+ dvb->frontend->callback = cx231xx_tuner_callback;
+
+ dvb_attach(tda18271_attach, dev->dvb->frontend,
+ 0x60, &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
+ &hcw_tda18271_config);
+ break;
+
+ case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID:
+ case CX231XX_BOARD_KWORLD_UB430_USB_HYBRID:
+
+ printk(KERN_INFO "%s: looking for demod on i2c bus: %d\n",
+ __func__, i2c_adapter_id(&dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap));
+
+ dev->dvb->frontend = dvb_attach(mb86a20s_attach,
+ &pv_mb86a20s_config,
+ &dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap);
+
+ if (dev->dvb->frontend == NULL) {
+ printk(DRIVER_NAME
+ ": Failed to attach mb86a20s demod\n");
+ result = -EINVAL;
+ goto out_free;
+ }
+
+ /* define general-purpose callback pointer */
+ dvb->frontend->callback = cx231xx_tuner_callback;
+
+ dvb_attach(tda18271_attach, dev->dvb->frontend,
+ 0x60, &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
+ &pv_tda18271_config);
+ break;
+
+ default:
+ printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card"
+ " isn't supported yet\n", dev->name);
+ break;
+ }
+ if (NULL == dvb->frontend) {
+ printk(KERN_ERR
+ "%s/2: frontend initialization failed\n", dev->name);
+ result = -EINVAL;
+ goto out_free;
+ }
+
+ /* register everything */
+ result = register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev);
+
+ if (result < 0)
+ goto out_free;
+
+
+ printk(KERN_INFO "Successfully loaded cx231xx-dvb\n");
+
+ret:
+ cx231xx_set_mode(dev, CX231XX_SUSPEND);
+ mutex_unlock(&dev->lock);
+ return result;
+
+out_free:
+ kfree(dvb);
+ dev->dvb = NULL;
+ goto ret;
+}
+
+static int dvb_fini(struct cx231xx *dev)
+{
+ if (!dev->board.has_dvb) {
+ /* This device does not support the extension */
+ return 0;
+ }
+
+ if (dev->dvb) {
+ unregister_dvb(dev->dvb);
+ dev->dvb = NULL;
+ }
+
+ return 0;
+}
+
+static struct cx231xx_ops dvb_ops = {
+ .id = CX231XX_DVB,
+ .name = "Cx231xx dvb Extension",
+ .init = dvb_init,
+ .fini = dvb_fini,
+};
+
+static int __init cx231xx_dvb_register(void)
+{
+ return cx231xx_register_extension(&dvb_ops);
+}
+
+static void __exit cx231xx_dvb_unregister(void)
+{
+ cx231xx_unregister_extension(&dvb_ops);
+}
+
+module_init(cx231xx_dvb_register);
+module_exit(cx231xx_dvb_unregister);
diff --git a/drivers/media/video/cx231xx/cx231xx-i2c.c b/drivers/media/video/cx231xx/cx231xx-i2c.c
new file mode 100644
index 00000000..925f3a04
--- /dev/null
+++ b/drivers/media/video/cx231xx/cx231xx-i2c.c
@@ -0,0 +1,536 @@
+/*
+ cx231xx-i2c.c - driver for Conexant Cx23100/101/102 USB video capture devices
+
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
+ Based on em28xx driver
+ Based on Cx23885 driver
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <media/v4l2-common.h>
+#include <media/tuner.h>
+
+#include "cx231xx.h"
+
+/* ----------------------------------------------------------- */
+
+static unsigned int i2c_scan;
+module_param(i2c_scan, int, 0444);
+MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time");
+
+static unsigned int i2c_debug;
+module_param(i2c_debug, int, 0644);
+MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
+
+#define dprintk1(lvl, fmt, args...) \
+do { \
+ if (i2c_debug >= lvl) { \
+ printk(fmt, ##args); \
+ } \
+} while (0)
+
+#define dprintk2(lvl, fmt, args...) \
+do { \
+ if (i2c_debug >= lvl) { \
+ printk(KERN_DEBUG "%s at %s: " fmt, \
+ dev->name, __func__ , ##args); \
+ } \
+} while (0)
+
+static inline bool is_tuner(struct cx231xx *dev, struct cx231xx_i2c *bus,
+ const struct i2c_msg *msg, int tuner_type)
+{
+ if (bus->nr != dev->board.tuner_i2c_master)
+ return false;
+
+ if (msg->addr != dev->board.tuner_addr)
+ return false;
+
+ if (dev->tuner_type != tuner_type)
+ return false;
+
+ return true;
+}
+
+/*
+ * cx231xx_i2c_send_bytes()
+ */
+int cx231xx_i2c_send_bytes(struct i2c_adapter *i2c_adap,
+ const struct i2c_msg *msg)
+{
+ struct cx231xx_i2c *bus = i2c_adap->algo_data;
+ struct cx231xx *dev = bus->dev;
+ struct cx231xx_i2c_xfer_data req_data;
+ int status = 0;
+ u16 size = 0;
+ u8 loop = 0;
+ u8 saddr_len = 1;
+ u8 *buf_ptr = NULL;
+ u16 saddr = 0;
+ u8 need_gpio = 0;
+
+ if (is_tuner(dev, bus, msg, TUNER_XC5000)) {
+ size = msg->len;
+
+ if (size == 2) { /* register write sub addr */
+ /* Just writing sub address will cause problem
+ * to XC5000. So ignore the request */
+ return 0;
+ } else if (size == 4) { /* register write with sub addr */
+ if (msg->len >= 2)
+ saddr = msg->buf[0] << 8 | msg->buf[1];
+ else if (msg->len == 1)
+ saddr = msg->buf[0];
+
+ switch (saddr) {
+ case 0x0000: /* start tuner calibration mode */
+ need_gpio = 1;
+ /* FW Loading is done */
+ dev->xc_fw_load_done = 1;
+ break;
+ case 0x000D: /* Set signal source */
+ case 0x0001: /* Set TV standard - Video */
+ case 0x0002: /* Set TV standard - Audio */
+ case 0x0003: /* Set RF Frequency */
+ need_gpio = 1;
+ break;
+ default:
+ if (dev->xc_fw_load_done)
+ need_gpio = 1;
+ break;
+ }
+
+ if (need_gpio) {
+ dprintk1(1,
+ "GPIO WRITE: addr 0x%x, len %d, saddr 0x%x\n",
+ msg->addr, msg->len, saddr);
+
+ return dev->cx231xx_gpio_i2c_write(dev,
+ msg->addr,
+ msg->buf,
+ msg->len);
+ }
+ }
+
+ /* special case for Xc5000 tuner case */
+ saddr_len = 1;
+
+ /* adjust the length to correct length */
+ size -= saddr_len;
+ buf_ptr = (u8 *) (msg->buf + 1);
+
+ do {
+ /* prepare xfer_data struct */
+ req_data.dev_addr = msg->addr;
+ req_data.direction = msg->flags;
+ req_data.saddr_len = saddr_len;
+ req_data.saddr_dat = msg->buf[0];
+ req_data.buf_size = size > 16 ? 16 : size;
+ req_data.p_buffer = (u8 *) (buf_ptr + loop * 16);
+
+ bus->i2c_nostop = (size > 16) ? 1 : 0;
+ bus->i2c_reserve = (loop == 0) ? 0 : 1;
+
+ /* usb send command */
+ status = dev->cx231xx_send_usb_command(bus, &req_data);
+ loop++;
+
+ if (size >= 16)
+ size -= 16;
+ else
+ size = 0;
+
+ } while (size > 0);
+
+ bus->i2c_nostop = 0;
+ bus->i2c_reserve = 0;
+
+ } else { /* regular case */
+
+ /* prepare xfer_data struct */
+ req_data.dev_addr = msg->addr;
+ req_data.direction = msg->flags;
+ req_data.saddr_len = 0;
+ req_data.saddr_dat = 0;
+ req_data.buf_size = msg->len;
+ req_data.p_buffer = msg->buf;
+
+ /* usb send command */
+ status = dev->cx231xx_send_usb_command(bus, &req_data);
+ }
+
+ return status < 0 ? status : 0;
+}
+
+/*
+ * cx231xx_i2c_recv_bytes()
+ * read a byte from the i2c device
+ */
+static int cx231xx_i2c_recv_bytes(struct i2c_adapter *i2c_adap,
+ const struct i2c_msg *msg)
+{
+ struct cx231xx_i2c *bus = i2c_adap->algo_data;
+ struct cx231xx *dev = bus->dev;
+ struct cx231xx_i2c_xfer_data req_data;
+ int status = 0;
+ u16 saddr = 0;
+ u8 need_gpio = 0;
+
+ if (is_tuner(dev, bus, msg, TUNER_XC5000)) {
+ if (msg->len == 2)
+ saddr = msg->buf[0] << 8 | msg->buf[1];
+ else if (msg->len == 1)
+ saddr = msg->buf[0];
+
+ if (dev->xc_fw_load_done) {
+
+ switch (saddr) {
+ case 0x0009: /* BUSY check */
+ dprintk1(1,
+ "GPIO R E A D: Special case BUSY check \n");
+ /*Try read BUSY register, just set it to zero*/
+ msg->buf[0] = 0;
+ if (msg->len == 2)
+ msg->buf[1] = 0;
+ return 0;
+ case 0x0004: /* read Lock status */
+ need_gpio = 1;
+ break;
+
+ }
+
+ if (need_gpio) {
+ /* this is a special case to handle Xceive tuner
+ clock stretch issue with gpio based I2C */
+
+ dprintk1(1,
+ "GPIO R E A D: addr 0x%x, len %d, saddr 0x%x\n",
+ msg->addr, msg->len,
+ msg->buf[0] << 8 | msg->buf[1]);
+
+ status =
+ dev->cx231xx_gpio_i2c_write(dev, msg->addr,
+ msg->buf,
+ msg->len);
+ status =
+ dev->cx231xx_gpio_i2c_read(dev, msg->addr,
+ msg->buf,
+ msg->len);
+ return status;
+ }
+ }
+
+ /* prepare xfer_data struct */
+ req_data.dev_addr = msg->addr;
+ req_data.direction = msg->flags;
+ req_data.saddr_len = msg->len;
+ req_data.saddr_dat = msg->buf[0] << 8 | msg->buf[1];
+ req_data.buf_size = msg->len;
+ req_data.p_buffer = msg->buf;
+
+ /* usb send command */
+ status = dev->cx231xx_send_usb_command(bus, &req_data);
+
+ } else {
+
+ /* prepare xfer_data struct */
+ req_data.dev_addr = msg->addr;
+ req_data.direction = msg->flags;
+ req_data.saddr_len = 0;
+ req_data.saddr_dat = 0;
+ req_data.buf_size = msg->len;
+ req_data.p_buffer = msg->buf;
+
+ /* usb send command */
+ status = dev->cx231xx_send_usb_command(bus, &req_data);
+ }
+
+ return status < 0 ? status : 0;
+}
+
+/*
+ * cx231xx_i2c_recv_bytes_with_saddr()
+ * read a byte from the i2c device
+ */
+static int cx231xx_i2c_recv_bytes_with_saddr(struct i2c_adapter *i2c_adap,
+ const struct i2c_msg *msg1,
+ const struct i2c_msg *msg2)
+{
+ struct cx231xx_i2c *bus = i2c_adap->algo_data;
+ struct cx231xx *dev = bus->dev;
+ struct cx231xx_i2c_xfer_data req_data;
+ int status = 0;
+ u16 saddr = 0;
+ u8 need_gpio = 0;
+
+ if (msg1->len == 2)
+ saddr = msg1->buf[0] << 8 | msg1->buf[1];
+ else if (msg1->len == 1)
+ saddr = msg1->buf[0];
+
+ if (is_tuner(dev, bus, msg2, TUNER_XC5000)) {
+ if ((msg2->len < 16)) {
+
+ dprintk1(1,
+ "i2c_read: addr 0x%x, len %d, saddr 0x%x, len %d\n",
+ msg2->addr, msg2->len, saddr, msg1->len);
+
+ switch (saddr) {
+ case 0x0008: /* read FW load status */
+ need_gpio = 1;
+ break;
+ case 0x0004: /* read Lock status */
+ need_gpio = 1;
+ break;
+ }
+
+ if (need_gpio) {
+ status =
+ dev->cx231xx_gpio_i2c_write(dev, msg1->addr,
+ msg1->buf,
+ msg1->len);
+ status =
+ dev->cx231xx_gpio_i2c_read(dev, msg2->addr,
+ msg2->buf,
+ msg2->len);
+ return status;
+ }
+ }
+ }
+
+ /* prepare xfer_data struct */
+ req_data.dev_addr = msg2->addr;
+ req_data.direction = msg2->flags;
+ req_data.saddr_len = msg1->len;
+ req_data.saddr_dat = saddr;
+ req_data.buf_size = msg2->len;
+ req_data.p_buffer = msg2->buf;
+
+ /* usb send command */
+ status = dev->cx231xx_send_usb_command(bus, &req_data);
+
+ return status < 0 ? status : 0;
+}
+
+/*
+ * cx231xx_i2c_check_for_device()
+ * check if there is a i2c_device at the supplied address
+ */
+static int cx231xx_i2c_check_for_device(struct i2c_adapter *i2c_adap,
+ const struct i2c_msg *msg)
+{
+ struct cx231xx_i2c *bus = i2c_adap->algo_data;
+ struct cx231xx *dev = bus->dev;
+ struct cx231xx_i2c_xfer_data req_data;
+ int status = 0;
+
+ /* prepare xfer_data struct */
+ req_data.dev_addr = msg->addr;
+ req_data.direction = msg->flags;
+ req_data.saddr_len = 0;
+ req_data.saddr_dat = 0;
+ req_data.buf_size = 0;
+ req_data.p_buffer = NULL;
+
+ /* usb send command */
+ status = dev->cx231xx_send_usb_command(bus, &req_data);
+
+ return status < 0 ? status : 0;
+}
+
+/*
+ * cx231xx_i2c_xfer()
+ * the main i2c transfer function
+ */
+static int cx231xx_i2c_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg msgs[], int num)
+{
+ struct cx231xx_i2c *bus = i2c_adap->algo_data;
+ struct cx231xx *dev = bus->dev;
+ int addr, rc, i, byte;
+
+ if (num <= 0)
+ return 0;
+ mutex_lock(&dev->i2c_lock);
+ for (i = 0; i < num; i++) {
+
+ addr = msgs[i].addr >> 1;
+
+ dprintk2(2, "%s %s addr=%x len=%d:",
+ (msgs[i].flags & I2C_M_RD) ? "read" : "write",
+ i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len);
+ if (!msgs[i].len) {
+ /* no len: check only for device presence */
+ rc = cx231xx_i2c_check_for_device(i2c_adap, &msgs[i]);
+ if (rc < 0) {
+ dprintk2(2, " no device\n");
+ mutex_unlock(&dev->i2c_lock);
+ return rc;
+ }
+
+ } else if (msgs[i].flags & I2C_M_RD) {
+ /* read bytes */
+ rc = cx231xx_i2c_recv_bytes(i2c_adap, &msgs[i]);
+ if (i2c_debug >= 2) {
+ for (byte = 0; byte < msgs[i].len; byte++)
+ printk(" %02x", msgs[i].buf[byte]);
+ }
+ } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) &&
+ msgs[i].addr == msgs[i + 1].addr
+ && (msgs[i].len <= 2) && (bus->nr < 3)) {
+ /* read bytes */
+ rc = cx231xx_i2c_recv_bytes_with_saddr(i2c_adap,
+ &msgs[i],
+ &msgs[i + 1]);
+ if (i2c_debug >= 2) {
+ for (byte = 0; byte < msgs[i].len; byte++)
+ printk(" %02x", msgs[i].buf[byte]);
+ }
+ i++;
+ } else {
+ /* write bytes */
+ if (i2c_debug >= 2) {
+ for (byte = 0; byte < msgs[i].len; byte++)
+ printk(" %02x", msgs[i].buf[byte]);
+ }
+ rc = cx231xx_i2c_send_bytes(i2c_adap, &msgs[i]);
+ }
+ if (rc < 0)
+ goto err;
+ if (i2c_debug >= 2)
+ printk("\n");
+ }
+ mutex_unlock(&dev->i2c_lock);
+ return num;
+err:
+ dprintk2(2, " ERROR: %i\n", rc);
+ mutex_unlock(&dev->i2c_lock);
+ return rc;
+}
+
+/* ----------------------------------------------------------- */
+
+/*
+ * functionality()
+ */
+static u32 functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm cx231xx_algo = {
+ .master_xfer = cx231xx_i2c_xfer,
+ .functionality = functionality,
+};
+
+static struct i2c_adapter cx231xx_adap_template = {
+ .owner = THIS_MODULE,
+ .name = "cx231xx",
+ .algo = &cx231xx_algo,
+};
+
+static struct i2c_client cx231xx_client_template = {
+ .name = "cx231xx internal",
+};
+
+/* ----------------------------------------------------------- */
+
+/*
+ * i2c_devs
+ * incomplete list of known devices
+ */
+static char *i2c_devs[128] = {
+ [0x60 >> 1] = "colibri",
+ [0x88 >> 1] = "hammerhead",
+ [0x8e >> 1] = "CIR",
+ [0x32 >> 1] = "GeminiIII",
+ [0x02 >> 1] = "Aquarius",
+ [0xa0 >> 1] = "eeprom",
+ [0xc0 >> 1] = "tuner",
+ [0xc2 >> 1] = "tuner",
+};
+
+/*
+ * cx231xx_do_i2c_scan()
+ * check i2c address range for devices
+ */
+void cx231xx_do_i2c_scan(struct cx231xx *dev, struct i2c_client *c)
+{
+ unsigned char buf;
+ int i, rc;
+
+ cx231xx_info(": Checking for I2C devices ..\n");
+ for (i = 0; i < 128; i++) {
+ c->addr = i;
+ rc = i2c_master_recv(c, &buf, 0);
+ if (rc < 0)
+ continue;
+ cx231xx_info("%s: i2c scan: found device @ 0x%x [%s]\n",
+ dev->name, i << 1,
+ i2c_devs[i] ? i2c_devs[i] : "???");
+ }
+ cx231xx_info(": Completed Checking for I2C devices.\n");
+}
+
+/*
+ * cx231xx_i2c_register()
+ * register i2c bus
+ */
+int cx231xx_i2c_register(struct cx231xx_i2c *bus)
+{
+ struct cx231xx *dev = bus->dev;
+
+ BUG_ON(!dev->cx231xx_send_usb_command);
+
+ memcpy(&bus->i2c_adap, &cx231xx_adap_template, sizeof(bus->i2c_adap));
+ memcpy(&bus->i2c_algo, &cx231xx_algo, sizeof(bus->i2c_algo));
+ memcpy(&bus->i2c_client, &cx231xx_client_template,
+ sizeof(bus->i2c_client));
+
+ bus->i2c_adap.dev.parent = &dev->udev->dev;
+
+ strlcpy(bus->i2c_adap.name, bus->dev->name, sizeof(bus->i2c_adap.name));
+
+ bus->i2c_algo.data = bus;
+ bus->i2c_adap.algo_data = bus;
+ i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev);
+ i2c_add_adapter(&bus->i2c_adap);
+
+ bus->i2c_client.adapter = &bus->i2c_adap;
+
+ if (0 == bus->i2c_rc) {
+ if (i2c_scan)
+ cx231xx_do_i2c_scan(dev, &bus->i2c_client);
+ } else
+ cx231xx_warn("%s: i2c bus %d register FAILED\n",
+ dev->name, bus->nr);
+
+ return bus->i2c_rc;
+}
+
+/*
+ * cx231xx_i2c_unregister()
+ * unregister i2c_bus
+ */
+int cx231xx_i2c_unregister(struct cx231xx_i2c *bus)
+{
+ i2c_del_adapter(&bus->i2c_adap);
+ return 0;
+}
diff --git a/drivers/media/video/cx231xx/cx231xx-input.c b/drivers/media/video/cx231xx/cx231xx-input.c
new file mode 100644
index 00000000..96176e9d
--- /dev/null
+++ b/drivers/media/video/cx231xx/cx231xx-input.c
@@ -0,0 +1,119 @@
+/*
+ * cx231xx IR glue driver
+ *
+ * Copyright (C) 2010 Mauro Carvalho Chehab <mchehab@redhat.com>
+ *
+ * Polaris (cx231xx) has its support for IR's with a design close to MCE.
+ * however, a few designs are using an external I2C chip for IR, instead
+ * of using the one provided by the chip.
+ * This driver provides support for those extra devices
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include "cx231xx.h"
+#include <linux/usb.h>
+#include <linux/slab.h>
+
+#define MODULE_NAME "cx231xx-input"
+
+static int get_key_isdbt(struct IR_i2c *ir, u32 *ir_key,
+ u32 *ir_raw)
+{
+ int rc;
+ u8 cmd, scancode;
+
+ dev_dbg(&ir->rc->input_dev->dev, "%s\n", __func__);
+
+ /* poll IR chip */
+ rc = i2c_master_recv(ir->c, &cmd, 1);
+ if (rc < 0)
+ return rc;
+ if (rc != 1)
+ return -EIO;
+
+ /* it seems that 0xFE indicates that a button is still hold
+ down, while 0xff indicates that no button is hold
+ down. 0xfe sequences are sometimes interrupted by 0xFF */
+
+ if (cmd == 0xff)
+ return 0;
+
+ scancode =
+ ((cmd & 0x01) ? 0x80 : 0) |
+ ((cmd & 0x02) ? 0x40 : 0) |
+ ((cmd & 0x04) ? 0x20 : 0) |
+ ((cmd & 0x08) ? 0x10 : 0) |
+ ((cmd & 0x10) ? 0x08 : 0) |
+ ((cmd & 0x20) ? 0x04 : 0) |
+ ((cmd & 0x40) ? 0x02 : 0) |
+ ((cmd & 0x80) ? 0x01 : 0);
+
+ dev_dbg(&ir->rc->input_dev->dev, "cmd %02x, scan = %02x\n",
+ cmd, scancode);
+
+ *ir_key = scancode;
+ *ir_raw = scancode;
+ return 1;
+}
+
+int cx231xx_ir_init(struct cx231xx *dev)
+{
+ struct i2c_board_info info;
+ u8 ir_i2c_bus;
+
+ dev_dbg(&dev->udev->dev, "%s\n", __func__);
+
+ /* Only initialize if a rc keycode map is defined */
+ if (!cx231xx_boards[dev->model].rc_map_name)
+ return -ENODEV;
+
+ request_module("ir-kbd-i2c");
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ memset(&dev->init_data, 0, sizeof(dev->init_data));
+ dev->init_data.rc_dev = rc_allocate_device();
+ if (!dev->init_data.rc_dev)
+ return -ENOMEM;
+
+ dev->init_data.name = cx231xx_boards[dev->model].name;
+
+ strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
+ info.platform_data = &dev->init_data;
+
+ /*
+ * Board-dependent values
+ *
+ * For now, there's just one type of hardware design using
+ * an i2c device.
+ */
+ dev->init_data.get_key = get_key_isdbt;
+ dev->init_data.ir_codes = cx231xx_boards[dev->model].rc_map_name;
+ /* The i2c micro-controller only outputs the cmd part of NEC protocol */
+ dev->init_data.rc_dev->scanmask = 0xff;
+ dev->init_data.rc_dev->driver_name = "cx231xx";
+ dev->init_data.type = RC_TYPE_NEC;
+ info.addr = 0x30;
+
+ /* Load and bind ir-kbd-i2c */
+ ir_i2c_bus = cx231xx_boards[dev->model].ir_i2c_master;
+ dev_dbg(&dev->udev->dev, "Trying to bind ir at bus %d, addr 0x%02x\n",
+ ir_i2c_bus, info.addr);
+ dev->ir_i2c_client = i2c_new_device(&dev->i2c_bus[ir_i2c_bus].i2c_adap, &info);
+
+ return 0;
+}
+
+void cx231xx_ir_exit(struct cx231xx *dev)
+{
+ if (dev->ir_i2c_client)
+ i2c_unregister_device(dev->ir_i2c_client);
+ dev->ir_i2c_client = NULL;
+}
diff --git a/drivers/media/video/cx231xx/cx231xx-pcb-cfg.c b/drivers/media/video/cx231xx/cx231xx-pcb-cfg.c
new file mode 100644
index 00000000..7473c33e
--- /dev/null
+++ b/drivers/media/video/cx231xx/cx231xx-pcb-cfg.c
@@ -0,0 +1,795 @@
+/*
+ cx231xx-pcb-config.c - driver for Conexant
+ Cx23100/101/102 USB video capture devices
+
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "cx231xx.h"
+#include "cx231xx-conf-reg.h"
+
+static unsigned int pcb_debug;
+module_param(pcb_debug, int, 0644);
+MODULE_PARM_DESC(pcb_debug, "enable pcb config debug messages [video]");
+
+/******************************************************************************/
+
+struct pcb_config cx231xx_Scenario[] = {
+ {
+ INDEX_SELFPOWER_DIGITAL_ONLY, /* index */
+ USB_SELF_POWER, /* power_type */
+ 0, /* speed , not decide yet */
+ MOD_DIGITAL, /* mode */
+ SOURCE_TS_BDA, /* ts1_source, digital tv only */
+ NOT_SUPPORTED, /* ts2_source */
+ NOT_SUPPORTED, /* analog source */
+
+ 0, /* digital_index */
+ 0, /* analog index */
+ 0, /* dif_index */
+ 0, /* external_index */
+
+ 1, /* only one configuration */
+ {
+ {
+ 0, /* config index */
+ {
+ 0, /* interrupt ep index */
+ 1, /* ts1 index */
+ NOT_SUPPORTED, /* TS2 index */
+ NOT_SUPPORTED, /* AUDIO */
+ NOT_SUPPORTED, /* VIDEO */
+ NOT_SUPPORTED, /* VANC */
+ NOT_SUPPORTED, /* HANC */
+ NOT_SUPPORTED /* ir_index */
+ }
+ ,
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ }
+ ,
+ /* full-speed config */
+ {
+ {
+ 0, /* config index */
+ {
+ 0, /* interrupt ep index */
+ 1, /* ts1 index */
+ NOT_SUPPORTED, /* TS2 index */
+ NOT_SUPPORTED, /* AUDIO */
+ NOT_SUPPORTED, /* VIDEO */
+ NOT_SUPPORTED, /* VANC */
+ NOT_SUPPORTED, /* HANC */
+ NOT_SUPPORTED /* ir_index */
+ }
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ }
+ }
+ ,
+
+ {
+ INDEX_SELFPOWER_DUAL_DIGITAL, /* index */
+ USB_SELF_POWER, /* power_type */
+ 0, /* speed , not decide yet */
+ MOD_DIGITAL, /* mode */
+ SOURCE_TS_BDA, /* ts1_source, digital tv only */
+ 0, /* ts2_source,need update from register */
+ NOT_SUPPORTED, /* analog source */
+ 0, /* digital_index */
+ 0, /* analog index */
+ 0, /* dif_index */
+ 0, /* external_index */
+
+ 1, /* only one configuration */
+ {
+ {
+ 0, /* config index */
+ {
+ 0, /* interrupt ep index */
+ 1, /* ts1 index */
+ 2, /* TS2 index */
+ NOT_SUPPORTED, /* AUDIO */
+ NOT_SUPPORTED, /* VIDEO */
+ NOT_SUPPORTED, /* VANC */
+ NOT_SUPPORTED, /* HANC */
+ NOT_SUPPORTED /* ir_index */
+ }
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ }
+ ,
+ /* full-speed */
+ {
+ {
+ 0, /* config index */
+ {
+ 0, /* interrupt ep index */
+ 1, /* ts1 index */
+ 2, /* TS2 index */
+ NOT_SUPPORTED, /* AUDIO */
+ NOT_SUPPORTED, /* VIDEO */
+ NOT_SUPPORTED, /* VANC */
+ NOT_SUPPORTED, /* HANC */
+ NOT_SUPPORTED /* ir_index */
+ }
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ }
+ }
+ ,
+
+ {
+ INDEX_SELFPOWER_ANALOG_ONLY, /* index */
+ USB_SELF_POWER, /* power_type */
+ 0, /* speed , not decide yet */
+ MOD_ANALOG | MOD_DIF | MOD_EXTERNAL, /* mode ,analog tv only */
+ NOT_SUPPORTED, /* ts1_source, NOT SUPPORT */
+ NOT_SUPPORTED, /* ts2_source,NOT SUPPORT */
+ 0, /* analog source, need update */
+
+ 0, /* digital_index */
+ 0, /* analog index */
+ 0, /* dif_index */
+ 0, /* external_index */
+
+ 1, /* only one configuration */
+ {
+ {
+ 0, /* config index */
+ {
+ 0, /* interrupt ep index */
+ NOT_SUPPORTED, /* ts1 index */
+ NOT_SUPPORTED, /* TS2 index */
+ 1, /* AUDIO */
+ 2, /* VIDEO */
+ 3, /* VANC */
+ 4, /* HANC */
+ NOT_SUPPORTED /* ir_index */
+ }
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ }
+ ,
+ /* full-speed */
+ {
+ {
+ 0, /* config index */
+ {
+ 0, /* interrupt ep index */
+ NOT_SUPPORTED, /* ts1 index */
+ NOT_SUPPORTED, /* TS2 index */
+ 1, /* AUDIO */
+ 2, /* VIDEO */
+ NOT_SUPPORTED, /* VANC */
+ NOT_SUPPORTED, /* HANC */
+ NOT_SUPPORTED /* ir_index */
+ }
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ }
+ }
+ ,
+
+ {
+ INDEX_SELFPOWER_DUAL, /* index */
+ USB_SELF_POWER, /* power_type */
+ 0, /* speed , not decide yet */
+ /* mode ,analog tv and digital path */
+ MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL,
+ 0, /* ts1_source,will update in register */
+ NOT_SUPPORTED, /* ts2_source,NOT SUPPORT */
+ 0, /* analog source need update */
+ 0, /* digital_index */
+ 0, /* analog index */
+ 0, /* dif_index */
+ 0, /* external_index */
+ 1, /* only one configuration */
+ {
+ {
+ 0, /* config index */
+ {
+ 0, /* interrupt ep index */
+ 1, /* ts1 index */
+ NOT_SUPPORTED, /* TS2 index */
+ 2, /* AUDIO */
+ 3, /* VIDEO */
+ 4, /* VANC */
+ 5, /* HANC */
+ NOT_SUPPORTED /* ir_index */
+ }
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ }
+ ,
+ /* full-speed */
+ {
+ {
+ 0, /* config index */
+ {
+ 0, /* interrupt ep index */
+ 1, /* ts1 index */
+ NOT_SUPPORTED, /* TS2 index */
+ 2, /* AUDIO */
+ 3, /* VIDEO */
+ NOT_SUPPORTED, /* VANC */
+ NOT_SUPPORTED, /* HANC */
+ NOT_SUPPORTED /* ir_index */
+ }
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ }
+ }
+ ,
+
+ {
+ INDEX_SELFPOWER_TRIPLE, /* index */
+ USB_SELF_POWER, /* power_type */
+ 0, /* speed , not decide yet */
+ /* mode ,analog tv and digital path */
+ MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL,
+ 0, /* ts1_source, update in register */
+ 0, /* ts2_source,update in register */
+ 0, /* analog source, need update */
+
+ 0, /* digital_index */
+ 0, /* analog index */
+ 0, /* dif_index */
+ 0, /* external_index */
+ 1, /* only one configuration */
+ {
+ {
+ 0, /* config index */
+ {
+ 0, /* interrupt ep index */
+ 1, /* ts1 index */
+ 2, /* TS2 index */
+ 3, /* AUDIO */
+ 4, /* VIDEO */
+ 5, /* VANC */
+ 6, /* HANC */
+ NOT_SUPPORTED /* ir_index */
+ }
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ }
+ ,
+ /* full-speed */
+ {
+ {
+ 0, /* config index */
+ {
+ 0, /* interrupt ep index */
+ 1, /* ts1 index */
+ 2, /* TS2 index */
+ 3, /* AUDIO */
+ 4, /* VIDEO */
+ NOT_SUPPORTED, /* VANC */
+ NOT_SUPPORTED, /* HANC */
+ NOT_SUPPORTED /* ir_index */
+ }
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ }
+ }
+ ,
+
+ {
+ INDEX_SELFPOWER_COMPRESSOR, /* index */
+ USB_SELF_POWER, /* power_type */
+ 0, /* speed , not decide yet */
+ /* mode ,analog tv AND DIGITAL path */
+ MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL,
+ NOT_SUPPORTED, /* ts1_source, disable */
+ SOURCE_TS_BDA, /* ts2_source */
+ 0, /* analog source,need update */
+ 0, /* digital_index */
+ 0, /* analog index */
+ 0, /* dif_index */
+ 0, /* external_index */
+ 1, /* only one configuration */
+ {
+ {
+ 0, /* config index */
+ {
+ 0, /* interrupt ep index */
+ NOT_SUPPORTED, /* ts1 index */
+ 1, /* TS2 index */
+ 2, /* AUDIO */
+ 3, /* VIDEO */
+ 4, /* VANC */
+ 5, /* HANC */
+ NOT_SUPPORTED /* ir_index */
+ }
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ }
+ ,
+ /* full-speed */
+ {
+ {
+ 0, /* config index */
+ {
+ 0, /* interrupt ep index */
+ NOT_SUPPORTED, /* ts1 index */
+ 1, /* TS2 index */
+ 2, /* AUDIO */
+ 3, /* VIDEO */
+ NOT_SUPPORTED, /* VANC */
+ NOT_SUPPORTED, /* HANC */
+ NOT_SUPPORTED /* ir_index */
+ }
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ }
+ }
+ ,
+
+ {
+ INDEX_BUSPOWER_DIGITAL_ONLY, /* index */
+ USB_BUS_POWER, /* power_type */
+ 0, /* speed , not decide yet */
+ MOD_DIGITAL, /* mode ,analog tv AND DIGITAL path */
+ SOURCE_TS_BDA, /* ts1_source, disable */
+ NOT_SUPPORTED, /* ts2_source */
+ NOT_SUPPORTED, /* analog source */
+
+ 0, /* digital_index */
+ 0, /* analog index */
+ 0, /* dif_index */
+ 0, /* external_index */
+
+ 1, /* only one configuration */
+ {
+ {
+ 0, /* config index */
+ {
+ 0, /* interrupt ep index = 2 */
+ 1, /* ts1 index */
+ NOT_SUPPORTED, /* TS2 index */
+ NOT_SUPPORTED, /* AUDIO */
+ NOT_SUPPORTED, /* VIDEO */
+ NOT_SUPPORTED, /* VANC */
+ NOT_SUPPORTED, /* HANC */
+ NOT_SUPPORTED /* ir_index */
+ }
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ }
+ ,
+ /* full-speed */
+ {
+ {
+ 0, /* config index */
+ {
+ 0, /* interrupt ep index = 2 */
+ 1, /* ts1 index */
+ NOT_SUPPORTED, /* TS2 index */
+ NOT_SUPPORTED, /* AUDIO */
+ NOT_SUPPORTED, /* VIDEO */
+ NOT_SUPPORTED, /* VANC */
+ NOT_SUPPORTED, /* HANC */
+ NOT_SUPPORTED /* ir_index */
+ }
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ }
+ }
+ ,
+ {
+ INDEX_BUSPOWER_ANALOG_ONLY, /* index */
+ USB_BUS_POWER, /* power_type */
+ 0, /* speed , not decide yet */
+ MOD_ANALOG, /* mode ,analog tv AND DIGITAL path */
+ NOT_SUPPORTED, /* ts1_source, disable */
+ NOT_SUPPORTED, /* ts2_source */
+ SOURCE_ANALOG, /* analog source--analog */
+ 0, /* digital_index */
+ 0, /* analog index */
+ 0, /* dif_index */
+ 0, /* external_index */
+ 1, /* only one configuration */
+ {
+ {
+ 0, /* config index */
+ {
+ 0, /* interrupt ep index */
+ NOT_SUPPORTED, /* ts1 index */
+ NOT_SUPPORTED, /* TS2 index */
+ 1, /* AUDIO */
+ 2, /* VIDEO */
+ 3, /* VANC */
+ 4, /* HANC */
+ NOT_SUPPORTED /* ir_index */
+ }
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ }
+ ,
+ { /* full-speed */
+ {
+ 0, /* config index */
+ {
+ 0, /* interrupt ep index */
+ NOT_SUPPORTED, /* ts1 index */
+ NOT_SUPPORTED, /* TS2 index */
+ 1, /* AUDIO */
+ 2, /* VIDEO */
+ NOT_SUPPORTED, /* VANC */
+ NOT_SUPPORTED, /* HANC */
+ NOT_SUPPORTED /* ir_index */
+ }
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ }
+ }
+ ,
+ {
+ INDEX_BUSPOWER_DIF_ONLY, /* index */
+ USB_BUS_POWER, /* power_type */
+ 0, /* speed , not decide yet */
+ /* mode ,analog tv AND DIGITAL path */
+ MOD_DIF | MOD_ANALOG | MOD_DIGITAL | MOD_EXTERNAL,
+ SOURCE_TS_BDA, /* ts1_source, disable */
+ NOT_SUPPORTED, /* ts2_source */
+ SOURCE_DIF | SOURCE_ANALOG | SOURCE_EXTERNAL, /* analog source, dif */
+ 0, /* digital_index */
+ 0, /* analog index */
+ 0, /* dif_index */
+ 0, /* external_index */
+ 1, /* only one configuration */
+ {
+ {
+ 0, /* config index */
+ {
+ 0, /* interrupt ep index */
+ 1, /* ts1 index */
+ NOT_SUPPORTED, /* TS2 index */
+ 2, /* AUDIO */
+ 3, /* VIDEO */
+ 4, /* VANC */
+ 5, /* HANC */
+ NOT_SUPPORTED /* ir_index */
+ }
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ }
+ ,
+ { /* full speed */
+ {
+ 0, /* config index */
+ {
+ 0, /* interrupt ep index */
+ 1, /* ts1 index */
+ NOT_SUPPORTED, /* TS2 index */
+ 2, /* AUDIO */
+ 3, /* VIDEO */
+ NOT_SUPPORTED, /* VANC */
+ NOT_SUPPORTED, /* HANC */
+ NOT_SUPPORTED /* ir_index */
+ }
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ ,
+ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
+ NOT_SUPPORTED}
+ }
+ }
+ }
+ ,
+
+};
+
+/*****************************************************************/
+
+u32 initialize_cx231xx(struct cx231xx *dev)
+{
+ u32 config_info = 0;
+ struct pcb_config *p_pcb_info;
+ u8 usb_speed = 1; /* from register,1--HS, 0--FS */
+ u8 data[4] = { 0, 0, 0, 0 };
+ u32 ts1_source = 0;
+ u32 ts2_source = 0;
+ u32 analog_source = 0;
+ u8 _current_scenario_idx = 0xff;
+
+ ts1_source = SOURCE_TS_BDA;
+ ts2_source = SOURCE_TS_BDA;
+
+ /* read board config register to find out which
+ pcb config it is related to */
+ cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT, data, 4);
+
+ config_info = *((u32 *) data);
+ usb_speed = (u8) (config_info & 0x1);
+
+ /* Verify this device belongs to Bus power or Self power device */
+ if (config_info & BUS_POWER) { /* bus-power */
+ switch (config_info & BUSPOWER_MASK) {
+ case TS1_PORT | BUS_POWER:
+ cx231xx_Scenario[INDEX_BUSPOWER_DIGITAL_ONLY].speed =
+ usb_speed;
+ p_pcb_info =
+ &cx231xx_Scenario[INDEX_BUSPOWER_DIGITAL_ONLY];
+ _current_scenario_idx = INDEX_BUSPOWER_DIGITAL_ONLY;
+ break;
+ case AVDEC_ENABLE | BUS_POWER:
+ cx231xx_Scenario[INDEX_BUSPOWER_ANALOG_ONLY].speed =
+ usb_speed;
+ p_pcb_info =
+ &cx231xx_Scenario[INDEX_BUSPOWER_ANALOG_ONLY];
+ _current_scenario_idx = INDEX_BUSPOWER_ANALOG_ONLY;
+ break;
+ case AVDEC_ENABLE | BUS_POWER | TS1_PORT:
+ cx231xx_Scenario[INDEX_BUSPOWER_DIF_ONLY].speed =
+ usb_speed;
+ p_pcb_info = &cx231xx_Scenario[INDEX_BUSPOWER_DIF_ONLY];
+ _current_scenario_idx = INDEX_BUSPOWER_DIF_ONLY;
+ break;
+ default:
+ cx231xx_info("bad config in buspower!!!!\n");
+ cx231xx_info("config_info=%x\n",
+ (config_info & BUSPOWER_MASK));
+ return 1;
+ }
+ } else { /* self-power */
+
+ switch (config_info & SELFPOWER_MASK) {
+ case TS1_PORT | SELF_POWER:
+ cx231xx_Scenario[INDEX_SELFPOWER_DIGITAL_ONLY].speed =
+ usb_speed;
+ p_pcb_info =
+ &cx231xx_Scenario[INDEX_SELFPOWER_DIGITAL_ONLY];
+ _current_scenario_idx = INDEX_SELFPOWER_DIGITAL_ONLY;
+ break;
+ case TS1_TS2_PORT | SELF_POWER:
+ cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL].speed =
+ usb_speed;
+ cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL].
+ ts2_source = ts2_source;
+ p_pcb_info =
+ &cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL];
+ _current_scenario_idx = INDEX_SELFPOWER_DUAL_DIGITAL;
+ break;
+ case AVDEC_ENABLE | SELF_POWER:
+ cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY].speed =
+ usb_speed;
+ cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY].
+ analog_source = analog_source;
+ p_pcb_info =
+ &cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY];
+ _current_scenario_idx = INDEX_SELFPOWER_ANALOG_ONLY;
+ break;
+ case AVDEC_ENABLE | TS1_PORT | SELF_POWER:
+ cx231xx_Scenario[INDEX_SELFPOWER_DUAL].speed =
+ usb_speed;
+ cx231xx_Scenario[INDEX_SELFPOWER_DUAL].ts1_source =
+ ts1_source;
+ cx231xx_Scenario[INDEX_SELFPOWER_DUAL].analog_source =
+ analog_source;
+ p_pcb_info = &cx231xx_Scenario[INDEX_SELFPOWER_DUAL];
+ _current_scenario_idx = INDEX_SELFPOWER_DUAL;
+ break;
+ case AVDEC_ENABLE | TS1_TS2_PORT | SELF_POWER:
+ cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].speed =
+ usb_speed;
+ cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].ts1_source =
+ ts1_source;
+ cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].ts2_source =
+ ts2_source;
+ cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].analog_source =
+ analog_source;
+ p_pcb_info = &cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE];
+ _current_scenario_idx = INDEX_SELFPOWER_TRIPLE;
+ break;
+ case AVDEC_ENABLE | TS1VIP_TS2_PORT | SELF_POWER:
+ cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR].speed =
+ usb_speed;
+ cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR].
+ analog_source = analog_source;
+ p_pcb_info =
+ &cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR];
+ _current_scenario_idx = INDEX_SELFPOWER_COMPRESSOR;
+ break;
+ default:
+ cx231xx_info("bad senario!!!!!\n");
+ cx231xx_info("config_info=%x\n",
+ (config_info & SELFPOWER_MASK));
+ return 1;
+ }
+ }
+
+ dev->current_scenario_idx = _current_scenario_idx;
+
+ memcpy(&dev->current_pcb_config, p_pcb_info,
+ sizeof(struct pcb_config));
+
+ if (pcb_debug) {
+ cx231xx_info("SC(0x00) register = 0x%x\n", config_info);
+ cx231xx_info("scenario %d\n",
+ (dev->current_pcb_config.index) + 1);
+ cx231xx_info("type=%x\n", dev->current_pcb_config.type);
+ cx231xx_info("mode=%x\n", dev->current_pcb_config.mode);
+ cx231xx_info("speed=%x\n", dev->current_pcb_config.speed);
+ cx231xx_info("ts1_source=%x\n",
+ dev->current_pcb_config.ts1_source);
+ cx231xx_info("ts2_source=%x\n",
+ dev->current_pcb_config.ts2_source);
+ cx231xx_info("analog_source=%x\n",
+ dev->current_pcb_config.analog_source);
+ }
+
+ return 0;
+}
diff --git a/drivers/media/video/cx231xx/cx231xx-pcb-cfg.h b/drivers/media/video/cx231xx/cx231xx-pcb-cfg.h
new file mode 100644
index 00000000..f5e46e89
--- /dev/null
+++ b/drivers/media/video/cx231xx/cx231xx-pcb-cfg.h
@@ -0,0 +1,231 @@
+/*
+ cx231xx-pcb-cfg.h - driver for Conexant
+ Cx23100/101/102 USB video capture devices
+
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _PCB_CONFIG_H_
+#define _PCB_CONFIG_H_
+
+#include <linux/init.h>
+#include <linux/module.h>
+
+/***************************************************************************
+ * Class Information *
+***************************************************************************/
+#define CLASS_DEFAULT 0xFF
+
+enum VENDOR_REQUEST_TYPE {
+ /* Set/Get I2C */
+ VRT_SET_I2C0 = 0x0,
+ VRT_SET_I2C1 = 0x1,
+ VRT_SET_I2C2 = 0x2,
+ VRT_GET_I2C0 = 0x4,
+ VRT_GET_I2C1 = 0x5,
+ VRT_GET_I2C2 = 0x6,
+
+ /* Set/Get GPIO */
+ VRT_SET_GPIO = 0x8,
+ VRT_GET_GPIO = 0x9,
+
+ /* Set/Get GPIE */
+ VRT_SET_GPIE = 0xA,
+ VRT_GET_GPIE = 0xB,
+
+ /* Set/Get Register Control/Status */
+ VRT_SET_REGISTER = 0xC,
+ VRT_GET_REGISTER = 0xD,
+
+ /* Get Extended Compat ID Descriptor */
+ VRT_GET_EXTCID_DESC = 0xFF,
+};
+
+enum BYTE_ENABLE_MASK {
+ ENABLE_ONE_BYTE = 0x1,
+ ENABLE_TWE_BYTE = 0x3,
+ ENABLE_THREE_BYTE = 0x7,
+ ENABLE_FOUR_BYTE = 0xF,
+};
+
+#define SPEED_MASK 0x1
+enum USB_SPEED{
+ FULL_SPEED = 0x0, /* 0: full speed */
+ HIGH_SPEED = 0x1 /* 1: high speed */
+};
+
+enum _true_false{
+ FALSE = 0,
+ TRUE = 1
+};
+
+#define TS_MASK 0x6
+enum TS_PORT{
+ NO_TS_PORT = 0x0, /* 2'b00: Neither port used. PCB not a Hybrid,
+ only offers Analog TV or Video */
+ TS1_PORT = 0x4, /* 2'b10: TS1 Input (Hybrid mode :
+ Digital or External Analog/Compressed source) */
+ TS1_TS2_PORT = 0x6, /* 2'b11: TS1 & TS2 Inputs
+ (Dual inputs from Digital and/or
+ External Analog/Compressed sources) */
+ TS1_EXT_CLOCK = 0x6, /* 2'b11: TS1 & TS2 as selector
+ to external clock */
+ TS1VIP_TS2_PORT = 0x2 /* 2'b01: TS1 used as 656/VIP Output,
+ TS2 Input (from Compressor) */
+};
+
+#define EAVP_MASK 0x8
+enum EAV_PRESENT{
+ NO_EXTERNAL_AV = 0x0, /* 0: No External A/V inputs
+ (no need for i2s blcok),
+ Analog Tuner must be present */
+ EXTERNAL_AV = 0x8 /* 1: External A/V inputs
+ present (requires i2s blk) */
+};
+
+#define ATM_MASK 0x30
+enum AT_MODE{
+ DIF_TUNER = 0x30, /* 2'b11: IF Tuner (requires use of DIF) */
+ BASEBAND_SOUND = 0x20, /* 2'b10: Baseband Composite &
+ Sound-IF Signals present */
+ NO_TUNER = 0x10 /* 2'b0x: No Analog Tuner present */
+};
+
+#define PWR_SEL_MASK 0x40
+enum POWE_TYPE{
+ SELF_POWER = 0x0, /* 0: self power */
+ BUS_POWER = 0x40 /* 1: bus power */
+};
+
+enum USB_POWE_TYPE{
+ USB_SELF_POWER = 0,
+ USB_BUS_POWER
+};
+
+#define BO_0_MASK 0x80
+enum AVDEC_STATUS{
+ AVDEC_DISABLE = 0x0, /* 0: A/V Decoder Disabled */
+ AVDEC_ENABLE = 0x80 /* 1: A/V Decoder Enabled */
+};
+
+#define BO_1_MASK 0x100
+
+#define BUSPOWER_MASK 0xC4 /* for Polaris spec 0.8 */
+#define SELFPOWER_MASK 0x86
+
+/***************************************************************************/
+#define NOT_DECIDE_YET 0xFE
+#define NOT_SUPPORTED 0xFF
+
+/***************************************************************************
+ * for mod field use *
+***************************************************************************/
+#define MOD_DIGITAL 0x1
+#define MOD_ANALOG 0x2
+#define MOD_DIF 0x4
+#define MOD_EXTERNAL 0x8
+#define CAP_ALL_MOD 0x0f
+
+/***************************************************************************
+ * source define *
+***************************************************************************/
+#define SOURCE_DIGITAL 0x1
+#define SOURCE_ANALOG 0x2
+#define SOURCE_DIF 0x4
+#define SOURCE_EXTERNAL 0x8
+#define SOURCE_TS_BDA 0x10
+#define SOURCE_TS_ENCODE 0x20
+#define SOURCE_TS_EXTERNAL 0x40
+
+/***************************************************************************
+ * interface information define *
+***************************************************************************/
+struct INTERFACE_INFO {
+ u8 interrupt_index;
+ u8 ts1_index;
+ u8 ts2_index;
+ u8 audio_index;
+ u8 video_index;
+ u8 vanc_index; /* VBI */
+ u8 hanc_index; /* Sliced CC */
+ u8 ir_index;
+};
+
+enum INDEX_INTERFACE_INFO{
+ INDEX_INTERRUPT = 0x0,
+ INDEX_TS1,
+ INDEX_TS2,
+ INDEX_AUDIO,
+ INDEX_VIDEO,
+ INDEX_VANC,
+ INDEX_HANC,
+ INDEX_IR,
+};
+
+/***************************************************************************
+ * configuration information define *
+***************************************************************************/
+struct CONFIG_INFO {
+ u8 config_index;
+ struct INTERFACE_INFO interface_info;
+};
+
+struct pcb_config {
+ u8 index;
+ u8 type; /* bus power or self power,
+ self power--0, bus_power--1 */
+ u8 speed; /* usb speed, 2.0--1, 1.1--0 */
+ u8 mode; /* digital , anlog, dif or external A/V */
+ u32 ts1_source; /* three source -- BDA,External,encode */
+ u32 ts2_source;
+ u32 analog_source;
+ u8 digital_index; /* bus-power used */
+ u8 analog_index; /* bus-power used */
+ u8 dif_index; /* bus-power used */
+ u8 external_index; /* bus-power used */
+ u8 config_num; /* current config num, 0,1,2,
+ for self-power, always 0 */
+ struct CONFIG_INFO hs_config_info[3];
+ struct CONFIG_INFO fs_config_info[3];
+};
+
+enum INDEX_PCB_CONFIG{
+ INDEX_SELFPOWER_DIGITAL_ONLY = 0x0,
+ INDEX_SELFPOWER_DUAL_DIGITAL,
+ INDEX_SELFPOWER_ANALOG_ONLY,
+ INDEX_SELFPOWER_DUAL,
+ INDEX_SELFPOWER_TRIPLE,
+ INDEX_SELFPOWER_COMPRESSOR,
+ INDEX_BUSPOWER_DIGITAL_ONLY,
+ INDEX_BUSPOWER_ANALOG_ONLY,
+ INDEX_BUSPOWER_DIF_ONLY,
+ INDEX_BUSPOWER_EXTERNAL_ONLY,
+ INDEX_BUSPOWER_EXTERNAL_ANALOG,
+ INDEX_BUSPOWER_EXTERNAL_DIF,
+ INDEX_BUSPOWER_EXTERNAL_DIGITAL,
+ INDEX_BUSPOWER_DIGITAL_ANALOG,
+ INDEX_BUSPOWER_DIGITAL_DIF,
+ INDEX_BUSPOWER_DIGITAL_ANALOG_EXTERNAL,
+ INDEX_BUSPOWER_DIGITAL_DIF_EXTERNAL,
+};
+
+/***************************************************************************/
+struct cx231xx;
+
+u32 initialize_cx231xx(struct cx231xx *p_dev);
+
+#endif
diff --git a/drivers/media/video/cx231xx/cx231xx-reg.h b/drivers/media/video/cx231xx/cx231xx-reg.h
new file mode 100644
index 00000000..750c5d37
--- /dev/null
+++ b/drivers/media/video/cx231xx/cx231xx-reg.h
@@ -0,0 +1,1564 @@
+/*
+ cx231xx-reg.h - driver for Conexant Cx23100/101/102
+ USB video capture devices
+
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _CX231XX_REG_H
+#define _CX231XX_REG_H
+
+/*****************************************************************************
+ * VBI codes *
+*****************************************************************************/
+
+#define SAV_ACTIVE_VIDEO_FIELD1 0x80
+#define EAV_ACTIVE_VIDEO_FIELD1 0x90
+
+#define SAV_ACTIVE_VIDEO_FIELD2 0xc0
+#define EAV_ACTIVE_VIDEO_FIELD2 0xd0
+
+#define SAV_VBLANK_FIELD1 0xa0
+#define EAV_VBLANK_FIELD1 0xb0
+
+#define SAV_VBLANK_FIELD2 0xe0
+#define EAV_VBLANK_FIELD2 0xf0
+
+#define SAV_VBI_FIELD1 0x20
+#define EAV_VBI_FIELD1 0x30
+
+#define SAV_VBI_FIELD2 0x60
+#define EAV_VBI_FIELD2 0x70
+
+/*****************************************************************************/
+/* Audio ADC Registers */
+#define CH_PWR_CTRL1 0x0000000e
+#define CH_PWR_CTRL2 0x0000000f
+/*****************************************************************************/
+
+#define HOST_REG1 0x000
+#define FLD_FORCE_CHIP_SEL 0x80
+#define FLD_AUTO_INC_DIS 0x20
+#define FLD_PREFETCH_EN 0x10
+/* Reserved [2:3] */
+#define FLD_DIGITAL_PWR_DN 0x02
+#define FLD_SLEEP 0x01
+
+/*****************************************************************************/
+#define HOST_REG2 0x001
+
+/*****************************************************************************/
+#define HOST_REG3 0x002
+
+/*****************************************************************************/
+/* added for polaris */
+#define GPIO_PIN_CTL0 0x3
+#define GPIO_PIN_CTL1 0x4
+#define GPIO_PIN_CTL2 0x5
+#define GPIO_PIN_CTL3 0x6
+#define TS1_PIN_CTL0 0x7
+#define TS1_PIN_CTL1 0x8
+/*****************************************************************************/
+
+#define FLD_CLK_IN_EN 0x80
+#define FLD_XTAL_CTRL 0x70
+#define FLD_BB_CLK_MODE 0x0C
+#define FLD_REF_DIV_PLL 0x02
+#define FLD_REF_SEL_PLL1 0x01
+
+/*****************************************************************************/
+#define CHIP_CTRL 0x100
+/* Reserved [27] */
+/* Reserved [31:21] */
+#define FLD_CHIP_ACFG_DIS 0x00100000
+/* Reserved [19] */
+#define FLD_DUAL_MODE_ADC2 0x00040000
+#define FLD_SIF_EN 0x00020000
+#define FLD_SOFT_RST 0x00010000
+#define FLD_DEVICE_ID 0x0000ffff
+
+/*****************************************************************************/
+#define AFE_CTRL 0x104
+#define AFE_CTRL_C2HH_SRC_CTRL 0x104
+#define FLD_DIF_OUT_SEL 0xc0000000
+#define FLD_AUX_PLL_CLK_ALT_SEL 0x3c000000
+#define FLD_UV_ORDER_MODE 0x02000000
+#define FLD_FUNC_MODE 0x01800000
+#define FLD_ROT1_PHASE_CTL 0x007f8000
+#define FLD_AUD_IN_SEL 0x00004000
+#define FLD_LUMA_IN_SEL 0x00002000
+#define FLD_CHROMA_IN_SEL 0x00001000
+/* reserve [11:10] */
+#define FLD_INV_SPEC_DIS 0x00000200
+#define FLD_VGA_SEL_CH3 0x00000100
+#define FLD_VGA_SEL_CH2 0x00000080
+#define FLD_VGA_SEL_CH1 0x00000040
+#define FLD_DCR_BYP_CH1 0x00000020
+#define FLD_DCR_BYP_CH2 0x00000010
+#define FLD_DCR_BYP_CH3 0x00000008
+#define FLD_EN_12DB_CH3 0x00000004
+#define FLD_EN_12DB_CH2 0x00000002
+#define FLD_EN_12DB_CH1 0x00000001
+
+/* redefine in Cx231xx */
+/*****************************************************************************/
+#define DC_CTRL1 0x108
+/* reserve [31:30] */
+#define FLD_CLAMP_LVL_CH1 0x3fff8000
+#define FLD_CLAMP_LVL_CH2 0x00007fff
+/*****************************************************************************/
+
+/*****************************************************************************/
+#define DC_CTRL2 0x10c
+/* reserve [31:28] */
+#define FLD_CLAMP_LVL_CH3 0x00fffe00
+#define FLD_CLAMP_WIND_LENTH 0x000001e0
+#define FLD_C2HH_SAT_MIN 0x0000001e
+#define FLD_FLT_BYP_SEL 0x00000001
+/*****************************************************************************/
+
+/*****************************************************************************/
+#define DC_CTRL3 0x110
+/* reserve [31:16] */
+#define FLD_ERR_GAIN_CTL 0x00070000
+#define FLD_LPF_MIN 0x0000ffff
+/*****************************************************************************/
+
+/*****************************************************************************/
+#define DC_CTRL4 0x114
+/* reserve [31:31] */
+#define FLD_INTG_CH1 0x7fffffff
+/*****************************************************************************/
+
+/*****************************************************************************/
+#define DC_CTRL5 0x118
+/* reserve [31:31] */
+#define FLD_INTG_CH2 0x7fffffff
+/*****************************************************************************/
+
+/*****************************************************************************/
+#define DC_CTRL6 0x11c
+/* reserve [31:31] */
+#define FLD_INTG_CH3 0x7fffffff
+/*****************************************************************************/
+
+/*****************************************************************************/
+#define PIN_CTRL 0x120
+#define FLD_OEF_AGC_RF 0x00000001
+#define FLD_OEF_AGC_IFVGA 0x00000002
+#define FLD_OEF_AGC_IF 0x00000004
+#define FLD_REG_BO_PUD 0x80000000
+#define FLD_IR_IRQ_STAT 0x40000000
+#define FLD_AUD_IRQ_STAT 0x20000000
+#define FLD_VID_IRQ_STAT 0x10000000
+/* Reserved [27:26] */
+#define FLD_IRQ_N_OUT_EN 0x02000000
+#define FLD_IRQ_N_POLAR 0x01000000
+/* Reserved [23:6] */
+#define FLD_OE_AUX_PLL_CLK 0x00000020
+#define FLD_OE_I2S_BCLK 0x00000010
+#define FLD_OE_I2S_WCLK 0x00000008
+#define FLD_OE_AGC_IF 0x00000004
+#define FLD_OE_AGC_IFVGA 0x00000002
+#define FLD_OE_AGC_RF 0x00000001
+
+/*****************************************************************************/
+#define AUD_IO_CTRL 0x124
+/* Reserved [31:8] */
+#define FLD_I2S_PORT_DIR 0x00000080
+#define FLD_I2S_OUT_SRC 0x00000040
+#define FLD_AUD_CHAN3_SRC 0x00000030
+#define FLD_AUD_CHAN2_SRC 0x0000000c
+#define FLD_AUD_CHAN1_SRC 0x00000003
+
+/*****************************************************************************/
+#define AUD_LOCK1 0x128
+#define FLD_AUD_LOCK_KI_SHIFT 0xc0000000
+#define FLD_AUD_LOCK_KD_SHIFT 0x30000000
+/* Reserved [27:25] */
+#define FLD_EN_AV_LOCK 0x01000000
+#define FLD_VID_COUNT 0x00ffffff
+
+/*****************************************************************************/
+#define AUD_LOCK2 0x12c
+#define FLD_AUD_LOCK_KI_MULT 0xf0000000
+#define FLD_AUD_LOCK_KD_MULT 0x0F000000
+/* Reserved [23:22] */
+#define FLD_AUD_LOCK_FREQ_SHIFT 0x00300000
+#define FLD_AUD_COUNT 0x000fffff
+
+/*****************************************************************************/
+#define AFE_DIAG_CTRL1 0x134
+/* Reserved [31:16] */
+#define FLD_CUV_DLY_LENGTH 0x0000ff00
+#define FLD_YC_DLY_LENGTH 0x000000ff
+
+/*****************************************************************************/
+/* Poalris redefine */
+#define AFE_DIAG_CTRL3 0x138
+/* Reserved [31:26] */
+#define FLD_AUD_DUAL_FLAG_POL 0x02000000
+#define FLD_VID_DUAL_FLAG_POL 0x01000000
+/* Reserved [23:23] */
+#define FLD_COL_CLAMP_DIS_CH1 0x00400000
+#define FLD_COL_CLAMP_DIS_CH2 0x00200000
+#define FLD_COL_CLAMP_DIS_CH3 0x00100000
+
+#define TEST_CTRL1 0x144
+/* Reserved [31:29] */
+#define FLD_LBIST_EN 0x10000000
+/* Reserved [27:10] */
+#define FLD_FI_BIST_INTR_R 0x0000200
+#define FLD_FI_BIST_INTR_L 0x0000100
+#define FLD_BIST_FAIL_AUD_PLL 0x0000080
+#define FLD_BIST_INTR_AUD_PLL 0x0000040
+#define FLD_BIST_FAIL_VID_PLL 0x0000020
+#define FLD_BIST_INTR_VID_PLL 0x0000010
+/* Reserved [3:1] */
+#define FLD_CIR_TEST_DIS 0x00000001
+
+/*****************************************************************************/
+#define TEST_CTRL2 0x148
+#define FLD_TSXCLK_POL_CTL 0x80000000
+#define FLD_ISO_CTL_SEL 0x40000000
+#define FLD_ISO_CTL_EN 0x20000000
+#define FLD_BIST_DEBUGZ 0x10000000
+#define FLD_AUD_BIST_TEST_H 0x0f000000
+/* Reserved [23:22] */
+#define FLD_FLTRN_BIST_TEST_H 0x00020000
+#define FLD_VID_BIST_TEST_H 0x00010000
+/* Reserved [19:17] */
+#define FLD_BIST_TEST_H 0x00010000
+/* Reserved [15:13] */
+#define FLD_TAB_EN 0x00001000
+/* Reserved [11:0] */
+
+/*****************************************************************************/
+#define BIST_STAT 0x14c
+#define FLD_AUD_BIST_FAIL_H 0xfff00000
+#define FLD_FLTRN_BIST_FAIL_H 0x00180000
+#define FLD_VID_BIST_FAIL_H 0x00070000
+#define FLD_AUD_BIST_TST_DONE 0x0000fff0
+#define FLD_FLTRN_BIST_TST_DONE 0x00000008
+#define FLD_VID_BIST_TST_DONE 0x00000007
+
+/*****************************************************************************/
+/* DirectIF registers definition have been moved to DIF_reg.h */
+/*****************************************************************************/
+#define MODE_CTRL 0x400
+#define FLD_AFD_PAL60_DIS 0x20000000
+#define FLD_AFD_FORCE_SECAM 0x10000000
+#define FLD_AFD_FORCE_PALNC 0x08000000
+#define FLD_AFD_FORCE_PAL 0x04000000
+#define FLD_AFD_PALM_SEL 0x03000000
+#define FLD_CKILL_MODE 0x00300000
+#define FLD_COMB_NOTCH_MODE 0x00c00000 /* bit[19:18] */
+#define FLD_CLR_LOCK_STAT 0x00020000
+#define FLD_FAST_LOCK_MD 0x00010000
+#define FLD_WCEN 0x00008000
+#define FLD_CAGCEN 0x00004000
+#define FLD_CKILLEN 0x00002000
+#define FLD_AUTO_SC_LOCK 0x00001000
+#define FLD_MAN_SC_FAST_LOCK 0x00000800
+#define FLD_INPUT_MODE 0x00000600
+#define FLD_AFD_ACQUIRE 0x00000100
+#define FLD_AFD_NTSC_SEL 0x00000080
+#define FLD_AFD_PAL_SEL 0x00000040
+#define FLD_ACFG_DIS 0x00000020
+#define FLD_SQ_PIXEL 0x00000010
+#define FLD_VID_FMT_SEL 0x0000000f
+
+/*****************************************************************************/
+#define OUT_CTRL1 0x404
+#define FLD_POLAR 0x7f000000
+/* Reserved [23] */
+#define FLD_RND_MODE 0x00600000
+#define FLD_VIPCLAMP_EN 0x00100000
+#define FLD_VIPBLANK_EN 0x00080000
+#define FLD_VIP_OPT_AL 0x00040000
+#define FLD_IDID0_SOURCE 0x00020000
+#define FLD_DCMODE 0x00010000
+#define FLD_CLK_GATING 0x0000c000
+#define FLD_CLK_INVERT 0x00002000
+#define FLD_HSFMT 0x00001000
+#define FLD_VALIDFMT 0x00000800
+#define FLD_ACTFMT 0x00000400
+#define FLD_SWAPRAW 0x00000200
+#define FLD_CLAMPRAW_EN 0x00000100
+#define FLD_BLUE_FIELD_EN 0x00000080
+#define FLD_BLUE_FIELD_ACT 0x00000040
+#define FLD_TASKBIT_VAL 0x00000020
+#define FLD_ANC_DATA_EN 0x00000010
+#define FLD_VBIHACTRAW_EN 0x00000008
+#define FLD_MODE10B 0x00000004
+#define FLD_OUT_MODE 0x00000003
+
+/*****************************************************************************/
+#define OUT_CTRL2 0x408
+#define FLD_AUD_GRP 0xc0000000
+#define FLD_SAMPLE_RATE 0x30000000
+#define FLD_AUD_ANC_EN 0x08000000
+#define FLD_EN_C 0x04000000
+#define FLD_EN_B 0x02000000
+#define FLD_EN_A 0x01000000
+/* Reserved [23:20] */
+#define FLD_IDID1_LSB 0x000c0000
+#define FLD_IDID0_LSB 0x00030000
+#define FLD_IDID1_MSB 0x0000ff00
+#define FLD_IDID0_MSB 0x000000ff
+
+/*****************************************************************************/
+#define GEN_STAT 0x40c
+#define FLD_VCR_DETECT 0x00800000
+#define FLD_SPECIAL_PLAY_N 0x00400000
+#define FLD_VPRES 0x00200000
+#define FLD_AGC_LOCK 0x00100000
+#define FLD_CSC_LOCK 0x00080000
+#define FLD_VLOCK 0x00040000
+#define FLD_SRC_LOCK 0x00020000
+#define FLD_HLOCK 0x00010000
+#define FLD_VSYNC_N 0x00008000
+#define FLD_SRC_FIFO_UFLOW 0x00004000
+#define FLD_SRC_FIFO_OFLOW 0x00002000
+#define FLD_FIELD 0x00001000
+#define FLD_AFD_FMT_STAT 0x00000f00
+#define FLD_MV_TYPE2_PAIR 0x00000080
+#define FLD_MV_T3CS 0x00000040
+#define FLD_MV_CS 0x00000020
+#define FLD_MV_PSP 0x00000010
+/* Reserved [3] */
+#define FLD_MV_CDAT 0x00000003
+
+/*****************************************************************************/
+#define INT_STAT_MASK 0x410
+#define FLD_COMB_3D_FIFO_MSK 0x80000000
+#define FLD_WSS_DAT_AVAIL_MSK 0x40000000
+#define FLD_GS2_DAT_AVAIL_MSK 0x20000000
+#define FLD_GS1_DAT_AVAIL_MSK 0x10000000
+#define FLD_CC_DAT_AVAIL_MSK 0x08000000
+#define FLD_VPRES_CHANGE_MSK 0x04000000
+#define FLD_MV_CHANGE_MSK 0x02000000
+#define FLD_END_VBI_EVEN_MSK 0x01000000
+#define FLD_END_VBI_ODD_MSK 0x00800000
+#define FLD_FMT_CHANGE_MSK 0x00400000
+#define FLD_VSYNC_TRAIL_MSK 0x00200000
+#define FLD_HLOCK_CHANGE_MSK 0x00100000
+#define FLD_VLOCK_CHANGE_MSK 0x00080000
+#define FLD_CSC_LOCK_CHANGE_MSK 0x00040000
+#define FLD_SRC_FIFO_UFLOW_MSK 0x00020000
+#define FLD_SRC_FIFO_OFLOW_MSK 0x00010000
+#define FLD_COMB_3D_FIFO_STAT 0x00008000
+#define FLD_WSS_DAT_AVAIL_STAT 0x00004000
+#define FLD_GS2_DAT_AVAIL_STAT 0x00002000
+#define FLD_GS1_DAT_AVAIL_STAT 0x00001000
+#define FLD_CC_DAT_AVAIL_STAT 0x00000800
+#define FLD_VPRES_CHANGE_STAT 0x00000400
+#define FLD_MV_CHANGE_STAT 0x00000200
+#define FLD_END_VBI_EVEN_STAT 0x00000100
+#define FLD_END_VBI_ODD_STAT 0x00000080
+#define FLD_FMT_CHANGE_STAT 0x00000040
+#define FLD_VSYNC_TRAIL_STAT 0x00000020
+#define FLD_HLOCK_CHANGE_STAT 0x00000010
+#define FLD_VLOCK_CHANGE_STAT 0x00000008
+#define FLD_CSC_LOCK_CHANGE_STAT 0x00000004
+#define FLD_SRC_FIFO_UFLOW_STAT 0x00000002
+#define FLD_SRC_FIFO_OFLOW_STAT 0x00000001
+
+/*****************************************************************************/
+#define LUMA_CTRL 0x414
+#define BRIGHTNESS_CTRL_BYTE 0x414
+#define CONTRAST_CTRL_BYTE 0x415
+#define LUMA_CTRL_BYTE_3 0x416
+#define FLD_LUMA_CORE_SEL 0x00c00000
+#define FLD_RANGE 0x00300000
+/* Reserved [19] */
+#define FLD_PEAK_EN 0x00040000
+#define FLD_PEAK_SEL 0x00030000
+#define FLD_CNTRST 0x0000ff00
+#define FLD_BRITE 0x000000ff
+
+/*****************************************************************************/
+#define HSCALE_CTRL 0x418
+#define FLD_HFILT 0x03000000
+#define FLD_HSCALE 0x00ffffff
+
+/*****************************************************************************/
+#define VSCALE_CTRL 0x41c
+#define FLD_LINE_AVG_DIS 0x01000000
+/* Reserved [23:20] */
+#define FLD_VS_INTRLACE 0x00080000
+#define FLD_VFILT 0x00070000
+/* Reserved [15:13] */
+#define FLD_VSCALE 0x00001fff
+
+/*****************************************************************************/
+#define CHROMA_CTRL 0x420
+#define USAT_CTRL_BYTE 0x420
+#define VSAT_CTRL_BYTE 0x421
+#define HUE_CTRL_BYTE 0x422
+#define FLD_C_LPF_EN 0x20000000
+#define FLD_CHR_DELAY 0x1c000000
+#define FLD_C_CORE_SEL 0x03000000
+#define FLD_HUE 0x00ff0000
+#define FLD_VSAT 0x0000ff00
+#define FLD_USAT 0x000000ff
+
+/*****************************************************************************/
+#define VBI_LINE_CTRL1 0x424
+#define FLD_VBI_MD_LINE4 0xff000000
+#define FLD_VBI_MD_LINE3 0x00ff0000
+#define FLD_VBI_MD_LINE2 0x0000ff00
+#define FLD_VBI_MD_LINE1 0x000000ff
+
+/*****************************************************************************/
+#define VBI_LINE_CTRL2 0x428
+#define FLD_VBI_MD_LINE8 0xff000000
+#define FLD_VBI_MD_LINE7 0x00ff0000
+#define FLD_VBI_MD_LINE6 0x0000ff00
+#define FLD_VBI_MD_LINE5 0x000000ff
+
+/*****************************************************************************/
+#define VBI_LINE_CTRL3 0x42c
+#define FLD_VBI_MD_LINE12 0xff000000
+#define FLD_VBI_MD_LINE11 0x00ff0000
+#define FLD_VBI_MD_LINE10 0x0000ff00
+#define FLD_VBI_MD_LINE9 0x000000ff
+
+/*****************************************************************************/
+#define VBI_LINE_CTRL4 0x430
+#define FLD_VBI_MD_LINE16 0xff000000
+#define FLD_VBI_MD_LINE15 0x00ff0000
+#define FLD_VBI_MD_LINE14 0x0000ff00
+#define FLD_VBI_MD_LINE13 0x000000ff
+
+/*****************************************************************************/
+#define VBI_LINE_CTRL5 0x434
+#define FLD_VBI_MD_LINE17 0x000000ff
+
+/*****************************************************************************/
+#define VBI_FC_CFG 0x438
+#define FLD_FC_ALT2 0xff000000
+#define FLD_FC_ALT1 0x00ff0000
+#define FLD_FC_ALT2_TYPE 0x0000f000
+#define FLD_FC_ALT1_TYPE 0x00000f00
+/* Reserved [7:1] */
+#define FLD_FC_SEARCH_MODE 0x00000001
+
+/*****************************************************************************/
+#define VBI_MISC_CFG1 0x43c
+#define FLD_TTX_PKTADRU 0xfff00000
+#define FLD_TTX_PKTADRL 0x000fff00
+/* Reserved [7:6] */
+#define FLD_MOJI_PACK_DIS 0x00000020
+#define FLD_VPS_DEC_DIS 0x00000010
+#define FLD_CRI_MARG_SCALE 0x0000000c
+#define FLD_EDGE_RESYNC_EN 0x00000002
+#define FLD_ADAPT_SLICE_DIS 0x00000001
+
+/*****************************************************************************/
+#define VBI_MISC_CFG2 0x440
+#define FLD_HAMMING_TYPE 0x0f000000
+/* Reserved [23:20] */
+#define FLD_WSS_FIFO_RST 0x00080000
+#define FLD_GS2_FIFO_RST 0x00040000
+#define FLD_GS1_FIFO_RST 0x00020000
+#define FLD_CC_FIFO_RST 0x00010000
+/* Reserved [15:12] */
+#define FLD_VBI3_SDID 0x00000f00
+#define FLD_VBI2_SDID 0x000000f0
+#define FLD_VBI1_SDID 0x0000000f
+
+/*****************************************************************************/
+#define VBI_PAY1 0x444
+#define FLD_GS1_FIFO_DAT 0xFF000000
+#define FLD_GS1_STAT 0x00FF0000
+#define FLD_CC_FIFO_DAT 0x0000FF00
+#define FLD_CC_STAT 0x000000FF
+
+/*****************************************************************************/
+#define VBI_PAY2 0x448
+#define FLD_WSS_FIFO_DAT 0xff000000
+#define FLD_WSS_STAT 0x00ff0000
+#define FLD_GS2_FIFO_DAT 0x0000ff00
+#define FLD_GS2_STAT 0x000000ff
+
+/*****************************************************************************/
+#define VBI_CUST1_CFG1 0x44c
+/* Reserved [31] */
+#define FLD_VBI1_CRIWIN 0x7f000000
+#define FLD_VBI1_SLICE_DIST 0x00f00000
+#define FLD_VBI1_BITINC 0x000fff00
+#define FLD_VBI1_HDELAY 0x000000ff
+
+/*****************************************************************************/
+#define VBI_CUST1_CFG2 0x450
+#define FLD_VBI1_FC_LENGTH 0x1f000000
+#define FLD_VBI1_FRAME_CODE 0x00ffffff
+
+/*****************************************************************************/
+#define VBI_CUST1_CFG3 0x454
+#define FLD_VBI1_HAM_EN 0x80000000
+#define FLD_VBI1_FIFO_MODE 0x70000000
+#define FLD_VBI1_FORMAT_TYPE 0x0f000000
+#define FLD_VBI1_PAYLD_LENGTH 0x00ff0000
+#define FLD_VBI1_CRI_LENGTH 0x0000f000
+#define FLD_VBI1_CRI_MARGIN 0x00000f00
+#define FLD_VBI1_CRI_TIME 0x000000ff
+
+/*****************************************************************************/
+#define VBI_CUST2_CFG1 0x458
+/* Reserved [31] */
+#define FLD_VBI2_CRIWIN 0x7f000000
+#define FLD_VBI2_SLICE_DIST 0x00f00000
+#define FLD_VBI2_BITINC 0x000fff00
+#define FLD_VBI2_HDELAY 0x000000ff
+
+/*****************************************************************************/
+#define VBI_CUST2_CFG2 0x45c
+#define FLD_VBI2_FC_LENGTH 0x1f000000
+#define FLD_VBI2_FRAME_CODE 0x00ffffff
+
+/*****************************************************************************/
+#define VBI_CUST2_CFG3 0x460
+#define FLD_VBI2_HAM_EN 0x80000000
+#define FLD_VBI2_FIFO_MODE 0x70000000
+#define FLD_VBI2_FORMAT_TYPE 0x0f000000
+#define FLD_VBI2_PAYLD_LENGTH 0x00ff0000
+#define FLD_VBI2_CRI_LENGTH 0x0000f000
+#define FLD_VBI2_CRI_MARGIN 0x00000f00
+#define FLD_VBI2_CRI_TIME 0x000000ff
+
+/*****************************************************************************/
+#define VBI_CUST3_CFG1 0x464
+/* Reserved [31] */
+#define FLD_VBI3_CRIWIN 0x7f000000
+#define FLD_VBI3_SLICE_DIST 0x00f00000
+#define FLD_VBI3_BITINC 0x000fff00
+#define FLD_VBI3_HDELAY 0x000000ff
+
+/*****************************************************************************/
+#define VBI_CUST3_CFG2 0x468
+#define FLD_VBI3_FC_LENGTH 0x1f000000
+#define FLD_VBI3_FRAME_CODE 0x00ffffff
+
+/*****************************************************************************/
+#define VBI_CUST3_CFG3 0x46c
+#define FLD_VBI3_HAM_EN 0x80000000
+#define FLD_VBI3_FIFO_MODE 0x70000000
+#define FLD_VBI3_FORMAT_TYPE 0x0f000000
+#define FLD_VBI3_PAYLD_LENGTH 0x00ff0000
+#define FLD_VBI3_CRI_LENGTH 0x0000f000
+#define FLD_VBI3_CRI_MARGIN 0x00000f00
+#define FLD_VBI3_CRI_TIME 0x000000ff
+
+/*****************************************************************************/
+#define HORIZ_TIM_CTRL 0x470
+#define FLD_BGDEL_CNT 0xff000000
+/* Reserved [23:22] */
+#define FLD_HACTIVE_CNT 0x003ff000
+/* Reserved [11:10] */
+#define FLD_HBLANK_CNT 0x000003ff
+
+/*****************************************************************************/
+#define VERT_TIM_CTRL 0x474
+#define FLD_V656BLANK_CNT 0xff000000
+/* Reserved [23:22] */
+#define FLD_VACTIVE_CNT 0x003ff000
+/* Reserved [11:10] */
+#define FLD_VBLANK_CNT 0x000003ff
+
+/*****************************************************************************/
+#define SRC_COMB_CFG 0x478
+#define FLD_CCOMB_2LN_CHECK 0x80000000
+#define FLD_CCOMB_3LN_EN 0x40000000
+#define FLD_CCOMB_2LN_EN 0x20000000
+#define FLD_CCOMB_3D_EN 0x10000000
+/* Reserved [27] */
+#define FLD_LCOMB_3LN_EN 0x04000000
+#define FLD_LCOMB_2LN_EN 0x02000000
+#define FLD_LCOMB_3D_EN 0x01000000
+#define FLD_LUMA_LPF_SEL 0x00c00000
+#define FLD_UV_LPF_SEL 0x00300000
+#define FLD_BLEND_SLOPE 0x000f0000
+#define FLD_CCOMB_REDUCE_EN 0x00008000
+/* Reserved [14:10] */
+#define FLD_SRC_DECIM_RATIO 0x000003ff
+
+/*****************************************************************************/
+#define CHROMA_VBIOFF_CFG 0x47c
+#define FLD_VBI_VOFFSET 0x1f000000
+/* Reserved [23:20] */
+#define FLD_SC_STEP 0x000fffff
+
+/*****************************************************************************/
+#define FIELD_COUNT 0x480
+#define FLD_FIELD_COUNT_FLD 0x000003ff
+
+/*****************************************************************************/
+#define MISC_TIM_CTRL 0x484
+#define FLD_DEBOUNCE_COUNT 0xc0000000
+#define FLD_VT_LINE_CNT_HYST 0x30000000
+/* Reserved [27] */
+#define FLD_AFD_STAT 0x07ff0000
+#define FLD_VPRES_VERT_EN 0x00008000
+/* Reserved [14:12] */
+#define FLD_HR32 0x00000800
+#define FLD_TDALGN 0x00000400
+#define FLD_TDFIELD 0x00000200
+/* Reserved [8:6] */
+#define FLD_TEMPDEC 0x0000003f
+
+/*****************************************************************************/
+#define DFE_CTRL1 0x488
+#define FLD_CLAMP_AUTO_EN 0x80000000
+#define FLD_AGC_AUTO_EN 0x40000000
+#define FLD_VGA_CRUSH_EN 0x20000000
+#define FLD_VGA_AUTO_EN 0x10000000
+#define FLD_VBI_GATE_EN 0x08000000
+#define FLD_CLAMP_LEVEL 0x07000000
+/* Reserved [23:22] */
+#define FLD_CLAMP_SKIP_CNT 0x00300000
+#define FLD_AGC_GAIN 0x000fff00
+/* Reserved [7:6] */
+#define FLD_VGA_GAIN 0x0000003f
+
+/*****************************************************************************/
+#define DFE_CTRL2 0x48c
+#define FLD_VGA_ACQUIRE_RANGE 0x00ff0000
+#define FLD_VGA_TRACK_RANGE 0x0000ff00
+#define FLD_VGA_SYNC 0x000000ff
+
+/*****************************************************************************/
+#define DFE_CTRL3 0x490
+#define FLD_BP_PERCENT 0xff000000
+#define FLD_DFT_THRESHOLD 0x00ff0000
+/* Reserved [15:12] */
+#define FLD_SYNC_WIDTH_SEL 0x00000600
+#define FLD_BP_LOOP_GAIN 0x00000300
+#define FLD_SYNC_LOOP_GAIN 0x000000c0
+/* Reserved [5:4] */
+#define FLD_AGC_LOOP_GAIN 0x0000000c
+#define FLD_DCC_LOOP_GAIN 0x00000003
+
+/*****************************************************************************/
+#define PLL_CTRL 0x494
+#define FLD_PLL_KD 0xff000000
+#define FLD_PLL_KI 0x00ff0000
+#define FLD_PLL_MAX_OFFSET 0x0000ffff
+
+/*****************************************************************************/
+#define HTL_CTRL 0x498
+/* Reserved [31:24] */
+#define FLD_AUTO_LOCK_SPD 0x00080000
+#define FLD_MAN_FAST_LOCK 0x00040000
+#define FLD_HTL_15K_EN 0x00020000
+#define FLD_HTL_500K_EN 0x00010000
+#define FLD_HTL_KD 0x0000ff00
+#define FLD_HTL_KI 0x000000ff
+
+/*****************************************************************************/
+#define COMB_CTRL 0x49c
+#define FLD_COMB_PHASE_LIMIT 0xff000000
+#define FLD_CCOMB_ERR_LIMIT 0x00ff0000
+#define FLD_LUMA_THRESHOLD 0x0000ff00
+#define FLD_LCOMB_ERR_LIMIT 0x000000ff
+
+/*****************************************************************************/
+#define CRUSH_CTRL 0x4a0
+#define FLD_WTW_EN 0x00400000
+#define FLD_CRUSH_FREQ 0x00200000
+#define FLD_MAJ_SEL_EN 0x00100000
+#define FLD_MAJ_SEL 0x000c0000
+/* Reserved [17:15] */
+#define FLD_SYNC_TIP_REDUCE 0x00007e00
+/* Reserved [8:6] */
+#define FLD_SYNC_TIP_INC 0x0000003f
+
+/*****************************************************************************/
+#define SOFT_RST_CTRL 0x4a4
+#define FLD_VD_SOFT_RST 0x00008000
+/* Reserved [14:12] */
+#define FLD_REG_RST_MSK 0x00000800
+#define FLD_VOF_RST_MSK 0x00000400
+#define FLD_MVDET_RST_MSK 0x00000200
+#define FLD_VBI_RST_MSK 0x00000100
+#define FLD_SCALE_RST_MSK 0x00000080
+#define FLD_CHROMA_RST_MSK 0x00000040
+#define FLD_LUMA_RST_MSK 0x00000020
+#define FLD_VTG_RST_MSK 0x00000010
+#define FLD_YCSEP_RST_MSK 0x00000008
+#define FLD_SRC_RST_MSK 0x00000004
+#define FLD_DFE_RST_MSK 0x00000002
+/* Reserved [0] */
+
+/*****************************************************************************/
+#define MV_DT_CTRL1 0x4a8
+/* Reserved [31:29] */
+#define FLD_PSP_STOP_LINE 0x1f000000
+/* Reserved [23:21] */
+#define FLD_PSP_STRT_LINE 0x001f0000
+/* Reserved [15] */
+#define FLD_PSP_LLIMW 0x00007f00
+/* Reserved [7] */
+#define FLD_PSP_ULIMW 0x0000007f
+
+/*****************************************************************************/
+#define MV_DT_CTRL2 0x4aC
+#define FLD_CS_STOPWIN 0xff000000
+#define FLD_CS_STRTWIN 0x00ff0000
+#define FLD_CS_WIDTH 0x0000ff00
+#define FLD_PSP_SPEC_VAL 0x000000ff
+
+/*****************************************************************************/
+#define MV_DT_CTRL3 0x4B0
+#define FLD_AUTO_RATE_DIS 0x80000000
+#define FLD_HLOCK_DIS 0x40000000
+#define FLD_SEL_FIELD_CNT 0x20000000
+#define FLD_CS_TYPE2_SEL 0x10000000
+#define FLD_CS_LINE_THRSH_SEL 0x08000000
+#define FLD_CS_ATHRESH_SEL 0x04000000
+#define FLD_PSP_SPEC_SEL 0x02000000
+#define FLD_PSP_LINES_SEL 0x01000000
+#define FLD_FIELD_CNT 0x00f00000
+#define FLD_CS_TYPE2_CNT 0x000fc000
+#define FLD_CS_LINE_CNT 0x00003f00
+#define FLD_CS_ATHRESH_LEV 0x000000ff
+
+/*****************************************************************************/
+#define CHIP_VERSION 0x4b4
+/* Cx231xx redefine */
+#define VERSION 0x4b4
+#define FLD_REV_ID 0x000000ff
+
+/*****************************************************************************/
+#define MISC_DIAG_CTRL 0x4b8
+/* Reserved [31:24] */
+#define FLD_SC_CONVERGE_THRESH 0x00ff0000
+#define FLD_CCOMB_ERR_LIMIT_3D 0x0000ff00
+#define FLD_LCOMB_ERR_LIMIT_3D 0x000000ff
+
+/*****************************************************************************/
+#define VBI_PASS_CTRL 0x4bc
+#define FLD_VBI_PASS_MD 0x00200000
+#define FLD_VBI_SETUP_DIS 0x00100000
+#define FLD_PASS_LINE_CTRL 0x000fffff
+
+/*****************************************************************************/
+/* Cx231xx redefine */
+#define VCR_DET_CTRL 0x4c0
+#define FLD_EN_FIELD_PHASE_DET 0x80000000
+#define FLD_EN_HEAD_SW_DET 0x40000000
+#define FLD_FIELD_PHASE_LENGTH 0x01ff0000
+/* Reserved [29:25] */
+#define FLD_FIELD_PHASE_DELAY 0x0000ff00
+#define FLD_FIELD_PHASE_LIMIT 0x000000f0
+#define FLD_HEAD_SW_DET_LIMIT 0x0000000f
+
+/*****************************************************************************/
+#define DL_CTL 0x800
+#define DL_CTL_ADDRESS_LOW 0x800 /* Byte 1 in DL_CTL */
+#define DL_CTL_ADDRESS_HIGH 0x801 /* Byte 2 in DL_CTL */
+#define DL_CTL_DATA 0x802 /* Byte 3 in DL_CTL */
+#define DL_CTL_CONTROL 0x803 /* Byte 4 in DL_CTL */
+/* Reserved [31:5] */
+#define FLD_START_8051 0x10000000
+#define FLD_DL_ENABLE 0x08000000
+#define FLD_DL_AUTO_INC 0x04000000
+#define FLD_DL_MAP 0x03000000
+
+/*****************************************************************************/
+#define STD_DET_STATUS 0x804
+#define FLD_SPARE_STATUS1 0xff000000
+#define FLD_SPARE_STATUS0 0x00ff0000
+#define FLD_MOD_DET_STATUS1 0x0000ff00
+#define FLD_MOD_DET_STATUS0 0x000000ff
+
+/*****************************************************************************/
+#define AUD_BUILD_NUM 0x806
+#define AUD_VER_NUM 0x807
+#define STD_DET_CTL 0x808
+#define STD_DET_CTL_AUD_CTL 0x808 /* Byte 1 in STD_DET_CTL */
+#define STD_DET_CTL_PREF_MODE 0x809 /* Byte 2 in STD_DET_CTL */
+#define FLD_SPARE_CTL0 0xff000000
+#define FLD_DIS_DBX 0x00800000
+#define FLD_DIS_BTSC 0x00400000
+#define FLD_DIS_NICAM_A2 0x00200000
+#define FLD_VIDEO_PRESENT 0x00100000
+#define FLD_DW8051_VIDEO_FORMAT 0x000f0000
+#define FLD_PREF_DEC_MODE 0x0000ff00
+#define FLD_AUD_CONFIG 0x000000ff
+
+/*****************************************************************************/
+#define DW8051_INT 0x80c
+#define FLD_VIDEO_PRESENT_CHANGE 0x80000000
+#define FLD_VIDEO_CHANGE 0x40000000
+#define FLD_RDS_READY 0x20000000
+#define FLD_AC97_INT 0x10000000
+#define FLD_NICAM_BIT_ERROR_TOO_HIGH 0x08000000
+#define FLD_NICAM_LOCK 0x04000000
+#define FLD_NICAM_UNLOCK 0x02000000
+#define FLD_DFT4_TH_CMP 0x01000000
+/* Reserved [23:22] */
+#define FLD_LOCK_IND_INT 0x00200000
+#define FLD_DFT3_TH_CMP 0x00100000
+#define FLD_DFT2_TH_CMP 0x00080000
+#define FLD_DFT1_TH_CMP 0x00040000
+#define FLD_FM2_DFT_TH_CMP 0x00020000
+#define FLD_FM1_DFT_TH_CMP 0x00010000
+#define FLD_VIDEO_PRESENT_EN 0x00008000
+#define FLD_VIDEO_CHANGE_EN 0x00004000
+#define FLD_RDS_READY_EN 0x00002000
+#define FLD_AC97_INT_EN 0x00001000
+#define FLD_NICAM_BIT_ERROR_TOO_HIGH_EN 0x00000800
+#define FLD_NICAM_LOCK_EN 0x00000400
+#define FLD_NICAM_UNLOCK_EN 0x00000200
+#define FLD_DFT4_TH_CMP_EN 0x00000100
+/* Reserved [7] */
+#define FLD_DW8051_INT6_CTL1 0x00000040
+#define FLD_DW8051_INT5_CTL1 0x00000020
+#define FLD_DW8051_INT4_CTL1 0x00000010
+#define FLD_DW8051_INT3_CTL1 0x00000008
+#define FLD_DW8051_INT2_CTL1 0x00000004
+#define FLD_DW8051_INT1_CTL1 0x00000002
+#define FLD_DW8051_INT0_CTL1 0x00000001
+
+/*****************************************************************************/
+#define GENERAL_CTL 0x810
+#define FLD_RDS_INT 0x80000000
+#define FLD_NBER_INT 0x40000000
+#define FLD_NLL_INT 0x20000000
+#define FLD_IFL_INT 0x10000000
+#define FLD_FDL_INT 0x08000000
+#define FLD_AFC_INT 0x04000000
+#define FLD_AMC_INT 0x02000000
+#define FLD_AC97_INT_CTL 0x01000000
+#define FLD_RDS_INT_DIS 0x00800000
+#define FLD_NBER_INT_DIS 0x00400000
+#define FLD_NLL_INT_DIS 0x00200000
+#define FLD_IFL_INT_DIS 0x00100000
+#define FLD_FDL_INT_DIS 0x00080000
+#define FLD_FC_INT_DIS 0x00040000
+#define FLD_AMC_INT_DIS 0x00020000
+#define FLD_AC97_INT_DIS 0x00010000
+#define FLD_REV_NUM 0x0000ff00
+/* Reserved [7:5] */
+#define FLD_DBX_SOFT_RESET_REG 0x00000010
+#define FLD_AD_SOFT_RESET_REG 0x00000008
+#define FLD_SRC_SOFT_RESET_REG 0x00000004
+#define FLD_CDMOD_SOFT_RESET 0x00000002
+#define FLD_8051_SOFT_RESET 0x00000001
+
+/*****************************************************************************/
+#define AAGC_CTL 0x814
+#define FLD_AFE_12DB_EN 0x80000000
+#define FLD_AAGC_DEFAULT_EN 0x40000000
+#define FLD_AAGC_DEFAULT 0x3f000000
+/* Reserved [23] */
+#define FLD_AAGC_GAIN 0x00600000
+#define FLD_AAGC_TH 0x001f0000
+/* Reserved [15:14] */
+#define FLD_AAGC_HYST2 0x00003f00
+/* Reserved [7:6] */
+#define FLD_AAGC_HYST1 0x0000003f
+
+/*****************************************************************************/
+#define IF_SRC_CTL 0x818
+#define FLD_DBX_BYPASS 0x80000000
+/* Reserved [30:25] */
+#define FLD_IF_SRC_MODE 0x01000000
+/* Reserved [23:18] */
+#define FLD_IF_SRC_PHASE_INC 0x0001ffff
+
+/*****************************************************************************/
+#define ANALOG_DEMOD_CTL 0x81c
+#define FLD_ROT1_PHACC_PROG 0xffff0000
+/* Reserved [15] */
+#define FLD_FM1_DELAY_FIX 0x00007000
+#define FLD_PDF4_SHIFT 0x00000c00
+#define FLD_PDF3_SHIFT 0x00000300
+#define FLD_PDF2_SHIFT 0x000000c0
+#define FLD_PDF1_SHIFT 0x00000030
+#define FLD_FMBYPASS_MODE2 0x00000008
+#define FLD_FMBYPASS_MODE1 0x00000004
+#define FLD_NICAM_MODE 0x00000002
+#define FLD_BTSC_FMRADIO_MODE 0x00000001
+
+/*****************************************************************************/
+#define ROT_FREQ_CTL 0x820
+#define FLD_ROT3_PHACC_PROG 0xffff0000
+#define FLD_ROT2_PHACC_PROG 0x0000ffff
+
+/*****************************************************************************/
+#define FM_CTL 0x824
+#define FLD_FM2_DC_FB_SHIFT 0xf0000000
+#define FLD_FM2_DC_INT_SHIFT 0x0f000000
+#define FLD_FM2_AFC_RESET 0x00800000
+#define FLD_FM2_DC_PASS_IN 0x00400000
+#define FLD_FM2_DAGC_SHIFT 0x00380000
+#define FLD_FM2_CORDIC_SHIFT 0x00070000
+#define FLD_FM1_DC_FB_SHIFT 0x0000f000
+#define FLD_FM1_DC_INT_SHIFT 0x00000f00
+#define FLD_FM1_AFC_RESET 0x00000080
+#define FLD_FM1_DC_PASS_IN 0x00000040
+#define FLD_FM1_DAGC_SHIFT 0x00000038
+#define FLD_FM1_CORDIC_SHIFT 0x00000007
+
+/*****************************************************************************/
+#define LPF_PDF_CTL 0x828
+/* Reserved [31:30] */
+#define FLD_LPF32_SHIFT1 0x30000000
+#define FLD_LPF32_SHIFT2 0x0c000000
+#define FLD_LPF160_SHIFTA 0x03000000
+#define FLD_LPF160_SHIFTB 0x00c00000
+#define FLD_LPF160_SHIFTC 0x00300000
+#define FLD_LPF32_COEF_SEL2 0x000c0000
+#define FLD_LPF32_COEF_SEL1 0x00030000
+#define FLD_LPF160_COEF_SELC 0x0000c000
+#define FLD_LPF160_COEF_SELB 0x00003000
+#define FLD_LPF160_COEF_SELA 0x00000c00
+#define FLD_LPF160_IN_EN_REG 0x00000300
+#define FLD_PDF4_PDF_SEL 0x000000c0
+#define FLD_PDF3_PDF_SEL 0x00000030
+#define FLD_PDF2_PDF_SEL 0x0000000c
+#define FLD_PDF1_PDF_SEL 0x00000003
+
+/*****************************************************************************/
+#define DFT1_CTL1 0x82c
+#define FLD_DFT1_DWELL 0xffff0000
+#define FLD_DFT1_FREQ 0x0000ffff
+
+/*****************************************************************************/
+#define DFT1_CTL2 0x830
+#define FLD_DFT1_THRESHOLD 0xffffff00
+#define FLD_DFT1_CMP_CTL 0x00000080
+#define FLD_DFT1_AVG 0x00000070
+/* Reserved [3:1] */
+#define FLD_DFT1_START 0x00000001
+
+/*****************************************************************************/
+#define DFT1_STATUS 0x834
+#define FLD_DFT1_DONE 0x80000000
+#define FLD_DFT1_TH_CMP_STAT 0x40000000
+#define FLD_DFT1_RESULT 0x3fffffff
+
+/*****************************************************************************/
+#define DFT2_CTL1 0x838
+#define FLD_DFT2_DWELL 0xffff0000
+#define FLD_DFT2_FREQ 0x0000ffff
+
+/*****************************************************************************/
+#define DFT2_CTL2 0x83C
+#define FLD_DFT2_THRESHOLD 0xffffff00
+#define FLD_DFT2_CMP_CTL 0x00000080
+#define FLD_DFT2_AVG 0x00000070
+/* Reserved [3:1] */
+#define FLD_DFT2_START 0x00000001
+
+/*****************************************************************************/
+#define DFT2_STATUS 0x840
+#define FLD_DFT2_DONE 0x80000000
+#define FLD_DFT2_TH_CMP_STAT 0x40000000
+#define FLD_DFT2_RESULT 0x3fffffff
+
+/*****************************************************************************/
+#define DFT3_CTL1 0x844
+#define FLD_DFT3_DWELL 0xffff0000
+#define FLD_DFT3_FREQ 0x0000ffff
+
+/*****************************************************************************/
+#define DFT3_CTL2 0x848
+#define FLD_DFT3_THRESHOLD 0xffffff00
+#define FLD_DFT3_CMP_CTL 0x00000080
+#define FLD_DFT3_AVG 0x00000070
+/* Reserved [3:1] */
+#define FLD_DFT3_START 0x00000001
+
+/*****************************************************************************/
+#define DFT3_STATUS 0x84c
+#define FLD_DFT3_DONE 0x80000000
+#define FLD_DFT3_TH_CMP_STAT 0x40000000
+#define FLD_DFT3_RESULT 0x3fffffff
+
+/*****************************************************************************/
+#define DFT4_CTL1 0x850
+#define FLD_DFT4_DWELL 0xffff0000
+#define FLD_DFT4_FREQ 0x0000ffff
+
+/*****************************************************************************/
+#define DFT4_CTL2 0x854
+#define FLD_DFT4_THRESHOLD 0xffffff00
+#define FLD_DFT4_CMP_CTL 0x00000080
+#define FLD_DFT4_AVG 0x00000070
+/* Reserved [3:1] */
+#define FLD_DFT4_START 0x00000001
+
+/*****************************************************************************/
+#define DFT4_STATUS 0x858
+#define FLD_DFT4_DONE 0x80000000
+#define FLD_DFT4_TH_CMP_STAT 0x40000000
+#define FLD_DFT4_RESULT 0x3fffffff
+
+/*****************************************************************************/
+#define AM_MTS_DET 0x85c
+#define FLD_AM_MTS_MODE 0x80000000
+/* Reserved [30:26] */
+#define FLD_AM_SUB 0x02000000
+#define FLD_AM_GAIN_EN 0x01000000
+/* Reserved [23:16] */
+#define FLD_AMMTS_GAIN_SCALE 0x0000e000
+#define FLD_MTS_PDF_SHIFT 0x00001800
+#define FLD_AM_REG_GAIN 0x00000700
+#define FLD_AGC_REF 0x000000ff
+
+/*****************************************************************************/
+#define ANALOG_MUX_CTL 0x860
+/* Reserved [31:29] */
+#define FLD_MUX21_SEL 0x10000000
+#define FLD_MUX20_SEL 0x08000000
+#define FLD_MUX19_SEL 0x04000000
+#define FLD_MUX18_SEL 0x02000000
+#define FLD_MUX17_SEL 0x01000000
+#define FLD_MUX16_SEL 0x00800000
+#define FLD_MUX15_SEL 0x00400000
+#define FLD_MUX14_SEL 0x00300000
+#define FLD_MUX13_SEL 0x000C0000
+#define FLD_MUX12_SEL 0x00020000
+#define FLD_MUX11_SEL 0x00018000
+#define FLD_MUX10_SEL 0x00004000
+#define FLD_MUX9_SEL 0x00002000
+#define FLD_MUX8_SEL 0x00001000
+#define FLD_MUX7_SEL 0x00000800
+#define FLD_MUX6_SEL 0x00000600
+#define FLD_MUX5_SEL 0x00000100
+#define FLD_MUX4_SEL 0x000000c0
+#define FLD_MUX3_SEL 0x00000030
+#define FLD_MUX2_SEL 0x0000000c
+#define FLD_MUX1_SEL 0x00000003
+
+/*****************************************************************************/
+/* Cx231xx redefine */
+#define DPLL_CTRL1 0x864
+#define DIG_PLL_CTL1 0x864
+
+#define FLD_PLL_STATUS 0x07000000
+#define FLD_BANDWIDTH_SELECT 0x00030000
+#define FLD_PLL_SHIFT_REG 0x00007000
+#define FLD_PHASE_SHIFT 0x000007ff
+
+/*****************************************************************************/
+/* Cx231xx redefine */
+#define DPLL_CTRL2 0x868
+#define DIG_PLL_CTL2 0x868
+#define FLD_PLL_UNLOCK_THR 0xff000000
+#define FLD_PLL_LOCK_THR 0x00ff0000
+/* Reserved [15:8] */
+#define FLD_AM_PDF_SEL2 0x000000c0
+#define FLD_AM_PDF_SEL1 0x00000030
+#define FLD_DPLL_FSM_CTRL 0x0000000c
+/* Reserved [1] */
+#define FLD_PLL_PILOT_DET 0x00000001
+
+/*****************************************************************************/
+/* Cx231xx redefine */
+#define DPLL_CTRL3 0x86c
+#define DIG_PLL_CTL3 0x86c
+#define FLD_DISABLE_LOOP 0x01000000
+#define FLD_A1_DS1_SEL 0x000c0000
+#define FLD_A1_DS2_SEL 0x00030000
+#define FLD_A1_KI 0x0000ff00
+#define FLD_A1_KD 0x000000ff
+
+/*****************************************************************************/
+/* Cx231xx redefine */
+#define DPLL_CTRL4 0x870
+#define DIG_PLL_CTL4 0x870
+#define FLD_A2_DS1_SEL 0x000c0000
+#define FLD_A2_DS2_SEL 0x00030000
+#define FLD_A2_KI 0x0000ff00
+#define FLD_A2_KD 0x000000ff
+
+/*****************************************************************************/
+/* Cx231xx redefine */
+#define DPLL_CTRL5 0x874
+#define DIG_PLL_CTL5 0x874
+#define FLD_TRK_DS1_SEL 0x000c0000
+#define FLD_TRK_DS2_SEL 0x00030000
+#define FLD_TRK_KI 0x0000ff00
+#define FLD_TRK_KD 0x000000ff
+
+/*****************************************************************************/
+#define DEEMPH_GAIN_CTL 0x878
+#define FLD_DEEMPH2_GAIN 0xFFFF0000
+#define FLD_DEEMPH1_GAIN 0x0000FFFF
+
+/*****************************************************************************/
+/* Cx231xx redefine */
+#define DEEMPH_COEFF1 0x87c
+#define DEEMPH_COEF1 0x87c
+#define FLD_DEEMPH_B0 0xffff0000
+#define FLD_DEEMPH_A0 0x0000ffff
+
+/*****************************************************************************/
+/* Cx231xx redefine */
+#define DEEMPH_COEFF2 0x880
+#define DEEMPH_COEF2 0x880
+#define FLD_DEEMPH_B1 0xFFFF0000
+#define FLD_DEEMPH_A1 0x0000FFFF
+
+/*****************************************************************************/
+#define DBX1_CTL1 0x884
+#define FLD_DBX1_WBE_GAIN 0xffff0000
+#define FLD_DBX1_IN_GAIN 0x0000ffff
+
+/*****************************************************************************/
+#define DBX1_CTL2 0x888
+#define FLD_DBX1_SE_BYPASS 0xffff0000
+#define FLD_DBX1_SE_GAIN 0x0000ffff
+
+/*****************************************************************************/
+#define DBX1_RMS_SE 0x88C
+#define FLD_DBX1_RMS_WBE 0xffff0000
+#define FLD_DBX1_RMS_SE_FLD 0x0000ffff
+
+/*****************************************************************************/
+#define DBX2_CTL1 0x890
+#define FLD_DBX2_WBE_GAIN 0xffff0000
+#define FLD_DBX2_IN_GAIN 0x0000ffff
+
+/*****************************************************************************/
+#define DBX2_CTL2 0x894
+#define FLD_DBX2_SE_BYPASS 0xffff0000
+#define FLD_DBX2_SE_GAIN 0x0000ffff
+
+/*****************************************************************************/
+#define DBX2_RMS_SE 0x898
+#define FLD_DBX2_RMS_WBE 0xffff0000
+#define FLD_DBX2_RMS_SE_FLD 0x0000ffff
+
+/*****************************************************************************/
+#define AM_FM_DIFF 0x89c
+/* Reserved [31] */
+#define FLD_FM_DIFF_OUT 0x7fff0000
+/* Reserved [15] */
+#define FLD_AM_DIFF_OUT 0x00007fff
+
+/*****************************************************************************/
+#define NICAM_FAW 0x8a0
+#define FLD_FAWDETWINEND 0xFc000000
+#define FLD_FAWDETWINSTR 0x03ff0000
+/* Reserved [15:12] */
+#define FLD_FAWDETTHRSHLD3 0x00000f00
+#define FLD_FAWDETTHRSHLD2 0x000000f0
+#define FLD_FAWDETTHRSHLD1 0x0000000f
+
+/*****************************************************************************/
+/* Cx231xx redefine */
+#define DEEMPH_GAIN 0x8a4
+#define NICAM_DEEMPHGAIN 0x8a4
+/* Reserved [31:18] */
+#define FLD_DEEMPHGAIN 0x0003ffff
+
+/*****************************************************************************/
+/* Cx231xx redefine */
+#define DEEMPH_NUMER1 0x8a8
+#define NICAM_DEEMPHNUMER1 0x8a8
+/* Reserved [31:18] */
+#define FLD_DEEMPHNUMER1 0x0003ffff
+
+/*****************************************************************************/
+/* Cx231xx redefine */
+#define DEEMPH_NUMER2 0x8ac
+#define NICAM_DEEMPHNUMER2 0x8ac
+/* Reserved [31:18] */
+#define FLD_DEEMPHNUMER2 0x0003ffff
+
+/*****************************************************************************/
+/* Cx231xx redefine */
+#define DEEMPH_DENOM1 0x8b0
+#define NICAM_DEEMPHDENOM1 0x8b0
+/* Reserved [31:18] */
+#define FLD_DEEMPHDENOM1 0x0003ffff
+
+/*****************************************************************************/
+/* Cx231xx redefine */
+#define DEEMPH_DENOM2 0x8b4
+#define NICAM_DEEMPHDENOM2 0x8b4
+/* Reserved [31:18] */
+#define FLD_DEEMPHDENOM2 0x0003ffff
+
+/*****************************************************************************/
+#define NICAM_ERRLOG_CTL1 0x8B8
+/* Reserved [31:28] */
+#define FLD_ERRINTRPTTHSHLD1 0x0fff0000
+/* Reserved [15:12] */
+#define FLD_ERRLOGPERIOD 0x00000fff
+
+/*****************************************************************************/
+#define NICAM_ERRLOG_CTL2 0x8bc
+/* Reserved [31:28] */
+#define FLD_ERRINTRPTTHSHLD3 0x0fff0000
+/* Reserved [15:12] */
+#define FLD_ERRINTRPTTHSHLD2 0x00000fff
+
+/*****************************************************************************/
+#define NICAM_ERRLOG_STS1 0x8c0
+/* Reserved [31:28] */
+#define FLD_ERRLOG2 0x0fff0000
+/* Reserved [15:12] */
+#define FLD_ERRLOG1 0x00000fff
+
+/*****************************************************************************/
+#define NICAM_ERRLOG_STS2 0x8c4
+/* Reserved [31:12] */
+#define FLD_ERRLOG3 0x00000fff
+
+/*****************************************************************************/
+#define NICAM_STATUS 0x8c8
+/* Reserved [31:20] */
+#define FLD_NICAM_CIB 0x000c0000
+#define FLD_NICAM_LOCK_STAT 0x00020000
+#define FLD_NICAM_MUTE 0x00010000
+#define FLD_NICAMADDIT_DATA 0x0000ffe0
+#define FLD_NICAMCNTRL 0x0000001f
+
+/*****************************************************************************/
+#define DEMATRIX_CTL 0x8cc
+#define FLD_AC97_IN_SHIFT 0xf0000000
+#define FLD_I2S_IN_SHIFT 0x0f000000
+#define FLD_DEMATRIX_SEL_CTL 0x00ff0000
+/* Reserved [15:11] */
+#define FLD_DMTRX_BYPASS 0x00000400
+#define FLD_DEMATRIX_MODE 0x00000300
+/* Reserved [7:6] */
+#define FLD_PH_DBX_SEL 0x00000020
+#define FLD_PH_CH_SEL 0x00000010
+#define FLD_PHASE_FIX 0x0000000f
+
+/*****************************************************************************/
+#define PATH1_CTL1 0x8d0
+/* Reserved [31:29] */
+#define FLD_PATH1_MUTE_CTL 0x1f000000
+/* Reserved [23:22] */
+#define FLD_PATH1_AVC_CG 0x00300000
+#define FLD_PATH1_AVC_RT 0x000f0000
+#define FLD_PATH1_AVC_AT 0x0000f000
+#define FLD_PATH1_AVC_STEREO 0x00000800
+#define FLD_PATH1_AVC_CR 0x00000700
+#define FLD_PATH1_AVC_RMS_CON 0x000000f0
+#define FLD_PATH1_SEL_CTL 0x0000000f
+
+/*****************************************************************************/
+#define PATH1_VOL_CTL 0x8d4
+#define FLD_PATH1_AVC_THRESHOLD 0x7fff0000
+#define FLD_PATH1_BAL_LEFT 0x00008000
+#define FLD_PATH1_BAL_LEVEL 0x00007f00
+#define FLD_PATH1_VOLUME 0x000000ff
+
+/*****************************************************************************/
+#define PATH1_EQ_CTL 0x8d8
+/* Reserved [31:30] */
+#define FLD_PATH1_EQ_TREBLE_VOL 0x3f000000
+/* Reserved [23:22] */
+#define FLD_PATH1_EQ_MID_VOL 0x003f0000
+/* Reserved [15:14] */
+#define FLD_PATH1_EQ_BASS_VOL 0x00003f00
+/* Reserved [7:1] */
+#define FLD_PATH1_EQ_BAND_SEL 0x00000001
+
+/*****************************************************************************/
+#define PATH1_SC_CTL 0x8dc
+#define FLD_PATH1_SC_THRESHOLD 0x7fff0000
+#define FLD_PATH1_SC_RT 0x0000f000
+#define FLD_PATH1_SC_AT 0x00000f00
+#define FLD_PATH1_SC_STEREO 0x00000080
+#define FLD_PATH1_SC_CR 0x00000070
+#define FLD_PATH1_SC_RMS_CON 0x0000000f
+
+/*****************************************************************************/
+#define PATH2_CTL1 0x8e0
+/* Reserved [31:26] */
+#define FLD_PATH2_MUTE_CTL 0x03000000
+/* Reserved [23:22] */
+#define FLD_PATH2_AVC_CG 0x00300000
+#define FLD_PATH2_AVC_RT 0x000f0000
+#define FLD_PATH2_AVC_AT 0x0000f000
+#define FLD_PATH2_AVC_STEREO 0x00000800
+#define FLD_PATH2_AVC_CR 0x00000700
+#define FLD_PATH2_AVC_RMS_CON 0x000000f0
+#define FLD_PATH2_SEL_CTL 0x0000000f
+
+/*****************************************************************************/
+#define PATH2_VOL_CTL 0x8e4
+#define FLD_PATH2_AVC_THRESHOLD 0xffff0000
+#define FLD_PATH2_BAL_LEFT 0x00008000
+#define FLD_PATH2_BAL_LEVEL 0x00007f00
+#define FLD_PATH2_VOLUME 0x000000ff
+
+/*****************************************************************************/
+#define PATH2_EQ_CTL 0x8e8
+/* Reserved [31:30] */
+#define FLD_PATH2_EQ_TREBLE_VOL 0x3f000000
+/* Reserved [23:22] */
+#define FLD_PATH2_EQ_MID_VOL 0x003f0000
+/* Reserved [15:14] */
+#define FLD_PATH2_EQ_BASS_VOL 0x00003f00
+/* Reserved [7:1] */
+#define FLD_PATH2_EQ_BAND_SEL 0x00000001
+
+/*****************************************************************************/
+#define PATH2_SC_CTL 0x8eC
+#define FLD_PATH2_SC_THRESHOLD 0xffff0000
+#define FLD_PATH2_SC_RT 0x0000f000
+#define FLD_PATH2_SC_AT 0x00000f00
+#define FLD_PATH2_SC_STEREO 0x00000080
+#define FLD_PATH2_SC_CR 0x00000070
+#define FLD_PATH2_SC_RMS_CON 0x0000000f
+
+/*****************************************************************************/
+#define SRC_CTL 0x8f0
+#define FLD_SRC_STATUS 0xffffff00
+#define FLD_FIFO_LF_EN 0x000000fc
+#define FLD_BYPASS_LI 0x00000002
+#define FLD_BYPASS_PF 0x00000001
+
+/*****************************************************************************/
+#define SRC_LF_COEF 0x8f4
+#define FLD_LOOP_FILTER_COEF2 0xffff0000
+#define FLD_LOOP_FILTER_COEF1 0x0000ffff
+
+/*****************************************************************************/
+#define SRC1_CTL 0x8f8
+/* Reserved [31:28] */
+#define FLD_SRC1_FIFO_RD_TH 0x0f000000
+/* Reserved [23:18] */
+#define FLD_SRC1_PHASE_INC 0x0003ffff
+
+/*****************************************************************************/
+#define SRC2_CTL 0x8fc
+/* Reserved [31:28] */
+#define FLD_SRC2_FIFO_RD_TH 0x0f000000
+/* Reserved [23:18] */
+#define FLD_SRC2_PHASE_INC 0x0003ffff
+
+/*****************************************************************************/
+#define SRC3_CTL 0x900
+/* Reserved [31:28] */
+#define FLD_SRC3_FIFO_RD_TH 0x0f000000
+/* Reserved [23:18] */
+#define FLD_SRC3_PHASE_INC 0x0003ffff
+
+/*****************************************************************************/
+#define SRC4_CTL 0x904
+/* Reserved [31:28] */
+#define FLD_SRC4_FIFO_RD_TH 0x0f000000
+/* Reserved [23:18] */
+#define FLD_SRC4_PHASE_INC 0x0003ffff
+
+/*****************************************************************************/
+#define SRC5_CTL 0x908
+/* Reserved [31:28] */
+#define FLD_SRC5_FIFO_RD_TH 0x0f000000
+/* Reserved [23:18] */
+#define FLD_SRC5_PHASE_INC 0x0003ffff
+
+/*****************************************************************************/
+#define SRC6_CTL 0x90c
+/* Reserved [31:28] */
+#define FLD_SRC6_FIFO_RD_TH 0x0f000000
+/* Reserved [23:18] */
+#define FLD_SRC6_PHASE_INC 0x0003ffff
+
+/*****************************************************************************/
+#define BAND_OUT_SEL 0x910
+#define FLD_SRC6_IN_SEL 0xc0000000
+#define FLD_SRC6_CLK_SEL 0x30000000
+#define FLD_SRC5_IN_SEL 0x0c000000
+#define FLD_SRC5_CLK_SEL 0x03000000
+#define FLD_SRC4_IN_SEL 0x00c00000
+#define FLD_SRC4_CLK_SEL 0x00300000
+#define FLD_SRC3_IN_SEL 0x000c0000
+#define FLD_SRC3_CLK_SEL 0x00030000
+#define FLD_BASEBAND_BYPASS_CTL 0x0000ff00
+#define FLD_AC97_SRC_SEL 0x000000c0
+#define FLD_I2S_SRC_SEL 0x00000030
+#define FLD_PARALLEL2_SRC_SEL 0x0000000c
+#define FLD_PARALLEL1_SRC_SEL 0x00000003
+
+/*****************************************************************************/
+#define I2S_IN_CTL 0x914
+/* Reserved [31:11] */
+#define FLD_I2S_UP2X_BW20K 0x00000400
+#define FLD_I2S_UP2X_BYPASS 0x00000200
+#define FLD_I2S_IN_MASTER_MODE 0x00000100
+#define FLD_I2S_IN_SONY_MODE 0x00000080
+#define FLD_I2S_IN_RIGHT_JUST 0x00000040
+#define FLD_I2S_IN_WS_SEL 0x00000020
+#define FLD_I2S_IN_BCN_DEL 0x0000001f
+
+/*****************************************************************************/
+#define I2S_OUT_CTL 0x918
+/* Reserved [31:17] */
+#define FLD_I2S_OUT_SOFT_RESET_EN 0x00010000
+/* Reserved [15:9] */
+#define FLD_I2S_OUT_MASTER_MODE 0x00000100
+#define FLD_I2S_OUT_SONY_MODE 0x00000080
+#define FLD_I2S_OUT_RIGHT_JUST 0x00000040
+#define FLD_I2S_OUT_WS_SEL 0x00000020
+#define FLD_I2S_OUT_BCN_DEL 0x0000001f
+
+/*****************************************************************************/
+#define AC97_CTL 0x91c
+/* Reserved [31:26] */
+#define FLD_AC97_UP2X_BW20K 0x02000000
+#define FLD_AC97_UP2X_BYPASS 0x01000000
+/* Reserved [23:17] */
+#define FLD_AC97_RST_ACL 0x00010000
+/* Reserved [15:9] */
+#define FLD_AC97_WAKE_UP_SYNC 0x00000100
+/* Reserved [7:1] */
+#define FLD_AC97_SHUTDOWN 0x00000001
+
+/* Cx231xx redefine */
+#define QPSK_IAGC_CTL1 0x94c
+#define QPSK_IAGC_CTL2 0x950
+#define QPSK_FEPR_FREQ 0x954
+#define QPSK_BTL_CTL1 0x958
+#define QPSK_BTL_CTL2 0x95c
+#define QPSK_CTL_CTL1 0x960
+#define QPSK_CTL_CTL2 0x964
+#define QPSK_MF_FAGC_CTL 0x968
+#define QPSK_EQ_CTL 0x96c
+#define QPSK_LOCK_CTL 0x970
+
+/*****************************************************************************/
+#define FM1_DFT_CTL 0x9a8
+#define FLD_FM1_DFT_THRESHOLD 0xffff0000
+/* Reserved [15:8] */
+#define FLD_FM1_DFT_CMP_CTL 0x00000080
+#define FLD_FM1_DFT_AVG 0x00000070
+/* Reserved [3:1] */
+#define FLD_FM1_DFT_START 0x00000001
+
+/*****************************************************************************/
+#define FM1_DFT_STATUS 0x9ac
+#define FLD_FM1_DFT_DONE 0x80000000
+/* Reserved [30:19] */
+#define FLD_FM_DFT_TH_CMP 0x00040000
+#define FLD_FM1_DFT 0x0003ffff
+
+/*****************************************************************************/
+#define FM2_DFT_CTL 0x9b0
+#define FLD_FM2_DFT_THRESHOLD 0xffff0000
+/* Reserved [15:8] */
+#define FLD_FM2_DFT_CMP_CTL 0x00000080
+#define FLD_FM2_DFT_AVG 0x00000070
+/* Reserved [3:1] */
+#define FLD_FM2_DFT_START 0x00000001
+
+/*****************************************************************************/
+#define FM2_DFT_STATUS 0x9b4
+#define FLD_FM2_DFT_DONE 0x80000000
+/* Reserved [30:19] */
+#define FLD_FM2_DFT_TH_CMP_STAT 0x00040000
+#define FLD_FM2_DFT 0x0003ffff
+
+/*****************************************************************************/
+/* Cx231xx redefine */
+#define AAGC_STATUS_REG 0x9b8
+#define AAGC_STATUS 0x9b8
+/* Reserved [31:27] */
+#define FLD_FM2_DAGC_OUT 0x07000000
+/* Reserved [23:19] */
+#define FLD_FM1_DAGC_OUT 0x00070000
+/* Reserved [15:6] */
+#define FLD_AFE_VGA_OUT 0x0000003f
+
+/*****************************************************************************/
+#define MTS_GAIN_STATUS 0x9bc
+/* Reserved [31:14] */
+#define FLD_MTS_GAIN 0x00003fff
+
+#define RDS_OUT 0x9c0
+#define FLD_RDS_Q 0xffff0000
+#define FLD_RDS_I 0x0000ffff
+
+/*****************************************************************************/
+#define AUTOCONFIG_REG 0x9c4
+/* Reserved [31:4] */
+#define FLD_AUTOCONFIG_MODE 0x0000000f
+
+#define FM_AFC 0x9c8
+#define FLD_FM2_AFC 0xffff0000
+#define FLD_FM1_AFC 0x0000ffff
+
+/*****************************************************************************/
+/* Cx231xx redefine */
+#define NEW_SPARE 0x9cc
+#define NEW_SPARE_REG 0x9cc
+
+/*****************************************************************************/
+#define DBX_ADJ 0x9d0
+/* Reserved [31:28] */
+#define FLD_DBX2_ADJ 0x0fff0000
+/* Reserved [15:12] */
+#define FLD_DBX1_ADJ 0x00000fff
+
+#define VID_FMT_AUTO 0
+#define VID_FMT_NTSC_M 1
+#define VID_FMT_NTSC_J 2
+#define VID_FMT_NTSC_443 3
+#define VID_FMT_PAL_BDGHI 4
+#define VID_FMT_PAL_M 5
+#define VID_FMT_PAL_N 6
+#define VID_FMT_PAL_NC 7
+#define VID_FMT_PAL_60 8
+#define VID_FMT_SECAM 12
+#define VID_FMT_SECAM_60 13
+
+#define INPUT_MODE_CVBS_0 0 /* INPUT_MODE_VALUE(0) */
+#define INPUT_MODE_YC_1 1 /* INPUT_MODE_VALUE(1) */
+#define INPUT_MODE_YC2_2 2 /* INPUT_MODE_VALUE(2) */
+#define INPUT_MODE_YUV_3 3 /* INPUT_MODE_VALUE(3) */
+
+#define LUMA_LPF_LOW_BANDPASS 0 /* 0.6Mhz LPF BW */
+#define LUMA_LPF_MEDIUM_BANDPASS 1 /* 1.0Mhz LPF BW */
+#define LUMA_LPF_HIGH_BANDPASS 2 /* 1.5Mhz LPF BW */
+
+#define UV_LPF_LOW_BANDPASS 0 /* 0.6Mhz LPF BW */
+#define UV_LPF_MEDIUM_BANDPASS 1 /* 1.0Mhz LPF BW */
+#define UV_LPF_HIGH_BANDPASS 2 /* 1.5Mhz LPF BW */
+
+#define TWO_TAP_FILT 0
+#define THREE_TAP_FILT 1
+#define FOUR_TAP_FILT 2
+#define FIVE_TAP_FILT 3
+
+#define AUD_CHAN_SRC_PARALLEL 0
+#define AUD_CHAN_SRC_I2S_INPUT 1
+#define AUD_CHAN_SRC_FLATIRON 2
+#define AUD_CHAN_SRC_PARALLEL3 3
+
+#define OUT_MODE_601 0
+#define OUT_MODE_656 1
+#define OUT_MODE_VIP11 2
+#define OUT_MODE_VIP20 3
+
+#define PHASE_INC_49MHZ 0x0df22
+#define PHASE_INC_56MHZ 0x0fa5b
+#define PHASE_INC_28MHZ 0x010000
+
+#endif
diff --git a/drivers/media/video/cx231xx/cx231xx-vbi.c b/drivers/media/video/cx231xx/cx231xx-vbi.c
new file mode 100644
index 00000000..8cdee5f7
--- /dev/null
+++ b/drivers/media/video/cx231xx/cx231xx-vbi.c
@@ -0,0 +1,714 @@
+/*
+ cx231xx_vbi.c - driver for Conexant Cx23100/101/102 USB video capture devices
+
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
+ Based on cx88 driver
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/bitmap.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/msp3400.h>
+#include <media/tuner.h>
+
+#include "cx231xx.h"
+#include "cx231xx-vbi.h"
+
+static inline void print_err_status(struct cx231xx *dev, int packet, int status)
+{
+ char *errmsg = "Unknown";
+
+ switch (status) {
+ case -ENOENT:
+ errmsg = "unlinked synchronuously";
+ break;
+ case -ECONNRESET:
+ errmsg = "unlinked asynchronuously";
+ break;
+ case -ENOSR:
+ errmsg = "Buffer error (overrun)";
+ break;
+ case -EPIPE:
+ errmsg = "Stalled (device not responding)";
+ break;
+ case -EOVERFLOW:
+ errmsg = "Babble (bad cable?)";
+ break;
+ case -EPROTO:
+ errmsg = "Bit-stuff error (bad cable?)";
+ break;
+ case -EILSEQ:
+ errmsg = "CRC/Timeout (could be anything)";
+ break;
+ case -ETIME:
+ errmsg = "Device does not respond";
+ break;
+ }
+ if (packet < 0) {
+ cx231xx_err(DRIVER_NAME "URB status %d [%s].\n", status,
+ errmsg);
+ } else {
+ cx231xx_err(DRIVER_NAME "URB packet %d, status %d [%s].\n",
+ packet, status, errmsg);
+ }
+}
+
+/*
+ * Controls the isoc copy of each urb packet
+ */
+static inline int cx231xx_isoc_vbi_copy(struct cx231xx *dev, struct urb *urb)
+{
+ struct cx231xx_buffer *buf;
+ struct cx231xx_dmaqueue *dma_q = urb->context;
+ int rc = 1;
+ unsigned char *p_buffer;
+ u32 bytes_parsed = 0, buffer_size = 0;
+ u8 sav_eav = 0;
+
+ if (!dev)
+ return 0;
+
+ if (dev->state & DEV_DISCONNECTED)
+ return 0;
+
+ if (urb->status < 0) {
+ print_err_status(dev, -1, urb->status);
+ if (urb->status == -ENOENT)
+ return 0;
+ }
+
+ buf = dev->vbi_mode.bulk_ctl.buf;
+
+ /* get buffer pointer and length */
+ p_buffer = urb->transfer_buffer;
+ buffer_size = urb->actual_length;
+
+ if (buffer_size > 0) {
+ bytes_parsed = 0;
+
+ if (dma_q->is_partial_line) {
+ /* Handle the case where we were working on a partial
+ line */
+ sav_eav = dma_q->last_sav;
+ } else {
+ /* Check for a SAV/EAV overlapping the
+ buffer boundary */
+
+ sav_eav = cx231xx_find_boundary_SAV_EAV(p_buffer,
+ dma_q->partial_buf,
+ &bytes_parsed);
+ }
+
+ sav_eav &= 0xF0;
+ /* Get the first line if we have some portion of an SAV/EAV from
+ the last buffer or a partial line */
+ if (sav_eav) {
+ bytes_parsed += cx231xx_get_vbi_line(dev, dma_q,
+ sav_eav, /* SAV/EAV */
+ p_buffer + bytes_parsed, /* p_buffer */
+ buffer_size - bytes_parsed); /* buffer size */
+ }
+
+ /* Now parse data that is completely in this buffer */
+ dma_q->is_partial_line = 0;
+
+ while (bytes_parsed < buffer_size) {
+ u32 bytes_used = 0;
+
+ sav_eav = cx231xx_find_next_SAV_EAV(
+ p_buffer + bytes_parsed, /* p_buffer */
+ buffer_size - bytes_parsed, /* buffer size */
+ &bytes_used); /* bytes used to get SAV/EAV */
+
+ bytes_parsed += bytes_used;
+
+ sav_eav &= 0xF0;
+ if (sav_eav && (bytes_parsed < buffer_size)) {
+ bytes_parsed += cx231xx_get_vbi_line(dev,
+ dma_q, sav_eav, /* SAV/EAV */
+ p_buffer+bytes_parsed, /* p_buffer */
+ buffer_size-bytes_parsed);/*buf size*/
+ }
+ }
+
+ /* Save the last four bytes of the buffer so we can
+ check the buffer boundary condition next time */
+ memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4);
+ bytes_parsed = 0;
+ }
+
+ return rc;
+}
+
+/* ------------------------------------------------------------------
+ Vbi buf operations
+ ------------------------------------------------------------------*/
+
+static int
+vbi_buffer_setup(struct videobuf_queue *vq, unsigned int *count,
+ unsigned int *size)
+{
+ struct cx231xx_fh *fh = vq->priv_data;
+ struct cx231xx *dev = fh->dev;
+ u32 height = 0;
+
+ height = ((dev->norm & V4L2_STD_625_50) ?
+ PAL_VBI_LINES : NTSC_VBI_LINES);
+
+ *size = (dev->width * height * 2 * 2);
+ if (0 == *count)
+ *count = CX231XX_DEF_VBI_BUF;
+
+ if (*count < CX231XX_MIN_BUF)
+ *count = CX231XX_MIN_BUF;
+
+ return 0;
+}
+
+/* This is called *without* dev->slock held; please keep it that way */
+static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf)
+{
+ struct cx231xx_fh *fh = vq->priv_data;
+ struct cx231xx *dev = fh->dev;
+ unsigned long flags = 0;
+ if (in_interrupt())
+ BUG();
+
+ /* We used to wait for the buffer to finish here, but this didn't work
+ because, as we were keeping the state as VIDEOBUF_QUEUED,
+ videobuf_queue_cancel marked it as finished for us.
+ (Also, it could wedge forever if the hardware was misconfigured.)
+
+ This should be safe; by the time we get here, the buffer isn't
+ queued anymore. If we ever start marking the buffers as
+ VIDEOBUF_ACTIVE, it won't be, though.
+ */
+ spin_lock_irqsave(&dev->vbi_mode.slock, flags);
+ if (dev->vbi_mode.bulk_ctl.buf == buf)
+ dev->vbi_mode.bulk_ctl.buf = NULL;
+ spin_unlock_irqrestore(&dev->vbi_mode.slock, flags);
+
+ videobuf_vmalloc_free(&buf->vb);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int
+vbi_buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct cx231xx_fh *fh = vq->priv_data;
+ struct cx231xx_buffer *buf =
+ container_of(vb, struct cx231xx_buffer, vb);
+ struct cx231xx *dev = fh->dev;
+ int rc = 0, urb_init = 0;
+ u32 height = 0;
+
+ height = ((dev->norm & V4L2_STD_625_50) ?
+ PAL_VBI_LINES : NTSC_VBI_LINES);
+ buf->vb.size = ((dev->width << 1) * height * 2);
+
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+
+ buf->vb.width = dev->width;
+ buf->vb.height = height;
+ buf->vb.field = field;
+ buf->vb.field = V4L2_FIELD_SEQ_TB;
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ rc = videobuf_iolock(vq, &buf->vb, NULL);
+ if (rc < 0)
+ goto fail;
+ }
+
+ if (!dev->vbi_mode.bulk_ctl.num_bufs)
+ urb_init = 1;
+
+ if (urb_init) {
+ rc = cx231xx_init_vbi_isoc(dev, CX231XX_NUM_VBI_PACKETS,
+ CX231XX_NUM_VBI_BUFS,
+ dev->vbi_mode.alt_max_pkt_size[0],
+ cx231xx_isoc_vbi_copy);
+ if (rc < 0)
+ goto fail;
+ }
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+fail:
+ free_buffer(vq, buf);
+ return rc;
+}
+
+static void
+vbi_buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct cx231xx_buffer *buf =
+ container_of(vb, struct cx231xx_buffer, vb);
+ struct cx231xx_fh *fh = vq->priv_data;
+ struct cx231xx *dev = fh->dev;
+ struct cx231xx_dmaqueue *vidq = &dev->vbi_mode.vidq;
+
+ buf->vb.state = VIDEOBUF_QUEUED;
+ list_add_tail(&buf->vb.queue, &vidq->active);
+
+}
+
+static void vbi_buffer_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct cx231xx_buffer *buf =
+ container_of(vb, struct cx231xx_buffer, vb);
+
+
+ free_buffer(vq, buf);
+}
+
+struct videobuf_queue_ops cx231xx_vbi_qops = {
+ .buf_setup = vbi_buffer_setup,
+ .buf_prepare = vbi_buffer_prepare,
+ .buf_queue = vbi_buffer_queue,
+ .buf_release = vbi_buffer_release,
+};
+
+/* ------------------------------------------------------------------
+ URB control
+ ------------------------------------------------------------------*/
+
+/*
+ * IRQ callback, called by URB callback
+ */
+static void cx231xx_irq_vbi_callback(struct urb *urb)
+{
+ struct cx231xx_dmaqueue *dma_q = urb->context;
+ struct cx231xx_video_mode *vmode =
+ container_of(dma_q, struct cx231xx_video_mode, vidq);
+ struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode);
+ int rc;
+
+ switch (urb->status) {
+ case 0: /* success */
+ case -ETIMEDOUT: /* NAK */
+ break;
+ case -ECONNRESET: /* kill */
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ default: /* error */
+ cx231xx_err(DRIVER_NAME "urb completition error %d.\n",
+ urb->status);
+ break;
+ }
+
+ /* Copy data from URB */
+ spin_lock(&dev->vbi_mode.slock);
+ rc = dev->vbi_mode.bulk_ctl.bulk_copy(dev, urb);
+ spin_unlock(&dev->vbi_mode.slock);
+
+ /* Reset status */
+ urb->status = 0;
+
+ urb->status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (urb->status) {
+ cx231xx_err(DRIVER_NAME "urb resubmit failed (error=%i)\n",
+ urb->status);
+ }
+}
+
+/*
+ * Stop and Deallocate URBs
+ */
+void cx231xx_uninit_vbi_isoc(struct cx231xx *dev)
+{
+ struct urb *urb;
+ int i;
+
+ cx231xx_info(DRIVER_NAME "cx231xx: called cx231xx_uninit_vbi_isoc\n");
+
+ dev->vbi_mode.bulk_ctl.nfields = -1;
+ for (i = 0; i < dev->vbi_mode.bulk_ctl.num_bufs; i++) {
+ urb = dev->vbi_mode.bulk_ctl.urb[i];
+ if (urb) {
+ if (!irqs_disabled())
+ usb_kill_urb(urb);
+ else
+ usb_unlink_urb(urb);
+
+ if (dev->vbi_mode.bulk_ctl.transfer_buffer[i]) {
+
+ kfree(dev->vbi_mode.bulk_ctl.
+ transfer_buffer[i]);
+ dev->vbi_mode.bulk_ctl.transfer_buffer[i] =
+ NULL;
+ }
+ usb_free_urb(urb);
+ dev->vbi_mode.bulk_ctl.urb[i] = NULL;
+ }
+ dev->vbi_mode.bulk_ctl.transfer_buffer[i] = NULL;
+ }
+
+ kfree(dev->vbi_mode.bulk_ctl.urb);
+ kfree(dev->vbi_mode.bulk_ctl.transfer_buffer);
+
+ dev->vbi_mode.bulk_ctl.urb = NULL;
+ dev->vbi_mode.bulk_ctl.transfer_buffer = NULL;
+ dev->vbi_mode.bulk_ctl.num_bufs = 0;
+
+ cx231xx_capture_start(dev, 0, Vbi);
+}
+EXPORT_SYMBOL_GPL(cx231xx_uninit_vbi_isoc);
+
+/*
+ * Allocate URBs and start IRQ
+ */
+int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets,
+ int num_bufs, int max_pkt_size,
+ int (*bulk_copy) (struct cx231xx *dev,
+ struct urb *urb))
+{
+ struct cx231xx_dmaqueue *dma_q = &dev->vbi_mode.vidq;
+ int i;
+ int sb_size, pipe;
+ struct urb *urb;
+ int rc;
+
+ cx231xx_info(DRIVER_NAME "cx231xx: called cx231xx_prepare_isoc\n");
+
+ /* De-allocates all pending stuff */
+ cx231xx_uninit_vbi_isoc(dev);
+
+ /* clear if any halt */
+ usb_clear_halt(dev->udev,
+ usb_rcvbulkpipe(dev->udev,
+ dev->vbi_mode.end_point_addr));
+
+ dev->vbi_mode.bulk_ctl.bulk_copy = bulk_copy;
+ dev->vbi_mode.bulk_ctl.num_bufs = num_bufs;
+ dma_q->pos = 0;
+ dma_q->is_partial_line = 0;
+ dma_q->last_sav = 0;
+ dma_q->current_field = -1;
+ dma_q->bytes_left_in_line = dev->width << 1;
+ dma_q->lines_per_field = ((dev->norm & V4L2_STD_625_50) ?
+ PAL_VBI_LINES : NTSC_VBI_LINES);
+ dma_q->lines_completed = 0;
+ for (i = 0; i < 8; i++)
+ dma_q->partial_buf[i] = 0;
+
+ dev->vbi_mode.bulk_ctl.urb = kzalloc(sizeof(void *) * num_bufs,
+ GFP_KERNEL);
+ if (!dev->vbi_mode.bulk_ctl.urb) {
+ cx231xx_errdev("cannot alloc memory for usb buffers\n");
+ return -ENOMEM;
+ }
+
+ dev->vbi_mode.bulk_ctl.transfer_buffer =
+ kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
+ if (!dev->vbi_mode.bulk_ctl.transfer_buffer) {
+ cx231xx_errdev("cannot allocate memory for usbtransfer\n");
+ kfree(dev->vbi_mode.bulk_ctl.urb);
+ return -ENOMEM;
+ }
+
+ dev->vbi_mode.bulk_ctl.max_pkt_size = max_pkt_size;
+ dev->vbi_mode.bulk_ctl.buf = NULL;
+
+ sb_size = max_packets * dev->vbi_mode.bulk_ctl.max_pkt_size;
+
+ /* allocate urbs and transfer buffers */
+ for (i = 0; i < dev->vbi_mode.bulk_ctl.num_bufs; i++) {
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ cx231xx_err(DRIVER_NAME
+ ": cannot alloc bulk_ctl.urb %i\n", i);
+ cx231xx_uninit_vbi_isoc(dev);
+ return -ENOMEM;
+ }
+ dev->vbi_mode.bulk_ctl.urb[i] = urb;
+ urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+
+ dev->vbi_mode.bulk_ctl.transfer_buffer[i] =
+ kzalloc(sb_size, GFP_KERNEL);
+ if (!dev->vbi_mode.bulk_ctl.transfer_buffer[i]) {
+ cx231xx_err(DRIVER_NAME
+ ": unable to allocate %i bytes for transfer"
+ " buffer %i%s\n", sb_size, i,
+ in_interrupt() ? " while in int" : "");
+ cx231xx_uninit_vbi_isoc(dev);
+ return -ENOMEM;
+ }
+
+ pipe = usb_rcvbulkpipe(dev->udev, dev->vbi_mode.end_point_addr);
+ usb_fill_bulk_urb(urb, dev->udev, pipe,
+ dev->vbi_mode.bulk_ctl.transfer_buffer[i],
+ sb_size, cx231xx_irq_vbi_callback, dma_q);
+ }
+
+ init_waitqueue_head(&dma_q->wq);
+
+ /* submit urbs and enables IRQ */
+ for (i = 0; i < dev->vbi_mode.bulk_ctl.num_bufs; i++) {
+ rc = usb_submit_urb(dev->vbi_mode.bulk_ctl.urb[i], GFP_ATOMIC);
+ if (rc) {
+ cx231xx_err(DRIVER_NAME
+ ": submit of urb %i failed (error=%i)\n", i,
+ rc);
+ cx231xx_uninit_vbi_isoc(dev);
+ return rc;
+ }
+ }
+
+ cx231xx_capture_start(dev, 1, Vbi);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cx231xx_init_vbi_isoc);
+
+u32 cx231xx_get_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
+ u8 sav_eav, u8 *p_buffer, u32 buffer_size)
+{
+ u32 bytes_copied = 0;
+ int current_field = -1;
+
+ switch (sav_eav) {
+
+ case SAV_VBI_FIELD1:
+ current_field = 1;
+ break;
+
+ case SAV_VBI_FIELD2:
+ current_field = 2;
+ break;
+ default:
+ break;
+ }
+
+ if (current_field < 0)
+ return bytes_copied;
+
+ dma_q->last_sav = sav_eav;
+
+ bytes_copied =
+ cx231xx_copy_vbi_line(dev, dma_q, p_buffer, buffer_size,
+ current_field);
+
+ return bytes_copied;
+}
+
+/*
+ * Announces that a buffer were filled and request the next
+ */
+static inline void vbi_buffer_filled(struct cx231xx *dev,
+ struct cx231xx_dmaqueue *dma_q,
+ struct cx231xx_buffer *buf)
+{
+ /* Advice that buffer was filled */
+ /* cx231xx_info(DRIVER_NAME "[%p/%d] wakeup\n", buf, buf->vb.i); */
+
+ buf->vb.state = VIDEOBUF_DONE;
+ buf->vb.field_count++;
+ do_gettimeofday(&buf->vb.ts);
+
+ dev->vbi_mode.bulk_ctl.buf = NULL;
+
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+}
+
+u32 cx231xx_copy_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
+ u8 *p_line, u32 length, int field_number)
+{
+ u32 bytes_to_copy;
+ struct cx231xx_buffer *buf;
+ u32 _line_size = dev->width * 2;
+
+ if (dma_q->current_field == -1) {
+ /* Just starting up */
+ cx231xx_reset_vbi_buffer(dev, dma_q);
+ }
+
+ if (dma_q->current_field != field_number)
+ dma_q->lines_completed = 0;
+
+ /* get the buffer pointer */
+ buf = dev->vbi_mode.bulk_ctl.buf;
+
+ /* Remember the field number for next time */
+ dma_q->current_field = field_number;
+
+ bytes_to_copy = dma_q->bytes_left_in_line;
+ if (bytes_to_copy > length)
+ bytes_to_copy = length;
+
+ if (dma_q->lines_completed >= dma_q->lines_per_field) {
+ dma_q->bytes_left_in_line -= bytes_to_copy;
+ dma_q->is_partial_line =
+ (dma_q->bytes_left_in_line == 0) ? 0 : 1;
+ return 0;
+ }
+
+ dma_q->is_partial_line = 1;
+
+ /* If we don't have a buffer, just return the number of bytes we would
+ have copied if we had a buffer. */
+ if (!buf) {
+ dma_q->bytes_left_in_line -= bytes_to_copy;
+ dma_q->is_partial_line =
+ (dma_q->bytes_left_in_line == 0) ? 0 : 1;
+ return bytes_to_copy;
+ }
+
+ /* copy the data to video buffer */
+ cx231xx_do_vbi_copy(dev, dma_q, p_line, bytes_to_copy);
+
+ dma_q->pos += bytes_to_copy;
+ dma_q->bytes_left_in_line -= bytes_to_copy;
+
+ if (dma_q->bytes_left_in_line == 0) {
+
+ dma_q->bytes_left_in_line = _line_size;
+ dma_q->lines_completed++;
+ dma_q->is_partial_line = 0;
+
+ if (cx231xx_is_vbi_buffer_done(dev, dma_q) && buf) {
+
+ vbi_buffer_filled(dev, dma_q, buf);
+
+ dma_q->pos = 0;
+ dma_q->lines_completed = 0;
+ cx231xx_reset_vbi_buffer(dev, dma_q);
+ }
+ }
+
+ return bytes_to_copy;
+}
+
+/*
+ * video-buf generic routine to get the next available buffer
+ */
+static inline void get_next_vbi_buf(struct cx231xx_dmaqueue *dma_q,
+ struct cx231xx_buffer **buf)
+{
+ struct cx231xx_video_mode *vmode =
+ container_of(dma_q, struct cx231xx_video_mode, vidq);
+ struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode);
+ char *outp;
+
+ if (list_empty(&dma_q->active)) {
+ cx231xx_err(DRIVER_NAME ": No active queue to serve\n");
+ dev->vbi_mode.bulk_ctl.buf = NULL;
+ *buf = NULL;
+ return;
+ }
+
+ /* Get the next buffer */
+ *buf = list_entry(dma_q->active.next, struct cx231xx_buffer, vb.queue);
+
+ /* Cleans up buffer - Useful for testing for frame/URB loss */
+ outp = videobuf_to_vmalloc(&(*buf)->vb);
+ memset(outp, 0, (*buf)->vb.size);
+
+ dev->vbi_mode.bulk_ctl.buf = *buf;
+
+ return;
+}
+
+void cx231xx_reset_vbi_buffer(struct cx231xx *dev,
+ struct cx231xx_dmaqueue *dma_q)
+{
+ struct cx231xx_buffer *buf;
+
+ buf = dev->vbi_mode.bulk_ctl.buf;
+
+ if (buf == NULL) {
+ /* first try to get the buffer */
+ get_next_vbi_buf(dma_q, &buf);
+
+ dma_q->pos = 0;
+ dma_q->current_field = -1;
+ }
+
+ dma_q->bytes_left_in_line = dev->width << 1;
+ dma_q->lines_completed = 0;
+}
+
+int cx231xx_do_vbi_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
+ u8 *p_buffer, u32 bytes_to_copy)
+{
+ u8 *p_out_buffer = NULL;
+ u32 current_line_bytes_copied = 0;
+ struct cx231xx_buffer *buf;
+ u32 _line_size = dev->width << 1;
+ void *startwrite;
+ int offset, lencopy;
+
+ buf = dev->vbi_mode.bulk_ctl.buf;
+
+ if (buf == NULL)
+ return -EINVAL;
+
+ p_out_buffer = videobuf_to_vmalloc(&buf->vb);
+
+ if (dma_q->bytes_left_in_line != _line_size) {
+ current_line_bytes_copied =
+ _line_size - dma_q->bytes_left_in_line;
+ }
+
+ offset = (dma_q->lines_completed * _line_size) +
+ current_line_bytes_copied;
+
+ if (dma_q->current_field == 2) {
+ /* Populate the second half of the frame */
+ offset += (dev->width * 2 * dma_q->lines_per_field);
+ }
+
+ /* prepare destination address */
+ startwrite = p_out_buffer + offset;
+
+ lencopy = dma_q->bytes_left_in_line > bytes_to_copy ?
+ bytes_to_copy : dma_q->bytes_left_in_line;
+
+ memcpy(startwrite, p_buffer, lencopy);
+
+ return 0;
+}
+
+u8 cx231xx_is_vbi_buffer_done(struct cx231xx *dev,
+ struct cx231xx_dmaqueue *dma_q)
+{
+ u32 height = 0;
+
+ height = ((dev->norm & V4L2_STD_625_50) ?
+ PAL_VBI_LINES : NTSC_VBI_LINES);
+ if (dma_q->lines_completed == height && dma_q->current_field == 2)
+ return 1;
+ else
+ return 0;
+}
diff --git a/drivers/media/video/cx231xx/cx231xx-vbi.h b/drivers/media/video/cx231xx/cx231xx-vbi.h
new file mode 100644
index 00000000..16c7d20a
--- /dev/null
+++ b/drivers/media/video/cx231xx/cx231xx-vbi.h
@@ -0,0 +1,65 @@
+/*
+ cx231xx_vbi.h - driver for Conexant Cx23100/101/102 USB video capture devices
+
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
+ Based on cx88 driver
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _CX231XX_VBI_H
+#define _CX231XX_VBI_H
+
+extern struct videobuf_queue_ops cx231xx_vbi_qops;
+
+#define NTSC_VBI_START_LINE 10 /* line 10 - 21 */
+#define NTSC_VBI_END_LINE 21
+#define NTSC_VBI_LINES (NTSC_VBI_END_LINE-NTSC_VBI_START_LINE+1)
+
+#define PAL_VBI_START_LINE 6
+#define PAL_VBI_END_LINE 23
+#define PAL_VBI_LINES (PAL_VBI_END_LINE-PAL_VBI_START_LINE+1)
+
+#define VBI_STRIDE 1440
+#define VBI_SAMPLES_PER_LINE 1440
+
+#define CX231XX_NUM_VBI_PACKETS 4
+#define CX231XX_NUM_VBI_BUFS 5
+
+/* stream functions */
+int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets,
+ int num_bufs, int max_pkt_size,
+ int (*bulk_copy) (struct cx231xx *dev,
+ struct urb *urb));
+
+void cx231xx_uninit_vbi_isoc(struct cx231xx *dev);
+
+/* vbi data copy functions */
+u32 cx231xx_get_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
+ u8 sav_eav, u8 *p_buffer, u32 buffer_size);
+
+u32 cx231xx_copy_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
+ u8 *p_line, u32 length, int field_number);
+
+void cx231xx_reset_vbi_buffer(struct cx231xx *dev,
+ struct cx231xx_dmaqueue *dma_q);
+
+int cx231xx_do_vbi_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
+ u8 *p_buffer, u32 bytes_to_copy);
+
+u8 cx231xx_is_vbi_buffer_done(struct cx231xx *dev,
+ struct cx231xx_dmaqueue *dma_q);
+
+#endif
diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c
new file mode 100644
index 00000000..7f916f06
--- /dev/null
+++ b/drivers/media/video/cx231xx/cx231xx-video.c
@@ -0,0 +1,2657 @@
+/*
+ cx231xx-video.c - driver for Conexant Cx23100/101/102
+ USB video capture devices
+
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
+ Based on em28xx driver
+ Based on cx23885 driver
+ Based on cx88 driver
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/bitmap.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/msp3400.h>
+#include <media/tuner.h>
+
+#include "dvb_frontend.h"
+
+#include "cx231xx.h"
+#include "cx231xx-vbi.h"
+
+#define CX231XX_VERSION "0.0.2"
+
+#define DRIVER_AUTHOR "Srinivasa Deevi <srinivasa.deevi@conexant.com>"
+#define DRIVER_DESC "Conexant cx231xx based USB video device driver"
+
+#define cx231xx_videodbg(fmt, arg...) do {\
+ if (video_debug) \
+ printk(KERN_INFO "%s %s :"fmt, \
+ dev->name, __func__ , ##arg); } while (0)
+
+static unsigned int isoc_debug;
+module_param(isoc_debug, int, 0644);
+MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]");
+
+#define cx231xx_isocdbg(fmt, arg...) \
+do {\
+ if (isoc_debug) { \
+ printk(KERN_INFO "%s %s :"fmt, \
+ dev->name, __func__ , ##arg); \
+ } \
+ } while (0)
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(CX231XX_VERSION);
+
+static unsigned int card[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET };
+static unsigned int video_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET };
+static unsigned int vbi_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET };
+static unsigned int radio_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET };
+
+module_param_array(card, int, NULL, 0444);
+module_param_array(video_nr, int, NULL, 0444);
+module_param_array(vbi_nr, int, NULL, 0444);
+module_param_array(radio_nr, int, NULL, 0444);
+
+MODULE_PARM_DESC(card, "card type");
+MODULE_PARM_DESC(video_nr, "video device numbers");
+MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
+MODULE_PARM_DESC(radio_nr, "radio device numbers");
+
+static unsigned int video_debug;
+module_param(video_debug, int, 0644);
+MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
+
+/* supported video standards */
+static struct cx231xx_fmt format[] = {
+ {
+ .name = "16bpp YUY2, 4:2:2, packed",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .reg = 0,
+ },
+};
+
+/* supported controls */
+/* Common to all boards */
+
+/* ------------------------------------------------------------------- */
+
+static const struct v4l2_queryctrl no_ctl = {
+ .name = "42",
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+};
+
+static struct cx231xx_ctrl cx231xx_ctls[] = {
+ /* --- video --- */
+ {
+ .v = {
+ .id = V4L2_CID_BRIGHTNESS,
+ .name = "Brightness",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 128,
+ .reg = LUMA_CTRL,
+ .mask = 0x00ff,
+ .shift = 0,
+ }, {
+ .v = {
+ .id = V4L2_CID_CONTRAST,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x3f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 0,
+ .reg = LUMA_CTRL,
+ .mask = 0xff00,
+ .shift = 8,
+ }, {
+ .v = {
+ .id = V4L2_CID_HUE,
+ .name = "Hue",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 128,
+ .reg = CHROMA_CTRL,
+ .mask = 0xff0000,
+ .shift = 16,
+ }, {
+ /* strictly, this only describes only U saturation.
+ * V saturation is handled specially through code.
+ */
+ .v = {
+ .id = V4L2_CID_SATURATION,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 0,
+ .reg = CHROMA_CTRL,
+ .mask = 0x00ff,
+ .shift = 0,
+ }, {
+ /* --- audio --- */
+ .v = {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },
+ .reg = PATH1_CTL1,
+ .mask = (0x1f << 24),
+ .shift = 24,
+ }, {
+ .v = {
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .name = "Volume",
+ .minimum = 0,
+ .maximum = 0x3f,
+ .step = 1,
+ .default_value = 0x3f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .reg = PATH1_VOL_CTL,
+ .mask = 0xff,
+ .shift = 0,
+ }
+};
+static const int CX231XX_CTLS = ARRAY_SIZE(cx231xx_ctls);
+
+static const u32 cx231xx_user_ctrls[] = {
+ V4L2_CID_USER_CLASS,
+ V4L2_CID_BRIGHTNESS,
+ V4L2_CID_CONTRAST,
+ V4L2_CID_SATURATION,
+ V4L2_CID_HUE,
+ V4L2_CID_AUDIO_VOLUME,
+#if 0
+ V4L2_CID_AUDIO_BALANCE,
+#endif
+ V4L2_CID_AUDIO_MUTE,
+ 0
+};
+
+static const u32 *ctrl_classes[] = {
+ cx231xx_user_ctrls,
+ NULL
+};
+
+/* ------------------------------------------------------------------
+ Video buffer and parser functions
+ ------------------------------------------------------------------*/
+
+/*
+ * Announces that a buffer were filled and request the next
+ */
+static inline void buffer_filled(struct cx231xx *dev,
+ struct cx231xx_dmaqueue *dma_q,
+ struct cx231xx_buffer *buf)
+{
+ /* Advice that buffer was filled */
+ cx231xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i);
+ buf->vb.state = VIDEOBUF_DONE;
+ buf->vb.field_count++;
+ do_gettimeofday(&buf->vb.ts);
+
+ if (dev->USE_ISO)
+ dev->video_mode.isoc_ctl.buf = NULL;
+ else
+ dev->video_mode.bulk_ctl.buf = NULL;
+
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+}
+
+static inline void print_err_status(struct cx231xx *dev, int packet, int status)
+{
+ char *errmsg = "Unknown";
+
+ switch (status) {
+ case -ENOENT:
+ errmsg = "unlinked synchronuously";
+ break;
+ case -ECONNRESET:
+ errmsg = "unlinked asynchronuously";
+ break;
+ case -ENOSR:
+ errmsg = "Buffer error (overrun)";
+ break;
+ case -EPIPE:
+ errmsg = "Stalled (device not responding)";
+ break;
+ case -EOVERFLOW:
+ errmsg = "Babble (bad cable?)";
+ break;
+ case -EPROTO:
+ errmsg = "Bit-stuff error (bad cable?)";
+ break;
+ case -EILSEQ:
+ errmsg = "CRC/Timeout (could be anything)";
+ break;
+ case -ETIME:
+ errmsg = "Device does not respond";
+ break;
+ }
+ if (packet < 0) {
+ cx231xx_isocdbg("URB status %d [%s].\n", status, errmsg);
+ } else {
+ cx231xx_isocdbg("URB packet %d, status %d [%s].\n",
+ packet, status, errmsg);
+ }
+}
+
+/*
+ * video-buf generic routine to get the next available buffer
+ */
+static inline void get_next_buf(struct cx231xx_dmaqueue *dma_q,
+ struct cx231xx_buffer **buf)
+{
+ struct cx231xx_video_mode *vmode =
+ container_of(dma_q, struct cx231xx_video_mode, vidq);
+ struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode);
+
+ char *outp;
+
+ if (list_empty(&dma_q->active)) {
+ cx231xx_isocdbg("No active queue to serve\n");
+ if (dev->USE_ISO)
+ dev->video_mode.isoc_ctl.buf = NULL;
+ else
+ dev->video_mode.bulk_ctl.buf = NULL;
+ *buf = NULL;
+ return;
+ }
+
+ /* Get the next buffer */
+ *buf = list_entry(dma_q->active.next, struct cx231xx_buffer, vb.queue);
+
+ /* Cleans up buffer - Useful for testing for frame/URB loss */
+ outp = videobuf_to_vmalloc(&(*buf)->vb);
+ memset(outp, 0, (*buf)->vb.size);
+
+ if (dev->USE_ISO)
+ dev->video_mode.isoc_ctl.buf = *buf;
+ else
+ dev->video_mode.bulk_ctl.buf = *buf;
+
+ return;
+}
+
+/*
+ * Controls the isoc copy of each urb packet
+ */
+static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb)
+{
+ struct cx231xx_buffer *buf;
+ struct cx231xx_dmaqueue *dma_q = urb->context;
+ unsigned char *outp = NULL;
+ int i, rc = 1;
+ unsigned char *p_buffer;
+ u32 bytes_parsed = 0, buffer_size = 0;
+ u8 sav_eav = 0;
+
+ if (!dev)
+ return 0;
+
+ if (dev->state & DEV_DISCONNECTED)
+ return 0;
+
+ if (urb->status < 0) {
+ print_err_status(dev, -1, urb->status);
+ if (urb->status == -ENOENT)
+ return 0;
+ }
+
+ buf = dev->video_mode.isoc_ctl.buf;
+ if (buf != NULL)
+ outp = videobuf_to_vmalloc(&buf->vb);
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ int status = urb->iso_frame_desc[i].status;
+
+ if (status < 0) {
+ print_err_status(dev, i, status);
+ if (urb->iso_frame_desc[i].status != -EPROTO)
+ continue;
+ }
+
+ if (urb->iso_frame_desc[i].actual_length <= 0) {
+ /* cx231xx_isocdbg("packet %d is empty",i); - spammy */
+ continue;
+ }
+ if (urb->iso_frame_desc[i].actual_length >
+ dev->video_mode.max_pkt_size) {
+ cx231xx_isocdbg("packet bigger than packet size");
+ continue;
+ }
+
+ /* get buffer pointer and length */
+ p_buffer = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+ buffer_size = urb->iso_frame_desc[i].actual_length;
+ bytes_parsed = 0;
+
+ if (dma_q->is_partial_line) {
+ /* Handle the case of a partial line */
+ sav_eav = dma_q->last_sav;
+ } else {
+ /* Check for a SAV/EAV overlapping
+ the buffer boundary */
+ sav_eav =
+ cx231xx_find_boundary_SAV_EAV(p_buffer,
+ dma_q->partial_buf,
+ &bytes_parsed);
+ }
+
+ sav_eav &= 0xF0;
+ /* Get the first line if we have some portion of an SAV/EAV from
+ the last buffer or a partial line */
+ if (sav_eav) {
+ bytes_parsed += cx231xx_get_video_line(dev, dma_q,
+ sav_eav, /* SAV/EAV */
+ p_buffer + bytes_parsed, /* p_buffer */
+ buffer_size - bytes_parsed);/* buf size */
+ }
+
+ /* Now parse data that is completely in this buffer */
+ /* dma_q->is_partial_line = 0; */
+
+ while (bytes_parsed < buffer_size) {
+ u32 bytes_used = 0;
+
+ sav_eav = cx231xx_find_next_SAV_EAV(
+ p_buffer + bytes_parsed, /* p_buffer */
+ buffer_size - bytes_parsed, /* buf size */
+ &bytes_used);/* bytes used to get SAV/EAV */
+
+ bytes_parsed += bytes_used;
+
+ sav_eav &= 0xF0;
+ if (sav_eav && (bytes_parsed < buffer_size)) {
+ bytes_parsed += cx231xx_get_video_line(dev,
+ dma_q, sav_eav, /* SAV/EAV */
+ p_buffer + bytes_parsed,/* p_buffer */
+ buffer_size - bytes_parsed);/*buf size*/
+ }
+ }
+
+ /* Save the last four bytes of the buffer so we can check the
+ buffer boundary condition next time */
+ memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4);
+ bytes_parsed = 0;
+
+ }
+ return rc;
+}
+
+static inline int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb)
+{
+ struct cx231xx_buffer *buf;
+ struct cx231xx_dmaqueue *dma_q = urb->context;
+ unsigned char *outp = NULL;
+ int rc = 1;
+ unsigned char *p_buffer;
+ u32 bytes_parsed = 0, buffer_size = 0;
+ u8 sav_eav = 0;
+
+ if (!dev)
+ return 0;
+
+ if (dev->state & DEV_DISCONNECTED)
+ return 0;
+
+ if (urb->status < 0) {
+ print_err_status(dev, -1, urb->status);
+ if (urb->status == -ENOENT)
+ return 0;
+ }
+
+ buf = dev->video_mode.bulk_ctl.buf;
+ if (buf != NULL)
+ outp = videobuf_to_vmalloc(&buf->vb);
+
+ if (1) {
+
+ /* get buffer pointer and length */
+ p_buffer = urb->transfer_buffer;
+ buffer_size = urb->actual_length;
+ bytes_parsed = 0;
+
+ if (dma_q->is_partial_line) {
+ /* Handle the case of a partial line */
+ sav_eav = dma_q->last_sav;
+ } else {
+ /* Check for a SAV/EAV overlapping
+ the buffer boundary */
+ sav_eav =
+ cx231xx_find_boundary_SAV_EAV(p_buffer,
+ dma_q->partial_buf,
+ &bytes_parsed);
+ }
+
+ sav_eav &= 0xF0;
+ /* Get the first line if we have some portion of an SAV/EAV from
+ the last buffer or a partial line */
+ if (sav_eav) {
+ bytes_parsed += cx231xx_get_video_line(dev, dma_q,
+ sav_eav, /* SAV/EAV */
+ p_buffer + bytes_parsed, /* p_buffer */
+ buffer_size - bytes_parsed);/* buf size */
+ }
+
+ /* Now parse data that is completely in this buffer */
+ /* dma_q->is_partial_line = 0; */
+
+ while (bytes_parsed < buffer_size) {
+ u32 bytes_used = 0;
+
+ sav_eav = cx231xx_find_next_SAV_EAV(
+ p_buffer + bytes_parsed, /* p_buffer */
+ buffer_size - bytes_parsed, /* buf size */
+ &bytes_used);/* bytes used to get SAV/EAV */
+
+ bytes_parsed += bytes_used;
+
+ sav_eav &= 0xF0;
+ if (sav_eav && (bytes_parsed < buffer_size)) {
+ bytes_parsed += cx231xx_get_video_line(dev,
+ dma_q, sav_eav, /* SAV/EAV */
+ p_buffer + bytes_parsed,/* p_buffer */
+ buffer_size - bytes_parsed);/*buf size*/
+ }
+ }
+
+ /* Save the last four bytes of the buffer so we can check the
+ buffer boundary condition next time */
+ memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4);
+ bytes_parsed = 0;
+
+ }
+ return rc;
+}
+
+
+u8 cx231xx_find_boundary_SAV_EAV(u8 *p_buffer, u8 *partial_buf,
+ u32 *p_bytes_used)
+{
+ u32 bytes_used;
+ u8 boundary_bytes[8];
+ u8 sav_eav = 0;
+
+ *p_bytes_used = 0;
+
+ /* Create an array of the last 4 bytes of the last buffer and the first
+ 4 bytes of the current buffer. */
+
+ memcpy(boundary_bytes, partial_buf, 4);
+ memcpy(boundary_bytes + 4, p_buffer, 4);
+
+ /* Check for the SAV/EAV in the boundary buffer */
+ sav_eav = cx231xx_find_next_SAV_EAV((u8 *)&boundary_bytes, 8,
+ &bytes_used);
+
+ if (sav_eav) {
+ /* found a boundary SAV/EAV. Updates the bytes used to reflect
+ only those used in the new buffer */
+ *p_bytes_used = bytes_used - 4;
+ }
+
+ return sav_eav;
+}
+
+u8 cx231xx_find_next_SAV_EAV(u8 *p_buffer, u32 buffer_size, u32 *p_bytes_used)
+{
+ u32 i;
+ u8 sav_eav = 0;
+
+ /*
+ * Don't search if the buffer size is less than 4. It causes a page
+ * fault since buffer_size - 4 evaluates to a large number in that
+ * case.
+ */
+ if (buffer_size < 4) {
+ *p_bytes_used = buffer_size;
+ return 0;
+ }
+
+ for (i = 0; i < (buffer_size - 3); i++) {
+
+ if ((p_buffer[i] == 0xFF) &&
+ (p_buffer[i + 1] == 0x00) && (p_buffer[i + 2] == 0x00)) {
+
+ *p_bytes_used = i + 4;
+ sav_eav = p_buffer[i + 3];
+ return sav_eav;
+ }
+ }
+
+ *p_bytes_used = buffer_size;
+ return 0;
+}
+
+u32 cx231xx_get_video_line(struct cx231xx *dev,
+ struct cx231xx_dmaqueue *dma_q, u8 sav_eav,
+ u8 *p_buffer, u32 buffer_size)
+{
+ u32 bytes_copied = 0;
+ int current_field = -1;
+
+ switch (sav_eav) {
+ case SAV_ACTIVE_VIDEO_FIELD1:
+ /* looking for skipped line which occurred in PAL 720x480 mode.
+ In this case, there will be no active data contained
+ between the SAV and EAV */
+ if ((buffer_size > 3) && (p_buffer[0] == 0xFF) &&
+ (p_buffer[1] == 0x00) && (p_buffer[2] == 0x00) &&
+ ((p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD1) ||
+ (p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD2) ||
+ (p_buffer[3] == EAV_VBLANK_FIELD1) ||
+ (p_buffer[3] == EAV_VBLANK_FIELD2)))
+ return bytes_copied;
+ current_field = 1;
+ break;
+
+ case SAV_ACTIVE_VIDEO_FIELD2:
+ /* looking for skipped line which occurred in PAL 720x480 mode.
+ In this case, there will be no active data contained between
+ the SAV and EAV */
+ if ((buffer_size > 3) && (p_buffer[0] == 0xFF) &&
+ (p_buffer[1] == 0x00) && (p_buffer[2] == 0x00) &&
+ ((p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD1) ||
+ (p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD2) ||
+ (p_buffer[3] == EAV_VBLANK_FIELD1) ||
+ (p_buffer[3] == EAV_VBLANK_FIELD2)))
+ return bytes_copied;
+ current_field = 2;
+ break;
+ }
+
+ dma_q->last_sav = sav_eav;
+
+ bytes_copied = cx231xx_copy_video_line(dev, dma_q, p_buffer,
+ buffer_size, current_field);
+
+ return bytes_copied;
+}
+
+u32 cx231xx_copy_video_line(struct cx231xx *dev,
+ struct cx231xx_dmaqueue *dma_q, u8 *p_line,
+ u32 length, int field_number)
+{
+ u32 bytes_to_copy;
+ struct cx231xx_buffer *buf;
+ u32 _line_size = dev->width * 2;
+
+ if (dma_q->current_field != field_number)
+ cx231xx_reset_video_buffer(dev, dma_q);
+
+ /* get the buffer pointer */
+ if (dev->USE_ISO)
+ buf = dev->video_mode.isoc_ctl.buf;
+ else
+ buf = dev->video_mode.bulk_ctl.buf;
+
+ /* Remember the field number for next time */
+ dma_q->current_field = field_number;
+
+ bytes_to_copy = dma_q->bytes_left_in_line;
+ if (bytes_to_copy > length)
+ bytes_to_copy = length;
+
+ if (dma_q->lines_completed >= dma_q->lines_per_field) {
+ dma_q->bytes_left_in_line -= bytes_to_copy;
+ dma_q->is_partial_line = (dma_q->bytes_left_in_line == 0) ?
+ 0 : 1;
+ return 0;
+ }
+
+ dma_q->is_partial_line = 1;
+
+ /* If we don't have a buffer, just return the number of bytes we would
+ have copied if we had a buffer. */
+ if (!buf) {
+ dma_q->bytes_left_in_line -= bytes_to_copy;
+ dma_q->is_partial_line = (dma_q->bytes_left_in_line == 0)
+ ? 0 : 1;
+ return bytes_to_copy;
+ }
+
+ /* copy the data to video buffer */
+ cx231xx_do_copy(dev, dma_q, p_line, bytes_to_copy);
+
+ dma_q->pos += bytes_to_copy;
+ dma_q->bytes_left_in_line -= bytes_to_copy;
+
+ if (dma_q->bytes_left_in_line == 0) {
+ dma_q->bytes_left_in_line = _line_size;
+ dma_q->lines_completed++;
+ dma_q->is_partial_line = 0;
+
+ if (cx231xx_is_buffer_done(dev, dma_q) && buf) {
+ buffer_filled(dev, dma_q, buf);
+
+ dma_q->pos = 0;
+ buf = NULL;
+ dma_q->lines_completed = 0;
+ }
+ }
+
+ return bytes_to_copy;
+}
+
+void cx231xx_reset_video_buffer(struct cx231xx *dev,
+ struct cx231xx_dmaqueue *dma_q)
+{
+ struct cx231xx_buffer *buf;
+
+ /* handle the switch from field 1 to field 2 */
+ if (dma_q->current_field == 1) {
+ if (dma_q->lines_completed >= dma_q->lines_per_field)
+ dma_q->field1_done = 1;
+ else
+ dma_q->field1_done = 0;
+ }
+
+ if (dev->USE_ISO)
+ buf = dev->video_mode.isoc_ctl.buf;
+ else
+ buf = dev->video_mode.bulk_ctl.buf;
+
+ if (buf == NULL) {
+ u8 *outp = NULL;
+ /* first try to get the buffer */
+ get_next_buf(dma_q, &buf);
+
+ if (buf)
+ outp = videobuf_to_vmalloc(&buf->vb);
+
+ dma_q->pos = 0;
+ dma_q->field1_done = 0;
+ dma_q->current_field = -1;
+ }
+
+ /* reset the counters */
+ dma_q->bytes_left_in_line = dev->width << 1;
+ dma_q->lines_completed = 0;
+}
+
+int cx231xx_do_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
+ u8 *p_buffer, u32 bytes_to_copy)
+{
+ u8 *p_out_buffer = NULL;
+ u32 current_line_bytes_copied = 0;
+ struct cx231xx_buffer *buf;
+ u32 _line_size = dev->width << 1;
+ void *startwrite;
+ int offset, lencopy;
+
+ if (dev->USE_ISO)
+ buf = dev->video_mode.isoc_ctl.buf;
+ else
+ buf = dev->video_mode.bulk_ctl.buf;
+
+ if (buf == NULL)
+ return -1;
+
+ p_out_buffer = videobuf_to_vmalloc(&buf->vb);
+
+ current_line_bytes_copied = _line_size - dma_q->bytes_left_in_line;
+
+ /* Offset field 2 one line from the top of the buffer */
+ offset = (dma_q->current_field == 1) ? 0 : _line_size;
+
+ /* Offset for field 2 */
+ startwrite = p_out_buffer + offset;
+
+ /* lines already completed in the current field */
+ startwrite += (dma_q->lines_completed * _line_size * 2);
+
+ /* bytes already completed in the current line */
+ startwrite += current_line_bytes_copied;
+
+ lencopy = dma_q->bytes_left_in_line > bytes_to_copy ?
+ bytes_to_copy : dma_q->bytes_left_in_line;
+
+ if ((u8 *)(startwrite + lencopy) > (u8 *)(p_out_buffer + buf->vb.size))
+ return 0;
+
+ /* The below copies the UYVY data straight into video buffer */
+ cx231xx_swab((u16 *) p_buffer, (u16 *) startwrite, (u16) lencopy);
+
+ return 0;
+}
+
+void cx231xx_swab(u16 *from, u16 *to, u16 len)
+{
+ u16 i;
+
+ if (len <= 0)
+ return;
+
+ for (i = 0; i < len / 2; i++)
+ to[i] = (from[i] << 8) | (from[i] >> 8);
+}
+
+u8 cx231xx_is_buffer_done(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q)
+{
+ u8 buffer_complete = 0;
+
+ /* Dual field stream */
+ buffer_complete = ((dma_q->current_field == 2) &&
+ (dma_q->lines_completed >= dma_q->lines_per_field) &&
+ dma_q->field1_done);
+
+ return buffer_complete;
+}
+
+/* ------------------------------------------------------------------
+ Videobuf operations
+ ------------------------------------------------------------------*/
+
+static int
+buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
+{
+ struct cx231xx_fh *fh = vq->priv_data;
+ struct cx231xx *dev = fh->dev;
+
+ *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7)>>3;
+ if (0 == *count)
+ *count = CX231XX_DEF_BUF;
+
+ if (*count < CX231XX_MIN_BUF)
+ *count = CX231XX_MIN_BUF;
+
+ return 0;
+}
+
+/* This is called *without* dev->slock held; please keep it that way */
+static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf)
+{
+ struct cx231xx_fh *fh = vq->priv_data;
+ struct cx231xx *dev = fh->dev;
+ unsigned long flags = 0;
+
+ if (in_interrupt())
+ BUG();
+
+ /* We used to wait for the buffer to finish here, but this didn't work
+ because, as we were keeping the state as VIDEOBUF_QUEUED,
+ videobuf_queue_cancel marked it as finished for us.
+ (Also, it could wedge forever if the hardware was misconfigured.)
+
+ This should be safe; by the time we get here, the buffer isn't
+ queued anymore. If we ever start marking the buffers as
+ VIDEOBUF_ACTIVE, it won't be, though.
+ */
+ spin_lock_irqsave(&dev->video_mode.slock, flags);
+ if (dev->USE_ISO) {
+ if (dev->video_mode.isoc_ctl.buf == buf)
+ dev->video_mode.isoc_ctl.buf = NULL;
+ } else {
+ if (dev->video_mode.bulk_ctl.buf == buf)
+ dev->video_mode.bulk_ctl.buf = NULL;
+ }
+ spin_unlock_irqrestore(&dev->video_mode.slock, flags);
+
+ videobuf_vmalloc_free(&buf->vb);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int
+buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct cx231xx_fh *fh = vq->priv_data;
+ struct cx231xx_buffer *buf =
+ container_of(vb, struct cx231xx_buffer, vb);
+ struct cx231xx *dev = fh->dev;
+ int rc = 0, urb_init = 0;
+
+ /* The only currently supported format is 16 bits/pixel */
+ buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth
+ + 7) >> 3;
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+
+ buf->vb.width = dev->width;
+ buf->vb.height = dev->height;
+ buf->vb.field = field;
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ rc = videobuf_iolock(vq, &buf->vb, NULL);
+ if (rc < 0)
+ goto fail;
+ }
+
+ if (dev->USE_ISO) {
+ if (!dev->video_mode.isoc_ctl.num_bufs)
+ urb_init = 1;
+ } else {
+ if (!dev->video_mode.bulk_ctl.num_bufs)
+ urb_init = 1;
+ }
+ /*cx231xx_info("urb_init=%d dev->video_mode.max_pkt_size=%d\n",
+ urb_init, dev->video_mode.max_pkt_size);*/
+ if (urb_init) {
+ dev->mode_tv = 0;
+ if (dev->USE_ISO)
+ rc = cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS,
+ CX231XX_NUM_BUFS,
+ dev->video_mode.max_pkt_size,
+ cx231xx_isoc_copy);
+ else
+ rc = cx231xx_init_bulk(dev, CX231XX_NUM_PACKETS,
+ CX231XX_NUM_BUFS,
+ dev->video_mode.max_pkt_size,
+ cx231xx_bulk_copy);
+ if (rc < 0)
+ goto fail;
+ }
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+fail:
+ free_buffer(vq, buf);
+ return rc;
+}
+
+static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct cx231xx_buffer *buf =
+ container_of(vb, struct cx231xx_buffer, vb);
+ struct cx231xx_fh *fh = vq->priv_data;
+ struct cx231xx *dev = fh->dev;
+ struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq;
+
+ buf->vb.state = VIDEOBUF_QUEUED;
+ list_add_tail(&buf->vb.queue, &vidq->active);
+
+}
+
+static void buffer_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct cx231xx_buffer *buf =
+ container_of(vb, struct cx231xx_buffer, vb);
+ struct cx231xx_fh *fh = vq->priv_data;
+ struct cx231xx *dev = (struct cx231xx *)fh->dev;
+
+ cx231xx_isocdbg("cx231xx: called buffer_release\n");
+
+ free_buffer(vq, buf);
+}
+
+static struct videobuf_queue_ops cx231xx_video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/********************* v4l2 interface **************************************/
+
+void video_mux(struct cx231xx *dev, int index)
+{
+ dev->video_input = index;
+ dev->ctl_ainput = INPUT(index)->amux;
+
+ cx231xx_set_video_input_mux(dev, index);
+
+ cx25840_call(dev, video, s_routing, INPUT(index)->vmux, 0, 0);
+
+ cx231xx_set_audio_input(dev, dev->ctl_ainput);
+
+ cx231xx_info("video_mux : %d\n", index);
+
+ /* do mode control overrides if required */
+ cx231xx_do_mode_ctrl_overrides(dev);
+}
+
+/* Usage lock check functions */
+static int res_get(struct cx231xx_fh *fh)
+{
+ struct cx231xx *dev = fh->dev;
+ int rc = 0;
+
+ /* This instance already has stream_on */
+ if (fh->stream_on)
+ return rc;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (dev->stream_on)
+ return -EBUSY;
+ dev->stream_on = 1;
+ } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ if (dev->vbi_stream_on)
+ return -EBUSY;
+ dev->vbi_stream_on = 1;
+ } else
+ return -EINVAL;
+
+ fh->stream_on = 1;
+
+ return rc;
+}
+
+static int res_check(struct cx231xx_fh *fh)
+{
+ return fh->stream_on;
+}
+
+static void res_free(struct cx231xx_fh *fh)
+{
+ struct cx231xx *dev = fh->dev;
+
+ fh->stream_on = 0;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ dev->stream_on = 0;
+ if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ dev->vbi_stream_on = 0;
+}
+
+static int check_dev(struct cx231xx *dev)
+{
+ if (dev->state & DEV_DISCONNECTED) {
+ cx231xx_errdev("v4l2 ioctl: device not present\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------------
+ IOCTL vidioc handling
+ ------------------------------------------------------------------*/
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+
+ f->fmt.pix.width = dev->width;
+ f->fmt.pix.height = dev->height;
+ f->fmt.pix.pixelformat = dev->format->fourcc;
+ f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3;
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * dev->height;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
+
+ return 0;
+}
+
+static struct cx231xx_fmt *format_by_fourcc(unsigned int fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(format); i++)
+ if (format[i].fourcc == fourcc)
+ return &format[i];
+
+ return NULL;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ unsigned int width = f->fmt.pix.width;
+ unsigned int height = f->fmt.pix.height;
+ unsigned int maxw = norm_maxw(dev);
+ unsigned int maxh = norm_maxh(dev);
+ struct cx231xx_fmt *fmt;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (!fmt) {
+ cx231xx_videodbg("Fourcc format (%08x) invalid.\n",
+ f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ /* width must even because of the YUYV format
+ height must be even because of interlacing */
+ v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, 1, 0);
+
+ f->fmt.pix.width = width;
+ f->fmt.pix.height = height;
+ f->fmt.pix.pixelformat = fmt->fourcc;
+ f->fmt.pix.bytesperline = (dev->width * fmt->depth + 7) >> 3;
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+ struct cx231xx_fmt *fmt;
+ struct v4l2_mbus_framefmt mbus_fmt;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ vidioc_try_fmt_vid_cap(file, priv, f);
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (!fmt)
+ return -EINVAL;
+
+ if (videobuf_queue_is_busy(&fh->vb_vidq)) {
+ cx231xx_errdev("%s queue busy\n", __func__);
+ return -EBUSY;
+ }
+
+ if (dev->stream_on && !fh->stream_on) {
+ cx231xx_errdev("%s device in use by another fh\n", __func__);
+ return -EBUSY;
+ }
+
+ /* set new image size */
+ dev->width = f->fmt.pix.width;
+ dev->height = f->fmt.pix.height;
+ dev->format = fmt;
+
+ v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED);
+ call_all(dev, video, s_mbus_fmt, &mbus_fmt);
+ v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt);
+
+ return rc;
+}
+
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+
+ *id = dev->norm;
+ return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ struct v4l2_mbus_framefmt mbus_fmt;
+ struct v4l2_format f;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ cx231xx_info("vidioc_s_std : 0x%x\n", (unsigned int)*norm);
+
+ dev->norm = *norm;
+
+ /* Adjusts width/height, if needed */
+ f.fmt.pix.width = dev->width;
+ f.fmt.pix.height = dev->height;
+ vidioc_try_fmt_vid_cap(file, priv, &f);
+
+ call_all(dev, core, s_std, dev->norm);
+
+ /* We need to reset basic properties in the decoder related to
+ resolution (since a standard change effects things like the number
+ of lines in VACT, etc) */
+ v4l2_fill_mbus_format(&mbus_fmt, &f.fmt.pix, V4L2_MBUS_FMT_FIXED);
+ call_all(dev, video, s_mbus_fmt, &mbus_fmt);
+ v4l2_fill_pix_format(&f.fmt.pix, &mbus_fmt);
+
+ /* set new image size */
+ dev->width = f.fmt.pix.width;
+ dev->height = f.fmt.pix.height;
+
+ /* do mode control overrides */
+ cx231xx_do_mode_ctrl_overrides(dev);
+
+ return 0;
+}
+
+static const char *iname[] = {
+ [CX231XX_VMUX_COMPOSITE1] = "Composite1",
+ [CX231XX_VMUX_SVIDEO] = "S-Video",
+ [CX231XX_VMUX_TELEVISION] = "Television",
+ [CX231XX_VMUX_CABLE] = "Cable TV",
+ [CX231XX_VMUX_DVB] = "DVB",
+ [CX231XX_VMUX_DEBUG] = "for debug only",
+};
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ u32 gen_stat;
+ unsigned int ret, n;
+
+ n = i->index;
+ if (n >= MAX_CX231XX_INPUT)
+ return -EINVAL;
+ if (0 == INPUT(n)->type)
+ return -EINVAL;
+
+ i->index = n;
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+
+ strcpy(i->name, iname[INPUT(n)->type]);
+
+ if ((CX231XX_VMUX_TELEVISION == INPUT(n)->type) ||
+ (CX231XX_VMUX_CABLE == INPUT(n)->type))
+ i->type = V4L2_INPUT_TYPE_TUNER;
+
+ i->std = dev->vdev->tvnorms;
+
+ /* If they are asking about the active input, read signal status */
+ if (n == dev->video_input) {
+ ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
+ GEN_STAT, 2, &gen_stat, 4);
+ if (ret > 0) {
+ if ((gen_stat & FLD_VPRES) == 0x00)
+ i->status |= V4L2_IN_ST_NO_SIGNAL;
+ if ((gen_stat & FLD_HLOCK) == 0x00)
+ i->status |= V4L2_IN_ST_NO_H_LOCK;
+ }
+ }
+
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+
+ *i = dev->video_input;
+
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ dev->mode_tv = 0;
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (i >= MAX_CX231XX_INPUT)
+ return -EINVAL;
+ if (0 == INPUT(i)->type)
+ return -EINVAL;
+
+ video_mux(dev, i);
+
+ if (INPUT(i)->type == CX231XX_VMUX_TELEVISION ||
+ INPUT(i)->type == CX231XX_VMUX_CABLE) {
+ /* There's a tuner, so reset the standard and put it on the
+ last known frequency (since it was probably powered down
+ until now */
+ call_all(dev, core, s_std, dev->norm);
+ }
+
+ return 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+
+ switch (a->index) {
+ case CX231XX_AMUX_VIDEO:
+ strcpy(a->name, "Television");
+ break;
+ case CX231XX_AMUX_LINE_IN:
+ strcpy(a->name, "Line In");
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ a->index = dev->ctl_ainput;
+ a->capability = V4L2_AUDCAP_STEREO;
+
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int status = 0;
+
+ /* Doesn't allow manual routing */
+ if (a->index != dev->ctl_ainput)
+ return -EINVAL;
+
+ dev->ctl_ainput = INPUT(a->index)->amux;
+ status = cx231xx_set_audio_input(dev, dev->ctl_ainput);
+
+ return status;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int id = qc->id;
+ int i;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ qc->id = v4l2_ctrl_next(ctrl_classes, qc->id);
+ if (unlikely(qc->id == 0))
+ return -EINVAL;
+
+ memset(qc, 0, sizeof(*qc));
+
+ qc->id = id;
+
+ if (qc->id < V4L2_CID_BASE || qc->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+
+ for (i = 0; i < CX231XX_CTLS; i++)
+ if (cx231xx_ctls[i].v.id == qc->id)
+ break;
+
+ if (i == CX231XX_CTLS) {
+ *qc = no_ctl;
+ return 0;
+ }
+ *qc = cx231xx_ctls[i].v;
+
+ call_all(dev, core, queryctrl, qc);
+
+ if (qc->type)
+ return 0;
+ else
+ return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ call_all(dev, core, g_ctrl, ctrl);
+ return rc;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ call_all(dev, core, s_ctrl, ctrl);
+ return rc;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ strcpy(t->name, "Tuner");
+
+ t->type = V4L2_TUNER_ANALOG_TV;
+ t->capability = V4L2_TUNER_CAP_NORM;
+ t->rangehigh = 0xffffffffUL;
+ t->signal = 0xffff; /* LOCKED */
+
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (0 != t->index)
+ return -EINVAL;
+#if 0
+ call_all(dev, tuner, s_tuner, t);
+#endif
+ return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+
+ f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ f->frequency = dev->ctl_freq;
+
+ call_all(dev, tuner, g_frequency, f);
+
+ return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+ u32 if_frequency = 5400000;
+
+ cx231xx_info("Enter vidioc_s_frequency()f->frequency=%d;f->type=%d\n",
+ f->frequency, f->type);
+ /*cx231xx_info("f->type: 1-radio 2-analogTV 3-digitalTV\n");*/
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (0 != f->tuner)
+ return -EINVAL;
+
+ if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV))
+ return -EINVAL;
+ if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO))
+ return -EINVAL;
+
+ /* set pre channel change settings in DIF first */
+ rc = cx231xx_tuner_pre_channel_change(dev);
+
+ dev->ctl_freq = f->frequency;
+ call_all(dev, tuner, s_frequency, f);
+
+ /* set post channel change settings in DIF first */
+ rc = cx231xx_tuner_post_channel_change(dev);
+
+ if (dev->tuner_type == TUNER_NXP_TDA18271) {
+ if (dev->norm & (V4L2_STD_MN | V4L2_STD_NTSC_443))
+ if_frequency = 5400000; /*5.4MHz */
+ else if (dev->norm & V4L2_STD_B)
+ if_frequency = 6000000; /*6.0MHz */
+ else if (dev->norm & (V4L2_STD_PAL_DK | V4L2_STD_SECAM_DK))
+ if_frequency = 6900000; /*6.9MHz */
+ else if (dev->norm & V4L2_STD_GH)
+ if_frequency = 7100000; /*7.1MHz */
+ else if (dev->norm & V4L2_STD_PAL_I)
+ if_frequency = 7250000; /*7.25MHz */
+ else if (dev->norm & V4L2_STD_SECAM_L)
+ if_frequency = 6900000; /*6.9MHz */
+ else if (dev->norm & V4L2_STD_SECAM_LC)
+ if_frequency = 1250000; /*1.25MHz */
+
+ cx231xx_info("if_frequency is set to %d\n", if_frequency);
+ cx231xx_set_Colibri_For_LowIF(dev, if_frequency, 1, 1);
+
+ update_HH_register_after_set_DIF(dev);
+ }
+
+ cx231xx_info("Set New FREQUENCY to %d\n", f->frequency);
+
+ return rc;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+
+/*
+ -R, --list-registers=type=<host/i2cdrv/i2caddr>,
+ chip=<chip>[,min=<addr>,max=<addr>]
+ dump registers from <min> to <max> [VIDIOC_DBG_G_REGISTER]
+ -r, --set-register=type=<host/i2cdrv/i2caddr>,
+ chip=<chip>,reg=<addr>,val=<val>
+ set the register [VIDIOC_DBG_S_REGISTER]
+
+ if type == host, then <chip> is the hosts chip ID (default 0)
+ if type == i2cdrv (default), then <chip> is the I2C driver name or ID
+ if type == i2caddr, then <chip> is the 7-bit I2C address
+*/
+
+static int vidioc_g_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int ret = 0;
+ u8 value[4] = { 0, 0, 0, 0 };
+ u32 data = 0;
+
+ switch (reg->match.type) {
+ case V4L2_CHIP_MATCH_HOST:
+ switch (reg->match.addr) {
+ case 0: /* Cx231xx - internal registers */
+ ret = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER,
+ (u16)reg->reg, value, 4);
+ reg->val = value[0] | value[1] << 8 |
+ value[2] << 16 | value[3] << 24;
+ break;
+ case 1: /* AFE - read byte */
+ ret = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS,
+ (u16)reg->reg, 2, &data, 1);
+ reg->val = le32_to_cpu(data & 0xff);
+ break;
+ case 14: /* AFE - read dword */
+ ret = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS,
+ (u16)reg->reg, 2, &data, 4);
+ reg->val = le32_to_cpu(data);
+ break;
+ case 2: /* Video Block - read byte */
+ ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
+ (u16)reg->reg, 2, &data, 1);
+ reg->val = le32_to_cpu(data & 0xff);
+ break;
+ case 24: /* Video Block - read dword */
+ ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
+ (u16)reg->reg, 2, &data, 4);
+ reg->val = le32_to_cpu(data);
+ break;
+ case 3: /* I2S block - read byte */
+ ret = cx231xx_read_i2c_data(dev,
+ I2S_BLK_DEVICE_ADDRESS,
+ (u16)reg->reg, 1,
+ &data, 1);
+ reg->val = le32_to_cpu(data & 0xff);
+ break;
+ case 34: /* I2S Block - read dword */
+ ret =
+ cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
+ (u16)reg->reg, 1, &data, 4);
+ reg->val = le32_to_cpu(data);
+ break;
+ }
+ return ret < 0 ? ret : 0;
+
+ case V4L2_CHIP_MATCH_I2C_DRIVER:
+ call_all(dev, core, g_register, reg);
+ return 0;
+ case V4L2_CHIP_MATCH_I2C_ADDR:/*for register debug*/
+ switch (reg->match.addr) {
+ case 0: /* Cx231xx - internal registers */
+ ret = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER,
+ (u16)reg->reg, value, 4);
+ reg->val = value[0] | value[1] << 8 |
+ value[2] << 16 | value[3] << 24;
+
+ break;
+ case 0x600:/* AFE - read byte */
+ ret = cx231xx_read_i2c_master(dev, AFE_DEVICE_ADDRESS,
+ (u16)reg->reg, 2,
+ &data, 1 , 0);
+ reg->val = le32_to_cpu(data & 0xff);
+ break;
+
+ case 0x880:/* Video Block - read byte */
+ if (reg->reg < 0x0b) {
+ ret = cx231xx_read_i2c_master(dev,
+ VID_BLK_I2C_ADDRESS,
+ (u16)reg->reg, 2,
+ &data, 1 , 0);
+ reg->val = le32_to_cpu(data & 0xff);
+ } else {
+ ret = cx231xx_read_i2c_master(dev,
+ VID_BLK_I2C_ADDRESS,
+ (u16)reg->reg, 2,
+ &data, 4 , 0);
+ reg->val = le32_to_cpu(data);
+ }
+ break;
+ case 0x980:
+ ret = cx231xx_read_i2c_master(dev,
+ I2S_BLK_DEVICE_ADDRESS,
+ (u16)reg->reg, 1,
+ &data, 1 , 0);
+ reg->val = le32_to_cpu(data & 0xff);
+ break;
+ case 0x400:
+ ret =
+ cx231xx_read_i2c_master(dev, 0x40,
+ (u16)reg->reg, 1,
+ &data, 1 , 0);
+ reg->val = le32_to_cpu(data & 0xff);
+ break;
+ case 0xc01:
+ ret =
+ cx231xx_read_i2c_master(dev, 0xc0,
+ (u16)reg->reg, 2,
+ &data, 38, 1);
+ reg->val = le32_to_cpu(data);
+ break;
+ case 0x022:
+ ret =
+ cx231xx_read_i2c_master(dev, 0x02,
+ (u16)reg->reg, 1,
+ &data, 1, 2);
+ reg->val = le32_to_cpu(data & 0xff);
+ break;
+ case 0x322:
+ ret = cx231xx_read_i2c_master(dev,
+ 0x32,
+ (u16)reg->reg, 1,
+ &data, 4 , 2);
+ reg->val = le32_to_cpu(data);
+ break;
+ case 0x342:
+ ret = cx231xx_read_i2c_master(dev,
+ 0x34,
+ (u16)reg->reg, 1,
+ &data, 4 , 2);
+ reg->val = le32_to_cpu(data);
+ break;
+
+ default:
+ cx231xx_info("no match device address!!\n");
+ break;
+ }
+ return ret < 0 ? ret : 0;
+ /*return -EINVAL;*/
+ default:
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+ }
+
+ call_all(dev, core, g_register, reg);
+
+ return ret;
+}
+
+static int vidioc_s_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int ret = 0;
+ __le64 buf;
+ u32 value;
+ u8 data[4] = { 0, 0, 0, 0 };
+
+ buf = cpu_to_le64(reg->val);
+
+ switch (reg->match.type) {
+ case V4L2_CHIP_MATCH_HOST:
+ {
+ value = (u32) buf & 0xffffffff;
+
+ switch (reg->match.addr) {
+ case 0: /* cx231xx internal registers */
+ data[0] = (u8) value;
+ data[1] = (u8) (value >> 8);
+ data[2] = (u8) (value >> 16);
+ data[3] = (u8) (value >> 24);
+ ret = cx231xx_write_ctrl_reg(dev,
+ VRT_SET_REGISTER,
+ (u16)reg->reg, data,
+ 4);
+ break;
+ case 1: /* AFE - read byte */
+ ret = cx231xx_write_i2c_data(dev,
+ AFE_DEVICE_ADDRESS,
+ (u16)reg->reg, 2,
+ value, 1);
+ break;
+ case 14: /* AFE - read dword */
+ ret = cx231xx_write_i2c_data(dev,
+ AFE_DEVICE_ADDRESS,
+ (u16)reg->reg, 2,
+ value, 4);
+ break;
+ case 2: /* Video Block - read byte */
+ ret =
+ cx231xx_write_i2c_data(dev,
+ VID_BLK_I2C_ADDRESS,
+ (u16)reg->reg, 2,
+ value, 1);
+ break;
+ case 24: /* Video Block - read dword */
+ ret =
+ cx231xx_write_i2c_data(dev,
+ VID_BLK_I2C_ADDRESS,
+ (u16)reg->reg, 2,
+ value, 4);
+ break;
+ case 3: /* I2S block - read byte */
+ ret =
+ cx231xx_write_i2c_data(dev,
+ I2S_BLK_DEVICE_ADDRESS,
+ (u16)reg->reg, 1,
+ value, 1);
+ break;
+ case 34: /* I2S block - read dword */
+ ret =
+ cx231xx_write_i2c_data(dev,
+ I2S_BLK_DEVICE_ADDRESS,
+ (u16)reg->reg, 1,
+ value, 4);
+ break;
+ }
+ }
+ return ret < 0 ? ret : 0;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ {
+ value = (u32) buf & 0xffffffff;
+
+ switch (reg->match.addr) {
+ case 0:/*cx231xx internal registers*/
+ data[0] = (u8) value;
+ data[1] = (u8) (value >> 8);
+ data[2] = (u8) (value >> 16);
+ data[3] = (u8) (value >> 24);
+ ret = cx231xx_write_ctrl_reg(dev,
+ VRT_SET_REGISTER,
+ (u16)reg->reg, data,
+ 4);
+ break;
+ case 0x600:/* AFE - read byte */
+ ret = cx231xx_write_i2c_master(dev,
+ AFE_DEVICE_ADDRESS,
+ (u16)reg->reg, 2,
+ value, 1 , 0);
+ break;
+
+ case 0x880:/* Video Block - read byte */
+ if (reg->reg < 0x0b)
+ cx231xx_write_i2c_master(dev,
+ VID_BLK_I2C_ADDRESS,
+ (u16)reg->reg, 2,
+ value, 1, 0);
+ else
+ cx231xx_write_i2c_master(dev,
+ VID_BLK_I2C_ADDRESS,
+ (u16)reg->reg, 2,
+ value, 4, 0);
+ break;
+ case 0x980:
+ ret =
+ cx231xx_write_i2c_master(dev,
+ I2S_BLK_DEVICE_ADDRESS,
+ (u16)reg->reg, 1,
+ value, 1, 0);
+ break;
+ case 0x400:
+ ret =
+ cx231xx_write_i2c_master(dev,
+ 0x40,
+ (u16)reg->reg, 1,
+ value, 1, 0);
+ break;
+ case 0xc01:
+ ret =
+ cx231xx_write_i2c_master(dev,
+ 0xc0,
+ (u16)reg->reg, 1,
+ value, 1, 1);
+ break;
+
+ case 0x022:
+ ret =
+ cx231xx_write_i2c_master(dev,
+ 0x02,
+ (u16)reg->reg, 1,
+ value, 1, 2);
+ case 0x322:
+ ret =
+ cx231xx_write_i2c_master(dev,
+ 0x32,
+ (u16)reg->reg, 1,
+ value, 4, 2);
+ break;
+
+ case 0x342:
+ ret =
+ cx231xx_write_i2c_master(dev,
+ 0x34,
+ (u16)reg->reg, 1,
+ value, 4, 2);
+ break;
+ default:
+ cx231xx_info("no match device address, "
+ "the value is %x\n", reg->match.addr);
+ break;
+
+ }
+
+ }
+ default:
+ break;
+ }
+
+ call_all(dev, core, s_register, reg);
+
+ return ret;
+}
+#endif
+
+static int vidioc_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cc)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+
+ if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ cc->bounds.left = 0;
+ cc->bounds.top = 0;
+ cc->bounds.width = dev->width;
+ cc->bounds.height = dev->height;
+ cc->defrect = cc->bounds;
+ cc->pixelaspect.numerator = 54; /* 4:3 FIXME: remove magic numbers */
+ cc->pixelaspect.denominator = 59;
+
+ return 0;
+}
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ rc = res_get(fh);
+
+ if (likely(rc >= 0))
+ rc = videobuf_streamon(&fh->vb_vidq);
+
+ call_all(dev, video, s_stream, 1);
+
+ return rc;
+}
+
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE))
+ return -EINVAL;
+ if (type != fh->type)
+ return -EINVAL;
+
+ cx25840_call(dev, video, s_stream, 0);
+
+ videobuf_streamoff(&fh->vb_vidq);
+ res_free(fh);
+
+ return 0;
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+
+ strlcpy(cap->driver, "cx231xx", sizeof(cap->driver));
+ strlcpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card));
+ usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
+
+ cap->capabilities = V4L2_CAP_VBI_CAPTURE |
+#if 0
+ V4L2_CAP_SLICED_VBI_CAPTURE |
+#endif
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_AUDIO |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+
+ if (dev->tuner_type != TUNER_ABSENT)
+ cap->capabilities |= V4L2_CAP_TUNER;
+
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (unlikely(f->index >= ARRAY_SIZE(format)))
+ return -EINVAL;
+
+ strlcpy(f->description, format[f->index].name, sizeof(f->description));
+ f->pixelformat = format[f->index].fourcc;
+
+ return 0;
+}
+
+/* Sliced VBI ioctls */
+static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ f->fmt.sliced.service_set = 0;
+
+ call_all(dev, vbi, g_sliced_fmt, &f->fmt.sliced);
+
+ if (f->fmt.sliced.service_set == 0)
+ rc = -EINVAL;
+
+ return rc;
+}
+
+static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ call_all(dev, vbi, g_sliced_fmt, &f->fmt.sliced);
+
+ if (f->fmt.sliced.service_set == 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+/* RAW VBI ioctls */
+
+static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ f->fmt.vbi.sampling_rate = 6750000 * 4;
+ f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
+ f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+ f->fmt.vbi.offset = 0;
+ f->fmt.vbi.start[0] = (dev->norm & V4L2_STD_625_50) ?
+ PAL_VBI_START_LINE : NTSC_VBI_START_LINE;
+ f->fmt.vbi.count[0] = (dev->norm & V4L2_STD_625_50) ?
+ PAL_VBI_LINES : NTSC_VBI_LINES;
+ f->fmt.vbi.start[1] = (dev->norm & V4L2_STD_625_50) ?
+ PAL_VBI_START_LINE + 312 : NTSC_VBI_START_LINE + 263;
+ f->fmt.vbi.count[1] = f->fmt.vbi.count[0];
+
+ return 0;
+
+}
+
+static int vidioc_try_fmt_vbi_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+
+ if (dev->vbi_stream_on && !fh->stream_on) {
+ cx231xx_errdev("%s device in use by another fh\n", __func__);
+ return -EBUSY;
+ }
+
+ f->type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ f->fmt.vbi.sampling_rate = 6750000 * 4;
+ f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
+ f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+ f->fmt.vbi.offset = 0;
+ f->fmt.vbi.flags = 0;
+ f->fmt.vbi.start[0] = (dev->norm & V4L2_STD_625_50) ?
+ PAL_VBI_START_LINE : NTSC_VBI_START_LINE;
+ f->fmt.vbi.count[0] = (dev->norm & V4L2_STD_625_50) ?
+ PAL_VBI_LINES : NTSC_VBI_LINES;
+ f->fmt.vbi.start[1] = (dev->norm & V4L2_STD_625_50) ?
+ PAL_VBI_START_LINE + 312 : NTSC_VBI_START_LINE + 263;
+ f->fmt.vbi.count[1] = f->fmt.vbi.count[0];
+
+ return 0;
+
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *rb)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ return videobuf_reqbufs(&fh->vb_vidq, rb);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ return videobuf_querybuf(&fh->vb_vidq, b);
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ return videobuf_qbuf(&fh->vb_vidq, b);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK);
+}
+
+/* ----------------------------------------------------------- */
+/* RADIO ESPECIFIC IOCTLS */
+/* ----------------------------------------------------------- */
+
+static int radio_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev;
+
+ strlcpy(cap->driver, "cx231xx", sizeof(cap->driver));
+ strlcpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card));
+ usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
+
+ cap->capabilities = V4L2_CAP_TUNER;
+ return 0;
+}
+
+static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
+{
+ struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev;
+
+ if (unlikely(t->index > 0))
+ return -EINVAL;
+
+ strcpy(t->name, "Radio");
+ t->type = V4L2_TUNER_RADIO;
+
+ call_all(dev, tuner, s_tuner, t);
+
+ return 0;
+}
+
+static int radio_enum_input(struct file *file, void *priv, struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
+ strcpy(i->name, "Radio");
+ i->type = V4L2_INPUT_TYPE_TUNER;
+
+ return 0;
+}
+
+static int radio_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ if (unlikely(a->index))
+ return -EINVAL;
+
+ strcpy(a->name, "Radio");
+ return 0;
+}
+
+static int radio_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
+{
+ struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ call_all(dev, tuner, s_tuner, t);
+
+ return 0;
+}
+
+static int radio_s_audio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+ return 0;
+}
+
+static int radio_s_input(struct file *file, void *fh, unsigned int i)
+{
+ return 0;
+}
+
+static int radio_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ int i;
+
+ if (c->id < V4L2_CID_BASE || c->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+ if (c->id == V4L2_CID_AUDIO_MUTE) {
+ for (i = 0; i < CX231XX_CTLS; i++) {
+ if (cx231xx_ctls[i].v.id == c->id)
+ break;
+ }
+ if (i == CX231XX_CTLS)
+ return -EINVAL;
+ *c = cx231xx_ctls[i].v;
+ } else
+ *c = no_ctl;
+ return 0;
+}
+
+/*
+ * cx231xx_v4l2_open()
+ * inits the device and starts isoc transfer
+ */
+static int cx231xx_v4l2_open(struct file *filp)
+{
+ int errCode = 0, radio = 0;
+ struct video_device *vdev = video_devdata(filp);
+ struct cx231xx *dev = video_drvdata(filp);
+ struct cx231xx_fh *fh;
+ enum v4l2_buf_type fh_type = 0;
+
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
+ fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ break;
+ case VFL_TYPE_VBI:
+ fh_type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ break;
+ case VFL_TYPE_RADIO:
+ radio = 1;
+ break;
+ }
+
+ cx231xx_videodbg("open dev=%s type=%s users=%d\n",
+ video_device_node_name(vdev), v4l2_type_names[fh_type],
+ dev->users);
+
+#if 0
+ errCode = cx231xx_set_mode(dev, CX231XX_ANALOG_MODE);
+ if (errCode < 0) {
+ cx231xx_errdev
+ ("Device locked on digital mode. Can't open analog\n");
+ return -EBUSY;
+ }
+#endif
+
+ fh = kzalloc(sizeof(struct cx231xx_fh), GFP_KERNEL);
+ if (!fh) {
+ cx231xx_errdev("cx231xx-video.c: Out of memory?!\n");
+ return -ENOMEM;
+ }
+ fh->dev = dev;
+ fh->radio = radio;
+ fh->type = fh_type;
+ filp->private_data = fh;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
+ dev->width = norm_maxw(dev);
+ dev->height = norm_maxh(dev);
+
+ /* Power up in Analog TV mode */
+ if (dev->board.external_av)
+ cx231xx_set_power_mode(dev,
+ POLARIS_AVMODE_ENXTERNAL_AV);
+ else
+ cx231xx_set_power_mode(dev, POLARIS_AVMODE_ANALOGT_TV);
+
+#if 0
+ cx231xx_set_mode(dev, CX231XX_ANALOG_MODE);
+#endif
+
+ /* set video alternate setting */
+ cx231xx_set_video_alternate(dev);
+
+ /* Needed, since GPIO might have disabled power of
+ some i2c device */
+ cx231xx_config_i2c(dev);
+
+ /* device needs to be initialized before isoc transfer */
+ dev->video_input = dev->video_input > 2 ? 2 : dev->video_input;
+
+ }
+ if (fh->radio) {
+ cx231xx_videodbg("video_open: setting radio device\n");
+
+ /* cx231xx_start_radio(dev); */
+
+ call_all(dev, tuner, s_radio);
+ }
+
+ dev->users++;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ videobuf_queue_vmalloc_init(&fh->vb_vidq, &cx231xx_video_qops,
+ NULL, &dev->video_mode.slock,
+ fh->type, V4L2_FIELD_INTERLACED,
+ sizeof(struct cx231xx_buffer),
+ fh, &dev->lock);
+ if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ /* Set the required alternate setting VBI interface works in
+ Bulk mode only */
+ cx231xx_set_alt_setting(dev, INDEX_VANC, 0);
+
+ videobuf_queue_vmalloc_init(&fh->vb_vidq, &cx231xx_vbi_qops,
+ NULL, &dev->vbi_mode.slock,
+ fh->type, V4L2_FIELD_SEQ_TB,
+ sizeof(struct cx231xx_buffer),
+ fh, &dev->lock);
+ }
+
+ return errCode;
+}
+
+/*
+ * cx231xx_realease_resources()
+ * unregisters the v4l2,i2c and usb devices
+ * called when the device gets disconected or at module unload
+*/
+void cx231xx_release_analog_resources(struct cx231xx *dev)
+{
+
+ /*FIXME: I2C IR should be disconnected */
+
+ if (dev->radio_dev) {
+ if (video_is_registered(dev->radio_dev))
+ video_unregister_device(dev->radio_dev);
+ else
+ video_device_release(dev->radio_dev);
+ dev->radio_dev = NULL;
+ }
+ if (dev->vbi_dev) {
+ cx231xx_info("V4L2 device %s deregistered\n",
+ video_device_node_name(dev->vbi_dev));
+ if (video_is_registered(dev->vbi_dev))
+ video_unregister_device(dev->vbi_dev);
+ else
+ video_device_release(dev->vbi_dev);
+ dev->vbi_dev = NULL;
+ }
+ if (dev->vdev) {
+ cx231xx_info("V4L2 device %s deregistered\n",
+ video_device_node_name(dev->vdev));
+
+ if (dev->board.has_417)
+ cx231xx_417_unregister(dev);
+
+ if (video_is_registered(dev->vdev))
+ video_unregister_device(dev->vdev);
+ else
+ video_device_release(dev->vdev);
+ dev->vdev = NULL;
+ }
+}
+
+/*
+ * cx231xx_v4l2_close()
+ * stops streaming and deallocates all resources allocated by the v4l2
+ * calls and ioctls
+ */
+static int cx231xx_v4l2_close(struct file *filp)
+{
+ struct cx231xx_fh *fh = filp->private_data;
+ struct cx231xx *dev = fh->dev;
+
+ cx231xx_videodbg("users=%d\n", dev->users);
+
+ cx231xx_videodbg("users=%d\n", dev->users);
+ if (res_check(fh))
+ res_free(fh);
+
+ /*
+ * To workaround error number=-71 on EP0 for VideoGrabber,
+ * need exclude following.
+ * FIXME: It is probably safe to remove most of these, as we're
+ * now avoiding the alternate setting for INDEX_VANC
+ */
+ if (!dev->board.no_alt_vanc)
+ if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ videobuf_stop(&fh->vb_vidq);
+ videobuf_mmap_free(&fh->vb_vidq);
+
+ /* the device is already disconnect,
+ free the remaining resources */
+ if (dev->state & DEV_DISCONNECTED) {
+ if (atomic_read(&dev->devlist_count) > 0) {
+ cx231xx_release_resources(dev);
+ fh->dev = NULL;
+ return 0;
+ }
+ return 0;
+ }
+
+ /* do this before setting alternate! */
+ cx231xx_uninit_vbi_isoc(dev);
+
+ /* set alternate 0 */
+ if (!dev->vbi_or_sliced_cc_mode)
+ cx231xx_set_alt_setting(dev, INDEX_VANC, 0);
+ else
+ cx231xx_set_alt_setting(dev, INDEX_HANC, 0);
+
+ kfree(fh);
+ dev->users--;
+ wake_up_interruptible_nr(&dev->open, 1);
+ return 0;
+ }
+
+ dev->users--;
+ if (!dev->users) {
+ videobuf_stop(&fh->vb_vidq);
+ videobuf_mmap_free(&fh->vb_vidq);
+
+ /* the device is already disconnect,
+ free the remaining resources */
+ if (dev->state & DEV_DISCONNECTED) {
+ cx231xx_release_resources(dev);
+ fh->dev = NULL;
+ return 0;
+ }
+
+ /* Save some power by putting tuner to sleep */
+ call_all(dev, core, s_power, 0);
+
+ /* do this before setting alternate! */
+ if (dev->USE_ISO)
+ cx231xx_uninit_isoc(dev);
+ else
+ cx231xx_uninit_bulk(dev);
+ cx231xx_set_mode(dev, CX231XX_SUSPEND);
+
+ /* set alternate 0 */
+ cx231xx_set_alt_setting(dev, INDEX_VIDEO, 0);
+ }
+ kfree(fh);
+ wake_up_interruptible_nr(&dev->open, 1);
+ return 0;
+}
+
+/*
+ * cx231xx_v4l2_read()
+ * will allocate buffers when called for the first time
+ */
+static ssize_t
+cx231xx_v4l2_read(struct file *filp, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct cx231xx_fh *fh = filp->private_data;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if ((fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+ (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)) {
+ rc = res_get(fh);
+
+ if (unlikely(rc < 0))
+ return rc;
+
+ return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0,
+ filp->f_flags & O_NONBLOCK);
+ }
+ return 0;
+}
+
+/*
+ * cx231xx_v4l2_poll()
+ * will allocate buffers when called for the first time
+ */
+static unsigned int cx231xx_v4l2_poll(struct file *filp, poll_table *wait)
+{
+ struct cx231xx_fh *fh = filp->private_data;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ rc = res_get(fh);
+
+ if (unlikely(rc < 0))
+ return POLLERR;
+
+ if ((V4L2_BUF_TYPE_VIDEO_CAPTURE == fh->type) ||
+ (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type))
+ return videobuf_poll_stream(filp, &fh->vb_vidq, wait);
+ else
+ return POLLERR;
+}
+
+/*
+ * cx231xx_v4l2_mmap()
+ */
+static int cx231xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct cx231xx_fh *fh = filp->private_data;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ rc = res_get(fh);
+
+ if (unlikely(rc < 0))
+ return rc;
+
+ rc = videobuf_mmap_mapper(&fh->vb_vidq, vma);
+
+ cx231xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n",
+ (unsigned long)vma->vm_start,
+ (unsigned long)vma->vm_end -
+ (unsigned long)vma->vm_start, rc);
+
+ return rc;
+}
+
+static const struct v4l2_file_operations cx231xx_v4l_fops = {
+ .owner = THIS_MODULE,
+ .open = cx231xx_v4l2_open,
+ .release = cx231xx_v4l2_close,
+ .read = cx231xx_v4l2_read,
+ .poll = cx231xx_v4l2_poll,
+ .mmap = cx231xx_v4l2_mmap,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops video_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
+ .vidioc_try_fmt_vbi_cap = vidioc_try_fmt_vbi_cap,
+ .vidioc_s_fmt_vbi_cap = vidioc_try_fmt_vbi_cap,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_cropcap = vidioc_cropcap,
+ .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap,
+ .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+#endif
+};
+
+static struct video_device cx231xx_vbi_template;
+
+static const struct video_device cx231xx_video_template = {
+ .fops = &cx231xx_v4l_fops,
+ .release = video_device_release,
+ .ioctl_ops = &video_ioctl_ops,
+ .tvnorms = V4L2_STD_ALL,
+ .current_norm = V4L2_STD_PAL,
+};
+
+static const struct v4l2_file_operations radio_fops = {
+ .owner = THIS_MODULE,
+ .open = cx231xx_v4l2_open,
+ .release = cx231xx_v4l2_close,
+ .ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops radio_ioctl_ops = {
+ .vidioc_querycap = radio_querycap,
+ .vidioc_g_tuner = radio_g_tuner,
+ .vidioc_enum_input = radio_enum_input,
+ .vidioc_g_audio = radio_g_audio,
+ .vidioc_s_tuner = radio_s_tuner,
+ .vidioc_s_audio = radio_s_audio,
+ .vidioc_s_input = radio_s_input,
+ .vidioc_queryctrl = radio_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+#endif
+};
+
+static struct video_device cx231xx_radio_template = {
+ .name = "cx231xx-radio",
+ .fops = &radio_fops,
+ .ioctl_ops = &radio_ioctl_ops,
+};
+
+/******************************** usb interface ******************************/
+
+static struct video_device *cx231xx_vdev_init(struct cx231xx *dev,
+ const struct video_device
+ *template, const char *type_name)
+{
+ struct video_device *vfd;
+
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+
+ *vfd = *template;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+ vfd->release = video_device_release;
+ vfd->debug = video_debug;
+ vfd->lock = &dev->lock;
+
+ snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name);
+
+ video_set_drvdata(vfd, dev);
+ return vfd;
+}
+
+int cx231xx_register_analog_devices(struct cx231xx *dev)
+{
+ int ret;
+
+ cx231xx_info("%s: v4l2 driver version %s\n",
+ dev->name, CX231XX_VERSION);
+
+ /* set default norm */
+ /*dev->norm = cx231xx_video_template.current_norm; */
+ dev->width = norm_maxw(dev);
+ dev->height = norm_maxh(dev);
+ dev->interlaced = 0;
+
+ /* Analog specific initialization */
+ dev->format = &format[0];
+
+ /* Set the initial input */
+ video_mux(dev, dev->video_input);
+
+ /* Audio defaults */
+ dev->mute = 1;
+ dev->volume = 0x1f;
+
+ /* enable vbi capturing */
+ /* write code here... */
+
+ /* allocate and fill video video_device struct */
+ dev->vdev = cx231xx_vdev_init(dev, &cx231xx_video_template, "video");
+ if (!dev->vdev) {
+ cx231xx_errdev("cannot allocate video_device.\n");
+ return -ENODEV;
+ }
+
+ /* register v4l2 video video_device */
+ ret = video_register_device(dev->vdev, VFL_TYPE_GRABBER,
+ video_nr[dev->devno]);
+ if (ret) {
+ cx231xx_errdev("unable to register video device (error=%i).\n",
+ ret);
+ return ret;
+ }
+
+ cx231xx_info("%s/0: registered device %s [v4l2]\n",
+ dev->name, video_device_node_name(dev->vdev));
+
+ /* Initialize VBI template */
+ memcpy(&cx231xx_vbi_template, &cx231xx_video_template,
+ sizeof(cx231xx_vbi_template));
+ strcpy(cx231xx_vbi_template.name, "cx231xx-vbi");
+
+ /* Allocate and fill vbi video_device struct */
+ dev->vbi_dev = cx231xx_vdev_init(dev, &cx231xx_vbi_template, "vbi");
+
+ /* register v4l2 vbi video_device */
+ ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
+ vbi_nr[dev->devno]);
+ if (ret < 0) {
+ cx231xx_errdev("unable to register vbi device\n");
+ return ret;
+ }
+
+ cx231xx_info("%s/0: registered device %s\n",
+ dev->name, video_device_node_name(dev->vbi_dev));
+
+ if (cx231xx_boards[dev->model].radio.type == CX231XX_RADIO) {
+ dev->radio_dev = cx231xx_vdev_init(dev, &cx231xx_radio_template,
+ "radio");
+ if (!dev->radio_dev) {
+ cx231xx_errdev("cannot allocate video_device.\n");
+ return -ENODEV;
+ }
+ ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO,
+ radio_nr[dev->devno]);
+ if (ret < 0) {
+ cx231xx_errdev("can't register radio device\n");
+ return ret;
+ }
+ cx231xx_info("Registered radio device as %s\n",
+ video_device_node_name(dev->radio_dev));
+ }
+
+ cx231xx_info("V4L2 device registered as %s and %s\n",
+ video_device_node_name(dev->vdev),
+ video_device_node_name(dev->vbi_dev));
+
+ return 0;
+}
diff --git a/drivers/media/video/cx231xx/cx231xx.h b/drivers/media/video/cx231xx/cx231xx.h
new file mode 100644
index 00000000..e1744755
--- /dev/null
+++ b/drivers/media/video/cx231xx/cx231xx.h
@@ -0,0 +1,1014 @@
+/*
+ cx231xx.h - driver for Conexant Cx23100/101/102 USB video capture devices
+
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
+ Based on em28xx driver
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _CX231XX_H
+#define _CX231XX_H
+
+#include <linux/videodev2.h>
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+
+#include <media/cx2341x.h>
+
+#include <media/videobuf-vmalloc.h>
+#include <media/v4l2-device.h>
+#include <media/rc-core.h>
+#include <media/ir-kbd-i2c.h>
+#include <media/videobuf-dvb.h>
+
+#include "cx231xx-reg.h"
+#include "cx231xx-pcb-cfg.h"
+#include "cx231xx-conf-reg.h"
+
+#define DRIVER_NAME "cx231xx"
+#define PWR_SLEEP_INTERVAL 10
+
+/* I2C addresses for control block in Cx231xx */
+#define AFE_DEVICE_ADDRESS 0x60
+#define I2S_BLK_DEVICE_ADDRESS 0x98
+#define VID_BLK_I2C_ADDRESS 0x88
+#define VERVE_I2C_ADDRESS 0x40
+#define DIF_USE_BASEBAND 0xFFFFFFFF
+
+/* Boards supported by driver */
+#define CX231XX_BOARD_UNKNOWN 0
+#define CX231XX_BOARD_CNXT_CARRAERA 1
+#define CX231XX_BOARD_CNXT_SHELBY 2
+#define CX231XX_BOARD_CNXT_RDE_253S 3
+#define CX231XX_BOARD_CNXT_RDU_253S 4
+#define CX231XX_BOARD_CNXT_VIDEO_GRABBER 5
+#define CX231XX_BOARD_CNXT_RDE_250 6
+#define CX231XX_BOARD_CNXT_RDU_250 7
+#define CX231XX_BOARD_HAUPPAUGE_EXETER 8
+#define CX231XX_BOARD_HAUPPAUGE_USBLIVE2 9
+#define CX231XX_BOARD_PV_PLAYTV_USB_HYBRID 10
+#define CX231XX_BOARD_PV_XCAPTURE_USB 11
+#define CX231XX_BOARD_KWORLD_UB430_USB_HYBRID 12
+#define CX231XX_BOARD_ICONBIT_U100 13
+#define CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL 14
+#define CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC 15
+
+/* Limits minimum and default number of buffers */
+#define CX231XX_MIN_BUF 4
+#define CX231XX_DEF_BUF 12
+#define CX231XX_DEF_VBI_BUF 6
+
+#define VBI_LINE_COUNT 17
+#define VBI_LINE_LENGTH 1440
+
+/*Limits the max URB message size */
+#define URB_MAX_CTRL_SIZE 80
+
+/* Params for validated field */
+#define CX231XX_BOARD_NOT_VALIDATED 1
+#define CX231XX_BOARD_VALIDATED 0
+
+/* maximum number of cx231xx boards */
+#define CX231XX_MAXBOARDS 8
+
+/* maximum number of frames that can be queued */
+#define CX231XX_NUM_FRAMES 5
+
+/* number of buffers for isoc transfers */
+#define CX231XX_NUM_BUFS 8
+
+/* number of packets for each buffer
+ windows requests only 40 packets .. so we better do the same
+ this is what I found out for all alternate numbers there!
+ */
+#define CX231XX_NUM_PACKETS 40
+
+/* default alternate; 0 means choose the best */
+#define CX231XX_PINOUT 0
+
+#define CX231XX_INTERLACED_DEFAULT 1
+
+/* time to wait when stopping the isoc transfer */
+#define CX231XX_URB_TIMEOUT \
+ msecs_to_jiffies(CX231XX_NUM_BUFS * CX231XX_NUM_PACKETS)
+
+#define CX231xx_NORMS (\
+ V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_JP | V4L2_STD_NTSC_443 | \
+ V4L2_STD_PAL_BG | V4L2_STD_PAL_DK | V4L2_STD_PAL_I | \
+ V4L2_STD_PAL_M | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | \
+ V4L2_STD_PAL_60 | V4L2_STD_SECAM_L | V4L2_STD_SECAM_DK)
+
+#define SLEEP_S5H1432 30
+#define CX23417_OSC_EN 8
+#define CX23417_RESET 9
+
+struct cx23417_fmt {
+ char *name;
+ u32 fourcc; /* v4l2 format id */
+ int depth;
+ int flags;
+ u32 cxformat;
+};
+enum cx231xx_mode {
+ CX231XX_SUSPEND,
+ CX231XX_ANALOG_MODE,
+ CX231XX_DIGITAL_MODE,
+};
+
+enum cx231xx_std_mode {
+ CX231XX_TV_AIR = 0,
+ CX231XX_TV_CABLE
+};
+
+enum cx231xx_stream_state {
+ STREAM_OFF,
+ STREAM_INTERRUPT,
+ STREAM_ON,
+};
+
+struct cx231xx;
+
+struct cx231xx_isoc_ctl {
+ /* max packet size of isoc transaction */
+ int max_pkt_size;
+
+ /* number of allocated urbs */
+ int num_bufs;
+
+ /* urb for isoc transfers */
+ struct urb **urb;
+
+ /* transfer buffers for isoc transfer */
+ char **transfer_buffer;
+
+ /* Last buffer command and region */
+ u8 cmd;
+ int pos, size, pktsize;
+
+ /* Last field: ODD or EVEN? */
+ int field;
+
+ /* Stores incomplete commands */
+ u32 tmp_buf;
+ int tmp_buf_len;
+
+ /* Stores already requested buffers */
+ struct cx231xx_buffer *buf;
+
+ /* Stores the number of received fields */
+ int nfields;
+
+ /* isoc urb callback */
+ int (*isoc_copy) (struct cx231xx *dev, struct urb *urb);
+};
+
+struct cx231xx_bulk_ctl {
+ /* max packet size of bulk transaction */
+ int max_pkt_size;
+
+ /* number of allocated urbs */
+ int num_bufs;
+
+ /* urb for bulk transfers */
+ struct urb **urb;
+
+ /* transfer buffers for bulk transfer */
+ char **transfer_buffer;
+
+ /* Last buffer command and region */
+ u8 cmd;
+ int pos, size, pktsize;
+
+ /* Last field: ODD or EVEN? */
+ int field;
+
+ /* Stores incomplete commands */
+ u32 tmp_buf;
+ int tmp_buf_len;
+
+ /* Stores already requested buffers */
+ struct cx231xx_buffer *buf;
+
+ /* Stores the number of received fields */
+ int nfields;
+
+ /* bulk urb callback */
+ int (*bulk_copy) (struct cx231xx *dev, struct urb *urb);
+};
+
+struct cx231xx_fmt {
+ char *name;
+ u32 fourcc; /* v4l2 format id */
+ int depth;
+ int reg;
+};
+
+/* buffer for one video frame */
+struct cx231xx_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+
+ struct list_head frame;
+ int top_field;
+ int receiving;
+};
+
+enum ps_package_head {
+ CX231XX_NEED_ADD_PS_PACKAGE_HEAD = 0,
+ CX231XX_NONEED_PS_PACKAGE_HEAD
+};
+
+struct cx231xx_dmaqueue {
+ struct list_head active;
+ struct list_head queued;
+
+ wait_queue_head_t wq;
+
+ /* Counters to control buffer fill */
+ int pos;
+ u8 is_partial_line;
+ u8 partial_buf[8];
+ u8 last_sav;
+ int current_field;
+ u32 bytes_left_in_line;
+ u32 lines_completed;
+ u8 field1_done;
+ u32 lines_per_field;
+
+ /*Mpeg2 control buffer*/
+ u8 *p_left_data;
+ u32 left_data_count;
+ u8 mpeg_buffer_done;
+ u32 mpeg_buffer_completed;
+ enum ps_package_head add_ps_package_head;
+ char ps_head[10];
+};
+
+/* inputs */
+
+#define MAX_CX231XX_INPUT 4
+
+enum cx231xx_itype {
+ CX231XX_VMUX_COMPOSITE1 = 1,
+ CX231XX_VMUX_SVIDEO,
+ CX231XX_VMUX_TELEVISION,
+ CX231XX_VMUX_CABLE,
+ CX231XX_RADIO,
+ CX231XX_VMUX_DVB,
+ CX231XX_VMUX_DEBUG
+};
+
+enum cx231xx_v_input {
+ CX231XX_VIN_1_1 = 0x1,
+ CX231XX_VIN_2_1,
+ CX231XX_VIN_3_1,
+ CX231XX_VIN_4_1,
+ CX231XX_VIN_1_2 = 0x01,
+ CX231XX_VIN_2_2,
+ CX231XX_VIN_3_2,
+ CX231XX_VIN_1_3 = 0x1,
+ CX231XX_VIN_2_3,
+ CX231XX_VIN_3_3,
+};
+
+/* cx231xx has two audio inputs: tuner and line in */
+enum cx231xx_amux {
+ /* This is the only entry for cx231xx tuner input */
+ CX231XX_AMUX_VIDEO, /* cx231xx tuner */
+ CX231XX_AMUX_LINE_IN, /* Line In */
+};
+
+struct cx231xx_reg_seq {
+ unsigned char bit;
+ unsigned char val;
+ int sleep;
+};
+
+struct cx231xx_input {
+ enum cx231xx_itype type;
+ unsigned int vmux;
+ enum cx231xx_amux amux;
+ struct cx231xx_reg_seq *gpio;
+};
+
+#define INPUT(nr) (&cx231xx_boards[dev->model].input[nr])
+
+enum cx231xx_decoder {
+ CX231XX_NODECODER,
+ CX231XX_AVDECODER
+};
+
+enum CX231XX_I2C_MASTER_PORT {
+ I2C_0 = 0,
+ I2C_1 = 1,
+ I2C_2 = 2,
+ I2C_3 = 3
+};
+
+struct cx231xx_board {
+ char *name;
+ int vchannels;
+ int tuner_type;
+ int tuner_addr;
+ v4l2_std_id norm; /* tv norm */
+
+ /* demod related */
+ int demod_addr;
+ u8 demod_xfer_mode; /* 0 - Serial; 1 - parallel */
+
+ /* GPIO Pins */
+ struct cx231xx_reg_seq *dvb_gpio;
+ struct cx231xx_reg_seq *suspend_gpio;
+ struct cx231xx_reg_seq *tuner_gpio;
+ /* Negative means don't use it */
+ s8 tuner_sif_gpio;
+ s8 tuner_scl_gpio;
+ s8 tuner_sda_gpio;
+
+ /* PIN ctrl */
+ u32 ctl_pin_status_mask;
+ u8 agc_analog_digital_select_gpio;
+ u32 gpio_pin_status_mask;
+
+ /* i2c masters */
+ u8 tuner_i2c_master;
+ u8 demod_i2c_master;
+ u8 ir_i2c_master;
+
+ /* for devices with I2C chips for IR */
+ char *rc_map_name;
+
+ unsigned int max_range_640_480:1;
+ unsigned int has_dvb:1;
+ unsigned int has_417:1;
+ unsigned int valid:1;
+ unsigned int no_alt_vanc:1;
+ unsigned int external_av:1;
+ unsigned int dont_use_port_3:1;
+
+ unsigned char xclk, i2c_speed;
+
+ enum cx231xx_decoder decoder;
+ int output_mode;
+
+ struct cx231xx_input input[MAX_CX231XX_INPUT];
+ struct cx231xx_input radio;
+ struct rc_map *ir_codes;
+};
+
+/* device states */
+enum cx231xx_dev_state {
+ DEV_INITIALIZED = 0x01,
+ DEV_DISCONNECTED = 0x02,
+};
+
+enum AFE_MODE {
+ AFE_MODE_LOW_IF,
+ AFE_MODE_BASEBAND,
+ AFE_MODE_EU_HI_IF,
+ AFE_MODE_US_HI_IF,
+ AFE_MODE_JAPAN_HI_IF
+};
+
+enum AUDIO_INPUT {
+ AUDIO_INPUT_MUTE,
+ AUDIO_INPUT_LINE,
+ AUDIO_INPUT_TUNER_TV,
+ AUDIO_INPUT_SPDIF,
+ AUDIO_INPUT_TUNER_FM
+};
+
+#define CX231XX_AUDIO_BUFS 5
+#define CX231XX_NUM_AUDIO_PACKETS 16
+#define CX231XX_ISO_NUM_AUDIO_PACKETS 64
+
+/* cx231xx extensions */
+#define CX231XX_AUDIO 0x10
+#define CX231XX_DVB 0x20
+
+struct cx231xx_audio {
+ char name[50];
+ char *transfer_buffer[CX231XX_AUDIO_BUFS];
+ struct urb *urb[CX231XX_AUDIO_BUFS];
+ struct usb_device *udev;
+ unsigned int capture_transfer_done;
+ struct snd_pcm_substream *capture_pcm_substream;
+
+ unsigned int hwptr_done_capture;
+ struct snd_card *sndcard;
+
+ int users, shutdown;
+ /* locks */
+ spinlock_t slock;
+
+ int alt; /* alternate */
+ int max_pkt_size; /* max packet size of isoc transaction */
+ int num_alt; /* Number of alternative settings */
+ unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */
+ u16 end_point_addr;
+};
+
+struct cx231xx;
+
+struct cx231xx_fh {
+ struct cx231xx *dev;
+ unsigned int stream_on:1; /* Locks streams */
+ int radio;
+
+ struct videobuf_queue vb_vidq;
+
+ enum v4l2_buf_type type;
+
+
+
+/*following is copyed from cx23885.h*/
+ u32 resources;
+
+ /* video overlay */
+ struct v4l2_window win;
+ struct v4l2_clip *clips;
+ unsigned int nclips;
+
+ /* video capture */
+ struct cx23417_fmt *fmt;
+ unsigned int width, height;
+
+ /* vbi capture */
+ struct videobuf_queue vidq;
+ struct videobuf_queue vbiq;
+
+ /* MPEG Encoder specifics ONLY */
+
+ atomic_t v4l_reading;
+};
+
+/*****************************************************************/
+/* set/get i2c */
+/* 00--1Mb/s, 01-400kb/s, 10--100kb/s, 11--5Mb/s */
+#define I2C_SPEED_1M 0x0
+#define I2C_SPEED_400K 0x1
+#define I2C_SPEED_100K 0x2
+#define I2C_SPEED_5M 0x3
+
+/* 0-- STOP transaction */
+#define I2C_STOP 0x0
+/* 1-- do not transmit STOP at end of transaction */
+#define I2C_NOSTOP 0x1
+/* 1--allow slave to insert clock wait states */
+#define I2C_SYNC 0x1
+
+struct cx231xx_i2c {
+ struct cx231xx *dev;
+
+ int nr;
+
+ /* i2c i/o */
+ struct i2c_adapter i2c_adap;
+ struct i2c_algo_bit_data i2c_algo;
+ struct i2c_client i2c_client;
+ u32 i2c_rc;
+
+ /* different settings for each bus */
+ u8 i2c_period;
+ u8 i2c_nostop;
+ u8 i2c_reserve;
+};
+
+struct cx231xx_i2c_xfer_data {
+ u8 dev_addr;
+ u8 direction; /* 1 - IN, 0 - OUT */
+ u8 saddr_len; /* sub address len */
+ u16 saddr_dat; /* sub addr data */
+ u8 buf_size; /* buffer size */
+ u8 *p_buffer; /* pointer to the buffer */
+};
+
+struct VENDOR_REQUEST_IN {
+ u8 bRequest;
+ u16 wValue;
+ u16 wIndex;
+ u16 wLength;
+ u8 direction;
+ u8 bData;
+ u8 *pBuff;
+};
+
+struct cx231xx_tvnorm {
+ char *name;
+ v4l2_std_id id;
+ u32 cxiformat;
+ u32 cxoformat;
+};
+
+struct cx231xx_ctrl {
+ struct v4l2_queryctrl v;
+ u32 off;
+ u32 reg;
+ u32 mask;
+ u32 shift;
+};
+
+enum TRANSFER_TYPE {
+ Raw_Video = 0,
+ Audio,
+ Vbi, /* VANC */
+ Sliced_cc, /* HANC */
+ TS1_serial_mode,
+ TS2,
+ TS1_parallel_mode
+} ;
+
+struct cx231xx_video_mode {
+ /* Isoc control struct */
+ struct cx231xx_dmaqueue vidq;
+ struct cx231xx_isoc_ctl isoc_ctl;
+ struct cx231xx_bulk_ctl bulk_ctl;
+ /* locks */
+ spinlock_t slock;
+
+ /* usb transfer */
+ int alt; /* alternate */
+ int max_pkt_size; /* max packet size of isoc transaction */
+ int num_alt; /* Number of alternative settings */
+ unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */
+ u16 end_point_addr;
+};
+/*
+struct cx23885_dmaqueue {
+ struct list_head active;
+ struct list_head queued;
+ struct timer_list timeout;
+ struct btcx_riscmem stopper;
+ u32 count;
+};
+*/
+struct cx231xx_tsport {
+ struct cx231xx *dev;
+
+ int nr;
+ int sram_chno;
+
+ struct videobuf_dvb_frontends frontends;
+
+ /* dma queues */
+
+ u32 ts_packet_size;
+ u32 ts_packet_count;
+
+ int width;
+ int height;
+
+ /* locks */
+ spinlock_t slock;
+
+ /* registers */
+ u32 reg_gpcnt;
+ u32 reg_gpcnt_ctl;
+ u32 reg_dma_ctl;
+ u32 reg_lngth;
+ u32 reg_hw_sop_ctrl;
+ u32 reg_gen_ctrl;
+ u32 reg_bd_pkt_status;
+ u32 reg_sop_status;
+ u32 reg_fifo_ovfl_stat;
+ u32 reg_vld_misc;
+ u32 reg_ts_clk_en;
+ u32 reg_ts_int_msk;
+ u32 reg_ts_int_stat;
+ u32 reg_src_sel;
+
+ /* Default register vals */
+ int pci_irqmask;
+ u32 dma_ctl_val;
+ u32 ts_int_msk_val;
+ u32 gen_ctrl_val;
+ u32 ts_clk_en_val;
+ u32 src_sel_val;
+ u32 vld_misc_val;
+ u32 hw_sop_ctrl_val;
+
+ /* Allow a single tsport to have multiple frontends */
+ u32 num_frontends;
+ void *port_priv;
+};
+
+/* main device struct */
+struct cx231xx {
+ /* generic device properties */
+ char name[30]; /* name (including minor) of the device */
+ int model; /* index in the device_data struct */
+ int devno; /* marks the number of this device */
+
+ struct cx231xx_board board;
+
+ /* For I2C IR support */
+ struct IR_i2c_init_data init_data;
+ struct i2c_client *ir_i2c_client;
+
+ unsigned int stream_on:1; /* Locks streams */
+ unsigned int vbi_stream_on:1; /* Locks streams for VBI */
+ unsigned int has_audio_class:1;
+ unsigned int has_alsa_audio:1;
+
+ struct cx231xx_fmt *format;
+
+ struct v4l2_device v4l2_dev;
+ struct v4l2_subdev *sd_cx25840;
+ struct v4l2_subdev *sd_tuner;
+
+ struct work_struct wq_trigger; /* Trigger to start/stop audio for alsa module */
+ atomic_t stream_started; /* stream should be running if true */
+
+ struct list_head devlist;
+
+ int tuner_type; /* type of the tuner */
+ int tuner_addr; /* tuner address */
+
+ /* I2C adapters: Master 1 & 2 (External) & Master 3 (Internal only) */
+ struct cx231xx_i2c i2c_bus[3];
+ unsigned int xc_fw_load_done:1;
+ /* locks */
+ struct mutex gpio_i2c_lock;
+ struct mutex i2c_lock;
+
+ /* video for linux */
+ int users; /* user count for exclusive use */
+ struct video_device *vdev; /* video for linux device struct */
+ v4l2_std_id norm; /* selected tv norm */
+ int ctl_freq; /* selected frequency */
+ unsigned int ctl_ainput; /* selected audio input */
+ int mute;
+ int volume;
+
+ /* frame properties */
+ int width; /* current frame width */
+ int height; /* current frame height */
+ int interlaced; /* 1=interlace fileds, 0=just top fileds */
+
+ struct cx231xx_audio adev;
+
+ /* states */
+ enum cx231xx_dev_state state;
+
+ struct work_struct request_module_wk;
+
+ /* locks */
+ struct mutex lock;
+ struct mutex ctrl_urb_lock; /* protects urb_buf */
+ struct list_head inqueue, outqueue;
+ wait_queue_head_t open, wait_frame, wait_stream;
+ struct video_device *vbi_dev;
+ struct video_device *radio_dev;
+
+ unsigned char eedata[256];
+
+ struct cx231xx_video_mode video_mode;
+ struct cx231xx_video_mode vbi_mode;
+ struct cx231xx_video_mode sliced_cc_mode;
+ struct cx231xx_video_mode ts1_mode;
+
+ atomic_t devlist_count;
+
+ struct usb_device *udev; /* the usb device */
+ char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */
+
+ /* helper funcs that call usb_control_msg */
+ int (*cx231xx_read_ctrl_reg) (struct cx231xx *dev, u8 req, u16 reg,
+ char *buf, int len);
+ int (*cx231xx_write_ctrl_reg) (struct cx231xx *dev, u8 req, u16 reg,
+ char *buf, int len);
+ int (*cx231xx_send_usb_command) (struct cx231xx_i2c *i2c_bus,
+ struct cx231xx_i2c_xfer_data *req_data);
+ int (*cx231xx_gpio_i2c_read) (struct cx231xx *dev, u8 dev_addr,
+ u8 *buf, u8 len);
+ int (*cx231xx_gpio_i2c_write) (struct cx231xx *dev, u8 dev_addr,
+ u8 *buf, u8 len);
+
+ int (*cx231xx_set_analog_freq) (struct cx231xx *dev, u32 freq);
+ int (*cx231xx_reset_analog_tuner) (struct cx231xx *dev);
+
+ enum cx231xx_mode mode;
+
+ struct cx231xx_dvb *dvb;
+
+ /* Cx231xx supported PCB config's */
+ struct pcb_config current_pcb_config;
+ u8 current_scenario_idx;
+ u8 interface_count;
+ u8 max_iad_interface_count;
+
+ /* GPIO related register direction and values */
+ u32 gpio_dir;
+ u32 gpio_val;
+
+ /* Power Modes */
+ int power_mode;
+
+ /* afe parameters */
+ enum AFE_MODE afe_mode;
+ u32 afe_ref_count;
+
+ /* video related parameters */
+ u32 video_input;
+ u32 active_mode;
+ u8 vbi_or_sliced_cc_mode; /* 0 - vbi ; 1 - sliced cc mode */
+ enum cx231xx_std_mode std_mode; /* 0 - Air; 1 - cable */
+
+ /*mode: digital=1 or analog=0*/
+ u8 mode_tv;
+
+ u8 USE_ISO;
+ struct cx231xx_tvnorm encodernorm;
+ struct cx231xx_tsport ts1, ts2;
+ struct cx2341x_mpeg_params mpeg_params;
+ struct video_device *v4l_device;
+ atomic_t v4l_reader_count;
+ u32 freq;
+ unsigned int input;
+ u32 cx23417_mailbox;
+ u32 __iomem *lmmio;
+ u8 __iomem *bmmio;
+};
+
+extern struct list_head cx231xx_devlist;
+
+#define cx25840_call(cx231xx, o, f, args...) \
+ v4l2_subdev_call(cx231xx->sd_cx25840, o, f, ##args)
+#define tuner_call(cx231xx, o, f, args...) \
+ v4l2_subdev_call(cx231xx->sd_tuner, o, f, ##args)
+#define call_all(dev, o, f, args...) \
+ v4l2_device_call_until_err(&dev->v4l2_dev, 0, o, f, ##args)
+
+struct cx231xx_ops {
+ struct list_head next;
+ char *name;
+ int id;
+ int (*init) (struct cx231xx *);
+ int (*fini) (struct cx231xx *);
+};
+
+/* call back functions in dvb module */
+int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq);
+int cx231xx_reset_analog_tuner(struct cx231xx *dev);
+
+/* Provided by cx231xx-i2c.c */
+void cx231xx_do_i2c_scan(struct cx231xx *dev, struct i2c_client *c);
+int cx231xx_i2c_register(struct cx231xx_i2c *bus);
+int cx231xx_i2c_unregister(struct cx231xx_i2c *bus);
+
+/* Internal block control functions */
+int cx231xx_read_i2c_master(struct cx231xx *dev, u8 dev_addr, u16 saddr,
+ u8 saddr_len, u32 *data, u8 data_len, int master);
+int cx231xx_write_i2c_master(struct cx231xx *dev, u8 dev_addr, u16 saddr,
+ u8 saddr_len, u32 data, u8 data_len, int master);
+int cx231xx_read_i2c_data(struct cx231xx *dev, u8 dev_addr,
+ u16 saddr, u8 saddr_len, u32 *data, u8 data_len);
+int cx231xx_write_i2c_data(struct cx231xx *dev, u8 dev_addr,
+ u16 saddr, u8 saddr_len, u32 data, u8 data_len);
+int cx231xx_reg_mask_write(struct cx231xx *dev, u8 dev_addr, u8 size,
+ u16 register_address, u8 bit_start, u8 bit_end,
+ u32 value);
+int cx231xx_read_modify_write_i2c_dword(struct cx231xx *dev, u8 dev_addr,
+ u16 saddr, u32 mask, u32 value);
+u32 cx231xx_set_field(u32 field_mask, u32 data);
+
+/*verve r/w*/
+void initGPIO(struct cx231xx *dev);
+void uninitGPIO(struct cx231xx *dev);
+/* afe related functions */
+int cx231xx_afe_init_super_block(struct cx231xx *dev, u32 ref_count);
+int cx231xx_afe_init_channels(struct cx231xx *dev);
+int cx231xx_afe_setup_AFE_for_baseband(struct cx231xx *dev);
+int cx231xx_afe_set_input_mux(struct cx231xx *dev, u32 input_mux);
+int cx231xx_afe_set_mode(struct cx231xx *dev, enum AFE_MODE mode);
+int cx231xx_afe_update_power_control(struct cx231xx *dev,
+ enum AV_MODE avmode);
+int cx231xx_afe_adjust_ref_count(struct cx231xx *dev, u32 video_input);
+
+/* i2s block related functions */
+int cx231xx_i2s_blk_initialize(struct cx231xx *dev);
+int cx231xx_i2s_blk_update_power_control(struct cx231xx *dev,
+ enum AV_MODE avmode);
+int cx231xx_i2s_blk_set_audio_input(struct cx231xx *dev, u8 audio_input);
+
+/* DIF related functions */
+int cx231xx_dif_configure_C2HH_for_low_IF(struct cx231xx *dev, u32 mode,
+ u32 function_mode, u32 standard);
+void cx231xx_set_Colibri_For_LowIF(struct cx231xx *dev, u32 if_freq,
+ u8 spectral_invert, u32 mode);
+u32 cx231xx_Get_Colibri_CarrierOffset(u32 mode, u32 standerd);
+void cx231xx_set_DIF_bandpass(struct cx231xx *dev, u32 if_freq,
+ u8 spectral_invert, u32 mode);
+void cx231xx_Setup_AFE_for_LowIF(struct cx231xx *dev);
+void reset_s5h1432_demod(struct cx231xx *dev);
+void cx231xx_dump_HH_reg(struct cx231xx *dev);
+void update_HH_register_after_set_DIF(struct cx231xx *dev);
+void cx231xx_dump_SC_reg(struct cx231xx *dev);
+
+
+
+int cx231xx_dif_set_standard(struct cx231xx *dev, u32 standard);
+int cx231xx_tuner_pre_channel_change(struct cx231xx *dev);
+int cx231xx_tuner_post_channel_change(struct cx231xx *dev);
+
+/* video parser functions */
+u8 cx231xx_find_next_SAV_EAV(u8 *p_buffer, u32 buffer_size,
+ u32 *p_bytes_used);
+u8 cx231xx_find_boundary_SAV_EAV(u8 *p_buffer, u8 *partial_buf,
+ u32 *p_bytes_used);
+int cx231xx_do_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
+ u8 *p_buffer, u32 bytes_to_copy);
+void cx231xx_reset_video_buffer(struct cx231xx *dev,
+ struct cx231xx_dmaqueue *dma_q);
+u8 cx231xx_is_buffer_done(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q);
+u32 cx231xx_copy_video_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
+ u8 *p_line, u32 length, int field_number);
+u32 cx231xx_get_video_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
+ u8 sav_eav, u8 *p_buffer, u32 buffer_size);
+void cx231xx_swab(u16 *from, u16 *to, u16 len);
+
+/* Provided by cx231xx-core.c */
+
+u32 cx231xx_request_buffers(struct cx231xx *dev, u32 count);
+void cx231xx_queue_unusedframes(struct cx231xx *dev);
+void cx231xx_release_buffers(struct cx231xx *dev);
+
+/* read from control pipe */
+int cx231xx_read_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg,
+ char *buf, int len);
+
+/* write to control pipe */
+int cx231xx_write_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg,
+ char *buf, int len);
+int cx231xx_mode_register(struct cx231xx *dev, u16 address, u32 mode);
+
+int cx231xx_send_vendor_cmd(struct cx231xx *dev,
+ struct VENDOR_REQUEST_IN *ven_req);
+int cx231xx_send_usb_command(struct cx231xx_i2c *i2c_bus,
+ struct cx231xx_i2c_xfer_data *req_data);
+
+/* Gpio related functions */
+int cx231xx_send_gpio_cmd(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val,
+ u8 len, u8 request, u8 direction);
+int cx231xx_set_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val);
+int cx231xx_get_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val);
+int cx231xx_set_gpio_value(struct cx231xx *dev, int pin_number, int pin_value);
+int cx231xx_set_gpio_direction(struct cx231xx *dev, int pin_number,
+ int pin_value);
+
+int cx231xx_gpio_i2c_start(struct cx231xx *dev);
+int cx231xx_gpio_i2c_end(struct cx231xx *dev);
+int cx231xx_gpio_i2c_write_byte(struct cx231xx *dev, u8 data);
+int cx231xx_gpio_i2c_read_byte(struct cx231xx *dev, u8 *buf);
+int cx231xx_gpio_i2c_read_ack(struct cx231xx *dev);
+int cx231xx_gpio_i2c_write_ack(struct cx231xx *dev);
+int cx231xx_gpio_i2c_write_nak(struct cx231xx *dev);
+
+int cx231xx_gpio_i2c_read(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len);
+int cx231xx_gpio_i2c_write(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len);
+
+/* audio related functions */
+int cx231xx_set_audio_decoder_input(struct cx231xx *dev,
+ enum AUDIO_INPUT audio_input);
+
+int cx231xx_capture_start(struct cx231xx *dev, int start, u8 media_type);
+int cx231xx_set_video_alternate(struct cx231xx *dev);
+int cx231xx_set_alt_setting(struct cx231xx *dev, u8 index, u8 alt);
+int is_fw_load(struct cx231xx *dev);
+int cx231xx_check_fw(struct cx231xx *dev);
+int cx231xx_init_isoc(struct cx231xx *dev, int max_packets,
+ int num_bufs, int max_pkt_size,
+ int (*isoc_copy) (struct cx231xx *dev,
+ struct urb *urb));
+int cx231xx_init_bulk(struct cx231xx *dev, int max_packets,
+ int num_bufs, int max_pkt_size,
+ int (*bulk_copy) (struct cx231xx *dev,
+ struct urb *urb));
+void cx231xx_stop_TS1(struct cx231xx *dev);
+void cx231xx_start_TS1(struct cx231xx *dev);
+void cx231xx_uninit_isoc(struct cx231xx *dev);
+void cx231xx_uninit_bulk(struct cx231xx *dev);
+int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode);
+int cx231xx_unmute_audio(struct cx231xx *dev);
+int cx231xx_ep5_bulkout(struct cx231xx *dev, u8 *firmware, u16 size);
+void cx231xx_disable656(struct cx231xx *dev);
+void cx231xx_enable656(struct cx231xx *dev);
+int cx231xx_demod_reset(struct cx231xx *dev);
+int cx231xx_gpio_set(struct cx231xx *dev, struct cx231xx_reg_seq *gpio);
+
+/* Device list functions */
+void cx231xx_release_resources(struct cx231xx *dev);
+void cx231xx_release_analog_resources(struct cx231xx *dev);
+int cx231xx_register_analog_devices(struct cx231xx *dev);
+void cx231xx_remove_from_devlist(struct cx231xx *dev);
+void cx231xx_add_into_devlist(struct cx231xx *dev);
+void cx231xx_init_extension(struct cx231xx *dev);
+void cx231xx_close_extension(struct cx231xx *dev);
+
+/* hardware init functions */
+int cx231xx_dev_init(struct cx231xx *dev);
+void cx231xx_dev_uninit(struct cx231xx *dev);
+void cx231xx_config_i2c(struct cx231xx *dev);
+int cx231xx_config(struct cx231xx *dev);
+
+/* Stream control functions */
+int cx231xx_start_stream(struct cx231xx *dev, u32 ep_mask);
+int cx231xx_stop_stream(struct cx231xx *dev, u32 ep_mask);
+
+int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type);
+
+/* Power control functions */
+int cx231xx_set_power_mode(struct cx231xx *dev, enum AV_MODE mode);
+int cx231xx_power_suspend(struct cx231xx *dev);
+
+/* chip specific control functions */
+int cx231xx_init_ctrl_pin_status(struct cx231xx *dev);
+int cx231xx_set_agc_analog_digital_mux_select(struct cx231xx *dev,
+ u8 analog_or_digital);
+int cx231xx_enable_i2c_port_3(struct cx231xx *dev, bool is_port_3);
+
+/* video audio decoder related functions */
+void video_mux(struct cx231xx *dev, int index);
+int cx231xx_set_video_input_mux(struct cx231xx *dev, u8 input);
+int cx231xx_set_decoder_video_input(struct cx231xx *dev, u8 pin_type, u8 input);
+int cx231xx_do_mode_ctrl_overrides(struct cx231xx *dev);
+int cx231xx_set_audio_input(struct cx231xx *dev, u8 input);
+
+/* Provided by cx231xx-video.c */
+int cx231xx_register_extension(struct cx231xx_ops *dev);
+void cx231xx_unregister_extension(struct cx231xx_ops *dev);
+void cx231xx_init_extension(struct cx231xx *dev);
+void cx231xx_close_extension(struct cx231xx *dev);
+
+/* Provided by cx231xx-cards.c */
+extern void cx231xx_pre_card_setup(struct cx231xx *dev);
+extern void cx231xx_card_setup(struct cx231xx *dev);
+extern struct cx231xx_board cx231xx_boards[];
+extern struct usb_device_id cx231xx_id_table[];
+extern const unsigned int cx231xx_bcount;
+int cx231xx_tuner_callback(void *ptr, int component, int command, int arg);
+
+/* cx23885-417.c */
+extern int cx231xx_417_register(struct cx231xx *dev);
+extern void cx231xx_417_unregister(struct cx231xx *dev);
+
+/* cx23885-input.c */
+
+#if defined(CONFIG_VIDEO_CX231XX_RC)
+int cx231xx_ir_init(struct cx231xx *dev);
+void cx231xx_ir_exit(struct cx231xx *dev);
+#else
+#define cx231xx_ir_init(dev) (0)
+#define cx231xx_ir_exit(dev) (0)
+#endif
+
+
+/* printk macros */
+
+#define cx231xx_err(fmt, arg...) do {\
+ printk(KERN_ERR fmt , ##arg); } while (0)
+
+#define cx231xx_errdev(fmt, arg...) do {\
+ printk(KERN_ERR "%s: "fmt,\
+ dev->name , ##arg); } while (0)
+
+#define cx231xx_info(fmt, arg...) do {\
+ printk(KERN_INFO "%s: "fmt,\
+ dev->name , ##arg); } while (0)
+#define cx231xx_warn(fmt, arg...) do {\
+ printk(KERN_WARNING "%s: "fmt,\
+ dev->name , ##arg); } while (0)
+
+static inline unsigned int norm_maxw(struct cx231xx *dev)
+{
+ if (dev->board.max_range_640_480)
+ return 640;
+ else
+ return 720;
+}
+
+static inline unsigned int norm_maxh(struct cx231xx *dev)
+{
+ if (dev->board.max_range_640_480)
+ return 480;
+ else
+ return (dev->norm & V4L2_STD_625_50) ? 576 : 480;
+}
+#endif
diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c
new file mode 100644
index 00000000..103ef6ba
--- /dev/null
+++ b/drivers/media/video/cx2341x.c
@@ -0,0 +1,1726 @@
+/*
+ * cx2341x - generic code for cx23415/6/8 based devices
+ *
+ * Copyright (C) 2006 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+#include <media/tuner.h>
+#include <media/cx2341x.h>
+#include <media/v4l2-common.h>
+
+MODULE_DESCRIPTION("cx23415/6/8 driver");
+MODULE_AUTHOR("Hans Verkuil");
+MODULE_LICENSE("GPL");
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+/********************** COMMON CODE *********************/
+
+/* definitions for audio properties bits 29-28 */
+#define CX2341X_AUDIO_ENCODING_METHOD_MPEG 0
+#define CX2341X_AUDIO_ENCODING_METHOD_AC3 1
+#define CX2341X_AUDIO_ENCODING_METHOD_LPCM 2
+
+static const char *cx2341x_get_name(u32 id)
+{
+ switch (id) {
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+ return "Spatial Filter Mode";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
+ return "Spatial Filter";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+ return "Spatial Luma Filter Type";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
+ return "Spatial Chroma Filter Type";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+ return "Temporal Filter Mode";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
+ return "Temporal Filter";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+ return "Median Filter Type";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
+ return "Median Luma Filter Maximum";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM:
+ return "Median Luma Filter Minimum";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP:
+ return "Median Chroma Filter Maximum";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
+ return "Median Chroma Filter Minimum";
+ case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+ return "Insert Navigation Packets";
+ }
+ return NULL;
+}
+
+static const char **cx2341x_get_menu(u32 id)
+{
+ static const char *cx2341x_video_spatial_filter_mode_menu[] = {
+ "Manual",
+ "Auto",
+ NULL
+ };
+
+ static const char *cx2341x_video_luma_spatial_filter_type_menu[] = {
+ "Off",
+ "1D Horizontal",
+ "1D Vertical",
+ "2D H/V Separable",
+ "2D Symmetric non-separable",
+ NULL
+ };
+
+ static const char *cx2341x_video_chroma_spatial_filter_type_menu[] = {
+ "Off",
+ "1D Horizontal",
+ NULL
+ };
+
+ static const char *cx2341x_video_temporal_filter_mode_menu[] = {
+ "Manual",
+ "Auto",
+ NULL
+ };
+
+ static const char *cx2341x_video_median_filter_type_menu[] = {
+ "Off",
+ "Horizontal",
+ "Vertical",
+ "Horizontal/Vertical",
+ "Diagonal",
+ NULL
+ };
+
+ switch (id) {
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+ return cx2341x_video_spatial_filter_mode_menu;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+ return cx2341x_video_luma_spatial_filter_type_menu;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
+ return cx2341x_video_chroma_spatial_filter_type_menu;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+ return cx2341x_video_temporal_filter_mode_menu;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+ return cx2341x_video_median_filter_type_menu;
+ }
+ return NULL;
+}
+
+static void cx2341x_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
+ s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags)
+{
+ *name = cx2341x_get_name(id);
+ *flags = 0;
+
+ switch (id) {
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+ *type = V4L2_CTRL_TYPE_MENU;
+ *min = 0;
+ *step = 0;
+ break;
+ case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+ *type = V4L2_CTRL_TYPE_BOOLEAN;
+ *min = 0;
+ *max = *step = 1;
+ break;
+ default:
+ *type = V4L2_CTRL_TYPE_INTEGER;
+ break;
+ }
+ switch (id) {
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+ *flags |= V4L2_CTRL_FLAG_UPDATE;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
+ *flags |= V4L2_CTRL_FLAG_SLIDER;
+ break;
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ *flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ break;
+ }
+}
+
+
+/********************** OLD CODE *********************/
+
+/* Must be sorted from low to high control ID! */
+const u32 cx2341x_mpeg_ctrls[] = {
+ V4L2_CID_MPEG_CLASS,
+ V4L2_CID_MPEG_STREAM_TYPE,
+ V4L2_CID_MPEG_STREAM_VBI_FMT,
+ V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ,
+ V4L2_CID_MPEG_AUDIO_ENCODING,
+ V4L2_CID_MPEG_AUDIO_L2_BITRATE,
+ V4L2_CID_MPEG_AUDIO_MODE,
+ V4L2_CID_MPEG_AUDIO_MODE_EXTENSION,
+ V4L2_CID_MPEG_AUDIO_EMPHASIS,
+ V4L2_CID_MPEG_AUDIO_CRC,
+ V4L2_CID_MPEG_AUDIO_MUTE,
+ V4L2_CID_MPEG_AUDIO_AC3_BITRATE,
+ V4L2_CID_MPEG_VIDEO_ENCODING,
+ V4L2_CID_MPEG_VIDEO_ASPECT,
+ V4L2_CID_MPEG_VIDEO_B_FRAMES,
+ V4L2_CID_MPEG_VIDEO_GOP_SIZE,
+ V4L2_CID_MPEG_VIDEO_GOP_CLOSURE,
+ V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+ V4L2_CID_MPEG_VIDEO_BITRATE,
+ V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
+ V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION,
+ V4L2_CID_MPEG_VIDEO_MUTE,
+ V4L2_CID_MPEG_VIDEO_MUTE_YUV,
+ V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE,
+ V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER,
+ V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE,
+ V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE,
+ V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE,
+ V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER,
+ V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE,
+ V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM,
+ V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP,
+ V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM,
+ V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP,
+ V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS,
+ 0
+};
+EXPORT_SYMBOL(cx2341x_mpeg_ctrls);
+
+static const struct cx2341x_mpeg_params default_params = {
+ /* misc */
+ .capabilities = 0,
+ .port = CX2341X_PORT_MEMORY,
+ .width = 720,
+ .height = 480,
+ .is_50hz = 0,
+
+ /* stream */
+ .stream_type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS,
+ .stream_vbi_fmt = V4L2_MPEG_STREAM_VBI_FMT_NONE,
+ .stream_insert_nav_packets = 0,
+
+ /* audio */
+ .audio_sampling_freq = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000,
+ .audio_encoding = V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
+ .audio_l2_bitrate = V4L2_MPEG_AUDIO_L2_BITRATE_224K,
+ .audio_ac3_bitrate = V4L2_MPEG_AUDIO_AC3_BITRATE_224K,
+ .audio_mode = V4L2_MPEG_AUDIO_MODE_STEREO,
+ .audio_mode_extension = V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4,
+ .audio_emphasis = V4L2_MPEG_AUDIO_EMPHASIS_NONE,
+ .audio_crc = V4L2_MPEG_AUDIO_CRC_NONE,
+ .audio_mute = 0,
+
+ /* video */
+ .video_encoding = V4L2_MPEG_VIDEO_ENCODING_MPEG_2,
+ .video_aspect = V4L2_MPEG_VIDEO_ASPECT_4x3,
+ .video_b_frames = 2,
+ .video_gop_size = 12,
+ .video_gop_closure = 1,
+ .video_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
+ .video_bitrate = 6000000,
+ .video_bitrate_peak = 8000000,
+ .video_temporal_decimation = 0,
+ .video_mute = 0,
+ .video_mute_yuv = 0x008080, /* YCbCr value for black */
+
+ /* encoding filters */
+ .video_spatial_filter_mode =
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL,
+ .video_spatial_filter = 0,
+ .video_luma_spatial_filter_type =
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR,
+ .video_chroma_spatial_filter_type =
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR,
+ .video_temporal_filter_mode =
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL,
+ .video_temporal_filter = 8,
+ .video_median_filter_type =
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF,
+ .video_luma_median_filter_top = 255,
+ .video_luma_median_filter_bottom = 0,
+ .video_chroma_median_filter_top = 255,
+ .video_chroma_median_filter_bottom = 0,
+};
+/* Map the control ID to the correct field in the cx2341x_mpeg_params
+ struct. Return -EINVAL if the ID is unknown, else return 0. */
+static int cx2341x_get_ctrl(const struct cx2341x_mpeg_params *params,
+ struct v4l2_ext_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+ ctrl->value = params->audio_sampling_freq;
+ break;
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ ctrl->value = params->audio_encoding;
+ break;
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+ ctrl->value = params->audio_l2_bitrate;
+ break;
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+ ctrl->value = params->audio_ac3_bitrate;
+ break;
+ case V4L2_CID_MPEG_AUDIO_MODE:
+ ctrl->value = params->audio_mode;
+ break;
+ case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
+ ctrl->value = params->audio_mode_extension;
+ break;
+ case V4L2_CID_MPEG_AUDIO_EMPHASIS:
+ ctrl->value = params->audio_emphasis;
+ break;
+ case V4L2_CID_MPEG_AUDIO_CRC:
+ ctrl->value = params->audio_crc;
+ break;
+ case V4L2_CID_MPEG_AUDIO_MUTE:
+ ctrl->value = params->audio_mute;
+ break;
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ ctrl->value = params->video_encoding;
+ break;
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ ctrl->value = params->video_aspect;
+ break;
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ ctrl->value = params->video_b_frames;
+ break;
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ ctrl->value = params->video_gop_size;
+ break;
+ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+ ctrl->value = params->video_gop_closure;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ ctrl->value = params->video_bitrate_mode;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ ctrl->value = params->video_bitrate;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+ ctrl->value = params->video_bitrate_peak;
+ break;
+ case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION:
+ ctrl->value = params->video_temporal_decimation;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MUTE:
+ ctrl->value = params->video_mute;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MUTE_YUV:
+ ctrl->value = params->video_mute_yuv;
+ break;
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ ctrl->value = params->stream_type;
+ break;
+ case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ ctrl->value = params->stream_vbi_fmt;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+ ctrl->value = params->video_spatial_filter_mode;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
+ ctrl->value = params->video_spatial_filter;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+ ctrl->value = params->video_luma_spatial_filter_type;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
+ ctrl->value = params->video_chroma_spatial_filter_type;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+ ctrl->value = params->video_temporal_filter_mode;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
+ ctrl->value = params->video_temporal_filter;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+ ctrl->value = params->video_median_filter_type;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
+ ctrl->value = params->video_luma_median_filter_top;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM:
+ ctrl->value = params->video_luma_median_filter_bottom;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP:
+ ctrl->value = params->video_chroma_median_filter_top;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
+ ctrl->value = params->video_chroma_median_filter_bottom;
+ break;
+ case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+ ctrl->value = params->stream_insert_nav_packets;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* Map the control ID to the correct field in the cx2341x_mpeg_params
+ struct. Return -EINVAL if the ID is unknown, else return 0. */
+static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params, int busy,
+ struct v4l2_ext_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+ if (busy)
+ return -EBUSY;
+ params->audio_sampling_freq = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ if (busy)
+ return -EBUSY;
+ if (params->capabilities & CX2341X_CAP_HAS_AC3)
+ if (ctrl->value != V4L2_MPEG_AUDIO_ENCODING_LAYER_2 &&
+ ctrl->value != V4L2_MPEG_AUDIO_ENCODING_AC3)
+ return -ERANGE;
+ params->audio_encoding = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+ if (busy)
+ return -EBUSY;
+ params->audio_l2_bitrate = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+ if (busy)
+ return -EBUSY;
+ if (!(params->capabilities & CX2341X_CAP_HAS_AC3))
+ return -EINVAL;
+ params->audio_ac3_bitrate = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_AUDIO_MODE:
+ params->audio_mode = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
+ params->audio_mode_extension = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_AUDIO_EMPHASIS:
+ params->audio_emphasis = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_AUDIO_CRC:
+ params->audio_crc = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_AUDIO_MUTE:
+ params->audio_mute = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ params->video_aspect = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES: {
+ int b = ctrl->value + 1;
+ int gop = params->video_gop_size;
+ params->video_b_frames = ctrl->value;
+ params->video_gop_size = b * ((gop + b - 1) / b);
+ /* Max GOP size = 34 */
+ while (params->video_gop_size > 34)
+ params->video_gop_size -= b;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE: {
+ int b = params->video_b_frames + 1;
+ int gop = ctrl->value;
+ params->video_gop_size = b * ((gop + b - 1) / b);
+ /* Max GOP size = 34 */
+ while (params->video_gop_size > 34)
+ params->video_gop_size -= b;
+ ctrl->value = params->video_gop_size;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+ params->video_gop_closure = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ if (busy)
+ return -EBUSY;
+ /* MPEG-1 only allows CBR */
+ if (params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1 &&
+ ctrl->value != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
+ return -EINVAL;
+ params->video_bitrate_mode = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ if (busy)
+ return -EBUSY;
+ params->video_bitrate = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+ if (busy)
+ return -EBUSY;
+ params->video_bitrate_peak = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION:
+ params->video_temporal_decimation = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MUTE:
+ params->video_mute = (ctrl->value != 0);
+ break;
+ case V4L2_CID_MPEG_VIDEO_MUTE_YUV:
+ params->video_mute_yuv = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ if (busy)
+ return -EBUSY;
+ params->stream_type = ctrl->value;
+ params->video_encoding =
+ (params->stream_type == V4L2_MPEG_STREAM_TYPE_MPEG1_SS ||
+ params->stream_type == V4L2_MPEG_STREAM_TYPE_MPEG1_VCD) ?
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_1 :
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2;
+ if (params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
+ /* MPEG-1 implies CBR */
+ params->video_bitrate_mode =
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR;
+ break;
+ case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ params->stream_vbi_fmt = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+ params->video_spatial_filter_mode = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
+ params->video_spatial_filter = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+ params->video_luma_spatial_filter_type = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
+ params->video_chroma_spatial_filter_type = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+ params->video_temporal_filter_mode = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
+ params->video_temporal_filter = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+ params->video_median_filter_type = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
+ params->video_luma_median_filter_top = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM:
+ params->video_luma_median_filter_bottom = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP:
+ params->video_chroma_median_filter_top = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
+ params->video_chroma_median_filter_bottom = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+ params->stream_insert_nav_packets = ctrl->value;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cx2341x_ctrl_query_fill(struct v4l2_queryctrl *qctrl,
+ s32 min, s32 max, s32 step, s32 def)
+{
+ const char *name;
+
+ switch (qctrl->id) {
+ /* MPEG controls */
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
+ case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+ cx2341x_ctrl_fill(qctrl->id, &name, &qctrl->type,
+ &min, &max, &step, &def, &qctrl->flags);
+ qctrl->minimum = min;
+ qctrl->maximum = max;
+ qctrl->step = step;
+ qctrl->default_value = def;
+ qctrl->reserved[0] = qctrl->reserved[1] = 0;
+ strlcpy(qctrl->name, name, sizeof(qctrl->name));
+ return 0;
+
+ default:
+ return v4l2_ctrl_query_fill(qctrl, min, max, step, def);
+ }
+}
+
+int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params,
+ struct v4l2_queryctrl *qctrl)
+{
+ int err;
+
+ switch (qctrl->id) {
+ case V4L2_CID_MPEG_CLASS:
+ return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0);
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_STREAM_TYPE_MPEG2_PS,
+ V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, 1,
+ V4L2_MPEG_STREAM_TYPE_MPEG2_PS);
+
+ case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ if (params->capabilities & CX2341X_CAP_HAS_SLICED_VBI)
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_STREAM_VBI_FMT_NONE,
+ V4L2_MPEG_STREAM_VBI_FMT_IVTV, 1,
+ V4L2_MPEG_STREAM_VBI_FMT_NONE);
+ return cx2341x_ctrl_query_fill(qctrl,
+ V4L2_MPEG_STREAM_VBI_FMT_NONE,
+ V4L2_MPEG_STREAM_VBI_FMT_NONE, 1,
+ default_params.stream_vbi_fmt);
+
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100,
+ V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, 1,
+ V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000);
+
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ if (params->capabilities & CX2341X_CAP_HAS_AC3) {
+ /*
+ * The state of L2 & AC3 bitrate controls can change
+ * when this control changes, but v4l2_ctrl_query_fill()
+ * already sets V4L2_CTRL_FLAG_UPDATE for
+ * V4L2_CID_MPEG_AUDIO_ENCODING, so we don't here.
+ */
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
+ V4L2_MPEG_AUDIO_ENCODING_AC3, 1,
+ default_params.audio_encoding);
+ }
+
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
+ V4L2_MPEG_AUDIO_ENCODING_LAYER_2, 1,
+ default_params.audio_encoding);
+
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+ err = v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_AUDIO_L2_BITRATE_192K,
+ V4L2_MPEG_AUDIO_L2_BITRATE_384K, 1,
+ default_params.audio_l2_bitrate);
+ if (err)
+ return err;
+ if (params->capabilities & CX2341X_CAP_HAS_AC3 &&
+ params->audio_encoding != V4L2_MPEG_AUDIO_ENCODING_LAYER_2)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return 0;
+
+ case V4L2_CID_MPEG_AUDIO_MODE:
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_AUDIO_MODE_STEREO,
+ V4L2_MPEG_AUDIO_MODE_MONO, 1,
+ V4L2_MPEG_AUDIO_MODE_STEREO);
+
+ case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
+ err = v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4,
+ V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, 1,
+ V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4);
+ if (err == 0 &&
+ params->audio_mode != V4L2_MPEG_AUDIO_MODE_JOINT_STEREO)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return err;
+
+ case V4L2_CID_MPEG_AUDIO_EMPHASIS:
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_AUDIO_EMPHASIS_NONE,
+ V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, 1,
+ V4L2_MPEG_AUDIO_EMPHASIS_NONE);
+
+ case V4L2_CID_MPEG_AUDIO_CRC:
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_AUDIO_CRC_NONE,
+ V4L2_MPEG_AUDIO_CRC_CRC16, 1,
+ V4L2_MPEG_AUDIO_CRC_NONE);
+
+ case V4L2_CID_MPEG_AUDIO_MUTE:
+ return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0);
+
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+ err = v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_48K,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_448K, 1,
+ default_params.audio_ac3_bitrate);
+ if (err)
+ return err;
+ if (params->capabilities & CX2341X_CAP_HAS_AC3) {
+ if (params->audio_encoding !=
+ V4L2_MPEG_AUDIO_ENCODING_AC3)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ } else
+ qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+ return 0;
+
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ /* this setting is read-only for the cx2341x since the
+ V4L2_CID_MPEG_STREAM_TYPE really determines the
+ MPEG-1/2 setting */
+ err = v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_1,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 1,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2);
+ if (err == 0)
+ qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ return err;
+
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_VIDEO_ASPECT_1x1,
+ V4L2_MPEG_VIDEO_ASPECT_221x100, 1,
+ V4L2_MPEG_VIDEO_ASPECT_4x3);
+
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ return v4l2_ctrl_query_fill(qctrl, 0, 33, 1, 2);
+
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ return v4l2_ctrl_query_fill(qctrl, 1, 34, 1,
+ params->is_50hz ? 12 : 15);
+
+ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+ return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 1);
+
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ err = v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
+ if (err == 0 &&
+ params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return err;
+
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 6000000);
+
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+ err = v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 8000000);
+ if (err == 0 &&
+ params->video_bitrate_mode ==
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return err;
+
+ case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION:
+ return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 0);
+
+ case V4L2_CID_MPEG_VIDEO_MUTE:
+ return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0);
+
+ case V4L2_CID_MPEG_VIDEO_MUTE_YUV: /* Init YUV (really YCbCr) to black */
+ return v4l2_ctrl_query_fill(qctrl, 0, 0xffffff, 1, 0x008080);
+
+ /* CX23415/6 specific */
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+ return cx2341x_ctrl_query_fill(qctrl,
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL,
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, 1,
+ default_params.video_spatial_filter_mode);
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
+ cx2341x_ctrl_query_fill(qctrl, 0, 15, 1,
+ default_params.video_spatial_filter);
+ qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
+ if (params->video_spatial_filter_mode ==
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return 0;
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+ cx2341x_ctrl_query_fill(qctrl,
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF,
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE,
+ 1,
+ default_params.video_luma_spatial_filter_type);
+ if (params->video_spatial_filter_mode ==
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return 0;
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
+ cx2341x_ctrl_query_fill(qctrl,
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF,
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR,
+ 1,
+ default_params.video_chroma_spatial_filter_type);
+ if (params->video_spatial_filter_mode ==
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return 0;
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+ return cx2341x_ctrl_query_fill(qctrl,
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL,
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO, 1,
+ default_params.video_temporal_filter_mode);
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
+ cx2341x_ctrl_query_fill(qctrl, 0, 31, 1,
+ default_params.video_temporal_filter);
+ qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
+ if (params->video_temporal_filter_mode ==
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return 0;
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+ return cx2341x_ctrl_query_fill(qctrl,
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF,
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG, 1,
+ default_params.video_median_filter_type);
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
+ cx2341x_ctrl_query_fill(qctrl, 0, 255, 1,
+ default_params.video_luma_median_filter_top);
+ qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
+ if (params->video_median_filter_type ==
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return 0;
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM:
+ cx2341x_ctrl_query_fill(qctrl, 0, 255, 1,
+ default_params.video_luma_median_filter_bottom);
+ qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
+ if (params->video_median_filter_type ==
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return 0;
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP:
+ cx2341x_ctrl_query_fill(qctrl, 0, 255, 1,
+ default_params.video_chroma_median_filter_top);
+ qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
+ if (params->video_median_filter_type ==
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return 0;
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
+ cx2341x_ctrl_query_fill(qctrl, 0, 255, 1,
+ default_params.video_chroma_median_filter_bottom);
+ qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
+ if (params->video_median_filter_type ==
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return 0;
+
+ case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+ return cx2341x_ctrl_query_fill(qctrl, 0, 1, 1,
+ default_params.stream_insert_nav_packets);
+
+ default:
+ return -EINVAL;
+
+ }
+}
+EXPORT_SYMBOL(cx2341x_ctrl_query);
+
+const char * const *cx2341x_ctrl_get_menu(const struct cx2341x_mpeg_params *p, u32 id)
+{
+ static const char * const mpeg_stream_type_without_ts[] = {
+ "MPEG-2 Program Stream",
+ "",
+ "MPEG-1 System Stream",
+ "MPEG-2 DVD-compatible Stream",
+ "MPEG-1 VCD-compatible Stream",
+ "MPEG-2 SVCD-compatible Stream",
+ NULL
+ };
+
+ static const char *mpeg_stream_type_with_ts[] = {
+ "MPEG-2 Program Stream",
+ "MPEG-2 Transport Stream",
+ "MPEG-1 System Stream",
+ "MPEG-2 DVD-compatible Stream",
+ "MPEG-1 VCD-compatible Stream",
+ "MPEG-2 SVCD-compatible Stream",
+ NULL
+ };
+
+ static const char *mpeg_audio_encoding_l2_ac3[] = {
+ "",
+ "MPEG-1/2 Layer II",
+ "",
+ "",
+ "AC-3",
+ NULL
+ };
+
+ switch (id) {
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ return (p->capabilities & CX2341X_CAP_HAS_TS) ?
+ mpeg_stream_type_with_ts : mpeg_stream_type_without_ts;
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ return (p->capabilities & CX2341X_CAP_HAS_AC3) ?
+ mpeg_audio_encoding_l2_ac3 : v4l2_ctrl_get_menu(id);
+ case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
+ case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
+ return NULL;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+ return cx2341x_get_menu(id);
+ default:
+ return v4l2_ctrl_get_menu(id);
+ }
+}
+EXPORT_SYMBOL(cx2341x_ctrl_get_menu);
+
+static void cx2341x_calc_audio_properties(struct cx2341x_mpeg_params *params)
+{
+ params->audio_properties =
+ (params->audio_sampling_freq << 0) |
+ (params->audio_mode << 8) |
+ (params->audio_mode_extension << 10) |
+ (((params->audio_emphasis == V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17)
+ ? 3 : params->audio_emphasis) << 12) |
+ (params->audio_crc << 14);
+
+ if ((params->capabilities & CX2341X_CAP_HAS_AC3) &&
+ params->audio_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3) {
+ params->audio_properties |=
+ /* Not sure if this MPEG Layer II setting is required */
+ ((3 - V4L2_MPEG_AUDIO_ENCODING_LAYER_2) << 2) |
+ (params->audio_ac3_bitrate << 4) |
+ (CX2341X_AUDIO_ENCODING_METHOD_AC3 << 28);
+ } else {
+ /* Assuming MPEG Layer II */
+ params->audio_properties |=
+ ((3 - params->audio_encoding) << 2) |
+ ((1 + params->audio_l2_bitrate) << 4);
+ }
+}
+
+int cx2341x_ext_ctrls(struct cx2341x_mpeg_params *params, int busy,
+ struct v4l2_ext_controls *ctrls, unsigned int cmd)
+{
+ int err = 0;
+ int i;
+
+ if (cmd == VIDIOC_G_EXT_CTRLS) {
+ for (i = 0; i < ctrls->count; i++) {
+ struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+ err = cx2341x_get_ctrl(params, ctrl);
+ if (err) {
+ ctrls->error_idx = i;
+ break;
+ }
+ }
+ return err;
+ }
+ for (i = 0; i < ctrls->count; i++) {
+ struct v4l2_ext_control *ctrl = ctrls->controls + i;
+ struct v4l2_queryctrl qctrl;
+ const char * const *menu_items = NULL;
+
+ qctrl.id = ctrl->id;
+ err = cx2341x_ctrl_query(params, &qctrl);
+ if (err)
+ break;
+ if (qctrl.type == V4L2_CTRL_TYPE_MENU)
+ menu_items = cx2341x_ctrl_get_menu(params, qctrl.id);
+ err = v4l2_ctrl_check(ctrl, &qctrl, menu_items);
+ if (err)
+ break;
+ err = cx2341x_set_ctrl(params, busy, ctrl);
+ if (err)
+ break;
+ }
+ if (err == 0 &&
+ params->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR &&
+ params->video_bitrate_peak < params->video_bitrate) {
+ err = -ERANGE;
+ ctrls->error_idx = ctrls->count;
+ }
+ if (err)
+ ctrls->error_idx = i;
+ else
+ cx2341x_calc_audio_properties(params);
+ return err;
+}
+EXPORT_SYMBOL(cx2341x_ext_ctrls);
+
+void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p)
+{
+ *p = default_params;
+ cx2341x_calc_audio_properties(p);
+}
+EXPORT_SYMBOL(cx2341x_fill_defaults);
+
+static int cx2341x_api(void *priv, cx2341x_mbox_func func,
+ u32 cmd, int args, ...)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ va_list vargs;
+ int i;
+
+ va_start(vargs, args);
+
+ for (i = 0; i < args; i++)
+ data[i] = va_arg(vargs, int);
+ va_end(vargs);
+ return func(priv, cmd, args, 0, data);
+}
+
+#define NEQ(field) (old->field != new->field)
+
+int cx2341x_update(void *priv, cx2341x_mbox_func func,
+ const struct cx2341x_mpeg_params *old,
+ const struct cx2341x_mpeg_params *new)
+{
+ static int mpeg_stream_type[] = {
+ 0, /* MPEG-2 PS */
+ 1, /* MPEG-2 TS */
+ 2, /* MPEG-1 SS */
+ 14, /* DVD */
+ 11, /* VCD */
+ 12, /* SVCD */
+ };
+
+ int err = 0;
+ int force = (old == NULL);
+ u16 temporal = new->video_temporal_filter;
+
+ cx2341x_api(priv, func, CX2341X_ENC_SET_OUTPUT_PORT, 2, new->port, 0);
+
+ if (force || NEQ(is_50hz)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_RATE, 1,
+ new->is_50hz);
+ if (err) return err;
+ }
+
+ if (force || NEQ(width) || NEQ(height) || NEQ(video_encoding)) {
+ u16 w = new->width;
+ u16 h = new->height;
+
+ if (new->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) {
+ w /= 2;
+ h /= 2;
+ }
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_SIZE, 2,
+ h, w);
+ if (err) return err;
+ }
+ if (force || NEQ(stream_type)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_STREAM_TYPE, 1,
+ mpeg_stream_type[new->stream_type]);
+ if (err) return err;
+ }
+ if (force || NEQ(video_aspect)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_ASPECT_RATIO, 1,
+ 1 + new->video_aspect);
+ if (err) return err;
+ }
+ if (force || NEQ(video_b_frames) || NEQ(video_gop_size)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_GOP_PROPERTIES, 2,
+ new->video_gop_size, new->video_b_frames + 1);
+ if (err) return err;
+ }
+ if (force || NEQ(video_gop_closure)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_GOP_CLOSURE, 1,
+ new->video_gop_closure);
+ if (err) return err;
+ }
+ if (force || NEQ(audio_properties)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_AUDIO_PROPERTIES,
+ 1, new->audio_properties);
+ if (err) return err;
+ }
+ if (force || NEQ(audio_mute)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_AUDIO, 1,
+ new->audio_mute);
+ if (err) return err;
+ }
+ if (force || NEQ(video_bitrate_mode) || NEQ(video_bitrate) ||
+ NEQ(video_bitrate_peak)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_BIT_RATE, 5,
+ new->video_bitrate_mode, new->video_bitrate,
+ new->video_bitrate_peak / 400, 0, 0);
+ if (err) return err;
+ }
+ if (force || NEQ(video_spatial_filter_mode) ||
+ NEQ(video_temporal_filter_mode) ||
+ NEQ(video_median_filter_type)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_DNR_FILTER_MODE,
+ 2, new->video_spatial_filter_mode |
+ (new->video_temporal_filter_mode << 1),
+ new->video_median_filter_type);
+ if (err) return err;
+ }
+ if (force || NEQ(video_luma_median_filter_bottom) ||
+ NEQ(video_luma_median_filter_top) ||
+ NEQ(video_chroma_median_filter_bottom) ||
+ NEQ(video_chroma_median_filter_top)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_CORING_LEVELS, 4,
+ new->video_luma_median_filter_bottom,
+ new->video_luma_median_filter_top,
+ new->video_chroma_median_filter_bottom,
+ new->video_chroma_median_filter_top);
+ if (err) return err;
+ }
+ if (force || NEQ(video_luma_spatial_filter_type) ||
+ NEQ(video_chroma_spatial_filter_type)) {
+ err = cx2341x_api(priv, func,
+ CX2341X_ENC_SET_SPATIAL_FILTER_TYPE,
+ 2, new->video_luma_spatial_filter_type,
+ new->video_chroma_spatial_filter_type);
+ if (err) return err;
+ }
+ if (force || NEQ(video_spatial_filter) ||
+ old->video_temporal_filter != temporal) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_DNR_FILTER_PROPS,
+ 2, new->video_spatial_filter, temporal);
+ if (err) return err;
+ }
+ if (force || NEQ(video_temporal_decimation)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_DROP_RATE,
+ 1, new->video_temporal_decimation);
+ if (err) return err;
+ }
+ if (force || NEQ(video_mute) ||
+ (new->video_mute && NEQ(video_mute_yuv))) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_VIDEO, 1,
+ new->video_mute | (new->video_mute_yuv << 8));
+ if (err) return err;
+ }
+ if (force || NEQ(stream_insert_nav_packets)) {
+ err = cx2341x_api(priv, func, CX2341X_ENC_MISC, 2,
+ 7, new->stream_insert_nav_packets);
+ if (err) return err;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(cx2341x_update);
+
+static const char *cx2341x_menu_item(const struct cx2341x_mpeg_params *p, u32 id)
+{
+ const char * const *menu = cx2341x_ctrl_get_menu(p, id);
+ struct v4l2_ext_control ctrl;
+
+ if (menu == NULL)
+ goto invalid;
+ ctrl.id = id;
+ if (cx2341x_get_ctrl(p, &ctrl))
+ goto invalid;
+ while (ctrl.value-- && *menu) menu++;
+ if (*menu == NULL)
+ goto invalid;
+ return *menu;
+
+invalid:
+ return "<invalid>";
+}
+
+void cx2341x_log_status(const struct cx2341x_mpeg_params *p, const char *prefix)
+{
+ int is_mpeg1 = p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
+
+ /* Stream */
+ printk(KERN_INFO "%s: Stream: %s",
+ prefix,
+ cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_TYPE));
+ if (p->stream_insert_nav_packets)
+ printk(" (with navigation packets)");
+ printk("\n");
+ printk(KERN_INFO "%s: VBI Format: %s\n",
+ prefix,
+ cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_VBI_FMT));
+
+ /* Video */
+ printk(KERN_INFO "%s: Video: %dx%d, %d fps%s\n",
+ prefix,
+ p->width / (is_mpeg1 ? 2 : 1), p->height / (is_mpeg1 ? 2 : 1),
+ p->is_50hz ? 25 : 30,
+ (p->video_mute) ? " (muted)" : "");
+ printk(KERN_INFO "%s: Video: %s, %s, %s, %d",
+ prefix,
+ cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ENCODING),
+ cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ASPECT),
+ cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_BITRATE_MODE),
+ p->video_bitrate);
+ if (p->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR)
+ printk(", Peak %d", p->video_bitrate_peak);
+ printk("\n");
+ printk(KERN_INFO
+ "%s: Video: GOP Size %d, %d B-Frames, %sGOP Closure\n",
+ prefix,
+ p->video_gop_size, p->video_b_frames,
+ p->video_gop_closure ? "" : "No ");
+ if (p->video_temporal_decimation)
+ printk(KERN_INFO "%s: Video: Temporal Decimation %d\n",
+ prefix, p->video_temporal_decimation);
+
+ /* Audio */
+ printk(KERN_INFO "%s: Audio: %s, %s, %s, %s%s",
+ prefix,
+ cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ),
+ cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_ENCODING),
+ cx2341x_menu_item(p,
+ p->audio_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3
+ ? V4L2_CID_MPEG_AUDIO_AC3_BITRATE
+ : V4L2_CID_MPEG_AUDIO_L2_BITRATE),
+ cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE),
+ p->audio_mute ? " (muted)" : "");
+ if (p->audio_mode == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO)
+ printk(", %s", cx2341x_menu_item(p,
+ V4L2_CID_MPEG_AUDIO_MODE_EXTENSION));
+ printk(", %s, %s\n",
+ cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_EMPHASIS),
+ cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_CRC));
+
+ /* Encoding filters */
+ printk(KERN_INFO "%s: Spatial Filter: %s, Luma %s, Chroma %s, %d\n",
+ prefix,
+ cx2341x_menu_item(p,
+ V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE),
+ cx2341x_menu_item(p,
+ V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE),
+ cx2341x_menu_item(p,
+ V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE),
+ p->video_spatial_filter);
+
+ printk(KERN_INFO "%s: Temporal Filter: %s, %d\n",
+ prefix,
+ cx2341x_menu_item(p,
+ V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE),
+ p->video_temporal_filter);
+ printk(KERN_INFO
+ "%s: Median Filter: %s, Luma [%d, %d], Chroma [%d, %d]\n",
+ prefix,
+ cx2341x_menu_item(p,
+ V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE),
+ p->video_luma_median_filter_bottom,
+ p->video_luma_median_filter_top,
+ p->video_chroma_median_filter_bottom,
+ p->video_chroma_median_filter_top);
+}
+EXPORT_SYMBOL(cx2341x_log_status);
+
+
+
+/********************** NEW CODE *********************/
+
+static inline struct cx2341x_handler *to_cxhdl(struct v4l2_ctrl *ctrl)
+{
+ return container_of(ctrl->handler, struct cx2341x_handler, hdl);
+}
+
+static int cx2341x_hdl_api(struct cx2341x_handler *hdl,
+ u32 cmd, int args, ...)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ va_list vargs;
+ int i;
+
+ va_start(vargs, args);
+
+ for (i = 0; i < args; i++)
+ data[i] = va_arg(vargs, int);
+ va_end(vargs);
+ return hdl->func(hdl->priv, cmd, args, 0, data);
+}
+
+/* ctrl->handler->lock is held, so it is safe to access cur.val */
+static inline int cx2341x_neq(struct v4l2_ctrl *ctrl)
+{
+ return ctrl && ctrl->val != ctrl->cur.val;
+}
+
+static int cx2341x_try_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct cx2341x_handler *hdl = to_cxhdl(ctrl);
+ s32 val = ctrl->val;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES: {
+ /* video gop cluster */
+ int b = val + 1;
+ int gop = hdl->video_gop_size->val;
+
+ gop = b * ((gop + b - 1) / b);
+
+ /* Max GOP size = 34 */
+ while (gop > 34)
+ gop -= b;
+ hdl->video_gop_size->val = gop;
+ break;
+ }
+
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ /* stream type cluster */
+ hdl->video_encoding->val =
+ (hdl->stream_type->val == V4L2_MPEG_STREAM_TYPE_MPEG1_SS ||
+ hdl->stream_type->val == V4L2_MPEG_STREAM_TYPE_MPEG1_VCD) ?
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_1 :
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2;
+ if (hdl->video_encoding->val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
+ /* MPEG-1 implies CBR */
+ hdl->video_bitrate_mode->val =
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR;
+ /* peak bitrate shall be >= normal bitrate */
+ if (hdl->video_bitrate_mode->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR &&
+ hdl->video_bitrate_peak->val < hdl->video_bitrate->val)
+ hdl->video_bitrate_peak->val = hdl->video_bitrate->val;
+ break;
+ }
+ return 0;
+}
+
+static int cx2341x_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ static const int mpeg_stream_type[] = {
+ 0, /* MPEG-2 PS */
+ 1, /* MPEG-2 TS */
+ 2, /* MPEG-1 SS */
+ 14, /* DVD */
+ 11, /* VCD */
+ 12, /* SVCD */
+ };
+ struct cx2341x_handler *hdl = to_cxhdl(ctrl);
+ s32 val = ctrl->val;
+ u32 props;
+ int err;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ if (hdl->ops && hdl->ops->s_stream_vbi_fmt)
+ return hdl->ops->s_stream_vbi_fmt(hdl, val);
+ return 0;
+
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ return cx2341x_hdl_api(hdl,
+ CX2341X_ENC_SET_ASPECT_RATIO, 1, val + 1);
+
+ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+ return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_GOP_CLOSURE, 1, val);
+
+ case V4L2_CID_MPEG_AUDIO_MUTE:
+ return cx2341x_hdl_api(hdl, CX2341X_ENC_MUTE_AUDIO, 1, val);
+
+ case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION:
+ return cx2341x_hdl_api(hdl,
+ CX2341X_ENC_SET_FRAME_DROP_RATE, 1, val);
+
+ case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+ return cx2341x_hdl_api(hdl, CX2341X_ENC_MISC, 2, 7, val);
+
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+ /* audio properties cluster */
+ props = (hdl->audio_sampling_freq->val << 0) |
+ (hdl->audio_mode->val << 8) |
+ (hdl->audio_mode_extension->val << 10) |
+ (hdl->audio_crc->val << 14);
+ if (hdl->audio_emphasis->val == V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17)
+ props |= 3 << 12;
+ else
+ props |= hdl->audio_emphasis->val << 12;
+
+ if (hdl->audio_encoding->val == V4L2_MPEG_AUDIO_ENCODING_AC3) {
+ props |=
+#if 1
+ /* Not sure if this MPEG Layer II setting is required */
+ ((3 - V4L2_MPEG_AUDIO_ENCODING_LAYER_2) << 2) |
+#endif
+ (hdl->audio_ac3_bitrate->val << 4) |
+ (CX2341X_AUDIO_ENCODING_METHOD_AC3 << 28);
+ } else {
+ /* Assuming MPEG Layer II */
+ props |=
+ ((3 - hdl->audio_encoding->val) << 2) |
+ ((1 + hdl->audio_l2_bitrate->val) << 4);
+ }
+ err = cx2341x_hdl_api(hdl,
+ CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, props);
+ if (err)
+ return err;
+
+ hdl->audio_properties = props;
+ if (hdl->audio_ac3_bitrate) {
+ int is_ac3 = hdl->audio_encoding->val ==
+ V4L2_MPEG_AUDIO_ENCODING_AC3;
+
+ v4l2_ctrl_activate(hdl->audio_ac3_bitrate, is_ac3);
+ v4l2_ctrl_activate(hdl->audio_l2_bitrate, !is_ac3);
+ }
+ v4l2_ctrl_activate(hdl->audio_mode_extension,
+ hdl->audio_mode->val == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO);
+ if (cx2341x_neq(hdl->audio_sampling_freq) &&
+ hdl->ops && hdl->ops->s_audio_sampling_freq)
+ return hdl->ops->s_audio_sampling_freq(hdl, hdl->audio_sampling_freq->val);
+ if (cx2341x_neq(hdl->audio_mode) &&
+ hdl->ops && hdl->ops->s_audio_mode)
+ return hdl->ops->s_audio_mode(hdl, hdl->audio_mode->val);
+ return 0;
+
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ /* video gop cluster */
+ return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_GOP_PROPERTIES, 2,
+ hdl->video_gop_size->val,
+ hdl->video_b_frames->val + 1);
+
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ /* stream type cluster */
+ err = cx2341x_hdl_api(hdl,
+ CX2341X_ENC_SET_STREAM_TYPE, 1, mpeg_stream_type[val]);
+ if (err)
+ return err;
+
+ err = cx2341x_hdl_api(hdl, CX2341X_ENC_SET_BIT_RATE, 5,
+ hdl->video_bitrate_mode->val,
+ hdl->video_bitrate->val,
+ hdl->video_bitrate_peak->val / 400, 0, 0);
+ if (err)
+ return err;
+
+ v4l2_ctrl_activate(hdl->video_bitrate_mode,
+ hdl->video_encoding->val != V4L2_MPEG_VIDEO_ENCODING_MPEG_1);
+ v4l2_ctrl_activate(hdl->video_bitrate_peak,
+ hdl->video_bitrate_mode->val != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
+ if (cx2341x_neq(hdl->video_encoding) &&
+ hdl->ops && hdl->ops->s_video_encoding)
+ return hdl->ops->s_video_encoding(hdl, hdl->video_encoding->val);
+ return 0;
+
+ case V4L2_CID_MPEG_VIDEO_MUTE:
+ /* video mute cluster */
+ return cx2341x_hdl_api(hdl, CX2341X_ENC_MUTE_VIDEO, 1,
+ hdl->video_mute->val |
+ (hdl->video_mute_yuv->val << 8));
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: {
+ int active_filter;
+
+ /* video filter mode */
+ err = cx2341x_hdl_api(hdl, CX2341X_ENC_SET_DNR_FILTER_MODE, 2,
+ hdl->video_spatial_filter_mode->val |
+ (hdl->video_temporal_filter_mode->val << 1),
+ hdl->video_median_filter_type->val);
+ if (err)
+ return err;
+
+ active_filter = hdl->video_spatial_filter_mode->val !=
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO;
+ v4l2_ctrl_activate(hdl->video_spatial_filter, active_filter);
+ v4l2_ctrl_activate(hdl->video_luma_spatial_filter_type, active_filter);
+ v4l2_ctrl_activate(hdl->video_chroma_spatial_filter_type, active_filter);
+ active_filter = hdl->video_temporal_filter_mode->val !=
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO;
+ v4l2_ctrl_activate(hdl->video_temporal_filter, active_filter);
+ active_filter = hdl->video_median_filter_type->val !=
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF;
+ v4l2_ctrl_activate(hdl->video_luma_median_filter_bottom, active_filter);
+ v4l2_ctrl_activate(hdl->video_luma_median_filter_top, active_filter);
+ v4l2_ctrl_activate(hdl->video_chroma_median_filter_bottom, active_filter);
+ v4l2_ctrl_activate(hdl->video_chroma_median_filter_top, active_filter);
+ return 0;
+ }
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+ /* video filter type cluster */
+ return cx2341x_hdl_api(hdl,
+ CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, 2,
+ hdl->video_luma_spatial_filter_type->val,
+ hdl->video_chroma_spatial_filter_type->val);
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
+ /* video filter cluster */
+ return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_DNR_FILTER_PROPS, 2,
+ hdl->video_spatial_filter->val,
+ hdl->video_temporal_filter->val);
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
+ /* video median cluster */
+ return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_CORING_LEVELS, 4,
+ hdl->video_luma_median_filter_bottom->val,
+ hdl->video_luma_median_filter_top->val,
+ hdl->video_chroma_median_filter_bottom->val,
+ hdl->video_chroma_median_filter_top->val);
+ }
+ return -EINVAL;
+}
+
+static const struct v4l2_ctrl_ops cx2341x_ops = {
+ .try_ctrl = cx2341x_try_ctrl,
+ .s_ctrl = cx2341x_s_ctrl,
+};
+
+static struct v4l2_ctrl *cx2341x_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
+ u32 id, s32 min, s32 max, s32 step, s32 def)
+{
+ struct v4l2_ctrl_config cfg;
+
+ cx2341x_ctrl_fill(id, &cfg.name, &cfg.type, &min, &max, &step, &def, &cfg.flags);
+ cfg.ops = &cx2341x_ops;
+ cfg.id = id;
+ cfg.min = min;
+ cfg.max = max;
+ cfg.def = def;
+ if (cfg.type == V4L2_CTRL_TYPE_MENU) {
+ cfg.step = 0;
+ cfg.menu_skip_mask = step;
+ cfg.qmenu = cx2341x_get_menu(id);
+ } else {
+ cfg.step = step;
+ cfg.menu_skip_mask = 0;
+ }
+ return v4l2_ctrl_new_custom(hdl, &cfg, NULL);
+}
+
+static struct v4l2_ctrl *cx2341x_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
+ u32 id, s32 min, s32 max, s32 step, s32 def)
+{
+ return v4l2_ctrl_new_std(hdl, &cx2341x_ops, id, min, max, step, def);
+}
+
+static struct v4l2_ctrl *cx2341x_ctrl_new_menu(struct v4l2_ctrl_handler *hdl,
+ u32 id, s32 max, s32 mask, s32 def)
+{
+ return v4l2_ctrl_new_std_menu(hdl, &cx2341x_ops, id, max, mask, def);
+}
+
+int cx2341x_handler_init(struct cx2341x_handler *cxhdl,
+ unsigned nr_of_controls_hint)
+{
+ struct v4l2_ctrl_handler *hdl = &cxhdl->hdl;
+ u32 caps = cxhdl->capabilities;
+ int has_sliced_vbi = caps & CX2341X_CAP_HAS_SLICED_VBI;
+ int has_ac3 = caps & CX2341X_CAP_HAS_AC3;
+ int has_ts = caps & CX2341X_CAP_HAS_TS;
+
+ cxhdl->width = 720;
+ cxhdl->height = 480;
+
+ v4l2_ctrl_handler_init(hdl, nr_of_controls_hint);
+
+ /* Add controls in ascending control ID order for fastest
+ insertion time. */
+ cxhdl->stream_type = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_STREAM_TYPE,
+ V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, has_ts ? 0 : 2,
+ V4L2_MPEG_STREAM_TYPE_MPEG2_PS);
+ cxhdl->stream_vbi_fmt = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_STREAM_VBI_FMT,
+ V4L2_MPEG_STREAM_VBI_FMT_IVTV, has_sliced_vbi ? 0 : 2,
+ V4L2_MPEG_STREAM_VBI_FMT_NONE);
+ cxhdl->audio_sampling_freq = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ,
+ V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, 0,
+ V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000);
+ cxhdl->audio_encoding = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_ENCODING,
+ V4L2_MPEG_AUDIO_ENCODING_AC3, has_ac3 ? ~0x12 : ~0x2,
+ V4L2_MPEG_AUDIO_ENCODING_LAYER_2);
+ cxhdl->audio_l2_bitrate = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_L2_BITRATE,
+ V4L2_MPEG_AUDIO_L2_BITRATE_384K, 0x1ff,
+ V4L2_MPEG_AUDIO_L2_BITRATE_224K);
+ cxhdl->audio_mode = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_MODE,
+ V4L2_MPEG_AUDIO_MODE_MONO, 0,
+ V4L2_MPEG_AUDIO_MODE_STEREO);
+ cxhdl->audio_mode_extension = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_MODE_EXTENSION,
+ V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, 0,
+ V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4);
+ cxhdl->audio_emphasis = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_EMPHASIS,
+ V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, 0,
+ V4L2_MPEG_AUDIO_EMPHASIS_NONE);
+ cxhdl->audio_crc = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_CRC,
+ V4L2_MPEG_AUDIO_CRC_CRC16, 0,
+ V4L2_MPEG_AUDIO_CRC_NONE);
+
+ cx2341x_ctrl_new_std(hdl, V4L2_CID_MPEG_AUDIO_MUTE, 0, 1, 1, 0);
+ if (has_ac3)
+ cxhdl->audio_ac3_bitrate = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_AC3_BITRATE,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_448K, 0x03,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_224K);
+ cxhdl->video_encoding = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_VIDEO_ENCODING,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 0,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2);
+ cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_VIDEO_ASPECT,
+ V4L2_MPEG_VIDEO_ASPECT_221x100, 0,
+ V4L2_MPEG_VIDEO_ASPECT_4x3);
+ cxhdl->video_b_frames = cx2341x_ctrl_new_std(hdl,
+ V4L2_CID_MPEG_VIDEO_B_FRAMES, 0, 33, 1, 2);
+ cxhdl->video_gop_size = cx2341x_ctrl_new_std(hdl,
+ V4L2_CID_MPEG_VIDEO_GOP_SIZE,
+ 1, 34, 1, cxhdl->is_50hz ? 12 : 15);
+ cx2341x_ctrl_new_std(hdl, V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, 0, 1, 1, 1);
+ cxhdl->video_bitrate_mode = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
+ cxhdl->video_bitrate = cx2341x_ctrl_new_std(hdl,
+ V4L2_CID_MPEG_VIDEO_BITRATE,
+ 0, 27000000, 1, 6000000);
+ cxhdl->video_bitrate_peak = cx2341x_ctrl_new_std(hdl,
+ V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
+ 0, 27000000, 1, 8000000);
+ cx2341x_ctrl_new_std(hdl,
+ V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION, 0, 255, 1, 0);
+ cxhdl->video_mute = cx2341x_ctrl_new_std(hdl,
+ V4L2_CID_MPEG_VIDEO_MUTE, 0, 1, 1, 0);
+ cxhdl->video_mute_yuv = cx2341x_ctrl_new_std(hdl,
+ V4L2_CID_MPEG_VIDEO_MUTE_YUV, 0, 0xffffff, 1, 0x008080);
+
+ /* CX23415/6 specific */
+ cxhdl->video_spatial_filter_mode = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE,
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL,
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, 0,
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL);
+ cxhdl->video_spatial_filter = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER,
+ 0, 15, 1, 0);
+ cxhdl->video_luma_spatial_filter_type = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE,
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF,
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE,
+ 0,
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR);
+ cxhdl->video_chroma_spatial_filter_type = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE,
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF,
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR,
+ 0,
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR);
+ cxhdl->video_temporal_filter_mode = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE,
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL,
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO,
+ 0,
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL);
+ cxhdl->video_temporal_filter = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER,
+ 0, 31, 1, 8);
+ cxhdl->video_median_filter_type = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE,
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF,
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG,
+ 0,
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF);
+ cxhdl->video_luma_median_filter_bottom = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM,
+ 0, 255, 1, 0);
+ cxhdl->video_luma_median_filter_top = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP,
+ 0, 255, 1, 255);
+ cxhdl->video_chroma_median_filter_bottom = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM,
+ 0, 255, 1, 0);
+ cxhdl->video_chroma_median_filter_top = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP,
+ 0, 255, 1, 255);
+ cx2341x_ctrl_new_custom(hdl, V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS,
+ 0, 1, 1, 0);
+
+ if (hdl->error) {
+ int err = hdl->error;
+
+ v4l2_ctrl_handler_free(hdl);
+ return err;
+ }
+
+ v4l2_ctrl_cluster(8, &cxhdl->audio_sampling_freq);
+ v4l2_ctrl_cluster(2, &cxhdl->video_b_frames);
+ v4l2_ctrl_cluster(5, &cxhdl->stream_type);
+ v4l2_ctrl_cluster(2, &cxhdl->video_mute);
+ v4l2_ctrl_cluster(3, &cxhdl->video_spatial_filter_mode);
+ v4l2_ctrl_cluster(2, &cxhdl->video_luma_spatial_filter_type);
+ v4l2_ctrl_cluster(2, &cxhdl->video_spatial_filter);
+ v4l2_ctrl_cluster(4, &cxhdl->video_luma_median_filter_top);
+
+ return 0;
+}
+EXPORT_SYMBOL(cx2341x_handler_init);
+
+void cx2341x_handler_set_50hz(struct cx2341x_handler *cxhdl, int is_50hz)
+{
+ cxhdl->is_50hz = is_50hz;
+ cxhdl->video_gop_size->default_value = cxhdl->is_50hz ? 12 : 15;
+}
+EXPORT_SYMBOL(cx2341x_handler_set_50hz);
+
+int cx2341x_handler_setup(struct cx2341x_handler *cxhdl)
+{
+ int h = cxhdl->height;
+ int w = cxhdl->width;
+ int err;
+
+ err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_OUTPUT_PORT, 2, cxhdl->port, 0);
+ if (err)
+ return err;
+ err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_FRAME_RATE, 1, cxhdl->is_50hz);
+ if (err)
+ return err;
+
+ if (v4l2_ctrl_g_ctrl(cxhdl->video_encoding) == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) {
+ w /= 2;
+ h /= 2;
+ }
+ err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_FRAME_SIZE, 2, h, w);
+ if (err)
+ return err;
+ return v4l2_ctrl_handler_setup(&cxhdl->hdl);
+}
+EXPORT_SYMBOL(cx2341x_handler_setup);
+
+void cx2341x_handler_set_busy(struct cx2341x_handler *cxhdl, int busy)
+{
+ v4l2_ctrl_grab(cxhdl->audio_sampling_freq, busy);
+ v4l2_ctrl_grab(cxhdl->audio_encoding, busy);
+ v4l2_ctrl_grab(cxhdl->audio_l2_bitrate, busy);
+ v4l2_ctrl_grab(cxhdl->audio_ac3_bitrate, busy);
+ v4l2_ctrl_grab(cxhdl->stream_vbi_fmt, busy);
+ v4l2_ctrl_grab(cxhdl->stream_type, busy);
+ v4l2_ctrl_grab(cxhdl->video_bitrate_mode, busy);
+ v4l2_ctrl_grab(cxhdl->video_bitrate, busy);
+ v4l2_ctrl_grab(cxhdl->video_bitrate_peak, busy);
+}
+EXPORT_SYMBOL(cx2341x_handler_set_busy);
diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig
new file mode 100644
index 00000000..b391e9bd
--- /dev/null
+++ b/drivers/media/video/cx23885/Kconfig
@@ -0,0 +1,46 @@
+config VIDEO_CX23885
+ tristate "Conexant cx23885 (2388x successor) support"
+ depends on DVB_CORE && VIDEO_DEV && PCI && I2C && INPUT && SND
+ select SND_PCM
+ select I2C_ALGOBIT
+ select VIDEO_BTCX
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ depends on RC_CORE
+ select VIDEOBUF_DVB
+ select VIDEOBUF_DMA_SG
+ select VIDEO_CX25840
+ select VIDEO_CX2341X
+ select DVB_DIB7000P if !DVB_FE_CUSTOMISE
+ select DVB_S5H1409 if !DVB_FE_CUSTOMISE
+ select DVB_S5H1411 if !DVB_FE_CUSTOMISE
+ select DVB_LGDT330X if !DVB_FE_CUSTOMISE
+ select DVB_ZL10353 if !DVB_FE_CUSTOMISE
+ select DVB_TDA10048 if !DVB_FE_CUSTOMISE
+ select DVB_LNBP21 if !DVB_FE_CUSTOMISE
+ select DVB_STV6110 if !DVB_FE_CUSTOMISE
+ select DVB_CX24116 if !DVB_FE_CUSTOMISE
+ select DVB_STV0900 if !DVB_FE_CUSTOMISE
+ select DVB_DS3000 if !DVB_FE_CUSTOMISE
+ select DVB_STV0367 if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMISE
+ select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE
+ select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE
+ select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE
+ select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE
+ ---help---
+ This is a video4linux driver for Conexant 23885 based
+ TV cards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cx23885
+
+config MEDIA_ALTERA_CI
+ tristate "Altera FPGA based CI module"
+ depends on VIDEO_CX23885 && DVB_CORE
+ select ALTERA_STAPL
+ ---help---
+ An Altera FPGA CI module for NetUP Dual DVB-T/C RF CI card.
+
+ To compile this driver as a module, choose M here: the
+ module will be called altera-ci
diff --git a/drivers/media/video/cx23885/Makefile b/drivers/media/video/cx23885/Makefile
new file mode 100644
index 00000000..f81f2796
--- /dev/null
+++ b/drivers/media/video/cx23885/Makefile
@@ -0,0 +1,15 @@
+cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o \
+ cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o \
+ cx23885-ioctl.o cx23885-ir.o cx23885-av.o cx23885-input.o \
+ cx23888-ir.o netup-init.o cimax2.o netup-eeprom.o \
+ cx23885-f300.o cx23885-alsa.o
+
+obj-$(CONFIG_VIDEO_CX23885) += cx23885.o
+obj-$(CONFIG_MEDIA_ALTERA_CI) += altera-ci.o
+
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
+
+ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/video/cx23885/altera-ci.c b/drivers/media/video/cx23885/altera-ci.c
new file mode 100644
index 00000000..1fa8927f
--- /dev/null
+++ b/drivers/media/video/cx23885/altera-ci.c
@@ -0,0 +1,837 @@
+/*
+ * altera-ci.c
+ *
+ * CI driver in conjunction with NetUp Dual DVB-T/C RF CI card
+ *
+ * Copyright (C) 2010,2011 NetUP Inc.
+ * Copyright (C) 2010,2011 Igor M. Liplianin <liplianin@netup.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * currently cx23885 GPIO's used.
+ * GPIO-0 ~INT in
+ * GPIO-1 TMS out
+ * GPIO-2 ~reset chips out
+ * GPIO-3 to GPIO-10 data/addr for CA in/out
+ * GPIO-11 ~CS out
+ * GPIO-12 AD_RG out
+ * GPIO-13 ~WR out
+ * GPIO-14 ~RD out
+ * GPIO-15 ~RDY in
+ * GPIO-16 TCK out
+ * GPIO-17 TDO in
+ * GPIO-18 TDI out
+ */
+/*
+ * Bit definitions for MC417_RWD and MC417_OEN registers
+ * bits 31-16
+ * +-----------+
+ * | Reserved |
+ * +-----------+
+ * bit 15 bit 14 bit 13 bit 12 bit 11 bit 10 bit 9 bit 8
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * | TDI | TDO | TCK | RDY# | #RD | #WR | AD_RG | #CS |
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * | DATA7| DATA6| DATA5| DATA4| DATA3| DATA2| DATA1| DATA0|
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ */
+#include <media/videobuf-dma-sg.h>
+#include <media/videobuf-dvb.h>
+#include "altera-ci.h"
+#include "dvb_ca_en50221.h"
+
+/* FPGA regs */
+#define NETUP_CI_INT_CTRL 0x00
+#define NETUP_CI_BUSCTRL2 0x01
+#define NETUP_CI_ADDR0 0x04
+#define NETUP_CI_ADDR1 0x05
+#define NETUP_CI_DATA 0x06
+#define NETUP_CI_BUSCTRL 0x07
+#define NETUP_CI_PID_ADDR0 0x08
+#define NETUP_CI_PID_ADDR1 0x09
+#define NETUP_CI_PID_DATA 0x0a
+#define NETUP_CI_TSA_DIV 0x0c
+#define NETUP_CI_TSB_DIV 0x0d
+#define NETUP_CI_REVISION 0x0f
+
+/* const for ci op */
+#define NETUP_CI_FLG_CTL 1
+#define NETUP_CI_FLG_RD 1
+#define NETUP_CI_FLG_AD 1
+
+static unsigned int ci_dbg;
+module_param(ci_dbg, int, 0644);
+MODULE_PARM_DESC(ci_dbg, "Enable CI debugging");
+
+static unsigned int pid_dbg;
+module_param(pid_dbg, int, 0644);
+MODULE_PARM_DESC(pid_dbg, "Enable PID filtering debugging");
+
+MODULE_DESCRIPTION("altera FPGA CI module");
+MODULE_AUTHOR("Igor M. Liplianin <liplianin@netup.ru>");
+MODULE_LICENSE("GPL");
+
+#define ci_dbg_print(args...) \
+ do { \
+ if (ci_dbg) \
+ printk(KERN_DEBUG args); \
+ } while (0)
+
+#define pid_dbg_print(args...) \
+ do { \
+ if (pid_dbg) \
+ printk(KERN_DEBUG args); \
+ } while (0)
+
+struct altera_ci_state;
+struct netup_hw_pid_filter;
+
+struct fpga_internal {
+ void *dev;
+ struct mutex fpga_mutex;/* two CI's on the same fpga */
+ struct netup_hw_pid_filter *pid_filt[2];
+ struct altera_ci_state *state[2];
+ struct work_struct work;
+ int (*fpga_rw) (void *dev, int flag, int data, int rw);
+ int cis_used;
+ int filts_used;
+ int strt_wrk;
+};
+
+/* stores all private variables for communication with CI */
+struct altera_ci_state {
+ struct fpga_internal *internal;
+ struct dvb_ca_en50221 ca;
+ int status;
+ int nr;
+};
+
+/* stores all private variables for hardware pid filtering */
+struct netup_hw_pid_filter {
+ struct fpga_internal *internal;
+ struct dvb_demux *demux;
+ /* save old functions */
+ int (*start_feed)(struct dvb_demux_feed *feed);
+ int (*stop_feed)(struct dvb_demux_feed *feed);
+
+ int status;
+ int nr;
+};
+
+/* internal params node */
+struct fpga_inode {
+ /* pointer for internal params, one for each pair of CI's */
+ struct fpga_internal *internal;
+ struct fpga_inode *next_inode;
+};
+
+/* first internal params */
+static struct fpga_inode *fpga_first_inode;
+
+/* find chip by dev */
+static struct fpga_inode *find_inode(void *dev)
+{
+ struct fpga_inode *temp_chip = fpga_first_inode;
+
+ if (temp_chip == NULL)
+ return temp_chip;
+
+ /*
+ Search for the last fpga CI chip or
+ find it by dev */
+ while ((temp_chip != NULL) &&
+ (temp_chip->internal->dev != dev))
+ temp_chip = temp_chip->next_inode;
+
+ return temp_chip;
+}
+/* check demux */
+static struct fpga_internal *check_filter(struct fpga_internal *temp_int,
+ void *demux_dev, int filt_nr)
+{
+ if (temp_int == NULL)
+ return NULL;
+
+ if ((temp_int->pid_filt[filt_nr]) == NULL)
+ return NULL;
+
+ if (temp_int->pid_filt[filt_nr]->demux == demux_dev)
+ return temp_int;
+
+ return NULL;
+}
+
+/* find chip by demux */
+static struct fpga_inode *find_dinode(void *demux_dev)
+{
+ struct fpga_inode *temp_chip = fpga_first_inode;
+ struct fpga_internal *temp_int;
+
+ /*
+ * Search of the last fpga CI chip or
+ * find it by demux
+ */
+ while (temp_chip != NULL) {
+ if (temp_chip->internal != NULL) {
+ temp_int = temp_chip->internal;
+ if (check_filter(temp_int, demux_dev, 0))
+ break;
+ if (check_filter(temp_int, demux_dev, 1))
+ break;
+ }
+
+ temp_chip = temp_chip->next_inode;
+ }
+
+ return temp_chip;
+}
+
+/* deallocating chip */
+static void remove_inode(struct fpga_internal *internal)
+{
+ struct fpga_inode *prev_node = fpga_first_inode;
+ struct fpga_inode *del_node = find_inode(internal->dev);
+
+ if (del_node != NULL) {
+ if (del_node == fpga_first_inode) {
+ fpga_first_inode = del_node->next_inode;
+ } else {
+ while (prev_node->next_inode != del_node)
+ prev_node = prev_node->next_inode;
+
+ if (del_node->next_inode == NULL)
+ prev_node->next_inode = NULL;
+ else
+ prev_node->next_inode =
+ prev_node->next_inode->next_inode;
+ }
+
+ kfree(del_node);
+ }
+}
+
+/* allocating new chip */
+static struct fpga_inode *append_internal(struct fpga_internal *internal)
+{
+ struct fpga_inode *new_node = fpga_first_inode;
+
+ if (new_node == NULL) {
+ new_node = kmalloc(sizeof(struct fpga_inode), GFP_KERNEL);
+ fpga_first_inode = new_node;
+ } else {
+ while (new_node->next_inode != NULL)
+ new_node = new_node->next_inode;
+
+ new_node->next_inode =
+ kmalloc(sizeof(struct fpga_inode), GFP_KERNEL);
+ if (new_node->next_inode != NULL)
+ new_node = new_node->next_inode;
+ else
+ new_node = NULL;
+ }
+
+ if (new_node != NULL) {
+ new_node->internal = internal;
+ new_node->next_inode = NULL;
+ }
+
+ return new_node;
+}
+
+static int netup_fpga_op_rw(struct fpga_internal *inter, int addr,
+ u8 val, u8 read)
+{
+ inter->fpga_rw(inter->dev, NETUP_CI_FLG_AD, addr, 0);
+ return inter->fpga_rw(inter->dev, 0, val, read);
+}
+
+/* flag - mem/io, read - read/write */
+int altera_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot,
+ u8 flag, u8 read, int addr, u8 val)
+{
+
+ struct altera_ci_state *state = en50221->data;
+ struct fpga_internal *inter = state->internal;
+
+ u8 store;
+ int mem = 0;
+
+ if (0 != slot)
+ return -EINVAL;
+
+ mutex_lock(&inter->fpga_mutex);
+
+ netup_fpga_op_rw(inter, NETUP_CI_ADDR0, ((addr << 1) & 0xfe), 0);
+ netup_fpga_op_rw(inter, NETUP_CI_ADDR1, ((addr >> 7) & 0x7f), 0);
+ store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD);
+
+ store &= 0x0f;
+ store |= ((state->nr << 7) | (flag << 6));
+
+ netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, store, 0);
+ mem = netup_fpga_op_rw(inter, NETUP_CI_DATA, val, read);
+
+ mutex_unlock(&inter->fpga_mutex);
+
+ ci_dbg_print("%s: %s: addr=[0x%02x], %s=%x\n", __func__,
+ (read) ? "read" : "write", addr,
+ (flag == NETUP_CI_FLG_CTL) ? "ctl" : "mem",
+ (read) ? mem : val);
+
+ return mem;
+}
+
+int altera_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221,
+ int slot, int addr)
+{
+ return altera_ci_op_cam(en50221, slot, 0, NETUP_CI_FLG_RD, addr, 0);
+}
+
+int altera_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221,
+ int slot, int addr, u8 data)
+{
+ return altera_ci_op_cam(en50221, slot, 0, 0, addr, data);
+}
+
+int altera_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, u8 addr)
+{
+ return altera_ci_op_cam(en50221, slot, NETUP_CI_FLG_CTL,
+ NETUP_CI_FLG_RD, addr, 0);
+}
+
+int altera_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221, int slot,
+ u8 addr, u8 data)
+{
+ return altera_ci_op_cam(en50221, slot, NETUP_CI_FLG_CTL, 0, addr, data);
+}
+
+int altera_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot)
+{
+ struct altera_ci_state *state = en50221->data;
+ struct fpga_internal *inter = state->internal;
+ /* reasonable timeout for CI reset is 10 seconds */
+ unsigned long t_out = jiffies + msecs_to_jiffies(9999);
+ int ret;
+
+ ci_dbg_print("%s\n", __func__);
+
+ if (0 != slot)
+ return -EINVAL;
+
+ mutex_lock(&inter->fpga_mutex);
+
+ ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD);
+ netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL,
+ (ret & 0xcf) | (1 << (5 - state->nr)), 0);
+
+ mutex_unlock(&inter->fpga_mutex);
+
+ for (;;) {
+ mdelay(50);
+
+ mutex_lock(&inter->fpga_mutex);
+
+ ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL,
+ 0, NETUP_CI_FLG_RD);
+ mutex_unlock(&inter->fpga_mutex);
+
+ if ((ret & (1 << (5 - state->nr))) == 0)
+ break;
+ if (time_after(jiffies, t_out))
+ break;
+ }
+
+
+ ci_dbg_print("%s: %d msecs\n", __func__,
+ jiffies_to_msecs(jiffies + msecs_to_jiffies(9999) - t_out));
+
+ return 0;
+}
+
+int altera_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot)
+{
+ /* not implemented */
+ return 0;
+}
+
+int altera_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, int slot)
+{
+ struct altera_ci_state *state = en50221->data;
+ struct fpga_internal *inter = state->internal;
+ int ret;
+
+ ci_dbg_print("%s\n", __func__);
+
+ if (0 != slot)
+ return -EINVAL;
+
+ mutex_lock(&inter->fpga_mutex);
+
+ ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD);
+ netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL,
+ (ret & 0x0f) | (1 << (3 - state->nr)), 0);
+
+ mutex_unlock(&inter->fpga_mutex);
+
+ return 0;
+}
+
+/* work handler */
+static void netup_read_ci_status(struct work_struct *work)
+{
+ struct fpga_internal *inter =
+ container_of(work, struct fpga_internal, work);
+ int ret;
+
+ ci_dbg_print("%s\n", __func__);
+
+ mutex_lock(&inter->fpga_mutex);
+ /* ack' irq */
+ ret = netup_fpga_op_rw(inter, NETUP_CI_INT_CTRL, 0, NETUP_CI_FLG_RD);
+ ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD);
+
+ mutex_unlock(&inter->fpga_mutex);
+
+ if (inter->state[1] != NULL) {
+ inter->state[1]->status =
+ ((ret & 1) == 0 ?
+ DVB_CA_EN50221_POLL_CAM_PRESENT |
+ DVB_CA_EN50221_POLL_CAM_READY : 0);
+ ci_dbg_print("%s: setting CI[1] status = 0x%x\n",
+ __func__, inter->state[1]->status);
+ };
+
+ if (inter->state[0] != NULL) {
+ inter->state[0]->status =
+ ((ret & 2) == 0 ?
+ DVB_CA_EN50221_POLL_CAM_PRESENT |
+ DVB_CA_EN50221_POLL_CAM_READY : 0);
+ ci_dbg_print("%s: setting CI[0] status = 0x%x\n",
+ __func__, inter->state[0]->status);
+ };
+}
+
+/* CI irq handler */
+int altera_ci_irq(void *dev)
+{
+ struct fpga_inode *temp_int = NULL;
+ struct fpga_internal *inter = NULL;
+
+ ci_dbg_print("%s\n", __func__);
+
+ if (dev != NULL) {
+ temp_int = find_inode(dev);
+ if (temp_int != NULL) {
+ inter = temp_int->internal;
+ schedule_work(&inter->work);
+ }
+ }
+
+ return 1;
+}
+EXPORT_SYMBOL(altera_ci_irq);
+
+int altera_poll_ci_slot_status(struct dvb_ca_en50221 *en50221, int slot,
+ int open)
+{
+ struct altera_ci_state *state = en50221->data;
+
+ if (0 != slot)
+ return -EINVAL;
+
+ return state->status;
+}
+
+void altera_hw_filt_release(void *main_dev, int filt_nr)
+{
+ struct fpga_inode *temp_int = find_inode(main_dev);
+ struct netup_hw_pid_filter *pid_filt = NULL;
+
+ ci_dbg_print("%s\n", __func__);
+
+ if (temp_int != NULL) {
+ pid_filt = temp_int->internal->pid_filt[filt_nr - 1];
+ /* stored old feed controls */
+ pid_filt->demux->start_feed = pid_filt->start_feed;
+ pid_filt->demux->stop_feed = pid_filt->stop_feed;
+
+ if (((--(temp_int->internal->filts_used)) <= 0) &&
+ ((temp_int->internal->cis_used) <= 0)) {
+
+ ci_dbg_print("%s: Actually removing\n", __func__);
+
+ remove_inode(temp_int->internal);
+ kfree(pid_filt->internal);
+ }
+
+ kfree(pid_filt);
+
+ }
+
+}
+EXPORT_SYMBOL(altera_hw_filt_release);
+
+void altera_ci_release(void *dev, int ci_nr)
+{
+ struct fpga_inode *temp_int = find_inode(dev);
+ struct altera_ci_state *state = NULL;
+
+ ci_dbg_print("%s\n", __func__);
+
+ if (temp_int != NULL) {
+ state = temp_int->internal->state[ci_nr - 1];
+ altera_hw_filt_release(dev, ci_nr);
+
+
+ if (((temp_int->internal->filts_used) <= 0) &&
+ ((--(temp_int->internal->cis_used)) <= 0)) {
+
+ ci_dbg_print("%s: Actually removing\n", __func__);
+
+ remove_inode(temp_int->internal);
+ kfree(state->internal);
+ }
+
+ if (state != NULL) {
+ if (state->ca.data != NULL)
+ dvb_ca_en50221_release(&state->ca);
+
+ kfree(state);
+ }
+ }
+
+}
+EXPORT_SYMBOL(altera_ci_release);
+
+static void altera_pid_control(struct netup_hw_pid_filter *pid_filt,
+ u16 pid, int onoff)
+{
+ struct fpga_internal *inter = pid_filt->internal;
+ u8 store = 0;
+
+ /* pid 0-0x1f always enabled, don't touch them */
+ if ((pid == 0x2000) || (pid < 0x20))
+ return;
+
+ mutex_lock(&inter->fpga_mutex);
+
+ netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR0, (pid >> 3) & 0xff, 0);
+ netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR1,
+ ((pid >> 11) & 0x03) | (pid_filt->nr << 2), 0);
+
+ store = netup_fpga_op_rw(inter, NETUP_CI_PID_DATA, 0, NETUP_CI_FLG_RD);
+
+ if (onoff)/* 0 - on, 1 - off */
+ store |= (1 << (pid & 7));
+ else
+ store &= ~(1 << (pid & 7));
+
+ netup_fpga_op_rw(inter, NETUP_CI_PID_DATA, store, 0);
+
+ mutex_unlock(&inter->fpga_mutex);
+
+ pid_dbg_print("%s: (%d) set pid: %5d 0x%04x '%s'\n", __func__,
+ pid_filt->nr, pid, pid, onoff ? "off" : "on");
+}
+
+static void altera_toggle_fullts_streaming(struct netup_hw_pid_filter *pid_filt,
+ int filt_nr, int onoff)
+{
+ struct fpga_internal *inter = pid_filt->internal;
+ u8 store = 0;
+ int i;
+
+ pid_dbg_print("%s: pid_filt->nr[%d] now %s\n", __func__, pid_filt->nr,
+ onoff ? "off" : "on");
+
+ if (onoff)/* 0 - on, 1 - off */
+ store = 0xff;/* ignore pid */
+ else
+ store = 0;/* enable pid */
+
+ mutex_lock(&inter->fpga_mutex);
+
+ for (i = 0; i < 1024; i++) {
+ netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR0, i & 0xff, 0);
+
+ netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR1,
+ ((i >> 8) & 0x03) | (pid_filt->nr << 2), 0);
+ /* pid 0-0x1f always enabled */
+ netup_fpga_op_rw(inter, NETUP_CI_PID_DATA,
+ (i > 3 ? store : 0), 0);
+ }
+
+ mutex_unlock(&inter->fpga_mutex);
+}
+
+int altera_pid_feed_control(void *demux_dev, int filt_nr,
+ struct dvb_demux_feed *feed, int onoff)
+{
+ struct fpga_inode *temp_int = find_dinode(demux_dev);
+ struct fpga_internal *inter = temp_int->internal;
+ struct netup_hw_pid_filter *pid_filt = inter->pid_filt[filt_nr - 1];
+
+ altera_pid_control(pid_filt, feed->pid, onoff ? 0 : 1);
+ /* call old feed proc's */
+ if (onoff)
+ pid_filt->start_feed(feed);
+ else
+ pid_filt->stop_feed(feed);
+
+ if (feed->pid == 0x2000)
+ altera_toggle_fullts_streaming(pid_filt, filt_nr,
+ onoff ? 0 : 1);
+
+ return 0;
+}
+EXPORT_SYMBOL(altera_pid_feed_control);
+
+int altera_ci_start_feed(struct dvb_demux_feed *feed, int num)
+{
+ altera_pid_feed_control(feed->demux, num, feed, 1);
+
+ return 0;
+}
+
+int altera_ci_stop_feed(struct dvb_demux_feed *feed, int num)
+{
+ altera_pid_feed_control(feed->demux, num, feed, 0);
+
+ return 0;
+}
+
+int altera_ci_start_feed_1(struct dvb_demux_feed *feed)
+{
+ return altera_ci_start_feed(feed, 1);
+}
+
+int altera_ci_stop_feed_1(struct dvb_demux_feed *feed)
+{
+ return altera_ci_stop_feed(feed, 1);
+}
+
+int altera_ci_start_feed_2(struct dvb_demux_feed *feed)
+{
+ return altera_ci_start_feed(feed, 2);
+}
+
+int altera_ci_stop_feed_2(struct dvb_demux_feed *feed)
+{
+ return altera_ci_stop_feed(feed, 2);
+}
+
+int altera_hw_filt_init(struct altera_ci_config *config, int hw_filt_nr)
+{
+ struct netup_hw_pid_filter *pid_filt = NULL;
+ struct fpga_inode *temp_int = find_inode(config->dev);
+ struct fpga_internal *inter = NULL;
+ int ret = 0;
+
+ pid_filt = kzalloc(sizeof(struct netup_hw_pid_filter), GFP_KERNEL);
+
+ ci_dbg_print("%s\n", __func__);
+
+ if (!pid_filt) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ if (temp_int != NULL) {
+ inter = temp_int->internal;
+ (inter->filts_used)++;
+ ci_dbg_print("%s: Find Internal Structure!\n", __func__);
+ } else {
+ inter = kzalloc(sizeof(struct fpga_internal), GFP_KERNEL);
+ if (!inter) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ temp_int = append_internal(inter);
+ inter->filts_used = 1;
+ inter->dev = config->dev;
+ inter->fpga_rw = config->fpga_rw;
+ mutex_init(&inter->fpga_mutex);
+ inter->strt_wrk = 1;
+ ci_dbg_print("%s: Create New Internal Structure!\n", __func__);
+ }
+
+ ci_dbg_print("%s: setting hw pid filter = %p for ci = %d\n", __func__,
+ pid_filt, hw_filt_nr - 1);
+ inter->pid_filt[hw_filt_nr - 1] = pid_filt;
+ pid_filt->demux = config->demux;
+ pid_filt->internal = inter;
+ pid_filt->nr = hw_filt_nr - 1;
+ /* store old feed controls */
+ pid_filt->start_feed = config->demux->start_feed;
+ pid_filt->stop_feed = config->demux->stop_feed;
+ /* replace with new feed controls */
+ if (hw_filt_nr == 1) {
+ pid_filt->demux->start_feed = altera_ci_start_feed_1;
+ pid_filt->demux->stop_feed = altera_ci_stop_feed_1;
+ } else if (hw_filt_nr == 2) {
+ pid_filt->demux->start_feed = altera_ci_start_feed_2;
+ pid_filt->demux->stop_feed = altera_ci_stop_feed_2;
+ }
+
+ altera_toggle_fullts_streaming(pid_filt, 0, 1);
+
+ return 0;
+err:
+ ci_dbg_print("%s: Can't init hardware filter: Error %d\n",
+ __func__, ret);
+
+ kfree(pid_filt);
+
+ return ret;
+}
+EXPORT_SYMBOL(altera_hw_filt_init);
+
+int altera_ci_init(struct altera_ci_config *config, int ci_nr)
+{
+ struct altera_ci_state *state;
+ struct fpga_inode *temp_int = find_inode(config->dev);
+ struct fpga_internal *inter = NULL;
+ int ret = 0;
+ u8 store = 0;
+
+ state = kzalloc(sizeof(struct altera_ci_state), GFP_KERNEL);
+
+ ci_dbg_print("%s\n", __func__);
+
+ if (!state) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ if (temp_int != NULL) {
+ inter = temp_int->internal;
+ (inter->cis_used)++;
+ ci_dbg_print("%s: Find Internal Structure!\n", __func__);
+ } else {
+ inter = kzalloc(sizeof(struct fpga_internal), GFP_KERNEL);
+ if (!inter) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ temp_int = append_internal(inter);
+ inter->cis_used = 1;
+ inter->dev = config->dev;
+ inter->fpga_rw = config->fpga_rw;
+ mutex_init(&inter->fpga_mutex);
+ inter->strt_wrk = 1;
+ ci_dbg_print("%s: Create New Internal Structure!\n", __func__);
+ }
+
+ ci_dbg_print("%s: setting state = %p for ci = %d\n", __func__,
+ state, ci_nr - 1);
+ inter->state[ci_nr - 1] = state;
+ state->internal = inter;
+ state->nr = ci_nr - 1;
+
+ state->ca.owner = THIS_MODULE;
+ state->ca.read_attribute_mem = altera_ci_read_attribute_mem;
+ state->ca.write_attribute_mem = altera_ci_write_attribute_mem;
+ state->ca.read_cam_control = altera_ci_read_cam_ctl;
+ state->ca.write_cam_control = altera_ci_write_cam_ctl;
+ state->ca.slot_reset = altera_ci_slot_reset;
+ state->ca.slot_shutdown = altera_ci_slot_shutdown;
+ state->ca.slot_ts_enable = altera_ci_slot_ts_ctl;
+ state->ca.poll_slot_status = altera_poll_ci_slot_status;
+ state->ca.data = state;
+
+ ret = dvb_ca_en50221_init(config->adapter,
+ &state->ca,
+ /* flags */ 0,
+ /* n_slots */ 1);
+ if (0 != ret)
+ goto err;
+
+ altera_hw_filt_init(config, ci_nr);
+
+ if (inter->strt_wrk) {
+ INIT_WORK(&inter->work, netup_read_ci_status);
+ inter->strt_wrk = 0;
+ }
+
+ ci_dbg_print("%s: CI initialized!\n", __func__);
+
+ mutex_lock(&inter->fpga_mutex);
+
+ /* Enable div */
+ netup_fpga_op_rw(inter, NETUP_CI_TSA_DIV, 0x0, 0);
+ netup_fpga_op_rw(inter, NETUP_CI_TSB_DIV, 0x0, 0);
+
+ /* enable TS out */
+ store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, 0, NETUP_CI_FLG_RD);
+ store |= (3 << 4);
+ netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, store, 0);
+
+ ret = netup_fpga_op_rw(inter, NETUP_CI_REVISION, 0, NETUP_CI_FLG_RD);
+ /* enable irq */
+ netup_fpga_op_rw(inter, NETUP_CI_INT_CTRL, 0x44, 0);
+
+ mutex_unlock(&inter->fpga_mutex);
+
+ ci_dbg_print("%s: NetUP CI Revision = 0x%x\n", __func__, ret);
+
+ schedule_work(&inter->work);
+
+ return 0;
+err:
+ ci_dbg_print("%s: Cannot initialize CI: Error %d.\n", __func__, ret);
+
+ kfree(state);
+
+ return ret;
+}
+EXPORT_SYMBOL(altera_ci_init);
+
+int altera_ci_tuner_reset(void *dev, int ci_nr)
+{
+ struct fpga_inode *temp_int = find_inode(dev);
+ struct fpga_internal *inter = NULL;
+ u8 store;
+
+ ci_dbg_print("%s\n", __func__);
+
+ if (temp_int == NULL)
+ return -1;
+
+ if (temp_int->internal == NULL)
+ return -1;
+
+ inter = temp_int->internal;
+
+ mutex_lock(&inter->fpga_mutex);
+
+ store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, 0, NETUP_CI_FLG_RD);
+ store &= ~(4 << (2 - ci_nr));
+ netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, store, 0);
+ msleep(100);
+ store |= (4 << (2 - ci_nr));
+ netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, store, 0);
+
+ mutex_unlock(&inter->fpga_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(altera_ci_tuner_reset);
diff --git a/drivers/media/video/cx23885/altera-ci.h b/drivers/media/video/cx23885/altera-ci.h
new file mode 100644
index 00000000..70e4fd69
--- /dev/null
+++ b/drivers/media/video/cx23885/altera-ci.h
@@ -0,0 +1,100 @@
+/*
+ * altera-ci.c
+ *
+ * CI driver in conjunction with NetUp Dual DVB-T/C RF CI card
+ *
+ * Copyright (C) 2010 NetUP Inc.
+ * Copyright (C) 2010 Igor M. Liplianin <liplianin@netup.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef __ALTERA_CI_H
+#define __ALTERA_CI_H
+
+#define ALT_DATA 0x000000ff
+#define ALT_TDI 0x00008000
+#define ALT_TDO 0x00004000
+#define ALT_TCK 0x00002000
+#define ALT_RDY 0x00001000
+#define ALT_RD 0x00000800
+#define ALT_WR 0x00000400
+#define ALT_AD_RG 0x00000200
+#define ALT_CS 0x00000100
+
+struct altera_ci_config {
+ void *dev;/* main dev, for example cx23885_dev */
+ void *adapter;/* for CI to connect to */
+ struct dvb_demux *demux;/* for hardware PID filter to connect to */
+ int (*fpga_rw) (void *dev, int ad_rg, int val, int rw);
+};
+
+#if defined(CONFIG_MEDIA_ALTERA_CI) || (defined(CONFIG_MEDIA_ALTERA_CI_MODULE) \
+ && defined(MODULE))
+
+extern int altera_ci_init(struct altera_ci_config *config, int ci_nr);
+extern void altera_ci_release(void *dev, int ci_nr);
+extern int altera_ci_irq(void *dev);
+extern int altera_ci_tuner_reset(void *dev, int ci_nr);
+
+#else
+
+static inline int altera_ci_init(struct altera_ci_config *config, int ci_nr)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return 0;
+}
+
+static inline void altera_ci_release(void *dev, int ci_nr)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+}
+
+static inline int altera_ci_irq(void *dev)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return 0;
+}
+
+static inline int altera_ci_tuner_reset(void *dev, int ci_nr)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return 0;
+}
+
+#endif
+#if 0
+static inline int altera_hw_filt_init(struct altera_ci_config *config,
+ int hw_filt_nr)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return 0;
+}
+
+static inline void altera_hw_filt_release(void *dev, int filt_nr)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+}
+
+static inline int altera_pid_feed_control(void *dev, int filt_nr,
+ struct dvb_demux_feed *dvbdmxfeed, int onoff)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return 0;
+}
+
+#endif /* CONFIG_MEDIA_ALTERA_CI */
+
+#endif /* __ALTERA_CI_H */
diff --git a/drivers/media/video/cx23885/cimax2.c b/drivers/media/video/cx23885/cimax2.c
new file mode 100644
index 00000000..c9f15d6d
--- /dev/null
+++ b/drivers/media/video/cx23885/cimax2.c
@@ -0,0 +1,536 @@
+/*
+ * cimax2.c
+ *
+ * CIMax2(R) SP2 driver in conjunction with NetUp Dual DVB-S2 CI card
+ *
+ * Copyright (C) 2009 NetUP Inc.
+ * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
+ * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "cx23885.h"
+#include "dvb_ca_en50221.h"
+/**** Bit definitions for MC417_RWD and MC417_OEN registers ***
+ bits 31-16
++-----------+
+| Reserved |
++-----------+
+ bit 15 bit 14 bit 13 bit 12 bit 11 bit 10 bit 9 bit 8
++-------+-------+-------+-------+-------+-------+-------+-------+
+| WR# | RD# | | ACK# | ADHI | ADLO | CS1# | CS0# |
++-------+-------+-------+-------+-------+-------+-------+-------+
+ bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
++-------+-------+-------+-------+-------+-------+-------+-------+
+| DATA7| DATA6| DATA5| DATA4| DATA3| DATA2| DATA1| DATA0|
++-------+-------+-------+-------+-------+-------+-------+-------+
+***/
+/* MC417 */
+#define NETUP_DATA 0x000000ff
+#define NETUP_WR 0x00008000
+#define NETUP_RD 0x00004000
+#define NETUP_ACK 0x00001000
+#define NETUP_ADHI 0x00000800
+#define NETUP_ADLO 0x00000400
+#define NETUP_CS1 0x00000200
+#define NETUP_CS0 0x00000100
+#define NETUP_EN_ALL 0x00001000
+#define NETUP_CTRL_OFF (NETUP_CS1 | NETUP_CS0 | NETUP_WR | NETUP_RD)
+#define NETUP_CI_CTL 0x04
+#define NETUP_CI_RD 1
+
+#define NETUP_IRQ_DETAM 0x1
+#define NETUP_IRQ_IRQAM 0x4
+
+static unsigned int ci_dbg;
+module_param(ci_dbg, int, 0644);
+MODULE_PARM_DESC(ci_dbg, "Enable CI debugging");
+
+static unsigned int ci_irq_enable;
+module_param(ci_irq_enable, int, 0644);
+MODULE_PARM_DESC(ci_irq_enable, "Enable IRQ from CAM");
+
+#define ci_dbg_print(args...) \
+ do { \
+ if (ci_dbg) \
+ printk(KERN_DEBUG args); \
+ } while (0)
+
+#define ci_irq_flags() (ci_irq_enable ? NETUP_IRQ_IRQAM : 0)
+
+/* stores all private variables for communication with CI */
+struct netup_ci_state {
+ struct dvb_ca_en50221 ca;
+ struct mutex ca_mutex;
+ struct i2c_adapter *i2c_adap;
+ u8 ci_i2c_addr;
+ int status;
+ struct work_struct work;
+ void *priv;
+ u8 current_irq_mode;
+ int current_ci_flag;
+ unsigned long next_status_checked_time;
+};
+
+
+int netup_read_i2c(struct i2c_adapter *i2c_adap, u8 addr, u8 reg,
+ u8 *buf, int len)
+{
+ int ret;
+ struct i2c_msg msg[] = {
+ {
+ .addr = addr,
+ .flags = 0,
+ .buf = &reg,
+ .len = 1
+ }, {
+ .addr = addr,
+ .flags = I2C_M_RD,
+ .buf = buf,
+ .len = len
+ }
+ };
+
+ ret = i2c_transfer(i2c_adap, msg, 2);
+
+ if (ret != 2) {
+ ci_dbg_print("%s: i2c read error, Reg = 0x%02x, Status = %d\n",
+ __func__, reg, ret);
+
+ return -1;
+ }
+
+ ci_dbg_print("%s: i2c read Addr=0x%04x, Reg = 0x%02x, data = %02x\n",
+ __func__, addr, reg, buf[0]);
+
+ return 0;
+}
+
+int netup_write_i2c(struct i2c_adapter *i2c_adap, u8 addr, u8 reg,
+ u8 *buf, int len)
+{
+ int ret;
+ u8 buffer[len + 1];
+
+ struct i2c_msg msg = {
+ .addr = addr,
+ .flags = 0,
+ .buf = &buffer[0],
+ .len = len + 1
+ };
+
+ buffer[0] = reg;
+ memcpy(&buffer[1], buf, len);
+
+ ret = i2c_transfer(i2c_adap, &msg, 1);
+
+ if (ret != 1) {
+ ci_dbg_print("%s: i2c write error, Reg=[0x%02x], Status=%d\n",
+ __func__, reg, ret);
+ return -1;
+ }
+
+ return 0;
+}
+
+int netup_ci_get_mem(struct cx23885_dev *dev)
+{
+ int mem;
+ unsigned long timeout = jiffies + msecs_to_jiffies(1);
+
+ for (;;) {
+ mem = cx_read(MC417_RWD);
+ if ((mem & NETUP_ACK) == 0)
+ break;
+ if (time_after(jiffies, timeout))
+ break;
+ udelay(1);
+ }
+
+ cx_set(MC417_RWD, NETUP_CTRL_OFF);
+
+ return mem & 0xff;
+}
+
+int netup_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot,
+ u8 flag, u8 read, int addr, u8 data)
+{
+ struct netup_ci_state *state = en50221->data;
+ struct cx23885_tsport *port = state->priv;
+ struct cx23885_dev *dev = port->dev;
+
+ u8 store;
+ int mem;
+ int ret;
+
+ if (0 != slot)
+ return -EINVAL;
+
+ if (state->current_ci_flag != flag) {
+ ret = netup_read_i2c(state->i2c_adap, state->ci_i2c_addr,
+ 0, &store, 1);
+ if (ret != 0)
+ return ret;
+
+ store &= ~0x0c;
+ store |= flag;
+
+ ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr,
+ 0, &store, 1);
+ if (ret != 0)
+ return ret;
+ };
+ state->current_ci_flag = flag;
+
+ mutex_lock(&dev->gpio_lock);
+
+ /* write addr */
+ cx_write(MC417_OEN, NETUP_EN_ALL);
+ cx_write(MC417_RWD, NETUP_CTRL_OFF |
+ NETUP_ADLO | (0xff & addr));
+ cx_clear(MC417_RWD, NETUP_ADLO);
+ cx_write(MC417_RWD, NETUP_CTRL_OFF |
+ NETUP_ADHI | (0xff & (addr >> 8)));
+ cx_clear(MC417_RWD, NETUP_ADHI);
+
+ if (read) { /* data in */
+ cx_write(MC417_OEN, NETUP_EN_ALL | NETUP_DATA);
+ } else /* data out */
+ cx_write(MC417_RWD, NETUP_CTRL_OFF | data);
+
+ /* choose chip */
+ cx_clear(MC417_RWD,
+ (state->ci_i2c_addr == 0x40) ? NETUP_CS0 : NETUP_CS1);
+ /* read/write */
+ cx_clear(MC417_RWD, (read) ? NETUP_RD : NETUP_WR);
+ mem = netup_ci_get_mem(dev);
+
+ mutex_unlock(&dev->gpio_lock);
+
+ if (!read)
+ if (mem < 0)
+ return -EREMOTEIO;
+
+ ci_dbg_print("%s: %s: chipaddr=[0x%x] addr=[0x%02x], %s=%x\n", __func__,
+ (read) ? "read" : "write", state->ci_i2c_addr, addr,
+ (flag == NETUP_CI_CTL) ? "ctl" : "mem",
+ (read) ? mem : data);
+
+ if (read)
+ return mem;
+
+ return 0;
+}
+
+int netup_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221,
+ int slot, int addr)
+{
+ return netup_ci_op_cam(en50221, slot, 0, NETUP_CI_RD, addr, 0);
+}
+
+int netup_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221,
+ int slot, int addr, u8 data)
+{
+ return netup_ci_op_cam(en50221, slot, 0, 0, addr, data);
+}
+
+int netup_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, u8 addr)
+{
+ return netup_ci_op_cam(en50221, slot, NETUP_CI_CTL,
+ NETUP_CI_RD, addr, 0);
+}
+
+int netup_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221, int slot,
+ u8 addr, u8 data)
+{
+ return netup_ci_op_cam(en50221, slot, NETUP_CI_CTL, 0, addr, data);
+}
+
+int netup_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot)
+{
+ struct netup_ci_state *state = en50221->data;
+ u8 buf = 0x80;
+ int ret;
+
+ if (0 != slot)
+ return -EINVAL;
+
+ udelay(500);
+ ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr,
+ 0, &buf, 1);
+
+ if (ret != 0)
+ return ret;
+
+ udelay(500);
+
+ buf = 0x00;
+ ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr,
+ 0, &buf, 1);
+
+ msleep(1000);
+ dvb_ca_en50221_camready_irq(&state->ca, 0);
+
+ return 0;
+
+}
+
+int netup_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot)
+{
+ /* not implemented */
+ return 0;
+}
+
+int netup_ci_set_irq(struct dvb_ca_en50221 *en50221, u8 irq_mode)
+{
+ struct netup_ci_state *state = en50221->data;
+ int ret;
+
+ if (irq_mode == state->current_irq_mode)
+ return 0;
+
+ ci_dbg_print("%s: chipaddr=[0x%x] setting ci IRQ to [0x%x] \n",
+ __func__, state->ci_i2c_addr, irq_mode);
+ ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr,
+ 0x1b, &irq_mode, 1);
+
+ if (ret != 0)
+ return ret;
+
+ state->current_irq_mode = irq_mode;
+
+ return 0;
+}
+
+int netup_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, int slot)
+{
+ struct netup_ci_state *state = en50221->data;
+ u8 buf;
+
+ if (0 != slot)
+ return -EINVAL;
+
+ netup_read_i2c(state->i2c_adap, state->ci_i2c_addr,
+ 0, &buf, 1);
+ buf |= 0x60;
+
+ return netup_write_i2c(state->i2c_adap, state->ci_i2c_addr,
+ 0, &buf, 1);
+}
+
+/* work handler */
+static void netup_read_ci_status(struct work_struct *work)
+{
+ struct netup_ci_state *state =
+ container_of(work, struct netup_ci_state, work);
+ u8 buf[33];
+ int ret;
+
+ /* CAM module IRQ processing. fast operation */
+ dvb_ca_en50221_frda_irq(&state->ca, 0);
+
+ /* CAM module INSERT/REMOVE processing. slow operation because of i2c
+ * transfers */
+ if (time_after(jiffies, state->next_status_checked_time)
+ || !state->status) {
+ ret = netup_read_i2c(state->i2c_adap, state->ci_i2c_addr,
+ 0, &buf[0], 33);
+
+ state->next_status_checked_time = jiffies
+ + msecs_to_jiffies(1000);
+
+ if (ret != 0)
+ return;
+
+ ci_dbg_print("%s: Slot Status Addr=[0x%04x], "
+ "Reg=[0x%02x], data=%02x, "
+ "TS config = %02x\n", __func__,
+ state->ci_i2c_addr, 0, buf[0],
+ buf[0]);
+
+
+ if (buf[0] & 1)
+ state->status = DVB_CA_EN50221_POLL_CAM_PRESENT |
+ DVB_CA_EN50221_POLL_CAM_READY;
+ else
+ state->status = 0;
+ }
+}
+
+/* CI irq handler */
+int netup_ci_slot_status(struct cx23885_dev *dev, u32 pci_status)
+{
+ struct cx23885_tsport *port = NULL;
+ struct netup_ci_state *state = NULL;
+
+ ci_dbg_print("%s:\n", __func__);
+
+ if (0 == (pci_status & (PCI_MSK_GPIO0 | PCI_MSK_GPIO1)))
+ return 0;
+
+ if (pci_status & PCI_MSK_GPIO0) {
+ port = &dev->ts1;
+ state = port->port_priv;
+ schedule_work(&state->work);
+ ci_dbg_print("%s: Wakeup CI0\n", __func__);
+ }
+
+ if (pci_status & PCI_MSK_GPIO1) {
+ port = &dev->ts2;
+ state = port->port_priv;
+ schedule_work(&state->work);
+ ci_dbg_print("%s: Wakeup CI1\n", __func__);
+ }
+
+ return 1;
+}
+
+int netup_poll_ci_slot_status(struct dvb_ca_en50221 *en50221, int slot, int open)
+{
+ struct netup_ci_state *state = en50221->data;
+
+ if (0 != slot)
+ return -EINVAL;
+
+ netup_ci_set_irq(en50221, open ? (NETUP_IRQ_DETAM | ci_irq_flags())
+ : NETUP_IRQ_DETAM);
+
+ return state->status;
+}
+
+int netup_ci_init(struct cx23885_tsport *port)
+{
+ struct netup_ci_state *state;
+ u8 cimax_init[34] = {
+ 0x00, /* module A control*/
+ 0x00, /* auto select mask high A */
+ 0x00, /* auto select mask low A */
+ 0x00, /* auto select pattern high A */
+ 0x00, /* auto select pattern low A */
+ 0x44, /* memory access time A */
+ 0x00, /* invert input A */
+ 0x00, /* RFU */
+ 0x00, /* RFU */
+ 0x00, /* module B control*/
+ 0x00, /* auto select mask high B */
+ 0x00, /* auto select mask low B */
+ 0x00, /* auto select pattern high B */
+ 0x00, /* auto select pattern low B */
+ 0x44, /* memory access time B */
+ 0x00, /* invert input B */
+ 0x00, /* RFU */
+ 0x00, /* RFU */
+ 0x00, /* auto select mask high Ext */
+ 0x00, /* auto select mask low Ext */
+ 0x00, /* auto select pattern high Ext */
+ 0x00, /* auto select pattern low Ext */
+ 0x00, /* RFU */
+ 0x02, /* destination - module A */
+ 0x01, /* power on (use it like store place) */
+ 0x00, /* RFU */
+ 0x00, /* int status read only */
+ ci_irq_flags() | NETUP_IRQ_DETAM, /* DETAM, IRQAM unmasked */
+ 0x05, /* EXTINT=active-high, INT=push-pull */
+ 0x00, /* USCG1 */
+ 0x04, /* ack active low */
+ 0x00, /* LOCK = 0 */
+ 0x33, /* serial mode, rising in, rising out, MSB first*/
+ 0x31, /* synchronization */
+ };
+ int ret;
+
+ ci_dbg_print("%s\n", __func__);
+ state = kzalloc(sizeof(struct netup_ci_state), GFP_KERNEL);
+ if (!state) {
+ ci_dbg_print("%s: Unable create CI structure!\n", __func__);
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ port->port_priv = state;
+
+ switch (port->nr) {
+ case 1:
+ state->ci_i2c_addr = 0x40;
+ break;
+ case 2:
+ state->ci_i2c_addr = 0x41;
+ break;
+ }
+
+ state->i2c_adap = &port->dev->i2c_bus[0].i2c_adap;
+ state->ca.owner = THIS_MODULE;
+ state->ca.read_attribute_mem = netup_ci_read_attribute_mem;
+ state->ca.write_attribute_mem = netup_ci_write_attribute_mem;
+ state->ca.read_cam_control = netup_ci_read_cam_ctl;
+ state->ca.write_cam_control = netup_ci_write_cam_ctl;
+ state->ca.slot_reset = netup_ci_slot_reset;
+ state->ca.slot_shutdown = netup_ci_slot_shutdown;
+ state->ca.slot_ts_enable = netup_ci_slot_ts_ctl;
+ state->ca.poll_slot_status = netup_poll_ci_slot_status;
+ state->ca.data = state;
+ state->priv = port;
+ state->current_irq_mode = ci_irq_flags() | NETUP_IRQ_DETAM;
+
+ ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr,
+ 0, &cimax_init[0], 34);
+ /* lock registers */
+ ret |= netup_write_i2c(state->i2c_adap, state->ci_i2c_addr,
+ 0x1f, &cimax_init[0x18], 1);
+ /* power on slots */
+ ret |= netup_write_i2c(state->i2c_adap, state->ci_i2c_addr,
+ 0x18, &cimax_init[0x18], 1);
+
+ if (0 != ret)
+ goto err;
+
+ ret = dvb_ca_en50221_init(&port->frontends.adapter,
+ &state->ca,
+ /* flags */ 0,
+ /* n_slots */ 1);
+ if (0 != ret)
+ goto err;
+
+ INIT_WORK(&state->work, netup_read_ci_status);
+ schedule_work(&state->work);
+
+ ci_dbg_print("%s: CI initialized!\n", __func__);
+
+ return 0;
+err:
+ ci_dbg_print("%s: Cannot initialize CI: Error %d.\n", __func__, ret);
+ kfree(state);
+ return ret;
+}
+
+void netup_ci_exit(struct cx23885_tsport *port)
+{
+ struct netup_ci_state *state;
+
+ if (NULL == port)
+ return;
+
+ state = (struct netup_ci_state *)port->port_priv;
+ if (NULL == state)
+ return;
+
+ if (NULL == state->ca.data)
+ return;
+
+ dvb_ca_en50221_release(&state->ca);
+ kfree(state);
+}
diff --git a/drivers/media/video/cx23885/cimax2.h b/drivers/media/video/cx23885/cimax2.h
new file mode 100644
index 00000000..518744a4
--- /dev/null
+++ b/drivers/media/video/cx23885/cimax2.h
@@ -0,0 +1,47 @@
+/*
+ * cimax2.h
+ *
+ * CIMax(R) SP2 driver in conjunction with NetUp Dual DVB-S2 CI card
+ *
+ * Copyright (C) 2009 NetUP Inc.
+ * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
+ * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef CIMAX2_H
+#define CIMAX2_H
+#include "dvb_ca_en50221.h"
+
+extern int netup_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221,
+ int slot, int addr);
+extern int netup_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221,
+ int slot, int addr, u8 data);
+extern int netup_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221,
+ int slot, u8 addr);
+extern int netup_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221,
+ int slot, u8 addr, u8 data);
+extern int netup_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot);
+extern int netup_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot);
+extern int netup_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, int slot);
+extern int netup_ci_slot_status(struct cx23885_dev *dev, u32 pci_status);
+extern int netup_poll_ci_slot_status(struct dvb_ca_en50221 *en50221,
+ int slot, int open);
+extern int netup_ci_init(struct cx23885_tsport *port);
+extern void netup_ci_exit(struct cx23885_tsport *port);
+
+#endif
diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c
new file mode 100644
index 00000000..f5c79e53
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-417.c
@@ -0,0 +1,1788 @@
+/*
+ *
+ * Support for a cx23417 mpeg encoder via cx23885 host port.
+ *
+ * (c) 2004 Jelle Foks <jelle@foks.us>
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org>
+ * (c) 2008 Steven Toth <stoth@linuxtv.org>
+ * - CX23885/7/8 support
+ *
+ * Includes parts from the ivtv driver <http://sourceforge.net/projects/ivtv/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/cx2341x.h>
+
+#include "cx23885.h"
+#include "cx23885-ioctl.h"
+
+#define CX23885_FIRM_IMAGE_SIZE 376836
+#define CX23885_FIRM_IMAGE_NAME "v4l-cx23885-enc.fw"
+
+static unsigned int mpegbufs = 32;
+module_param(mpegbufs, int, 0644);
+MODULE_PARM_DESC(mpegbufs, "number of mpeg buffers, range 2-32");
+static unsigned int mpeglines = 32;
+module_param(mpeglines, int, 0644);
+MODULE_PARM_DESC(mpeglines, "number of lines in an MPEG buffer, range 2-32");
+static unsigned int mpeglinesize = 512;
+module_param(mpeglinesize, int, 0644);
+MODULE_PARM_DESC(mpeglinesize,
+ "number of bytes in each line of an MPEG buffer, range 512-1024");
+
+static unsigned int v4l_debug;
+module_param(v4l_debug, int, 0644);
+MODULE_PARM_DESC(v4l_debug, "enable V4L debug messages");
+
+#define dprintk(level, fmt, arg...)\
+ do { if (v4l_debug >= level) \
+ printk(KERN_DEBUG "%s: " fmt, \
+ (dev) ? dev->name : "cx23885[?]", ## arg); \
+ } while (0)
+
+static struct cx23885_tvnorm cx23885_tvnorms[] = {
+ {
+ .name = "NTSC-M",
+ .id = V4L2_STD_NTSC_M,
+ }, {
+ .name = "NTSC-JP",
+ .id = V4L2_STD_NTSC_M_JP,
+ }, {
+ .name = "PAL-BG",
+ .id = V4L2_STD_PAL_BG,
+ }, {
+ .name = "PAL-DK",
+ .id = V4L2_STD_PAL_DK,
+ }, {
+ .name = "PAL-I",
+ .id = V4L2_STD_PAL_I,
+ }, {
+ .name = "PAL-M",
+ .id = V4L2_STD_PAL_M,
+ }, {
+ .name = "PAL-N",
+ .id = V4L2_STD_PAL_N,
+ }, {
+ .name = "PAL-Nc",
+ .id = V4L2_STD_PAL_Nc,
+ }, {
+ .name = "PAL-60",
+ .id = V4L2_STD_PAL_60,
+ }, {
+ .name = "SECAM-L",
+ .id = V4L2_STD_SECAM_L,
+ }, {
+ .name = "SECAM-DK",
+ .id = V4L2_STD_SECAM_DK,
+ }
+};
+
+/* ------------------------------------------------------------------ */
+enum cx23885_capture_type {
+ CX23885_MPEG_CAPTURE,
+ CX23885_RAW_CAPTURE,
+ CX23885_RAW_PASSTHRU_CAPTURE
+};
+enum cx23885_capture_bits {
+ CX23885_RAW_BITS_NONE = 0x00,
+ CX23885_RAW_BITS_YUV_CAPTURE = 0x01,
+ CX23885_RAW_BITS_PCM_CAPTURE = 0x02,
+ CX23885_RAW_BITS_VBI_CAPTURE = 0x04,
+ CX23885_RAW_BITS_PASSTHRU_CAPTURE = 0x08,
+ CX23885_RAW_BITS_TO_HOST_CAPTURE = 0x10
+};
+enum cx23885_capture_end {
+ CX23885_END_AT_GOP, /* stop at the end of gop, generate irq */
+ CX23885_END_NOW, /* stop immediately, no irq */
+};
+enum cx23885_framerate {
+ CX23885_FRAMERATE_NTSC_30, /* NTSC: 30fps */
+ CX23885_FRAMERATE_PAL_25 /* PAL: 25fps */
+};
+enum cx23885_stream_port {
+ CX23885_OUTPUT_PORT_MEMORY,
+ CX23885_OUTPUT_PORT_STREAMING,
+ CX23885_OUTPUT_PORT_SERIAL
+};
+enum cx23885_data_xfer_status {
+ CX23885_MORE_BUFFERS_FOLLOW,
+ CX23885_LAST_BUFFER,
+};
+enum cx23885_picture_mask {
+ CX23885_PICTURE_MASK_NONE,
+ CX23885_PICTURE_MASK_I_FRAMES,
+ CX23885_PICTURE_MASK_I_P_FRAMES = 0x3,
+ CX23885_PICTURE_MASK_ALL_FRAMES = 0x7,
+};
+enum cx23885_vbi_mode_bits {
+ CX23885_VBI_BITS_SLICED,
+ CX23885_VBI_BITS_RAW,
+};
+enum cx23885_vbi_insertion_bits {
+ CX23885_VBI_BITS_INSERT_IN_XTENSION_USR_DATA,
+ CX23885_VBI_BITS_INSERT_IN_PRIVATE_PACKETS = 0x1 << 1,
+ CX23885_VBI_BITS_SEPARATE_STREAM = 0x2 << 1,
+ CX23885_VBI_BITS_SEPARATE_STREAM_USR_DATA = 0x4 << 1,
+ CX23885_VBI_BITS_SEPARATE_STREAM_PRV_DATA = 0x5 << 1,
+};
+enum cx23885_dma_unit {
+ CX23885_DMA_BYTES,
+ CX23885_DMA_FRAMES,
+};
+enum cx23885_dma_transfer_status_bits {
+ CX23885_DMA_TRANSFER_BITS_DONE = 0x01,
+ CX23885_DMA_TRANSFER_BITS_ERROR = 0x04,
+ CX23885_DMA_TRANSFER_BITS_LL_ERROR = 0x10,
+};
+enum cx23885_pause {
+ CX23885_PAUSE_ENCODING,
+ CX23885_RESUME_ENCODING,
+};
+enum cx23885_copyright {
+ CX23885_COPYRIGHT_OFF,
+ CX23885_COPYRIGHT_ON,
+};
+enum cx23885_notification_type {
+ CX23885_NOTIFICATION_REFRESH,
+};
+enum cx23885_notification_status {
+ CX23885_NOTIFICATION_OFF,
+ CX23885_NOTIFICATION_ON,
+};
+enum cx23885_notification_mailbox {
+ CX23885_NOTIFICATION_NO_MAILBOX = -1,
+};
+enum cx23885_field1_lines {
+ CX23885_FIELD1_SAA7114 = 0x00EF, /* 239 */
+ CX23885_FIELD1_SAA7115 = 0x00F0, /* 240 */
+ CX23885_FIELD1_MICRONAS = 0x0105, /* 261 */
+};
+enum cx23885_field2_lines {
+ CX23885_FIELD2_SAA7114 = 0x00EF, /* 239 */
+ CX23885_FIELD2_SAA7115 = 0x00F0, /* 240 */
+ CX23885_FIELD2_MICRONAS = 0x0106, /* 262 */
+};
+enum cx23885_custom_data_type {
+ CX23885_CUSTOM_EXTENSION_USR_DATA,
+ CX23885_CUSTOM_PRIVATE_PACKET,
+};
+enum cx23885_mute {
+ CX23885_UNMUTE,
+ CX23885_MUTE,
+};
+enum cx23885_mute_video_mask {
+ CX23885_MUTE_VIDEO_V_MASK = 0x0000FF00,
+ CX23885_MUTE_VIDEO_U_MASK = 0x00FF0000,
+ CX23885_MUTE_VIDEO_Y_MASK = 0xFF000000,
+};
+enum cx23885_mute_video_shift {
+ CX23885_MUTE_VIDEO_V_SHIFT = 8,
+ CX23885_MUTE_VIDEO_U_SHIFT = 16,
+ CX23885_MUTE_VIDEO_Y_SHIFT = 24,
+};
+
+/* defines below are from ivtv-driver.h */
+#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF
+
+/* Firmware API commands */
+#define IVTV_API_STD_TIMEOUT 500
+
+/* Registers */
+/* IVTV_REG_OFFSET */
+#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8)
+#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC)
+#define IVTV_REG_SPU (0x9050)
+#define IVTV_REG_HW_BLOCKS (0x9054)
+#define IVTV_REG_VPU (0x9058)
+#define IVTV_REG_APU (0xA064)
+
+/**** Bit definitions for MC417_RWD and MC417_OEN registers ***
+ bits 31-16
++-----------+
+| Reserved |
++-----------+
+ bit 15 bit 14 bit 13 bit 12 bit 11 bit 10 bit 9 bit 8
++-------+-------+-------+-------+-------+-------+-------+-------+
+| MIWR# | MIRD# | MICS# |MIRDY# |MIADDR3|MIADDR2|MIADDR1|MIADDR0|
++-------+-------+-------+-------+-------+-------+-------+-------+
+ bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
++-------+-------+-------+-------+-------+-------+-------+-------+
+|MIDATA7|MIDATA6|MIDATA5|MIDATA4|MIDATA3|MIDATA2|MIDATA1|MIDATA0|
++-------+-------+-------+-------+-------+-------+-------+-------+
+***/
+#define MC417_MIWR 0x8000
+#define MC417_MIRD 0x4000
+#define MC417_MICS 0x2000
+#define MC417_MIRDY 0x1000
+#define MC417_MIADDR 0x0F00
+#define MC417_MIDATA 0x00FF
+
+/* MIADDR* nibble definitions */
+#define MCI_MEMORY_DATA_BYTE0 0x000
+#define MCI_MEMORY_DATA_BYTE1 0x100
+#define MCI_MEMORY_DATA_BYTE2 0x200
+#define MCI_MEMORY_DATA_BYTE3 0x300
+#define MCI_MEMORY_ADDRESS_BYTE2 0x400
+#define MCI_MEMORY_ADDRESS_BYTE1 0x500
+#define MCI_MEMORY_ADDRESS_BYTE0 0x600
+#define MCI_REGISTER_DATA_BYTE0 0x800
+#define MCI_REGISTER_DATA_BYTE1 0x900
+#define MCI_REGISTER_DATA_BYTE2 0xA00
+#define MCI_REGISTER_DATA_BYTE3 0xB00
+#define MCI_REGISTER_ADDRESS_BYTE0 0xC00
+#define MCI_REGISTER_ADDRESS_BYTE1 0xD00
+#define MCI_REGISTER_MODE 0xE00
+
+/* Read and write modes */
+#define MCI_MODE_REGISTER_READ 0
+#define MCI_MODE_REGISTER_WRITE 1
+#define MCI_MODE_MEMORY_READ 0
+#define MCI_MODE_MEMORY_WRITE 0x40
+
+/*** Bit definitions for MC417_CTL register ****
+ bits 31-6 bits 5-4 bit 3 bits 2-1 Bit 0
++--------+-------------+--------+--------------+------------+
+|Reserved|MC417_SPD_CTL|Reserved|MC417_GPIO_SEL|UART_GPIO_EN|
++--------+-------------+--------+--------------+------------+
+***/
+#define MC417_SPD_CTL(x) (((x) << 4) & 0x00000030)
+#define MC417_GPIO_SEL(x) (((x) << 1) & 0x00000006)
+#define MC417_UART_GPIO_EN 0x00000001
+
+/* Values for speed control */
+#define MC417_SPD_CTL_SLOW 0x1
+#define MC417_SPD_CTL_MEDIUM 0x0
+#define MC417_SPD_CTL_FAST 0x3 /* b'1x, but we use b'11 */
+
+/* Values for GPIO select */
+#define MC417_GPIO_SEL_GPIO3 0x3
+#define MC417_GPIO_SEL_GPIO2 0x2
+#define MC417_GPIO_SEL_GPIO1 0x1
+#define MC417_GPIO_SEL_GPIO0 0x0
+
+void cx23885_mc417_init(struct cx23885_dev *dev)
+{
+ u32 regval;
+
+ dprintk(2, "%s()\n", __func__);
+
+ /* Configure MC417_CTL register to defaults. */
+ regval = MC417_SPD_CTL(MC417_SPD_CTL_FAST) |
+ MC417_GPIO_SEL(MC417_GPIO_SEL_GPIO3) |
+ MC417_UART_GPIO_EN;
+ cx_write(MC417_CTL, regval);
+
+ /* Configure MC417_OEN to defaults. */
+ regval = MC417_MIRDY;
+ cx_write(MC417_OEN, regval);
+
+ /* Configure MC417_RWD to defaults. */
+ regval = MC417_MIWR | MC417_MIRD | MC417_MICS;
+ cx_write(MC417_RWD, regval);
+}
+
+static int mc417_wait_ready(struct cx23885_dev *dev)
+{
+ u32 mi_ready;
+ unsigned long timeout = jiffies + msecs_to_jiffies(1);
+
+ for (;;) {
+ mi_ready = cx_read(MC417_RWD) & MC417_MIRDY;
+ if (mi_ready != 0)
+ return 0;
+ if (time_after(jiffies, timeout))
+ return -1;
+ udelay(1);
+ }
+}
+
+int mc417_register_write(struct cx23885_dev *dev, u16 address, u32 value)
+{
+ u32 regval;
+
+ /* Enable MC417 GPIO outputs except for MC417_MIRDY,
+ * which is an input.
+ */
+ cx_write(MC417_OEN, MC417_MIRDY);
+
+ /* Write data byte 0 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE0 |
+ (value & 0x000000FF);
+ cx_write(MC417_RWD, regval);
+
+ /* Transition CS/WR to effect write transaction across bus. */
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Write data byte 1 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE1 |
+ ((value >> 8) & 0x000000FF);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Write data byte 2 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE2 |
+ ((value >> 16) & 0x000000FF);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Write data byte 3 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE3 |
+ ((value >> 24) & 0x000000FF);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Write address byte 0 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_ADDRESS_BYTE0 |
+ (address & 0xFF);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Write address byte 1 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_ADDRESS_BYTE1 |
+ ((address >> 8) & 0xFF);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Indicate that this is a write. */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_MODE |
+ MCI_MODE_REGISTER_WRITE;
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Wait for the trans to complete (MC417_MIRDY asserted). */
+ return mc417_wait_ready(dev);
+}
+
+int mc417_register_read(struct cx23885_dev *dev, u16 address, u32 *value)
+{
+ int retval;
+ u32 regval;
+ u32 tempval;
+ u32 dataval;
+
+ /* Enable MC417 GPIO outputs except for MC417_MIRDY,
+ * which is an input.
+ */
+ cx_write(MC417_OEN, MC417_MIRDY);
+
+ /* Write address byte 0 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_ADDRESS_BYTE0 |
+ ((address & 0x00FF));
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Write address byte 1 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_ADDRESS_BYTE1 |
+ ((address >> 8) & 0xFF);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Indicate that this is a register read. */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_MODE |
+ MCI_MODE_REGISTER_READ;
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Wait for the trans to complete (MC417_MIRDY asserted). */
+ retval = mc417_wait_ready(dev);
+
+ /* switch the DAT0-7 GPIO[10:3] to input mode */
+ cx_write(MC417_OEN, MC417_MIRDY | MC417_MIDATA);
+
+ /* Read data byte 0 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE0;
+ cx_write(MC417_RWD, regval);
+
+ /* Transition RD to effect read transaction across bus.
+ * Transtion 0x5000 -> 0x9000 correct (RD/RDY -> WR/RDY)?
+ * Should it be 0x9000 -> 0xF000 (also why is RDY being set, its
+ * input only...)
+ */
+ regval = MC417_MIWR | MC417_MIRDY | MCI_REGISTER_DATA_BYTE0;
+ cx_write(MC417_RWD, regval);
+
+ /* Collect byte */
+ tempval = cx_read(MC417_RWD);
+ dataval = tempval & 0x000000FF;
+
+ /* Bring CS and RD high. */
+ regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
+ cx_write(MC417_RWD, regval);
+
+ /* Read data byte 1 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE1;
+ cx_write(MC417_RWD, regval);
+ regval = MC417_MIWR | MC417_MIRDY | MCI_REGISTER_DATA_BYTE1;
+ cx_write(MC417_RWD, regval);
+ tempval = cx_read(MC417_RWD);
+ dataval |= ((tempval & 0x000000FF) << 8);
+ regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
+ cx_write(MC417_RWD, regval);
+
+ /* Read data byte 2 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE2;
+ cx_write(MC417_RWD, regval);
+ regval = MC417_MIWR | MC417_MIRDY | MCI_REGISTER_DATA_BYTE2;
+ cx_write(MC417_RWD, regval);
+ tempval = cx_read(MC417_RWD);
+ dataval |= ((tempval & 0x000000FF) << 16);
+ regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
+ cx_write(MC417_RWD, regval);
+
+ /* Read data byte 3 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE3;
+ cx_write(MC417_RWD, regval);
+ regval = MC417_MIWR | MC417_MIRDY | MCI_REGISTER_DATA_BYTE3;
+ cx_write(MC417_RWD, regval);
+ tempval = cx_read(MC417_RWD);
+ dataval |= ((tempval & 0x000000FF) << 24);
+ regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
+ cx_write(MC417_RWD, regval);
+
+ *value = dataval;
+
+ return retval;
+}
+
+int mc417_memory_write(struct cx23885_dev *dev, u32 address, u32 value)
+{
+ u32 regval;
+
+ /* Enable MC417 GPIO outputs except for MC417_MIRDY,
+ * which is an input.
+ */
+ cx_write(MC417_OEN, MC417_MIRDY);
+
+ /* Write data byte 0 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE0 |
+ (value & 0x000000FF);
+ cx_write(MC417_RWD, regval);
+
+ /* Transition CS/WR to effect write transaction across bus. */
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Write data byte 1 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE1 |
+ ((value >> 8) & 0x000000FF);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Write data byte 2 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE2 |
+ ((value >> 16) & 0x000000FF);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Write data byte 3 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE3 |
+ ((value >> 24) & 0x000000FF);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Write address byte 2 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE2 |
+ MCI_MODE_MEMORY_WRITE | ((address >> 16) & 0x3F);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Write address byte 1 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE1 |
+ ((address >> 8) & 0xFF);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Write address byte 0 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE0 |
+ (address & 0xFF);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Wait for the trans to complete (MC417_MIRDY asserted). */
+ return mc417_wait_ready(dev);
+}
+
+int mc417_memory_read(struct cx23885_dev *dev, u32 address, u32 *value)
+{
+ int retval;
+ u32 regval;
+ u32 tempval;
+ u32 dataval;
+
+ /* Enable MC417 GPIO outputs except for MC417_MIRDY,
+ * which is an input.
+ */
+ cx_write(MC417_OEN, MC417_MIRDY);
+
+ /* Write address byte 2 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE2 |
+ MCI_MODE_MEMORY_READ | ((address >> 16) & 0x3F);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Write address byte 1 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE1 |
+ ((address >> 8) & 0xFF);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Write address byte 0 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE0 |
+ (address & 0xFF);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Wait for the trans to complete (MC417_MIRDY asserted). */
+ retval = mc417_wait_ready(dev);
+
+ /* switch the DAT0-7 GPIO[10:3] to input mode */
+ cx_write(MC417_OEN, MC417_MIRDY | MC417_MIDATA);
+
+ /* Read data byte 3 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE3;
+ cx_write(MC417_RWD, regval);
+
+ /* Transition RD to effect read transaction across bus. */
+ regval = MC417_MIWR | MC417_MIRDY | MCI_MEMORY_DATA_BYTE3;
+ cx_write(MC417_RWD, regval);
+
+ /* Collect byte */
+ tempval = cx_read(MC417_RWD);
+ dataval = ((tempval & 0x000000FF) << 24);
+
+ /* Bring CS and RD high. */
+ regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
+ cx_write(MC417_RWD, regval);
+
+ /* Read data byte 2 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE2;
+ cx_write(MC417_RWD, regval);
+ regval = MC417_MIWR | MC417_MIRDY | MCI_MEMORY_DATA_BYTE2;
+ cx_write(MC417_RWD, regval);
+ tempval = cx_read(MC417_RWD);
+ dataval |= ((tempval & 0x000000FF) << 16);
+ regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
+ cx_write(MC417_RWD, regval);
+
+ /* Read data byte 1 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE1;
+ cx_write(MC417_RWD, regval);
+ regval = MC417_MIWR | MC417_MIRDY | MCI_MEMORY_DATA_BYTE1;
+ cx_write(MC417_RWD, regval);
+ tempval = cx_read(MC417_RWD);
+ dataval |= ((tempval & 0x000000FF) << 8);
+ regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
+ cx_write(MC417_RWD, regval);
+
+ /* Read data byte 0 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE0;
+ cx_write(MC417_RWD, regval);
+ regval = MC417_MIWR | MC417_MIRDY | MCI_MEMORY_DATA_BYTE0;
+ cx_write(MC417_RWD, regval);
+ tempval = cx_read(MC417_RWD);
+ dataval |= (tempval & 0x000000FF);
+ regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
+ cx_write(MC417_RWD, regval);
+
+ *value = dataval;
+
+ return retval;
+}
+
+void mc417_gpio_set(struct cx23885_dev *dev, u32 mask)
+{
+ u32 val;
+
+ /* Set the gpio value */
+ mc417_register_read(dev, 0x900C, &val);
+ val |= (mask & 0x000ffff);
+ mc417_register_write(dev, 0x900C, val);
+}
+
+void mc417_gpio_clear(struct cx23885_dev *dev, u32 mask)
+{
+ u32 val;
+
+ /* Clear the gpio value */
+ mc417_register_read(dev, 0x900C, &val);
+ val &= ~(mask & 0x0000ffff);
+ mc417_register_write(dev, 0x900C, val);
+}
+
+void mc417_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput)
+{
+ u32 val;
+
+ /* Enable GPIO direction bits */
+ mc417_register_read(dev, 0x9020, &val);
+ if (asoutput)
+ val |= (mask & 0x0000ffff);
+ else
+ val &= ~(mask & 0x0000ffff);
+
+ mc417_register_write(dev, 0x9020, val);
+}
+/* ------------------------------------------------------------------ */
+
+/* MPEG encoder API */
+static char *cmd_to_str(int cmd)
+{
+ switch (cmd) {
+ case CX2341X_ENC_PING_FW:
+ return "PING_FW";
+ case CX2341X_ENC_START_CAPTURE:
+ return "START_CAPTURE";
+ case CX2341X_ENC_STOP_CAPTURE:
+ return "STOP_CAPTURE";
+ case CX2341X_ENC_SET_AUDIO_ID:
+ return "SET_AUDIO_ID";
+ case CX2341X_ENC_SET_VIDEO_ID:
+ return "SET_VIDEO_ID";
+ case CX2341X_ENC_SET_PCR_ID:
+ return "SET_PCR_ID";
+ case CX2341X_ENC_SET_FRAME_RATE:
+ return "SET_FRAME_RATE";
+ case CX2341X_ENC_SET_FRAME_SIZE:
+ return "SET_FRAME_SIZE";
+ case CX2341X_ENC_SET_BIT_RATE:
+ return "SET_BIT_RATE";
+ case CX2341X_ENC_SET_GOP_PROPERTIES:
+ return "SET_GOP_PROPERTIES";
+ case CX2341X_ENC_SET_ASPECT_RATIO:
+ return "SET_ASPECT_RATIO";
+ case CX2341X_ENC_SET_DNR_FILTER_MODE:
+ return "SET_DNR_FILTER_MODE";
+ case CX2341X_ENC_SET_DNR_FILTER_PROPS:
+ return "SET_DNR_FILTER_PROPS";
+ case CX2341X_ENC_SET_CORING_LEVELS:
+ return "SET_CORING_LEVELS";
+ case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE:
+ return "SET_SPATIAL_FILTER_TYPE";
+ case CX2341X_ENC_SET_VBI_LINE:
+ return "SET_VBI_LINE";
+ case CX2341X_ENC_SET_STREAM_TYPE:
+ return "SET_STREAM_TYPE";
+ case CX2341X_ENC_SET_OUTPUT_PORT:
+ return "SET_OUTPUT_PORT";
+ case CX2341X_ENC_SET_AUDIO_PROPERTIES:
+ return "SET_AUDIO_PROPERTIES";
+ case CX2341X_ENC_HALT_FW:
+ return "HALT_FW";
+ case CX2341X_ENC_GET_VERSION:
+ return "GET_VERSION";
+ case CX2341X_ENC_SET_GOP_CLOSURE:
+ return "SET_GOP_CLOSURE";
+ case CX2341X_ENC_GET_SEQ_END:
+ return "GET_SEQ_END";
+ case CX2341X_ENC_SET_PGM_INDEX_INFO:
+ return "SET_PGM_INDEX_INFO";
+ case CX2341X_ENC_SET_VBI_CONFIG:
+ return "SET_VBI_CONFIG";
+ case CX2341X_ENC_SET_DMA_BLOCK_SIZE:
+ return "SET_DMA_BLOCK_SIZE";
+ case CX2341X_ENC_GET_PREV_DMA_INFO_MB_10:
+ return "GET_PREV_DMA_INFO_MB_10";
+ case CX2341X_ENC_GET_PREV_DMA_INFO_MB_9:
+ return "GET_PREV_DMA_INFO_MB_9";
+ case CX2341X_ENC_SCHED_DMA_TO_HOST:
+ return "SCHED_DMA_TO_HOST";
+ case CX2341X_ENC_INITIALIZE_INPUT:
+ return "INITIALIZE_INPUT";
+ case CX2341X_ENC_SET_FRAME_DROP_RATE:
+ return "SET_FRAME_DROP_RATE";
+ case CX2341X_ENC_PAUSE_ENCODER:
+ return "PAUSE_ENCODER";
+ case CX2341X_ENC_REFRESH_INPUT:
+ return "REFRESH_INPUT";
+ case CX2341X_ENC_SET_COPYRIGHT:
+ return "SET_COPYRIGHT";
+ case CX2341X_ENC_SET_EVENT_NOTIFICATION:
+ return "SET_EVENT_NOTIFICATION";
+ case CX2341X_ENC_SET_NUM_VSYNC_LINES:
+ return "SET_NUM_VSYNC_LINES";
+ case CX2341X_ENC_SET_PLACEHOLDER:
+ return "SET_PLACEHOLDER";
+ case CX2341X_ENC_MUTE_VIDEO:
+ return "MUTE_VIDEO";
+ case CX2341X_ENC_MUTE_AUDIO:
+ return "MUTE_AUDIO";
+ case CX2341X_ENC_MISC:
+ return "MISC";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static int cx23885_mbox_func(void *priv,
+ u32 command,
+ int in,
+ int out,
+ u32 data[CX2341X_MBOX_MAX_DATA])
+{
+ struct cx23885_dev *dev = priv;
+ unsigned long timeout;
+ u32 value, flag, retval = 0;
+ int i;
+
+ dprintk(3, "%s: command(0x%X) = %s\n", __func__, command,
+ cmd_to_str(command));
+
+ /* this may not be 100% safe if we can't read any memory location
+ without side effects */
+ mc417_memory_read(dev, dev->cx23417_mailbox - 4, &value);
+ if (value != 0x12345678) {
+ printk(KERN_ERR
+ "Firmware and/or mailbox pointer not initialized "
+ "or corrupted, signature = 0x%x, cmd = %s\n", value,
+ cmd_to_str(command));
+ return -1;
+ }
+
+ /* This read looks at 32 bits, but flag is only 8 bits.
+ * Seems we also bail if CMD or TIMEOUT bytes are set???
+ */
+ mc417_memory_read(dev, dev->cx23417_mailbox, &flag);
+ if (flag) {
+ printk(KERN_ERR "ERROR: Mailbox appears to be in use "
+ "(%x), cmd = %s\n", flag, cmd_to_str(command));
+ return -1;
+ }
+
+ flag |= 1; /* tell 'em we're working on it */
+ mc417_memory_write(dev, dev->cx23417_mailbox, flag);
+
+ /* write command + args + fill remaining with zeros */
+ /* command code */
+ mc417_memory_write(dev, dev->cx23417_mailbox + 1, command);
+ mc417_memory_write(dev, dev->cx23417_mailbox + 3,
+ IVTV_API_STD_TIMEOUT); /* timeout */
+ for (i = 0; i < in; i++) {
+ mc417_memory_write(dev, dev->cx23417_mailbox + 4 + i, data[i]);
+ dprintk(3, "API Input %d = %d\n", i, data[i]);
+ }
+ for (; i < CX2341X_MBOX_MAX_DATA; i++)
+ mc417_memory_write(dev, dev->cx23417_mailbox + 4 + i, 0);
+
+ flag |= 3; /* tell 'em we're done writing */
+ mc417_memory_write(dev, dev->cx23417_mailbox, flag);
+
+ /* wait for firmware to handle the API command */
+ timeout = jiffies + msecs_to_jiffies(10);
+ for (;;) {
+ mc417_memory_read(dev, dev->cx23417_mailbox, &flag);
+ if (0 != (flag & 4))
+ break;
+ if (time_after(jiffies, timeout)) {
+ printk(KERN_ERR "ERROR: API Mailbox timeout\n");
+ return -1;
+ }
+ udelay(10);
+ }
+
+ /* read output values */
+ for (i = 0; i < out; i++) {
+ mc417_memory_read(dev, dev->cx23417_mailbox + 4 + i, data + i);
+ dprintk(3, "API Output %d = %d\n", i, data[i]);
+ }
+
+ mc417_memory_read(dev, dev->cx23417_mailbox + 2, &retval);
+ dprintk(3, "API result = %d\n", retval);
+
+ flag = 0;
+ mc417_memory_write(dev, dev->cx23417_mailbox, flag);
+
+ return retval;
+}
+
+/* We don't need to call the API often, so using just one
+ * mailbox will probably suffice
+ */
+static int cx23885_api_cmd(struct cx23885_dev *dev,
+ u32 command,
+ u32 inputcnt,
+ u32 outputcnt,
+ ...)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ va_list vargs;
+ int i, err;
+
+ dprintk(3, "%s() cmds = 0x%08x\n", __func__, command);
+
+ va_start(vargs, outputcnt);
+ for (i = 0; i < inputcnt; i++)
+ data[i] = va_arg(vargs, int);
+
+ err = cx23885_mbox_func(dev, command, inputcnt, outputcnt, data);
+ for (i = 0; i < outputcnt; i++) {
+ int *vptr = va_arg(vargs, int *);
+ *vptr = data[i];
+ }
+ va_end(vargs);
+
+ return err;
+}
+
+static int cx23885_find_mailbox(struct cx23885_dev *dev)
+{
+ u32 signature[4] = {
+ 0x12345678, 0x34567812, 0x56781234, 0x78123456
+ };
+ int signaturecnt = 0;
+ u32 value;
+ int i;
+
+ dprintk(2, "%s()\n", __func__);
+
+ for (i = 0; i < CX23885_FIRM_IMAGE_SIZE; i++) {
+ mc417_memory_read(dev, i, &value);
+ if (value == signature[signaturecnt])
+ signaturecnt++;
+ else
+ signaturecnt = 0;
+ if (4 == signaturecnt) {
+ dprintk(1, "Mailbox signature found at 0x%x\n", i+1);
+ return i+1;
+ }
+ }
+ printk(KERN_ERR "Mailbox signature values not found!\n");
+ return -1;
+}
+
+static int cx23885_load_firmware(struct cx23885_dev *dev)
+{
+ static const unsigned char magic[8] = {
+ 0xa7, 0x0d, 0x00, 0x00, 0x66, 0xbb, 0x55, 0xaa
+ };
+ const struct firmware *firmware;
+ int i, retval = 0;
+ u32 value = 0;
+ u32 gpio_output = 0;
+ u32 gpio_value;
+ u32 checksum = 0;
+ u32 *dataptr;
+
+ dprintk(2, "%s()\n", __func__);
+
+ /* Save GPIO settings before reset of APU */
+ retval |= mc417_memory_read(dev, 0x9020, &gpio_output);
+ retval |= mc417_memory_read(dev, 0x900C, &gpio_value);
+
+ retval = mc417_register_write(dev,
+ IVTV_REG_VPU, 0xFFFFFFED);
+ retval |= mc417_register_write(dev,
+ IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST);
+ retval |= mc417_register_write(dev,
+ IVTV_REG_ENC_SDRAM_REFRESH, 0x80000800);
+ retval |= mc417_register_write(dev,
+ IVTV_REG_ENC_SDRAM_PRECHARGE, 0x1A);
+ retval |= mc417_register_write(dev,
+ IVTV_REG_APU, 0);
+
+ if (retval != 0) {
+ printk(KERN_ERR "%s: Error with mc417_register_write\n",
+ __func__);
+ return -1;
+ }
+
+ retval = request_firmware(&firmware, CX23885_FIRM_IMAGE_NAME,
+ &dev->pci->dev);
+
+ if (retval != 0) {
+ printk(KERN_ERR
+ "ERROR: Hotplug firmware request failed (%s).\n",
+ CX23885_FIRM_IMAGE_NAME);
+ printk(KERN_ERR "Please fix your hotplug setup, the board will "
+ "not work without firmware loaded!\n");
+ return -1;
+ }
+
+ if (firmware->size != CX23885_FIRM_IMAGE_SIZE) {
+ printk(KERN_ERR "ERROR: Firmware size mismatch "
+ "(have %zd, expected %d)\n",
+ firmware->size, CX23885_FIRM_IMAGE_SIZE);
+ release_firmware(firmware);
+ return -1;
+ }
+
+ if (0 != memcmp(firmware->data, magic, 8)) {
+ printk(KERN_ERR
+ "ERROR: Firmware magic mismatch, wrong file?\n");
+ release_firmware(firmware);
+ return -1;
+ }
+
+ /* transfer to the chip */
+ dprintk(2, "Loading firmware ...\n");
+ dataptr = (u32 *)firmware->data;
+ for (i = 0; i < (firmware->size >> 2); i++) {
+ value = *dataptr;
+ checksum += ~value;
+ if (mc417_memory_write(dev, i, value) != 0) {
+ printk(KERN_ERR "ERROR: Loading firmware failed!\n");
+ release_firmware(firmware);
+ return -1;
+ }
+ dataptr++;
+ }
+
+ /* read back to verify with the checksum */
+ dprintk(1, "Verifying firmware ...\n");
+ for (i--; i >= 0; i--) {
+ if (mc417_memory_read(dev, i, &value) != 0) {
+ printk(KERN_ERR "ERROR: Reading firmware failed!\n");
+ release_firmware(firmware);
+ return -1;
+ }
+ checksum -= ~value;
+ }
+ if (checksum) {
+ printk(KERN_ERR
+ "ERROR: Firmware load failed (checksum mismatch).\n");
+ release_firmware(firmware);
+ return -1;
+ }
+ release_firmware(firmware);
+ dprintk(1, "Firmware upload successful.\n");
+
+ retval |= mc417_register_write(dev, IVTV_REG_HW_BLOCKS,
+ IVTV_CMD_HW_BLOCKS_RST);
+
+ /* F/W power up disturbs the GPIOs, restore state */
+ retval |= mc417_register_write(dev, 0x9020, gpio_output);
+ retval |= mc417_register_write(dev, 0x900C, gpio_value);
+
+ retval |= mc417_register_read(dev, IVTV_REG_VPU, &value);
+ retval |= mc417_register_write(dev, IVTV_REG_VPU, value & 0xFFFFFFE8);
+
+ /* Hardcoded GPIO's here */
+ retval |= mc417_register_write(dev, 0x9020, 0x4000);
+ retval |= mc417_register_write(dev, 0x900C, 0x4000);
+
+ mc417_register_read(dev, 0x9020, &gpio_output);
+ mc417_register_read(dev, 0x900C, &gpio_value);
+
+ if (retval < 0)
+ printk(KERN_ERR "%s: Error with mc417_register_write\n",
+ __func__);
+ return 0;
+}
+
+void cx23885_417_check_encoder(struct cx23885_dev *dev)
+{
+ u32 status, seq;
+
+ status = seq = 0;
+ cx23885_api_cmd(dev, CX2341X_ENC_GET_SEQ_END, 0, 2, &status, &seq);
+ dprintk(1, "%s() status = %d, seq = %d\n", __func__, status, seq);
+}
+
+static void cx23885_codec_settings(struct cx23885_dev *dev)
+{
+ dprintk(1, "%s()\n", __func__);
+
+ /* Dynamically change the height based on video standard */
+ if (dev->encodernorm.id & V4L2_STD_525_60)
+ dev->ts1.height = 480;
+ else
+ dev->ts1.height = 576;
+
+ /* assign frame size */
+ cx23885_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0,
+ dev->ts1.height, dev->ts1.width);
+
+ dev->mpeg_params.width = dev->ts1.width;
+ dev->mpeg_params.height = dev->ts1.height;
+ dev->mpeg_params.is_50hz =
+ (dev->encodernorm.id & V4L2_STD_625_50) != 0;
+
+ cx2341x_update(dev, cx23885_mbox_func, NULL, &dev->mpeg_params);
+
+ cx23885_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 3, 1);
+ cx23885_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 4, 1);
+}
+
+static int cx23885_initialize_codec(struct cx23885_dev *dev, int startencoder)
+{
+ int version;
+ int retval;
+ u32 i, data[7];
+
+ dprintk(1, "%s()\n", __func__);
+
+ retval = cx23885_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */
+ if (retval < 0) {
+ dprintk(2, "%s() PING OK\n", __func__);
+ retval = cx23885_load_firmware(dev);
+ if (retval < 0) {
+ printk(KERN_ERR "%s() f/w load failed\n", __func__);
+ return retval;
+ }
+ retval = cx23885_find_mailbox(dev);
+ if (retval < 0) {
+ printk(KERN_ERR "%s() mailbox < 0, error\n",
+ __func__);
+ return -1;
+ }
+ dev->cx23417_mailbox = retval;
+ retval = cx23885_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0);
+ if (retval < 0) {
+ printk(KERN_ERR
+ "ERROR: cx23417 firmware ping failed!\n");
+ return -1;
+ }
+ retval = cx23885_api_cmd(dev, CX2341X_ENC_GET_VERSION, 0, 1,
+ &version);
+ if (retval < 0) {
+ printk(KERN_ERR "ERROR: cx23417 firmware get encoder :"
+ "version failed!\n");
+ return -1;
+ }
+ dprintk(1, "cx23417 firmware version is 0x%08x\n", version);
+ msleep(200);
+ }
+
+ cx23885_codec_settings(dev);
+ msleep(60);
+
+ cx23885_api_cmd(dev, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, 0,
+ CX23885_FIELD1_SAA7115, CX23885_FIELD2_SAA7115);
+ cx23885_api_cmd(dev, CX2341X_ENC_SET_PLACEHOLDER, 12, 0,
+ CX23885_CUSTOM_EXTENSION_USR_DATA, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0);
+
+ /* Setup to capture VBI */
+ data[0] = 0x0001BD00;
+ data[1] = 1; /* frames per interrupt */
+ data[2] = 4; /* total bufs */
+ data[3] = 0x91559155; /* start codes */
+ data[4] = 0x206080C0; /* stop codes */
+ data[5] = 6; /* lines */
+ data[6] = 64; /* BPL */
+
+ cx23885_api_cmd(dev, CX2341X_ENC_SET_VBI_CONFIG, 7, 0, data[0], data[1],
+ data[2], data[3], data[4], data[5], data[6]);
+
+ for (i = 2; i <= 24; i++) {
+ int valid;
+
+ valid = ((i >= 19) && (i <= 21));
+ cx23885_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0, i,
+ valid, 0 , 0, 0);
+ cx23885_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0,
+ i | 0x80000000, valid, 0, 0, 0);
+ }
+
+ cx23885_api_cmd(dev, CX2341X_ENC_MUTE_AUDIO, 1, 0, CX23885_UNMUTE);
+ msleep(60);
+
+ /* initialize the video input */
+ cx23885_api_cmd(dev, CX2341X_ENC_INITIALIZE_INPUT, 0, 0);
+ msleep(60);
+
+ /* Enable VIP style pixel invalidation so we work with scaled mode */
+ mc417_memory_write(dev, 2120, 0x00000080);
+
+ /* start capturing to the host interface */
+ if (startencoder) {
+ cx23885_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0,
+ CX23885_MPEG_CAPTURE, CX23885_RAW_BITS_NONE);
+ msleep(10);
+ }
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int bb_buf_setup(struct videobuf_queue *q,
+ unsigned int *count, unsigned int *size)
+{
+ struct cx23885_fh *fh = q->priv_data;
+
+ fh->dev->ts1.ts_packet_size = mpeglinesize;
+ fh->dev->ts1.ts_packet_count = mpeglines;
+
+ *size = fh->dev->ts1.ts_packet_size * fh->dev->ts1.ts_packet_count;
+ *count = mpegbufs;
+
+ return 0;
+}
+
+static int bb_buf_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb, enum v4l2_field field)
+{
+ struct cx23885_fh *fh = q->priv_data;
+ return cx23885_buf_prepare(q, &fh->dev->ts1,
+ (struct cx23885_buffer *)vb,
+ field);
+}
+
+static void bb_buf_queue(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ struct cx23885_fh *fh = q->priv_data;
+ cx23885_buf_queue(&fh->dev->ts1, (struct cx23885_buffer *)vb);
+}
+
+static void bb_buf_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ cx23885_free_buffer(q, (struct cx23885_buffer *)vb);
+}
+
+static struct videobuf_queue_ops cx23885_qops = {
+ .buf_setup = bb_buf_setup,
+ .buf_prepare = bb_buf_prepare,
+ .buf_queue = bb_buf_queue,
+ .buf_release = bb_buf_release,
+};
+
+/* ------------------------------------------------------------------ */
+
+static const u32 *ctrl_classes[] = {
+ cx2341x_mpeg_ctrls,
+ NULL
+};
+
+static int cx23885_queryctrl(struct cx23885_dev *dev,
+ struct v4l2_queryctrl *qctrl)
+{
+ qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
+ if (qctrl->id == 0)
+ return -EINVAL;
+
+ /* MPEG V4L2 controls */
+ if (cx2341x_ctrl_query(&dev->mpeg_params, qctrl))
+ qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+
+ return 0;
+}
+
+static int cx23885_querymenu(struct cx23885_dev *dev,
+ struct v4l2_querymenu *qmenu)
+{
+ struct v4l2_queryctrl qctrl;
+
+ qctrl.id = qmenu->id;
+ cx23885_queryctrl(dev, &qctrl);
+ return v4l2_ctrl_query_menu(qmenu, &qctrl,
+ cx2341x_ctrl_get_menu(&dev->mpeg_params, qmenu->id));
+}
+
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+ call_all(dev, core, g_std, id);
+
+ return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(cx23885_tvnorms); i++)
+ if (*id & cx23885_tvnorms[i].id)
+ break;
+ if (i == ARRAY_SIZE(cx23885_tvnorms))
+ return -EINVAL;
+ dev->encodernorm = cx23885_tvnorms[i];
+
+ /* Have the drier core notify the subdevices */
+ mutex_lock(&dev->lock);
+ cx23885_set_tvnorm(dev, *id);
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ dprintk(1, "%s()\n", __func__);
+ return cx23885_enum_input(dev, i);
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ return cx23885_get_input(file, priv, i);
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ return cx23885_set_input(file, priv, i);
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+ if (UNSET == dev->tuner_type)
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+ strcpy(t->name, "Television");
+ call_all(dev, tuner, g_tuner, t);
+
+ dprintk(1, "VIDIOC_G_TUNER: tuner type %d\n", t->type);
+
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+ if (UNSET == dev->tuner_type)
+ return -EINVAL;
+
+ /* Update the A/V core */
+ call_all(dev, tuner, s_tuner, t);
+
+ return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+ if (UNSET == dev->tuner_type)
+ return -EINVAL;
+ f->type = V4L2_TUNER_ANALOG_TV;
+ f->frequency = dev->freq;
+
+ call_all(dev, tuner, g_frequency, f);
+
+ return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ return cx23885_set_frequency(file, priv, f);
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctl)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ return cx23885_get_control(dev, ctl);
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctl)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ return cx23885_set_control(dev, ctl);
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+ struct cx23885_tsport *tsport = &dev->ts1;
+
+ strlcpy(cap->driver, dev->name, sizeof(cap->driver));
+ strlcpy(cap->card, cx23885_boards[tsport->dev->board].name,
+ sizeof(cap->card));
+ sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING |
+ 0;
+ if (UNSET != dev->tuner_type)
+ cap->capabilities |= V4L2_CAP_TUNER;
+
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index != 0)
+ return -EINVAL;
+
+ strlcpy(f->description, "MPEG", sizeof(f->description));
+ f->pixelformat = V4L2_PIX_FMT_MPEG;
+
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage =
+ dev->ts1.ts_packet_size * dev->ts1.ts_packet_count;
+ f->fmt.pix.colorspace = 0;
+ f->fmt.pix.width = dev->ts1.width;
+ f->fmt.pix.height = dev->ts1.height;
+ f->fmt.pix.field = fh->mpegq.field;
+ dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n",
+ dev->ts1.width, dev->ts1.height, fh->mpegq.field);
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage =
+ dev->ts1.ts_packet_size * dev->ts1.ts_packet_count;
+ f->fmt.pix.colorspace = 0;
+ dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n",
+ dev->ts1.width, dev->ts1.height, fh->mpegq.field);
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage =
+ dev->ts1.ts_packet_size * dev->ts1.ts_packet_count;
+ f->fmt.pix.colorspace = 0;
+ dprintk(1, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n",
+ f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
+ return 0;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct cx23885_fh *fh = file->private_data;
+
+ return videobuf_reqbufs(&fh->mpegq, p);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct cx23885_fh *fh = file->private_data;
+
+ return videobuf_querybuf(&fh->mpegq, p);
+}
+
+static int vidioc_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct cx23885_fh *fh = file->private_data;
+
+ return videobuf_qbuf(&fh->mpegq, p);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct cx23885_fh *fh = priv;
+
+ return videobuf_dqbuf(&fh->mpegq, b, file->f_flags & O_NONBLOCK);
+}
+
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type i)
+{
+ struct cx23885_fh *fh = file->private_data;
+
+ return videobuf_streamon(&fh->mpegq);
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct cx23885_fh *fh = file->private_data;
+
+ return videobuf_streamoff(&fh->mpegq);
+}
+
+static int vidioc_g_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *f)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+
+ if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+ return -EINVAL;
+ return cx2341x_ext_ctrls(&dev->mpeg_params, 0, f, VIDIOC_G_EXT_CTRLS);
+}
+
+static int vidioc_s_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *f)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+ struct cx2341x_mpeg_params p;
+ int err;
+
+ if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+ return -EINVAL;
+
+ p = dev->mpeg_params;
+ err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_S_EXT_CTRLS);
+
+ if (err == 0) {
+ err = cx2341x_update(dev, cx23885_mbox_func,
+ &dev->mpeg_params, &p);
+ dev->mpeg_params = p;
+ }
+ return err;
+}
+
+static int vidioc_try_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *f)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+ struct cx2341x_mpeg_params p;
+ int err;
+
+ if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+ return -EINVAL;
+
+ p = dev->mpeg_params;
+ err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_TRY_EXT_CTRLS);
+ return err;
+}
+
+static int vidioc_log_status(struct file *file, void *priv)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+ char name[32 + 2];
+
+ snprintf(name, sizeof(name), "%s/2", dev->name);
+ printk(KERN_INFO
+ "%s/2: ============ START LOG STATUS ============\n",
+ dev->name);
+ call_all(dev, core, log_status);
+ cx2341x_log_status(&dev->mpeg_params, name);
+ printk(KERN_INFO
+ "%s/2: ============= END LOG STATUS =============\n",
+ dev->name);
+ return 0;
+}
+
+static int vidioc_querymenu(struct file *file, void *priv,
+ struct v4l2_querymenu *a)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+
+ return cx23885_querymenu(dev, a);
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+
+ return cx23885_queryctrl(dev, c);
+}
+
+static int mpeg_open(struct file *file)
+{
+ struct cx23885_dev *dev = video_drvdata(file);
+ struct cx23885_fh *fh;
+
+ dprintk(2, "%s()\n", __func__);
+
+ /* allocate + initialize per filehandle data */
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (!fh)
+ return -ENOMEM;
+
+ file->private_data = fh;
+ fh->dev = dev;
+
+ videobuf_queue_sg_init(&fh->mpegq, &cx23885_qops,
+ &dev->pci->dev, &dev->ts1.slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct cx23885_buffer),
+ fh, NULL);
+ return 0;
+}
+
+static int mpeg_release(struct file *file)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+ dprintk(2, "%s()\n", __func__);
+
+ /* FIXME: Review this crap */
+ /* Shut device down on last close */
+ if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) {
+ if (atomic_dec_return(&dev->v4l_reader_count) == 0) {
+ /* stop mpeg capture */
+ cx23885_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0,
+ CX23885_END_NOW, CX23885_MPEG_CAPTURE,
+ CX23885_RAW_BITS_NONE);
+
+ msleep(500);
+ cx23885_417_check_encoder(dev);
+
+ cx23885_cancel_buffers(&fh->dev->ts1);
+ }
+ }
+
+ if (fh->mpegq.streaming)
+ videobuf_streamoff(&fh->mpegq);
+ if (fh->mpegq.reading)
+ videobuf_read_stop(&fh->mpegq);
+
+ videobuf_mmap_free(&fh->mpegq);
+ file->private_data = NULL;
+ kfree(fh);
+
+ return 0;
+}
+
+static ssize_t mpeg_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+ dprintk(2, "%s()\n", __func__);
+
+ /* Deal w/ A/V decoder * and mpeg encoder sync issues. */
+ /* Start mpeg encoder on first read. */
+ if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
+ if (atomic_inc_return(&dev->v4l_reader_count) == 1) {
+ if (cx23885_initialize_codec(dev, 1) < 0)
+ return -EINVAL;
+ }
+ }
+
+ return videobuf_read_stream(&fh->mpegq, data, count, ppos, 0,
+ file->f_flags & O_NONBLOCK);
+}
+
+static unsigned int mpeg_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+ dprintk(2, "%s\n", __func__);
+
+ return videobuf_poll_stream(file, &fh->mpegq, wait);
+}
+
+static int mpeg_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+ dprintk(2, "%s()\n", __func__);
+
+ return videobuf_mmap_mapper(&fh->mpegq, vma);
+}
+
+static struct v4l2_file_operations mpeg_fops = {
+ .owner = THIS_MODULE,
+ .open = mpeg_open,
+ .release = mpeg_release,
+ .read = mpeg_read,
+ .poll = mpeg_poll,
+ .mmap = mpeg_mmap,
+ .ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
+ .vidioc_querystd = vidioc_g_std,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls,
+ .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls,
+ .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls,
+ .vidioc_log_status = vidioc_log_status,
+ .vidioc_querymenu = vidioc_querymenu,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_chip_ident = cx23885_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = cx23885_g_register,
+ .vidioc_s_register = cx23885_s_register,
+#endif
+};
+
+static struct video_device cx23885_mpeg_template = {
+ .name = "cx23885",
+ .fops = &mpeg_fops,
+ .ioctl_ops = &mpeg_ioctl_ops,
+ .tvnorms = CX23885_NORMS,
+ .current_norm = V4L2_STD_NTSC_M,
+};
+
+void cx23885_417_unregister(struct cx23885_dev *dev)
+{
+ dprintk(1, "%s()\n", __func__);
+
+ if (dev->v4l_device) {
+ if (video_is_registered(dev->v4l_device))
+ video_unregister_device(dev->v4l_device);
+ else
+ video_device_release(dev->v4l_device);
+ dev->v4l_device = NULL;
+ }
+}
+
+static struct video_device *cx23885_video_dev_alloc(
+ struct cx23885_tsport *tsport,
+ struct pci_dev *pci,
+ struct video_device *template,
+ char *type)
+{
+ struct video_device *vfd;
+ struct cx23885_dev *dev = tsport->dev;
+
+ dprintk(1, "%s()\n", __func__);
+
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+ *vfd = *template;
+ snprintf(vfd->name, sizeof(vfd->name), "%s (%s)",
+ cx23885_boards[tsport->dev->board].name, type);
+ vfd->parent = &pci->dev;
+ vfd->release = video_device_release;
+ return vfd;
+}
+
+int cx23885_417_register(struct cx23885_dev *dev)
+{
+ /* FIXME: Port1 hardcoded here */
+ int err = -ENODEV;
+ struct cx23885_tsport *tsport = &dev->ts1;
+
+ dprintk(1, "%s()\n", __func__);
+
+ if (cx23885_boards[dev->board].portb != CX23885_MPEG_ENCODER)
+ return err;
+
+ /* Set default TV standard */
+ dev->encodernorm = cx23885_tvnorms[0];
+
+ if (dev->encodernorm.id & V4L2_STD_525_60)
+ tsport->height = 480;
+ else
+ tsport->height = 576;
+
+ tsport->width = 720;
+ cx2341x_fill_defaults(&dev->mpeg_params);
+
+ dev->mpeg_params.port = CX2341X_PORT_SERIAL;
+
+ /* Allocate and initialize V4L video device */
+ dev->v4l_device = cx23885_video_dev_alloc(tsport,
+ dev->pci, &cx23885_mpeg_template, "mpeg");
+ video_set_drvdata(dev->v4l_device, dev);
+ err = video_register_device(dev->v4l_device,
+ VFL_TYPE_GRABBER, -1);
+ if (err < 0) {
+ printk(KERN_INFO "%s: can't register mpeg device\n", dev->name);
+ return err;
+ }
+
+ printk(KERN_INFO "%s: registered device %s [mpeg]\n",
+ dev->name, video_device_node_name(dev->v4l_device));
+
+ /* ST: Configure the encoder paramaters, but don't begin
+ * encoding, this resolves an issue where the first time the
+ * encoder is started video can be choppy.
+ */
+ cx23885_initialize_codec(dev, 0);
+
+ return 0;
+}
diff --git a/drivers/media/video/cx23885/cx23885-alsa.c b/drivers/media/video/cx23885/cx23885-alsa.c
new file mode 100644
index 00000000..79516923
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-alsa.c
@@ -0,0 +1,535 @@
+/*
+ *
+ * Support for CX23885 analog audio capture
+ *
+ * (c) 2008 Mijhail Moreyra <mijhail.moreyra@gmail.com>
+ * Adapted from cx88-alsa.c
+ * (c) 2009 Steven Toth <stoth@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/vmalloc.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+
+#include <asm/delay.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+
+#include <sound/tlv.h>
+
+
+#include "cx23885.h"
+#include "cx23885-reg.h"
+
+#define AUDIO_SRAM_CHANNEL SRAM_CH07
+
+#define dprintk(level, fmt, arg...) if (audio_debug >= level) \
+ printk(KERN_INFO "%s: " fmt, chip->dev->name , ## arg)
+
+#define dprintk_core(level, fmt, arg...) if (audio_debug >= level) \
+ printk(KERN_DEBUG "%s: " fmt, chip->dev->name , ## arg)
+
+/****************************************************************************
+ Module global static vars
+ ****************************************************************************/
+
+static unsigned int disable_analog_audio;
+module_param(disable_analog_audio, int, 0644);
+MODULE_PARM_DESC(disable_analog_audio, "disable analog audio ALSA driver");
+
+static unsigned int audio_debug;
+module_param(audio_debug, int, 0644);
+MODULE_PARM_DESC(audio_debug, "enable debug messages [analog audio]");
+
+/****************************************************************************
+ Board specific funtions
+ ****************************************************************************/
+
+/* Constants taken from cx88-reg.h */
+#define AUD_INT_DN_RISCI1 (1 << 0)
+#define AUD_INT_UP_RISCI1 (1 << 1)
+#define AUD_INT_RDS_DN_RISCI1 (1 << 2)
+#define AUD_INT_DN_RISCI2 (1 << 4) /* yes, 3 is skipped */
+#define AUD_INT_UP_RISCI2 (1 << 5)
+#define AUD_INT_RDS_DN_RISCI2 (1 << 6)
+#define AUD_INT_DN_SYNC (1 << 12)
+#define AUD_INT_UP_SYNC (1 << 13)
+#define AUD_INT_RDS_DN_SYNC (1 << 14)
+#define AUD_INT_OPC_ERR (1 << 16)
+#define AUD_INT_BER_IRQ (1 << 20)
+#define AUD_INT_MCHG_IRQ (1 << 21)
+#define GP_COUNT_CONTROL_RESET 0x3
+
+/*
+ * BOARD Specific: Sets audio DMA
+ */
+
+static int cx23885_start_audio_dma(struct cx23885_audio_dev *chip)
+{
+ struct cx23885_audio_buffer *buf = chip->buf;
+ struct cx23885_dev *dev = chip->dev;
+ struct sram_channel *audio_ch =
+ &dev->sram_channels[AUDIO_SRAM_CHANNEL];
+
+ dprintk(1, "%s()\n", __func__);
+
+ /* Make sure RISC/FIFO are off before changing FIFO/RISC settings */
+ cx_clear(AUD_INT_DMA_CTL, 0x11);
+
+ /* setup fifo + format - out channel */
+ cx23885_sram_channel_setup(chip->dev, audio_ch, buf->bpl,
+ buf->risc.dma);
+
+ /* sets bpl size */
+ cx_write(AUD_INT_A_LNGTH, buf->bpl);
+
+ /* This is required to get good audio (1 seems to be ok) */
+ cx_write(AUD_INT_A_MODE, 1);
+
+ /* reset counter */
+ cx_write(AUD_INT_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET);
+ atomic_set(&chip->count, 0);
+
+ dprintk(1, "Start audio DMA, %d B/line, %d lines/FIFO, %d periods, %d "
+ "byte buffer\n", buf->bpl, cx_read(audio_ch->cmds_start+12)>>1,
+ chip->num_periods, buf->bpl * chip->num_periods);
+
+ /* Enables corresponding bits at AUD_INT_STAT */
+ cx_write(AUDIO_INT_INT_MSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC |
+ AUD_INT_DN_RISCI1);
+
+ /* Clean any pending interrupt bits already set */
+ cx_write(AUDIO_INT_INT_STAT, ~0);
+
+ /* enable audio irqs */
+ cx_set(PCI_INT_MSK, chip->dev->pci_irqmask | PCI_MSK_AUD_INT);
+
+ /* start dma */
+ cx_set(DEV_CNTRL2, (1<<5)); /* Enables Risc Processor */
+ cx_set(AUD_INT_DMA_CTL, 0x11); /* audio downstream FIFO and
+ RISC enable */
+ if (audio_debug)
+ cx23885_sram_channel_dump(chip->dev, audio_ch);
+
+ return 0;
+}
+
+/*
+ * BOARD Specific: Resets audio DMA
+ */
+static int cx23885_stop_audio_dma(struct cx23885_audio_dev *chip)
+{
+ struct cx23885_dev *dev = chip->dev;
+ dprintk(1, "Stopping audio DMA\n");
+
+ /* stop dma */
+ cx_clear(AUD_INT_DMA_CTL, 0x11);
+
+ /* disable irqs */
+ cx_clear(PCI_INT_MSK, PCI_MSK_AUD_INT);
+ cx_clear(AUDIO_INT_INT_MSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC |
+ AUD_INT_DN_RISCI1);
+
+ if (audio_debug)
+ cx23885_sram_channel_dump(chip->dev,
+ &dev->sram_channels[AUDIO_SRAM_CHANNEL]);
+
+ return 0;
+}
+
+/*
+ * BOARD Specific: Handles audio IRQ
+ */
+int cx23885_audio_irq(struct cx23885_dev *dev, u32 status, u32 mask)
+{
+ struct cx23885_audio_dev *chip = dev->audio_dev;
+
+ if (0 == (status & mask))
+ return 0;
+
+ cx_write(AUDIO_INT_INT_STAT, status);
+
+ /* risc op code error */
+ if (status & AUD_INT_OPC_ERR) {
+ printk(KERN_WARNING "%s/1: Audio risc op code error\n",
+ dev->name);
+ cx_clear(AUD_INT_DMA_CTL, 0x11);
+ cx23885_sram_channel_dump(dev,
+ &dev->sram_channels[AUDIO_SRAM_CHANNEL]);
+ }
+ if (status & AUD_INT_DN_SYNC) {
+ dprintk(1, "Downstream sync error\n");
+ cx_write(AUD_INT_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET);
+ return 1;
+ }
+ /* risc1 downstream */
+ if (status & AUD_INT_DN_RISCI1) {
+ atomic_set(&chip->count, cx_read(AUD_INT_A_GPCNT));
+ snd_pcm_period_elapsed(chip->substream);
+ }
+ /* FIXME: Any other status should deserve a special handling? */
+
+ return 1;
+}
+
+static int dsp_buffer_free(struct cx23885_audio_dev *chip)
+{
+ BUG_ON(!chip->dma_size);
+
+ dprintk(2, "Freeing buffer\n");
+ videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc);
+ videobuf_dma_free(chip->dma_risc);
+ btcx_riscmem_free(chip->pci, &chip->buf->risc);
+ kfree(chip->buf);
+
+ chip->dma_risc = NULL;
+ chip->dma_size = 0;
+
+ return 0;
+}
+
+/****************************************************************************
+ ALSA PCM Interface
+ ****************************************************************************/
+
+/*
+ * Digital hardware definition
+ */
+#define DEFAULT_FIFO_SIZE 4096
+
+static struct snd_pcm_hardware snd_cx23885_digital_hw = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* Analog audio output will be full of clicks and pops if there
+ are not exactly four lines in the SRAM FIFO buffer. */
+ .period_bytes_min = DEFAULT_FIFO_SIZE/4,
+ .period_bytes_max = DEFAULT_FIFO_SIZE/4,
+ .periods_min = 1,
+ .periods_max = 1024,
+ .buffer_bytes_max = (1024*1024),
+};
+
+/*
+ * audio pcm capture open callback
+ */
+static int snd_cx23885_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ if (!chip) {
+ printk(KERN_ERR "BUG: cx23885 can't find device struct."
+ " Can't proceed with open\n");
+ return -ENODEV;
+ }
+
+ err = snd_pcm_hw_constraint_pow2(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
+ goto _error;
+
+ chip->substream = substream;
+
+ runtime->hw = snd_cx23885_digital_hw;
+
+ if (chip->dev->sram_channels[AUDIO_SRAM_CHANNEL].fifo_size !=
+ DEFAULT_FIFO_SIZE) {
+ unsigned int bpl = chip->dev->
+ sram_channels[AUDIO_SRAM_CHANNEL].fifo_size / 4;
+ bpl &= ~7; /* must be multiple of 8 */
+ runtime->hw.period_bytes_min = bpl;
+ runtime->hw.period_bytes_max = bpl;
+ }
+
+ return 0;
+_error:
+ dprintk(1, "Error opening PCM!\n");
+ return err;
+}
+
+/*
+ * audio close callback
+ */
+static int snd_cx23885_close(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+/*
+ * hw_params callback
+ */
+static int snd_cx23885_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);
+ struct videobuf_dmabuf *dma;
+
+ struct cx23885_audio_buffer *buf;
+ int ret;
+
+ if (substream->runtime->dma_area) {
+ dsp_buffer_free(chip);
+ substream->runtime->dma_area = NULL;
+ }
+
+ chip->period_size = params_period_bytes(hw_params);
+ chip->num_periods = params_periods(hw_params);
+ chip->dma_size = chip->period_size * params_periods(hw_params);
+
+ BUG_ON(!chip->dma_size);
+ BUG_ON(chip->num_periods & (chip->num_periods-1));
+
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (NULL == buf)
+ return -ENOMEM;
+
+ buf->bpl = chip->period_size;
+
+ dma = &buf->dma;
+ videobuf_dma_init(dma);
+ ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE,
+ (PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT));
+ if (ret < 0)
+ goto error;
+
+ ret = videobuf_dma_map(&chip->pci->dev, dma);
+ if (ret < 0)
+ goto error;
+
+ ret = cx23885_risc_databuffer(chip->pci, &buf->risc, dma->sglist,
+ chip->period_size, chip->num_periods, 1);
+ if (ret < 0)
+ goto error;
+
+ /* Loop back to start of program */
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP|RISC_IRQ1|RISC_CNT_INC);
+ buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
+
+ chip->buf = buf;
+ chip->dma_risc = dma;
+
+ substream->runtime->dma_area = chip->dma_risc->vaddr;
+ substream->runtime->dma_bytes = chip->dma_size;
+ substream->runtime->dma_addr = 0;
+
+ return 0;
+
+error:
+ kfree(buf);
+ return ret;
+}
+
+/*
+ * hw free callback
+ */
+static int snd_cx23885_hw_free(struct snd_pcm_substream *substream)
+{
+
+ struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);
+
+ if (substream->runtime->dma_area) {
+ dsp_buffer_free(chip);
+ substream->runtime->dma_area = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * prepare callback
+ */
+static int snd_cx23885_prepare(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+/*
+ * trigger callback
+ */
+static int snd_cx23885_card_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);
+ int err;
+
+ /* Local interrupts are already disabled by ALSA */
+ spin_lock(&chip->lock);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ err = cx23885_start_audio_dma(chip);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ err = cx23885_stop_audio_dma(chip);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ spin_unlock(&chip->lock);
+
+ return err;
+}
+
+/*
+ * pointer callback
+ */
+static snd_pcm_uframes_t snd_cx23885_pointer(
+ struct snd_pcm_substream *substream)
+{
+ struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ u16 count;
+
+ count = atomic_read(&chip->count);
+
+ return runtime->period_size * (count & (runtime->periods-1));
+}
+
+/*
+ * page callback (needed for mmap)
+ */
+static struct page *snd_cx23885_page(struct snd_pcm_substream *substream,
+ unsigned long offset)
+{
+ void *pageptr = substream->runtime->dma_area + offset;
+ return vmalloc_to_page(pageptr);
+}
+
+/*
+ * operators
+ */
+static struct snd_pcm_ops snd_cx23885_pcm_ops = {
+ .open = snd_cx23885_pcm_open,
+ .close = snd_cx23885_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_cx23885_hw_params,
+ .hw_free = snd_cx23885_hw_free,
+ .prepare = snd_cx23885_prepare,
+ .trigger = snd_cx23885_card_trigger,
+ .pointer = snd_cx23885_pointer,
+ .page = snd_cx23885_page,
+};
+
+/*
+ * create a PCM device
+ */
+static int snd_cx23885_pcm(struct cx23885_audio_dev *chip, int device,
+ char *name)
+{
+ int err;
+ struct snd_pcm *pcm;
+
+ err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm);
+ if (err < 0)
+ return err;
+ pcm->private_data = chip;
+ strcpy(pcm->name, name);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx23885_pcm_ops);
+
+ return 0;
+}
+
+/****************************************************************************
+ Basic Flow for Sound Devices
+ ****************************************************************************/
+
+/*
+ * Alsa Constructor - Component probe
+ */
+
+struct cx23885_audio_dev *cx23885_audio_register(struct cx23885_dev *dev)
+{
+ struct snd_card *card;
+ struct cx23885_audio_dev *chip;
+ int err;
+
+ if (disable_analog_audio)
+ return NULL;
+
+ if (dev->sram_channels[AUDIO_SRAM_CHANNEL].cmds_start == 0) {
+ printk(KERN_WARNING "%s(): Missing SRAM channel configuration "
+ "for analog TV Audio\n", __func__);
+ return NULL;
+ }
+
+ err = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+ THIS_MODULE, sizeof(struct cx23885_audio_dev), &card);
+ if (err < 0)
+ goto error;
+
+ chip = (struct cx23885_audio_dev *) card->private_data;
+ chip->dev = dev;
+ chip->pci = dev->pci;
+ chip->card = card;
+ spin_lock_init(&chip->lock);
+
+ snd_card_set_dev(card, &dev->pci->dev);
+
+ err = snd_cx23885_pcm(chip, 0, "CX23885 Digital");
+ if (err < 0)
+ goto error;
+
+ strcpy(card->driver, "CX23885");
+ sprintf(card->shortname, "Conexant CX23885");
+ sprintf(card->longname, "%s at %s", card->shortname, dev->name);
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ dprintk(0, "registered ALSA audio device\n");
+
+ return chip;
+
+error:
+ snd_card_free(card);
+ printk(KERN_ERR "%s(): Failed to register analog "
+ "audio adapter\n", __func__);
+
+ return NULL;
+}
+
+/*
+ * ALSA destructor
+ */
+void cx23885_audio_unregister(struct cx23885_dev *dev)
+{
+ struct cx23885_audio_dev *chip = dev->audio_dev;
+
+ snd_card_free(chip->card);
+}
diff --git a/drivers/media/video/cx23885/cx23885-av.c b/drivers/media/video/cx23885/cx23885-av.c
new file mode 100644
index 00000000..134ebddd
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-av.c
@@ -0,0 +1,35 @@
+/*
+ * Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ * AV device support routines - non-input, non-vl42_subdev routines
+ *
+ * Copyright (C) 2010 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "cx23885.h"
+
+void cx23885_av_work_handler(struct work_struct *work)
+{
+ struct cx23885_dev *dev =
+ container_of(work, struct cx23885_dev, cx25840_work);
+ bool handled;
+
+ v4l2_subdev_call(dev->sd_cx25840, core, interrupt_service_routine,
+ PCI_MSK_AV_CORE, &handled);
+ cx23885_irq_enable(dev, PCI_MSK_AV_CORE);
+}
diff --git a/drivers/media/video/cx23885/cx23885-av.h b/drivers/media/video/cx23885/cx23885-av.h
new file mode 100644
index 00000000..d2915c3e
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-av.h
@@ -0,0 +1,27 @@
+/*
+ * Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ * AV device support routines - non-input, non-vl42_subdev routines
+ *
+ * Copyright (C) 2010 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef _CX23885_AV_H_
+#define _CX23885_AV_H_
+void cx23885_av_work_handler(struct work_struct *work);
+#endif
diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c
new file mode 100644
index 00000000..19b5499d
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-cards.c
@@ -0,0 +1,1602 @@
+/*
+ * Driver for the Conexant CX23885 PCIe bridge
+ *
+ * Copyright (c) 2006 Steven Toth <stoth@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <media/cx25840.h>
+#include <linux/firmware.h>
+#include <misc/altera.h>
+
+#include "cx23885.h"
+#include "tuner-xc2028.h"
+#include "netup-eeprom.h"
+#include "netup-init.h"
+#include "altera-ci.h"
+#include "xc4000.h"
+#include "xc5000.h"
+#include "cx23888-ir.h"
+
+static unsigned int netup_card_rev = 1;
+module_param(netup_card_rev, int, 0644);
+MODULE_PARM_DESC(netup_card_rev,
+ "NetUP Dual DVB-T/C CI card revision");
+static unsigned int enable_885_ir;
+module_param(enable_885_ir, int, 0644);
+MODULE_PARM_DESC(enable_885_ir,
+ "Enable integrated IR controller for supported\n"
+ "\t\t CX2388[57] boards that are wired for it:\n"
+ "\t\t\tHVR-1250 (reported safe)\n"
+ "\t\t\tTeVii S470 (reported unsafe)\n"
+ "\t\t This can cause an interrupt storm with some cards.\n"
+ "\t\t Default: 0 [Disabled]");
+
+/* ------------------------------------------------------------------ */
+/* board config info */
+
+struct cx23885_board cx23885_boards[] = {
+ [CX23885_BOARD_UNKNOWN] = {
+ .name = "UNKNOWN/GENERIC",
+ /* Ensure safe default for unknown boards */
+ .clk_freq = 0,
+ .input = {{
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = 0,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE2,
+ .vmux = 1,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE3,
+ .vmux = 2,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE4,
+ .vmux = 3,
+ } },
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1800lp] = {
+ .name = "Hauppauge WinTV-HVR1800lp",
+ .portc = CX23885_MPEG_DVB,
+ .input = {{
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0xff00,
+ }, {
+ .type = CX23885_VMUX_DEBUG,
+ .vmux = 0,
+ .gpio0 = 0xff01,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0xff02,
+ }, {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0xff02,
+ } },
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1800] = {
+ .name = "Hauppauge WinTV-HVR1800",
+ .porta = CX23885_ANALOG_VIDEO,
+ .portb = CX23885_MPEG_ENCODER,
+ .portc = CX23885_MPEG_DVB,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .tuner_addr = 0x42, /* 0x84 >> 1 */
+ .tuner_bus = 1,
+ .input = {{
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN5_CH2 |
+ CX25840_VIN2_CH1,
+ .amux = CX25840_AUDIO8,
+ .gpio0 = 0,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN6_CH1,
+ .amux = CX25840_AUDIO7,
+ .gpio0 = 0,
+ }, {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN8_CH1 |
+ CX25840_SVIDEO_ON,
+ .amux = CX25840_AUDIO7,
+ .gpio0 = 0,
+ } },
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1250] = {
+ .name = "Hauppauge WinTV-HVR1250",
+ .portc = CX23885_MPEG_DVB,
+ .input = {{
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0xff00,
+ }, {
+ .type = CX23885_VMUX_DEBUG,
+ .vmux = 0,
+ .gpio0 = 0xff01,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0xff02,
+ }, {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0xff02,
+ } },
+ },
+ [CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP] = {
+ .name = "DViCO FusionHDTV5 Express",
+ .portb = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1500Q] = {
+ .name = "Hauppauge WinTV-HVR1500Q",
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1500] = {
+ .name = "Hauppauge WinTV-HVR1500",
+ .porta = CX23885_ANALOG_VIDEO,
+ .portc = CX23885_MPEG_DVB,
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0x61, /* 0xc2 >> 1 */
+ .input = {{
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN5_CH2 |
+ CX25840_VIN2_CH1,
+ .gpio0 = 0,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN6_CH1,
+ .gpio0 = 0,
+ }, {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN8_CH1 |
+ CX25840_SVIDEO_ON,
+ .gpio0 = 0,
+ } },
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1200] = {
+ .name = "Hauppauge WinTV-HVR1200",
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1700] = {
+ .name = "Hauppauge WinTV-HVR1700",
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1400] = {
+ .name = "Hauppauge WinTV-HVR1400",
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP] = {
+ .name = "DViCO FusionHDTV7 Dual Express",
+ .portb = CX23885_MPEG_DVB,
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP] = {
+ .name = "DViCO FusionHDTV DVB-T Dual Express",
+ .portb = CX23885_MPEG_DVB,
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H] = {
+ .name = "Leadtek Winfast PxDVR3200 H",
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000] = {
+ .name = "Leadtek Winfast PxDVR3200 H XC4000",
+ .porta = CX23885_ANALOG_VIDEO,
+ .portc = CX23885_MPEG_DVB,
+ .tuner_type = TUNER_XC4000,
+ .tuner_addr = 0x61,
+ .radio_type = UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_VIN2_CH1 |
+ CX25840_VIN5_CH2 |
+ CX25840_NONE0_CH3,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_COMPOSITE1,
+ }, {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_SVIDEO_LUMA3 |
+ CX25840_SVIDEO_CHROMA4,
+ }, {
+ .type = CX23885_VMUX_COMPONENT,
+ .vmux = CX25840_VIN7_CH1 |
+ CX25840_VIN6_CH2 |
+ CX25840_VIN8_CH3 |
+ CX25840_COMPONENT_ON,
+ } },
+ },
+ [CX23885_BOARD_COMPRO_VIDEOMATE_E650F] = {
+ .name = "Compro VideoMate E650F",
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_TBS_6920] = {
+ .name = "TurboSight TBS 6920",
+ .portb = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_TEVII_S470] = {
+ .name = "TeVii S470",
+ .portb = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_DVBWORLD_2005] = {
+ .name = "DVBWorld DVB-S2 2005",
+ .portb = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_NETUP_DUAL_DVBS2_CI] = {
+ .ci_type = 1,
+ .name = "NetUP Dual DVB-S2 CI",
+ .portb = CX23885_MPEG_DVB,
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1270] = {
+ .name = "Hauppauge WinTV-HVR1270",
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1275] = {
+ .name = "Hauppauge WinTV-HVR1275",
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1255] = {
+ .name = "Hauppauge WinTV-HVR1255",
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1210] = {
+ .name = "Hauppauge WinTV-HVR1210",
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_MYGICA_X8506] = {
+ .name = "Mygica X8506 DMB-TH",
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0x61,
+ .tuner_bus = 1,
+ .porta = CX23885_ANALOG_VIDEO,
+ .portb = CX23885_MPEG_DVB,
+ .input = {
+ {
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_COMPOSITE2,
+ },
+ {
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_COMPOSITE8,
+ },
+ {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_SVIDEO_LUMA3 |
+ CX25840_SVIDEO_CHROMA4,
+ },
+ {
+ .type = CX23885_VMUX_COMPONENT,
+ .vmux = CX25840_COMPONENT_ON |
+ CX25840_VIN1_CH1 |
+ CX25840_VIN6_CH2 |
+ CX25840_VIN7_CH3,
+ },
+ },
+ },
+ [CX23885_BOARD_MAGICPRO_PROHDTVE2] = {
+ .name = "Magic-Pro ProHDTV Extreme 2",
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0x61,
+ .tuner_bus = 1,
+ .porta = CX23885_ANALOG_VIDEO,
+ .portb = CX23885_MPEG_DVB,
+ .input = {
+ {
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_COMPOSITE2,
+ },
+ {
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_COMPOSITE8,
+ },
+ {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_SVIDEO_LUMA3 |
+ CX25840_SVIDEO_CHROMA4,
+ },
+ {
+ .type = CX23885_VMUX_COMPONENT,
+ .vmux = CX25840_COMPONENT_ON |
+ CX25840_VIN1_CH1 |
+ CX25840_VIN6_CH2 |
+ CX25840_VIN7_CH3,
+ },
+ },
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1850] = {
+ .name = "Hauppauge WinTV-HVR1850",
+ .porta = CX23885_ANALOG_VIDEO,
+ .portb = CX23885_MPEG_ENCODER,
+ .portc = CX23885_MPEG_DVB,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = 0x42, /* 0x84 >> 1 */
+ .force_bff = 1,
+ .input = {{
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN5_CH2 |
+ CX25840_VIN2_CH1 |
+ CX25840_DIF_ON,
+ .amux = CX25840_AUDIO8,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN6_CH1,
+ .amux = CX25840_AUDIO7,
+ }, {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN8_CH1 |
+ CX25840_SVIDEO_ON,
+ .amux = CX25840_AUDIO7,
+ } },
+ },
+ [CX23885_BOARD_COMPRO_VIDEOMATE_E800] = {
+ .name = "Compro VideoMate E800",
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1290] = {
+ .name = "Hauppauge WinTV-HVR1290",
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_MYGICA_X8558PRO] = {
+ .name = "Mygica X8558 PRO DMB-TH",
+ .portb = CX23885_MPEG_DVB,
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_LEADTEK_WINFAST_PXTV1200] = {
+ .name = "LEADTEK WinFast PxTV1200",
+ .porta = CX23885_ANALOG_VIDEO,
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0x61,
+ .tuner_bus = 1,
+ .input = {{
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_VIN2_CH1 |
+ CX25840_VIN5_CH2 |
+ CX25840_NONE0_CH3,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_COMPOSITE1,
+ }, {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_SVIDEO_LUMA3 |
+ CX25840_SVIDEO_CHROMA4,
+ }, {
+ .type = CX23885_VMUX_COMPONENT,
+ .vmux = CX25840_VIN7_CH1 |
+ CX25840_VIN6_CH2 |
+ CX25840_VIN8_CH3 |
+ CX25840_COMPONENT_ON,
+ } },
+ },
+ [CX23885_BOARD_GOTVIEW_X5_3D_HYBRID] = {
+ .name = "GoTView X5 3D Hybrid",
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0x64,
+ .tuner_bus = 1,
+ .porta = CX23885_ANALOG_VIDEO,
+ .portb = CX23885_MPEG_DVB,
+ .input = {{
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_VIN2_CH1 |
+ CX25840_VIN5_CH2,
+ .gpio0 = 0x02,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX23885_VMUX_COMPOSITE1,
+ }, {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_SVIDEO_LUMA3 |
+ CX25840_SVIDEO_CHROMA4,
+ } },
+ },
+ [CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF] = {
+ .ci_type = 2,
+ .name = "NetUP Dual DVB-T/C-CI RF",
+ .porta = CX23885_ANALOG_VIDEO,
+ .portb = CX23885_MPEG_DVB,
+ .portc = CX23885_MPEG_DVB,
+ .num_fds_portb = 2,
+ .num_fds_portc = 2,
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0x64,
+ .input = { {
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_COMPOSITE1,
+ } },
+ },
+ [CX23885_BOARD_MPX885] = {
+ .name = "MPX-885",
+ .porta = CX23885_ANALOG_VIDEO,
+ .input = {{
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_COMPOSITE1,
+ .amux = CX25840_AUDIO6,
+ .gpio0 = 0,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE2,
+ .vmux = CX25840_COMPOSITE2,
+ .amux = CX25840_AUDIO6,
+ .gpio0 = 0,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE3,
+ .vmux = CX25840_COMPOSITE3,
+ .amux = CX25840_AUDIO7,
+ .gpio0 = 0,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE4,
+ .vmux = CX25840_COMPOSITE4,
+ .amux = CX25840_AUDIO7,
+ .gpio0 = 0,
+ } },
+ },
+ [CX23885_BOARD_MYGICA_X8507] = {
+ .name = "Mygica X8507",
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0x61,
+ .tuner_bus = 1,
+ .porta = CX23885_ANALOG_VIDEO,
+ .input = {
+ {
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_COMPOSITE2,
+ .amux = CX25840_AUDIO8,
+ },
+ {
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_COMPOSITE8,
+ },
+ {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_SVIDEO_LUMA3 |
+ CX25840_SVIDEO_CHROMA4,
+ },
+ {
+ .type = CX23885_VMUX_COMPONENT,
+ .vmux = CX25840_COMPONENT_ON |
+ CX25840_VIN1_CH1 |
+ CX25840_VIN6_CH2 |
+ CX25840_VIN7_CH3,
+ },
+ },
+ },
+ [CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL] = {
+ .name = "TerraTec Cinergy T PCIe Dual",
+ .portb = CX23885_MPEG_DVB,
+ .portc = CX23885_MPEG_DVB,
+ }
+};
+const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
+
+/* ------------------------------------------------------------------ */
+/* PCI subsystem IDs */
+
+struct cx23885_subid cx23885_subids[] = {
+ {
+ .subvendor = 0x0070,
+ .subdevice = 0x3400,
+ .card = CX23885_BOARD_UNKNOWN,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x7600,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1800lp,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x7800,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1800,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x7801,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1800,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x7809,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1800,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x7911,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1250,
+ }, {
+ .subvendor = 0x18ac,
+ .subdevice = 0xd500,
+ .card = CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x7790,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1500Q,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x7797,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1500Q,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x7710,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1500,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x7717,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1500,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x71d1,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1200,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x71d3,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1200,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x8101,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1700,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x8010,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1400,
+ }, {
+ .subvendor = 0x18ac,
+ .subdevice = 0xd618,
+ .card = CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP,
+ }, {
+ .subvendor = 0x18ac,
+ .subdevice = 0xdb78,
+ .card = CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP,
+ }, {
+ .subvendor = 0x107d,
+ .subdevice = 0x6681,
+ .card = CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H,
+ }, {
+ .subvendor = 0x107d,
+ .subdevice = 0x6f39,
+ .card = CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000,
+ }, {
+ .subvendor = 0x185b,
+ .subdevice = 0xe800,
+ .card = CX23885_BOARD_COMPRO_VIDEOMATE_E650F,
+ }, {
+ .subvendor = 0x6920,
+ .subdevice = 0x8888,
+ .card = CX23885_BOARD_TBS_6920,
+ }, {
+ .subvendor = 0xd470,
+ .subdevice = 0x9022,
+ .card = CX23885_BOARD_TEVII_S470,
+ }, {
+ .subvendor = 0x0001,
+ .subdevice = 0x2005,
+ .card = CX23885_BOARD_DVBWORLD_2005,
+ }, {
+ .subvendor = 0x1b55,
+ .subdevice = 0x2a2c,
+ .card = CX23885_BOARD_NETUP_DUAL_DVBS2_CI,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x2211,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1270,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x2215,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1275,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x221d,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1275,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x2251,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1255,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x2259,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1255,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x2291,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1210,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x2295,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1210,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x2299,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1210,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x229d,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1210, /* HVR1215 */
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x22f0,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1210,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x22f1,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1255,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x22f2,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1275,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x22f3,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1210, /* HVR1215 */
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x22f4,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1210,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x22f5,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1210, /* HVR1215 */
+ }, {
+ .subvendor = 0x14f1,
+ .subdevice = 0x8651,
+ .card = CX23885_BOARD_MYGICA_X8506,
+ }, {
+ .subvendor = 0x14f1,
+ .subdevice = 0x8657,
+ .card = CX23885_BOARD_MAGICPRO_PROHDTVE2,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x8541,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1850,
+ }, {
+ .subvendor = 0x1858,
+ .subdevice = 0xe800,
+ .card = CX23885_BOARD_COMPRO_VIDEOMATE_E800,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x8551,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1290,
+ }, {
+ .subvendor = 0x14f1,
+ .subdevice = 0x8578,
+ .card = CX23885_BOARD_MYGICA_X8558PRO,
+ }, {
+ .subvendor = 0x107d,
+ .subdevice = 0x6f22,
+ .card = CX23885_BOARD_LEADTEK_WINFAST_PXTV1200,
+ }, {
+ .subvendor = 0x5654,
+ .subdevice = 0x2390,
+ .card = CX23885_BOARD_GOTVIEW_X5_3D_HYBRID,
+ }, {
+ .subvendor = 0x1b55,
+ .subdevice = 0xe2e4,
+ .card = CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF,
+ }, {
+ .subvendor = 0x14f1,
+ .subdevice = 0x8502,
+ .card = CX23885_BOARD_MYGICA_X8507,
+ }, {
+ .subvendor = 0x153b,
+ .subdevice = 0x117e,
+ .card = CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL,
+ },
+};
+const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
+
+void cx23885_card_list(struct cx23885_dev *dev)
+{
+ int i;
+
+ if (0 == dev->pci->subsystem_vendor &&
+ 0 == dev->pci->subsystem_device) {
+ printk(KERN_INFO
+ "%s: Board has no valid PCIe Subsystem ID and can't\n"
+ "%s: be autodetected. Pass card=<n> insmod option\n"
+ "%s: to workaround that. Redirect complaints to the\n"
+ "%s: vendor of the TV card. Best regards,\n"
+ "%s: -- tux\n",
+ dev->name, dev->name, dev->name, dev->name, dev->name);
+ } else {
+ printk(KERN_INFO
+ "%s: Your board isn't known (yet) to the driver.\n"
+ "%s: Try to pick one of the existing card configs via\n"
+ "%s: card=<n> insmod option. Updating to the latest\n"
+ "%s: version might help as well.\n",
+ dev->name, dev->name, dev->name, dev->name);
+ }
+ printk(KERN_INFO "%s: Here is a list of valid choices for the card=<n> insmod option:\n",
+ dev->name);
+ for (i = 0; i < cx23885_bcount; i++)
+ printk(KERN_INFO "%s: card=%d -> %s\n",
+ dev->name, i, cx23885_boards[i].name);
+}
+
+static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data)
+{
+ struct tveeprom tv;
+
+ tveeprom_hauppauge_analog(&dev->i2c_bus[0].i2c_client, &tv,
+ eeprom_data);
+
+ /* Make sure we support the board model */
+ switch (tv.model) {
+ case 22001:
+ /* WinTV-HVR1270 (PCIe, Retail, half height)
+ * ATSC/QAM and basic analog, IR Blast */
+ case 22009:
+ /* WinTV-HVR1210 (PCIe, Retail, half height)
+ * DVB-T and basic analog, IR Blast */
+ case 22011:
+ /* WinTV-HVR1270 (PCIe, Retail, half height)
+ * ATSC/QAM and basic analog, IR Recv */
+ case 22019:
+ /* WinTV-HVR1210 (PCIe, Retail, half height)
+ * DVB-T and basic analog, IR Recv */
+ case 22021:
+ /* WinTV-HVR1275 (PCIe, Retail, half height)
+ * ATSC/QAM and basic analog, IR Recv */
+ case 22029:
+ /* WinTV-HVR1210 (PCIe, Retail, half height)
+ * DVB-T and basic analog, IR Recv */
+ case 22101:
+ /* WinTV-HVR1270 (PCIe, Retail, full height)
+ * ATSC/QAM and basic analog, IR Blast */
+ case 22109:
+ /* WinTV-HVR1210 (PCIe, Retail, full height)
+ * DVB-T and basic analog, IR Blast */
+ case 22111:
+ /* WinTV-HVR1270 (PCIe, Retail, full height)
+ * ATSC/QAM and basic analog, IR Recv */
+ case 22119:
+ /* WinTV-HVR1210 (PCIe, Retail, full height)
+ * DVB-T and basic analog, IR Recv */
+ case 22121:
+ /* WinTV-HVR1275 (PCIe, Retail, full height)
+ * ATSC/QAM and basic analog, IR Recv */
+ case 22129:
+ /* WinTV-HVR1210 (PCIe, Retail, full height)
+ * DVB-T and basic analog, IR Recv */
+ case 71009:
+ /* WinTV-HVR1200 (PCIe, Retail, full height)
+ * DVB-T and basic analog */
+ case 71359:
+ /* WinTV-HVR1200 (PCIe, OEM, half height)
+ * DVB-T and basic analog */
+ case 71439:
+ /* WinTV-HVR1200 (PCIe, OEM, half height)
+ * DVB-T and basic analog */
+ case 71449:
+ /* WinTV-HVR1200 (PCIe, OEM, full height)
+ * DVB-T and basic analog */
+ case 71939:
+ /* WinTV-HVR1200 (PCIe, OEM, half height)
+ * DVB-T and basic analog */
+ case 71949:
+ /* WinTV-HVR1200 (PCIe, OEM, full height)
+ * DVB-T and basic analog */
+ case 71959:
+ /* WinTV-HVR1200 (PCIe, OEM, full height)
+ * DVB-T and basic analog */
+ case 71979:
+ /* WinTV-HVR1200 (PCIe, OEM, half height)
+ * DVB-T and basic analog */
+ case 71999:
+ /* WinTV-HVR1200 (PCIe, OEM, full height)
+ * DVB-T and basic analog */
+ case 76601:
+ /* WinTV-HVR1800lp (PCIe, Retail, No IR, Dual
+ channel ATSC and MPEG2 HW Encoder */
+ case 77001:
+ /* WinTV-HVR1500 (Express Card, OEM, No IR, ATSC
+ and Basic analog */
+ case 77011:
+ /* WinTV-HVR1500 (Express Card, Retail, No IR, ATSC
+ and Basic analog */
+ case 77041:
+ /* WinTV-HVR1500Q (Express Card, OEM, No IR, ATSC/QAM
+ and Basic analog */
+ case 77051:
+ /* WinTV-HVR1500Q (Express Card, Retail, No IR, ATSC/QAM
+ and Basic analog */
+ case 78011:
+ /* WinTV-HVR1800 (PCIe, Retail, 3.5mm in, IR, No FM,
+ Dual channel ATSC and MPEG2 HW Encoder */
+ case 78501:
+ /* WinTV-HVR1800 (PCIe, OEM, RCA in, No IR, FM,
+ Dual channel ATSC and MPEG2 HW Encoder */
+ case 78521:
+ /* WinTV-HVR1800 (PCIe, OEM, RCA in, No IR, FM,
+ Dual channel ATSC and MPEG2 HW Encoder */
+ case 78531:
+ /* WinTV-HVR1800 (PCIe, OEM, RCA in, No IR, No FM,
+ Dual channel ATSC and MPEG2 HW Encoder */
+ case 78631:
+ /* WinTV-HVR1800 (PCIe, OEM, No IR, No FM,
+ Dual channel ATSC and MPEG2 HW Encoder */
+ case 79001:
+ /* WinTV-HVR1250 (PCIe, Retail, IR, full height,
+ ATSC and Basic analog */
+ case 79101:
+ /* WinTV-HVR1250 (PCIe, Retail, IR, half height,
+ ATSC and Basic analog */
+ case 79501:
+ /* WinTV-HVR1250 (PCIe, No IR, half height,
+ ATSC [at least] and Basic analog) */
+ case 79561:
+ /* WinTV-HVR1250 (PCIe, OEM, No IR, half height,
+ ATSC and Basic analog */
+ case 79571:
+ /* WinTV-HVR1250 (PCIe, OEM, No IR, full height,
+ ATSC and Basic analog */
+ case 79671:
+ /* WinTV-HVR1250 (PCIe, OEM, No IR, half height,
+ ATSC and Basic analog */
+ case 80019:
+ /* WinTV-HVR1400 (Express Card, Retail, IR,
+ * DVB-T and Basic analog */
+ case 81509:
+ /* WinTV-HVR1700 (PCIe, OEM, No IR, half height)
+ * DVB-T and MPEG2 HW Encoder */
+ case 81519:
+ /* WinTV-HVR1700 (PCIe, OEM, No IR, full height)
+ * DVB-T and MPEG2 HW Encoder */
+ break;
+ case 85021:
+ /* WinTV-HVR1850 (PCIe, Retail, 3.5mm in, IR, FM,
+ Dual channel ATSC and MPEG2 HW Encoder */
+ break;
+ case 85721:
+ /* WinTV-HVR1290 (PCIe, OEM, RCA in, IR,
+ Dual channel ATSC and Basic analog */
+ break;
+ default:
+ printk(KERN_WARNING "%s: warning: "
+ "unknown hauppauge model #%d\n",
+ dev->name, tv.model);
+ break;
+ }
+
+ printk(KERN_INFO "%s: hauppauge eeprom: model=%d\n",
+ dev->name, tv.model);
+}
+
+int cx23885_tuner_callback(void *priv, int component, int command, int arg)
+{
+ struct cx23885_tsport *port = priv;
+ struct cx23885_dev *dev = port->dev;
+ u32 bitmask = 0;
+
+ if (command == XC2028_RESET_CLK)
+ return 0;
+
+ if (command != 0) {
+ printk(KERN_ERR "%s(): Unknown command 0x%x.\n",
+ __func__, command);
+ return -EINVAL;
+ }
+
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1400:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200:
+ /* Tuner Reset Command */
+ bitmask = 0x04;
+ break;
+ case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP:
+ case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
+ /* Two identical tuners on two different i2c buses,
+ * we need to reset the correct gpio. */
+ if (port->nr == 1)
+ bitmask = 0x01;
+ else if (port->nr == 2)
+ bitmask = 0x04;
+ break;
+ case CX23885_BOARD_GOTVIEW_X5_3D_HYBRID:
+ /* Tuner Reset Command */
+ bitmask = 0x02;
+ break;
+ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
+ altera_ci_tuner_reset(dev, port->nr);
+ break;
+ }
+
+ if (bitmask) {
+ /* Drive the tuner into reset and back out */
+ cx_clear(GP0_IO, bitmask);
+ mdelay(200);
+ cx_set(GP0_IO, bitmask);
+ }
+
+ return 0;
+}
+
+void cx23885_gpio_setup(struct cx23885_dev *dev)
+{
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ /* GPIO-0 cx24227 demodulator reset */
+ cx_set(GP0_IO, 0x00010001); /* Bring the part out of reset */
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1500:
+ /* GPIO-0 cx24227 demodulator */
+ /* GPIO-2 xc3028 tuner */
+
+ /* Put the parts into reset */
+ cx_set(GP0_IO, 0x00050000);
+ cx_clear(GP0_IO, 0x00000005);
+ msleep(5);
+
+ /* Bring the parts out of reset */
+ cx_set(GP0_IO, 0x00050005);
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
+ /* GPIO-0 cx24227 demodulator reset */
+ /* GPIO-2 xc5000 tuner reset */
+ cx_set(GP0_IO, 0x00050005); /* Bring the part out of reset */
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1800:
+ /* GPIO-0 656_CLK */
+ /* GPIO-1 656_D0 */
+ /* GPIO-2 8295A Reset */
+ /* GPIO-3-10 cx23417 data0-7 */
+ /* GPIO-11-14 cx23417 addr0-3 */
+ /* GPIO-15-18 cx23417 READY, CS, RD, WR */
+ /* GPIO-19 IR_RX */
+
+ /* CX23417 GPIO's */
+ /* EIO15 Zilog Reset */
+ /* EIO14 S5H1409/CX24227 Reset */
+ mc417_gpio_enable(dev, GPIO_15 | GPIO_14, 1);
+
+ /* Put the demod into reset and protect the eeprom */
+ mc417_gpio_clear(dev, GPIO_15 | GPIO_14);
+ mdelay(100);
+
+ /* Bring the demod and blaster out of reset */
+ mc417_gpio_set(dev, GPIO_15 | GPIO_14);
+ mdelay(100);
+
+ /* Force the TDA8295A into reset and back */
+ cx23885_gpio_enable(dev, GPIO_2, 1);
+ cx23885_gpio_set(dev, GPIO_2);
+ mdelay(20);
+ cx23885_gpio_clear(dev, GPIO_2);
+ mdelay(20);
+ cx23885_gpio_set(dev, GPIO_2);
+ mdelay(20);
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1200:
+ /* GPIO-0 tda10048 demodulator reset */
+ /* GPIO-2 tda18271 tuner reset */
+
+ /* Put the parts into reset and back */
+ cx_set(GP0_IO, 0x00050000);
+ mdelay(20);
+ cx_clear(GP0_IO, 0x00000005);
+ mdelay(20);
+ cx_set(GP0_IO, 0x00050005);
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1700:
+ /* GPIO-0 TDA10048 demodulator reset */
+ /* GPIO-2 TDA8295A Reset */
+ /* GPIO-3-10 cx23417 data0-7 */
+ /* GPIO-11-14 cx23417 addr0-3 */
+ /* GPIO-15-18 cx23417 READY, CS, RD, WR */
+
+ /* The following GPIO's are on the interna AVCore (cx25840) */
+ /* GPIO-19 IR_RX */
+ /* GPIO-20 IR_TX 416/DVBT Select */
+ /* GPIO-21 IIS DAT */
+ /* GPIO-22 IIS WCLK */
+ /* GPIO-23 IIS BCLK */
+
+ /* Put the parts into reset and back */
+ cx_set(GP0_IO, 0x00050000);
+ mdelay(20);
+ cx_clear(GP0_IO, 0x00000005);
+ mdelay(20);
+ cx_set(GP0_IO, 0x00050005);
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1400:
+ /* GPIO-0 Dibcom7000p demodulator reset */
+ /* GPIO-2 xc3028L tuner reset */
+ /* GPIO-13 LED */
+
+ /* Put the parts into reset and back */
+ cx_set(GP0_IO, 0x00050000);
+ mdelay(20);
+ cx_clear(GP0_IO, 0x00000005);
+ mdelay(20);
+ cx_set(GP0_IO, 0x00050005);
+ break;
+ case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP:
+ /* GPIO-0 xc5000 tuner reset i2c bus 0 */
+ /* GPIO-1 s5h1409 demod reset i2c bus 0 */
+ /* GPIO-2 xc5000 tuner reset i2c bus 1 */
+ /* GPIO-3 s5h1409 demod reset i2c bus 0 */
+
+ /* Put the parts into reset and back */
+ cx_set(GP0_IO, 0x000f0000);
+ mdelay(20);
+ cx_clear(GP0_IO, 0x0000000f);
+ mdelay(20);
+ cx_set(GP0_IO, 0x000f000f);
+ break;
+ case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
+ /* GPIO-0 portb xc3028 reset */
+ /* GPIO-1 portb zl10353 reset */
+ /* GPIO-2 portc xc3028 reset */
+ /* GPIO-3 portc zl10353 reset */
+
+ /* Put the parts into reset and back */
+ cx_set(GP0_IO, 0x000f0000);
+ mdelay(20);
+ cx_clear(GP0_IO, 0x0000000f);
+ mdelay(20);
+ cx_set(GP0_IO, 0x000f000f);
+ break;
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200:
+ /* GPIO-2 xc3028 tuner reset */
+
+ /* The following GPIO's are on the internal AVCore (cx25840) */
+ /* GPIO-? zl10353 demod reset */
+
+ /* Put the parts into reset and back */
+ cx_set(GP0_IO, 0x00040000);
+ mdelay(20);
+ cx_clear(GP0_IO, 0x00000004);
+ mdelay(20);
+ cx_set(GP0_IO, 0x00040004);
+ break;
+ case CX23885_BOARD_TBS_6920:
+ cx_write(MC417_CTL, 0x00000036);
+ cx_write(MC417_OEN, 0x00001000);
+ cx_set(MC417_RWD, 0x00000002);
+ mdelay(200);
+ cx_clear(MC417_RWD, 0x00000800);
+ mdelay(200);
+ cx_set(MC417_RWD, 0x00000800);
+ mdelay(200);
+ break;
+ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
+ /* GPIO-0 INTA from CiMax1
+ GPIO-1 INTB from CiMax2
+ GPIO-2 reset chips
+ GPIO-3 to GPIO-10 data/addr for CA
+ GPIO-11 ~CS0 to CiMax1
+ GPIO-12 ~CS1 to CiMax2
+ GPIO-13 ADL0 load LSB addr
+ GPIO-14 ADL1 load MSB addr
+ GPIO-15 ~RDY from CiMax
+ GPIO-17 ~RD to CiMax
+ GPIO-18 ~WR to CiMax
+ */
+ cx_set(GP0_IO, 0x00040000); /* GPIO as out */
+ /* GPIO1 and GPIO2 as INTA and INTB from CiMaxes, reset low */
+ cx_clear(GP0_IO, 0x00030004);
+ mdelay(100);/* reset delay */
+ cx_set(GP0_IO, 0x00040004); /* GPIO as out, reset high */
+ cx_write(MC417_CTL, 0x00000037);/* enable GPIO3-18 pins */
+ /* GPIO-15 IN as ~ACK, rest as OUT */
+ cx_write(MC417_OEN, 0x00001000);
+ /* ~RD, ~WR high; ADL0, ADL1 low; ~CS0, ~CS1 high */
+ cx_write(MC417_RWD, 0x0000c300);
+ /* enable irq */
+ cx_write(GPIO_ISM, 0x00000000);/* INTERRUPTS active low*/
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
+ case CX23885_BOARD_HAUPPAUGE_HVR1275:
+ case CX23885_BOARD_HAUPPAUGE_HVR1255:
+ case CX23885_BOARD_HAUPPAUGE_HVR1210:
+ /* GPIO-5 RF Control: 0 = RF1 Terrestrial, 1 = RF2 Cable */
+ /* GPIO-6 I2C Gate which can isolate the demod from the bus */
+ /* GPIO-9 Demod reset */
+
+ /* Put the parts into reset and back */
+ cx23885_gpio_enable(dev, GPIO_9 | GPIO_6 | GPIO_5, 1);
+ cx23885_gpio_set(dev, GPIO_9 | GPIO_6 | GPIO_5);
+ cx23885_gpio_clear(dev, GPIO_9);
+ mdelay(20);
+ cx23885_gpio_set(dev, GPIO_9);
+ break;
+ case CX23885_BOARD_MYGICA_X8506:
+ case CX23885_BOARD_MAGICPRO_PROHDTVE2:
+ case CX23885_BOARD_MYGICA_X8507:
+ /* GPIO-0 (0)Analog / (1)Digital TV */
+ /* GPIO-1 reset XC5000 */
+ /* GPIO-2 reset LGS8GL5 / LGS8G75 */
+ cx23885_gpio_enable(dev, GPIO_0 | GPIO_1 | GPIO_2, 1);
+ cx23885_gpio_clear(dev, GPIO_1 | GPIO_2);
+ mdelay(100);
+ cx23885_gpio_set(dev, GPIO_0 | GPIO_1 | GPIO_2);
+ mdelay(100);
+ break;
+ case CX23885_BOARD_MYGICA_X8558PRO:
+ /* GPIO-0 reset first ATBM8830 */
+ /* GPIO-1 reset second ATBM8830 */
+ cx23885_gpio_enable(dev, GPIO_0 | GPIO_1, 1);
+ cx23885_gpio_clear(dev, GPIO_0 | GPIO_1);
+ mdelay(100);
+ cx23885_gpio_set(dev, GPIO_0 | GPIO_1);
+ mdelay(100);
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ /* GPIO-0 656_CLK */
+ /* GPIO-1 656_D0 */
+ /* GPIO-2 Wake# */
+ /* GPIO-3-10 cx23417 data0-7 */
+ /* GPIO-11-14 cx23417 addr0-3 */
+ /* GPIO-15-18 cx23417 READY, CS, RD, WR */
+ /* GPIO-19 IR_RX */
+ /* GPIO-20 C_IR_TX */
+ /* GPIO-21 I2S DAT */
+ /* GPIO-22 I2S WCLK */
+ /* GPIO-23 I2S BCLK */
+ /* ALT GPIO: EXP GPIO LATCH */
+
+ /* CX23417 GPIO's */
+ /* GPIO-14 S5H1411/CX24228 Reset */
+ /* GPIO-13 EEPROM write protect */
+ mc417_gpio_enable(dev, GPIO_14 | GPIO_13, 1);
+
+ /* Put the demod into reset and protect the eeprom */
+ mc417_gpio_clear(dev, GPIO_14 | GPIO_13);
+ mdelay(100);
+
+ /* Bring the demod out of reset */
+ mc417_gpio_set(dev, GPIO_14);
+ mdelay(100);
+
+ /* CX24228 GPIO */
+ /* Connected to IF / Mux */
+ break;
+ case CX23885_BOARD_GOTVIEW_X5_3D_HYBRID:
+ cx_set(GP0_IO, 0x00010001); /* Bring the part out of reset */
+ break;
+ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
+ /* GPIO-0 ~INT in
+ GPIO-1 TMS out
+ GPIO-2 ~reset chips out
+ GPIO-3 to GPIO-10 data/addr for CA in/out
+ GPIO-11 ~CS out
+ GPIO-12 ADDR out
+ GPIO-13 ~WR out
+ GPIO-14 ~RD out
+ GPIO-15 ~RDY in
+ GPIO-16 TCK out
+ GPIO-17 TDO in
+ GPIO-18 TDI out
+ */
+ cx_set(GP0_IO, 0x00060000); /* GPIO-1,2 as out */
+ /* GPIO-0 as INT, reset & TMS low */
+ cx_clear(GP0_IO, 0x00010006);
+ mdelay(100);/* reset delay */
+ cx_set(GP0_IO, 0x00000004); /* reset high */
+ cx_write(MC417_CTL, 0x00000037);/* enable GPIO-3..18 pins */
+ /* GPIO-17 is TDO in, GPIO-15 is ~RDY in, rest is out */
+ cx_write(MC417_OEN, 0x00005000);
+ /* ~RD, ~WR high; ADDR low; ~CS high */
+ cx_write(MC417_RWD, 0x00000d00);
+ /* enable irq */
+ cx_write(GPIO_ISM, 0x00000000);/* INTERRUPTS active low*/
+ break;
+ }
+}
+
+int cx23885_ir_init(struct cx23885_dev *dev)
+{
+ static struct v4l2_subdev_io_pin_config ir_rxtx_pin_cfg[] = {
+ {
+ .flags = V4L2_SUBDEV_IO_PIN_INPUT,
+ .pin = CX23885_PIN_IR_RX_GPIO19,
+ .function = CX23885_PAD_IR_RX,
+ .value = 0,
+ .strength = CX25840_PIN_DRIVE_MEDIUM,
+ }, {
+ .flags = V4L2_SUBDEV_IO_PIN_OUTPUT,
+ .pin = CX23885_PIN_IR_TX_GPIO20,
+ .function = CX23885_PAD_IR_TX,
+ .value = 0,
+ .strength = CX25840_PIN_DRIVE_MEDIUM,
+ }
+ };
+ const size_t ir_rxtx_pin_cfg_count = ARRAY_SIZE(ir_rxtx_pin_cfg);
+
+ static struct v4l2_subdev_io_pin_config ir_rx_pin_cfg[] = {
+ {
+ .flags = V4L2_SUBDEV_IO_PIN_INPUT,
+ .pin = CX23885_PIN_IR_RX_GPIO19,
+ .function = CX23885_PAD_IR_RX,
+ .value = 0,
+ .strength = CX25840_PIN_DRIVE_MEDIUM,
+ }
+ };
+ const size_t ir_rx_pin_cfg_count = ARRAY_SIZE(ir_rx_pin_cfg);
+
+ struct v4l2_subdev_ir_parameters params;
+ int ret = 0;
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1500:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
+ case CX23885_BOARD_HAUPPAUGE_HVR1800:
+ case CX23885_BOARD_HAUPPAUGE_HVR1200:
+ case CX23885_BOARD_HAUPPAUGE_HVR1400:
+ case CX23885_BOARD_HAUPPAUGE_HVR1275:
+ case CX23885_BOARD_HAUPPAUGE_HVR1255:
+ case CX23885_BOARD_HAUPPAUGE_HVR1210:
+ /* FIXME: Implement me */
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
+ ret = cx23888_ir_probe(dev);
+ if (ret)
+ break;
+ dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_888_IR);
+ v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
+ ir_rx_pin_cfg_count, ir_rx_pin_cfg);
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ ret = cx23888_ir_probe(dev);
+ if (ret)
+ break;
+ dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_888_IR);
+ v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
+ ir_rxtx_pin_cfg_count, ir_rxtx_pin_cfg);
+ /*
+ * For these boards we need to invert the Tx output via the
+ * IR controller to have the LED off while idle
+ */
+ v4l2_subdev_call(dev->sd_ir, ir, tx_g_parameters, &params);
+ params.enable = false;
+ params.shutdown = false;
+ params.invert_level = true;
+ v4l2_subdev_call(dev->sd_ir, ir, tx_s_parameters, &params);
+ params.shutdown = true;
+ v4l2_subdev_call(dev->sd_ir, ir, tx_s_parameters, &params);
+ break;
+ case CX23885_BOARD_TEVII_S470:
+ if (!enable_885_ir)
+ break;
+ dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE);
+ if (dev->sd_ir == NULL) {
+ ret = -ENODEV;
+ break;
+ }
+ v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
+ ir_rx_pin_cfg_count, ir_rx_pin_cfg);
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ if (!enable_885_ir)
+ break;
+ dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE);
+ if (dev->sd_ir == NULL) {
+ ret = -ENODEV;
+ break;
+ }
+ v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
+ ir_rxtx_pin_cfg_count, ir_rxtx_pin_cfg);
+ break;
+ case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
+ request_module("ir-kbd-i2c");
+ break;
+ }
+
+ return ret;
+}
+
+void cx23885_ir_fini(struct cx23885_dev *dev)
+{
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ cx23885_irq_remove(dev, PCI_MSK_IR);
+ cx23888_ir_remove(dev);
+ dev->sd_ir = NULL;
+ break;
+ case CX23885_BOARD_TEVII_S470:
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ cx23885_irq_remove(dev, PCI_MSK_AV_CORE);
+ /* sd_ir is a duplicate pointer to the AV Core, just clear it */
+ dev->sd_ir = NULL;
+ break;
+ }
+}
+
+int netup_jtag_io(void *device, int tms, int tdi, int read_tdo)
+{
+ int data;
+ int tdo = 0;
+ struct cx23885_dev *dev = (struct cx23885_dev *)device;
+ /*TMS*/
+ data = ((cx_read(GP0_IO)) & (~0x00000002));
+ data |= (tms ? 0x00020002 : 0x00020000);
+ cx_write(GP0_IO, data);
+
+ /*TDI*/
+ data = ((cx_read(MC417_RWD)) & (~0x0000a000));
+ data |= (tdi ? 0x00008000 : 0);
+ cx_write(MC417_RWD, data);
+ if (read_tdo)
+ tdo = (data & 0x00004000) ? 1 : 0; /*TDO*/
+
+ cx_write(MC417_RWD, data | 0x00002000);
+ udelay(1);
+ /*TCK*/
+ cx_write(MC417_RWD, data);
+
+ return tdo;
+}
+
+void cx23885_ir_pci_int_enable(struct cx23885_dev *dev)
+{
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ if (dev->sd_ir)
+ cx23885_irq_add_enable(dev, PCI_MSK_IR);
+ break;
+ case CX23885_BOARD_TEVII_S470:
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ if (dev->sd_ir)
+ cx23885_irq_add_enable(dev, PCI_MSK_AV_CORE);
+ break;
+ }
+}
+
+void cx23885_card_setup(struct cx23885_dev *dev)
+{
+ struct cx23885_tsport *ts1 = &dev->ts1;
+ struct cx23885_tsport *ts2 = &dev->ts2;
+
+ static u8 eeprom[256];
+
+ if (dev->i2c_bus[0].i2c_rc == 0) {
+ dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1;
+ tveeprom_read(&dev->i2c_bus[0].i2c_client,
+ eeprom, sizeof(eeprom));
+ }
+
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ if (dev->i2c_bus[0].i2c_rc == 0) {
+ if (eeprom[0x80] != 0x84)
+ hauppauge_eeprom(dev, eeprom+0xc0);
+ else
+ hauppauge_eeprom(dev, eeprom+0x80);
+ }
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1500:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
+ case CX23885_BOARD_HAUPPAUGE_HVR1400:
+ if (dev->i2c_bus[0].i2c_rc == 0)
+ hauppauge_eeprom(dev, eeprom+0x80);
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1800:
+ case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
+ case CX23885_BOARD_HAUPPAUGE_HVR1200:
+ case CX23885_BOARD_HAUPPAUGE_HVR1700:
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
+ case CX23885_BOARD_HAUPPAUGE_HVR1275:
+ case CX23885_BOARD_HAUPPAUGE_HVR1255:
+ case CX23885_BOARD_HAUPPAUGE_HVR1210:
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ if (dev->i2c_bus[0].i2c_rc == 0)
+ hauppauge_eeprom(dev, eeprom+0xc0);
+ break;
+ }
+
+ switch (dev->board) {
+ case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP:
+ case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
+ ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
+ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ /* break omitted intentionally */
+ case CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP:
+ ts1->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
+ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1800:
+ /* Defaults for VID B - Analog encoder */
+ /* DREQ_POL, SMODE, PUNC_CLK, MCLK_POL Serial bus + punc clk */
+ ts1->gen_ctrl_val = 0x10e;
+ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+
+ /* APB_TSVALERR_POL (active low)*/
+ ts1->vld_misc_val = 0x2000;
+ ts1->hw_sop_ctrl_val = (0x47 << 16 | 188 << 4 | 0xc);
+ cx_write(0x130184, 0xc);
+
+ /* Defaults for VID C */
+ ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
+ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ break;
+ case CX23885_BOARD_TBS_6920:
+ ts1->gen_ctrl_val = 0x4; /* Parallel */
+ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ break;
+ case CX23885_BOARD_TEVII_S470:
+ case CX23885_BOARD_DVBWORLD_2005:
+ ts1->gen_ctrl_val = 0x5; /* Parallel */
+ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ break;
+ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
+ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
+ case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+ ts1->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
+ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
+ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ break;
+ case CX23885_BOARD_MYGICA_X8506:
+ case CX23885_BOARD_MAGICPRO_PROHDTVE2:
+ ts1->gen_ctrl_val = 0x5; /* Parallel */
+ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ break;
+ case CX23885_BOARD_MYGICA_X8558PRO:
+ ts1->gen_ctrl_val = 0x5; /* Parallel */
+ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
+ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
+ case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
+ case CX23885_BOARD_HAUPPAUGE_HVR1200:
+ case CX23885_BOARD_HAUPPAUGE_HVR1700:
+ case CX23885_BOARD_HAUPPAUGE_HVR1400:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
+ case CX23885_BOARD_HAUPPAUGE_HVR1275:
+ case CX23885_BOARD_HAUPPAUGE_HVR1255:
+ case CX23885_BOARD_HAUPPAUGE_HVR1210:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ case CX23885_BOARD_GOTVIEW_X5_3D_HYBRID:
+ default:
+ ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
+ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ }
+
+ /* Certain boards support analog, or require the avcore to be
+ * loaded, ensure this happens.
+ */
+ switch (dev->board) {
+ case CX23885_BOARD_TEVII_S470:
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ /* Currently only enabled for the integrated IR controller */
+ if (!enable_885_ir)
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1800:
+ case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
+ case CX23885_BOARD_HAUPPAUGE_HVR1700:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
+ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
+ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_MYGICA_X8506:
+ case CX23885_BOARD_MAGICPRO_PROHDTVE2:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200:
+ case CX23885_BOARD_GOTVIEW_X5_3D_HYBRID:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500:
+ case CX23885_BOARD_MPX885:
+ case CX23885_BOARD_MYGICA_X8507:
+ case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+ dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
+ &dev->i2c_bus[2].i2c_adap,
+ "cx25840", 0x88 >> 1, NULL);
+ if (dev->sd_cx25840) {
+ dev->sd_cx25840->grp_id = CX23885_HW_AV_CORE;
+ v4l2_subdev_call(dev->sd_cx25840, core, load_fw);
+ }
+ break;
+ }
+
+ /* AUX-PLL 27MHz CLK */
+ switch (dev->board) {
+ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
+ netup_initialize(dev);
+ break;
+ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: {
+ int ret;
+ const struct firmware *fw;
+ const char *filename = "dvb-netup-altera-01.fw";
+ char *action = "configure";
+ static struct netup_card_info cinfo;
+ struct altera_config netup_config = {
+ .dev = dev,
+ .action = action,
+ .jtag_io = netup_jtag_io,
+ };
+
+ netup_initialize(dev);
+
+ netup_get_card_info(&dev->i2c_bus[0].i2c_adap, &cinfo);
+ if (netup_card_rev)
+ cinfo.rev = netup_card_rev;
+
+ switch (cinfo.rev) {
+ case 0x4:
+ filename = "dvb-netup-altera-04.fw";
+ break;
+ default:
+ filename = "dvb-netup-altera-01.fw";
+ break;
+ }
+ printk(KERN_INFO "NetUP card rev=0x%x fw_filename=%s\n",
+ cinfo.rev, filename);
+
+ ret = request_firmware(&fw, filename, &dev->pci->dev);
+ if (ret != 0)
+ printk(KERN_ERR "did not find the firmware file. (%s) "
+ "Please see linux/Documentation/dvb/ for more details "
+ "on firmware-problems.", filename);
+ else
+ altera_init(&netup_config, fw);
+
+ release_firmware(fw);
+ break;
+ }
+ }
+}
+
+/* ------------------------------------------------------------------ */
diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c
new file mode 100644
index 00000000..6ad22702
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-core.c
@@ -0,0 +1,2227 @@
+/*
+ * Driver for the Conexant CX23885 PCIe bridge
+ *
+ * Copyright (c) 2006 Steven Toth <stoth@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kmod.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <asm/div64.h>
+#include <linux/firmware.h>
+
+#include "cx23885.h"
+#include "cimax2.h"
+#include "altera-ci.h"
+#include "cx23888-ir.h"
+#include "cx23885-ir.h"
+#include "cx23885-av.h"
+#include "cx23885-input.h"
+
+MODULE_DESCRIPTION("Driver for cx23885 based TV cards");
+MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(CX23885_VERSION);
+
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable debug messages");
+
+static unsigned int card[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
+module_param_array(card, int, NULL, 0444);
+MODULE_PARM_DESC(card, "card type");
+
+#define dprintk(level, fmt, arg...)\
+ do { if (debug >= level)\
+ printk(KERN_DEBUG "%s: " fmt, dev->name, ## arg);\
+ } while (0)
+
+static unsigned int cx23885_devcount;
+
+#define NO_SYNC_LINE (-1U)
+
+/* FIXME, these allocations will change when
+ * analog arrives. The be reviewed.
+ * CX23887 Assumptions
+ * 1 line = 16 bytes of CDT
+ * cmds size = 80
+ * cdt size = 16 * linesize
+ * iqsize = 64
+ * maxlines = 6
+ *
+ * Address Space:
+ * 0x00000000 0x00008fff FIFO clusters
+ * 0x00010000 0x000104af Channel Management Data Structures
+ * 0x000104b0 0x000104ff Free
+ * 0x00010500 0x000108bf 15 channels * iqsize
+ * 0x000108c0 0x000108ff Free
+ * 0x00010900 0x00010e9f IQ's + Cluster Descriptor Tables
+ * 15 channels * (iqsize + (maxlines * linesize))
+ * 0x00010ea0 0x00010xxx Free
+ */
+
+static struct sram_channel cx23885_sram_channels[] = {
+ [SRAM_CH01] = {
+ .name = "VID A",
+ .cmds_start = 0x10000,
+ .ctrl_start = 0x10380,
+ .cdt = 0x104c0,
+ .fifo_start = 0x40,
+ .fifo_size = 0x2800,
+ .ptr1_reg = DMA1_PTR1,
+ .ptr2_reg = DMA1_PTR2,
+ .cnt1_reg = DMA1_CNT1,
+ .cnt2_reg = DMA1_CNT2,
+ },
+ [SRAM_CH02] = {
+ .name = "ch2",
+ .cmds_start = 0x0,
+ .ctrl_start = 0x0,
+ .cdt = 0x0,
+ .fifo_start = 0x0,
+ .fifo_size = 0x0,
+ .ptr1_reg = DMA2_PTR1,
+ .ptr2_reg = DMA2_PTR2,
+ .cnt1_reg = DMA2_CNT1,
+ .cnt2_reg = DMA2_CNT2,
+ },
+ [SRAM_CH03] = {
+ .name = "TS1 B",
+ .cmds_start = 0x100A0,
+ .ctrl_start = 0x10400,
+ .cdt = 0x10580,
+ .fifo_start = 0x5000,
+ .fifo_size = 0x1000,
+ .ptr1_reg = DMA3_PTR1,
+ .ptr2_reg = DMA3_PTR2,
+ .cnt1_reg = DMA3_CNT1,
+ .cnt2_reg = DMA3_CNT2,
+ },
+ [SRAM_CH04] = {
+ .name = "ch4",
+ .cmds_start = 0x0,
+ .ctrl_start = 0x0,
+ .cdt = 0x0,
+ .fifo_start = 0x0,
+ .fifo_size = 0x0,
+ .ptr1_reg = DMA4_PTR1,
+ .ptr2_reg = DMA4_PTR2,
+ .cnt1_reg = DMA4_CNT1,
+ .cnt2_reg = DMA4_CNT2,
+ },
+ [SRAM_CH05] = {
+ .name = "ch5",
+ .cmds_start = 0x0,
+ .ctrl_start = 0x0,
+ .cdt = 0x0,
+ .fifo_start = 0x0,
+ .fifo_size = 0x0,
+ .ptr1_reg = DMA5_PTR1,
+ .ptr2_reg = DMA5_PTR2,
+ .cnt1_reg = DMA5_CNT1,
+ .cnt2_reg = DMA5_CNT2,
+ },
+ [SRAM_CH06] = {
+ .name = "TS2 C",
+ .cmds_start = 0x10140,
+ .ctrl_start = 0x10440,
+ .cdt = 0x105e0,
+ .fifo_start = 0x6000,
+ .fifo_size = 0x1000,
+ .ptr1_reg = DMA5_PTR1,
+ .ptr2_reg = DMA5_PTR2,
+ .cnt1_reg = DMA5_CNT1,
+ .cnt2_reg = DMA5_CNT2,
+ },
+ [SRAM_CH07] = {
+ .name = "TV Audio",
+ .cmds_start = 0x10190,
+ .ctrl_start = 0x10480,
+ .cdt = 0x10a00,
+ .fifo_start = 0x7000,
+ .fifo_size = 0x1000,
+ .ptr1_reg = DMA6_PTR1,
+ .ptr2_reg = DMA6_PTR2,
+ .cnt1_reg = DMA6_CNT1,
+ .cnt2_reg = DMA6_CNT2,
+ },
+ [SRAM_CH08] = {
+ .name = "ch8",
+ .cmds_start = 0x0,
+ .ctrl_start = 0x0,
+ .cdt = 0x0,
+ .fifo_start = 0x0,
+ .fifo_size = 0x0,
+ .ptr1_reg = DMA7_PTR1,
+ .ptr2_reg = DMA7_PTR2,
+ .cnt1_reg = DMA7_CNT1,
+ .cnt2_reg = DMA7_CNT2,
+ },
+ [SRAM_CH09] = {
+ .name = "ch9",
+ .cmds_start = 0x0,
+ .ctrl_start = 0x0,
+ .cdt = 0x0,
+ .fifo_start = 0x0,
+ .fifo_size = 0x0,
+ .ptr1_reg = DMA8_PTR1,
+ .ptr2_reg = DMA8_PTR2,
+ .cnt1_reg = DMA8_CNT1,
+ .cnt2_reg = DMA8_CNT2,
+ },
+};
+
+static struct sram_channel cx23887_sram_channels[] = {
+ [SRAM_CH01] = {
+ .name = "VID A",
+ .cmds_start = 0x10000,
+ .ctrl_start = 0x105b0,
+ .cdt = 0x107b0,
+ .fifo_start = 0x40,
+ .fifo_size = 0x2800,
+ .ptr1_reg = DMA1_PTR1,
+ .ptr2_reg = DMA1_PTR2,
+ .cnt1_reg = DMA1_CNT1,
+ .cnt2_reg = DMA1_CNT2,
+ },
+ [SRAM_CH02] = {
+ .name = "VID A (VBI)",
+ .cmds_start = 0x10050,
+ .ctrl_start = 0x105F0,
+ .cdt = 0x10810,
+ .fifo_start = 0x3000,
+ .fifo_size = 0x1000,
+ .ptr1_reg = DMA2_PTR1,
+ .ptr2_reg = DMA2_PTR2,
+ .cnt1_reg = DMA2_CNT1,
+ .cnt2_reg = DMA2_CNT2,
+ },
+ [SRAM_CH03] = {
+ .name = "TS1 B",
+ .cmds_start = 0x100A0,
+ .ctrl_start = 0x10630,
+ .cdt = 0x10870,
+ .fifo_start = 0x5000,
+ .fifo_size = 0x1000,
+ .ptr1_reg = DMA3_PTR1,
+ .ptr2_reg = DMA3_PTR2,
+ .cnt1_reg = DMA3_CNT1,
+ .cnt2_reg = DMA3_CNT2,
+ },
+ [SRAM_CH04] = {
+ .name = "ch4",
+ .cmds_start = 0x0,
+ .ctrl_start = 0x0,
+ .cdt = 0x0,
+ .fifo_start = 0x0,
+ .fifo_size = 0x0,
+ .ptr1_reg = DMA4_PTR1,
+ .ptr2_reg = DMA4_PTR2,
+ .cnt1_reg = DMA4_CNT1,
+ .cnt2_reg = DMA4_CNT2,
+ },
+ [SRAM_CH05] = {
+ .name = "ch5",
+ .cmds_start = 0x0,
+ .ctrl_start = 0x0,
+ .cdt = 0x0,
+ .fifo_start = 0x0,
+ .fifo_size = 0x0,
+ .ptr1_reg = DMA5_PTR1,
+ .ptr2_reg = DMA5_PTR2,
+ .cnt1_reg = DMA5_CNT1,
+ .cnt2_reg = DMA5_CNT2,
+ },
+ [SRAM_CH06] = {
+ .name = "TS2 C",
+ .cmds_start = 0x10140,
+ .ctrl_start = 0x10670,
+ .cdt = 0x108d0,
+ .fifo_start = 0x6000,
+ .fifo_size = 0x1000,
+ .ptr1_reg = DMA5_PTR1,
+ .ptr2_reg = DMA5_PTR2,
+ .cnt1_reg = DMA5_CNT1,
+ .cnt2_reg = DMA5_CNT2,
+ },
+ [SRAM_CH07] = {
+ .name = "TV Audio",
+ .cmds_start = 0x10190,
+ .ctrl_start = 0x106B0,
+ .cdt = 0x10930,
+ .fifo_start = 0x7000,
+ .fifo_size = 0x1000,
+ .ptr1_reg = DMA6_PTR1,
+ .ptr2_reg = DMA6_PTR2,
+ .cnt1_reg = DMA6_CNT1,
+ .cnt2_reg = DMA6_CNT2,
+ },
+ [SRAM_CH08] = {
+ .name = "ch8",
+ .cmds_start = 0x0,
+ .ctrl_start = 0x0,
+ .cdt = 0x0,
+ .fifo_start = 0x0,
+ .fifo_size = 0x0,
+ .ptr1_reg = DMA7_PTR1,
+ .ptr2_reg = DMA7_PTR2,
+ .cnt1_reg = DMA7_CNT1,
+ .cnt2_reg = DMA7_CNT2,
+ },
+ [SRAM_CH09] = {
+ .name = "ch9",
+ .cmds_start = 0x0,
+ .ctrl_start = 0x0,
+ .cdt = 0x0,
+ .fifo_start = 0x0,
+ .fifo_size = 0x0,
+ .ptr1_reg = DMA8_PTR1,
+ .ptr2_reg = DMA8_PTR2,
+ .cnt1_reg = DMA8_CNT1,
+ .cnt2_reg = DMA8_CNT2,
+ },
+};
+
+void cx23885_irq_add(struct cx23885_dev *dev, u32 mask)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+ dev->pci_irqmask |= mask;
+
+ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+void cx23885_irq_add_enable(struct cx23885_dev *dev, u32 mask)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+ dev->pci_irqmask |= mask;
+ cx_set(PCI_INT_MSK, mask);
+
+ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+void cx23885_irq_enable(struct cx23885_dev *dev, u32 mask)
+{
+ u32 v;
+ unsigned long flags;
+ spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+ v = mask & dev->pci_irqmask;
+ if (v)
+ cx_set(PCI_INT_MSK, v);
+
+ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+static inline void cx23885_irq_enable_all(struct cx23885_dev *dev)
+{
+ cx23885_irq_enable(dev, 0xffffffff);
+}
+
+void cx23885_irq_disable(struct cx23885_dev *dev, u32 mask)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+ cx_clear(PCI_INT_MSK, mask);
+
+ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+static inline void cx23885_irq_disable_all(struct cx23885_dev *dev)
+{
+ cx23885_irq_disable(dev, 0xffffffff);
+}
+
+void cx23885_irq_remove(struct cx23885_dev *dev, u32 mask)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+ dev->pci_irqmask &= ~mask;
+ cx_clear(PCI_INT_MSK, mask);
+
+ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+static u32 cx23885_irq_get_mask(struct cx23885_dev *dev)
+{
+ u32 v;
+ unsigned long flags;
+ spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+ v = cx_read(PCI_INT_MSK);
+
+ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+ return v;
+}
+
+static int cx23885_risc_decode(u32 risc)
+{
+ static char *instr[16] = {
+ [RISC_SYNC >> 28] = "sync",
+ [RISC_WRITE >> 28] = "write",
+ [RISC_WRITEC >> 28] = "writec",
+ [RISC_READ >> 28] = "read",
+ [RISC_READC >> 28] = "readc",
+ [RISC_JUMP >> 28] = "jump",
+ [RISC_SKIP >> 28] = "skip",
+ [RISC_WRITERM >> 28] = "writerm",
+ [RISC_WRITECM >> 28] = "writecm",
+ [RISC_WRITECR >> 28] = "writecr",
+ };
+ static int incr[16] = {
+ [RISC_WRITE >> 28] = 3,
+ [RISC_JUMP >> 28] = 3,
+ [RISC_SKIP >> 28] = 1,
+ [RISC_SYNC >> 28] = 1,
+ [RISC_WRITERM >> 28] = 3,
+ [RISC_WRITECM >> 28] = 3,
+ [RISC_WRITECR >> 28] = 4,
+ };
+ static char *bits[] = {
+ "12", "13", "14", "resync",
+ "cnt0", "cnt1", "18", "19",
+ "20", "21", "22", "23",
+ "irq1", "irq2", "eol", "sol",
+ };
+ int i;
+
+ printk("0x%08x [ %s", risc,
+ instr[risc >> 28] ? instr[risc >> 28] : "INVALID");
+ for (i = ARRAY_SIZE(bits) - 1; i >= 0; i--)
+ if (risc & (1 << (i + 12)))
+ printk(" %s", bits[i]);
+ printk(" count=%d ]\n", risc & 0xfff);
+ return incr[risc >> 28] ? incr[risc >> 28] : 1;
+}
+
+void cx23885_wakeup(struct cx23885_tsport *port,
+ struct cx23885_dmaqueue *q, u32 count)
+{
+ struct cx23885_dev *dev = port->dev;
+ struct cx23885_buffer *buf;
+ int bc;
+
+ for (bc = 0;; bc++) {
+ if (list_empty(&q->active))
+ break;
+ buf = list_entry(q->active.next,
+ struct cx23885_buffer, vb.queue);
+
+ /* count comes from the hw and is is 16bit wide --
+ * this trick handles wrap-arounds correctly for
+ * up to 32767 buffers in flight... */
+ if ((s16) (count - buf->count) < 0)
+ break;
+
+ do_gettimeofday(&buf->vb.ts);
+ dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.i,
+ count, buf->count);
+ buf->vb.state = VIDEOBUF_DONE;
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+ }
+ if (list_empty(&q->active))
+ del_timer(&q->timeout);
+ else
+ mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT);
+ if (bc != 1)
+ printk(KERN_WARNING "%s: %d buffers handled (should be 1)\n",
+ __func__, bc);
+}
+
+int cx23885_sram_channel_setup(struct cx23885_dev *dev,
+ struct sram_channel *ch,
+ unsigned int bpl, u32 risc)
+{
+ unsigned int i, lines;
+ u32 cdt;
+
+ if (ch->cmds_start == 0) {
+ dprintk(1, "%s() Erasing channel [%s]\n", __func__,
+ ch->name);
+ cx_write(ch->ptr1_reg, 0);
+ cx_write(ch->ptr2_reg, 0);
+ cx_write(ch->cnt2_reg, 0);
+ cx_write(ch->cnt1_reg, 0);
+ return 0;
+ } else {
+ dprintk(1, "%s() Configuring channel [%s]\n", __func__,
+ ch->name);
+ }
+
+ bpl = (bpl + 7) & ~7; /* alignment */
+ cdt = ch->cdt;
+ lines = ch->fifo_size / bpl;
+ if (lines > 6)
+ lines = 6;
+ BUG_ON(lines < 2);
+
+ cx_write(8 + 0, RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
+ cx_write(8 + 4, 8);
+ cx_write(8 + 8, 0);
+
+ /* write CDT */
+ for (i = 0; i < lines; i++) {
+ dprintk(2, "%s() 0x%08x <- 0x%08x\n", __func__, cdt + 16*i,
+ ch->fifo_start + bpl*i);
+ cx_write(cdt + 16*i, ch->fifo_start + bpl*i);
+ cx_write(cdt + 16*i + 4, 0);
+ cx_write(cdt + 16*i + 8, 0);
+ cx_write(cdt + 16*i + 12, 0);
+ }
+
+ /* write CMDS */
+ if (ch->jumponly)
+ cx_write(ch->cmds_start + 0, 8);
+ else
+ cx_write(ch->cmds_start + 0, risc);
+ cx_write(ch->cmds_start + 4, 0); /* 64 bits 63-32 */
+ cx_write(ch->cmds_start + 8, cdt);
+ cx_write(ch->cmds_start + 12, (lines*16) >> 3);
+ cx_write(ch->cmds_start + 16, ch->ctrl_start);
+ if (ch->jumponly)
+ cx_write(ch->cmds_start + 20, 0x80000000 | (64 >> 2));
+ else
+ cx_write(ch->cmds_start + 20, 64 >> 2);
+ for (i = 24; i < 80; i += 4)
+ cx_write(ch->cmds_start + i, 0);
+
+ /* fill registers */
+ cx_write(ch->ptr1_reg, ch->fifo_start);
+ cx_write(ch->ptr2_reg, cdt);
+ cx_write(ch->cnt2_reg, (lines*16) >> 3);
+ cx_write(ch->cnt1_reg, (bpl >> 3) - 1);
+
+ dprintk(2, "[bridge %d] sram setup %s: bpl=%d lines=%d\n",
+ dev->bridge,
+ ch->name,
+ bpl,
+ lines);
+
+ return 0;
+}
+
+void cx23885_sram_channel_dump(struct cx23885_dev *dev,
+ struct sram_channel *ch)
+{
+ static char *name[] = {
+ "init risc lo",
+ "init risc hi",
+ "cdt base",
+ "cdt size",
+ "iq base",
+ "iq size",
+ "risc pc lo",
+ "risc pc hi",
+ "iq wr ptr",
+ "iq rd ptr",
+ "cdt current",
+ "pci target lo",
+ "pci target hi",
+ "line / byte",
+ };
+ u32 risc;
+ unsigned int i, j, n;
+
+ printk(KERN_WARNING "%s: %s - dma channel status dump\n",
+ dev->name, ch->name);
+ for (i = 0; i < ARRAY_SIZE(name); i++)
+ printk(KERN_WARNING "%s: cmds: %-15s: 0x%08x\n",
+ dev->name, name[i],
+ cx_read(ch->cmds_start + 4*i));
+
+ for (i = 0; i < 4; i++) {
+ risc = cx_read(ch->cmds_start + 4 * (i + 14));
+ printk(KERN_WARNING "%s: risc%d: ", dev->name, i);
+ cx23885_risc_decode(risc);
+ }
+ for (i = 0; i < (64 >> 2); i += n) {
+ risc = cx_read(ch->ctrl_start + 4 * i);
+ /* No consideration for bits 63-32 */
+
+ printk(KERN_WARNING "%s: (0x%08x) iq %x: ", dev->name,
+ ch->ctrl_start + 4 * i, i);
+ n = cx23885_risc_decode(risc);
+ for (j = 1; j < n; j++) {
+ risc = cx_read(ch->ctrl_start + 4 * (i + j));
+ printk(KERN_WARNING "%s: iq %x: 0x%08x [ arg #%d ]\n",
+ dev->name, i+j, risc, j);
+ }
+ }
+
+ printk(KERN_WARNING "%s: fifo: 0x%08x -> 0x%x\n",
+ dev->name, ch->fifo_start, ch->fifo_start+ch->fifo_size);
+ printk(KERN_WARNING "%s: ctrl: 0x%08x -> 0x%x\n",
+ dev->name, ch->ctrl_start, ch->ctrl_start + 6*16);
+ printk(KERN_WARNING "%s: ptr1_reg: 0x%08x\n",
+ dev->name, cx_read(ch->ptr1_reg));
+ printk(KERN_WARNING "%s: ptr2_reg: 0x%08x\n",
+ dev->name, cx_read(ch->ptr2_reg));
+ printk(KERN_WARNING "%s: cnt1_reg: 0x%08x\n",
+ dev->name, cx_read(ch->cnt1_reg));
+ printk(KERN_WARNING "%s: cnt2_reg: 0x%08x\n",
+ dev->name, cx_read(ch->cnt2_reg));
+}
+
+static void cx23885_risc_disasm(struct cx23885_tsport *port,
+ struct btcx_riscmem *risc)
+{
+ struct cx23885_dev *dev = port->dev;
+ unsigned int i, j, n;
+
+ printk(KERN_INFO "%s: risc disasm: %p [dma=0x%08lx]\n",
+ dev->name, risc->cpu, (unsigned long)risc->dma);
+ for (i = 0; i < (risc->size >> 2); i += n) {
+ printk(KERN_INFO "%s: %04d: ", dev->name, i);
+ n = cx23885_risc_decode(le32_to_cpu(risc->cpu[i]));
+ for (j = 1; j < n; j++)
+ printk(KERN_INFO "%s: %04d: 0x%08x [ arg #%d ]\n",
+ dev->name, i + j, risc->cpu[i + j], j);
+ if (risc->cpu[i] == cpu_to_le32(RISC_JUMP))
+ break;
+ }
+}
+
+static void cx23885_shutdown(struct cx23885_dev *dev)
+{
+ /* disable RISC controller */
+ cx_write(DEV_CNTRL2, 0);
+
+ /* Disable all IR activity */
+ cx_write(IR_CNTRL_REG, 0);
+
+ /* Disable Video A/B activity */
+ cx_write(VID_A_DMA_CTL, 0);
+ cx_write(VID_B_DMA_CTL, 0);
+ cx_write(VID_C_DMA_CTL, 0);
+
+ /* Disable Audio activity */
+ cx_write(AUD_INT_DMA_CTL, 0);
+ cx_write(AUD_EXT_DMA_CTL, 0);
+
+ /* Disable Serial port */
+ cx_write(UART_CTL, 0);
+
+ /* Disable Interrupts */
+ cx23885_irq_disable_all(dev);
+ cx_write(VID_A_INT_MSK, 0);
+ cx_write(VID_B_INT_MSK, 0);
+ cx_write(VID_C_INT_MSK, 0);
+ cx_write(AUDIO_INT_INT_MSK, 0);
+ cx_write(AUDIO_EXT_INT_MSK, 0);
+
+}
+
+static void cx23885_reset(struct cx23885_dev *dev)
+{
+ dprintk(1, "%s()\n", __func__);
+
+ cx23885_shutdown(dev);
+
+ cx_write(PCI_INT_STAT, 0xffffffff);
+ cx_write(VID_A_INT_STAT, 0xffffffff);
+ cx_write(VID_B_INT_STAT, 0xffffffff);
+ cx_write(VID_C_INT_STAT, 0xffffffff);
+ cx_write(AUDIO_INT_INT_STAT, 0xffffffff);
+ cx_write(AUDIO_EXT_INT_STAT, 0xffffffff);
+ cx_write(CLK_DELAY, cx_read(CLK_DELAY) & 0x80000000);
+ cx_write(PAD_CTRL, 0x00500300);
+
+ mdelay(100);
+
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH01],
+ 720*4, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH02], 128, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH03],
+ 188*4, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH04], 128, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH05], 128, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH06],
+ 188*4, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH07], 128, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH08], 128, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH09], 128, 0);
+
+ cx23885_gpio_setup(dev);
+}
+
+
+static int cx23885_pci_quirks(struct cx23885_dev *dev)
+{
+ dprintk(1, "%s()\n", __func__);
+
+ /* The cx23885 bridge has a weird bug which causes NMI to be asserted
+ * when DMA begins if RDR_TLCTL0 bit4 is not cleared. It does not
+ * occur on the cx23887 bridge.
+ */
+ if (dev->bridge == CX23885_BRIDGE_885)
+ cx_clear(RDR_TLCTL0, 1 << 4);
+
+ return 0;
+}
+
+static int get_resources(struct cx23885_dev *dev)
+{
+ if (request_mem_region(pci_resource_start(dev->pci, 0),
+ pci_resource_len(dev->pci, 0),
+ dev->name))
+ return 0;
+
+ printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx\n",
+ dev->name, (unsigned long long)pci_resource_start(dev->pci, 0));
+
+ return -EBUSY;
+}
+
+static void cx23885_timeout(unsigned long data);
+int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
+ u32 reg, u32 mask, u32 value);
+
+static int cx23885_init_tsport(struct cx23885_dev *dev,
+ struct cx23885_tsport *port, int portno)
+{
+ dprintk(1, "%s(portno=%d)\n", __func__, portno);
+
+ /* Transport bus init dma queue - Common settings */
+ port->dma_ctl_val = 0x11; /* Enable RISC controller and Fifo */
+ port->ts_int_msk_val = 0x1111; /* TS port bits for RISC */
+ port->vld_misc_val = 0x0;
+ port->hw_sop_ctrl_val = (0x47 << 16 | 188 << 4);
+
+ spin_lock_init(&port->slock);
+ port->dev = dev;
+ port->nr = portno;
+
+ INIT_LIST_HEAD(&port->mpegq.active);
+ INIT_LIST_HEAD(&port->mpegq.queued);
+ port->mpegq.timeout.function = cx23885_timeout;
+ port->mpegq.timeout.data = (unsigned long)port;
+ init_timer(&port->mpegq.timeout);
+
+ mutex_init(&port->frontends.lock);
+ INIT_LIST_HEAD(&port->frontends.felist);
+ port->frontends.active_fe_id = 0;
+
+ /* This should be hardcoded allow a single frontend
+ * attachment to this tsport, keeping the -dvb.c
+ * code clean and safe.
+ */
+ if (!port->num_frontends)
+ port->num_frontends = 1;
+
+ switch (portno) {
+ case 1:
+ port->reg_gpcnt = VID_B_GPCNT;
+ port->reg_gpcnt_ctl = VID_B_GPCNT_CTL;
+ port->reg_dma_ctl = VID_B_DMA_CTL;
+ port->reg_lngth = VID_B_LNGTH;
+ port->reg_hw_sop_ctrl = VID_B_HW_SOP_CTL;
+ port->reg_gen_ctrl = VID_B_GEN_CTL;
+ port->reg_bd_pkt_status = VID_B_BD_PKT_STATUS;
+ port->reg_sop_status = VID_B_SOP_STATUS;
+ port->reg_fifo_ovfl_stat = VID_B_FIFO_OVFL_STAT;
+ port->reg_vld_misc = VID_B_VLD_MISC;
+ port->reg_ts_clk_en = VID_B_TS_CLK_EN;
+ port->reg_src_sel = VID_B_SRC_SEL;
+ port->reg_ts_int_msk = VID_B_INT_MSK;
+ port->reg_ts_int_stat = VID_B_INT_STAT;
+ port->sram_chno = SRAM_CH03; /* VID_B */
+ port->pci_irqmask = 0x02; /* VID_B bit1 */
+ break;
+ case 2:
+ port->reg_gpcnt = VID_C_GPCNT;
+ port->reg_gpcnt_ctl = VID_C_GPCNT_CTL;
+ port->reg_dma_ctl = VID_C_DMA_CTL;
+ port->reg_lngth = VID_C_LNGTH;
+ port->reg_hw_sop_ctrl = VID_C_HW_SOP_CTL;
+ port->reg_gen_ctrl = VID_C_GEN_CTL;
+ port->reg_bd_pkt_status = VID_C_BD_PKT_STATUS;
+ port->reg_sop_status = VID_C_SOP_STATUS;
+ port->reg_fifo_ovfl_stat = VID_C_FIFO_OVFL_STAT;
+ port->reg_vld_misc = VID_C_VLD_MISC;
+ port->reg_ts_clk_en = VID_C_TS_CLK_EN;
+ port->reg_src_sel = 0;
+ port->reg_ts_int_msk = VID_C_INT_MSK;
+ port->reg_ts_int_stat = VID_C_INT_STAT;
+ port->sram_chno = SRAM_CH06; /* VID_C */
+ port->pci_irqmask = 0x04; /* VID_C bit2 */
+ break;
+ default:
+ BUG();
+ }
+
+ cx23885_risc_stopper(dev->pci, &port->mpegq.stopper,
+ port->reg_dma_ctl, port->dma_ctl_val, 0x00);
+
+ return 0;
+}
+
+static void cx23885_dev_checkrevision(struct cx23885_dev *dev)
+{
+ switch (cx_read(RDR_CFG2) & 0xff) {
+ case 0x00:
+ /* cx23885 */
+ dev->hwrevision = 0xa0;
+ break;
+ case 0x01:
+ /* CX23885-12Z */
+ dev->hwrevision = 0xa1;
+ break;
+ case 0x02:
+ /* CX23885-13Z/14Z */
+ dev->hwrevision = 0xb0;
+ break;
+ case 0x03:
+ if (dev->pci->device == 0x8880) {
+ /* CX23888-21Z/22Z */
+ dev->hwrevision = 0xc0;
+ } else {
+ /* CX23885-14Z */
+ dev->hwrevision = 0xa4;
+ }
+ break;
+ case 0x04:
+ if (dev->pci->device == 0x8880) {
+ /* CX23888-31Z */
+ dev->hwrevision = 0xd0;
+ } else {
+ /* CX23885-15Z, CX23888-31Z */
+ dev->hwrevision = 0xa5;
+ }
+ break;
+ case 0x0e:
+ /* CX23887-15Z */
+ dev->hwrevision = 0xc0;
+ break;
+ case 0x0f:
+ /* CX23887-14Z */
+ dev->hwrevision = 0xb1;
+ break;
+ default:
+ printk(KERN_ERR "%s() New hardware revision found 0x%x\n",
+ __func__, dev->hwrevision);
+ }
+ if (dev->hwrevision)
+ printk(KERN_INFO "%s() Hardware revision = 0x%02x\n",
+ __func__, dev->hwrevision);
+ else
+ printk(KERN_ERR "%s() Hardware revision unknown 0x%x\n",
+ __func__, dev->hwrevision);
+}
+
+/* Find the first v4l2_subdev member of the group id in hw */
+struct v4l2_subdev *cx23885_find_hw(struct cx23885_dev *dev, u32 hw)
+{
+ struct v4l2_subdev *result = NULL;
+ struct v4l2_subdev *sd;
+
+ spin_lock(&dev->v4l2_dev.lock);
+ v4l2_device_for_each_subdev(sd, &dev->v4l2_dev) {
+ if (sd->grp_id == hw) {
+ result = sd;
+ break;
+ }
+ }
+ spin_unlock(&dev->v4l2_dev.lock);
+ return result;
+}
+
+static int cx23885_dev_setup(struct cx23885_dev *dev)
+{
+ int i;
+
+ spin_lock_init(&dev->pci_irqmask_lock);
+
+ mutex_init(&dev->lock);
+ mutex_init(&dev->gpio_lock);
+
+ atomic_inc(&dev->refcount);
+
+ dev->nr = cx23885_devcount++;
+ sprintf(dev->name, "cx23885[%d]", dev->nr);
+
+ /* Configure the internal memory */
+ if (dev->pci->device == 0x8880) {
+ /* Could be 887 or 888, assume a default */
+ dev->bridge = CX23885_BRIDGE_887;
+ /* Apply a sensible clock frequency for the PCIe bridge */
+ dev->clk_freq = 25000000;
+ dev->sram_channels = cx23887_sram_channels;
+ } else
+ if (dev->pci->device == 0x8852) {
+ dev->bridge = CX23885_BRIDGE_885;
+ /* Apply a sensible clock frequency for the PCIe bridge */
+ dev->clk_freq = 28000000;
+ dev->sram_channels = cx23885_sram_channels;
+ } else
+ BUG();
+
+ dprintk(1, "%s() Memory configured for PCIe bridge type %d\n",
+ __func__, dev->bridge);
+
+ /* board config */
+ dev->board = UNSET;
+ if (card[dev->nr] < cx23885_bcount)
+ dev->board = card[dev->nr];
+ for (i = 0; UNSET == dev->board && i < cx23885_idcount; i++)
+ if (dev->pci->subsystem_vendor == cx23885_subids[i].subvendor &&
+ dev->pci->subsystem_device == cx23885_subids[i].subdevice)
+ dev->board = cx23885_subids[i].card;
+ if (UNSET == dev->board) {
+ dev->board = CX23885_BOARD_UNKNOWN;
+ cx23885_card_list(dev);
+ }
+
+ /* If the user specific a clk freq override, apply it */
+ if (cx23885_boards[dev->board].clk_freq > 0)
+ dev->clk_freq = cx23885_boards[dev->board].clk_freq;
+
+ dev->pci_bus = dev->pci->bus->number;
+ dev->pci_slot = PCI_SLOT(dev->pci->devfn);
+ cx23885_irq_add(dev, 0x001f00);
+
+ /* External Master 1 Bus */
+ dev->i2c_bus[0].nr = 0;
+ dev->i2c_bus[0].dev = dev;
+ dev->i2c_bus[0].reg_stat = I2C1_STAT;
+ dev->i2c_bus[0].reg_ctrl = I2C1_CTRL;
+ dev->i2c_bus[0].reg_addr = I2C1_ADDR;
+ dev->i2c_bus[0].reg_rdata = I2C1_RDATA;
+ dev->i2c_bus[0].reg_wdata = I2C1_WDATA;
+ dev->i2c_bus[0].i2c_period = (0x9d << 24); /* 100kHz */
+
+ /* External Master 2 Bus */
+ dev->i2c_bus[1].nr = 1;
+ dev->i2c_bus[1].dev = dev;
+ dev->i2c_bus[1].reg_stat = I2C2_STAT;
+ dev->i2c_bus[1].reg_ctrl = I2C2_CTRL;
+ dev->i2c_bus[1].reg_addr = I2C2_ADDR;
+ dev->i2c_bus[1].reg_rdata = I2C2_RDATA;
+ dev->i2c_bus[1].reg_wdata = I2C2_WDATA;
+ dev->i2c_bus[1].i2c_period = (0x9d << 24); /* 100kHz */
+
+ /* Internal Master 3 Bus */
+ dev->i2c_bus[2].nr = 2;
+ dev->i2c_bus[2].dev = dev;
+ dev->i2c_bus[2].reg_stat = I2C3_STAT;
+ dev->i2c_bus[2].reg_ctrl = I2C3_CTRL;
+ dev->i2c_bus[2].reg_addr = I2C3_ADDR;
+ dev->i2c_bus[2].reg_rdata = I2C3_RDATA;
+ dev->i2c_bus[2].reg_wdata = I2C3_WDATA;
+ dev->i2c_bus[2].i2c_period = (0x07 << 24); /* 1.95MHz */
+
+ if ((cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) ||
+ (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER))
+ cx23885_init_tsport(dev, &dev->ts1, 1);
+
+ if ((cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) ||
+ (cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER))
+ cx23885_init_tsport(dev, &dev->ts2, 2);
+
+ if (get_resources(dev) < 0) {
+ printk(KERN_ERR "CORE %s No more PCIe resources for "
+ "subsystem: %04x:%04x\n",
+ dev->name, dev->pci->subsystem_vendor,
+ dev->pci->subsystem_device);
+
+ cx23885_devcount--;
+ return -ENODEV;
+ }
+
+ /* PCIe stuff */
+ dev->lmmio = ioremap(pci_resource_start(dev->pci, 0),
+ pci_resource_len(dev->pci, 0));
+
+ dev->bmmio = (u8 __iomem *)dev->lmmio;
+
+ printk(KERN_INFO "CORE %s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n",
+ dev->name, dev->pci->subsystem_vendor,
+ dev->pci->subsystem_device, cx23885_boards[dev->board].name,
+ dev->board, card[dev->nr] == dev->board ?
+ "insmod option" : "autodetected");
+
+ cx23885_pci_quirks(dev);
+
+ /* Assume some sensible defaults */
+ dev->tuner_type = cx23885_boards[dev->board].tuner_type;
+ dev->tuner_addr = cx23885_boards[dev->board].tuner_addr;
+ dev->tuner_bus = cx23885_boards[dev->board].tuner_bus;
+ dev->radio_type = cx23885_boards[dev->board].radio_type;
+ dev->radio_addr = cx23885_boards[dev->board].radio_addr;
+
+ dprintk(1, "%s() tuner_type = 0x%x tuner_addr = 0x%x tuner_bus = %d\n",
+ __func__, dev->tuner_type, dev->tuner_addr, dev->tuner_bus);
+ dprintk(1, "%s() radio_type = 0x%x radio_addr = 0x%x\n",
+ __func__, dev->radio_type, dev->radio_addr);
+
+ /* The cx23417 encoder has GPIO's that need to be initialised
+ * before DVB, so that demodulators and tuners are out of
+ * reset before DVB uses them.
+ */
+ if ((cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) ||
+ (cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER))
+ cx23885_mc417_init(dev);
+
+ /* init hardware */
+ cx23885_reset(dev);
+
+ cx23885_i2c_register(&dev->i2c_bus[0]);
+ cx23885_i2c_register(&dev->i2c_bus[1]);
+ cx23885_i2c_register(&dev->i2c_bus[2]);
+ cx23885_card_setup(dev);
+ call_all(dev, core, s_power, 0);
+ cx23885_ir_init(dev);
+
+ if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO) {
+ if (cx23885_video_register(dev) < 0) {
+ printk(KERN_ERR "%s() Failed to register analog "
+ "video adapters on VID_A\n", __func__);
+ }
+ }
+
+ if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) {
+ if (cx23885_boards[dev->board].num_fds_portb)
+ dev->ts1.num_frontends =
+ cx23885_boards[dev->board].num_fds_portb;
+ if (cx23885_dvb_register(&dev->ts1) < 0) {
+ printk(KERN_ERR "%s() Failed to register dvb adapters on VID_B\n",
+ __func__);
+ }
+ } else
+ if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) {
+ if (cx23885_417_register(dev) < 0) {
+ printk(KERN_ERR
+ "%s() Failed to register 417 on VID_B\n",
+ __func__);
+ }
+ }
+
+ if (cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) {
+ if (cx23885_boards[dev->board].num_fds_portc)
+ dev->ts2.num_frontends =
+ cx23885_boards[dev->board].num_fds_portc;
+ if (cx23885_dvb_register(&dev->ts2) < 0) {
+ printk(KERN_ERR
+ "%s() Failed to register dvb on VID_C\n",
+ __func__);
+ }
+ } else
+ if (cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER) {
+ if (cx23885_417_register(dev) < 0) {
+ printk(KERN_ERR
+ "%s() Failed to register 417 on VID_C\n",
+ __func__);
+ }
+ }
+
+ cx23885_dev_checkrevision(dev);
+
+ /* disable MSI for NetUP cards, otherwise CI is not working */
+ if (cx23885_boards[dev->board].ci_type > 0)
+ cx_clear(RDR_RDRCTL1, 1 << 8);
+
+ return 0;
+}
+
+static void cx23885_dev_unregister(struct cx23885_dev *dev)
+{
+ release_mem_region(pci_resource_start(dev->pci, 0),
+ pci_resource_len(dev->pci, 0));
+
+ if (!atomic_dec_and_test(&dev->refcount))
+ return;
+
+ if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO)
+ cx23885_video_unregister(dev);
+
+ if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB)
+ cx23885_dvb_unregister(&dev->ts1);
+
+ if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER)
+ cx23885_417_unregister(dev);
+
+ if (cx23885_boards[dev->board].portc == CX23885_MPEG_DVB)
+ cx23885_dvb_unregister(&dev->ts2);
+
+ if (cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER)
+ cx23885_417_unregister(dev);
+
+ cx23885_i2c_unregister(&dev->i2c_bus[2]);
+ cx23885_i2c_unregister(&dev->i2c_bus[1]);
+ cx23885_i2c_unregister(&dev->i2c_bus[0]);
+
+ iounmap(dev->lmmio);
+}
+
+static __le32 *cx23885_risc_field(__le32 *rp, struct scatterlist *sglist,
+ unsigned int offset, u32 sync_line,
+ unsigned int bpl, unsigned int padding,
+ unsigned int lines, unsigned int lpi)
+{
+ struct scatterlist *sg;
+ unsigned int line, todo, sol;
+
+ /* sync instruction */
+ if (sync_line != NO_SYNC_LINE)
+ *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
+
+ /* scan lines */
+ sg = sglist;
+ for (line = 0; line < lines; line++) {
+ while (offset && offset >= sg_dma_len(sg)) {
+ offset -= sg_dma_len(sg);
+ sg++;
+ }
+
+ if (lpi && line > 0 && !(line % lpi))
+ sol = RISC_SOL | RISC_IRQ1 | RISC_CNT_INC;
+ else
+ sol = RISC_SOL;
+
+ if (bpl <= sg_dma_len(sg)-offset) {
+ /* fits into current chunk */
+ *(rp++) = cpu_to_le32(RISC_WRITE|sol|RISC_EOL|bpl);
+ *(rp++) = cpu_to_le32(sg_dma_address(sg)+offset);
+ *(rp++) = cpu_to_le32(0); /* bits 63-32 */
+ offset += bpl;
+ } else {
+ /* scanline needs to be split */
+ todo = bpl;
+ *(rp++) = cpu_to_le32(RISC_WRITE|sol|
+ (sg_dma_len(sg)-offset));
+ *(rp++) = cpu_to_le32(sg_dma_address(sg)+offset);
+ *(rp++) = cpu_to_le32(0); /* bits 63-32 */
+ todo -= (sg_dma_len(sg)-offset);
+ offset = 0;
+ sg++;
+ while (todo > sg_dma_len(sg)) {
+ *(rp++) = cpu_to_le32(RISC_WRITE|
+ sg_dma_len(sg));
+ *(rp++) = cpu_to_le32(sg_dma_address(sg));
+ *(rp++) = cpu_to_le32(0); /* bits 63-32 */
+ todo -= sg_dma_len(sg);
+ sg++;
+ }
+ *(rp++) = cpu_to_le32(RISC_WRITE|RISC_EOL|todo);
+ *(rp++) = cpu_to_le32(sg_dma_address(sg));
+ *(rp++) = cpu_to_le32(0); /* bits 63-32 */
+ offset += todo;
+ }
+ offset += padding;
+ }
+
+ return rp;
+}
+
+int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+ struct scatterlist *sglist, unsigned int top_offset,
+ unsigned int bottom_offset, unsigned int bpl,
+ unsigned int padding, unsigned int lines)
+{
+ u32 instructions, fields;
+ __le32 *rp;
+ int rc;
+
+ fields = 0;
+ if (UNSET != top_offset)
+ fields++;
+ if (UNSET != bottom_offset)
+ fields++;
+
+ /* estimate risc mem: worst case is one write per page border +
+ one write per scan line + syncs + jump (all 2 dwords). Padding
+ can cause next bpl to start close to a page border. First DMA
+ region may be smaller than PAGE_SIZE */
+ /* write and jump need and extra dword */
+ instructions = fields * (1 + ((bpl + padding) * lines)
+ / PAGE_SIZE + lines);
+ instructions += 2;
+ rc = btcx_riscmem_alloc(pci, risc, instructions*12);
+ if (rc < 0)
+ return rc;
+
+ /* write risc instructions */
+ rp = risc->cpu;
+ if (UNSET != top_offset)
+ rp = cx23885_risc_field(rp, sglist, top_offset, 0,
+ bpl, padding, lines, 0);
+ if (UNSET != bottom_offset)
+ rp = cx23885_risc_field(rp, sglist, bottom_offset, 0x200,
+ bpl, padding, lines, 0);
+
+ /* save pointer to jmp instruction address */
+ risc->jmp = rp;
+ BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size);
+ return 0;
+}
+
+int cx23885_risc_databuffer(struct pci_dev *pci,
+ struct btcx_riscmem *risc,
+ struct scatterlist *sglist,
+ unsigned int bpl,
+ unsigned int lines, unsigned int lpi)
+{
+ u32 instructions;
+ __le32 *rp;
+ int rc;
+
+ /* estimate risc mem: worst case is one write per page border +
+ one write per scan line + syncs + jump (all 2 dwords). Here
+ there is no padding and no sync. First DMA region may be smaller
+ than PAGE_SIZE */
+ /* Jump and write need an extra dword */
+ instructions = 1 + (bpl * lines) / PAGE_SIZE + lines;
+ instructions += 1;
+
+ rc = btcx_riscmem_alloc(pci, risc, instructions*12);
+ if (rc < 0)
+ return rc;
+
+ /* write risc instructions */
+ rp = risc->cpu;
+ rp = cx23885_risc_field(rp, sglist, 0, NO_SYNC_LINE,
+ bpl, 0, lines, lpi);
+
+ /* save pointer to jmp instruction address */
+ risc->jmp = rp;
+ BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size);
+ return 0;
+}
+
+int cx23885_risc_vbibuffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+ struct scatterlist *sglist, unsigned int top_offset,
+ unsigned int bottom_offset, unsigned int bpl,
+ unsigned int padding, unsigned int lines)
+{
+ u32 instructions, fields;
+ __le32 *rp;
+ int rc;
+
+ fields = 0;
+ if (UNSET != top_offset)
+ fields++;
+ if (UNSET != bottom_offset)
+ fields++;
+
+ /* estimate risc mem: worst case is one write per page border +
+ one write per scan line + syncs + jump (all 2 dwords). Padding
+ can cause next bpl to start close to a page border. First DMA
+ region may be smaller than PAGE_SIZE */
+ /* write and jump need and extra dword */
+ instructions = fields * (1 + ((bpl + padding) * lines)
+ / PAGE_SIZE + lines);
+ instructions += 2;
+ rc = btcx_riscmem_alloc(pci, risc, instructions*12);
+ if (rc < 0)
+ return rc;
+ /* write risc instructions */
+ rp = risc->cpu;
+
+ /* Sync to line 6, so US CC line 21 will appear in line '12'
+ * in the userland vbi payload */
+ if (UNSET != top_offset)
+ rp = cx23885_risc_field(rp, sglist, top_offset, 6,
+ bpl, padding, lines, 0);
+
+ if (UNSET != bottom_offset)
+ rp = cx23885_risc_field(rp, sglist, bottom_offset, 0x207,
+ bpl, padding, lines, 0);
+
+
+
+ /* save pointer to jmp instruction address */
+ risc->jmp = rp;
+ BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size);
+ return 0;
+}
+
+
+int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
+ u32 reg, u32 mask, u32 value)
+{
+ __le32 *rp;
+ int rc;
+
+ rc = btcx_riscmem_alloc(pci, risc, 4*16);
+ if (rc < 0)
+ return rc;
+
+ /* write risc instructions */
+ rp = risc->cpu;
+ *(rp++) = cpu_to_le32(RISC_WRITECR | RISC_IRQ2);
+ *(rp++) = cpu_to_le32(reg);
+ *(rp++) = cpu_to_le32(value);
+ *(rp++) = cpu_to_le32(mask);
+ *(rp++) = cpu_to_le32(RISC_JUMP);
+ *(rp++) = cpu_to_le32(risc->dma);
+ *(rp++) = cpu_to_le32(0); /* bits 63-32 */
+ return 0;
+}
+
+void cx23885_free_buffer(struct videobuf_queue *q, struct cx23885_buffer *buf)
+{
+ struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
+
+ BUG_ON(in_interrupt());
+ videobuf_waiton(q, &buf->vb, 0, 0);
+ videobuf_dma_unmap(q->dev, dma);
+ videobuf_dma_free(dma);
+ btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static void cx23885_tsport_reg_dump(struct cx23885_tsport *port)
+{
+ struct cx23885_dev *dev = port->dev;
+
+ dprintk(1, "%s() Register Dump\n", __func__);
+ dprintk(1, "%s() DEV_CNTRL2 0x%08X\n", __func__,
+ cx_read(DEV_CNTRL2));
+ dprintk(1, "%s() PCI_INT_MSK 0x%08X\n", __func__,
+ cx23885_irq_get_mask(dev));
+ dprintk(1, "%s() AUD_INT_INT_MSK 0x%08X\n", __func__,
+ cx_read(AUDIO_INT_INT_MSK));
+ dprintk(1, "%s() AUD_INT_DMA_CTL 0x%08X\n", __func__,
+ cx_read(AUD_INT_DMA_CTL));
+ dprintk(1, "%s() AUD_EXT_INT_MSK 0x%08X\n", __func__,
+ cx_read(AUDIO_EXT_INT_MSK));
+ dprintk(1, "%s() AUD_EXT_DMA_CTL 0x%08X\n", __func__,
+ cx_read(AUD_EXT_DMA_CTL));
+ dprintk(1, "%s() PAD_CTRL 0x%08X\n", __func__,
+ cx_read(PAD_CTRL));
+ dprintk(1, "%s() ALT_PIN_OUT_SEL 0x%08X\n", __func__,
+ cx_read(ALT_PIN_OUT_SEL));
+ dprintk(1, "%s() GPIO2 0x%08X\n", __func__,
+ cx_read(GPIO2));
+ dprintk(1, "%s() gpcnt(0x%08X) 0x%08X\n", __func__,
+ port->reg_gpcnt, cx_read(port->reg_gpcnt));
+ dprintk(1, "%s() gpcnt_ctl(0x%08X) 0x%08x\n", __func__,
+ port->reg_gpcnt_ctl, cx_read(port->reg_gpcnt_ctl));
+ dprintk(1, "%s() dma_ctl(0x%08X) 0x%08x\n", __func__,
+ port->reg_dma_ctl, cx_read(port->reg_dma_ctl));
+ if (port->reg_src_sel)
+ dprintk(1, "%s() src_sel(0x%08X) 0x%08x\n", __func__,
+ port->reg_src_sel, cx_read(port->reg_src_sel));
+ dprintk(1, "%s() lngth(0x%08X) 0x%08x\n", __func__,
+ port->reg_lngth, cx_read(port->reg_lngth));
+ dprintk(1, "%s() hw_sop_ctrl(0x%08X) 0x%08x\n", __func__,
+ port->reg_hw_sop_ctrl, cx_read(port->reg_hw_sop_ctrl));
+ dprintk(1, "%s() gen_ctrl(0x%08X) 0x%08x\n", __func__,
+ port->reg_gen_ctrl, cx_read(port->reg_gen_ctrl));
+ dprintk(1, "%s() bd_pkt_status(0x%08X) 0x%08x\n", __func__,
+ port->reg_bd_pkt_status, cx_read(port->reg_bd_pkt_status));
+ dprintk(1, "%s() sop_status(0x%08X) 0x%08x\n", __func__,
+ port->reg_sop_status, cx_read(port->reg_sop_status));
+ dprintk(1, "%s() fifo_ovfl_stat(0x%08X) 0x%08x\n", __func__,
+ port->reg_fifo_ovfl_stat, cx_read(port->reg_fifo_ovfl_stat));
+ dprintk(1, "%s() vld_misc(0x%08X) 0x%08x\n", __func__,
+ port->reg_vld_misc, cx_read(port->reg_vld_misc));
+ dprintk(1, "%s() ts_clk_en(0x%08X) 0x%08x\n", __func__,
+ port->reg_ts_clk_en, cx_read(port->reg_ts_clk_en));
+ dprintk(1, "%s() ts_int_msk(0x%08X) 0x%08x\n", __func__,
+ port->reg_ts_int_msk, cx_read(port->reg_ts_int_msk));
+}
+
+static int cx23885_start_dma(struct cx23885_tsport *port,
+ struct cx23885_dmaqueue *q,
+ struct cx23885_buffer *buf)
+{
+ struct cx23885_dev *dev = port->dev;
+ u32 reg;
+
+ dprintk(1, "%s() w: %d, h: %d, f: %d\n", __func__,
+ buf->vb.width, buf->vb.height, buf->vb.field);
+
+ /* Stop the fifo and risc engine for this port */
+ cx_clear(port->reg_dma_ctl, port->dma_ctl_val);
+
+ /* setup fifo + format */
+ cx23885_sram_channel_setup(dev,
+ &dev->sram_channels[port->sram_chno],
+ port->ts_packet_size, buf->risc.dma);
+ if (debug > 5) {
+ cx23885_sram_channel_dump(dev,
+ &dev->sram_channels[port->sram_chno]);
+ cx23885_risc_disasm(port, &buf->risc);
+ }
+
+ /* write TS length to chip */
+ cx_write(port->reg_lngth, buf->vb.width);
+
+ if ((!(cx23885_boards[dev->board].portb & CX23885_MPEG_DVB)) &&
+ (!(cx23885_boards[dev->board].portc & CX23885_MPEG_DVB))) {
+ printk("%s() Unsupported .portb/c (0x%08x)/(0x%08x)\n",
+ __func__,
+ cx23885_boards[dev->board].portb,
+ cx23885_boards[dev->board].portc);
+ return -EINVAL;
+ }
+
+ if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER)
+ cx23885_av_clk(dev, 0);
+
+ udelay(100);
+
+ /* If the port supports SRC SELECT, configure it */
+ if (port->reg_src_sel)
+ cx_write(port->reg_src_sel, port->src_sel_val);
+
+ cx_write(port->reg_hw_sop_ctrl, port->hw_sop_ctrl_val);
+ cx_write(port->reg_ts_clk_en, port->ts_clk_en_val);
+ cx_write(port->reg_vld_misc, port->vld_misc_val);
+ cx_write(port->reg_gen_ctrl, port->gen_ctrl_val);
+ udelay(100);
+
+ /* NOTE: this is 2 (reserved) for portb, does it matter? */
+ /* reset counter to zero */
+ cx_write(port->reg_gpcnt_ctl, 3);
+ q->count = 1;
+
+ /* Set VIDB pins to input */
+ if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) {
+ reg = cx_read(PAD_CTRL);
+ reg &= ~0x3; /* Clear TS1_OE & TS1_SOP_OE */
+ cx_write(PAD_CTRL, reg);
+ }
+
+ /* Set VIDC pins to input */
+ if (cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) {
+ reg = cx_read(PAD_CTRL);
+ reg &= ~0x4; /* Clear TS2_SOP_OE */
+ cx_write(PAD_CTRL, reg);
+ }
+
+ if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) {
+
+ reg = cx_read(PAD_CTRL);
+ reg = reg & ~0x1; /* Clear TS1_OE */
+
+ /* FIXME, bit 2 writing here is questionable */
+ /* set TS1_SOP_OE and TS1_OE_HI */
+ reg = reg | 0xa;
+ cx_write(PAD_CTRL, reg);
+
+ /* FIXME and these two registers should be documented. */
+ cx_write(CLK_DELAY, cx_read(CLK_DELAY) | 0x80000011);
+ cx_write(ALT_PIN_OUT_SEL, 0x10100045);
+ }
+
+ switch (dev->bridge) {
+ case CX23885_BRIDGE_885:
+ case CX23885_BRIDGE_887:
+ case CX23885_BRIDGE_888:
+ /* enable irqs */
+ dprintk(1, "%s() enabling TS int's and DMA\n", __func__);
+ cx_set(port->reg_ts_int_msk, port->ts_int_msk_val);
+ cx_set(port->reg_dma_ctl, port->dma_ctl_val);
+ cx23885_irq_add(dev, port->pci_irqmask);
+ cx23885_irq_enable_all(dev);
+ break;
+ default:
+ BUG();
+ }
+
+ cx_set(DEV_CNTRL2, (1<<5)); /* Enable RISC controller */
+
+ if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER)
+ cx23885_av_clk(dev, 1);
+
+ if (debug > 4)
+ cx23885_tsport_reg_dump(port);
+
+ return 0;
+}
+
+static int cx23885_stop_dma(struct cx23885_tsport *port)
+{
+ struct cx23885_dev *dev = port->dev;
+ u32 reg;
+
+ dprintk(1, "%s()\n", __func__);
+
+ /* Stop interrupts and DMA */
+ cx_clear(port->reg_ts_int_msk, port->ts_int_msk_val);
+ cx_clear(port->reg_dma_ctl, port->dma_ctl_val);
+
+ if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) {
+
+ reg = cx_read(PAD_CTRL);
+
+ /* Set TS1_OE */
+ reg = reg | 0x1;
+
+ /* clear TS1_SOP_OE and TS1_OE_HI */
+ reg = reg & ~0xa;
+ cx_write(PAD_CTRL, reg);
+ cx_write(port->reg_src_sel, 0);
+ cx_write(port->reg_gen_ctrl, 8);
+
+ }
+
+ if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER)
+ cx23885_av_clk(dev, 0);
+
+ return 0;
+}
+
+int cx23885_restart_queue(struct cx23885_tsport *port,
+ struct cx23885_dmaqueue *q)
+{
+ struct cx23885_dev *dev = port->dev;
+ struct cx23885_buffer *buf;
+
+ dprintk(5, "%s()\n", __func__);
+ if (list_empty(&q->active)) {
+ struct cx23885_buffer *prev;
+ prev = NULL;
+
+ dprintk(5, "%s() queue is empty\n", __func__);
+
+ for (;;) {
+ if (list_empty(&q->queued))
+ return 0;
+ buf = list_entry(q->queued.next, struct cx23885_buffer,
+ vb.queue);
+ if (NULL == prev) {
+ list_del(&buf->vb.queue);
+ list_add_tail(&buf->vb.queue, &q->active);
+ cx23885_start_dma(port, q, buf);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ dprintk(5, "[%p/%d] restart_queue - f/active\n",
+ buf, buf->vb.i);
+
+ } else if (prev->vb.width == buf->vb.width &&
+ prev->vb.height == buf->vb.height &&
+ prev->fmt == buf->fmt) {
+ list_del(&buf->vb.queue);
+ list_add_tail(&buf->vb.queue, &q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ /* 64 bit bits 63-32 */
+ prev->risc.jmp[2] = cpu_to_le32(0);
+ dprintk(5, "[%p/%d] restart_queue - m/active\n",
+ buf, buf->vb.i);
+ } else {
+ return 0;
+ }
+ prev = buf;
+ }
+ return 0;
+ }
+
+ buf = list_entry(q->active.next, struct cx23885_buffer, vb.queue);
+ dprintk(2, "restart_queue [%p/%d]: restart dma\n",
+ buf, buf->vb.i);
+ cx23885_start_dma(port, q, buf);
+ list_for_each_entry(buf, &q->active, vb.queue)
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT);
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+int cx23885_buf_prepare(struct videobuf_queue *q, struct cx23885_tsport *port,
+ struct cx23885_buffer *buf, enum v4l2_field field)
+{
+ struct cx23885_dev *dev = port->dev;
+ int size = port->ts_packet_size * port->ts_packet_count;
+ int rc;
+
+ dprintk(1, "%s: %p\n", __func__, buf);
+ if (0 != buf->vb.baddr && buf->vb.bsize < size)
+ return -EINVAL;
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ buf->vb.width = port->ts_packet_size;
+ buf->vb.height = port->ts_packet_count;
+ buf->vb.size = size;
+ buf->vb.field = field /*V4L2_FIELD_TOP*/;
+
+ rc = videobuf_iolock(q, &buf->vb, NULL);
+ if (0 != rc)
+ goto fail;
+ cx23885_risc_databuffer(dev->pci, &buf->risc,
+ videobuf_to_dma(&buf->vb)->sglist,
+ buf->vb.width, buf->vb.height, 0);
+ }
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+ fail:
+ cx23885_free_buffer(q, buf);
+ return rc;
+}
+
+void cx23885_buf_queue(struct cx23885_tsport *port, struct cx23885_buffer *buf)
+{
+ struct cx23885_buffer *prev;
+ struct cx23885_dev *dev = port->dev;
+ struct cx23885_dmaqueue *cx88q = &port->mpegq;
+
+ /* add jump to stopper */
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
+ buf->risc.jmp[1] = cpu_to_le32(cx88q->stopper.dma);
+ buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
+
+ if (list_empty(&cx88q->active)) {
+ dprintk(1, "queue is empty - first active\n");
+ list_add_tail(&buf->vb.queue, &cx88q->active);
+ cx23885_start_dma(port, cx88q, buf);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = cx88q->count++;
+ mod_timer(&cx88q->timeout, jiffies + BUFFER_TIMEOUT);
+ dprintk(1, "[%p/%d] %s - first active\n",
+ buf, buf->vb.i, __func__);
+ } else {
+ dprintk(1, "queue is not empty - append to active\n");
+ prev = list_entry(cx88q->active.prev, struct cx23885_buffer,
+ vb.queue);
+ list_add_tail(&buf->vb.queue, &cx88q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = cx88q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ prev->risc.jmp[2] = cpu_to_le32(0); /* 64 bit bits 63-32 */
+ dprintk(1, "[%p/%d] %s - append to active\n",
+ buf, buf->vb.i, __func__);
+ }
+}
+
+/* ----------------------------------------------------------- */
+
+static void do_cancel_buffers(struct cx23885_tsport *port, char *reason,
+ int restart)
+{
+ struct cx23885_dev *dev = port->dev;
+ struct cx23885_dmaqueue *q = &port->mpegq;
+ struct cx23885_buffer *buf;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->slock, flags);
+ while (!list_empty(&q->active)) {
+ buf = list_entry(q->active.next, struct cx23885_buffer,
+ vb.queue);
+ list_del(&buf->vb.queue);
+ buf->vb.state = VIDEOBUF_ERROR;
+ wake_up(&buf->vb.done);
+ dprintk(1, "[%p/%d] %s - dma=0x%08lx\n",
+ buf, buf->vb.i, reason, (unsigned long)buf->risc.dma);
+ }
+ if (restart) {
+ dprintk(1, "restarting queue\n");
+ cx23885_restart_queue(port, q);
+ }
+ spin_unlock_irqrestore(&port->slock, flags);
+}
+
+void cx23885_cancel_buffers(struct cx23885_tsport *port)
+{
+ struct cx23885_dev *dev = port->dev;
+ struct cx23885_dmaqueue *q = &port->mpegq;
+
+ dprintk(1, "%s()\n", __func__);
+ del_timer_sync(&q->timeout);
+ cx23885_stop_dma(port);
+ do_cancel_buffers(port, "cancel", 0);
+}
+
+static void cx23885_timeout(unsigned long data)
+{
+ struct cx23885_tsport *port = (struct cx23885_tsport *)data;
+ struct cx23885_dev *dev = port->dev;
+
+ dprintk(1, "%s()\n", __func__);
+
+ if (debug > 5)
+ cx23885_sram_channel_dump(dev,
+ &dev->sram_channels[port->sram_chno]);
+
+ cx23885_stop_dma(port);
+ do_cancel_buffers(port, "timeout", 1);
+}
+
+int cx23885_irq_417(struct cx23885_dev *dev, u32 status)
+{
+ /* FIXME: port1 assumption here. */
+ struct cx23885_tsport *port = &dev->ts1;
+ int count = 0;
+ int handled = 0;
+
+ if (status == 0)
+ return handled;
+
+ count = cx_read(port->reg_gpcnt);
+ dprintk(7, "status: 0x%08x mask: 0x%08x count: 0x%x\n",
+ status, cx_read(port->reg_ts_int_msk), count);
+
+ if ((status & VID_B_MSK_BAD_PKT) ||
+ (status & VID_B_MSK_OPC_ERR) ||
+ (status & VID_B_MSK_VBI_OPC_ERR) ||
+ (status & VID_B_MSK_SYNC) ||
+ (status & VID_B_MSK_VBI_SYNC) ||
+ (status & VID_B_MSK_OF) ||
+ (status & VID_B_MSK_VBI_OF)) {
+ printk(KERN_ERR "%s: V4L mpeg risc op code error, status "
+ "= 0x%x\n", dev->name, status);
+ if (status & VID_B_MSK_BAD_PKT)
+ dprintk(1, " VID_B_MSK_BAD_PKT\n");
+ if (status & VID_B_MSK_OPC_ERR)
+ dprintk(1, " VID_B_MSK_OPC_ERR\n");
+ if (status & VID_B_MSK_VBI_OPC_ERR)
+ dprintk(1, " VID_B_MSK_VBI_OPC_ERR\n");
+ if (status & VID_B_MSK_SYNC)
+ dprintk(1, " VID_B_MSK_SYNC\n");
+ if (status & VID_B_MSK_VBI_SYNC)
+ dprintk(1, " VID_B_MSK_VBI_SYNC\n");
+ if (status & VID_B_MSK_OF)
+ dprintk(1, " VID_B_MSK_OF\n");
+ if (status & VID_B_MSK_VBI_OF)
+ dprintk(1, " VID_B_MSK_VBI_OF\n");
+
+ cx_clear(port->reg_dma_ctl, port->dma_ctl_val);
+ cx23885_sram_channel_dump(dev,
+ &dev->sram_channels[port->sram_chno]);
+ cx23885_417_check_encoder(dev);
+ } else if (status & VID_B_MSK_RISCI1) {
+ dprintk(7, " VID_B_MSK_RISCI1\n");
+ spin_lock(&port->slock);
+ cx23885_wakeup(port, &port->mpegq, count);
+ spin_unlock(&port->slock);
+ } else if (status & VID_B_MSK_RISCI2) {
+ dprintk(7, " VID_B_MSK_RISCI2\n");
+ spin_lock(&port->slock);
+ cx23885_restart_queue(port, &port->mpegq);
+ spin_unlock(&port->slock);
+ }
+ if (status) {
+ cx_write(port->reg_ts_int_stat, status);
+ handled = 1;
+ }
+
+ return handled;
+}
+
+static int cx23885_irq_ts(struct cx23885_tsport *port, u32 status)
+{
+ struct cx23885_dev *dev = port->dev;
+ int handled = 0;
+ u32 count;
+
+ if ((status & VID_BC_MSK_OPC_ERR) ||
+ (status & VID_BC_MSK_BAD_PKT) ||
+ (status & VID_BC_MSK_SYNC) ||
+ (status & VID_BC_MSK_OF)) {
+
+ if (status & VID_BC_MSK_OPC_ERR)
+ dprintk(7, " (VID_BC_MSK_OPC_ERR 0x%08x)\n",
+ VID_BC_MSK_OPC_ERR);
+
+ if (status & VID_BC_MSK_BAD_PKT)
+ dprintk(7, " (VID_BC_MSK_BAD_PKT 0x%08x)\n",
+ VID_BC_MSK_BAD_PKT);
+
+ if (status & VID_BC_MSK_SYNC)
+ dprintk(7, " (VID_BC_MSK_SYNC 0x%08x)\n",
+ VID_BC_MSK_SYNC);
+
+ if (status & VID_BC_MSK_OF)
+ dprintk(7, " (VID_BC_MSK_OF 0x%08x)\n",
+ VID_BC_MSK_OF);
+
+ printk(KERN_ERR "%s: mpeg risc op code error\n", dev->name);
+
+ cx_clear(port->reg_dma_ctl, port->dma_ctl_val);
+ cx23885_sram_channel_dump(dev,
+ &dev->sram_channels[port->sram_chno]);
+
+ } else if (status & VID_BC_MSK_RISCI1) {
+
+ dprintk(7, " (RISCI1 0x%08x)\n", VID_BC_MSK_RISCI1);
+
+ spin_lock(&port->slock);
+ count = cx_read(port->reg_gpcnt);
+ cx23885_wakeup(port, &port->mpegq, count);
+ spin_unlock(&port->slock);
+
+ } else if (status & VID_BC_MSK_RISCI2) {
+
+ dprintk(7, " (RISCI2 0x%08x)\n", VID_BC_MSK_RISCI2);
+
+ spin_lock(&port->slock);
+ cx23885_restart_queue(port, &port->mpegq);
+ spin_unlock(&port->slock);
+
+ }
+ if (status) {
+ cx_write(port->reg_ts_int_stat, status);
+ handled = 1;
+ }
+
+ return handled;
+}
+
+static irqreturn_t cx23885_irq(int irq, void *dev_id)
+{
+ struct cx23885_dev *dev = dev_id;
+ struct cx23885_tsport *ts1 = &dev->ts1;
+ struct cx23885_tsport *ts2 = &dev->ts2;
+ u32 pci_status, pci_mask;
+ u32 vida_status, vida_mask;
+ u32 audint_status, audint_mask;
+ u32 ts1_status, ts1_mask;
+ u32 ts2_status, ts2_mask;
+ int vida_count = 0, ts1_count = 0, ts2_count = 0, handled = 0;
+ int audint_count = 0;
+ bool subdev_handled;
+
+ pci_status = cx_read(PCI_INT_STAT);
+ pci_mask = cx23885_irq_get_mask(dev);
+ vida_status = cx_read(VID_A_INT_STAT);
+ vida_mask = cx_read(VID_A_INT_MSK);
+ audint_status = cx_read(AUDIO_INT_INT_STAT);
+ audint_mask = cx_read(AUDIO_INT_INT_MSK);
+ ts1_status = cx_read(VID_B_INT_STAT);
+ ts1_mask = cx_read(VID_B_INT_MSK);
+ ts2_status = cx_read(VID_C_INT_STAT);
+ ts2_mask = cx_read(VID_C_INT_MSK);
+
+ if ((pci_status == 0) && (ts2_status == 0) && (ts1_status == 0))
+ goto out;
+
+ vida_count = cx_read(VID_A_GPCNT);
+ audint_count = cx_read(AUD_INT_A_GPCNT);
+ ts1_count = cx_read(ts1->reg_gpcnt);
+ ts2_count = cx_read(ts2->reg_gpcnt);
+ dprintk(7, "pci_status: 0x%08x pci_mask: 0x%08x\n",
+ pci_status, pci_mask);
+ dprintk(7, "vida_status: 0x%08x vida_mask: 0x%08x count: 0x%x\n",
+ vida_status, vida_mask, vida_count);
+ dprintk(7, "audint_status: 0x%08x audint_mask: 0x%08x count: 0x%x\n",
+ audint_status, audint_mask, audint_count);
+ dprintk(7, "ts1_status: 0x%08x ts1_mask: 0x%08x count: 0x%x\n",
+ ts1_status, ts1_mask, ts1_count);
+ dprintk(7, "ts2_status: 0x%08x ts2_mask: 0x%08x count: 0x%x\n",
+ ts2_status, ts2_mask, ts2_count);
+
+ if (pci_status & (PCI_MSK_RISC_RD | PCI_MSK_RISC_WR |
+ PCI_MSK_AL_RD | PCI_MSK_AL_WR | PCI_MSK_APB_DMA |
+ PCI_MSK_VID_C | PCI_MSK_VID_B | PCI_MSK_VID_A |
+ PCI_MSK_AUD_INT | PCI_MSK_AUD_EXT |
+ PCI_MSK_GPIO0 | PCI_MSK_GPIO1 |
+ PCI_MSK_AV_CORE | PCI_MSK_IR)) {
+
+ if (pci_status & PCI_MSK_RISC_RD)
+ dprintk(7, " (PCI_MSK_RISC_RD 0x%08x)\n",
+ PCI_MSK_RISC_RD);
+
+ if (pci_status & PCI_MSK_RISC_WR)
+ dprintk(7, " (PCI_MSK_RISC_WR 0x%08x)\n",
+ PCI_MSK_RISC_WR);
+
+ if (pci_status & PCI_MSK_AL_RD)
+ dprintk(7, " (PCI_MSK_AL_RD 0x%08x)\n",
+ PCI_MSK_AL_RD);
+
+ if (pci_status & PCI_MSK_AL_WR)
+ dprintk(7, " (PCI_MSK_AL_WR 0x%08x)\n",
+ PCI_MSK_AL_WR);
+
+ if (pci_status & PCI_MSK_APB_DMA)
+ dprintk(7, " (PCI_MSK_APB_DMA 0x%08x)\n",
+ PCI_MSK_APB_DMA);
+
+ if (pci_status & PCI_MSK_VID_C)
+ dprintk(7, " (PCI_MSK_VID_C 0x%08x)\n",
+ PCI_MSK_VID_C);
+
+ if (pci_status & PCI_MSK_VID_B)
+ dprintk(7, " (PCI_MSK_VID_B 0x%08x)\n",
+ PCI_MSK_VID_B);
+
+ if (pci_status & PCI_MSK_VID_A)
+ dprintk(7, " (PCI_MSK_VID_A 0x%08x)\n",
+ PCI_MSK_VID_A);
+
+ if (pci_status & PCI_MSK_AUD_INT)
+ dprintk(7, " (PCI_MSK_AUD_INT 0x%08x)\n",
+ PCI_MSK_AUD_INT);
+
+ if (pci_status & PCI_MSK_AUD_EXT)
+ dprintk(7, " (PCI_MSK_AUD_EXT 0x%08x)\n",
+ PCI_MSK_AUD_EXT);
+
+ if (pci_status & PCI_MSK_GPIO0)
+ dprintk(7, " (PCI_MSK_GPIO0 0x%08x)\n",
+ PCI_MSK_GPIO0);
+
+ if (pci_status & PCI_MSK_GPIO1)
+ dprintk(7, " (PCI_MSK_GPIO1 0x%08x)\n",
+ PCI_MSK_GPIO1);
+
+ if (pci_status & PCI_MSK_AV_CORE)
+ dprintk(7, " (PCI_MSK_AV_CORE 0x%08x)\n",
+ PCI_MSK_AV_CORE);
+
+ if (pci_status & PCI_MSK_IR)
+ dprintk(7, " (PCI_MSK_IR 0x%08x)\n",
+ PCI_MSK_IR);
+ }
+
+ if (cx23885_boards[dev->board].ci_type == 1 &&
+ (pci_status & (PCI_MSK_GPIO1 | PCI_MSK_GPIO0)))
+ handled += netup_ci_slot_status(dev, pci_status);
+
+ if (cx23885_boards[dev->board].ci_type == 2 &&
+ (pci_status & PCI_MSK_GPIO0))
+ handled += altera_ci_irq(dev);
+
+ if (ts1_status) {
+ if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB)
+ handled += cx23885_irq_ts(ts1, ts1_status);
+ else
+ if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER)
+ handled += cx23885_irq_417(dev, ts1_status);
+ }
+
+ if (ts2_status) {
+ if (cx23885_boards[dev->board].portc == CX23885_MPEG_DVB)
+ handled += cx23885_irq_ts(ts2, ts2_status);
+ else
+ if (cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER)
+ handled += cx23885_irq_417(dev, ts2_status);
+ }
+
+ if (vida_status)
+ handled += cx23885_video_irq(dev, vida_status);
+
+ if (audint_status)
+ handled += cx23885_audio_irq(dev, audint_status, audint_mask);
+
+ if (pci_status & PCI_MSK_IR) {
+ subdev_handled = false;
+ v4l2_subdev_call(dev->sd_ir, core, interrupt_service_routine,
+ pci_status, &subdev_handled);
+ if (subdev_handled)
+ handled++;
+ }
+
+ if ((pci_status & pci_mask) & PCI_MSK_AV_CORE) {
+ cx23885_irq_disable(dev, PCI_MSK_AV_CORE);
+ if (!schedule_work(&dev->cx25840_work))
+ printk(KERN_ERR "%s: failed to set up deferred work for"
+ " AV Core/IR interrupt. Interrupt is disabled"
+ " and won't be re-enabled\n", dev->name);
+ handled++;
+ }
+
+ if (handled)
+ cx_write(PCI_INT_STAT, pci_status);
+out:
+ return IRQ_RETVAL(handled);
+}
+
+static void cx23885_v4l2_dev_notify(struct v4l2_subdev *sd,
+ unsigned int notification, void *arg)
+{
+ struct cx23885_dev *dev;
+
+ if (sd == NULL)
+ return;
+
+ dev = to_cx23885(sd->v4l2_dev);
+
+ switch (notification) {
+ case V4L2_SUBDEV_IR_RX_NOTIFY: /* Possibly called in an IRQ context */
+ if (sd == dev->sd_ir)
+ cx23885_ir_rx_v4l2_dev_notify(sd, *(u32 *)arg);
+ break;
+ case V4L2_SUBDEV_IR_TX_NOTIFY: /* Possibly called in an IRQ context */
+ if (sd == dev->sd_ir)
+ cx23885_ir_tx_v4l2_dev_notify(sd, *(u32 *)arg);
+ break;
+ }
+}
+
+static void cx23885_v4l2_dev_notify_init(struct cx23885_dev *dev)
+{
+ INIT_WORK(&dev->cx25840_work, cx23885_av_work_handler);
+ INIT_WORK(&dev->ir_rx_work, cx23885_ir_rx_work_handler);
+ INIT_WORK(&dev->ir_tx_work, cx23885_ir_tx_work_handler);
+ dev->v4l2_dev.notify = cx23885_v4l2_dev_notify;
+}
+
+static inline int encoder_on_portb(struct cx23885_dev *dev)
+{
+ return cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER;
+}
+
+static inline int encoder_on_portc(struct cx23885_dev *dev)
+{
+ return cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER;
+}
+
+/* Mask represents 32 different GPIOs, GPIO's are split into multiple
+ * registers depending on the board configuration (and whether the
+ * 417 encoder (wi it's own GPIO's) are present. Each GPIO bit will
+ * be pushed into the correct hardware register, regardless of the
+ * physical location. Certain registers are shared so we sanity check
+ * and report errors if we think we're tampering with a GPIo that might
+ * be assigned to the encoder (and used for the host bus).
+ *
+ * GPIO 2 thru 0 - On the cx23885 bridge
+ * GPIO 18 thru 3 - On the cx23417 host bus interface
+ * GPIO 23 thru 19 - On the cx25840 a/v core
+ */
+void cx23885_gpio_set(struct cx23885_dev *dev, u32 mask)
+{
+ if (mask & 0x7)
+ cx_set(GP0_IO, mask & 0x7);
+
+ if (mask & 0x0007fff8) {
+ if (encoder_on_portb(dev) || encoder_on_portc(dev))
+ printk(KERN_ERR
+ "%s: Setting GPIO on encoder ports\n",
+ dev->name);
+ cx_set(MC417_RWD, (mask & 0x0007fff8) >> 3);
+ }
+
+ /* TODO: 23-19 */
+ if (mask & 0x00f80000)
+ printk(KERN_INFO "%s: Unsupported\n", dev->name);
+}
+
+void cx23885_gpio_clear(struct cx23885_dev *dev, u32 mask)
+{
+ if (mask & 0x00000007)
+ cx_clear(GP0_IO, mask & 0x7);
+
+ if (mask & 0x0007fff8) {
+ if (encoder_on_portb(dev) || encoder_on_portc(dev))
+ printk(KERN_ERR
+ "%s: Clearing GPIO moving on encoder ports\n",
+ dev->name);
+ cx_clear(MC417_RWD, (mask & 0x7fff8) >> 3);
+ }
+
+ /* TODO: 23-19 */
+ if (mask & 0x00f80000)
+ printk(KERN_INFO "%s: Unsupported\n", dev->name);
+}
+
+u32 cx23885_gpio_get(struct cx23885_dev *dev, u32 mask)
+{
+ if (mask & 0x00000007)
+ return (cx_read(GP0_IO) >> 8) & mask & 0x7;
+
+ if (mask & 0x0007fff8) {
+ if (encoder_on_portb(dev) || encoder_on_portc(dev))
+ printk(KERN_ERR
+ "%s: Reading GPIO moving on encoder ports\n",
+ dev->name);
+ return (cx_read(MC417_RWD) & ((mask & 0x7fff8) >> 3)) << 3;
+ }
+
+ /* TODO: 23-19 */
+ if (mask & 0x00f80000)
+ printk(KERN_INFO "%s: Unsupported\n", dev->name);
+
+ return 0;
+}
+
+void cx23885_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput)
+{
+ if ((mask & 0x00000007) && asoutput)
+ cx_set(GP0_IO, (mask & 0x7) << 16);
+ else if ((mask & 0x00000007) && !asoutput)
+ cx_clear(GP0_IO, (mask & 0x7) << 16);
+
+ if (mask & 0x0007fff8) {
+ if (encoder_on_portb(dev) || encoder_on_portc(dev))
+ printk(KERN_ERR
+ "%s: Enabling GPIO on encoder ports\n",
+ dev->name);
+ }
+
+ /* MC417_OEN is active low for output, write 1 for an input */
+ if ((mask & 0x0007fff8) && asoutput)
+ cx_clear(MC417_OEN, (mask & 0x7fff8) >> 3);
+
+ else if ((mask & 0x0007fff8) && !asoutput)
+ cx_set(MC417_OEN, (mask & 0x7fff8) >> 3);
+
+ /* TODO: 23-19 */
+}
+
+static int __devinit cx23885_initdev(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_id)
+{
+ struct cx23885_dev *dev;
+ int err;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (NULL == dev)
+ return -ENOMEM;
+
+ err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev);
+ if (err < 0)
+ goto fail_free;
+
+ /* Prepare to handle notifications from subdevices */
+ cx23885_v4l2_dev_notify_init(dev);
+
+ /* pci init */
+ dev->pci = pci_dev;
+ if (pci_enable_device(pci_dev)) {
+ err = -EIO;
+ goto fail_unreg;
+ }
+
+ if (cx23885_dev_setup(dev) < 0) {
+ err = -EINVAL;
+ goto fail_unreg;
+ }
+
+ /* print pci info */
+ dev->pci_rev = pci_dev->revision;
+ pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat);
+ printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, "
+ "latency: %d, mmio: 0x%llx\n", dev->name,
+ pci_name(pci_dev), dev->pci_rev, pci_dev->irq,
+ dev->pci_lat,
+ (unsigned long long)pci_resource_start(pci_dev, 0));
+
+ pci_set_master(pci_dev);
+ if (!pci_dma_supported(pci_dev, 0xffffffff)) {
+ printk("%s/0: Oops: no 32bit PCI DMA ???\n", dev->name);
+ err = -EIO;
+ goto fail_irq;
+ }
+
+ err = request_irq(pci_dev->irq, cx23885_irq,
+ IRQF_SHARED | IRQF_DISABLED, dev->name, dev);
+ if (err < 0) {
+ printk(KERN_ERR "%s: can't get IRQ %d\n",
+ dev->name, pci_dev->irq);
+ goto fail_irq;
+ }
+
+ switch (dev->board) {
+ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
+ cx23885_irq_add_enable(dev, PCI_MSK_GPIO1 | PCI_MSK_GPIO0);
+ break;
+ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
+ cx23885_irq_add_enable(dev, PCI_MSK_GPIO0);
+ break;
+ }
+
+ /*
+ * The CX2388[58] IR controller can start firing interrupts when
+ * enabled, so these have to take place after the cx23885_irq() handler
+ * is hooked up by the call to request_irq() above.
+ */
+ cx23885_ir_pci_int_enable(dev);
+ cx23885_input_init(dev);
+
+ return 0;
+
+fail_irq:
+ cx23885_dev_unregister(dev);
+fail_unreg:
+ v4l2_device_unregister(&dev->v4l2_dev);
+fail_free:
+ kfree(dev);
+ return err;
+}
+
+static void __devexit cx23885_finidev(struct pci_dev *pci_dev)
+{
+ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+ struct cx23885_dev *dev = to_cx23885(v4l2_dev);
+
+ cx23885_input_fini(dev);
+ cx23885_ir_fini(dev);
+
+ cx23885_shutdown(dev);
+
+ pci_disable_device(pci_dev);
+
+ /* unregister stuff */
+ free_irq(pci_dev->irq, dev);
+
+ cx23885_dev_unregister(dev);
+ v4l2_device_unregister(v4l2_dev);
+ kfree(dev);
+}
+
+static struct pci_device_id cx23885_pci_tbl[] = {
+ {
+ /* CX23885 */
+ .vendor = 0x14f1,
+ .device = 0x8852,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ }, {
+ /* CX23887 Rev 2 */
+ .vendor = 0x14f1,
+ .device = 0x8880,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ }, {
+ /* --- end of list --- */
+ }
+};
+MODULE_DEVICE_TABLE(pci, cx23885_pci_tbl);
+
+static struct pci_driver cx23885_pci_driver = {
+ .name = "cx23885",
+ .id_table = cx23885_pci_tbl,
+ .probe = cx23885_initdev,
+ .remove = __devexit_p(cx23885_finidev),
+ /* TODO */
+ .suspend = NULL,
+ .resume = NULL,
+};
+
+static int __init cx23885_init(void)
+{
+ printk(KERN_INFO "cx23885 driver version %s loaded\n",
+ CX23885_VERSION);
+ return pci_register_driver(&cx23885_pci_driver);
+}
+
+static void __exit cx23885_fini(void)
+{
+ pci_unregister_driver(&cx23885_pci_driver);
+}
+
+module_init(cx23885_init);
+module_exit(cx23885_fini);
diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c
new file mode 100644
index 00000000..6835eb1f
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-dvb.c
@@ -0,0 +1,1344 @@
+/*
+ * Driver for the Conexant CX23885 PCIe bridge
+ *
+ * Copyright (c) 2006 Steven Toth <stoth@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/kthread.h>
+#include <linux/file.h>
+#include <linux/suspend.h>
+
+#include "cx23885.h"
+#include <media/v4l2-common.h>
+
+#include "dvb_ca_en50221.h"
+#include "s5h1409.h"
+#include "s5h1411.h"
+#include "mt2131.h"
+#include "tda8290.h"
+#include "tda18271.h"
+#include "lgdt330x.h"
+#include "xc4000.h"
+#include "xc5000.h"
+#include "max2165.h"
+#include "tda10048.h"
+#include "tuner-xc2028.h"
+#include "tuner-simple.h"
+#include "dib7000p.h"
+#include "dibx000_common.h"
+#include "zl10353.h"
+#include "stv0900.h"
+#include "stv0900_reg.h"
+#include "stv6110.h"
+#include "lnbh24.h"
+#include "cx24116.h"
+#include "cimax2.h"
+#include "lgs8gxx.h"
+#include "netup-eeprom.h"
+#include "netup-init.h"
+#include "lgdt3305.h"
+#include "atbm8830.h"
+#include "ds3000.h"
+#include "cx23885-f300.h"
+#include "altera-ci.h"
+#include "stv0367.h"
+#include "drxk.h"
+#include "mt2063.h"
+
+static unsigned int debug;
+
+#define dprintk(level, fmt, arg...)\
+ do { if (debug >= level)\
+ printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\
+ } while (0)
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int alt_tuner;
+module_param(alt_tuner, int, 0644);
+MODULE_PARM_DESC(alt_tuner, "Enable alternate tuner configuration");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+/* ------------------------------------------------------------------ */
+
+static int dvb_buf_setup(struct videobuf_queue *q,
+ unsigned int *count, unsigned int *size)
+{
+ struct cx23885_tsport *port = q->priv_data;
+
+ port->ts_packet_size = 188 * 4;
+ port->ts_packet_count = 32;
+
+ *size = port->ts_packet_size * port->ts_packet_count;
+ *count = 32;
+ return 0;
+}
+
+static int dvb_buf_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb, enum v4l2_field field)
+{
+ struct cx23885_tsport *port = q->priv_data;
+ return cx23885_buf_prepare(q, port, (struct cx23885_buffer *)vb, field);
+}
+
+static void dvb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct cx23885_tsport *port = q->priv_data;
+ cx23885_buf_queue(port, (struct cx23885_buffer *)vb);
+}
+
+static void dvb_buf_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ cx23885_free_buffer(q, (struct cx23885_buffer *)vb);
+}
+
+static int cx23885_dvb_set_frontend(struct dvb_frontend *fe);
+
+static void cx23885_dvb_gate_ctrl(struct cx23885_tsport *port, int open)
+{
+ struct videobuf_dvb_frontends *f;
+ struct videobuf_dvb_frontend *fe;
+
+ f = &port->frontends;
+
+ if (f->gate <= 1) /* undefined or fe0 */
+ fe = videobuf_dvb_get_frontend(f, 1);
+ else
+ fe = videobuf_dvb_get_frontend(f, f->gate);
+
+ if (fe && fe->dvb.frontend && fe->dvb.frontend->ops.i2c_gate_ctrl)
+ fe->dvb.frontend->ops.i2c_gate_ctrl(fe->dvb.frontend, open);
+
+ /*
+ * FIXME: Improve this path to avoid calling the
+ * cx23885_dvb_set_frontend() every time it passes here.
+ */
+ cx23885_dvb_set_frontend(fe->dvb.frontend);
+}
+
+static struct videobuf_queue_ops dvb_qops = {
+ .buf_setup = dvb_buf_setup,
+ .buf_prepare = dvb_buf_prepare,
+ .buf_queue = dvb_buf_queue,
+ .buf_release = dvb_buf_release,
+};
+
+static struct s5h1409_config hauppauge_generic_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_SERIAL_OUTPUT,
+ .gpio = S5H1409_GPIO_ON,
+ .qam_if = 44000,
+ .inversion = S5H1409_INVERSION_OFF,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct tda10048_config hauppauge_hvr1200_config = {
+ .demod_address = 0x10 >> 1,
+ .output_mode = TDA10048_SERIAL_OUTPUT,
+ .fwbulkwritelen = TDA10048_BULKWRITE_200,
+ .inversion = TDA10048_INVERSION_ON,
+ .dtv6_if_freq_khz = TDA10048_IF_3300,
+ .dtv7_if_freq_khz = TDA10048_IF_3800,
+ .dtv8_if_freq_khz = TDA10048_IF_4300,
+ .clk_freq_khz = TDA10048_CLK_16000,
+};
+
+static struct tda10048_config hauppauge_hvr1210_config = {
+ .demod_address = 0x10 >> 1,
+ .output_mode = TDA10048_SERIAL_OUTPUT,
+ .fwbulkwritelen = TDA10048_BULKWRITE_200,
+ .inversion = TDA10048_INVERSION_ON,
+ .dtv6_if_freq_khz = TDA10048_IF_3300,
+ .dtv7_if_freq_khz = TDA10048_IF_3500,
+ .dtv8_if_freq_khz = TDA10048_IF_4000,
+ .clk_freq_khz = TDA10048_CLK_16000,
+};
+
+static struct s5h1409_config hauppauge_ezqam_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_SERIAL_OUTPUT,
+ .gpio = S5H1409_GPIO_OFF,
+ .qam_if = 4000,
+ .inversion = S5H1409_INVERSION_ON,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct s5h1409_config hauppauge_hvr1800lp_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_SERIAL_OUTPUT,
+ .gpio = S5H1409_GPIO_OFF,
+ .qam_if = 44000,
+ .inversion = S5H1409_INVERSION_OFF,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct s5h1409_config hauppauge_hvr1500_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_SERIAL_OUTPUT,
+ .gpio = S5H1409_GPIO_OFF,
+ .inversion = S5H1409_INVERSION_OFF,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct mt2131_config hauppauge_generic_tunerconfig = {
+ 0x61
+};
+
+static struct lgdt330x_config fusionhdtv_5_express = {
+ .demod_address = 0x0e,
+ .demod_chip = LGDT3303,
+ .serial_mpeg = 0x40,
+};
+
+static struct s5h1409_config hauppauge_hvr1500q_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_SERIAL_OUTPUT,
+ .gpio = S5H1409_GPIO_ON,
+ .qam_if = 44000,
+ .inversion = S5H1409_INVERSION_OFF,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct s5h1409_config dvico_s5h1409_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_SERIAL_OUTPUT,
+ .gpio = S5H1409_GPIO_ON,
+ .qam_if = 44000,
+ .inversion = S5H1409_INVERSION_OFF,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct s5h1411_config dvico_s5h1411_config = {
+ .output_mode = S5H1411_SERIAL_OUTPUT,
+ .gpio = S5H1411_GPIO_ON,
+ .qam_if = S5H1411_IF_44000,
+ .vsb_if = S5H1411_IF_44000,
+ .inversion = S5H1411_INVERSION_OFF,
+ .status_mode = S5H1411_DEMODLOCKING,
+ .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct s5h1411_config hcw_s5h1411_config = {
+ .output_mode = S5H1411_SERIAL_OUTPUT,
+ .gpio = S5H1411_GPIO_OFF,
+ .vsb_if = S5H1411_IF_44000,
+ .qam_if = S5H1411_IF_4000,
+ .inversion = S5H1411_INVERSION_ON,
+ .status_mode = S5H1411_DEMODLOCKING,
+ .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct xc5000_config hauppauge_hvr1500q_tunerconfig = {
+ .i2c_address = 0x61,
+ .if_khz = 5380,
+};
+
+static struct xc5000_config dvico_xc5000_tunerconfig = {
+ .i2c_address = 0x64,
+ .if_khz = 5380,
+};
+
+static struct tda829x_config tda829x_no_probe = {
+ .probe_tuner = TDA829X_DONT_PROBE,
+};
+
+static struct tda18271_std_map hauppauge_tda18271_std_map = {
+ .atsc_6 = { .if_freq = 5380, .agc_mode = 3, .std = 3,
+ .if_lvl = 6, .rfagc_top = 0x37 },
+ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0,
+ .if_lvl = 6, .rfagc_top = 0x37 },
+};
+
+static struct tda18271_std_map hauppauge_hvr1200_tda18271_std_map = {
+ .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+ .dvbt_7 = { .if_freq = 3800, .agc_mode = 3, .std = 5,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+ .dvbt_8 = { .if_freq = 4300, .agc_mode = 3, .std = 6,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+};
+
+static struct tda18271_config hauppauge_tda18271_config = {
+ .std_map = &hauppauge_tda18271_std_map,
+ .gate = TDA18271_GATE_ANALOG,
+ .output_opt = TDA18271_OUTPUT_LT_OFF,
+};
+
+static struct tda18271_config hauppauge_hvr1200_tuner_config = {
+ .std_map = &hauppauge_hvr1200_tda18271_std_map,
+ .gate = TDA18271_GATE_ANALOG,
+ .output_opt = TDA18271_OUTPUT_LT_OFF,
+};
+
+static struct tda18271_config hauppauge_hvr1210_tuner_config = {
+ .gate = TDA18271_GATE_DIGITAL,
+ .output_opt = TDA18271_OUTPUT_LT_OFF,
+};
+
+static struct tda18271_std_map hauppauge_hvr127x_std_map = {
+ .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 4,
+ .if_lvl = 1, .rfagc_top = 0x58 },
+ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 5,
+ .if_lvl = 1, .rfagc_top = 0x58 },
+};
+
+static struct tda18271_config hauppauge_hvr127x_config = {
+ .std_map = &hauppauge_hvr127x_std_map,
+ .output_opt = TDA18271_OUTPUT_LT_OFF,
+};
+
+static struct lgdt3305_config hauppauge_lgdt3305_config = {
+ .i2c_addr = 0x0e,
+ .mpeg_mode = LGDT3305_MPEG_SERIAL,
+ .tpclk_edge = LGDT3305_TPCLK_FALLING_EDGE,
+ .tpvalid_polarity = LGDT3305_TP_VALID_HIGH,
+ .deny_i2c_rptr = 1,
+ .spectral_inversion = 1,
+ .qam_if_khz = 4000,
+ .vsb_if_khz = 3250,
+};
+
+static struct dibx000_agc_config xc3028_agc_config = {
+ BAND_VHF | BAND_UHF, /* band_caps */
+
+ /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=0,
+ * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0,
+ * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0,
+ * P_agc_nb_est=2, P_agc_write=0
+ */
+ (0 << 15) | (0 << 14) | (0 << 11) | (0 << 10) | (0 << 9) | (0 << 8) |
+ (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), /* setup */
+
+ 712, /* inv_gain */
+ 21, /* time_stabiliz */
+
+ 0, /* alpha_level */
+ 118, /* thlock */
+
+ 0, /* wbd_inv */
+ 2867, /* wbd_ref */
+ 0, /* wbd_sel */
+ 2, /* wbd_alpha */
+
+ 0, /* agc1_max */
+ 0, /* agc1_min */
+ 39718, /* agc2_max */
+ 9930, /* agc2_min */
+ 0, /* agc1_pt1 */
+ 0, /* agc1_pt2 */
+ 0, /* agc1_pt3 */
+ 0, /* agc1_slope1 */
+ 0, /* agc1_slope2 */
+ 0, /* agc2_pt1 */
+ 128, /* agc2_pt2 */
+ 29, /* agc2_slope1 */
+ 29, /* agc2_slope2 */
+
+ 17, /* alpha_mant */
+ 27, /* alpha_exp */
+ 23, /* beta_mant */
+ 51, /* beta_exp */
+
+ 1, /* perform_agc_softsplit */
+};
+
+/* PLL Configuration for COFDM BW_MHz = 8.000000
+ * With external clock = 30.000000 */
+static struct dibx000_bandwidth_config xc3028_bw_config = {
+ 60000, /* internal */
+ 30000, /* sampling */
+ 1, /* pll_cfg: prediv */
+ 8, /* pll_cfg: ratio */
+ 3, /* pll_cfg: range */
+ 1, /* pll_cfg: reset */
+ 0, /* pll_cfg: bypass */
+ 0, /* misc: refdiv */
+ 0, /* misc: bypclk_div */
+ 1, /* misc: IO_CLK_en_core */
+ 1, /* misc: ADClkSrc */
+ 0, /* misc: modulo */
+ (3 << 14) | (1 << 12) | (524 << 0), /* sad_cfg: refsel, sel, freq_15k */
+ (1 << 25) | 5816102, /* ifreq = 5.200000 MHz */
+ 20452225, /* timf */
+ 30000000 /* xtal_hz */
+};
+
+static struct dib7000p_config hauppauge_hvr1400_dib7000_config = {
+ .output_mpeg2_in_188_bytes = 1,
+ .hostbus_diversity = 1,
+ .tuner_is_baseband = 0,
+ .update_lna = NULL,
+
+ .agc_config_count = 1,
+ .agc = &xc3028_agc_config,
+ .bw = &xc3028_bw_config,
+
+ .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS,
+ .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES,
+ .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS,
+
+ .pwm_freq_div = 0,
+ .agc_control = NULL,
+ .spur_protect = 0,
+
+ .output_mode = OUTMODE_MPEG2_SERIAL,
+};
+
+static struct zl10353_config dvico_fusionhdtv_xc3028 = {
+ .demod_address = 0x0f,
+ .if2 = 45600,
+ .no_tuner = 1,
+ .disable_i2c_gate_ctrl = 1,
+};
+
+static struct stv0900_reg stv0900_ts_regs[] = {
+ { R0900_TSGENERAL, 0x00 },
+ { R0900_P1_TSSPEED, 0x40 },
+ { R0900_P2_TSSPEED, 0x40 },
+ { R0900_P1_TSCFGM, 0xc0 },
+ { R0900_P2_TSCFGM, 0xc0 },
+ { R0900_P1_TSCFGH, 0xe0 },
+ { R0900_P2_TSCFGH, 0xe0 },
+ { R0900_P1_TSCFGL, 0x20 },
+ { R0900_P2_TSCFGL, 0x20 },
+ { 0xffff, 0xff }, /* terminate */
+};
+
+static struct stv0900_config netup_stv0900_config = {
+ .demod_address = 0x68,
+ .demod_mode = 1, /* dual */
+ .xtal = 8000000,
+ .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */
+ .diseqc_mode = 2,/* 2/3 PWM */
+ .ts_config_regs = stv0900_ts_regs,
+ .tun1_maddress = 0,/* 0x60 */
+ .tun2_maddress = 3,/* 0x63 */
+ .tun1_adc = 1,/* 1 Vpp */
+ .tun2_adc = 1,/* 1 Vpp */
+};
+
+static struct stv6110_config netup_stv6110_tunerconfig_a = {
+ .i2c_address = 0x60,
+ .mclk = 16000000,
+ .clk_div = 1,
+ .gain = 8, /* +16 dB - maximum gain */
+};
+
+static struct stv6110_config netup_stv6110_tunerconfig_b = {
+ .i2c_address = 0x63,
+ .mclk = 16000000,
+ .clk_div = 1,
+ .gain = 8, /* +16 dB - maximum gain */
+};
+
+static struct cx24116_config tbs_cx24116_config = {
+ .demod_address = 0x55,
+};
+
+static struct ds3000_config tevii_ds3000_config = {
+ .demod_address = 0x68,
+};
+
+static struct cx24116_config dvbworld_cx24116_config = {
+ .demod_address = 0x05,
+};
+
+static struct lgs8gxx_config mygica_x8506_lgs8gl5_config = {
+ .prod = LGS8GXX_PROD_LGS8GL5,
+ .demod_address = 0x19,
+ .serial_ts = 0,
+ .ts_clk_pol = 1,
+ .ts_clk_gated = 1,
+ .if_clk_freq = 30400, /* 30.4 MHz */
+ .if_freq = 5380, /* 5.38 MHz */
+ .if_neg_center = 1,
+ .ext_adc = 0,
+ .adc_signed = 0,
+ .if_neg_edge = 0,
+};
+
+static struct xc5000_config mygica_x8506_xc5000_config = {
+ .i2c_address = 0x61,
+ .if_khz = 5380,
+};
+
+static int cx23885_dvb_set_frontend(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct cx23885_tsport *port = fe->dvb->priv;
+ struct cx23885_dev *dev = port->dev;
+
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1275:
+ switch (p->modulation) {
+ case VSB_8:
+ cx23885_gpio_clear(dev, GPIO_5);
+ break;
+ case QAM_64:
+ case QAM_256:
+ default:
+ cx23885_gpio_set(dev, GPIO_5);
+ break;
+ }
+ break;
+ case CX23885_BOARD_MYGICA_X8506:
+ case CX23885_BOARD_MAGICPRO_PROHDTVE2:
+ /* Select Digital TV */
+ cx23885_gpio_set(dev, GPIO_0);
+ break;
+ }
+ return 0;
+}
+
+static struct lgs8gxx_config magicpro_prohdtve2_lgs8g75_config = {
+ .prod = LGS8GXX_PROD_LGS8G75,
+ .demod_address = 0x19,
+ .serial_ts = 0,
+ .ts_clk_pol = 1,
+ .ts_clk_gated = 1,
+ .if_clk_freq = 30400, /* 30.4 MHz */
+ .if_freq = 6500, /* 6.50 MHz */
+ .if_neg_center = 1,
+ .ext_adc = 0,
+ .adc_signed = 1,
+ .adc_vpp = 2, /* 1.6 Vpp */
+ .if_neg_edge = 1,
+};
+
+static struct xc5000_config magicpro_prohdtve2_xc5000_config = {
+ .i2c_address = 0x61,
+ .if_khz = 6500,
+};
+
+static struct atbm8830_config mygica_x8558pro_atbm8830_cfg1 = {
+ .prod = ATBM8830_PROD_8830,
+ .demod_address = 0x44,
+ .serial_ts = 0,
+ .ts_sampling_edge = 1,
+ .ts_clk_gated = 0,
+ .osc_clk_freq = 30400, /* in kHz */
+ .if_freq = 0, /* zero IF */
+ .zif_swap_iq = 1,
+ .agc_min = 0x2E,
+ .agc_max = 0xFF,
+ .agc_hold_loop = 0,
+};
+
+static struct max2165_config mygic_x8558pro_max2165_cfg1 = {
+ .i2c_address = 0x60,
+ .osc_clk = 20
+};
+
+static struct atbm8830_config mygica_x8558pro_atbm8830_cfg2 = {
+ .prod = ATBM8830_PROD_8830,
+ .demod_address = 0x44,
+ .serial_ts = 1,
+ .ts_sampling_edge = 1,
+ .ts_clk_gated = 0,
+ .osc_clk_freq = 30400, /* in kHz */
+ .if_freq = 0, /* zero IF */
+ .zif_swap_iq = 1,
+ .agc_min = 0x2E,
+ .agc_max = 0xFF,
+ .agc_hold_loop = 0,
+};
+
+static struct max2165_config mygic_x8558pro_max2165_cfg2 = {
+ .i2c_address = 0x60,
+ .osc_clk = 20
+};
+static struct stv0367_config netup_stv0367_config[] = {
+ {
+ .demod_address = 0x1c,
+ .xtal = 27000000,
+ .if_khz = 4500,
+ .if_iq_mode = 0,
+ .ts_mode = 1,
+ .clk_pol = 0,
+ }, {
+ .demod_address = 0x1d,
+ .xtal = 27000000,
+ .if_khz = 4500,
+ .if_iq_mode = 0,
+ .ts_mode = 1,
+ .clk_pol = 0,
+ },
+};
+
+static struct xc5000_config netup_xc5000_config[] = {
+ {
+ .i2c_address = 0x61,
+ .if_khz = 4500,
+ }, {
+ .i2c_address = 0x64,
+ .if_khz = 4500,
+ },
+};
+
+static struct drxk_config terratec_drxk_config[] = {
+ {
+ .adr = 0x29,
+ .no_i2c_bridge = 1,
+ }, {
+ .adr = 0x2a,
+ .no_i2c_bridge = 1,
+ },
+};
+
+static struct mt2063_config terratec_mt2063_config[] = {
+ {
+ .tuner_address = 0x60,
+ }, {
+ .tuner_address = 0x67,
+ },
+};
+
+int netup_altera_fpga_rw(void *device, int flag, int data, int read)
+{
+ struct cx23885_dev *dev = (struct cx23885_dev *)device;
+ unsigned long timeout = jiffies + msecs_to_jiffies(1);
+ uint32_t mem = 0;
+
+ mem = cx_read(MC417_RWD);
+ if (read)
+ cx_set(MC417_OEN, ALT_DATA);
+ else {
+ cx_clear(MC417_OEN, ALT_DATA);/* D0-D7 out */
+ mem &= ~ALT_DATA;
+ mem |= (data & ALT_DATA);
+ }
+
+ if (flag)
+ mem |= ALT_AD_RG;
+ else
+ mem &= ~ALT_AD_RG;
+
+ mem &= ~ALT_CS;
+ if (read)
+ mem = (mem & ~ALT_RD) | ALT_WR;
+ else
+ mem = (mem & ~ALT_WR) | ALT_RD;
+
+ cx_write(MC417_RWD, mem); /* start RW cycle */
+
+ for (;;) {
+ mem = cx_read(MC417_RWD);
+ if ((mem & ALT_RDY) == 0)
+ break;
+ if (time_after(jiffies, timeout))
+ break;
+ udelay(1);
+ }
+
+ cx_set(MC417_RWD, ALT_RD | ALT_WR | ALT_CS);
+ if (read)
+ return mem & ALT_DATA;
+
+ return 0;
+};
+
+static int dvb_register(struct cx23885_tsport *port)
+{
+ struct cx23885_dev *dev = port->dev;
+ struct cx23885_i2c *i2c_bus = NULL, *i2c_bus2 = NULL;
+ struct videobuf_dvb_frontend *fe0, *fe1 = NULL;
+ int mfe_shared = 0; /* bus not shared by default */
+ int ret;
+
+ /* Get the first frontend */
+ fe0 = videobuf_dvb_get_frontend(&port->frontends, 1);
+ if (!fe0)
+ return -EINVAL;
+
+ /* init struct videobuf_dvb */
+ fe0->dvb.name = dev->name;
+
+ /* multi-frontend gate control is undefined or defaults to fe0 */
+ port->frontends.gate = 0;
+
+ /* Sets the gate control callback to be used by i2c command calls */
+ port->gate_ctrl = cx23885_dvb_gate_ctrl;
+
+ /* init frontend */
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ i2c_bus = &dev->i2c_bus[0];
+ fe0->dvb.frontend = dvb_attach(s5h1409_attach,
+ &hauppauge_generic_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(mt2131_attach, fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &hauppauge_generic_tunerconfig, 0);
+ }
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
+ case CX23885_BOARD_HAUPPAUGE_HVR1275:
+ i2c_bus = &dev->i2c_bus[0];
+ fe0->dvb.frontend = dvb_attach(lgdt3305_attach,
+ &hauppauge_lgdt3305_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_bus[1].i2c_adap,
+ &hauppauge_hvr127x_config);
+ }
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1255:
+ i2c_bus = &dev->i2c_bus[0];
+ fe0->dvb.frontend = dvb_attach(s5h1411_attach,
+ &hcw_s5h1411_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_bus[1].i2c_adap,
+ &hauppauge_tda18271_config);
+ }
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1800:
+ i2c_bus = &dev->i2c_bus[0];
+ switch (alt_tuner) {
+ case 1:
+ fe0->dvb.frontend =
+ dvb_attach(s5h1409_attach,
+ &hauppauge_ezqam_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(tda829x_attach, fe0->dvb.frontend,
+ &dev->i2c_bus[1].i2c_adap, 0x42,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_bus[1].i2c_adap,
+ &hauppauge_tda18271_config);
+ }
+ break;
+ case 0:
+ default:
+ fe0->dvb.frontend =
+ dvb_attach(s5h1409_attach,
+ &hauppauge_generic_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL)
+ dvb_attach(mt2131_attach, fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &hauppauge_generic_tunerconfig, 0);
+ break;
+ }
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
+ i2c_bus = &dev->i2c_bus[0];
+ fe0->dvb.frontend = dvb_attach(s5h1409_attach,
+ &hauppauge_hvr1800lp_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(mt2131_attach, fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &hauppauge_generic_tunerconfig, 0);
+ }
+ break;
+ case CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP:
+ i2c_bus = &dev->i2c_bus[0];
+ fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
+ &fusionhdtv_5_express,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &i2c_bus->i2c_adap, 0x61,
+ TUNER_LG_TDVS_H06XF);
+ }
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
+ i2c_bus = &dev->i2c_bus[1];
+ fe0->dvb.frontend = dvb_attach(s5h1409_attach,
+ &hauppauge_hvr1500q_config,
+ &dev->i2c_bus[0].i2c_adap);
+ if (fe0->dvb.frontend != NULL)
+ dvb_attach(xc5000_attach, fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &hauppauge_hvr1500q_tunerconfig);
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1500:
+ i2c_bus = &dev->i2c_bus[1];
+ fe0->dvb.frontend = dvb_attach(s5h1409_attach,
+ &hauppauge_hvr1500_config,
+ &dev->i2c_bus[0].i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ struct dvb_frontend *fe;
+ struct xc2028_config cfg = {
+ .i2c_adap = &i2c_bus->i2c_adap,
+ .i2c_addr = 0x61,
+ };
+ static struct xc2028_ctrl ctl = {
+ .fname = XC2028_DEFAULT_FIRMWARE,
+ .max_len = 64,
+ .demod = XC3028_FE_OREN538,
+ };
+
+ fe = dvb_attach(xc2028_attach,
+ fe0->dvb.frontend, &cfg);
+ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+ fe->ops.tuner_ops.set_config(fe, &ctl);
+ }
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1200:
+ case CX23885_BOARD_HAUPPAUGE_HVR1700:
+ i2c_bus = &dev->i2c_bus[0];
+ fe0->dvb.frontend = dvb_attach(tda10048_attach,
+ &hauppauge_hvr1200_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(tda829x_attach, fe0->dvb.frontend,
+ &dev->i2c_bus[1].i2c_adap, 0x42,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_bus[1].i2c_adap,
+ &hauppauge_hvr1200_tuner_config);
+ }
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1210:
+ i2c_bus = &dev->i2c_bus[0];
+ fe0->dvb.frontend = dvb_attach(tda10048_attach,
+ &hauppauge_hvr1210_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_bus[1].i2c_adap,
+ &hauppauge_hvr1210_tuner_config);
+ }
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1400:
+ i2c_bus = &dev->i2c_bus[0];
+ fe0->dvb.frontend = dvb_attach(dib7000p_attach,
+ &i2c_bus->i2c_adap,
+ 0x12, &hauppauge_hvr1400_dib7000_config);
+ if (fe0->dvb.frontend != NULL) {
+ struct dvb_frontend *fe;
+ struct xc2028_config cfg = {
+ .i2c_adap = &dev->i2c_bus[1].i2c_adap,
+ .i2c_addr = 0x64,
+ };
+ static struct xc2028_ctrl ctl = {
+ .fname = XC3028L_DEFAULT_FIRMWARE,
+ .max_len = 64,
+ .demod = XC3028_FE_DIBCOM52,
+ /* This is true for all demods with
+ v36 firmware? */
+ .type = XC2028_D2633,
+ };
+
+ fe = dvb_attach(xc2028_attach,
+ fe0->dvb.frontend, &cfg);
+ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+ fe->ops.tuner_ops.set_config(fe, &ctl);
+ }
+ break;
+ case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP:
+ i2c_bus = &dev->i2c_bus[port->nr - 1];
+
+ fe0->dvb.frontend = dvb_attach(s5h1409_attach,
+ &dvico_s5h1409_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend == NULL)
+ fe0->dvb.frontend = dvb_attach(s5h1411_attach,
+ &dvico_s5h1411_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL)
+ dvb_attach(xc5000_attach, fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &dvico_xc5000_tunerconfig);
+ break;
+ case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: {
+ i2c_bus = &dev->i2c_bus[port->nr - 1];
+
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &dvico_fusionhdtv_xc3028,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ struct dvb_frontend *fe;
+ struct xc2028_config cfg = {
+ .i2c_adap = &i2c_bus->i2c_adap,
+ .i2c_addr = 0x61,
+ };
+ static struct xc2028_ctrl ctl = {
+ .fname = XC2028_DEFAULT_FIRMWARE,
+ .max_len = 64,
+ .demod = XC3028_FE_ZARLINK456,
+ };
+
+ fe = dvb_attach(xc2028_attach, fe0->dvb.frontend,
+ &cfg);
+ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+ fe->ops.tuner_ops.set_config(fe, &ctl);
+ }
+ break;
+ }
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
+ i2c_bus = &dev->i2c_bus[0];
+
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &dvico_fusionhdtv_xc3028,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ struct dvb_frontend *fe;
+ struct xc2028_config cfg = {
+ .i2c_adap = &dev->i2c_bus[1].i2c_adap,
+ .i2c_addr = 0x61,
+ };
+ static struct xc2028_ctrl ctl = {
+ .fname = XC2028_DEFAULT_FIRMWARE,
+ .max_len = 64,
+ .demod = XC3028_FE_ZARLINK456,
+ };
+
+ fe = dvb_attach(xc2028_attach, fe0->dvb.frontend,
+ &cfg);
+ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+ fe->ops.tuner_ops.set_config(fe, &ctl);
+ }
+ break;
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
+ i2c_bus = &dev->i2c_bus[0];
+
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &dvico_fusionhdtv_xc3028,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ struct dvb_frontend *fe;
+ struct xc4000_config cfg = {
+ .i2c_address = 0x61,
+ .default_pm = 0,
+ .dvb_amplitude = 134,
+ .set_smoothedcvbs = 1,
+ .if_khz = 4560
+ };
+
+ fe = dvb_attach(xc4000_attach, fe0->dvb.frontend,
+ &dev->i2c_bus[1].i2c_adap, &cfg);
+ if (!fe) {
+ printk(KERN_ERR "%s/2: xc4000 attach failed\n",
+ dev->name);
+ goto frontend_detach;
+ }
+ }
+ break;
+ case CX23885_BOARD_TBS_6920:
+ i2c_bus = &dev->i2c_bus[1];
+
+ fe0->dvb.frontend = dvb_attach(cx24116_attach,
+ &tbs_cx24116_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL)
+ fe0->dvb.frontend->ops.set_voltage = f300_set_voltage;
+
+ break;
+ case CX23885_BOARD_TEVII_S470:
+ i2c_bus = &dev->i2c_bus[1];
+
+ fe0->dvb.frontend = dvb_attach(ds3000_attach,
+ &tevii_ds3000_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL)
+ fe0->dvb.frontend->ops.set_voltage = f300_set_voltage;
+
+ break;
+ case CX23885_BOARD_DVBWORLD_2005:
+ i2c_bus = &dev->i2c_bus[1];
+
+ fe0->dvb.frontend = dvb_attach(cx24116_attach,
+ &dvbworld_cx24116_config,
+ &i2c_bus->i2c_adap);
+ break;
+ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
+ i2c_bus = &dev->i2c_bus[0];
+ switch (port->nr) {
+ /* port B */
+ case 1:
+ fe0->dvb.frontend = dvb_attach(stv0900_attach,
+ &netup_stv0900_config,
+ &i2c_bus->i2c_adap, 0);
+ if (fe0->dvb.frontend != NULL) {
+ if (dvb_attach(stv6110_attach,
+ fe0->dvb.frontend,
+ &netup_stv6110_tunerconfig_a,
+ &i2c_bus->i2c_adap)) {
+ if (!dvb_attach(lnbh24_attach,
+ fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ LNBH24_PCL | LNBH24_TTX,
+ LNBH24_TEN, 0x09))
+ printk(KERN_ERR
+ "No LNBH24 found!\n");
+
+ }
+ }
+ break;
+ /* port C */
+ case 2:
+ fe0->dvb.frontend = dvb_attach(stv0900_attach,
+ &netup_stv0900_config,
+ &i2c_bus->i2c_adap, 1);
+ if (fe0->dvb.frontend != NULL) {
+ if (dvb_attach(stv6110_attach,
+ fe0->dvb.frontend,
+ &netup_stv6110_tunerconfig_b,
+ &i2c_bus->i2c_adap)) {
+ if (!dvb_attach(lnbh24_attach,
+ fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ LNBH24_PCL | LNBH24_TTX,
+ LNBH24_TEN, 0x0a))
+ printk(KERN_ERR
+ "No LNBH24 found!\n");
+
+ }
+ }
+ break;
+ }
+ break;
+ case CX23885_BOARD_MYGICA_X8506:
+ i2c_bus = &dev->i2c_bus[0];
+ i2c_bus2 = &dev->i2c_bus[1];
+ fe0->dvb.frontend = dvb_attach(lgs8gxx_attach,
+ &mygica_x8506_lgs8gl5_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(xc5000_attach,
+ fe0->dvb.frontend,
+ &i2c_bus2->i2c_adap,
+ &mygica_x8506_xc5000_config);
+ }
+ break;
+ case CX23885_BOARD_MAGICPRO_PROHDTVE2:
+ i2c_bus = &dev->i2c_bus[0];
+ i2c_bus2 = &dev->i2c_bus[1];
+ fe0->dvb.frontend = dvb_attach(lgs8gxx_attach,
+ &magicpro_prohdtve2_lgs8g75_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(xc5000_attach,
+ fe0->dvb.frontend,
+ &i2c_bus2->i2c_adap,
+ &magicpro_prohdtve2_xc5000_config);
+ }
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ i2c_bus = &dev->i2c_bus[0];
+ fe0->dvb.frontend = dvb_attach(s5h1411_attach,
+ &hcw_s5h1411_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL)
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_bus[0].i2c_adap,
+ &hauppauge_tda18271_config);
+
+ tda18271_attach(&dev->ts1.analog_fe,
+ 0x60, &dev->i2c_bus[1].i2c_adap,
+ &hauppauge_tda18271_config);
+
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ i2c_bus = &dev->i2c_bus[0];
+ fe0->dvb.frontend = dvb_attach(s5h1411_attach,
+ &hcw_s5h1411_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL)
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_bus[0].i2c_adap,
+ &hauppauge_tda18271_config);
+ break;
+ case CX23885_BOARD_MYGICA_X8558PRO:
+ switch (port->nr) {
+ /* port B */
+ case 1:
+ i2c_bus = &dev->i2c_bus[0];
+ fe0->dvb.frontend = dvb_attach(atbm8830_attach,
+ &mygica_x8558pro_atbm8830_cfg1,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(max2165_attach,
+ fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &mygic_x8558pro_max2165_cfg1);
+ }
+ break;
+ /* port C */
+ case 2:
+ i2c_bus = &dev->i2c_bus[1];
+ fe0->dvb.frontend = dvb_attach(atbm8830_attach,
+ &mygica_x8558pro_atbm8830_cfg2,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(max2165_attach,
+ fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &mygic_x8558pro_max2165_cfg2);
+ }
+ break;
+ }
+ break;
+ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
+ i2c_bus = &dev->i2c_bus[0];
+ mfe_shared = 1;/* MFE */
+ port->frontends.gate = 0;/* not clear for me yet */
+ /* ports B, C */
+ /* MFE frontend 1 DVB-T */
+ fe0->dvb.frontend = dvb_attach(stv0367ter_attach,
+ &netup_stv0367_config[port->nr - 1],
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (NULL == dvb_attach(xc5000_attach,
+ fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &netup_xc5000_config[port->nr - 1]))
+ goto frontend_detach;
+ /* load xc5000 firmware */
+ fe0->dvb.frontend->ops.tuner_ops.init(fe0->dvb.frontend);
+ }
+ /* MFE frontend 2 */
+ fe1 = videobuf_dvb_get_frontend(&port->frontends, 2);
+ if (fe1 == NULL)
+ goto frontend_detach;
+ /* DVB-C init */
+ fe1->dvb.frontend = dvb_attach(stv0367cab_attach,
+ &netup_stv0367_config[port->nr - 1],
+ &i2c_bus->i2c_adap);
+ if (fe1->dvb.frontend != NULL) {
+ fe1->dvb.frontend->id = 1;
+ if (NULL == dvb_attach(xc5000_attach,
+ fe1->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &netup_xc5000_config[port->nr - 1]))
+ goto frontend_detach;
+ }
+ break;
+ case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+ i2c_bus = &dev->i2c_bus[0];
+ i2c_bus2 = &dev->i2c_bus[1];
+
+ switch (port->nr) {
+ /* port b */
+ case 1:
+ fe0->dvb.frontend = dvb_attach(drxk_attach,
+ &terratec_drxk_config[0],
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(mt2063_attach,
+ fe0->dvb.frontend,
+ &terratec_mt2063_config[0],
+ &i2c_bus2->i2c_adap))
+ goto frontend_detach;
+ }
+ break;
+ /* port c */
+ case 2:
+ fe0->dvb.frontend = dvb_attach(drxk_attach,
+ &terratec_drxk_config[1],
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(mt2063_attach,
+ fe0->dvb.frontend,
+ &terratec_mt2063_config[1],
+ &i2c_bus2->i2c_adap))
+ goto frontend_detach;
+ }
+ break;
+ }
+ break;
+ default:
+ printk(KERN_INFO "%s: The frontend of your DVB/ATSC card "
+ " isn't supported yet\n",
+ dev->name);
+ break;
+ }
+
+ if ((NULL == fe0->dvb.frontend) || (fe1 && NULL == fe1->dvb.frontend)) {
+ printk(KERN_ERR "%s: frontend initialization failed\n",
+ dev->name);
+ goto frontend_detach;
+ }
+
+ /* define general-purpose callback pointer */
+ fe0->dvb.frontend->callback = cx23885_tuner_callback;
+ if (fe1)
+ fe1->dvb.frontend->callback = cx23885_tuner_callback;
+#if 0
+ /* Ensure all frontends negotiate bus access */
+ fe0->dvb.frontend->ops.ts_bus_ctrl = cx23885_dvb_bus_ctrl;
+ if (fe1)
+ fe1->dvb.frontend->ops.ts_bus_ctrl = cx23885_dvb_bus_ctrl;
+#endif
+
+ /* Put the analog decoder in standby to keep it quiet */
+ call_all(dev, core, s_power, 0);
+
+ if (fe0->dvb.frontend->ops.analog_ops.standby)
+ fe0->dvb.frontend->ops.analog_ops.standby(fe0->dvb.frontend);
+
+ /* register everything */
+ ret = videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port,
+ &dev->pci->dev, adapter_nr, mfe_shared,
+ NULL);
+ if (ret)
+ goto frontend_detach;
+
+ /* init CI & MAC */
+ switch (dev->board) {
+ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: {
+ static struct netup_card_info cinfo;
+
+ netup_get_card_info(&dev->i2c_bus[0].i2c_adap, &cinfo);
+ memcpy(port->frontends.adapter.proposed_mac,
+ cinfo.port[port->nr - 1].mac, 6);
+ printk(KERN_INFO "NetUP Dual DVB-S2 CI card port%d MAC=%pM\n",
+ port->nr, port->frontends.adapter.proposed_mac);
+
+ netup_ci_init(port);
+ break;
+ }
+ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: {
+ struct altera_ci_config netup_ci_cfg = {
+ .dev = dev,/* magic number to identify*/
+ .adapter = &port->frontends.adapter,/* for CI */
+ .demux = &fe0->dvb.demux,/* for hw pid filter */
+ .fpga_rw = netup_altera_fpga_rw,
+ };
+
+ altera_ci_init(&netup_ci_cfg, port->nr);
+ break;
+ }
+ case CX23885_BOARD_TEVII_S470: {
+ u8 eeprom[256]; /* 24C02 i2c eeprom */
+
+ if (port->nr != 1)
+ break;
+
+ /* Read entire EEPROM */
+ dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1;
+ tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, sizeof(eeprom));
+ printk(KERN_INFO "TeVii S470 MAC= %pM\n", eeprom + 0xa0);
+ memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xa0, 6);
+ break;
+ }
+ }
+
+ return ret;
+
+frontend_detach:
+ port->gate_ctrl = NULL;
+ videobuf_dvb_dealloc_frontends(&port->frontends);
+ return -EINVAL;
+}
+
+int cx23885_dvb_register(struct cx23885_tsport *port)
+{
+
+ struct videobuf_dvb_frontend *fe0;
+ struct cx23885_dev *dev = port->dev;
+ int err, i;
+
+ /* Here we need to allocate the correct number of frontends,
+ * as reflected in the cards struct. The reality is that currently
+ * no cx23885 boards support this - yet. But, if we don't modify this
+ * code then the second frontend would never be allocated (later)
+ * and fail with error before the attach in dvb_register().
+ * Without these changes we risk an OOPS later. The changes here
+ * are for safety, and should provide a good foundation for the
+ * future addition of any multi-frontend cx23885 based boards.
+ */
+ printk(KERN_INFO "%s() allocating %d frontend(s)\n", __func__,
+ port->num_frontends);
+
+ for (i = 1; i <= port->num_frontends; i++) {
+ if (videobuf_dvb_alloc_frontend(
+ &port->frontends, i) == NULL) {
+ printk(KERN_ERR "%s() failed to alloc\n", __func__);
+ return -ENOMEM;
+ }
+
+ fe0 = videobuf_dvb_get_frontend(&port->frontends, i);
+ if (!fe0)
+ err = -EINVAL;
+
+ dprintk(1, "%s\n", __func__);
+ dprintk(1, " ->probed by Card=%d Name=%s, PCI %02x:%02x\n",
+ dev->board,
+ dev->name,
+ dev->pci_bus,
+ dev->pci_slot);
+
+ err = -ENODEV;
+
+ /* dvb stuff */
+ /* We have to init the queue for each frontend on a port. */
+ printk(KERN_INFO "%s: cx23885 based dvb card\n", dev->name);
+ videobuf_queue_sg_init(&fe0->dvb.dvbq, &dvb_qops,
+ &dev->pci->dev, &port->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_TOP,
+ sizeof(struct cx23885_buffer), port, NULL);
+ }
+ err = dvb_register(port);
+ if (err != 0)
+ printk(KERN_ERR "%s() dvb_register failed err = %d\n",
+ __func__, err);
+
+ return err;
+}
+
+int cx23885_dvb_unregister(struct cx23885_tsport *port)
+{
+ struct videobuf_dvb_frontend *fe0;
+
+ /* FIXME: in an error condition where the we have
+ * an expected number of frontends (attach problem)
+ * then this might not clean up correctly, if 1
+ * is invalid.
+ * This comment only applies to future boards IF they
+ * implement MFE support.
+ */
+ fe0 = videobuf_dvb_get_frontend(&port->frontends, 1);
+ if (fe0 && fe0->dvb.frontend)
+ videobuf_dvb_unregister_bus(&port->frontends);
+
+ switch (port->dev->board) {
+ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
+ netup_ci_exit(port);
+ break;
+ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
+ altera_ci_release(port->dev, port->nr);
+ break;
+ }
+
+ port->gate_ctrl = NULL;
+
+ return 0;
+}
+
diff --git a/drivers/media/video/cx23885/cx23885-f300.c b/drivers/media/video/cx23885/cx23885-f300.c
new file mode 100644
index 00000000..93998f22
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-f300.c
@@ -0,0 +1,177 @@
+/*
+ * Driver for Silicon Labs C8051F300 microcontroller.
+ *
+ * It is used for LNB power control in TeVii S470,
+ * TBS 6920 PCIe DVB-S2 cards.
+ *
+ * Microcontroller connected to cx23885 GPIO pins:
+ * GPIO0 - data - P0.3 F300
+ * GPIO1 - reset - P0.2 F300
+ * GPIO2 - clk - P0.1 F300
+ * GPIO3 - busy - P0.0 F300
+ *
+ * Copyright (C) 2009 Igor M. Liplianin <liplianin@me.by>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "cx23885.h"
+
+#define F300_DATA GPIO_0
+#define F300_RESET GPIO_1
+#define F300_CLK GPIO_2
+#define F300_BUSY GPIO_3
+
+static void f300_set_line(struct cx23885_dev *dev, u32 line, u8 lvl)
+{
+ cx23885_gpio_enable(dev, line, 1);
+ if (lvl == 1)
+ cx23885_gpio_set(dev, line);
+ else
+ cx23885_gpio_clear(dev, line);
+}
+
+static u8 f300_get_line(struct cx23885_dev *dev, u32 line)
+{
+ cx23885_gpio_enable(dev, line, 0);
+
+ return cx23885_gpio_get(dev, line);
+}
+
+static void f300_send_byte(struct cx23885_dev *dev, u8 dta)
+{
+ u8 i;
+
+ for (i = 0; i < 8; i++) {
+ f300_set_line(dev, F300_CLK, 0);
+ udelay(30);
+ f300_set_line(dev, F300_DATA, (dta & 0x80) >> 7);/* msb first */
+ udelay(30);
+ dta <<= 1;
+ f300_set_line(dev, F300_CLK, 1);
+ udelay(30);
+ }
+}
+
+static u8 f300_get_byte(struct cx23885_dev *dev)
+{
+ u8 i, dta = 0;
+
+ for (i = 0; i < 8; i++) {
+ f300_set_line(dev, F300_CLK, 0);
+ udelay(30);
+ dta <<= 1;
+ f300_set_line(dev, F300_CLK, 1);
+ udelay(30);
+ dta |= f300_get_line(dev, F300_DATA);/* msb first */
+
+ }
+
+ return dta;
+}
+
+static u8 f300_xfer(struct dvb_frontend *fe, u8 *buf)
+{
+ struct cx23885_tsport *port = fe->dvb->priv;
+ struct cx23885_dev *dev = port->dev;
+ u8 i, temp, ret = 0;
+
+ temp = buf[0];
+ for (i = 0; i < buf[0]; i++)
+ temp += buf[i + 1];
+ temp = (~temp + 1);/* get check sum */
+ buf[1 + buf[0]] = temp;
+
+ f300_set_line(dev, F300_RESET, 1);
+ f300_set_line(dev, F300_CLK, 1);
+ udelay(30);
+ f300_set_line(dev, F300_DATA, 1);
+ msleep(1);
+
+ /* question: */
+ f300_set_line(dev, F300_RESET, 0);/* begin to send data */
+ msleep(1);
+
+ f300_send_byte(dev, 0xe0);/* the slave address is 0xe0, write */
+ msleep(1);
+
+ temp = buf[0];
+ temp += 2;
+ for (i = 0; i < temp; i++)
+ f300_send_byte(dev, buf[i]);
+
+ f300_set_line(dev, F300_RESET, 1);/* sent data over */
+ f300_set_line(dev, F300_DATA, 1);
+
+ /* answer: */
+ temp = 0;
+ for (i = 0; ((i < 8) & (temp == 0)); i++) {
+ msleep(1);
+ if (f300_get_line(dev, F300_BUSY) == 0)
+ temp = 1;
+ }
+
+ if (i > 7) {
+ printk(KERN_ERR "%s: timeout, the slave no response\n",
+ __func__);
+ ret = 1; /* timeout, the slave no response */
+ } else { /* the slave not busy, prepare for getting data */
+ f300_set_line(dev, F300_RESET, 0);/*ready...*/
+ msleep(1);
+ f300_send_byte(dev, 0xe1);/* 0xe1 is Read */
+ msleep(1);
+ temp = f300_get_byte(dev);/*get the data length */
+ if (temp > 14)
+ temp = 14;
+
+ for (i = 0; i < (temp + 1); i++)
+ f300_get_byte(dev);/* get data to empty buffer */
+
+ f300_set_line(dev, F300_RESET, 1);/* received data over */
+ f300_set_line(dev, F300_DATA, 1);
+ }
+
+ return ret;
+}
+
+int f300_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+ u8 buf[16];
+
+ buf[0] = 0x05;
+ buf[1] = 0x38;/* write port */
+ buf[2] = 0x01;/* A port, lnb power */
+
+ switch (voltage) {
+ case SEC_VOLTAGE_13:
+ buf[3] = 0x01;/* power on */
+ buf[4] = 0x02;/* B port, H/V */
+ buf[5] = 0x00;/*13V v*/
+ break;
+ case SEC_VOLTAGE_18:
+ buf[3] = 0x01;
+ buf[4] = 0x02;
+ buf[5] = 0x01;/* 18V h*/
+ break;
+ case SEC_VOLTAGE_OFF:
+ buf[3] = 0x00;/* power off */
+ buf[4] = 0x00;
+ buf[5] = 0x00;
+ break;
+ }
+
+ return f300_xfer(fe, buf);
+}
diff --git a/drivers/media/video/cx23885/cx23885-f300.h b/drivers/media/video/cx23885/cx23885-f300.h
new file mode 100644
index 00000000..e73344c9
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-f300.h
@@ -0,0 +1,2 @@
+extern int f300_set_voltage(struct dvb_frontend *fe,
+ fe_sec_voltage_t voltage);
diff --git a/drivers/media/video/cx23885/cx23885-i2c.c b/drivers/media/video/cx23885/cx23885-i2c.c
new file mode 100644
index 00000000..be1e21d8
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-i2c.c
@@ -0,0 +1,402 @@
+/*
+ * Driver for the Conexant CX23885 PCIe bridge
+ *
+ * Copyright (c) 2006 Steven Toth <stoth@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+#include "cx23885.h"
+
+#include <media/v4l2-common.h>
+
+static unsigned int i2c_debug;
+module_param(i2c_debug, int, 0644);
+MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
+
+static unsigned int i2c_scan;
+module_param(i2c_scan, int, 0444);
+MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time");
+
+#define dprintk(level, fmt, arg...)\
+ do { if (i2c_debug >= level)\
+ printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\
+ } while (0)
+
+#define I2C_WAIT_DELAY 32
+#define I2C_WAIT_RETRY 64
+
+#define I2C_EXTEND (1 << 3)
+#define I2C_NOSTOP (1 << 4)
+
+static inline int i2c_slave_did_ack(struct i2c_adapter *i2c_adap)
+{
+ struct cx23885_i2c *bus = i2c_adap->algo_data;
+ struct cx23885_dev *dev = bus->dev;
+ return cx_read(bus->reg_stat) & 0x01;
+}
+
+static inline int i2c_is_busy(struct i2c_adapter *i2c_adap)
+{
+ struct cx23885_i2c *bus = i2c_adap->algo_data;
+ struct cx23885_dev *dev = bus->dev;
+ return cx_read(bus->reg_stat) & 0x02 ? 1 : 0;
+}
+
+static int i2c_wait_done(struct i2c_adapter *i2c_adap)
+{
+ int count;
+
+ for (count = 0; count < I2C_WAIT_RETRY; count++) {
+ if (!i2c_is_busy(i2c_adap))
+ break;
+ udelay(I2C_WAIT_DELAY);
+ }
+
+ if (I2C_WAIT_RETRY == count)
+ return 0;
+
+ return 1;
+}
+
+static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
+ const struct i2c_msg *msg, int joined_rlen)
+{
+ struct cx23885_i2c *bus = i2c_adap->algo_data;
+ struct cx23885_dev *dev = bus->dev;
+ u32 wdata, addr, ctrl;
+ int retval, cnt;
+
+ if (joined_rlen)
+ dprintk(1, "%s(msg->wlen=%d, nextmsg->rlen=%d)\n", __func__,
+ msg->len, joined_rlen);
+ else
+ dprintk(1, "%s(msg->len=%d)\n", __func__, msg->len);
+
+ /* Deal with i2c probe functions with zero payload */
+ if (msg->len == 0) {
+ cx_write(bus->reg_addr, msg->addr << 25);
+ cx_write(bus->reg_ctrl, bus->i2c_period | (1 << 2));
+ if (!i2c_wait_done(i2c_adap))
+ return -EIO;
+ if (!i2c_slave_did_ack(i2c_adap))
+ return -ENXIO;
+
+ dprintk(1, "%s() returns 0\n", __func__);
+ return 0;
+ }
+
+
+ /* dev, reg + first byte */
+ addr = (msg->addr << 25) | msg->buf[0];
+ wdata = msg->buf[0];
+ ctrl = bus->i2c_period | (1 << 12) | (1 << 2);
+
+ if (msg->len > 1)
+ ctrl |= I2C_NOSTOP | I2C_EXTEND;
+ else if (joined_rlen)
+ ctrl |= I2C_NOSTOP;
+
+ cx_write(bus->reg_addr, addr);
+ cx_write(bus->reg_wdata, wdata);
+ cx_write(bus->reg_ctrl, ctrl);
+
+ if (!i2c_wait_done(i2c_adap))
+ goto eio;
+ if (i2c_debug) {
+ printk(" <W %02x %02x", msg->addr << 1, msg->buf[0]);
+ if (!(ctrl & I2C_NOSTOP))
+ printk(" >\n");
+ }
+
+ for (cnt = 1; cnt < msg->len; cnt++) {
+ /* following bytes */
+ wdata = msg->buf[cnt];
+ ctrl = bus->i2c_period | (1 << 12) | (1 << 2);
+
+ if (cnt < msg->len - 1)
+ ctrl |= I2C_NOSTOP | I2C_EXTEND;
+ else if (joined_rlen)
+ ctrl |= I2C_NOSTOP;
+
+ cx_write(bus->reg_addr, addr);
+ cx_write(bus->reg_wdata, wdata);
+ cx_write(bus->reg_ctrl, ctrl);
+
+ if (!i2c_wait_done(i2c_adap))
+ goto eio;
+ if (i2c_debug) {
+ dprintk(1, " %02x", msg->buf[cnt]);
+ if (!(ctrl & I2C_NOSTOP))
+ dprintk(1, " >\n");
+ }
+ }
+ return msg->len;
+
+ eio:
+ retval = -EIO;
+ if (i2c_debug)
+ printk(KERN_ERR " ERR: %d\n", retval);
+ return retval;
+}
+
+static int i2c_readbytes(struct i2c_adapter *i2c_adap,
+ const struct i2c_msg *msg, int joined)
+{
+ struct cx23885_i2c *bus = i2c_adap->algo_data;
+ struct cx23885_dev *dev = bus->dev;
+ u32 ctrl, cnt;
+ int retval;
+
+
+ if (i2c_debug && !joined)
+ dprintk(1, "%s(msg->len=%d)\n", __func__, msg->len);
+
+ /* Deal with i2c probe functions with zero payload */
+ if (msg->len == 0) {
+ cx_write(bus->reg_addr, msg->addr << 25);
+ cx_write(bus->reg_ctrl, bus->i2c_period | (1 << 2) | 1);
+ if (!i2c_wait_done(i2c_adap))
+ return -EIO;
+ if (!i2c_slave_did_ack(i2c_adap))
+ return -ENXIO;
+
+
+ dprintk(1, "%s() returns 0\n", __func__);
+ return 0;
+ }
+
+ if (i2c_debug) {
+ if (joined)
+ dprintk(1, " R");
+ else
+ dprintk(1, " <R %02x", (msg->addr << 1) + 1);
+ }
+
+ for (cnt = 0; cnt < msg->len; cnt++) {
+
+ ctrl = bus->i2c_period | (1 << 12) | (1 << 2) | 1;
+
+ if (cnt < msg->len - 1)
+ ctrl |= I2C_NOSTOP | I2C_EXTEND;
+
+ cx_write(bus->reg_addr, msg->addr << 25);
+ cx_write(bus->reg_ctrl, ctrl);
+
+ if (!i2c_wait_done(i2c_adap))
+ goto eio;
+ msg->buf[cnt] = cx_read(bus->reg_rdata) & 0xff;
+ if (i2c_debug) {
+ dprintk(1, " %02x", msg->buf[cnt]);
+ if (!(ctrl & I2C_NOSTOP))
+ dprintk(1, " >\n");
+ }
+ }
+ return msg->len;
+
+ eio:
+ retval = -EIO;
+ if (i2c_debug)
+ printk(KERN_ERR " ERR: %d\n", retval);
+ return retval;
+}
+
+static int i2c_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg *msgs, int num)
+{
+ struct cx23885_i2c *bus = i2c_adap->algo_data;
+ struct cx23885_dev *dev = bus->dev;
+ int i, retval = 0;
+
+ dprintk(1, "%s(num = %d)\n", __func__, num);
+
+ for (i = 0 ; i < num; i++) {
+ dprintk(1, "%s(num = %d) addr = 0x%02x len = 0x%x\n",
+ __func__, num, msgs[i].addr, msgs[i].len);
+ if (msgs[i].flags & I2C_M_RD) {
+ /* read */
+ retval = i2c_readbytes(i2c_adap, &msgs[i], 0);
+ } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) &&
+ msgs[i].addr == msgs[i + 1].addr) {
+ /* write then read from same address */
+ retval = i2c_sendbytes(i2c_adap, &msgs[i],
+ msgs[i + 1].len);
+ if (retval < 0)
+ goto err;
+ i++;
+ retval = i2c_readbytes(i2c_adap, &msgs[i], 1);
+ } else {
+ /* write */
+ retval = i2c_sendbytes(i2c_adap, &msgs[i], 0);
+ }
+ if (retval < 0)
+ goto err;
+ }
+ return num;
+
+ err:
+ return retval;
+}
+
+static u32 cx23885_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm cx23885_i2c_algo_template = {
+ .master_xfer = i2c_xfer,
+ .functionality = cx23885_functionality,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static struct i2c_adapter cx23885_i2c_adap_template = {
+ .name = "cx23885",
+ .owner = THIS_MODULE,
+ .algo = &cx23885_i2c_algo_template,
+};
+
+static struct i2c_client cx23885_i2c_client_template = {
+ .name = "cx23885 internal",
+};
+
+static char *i2c_devs[128] = {
+ [0x10 >> 1] = "tda10048",
+ [0x12 >> 1] = "dib7000pc",
+ [0x1c >> 1] = "lgdt3303",
+ [0x86 >> 1] = "tda9887",
+ [0x32 >> 1] = "cx24227",
+ [0x88 >> 1] = "cx25837",
+ [0x84 >> 1] = "tda8295",
+ [0x98 >> 1] = "flatiron",
+ [0xa0 >> 1] = "eeprom",
+ [0xc0 >> 1] = "tuner/mt2131/tda8275",
+ [0xc2 >> 1] = "tuner/mt2131/tda8275/xc5000/xc3028",
+ [0xc8 >> 1] = "tuner/xc3028L",
+};
+
+static void do_i2c_scan(char *name, struct i2c_client *c)
+{
+ unsigned char buf;
+ int i, rc;
+
+ for (i = 0; i < 128; i++) {
+ c->addr = i;
+ rc = i2c_master_recv(c, &buf, 0);
+ if (rc < 0)
+ continue;
+ printk(KERN_INFO "%s: i2c scan: found device @ 0x%x [%s]\n",
+ name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
+ }
+}
+
+/* init + register i2c adapter */
+int cx23885_i2c_register(struct cx23885_i2c *bus)
+{
+ struct cx23885_dev *dev = bus->dev;
+
+ dprintk(1, "%s(bus = %d)\n", __func__, bus->nr);
+
+ memcpy(&bus->i2c_adap, &cx23885_i2c_adap_template,
+ sizeof(bus->i2c_adap));
+ memcpy(&bus->i2c_algo, &cx23885_i2c_algo_template,
+ sizeof(bus->i2c_algo));
+ memcpy(&bus->i2c_client, &cx23885_i2c_client_template,
+ sizeof(bus->i2c_client));
+
+ bus->i2c_adap.dev.parent = &dev->pci->dev;
+
+ strlcpy(bus->i2c_adap.name, bus->dev->name,
+ sizeof(bus->i2c_adap.name));
+
+ bus->i2c_algo.data = bus;
+ bus->i2c_adap.algo_data = bus;
+ i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev);
+ i2c_add_adapter(&bus->i2c_adap);
+
+ bus->i2c_client.adapter = &bus->i2c_adap;
+
+ if (0 == bus->i2c_rc) {
+ dprintk(1, "%s: i2c bus %d registered\n", dev->name, bus->nr);
+ if (i2c_scan) {
+ printk(KERN_INFO "%s: scan bus %d:\n",
+ dev->name, bus->nr);
+ do_i2c_scan(dev->name, &bus->i2c_client);
+ }
+ } else
+ printk(KERN_WARNING "%s: i2c bus %d register FAILED\n",
+ dev->name, bus->nr);
+
+ /* Instantiate the IR receiver device, if present */
+ if (0 == bus->i2c_rc) {
+ struct i2c_board_info info;
+ const unsigned short addr_list[] = {
+ 0x6b, I2C_CLIENT_END
+ };
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
+ /* Use quick read command for probe, some IR chips don't
+ * support writes */
+ i2c_new_probed_device(&bus->i2c_adap, &info, addr_list,
+ i2c_probe_func_quick_read);
+ }
+
+ return bus->i2c_rc;
+}
+
+int cx23885_i2c_unregister(struct cx23885_i2c *bus)
+{
+ i2c_del_adapter(&bus->i2c_adap);
+ return 0;
+}
+
+void cx23885_av_clk(struct cx23885_dev *dev, int enable)
+{
+ /* write 0 to bus 2 addr 0x144 via i2x_xfer() */
+ char buffer[3];
+ struct i2c_msg msg;
+ dprintk(1, "%s(enabled = %d)\n", __func__, enable);
+
+ /* Register 0x144 */
+ buffer[0] = 0x01;
+ buffer[1] = 0x44;
+ if (enable == 1)
+ buffer[2] = 0x05;
+ else
+ buffer[2] = 0x00;
+
+ msg.addr = 0x44;
+ msg.flags = I2C_M_TEN;
+ msg.len = 3;
+ msg.buf = buffer;
+
+ i2c_xfer(&dev->i2c_bus[2].i2c_adap, &msg, 1);
+}
+
+/* ----------------------------------------------------------------------- */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/cx23885/cx23885-input.c b/drivers/media/video/cx23885/cx23885-input.c
new file mode 100644
index 00000000..ce765e3f
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-input.c
@@ -0,0 +1,356 @@
+/*
+ * Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ * Infrared remote control input device
+ *
+ * Most of this file is
+ *
+ * Copyright (C) 2009 Andy Walls <awalls@md.metrocast.net>
+ *
+ * However, the cx23885_input_{init,fini} functions contained herein are
+ * derived from Linux kernel files linux/media/video/.../...-input.c marked as:
+ *
+ * Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
+ * Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
+ * Markus Rechberger <mrechberger@gmail.com>
+ * Mauro Carvalho Chehab <mchehab@infradead.org>
+ * Sascha Sommer <saschasommer@freenet.de>
+ * Copyright (C) 2004, 2005 Chris Pascoe
+ * Copyright (C) 2003, 2004 Gerd Knorr
+ * Copyright (C) 2003 Pavel Machek
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <linux/slab.h>
+#include <media/rc-core.h>
+#include <media/v4l2-subdev.h>
+
+#include "cx23885.h"
+
+#define MODULE_NAME "cx23885"
+
+static void cx23885_input_process_measurements(struct cx23885_dev *dev,
+ bool overrun)
+{
+ struct cx23885_kernel_ir *kernel_ir = dev->kernel_ir;
+
+ ssize_t num;
+ int count, i;
+ bool handle = false;
+ struct ir_raw_event ir_core_event[64];
+
+ do {
+ num = 0;
+ v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) ir_core_event,
+ sizeof(ir_core_event), &num);
+
+ count = num / sizeof(struct ir_raw_event);
+
+ for (i = 0; i < count; i++) {
+ ir_raw_event_store(kernel_ir->rc,
+ &ir_core_event[i]);
+ handle = true;
+ }
+ } while (num != 0);
+
+ if (overrun)
+ ir_raw_event_reset(kernel_ir->rc);
+ else if (handle)
+ ir_raw_event_handle(kernel_ir->rc);
+}
+
+void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events)
+{
+ struct v4l2_subdev_ir_parameters params;
+ int overrun, data_available;
+
+ if (dev->sd_ir == NULL || events == 0)
+ return;
+
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ case CX23885_BOARD_TEVII_S470:
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ /*
+ * The only boards we handle right now. However other boards
+ * using the CX2388x integrated IR controller should be similar
+ */
+ break;
+ default:
+ return;
+ }
+
+ overrun = events & (V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN |
+ V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN);
+
+ data_available = events & (V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED |
+ V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ);
+
+ if (overrun) {
+ /* If there was a FIFO overrun, stop the device */
+ v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
+ params.enable = false;
+ /* Mitigate race with cx23885_input_ir_stop() */
+ params.shutdown = atomic_read(&dev->ir_input_stopping);
+ v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
+ }
+
+ if (data_available)
+ cx23885_input_process_measurements(dev, overrun);
+
+ if (overrun) {
+ /* If there was a FIFO overrun, clear & restart the device */
+ params.enable = true;
+ /* Mitigate race with cx23885_input_ir_stop() */
+ params.shutdown = atomic_read(&dev->ir_input_stopping);
+ v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
+ }
+}
+
+static int cx23885_input_ir_start(struct cx23885_dev *dev)
+{
+ struct v4l2_subdev_ir_parameters params;
+
+ if (dev->sd_ir == NULL)
+ return -ENODEV;
+
+ atomic_set(&dev->ir_input_stopping, 0);
+
+ v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ /*
+ * The IR controller on this board only returns pulse widths.
+ * Any other mode setting will fail to set up the device.
+ */
+ params.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+ params.enable = true;
+ params.interrupt_enable = true;
+ params.shutdown = false;
+
+ /* Setup for baseband compatible with both RC-5 and RC-6A */
+ params.modulation = false;
+ /* RC-5: 2,222,222 ns = 1/36 kHz * 32 cycles * 2 marks * 1.25*/
+ /* RC-6A: 3,333,333 ns = 1/36 kHz * 16 cycles * 6 marks * 1.25*/
+ params.max_pulse_width = 3333333; /* ns */
+ /* RC-5: 666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */
+ /* RC-6A: 333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */
+ params.noise_filter_min_width = 333333; /* ns */
+ /*
+ * This board has inverted receive sense:
+ * mark is received as low logic level;
+ * falling edges are detected as rising edges; etc.
+ */
+ params.invert_level = true;
+ break;
+ case CX23885_BOARD_TEVII_S470:
+ /*
+ * The IR controller on this board only returns pulse widths.
+ * Any other mode setting will fail to set up the device.
+ */
+ params.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+ params.enable = true;
+ params.interrupt_enable = true;
+ params.shutdown = false;
+
+ /* Setup for a standard NEC protocol */
+ params.carrier_freq = 37917; /* Hz, 455 kHz/12 for NEC */
+ params.carrier_range_lower = 33000; /* Hz */
+ params.carrier_range_upper = 43000; /* Hz */
+ params.duty_cycle = 33; /* percent, 33 percent for NEC */
+
+ /*
+ * NEC max pulse width: (64/3)/(455 kHz/12) * 16 nec_units
+ * (64/3)/(455 kHz/12) * 16 nec_units * 1.375 = 12378022 ns
+ */
+ params.max_pulse_width = 12378022; /* ns */
+
+ /*
+ * NEC noise filter min width: (64/3)/(455 kHz/12) * 1 nec_unit
+ * (64/3)/(455 kHz/12) * 1 nec_units * 0.625 = 351648 ns
+ */
+ params.noise_filter_min_width = 351648; /* ns */
+
+ params.modulation = false;
+ params.invert_level = true;
+ break;
+ }
+ v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
+ return 0;
+}
+
+static int cx23885_input_ir_open(struct rc_dev *rc)
+{
+ struct cx23885_kernel_ir *kernel_ir = rc->priv;
+
+ if (kernel_ir->cx == NULL)
+ return -ENODEV;
+
+ return cx23885_input_ir_start(kernel_ir->cx);
+}
+
+static void cx23885_input_ir_stop(struct cx23885_dev *dev)
+{
+ struct v4l2_subdev_ir_parameters params;
+
+ if (dev->sd_ir == NULL)
+ return;
+
+ /*
+ * Stop the sd_ir subdevice from generating notifications and
+ * scheduling work.
+ * It is shutdown this way in order to mitigate a race with
+ * cx23885_input_rx_work_handler() in the overrun case, which could
+ * re-enable the subdevice.
+ */
+ atomic_set(&dev->ir_input_stopping, 1);
+ v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
+ while (params.shutdown == false) {
+ params.enable = false;
+ params.interrupt_enable = false;
+ params.shutdown = true;
+ v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
+ v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
+ }
+ flush_work_sync(&dev->cx25840_work);
+ flush_work_sync(&dev->ir_rx_work);
+ flush_work_sync(&dev->ir_tx_work);
+}
+
+static void cx23885_input_ir_close(struct rc_dev *rc)
+{
+ struct cx23885_kernel_ir *kernel_ir = rc->priv;
+
+ if (kernel_ir->cx != NULL)
+ cx23885_input_ir_stop(kernel_ir->cx);
+}
+
+int cx23885_input_init(struct cx23885_dev *dev)
+{
+ struct cx23885_kernel_ir *kernel_ir;
+ struct rc_dev *rc;
+ char *rc_map;
+ enum rc_driver_type driver_type;
+ unsigned long allowed_protos;
+
+ int ret;
+
+ /*
+ * If the IR device (hardware registers, chip, GPIO lines, etc.) isn't
+ * encapsulated in a v4l2_subdev, then I'm not going to deal with it.
+ */
+ if (dev->sd_ir == NULL)
+ return -ENODEV;
+
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ /* Integrated CX2388[58] IR controller */
+ driver_type = RC_DRIVER_IR_RAW;
+ allowed_protos = RC_TYPE_ALL;
+ /* The grey Hauppauge RC-5 remote */
+ rc_map = RC_MAP_HAUPPAUGE;
+ break;
+ case CX23885_BOARD_TEVII_S470:
+ /* Integrated CX23885 IR controller */
+ driver_type = RC_DRIVER_IR_RAW;
+ allowed_protos = RC_TYPE_ALL;
+ /* A guess at the remote */
+ rc_map = RC_MAP_TEVII_NEC;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ /* cx23885 board instance kernel IR state */
+ kernel_ir = kzalloc(sizeof(struct cx23885_kernel_ir), GFP_KERNEL);
+ if (kernel_ir == NULL)
+ return -ENOMEM;
+
+ kernel_ir->cx = dev;
+ kernel_ir->name = kasprintf(GFP_KERNEL, "cx23885 IR (%s)",
+ cx23885_boards[dev->board].name);
+ kernel_ir->phys = kasprintf(GFP_KERNEL, "pci-%s/ir0",
+ pci_name(dev->pci));
+
+ /* input device */
+ rc = rc_allocate_device();
+ if (!rc) {
+ ret = -ENOMEM;
+ goto err_out_free;
+ }
+
+ kernel_ir->rc = rc;
+ rc->input_name = kernel_ir->name;
+ rc->input_phys = kernel_ir->phys;
+ rc->input_id.bustype = BUS_PCI;
+ rc->input_id.version = 1;
+ if (dev->pci->subsystem_vendor) {
+ rc->input_id.vendor = dev->pci->subsystem_vendor;
+ rc->input_id.product = dev->pci->subsystem_device;
+ } else {
+ rc->input_id.vendor = dev->pci->vendor;
+ rc->input_id.product = dev->pci->device;
+ }
+ rc->dev.parent = &dev->pci->dev;
+ rc->driver_type = driver_type;
+ rc->allowed_protos = allowed_protos;
+ rc->priv = kernel_ir;
+ rc->open = cx23885_input_ir_open;
+ rc->close = cx23885_input_ir_close;
+ rc->map_name = rc_map;
+ rc->driver_name = MODULE_NAME;
+
+ /* Go */
+ dev->kernel_ir = kernel_ir;
+ ret = rc_register_device(rc);
+ if (ret)
+ goto err_out_stop;
+
+ return 0;
+
+err_out_stop:
+ cx23885_input_ir_stop(dev);
+ dev->kernel_ir = NULL;
+ rc_free_device(rc);
+err_out_free:
+ kfree(kernel_ir->phys);
+ kfree(kernel_ir->name);
+ kfree(kernel_ir);
+ return ret;
+}
+
+void cx23885_input_fini(struct cx23885_dev *dev)
+{
+ /* Always stop the IR hardware from generating interrupts */
+ cx23885_input_ir_stop(dev);
+
+ if (dev->kernel_ir == NULL)
+ return;
+ rc_unregister_device(dev->kernel_ir->rc);
+ kfree(dev->kernel_ir->phys);
+ kfree(dev->kernel_ir->name);
+ kfree(dev->kernel_ir);
+ dev->kernel_ir = NULL;
+}
diff --git a/drivers/media/video/cx23885/cx23885-input.h b/drivers/media/video/cx23885/cx23885-input.h
new file mode 100644
index 00000000..75ef15d3
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-input.h
@@ -0,0 +1,30 @@
+/*
+ * Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ * Infrared remote control input device
+ *
+ * Copyright (C) 2009 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef _CX23885_INPUT_H_
+#define _CX23885_INPUT_H_
+int cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events);
+
+int cx23885_input_init(struct cx23885_dev *dev);
+void cx23885_input_fini(struct cx23885_dev *dev);
+#endif
diff --git a/drivers/media/video/cx23885/cx23885-ioctl.c b/drivers/media/video/cx23885/cx23885-ioctl.c
new file mode 100644
index 00000000..44812ca7
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-ioctl.c
@@ -0,0 +1,208 @@
+/*
+ * Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ * Various common ioctl() support functions
+ *
+ * Copyright (c) 2009 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "cx23885.h"
+#include <media/v4l2-chip-ident.h>
+
+int cx23885_g_chip_ident(struct file *file, void *fh,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
+ int err = 0;
+ u8 rev;
+
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+ switch (chip->match.type) {
+ case V4L2_CHIP_MATCH_HOST:
+ switch (chip->match.addr) {
+ case 0:
+ rev = cx_read(RDR_CFG2) & 0xff;
+ switch (dev->pci->device) {
+ case 0x8852:
+ /* rev 0x04 could be '885 or '888. Pick '888. */
+ if (rev == 0x04)
+ chip->ident = V4L2_IDENT_CX23888;
+ else
+ chip->ident = V4L2_IDENT_CX23885;
+ break;
+ case 0x8880:
+ if (rev == 0x0e || rev == 0x0f)
+ chip->ident = V4L2_IDENT_CX23887;
+ else
+ chip->ident = V4L2_IDENT_CX23888;
+ break;
+ default:
+ chip->ident = V4L2_IDENT_UNKNOWN;
+ break;
+ }
+ chip->revision = (dev->pci->device << 16) | (rev << 8) |
+ (dev->hwrevision & 0xff);
+ break;
+ case 1:
+ if (dev->v4l_device != NULL) {
+ chip->ident = V4L2_IDENT_CX23417;
+ chip->revision = 0;
+ }
+ break;
+ case 2:
+ /*
+ * The integrated IR controller on the CX23888 is
+ * host chip 2. It may not be used/initialized or sd_ir
+ * may be pointing at the cx25840 subdevice for the
+ * IR controller on the CX23885. Thus we find it
+ * without using the dev->sd_ir pointer.
+ */
+ call_hw(dev, CX23885_HW_888_IR, core, g_chip_ident,
+ chip);
+ break;
+ default:
+ err = -EINVAL; /* per V4L2 spec */
+ break;
+ }
+ break;
+ case V4L2_CHIP_MATCH_I2C_DRIVER:
+ /* If needed, returns V4L2_IDENT_AMBIGUOUS without extra work */
+ call_all(dev, core, g_chip_ident, chip);
+ break;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ /*
+ * We could return V4L2_IDENT_UNKNOWN, but we don't do the work
+ * to look if a chip is at the address with no driver. That's a
+ * dangerous thing to do with EEPROMs anyway.
+ */
+ call_all(dev, core, g_chip_ident, chip);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ return err;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int cx23885_g_host_register(struct cx23885_dev *dev,
+ struct v4l2_dbg_register *reg)
+{
+ if ((reg->reg & 0x3) != 0 || reg->reg >= pci_resource_len(dev->pci, 0))
+ return -EINVAL;
+
+ reg->size = 4;
+ reg->val = cx_read(reg->reg);
+ return 0;
+}
+
+static int cx23417_g_register(struct cx23885_dev *dev,
+ struct v4l2_dbg_register *reg)
+{
+ u32 value;
+
+ if (dev->v4l_device == NULL)
+ return -EINVAL;
+
+ if ((reg->reg & 0x3) != 0 || reg->reg >= 0x10000)
+ return -EINVAL;
+
+ if (mc417_register_read(dev, (u16) reg->reg, &value))
+ return -EINVAL; /* V4L2 spec, but -EREMOTEIO really */
+
+ reg->size = 4;
+ reg->val = value;
+ return 0;
+}
+
+int cx23885_g_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (reg->match.type == V4L2_CHIP_MATCH_HOST) {
+ switch (reg->match.addr) {
+ case 0:
+ return cx23885_g_host_register(dev, reg);
+ case 1:
+ return cx23417_g_register(dev, reg);
+ default:
+ break;
+ }
+ }
+
+ /* FIXME - any error returns should not be ignored */
+ call_all(dev, core, g_register, reg);
+ return 0;
+}
+
+static int cx23885_s_host_register(struct cx23885_dev *dev,
+ struct v4l2_dbg_register *reg)
+{
+ if ((reg->reg & 0x3) != 0 || reg->reg >= pci_resource_len(dev->pci, 0))
+ return -EINVAL;
+
+ reg->size = 4;
+ cx_write(reg->reg, reg->val);
+ return 0;
+}
+
+static int cx23417_s_register(struct cx23885_dev *dev,
+ struct v4l2_dbg_register *reg)
+{
+ if (dev->v4l_device == NULL)
+ return -EINVAL;
+
+ if ((reg->reg & 0x3) != 0 || reg->reg >= 0x10000)
+ return -EINVAL;
+
+ if (mc417_register_write(dev, (u16) reg->reg, (u32) reg->val))
+ return -EINVAL; /* V4L2 spec, but -EREMOTEIO really */
+
+ reg->size = 4;
+ return 0;
+}
+
+int cx23885_s_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (reg->match.type == V4L2_CHIP_MATCH_HOST) {
+ switch (reg->match.addr) {
+ case 0:
+ return cx23885_s_host_register(dev, reg);
+ case 1:
+ return cx23417_s_register(dev, reg);
+ default:
+ break;
+ }
+ }
+
+ /* FIXME - any error returns should not be ignored */
+ call_all(dev, core, s_register, reg);
+ return 0;
+}
+#endif
diff --git a/drivers/media/video/cx23885/cx23885-ioctl.h b/drivers/media/video/cx23885/cx23885-ioctl.h
new file mode 100644
index 00000000..315be0ca
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-ioctl.h
@@ -0,0 +1,39 @@
+/*
+ * Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ * Various common ioctl() support functions
+ *
+ * Copyright (c) 2009 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _CX23885_IOCTL_H_
+#define _CX23885_IOCTL_H_
+
+int cx23885_g_chip_ident(struct file *file, void *fh,
+ struct v4l2_dbg_chip_ident *chip);
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+int cx23885_g_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg);
+
+
+int cx23885_s_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg);
+
+#endif
+#endif
diff --git a/drivers/media/video/cx23885/cx23885-ir.c b/drivers/media/video/cx23885/cx23885-ir.c
new file mode 100644
index 00000000..7125247d
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-ir.c
@@ -0,0 +1,117 @@
+/*
+ * Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ * Infrared device support routines - non-input, non-vl42_subdev routines
+ *
+ * Copyright (C) 2009 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <media/v4l2-device.h>
+
+#include "cx23885.h"
+#include "cx23885-input.h"
+
+#define CX23885_IR_RX_FIFO_SERVICE_REQ 0
+#define CX23885_IR_RX_END_OF_RX_DETECTED 1
+#define CX23885_IR_RX_HW_FIFO_OVERRUN 2
+#define CX23885_IR_RX_SW_FIFO_OVERRUN 3
+
+#define CX23885_IR_TX_FIFO_SERVICE_REQ 0
+
+
+void cx23885_ir_rx_work_handler(struct work_struct *work)
+{
+ struct cx23885_dev *dev =
+ container_of(work, struct cx23885_dev, ir_rx_work);
+ u32 events = 0;
+ unsigned long *notifications = &dev->ir_rx_notifications;
+
+ if (test_and_clear_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, notifications))
+ events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN;
+ if (test_and_clear_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, notifications))
+ events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN;
+ if (test_and_clear_bit(CX23885_IR_RX_END_OF_RX_DETECTED, notifications))
+ events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED;
+ if (test_and_clear_bit(CX23885_IR_RX_FIFO_SERVICE_REQ, notifications))
+ events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ;
+
+ if (events == 0)
+ return;
+
+ if (dev->kernel_ir)
+ cx23885_input_rx_work_handler(dev, events);
+}
+
+void cx23885_ir_tx_work_handler(struct work_struct *work)
+{
+ struct cx23885_dev *dev =
+ container_of(work, struct cx23885_dev, ir_tx_work);
+ u32 events = 0;
+ unsigned long *notifications = &dev->ir_tx_notifications;
+
+ if (test_and_clear_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, notifications))
+ events |= V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ;
+
+ if (events == 0)
+ return;
+
+}
+
+/* Possibly called in an IRQ context */
+void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
+{
+ struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev);
+ unsigned long *notifications = &dev->ir_rx_notifications;
+
+ if (events & V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ)
+ set_bit(CX23885_IR_RX_FIFO_SERVICE_REQ, notifications);
+ if (events & V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED)
+ set_bit(CX23885_IR_RX_END_OF_RX_DETECTED, notifications);
+ if (events & V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN)
+ set_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, notifications);
+ if (events & V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN)
+ set_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, notifications);
+
+ /*
+ * For the integrated AV core, we are already in a workqueue context.
+ * For the CX23888 integrated IR, we are in an interrupt context.
+ */
+ if (sd == dev->sd_cx25840)
+ cx23885_ir_rx_work_handler(&dev->ir_rx_work);
+ else
+ schedule_work(&dev->ir_rx_work);
+}
+
+/* Possibly called in an IRQ context */
+void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
+{
+ struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev);
+ unsigned long *notifications = &dev->ir_tx_notifications;
+
+ if (events & V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ)
+ set_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, notifications);
+
+ /*
+ * For the integrated AV core, we are already in a workqueue context.
+ * For the CX23888 integrated IR, we are in an interrupt context.
+ */
+ if (sd == dev->sd_cx25840)
+ cx23885_ir_tx_work_handler(&dev->ir_tx_work);
+ else
+ schedule_work(&dev->ir_tx_work);
+}
diff --git a/drivers/media/video/cx23885/cx23885-ir.h b/drivers/media/video/cx23885/cx23885-ir.h
new file mode 100644
index 00000000..0c9d8bda
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-ir.h
@@ -0,0 +1,31 @@
+/*
+ * Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ * Infrared device support routines - non-input, non-vl42_subdev routines
+ *
+ * Copyright (C) 2009 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef _CX23885_IR_H_
+#define _CX23885_IR_H_
+void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events);
+void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events);
+
+void cx23885_ir_rx_work_handler(struct work_struct *work);
+void cx23885_ir_tx_work_handler(struct work_struct *work);
+#endif
diff --git a/drivers/media/video/cx23885/cx23885-reg.h b/drivers/media/video/cx23885/cx23885-reg.h
new file mode 100644
index 00000000..a99936e0
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-reg.h
@@ -0,0 +1,452 @@
+/*
+ * Driver for the Conexant CX23885 PCIe bridge
+ *
+ * Copyright (c) 2006 Steven Toth <stoth@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _CX23885_REG_H_
+#define _CX23885_REG_H_
+
+/*
+Address Map
+0x00000000 -> 0x00009000 TX SRAM (Fifos)
+0x00010000 -> 0x00013c00 RX SRAM CMDS + CDT
+
+EACH CMDS struct is 0x80 bytes long
+
+DMAx_PTR1 = 0x03040 address of first cluster
+DMAx_PTR2 = 0x10600 address of the CDT
+DMAx_CNT1 = cluster size in (bytes >> 4) -1
+DMAx_CNT2 = total cdt size for all entries >> 3
+
+Cluster Descriptor entry = 4 DWORDS
+ DWORD 0 -> ptr to cluster
+ DWORD 1 Reserved
+ DWORD 2 Reserved
+ DWORD 3 Reserved
+
+Channel manager Data Structure entry = 20 DWORD
+ 0 IntialProgramCounterLow
+ 1 IntialProgramCounterHigh
+ 2 ClusterDescriptorTableBase
+ 3 ClusterDescriptorTableSize
+ 4 InstructionQueueBase
+ 5 InstructionQueueSize
+... Reserved
+ 19 Reserved
+*/
+
+/* Risc Instructions */
+#define RISC_CNT_INC 0x00010000
+#define RISC_CNT_RESET 0x00030000
+#define RISC_IRQ1 0x01000000
+#define RISC_IRQ2 0x02000000
+#define RISC_EOL 0x04000000
+#define RISC_SOL 0x08000000
+#define RISC_WRITE 0x10000000
+#define RISC_SKIP 0x20000000
+#define RISC_JUMP 0x70000000
+#define RISC_SYNC 0x80000000
+#define RISC_RESYNC 0x80008000
+#define RISC_READ 0x90000000
+#define RISC_WRITERM 0xB0000000
+#define RISC_WRITECM 0xC0000000
+#define RISC_WRITECR 0xD0000000
+#define RISC_WRITEC 0x50000000
+#define RISC_READC 0xA0000000
+
+
+/* Audio and Video Core */
+#define HOST_REG1 0x00000000
+#define HOST_REG2 0x00000001
+#define HOST_REG3 0x00000002
+
+/* Chip Configuration Registers */
+#define CHIP_CTRL 0x00000100
+#define AFE_CTRL 0x00000104
+#define VID_PLL_INT_POST 0x00000108
+#define VID_PLL_FRAC 0x0000010C
+#define AUX_PLL_INT_POST 0x00000110
+#define AUX_PLL_FRAC 0x00000114
+#define SYS_PLL_INT_POST 0x00000118
+#define SYS_PLL_FRAC 0x0000011C
+#define PIN_CTRL 0x00000120
+#define AUD_IO_CTRL 0x00000124
+#define AUD_LOCK1 0x00000128
+#define AUD_LOCK2 0x0000012C
+#define POWER_CTRL 0x00000130
+#define AFE_DIAG_CTRL1 0x00000134
+#define AFE_DIAG_CTRL3 0x0000013C
+#define PLL_DIAG_CTRL 0x00000140
+#define AFE_CLK_OUT_CTRL 0x00000144
+#define DLL1_DIAG_CTRL 0x0000015C
+
+/* GPIO[23:19] Output Enable */
+#define GPIO2_OUT_EN_REG 0x00000160
+/* GPIO[23:19] Data Registers */
+#define GPIO2 0x00000164
+
+#define IFADC_CTRL 0x00000180
+
+/* Infrared Remote Registers */
+#define IR_CNTRL_REG 0x00000200
+#define IR_TXCLK_REG 0x00000204
+#define IR_RXCLK_REG 0x00000208
+#define IR_CDUTY_REG 0x0000020C
+#define IR_STAT_REG 0x00000210
+#define IR_IRQEN_REG 0x00000214
+#define IR_FILTR_REG 0x00000218
+#define IR_FIFO_REG 0x0000023C
+
+/* Video Decoder Registers */
+#define MODE_CTRL 0x00000400
+#define OUT_CTRL1 0x00000404
+#define OUT_CTRL2 0x00000408
+#define GEN_STAT 0x0000040C
+#define INT_STAT_MASK 0x00000410
+#define LUMA_CTRL 0x00000414
+#define HSCALE_CTRL 0x00000418
+#define VSCALE_CTRL 0x0000041C
+#define CHROMA_CTRL 0x00000420
+#define VBI_LINE_CTRL1 0x00000424
+#define VBI_LINE_CTRL2 0x00000428
+#define VBI_LINE_CTRL3 0x0000042C
+#define VBI_LINE_CTRL4 0x00000430
+#define VBI_LINE_CTRL5 0x00000434
+#define VBI_FC_CFG 0x00000438
+#define VBI_MISC_CFG1 0x0000043C
+#define VBI_MISC_CFG2 0x00000440
+#define VBI_PAY1 0x00000444
+#define VBI_PAY2 0x00000448
+#define VBI_CUST1_CFG1 0x0000044C
+#define VBI_CUST1_CFG2 0x00000450
+#define VBI_CUST1_CFG3 0x00000454
+#define VBI_CUST2_CFG1 0x00000458
+#define VBI_CUST2_CFG2 0x0000045C
+#define VBI_CUST2_CFG3 0x00000460
+#define VBI_CUST3_CFG1 0x00000464
+#define VBI_CUST3_CFG2 0x00000468
+#define VBI_CUST3_CFG3 0x0000046C
+#define HORIZ_TIM_CTRL 0x00000470
+#define VERT_TIM_CTRL 0x00000474
+#define SRC_COMB_CFG 0x00000478
+#define CHROMA_VBIOFF_CFG 0x0000047C
+#define FIELD_COUNT 0x00000480
+#define MISC_TIM_CTRL 0x00000484
+#define DFE_CTRL1 0x00000488
+#define DFE_CTRL2 0x0000048C
+#define DFE_CTRL3 0x00000490
+#define PLL_CTRL 0x00000494
+#define HTL_CTRL 0x00000498
+#define COMB_CTRL 0x0000049C
+#define CRUSH_CTRL 0x000004A0
+#define SOFT_RST_CTRL 0x000004A4
+#define CX885_VERSION 0x000004B4
+#define VBI_PASS_CTRL 0x000004BC
+
+/* Audio Decoder Registers */
+/* 8051 Configuration */
+#define DL_CTL 0x00000800
+#define STD_DET_STATUS 0x00000804
+#define STD_DET_CTL 0x00000808
+#define DW8051_INT 0x0000080C
+#define GENERAL_CTL 0x00000810
+#define AAGC_CTL 0x00000814
+#define DEMATRIX_CTL 0x000008CC
+#define PATH1_CTL1 0x000008D0
+#define PATH1_VOL_CTL 0x000008D4
+#define PATH1_EQ_CTL 0x000008D8
+#define PATH1_SC_CTL 0x000008DC
+#define PATH2_CTL1 0x000008E0
+#define PATH2_VOL_CTL 0x000008E4
+#define PATH2_EQ_CTL 0x000008E8
+#define PATH2_SC_CTL 0x000008EC
+
+/* Sample Rate Converter */
+#define SRC_CTL 0x000008F0
+#define SRC_LF_COEF 0x000008F4
+#define SRC1_CTL 0x000008F8
+#define SRC2_CTL 0x000008FC
+#define SRC3_CTL 0x00000900
+#define SRC4_CTL 0x00000904
+#define SRC5_CTL 0x00000908
+#define SRC6_CTL 0x0000090C
+#define BAND_OUT_SEL 0x00000910
+#define I2S_N_CTL 0x00000914
+#define I2S_OUT_CTL 0x00000918
+#define AUTOCONFIG_REG 0x000009C4
+
+/* Audio ADC Registers */
+#define DSM_CTRL1 0x00000000
+#define DSM_CTRL2 0x00000001
+#define CHP_EN_CTRL 0x00000002
+#define CHP_CLK_CTRL1 0x00000004
+#define CHP_CLK_CTRL2 0x00000005
+#define BG_REF_CTRL 0x00000006
+#define SD2_SW_CTRL1 0x00000008
+#define SD2_SW_CTRL2 0x00000009
+#define SD2_BIAS_CTRL 0x0000000A
+#define AMP_BIAS_CTRL 0x0000000C
+#define CH_PWR_CTRL1 0x0000000E
+#define FLD_CH_SEL (1 << 3)
+#define CH_PWR_CTRL2 0x0000000F
+#define DSM_STATUS1 0x00000010
+#define DSM_STATUS2 0x00000011
+#define DIG_CTL1 0x00000012
+#define DIG_CTL2 0x00000013
+#define I2S_TX_CFG 0x0000001A
+
+#define DEV_CNTRL2 0x00040000
+
+#define PCI_MSK_IR (1 << 28)
+#define PCI_MSK_AV_CORE (1 << 27)
+#define PCI_MSK_GPIO1 (1 << 24)
+#define PCI_MSK_GPIO0 (1 << 23)
+#define PCI_MSK_APB_DMA (1 << 12)
+#define PCI_MSK_AL_WR (1 << 11)
+#define PCI_MSK_AL_RD (1 << 10)
+#define PCI_MSK_RISC_WR (1 << 9)
+#define PCI_MSK_RISC_RD (1 << 8)
+#define PCI_MSK_AUD_EXT (1 << 4)
+#define PCI_MSK_AUD_INT (1 << 3)
+#define PCI_MSK_VID_C (1 << 2)
+#define PCI_MSK_VID_B (1 << 1)
+#define PCI_MSK_VID_A 1
+#define PCI_INT_MSK 0x00040010
+
+#define PCI_INT_STAT 0x00040014
+#define PCI_INT_MSTAT 0x00040018
+
+#define VID_A_INT_MSK 0x00040020
+#define VID_A_INT_STAT 0x00040024
+#define VID_A_INT_MSTAT 0x00040028
+#define VID_A_INT_SSTAT 0x0004002C
+
+#define VID_B_INT_MSK 0x00040030
+#define VID_B_MSK_BAD_PKT (1 << 20)
+#define VID_B_MSK_VBI_OPC_ERR (1 << 17)
+#define VID_B_MSK_OPC_ERR (1 << 16)
+#define VID_B_MSK_VBI_SYNC (1 << 13)
+#define VID_B_MSK_SYNC (1 << 12)
+#define VID_B_MSK_VBI_OF (1 << 9)
+#define VID_B_MSK_OF (1 << 8)
+#define VID_B_MSK_VBI_RISCI2 (1 << 5)
+#define VID_B_MSK_RISCI2 (1 << 4)
+#define VID_B_MSK_VBI_RISCI1 (1 << 1)
+#define VID_B_MSK_RISCI1 1
+#define VID_B_INT_STAT 0x00040034
+#define VID_B_INT_MSTAT 0x00040038
+#define VID_B_INT_SSTAT 0x0004003C
+
+#define VID_B_MSK_BAD_PKT (1 << 20)
+#define VID_B_MSK_OPC_ERR (1 << 16)
+#define VID_B_MSK_SYNC (1 << 12)
+#define VID_B_MSK_OF (1 << 8)
+#define VID_B_MSK_RISCI2 (1 << 4)
+#define VID_B_MSK_RISCI1 1
+
+#define VID_C_MSK_BAD_PKT (1 << 20)
+#define VID_C_MSK_OPC_ERR (1 << 16)
+#define VID_C_MSK_SYNC (1 << 12)
+#define VID_C_MSK_OF (1 << 8)
+#define VID_C_MSK_RISCI2 (1 << 4)
+#define VID_C_MSK_RISCI1 1
+
+/* A superset for testing purposes */
+#define VID_BC_MSK_BAD_PKT (1 << 20)
+#define VID_BC_MSK_OPC_ERR (1 << 16)
+#define VID_BC_MSK_SYNC (1 << 12)
+#define VID_BC_MSK_OF (1 << 8)
+#define VID_BC_MSK_VBI_RISCI2 (1 << 5)
+#define VID_BC_MSK_RISCI2 (1 << 4)
+#define VID_BC_MSK_VBI_RISCI1 (1 << 1)
+#define VID_BC_MSK_RISCI1 1
+
+#define VID_C_INT_MSK 0x00040040
+#define VID_C_INT_STAT 0x00040044
+#define VID_C_INT_MSTAT 0x00040048
+#define VID_C_INT_SSTAT 0x0004004C
+
+#define AUDIO_INT_INT_MSK 0x00040050
+#define AUDIO_INT_INT_STAT 0x00040054
+#define AUDIO_INT_INT_MSTAT 0x00040058
+#define AUDIO_INT_INT_SSTAT 0x0004005C
+
+#define AUDIO_EXT_INT_MSK 0x00040060
+#define AUDIO_EXT_INT_STAT 0x00040064
+#define AUDIO_EXT_INT_MSTAT 0x00040068
+#define AUDIO_EXT_INT_SSTAT 0x0004006C
+
+#define RDR_CFG0 0x00050000
+#define RDR_CFG1 0x00050004
+#define RDR_CFG2 0x00050008
+#define RDR_RDRCTL1 0x0005030c
+#define RDR_TLCTL0 0x00050318
+
+/* APB DMAC Current Buffer Pointer */
+#define DMA1_PTR1 0x00100000
+#define DMA2_PTR1 0x00100004
+#define DMA3_PTR1 0x00100008
+#define DMA4_PTR1 0x0010000C
+#define DMA5_PTR1 0x00100010
+#define DMA6_PTR1 0x00100014
+#define DMA7_PTR1 0x00100018
+#define DMA8_PTR1 0x0010001C
+
+/* APB DMAC Current Table Pointer */
+#define DMA1_PTR2 0x00100040
+#define DMA2_PTR2 0x00100044
+#define DMA3_PTR2 0x00100048
+#define DMA4_PTR2 0x0010004C
+#define DMA5_PTR2 0x00100050
+#define DMA6_PTR2 0x00100054
+#define DMA7_PTR2 0x00100058
+#define DMA8_PTR2 0x0010005C
+
+/* APB DMAC Buffer Limit */
+#define DMA1_CNT1 0x00100080
+#define DMA2_CNT1 0x00100084
+#define DMA3_CNT1 0x00100088
+#define DMA4_CNT1 0x0010008C
+#define DMA5_CNT1 0x00100090
+#define DMA6_CNT1 0x00100094
+#define DMA7_CNT1 0x00100098
+#define DMA8_CNT1 0x0010009C
+
+/* APB DMAC Table Size */
+#define DMA1_CNT2 0x001000C0
+#define DMA2_CNT2 0x001000C4
+#define DMA3_CNT2 0x001000C8
+#define DMA4_CNT2 0x001000CC
+#define DMA5_CNT2 0x001000D0
+#define DMA6_CNT2 0x001000D4
+#define DMA7_CNT2 0x001000D8
+#define DMA8_CNT2 0x001000DC
+
+/* Timer Counters */
+#define TM_CNT_LDW 0x00110000
+#define TM_CNT_UW 0x00110004
+#define TM_LMT_LDW 0x00110008
+#define TM_LMT_UW 0x0011000C
+
+/* GPIO */
+#define GP0_IO 0x00110010
+#define GPIO_ISM 0x00110014
+#define SOFT_RESET 0x0011001C
+
+/* GPIO (417 Microsoftcontroller) RW Data */
+#define MC417_RWD 0x00110020
+
+/* GPIO (417 Microsoftcontroller) Output Enable, Low Active */
+#define MC417_OEN 0x00110024
+#define MC417_CTL 0x00110028
+#define ALT_PIN_OUT_SEL 0x0011002C
+#define CLK_DELAY 0x00110048
+#define PAD_CTRL 0x0011004C
+
+/* Video A Interface */
+#define VID_A_GPCNT 0x00130020
+#define VBI_A_GPCNT 0x00130024
+#define VID_A_GPCNT_CTL 0x00130030
+#define VBI_A_GPCNT_CTL 0x00130034
+#define VID_A_DMA_CTL 0x00130040
+#define VID_A_VIP_CTRL 0x00130080
+#define VID_A_PIXEL_FRMT 0x00130084
+#define VID_A_VBI_CTRL 0x00130088
+
+/* Video B Interface */
+#define VID_B_DMA 0x00130100
+#define VBI_B_DMA 0x00130108
+#define VID_B_GPCNT 0x00130120
+#define VBI_B_GPCNT 0x00130124
+#define VID_B_GPCNT_CTL 0x00130134
+#define VBI_B_GPCNT_CTL 0x00130138
+#define VID_B_DMA_CTL 0x00130140
+#define VID_B_SRC_SEL 0x00130144
+#define VID_B_LNGTH 0x00130150
+#define VID_B_HW_SOP_CTL 0x00130154
+#define VID_B_GEN_CTL 0x00130158
+#define VID_B_BD_PKT_STATUS 0x0013015C
+#define VID_B_SOP_STATUS 0x00130160
+#define VID_B_FIFO_OVFL_STAT 0x00130164
+#define VID_B_VLD_MISC 0x00130168
+#define VID_B_TS_CLK_EN 0x0013016C
+#define VID_B_VIP_CTRL 0x00130180
+#define VID_B_PIXEL_FRMT 0x00130184
+
+/* Video C Interface */
+#define VID_C_GPCNT 0x00130220
+#define VID_C_GPCNT_CTL 0x00130230
+#define VBI_C_GPCNT_CTL 0x00130234
+#define VID_C_DMA_CTL 0x00130240
+#define VID_C_LNGTH 0x00130250
+#define VID_C_HW_SOP_CTL 0x00130254
+#define VID_C_GEN_CTL 0x00130258
+#define VID_C_BD_PKT_STATUS 0x0013025C
+#define VID_C_SOP_STATUS 0x00130260
+#define VID_C_FIFO_OVFL_STAT 0x00130264
+#define VID_C_VLD_MISC 0x00130268
+#define VID_C_TS_CLK_EN 0x0013026C
+
+/* Internal Audio Interface */
+#define AUD_INT_A_GPCNT 0x00140020
+#define AUD_INT_B_GPCNT 0x00140024
+#define AUD_INT_A_GPCNT_CTL 0x00140030
+#define AUD_INT_B_GPCNT_CTL 0x00140034
+#define AUD_INT_DMA_CTL 0x00140040
+#define AUD_INT_A_LNGTH 0x00140050
+#define AUD_INT_B_LNGTH 0x00140054
+#define AUD_INT_A_MODE 0x00140058
+#define AUD_INT_B_MODE 0x0014005C
+
+/* External Audio Interface */
+#define AUD_EXT_DMA 0x00140100
+#define AUD_EXT_GPCNT 0x00140120
+#define AUD_EXT_GPCNT_CTL 0x00140130
+#define AUD_EXT_DMA_CTL 0x00140140
+#define AUD_EXT_LNGTH 0x00140150
+#define AUD_EXT_A_MODE 0x00140158
+
+/* I2C Bus 1 */
+#define I2C1_ADDR 0x00180000
+#define I2C1_WDATA 0x00180004
+#define I2C1_CTRL 0x00180008
+#define I2C1_RDATA 0x0018000C
+#define I2C1_STAT 0x00180010
+
+/* I2C Bus 2 */
+#define I2C2_ADDR 0x00190000
+#define I2C2_WDATA 0x00190004
+#define I2C2_CTRL 0x00190008
+#define I2C2_RDATA 0x0019000C
+#define I2C2_STAT 0x00190010
+
+/* I2C Bus 3 */
+#define I2C3_ADDR 0x001A0000
+#define I2C3_WDATA 0x001A0004
+#define I2C3_CTRL 0x001A0008
+#define I2C3_RDATA 0x001A000C
+#define I2C3_STAT 0x001A0010
+
+/* UART */
+#define UART_CTL 0x001B0000
+#define UART_BRD 0x001B0004
+#define UART_ISR 0x001B000C
+#define UART_CNT 0x001B0010
+
+#endif /* _CX23885_REG_H_ */
diff --git a/drivers/media/video/cx23885/cx23885-vbi.c b/drivers/media/video/cx23885/cx23885-vbi.c
new file mode 100644
index 00000000..a1154f03
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-vbi.c
@@ -0,0 +1,295 @@
+/*
+ * Driver for the Conexant CX23885 PCIe bridge
+ *
+ * Copyright (c) 2007 Steven Toth <stoth@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+
+#include "cx23885.h"
+
+static unsigned int vbibufs = 4;
+module_param(vbibufs, int, 0644);
+MODULE_PARM_DESC(vbibufs, "number of vbi buffers, range 2-32");
+
+static unsigned int vbi_debug;
+module_param(vbi_debug, int, 0644);
+MODULE_PARM_DESC(vbi_debug, "enable debug messages [vbi]");
+
+#define dprintk(level, fmt, arg...)\
+ do { if (vbi_debug >= level)\
+ printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\
+ } while (0)
+
+/* ------------------------------------------------------------------ */
+
+#define VBI_LINE_LENGTH 1440
+#define NTSC_VBI_START_LINE 10 /* line 10 - 21 */
+#define NTSC_VBI_END_LINE 21
+#define NTSC_VBI_LINES (NTSC_VBI_END_LINE - NTSC_VBI_START_LINE + 1)
+
+
+int cx23885_vbi_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+
+ if (dev->tvnorm & V4L2_STD_525_60) {
+ /* ntsc */
+ f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
+ f->fmt.vbi.sampling_rate = 27000000;
+ f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+ f->fmt.vbi.offset = 0;
+ f->fmt.vbi.flags = 0;
+ f->fmt.vbi.start[0] = 10;
+ f->fmt.vbi.count[0] = 17;
+ f->fmt.vbi.start[1] = 263 + 10 + 1;
+ f->fmt.vbi.count[1] = 17;
+ } else if (dev->tvnorm & V4L2_STD_625_50) {
+ /* pal */
+ f->fmt.vbi.sampling_rate = 35468950;
+ f->fmt.vbi.start[0] = 7 - 1;
+ f->fmt.vbi.start[1] = 319 - 1;
+ }
+
+ return 0;
+}
+
+/* We're given the Video Interrupt status register.
+ * The cx23885_video_irq() func has already validated
+ * the potential error bits, we just need to
+ * deal with vbi payload and return indication if
+ * we actually processed any payload.
+ */
+int cx23885_vbi_irq(struct cx23885_dev *dev, u32 status)
+{
+ u32 count;
+ int handled = 0;
+
+ if (status & VID_BC_MSK_VBI_RISCI1) {
+ dprintk(1, "%s() VID_BC_MSK_VBI_RISCI1\n", __func__);
+ spin_lock(&dev->slock);
+ count = cx_read(VID_A_GPCNT);
+ cx23885_video_wakeup(dev, &dev->vbiq, count);
+ spin_unlock(&dev->slock);
+ handled++;
+ }
+
+ if (status & VID_BC_MSK_VBI_RISCI2) {
+ dprintk(1, "%s() VID_BC_MSK_VBI_RISCI2\n", __func__);
+ dprintk(2, "stopper vbi\n");
+ spin_lock(&dev->slock);
+ cx23885_restart_vbi_queue(dev, &dev->vbiq);
+ spin_unlock(&dev->slock);
+ handled++;
+ }
+
+ return handled;
+}
+
+static int cx23885_start_vbi_dma(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q,
+ struct cx23885_buffer *buf)
+{
+ dprintk(1, "%s()\n", __func__);
+
+ /* setup fifo + format */
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH02],
+ buf->vb.width, buf->risc.dma);
+
+ /* reset counter */
+ cx_write(VID_A_GPCNT_CTL, 3);
+ cx_write(VID_A_VBI_CTRL, 3);
+ cx_write(VBI_A_GPCNT_CTL, 3);
+ q->count = 1;
+
+ /* enable irq */
+ cx23885_irq_add_enable(dev, 0x01);
+ cx_set(VID_A_INT_MSK, 0x000022);
+
+ /* start dma */
+ cx_set(DEV_CNTRL2, (1<<5));
+ cx_set(VID_A_DMA_CTL, 0x22); /* FIFO and RISC enable */
+
+ return 0;
+}
+
+
+int cx23885_restart_vbi_queue(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q)
+{
+ struct cx23885_buffer *buf;
+ struct list_head *item;
+
+ if (list_empty(&q->active))
+ return 0;
+
+ buf = list_entry(q->active.next, struct cx23885_buffer, vb.queue);
+ dprintk(2, "restart_queue [%p/%d]: restart dma\n",
+ buf, buf->vb.i);
+ cx23885_start_vbi_dma(dev, q, buf);
+ list_for_each(item, &q->active) {
+ buf = list_entry(item, struct cx23885_buffer, vb.queue);
+ buf->count = q->count++;
+ }
+ mod_timer(&q->timeout, jiffies + (BUFFER_TIMEOUT / 30));
+ return 0;
+}
+
+void cx23885_vbi_timeout(unsigned long data)
+{
+ struct cx23885_dev *dev = (struct cx23885_dev *)data;
+ struct cx23885_dmaqueue *q = &dev->vbiq;
+ struct cx23885_buffer *buf;
+ unsigned long flags;
+
+ /* Stop the VBI engine */
+ cx_clear(VID_A_DMA_CTL, 0x22);
+
+ spin_lock_irqsave(&dev->slock, flags);
+ while (!list_empty(&q->active)) {
+ buf = list_entry(q->active.next, struct cx23885_buffer,
+ vb.queue);
+ list_del(&buf->vb.queue);
+ buf->vb.state = VIDEOBUF_ERROR;
+ wake_up(&buf->vb.done);
+ printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", dev->name,
+ buf, buf->vb.i, (unsigned long)buf->risc.dma);
+ }
+ cx23885_restart_vbi_queue(dev, q);
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+/* ------------------------------------------------------------------ */
+#define VBI_LINE_LENGTH 1440
+#define VBI_LINE_COUNT 17
+
+static int
+vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+ *size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2;
+ if (0 == *count)
+ *count = vbibufs;
+ if (*count < 2)
+ *count = 2;
+ if (*count > 32)
+ *count = 32;
+ return 0;
+}
+
+static int
+vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct cx23885_fh *fh = q->priv_data;
+ struct cx23885_dev *dev = fh->dev;
+ struct cx23885_buffer *buf = container_of(vb,
+ struct cx23885_buffer, vb);
+ struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
+ unsigned int size;
+ int rc;
+
+ size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2;
+ if (0 != buf->vb.baddr && buf->vb.bsize < size)
+ return -EINVAL;
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ buf->vb.width = VBI_LINE_LENGTH;
+ buf->vb.height = VBI_LINE_COUNT;
+ buf->vb.size = size;
+ buf->vb.field = V4L2_FIELD_SEQ_TB;
+
+ rc = videobuf_iolock(q, &buf->vb, NULL);
+ if (0 != rc)
+ goto fail;
+ cx23885_risc_vbibuffer(dev->pci, &buf->risc,
+ dma->sglist,
+ 0, buf->vb.width * buf->vb.height,
+ buf->vb.width, 0,
+ buf->vb.height);
+ }
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+ fail:
+ cx23885_free_buffer(q, buf);
+ return rc;
+}
+
+static void
+vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct cx23885_buffer *buf =
+ container_of(vb, struct cx23885_buffer, vb);
+ struct cx23885_buffer *prev;
+ struct cx23885_fh *fh = vq->priv_data;
+ struct cx23885_dev *dev = fh->dev;
+ struct cx23885_dmaqueue *q = &dev->vbiq;
+
+ /* add jump to stopper */
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
+ buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
+ buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
+
+ if (list_empty(&q->active)) {
+ list_add_tail(&buf->vb.queue, &q->active);
+ cx23885_start_vbi_dma(dev, q, buf);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies + (BUFFER_TIMEOUT / 30));
+ dprintk(2, "[%p/%d] vbi_queue - first active\n",
+ buf, buf->vb.i);
+
+ } else {
+ prev = list_entry(q->active.prev, struct cx23885_buffer,
+ vb.queue);
+ list_add_tail(&buf->vb.queue, &q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63-32 */
+ dprintk(2, "[%p/%d] buffer_queue - append to active\n",
+ buf, buf->vb.i);
+ }
+}
+
+static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct cx23885_buffer *buf =
+ container_of(vb, struct cx23885_buffer, vb);
+
+ cx23885_free_buffer(q, buf);
+}
+
+struct videobuf_queue_ops cx23885_vbi_qops = {
+ .buf_setup = vbi_setup,
+ .buf_prepare = vbi_prepare,
+ .buf_queue = vbi_queue,
+ .buf_release = vbi_release,
+};
+
+/* ------------------------------------------------------------------ */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c
new file mode 100644
index 00000000..c654bdc7
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-video.c
@@ -0,0 +1,1919 @@
+/*
+ * Driver for the Conexant CX23885 PCIe bridge
+ *
+ * Copyright (c) 2007 Steven Toth <stoth@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kmod.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <asm/div64.h>
+
+#include "cx23885.h"
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include "cx23885-ioctl.h"
+#include "tuner-xc2028.h"
+
+#include <media/cx25840.h>
+
+MODULE_DESCRIPTION("v4l2 driver module for cx23885 based TV cards");
+MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int video_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
+static unsigned int vbi_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
+static unsigned int radio_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
+
+module_param_array(video_nr, int, NULL, 0444);
+module_param_array(vbi_nr, int, NULL, 0444);
+module_param_array(radio_nr, int, NULL, 0444);
+
+MODULE_PARM_DESC(video_nr, "video device numbers");
+MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
+MODULE_PARM_DESC(radio_nr, "radio device numbers");
+
+static unsigned int video_debug;
+module_param(video_debug, int, 0644);
+MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
+
+static unsigned int irq_debug;
+module_param(irq_debug, int, 0644);
+MODULE_PARM_DESC(irq_debug, "enable debug messages [IRQ handler]");
+
+static unsigned int vid_limit = 16;
+module_param(vid_limit, int, 0644);
+MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
+
+#define dprintk(level, fmt, arg...)\
+ do { if (video_debug >= level)\
+ printk(KERN_DEBUG "%s: " fmt, dev->name, ## arg);\
+ } while (0)
+
+/* ------------------------------------------------------------------- */
+/* static data */
+
+#define FORMAT_FLAGS_PACKED 0x01
+#if 0
+static struct cx23885_fmt formats[] = {
+ {
+ .name = "8 bpp, gray",
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .depth = 8,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "15 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "15 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB555X,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "16 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "16 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "24 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .depth = 24,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "32 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_BGR32,
+ .depth = 32,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "32 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .depth = 32,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "4:2:2, packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },
+};
+#else
+static struct cx23885_fmt formats[] = {
+ {
+#if 0
+ .name = "4:2:2, packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+#endif
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }
+};
+#endif
+
+static struct cx23885_fmt *format_by_fourcc(unsigned int fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(formats); i++)
+ if (formats[i].fourcc == fourcc)
+ return formats+i;
+
+ printk(KERN_ERR "%s(%c%c%c%c) NOT FOUND\n", __func__,
+ (fourcc & 0xff),
+ ((fourcc >> 8) & 0xff),
+ ((fourcc >> 16) & 0xff),
+ ((fourcc >> 24) & 0xff)
+ );
+ return NULL;
+}
+
+/* ------------------------------------------------------------------- */
+
+static const struct v4l2_queryctrl no_ctl = {
+ .name = "42",
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+};
+
+static struct cx23885_ctrl cx23885_ctls[] = {
+ /* --- video --- */
+ {
+ .v = {
+ .id = V4L2_CID_BRIGHTNESS,
+ .name = "Brightness",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 128,
+ .reg = LUMA_CTRL,
+ .mask = 0x00ff,
+ .shift = 0,
+ }, {
+ .v = {
+ .id = V4L2_CID_CONTRAST,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 0x7f,
+ .step = 1,
+ .default_value = 0x3f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 0,
+ .reg = LUMA_CTRL,
+ .mask = 0xff00,
+ .shift = 8,
+ }, {
+ .v = {
+ .id = V4L2_CID_HUE,
+ .name = "Hue",
+ .minimum = -127,
+ .maximum = 128,
+ .step = 1,
+ .default_value = 0x0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 128,
+ .reg = CHROMA_CTRL,
+ .mask = 0xff0000,
+ .shift = 16,
+ }, {
+ /* strictly, this only describes only U saturation.
+ * V saturation is handled specially through code.
+ */
+ .v = {
+ .id = V4L2_CID_SATURATION,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 0x7f,
+ .step = 1,
+ .default_value = 0x3f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 0,
+ .reg = CHROMA_CTRL,
+ .mask = 0x00ff,
+ .shift = 0,
+ }, {
+ /* --- audio --- */
+ .v = {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },
+ .reg = PATH1_CTL1,
+ .mask = (0x1f << 24),
+ .shift = 24,
+ }, {
+ .v = {
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .name = "Volume",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 65535 / 100,
+ .default_value = 65535,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .reg = PATH1_VOL_CTL,
+ .mask = 0xff,
+ .shift = 0,
+ }
+};
+static const int CX23885_CTLS = ARRAY_SIZE(cx23885_ctls);
+
+/* Must be sorted from low to high control ID! */
+static const u32 cx23885_user_ctrls[] = {
+ V4L2_CID_USER_CLASS,
+ V4L2_CID_BRIGHTNESS,
+ V4L2_CID_CONTRAST,
+ V4L2_CID_SATURATION,
+ V4L2_CID_HUE,
+ V4L2_CID_AUDIO_VOLUME,
+ V4L2_CID_AUDIO_MUTE,
+ 0
+};
+
+static const u32 *ctrl_classes[] = {
+ cx23885_user_ctrls,
+ NULL
+};
+
+void cx23885_video_wakeup(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q, u32 count)
+{
+ struct cx23885_buffer *buf;
+ int bc;
+
+ for (bc = 0;; bc++) {
+ if (list_empty(&q->active))
+ break;
+ buf = list_entry(q->active.next,
+ struct cx23885_buffer, vb.queue);
+
+ /* count comes from the hw and is is 16bit wide --
+ * this trick handles wrap-arounds correctly for
+ * up to 32767 buffers in flight... */
+ if ((s16) (count - buf->count) < 0)
+ break;
+
+ do_gettimeofday(&buf->vb.ts);
+ dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.i,
+ count, buf->count);
+ buf->vb.state = VIDEOBUF_DONE;
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+ }
+ if (list_empty(&q->active))
+ del_timer(&q->timeout);
+ else
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ if (bc != 1)
+ printk(KERN_ERR "%s: %d buffers handled (should be 1)\n",
+ __func__, bc);
+}
+
+int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm)
+{
+ dprintk(1, "%s(norm = 0x%08x) name: [%s]\n",
+ __func__,
+ (unsigned int)norm,
+ v4l2_norm_to_name(norm));
+
+ dev->tvnorm = norm;
+
+ call_all(dev, core, s_std, norm);
+
+ return 0;
+}
+
+static struct video_device *cx23885_vdev_init(struct cx23885_dev *dev,
+ struct pci_dev *pci,
+ struct video_device *template,
+ char *type)
+{
+ struct video_device *vfd;
+ dprintk(1, "%s()\n", __func__);
+
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+ *vfd = *template;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+ vfd->release = video_device_release;
+ snprintf(vfd->name, sizeof(vfd->name), "%s (%s)",
+ cx23885_boards[dev->board].name, type);
+ video_set_drvdata(vfd, dev);
+ return vfd;
+}
+
+static int cx23885_ctrl_query(struct v4l2_queryctrl *qctrl)
+{
+ int i;
+
+ if (qctrl->id < V4L2_CID_BASE ||
+ qctrl->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+ for (i = 0; i < CX23885_CTLS; i++)
+ if (cx23885_ctls[i].v.id == qctrl->id)
+ break;
+ if (i == CX23885_CTLS) {
+ *qctrl = no_ctl;
+ return 0;
+ }
+ *qctrl = cx23885_ctls[i].v;
+ return 0;
+}
+
+/* ------------------------------------------------------------------- */
+/* resource management */
+
+static int res_get(struct cx23885_dev *dev, struct cx23885_fh *fh,
+ unsigned int bit)
+{
+ dprintk(1, "%s()\n", __func__);
+ if (fh->resources & bit)
+ /* have it already allocated */
+ return 1;
+
+ /* is it free? */
+ mutex_lock(&dev->lock);
+ if (dev->resources & bit) {
+ /* no, someone else uses it */
+ mutex_unlock(&dev->lock);
+ return 0;
+ }
+ /* it's free, grab it */
+ fh->resources |= bit;
+ dev->resources |= bit;
+ dprintk(1, "res: get %d\n", bit);
+ mutex_unlock(&dev->lock);
+ return 1;
+}
+
+static int res_check(struct cx23885_fh *fh, unsigned int bit)
+{
+ return fh->resources & bit;
+}
+
+static int res_locked(struct cx23885_dev *dev, unsigned int bit)
+{
+ return dev->resources & bit;
+}
+
+static void res_free(struct cx23885_dev *dev, struct cx23885_fh *fh,
+ unsigned int bits)
+{
+ BUG_ON((fh->resources & bits) != bits);
+ dprintk(1, "%s()\n", __func__);
+
+ mutex_lock(&dev->lock);
+ fh->resources &= ~bits;
+ dev->resources &= ~bits;
+ dprintk(1, "res: put %d\n", bits);
+ mutex_unlock(&dev->lock);
+}
+
+static int cx23885_flatiron_write(struct cx23885_dev *dev, u8 reg, u8 data)
+{
+ /* 8 bit registers, 8 bit values */
+ u8 buf[] = { reg, data };
+
+ struct i2c_msg msg = { .addr = 0x98 >> 1,
+ .flags = 0, .buf = buf, .len = 2 };
+
+ return i2c_transfer(&dev->i2c_bus[2].i2c_adap, &msg, 1);
+}
+
+static u8 cx23885_flatiron_read(struct cx23885_dev *dev, u8 reg)
+{
+ /* 8 bit registers, 8 bit values */
+ int ret;
+ u8 b0[] = { reg };
+ u8 b1[] = { 0 };
+
+ struct i2c_msg msg[] = {
+ { .addr = 0x98 >> 1, .flags = 0, .buf = b0, .len = 1 },
+ { .addr = 0x98 >> 1, .flags = I2C_M_RD, .buf = b1, .len = 1 }
+ };
+
+ ret = i2c_transfer(&dev->i2c_bus[2].i2c_adap, &msg[0], 2);
+ if (ret != 2)
+ printk(KERN_ERR "%s() error\n", __func__);
+
+ return b1[0];
+}
+
+static void cx23885_flatiron_dump(struct cx23885_dev *dev)
+{
+ int i;
+ dprintk(1, "Flatiron dump\n");
+ for (i = 0; i < 0x24; i++) {
+ dprintk(1, "FI[%02x] = %02x\n", i,
+ cx23885_flatiron_read(dev, i));
+ }
+}
+
+static int cx23885_flatiron_mux(struct cx23885_dev *dev, int input)
+{
+ u8 val;
+ dprintk(1, "%s(input = %d)\n", __func__, input);
+
+ if (input == 1)
+ val = cx23885_flatiron_read(dev, CH_PWR_CTRL1) & ~FLD_CH_SEL;
+ else if (input == 2)
+ val = cx23885_flatiron_read(dev, CH_PWR_CTRL1) | FLD_CH_SEL;
+ else
+ return -EINVAL;
+
+ val |= 0x20; /* Enable clock to delta-sigma and dec filter */
+
+ cx23885_flatiron_write(dev, CH_PWR_CTRL1, val);
+
+ /* Wake up */
+ cx23885_flatiron_write(dev, CH_PWR_CTRL2, 0);
+
+ if (video_debug)
+ cx23885_flatiron_dump(dev);
+
+ return 0;
+}
+
+static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input)
+{
+ dprintk(1, "%s() video_mux: %d [vmux=%d, gpio=0x%x,0x%x,0x%x,0x%x]\n",
+ __func__,
+ input, INPUT(input)->vmux,
+ INPUT(input)->gpio0, INPUT(input)->gpio1,
+ INPUT(input)->gpio2, INPUT(input)->gpio3);
+ dev->input = input;
+
+ if (dev->board == CX23885_BOARD_MYGICA_X8506 ||
+ dev->board == CX23885_BOARD_MAGICPRO_PROHDTVE2 ||
+ dev->board == CX23885_BOARD_MYGICA_X8507) {
+ /* Select Analog TV */
+ if (INPUT(input)->type == CX23885_VMUX_TELEVISION)
+ cx23885_gpio_clear(dev, GPIO_0);
+ }
+
+ /* Tell the internal A/V decoder */
+ v4l2_subdev_call(dev->sd_cx25840, video, s_routing,
+ INPUT(input)->vmux, 0, 0);
+
+ if ((dev->board == CX23885_BOARD_HAUPPAUGE_HVR1800) ||
+ (dev->board == CX23885_BOARD_MPX885) ||
+ (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850)) {
+ /* Configure audio routing */
+ v4l2_subdev_call(dev->sd_cx25840, audio, s_routing,
+ INPUT(input)->amux, 0, 0);
+
+ if (INPUT(input)->amux == CX25840_AUDIO7)
+ cx23885_flatiron_mux(dev, 1);
+ else if (INPUT(input)->amux == CX25840_AUDIO6)
+ cx23885_flatiron_mux(dev, 2);
+ }
+
+ return 0;
+}
+
+static int cx23885_audio_mux(struct cx23885_dev *dev, unsigned int input)
+{
+ dprintk(1, "%s(input=%d)\n", __func__, input);
+
+ /* The baseband video core of the cx23885 has two audio inputs.
+ * LR1 and LR2. In almost every single case so far only HVR1xxx
+ * cards we've only ever supported LR1. Time to support LR2,
+ * which is available via the optional white breakout header on
+ * the board.
+ * We'll use a could of existing enums in the card struct to allow
+ * devs to specify which baseband input they need, or just default
+ * to what we've always used.
+ */
+ if (INPUT(input)->amux == CX25840_AUDIO7)
+ cx23885_flatiron_mux(dev, 1);
+ else if (INPUT(input)->amux == CX25840_AUDIO6)
+ cx23885_flatiron_mux(dev, 2);
+ else {
+ /* Not specifically defined, assume the default. */
+ cx23885_flatiron_mux(dev, 1);
+ }
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+static int cx23885_start_video_dma(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q,
+ struct cx23885_buffer *buf)
+{
+ dprintk(1, "%s()\n", __func__);
+
+ /* Stop the dma/fifo before we tamper with it's risc programs */
+ cx_clear(VID_A_DMA_CTL, 0x11);
+
+ /* setup fifo + format */
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH01],
+ buf->bpl, buf->risc.dma);
+
+ /* reset counter */
+ cx_write(VID_A_GPCNT_CTL, 3);
+ q->count = 1;
+
+ /* enable irq */
+ cx23885_irq_add_enable(dev, 0x01);
+ cx_set(VID_A_INT_MSK, 0x000011);
+
+ /* start dma */
+ cx_set(DEV_CNTRL2, (1<<5));
+ cx_set(VID_A_DMA_CTL, 0x11); /* FIFO and RISC enable */
+
+ return 0;
+}
+
+
+static int cx23885_restart_video_queue(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q)
+{
+ struct cx23885_buffer *buf, *prev;
+ struct list_head *item;
+ dprintk(1, "%s()\n", __func__);
+
+ if (!list_empty(&q->active)) {
+ buf = list_entry(q->active.next, struct cx23885_buffer,
+ vb.queue);
+ dprintk(2, "restart_queue [%p/%d]: restart dma\n",
+ buf, buf->vb.i);
+ cx23885_start_video_dma(dev, q, buf);
+ list_for_each(item, &q->active) {
+ buf = list_entry(item, struct cx23885_buffer,
+ vb.queue);
+ buf->count = q->count++;
+ }
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ return 0;
+ }
+
+ prev = NULL;
+ for (;;) {
+ if (list_empty(&q->queued))
+ return 0;
+ buf = list_entry(q->queued.next, struct cx23885_buffer,
+ vb.queue);
+ if (NULL == prev) {
+ list_move_tail(&buf->vb.queue, &q->active);
+ cx23885_start_video_dma(dev, q, buf);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ dprintk(2, "[%p/%d] restart_queue - first active\n",
+ buf, buf->vb.i);
+
+ } else if (prev->vb.width == buf->vb.width &&
+ prev->vb.height == buf->vb.height &&
+ prev->fmt == buf->fmt) {
+ list_move_tail(&buf->vb.queue, &q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63 - 32 */
+ dprintk(2, "[%p/%d] restart_queue - move to active\n",
+ buf, buf->vb.i);
+ } else {
+ return 0;
+ }
+ prev = buf;
+ }
+}
+
+static int buffer_setup(struct videobuf_queue *q, unsigned int *count,
+ unsigned int *size)
+{
+ struct cx23885_fh *fh = q->priv_data;
+
+ *size = fh->fmt->depth*fh->width*fh->height >> 3;
+ if (0 == *count)
+ *count = 32;
+ if (*size * *count > vid_limit * 1024 * 1024)
+ *count = (vid_limit * 1024 * 1024) / *size;
+ return 0;
+}
+
+static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct cx23885_fh *fh = q->priv_data;
+ struct cx23885_dev *dev = fh->dev;
+ struct cx23885_buffer *buf =
+ container_of(vb, struct cx23885_buffer, vb);
+ int rc, init_buffer = 0;
+ u32 line0_offset, line1_offset;
+ struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
+ int field_tff;
+
+ BUG_ON(NULL == fh->fmt);
+ if (fh->width < 48 || fh->width > norm_maxw(dev->tvnorm) ||
+ fh->height < 32 || fh->height > norm_maxh(dev->tvnorm))
+ return -EINVAL;
+ buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3;
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+
+ if (buf->fmt != fh->fmt ||
+ buf->vb.width != fh->width ||
+ buf->vb.height != fh->height ||
+ buf->vb.field != field) {
+ buf->fmt = fh->fmt;
+ buf->vb.width = fh->width;
+ buf->vb.height = fh->height;
+ buf->vb.field = field;
+ init_buffer = 1;
+ }
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ init_buffer = 1;
+ rc = videobuf_iolock(q, &buf->vb, NULL);
+ if (0 != rc)
+ goto fail;
+ }
+
+ if (init_buffer) {
+ buf->bpl = buf->vb.width * buf->fmt->depth >> 3;
+ switch (buf->vb.field) {
+ case V4L2_FIELD_TOP:
+ cx23885_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist, 0, UNSET,
+ buf->bpl, 0, buf->vb.height);
+ break;
+ case V4L2_FIELD_BOTTOM:
+ cx23885_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist, UNSET, 0,
+ buf->bpl, 0, buf->vb.height);
+ break;
+ case V4L2_FIELD_INTERLACED:
+ if (dev->tvnorm & V4L2_STD_NTSC)
+ /* NTSC or */
+ field_tff = 1;
+ else
+ field_tff = 0;
+
+ if (cx23885_boards[dev->board].force_bff)
+ /* PAL / SECAM OR 888 in NTSC MODE */
+ field_tff = 0;
+
+ if (field_tff) {
+ /* cx25840 transmits NTSC bottom field first */
+ dprintk(1, "%s() Creating TFF/NTSC risc\n",
+ __func__);
+ line0_offset = buf->bpl;
+ line1_offset = 0;
+ } else {
+ /* All other formats are top field first */
+ dprintk(1, "%s() Creating BFF/PAL/SECAM risc\n",
+ __func__);
+ line0_offset = 0;
+ line1_offset = buf->bpl;
+ }
+ cx23885_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist, line0_offset,
+ line1_offset,
+ buf->bpl, buf->bpl,
+ buf->vb.height >> 1);
+ break;
+ case V4L2_FIELD_SEQ_TB:
+ cx23885_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist,
+ 0, buf->bpl * (buf->vb.height >> 1),
+ buf->bpl, 0,
+ buf->vb.height >> 1);
+ break;
+ case V4L2_FIELD_SEQ_BT:
+ cx23885_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist,
+ buf->bpl * (buf->vb.height >> 1), 0,
+ buf->bpl, 0,
+ buf->vb.height >> 1);
+ break;
+ default:
+ BUG();
+ }
+ }
+ dprintk(2, "[%p/%d] buffer_prep - %dx%d %dbpp \"%s\" - dma=0x%08lx\n",
+ buf, buf->vb.i,
+ fh->width, fh->height, fh->fmt->depth, fh->fmt->name,
+ (unsigned long)buf->risc.dma);
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+ fail:
+ cx23885_free_buffer(q, buf);
+ return rc;
+}
+
+static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct cx23885_buffer *buf = container_of(vb,
+ struct cx23885_buffer, vb);
+ struct cx23885_buffer *prev;
+ struct cx23885_fh *fh = vq->priv_data;
+ struct cx23885_dev *dev = fh->dev;
+ struct cx23885_dmaqueue *q = &dev->vidq;
+
+ /* add jump to stopper */
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
+ buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
+ buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
+
+ if (!list_empty(&q->queued)) {
+ list_add_tail(&buf->vb.queue, &q->queued);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ dprintk(2, "[%p/%d] buffer_queue - append to queued\n",
+ buf, buf->vb.i);
+
+ } else if (list_empty(&q->active)) {
+ list_add_tail(&buf->vb.queue, &q->active);
+ cx23885_start_video_dma(dev, q, buf);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ dprintk(2, "[%p/%d] buffer_queue - first active\n",
+ buf, buf->vb.i);
+
+ } else {
+ prev = list_entry(q->active.prev, struct cx23885_buffer,
+ vb.queue);
+ if (prev->vb.width == buf->vb.width &&
+ prev->vb.height == buf->vb.height &&
+ prev->fmt == buf->fmt) {
+ list_add_tail(&buf->vb.queue, &q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ /* 64 bit bits 63-32 */
+ prev->risc.jmp[2] = cpu_to_le32(0);
+ dprintk(2, "[%p/%d] buffer_queue - append to active\n",
+ buf, buf->vb.i);
+
+ } else {
+ list_add_tail(&buf->vb.queue, &q->queued);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ dprintk(2, "[%p/%d] buffer_queue - first queued\n",
+ buf, buf->vb.i);
+ }
+ }
+}
+
+static void buffer_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ struct cx23885_buffer *buf = container_of(vb,
+ struct cx23885_buffer, vb);
+
+ cx23885_free_buffer(q, buf);
+}
+
+static struct videobuf_queue_ops cx23885_video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+static struct videobuf_queue *get_queue(struct cx23885_fh *fh)
+{
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return &fh->vidq;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ return &fh->vbiq;
+ default:
+ BUG();
+ return NULL;
+ }
+}
+
+static int get_resource(struct cx23885_fh *fh)
+{
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return RESOURCE_VIDEO;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ return RESOURCE_VBI;
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+static int video_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct cx23885_dev *dev = video_drvdata(file);
+ struct cx23885_fh *fh;
+ enum v4l2_buf_type type = 0;
+ int radio = 0;
+
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ break;
+ case VFL_TYPE_VBI:
+ type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ break;
+ case VFL_TYPE_RADIO:
+ radio = 1;
+ break;
+ }
+
+ dprintk(1, "open dev=%s radio=%d type=%s\n",
+ video_device_node_name(vdev), radio, v4l2_type_names[type]);
+
+ /* allocate + initialize per filehandle data */
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (NULL == fh)
+ return -ENOMEM;
+
+ file->private_data = fh;
+ fh->dev = dev;
+ fh->radio = radio;
+ fh->type = type;
+ fh->width = 320;
+ fh->height = 240;
+ fh->fmt = format_by_fourcc(V4L2_PIX_FMT_YUYV);
+
+ videobuf_queue_sg_init(&fh->vidq, &cx23885_video_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct cx23885_buffer),
+ fh, NULL);
+
+ videobuf_queue_sg_init(&fh->vbiq, &cx23885_vbi_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_FIELD_SEQ_TB,
+ sizeof(struct cx23885_buffer),
+ fh, NULL);
+
+
+ dprintk(1, "post videobuf_queue_init()\n");
+
+ return 0;
+}
+
+static ssize_t video_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct cx23885_fh *fh = file->private_data;
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (res_locked(fh->dev, RESOURCE_VIDEO))
+ return -EBUSY;
+ return videobuf_read_one(&fh->vidq, data, count, ppos,
+ file->f_flags & O_NONBLOCK);
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ if (!res_get(fh->dev, fh, RESOURCE_VBI))
+ return -EBUSY;
+ return videobuf_read_stream(&fh->vbiq, data, count, ppos, 1,
+ file->f_flags & O_NONBLOCK);
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+static unsigned int video_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_buffer *buf;
+ unsigned int rc = POLLERR;
+
+ if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
+ if (!res_get(fh->dev, fh, RESOURCE_VBI))
+ return POLLERR;
+ return videobuf_poll_stream(file, &fh->vbiq, wait);
+ }
+
+ mutex_lock(&fh->vidq.vb_lock);
+ if (res_check(fh, RESOURCE_VIDEO)) {
+ /* streaming capture */
+ if (list_empty(&fh->vidq.stream))
+ goto done;
+ buf = list_entry(fh->vidq.stream.next,
+ struct cx23885_buffer, vb.stream);
+ } else {
+ /* read() capture */
+ buf = (struct cx23885_buffer *)fh->vidq.read_buf;
+ if (NULL == buf)
+ goto done;
+ }
+ poll_wait(file, &buf->vb.done, wait);
+ if (buf->vb.state == VIDEOBUF_DONE ||
+ buf->vb.state == VIDEOBUF_ERROR)
+ rc = POLLIN|POLLRDNORM;
+ else
+ rc = 0;
+done:
+ mutex_unlock(&fh->vidq.vb_lock);
+ return rc;
+}
+
+static int video_release(struct file *file)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+ /* turn off overlay */
+ if (res_check(fh, RESOURCE_OVERLAY)) {
+ /* FIXME */
+ res_free(dev, fh, RESOURCE_OVERLAY);
+ }
+
+ /* stop video capture */
+ if (res_check(fh, RESOURCE_VIDEO)) {
+ videobuf_queue_cancel(&fh->vidq);
+ res_free(dev, fh, RESOURCE_VIDEO);
+ }
+ if (fh->vidq.read_buf) {
+ buffer_release(&fh->vidq, fh->vidq.read_buf);
+ kfree(fh->vidq.read_buf);
+ }
+
+ /* stop vbi capture */
+ if (res_check(fh, RESOURCE_VBI)) {
+ if (fh->vbiq.streaming)
+ videobuf_streamoff(&fh->vbiq);
+ if (fh->vbiq.reading)
+ videobuf_read_stop(&fh->vbiq);
+ res_free(dev, fh, RESOURCE_VBI);
+ }
+
+ videobuf_mmap_free(&fh->vidq);
+ videobuf_mmap_free(&fh->vbiq);
+
+ file->private_data = NULL;
+ kfree(fh);
+
+ /* We are not putting the tuner to sleep here on exit, because
+ * we want to use the mpeg encoder in another session to capture
+ * tuner video. Closing this will result in no video to the encoder.
+ */
+
+ return 0;
+}
+
+static int video_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct cx23885_fh *fh = file->private_data;
+
+ return videobuf_mmap_mapper(get_queue(fh), vma);
+}
+
+/* ------------------------------------------------------------------ */
+/* VIDEO CTRL IOCTLS */
+
+int cx23885_get_control(struct cx23885_dev *dev,
+ struct v4l2_control *ctl)
+{
+ dprintk(1, "%s() calling cx25840(VIDIOC_G_CTRL)\n", __func__);
+ call_all(dev, core, g_ctrl, ctl);
+ return 0;
+}
+
+int cx23885_set_control(struct cx23885_dev *dev,
+ struct v4l2_control *ctl)
+{
+ dprintk(1, "%s() calling cx25840(VIDIOC_S_CTRL)\n", __func__);
+ call_all(dev, core, s_ctrl, ctl);
+
+ return 0;
+}
+
+static void init_controls(struct cx23885_dev *dev)
+{
+ struct v4l2_control ctrl;
+ int i;
+
+ for (i = 0; i < CX23885_CTLS; i++) {
+ ctrl.id = cx23885_ctls[i].v.id;
+ ctrl.value = cx23885_ctls[i].v.default_value;
+
+ cx23885_set_control(dev, &ctrl);
+ }
+}
+
+/* ------------------------------------------------------------------ */
+/* VIDEO IOCTLS */
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx23885_fh *fh = priv;
+
+ f->fmt.pix.width = fh->width;
+ f->fmt.pix.height = fh->height;
+ f->fmt.pix.field = fh->vidq.field;
+ f->fmt.pix.pixelformat = fh->fmt->fourcc;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fh->fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ struct cx23885_fmt *fmt;
+ enum v4l2_field field;
+ unsigned int maxw, maxh;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+
+ field = f->fmt.pix.field;
+ maxw = norm_maxw(dev->tvnorm);
+ maxh = norm_maxh(dev->tvnorm);
+
+ if (V4L2_FIELD_ANY == field) {
+ field = (f->fmt.pix.height > maxh/2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_BOTTOM;
+ }
+
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ f->fmt.pix.field = field;
+ v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2,
+ &f->fmt.pix.height, 32, maxh, 0, 0);
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ struct v4l2_mbus_framefmt mbus_fmt;
+ int err;
+
+ dprintk(2, "%s()\n", __func__);
+ err = vidioc_try_fmt_vid_cap(file, priv, f);
+
+ if (0 != err)
+ return err;
+ fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ fh->width = f->fmt.pix.width;
+ fh->height = f->fmt.pix.height;
+ fh->vidq.field = f->fmt.pix.field;
+ dprintk(2, "%s() width=%d height=%d field=%d\n", __func__,
+ fh->width, fh->height, fh->vidq.field);
+ v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED);
+ call_all(dev, video, s_mbus_fmt, &mbus_fmt);
+ v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt);
+ return 0;
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ strcpy(cap->driver, "cx23885");
+ strlcpy(cap->card, cx23885_boards[dev->board].name,
+ sizeof(cap->card));
+ sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci));
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING |
+ V4L2_CAP_VBI_CAPTURE;
+ if (UNSET != dev->tuner_type)
+ cap->capabilities |= V4L2_CAP_TUNER;
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (unlikely(f->index >= ARRAY_SIZE(formats)))
+ return -EINVAL;
+
+ strlcpy(f->description, formats[f->index].name,
+ sizeof(f->description));
+ f->pixelformat = formats[f->index].fourcc;
+
+ return 0;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct cx23885_fh *fh = priv;
+ return videobuf_reqbufs(get_queue(fh), p);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct cx23885_fh *fh = priv;
+ return videobuf_querybuf(get_queue(fh), p);
+}
+
+static int vidioc_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct cx23885_fh *fh = priv;
+ return videobuf_qbuf(get_queue(fh), p);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct cx23885_fh *fh = priv;
+ return videobuf_dqbuf(get_queue(fh), p,
+ file->f_flags & O_NONBLOCK);
+}
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type i)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+ dprintk(1, "%s()\n", __func__);
+
+ if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE))
+ return -EINVAL;
+ if (unlikely(i != fh->type))
+ return -EINVAL;
+
+ if (unlikely(!res_get(dev, fh, get_resource(fh))))
+ return -EBUSY;
+
+ /* Don't start VBI streaming unless vida streaming
+ * has already started.
+ */
+ if ((fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) &&
+ ((cx_read(VID_A_DMA_CTL) & 0x11) == 0))
+ return -EINVAL;
+
+ return videobuf_streamon(get_queue(fh));
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+ int err, res;
+ dprintk(1, "%s()\n", __func__);
+
+ if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE))
+ return -EINVAL;
+ if (i != fh->type)
+ return -EINVAL;
+
+ res = get_resource(fh);
+ err = videobuf_streamoff(get_queue(fh));
+ if (err < 0)
+ return err;
+ res_free(dev, fh, res);
+ return 0;
+}
+
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ dprintk(1, "%s()\n", __func__);
+
+ call_all(dev, core, g_std, id);
+
+ return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *tvnorms)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ dprintk(1, "%s()\n", __func__);
+
+ mutex_lock(&dev->lock);
+ cx23885_set_tvnorm(dev, *tvnorms);
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+
+int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i)
+{
+ static const char *iname[] = {
+ [CX23885_VMUX_COMPOSITE1] = "Composite1",
+ [CX23885_VMUX_COMPOSITE2] = "Composite2",
+ [CX23885_VMUX_COMPOSITE3] = "Composite3",
+ [CX23885_VMUX_COMPOSITE4] = "Composite4",
+ [CX23885_VMUX_SVIDEO] = "S-Video",
+ [CX23885_VMUX_COMPONENT] = "Component",
+ [CX23885_VMUX_TELEVISION] = "Television",
+ [CX23885_VMUX_CABLE] = "Cable TV",
+ [CX23885_VMUX_DVB] = "DVB",
+ [CX23885_VMUX_DEBUG] = "for debug only",
+ };
+ unsigned int n;
+ dprintk(1, "%s()\n", __func__);
+
+ n = i->index;
+ if (n >= MAX_CX23885_INPUT)
+ return -EINVAL;
+
+ if (0 == INPUT(n)->type)
+ return -EINVAL;
+
+ i->index = n;
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ strcpy(i->name, iname[INPUT(n)->type]);
+ if ((CX23885_VMUX_TELEVISION == INPUT(n)->type) ||
+ (CX23885_VMUX_CABLE == INPUT(n)->type)) {
+ i->type = V4L2_INPUT_TYPE_TUNER;
+ i->std = CX23885_NORMS;
+ }
+
+ /* Two selectable audio inputs for non-tv inputs */
+ if (INPUT(n)->type != CX23885_VMUX_TELEVISION)
+ i->audioset = 0x3;
+
+ if (dev->input == n) {
+ /* enum'd input matches our configured input.
+ * Ask the video decoder to process the call
+ * and give it an oppertunity to update the
+ * status field.
+ */
+ call_all(dev, video, g_input_status, &i->status);
+ }
+
+ return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ dprintk(1, "%s()\n", __func__);
+ return cx23885_enum_input(dev, i);
+}
+
+int cx23885_get_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ *i = dev->input;
+ dprintk(1, "%s() returns %d\n", __func__, *i);
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ return cx23885_get_input(file, priv, i);
+}
+
+int cx23885_set_input(struct file *file, void *priv, unsigned int i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ dprintk(1, "%s(%d)\n", __func__, i);
+
+ if (i >= MAX_CX23885_INPUT) {
+ dprintk(1, "%s() -EINVAL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (INPUT(i)->type == 0)
+ return -EINVAL;
+
+ mutex_lock(&dev->lock);
+ cx23885_video_mux(dev, i);
+
+ /* By default establish the default audio input for the card also */
+ /* Caller is free to use VIDIOC_S_AUDIO to override afterwards */
+ cx23885_audio_mux(dev, i);
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ return cx23885_set_input(file, priv, i);
+}
+
+static int vidioc_log_status(struct file *file, void *priv)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+
+ printk(KERN_INFO
+ "%s/0: ============ START LOG STATUS ============\n",
+ dev->name);
+ call_all(dev, core, log_status);
+ printk(KERN_INFO
+ "%s/0: ============= END LOG STATUS =============\n",
+ dev->name);
+ return 0;
+}
+
+static int cx23885_query_audinput(struct file *file, void *priv,
+ struct v4l2_audio *i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ static const char *iname[] = {
+ [0] = "Baseband L/R 1",
+ [1] = "Baseband L/R 2",
+ };
+ unsigned int n;
+ dprintk(1, "%s()\n", __func__);
+
+ n = i->index;
+ if (n >= 2)
+ return -EINVAL;
+
+ memset(i, 0, sizeof(*i));
+ i->index = n;
+ strcpy(i->name, iname[n]);
+ i->capability = V4L2_AUDCAP_STEREO;
+ i->mode = V4L2_AUDMODE_AVL;
+ return 0;
+
+}
+
+static int vidioc_enum_audinput(struct file *file, void *priv,
+ struct v4l2_audio *i)
+{
+ return cx23885_query_audinput(file, priv, i);
+}
+
+static int vidioc_g_audinput(struct file *file, void *priv,
+ struct v4l2_audio *i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ i->index = dev->audinput;
+ dprintk(1, "%s(input=%d)\n", __func__, i->index);
+
+ return cx23885_query_audinput(file, priv, i);
+}
+
+static int vidioc_s_audinput(struct file *file, void *priv,
+ struct v4l2_audio *i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ if (i->index >= 2)
+ return -EINVAL;
+
+ dprintk(1, "%s(%d)\n", __func__, i->index);
+
+ dev->audinput = i->index;
+
+ /* Skip the audio defaults from the cards struct, caller wants
+ * directly touch the audio mux hardware. */
+ cx23885_flatiron_mux(dev, dev->audinput + 1);
+ return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qctrl)
+{
+ qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
+ if (unlikely(qctrl->id == 0))
+ return -EINVAL;
+ return cx23885_ctrl_query(qctrl);
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctl)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ return cx23885_get_control(dev, ctl);
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctl)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ return cx23885_set_control(dev, ctl);
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ if (unlikely(UNSET == dev->tuner_type))
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+
+ strcpy(t->name, "Television");
+
+ call_all(dev, tuner, g_tuner, t);
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ if (UNSET == dev->tuner_type)
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+ /* Update the A/V core */
+ call_all(dev, tuner, s_tuner, t);
+
+ return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+
+ if (unlikely(UNSET == dev->tuner_type))
+ return -EINVAL;
+
+ /* f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; */
+ f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ f->frequency = dev->freq;
+
+ call_all(dev, tuner, g_frequency, f);
+
+ return 0;
+}
+
+static int cx23885_set_freq(struct cx23885_dev *dev, struct v4l2_frequency *f)
+{
+ struct v4l2_control ctrl;
+
+ if (unlikely(UNSET == dev->tuner_type))
+ return -EINVAL;
+ if (unlikely(f->tuner != 0))
+ return -EINVAL;
+
+ mutex_lock(&dev->lock);
+ dev->freq = f->frequency;
+
+ /* I need to mute audio here */
+ ctrl.id = V4L2_CID_AUDIO_MUTE;
+ ctrl.value = 1;
+ cx23885_set_control(dev, &ctrl);
+
+ call_all(dev, tuner, s_frequency, f);
+
+ /* When changing channels it is required to reset TVAUDIO */
+ msleep(100);
+
+ /* I need to unmute audio here */
+ ctrl.value = 0;
+ cx23885_set_control(dev, &ctrl);
+
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+
+static int cx23885_set_freq_via_ops(struct cx23885_dev *dev,
+ struct v4l2_frequency *f)
+{
+ struct v4l2_control ctrl;
+ struct videobuf_dvb_frontend *vfe;
+ struct dvb_frontend *fe;
+
+ struct analog_parameters params = {
+ .mode = V4L2_TUNER_ANALOG_TV,
+ .audmode = V4L2_TUNER_MODE_STEREO,
+ .std = dev->tvnorm,
+ .frequency = f->frequency
+ };
+
+ mutex_lock(&dev->lock);
+ dev->freq = f->frequency;
+
+ /* I need to mute audio here */
+ ctrl.id = V4L2_CID_AUDIO_MUTE;
+ ctrl.value = 1;
+ cx23885_set_control(dev, &ctrl);
+
+ /* If HVR1850 */
+ dprintk(1, "%s() frequency=%d tuner=%d std=0x%llx\n", __func__,
+ params.frequency, f->tuner, params.std);
+
+ vfe = videobuf_dvb_get_frontend(&dev->ts2.frontends, 1);
+ if (!vfe) {
+ mutex_unlock(&dev->lock);
+ return -EINVAL;
+ }
+
+ fe = vfe->dvb.frontend;
+
+ if (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850)
+ fe = &dev->ts1.analog_fe;
+
+ if (fe && fe->ops.tuner_ops.set_analog_params) {
+ call_all(dev, core, s_std, dev->tvnorm);
+ fe->ops.tuner_ops.set_analog_params(fe, &params);
+ }
+ else
+ printk(KERN_ERR "%s() No analog tuner, aborting\n", __func__);
+
+ /* When changing channels it is required to reset TVAUDIO */
+ msleep(100);
+
+ /* I need to unmute audio here */
+ ctrl.value = 0;
+ cx23885_set_control(dev, &ctrl);
+
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+
+int cx23885_set_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+ int ret;
+
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ ret = cx23885_set_freq_via_ops(dev, f);
+ break;
+ default:
+ ret = cx23885_set_freq(dev, f);
+ }
+
+ return ret;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ return cx23885_set_frequency(file, priv, f);
+}
+
+/* ----------------------------------------------------------- */
+
+static void cx23885_vid_timeout(unsigned long data)
+{
+ struct cx23885_dev *dev = (struct cx23885_dev *)data;
+ struct cx23885_dmaqueue *q = &dev->vidq;
+ struct cx23885_buffer *buf;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->slock, flags);
+ while (!list_empty(&q->active)) {
+ buf = list_entry(q->active.next,
+ struct cx23885_buffer, vb.queue);
+ list_del(&buf->vb.queue);
+ buf->vb.state = VIDEOBUF_ERROR;
+ wake_up(&buf->vb.done);
+ printk(KERN_ERR "%s: [%p/%d] timeout - dma=0x%08lx\n",
+ dev->name, buf, buf->vb.i,
+ (unsigned long)buf->risc.dma);
+ }
+ cx23885_restart_video_queue(dev, q);
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+int cx23885_video_irq(struct cx23885_dev *dev, u32 status)
+{
+ u32 mask, count;
+ int handled = 0;
+
+ mask = cx_read(VID_A_INT_MSK);
+ if (0 == (status & mask))
+ return handled;
+
+ cx_write(VID_A_INT_STAT, status);
+
+ /* risc op code error, fifo overflow or line sync detection error */
+ if ((status & VID_BC_MSK_OPC_ERR) ||
+ (status & VID_BC_MSK_SYNC) ||
+ (status & VID_BC_MSK_OF)) {
+
+ if (status & VID_BC_MSK_OPC_ERR) {
+ dprintk(7, " (VID_BC_MSK_OPC_ERR 0x%08x)\n",
+ VID_BC_MSK_OPC_ERR);
+ printk(KERN_WARNING "%s: video risc op code error\n",
+ dev->name);
+ cx23885_sram_channel_dump(dev,
+ &dev->sram_channels[SRAM_CH01]);
+ }
+
+ if (status & VID_BC_MSK_SYNC)
+ dprintk(7, " (VID_BC_MSK_SYNC 0x%08x) "
+ "video lines miss-match\n",
+ VID_BC_MSK_SYNC);
+
+ if (status & VID_BC_MSK_OF)
+ dprintk(7, " (VID_BC_MSK_OF 0x%08x) fifo overflow\n",
+ VID_BC_MSK_OF);
+
+ }
+
+ /* Video */
+ if (status & VID_BC_MSK_RISCI1) {
+ spin_lock(&dev->slock);
+ count = cx_read(VID_A_GPCNT);
+ cx23885_video_wakeup(dev, &dev->vidq, count);
+ spin_unlock(&dev->slock);
+ handled++;
+ }
+ if (status & VID_BC_MSK_RISCI2) {
+ dprintk(2, "stopper video\n");
+ spin_lock(&dev->slock);
+ cx23885_restart_video_queue(dev, &dev->vidq);
+ spin_unlock(&dev->slock);
+ handled++;
+ }
+
+ /* Allow the VBI framework to process it's payload */
+ handled += cx23885_vbi_irq(dev, status);
+
+ return handled;
+}
+
+/* ----------------------------------------------------------- */
+/* exported stuff */
+
+static const struct v4l2_file_operations video_fops = {
+ .owner = THIS_MODULE,
+ .open = video_open,
+ .release = video_release,
+ .read = video_read,
+ .poll = video_poll,
+ .mmap = video_mmap,
+ .ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops video_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_g_fmt_vbi_cap = cx23885_vbi_fmt,
+ .vidioc_try_fmt_vbi_cap = cx23885_vbi_fmt,
+ .vidioc_s_fmt_vbi_cap = cx23885_vbi_fmt,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_querystd = vidioc_g_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_log_status = vidioc_log_status,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_g_chip_ident = cx23885_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = cx23885_g_register,
+ .vidioc_s_register = cx23885_s_register,
+#endif
+ .vidioc_enumaudio = vidioc_enum_audinput,
+ .vidioc_g_audio = vidioc_g_audinput,
+ .vidioc_s_audio = vidioc_s_audinput,
+};
+
+static struct video_device cx23885_vbi_template;
+static struct video_device cx23885_video_template = {
+ .name = "cx23885-video",
+ .fops = &video_fops,
+ .ioctl_ops = &video_ioctl_ops,
+ .tvnorms = CX23885_NORMS,
+ .current_norm = V4L2_STD_NTSC_M,
+};
+
+static const struct v4l2_file_operations radio_fops = {
+ .owner = THIS_MODULE,
+ .open = video_open,
+ .release = video_release,
+ .ioctl = video_ioctl2,
+};
+
+
+void cx23885_video_unregister(struct cx23885_dev *dev)
+{
+ dprintk(1, "%s()\n", __func__);
+ cx23885_irq_remove(dev, 0x01);
+
+ if (dev->vbi_dev) {
+ if (video_is_registered(dev->vbi_dev))
+ video_unregister_device(dev->vbi_dev);
+ else
+ video_device_release(dev->vbi_dev);
+ dev->vbi_dev = NULL;
+ btcx_riscmem_free(dev->pci, &dev->vbiq.stopper);
+ }
+ if (dev->video_dev) {
+ if (video_is_registered(dev->video_dev))
+ video_unregister_device(dev->video_dev);
+ else
+ video_device_release(dev->video_dev);
+ dev->video_dev = NULL;
+
+ btcx_riscmem_free(dev->pci, &dev->vidq.stopper);
+ }
+
+ if (dev->audio_dev)
+ cx23885_audio_unregister(dev);
+}
+
+int cx23885_video_register(struct cx23885_dev *dev)
+{
+ int err;
+
+ dprintk(1, "%s()\n", __func__);
+ spin_lock_init(&dev->slock);
+
+ /* Initialize VBI template */
+ memcpy(&cx23885_vbi_template, &cx23885_video_template,
+ sizeof(cx23885_vbi_template));
+ strcpy(cx23885_vbi_template.name, "cx23885-vbi");
+
+ dev->tvnorm = cx23885_video_template.current_norm;
+
+ /* init video dma queues */
+ INIT_LIST_HEAD(&dev->vidq.active);
+ INIT_LIST_HEAD(&dev->vidq.queued);
+ dev->vidq.timeout.function = cx23885_vid_timeout;
+ dev->vidq.timeout.data = (unsigned long)dev;
+ init_timer(&dev->vidq.timeout);
+ cx23885_risc_stopper(dev->pci, &dev->vidq.stopper,
+ VID_A_DMA_CTL, 0x11, 0x00);
+
+ /* init vbi dma queues */
+ INIT_LIST_HEAD(&dev->vbiq.active);
+ INIT_LIST_HEAD(&dev->vbiq.queued);
+ dev->vbiq.timeout.function = cx23885_vbi_timeout;
+ dev->vbiq.timeout.data = (unsigned long)dev;
+ init_timer(&dev->vbiq.timeout);
+ cx23885_risc_stopper(dev->pci, &dev->vbiq.stopper,
+ VID_A_DMA_CTL, 0x22, 0x00);
+
+ cx23885_irq_add_enable(dev, 0x01);
+
+ if ((TUNER_ABSENT != dev->tuner_type) &&
+ ((dev->tuner_bus == 0) || (dev->tuner_bus == 1))) {
+ struct v4l2_subdev *sd = NULL;
+
+ if (dev->tuner_addr)
+ sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
+ &dev->i2c_bus[dev->tuner_bus].i2c_adap,
+ "tuner", dev->tuner_addr, NULL);
+ else
+ sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
+ &dev->i2c_bus[dev->tuner_bus].i2c_adap,
+ "tuner", 0, v4l2_i2c_tuner_addrs(ADDRS_TV));
+ if (sd) {
+ struct tuner_setup tun_setup;
+
+ memset(&tun_setup, 0, sizeof(tun_setup));
+ tun_setup.mode_mask = T_ANALOG_TV;
+ tun_setup.type = dev->tuner_type;
+ tun_setup.addr = v4l2_i2c_subdev_addr(sd);
+ tun_setup.tuner_callback = cx23885_tuner_callback;
+
+ v4l2_subdev_call(sd, tuner, s_type_addr, &tun_setup);
+
+ if (dev->board == CX23885_BOARD_LEADTEK_WINFAST_PXTV1200) {
+ struct xc2028_ctrl ctrl = {
+ .fname = XC2028_DEFAULT_FIRMWARE,
+ .max_len = 64
+ };
+ struct v4l2_priv_tun_config cfg = {
+ .tuner = dev->tuner_type,
+ .priv = &ctrl
+ };
+ v4l2_subdev_call(sd, tuner, s_config, &cfg);
+ }
+ }
+ }
+
+ /* register Video device */
+ dev->video_dev = cx23885_vdev_init(dev, dev->pci,
+ &cx23885_video_template, "video");
+ err = video_register_device(dev->video_dev, VFL_TYPE_GRABBER,
+ video_nr[dev->nr]);
+ if (err < 0) {
+ printk(KERN_INFO "%s: can't register video device\n",
+ dev->name);
+ goto fail_unreg;
+ }
+ printk(KERN_INFO "%s: registered device %s [v4l2]\n",
+ dev->name, video_device_node_name(dev->video_dev));
+
+ /* register VBI device */
+ dev->vbi_dev = cx23885_vdev_init(dev, dev->pci,
+ &cx23885_vbi_template, "vbi");
+ err = video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
+ vbi_nr[dev->nr]);
+ if (err < 0) {
+ printk(KERN_INFO "%s: can't register vbi device\n",
+ dev->name);
+ goto fail_unreg;
+ }
+ printk(KERN_INFO "%s: registered device %s\n",
+ dev->name, video_device_node_name(dev->vbi_dev));
+
+ /* Register ALSA audio device */
+ dev->audio_dev = cx23885_audio_register(dev);
+
+ /* initial device configuration */
+ mutex_lock(&dev->lock);
+ cx23885_set_tvnorm(dev, dev->tvnorm);
+ init_controls(dev);
+ cx23885_video_mux(dev, 0);
+ cx23885_audio_mux(dev, 0);
+ mutex_unlock(&dev->lock);
+
+ return 0;
+
+fail_unreg:
+ cx23885_video_unregister(dev);
+ return err;
+}
+
diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h
new file mode 100644
index 00000000..f020f056
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885.h
@@ -0,0 +1,653 @@
+/*
+ * Driver for the Conexant CX23885 PCIe bridge
+ *
+ * Copyright (c) 2006 Steven Toth <stoth@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/kdev_t.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-device.h>
+#include <media/tuner.h>
+#include <media/tveeprom.h>
+#include <media/videobuf-dma-sg.h>
+#include <media/videobuf-dvb.h>
+#include <media/rc-core.h>
+
+#include "btcx-risc.h"
+#include "cx23885-reg.h"
+#include "media/cx2341x.h"
+
+#include <linux/mutex.h>
+
+#define CX23885_VERSION "0.0.3"
+
+#define UNSET (-1U)
+
+#define CX23885_MAXBOARDS 8
+
+/* Max number of inputs by card */
+#define MAX_CX23885_INPUT 8
+#define INPUT(nr) (&cx23885_boards[dev->board].input[nr])
+#define RESOURCE_OVERLAY 1
+#define RESOURCE_VIDEO 2
+#define RESOURCE_VBI 4
+
+#define BUFFER_TIMEOUT (HZ) /* 0.5 seconds */
+
+#define CX23885_BOARD_NOAUTO UNSET
+#define CX23885_BOARD_UNKNOWN 0
+#define CX23885_BOARD_HAUPPAUGE_HVR1800lp 1
+#define CX23885_BOARD_HAUPPAUGE_HVR1800 2
+#define CX23885_BOARD_HAUPPAUGE_HVR1250 3
+#define CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP 4
+#define CX23885_BOARD_HAUPPAUGE_HVR1500Q 5
+#define CX23885_BOARD_HAUPPAUGE_HVR1500 6
+#define CX23885_BOARD_HAUPPAUGE_HVR1200 7
+#define CX23885_BOARD_HAUPPAUGE_HVR1700 8
+#define CX23885_BOARD_HAUPPAUGE_HVR1400 9
+#define CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP 10
+#define CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP 11
+#define CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H 12
+#define CX23885_BOARD_COMPRO_VIDEOMATE_E650F 13
+#define CX23885_BOARD_TBS_6920 14
+#define CX23885_BOARD_TEVII_S470 15
+#define CX23885_BOARD_DVBWORLD_2005 16
+#define CX23885_BOARD_NETUP_DUAL_DVBS2_CI 17
+#define CX23885_BOARD_HAUPPAUGE_HVR1270 18
+#define CX23885_BOARD_HAUPPAUGE_HVR1275 19
+#define CX23885_BOARD_HAUPPAUGE_HVR1255 20
+#define CX23885_BOARD_HAUPPAUGE_HVR1210 21
+#define CX23885_BOARD_MYGICA_X8506 22
+#define CX23885_BOARD_MAGICPRO_PROHDTVE2 23
+#define CX23885_BOARD_HAUPPAUGE_HVR1850 24
+#define CX23885_BOARD_COMPRO_VIDEOMATE_E800 25
+#define CX23885_BOARD_HAUPPAUGE_HVR1290 26
+#define CX23885_BOARD_MYGICA_X8558PRO 27
+#define CX23885_BOARD_LEADTEK_WINFAST_PXTV1200 28
+#define CX23885_BOARD_GOTVIEW_X5_3D_HYBRID 29
+#define CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF 30
+#define CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000 31
+#define CX23885_BOARD_MPX885 32
+#define CX23885_BOARD_MYGICA_X8507 33
+#define CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL 34
+
+#define GPIO_0 0x00000001
+#define GPIO_1 0x00000002
+#define GPIO_2 0x00000004
+#define GPIO_3 0x00000008
+#define GPIO_4 0x00000010
+#define GPIO_5 0x00000020
+#define GPIO_6 0x00000040
+#define GPIO_7 0x00000080
+#define GPIO_8 0x00000100
+#define GPIO_9 0x00000200
+#define GPIO_10 0x00000400
+#define GPIO_11 0x00000800
+#define GPIO_12 0x00001000
+#define GPIO_13 0x00002000
+#define GPIO_14 0x00004000
+#define GPIO_15 0x00008000
+
+/* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */
+#define CX23885_NORMS (\
+ V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_JP | V4L2_STD_NTSC_443 | \
+ V4L2_STD_PAL_BG | V4L2_STD_PAL_DK | V4L2_STD_PAL_I | \
+ V4L2_STD_PAL_M | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | \
+ V4L2_STD_PAL_60 | V4L2_STD_SECAM_L | V4L2_STD_SECAM_DK)
+
+struct cx23885_fmt {
+ char *name;
+ u32 fourcc; /* v4l2 format id */
+ int depth;
+ int flags;
+ u32 cxformat;
+};
+
+struct cx23885_ctrl {
+ struct v4l2_queryctrl v;
+ u32 off;
+ u32 reg;
+ u32 mask;
+ u32 shift;
+};
+
+struct cx23885_tvnorm {
+ char *name;
+ v4l2_std_id id;
+ u32 cxiformat;
+ u32 cxoformat;
+};
+
+struct cx23885_fh {
+ struct cx23885_dev *dev;
+ enum v4l2_buf_type type;
+ int radio;
+ u32 resources;
+
+ /* video overlay */
+ struct v4l2_window win;
+ struct v4l2_clip *clips;
+ unsigned int nclips;
+
+ /* video capture */
+ struct cx23885_fmt *fmt;
+ unsigned int width, height;
+
+ /* vbi capture */
+ struct videobuf_queue vidq;
+ struct videobuf_queue vbiq;
+
+ /* MPEG Encoder specifics ONLY */
+ struct videobuf_queue mpegq;
+ atomic_t v4l_reading;
+};
+
+enum cx23885_itype {
+ CX23885_VMUX_COMPOSITE1 = 1,
+ CX23885_VMUX_COMPOSITE2,
+ CX23885_VMUX_COMPOSITE3,
+ CX23885_VMUX_COMPOSITE4,
+ CX23885_VMUX_SVIDEO,
+ CX23885_VMUX_COMPONENT,
+ CX23885_VMUX_TELEVISION,
+ CX23885_VMUX_CABLE,
+ CX23885_VMUX_DVB,
+ CX23885_VMUX_DEBUG,
+ CX23885_RADIO,
+};
+
+enum cx23885_src_sel_type {
+ CX23885_SRC_SEL_EXT_656_VIDEO = 0,
+ CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO
+};
+
+/* buffer for one video frame */
+struct cx23885_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+
+ /* cx23885 specific */
+ unsigned int bpl;
+ struct btcx_riscmem risc;
+ struct cx23885_fmt *fmt;
+ u32 count;
+};
+
+struct cx23885_input {
+ enum cx23885_itype type;
+ unsigned int vmux;
+ unsigned int amux;
+ u32 gpio0, gpio1, gpio2, gpio3;
+};
+
+typedef enum {
+ CX23885_MPEG_UNDEFINED = 0,
+ CX23885_MPEG_DVB,
+ CX23885_ANALOG_VIDEO,
+ CX23885_MPEG_ENCODER,
+} port_t;
+
+struct cx23885_board {
+ char *name;
+ port_t porta, portb, portc;
+ int num_fds_portb, num_fds_portc;
+ unsigned int tuner_type;
+ unsigned int radio_type;
+ unsigned char tuner_addr;
+ unsigned char radio_addr;
+ unsigned int tuner_bus;
+
+ /* Vendors can and do run the PCIe bridge at different
+ * clock rates, driven physically by crystals on the PCBs.
+ * The core has to accommodate this. This allows the user
+ * to add new boards with new frequencys. The value is
+ * expressed in Hz.
+ *
+ * The core framework will default this value based on
+ * current designs, but it can vary.
+ */
+ u32 clk_freq;
+ struct cx23885_input input[MAX_CX23885_INPUT];
+ int ci_type; /* for NetUP */
+ /* Force bottom field first during DMA (888 workaround) */
+ u32 force_bff;
+};
+
+struct cx23885_subid {
+ u16 subvendor;
+ u16 subdevice;
+ u32 card;
+};
+
+struct cx23885_i2c {
+ struct cx23885_dev *dev;
+
+ int nr;
+
+ /* i2c i/o */
+ struct i2c_adapter i2c_adap;
+ struct i2c_algo_bit_data i2c_algo;
+ struct i2c_client i2c_client;
+ u32 i2c_rc;
+
+ /* 885 registers used for raw addess */
+ u32 i2c_period;
+ u32 reg_ctrl;
+ u32 reg_stat;
+ u32 reg_addr;
+ u32 reg_rdata;
+ u32 reg_wdata;
+};
+
+struct cx23885_dmaqueue {
+ struct list_head active;
+ struct list_head queued;
+ struct timer_list timeout;
+ struct btcx_riscmem stopper;
+ u32 count;
+};
+
+struct cx23885_tsport {
+ struct cx23885_dev *dev;
+
+ int nr;
+ int sram_chno;
+
+ struct videobuf_dvb_frontends frontends;
+
+ /* dma queues */
+ struct cx23885_dmaqueue mpegq;
+ u32 ts_packet_size;
+ u32 ts_packet_count;
+
+ int width;
+ int height;
+
+ spinlock_t slock;
+
+ /* registers */
+ u32 reg_gpcnt;
+ u32 reg_gpcnt_ctl;
+ u32 reg_dma_ctl;
+ u32 reg_lngth;
+ u32 reg_hw_sop_ctrl;
+ u32 reg_gen_ctrl;
+ u32 reg_bd_pkt_status;
+ u32 reg_sop_status;
+ u32 reg_fifo_ovfl_stat;
+ u32 reg_vld_misc;
+ u32 reg_ts_clk_en;
+ u32 reg_ts_int_msk;
+ u32 reg_ts_int_stat;
+ u32 reg_src_sel;
+
+ /* Default register vals */
+ int pci_irqmask;
+ u32 dma_ctl_val;
+ u32 ts_int_msk_val;
+ u32 gen_ctrl_val;
+ u32 ts_clk_en_val;
+ u32 src_sel_val;
+ u32 vld_misc_val;
+ u32 hw_sop_ctrl_val;
+
+ /* Allow a single tsport to have multiple frontends */
+ u32 num_frontends;
+ void (*gate_ctrl)(struct cx23885_tsport *port, int open);
+ void *port_priv;
+
+ /* Workaround for a temp dvb_frontend that the tuner can attached to */
+ struct dvb_frontend analog_fe;
+};
+
+struct cx23885_kernel_ir {
+ struct cx23885_dev *cx;
+ char *name;
+ char *phys;
+
+ struct rc_dev *rc;
+};
+
+struct cx23885_audio_buffer {
+ unsigned int bpl;
+ struct btcx_riscmem risc;
+ struct videobuf_dmabuf dma;
+};
+
+struct cx23885_audio_dev {
+ struct cx23885_dev *dev;
+
+ struct pci_dev *pci;
+
+ struct snd_card *card;
+
+ spinlock_t lock;
+
+ atomic_t count;
+
+ unsigned int dma_size;
+ unsigned int period_size;
+ unsigned int num_periods;
+
+ struct videobuf_dmabuf *dma_risc;
+
+ struct cx23885_audio_buffer *buf;
+
+ struct snd_pcm_substream *substream;
+};
+
+struct cx23885_dev {
+ atomic_t refcount;
+ struct v4l2_device v4l2_dev;
+
+ /* pci stuff */
+ struct pci_dev *pci;
+ unsigned char pci_rev, pci_lat;
+ int pci_bus, pci_slot;
+ u32 __iomem *lmmio;
+ u8 __iomem *bmmio;
+ int pci_irqmask;
+ spinlock_t pci_irqmask_lock; /* protects mask reg too */
+ int hwrevision;
+
+ /* This valud is board specific and is used to configure the
+ * AV core so we see nice clean and stable video and audio. */
+ u32 clk_freq;
+
+ /* I2C adapters: Master 1 & 2 (External) & Master 3 (Internal only) */
+ struct cx23885_i2c i2c_bus[3];
+
+ int nr;
+ struct mutex lock;
+ struct mutex gpio_lock;
+
+ /* board details */
+ unsigned int board;
+ char name[32];
+
+ struct cx23885_tsport ts1, ts2;
+
+ /* sram configuration */
+ struct sram_channel *sram_channels;
+
+ enum {
+ CX23885_BRIDGE_UNDEFINED = 0,
+ CX23885_BRIDGE_885 = 885,
+ CX23885_BRIDGE_887 = 887,
+ CX23885_BRIDGE_888 = 888,
+ } bridge;
+
+ /* Analog video */
+ u32 resources;
+ unsigned int input;
+ unsigned int audinput; /* Selectable audio input */
+ u32 tvaudio;
+ v4l2_std_id tvnorm;
+ unsigned int tuner_type;
+ unsigned char tuner_addr;
+ unsigned int tuner_bus;
+ unsigned int radio_type;
+ unsigned char radio_addr;
+ unsigned int has_radio;
+ struct v4l2_subdev *sd_cx25840;
+ struct work_struct cx25840_work;
+
+ /* Infrared */
+ struct v4l2_subdev *sd_ir;
+ struct work_struct ir_rx_work;
+ unsigned long ir_rx_notifications;
+ struct work_struct ir_tx_work;
+ unsigned long ir_tx_notifications;
+
+ struct cx23885_kernel_ir *kernel_ir;
+ atomic_t ir_input_stopping;
+
+ /* V4l */
+ u32 freq;
+ struct video_device *video_dev;
+ struct video_device *vbi_dev;
+ struct video_device *radio_dev;
+
+ struct cx23885_dmaqueue vidq;
+ struct cx23885_dmaqueue vbiq;
+ spinlock_t slock;
+
+ /* MPEG Encoder ONLY settings */
+ u32 cx23417_mailbox;
+ struct cx2341x_mpeg_params mpeg_params;
+ struct video_device *v4l_device;
+ atomic_t v4l_reader_count;
+ struct cx23885_tvnorm encodernorm;
+
+ /* Analog raw audio */
+ struct cx23885_audio_dev *audio_dev;
+
+};
+
+static inline struct cx23885_dev *to_cx23885(struct v4l2_device *v4l2_dev)
+{
+ return container_of(v4l2_dev, struct cx23885_dev, v4l2_dev);
+}
+
+#define call_all(dev, o, f, args...) \
+ v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args)
+
+#define CX23885_HW_888_IR (1 << 0)
+#define CX23885_HW_AV_CORE (1 << 1)
+
+#define call_hw(dev, grpid, o, f, args...) \
+ v4l2_device_call_all(&dev->v4l2_dev, grpid, o, f, ##args)
+
+extern struct v4l2_subdev *cx23885_find_hw(struct cx23885_dev *dev, u32 hw);
+
+#define SRAM_CH01 0 /* Video A */
+#define SRAM_CH02 1 /* VBI A */
+#define SRAM_CH03 2 /* Video B */
+#define SRAM_CH04 3 /* Transport via B */
+#define SRAM_CH05 4 /* VBI B */
+#define SRAM_CH06 5 /* Video C */
+#define SRAM_CH07 6 /* Transport via C */
+#define SRAM_CH08 7 /* Audio Internal A */
+#define SRAM_CH09 8 /* Audio Internal B */
+#define SRAM_CH10 9 /* Audio External */
+#define SRAM_CH11 10 /* COMB_3D_N */
+#define SRAM_CH12 11 /* Comb 3D N1 */
+#define SRAM_CH13 12 /* Comb 3D N2 */
+#define SRAM_CH14 13 /* MOE Vid */
+#define SRAM_CH15 14 /* MOE RSLT */
+
+struct sram_channel {
+ char *name;
+ u32 cmds_start;
+ u32 ctrl_start;
+ u32 cdt;
+ u32 fifo_start;
+ u32 fifo_size;
+ u32 ptr1_reg;
+ u32 ptr2_reg;
+ u32 cnt1_reg;
+ u32 cnt2_reg;
+ u32 jumponly;
+};
+
+/* ----------------------------------------------------------- */
+
+#define cx_read(reg) readl(dev->lmmio + ((reg)>>2))
+#define cx_write(reg, value) writel((value), dev->lmmio + ((reg)>>2))
+
+#define cx_andor(reg, mask, value) \
+ writel((readl(dev->lmmio+((reg)>>2)) & ~(mask)) |\
+ ((value) & (mask)), dev->lmmio+((reg)>>2))
+
+#define cx_set(reg, bit) cx_andor((reg), (bit), (bit))
+#define cx_clear(reg, bit) cx_andor((reg), (bit), 0)
+
+/* ----------------------------------------------------------- */
+/* cx23885-core.c */
+
+extern int cx23885_sram_channel_setup(struct cx23885_dev *dev,
+ struct sram_channel *ch,
+ unsigned int bpl, u32 risc);
+
+extern void cx23885_sram_channel_dump(struct cx23885_dev *dev,
+ struct sram_channel *ch);
+
+extern int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
+ u32 reg, u32 mask, u32 value);
+
+extern int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+ struct scatterlist *sglist,
+ unsigned int top_offset, unsigned int bottom_offset,
+ unsigned int bpl, unsigned int padding, unsigned int lines);
+
+extern int cx23885_risc_vbibuffer(struct pci_dev *pci,
+ struct btcx_riscmem *risc, struct scatterlist *sglist,
+ unsigned int top_offset, unsigned int bottom_offset,
+ unsigned int bpl, unsigned int padding, unsigned int lines);
+
+void cx23885_cancel_buffers(struct cx23885_tsport *port);
+
+extern int cx23885_restart_queue(struct cx23885_tsport *port,
+ struct cx23885_dmaqueue *q);
+
+extern void cx23885_wakeup(struct cx23885_tsport *port,
+ struct cx23885_dmaqueue *q, u32 count);
+
+extern void cx23885_gpio_set(struct cx23885_dev *dev, u32 mask);
+extern void cx23885_gpio_clear(struct cx23885_dev *dev, u32 mask);
+extern u32 cx23885_gpio_get(struct cx23885_dev *dev, u32 mask);
+extern void cx23885_gpio_enable(struct cx23885_dev *dev, u32 mask,
+ int asoutput);
+
+extern void cx23885_irq_add_enable(struct cx23885_dev *dev, u32 mask);
+extern void cx23885_irq_enable(struct cx23885_dev *dev, u32 mask);
+extern void cx23885_irq_disable(struct cx23885_dev *dev, u32 mask);
+extern void cx23885_irq_remove(struct cx23885_dev *dev, u32 mask);
+
+/* ----------------------------------------------------------- */
+/* cx23885-cards.c */
+extern struct cx23885_board cx23885_boards[];
+extern const unsigned int cx23885_bcount;
+
+extern struct cx23885_subid cx23885_subids[];
+extern const unsigned int cx23885_idcount;
+
+extern int cx23885_tuner_callback(void *priv, int component,
+ int command, int arg);
+extern void cx23885_card_list(struct cx23885_dev *dev);
+extern int cx23885_ir_init(struct cx23885_dev *dev);
+extern void cx23885_ir_pci_int_enable(struct cx23885_dev *dev);
+extern void cx23885_ir_fini(struct cx23885_dev *dev);
+extern void cx23885_gpio_setup(struct cx23885_dev *dev);
+extern void cx23885_card_setup(struct cx23885_dev *dev);
+extern void cx23885_card_setup_pre_i2c(struct cx23885_dev *dev);
+
+extern int cx23885_dvb_register(struct cx23885_tsport *port);
+extern int cx23885_dvb_unregister(struct cx23885_tsport *port);
+
+extern int cx23885_buf_prepare(struct videobuf_queue *q,
+ struct cx23885_tsport *port,
+ struct cx23885_buffer *buf,
+ enum v4l2_field field);
+extern void cx23885_buf_queue(struct cx23885_tsport *port,
+ struct cx23885_buffer *buf);
+extern void cx23885_free_buffer(struct videobuf_queue *q,
+ struct cx23885_buffer *buf);
+
+/* ----------------------------------------------------------- */
+/* cx23885-video.c */
+/* Video */
+extern int cx23885_video_register(struct cx23885_dev *dev);
+extern void cx23885_video_unregister(struct cx23885_dev *dev);
+extern int cx23885_video_irq(struct cx23885_dev *dev, u32 status);
+extern void cx23885_video_wakeup(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q, u32 count);
+int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i);
+int cx23885_set_input(struct file *file, void *priv, unsigned int i);
+int cx23885_get_input(struct file *file, void *priv, unsigned int *i);
+int cx23885_set_frequency(struct file *file, void *priv, struct v4l2_frequency *f);
+int cx23885_set_control(struct cx23885_dev *dev, struct v4l2_control *ctl);
+int cx23885_get_control(struct cx23885_dev *dev, struct v4l2_control *ctl);
+int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm);
+
+/* ----------------------------------------------------------- */
+/* cx23885-vbi.c */
+extern int cx23885_vbi_fmt(struct file *file, void *priv,
+ struct v4l2_format *f);
+extern void cx23885_vbi_timeout(unsigned long data);
+extern struct videobuf_queue_ops cx23885_vbi_qops;
+extern int cx23885_restart_vbi_queue(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q);
+extern int cx23885_vbi_irq(struct cx23885_dev *dev, u32 status);
+
+/* cx23885-i2c.c */
+extern int cx23885_i2c_register(struct cx23885_i2c *bus);
+extern int cx23885_i2c_unregister(struct cx23885_i2c *bus);
+extern void cx23885_av_clk(struct cx23885_dev *dev, int enable);
+
+/* ----------------------------------------------------------- */
+/* cx23885-417.c */
+extern int cx23885_417_register(struct cx23885_dev *dev);
+extern void cx23885_417_unregister(struct cx23885_dev *dev);
+extern int cx23885_irq_417(struct cx23885_dev *dev, u32 status);
+extern void cx23885_417_check_encoder(struct cx23885_dev *dev);
+extern void cx23885_mc417_init(struct cx23885_dev *dev);
+extern int mc417_memory_read(struct cx23885_dev *dev, u32 address, u32 *value);
+extern int mc417_memory_write(struct cx23885_dev *dev, u32 address, u32 value);
+extern int mc417_register_read(struct cx23885_dev *dev,
+ u16 address, u32 *value);
+extern int mc417_register_write(struct cx23885_dev *dev,
+ u16 address, u32 value);
+extern void mc417_gpio_set(struct cx23885_dev *dev, u32 mask);
+extern void mc417_gpio_clear(struct cx23885_dev *dev, u32 mask);
+extern void mc417_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput);
+
+/* ----------------------------------------------------------- */
+/* cx23885-alsa.c */
+extern struct cx23885_audio_dev *cx23885_audio_register(
+ struct cx23885_dev *dev);
+extern void cx23885_audio_unregister(struct cx23885_dev *dev);
+extern int cx23885_audio_irq(struct cx23885_dev *dev, u32 status, u32 mask);
+extern int cx23885_risc_databuffer(struct pci_dev *pci,
+ struct btcx_riscmem *risc,
+ struct scatterlist *sglist,
+ unsigned int bpl,
+ unsigned int lines,
+ unsigned int lpi);
+
+/* ----------------------------------------------------------- */
+/* tv norms */
+
+static inline unsigned int norm_maxw(v4l2_std_id norm)
+{
+ return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 720 : 768;
+}
+
+static inline unsigned int norm_maxh(v4l2_std_id norm)
+{
+ return (norm & V4L2_STD_625_50) ? 576 : 480;
+}
+
+static inline unsigned int norm_swidth(v4l2_std_id norm)
+{
+ return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922;
+}
diff --git a/drivers/media/video/cx23885/cx23888-ir.c b/drivers/media/video/cx23885/cx23888-ir.c
new file mode 100644
index 00000000..bb1ce346
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23888-ir.c
@@ -0,0 +1,1273 @@
+/*
+ * Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ * CX23888 Integrated Consumer Infrared Controller
+ *
+ * Copyright (C) 2009 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <linux/kfifo.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/rc-core.h>
+
+#include "cx23885.h"
+
+static unsigned int ir_888_debug;
+module_param(ir_888_debug, int, 0644);
+MODULE_PARM_DESC(ir_888_debug, "enable debug messages [CX23888 IR controller]");
+
+#define CX23888_IR_REG_BASE 0x170000
+/*
+ * These CX23888 register offsets have a straightforward one to one mapping
+ * to the CX23885 register offsets of 0x200 through 0x218
+ */
+#define CX23888_IR_CNTRL_REG 0x170000
+#define CNTRL_WIN_3_3 0x00000000
+#define CNTRL_WIN_4_3 0x00000001
+#define CNTRL_WIN_3_4 0x00000002
+#define CNTRL_WIN_4_4 0x00000003
+#define CNTRL_WIN 0x00000003
+#define CNTRL_EDG_NONE 0x00000000
+#define CNTRL_EDG_FALL 0x00000004
+#define CNTRL_EDG_RISE 0x00000008
+#define CNTRL_EDG_BOTH 0x0000000C
+#define CNTRL_EDG 0x0000000C
+#define CNTRL_DMD 0x00000010
+#define CNTRL_MOD 0x00000020
+#define CNTRL_RFE 0x00000040
+#define CNTRL_TFE 0x00000080
+#define CNTRL_RXE 0x00000100
+#define CNTRL_TXE 0x00000200
+#define CNTRL_RIC 0x00000400
+#define CNTRL_TIC 0x00000800
+#define CNTRL_CPL 0x00001000
+#define CNTRL_LBM 0x00002000
+#define CNTRL_R 0x00004000
+/* CX23888 specific control flag */
+#define CNTRL_IVO 0x00008000
+
+#define CX23888_IR_TXCLK_REG 0x170004
+#define TXCLK_TCD 0x0000FFFF
+
+#define CX23888_IR_RXCLK_REG 0x170008
+#define RXCLK_RCD 0x0000FFFF
+
+#define CX23888_IR_CDUTY_REG 0x17000C
+#define CDUTY_CDC 0x0000000F
+
+#define CX23888_IR_STATS_REG 0x170010
+#define STATS_RTO 0x00000001
+#define STATS_ROR 0x00000002
+#define STATS_RBY 0x00000004
+#define STATS_TBY 0x00000008
+#define STATS_RSR 0x00000010
+#define STATS_TSR 0x00000020
+
+#define CX23888_IR_IRQEN_REG 0x170014
+#define IRQEN_RTE 0x00000001
+#define IRQEN_ROE 0x00000002
+#define IRQEN_RSE 0x00000010
+#define IRQEN_TSE 0x00000020
+
+#define CX23888_IR_FILTR_REG 0x170018
+#define FILTR_LPF 0x0000FFFF
+
+/* This register doesn't follow the pattern; it's 0x23C on a CX23885 */
+#define CX23888_IR_FIFO_REG 0x170040
+#define FIFO_RXTX 0x0000FFFF
+#define FIFO_RXTX_LVL 0x00010000
+#define FIFO_RXTX_RTO 0x0001FFFF
+#define FIFO_RX_NDV 0x00020000
+#define FIFO_RX_DEPTH 8
+#define FIFO_TX_DEPTH 8
+
+/* CX23888 unique registers */
+#define CX23888_IR_SEEDP_REG 0x17001C
+#define CX23888_IR_TIMOL_REG 0x170020
+#define CX23888_IR_WAKE0_REG 0x170024
+#define CX23888_IR_WAKE1_REG 0x170028
+#define CX23888_IR_WAKE2_REG 0x17002C
+#define CX23888_IR_MASK0_REG 0x170030
+#define CX23888_IR_MASK1_REG 0x170034
+#define CX23888_IR_MAKS2_REG 0x170038
+#define CX23888_IR_DPIPG_REG 0x17003C
+#define CX23888_IR_LEARN_REG 0x170044
+
+#define CX23888_VIDCLK_FREQ 108000000 /* 108 MHz, BT.656 */
+#define CX23888_IR_REFCLK_FREQ (CX23888_VIDCLK_FREQ / 2)
+
+/*
+ * We use this union internally for convenience, but callers to tx_write
+ * and rx_read will be expecting records of type struct ir_raw_event.
+ * Always ensure the size of this union is dictated by struct ir_raw_event.
+ */
+union cx23888_ir_fifo_rec {
+ u32 hw_fifo_data;
+ struct ir_raw_event ir_core_data;
+};
+
+#define CX23888_IR_RX_KFIFO_SIZE (256 * sizeof(union cx23888_ir_fifo_rec))
+#define CX23888_IR_TX_KFIFO_SIZE (256 * sizeof(union cx23888_ir_fifo_rec))
+
+struct cx23888_ir_state {
+ struct v4l2_subdev sd;
+ struct cx23885_dev *dev;
+ u32 id;
+ u32 rev;
+
+ struct v4l2_subdev_ir_parameters rx_params;
+ struct mutex rx_params_lock;
+ atomic_t rxclk_divider;
+ atomic_t rx_invert;
+
+ struct kfifo rx_kfifo;
+ spinlock_t rx_kfifo_lock;
+
+ struct v4l2_subdev_ir_parameters tx_params;
+ struct mutex tx_params_lock;
+ atomic_t txclk_divider;
+};
+
+static inline struct cx23888_ir_state *to_state(struct v4l2_subdev *sd)
+{
+ return v4l2_get_subdevdata(sd);
+}
+
+/*
+ * IR register block read and write functions
+ */
+static
+inline int cx23888_ir_write4(struct cx23885_dev *dev, u32 addr, u32 value)
+{
+ cx_write(addr, value);
+ return 0;
+}
+
+static inline u32 cx23888_ir_read4(struct cx23885_dev *dev, u32 addr)
+{
+ return cx_read(addr);
+}
+
+static inline int cx23888_ir_and_or4(struct cx23885_dev *dev, u32 addr,
+ u32 and_mask, u32 or_value)
+{
+ cx_andor(addr, ~and_mask, or_value);
+ return 0;
+}
+
+/*
+ * Rx and Tx Clock Divider register computations
+ *
+ * Note the largest clock divider value of 0xffff corresponds to:
+ * (0xffff + 1) * 1000 / 108/2 MHz = 1,213,629.629... ns
+ * which fits in 21 bits, so we'll use unsigned int for time arguments.
+ */
+static inline u16 count_to_clock_divider(unsigned int d)
+{
+ if (d > RXCLK_RCD + 1)
+ d = RXCLK_RCD;
+ else if (d < 2)
+ d = 1;
+ else
+ d--;
+ return (u16) d;
+}
+
+static inline u16 ns_to_clock_divider(unsigned int ns)
+{
+ return count_to_clock_divider(
+ DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ / 1000000 * ns, 1000));
+}
+
+static inline unsigned int clock_divider_to_ns(unsigned int divider)
+{
+ /* Period of the Rx or Tx clock in ns */
+ return DIV_ROUND_CLOSEST((divider + 1) * 1000,
+ CX23888_IR_REFCLK_FREQ / 1000000);
+}
+
+static inline u16 carrier_freq_to_clock_divider(unsigned int freq)
+{
+ return count_to_clock_divider(
+ DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, freq * 16));
+}
+
+static inline unsigned int clock_divider_to_carrier_freq(unsigned int divider)
+{
+ return DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, (divider + 1) * 16);
+}
+
+static inline u16 freq_to_clock_divider(unsigned int freq,
+ unsigned int rollovers)
+{
+ return count_to_clock_divider(
+ DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, freq * rollovers));
+}
+
+static inline unsigned int clock_divider_to_freq(unsigned int divider,
+ unsigned int rollovers)
+{
+ return DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ,
+ (divider + 1) * rollovers);
+}
+
+/*
+ * Low Pass Filter register calculations
+ *
+ * Note the largest count value of 0xffff corresponds to:
+ * 0xffff * 1000 / 108/2 MHz = 1,213,611.11... ns
+ * which fits in 21 bits, so we'll use unsigned int for time arguments.
+ */
+static inline u16 count_to_lpf_count(unsigned int d)
+{
+ if (d > FILTR_LPF)
+ d = FILTR_LPF;
+ else if (d < 4)
+ d = 0;
+ return (u16) d;
+}
+
+static inline u16 ns_to_lpf_count(unsigned int ns)
+{
+ return count_to_lpf_count(
+ DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ / 1000000 * ns, 1000));
+}
+
+static inline unsigned int lpf_count_to_ns(unsigned int count)
+{
+ /* Duration of the Low Pass Filter rejection window in ns */
+ return DIV_ROUND_CLOSEST(count * 1000,
+ CX23888_IR_REFCLK_FREQ / 1000000);
+}
+
+static inline unsigned int lpf_count_to_us(unsigned int count)
+{
+ /* Duration of the Low Pass Filter rejection window in us */
+ return DIV_ROUND_CLOSEST(count, CX23888_IR_REFCLK_FREQ / 1000000);
+}
+
+/*
+ * FIFO register pulse width count compuations
+ */
+static u32 clock_divider_to_resolution(u16 divider)
+{
+ /*
+ * Resolution is the duration of 1 tick of the readable portion of
+ * of the pulse width counter as read from the FIFO. The two lsb's are
+ * not readable, hence the << 2. This function returns ns.
+ */
+ return DIV_ROUND_CLOSEST((1 << 2) * ((u32) divider + 1) * 1000,
+ CX23888_IR_REFCLK_FREQ / 1000000);
+}
+
+static u64 pulse_width_count_to_ns(u16 count, u16 divider)
+{
+ u64 n;
+ u32 rem;
+
+ /*
+ * The 2 lsb's of the pulse width timer count are not readable, hence
+ * the (count << 2) | 0x3
+ */
+ n = (((u64) count << 2) | 0x3) * (divider + 1) * 1000; /* millicycles */
+ rem = do_div(n, CX23888_IR_REFCLK_FREQ / 1000000); /* / MHz => ns */
+ if (rem >= CX23888_IR_REFCLK_FREQ / 1000000 / 2)
+ n++;
+ return n;
+}
+
+static unsigned int pulse_width_count_to_us(u16 count, u16 divider)
+{
+ u64 n;
+ u32 rem;
+
+ /*
+ * The 2 lsb's of the pulse width timer count are not readable, hence
+ * the (count << 2) | 0x3
+ */
+ n = (((u64) count << 2) | 0x3) * (divider + 1); /* cycles */
+ rem = do_div(n, CX23888_IR_REFCLK_FREQ / 1000000); /* / MHz => us */
+ if (rem >= CX23888_IR_REFCLK_FREQ / 1000000 / 2)
+ n++;
+ return (unsigned int) n;
+}
+
+/*
+ * Pulse Clocks computations: Combined Pulse Width Count & Rx Clock Counts
+ *
+ * The total pulse clock count is an 18 bit pulse width timer count as the most
+ * significant part and (up to) 16 bit clock divider count as a modulus.
+ * When the Rx clock divider ticks down to 0, it increments the 18 bit pulse
+ * width timer count's least significant bit.
+ */
+static u64 ns_to_pulse_clocks(u32 ns)
+{
+ u64 clocks;
+ u32 rem;
+ clocks = CX23888_IR_REFCLK_FREQ / 1000000 * (u64) ns; /* millicycles */
+ rem = do_div(clocks, 1000); /* /1000 = cycles */
+ if (rem >= 1000 / 2)
+ clocks++;
+ return clocks;
+}
+
+static u16 pulse_clocks_to_clock_divider(u64 count)
+{
+ u32 rem;
+
+ rem = do_div(count, (FIFO_RXTX << 2) | 0x3);
+
+ /* net result needs to be rounded down and decremented by 1 */
+ if (count > RXCLK_RCD + 1)
+ count = RXCLK_RCD;
+ else if (count < 2)
+ count = 1;
+ else
+ count--;
+ return (u16) count;
+}
+
+/*
+ * IR Control Register helpers
+ */
+enum tx_fifo_watermark {
+ TX_FIFO_HALF_EMPTY = 0,
+ TX_FIFO_EMPTY = CNTRL_TIC,
+};
+
+enum rx_fifo_watermark {
+ RX_FIFO_HALF_FULL = 0,
+ RX_FIFO_NOT_EMPTY = CNTRL_RIC,
+};
+
+static inline void control_tx_irq_watermark(struct cx23885_dev *dev,
+ enum tx_fifo_watermark level)
+{
+ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_TIC, level);
+}
+
+static inline void control_rx_irq_watermark(struct cx23885_dev *dev,
+ enum rx_fifo_watermark level)
+{
+ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_RIC, level);
+}
+
+static inline void control_tx_enable(struct cx23885_dev *dev, bool enable)
+{
+ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~(CNTRL_TXE | CNTRL_TFE),
+ enable ? (CNTRL_TXE | CNTRL_TFE) : 0);
+}
+
+static inline void control_rx_enable(struct cx23885_dev *dev, bool enable)
+{
+ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~(CNTRL_RXE | CNTRL_RFE),
+ enable ? (CNTRL_RXE | CNTRL_RFE) : 0);
+}
+
+static inline void control_tx_modulation_enable(struct cx23885_dev *dev,
+ bool enable)
+{
+ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_MOD,
+ enable ? CNTRL_MOD : 0);
+}
+
+static inline void control_rx_demodulation_enable(struct cx23885_dev *dev,
+ bool enable)
+{
+ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_DMD,
+ enable ? CNTRL_DMD : 0);
+}
+
+static inline void control_rx_s_edge_detection(struct cx23885_dev *dev,
+ u32 edge_types)
+{
+ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_EDG_BOTH,
+ edge_types & CNTRL_EDG_BOTH);
+}
+
+static void control_rx_s_carrier_window(struct cx23885_dev *dev,
+ unsigned int carrier,
+ unsigned int *carrier_range_low,
+ unsigned int *carrier_range_high)
+{
+ u32 v;
+ unsigned int c16 = carrier * 16;
+
+ if (*carrier_range_low < DIV_ROUND_CLOSEST(c16, 16 + 3)) {
+ v = CNTRL_WIN_3_4;
+ *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 4);
+ } else {
+ v = CNTRL_WIN_3_3;
+ *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 3);
+ }
+
+ if (*carrier_range_high > DIV_ROUND_CLOSEST(c16, 16 - 3)) {
+ v |= CNTRL_WIN_4_3;
+ *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 4);
+ } else {
+ v |= CNTRL_WIN_3_3;
+ *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 3);
+ }
+ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_WIN, v);
+}
+
+static inline void control_tx_polarity_invert(struct cx23885_dev *dev,
+ bool invert)
+{
+ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_CPL,
+ invert ? CNTRL_CPL : 0);
+}
+
+static inline void control_tx_level_invert(struct cx23885_dev *dev,
+ bool invert)
+{
+ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_IVO,
+ invert ? CNTRL_IVO : 0);
+}
+
+/*
+ * IR Rx & Tx Clock Register helpers
+ */
+static unsigned int txclk_tx_s_carrier(struct cx23885_dev *dev,
+ unsigned int freq,
+ u16 *divider)
+{
+ *divider = carrier_freq_to_clock_divider(freq);
+ cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, *divider);
+ return clock_divider_to_carrier_freq(*divider);
+}
+
+static unsigned int rxclk_rx_s_carrier(struct cx23885_dev *dev,
+ unsigned int freq,
+ u16 *divider)
+{
+ *divider = carrier_freq_to_clock_divider(freq);
+ cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, *divider);
+ return clock_divider_to_carrier_freq(*divider);
+}
+
+static u32 txclk_tx_s_max_pulse_width(struct cx23885_dev *dev, u32 ns,
+ u16 *divider)
+{
+ u64 pulse_clocks;
+
+ if (ns > IR_MAX_DURATION)
+ ns = IR_MAX_DURATION;
+ pulse_clocks = ns_to_pulse_clocks(ns);
+ *divider = pulse_clocks_to_clock_divider(pulse_clocks);
+ cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, *divider);
+ return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider);
+}
+
+static u32 rxclk_rx_s_max_pulse_width(struct cx23885_dev *dev, u32 ns,
+ u16 *divider)
+{
+ u64 pulse_clocks;
+
+ if (ns > IR_MAX_DURATION)
+ ns = IR_MAX_DURATION;
+ pulse_clocks = ns_to_pulse_clocks(ns);
+ *divider = pulse_clocks_to_clock_divider(pulse_clocks);
+ cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, *divider);
+ return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider);
+}
+
+/*
+ * IR Tx Carrier Duty Cycle register helpers
+ */
+static unsigned int cduty_tx_s_duty_cycle(struct cx23885_dev *dev,
+ unsigned int duty_cycle)
+{
+ u32 n;
+ n = DIV_ROUND_CLOSEST(duty_cycle * 100, 625); /* 16ths of 100% */
+ if (n != 0)
+ n--;
+ if (n > 15)
+ n = 15;
+ cx23888_ir_write4(dev, CX23888_IR_CDUTY_REG, n);
+ return DIV_ROUND_CLOSEST((n + 1) * 100, 16);
+}
+
+/*
+ * IR Filter Register helpers
+ */
+static u32 filter_rx_s_min_width(struct cx23885_dev *dev, u32 min_width_ns)
+{
+ u32 count = ns_to_lpf_count(min_width_ns);
+ cx23888_ir_write4(dev, CX23888_IR_FILTR_REG, count);
+ return lpf_count_to_ns(count);
+}
+
+/*
+ * IR IRQ Enable Register helpers
+ */
+static inline void irqenable_rx(struct cx23885_dev *dev, u32 mask)
+{
+ mask &= (IRQEN_RTE | IRQEN_ROE | IRQEN_RSE);
+ cx23888_ir_and_or4(dev, CX23888_IR_IRQEN_REG,
+ ~(IRQEN_RTE | IRQEN_ROE | IRQEN_RSE), mask);
+}
+
+static inline void irqenable_tx(struct cx23885_dev *dev, u32 mask)
+{
+ mask &= IRQEN_TSE;
+ cx23888_ir_and_or4(dev, CX23888_IR_IRQEN_REG, ~IRQEN_TSE, mask);
+}
+
+/*
+ * V4L2 Subdevice IR Ops
+ */
+static int cx23888_ir_irq_handler(struct v4l2_subdev *sd, u32 status,
+ bool *handled)
+{
+ struct cx23888_ir_state *state = to_state(sd);
+ struct cx23885_dev *dev = state->dev;
+ unsigned long flags;
+
+ u32 cntrl = cx23888_ir_read4(dev, CX23888_IR_CNTRL_REG);
+ u32 irqen = cx23888_ir_read4(dev, CX23888_IR_IRQEN_REG);
+ u32 stats = cx23888_ir_read4(dev, CX23888_IR_STATS_REG);
+
+ union cx23888_ir_fifo_rec rx_data[FIFO_RX_DEPTH];
+ unsigned int i, j, k;
+ u32 events, v;
+ int tsr, rsr, rto, ror, tse, rse, rte, roe, kror;
+
+ tsr = stats & STATS_TSR; /* Tx FIFO Service Request */
+ rsr = stats & STATS_RSR; /* Rx FIFO Service Request */
+ rto = stats & STATS_RTO; /* Rx Pulse Width Timer Time Out */
+ ror = stats & STATS_ROR; /* Rx FIFO Over Run */
+
+ tse = irqen & IRQEN_TSE; /* Tx FIFO Service Request IRQ Enable */
+ rse = irqen & IRQEN_RSE; /* Rx FIFO Service Reuqest IRQ Enable */
+ rte = irqen & IRQEN_RTE; /* Rx Pulse Width Timer Time Out IRQ Enable */
+ roe = irqen & IRQEN_ROE; /* Rx FIFO Over Run IRQ Enable */
+
+ *handled = false;
+ v4l2_dbg(2, ir_888_debug, sd, "IRQ Status: %s %s %s %s %s %s\n",
+ tsr ? "tsr" : " ", rsr ? "rsr" : " ",
+ rto ? "rto" : " ", ror ? "ror" : " ",
+ stats & STATS_TBY ? "tby" : " ",
+ stats & STATS_RBY ? "rby" : " ");
+
+ v4l2_dbg(2, ir_888_debug, sd, "IRQ Enables: %s %s %s %s\n",
+ tse ? "tse" : " ", rse ? "rse" : " ",
+ rte ? "rte" : " ", roe ? "roe" : " ");
+
+ /*
+ * Transmitter interrupt service
+ */
+ if (tse && tsr) {
+ /*
+ * TODO:
+ * Check the watermark threshold setting
+ * Pull FIFO_TX_DEPTH or FIFO_TX_DEPTH/2 entries from tx_kfifo
+ * Push the data to the hardware FIFO.
+ * If there was nothing more to send in the tx_kfifo, disable
+ * the TSR IRQ and notify the v4l2_device.
+ * If there was something in the tx_kfifo, check the tx_kfifo
+ * level and notify the v4l2_device, if it is low.
+ */
+ /* For now, inhibit TSR interrupt until Tx is implemented */
+ irqenable_tx(dev, 0);
+ events = V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ;
+ v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_TX_NOTIFY, &events);
+ *handled = true;
+ }
+
+ /*
+ * Receiver interrupt service
+ */
+ kror = 0;
+ if ((rse && rsr) || (rte && rto)) {
+ /*
+ * Receive data on RSR to clear the STATS_RSR.
+ * Receive data on RTO, since we may not have yet hit the RSR
+ * watermark when we receive the RTO.
+ */
+ for (i = 0, v = FIFO_RX_NDV;
+ (v & FIFO_RX_NDV) && !kror; i = 0) {
+ for (j = 0;
+ (v & FIFO_RX_NDV) && j < FIFO_RX_DEPTH; j++) {
+ v = cx23888_ir_read4(dev, CX23888_IR_FIFO_REG);
+ rx_data[i].hw_fifo_data = v & ~FIFO_RX_NDV;
+ i++;
+ }
+ if (i == 0)
+ break;
+ j = i * sizeof(union cx23888_ir_fifo_rec);
+ k = kfifo_in_locked(&state->rx_kfifo,
+ (unsigned char *) rx_data, j,
+ &state->rx_kfifo_lock);
+ if (k != j)
+ kror++; /* rx_kfifo over run */
+ }
+ *handled = true;
+ }
+
+ events = 0;
+ v = 0;
+ if (kror) {
+ events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN;
+ v4l2_err(sd, "IR receiver software FIFO overrun\n");
+ }
+ if (roe && ror) {
+ /*
+ * The RX FIFO Enable (CNTRL_RFE) must be toggled to clear
+ * the Rx FIFO Over Run status (STATS_ROR)
+ */
+ v |= CNTRL_RFE;
+ events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN;
+ v4l2_err(sd, "IR receiver hardware FIFO overrun\n");
+ }
+ if (rte && rto) {
+ /*
+ * The IR Receiver Enable (CNTRL_RXE) must be toggled to clear
+ * the Rx Pulse Width Timer Time Out (STATS_RTO)
+ */
+ v |= CNTRL_RXE;
+ events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED;
+ }
+ if (v) {
+ /* Clear STATS_ROR & STATS_RTO as needed by reseting hardware */
+ cx23888_ir_write4(dev, CX23888_IR_CNTRL_REG, cntrl & ~v);
+ cx23888_ir_write4(dev, CX23888_IR_CNTRL_REG, cntrl);
+ *handled = true;
+ }
+
+ spin_lock_irqsave(&state->rx_kfifo_lock, flags);
+ if (kfifo_len(&state->rx_kfifo) >= CX23888_IR_RX_KFIFO_SIZE / 2)
+ events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ;
+ spin_unlock_irqrestore(&state->rx_kfifo_lock, flags);
+
+ if (events)
+ v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_RX_NOTIFY, &events);
+ return 0;
+}
+
+/* Receiver */
+static int cx23888_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count,
+ ssize_t *num)
+{
+ struct cx23888_ir_state *state = to_state(sd);
+ bool invert = (bool) atomic_read(&state->rx_invert);
+ u16 divider = (u16) atomic_read(&state->rxclk_divider);
+
+ unsigned int i, n;
+ union cx23888_ir_fifo_rec *p;
+ unsigned u, v, w;
+
+ n = count / sizeof(union cx23888_ir_fifo_rec)
+ * sizeof(union cx23888_ir_fifo_rec);
+ if (n == 0) {
+ *num = 0;
+ return 0;
+ }
+
+ n = kfifo_out_locked(&state->rx_kfifo, buf, n, &state->rx_kfifo_lock);
+
+ n /= sizeof(union cx23888_ir_fifo_rec);
+ *num = n * sizeof(union cx23888_ir_fifo_rec);
+
+ for (p = (union cx23888_ir_fifo_rec *) buf, i = 0; i < n; p++, i++) {
+
+ if ((p->hw_fifo_data & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) {
+ /* Assume RTO was because of no IR light input */
+ u = 0;
+ w = 1;
+ } else {
+ u = (p->hw_fifo_data & FIFO_RXTX_LVL) ? 1 : 0;
+ if (invert)
+ u = u ? 0 : 1;
+ w = 0;
+ }
+
+ v = (unsigned) pulse_width_count_to_ns(
+ (u16) (p->hw_fifo_data & FIFO_RXTX), divider);
+ if (v > IR_MAX_DURATION)
+ v = IR_MAX_DURATION;
+
+ init_ir_raw_event(&p->ir_core_data);
+ p->ir_core_data.pulse = u;
+ p->ir_core_data.duration = v;
+ p->ir_core_data.timeout = w;
+
+ v4l2_dbg(2, ir_888_debug, sd, "rx read: %10u ns %s %s\n",
+ v, u ? "mark" : "space", w ? "(timed out)" : "");
+ if (w)
+ v4l2_dbg(2, ir_888_debug, sd, "rx read: end of rx\n");
+ }
+ return 0;
+}
+
+static int cx23888_ir_rx_g_parameters(struct v4l2_subdev *sd,
+ struct v4l2_subdev_ir_parameters *p)
+{
+ struct cx23888_ir_state *state = to_state(sd);
+ mutex_lock(&state->rx_params_lock);
+ memcpy(p, &state->rx_params, sizeof(struct v4l2_subdev_ir_parameters));
+ mutex_unlock(&state->rx_params_lock);
+ return 0;
+}
+
+static int cx23888_ir_rx_shutdown(struct v4l2_subdev *sd)
+{
+ struct cx23888_ir_state *state = to_state(sd);
+ struct cx23885_dev *dev = state->dev;
+
+ mutex_lock(&state->rx_params_lock);
+
+ /* Disable or slow down all IR Rx circuits and counters */
+ irqenable_rx(dev, 0);
+ control_rx_enable(dev, false);
+ control_rx_demodulation_enable(dev, false);
+ control_rx_s_edge_detection(dev, CNTRL_EDG_NONE);
+ filter_rx_s_min_width(dev, 0);
+ cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, RXCLK_RCD);
+
+ state->rx_params.shutdown = true;
+
+ mutex_unlock(&state->rx_params_lock);
+ return 0;
+}
+
+static int cx23888_ir_rx_s_parameters(struct v4l2_subdev *sd,
+ struct v4l2_subdev_ir_parameters *p)
+{
+ struct cx23888_ir_state *state = to_state(sd);
+ struct cx23885_dev *dev = state->dev;
+ struct v4l2_subdev_ir_parameters *o = &state->rx_params;
+ u16 rxclk_divider;
+
+ if (p->shutdown)
+ return cx23888_ir_rx_shutdown(sd);
+
+ if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH)
+ return -ENOSYS;
+
+ mutex_lock(&state->rx_params_lock);
+
+ o->shutdown = p->shutdown;
+
+ o->mode = p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+
+ o->bytes_per_data_element = p->bytes_per_data_element
+ = sizeof(union cx23888_ir_fifo_rec);
+
+ /* Before we tweak the hardware, we have to disable the receiver */
+ irqenable_rx(dev, 0);
+ control_rx_enable(dev, false);
+
+ control_rx_demodulation_enable(dev, p->modulation);
+ o->modulation = p->modulation;
+
+ if (p->modulation) {
+ p->carrier_freq = rxclk_rx_s_carrier(dev, p->carrier_freq,
+ &rxclk_divider);
+
+ o->carrier_freq = p->carrier_freq;
+
+ o->duty_cycle = p->duty_cycle = 50;
+
+ control_rx_s_carrier_window(dev, p->carrier_freq,
+ &p->carrier_range_lower,
+ &p->carrier_range_upper);
+ o->carrier_range_lower = p->carrier_range_lower;
+ o->carrier_range_upper = p->carrier_range_upper;
+
+ p->max_pulse_width =
+ (u32) pulse_width_count_to_ns(FIFO_RXTX, rxclk_divider);
+ } else {
+ p->max_pulse_width =
+ rxclk_rx_s_max_pulse_width(dev, p->max_pulse_width,
+ &rxclk_divider);
+ }
+ o->max_pulse_width = p->max_pulse_width;
+ atomic_set(&state->rxclk_divider, rxclk_divider);
+
+ p->noise_filter_min_width =
+ filter_rx_s_min_width(dev, p->noise_filter_min_width);
+ o->noise_filter_min_width = p->noise_filter_min_width;
+
+ p->resolution = clock_divider_to_resolution(rxclk_divider);
+ o->resolution = p->resolution;
+
+ /* FIXME - make this dependent on resolution for better performance */
+ control_rx_irq_watermark(dev, RX_FIFO_HALF_FULL);
+
+ control_rx_s_edge_detection(dev, CNTRL_EDG_BOTH);
+
+ o->invert_level = p->invert_level;
+ atomic_set(&state->rx_invert, p->invert_level);
+
+ o->interrupt_enable = p->interrupt_enable;
+ o->enable = p->enable;
+ if (p->enable) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&state->rx_kfifo_lock, flags);
+ kfifo_reset(&state->rx_kfifo);
+ /* reset tx_fifo too if there is one... */
+ spin_unlock_irqrestore(&state->rx_kfifo_lock, flags);
+ if (p->interrupt_enable)
+ irqenable_rx(dev, IRQEN_RSE | IRQEN_RTE | IRQEN_ROE);
+ control_rx_enable(dev, p->enable);
+ }
+
+ mutex_unlock(&state->rx_params_lock);
+ return 0;
+}
+
+/* Transmitter */
+static int cx23888_ir_tx_write(struct v4l2_subdev *sd, u8 *buf, size_t count,
+ ssize_t *num)
+{
+ struct cx23888_ir_state *state = to_state(sd);
+ struct cx23885_dev *dev = state->dev;
+ /* For now enable the Tx FIFO Service interrupt & pretend we did work */
+ irqenable_tx(dev, IRQEN_TSE);
+ *num = count;
+ return 0;
+}
+
+static int cx23888_ir_tx_g_parameters(struct v4l2_subdev *sd,
+ struct v4l2_subdev_ir_parameters *p)
+{
+ struct cx23888_ir_state *state = to_state(sd);
+ mutex_lock(&state->tx_params_lock);
+ memcpy(p, &state->tx_params, sizeof(struct v4l2_subdev_ir_parameters));
+ mutex_unlock(&state->tx_params_lock);
+ return 0;
+}
+
+static int cx23888_ir_tx_shutdown(struct v4l2_subdev *sd)
+{
+ struct cx23888_ir_state *state = to_state(sd);
+ struct cx23885_dev *dev = state->dev;
+
+ mutex_lock(&state->tx_params_lock);
+
+ /* Disable or slow down all IR Tx circuits and counters */
+ irqenable_tx(dev, 0);
+ control_tx_enable(dev, false);
+ control_tx_modulation_enable(dev, false);
+ cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, TXCLK_TCD);
+
+ state->tx_params.shutdown = true;
+
+ mutex_unlock(&state->tx_params_lock);
+ return 0;
+}
+
+static int cx23888_ir_tx_s_parameters(struct v4l2_subdev *sd,
+ struct v4l2_subdev_ir_parameters *p)
+{
+ struct cx23888_ir_state *state = to_state(sd);
+ struct cx23885_dev *dev = state->dev;
+ struct v4l2_subdev_ir_parameters *o = &state->tx_params;
+ u16 txclk_divider;
+
+ if (p->shutdown)
+ return cx23888_ir_tx_shutdown(sd);
+
+ if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH)
+ return -ENOSYS;
+
+ mutex_lock(&state->tx_params_lock);
+
+ o->shutdown = p->shutdown;
+
+ o->mode = p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+
+ o->bytes_per_data_element = p->bytes_per_data_element
+ = sizeof(union cx23888_ir_fifo_rec);
+
+ /* Before we tweak the hardware, we have to disable the transmitter */
+ irqenable_tx(dev, 0);
+ control_tx_enable(dev, false);
+
+ control_tx_modulation_enable(dev, p->modulation);
+ o->modulation = p->modulation;
+
+ if (p->modulation) {
+ p->carrier_freq = txclk_tx_s_carrier(dev, p->carrier_freq,
+ &txclk_divider);
+ o->carrier_freq = p->carrier_freq;
+
+ p->duty_cycle = cduty_tx_s_duty_cycle(dev, p->duty_cycle);
+ o->duty_cycle = p->duty_cycle;
+
+ p->max_pulse_width =
+ (u32) pulse_width_count_to_ns(FIFO_RXTX, txclk_divider);
+ } else {
+ p->max_pulse_width =
+ txclk_tx_s_max_pulse_width(dev, p->max_pulse_width,
+ &txclk_divider);
+ }
+ o->max_pulse_width = p->max_pulse_width;
+ atomic_set(&state->txclk_divider, txclk_divider);
+
+ p->resolution = clock_divider_to_resolution(txclk_divider);
+ o->resolution = p->resolution;
+
+ /* FIXME - make this dependent on resolution for better performance */
+ control_tx_irq_watermark(dev, TX_FIFO_HALF_EMPTY);
+
+ control_tx_polarity_invert(dev, p->invert_carrier_sense);
+ o->invert_carrier_sense = p->invert_carrier_sense;
+
+ control_tx_level_invert(dev, p->invert_level);
+ o->invert_level = p->invert_level;
+
+ o->interrupt_enable = p->interrupt_enable;
+ o->enable = p->enable;
+ if (p->enable) {
+ if (p->interrupt_enable)
+ irqenable_tx(dev, IRQEN_TSE);
+ control_tx_enable(dev, p->enable);
+ }
+
+ mutex_unlock(&state->tx_params_lock);
+ return 0;
+}
+
+
+/*
+ * V4L2 Subdevice Core Ops
+ */
+static int cx23888_ir_log_status(struct v4l2_subdev *sd)
+{
+ struct cx23888_ir_state *state = to_state(sd);
+ struct cx23885_dev *dev = state->dev;
+ char *s;
+ int i, j;
+
+ u32 cntrl = cx23888_ir_read4(dev, CX23888_IR_CNTRL_REG);
+ u32 txclk = cx23888_ir_read4(dev, CX23888_IR_TXCLK_REG) & TXCLK_TCD;
+ u32 rxclk = cx23888_ir_read4(dev, CX23888_IR_RXCLK_REG) & RXCLK_RCD;
+ u32 cduty = cx23888_ir_read4(dev, CX23888_IR_CDUTY_REG) & CDUTY_CDC;
+ u32 stats = cx23888_ir_read4(dev, CX23888_IR_STATS_REG);
+ u32 irqen = cx23888_ir_read4(dev, CX23888_IR_IRQEN_REG);
+ u32 filtr = cx23888_ir_read4(dev, CX23888_IR_FILTR_REG) & FILTR_LPF;
+
+ v4l2_info(sd, "IR Receiver:\n");
+ v4l2_info(sd, "\tEnabled: %s\n",
+ cntrl & CNTRL_RXE ? "yes" : "no");
+ v4l2_info(sd, "\tDemodulation from a carrier: %s\n",
+ cntrl & CNTRL_DMD ? "enabled" : "disabled");
+ v4l2_info(sd, "\tFIFO: %s\n",
+ cntrl & CNTRL_RFE ? "enabled" : "disabled");
+ switch (cntrl & CNTRL_EDG) {
+ case CNTRL_EDG_NONE:
+ s = "disabled";
+ break;
+ case CNTRL_EDG_FALL:
+ s = "falling edge";
+ break;
+ case CNTRL_EDG_RISE:
+ s = "rising edge";
+ break;
+ case CNTRL_EDG_BOTH:
+ s = "rising & falling edges";
+ break;
+ default:
+ s = "??? edge";
+ break;
+ }
+ v4l2_info(sd, "\tPulse timers' start/stop trigger: %s\n", s);
+ v4l2_info(sd, "\tFIFO data on pulse timer overflow: %s\n",
+ cntrl & CNTRL_R ? "not loaded" : "overflow marker");
+ v4l2_info(sd, "\tFIFO interrupt watermark: %s\n",
+ cntrl & CNTRL_RIC ? "not empty" : "half full or greater");
+ v4l2_info(sd, "\tLoopback mode: %s\n",
+ cntrl & CNTRL_LBM ? "loopback active" : "normal receive");
+ if (cntrl & CNTRL_DMD) {
+ v4l2_info(sd, "\tExpected carrier (16 clocks): %u Hz\n",
+ clock_divider_to_carrier_freq(rxclk));
+ switch (cntrl & CNTRL_WIN) {
+ case CNTRL_WIN_3_3:
+ i = 3;
+ j = 3;
+ break;
+ case CNTRL_WIN_4_3:
+ i = 4;
+ j = 3;
+ break;
+ case CNTRL_WIN_3_4:
+ i = 3;
+ j = 4;
+ break;
+ case CNTRL_WIN_4_4:
+ i = 4;
+ j = 4;
+ break;
+ default:
+ i = 0;
+ j = 0;
+ break;
+ }
+ v4l2_info(sd, "\tNext carrier edge window: 16 clocks "
+ "-%1d/+%1d, %u to %u Hz\n", i, j,
+ clock_divider_to_freq(rxclk, 16 + j),
+ clock_divider_to_freq(rxclk, 16 - i));
+ }
+ v4l2_info(sd, "\tMax measurable pulse width: %u us, %llu ns\n",
+ pulse_width_count_to_us(FIFO_RXTX, rxclk),
+ pulse_width_count_to_ns(FIFO_RXTX, rxclk));
+ v4l2_info(sd, "\tLow pass filter: %s\n",
+ filtr ? "enabled" : "disabled");
+ if (filtr)
+ v4l2_info(sd, "\tMin acceptable pulse width (LPF): %u us, "
+ "%u ns\n",
+ lpf_count_to_us(filtr),
+ lpf_count_to_ns(filtr));
+ v4l2_info(sd, "\tPulse width timer timed-out: %s\n",
+ stats & STATS_RTO ? "yes" : "no");
+ v4l2_info(sd, "\tPulse width timer time-out intr: %s\n",
+ irqen & IRQEN_RTE ? "enabled" : "disabled");
+ v4l2_info(sd, "\tFIFO overrun: %s\n",
+ stats & STATS_ROR ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO overrun interrupt: %s\n",
+ irqen & IRQEN_ROE ? "enabled" : "disabled");
+ v4l2_info(sd, "\tBusy: %s\n",
+ stats & STATS_RBY ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO service requested: %s\n",
+ stats & STATS_RSR ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO service request interrupt: %s\n",
+ irqen & IRQEN_RSE ? "enabled" : "disabled");
+
+ v4l2_info(sd, "IR Transmitter:\n");
+ v4l2_info(sd, "\tEnabled: %s\n",
+ cntrl & CNTRL_TXE ? "yes" : "no");
+ v4l2_info(sd, "\tModulation onto a carrier: %s\n",
+ cntrl & CNTRL_MOD ? "enabled" : "disabled");
+ v4l2_info(sd, "\tFIFO: %s\n",
+ cntrl & CNTRL_TFE ? "enabled" : "disabled");
+ v4l2_info(sd, "\tFIFO interrupt watermark: %s\n",
+ cntrl & CNTRL_TIC ? "not empty" : "half full or less");
+ v4l2_info(sd, "\tOutput pin level inversion %s\n",
+ cntrl & CNTRL_IVO ? "yes" : "no");
+ v4l2_info(sd, "\tCarrier polarity: %s\n",
+ cntrl & CNTRL_CPL ? "space:burst mark:noburst"
+ : "space:noburst mark:burst");
+ if (cntrl & CNTRL_MOD) {
+ v4l2_info(sd, "\tCarrier (16 clocks): %u Hz\n",
+ clock_divider_to_carrier_freq(txclk));
+ v4l2_info(sd, "\tCarrier duty cycle: %2u/16\n",
+ cduty + 1);
+ }
+ v4l2_info(sd, "\tMax pulse width: %u us, %llu ns\n",
+ pulse_width_count_to_us(FIFO_RXTX, txclk),
+ pulse_width_count_to_ns(FIFO_RXTX, txclk));
+ v4l2_info(sd, "\tBusy: %s\n",
+ stats & STATS_TBY ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO service requested: %s\n",
+ stats & STATS_TSR ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO service request interrupt: %s\n",
+ irqen & IRQEN_TSE ? "enabled" : "disabled");
+
+ return 0;
+}
+
+static inline int cx23888_ir_dbg_match(const struct v4l2_dbg_match *match)
+{
+ return match->type == V4L2_CHIP_MATCH_HOST && match->addr == 2;
+}
+
+static int cx23888_ir_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct cx23888_ir_state *state = to_state(sd);
+
+ if (cx23888_ir_dbg_match(&chip->match)) {
+ chip->ident = state->id;
+ chip->revision = state->rev;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int cx23888_ir_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct cx23888_ir_state *state = to_state(sd);
+ u32 addr = CX23888_IR_REG_BASE + (u32) reg->reg;
+
+ if (!cx23888_ir_dbg_match(&reg->match))
+ return -EINVAL;
+ if ((addr & 0x3) != 0)
+ return -EINVAL;
+ if (addr < CX23888_IR_CNTRL_REG || addr > CX23888_IR_LEARN_REG)
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->size = 4;
+ reg->val = cx23888_ir_read4(state->dev, addr);
+ return 0;
+}
+
+static int cx23888_ir_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct cx23888_ir_state *state = to_state(sd);
+ u32 addr = CX23888_IR_REG_BASE + (u32) reg->reg;
+
+ if (!cx23888_ir_dbg_match(&reg->match))
+ return -EINVAL;
+ if ((addr & 0x3) != 0)
+ return -EINVAL;
+ if (addr < CX23888_IR_CNTRL_REG || addr > CX23888_IR_LEARN_REG)
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ cx23888_ir_write4(state->dev, addr, reg->val);
+ return 0;
+}
+#endif
+
+static const struct v4l2_subdev_core_ops cx23888_ir_core_ops = {
+ .g_chip_ident = cx23888_ir_g_chip_ident,
+ .log_status = cx23888_ir_log_status,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = cx23888_ir_g_register,
+ .s_register = cx23888_ir_s_register,
+#endif
+ .interrupt_service_routine = cx23888_ir_irq_handler,
+};
+
+static const struct v4l2_subdev_ir_ops cx23888_ir_ir_ops = {
+ .rx_read = cx23888_ir_rx_read,
+ .rx_g_parameters = cx23888_ir_rx_g_parameters,
+ .rx_s_parameters = cx23888_ir_rx_s_parameters,
+
+ .tx_write = cx23888_ir_tx_write,
+ .tx_g_parameters = cx23888_ir_tx_g_parameters,
+ .tx_s_parameters = cx23888_ir_tx_s_parameters,
+};
+
+static const struct v4l2_subdev_ops cx23888_ir_controller_ops = {
+ .core = &cx23888_ir_core_ops,
+ .ir = &cx23888_ir_ir_ops,
+};
+
+static const struct v4l2_subdev_ir_parameters default_rx_params = {
+ .bytes_per_data_element = sizeof(union cx23888_ir_fifo_rec),
+ .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
+
+ .enable = false,
+ .interrupt_enable = false,
+ .shutdown = true,
+
+ .modulation = true,
+ .carrier_freq = 36000, /* 36 kHz - RC-5, RC-6, and RC-6A carrier */
+
+ /* RC-5: 666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */
+ /* RC-6A: 333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */
+ .noise_filter_min_width = 333333, /* ns */
+ .carrier_range_lower = 35000,
+ .carrier_range_upper = 37000,
+ .invert_level = false,
+};
+
+static const struct v4l2_subdev_ir_parameters default_tx_params = {
+ .bytes_per_data_element = sizeof(union cx23888_ir_fifo_rec),
+ .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
+
+ .enable = false,
+ .interrupt_enable = false,
+ .shutdown = true,
+
+ .modulation = true,
+ .carrier_freq = 36000, /* 36 kHz - RC-5 carrier */
+ .duty_cycle = 25, /* 25 % - RC-5 carrier */
+ .invert_level = false,
+ .invert_carrier_sense = false,
+};
+
+int cx23888_ir_probe(struct cx23885_dev *dev)
+{
+ struct cx23888_ir_state *state;
+ struct v4l2_subdev *sd;
+ struct v4l2_subdev_ir_parameters default_params;
+ int ret;
+
+ state = kzalloc(sizeof(struct cx23888_ir_state), GFP_KERNEL);
+ if (state == NULL)
+ return -ENOMEM;
+
+ spin_lock_init(&state->rx_kfifo_lock);
+ if (kfifo_alloc(&state->rx_kfifo, CX23888_IR_RX_KFIFO_SIZE, GFP_KERNEL))
+ return -ENOMEM;
+
+ state->dev = dev;
+ state->id = V4L2_IDENT_CX23888_IR;
+ state->rev = 0;
+ sd = &state->sd;
+
+ v4l2_subdev_init(sd, &cx23888_ir_controller_ops);
+ v4l2_set_subdevdata(sd, state);
+ /* FIXME - fix the formatting of dev->v4l2_dev.name and use it */
+ snprintf(sd->name, sizeof(sd->name), "%s/888-ir", dev->name);
+ sd->grp_id = CX23885_HW_888_IR;
+
+ ret = v4l2_device_register_subdev(&dev->v4l2_dev, sd);
+ if (ret == 0) {
+ /*
+ * Ensure no interrupts arrive from '888 specific conditions,
+ * since we ignore them in this driver to have commonality with
+ * similar IR controller cores.
+ */
+ cx23888_ir_write4(dev, CX23888_IR_IRQEN_REG, 0);
+
+ mutex_init(&state->rx_params_lock);
+ memcpy(&default_params, &default_rx_params,
+ sizeof(struct v4l2_subdev_ir_parameters));
+ v4l2_subdev_call(sd, ir, rx_s_parameters, &default_params);
+
+ mutex_init(&state->tx_params_lock);
+ memcpy(&default_params, &default_tx_params,
+ sizeof(struct v4l2_subdev_ir_parameters));
+ v4l2_subdev_call(sd, ir, tx_s_parameters, &default_params);
+ } else {
+ kfifo_free(&state->rx_kfifo);
+ }
+ return ret;
+}
+
+int cx23888_ir_remove(struct cx23885_dev *dev)
+{
+ struct v4l2_subdev *sd;
+ struct cx23888_ir_state *state;
+
+ sd = cx23885_find_hw(dev, CX23885_HW_888_IR);
+ if (sd == NULL)
+ return -ENODEV;
+
+ cx23888_ir_rx_shutdown(sd);
+ cx23888_ir_tx_shutdown(sd);
+
+ state = to_state(sd);
+ v4l2_device_unregister_subdev(sd);
+ kfifo_free(&state->rx_kfifo);
+ kfree(state);
+ /* Nothing more to free() as state held the actual v4l2_subdev object */
+ return 0;
+}
diff --git a/drivers/media/video/cx23885/cx23888-ir.h b/drivers/media/video/cx23885/cx23888-ir.h
new file mode 100644
index 00000000..d2de41ca
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23888-ir.h
@@ -0,0 +1,28 @@
+/*
+ * Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ * CX23888 Integrated Consumer Infrared Controller
+ *
+ * Copyright (C) 2009 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef _CX23888_IR_H_
+#define _CX23888_IR_H_
+int cx23888_ir_probe(struct cx23885_dev *dev);
+int cx23888_ir_remove(struct cx23885_dev *dev);
+#endif
diff --git a/drivers/media/video/cx23885/netup-eeprom.c b/drivers/media/video/cx23885/netup-eeprom.c
new file mode 100644
index 00000000..98a48f50
--- /dev/null
+++ b/drivers/media/video/cx23885/netup-eeprom.c
@@ -0,0 +1,107 @@
+
+/*
+ * netup-eeprom.c
+ *
+ * 24LC02 EEPROM driver in conjunction with NetUP Dual DVB-S2 CI card
+ *
+ * Copyright (C) 2009 NetUP Inc.
+ * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#
+#include "cx23885.h"
+#include "netup-eeprom.h"
+
+#define EEPROM_I2C_ADDR 0x50
+
+int netup_eeprom_read(struct i2c_adapter *i2c_adap, u8 addr)
+{
+ int ret;
+ unsigned char buf[2];
+
+ /* Read from EEPROM */
+ struct i2c_msg msg[] = {
+ {
+ .addr = EEPROM_I2C_ADDR,
+ .flags = 0,
+ .buf = &buf[0],
+ .len = 1
+ }, {
+ .addr = EEPROM_I2C_ADDR,
+ .flags = I2C_M_RD,
+ .buf = &buf[1],
+ .len = 1
+ }
+
+ };
+
+ buf[0] = addr;
+ buf[1] = 0x0;
+
+ ret = i2c_transfer(i2c_adap, msg, 2);
+
+ if (ret != 2) {
+ printk(KERN_ERR "eeprom i2c read error, status=%d\n", ret);
+ return -1;
+ }
+
+ return buf[1];
+};
+
+int netup_eeprom_write(struct i2c_adapter *i2c_adap, u8 addr, u8 data)
+{
+ int ret;
+ unsigned char bufw[2];
+
+ /* Write into EEPROM */
+ struct i2c_msg msg[] = {
+ {
+ .addr = EEPROM_I2C_ADDR,
+ .flags = 0,
+ .buf = &bufw[0],
+ .len = 2
+ }
+ };
+
+ bufw[0] = addr;
+ bufw[1] = data;
+
+ ret = i2c_transfer(i2c_adap, msg, 1);
+
+ if (ret != 1) {
+ printk(KERN_ERR "eeprom i2c write error, status=%d\n", ret);
+ return -1;
+ }
+
+ mdelay(10); /* prophylactic delay, datasheet write cycle time = 5 ms */
+ return 0;
+};
+
+void netup_get_card_info(struct i2c_adapter *i2c_adap,
+ struct netup_card_info *cinfo)
+{
+ int i, j;
+
+ cinfo->rev = netup_eeprom_read(i2c_adap, 63);
+
+ for (i = 64, j = 0; i < 70; i++, j++)
+ cinfo->port[0].mac[j] = netup_eeprom_read(i2c_adap, i);
+
+ for (i = 70, j = 0; i < 76; i++, j++)
+ cinfo->port[1].mac[j] = netup_eeprom_read(i2c_adap, i);
+};
diff --git a/drivers/media/video/cx23885/netup-eeprom.h b/drivers/media/video/cx23885/netup-eeprom.h
new file mode 100644
index 00000000..13926e18
--- /dev/null
+++ b/drivers/media/video/cx23885/netup-eeprom.h
@@ -0,0 +1,42 @@
+/*
+ * netup-eeprom.h
+ *
+ * 24LC02 EEPROM driver in conjunction with NetUP Dual DVB-S2 CI card
+ *
+ * Copyright (C) 2009 NetUP Inc.
+ * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef NETUP_EEPROM_H
+#define NETUP_EEPROM_H
+
+struct netup_port_info {
+ u8 mac[6];/* card MAC address */
+};
+
+struct netup_card_info {
+ struct netup_port_info port[2];/* ports - 1,2 */
+ u8 rev;/* card revision */
+};
+
+extern int netup_eeprom_read(struct i2c_adapter *i2c_adap, u8 addr);
+extern int netup_eeprom_write(struct i2c_adapter *i2c_adap, u8 addr, u8 data);
+extern void netup_get_card_info(struct i2c_adapter *i2c_adap,
+ struct netup_card_info *cinfo);
+
+#endif
diff --git a/drivers/media/video/cx23885/netup-init.c b/drivers/media/video/cx23885/netup-init.c
new file mode 100644
index 00000000..f4893e69
--- /dev/null
+++ b/drivers/media/video/cx23885/netup-init.c
@@ -0,0 +1,125 @@
+/*
+ * netup-init.c
+ *
+ * NetUP Dual DVB-S2 CI driver
+ *
+ * Copyright (C) 2009 NetUP Inc.
+ * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
+ * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "cx23885.h"
+
+static void i2c_av_write(struct i2c_adapter *i2c, u16 reg, u8 val)
+{
+ int ret;
+ u8 buf[3];
+ struct i2c_msg msg = {
+ .addr = 0x88 >> 1,
+ .flags = 0,
+ .buf = buf,
+ .len = 3
+ };
+
+ buf[0] = reg >> 8;
+ buf[1] = reg & 0xff;
+ buf[2] = val;
+
+ ret = i2c_transfer(i2c, &msg, 1);
+
+ if (ret != 1)
+ printk(KERN_ERR "%s: i2c write error!\n", __func__);
+}
+
+static void i2c_av_write4(struct i2c_adapter *i2c, u16 reg, u32 val)
+{
+ int ret;
+ u8 buf[6];
+ struct i2c_msg msg = {
+ .addr = 0x88 >> 1,
+ .flags = 0,
+ .buf = buf,
+ .len = 6
+ };
+
+ buf[0] = reg >> 8;
+ buf[1] = reg & 0xff;
+ buf[2] = val & 0xff;
+ buf[3] = (val >> 8) & 0xff;
+ buf[4] = (val >> 16) & 0xff;
+ buf[5] = val >> 24;
+
+ ret = i2c_transfer(i2c, &msg, 1);
+
+ if (ret != 1)
+ printk(KERN_ERR "%s: i2c write error!\n", __func__);
+}
+
+static u8 i2c_av_read(struct i2c_adapter *i2c, u16 reg)
+{
+ int ret;
+ u8 buf[2];
+ struct i2c_msg msg = {
+ .addr = 0x88 >> 1,
+ .flags = 0,
+ .buf = buf,
+ .len = 2
+ };
+
+ buf[0] = reg >> 8;
+ buf[1] = reg & 0xff;
+
+ ret = i2c_transfer(i2c, &msg, 1);
+
+ if (ret != 1)
+ printk(KERN_ERR "%s: i2c write error!\n", __func__);
+
+ msg.flags = I2C_M_RD;
+ msg.len = 1;
+
+ ret = i2c_transfer(i2c, &msg, 1);
+
+ if (ret != 1)
+ printk(KERN_ERR "%s: i2c read error!\n", __func__);
+
+ return buf[0];
+}
+
+static void i2c_av_and_or(struct i2c_adapter *i2c, u16 reg, unsigned and_mask,
+ u8 or_value)
+{
+ i2c_av_write(i2c, reg, (i2c_av_read(i2c, reg) & and_mask) | or_value);
+}
+/* set 27MHz on AUX_CLK */
+void netup_initialize(struct cx23885_dev *dev)
+{
+ struct cx23885_i2c *i2c_bus = &dev->i2c_bus[2];
+ struct i2c_adapter *i2c = &i2c_bus->i2c_adap;
+
+ /* Stop microcontroller */
+ i2c_av_and_or(i2c, 0x803, ~0x10, 0x00);
+
+ /* Aux PLL frac for 27 MHz */
+ i2c_av_write4(i2c, 0x114, 0xea0eb3);
+
+ /* Aux PLL int for 27 MHz */
+ i2c_av_write4(i2c, 0x110, 0x090319);
+
+ /* start microcontroller */
+ i2c_av_and_or(i2c, 0x803, ~0x10, 0x10);
+}
diff --git a/drivers/media/video/cx23885/netup-init.h b/drivers/media/video/cx23885/netup-init.h
new file mode 100644
index 00000000..d26ae4b1
--- /dev/null
+++ b/drivers/media/video/cx23885/netup-init.h
@@ -0,0 +1,25 @@
+/*
+ * netup-init.h
+ *
+ * NetUP Dual DVB-S2 CI driver
+ *
+ * Copyright (C) 2009 NetUP Inc.
+ * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
+ * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+extern void netup_initialize(struct cx23885_dev *dev);
diff --git a/drivers/media/video/cx25821/Kconfig b/drivers/media/video/cx25821/Kconfig
new file mode 100644
index 00000000..5f6b5421
--- /dev/null
+++ b/drivers/media/video/cx25821/Kconfig
@@ -0,0 +1,34 @@
+config VIDEO_CX25821
+ tristate "Conexant cx25821 support"
+ depends on DVB_CORE && VIDEO_DEV && PCI && I2C
+ select I2C_ALGOBIT
+ select VIDEO_BTCX
+ select VIDEO_TVEEPROM
+ depends on RC_CORE
+ select VIDEOBUF_DVB
+ select VIDEOBUF_DMA_SG
+ select VIDEO_CX25840
+ select VIDEO_CX2341X
+ ---help---
+ This is a video4linux driver for Conexant 25821 based
+ TV cards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cx25821
+
+config VIDEO_CX25821_ALSA
+ tristate "Conexant 25821 DMA audio support"
+ depends on VIDEO_CX25821 && SND && EXPERIMENTAL
+ select SND_PCM
+ ---help---
+ This is a video4linux driver for direct (DMA) audio on
+ Conexant 25821 based capture cards using ALSA.
+
+ It only works with boards with function 01 enabled.
+ To check if your board supports, use lspci -n.
+ If supported, you should see 14f1:8801 or 14f1:8811
+ PCI device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cx25821-alsa.
+
diff --git a/drivers/media/video/cx25821/Makefile b/drivers/media/video/cx25821/Makefile
new file mode 100644
index 00000000..aedde18c
--- /dev/null
+++ b/drivers/media/video/cx25821/Makefile
@@ -0,0 +1,13 @@
+cx25821-y := cx25821-core.o cx25821-cards.o cx25821-i2c.o \
+ cx25821-gpio.o cx25821-medusa-video.o \
+ cx25821-video.o cx25821-video-upstream.o \
+ cx25821-video-upstream-ch2.o \
+ cx25821-audio-upstream.o
+
+obj-$(CONFIG_VIDEO_CX25821) += cx25821.o
+obj-$(CONFIG_VIDEO_CX25821_ALSA) += cx25821-alsa.o
+
+ccflags-y := -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/cx25821/cx25821-alsa.c b/drivers/media/video/cx25821/cx25821-alsa.c
new file mode 100644
index 00000000..03cfac47
--- /dev/null
+++ b/drivers/media/video/cx25821/cx25821-alsa.c
@@ -0,0 +1,786 @@
+/*
+ * Driver for the Conexant CX25821 PCIe bridge
+ *
+ * Copyright (C) 2009 Conexant Systems Inc.
+ * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
+ * Based on SAA713x ALSA driver and CX88 driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/vmalloc.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "cx25821.h"
+#include "cx25821-reg.h"
+
+#define AUDIO_SRAM_CHANNEL SRAM_CH08
+
+#define dprintk(level, fmt, arg...) \
+do { \
+ if (debug >= level) \
+ pr_info("%s/1: " fmt, chip->dev->name, ##arg); \
+} while (0)
+#define dprintk_core(level, fmt, arg...) \
+do { \
+ if (debug >= level) \
+ printk(KERN_DEBUG "%s/1: " fmt, chip->dev->name, ##arg); \
+} while (0)
+
+/****************************************************************************
+ Data type declarations - Can be moded to a header file later
+ ****************************************************************************/
+
+static struct snd_card *snd_cx25821_cards[SNDRV_CARDS];
+static int devno;
+
+struct cx25821_audio_buffer {
+ unsigned int bpl;
+ struct btcx_riscmem risc;
+ struct videobuf_dmabuf dma;
+};
+
+struct cx25821_audio_dev {
+ struct cx25821_dev *dev;
+ struct cx25821_dmaqueue q;
+
+ /* pci i/o */
+ struct pci_dev *pci;
+
+ /* audio controls */
+ int irq;
+
+ struct snd_card *card;
+
+ unsigned long iobase;
+ spinlock_t reg_lock;
+ atomic_t count;
+
+ unsigned int dma_size;
+ unsigned int period_size;
+ unsigned int num_periods;
+
+ struct videobuf_dmabuf *dma_risc;
+
+ struct cx25821_audio_buffer *buf;
+
+ struct snd_pcm_substream *substream;
+};
+
+
+/****************************************************************************
+ Module global static vars
+ ****************************************************************************/
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static bool enable[SNDRV_CARDS] = { 1, [1 ... (SNDRV_CARDS - 1)] = 1 };
+
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable cx25821 soundcard. default enabled.");
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for cx25821 capture interface(s).");
+
+/****************************************************************************
+ Module macros
+ ****************************************************************************/
+
+MODULE_DESCRIPTION("ALSA driver module for cx25821 based capture cards");
+MODULE_AUTHOR("Hiep Huynh");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Conexant,25821}"); /* "{{Conexant,23881}," */
+
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable debug messages");
+
+/****************************************************************************
+ Module specific funtions
+ ****************************************************************************/
+/* Constants taken from cx88-reg.h */
+#define AUD_INT_DN_RISCI1 (1 << 0)
+#define AUD_INT_UP_RISCI1 (1 << 1)
+#define AUD_INT_RDS_DN_RISCI1 (1 << 2)
+#define AUD_INT_DN_RISCI2 (1 << 4) /* yes, 3 is skipped */
+#define AUD_INT_UP_RISCI2 (1 << 5)
+#define AUD_INT_RDS_DN_RISCI2 (1 << 6)
+#define AUD_INT_DN_SYNC (1 << 12)
+#define AUD_INT_UP_SYNC (1 << 13)
+#define AUD_INT_RDS_DN_SYNC (1 << 14)
+#define AUD_INT_OPC_ERR (1 << 16)
+#define AUD_INT_BER_IRQ (1 << 20)
+#define AUD_INT_MCHG_IRQ (1 << 21)
+#define GP_COUNT_CONTROL_RESET 0x3
+
+#define PCI_MSK_AUD_EXT (1 << 4)
+#define PCI_MSK_AUD_INT (1 << 3)
+/*
+ * BOARD Specific: Sets audio DMA
+ */
+
+static int _cx25821_start_audio_dma(struct cx25821_audio_dev *chip)
+{
+ struct cx25821_audio_buffer *buf = chip->buf;
+ struct cx25821_dev *dev = chip->dev;
+ struct sram_channel *audio_ch =
+ &cx25821_sram_channels[AUDIO_SRAM_CHANNEL];
+ u32 tmp = 0;
+
+ /* enable output on the GPIO 0 for the MCLK ADC (Audio) */
+ cx25821_set_gpiopin_direction(chip->dev, 0, 0);
+
+ /* Make sure RISC/FIFO are off before changing FIFO/RISC settings */
+ cx_clear(AUD_INT_DMA_CTL,
+ FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN);
+
+ /* setup fifo + format - out channel */
+ cx25821_sram_channel_setup_audio(chip->dev, audio_ch, buf->bpl,
+ buf->risc.dma);
+
+ /* sets bpl size */
+ cx_write(AUD_A_LNGTH, buf->bpl);
+
+ /* reset counter */
+ /* GP_COUNT_CONTROL_RESET = 0x3 */
+ cx_write(AUD_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET);
+ atomic_set(&chip->count, 0);
+
+ /* Set the input mode to 16-bit */
+ tmp = cx_read(AUD_A_CFG);
+ cx_write(AUD_A_CFG, tmp | FLD_AUD_DST_PK_MODE | FLD_AUD_DST_ENABLE |
+ FLD_AUD_CLK_ENABLE);
+
+ /*
+ pr_info("DEBUG: Start audio DMA, %d B/line, cmds_start(0x%x)= %d lines/FIFO, %d periods, %d byte buffer\n",
+ buf->bpl, audio_ch->cmds_start,
+ cx_read(audio_ch->cmds_start + 12)>>1,
+ chip->num_periods, buf->bpl * chip->num_periods);
+ */
+
+ /* Enables corresponding bits at AUD_INT_STAT */
+ cx_write(AUD_A_INT_MSK, FLD_AUD_DST_RISCI1 | FLD_AUD_DST_OF |
+ FLD_AUD_DST_SYNC | FLD_AUD_DST_OPC_ERR);
+
+ /* Clean any pending interrupt bits already set */
+ cx_write(AUD_A_INT_STAT, ~0);
+
+ /* enable audio irqs */
+ cx_set(PCI_INT_MSK, chip->dev->pci_irqmask | PCI_MSK_AUD_INT);
+
+ /* Turn on audio downstream fifo and risc enable 0x101 */
+ tmp = cx_read(AUD_INT_DMA_CTL);
+ cx_set(AUD_INT_DMA_CTL, tmp |
+ (FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN));
+
+ mdelay(100);
+ return 0;
+}
+
+/*
+ * BOARD Specific: Resets audio DMA
+ */
+static int _cx25821_stop_audio_dma(struct cx25821_audio_dev *chip)
+{
+ struct cx25821_dev *dev = chip->dev;
+
+ /* stop dma */
+ cx_clear(AUD_INT_DMA_CTL,
+ FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN);
+
+ /* disable irqs */
+ cx_clear(PCI_INT_MSK, PCI_MSK_AUD_INT);
+ cx_clear(AUD_A_INT_MSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC |
+ AUD_INT_DN_RISCI2 | AUD_INT_DN_RISCI1);
+
+ return 0;
+}
+
+#define MAX_IRQ_LOOP 50
+
+/*
+ * BOARD Specific: IRQ dma bits
+ */
+static char *cx25821_aud_irqs[32] = {
+ "dn_risci1", "up_risci1", "rds_dn_risc1", /* 0-2 */
+ NULL, /* reserved */
+ "dn_risci2", "up_risci2", "rds_dn_risc2", /* 4-6 */
+ NULL, /* reserved */
+ "dnf_of", "upf_uf", "rds_dnf_uf", /* 8-10 */
+ NULL, /* reserved */
+ "dn_sync", "up_sync", "rds_dn_sync", /* 12-14 */
+ NULL, /* reserved */
+ "opc_err", "par_err", "rip_err", /* 16-18 */
+ "pci_abort", "ber_irq", "mchg_irq" /* 19-21 */
+};
+
+/*
+ * BOARD Specific: Threats IRQ audio specific calls
+ */
+static void cx25821_aud_irq(struct cx25821_audio_dev *chip, u32 status,
+ u32 mask)
+{
+ struct cx25821_dev *dev = chip->dev;
+
+ if (0 == (status & mask))
+ return;
+
+ cx_write(AUD_A_INT_STAT, status);
+ if (debug > 1 || (status & mask & ~0xff))
+ cx25821_print_irqbits(dev->name, "irq aud", cx25821_aud_irqs,
+ ARRAY_SIZE(cx25821_aud_irqs), status, mask);
+
+ /* risc op code error */
+ if (status & AUD_INT_OPC_ERR) {
+ pr_warn("WARNING %s/1: Audio risc op code error\n", dev->name);
+
+ cx_clear(AUD_INT_DMA_CTL,
+ FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN);
+ cx25821_sram_channel_dump_audio(dev,
+ &cx25821_sram_channels[AUDIO_SRAM_CHANNEL]);
+ }
+ if (status & AUD_INT_DN_SYNC) {
+ pr_warn("WARNING %s: Downstream sync error!\n", dev->name);
+ cx_write(AUD_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET);
+ return;
+ }
+
+ /* risc1 downstream */
+ if (status & AUD_INT_DN_RISCI1) {
+ atomic_set(&chip->count, cx_read(AUD_A_GPCNT));
+ snd_pcm_period_elapsed(chip->substream);
+ }
+}
+
+/*
+ * BOARD Specific: Handles IRQ calls
+ */
+static irqreturn_t cx25821_irq(int irq, void *dev_id)
+{
+ struct cx25821_audio_dev *chip = dev_id;
+ struct cx25821_dev *dev = chip->dev;
+ u32 status, pci_status;
+ u32 audint_status, audint_mask;
+ int loop, handled = 0;
+ int audint_count = 0;
+
+ audint_status = cx_read(AUD_A_INT_STAT);
+ audint_mask = cx_read(AUD_A_INT_MSK);
+ audint_count = cx_read(AUD_A_GPCNT);
+ status = cx_read(PCI_INT_STAT);
+
+ for (loop = 0; loop < 1; loop++) {
+ status = cx_read(PCI_INT_STAT);
+ if (0 == status) {
+ status = cx_read(PCI_INT_STAT);
+ audint_status = cx_read(AUD_A_INT_STAT);
+ audint_mask = cx_read(AUD_A_INT_MSK);
+
+ if (status) {
+ handled = 1;
+ cx_write(PCI_INT_STAT, status);
+
+ cx25821_aud_irq(chip, audint_status,
+ audint_mask);
+ break;
+ } else {
+ goto out;
+ }
+ }
+
+ handled = 1;
+ cx_write(PCI_INT_STAT, status);
+
+ cx25821_aud_irq(chip, audint_status, audint_mask);
+ }
+
+ pci_status = cx_read(PCI_INT_STAT);
+
+ if (handled)
+ cx_write(PCI_INT_STAT, pci_status);
+
+out:
+ return IRQ_RETVAL(handled);
+}
+
+static int dsp_buffer_free(struct cx25821_audio_dev *chip)
+{
+ BUG_ON(!chip->dma_size);
+
+ dprintk(2, "Freeing buffer\n");
+ videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc);
+ videobuf_dma_free(chip->dma_risc);
+ btcx_riscmem_free(chip->pci, &chip->buf->risc);
+ kfree(chip->buf);
+
+ chip->dma_risc = NULL;
+ chip->dma_size = 0;
+
+ return 0;
+}
+
+/****************************************************************************
+ ALSA PCM Interface
+ ****************************************************************************/
+
+/*
+ * Digital hardware definition
+ */
+#define DEFAULT_FIFO_SIZE 384
+static struct snd_pcm_hardware snd_cx25821_digital_hw = {
+ .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* Analog audio output will be full of clicks and pops if there
+ are not exactly four lines in the SRAM FIFO buffer. */
+ .period_bytes_min = DEFAULT_FIFO_SIZE / 3,
+ .period_bytes_max = DEFAULT_FIFO_SIZE / 3,
+ .periods_min = 1,
+ .periods_max = AUDIO_LINE_SIZE,
+ /* 128 * 128 = 16384 = 1024 * 16 */
+ .buffer_bytes_max = (AUDIO_LINE_SIZE * AUDIO_LINE_SIZE),
+};
+
+/*
+ * audio pcm capture open callback
+ */
+static int snd_cx25821_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+ unsigned int bpl = 0;
+
+ if (!chip) {
+ pr_err("DEBUG: cx25821 can't find device struct. Can't proceed with open\n");
+ return -ENODEV;
+ }
+
+ err = snd_pcm_hw_constraint_pow2(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
+ goto _error;
+
+ chip->substream = substream;
+
+ runtime->hw = snd_cx25821_digital_hw;
+
+ if (cx25821_sram_channels[AUDIO_SRAM_CHANNEL].fifo_size !=
+ DEFAULT_FIFO_SIZE) {
+ /* since there are 3 audio Clusters */
+ bpl = cx25821_sram_channels[AUDIO_SRAM_CHANNEL].fifo_size / 3;
+ bpl &= ~7; /* must be multiple of 8 */
+
+ if (bpl > AUDIO_LINE_SIZE)
+ bpl = AUDIO_LINE_SIZE;
+
+ runtime->hw.period_bytes_min = bpl;
+ runtime->hw.period_bytes_max = bpl;
+ }
+
+ return 0;
+_error:
+ dprintk(1, "Error opening PCM!\n");
+ return err;
+}
+
+/*
+ * audio close callback
+ */
+static int snd_cx25821_close(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+/*
+ * hw_params callback
+ */
+static int snd_cx25821_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream);
+ struct videobuf_dmabuf *dma;
+
+ struct cx25821_audio_buffer *buf;
+ int ret;
+
+ if (substream->runtime->dma_area) {
+ dsp_buffer_free(chip);
+ substream->runtime->dma_area = NULL;
+ }
+
+ chip->period_size = params_period_bytes(hw_params);
+ chip->num_periods = params_periods(hw_params);
+ chip->dma_size = chip->period_size * params_periods(hw_params);
+
+ BUG_ON(!chip->dma_size);
+ BUG_ON(chip->num_periods & (chip->num_periods - 1));
+
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (NULL == buf)
+ return -ENOMEM;
+
+ if (chip->period_size > AUDIO_LINE_SIZE)
+ chip->period_size = AUDIO_LINE_SIZE;
+
+ buf->bpl = chip->period_size;
+
+ dma = &buf->dma;
+ videobuf_dma_init(dma);
+ ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE,
+ (PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT));
+ if (ret < 0)
+ goto error;
+
+ ret = videobuf_dma_map(&chip->pci->dev, dma);
+ if (ret < 0)
+ goto error;
+
+ ret = cx25821_risc_databuffer_audio(chip->pci, &buf->risc, dma->sglist,
+ chip->period_size, chip->num_periods, 1);
+ if (ret < 0) {
+ pr_info("DEBUG: ERROR after cx25821_risc_databuffer_audio()\n");
+ goto error;
+ }
+
+ /* Loop back to start of program */
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
+ buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
+
+ chip->buf = buf;
+ chip->dma_risc = dma;
+
+ substream->runtime->dma_area = chip->dma_risc->vaddr;
+ substream->runtime->dma_bytes = chip->dma_size;
+ substream->runtime->dma_addr = 0;
+
+ return 0;
+
+error:
+ kfree(buf);
+ return ret;
+}
+
+/*
+ * hw free callback
+ */
+static int snd_cx25821_hw_free(struct snd_pcm_substream *substream)
+{
+ struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream);
+
+ if (substream->runtime->dma_area) {
+ dsp_buffer_free(chip);
+ substream->runtime->dma_area = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * prepare callback
+ */
+static int snd_cx25821_prepare(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+/*
+ * trigger callback
+ */
+static int snd_cx25821_card_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream);
+ int err = 0;
+
+ /* Local interrupts are already disabled by ALSA */
+ spin_lock(&chip->reg_lock);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ err = _cx25821_start_audio_dma(chip);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ err = _cx25821_stop_audio_dma(chip);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ spin_unlock(&chip->reg_lock);
+
+ return err;
+}
+
+/*
+ * pointer callback
+ */
+static snd_pcm_uframes_t snd_cx25821_pointer(struct snd_pcm_substream
+ *substream)
+{
+ struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ u16 count;
+
+ count = atomic_read(&chip->count);
+
+ return runtime->period_size * (count & (runtime->periods - 1));
+}
+
+/*
+ * page callback (needed for mmap)
+ */
+static struct page *snd_cx25821_page(struct snd_pcm_substream *substream,
+ unsigned long offset)
+{
+ void *pageptr = substream->runtime->dma_area + offset;
+
+ return vmalloc_to_page(pageptr);
+}
+
+/*
+ * operators
+ */
+static struct snd_pcm_ops snd_cx25821_pcm_ops = {
+ .open = snd_cx25821_pcm_open,
+ .close = snd_cx25821_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_cx25821_hw_params,
+ .hw_free = snd_cx25821_hw_free,
+ .prepare = snd_cx25821_prepare,
+ .trigger = snd_cx25821_card_trigger,
+ .pointer = snd_cx25821_pointer,
+ .page = snd_cx25821_page,
+};
+
+/*
+ * ALSA create a PCM device: Called when initializing the board.
+ * Sets up the name and hooks up the callbacks
+ */
+static int snd_cx25821_pcm(struct cx25821_audio_dev *chip, int device,
+ char *name)
+{
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm);
+ if (err < 0) {
+ pr_info("ERROR: FAILED snd_pcm_new() in %s\n", __func__);
+ return err;
+ }
+ pcm->private_data = chip;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, name);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx25821_pcm_ops);
+
+ return 0;
+}
+
+/****************************************************************************
+ Basic Flow for Sound Devices
+ ****************************************************************************/
+
+/*
+ * PCI ID Table - 14f1:8801 and 14f1:8811 means function 1: Audio
+ * Only boards with eeprom and byte 1 at eeprom=1 have it
+ */
+
+static DEFINE_PCI_DEVICE_TABLE(cx25821_audio_pci_tbl) = {
+ {0x14f1, 0x0920, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, cx25821_audio_pci_tbl);
+
+/*
+ * Not used in the function snd_cx25821_dev_free so removing
+ * from the file.
+ */
+/*
+static int snd_cx25821_free(struct cx25821_audio_dev *chip)
+{
+ if (chip->irq >= 0)
+ free_irq(chip->irq, chip);
+
+ cx25821_dev_unregister(chip->dev);
+ pci_disable_device(chip->pci);
+
+ return 0;
+}
+*/
+
+/*
+ * Component Destructor
+ */
+static void snd_cx25821_dev_free(struct snd_card *card)
+{
+ struct cx25821_audio_dev *chip = card->private_data;
+
+ /* snd_cx25821_free(chip); */
+ snd_card_free(chip->card);
+}
+
+/*
+ * Alsa Constructor - Component probe
+ */
+static int cx25821_audio_initdev(struct cx25821_dev *dev)
+{
+ struct snd_card *card;
+ struct cx25821_audio_dev *chip;
+ int err;
+
+ if (devno >= SNDRV_CARDS) {
+ pr_info("DEBUG ERROR: devno >= SNDRV_CARDS %s\n", __func__);
+ return -ENODEV;
+ }
+
+ if (!enable[devno]) {
+ ++devno;
+ pr_info("DEBUG ERROR: !enable[devno] %s\n", __func__);
+ return -ENOENT;
+ }
+
+ err = snd_card_create(index[devno], id[devno], THIS_MODULE,
+ sizeof(struct cx25821_audio_dev), &card);
+ if (err < 0) {
+ pr_info("DEBUG ERROR: cannot create snd_card_new in %s\n",
+ __func__);
+ return err;
+ }
+
+ strcpy(card->driver, "cx25821");
+
+ /* Card "creation" */
+ card->private_free = snd_cx25821_dev_free;
+ chip = card->private_data;
+ spin_lock_init(&chip->reg_lock);
+
+ chip->dev = dev;
+ chip->card = card;
+ chip->pci = dev->pci;
+ chip->iobase = pci_resource_start(dev->pci, 0);
+
+ chip->irq = dev->pci->irq;
+
+ err = request_irq(dev->pci->irq, cx25821_irq,
+ IRQF_SHARED, chip->dev->name, chip);
+
+ if (err < 0) {
+ pr_err("ERROR %s: can't get IRQ %d for ALSA\n", chip->dev->name,
+ dev->pci->irq);
+ goto error;
+ }
+
+ err = snd_cx25821_pcm(chip, 0, "cx25821 Digital");
+ if (err < 0) {
+ pr_info("DEBUG ERROR: cannot create snd_cx25821_pcm %s\n",
+ __func__);
+ goto error;
+ }
+
+ snd_card_set_dev(card, &chip->pci->dev);
+
+ strcpy(card->shortname, "cx25821");
+ sprintf(card->longname, "%s at 0x%lx irq %d", chip->dev->name,
+ chip->iobase, chip->irq);
+ strcpy(card->mixername, "CX25821");
+
+ pr_info("%s/%i: ALSA support for cx25821 boards\n", card->driver,
+ devno);
+
+ err = snd_card_register(card);
+ if (err < 0) {
+ pr_info("DEBUG ERROR: cannot register sound card %s\n",
+ __func__);
+ goto error;
+ }
+
+ snd_cx25821_cards[devno] = card;
+
+ devno++;
+ return 0;
+
+error:
+ snd_card_free(card);
+ return err;
+}
+
+/****************************************************************************
+ LINUX MODULE INIT
+ ****************************************************************************/
+static void cx25821_audio_fini(void)
+{
+ snd_card_free(snd_cx25821_cards[0]);
+}
+
+/*
+ * Module initializer
+ *
+ * Loops through present saa7134 cards, and assigns an ALSA device
+ * to each one
+ *
+ */
+static int cx25821_alsa_init(void)
+{
+ struct cx25821_dev *dev = NULL;
+ struct list_head *list;
+
+ mutex_lock(&cx25821_devlist_mutex);
+ list_for_each(list, &cx25821_devlist) {
+ dev = list_entry(list, struct cx25821_dev, devlist);
+ cx25821_audio_initdev(dev);
+ }
+ mutex_unlock(&cx25821_devlist_mutex);
+
+ if (dev == NULL)
+ pr_info("ERROR ALSA: no cx25821 cards found\n");
+
+ return 0;
+
+}
+
+late_initcall(cx25821_alsa_init);
+module_exit(cx25821_audio_fini);
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/cx25821/cx25821-audio-upstream.c b/drivers/media/video/cx25821/cx25821-audio-upstream.c
new file mode 100644
index 00000000..20c7ca33
--- /dev/null
+++ b/drivers/media/video/cx25821/cx25821-audio-upstream.c
@@ -0,0 +1,779 @@
+/*
+ * Driver for the Conexant CX25821 PCIe bridge
+ *
+ * Copyright (C) 2009 Conexant Systems Inc.
+ * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "cx25821-video.h"
+#include "cx25821-audio-upstream.h"
+
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/syscalls.h>
+#include <linux/file.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards");
+MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>");
+MODULE_LICENSE("GPL");
+
+static int _intr_msk = FLD_AUD_SRC_RISCI1 | FLD_AUD_SRC_OF |
+ FLD_AUD_SRC_SYNC | FLD_AUD_SRC_OPC_ERR;
+
+int cx25821_sram_channel_setup_upstream_audio(struct cx25821_dev *dev,
+ struct sram_channel *ch,
+ unsigned int bpl, u32 risc)
+{
+ unsigned int i, lines;
+ u32 cdt;
+
+ if (ch->cmds_start == 0) {
+ cx_write(ch->ptr1_reg, 0);
+ cx_write(ch->ptr2_reg, 0);
+ cx_write(ch->cnt2_reg, 0);
+ cx_write(ch->cnt1_reg, 0);
+ return 0;
+ }
+
+ bpl = (bpl + 7) & ~7; /* alignment */
+ cdt = ch->cdt;
+ lines = ch->fifo_size / bpl;
+
+ if (lines > 3)
+ lines = 3;
+
+ BUG_ON(lines < 2);
+
+ /* write CDT */
+ for (i = 0; i < lines; i++) {
+ cx_write(cdt + 16 * i, ch->fifo_start + bpl * i);
+ cx_write(cdt + 16 * i + 4, 0);
+ cx_write(cdt + 16 * i + 8, 0);
+ cx_write(cdt + 16 * i + 12, 0);
+ }
+
+ /* write CMDS */
+ cx_write(ch->cmds_start + 0, risc);
+
+ cx_write(ch->cmds_start + 4, 0);
+ cx_write(ch->cmds_start + 8, cdt);
+ cx_write(ch->cmds_start + 12, AUDIO_CDT_SIZE_QW);
+ cx_write(ch->cmds_start + 16, ch->ctrl_start);
+
+ /* IQ size */
+ cx_write(ch->cmds_start + 20, AUDIO_IQ_SIZE_DW);
+
+ for (i = 24; i < 80; i += 4)
+ cx_write(ch->cmds_start + i, 0);
+
+ /* fill registers */
+ cx_write(ch->ptr1_reg, ch->fifo_start);
+ cx_write(ch->ptr2_reg, cdt);
+ cx_write(ch->cnt2_reg, AUDIO_CDT_SIZE_QW);
+ cx_write(ch->cnt1_reg, AUDIO_CLUSTER_SIZE_QW - 1);
+
+ return 0;
+}
+
+static __le32 *cx25821_risc_field_upstream_audio(struct cx25821_dev *dev,
+ __le32 *rp,
+ dma_addr_t databuf_phys_addr,
+ unsigned int bpl,
+ int fifo_enable)
+{
+ unsigned int line;
+ struct sram_channel *sram_ch =
+ dev->channels[dev->_audio_upstream_channel].sram_channels;
+ int offset = 0;
+
+ /* scan lines */
+ for (line = 0; line < LINES_PER_AUDIO_BUFFER; line++) {
+ *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl);
+ *(rp++) = cpu_to_le32(databuf_phys_addr + offset);
+ *(rp++) = cpu_to_le32(0); /* bits 63-32 */
+
+ /* Check if we need to enable the FIFO
+ * after the first 3 lines.
+ * For the upstream audio channel,
+ * the risc engine will enable the FIFO */
+ if (fifo_enable && line == 2) {
+ *(rp++) = RISC_WRITECR;
+ *(rp++) = sram_ch->dma_ctl;
+ *(rp++) = sram_ch->fld_aud_fifo_en;
+ *(rp++) = 0x00000020;
+ }
+
+ offset += AUDIO_LINE_SIZE;
+ }
+
+ return rp;
+}
+
+int cx25821_risc_buffer_upstream_audio(struct cx25821_dev *dev,
+ struct pci_dev *pci,
+ unsigned int bpl, unsigned int lines)
+{
+ __le32 *rp;
+ int fifo_enable = 0;
+ int frame = 0, i = 0;
+ int frame_size = AUDIO_DATA_BUF_SZ;
+ int databuf_offset = 0;
+ int risc_flag = RISC_CNT_INC;
+ dma_addr_t risc_phys_jump_addr;
+
+ /* Virtual address of Risc buffer program */
+ rp = dev->_risc_virt_addr;
+
+ /* sync instruction */
+ *(rp++) = cpu_to_le32(RISC_RESYNC | AUDIO_SYNC_LINE);
+
+ for (frame = 0; frame < NUM_AUDIO_FRAMES; frame++) {
+ databuf_offset = frame_size * frame;
+
+ if (frame == 0) {
+ fifo_enable = 1;
+ risc_flag = RISC_CNT_RESET;
+ } else {
+ fifo_enable = 0;
+ risc_flag = RISC_CNT_INC;
+ }
+
+ /* Calculate physical jump address */
+ if ((frame + 1) == NUM_AUDIO_FRAMES) {
+ risc_phys_jump_addr =
+ dev->_risc_phys_start_addr +
+ RISC_SYNC_INSTRUCTION_SIZE;
+ } else {
+ risc_phys_jump_addr =
+ dev->_risc_phys_start_addr +
+ RISC_SYNC_INSTRUCTION_SIZE +
+ AUDIO_RISC_DMA_BUF_SIZE * (frame + 1);
+ }
+
+ rp = cx25821_risc_field_upstream_audio(dev, rp,
+ dev->_audiodata_buf_phys_addr + databuf_offset,
+ bpl, fifo_enable);
+
+ if (USE_RISC_NOOP_AUDIO) {
+ for (i = 0; i < NUM_NO_OPS; i++)
+ *(rp++) = cpu_to_le32(RISC_NOOP);
+ }
+
+ /* Loop to (Nth)FrameRISC or to Start of Risc program &
+ * generate IRQ */
+ *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | risc_flag);
+ *(rp++) = cpu_to_le32(risc_phys_jump_addr);
+ *(rp++) = cpu_to_le32(0);
+
+ /* Recalculate virtual address based on frame index */
+ rp = dev->_risc_virt_addr + RISC_SYNC_INSTRUCTION_SIZE / 4 +
+ (AUDIO_RISC_DMA_BUF_SIZE * (frame + 1) / 4);
+ }
+
+ return 0;
+}
+
+void cx25821_free_memory_audio(struct cx25821_dev *dev)
+{
+ if (dev->_risc_virt_addr) {
+ pci_free_consistent(dev->pci, dev->_audiorisc_size,
+ dev->_risc_virt_addr, dev->_risc_phys_addr);
+ dev->_risc_virt_addr = NULL;
+ }
+
+ if (dev->_audiodata_buf_virt_addr) {
+ pci_free_consistent(dev->pci, dev->_audiodata_buf_size,
+ dev->_audiodata_buf_virt_addr,
+ dev->_audiodata_buf_phys_addr);
+ dev->_audiodata_buf_virt_addr = NULL;
+ }
+}
+
+void cx25821_stop_upstream_audio(struct cx25821_dev *dev)
+{
+ struct sram_channel *sram_ch =
+ dev->channels[AUDIO_UPSTREAM_SRAM_CHANNEL_B].sram_channels;
+ u32 tmp = 0;
+
+ if (!dev->_audio_is_running) {
+ printk(KERN_DEBUG
+ pr_fmt("No audio file is currently running so return!\n"));
+ return;
+ }
+ /* Disable RISC interrupts */
+ cx_write(sram_ch->int_msk, 0);
+
+ /* Turn OFF risc and fifo enable in AUD_DMA_CNTRL */
+ tmp = cx_read(sram_ch->dma_ctl);
+ cx_write(sram_ch->dma_ctl,
+ tmp & ~(sram_ch->fld_aud_fifo_en | sram_ch->fld_aud_risc_en));
+
+ /* Clear data buffer memory */
+ if (dev->_audiodata_buf_virt_addr)
+ memset(dev->_audiodata_buf_virt_addr, 0,
+ dev->_audiodata_buf_size);
+
+ dev->_audio_is_running = 0;
+ dev->_is_first_audio_frame = 0;
+ dev->_audioframe_count = 0;
+ dev->_audiofile_status = END_OF_FILE;
+
+ kfree(dev->_irq_audio_queues);
+ dev->_irq_audio_queues = NULL;
+
+ kfree(dev->_audiofilename);
+}
+
+void cx25821_free_mem_upstream_audio(struct cx25821_dev *dev)
+{
+ if (dev->_audio_is_running)
+ cx25821_stop_upstream_audio(dev);
+
+ cx25821_free_memory_audio(dev);
+}
+
+int cx25821_get_audio_data(struct cx25821_dev *dev,
+ struct sram_channel *sram_ch)
+{
+ struct file *myfile;
+ int frame_index_temp = dev->_audioframe_index;
+ int i = 0;
+ int line_size = AUDIO_LINE_SIZE;
+ int frame_size = AUDIO_DATA_BUF_SZ;
+ int frame_offset = frame_size * frame_index_temp;
+ ssize_t vfs_read_retval = 0;
+ char mybuf[line_size];
+ loff_t file_offset = dev->_audioframe_count * frame_size;
+ loff_t pos;
+ mm_segment_t old_fs;
+
+ if (dev->_audiofile_status == END_OF_FILE)
+ return 0;
+
+ myfile = filp_open(dev->_audiofilename, O_RDONLY | O_LARGEFILE, 0);
+
+ if (IS_ERR(myfile)) {
+ const int open_errno = -PTR_ERR(myfile);
+ pr_err("%s(): ERROR opening file(%s) with errno = %d!\n",
+ __func__, dev->_audiofilename, open_errno);
+ return PTR_ERR(myfile);
+ } else {
+ if (!(myfile->f_op)) {
+ pr_err("%s(): File has no file operations registered!\n",
+ __func__);
+ filp_close(myfile, NULL);
+ return -EIO;
+ }
+
+ if (!myfile->f_op->read) {
+ pr_err("%s(): File has no READ operations registered!\n",
+ __func__);
+ filp_close(myfile, NULL);
+ return -EIO;
+ }
+
+ pos = myfile->f_pos;
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ for (i = 0; i < dev->_audio_lines_count; i++) {
+ pos = file_offset;
+
+ vfs_read_retval = vfs_read(myfile, mybuf, line_size,
+ &pos);
+
+ if (vfs_read_retval > 0 && vfs_read_retval == line_size
+ && dev->_audiodata_buf_virt_addr != NULL) {
+ memcpy((void *)(dev->_audiodata_buf_virt_addr +
+ frame_offset / 4), mybuf,
+ vfs_read_retval);
+ }
+
+ file_offset += vfs_read_retval;
+ frame_offset += vfs_read_retval;
+
+ if (vfs_read_retval < line_size) {
+ pr_info("Done: exit %s() since no more bytes to read from Audio file\n",
+ __func__);
+ break;
+ }
+ }
+
+ if (i > 0)
+ dev->_audioframe_count++;
+
+ dev->_audiofile_status = (vfs_read_retval == line_size) ?
+ IN_PROGRESS : END_OF_FILE;
+
+ set_fs(old_fs);
+ filp_close(myfile, NULL);
+ }
+
+ return 0;
+}
+
+static void cx25821_audioups_handler(struct work_struct *work)
+{
+ struct cx25821_dev *dev = container_of(work, struct cx25821_dev,
+ _audio_work_entry);
+
+ if (!dev) {
+ pr_err("ERROR %s(): since container_of(work_struct) FAILED!\n",
+ __func__);
+ return;
+ }
+
+ cx25821_get_audio_data(dev, dev->channels[dev->_audio_upstream_channel].
+ sram_channels);
+}
+
+int cx25821_openfile_audio(struct cx25821_dev *dev,
+ struct sram_channel *sram_ch)
+{
+ struct file *myfile;
+ int i = 0, j = 0;
+ int line_size = AUDIO_LINE_SIZE;
+ ssize_t vfs_read_retval = 0;
+ char mybuf[line_size];
+ loff_t pos;
+ loff_t offset = (unsigned long)0;
+ mm_segment_t old_fs;
+
+ myfile = filp_open(dev->_audiofilename, O_RDONLY | O_LARGEFILE, 0);
+
+ if (IS_ERR(myfile)) {
+ const int open_errno = -PTR_ERR(myfile);
+ pr_err("%s(): ERROR opening file(%s) with errno = %d!\n",
+ __func__, dev->_audiofilename, open_errno);
+ return PTR_ERR(myfile);
+ } else {
+ if (!(myfile->f_op)) {
+ pr_err("%s(): File has no file operations registered!\n",
+ __func__);
+ filp_close(myfile, NULL);
+ return -EIO;
+ }
+
+ if (!myfile->f_op->read) {
+ pr_err("%s(): File has no READ operations registered!\n",
+ __func__);
+ filp_close(myfile, NULL);
+ return -EIO;
+ }
+
+ pos = myfile->f_pos;
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ for (j = 0; j < NUM_AUDIO_FRAMES; j++) {
+ for (i = 0; i < dev->_audio_lines_count; i++) {
+ pos = offset;
+
+ vfs_read_retval = vfs_read(myfile, mybuf,
+ line_size, &pos);
+
+ if (vfs_read_retval > 0 &&
+ vfs_read_retval == line_size &&
+ dev->_audiodata_buf_virt_addr != NULL) {
+ memcpy((void *)(dev->
+ _audiodata_buf_virt_addr
+ + offset / 4), mybuf,
+ vfs_read_retval);
+ }
+
+ offset += vfs_read_retval;
+
+ if (vfs_read_retval < line_size) {
+ pr_info("Done: exit %s() since no more bytes to read from Audio file\n",
+ __func__);
+ break;
+ }
+ }
+
+ if (i > 0)
+ dev->_audioframe_count++;
+
+ if (vfs_read_retval < line_size)
+ break;
+ }
+
+ dev->_audiofile_status = (vfs_read_retval == line_size) ?
+ IN_PROGRESS : END_OF_FILE;
+
+ set_fs(old_fs);
+ myfile->f_pos = 0;
+ filp_close(myfile, NULL);
+ }
+
+ return 0;
+}
+
+static int cx25821_audio_upstream_buffer_prepare(struct cx25821_dev *dev,
+ struct sram_channel *sram_ch,
+ int bpl)
+{
+ int ret = 0;
+ dma_addr_t dma_addr;
+ dma_addr_t data_dma_addr;
+
+ cx25821_free_memory_audio(dev);
+
+ dev->_risc_virt_addr = pci_alloc_consistent(dev->pci,
+ dev->audio_upstream_riscbuf_size, &dma_addr);
+ dev->_risc_virt_start_addr = dev->_risc_virt_addr;
+ dev->_risc_phys_start_addr = dma_addr;
+ dev->_risc_phys_addr = dma_addr;
+ dev->_audiorisc_size = dev->audio_upstream_riscbuf_size;
+
+ if (!dev->_risc_virt_addr) {
+ printk(KERN_DEBUG
+ pr_fmt("ERROR: pci_alloc_consistent() FAILED to allocate memory for RISC program! Returning\n"));
+ return -ENOMEM;
+ }
+ /* Clear out memory at address */
+ memset(dev->_risc_virt_addr, 0, dev->_audiorisc_size);
+
+ /* For Audio Data buffer allocation */
+ dev->_audiodata_buf_virt_addr = pci_alloc_consistent(dev->pci,
+ dev->audio_upstream_databuf_size, &data_dma_addr);
+ dev->_audiodata_buf_phys_addr = data_dma_addr;
+ dev->_audiodata_buf_size = dev->audio_upstream_databuf_size;
+
+ if (!dev->_audiodata_buf_virt_addr) {
+ printk(KERN_DEBUG
+ pr_fmt("ERROR: pci_alloc_consistent() FAILED to allocate memory for data buffer! Returning\n"));
+ return -ENOMEM;
+ }
+ /* Clear out memory at address */
+ memset(dev->_audiodata_buf_virt_addr, 0, dev->_audiodata_buf_size);
+
+ ret = cx25821_openfile_audio(dev, sram_ch);
+ if (ret < 0)
+ return ret;
+
+ /* Creating RISC programs */
+ ret = cx25821_risc_buffer_upstream_audio(dev, dev->pci, bpl,
+ dev->_audio_lines_count);
+ if (ret < 0) {
+ printk(KERN_DEBUG
+ pr_fmt("ERROR creating audio upstream RISC programs!\n"));
+ goto error;
+ }
+
+ return 0;
+
+error:
+ return ret;
+}
+
+int cx25821_audio_upstream_irq(struct cx25821_dev *dev, int chan_num,
+ u32 status)
+{
+ int i = 0;
+ u32 int_msk_tmp;
+ struct sram_channel *channel = dev->channels[chan_num].sram_channels;
+ dma_addr_t risc_phys_jump_addr;
+ __le32 *rp;
+
+ if (status & FLD_AUD_SRC_RISCI1) {
+ /* Get interrupt_index of the program that interrupted */
+ u32 prog_cnt = cx_read(channel->gpcnt);
+
+ /* Since we've identified our IRQ, clear our bits from the
+ * interrupt mask and interrupt status registers */
+ cx_write(channel->int_msk, 0);
+ cx_write(channel->int_stat, cx_read(channel->int_stat));
+
+ spin_lock(&dev->slock);
+
+ while (prog_cnt != dev->_last_index_irq) {
+ /* Update _last_index_irq */
+ if (dev->_last_index_irq < (NUMBER_OF_PROGRAMS - 1))
+ dev->_last_index_irq++;
+ else
+ dev->_last_index_irq = 0;
+
+ dev->_audioframe_index = dev->_last_index_irq;
+
+ queue_work(dev->_irq_audio_queues,
+ &dev->_audio_work_entry);
+ }
+
+ if (dev->_is_first_audio_frame) {
+ dev->_is_first_audio_frame = 0;
+
+ if (dev->_risc_virt_start_addr != NULL) {
+ risc_phys_jump_addr =
+ dev->_risc_phys_start_addr +
+ RISC_SYNC_INSTRUCTION_SIZE +
+ AUDIO_RISC_DMA_BUF_SIZE;
+
+ rp = cx25821_risc_field_upstream_audio(dev,
+ dev->_risc_virt_start_addr + 1,
+ dev->_audiodata_buf_phys_addr,
+ AUDIO_LINE_SIZE, FIFO_DISABLE);
+
+ if (USE_RISC_NOOP_AUDIO) {
+ for (i = 0; i < NUM_NO_OPS; i++) {
+ *(rp++) =
+ cpu_to_le32(RISC_NOOP);
+ }
+ }
+ /* Jump to 2nd Audio Frame */
+ *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 |
+ RISC_CNT_RESET);
+ *(rp++) = cpu_to_le32(risc_phys_jump_addr);
+ *(rp++) = cpu_to_le32(0);
+ }
+ }
+
+ spin_unlock(&dev->slock);
+ } else {
+ if (status & FLD_AUD_SRC_OF)
+ pr_warn("%s(): Audio Received Overflow Error Interrupt!\n",
+ __func__);
+
+ if (status & FLD_AUD_SRC_SYNC)
+ pr_warn("%s(): Audio Received Sync Error Interrupt!\n",
+ __func__);
+
+ if (status & FLD_AUD_SRC_OPC_ERR)
+ pr_warn("%s(): Audio Received OpCode Error Interrupt!\n",
+ __func__);
+
+ /* Read and write back the interrupt status register to clear
+ * our bits */
+ cx_write(channel->int_stat, cx_read(channel->int_stat));
+ }
+
+ if (dev->_audiofile_status == END_OF_FILE) {
+ pr_warn("EOF Channel Audio Framecount = %d\n",
+ dev->_audioframe_count);
+ return -1;
+ }
+ /* ElSE, set the interrupt mask register, re-enable irq. */
+ int_msk_tmp = cx_read(channel->int_msk);
+ cx_write(channel->int_msk, int_msk_tmp |= _intr_msk);
+
+ return 0;
+}
+
+static irqreturn_t cx25821_upstream_irq_audio(int irq, void *dev_id)
+{
+ struct cx25821_dev *dev = dev_id;
+ u32 msk_stat, audio_status;
+ int handled = 0;
+ struct sram_channel *sram_ch;
+
+ if (!dev)
+ return -1;
+
+ sram_ch = dev->channels[dev->_audio_upstream_channel].sram_channels;
+
+ msk_stat = cx_read(sram_ch->int_mstat);
+ audio_status = cx_read(sram_ch->int_stat);
+
+ /* Only deal with our interrupt */
+ if (audio_status) {
+ handled = cx25821_audio_upstream_irq(dev,
+ dev->_audio_upstream_channel, audio_status);
+ }
+
+ if (handled < 0)
+ cx25821_stop_upstream_audio(dev);
+ else
+ handled += handled;
+
+ return IRQ_RETVAL(handled);
+}
+
+static void cx25821_wait_fifo_enable(struct cx25821_dev *dev,
+ struct sram_channel *sram_ch)
+{
+ int count = 0;
+ u32 tmp;
+
+ do {
+ /* Wait 10 microsecond before checking to see if the FIFO is
+ * turned ON. */
+ udelay(10);
+
+ tmp = cx_read(sram_ch->dma_ctl);
+
+ /* 10 millisecond timeout */
+ if (count++ > 1000) {
+ pr_err("ERROR: %s() fifo is NOT turned on. Timeout!\n",
+ __func__);
+ return;
+ }
+
+ } while (!(tmp & sram_ch->fld_aud_fifo_en));
+
+}
+
+int cx25821_start_audio_dma_upstream(struct cx25821_dev *dev,
+ struct sram_channel *sram_ch)
+{
+ u32 tmp = 0;
+ int err = 0;
+
+ /* Set the physical start address of the RISC program in the initial
+ * program counter(IPC) member of the CMDS. */
+ cx_write(sram_ch->cmds_start + 0, dev->_risc_phys_addr);
+ /* Risc IPC High 64 bits 63-32 */
+ cx_write(sram_ch->cmds_start + 4, 0);
+
+ /* reset counter */
+ cx_write(sram_ch->gpcnt_ctl, 3);
+
+ /* Set the line length (It looks like we do not need to set the
+ * line length) */
+ cx_write(sram_ch->aud_length, AUDIO_LINE_SIZE & FLD_AUD_DST_LN_LNGTH);
+
+ /* Set the input mode to 16-bit */
+ tmp = cx_read(sram_ch->aud_cfg);
+ tmp |= FLD_AUD_SRC_ENABLE | FLD_AUD_DST_PK_MODE | FLD_AUD_CLK_ENABLE |
+ FLD_AUD_MASTER_MODE | FLD_AUD_CLK_SELECT_PLL_D |
+ FLD_AUD_SONY_MODE;
+ cx_write(sram_ch->aud_cfg, tmp);
+
+ /* Read and write back the interrupt status register to clear it */
+ tmp = cx_read(sram_ch->int_stat);
+ cx_write(sram_ch->int_stat, tmp);
+
+ /* Clear our bits from the interrupt status register. */
+ cx_write(sram_ch->int_stat, _intr_msk);
+
+ /* Set the interrupt mask register, enable irq. */
+ cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit));
+ tmp = cx_read(sram_ch->int_msk);
+ cx_write(sram_ch->int_msk, tmp |= _intr_msk);
+
+ err = request_irq(dev->pci->irq, cx25821_upstream_irq_audio,
+ IRQF_SHARED, dev->name, dev);
+ if (err < 0) {
+ pr_err("%s: can't get upstream IRQ %d\n", dev->name,
+ dev->pci->irq);
+ goto fail_irq;
+ }
+
+ /* Start the DMA engine */
+ tmp = cx_read(sram_ch->dma_ctl);
+ cx_set(sram_ch->dma_ctl, tmp | sram_ch->fld_aud_risc_en);
+
+ dev->_audio_is_running = 1;
+ dev->_is_first_audio_frame = 1;
+
+ /* The fifo_en bit turns on by the first Risc program */
+ cx25821_wait_fifo_enable(dev, sram_ch);
+
+ return 0;
+
+fail_irq:
+ cx25821_dev_unregister(dev);
+ return err;
+}
+
+int cx25821_audio_upstream_init(struct cx25821_dev *dev, int channel_select)
+{
+ struct sram_channel *sram_ch;
+ int retval = 0;
+ int err = 0;
+ int str_length = 0;
+
+ if (dev->_audio_is_running) {
+ pr_warn("Audio Channel is still running so return!\n");
+ return 0;
+ }
+
+ dev->_audio_upstream_channel = channel_select;
+ sram_ch = dev->channels[channel_select].sram_channels;
+
+ /* Work queue */
+ INIT_WORK(&dev->_audio_work_entry, cx25821_audioups_handler);
+ dev->_irq_audio_queues =
+ create_singlethread_workqueue("cx25821_audioworkqueue");
+
+ if (!dev->_irq_audio_queues) {
+ printk(KERN_DEBUG
+ pr_fmt("ERROR: create_singlethread_workqueue() for Audio FAILED!\n"));
+ return -ENOMEM;
+ }
+
+ dev->_last_index_irq = 0;
+ dev->_audio_is_running = 0;
+ dev->_audioframe_count = 0;
+ dev->_audiofile_status = RESET_STATUS;
+ dev->_audio_lines_count = LINES_PER_AUDIO_BUFFER;
+ _line_size = AUDIO_LINE_SIZE;
+
+ if (dev->input_audiofilename) {
+ str_length = strlen(dev->input_audiofilename);
+ dev->_audiofilename = kmemdup(dev->input_audiofilename,
+ str_length + 1, GFP_KERNEL);
+
+ if (!dev->_audiofilename)
+ goto error;
+
+ /* Default if filename is empty string */
+ if (strcmp(dev->input_audiofilename, "") == 0)
+ dev->_audiofilename = "/root/audioGOOD.wav";
+ } else {
+ str_length = strlen(_defaultAudioName);
+ dev->_audiofilename = kmemdup(_defaultAudioName,
+ str_length + 1, GFP_KERNEL);
+
+ if (!dev->_audiofilename)
+ goto error;
+ }
+
+ retval = cx25821_sram_channel_setup_upstream_audio(dev, sram_ch,
+ _line_size, 0);
+
+ dev->audio_upstream_riscbuf_size =
+ AUDIO_RISC_DMA_BUF_SIZE * NUM_AUDIO_PROGS +
+ RISC_SYNC_INSTRUCTION_SIZE;
+ dev->audio_upstream_databuf_size = AUDIO_DATA_BUF_SZ * NUM_AUDIO_PROGS;
+
+ /* Allocating buffers and prepare RISC program */
+ retval = cx25821_audio_upstream_buffer_prepare(dev, sram_ch,
+ _line_size);
+ if (retval < 0) {
+ pr_err("%s: Failed to set up Audio upstream buffers!\n",
+ dev->name);
+ goto error;
+ }
+ /* Start RISC engine */
+ cx25821_start_audio_dma_upstream(dev, sram_ch);
+
+ return 0;
+
+error:
+ cx25821_dev_unregister(dev);
+
+ return err;
+}
diff --git a/drivers/media/video/cx25821/cx25821-audio-upstream.h b/drivers/media/video/cx25821/cx25821-audio-upstream.h
new file mode 100644
index 00000000..af2ae7c5
--- /dev/null
+++ b/drivers/media/video/cx25821/cx25821-audio-upstream.h
@@ -0,0 +1,62 @@
+/*
+ * Driver for the Conexant CX25821 PCIe bridge
+ *
+ * Copyright (C) 2009 Conexant Systems Inc.
+ * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+
+#define NUM_AUDIO_PROGS 8
+#define NUM_AUDIO_FRAMES 8
+#define END_OF_FILE 0
+#define IN_PROGRESS 1
+#define RESET_STATUS -1
+#define FIFO_DISABLE 0
+#define FIFO_ENABLE 1
+#define NUM_NO_OPS 4
+
+#define RISC_READ_INSTRUCTION_SIZE 12
+#define RISC_JUMP_INSTRUCTION_SIZE 12
+#define RISC_WRITECR_INSTRUCTION_SIZE 16
+#define RISC_SYNC_INSTRUCTION_SIZE 4
+#define DWORD_SIZE 4
+#define AUDIO_SYNC_LINE 4
+
+#define LINES_PER_AUDIO_BUFFER 15
+#define AUDIO_LINE_SIZE 128
+#define AUDIO_DATA_BUF_SZ (AUDIO_LINE_SIZE * LINES_PER_AUDIO_BUFFER)
+
+#define USE_RISC_NOOP_AUDIO 1
+
+#ifdef USE_RISC_NOOP_AUDIO
+#define AUDIO_RISC_DMA_BUF_SIZE \
+ (LINES_PER_AUDIO_BUFFER * RISC_READ_INSTRUCTION_SIZE + \
+ RISC_WRITECR_INSTRUCTION_SIZE + NUM_NO_OPS * DWORD_SIZE + \
+ RISC_JUMP_INSTRUCTION_SIZE)
+#endif
+
+#ifndef USE_RISC_NOOP_AUDIO
+#define AUDIO_RISC_DMA_BUF_SIZE \
+ (LINES_PER_AUDIO_BUFFER * RISC_READ_INSTRUCTION_SIZE + \
+ RISC_WRITECR_INSTRUCTION_SIZE + RISC_JUMP_INSTRUCTION_SIZE)
+#endif
+
+static int _line_size;
+char *_defaultAudioName = "/root/audioGOOD.wav";
diff --git a/drivers/media/video/cx25821/cx25821-audio.h b/drivers/media/video/cx25821/cx25821-audio.h
new file mode 100644
index 00000000..1fc2d24f
--- /dev/null
+++ b/drivers/media/video/cx25821/cx25821-audio.h
@@ -0,0 +1,62 @@
+/*
+ * Driver for the Conexant CX25821 PCIe bridge
+ *
+ * Copyright (C) 2009 Conexant Systems Inc.
+ * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __CX25821_AUDIO_H__
+#define __CX25821_AUDIO_H__
+
+#define USE_RISC_NOOP 1
+#define LINES_PER_BUFFER 15
+#define AUDIO_LINE_SIZE 128
+
+/* Number of buffer programs to use at once. */
+#define NUMBER_OF_PROGRAMS 8
+
+/*
+ * Max size of the RISC program for a buffer. - worst case is 2 writes per line
+ * Space is also added for the 4 no-op instructions added on the end.
+ */
+#ifndef USE_RISC_NOOP
+#define MAX_BUFFER_PROGRAM_SIZE \
+ (2 * LINES_PER_BUFFER * RISC_WRITE_INSTRUCTION_SIZE + \
+ RISC_WRITECR_INSTRUCTION_SIZE * 4)
+#endif
+
+/* MAE 12 July 2005 Try to use NOOP RISC instruction instead */
+#ifdef USE_RISC_NOOP
+#define MAX_BUFFER_PROGRAM_SIZE \
+ (2 * LINES_PER_BUFFER * RISC_WRITE_INSTRUCTION_SIZE + \
+ RISC_NOOP_INSTRUCTION_SIZE * 4)
+#endif
+
+/* Sizes of various instructions in bytes. Used when adding instructions. */
+#define RISC_WRITE_INSTRUCTION_SIZE 12
+#define RISC_JUMP_INSTRUCTION_SIZE 12
+#define RISC_SKIP_INSTRUCTION_SIZE 4
+#define RISC_SYNC_INSTRUCTION_SIZE 4
+#define RISC_WRITECR_INSTRUCTION_SIZE 16
+#define RISC_NOOP_INSTRUCTION_SIZE 4
+
+#define MAX_AUDIO_DMA_BUFFER_SIZE \
+ (MAX_BUFFER_PROGRAM_SIZE * NUMBER_OF_PROGRAMS + \
+ RISC_SYNC_INSTRUCTION_SIZE)
+
+#endif
diff --git a/drivers/media/video/cx25821/cx25821-biffuncs.h b/drivers/media/video/cx25821/cx25821-biffuncs.h
new file mode 100644
index 00000000..9326a7c7
--- /dev/null
+++ b/drivers/media/video/cx25821/cx25821-biffuncs.h
@@ -0,0 +1,45 @@
+/*
+ * Driver for the Conexant CX25821 PCIe bridge
+ *
+ * Copyright (C) 2009 Conexant Systems Inc.
+ * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _BITFUNCS_H
+#define _BITFUNCS_H
+
+#define SetBit(Bit) (1 << Bit)
+
+inline u8 getBit(u32 sample, u8 index)
+{
+ return (u8) ((sample >> index) & 1);
+}
+
+inline u32 clearBitAtPos(u32 value, u8 bit)
+{
+ return value & ~(1 << bit);
+}
+
+inline u32 setBitAtPos(u32 sample, u8 bit)
+{
+ sample |= (1 << bit);
+ return sample;
+
+}
+
+#endif
diff --git a/drivers/media/video/cx25821/cx25821-cards.c b/drivers/media/video/cx25821/cx25821-cards.c
new file mode 100644
index 00000000..99988c98
--- /dev/null
+++ b/drivers/media/video/cx25821/cx25821-cards.c
@@ -0,0 +1,72 @@
+/*
+ * Driver for the Conexant CX25821 PCIe bridge
+ *
+ * Copyright (C) 2009 Conexant Systems Inc.
+ * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
+ * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <media/cx25840.h>
+
+#include "cx25821.h"
+#include "tuner-xc2028.h"
+
+/* board config info */
+
+struct cx25821_board cx25821_boards[] = {
+ [UNKNOWN_BOARD] = {
+ .name = "UNKNOWN/GENERIC",
+ /* Ensure safe default for unknown boards */
+ .clk_freq = 0,
+ },
+
+ [CX25821_BOARD] = {
+ .name = "CX25821",
+ .portb = CX25821_RAW,
+ .portc = CX25821_264,
+ .input[0].type = CX25821_VMUX_COMPOSITE,
+ },
+
+};
+
+const unsigned int cx25821_bcount = ARRAY_SIZE(cx25821_boards);
+
+struct cx25821_subid cx25821_subids[] = {
+ {
+ .subvendor = 0x14f1,
+ .subdevice = 0x0920,
+ .card = CX25821_BOARD,
+ },
+};
+
+void cx25821_card_setup(struct cx25821_dev *dev)
+{
+ static u8 eeprom[256];
+
+ if (dev->i2c_bus[0].i2c_rc == 0) {
+ dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1;
+ tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom,
+ sizeof(eeprom));
+ }
+}
diff --git a/drivers/media/video/cx25821/cx25821-core.c b/drivers/media/video/cx25821/cx25821-core.c
new file mode 100644
index 00000000..7930ca58
--- /dev/null
+++ b/drivers/media/video/cx25821/cx25821-core.c
@@ -0,0 +1,1515 @@
+/*
+ * Driver for the Conexant CX25821 PCIe bridge
+ *
+ * Copyright (C) 2009 Conexant Systems Inc.
+ * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
+ * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include "cx25821.h"
+#include "cx25821-sram.h"
+#include "cx25821-video.h"
+
+MODULE_DESCRIPTION("Driver for Athena cards");
+MODULE_AUTHOR("Shu Lin - Hiep Huynh");
+MODULE_LICENSE("GPL");
+
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable debug messages");
+
+static unsigned int card[] = {[0 ... (CX25821_MAXBOARDS - 1)] = UNSET };
+module_param_array(card, int, NULL, 0444);
+MODULE_PARM_DESC(card, "card type");
+
+static unsigned int cx25821_devcount;
+
+DEFINE_MUTEX(cx25821_devlist_mutex);
+EXPORT_SYMBOL(cx25821_devlist_mutex);
+LIST_HEAD(cx25821_devlist);
+EXPORT_SYMBOL(cx25821_devlist);
+
+struct sram_channel cx25821_sram_channels[] = {
+ [SRAM_CH00] = {
+ .i = SRAM_CH00,
+ .name = "VID A",
+ .cmds_start = VID_A_DOWN_CMDS,
+ .ctrl_start = VID_A_IQ,
+ .cdt = VID_A_CDT,
+ .fifo_start = VID_A_DOWN_CLUSTER_1,
+ .fifo_size = (VID_CLUSTER_SIZE << 2),
+ .ptr1_reg = DMA1_PTR1,
+ .ptr2_reg = DMA1_PTR2,
+ .cnt1_reg = DMA1_CNT1,
+ .cnt2_reg = DMA1_CNT2,
+ .int_msk = VID_A_INT_MSK,
+ .int_stat = VID_A_INT_STAT,
+ .int_mstat = VID_A_INT_MSTAT,
+ .dma_ctl = VID_DST_A_DMA_CTL,
+ .gpcnt_ctl = VID_DST_A_GPCNT_CTL,
+ .gpcnt = VID_DST_A_GPCNT,
+ .vip_ctl = VID_DST_A_VIP_CTL,
+ .pix_frmt = VID_DST_A_PIX_FRMT,
+ },
+
+ [SRAM_CH01] = {
+ .i = SRAM_CH01,
+ .name = "VID B",
+ .cmds_start = VID_B_DOWN_CMDS,
+ .ctrl_start = VID_B_IQ,
+ .cdt = VID_B_CDT,
+ .fifo_start = VID_B_DOWN_CLUSTER_1,
+ .fifo_size = (VID_CLUSTER_SIZE << 2),
+ .ptr1_reg = DMA2_PTR1,
+ .ptr2_reg = DMA2_PTR2,
+ .cnt1_reg = DMA2_CNT1,
+ .cnt2_reg = DMA2_CNT2,
+ .int_msk = VID_B_INT_MSK,
+ .int_stat = VID_B_INT_STAT,
+ .int_mstat = VID_B_INT_MSTAT,
+ .dma_ctl = VID_DST_B_DMA_CTL,
+ .gpcnt_ctl = VID_DST_B_GPCNT_CTL,
+ .gpcnt = VID_DST_B_GPCNT,
+ .vip_ctl = VID_DST_B_VIP_CTL,
+ .pix_frmt = VID_DST_B_PIX_FRMT,
+ },
+
+ [SRAM_CH02] = {
+ .i = SRAM_CH02,
+ .name = "VID C",
+ .cmds_start = VID_C_DOWN_CMDS,
+ .ctrl_start = VID_C_IQ,
+ .cdt = VID_C_CDT,
+ .fifo_start = VID_C_DOWN_CLUSTER_1,
+ .fifo_size = (VID_CLUSTER_SIZE << 2),
+ .ptr1_reg = DMA3_PTR1,
+ .ptr2_reg = DMA3_PTR2,
+ .cnt1_reg = DMA3_CNT1,
+ .cnt2_reg = DMA3_CNT2,
+ .int_msk = VID_C_INT_MSK,
+ .int_stat = VID_C_INT_STAT,
+ .int_mstat = VID_C_INT_MSTAT,
+ .dma_ctl = VID_DST_C_DMA_CTL,
+ .gpcnt_ctl = VID_DST_C_GPCNT_CTL,
+ .gpcnt = VID_DST_C_GPCNT,
+ .vip_ctl = VID_DST_C_VIP_CTL,
+ .pix_frmt = VID_DST_C_PIX_FRMT,
+ },
+
+ [SRAM_CH03] = {
+ .i = SRAM_CH03,
+ .name = "VID D",
+ .cmds_start = VID_D_DOWN_CMDS,
+ .ctrl_start = VID_D_IQ,
+ .cdt = VID_D_CDT,
+ .fifo_start = VID_D_DOWN_CLUSTER_1,
+ .fifo_size = (VID_CLUSTER_SIZE << 2),
+ .ptr1_reg = DMA4_PTR1,
+ .ptr2_reg = DMA4_PTR2,
+ .cnt1_reg = DMA4_CNT1,
+ .cnt2_reg = DMA4_CNT2,
+ .int_msk = VID_D_INT_MSK,
+ .int_stat = VID_D_INT_STAT,
+ .int_mstat = VID_D_INT_MSTAT,
+ .dma_ctl = VID_DST_D_DMA_CTL,
+ .gpcnt_ctl = VID_DST_D_GPCNT_CTL,
+ .gpcnt = VID_DST_D_GPCNT,
+ .vip_ctl = VID_DST_D_VIP_CTL,
+ .pix_frmt = VID_DST_D_PIX_FRMT,
+ },
+
+ [SRAM_CH04] = {
+ .i = SRAM_CH04,
+ .name = "VID E",
+ .cmds_start = VID_E_DOWN_CMDS,
+ .ctrl_start = VID_E_IQ,
+ .cdt = VID_E_CDT,
+ .fifo_start = VID_E_DOWN_CLUSTER_1,
+ .fifo_size = (VID_CLUSTER_SIZE << 2),
+ .ptr1_reg = DMA5_PTR1,
+ .ptr2_reg = DMA5_PTR2,
+ .cnt1_reg = DMA5_CNT1,
+ .cnt2_reg = DMA5_CNT2,
+ .int_msk = VID_E_INT_MSK,
+ .int_stat = VID_E_INT_STAT,
+ .int_mstat = VID_E_INT_MSTAT,
+ .dma_ctl = VID_DST_E_DMA_CTL,
+ .gpcnt_ctl = VID_DST_E_GPCNT_CTL,
+ .gpcnt = VID_DST_E_GPCNT,
+ .vip_ctl = VID_DST_E_VIP_CTL,
+ .pix_frmt = VID_DST_E_PIX_FRMT,
+ },
+
+ [SRAM_CH05] = {
+ .i = SRAM_CH05,
+ .name = "VID F",
+ .cmds_start = VID_F_DOWN_CMDS,
+ .ctrl_start = VID_F_IQ,
+ .cdt = VID_F_CDT,
+ .fifo_start = VID_F_DOWN_CLUSTER_1,
+ .fifo_size = (VID_CLUSTER_SIZE << 2),
+ .ptr1_reg = DMA6_PTR1,
+ .ptr2_reg = DMA6_PTR2,
+ .cnt1_reg = DMA6_CNT1,
+ .cnt2_reg = DMA6_CNT2,
+ .int_msk = VID_F_INT_MSK,
+ .int_stat = VID_F_INT_STAT,
+ .int_mstat = VID_F_INT_MSTAT,
+ .dma_ctl = VID_DST_F_DMA_CTL,
+ .gpcnt_ctl = VID_DST_F_GPCNT_CTL,
+ .gpcnt = VID_DST_F_GPCNT,
+ .vip_ctl = VID_DST_F_VIP_CTL,
+ .pix_frmt = VID_DST_F_PIX_FRMT,
+ },
+
+ [SRAM_CH06] = {
+ .i = SRAM_CH06,
+ .name = "VID G",
+ .cmds_start = VID_G_DOWN_CMDS,
+ .ctrl_start = VID_G_IQ,
+ .cdt = VID_G_CDT,
+ .fifo_start = VID_G_DOWN_CLUSTER_1,
+ .fifo_size = (VID_CLUSTER_SIZE << 2),
+ .ptr1_reg = DMA7_PTR1,
+ .ptr2_reg = DMA7_PTR2,
+ .cnt1_reg = DMA7_CNT1,
+ .cnt2_reg = DMA7_CNT2,
+ .int_msk = VID_G_INT_MSK,
+ .int_stat = VID_G_INT_STAT,
+ .int_mstat = VID_G_INT_MSTAT,
+ .dma_ctl = VID_DST_G_DMA_CTL,
+ .gpcnt_ctl = VID_DST_G_GPCNT_CTL,
+ .gpcnt = VID_DST_G_GPCNT,
+ .vip_ctl = VID_DST_G_VIP_CTL,
+ .pix_frmt = VID_DST_G_PIX_FRMT,
+ },
+
+ [SRAM_CH07] = {
+ .i = SRAM_CH07,
+ .name = "VID H",
+ .cmds_start = VID_H_DOWN_CMDS,
+ .ctrl_start = VID_H_IQ,
+ .cdt = VID_H_CDT,
+ .fifo_start = VID_H_DOWN_CLUSTER_1,
+ .fifo_size = (VID_CLUSTER_SIZE << 2),
+ .ptr1_reg = DMA8_PTR1,
+ .ptr2_reg = DMA8_PTR2,
+ .cnt1_reg = DMA8_CNT1,
+ .cnt2_reg = DMA8_CNT2,
+ .int_msk = VID_H_INT_MSK,
+ .int_stat = VID_H_INT_STAT,
+ .int_mstat = VID_H_INT_MSTAT,
+ .dma_ctl = VID_DST_H_DMA_CTL,
+ .gpcnt_ctl = VID_DST_H_GPCNT_CTL,
+ .gpcnt = VID_DST_H_GPCNT,
+ .vip_ctl = VID_DST_H_VIP_CTL,
+ .pix_frmt = VID_DST_H_PIX_FRMT,
+ },
+
+ [SRAM_CH08] = {
+ .name = "audio from",
+ .cmds_start = AUD_A_DOWN_CMDS,
+ .ctrl_start = AUD_A_IQ,
+ .cdt = AUD_A_CDT,
+ .fifo_start = AUD_A_DOWN_CLUSTER_1,
+ .fifo_size = AUDIO_CLUSTER_SIZE * 3,
+ .ptr1_reg = DMA17_PTR1,
+ .ptr2_reg = DMA17_PTR2,
+ .cnt1_reg = DMA17_CNT1,
+ .cnt2_reg = DMA17_CNT2,
+ },
+
+ [SRAM_CH09] = {
+ .i = SRAM_CH09,
+ .name = "VID Upstream I",
+ .cmds_start = VID_I_UP_CMDS,
+ .ctrl_start = VID_I_IQ,
+ .cdt = VID_I_CDT,
+ .fifo_start = VID_I_UP_CLUSTER_1,
+ .fifo_size = (VID_CLUSTER_SIZE << 2),
+ .ptr1_reg = DMA15_PTR1,
+ .ptr2_reg = DMA15_PTR2,
+ .cnt1_reg = DMA15_CNT1,
+ .cnt2_reg = DMA15_CNT2,
+ .int_msk = VID_I_INT_MSK,
+ .int_stat = VID_I_INT_STAT,
+ .int_mstat = VID_I_INT_MSTAT,
+ .dma_ctl = VID_SRC_I_DMA_CTL,
+ .gpcnt_ctl = VID_SRC_I_GPCNT_CTL,
+ .gpcnt = VID_SRC_I_GPCNT,
+
+ .vid_fmt_ctl = VID_SRC_I_FMT_CTL,
+ .vid_active_ctl1 = VID_SRC_I_ACTIVE_CTL1,
+ .vid_active_ctl2 = VID_SRC_I_ACTIVE_CTL2,
+ .vid_cdt_size = VID_SRC_I_CDT_SZ,
+ .irq_bit = 8,
+ },
+
+ [SRAM_CH10] = {
+ .i = SRAM_CH10,
+ .name = "VID Upstream J",
+ .cmds_start = VID_J_UP_CMDS,
+ .ctrl_start = VID_J_IQ,
+ .cdt = VID_J_CDT,
+ .fifo_start = VID_J_UP_CLUSTER_1,
+ .fifo_size = (VID_CLUSTER_SIZE << 2),
+ .ptr1_reg = DMA16_PTR1,
+ .ptr2_reg = DMA16_PTR2,
+ .cnt1_reg = DMA16_CNT1,
+ .cnt2_reg = DMA16_CNT2,
+ .int_msk = VID_J_INT_MSK,
+ .int_stat = VID_J_INT_STAT,
+ .int_mstat = VID_J_INT_MSTAT,
+ .dma_ctl = VID_SRC_J_DMA_CTL,
+ .gpcnt_ctl = VID_SRC_J_GPCNT_CTL,
+ .gpcnt = VID_SRC_J_GPCNT,
+
+ .vid_fmt_ctl = VID_SRC_J_FMT_CTL,
+ .vid_active_ctl1 = VID_SRC_J_ACTIVE_CTL1,
+ .vid_active_ctl2 = VID_SRC_J_ACTIVE_CTL2,
+ .vid_cdt_size = VID_SRC_J_CDT_SZ,
+ .irq_bit = 9,
+ },
+
+ [SRAM_CH11] = {
+ .i = SRAM_CH11,
+ .name = "Audio Upstream Channel B",
+ .cmds_start = AUD_B_UP_CMDS,
+ .ctrl_start = AUD_B_IQ,
+ .cdt = AUD_B_CDT,
+ .fifo_start = AUD_B_UP_CLUSTER_1,
+ .fifo_size = (AUDIO_CLUSTER_SIZE * 3),
+ .ptr1_reg = DMA22_PTR1,
+ .ptr2_reg = DMA22_PTR2,
+ .cnt1_reg = DMA22_CNT1,
+ .cnt2_reg = DMA22_CNT2,
+ .int_msk = AUD_B_INT_MSK,
+ .int_stat = AUD_B_INT_STAT,
+ .int_mstat = AUD_B_INT_MSTAT,
+ .dma_ctl = AUD_INT_DMA_CTL,
+ .gpcnt_ctl = AUD_B_GPCNT_CTL,
+ .gpcnt = AUD_B_GPCNT,
+ .aud_length = AUD_B_LNGTH,
+ .aud_cfg = AUD_B_CFG,
+ .fld_aud_fifo_en = FLD_AUD_SRC_B_FIFO_EN,
+ .fld_aud_risc_en = FLD_AUD_SRC_B_RISC_EN,
+ .irq_bit = 11,
+ },
+};
+EXPORT_SYMBOL(cx25821_sram_channels);
+
+struct sram_channel *channel0 = &cx25821_sram_channels[SRAM_CH00];
+struct sram_channel *channel1 = &cx25821_sram_channels[SRAM_CH01];
+struct sram_channel *channel2 = &cx25821_sram_channels[SRAM_CH02];
+struct sram_channel *channel3 = &cx25821_sram_channels[SRAM_CH03];
+struct sram_channel *channel4 = &cx25821_sram_channels[SRAM_CH04];
+struct sram_channel *channel5 = &cx25821_sram_channels[SRAM_CH05];
+struct sram_channel *channel6 = &cx25821_sram_channels[SRAM_CH06];
+struct sram_channel *channel7 = &cx25821_sram_channels[SRAM_CH07];
+struct sram_channel *channel9 = &cx25821_sram_channels[SRAM_CH09];
+struct sram_channel *channel10 = &cx25821_sram_channels[SRAM_CH10];
+struct sram_channel *channel11 = &cx25821_sram_channels[SRAM_CH11];
+
+struct cx25821_dmaqueue mpegq;
+
+static int cx25821_risc_decode(u32 risc)
+{
+ static const char * const instr[16] = {
+ [RISC_SYNC >> 28] = "sync",
+ [RISC_WRITE >> 28] = "write",
+ [RISC_WRITEC >> 28] = "writec",
+ [RISC_READ >> 28] = "read",
+ [RISC_READC >> 28] = "readc",
+ [RISC_JUMP >> 28] = "jump",
+ [RISC_SKIP >> 28] = "skip",
+ [RISC_WRITERM >> 28] = "writerm",
+ [RISC_WRITECM >> 28] = "writecm",
+ [RISC_WRITECR >> 28] = "writecr",
+ };
+ static const int incr[16] = {
+ [RISC_WRITE >> 28] = 3,
+ [RISC_JUMP >> 28] = 3,
+ [RISC_SKIP >> 28] = 1,
+ [RISC_SYNC >> 28] = 1,
+ [RISC_WRITERM >> 28] = 3,
+ [RISC_WRITECM >> 28] = 3,
+ [RISC_WRITECR >> 28] = 4,
+ };
+ static const char * const bits[] = {
+ "12", "13", "14", "resync",
+ "cnt0", "cnt1", "18", "19",
+ "20", "21", "22", "23",
+ "irq1", "irq2", "eol", "sol",
+ };
+ int i;
+
+ pr_cont("0x%08x [ %s",
+ risc, instr[risc >> 28] ? instr[risc >> 28] : "INVALID");
+ for (i = ARRAY_SIZE(bits) - 1; i >= 0; i--) {
+ if (risc & (1 << (i + 12)))
+ pr_cont(" %s", bits[i]);
+ }
+ pr_cont(" count=%d ]\n", risc & 0xfff);
+ return incr[risc >> 28] ? incr[risc >> 28] : 1;
+}
+
+static inline int i2c_slave_did_ack(struct i2c_adapter *i2c_adap)
+{
+ struct cx25821_i2c *bus = i2c_adap->algo_data;
+ struct cx25821_dev *dev = bus->dev;
+ return cx_read(bus->reg_stat) & 0x01;
+}
+
+void cx_i2c_read_print(struct cx25821_dev *dev, u32 reg, const char *reg_string)
+{
+ int tmp = 0;
+ u32 value = 0;
+
+ value = cx25821_i2c_read(&dev->i2c_bus[0], reg, &tmp);
+}
+
+static void cx25821_registers_init(struct cx25821_dev *dev)
+{
+ u32 tmp;
+
+ /* enable RUN_RISC in Pecos */
+ cx_write(DEV_CNTRL2, 0x20);
+
+ /* Set the master PCI interrupt masks to enable video, audio, MBIF,
+ * and GPIO interrupts
+ * I2C interrupt masking is handled by the I2C objects themselves. */
+ cx_write(PCI_INT_MSK, 0x2001FFFF);
+
+ tmp = cx_read(RDR_TLCTL0);
+ tmp &= ~FLD_CFG_RCB_CK_EN; /* Clear the RCB_CK_EN bit */
+ cx_write(RDR_TLCTL0, tmp);
+
+ /* PLL-A setting for the Audio Master Clock */
+ cx_write(PLL_A_INT_FRAC, 0x9807A58B);
+
+ /* PLL_A_POST = 0x1C, PLL_A_OUT_TO_PIN = 0x1 */
+ cx_write(PLL_A_POST_STAT_BIST, 0x8000019C);
+
+ /* clear reset bit [31] */
+ tmp = cx_read(PLL_A_INT_FRAC);
+ cx_write(PLL_A_INT_FRAC, tmp & 0x7FFFFFFF);
+
+ /* PLL-B setting for Mobilygen Host Bus Interface */
+ cx_write(PLL_B_INT_FRAC, 0x9883A86F);
+
+ /* PLL_B_POST = 0xD, PLL_B_OUT_TO_PIN = 0x0 */
+ cx_write(PLL_B_POST_STAT_BIST, 0x8000018D);
+
+ /* clear reset bit [31] */
+ tmp = cx_read(PLL_B_INT_FRAC);
+ cx_write(PLL_B_INT_FRAC, tmp & 0x7FFFFFFF);
+
+ /* PLL-C setting for video upstream channel */
+ cx_write(PLL_C_INT_FRAC, 0x96A0EA3F);
+
+ /* PLL_C_POST = 0x3, PLL_C_OUT_TO_PIN = 0x0 */
+ cx_write(PLL_C_POST_STAT_BIST, 0x80000103);
+
+ /* clear reset bit [31] */
+ tmp = cx_read(PLL_C_INT_FRAC);
+ cx_write(PLL_C_INT_FRAC, tmp & 0x7FFFFFFF);
+
+ /* PLL-D setting for audio upstream channel */
+ cx_write(PLL_D_INT_FRAC, 0x98757F5B);
+
+ /* PLL_D_POST = 0x13, PLL_D_OUT_TO_PIN = 0x0 */
+ cx_write(PLL_D_POST_STAT_BIST, 0x80000113);
+
+ /* clear reset bit [31] */
+ tmp = cx_read(PLL_D_INT_FRAC);
+ cx_write(PLL_D_INT_FRAC, tmp & 0x7FFFFFFF);
+
+ /* This selects the PLL C clock source for the video upstream channel
+ * I and J */
+ tmp = cx_read(VID_CH_CLK_SEL);
+ cx_write(VID_CH_CLK_SEL, (tmp & 0x00FFFFFF) | 0x24000000);
+
+ /* 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for
+ * channel A-C
+ * select 656/VIP DST for downstream Channel A - C */
+ tmp = cx_read(VID_CH_MODE_SEL);
+ /* cx_write( VID_CH_MODE_SEL, tmp | 0x1B0001FF); */
+ cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00);
+
+ /* enables 656 port I and J as output */
+ tmp = cx_read(CLK_RST);
+ /* use external ALT_PLL_REF pin as its reference clock instead */
+ tmp |= FLD_USE_ALT_PLL_REF;
+ cx_write(CLK_RST, tmp & ~(FLD_VID_I_CLK_NOE | FLD_VID_J_CLK_NOE));
+
+ mdelay(100);
+}
+
+int cx25821_sram_channel_setup(struct cx25821_dev *dev,
+ struct sram_channel *ch,
+ unsigned int bpl, u32 risc)
+{
+ unsigned int i, lines;
+ u32 cdt;
+
+ if (ch->cmds_start == 0) {
+ cx_write(ch->ptr1_reg, 0);
+ cx_write(ch->ptr2_reg, 0);
+ cx_write(ch->cnt2_reg, 0);
+ cx_write(ch->cnt1_reg, 0);
+ return 0;
+ }
+
+ bpl = (bpl + 7) & ~7; /* alignment */
+ cdt = ch->cdt;
+ lines = ch->fifo_size / bpl;
+
+ if (lines > 4)
+ lines = 4;
+
+ BUG_ON(lines < 2);
+
+ cx_write(8 + 0, RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
+ cx_write(8 + 4, 8);
+ cx_write(8 + 8, 0);
+
+ /* write CDT */
+ for (i = 0; i < lines; i++) {
+ cx_write(cdt + 16 * i, ch->fifo_start + bpl * i);
+ cx_write(cdt + 16 * i + 4, 0);
+ cx_write(cdt + 16 * i + 8, 0);
+ cx_write(cdt + 16 * i + 12, 0);
+ }
+
+ /* init the first cdt buffer */
+ for (i = 0; i < 128; i++)
+ cx_write(ch->fifo_start + 4 * i, i);
+
+ /* write CMDS */
+ if (ch->jumponly)
+ cx_write(ch->cmds_start + 0, 8);
+ else
+ cx_write(ch->cmds_start + 0, risc);
+
+ cx_write(ch->cmds_start + 4, 0); /* 64 bits 63-32 */
+ cx_write(ch->cmds_start + 8, cdt);
+ cx_write(ch->cmds_start + 12, (lines * 16) >> 3);
+ cx_write(ch->cmds_start + 16, ch->ctrl_start);
+
+ if (ch->jumponly)
+ cx_write(ch->cmds_start + 20, 0x80000000 | (64 >> 2));
+ else
+ cx_write(ch->cmds_start + 20, 64 >> 2);
+
+ for (i = 24; i < 80; i += 4)
+ cx_write(ch->cmds_start + i, 0);
+
+ /* fill registers */
+ cx_write(ch->ptr1_reg, ch->fifo_start);
+ cx_write(ch->ptr2_reg, cdt);
+ cx_write(ch->cnt2_reg, (lines * 16) >> 3);
+ cx_write(ch->cnt1_reg, (bpl >> 3) - 1);
+
+ return 0;
+}
+EXPORT_SYMBOL(cx25821_sram_channel_setup);
+
+int cx25821_sram_channel_setup_audio(struct cx25821_dev *dev,
+ struct sram_channel *ch,
+ unsigned int bpl, u32 risc)
+{
+ unsigned int i, lines;
+ u32 cdt;
+
+ if (ch->cmds_start == 0) {
+ cx_write(ch->ptr1_reg, 0);
+ cx_write(ch->ptr2_reg, 0);
+ cx_write(ch->cnt2_reg, 0);
+ cx_write(ch->cnt1_reg, 0);
+ return 0;
+ }
+
+ bpl = (bpl + 7) & ~7; /* alignment */
+ cdt = ch->cdt;
+ lines = ch->fifo_size / bpl;
+
+ if (lines > 3)
+ lines = 3; /* for AUDIO */
+
+ BUG_ON(lines < 2);
+
+ cx_write(8 + 0, RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
+ cx_write(8 + 4, 8);
+ cx_write(8 + 8, 0);
+
+ /* write CDT */
+ for (i = 0; i < lines; i++) {
+ cx_write(cdt + 16 * i, ch->fifo_start + bpl * i);
+ cx_write(cdt + 16 * i + 4, 0);
+ cx_write(cdt + 16 * i + 8, 0);
+ cx_write(cdt + 16 * i + 12, 0);
+ }
+
+ /* write CMDS */
+ if (ch->jumponly)
+ cx_write(ch->cmds_start + 0, 8);
+ else
+ cx_write(ch->cmds_start + 0, risc);
+
+ cx_write(ch->cmds_start + 4, 0); /* 64 bits 63-32 */
+ cx_write(ch->cmds_start + 8, cdt);
+ cx_write(ch->cmds_start + 12, (lines * 16) >> 3);
+ cx_write(ch->cmds_start + 16, ch->ctrl_start);
+
+ /* IQ size */
+ if (ch->jumponly)
+ cx_write(ch->cmds_start + 20, 0x80000000 | (64 >> 2));
+ else
+ cx_write(ch->cmds_start + 20, 64 >> 2);
+
+ /* zero out */
+ for (i = 24; i < 80; i += 4)
+ cx_write(ch->cmds_start + i, 0);
+
+ /* fill registers */
+ cx_write(ch->ptr1_reg, ch->fifo_start);
+ cx_write(ch->ptr2_reg, cdt);
+ cx_write(ch->cnt2_reg, (lines * 16) >> 3);
+ cx_write(ch->cnt1_reg, (bpl >> 3) - 1);
+
+ return 0;
+}
+EXPORT_SYMBOL(cx25821_sram_channel_setup_audio);
+
+void cx25821_sram_channel_dump(struct cx25821_dev *dev, struct sram_channel *ch)
+{
+ static char *name[] = {
+ "init risc lo",
+ "init risc hi",
+ "cdt base",
+ "cdt size",
+ "iq base",
+ "iq size",
+ "risc pc lo",
+ "risc pc hi",
+ "iq wr ptr",
+ "iq rd ptr",
+ "cdt current",
+ "pci target lo",
+ "pci target hi",
+ "line / byte",
+ };
+ u32 risc;
+ unsigned int i, j, n;
+
+ pr_warn("%s: %s - dma channel status dump\n", dev->name, ch->name);
+ for (i = 0; i < ARRAY_SIZE(name); i++)
+ pr_warn("cmds + 0x%2x: %-15s: 0x%08x\n",
+ i * 4, name[i], cx_read(ch->cmds_start + 4 * i));
+
+ j = i * 4;
+ for (i = 0; i < 4;) {
+ risc = cx_read(ch->cmds_start + 4 * (i + 14));
+ pr_warn("cmds + 0x%2x: risc%d: ", j + i * 4, i);
+ i += cx25821_risc_decode(risc);
+ }
+
+ for (i = 0; i < (64 >> 2); i += n) {
+ risc = cx_read(ch->ctrl_start + 4 * i);
+ /* No consideration for bits 63-32 */
+
+ pr_warn("ctrl + 0x%2x (0x%08x): iq %x: ",
+ i * 4, ch->ctrl_start + 4 * i, i);
+ n = cx25821_risc_decode(risc);
+ for (j = 1; j < n; j++) {
+ risc = cx_read(ch->ctrl_start + 4 * (i + j));
+ pr_warn("ctrl + 0x%2x : iq %x: 0x%08x [ arg #%d ]\n",
+ 4 * (i + j), i + j, risc, j);
+ }
+ }
+
+ pr_warn(" : fifo: 0x%08x -> 0x%x\n",
+ ch->fifo_start, ch->fifo_start + ch->fifo_size);
+ pr_warn(" : ctrl: 0x%08x -> 0x%x\n",
+ ch->ctrl_start, ch->ctrl_start + 6 * 16);
+ pr_warn(" : ptr1_reg: 0x%08x\n",
+ cx_read(ch->ptr1_reg));
+ pr_warn(" : ptr2_reg: 0x%08x\n",
+ cx_read(ch->ptr2_reg));
+ pr_warn(" : cnt1_reg: 0x%08x\n",
+ cx_read(ch->cnt1_reg));
+ pr_warn(" : cnt2_reg: 0x%08x\n",
+ cx_read(ch->cnt2_reg));
+}
+EXPORT_SYMBOL(cx25821_sram_channel_dump);
+
+void cx25821_sram_channel_dump_audio(struct cx25821_dev *dev,
+ struct sram_channel *ch)
+{
+ static const char * const name[] = {
+ "init risc lo",
+ "init risc hi",
+ "cdt base",
+ "cdt size",
+ "iq base",
+ "iq size",
+ "risc pc lo",
+ "risc pc hi",
+ "iq wr ptr",
+ "iq rd ptr",
+ "cdt current",
+ "pci target lo",
+ "pci target hi",
+ "line / byte",
+ };
+
+ u32 risc, value, tmp;
+ unsigned int i, j, n;
+
+ pr_info("\n%s: %s - dma Audio channel status dump\n",
+ dev->name, ch->name);
+
+ for (i = 0; i < ARRAY_SIZE(name); i++)
+ pr_info("%s: cmds + 0x%2x: %-15s: 0x%08x\n",
+ dev->name, i * 4, name[i],
+ cx_read(ch->cmds_start + 4 * i));
+
+ j = i * 4;
+ for (i = 0; i < 4;) {
+ risc = cx_read(ch->cmds_start + 4 * (i + 14));
+ pr_warn("cmds + 0x%2x: risc%d: ", j + i * 4, i);
+ i += cx25821_risc_decode(risc);
+ }
+
+ for (i = 0; i < (64 >> 2); i += n) {
+ risc = cx_read(ch->ctrl_start + 4 * i);
+ /* No consideration for bits 63-32 */
+
+ pr_warn("ctrl + 0x%2x (0x%08x): iq %x: ",
+ i * 4, ch->ctrl_start + 4 * i, i);
+ n = cx25821_risc_decode(risc);
+
+ for (j = 1; j < n; j++) {
+ risc = cx_read(ch->ctrl_start + 4 * (i + j));
+ pr_warn("ctrl + 0x%2x : iq %x: 0x%08x [ arg #%d ]\n",
+ 4 * (i + j), i + j, risc, j);
+ }
+ }
+
+ pr_warn(" : fifo: 0x%08x -> 0x%x\n",
+ ch->fifo_start, ch->fifo_start + ch->fifo_size);
+ pr_warn(" : ctrl: 0x%08x -> 0x%x\n",
+ ch->ctrl_start, ch->ctrl_start + 6 * 16);
+ pr_warn(" : ptr1_reg: 0x%08x\n",
+ cx_read(ch->ptr1_reg));
+ pr_warn(" : ptr2_reg: 0x%08x\n",
+ cx_read(ch->ptr2_reg));
+ pr_warn(" : cnt1_reg: 0x%08x\n",
+ cx_read(ch->cnt1_reg));
+ pr_warn(" : cnt2_reg: 0x%08x\n",
+ cx_read(ch->cnt2_reg));
+
+ for (i = 0; i < 4; i++) {
+ risc = cx_read(ch->cmds_start + 56 + (i * 4));
+ pr_warn("instruction %d = 0x%x\n", i, risc);
+ }
+
+ /* read data from the first cdt buffer */
+ risc = cx_read(AUD_A_CDT);
+ pr_warn("\nread cdt loc=0x%x\n", risc);
+ for (i = 0; i < 8; i++) {
+ n = cx_read(risc + i * 4);
+ pr_cont("0x%x ", n);
+ }
+ pr_cont("\n\n");
+
+ value = cx_read(CLK_RST);
+ CX25821_INFO(" CLK_RST = 0x%x\n\n", value);
+
+ value = cx_read(PLL_A_POST_STAT_BIST);
+ CX25821_INFO(" PLL_A_POST_STAT_BIST = 0x%x\n\n", value);
+ value = cx_read(PLL_A_INT_FRAC);
+ CX25821_INFO(" PLL_A_INT_FRAC = 0x%x\n\n", value);
+
+ value = cx_read(PLL_B_POST_STAT_BIST);
+ CX25821_INFO(" PLL_B_POST_STAT_BIST = 0x%x\n\n", value);
+ value = cx_read(PLL_B_INT_FRAC);
+ CX25821_INFO(" PLL_B_INT_FRAC = 0x%x\n\n", value);
+
+ value = cx_read(PLL_C_POST_STAT_BIST);
+ CX25821_INFO(" PLL_C_POST_STAT_BIST = 0x%x\n\n", value);
+ value = cx_read(PLL_C_INT_FRAC);
+ CX25821_INFO(" PLL_C_INT_FRAC = 0x%x\n\n", value);
+
+ value = cx_read(PLL_D_POST_STAT_BIST);
+ CX25821_INFO(" PLL_D_POST_STAT_BIST = 0x%x\n\n", value);
+ value = cx_read(PLL_D_INT_FRAC);
+ CX25821_INFO(" PLL_D_INT_FRAC = 0x%x\n\n", value);
+
+ value = cx25821_i2c_read(&dev->i2c_bus[0], AFE_AB_DIAG_CTRL, &tmp);
+ CX25821_INFO(" AFE_AB_DIAG_CTRL (0x10900090) = 0x%x\n\n", value);
+}
+EXPORT_SYMBOL(cx25821_sram_channel_dump_audio);
+
+static void cx25821_shutdown(struct cx25821_dev *dev)
+{
+ int i;
+
+ /* disable RISC controller */
+ cx_write(DEV_CNTRL2, 0);
+
+ /* Disable Video A/B activity */
+ for (i = 0; i < VID_CHANNEL_NUM; i++) {
+ cx_write(dev->channels[i].sram_channels->dma_ctl, 0);
+ cx_write(dev->channels[i].sram_channels->int_msk, 0);
+ }
+
+ for (i = VID_UPSTREAM_SRAM_CHANNEL_I;
+ i <= VID_UPSTREAM_SRAM_CHANNEL_J; i++) {
+ cx_write(dev->channels[i].sram_channels->dma_ctl, 0);
+ cx_write(dev->channels[i].sram_channels->int_msk, 0);
+ }
+
+ /* Disable Audio activity */
+ cx_write(AUD_INT_DMA_CTL, 0);
+
+ /* Disable Serial port */
+ cx_write(UART_CTL, 0);
+
+ /* Disable Interrupts */
+ cx_write(PCI_INT_MSK, 0);
+ cx_write(AUD_A_INT_MSK, 0);
+}
+
+void cx25821_set_pixel_format(struct cx25821_dev *dev, int channel_select,
+ u32 format)
+{
+ if (channel_select <= 7 && channel_select >= 0) {
+ cx_write(dev->channels[channel_select].sram_channels->pix_frmt,
+ format);
+ dev->channels[channel_select].pixel_formats = format;
+ }
+}
+
+static void cx25821_set_vip_mode(struct cx25821_dev *dev,
+ struct sram_channel *ch)
+{
+ cx_write(ch->pix_frmt, PIXEL_FRMT_422);
+ cx_write(ch->vip_ctl, PIXEL_ENGINE_VIP1);
+}
+
+static void cx25821_initialize(struct cx25821_dev *dev)
+{
+ int i;
+
+ dprintk(1, "%s()\n", __func__);
+
+ cx25821_shutdown(dev);
+ cx_write(PCI_INT_STAT, 0xffffffff);
+
+ for (i = 0; i < VID_CHANNEL_NUM; i++)
+ cx_write(dev->channels[i].sram_channels->int_stat, 0xffffffff);
+
+ cx_write(AUD_A_INT_STAT, 0xffffffff);
+ cx_write(AUD_B_INT_STAT, 0xffffffff);
+ cx_write(AUD_C_INT_STAT, 0xffffffff);
+ cx_write(AUD_D_INT_STAT, 0xffffffff);
+ cx_write(AUD_E_INT_STAT, 0xffffffff);
+
+ cx_write(CLK_DELAY, cx_read(CLK_DELAY) & 0x80000000);
+ cx_write(PAD_CTRL, 0x12); /* for I2C */
+ cx25821_registers_init(dev); /* init Pecos registers */
+ mdelay(100);
+
+ for (i = 0; i < VID_CHANNEL_NUM; i++) {
+ cx25821_set_vip_mode(dev, dev->channels[i].sram_channels);
+ cx25821_sram_channel_setup(dev, dev->channels[i].sram_channels,
+ 1440, 0);
+ dev->channels[i].pixel_formats = PIXEL_FRMT_422;
+ dev->channels[i].use_cif_resolution = FALSE;
+ }
+
+ /* Probably only affect Downstream */
+ for (i = VID_UPSTREAM_SRAM_CHANNEL_I;
+ i <= VID_UPSTREAM_SRAM_CHANNEL_J; i++) {
+ cx25821_set_vip_mode(dev, dev->channels[i].sram_channels);
+ }
+
+ cx25821_sram_channel_setup_audio(dev,
+ dev->channels[SRAM_CH08].sram_channels, 128, 0);
+
+ cx25821_gpio_init(dev);
+}
+
+static int cx25821_get_resources(struct cx25821_dev *dev)
+{
+ if (request_mem_region(pci_resource_start(dev->pci, 0),
+ pci_resource_len(dev->pci, 0), dev->name))
+ return 0;
+
+ pr_err("%s: can't get MMIO memory @ 0x%llx\n",
+ dev->name, (unsigned long long)pci_resource_start(dev->pci, 0));
+
+ return -EBUSY;
+}
+
+static void cx25821_dev_checkrevision(struct cx25821_dev *dev)
+{
+ dev->hwrevision = cx_read(RDR_CFG2) & 0xff;
+
+ pr_info("%s(): Hardware revision = 0x%02x\n",
+ __func__, dev->hwrevision);
+}
+
+static void cx25821_iounmap(struct cx25821_dev *dev)
+{
+ if (dev == NULL)
+ return;
+
+ /* Releasing IO memory */
+ if (dev->lmmio != NULL) {
+ CX25821_INFO("Releasing lmmio.\n");
+ iounmap(dev->lmmio);
+ dev->lmmio = NULL;
+ }
+}
+
+static int cx25821_dev_setup(struct cx25821_dev *dev)
+{
+ int io_size = 0, i;
+
+ pr_info("\n***********************************\n");
+ pr_info("cx25821 set up\n");
+ pr_info("***********************************\n\n");
+
+ mutex_init(&dev->lock);
+
+ atomic_inc(&dev->refcount);
+
+ dev->nr = ++cx25821_devcount;
+ sprintf(dev->name, "cx25821[%d]", dev->nr);
+
+ mutex_lock(&cx25821_devlist_mutex);
+ list_add_tail(&dev->devlist, &cx25821_devlist);
+ mutex_unlock(&cx25821_devlist_mutex);
+
+ strcpy(cx25821_boards[UNKNOWN_BOARD].name, "unknown");
+ strcpy(cx25821_boards[CX25821_BOARD].name, "cx25821");
+
+ if (dev->pci->device != 0x8210) {
+ pr_info("%s(): Exiting. Incorrect Hardware device = 0x%02x\n",
+ __func__, dev->pci->device);
+ return -1;
+ } else {
+ pr_info("Athena Hardware device = 0x%02x\n", dev->pci->device);
+ }
+
+ /* Apply a sensible clock frequency for the PCIe bridge */
+ dev->clk_freq = 28000000;
+ for (i = 0; i < MAX_VID_CHANNEL_NUM; i++)
+ dev->channels[i].sram_channels = &cx25821_sram_channels[i];
+
+ if (dev->nr > 1)
+ CX25821_INFO("dev->nr > 1!");
+
+ /* board config */
+ dev->board = 1; /* card[dev->nr]; */
+ dev->_max_num_decoders = MAX_DECODERS;
+
+ dev->pci_bus = dev->pci->bus->number;
+ dev->pci_slot = PCI_SLOT(dev->pci->devfn);
+ dev->pci_irqmask = 0x001f00;
+
+ /* External Master 1 Bus */
+ dev->i2c_bus[0].nr = 0;
+ dev->i2c_bus[0].dev = dev;
+ dev->i2c_bus[0].reg_stat = I2C1_STAT;
+ dev->i2c_bus[0].reg_ctrl = I2C1_CTRL;
+ dev->i2c_bus[0].reg_addr = I2C1_ADDR;
+ dev->i2c_bus[0].reg_rdata = I2C1_RDATA;
+ dev->i2c_bus[0].reg_wdata = I2C1_WDATA;
+ dev->i2c_bus[0].i2c_period = (0x07 << 24); /* 1.95MHz */
+
+ if (cx25821_get_resources(dev) < 0) {
+ pr_err("%s: No more PCIe resources for subsystem: %04x:%04x\n",
+ dev->name, dev->pci->subsystem_vendor,
+ dev->pci->subsystem_device);
+
+ cx25821_devcount--;
+ return -EBUSY;
+ }
+
+ /* PCIe stuff */
+ dev->base_io_addr = pci_resource_start(dev->pci, 0);
+ io_size = pci_resource_len(dev->pci, 0);
+
+ if (!dev->base_io_addr) {
+ CX25821_ERR("No PCI Memory resources, exiting!\n");
+ return -ENODEV;
+ }
+
+ dev->lmmio = ioremap(dev->base_io_addr, pci_resource_len(dev->pci, 0));
+
+ if (!dev->lmmio) {
+ CX25821_ERR("ioremap failed, maybe increasing __VMALLOC_RESERVE in page.h\n");
+ cx25821_iounmap(dev);
+ return -ENOMEM;
+ }
+
+ dev->bmmio = (u8 __iomem *) dev->lmmio;
+
+ pr_info("%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n",
+ dev->name, dev->pci->subsystem_vendor,
+ dev->pci->subsystem_device, cx25821_boards[dev->board].name,
+ dev->board, card[dev->nr] == dev->board ?
+ "insmod option" : "autodetected");
+
+ /* init hardware */
+ cx25821_initialize(dev);
+
+ cx25821_i2c_register(&dev->i2c_bus[0]);
+/* cx25821_i2c_register(&dev->i2c_bus[1]);
+ * cx25821_i2c_register(&dev->i2c_bus[2]); */
+
+ CX25821_INFO("i2c register! bus->i2c_rc = %d\n",
+ dev->i2c_bus[0].i2c_rc);
+
+ cx25821_card_setup(dev);
+
+ if (medusa_video_init(dev) < 0)
+ CX25821_ERR("%s(): Failed to initialize medusa!\n", __func__);
+
+ cx25821_video_register(dev);
+
+ /* register IOCTL device */
+ dev->ioctl_dev = cx25821_vdev_init(dev, dev->pci,
+ &cx25821_videoioctl_template, "video");
+
+ if (video_register_device
+ (dev->ioctl_dev, VFL_TYPE_GRABBER, VIDEO_IOCTL_CH) < 0) {
+ cx25821_videoioctl_unregister(dev);
+ pr_err("%s(): Failed to register video adapter for IOCTL, so unregistering videoioctl device\n",
+ __func__);
+ }
+
+ cx25821_dev_checkrevision(dev);
+ CX25821_INFO("setup done!\n");
+
+ return 0;
+}
+
+void cx25821_start_upstream_video_ch1(struct cx25821_dev *dev,
+ struct upstream_user_struct *up_data)
+{
+ dev->_isNTSC = !strcmp(dev->vid_stdname, "NTSC") ? 1 : 0;
+
+ dev->tvnorm = !dev->_isNTSC ? V4L2_STD_PAL_BG : V4L2_STD_NTSC_M;
+ medusa_set_videostandard(dev);
+
+ cx25821_vidupstream_init_ch1(dev, dev->channel_select,
+ dev->pixel_format);
+}
+
+void cx25821_start_upstream_video_ch2(struct cx25821_dev *dev,
+ struct upstream_user_struct *up_data)
+{
+ dev->_isNTSC_ch2 = !strcmp(dev->vid_stdname_ch2, "NTSC") ? 1 : 0;
+
+ dev->tvnorm = !dev->_isNTSC_ch2 ? V4L2_STD_PAL_BG : V4L2_STD_NTSC_M;
+ medusa_set_videostandard(dev);
+
+ cx25821_vidupstream_init_ch2(dev, dev->channel_select_ch2,
+ dev->pixel_format_ch2);
+}
+
+void cx25821_start_upstream_audio(struct cx25821_dev *dev,
+ struct upstream_user_struct *up_data)
+{
+ cx25821_audio_upstream_init(dev, AUDIO_UPSTREAM_SRAM_CHANNEL_B);
+}
+
+void cx25821_dev_unregister(struct cx25821_dev *dev)
+{
+ int i;
+
+ if (!dev->base_io_addr)
+ return;
+
+ cx25821_free_mem_upstream_ch1(dev);
+ cx25821_free_mem_upstream_ch2(dev);
+ cx25821_free_mem_upstream_audio(dev);
+
+ release_mem_region(dev->base_io_addr, pci_resource_len(dev->pci, 0));
+
+ if (!atomic_dec_and_test(&dev->refcount))
+ return;
+
+ for (i = 0; i < VID_CHANNEL_NUM; i++)
+ cx25821_video_unregister(dev, i);
+
+ for (i = VID_UPSTREAM_SRAM_CHANNEL_I;
+ i <= AUDIO_UPSTREAM_SRAM_CHANNEL_B; i++) {
+ cx25821_video_unregister(dev, i);
+ }
+
+ cx25821_videoioctl_unregister(dev);
+
+ cx25821_i2c_unregister(&dev->i2c_bus[0]);
+ cx25821_iounmap(dev);
+}
+EXPORT_SYMBOL(cx25821_dev_unregister);
+
+static __le32 *cx25821_risc_field(__le32 * rp, struct scatterlist *sglist,
+ unsigned int offset, u32 sync_line,
+ unsigned int bpl, unsigned int padding,
+ unsigned int lines)
+{
+ struct scatterlist *sg;
+ unsigned int line, todo;
+
+ /* sync instruction */
+ if (sync_line != NO_SYNC_LINE)
+ *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
+
+ /* scan lines */
+ sg = sglist;
+ for (line = 0; line < lines; line++) {
+ while (offset && offset >= sg_dma_len(sg)) {
+ offset -= sg_dma_len(sg);
+ sg++;
+ }
+ if (bpl <= sg_dma_len(sg) - offset) {
+ /* fits into current chunk */
+ *(rp++) = cpu_to_le32(RISC_WRITE | RISC_SOL | RISC_EOL |
+ bpl);
+ *(rp++) = cpu_to_le32(sg_dma_address(sg) + offset);
+ *(rp++) = cpu_to_le32(0); /* bits 63-32 */
+ offset += bpl;
+ } else {
+ /* scanline needs to be split */
+ todo = bpl;
+ *(rp++) = cpu_to_le32(RISC_WRITE | RISC_SOL |
+ (sg_dma_len(sg) - offset));
+ *(rp++) = cpu_to_le32(sg_dma_address(sg) + offset);
+ *(rp++) = cpu_to_le32(0); /* bits 63-32 */
+ todo -= (sg_dma_len(sg) - offset);
+ offset = 0;
+ sg++;
+ while (todo > sg_dma_len(sg)) {
+ *(rp++) = cpu_to_le32(RISC_WRITE |
+ sg_dma_len(sg));
+ *(rp++) = cpu_to_le32(sg_dma_address(sg));
+ *(rp++) = cpu_to_le32(0); /* bits 63-32 */
+ todo -= sg_dma_len(sg);
+ sg++;
+ }
+ *(rp++) = cpu_to_le32(RISC_WRITE | RISC_EOL | todo);
+ *(rp++) = cpu_to_le32(sg_dma_address(sg));
+ *(rp++) = cpu_to_le32(0); /* bits 63-32 */
+ offset += todo;
+ }
+
+ offset += padding;
+ }
+
+ return rp;
+}
+
+int cx25821_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+ struct scatterlist *sglist, unsigned int top_offset,
+ unsigned int bottom_offset, unsigned int bpl,
+ unsigned int padding, unsigned int lines)
+{
+ u32 instructions;
+ u32 fields;
+ __le32 *rp;
+ int rc;
+
+ fields = 0;
+ if (UNSET != top_offset)
+ fields++;
+ if (UNSET != bottom_offset)
+ fields++;
+
+ /* estimate risc mem: worst case is one write per page border +
+ one write per scan line + syncs + jump (all 2 dwords). Padding
+ can cause next bpl to start close to a page border. First DMA
+ region may be smaller than PAGE_SIZE */
+ /* write and jump need and extra dword */
+ instructions = fields * (1 + ((bpl + padding) * lines) / PAGE_SIZE +
+ lines);
+ instructions += 2;
+ rc = btcx_riscmem_alloc(pci, risc, instructions * 12);
+
+ if (rc < 0)
+ return rc;
+
+ /* write risc instructions */
+ rp = risc->cpu;
+
+ if (UNSET != top_offset) {
+ rp = cx25821_risc_field(rp, sglist, top_offset, 0, bpl, padding,
+ lines);
+ }
+
+ if (UNSET != bottom_offset) {
+ rp = cx25821_risc_field(rp, sglist, bottom_offset, 0x200, bpl,
+ padding, lines);
+ }
+
+ /* save pointer to jmp instruction address */
+ risc->jmp = rp;
+ BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size);
+
+ return 0;
+}
+
+static __le32 *cx25821_risc_field_audio(__le32 * rp, struct scatterlist *sglist,
+ unsigned int offset, u32 sync_line,
+ unsigned int bpl, unsigned int padding,
+ unsigned int lines, unsigned int lpi)
+{
+ struct scatterlist *sg;
+ unsigned int line, todo, sol;
+
+ /* sync instruction */
+ if (sync_line != NO_SYNC_LINE)
+ *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
+
+ /* scan lines */
+ sg = sglist;
+ for (line = 0; line < lines; line++) {
+ while (offset && offset >= sg_dma_len(sg)) {
+ offset -= sg_dma_len(sg);
+ sg++;
+ }
+
+ if (lpi && line > 0 && !(line % lpi))
+ sol = RISC_SOL | RISC_IRQ1 | RISC_CNT_INC;
+ else
+ sol = RISC_SOL;
+
+ if (bpl <= sg_dma_len(sg) - offset) {
+ /* fits into current chunk */
+ *(rp++) = cpu_to_le32(RISC_WRITE | sol | RISC_EOL |
+ bpl);
+ *(rp++) = cpu_to_le32(sg_dma_address(sg) + offset);
+ *(rp++) = cpu_to_le32(0); /* bits 63-32 */
+ offset += bpl;
+ } else {
+ /* scanline needs to be split */
+ todo = bpl;
+ *(rp++) = cpu_to_le32(RISC_WRITE | sol |
+ (sg_dma_len(sg) - offset));
+ *(rp++) = cpu_to_le32(sg_dma_address(sg) + offset);
+ *(rp++) = cpu_to_le32(0); /* bits 63-32 */
+ todo -= (sg_dma_len(sg) - offset);
+ offset = 0;
+ sg++;
+ while (todo > sg_dma_len(sg)) {
+ *(rp++) = cpu_to_le32(RISC_WRITE |
+ sg_dma_len(sg));
+ *(rp++) = cpu_to_le32(sg_dma_address(sg));
+ *(rp++) = cpu_to_le32(0); /* bits 63-32 */
+ todo -= sg_dma_len(sg);
+ sg++;
+ }
+ *(rp++) = cpu_to_le32(RISC_WRITE | RISC_EOL | todo);
+ *(rp++) = cpu_to_le32(sg_dma_address(sg));
+ *(rp++) = cpu_to_le32(0); /* bits 63-32 */
+ offset += todo;
+ }
+ offset += padding;
+ }
+
+ return rp;
+}
+
+int cx25821_risc_databuffer_audio(struct pci_dev *pci,
+ struct btcx_riscmem *risc,
+ struct scatterlist *sglist,
+ unsigned int bpl,
+ unsigned int lines, unsigned int lpi)
+{
+ u32 instructions;
+ __le32 *rp;
+ int rc;
+
+ /* estimate risc mem: worst case is one write per page border +
+ one write per scan line + syncs + jump (all 2 dwords). Here
+ there is no padding and no sync. First DMA region may be smaller
+ than PAGE_SIZE */
+ /* Jump and write need an extra dword */
+ instructions = 1 + (bpl * lines) / PAGE_SIZE + lines;
+ instructions += 1;
+
+ rc = btcx_riscmem_alloc(pci, risc, instructions * 12);
+ if (rc < 0)
+ return rc;
+
+ /* write risc instructions */
+ rp = risc->cpu;
+ rp = cx25821_risc_field_audio(rp, sglist, 0, NO_SYNC_LINE, bpl, 0,
+ lines, lpi);
+
+ /* save pointer to jmp instruction address */
+ risc->jmp = rp;
+ BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size);
+ return 0;
+}
+EXPORT_SYMBOL(cx25821_risc_databuffer_audio);
+
+int cx25821_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
+ u32 reg, u32 mask, u32 value)
+{
+ __le32 *rp;
+ int rc;
+
+ rc = btcx_riscmem_alloc(pci, risc, 4 * 16);
+
+ if (rc < 0)
+ return rc;
+
+ /* write risc instructions */
+ rp = risc->cpu;
+
+ *(rp++) = cpu_to_le32(RISC_WRITECR | RISC_IRQ1);
+ *(rp++) = cpu_to_le32(reg);
+ *(rp++) = cpu_to_le32(value);
+ *(rp++) = cpu_to_le32(mask);
+ *(rp++) = cpu_to_le32(RISC_JUMP);
+ *(rp++) = cpu_to_le32(risc->dma);
+ *(rp++) = cpu_to_le32(0); /* bits 63-32 */
+ return 0;
+}
+
+void cx25821_free_buffer(struct videobuf_queue *q, struct cx25821_buffer *buf)
+{
+ struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
+
+ BUG_ON(in_interrupt());
+ videobuf_waiton(q, &buf->vb, 0, 0);
+ videobuf_dma_unmap(q->dev, dma);
+ videobuf_dma_free(dma);
+ btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static irqreturn_t cx25821_irq(int irq, void *dev_id)
+{
+ struct cx25821_dev *dev = dev_id;
+ u32 pci_status, pci_mask;
+ u32 vid_status;
+ int i, handled = 0;
+ u32 mask[8] = { 1, 2, 4, 8, 16, 32, 64, 128 };
+
+ pci_status = cx_read(PCI_INT_STAT);
+ pci_mask = cx_read(PCI_INT_MSK);
+
+ if (pci_status == 0)
+ goto out;
+
+ for (i = 0; i < VID_CHANNEL_NUM; i++) {
+ if (pci_status & mask[i]) {
+ vid_status = cx_read(dev->channels[i].
+ sram_channels->int_stat);
+
+ if (vid_status)
+ handled += cx25821_video_irq(dev, i,
+ vid_status);
+
+ cx_write(PCI_INT_STAT, mask[i]);
+ }
+ }
+
+out:
+ return IRQ_RETVAL(handled);
+}
+
+void cx25821_print_irqbits(char *name, char *tag, char **strings,
+ int len, u32 bits, u32 mask)
+{
+ unsigned int i;
+
+ printk(KERN_DEBUG pr_fmt("%s: %s [0x%x]"), name, tag, bits);
+
+ for (i = 0; i < len; i++) {
+ if (!(bits & (1 << i)))
+ continue;
+ if (strings[i])
+ pr_cont(" %s", strings[i]);
+ else
+ pr_cont(" %d", i);
+ if (!(mask & (1 << i)))
+ continue;
+ pr_cont("*");
+ }
+ pr_cont("\n");
+}
+EXPORT_SYMBOL(cx25821_print_irqbits);
+
+struct cx25821_dev *cx25821_dev_get(struct pci_dev *pci)
+{
+ struct cx25821_dev *dev = pci_get_drvdata(pci);
+ return dev;
+}
+EXPORT_SYMBOL(cx25821_dev_get);
+
+static int __devinit cx25821_initdev(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_id)
+{
+ struct cx25821_dev *dev;
+ int err = 0;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (NULL == dev)
+ return -ENOMEM;
+
+ err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev);
+ if (err < 0)
+ goto fail_free;
+
+ /* pci init */
+ dev->pci = pci_dev;
+ if (pci_enable_device(pci_dev)) {
+ err = -EIO;
+
+ pr_info("pci enable failed!\n");
+
+ goto fail_unregister_device;
+ }
+
+ pr_info("Athena pci enable !\n");
+
+ err = cx25821_dev_setup(dev);
+ if (err) {
+ if (err == -EBUSY)
+ goto fail_unregister_device;
+ else
+ goto fail_unregister_pci;
+ }
+
+ /* print pci info */
+ pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev);
+ pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat);
+ pr_info("%s/0: found at %s, rev: %d, irq: %d, latency: %d, mmio: 0x%llx\n",
+ dev->name, pci_name(pci_dev), dev->pci_rev, pci_dev->irq,
+ dev->pci_lat, (unsigned long long)dev->base_io_addr);
+
+ pci_set_master(pci_dev);
+ if (!pci_dma_supported(pci_dev, 0xffffffff)) {
+ pr_err("%s/0: Oops: no 32bit PCI DMA ???\n", dev->name);
+ err = -EIO;
+ goto fail_irq;
+ }
+
+ err = request_irq(pci_dev->irq, cx25821_irq,
+ IRQF_SHARED, dev->name, dev);
+
+ if (err < 0) {
+ pr_err("%s: can't get IRQ %d\n", dev->name, pci_dev->irq);
+ goto fail_irq;
+ }
+
+ return 0;
+
+fail_irq:
+ pr_info("cx25821_initdev() can't get IRQ !\n");
+ cx25821_dev_unregister(dev);
+
+fail_unregister_pci:
+ pci_disable_device(pci_dev);
+fail_unregister_device:
+ v4l2_device_unregister(&dev->v4l2_dev);
+
+fail_free:
+ kfree(dev);
+ return err;
+}
+
+static void __devexit cx25821_finidev(struct pci_dev *pci_dev)
+{
+ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+ struct cx25821_dev *dev = get_cx25821(v4l2_dev);
+
+ cx25821_shutdown(dev);
+ pci_disable_device(pci_dev);
+
+ /* unregister stuff */
+ if (pci_dev->irq)
+ free_irq(pci_dev->irq, dev);
+
+ mutex_lock(&cx25821_devlist_mutex);
+ list_del(&dev->devlist);
+ mutex_unlock(&cx25821_devlist_mutex);
+
+ cx25821_dev_unregister(dev);
+ v4l2_device_unregister(v4l2_dev);
+ kfree(dev);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(cx25821_pci_tbl) = {
+ {
+ /* CX25821 Athena */
+ .vendor = 0x14f1,
+ .device = 0x8210,
+ .subvendor = 0x14f1,
+ .subdevice = 0x0920,
+ }, {
+ /* CX25821 No Brand */
+ .vendor = 0x14f1,
+ .device = 0x8210,
+ .subvendor = 0x0000,
+ .subdevice = 0x0000,
+ }, {
+ /* --- end of list --- */
+ }
+};
+
+MODULE_DEVICE_TABLE(pci, cx25821_pci_tbl);
+
+static struct pci_driver cx25821_pci_driver = {
+ .name = "cx25821",
+ .id_table = cx25821_pci_tbl,
+ .probe = cx25821_initdev,
+ .remove = __devexit_p(cx25821_finidev),
+ /* TODO */
+ .suspend = NULL,
+ .resume = NULL,
+};
+
+static int __init cx25821_init(void)
+{
+ pr_info("driver version %d.%d.%d loaded\n",
+ (CX25821_VERSION_CODE >> 16) & 0xff,
+ (CX25821_VERSION_CODE >> 8) & 0xff,
+ CX25821_VERSION_CODE & 0xff);
+ return pci_register_driver(&cx25821_pci_driver);
+}
+
+static void __exit cx25821_fini(void)
+{
+ pci_unregister_driver(&cx25821_pci_driver);
+}
+
+module_init(cx25821_init);
+module_exit(cx25821_fini);
diff --git a/drivers/media/video/cx25821/cx25821-gpio.c b/drivers/media/video/cx25821/cx25821-gpio.c
new file mode 100644
index 00000000..29e43b03
--- /dev/null
+++ b/drivers/media/video/cx25821/cx25821-gpio.c
@@ -0,0 +1,98 @@
+/*
+ * Driver for the Conexant CX25821 PCIe bridge
+ *
+ * Copyright (C) 2009 Conexant Systems Inc.
+ * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "cx25821.h"
+
+/********************* GPIO stuffs *********************/
+void cx25821_set_gpiopin_direction(struct cx25821_dev *dev,
+ int pin_number, int pin_logic_value)
+{
+ int bit = pin_number;
+ u32 gpio_oe_reg = GPIO_LO_OE;
+ u32 gpio_register = 0;
+ u32 value = 0;
+
+ /* Check for valid pinNumber */
+ if (pin_number >= 47)
+ return;
+
+ if (pin_number > 31) {
+ bit = pin_number - 31;
+ gpio_oe_reg = GPIO_HI_OE;
+ }
+ /* Here we will make sure that the GPIOs 0 and 1 are output. keep the
+ * rest as is */
+ gpio_register = cx_read(gpio_oe_reg);
+
+ if (pin_logic_value == 1)
+ value = gpio_register | Set_GPIO_Bit(bit);
+ else
+ value = gpio_register & Clear_GPIO_Bit(bit);
+
+ cx_write(gpio_oe_reg, value);
+}
+EXPORT_SYMBOL(cx25821_set_gpiopin_direction);
+
+static void cx25821_set_gpiopin_logicvalue(struct cx25821_dev *dev,
+ int pin_number, int pin_logic_value)
+{
+ int bit = pin_number;
+ u32 gpio_reg = GPIO_LO;
+ u32 value = 0;
+
+ /* Check for valid pinNumber */
+ if (pin_number >= 47)
+ return;
+
+ /* change to output direction */
+ cx25821_set_gpiopin_direction(dev, pin_number, 0);
+
+ if (pin_number > 31) {
+ bit = pin_number - 31;
+ gpio_reg = GPIO_HI;
+ }
+
+ value = cx_read(gpio_reg);
+
+ if (pin_logic_value == 0)
+ value &= Clear_GPIO_Bit(bit);
+ else
+ value |= Set_GPIO_Bit(bit);
+
+ cx_write(gpio_reg, value);
+}
+
+void cx25821_gpio_init(struct cx25821_dev *dev)
+{
+ if (dev == NULL)
+ return;
+
+ switch (dev->board) {
+ case CX25821_BOARD_CONEXANT_ATHENA10:
+ default:
+ /* set GPIO 5 to select the path for Medusa/Athena */
+ cx25821_set_gpiopin_logicvalue(dev, 5, 1);
+ mdelay(20);
+ break;
+ }
+
+}
diff --git a/drivers/media/video/cx25821/cx25821-i2c.c b/drivers/media/video/cx25821/cx25821-i2c.c
new file mode 100644
index 00000000..12d7300f
--- /dev/null
+++ b/drivers/media/video/cx25821/cx25821-i2c.c
@@ -0,0 +1,423 @@
+/*
+ * Driver for the Conexant CX25821 PCIe bridge
+ *
+ * Copyright (C) 2009 Conexant Systems Inc.
+ * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
+ * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "cx25821.h"
+#include <linux/i2c.h>
+
+static unsigned int i2c_debug;
+module_param(i2c_debug, int, 0644);
+MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
+
+static unsigned int i2c_scan;
+module_param(i2c_scan, int, 0444);
+MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time");
+
+#define dprintk(level, fmt, arg...) \
+do { \
+ if (i2c_debug >= level) \
+ printk(KERN_DEBUG "%s/0: " fmt, dev->name, ##arg); \
+} while (0)
+
+#define I2C_WAIT_DELAY 32
+#define I2C_WAIT_RETRY 64
+
+#define I2C_EXTEND (1 << 3)
+#define I2C_NOSTOP (1 << 4)
+
+static inline int i2c_slave_did_ack(struct i2c_adapter *i2c_adap)
+{
+ struct cx25821_i2c *bus = i2c_adap->algo_data;
+ struct cx25821_dev *dev = bus->dev;
+ return cx_read(bus->reg_stat) & 0x01;
+}
+
+static inline int i2c_is_busy(struct i2c_adapter *i2c_adap)
+{
+ struct cx25821_i2c *bus = i2c_adap->algo_data;
+ struct cx25821_dev *dev = bus->dev;
+ return cx_read(bus->reg_stat) & 0x02 ? 1 : 0;
+}
+
+static int i2c_wait_done(struct i2c_adapter *i2c_adap)
+{
+ int count;
+
+ for (count = 0; count < I2C_WAIT_RETRY; count++) {
+ if (!i2c_is_busy(i2c_adap))
+ break;
+ udelay(I2C_WAIT_DELAY);
+ }
+
+ if (I2C_WAIT_RETRY == count)
+ return 0;
+
+ return 1;
+}
+
+static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
+ const struct i2c_msg *msg, int joined_rlen)
+{
+ struct cx25821_i2c *bus = i2c_adap->algo_data;
+ struct cx25821_dev *dev = bus->dev;
+ u32 wdata, addr, ctrl;
+ int retval, cnt;
+
+ if (joined_rlen)
+ dprintk(1, "%s(msg->wlen=%d, nextmsg->rlen=%d)\n", __func__,
+ msg->len, joined_rlen);
+ else
+ dprintk(1, "%s(msg->len=%d)\n", __func__, msg->len);
+
+ /* Deal with i2c probe functions with zero payload */
+ if (msg->len == 0) {
+ cx_write(bus->reg_addr, msg->addr << 25);
+ cx_write(bus->reg_ctrl, bus->i2c_period | (1 << 2));
+
+ if (!i2c_wait_done(i2c_adap))
+ return -EIO;
+
+ if (!i2c_slave_did_ack(i2c_adap))
+ return -EIO;
+
+ dprintk(1, "%s(): returns 0\n", __func__);
+ return 0;
+ }
+
+ /* dev, reg + first byte */
+ addr = (msg->addr << 25) | msg->buf[0];
+ wdata = msg->buf[0];
+
+ ctrl = bus->i2c_period | (1 << 12) | (1 << 2);
+
+ if (msg->len > 1)
+ ctrl |= I2C_NOSTOP | I2C_EXTEND;
+ else if (joined_rlen)
+ ctrl |= I2C_NOSTOP;
+
+ cx_write(bus->reg_addr, addr);
+ cx_write(bus->reg_wdata, wdata);
+ cx_write(bus->reg_ctrl, ctrl);
+
+ retval = i2c_wait_done(i2c_adap);
+ if (retval < 0)
+ goto err;
+
+ if (retval == 0)
+ goto eio;
+
+ if (i2c_debug) {
+ if (!(ctrl & I2C_NOSTOP))
+ printk(" >\n");
+ }
+
+ for (cnt = 1; cnt < msg->len; cnt++) {
+ /* following bytes */
+ wdata = msg->buf[cnt];
+ ctrl = bus->i2c_period | (1 << 12) | (1 << 2);
+
+ if (cnt < msg->len - 1)
+ ctrl |= I2C_NOSTOP | I2C_EXTEND;
+ else if (joined_rlen)
+ ctrl |= I2C_NOSTOP;
+
+ cx_write(bus->reg_addr, addr);
+ cx_write(bus->reg_wdata, wdata);
+ cx_write(bus->reg_ctrl, ctrl);
+
+ retval = i2c_wait_done(i2c_adap);
+ if (retval < 0)
+ goto err;
+
+ if (retval == 0)
+ goto eio;
+
+ if (i2c_debug) {
+ dprintk(1, " %02x", msg->buf[cnt]);
+ if (!(ctrl & I2C_NOSTOP))
+ dprintk(1, " >\n");
+ }
+ }
+
+ return msg->len;
+
+eio:
+ retval = -EIO;
+err:
+ if (i2c_debug)
+ pr_err(" ERR: %d\n", retval);
+ return retval;
+}
+
+static int i2c_readbytes(struct i2c_adapter *i2c_adap,
+ const struct i2c_msg *msg, int joined)
+{
+ struct cx25821_i2c *bus = i2c_adap->algo_data;
+ struct cx25821_dev *dev = bus->dev;
+ u32 ctrl, cnt;
+ int retval;
+
+ if (i2c_debug && !joined)
+ dprintk(1, "6-%s(msg->len=%d)\n", __func__, msg->len);
+
+ /* Deal with i2c probe functions with zero payload */
+ if (msg->len == 0) {
+ cx_write(bus->reg_addr, msg->addr << 25);
+ cx_write(bus->reg_ctrl, bus->i2c_period | (1 << 2) | 1);
+ if (!i2c_wait_done(i2c_adap))
+ return -EIO;
+ if (!i2c_slave_did_ack(i2c_adap))
+ return -EIO;
+
+ dprintk(1, "%s(): returns 0\n", __func__);
+ return 0;
+ }
+
+ if (i2c_debug) {
+ if (joined)
+ dprintk(1, " R");
+ else
+ dprintk(1, " <R %02x", (msg->addr << 1) + 1);
+ }
+
+ for (cnt = 0; cnt < msg->len; cnt++) {
+
+ ctrl = bus->i2c_period | (1 << 12) | (1 << 2) | 1;
+
+ if (cnt < msg->len - 1)
+ ctrl |= I2C_NOSTOP | I2C_EXTEND;
+
+ cx_write(bus->reg_addr, msg->addr << 25);
+ cx_write(bus->reg_ctrl, ctrl);
+
+ retval = i2c_wait_done(i2c_adap);
+ if (retval < 0)
+ goto err;
+ if (retval == 0)
+ goto eio;
+ msg->buf[cnt] = cx_read(bus->reg_rdata) & 0xff;
+
+ if (i2c_debug) {
+ dprintk(1, " %02x", msg->buf[cnt]);
+ if (!(ctrl & I2C_NOSTOP))
+ dprintk(1, " >\n");
+ }
+ }
+
+ return msg->len;
+eio:
+ retval = -EIO;
+err:
+ if (i2c_debug)
+ pr_err(" ERR: %d\n", retval);
+ return retval;
+}
+
+static int i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
+{
+ struct cx25821_i2c *bus = i2c_adap->algo_data;
+ struct cx25821_dev *dev = bus->dev;
+ int i, retval = 0;
+
+ dprintk(1, "%s(num = %d)\n", __func__, num);
+
+ for (i = 0; i < num; i++) {
+ dprintk(1, "%s(num = %d) addr = 0x%02x len = 0x%x\n",
+ __func__, num, msgs[i].addr, msgs[i].len);
+
+ if (msgs[i].flags & I2C_M_RD) {
+ /* read */
+ retval = i2c_readbytes(i2c_adap, &msgs[i], 0);
+ } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) &&
+ msgs[i].addr == msgs[i + 1].addr) {
+ /* write then read from same address */
+ retval = i2c_sendbytes(i2c_adap, &msgs[i],
+ msgs[i + 1].len);
+
+ if (retval < 0)
+ goto err;
+ i++;
+ retval = i2c_readbytes(i2c_adap, &msgs[i], 1);
+ } else {
+ /* write */
+ retval = i2c_sendbytes(i2c_adap, &msgs[i], 0);
+ }
+
+ if (retval < 0)
+ goto err;
+ }
+ return num;
+
+err:
+ return retval;
+}
+
+
+static u32 cx25821_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C | I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_READ_WORD_DATA | I2C_FUNC_SMBUS_WRITE_WORD_DATA;
+}
+
+static struct i2c_algorithm cx25821_i2c_algo_template = {
+ .master_xfer = i2c_xfer,
+ .functionality = cx25821_functionality,
+#ifdef NEED_ALGO_CONTROL
+ .algo_control = dummy_algo_control,
+#endif
+};
+
+static struct i2c_adapter cx25821_i2c_adap_template = {
+ .name = "cx25821",
+ .owner = THIS_MODULE,
+ .algo = &cx25821_i2c_algo_template,
+};
+
+static struct i2c_client cx25821_i2c_client_template = {
+ .name = "cx25821 internal",
+};
+
+/* init + register i2c adapter */
+int cx25821_i2c_register(struct cx25821_i2c *bus)
+{
+ struct cx25821_dev *dev = bus->dev;
+
+ dprintk(1, "%s(bus = %d)\n", __func__, bus->nr);
+
+ memcpy(&bus->i2c_adap, &cx25821_i2c_adap_template,
+ sizeof(bus->i2c_adap));
+ memcpy(&bus->i2c_algo, &cx25821_i2c_algo_template,
+ sizeof(bus->i2c_algo));
+ memcpy(&bus->i2c_client, &cx25821_i2c_client_template,
+ sizeof(bus->i2c_client));
+
+ bus->i2c_adap.dev.parent = &dev->pci->dev;
+
+ strlcpy(bus->i2c_adap.name, bus->dev->name, sizeof(bus->i2c_adap.name));
+
+ bus->i2c_algo.data = bus;
+ bus->i2c_adap.algo_data = bus;
+ i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev);
+ i2c_add_adapter(&bus->i2c_adap);
+
+ bus->i2c_client.adapter = &bus->i2c_adap;
+
+ /* set up the I2c */
+ bus->i2c_client.addr = (0x88 >> 1);
+
+ return bus->i2c_rc;
+}
+
+int cx25821_i2c_unregister(struct cx25821_i2c *bus)
+{
+ i2c_del_adapter(&bus->i2c_adap);
+ return 0;
+}
+
+void cx25821_av_clk(struct cx25821_dev *dev, int enable)
+{
+ /* write 0 to bus 2 addr 0x144 via i2x_xfer() */
+ char buffer[3];
+ struct i2c_msg msg;
+ dprintk(1, "%s(enabled = %d)\n", __func__, enable);
+
+ /* Register 0x144 */
+ buffer[0] = 0x01;
+ buffer[1] = 0x44;
+ if (enable == 1)
+ buffer[2] = 0x05;
+ else
+ buffer[2] = 0x00;
+
+ msg.addr = 0x44;
+ msg.flags = I2C_M_TEN;
+ msg.len = 3;
+ msg.buf = buffer;
+
+ i2c_xfer(&dev->i2c_bus[0].i2c_adap, &msg, 1);
+}
+
+int cx25821_i2c_read(struct cx25821_i2c *bus, u16 reg_addr, int *value)
+{
+ struct i2c_client *client = &bus->i2c_client;
+ int retval = 0;
+ int v = 0;
+ u8 addr[2] = { 0, 0 };
+ u8 buf[4] = { 0, 0, 0, 0 };
+
+ struct i2c_msg msgs[2] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = addr,
+ }, {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 4,
+ .buf = buf,
+ }
+ };
+
+ addr[0] = (reg_addr >> 8);
+ addr[1] = (reg_addr & 0xff);
+ msgs[0].addr = 0x44;
+ msgs[1].addr = 0x44;
+
+ retval = i2c_xfer(client->adapter, msgs, 2);
+
+ v = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ *value = v;
+
+ return v;
+}
+
+int cx25821_i2c_write(struct cx25821_i2c *bus, u16 reg_addr, int value)
+{
+ struct i2c_client *client = &bus->i2c_client;
+ int retval = 0;
+ u8 buf[6] = { 0, 0, 0, 0, 0, 0 };
+
+ struct i2c_msg msgs[1] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 6,
+ .buf = buf,
+ }
+ };
+
+ buf[0] = reg_addr >> 8;
+ buf[1] = reg_addr & 0xff;
+ buf[5] = (value >> 24) & 0xff;
+ buf[4] = (value >> 16) & 0xff;
+ buf[3] = (value >> 8) & 0xff;
+ buf[2] = value & 0xff;
+ client->flags = 0;
+ msgs[0].addr = 0x44;
+
+ retval = i2c_xfer(client->adapter, msgs, 1);
+
+ return retval;
+}
diff --git a/drivers/media/video/cx25821/cx25821-medusa-defines.h b/drivers/media/video/cx25821/cx25821-medusa-defines.h
new file mode 100644
index 00000000..7a9e6470
--- /dev/null
+++ b/drivers/media/video/cx25821/cx25821-medusa-defines.h
@@ -0,0 +1,42 @@
+/*
+ * Driver for the Conexant CX25821 PCIe bridge
+ *
+ * Copyright (C) 2009 Conexant Systems Inc.
+ * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _MEDUSA_DEF_H_
+#define _MEDUSA_DEF_H_
+
+/* Video decoder that we supported */
+#define VDEC_A 0
+#define VDEC_B 1
+#define VDEC_C 2
+#define VDEC_D 3
+#define VDEC_E 4
+#define VDEC_F 5
+#define VDEC_G 6
+#define VDEC_H 7
+
+/* end of display sequence */
+#define END_OF_SEQ 0xF;
+
+/* registry string size */
+#define MAX_REGISTRY_SZ 40;
+
+#endif
diff --git a/drivers/media/video/cx25821/cx25821-medusa-reg.h b/drivers/media/video/cx25821/cx25821-medusa-reg.h
new file mode 100644
index 00000000..c98ac946
--- /dev/null
+++ b/drivers/media/video/cx25821/cx25821-medusa-reg.h
@@ -0,0 +1,455 @@
+/*
+ * Driver for the Conexant CX25821 PCIe bridge
+ *
+ * Copyright (C) 2009 Conexant Systems Inc.
+ * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __MEDUSA_REGISTERS__
+#define __MEDUSA_REGISTERS__
+
+/* Serial Slave Registers */
+#define HOST_REGISTER1 0x0000
+#define HOST_REGISTER2 0x0001
+
+/* Chip Configuration Registers */
+#define CHIP_CTRL 0x0100
+#define AFE_AB_CTRL 0x0104
+#define AFE_CD_CTRL 0x0108
+#define AFE_EF_CTRL 0x010C
+#define AFE_GH_CTRL 0x0110
+#define DENC_AB_CTRL 0x0114
+#define BYP_AB_CTRL 0x0118
+#define MON_A_CTRL 0x011C
+#define DISP_SEQ_A 0x0120
+#define DISP_SEQ_B 0x0124
+#define DISP_AB_CNT 0x0128
+#define DISP_CD_CNT 0x012C
+#define DISP_EF_CNT 0x0130
+#define DISP_GH_CNT 0x0134
+#define DISP_IJ_CNT 0x0138
+#define PIN_OE_CTRL 0x013C
+#define PIN_SPD_CTRL 0x0140
+#define PIN_SPD_CTRL2 0x0144
+#define IRQ_STAT_CTRL 0x0148
+#define POWER_CTRL_AB 0x014C
+#define POWER_CTRL_CD 0x0150
+#define POWER_CTRL_EF 0x0154
+#define POWER_CTRL_GH 0x0158
+#define TUNE_CTRL 0x015C
+#define BIAS_CTRL 0x0160
+#define AFE_AB_DIAG_CTRL 0x0164
+#define AFE_CD_DIAG_CTRL 0x0168
+#define AFE_EF_DIAG_CTRL 0x016C
+#define AFE_GH_DIAG_CTRL 0x0170
+#define PLL_AB_DIAG_CTRL 0x0174
+#define PLL_CD_DIAG_CTRL 0x0178
+#define PLL_EF_DIAG_CTRL 0x017C
+#define PLL_GH_DIAG_CTRL 0x0180
+#define TEST_CTRL 0x0184
+#define BIST_STAT 0x0188
+#define BIST_STAT2 0x018C
+#define BIST_VID_PLL_AB_STAT 0x0190
+#define BIST_VID_PLL_CD_STAT 0x0194
+#define BIST_VID_PLL_EF_STAT 0x0198
+#define BIST_VID_PLL_GH_STAT 0x019C
+#define DLL_DIAG_CTRL 0x01A0
+#define DEV_CH_ID_CTRL 0x01A4
+#define ABIST_CTRL_STATUS 0x01A8
+#define ABIST_FREQ 0x01AC
+#define ABIST_GOERT_SHIFT 0x01B0
+#define ABIST_COEF12 0x01B4
+#define ABIST_COEF34 0x01B8
+#define ABIST_COEF56 0x01BC
+#define ABIST_COEF7_SNR 0x01C0
+#define ABIST_ADC_CAL 0x01C4
+#define ABIST_BIN1_VGA0 0x01C8
+#define ABIST_BIN2_VGA1 0x01CC
+#define ABIST_BIN3_VGA2 0x01D0
+#define ABIST_BIN4_VGA3 0x01D4
+#define ABIST_BIN5_VGA4 0x01D8
+#define ABIST_BIN6_VGA5 0x01DC
+#define ABIST_BIN7_VGA6 0x0x1E0
+#define ABIST_CLAMP_A 0x0x1E4
+#define ABIST_CLAMP_B 0x0x1E8
+#define ABIST_CLAMP_C 0x01EC
+#define ABIST_CLAMP_D 0x01F0
+#define ABIST_CLAMP_E 0x01F4
+#define ABIST_CLAMP_F 0x01F8
+
+/* Digital Video Encoder A Registers */
+#define DENC_A_REG_1 0x0200
+#define DENC_A_REG_2 0x0204
+#define DENC_A_REG_3 0x0208
+#define DENC_A_REG_4 0x020C
+#define DENC_A_REG_5 0x0210
+#define DENC_A_REG_6 0x0214
+#define DENC_A_REG_7 0x0218
+#define DENC_A_REG_8 0x021C
+
+/* Digital Video Encoder B Registers */
+#define DENC_B_REG_1 0x0300
+#define DENC_B_REG_2 0x0304
+#define DENC_B_REG_3 0x0308
+#define DENC_B_REG_4 0x030C
+#define DENC_B_REG_5 0x0310
+#define DENC_B_REG_6 0x0314
+#define DENC_B_REG_7 0x0318
+#define DENC_B_REG_8 0x031C
+
+/* Video Decoder A Registers */
+#define MODE_CTRL 0x1000
+#define OUT_CTRL1 0x1004
+#define OUT_CTRL_NS 0x1008
+#define GEN_STAT 0x100C
+#define INT_STAT_MASK 0x1010
+#define LUMA_CTRL 0x1014
+#define CHROMA_CTRL 0x1018
+#define CRUSH_CTRL 0x101C
+#define HORIZ_TIM_CTRL 0x1020
+#define VERT_TIM_CTRL 0x1024
+#define MISC_TIM_CTRL 0x1028
+#define FIELD_COUNT 0x102C
+#define HSCALE_CTRL 0x1030
+#define VSCALE_CTRL 0x1034
+#define MAN_VGA_CTRL 0x1038
+#define MAN_AGC_CTRL 0x103C
+#define DFE_CTRL1 0x1040
+#define DFE_CTRL2 0x1044
+#define DFE_CTRL3 0x1048
+#define PLL_CTRL 0x104C
+#define PLL_CTRL_FAST 0x1050
+#define HTL_CTRL 0x1054
+#define SRC_CFG 0x1058
+#define SC_STEP_SIZE 0x105C
+#define SC_CONVERGE_CTRL 0x1060
+#define SC_LOOP_CTRL 0x1064
+#define COMB_2D_HFS_CFG 0x1068
+#define COMB_2D_HFD_CFG 0x106C
+#define COMB_2D_LF_CFG 0x1070
+#define COMB_2D_BLEND 0x1074
+#define COMB_MISC_CTRL 0x1078
+#define COMB_FLAT_THRESH_CTRL 0x107C
+#define COMB_TEST 0x1080
+#define BP_MISC_CTRL 0x1084
+#define VCR_DET_CTRL 0x1088
+#define NOISE_DET_CTRL 0x108C
+#define COMB_FLAT_NOISE_CTRL 0x1090
+#define VERSION 0x11F8
+#define SOFT_RST_CTRL 0x11FC
+
+/* Video Decoder B Registers */
+#define VDEC_B_MODE_CTRL 0x1200
+#define VDEC_B_OUT_CTRL1 0x1204
+#define VDEC_B_OUT_CTRL_NS 0x1208
+#define VDEC_B_GEN_STAT 0x120C
+#define VDEC_B_INT_STAT_MASK 0x1210
+#define VDEC_B_LUMA_CTRL 0x1214
+#define VDEC_B_CHROMA_CTRL 0x1218
+#define VDEC_B_CRUSH_CTRL 0x121C
+#define VDEC_B_HORIZ_TIM_CTRL 0x1220
+#define VDEC_B_VERT_TIM_CTRL 0x1224
+#define VDEC_B_MISC_TIM_CTRL 0x1228
+#define VDEC_B_FIELD_COUNT 0x122C
+#define VDEC_B_HSCALE_CTRL 0x1230
+#define VDEC_B_VSCALE_CTRL 0x1234
+#define VDEC_B_MAN_VGA_CTRL 0x1238
+#define VDEC_B_MAN_AGC_CTRL 0x123C
+#define VDEC_B_DFE_CTRL1 0x1240
+#define VDEC_B_DFE_CTRL2 0x1244
+#define VDEC_B_DFE_CTRL3 0x1248
+#define VDEC_B_PLL_CTRL 0x124C
+#define VDEC_B_PLL_CTRL_FAST 0x1250
+#define VDEC_B_HTL_CTRL 0x1254
+#define VDEC_B_SRC_CFG 0x1258
+#define VDEC_B_SC_STEP_SIZE 0x125C
+#define VDEC_B_SC_CONVERGE_CTRL 0x1260
+#define VDEC_B_SC_LOOP_CTRL 0x1264
+#define VDEC_B_COMB_2D_HFS_CFG 0x1268
+#define VDEC_B_COMB_2D_HFD_CFG 0x126C
+#define VDEC_B_COMB_2D_LF_CFG 0x1270
+#define VDEC_B_COMB_2D_BLEND 0x1274
+#define VDEC_B_COMB_MISC_CTRL 0x1278
+#define VDEC_B_COMB_FLAT_THRESH_CTRL 0x127C
+#define VDEC_B_COMB_TEST 0x1280
+#define VDEC_B_BP_MISC_CTRL 0x1284
+#define VDEC_B_VCR_DET_CTRL 0x1288
+#define VDEC_B_NOISE_DET_CTRL 0x128C
+#define VDEC_B_COMB_FLAT_NOISE_CTRL 0x1290
+#define VDEC_B_VERSION 0x13F8
+#define VDEC_B_SOFT_RST_CTRL 0x13FC
+
+/* Video Decoder C Registers */
+#define VDEC_C_MODE_CTRL 0x1400
+#define VDEC_C_OUT_CTRL1 0x1404
+#define VDEC_C_OUT_CTRL_NS 0x1408
+#define VDEC_C_GEN_STAT 0x140C
+#define VDEC_C_INT_STAT_MASK 0x1410
+#define VDEC_C_LUMA_CTRL 0x1414
+#define VDEC_C_CHROMA_CTRL 0x1418
+#define VDEC_C_CRUSH_CTRL 0x141C
+#define VDEC_C_HORIZ_TIM_CTRL 0x1420
+#define VDEC_C_VERT_TIM_CTRL 0x1424
+#define VDEC_C_MISC_TIM_CTRL 0x1428
+#define VDEC_C_FIELD_COUNT 0x142C
+#define VDEC_C_HSCALE_CTRL 0x1430
+#define VDEC_C_VSCALE_CTRL 0x1434
+#define VDEC_C_MAN_VGA_CTRL 0x1438
+#define VDEC_C_MAN_AGC_CTRL 0x143C
+#define VDEC_C_DFE_CTRL1 0x1440
+#define VDEC_C_DFE_CTRL2 0x1444
+#define VDEC_C_DFE_CTRL3 0x1448
+#define VDEC_C_PLL_CTRL 0x144C
+#define VDEC_C_PLL_CTRL_FAST 0x1450
+#define VDEC_C_HTL_CTRL 0x1454
+#define VDEC_C_SRC_CFG 0x1458
+#define VDEC_C_SC_STEP_SIZE 0x145C
+#define VDEC_C_SC_CONVERGE_CTRL 0x1460
+#define VDEC_C_SC_LOOP_CTRL 0x1464
+#define VDEC_C_COMB_2D_HFS_CFG 0x1468
+#define VDEC_C_COMB_2D_HFD_CFG 0x146C
+#define VDEC_C_COMB_2D_LF_CFG 0x1470
+#define VDEC_C_COMB_2D_BLEND 0x1474
+#define VDEC_C_COMB_MISC_CTRL 0x1478
+#define VDEC_C_COMB_FLAT_THRESH_CTRL 0x147C
+#define VDEC_C_COMB_TEST 0x1480
+#define VDEC_C_BP_MISC_CTRL 0x1484
+#define VDEC_C_VCR_DET_CTRL 0x1488
+#define VDEC_C_NOISE_DET_CTRL 0x148C
+#define VDEC_C_COMB_FLAT_NOISE_CTRL 0x1490
+#define VDEC_C_VERSION 0x15F8
+#define VDEC_C_SOFT_RST_CTRL 0x15FC
+
+/* Video Decoder D Registers */
+#define VDEC_D_MODE_CTRL 0x1600
+#define VDEC_D_OUT_CTRL1 0x1604
+#define VDEC_D_OUT_CTRL_NS 0x1608
+#define VDEC_D_GEN_STAT 0x160C
+#define VDEC_D_INT_STAT_MASK 0x1610
+#define VDEC_D_LUMA_CTRL 0x1614
+#define VDEC_D_CHROMA_CTRL 0x1618
+#define VDEC_D_CRUSH_CTRL 0x161C
+#define VDEC_D_HORIZ_TIM_CTRL 0x1620
+#define VDEC_D_VERT_TIM_CTRL 0x1624
+#define VDEC_D_MISC_TIM_CTRL 0x1628
+#define VDEC_D_FIELD_COUNT 0x162C
+#define VDEC_D_HSCALE_CTRL 0x1630
+#define VDEC_D_VSCALE_CTRL 0x1634
+#define VDEC_D_MAN_VGA_CTRL 0x1638
+#define VDEC_D_MAN_AGC_CTRL 0x163C
+#define VDEC_D_DFE_CTRL1 0x1640
+#define VDEC_D_DFE_CTRL2 0x1644
+#define VDEC_D_DFE_CTRL3 0x1648
+#define VDEC_D_PLL_CTRL 0x164C
+#define VDEC_D_PLL_CTRL_FAST 0x1650
+#define VDEC_D_HTL_CTRL 0x1654
+#define VDEC_D_SRC_CFG 0x1658
+#define VDEC_D_SC_STEP_SIZE 0x165C
+#define VDEC_D_SC_CONVERGE_CTRL 0x1660
+#define VDEC_D_SC_LOOP_CTRL 0x1664
+#define VDEC_D_COMB_2D_HFS_CFG 0x1668
+#define VDEC_D_COMB_2D_HFD_CFG 0x166C
+#define VDEC_D_COMB_2D_LF_CFG 0x1670
+#define VDEC_D_COMB_2D_BLEND 0x1674
+#define VDEC_D_COMB_MISC_CTRL 0x1678
+#define VDEC_D_COMB_FLAT_THRESH_CTRL 0x167C
+#define VDEC_D_COMB_TEST 0x1680
+#define VDEC_D_BP_MISC_CTRL 0x1684
+#define VDEC_D_VCR_DET_CTRL 0x1688
+#define VDEC_D_NOISE_DET_CTRL 0x168C
+#define VDEC_D_COMB_FLAT_NOISE_CTRL 0x1690
+#define VDEC_D_VERSION 0x17F8
+#define VDEC_D_SOFT_RST_CTRL 0x17FC
+
+/* Video Decoder E Registers */
+#define VDEC_E_MODE_CTRL 0x1800
+#define VDEC_E_OUT_CTRL1 0x1804
+#define VDEC_E_OUT_CTRL_NS 0x1808
+#define VDEC_E_GEN_STAT 0x180C
+#define VDEC_E_INT_STAT_MASK 0x1810
+#define VDEC_E_LUMA_CTRL 0x1814
+#define VDEC_E_CHROMA_CTRL 0x1818
+#define VDEC_E_CRUSH_CTRL 0x181C
+#define VDEC_E_HORIZ_TIM_CTRL 0x1820
+#define VDEC_E_VERT_TIM_CTRL 0x1824
+#define VDEC_E_MISC_TIM_CTRL 0x1828
+#define VDEC_E_FIELD_COUNT 0x182C
+#define VDEC_E_HSCALE_CTRL 0x1830
+#define VDEC_E_VSCALE_CTRL 0x1834
+#define VDEC_E_MAN_VGA_CTRL 0x1838
+#define VDEC_E_MAN_AGC_CTRL 0x183C
+#define VDEC_E_DFE_CTRL1 0x1840
+#define VDEC_E_DFE_CTRL2 0x1844
+#define VDEC_E_DFE_CTRL3 0x1848
+#define VDEC_E_PLL_CTRL 0x184C
+#define VDEC_E_PLL_CTRL_FAST 0x1850
+#define VDEC_E_HTL_CTRL 0x1854
+#define VDEC_E_SRC_CFG 0x1858
+#define VDEC_E_SC_STEP_SIZE 0x185C
+#define VDEC_E_SC_CONVERGE_CTRL 0x1860
+#define VDEC_E_SC_LOOP_CTRL 0x1864
+#define VDEC_E_COMB_2D_HFS_CFG 0x1868
+#define VDEC_E_COMB_2D_HFD_CFG 0x186C
+#define VDEC_E_COMB_2D_LF_CFG 0x1870
+#define VDEC_E_COMB_2D_BLEND 0x1874
+#define VDEC_E_COMB_MISC_CTRL 0x1878
+#define VDEC_E_COMB_FLAT_THRESH_CTRL 0x187C
+#define VDEC_E_COMB_TEST 0x1880
+#define VDEC_E_BP_MISC_CTRL 0x1884
+#define VDEC_E_VCR_DET_CTRL 0x1888
+#define VDEC_E_NOISE_DET_CTRL 0x188C
+#define VDEC_E_COMB_FLAT_NOISE_CTRL 0x1890
+#define VDEC_E_VERSION 0x19F8
+#define VDEC_E_SOFT_RST_CTRL 0x19FC
+
+/* Video Decoder F Registers */
+#define VDEC_F_MODE_CTRL 0x1A00
+#define VDEC_F_OUT_CTRL1 0x1A04
+#define VDEC_F_OUT_CTRL_NS 0x1A08
+#define VDEC_F_GEN_STAT 0x1A0C
+#define VDEC_F_INT_STAT_MASK 0x1A10
+#define VDEC_F_LUMA_CTRL 0x1A14
+#define VDEC_F_CHROMA_CTRL 0x1A18
+#define VDEC_F_CRUSH_CTRL 0x1A1C
+#define VDEC_F_HORIZ_TIM_CTRL 0x1A20
+#define VDEC_F_VERT_TIM_CTRL 0x1A24
+#define VDEC_F_MISC_TIM_CTRL 0x1A28
+#define VDEC_F_FIELD_COUNT 0x1A2C
+#define VDEC_F_HSCALE_CTRL 0x1A30
+#define VDEC_F_VSCALE_CTRL 0x1A34
+#define VDEC_F_MAN_VGA_CTRL 0x1A38
+#define VDEC_F_MAN_AGC_CTRL 0x1A3C
+#define VDEC_F_DFE_CTRL1 0x1A40
+#define VDEC_F_DFE_CTRL2 0x1A44
+#define VDEC_F_DFE_CTRL3 0x1A48
+#define VDEC_F_PLL_CTRL 0x1A4C
+#define VDEC_F_PLL_CTRL_FAST 0x1A50
+#define VDEC_F_HTL_CTRL 0x1A54
+#define VDEC_F_SRC_CFG 0x1A58
+#define VDEC_F_SC_STEP_SIZE 0x1A5C
+#define VDEC_F_SC_CONVERGE_CTRL 0x1A60
+#define VDEC_F_SC_LOOP_CTRL 0x1A64
+#define VDEC_F_COMB_2D_HFS_CFG 0x1A68
+#define VDEC_F_COMB_2D_HFD_CFG 0x1A6C
+#define VDEC_F_COMB_2D_LF_CFG 0x1A70
+#define VDEC_F_COMB_2D_BLEND 0x1A74
+#define VDEC_F_COMB_MISC_CTRL 0x1A78
+#define VDEC_F_COMB_FLAT_THRESH_CTRL 0x1A7C
+#define VDEC_F_COMB_TEST 0x1A80
+#define VDEC_F_BP_MISC_CTRL 0x1A84
+#define VDEC_F_VCR_DET_CTRL 0x1A88
+#define VDEC_F_NOISE_DET_CTRL 0x1A8C
+#define VDEC_F_COMB_FLAT_NOISE_CTRL 0x1A90
+#define VDEC_F_VERSION 0x1BF8
+#define VDEC_F_SOFT_RST_CTRL 0x1BFC
+
+/* Video Decoder G Registers */
+#define VDEC_G_MODE_CTRL 0x1C00
+#define VDEC_G_OUT_CTRL1 0x1C04
+#define VDEC_G_OUT_CTRL_NS 0x1C08
+#define VDEC_G_GEN_STAT 0x1C0C
+#define VDEC_G_INT_STAT_MASK 0x1C10
+#define VDEC_G_LUMA_CTRL 0x1C14
+#define VDEC_G_CHROMA_CTRL 0x1C18
+#define VDEC_G_CRUSH_CTRL 0x1C1C
+#define VDEC_G_HORIZ_TIM_CTRL 0x1C20
+#define VDEC_G_VERT_TIM_CTRL 0x1C24
+#define VDEC_G_MISC_TIM_CTRL 0x1C28
+#define VDEC_G_FIELD_COUNT 0x1C2C
+#define VDEC_G_HSCALE_CTRL 0x1C30
+#define VDEC_G_VSCALE_CTRL 0x1C34
+#define VDEC_G_MAN_VGA_CTRL 0x1C38
+#define VDEC_G_MAN_AGC_CTRL 0x1C3C
+#define VDEC_G_DFE_CTRL1 0x1C40
+#define VDEC_G_DFE_CTRL2 0x1C44
+#define VDEC_G_DFE_CTRL3 0x1C48
+#define VDEC_G_PLL_CTRL 0x1C4C
+#define VDEC_G_PLL_CTRL_FAST 0x1C50
+#define VDEC_G_HTL_CTRL 0x1C54
+#define VDEC_G_SRC_CFG 0x1C58
+#define VDEC_G_SC_STEP_SIZE 0x1C5C
+#define VDEC_G_SC_CONVERGE_CTRL 0x1C60
+#define VDEC_G_SC_LOOP_CTRL 0x1C64
+#define VDEC_G_COMB_2D_HFS_CFG 0x1C68
+#define VDEC_G_COMB_2D_HFD_CFG 0x1C6C
+#define VDEC_G_COMB_2D_LF_CFG 0x1C70
+#define VDEC_G_COMB_2D_BLEND 0x1C74
+#define VDEC_G_COMB_MISC_CTRL 0x1C78
+#define VDEC_G_COMB_FLAT_THRESH_CTRL 0x1C7C
+#define VDEC_G_COMB_TEST 0x1C80
+#define VDEC_G_BP_MISC_CTRL 0x1C84
+#define VDEC_G_VCR_DET_CTRL 0x1C88
+#define VDEC_G_NOISE_DET_CTRL 0x1C8C
+#define VDEC_G_COMB_FLAT_NOISE_CTRL 0x1C90
+#define VDEC_G_VERSION 0x1DF8
+#define VDEC_G_SOFT_RST_CTRL 0x1DFC
+
+/* Video Decoder H Registers */
+#define VDEC_H_MODE_CTRL 0x1E00
+#define VDEC_H_OUT_CTRL1 0x1E04
+#define VDEC_H_OUT_CTRL_NS 0x1E08
+#define VDEC_H_GEN_STAT 0x1E0C
+#define VDEC_H_INT_STAT_MASK 0x1E1E
+#define VDEC_H_LUMA_CTRL 0x1E14
+#define VDEC_H_CHROMA_CTRL 0x1E18
+#define VDEC_H_CRUSH_CTRL 0x1E1C
+#define VDEC_H_HORIZ_TIM_CTRL 0x1E20
+#define VDEC_H_VERT_TIM_CTRL 0x1E24
+#define VDEC_H_MISC_TIM_CTRL 0x1E28
+#define VDEC_H_FIELD_COUNT 0x1E2C
+#define VDEC_H_HSCALE_CTRL 0x1E30
+#define VDEC_H_VSCALE_CTRL 0x1E34
+#define VDEC_H_MAN_VGA_CTRL 0x1E38
+#define VDEC_H_MAN_AGC_CTRL 0x1E3C
+#define VDEC_H_DFE_CTRL1 0x1E40
+#define VDEC_H_DFE_CTRL2 0x1E44
+#define VDEC_H_DFE_CTRL3 0x1E48
+#define VDEC_H_PLL_CTRL 0x1E4C
+#define VDEC_H_PLL_CTRL_FAST 0x1E50
+#define VDEC_H_HTL_CTRL 0x1E54
+#define VDEC_H_SRC_CFG 0x1E58
+#define VDEC_H_SC_STEP_SIZE 0x1E5C
+#define VDEC_H_SC_CONVERGE_CTRL 0x1E60
+#define VDEC_H_SC_LOOP_CTRL 0x1E64
+#define VDEC_H_COMB_2D_HFS_CFG 0x1E68
+#define VDEC_H_COMB_2D_HFD_CFG 0x1E6C
+#define VDEC_H_COMB_2D_LF_CFG 0x1E70
+#define VDEC_H_COMB_2D_BLEND 0x1E74
+#define VDEC_H_COMB_MISC_CTRL 0x1E78
+#define VDEC_H_COMB_FLAT_THRESH_CTRL 0x1E7C
+#define VDEC_H_COMB_TEST 0x1E80
+#define VDEC_H_BP_MISC_CTRL 0x1E84
+#define VDEC_H_VCR_DET_CTRL 0x1E88
+#define VDEC_H_NOISE_DET_CTRL 0x1E8C
+#define VDEC_H_COMB_FLAT_NOISE_CTRL 0x1E90
+#define VDEC_H_VERSION 0x1FF8
+#define VDEC_H_SOFT_RST_CTRL 0x1FFC
+
+/*****************************************************************************/
+/* LUMA_CTRL register fields */
+#define VDEC_A_BRITE_CTRL 0x1014
+#define VDEC_A_CNTRST_CTRL 0x1015
+#define VDEC_A_PEAK_SEL 0x1016
+
+/*****************************************************************************/
+/* CHROMA_CTRL register fields */
+#define VDEC_A_USAT_CTRL 0x1018
+#define VDEC_A_VSAT_CTRL 0x1019
+#define VDEC_A_HUE_CTRL 0x101A
+
+#endif
diff --git a/drivers/media/video/cx25821/cx25821-medusa-video.c b/drivers/media/video/cx25821/cx25821-medusa-video.c
new file mode 100644
index 00000000..298a68d9
--- /dev/null
+++ b/drivers/media/video/cx25821/cx25821-medusa-video.c
@@ -0,0 +1,790 @@
+/*
+ * Driver for the Conexant CX25821 PCIe bridge
+ *
+ * Copyright (C) 2009 Conexant Systems Inc.
+ * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "cx25821.h"
+#include "cx25821-medusa-video.h"
+#include "cx25821-biffuncs.h"
+
+/*
+ * medusa_enable_bluefield_output()
+ *
+ * Enable the generation of blue filed output if no video
+ *
+ */
+static void medusa_enable_bluefield_output(struct cx25821_dev *dev, int channel,
+ int enable)
+{
+ int ret_val = 1;
+ u32 value = 0;
+ u32 tmp = 0;
+ int out_ctrl = OUT_CTRL1;
+ int out_ctrl_ns = OUT_CTRL_NS;
+
+ switch (channel) {
+ default:
+ case VDEC_A:
+ break;
+ case VDEC_B:
+ out_ctrl = VDEC_B_OUT_CTRL1;
+ out_ctrl_ns = VDEC_B_OUT_CTRL_NS;
+ break;
+ case VDEC_C:
+ out_ctrl = VDEC_C_OUT_CTRL1;
+ out_ctrl_ns = VDEC_C_OUT_CTRL_NS;
+ break;
+ case VDEC_D:
+ out_ctrl = VDEC_D_OUT_CTRL1;
+ out_ctrl_ns = VDEC_D_OUT_CTRL_NS;
+ break;
+ case VDEC_E:
+ out_ctrl = VDEC_E_OUT_CTRL1;
+ out_ctrl_ns = VDEC_E_OUT_CTRL_NS;
+ return;
+ case VDEC_F:
+ out_ctrl = VDEC_F_OUT_CTRL1;
+ out_ctrl_ns = VDEC_F_OUT_CTRL_NS;
+ return;
+ case VDEC_G:
+ out_ctrl = VDEC_G_OUT_CTRL1;
+ out_ctrl_ns = VDEC_G_OUT_CTRL_NS;
+ return;
+ case VDEC_H:
+ out_ctrl = VDEC_H_OUT_CTRL1;
+ out_ctrl_ns = VDEC_H_OUT_CTRL_NS;
+ return;
+ }
+
+ value = cx25821_i2c_read(&dev->i2c_bus[0], out_ctrl, &tmp);
+ value &= 0xFFFFFF7F; /* clear BLUE_FIELD_EN */
+ if (enable)
+ value |= 0x00000080; /* set BLUE_FIELD_EN */
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl, value);
+
+ value = cx25821_i2c_read(&dev->i2c_bus[0], out_ctrl_ns, &tmp);
+ value &= 0xFFFFFF7F;
+ if (enable)
+ value |= 0x00000080; /* set BLUE_FIELD_EN */
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl_ns, value);
+}
+
+static int medusa_initialize_ntsc(struct cx25821_dev *dev)
+{
+ int ret_val = 0;
+ int i = 0;
+ u32 value = 0;
+ u32 tmp = 0;
+
+ mutex_lock(&dev->lock);
+
+ for (i = 0; i < MAX_DECODERS; i++) {
+ /* set video format NTSC-M */
+ value = cx25821_i2c_read(&dev->i2c_bus[0],
+ MODE_CTRL + (0x200 * i), &tmp);
+ value &= 0xFFFFFFF0;
+ /* enable the fast locking mode bit[16] */
+ value |= 0x10001;
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ MODE_CTRL + (0x200 * i), value);
+
+ /* resolution NTSC 720x480 */
+ value = cx25821_i2c_read(&dev->i2c_bus[0],
+ HORIZ_TIM_CTRL + (0x200 * i), &tmp);
+ value &= 0x00C00C00;
+ value |= 0x612D0074;
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ HORIZ_TIM_CTRL + (0x200 * i), value);
+
+ value = cx25821_i2c_read(&dev->i2c_bus[0],
+ VERT_TIM_CTRL + (0x200 * i), &tmp);
+ value &= 0x00C00C00;
+ value |= 0x1C1E001A; /* vblank_cnt + 2 to get camera ID */
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ VERT_TIM_CTRL + (0x200 * i), value);
+
+ /* chroma subcarrier step size */
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ SC_STEP_SIZE + (0x200 * i), 0x43E00000);
+
+ /* enable VIP optional active */
+ value = cx25821_i2c_read(&dev->i2c_bus[0],
+ OUT_CTRL_NS + (0x200 * i), &tmp);
+ value &= 0xFFFBFFFF;
+ value |= 0x00040000;
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ OUT_CTRL_NS + (0x200 * i), value);
+
+ /* enable VIP optional active (VIP_OPT_AL) for direct output. */
+ value = cx25821_i2c_read(&dev->i2c_bus[0],
+ OUT_CTRL1 + (0x200 * i), &tmp);
+ value &= 0xFFFBFFFF;
+ value |= 0x00040000;
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ OUT_CTRL1 + (0x200 * i), value);
+
+ /*
+ * clear VPRES_VERT_EN bit, fixes the chroma run away problem
+ * when the input switching rate < 16 fields
+ */
+ value = cx25821_i2c_read(&dev->i2c_bus[0],
+ MISC_TIM_CTRL + (0x200 * i), &tmp);
+ /* disable special play detection */
+ value = setBitAtPos(value, 14);
+ value = clearBitAtPos(value, 15);
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ MISC_TIM_CTRL + (0x200 * i), value);
+
+ /* set vbi_gate_en to 0 */
+ value = cx25821_i2c_read(&dev->i2c_bus[0],
+ DFE_CTRL1 + (0x200 * i), &tmp);
+ value = clearBitAtPos(value, 29);
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ DFE_CTRL1 + (0x200 * i), value);
+
+ /* Enable the generation of blue field output if no video */
+ medusa_enable_bluefield_output(dev, i, 1);
+ }
+
+ for (i = 0; i < MAX_ENCODERS; i++) {
+ /* NTSC hclock */
+ value = cx25821_i2c_read(&dev->i2c_bus[0],
+ DENC_A_REG_1 + (0x100 * i), &tmp);
+ value &= 0xF000FC00;
+ value |= 0x06B402D0;
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ DENC_A_REG_1 + (0x100 * i), value);
+
+ /* burst begin and burst end */
+ value = cx25821_i2c_read(&dev->i2c_bus[0],
+ DENC_A_REG_2 + (0x100 * i), &tmp);
+ value &= 0xFF000000;
+ value |= 0x007E9054;
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ DENC_A_REG_2 + (0x100 * i), value);
+
+ value = cx25821_i2c_read(&dev->i2c_bus[0],
+ DENC_A_REG_3 + (0x100 * i), &tmp);
+ value &= 0xFC00FE00;
+ value |= 0x00EC00F0;
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ DENC_A_REG_3 + (0x100 * i), value);
+
+ /* set NTSC vblank, no phase alternation, 7.5 IRE pedestal */
+ value = cx25821_i2c_read(&dev->i2c_bus[0],
+ DENC_A_REG_4 + (0x100 * i), &tmp);
+ value &= 0x00FCFFFF;
+ value |= 0x13020000;
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ DENC_A_REG_4 + (0x100 * i), value);
+
+ value = cx25821_i2c_read(&dev->i2c_bus[0],
+ DENC_A_REG_5 + (0x100 * i), &tmp);
+ value &= 0xFFFF0000;
+ value |= 0x0000E575;
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ DENC_A_REG_5 + (0x100 * i), value);
+
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ DENC_A_REG_6 + (0x100 * i), 0x009A89C1);
+
+ /* Subcarrier Increment */
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ DENC_A_REG_7 + (0x100 * i), 0x21F07C1F);
+ }
+
+ /* set picture resolutions */
+ /* 0 - 720 */
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], HSCALE_CTRL, 0x0);
+ /* 0 - 480 */
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], VSCALE_CTRL, 0x0);
+
+ /* set Bypass input format to NTSC 525 lines */
+ value = cx25821_i2c_read(&dev->i2c_bus[0], BYP_AB_CTRL, &tmp);
+ value |= 0x00080200;
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], BYP_AB_CTRL, value);
+
+ mutex_unlock(&dev->lock);
+
+ return ret_val;
+}
+
+static int medusa_PALCombInit(struct cx25821_dev *dev, int dec)
+{
+ int ret_val = -1;
+ u32 value = 0, tmp = 0;
+
+ /* Setup for 2D threshold */
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ COMB_2D_HFS_CFG + (0x200 * dec), 0x20002861);
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ COMB_2D_HFD_CFG + (0x200 * dec), 0x20002861);
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ COMB_2D_LF_CFG + (0x200 * dec), 0x200A1023);
+
+ /* Setup flat chroma and luma thresholds */
+ value = cx25821_i2c_read(&dev->i2c_bus[0],
+ COMB_FLAT_THRESH_CTRL + (0x200 * dec), &tmp);
+ value &= 0x06230000;
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ COMB_FLAT_THRESH_CTRL + (0x200 * dec), value);
+
+ /* set comb 2D blend */
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ COMB_2D_BLEND + (0x200 * dec), 0x210F0F0F);
+
+ /* COMB MISC CONTROL */
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ COMB_MISC_CTRL + (0x200 * dec), 0x41120A7F);
+
+ return ret_val;
+}
+
+static int medusa_initialize_pal(struct cx25821_dev *dev)
+{
+ int ret_val = 0;
+ int i = 0;
+ u32 value = 0;
+ u32 tmp = 0;
+
+ mutex_lock(&dev->lock);
+
+ for (i = 0; i < MAX_DECODERS; i++) {
+ /* set video format PAL-BDGHI */
+ value = cx25821_i2c_read(&dev->i2c_bus[0],
+ MODE_CTRL + (0x200 * i), &tmp);
+ value &= 0xFFFFFFF0;
+ /* enable the fast locking mode bit[16] */
+ value |= 0x10004;
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ MODE_CTRL + (0x200 * i), value);
+
+ /* resolution PAL 720x576 */
+ value = cx25821_i2c_read(&dev->i2c_bus[0],
+ HORIZ_TIM_CTRL + (0x200 * i), &tmp);
+ value &= 0x00C00C00;
+ value |= 0x632D007D;
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ HORIZ_TIM_CTRL + (0x200 * i), value);
+
+ /* vblank656_cnt=x26, vactive_cnt=240h, vblank_cnt=x24 */
+ value = cx25821_i2c_read(&dev->i2c_bus[0],
+ VERT_TIM_CTRL + (0x200 * i), &tmp);
+ value &= 0x00C00C00;
+ value |= 0x28240026; /* vblank_cnt + 2 to get camera ID */
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ VERT_TIM_CTRL + (0x200 * i), value);
+
+ /* chroma subcarrier step size */
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ SC_STEP_SIZE + (0x200 * i), 0x5411E2D0);
+
+ /* enable VIP optional active */
+ value = cx25821_i2c_read(&dev->i2c_bus[0],
+ OUT_CTRL_NS + (0x200 * i), &tmp);
+ value &= 0xFFFBFFFF;
+ value |= 0x00040000;
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ OUT_CTRL_NS + (0x200 * i), value);
+
+ /* enable VIP optional active (VIP_OPT_AL) for direct output. */
+ value = cx25821_i2c_read(&dev->i2c_bus[0],
+ OUT_CTRL1 + (0x200 * i), &tmp);
+ value &= 0xFFFBFFFF;
+ value |= 0x00040000;
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ OUT_CTRL1 + (0x200 * i), value);
+
+ /*
+ * clear VPRES_VERT_EN bit, fixes the chroma run away problem
+ * when the input switching rate < 16 fields
+ */
+ value = cx25821_i2c_read(&dev->i2c_bus[0],
+ MISC_TIM_CTRL + (0x200 * i), &tmp);
+ /* disable special play detection */
+ value = setBitAtPos(value, 14);
+ value = clearBitAtPos(value, 15);
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ MISC_TIM_CTRL + (0x200 * i), value);
+
+ /* set vbi_gate_en to 0 */
+ value = cx25821_i2c_read(&dev->i2c_bus[0],
+ DFE_CTRL1 + (0x200 * i), &tmp);
+ value = clearBitAtPos(value, 29);
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ DFE_CTRL1 + (0x200 * i), value);
+
+ medusa_PALCombInit(dev, i);
+
+ /* Enable the generation of blue field output if no video */
+ medusa_enable_bluefield_output(dev, i, 1);
+ }
+
+ for (i = 0; i < MAX_ENCODERS; i++) {
+ /* PAL hclock */
+ value = cx25821_i2c_read(&dev->i2c_bus[0],
+ DENC_A_REG_1 + (0x100 * i), &tmp);
+ value &= 0xF000FC00;
+ value |= 0x06C002D0;
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ DENC_A_REG_1 + (0x100 * i), value);
+
+ /* burst begin and burst end */
+ value = cx25821_i2c_read(&dev->i2c_bus[0],
+ DENC_A_REG_2 + (0x100 * i), &tmp);
+ value &= 0xFF000000;
+ value |= 0x007E9754;
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ DENC_A_REG_2 + (0x100 * i), value);
+
+ /* hblank and vactive */
+ value = cx25821_i2c_read(&dev->i2c_bus[0],
+ DENC_A_REG_3 + (0x100 * i), &tmp);
+ value &= 0xFC00FE00;
+ value |= 0x00FC0120;
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ DENC_A_REG_3 + (0x100 * i), value);
+
+ /* set PAL vblank, phase alternation, 0 IRE pedestal */
+ value = cx25821_i2c_read(&dev->i2c_bus[0],
+ DENC_A_REG_4 + (0x100 * i), &tmp);
+ value &= 0x00FCFFFF;
+ value |= 0x14010000;
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ DENC_A_REG_4 + (0x100 * i), value);
+
+ value = cx25821_i2c_read(&dev->i2c_bus[0],
+ DENC_A_REG_5 + (0x100 * i), &tmp);
+ value &= 0xFFFF0000;
+ value |= 0x0000F078;
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ DENC_A_REG_5 + (0x100 * i), value);
+
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ DENC_A_REG_6 + (0x100 * i), 0x00A493CF);
+
+ /* Subcarrier Increment */
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ DENC_A_REG_7 + (0x100 * i), 0x2A098ACB);
+ }
+
+ /* set picture resolutions */
+ /* 0 - 720 */
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], HSCALE_CTRL, 0x0);
+ /* 0 - 576 */
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], VSCALE_CTRL, 0x0);
+
+ /* set Bypass input format to PAL 625 lines */
+ value = cx25821_i2c_read(&dev->i2c_bus[0], BYP_AB_CTRL, &tmp);
+ value &= 0xFFF7FDFF;
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], BYP_AB_CTRL, value);
+
+ mutex_unlock(&dev->lock);
+
+ return ret_val;
+}
+
+int medusa_set_videostandard(struct cx25821_dev *dev)
+{
+ int status = STATUS_SUCCESS;
+ u32 value = 0, tmp = 0;
+
+ if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK)
+ status = medusa_initialize_pal(dev);
+ else
+ status = medusa_initialize_ntsc(dev);
+
+ /* Enable DENC_A output */
+ value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_A_REG_4, &tmp);
+ value = setBitAtPos(value, 4);
+ status = cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_4, value);
+
+ /* Enable DENC_B output */
+ value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_B_REG_4, &tmp);
+ value = setBitAtPos(value, 4);
+ status = cx25821_i2c_write(&dev->i2c_bus[0], DENC_B_REG_4, value);
+
+ return status;
+}
+
+void medusa_set_resolution(struct cx25821_dev *dev, int width,
+ int decoder_select)
+{
+ int decoder = 0;
+ int decoder_count = 0;
+ int ret_val = 0;
+ u32 hscale = 0x0;
+ u32 vscale = 0x0;
+ const int MAX_WIDTH = 720;
+
+ mutex_lock(&dev->lock);
+
+ /* validate the width */
+ if (width > MAX_WIDTH) {
+ pr_info("%s(): width %d > MAX_WIDTH %d ! resetting to MAX_WIDTH\n",
+ __func__, width, MAX_WIDTH);
+ width = MAX_WIDTH;
+ }
+
+ if (decoder_select <= 7 && decoder_select >= 0) {
+ decoder = decoder_select;
+ decoder_count = decoder_select + 1;
+ } else {
+ decoder = 0;
+ decoder_count = _num_decoders;
+ }
+
+ switch (width) {
+ case 320:
+ hscale = 0x13E34B;
+ vscale = 0x0;
+ break;
+
+ case 352:
+ hscale = 0x10A273;
+ vscale = 0x0;
+ break;
+
+ case 176:
+ hscale = 0x3115B2;
+ vscale = 0x1E00;
+ break;
+
+ case 160:
+ hscale = 0x378D84;
+ vscale = 0x1E00;
+ break;
+
+ default: /* 720 */
+ hscale = 0x0;
+ vscale = 0x0;
+ break;
+ }
+
+ for (; decoder < decoder_count; decoder++) {
+ /* write scaling values for each decoder */
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ HSCALE_CTRL + (0x200 * decoder), hscale);
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ VSCALE_CTRL + (0x200 * decoder), vscale);
+ }
+
+ mutex_unlock(&dev->lock);
+}
+
+static void medusa_set_decoderduration(struct cx25821_dev *dev, int decoder,
+ int duration)
+{
+ int ret_val = 0;
+ u32 fld_cnt = 0;
+ u32 tmp = 0;
+ u32 disp_cnt_reg = DISP_AB_CNT;
+
+ mutex_lock(&dev->lock);
+
+ /* no support */
+ if (decoder < VDEC_A && decoder > VDEC_H) {
+ mutex_unlock(&dev->lock);
+ return;
+ }
+
+ switch (decoder) {
+ default:
+ break;
+ case VDEC_C:
+ case VDEC_D:
+ disp_cnt_reg = DISP_CD_CNT;
+ break;
+ case VDEC_E:
+ case VDEC_F:
+ disp_cnt_reg = DISP_EF_CNT;
+ break;
+ case VDEC_G:
+ case VDEC_H:
+ disp_cnt_reg = DISP_GH_CNT;
+ break;
+ }
+
+ _display_field_cnt[decoder] = duration;
+
+ /* update hardware */
+ fld_cnt = cx25821_i2c_read(&dev->i2c_bus[0], disp_cnt_reg, &tmp);
+
+ if (!(decoder % 2)) { /* EVEN decoder */
+ fld_cnt &= 0xFFFF0000;
+ fld_cnt |= duration;
+ } else {
+ fld_cnt &= 0x0000FFFF;
+ fld_cnt |= ((u32) duration) << 16;
+ }
+
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], disp_cnt_reg, fld_cnt);
+
+ mutex_unlock(&dev->lock);
+}
+
+/* Map to Medusa register setting */
+static int mapM(int srcMin, int srcMax, int srcVal, int dstMin, int dstMax,
+ int *dstVal)
+{
+ int numerator;
+ int denominator;
+ int quotient;
+
+ if ((srcMin == srcMax) || (srcVal < srcMin) || (srcVal > srcMax))
+ return -1;
+ /*
+ * This is the overall expression used:
+ * *dstVal =
+ * (srcVal - srcMin)*(dstMax - dstMin) / (srcMax - srcMin) + dstMin;
+ * but we need to account for rounding so below we use the modulus
+ * operator to find the remainder and increment if necessary.
+ */
+ numerator = (srcVal - srcMin) * (dstMax - dstMin);
+ denominator = srcMax - srcMin;
+ quotient = numerator / denominator;
+
+ if (2 * (numerator % denominator) >= denominator)
+ quotient++;
+
+ *dstVal = quotient + dstMin;
+
+ return 0;
+}
+
+static unsigned long convert_to_twos(long numeric, unsigned long bits_len)
+{
+ unsigned char temp;
+
+ if (numeric >= 0)
+ return numeric;
+ else {
+ temp = ~(abs(numeric) & 0xFF);
+ temp += 1;
+ return temp;
+ }
+}
+
+int medusa_set_brightness(struct cx25821_dev *dev, int brightness, int decoder)
+{
+ int ret_val = 0;
+ int value = 0;
+ u32 val = 0, tmp = 0;
+
+ mutex_lock(&dev->lock);
+ if ((brightness > VIDEO_PROCAMP_MAX) ||
+ (brightness < VIDEO_PROCAMP_MIN)) {
+ mutex_unlock(&dev->lock);
+ return -1;
+ }
+ ret_val = mapM(VIDEO_PROCAMP_MIN, VIDEO_PROCAMP_MAX, brightness,
+ SIGNED_BYTE_MIN, SIGNED_BYTE_MAX, &value);
+ value = convert_to_twos(value, 8);
+ val = cx25821_i2c_read(&dev->i2c_bus[0],
+ VDEC_A_BRITE_CTRL + (0x200 * decoder), &tmp);
+ val &= 0xFFFFFF00;
+ ret_val |= cx25821_i2c_write(&dev->i2c_bus[0],
+ VDEC_A_BRITE_CTRL + (0x200 * decoder), val | value);
+ mutex_unlock(&dev->lock);
+ return ret_val;
+}
+
+int medusa_set_contrast(struct cx25821_dev *dev, int contrast, int decoder)
+{
+ int ret_val = 0;
+ int value = 0;
+ u32 val = 0, tmp = 0;
+
+ mutex_lock(&dev->lock);
+
+ if ((contrast > VIDEO_PROCAMP_MAX) || (contrast < VIDEO_PROCAMP_MIN)) {
+ mutex_unlock(&dev->lock);
+ return -1;
+ }
+
+ ret_val = mapM(VIDEO_PROCAMP_MIN, VIDEO_PROCAMP_MAX, contrast,
+ UNSIGNED_BYTE_MIN, UNSIGNED_BYTE_MAX, &value);
+ val = cx25821_i2c_read(&dev->i2c_bus[0],
+ VDEC_A_CNTRST_CTRL + (0x200 * decoder), &tmp);
+ val &= 0xFFFFFF00;
+ ret_val |= cx25821_i2c_write(&dev->i2c_bus[0],
+ VDEC_A_CNTRST_CTRL + (0x200 * decoder), val | value);
+
+ mutex_unlock(&dev->lock);
+ return ret_val;
+}
+
+int medusa_set_hue(struct cx25821_dev *dev, int hue, int decoder)
+{
+ int ret_val = 0;
+ int value = 0;
+ u32 val = 0, tmp = 0;
+
+ mutex_lock(&dev->lock);
+
+ if ((hue > VIDEO_PROCAMP_MAX) || (hue < VIDEO_PROCAMP_MIN)) {
+ mutex_unlock(&dev->lock);
+ return -1;
+ }
+
+ ret_val = mapM(VIDEO_PROCAMP_MIN, VIDEO_PROCAMP_MAX, hue,
+ SIGNED_BYTE_MIN, SIGNED_BYTE_MAX, &value);
+
+ value = convert_to_twos(value, 8);
+ val = cx25821_i2c_read(&dev->i2c_bus[0],
+ VDEC_A_HUE_CTRL + (0x200 * decoder), &tmp);
+ val &= 0xFFFFFF00;
+
+ ret_val |= cx25821_i2c_write(&dev->i2c_bus[0],
+ VDEC_A_HUE_CTRL + (0x200 * decoder), val | value);
+
+ mutex_unlock(&dev->lock);
+ return ret_val;
+}
+
+int medusa_set_saturation(struct cx25821_dev *dev, int saturation, int decoder)
+{
+ int ret_val = 0;
+ int value = 0;
+ u32 val = 0, tmp = 0;
+
+ mutex_lock(&dev->lock);
+
+ if ((saturation > VIDEO_PROCAMP_MAX) ||
+ (saturation < VIDEO_PROCAMP_MIN)) {
+ mutex_unlock(&dev->lock);
+ return -1;
+ }
+
+ ret_val = mapM(VIDEO_PROCAMP_MIN, VIDEO_PROCAMP_MAX, saturation,
+ UNSIGNED_BYTE_MIN, UNSIGNED_BYTE_MAX, &value);
+
+ val = cx25821_i2c_read(&dev->i2c_bus[0],
+ VDEC_A_USAT_CTRL + (0x200 * decoder), &tmp);
+ val &= 0xFFFFFF00;
+ ret_val |= cx25821_i2c_write(&dev->i2c_bus[0],
+ VDEC_A_USAT_CTRL + (0x200 * decoder), val | value);
+
+ val = cx25821_i2c_read(&dev->i2c_bus[0],
+ VDEC_A_VSAT_CTRL + (0x200 * decoder), &tmp);
+ val &= 0xFFFFFF00;
+ ret_val |= cx25821_i2c_write(&dev->i2c_bus[0],
+ VDEC_A_VSAT_CTRL + (0x200 * decoder), val | value);
+
+ mutex_unlock(&dev->lock);
+ return ret_val;
+}
+
+/* Program the display sequence and monitor output. */
+
+int medusa_video_init(struct cx25821_dev *dev)
+{
+ u32 value = 0, tmp = 0;
+ int ret_val = 0;
+ int i = 0;
+
+ mutex_lock(&dev->lock);
+
+ _num_decoders = dev->_max_num_decoders;
+
+ /* disable Auto source selection on all video decoders */
+ value = cx25821_i2c_read(&dev->i2c_bus[0], MON_A_CTRL, &tmp);
+ value &= 0xFFFFF0FF;
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], MON_A_CTRL, value);
+
+ if (ret_val < 0)
+ goto error;
+
+ /* Turn off Master source switch enable */
+ value = cx25821_i2c_read(&dev->i2c_bus[0], MON_A_CTRL, &tmp);
+ value &= 0xFFFFFFDF;
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], MON_A_CTRL, value);
+
+ if (ret_val < 0)
+ goto error;
+
+ mutex_unlock(&dev->lock);
+
+ for (i = 0; i < _num_decoders; i++)
+ medusa_set_decoderduration(dev, i, _display_field_cnt[i]);
+
+ mutex_lock(&dev->lock);
+
+ /* Select monitor as DENC A input, power up the DAC */
+ value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_AB_CTRL, &tmp);
+ value &= 0xFF70FF70;
+ value |= 0x00090008; /* set en_active */
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], DENC_AB_CTRL, value);
+
+ if (ret_val < 0)
+ goto error;
+
+ /* enable input is VIP/656 */
+ value = cx25821_i2c_read(&dev->i2c_bus[0], BYP_AB_CTRL, &tmp);
+ value |= 0x00040100; /* enable VIP */
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], BYP_AB_CTRL, value);
+
+ if (ret_val < 0)
+ goto error;
+
+ /* select AFE clock to output mode */
+ value = cx25821_i2c_read(&dev->i2c_bus[0], AFE_AB_DIAG_CTRL, &tmp);
+ value &= 0x83FFFFFF;
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], AFE_AB_DIAG_CTRL,
+ value | 0x10000000);
+
+ if (ret_val < 0)
+ goto error;
+
+ /* Turn on all of the data out and control output pins. */
+ value = cx25821_i2c_read(&dev->i2c_bus[0], PIN_OE_CTRL, &tmp);
+ value &= 0xFEF0FE00;
+ if (_num_decoders == MAX_DECODERS) {
+ /*
+ * Note: The octal board does not support control pins(bit16-19)
+ * These bits are ignored in the octal board.
+ *
+ * disable VDEC A-C port, default to Mobilygen Interface
+ */
+ value |= 0x010001F8;
+ } else {
+ /* disable VDEC A-C port, default to Mobilygen Interface */
+ value |= 0x010F0108;
+ }
+
+ value |= 7;
+ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], PIN_OE_CTRL, value);
+
+ if (ret_val < 0)
+ goto error;
+
+
+ mutex_unlock(&dev->lock);
+
+ ret_val = medusa_set_videostandard(dev);
+
+ return ret_val;
+
+error:
+ mutex_unlock(&dev->lock);
+ return ret_val;
+}
diff --git a/drivers/media/video/cx25821/cx25821-medusa-video.h b/drivers/media/video/cx25821/cx25821-medusa-video.h
new file mode 100644
index 00000000..6175e096
--- /dev/null
+++ b/drivers/media/video/cx25821/cx25821-medusa-video.h
@@ -0,0 +1,49 @@
+/*
+ * Driver for the Conexant CX25821 PCIe bridge
+ *
+ * Copyright (C) 2009 Conexant Systems Inc.
+ * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _MEDUSA_VIDEO_H
+#define _MEDUSA_VIDEO_H
+
+#include "cx25821-medusa-defines.h"
+
+/* Color control constants */
+#define VIDEO_PROCAMP_MIN 0
+#define VIDEO_PROCAMP_MAX 10000
+#define UNSIGNED_BYTE_MIN 0
+#define UNSIGNED_BYTE_MAX 0xFF
+#define SIGNED_BYTE_MIN -128
+#define SIGNED_BYTE_MAX 127
+
+/* Default video color settings */
+#define SHARPNESS_DEFAULT 50
+#define SATURATION_DEFAULT 5000
+#define BRIGHTNESS_DEFAULT 6200
+#define CONTRAST_DEFAULT 5000
+#define HUE_DEFAULT 5000
+
+unsigned short _num_decoders;
+unsigned short _num_cameras;
+
+unsigned int _video_standard;
+int _display_field_cnt[MAX_DECODERS];
+
+#endif
diff --git a/drivers/media/video/cx25821/cx25821-reg.h b/drivers/media/video/cx25821/cx25821-reg.h
new file mode 100644
index 00000000..a3fc25a4
--- /dev/null
+++ b/drivers/media/video/cx25821/cx25821-reg.h
@@ -0,0 +1,1592 @@
+/*
+ * Driver for the Conexant CX25821 PCIe bridge
+ *
+ * Copyright (C) 2009 Conexant Systems Inc.
+ * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __CX25821_REGISTERS__
+#define __CX25821_REGISTERS__
+
+/* Risc Instructions */
+#define RISC_CNT_INC 0x00010000
+#define RISC_CNT_RESET 0x00030000
+#define RISC_IRQ1 0x01000000
+#define RISC_IRQ2 0x02000000
+#define RISC_EOL 0x04000000
+#define RISC_SOL 0x08000000
+#define RISC_WRITE 0x10000000
+#define RISC_SKIP 0x20000000
+#define RISC_JUMP 0x70000000
+#define RISC_SYNC 0x80000000
+#define RISC_RESYNC 0x80008000
+#define RISC_READ 0x90000000
+#define RISC_WRITERM 0xB0000000
+#define RISC_WRITECM 0xC0000000
+#define RISC_WRITECR 0xD0000000
+#define RISC_WRITEC 0x50000000
+#define RISC_READC 0xA0000000
+
+#define RISC_SYNC_ODD 0x00000000
+#define RISC_SYNC_EVEN 0x00000200
+#define RISC_SYNC_ODD_VBI 0x00000006
+#define RISC_SYNC_EVEN_VBI 0x00000207
+#define RISC_NOOP 0xF0000000
+
+/*****************************************************************************
+* ASB SRAM
+ *****************************************************************************/
+#define TX_SRAM 0x000000 /* Transmit SRAM */
+
+/*****************************************************************************/
+#define RX_RAM 0x010000 /* Receive SRAM */
+
+/*****************************************************************************
+* Application Layer (AL)
+ *****************************************************************************/
+#define DEV_CNTRL2 0x040000 /* Device control */
+#define FLD_RUN_RISC 0x00000020
+
+/* ***************************************************************************** */
+#define PCI_INT_MSK 0x040010 /* PCI interrupt mask */
+#define PCI_INT_STAT 0x040014 /* PCI interrupt status */
+#define PCI_INT_MSTAT 0x040018 /* PCI interrupt masked status */
+#define FLD_HAMMERHEAD_INT (1 << 27)
+#define FLD_UART_INT (1 << 26)
+#define FLD_IRQN_INT (1 << 25)
+#define FLD_TM_INT (1 << 28)
+#define FLD_I2C_3_RACK (1 << 27)
+#define FLD_I2C_3_INT (1 << 26)
+#define FLD_I2C_2_RACK (1 << 25)
+#define FLD_I2C_2_INT (1 << 24)
+#define FLD_I2C_1_RACK (1 << 23)
+#define FLD_I2C_1_INT (1 << 22)
+
+#define FLD_APB_DMA_BERR_INT (1 << 21)
+#define FLD_AL_WR_BERR_INT (1 << 20)
+#define FLD_AL_RD_BERR_INT (1 << 19)
+#define FLD_RISC_WR_BERR_INT (1 << 18)
+#define FLD_RISC_RD_BERR_INT (1 << 17)
+
+#define FLD_VID_I_INT (1 << 8)
+#define FLD_VID_H_INT (1 << 7)
+#define FLD_VID_G_INT (1 << 6)
+#define FLD_VID_F_INT (1 << 5)
+#define FLD_VID_E_INT (1 << 4)
+#define FLD_VID_D_INT (1 << 3)
+#define FLD_VID_C_INT (1 << 2)
+#define FLD_VID_B_INT (1 << 1)
+#define FLD_VID_A_INT (1 << 0)
+
+/* ***************************************************************************** */
+#define VID_A_INT_MSK 0x040020 /* Video A interrupt mask */
+#define VID_A_INT_STAT 0x040024 /* Video A interrupt status */
+#define VID_A_INT_MSTAT 0x040028 /* Video A interrupt masked status */
+#define VID_A_INT_SSTAT 0x04002C /* Video A interrupt set status */
+
+/* ***************************************************************************** */
+#define VID_B_INT_MSK 0x040030 /* Video B interrupt mask */
+#define VID_B_INT_STAT 0x040034 /* Video B interrupt status */
+#define VID_B_INT_MSTAT 0x040038 /* Video B interrupt masked status */
+#define VID_B_INT_SSTAT 0x04003C /* Video B interrupt set status */
+
+/* ***************************************************************************** */
+#define VID_C_INT_MSK 0x040040 /* Video C interrupt mask */
+#define VID_C_INT_STAT 0x040044 /* Video C interrupt status */
+#define VID_C_INT_MSTAT 0x040048 /* Video C interrupt masked status */
+#define VID_C_INT_SSTAT 0x04004C /* Video C interrupt set status */
+
+/* ***************************************************************************** */
+#define VID_D_INT_MSK 0x040050 /* Video D interrupt mask */
+#define VID_D_INT_STAT 0x040054 /* Video D interrupt status */
+#define VID_D_INT_MSTAT 0x040058 /* Video D interrupt masked status */
+#define VID_D_INT_SSTAT 0x04005C /* Video D interrupt set status */
+
+/* ***************************************************************************** */
+#define VID_E_INT_MSK 0x040060 /* Video E interrupt mask */
+#define VID_E_INT_STAT 0x040064 /* Video E interrupt status */
+#define VID_E_INT_MSTAT 0x040068 /* Video E interrupt masked status */
+#define VID_E_INT_SSTAT 0x04006C /* Video E interrupt set status */
+
+/* ***************************************************************************** */
+#define VID_F_INT_MSK 0x040070 /* Video F interrupt mask */
+#define VID_F_INT_STAT 0x040074 /* Video F interrupt status */
+#define VID_F_INT_MSTAT 0x040078 /* Video F interrupt masked status */
+#define VID_F_INT_SSTAT 0x04007C /* Video F interrupt set status */
+
+/* ***************************************************************************** */
+#define VID_G_INT_MSK 0x040080 /* Video G interrupt mask */
+#define VID_G_INT_STAT 0x040084 /* Video G interrupt status */
+#define VID_G_INT_MSTAT 0x040088 /* Video G interrupt masked status */
+#define VID_G_INT_SSTAT 0x04008C /* Video G interrupt set status */
+
+/* ***************************************************************************** */
+#define VID_H_INT_MSK 0x040090 /* Video H interrupt mask */
+#define VID_H_INT_STAT 0x040094 /* Video H interrupt status */
+#define VID_H_INT_MSTAT 0x040098 /* Video H interrupt masked status */
+#define VID_H_INT_SSTAT 0x04009C /* Video H interrupt set status */
+
+/* ***************************************************************************** */
+#define VID_I_INT_MSK 0x0400A0 /* Video I interrupt mask */
+#define VID_I_INT_STAT 0x0400A4 /* Video I interrupt status */
+#define VID_I_INT_MSTAT 0x0400A8 /* Video I interrupt masked status */
+#define VID_I_INT_SSTAT 0x0400AC /* Video I interrupt set status */
+
+/* ***************************************************************************** */
+#define VID_J_INT_MSK 0x0400B0 /* Video J interrupt mask */
+#define VID_J_INT_STAT 0x0400B4 /* Video J interrupt status */
+#define VID_J_INT_MSTAT 0x0400B8 /* Video J interrupt masked status */
+#define VID_J_INT_SSTAT 0x0400BC /* Video J interrupt set status */
+
+#define FLD_VID_SRC_OPC_ERR 0x00020000
+#define FLD_VID_DST_OPC_ERR 0x00010000
+#define FLD_VID_SRC_SYNC 0x00002000
+#define FLD_VID_DST_SYNC 0x00001000
+#define FLD_VID_SRC_UF 0x00000200
+#define FLD_VID_DST_OF 0x00000100
+#define FLD_VID_SRC_RISC2 0x00000020
+#define FLD_VID_DST_RISC2 0x00000010
+#define FLD_VID_SRC_RISC1 0x00000002
+#define FLD_VID_DST_RISC1 0x00000001
+#define FLD_VID_SRC_ERRORS (FLD_VID_SRC_OPC_ERR | FLD_VID_SRC_SYNC | FLD_VID_SRC_UF)
+#define FLD_VID_DST_ERRORS (FLD_VID_DST_OPC_ERR | FLD_VID_DST_SYNC | FLD_VID_DST_OF)
+
+/* ***************************************************************************** */
+#define AUD_A_INT_MSK 0x0400C0 /* Audio Int interrupt mask */
+#define AUD_A_INT_STAT 0x0400C4 /* Audio Int interrupt status */
+#define AUD_A_INT_MSTAT 0x0400C8 /* Audio Int interrupt masked status */
+#define AUD_A_INT_SSTAT 0x0400CC /* Audio Int interrupt set status */
+
+/* ***************************************************************************** */
+#define AUD_B_INT_MSK 0x0400D0 /* Audio Int interrupt mask */
+#define AUD_B_INT_STAT 0x0400D4 /* Audio Int interrupt status */
+#define AUD_B_INT_MSTAT 0x0400D8 /* Audio Int interrupt masked status */
+#define AUD_B_INT_SSTAT 0x0400DC /* Audio Int interrupt set status */
+
+/* ***************************************************************************** */
+#define AUD_C_INT_MSK 0x0400E0 /* Audio Int interrupt mask */
+#define AUD_C_INT_STAT 0x0400E4 /* Audio Int interrupt status */
+#define AUD_C_INT_MSTAT 0x0400E8 /* Audio Int interrupt masked status */
+#define AUD_C_INT_SSTAT 0x0400EC /* Audio Int interrupt set status */
+
+/* ***************************************************************************** */
+#define AUD_D_INT_MSK 0x0400F0 /* Audio Int interrupt mask */
+#define AUD_D_INT_STAT 0x0400F4 /* Audio Int interrupt status */
+#define AUD_D_INT_MSTAT 0x0400F8 /* Audio Int interrupt masked status */
+#define AUD_D_INT_SSTAT 0x0400FC /* Audio Int interrupt set status */
+
+/* ***************************************************************************** */
+#define AUD_E_INT_MSK 0x040100 /* Audio Int interrupt mask */
+#define AUD_E_INT_STAT 0x040104 /* Audio Int interrupt status */
+#define AUD_E_INT_MSTAT 0x040108 /* Audio Int interrupt masked status */
+#define AUD_E_INT_SSTAT 0x04010C /* Audio Int interrupt set status */
+
+#define FLD_AUD_SRC_OPC_ERR 0x00020000
+#define FLD_AUD_DST_OPC_ERR 0x00010000
+#define FLD_AUD_SRC_SYNC 0x00002000
+#define FLD_AUD_DST_SYNC 0x00001000
+#define FLD_AUD_SRC_OF 0x00000200
+#define FLD_AUD_DST_OF 0x00000100
+#define FLD_AUD_SRC_RISCI2 0x00000020
+#define FLD_AUD_DST_RISCI2 0x00000010
+#define FLD_AUD_SRC_RISCI1 0x00000002
+#define FLD_AUD_DST_RISCI1 0x00000001
+
+/* ***************************************************************************** */
+#define MBIF_A_INT_MSK 0x040110 /* MBIF Int interrupt mask */
+#define MBIF_A_INT_STAT 0x040114 /* MBIF Int interrupt status */
+#define MBIF_A_INT_MSTAT 0x040118 /* MBIF Int interrupt masked status */
+#define MBIF_A_INT_SSTAT 0x04011C /* MBIF Int interrupt set status */
+
+/* ***************************************************************************** */
+#define MBIF_B_INT_MSK 0x040120 /* MBIF Int interrupt mask */
+#define MBIF_B_INT_STAT 0x040124 /* MBIF Int interrupt status */
+#define MBIF_B_INT_MSTAT 0x040128 /* MBIF Int interrupt masked status */
+#define MBIF_B_INT_SSTAT 0x04012C /* MBIF Int interrupt set status */
+
+#define FLD_MBIF_DST_OPC_ERR 0x00010000
+#define FLD_MBIF_DST_SYNC 0x00001000
+#define FLD_MBIF_DST_OF 0x00000100
+#define FLD_MBIF_DST_RISCI2 0x00000010
+#define FLD_MBIF_DST_RISCI1 0x00000001
+
+/* ***************************************************************************** */
+#define AUD_EXT_INT_MSK 0x040060 /* Audio Ext interrupt mask */
+#define AUD_EXT_INT_STAT 0x040064 /* Audio Ext interrupt status */
+#define AUD_EXT_INT_MSTAT 0x040068 /* Audio Ext interrupt masked status */
+#define AUD_EXT_INT_SSTAT 0x04006C /* Audio Ext interrupt set status */
+#define FLD_AUD_EXT_OPC_ERR 0x00010000
+#define FLD_AUD_EXT_SYNC 0x00001000
+#define FLD_AUD_EXT_OF 0x00000100
+#define FLD_AUD_EXT_RISCI2 0x00000010
+#define FLD_AUD_EXT_RISCI1 0x00000001
+
+/* ***************************************************************************** */
+#define GPIO_LO 0x110010 /* Lower of GPIO pins [31:0] */
+#define GPIO_HI 0x110014 /* Upper WORD of GPIO pins [47:31] */
+
+#define GPIO_LO_OE 0x110018 /* Lower of GPIO output enable [31:0] */
+#define GPIO_HI_OE 0x11001C /* Upper word of GPIO output enable [47:32] */
+
+#define GPIO_LO_INT_MSK 0x11003C /* GPIO interrupt mask */
+#define GPIO_LO_INT_STAT 0x110044 /* GPIO interrupt status */
+#define GPIO_LO_INT_MSTAT 0x11004C /* GPIO interrupt masked status */
+#define GPIO_LO_ISM_SNS 0x110054 /* GPIO interrupt sensitivity */
+#define GPIO_LO_ISM_POL 0x11005C /* GPIO interrupt polarity */
+
+#define GPIO_HI_INT_MSK 0x110040 /* GPIO interrupt mask */
+#define GPIO_HI_INT_STAT 0x110048 /* GPIO interrupt status */
+#define GPIO_HI_INT_MSTAT 0x110050 /* GPIO interrupt masked status */
+#define GPIO_HI_ISM_SNS 0x110058 /* GPIO interrupt sensitivity */
+#define GPIO_HI_ISM_POL 0x110060 /* GPIO interrupt polarity */
+
+#define FLD_GPIO43_INT (1 << 11)
+#define FLD_GPIO42_INT (1 << 10)
+#define FLD_GPIO41_INT (1 << 9)
+#define FLD_GPIO40_INT (1 << 8)
+
+#define FLD_GPIO9_INT (1 << 9)
+#define FLD_GPIO8_INT (1 << 8)
+#define FLD_GPIO7_INT (1 << 7)
+#define FLD_GPIO6_INT (1 << 6)
+#define FLD_GPIO5_INT (1 << 5)
+#define FLD_GPIO4_INT (1 << 4)
+#define FLD_GPIO3_INT (1 << 3)
+#define FLD_GPIO2_INT (1 << 2)
+#define FLD_GPIO1_INT (1 << 1)
+#define FLD_GPIO0_INT (1 << 0)
+
+/* ***************************************************************************** */
+#define TC_REQ 0x040090 /* Rider PCI Express traFFic class request */
+
+/* ***************************************************************************** */
+#define TC_REQ_SET 0x040094 /* Rider PCI Express traFFic class request set */
+
+/* ***************************************************************************** */
+/* Rider */
+/* ***************************************************************************** */
+
+/* PCI Compatible Header */
+/* ***************************************************************************** */
+#define RDR_CFG0 0x050000
+#define RDR_VENDOR_DEVICE_ID_CFG 0x050000
+
+/* ***************************************************************************** */
+#define RDR_CFG1 0x050004
+
+/* ***************************************************************************** */
+#define RDR_CFG2 0x050008
+
+/* ***************************************************************************** */
+#define RDR_CFG3 0x05000C
+
+/* ***************************************************************************** */
+#define RDR_CFG4 0x050010
+
+/* ***************************************************************************** */
+#define RDR_CFG5 0x050014
+
+/* ***************************************************************************** */
+#define RDR_CFG6 0x050018
+
+/* ***************************************************************************** */
+#define RDR_CFG7 0x05001C
+
+/* ***************************************************************************** */
+#define RDR_CFG8 0x050020
+
+/* ***************************************************************************** */
+#define RDR_CFG9 0x050024
+
+/* ***************************************************************************** */
+#define RDR_CFGA 0x050028
+
+/* ***************************************************************************** */
+#define RDR_CFGB 0x05002C
+#define RDR_SUSSYSTEM_ID_CFG 0x05002C
+
+/* ***************************************************************************** */
+#define RDR_CFGC 0x050030
+
+/* ***************************************************************************** */
+#define RDR_CFGD 0x050034
+
+/* ***************************************************************************** */
+#define RDR_CFGE 0x050038
+
+/* ***************************************************************************** */
+#define RDR_CFGF 0x05003C
+
+/* ***************************************************************************** */
+/* PCI-Express Capabilities */
+/* ***************************************************************************** */
+#define RDR_PECAP 0x050040
+
+/* ***************************************************************************** */
+#define RDR_PEDEVCAP 0x050044
+
+/* ***************************************************************************** */
+#define RDR_PEDEVSC 0x050048
+
+/* ***************************************************************************** */
+#define RDR_PELINKCAP 0x05004C
+
+/* ***************************************************************************** */
+#define RDR_PELINKSC 0x050050
+
+/* ***************************************************************************** */
+#define RDR_PMICAP 0x050080
+
+/* ***************************************************************************** */
+#define RDR_PMCSR 0x050084
+
+/* ***************************************************************************** */
+#define RDR_VPDCAP 0x050090
+
+/* ***************************************************************************** */
+#define RDR_VPDDATA 0x050094
+
+/* ***************************************************************************** */
+#define RDR_MSICAP 0x0500A0
+
+/* ***************************************************************************** */
+#define RDR_MSIARL 0x0500A4
+
+/* ***************************************************************************** */
+#define RDR_MSIARU 0x0500A8
+
+/* ***************************************************************************** */
+#define RDR_MSIDATA 0x0500AC
+
+/* ***************************************************************************** */
+/* PCI Express Extended Capabilities */
+/* ***************************************************************************** */
+#define RDR_AERXCAP 0x050100
+
+/* ***************************************************************************** */
+#define RDR_AERUESTA 0x050104
+
+/* ***************************************************************************** */
+#define RDR_AERUEMSK 0x050108
+
+/* ***************************************************************************** */
+#define RDR_AERUESEV 0x05010C
+
+/* ***************************************************************************** */
+#define RDR_AERCESTA 0x050110
+
+/* ***************************************************************************** */
+#define RDR_AERCEMSK 0x050114
+
+/* ***************************************************************************** */
+#define RDR_AERCC 0x050118
+
+/* ***************************************************************************** */
+#define RDR_AERHL0 0x05011C
+
+/* ***************************************************************************** */
+#define RDR_AERHL1 0x050120
+
+/* ***************************************************************************** */
+#define RDR_AERHL2 0x050124
+
+/* ***************************************************************************** */
+#define RDR_AERHL3 0x050128
+
+/* ***************************************************************************** */
+#define RDR_VCXCAP 0x050200
+
+/* ***************************************************************************** */
+#define RDR_VCCAP1 0x050204
+
+/* ***************************************************************************** */
+#define RDR_VCCAP2 0x050208
+
+/* ***************************************************************************** */
+#define RDR_VCSC 0x05020C
+
+/* ***************************************************************************** */
+#define RDR_VCR0_CAP 0x050210
+
+/* ***************************************************************************** */
+#define RDR_VCR0_CTRL 0x050214
+
+/* ***************************************************************************** */
+#define RDR_VCR0_STAT 0x050218
+
+/* ***************************************************************************** */
+#define RDR_VCR1_CAP 0x05021C
+
+/* ***************************************************************************** */
+#define RDR_VCR1_CTRL 0x050220
+
+/* ***************************************************************************** */
+#define RDR_VCR1_STAT 0x050224
+
+/* ***************************************************************************** */
+#define RDR_VCR2_CAP 0x050228
+
+/* ***************************************************************************** */
+#define RDR_VCR2_CTRL 0x05022C
+
+/* ***************************************************************************** */
+#define RDR_VCR2_STAT 0x050230
+
+/* ***************************************************************************** */
+#define RDR_VCR3_CAP 0x050234
+
+/* ***************************************************************************** */
+#define RDR_VCR3_CTRL 0x050238
+
+/* ***************************************************************************** */
+#define RDR_VCR3_STAT 0x05023C
+
+/* ***************************************************************************** */
+#define RDR_VCARB0 0x050240
+
+/* ***************************************************************************** */
+#define RDR_VCARB1 0x050244
+
+/* ***************************************************************************** */
+#define RDR_VCARB2 0x050248
+
+/* ***************************************************************************** */
+#define RDR_VCARB3 0x05024C
+
+/* ***************************************************************************** */
+#define RDR_VCARB4 0x050250
+
+/* ***************************************************************************** */
+#define RDR_VCARB5 0x050254
+
+/* ***************************************************************************** */
+#define RDR_VCARB6 0x050258
+
+/* ***************************************************************************** */
+#define RDR_VCARB7 0x05025C
+
+/* ***************************************************************************** */
+#define RDR_RDRSTAT0 0x050300
+
+/* ***************************************************************************** */
+#define RDR_RDRSTAT1 0x050304
+
+/* ***************************************************************************** */
+#define RDR_RDRCTL0 0x050308
+
+/* ***************************************************************************** */
+#define RDR_RDRCTL1 0x05030C
+
+/* ***************************************************************************** */
+/* Transaction Layer Registers */
+/* ***************************************************************************** */
+#define RDR_TLSTAT0 0x050310
+
+/* ***************************************************************************** */
+#define RDR_TLSTAT1 0x050314
+
+/* ***************************************************************************** */
+#define RDR_TLCTL0 0x050318
+#define FLD_CFG_UR_CPL_MODE 0x00000040
+#define FLD_CFG_CORR_ERR_QUITE 0x00000020
+#define FLD_CFG_RCB_CK_EN 0x00000010
+#define FLD_CFG_BNDRY_CK_EN 0x00000008
+#define FLD_CFG_BYTE_EN_CK_EN 0x00000004
+#define FLD_CFG_RELAX_ORDER_MSK 0x00000002
+#define FLD_CFG_TAG_ORDER_EN 0x00000001
+
+/* ***************************************************************************** */
+#define RDR_TLCTL1 0x05031C
+
+/* ***************************************************************************** */
+#define RDR_REQRCAL 0x050320
+
+/* ***************************************************************************** */
+#define RDR_REQRCAU 0x050324
+
+/* ***************************************************************************** */
+#define RDR_REQEPA 0x050328
+
+/* ***************************************************************************** */
+#define RDR_REQCTRL 0x05032C
+
+/* ***************************************************************************** */
+#define RDR_REQSTAT 0x050330
+
+/* ***************************************************************************** */
+#define RDR_TL_TEST 0x050334
+
+/* ***************************************************************************** */
+#define RDR_VCR01_CTL 0x050348
+
+/* ***************************************************************************** */
+#define RDR_VCR23_CTL 0x05034C
+
+/* ***************************************************************************** */
+#define RDR_RX_VCR0_FC 0x050350
+
+/* ***************************************************************************** */
+#define RDR_RX_VCR1_FC 0x050354
+
+/* ***************************************************************************** */
+#define RDR_RX_VCR2_FC 0x050358
+
+/* ***************************************************************************** */
+#define RDR_RX_VCR3_FC 0x05035C
+
+/* ***************************************************************************** */
+/* Data Link Layer Registers */
+/* ***************************************************************************** */
+#define RDR_DLLSTAT 0x050360
+
+/* ***************************************************************************** */
+#define RDR_DLLCTRL 0x050364
+
+/* ***************************************************************************** */
+#define RDR_REPLAYTO 0x050368
+
+/* ***************************************************************************** */
+#define RDR_ACKLATTO 0x05036C
+
+/* ***************************************************************************** */
+/* MAC Layer Registers */
+/* ***************************************************************************** */
+#define RDR_MACSTAT0 0x050380
+
+/* ***************************************************************************** */
+#define RDR_MACSTAT1 0x050384
+
+/* ***************************************************************************** */
+#define RDR_MACCTRL0 0x050388
+
+/* ***************************************************************************** */
+#define RDR_MACCTRL1 0x05038C
+
+/* ***************************************************************************** */
+#define RDR_MACCTRL2 0x050390
+
+/* ***************************************************************************** */
+#define RDR_MAC_LB_DATA 0x050394
+
+/* ***************************************************************************** */
+#define RDR_L0S_EXIT_LAT 0x050398
+
+/* ***************************************************************************** */
+/* DMAC */
+/* ***************************************************************************** */
+#define DMA1_PTR1 0x100000 /* DMA Current Ptr : Ch#1 */
+
+/* ***************************************************************************** */
+#define DMA2_PTR1 0x100004 /* DMA Current Ptr : Ch#2 */
+
+/* ***************************************************************************** */
+#define DMA3_PTR1 0x100008 /* DMA Current Ptr : Ch#3 */
+
+/* ***************************************************************************** */
+#define DMA4_PTR1 0x10000C /* DMA Current Ptr : Ch#4 */
+
+/* ***************************************************************************** */
+#define DMA5_PTR1 0x100010 /* DMA Current Ptr : Ch#5 */
+
+/* ***************************************************************************** */
+#define DMA6_PTR1 0x100014 /* DMA Current Ptr : Ch#6 */
+
+/* ***************************************************************************** */
+#define DMA7_PTR1 0x100018 /* DMA Current Ptr : Ch#7 */
+
+/* ***************************************************************************** */
+#define DMA8_PTR1 0x10001C /* DMA Current Ptr : Ch#8 */
+
+/* ***************************************************************************** */
+#define DMA9_PTR1 0x100020 /* DMA Current Ptr : Ch#9 */
+
+/* ***************************************************************************** */
+#define DMA10_PTR1 0x100024 /* DMA Current Ptr : Ch#10 */
+
+/* ***************************************************************************** */
+#define DMA11_PTR1 0x100028 /* DMA Current Ptr : Ch#11 */
+
+/* ***************************************************************************** */
+#define DMA12_PTR1 0x10002C /* DMA Current Ptr : Ch#12 */
+
+/* ***************************************************************************** */
+#define DMA13_PTR1 0x100030 /* DMA Current Ptr : Ch#13 */
+
+/* ***************************************************************************** */
+#define DMA14_PTR1 0x100034 /* DMA Current Ptr : Ch#14 */
+
+/* ***************************************************************************** */
+#define DMA15_PTR1 0x100038 /* DMA Current Ptr : Ch#15 */
+
+/* ***************************************************************************** */
+#define DMA16_PTR1 0x10003C /* DMA Current Ptr : Ch#16 */
+
+/* ***************************************************************************** */
+#define DMA17_PTR1 0x100040 /* DMA Current Ptr : Ch#17 */
+
+/* ***************************************************************************** */
+#define DMA18_PTR1 0x100044 /* DMA Current Ptr : Ch#18 */
+
+/* ***************************************************************************** */
+#define DMA19_PTR1 0x100048 /* DMA Current Ptr : Ch#19 */
+
+/* ***************************************************************************** */
+#define DMA20_PTR1 0x10004C /* DMA Current Ptr : Ch#20 */
+
+/* ***************************************************************************** */
+#define DMA21_PTR1 0x100050 /* DMA Current Ptr : Ch#21 */
+
+/* ***************************************************************************** */
+#define DMA22_PTR1 0x100054 /* DMA Current Ptr : Ch#22 */
+
+/* ***************************************************************************** */
+#define DMA23_PTR1 0x100058 /* DMA Current Ptr : Ch#23 */
+
+/* ***************************************************************************** */
+#define DMA24_PTR1 0x10005C /* DMA Current Ptr : Ch#24 */
+
+/* ***************************************************************************** */
+#define DMA25_PTR1 0x100060 /* DMA Current Ptr : Ch#25 */
+
+/* ***************************************************************************** */
+#define DMA26_PTR1 0x100064 /* DMA Current Ptr : Ch#26 */
+
+/* ***************************************************************************** */
+#define DMA1_PTR2 0x100080 /* DMA Tab Ptr : Ch#1 */
+
+/* ***************************************************************************** */
+#define DMA2_PTR2 0x100084 /* DMA Tab Ptr : Ch#2 */
+
+/* ***************************************************************************** */
+#define DMA3_PTR2 0x100088 /* DMA Tab Ptr : Ch#3 */
+
+/* ***************************************************************************** */
+#define DMA4_PTR2 0x10008C /* DMA Tab Ptr : Ch#4 */
+
+/* ***************************************************************************** */
+#define DMA5_PTR2 0x100090 /* DMA Tab Ptr : Ch#5 */
+
+/* ***************************************************************************** */
+#define DMA6_PTR2 0x100094 /* DMA Tab Ptr : Ch#6 */
+
+/* ***************************************************************************** */
+#define DMA7_PTR2 0x100098 /* DMA Tab Ptr : Ch#7 */
+
+/* ***************************************************************************** */
+#define DMA8_PTR2 0x10009C /* DMA Tab Ptr : Ch#8 */
+
+/* ***************************************************************************** */
+#define DMA9_PTR2 0x1000A0 /* DMA Tab Ptr : Ch#9 */
+
+/* ***************************************************************************** */
+#define DMA10_PTR2 0x1000A4 /* DMA Tab Ptr : Ch#10 */
+
+/* ***************************************************************************** */
+#define DMA11_PTR2 0x1000A8 /* DMA Tab Ptr : Ch#11 */
+
+/* ***************************************************************************** */
+#define DMA12_PTR2 0x1000AC /* DMA Tab Ptr : Ch#12 */
+
+/* ***************************************************************************** */
+#define DMA13_PTR2 0x1000B0 /* DMA Tab Ptr : Ch#13 */
+
+/* ***************************************************************************** */
+#define DMA14_PTR2 0x1000B4 /* DMA Tab Ptr : Ch#14 */
+
+/* ***************************************************************************** */
+#define DMA15_PTR2 0x1000B8 /* DMA Tab Ptr : Ch#15 */
+
+/* ***************************************************************************** */
+#define DMA16_PTR2 0x1000BC /* DMA Tab Ptr : Ch#16 */
+
+/* ***************************************************************************** */
+#define DMA17_PTR2 0x1000C0 /* DMA Tab Ptr : Ch#17 */
+
+/* ***************************************************************************** */
+#define DMA18_PTR2 0x1000C4 /* DMA Tab Ptr : Ch#18 */
+
+/* ***************************************************************************** */
+#define DMA19_PTR2 0x1000C8 /* DMA Tab Ptr : Ch#19 */
+
+/* ***************************************************************************** */
+#define DMA20_PTR2 0x1000CC /* DMA Tab Ptr : Ch#20 */
+
+/* ***************************************************************************** */
+#define DMA21_PTR2 0x1000D0 /* DMA Tab Ptr : Ch#21 */
+
+/* ***************************************************************************** */
+#define DMA22_PTR2 0x1000D4 /* DMA Tab Ptr : Ch#22 */
+
+/* ***************************************************************************** */
+#define DMA23_PTR2 0x1000D8 /* DMA Tab Ptr : Ch#23 */
+
+/* ***************************************************************************** */
+#define DMA24_PTR2 0x1000DC /* DMA Tab Ptr : Ch#24 */
+
+/* ***************************************************************************** */
+#define DMA25_PTR2 0x1000E0 /* DMA Tab Ptr : Ch#25 */
+
+/* ***************************************************************************** */
+#define DMA26_PTR2 0x1000E4 /* DMA Tab Ptr : Ch#26 */
+
+/* ***************************************************************************** */
+#define DMA1_CNT1 0x100100 /* DMA BuFFer Size : Ch#1 */
+
+/* ***************************************************************************** */
+#define DMA2_CNT1 0x100104 /* DMA BuFFer Size : Ch#2 */
+
+/* ***************************************************************************** */
+#define DMA3_CNT1 0x100108 /* DMA BuFFer Size : Ch#3 */
+
+/* ***************************************************************************** */
+#define DMA4_CNT1 0x10010C /* DMA BuFFer Size : Ch#4 */
+
+/* ***************************************************************************** */
+#define DMA5_CNT1 0x100110 /* DMA BuFFer Size : Ch#5 */
+
+/* ***************************************************************************** */
+#define DMA6_CNT1 0x100114 /* DMA BuFFer Size : Ch#6 */
+
+/* ***************************************************************************** */
+#define DMA7_CNT1 0x100118 /* DMA BuFFer Size : Ch#7 */
+
+/* ***************************************************************************** */
+#define DMA8_CNT1 0x10011C /* DMA BuFFer Size : Ch#8 */
+
+/* ***************************************************************************** */
+#define DMA9_CNT1 0x100120 /* DMA BuFFer Size : Ch#9 */
+
+/* ***************************************************************************** */
+#define DMA10_CNT1 0x100124 /* DMA BuFFer Size : Ch#10 */
+
+/* ***************************************************************************** */
+#define DMA11_CNT1 0x100128 /* DMA BuFFer Size : Ch#11 */
+
+/* ***************************************************************************** */
+#define DMA12_CNT1 0x10012C /* DMA BuFFer Size : Ch#12 */
+
+/* ***************************************************************************** */
+#define DMA13_CNT1 0x100130 /* DMA BuFFer Size : Ch#13 */
+
+/* ***************************************************************************** */
+#define DMA14_CNT1 0x100134 /* DMA BuFFer Size : Ch#14 */
+
+/* ***************************************************************************** */
+#define DMA15_CNT1 0x100138 /* DMA BuFFer Size : Ch#15 */
+
+/* ***************************************************************************** */
+#define DMA16_CNT1 0x10013C /* DMA BuFFer Size : Ch#16 */
+
+/* ***************************************************************************** */
+#define DMA17_CNT1 0x100140 /* DMA BuFFer Size : Ch#17 */
+
+/* ***************************************************************************** */
+#define DMA18_CNT1 0x100144 /* DMA BuFFer Size : Ch#18 */
+
+/* ***************************************************************************** */
+#define DMA19_CNT1 0x100148 /* DMA BuFFer Size : Ch#19 */
+
+/* ***************************************************************************** */
+#define DMA20_CNT1 0x10014C /* DMA BuFFer Size : Ch#20 */
+
+/* ***************************************************************************** */
+#define DMA21_CNT1 0x100150 /* DMA BuFFer Size : Ch#21 */
+
+/* ***************************************************************************** */
+#define DMA22_CNT1 0x100154 /* DMA BuFFer Size : Ch#22 */
+
+/* ***************************************************************************** */
+#define DMA23_CNT1 0x100158 /* DMA BuFFer Size : Ch#23 */
+
+/* ***************************************************************************** */
+#define DMA24_CNT1 0x10015C /* DMA BuFFer Size : Ch#24 */
+
+/* ***************************************************************************** */
+#define DMA25_CNT1 0x100160 /* DMA BuFFer Size : Ch#25 */
+
+/* ***************************************************************************** */
+#define DMA26_CNT1 0x100164 /* DMA BuFFer Size : Ch#26 */
+
+/* ***************************************************************************** */
+#define DMA1_CNT2 0x100180 /* DMA Table Size : Ch#1 */
+
+/* ***************************************************************************** */
+#define DMA2_CNT2 0x100184 /* DMA Table Size : Ch#2 */
+
+/* ***************************************************************************** */
+#define DMA3_CNT2 0x100188 /* DMA Table Size : Ch#3 */
+
+/* ***************************************************************************** */
+#define DMA4_CNT2 0x10018C /* DMA Table Size : Ch#4 */
+
+/* ***************************************************************************** */
+#define DMA5_CNT2 0x100190 /* DMA Table Size : Ch#5 */
+
+/* ***************************************************************************** */
+#define DMA6_CNT2 0x100194 /* DMA Table Size : Ch#6 */
+
+/* ***************************************************************************** */
+#define DMA7_CNT2 0x100198 /* DMA Table Size : Ch#7 */
+
+/* ***************************************************************************** */
+#define DMA8_CNT2 0x10019C /* DMA Table Size : Ch#8 */
+
+/* ***************************************************************************** */
+#define DMA9_CNT2 0x1001A0 /* DMA Table Size : Ch#9 */
+
+/* ***************************************************************************** */
+#define DMA10_CNT2 0x1001A4 /* DMA Table Size : Ch#10 */
+
+/* ***************************************************************************** */
+#define DMA11_CNT2 0x1001A8 /* DMA Table Size : Ch#11 */
+
+/* ***************************************************************************** */
+#define DMA12_CNT2 0x1001AC /* DMA Table Size : Ch#12 */
+
+/* ***************************************************************************** */
+#define DMA13_CNT2 0x1001B0 /* DMA Table Size : Ch#13 */
+
+/* ***************************************************************************** */
+#define DMA14_CNT2 0x1001B4 /* DMA Table Size : Ch#14 */
+
+/* ***************************************************************************** */
+#define DMA15_CNT2 0x1001B8 /* DMA Table Size : Ch#15 */
+
+/* ***************************************************************************** */
+#define DMA16_CNT2 0x1001BC /* DMA Table Size : Ch#16 */
+
+/* ***************************************************************************** */
+#define DMA17_CNT2 0x1001C0 /* DMA Table Size : Ch#17 */
+
+/* ***************************************************************************** */
+#define DMA18_CNT2 0x1001C4 /* DMA Table Size : Ch#18 */
+
+/* ***************************************************************************** */
+#define DMA19_CNT2 0x1001C8 /* DMA Table Size : Ch#19 */
+
+/* ***************************************************************************** */
+#define DMA20_CNT2 0x1001CC /* DMA Table Size : Ch#20 */
+
+/* ***************************************************************************** */
+#define DMA21_CNT2 0x1001D0 /* DMA Table Size : Ch#21 */
+
+/* ***************************************************************************** */
+#define DMA22_CNT2 0x1001D4 /* DMA Table Size : Ch#22 */
+
+/* ***************************************************************************** */
+#define DMA23_CNT2 0x1001D8 /* DMA Table Size : Ch#23 */
+
+/* ***************************************************************************** */
+#define DMA24_CNT2 0x1001DC /* DMA Table Size : Ch#24 */
+
+/* ***************************************************************************** */
+#define DMA25_CNT2 0x1001E0 /* DMA Table Size : Ch#25 */
+
+/* ***************************************************************************** */
+#define DMA26_CNT2 0x1001E4 /* DMA Table Size : Ch#26 */
+
+/* ***************************************************************************** */
+ /* ITG */
+/* ***************************************************************************** */
+#define TM_CNT_LDW 0x110000 /* Timer : Counter low */
+
+/* ***************************************************************************** */
+#define TM_CNT_UW 0x110004 /* Timer : Counter high word */
+
+/* ***************************************************************************** */
+#define TM_LMT_LDW 0x110008 /* Timer : Limit low */
+
+/* ***************************************************************************** */
+#define TM_LMT_UW 0x11000C /* Timer : Limit high word */
+
+/* ***************************************************************************** */
+#define GP0_IO 0x110010 /* GPIO output enables data I/O */
+#define FLD_GP_OE 0x00FF0000 /* GPIO: GP_OE output enable */
+#define FLD_GP_IN 0x0000FF00 /* GPIO: GP_IN status */
+#define FLD_GP_OUT 0x000000FF /* GPIO: GP_OUT control */
+
+/* ***************************************************************************** */
+#define GPIO_ISM 0x110014 /* GPIO interrupt sensitivity mode */
+#define FLD_GP_ISM_SNS 0x00000070
+#define FLD_GP_ISM_POL 0x00000007
+
+/* ***************************************************************************** */
+#define SOFT_RESET 0x11001C /* Output system reset reg */
+#define FLD_PECOS_SOFT_RESET 0x00000001
+
+/* ***************************************************************************** */
+#define MC416_RWD 0x110020 /* MC416 GPIO[18:3] pin */
+#define MC416_OEN 0x110024 /* Output enable of GPIO[18:3] */
+#define MC416_CTL 0x110028
+
+/* ***************************************************************************** */
+#define ALT_PIN_OUT_SEL 0x11002C /* Alternate GPIO output select */
+
+#define FLD_ALT_GPIO_OUT_SEL 0xF0000000
+/* 0 Disabled <-- default */
+/* 1 GPIO[0] */
+/* 2 GPIO[10] */
+/* 3 VIP_656_DATA_VAL */
+/* 4 VIP_656_DATA[0] */
+/* 5 VIP_656_CLK */
+/* 6 VIP_656_DATA_EXT[1] */
+/* 7 VIP_656_DATA_EXT[0] */
+/* 8 ATT_IF */
+
+#define FLD_AUX_PLL_CLK_ALT_SEL 0x0F000000
+/* 0 AUX_PLL_CLK<-- default */
+/* 1 GPIO[2] */
+/* 2 GPIO[10] */
+/* 3 VIP_656_DATA_VAL */
+/* 4 VIP_656_DATA[0] */
+/* 5 VIP_656_CLK */
+/* 6 VIP_656_DATA_EXT[1] */
+/* 7 VIP_656_DATA_EXT[0] */
+
+#define FLD_IR_TX_ALT_SEL 0x00F00000
+/* 0 IR_TX <-- default */
+/* 1 GPIO[1] */
+/* 2 GPIO[10] */
+/* 3 VIP_656_DATA_VAL */
+/* 4 VIP_656_DATA[0] */
+/* 5 VIP_656_CLK */
+/* 6 VIP_656_DATA_EXT[1] */
+/* 7 VIP_656_DATA_EXT[0] */
+
+#define FLD_IR_RX_ALT_SEL 0x000F0000
+/* 0 IR_RX <-- default */
+/* 1 GPIO[0] */
+/* 2 GPIO[10] */
+/* 3 VIP_656_DATA_VAL */
+/* 4 VIP_656_DATA[0] */
+/* 5 VIP_656_CLK */
+/* 6 VIP_656_DATA_EXT[1] */
+/* 7 VIP_656_DATA_EXT[0] */
+
+#define FLD_GPIO10_ALT_SEL 0x0000F000
+/* 0 GPIO[10] <-- default */
+/* 1 GPIO[0] */
+/* 2 GPIO[10] */
+/* 3 VIP_656_DATA_VAL */
+/* 4 VIP_656_DATA[0] */
+/* 5 VIP_656_CLK */
+/* 6 VIP_656_DATA_EXT[1] */
+/* 7 VIP_656_DATA_EXT[0] */
+
+#define FLD_GPIO2_ALT_SEL 0x00000F00
+/* 0 GPIO[2] <-- default */
+/* 1 GPIO[1] */
+/* 2 GPIO[10] */
+/* 3 VIP_656_DATA_VAL */
+/* 4 VIP_656_DATA[0] */
+/* 5 VIP_656_CLK */
+/* 6 VIP_656_DATA_EXT[1] */
+/* 7 VIP_656_DATA_EXT[0] */
+
+#define FLD_GPIO1_ALT_SEL 0x000000F0
+/* 0 GPIO[1] <-- default */
+/* 1 GPIO[0] */
+/* 2 GPIO[10] */
+/* 3 VIP_656_DATA_VAL */
+/* 4 VIP_656_DATA[0] */
+/* 5 VIP_656_CLK */
+/* 6 VIP_656_DATA_EXT[1] */
+/* 7 VIP_656_DATA_EXT[0] */
+
+#define FLD_GPIO0_ALT_SEL 0x0000000F
+/* 0 GPIO[0] <-- default */
+/* 1 GPIO[1] */
+/* 2 GPIO[10] */
+/* 3 VIP_656_DATA_VAL */
+/* 4 VIP_656_DATA[0] */
+/* 5 VIP_656_CLK */
+/* 6 VIP_656_DATA_EXT[1] */
+/* 7 VIP_656_DATA_EXT[0] */
+
+#define ALT_PIN_IN_SEL 0x110030 /* Alternate GPIO input select */
+
+#define FLD_GPIO10_ALT_IN_SEL 0x0000F000
+/* 0 GPIO[10] <-- default */
+/* 1 IR_RX */
+/* 2 IR_TX */
+/* 3 AUX_PLL_CLK */
+/* 4 IF_ATT_SEL */
+/* 5 GPIO[0] */
+/* 6 GPIO[1] */
+/* 7 GPIO[2] */
+
+#define FLD_GPIO2_ALT_IN_SEL 0x00000F00
+/* 0 GPIO[2] <-- default */
+/* 1 IR_RX */
+/* 2 IR_TX */
+/* 3 AUX_PLL_CLK */
+/* 4 IF_ATT_SEL */
+
+#define FLD_GPIO1_ALT_IN_SEL 0x000000F0
+/* 0 GPIO[1] <-- default */
+/* 1 IR_RX */
+/* 2 IR_TX */
+/* 3 AUX_PLL_CLK */
+/* 4 IF_ATT_SEL */
+
+#define FLD_GPIO0_ALT_IN_SEL 0x0000000F
+/* 0 GPIO[0] <-- default */
+/* 1 IR_RX */
+/* 2 IR_TX */
+/* 3 AUX_PLL_CLK */
+/* 4 IF_ATT_SEL */
+
+/* ***************************************************************************** */
+#define TEST_BUS_CTL1 0x110040 /* Test bus control register #1 */
+
+/* ***************************************************************************** */
+#define TEST_BUS_CTL2 0x110044 /* Test bus control register #2 */
+
+/* ***************************************************************************** */
+#define CLK_DELAY 0x110048 /* Clock delay */
+#define FLD_MOE_CLK_DIS 0x80000000 /* Disable MoE clock */
+
+/* ***************************************************************************** */
+#define PAD_CTRL 0x110068 /* Pad drive strength control */
+
+/* ***************************************************************************** */
+#define MBIST_CTRL 0x110050 /* SRAM memory built-in self test control */
+
+/* ***************************************************************************** */
+#define MBIST_STAT 0x110054 /* SRAM memory built-in self test status */
+
+/* ***************************************************************************** */
+/* PLL registers */
+/* ***************************************************************************** */
+#define PLL_A_INT_FRAC 0x110088
+#define PLL_A_POST_STAT_BIST 0x11008C
+#define PLL_B_INT_FRAC 0x110090
+#define PLL_B_POST_STAT_BIST 0x110094
+#define PLL_C_INT_FRAC 0x110098
+#define PLL_C_POST_STAT_BIST 0x11009C
+#define PLL_D_INT_FRAC 0x1100A0
+#define PLL_D_POST_STAT_BIST 0x1100A4
+
+#define CLK_RST 0x11002C
+#define FLD_VID_I_CLK_NOE 0x00001000
+#define FLD_VID_J_CLK_NOE 0x00002000
+#define FLD_USE_ALT_PLL_REF 0x00004000
+
+#define VID_CH_MODE_SEL 0x110078
+#define VID_CH_CLK_SEL 0x11007C
+
+/* ***************************************************************************** */
+#define VBI_A_DMA 0x130008 /* VBI A DMA data port */
+
+/* ***************************************************************************** */
+#define VID_A_VIP_CTL 0x130080 /* Video A VIP format control */
+#define FLD_VIP_MODE 0x00000001
+
+/* ***************************************************************************** */
+#define VID_A_PIXEL_FRMT 0x130084 /* Video A pixel format */
+#define FLD_VID_A_GAMMA_DIS 0x00000008
+#define FLD_VID_A_FORMAT 0x00000007
+#define FLD_VID_A_GAMMA_FACTOR 0x00000010
+
+/* ***************************************************************************** */
+#define VID_A_VBI_CTL 0x130088 /* Video A VBI miscellaneous control */
+#define FLD_VID_A_VIP_EXT 0x00000003
+
+/* ***************************************************************************** */
+#define VID_B_DMA 0x130100 /* Video B DMA data port */
+
+/* ***************************************************************************** */
+#define VBI_B_DMA 0x130108 /* VBI B DMA data port */
+
+/* ***************************************************************************** */
+#define VID_B_SRC_SEL 0x130144 /* Video B source select */
+#define FLD_VID_B_SRC_SEL 0x00000000
+
+/* ***************************************************************************** */
+#define VID_B_LNGTH 0x130150 /* Video B line length */
+#define FLD_VID_B_LN_LNGTH 0x00000FFF
+
+/* ***************************************************************************** */
+#define VID_B_VIP_CTL 0x130180 /* Video B VIP format control */
+
+/* ***************************************************************************** */
+#define VID_B_PIXEL_FRMT 0x130184 /* Video B pixel format */
+#define FLD_VID_B_GAMMA_DIS 0x00000008
+#define FLD_VID_B_FORMAT 0x00000007
+#define FLD_VID_B_GAMMA_FACTOR 0x00000010
+
+/* ***************************************************************************** */
+#define VID_C_DMA 0x130200 /* Video C DMA data port */
+
+/* ***************************************************************************** */
+#define VID_C_LNGTH 0x130250 /* Video C line length */
+#define FLD_VID_C_LN_LNGTH 0x00000FFF
+
+/* ***************************************************************************** */
+/* Video Destination Channels */
+/* ***************************************************************************** */
+
+#define VID_DST_A_GPCNT 0x130020 /* Video A general purpose counter */
+#define VID_DST_B_GPCNT 0x130120 /* Video B general purpose counter */
+#define VID_DST_C_GPCNT 0x130220 /* Video C general purpose counter */
+#define VID_DST_D_GPCNT 0x130320 /* Video D general purpose counter */
+#define VID_DST_E_GPCNT 0x130420 /* Video E general purpose counter */
+#define VID_DST_F_GPCNT 0x130520 /* Video F general purpose counter */
+#define VID_DST_G_GPCNT 0x130620 /* Video G general purpose counter */
+#define VID_DST_H_GPCNT 0x130720 /* Video H general purpose counter */
+
+/* ***************************************************************************** */
+
+#define VID_DST_A_GPCNT_CTL 0x130030 /* Video A general purpose control */
+#define VID_DST_B_GPCNT_CTL 0x130130 /* Video B general purpose control */
+#define VID_DST_C_GPCNT_CTL 0x130230 /* Video C general purpose control */
+#define VID_DST_D_GPCNT_CTL 0x130330 /* Video D general purpose control */
+#define VID_DST_E_GPCNT_CTL 0x130430 /* Video E general purpose control */
+#define VID_DST_F_GPCNT_CTL 0x130530 /* Video F general purpose control */
+#define VID_DST_G_GPCNT_CTL 0x130630 /* Video G general purpose control */
+#define VID_DST_H_GPCNT_CTL 0x130730 /* Video H general purpose control */
+
+/* ***************************************************************************** */
+
+#define VID_DST_A_DMA_CTL 0x130040 /* Video A DMA control */
+#define VID_DST_B_DMA_CTL 0x130140 /* Video B DMA control */
+#define VID_DST_C_DMA_CTL 0x130240 /* Video C DMA control */
+#define VID_DST_D_DMA_CTL 0x130340 /* Video D DMA control */
+#define VID_DST_E_DMA_CTL 0x130440 /* Video E DMA control */
+#define VID_DST_F_DMA_CTL 0x130540 /* Video F DMA control */
+#define VID_DST_G_DMA_CTL 0x130640 /* Video G DMA control */
+#define VID_DST_H_DMA_CTL 0x130740 /* Video H DMA control */
+
+#define FLD_VID_RISC_EN 0x00000010
+#define FLD_VID_FIFO_EN 0x00000001
+
+/* ***************************************************************************** */
+
+#define VID_DST_A_VIP_CTL 0x130080 /* Video A VIP control */
+#define VID_DST_B_VIP_CTL 0x130180 /* Video B VIP control */
+#define VID_DST_C_VIP_CTL 0x130280 /* Video C VIP control */
+#define VID_DST_D_VIP_CTL 0x130380 /* Video D VIP control */
+#define VID_DST_E_VIP_CTL 0x130480 /* Video E VIP control */
+#define VID_DST_F_VIP_CTL 0x130580 /* Video F VIP control */
+#define VID_DST_G_VIP_CTL 0x130680 /* Video G VIP control */
+#define VID_DST_H_VIP_CTL 0x130780 /* Video H VIP control */
+
+/* ***************************************************************************** */
+
+#define VID_DST_A_PIX_FRMT 0x130084 /* Video A Pixel format */
+#define VID_DST_B_PIX_FRMT 0x130184 /* Video B Pixel format */
+#define VID_DST_C_PIX_FRMT 0x130284 /* Video C Pixel format */
+#define VID_DST_D_PIX_FRMT 0x130384 /* Video D Pixel format */
+#define VID_DST_E_PIX_FRMT 0x130484 /* Video E Pixel format */
+#define VID_DST_F_PIX_FRMT 0x130584 /* Video F Pixel format */
+#define VID_DST_G_PIX_FRMT 0x130684 /* Video G Pixel format */
+#define VID_DST_H_PIX_FRMT 0x130784 /* Video H Pixel format */
+
+/* ***************************************************************************** */
+/* Video Source Channels */
+/* ***************************************************************************** */
+
+#define VID_SRC_A_GPCNT_CTL 0x130804 /* Video A general purpose control */
+#define VID_SRC_B_GPCNT_CTL 0x130904 /* Video B general purpose control */
+#define VID_SRC_C_GPCNT_CTL 0x130A04 /* Video C general purpose control */
+#define VID_SRC_D_GPCNT_CTL 0x130B04 /* Video D general purpose control */
+#define VID_SRC_E_GPCNT_CTL 0x130C04 /* Video E general purpose control */
+#define VID_SRC_F_GPCNT_CTL 0x130D04 /* Video F general purpose control */
+#define VID_SRC_I_GPCNT_CTL 0x130E04 /* Video I general purpose control */
+#define VID_SRC_J_GPCNT_CTL 0x130F04 /* Video J general purpose control */
+
+/* ***************************************************************************** */
+
+#define VID_SRC_A_GPCNT 0x130808 /* Video A general purpose counter */
+#define VID_SRC_B_GPCNT 0x130908 /* Video B general purpose counter */
+#define VID_SRC_C_GPCNT 0x130A08 /* Video C general purpose counter */
+#define VID_SRC_D_GPCNT 0x130B08 /* Video D general purpose counter */
+#define VID_SRC_E_GPCNT 0x130C08 /* Video E general purpose counter */
+#define VID_SRC_F_GPCNT 0x130D08 /* Video F general purpose counter */
+#define VID_SRC_I_GPCNT 0x130E08 /* Video I general purpose counter */
+#define VID_SRC_J_GPCNT 0x130F08 /* Video J general purpose counter */
+
+/* ***************************************************************************** */
+
+#define VID_SRC_A_DMA_CTL 0x13080C /* Video A DMA control */
+#define VID_SRC_B_DMA_CTL 0x13090C /* Video B DMA control */
+#define VID_SRC_C_DMA_CTL 0x130A0C /* Video C DMA control */
+#define VID_SRC_D_DMA_CTL 0x130B0C /* Video D DMA control */
+#define VID_SRC_E_DMA_CTL 0x130C0C /* Video E DMA control */
+#define VID_SRC_F_DMA_CTL 0x130D0C /* Video F DMA control */
+#define VID_SRC_I_DMA_CTL 0x130E0C /* Video I DMA control */
+#define VID_SRC_J_DMA_CTL 0x130F0C /* Video J DMA control */
+
+#define FLD_APB_RISC_EN 0x00000010
+#define FLD_APB_FIFO_EN 0x00000001
+
+/* ***************************************************************************** */
+
+#define VID_SRC_A_FMT_CTL 0x130810 /* Video A format control */
+#define VID_SRC_B_FMT_CTL 0x130910 /* Video B format control */
+#define VID_SRC_C_FMT_CTL 0x130A10 /* Video C format control */
+#define VID_SRC_D_FMT_CTL 0x130B10 /* Video D format control */
+#define VID_SRC_E_FMT_CTL 0x130C10 /* Video E format control */
+#define VID_SRC_F_FMT_CTL 0x130D10 /* Video F format control */
+#define VID_SRC_I_FMT_CTL 0x130E10 /* Video I format control */
+#define VID_SRC_J_FMT_CTL 0x130F10 /* Video J format control */
+
+/* ***************************************************************************** */
+
+#define VID_SRC_A_ACTIVE_CTL1 0x130814 /* Video A active control 1 */
+#define VID_SRC_B_ACTIVE_CTL1 0x130914 /* Video B active control 1 */
+#define VID_SRC_C_ACTIVE_CTL1 0x130A14 /* Video C active control 1 */
+#define VID_SRC_D_ACTIVE_CTL1 0x130B14 /* Video D active control 1 */
+#define VID_SRC_E_ACTIVE_CTL1 0x130C14 /* Video E active control 1 */
+#define VID_SRC_F_ACTIVE_CTL1 0x130D14 /* Video F active control 1 */
+#define VID_SRC_I_ACTIVE_CTL1 0x130E14 /* Video I active control 1 */
+#define VID_SRC_J_ACTIVE_CTL1 0x130F14 /* Video J active control 1 */
+
+/* ***************************************************************************** */
+
+#define VID_SRC_A_ACTIVE_CTL2 0x130818 /* Video A active control 2 */
+#define VID_SRC_B_ACTIVE_CTL2 0x130918 /* Video B active control 2 */
+#define VID_SRC_C_ACTIVE_CTL2 0x130A18 /* Video C active control 2 */
+#define VID_SRC_D_ACTIVE_CTL2 0x130B18 /* Video D active control 2 */
+#define VID_SRC_E_ACTIVE_CTL2 0x130C18 /* Video E active control 2 */
+#define VID_SRC_F_ACTIVE_CTL2 0x130D18 /* Video F active control 2 */
+#define VID_SRC_I_ACTIVE_CTL2 0x130E18 /* Video I active control 2 */
+#define VID_SRC_J_ACTIVE_CTL2 0x130F18 /* Video J active control 2 */
+
+/* ***************************************************************************** */
+
+#define VID_SRC_A_CDT_SZ 0x13081C /* Video A CDT size */
+#define VID_SRC_B_CDT_SZ 0x13091C /* Video B CDT size */
+#define VID_SRC_C_CDT_SZ 0x130A1C /* Video C CDT size */
+#define VID_SRC_D_CDT_SZ 0x130B1C /* Video D CDT size */
+#define VID_SRC_E_CDT_SZ 0x130C1C /* Video E CDT size */
+#define VID_SRC_F_CDT_SZ 0x130D1C /* Video F CDT size */
+#define VID_SRC_I_CDT_SZ 0x130E1C /* Video I CDT size */
+#define VID_SRC_J_CDT_SZ 0x130F1C /* Video J CDT size */
+
+/* ***************************************************************************** */
+/* Audio I/F */
+/* ***************************************************************************** */
+#define AUD_DST_A_DMA 0x140000 /* Audio Int A DMA data port */
+#define AUD_SRC_A_DMA 0x140008 /* Audio Int A DMA data port */
+
+#define AUD_A_GPCNT 0x140010 /* Audio Int A gp counter */
+#define FLD_AUD_A_GP_CNT 0x0000FFFF
+
+#define AUD_A_GPCNT_CTL 0x140014 /* Audio Int A gp control */
+
+#define AUD_A_LNGTH 0x140018 /* Audio Int A line length */
+
+#define AUD_A_CFG 0x14001C /* Audio Int A configuration */
+
+/* ***************************************************************************** */
+#define AUD_DST_B_DMA 0x140100 /* Audio Int B DMA data port */
+#define AUD_SRC_B_DMA 0x140108 /* Audio Int B DMA data port */
+
+#define AUD_B_GPCNT 0x140110 /* Audio Int B gp counter */
+#define FLD_AUD_B_GP_CNT 0x0000FFFF
+
+#define AUD_B_GPCNT_CTL 0x140114 /* Audio Int B gp control */
+
+#define AUD_B_LNGTH 0x140118 /* Audio Int B line length */
+
+#define AUD_B_CFG 0x14011C /* Audio Int B configuration */
+
+/* ***************************************************************************** */
+#define AUD_DST_C_DMA 0x140200 /* Audio Int C DMA data port */
+#define AUD_SRC_C_DMA 0x140208 /* Audio Int C DMA data port */
+
+#define AUD_C_GPCNT 0x140210 /* Audio Int C gp counter */
+#define FLD_AUD_C_GP_CNT 0x0000FFFF
+
+#define AUD_C_GPCNT_CTL 0x140214 /* Audio Int C gp control */
+
+#define AUD_C_LNGTH 0x140218 /* Audio Int C line length */
+
+#define AUD_C_CFG 0x14021C /* Audio Int C configuration */
+
+/* ***************************************************************************** */
+#define AUD_DST_D_DMA 0x140300 /* Audio Int D DMA data port */
+#define AUD_SRC_D_DMA 0x140308 /* Audio Int D DMA data port */
+
+#define AUD_D_GPCNT 0x140310 /* Audio Int D gp counter */
+#define FLD_AUD_D_GP_CNT 0x0000FFFF
+
+#define AUD_D_GPCNT_CTL 0x140314 /* Audio Int D gp control */
+
+#define AUD_D_LNGTH 0x140318 /* Audio Int D line length */
+
+#define AUD_D_CFG 0x14031C /* Audio Int D configuration */
+
+/* ***************************************************************************** */
+#define AUD_SRC_E_DMA 0x140400 /* Audio Int E DMA data port */
+
+#define AUD_E_GPCNT 0x140410 /* Audio Int E gp counter */
+#define FLD_AUD_E_GP_CNT 0x0000FFFF
+
+#define AUD_E_GPCNT_CTL 0x140414 /* Audio Int E gp control */
+
+#define AUD_E_CFG 0x14041C /* Audio Int E configuration */
+
+/* ***************************************************************************** */
+
+#define FLD_AUD_DST_LN_LNGTH 0x00000FFF
+
+#define FLD_AUD_DST_PK_MODE 0x00004000
+
+#define FLD_AUD_CLK_ENABLE 0x00000200
+
+#define FLD_AUD_MASTER_MODE 0x00000002
+
+#define FLD_AUD_SONY_MODE 0x00000001
+
+#define FLD_AUD_CLK_SELECT_PLL_D 0x00001800
+
+#define FLD_AUD_DST_ENABLE 0x00020000
+
+#define FLD_AUD_SRC_ENABLE 0x00010000
+
+/* ***************************************************************************** */
+#define AUD_INT_DMA_CTL 0x140500 /* Audio Int DMA control */
+
+#define FLD_AUD_SRC_E_RISC_EN 0x00008000
+#define FLD_AUD_SRC_C_RISC_EN 0x00004000
+#define FLD_AUD_SRC_B_RISC_EN 0x00002000
+#define FLD_AUD_SRC_A_RISC_EN 0x00001000
+
+#define FLD_AUD_DST_D_RISC_EN 0x00000800
+#define FLD_AUD_DST_C_RISC_EN 0x00000400
+#define FLD_AUD_DST_B_RISC_EN 0x00000200
+#define FLD_AUD_DST_A_RISC_EN 0x00000100
+
+#define FLD_AUD_SRC_E_FIFO_EN 0x00000080
+#define FLD_AUD_SRC_C_FIFO_EN 0x00000040
+#define FLD_AUD_SRC_B_FIFO_EN 0x00000020
+#define FLD_AUD_SRC_A_FIFO_EN 0x00000010
+
+#define FLD_AUD_DST_D_FIFO_EN 0x00000008
+#define FLD_AUD_DST_C_FIFO_EN 0x00000004
+#define FLD_AUD_DST_B_FIFO_EN 0x00000002
+#define FLD_AUD_DST_A_FIFO_EN 0x00000001
+
+/* ***************************************************************************** */
+/* */
+/* Mobilygen Interface Registers */
+/* */
+/* ***************************************************************************** */
+/* Mobilygen Interface A */
+/* ***************************************************************************** */
+#define MB_IF_A_DMA 0x150000 /* MBIF A DMA data port */
+#define MB_IF_A_GPCN 0x150008 /* MBIF A GP counter */
+#define MB_IF_A_GPCN_CTRL 0x15000C
+#define MB_IF_A_DMA_CTRL 0x150010
+#define MB_IF_A_LENGTH 0x150014
+#define MB_IF_A_HDMA_XFER_SZ 0x150018
+#define MB_IF_A_HCMD 0x15001C
+#define MB_IF_A_HCONFIG 0x150020
+#define MB_IF_A_DATA_STRUCT_0 0x150024
+#define MB_IF_A_DATA_STRUCT_1 0x150028
+#define MB_IF_A_DATA_STRUCT_2 0x15002C
+#define MB_IF_A_DATA_STRUCT_3 0x150030
+#define MB_IF_A_DATA_STRUCT_4 0x150034
+#define MB_IF_A_DATA_STRUCT_5 0x150038
+#define MB_IF_A_DATA_STRUCT_6 0x15003C
+#define MB_IF_A_DATA_STRUCT_7 0x150040
+#define MB_IF_A_DATA_STRUCT_8 0x150044
+#define MB_IF_A_DATA_STRUCT_9 0x150048
+#define MB_IF_A_DATA_STRUCT_A 0x15004C
+#define MB_IF_A_DATA_STRUCT_B 0x150050
+#define MB_IF_A_DATA_STRUCT_C 0x150054
+#define MB_IF_A_DATA_STRUCT_D 0x150058
+#define MB_IF_A_DATA_STRUCT_E 0x15005C
+#define MB_IF_A_DATA_STRUCT_F 0x150060
+/* ***************************************************************************** */
+/* Mobilygen Interface B */
+/* ***************************************************************************** */
+#define MB_IF_B_DMA 0x160000 /* MBIF A DMA data port */
+#define MB_IF_B_GPCN 0x160008 /* MBIF A GP counter */
+#define MB_IF_B_GPCN_CTRL 0x16000C
+#define MB_IF_B_DMA_CTRL 0x160010
+#define MB_IF_B_LENGTH 0x160014
+#define MB_IF_B_HDMA_XFER_SZ 0x160018
+#define MB_IF_B_HCMD 0x16001C
+#define MB_IF_B_HCONFIG 0x160020
+#define MB_IF_B_DATA_STRUCT_0 0x160024
+#define MB_IF_B_DATA_STRUCT_1 0x160028
+#define MB_IF_B_DATA_STRUCT_2 0x16002C
+#define MB_IF_B_DATA_STRUCT_3 0x160030
+#define MB_IF_B_DATA_STRUCT_4 0x160034
+#define MB_IF_B_DATA_STRUCT_5 0x160038
+#define MB_IF_B_DATA_STRUCT_6 0x16003C
+#define MB_IF_B_DATA_STRUCT_7 0x160040
+#define MB_IF_B_DATA_STRUCT_8 0x160044
+#define MB_IF_B_DATA_STRUCT_9 0x160048
+#define MB_IF_B_DATA_STRUCT_A 0x16004C
+#define MB_IF_B_DATA_STRUCT_B 0x160050
+#define MB_IF_B_DATA_STRUCT_C 0x160054
+#define MB_IF_B_DATA_STRUCT_D 0x160058
+#define MB_IF_B_DATA_STRUCT_E 0x16005C
+#define MB_IF_B_DATA_STRUCT_F 0x160060
+
+/* MB_DMA_CTRL */
+#define FLD_MB_IF_RISC_EN 0x00000010
+#define FLD_MB_IF_FIFO_EN 0x00000001
+
+/* MB_LENGTH */
+#define FLD_MB_IF_LN_LNGTH 0x00000FFF
+
+/* MB_HCMD register */
+#define FLD_MB_HCMD_H_GO 0x80000000
+#define FLD_MB_HCMD_H_BUSY 0x40000000
+#define FLD_MB_HCMD_H_DMA_HOLD 0x10000000
+#define FLD_MB_HCMD_H_DMA_BUSY 0x08000000
+#define FLD_MB_HCMD_H_DMA_TYPE 0x04000000
+#define FLD_MB_HCMD_H_DMA_XACT 0x02000000
+#define FLD_MB_HCMD_H_RW_N 0x01000000
+#define FLD_MB_HCMD_H_ADDR 0x00FF0000
+#define FLD_MB_HCMD_H_DATA 0x0000FFFF
+
+/* ***************************************************************************** */
+/* I2C #1 */
+/* ***************************************************************************** */
+#define I2C1_ADDR 0x180000 /* I2C #1 address */
+#define FLD_I2C_DADDR 0xfe000000 /* RW [31:25] I2C Device Address */
+ /* RO [24] reserved */
+/* ***************************************************************************** */
+#define FLD_I2C_SADDR 0x00FFFFFF /* RW [23:0] I2C Sub-address */
+
+/* ***************************************************************************** */
+#define I2C1_WDATA 0x180004 /* I2C #1 write data */
+#define FLD_I2C_WDATA 0xFFFFFFFF /* RW [31:0] */
+
+/* ***************************************************************************** */
+#define I2C1_CTRL 0x180008 /* I2C #1 control */
+#define FLD_I2C_PERIOD 0xFF000000 /* RW [31:24] */
+#define FLD_I2C_SCL_IN 0x00200000 /* RW [21] */
+#define FLD_I2C_SDA_IN 0x00100000 /* RW [20] */
+ /* RO [19:18] reserved */
+#define FLD_I2C_SCL_OUT 0x00020000 /* RW [17] */
+#define FLD_I2C_SDA_OUT 0x00010000 /* RW [16] */
+ /* RO [15] reserved */
+#define FLD_I2C_DATA_LEN 0x00007000 /* RW [14:12] */
+#define FLD_I2C_SADDR_INC 0x00000800 /* RW [11] */
+ /* RO [10:9] reserved */
+#define FLD_I2C_SADDR_LEN 0x00000300 /* RW [9:8] */
+ /* RO [7:6] reserved */
+#define FLD_I2C_SOFT 0x00000020 /* RW [5] */
+#define FLD_I2C_NOSTOP 0x00000010 /* RW [4] */
+#define FLD_I2C_EXTEND 0x00000008 /* RW [3] */
+#define FLD_I2C_SYNC 0x00000004 /* RW [2] */
+#define FLD_I2C_READ_SA 0x00000002 /* RW [1] */
+#define FLD_I2C_READ_WRN 0x00000001 /* RW [0] */
+
+/* ***************************************************************************** */
+#define I2C1_RDATA 0x18000C /* I2C #1 read data */
+#define FLD_I2C_RDATA 0xFFFFFFFF /* RO [31:0] */
+
+/* ***************************************************************************** */
+#define I2C1_STAT 0x180010 /* I2C #1 status */
+#define FLD_I2C_XFER_IN_PROG 0x00000002 /* RO [1] */
+#define FLD_I2C_RACK 0x00000001 /* RO [0] */
+
+/* ***************************************************************************** */
+/* I2C #2 */
+/* ***************************************************************************** */
+#define I2C2_ADDR 0x190000 /* I2C #2 address */
+
+/* ***************************************************************************** */
+#define I2C2_WDATA 0x190004 /* I2C #2 write data */
+
+/* ***************************************************************************** */
+#define I2C2_CTRL 0x190008 /* I2C #2 control */
+
+/* ***************************************************************************** */
+#define I2C2_RDATA 0x19000C /* I2C #2 read data */
+
+/* ***************************************************************************** */
+#define I2C2_STAT 0x190010 /* I2C #2 status */
+
+/* ***************************************************************************** */
+/* I2C #3 */
+/* ***************************************************************************** */
+#define I2C3_ADDR 0x1A0000 /* I2C #3 address */
+
+/* ***************************************************************************** */
+#define I2C3_WDATA 0x1A0004 /* I2C #3 write data */
+
+/* ***************************************************************************** */
+#define I2C3_CTRL 0x1A0008 /* I2C #3 control */
+
+/* ***************************************************************************** */
+#define I2C3_RDATA 0x1A000C /* I2C #3 read data */
+
+/* ***************************************************************************** */
+#define I2C3_STAT 0x1A0010 /* I2C #3 status */
+
+/* ***************************************************************************** */
+/* UART */
+/* ***************************************************************************** */
+#define UART_CTL 0x1B0000 /* UART Control Register */
+#define FLD_LOOP_BACK_EN (1 << 7) /* RW field - default 0 */
+#define FLD_RX_TRG_SZ (3 << 2) /* RW field - default 0 */
+#define FLD_RX_EN (1 << 1) /* RW field - default 0 */
+#define FLD_TX_EN (1 << 0) /* RW field - default 0 */
+
+/* ***************************************************************************** */
+#define UART_BRD 0x1B0004 /* UART Baud Rate Divisor */
+#define FLD_BRD 0x0000FFFF /* RW field - default 0x197 */
+
+/* ***************************************************************************** */
+#define UART_DBUF 0x1B0008 /* UART Tx/Rx Data BuFFer */
+#define FLD_DB 0xFFFFFFFF /* RW field - default 0 */
+
+/* ***************************************************************************** */
+#define UART_ISR 0x1B000C /* UART Interrupt Status */
+#define FLD_RXD_TIMEOUT_EN (1 << 7) /* RW field - default 0 */
+#define FLD_FRM_ERR_EN (1 << 6) /* RW field - default 0 */
+#define FLD_RXD_RDY_EN (1 << 5) /* RW field - default 0 */
+#define FLD_TXD_EMPTY_EN (1 << 4) /* RW field - default 0 */
+#define FLD_RXD_OVERFLOW (1 << 3) /* RW field - default 0 */
+#define FLD_FRM_ERR (1 << 2) /* RW field - default 0 */
+#define FLD_RXD_RDY (1 << 1) /* RW field - default 0 */
+#define FLD_TXD_EMPTY (1 << 0) /* RW field - default 0 */
+
+/* ***************************************************************************** */
+#define UART_CNT 0x1B0010 /* UART Tx/Rx FIFO Byte Count */
+#define FLD_TXD_CNT (0x1F << 8) /* RW field - default 0 */
+#define FLD_RXD_CNT (0x1F << 0) /* RW field - default 0 */
+
+/* ***************************************************************************** */
+/* Motion Detection */
+#define MD_CH0_GRID_BLOCK_YCNT 0x170014
+#define MD_CH1_GRID_BLOCK_YCNT 0x170094
+#define MD_CH2_GRID_BLOCK_YCNT 0x170114
+#define MD_CH3_GRID_BLOCK_YCNT 0x170194
+#define MD_CH4_GRID_BLOCK_YCNT 0x170214
+#define MD_CH5_GRID_BLOCK_YCNT 0x170294
+#define MD_CH6_GRID_BLOCK_YCNT 0x170314
+#define MD_CH7_GRID_BLOCK_YCNT 0x170394
+
+#define PIXEL_FRMT_422 4
+#define PIXEL_FRMT_411 5
+#define PIXEL_FRMT_Y8 6
+
+#define PIXEL_ENGINE_VIP1 0
+#define PIXEL_ENGINE_VIP2 1
+
+#endif /* Athena_REGISTERS */
diff --git a/drivers/media/video/cx25821/cx25821-sram.h b/drivers/media/video/cx25821/cx25821-sram.h
new file mode 100644
index 00000000..5f05d153
--- /dev/null
+++ b/drivers/media/video/cx25821/cx25821-sram.h
@@ -0,0 +1,261 @@
+/*
+ * Driver for the Conexant CX25821 PCIe bridge
+ *
+ * Copyright (C) 2009 Conexant Systems Inc.
+ * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ATHENA_SRAM_H__
+#define __ATHENA_SRAM_H__
+
+/* #define RX_SRAM_START_SIZE = 0; // Start of reserved SRAM */
+#define VID_CMDS_SIZE 80 /* Video CMDS size in bytes */
+#define AUDIO_CMDS_SIZE 80 /* AUDIO CMDS size in bytes */
+#define MBIF_CMDS_SIZE 80 /* MBIF CMDS size in bytes */
+
+/* #define RX_SRAM_POOL_START_SIZE = 0; // Start of useable RX SRAM for buffers */
+#define VID_IQ_SIZE 64 /* VID instruction queue size in bytes */
+#define MBIF_IQ_SIZE 64
+#define AUDIO_IQ_SIZE 64 /* AUD instruction queue size in bytes */
+
+#define VID_CDT_SIZE 64 /* VID cluster descriptor table size in bytes */
+#define MBIF_CDT_SIZE 64 /* MBIF/HBI cluster descriptor table size in bytes */
+#define AUDIO_CDT_SIZE 48 /* AUD cluster descriptor table size in bytes */
+
+/* #define RX_SRAM_POOL_FREE_SIZE = 16; // Start of available RX SRAM */
+/* #define RX_SRAM_END_SIZE = 0; // End of RX SRAM */
+
+/* #define TX_SRAM_POOL_START_SIZE = 0; // Start of transmit pool SRAM */
+/* #define MSI_DATA_SIZE = 64; // Reserved (MSI Data, RISC working stora */
+
+#define VID_CLUSTER_SIZE 1440 /* VID cluster data line */
+#define AUDIO_CLUSTER_SIZE 128 /* AUDIO cluster data line */
+#define MBIF_CLUSTER_SIZE 1440 /* MBIF/HBI cluster data line */
+
+/* #define TX_SRAM_POOL_FREE_SIZE = 704; // Start of available TX SRAM */
+/* #define TX_SRAM_END_SIZE = 0; // End of TX SRAM */
+
+/* Receive SRAM */
+#define RX_SRAM_START 0x10000
+#define VID_A_DOWN_CMDS 0x10000
+#define VID_B_DOWN_CMDS 0x10050
+#define VID_C_DOWN_CMDS 0x100A0
+#define VID_D_DOWN_CMDS 0x100F0
+#define VID_E_DOWN_CMDS 0x10140
+#define VID_F_DOWN_CMDS 0x10190
+#define VID_G_DOWN_CMDS 0x101E0
+#define VID_H_DOWN_CMDS 0x10230
+#define VID_A_UP_CMDS 0x10280
+#define VID_B_UP_CMDS 0x102D0
+#define VID_C_UP_CMDS 0x10320
+#define VID_D_UP_CMDS 0x10370
+#define VID_E_UP_CMDS 0x103C0
+#define VID_F_UP_CMDS 0x10410
+#define VID_I_UP_CMDS 0x10460
+#define VID_J_UP_CMDS 0x104B0
+#define AUD_A_DOWN_CMDS 0x10500
+#define AUD_B_DOWN_CMDS 0x10550
+#define AUD_C_DOWN_CMDS 0x105A0
+#define AUD_D_DOWN_CMDS 0x105F0
+#define AUD_A_UP_CMDS 0x10640
+#define AUD_B_UP_CMDS 0x10690
+#define AUD_C_UP_CMDS 0x106E0
+#define AUD_E_UP_CMDS 0x10730
+#define MBIF_A_DOWN_CMDS 0x10780
+#define MBIF_B_DOWN_CMDS 0x107D0
+#define DMA_SCRATCH_PAD 0x10820 /* Scratch pad area from 0x10820 to 0x10B40 */
+
+/* #define RX_SRAM_POOL_START = 0x105B0; */
+
+#define VID_A_IQ 0x11000
+#define VID_B_IQ 0x11040
+#define VID_C_IQ 0x11080
+#define VID_D_IQ 0x110C0
+#define VID_E_IQ 0x11100
+#define VID_F_IQ 0x11140
+#define VID_G_IQ 0x11180
+#define VID_H_IQ 0x111C0
+#define VID_I_IQ 0x11200
+#define VID_J_IQ 0x11240
+#define AUD_A_IQ 0x11280
+#define AUD_B_IQ 0x112C0
+#define AUD_C_IQ 0x11300
+#define AUD_D_IQ 0x11340
+#define AUD_E_IQ 0x11380
+#define MBIF_A_IQ 0x11000
+#define MBIF_B_IQ 0x110C0
+
+#define VID_A_CDT 0x10C00
+#define VID_B_CDT 0x10C40
+#define VID_C_CDT 0x10C80
+#define VID_D_CDT 0x10CC0
+#define VID_E_CDT 0x10D00
+#define VID_F_CDT 0x10D40
+#define VID_G_CDT 0x10D80
+#define VID_H_CDT 0x10DC0
+#define VID_I_CDT 0x10E00
+#define VID_J_CDT 0x10E40
+#define AUD_A_CDT 0x10E80
+#define AUD_B_CDT 0x10EB0
+#define AUD_C_CDT 0x10EE0
+#define AUD_D_CDT 0x10F10
+#define AUD_E_CDT 0x10F40
+#define MBIF_A_CDT 0x10C00
+#define MBIF_B_CDT 0x10CC0
+
+/* Cluster Buffer for RX */
+#define VID_A_UP_CLUSTER_1 0x11400
+#define VID_A_UP_CLUSTER_2 0x119A0
+#define VID_A_UP_CLUSTER_3 0x11F40
+#define VID_A_UP_CLUSTER_4 0x124E0
+
+#define VID_B_UP_CLUSTER_1 0x12A80
+#define VID_B_UP_CLUSTER_2 0x13020
+#define VID_B_UP_CLUSTER_3 0x135C0
+#define VID_B_UP_CLUSTER_4 0x13B60
+
+#define VID_C_UP_CLUSTER_1 0x14100
+#define VID_C_UP_CLUSTER_2 0x146A0
+#define VID_C_UP_CLUSTER_3 0x14C40
+#define VID_C_UP_CLUSTER_4 0x151E0
+
+#define VID_D_UP_CLUSTER_1 0x15780
+#define VID_D_UP_CLUSTER_2 0x15D20
+#define VID_D_UP_CLUSTER_3 0x162C0
+#define VID_D_UP_CLUSTER_4 0x16860
+
+#define VID_E_UP_CLUSTER_1 0x16E00
+#define VID_E_UP_CLUSTER_2 0x173A0
+#define VID_E_UP_CLUSTER_3 0x17940
+#define VID_E_UP_CLUSTER_4 0x17EE0
+
+#define VID_F_UP_CLUSTER_1 0x18480
+#define VID_F_UP_CLUSTER_2 0x18A20
+#define VID_F_UP_CLUSTER_3 0x18FC0
+#define VID_F_UP_CLUSTER_4 0x19560
+
+#define VID_I_UP_CLUSTER_1 0x19B00
+#define VID_I_UP_CLUSTER_2 0x1A0A0
+#define VID_I_UP_CLUSTER_3 0x1A640
+#define VID_I_UP_CLUSTER_4 0x1ABE0
+
+#define VID_J_UP_CLUSTER_1 0x1B180
+#define VID_J_UP_CLUSTER_2 0x1B720
+#define VID_J_UP_CLUSTER_3 0x1BCC0
+#define VID_J_UP_CLUSTER_4 0x1C260
+
+#define AUD_A_UP_CLUSTER_1 0x1C800
+#define AUD_A_UP_CLUSTER_2 0x1C880
+#define AUD_A_UP_CLUSTER_3 0x1C900
+
+#define AUD_B_UP_CLUSTER_1 0x1C980
+#define AUD_B_UP_CLUSTER_2 0x1CA00
+#define AUD_B_UP_CLUSTER_3 0x1CA80
+
+#define AUD_C_UP_CLUSTER_1 0x1CB00
+#define AUD_C_UP_CLUSTER_2 0x1CB80
+#define AUD_C_UP_CLUSTER_3 0x1CC00
+
+#define AUD_E_UP_CLUSTER_1 0x1CC80
+#define AUD_E_UP_CLUSTER_2 0x1CD00
+#define AUD_E_UP_CLUSTER_3 0x1CD80
+
+#define RX_SRAM_POOL_FREE 0x1CE00
+#define RX_SRAM_END 0x1D000
+
+/* Free Receive SRAM 144 Bytes */
+
+/* Transmit SRAM */
+#define TX_SRAM_POOL_START 0x00000
+
+#define VID_A_DOWN_CLUSTER_1 0x00040
+#define VID_A_DOWN_CLUSTER_2 0x005E0
+#define VID_A_DOWN_CLUSTER_3 0x00B80
+#define VID_A_DOWN_CLUSTER_4 0x01120
+
+#define VID_B_DOWN_CLUSTER_1 0x016C0
+#define VID_B_DOWN_CLUSTER_2 0x01C60
+#define VID_B_DOWN_CLUSTER_3 0x02200
+#define VID_B_DOWN_CLUSTER_4 0x027A0
+
+#define VID_C_DOWN_CLUSTER_1 0x02D40
+#define VID_C_DOWN_CLUSTER_2 0x032E0
+#define VID_C_DOWN_CLUSTER_3 0x03880
+#define VID_C_DOWN_CLUSTER_4 0x03E20
+
+#define VID_D_DOWN_CLUSTER_1 0x043C0
+#define VID_D_DOWN_CLUSTER_2 0x04960
+#define VID_D_DOWN_CLUSTER_3 0x04F00
+#define VID_D_DOWN_CLUSTER_4 0x054A0
+
+#define VID_E_DOWN_CLUSTER_1 0x05a40
+#define VID_E_DOWN_CLUSTER_2 0x05FE0
+#define VID_E_DOWN_CLUSTER_3 0x06580
+#define VID_E_DOWN_CLUSTER_4 0x06B20
+
+#define VID_F_DOWN_CLUSTER_1 0x070C0
+#define VID_F_DOWN_CLUSTER_2 0x07660
+#define VID_F_DOWN_CLUSTER_3 0x07C00
+#define VID_F_DOWN_CLUSTER_4 0x081A0
+
+#define VID_G_DOWN_CLUSTER_1 0x08740
+#define VID_G_DOWN_CLUSTER_2 0x08CE0
+#define VID_G_DOWN_CLUSTER_3 0x09280
+#define VID_G_DOWN_CLUSTER_4 0x09820
+
+#define VID_H_DOWN_CLUSTER_1 0x09DC0
+#define VID_H_DOWN_CLUSTER_2 0x0A360
+#define VID_H_DOWN_CLUSTER_3 0x0A900
+#define VID_H_DOWN_CLUSTER_4 0x0AEA0
+
+#define AUD_A_DOWN_CLUSTER_1 0x0B500
+#define AUD_A_DOWN_CLUSTER_2 0x0B580
+#define AUD_A_DOWN_CLUSTER_3 0x0B600
+
+#define AUD_B_DOWN_CLUSTER_1 0x0B680
+#define AUD_B_DOWN_CLUSTER_2 0x0B700
+#define AUD_B_DOWN_CLUSTER_3 0x0B780
+
+#define AUD_C_DOWN_CLUSTER_1 0x0B800
+#define AUD_C_DOWN_CLUSTER_2 0x0B880
+#define AUD_C_DOWN_CLUSTER_3 0x0B900
+
+#define AUD_D_DOWN_CLUSTER_1 0x0B980
+#define AUD_D_DOWN_CLUSTER_2 0x0BA00
+#define AUD_D_DOWN_CLUSTER_3 0x0BA80
+
+#define TX_SRAM_POOL_FREE 0x0BB00
+#define TX_SRAM_END 0x0C000
+
+#define BYTES_TO_DWORDS(bcount) ((bcount) >> 2)
+#define BYTES_TO_QWORDS(bcount) ((bcount) >> 3)
+#define BYTES_TO_OWORDS(bcount) ((bcount) >> 4)
+
+#define VID_IQ_SIZE_DW BYTES_TO_DWORDS(VID_IQ_SIZE)
+#define VID_CDT_SIZE_QW BYTES_TO_QWORDS(VID_CDT_SIZE)
+#define VID_CLUSTER_SIZE_OW BYTES_TO_OWORDS(VID_CLUSTER_SIZE)
+
+#define AUDIO_IQ_SIZE_DW BYTES_TO_DWORDS(AUDIO_IQ_SIZE)
+#define AUDIO_CDT_SIZE_QW BYTES_TO_QWORDS(AUDIO_CDT_SIZE)
+#define AUDIO_CLUSTER_SIZE_QW BYTES_TO_QWORDS(AUDIO_CLUSTER_SIZE)
+
+#define MBIF_IQ_SIZE_DW BYTES_TO_DWORDS(MBIF_IQ_SIZE)
+#define MBIF_CDT_SIZE_QW BYTES_TO_QWORDS(MBIF_CDT_SIZE)
+#define MBIF_CLUSTER_SIZE_OW BYTES_TO_OWORDS(MBIF_CLUSTER_SIZE)
+
+#endif
diff --git a/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c b/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c
new file mode 100644
index 00000000..5a157cf4
--- /dev/null
+++ b/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c
@@ -0,0 +1,803 @@
+/*
+ * Driver for the Conexant CX25821 PCIe bridge
+ *
+ * Copyright (C) 2009 Conexant Systems Inc.
+ * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "cx25821-video.h"
+#include "cx25821-video-upstream-ch2.h"
+
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/syscalls.h>
+#include <linux/file.h>
+#include <linux/fcntl.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards");
+MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>");
+MODULE_LICENSE("GPL");
+
+static int _intr_msk = FLD_VID_SRC_RISC1 | FLD_VID_SRC_UF | FLD_VID_SRC_SYNC |
+ FLD_VID_SRC_OPC_ERR;
+
+static __le32 *cx25821_update_riscprogram_ch2(struct cx25821_dev *dev,
+ __le32 *rp, unsigned int offset,
+ unsigned int bpl, u32 sync_line,
+ unsigned int lines,
+ int fifo_enable, int field_type)
+{
+ unsigned int line, i;
+ int dist_betwn_starts = bpl * 2;
+
+ *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
+
+ if (USE_RISC_NOOP_VIDEO) {
+ for (i = 0; i < NUM_NO_OPS; i++)
+ *(rp++) = cpu_to_le32(RISC_NOOP);
+ }
+
+ /* scan lines */
+ for (line = 0; line < lines; line++) {
+ *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl);
+ *(rp++) = cpu_to_le32(dev->_data_buf_phys_addr_ch2 + offset);
+ *(rp++) = cpu_to_le32(0); /* bits 63-32 */
+
+ if ((lines <= NTSC_FIELD_HEIGHT) ||
+ (line < (NTSC_FIELD_HEIGHT - 1)) || !(dev->_isNTSC_ch2)) {
+ offset += dist_betwn_starts;
+ }
+ }
+
+ return rp;
+}
+
+static __le32 *cx25821_risc_field_upstream_ch2(struct cx25821_dev *dev,
+ __le32 *rp,
+ dma_addr_t databuf_phys_addr,
+ unsigned int offset,
+ u32 sync_line, unsigned int bpl,
+ unsigned int lines,
+ int fifo_enable, int field_type)
+{
+ unsigned int line, i;
+ struct sram_channel *sram_ch =
+ dev->channels[dev->_channel2_upstream_select].sram_channels;
+ int dist_betwn_starts = bpl * 2;
+
+ /* sync instruction */
+ if (sync_line != NO_SYNC_LINE)
+ *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
+
+ if (USE_RISC_NOOP_VIDEO) {
+ for (i = 0; i < NUM_NO_OPS; i++)
+ *(rp++) = cpu_to_le32(RISC_NOOP);
+ }
+
+ /* scan lines */
+ for (line = 0; line < lines; line++) {
+ *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl);
+ *(rp++) = cpu_to_le32(databuf_phys_addr + offset);
+ *(rp++) = cpu_to_le32(0); /* bits 63-32 */
+
+ if ((lines <= NTSC_FIELD_HEIGHT) ||
+ (line < (NTSC_FIELD_HEIGHT - 1)) || !(dev->_isNTSC_ch2)) {
+ offset += dist_betwn_starts;
+ }
+
+ /*
+ check if we need to enable the FIFO after the first 4 lines
+ For the upstream video channel, the risc engine will enable
+ the FIFO.
+ */
+ if (fifo_enable && line == 3) {
+ *(rp++) = RISC_WRITECR;
+ *(rp++) = sram_ch->dma_ctl;
+ *(rp++) = FLD_VID_FIFO_EN;
+ *(rp++) = 0x00000001;
+ }
+ }
+
+ return rp;
+}
+
+int cx25821_risc_buffer_upstream_ch2(struct cx25821_dev *dev,
+ struct pci_dev *pci,
+ unsigned int top_offset, unsigned int bpl,
+ unsigned int lines)
+{
+ __le32 *rp;
+ int fifo_enable = 0;
+ int singlefield_lines = lines >> 1; /*get line count for single field */
+ int odd_num_lines = singlefield_lines;
+ int frame = 0;
+ int frame_size = 0;
+ int databuf_offset = 0;
+ int risc_program_size = 0;
+ int risc_flag = RISC_CNT_RESET;
+ unsigned int bottom_offset = bpl;
+ dma_addr_t risc_phys_jump_addr;
+
+ if (dev->_isNTSC_ch2) {
+ odd_num_lines = singlefield_lines + 1;
+ risc_program_size = FRAME1_VID_PROG_SIZE;
+ if (bpl == Y411_LINE_SZ)
+ frame_size = FRAME_SIZE_NTSC_Y411;
+ else
+ frame_size = FRAME_SIZE_NTSC_Y422;
+ } else {
+ risc_program_size = PAL_VID_PROG_SIZE;
+ if (bpl == Y411_LINE_SZ)
+ frame_size = FRAME_SIZE_PAL_Y411;
+ else
+ frame_size = FRAME_SIZE_PAL_Y422;
+ }
+
+ /* Virtual address of Risc buffer program */
+ rp = dev->_dma_virt_addr_ch2;
+
+ for (frame = 0; frame < NUM_FRAMES; frame++) {
+ databuf_offset = frame_size * frame;
+
+ if (UNSET != top_offset) {
+ fifo_enable = (frame == 0) ? FIFO_ENABLE : FIFO_DISABLE;
+ rp = cx25821_risc_field_upstream_ch2(dev, rp,
+ dev->_data_buf_phys_addr_ch2 + databuf_offset,
+ top_offset, 0, bpl, odd_num_lines, fifo_enable,
+ ODD_FIELD);
+ }
+
+ fifo_enable = FIFO_DISABLE;
+
+ /* Even field */
+ rp = cx25821_risc_field_upstream_ch2(dev, rp,
+ dev->_data_buf_phys_addr_ch2 + databuf_offset,
+ bottom_offset, 0x200, bpl, singlefield_lines,
+ fifo_enable, EVEN_FIELD);
+
+ if (frame == 0) {
+ risc_flag = RISC_CNT_RESET;
+ risc_phys_jump_addr = dev->_dma_phys_start_addr_ch2 +
+ risc_program_size;
+ } else {
+ risc_flag = RISC_CNT_INC;
+ risc_phys_jump_addr = dev->_dma_phys_start_addr_ch2;
+ }
+
+ /*
+ * Loop to 2ndFrameRISC or to Start of
+ * Risc program & generate IRQ
+ */
+ *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | risc_flag);
+ *(rp++) = cpu_to_le32(risc_phys_jump_addr);
+ *(rp++) = cpu_to_le32(0);
+ }
+
+ return 0;
+}
+
+void cx25821_stop_upstream_video_ch2(struct cx25821_dev *dev)
+{
+ struct sram_channel *sram_ch =
+ dev->channels[VID_UPSTREAM_SRAM_CHANNEL_J].sram_channels;
+ u32 tmp = 0;
+
+ if (!dev->_is_running_ch2) {
+ pr_info("No video file is currently running so return!\n");
+ return;
+ }
+ /* Disable RISC interrupts */
+ tmp = cx_read(sram_ch->int_msk);
+ cx_write(sram_ch->int_msk, tmp & ~_intr_msk);
+
+ /* Turn OFF risc and fifo */
+ tmp = cx_read(sram_ch->dma_ctl);
+ cx_write(sram_ch->dma_ctl, tmp & ~(FLD_VID_FIFO_EN | FLD_VID_RISC_EN));
+
+ /* Clear data buffer memory */
+ if (dev->_data_buf_virt_addr_ch2)
+ memset(dev->_data_buf_virt_addr_ch2, 0,
+ dev->_data_buf_size_ch2);
+
+ dev->_is_running_ch2 = 0;
+ dev->_is_first_frame_ch2 = 0;
+ dev->_frame_count_ch2 = 0;
+ dev->_file_status_ch2 = END_OF_FILE;
+
+ kfree(dev->_irq_queues_ch2);
+ dev->_irq_queues_ch2 = NULL;
+
+ kfree(dev->_filename_ch2);
+
+ tmp = cx_read(VID_CH_MODE_SEL);
+ cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00);
+}
+
+void cx25821_free_mem_upstream_ch2(struct cx25821_dev *dev)
+{
+ if (dev->_is_running_ch2)
+ cx25821_stop_upstream_video_ch2(dev);
+
+ if (dev->_dma_virt_addr_ch2) {
+ pci_free_consistent(dev->pci, dev->_risc_size_ch2,
+ dev->_dma_virt_addr_ch2,
+ dev->_dma_phys_addr_ch2);
+ dev->_dma_virt_addr_ch2 = NULL;
+ }
+
+ if (dev->_data_buf_virt_addr_ch2) {
+ pci_free_consistent(dev->pci, dev->_data_buf_size_ch2,
+ dev->_data_buf_virt_addr_ch2,
+ dev->_data_buf_phys_addr_ch2);
+ dev->_data_buf_virt_addr_ch2 = NULL;
+ }
+}
+
+int cx25821_get_frame_ch2(struct cx25821_dev *dev, struct sram_channel *sram_ch)
+{
+ struct file *myfile;
+ int frame_index_temp = dev->_frame_index_ch2;
+ int i = 0;
+ int line_size = (dev->_pixel_format_ch2 == PIXEL_FRMT_411) ?
+ Y411_LINE_SZ : Y422_LINE_SZ;
+ int frame_size = 0;
+ int frame_offset = 0;
+ ssize_t vfs_read_retval = 0;
+ char mybuf[line_size];
+ loff_t file_offset;
+ loff_t pos;
+ mm_segment_t old_fs;
+
+ if (dev->_file_status_ch2 == END_OF_FILE)
+ return 0;
+
+ if (dev->_isNTSC_ch2) {
+ frame_size = (line_size == Y411_LINE_SZ) ?
+ FRAME_SIZE_NTSC_Y411 : FRAME_SIZE_NTSC_Y422;
+ } else {
+ frame_size = (line_size == Y411_LINE_SZ) ?
+ FRAME_SIZE_PAL_Y411 : FRAME_SIZE_PAL_Y422;
+ }
+
+ frame_offset = (frame_index_temp > 0) ? frame_size : 0;
+ file_offset = dev->_frame_count_ch2 * frame_size;
+
+ myfile = filp_open(dev->_filename_ch2, O_RDONLY | O_LARGEFILE, 0);
+ if (IS_ERR(myfile)) {
+ const int open_errno = -PTR_ERR(myfile);
+ pr_err("%s(): ERROR opening file(%s) with errno = %d!\n",
+ __func__, dev->_filename_ch2, open_errno);
+ return PTR_ERR(myfile);
+ } else {
+ if (!(myfile->f_op)) {
+ pr_err("%s(): File has no file operations registered!\n",
+ __func__);
+ filp_close(myfile, NULL);
+ return -EIO;
+ }
+
+ if (!myfile->f_op->read) {
+ pr_err("%s(): File has no READ operations registered!\n",
+ __func__);
+ filp_close(myfile, NULL);
+ return -EIO;
+ }
+
+ pos = myfile->f_pos;
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ for (i = 0; i < dev->_lines_count_ch2; i++) {
+ pos = file_offset;
+
+ vfs_read_retval = vfs_read(myfile, mybuf, line_size,
+ &pos);
+
+ if (vfs_read_retval > 0 && vfs_read_retval == line_size
+ && dev->_data_buf_virt_addr_ch2 != NULL) {
+ memcpy((void *)(dev->_data_buf_virt_addr_ch2 +
+ frame_offset / 4), mybuf,
+ vfs_read_retval);
+ }
+
+ file_offset += vfs_read_retval;
+ frame_offset += vfs_read_retval;
+
+ if (vfs_read_retval < line_size) {
+ pr_info("Done: exit %s() since no more bytes to read from Video file\n",
+ __func__);
+ break;
+ }
+ }
+
+ if (i > 0)
+ dev->_frame_count_ch2++;
+
+ dev->_file_status_ch2 = (vfs_read_retval == line_size) ?
+ IN_PROGRESS : END_OF_FILE;
+
+ set_fs(old_fs);
+ filp_close(myfile, NULL);
+ }
+
+ return 0;
+}
+
+static void cx25821_vidups_handler_ch2(struct work_struct *work)
+{
+ struct cx25821_dev *dev = container_of(work, struct cx25821_dev,
+ _irq_work_entry_ch2);
+
+ if (!dev) {
+ pr_err("ERROR %s(): since container_of(work_struct) FAILED!\n",
+ __func__);
+ return;
+ }
+
+ cx25821_get_frame_ch2(dev, dev->channels[dev->
+ _channel2_upstream_select].sram_channels);
+}
+
+int cx25821_openfile_ch2(struct cx25821_dev *dev, struct sram_channel *sram_ch)
+{
+ struct file *myfile;
+ int i = 0, j = 0;
+ int line_size = (dev->_pixel_format_ch2 == PIXEL_FRMT_411) ?
+ Y411_LINE_SZ : Y422_LINE_SZ;
+ ssize_t vfs_read_retval = 0;
+ char mybuf[line_size];
+ loff_t pos;
+ loff_t offset = (unsigned long)0;
+ mm_segment_t old_fs;
+
+ myfile = filp_open(dev->_filename_ch2, O_RDONLY | O_LARGEFILE, 0);
+
+ if (IS_ERR(myfile)) {
+ const int open_errno = -PTR_ERR(myfile);
+ pr_err("%s(): ERROR opening file(%s) with errno = %d!\n",
+ __func__, dev->_filename_ch2, open_errno);
+ return PTR_ERR(myfile);
+ } else {
+ if (!(myfile->f_op)) {
+ pr_err("%s(): File has no file operations registered!\n",
+ __func__);
+ filp_close(myfile, NULL);
+ return -EIO;
+ }
+
+ if (!myfile->f_op->read) {
+ pr_err("%s(): File has no READ operations registered! Returning\n",
+ __func__);
+ filp_close(myfile, NULL);
+ return -EIO;
+ }
+
+ pos = myfile->f_pos;
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ for (j = 0; j < NUM_FRAMES; j++) {
+ for (i = 0; i < dev->_lines_count_ch2; i++) {
+ pos = offset;
+
+ vfs_read_retval = vfs_read(myfile, mybuf,
+ line_size, &pos);
+
+ if (vfs_read_retval > 0 &&
+ vfs_read_retval == line_size &&
+ dev->_data_buf_virt_addr_ch2 != NULL) {
+ memcpy((void *)(dev->
+ _data_buf_virt_addr_ch2
+ + offset / 4), mybuf,
+ vfs_read_retval);
+ }
+
+ offset += vfs_read_retval;
+
+ if (vfs_read_retval < line_size) {
+ pr_info("Done: exit %s() since no more bytes to read from Video file\n",
+ __func__);
+ break;
+ }
+ }
+
+ if (i > 0)
+ dev->_frame_count_ch2++;
+
+ if (vfs_read_retval < line_size)
+ break;
+ }
+
+ dev->_file_status_ch2 = (vfs_read_retval == line_size) ?
+ IN_PROGRESS : END_OF_FILE;
+
+ set_fs(old_fs);
+ myfile->f_pos = 0;
+ filp_close(myfile, NULL);
+ }
+
+ return 0;
+}
+
+static int cx25821_upstream_buffer_prepare_ch2(struct cx25821_dev *dev,
+ struct sram_channel *sram_ch,
+ int bpl)
+{
+ int ret = 0;
+ dma_addr_t dma_addr;
+ dma_addr_t data_dma_addr;
+
+ if (dev->_dma_virt_addr_ch2 != NULL) {
+ pci_free_consistent(dev->pci, dev->upstream_riscbuf_size_ch2,
+ dev->_dma_virt_addr_ch2,
+ dev->_dma_phys_addr_ch2);
+ }
+
+ dev->_dma_virt_addr_ch2 = pci_alloc_consistent(dev->pci,
+ dev->upstream_riscbuf_size_ch2, &dma_addr);
+ dev->_dma_virt_start_addr_ch2 = dev->_dma_virt_addr_ch2;
+ dev->_dma_phys_start_addr_ch2 = dma_addr;
+ dev->_dma_phys_addr_ch2 = dma_addr;
+ dev->_risc_size_ch2 = dev->upstream_riscbuf_size_ch2;
+
+ if (!dev->_dma_virt_addr_ch2) {
+ pr_err("FAILED to allocate memory for Risc buffer! Returning\n");
+ return -ENOMEM;
+ }
+
+ /* Iniitize at this address until n bytes to 0 */
+ memset(dev->_dma_virt_addr_ch2, 0, dev->_risc_size_ch2);
+
+ if (dev->_data_buf_virt_addr_ch2 != NULL) {
+ pci_free_consistent(dev->pci, dev->upstream_databuf_size_ch2,
+ dev->_data_buf_virt_addr_ch2,
+ dev->_data_buf_phys_addr_ch2);
+ }
+ /* For Video Data buffer allocation */
+ dev->_data_buf_virt_addr_ch2 = pci_alloc_consistent(dev->pci,
+ dev->upstream_databuf_size_ch2, &data_dma_addr);
+ dev->_data_buf_phys_addr_ch2 = data_dma_addr;
+ dev->_data_buf_size_ch2 = dev->upstream_databuf_size_ch2;
+
+ if (!dev->_data_buf_virt_addr_ch2) {
+ pr_err("FAILED to allocate memory for data buffer! Returning\n");
+ return -ENOMEM;
+ }
+
+ /* Initialize at this address until n bytes to 0 */
+ memset(dev->_data_buf_virt_addr_ch2, 0, dev->_data_buf_size_ch2);
+
+ ret = cx25821_openfile_ch2(dev, sram_ch);
+ if (ret < 0)
+ return ret;
+
+ /* Creating RISC programs */
+ ret = cx25821_risc_buffer_upstream_ch2(dev, dev->pci, 0, bpl,
+ dev->_lines_count_ch2);
+ if (ret < 0) {
+ pr_info("Failed creating Video Upstream Risc programs!\n");
+ goto error;
+ }
+
+ return 0;
+
+error:
+ return ret;
+}
+
+int cx25821_video_upstream_irq_ch2(struct cx25821_dev *dev, int chan_num,
+ u32 status)
+{
+ u32 int_msk_tmp;
+ struct sram_channel *channel = dev->channels[chan_num].sram_channels;
+ int singlefield_lines = NTSC_FIELD_HEIGHT;
+ int line_size_in_bytes = Y422_LINE_SZ;
+ int odd_risc_prog_size = 0;
+ dma_addr_t risc_phys_jump_addr;
+ __le32 *rp;
+
+ if (status & FLD_VID_SRC_RISC1) {
+ /* We should only process one program per call */
+ u32 prog_cnt = cx_read(channel->gpcnt);
+
+ /*
+ * Since we've identified our IRQ, clear our bits from the
+ * interrupt mask and interrupt status registers
+ */
+ int_msk_tmp = cx_read(channel->int_msk);
+ cx_write(channel->int_msk, int_msk_tmp & ~_intr_msk);
+ cx_write(channel->int_stat, _intr_msk);
+
+ spin_lock(&dev->slock);
+
+ dev->_frame_index_ch2 = prog_cnt;
+
+ queue_work(dev->_irq_queues_ch2, &dev->_irq_work_entry_ch2);
+
+ if (dev->_is_first_frame_ch2) {
+ dev->_is_first_frame_ch2 = 0;
+
+ if (dev->_isNTSC_ch2) {
+ singlefield_lines += 1;
+ odd_risc_prog_size = ODD_FLD_NTSC_PROG_SIZE;
+ } else {
+ singlefield_lines = PAL_FIELD_HEIGHT;
+ odd_risc_prog_size = ODD_FLD_PAL_PROG_SIZE;
+ }
+
+ if (dev->_dma_virt_start_addr_ch2 != NULL) {
+ if (dev->_pixel_format_ch2 == PIXEL_FRMT_411)
+ line_size_in_bytes = Y411_LINE_SZ;
+ else
+ line_size_in_bytes = Y422_LINE_SZ;
+ risc_phys_jump_addr =
+ dev->_dma_phys_start_addr_ch2 +
+ odd_risc_prog_size;
+
+ rp = cx25821_update_riscprogram_ch2(dev,
+ dev->_dma_virt_start_addr_ch2,
+ TOP_OFFSET, line_size_in_bytes,
+ 0x0, singlefield_lines,
+ FIFO_DISABLE, ODD_FIELD);
+
+ /* Jump to Even Risc program of 1st Frame */
+ *(rp++) = cpu_to_le32(RISC_JUMP);
+ *(rp++) = cpu_to_le32(risc_phys_jump_addr);
+ *(rp++) = cpu_to_le32(0);
+ }
+ }
+
+ spin_unlock(&dev->slock);
+ }
+
+ if (dev->_file_status_ch2 == END_OF_FILE) {
+ pr_info("EOF Channel 2 Framecount = %d\n",
+ dev->_frame_count_ch2);
+ return -1;
+ }
+ /* ElSE, set the interrupt mask register, re-enable irq. */
+ int_msk_tmp = cx_read(channel->int_msk);
+ cx_write(channel->int_msk, int_msk_tmp |= _intr_msk);
+
+ return 0;
+}
+
+static irqreturn_t cx25821_upstream_irq_ch2(int irq, void *dev_id)
+{
+ struct cx25821_dev *dev = dev_id;
+ u32 msk_stat, vid_status;
+ int handled = 0;
+ int channel_num = 0;
+ struct sram_channel *sram_ch;
+
+ if (!dev)
+ return -1;
+
+ channel_num = VID_UPSTREAM_SRAM_CHANNEL_J;
+ sram_ch = dev->channels[channel_num].sram_channels;
+
+ msk_stat = cx_read(sram_ch->int_mstat);
+ vid_status = cx_read(sram_ch->int_stat);
+
+ /* Only deal with our interrupt */
+ if (vid_status)
+ handled = cx25821_video_upstream_irq_ch2(dev, channel_num,
+ vid_status);
+
+ if (handled < 0)
+ cx25821_stop_upstream_video_ch2(dev);
+ else
+ handled += handled;
+
+ return IRQ_RETVAL(handled);
+}
+
+static void cx25821_set_pixelengine_ch2(struct cx25821_dev *dev,
+ struct sram_channel *ch, int pix_format)
+{
+ int width = WIDTH_D1;
+ int height = dev->_lines_count_ch2;
+ int num_lines, odd_num_lines;
+ u32 value;
+ int vip_mode = PIXEL_ENGINE_VIP1;
+
+ value = ((pix_format & 0x3) << 12) | (vip_mode & 0x7);
+ value &= 0xFFFFFFEF;
+ value |= dev->_isNTSC_ch2 ? 0 : 0x10;
+ cx_write(ch->vid_fmt_ctl, value);
+
+ /*
+ * set number of active pixels in each line. Default is 720
+ * pixels in both NTSC and PAL format
+ */
+ cx_write(ch->vid_active_ctl1, width);
+
+ num_lines = (height / 2) & 0x3FF;
+ odd_num_lines = num_lines;
+
+ if (dev->_isNTSC_ch2)
+ odd_num_lines += 1;
+
+ value = (num_lines << 16) | odd_num_lines;
+
+ /* set number of active lines in field 0 (top) and field 1 (bottom) */
+ cx_write(ch->vid_active_ctl2, value);
+
+ cx_write(ch->vid_cdt_size, VID_CDT_SIZE >> 3);
+}
+
+int cx25821_start_video_dma_upstream_ch2(struct cx25821_dev *dev,
+ struct sram_channel *sram_ch)
+{
+ u32 tmp = 0;
+ int err = 0;
+
+ /*
+ * 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface
+ * for channel A-C
+ */
+ tmp = cx_read(VID_CH_MODE_SEL);
+ cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF);
+
+ /*
+ * Set the physical start address of the RISC program in the initial
+ * program counter(IPC) member of the cmds.
+ */
+ cx_write(sram_ch->cmds_start + 0, dev->_dma_phys_addr_ch2);
+ cx_write(sram_ch->cmds_start + 4, 0); /* Risc IPC High 64 bits 63-32 */
+
+ /* reset counter */
+ cx_write(sram_ch->gpcnt_ctl, 3);
+
+ /* Clear our bits from the interrupt status register. */
+ cx_write(sram_ch->int_stat, _intr_msk);
+
+ /* Set the interrupt mask register, enable irq. */
+ cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit));
+ tmp = cx_read(sram_ch->int_msk);
+ cx_write(sram_ch->int_msk, tmp |= _intr_msk);
+
+ err = request_irq(dev->pci->irq, cx25821_upstream_irq_ch2,
+ IRQF_SHARED, dev->name, dev);
+ if (err < 0) {
+ pr_err("%s: can't get upstream IRQ %d\n",
+ dev->name, dev->pci->irq);
+ goto fail_irq;
+ }
+ /* Start the DMA engine */
+ tmp = cx_read(sram_ch->dma_ctl);
+ cx_set(sram_ch->dma_ctl, tmp | FLD_VID_RISC_EN);
+
+ dev->_is_running_ch2 = 1;
+ dev->_is_first_frame_ch2 = 1;
+
+ return 0;
+
+fail_irq:
+ cx25821_dev_unregister(dev);
+ return err;
+}
+
+int cx25821_vidupstream_init_ch2(struct cx25821_dev *dev, int channel_select,
+ int pixel_format)
+{
+ struct sram_channel *sram_ch;
+ u32 tmp;
+ int retval = 0;
+ int err = 0;
+ int data_frame_size = 0;
+ int risc_buffer_size = 0;
+ int str_length = 0;
+
+ if (dev->_is_running_ch2) {
+ pr_info("Video Channel is still running so return!\n");
+ return 0;
+ }
+
+ dev->_channel2_upstream_select = channel_select;
+ sram_ch = dev->channels[channel_select].sram_channels;
+
+ INIT_WORK(&dev->_irq_work_entry_ch2, cx25821_vidups_handler_ch2);
+ dev->_irq_queues_ch2 =
+ create_singlethread_workqueue("cx25821_workqueue2");
+
+ if (!dev->_irq_queues_ch2) {
+ pr_err("create_singlethread_workqueue() for Video FAILED!\n");
+ return -ENOMEM;
+ }
+ /*
+ * 656/VIP SRC Upstream Channel I & J and 7 -
+ * Host Bus Interface for channel A-C
+ */
+ tmp = cx_read(VID_CH_MODE_SEL);
+ cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF);
+
+ dev->_is_running_ch2 = 0;
+ dev->_frame_count_ch2 = 0;
+ dev->_file_status_ch2 = RESET_STATUS;
+ dev->_lines_count_ch2 = dev->_isNTSC_ch2 ? 480 : 576;
+ dev->_pixel_format_ch2 = pixel_format;
+ dev->_line_size_ch2 = (dev->_pixel_format_ch2 == PIXEL_FRMT_422) ?
+ (WIDTH_D1 * 2) : (WIDTH_D1 * 3) / 2;
+ data_frame_size = dev->_isNTSC_ch2 ? NTSC_DATA_BUF_SZ : PAL_DATA_BUF_SZ;
+ risc_buffer_size = dev->_isNTSC_ch2 ?
+ NTSC_RISC_BUF_SIZE : PAL_RISC_BUF_SIZE;
+
+ if (dev->input_filename_ch2) {
+ str_length = strlen(dev->input_filename_ch2);
+ dev->_filename_ch2 = kmemdup(dev->input_filename_ch2,
+ str_length + 1, GFP_KERNEL);
+
+ if (!dev->_filename_ch2)
+ goto error;
+ } else {
+ str_length = strlen(dev->_defaultname_ch2);
+ dev->_filename_ch2 = kmemdup(dev->_defaultname_ch2,
+ str_length + 1, GFP_KERNEL);
+
+ if (!dev->_filename_ch2)
+ goto error;
+ }
+
+ /* Default if filename is empty string */
+ if (strcmp(dev->input_filename_ch2, "") == 0) {
+ if (dev->_isNTSC_ch2) {
+ dev->_filename_ch2 = (dev->_pixel_format_ch2 ==
+ PIXEL_FRMT_411) ? "/root/vid411.yuv" :
+ "/root/vidtest.yuv";
+ } else {
+ dev->_filename_ch2 = (dev->_pixel_format_ch2 ==
+ PIXEL_FRMT_411) ? "/root/pal411.yuv" :
+ "/root/pal422.yuv";
+ }
+ }
+
+ retval = cx25821_sram_channel_setup_upstream(dev, sram_ch,
+ dev->_line_size_ch2, 0);
+
+ /* setup fifo + format */
+ cx25821_set_pixelengine_ch2(dev, sram_ch, dev->_pixel_format_ch2);
+
+ dev->upstream_riscbuf_size_ch2 = risc_buffer_size * 2;
+ dev->upstream_databuf_size_ch2 = data_frame_size * 2;
+
+ /* Allocating buffers and prepare RISC program */
+ retval = cx25821_upstream_buffer_prepare_ch2(dev, sram_ch,
+ dev->_line_size_ch2);
+ if (retval < 0) {
+ pr_err("%s: Failed to set up Video upstream buffers!\n",
+ dev->name);
+ goto error;
+ }
+
+ cx25821_start_video_dma_upstream_ch2(dev, sram_ch);
+
+ return 0;
+
+error:
+ cx25821_dev_unregister(dev);
+
+ return err;
+}
diff --git a/drivers/media/video/cx25821/cx25821-video-upstream-ch2.h b/drivers/media/video/cx25821/cx25821-video-upstream-ch2.h
new file mode 100644
index 00000000..d42dab59
--- /dev/null
+++ b/drivers/media/video/cx25821/cx25821-video-upstream-ch2.h
@@ -0,0 +1,138 @@
+/*
+ * Driver for the Conexant CX25821 PCIe bridge
+ *
+ * Copyright (C) 2009 Conexant Systems Inc.
+ * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+
+#define OPEN_FILE_1 0
+#define NUM_PROGS 8
+#define NUM_FRAMES 2
+#define ODD_FIELD 0
+#define EVEN_FIELD 1
+#define TOP_OFFSET 0
+#define FIFO_DISABLE 0
+#define FIFO_ENABLE 1
+#define TEST_FRAMES 5
+#define END_OF_FILE 0
+#define IN_PROGRESS 1
+#define RESET_STATUS -1
+#define NUM_NO_OPS 5
+
+/* PAL and NTSC line sizes and number of lines. */
+#define WIDTH_D1 720
+#define NTSC_LINES_PER_FRAME 480
+#define PAL_LINES_PER_FRAME 576
+#define PAL_LINE_SZ 1440
+#define Y422_LINE_SZ 1440
+#define Y411_LINE_SZ 1080
+#define NTSC_FIELD_HEIGHT 240
+#define NTSC_ODD_FLD_LINES 241
+#define PAL_FIELD_HEIGHT 288
+
+#define FRAME_SIZE_NTSC_Y422 (NTSC_LINES_PER_FRAME * Y422_LINE_SZ)
+#define FRAME_SIZE_NTSC_Y411 (NTSC_LINES_PER_FRAME * Y411_LINE_SZ)
+#define FRAME_SIZE_PAL_Y422 (PAL_LINES_PER_FRAME * Y422_LINE_SZ)
+#define FRAME_SIZE_PAL_Y411 (PAL_LINES_PER_FRAME * Y411_LINE_SZ)
+
+#define NTSC_DATA_BUF_SZ (Y422_LINE_SZ * NTSC_LINES_PER_FRAME)
+#define PAL_DATA_BUF_SZ (Y422_LINE_SZ * PAL_LINES_PER_FRAME)
+
+#define RISC_WRITECR_INSTRUCTION_SIZE 16
+#define RISC_SYNC_INSTRUCTION_SIZE 4
+#define JUMP_INSTRUCTION_SIZE 12
+#define MAXSIZE_NO_OPS 36
+#define DWORD_SIZE 4
+
+#define USE_RISC_NOOP_VIDEO 1
+
+#ifdef USE_RISC_NOOP_VIDEO
+#define PAL_US_VID_PROG_SIZE \
+ (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \
+ RISC_WRITECR_INSTRUCTION_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \
+ NUM_NO_OPS * DWORD_SIZE)
+
+#define PAL_RISC_BUF_SIZE (2 * PAL_US_VID_PROG_SIZE)
+
+#define PAL_VID_PROG_SIZE \
+ ((PAL_FIELD_HEIGHT * 2) * 3 * DWORD_SIZE + \
+ 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
+ JUMP_INSTRUCTION_SIZE + 2 * NUM_NO_OPS * DWORD_SIZE)
+
+#define ODD_FLD_PAL_PROG_SIZE \
+ (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \
+ RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
+ NUM_NO_OPS * DWORD_SIZE)
+
+#define NTSC_US_VID_PROG_SIZE \
+ ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + \
+ RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE + \
+ NUM_NO_OPS * DWORD_SIZE)
+
+#define NTSC_RISC_BUF_SIZE \
+ (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE))
+
+#define FRAME1_VID_PROG_SIZE \
+ ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * \
+ 3 * DWORD_SIZE + 2 * RISC_SYNC_INSTRUCTION_SIZE + \
+ RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE + \
+ 2 * NUM_NO_OPS * DWORD_SIZE)
+
+#define ODD_FLD_NTSC_PROG_SIZE \
+ (NTSC_ODD_FLD_LINES * 3 * DWORD_SIZE + \
+ RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
+ NUM_NO_OPS * DWORD_SIZE)
+#endif
+
+#ifndef USE_RISC_NOOP_VIDEO
+#define PAL_US_VID_PROG_SIZE \
+ ((PAL_FIELD_HEIGHT + 1) * 3 * DWORD_SIZE + \
+ RISC_WRITECR_INSTRUCTION_SIZE)
+
+#define PAL_RISC_BUF_SIZE \
+ (2 * (RISC_SYNC_INSTRUCTION_SIZE + PAL_US_VID_PROG_SIZE))
+
+#define PAL_VID_PROG_SIZE \
+ ((PAL_FIELD_HEIGHT * 2) * 3 * DWORD_SIZE + \
+ 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
+ JUMP_INSTRUCTION_SIZE)
+
+#define ODD_FLD_PAL_PROG_SIZE \
+ (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \
+ RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE)
+
+#define ODD_FLD_NTSC_PROG_SIZE \
+ (NTSC_ODD_FLD_LINES * 3 * DWORD_SIZE + \
+ RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE)
+
+#define NTSC_US_VID_PROG_SIZE \
+ ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + \
+ RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE)
+
+#define NTSC_RISC_BUF_SIZE \
+ (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE))
+
+#define FRAME1_VID_PROG_SIZE \
+ ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * \
+ 3 * DWORD_SIZE + 2 * RISC_SYNC_INSTRUCTION_SIZE + \
+ RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE)
+
+#endif
diff --git a/drivers/media/video/cx25821/cx25821-video-upstream.c b/drivers/media/video/cx25821/cx25821-video-upstream.c
new file mode 100644
index 00000000..21e7d657
--- /dev/null
+++ b/drivers/media/video/cx25821/cx25821-video-upstream.c
@@ -0,0 +1,857 @@
+/*
+ * Driver for the Conexant CX25821 PCIe bridge
+ *
+ * Copyright (C) 2009 Conexant Systems Inc.
+ * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "cx25821-video.h"
+#include "cx25821-video-upstream.h"
+
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/syscalls.h>
+#include <linux/file.h>
+#include <linux/fcntl.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards");
+MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>");
+MODULE_LICENSE("GPL");
+
+static int _intr_msk = FLD_VID_SRC_RISC1 | FLD_VID_SRC_UF | FLD_VID_SRC_SYNC |
+ FLD_VID_SRC_OPC_ERR;
+
+int cx25821_sram_channel_setup_upstream(struct cx25821_dev *dev,
+ struct sram_channel *ch,
+ unsigned int bpl, u32 risc)
+{
+ unsigned int i, lines;
+ u32 cdt;
+
+ if (ch->cmds_start == 0) {
+ cx_write(ch->ptr1_reg, 0);
+ cx_write(ch->ptr2_reg, 0);
+ cx_write(ch->cnt2_reg, 0);
+ cx_write(ch->cnt1_reg, 0);
+ return 0;
+ }
+
+ bpl = (bpl + 7) & ~7; /* alignment */
+ cdt = ch->cdt;
+ lines = ch->fifo_size / bpl;
+
+ if (lines > 4)
+ lines = 4;
+
+ BUG_ON(lines < 2);
+
+ /* write CDT */
+ for (i = 0; i < lines; i++) {
+ cx_write(cdt + 16 * i, ch->fifo_start + bpl * i);
+ cx_write(cdt + 16 * i + 4, 0);
+ cx_write(cdt + 16 * i + 8, 0);
+ cx_write(cdt + 16 * i + 12, 0);
+ }
+
+ /* write CMDS */
+ cx_write(ch->cmds_start + 0, risc);
+
+ cx_write(ch->cmds_start + 4, 0);
+ cx_write(ch->cmds_start + 8, cdt);
+ cx_write(ch->cmds_start + 12, (lines * 16) >> 3);
+ cx_write(ch->cmds_start + 16, ch->ctrl_start);
+
+ cx_write(ch->cmds_start + 20, VID_IQ_SIZE_DW);
+
+ for (i = 24; i < 80; i += 4)
+ cx_write(ch->cmds_start + i, 0);
+
+ /* fill registers */
+ cx_write(ch->ptr1_reg, ch->fifo_start);
+ cx_write(ch->ptr2_reg, cdt);
+ cx_write(ch->cnt2_reg, (lines * 16) >> 3);
+ cx_write(ch->cnt1_reg, (bpl >> 3) - 1);
+
+ return 0;
+}
+
+static __le32 *cx25821_update_riscprogram(struct cx25821_dev *dev,
+ __le32 *rp, unsigned int offset,
+ unsigned int bpl, u32 sync_line,
+ unsigned int lines, int fifo_enable,
+ int field_type)
+{
+ unsigned int line, i;
+ int dist_betwn_starts = bpl * 2;
+
+ *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
+
+ if (USE_RISC_NOOP_VIDEO) {
+ for (i = 0; i < NUM_NO_OPS; i++)
+ *(rp++) = cpu_to_le32(RISC_NOOP);
+ }
+
+ /* scan lines */
+ for (line = 0; line < lines; line++) {
+ *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl);
+ *(rp++) = cpu_to_le32(dev->_data_buf_phys_addr + offset);
+ *(rp++) = cpu_to_le32(0); /* bits 63-32 */
+
+ if ((lines <= NTSC_FIELD_HEIGHT)
+ || (line < (NTSC_FIELD_HEIGHT - 1)) || !(dev->_isNTSC)) {
+ offset += dist_betwn_starts;
+ }
+ }
+
+ return rp;
+}
+
+static __le32 *cx25821_risc_field_upstream(struct cx25821_dev *dev, __le32 * rp,
+ dma_addr_t databuf_phys_addr,
+ unsigned int offset, u32 sync_line,
+ unsigned int bpl, unsigned int lines,
+ int fifo_enable, int field_type)
+{
+ unsigned int line, i;
+ struct sram_channel *sram_ch =
+ dev->channels[dev->_channel_upstream_select].sram_channels;
+ int dist_betwn_starts = bpl * 2;
+
+ /* sync instruction */
+ if (sync_line != NO_SYNC_LINE)
+ *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
+
+ if (USE_RISC_NOOP_VIDEO) {
+ for (i = 0; i < NUM_NO_OPS; i++)
+ *(rp++) = cpu_to_le32(RISC_NOOP);
+ }
+
+ /* scan lines */
+ for (line = 0; line < lines; line++) {
+ *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl);
+ *(rp++) = cpu_to_le32(databuf_phys_addr + offset);
+ *(rp++) = cpu_to_le32(0); /* bits 63-32 */
+
+ if ((lines <= NTSC_FIELD_HEIGHT)
+ || (line < (NTSC_FIELD_HEIGHT - 1)) || !(dev->_isNTSC))
+ /* to skip the other field line */
+ offset += dist_betwn_starts;
+
+ /* check if we need to enable the FIFO after the first 4 lines
+ * For the upstream video channel, the risc engine will enable
+ * the FIFO. */
+ if (fifo_enable && line == 3) {
+ *(rp++) = RISC_WRITECR;
+ *(rp++) = sram_ch->dma_ctl;
+ *(rp++) = FLD_VID_FIFO_EN;
+ *(rp++) = 0x00000001;
+ }
+ }
+
+ return rp;
+}
+
+int cx25821_risc_buffer_upstream(struct cx25821_dev *dev,
+ struct pci_dev *pci,
+ unsigned int top_offset,
+ unsigned int bpl, unsigned int lines)
+{
+ __le32 *rp;
+ int fifo_enable = 0;
+ /* get line count for single field */
+ int singlefield_lines = lines >> 1;
+ int odd_num_lines = singlefield_lines;
+ int frame = 0;
+ int frame_size = 0;
+ int databuf_offset = 0;
+ int risc_program_size = 0;
+ int risc_flag = RISC_CNT_RESET;
+ unsigned int bottom_offset = bpl;
+ dma_addr_t risc_phys_jump_addr;
+
+ if (dev->_isNTSC) {
+ odd_num_lines = singlefield_lines + 1;
+ risc_program_size = FRAME1_VID_PROG_SIZE;
+ frame_size = (bpl == Y411_LINE_SZ) ?
+ FRAME_SIZE_NTSC_Y411 : FRAME_SIZE_NTSC_Y422;
+ } else {
+ risc_program_size = PAL_VID_PROG_SIZE;
+ frame_size = (bpl == Y411_LINE_SZ) ?
+ FRAME_SIZE_PAL_Y411 : FRAME_SIZE_PAL_Y422;
+ }
+
+ /* Virtual address of Risc buffer program */
+ rp = dev->_dma_virt_addr;
+
+ for (frame = 0; frame < NUM_FRAMES; frame++) {
+ databuf_offset = frame_size * frame;
+
+ if (UNSET != top_offset) {
+ fifo_enable = (frame == 0) ? FIFO_ENABLE : FIFO_DISABLE;
+ rp = cx25821_risc_field_upstream(dev, rp,
+ dev->_data_buf_phys_addr +
+ databuf_offset, top_offset, 0, bpl,
+ odd_num_lines, fifo_enable, ODD_FIELD);
+ }
+
+ fifo_enable = FIFO_DISABLE;
+
+ /* Even Field */
+ rp = cx25821_risc_field_upstream(dev, rp,
+ dev->_data_buf_phys_addr +
+ databuf_offset, bottom_offset,
+ 0x200, bpl, singlefield_lines,
+ fifo_enable, EVEN_FIELD);
+
+ if (frame == 0) {
+ risc_flag = RISC_CNT_RESET;
+ risc_phys_jump_addr = dev->_dma_phys_start_addr +
+ risc_program_size;
+ } else {
+ risc_phys_jump_addr = dev->_dma_phys_start_addr;
+ risc_flag = RISC_CNT_INC;
+ }
+
+ /* Loop to 2ndFrameRISC or to Start of Risc
+ * program & generate IRQ
+ */
+ *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | risc_flag);
+ *(rp++) = cpu_to_le32(risc_phys_jump_addr);
+ *(rp++) = cpu_to_le32(0);
+ }
+
+ return 0;
+}
+
+void cx25821_stop_upstream_video_ch1(struct cx25821_dev *dev)
+{
+ struct sram_channel *sram_ch =
+ dev->channels[VID_UPSTREAM_SRAM_CHANNEL_I].sram_channels;
+ u32 tmp = 0;
+
+ if (!dev->_is_running) {
+ pr_info("No video file is currently running so return!\n");
+ return;
+ }
+ /* Disable RISC interrupts */
+ tmp = cx_read(sram_ch->int_msk);
+ cx_write(sram_ch->int_msk, tmp & ~_intr_msk);
+
+ /* Turn OFF risc and fifo enable */
+ tmp = cx_read(sram_ch->dma_ctl);
+ cx_write(sram_ch->dma_ctl, tmp & ~(FLD_VID_FIFO_EN | FLD_VID_RISC_EN));
+
+ /* Clear data buffer memory */
+ if (dev->_data_buf_virt_addr)
+ memset(dev->_data_buf_virt_addr, 0, dev->_data_buf_size);
+
+ dev->_is_running = 0;
+ dev->_is_first_frame = 0;
+ dev->_frame_count = 0;
+ dev->_file_status = END_OF_FILE;
+
+ kfree(dev->_irq_queues);
+ dev->_irq_queues = NULL;
+
+ kfree(dev->_filename);
+
+ tmp = cx_read(VID_CH_MODE_SEL);
+ cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00);
+}
+
+void cx25821_free_mem_upstream_ch1(struct cx25821_dev *dev)
+{
+ if (dev->_is_running)
+ cx25821_stop_upstream_video_ch1(dev);
+
+ if (dev->_dma_virt_addr) {
+ pci_free_consistent(dev->pci, dev->_risc_size,
+ dev->_dma_virt_addr, dev->_dma_phys_addr);
+ dev->_dma_virt_addr = NULL;
+ }
+
+ if (dev->_data_buf_virt_addr) {
+ pci_free_consistent(dev->pci, dev->_data_buf_size,
+ dev->_data_buf_virt_addr,
+ dev->_data_buf_phys_addr);
+ dev->_data_buf_virt_addr = NULL;
+ }
+}
+
+int cx25821_get_frame(struct cx25821_dev *dev, struct sram_channel *sram_ch)
+{
+ struct file *myfile;
+ int frame_index_temp = dev->_frame_index;
+ int i = 0;
+ int line_size = (dev->_pixel_format == PIXEL_FRMT_411) ?
+ Y411_LINE_SZ : Y422_LINE_SZ;
+ int frame_size = 0;
+ int frame_offset = 0;
+ ssize_t vfs_read_retval = 0;
+ char mybuf[line_size];
+ loff_t file_offset;
+ loff_t pos;
+ mm_segment_t old_fs;
+
+ if (dev->_file_status == END_OF_FILE)
+ return 0;
+
+ if (dev->_isNTSC)
+ frame_size = (line_size == Y411_LINE_SZ) ?
+ FRAME_SIZE_NTSC_Y411 : FRAME_SIZE_NTSC_Y422;
+ else
+ frame_size = (line_size == Y411_LINE_SZ) ?
+ FRAME_SIZE_PAL_Y411 : FRAME_SIZE_PAL_Y422;
+
+ frame_offset = (frame_index_temp > 0) ? frame_size : 0;
+ file_offset = dev->_frame_count * frame_size;
+
+ myfile = filp_open(dev->_filename, O_RDONLY | O_LARGEFILE, 0);
+
+ if (IS_ERR(myfile)) {
+ const int open_errno = -PTR_ERR(myfile);
+ pr_err("%s(): ERROR opening file(%s) with errno = %d!\n",
+ __func__, dev->_filename, open_errno);
+ return PTR_ERR(myfile);
+ } else {
+ if (!(myfile->f_op)) {
+ pr_err("%s(): File has no file operations registered!\n",
+ __func__);
+ filp_close(myfile, NULL);
+ return -EIO;
+ }
+
+ if (!myfile->f_op->read) {
+ pr_err("%s(): File has no READ operations registered!\n",
+ __func__);
+ filp_close(myfile, NULL);
+ return -EIO;
+ }
+
+ pos = myfile->f_pos;
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ for (i = 0; i < dev->_lines_count; i++) {
+ pos = file_offset;
+
+ vfs_read_retval = vfs_read(myfile, mybuf, line_size,
+ &pos);
+
+ if (vfs_read_retval > 0 && vfs_read_retval == line_size
+ && dev->_data_buf_virt_addr != NULL) {
+ memcpy((void *)(dev->_data_buf_virt_addr +
+ frame_offset / 4), mybuf,
+ vfs_read_retval);
+ }
+
+ file_offset += vfs_read_retval;
+ frame_offset += vfs_read_retval;
+
+ if (vfs_read_retval < line_size) {
+ pr_info("Done: exit %s() since no more bytes to read from Video file\n",
+ __func__);
+ break;
+ }
+ }
+
+ if (i > 0)
+ dev->_frame_count++;
+
+ dev->_file_status = (vfs_read_retval == line_size) ?
+ IN_PROGRESS : END_OF_FILE;
+
+ set_fs(old_fs);
+ filp_close(myfile, NULL);
+ }
+
+ return 0;
+}
+
+static void cx25821_vidups_handler(struct work_struct *work)
+{
+ struct cx25821_dev *dev = container_of(work, struct cx25821_dev,
+ _irq_work_entry);
+
+ if (!dev) {
+ pr_err("ERROR %s(): since container_of(work_struct) FAILED!\n",
+ __func__);
+ return;
+ }
+
+ cx25821_get_frame(dev, dev->channels[dev->_channel_upstream_select].
+ sram_channels);
+}
+
+int cx25821_openfile(struct cx25821_dev *dev, struct sram_channel *sram_ch)
+{
+ struct file *myfile;
+ int i = 0, j = 0;
+ int line_size = (dev->_pixel_format == PIXEL_FRMT_411) ?
+ Y411_LINE_SZ : Y422_LINE_SZ;
+ ssize_t vfs_read_retval = 0;
+ char mybuf[line_size];
+ loff_t pos;
+ loff_t offset = (unsigned long)0;
+ mm_segment_t old_fs;
+
+ myfile = filp_open(dev->_filename, O_RDONLY | O_LARGEFILE, 0);
+
+ if (IS_ERR(myfile)) {
+ const int open_errno = -PTR_ERR(myfile);
+ pr_err("%s(): ERROR opening file(%s) with errno = %d!\n",
+ __func__, dev->_filename, open_errno);
+ return PTR_ERR(myfile);
+ } else {
+ if (!(myfile->f_op)) {
+ pr_err("%s(): File has no file operations registered!\n",
+ __func__);
+ filp_close(myfile, NULL);
+ return -EIO;
+ }
+
+ if (!myfile->f_op->read) {
+ pr_err("%s(): File has no READ operations registered! Returning\n",
+ __func__);
+ filp_close(myfile, NULL);
+ return -EIO;
+ }
+
+ pos = myfile->f_pos;
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ for (j = 0; j < NUM_FRAMES; j++) {
+ for (i = 0; i < dev->_lines_count; i++) {
+ pos = offset;
+
+ vfs_read_retval = vfs_read(myfile, mybuf,
+ line_size, &pos);
+
+ if (vfs_read_retval > 0
+ && vfs_read_retval == line_size
+ && dev->_data_buf_virt_addr != NULL) {
+ memcpy((void *)(dev->
+ _data_buf_virt_addr +
+ offset / 4), mybuf,
+ vfs_read_retval);
+ }
+
+ offset += vfs_read_retval;
+
+ if (vfs_read_retval < line_size) {
+ pr_info("Done: exit %s() since no more bytes to read from Video file\n",
+ __func__);
+ break;
+ }
+ }
+
+ if (i > 0)
+ dev->_frame_count++;
+
+ if (vfs_read_retval < line_size)
+ break;
+ }
+
+ dev->_file_status = (vfs_read_retval == line_size) ?
+ IN_PROGRESS : END_OF_FILE;
+
+ set_fs(old_fs);
+ myfile->f_pos = 0;
+ filp_close(myfile, NULL);
+ }
+
+ return 0;
+}
+
+int cx25821_upstream_buffer_prepare(struct cx25821_dev *dev,
+ struct sram_channel *sram_ch, int bpl)
+{
+ int ret = 0;
+ dma_addr_t dma_addr;
+ dma_addr_t data_dma_addr;
+
+ if (dev->_dma_virt_addr != NULL)
+ pci_free_consistent(dev->pci, dev->upstream_riscbuf_size,
+ dev->_dma_virt_addr, dev->_dma_phys_addr);
+
+ dev->_dma_virt_addr = pci_alloc_consistent(dev->pci,
+ dev->upstream_riscbuf_size, &dma_addr);
+ dev->_dma_virt_start_addr = dev->_dma_virt_addr;
+ dev->_dma_phys_start_addr = dma_addr;
+ dev->_dma_phys_addr = dma_addr;
+ dev->_risc_size = dev->upstream_riscbuf_size;
+
+ if (!dev->_dma_virt_addr) {
+ pr_err("FAILED to allocate memory for Risc buffer! Returning\n");
+ return -ENOMEM;
+ }
+
+ /* Clear memory at address */
+ memset(dev->_dma_virt_addr, 0, dev->_risc_size);
+
+ if (dev->_data_buf_virt_addr != NULL)
+ pci_free_consistent(dev->pci, dev->upstream_databuf_size,
+ dev->_data_buf_virt_addr,
+ dev->_data_buf_phys_addr);
+ /* For Video Data buffer allocation */
+ dev->_data_buf_virt_addr = pci_alloc_consistent(dev->pci,
+ dev->upstream_databuf_size, &data_dma_addr);
+ dev->_data_buf_phys_addr = data_dma_addr;
+ dev->_data_buf_size = dev->upstream_databuf_size;
+
+ if (!dev->_data_buf_virt_addr) {
+ pr_err("FAILED to allocate memory for data buffer! Returning\n");
+ return -ENOMEM;
+ }
+
+ /* Clear memory at address */
+ memset(dev->_data_buf_virt_addr, 0, dev->_data_buf_size);
+
+ ret = cx25821_openfile(dev, sram_ch);
+ if (ret < 0)
+ return ret;
+
+ /* Create RISC programs */
+ ret = cx25821_risc_buffer_upstream(dev, dev->pci, 0, bpl,
+ dev->_lines_count);
+ if (ret < 0) {
+ pr_info("Failed creating Video Upstream Risc programs!\n");
+ goto error;
+ }
+
+ return 0;
+
+error:
+ return ret;
+}
+
+int cx25821_video_upstream_irq(struct cx25821_dev *dev, int chan_num,
+ u32 status)
+{
+ u32 int_msk_tmp;
+ struct sram_channel *channel = dev->channels[chan_num].sram_channels;
+ int singlefield_lines = NTSC_FIELD_HEIGHT;
+ int line_size_in_bytes = Y422_LINE_SZ;
+ int odd_risc_prog_size = 0;
+ dma_addr_t risc_phys_jump_addr;
+ __le32 *rp;
+
+ if (status & FLD_VID_SRC_RISC1) {
+ /* We should only process one program per call */
+ u32 prog_cnt = cx_read(channel->gpcnt);
+
+ /* Since we've identified our IRQ, clear our bits from the
+ * interrupt mask and interrupt status registers */
+ int_msk_tmp = cx_read(channel->int_msk);
+ cx_write(channel->int_msk, int_msk_tmp & ~_intr_msk);
+ cx_write(channel->int_stat, _intr_msk);
+
+ spin_lock(&dev->slock);
+
+ dev->_frame_index = prog_cnt;
+
+ queue_work(dev->_irq_queues, &dev->_irq_work_entry);
+
+ if (dev->_is_first_frame) {
+ dev->_is_first_frame = 0;
+
+ if (dev->_isNTSC) {
+ singlefield_lines += 1;
+ odd_risc_prog_size = ODD_FLD_NTSC_PROG_SIZE;
+ } else {
+ singlefield_lines = PAL_FIELD_HEIGHT;
+ odd_risc_prog_size = ODD_FLD_PAL_PROG_SIZE;
+ }
+
+ if (dev->_dma_virt_start_addr != NULL) {
+ line_size_in_bytes =
+ (dev->_pixel_format ==
+ PIXEL_FRMT_411) ? Y411_LINE_SZ :
+ Y422_LINE_SZ;
+ risc_phys_jump_addr =
+ dev->_dma_phys_start_addr +
+ odd_risc_prog_size;
+
+ rp = cx25821_update_riscprogram(dev,
+ dev->_dma_virt_start_addr, TOP_OFFSET,
+ line_size_in_bytes, 0x0,
+ singlefield_lines, FIFO_DISABLE,
+ ODD_FIELD);
+
+ /* Jump to Even Risc program of 1st Frame */
+ *(rp++) = cpu_to_le32(RISC_JUMP);
+ *(rp++) = cpu_to_le32(risc_phys_jump_addr);
+ *(rp++) = cpu_to_le32(0);
+ }
+ }
+
+ spin_unlock(&dev->slock);
+ } else {
+ if (status & FLD_VID_SRC_UF)
+ pr_err("%s(): Video Received Underflow Error Interrupt!\n",
+ __func__);
+
+ if (status & FLD_VID_SRC_SYNC)
+ pr_err("%s(): Video Received Sync Error Interrupt!\n",
+ __func__);
+
+ if (status & FLD_VID_SRC_OPC_ERR)
+ pr_err("%s(): Video Received OpCode Error Interrupt!\n",
+ __func__);
+ }
+
+ if (dev->_file_status == END_OF_FILE) {
+ pr_err("EOF Channel 1 Framecount = %d\n", dev->_frame_count);
+ return -1;
+ }
+ /* ElSE, set the interrupt mask register, re-enable irq. */
+ int_msk_tmp = cx_read(channel->int_msk);
+ cx_write(channel->int_msk, int_msk_tmp |= _intr_msk);
+
+ return 0;
+}
+
+static irqreturn_t cx25821_upstream_irq(int irq, void *dev_id)
+{
+ struct cx25821_dev *dev = dev_id;
+ u32 msk_stat, vid_status;
+ int handled = 0;
+ int channel_num = 0;
+ struct sram_channel *sram_ch;
+
+ if (!dev)
+ return -1;
+
+ channel_num = VID_UPSTREAM_SRAM_CHANNEL_I;
+
+ sram_ch = dev->channels[channel_num].sram_channels;
+
+ msk_stat = cx_read(sram_ch->int_mstat);
+ vid_status = cx_read(sram_ch->int_stat);
+
+ /* Only deal with our interrupt */
+ if (vid_status)
+ handled = cx25821_video_upstream_irq(dev, channel_num,
+ vid_status);
+
+ if (handled < 0)
+ cx25821_stop_upstream_video_ch1(dev);
+ else
+ handled += handled;
+
+ return IRQ_RETVAL(handled);
+}
+
+void cx25821_set_pixelengine(struct cx25821_dev *dev, struct sram_channel *ch,
+ int pix_format)
+{
+ int width = WIDTH_D1;
+ int height = dev->_lines_count;
+ int num_lines, odd_num_lines;
+ u32 value;
+ int vip_mode = OUTPUT_FRMT_656;
+
+ value = ((pix_format & 0x3) << 12) | (vip_mode & 0x7);
+ value &= 0xFFFFFFEF;
+ value |= dev->_isNTSC ? 0 : 0x10;
+ cx_write(ch->vid_fmt_ctl, value);
+
+ /* set number of active pixels in each line.
+ * Default is 720 pixels in both NTSC and PAL format */
+ cx_write(ch->vid_active_ctl1, width);
+
+ num_lines = (height / 2) & 0x3FF;
+ odd_num_lines = num_lines;
+
+ if (dev->_isNTSC)
+ odd_num_lines += 1;
+
+ value = (num_lines << 16) | odd_num_lines;
+
+ /* set number of active lines in field 0 (top) and field 1 (bottom) */
+ cx_write(ch->vid_active_ctl2, value);
+
+ cx_write(ch->vid_cdt_size, VID_CDT_SIZE >> 3);
+}
+
+int cx25821_start_video_dma_upstream(struct cx25821_dev *dev,
+ struct sram_channel *sram_ch)
+{
+ u32 tmp = 0;
+ int err = 0;
+
+ /* 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for
+ * channel A-C
+ */
+ tmp = cx_read(VID_CH_MODE_SEL);
+ cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF);
+
+ /* Set the physical start address of the RISC program in the initial
+ * program counter(IPC) member of the cmds.
+ */
+ cx_write(sram_ch->cmds_start + 0, dev->_dma_phys_addr);
+ /* Risc IPC High 64 bits 63-32 */
+ cx_write(sram_ch->cmds_start + 4, 0);
+
+ /* reset counter */
+ cx_write(sram_ch->gpcnt_ctl, 3);
+
+ /* Clear our bits from the interrupt status register. */
+ cx_write(sram_ch->int_stat, _intr_msk);
+
+ /* Set the interrupt mask register, enable irq. */
+ cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit));
+ tmp = cx_read(sram_ch->int_msk);
+ cx_write(sram_ch->int_msk, tmp |= _intr_msk);
+
+ err = request_irq(dev->pci->irq, cx25821_upstream_irq,
+ IRQF_SHARED, dev->name, dev);
+ if (err < 0) {
+ pr_err("%s: can't get upstream IRQ %d\n",
+ dev->name, dev->pci->irq);
+ goto fail_irq;
+ }
+
+ /* Start the DMA engine */
+ tmp = cx_read(sram_ch->dma_ctl);
+ cx_set(sram_ch->dma_ctl, tmp | FLD_VID_RISC_EN);
+
+ dev->_is_running = 1;
+ dev->_is_first_frame = 1;
+
+ return 0;
+
+fail_irq:
+ cx25821_dev_unregister(dev);
+ return err;
+}
+
+int cx25821_vidupstream_init_ch1(struct cx25821_dev *dev, int channel_select,
+ int pixel_format)
+{
+ struct sram_channel *sram_ch;
+ u32 tmp;
+ int retval = 0;
+ int err = 0;
+ int data_frame_size = 0;
+ int risc_buffer_size = 0;
+ int str_length = 0;
+
+ if (dev->_is_running) {
+ pr_info("Video Channel is still running so return!\n");
+ return 0;
+ }
+
+ dev->_channel_upstream_select = channel_select;
+ sram_ch = dev->channels[channel_select].sram_channels;
+
+ INIT_WORK(&dev->_irq_work_entry, cx25821_vidups_handler);
+ dev->_irq_queues = create_singlethread_workqueue("cx25821_workqueue");
+
+ if (!dev->_irq_queues) {
+ pr_err("create_singlethread_workqueue() for Video FAILED!\n");
+ return -ENOMEM;
+ }
+ /* 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for
+ * channel A-C
+ */
+ tmp = cx_read(VID_CH_MODE_SEL);
+ cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF);
+
+ dev->_is_running = 0;
+ dev->_frame_count = 0;
+ dev->_file_status = RESET_STATUS;
+ dev->_lines_count = dev->_isNTSC ? 480 : 576;
+ dev->_pixel_format = pixel_format;
+ dev->_line_size = (dev->_pixel_format == PIXEL_FRMT_422) ?
+ (WIDTH_D1 * 2) : (WIDTH_D1 * 3) / 2;
+ data_frame_size = dev->_isNTSC ? NTSC_DATA_BUF_SZ : PAL_DATA_BUF_SZ;
+ risc_buffer_size = dev->_isNTSC ?
+ NTSC_RISC_BUF_SIZE : PAL_RISC_BUF_SIZE;
+
+ if (dev->input_filename) {
+ str_length = strlen(dev->input_filename);
+ dev->_filename = kmemdup(dev->input_filename, str_length + 1,
+ GFP_KERNEL);
+
+ if (!dev->_filename)
+ goto error;
+ } else {
+ str_length = strlen(dev->_defaultname);
+ dev->_filename = kmemdup(dev->_defaultname, str_length + 1,
+ GFP_KERNEL);
+
+ if (!dev->_filename)
+ goto error;
+ }
+
+ /* Default if filename is empty string */
+ if (strcmp(dev->input_filename, "") == 0) {
+ if (dev->_isNTSC) {
+ dev->_filename =
+ (dev->_pixel_format == PIXEL_FRMT_411) ?
+ "/root/vid411.yuv" : "/root/vidtest.yuv";
+ } else {
+ dev->_filename =
+ (dev->_pixel_format == PIXEL_FRMT_411) ?
+ "/root/pal411.yuv" : "/root/pal422.yuv";
+ }
+ }
+
+ dev->_is_running = 0;
+ dev->_frame_count = 0;
+ dev->_file_status = RESET_STATUS;
+ dev->_lines_count = dev->_isNTSC ? 480 : 576;
+ dev->_pixel_format = pixel_format;
+ dev->_line_size = (dev->_pixel_format == PIXEL_FRMT_422) ?
+ (WIDTH_D1 * 2) : (WIDTH_D1 * 3) / 2;
+
+ retval = cx25821_sram_channel_setup_upstream(dev, sram_ch,
+ dev->_line_size, 0);
+
+ /* setup fifo + format */
+ cx25821_set_pixelengine(dev, sram_ch, dev->_pixel_format);
+
+ dev->upstream_riscbuf_size = risc_buffer_size * 2;
+ dev->upstream_databuf_size = data_frame_size * 2;
+
+ /* Allocating buffers and prepare RISC program */
+ retval = cx25821_upstream_buffer_prepare(dev, sram_ch, dev->_line_size);
+ if (retval < 0) {
+ pr_err("%s: Failed to set up Video upstream buffers!\n",
+ dev->name);
+ goto error;
+ }
+
+ cx25821_start_video_dma_upstream(dev, sram_ch);
+
+ return 0;
+
+error:
+ cx25821_dev_unregister(dev);
+
+ return err;
+}
diff --git a/drivers/media/video/cx25821/cx25821-video-upstream.h b/drivers/media/video/cx25821/cx25821-video-upstream.h
new file mode 100644
index 00000000..268ec8aa
--- /dev/null
+++ b/drivers/media/video/cx25821/cx25821-video-upstream.h
@@ -0,0 +1,139 @@
+/*
+ * Driver for the Conexant CX25821 PCIe bridge
+ *
+ * Copyright (C) 2009 Conexant Systems Inc.
+ * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+
+#define OUTPUT_FRMT_656 0
+#define OPEN_FILE_1 0
+#define NUM_PROGS 8
+#define NUM_FRAMES 2
+#define ODD_FIELD 0
+#define EVEN_FIELD 1
+#define TOP_OFFSET 0
+#define FIFO_DISABLE 0
+#define FIFO_ENABLE 1
+#define TEST_FRAMES 5
+#define END_OF_FILE 0
+#define IN_PROGRESS 1
+#define RESET_STATUS -1
+#define NUM_NO_OPS 5
+
+/* PAL and NTSC line sizes and number of lines. */
+#define WIDTH_D1 720
+#define NTSC_LINES_PER_FRAME 480
+#define PAL_LINES_PER_FRAME 576
+#define PAL_LINE_SZ 1440
+#define Y422_LINE_SZ 1440
+#define Y411_LINE_SZ 1080
+#define NTSC_FIELD_HEIGHT 240
+#define NTSC_ODD_FLD_LINES 241
+#define PAL_FIELD_HEIGHT 288
+
+#define FRAME_SIZE_NTSC_Y422 (NTSC_LINES_PER_FRAME * Y422_LINE_SZ)
+#define FRAME_SIZE_NTSC_Y411 (NTSC_LINES_PER_FRAME * Y411_LINE_SZ)
+#define FRAME_SIZE_PAL_Y422 (PAL_LINES_PER_FRAME * Y422_LINE_SZ)
+#define FRAME_SIZE_PAL_Y411 (PAL_LINES_PER_FRAME * Y411_LINE_SZ)
+
+#define NTSC_DATA_BUF_SZ (Y422_LINE_SZ * NTSC_LINES_PER_FRAME)
+#define PAL_DATA_BUF_SZ (Y422_LINE_SZ * PAL_LINES_PER_FRAME)
+
+#define RISC_WRITECR_INSTRUCTION_SIZE 16
+#define RISC_SYNC_INSTRUCTION_SIZE 4
+#define JUMP_INSTRUCTION_SIZE 12
+#define MAXSIZE_NO_OPS 36
+#define DWORD_SIZE 4
+
+#define USE_RISC_NOOP_VIDEO 1
+
+#ifdef USE_RISC_NOOP_VIDEO
+#define PAL_US_VID_PROG_SIZE \
+ (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \
+ RISC_WRITECR_INSTRUCTION_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \
+ NUM_NO_OPS * DWORD_SIZE)
+
+#define PAL_RISC_BUF_SIZE (2 * PAL_US_VID_PROG_SIZE)
+
+#define PAL_VID_PROG_SIZE \
+ ((PAL_FIELD_HEIGHT * 2) * 3 * DWORD_SIZE + \
+ 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
+ JUMP_INSTRUCTION_SIZE + 2 * NUM_NO_OPS * DWORD_SIZE)
+
+#define ODD_FLD_PAL_PROG_SIZE \
+ (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \
+ RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
+ NUM_NO_OPS * DWORD_SIZE)
+
+#define ODD_FLD_NTSC_PROG_SIZE \
+ (NTSC_ODD_FLD_LINES * 3 * DWORD_SIZE + \
+ RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
+ NUM_NO_OPS * DWORD_SIZE)
+
+#define NTSC_US_VID_PROG_SIZE \
+ ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + \
+ RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE + \
+ NUM_NO_OPS * DWORD_SIZE)
+
+#define NTSC_RISC_BUF_SIZE \
+ (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE))
+
+#define FRAME1_VID_PROG_SIZE \
+ ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * 3 * DWORD_SIZE + \
+ 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
+ JUMP_INSTRUCTION_SIZE + 2 * NUM_NO_OPS * DWORD_SIZE)
+
+#endif
+
+#ifndef USE_RISC_NOOP_VIDEO
+#define PAL_US_VID_PROG_SIZE \
+ (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \
+ RISC_WRITECR_INSTRUCTION_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \
+ JUMP_INSTRUCTION_SIZE)
+
+#define PAL_RISC_BUF_SIZE (2 * PAL_US_VID_PROG_SIZE)
+
+#define PAL_VID_PROG_SIZE \
+ ((PAL_FIELD_HEIGHT * 2) * 3 * DWORD_SIZE + \
+ 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
+ JUMP_INSTRUCTION_SIZE)
+
+#define ODD_FLD_PAL_PROG_SIZE \
+ (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \
+ RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE)
+
+#define ODD_FLD_NTSC_PROG_SIZE \
+ (NTSC_ODD_FLD_LINES * 3 * DWORD_SIZE + \
+ RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE)
+
+#define NTSC_US_VID_PROG_SIZE \
+ ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + \
+ RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE)
+
+#define NTSC_RISC_BUF_SIZE \
+ (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE))
+
+#define FRAME1_VID_PROG_SIZE \
+ ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * 3 * DWORD_SIZE + \
+ 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \
+ JUMP_INSTRUCTION_SIZE)
+
+#endif
diff --git a/drivers/media/video/cx25821/cx25821-video.c b/drivers/media/video/cx25821/cx25821-video.c
new file mode 100644
index 00000000..ffd8bc79
--- /dev/null
+++ b/drivers/media/video/cx25821/cx25821-video.c
@@ -0,0 +1,2011 @@
+/*
+ * Driver for the Conexant CX25821 PCIe bridge
+ *
+ * Copyright (C) 2009 Conexant Systems Inc.
+ * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
+ * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
+ * Parts adapted/taken from Eduardo Moscoso Rubino
+ * Copyright (C) 2009 Eduardo Moscoso Rubino <moscoso@TopoLogica.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "cx25821-video.h"
+
+MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards");
+MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>");
+MODULE_LICENSE("GPL");
+
+static unsigned int video_nr[] = {[0 ... (CX25821_MAXBOARDS - 1)] = UNSET };
+static unsigned int radio_nr[] = {[0 ... (CX25821_MAXBOARDS - 1)] = UNSET };
+
+module_param_array(video_nr, int, NULL, 0444);
+module_param_array(radio_nr, int, NULL, 0444);
+
+MODULE_PARM_DESC(video_nr, "video device numbers");
+MODULE_PARM_DESC(radio_nr, "radio device numbers");
+
+static unsigned int video_debug = VIDEO_DEBUG;
+module_param(video_debug, int, 0644);
+MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
+
+static unsigned int irq_debug;
+module_param(irq_debug, int, 0644);
+MODULE_PARM_DESC(irq_debug, "enable debug messages [IRQ handler]");
+
+unsigned int vid_limit = 16;
+module_param(vid_limit, int, 0644);
+MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
+
+static void cx25821_init_controls(struct cx25821_dev *dev, int chan_num);
+
+static const struct v4l2_file_operations video_fops;
+static const struct v4l2_ioctl_ops video_ioctl_ops;
+
+#define FORMAT_FLAGS_PACKED 0x01
+
+struct cx25821_fmt formats[] = {
+ {
+ .name = "8 bpp, gray",
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .depth = 8,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "4:1:1, packed, Y41P",
+ .fourcc = V4L2_PIX_FMT_Y41P,
+ .depth = 12,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "4:2:2, packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "4:2:0, YUV",
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .depth = 12,
+ .flags = FORMAT_FLAGS_PACKED,
+ },
+};
+
+int cx25821_get_format_size(void)
+{
+ return ARRAY_SIZE(formats);
+}
+
+struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc)
+{
+ unsigned int i;
+
+ if (fourcc == V4L2_PIX_FMT_Y41P || fourcc == V4L2_PIX_FMT_YUV411P)
+ return formats + 1;
+
+ for (i = 0; i < ARRAY_SIZE(formats); i++)
+ if (formats[i].fourcc == fourcc)
+ return formats + i;
+
+ pr_err("%s(0x%08x) NOT FOUND\n", __func__, fourcc);
+ return NULL;
+}
+
+void cx25821_dump_video_queue(struct cx25821_dev *dev,
+ struct cx25821_dmaqueue *q)
+{
+ struct cx25821_buffer *buf;
+ struct list_head *item;
+ dprintk(1, "%s()\n", __func__);
+
+ if (!list_empty(&q->active)) {
+ list_for_each(item, &q->active)
+ buf = list_entry(item, struct cx25821_buffer, vb.queue);
+ }
+
+ if (!list_empty(&q->queued)) {
+ list_for_each(item, &q->queued)
+ buf = list_entry(item, struct cx25821_buffer, vb.queue);
+ }
+
+}
+
+void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q,
+ u32 count)
+{
+ struct cx25821_buffer *buf;
+ int bc;
+
+ for (bc = 0;; bc++) {
+ if (list_empty(&q->active)) {
+ dprintk(1, "bc=%d (=0: active empty)\n", bc);
+ break;
+ }
+
+ buf = list_entry(q->active.next, struct cx25821_buffer,
+ vb.queue);
+
+ /* count comes from the hw and it is 16bit wide --
+ * this trick handles wrap-arounds correctly for
+ * up to 32767 buffers in flight... */
+ if ((s16) (count - buf->count) < 0)
+ break;
+
+ do_gettimeofday(&buf->vb.ts);
+ buf->vb.state = VIDEOBUF_DONE;
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+ }
+
+ if (list_empty(&q->active))
+ del_timer(&q->timeout);
+ else
+ mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT);
+ if (bc != 1)
+ pr_err("%s: %d buffers handled (should be 1)\n", __func__, bc);
+}
+
+#ifdef TUNER_FLAG
+int cx25821_set_tvnorm(struct cx25821_dev *dev, v4l2_std_id norm)
+{
+ dprintk(1, "%s(norm = 0x%08x) name: [%s]\n",
+ __func__, (unsigned int)norm, v4l2_norm_to_name(norm));
+
+ dev->tvnorm = norm;
+
+ /* Tell the internal A/V decoder */
+ cx25821_call_all(dev, core, s_std, norm);
+
+ return 0;
+}
+#endif
+
+struct video_device *cx25821_vdev_init(struct cx25821_dev *dev,
+ struct pci_dev *pci,
+ struct video_device *template,
+ char *type)
+{
+ struct video_device *vfd;
+ dprintk(1, "%s()\n", __func__);
+
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+ *vfd = *template;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+ vfd->release = video_device_release;
+ snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name, type,
+ cx25821_boards[dev->board].name);
+ video_set_drvdata(vfd, dev);
+ return vfd;
+}
+
+/*
+static int cx25821_ctrl_query(struct v4l2_queryctrl *qctrl)
+{
+ int i;
+
+ if (qctrl->id < V4L2_CID_BASE || qctrl->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+ for (i = 0; i < CX25821_CTLS; i++)
+ if (cx25821_ctls[i].v.id == qctrl->id)
+ break;
+ if (i == CX25821_CTLS) {
+ *qctrl = no_ctl;
+ return 0;
+ }
+ *qctrl = cx25821_ctls[i].v;
+ return 0;
+}
+*/
+
+/* resource management */
+int cx25821_res_get(struct cx25821_dev *dev, struct cx25821_fh *fh,
+ unsigned int bit)
+{
+ dprintk(1, "%s()\n", __func__);
+ if (fh->resources & bit)
+ /* have it already allocated */
+ return 1;
+
+ /* is it free? */
+ mutex_lock(&dev->lock);
+ if (dev->channels[fh->channel_id].resources & bit) {
+ /* no, someone else uses it */
+ mutex_unlock(&dev->lock);
+ return 0;
+ }
+ /* it's free, grab it */
+ fh->resources |= bit;
+ dev->channels[fh->channel_id].resources |= bit;
+ dprintk(1, "res: get %d\n", bit);
+ mutex_unlock(&dev->lock);
+ return 1;
+}
+
+int cx25821_res_check(struct cx25821_fh *fh, unsigned int bit)
+{
+ return fh->resources & bit;
+}
+
+int cx25821_res_locked(struct cx25821_fh *fh, unsigned int bit)
+{
+ return fh->dev->channels[fh->channel_id].resources & bit;
+}
+
+void cx25821_res_free(struct cx25821_dev *dev, struct cx25821_fh *fh,
+ unsigned int bits)
+{
+ BUG_ON((fh->resources & bits) != bits);
+ dprintk(1, "%s()\n", __func__);
+
+ mutex_lock(&dev->lock);
+ fh->resources &= ~bits;
+ dev->channels[fh->channel_id].resources &= ~bits;
+ dprintk(1, "res: put %d\n", bits);
+ mutex_unlock(&dev->lock);
+}
+
+int cx25821_video_mux(struct cx25821_dev *dev, unsigned int input)
+{
+ struct v4l2_routing route;
+ memset(&route, 0, sizeof(route));
+
+ dprintk(1, "%s(): video_mux: %d [vmux=%d, gpio=0x%x,0x%x,0x%x,0x%x]\n",
+ __func__, input, INPUT(input)->vmux, INPUT(input)->gpio0,
+ INPUT(input)->gpio1, INPUT(input)->gpio2, INPUT(input)->gpio3);
+ dev->input = input;
+
+ route.input = INPUT(input)->vmux;
+
+ /* Tell the internal A/V decoder */
+ cx25821_call_all(dev, video, s_routing, INPUT(input)->vmux, 0, 0);
+
+ return 0;
+}
+
+int cx25821_start_video_dma(struct cx25821_dev *dev,
+ struct cx25821_dmaqueue *q,
+ struct cx25821_buffer *buf,
+ struct sram_channel *channel)
+{
+ int tmp = 0;
+
+ /* setup fifo + format */
+ cx25821_sram_channel_setup(dev, channel, buf->bpl, buf->risc.dma);
+
+ /* reset counter */
+ cx_write(channel->gpcnt_ctl, 3);
+ q->count = 1;
+
+ /* enable irq */
+ cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << channel->i));
+ cx_set(channel->int_msk, 0x11);
+
+ /* start dma */
+ cx_write(channel->dma_ctl, 0x11); /* FIFO and RISC enable */
+
+ /* make sure upstream setting if any is reversed */
+ tmp = cx_read(VID_CH_MODE_SEL);
+ cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00);
+
+ return 0;
+}
+
+int cx25821_restart_video_queue(struct cx25821_dev *dev,
+ struct cx25821_dmaqueue *q,
+ struct sram_channel *channel)
+{
+ struct cx25821_buffer *buf, *prev;
+ struct list_head *item;
+
+ if (!list_empty(&q->active)) {
+ buf = list_entry(q->active.next, struct cx25821_buffer,
+ vb.queue);
+
+ cx25821_start_video_dma(dev, q, buf, channel);
+
+ list_for_each(item, &q->active) {
+ buf = list_entry(item, struct cx25821_buffer, vb.queue);
+ buf->count = q->count++;
+ }
+
+ mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT);
+ return 0;
+ }
+
+ prev = NULL;
+ for (;;) {
+ if (list_empty(&q->queued))
+ return 0;
+
+ buf = list_entry(q->queued.next, struct cx25821_buffer,
+ vb.queue);
+
+ if (NULL == prev) {
+ list_move_tail(&buf->vb.queue, &q->active);
+ cx25821_start_video_dma(dev, q, buf, channel);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT);
+ } else if (prev->vb.width == buf->vb.width &&
+ prev->vb.height == buf->vb.height &&
+ prev->fmt == buf->fmt) {
+ list_move_tail(&buf->vb.queue, &q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63 - 32 */
+ } else {
+ return 0;
+ }
+ prev = buf;
+ }
+}
+
+void cx25821_vid_timeout(unsigned long data)
+{
+ struct cx25821_data *timeout_data = (struct cx25821_data *)data;
+ struct cx25821_dev *dev = timeout_data->dev;
+ struct sram_channel *channel = timeout_data->channel;
+ struct cx25821_dmaqueue *q = &dev->channels[channel->i].vidq;
+ struct cx25821_buffer *buf;
+ unsigned long flags;
+
+ /* cx25821_sram_channel_dump(dev, channel); */
+ cx_clear(channel->dma_ctl, 0x11);
+
+ spin_lock_irqsave(&dev->slock, flags);
+ while (!list_empty(&q->active)) {
+ buf = list_entry(q->active.next, struct cx25821_buffer,
+ vb.queue);
+ list_del(&buf->vb.queue);
+
+ buf->vb.state = VIDEOBUF_ERROR;
+ wake_up(&buf->vb.done);
+ }
+
+ cx25821_restart_video_queue(dev, q, channel);
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status)
+{
+ u32 count = 0;
+ int handled = 0;
+ u32 mask;
+ struct sram_channel *channel = dev->channels[chan_num].sram_channels;
+
+ mask = cx_read(channel->int_msk);
+ if (0 == (status & mask))
+ return handled;
+
+ cx_write(channel->int_stat, status);
+
+ /* risc op code error */
+ if (status & (1 << 16)) {
+ pr_warn("%s, %s: video risc op code error\n",
+ dev->name, channel->name);
+ cx_clear(channel->dma_ctl, 0x11);
+ cx25821_sram_channel_dump(dev, channel);
+ }
+
+ /* risc1 y */
+ if (status & FLD_VID_DST_RISC1) {
+ spin_lock(&dev->slock);
+ count = cx_read(channel->gpcnt);
+ cx25821_video_wakeup(dev, &dev->channels[channel->i].vidq,
+ count);
+ spin_unlock(&dev->slock);
+ handled++;
+ }
+
+ /* risc2 y */
+ if (status & 0x10) {
+ dprintk(2, "stopper video\n");
+ spin_lock(&dev->slock);
+ cx25821_restart_video_queue(dev,
+ &dev->channels[channel->i].vidq, channel);
+ spin_unlock(&dev->slock);
+ handled++;
+ }
+ return handled;
+}
+
+void cx25821_videoioctl_unregister(struct cx25821_dev *dev)
+{
+ if (dev->ioctl_dev) {
+ if (video_is_registered(dev->ioctl_dev))
+ video_unregister_device(dev->ioctl_dev);
+ else
+ video_device_release(dev->ioctl_dev);
+
+ dev->ioctl_dev = NULL;
+ }
+}
+
+void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num)
+{
+ cx_clear(PCI_INT_MSK, 1);
+
+ if (dev->channels[chan_num].video_dev) {
+ if (video_is_registered(dev->channels[chan_num].video_dev))
+ video_unregister_device(
+ dev->channels[chan_num].video_dev);
+ else
+ video_device_release(
+ dev->channels[chan_num].video_dev);
+
+ dev->channels[chan_num].video_dev = NULL;
+
+ btcx_riscmem_free(dev->pci,
+ &dev->channels[chan_num].vidq.stopper);
+
+ pr_warn("device %d released!\n", chan_num);
+ }
+
+}
+
+int cx25821_video_register(struct cx25821_dev *dev)
+{
+ int err;
+ int i;
+
+ struct video_device cx25821_video_device = {
+ .name = "cx25821-video",
+ .fops = &video_fops,
+ .minor = -1,
+ .ioctl_ops = &video_ioctl_ops,
+ .tvnorms = CX25821_NORMS,
+ .current_norm = V4L2_STD_NTSC_M,
+ };
+
+ spin_lock_init(&dev->slock);
+
+ for (i = 0; i < MAX_VID_CHANNEL_NUM - 1; ++i) {
+ cx25821_init_controls(dev, i);
+
+ cx25821_risc_stopper(dev->pci, &dev->channels[i].vidq.stopper,
+ dev->channels[i].sram_channels->dma_ctl, 0x11, 0);
+
+ dev->channels[i].sram_channels = &cx25821_sram_channels[i];
+ dev->channels[i].video_dev = NULL;
+ dev->channels[i].resources = 0;
+
+ cx_write(dev->channels[i].sram_channels->int_stat, 0xffffffff);
+
+ INIT_LIST_HEAD(&dev->channels[i].vidq.active);
+ INIT_LIST_HEAD(&dev->channels[i].vidq.queued);
+
+ dev->channels[i].timeout_data.dev = dev;
+ dev->channels[i].timeout_data.channel =
+ &cx25821_sram_channels[i];
+ dev->channels[i].vidq.timeout.function = cx25821_vid_timeout;
+ dev->channels[i].vidq.timeout.data =
+ (unsigned long)&dev->channels[i].timeout_data;
+ init_timer(&dev->channels[i].vidq.timeout);
+
+ /* register v4l devices */
+ dev->channels[i].video_dev = cx25821_vdev_init(dev, dev->pci,
+ &cx25821_video_device, "video");
+
+ err = video_register_device(dev->channels[i].video_dev,
+ VFL_TYPE_GRABBER, video_nr[dev->nr]);
+
+ if (err < 0)
+ goto fail_unreg;
+
+ }
+
+ /* set PCI interrupt */
+ cx_set(PCI_INT_MSK, 0xff);
+
+ /* initial device configuration */
+ mutex_lock(&dev->lock);
+#ifdef TUNER_FLAG
+ dev->tvnorm = cx25821_video_device.current_norm;
+ cx25821_set_tvnorm(dev, dev->tvnorm);
+#endif
+ mutex_unlock(&dev->lock);
+
+ return 0;
+
+fail_unreg:
+ cx25821_video_unregister(dev, i);
+ return err;
+}
+
+int cx25821_buffer_setup(struct videobuf_queue *q, unsigned int *count,
+ unsigned int *size)
+{
+ struct cx25821_fh *fh = q->priv_data;
+
+ *size = fh->fmt->depth * fh->width * fh->height >> 3;
+
+ if (0 == *count)
+ *count = 32;
+
+ if (*size * *count > vid_limit * 1024 * 1024)
+ *count = (vid_limit * 1024 * 1024) / *size;
+
+ return 0;
+}
+
+int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct cx25821_fh *fh = q->priv_data;
+ struct cx25821_dev *dev = fh->dev;
+ struct cx25821_buffer *buf =
+ container_of(vb, struct cx25821_buffer, vb);
+ int rc, init_buffer = 0;
+ u32 line0_offset, line1_offset;
+ struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
+ int bpl_local = LINE_SIZE_D1;
+ int channel_opened = fh->channel_id;
+
+ BUG_ON(NULL == fh->fmt);
+ if (fh->width < 48 || fh->width > 720 ||
+ fh->height < 32 || fh->height > 576)
+ return -EINVAL;
+
+ buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3;
+
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+
+ if (buf->fmt != fh->fmt ||
+ buf->vb.width != fh->width ||
+ buf->vb.height != fh->height || buf->vb.field != field) {
+ buf->fmt = fh->fmt;
+ buf->vb.width = fh->width;
+ buf->vb.height = fh->height;
+ buf->vb.field = field;
+ init_buffer = 1;
+ }
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ init_buffer = 1;
+ rc = videobuf_iolock(q, &buf->vb, NULL);
+ if (0 != rc) {
+ printk(KERN_DEBUG pr_fmt("videobuf_iolock failed!\n"));
+ goto fail;
+ }
+ }
+
+ dprintk(1, "init_buffer=%d\n", init_buffer);
+
+ if (init_buffer) {
+
+ channel_opened = dev->channel_opened;
+ if (channel_opened < 0 || channel_opened > 7)
+ channel_opened = 7;
+
+ if (dev->channels[channel_opened].pixel_formats ==
+ PIXEL_FRMT_411)
+ buf->bpl = (buf->fmt->depth * buf->vb.width) >> 3;
+ else
+ buf->bpl = (buf->fmt->depth >> 3) * (buf->vb.width);
+
+ if (dev->channels[channel_opened].pixel_formats ==
+ PIXEL_FRMT_411) {
+ bpl_local = buf->bpl;
+ } else {
+ bpl_local = buf->bpl; /* Default */
+
+ if (channel_opened >= 0 && channel_opened <= 7) {
+ if (dev->channels[channel_opened]
+ .use_cif_resolution) {
+ if (dev->tvnorm & V4L2_STD_PAL_BG ||
+ dev->tvnorm & V4L2_STD_PAL_DK)
+ bpl_local = 352 << 1;
+ else
+ bpl_local = dev->channels[
+ channel_opened].
+ cif_width << 1;
+ }
+ }
+ }
+
+ switch (buf->vb.field) {
+ case V4L2_FIELD_TOP:
+ cx25821_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist, 0, UNSET,
+ buf->bpl, 0, buf->vb.height);
+ break;
+ case V4L2_FIELD_BOTTOM:
+ cx25821_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist, UNSET, 0,
+ buf->bpl, 0, buf->vb.height);
+ break;
+ case V4L2_FIELD_INTERLACED:
+ /* All other formats are top field first */
+ line0_offset = 0;
+ line1_offset = buf->bpl;
+ dprintk(1, "top field first\n");
+
+ cx25821_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist, line0_offset,
+ bpl_local, bpl_local, bpl_local,
+ buf->vb.height >> 1);
+ break;
+ case V4L2_FIELD_SEQ_TB:
+ cx25821_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist,
+ 0, buf->bpl * (buf->vb.height >> 1),
+ buf->bpl, 0, buf->vb.height >> 1);
+ break;
+ case V4L2_FIELD_SEQ_BT:
+ cx25821_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist,
+ buf->bpl * (buf->vb.height >> 1), 0,
+ buf->bpl, 0, buf->vb.height >> 1);
+ break;
+ default:
+ BUG();
+ }
+ }
+
+ dprintk(2, "[%p/%d] buffer_prep - %dx%d %dbpp \"%s\" - dma=0x%08lx\n",
+ buf, buf->vb.i, fh->width, fh->height, fh->fmt->depth,
+ fh->fmt->name, (unsigned long)buf->risc.dma);
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+
+ return 0;
+
+fail:
+ cx25821_free_buffer(q, buf);
+ return rc;
+}
+
+void cx25821_buffer_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ struct cx25821_buffer *buf =
+ container_of(vb, struct cx25821_buffer, vb);
+
+ cx25821_free_buffer(q, buf);
+}
+
+struct videobuf_queue *get_queue(struct cx25821_fh *fh)
+{
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return &fh->vidq;
+ default:
+ BUG();
+ return NULL;
+ }
+}
+
+int cx25821_get_resource(struct cx25821_fh *fh, int resource)
+{
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return resource;
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+int cx25821_video_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct cx25821_fh *fh = file->private_data;
+
+ return videobuf_mmap_mapper(get_queue(fh), vma);
+}
+
+
+static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct cx25821_buffer *buf =
+ container_of(vb, struct cx25821_buffer, vb);
+ struct cx25821_buffer *prev;
+ struct cx25821_fh *fh = vq->priv_data;
+ struct cx25821_dev *dev = fh->dev;
+ struct cx25821_dmaqueue *q = &dev->channels[fh->channel_id].vidq;
+
+ /* add jump to stopper */
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
+ buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
+ buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
+
+ dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]);
+
+ if (!list_empty(&q->queued)) {
+ list_add_tail(&buf->vb.queue, &q->queued);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf,
+ buf->vb.i);
+
+ } else if (list_empty(&q->active)) {
+ list_add_tail(&buf->vb.queue, &q->active);
+ cx25821_start_video_dma(dev, q, buf,
+ dev->channels[fh->channel_id].sram_channels);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT);
+ dprintk(2, "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n",
+ buf, buf->vb.i, buf->count, q->count);
+ } else {
+ prev = list_entry(q->active.prev, struct cx25821_buffer,
+ vb.queue);
+ if (prev->vb.width == buf->vb.width
+ && prev->vb.height == buf->vb.height
+ && prev->fmt == buf->fmt) {
+ list_add_tail(&buf->vb.queue, &q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+
+ /* 64 bit bits 63-32 */
+ prev->risc.jmp[2] = cpu_to_le32(0);
+ dprintk(2, "[%p/%d] buffer_queue - append to active, buf->count=%d\n",
+ buf, buf->vb.i, buf->count);
+
+ } else {
+ list_add_tail(&buf->vb.queue, &q->queued);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf,
+ buf->vb.i);
+ }
+ }
+
+ if (list_empty(&q->active))
+ dprintk(2, "active queue empty!\n");
+}
+
+static struct videobuf_queue_ops cx25821_video_qops = {
+ .buf_setup = cx25821_buffer_setup,
+ .buf_prepare = cx25821_buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = cx25821_buffer_release,
+};
+
+static int video_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct cx25821_dev *h, *dev = video_drvdata(file);
+ struct cx25821_fh *fh;
+ struct list_head *list;
+ int minor = video_devdata(file)->minor;
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ u32 pix_format;
+ int ch_id = 0;
+ int i;
+
+ dprintk(1, "open dev=%s type=%s\n", video_device_node_name(vdev),
+ v4l2_type_names[type]);
+
+ /* allocate + initialize per filehandle data */
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (NULL == fh)
+ return -ENOMEM;
+
+ mutex_lock(&cx25821_devlist_mutex);
+
+ list_for_each(list, &cx25821_devlist)
+ {
+ h = list_entry(list, struct cx25821_dev, devlist);
+
+ for (i = 0; i < MAX_VID_CHANNEL_NUM; i++) {
+ if (h->channels[i].video_dev &&
+ h->channels[i].video_dev->minor == minor) {
+ dev = h;
+ ch_id = i;
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ }
+ }
+ }
+
+ if (NULL == dev) {
+ mutex_unlock(&cx25821_devlist_mutex);
+ kfree(fh);
+ return -ENODEV;
+ }
+
+ file->private_data = fh;
+ fh->dev = dev;
+ fh->type = type;
+ fh->width = 720;
+ fh->channel_id = ch_id;
+
+ if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK)
+ fh->height = 576;
+ else
+ fh->height = 480;
+
+ dev->channel_opened = fh->channel_id;
+ if (dev->channels[ch_id].pixel_formats == PIXEL_FRMT_411)
+ pix_format = V4L2_PIX_FMT_Y41P;
+ else
+ pix_format = V4L2_PIX_FMT_YUYV;
+ fh->fmt = cx25821_format_by_fourcc(pix_format);
+
+ v4l2_prio_open(&dev->channels[ch_id].prio, &fh->prio);
+
+ videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, &dev->pci->dev,
+ &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED, sizeof(struct cx25821_buffer),
+ fh, NULL);
+
+ dprintk(1, "post videobuf_queue_init()\n");
+ mutex_unlock(&cx25821_devlist_mutex);
+
+ return 0;
+}
+
+static ssize_t video_read(struct file *file, char __user * data, size_t count,
+ loff_t *ppos)
+{
+ struct cx25821_fh *fh = file->private_data;
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (cx25821_res_locked(fh, RESOURCE_VIDEO0))
+ return -EBUSY;
+
+ return videobuf_read_one(&fh->vidq, data, count, ppos,
+ file->f_flags & O_NONBLOCK);
+
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+static unsigned int video_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct cx25821_fh *fh = file->private_data;
+ struct cx25821_buffer *buf;
+
+ if (cx25821_res_check(fh, RESOURCE_VIDEO0)) {
+ /* streaming capture */
+ if (list_empty(&fh->vidq.stream))
+ return POLLERR;
+ buf = list_entry(fh->vidq.stream.next,
+ struct cx25821_buffer, vb.stream);
+ } else {
+ /* read() capture */
+ buf = (struct cx25821_buffer *)fh->vidq.read_buf;
+ if (NULL == buf)
+ return POLLERR;
+ }
+
+ poll_wait(file, &buf->vb.done, wait);
+ if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) {
+ if (buf->vb.state == VIDEOBUF_DONE) {
+ struct cx25821_dev *dev = fh->dev;
+
+ if (dev && dev->channels[fh->channel_id]
+ .use_cif_resolution) {
+ u8 cam_id = *((char *)buf->vb.baddr + 3);
+ memcpy((char *)buf->vb.baddr,
+ (char *)buf->vb.baddr + (fh->width * 2),
+ (fh->width * 2));
+ *((char *)buf->vb.baddr + 3) = cam_id;
+ }
+ }
+
+ return POLLIN | POLLRDNORM;
+ }
+
+ return 0;
+}
+
+static int video_release(struct file *file)
+{
+ struct cx25821_fh *fh = file->private_data;
+ struct cx25821_dev *dev = fh->dev;
+
+ /* stop the risc engine and fifo */
+ cx_write(channel0->dma_ctl, 0); /* FIFO and RISC disable */
+
+ /* stop video capture */
+ if (cx25821_res_check(fh, RESOURCE_VIDEO0)) {
+ videobuf_queue_cancel(&fh->vidq);
+ cx25821_res_free(dev, fh, RESOURCE_VIDEO0);
+ }
+
+ if (fh->vidq.read_buf) {
+ cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf);
+ kfree(fh->vidq.read_buf);
+ }
+
+ videobuf_mmap_free(&fh->vidq);
+
+ v4l2_prio_close(&dev->channels[fh->channel_id].prio, fh->prio);
+ file->private_data = NULL;
+ kfree(fh);
+
+ return 0;
+}
+
+static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct cx25821_fh *fh = priv;
+ struct cx25821_dev *dev = fh->dev;
+
+ if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
+ return -EINVAL;
+
+ if (unlikely(i != fh->type))
+ return -EINVAL;
+
+ if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh,
+ RESOURCE_VIDEO0))))
+ return -EBUSY;
+
+ return videobuf_streamon(get_queue(fh));
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct cx25821_fh *fh = priv;
+ struct cx25821_dev *dev = fh->dev;
+ int err, res;
+
+ if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (i != fh->type)
+ return -EINVAL;
+
+ res = cx25821_get_resource(fh, RESOURCE_VIDEO0);
+ err = videobuf_streamoff(get_queue(fh));
+ if (err < 0)
+ return err;
+ cx25821_res_free(dev, fh, res);
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx25821_fh *fh = priv;
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+ struct v4l2_mbus_framefmt mbus_fmt;
+ int err;
+ int pix_format = PIXEL_FRMT_422;
+
+ if (fh) {
+ err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
+ fh->prio);
+ if (0 != err)
+ return err;
+ }
+
+ dprintk(2, "%s()\n", __func__);
+ err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f);
+
+ if (0 != err)
+ return err;
+
+ fh->fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat);
+ fh->vidq.field = f->fmt.pix.field;
+
+ /* check if width and height is valid based on set standard */
+ if (cx25821_is_valid_width(f->fmt.pix.width, dev->tvnorm))
+ fh->width = f->fmt.pix.width;
+
+ if (cx25821_is_valid_height(f->fmt.pix.height, dev->tvnorm))
+ fh->height = f->fmt.pix.height;
+
+ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P)
+ pix_format = PIXEL_FRMT_411;
+ else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
+ pix_format = PIXEL_FRMT_422;
+ else
+ return -EINVAL;
+
+ cx25821_set_pixel_format(dev, SRAM_CH00, pix_format);
+
+ /* check if cif resolution */
+ if (fh->width == 320 || fh->width == 352)
+ dev->channels[fh->channel_id].use_cif_resolution = 1;
+ else
+ dev->channels[fh->channel_id].use_cif_resolution = 0;
+
+ dev->channels[fh->channel_id].cif_width = fh->width;
+ medusa_set_resolution(dev, fh->width, SRAM_CH00);
+
+ dprintk(2, "%s(): width=%d height=%d field=%d\n", __func__, fh->width,
+ fh->height, fh->vidq.field);
+ v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED);
+ cx25821_call_all(dev, video, s_mbus_fmt, &mbus_fmt);
+
+ return 0;
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ int ret_val = 0;
+ struct cx25821_fh *fh = priv;
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+
+ ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK);
+
+ p->sequence = dev->channels[fh->channel_id].vidq.count;
+
+ return ret_val;
+}
+
+static int vidioc_log_status(struct file *file, void *priv)
+{
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+ struct cx25821_fh *fh = priv;
+ char name[32 + 2];
+
+ struct sram_channel *sram_ch = dev->channels[fh->channel_id]
+ .sram_channels;
+ u32 tmp = 0;
+
+ snprintf(name, sizeof(name), "%s/2", dev->name);
+ pr_info("%s/2: ============ START LOG STATUS ============\n",
+ dev->name);
+ cx25821_call_all(dev, core, log_status);
+ tmp = cx_read(sram_ch->dma_ctl);
+ pr_info("Video input 0 is %s\n",
+ (tmp & 0x11) ? "streaming" : "stopped");
+ pr_info("%s/2: ============= END LOG STATUS =============\n",
+ dev->name);
+ return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctl)
+{
+ struct cx25821_fh *fh = priv;
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+ int err;
+
+ if (fh) {
+ err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
+ fh->prio);
+ if (0 != err)
+ return err;
+ }
+
+ return cx25821_set_control(dev, ctl, fh->channel_id);
+}
+
+/* VIDEO IOCTLS */
+int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx25821_fh *fh = priv;
+
+ f->fmt.pix.width = fh->width;
+ f->fmt.pix.height = fh->height;
+ f->fmt.pix.field = fh->vidq.field;
+ f->fmt.pix.pixelformat = fh->fmt->fourcc;
+ f->fmt.pix.bytesperline = (f->fmt.pix.width * fh->fmt->depth) >> 3;
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+int cx25821_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx25821_fmt *fmt;
+ enum v4l2_field field;
+ unsigned int maxw, maxh;
+
+ fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+
+ field = f->fmt.pix.field;
+ maxw = 720;
+ maxh = 576;
+
+ if (V4L2_FIELD_ANY == field) {
+ if (f->fmt.pix.height > maxh / 2)
+ field = V4L2_FIELD_INTERLACED;
+ else
+ field = V4L2_FIELD_TOP;
+ }
+
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ f->fmt.pix.field = field;
+ if (f->fmt.pix.height < 32)
+ f->fmt.pix.height = 32;
+ if (f->fmt.pix.height > maxh)
+ f->fmt.pix.height = maxh;
+ if (f->fmt.pix.width < 48)
+ f->fmt.pix.width = 48;
+ if (f->fmt.pix.width > maxw)
+ f->fmt.pix.width = maxw;
+ f->fmt.pix.width &= ~0x03;
+ f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+int cx25821_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+
+ strcpy(cap->driver, "cx25821");
+ strlcpy(cap->card, cx25821_boards[dev->board].name, sizeof(cap->card));
+ sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci));
+ cap->version = CX25821_VERSION_CODE;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+ if (UNSET != dev->tuner_type)
+ cap->capabilities |= V4L2_CAP_TUNER;
+ return 0;
+}
+
+int cx25821_vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (unlikely(f->index >= ARRAY_SIZE(formats)))
+ return -EINVAL;
+
+ strlcpy(f->description, formats[f->index].name, sizeof(f->description));
+ f->pixelformat = formats[f->index].fourcc;
+
+ return 0;
+}
+
+int cx25821_vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct cx25821_fh *fh = priv;
+ return videobuf_reqbufs(get_queue(fh), p);
+}
+
+int cx25821_vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct cx25821_fh *fh = priv;
+ return videobuf_querybuf(get_queue(fh), p);
+}
+
+int cx25821_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct cx25821_fh *fh = priv;
+ return videobuf_qbuf(get_queue(fh), p);
+}
+
+int cx25821_vidioc_g_priority(struct file *file, void *f, enum v4l2_priority *p)
+{
+ struct cx25821_dev *dev = ((struct cx25821_fh *)f)->dev;
+ struct cx25821_fh *fh = f;
+
+ *p = v4l2_prio_max(&dev->channels[fh->channel_id].prio);
+
+ return 0;
+}
+
+int cx25821_vidioc_s_priority(struct file *file, void *f,
+ enum v4l2_priority prio)
+{
+ struct cx25821_fh *fh = f;
+ struct cx25821_dev *dev = ((struct cx25821_fh *)f)->dev;
+
+ return v4l2_prio_change(&dev->channels[fh->channel_id].prio, &fh->prio,
+ prio);
+}
+
+#ifdef TUNER_FLAG
+int cx25821_vidioc_s_std(struct file *file, void *priv, v4l2_std_id * tvnorms)
+{
+ struct cx25821_fh *fh = priv;
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+ int err;
+
+ dprintk(1, "%s()\n", __func__);
+
+ if (fh) {
+ err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
+ fh->prio);
+ if (0 != err)
+ return err;
+ }
+
+ if (dev->tvnorm == *tvnorms)
+ return 0;
+
+ mutex_lock(&dev->lock);
+ cx25821_set_tvnorm(dev, *tvnorms);
+ mutex_unlock(&dev->lock);
+
+ medusa_set_videostandard(dev);
+
+ return 0;
+}
+#endif
+
+int cx25821_enum_input(struct cx25821_dev *dev, struct v4l2_input *i)
+{
+ static const char * const iname[] = {
+ [CX25821_VMUX_COMPOSITE] = "Composite",
+ [CX25821_VMUX_SVIDEO] = "S-Video",
+ [CX25821_VMUX_DEBUG] = "for debug only",
+ };
+ unsigned int n;
+ dprintk(1, "%s()\n", __func__);
+
+ n = i->index;
+ if (n >= 2)
+ return -EINVAL;
+
+ if (0 == INPUT(n)->type)
+ return -EINVAL;
+
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ strcpy(i->name, iname[INPUT(n)->type]);
+
+ i->std = CX25821_NORMS;
+ return 0;
+}
+
+int cx25821_vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+ dprintk(1, "%s()\n", __func__);
+ return cx25821_enum_input(dev, i);
+}
+
+int cx25821_vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+
+ *i = dev->input;
+ dprintk(1, "%s(): returns %d\n", __func__, *i);
+ return 0;
+}
+
+int cx25821_vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct cx25821_fh *fh = priv;
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+ int err;
+
+ dprintk(1, "%s(%d)\n", __func__, i);
+
+ if (fh) {
+ err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
+ fh->prio);
+ if (0 != err)
+ return err;
+ }
+
+ if (i >= CX25821_NR_INPUT) {
+ dprintk(1, "%s(): -EINVAL\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&dev->lock);
+ cx25821_video_mux(dev, i);
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+#ifdef TUNER_FLAG
+int cx25821_vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct cx25821_fh *fh = priv;
+ struct cx25821_dev *dev = fh->dev;
+
+ f->frequency = dev->freq;
+
+ cx25821_call_all(dev, tuner, g_frequency, f);
+
+ return 0;
+}
+
+int cx25821_set_freq(struct cx25821_dev *dev, struct v4l2_frequency *f)
+{
+ mutex_lock(&dev->lock);
+ dev->freq = f->frequency;
+
+ cx25821_call_all(dev, tuner, s_frequency, f);
+
+ /* When changing channels it is required to reset TVAUDIO */
+ msleep(10);
+
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+
+int cx25821_vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct cx25821_fh *fh = priv;
+ struct cx25821_dev *dev;
+ int err;
+
+ if (fh) {
+ dev = fh->dev;
+ err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
+ fh->prio);
+ if (0 != err)
+ return err;
+ } else {
+ pr_err("Invalid fh pointer!\n");
+ return -EINVAL;
+ }
+
+ return cx25821_set_freq(dev, f);
+}
+#endif
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+int cx25821_vidioc_g_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg)
+{
+ struct cx25821_dev *dev = ((struct cx25821_fh *)fh)->dev;
+
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+
+ cx25821_call_all(dev, core, g_register, reg);
+
+ return 0;
+}
+
+int cx25821_vidioc_s_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg)
+{
+ struct cx25821_dev *dev = ((struct cx25821_fh *)fh)->dev;
+
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+
+ cx25821_call_all(dev, core, s_register, reg);
+
+ return 0;
+}
+
+#endif
+
+#ifdef TUNER_FLAG
+int cx25821_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
+{
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+
+ if (unlikely(UNSET == dev->tuner_type))
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+
+ strcpy(t->name, "Television");
+ t->type = V4L2_TUNER_ANALOG_TV;
+ t->capability = V4L2_TUNER_CAP_NORM;
+ t->rangehigh = 0xffffffffUL;
+
+ t->signal = 0xffff; /* LOCKED */
+ return 0;
+}
+
+int cx25821_vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
+{
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+ struct cx25821_fh *fh = priv;
+ int err;
+
+ if (fh) {
+ err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
+ fh->prio);
+ if (0 != err)
+ return err;
+ }
+
+ dprintk(1, "%s()\n", __func__);
+ if (UNSET == dev->tuner_type)
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+
+ return 0;
+}
+
+#endif
+/*****************************************************************************/
+static const struct v4l2_queryctrl no_ctl = {
+ .name = "42",
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+};
+
+static struct v4l2_queryctrl cx25821_ctls[] = {
+ /* --- video --- */
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 10000,
+ .step = 1,
+ .default_value = 6200,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ }, {
+ .id = V4L2_CID_CONTRAST,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 10000,
+ .step = 1,
+ .default_value = 5000,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ }, {
+ .id = V4L2_CID_SATURATION,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 10000,
+ .step = 1,
+ .default_value = 5000,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ }, {
+ .id = V4L2_CID_HUE,
+ .name = "Hue",
+ .minimum = 0,
+ .maximum = 10000,
+ .step = 1,
+ .default_value = 5000,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ }
+};
+static const int CX25821_CTLS = ARRAY_SIZE(cx25821_ctls);
+
+static int cx25821_ctrl_query(struct v4l2_queryctrl *qctrl)
+{
+ int i;
+
+ if (qctrl->id < V4L2_CID_BASE || qctrl->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+ for (i = 0; i < CX25821_CTLS; i++)
+ if (cx25821_ctls[i].id == qctrl->id)
+ break;
+ if (i == CX25821_CTLS) {
+ *qctrl = no_ctl;
+ return 0;
+ }
+ *qctrl = cx25821_ctls[i];
+ return 0;
+}
+
+int cx25821_vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qctrl)
+{
+ return cx25821_ctrl_query(qctrl);
+}
+
+/* ------------------------------------------------------------------ */
+/* VIDEO CTRL IOCTLS */
+
+static const struct v4l2_queryctrl *ctrl_by_id(unsigned int id)
+{
+ unsigned int i;
+
+ for (i = 0; i < CX25821_CTLS; i++)
+ if (cx25821_ctls[i].id == id)
+ return cx25821_ctls + i;
+ return NULL;
+}
+
+int cx25821_vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctl)
+{
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+ struct cx25821_fh *fh = priv;
+
+ const struct v4l2_queryctrl *ctrl;
+
+ ctrl = ctrl_by_id(ctl->id);
+
+ if (NULL == ctrl)
+ return -EINVAL;
+ switch (ctl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctl->value = dev->channels[fh->channel_id].ctl_bright;
+ break;
+ case V4L2_CID_HUE:
+ ctl->value = dev->channels[fh->channel_id].ctl_hue;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctl->value = dev->channels[fh->channel_id].ctl_contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ ctl->value = dev->channels[fh->channel_id].ctl_saturation;
+ break;
+ }
+ return 0;
+}
+
+int cx25821_set_control(struct cx25821_dev *dev,
+ struct v4l2_control *ctl, int chan_num)
+{
+ int err;
+ const struct v4l2_queryctrl *ctrl;
+
+ err = -EINVAL;
+
+ ctrl = ctrl_by_id(ctl->id);
+
+ if (NULL == ctrl)
+ return err;
+
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ case V4L2_CTRL_TYPE_MENU:
+ case V4L2_CTRL_TYPE_INTEGER:
+ if (ctl->value < ctrl->minimum)
+ ctl->value = ctrl->minimum;
+ if (ctl->value > ctrl->maximum)
+ ctl->value = ctrl->maximum;
+ break;
+ default:
+ /* nothing */ ;
+ }
+
+ switch (ctl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ dev->channels[chan_num].ctl_bright = ctl->value;
+ medusa_set_brightness(dev, ctl->value, chan_num);
+ break;
+ case V4L2_CID_HUE:
+ dev->channels[chan_num].ctl_hue = ctl->value;
+ medusa_set_hue(dev, ctl->value, chan_num);
+ break;
+ case V4L2_CID_CONTRAST:
+ dev->channels[chan_num].ctl_contrast = ctl->value;
+ medusa_set_contrast(dev, ctl->value, chan_num);
+ break;
+ case V4L2_CID_SATURATION:
+ dev->channels[chan_num].ctl_saturation = ctl->value;
+ medusa_set_saturation(dev, ctl->value, chan_num);
+ break;
+ }
+
+ err = 0;
+
+ return err;
+}
+
+static void cx25821_init_controls(struct cx25821_dev *dev, int chan_num)
+{
+ struct v4l2_control ctrl;
+ int i;
+ for (i = 0; i < CX25821_CTLS; i++) {
+ ctrl.id = cx25821_ctls[i].id;
+ ctrl.value = cx25821_ctls[i].default_value;
+
+ cx25821_set_control(dev, &ctrl, chan_num);
+ }
+}
+
+int cx25821_vidioc_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cropcap)
+{
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+
+ if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ cropcap->bounds.top = 0;
+ cropcap->bounds.left = 0;
+ cropcap->bounds.width = 720;
+ cropcap->bounds.height = dev->tvnorm == V4L2_STD_PAL_BG ? 576 : 480;
+ cropcap->pixelaspect.numerator =
+ dev->tvnorm == V4L2_STD_PAL_BG ? 59 : 10;
+ cropcap->pixelaspect.denominator =
+ dev->tvnorm == V4L2_STD_PAL_BG ? 54 : 11;
+ cropcap->defrect = cropcap->bounds;
+ return 0;
+}
+
+int cx25821_vidioc_s_crop(struct file *file, void *priv, struct v4l2_crop *crop)
+{
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+ struct cx25821_fh *fh = priv;
+ int err;
+
+ if (fh) {
+ err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
+ fh->prio);
+ if (0 != err)
+ return err;
+ }
+ /* cx25821_vidioc_s_crop not supported */
+ return -EINVAL;
+}
+
+int cx25821_vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop)
+{
+ /* cx25821_vidioc_g_crop not supported */
+ return -EINVAL;
+}
+
+int cx25821_vidioc_querystd(struct file *file, void *priv, v4l2_std_id * norm)
+{
+ /* medusa does not support video standard sensing of current input */
+ *norm = CX25821_NORMS;
+
+ return 0;
+}
+
+int cx25821_is_valid_width(u32 width, v4l2_std_id tvnorm)
+{
+ if (tvnorm == V4L2_STD_PAL_BG) {
+ if (width == 352 || width == 720)
+ return 1;
+ else
+ return 0;
+ }
+
+ if (tvnorm == V4L2_STD_NTSC_M) {
+ if (width == 320 || width == 352 || width == 720)
+ return 1;
+ else
+ return 0;
+ }
+ return 0;
+}
+
+int cx25821_is_valid_height(u32 height, v4l2_std_id tvnorm)
+{
+ if (tvnorm == V4L2_STD_PAL_BG) {
+ if (height == 576 || height == 288)
+ return 1;
+ else
+ return 0;
+ }
+
+ if (tvnorm == V4L2_STD_NTSC_M) {
+ if (height == 480 || height == 240)
+ return 1;
+ else
+ return 0;
+ }
+
+ return 0;
+}
+
+static long video_ioctl_upstream9(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct cx25821_fh *fh = file->private_data;
+ struct cx25821_dev *dev = fh->dev;
+ int command = 0;
+ struct upstream_user_struct *data_from_user;
+
+ data_from_user = (struct upstream_user_struct *)arg;
+
+ if (!data_from_user) {
+ pr_err("%s(): Upstream data is INVALID. Returning\n", __func__);
+ return 0;
+ }
+
+ command = data_from_user->command;
+
+ if (command != UPSTREAM_START_VIDEO && command != UPSTREAM_STOP_VIDEO)
+ return 0;
+
+ dev->input_filename = data_from_user->input_filename;
+ dev->input_audiofilename = data_from_user->input_filename;
+ dev->vid_stdname = data_from_user->vid_stdname;
+ dev->pixel_format = data_from_user->pixel_format;
+ dev->channel_select = data_from_user->channel_select;
+ dev->command = data_from_user->command;
+
+ switch (command) {
+ case UPSTREAM_START_VIDEO:
+ cx25821_start_upstream_video_ch1(dev, data_from_user);
+ break;
+
+ case UPSTREAM_STOP_VIDEO:
+ cx25821_stop_upstream_video_ch1(dev);
+ break;
+ }
+
+ return 0;
+}
+
+static long video_ioctl_upstream10(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct cx25821_fh *fh = file->private_data;
+ struct cx25821_dev *dev = fh->dev;
+ int command = 0;
+ struct upstream_user_struct *data_from_user;
+
+ data_from_user = (struct upstream_user_struct *)arg;
+
+ if (!data_from_user) {
+ pr_err("%s(): Upstream data is INVALID. Returning\n", __func__);
+ return 0;
+ }
+
+ command = data_from_user->command;
+
+ if (command != UPSTREAM_START_VIDEO && command != UPSTREAM_STOP_VIDEO)
+ return 0;
+
+ dev->input_filename_ch2 = data_from_user->input_filename;
+ dev->input_audiofilename = data_from_user->input_filename;
+ dev->vid_stdname_ch2 = data_from_user->vid_stdname;
+ dev->pixel_format_ch2 = data_from_user->pixel_format;
+ dev->channel_select_ch2 = data_from_user->channel_select;
+ dev->command_ch2 = data_from_user->command;
+
+ switch (command) {
+ case UPSTREAM_START_VIDEO:
+ cx25821_start_upstream_video_ch2(dev, data_from_user);
+ break;
+
+ case UPSTREAM_STOP_VIDEO:
+ cx25821_stop_upstream_video_ch2(dev);
+ break;
+ }
+
+ return 0;
+}
+
+static long video_ioctl_upstream11(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct cx25821_fh *fh = file->private_data;
+ struct cx25821_dev *dev = fh->dev;
+ int command = 0;
+ struct upstream_user_struct *data_from_user;
+
+ data_from_user = (struct upstream_user_struct *)arg;
+
+ if (!data_from_user) {
+ pr_err("%s(): Upstream data is INVALID. Returning\n", __func__);
+ return 0;
+ }
+
+ command = data_from_user->command;
+
+ if (command != UPSTREAM_START_AUDIO && command != UPSTREAM_STOP_AUDIO)
+ return 0;
+
+ dev->input_filename = data_from_user->input_filename;
+ dev->input_audiofilename = data_from_user->input_filename;
+ dev->vid_stdname = data_from_user->vid_stdname;
+ dev->pixel_format = data_from_user->pixel_format;
+ dev->channel_select = data_from_user->channel_select;
+ dev->command = data_from_user->command;
+
+ switch (command) {
+ case UPSTREAM_START_AUDIO:
+ cx25821_start_upstream_audio(dev, data_from_user);
+ break;
+
+ case UPSTREAM_STOP_AUDIO:
+ cx25821_stop_upstream_audio(dev);
+ break;
+ }
+
+ return 0;
+}
+
+static long video_ioctl_set(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct cx25821_fh *fh = file->private_data;
+ struct cx25821_dev *dev = fh->dev;
+ struct downstream_user_struct *data_from_user;
+ int command;
+ int width = 720;
+ int selected_channel = 0;
+ int pix_format = 0;
+ int i = 0;
+ int cif_enable = 0;
+ int cif_width = 0;
+ u32 value = 0;
+
+ data_from_user = (struct downstream_user_struct *)arg;
+
+ if (!data_from_user) {
+ pr_err("%s(): User data is INVALID. Returning\n", __func__);
+ return 0;
+ }
+
+ command = data_from_user->command;
+
+ if (command != SET_VIDEO_STD && command != SET_PIXEL_FORMAT
+ && command != ENABLE_CIF_RESOLUTION && command != REG_READ
+ && command != REG_WRITE && command != MEDUSA_READ
+ && command != MEDUSA_WRITE) {
+ return 0;
+ }
+
+ switch (command) {
+ case SET_VIDEO_STD:
+ if (!strcmp(data_from_user->vid_stdname, "PAL"))
+ dev->tvnorm = V4L2_STD_PAL_BG;
+ else
+ dev->tvnorm = V4L2_STD_NTSC_M;
+ medusa_set_videostandard(dev);
+ break;
+
+ case SET_PIXEL_FORMAT:
+ selected_channel = data_from_user->decoder_select;
+ pix_format = data_from_user->pixel_format;
+
+ if (!(selected_channel <= 7 && selected_channel >= 0)) {
+ selected_channel -= 4;
+ selected_channel = selected_channel % 8;
+ }
+
+ if (selected_channel >= 0)
+ cx25821_set_pixel_format(dev, selected_channel,
+ pix_format);
+
+ break;
+
+ case ENABLE_CIF_RESOLUTION:
+ selected_channel = data_from_user->decoder_select;
+ cif_enable = data_from_user->cif_resolution_enable;
+ cif_width = data_from_user->cif_width;
+
+ if (cif_enable) {
+ if (dev->tvnorm & V4L2_STD_PAL_BG
+ || dev->tvnorm & V4L2_STD_PAL_DK) {
+ width = 352;
+ } else {
+ width = cif_width;
+ if (cif_width != 320 && cif_width != 352)
+ width = 320;
+ }
+ }
+
+ if (!(selected_channel <= 7 && selected_channel >= 0)) {
+ selected_channel -= 4;
+ selected_channel = selected_channel % 8;
+ }
+
+ if (selected_channel <= 7 && selected_channel >= 0) {
+ dev->channels[selected_channel].use_cif_resolution =
+ cif_enable;
+ dev->channels[selected_channel].cif_width = width;
+ } else {
+ for (i = 0; i < VID_CHANNEL_NUM; i++) {
+ dev->channels[i].use_cif_resolution =
+ cif_enable;
+ dev->channels[i].cif_width = width;
+ }
+ }
+
+ medusa_set_resolution(dev, width, selected_channel);
+ break;
+ case REG_READ:
+ data_from_user->reg_data = cx_read(data_from_user->reg_address);
+ break;
+ case REG_WRITE:
+ cx_write(data_from_user->reg_address, data_from_user->reg_data);
+ break;
+ case MEDUSA_READ:
+ value = cx25821_i2c_read(&dev->i2c_bus[0],
+ (u16) data_from_user->reg_address,
+ &data_from_user->reg_data);
+ break;
+ case MEDUSA_WRITE:
+ cx25821_i2c_write(&dev->i2c_bus[0],
+ (u16) data_from_user->reg_address,
+ data_from_user->reg_data);
+ break;
+ }
+
+ return 0;
+}
+
+static long cx25821_video_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+
+ struct cx25821_fh *fh = file->private_data;
+
+ /* check to see if it's the video upstream */
+ if (fh->channel_id == SRAM_CH09) {
+ ret = video_ioctl_upstream9(file, cmd, arg);
+ return ret;
+ } else if (fh->channel_id == SRAM_CH10) {
+ ret = video_ioctl_upstream10(file, cmd, arg);
+ return ret;
+ } else if (fh->channel_id == SRAM_CH11) {
+ ret = video_ioctl_upstream11(file, cmd, arg);
+ ret = video_ioctl_set(file, cmd, arg);
+ return ret;
+ }
+
+ return video_ioctl2(file, cmd, arg);
+}
+
+/* exported stuff */
+static const struct v4l2_file_operations video_fops = {
+ .owner = THIS_MODULE,
+ .open = video_open,
+ .release = video_release,
+ .read = video_read,
+ .poll = video_poll,
+ .mmap = cx25821_video_mmap,
+ .ioctl = cx25821_video_ioctl,
+};
+
+static const struct v4l2_ioctl_ops video_ioctl_ops = {
+ .vidioc_querycap = cx25821_vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_reqbufs = cx25821_vidioc_reqbufs,
+ .vidioc_querybuf = cx25821_vidioc_querybuf,
+ .vidioc_qbuf = cx25821_vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+#ifdef TUNER_FLAG
+ .vidioc_s_std = cx25821_vidioc_s_std,
+ .vidioc_querystd = cx25821_vidioc_querystd,
+#endif
+ .vidioc_cropcap = cx25821_vidioc_cropcap,
+ .vidioc_s_crop = cx25821_vidioc_s_crop,
+ .vidioc_g_crop = cx25821_vidioc_g_crop,
+ .vidioc_enum_input = cx25821_vidioc_enum_input,
+ .vidioc_g_input = cx25821_vidioc_g_input,
+ .vidioc_s_input = cx25821_vidioc_s_input,
+ .vidioc_g_ctrl = cx25821_vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_queryctrl = cx25821_vidioc_queryctrl,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_log_status = vidioc_log_status,
+ .vidioc_g_priority = cx25821_vidioc_g_priority,
+ .vidioc_s_priority = cx25821_vidioc_s_priority,
+#ifdef TUNER_FLAG
+ .vidioc_g_tuner = cx25821_vidioc_g_tuner,
+ .vidioc_s_tuner = cx25821_vidioc_s_tuner,
+ .vidioc_g_frequency = cx25821_vidioc_g_frequency,
+ .vidioc_s_frequency = cx25821_vidioc_s_frequency,
+#endif
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = cx25821_vidioc_g_register,
+ .vidioc_s_register = cx25821_vidioc_s_register,
+#endif
+};
+
+struct video_device cx25821_videoioctl_template = {
+ .name = "cx25821-videoioctl",
+ .fops = &video_fops,
+ .ioctl_ops = &video_ioctl_ops,
+ .tvnorms = CX25821_NORMS,
+ .current_norm = V4L2_STD_NTSC_M,
+};
diff --git a/drivers/media/video/cx25821/cx25821-video.h b/drivers/media/video/cx25821/cx25821-video.h
new file mode 100644
index 00000000..d0d9538c
--- /dev/null
+++ b/drivers/media/video/cx25821/cx25821-video.h
@@ -0,0 +1,188 @@
+/*
+ * Driver for the Conexant CX25821 PCIe bridge
+ *
+ * Copyright (C) 2009 Conexant Systems Inc.
+ * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
+ * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef CX25821_VIDEO_H_
+#define CX25821_VIDEO_H_
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kmod.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <asm/div64.h>
+
+#include "cx25821.h"
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+
+#define TUNER_FLAG
+
+#define VIDEO_DEBUG 0
+
+#define dprintk(level, fmt, arg...) \
+do { \
+ if (VIDEO_DEBUG >= level) \
+ printk(KERN_DEBUG "%s/0: " fmt, dev->name, ##arg); \
+} while (0)
+
+/* For IOCTL to identify running upstream */
+#define UPSTREAM_START_VIDEO 700
+#define UPSTREAM_STOP_VIDEO 701
+#define UPSTREAM_START_AUDIO 702
+#define UPSTREAM_STOP_AUDIO 703
+#define UPSTREAM_DUMP_REGISTERS 702
+#define SET_VIDEO_STD 800
+#define SET_PIXEL_FORMAT 1000
+#define ENABLE_CIF_RESOLUTION 1001
+
+#define REG_READ 900
+#define REG_WRITE 901
+#define MEDUSA_READ 910
+#define MEDUSA_WRITE 911
+
+extern struct sram_channel *channel0;
+extern struct sram_channel *channel1;
+extern struct sram_channel *channel2;
+extern struct sram_channel *channel3;
+extern struct sram_channel *channel4;
+extern struct sram_channel *channel5;
+extern struct sram_channel *channel6;
+extern struct sram_channel *channel7;
+extern struct sram_channel *channel9;
+extern struct sram_channel *channel10;
+extern struct sram_channel *channel11;
+extern struct video_device cx25821_videoioctl_template;
+/* extern const u32 *ctrl_classes[]; */
+
+extern unsigned int vid_limit;
+
+#define FORMAT_FLAGS_PACKED 0x01
+extern struct cx25821_fmt formats[];
+extern struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc);
+extern struct cx25821_data timeout_data[MAX_VID_CHANNEL_NUM];
+
+extern void cx25821_dump_video_queue(struct cx25821_dev *dev,
+ struct cx25821_dmaqueue *q);
+extern void cx25821_video_wakeup(struct cx25821_dev *dev,
+ struct cx25821_dmaqueue *q, u32 count);
+
+#ifdef TUNER_FLAG
+extern int cx25821_set_tvnorm(struct cx25821_dev *dev, v4l2_std_id norm);
+#endif
+
+extern int cx25821_res_get(struct cx25821_dev *dev, struct cx25821_fh *fh,
+ unsigned int bit);
+extern int cx25821_res_check(struct cx25821_fh *fh, unsigned int bit);
+extern int cx25821_res_locked(struct cx25821_fh *fh, unsigned int bit);
+extern void cx25821_res_free(struct cx25821_dev *dev, struct cx25821_fh *fh,
+ unsigned int bits);
+extern int cx25821_video_mux(struct cx25821_dev *dev, unsigned int input);
+extern int cx25821_start_video_dma(struct cx25821_dev *dev,
+ struct cx25821_dmaqueue *q,
+ struct cx25821_buffer *buf,
+ struct sram_channel *channel);
+
+extern int cx25821_set_scale(struct cx25821_dev *dev, unsigned int width,
+ unsigned int height, enum v4l2_field field);
+extern int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status);
+extern void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num);
+extern int cx25821_video_register(struct cx25821_dev *dev);
+extern int cx25821_get_format_size(void);
+
+extern int cx25821_buffer_setup(struct videobuf_queue *q, unsigned int *count,
+ unsigned int *size);
+extern int cx25821_buffer_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field);
+extern void cx25821_buffer_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb);
+extern struct videobuf_queue *get_queue(struct cx25821_fh *fh);
+extern int cx25821_get_resource(struct cx25821_fh *fh, int resource);
+extern int cx25821_video_mmap(struct file *file, struct vm_area_struct *vma);
+extern int cx25821_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f);
+extern int cx25821_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap);
+extern int cx25821_vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f);
+extern int cx25821_vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p);
+extern int cx25821_vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *p);
+extern int cx25821_vidioc_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p);
+extern int cx25821_vidioc_s_std(struct file *file, void *priv,
+ v4l2_std_id *tvnorms);
+extern int cx25821_enum_input(struct cx25821_dev *dev, struct v4l2_input *i);
+extern int cx25821_vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i);
+extern int cx25821_vidioc_g_input(struct file *file, void *priv,
+ unsigned int *i);
+extern int cx25821_vidioc_s_input(struct file *file, void *priv,
+ unsigned int i);
+extern int cx25821_vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctl);
+extern int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f);
+extern int cx25821_vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f);
+extern int cx25821_set_freq(struct cx25821_dev *dev, struct v4l2_frequency *f);
+extern int cx25821_vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f);
+extern int cx25821_vidioc_g_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg);
+extern int cx25821_vidioc_s_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg);
+extern int cx25821_vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t);
+extern int cx25821_vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t);
+
+extern int cx25821_is_valid_width(u32 width, v4l2_std_id tvnorm);
+extern int cx25821_is_valid_height(u32 height, v4l2_std_id tvnorm);
+
+extern int cx25821_vidioc_g_priority(struct file *file, void *f,
+ enum v4l2_priority *p);
+extern int cx25821_vidioc_s_priority(struct file *file, void *f,
+ enum v4l2_priority prio);
+
+extern int cx25821_vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qctrl);
+extern int cx25821_set_control(struct cx25821_dev *dev,
+ struct v4l2_control *ctrl, int chan_num);
+
+extern int cx25821_vidioc_cropcap(struct file *file, void *fh,
+ struct v4l2_cropcap *cropcap);
+extern int cx25821_vidioc_s_crop(struct file *file, void *priv,
+ struct v4l2_crop *crop);
+extern int cx25821_vidioc_g_crop(struct file *file, void *priv,
+ struct v4l2_crop *crop);
+
+extern int cx25821_vidioc_querystd(struct file *file, void *priv,
+ v4l2_std_id *norm);
+#endif
diff --git a/drivers/media/video/cx25821/cx25821.h b/drivers/media/video/cx25821/cx25821.h
new file mode 100644
index 00000000..b9aa801b
--- /dev/null
+++ b/drivers/media/video/cx25821/cx25821.h
@@ -0,0 +1,617 @@
+/*
+ * Driver for the Conexant CX25821 PCIe bridge
+ *
+ * Copyright (C) 2009 Conexant Systems Inc.
+ * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
+ * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef CX25821_H_
+#define CX25821_H_
+
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/kdev_t.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/tuner.h>
+#include <media/tveeprom.h>
+#include <media/videobuf-dma-sg.h>
+#include <media/videobuf-dvb.h>
+
+#include "btcx-risc.h"
+#include "cx25821-reg.h"
+#include "cx25821-medusa-reg.h"
+#include "cx25821-sram.h"
+#include "cx25821-audio.h"
+#include "media/cx2341x.h"
+
+#include <linux/version.h>
+#include <linux/mutex.h>
+
+#define CX25821_VERSION_CODE KERNEL_VERSION(0, 0, 106)
+
+#define UNSET (-1U)
+#define NO_SYNC_LINE (-1U)
+
+#define CX25821_MAXBOARDS 2
+
+#define TRUE 1
+#define FALSE 0
+#define LINE_SIZE_D1 1440
+
+/* Number of decoders and encoders */
+#define MAX_DECODERS 8
+#define MAX_ENCODERS 2
+#define QUAD_DECODERS 4
+#define MAX_CAMERAS 16
+
+/* Max number of inputs by card */
+#define MAX_CX25821_INPUT 8
+#define INPUT(nr) (&cx25821_boards[dev->board].input[nr])
+#define RESOURCE_VIDEO0 1
+#define RESOURCE_VIDEO1 2
+#define RESOURCE_VIDEO2 4
+#define RESOURCE_VIDEO3 8
+#define RESOURCE_VIDEO4 16
+#define RESOURCE_VIDEO5 32
+#define RESOURCE_VIDEO6 64
+#define RESOURCE_VIDEO7 128
+#define RESOURCE_VIDEO8 256
+#define RESOURCE_VIDEO9 512
+#define RESOURCE_VIDEO10 1024
+#define RESOURCE_VIDEO11 2048
+#define RESOURCE_VIDEO_IOCTL 4096
+
+#define BUFFER_TIMEOUT (HZ) /* 0.5 seconds */
+
+#define UNKNOWN_BOARD 0
+#define CX25821_BOARD 1
+
+/* Currently supported by the driver */
+#define CX25821_NORMS (\
+ V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_JP | V4L2_STD_NTSC_M_KR | \
+ V4L2_STD_PAL_BG | V4L2_STD_PAL_DK | V4L2_STD_PAL_I | \
+ V4L2_STD_PAL_M | V4L2_STD_PAL_N | V4L2_STD_PAL_H | \
+ V4L2_STD_PAL_Nc)
+
+#define CX25821_BOARD_CONEXANT_ATHENA10 1
+#define MAX_VID_CHANNEL_NUM 12
+#define VID_CHANNEL_NUM 8
+#define CX25821_NR_INPUT 2
+
+struct cx25821_fmt {
+ char *name;
+ u32 fourcc; /* v4l2 format id */
+ int depth;
+ int flags;
+ u32 cxformat;
+};
+
+struct cx25821_ctrl {
+ struct v4l2_queryctrl v;
+ u32 off;
+ u32 reg;
+ u32 mask;
+ u32 shift;
+};
+
+struct cx25821_tvnorm {
+ char *name;
+ v4l2_std_id id;
+ u32 cxiformat;
+ u32 cxoformat;
+};
+
+struct cx25821_fh {
+ struct cx25821_dev *dev;
+ enum v4l2_buf_type type;
+ int radio;
+ u32 resources;
+
+ enum v4l2_priority prio;
+
+ /* video overlay */
+ struct v4l2_window win;
+ struct v4l2_clip *clips;
+ unsigned int nclips;
+
+ /* video capture */
+ struct cx25821_fmt *fmt;
+ unsigned int width, height;
+ int channel_id;
+
+ /* vbi capture */
+ struct videobuf_queue vidq;
+ struct videobuf_queue vbiq;
+
+ /* H264 Encoder specifics ONLY */
+ struct videobuf_queue mpegq;
+ atomic_t v4l_reading;
+};
+
+enum cx25821_itype {
+ CX25821_VMUX_COMPOSITE = 1,
+ CX25821_VMUX_SVIDEO,
+ CX25821_VMUX_DEBUG,
+ CX25821_RADIO,
+};
+
+enum cx25821_src_sel_type {
+ CX25821_SRC_SEL_EXT_656_VIDEO = 0,
+ CX25821_SRC_SEL_PARALLEL_MPEG_VIDEO
+};
+
+/* buffer for one video frame */
+struct cx25821_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+
+ /* cx25821 specific */
+ unsigned int bpl;
+ struct btcx_riscmem risc;
+ struct cx25821_fmt *fmt;
+ u32 count;
+};
+
+struct cx25821_input {
+ enum cx25821_itype type;
+ unsigned int vmux;
+ u32 gpio0, gpio1, gpio2, gpio3;
+};
+
+enum port {
+ CX25821_UNDEFINED = 0,
+ CX25821_RAW,
+ CX25821_264
+};
+
+struct cx25821_board {
+ char *name;
+ enum port porta;
+ enum port portb;
+ enum port portc;
+ unsigned int tuner_type;
+ unsigned int radio_type;
+ unsigned char tuner_addr;
+ unsigned char radio_addr;
+
+ u32 clk_freq;
+ struct cx25821_input input[CX25821_NR_INPUT];
+};
+
+struct cx25821_subid {
+ u16 subvendor;
+ u16 subdevice;
+ u32 card;
+};
+
+struct cx25821_i2c {
+ struct cx25821_dev *dev;
+
+ int nr;
+
+ /* i2c i/o */
+ struct i2c_adapter i2c_adap;
+ struct i2c_algo_bit_data i2c_algo;
+ struct i2c_client i2c_client;
+ u32 i2c_rc;
+
+ /* cx25821 registers used for raw addess */
+ u32 i2c_period;
+ u32 reg_ctrl;
+ u32 reg_stat;
+ u32 reg_addr;
+ u32 reg_rdata;
+ u32 reg_wdata;
+};
+
+struct cx25821_dmaqueue {
+ struct list_head active;
+ struct list_head queued;
+ struct timer_list timeout;
+ struct btcx_riscmem stopper;
+ u32 count;
+};
+
+struct cx25821_data {
+ struct cx25821_dev *dev;
+ struct sram_channel *channel;
+};
+
+struct cx25821_channel {
+ struct v4l2_prio_state prio;
+
+ int ctl_bright;
+ int ctl_contrast;
+ int ctl_hue;
+ int ctl_saturation;
+ struct cx25821_data timeout_data;
+
+ struct video_device *video_dev;
+ struct cx25821_dmaqueue vidq;
+
+ struct sram_channel *sram_channels;
+
+ struct mutex lock;
+ int resources;
+
+ int pixel_formats;
+ int use_cif_resolution;
+ int cif_width;
+};
+
+struct cx25821_dev {
+ struct list_head devlist;
+ atomic_t refcount;
+ struct v4l2_device v4l2_dev;
+
+ /* pci stuff */
+ struct pci_dev *pci;
+ unsigned char pci_rev, pci_lat;
+ int pci_bus, pci_slot;
+ u32 base_io_addr;
+ u32 __iomem *lmmio;
+ u8 __iomem *bmmio;
+ int pci_irqmask;
+ int hwrevision;
+
+ u32 clk_freq;
+
+ /* I2C adapters: Master 1 & 2 (External) & Master 3 (Internal only) */
+ struct cx25821_i2c i2c_bus[3];
+
+ int nr;
+ struct mutex lock;
+
+ struct cx25821_channel channels[MAX_VID_CHANNEL_NUM];
+
+ /* board details */
+ unsigned int board;
+ char name[32];
+
+ /* Analog video */
+ u32 resources;
+ unsigned int input;
+ u32 tvaudio;
+ v4l2_std_id tvnorm;
+ unsigned int tuner_type;
+ unsigned char tuner_addr;
+ unsigned int radio_type;
+ unsigned char radio_addr;
+ unsigned int has_radio;
+ unsigned int videc_type;
+ unsigned char videc_addr;
+ unsigned short _max_num_decoders;
+
+ /* Analog Audio Upstream */
+ int _audio_is_running;
+ int _audiopixel_format;
+ int _is_first_audio_frame;
+ int _audiofile_status;
+ int _audio_lines_count;
+ int _audioframe_count;
+ int _audio_upstream_channel;
+ int _last_index_irq; /* The last interrupt index processed. */
+
+ __le32 *_risc_audio_jmp_addr;
+ __le32 *_risc_virt_start_addr;
+ __le32 *_risc_virt_addr;
+ dma_addr_t _risc_phys_addr;
+ dma_addr_t _risc_phys_start_addr;
+
+ unsigned int _audiorisc_size;
+ unsigned int _audiodata_buf_size;
+ __le32 *_audiodata_buf_virt_addr;
+ dma_addr_t _audiodata_buf_phys_addr;
+ char *_audiofilename;
+
+ /* V4l */
+ u32 freq;
+ struct video_device *vbi_dev;
+ struct video_device *radio_dev;
+ struct video_device *ioctl_dev;
+
+ spinlock_t slock;
+
+ /* Video Upstream */
+ int _line_size;
+ int _prog_cnt;
+ int _pixel_format;
+ int _is_first_frame;
+ int _is_running;
+ int _file_status;
+ int _lines_count;
+ int _frame_count;
+ int _channel_upstream_select;
+ unsigned int _risc_size;
+
+ __le32 *_dma_virt_start_addr;
+ __le32 *_dma_virt_addr;
+ dma_addr_t _dma_phys_addr;
+ dma_addr_t _dma_phys_start_addr;
+
+ unsigned int _data_buf_size;
+ __le32 *_data_buf_virt_addr;
+ dma_addr_t _data_buf_phys_addr;
+ char *_filename;
+ char *_defaultname;
+
+ int _line_size_ch2;
+ int _prog_cnt_ch2;
+ int _pixel_format_ch2;
+ int _is_first_frame_ch2;
+ int _is_running_ch2;
+ int _file_status_ch2;
+ int _lines_count_ch2;
+ int _frame_count_ch2;
+ int _channel2_upstream_select;
+ unsigned int _risc_size_ch2;
+
+ __le32 *_dma_virt_start_addr_ch2;
+ __le32 *_dma_virt_addr_ch2;
+ dma_addr_t _dma_phys_addr_ch2;
+ dma_addr_t _dma_phys_start_addr_ch2;
+
+ unsigned int _data_buf_size_ch2;
+ __le32 *_data_buf_virt_addr_ch2;
+ dma_addr_t _data_buf_phys_addr_ch2;
+ char *_filename_ch2;
+ char *_defaultname_ch2;
+
+ /* MPEG Encoder ONLY settings */
+ u32 cx23417_mailbox;
+ struct cx2341x_mpeg_params mpeg_params;
+ struct video_device *v4l_device;
+ atomic_t v4l_reader_count;
+ struct cx25821_tvnorm encodernorm;
+
+ u32 upstream_riscbuf_size;
+ u32 upstream_databuf_size;
+ u32 upstream_riscbuf_size_ch2;
+ u32 upstream_databuf_size_ch2;
+ u32 audio_upstream_riscbuf_size;
+ u32 audio_upstream_databuf_size;
+ int _isNTSC;
+ int _frame_index;
+ int _audioframe_index;
+ struct workqueue_struct *_irq_queues;
+ struct work_struct _irq_work_entry;
+ struct workqueue_struct *_irq_queues_ch2;
+ struct work_struct _irq_work_entry_ch2;
+ struct workqueue_struct *_irq_audio_queues;
+ struct work_struct _audio_work_entry;
+ char *input_filename;
+ char *input_filename_ch2;
+ int _frame_index_ch2;
+ int _isNTSC_ch2;
+ char *vid_stdname_ch2;
+ int pixel_format_ch2;
+ int channel_select_ch2;
+ int command_ch2;
+ char *input_audiofilename;
+ char *vid_stdname;
+ int pixel_format;
+ int channel_select;
+ int command;
+ int channel_opened;
+};
+
+struct upstream_user_struct {
+ char *input_filename;
+ char *vid_stdname;
+ int pixel_format;
+ int channel_select;
+ int command;
+};
+
+struct downstream_user_struct {
+ char *vid_stdname;
+ int pixel_format;
+ int cif_resolution_enable;
+ int cif_width;
+ int decoder_select;
+ int command;
+ int reg_address;
+ int reg_data;
+};
+
+extern struct upstream_user_struct *up_data;
+
+static inline struct cx25821_dev *get_cx25821(struct v4l2_device *v4l2_dev)
+{
+ return container_of(v4l2_dev, struct cx25821_dev, v4l2_dev);
+}
+
+#define cx25821_call_all(dev, o, f, args...) \
+ v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args)
+
+extern struct list_head cx25821_devlist;
+extern struct mutex cx25821_devlist_mutex;
+
+extern struct cx25821_board cx25821_boards[];
+extern struct cx25821_subid cx25821_subids[];
+
+#define SRAM_CH00 0 /* Video A */
+#define SRAM_CH01 1 /* Video B */
+#define SRAM_CH02 2 /* Video C */
+#define SRAM_CH03 3 /* Video D */
+#define SRAM_CH04 4 /* Video E */
+#define SRAM_CH05 5 /* Video F */
+#define SRAM_CH06 6 /* Video G */
+#define SRAM_CH07 7 /* Video H */
+
+#define SRAM_CH08 8 /* Audio A */
+#define SRAM_CH09 9 /* Video Upstream I */
+#define SRAM_CH10 10 /* Video Upstream J */
+#define SRAM_CH11 11 /* Audio Upstream AUD_CHANNEL_B */
+
+#define VID_UPSTREAM_SRAM_CHANNEL_I SRAM_CH09
+#define VID_UPSTREAM_SRAM_CHANNEL_J SRAM_CH10
+#define AUDIO_UPSTREAM_SRAM_CHANNEL_B SRAM_CH11
+#define VIDEO_IOCTL_CH 11
+
+struct sram_channel {
+ char *name;
+ u32 i;
+ u32 cmds_start;
+ u32 ctrl_start;
+ u32 cdt;
+ u32 fifo_start;
+ u32 fifo_size;
+ u32 ptr1_reg;
+ u32 ptr2_reg;
+ u32 cnt1_reg;
+ u32 cnt2_reg;
+ u32 int_msk;
+ u32 int_stat;
+ u32 int_mstat;
+ u32 dma_ctl;
+ u32 gpcnt_ctl;
+ u32 gpcnt;
+ u32 aud_length;
+ u32 aud_cfg;
+ u32 fld_aud_fifo_en;
+ u32 fld_aud_risc_en;
+
+ /* For Upstream Video */
+ u32 vid_fmt_ctl;
+ u32 vid_active_ctl1;
+ u32 vid_active_ctl2;
+ u32 vid_cdt_size;
+
+ u32 vip_ctl;
+ u32 pix_frmt;
+ u32 jumponly;
+ u32 irq_bit;
+};
+extern struct sram_channel cx25821_sram_channels[];
+
+#define STATUS_SUCCESS 0
+#define STATUS_UNSUCCESSFUL -1
+
+#define cx_read(reg) readl(dev->lmmio + ((reg)>>2))
+#define cx_write(reg, value) writel((value), dev->lmmio + ((reg)>>2))
+
+#define cx_andor(reg, mask, value) \
+ writel((readl(dev->lmmio+((reg)>>2)) & ~(mask)) |\
+ ((value) & (mask)), dev->lmmio+((reg)>>2))
+
+#define cx_set(reg, bit) cx_andor((reg), (bit), (bit))
+#define cx_clear(reg, bit) cx_andor((reg), (bit), 0)
+
+#define Set_GPIO_Bit(Bit) (1 << Bit)
+#define Clear_GPIO_Bit(Bit) (~(1 << Bit))
+
+#define CX25821_ERR(fmt, args...) \
+ pr_err("(%d): " fmt, dev->board, ##args)
+#define CX25821_WARN(fmt, args...) \
+ pr_warn("(%d): " fmt, dev->board, ##args)
+#define CX25821_INFO(fmt, args...) \
+ pr_info("(%d): " fmt, dev->board, ##args)
+
+extern int cx25821_i2c_register(struct cx25821_i2c *bus);
+extern void cx25821_card_setup(struct cx25821_dev *dev);
+extern int cx25821_ir_init(struct cx25821_dev *dev);
+extern int cx25821_i2c_read(struct cx25821_i2c *bus, u16 reg_addr, int *value);
+extern int cx25821_i2c_write(struct cx25821_i2c *bus, u16 reg_addr, int value);
+extern int cx25821_i2c_unregister(struct cx25821_i2c *bus);
+extern void cx25821_gpio_init(struct cx25821_dev *dev);
+extern void cx25821_set_gpiopin_direction(struct cx25821_dev *dev,
+ int pin_number, int pin_logic_value);
+
+extern int medusa_video_init(struct cx25821_dev *dev);
+extern int medusa_set_videostandard(struct cx25821_dev *dev);
+extern void medusa_set_resolution(struct cx25821_dev *dev, int width,
+ int decoder_select);
+extern int medusa_set_brightness(struct cx25821_dev *dev, int brightness,
+ int decoder);
+extern int medusa_set_contrast(struct cx25821_dev *dev, int contrast,
+ int decoder);
+extern int medusa_set_hue(struct cx25821_dev *dev, int hue, int decoder);
+extern int medusa_set_saturation(struct cx25821_dev *dev, int saturation,
+ int decoder);
+
+extern int cx25821_sram_channel_setup(struct cx25821_dev *dev,
+ struct sram_channel *ch, unsigned int bpl,
+ u32 risc);
+
+extern int cx25821_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+ struct scatterlist *sglist,
+ unsigned int top_offset,
+ unsigned int bottom_offset,
+ unsigned int bpl,
+ unsigned int padding, unsigned int lines);
+extern int cx25821_risc_databuffer_audio(struct pci_dev *pci,
+ struct btcx_riscmem *risc,
+ struct scatterlist *sglist,
+ unsigned int bpl,
+ unsigned int lines, unsigned int lpi);
+extern void cx25821_free_buffer(struct videobuf_queue *q,
+ struct cx25821_buffer *buf);
+extern int cx25821_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
+ u32 reg, u32 mask, u32 value);
+extern void cx25821_sram_channel_dump(struct cx25821_dev *dev,
+ struct sram_channel *ch);
+extern void cx25821_sram_channel_dump_audio(struct cx25821_dev *dev,
+ struct sram_channel *ch);
+
+extern struct cx25821_dev *cx25821_dev_get(struct pci_dev *pci);
+extern void cx25821_print_irqbits(char *name, char *tag, char **strings,
+ int len, u32 bits, u32 mask);
+extern void cx25821_dev_unregister(struct cx25821_dev *dev);
+extern int cx25821_sram_channel_setup_audio(struct cx25821_dev *dev,
+ struct sram_channel *ch,
+ unsigned int bpl, u32 risc);
+
+extern int cx25821_vidupstream_init_ch1(struct cx25821_dev *dev,
+ int channel_select, int pixel_format);
+extern int cx25821_vidupstream_init_ch2(struct cx25821_dev *dev,
+ int channel_select, int pixel_format);
+extern int cx25821_audio_upstream_init(struct cx25821_dev *dev,
+ int channel_select);
+extern void cx25821_free_mem_upstream_ch1(struct cx25821_dev *dev);
+extern void cx25821_free_mem_upstream_ch2(struct cx25821_dev *dev);
+extern void cx25821_free_mem_upstream_audio(struct cx25821_dev *dev);
+extern void cx25821_start_upstream_video_ch1(struct cx25821_dev *dev,
+ struct upstream_user_struct
+ *up_data);
+extern void cx25821_start_upstream_video_ch2(struct cx25821_dev *dev,
+ struct upstream_user_struct
+ *up_data);
+extern void cx25821_start_upstream_audio(struct cx25821_dev *dev,
+ struct upstream_user_struct *up_data);
+extern void cx25821_stop_upstream_video_ch1(struct cx25821_dev *dev);
+extern void cx25821_stop_upstream_video_ch2(struct cx25821_dev *dev);
+extern void cx25821_stop_upstream_audio(struct cx25821_dev *dev);
+extern int cx25821_sram_channel_setup_upstream(struct cx25821_dev *dev,
+ struct sram_channel *ch,
+ unsigned int bpl, u32 risc);
+extern void cx25821_set_pixel_format(struct cx25821_dev *dev, int channel,
+ u32 format);
+extern void cx25821_videoioctl_unregister(struct cx25821_dev *dev);
+extern struct video_device *cx25821_vdev_init(struct cx25821_dev *dev,
+ struct pci_dev *pci,
+ struct video_device *template,
+ char *type);
+#endif
diff --git a/drivers/media/video/cx25840/Kconfig b/drivers/media/video/cx25840/Kconfig
new file mode 100644
index 00000000..451133ad
--- /dev/null
+++ b/drivers/media/video/cx25840/Kconfig
@@ -0,0 +1,8 @@
+config VIDEO_CX25840
+ tristate "Conexant CX2584x audio/video decoders"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Conexant CX2584x audio/video decoders.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cx25840
diff --git a/drivers/media/video/cx25840/Makefile b/drivers/media/video/cx25840/Makefile
new file mode 100644
index 00000000..dc40dde2
--- /dev/null
+++ b/drivers/media/video/cx25840/Makefile
@@ -0,0 +1,6 @@
+cx25840-objs := cx25840-core.o cx25840-audio.o cx25840-firmware.o \
+ cx25840-vbi.o cx25840-ir.o
+
+obj-$(CONFIG_VIDEO_CX25840) += cx25840.o
+
+ccflags-y += -Idrivers/media/video
diff --git a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/video/cx25840/cx25840-audio.c
new file mode 100644
index 00000000..34b96c7c
--- /dev/null
+++ b/drivers/media/video/cx25840/cx25840-audio.c
@@ -0,0 +1,571 @@
+/* cx25840 audio functions
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+#include <media/v4l2-common.h>
+#include <media/cx25840.h>
+
+#include "cx25840-core.h"
+
+/*
+ * Note: The PLL and SRC parameters are based on a reference frequency that
+ * would ideally be:
+ *
+ * NTSC Color subcarrier freq * 8 = 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz
+ *
+ * However, it's not the exact reference frequency that matters, only that the
+ * firmware and modules that comprise the driver for a particular board all
+ * use the same value (close to the ideal value).
+ *
+ * Comments below will note which reference frequency is assumed for various
+ * parameters. They will usually be one of
+ *
+ * ref_freq = 28.636360 MHz
+ * or
+ * ref_freq = 28.636363 MHz
+ */
+
+static int cx25840_set_audclk_freq(struct i2c_client *client, u32 freq)
+{
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+
+ if (state->aud_input != CX25840_AUDIO_SERIAL) {
+ switch (freq) {
+ case 32000:
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x06, AUX PLL Post Divider = 0x10
+ */
+ cx25840_write4(client, 0x108, 0x1006040f);
+
+ /*
+ * VID_PLL Fraction (register 0x10c) = 0x2be2fe
+ * 28636360 * 0xf.15f17f0/4 = 108 MHz
+ * 432 MHz pre-postdivide
+ */
+
+ /*
+ * AUX_PLL Fraction = 0x1bb39ee
+ * 28636363 * 0x6.dd9cf70/0x10 = 32000 * 384
+ * 196.6 MHz pre-postdivide
+ * FIXME < 200 MHz is out of specified valid range
+ * FIXME 28636363 ref_freq doesn't match VID PLL ref
+ */
+ cx25840_write4(client, 0x110, 0x01bb39ee);
+
+ /*
+ * SA_MCLK_SEL = 1
+ * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider
+ */
+ cx25840_write(client, 0x127, 0x50);
+
+ if (is_cx2583x(state))
+ break;
+
+ /* src3/4/6_ctl */
+ /* 0x1.f77f = (4 * 28636360/8 * 2/455) / 32000 */
+ cx25840_write4(client, 0x900, 0x0801f77f);
+ cx25840_write4(client, 0x904, 0x0801f77f);
+ cx25840_write4(client, 0x90c, 0x0801f77f);
+ break;
+
+ case 44100:
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x09, AUX PLL Post Divider = 0x10
+ */
+ cx25840_write4(client, 0x108, 0x1009040f);
+
+ /*
+ * VID_PLL Fraction (register 0x10c) = 0x2be2fe
+ * 28636360 * 0xf.15f17f0/4 = 108 MHz
+ * 432 MHz pre-postdivide
+ */
+
+ /*
+ * AUX_PLL Fraction = 0x0ec6bd6
+ * 28636363 * 0x9.7635eb0/0x10 = 44100 * 384
+ * 271 MHz pre-postdivide
+ * FIXME 28636363 ref_freq doesn't match VID PLL ref
+ */
+ cx25840_write4(client, 0x110, 0x00ec6bd6);
+
+ /*
+ * SA_MCLK_SEL = 1
+ * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider
+ */
+ cx25840_write(client, 0x127, 0x50);
+
+ if (is_cx2583x(state))
+ break;
+
+ /* src3/4/6_ctl */
+ /* 0x1.6d59 = (4 * 28636360/8 * 2/455) / 44100 */
+ cx25840_write4(client, 0x900, 0x08016d59);
+ cx25840_write4(client, 0x904, 0x08016d59);
+ cx25840_write4(client, 0x90c, 0x08016d59);
+ break;
+
+ case 48000:
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x0a, AUX PLL Post Divider = 0x10
+ */
+ cx25840_write4(client, 0x108, 0x100a040f);
+
+ /*
+ * VID_PLL Fraction (register 0x10c) = 0x2be2fe
+ * 28636360 * 0xf.15f17f0/4 = 108 MHz
+ * 432 MHz pre-postdivide
+ */
+
+ /*
+ * AUX_PLL Fraction = 0x098d6e5
+ * 28636363 * 0xa.4c6b728/0x10 = 48000 * 384
+ * 295 MHz pre-postdivide
+ * FIXME 28636363 ref_freq doesn't match VID PLL ref
+ */
+ cx25840_write4(client, 0x110, 0x0098d6e5);
+
+ /*
+ * SA_MCLK_SEL = 1
+ * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider
+ */
+ cx25840_write(client, 0x127, 0x50);
+
+ if (is_cx2583x(state))
+ break;
+
+ /* src3/4/6_ctl */
+ /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */
+ cx25840_write4(client, 0x900, 0x08014faa);
+ cx25840_write4(client, 0x904, 0x08014faa);
+ cx25840_write4(client, 0x90c, 0x08014faa);
+ break;
+ }
+ } else {
+ switch (freq) {
+ case 32000:
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x08, AUX PLL Post Divider = 0x1e
+ */
+ cx25840_write4(client, 0x108, 0x1e08040f);
+
+ /*
+ * VID_PLL Fraction (register 0x10c) = 0x2be2fe
+ * 28636360 * 0xf.15f17f0/4 = 108 MHz
+ * 432 MHz pre-postdivide
+ */
+
+ /*
+ * AUX_PLL Fraction = 0x12a0869
+ * 28636363 * 0x8.9504348/0x1e = 32000 * 256
+ * 246 MHz pre-postdivide
+ * FIXME 28636363 ref_freq doesn't match VID PLL ref
+ */
+ cx25840_write4(client, 0x110, 0x012a0869);
+
+ /*
+ * SA_MCLK_SEL = 1
+ * SA_MCLK_DIV = 0x14 = 256/384 * AUX_PLL post dvivider
+ */
+ cx25840_write(client, 0x127, 0x54);
+
+ if (is_cx2583x(state))
+ break;
+
+ /* src1_ctl */
+ /* 0x1.0000 = 32000/32000 */
+ cx25840_write4(client, 0x8f8, 0x08010000);
+
+ /* src3/4/6_ctl */
+ /* 0x2.0000 = 2 * (32000/32000) */
+ cx25840_write4(client, 0x900, 0x08020000);
+ cx25840_write4(client, 0x904, 0x08020000);
+ cx25840_write4(client, 0x90c, 0x08020000);
+ break;
+
+ case 44100:
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x09, AUX PLL Post Divider = 0x18
+ */
+ cx25840_write4(client, 0x108, 0x1809040f);
+
+ /*
+ * VID_PLL Fraction (register 0x10c) = 0x2be2fe
+ * 28636360 * 0xf.15f17f0/4 = 108 MHz
+ * 432 MHz pre-postdivide
+ */
+
+ /*
+ * AUX_PLL Fraction = 0x0ec6bd6
+ * 28636363 * 0x9.7635eb0/0x18 = 44100 * 256
+ * 271 MHz pre-postdivide
+ * FIXME 28636363 ref_freq doesn't match VID PLL ref
+ */
+ cx25840_write4(client, 0x110, 0x00ec6bd6);
+
+ /*
+ * SA_MCLK_SEL = 1
+ * SA_MCLK_DIV = 0x10 = 256/384 * AUX_PLL post dvivider
+ */
+ cx25840_write(client, 0x127, 0x50);
+
+ if (is_cx2583x(state))
+ break;
+
+ /* src1_ctl */
+ /* 0x1.60cd = 44100/32000 */
+ cx25840_write4(client, 0x8f8, 0x080160cd);
+
+ /* src3/4/6_ctl */
+ /* 0x1.7385 = 2 * (32000/44100) */
+ cx25840_write4(client, 0x900, 0x08017385);
+ cx25840_write4(client, 0x904, 0x08017385);
+ cx25840_write4(client, 0x90c, 0x08017385);
+ break;
+
+ case 48000:
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x0a, AUX PLL Post Divider = 0x18
+ */
+ cx25840_write4(client, 0x108, 0x180a040f);
+
+ /*
+ * VID_PLL Fraction (register 0x10c) = 0x2be2fe
+ * 28636360 * 0xf.15f17f0/4 = 108 MHz
+ * 432 MHz pre-postdivide
+ */
+
+ /*
+ * AUX_PLL Fraction = 0x098d6e5
+ * 28636363 * 0xa.4c6b728/0x18 = 48000 * 256
+ * 295 MHz pre-postdivide
+ * FIXME 28636363 ref_freq doesn't match VID PLL ref
+ */
+ cx25840_write4(client, 0x110, 0x0098d6e5);
+
+ /*
+ * SA_MCLK_SEL = 1
+ * SA_MCLK_DIV = 0x10 = 256/384 * AUX_PLL post dvivider
+ */
+ cx25840_write(client, 0x127, 0x50);
+
+ if (is_cx2583x(state))
+ break;
+
+ /* src1_ctl */
+ /* 0x1.8000 = 48000/32000 */
+ cx25840_write4(client, 0x8f8, 0x08018000);
+
+ /* src3/4/6_ctl */
+ /* 0x1.5555 = 2 * (32000/48000) */
+ cx25840_write4(client, 0x900, 0x08015555);
+ cx25840_write4(client, 0x904, 0x08015555);
+ cx25840_write4(client, 0x90c, 0x08015555);
+ break;
+ }
+ }
+
+ state->audclk_freq = freq;
+
+ return 0;
+}
+
+static inline int cx25836_set_audclk_freq(struct i2c_client *client, u32 freq)
+{
+ return cx25840_set_audclk_freq(client, freq);
+}
+
+static int cx23885_set_audclk_freq(struct i2c_client *client, u32 freq)
+{
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+
+ if (state->aud_input != CX25840_AUDIO_SERIAL) {
+ switch (freq) {
+ case 32000:
+ case 44100:
+ case 48000:
+ /* We don't have register values
+ * so avoid destroying registers. */
+ /* FIXME return -EINVAL; */
+ break;
+ }
+ } else {
+ switch (freq) {
+ case 32000:
+ case 44100:
+ /* We don't have register values
+ * so avoid destroying registers. */
+ /* FIXME return -EINVAL; */
+ break;
+
+ case 48000:
+ /* src1_ctl */
+ /* 0x1.867c = 48000 / (2 * 28636360/8 * 2/455) */
+ cx25840_write4(client, 0x8f8, 0x0801867c);
+
+ /* src3/4/6_ctl */
+ /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */
+ cx25840_write4(client, 0x900, 0x08014faa);
+ cx25840_write4(client, 0x904, 0x08014faa);
+ cx25840_write4(client, 0x90c, 0x08014faa);
+ break;
+ }
+ }
+
+ state->audclk_freq = freq;
+
+ return 0;
+}
+
+static int cx231xx_set_audclk_freq(struct i2c_client *client, u32 freq)
+{
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+
+ if (state->aud_input != CX25840_AUDIO_SERIAL) {
+ switch (freq) {
+ case 32000:
+ /* src3/4/6_ctl */
+ /* 0x1.f77f = (4 * 28636360/8 * 2/455) / 32000 */
+ cx25840_write4(client, 0x900, 0x0801f77f);
+ cx25840_write4(client, 0x904, 0x0801f77f);
+ cx25840_write4(client, 0x90c, 0x0801f77f);
+ break;
+
+ case 44100:
+ /* src3/4/6_ctl */
+ /* 0x1.6d59 = (4 * 28636360/8 * 2/455) / 44100 */
+ cx25840_write4(client, 0x900, 0x08016d59);
+ cx25840_write4(client, 0x904, 0x08016d59);
+ cx25840_write4(client, 0x90c, 0x08016d59);
+ break;
+
+ case 48000:
+ /* src3/4/6_ctl */
+ /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */
+ cx25840_write4(client, 0x900, 0x08014faa);
+ cx25840_write4(client, 0x904, 0x08014faa);
+ cx25840_write4(client, 0x90c, 0x08014faa);
+ break;
+ }
+ } else {
+ switch (freq) {
+ /* FIXME These cases make different assumptions about audclk */
+ case 32000:
+ /* src1_ctl */
+ /* 0x1.0000 = 32000/32000 */
+ cx25840_write4(client, 0x8f8, 0x08010000);
+
+ /* src3/4/6_ctl */
+ /* 0x2.0000 = 2 * (32000/32000) */
+ cx25840_write4(client, 0x900, 0x08020000);
+ cx25840_write4(client, 0x904, 0x08020000);
+ cx25840_write4(client, 0x90c, 0x08020000);
+ break;
+
+ case 44100:
+ /* src1_ctl */
+ /* 0x1.60cd = 44100/32000 */
+ cx25840_write4(client, 0x8f8, 0x080160cd);
+
+ /* src3/4/6_ctl */
+ /* 0x1.7385 = 2 * (32000/44100) */
+ cx25840_write4(client, 0x900, 0x08017385);
+ cx25840_write4(client, 0x904, 0x08017385);
+ cx25840_write4(client, 0x90c, 0x08017385);
+ break;
+
+ case 48000:
+ /* src1_ctl */
+ /* 0x1.867c = 48000 / (2 * 28636360/8 * 2/455) */
+ cx25840_write4(client, 0x8f8, 0x0801867c);
+
+ /* src3/4/6_ctl */
+ /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */
+ cx25840_write4(client, 0x900, 0x08014faa);
+ cx25840_write4(client, 0x904, 0x08014faa);
+ cx25840_write4(client, 0x90c, 0x08014faa);
+ break;
+ }
+ }
+
+ state->audclk_freq = freq;
+
+ return 0;
+}
+
+static int set_audclk_freq(struct i2c_client *client, u32 freq)
+{
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+
+ if (freq != 32000 && freq != 44100 && freq != 48000)
+ return -EINVAL;
+
+ if (is_cx231xx(state))
+ return cx231xx_set_audclk_freq(client, freq);
+
+ if (is_cx2388x(state))
+ return cx23885_set_audclk_freq(client, freq);
+
+ if (is_cx2583x(state))
+ return cx25836_set_audclk_freq(client, freq);
+
+ return cx25840_set_audclk_freq(client, freq);
+}
+
+void cx25840_audio_set_path(struct i2c_client *client)
+{
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+
+ if (!is_cx2583x(state)) {
+ /* assert soft reset */
+ cx25840_and_or(client, 0x810, ~0x1, 0x01);
+
+ /* stop microcontroller */
+ cx25840_and_or(client, 0x803, ~0x10, 0);
+
+ /* Mute everything to prevent the PFFT! */
+ cx25840_write(client, 0x8d3, 0x1f);
+
+ if (state->aud_input == CX25840_AUDIO_SERIAL) {
+ /* Set Path1 to Serial Audio Input */
+ cx25840_write4(client, 0x8d0, 0x01011012);
+
+ /* The microcontroller should not be started for the
+ * non-tuner inputs: autodetection is specific for
+ * TV audio. */
+ } else {
+ /* Set Path1 to Analog Demod Main Channel */
+ cx25840_write4(client, 0x8d0, 0x1f063870);
+ }
+ }
+
+ set_audclk_freq(client, state->audclk_freq);
+
+ if (!is_cx2583x(state)) {
+ if (state->aud_input != CX25840_AUDIO_SERIAL) {
+ /* When the microcontroller detects the
+ * audio format, it will unmute the lines */
+ cx25840_and_or(client, 0x803, ~0x10, 0x10);
+ }
+
+ /* deassert soft reset */
+ cx25840_and_or(client, 0x810, ~0x1, 0x00);
+
+ /* Ensure the controller is running when we exit */
+ if (is_cx2388x(state) || is_cx231xx(state))
+ cx25840_and_or(client, 0x803, ~0x10, 0x10);
+ }
+}
+
+static void set_volume(struct i2c_client *client, int volume)
+{
+ int vol;
+
+ /* Convert the volume to msp3400 values (0-127) */
+ vol = volume >> 9;
+
+ /* now scale it up to cx25840 values
+ * -114dB to -96dB maps to 0
+ * this should be 19, but in my testing that was 4dB too loud */
+ if (vol <= 23) {
+ vol = 0;
+ } else {
+ vol -= 23;
+ }
+
+ /* PATH1_VOLUME */
+ cx25840_write(client, 0x8d4, 228 - (vol * 2));
+}
+
+static void set_balance(struct i2c_client *client, int balance)
+{
+ int bal = balance >> 8;
+ if (bal > 0x80) {
+ /* PATH1_BAL_LEFT */
+ cx25840_and_or(client, 0x8d5, 0x7f, 0x80);
+ /* PATH1_BAL_LEVEL */
+ cx25840_and_or(client, 0x8d5, ~0x7f, bal & 0x7f);
+ } else {
+ /* PATH1_BAL_LEFT */
+ cx25840_and_or(client, 0x8d5, 0x7f, 0x00);
+ /* PATH1_BAL_LEVEL */
+ cx25840_and_or(client, 0x8d5, ~0x7f, 0x80 - bal);
+ }
+}
+
+int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct cx25840_state *state = to_state(sd);
+ int retval;
+
+ if (!is_cx2583x(state))
+ cx25840_and_or(client, 0x810, ~0x1, 1);
+ if (state->aud_input != CX25840_AUDIO_SERIAL) {
+ cx25840_and_or(client, 0x803, ~0x10, 0);
+ cx25840_write(client, 0x8d3, 0x1f);
+ }
+ retval = set_audclk_freq(client, freq);
+ if (state->aud_input != CX25840_AUDIO_SERIAL)
+ cx25840_and_or(client, 0x803, ~0x10, 0x10);
+ if (!is_cx2583x(state))
+ cx25840_and_or(client, 0x810, ~0x1, 0);
+ return retval;
+}
+
+static int cx25840_audio_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ if (state->mute->val)
+ set_volume(client, 0);
+ else
+ set_volume(client, state->volume->val);
+ break;
+ case V4L2_CID_AUDIO_BASS:
+ /* PATH1_EQ_BASS_VOL */
+ cx25840_and_or(client, 0x8d9, ~0x3f,
+ 48 - (ctrl->val * 48 / 0xffff));
+ break;
+ case V4L2_CID_AUDIO_TREBLE:
+ /* PATH1_EQ_TREBLE_VOL */
+ cx25840_and_or(client, 0x8db, ~0x3f,
+ 48 - (ctrl->val * 48 / 0xffff));
+ break;
+ case V4L2_CID_AUDIO_BALANCE:
+ set_balance(client, ctrl->val);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+const struct v4l2_ctrl_ops cx25840_audio_ctrl_ops = {
+ .s_ctrl = cx25840_audio_s_ctrl,
+};
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
new file mode 100644
index 00000000..fc1ff69c
--- /dev/null
+++ b/drivers/media/video/cx25840/cx25840-core.c
@@ -0,0 +1,5304 @@
+/* cx25840 - Conexant CX25840 audio/video decoder driver
+ *
+ * Copyright (C) 2004 Ulf Eklund
+ *
+ * Based on the saa7115 driver and on the first version of Chris Kennedy's
+ * cx25840 driver.
+ *
+ * Changes by Tyler Trafford <tatrafford@comcast.net>
+ * - cleanup/rewrite for V4L2 API (2005)
+ *
+ * VBI support by Hans Verkuil <hverkuil@xs4all.nl>.
+ *
+ * NTSC sliced VBI support by Christopher Neufeld <television@cneufeld.ca>
+ * with additional fixes by Hans Verkuil <hverkuil@xs4all.nl>.
+ *
+ * CX23885 support by Steven Toth <stoth@linuxtv.org>.
+ *
+ * CX2388[578] IRQ handling, IO Pin mux configuration and other small fixes are
+ * Copyright (C) 2010 Andy Walls <awalls@md.metrocast.net>
+ *
+ * CX23888 DIF support for the HVR1850
+ * Copyright (C) 2011 Steven Toth <stoth@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/math64.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/cx25840.h>
+
+#include "cx25840-core.h"
+
+MODULE_DESCRIPTION("Conexant CX25840 audio/video decoder driver");
+MODULE_AUTHOR("Ulf Eklund, Chris Kennedy, Hans Verkuil, Tyler Trafford");
+MODULE_LICENSE("GPL");
+
+#define CX25840_VID_INT_STAT_REG 0x410
+#define CX25840_VID_INT_STAT_BITS 0x0000ffff
+#define CX25840_VID_INT_MASK_BITS 0xffff0000
+#define CX25840_VID_INT_MASK_SHFT 16
+#define CX25840_VID_INT_MASK_REG 0x412
+
+#define CX23885_AUD_MC_INT_MASK_REG 0x80c
+#define CX23885_AUD_MC_INT_STAT_BITS 0xffff0000
+#define CX23885_AUD_MC_INT_CTRL_BITS 0x0000ffff
+#define CX23885_AUD_MC_INT_STAT_SHFT 16
+
+#define CX25840_AUD_INT_CTRL_REG 0x812
+#define CX25840_AUD_INT_STAT_REG 0x813
+
+#define CX23885_PIN_CTRL_IRQ_REG 0x123
+#define CX23885_PIN_CTRL_IRQ_IR_STAT 0x40
+#define CX23885_PIN_CTRL_IRQ_AUD_STAT 0x20
+#define CX23885_PIN_CTRL_IRQ_VID_STAT 0x10
+
+#define CX25840_IR_STATS_REG 0x210
+#define CX25840_IR_IRQEN_REG 0x214
+
+static int cx25840_debug;
+
+module_param_named(debug,cx25840_debug, int, 0644);
+
+MODULE_PARM_DESC(debug, "Debugging messages [0=Off (default) 1=On]");
+
+
+/* ----------------------------------------------------------------------- */
+static void cx23885_std_setup(struct i2c_client *client);
+
+int cx25840_write(struct i2c_client *client, u16 addr, u8 value)
+{
+ u8 buffer[3];
+ buffer[0] = addr >> 8;
+ buffer[1] = addr & 0xff;
+ buffer[2] = value;
+ return i2c_master_send(client, buffer, 3);
+}
+
+int cx25840_write4(struct i2c_client *client, u16 addr, u32 value)
+{
+ u8 buffer[6];
+ buffer[0] = addr >> 8;
+ buffer[1] = addr & 0xff;
+ buffer[2] = value & 0xff;
+ buffer[3] = (value >> 8) & 0xff;
+ buffer[4] = (value >> 16) & 0xff;
+ buffer[5] = value >> 24;
+ return i2c_master_send(client, buffer, 6);
+}
+
+u8 cx25840_read(struct i2c_client * client, u16 addr)
+{
+ struct i2c_msg msgs[2];
+ u8 tx_buf[2], rx_buf[1];
+
+ /* Write register address */
+ tx_buf[0] = addr >> 8;
+ tx_buf[1] = addr & 0xff;
+ msgs[0].addr = client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = 2;
+ msgs[0].buf = (char *) tx_buf;
+
+ /* Read data from register */
+ msgs[1].addr = client->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = 1;
+ msgs[1].buf = (char *) rx_buf;
+
+ if (i2c_transfer(client->adapter, msgs, 2) < 2)
+ return 0;
+
+ return rx_buf[0];
+}
+
+u32 cx25840_read4(struct i2c_client * client, u16 addr)
+{
+ struct i2c_msg msgs[2];
+ u8 tx_buf[2], rx_buf[4];
+
+ /* Write register address */
+ tx_buf[0] = addr >> 8;
+ tx_buf[1] = addr & 0xff;
+ msgs[0].addr = client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = 2;
+ msgs[0].buf = (char *) tx_buf;
+
+ /* Read data from registers */
+ msgs[1].addr = client->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = 4;
+ msgs[1].buf = (char *) rx_buf;
+
+ if (i2c_transfer(client->adapter, msgs, 2) < 2)
+ return 0;
+
+ return (rx_buf[3] << 24) | (rx_buf[2] << 16) | (rx_buf[1] << 8) |
+ rx_buf[0];
+}
+
+int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned and_mask,
+ u8 or_value)
+{
+ return cx25840_write(client, addr,
+ (cx25840_read(client, addr) & and_mask) |
+ or_value);
+}
+
+int cx25840_and_or4(struct i2c_client *client, u16 addr, u32 and_mask,
+ u32 or_value)
+{
+ return cx25840_write4(client, addr,
+ (cx25840_read4(client, addr) & and_mask) |
+ or_value);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input,
+ enum cx25840_audio_input aud_input);
+
+/* ----------------------------------------------------------------------- */
+
+static int cx23885_s_io_pin_config(struct v4l2_subdev *sd, size_t n,
+ struct v4l2_subdev_io_pin_config *p)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int i;
+ u32 pin_ctrl;
+ u8 gpio_oe, gpio_data, strength;
+
+ pin_ctrl = cx25840_read4(client, 0x120);
+ gpio_oe = cx25840_read(client, 0x160);
+ gpio_data = cx25840_read(client, 0x164);
+
+ for (i = 0; i < n; i++) {
+ strength = p[i].strength;
+ if (strength > CX25840_PIN_DRIVE_FAST)
+ strength = CX25840_PIN_DRIVE_FAST;
+
+ switch (p[i].pin) {
+ case CX23885_PIN_IRQ_N_GPIO16:
+ if (p[i].function != CX23885_PAD_IRQ_N) {
+ /* GPIO16 */
+ pin_ctrl &= ~(0x1 << 25);
+ } else {
+ /* IRQ_N */
+ if (p[i].flags &
+ (V4L2_SUBDEV_IO_PIN_DISABLE |
+ V4L2_SUBDEV_IO_PIN_INPUT)) {
+ pin_ctrl &= ~(0x1 << 25);
+ } else {
+ pin_ctrl |= (0x1 << 25);
+ }
+ if (p[i].flags &
+ V4L2_SUBDEV_IO_PIN_ACTIVE_LOW) {
+ pin_ctrl &= ~(0x1 << 24);
+ } else {
+ pin_ctrl |= (0x1 << 24);
+ }
+ }
+ break;
+ case CX23885_PIN_IR_RX_GPIO19:
+ if (p[i].function != CX23885_PAD_GPIO19) {
+ /* IR_RX */
+ gpio_oe |= (0x1 << 0);
+ pin_ctrl &= ~(0x3 << 18);
+ pin_ctrl |= (strength << 18);
+ } else {
+ /* GPIO19 */
+ gpio_oe &= ~(0x1 << 0);
+ if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
+ gpio_data &= ~(0x1 << 0);
+ gpio_data |= ((p[i].value & 0x1) << 0);
+ }
+ pin_ctrl &= ~(0x3 << 12);
+ pin_ctrl |= (strength << 12);
+ }
+ break;
+ case CX23885_PIN_IR_TX_GPIO20:
+ if (p[i].function != CX23885_PAD_GPIO20) {
+ /* IR_TX */
+ gpio_oe |= (0x1 << 1);
+ if (p[i].flags & V4L2_SUBDEV_IO_PIN_DISABLE)
+ pin_ctrl &= ~(0x1 << 10);
+ else
+ pin_ctrl |= (0x1 << 10);
+ pin_ctrl &= ~(0x3 << 18);
+ pin_ctrl |= (strength << 18);
+ } else {
+ /* GPIO20 */
+ gpio_oe &= ~(0x1 << 1);
+ if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
+ gpio_data &= ~(0x1 << 1);
+ gpio_data |= ((p[i].value & 0x1) << 1);
+ }
+ pin_ctrl &= ~(0x3 << 12);
+ pin_ctrl |= (strength << 12);
+ }
+ break;
+ case CX23885_PIN_I2S_SDAT_GPIO21:
+ if (p[i].function != CX23885_PAD_GPIO21) {
+ /* I2S_SDAT */
+ /* TODO: Input or Output config */
+ gpio_oe |= (0x1 << 2);
+ pin_ctrl &= ~(0x3 << 22);
+ pin_ctrl |= (strength << 22);
+ } else {
+ /* GPIO21 */
+ gpio_oe &= ~(0x1 << 2);
+ if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
+ gpio_data &= ~(0x1 << 2);
+ gpio_data |= ((p[i].value & 0x1) << 2);
+ }
+ pin_ctrl &= ~(0x3 << 12);
+ pin_ctrl |= (strength << 12);
+ }
+ break;
+ case CX23885_PIN_I2S_WCLK_GPIO22:
+ if (p[i].function != CX23885_PAD_GPIO22) {
+ /* I2S_WCLK */
+ /* TODO: Input or Output config */
+ gpio_oe |= (0x1 << 3);
+ pin_ctrl &= ~(0x3 << 22);
+ pin_ctrl |= (strength << 22);
+ } else {
+ /* GPIO22 */
+ gpio_oe &= ~(0x1 << 3);
+ if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
+ gpio_data &= ~(0x1 << 3);
+ gpio_data |= ((p[i].value & 0x1) << 3);
+ }
+ pin_ctrl &= ~(0x3 << 12);
+ pin_ctrl |= (strength << 12);
+ }
+ break;
+ case CX23885_PIN_I2S_BCLK_GPIO23:
+ if (p[i].function != CX23885_PAD_GPIO23) {
+ /* I2S_BCLK */
+ /* TODO: Input or Output config */
+ gpio_oe |= (0x1 << 4);
+ pin_ctrl &= ~(0x3 << 22);
+ pin_ctrl |= (strength << 22);
+ } else {
+ /* GPIO23 */
+ gpio_oe &= ~(0x1 << 4);
+ if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
+ gpio_data &= ~(0x1 << 4);
+ gpio_data |= ((p[i].value & 0x1) << 4);
+ }
+ pin_ctrl &= ~(0x3 << 12);
+ pin_ctrl |= (strength << 12);
+ }
+ break;
+ }
+ }
+
+ cx25840_write(client, 0x164, gpio_data);
+ cx25840_write(client, 0x160, gpio_oe);
+ cx25840_write4(client, 0x120, pin_ctrl);
+ return 0;
+}
+
+static int common_s_io_pin_config(struct v4l2_subdev *sd, size_t n,
+ struct v4l2_subdev_io_pin_config *pincfg)
+{
+ struct cx25840_state *state = to_state(sd);
+
+ if (is_cx2388x(state))
+ return cx23885_s_io_pin_config(sd, n, pincfg);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void init_dll1(struct i2c_client *client)
+{
+ /* This is the Hauppauge sequence used to
+ * initialize the Delay Lock Loop 1 (ADC DLL). */
+ cx25840_write(client, 0x159, 0x23);
+ cx25840_write(client, 0x15a, 0x87);
+ cx25840_write(client, 0x15b, 0x06);
+ udelay(10);
+ cx25840_write(client, 0x159, 0xe1);
+ udelay(10);
+ cx25840_write(client, 0x15a, 0x86);
+ cx25840_write(client, 0x159, 0xe0);
+ cx25840_write(client, 0x159, 0xe1);
+ cx25840_write(client, 0x15b, 0x10);
+}
+
+static void init_dll2(struct i2c_client *client)
+{
+ /* This is the Hauppauge sequence used to
+ * initialize the Delay Lock Loop 2 (ADC DLL). */
+ cx25840_write(client, 0x15d, 0xe3);
+ cx25840_write(client, 0x15e, 0x86);
+ cx25840_write(client, 0x15f, 0x06);
+ udelay(10);
+ cx25840_write(client, 0x15d, 0xe1);
+ cx25840_write(client, 0x15d, 0xe0);
+ cx25840_write(client, 0x15d, 0xe1);
+}
+
+static void cx25836_initialize(struct i2c_client *client)
+{
+ /* reset configuration is described on page 3-77 of the CX25836 datasheet */
+ /* 2. */
+ cx25840_and_or(client, 0x000, ~0x01, 0x01);
+ cx25840_and_or(client, 0x000, ~0x01, 0x00);
+ /* 3a. */
+ cx25840_and_or(client, 0x15a, ~0x70, 0x00);
+ /* 3b. */
+ cx25840_and_or(client, 0x15b, ~0x1e, 0x06);
+ /* 3c. */
+ cx25840_and_or(client, 0x159, ~0x02, 0x02);
+ /* 3d. */
+ udelay(10);
+ /* 3e. */
+ cx25840_and_or(client, 0x159, ~0x02, 0x00);
+ /* 3f. */
+ cx25840_and_or(client, 0x159, ~0xc0, 0xc0);
+ /* 3g. */
+ cx25840_and_or(client, 0x159, ~0x01, 0x00);
+ cx25840_and_or(client, 0x159, ~0x01, 0x01);
+ /* 3h. */
+ cx25840_and_or(client, 0x15b, ~0x1e, 0x10);
+}
+
+static void cx25840_work_handler(struct work_struct *work)
+{
+ struct cx25840_state *state = container_of(work, struct cx25840_state, fw_work);
+ cx25840_loadfw(state->c);
+ wake_up(&state->fw_wait);
+}
+
+static void cx25840_initialize(struct i2c_client *client)
+{
+ DEFINE_WAIT(wait);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+ struct workqueue_struct *q;
+
+ /* datasheet startup in numbered steps, refer to page 3-77 */
+ /* 2. */
+ cx25840_and_or(client, 0x803, ~0x10, 0x00);
+ /* The default of this register should be 4, but I get 0 instead.
+ * Set this register to 4 manually. */
+ cx25840_write(client, 0x000, 0x04);
+ /* 3. */
+ init_dll1(client);
+ init_dll2(client);
+ cx25840_write(client, 0x136, 0x0a);
+ /* 4. */
+ cx25840_write(client, 0x13c, 0x01);
+ cx25840_write(client, 0x13c, 0x00);
+ /* 5. */
+ /* Do the firmware load in a work handler to prevent.
+ Otherwise the kernel is blocked waiting for the
+ bit-banging i2c interface to finish uploading the
+ firmware. */
+ INIT_WORK(&state->fw_work, cx25840_work_handler);
+ init_waitqueue_head(&state->fw_wait);
+ q = create_singlethread_workqueue("cx25840_fw");
+ prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE);
+ queue_work(q, &state->fw_work);
+ schedule();
+ finish_wait(&state->fw_wait, &wait);
+ destroy_workqueue(q);
+
+ /* 6. */
+ cx25840_write(client, 0x115, 0x8c);
+ cx25840_write(client, 0x116, 0x07);
+ cx25840_write(client, 0x118, 0x02);
+ /* 7. */
+ cx25840_write(client, 0x4a5, 0x80);
+ cx25840_write(client, 0x4a5, 0x00);
+ cx25840_write(client, 0x402, 0x00);
+ /* 8. */
+ cx25840_and_or(client, 0x401, ~0x18, 0);
+ cx25840_and_or(client, 0x4a2, ~0x10, 0x10);
+ /* steps 8c and 8d are done in change_input() */
+ /* 10. */
+ cx25840_write(client, 0x8d3, 0x1f);
+ cx25840_write(client, 0x8e3, 0x03);
+
+ cx25840_std_setup(client);
+
+ /* trial and error says these are needed to get audio */
+ cx25840_write(client, 0x914, 0xa0);
+ cx25840_write(client, 0x918, 0xa0);
+ cx25840_write(client, 0x919, 0x01);
+
+ /* stereo preferred */
+ cx25840_write(client, 0x809, 0x04);
+ /* AC97 shift */
+ cx25840_write(client, 0x8cf, 0x0f);
+
+ /* (re)set input */
+ set_input(client, state->vid_input, state->aud_input);
+
+ /* start microcontroller */
+ cx25840_and_or(client, 0x803, ~0x10, 0x10);
+}
+
+static void cx23885_initialize(struct i2c_client *client)
+{
+ DEFINE_WAIT(wait);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+ struct workqueue_struct *q;
+
+ /*
+ * Come out of digital power down
+ * The CX23888, at least, needs this, otherwise registers aside from
+ * 0x0-0x2 can't be read or written.
+ */
+ cx25840_write(client, 0x000, 0);
+
+ /* Internal Reset */
+ cx25840_and_or(client, 0x102, ~0x01, 0x01);
+ cx25840_and_or(client, 0x102, ~0x01, 0x00);
+
+ /* Stop microcontroller */
+ cx25840_and_or(client, 0x803, ~0x10, 0x00);
+
+ /* DIF in reset? */
+ cx25840_write(client, 0x398, 0);
+
+ /*
+ * Trust the default xtal, no division
+ * '885: 28.636363... MHz
+ * '887: 25.000000 MHz
+ * '888: 50.000000 MHz
+ */
+ cx25840_write(client, 0x2, 0x76);
+
+ /* Power up all the PLL's and DLL */
+ cx25840_write(client, 0x1, 0x40);
+
+ /* Sys PLL */
+ switch (state->id) {
+ case V4L2_IDENT_CX23888_AV:
+ /*
+ * 50.0 MHz * (0xb + 0xe8ba26/0x2000000)/4 = 5 * 28.636363 MHz
+ * 572.73 MHz before post divide
+ */
+ /* HVR1850 or 50MHz xtal */
+ cx25840_write(client, 0x2, 0x71);
+ cx25840_write4(client, 0x11c, 0x01d1744c);
+ cx25840_write4(client, 0x118, 0x00000416);
+ cx25840_write4(client, 0x404, 0x0010253e);
+ cx25840_write4(client, 0x42c, 0x42600000);
+ cx25840_write4(client, 0x44c, 0x161f1000);
+ break;
+ case V4L2_IDENT_CX23887_AV:
+ /*
+ * 25.0 MHz * (0x16 + 0x1d1744c/0x2000000)/4 = 5 * 28.636363 MHz
+ * 572.73 MHz before post divide
+ */
+ cx25840_write4(client, 0x11c, 0x01d1744c);
+ cx25840_write4(client, 0x118, 0x00000416);
+ break;
+ case V4L2_IDENT_CX23885_AV:
+ default:
+ /*
+ * 28.636363 MHz * (0x14 + 0x0/0x2000000)/4 = 5 * 28.636363 MHz
+ * 572.73 MHz before post divide
+ */
+ cx25840_write4(client, 0x11c, 0x00000000);
+ cx25840_write4(client, 0x118, 0x00000414);
+ break;
+ }
+
+ /* Disable DIF bypass */
+ cx25840_write4(client, 0x33c, 0x00000001);
+
+ /* DIF Src phase inc */
+ cx25840_write4(client, 0x340, 0x0df7df83);
+
+ /*
+ * Vid PLL
+ * Setup for a BT.656 pixel clock of 13.5 Mpixels/second
+ *
+ * 28.636363 MHz * (0xf + 0x02be2c9/0x2000000)/4 = 8 * 13.5 MHz
+ * 432.0 MHz before post divide
+ */
+
+ /* HVR1850 */
+ switch (state->id) {
+ case V4L2_IDENT_CX23888_AV:
+ /* 888/HVR1250 specific */
+ cx25840_write4(client, 0x10c, 0x13333333);
+ cx25840_write4(client, 0x108, 0x00000515);
+ break;
+ default:
+ cx25840_write4(client, 0x10c, 0x002be2c9);
+ cx25840_write4(client, 0x108, 0x0000040f);
+ }
+
+ /* Luma */
+ cx25840_write4(client, 0x414, 0x00107d12);
+
+ /* Chroma */
+ cx25840_write4(client, 0x420, 0x3d008282);
+
+ /*
+ * Aux PLL
+ * Initial setup for audio sample clock:
+ * 48 ksps, 16 bits/sample, x160 multiplier = 122.88 MHz
+ * Initial I2S output/master clock(?):
+ * 48 ksps, 16 bits/sample, x16 multiplier = 12.288 MHz
+ */
+ switch (state->id) {
+ case V4L2_IDENT_CX23888_AV:
+ /*
+ * 50.0 MHz * (0x7 + 0x0bedfa4/0x2000000)/3 = 122.88 MHz
+ * 368.64 MHz before post divide
+ * 122.88 MHz / 0xa = 12.288 MHz
+ */
+ /* HVR1850 or 50MHz xtal */
+ cx25840_write4(client, 0x114, 0x017dbf48);
+ cx25840_write4(client, 0x110, 0x000a030e);
+ break;
+ case V4L2_IDENT_CX23887_AV:
+ /*
+ * 25.0 MHz * (0xe + 0x17dbf48/0x2000000)/3 = 122.88 MHz
+ * 368.64 MHz before post divide
+ * 122.88 MHz / 0xa = 12.288 MHz
+ */
+ cx25840_write4(client, 0x114, 0x017dbf48);
+ cx25840_write4(client, 0x110, 0x000a030e);
+ break;
+ case V4L2_IDENT_CX23885_AV:
+ default:
+ /*
+ * 28.636363 MHz * (0xc + 0x1bf0c9e/0x2000000)/3 = 122.88 MHz
+ * 368.64 MHz before post divide
+ * 122.88 MHz / 0xa = 12.288 MHz
+ */
+ cx25840_write4(client, 0x114, 0x01bf0c9e);
+ cx25840_write4(client, 0x110, 0x000a030c);
+ break;
+ };
+
+ /* ADC2 input select */
+ cx25840_write(client, 0x102, 0x10);
+
+ /* VIN1 & VIN5 */
+ cx25840_write(client, 0x103, 0x11);
+
+ /* Enable format auto detect */
+ cx25840_write(client, 0x400, 0);
+ /* Fast subchroma lock */
+ /* White crush, Chroma AGC & Chroma Killer enabled */
+ cx25840_write(client, 0x401, 0xe8);
+
+ /* Select AFE clock pad output source */
+ cx25840_write(client, 0x144, 0x05);
+
+ /* Drive GPIO2 direction and values for HVR1700
+ * where an onboard mux selects the output of demodulator
+ * vs the 417. Failure to set this results in no DTV.
+ * It's safe to set this across all Hauppauge boards
+ * currently, regardless of the board type.
+ */
+ cx25840_write(client, 0x160, 0x1d);
+ cx25840_write(client, 0x164, 0x00);
+
+ /* Do the firmware load in a work handler to prevent.
+ Otherwise the kernel is blocked waiting for the
+ bit-banging i2c interface to finish uploading the
+ firmware. */
+ INIT_WORK(&state->fw_work, cx25840_work_handler);
+ init_waitqueue_head(&state->fw_wait);
+ q = create_singlethread_workqueue("cx25840_fw");
+ prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE);
+ queue_work(q, &state->fw_work);
+ schedule();
+ finish_wait(&state->fw_wait, &wait);
+ destroy_workqueue(q);
+
+ /* Call the cx23885 specific std setup func, we no longer rely on
+ * the generic cx24840 func.
+ */
+ cx23885_std_setup(client);
+
+ /* (re)set input */
+ set_input(client, state->vid_input, state->aud_input);
+
+ /* start microcontroller */
+ cx25840_and_or(client, 0x803, ~0x10, 0x10);
+
+ /* Disable and clear video interrupts - we don't use them */
+ cx25840_write4(client, CX25840_VID_INT_STAT_REG, 0xffffffff);
+
+ /* Disable and clear audio interrupts - we don't use them */
+ cx25840_write(client, CX25840_AUD_INT_CTRL_REG, 0xff);
+ cx25840_write(client, CX25840_AUD_INT_STAT_REG, 0xff);
+
+ /* CC raw enable */
+ /* - VIP 1.1 control codes - 10bit, blue field enable.
+ * - enable raw data during vertical blanking.
+ * - enable ancillary Data insertion for 656 or VIP.
+ */
+ cx25840_write4(client, 0x404, 0x0010253e);
+
+ /* CC on - Undocumented Register */
+ cx25840_write(client, 0x42f, 0x66);
+
+ /* HVR-1250 / HVR1850 DIF related */
+ /* Power everything up */
+ cx25840_write4(client, 0x130, 0x0);
+
+ /* Undocumented */
+ cx25840_write4(client, 0x478, 0x6628021F);
+
+ /* AFE_CLK_OUT_CTRL - Select the clock output source as output */
+ cx25840_write4(client, 0x144, 0x5);
+
+ /* I2C_OUT_CTL - I2S output configuration as
+ * Master, Sony, Left justified, left sample on WS=1
+ */
+ cx25840_write4(client, 0x918, 0x1a0);
+
+ /* AFE_DIAG_CTRL1 */
+ cx25840_write4(client, 0x134, 0x000a1800);
+
+ /* AFE_DIAG_CTRL3 - Inverted Polarity for Audio and Video */
+ cx25840_write4(client, 0x13c, 0x00310000);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void cx231xx_initialize(struct i2c_client *client)
+{
+ DEFINE_WAIT(wait);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+ struct workqueue_struct *q;
+
+ /* Internal Reset */
+ cx25840_and_or(client, 0x102, ~0x01, 0x01);
+ cx25840_and_or(client, 0x102, ~0x01, 0x00);
+
+ /* Stop microcontroller */
+ cx25840_and_or(client, 0x803, ~0x10, 0x00);
+
+ /* DIF in reset? */
+ cx25840_write(client, 0x398, 0);
+
+ /* Trust the default xtal, no division */
+ /* This changes for the cx23888 products */
+ cx25840_write(client, 0x2, 0x76);
+
+ /* Bring down the regulator for AUX clk */
+ cx25840_write(client, 0x1, 0x40);
+
+ /* Disable DIF bypass */
+ cx25840_write4(client, 0x33c, 0x00000001);
+
+ /* DIF Src phase inc */
+ cx25840_write4(client, 0x340, 0x0df7df83);
+
+ /* Luma */
+ cx25840_write4(client, 0x414, 0x00107d12);
+
+ /* Chroma */
+ cx25840_write4(client, 0x420, 0x3d008282);
+
+ /* ADC2 input select */
+ cx25840_write(client, 0x102, 0x10);
+
+ /* VIN1 & VIN5 */
+ cx25840_write(client, 0x103, 0x11);
+
+ /* Enable format auto detect */
+ cx25840_write(client, 0x400, 0);
+ /* Fast subchroma lock */
+ /* White crush, Chroma AGC & Chroma Killer enabled */
+ cx25840_write(client, 0x401, 0xe8);
+
+ /* Do the firmware load in a work handler to prevent.
+ Otherwise the kernel is blocked waiting for the
+ bit-banging i2c interface to finish uploading the
+ firmware. */
+ INIT_WORK(&state->fw_work, cx25840_work_handler);
+ init_waitqueue_head(&state->fw_wait);
+ q = create_singlethread_workqueue("cx25840_fw");
+ prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE);
+ queue_work(q, &state->fw_work);
+ schedule();
+ finish_wait(&state->fw_wait, &wait);
+ destroy_workqueue(q);
+
+ cx25840_std_setup(client);
+
+ /* (re)set input */
+ set_input(client, state->vid_input, state->aud_input);
+
+ /* start microcontroller */
+ cx25840_and_or(client, 0x803, ~0x10, 0x10);
+
+ /* CC raw enable */
+ cx25840_write(client, 0x404, 0x0b);
+
+ /* CC on */
+ cx25840_write(client, 0x42f, 0x66);
+ cx25840_write4(client, 0x474, 0x1e1e601a);
+}
+
+/* ----------------------------------------------------------------------- */
+
+void cx25840_std_setup(struct i2c_client *client)
+{
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+ v4l2_std_id std = state->std;
+ int hblank, hactive, burst, vblank, vactive, sc;
+ int vblank656, src_decimation;
+ int luma_lpf, uv_lpf, comb;
+ u32 pll_int, pll_frac, pll_post;
+
+ /* datasheet startup, step 8d */
+ if (std & ~V4L2_STD_NTSC)
+ cx25840_write(client, 0x49f, 0x11);
+ else
+ cx25840_write(client, 0x49f, 0x14);
+
+ if (std & V4L2_STD_625_50) {
+ hblank = 132;
+ hactive = 720;
+ burst = 93;
+ vblank = 36;
+ vactive = 580;
+ vblank656 = 40;
+ src_decimation = 0x21f;
+ luma_lpf = 2;
+
+ if (std & V4L2_STD_SECAM) {
+ uv_lpf = 0;
+ comb = 0;
+ sc = 0x0a425f;
+ } else if (std == V4L2_STD_PAL_Nc) {
+ uv_lpf = 1;
+ comb = 0x20;
+ sc = 556453;
+ } else {
+ uv_lpf = 1;
+ comb = 0x20;
+ sc = 688739;
+ }
+ } else {
+ hactive = 720;
+ hblank = 122;
+ vactive = 487;
+ luma_lpf = 1;
+ uv_lpf = 1;
+
+ src_decimation = 0x21f;
+ if (std == V4L2_STD_PAL_60) {
+ vblank = 26;
+ vblank656 = 26;
+ burst = 0x5b;
+ luma_lpf = 2;
+ comb = 0x20;
+ sc = 688739;
+ } else if (std == V4L2_STD_PAL_M) {
+ vblank = 20;
+ vblank656 = 24;
+ burst = 0x61;
+ comb = 0x20;
+ sc = 555452;
+ } else {
+ vblank = 26;
+ vblank656 = 26;
+ burst = 0x5b;
+ comb = 0x66;
+ sc = 556063;
+ }
+ }
+
+ /* DEBUG: Displays configured PLL frequency */
+ if (!is_cx231xx(state)) {
+ pll_int = cx25840_read(client, 0x108);
+ pll_frac = cx25840_read4(client, 0x10c) & 0x1ffffff;
+ pll_post = cx25840_read(client, 0x109);
+ v4l_dbg(1, cx25840_debug, client,
+ "PLL regs = int: %u, frac: %u, post: %u\n",
+ pll_int, pll_frac, pll_post);
+
+ if (pll_post) {
+ int fin, fsc;
+ int pll = (28636363L * ((((u64)pll_int) << 25L) + pll_frac)) >> 25L;
+
+ pll /= pll_post;
+ v4l_dbg(1, cx25840_debug, client, "PLL = %d.%06d MHz\n",
+ pll / 1000000, pll % 1000000);
+ v4l_dbg(1, cx25840_debug, client, "PLL/8 = %d.%06d MHz\n",
+ pll / 8000000, (pll / 8) % 1000000);
+
+ fin = ((u64)src_decimation * pll) >> 12;
+ v4l_dbg(1, cx25840_debug, client,
+ "ADC Sampling freq = %d.%06d MHz\n",
+ fin / 1000000, fin % 1000000);
+
+ fsc = (((u64)sc) * pll) >> 24L;
+ v4l_dbg(1, cx25840_debug, client,
+ "Chroma sub-carrier freq = %d.%06d MHz\n",
+ fsc / 1000000, fsc % 1000000);
+
+ v4l_dbg(1, cx25840_debug, client, "hblank %i, hactive %i, "
+ "vblank %i, vactive %i, vblank656 %i, src_dec %i, "
+ "burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x, "
+ "sc 0x%06x\n",
+ hblank, hactive, vblank, vactive, vblank656,
+ src_decimation, burst, luma_lpf, uv_lpf, comb, sc);
+ }
+ }
+
+ /* Sets horizontal blanking delay and active lines */
+ cx25840_write(client, 0x470, hblank);
+ cx25840_write(client, 0x471,
+ 0xff & (((hblank >> 8) & 0x3) | (hactive << 4)));
+ cx25840_write(client, 0x472, hactive >> 4);
+
+ /* Sets burst gate delay */
+ cx25840_write(client, 0x473, burst);
+
+ /* Sets vertical blanking delay and active duration */
+ cx25840_write(client, 0x474, vblank);
+ cx25840_write(client, 0x475,
+ 0xff & (((vblank >> 8) & 0x3) | (vactive << 4)));
+ cx25840_write(client, 0x476, vactive >> 4);
+ cx25840_write(client, 0x477, vblank656);
+
+ /* Sets src decimation rate */
+ cx25840_write(client, 0x478, 0xff & src_decimation);
+ cx25840_write(client, 0x479, 0xff & (src_decimation >> 8));
+
+ /* Sets Luma and UV Low pass filters */
+ cx25840_write(client, 0x47a, luma_lpf << 6 | ((uv_lpf << 4) & 0x30));
+
+ /* Enables comb filters */
+ cx25840_write(client, 0x47b, comb);
+
+ /* Sets SC Step*/
+ cx25840_write(client, 0x47c, sc);
+ cx25840_write(client, 0x47d, 0xff & sc >> 8);
+ cx25840_write(client, 0x47e, 0xff & sc >> 16);
+
+ /* Sets VBI parameters */
+ if (std & V4L2_STD_625_50) {
+ cx25840_write(client, 0x47f, 0x01);
+ state->vbi_line_offset = 5;
+ } else {
+ cx25840_write(client, 0x47f, 0x00);
+ state->vbi_line_offset = 8;
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void input_change(struct i2c_client *client)
+{
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+ v4l2_std_id std = state->std;
+
+ /* Follow step 8c and 8d of section 3.16 in the cx25840 datasheet */
+ if (std & V4L2_STD_SECAM) {
+ cx25840_write(client, 0x402, 0);
+ }
+ else {
+ cx25840_write(client, 0x402, 0x04);
+ cx25840_write(client, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11);
+ }
+ cx25840_and_or(client, 0x401, ~0x60, 0);
+ cx25840_and_or(client, 0x401, ~0x60, 0x60);
+
+ /* Don't write into audio registers on cx2583x chips */
+ if (is_cx2583x(state))
+ return;
+
+ cx25840_and_or(client, 0x810, ~0x01, 1);
+
+ if (state->radio) {
+ cx25840_write(client, 0x808, 0xf9);
+ cx25840_write(client, 0x80b, 0x00);
+ }
+ else if (std & V4L2_STD_525_60) {
+ /* Certain Hauppauge PVR150 models have a hardware bug
+ that causes audio to drop out. For these models the
+ audio standard must be set explicitly.
+ To be precise: it affects cards with tuner models
+ 85, 99 and 112 (model numbers from tveeprom). */
+ int hw_fix = state->pvr150_workaround;
+
+ if (std == V4L2_STD_NTSC_M_JP) {
+ /* Japan uses EIAJ audio standard */
+ cx25840_write(client, 0x808, hw_fix ? 0x2f : 0xf7);
+ } else if (std == V4L2_STD_NTSC_M_KR) {
+ /* South Korea uses A2 audio standard */
+ cx25840_write(client, 0x808, hw_fix ? 0x3f : 0xf8);
+ } else {
+ /* Others use the BTSC audio standard */
+ cx25840_write(client, 0x808, hw_fix ? 0x1f : 0xf6);
+ }
+ cx25840_write(client, 0x80b, 0x00);
+ } else if (std & V4L2_STD_PAL) {
+ /* Autodetect audio standard and audio system */
+ cx25840_write(client, 0x808, 0xff);
+ /* Since system PAL-L is pretty much non-existent and
+ not used by any public broadcast network, force
+ 6.5 MHz carrier to be interpreted as System DK,
+ this avoids DK audio detection instability */
+ cx25840_write(client, 0x80b, 0x00);
+ } else if (std & V4L2_STD_SECAM) {
+ /* Autodetect audio standard and audio system */
+ cx25840_write(client, 0x808, 0xff);
+ /* If only one of SECAM-DK / SECAM-L is required, then force
+ 6.5MHz carrier, else autodetect it */
+ if ((std & V4L2_STD_SECAM_DK) &&
+ !(std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) {
+ /* 6.5 MHz carrier to be interpreted as System DK */
+ cx25840_write(client, 0x80b, 0x00);
+ } else if (!(std & V4L2_STD_SECAM_DK) &&
+ (std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) {
+ /* 6.5 MHz carrier to be interpreted as System L */
+ cx25840_write(client, 0x80b, 0x08);
+ } else {
+ /* 6.5 MHz carrier to be autodetected */
+ cx25840_write(client, 0x80b, 0x10);
+ }
+ }
+
+ cx25840_and_or(client, 0x810, ~0x01, 0);
+}
+
+static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input,
+ enum cx25840_audio_input aud_input)
+{
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+ u8 is_composite = (vid_input >= CX25840_COMPOSITE1 &&
+ vid_input <= CX25840_COMPOSITE8);
+ u8 is_component = (vid_input & CX25840_COMPONENT_ON) ==
+ CX25840_COMPONENT_ON;
+ u8 is_dif = (vid_input & CX25840_DIF_ON) ==
+ CX25840_DIF_ON;
+ u8 is_svideo = (vid_input & CX25840_SVIDEO_ON) ==
+ CX25840_SVIDEO_ON;
+ int luma = vid_input & 0xf0;
+ int chroma = vid_input & 0xf00;
+ u8 reg;
+ u32 val;
+
+ v4l_dbg(1, cx25840_debug, client,
+ "decoder set video input %d, audio input %d\n",
+ vid_input, aud_input);
+
+ if (vid_input >= CX25840_VIN1_CH1) {
+ v4l_dbg(1, cx25840_debug, client, "vid_input 0x%x\n",
+ vid_input);
+ reg = vid_input & 0xff;
+ is_composite = !is_component &&
+ ((vid_input & CX25840_SVIDEO_ON) != CX25840_SVIDEO_ON);
+
+ v4l_dbg(1, cx25840_debug, client, "mux cfg 0x%x comp=%d\n",
+ reg, is_composite);
+ } else if (is_composite) {
+ reg = 0xf0 + (vid_input - CX25840_COMPOSITE1);
+ } else {
+ if ((vid_input & ~0xff0) ||
+ luma < CX25840_SVIDEO_LUMA1 || luma > CX25840_SVIDEO_LUMA8 ||
+ chroma < CX25840_SVIDEO_CHROMA4 || chroma > CX25840_SVIDEO_CHROMA8) {
+ v4l_err(client, "0x%04x is not a valid video input!\n",
+ vid_input);
+ return -EINVAL;
+ }
+ reg = 0xf0 + ((luma - CX25840_SVIDEO_LUMA1) >> 4);
+ if (chroma >= CX25840_SVIDEO_CHROMA7) {
+ reg &= 0x3f;
+ reg |= (chroma - CX25840_SVIDEO_CHROMA7) >> 2;
+ } else {
+ reg &= 0xcf;
+ reg |= (chroma - CX25840_SVIDEO_CHROMA4) >> 4;
+ }
+ }
+
+ /* The caller has previously prepared the correct routing
+ * configuration in reg (for the cx23885) so we have no
+ * need to attempt to flip bits for earlier av decoders.
+ */
+ if (!is_cx2388x(state) && !is_cx231xx(state)) {
+ switch (aud_input) {
+ case CX25840_AUDIO_SERIAL:
+ /* do nothing, use serial audio input */
+ break;
+ case CX25840_AUDIO4: reg &= ~0x30; break;
+ case CX25840_AUDIO5: reg &= ~0x30; reg |= 0x10; break;
+ case CX25840_AUDIO6: reg &= ~0x30; reg |= 0x20; break;
+ case CX25840_AUDIO7: reg &= ~0xc0; break;
+ case CX25840_AUDIO8: reg &= ~0xc0; reg |= 0x40; break;
+
+ default:
+ v4l_err(client, "0x%04x is not a valid audio input!\n",
+ aud_input);
+ return -EINVAL;
+ }
+ }
+
+ cx25840_write(client, 0x103, reg);
+
+ /* Set INPUT_MODE to Composite, S-Video or Component */
+ if (is_component)
+ cx25840_and_or(client, 0x401, ~0x6, 0x6);
+ else
+ cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02);
+
+ if (is_cx2388x(state)) {
+
+ /* Enable or disable the DIF for tuner use */
+ if (is_dif) {
+ cx25840_and_or(client, 0x102, ~0x80, 0x80);
+
+ /* Set of defaults for NTSC and PAL */
+ cx25840_write4(client, 0x31c, 0xc2262600);
+ cx25840_write4(client, 0x320, 0xc2262600);
+
+ /* 18271 IF - Nobody else yet uses a different
+ * tuner with the DIF, so these are reasonable
+ * assumptions (HVR1250 and HVR1850 specific).
+ */
+ cx25840_write4(client, 0x318, 0xda262600);
+ cx25840_write4(client, 0x33c, 0x2a24c800);
+ cx25840_write4(client, 0x104, 0x0704dd00);
+ } else {
+ cx25840_write4(client, 0x300, 0x015c28f5);
+
+ cx25840_and_or(client, 0x102, ~0x80, 0);
+ cx25840_write4(client, 0x340, 0xdf7df83);
+ cx25840_write4(client, 0x104, 0x0704dd80);
+ cx25840_write4(client, 0x314, 0x22400600);
+ cx25840_write4(client, 0x318, 0x40002600);
+ cx25840_write4(client, 0x324, 0x40002600);
+ cx25840_write4(client, 0x32c, 0x0250e620);
+ cx25840_write4(client, 0x39c, 0x01FF0B00);
+
+ cx25840_write4(client, 0x410, 0xffff0dbf);
+ cx25840_write4(client, 0x414, 0x00137d03);
+ cx25840_write4(client, 0x418, 0x01008080);
+ cx25840_write4(client, 0x41c, 0x00000000);
+ cx25840_write4(client, 0x420, 0x001c3e0f);
+ cx25840_write4(client, 0x42c, 0x42600000);
+ cx25840_write4(client, 0x430, 0x0000039b);
+ cx25840_write4(client, 0x438, 0x00000000);
+
+ cx25840_write4(client, 0x440, 0xF8E3E824);
+ cx25840_write4(client, 0x444, 0x401040dc);
+ cx25840_write4(client, 0x448, 0xcd3f02a0);
+ cx25840_write4(client, 0x44c, 0x161f1000);
+ cx25840_write4(client, 0x450, 0x00000802);
+
+ cx25840_write4(client, 0x91c, 0x01000000);
+ cx25840_write4(client, 0x8e0, 0x03063870);
+ cx25840_write4(client, 0x8d4, 0x7FFF0024);
+ cx25840_write4(client, 0x8d0, 0x00063073);
+
+ cx25840_write4(client, 0x8c8, 0x00010000);
+ cx25840_write4(client, 0x8cc, 0x00080023);
+
+ /* DIF BYPASS */
+ cx25840_write4(client, 0x33c, 0x2a04c800);
+ }
+
+ /* Reset the DIF */
+ cx25840_write4(client, 0x398, 0);
+ }
+
+ if (!is_cx2388x(state) && !is_cx231xx(state)) {
+ /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */
+ cx25840_and_or(client, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0);
+ /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2&CH3 */
+ if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30)
+ cx25840_and_or(client, 0x102, ~0x4, 4);
+ else
+ cx25840_and_or(client, 0x102, ~0x4, 0);
+ } else {
+ /* Set DUAL_MODE_ADC2 to 1 if component*/
+ cx25840_and_or(client, 0x102, ~0x4, is_component ? 0x4 : 0x0);
+ if (is_composite) {
+ /* ADC2 input select channel 2 */
+ cx25840_and_or(client, 0x102, ~0x2, 0);
+ } else if (!is_component) {
+ /* S-Video */
+ if (chroma >= CX25840_SVIDEO_CHROMA7) {
+ /* ADC2 input select channel 3 */
+ cx25840_and_or(client, 0x102, ~0x2, 2);
+ } else {
+ /* ADC2 input select channel 2 */
+ cx25840_and_or(client, 0x102, ~0x2, 0);
+ }
+ }
+
+ /* cx23885 / SVIDEO */
+ if (is_cx2388x(state) && is_svideo) {
+#define AFE_CTRL (0x104)
+#define MODE_CTRL (0x400)
+ cx25840_and_or(client, 0x102, ~0x2, 0x2);
+
+ val = cx25840_read4(client, MODE_CTRL);
+ val &= 0xFFFFF9FF;
+
+ /* YC */
+ val |= 0x00000200;
+ val &= ~0x2000;
+ cx25840_write4(client, MODE_CTRL, val);
+
+ val = cx25840_read4(client, AFE_CTRL);
+
+ /* Chroma in select */
+ val |= 0x00001000;
+ val &= 0xfffffe7f;
+ /* Clear VGA_SEL_CH2 and VGA_SEL_CH3 (bits 7 and 8).
+ * This sets them to use video rather than audio.
+ * Only one of the two will be in use.
+ */
+ cx25840_write4(client, AFE_CTRL, val);
+ } else
+ cx25840_and_or(client, 0x102, ~0x2, 0);
+ }
+
+ state->vid_input = vid_input;
+ state->aud_input = aud_input;
+ cx25840_audio_set_path(client);
+ input_change(client);
+
+ if (is_cx2388x(state)) {
+ /* Audio channel 1 src : Parallel 1 */
+ cx25840_write(client, 0x124, 0x03);
+
+ /* Select AFE clock pad output source */
+ cx25840_write(client, 0x144, 0x05);
+
+ /* I2S_IN_CTL: I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 */
+ cx25840_write(client, 0x914, 0xa0);
+
+ /* I2S_OUT_CTL:
+ * I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1
+ * I2S_OUT_MASTER_MODE = Master
+ */
+ cx25840_write(client, 0x918, 0xa0);
+ cx25840_write(client, 0x919, 0x01);
+ } else if (is_cx231xx(state)) {
+ /* Audio channel 1 src : Parallel 1 */
+ cx25840_write(client, 0x124, 0x03);
+
+ /* I2S_IN_CTL: I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 */
+ cx25840_write(client, 0x914, 0xa0);
+
+ /* I2S_OUT_CTL:
+ * I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1
+ * I2S_OUT_MASTER_MODE = Master
+ */
+ cx25840_write(client, 0x918, 0xa0);
+ cx25840_write(client, 0x919, 0x01);
+ }
+
+ if (is_cx2388x(state) && ((aud_input == CX25840_AUDIO7) ||
+ (aud_input == CX25840_AUDIO6))) {
+ /* Configure audio from LR1 or LR2 input */
+ cx25840_write4(client, 0x910, 0);
+ cx25840_write4(client, 0x8d0, 0x63073);
+ } else
+ if (is_cx2388x(state) && (aud_input == CX25840_AUDIO8)) {
+ /* Configure audio from tuner/sif input */
+ cx25840_write4(client, 0x910, 0x12b000c9);
+ cx25840_write4(client, 0x8d0, 0x1f063870);
+ }
+
+ if (is_cx2388x(state)) {
+ /* HVR1850 */
+ /* AUD_IO_CTRL - I2S Input, Parallel1*/
+ /* - Channel 1 src - Parallel1 (Merlin out) */
+ /* - Channel 2 src - Parallel2 (Merlin out) */
+ /* - Channel 3 src - Parallel3 (Merlin AC97 out) */
+ /* - I2S source and dir - Merlin, output */
+ cx25840_write4(client, 0x124, 0x100);
+
+ if (!is_dif) {
+ /* Stop microcontroller if we don't need it
+ * to avoid audio popping on svideo/composite use.
+ */
+ cx25840_and_or(client, 0x803, ~0x10, 0x00);
+ }
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int set_v4lstd(struct i2c_client *client)
+{
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+ u8 fmt = 0; /* zero is autodetect */
+ u8 pal_m = 0;
+
+ /* First tests should be against specific std */
+ if (state->std == V4L2_STD_NTSC_M_JP) {
+ fmt = 0x2;
+ } else if (state->std == V4L2_STD_NTSC_443) {
+ fmt = 0x3;
+ } else if (state->std == V4L2_STD_PAL_M) {
+ pal_m = 1;
+ fmt = 0x5;
+ } else if (state->std == V4L2_STD_PAL_N) {
+ fmt = 0x6;
+ } else if (state->std == V4L2_STD_PAL_Nc) {
+ fmt = 0x7;
+ } else if (state->std == V4L2_STD_PAL_60) {
+ fmt = 0x8;
+ } else {
+ /* Then, test against generic ones */
+ if (state->std & V4L2_STD_NTSC)
+ fmt = 0x1;
+ else if (state->std & V4L2_STD_PAL)
+ fmt = 0x4;
+ else if (state->std & V4L2_STD_SECAM)
+ fmt = 0xc;
+ }
+
+ v4l_dbg(1, cx25840_debug, client, "changing video std to fmt %i\n",fmt);
+
+ /* Follow step 9 of section 3.16 in the cx25840 datasheet.
+ Without this PAL may display a vertical ghosting effect.
+ This happens for example with the Yuan MPC622. */
+ if (fmt >= 4 && fmt < 8) {
+ /* Set format to NTSC-M */
+ cx25840_and_or(client, 0x400, ~0xf, 1);
+ /* Turn off LCOMB */
+ cx25840_and_or(client, 0x47b, ~6, 0);
+ }
+ cx25840_and_or(client, 0x400, ~0xf, fmt);
+ cx25840_and_or(client, 0x403, ~0x3, pal_m);
+ if (is_cx2388x(state))
+ cx23885_std_setup(client);
+ else
+ cx25840_std_setup(client);
+ if (!is_cx2583x(state))
+ input_change(client);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int cx25840_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ cx25840_write(client, 0x414, ctrl->val - 128);
+ break;
+
+ case V4L2_CID_CONTRAST:
+ cx25840_write(client, 0x415, ctrl->val << 1);
+ break;
+
+ case V4L2_CID_SATURATION:
+ cx25840_write(client, 0x420, ctrl->val << 1);
+ cx25840_write(client, 0x421, ctrl->val << 1);
+ break;
+
+ case V4L2_CID_HUE:
+ cx25840_write(client, 0x422, ctrl->val);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int cx25840_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int HSC, VSC, Vsrc, Hsrc, filter, Vlines;
+ int is_50Hz = !(state->std & V4L2_STD_525_60);
+
+ if (fmt->code != V4L2_MBUS_FMT_FIXED)
+ return -EINVAL;
+
+ fmt->field = V4L2_FIELD_INTERLACED;
+ fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+ Vsrc = (cx25840_read(client, 0x476) & 0x3f) << 4;
+ Vsrc |= (cx25840_read(client, 0x475) & 0xf0) >> 4;
+
+ Hsrc = (cx25840_read(client, 0x472) & 0x3f) << 4;
+ Hsrc |= (cx25840_read(client, 0x471) & 0xf0) >> 4;
+
+ Vlines = fmt->height + (is_50Hz ? 4 : 7);
+
+ if ((fmt->width * 16 < Hsrc) || (Hsrc < fmt->width) ||
+ (Vlines * 8 < Vsrc) || (Vsrc < Vlines)) {
+ v4l_err(client, "%dx%d is not a valid size!\n",
+ fmt->width, fmt->height);
+ return -ERANGE;
+ }
+
+ HSC = (Hsrc * (1 << 20)) / fmt->width - (1 << 20);
+ VSC = (1 << 16) - (Vsrc * (1 << 9) / Vlines - (1 << 9));
+ VSC &= 0x1fff;
+
+ if (fmt->width >= 385)
+ filter = 0;
+ else if (fmt->width > 192)
+ filter = 1;
+ else if (fmt->width > 96)
+ filter = 2;
+ else
+ filter = 3;
+
+ v4l_dbg(1, cx25840_debug, client, "decoder set size %dx%d -> scale %ux%u\n",
+ fmt->width, fmt->height, HSC, VSC);
+
+ /* HSCALE=HSC */
+ cx25840_write(client, 0x418, HSC & 0xff);
+ cx25840_write(client, 0x419, (HSC >> 8) & 0xff);
+ cx25840_write(client, 0x41a, HSC >> 16);
+ /* VSCALE=VSC */
+ cx25840_write(client, 0x41c, VSC & 0xff);
+ cx25840_write(client, 0x41d, VSC >> 8);
+ /* VS_INTRLACE=1 VFILT=filter */
+ cx25840_write(client, 0x41e, 0x8 | filter);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void log_video_status(struct i2c_client *client)
+{
+ static const char *const fmt_strs[] = {
+ "0x0",
+ "NTSC-M", "NTSC-J", "NTSC-4.43",
+ "PAL-BDGHI", "PAL-M", "PAL-N", "PAL-Nc", "PAL-60",
+ "0x9", "0xA", "0xB",
+ "SECAM",
+ "0xD", "0xE", "0xF"
+ };
+
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+ u8 vidfmt_sel = cx25840_read(client, 0x400) & 0xf;
+ u8 gen_stat1 = cx25840_read(client, 0x40d);
+ u8 gen_stat2 = cx25840_read(client, 0x40e);
+ int vid_input = state->vid_input;
+
+ v4l_info(client, "Video signal: %spresent\n",
+ (gen_stat2 & 0x20) ? "" : "not ");
+ v4l_info(client, "Detected format: %s\n",
+ fmt_strs[gen_stat1 & 0xf]);
+
+ v4l_info(client, "Specified standard: %s\n",
+ vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection");
+
+ if (vid_input >= CX25840_COMPOSITE1 &&
+ vid_input <= CX25840_COMPOSITE8) {
+ v4l_info(client, "Specified video input: Composite %d\n",
+ vid_input - CX25840_COMPOSITE1 + 1);
+ } else {
+ v4l_info(client, "Specified video input: S-Video (Luma In%d, Chroma In%d)\n",
+ (vid_input & 0xf0) >> 4, (vid_input & 0xf00) >> 8);
+ }
+
+ v4l_info(client, "Specified audioclock freq: %d Hz\n", state->audclk_freq);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void log_audio_status(struct i2c_client *client)
+{
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+ u8 download_ctl = cx25840_read(client, 0x803);
+ u8 mod_det_stat0 = cx25840_read(client, 0x804);
+ u8 mod_det_stat1 = cx25840_read(client, 0x805);
+ u8 audio_config = cx25840_read(client, 0x808);
+ u8 pref_mode = cx25840_read(client, 0x809);
+ u8 afc0 = cx25840_read(client, 0x80b);
+ u8 mute_ctl = cx25840_read(client, 0x8d3);
+ int aud_input = state->aud_input;
+ char *p;
+
+ switch (mod_det_stat0) {
+ case 0x00: p = "mono"; break;
+ case 0x01: p = "stereo"; break;
+ case 0x02: p = "dual"; break;
+ case 0x04: p = "tri"; break;
+ case 0x10: p = "mono with SAP"; break;
+ case 0x11: p = "stereo with SAP"; break;
+ case 0x12: p = "dual with SAP"; break;
+ case 0x14: p = "tri with SAP"; break;
+ case 0xfe: p = "forced mode"; break;
+ default: p = "not defined";
+ }
+ v4l_info(client, "Detected audio mode: %s\n", p);
+
+ switch (mod_det_stat1) {
+ case 0x00: p = "not defined"; break;
+ case 0x01: p = "EIAJ"; break;
+ case 0x02: p = "A2-M"; break;
+ case 0x03: p = "A2-BG"; break;
+ case 0x04: p = "A2-DK1"; break;
+ case 0x05: p = "A2-DK2"; break;
+ case 0x06: p = "A2-DK3"; break;
+ case 0x07: p = "A1 (6.0 MHz FM Mono)"; break;
+ case 0x08: p = "AM-L"; break;
+ case 0x09: p = "NICAM-BG"; break;
+ case 0x0a: p = "NICAM-DK"; break;
+ case 0x0b: p = "NICAM-I"; break;
+ case 0x0c: p = "NICAM-L"; break;
+ case 0x0d: p = "BTSC/EIAJ/A2-M Mono (4.5 MHz FMMono)"; break;
+ case 0x0e: p = "IF FM Radio"; break;
+ case 0x0f: p = "BTSC"; break;
+ case 0x10: p = "high-deviation FM"; break;
+ case 0x11: p = "very high-deviation FM"; break;
+ case 0xfd: p = "unknown audio standard"; break;
+ case 0xfe: p = "forced audio standard"; break;
+ case 0xff: p = "no detected audio standard"; break;
+ default: p = "not defined";
+ }
+ v4l_info(client, "Detected audio standard: %s\n", p);
+ v4l_info(client, "Audio microcontroller: %s\n",
+ (download_ctl & 0x10) ?
+ ((mute_ctl & 0x2) ? "detecting" : "running") : "stopped");
+
+ switch (audio_config >> 4) {
+ case 0x00: p = "undefined"; break;
+ case 0x01: p = "BTSC"; break;
+ case 0x02: p = "EIAJ"; break;
+ case 0x03: p = "A2-M"; break;
+ case 0x04: p = "A2-BG"; break;
+ case 0x05: p = "A2-DK1"; break;
+ case 0x06: p = "A2-DK2"; break;
+ case 0x07: p = "A2-DK3"; break;
+ case 0x08: p = "A1 (6.0 MHz FM Mono)"; break;
+ case 0x09: p = "AM-L"; break;
+ case 0x0a: p = "NICAM-BG"; break;
+ case 0x0b: p = "NICAM-DK"; break;
+ case 0x0c: p = "NICAM-I"; break;
+ case 0x0d: p = "NICAM-L"; break;
+ case 0x0e: p = "FM radio"; break;
+ case 0x0f: p = "automatic detection"; break;
+ default: p = "undefined";
+ }
+ v4l_info(client, "Configured audio standard: %s\n", p);
+
+ if ((audio_config >> 4) < 0xF) {
+ switch (audio_config & 0xF) {
+ case 0x00: p = "MONO1 (LANGUAGE A/Mono L+R channel for BTSC, EIAJ, A2)"; break;
+ case 0x01: p = "MONO2 (LANGUAGE B)"; break;
+ case 0x02: p = "MONO3 (STEREO forced MONO)"; break;
+ case 0x03: p = "MONO4 (NICAM ANALOG-Language C/Analog Fallback)"; break;
+ case 0x04: p = "STEREO"; break;
+ case 0x05: p = "DUAL1 (AB)"; break;
+ case 0x06: p = "DUAL2 (AC) (FM)"; break;
+ case 0x07: p = "DUAL3 (BC) (FM)"; break;
+ case 0x08: p = "DUAL4 (AC) (AM)"; break;
+ case 0x09: p = "DUAL5 (BC) (AM)"; break;
+ case 0x0a: p = "SAP"; break;
+ default: p = "undefined";
+ }
+ v4l_info(client, "Configured audio mode: %s\n", p);
+ } else {
+ switch (audio_config & 0xF) {
+ case 0x00: p = "BG"; break;
+ case 0x01: p = "DK1"; break;
+ case 0x02: p = "DK2"; break;
+ case 0x03: p = "DK3"; break;
+ case 0x04: p = "I"; break;
+ case 0x05: p = "L"; break;
+ case 0x06: p = "BTSC"; break;
+ case 0x07: p = "EIAJ"; break;
+ case 0x08: p = "A2-M"; break;
+ case 0x09: p = "FM Radio"; break;
+ case 0x0f: p = "automatic standard and mode detection"; break;
+ default: p = "undefined";
+ }
+ v4l_info(client, "Configured audio system: %s\n", p);
+ }
+
+ if (aud_input) {
+ v4l_info(client, "Specified audio input: Tuner (In%d)\n", aud_input);
+ } else {
+ v4l_info(client, "Specified audio input: External\n");
+ }
+
+ switch (pref_mode & 0xf) {
+ case 0: p = "mono/language A"; break;
+ case 1: p = "language B"; break;
+ case 2: p = "language C"; break;
+ case 3: p = "analog fallback"; break;
+ case 4: p = "stereo"; break;
+ case 5: p = "language AC"; break;
+ case 6: p = "language BC"; break;
+ case 7: p = "language AB"; break;
+ default: p = "undefined";
+ }
+ v4l_info(client, "Preferred audio mode: %s\n", p);
+
+ if ((audio_config & 0xf) == 0xf) {
+ switch ((afc0 >> 3) & 0x3) {
+ case 0: p = "system DK"; break;
+ case 1: p = "system L"; break;
+ case 2: p = "autodetect"; break;
+ default: p = "undefined";
+ }
+ v4l_info(client, "Selected 65 MHz format: %s\n", p);
+
+ switch (afc0 & 0x7) {
+ case 0: p = "chroma"; break;
+ case 1: p = "BTSC"; break;
+ case 2: p = "EIAJ"; break;
+ case 3: p = "A2-M"; break;
+ case 4: p = "autodetect"; break;
+ default: p = "undefined";
+ }
+ v4l_info(client, "Selected 45 MHz format: %s\n", p);
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
+/* This load_fw operation must be called to load the driver's firmware.
+ Without this the audio standard detection will fail and you will
+ only get mono.
+
+ Since loading the firmware is often problematic when the driver is
+ compiled into the kernel I recommend postponing calling this function
+ until the first open of the video device. Another reason for
+ postponing it is that loading this firmware takes a long time (seconds)
+ due to the slow i2c bus speed. So it will speed up the boot process if
+ you can avoid loading the fw as long as the video device isn't used. */
+static int cx25840_load_fw(struct v4l2_subdev *sd)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!state->is_initialized) {
+ /* initialize and load firmware */
+ state->is_initialized = 1;
+ if (is_cx2583x(state))
+ cx25836_initialize(client);
+ else if (is_cx2388x(state))
+ cx23885_initialize(client);
+ else if (is_cx231xx(state))
+ cx231xx_initialize(client);
+ else
+ cx25840_initialize(client);
+ }
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int cx25840_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->size = 1;
+ reg->val = cx25840_read(client, reg->reg & 0x0fff);
+ return 0;
+}
+
+static int cx25840_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ cx25840_write(client, reg->reg & 0x0fff, reg->val & 0xff);
+ return 0;
+}
+#endif
+
+static int cx25840_s_audio_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 v;
+
+ if (is_cx2583x(state) || is_cx2388x(state) || is_cx231xx(state))
+ return 0;
+
+ v4l_dbg(1, cx25840_debug, client, "%s audio output\n",
+ enable ? "enable" : "disable");
+
+ if (enable) {
+ v = cx25840_read(client, 0x115) | 0x80;
+ cx25840_write(client, 0x115, v);
+ v = cx25840_read(client, 0x116) | 0x03;
+ cx25840_write(client, 0x116, v);
+ } else {
+ v = cx25840_read(client, 0x115) & ~(0x80);
+ cx25840_write(client, 0x115, v);
+ v = cx25840_read(client, 0x116) & ~(0x03);
+ cx25840_write(client, 0x116, v);
+ }
+ return 0;
+}
+
+static int cx25840_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 v;
+
+ v4l_dbg(1, cx25840_debug, client, "%s video output\n",
+ enable ? "enable" : "disable");
+ if (enable) {
+ if (is_cx2388x(state) || is_cx231xx(state)) {
+ v = cx25840_read(client, 0x421) | 0x0b;
+ cx25840_write(client, 0x421, v);
+ } else {
+ v = cx25840_read(client, 0x115) | 0x0c;
+ cx25840_write(client, 0x115, v);
+ v = cx25840_read(client, 0x116) | 0x04;
+ cx25840_write(client, 0x116, v);
+ }
+ } else {
+ if (is_cx2388x(state) || is_cx231xx(state)) {
+ v = cx25840_read(client, 0x421) & ~(0x0b);
+ cx25840_write(client, 0x421, v);
+ } else {
+ v = cx25840_read(client, 0x115) & ~(0x0c);
+ cx25840_write(client, 0x115, v);
+ v = cx25840_read(client, 0x116) & ~(0x04);
+ cx25840_write(client, 0x116, v);
+ }
+ }
+ return 0;
+}
+
+/* Query the current detected video format */
+static int cx25840_g_std(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ v4l2_std_id stds[] = {
+ /* 0000 */ V4L2_STD_UNKNOWN,
+
+ /* 0001 */ V4L2_STD_NTSC_M,
+ /* 0010 */ V4L2_STD_NTSC_M_JP,
+ /* 0011 */ V4L2_STD_NTSC_443,
+ /* 0100 */ V4L2_STD_PAL,
+ /* 0101 */ V4L2_STD_PAL_M,
+ /* 0110 */ V4L2_STD_PAL_N,
+ /* 0111 */ V4L2_STD_PAL_Nc,
+ /* 1000 */ V4L2_STD_PAL_60,
+
+ /* 1001 */ V4L2_STD_UNKNOWN,
+ /* 1010 */ V4L2_STD_UNKNOWN,
+ /* 1001 */ V4L2_STD_UNKNOWN,
+ /* 1010 */ V4L2_STD_UNKNOWN,
+ /* 1011 */ V4L2_STD_UNKNOWN,
+ /* 1110 */ V4L2_STD_UNKNOWN,
+ /* 1111 */ V4L2_STD_UNKNOWN
+ };
+
+ u32 fmt = (cx25840_read4(client, 0x40c) >> 8) & 0xf;
+ *std = stds[ fmt ];
+
+ v4l_dbg(1, cx25840_debug, client, "g_std fmt = %x, v4l2_std_id = 0x%x\n",
+ fmt, (unsigned int)stds[ fmt ]);
+
+ return 0;
+}
+
+static int cx25840_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ /* A limited function that checks for signal status and returns
+ * the state.
+ */
+
+ /* Check for status of Horizontal lock (SRC lock isn't reliable) */
+ if ((cx25840_read4(client, 0x40c) & 0x00010000) == 0)
+ *status |= V4L2_IN_ST_NO_SIGNAL;
+
+ return 0;
+}
+
+static int cx25840_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (state->radio == 0 && state->std == std)
+ return 0;
+ state->radio = 0;
+ state->std = std;
+ return set_v4lstd(client);
+}
+
+static int cx25840_s_radio(struct v4l2_subdev *sd)
+{
+ struct cx25840_state *state = to_state(sd);
+
+ state->radio = 1;
+ return 0;
+}
+
+static int cx25840_s_video_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (is_cx2388x(state))
+ cx23885_std_setup(client);
+
+ return set_input(client, input, state->aud_input);
+}
+
+static int cx25840_s_audio_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (is_cx2388x(state))
+ cx23885_std_setup(client);
+ return set_input(client, state->vid_input, input);
+}
+
+static int cx25840_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ input_change(client);
+ return 0;
+}
+
+static int cx25840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 vpres = cx25840_read(client, 0x40e) & 0x20;
+ u8 mode;
+ int val = 0;
+
+ if (state->radio)
+ return 0;
+
+ vt->signal = vpres ? 0xffff : 0x0;
+ if (is_cx2583x(state))
+ return 0;
+
+ vt->capability |=
+ V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
+ V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
+
+ mode = cx25840_read(client, 0x804);
+
+ /* get rxsubchans and audmode */
+ if ((mode & 0xf) == 1)
+ val |= V4L2_TUNER_SUB_STEREO;
+ else
+ val |= V4L2_TUNER_SUB_MONO;
+
+ if (mode == 2 || mode == 4)
+ val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+
+ if (mode & 0x10)
+ val |= V4L2_TUNER_SUB_SAP;
+
+ vt->rxsubchans = val;
+ vt->audmode = state->audmode;
+ return 0;
+}
+
+static int cx25840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (state->radio || is_cx2583x(state))
+ return 0;
+
+ switch (vt->audmode) {
+ case V4L2_TUNER_MODE_MONO:
+ /* mono -> mono
+ stereo -> mono
+ bilingual -> lang1 */
+ cx25840_and_or(client, 0x809, ~0xf, 0x00);
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ case V4L2_TUNER_MODE_LANG1:
+ /* mono -> mono
+ stereo -> stereo
+ bilingual -> lang1 */
+ cx25840_and_or(client, 0x809, ~0xf, 0x04);
+ break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ /* mono -> mono
+ stereo -> stereo
+ bilingual -> lang1/lang2 */
+ cx25840_and_or(client, 0x809, ~0xf, 0x07);
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ /* mono -> mono
+ stereo -> stereo
+ bilingual -> lang2 */
+ cx25840_and_or(client, 0x809, ~0xf, 0x01);
+ break;
+ default:
+ return -EINVAL;
+ }
+ state->audmode = vt->audmode;
+ return 0;
+}
+
+static int cx25840_reset(struct v4l2_subdev *sd, u32 val)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (is_cx2583x(state))
+ cx25836_initialize(client);
+ else if (is_cx2388x(state))
+ cx23885_initialize(client);
+ else if (is_cx231xx(state))
+ cx231xx_initialize(client);
+ else
+ cx25840_initialize(client);
+ return 0;
+}
+
+static int cx25840_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, state->id, state->rev);
+}
+
+static int cx25840_log_status(struct v4l2_subdev *sd)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ log_video_status(client);
+ if (!is_cx2583x(state))
+ log_audio_status(client);
+ cx25840_ir_log_status(sd);
+ v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
+ return 0;
+}
+
+static int cx23885_irq_handler(struct v4l2_subdev *sd, u32 status,
+ bool *handled)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
+ u8 irq_stat, aud_stat, aud_en, ir_stat, ir_en;
+ u32 vid_stat, aud_mc_stat;
+ bool block_handled;
+ int ret = 0;
+
+ irq_stat = cx25840_read(c, CX23885_PIN_CTRL_IRQ_REG);
+ v4l_dbg(2, cx25840_debug, c, "AV Core IRQ status (entry): %s %s %s\n",
+ irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT ? "ir" : " ",
+ irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT ? "aud" : " ",
+ irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT ? "vid" : " ");
+
+ if ((is_cx23885(state) || is_cx23887(state))) {
+ ir_stat = cx25840_read(c, CX25840_IR_STATS_REG);
+ ir_en = cx25840_read(c, CX25840_IR_IRQEN_REG);
+ v4l_dbg(2, cx25840_debug, c,
+ "AV Core ir IRQ status: %#04x disables: %#04x\n",
+ ir_stat, ir_en);
+ if (irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT) {
+ block_handled = false;
+ ret = cx25840_ir_irq_handler(sd,
+ status, &block_handled);
+ if (block_handled)
+ *handled = true;
+ }
+ }
+
+ aud_stat = cx25840_read(c, CX25840_AUD_INT_STAT_REG);
+ aud_en = cx25840_read(c, CX25840_AUD_INT_CTRL_REG);
+ v4l_dbg(2, cx25840_debug, c,
+ "AV Core audio IRQ status: %#04x disables: %#04x\n",
+ aud_stat, aud_en);
+ aud_mc_stat = cx25840_read4(c, CX23885_AUD_MC_INT_MASK_REG);
+ v4l_dbg(2, cx25840_debug, c,
+ "AV Core audio MC IRQ status: %#06x enables: %#06x\n",
+ aud_mc_stat >> CX23885_AUD_MC_INT_STAT_SHFT,
+ aud_mc_stat & CX23885_AUD_MC_INT_CTRL_BITS);
+ if (irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT) {
+ if (aud_stat) {
+ cx25840_write(c, CX25840_AUD_INT_STAT_REG, aud_stat);
+ *handled = true;
+ }
+ }
+
+ vid_stat = cx25840_read4(c, CX25840_VID_INT_STAT_REG);
+ v4l_dbg(2, cx25840_debug, c,
+ "AV Core video IRQ status: %#06x disables: %#06x\n",
+ vid_stat & CX25840_VID_INT_STAT_BITS,
+ vid_stat >> CX25840_VID_INT_MASK_SHFT);
+ if (irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT) {
+ if (vid_stat & CX25840_VID_INT_STAT_BITS) {
+ cx25840_write4(c, CX25840_VID_INT_STAT_REG, vid_stat);
+ *handled = true;
+ }
+ }
+
+ irq_stat = cx25840_read(c, CX23885_PIN_CTRL_IRQ_REG);
+ v4l_dbg(2, cx25840_debug, c, "AV Core IRQ status (exit): %s %s %s\n",
+ irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT ? "ir" : " ",
+ irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT ? "aud" : " ",
+ irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT ? "vid" : " ");
+
+ return ret;
+}
+
+static int cx25840_irq_handler(struct v4l2_subdev *sd, u32 status,
+ bool *handled)
+{
+ struct cx25840_state *state = to_state(sd);
+
+ *handled = false;
+
+ /* Only support the CX2388[578] AV Core for now */
+ if (is_cx2388x(state))
+ return cx23885_irq_handler(sd, status, handled);
+
+ return -ENODEV;
+}
+
+/* ----------------------------------------------------------------------- */
+
+#define DIF_PLL_FREQ_WORD (0x300)
+#define DIF_BPF_COEFF01 (0x348)
+#define DIF_BPF_COEFF23 (0x34c)
+#define DIF_BPF_COEFF45 (0x350)
+#define DIF_BPF_COEFF67 (0x354)
+#define DIF_BPF_COEFF89 (0x358)
+#define DIF_BPF_COEFF1011 (0x35c)
+#define DIF_BPF_COEFF1213 (0x360)
+#define DIF_BPF_COEFF1415 (0x364)
+#define DIF_BPF_COEFF1617 (0x368)
+#define DIF_BPF_COEFF1819 (0x36c)
+#define DIF_BPF_COEFF2021 (0x370)
+#define DIF_BPF_COEFF2223 (0x374)
+#define DIF_BPF_COEFF2425 (0x378)
+#define DIF_BPF_COEFF2627 (0x37c)
+#define DIF_BPF_COEFF2829 (0x380)
+#define DIF_BPF_COEFF3031 (0x384)
+#define DIF_BPF_COEFF3233 (0x388)
+#define DIF_BPF_COEFF3435 (0x38c)
+#define DIF_BPF_COEFF36 (0x390)
+
+void cx23885_dif_setup(struct i2c_client *client, u32 ifHz)
+{
+ u64 pll_freq;
+ u32 pll_freq_word;
+
+ v4l_dbg(1, cx25840_debug, client, "%s(%d)\n", __func__, ifHz);
+
+ /* Assuming TV */
+ /* Calculate the PLL frequency word based on the adjusted ifHz */
+ pll_freq = div_u64((u64)ifHz * 268435456, 50000000);
+ pll_freq_word = (u32)pll_freq;
+
+ cx25840_write4(client, DIF_PLL_FREQ_WORD, pll_freq_word);
+
+ /* Round down to the nearest 100KHz */
+ ifHz = (ifHz / 100000) * 100000;
+
+ if (ifHz < 3000000)
+ ifHz = 3000000;
+
+ if (ifHz > 16000000)
+ ifHz = 16000000;
+
+ v4l_dbg(1, cx25840_debug, client, "%s(%d) again\n", __func__, ifHz);
+
+ switch (ifHz) {
+ case 3000000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00080012);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x001e0024);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x001bfff8);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xffb4ff50);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed8fe68);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe24fe34);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfebaffc7);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d031f);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x04f0065d);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x07010688);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x04c901d6);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe00f9d3);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600f342);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf235f337);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf64efb22);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0105070f);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x0c460fce);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 3100000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00070012);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x00220032);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x00370026);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xfff0ff91);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff0efe7c);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe01fdcc);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe0afedb);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x00440224);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0434060c);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0738074e);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x06090361);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xff99fb39);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fef3b6);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf21af2a5);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf573fa33);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0034067d);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x0bfb0fb9);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 3200000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000000);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0004000e);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x00200038);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x004c004f);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x002fffdf);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff5cfeb6);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe0dfd92);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd7ffe03);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36010a);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x03410575);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x072607d2);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x071804d5);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0134fcb7);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81ff451);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf223f22e);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf4a7f94b);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xff6405e8);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x0bae0fa4);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 3300000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00000008);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x001a0036);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x0056006d);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x00670030);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xffbdff10);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe46fd8d);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd25fd4f);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35ffe0);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0224049f);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x06c9080e);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x07ef0627);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x02c9fe45);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf961f513);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf250f1d2);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf3ecf869);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfe930552);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x0b5f0f8f);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 3400000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffd0001);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x000f002c);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x0054007d);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x0093007c);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0024ff82);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfea6fdbb);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd03fcca);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51feb9);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x00eb0392);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x06270802);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x08880750);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x044dffdb);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabdf5f8);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2a0f193);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf342f78f);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfdc404b9);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x0b0e0f78);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 3500000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffafff9);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x0002001b);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x0046007d);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x00ad00ba);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00870000);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xff26fe1a);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd1bfc7e);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fda4);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xffa5025c);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x054507ad);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x08dd0847);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x05b80172);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2ef6ff);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf313f170);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf2abf6bd);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfcf6041f);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x0abc0f61);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 3600000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff3);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xfff50006);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x002f006c);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x00b200e3);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00dc007e);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xffb9fea0);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd6bfc71);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fcb1);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe65010b);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x042d0713);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x08ec0906);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x07020302);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaff823);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3a7f16a);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf228f5f5);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfc2a0384);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x0a670f4a);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 3700000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff7ffef);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe9fff1);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x0010004d);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x00a100f2);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x011a00f0);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x0053ff44);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdedfca2);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fbef);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd39ffae);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x02ea0638);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x08b50987);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x08230483);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xff39f960);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf45bf180);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf1b8f537);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfb6102e7);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x0a110f32);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 3800000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9ffee);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe1ffdd);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xfff00024);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x007c00e5);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x013a014a);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x00e6fff8);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe98fd0f);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fb67);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc32fe54);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x01880525);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x083909c7);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x091505ee);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c7fab3);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf52df1b4);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf15df484);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfa9b0249);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x09ba0f19);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 3900000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000000);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffbfff0);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffcf);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffd1fff6);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x004800be);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x01390184);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x016300ac);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xff5efdb1);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fb23);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb5cfd0d);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x001703e4);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x077b09c4);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x09d2073c);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0251fc18);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf61cf203);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf118f3dc);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf9d801aa);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x09600eff);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 4000000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffefff4);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe1ffc8);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffbaffca);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x000b0082);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x01170198);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01c10152);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0030fe7b);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fb24);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfac3fbe9);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfea5027f);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x0683097f);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a560867);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d2fd89);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf723f26f);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0e8f341);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf919010a);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x09060ee5);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 4100000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0002fffb);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe8ffca);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffacffa4);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xffcd0036);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00d70184);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01f601dc);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x00ffff60);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fb6d);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa6efaf5);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfd410103);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x055708f9);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a9e0969);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0543ff02);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf842f2f5);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0cef2b2);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf85e006b);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x08aa0ecb);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 4200000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010003);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00050003);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xfff3ffd3);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffaaff8b);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff95ffe5);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0080014a);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01fe023f);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x01ba0050);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fbf8);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa62fa3b);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfbf9ff7e);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x04010836);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0aa90a3d);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x069f007f);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf975f395);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0cbf231);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf7a9ffcb);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x084c0eaf);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 4300000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010003);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0008000a);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x0000ffe4);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffb4ff81);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff6aff96);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x001c00f0);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01d70271);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0254013b);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fcbd);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa9ff9c5);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfadbfdfe);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x028c073b);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a750adf);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e101fa);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfab8f44e);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0ddf1be);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf6f9ff2b);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x07ed0e94);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 4400000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0009000f);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x000efff8);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9ff87);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff52ff54);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xffb5007e);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01860270);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02c00210);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fdb2);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb22f997);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9f2fc90);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x0102060f);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a050b4c);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0902036e);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfc0af51e);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf106f15a);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf64efe8b);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x078d0e77);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 4500000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00080012);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x0019000e);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5ff9e);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff4fff25);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff560000);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x0112023b);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f702c0);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfec8);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfbe5f9b3);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf947fb41);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xff7004b9);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x095a0b81);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a0004d8);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfd65f603);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf144f104);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf5aafdec);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x072b0e5a);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 4600000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00060012);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x00200022);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x0005ffc1);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff61ff10);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff09ff82);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x008601d7);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f50340);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fff0);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfcddfa19);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8e2fa1e);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfde30343);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x08790b7f);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad50631);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfec7f6fc);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf198f0bd);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf50dfd4e);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x06c90e3d);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 4700000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0003000f);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x00220030);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x0025ffed);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff87ff15);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed6ff10);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xffed014c);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02b90386);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x03110119);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfdfefac4);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8c6f92f);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfc6701b7);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x07670b44);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7e0776);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x002df807);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf200f086);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf477fcb1);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x06650e1e);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 4800000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xffff0009);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x001e0038);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x003f001b);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xffbcff36);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec2feb6);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xff5600a5);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0248038d);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b00232);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xff39fbab);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8f4f87f);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb060020);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x062a0ad2);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf908a3);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0192f922);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf27df05e);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf3e8fc14);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x06000e00);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 4900000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffc0002);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x00160037);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x00510046);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xfff9ff6d);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed0fe7c);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfecefff0);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x01aa0356);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413032b);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x007ffcc5);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf96cf812);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf9cefe87);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x04c90a2c);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c4309b4);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x02f3fa4a);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf30ef046);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf361fb7a);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x059b0de0);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 5000000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fffa);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x000a002d);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x00570067);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x0037ffb5);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfefffe68);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe62ff3d);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x00ec02e3);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x043503f6);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x01befe05);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa27f7ee);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf8c6fcf8);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x034c0954);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c5c0aa4);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x044cfb7e);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf3b1f03f);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf2e2fae1);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x05340dc0);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 5100000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff4);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xfffd001e);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x0051007b);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x006e0006);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff48fe7c);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe1bfe9a);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x001d023e);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x04130488);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x02e6ff5b);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfb1ef812);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7f7fb7f);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x01bc084e);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c430b72);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x059afcba);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf467f046);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf26cfa4a);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x04cd0da0);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 5200000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8ffef);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xfff00009);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x003f007f);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x00980056);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xffa5feb6);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe00fe15);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xff4b0170);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b004d7);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x03e800b9);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfc48f87f);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf768fa23);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0022071f);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf90c1b);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x06dafdfd);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf52df05e);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf1fef9b5);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x04640d7f);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 5300000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9ffee);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe6fff3);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x00250072);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x00af009c);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x000cff10);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe13fdb8);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe870089);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x031104e1);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x04b8020f);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfd98f92f);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf71df8f0);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe8805ce);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7e0c9c);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0808ff44);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf603f086);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf19af922);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x03fb0d5e);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 5400000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffcffef);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe0ffe0);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x00050056);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x00b000d1);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0071ff82);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe53fd8c);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfddfff99);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x024104a3);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x054a034d);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xff01fa1e);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf717f7ed);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfcf50461);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad50cf4);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0921008d);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf6e7f0bd);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf13ff891);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x03920d3b);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 5500000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffffff3);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffd1);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5002f);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x009c00ed);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00cb0000);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfebafd94);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd61feb0);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d0422);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x05970464);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0074fb41);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf759f721);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfb7502de);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a000d21);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0a2201d4);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf7d9f104);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf0edf804);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x03280d19);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 5600000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010003);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0003fffa);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe3ffc9);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffc90002);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x007500ef);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x010e007e);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xff3dfdcf);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd16fddd);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x00440365);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x059b0548);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x01e3fc90);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7dff691);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfa0f014d);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x09020d23);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0b0a0318);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf8d7f15a);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf0a5f779);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x02bd0cf6);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 5700000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010003);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00060001);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffecffc9);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffb4ffd4);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x004000d5);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x013600f0);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xffd3fe39);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd04fd31);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xff360277);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x055605ef);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x033efdfe);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf8a5f642);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf8cbffb6);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e10cfb);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0bd50456);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf9dff1be);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf067f6f2);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x02520cd2);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 5800000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00080009);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xfff8ffd2);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffaaffac);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x000200a3);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x013c014a);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x006dfec9);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd2bfcb7);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe350165);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x04cb0651);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0477ff7e);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf9a5f635);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf7b1fe20);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x069f0ca8);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0c81058b);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xfaf0f231);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf033f66d);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x01e60cae);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 5900000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0009000e);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x0005ffe1);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffacff90);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xffc5005f);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x01210184);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x00fcff72);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd8afc77);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51003f);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x04020669);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x05830103);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfad7f66b);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf6c8fc93);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x05430c2b);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d0d06b5);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xfc08f2b2);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf00af5ec);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x017b0c89);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 6000000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00070012);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x0012fff5);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffbaff82);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff8e000f);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00e80198);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01750028);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe18fc75);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99ff15);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x03050636);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0656027f);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfc32f6e2);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf614fb17);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d20b87);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d7707d2);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xfd26f341);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xefeaf56f);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x010f0c64);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 6100000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00050012);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x001c000b);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffd1ff84);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff66ffbe);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00960184);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01cd00da);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfeccfcb2);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fdf9);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x01e005bc);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x06e703e4);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfdabf798);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf599f9b3);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x02510abd);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0dbf08df);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xfe48f3dc);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xefd5f4f6);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x00a20c3e);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 6200000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0002000f);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x0021001f);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xfff0ff97);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff50ff74);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0034014a);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01fa0179);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xff97fd2a);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fcfa);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x00a304fe);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x07310525);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xff37f886);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf55cf86e);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c709d0);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0de209db);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xff6df484);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xefcbf481);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0x00360c18);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 6300000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffe000a);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x0021002f);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x0010ffb8);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff50ff3b);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xffcc00f0);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01fa01fa);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0069fdd4);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fc26);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xff5d0407);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x07310638);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x00c9f9a8);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf55cf74e);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xff3908c3);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0de20ac3);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0093f537);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xefcbf410);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xffca0bf2);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 6400000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffb0003);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x001c0037);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x002fffe2);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff66ff17);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff6a007e);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01cd0251);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0134fea5);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fb8b);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe2002e0);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x06e70713);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x0255faf5);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf599f658);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaf0799);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0dbf0b96);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x01b8f5f5);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xefd5f3a3);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xff5e0bca);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 6500000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fffb);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x00120037);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x00460010);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff8eff0f);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff180000);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01750276);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x01e8ff8d);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fb31);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfcfb0198);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x065607ad);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x03cefc64);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf614f592);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2e0656);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d770c52);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x02daf6bd);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xefeaf33b);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xfef10ba3);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 6600000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff7fff5);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x0005002f);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x0054003c);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xffc5ff22);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfedfff82);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x00fc0267);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0276007e);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fb1c);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfbfe003e);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x05830802);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x0529fdec);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf6c8f4fe);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabd04ff);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d0d0cf6);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x03f8f78f);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf00af2d7);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xfe850b7b);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 6700000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff0);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xfff80020);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x00560060);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x0002ff4e);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec4ff10);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x006d0225);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02d50166);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fb4e);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb35fee1);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0477080e);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x065bff82);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf7b1f4a0);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf9610397);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0c810d80);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0510f869);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf033f278);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xfe1a0b52);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 6800000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffaffee);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffec000c);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x004c0078);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x0040ff8e);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfecafeb6);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xffd301b6);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02fc0235);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fbc5);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfaaafd90);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x033e07d2);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x075b011b);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf8cbf47a);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81f0224);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0bd50def);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0621f94b);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf067f21e);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xfdae0b29);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 6900000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffdffef);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe3fff6);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x0037007f);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x0075ffdc);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfef2fe7c);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xff3d0122);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02ea02dd);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fc79);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa65fc5d);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x01e3074e);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x082102ad);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfa0ff48c);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fe00a9);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0b0a0e43);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0729fa33);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf0a5f1c9);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xfd430b00);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 7000000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0001fff3);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffe2);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x001b0076);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x009c002d);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff35fe68);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfeba0076);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x029f0352);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfd60);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa69fb53);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x00740688);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x08a7042d);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfb75f4d6);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600ff2d);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0a220e7a);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0827fb22);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf0edf17a);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xfcd80ad6);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 7100000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0004fff9);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe0ffd2);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xfffb005e);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x00b0007a);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff8ffe7c);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe53ffc1);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0221038c);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fe6e);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfab6fa80);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xff010587);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x08e90590);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfcf5f556);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf52bfdb3);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x09210e95);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0919fc15);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf13ff12f);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xfc6e0aab);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 7200000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00070000);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe6ffc9);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffdb0039);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x00af00b8);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfff4feb6);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe13ff10);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x01790388);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0311ff92);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb48f9ed);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfd980453);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x08e306cd);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe88f60a);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf482fc40);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x08080e93);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x09fdfd0c);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf19af0ea);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xfc050a81);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 7300000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00080008);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xfff0ffc9);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffc1000d);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x009800e2);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x005bff10);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe00fe74);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x00b50345);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b000bc);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc18f9a1);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfc4802f9);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x089807dc);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0022f6f0);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf407fada);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x06da0e74);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0ad3fe06);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf1fef0ab);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xfb9c0a55);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 7400000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0008000e);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xfffdffd0);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffafffdf);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x006e00f2);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00b8ff82);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe1bfdf8);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xffe302c8);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x041301dc);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd1af99e);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfb1e0183);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x080908b5);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x01bcf801);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3bdf985);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x059a0e38);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0b99ff03);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf26cf071);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xfb330a2a);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 7500000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00070011);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x000affdf);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffa9ffb5);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x003700e6);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x01010000);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe62fda8);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xff140219);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x043502e1);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe42f9e6);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa270000);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x073a0953);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x034cf939);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3a4f845);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x044c0de1);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0c4f0000);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf2e2f03c);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xfacc09fe);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 7600000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00040012);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x0016fff3);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffafff95);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xfff900c0);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0130007e);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfecefd89);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe560146);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x041303bc);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xff81fa76);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf96cfe7d);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x063209b1);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x04c9fa93);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3bdf71e);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x02f30d6e);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0cf200fd);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf361f00e);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xfa6509d1);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 7700000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00010010);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x001e0008);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffc1ff84);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xffbc0084);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x013e00f0);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xff56fd9f);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdb8005c);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b00460);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x00c7fb45);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8f4fd07);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x04fa09ce);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x062afc07);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf407f614);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x01920ce0);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0d8301fa);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf3e8efe5);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xfa0009a4);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 7800000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffd000b);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x0022001d);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffdbff82);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff870039);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x012a014a);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xffedfde7);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd47ff6b);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x031104c6);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0202fc4c);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8c6fbad);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x039909a7);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0767fd8e);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf482f52b);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x002d0c39);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0e0002f4);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf477efc2);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf99b0977);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 7900000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffa0004);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x0020002d);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xfffbff91);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff61ffe8);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00f70184);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x0086fe5c);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd0bfe85);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x024104e5);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0323fd7d);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8e2fa79);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x021d093f);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0879ff22);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf52bf465);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfec70b79);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0e6803eb);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf50defa5);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf937094a);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 8000000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fffd);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x00190036);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x001bffaf);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff4fff99);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00aa0198);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x0112fef3);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd09fdb9);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d04be);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x041bfecc);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf947f978);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x00900897);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x095a00b9);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600f3c5);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfd650aa3);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0ebc04de);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf5aaef8e);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf8d5091c);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 8100000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff7fff6);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x000e0038);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x0037ffd7);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff52ff56);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x004b0184);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x0186ffa1);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd40fd16);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x00440452);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x04de0029);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9f2f8b2);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfefe07b5);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a05024d);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fef34d);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfc0a09b8);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0efa05cd);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf64eef7d);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf87308ed);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 8200000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff0);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x00000031);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x004c0005);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff6aff27);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xffe4014a);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01d70057);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdacfca6);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xff3603a7);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x05610184);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfadbf82e);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfd74069f);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a7503d6);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81ff2ff);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfab808b9);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0f2306b5);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf6f9ef72);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf81308bf);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 8300000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffbffee);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xfff30022);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x00560032);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff95ff10);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff8000f0);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01fe0106);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe46fc71);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe3502c7);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x059e02ce);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfbf9f7f2);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfbff055b);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0aa9054c);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf961f2db);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf97507aa);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0f350797);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf7a9ef6d);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf7b40890);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 8400000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffeffee);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe8000f);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x00540058);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xffcdff14);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff29007e);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01f6019e);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xff01fc7c);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd5101bf);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x059203f6);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfd41f7fe);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfaa903f3);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a9e06a9);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabdf2e2);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf842068b);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0f320871);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf85eef6e);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf7560860);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 8500000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0002fff2);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe1fff9);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x00460073);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x000bff34);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfee90000);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01c10215);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xffd0fcc5);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99009d);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x053d04f1);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfea5f853);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf97d0270);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a5607e4);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2ef314);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf723055f);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0f180943);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf919ef75);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf6fa0830);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 8600000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0005fff8);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffe4);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x002f007f);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x0048ff6b);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec7ff82);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x0163025f);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x00a2fd47);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17ff73);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x04a405b2);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0017f8ed);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf88500dc);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x09d208f9);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaff370);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf61c0429);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0ee80a0b);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf9d8ef82);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf6a00800);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 8700000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0007ffff);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe1ffd4);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x0010007a);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x007cffb2);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec6ff10);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x00e60277);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0168fdf9);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fe50);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x03ce0631);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0188f9c8);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7c7ff43);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x091509e3);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xff39f3f6);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf52d02ea);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0ea30ac9);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfa9bef95);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf64607d0);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 8800000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00090007);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe9ffca);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xfff00065);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x00a10003);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfee6feb6);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x0053025b);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0213fed0);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fd46);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x02c70668);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x02eafadb);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf74bfdae);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x08230a9c);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c7f4a3);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf45b01a6);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0e480b7c);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfb61efae);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf5ef079f);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 8900000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0008000d);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xfff5ffc8);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffd10043);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x00b20053);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff24fe7c);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xffb9020c);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0295ffbb);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fc64);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x019b0654);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x042dfc1c);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf714fc2a);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x07020b21);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0251f575);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3a7005e);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0dd80c24);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfc2aefcd);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf599076e);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 9000000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00060011);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x0002ffcf);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffba0018);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x00ad009a);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff79fe68);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xff260192);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02e500ab);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fbb6);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x005b05f7);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0545fd81);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf723fabf);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x05b80b70);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d2f669);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf313ff15);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0d550cbf);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfcf6eff2);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf544073d);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 9100000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00030012);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x000fffdd);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffacffea);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x009300cf);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xffdcfe7c);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfea600f7);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02fd0190);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fb46);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xff150554);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0627fefd);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf778f978);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x044d0b87);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0543f77d);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2a0fdcf);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0cbe0d4e);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfdc4f01d);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4f2070b);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 9200000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00000010);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x001afff0);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffaaffbf);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x006700ed);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0043feb6);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe460047);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02db0258);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fb1b);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfddc0473);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x06c90082);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf811f85e);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x02c90b66);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x069ff8ad);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf250fc8d);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0c140dcf);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfe93f04d);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4a106d9);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 9300000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffc000c);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x00200006);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffb4ff9c);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x002f00ef);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00a4ff10);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe0dff92);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x028102f7);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fb37);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfcbf035e);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x07260202);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf8e8f778);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x01340b0d);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e1f9f4);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf223fb51);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0b590e42);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xff64f083);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf45206a7);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 9400000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff90005);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x0022001a);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9ff86);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xfff000d7);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00f2ff82);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe01fee5);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x01f60362);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fb99);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfbcc0222);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x07380370);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf9f7f6cc);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xff990a7e);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0902fb50);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf21afa1f);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0a8d0ea6);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0034f0bf);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4050675);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 9500000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fffe);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x001e002b);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5ff81);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xffb400a5);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x01280000);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe24fe50);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x01460390);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfc3a);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb1000ce);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x070104bf);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb37f65f);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe0009bc);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a00fcbb);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf235f8f8);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x09b20efc);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0105f101);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf3ba0642);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 9600000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0001ffff);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff7);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x00150036);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x0005ff8c);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff810061);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x013d007e);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe71fddf);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x007c0380);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fd13);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa94ff70);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x068005e2);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfc9bf633);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfc7308ca);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad5fe30);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf274f7e0);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x08c90f43);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x01d4f147);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf371060f);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 9700000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fff1);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x00090038);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x0025ffa7);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff5e0012);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x013200f0);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfee3fd9b);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xffaa0331);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0311fe15);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa60fe18);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x05bd06d1);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfe1bf64a);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfafa07ae);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7effab);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2d5f6d7);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x07d30f7a);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x02a3f194);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf32905dc);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 9800000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffcffee);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xfffb0032);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x003fffcd);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff4effc1);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0106014a);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xff6efd8a);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfedd02aa);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b0ff34);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa74fcd7);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x04bf0781);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xffaaf6a3);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf99e066b);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf90128);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf359f5e1);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x06d20fa2);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0370f1e5);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2e405a8);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 9900000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xffffffee);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffef0024);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x0051fffa);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff54ff77);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00be0184);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x0006fdad);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe2701f3);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413005e);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfad1fbba);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x039007ee);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x013bf73d);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf868050a);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c4302a1);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3fdf4fe);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x05c70fba);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x043bf23c);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2a10575);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 10000000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0003fff1);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe50011);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x00570027);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff70ff3c);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00620198);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x009efe01);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd95011a);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x04350183);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb71fad0);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x023c0812);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x02c3f811);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf75e0390);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c5c0411);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf4c1f432);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x04b30fc1);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0503f297);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2610541);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 10100000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0006fff7);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffdffffc);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x00510050);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff9dff18);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfffc0184);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x0128fe80);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd32002e);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x04130292);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc4dfa21);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x00d107ee);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x0435f91c);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf6850205);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c430573);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf5a1f37d);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x03990fba);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x05c7f2f8);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf222050d);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 10200000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0008fffe);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffdfffe7);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x003f006e);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xffd6ff0f);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff96014a);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x0197ff1f);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd05ff3e);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b0037c);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd59f9b7);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xff5d0781);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x0585fa56);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf5e4006f);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf906c4);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf69df2e0);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x02790fa2);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0688f35d);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf1e604d8);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 10300000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00090005);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe4ffd6);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x0025007e);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x0014ff20);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff3c00f0);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01e1ffd0);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd12fe5c);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x03110433);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe88f996);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfdf106d1);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x06aafbb7);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf57efed8);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7e07ff);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf7b0f25e);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x01560f7a);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0745f3c7);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf1ac04a4);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 10400000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0008000c);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffedffcb);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x0005007d);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x0050ff4c);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfef6007e);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01ff0086);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd58fd97);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x024104ad);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xffcaf9c0);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfc9905e2);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x079afd35);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf555fd46);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad50920);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf8d9f1f6);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x00310f43);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x07fdf435);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf174046f);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 10500000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00050011);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xfffaffc8);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5006b);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x0082ff8c);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfecc0000);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01f00130);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdd2fcfc);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d04e3);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x010efa32);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfb6404bf);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x084efec5);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf569fbc2);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a000a23);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfa15f1ab);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xff0b0efc);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x08b0f4a7);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf13f043a);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 10600000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00020012);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x0007ffcd);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9004c);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x00a4ffd9);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec3ff82);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01b401c1);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe76fc97);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x004404d2);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0245fae8);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa5f0370);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x08c1005f);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf5bcfa52);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x09020b04);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfb60f17b);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xfde70ea6);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x095df51e);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf10c0405);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 10700000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xffff0011);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x0014ffdb);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffb40023);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x00b2002a);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfedbff10);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x0150022d);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xff38fc6f);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36047b);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x035efbda);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9940202);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x08ee01f5);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf649f8fe);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e10bc2);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfcb6f169);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xfcc60e42);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0a04f599);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0db03d0);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 10800000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffb000d);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x001dffed);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffaafff5);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x00aa0077);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff13feb6);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x00ce026b);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x000afc85);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe3503e3);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x044cfcfb);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf90c0082);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x08d5037f);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf710f7cc);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x069f0c59);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfe16f173);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xfbaa0dcf);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0aa5f617);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0ad039b);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 10900000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff90006);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x00210003);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffacffc8);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x008e00b6);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff63fe7c);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x003a0275);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x00dafcda);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd510313);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0501fe40);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8cbfefd);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x087604f0);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf80af6c2);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x05430cc8);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xff7af19a);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xfa940d4e);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0b3ff699);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0810365);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 11000000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0001ffff);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8ffff);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x00210018);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffbaffa3);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x006000e1);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xffc4fe68);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xffa0024b);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x019afd66);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc990216);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0575ff99);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8d4fd81);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x07d40640);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf932f5e6);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d20d0d);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x00dff1de);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf9860cbf);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0bd1f71e);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf058032f);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 11100000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff8);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x001b0029);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffd1ff8a);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x002600f2);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x002cfe7c);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xff0f01f0);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x023bfe20);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc1700fa);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x05a200f7);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf927fc1c);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x06f40765);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfa82f53b);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x02510d27);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0243f23d);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf8810c24);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0c5cf7a7);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf03102fa);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 11200000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffafff2);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x00110035);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xfff0ff81);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xffe700e7);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x008ffeb6);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe94016d);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02b0fefb);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3ffd1);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x05850249);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9c1fadb);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x05de0858);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfbf2f4c4);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c70d17);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x03a0f2b8);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf7870b7c);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0cdff833);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf00d02c4);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 11300000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffdffee);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x00040038);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x0010ff88);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xffac00c2);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00e2ff10);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe3900cb);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f1ffe9);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3feaa);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x05210381);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa9cf9c8);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x04990912);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfd7af484);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xff390cdb);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x04f4f34d);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf69a0ac9);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0d5af8c1);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xefec028e);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 11400000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0000ffee);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xfff60033);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x002fff9f);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff7b0087);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x011eff82);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe080018);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f900d8);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fd96);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x04790490);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfbadf8ed);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x032f098e);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xff10f47d);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaf0c75);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x063cf3fc);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf5ba0a0b);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0dccf952);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xefcd0258);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 11500000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0004fff1);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffea0026);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x0046ffc3);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff5a003c);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x013b0000);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe04ff63);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02c801b8);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fca6);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0397056a);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfcecf853);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x01ad09c9);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x00acf4ad);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2e0be7);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0773f4c2);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf4e90943);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0e35f9e6);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xefb10221);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 11600000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0007fff6);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe20014);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x0054ffee);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff4effeb);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0137007e);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe2efebb);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0260027a);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fbe6);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x02870605);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfe4af7fe);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x001d09c1);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0243f515);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabd0b32);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0897f59e);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf4280871);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0e95fa7c);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef9701eb);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 11700000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0008fffd);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffff);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x0056001d);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff57ff9c);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x011300f0);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe82fe2e);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x01ca0310);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fb62);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0155065a);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xffbaf7f2);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfe8c0977);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x03cef5b2);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf9610a58);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x09a5f68f);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf3790797);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0eebfb14);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef8001b5);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 11800000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00080004);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe0ffe9);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x004c0047);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff75ff58);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00d1014a);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfef9fdc8);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0111036f);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fb21);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x00120665);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x012df82e);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfd0708ec);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0542f682);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81f095c);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0a9af792);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf2db06b5);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0f38fbad);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef6c017e);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 11900000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0007000b);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe7ffd8);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x00370068);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xffa4ff28);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00790184);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xff87fd91);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x00430392);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fb26);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfece0626);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0294f8b2);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb990825);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0698f77f);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fe0842);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0b73f8a7);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf25105cd);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0f7bfc48);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef5a0148);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 12000000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00050010);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xfff2ffcc);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x001b007b);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xffdfff10);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00140198);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x0020fd8e);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xff710375);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfb73);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd9a059f);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x03e0f978);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfa4e0726);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x07c8f8a7);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600070c);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0c2ff9c9);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf1db04de);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0fb4fce5);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef4b0111);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 12100000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00010012);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffffffc8);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xfffb007e);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x001dff14);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xffad0184);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x00b7fdbe);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfea9031b);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fc01);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc8504d6);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0504fa79);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf93005f6);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x08caf9f2);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf52b05c0);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0ccbfaf9);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf17903eb);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0fe3fd83);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef3f00db);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 12200000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffe0011);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x000cffcc);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffdb0071);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x0058ff32);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff4f014a);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x013cfe1f);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdfb028a);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0311fcc9);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb9d03d6);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x05f4fbad);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf848049d);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0999fb5b);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf4820461);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d46fc32);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf12d02f4);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x1007fe21);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef3600a4);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 12300000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffa000e);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x0017ffd9);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffc10055);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x0088ff68);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff0400f0);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01a6fea7);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd7501cc);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b0fdc0);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfaef02a8);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x06a7fd07);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf79d0326);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a31fcda);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf40702f3);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d9ffd72);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0f601fa);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x1021fec0);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef2f006d);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 12400000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0001ffff);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80007);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x001fffeb);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffaf002d);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x00a8ffb0);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed3007e);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01e9ff4c);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd2000ee);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413fed8);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa82015c);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0715fe7d);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7340198);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a8dfe69);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3bd017c);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0dd5feb8);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0d500fd);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x1031ff60);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef2b0037);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 12500000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff70000);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x00220000);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffa90000);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x00b30000);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec20000);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x02000000);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd030000);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x04350000);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa5e0000);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x073b0000);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7110000);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0aac0000);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3a40000);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0de70000);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0c90000);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x10360000);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef290000);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 12600000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff9);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x001f0015);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffafffd3);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x00a80050);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed3ff82);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01e900b4);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd20ff12);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x04130128);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa82fea4);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x07150183);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf734fe68);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a8d0197);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3bdfe84);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0dd50148);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0d5ff03);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x103100a0);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef2bffc9);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 12700000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffafff2);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x00170027);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffc1ffab);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x00880098);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff04ff10);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01a60159);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd75fe34);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b00240);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfaeffd58);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x06a702f9);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf79dfcda);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a310326);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf407fd0d);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d9f028e);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0f6fe06);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x10210140);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef2fff93);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 12800000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffeffef);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x000c0034);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffdbff8f);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x005800ce);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff4ffeb6);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x013c01e1);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdfbfd76);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x03110337);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb9dfc2a);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x05f40453);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf848fb63);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x099904a5);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf482fb9f);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d4603ce);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf12dfd0c);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x100701df);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef36ff5c);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 12900000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0001ffee);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffff0038);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xfffbff82);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x001d00ec);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xffadfe7c);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x00b70242);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfea9fce5);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x024103ff);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc85fb2a);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x05040587);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf930fa0a);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x08ca060e);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf52bfa40);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0ccb0507);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf179fc15);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0fe3027d);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef3fff25);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 13000000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0005fff0);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xfff20034);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x001bff85);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xffdf00f0);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0014fe68);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x00200272);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xff71fc8b);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d048d);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd9afa61);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x03e00688);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfa4ef8da);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x07c80759);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600f8f4);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0c2f0637);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf1dbfb22);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0fb4031b);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef4bfeef);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 13100000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0007fff5);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe70028);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x0037ff98);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xffa400d8);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0079fe7c);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xff87026f);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0043fc6e);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x004404da);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfecef9da);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0294074e);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb99f7db);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x06980881);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fef7be);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0b730759);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf251fa33);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0f7b03b8);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef5afeb8);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 13200000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0008fffc);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe00017);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x004cffb9);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff7500a8);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00d1feb6);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfef90238);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0111fc91);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xff3604df);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0012f99b);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x012d07d2);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfd07f714);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0542097e);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81ff6a4);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0a9a086e);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf2dbf94b);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0f380453);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef6cfe82);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 13300000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00080003);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffde0001);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x0056ffe3);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff570064);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0113ff10);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe8201d2);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x01cafcf0);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35049e);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0155f9a6);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xffba080e);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfe8cf689);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x03ce0a4e);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf961f5a8);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x09a50971);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf379f869);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0eeb04ec);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef80fe4b);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 13400000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0007000a);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe2ffec);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x00540012);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff4e0015);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0137ff82);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe2e0145);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0260fd86);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51041a);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0287f9fb);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfe4a0802);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x001df63f);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x02430aeb);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabdf4ce);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x08970a62);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf428f78f);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0e950584);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef97fe15);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 13500000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0004000f);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffeaffda);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x0046003d);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff5affc4);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x013b0000);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe04009d);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02c8fe48);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99035a);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0397fa96);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfcec07ad);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x01adf637);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x00ac0b53);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2ef419);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x07730b3e);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf4e9f6bd);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0e35061a);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xefb1fddf);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 13600000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00000012);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xfff6ffcd);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x002f0061);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff7bff79);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x011e007e);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe08ffe8);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f9ff28);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17026a);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0479fb70);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfbad0713);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x032ff672);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xff100b83);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaff38b);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x063c0c04);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf5baf5f5);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0dcc06ae);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xefcdfda8);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 13700000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffd0012);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x0004ffc8);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x00100078);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xffacff3e);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00e200f0);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe39ff35);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f10017);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd30156);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0521fc7f);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa9c0638);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x0499f6ee);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfd7a0b7c);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0xff39f325);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x04f40cb3);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf69af537);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0d5a073f);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xefecfd72);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 13800000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0001fffe);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffa000e);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x0011ffcb);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xfff0007f);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xffe7ff19);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x008f014a);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe94fe93);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02b00105);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3002f);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0585fdb7);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9c10525);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x05def7a8);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfbf20b3c);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c7f2e9);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x03a00d48);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf787f484);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0cdf07cd);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf00dfd3c);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 13900000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80008);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x001bffd7);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffd10076);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x0026ff0e);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x002c0184);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xff0ffe10);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x023b01e0);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17ff06);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x05a2ff09);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf92703e4);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x06f4f89b);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfa820ac5);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0251f2d9);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x02430dc3);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf881f3dc);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0c5c0859);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf031fd06);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 14000000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80001);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x0021ffe8);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffba005d);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x0060ff1f);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xffc40198);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xffa0fdb5);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x019a029a);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fdea);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x05750067);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8d4027f);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x07d4f9c0);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf9320a1a);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d2f2f3);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0x00df0e22);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf986f341);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0bd108e2);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf058fcd1);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 14100000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fffa);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x0021fffd);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffac0038);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x008eff4a);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff630184);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x003afd8b);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x00da0326);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fced);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x050101c0);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8cb0103);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x0876fb10);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf80a093e);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0543f338);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xff7a0e66);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xfa94f2b2);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0b3f0967);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf081fc9b);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 14200000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffbfff3);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x001d0013);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffaa000b);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x00aaff89);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff13014a);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x00cefd95);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x000a037b);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fc1d);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x044c0305);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf90cff7e);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x08d5fc81);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf7100834);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x069ff3a7);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfe160e8d);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xfbaaf231);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0aa509e9);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0adfc65);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 14300000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xffffffef);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x00140025);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffb4ffdd);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x00b2ffd6);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfedb00f0);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x0150fdd3);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xff380391);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fb85);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x035e0426);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf994fdfe);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x08eefe0b);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf6490702);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e1f43e);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfcb60e97);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xfcc6f1be);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0a040a67);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0dbfc30);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 14400000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0002ffee);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x00070033);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9ffb4);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x00a40027);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec3007e);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01b4fe3f);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe760369);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fb2e);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x02450518);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa5ffc90);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x08c1ffa1);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf5bc05ae);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0902f4fc);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfb600e85);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xfde7f15a);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x095d0ae2);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf10cfbfb);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 14500000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0002);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0005ffef);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xfffa0038);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5ff95);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x00820074);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfecc0000);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01f0fed0);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdd20304);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfb1d);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x010e05ce);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfb64fb41);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x084e013b);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf569043e);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a00f5dd);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfa150e55);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0xff0bf104);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x08b00b59);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf13ffbc6);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 14600000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0008fff4);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffed0035);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x0005ff83);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x005000b4);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfef6ff82);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01ffff7a);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd580269);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fb53);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xffca0640);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfc99fa1e);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x079a02cb);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf55502ba);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad5f6e0);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf8d90e0a);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0031f0bd);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x07fd0bcb);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf174fb91);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 14700000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0009fffb);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe4002a);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x0025ff82);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x001400e0);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff3cff10);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01e10030);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd1201a4);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0311fbcd);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe88066a);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfdf1f92f);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x06aa0449);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf57e0128);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7ef801);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf7b00da2);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0156f086);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x07450c39);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf1acfb5c);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 14800000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00080002);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffdf0019);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x003fff92);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xffd600f1);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff96feb6);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x019700e1);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd0500c2);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b0fc84);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd590649);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0xff5df87f);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x058505aa);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf5e4ff91);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf9f93c);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf69d0d20);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0279f05e);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x06880ca3);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf1e6fb28);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 14900000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x00060009);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffdf0004);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x0051ffb0);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff9d00e8);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfffcfe7c);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01280180);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd32ffd2);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413fd6e);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc4d05df);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x00d1f812);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x043506e4);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf685fdfb);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c43fa8d);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf5a10c83);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0399f046);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x05c70d08);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf222faf3);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 15000000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0003000f);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe5ffef);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x0057ffd9);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff7000c4);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0062fe68);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x009e01ff);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd95fee6);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0435fe7d);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb710530);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x023cf7ee);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x02c307ef);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf75efc70);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c5cfbef);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf4c10bce);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x04b3f03f);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x05030d69);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf261fabf);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 15100000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xffff0012);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xffefffdc);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x00510006);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff540089);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00befe7c);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0x00060253);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe27fe0d);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413ffa2);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfad10446);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0390f812);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0x013b08c3);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf868faf6);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c43fd5f);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3fd0b02);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x05c7f046);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x043b0dc4);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2a1fa8b);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 15200000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0001fffe);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffc0012);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0xfffbffce);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x003f0033);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff4e003f);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0106feb6);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xff6e0276);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfeddfd56);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b000cc);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa740329);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x04bff87f);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xffaa095d);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf99ef995);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf9fed8);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3590a1f);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x06d2f05e);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x03700e1b);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2e4fa58);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 15300000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x0001ffff);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9000f);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x0009ffc8);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x00250059);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff5effee);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0132ff10);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfee30265);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0xffaafccf);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x031101eb);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa6001e8);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x05bdf92f);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfe1b09b6);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfafaf852);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7e0055);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2d50929);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x07d3f086);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x02a30e6c);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf329fa24);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 15400000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80009);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x0015ffca);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0x00050074);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xff81ff9f);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x013dff82);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe710221);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x007cfc80);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x024102ed);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa940090);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0680fa1e);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfc9b09cd);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfc73f736);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad501d0);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2740820);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x08c9f0bd);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x01d40eb9);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf371f9f1);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 15500000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80002);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x001effd5);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5007f);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xffb4ff5b);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x01280000);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe2401b0);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0146fc70);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d03c6);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb10ff32);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0701fb41);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb3709a1);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe00f644);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a000345);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2350708);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x09b2f104);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x01050eff);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf3baf9be);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 15600000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fffb);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x0022ffe6);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9007a);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0xfff0ff29);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00f2007e);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe01011b);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x01f6fc9e);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0x00440467);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfbccfdde);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0738fc90);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf9f70934);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0xff99f582);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x090204b0);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf21a05e1);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0a8df15a);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0x00340f41);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf405f98b);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 15700000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffcfff4);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x0020fffa);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffb40064);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x002fff11);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00a400f0);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe0d006e);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0281fd09);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xff3604c9);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfcbffca2);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0726fdfe);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf8e80888);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0134f4f3);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e1060c);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf22304af);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0b59f1be);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xff640f7d);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf452f959);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 15800000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0000fff0);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x001a0010);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffaa0041);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x0067ff13);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0043014a);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe46ffb9);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02dbfda8);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe3504e5);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfddcfb8d);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x06c9ff7e);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf81107a2);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x02c9f49a);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x069f0753);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2500373);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0c14f231);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfe930fb3);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4a1f927);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 15900000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0002);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0003ffee);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x000f0023);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffac0016);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x0093ff31);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xffdc0184);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfea6ff09);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02fdfe70);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd5104ba);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0xff15faac);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x06270103);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7780688);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x044df479);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x05430883);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2a00231);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0cbef2b2);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfdc40fe3);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4f2f8f5);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+
+ case 16000000:
+ cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001);
+ cx25840_write4(client, DIF_BPF_COEFF23, 0x0006ffef);
+ cx25840_write4(client, DIF_BPF_COEFF45, 0x00020031);
+ cx25840_write4(client, DIF_BPF_COEFF67, 0xffbaffe8);
+ cx25840_write4(client, DIF_BPF_COEFF89, 0x00adff66);
+ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff790198);
+ cx25840_write4(client, DIF_BPF_COEFF1213, 0xff26fe6e);
+ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02e5ff55);
+ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99044a);
+ cx25840_write4(client, DIF_BPF_COEFF1819, 0x005bfa09);
+ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0545027f);
+ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7230541);
+ cx25840_write4(client, DIF_BPF_COEFF2425, 0x05b8f490);
+ cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d20997);
+ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf31300eb);
+ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0d55f341);
+ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfcf6100e);
+ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf544f8c3);
+ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000);
+ break;
+ }
+}
+
+static void cx23885_std_setup(struct i2c_client *client)
+{
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+ v4l2_std_id std = state->std;
+ u32 ifHz;
+
+ cx25840_write4(client, 0x478, 0x6628021F);
+ cx25840_write4(client, 0x400, 0x0);
+ cx25840_write4(client, 0x4b4, 0x20524030);
+ cx25840_write4(client, 0x47c, 0x010a8263);
+
+ if (std & V4L2_STD_NTSC) {
+ v4l_dbg(1, cx25840_debug, client, "%s() Selecting NTSC",
+ __func__);
+
+ /* Horiz / vert timing */
+ cx25840_write4(client, 0x428, 0x1e1e601a);
+ cx25840_write4(client, 0x424, 0x5b2d007a);
+
+ /* DIF NTSC */
+ cx25840_write4(client, 0x304, 0x6503bc0c);
+ cx25840_write4(client, 0x308, 0xbd038c85);
+ cx25840_write4(client, 0x30c, 0x1db4640a);
+ cx25840_write4(client, 0x310, 0x00008800);
+ cx25840_write4(client, 0x314, 0x44400400);
+ cx25840_write4(client, 0x32c, 0x0c800800);
+ cx25840_write4(client, 0x330, 0x27000100);
+ cx25840_write4(client, 0x334, 0x1f296e1f);
+ cx25840_write4(client, 0x338, 0x009f50c1);
+ cx25840_write4(client, 0x340, 0x1befbf06);
+ cx25840_write4(client, 0x344, 0x000035e8);
+
+ /* DIF I/F */
+ ifHz = 5400000;
+
+ } else {
+ v4l_dbg(1, cx25840_debug, client, "%s() Selecting PAL-BG",
+ __func__);
+
+ /* Horiz / vert timing */
+ cx25840_write4(client, 0x428, 0x28244024);
+ cx25840_write4(client, 0x424, 0x5d2d0084);
+
+ /* DIF */
+ cx25840_write4(client, 0x304, 0x6503bc0c);
+ cx25840_write4(client, 0x308, 0xbd038c85);
+ cx25840_write4(client, 0x30c, 0x1db4640a);
+ cx25840_write4(client, 0x310, 0x00008800);
+ cx25840_write4(client, 0x314, 0x44400600);
+ cx25840_write4(client, 0x32c, 0x0c800800);
+ cx25840_write4(client, 0x330, 0x27000100);
+ cx25840_write4(client, 0x334, 0x213530ec);
+ cx25840_write4(client, 0x338, 0x00a65ba8);
+ cx25840_write4(client, 0x340, 0x1befbf06);
+ cx25840_write4(client, 0x344, 0x000035e8);
+
+ /* DIF I/F */
+ ifHz = 6000000;
+ }
+
+ cx23885_dif_setup(client, ifHz);
+
+ /* Explicitly ensure the inputs are reconfigured after
+ * a standard change.
+ */
+ set_input(client, state->vid_input, state->aud_input);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_ctrl_ops cx25840_ctrl_ops = {
+ .s_ctrl = cx25840_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops cx25840_core_ops = {
+ .log_status = cx25840_log_status,
+ .g_chip_ident = cx25840_g_chip_ident,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+ .s_std = cx25840_s_std,
+ .g_std = cx25840_g_std,
+ .reset = cx25840_reset,
+ .load_fw = cx25840_load_fw,
+ .s_io_pin_config = common_s_io_pin_config,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = cx25840_g_register,
+ .s_register = cx25840_s_register,
+#endif
+ .interrupt_service_routine = cx25840_irq_handler,
+};
+
+static const struct v4l2_subdev_tuner_ops cx25840_tuner_ops = {
+ .s_frequency = cx25840_s_frequency,
+ .s_radio = cx25840_s_radio,
+ .g_tuner = cx25840_g_tuner,
+ .s_tuner = cx25840_s_tuner,
+};
+
+static const struct v4l2_subdev_audio_ops cx25840_audio_ops = {
+ .s_clock_freq = cx25840_s_clock_freq,
+ .s_routing = cx25840_s_audio_routing,
+ .s_stream = cx25840_s_audio_stream,
+};
+
+static const struct v4l2_subdev_video_ops cx25840_video_ops = {
+ .s_routing = cx25840_s_video_routing,
+ .s_mbus_fmt = cx25840_s_mbus_fmt,
+ .s_stream = cx25840_s_stream,
+ .g_input_status = cx25840_g_input_status,
+};
+
+static const struct v4l2_subdev_vbi_ops cx25840_vbi_ops = {
+ .decode_vbi_line = cx25840_decode_vbi_line,
+ .s_raw_fmt = cx25840_s_raw_fmt,
+ .s_sliced_fmt = cx25840_s_sliced_fmt,
+ .g_sliced_fmt = cx25840_g_sliced_fmt,
+};
+
+static const struct v4l2_subdev_ops cx25840_ops = {
+ .core = &cx25840_core_ops,
+ .tuner = &cx25840_tuner_ops,
+ .audio = &cx25840_audio_ops,
+ .video = &cx25840_video_ops,
+ .vbi = &cx25840_vbi_ops,
+ .ir = &cx25840_ir_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static u32 get_cx2388x_ident(struct i2c_client *client)
+{
+ u32 ret;
+
+ /* Come out of digital power down */
+ cx25840_write(client, 0x000, 0);
+
+ /* Detecting whether the part is cx23885/7/8 is more
+ * difficult than it needs to be. No ID register. Instead we
+ * probe certain registers indicated in the datasheets to look
+ * for specific defaults that differ between the silicon designs. */
+
+ /* It's either 885/7 if the IR Tx Clk Divider register exists */
+ if (cx25840_read4(client, 0x204) & 0xffff) {
+ /* CX23885 returns bogus repetitive byte values for the DIF,
+ * which doesn't exist for it. (Ex. 8a8a8a8a or 31313131) */
+ ret = cx25840_read4(client, 0x300);
+ if (((ret & 0xffff0000) >> 16) == (ret & 0xffff)) {
+ /* No DIF */
+ ret = V4L2_IDENT_CX23885_AV;
+ } else {
+ /* CX23887 has a broken DIF, but the registers
+ * appear valid (but unused), good enough to detect. */
+ ret = V4L2_IDENT_CX23887_AV;
+ }
+ } else if (cx25840_read4(client, 0x300) & 0x0fffffff) {
+ /* DIF PLL Freq Word reg exists; chip must be a CX23888 */
+ ret = V4L2_IDENT_CX23888_AV;
+ } else {
+ v4l_err(client, "Unable to detect h/w, assuming cx23887\n");
+ ret = V4L2_IDENT_CX23887_AV;
+ }
+
+ /* Back into digital power down */
+ cx25840_write(client, 0x000, 2);
+ return ret;
+}
+
+static int cx25840_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct cx25840_state *state;
+ struct v4l2_subdev *sd;
+ int default_volume;
+ u32 id = V4L2_IDENT_NONE;
+ u16 device_id;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ v4l_dbg(1, cx25840_debug, client, "detecting cx25840 client on address 0x%x\n", client->addr << 1);
+
+ device_id = cx25840_read(client, 0x101) << 8;
+ device_id |= cx25840_read(client, 0x100);
+ v4l_dbg(1, cx25840_debug, client, "device_id = 0x%04x\n", device_id);
+
+ /* The high byte of the device ID should be
+ * 0x83 for the cx2583x and 0x84 for the cx2584x */
+ if ((device_id & 0xff00) == 0x8300) {
+ id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6;
+ } else if ((device_id & 0xff00) == 0x8400) {
+ id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf);
+ } else if (device_id == 0x0000) {
+ id = get_cx2388x_ident(client);
+ } else if ((device_id & 0xfff0) == 0x5A30) {
+ /* The CX23100 (0x5A3C = 23100) doesn't have an A/V decoder */
+ id = V4L2_IDENT_CX2310X_AV;
+ } else if ((device_id & 0xff) == (device_id >> 8)) {
+ v4l_err(client,
+ "likely a confused/unresponsive cx2388[578] A/V decoder"
+ " found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+ v4l_err(client, "A method to reset it from the cx25840 driver"
+ " software is not known at this time\n");
+ return -ENODEV;
+ } else {
+ v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n");
+ return -ENODEV;
+ }
+
+ state = kzalloc(sizeof(struct cx25840_state), GFP_KERNEL);
+ if (state == NULL)
+ return -ENOMEM;
+
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &cx25840_ops);
+
+ switch (id) {
+ case V4L2_IDENT_CX23885_AV:
+ v4l_info(client, "cx23885 A/V decoder found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+ break;
+ case V4L2_IDENT_CX23887_AV:
+ v4l_info(client, "cx23887 A/V decoder found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+ break;
+ case V4L2_IDENT_CX23888_AV:
+ v4l_info(client, "cx23888 A/V decoder found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+ break;
+ case V4L2_IDENT_CX2310X_AV:
+ v4l_info(client, "cx%d A/V decoder found @ 0x%x (%s)\n",
+ device_id, client->addr << 1, client->adapter->name);
+ break;
+ case V4L2_IDENT_CX25840:
+ case V4L2_IDENT_CX25841:
+ case V4L2_IDENT_CX25842:
+ case V4L2_IDENT_CX25843:
+ /* Note: revision '(device_id & 0x0f) == 2' was never built. The
+ marking skips from 0x1 == 22 to 0x3 == 23. */
+ v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n",
+ (device_id & 0xfff0) >> 4,
+ (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1
+ : (device_id & 0x0f),
+ client->addr << 1, client->adapter->name);
+ break;
+ case V4L2_IDENT_CX25836:
+ case V4L2_IDENT_CX25837:
+ default:
+ v4l_info(client, "cx25%3x-%x found @ 0x%x (%s)\n",
+ (device_id & 0xfff0) >> 4, device_id & 0x0f,
+ client->addr << 1, client->adapter->name);
+ break;
+ }
+
+ state->c = client;
+ state->vid_input = CX25840_COMPOSITE7;
+ state->aud_input = CX25840_AUDIO8;
+ state->audclk_freq = 48000;
+ state->audmode = V4L2_TUNER_MODE_LANG1;
+ state->vbi_line_offset = 8;
+ state->id = id;
+ state->rev = device_id;
+ v4l2_ctrl_handler_init(&state->hdl, 9);
+ v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
+ V4L2_CID_HUE, -128, 127, 1, 0);
+ if (!is_cx2583x(state)) {
+ default_volume = cx25840_read(client, 0x8d4);
+ /*
+ * Enforce the legacy PVR-350/MSP3400 to PVR-150/CX25843 volume
+ * scale mapping limits to avoid -ERANGE errors when
+ * initializing the volume control
+ */
+ if (default_volume > 228) {
+ /* Bottom out at -96 dB, v4l2 vol range 0x2e00-0x2fff */
+ default_volume = 228;
+ cx25840_write(client, 0x8d4, 228);
+ }
+ else if (default_volume < 20) {
+ /* Top out at + 8 dB, v4l2 vol range 0xfe00-0xffff */
+ default_volume = 20;
+ cx25840_write(client, 0x8d4, 20);
+ }
+ default_volume = (((228 - default_volume) >> 1) + 23) << 9;
+
+ state->volume = v4l2_ctrl_new_std(&state->hdl,
+ &cx25840_audio_ctrl_ops, V4L2_CID_AUDIO_VOLUME,
+ 0, 65535, 65535 / 100, default_volume);
+ state->mute = v4l2_ctrl_new_std(&state->hdl,
+ &cx25840_audio_ctrl_ops, V4L2_CID_AUDIO_MUTE,
+ 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops,
+ V4L2_CID_AUDIO_BALANCE,
+ 0, 65535, 65535 / 100, 32768);
+ v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops,
+ V4L2_CID_AUDIO_BASS,
+ 0, 65535, 65535 / 100, 32768);
+ v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops,
+ V4L2_CID_AUDIO_TREBLE,
+ 0, 65535, 65535 / 100, 32768);
+ }
+ sd->ctrl_handler = &state->hdl;
+ if (state->hdl.error) {
+ int err = state->hdl.error;
+
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ return err;
+ }
+ if (!is_cx2583x(state))
+ v4l2_ctrl_cluster(2, &state->volume);
+ v4l2_ctrl_handler_setup(&state->hdl);
+
+ if (client->dev.platform_data) {
+ struct cx25840_platform_data *pdata = client->dev.platform_data;
+
+ state->pvr150_workaround = pdata->pvr150_workaround;
+ }
+
+ cx25840_ir_probe(sd);
+ return 0;
+}
+
+static int cx25840_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(sd);
+
+ cx25840_ir_remove(sd);
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ return 0;
+}
+
+static const struct i2c_device_id cx25840_id[] = {
+ { "cx25840", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cx25840_id);
+
+static struct i2c_driver cx25840_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "cx25840",
+ },
+ .probe = cx25840_probe,
+ .remove = cx25840_remove,
+ .id_table = cx25840_id,
+};
+
+module_i2c_driver(cx25840_driver);
diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h
new file mode 100644
index 00000000..bd4ada28
--- /dev/null
+++ b/drivers/media/video/cx25840/cx25840-core.h
@@ -0,0 +1,137 @@
+/* cx25840 internal API header
+ *
+ * Copyright (C) 2003-2004 Chris Kennedy
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _CX25840_CORE_H_
+#define _CX25840_CORE_H_
+
+
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+#include <linux/i2c.h>
+
+struct cx25840_ir_state;
+
+struct cx25840_state {
+ struct i2c_client *c;
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+ struct {
+ /* volume cluster */
+ struct v4l2_ctrl *volume;
+ struct v4l2_ctrl *mute;
+ };
+ int pvr150_workaround;
+ int radio;
+ v4l2_std_id std;
+ enum cx25840_video_input vid_input;
+ enum cx25840_audio_input aud_input;
+ u32 audclk_freq;
+ int audmode;
+ int vbi_line_offset;
+ u32 id;
+ u32 rev;
+ int is_initialized;
+ wait_queue_head_t fw_wait; /* wake up when the fw load is finished */
+ struct work_struct fw_work; /* work entry for fw load */
+ struct cx25840_ir_state *ir_state;
+};
+
+static inline struct cx25840_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct cx25840_state, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct cx25840_state, hdl)->sd;
+}
+
+static inline bool is_cx2583x(struct cx25840_state *state)
+{
+ return state->id == V4L2_IDENT_CX25836 ||
+ state->id == V4L2_IDENT_CX25837;
+}
+
+static inline bool is_cx231xx(struct cx25840_state *state)
+{
+ return state->id == V4L2_IDENT_CX2310X_AV;
+}
+
+static inline bool is_cx2388x(struct cx25840_state *state)
+{
+ return state->id == V4L2_IDENT_CX23885_AV ||
+ state->id == V4L2_IDENT_CX23887_AV ||
+ state->id == V4L2_IDENT_CX23888_AV;
+}
+
+static inline bool is_cx23885(struct cx25840_state *state)
+{
+ return state->id == V4L2_IDENT_CX23885_AV;
+}
+
+static inline bool is_cx23887(struct cx25840_state *state)
+{
+ return state->id == V4L2_IDENT_CX23887_AV;
+}
+
+static inline bool is_cx23888(struct cx25840_state *state)
+{
+ return state->id == V4L2_IDENT_CX23888_AV;
+}
+
+/* ----------------------------------------------------------------------- */
+/* cx25850-core.c */
+int cx25840_write(struct i2c_client *client, u16 addr, u8 value);
+int cx25840_write4(struct i2c_client *client, u16 addr, u32 value);
+u8 cx25840_read(struct i2c_client *client, u16 addr);
+u32 cx25840_read4(struct i2c_client *client, u16 addr);
+int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned mask, u8 value);
+int cx25840_and_or4(struct i2c_client *client, u16 addr, u32 and_mask,
+ u32 or_value);
+void cx25840_std_setup(struct i2c_client *client);
+
+/* ----------------------------------------------------------------------- */
+/* cx25850-firmware.c */
+int cx25840_loadfw(struct i2c_client *client);
+
+/* ----------------------------------------------------------------------- */
+/* cx25850-audio.c */
+void cx25840_audio_set_path(struct i2c_client *client);
+int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq);
+
+extern const struct v4l2_ctrl_ops cx25840_audio_ctrl_ops;
+
+/* ----------------------------------------------------------------------- */
+/* cx25850-vbi.c */
+int cx25840_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt);
+int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt);
+int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt);
+int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi);
+
+/* ----------------------------------------------------------------------- */
+/* cx25850-ir.c */
+extern const struct v4l2_subdev_ir_ops cx25840_ir_ops;
+int cx25840_ir_log_status(struct v4l2_subdev *sd);
+int cx25840_ir_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled);
+int cx25840_ir_probe(struct v4l2_subdev *sd);
+int cx25840_ir_remove(struct v4l2_subdev *sd);
+
+#endif
diff --git a/drivers/media/video/cx25840/cx25840-firmware.c b/drivers/media/video/cx25840/cx25840-firmware.c
new file mode 100644
index 00000000..81502005
--- /dev/null
+++ b/drivers/media/video/cx25840/cx25840-firmware.c
@@ -0,0 +1,166 @@
+/* cx25840 firmware functions
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+#include <media/v4l2-common.h>
+#include <media/cx25840.h>
+
+#include "cx25840-core.h"
+
+/*
+ * Mike Isely <isely@pobox.com> - The FWSEND parameter controls the
+ * size of the firmware chunks sent down the I2C bus to the chip.
+ * Previously this had been set to 1024 but unfortunately some I2C
+ * implementations can't transfer data in such big gulps.
+ * Specifically, the pvrusb2 driver has a hard limit of around 60
+ * bytes, due to the encapsulation there of I2C traffic into USB
+ * messages. So we have to significantly reduce this parameter.
+ */
+#define FWSEND 48
+
+#define FWDEV(x) &((x)->dev)
+
+static char *firmware = "";
+
+module_param(firmware, charp, 0444);
+
+MODULE_PARM_DESC(firmware, "Firmware image to load");
+
+static void start_fw_load(struct i2c_client *client)
+{
+ /* DL_ADDR_LB=0 DL_ADDR_HB=0 */
+ cx25840_write(client, 0x800, 0x00);
+ cx25840_write(client, 0x801, 0x00);
+ // DL_MAP=3 DL_AUTO_INC=0 DL_ENABLE=1
+ cx25840_write(client, 0x803, 0x0b);
+ /* AUTO_INC_DIS=1 */
+ cx25840_write(client, 0x000, 0x20);
+}
+
+static void end_fw_load(struct i2c_client *client)
+{
+ /* AUTO_INC_DIS=0 */
+ cx25840_write(client, 0x000, 0x00);
+ /* DL_ENABLE=0 */
+ cx25840_write(client, 0x803, 0x03);
+}
+
+static const char *get_fw_name(struct i2c_client *client)
+{
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+
+ if (firmware[0])
+ return firmware;
+ if (is_cx2388x(state))
+ return "v4l-cx23885-avcore-01.fw";
+ if (is_cx231xx(state))
+ return "v4l-cx231xx-avcore-01.fw";
+ return "v4l-cx25840.fw";
+}
+
+static int check_fw_load(struct i2c_client *client, int size)
+{
+ /* DL_ADDR_HB DL_ADDR_LB */
+ int s = cx25840_read(client, 0x801) << 8;
+ s |= cx25840_read(client, 0x800);
+
+ if (size != s) {
+ v4l_err(client, "firmware %s load failed\n",
+ get_fw_name(client));
+ return -EINVAL;
+ }
+
+ v4l_info(client, "loaded %s firmware (%d bytes)\n",
+ get_fw_name(client), size);
+ return 0;
+}
+
+static int fw_write(struct i2c_client *client, const u8 *data, int size)
+{
+ if (i2c_master_send(client, data, size) < size) {
+ v4l_err(client, "firmware load i2c failure\n");
+ return -ENOSYS;
+ }
+
+ return 0;
+}
+
+int cx25840_loadfw(struct i2c_client *client)
+{
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+ const struct firmware *fw = NULL;
+ u8 buffer[FWSEND];
+ const u8 *ptr;
+ const char *fwname = get_fw_name(client);
+ int size, retval;
+ int MAX_BUF_SIZE = FWSEND;
+ u32 gpio_oe = 0, gpio_da = 0;
+
+ if (is_cx2388x(state)) {
+ /* Preserve the GPIO OE and output bits */
+ gpio_oe = cx25840_read(client, 0x160);
+ gpio_da = cx25840_read(client, 0x164);
+ }
+
+ if (is_cx231xx(state) && MAX_BUF_SIZE > 16) {
+ v4l_err(client, " Firmware download size changed to 16 bytes max length\n");
+ MAX_BUF_SIZE = 16; /* cx231xx cannot accept more than 16 bytes at a time */
+ }
+
+ if (request_firmware(&fw, fwname, FWDEV(client)) != 0) {
+ v4l_err(client, "unable to open firmware %s\n", fwname);
+ return -EINVAL;
+ }
+
+ start_fw_load(client);
+
+ buffer[0] = 0x08;
+ buffer[1] = 0x02;
+
+ size = fw->size;
+ ptr = fw->data;
+ while (size > 0) {
+ int len = min(MAX_BUF_SIZE - 2, size);
+
+ memcpy(buffer + 2, ptr, len);
+
+ retval = fw_write(client, buffer, len + 2);
+
+ if (retval < 0) {
+ release_firmware(fw);
+ return retval;
+ }
+
+ size -= len;
+ ptr += len;
+ }
+
+ end_fw_load(client);
+
+ size = fw->size;
+ release_firmware(fw);
+
+ if (is_cx2388x(state)) {
+ /* Restore GPIO configuration after f/w load */
+ cx25840_write(client, 0x160, gpio_oe);
+ cx25840_write(client, 0x164, gpio_da);
+ }
+
+ return check_fw_load(client, size);
+}
diff --git a/drivers/media/video/cx25840/cx25840-ir.c b/drivers/media/video/cx25840/cx25840-ir.c
new file mode 100644
index 00000000..13c380eb
--- /dev/null
+++ b/drivers/media/video/cx25840/cx25840-ir.c
@@ -0,0 +1,1285 @@
+/*
+ * Driver for the Conexant CX2584x Audio/Video decoder chip and related cores
+ *
+ * Integrated Consumer Infrared Controller
+ *
+ * Copyright (C) 2010 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <linux/slab.h>
+#include <linux/kfifo.h>
+#include <linux/module.h>
+#include <media/cx25840.h>
+#include <media/rc-core.h>
+
+#include "cx25840-core.h"
+
+static unsigned int ir_debug;
+module_param(ir_debug, int, 0644);
+MODULE_PARM_DESC(ir_debug, "enable integrated IR debug messages");
+
+#define CX25840_IR_REG_BASE 0x200
+
+#define CX25840_IR_CNTRL_REG 0x200
+#define CNTRL_WIN_3_3 0x00000000
+#define CNTRL_WIN_4_3 0x00000001
+#define CNTRL_WIN_3_4 0x00000002
+#define CNTRL_WIN_4_4 0x00000003
+#define CNTRL_WIN 0x00000003
+#define CNTRL_EDG_NONE 0x00000000
+#define CNTRL_EDG_FALL 0x00000004
+#define CNTRL_EDG_RISE 0x00000008
+#define CNTRL_EDG_BOTH 0x0000000C
+#define CNTRL_EDG 0x0000000C
+#define CNTRL_DMD 0x00000010
+#define CNTRL_MOD 0x00000020
+#define CNTRL_RFE 0x00000040
+#define CNTRL_TFE 0x00000080
+#define CNTRL_RXE 0x00000100
+#define CNTRL_TXE 0x00000200
+#define CNTRL_RIC 0x00000400
+#define CNTRL_TIC 0x00000800
+#define CNTRL_CPL 0x00001000
+#define CNTRL_LBM 0x00002000
+#define CNTRL_R 0x00004000
+
+#define CX25840_IR_TXCLK_REG 0x204
+#define TXCLK_TCD 0x0000FFFF
+
+#define CX25840_IR_RXCLK_REG 0x208
+#define RXCLK_RCD 0x0000FFFF
+
+#define CX25840_IR_CDUTY_REG 0x20C
+#define CDUTY_CDC 0x0000000F
+
+#define CX25840_IR_STATS_REG 0x210
+#define STATS_RTO 0x00000001
+#define STATS_ROR 0x00000002
+#define STATS_RBY 0x00000004
+#define STATS_TBY 0x00000008
+#define STATS_RSR 0x00000010
+#define STATS_TSR 0x00000020
+
+#define CX25840_IR_IRQEN_REG 0x214
+#define IRQEN_RTE 0x00000001
+#define IRQEN_ROE 0x00000002
+#define IRQEN_RSE 0x00000010
+#define IRQEN_TSE 0x00000020
+#define IRQEN_MSK 0x00000033
+
+#define CX25840_IR_FILTR_REG 0x218
+#define FILTR_LPF 0x0000FFFF
+
+#define CX25840_IR_FIFO_REG 0x23C
+#define FIFO_RXTX 0x0000FFFF
+#define FIFO_RXTX_LVL 0x00010000
+#define FIFO_RXTX_RTO 0x0001FFFF
+#define FIFO_RX_NDV 0x00020000
+#define FIFO_RX_DEPTH 8
+#define FIFO_TX_DEPTH 8
+
+#define CX25840_VIDCLK_FREQ 108000000 /* 108 MHz, BT.656 */
+#define CX25840_IR_REFCLK_FREQ (CX25840_VIDCLK_FREQ / 2)
+
+/*
+ * We use this union internally for convenience, but callers to tx_write
+ * and rx_read will be expecting records of type struct ir_raw_event.
+ * Always ensure the size of this union is dictated by struct ir_raw_event.
+ */
+union cx25840_ir_fifo_rec {
+ u32 hw_fifo_data;
+ struct ir_raw_event ir_core_data;
+};
+
+#define CX25840_IR_RX_KFIFO_SIZE (256 * sizeof(union cx25840_ir_fifo_rec))
+#define CX25840_IR_TX_KFIFO_SIZE (256 * sizeof(union cx25840_ir_fifo_rec))
+
+struct cx25840_ir_state {
+ struct i2c_client *c;
+
+ struct v4l2_subdev_ir_parameters rx_params;
+ struct mutex rx_params_lock; /* protects Rx parameter settings cache */
+ atomic_t rxclk_divider;
+ atomic_t rx_invert;
+
+ struct kfifo rx_kfifo;
+ spinlock_t rx_kfifo_lock; /* protect Rx data kfifo */
+
+ struct v4l2_subdev_ir_parameters tx_params;
+ struct mutex tx_params_lock; /* protects Tx parameter settings cache */
+ atomic_t txclk_divider;
+};
+
+static inline struct cx25840_ir_state *to_ir_state(struct v4l2_subdev *sd)
+{
+ struct cx25840_state *state = to_state(sd);
+ return state ? state->ir_state : NULL;
+}
+
+
+/*
+ * Rx and Tx Clock Divider register computations
+ *
+ * Note the largest clock divider value of 0xffff corresponds to:
+ * (0xffff + 1) * 1000 / 108/2 MHz = 1,213,629.629... ns
+ * which fits in 21 bits, so we'll use unsigned int for time arguments.
+ */
+static inline u16 count_to_clock_divider(unsigned int d)
+{
+ if (d > RXCLK_RCD + 1)
+ d = RXCLK_RCD;
+ else if (d < 2)
+ d = 1;
+ else
+ d--;
+ return (u16) d;
+}
+
+static inline u16 ns_to_clock_divider(unsigned int ns)
+{
+ return count_to_clock_divider(
+ DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ / 1000000 * ns, 1000));
+}
+
+static inline unsigned int clock_divider_to_ns(unsigned int divider)
+{
+ /* Period of the Rx or Tx clock in ns */
+ return DIV_ROUND_CLOSEST((divider + 1) * 1000,
+ CX25840_IR_REFCLK_FREQ / 1000000);
+}
+
+static inline u16 carrier_freq_to_clock_divider(unsigned int freq)
+{
+ return count_to_clock_divider(
+ DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, freq * 16));
+}
+
+static inline unsigned int clock_divider_to_carrier_freq(unsigned int divider)
+{
+ return DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, (divider + 1) * 16);
+}
+
+static inline u16 freq_to_clock_divider(unsigned int freq,
+ unsigned int rollovers)
+{
+ return count_to_clock_divider(
+ DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, freq * rollovers));
+}
+
+static inline unsigned int clock_divider_to_freq(unsigned int divider,
+ unsigned int rollovers)
+{
+ return DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ,
+ (divider + 1) * rollovers);
+}
+
+/*
+ * Low Pass Filter register calculations
+ *
+ * Note the largest count value of 0xffff corresponds to:
+ * 0xffff * 1000 / 108/2 MHz = 1,213,611.11... ns
+ * which fits in 21 bits, so we'll use unsigned int for time arguments.
+ */
+static inline u16 count_to_lpf_count(unsigned int d)
+{
+ if (d > FILTR_LPF)
+ d = FILTR_LPF;
+ else if (d < 4)
+ d = 0;
+ return (u16) d;
+}
+
+static inline u16 ns_to_lpf_count(unsigned int ns)
+{
+ return count_to_lpf_count(
+ DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ / 1000000 * ns, 1000));
+}
+
+static inline unsigned int lpf_count_to_ns(unsigned int count)
+{
+ /* Duration of the Low Pass Filter rejection window in ns */
+ return DIV_ROUND_CLOSEST(count * 1000,
+ CX25840_IR_REFCLK_FREQ / 1000000);
+}
+
+static inline unsigned int lpf_count_to_us(unsigned int count)
+{
+ /* Duration of the Low Pass Filter rejection window in us */
+ return DIV_ROUND_CLOSEST(count, CX25840_IR_REFCLK_FREQ / 1000000);
+}
+
+/*
+ * FIFO register pulse width count compuations
+ */
+static u32 clock_divider_to_resolution(u16 divider)
+{
+ /*
+ * Resolution is the duration of 1 tick of the readable portion of
+ * of the pulse width counter as read from the FIFO. The two lsb's are
+ * not readable, hence the << 2. This function returns ns.
+ */
+ return DIV_ROUND_CLOSEST((1 << 2) * ((u32) divider + 1) * 1000,
+ CX25840_IR_REFCLK_FREQ / 1000000);
+}
+
+static u64 pulse_width_count_to_ns(u16 count, u16 divider)
+{
+ u64 n;
+ u32 rem;
+
+ /*
+ * The 2 lsb's of the pulse width timer count are not readable, hence
+ * the (count << 2) | 0x3
+ */
+ n = (((u64) count << 2) | 0x3) * (divider + 1) * 1000; /* millicycles */
+ rem = do_div(n, CX25840_IR_REFCLK_FREQ / 1000000); /* / MHz => ns */
+ if (rem >= CX25840_IR_REFCLK_FREQ / 1000000 / 2)
+ n++;
+ return n;
+}
+
+#if 0
+/* Keep as we will need this for Transmit functionality */
+static u16 ns_to_pulse_width_count(u32 ns, u16 divider)
+{
+ u64 n;
+ u32 d;
+ u32 rem;
+
+ /*
+ * The 2 lsb's of the pulse width timer count are not accessible, hence
+ * the (1 << 2)
+ */
+ n = ((u64) ns) * CX25840_IR_REFCLK_FREQ / 1000000; /* millicycles */
+ d = (1 << 2) * ((u32) divider + 1) * 1000; /* millicycles/count */
+ rem = do_div(n, d);
+ if (rem >= d / 2)
+ n++;
+
+ if (n > FIFO_RXTX)
+ n = FIFO_RXTX;
+ else if (n == 0)
+ n = 1;
+ return (u16) n;
+}
+
+#endif
+static unsigned int pulse_width_count_to_us(u16 count, u16 divider)
+{
+ u64 n;
+ u32 rem;
+
+ /*
+ * The 2 lsb's of the pulse width timer count are not readable, hence
+ * the (count << 2) | 0x3
+ */
+ n = (((u64) count << 2) | 0x3) * (divider + 1); /* cycles */
+ rem = do_div(n, CX25840_IR_REFCLK_FREQ / 1000000); /* / MHz => us */
+ if (rem >= CX25840_IR_REFCLK_FREQ / 1000000 / 2)
+ n++;
+ return (unsigned int) n;
+}
+
+/*
+ * Pulse Clocks computations: Combined Pulse Width Count & Rx Clock Counts
+ *
+ * The total pulse clock count is an 18 bit pulse width timer count as the most
+ * significant part and (up to) 16 bit clock divider count as a modulus.
+ * When the Rx clock divider ticks down to 0, it increments the 18 bit pulse
+ * width timer count's least significant bit.
+ */
+static u64 ns_to_pulse_clocks(u32 ns)
+{
+ u64 clocks;
+ u32 rem;
+ clocks = CX25840_IR_REFCLK_FREQ / 1000000 * (u64) ns; /* millicycles */
+ rem = do_div(clocks, 1000); /* /1000 = cycles */
+ if (rem >= 1000 / 2)
+ clocks++;
+ return clocks;
+}
+
+static u16 pulse_clocks_to_clock_divider(u64 count)
+{
+ u32 rem;
+
+ rem = do_div(count, (FIFO_RXTX << 2) | 0x3);
+
+ /* net result needs to be rounded down and decremented by 1 */
+ if (count > RXCLK_RCD + 1)
+ count = RXCLK_RCD;
+ else if (count < 2)
+ count = 1;
+ else
+ count--;
+ return (u16) count;
+}
+
+/*
+ * IR Control Register helpers
+ */
+enum tx_fifo_watermark {
+ TX_FIFO_HALF_EMPTY = 0,
+ TX_FIFO_EMPTY = CNTRL_TIC,
+};
+
+enum rx_fifo_watermark {
+ RX_FIFO_HALF_FULL = 0,
+ RX_FIFO_NOT_EMPTY = CNTRL_RIC,
+};
+
+static inline void control_tx_irq_watermark(struct i2c_client *c,
+ enum tx_fifo_watermark level)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_TIC, level);
+}
+
+static inline void control_rx_irq_watermark(struct i2c_client *c,
+ enum rx_fifo_watermark level)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_RIC, level);
+}
+
+static inline void control_tx_enable(struct i2c_client *c, bool enable)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~(CNTRL_TXE | CNTRL_TFE),
+ enable ? (CNTRL_TXE | CNTRL_TFE) : 0);
+}
+
+static inline void control_rx_enable(struct i2c_client *c, bool enable)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~(CNTRL_RXE | CNTRL_RFE),
+ enable ? (CNTRL_RXE | CNTRL_RFE) : 0);
+}
+
+static inline void control_tx_modulation_enable(struct i2c_client *c,
+ bool enable)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_MOD,
+ enable ? CNTRL_MOD : 0);
+}
+
+static inline void control_rx_demodulation_enable(struct i2c_client *c,
+ bool enable)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_DMD,
+ enable ? CNTRL_DMD : 0);
+}
+
+static inline void control_rx_s_edge_detection(struct i2c_client *c,
+ u32 edge_types)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_EDG_BOTH,
+ edge_types & CNTRL_EDG_BOTH);
+}
+
+static void control_rx_s_carrier_window(struct i2c_client *c,
+ unsigned int carrier,
+ unsigned int *carrier_range_low,
+ unsigned int *carrier_range_high)
+{
+ u32 v;
+ unsigned int c16 = carrier * 16;
+
+ if (*carrier_range_low < DIV_ROUND_CLOSEST(c16, 16 + 3)) {
+ v = CNTRL_WIN_3_4;
+ *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 4);
+ } else {
+ v = CNTRL_WIN_3_3;
+ *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 3);
+ }
+
+ if (*carrier_range_high > DIV_ROUND_CLOSEST(c16, 16 - 3)) {
+ v |= CNTRL_WIN_4_3;
+ *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 4);
+ } else {
+ v |= CNTRL_WIN_3_3;
+ *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 3);
+ }
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_WIN, v);
+}
+
+static inline void control_tx_polarity_invert(struct i2c_client *c,
+ bool invert)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_CPL,
+ invert ? CNTRL_CPL : 0);
+}
+
+/*
+ * IR Rx & Tx Clock Register helpers
+ */
+static unsigned int txclk_tx_s_carrier(struct i2c_client *c,
+ unsigned int freq,
+ u16 *divider)
+{
+ *divider = carrier_freq_to_clock_divider(freq);
+ cx25840_write4(c, CX25840_IR_TXCLK_REG, *divider);
+ return clock_divider_to_carrier_freq(*divider);
+}
+
+static unsigned int rxclk_rx_s_carrier(struct i2c_client *c,
+ unsigned int freq,
+ u16 *divider)
+{
+ *divider = carrier_freq_to_clock_divider(freq);
+ cx25840_write4(c, CX25840_IR_RXCLK_REG, *divider);
+ return clock_divider_to_carrier_freq(*divider);
+}
+
+static u32 txclk_tx_s_max_pulse_width(struct i2c_client *c, u32 ns,
+ u16 *divider)
+{
+ u64 pulse_clocks;
+
+ if (ns > IR_MAX_DURATION)
+ ns = IR_MAX_DURATION;
+ pulse_clocks = ns_to_pulse_clocks(ns);
+ *divider = pulse_clocks_to_clock_divider(pulse_clocks);
+ cx25840_write4(c, CX25840_IR_TXCLK_REG, *divider);
+ return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider);
+}
+
+static u32 rxclk_rx_s_max_pulse_width(struct i2c_client *c, u32 ns,
+ u16 *divider)
+{
+ u64 pulse_clocks;
+
+ if (ns > IR_MAX_DURATION)
+ ns = IR_MAX_DURATION;
+ pulse_clocks = ns_to_pulse_clocks(ns);
+ *divider = pulse_clocks_to_clock_divider(pulse_clocks);
+ cx25840_write4(c, CX25840_IR_RXCLK_REG, *divider);
+ return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider);
+}
+
+/*
+ * IR Tx Carrier Duty Cycle register helpers
+ */
+static unsigned int cduty_tx_s_duty_cycle(struct i2c_client *c,
+ unsigned int duty_cycle)
+{
+ u32 n;
+ n = DIV_ROUND_CLOSEST(duty_cycle * 100, 625); /* 16ths of 100% */
+ if (n != 0)
+ n--;
+ if (n > 15)
+ n = 15;
+ cx25840_write4(c, CX25840_IR_CDUTY_REG, n);
+ return DIV_ROUND_CLOSEST((n + 1) * 100, 16);
+}
+
+/*
+ * IR Filter Register helpers
+ */
+static u32 filter_rx_s_min_width(struct i2c_client *c, u32 min_width_ns)
+{
+ u32 count = ns_to_lpf_count(min_width_ns);
+ cx25840_write4(c, CX25840_IR_FILTR_REG, count);
+ return lpf_count_to_ns(count);
+}
+
+/*
+ * IR IRQ Enable Register helpers
+ */
+static inline void irqenable_rx(struct v4l2_subdev *sd, u32 mask)
+{
+ struct cx25840_state *state = to_state(sd);
+
+ if (is_cx23885(state) || is_cx23887(state))
+ mask ^= IRQEN_MSK;
+ mask &= (IRQEN_RTE | IRQEN_ROE | IRQEN_RSE);
+ cx25840_and_or4(state->c, CX25840_IR_IRQEN_REG,
+ ~(IRQEN_RTE | IRQEN_ROE | IRQEN_RSE), mask);
+}
+
+static inline void irqenable_tx(struct v4l2_subdev *sd, u32 mask)
+{
+ struct cx25840_state *state = to_state(sd);
+
+ if (is_cx23885(state) || is_cx23887(state))
+ mask ^= IRQEN_MSK;
+ mask &= IRQEN_TSE;
+ cx25840_and_or4(state->c, CX25840_IR_IRQEN_REG, ~IRQEN_TSE, mask);
+}
+
+/*
+ * V4L2 Subdevice IR Ops
+ */
+int cx25840_ir_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+ struct i2c_client *c = NULL;
+ unsigned long flags;
+
+ union cx25840_ir_fifo_rec rx_data[FIFO_RX_DEPTH];
+ unsigned int i, j, k;
+ u32 events, v;
+ int tsr, rsr, rto, ror, tse, rse, rte, roe, kror;
+ u32 cntrl, irqen, stats;
+
+ *handled = false;
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ c = ir_state->c;
+
+ /* Only support the IR controller for the CX2388[57] AV Core for now */
+ if (!(is_cx23885(state) || is_cx23887(state)))
+ return -ENODEV;
+
+ cntrl = cx25840_read4(c, CX25840_IR_CNTRL_REG);
+ irqen = cx25840_read4(c, CX25840_IR_IRQEN_REG);
+ if (is_cx23885(state) || is_cx23887(state))
+ irqen ^= IRQEN_MSK;
+ stats = cx25840_read4(c, CX25840_IR_STATS_REG);
+
+ tsr = stats & STATS_TSR; /* Tx FIFO Service Request */
+ rsr = stats & STATS_RSR; /* Rx FIFO Service Request */
+ rto = stats & STATS_RTO; /* Rx Pulse Width Timer Time Out */
+ ror = stats & STATS_ROR; /* Rx FIFO Over Run */
+
+ tse = irqen & IRQEN_TSE; /* Tx FIFO Service Request IRQ Enable */
+ rse = irqen & IRQEN_RSE; /* Rx FIFO Service Reuqest IRQ Enable */
+ rte = irqen & IRQEN_RTE; /* Rx Pulse Width Timer Time Out IRQ Enable */
+ roe = irqen & IRQEN_ROE; /* Rx FIFO Over Run IRQ Enable */
+
+ v4l2_dbg(2, ir_debug, sd, "IR IRQ Status: %s %s %s %s %s %s\n",
+ tsr ? "tsr" : " ", rsr ? "rsr" : " ",
+ rto ? "rto" : " ", ror ? "ror" : " ",
+ stats & STATS_TBY ? "tby" : " ",
+ stats & STATS_RBY ? "rby" : " ");
+
+ v4l2_dbg(2, ir_debug, sd, "IR IRQ Enables: %s %s %s %s\n",
+ tse ? "tse" : " ", rse ? "rse" : " ",
+ rte ? "rte" : " ", roe ? "roe" : " ");
+
+ /*
+ * Transmitter interrupt service
+ */
+ if (tse && tsr) {
+ /*
+ * TODO:
+ * Check the watermark threshold setting
+ * Pull FIFO_TX_DEPTH or FIFO_TX_DEPTH/2 entries from tx_kfifo
+ * Push the data to the hardware FIFO.
+ * If there was nothing more to send in the tx_kfifo, disable
+ * the TSR IRQ and notify the v4l2_device.
+ * If there was something in the tx_kfifo, check the tx_kfifo
+ * level and notify the v4l2_device, if it is low.
+ */
+ /* For now, inhibit TSR interrupt until Tx is implemented */
+ irqenable_tx(sd, 0);
+ events = V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ;
+ v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_TX_NOTIFY, &events);
+ *handled = true;
+ }
+
+ /*
+ * Receiver interrupt service
+ */
+ kror = 0;
+ if ((rse && rsr) || (rte && rto)) {
+ /*
+ * Receive data on RSR to clear the STATS_RSR.
+ * Receive data on RTO, since we may not have yet hit the RSR
+ * watermark when we receive the RTO.
+ */
+ for (i = 0, v = FIFO_RX_NDV;
+ (v & FIFO_RX_NDV) && !kror; i = 0) {
+ for (j = 0;
+ (v & FIFO_RX_NDV) && j < FIFO_RX_DEPTH; j++) {
+ v = cx25840_read4(c, CX25840_IR_FIFO_REG);
+ rx_data[i].hw_fifo_data = v & ~FIFO_RX_NDV;
+ i++;
+ }
+ if (i == 0)
+ break;
+ j = i * sizeof(union cx25840_ir_fifo_rec);
+ k = kfifo_in_locked(&ir_state->rx_kfifo,
+ (unsigned char *) rx_data, j,
+ &ir_state->rx_kfifo_lock);
+ if (k != j)
+ kror++; /* rx_kfifo over run */
+ }
+ *handled = true;
+ }
+
+ events = 0;
+ v = 0;
+ if (kror) {
+ events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN;
+ v4l2_err(sd, "IR receiver software FIFO overrun\n");
+ }
+ if (roe && ror) {
+ /*
+ * The RX FIFO Enable (CNTRL_RFE) must be toggled to clear
+ * the Rx FIFO Over Run status (STATS_ROR)
+ */
+ v |= CNTRL_RFE;
+ events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN;
+ v4l2_err(sd, "IR receiver hardware FIFO overrun\n");
+ }
+ if (rte && rto) {
+ /*
+ * The IR Receiver Enable (CNTRL_RXE) must be toggled to clear
+ * the Rx Pulse Width Timer Time Out (STATS_RTO)
+ */
+ v |= CNTRL_RXE;
+ events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED;
+ }
+ if (v) {
+ /* Clear STATS_ROR & STATS_RTO as needed by reseting hardware */
+ cx25840_write4(c, CX25840_IR_CNTRL_REG, cntrl & ~v);
+ cx25840_write4(c, CX25840_IR_CNTRL_REG, cntrl);
+ *handled = true;
+ }
+ spin_lock_irqsave(&ir_state->rx_kfifo_lock, flags);
+ if (kfifo_len(&ir_state->rx_kfifo) >= CX25840_IR_RX_KFIFO_SIZE / 2)
+ events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ;
+ spin_unlock_irqrestore(&ir_state->rx_kfifo_lock, flags);
+
+ if (events)
+ v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_RX_NOTIFY, &events);
+ return 0;
+}
+
+/* Receiver */
+static int cx25840_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count,
+ ssize_t *num)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+ bool invert;
+ u16 divider;
+ unsigned int i, n;
+ union cx25840_ir_fifo_rec *p;
+ unsigned u, v, w;
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ invert = (bool) atomic_read(&ir_state->rx_invert);
+ divider = (u16) atomic_read(&ir_state->rxclk_divider);
+
+ n = count / sizeof(union cx25840_ir_fifo_rec)
+ * sizeof(union cx25840_ir_fifo_rec);
+ if (n == 0) {
+ *num = 0;
+ return 0;
+ }
+
+ n = kfifo_out_locked(&ir_state->rx_kfifo, buf, n,
+ &ir_state->rx_kfifo_lock);
+
+ n /= sizeof(union cx25840_ir_fifo_rec);
+ *num = n * sizeof(union cx25840_ir_fifo_rec);
+
+ for (p = (union cx25840_ir_fifo_rec *) buf, i = 0; i < n; p++, i++) {
+
+ if ((p->hw_fifo_data & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) {
+ /* Assume RTO was because of no IR light input */
+ u = 0;
+ w = 1;
+ } else {
+ u = (p->hw_fifo_data & FIFO_RXTX_LVL) ? 1 : 0;
+ if (invert)
+ u = u ? 0 : 1;
+ w = 0;
+ }
+
+ v = (unsigned) pulse_width_count_to_ns(
+ (u16) (p->hw_fifo_data & FIFO_RXTX), divider);
+ if (v > IR_MAX_DURATION)
+ v = IR_MAX_DURATION;
+
+ init_ir_raw_event(&p->ir_core_data);
+ p->ir_core_data.pulse = u;
+ p->ir_core_data.duration = v;
+ p->ir_core_data.timeout = w;
+
+ v4l2_dbg(2, ir_debug, sd, "rx read: %10u ns %s %s\n",
+ v, u ? "mark" : "space", w ? "(timed out)" : "");
+ if (w)
+ v4l2_dbg(2, ir_debug, sd, "rx read: end of rx\n");
+ }
+ return 0;
+}
+
+static int cx25840_ir_rx_g_parameters(struct v4l2_subdev *sd,
+ struct v4l2_subdev_ir_parameters *p)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ mutex_lock(&ir_state->rx_params_lock);
+ memcpy(p, &ir_state->rx_params,
+ sizeof(struct v4l2_subdev_ir_parameters));
+ mutex_unlock(&ir_state->rx_params_lock);
+ return 0;
+}
+
+static int cx25840_ir_rx_shutdown(struct v4l2_subdev *sd)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+ struct i2c_client *c;
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ c = ir_state->c;
+ mutex_lock(&ir_state->rx_params_lock);
+
+ /* Disable or slow down all IR Rx circuits and counters */
+ irqenable_rx(sd, 0);
+ control_rx_enable(c, false);
+ control_rx_demodulation_enable(c, false);
+ control_rx_s_edge_detection(c, CNTRL_EDG_NONE);
+ filter_rx_s_min_width(c, 0);
+ cx25840_write4(c, CX25840_IR_RXCLK_REG, RXCLK_RCD);
+
+ ir_state->rx_params.shutdown = true;
+
+ mutex_unlock(&ir_state->rx_params_lock);
+ return 0;
+}
+
+static int cx25840_ir_rx_s_parameters(struct v4l2_subdev *sd,
+ struct v4l2_subdev_ir_parameters *p)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+ struct i2c_client *c;
+ struct v4l2_subdev_ir_parameters *o;
+ u16 rxclk_divider;
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ if (p->shutdown)
+ return cx25840_ir_rx_shutdown(sd);
+
+ if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH)
+ return -ENOSYS;
+
+ c = ir_state->c;
+ o = &ir_state->rx_params;
+
+ mutex_lock(&ir_state->rx_params_lock);
+
+ o->shutdown = p->shutdown;
+
+ p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+ o->mode = p->mode;
+
+ p->bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec);
+ o->bytes_per_data_element = p->bytes_per_data_element;
+
+ /* Before we tweak the hardware, we have to disable the receiver */
+ irqenable_rx(sd, 0);
+ control_rx_enable(c, false);
+
+ control_rx_demodulation_enable(c, p->modulation);
+ o->modulation = p->modulation;
+
+ if (p->modulation) {
+ p->carrier_freq = rxclk_rx_s_carrier(c, p->carrier_freq,
+ &rxclk_divider);
+
+ o->carrier_freq = p->carrier_freq;
+
+ p->duty_cycle = 50;
+ o->duty_cycle = p->duty_cycle;
+
+ control_rx_s_carrier_window(c, p->carrier_freq,
+ &p->carrier_range_lower,
+ &p->carrier_range_upper);
+ o->carrier_range_lower = p->carrier_range_lower;
+ o->carrier_range_upper = p->carrier_range_upper;
+
+ p->max_pulse_width =
+ (u32) pulse_width_count_to_ns(FIFO_RXTX, rxclk_divider);
+ } else {
+ p->max_pulse_width =
+ rxclk_rx_s_max_pulse_width(c, p->max_pulse_width,
+ &rxclk_divider);
+ }
+ o->max_pulse_width = p->max_pulse_width;
+ atomic_set(&ir_state->rxclk_divider, rxclk_divider);
+
+ p->noise_filter_min_width =
+ filter_rx_s_min_width(c, p->noise_filter_min_width);
+ o->noise_filter_min_width = p->noise_filter_min_width;
+
+ p->resolution = clock_divider_to_resolution(rxclk_divider);
+ o->resolution = p->resolution;
+
+ /* FIXME - make this dependent on resolution for better performance */
+ control_rx_irq_watermark(c, RX_FIFO_HALF_FULL);
+
+ control_rx_s_edge_detection(c, CNTRL_EDG_BOTH);
+
+ o->invert_level = p->invert_level;
+ atomic_set(&ir_state->rx_invert, p->invert_level);
+
+ o->interrupt_enable = p->interrupt_enable;
+ o->enable = p->enable;
+ if (p->enable) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&ir_state->rx_kfifo_lock, flags);
+ kfifo_reset(&ir_state->rx_kfifo);
+ spin_unlock_irqrestore(&ir_state->rx_kfifo_lock, flags);
+ if (p->interrupt_enable)
+ irqenable_rx(sd, IRQEN_RSE | IRQEN_RTE | IRQEN_ROE);
+ control_rx_enable(c, p->enable);
+ }
+
+ mutex_unlock(&ir_state->rx_params_lock);
+ return 0;
+}
+
+/* Transmitter */
+static int cx25840_ir_tx_write(struct v4l2_subdev *sd, u8 *buf, size_t count,
+ ssize_t *num)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+ struct i2c_client *c;
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ c = ir_state->c;
+#if 0
+ /*
+ * FIXME - the code below is an incomplete and untested sketch of what
+ * may need to be done. The critical part is to get 4 (or 8) pulses
+ * from the tx_kfifo, or converted from ns to the proper units from the
+ * input, and push them off to the hardware Tx FIFO right away, if the
+ * HW TX fifo needs service. The rest can be pushed to the tx_kfifo in
+ * a less critical timeframe. Also watch out for overruning the
+ * tx_kfifo - don't let it happen and let the caller know not all his
+ * pulses were written.
+ */
+ u32 *ns_pulse = (u32 *) buf;
+ unsigned int n;
+ u32 fifo_pulse[FIFO_TX_DEPTH];
+ u32 mark;
+
+ /* Compute how much we can fit in the tx kfifo */
+ n = CX25840_IR_TX_KFIFO_SIZE - kfifo_len(ir_state->tx_kfifo);
+ n = min(n, (unsigned int) count);
+ n /= sizeof(u32);
+
+ /* FIXME - turn on Tx Fifo service interrupt
+ * check hardware fifo level, and other stuff
+ */
+ for (i = 0; i < n; ) {
+ for (j = 0; j < FIFO_TX_DEPTH / 2 && i < n; j++) {
+ mark = ns_pulse[i] & LEVEL_MASK;
+ fifo_pulse[j] = ns_to_pulse_width_count(
+ ns_pulse[i] &
+ ~LEVEL_MASK,
+ ir_state->txclk_divider);
+ if (mark)
+ fifo_pulse[j] &= FIFO_RXTX_LVL;
+ i++;
+ }
+ kfifo_put(ir_state->tx_kfifo, (u8 *) fifo_pulse,
+ j * sizeof(u32));
+ }
+ *num = n * sizeof(u32);
+#else
+ /* For now enable the Tx FIFO Service interrupt & pretend we did work */
+ irqenable_tx(sd, IRQEN_TSE);
+ *num = count;
+#endif
+ return 0;
+}
+
+static int cx25840_ir_tx_g_parameters(struct v4l2_subdev *sd,
+ struct v4l2_subdev_ir_parameters *p)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ mutex_lock(&ir_state->tx_params_lock);
+ memcpy(p, &ir_state->tx_params,
+ sizeof(struct v4l2_subdev_ir_parameters));
+ mutex_unlock(&ir_state->tx_params_lock);
+ return 0;
+}
+
+static int cx25840_ir_tx_shutdown(struct v4l2_subdev *sd)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+ struct i2c_client *c;
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ c = ir_state->c;
+ mutex_lock(&ir_state->tx_params_lock);
+
+ /* Disable or slow down all IR Tx circuits and counters */
+ irqenable_tx(sd, 0);
+ control_tx_enable(c, false);
+ control_tx_modulation_enable(c, false);
+ cx25840_write4(c, CX25840_IR_TXCLK_REG, TXCLK_TCD);
+
+ ir_state->tx_params.shutdown = true;
+
+ mutex_unlock(&ir_state->tx_params_lock);
+ return 0;
+}
+
+static int cx25840_ir_tx_s_parameters(struct v4l2_subdev *sd,
+ struct v4l2_subdev_ir_parameters *p)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+ struct i2c_client *c;
+ struct v4l2_subdev_ir_parameters *o;
+ u16 txclk_divider;
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ if (p->shutdown)
+ return cx25840_ir_tx_shutdown(sd);
+
+ if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH)
+ return -ENOSYS;
+
+ c = ir_state->c;
+ o = &ir_state->tx_params;
+ mutex_lock(&ir_state->tx_params_lock);
+
+ o->shutdown = p->shutdown;
+
+ p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+ o->mode = p->mode;
+
+ p->bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec);
+ o->bytes_per_data_element = p->bytes_per_data_element;
+
+ /* Before we tweak the hardware, we have to disable the transmitter */
+ irqenable_tx(sd, 0);
+ control_tx_enable(c, false);
+
+ control_tx_modulation_enable(c, p->modulation);
+ o->modulation = p->modulation;
+
+ if (p->modulation) {
+ p->carrier_freq = txclk_tx_s_carrier(c, p->carrier_freq,
+ &txclk_divider);
+ o->carrier_freq = p->carrier_freq;
+
+ p->duty_cycle = cduty_tx_s_duty_cycle(c, p->duty_cycle);
+ o->duty_cycle = p->duty_cycle;
+
+ p->max_pulse_width =
+ (u32) pulse_width_count_to_ns(FIFO_RXTX, txclk_divider);
+ } else {
+ p->max_pulse_width =
+ txclk_tx_s_max_pulse_width(c, p->max_pulse_width,
+ &txclk_divider);
+ }
+ o->max_pulse_width = p->max_pulse_width;
+ atomic_set(&ir_state->txclk_divider, txclk_divider);
+
+ p->resolution = clock_divider_to_resolution(txclk_divider);
+ o->resolution = p->resolution;
+
+ /* FIXME - make this dependent on resolution for better performance */
+ control_tx_irq_watermark(c, TX_FIFO_HALF_EMPTY);
+
+ control_tx_polarity_invert(c, p->invert_carrier_sense);
+ o->invert_carrier_sense = p->invert_carrier_sense;
+
+ /*
+ * FIXME: we don't have hardware help for IO pin level inversion
+ * here like we have on the CX23888.
+ * Act on this with some mix of logical inversion of data levels,
+ * carrier polarity, and carrier duty cycle.
+ */
+ o->invert_level = p->invert_level;
+
+ o->interrupt_enable = p->interrupt_enable;
+ o->enable = p->enable;
+ if (p->enable) {
+ /* reset tx_fifo here */
+ if (p->interrupt_enable)
+ irqenable_tx(sd, IRQEN_TSE);
+ control_tx_enable(c, p->enable);
+ }
+
+ mutex_unlock(&ir_state->tx_params_lock);
+ return 0;
+}
+
+
+/*
+ * V4L2 Subdevice Core Ops support
+ */
+int cx25840_ir_log_status(struct v4l2_subdev *sd)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *c = state->c;
+ char *s;
+ int i, j;
+ u32 cntrl, txclk, rxclk, cduty, stats, irqen, filtr;
+
+ /* The CX23888 chip doesn't have an IR controller on the A/V core */
+ if (is_cx23888(state))
+ return 0;
+
+ cntrl = cx25840_read4(c, CX25840_IR_CNTRL_REG);
+ txclk = cx25840_read4(c, CX25840_IR_TXCLK_REG) & TXCLK_TCD;
+ rxclk = cx25840_read4(c, CX25840_IR_RXCLK_REG) & RXCLK_RCD;
+ cduty = cx25840_read4(c, CX25840_IR_CDUTY_REG) & CDUTY_CDC;
+ stats = cx25840_read4(c, CX25840_IR_STATS_REG);
+ irqen = cx25840_read4(c, CX25840_IR_IRQEN_REG);
+ if (is_cx23885(state) || is_cx23887(state))
+ irqen ^= IRQEN_MSK;
+ filtr = cx25840_read4(c, CX25840_IR_FILTR_REG) & FILTR_LPF;
+
+ v4l2_info(sd, "IR Receiver:\n");
+ v4l2_info(sd, "\tEnabled: %s\n",
+ cntrl & CNTRL_RXE ? "yes" : "no");
+ v4l2_info(sd, "\tDemodulation from a carrier: %s\n",
+ cntrl & CNTRL_DMD ? "enabled" : "disabled");
+ v4l2_info(sd, "\tFIFO: %s\n",
+ cntrl & CNTRL_RFE ? "enabled" : "disabled");
+ switch (cntrl & CNTRL_EDG) {
+ case CNTRL_EDG_NONE:
+ s = "disabled";
+ break;
+ case CNTRL_EDG_FALL:
+ s = "falling edge";
+ break;
+ case CNTRL_EDG_RISE:
+ s = "rising edge";
+ break;
+ case CNTRL_EDG_BOTH:
+ s = "rising & falling edges";
+ break;
+ default:
+ s = "??? edge";
+ break;
+ }
+ v4l2_info(sd, "\tPulse timers' start/stop trigger: %s\n", s);
+ v4l2_info(sd, "\tFIFO data on pulse timer overflow: %s\n",
+ cntrl & CNTRL_R ? "not loaded" : "overflow marker");
+ v4l2_info(sd, "\tFIFO interrupt watermark: %s\n",
+ cntrl & CNTRL_RIC ? "not empty" : "half full or greater");
+ v4l2_info(sd, "\tLoopback mode: %s\n",
+ cntrl & CNTRL_LBM ? "loopback active" : "normal receive");
+ if (cntrl & CNTRL_DMD) {
+ v4l2_info(sd, "\tExpected carrier (16 clocks): %u Hz\n",
+ clock_divider_to_carrier_freq(rxclk));
+ switch (cntrl & CNTRL_WIN) {
+ case CNTRL_WIN_3_3:
+ i = 3;
+ j = 3;
+ break;
+ case CNTRL_WIN_4_3:
+ i = 4;
+ j = 3;
+ break;
+ case CNTRL_WIN_3_4:
+ i = 3;
+ j = 4;
+ break;
+ case CNTRL_WIN_4_4:
+ i = 4;
+ j = 4;
+ break;
+ default:
+ i = 0;
+ j = 0;
+ break;
+ }
+ v4l2_info(sd, "\tNext carrier edge window: 16 clocks "
+ "-%1d/+%1d, %u to %u Hz\n", i, j,
+ clock_divider_to_freq(rxclk, 16 + j),
+ clock_divider_to_freq(rxclk, 16 - i));
+ }
+ v4l2_info(sd, "\tMax measurable pulse width: %u us, %llu ns\n",
+ pulse_width_count_to_us(FIFO_RXTX, rxclk),
+ pulse_width_count_to_ns(FIFO_RXTX, rxclk));
+ v4l2_info(sd, "\tLow pass filter: %s\n",
+ filtr ? "enabled" : "disabled");
+ if (filtr)
+ v4l2_info(sd, "\tMin acceptable pulse width (LPF): %u us, "
+ "%u ns\n",
+ lpf_count_to_us(filtr),
+ lpf_count_to_ns(filtr));
+ v4l2_info(sd, "\tPulse width timer timed-out: %s\n",
+ stats & STATS_RTO ? "yes" : "no");
+ v4l2_info(sd, "\tPulse width timer time-out intr: %s\n",
+ irqen & IRQEN_RTE ? "enabled" : "disabled");
+ v4l2_info(sd, "\tFIFO overrun: %s\n",
+ stats & STATS_ROR ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO overrun interrupt: %s\n",
+ irqen & IRQEN_ROE ? "enabled" : "disabled");
+ v4l2_info(sd, "\tBusy: %s\n",
+ stats & STATS_RBY ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO service requested: %s\n",
+ stats & STATS_RSR ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO service request interrupt: %s\n",
+ irqen & IRQEN_RSE ? "enabled" : "disabled");
+
+ v4l2_info(sd, "IR Transmitter:\n");
+ v4l2_info(sd, "\tEnabled: %s\n",
+ cntrl & CNTRL_TXE ? "yes" : "no");
+ v4l2_info(sd, "\tModulation onto a carrier: %s\n",
+ cntrl & CNTRL_MOD ? "enabled" : "disabled");
+ v4l2_info(sd, "\tFIFO: %s\n",
+ cntrl & CNTRL_TFE ? "enabled" : "disabled");
+ v4l2_info(sd, "\tFIFO interrupt watermark: %s\n",
+ cntrl & CNTRL_TIC ? "not empty" : "half full or less");
+ v4l2_info(sd, "\tCarrier polarity: %s\n",
+ cntrl & CNTRL_CPL ? "space:burst mark:noburst"
+ : "space:noburst mark:burst");
+ if (cntrl & CNTRL_MOD) {
+ v4l2_info(sd, "\tCarrier (16 clocks): %u Hz\n",
+ clock_divider_to_carrier_freq(txclk));
+ v4l2_info(sd, "\tCarrier duty cycle: %2u/16\n",
+ cduty + 1);
+ }
+ v4l2_info(sd, "\tMax pulse width: %u us, %llu ns\n",
+ pulse_width_count_to_us(FIFO_RXTX, txclk),
+ pulse_width_count_to_ns(FIFO_RXTX, txclk));
+ v4l2_info(sd, "\tBusy: %s\n",
+ stats & STATS_TBY ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO service requested: %s\n",
+ stats & STATS_TSR ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO service request interrupt: %s\n",
+ irqen & IRQEN_TSE ? "enabled" : "disabled");
+
+ return 0;
+}
+
+
+const struct v4l2_subdev_ir_ops cx25840_ir_ops = {
+ .rx_read = cx25840_ir_rx_read,
+ .rx_g_parameters = cx25840_ir_rx_g_parameters,
+ .rx_s_parameters = cx25840_ir_rx_s_parameters,
+
+ .tx_write = cx25840_ir_tx_write,
+ .tx_g_parameters = cx25840_ir_tx_g_parameters,
+ .tx_s_parameters = cx25840_ir_tx_s_parameters,
+};
+
+
+static const struct v4l2_subdev_ir_parameters default_rx_params = {
+ .bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec),
+ .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
+
+ .enable = false,
+ .interrupt_enable = false,
+ .shutdown = true,
+
+ .modulation = true,
+ .carrier_freq = 36000, /* 36 kHz - RC-5, and RC-6 carrier */
+
+ /* RC-5: 666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */
+ /* RC-6: 333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */
+ .noise_filter_min_width = 333333, /* ns */
+ .carrier_range_lower = 35000,
+ .carrier_range_upper = 37000,
+ .invert_level = false,
+};
+
+static const struct v4l2_subdev_ir_parameters default_tx_params = {
+ .bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec),
+ .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
+
+ .enable = false,
+ .interrupt_enable = false,
+ .shutdown = true,
+
+ .modulation = true,
+ .carrier_freq = 36000, /* 36 kHz - RC-5 carrier */
+ .duty_cycle = 25, /* 25 % - RC-5 carrier */
+ .invert_level = false,
+ .invert_carrier_sense = false,
+};
+
+int cx25840_ir_probe(struct v4l2_subdev *sd)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct cx25840_ir_state *ir_state;
+ struct v4l2_subdev_ir_parameters default_params;
+
+ /* Only init the IR controller for the CX2388[57] AV Core for now */
+ if (!(is_cx23885(state) || is_cx23887(state)))
+ return 0;
+
+ ir_state = kzalloc(sizeof(struct cx25840_ir_state), GFP_KERNEL);
+ if (ir_state == NULL)
+ return -ENOMEM;
+
+ spin_lock_init(&ir_state->rx_kfifo_lock);
+ if (kfifo_alloc(&ir_state->rx_kfifo,
+ CX25840_IR_RX_KFIFO_SIZE, GFP_KERNEL)) {
+ kfree(ir_state);
+ return -ENOMEM;
+ }
+
+ ir_state->c = state->c;
+ state->ir_state = ir_state;
+
+ /* Ensure no interrupts arrive yet */
+ if (is_cx23885(state) || is_cx23887(state))
+ cx25840_write4(ir_state->c, CX25840_IR_IRQEN_REG, IRQEN_MSK);
+ else
+ cx25840_write4(ir_state->c, CX25840_IR_IRQEN_REG, 0);
+
+ mutex_init(&ir_state->rx_params_lock);
+ memcpy(&default_params, &default_rx_params,
+ sizeof(struct v4l2_subdev_ir_parameters));
+ v4l2_subdev_call(sd, ir, rx_s_parameters, &default_params);
+
+ mutex_init(&ir_state->tx_params_lock);
+ memcpy(&default_params, &default_tx_params,
+ sizeof(struct v4l2_subdev_ir_parameters));
+ v4l2_subdev_call(sd, ir, tx_s_parameters, &default_params);
+
+ return 0;
+}
+
+int cx25840_ir_remove(struct v4l2_subdev *sd)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ cx25840_ir_rx_shutdown(sd);
+ cx25840_ir_tx_shutdown(sd);
+
+ kfifo_free(&ir_state->rx_kfifo);
+ kfree(ir_state);
+ state->ir_state = NULL;
+ return 0;
+}
diff --git a/drivers/media/video/cx25840/cx25840-vbi.c b/drivers/media/video/cx25840/cx25840-vbi.c
new file mode 100644
index 00000000..64a4004f
--- /dev/null
+++ b/drivers/media/video/cx25840/cx25840-vbi.c
@@ -0,0 +1,256 @@
+/* cx25840 VBI functions
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+#include <media/v4l2-common.h>
+#include <media/cx25840.h>
+
+#include "cx25840-core.h"
+
+static int odd_parity(u8 c)
+{
+ c ^= (c >> 4);
+ c ^= (c >> 2);
+ c ^= (c >> 1);
+
+ return c & 1;
+}
+
+static int decode_vps(u8 * dst, u8 * p)
+{
+ static const u8 biphase_tbl[] = {
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
+ 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
+ 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
+ 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
+ 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
+ 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87,
+ 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3,
+ 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85,
+ 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1,
+ 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
+ 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
+ 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
+ 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
+ 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86,
+ 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2,
+ 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84,
+ 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0,
+ 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
+ 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
+ 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
+ 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
+ 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ };
+
+ u8 c, err = 0;
+ int i;
+
+ for (i = 0; i < 2 * 13; i += 2) {
+ err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]];
+ c = (biphase_tbl[p[i + 1]] & 0xf) |
+ ((biphase_tbl[p[i]] & 0xf) << 4);
+ dst[i / 2] = c;
+ }
+
+ return err & 0xf0;
+}
+
+int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct cx25840_state *state = to_state(sd);
+ static const u16 lcr2vbi[] = {
+ 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */
+ 0, V4L2_SLICED_WSS_625, 0, /* 4 */
+ V4L2_SLICED_CAPTION_525, /* 6 */
+ 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */
+ 0, 0, 0, 0
+ };
+ int is_pal = !(state->std & V4L2_STD_525_60);
+ int i;
+
+ memset(svbi, 0, sizeof(*svbi));
+ /* we're done if raw VBI is active */
+ if ((cx25840_read(client, 0x404) & 0x10) == 0)
+ return 0;
+
+ if (is_pal) {
+ for (i = 7; i <= 23; i++) {
+ u8 v = cx25840_read(client, 0x424 + i - 7);
+
+ svbi->service_lines[0][i] = lcr2vbi[v >> 4];
+ svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
+ svbi->service_set |= svbi->service_lines[0][i] |
+ svbi->service_lines[1][i];
+ }
+ } else {
+ for (i = 10; i <= 21; i++) {
+ u8 v = cx25840_read(client, 0x424 + i - 10);
+
+ svbi->service_lines[0][i] = lcr2vbi[v >> 4];
+ svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
+ svbi->service_set |= svbi->service_lines[0][i] |
+ svbi->service_lines[1][i];
+ }
+ }
+ return 0;
+}
+
+int cx25840_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct cx25840_state *state = to_state(sd);
+ int is_pal = !(state->std & V4L2_STD_525_60);
+ int vbi_offset = is_pal ? 1 : 0;
+
+ /* Setup standard */
+ cx25840_std_setup(client);
+
+ /* VBI Offset */
+ cx25840_write(client, 0x47f, vbi_offset);
+ cx25840_write(client, 0x404, 0x2e);
+ return 0;
+}
+
+int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct cx25840_state *state = to_state(sd);
+ int is_pal = !(state->std & V4L2_STD_525_60);
+ int vbi_offset = is_pal ? 1 : 0;
+ int i, x;
+ u8 lcr[24];
+
+ for (x = 0; x <= 23; x++)
+ lcr[x] = 0x00;
+
+ /* Setup standard */
+ cx25840_std_setup(client);
+
+ /* Sliced VBI */
+ cx25840_write(client, 0x404, 0x32); /* Ancillary data */
+ cx25840_write(client, 0x406, 0x13);
+ cx25840_write(client, 0x47f, vbi_offset);
+
+ if (is_pal) {
+ for (i = 0; i <= 6; i++)
+ svbi->service_lines[0][i] =
+ svbi->service_lines[1][i] = 0;
+ } else {
+ for (i = 0; i <= 9; i++)
+ svbi->service_lines[0][i] =
+ svbi->service_lines[1][i] = 0;
+
+ for (i = 22; i <= 23; i++)
+ svbi->service_lines[0][i] =
+ svbi->service_lines[1][i] = 0;
+ }
+
+ for (i = 7; i <= 23; i++) {
+ for (x = 0; x <= 1; x++) {
+ switch (svbi->service_lines[1-x][i]) {
+ case V4L2_SLICED_TELETEXT_B:
+ lcr[i] |= 1 << (4 * x);
+ break;
+ case V4L2_SLICED_WSS_625:
+ lcr[i] |= 4 << (4 * x);
+ break;
+ case V4L2_SLICED_CAPTION_525:
+ lcr[i] |= 6 << (4 * x);
+ break;
+ case V4L2_SLICED_VPS:
+ lcr[i] |= 9 << (4 * x);
+ break;
+ }
+ }
+ }
+
+ if (is_pal) {
+ for (x = 1, i = 0x424; i <= 0x434; i++, x++)
+ cx25840_write(client, i, lcr[6 + x]);
+ } else {
+ for (x = 1, i = 0x424; i <= 0x430; i++, x++)
+ cx25840_write(client, i, lcr[9 + x]);
+ for (i = 0x431; i <= 0x434; i++)
+ cx25840_write(client, i, 0);
+ }
+
+ cx25840_write(client, 0x43c, 0x16);
+ cx25840_write(client, 0x474, is_pal ? 0x2a : 0x22);
+ return 0;
+}
+
+int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi)
+{
+ struct cx25840_state *state = to_state(sd);
+ u8 *p = vbi->p;
+ int id1, id2, l, err = 0;
+
+ if (p[0] || p[1] != 0xff || p[2] != 0xff ||
+ (p[3] != 0x55 && p[3] != 0x91)) {
+ vbi->line = vbi->type = 0;
+ return 0;
+ }
+
+ p += 4;
+ id1 = p[-1];
+ id2 = p[0] & 0xf;
+ l = p[2] & 0x3f;
+ l += state->vbi_line_offset;
+ p += 4;
+
+ switch (id2) {
+ case 1:
+ id2 = V4L2_SLICED_TELETEXT_B;
+ break;
+ case 4:
+ id2 = V4L2_SLICED_WSS_625;
+ break;
+ case 6:
+ id2 = V4L2_SLICED_CAPTION_525;
+ err = !odd_parity(p[0]) || !odd_parity(p[1]);
+ break;
+ case 9:
+ id2 = V4L2_SLICED_VPS;
+ if (decode_vps(p, p) != 0)
+ err = 1;
+ break;
+ default:
+ id2 = 0;
+ err = 1;
+ break;
+ }
+
+ vbi->type = err ? 0 : id2;
+ vbi->line = err ? 0 : l;
+ vbi->is_second_field = err ? 0 : (id1 == 0x55);
+ vbi->p = p;
+ return 0;
+}
diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig
new file mode 100644
index 00000000..3598dc08
--- /dev/null
+++ b/drivers/media/video/cx88/Kconfig
@@ -0,0 +1,86 @@
+config VIDEO_CX88
+ tristate "Conexant 2388x (bt878 successor) support"
+ depends on VIDEO_DEV && PCI && I2C && RC_CORE
+ select I2C_ALGOBIT
+ select VIDEO_BTCX
+ select VIDEOBUF_DMA_SG
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ select VIDEO_WM8775 if VIDEO_HELPER_CHIPS_AUTO
+ ---help---
+ This is a video4linux driver for Conexant 2388x based
+ TV cards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cx8800
+
+config VIDEO_CX88_ALSA
+ tristate "Conexant 2388x DMA audio support"
+ depends on VIDEO_CX88 && SND
+ select SND_PCM
+ ---help---
+ This is a video4linux driver for direct (DMA) audio on
+ Conexant 2388x based TV cards using ALSA.
+
+ It only works with boards with function 01 enabled.
+ To check if your board supports, use lspci -n.
+ If supported, you should see 14f1:8801 or 14f1:8811
+ PCI device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cx88-alsa.
+
+config VIDEO_CX88_BLACKBIRD
+ tristate "Blackbird MPEG encoder support (cx2388x + cx23416)"
+ depends on VIDEO_CX88
+ select VIDEO_CX2341X
+ ---help---
+ This adds support for MPEG encoder cards based on the
+ Blackbird reference design, using the Conexant 2388x
+ and 23416 chips.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cx88-blackbird.
+
+config VIDEO_CX88_DVB
+ tristate "DVB/ATSC Support for cx2388x based TV cards"
+ depends on VIDEO_CX88 && DVB_CORE
+ select VIDEOBUF_DVB
+ select DVB_PLL if !DVB_FE_CUSTOMISE
+ select DVB_MT352 if !DVB_FE_CUSTOMISE
+ select DVB_ZL10353 if !DVB_FE_CUSTOMISE
+ select DVB_OR51132 if !DVB_FE_CUSTOMISE
+ select DVB_CX22702 if !DVB_FE_CUSTOMISE
+ select DVB_LGDT330X if !DVB_FE_CUSTOMISE
+ select DVB_NXT200X if !DVB_FE_CUSTOMISE
+ select DVB_CX24123 if !DVB_FE_CUSTOMISE
+ select DVB_ISL6421 if !DVB_FE_CUSTOMISE
+ select DVB_S5H1411 if !DVB_FE_CUSTOMISE
+ select DVB_CX24116 if !DVB_FE_CUSTOMISE
+ select DVB_STV0299 if !DVB_FE_CUSTOMISE
+ select DVB_STV0288 if !DVB_FE_CUSTOMISE
+ select DVB_STB6000 if !DVB_FE_CUSTOMISE
+ select DVB_STV0900 if !DVB_FE_CUSTOMISE
+ select DVB_STB6100 if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE
+ ---help---
+ This adds support for DVB/ATSC cards based on the
+ Conexant 2388x chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cx88-dvb.
+
+config VIDEO_CX88_VP3054
+ tristate "VP-3054 Secondary I2C Bus Support"
+ default m
+ depends on VIDEO_CX88_DVB && DVB_MT352
+ ---help---
+ This adds DVB-T support for cards based on the
+ Conexant 2388x chip and the MT352 demodulator,
+ which also require support for the VP-3054
+ Secondary I2C bus, such at DNTV Live! DVB-T Pro.
+
+config VIDEO_CX88_MPEG
+ tristate
+ depends on VIDEO_CX88_DVB || VIDEO_CX88_BLACKBIRD
+ default y
diff --git a/drivers/media/video/cx88/Makefile b/drivers/media/video/cx88/Makefile
new file mode 100644
index 00000000..c1a2785b
--- /dev/null
+++ b/drivers/media/video/cx88/Makefile
@@ -0,0 +1,16 @@
+cx88xx-objs := cx88-cards.o cx88-core.o cx88-i2c.o cx88-tvaudio.o \
+ cx88-dsp.o cx88-input.o
+cx8800-objs := cx88-video.o cx88-vbi.o
+cx8802-objs := cx88-mpeg.o
+
+obj-$(CONFIG_VIDEO_CX88) += cx88xx.o cx8800.o
+obj-$(CONFIG_VIDEO_CX88_MPEG) += cx8802.o
+obj-$(CONFIG_VIDEO_CX88_ALSA) += cx88-alsa.o
+obj-$(CONFIG_VIDEO_CX88_BLACKBIRD) += cx88-blackbird.o
+obj-$(CONFIG_VIDEO_CX88_DVB) += cx88-dvb.o
+obj-$(CONFIG_VIDEO_CX88_VP3054) += cx88-vp3054-i2c.o
+
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c
new file mode 100644
index 00000000..04bf6627
--- /dev/null
+++ b/drivers/media/video/cx88/cx88-alsa.c
@@ -0,0 +1,992 @@
+/*
+ *
+ * Support for audio capture
+ * PCI function #1 of the cx2388x.
+ *
+ * (c) 2007 Trent Piepho <xyzzy@speakeasy.org>
+ * (c) 2005,2006 Ricardo Cerqueira <v4l@cerqueira.org>
+ * (c) 2005 Mauro Carvalho Chehab <mchehab@infradead.org>
+ * Based on a dummy cx88 module by Gerd Knorr <kraxel@bytesex.org>
+ * Based on dummy.c by Jaroslav Kysela <perex@perex.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/vmalloc.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include <asm/delay.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <media/wm8775.h>
+
+#include "cx88.h"
+#include "cx88-reg.h"
+
+#define dprintk(level,fmt, arg...) if (debug >= level) \
+ printk(KERN_INFO "%s/1: " fmt, chip->core->name , ## arg)
+
+#define dprintk_core(level,fmt, arg...) if (debug >= level) \
+ printk(KERN_DEBUG "%s/1: " fmt, chip->core->name , ## arg)
+
+/****************************************************************************
+ Data type declarations - Can be moded to a header file later
+ ****************************************************************************/
+
+struct cx88_audio_buffer {
+ unsigned int bpl;
+ struct btcx_riscmem risc;
+ struct videobuf_dmabuf dma;
+};
+
+struct cx88_audio_dev {
+ struct cx88_core *core;
+ struct cx88_dmaqueue q;
+
+ /* pci i/o */
+ struct pci_dev *pci;
+
+ /* audio controls */
+ int irq;
+
+ struct snd_card *card;
+
+ spinlock_t reg_lock;
+ atomic_t count;
+
+ unsigned int dma_size;
+ unsigned int period_size;
+ unsigned int num_periods;
+
+ struct videobuf_dmabuf *dma_risc;
+
+ struct cx88_audio_buffer *buf;
+
+ struct snd_pcm_substream *substream;
+};
+typedef struct cx88_audio_dev snd_cx88_card_t;
+
+
+
+/****************************************************************************
+ Module global static vars
+ ****************************************************************************/
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static const char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1};
+
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable cx88x soundcard. default enabled.");
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for cx88x capture interface(s).");
+
+
+/****************************************************************************
+ Module macros
+ ****************************************************************************/
+
+MODULE_DESCRIPTION("ALSA driver module for cx2388x based TV cards");
+MODULE_AUTHOR("Ricardo Cerqueira");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(CX88_VERSION);
+
+MODULE_SUPPORTED_DEVICE("{{Conexant,23881},"
+ "{{Conexant,23882},"
+ "{{Conexant,23883}");
+static unsigned int debug;
+module_param(debug,int,0644);
+MODULE_PARM_DESC(debug,"enable debug messages");
+
+/****************************************************************************
+ Module specific funtions
+ ****************************************************************************/
+
+/*
+ * BOARD Specific: Sets audio DMA
+ */
+
+static int _cx88_start_audio_dma(snd_cx88_card_t *chip)
+{
+ struct cx88_audio_buffer *buf = chip->buf;
+ struct cx88_core *core=chip->core;
+ const struct sram_channel *audio_ch = &cx88_sram_channels[SRAM_CH25];
+
+ /* Make sure RISC/FIFO are off before changing FIFO/RISC settings */
+ cx_clear(MO_AUD_DMACNTRL, 0x11);
+
+ /* setup fifo + format - out channel */
+ cx88_sram_channel_setup(chip->core, audio_ch, buf->bpl, buf->risc.dma);
+
+ /* sets bpl size */
+ cx_write(MO_AUDD_LNGTH, buf->bpl);
+
+ /* reset counter */
+ cx_write(MO_AUDD_GPCNTRL, GP_COUNT_CONTROL_RESET);
+ atomic_set(&chip->count, 0);
+
+ dprintk(1, "Start audio DMA, %d B/line, %d lines/FIFO, %d periods, %d "
+ "byte buffer\n", buf->bpl, cx_read(audio_ch->cmds_start + 8)>>1,
+ chip->num_periods, buf->bpl * chip->num_periods);
+
+ /* Enables corresponding bits at AUD_INT_STAT */
+ cx_write(MO_AUD_INTMSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC |
+ AUD_INT_DN_RISCI2 | AUD_INT_DN_RISCI1);
+
+ /* Clean any pending interrupt bits already set */
+ cx_write(MO_AUD_INTSTAT, ~0);
+
+ /* enable audio irqs */
+ cx_set(MO_PCI_INTMSK, chip->core->pci_irqmask | PCI_INT_AUDINT);
+
+ /* start dma */
+ cx_set(MO_DEV_CNTRL2, (1<<5)); /* Enables Risc Processor */
+ cx_set(MO_AUD_DMACNTRL, 0x11); /* audio downstream FIFO and RISC enable */
+
+ if (debug)
+ cx88_sram_channel_dump(chip->core, audio_ch);
+
+ return 0;
+}
+
+/*
+ * BOARD Specific: Resets audio DMA
+ */
+static int _cx88_stop_audio_dma(snd_cx88_card_t *chip)
+{
+ struct cx88_core *core=chip->core;
+ dprintk(1, "Stopping audio DMA\n");
+
+ /* stop dma */
+ cx_clear(MO_AUD_DMACNTRL, 0x11);
+
+ /* disable irqs */
+ cx_clear(MO_PCI_INTMSK, PCI_INT_AUDINT);
+ cx_clear(MO_AUD_INTMSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC |
+ AUD_INT_DN_RISCI2 | AUD_INT_DN_RISCI1);
+
+ if (debug)
+ cx88_sram_channel_dump(chip->core, &cx88_sram_channels[SRAM_CH25]);
+
+ return 0;
+}
+
+#define MAX_IRQ_LOOP 50
+
+/*
+ * BOARD Specific: IRQ dma bits
+ */
+static const char *cx88_aud_irqs[32] = {
+ "dn_risci1", "up_risci1", "rds_dn_risc1", /* 0-2 */
+ NULL, /* reserved */
+ "dn_risci2", "up_risci2", "rds_dn_risc2", /* 4-6 */
+ NULL, /* reserved */
+ "dnf_of", "upf_uf", "rds_dnf_uf", /* 8-10 */
+ NULL, /* reserved */
+ "dn_sync", "up_sync", "rds_dn_sync", /* 12-14 */
+ NULL, /* reserved */
+ "opc_err", "par_err", "rip_err", /* 16-18 */
+ "pci_abort", "ber_irq", "mchg_irq" /* 19-21 */
+};
+
+/*
+ * BOARD Specific: Threats IRQ audio specific calls
+ */
+static void cx8801_aud_irq(snd_cx88_card_t *chip)
+{
+ struct cx88_core *core = chip->core;
+ u32 status, mask;
+
+ status = cx_read(MO_AUD_INTSTAT);
+ mask = cx_read(MO_AUD_INTMSK);
+ if (0 == (status & mask))
+ return;
+ cx_write(MO_AUD_INTSTAT, status);
+ if (debug > 1 || (status & mask & ~0xff))
+ cx88_print_irqbits(core->name, "irq aud",
+ cx88_aud_irqs, ARRAY_SIZE(cx88_aud_irqs),
+ status, mask);
+ /* risc op code error */
+ if (status & AUD_INT_OPC_ERR) {
+ printk(KERN_WARNING "%s/1: Audio risc op code error\n",core->name);
+ cx_clear(MO_AUD_DMACNTRL, 0x11);
+ cx88_sram_channel_dump(core, &cx88_sram_channels[SRAM_CH25]);
+ }
+ if (status & AUD_INT_DN_SYNC) {
+ dprintk(1, "Downstream sync error\n");
+ cx_write(MO_AUDD_GPCNTRL, GP_COUNT_CONTROL_RESET);
+ return;
+ }
+ /* risc1 downstream */
+ if (status & AUD_INT_DN_RISCI1) {
+ atomic_set(&chip->count, cx_read(MO_AUDD_GPCNT));
+ snd_pcm_period_elapsed(chip->substream);
+ }
+ /* FIXME: Any other status should deserve a special handling? */
+}
+
+/*
+ * BOARD Specific: Handles IRQ calls
+ */
+static irqreturn_t cx8801_irq(int irq, void *dev_id)
+{
+ snd_cx88_card_t *chip = dev_id;
+ struct cx88_core *core = chip->core;
+ u32 status;
+ int loop, handled = 0;
+
+ for (loop = 0; loop < MAX_IRQ_LOOP; loop++) {
+ status = cx_read(MO_PCI_INTSTAT) &
+ (core->pci_irqmask | PCI_INT_AUDINT);
+ if (0 == status)
+ goto out;
+ dprintk(3, "cx8801_irq loop %d/%d, status %x\n",
+ loop, MAX_IRQ_LOOP, status);
+ handled = 1;
+ cx_write(MO_PCI_INTSTAT, status);
+
+ if (status & core->pci_irqmask)
+ cx88_core_irq(core, status);
+ if (status & PCI_INT_AUDINT)
+ cx8801_aud_irq(chip);
+ }
+
+ if (MAX_IRQ_LOOP == loop) {
+ printk(KERN_ERR
+ "%s/1: IRQ loop detected, disabling interrupts\n",
+ core->name);
+ cx_clear(MO_PCI_INTMSK, PCI_INT_AUDINT);
+ }
+
+ out:
+ return IRQ_RETVAL(handled);
+}
+
+
+static int dsp_buffer_free(snd_cx88_card_t *chip)
+{
+ BUG_ON(!chip->dma_size);
+
+ dprintk(2,"Freeing buffer\n");
+ videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc);
+ videobuf_dma_free(chip->dma_risc);
+ btcx_riscmem_free(chip->pci,&chip->buf->risc);
+ kfree(chip->buf);
+
+ chip->dma_risc = NULL;
+ chip->dma_size = 0;
+
+ return 0;
+}
+
+/****************************************************************************
+ ALSA PCM Interface
+ ****************************************************************************/
+
+/*
+ * Digital hardware definition
+ */
+#define DEFAULT_FIFO_SIZE 4096
+static const struct snd_pcm_hardware snd_cx88_digital_hw = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* Analog audio output will be full of clicks and pops if there
+ are not exactly four lines in the SRAM FIFO buffer. */
+ .period_bytes_min = DEFAULT_FIFO_SIZE/4,
+ .period_bytes_max = DEFAULT_FIFO_SIZE/4,
+ .periods_min = 1,
+ .periods_max = 1024,
+ .buffer_bytes_max = (1024*1024),
+};
+
+/*
+ * audio pcm capture open callback
+ */
+static int snd_cx88_pcm_open(struct snd_pcm_substream *substream)
+{
+ snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ if (!chip) {
+ printk(KERN_ERR "BUG: cx88 can't find device struct."
+ " Can't proceed with open\n");
+ return -ENODEV;
+ }
+
+ err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
+ goto _error;
+
+ chip->substream = substream;
+
+ runtime->hw = snd_cx88_digital_hw;
+
+ if (cx88_sram_channels[SRAM_CH25].fifo_size != DEFAULT_FIFO_SIZE) {
+ unsigned int bpl = cx88_sram_channels[SRAM_CH25].fifo_size / 4;
+ bpl &= ~7; /* must be multiple of 8 */
+ runtime->hw.period_bytes_min = bpl;
+ runtime->hw.period_bytes_max = bpl;
+ }
+
+ return 0;
+_error:
+ dprintk(1,"Error opening PCM!\n");
+ return err;
+}
+
+/*
+ * audio close callback
+ */
+static int snd_cx88_close(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+/*
+ * hw_params callback
+ */
+static int snd_cx88_hw_params(struct snd_pcm_substream * substream,
+ struct snd_pcm_hw_params * hw_params)
+{
+ snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);
+ struct videobuf_dmabuf *dma;
+
+ struct cx88_audio_buffer *buf;
+ int ret;
+
+ if (substream->runtime->dma_area) {
+ dsp_buffer_free(chip);
+ substream->runtime->dma_area = NULL;
+ }
+
+ chip->period_size = params_period_bytes(hw_params);
+ chip->num_periods = params_periods(hw_params);
+ chip->dma_size = chip->period_size * params_periods(hw_params);
+
+ BUG_ON(!chip->dma_size);
+ BUG_ON(chip->num_periods & (chip->num_periods-1));
+
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (NULL == buf)
+ return -ENOMEM;
+
+ buf->bpl = chip->period_size;
+
+ dma = &buf->dma;
+ videobuf_dma_init(dma);
+ ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE,
+ (PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT));
+ if (ret < 0)
+ goto error;
+
+ ret = videobuf_dma_map(&chip->pci->dev, dma);
+ if (ret < 0)
+ goto error;
+
+ ret = cx88_risc_databuffer(chip->pci, &buf->risc, dma->sglist,
+ chip->period_size, chip->num_periods, 1);
+ if (ret < 0)
+ goto error;
+
+ /* Loop back to start of program */
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP|RISC_IRQ1|RISC_CNT_INC);
+ buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+
+ chip->buf = buf;
+ chip->dma_risc = dma;
+
+ substream->runtime->dma_area = chip->dma_risc->vaddr;
+ substream->runtime->dma_bytes = chip->dma_size;
+ substream->runtime->dma_addr = 0;
+ return 0;
+
+error:
+ kfree(buf);
+ return ret;
+}
+
+/*
+ * hw free callback
+ */
+static int snd_cx88_hw_free(struct snd_pcm_substream * substream)
+{
+
+ snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);
+
+ if (substream->runtime->dma_area) {
+ dsp_buffer_free(chip);
+ substream->runtime->dma_area = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * prepare callback
+ */
+static int snd_cx88_prepare(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+/*
+ * trigger callback
+ */
+static int snd_cx88_card_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);
+ int err;
+
+ /* Local interrupts are already disabled by ALSA */
+ spin_lock(&chip->reg_lock);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ err=_cx88_start_audio_dma(chip);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ err=_cx88_stop_audio_dma(chip);
+ break;
+ default:
+ err=-EINVAL;
+ break;
+ }
+
+ spin_unlock(&chip->reg_lock);
+
+ return err;
+}
+
+/*
+ * pointer callback
+ */
+static snd_pcm_uframes_t snd_cx88_pointer(struct snd_pcm_substream *substream)
+{
+ snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ u16 count;
+
+ count = atomic_read(&chip->count);
+
+// dprintk(2, "%s - count %d (+%u), period %d, frame %lu\n", __func__,
+// count, new, count & (runtime->periods-1),
+// runtime->period_size * (count & (runtime->periods-1)));
+ return runtime->period_size * (count & (runtime->periods-1));
+}
+
+/*
+ * page callback (needed for mmap)
+ */
+static struct page *snd_cx88_page(struct snd_pcm_substream *substream,
+ unsigned long offset)
+{
+ void *pageptr = substream->runtime->dma_area + offset;
+ return vmalloc_to_page(pageptr);
+}
+
+/*
+ * operators
+ */
+static struct snd_pcm_ops snd_cx88_pcm_ops = {
+ .open = snd_cx88_pcm_open,
+ .close = snd_cx88_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_cx88_hw_params,
+ .hw_free = snd_cx88_hw_free,
+ .prepare = snd_cx88_prepare,
+ .trigger = snd_cx88_card_trigger,
+ .pointer = snd_cx88_pointer,
+ .page = snd_cx88_page,
+};
+
+/*
+ * create a PCM device
+ */
+static int __devinit snd_cx88_pcm(snd_cx88_card_t *chip, int device, const char *name)
+{
+ int err;
+ struct snd_pcm *pcm;
+
+ err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm);
+ if (err < 0)
+ return err;
+ pcm->private_data = chip;
+ strcpy(pcm->name, name);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx88_pcm_ops);
+
+ return 0;
+}
+
+/****************************************************************************
+ CONTROL INTERFACE
+ ****************************************************************************/
+static int snd_cx88_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *info)
+{
+ info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ info->count = 2;
+ info->value.integer.min = 0;
+ info->value.integer.max = 0x3f;
+
+ return 0;
+}
+
+static int snd_cx88_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
+ struct cx88_core *core=chip->core;
+ int vol = 0x3f - (cx_read(AUD_VOL_CTL) & 0x3f),
+ bal = cx_read(AUD_BAL_CTL);
+
+ value->value.integer.value[(bal & 0x40) ? 0 : 1] = vol;
+ vol -= (bal & 0x3f);
+ value->value.integer.value[(bal & 0x40) ? 1 : 0] = vol < 0 ? 0 : vol;
+
+ return 0;
+}
+
+static void snd_cx88_wm8775_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
+ struct cx88_core *core = chip->core;
+ struct v4l2_control client_ctl;
+ int left = value->value.integer.value[0];
+ int right = value->value.integer.value[1];
+ int v, b;
+
+ memset(&client_ctl, 0, sizeof(client_ctl));
+
+ /* Pass volume & balance onto any WM8775 */
+ if (left >= right) {
+ v = left << 10;
+ b = left ? (0x8000 * right) / left : 0x8000;
+ } else {
+ v = right << 10;
+ b = right ? 0xffff - (0x8000 * left) / right : 0x8000;
+ }
+ client_ctl.value = v;
+ client_ctl.id = V4L2_CID_AUDIO_VOLUME;
+ call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
+
+ client_ctl.value = b;
+ client_ctl.id = V4L2_CID_AUDIO_BALANCE;
+ call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
+}
+
+/* OK - TODO: test it */
+static int snd_cx88_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
+ struct cx88_core *core=chip->core;
+ int left, right, v, b;
+ int changed = 0;
+ u32 old;
+
+ if (core->board.audio_chip == V4L2_IDENT_WM8775)
+ snd_cx88_wm8775_volume_put(kcontrol, value);
+
+ left = value->value.integer.value[0] & 0x3f;
+ right = value->value.integer.value[1] & 0x3f;
+ b = right - left;
+ if (b < 0) {
+ v = 0x3f - left;
+ b = (-b) | 0x40;
+ } else {
+ v = 0x3f - right;
+ }
+ /* Do we really know this will always be called with IRQs on? */
+ spin_lock_irq(&chip->reg_lock);
+ old = cx_read(AUD_VOL_CTL);
+ if (v != (old & 0x3f)) {
+ cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, (old & ~0x3f) | v);
+ changed = 1;
+ }
+ if ((cx_read(AUD_BAL_CTL) & 0x7f) != b) {
+ cx_write(AUD_BAL_CTL, b);
+ changed = 1;
+ }
+ spin_unlock_irq(&chip->reg_lock);
+
+ return changed;
+}
+
+static const DECLARE_TLV_DB_SCALE(snd_cx88_db_scale, -6300, 100, 0);
+
+static const struct snd_kcontrol_new snd_cx88_volume = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .name = "Analog-TV Volume",
+ .info = snd_cx88_volume_info,
+ .get = snd_cx88_volume_get,
+ .put = snd_cx88_volume_put,
+ .tlv.p = snd_cx88_db_scale,
+};
+
+static int snd_cx88_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
+ struct cx88_core *core = chip->core;
+ u32 bit = kcontrol->private_value;
+
+ value->value.integer.value[0] = !(cx_read(AUD_VOL_CTL) & bit);
+ return 0;
+}
+
+static int snd_cx88_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
+ struct cx88_core *core = chip->core;
+ u32 bit = kcontrol->private_value;
+ int ret = 0;
+ u32 vol;
+
+ spin_lock_irq(&chip->reg_lock);
+ vol = cx_read(AUD_VOL_CTL);
+ if (value->value.integer.value[0] != !(vol & bit)) {
+ vol ^= bit;
+ cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, vol);
+ /* Pass mute onto any WM8775 */
+ if ((core->board.audio_chip == V4L2_IDENT_WM8775) &&
+ ((1<<6) == bit)) {
+ struct v4l2_control client_ctl;
+
+ memset(&client_ctl, 0, sizeof(client_ctl));
+ client_ctl.value = 0 != (vol & bit);
+ client_ctl.id = V4L2_CID_AUDIO_MUTE;
+ call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
+ }
+ ret = 1;
+ }
+ spin_unlock_irq(&chip->reg_lock);
+ return ret;
+}
+
+static const struct snd_kcontrol_new snd_cx88_dac_switch = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Audio-Out Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = snd_cx88_switch_get,
+ .put = snd_cx88_switch_put,
+ .private_value = (1<<8),
+};
+
+static const struct snd_kcontrol_new snd_cx88_source_switch = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog-TV Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = snd_cx88_switch_get,
+ .put = snd_cx88_switch_put,
+ .private_value = (1<<6),
+};
+
+static int snd_cx88_alc_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
+ struct cx88_core *core = chip->core;
+ struct v4l2_control client_ctl;
+
+ memset(&client_ctl, 0, sizeof(client_ctl));
+ client_ctl.id = V4L2_CID_AUDIO_LOUDNESS;
+ call_hw(core, WM8775_GID, core, g_ctrl, &client_ctl);
+ value->value.integer.value[0] = client_ctl.value ? 1 : 0;
+
+ return 0;
+}
+
+static int snd_cx88_alc_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
+ struct cx88_core *core = chip->core;
+ struct v4l2_control client_ctl;
+
+ memset(&client_ctl, 0, sizeof(client_ctl));
+ client_ctl.value = 0 != value->value.integer.value[0];
+ client_ctl.id = V4L2_CID_AUDIO_LOUDNESS;
+ call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
+
+ return 0;
+}
+
+static struct snd_kcontrol_new snd_cx88_alc_switch = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Line-In ALC Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = snd_cx88_alc_get,
+ .put = snd_cx88_alc_put,
+};
+
+/****************************************************************************
+ Basic Flow for Sound Devices
+ ****************************************************************************/
+
+/*
+ * PCI ID Table - 14f1:8801 and 14f1:8811 means function 1: Audio
+ * Only boards with eeprom and byte 1 at eeprom=1 have it
+ */
+
+static const struct pci_device_id const cx88_audio_pci_tbl[] __devinitdata = {
+ {0x14f1,0x8801,PCI_ANY_ID,PCI_ANY_ID,0,0,0},
+ {0x14f1,0x8811,PCI_ANY_ID,PCI_ANY_ID,0,0,0},
+ {0, }
+};
+MODULE_DEVICE_TABLE(pci, cx88_audio_pci_tbl);
+
+/*
+ * Chip-specific destructor
+ */
+
+static int snd_cx88_free(snd_cx88_card_t *chip)
+{
+
+ if (chip->irq >= 0)
+ free_irq(chip->irq, chip);
+
+ cx88_core_put(chip->core,chip->pci);
+
+ pci_disable_device(chip->pci);
+ return 0;
+}
+
+/*
+ * Component Destructor
+ */
+static void snd_cx88_dev_free(struct snd_card * card)
+{
+ snd_cx88_card_t *chip = card->private_data;
+
+ snd_cx88_free(chip);
+}
+
+
+/*
+ * Alsa Constructor - Component probe
+ */
+
+static int devno;
+static int __devinit snd_cx88_create(struct snd_card *card,
+ struct pci_dev *pci,
+ snd_cx88_card_t **rchip,
+ struct cx88_core **core_ptr)
+{
+ snd_cx88_card_t *chip;
+ struct cx88_core *core;
+ int err;
+ unsigned char pci_lat;
+
+ *rchip = NULL;
+
+ err = pci_enable_device(pci);
+ if (err < 0)
+ return err;
+
+ pci_set_master(pci);
+
+ chip = card->private_data;
+
+ core = cx88_core_get(pci);
+ if (NULL == core) {
+ err = -EINVAL;
+ return err;
+ }
+
+ if (!pci_dma_supported(pci,DMA_BIT_MASK(32))) {
+ dprintk(0, "%s/1: Oops: no 32bit PCI DMA ???\n",core->name);
+ err = -EIO;
+ cx88_core_put(core, pci);
+ return err;
+ }
+
+
+ /* pci init */
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+ spin_lock_init(&chip->reg_lock);
+
+ chip->core = core;
+
+ /* get irq */
+ err = request_irq(chip->pci->irq, cx8801_irq,
+ IRQF_SHARED | IRQF_DISABLED, chip->core->name, chip);
+ if (err < 0) {
+ dprintk(0, "%s: can't get IRQ %d\n",
+ chip->core->name, chip->pci->irq);
+ return err;
+ }
+
+ /* print pci info */
+ pci_read_config_byte(pci, PCI_LATENCY_TIMER, &pci_lat);
+
+ dprintk(1,"ALSA %s/%i: found at %s, rev: %d, irq: %d, "
+ "latency: %d, mmio: 0x%llx\n", core->name, devno,
+ pci_name(pci), pci->revision, pci->irq,
+ pci_lat, (unsigned long long)pci_resource_start(pci,0));
+
+ chip->irq = pci->irq;
+ synchronize_irq(chip->irq);
+
+ snd_card_set_dev(card, &pci->dev);
+
+ *rchip = chip;
+ *core_ptr = core;
+
+ return 0;
+}
+
+static int __devinit cx88_audio_initdev(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ struct snd_card *card;
+ snd_cx88_card_t *chip;
+ struct cx88_core *core = NULL;
+ int err;
+
+ if (devno >= SNDRV_CARDS)
+ return (-ENODEV);
+
+ if (!enable[devno]) {
+ ++devno;
+ return (-ENOENT);
+ }
+
+ err = snd_card_create(index[devno], id[devno], THIS_MODULE,
+ sizeof(snd_cx88_card_t), &card);
+ if (err < 0)
+ return err;
+
+ card->private_free = snd_cx88_dev_free;
+
+ err = snd_cx88_create(card, pci, &chip, &core);
+ if (err < 0)
+ goto error;
+
+ err = snd_cx88_pcm(chip, 0, "CX88 Digital");
+ if (err < 0)
+ goto error;
+
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_volume, chip));
+ if (err < 0)
+ goto error;
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_dac_switch, chip));
+ if (err < 0)
+ goto error;
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_source_switch, chip));
+ if (err < 0)
+ goto error;
+
+ /* If there's a wm8775 then add a Line-In ALC switch */
+ if (core->board.audio_chip == V4L2_IDENT_WM8775)
+ snd_ctl_add(card, snd_ctl_new1(&snd_cx88_alc_switch, chip));
+
+ strcpy (card->driver, "CX88x");
+ sprintf(card->shortname, "Conexant CX%x", pci->device);
+ sprintf(card->longname, "%s at %#llx",
+ card->shortname,(unsigned long long)pci_resource_start(pci, 0));
+ strcpy (card->mixername, "CX88");
+
+ dprintk (0, "%s/%i: ALSA support for cx2388x boards\n",
+ card->driver,devno);
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+ pci_set_drvdata(pci,card);
+
+ devno++;
+ return 0;
+
+error:
+ snd_card_free(card);
+ return err;
+}
+/*
+ * ALSA destructor
+ */
+static void __devexit cx88_audio_finidev(struct pci_dev *pci)
+{
+ struct cx88_audio_dev *card = pci_get_drvdata(pci);
+
+ snd_card_free((void *)card);
+
+ pci_set_drvdata(pci, NULL);
+
+ devno--;
+}
+
+/*
+ * PCI driver definition
+ */
+
+static struct pci_driver cx88_audio_pci_driver = {
+ .name = "cx88_audio",
+ .id_table = cx88_audio_pci_tbl,
+ .probe = cx88_audio_initdev,
+ .remove = __devexit_p(cx88_audio_finidev),
+};
+
+/****************************************************************************
+ LINUX MODULE INIT
+ ****************************************************************************/
+
+/*
+ * module init
+ */
+static int __init cx88_audio_init(void)
+{
+ printk(KERN_INFO "cx2388x alsa driver version %s loaded\n",
+ CX88_VERSION);
+ return pci_register_driver(&cx88_audio_pci_driver);
+}
+
+/*
+ * module remove
+ */
+static void __exit cx88_audio_fini(void)
+{
+ pci_unregister_driver(&cx88_audio_pci_driver);
+}
+
+module_init(cx88_audio_init);
+module_exit(cx88_audio_fini);
diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c
new file mode 100644
index 00000000..e46446a4
--- /dev/null
+++ b/drivers/media/video/cx88/cx88-blackbird.c
@@ -0,0 +1,1385 @@
+/*
+ *
+ * Support for a cx23416 mpeg encoder via cx2388x host port.
+ * "blackbird" reference design.
+ *
+ * (c) 2004 Jelle Foks <jelle@foks.us>
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org>
+ *
+ * (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org>
+ * - video_ioctl2 conversion
+ *
+ * Includes parts from the ivtv driver <http://sourceforge.net/projects/ivtv/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/cx2341x.h>
+
+#include "cx88.h"
+
+MODULE_DESCRIPTION("driver for cx2388x/cx23416 based mpeg encoder cards");
+MODULE_AUTHOR("Jelle Foks <jelle@foks.us>, Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(CX88_VERSION);
+
+static unsigned int mpegbufs = 32;
+module_param(mpegbufs,int,0644);
+MODULE_PARM_DESC(mpegbufs,"number of mpeg buffers, range 2-32");
+
+static unsigned int debug;
+module_param(debug,int,0644);
+MODULE_PARM_DESC(debug,"enable debug messages [blackbird]");
+
+#define dprintk(level,fmt, arg...) if (debug >= level) \
+ printk(KERN_DEBUG "%s/2-bb: " fmt, dev->core->name , ## arg)
+
+
+/* ------------------------------------------------------------------ */
+
+#define BLACKBIRD_FIRM_IMAGE_SIZE 376836
+
+/* defines below are from ivtv-driver.h */
+
+#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF
+
+/* Firmware API commands */
+#define IVTV_API_STD_TIMEOUT 500
+
+enum blackbird_capture_type {
+ BLACKBIRD_MPEG_CAPTURE,
+ BLACKBIRD_RAW_CAPTURE,
+ BLACKBIRD_RAW_PASSTHRU_CAPTURE
+};
+enum blackbird_capture_bits {
+ BLACKBIRD_RAW_BITS_NONE = 0x00,
+ BLACKBIRD_RAW_BITS_YUV_CAPTURE = 0x01,
+ BLACKBIRD_RAW_BITS_PCM_CAPTURE = 0x02,
+ BLACKBIRD_RAW_BITS_VBI_CAPTURE = 0x04,
+ BLACKBIRD_RAW_BITS_PASSTHRU_CAPTURE = 0x08,
+ BLACKBIRD_RAW_BITS_TO_HOST_CAPTURE = 0x10
+};
+enum blackbird_capture_end {
+ BLACKBIRD_END_AT_GOP, /* stop at the end of gop, generate irq */
+ BLACKBIRD_END_NOW, /* stop immediately, no irq */
+};
+enum blackbird_framerate {
+ BLACKBIRD_FRAMERATE_NTSC_30, /* NTSC: 30fps */
+ BLACKBIRD_FRAMERATE_PAL_25 /* PAL: 25fps */
+};
+enum blackbird_stream_port {
+ BLACKBIRD_OUTPUT_PORT_MEMORY,
+ BLACKBIRD_OUTPUT_PORT_STREAMING,
+ BLACKBIRD_OUTPUT_PORT_SERIAL
+};
+enum blackbird_data_xfer_status {
+ BLACKBIRD_MORE_BUFFERS_FOLLOW,
+ BLACKBIRD_LAST_BUFFER,
+};
+enum blackbird_picture_mask {
+ BLACKBIRD_PICTURE_MASK_NONE,
+ BLACKBIRD_PICTURE_MASK_I_FRAMES,
+ BLACKBIRD_PICTURE_MASK_I_P_FRAMES = 0x3,
+ BLACKBIRD_PICTURE_MASK_ALL_FRAMES = 0x7,
+};
+enum blackbird_vbi_mode_bits {
+ BLACKBIRD_VBI_BITS_SLICED,
+ BLACKBIRD_VBI_BITS_RAW,
+};
+enum blackbird_vbi_insertion_bits {
+ BLACKBIRD_VBI_BITS_INSERT_IN_XTENSION_USR_DATA,
+ BLACKBIRD_VBI_BITS_INSERT_IN_PRIVATE_PACKETS = 0x1 << 1,
+ BLACKBIRD_VBI_BITS_SEPARATE_STREAM = 0x2 << 1,
+ BLACKBIRD_VBI_BITS_SEPARATE_STREAM_USR_DATA = 0x4 << 1,
+ BLACKBIRD_VBI_BITS_SEPARATE_STREAM_PRV_DATA = 0x5 << 1,
+};
+enum blackbird_dma_unit {
+ BLACKBIRD_DMA_BYTES,
+ BLACKBIRD_DMA_FRAMES,
+};
+enum blackbird_dma_transfer_status_bits {
+ BLACKBIRD_DMA_TRANSFER_BITS_DONE = 0x01,
+ BLACKBIRD_DMA_TRANSFER_BITS_ERROR = 0x04,
+ BLACKBIRD_DMA_TRANSFER_BITS_LL_ERROR = 0x10,
+};
+enum blackbird_pause {
+ BLACKBIRD_PAUSE_ENCODING,
+ BLACKBIRD_RESUME_ENCODING,
+};
+enum blackbird_copyright {
+ BLACKBIRD_COPYRIGHT_OFF,
+ BLACKBIRD_COPYRIGHT_ON,
+};
+enum blackbird_notification_type {
+ BLACKBIRD_NOTIFICATION_REFRESH,
+};
+enum blackbird_notification_status {
+ BLACKBIRD_NOTIFICATION_OFF,
+ BLACKBIRD_NOTIFICATION_ON,
+};
+enum blackbird_notification_mailbox {
+ BLACKBIRD_NOTIFICATION_NO_MAILBOX = -1,
+};
+enum blackbird_field1_lines {
+ BLACKBIRD_FIELD1_SAA7114 = 0x00EF, /* 239 */
+ BLACKBIRD_FIELD1_SAA7115 = 0x00F0, /* 240 */
+ BLACKBIRD_FIELD1_MICRONAS = 0x0105, /* 261 */
+};
+enum blackbird_field2_lines {
+ BLACKBIRD_FIELD2_SAA7114 = 0x00EF, /* 239 */
+ BLACKBIRD_FIELD2_SAA7115 = 0x00F0, /* 240 */
+ BLACKBIRD_FIELD2_MICRONAS = 0x0106, /* 262 */
+};
+enum blackbird_custom_data_type {
+ BLACKBIRD_CUSTOM_EXTENSION_USR_DATA,
+ BLACKBIRD_CUSTOM_PRIVATE_PACKET,
+};
+enum blackbird_mute {
+ BLACKBIRD_UNMUTE,
+ BLACKBIRD_MUTE,
+};
+enum blackbird_mute_video_mask {
+ BLACKBIRD_MUTE_VIDEO_V_MASK = 0x0000FF00,
+ BLACKBIRD_MUTE_VIDEO_U_MASK = 0x00FF0000,
+ BLACKBIRD_MUTE_VIDEO_Y_MASK = 0xFF000000,
+};
+enum blackbird_mute_video_shift {
+ BLACKBIRD_MUTE_VIDEO_V_SHIFT = 8,
+ BLACKBIRD_MUTE_VIDEO_U_SHIFT = 16,
+ BLACKBIRD_MUTE_VIDEO_Y_SHIFT = 24,
+};
+
+/* Registers */
+#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_SPU (0x9050 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_HW_BLOCKS (0x9054 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_VPU (0x9058 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_APU (0xA064 /*| IVTV_REG_OFFSET*/)
+
+/* ------------------------------------------------------------------ */
+
+static void host_setup(struct cx88_core *core)
+{
+ /* toggle reset of the host */
+ cx_write(MO_GPHST_SOFT_RST, 1);
+ udelay(100);
+ cx_write(MO_GPHST_SOFT_RST, 0);
+ udelay(100);
+
+ /* host port setup */
+ cx_write(MO_GPHST_WSC, 0x44444444U);
+ cx_write(MO_GPHST_XFR, 0);
+ cx_write(MO_GPHST_WDTH, 15);
+ cx_write(MO_GPHST_HDSHK, 0);
+ cx_write(MO_GPHST_MUX16, 0x44448888U);
+ cx_write(MO_GPHST_MODE, 0);
+}
+
+/* ------------------------------------------------------------------ */
+
+#define P1_MDATA0 0x390000
+#define P1_MDATA1 0x390001
+#define P1_MDATA2 0x390002
+#define P1_MDATA3 0x390003
+#define P1_MADDR2 0x390004
+#define P1_MADDR1 0x390005
+#define P1_MADDR0 0x390006
+#define P1_RDATA0 0x390008
+#define P1_RDATA1 0x390009
+#define P1_RDATA2 0x39000A
+#define P1_RDATA3 0x39000B
+#define P1_RADDR0 0x39000C
+#define P1_RADDR1 0x39000D
+#define P1_RRDWR 0x39000E
+
+static int wait_ready_gpio0_bit1(struct cx88_core *core, u32 state)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(1);
+ u32 gpio0,need;
+
+ need = state ? 2 : 0;
+ for (;;) {
+ gpio0 = cx_read(MO_GP0_IO) & 2;
+ if (need == gpio0)
+ return 0;
+ if (time_after(jiffies,timeout))
+ return -1;
+ udelay(1);
+ }
+}
+
+static int memory_write(struct cx88_core *core, u32 address, u32 value)
+{
+ /* Warning: address is dword address (4 bytes) */
+ cx_writeb(P1_MDATA0, (unsigned int)value);
+ cx_writeb(P1_MDATA1, (unsigned int)(value >> 8));
+ cx_writeb(P1_MDATA2, (unsigned int)(value >> 16));
+ cx_writeb(P1_MDATA3, (unsigned int)(value >> 24));
+ cx_writeb(P1_MADDR2, (unsigned int)(address >> 16) | 0x40);
+ cx_writeb(P1_MADDR1, (unsigned int)(address >> 8));
+ cx_writeb(P1_MADDR0, (unsigned int)address);
+ cx_read(P1_MDATA0);
+ cx_read(P1_MADDR0);
+
+ return wait_ready_gpio0_bit1(core,1);
+}
+
+static int memory_read(struct cx88_core *core, u32 address, u32 *value)
+{
+ int retval;
+ u32 val;
+
+ /* Warning: address is dword address (4 bytes) */
+ cx_writeb(P1_MADDR2, (unsigned int)(address >> 16) & ~0xC0);
+ cx_writeb(P1_MADDR1, (unsigned int)(address >> 8));
+ cx_writeb(P1_MADDR0, (unsigned int)address);
+ cx_read(P1_MADDR0);
+
+ retval = wait_ready_gpio0_bit1(core,1);
+
+ cx_writeb(P1_MDATA3, 0);
+ val = (unsigned char)cx_read(P1_MDATA3) << 24;
+ cx_writeb(P1_MDATA2, 0);
+ val |= (unsigned char)cx_read(P1_MDATA2) << 16;
+ cx_writeb(P1_MDATA1, 0);
+ val |= (unsigned char)cx_read(P1_MDATA1) << 8;
+ cx_writeb(P1_MDATA0, 0);
+ val |= (unsigned char)cx_read(P1_MDATA0);
+
+ *value = val;
+ return retval;
+}
+
+static int register_write(struct cx88_core *core, u32 address, u32 value)
+{
+ cx_writeb(P1_RDATA0, (unsigned int)value);
+ cx_writeb(P1_RDATA1, (unsigned int)(value >> 8));
+ cx_writeb(P1_RDATA2, (unsigned int)(value >> 16));
+ cx_writeb(P1_RDATA3, (unsigned int)(value >> 24));
+ cx_writeb(P1_RADDR0, (unsigned int)address);
+ cx_writeb(P1_RADDR1, (unsigned int)(address >> 8));
+ cx_writeb(P1_RRDWR, 1);
+ cx_read(P1_RDATA0);
+ cx_read(P1_RADDR0);
+
+ return wait_ready_gpio0_bit1(core,1);
+}
+
+
+static int register_read(struct cx88_core *core, u32 address, u32 *value)
+{
+ int retval;
+ u32 val;
+
+ cx_writeb(P1_RADDR0, (unsigned int)address);
+ cx_writeb(P1_RADDR1, (unsigned int)(address >> 8));
+ cx_writeb(P1_RRDWR, 0);
+ cx_read(P1_RADDR0);
+
+ retval = wait_ready_gpio0_bit1(core,1);
+ val = (unsigned char)cx_read(P1_RDATA0);
+ val |= (unsigned char)cx_read(P1_RDATA1) << 8;
+ val |= (unsigned char)cx_read(P1_RDATA2) << 16;
+ val |= (unsigned char)cx_read(P1_RDATA3) << 24;
+
+ *value = val;
+ return retval;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int blackbird_mbox_func(void *priv, u32 command, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA])
+{
+ struct cx8802_dev *dev = priv;
+ unsigned long timeout;
+ u32 value, flag, retval;
+ int i;
+
+ dprintk(1,"%s: 0x%X\n", __func__, command);
+
+ /* this may not be 100% safe if we can't read any memory location
+ without side effects */
+ memory_read(dev->core, dev->mailbox - 4, &value);
+ if (value != 0x12345678) {
+ dprintk(0, "Firmware and/or mailbox pointer not initialized or corrupted\n");
+ return -1;
+ }
+
+ memory_read(dev->core, dev->mailbox, &flag);
+ if (flag) {
+ dprintk(0, "ERROR: Mailbox appears to be in use (%x)\n", flag);
+ return -1;
+ }
+
+ flag |= 1; /* tell 'em we're working on it */
+ memory_write(dev->core, dev->mailbox, flag);
+
+ /* write command + args + fill remaining with zeros */
+ memory_write(dev->core, dev->mailbox + 1, command); /* command code */
+ memory_write(dev->core, dev->mailbox + 3, IVTV_API_STD_TIMEOUT); /* timeout */
+ for (i = 0; i < in; i++) {
+ memory_write(dev->core, dev->mailbox + 4 + i, data[i]);
+ dprintk(1, "API Input %d = %d\n", i, data[i]);
+ }
+ for (; i < CX2341X_MBOX_MAX_DATA; i++)
+ memory_write(dev->core, dev->mailbox + 4 + i, 0);
+
+ flag |= 3; /* tell 'em we're done writing */
+ memory_write(dev->core, dev->mailbox, flag);
+
+ /* wait for firmware to handle the API command */
+ timeout = jiffies + msecs_to_jiffies(10);
+ for (;;) {
+ memory_read(dev->core, dev->mailbox, &flag);
+ if (0 != (flag & 4))
+ break;
+ if (time_after(jiffies,timeout)) {
+ dprintk(0, "ERROR: API Mailbox timeout\n");
+ return -1;
+ }
+ udelay(10);
+ }
+
+ /* read output values */
+ for (i = 0; i < out; i++) {
+ memory_read(dev->core, dev->mailbox + 4 + i, data + i);
+ dprintk(1, "API Output %d = %d\n", i, data[i]);
+ }
+
+ memory_read(dev->core, dev->mailbox + 2, &retval);
+ dprintk(1, "API result = %d\n",retval);
+
+ flag = 0;
+ memory_write(dev->core, dev->mailbox, flag);
+ return retval;
+}
+/* ------------------------------------------------------------------ */
+
+/* We don't need to call the API often, so using just one mailbox will probably suffice */
+static int blackbird_api_cmd(struct cx8802_dev *dev, u32 command,
+ u32 inputcnt, u32 outputcnt, ...)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ va_list vargs;
+ int i, err;
+
+ va_start(vargs, outputcnt);
+
+ for (i = 0; i < inputcnt; i++) {
+ data[i] = va_arg(vargs, int);
+ }
+ err = blackbird_mbox_func(dev, command, inputcnt, outputcnt, data);
+ for (i = 0; i < outputcnt; i++) {
+ int *vptr = va_arg(vargs, int *);
+ *vptr = data[i];
+ }
+ va_end(vargs);
+ return err;
+}
+
+static int blackbird_find_mailbox(struct cx8802_dev *dev)
+{
+ u32 signature[4]={0x12345678, 0x34567812, 0x56781234, 0x78123456};
+ int signaturecnt=0;
+ u32 value;
+ int i;
+
+ for (i = 0; i < BLACKBIRD_FIRM_IMAGE_SIZE; i++) {
+ memory_read(dev->core, i, &value);
+ if (value == signature[signaturecnt])
+ signaturecnt++;
+ else
+ signaturecnt = 0;
+ if (4 == signaturecnt) {
+ dprintk(1, "Mailbox signature found\n");
+ return i+1;
+ }
+ }
+ dprintk(0, "Mailbox signature values not found!\n");
+ return -1;
+}
+
+static int blackbird_load_firmware(struct cx8802_dev *dev)
+{
+ static const unsigned char magic[8] = {
+ 0xa7, 0x0d, 0x00, 0x00, 0x66, 0xbb, 0x55, 0xaa
+ };
+ const struct firmware *firmware;
+ int i, retval = 0;
+ u32 value = 0;
+ u32 checksum = 0;
+ u32 *dataptr;
+
+ retval = register_write(dev->core, IVTV_REG_VPU, 0xFFFFFFED);
+ retval |= register_write(dev->core, IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST);
+ retval |= register_write(dev->core, IVTV_REG_ENC_SDRAM_REFRESH, 0x80000640);
+ retval |= register_write(dev->core, IVTV_REG_ENC_SDRAM_PRECHARGE, 0x1A);
+ msleep(1);
+ retval |= register_write(dev->core, IVTV_REG_APU, 0);
+
+ if (retval < 0)
+ dprintk(0, "Error with register_write\n");
+
+ retval = request_firmware(&firmware, CX2341X_FIRM_ENC_FILENAME,
+ &dev->pci->dev);
+
+
+ if (retval != 0) {
+ dprintk(0, "ERROR: Hotplug firmware request failed (%s).\n",
+ CX2341X_FIRM_ENC_FILENAME);
+ dprintk(0, "Please fix your hotplug setup, the board will "
+ "not work without firmware loaded!\n");
+ return -1;
+ }
+
+ if (firmware->size != BLACKBIRD_FIRM_IMAGE_SIZE) {
+ dprintk(0, "ERROR: Firmware size mismatch (have %zd, expected %d)\n",
+ firmware->size, BLACKBIRD_FIRM_IMAGE_SIZE);
+ release_firmware(firmware);
+ return -1;
+ }
+
+ if (0 != memcmp(firmware->data, magic, 8)) {
+ dprintk(0, "ERROR: Firmware magic mismatch, wrong file?\n");
+ release_firmware(firmware);
+ return -1;
+ }
+
+ /* transfer to the chip */
+ dprintk(1,"Loading firmware ...\n");
+ dataptr = (u32*)firmware->data;
+ for (i = 0; i < (firmware->size >> 2); i++) {
+ value = *dataptr;
+ checksum += ~value;
+ memory_write(dev->core, i, value);
+ dataptr++;
+ }
+
+ /* read back to verify with the checksum */
+ for (i--; i >= 0; i--) {
+ memory_read(dev->core, i, &value);
+ checksum -= ~value;
+ }
+ if (checksum) {
+ dprintk(0, "ERROR: Firmware load failed (checksum mismatch).\n");
+ release_firmware(firmware);
+ return -1;
+ }
+ release_firmware(firmware);
+ dprintk(0, "Firmware upload successful.\n");
+
+ retval |= register_write(dev->core, IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST);
+ retval |= register_read(dev->core, IVTV_REG_SPU, &value);
+ retval |= register_write(dev->core, IVTV_REG_SPU, value & 0xFFFFFFFE);
+ msleep(1);
+
+ retval |= register_read(dev->core, IVTV_REG_VPU, &value);
+ retval |= register_write(dev->core, IVTV_REG_VPU, value & 0xFFFFFFE8);
+
+ if (retval < 0)
+ dprintk(0, "Error with register_write\n");
+ return 0;
+}
+
+/**
+ Settings used by the windows tv app for PVR2000:
+=================================================================================================================
+Profile | Codec | Resolution | CBR/VBR | Video Qlty | V. Bitrate | Frmrate | Audio Codec | A. Bitrate | A. Mode
+-----------------------------------------------------------------------------------------------------------------
+MPEG-1 | MPEG1 | 352x288PAL | (CBR) | 1000:Optimal | 2000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo
+MPEG-2 | MPEG2 | 720x576PAL | VBR | 600 :Good | 4000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo
+VCD | MPEG1 | 352x288PAL | (CBR) | 1000:Optimal | 1150 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo
+DVD | MPEG2 | 720x576PAL | VBR | 600 :Good | 6000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo
+DB* DVD | MPEG2 | 720x576PAL | CBR | 600 :Good | 6000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo
+=================================================================================================================
+*DB: "DirectBurn"
+*/
+
+static void blackbird_codec_settings(struct cx8802_dev *dev)
+{
+ /* assign frame size */
+ blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0,
+ dev->height, dev->width);
+
+ dev->params.width = dev->width;
+ dev->params.height = dev->height;
+ dev->params.is_50hz = (dev->core->tvnorm & V4L2_STD_625_50) != 0;
+
+ cx2341x_update(dev, blackbird_mbox_func, NULL, &dev->params);
+}
+
+static int blackbird_initialize_codec(struct cx8802_dev *dev)
+{
+ struct cx88_core *core = dev->core;
+ int version;
+ int retval;
+
+ dprintk(1,"Initialize codec\n");
+ retval = blackbird_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */
+ if (retval < 0) {
+
+ dev->mpeg_active = 0;
+
+ /* ping was not successful, reset and upload firmware */
+ cx_write(MO_SRST_IO, 0); /* SYS_RSTO=0 */
+ cx_write(MO_SRST_IO, 1); /* SYS_RSTO=1 */
+ retval = blackbird_load_firmware(dev);
+ if (retval < 0)
+ return retval;
+
+ retval = blackbird_find_mailbox(dev);
+ if (retval < 0)
+ return -1;
+
+ dev->mailbox = retval;
+
+ retval = blackbird_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */
+ if (retval < 0) {
+ dprintk(0, "ERROR: Firmware ping failed!\n");
+ return -1;
+ }
+
+ retval = blackbird_api_cmd(dev, CX2341X_ENC_GET_VERSION, 0, 1, &version);
+ if (retval < 0) {
+ dprintk(0, "ERROR: Firmware get encoder version failed!\n");
+ return -1;
+ }
+ dprintk(0, "Firmware version is 0x%08x\n", version);
+ }
+
+ cx_write(MO_PINMUX_IO, 0x88); /* 656-8bit IO and enable MPEG parallel IO */
+ cx_clear(MO_INPUT_FORMAT, 0x100); /* chroma subcarrier lock to normal? */
+ cx_write(MO_VBOS_CONTROL, 0x84A00); /* no 656 mode, 8-bit pixels, disable VBI */
+ cx_clear(MO_OUTPUT_FORMAT, 0x0008); /* Normal Y-limits to let the mpeg encoder sync */
+
+ blackbird_codec_settings(dev);
+
+ blackbird_api_cmd(dev, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, 0,
+ BLACKBIRD_FIELD1_SAA7115,
+ BLACKBIRD_FIELD2_SAA7115
+ );
+
+ blackbird_api_cmd(dev, CX2341X_ENC_SET_PLACEHOLDER, 12, 0,
+ BLACKBIRD_CUSTOM_EXTENSION_USR_DATA,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+ return 0;
+}
+
+static int blackbird_start_codec(struct file *file, void *priv)
+{
+ struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev;
+ struct cx88_core *core = dev->core;
+ /* start capturing to the host interface */
+ u32 reg;
+
+ int i;
+ int lastchange = -1;
+ int lastval = 0;
+
+ for (i = 0; (i < 10) && (i < (lastchange + 4)); i++) {
+ reg = cx_read(AUD_STATUS);
+
+ dprintk(1, "AUD_STATUS:%dL: 0x%x\n", i, reg);
+ if ((reg & 0x0F) != lastval) {
+ lastval = reg & 0x0F;
+ lastchange = i;
+ }
+ msleep(100);
+ }
+
+ /* unmute audio source */
+ cx_clear(AUD_VOL_CTL, (1 << 6));
+
+ blackbird_api_cmd(dev, CX2341X_ENC_REFRESH_INPUT, 0, 0);
+
+ /* initialize the video input */
+ blackbird_api_cmd(dev, CX2341X_ENC_INITIALIZE_INPUT, 0, 0);
+
+ /* start capturing to the host interface */
+ blackbird_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0,
+ BLACKBIRD_MPEG_CAPTURE,
+ BLACKBIRD_RAW_BITS_NONE
+ );
+
+ dev->mpeg_active = 1;
+ return 0;
+}
+
+static int blackbird_stop_codec(struct cx8802_dev *dev)
+{
+ blackbird_api_cmd(dev, CX2341X_ENC_STOP_CAPTURE, 3, 0,
+ BLACKBIRD_END_NOW,
+ BLACKBIRD_MPEG_CAPTURE,
+ BLACKBIRD_RAW_BITS_NONE
+ );
+
+ dev->mpeg_active = 0;
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int bb_buf_setup(struct videobuf_queue *q,
+ unsigned int *count, unsigned int *size)
+{
+ struct cx8802_fh *fh = q->priv_data;
+
+ fh->dev->ts_packet_size = 188 * 4; /* was: 512 */
+ fh->dev->ts_packet_count = mpegbufs; /* was: 100 */
+
+ *size = fh->dev->ts_packet_size * fh->dev->ts_packet_count;
+ *count = fh->dev->ts_packet_count;
+ return 0;
+}
+
+static int
+bb_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct cx8802_fh *fh = q->priv_data;
+ return cx8802_buf_prepare(q, fh->dev, (struct cx88_buffer*)vb, field);
+}
+
+static void
+bb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct cx8802_fh *fh = q->priv_data;
+ cx8802_buf_queue(fh->dev, (struct cx88_buffer*)vb);
+}
+
+static void
+bb_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ cx88_free_buffer(q, (struct cx88_buffer*)vb);
+}
+
+static struct videobuf_queue_ops blackbird_qops = {
+ .buf_setup = bb_buf_setup,
+ .buf_prepare = bb_buf_prepare,
+ .buf_queue = bb_buf_queue,
+ .buf_release = bb_buf_release,
+};
+
+/* ------------------------------------------------------------------ */
+
+static const u32 *ctrl_classes[] = {
+ cx88_user_ctrls,
+ cx2341x_mpeg_ctrls,
+ NULL
+};
+
+static int blackbird_queryctrl(struct cx8802_dev *dev, struct v4l2_queryctrl *qctrl)
+{
+ qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
+ if (qctrl->id == 0)
+ return -EINVAL;
+
+ /* Standard V4L2 controls */
+ if (cx8800_ctrl_query(dev->core, qctrl) == 0)
+ return 0;
+
+ /* MPEG V4L2 controls */
+ if (cx2341x_ctrl_query(&dev->params, qctrl))
+ qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+/* IOCTL Handlers */
+
+static int vidioc_querymenu (struct file *file, void *priv,
+ struct v4l2_querymenu *qmenu)
+{
+ struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev;
+ struct v4l2_queryctrl qctrl;
+
+ qctrl.id = qmenu->id;
+ blackbird_queryctrl(dev, &qctrl);
+ return v4l2_ctrl_query_menu(qmenu, &qctrl,
+ cx2341x_ctrl_get_menu(&dev->params, qmenu->id));
+}
+
+static int vidioc_querycap (struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev;
+ struct cx88_core *core = dev->core;
+
+ strcpy(cap->driver, "cx88_blackbird");
+ strlcpy(cap->card, core->board.name, sizeof(cap->card));
+ sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci));
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+ if (UNSET != core->board.tuner_type)
+ cap->capabilities |= V4L2_CAP_TUNER;
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index != 0)
+ return -EINVAL;
+
+ strlcpy(f->description, "MPEG", sizeof(f->description));
+ f->pixelformat = V4L2_PIX_FMT_MPEG;
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap (struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx8802_fh *fh = priv;
+ struct cx8802_dev *dev = fh->dev;
+
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */
+ f->fmt.pix.colorspace = 0;
+ f->fmt.pix.width = dev->width;
+ f->fmt.pix.height = dev->height;
+ f->fmt.pix.field = fh->mpegq.field;
+ dprintk(0,"VIDIOC_G_FMT: w: %d, h: %d, f: %d\n",
+ dev->width, dev->height, fh->mpegq.field );
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap (struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx8802_fh *fh = priv;
+ struct cx8802_dev *dev = fh->dev;
+
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */;
+ f->fmt.pix.colorspace = 0;
+ dprintk(0,"VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n",
+ dev->width, dev->height, fh->mpegq.field );
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap (struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx8802_fh *fh = priv;
+ struct cx8802_dev *dev = fh->dev;
+ struct cx88_core *core = dev->core;
+
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */;
+ f->fmt.pix.colorspace = 0;
+ dev->width = f->fmt.pix.width;
+ dev->height = f->fmt.pix.height;
+ fh->mpegq.field = f->fmt.pix.field;
+ cx88_set_scale(core, f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
+ blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0,
+ f->fmt.pix.height, f->fmt.pix.width);
+ dprintk(0,"VIDIOC_S_FMT: w: %d, h: %d, f: %d\n",
+ f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field );
+ return 0;
+}
+
+static int vidioc_reqbufs (struct file *file, void *priv, struct v4l2_requestbuffers *p)
+{
+ struct cx8802_fh *fh = priv;
+ return (videobuf_reqbufs(&fh->mpegq, p));
+}
+
+static int vidioc_querybuf (struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct cx8802_fh *fh = priv;
+ return (videobuf_querybuf(&fh->mpegq, p));
+}
+
+static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct cx8802_fh *fh = priv;
+ return (videobuf_qbuf(&fh->mpegq, p));
+}
+
+static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct cx8802_fh *fh = priv;
+ return (videobuf_dqbuf(&fh->mpegq, p,
+ file->f_flags & O_NONBLOCK));
+}
+
+static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct cx8802_fh *fh = priv;
+ return videobuf_streamon(&fh->mpegq);
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct cx8802_fh *fh = priv;
+ return videobuf_streamoff(&fh->mpegq);
+}
+
+static int vidioc_g_ext_ctrls (struct file *file, void *priv,
+ struct v4l2_ext_controls *f)
+{
+ struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev;
+
+ if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+ return -EINVAL;
+ return cx2341x_ext_ctrls(&dev->params, 0, f, VIDIOC_G_EXT_CTRLS);
+}
+
+static int vidioc_s_ext_ctrls (struct file *file, void *priv,
+ struct v4l2_ext_controls *f)
+{
+ struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev;
+ struct cx2341x_mpeg_params p;
+ int err;
+
+ if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+ return -EINVAL;
+
+ if (dev->mpeg_active)
+ blackbird_stop_codec(dev);
+
+ p = dev->params;
+ err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_S_EXT_CTRLS);
+ if (!err) {
+ err = cx2341x_update(dev, blackbird_mbox_func, &dev->params, &p);
+ dev->params = p;
+ }
+ return err;
+}
+
+static int vidioc_try_ext_ctrls (struct file *file, void *priv,
+ struct v4l2_ext_controls *f)
+{
+ struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev;
+ struct cx2341x_mpeg_params p;
+ int err;
+
+ if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+ return -EINVAL;
+ p = dev->params;
+ err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_TRY_EXT_CTRLS);
+
+ return err;
+}
+
+static int vidioc_s_frequency (struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct cx8802_fh *fh = priv;
+ struct cx8802_dev *dev = fh->dev;
+ struct cx88_core *core = dev->core;
+
+ if (dev->mpeg_active)
+ blackbird_stop_codec(dev);
+
+ cx88_set_freq (core,f);
+ blackbird_initialize_codec(dev);
+ cx88_set_scale(dev->core, dev->width, dev->height,
+ fh->mpegq.field);
+ return 0;
+}
+
+static int vidioc_log_status (struct file *file, void *priv)
+{
+ struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev;
+ struct cx88_core *core = dev->core;
+ char name[32 + 2];
+
+ snprintf(name, sizeof(name), "%s/2", core->name);
+ printk("%s/2: ============ START LOG STATUS ============\n",
+ core->name);
+ call_all(core, core, log_status);
+ cx2341x_log_status(&dev->params, name);
+ printk("%s/2: ============= END LOG STATUS =============\n",
+ core->name);
+ return 0;
+}
+
+static int vidioc_queryctrl (struct file *file, void *priv,
+ struct v4l2_queryctrl *qctrl)
+{
+ struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev;
+
+ if (blackbird_queryctrl(dev, qctrl) == 0)
+ return 0;
+
+ qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
+ if (unlikely(qctrl->id == 0))
+ return -EINVAL;
+ return cx8800_ctrl_query(dev->core, qctrl);
+}
+
+static int vidioc_enum_input (struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
+ return cx88_enum_input (core,i);
+}
+
+static int vidioc_g_ctrl (struct file *file, void *priv,
+ struct v4l2_control *ctl)
+{
+ struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
+ return
+ cx88_get_control(core,ctl);
+}
+
+static int vidioc_s_ctrl (struct file *file, void *priv,
+ struct v4l2_control *ctl)
+{
+ struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
+ return
+ cx88_set_control(core,ctl);
+}
+
+static int vidioc_g_frequency (struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct cx8802_fh *fh = priv;
+ struct cx88_core *core = fh->dev->core;
+
+ if (unlikely(UNSET == core->board.tuner_type))
+ return -EINVAL;
+
+ f->type = V4L2_TUNER_ANALOG_TV;
+ f->frequency = core->freq;
+ call_all(core, tuner, g_frequency, f);
+
+ return 0;
+}
+
+static int vidioc_g_input (struct file *file, void *priv, unsigned int *i)
+{
+ struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
+
+ *i = core->input;
+ return 0;
+}
+
+static int vidioc_s_input (struct file *file, void *priv, unsigned int i)
+{
+ struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
+
+ if (i >= 4)
+ return -EINVAL;
+
+ mutex_lock(&core->lock);
+ cx88_newstation(core);
+ cx88_video_mux(core,i);
+ mutex_unlock(&core->lock);
+ return 0;
+}
+
+static int vidioc_g_tuner (struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
+ u32 reg;
+
+ if (unlikely(UNSET == core->board.tuner_type))
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+
+ strcpy(t->name, "Television");
+ t->type = V4L2_TUNER_ANALOG_TV;
+ t->capability = V4L2_TUNER_CAP_NORM;
+ t->rangehigh = 0xffffffffUL;
+
+ cx88_get_stereo(core ,t);
+ reg = cx_read(MO_DEVICE_STATUS);
+ t->signal = (reg & (1<<5)) ? 0xffff : 0x0000;
+ return 0;
+}
+
+static int vidioc_s_tuner (struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
+
+ if (UNSET == core->board.tuner_type)
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+
+ cx88_set_stereo(core, t->audmode, 1);
+ return 0;
+}
+
+static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
+
+ mutex_lock(&core->lock);
+ cx88_set_tvnorm(core,*id);
+ mutex_unlock(&core->lock);
+ return 0;
+}
+
+/* FIXME: cx88_ioctl_hook not implemented */
+
+static int mpeg_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct cx8802_dev *dev = video_drvdata(file);
+ struct cx8802_fh *fh;
+ struct cx8802_driver *drv = NULL;
+ int err;
+
+ dprintk( 1, "%s\n", __func__);
+
+ mutex_lock(&dev->core->lock);
+
+ /* Make sure we can acquire the hardware */
+ drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD);
+ if (!drv) {
+ dprintk(1, "%s: blackbird driver is not loaded\n", __func__);
+ mutex_unlock(&dev->core->lock);
+ return -ENODEV;
+ }
+
+ err = drv->request_acquire(drv);
+ if (err != 0) {
+ dprintk(1,"%s: Unable to acquire hardware, %d\n", __func__, err);
+ mutex_unlock(&dev->core->lock);
+ return err;
+ }
+
+ if (!dev->core->mpeg_users && blackbird_initialize_codec(dev) < 0) {
+ drv->request_release(drv);
+ mutex_unlock(&dev->core->lock);
+ return -EINVAL;
+ }
+ dprintk(1, "open dev=%s\n", video_device_node_name(vdev));
+
+ /* allocate + initialize per filehandle data */
+ fh = kzalloc(sizeof(*fh),GFP_KERNEL);
+ if (NULL == fh) {
+ drv->request_release(drv);
+ mutex_unlock(&dev->core->lock);
+ return -ENOMEM;
+ }
+ file->private_data = fh;
+ fh->dev = dev;
+
+ videobuf_queue_sg_init(&fh->mpegq, &blackbird_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct cx88_buffer),
+ fh, NULL);
+
+ /* FIXME: locking against other video device */
+ cx88_set_scale(dev->core, dev->width, dev->height,
+ fh->mpegq.field);
+
+ dev->core->mpeg_users++;
+ mutex_unlock(&dev->core->lock);
+ return 0;
+}
+
+static int mpeg_release(struct file *file)
+{
+ struct cx8802_fh *fh = file->private_data;
+ struct cx8802_dev *dev = fh->dev;
+ struct cx8802_driver *drv = NULL;
+
+ mutex_lock(&dev->core->lock);
+
+ if (dev->mpeg_active && dev->core->mpeg_users == 1)
+ blackbird_stop_codec(dev);
+
+ cx8802_cancel_buffers(fh->dev);
+ /* stop mpeg capture */
+ videobuf_stop(&fh->mpegq);
+
+ videobuf_mmap_free(&fh->mpegq);
+
+ file->private_data = NULL;
+ kfree(fh);
+
+ /* Make sure we release the hardware */
+ drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD);
+ WARN_ON(!drv);
+ if (drv)
+ drv->request_release(drv);
+
+ dev->core->mpeg_users--;
+
+ mutex_unlock(&dev->core->lock);
+
+ return 0;
+}
+
+static ssize_t
+mpeg_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+{
+ struct cx8802_fh *fh = file->private_data;
+ struct cx8802_dev *dev = fh->dev;
+
+ if (!dev->mpeg_active)
+ blackbird_start_codec(file, fh);
+
+ return videobuf_read_stream(&fh->mpegq, data, count, ppos, 0,
+ file->f_flags & O_NONBLOCK);
+}
+
+static unsigned int
+mpeg_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct cx8802_fh *fh = file->private_data;
+ struct cx8802_dev *dev = fh->dev;
+
+ if (!dev->mpeg_active)
+ blackbird_start_codec(file, fh);
+
+ return videobuf_poll_stream(file, &fh->mpegq, wait);
+}
+
+static int
+mpeg_mmap(struct file *file, struct vm_area_struct * vma)
+{
+ struct cx8802_fh *fh = file->private_data;
+
+ return videobuf_mmap_mapper(&fh->mpegq, vma);
+}
+
+static const struct v4l2_file_operations mpeg_fops =
+{
+ .owner = THIS_MODULE,
+ .open = mpeg_open,
+ .release = mpeg_release,
+ .read = mpeg_read,
+ .poll = mpeg_poll,
+ .mmap = mpeg_mmap,
+ .ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
+ .vidioc_querymenu = vidioc_querymenu,
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls,
+ .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls,
+ .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_log_status = vidioc_log_status,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_s_std = vidioc_s_std,
+};
+
+static struct video_device cx8802_mpeg_template = {
+ .name = "cx8802",
+ .fops = &mpeg_fops,
+ .ioctl_ops = &mpeg_ioctl_ops,
+ .tvnorms = CX88_NORMS,
+ .current_norm = V4L2_STD_NTSC_M,
+};
+
+/* ------------------------------------------------------------------ */
+
+/* The CX8802 MPEG API will call this when we can use the hardware */
+static int cx8802_blackbird_advise_acquire(struct cx8802_driver *drv)
+{
+ struct cx88_core *core = drv->core;
+ int err = 0;
+
+ switch (core->boardnr) {
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
+ /* By default, core setup will leave the cx22702 out of reset, on the bus.
+ * We left the hardware on power up with the cx22702 active.
+ * We're being given access to re-arrange the GPIOs.
+ * Take the bus off the cx22702 and put the cx23416 on it.
+ */
+ /* Toggle reset on cx22702 leaving i2c active */
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ cx_clear(MO_GP0_IO, 0x00000080);
+ udelay(50);
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ /* tri-state the cx22702 pins */
+ cx_set(MO_GP0_IO, 0x00000004);
+ udelay(1000);
+ break;
+ default:
+ err = -ENODEV;
+ }
+ return err;
+}
+
+/* The CX8802 MPEG API will call this when we need to release the hardware */
+static int cx8802_blackbird_advise_release(struct cx8802_driver *drv)
+{
+ struct cx88_core *core = drv->core;
+ int err = 0;
+
+ switch (core->boardnr) {
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
+ /* Exit leaving the cx23416 on the bus */
+ break;
+ default:
+ err = -ENODEV;
+ }
+ return err;
+}
+
+static void blackbird_unregister_video(struct cx8802_dev *dev)
+{
+ if (dev->mpeg_dev) {
+ if (video_is_registered(dev->mpeg_dev))
+ video_unregister_device(dev->mpeg_dev);
+ else
+ video_device_release(dev->mpeg_dev);
+ dev->mpeg_dev = NULL;
+ }
+}
+
+static int blackbird_register_video(struct cx8802_dev *dev)
+{
+ int err;
+
+ dev->mpeg_dev = cx88_vdev_init(dev->core,dev->pci,
+ &cx8802_mpeg_template,"mpeg");
+ video_set_drvdata(dev->mpeg_dev, dev);
+ err = video_register_device(dev->mpeg_dev,VFL_TYPE_GRABBER, -1);
+ if (err < 0) {
+ printk(KERN_INFO "%s/2: can't register mpeg device\n",
+ dev->core->name);
+ return err;
+ }
+ printk(KERN_INFO "%s/2: registered device %s [mpeg]\n",
+ dev->core->name, video_device_node_name(dev->mpeg_dev));
+ return 0;
+}
+
+/* ----------------------------------------------------------- */
+
+static int cx8802_blackbird_probe(struct cx8802_driver *drv)
+{
+ struct cx88_core *core = drv->core;
+ struct cx8802_dev *dev = core->dvbdev;
+ int err;
+
+ dprintk( 1, "%s\n", __func__);
+ dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n",
+ core->boardnr,
+ core->name,
+ core->pci_bus,
+ core->pci_slot);
+
+ err = -ENODEV;
+ if (!(core->board.mpeg & CX88_MPEG_BLACKBIRD))
+ goto fail_core;
+
+ dev->width = 720;
+ dev->height = 576;
+ cx2341x_fill_defaults(&dev->params);
+ dev->params.port = CX2341X_PORT_STREAMING;
+
+ cx8802_mpeg_template.current_norm = core->tvnorm;
+
+ if (core->tvnorm & V4L2_STD_525_60) {
+ dev->height = 480;
+ } else {
+ dev->height = 576;
+ }
+
+ /* blackbird stuff */
+ printk("%s/2: cx23416 based mpeg encoder (blackbird reference design)\n",
+ core->name);
+ host_setup(dev->core);
+
+ blackbird_initialize_codec(dev);
+ blackbird_register_video(dev);
+
+ /* initial device configuration: needed ? */
+// init_controls(core);
+ cx88_set_tvnorm(core,core->tvnorm);
+ cx88_video_mux(core,0);
+
+ return 0;
+
+ fail_core:
+ return err;
+}
+
+static int cx8802_blackbird_remove(struct cx8802_driver *drv)
+{
+ /* blackbird */
+ blackbird_unregister_video(drv->core->dvbdev);
+
+ return 0;
+}
+
+static struct cx8802_driver cx8802_blackbird_driver = {
+ .type_id = CX88_MPEG_BLACKBIRD,
+ .hw_access = CX8802_DRVCTL_SHARED,
+ .probe = cx8802_blackbird_probe,
+ .remove = cx8802_blackbird_remove,
+ .advise_acquire = cx8802_blackbird_advise_acquire,
+ .advise_release = cx8802_blackbird_advise_release,
+};
+
+static int __init blackbird_init(void)
+{
+ printk(KERN_INFO "cx2388x blackbird driver version %s loaded\n",
+ CX88_VERSION);
+ return cx8802_register_driver(&cx8802_blackbird_driver);
+}
+
+static void __exit blackbird_fini(void)
+{
+ cx8802_unregister_driver(&cx8802_blackbird_driver);
+}
+
+module_init(blackbird_init);
+module_exit(blackbird_fini);
+
+module_param_named(video_debug,cx8802_mpeg_template.debug, int, 0644);
+MODULE_PARM_DESC(debug,"enable debug messages [video]");
diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c
new file mode 100644
index 00000000..cbd5d119
--- /dev/null
+++ b/drivers/media/video/cx88/cx88-cards.c
@@ -0,0 +1,3791 @@
+/*
+ *
+ * device driver for Conexant 2388x based TV cards
+ * card-specific stuff.
+ *
+ * (c) 2003 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include "cx88.h"
+#include "tea5767.h"
+#include "xc4000.h"
+
+static unsigned int tuner[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
+static unsigned int radio[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
+static unsigned int card[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
+
+module_param_array(tuner, int, NULL, 0444);
+module_param_array(radio, int, NULL, 0444);
+module_param_array(card, int, NULL, 0444);
+
+MODULE_PARM_DESC(tuner,"tuner type");
+MODULE_PARM_DESC(radio,"radio tuner type");
+MODULE_PARM_DESC(card,"card type");
+
+static unsigned int latency = UNSET;
+module_param(latency,int,0444);
+MODULE_PARM_DESC(latency,"pci latency timer");
+
+static int disable_ir;
+module_param(disable_ir, int, 0444);
+MODULE_PARM_DESC(disable_ir, "Disable IR support");
+
+#define info_printk(core, fmt, arg...) \
+ printk(KERN_INFO "%s: " fmt, core->name , ## arg)
+
+#define warn_printk(core, fmt, arg...) \
+ printk(KERN_WARNING "%s: " fmt, core->name , ## arg)
+
+#define err_printk(core, fmt, arg...) \
+ printk(KERN_ERR "%s: " fmt, core->name , ## arg)
+
+
+/* ------------------------------------------------------------------ */
+/* board config info */
+
+/* If radio_type !=UNSET, radio_addr should be specified
+ */
+
+static const struct cx88_board cx88_boards[] = {
+ [CX88_BOARD_UNKNOWN] = {
+ .name = "UNKNOWN/GENERIC",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 0,
+ },{
+ .type = CX88_VMUX_COMPOSITE2,
+ .vmux = 1,
+ },{
+ .type = CX88_VMUX_COMPOSITE3,
+ .vmux = 2,
+ },{
+ .type = CX88_VMUX_COMPOSITE4,
+ .vmux = 3,
+ }},
+ },
+ [CX88_BOARD_HAUPPAUGE] = {
+ .name = "Hauppauge WinTV 34xxx models",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0xff00, // internal decoder
+ },{
+ .type = CX88_VMUX_DEBUG,
+ .vmux = 0,
+ .gpio0 = 0xff01, // mono from tuner chip
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0xff02,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0xff02,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0xff01,
+ },
+ },
+ [CX88_BOARD_GDI] = {
+ .name = "GDI Black Gold",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ }},
+ },
+ [CX88_BOARD_PIXELVIEW] = {
+ .name = "PixelView",
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0xff00, // internal decoder
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0xff10,
+ },
+ },
+ [CX88_BOARD_ATI_WONDER_PRO] = {
+ .name = "ATI TV Wonder Pro",
+ .tuner_type = TUNER_PHILIPS_4IN1,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x03ff,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x03fe,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x03fe,
+ }},
+ },
+ [CX88_BOARD_WINFAST2000XP_EXPERT] = {
+ .name = "Leadtek Winfast 2000XP Expert",
+ .tuner_type = TUNER_PHILIPS_4IN1,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x00F5e700,
+ .gpio1 = 0x00003004,
+ .gpio2 = 0x00F5e700,
+ .gpio3 = 0x02000000,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x00F5c700,
+ .gpio1 = 0x00003004,
+ .gpio2 = 0x00F5c700,
+ .gpio3 = 0x02000000,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x00F5c700,
+ .gpio1 = 0x00003004,
+ .gpio2 = 0x00F5c700,
+ .gpio3 = 0x02000000,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x00F5d700,
+ .gpio1 = 0x00003004,
+ .gpio2 = 0x00F5d700,
+ .gpio3 = 0x02000000,
+ },
+ },
+ [CX88_BOARD_AVERTV_STUDIO_303] = {
+ .name = "AverTV Studio 303 (M126)",
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio1 = 0xe09f,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio1 = 0xe05f,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio1 = 0xe05f,
+ }},
+ .radio = {
+ .gpio1 = 0xe0df,
+ .type = CX88_RADIO,
+ },
+ },
+ [CX88_BOARD_MSI_TVANYWHERE_MASTER] = {
+ // added gpio values thanks to Michal
+ // values for PAL from DScaler
+ .name = "MSI TV-@nywhere Master",
+ .tuner_type = TUNER_MT2032,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER_NTSC,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x000040bf,
+ .gpio1 = 0x000080c0,
+ .gpio2 = 0x0000ff40,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x000040bf,
+ .gpio1 = 0x000080c0,
+ .gpio2 = 0x0000ff40,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x000040bf,
+ .gpio1 = 0x000080c0,
+ .gpio2 = 0x0000ff40,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .vmux = 3,
+ .gpio0 = 0x000040bf,
+ .gpio1 = 0x000080c0,
+ .gpio2 = 0x0000ff20,
+ },
+ },
+ [CX88_BOARD_WINFAST_DV2000] = {
+ .name = "Leadtek Winfast DV2000",
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0035e700,
+ .gpio1 = 0x00003004,
+ .gpio2 = 0x0035e700,
+ .gpio3 = 0x02000000,
+ },{
+
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0035c700,
+ .gpio1 = 0x00003004,
+ .gpio2 = 0x0035c700,
+ .gpio3 = 0x02000000,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0035c700,
+ .gpio1 = 0x0035c700,
+ .gpio2 = 0x02000000,
+ .gpio3 = 0x02000000,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x0035d700,
+ .gpio1 = 0x00007004,
+ .gpio2 = 0x0035d700,
+ .gpio3 = 0x02000000,
+ },
+ },
+ [CX88_BOARD_LEADTEK_PVR2000] = {
+ // gpio values for PAL version from regspy by DScaler
+ .name = "Leadtek PVR 2000",
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0000bde2,
+ .audioroute = 1,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0000bde6,
+ .audioroute = 1,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0000bde6,
+ .audioroute = 1,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x0000bd62,
+ .audioroute = 1,
+ },
+ .mpeg = CX88_MPEG_BLACKBIRD,
+ },
+ [CX88_BOARD_IODATA_GVVCP3PCI] = {
+ .name = "IODATA GV-VCP3/PCI",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 0,
+ },{
+ .type = CX88_VMUX_COMPOSITE2,
+ .vmux = 1,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ }},
+ },
+ [CX88_BOARD_PROLINK_PLAYTVPVR] = {
+ .name = "Prolink PlayTV PVR",
+ .tuner_type = TUNER_PHILIPS_FM1236_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0xbff0,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0xbff3,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0xbff3,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0xbff0,
+ },
+ },
+ [CX88_BOARD_ASUS_PVR_416] = {
+ .name = "ASUS PVR-416",
+ .tuner_type = TUNER_PHILIPS_FM1236_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0000fde6,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0000fde6, // 0x0000fda6 L,R RCA audio in?
+ .audioroute = 1,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x0000fde2,
+ },
+ .mpeg = CX88_MPEG_BLACKBIRD,
+ },
+ [CX88_BOARD_MSI_TVANYWHERE] = {
+ .name = "MSI TV-@nywhere",
+ .tuner_type = TUNER_MT2032,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x00000fbf,
+ .gpio2 = 0x0000fc08,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x00000fbf,
+ .gpio2 = 0x0000fc68,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x00000fbf,
+ .gpio2 = 0x0000fc68,
+ }},
+ },
+ [CX88_BOARD_KWORLD_DVB_T] = {
+ .name = "KWorld/VStream XPert DVB-T",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0700,
+ .gpio2 = 0x0101,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0700,
+ .gpio2 = 0x0101,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1] = {
+ .name = "DViCO FusionHDTV DVB-T1",
+ .tuner_type = TUNER_ABSENT, /* No analog tuner */
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x000027df,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x000027df,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_KWORLD_LTV883] = {
+ .name = "KWorld LTV883RF",
+ .tuner_type = TUNER_TNF_8831BGFF,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x07f8,
+ },{
+ .type = CX88_VMUX_DEBUG,
+ .vmux = 0,
+ .gpio0 = 0x07f9, // mono from tuner chip
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x000007fa,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x000007fa,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x000007f8,
+ },
+ },
+ [CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q] = {
+ .name = "DViCO FusionHDTV 3 Gold-Q",
+ .tuner_type = TUNER_MICROTUNE_4042FI5,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ /*
+ GPIO[0] resets DT3302 DTV receiver
+ 0 - reset asserted
+ 1 - normal operation
+ GPIO[1] mutes analog audio output connector
+ 0 - enable selected source
+ 1 - mute
+ GPIO[2] selects source for analog audio output connector
+ 0 - analog audio input connector on tab
+ 1 - analog DAC output from CX23881 chip
+ GPIO[3] selects RF input connector on tuner module
+ 0 - RF connector labeled CABLE
+ 1 - RF connector labeled ANT
+ GPIO[4] selects high RF for QAM256 mode
+ 0 - normal RF
+ 1 - high RF
+ */
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0f0d,
+ },{
+ .type = CX88_VMUX_CABLE,
+ .vmux = 0,
+ .gpio0 = 0x0f05,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0f00,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0f00,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_HAUPPAUGE_DVB_T1] = {
+ .name = "Hauppauge Nova-T DVB-T",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_CONEXANT_DVB_T1] = {
+ .name = "Conexant DVB-T reference design",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_PROVIDEO_PV259] = {
+ .name = "Provideo PV259",
+ .tuner_type = TUNER_PHILIPS_FQ1216ME,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .audioroute = 1,
+ }},
+ .mpeg = CX88_MPEG_BLACKBIRD,
+ },
+ [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS] = {
+ .name = "DViCO FusionHDTV DVB-T Plus",
+ .tuner_type = TUNER_ABSENT, /* No analog tuner */
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x000027df,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x000027df,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_DNTV_LIVE_DVB_T] = {
+ .name = "digitalnow DNTV Live! DVB-T",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x00000700,
+ .gpio2 = 0x00000101,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x00000700,
+ .gpio2 = 0x00000101,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_PCHDTV_HD3000] = {
+ .name = "pcHDTV HD3000 HDTV",
+ .tuner_type = TUNER_THOMSON_DTT761X,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ /* GPIO[2] = audio source for analog audio out connector
+ * 0 = analog audio input connector
+ * 1 = CX88 audio DACs
+ *
+ * GPIO[7] = input to CX88's audio/chroma ADC
+ * 0 = FM 10.7 MHz IF
+ * 1 = Sound 4.5 MHz IF
+ *
+ * GPIO[1,5,6] = Oren 51132 pins 27,35,28 respectively
+ *
+ * GPIO[16] = Remote control input
+ */
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x00008484,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x00008400,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x00008400,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x00008404,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_HAUPPAUGE_ROSLYN] = {
+ // entry added by Kaustubh D. Bhalerao <bhalerao.1@osu.edu>
+ // GPIO values obtained from regspy, courtesy Sean Covel
+ .name = "Hauppauge WinTV 28xxx (Roslyn) models",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0xed1a,
+ .gpio2 = 0x00ff,
+ },{
+ .type = CX88_VMUX_DEBUG,
+ .vmux = 0,
+ .gpio0 = 0xff01,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0xff02,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0xed92,
+ .gpio2 = 0x00ff,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0xed96,
+ .gpio2 = 0x00ff,
+ },
+ .mpeg = CX88_MPEG_BLACKBIRD,
+ },
+ [CX88_BOARD_DIGITALLOGIC_MEC] = {
+ .name = "Digital-Logic MICROSPACE Entertainment Center (MEC)",
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x00009d80,
+ .audioroute = 1,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x00009d76,
+ .audioroute = 1,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x00009d76,
+ .audioroute = 1,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x00009d00,
+ .audioroute = 1,
+ },
+ .mpeg = CX88_MPEG_BLACKBIRD,
+ },
+ [CX88_BOARD_IODATA_GVBCTV7E] = {
+ .name = "IODATA GV/BCTV7E",
+ .tuner_type = TUNER_PHILIPS_FQ1286,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 1,
+ .gpio1 = 0x0000e03f,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 2,
+ .gpio1 = 0x0000e07f,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 3,
+ .gpio1 = 0x0000e07f,
+ }}
+ },
+ [CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO] = {
+ .name = "PixelView PlayTV Ultra Pro (Stereo)",
+ /* May be also TUNER_YMEC_TVF_5533MF for NTSC/M or PAL/M */
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ /* Some variants use a tda9874 and so need the tvaudio module. */
+ .audio_chip = V4L2_IDENT_TVAUDIO,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0xbf61, /* internal decoder */
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0xbf63,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0xbf63,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0xbf60,
+ },
+ },
+ [CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T] = {
+ .name = "DViCO FusionHDTV 3 Gold-T",
+ .tuner_type = TUNER_THOMSON_DTT761X,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x97ed,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x97e9,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x97e9,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_ADSTECH_DVB_T_PCI] = {
+ .name = "ADS Tech Instant TV DVB-T PCI",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0700,
+ .gpio2 = 0x0101,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0700,
+ .gpio2 = 0x0101,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1] = {
+ .name = "TerraTec Cinergy 1400 DVB-T",
+ .tuner_type = TUNER_ABSENT,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 2,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD] = {
+ .name = "DViCO FusionHDTV 5 Gold",
+ .tuner_type = TUNER_LG_TDVS_H06XF, /* TDVS-H062F */
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x87fd,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x87f9,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x87f9,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_AVERMEDIA_ULTRATV_MC_550] = {
+ .name = "AverMedia UltraTV Media Center PCI 550",
+ .tuner_type = TUNER_PHILIPS_FM1236_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 0,
+ .gpio0 = 0x0000cd73,
+ .audioroute = 1,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 1,
+ .gpio0 = 0x0000cd73,
+ .audioroute = 1,
+ },{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 3,
+ .gpio0 = 0x0000cdb3,
+ .audioroute = 1,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .vmux = 2,
+ .gpio0 = 0x0000cdf3,
+ .audioroute = 1,
+ },
+ .mpeg = CX88_MPEG_BLACKBIRD,
+ },
+ [CX88_BOARD_KWORLD_VSTREAM_EXPERT_DVD] = {
+ /* Alexander Wold <awold@bigfoot.com> */
+ .name = "Kworld V-Stream Xpert DVD",
+ .tuner_type = UNSET,
+ .input = {{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x03000000,
+ .gpio1 = 0x01000000,
+ .gpio2 = 0x02000000,
+ .gpio3 = 0x00100000,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x03000000,
+ .gpio1 = 0x01000000,
+ .gpio2 = 0x02000000,
+ .gpio3 = 0x00100000,
+ }},
+ },
+ [CX88_BOARD_ATI_HDTVWONDER] = {
+ .name = "ATI HDTV Wonder",
+ .tuner_type = TUNER_PHILIPS_TUV1236D,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x00000ff7,
+ .gpio1 = 0x000000ff,
+ .gpio2 = 0x00000001,
+ .gpio3 = 0x00000000,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x00000ffe,
+ .gpio1 = 0x000000ff,
+ .gpio2 = 0x00000001,
+ .gpio3 = 0x00000000,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x00000ffe,
+ .gpio1 = 0x000000ff,
+ .gpio2 = 0x00000001,
+ .gpio3 = 0x00000000,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_WINFAST_DTV1000] = {
+ .name = "WinFast DTV1000-T",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_AVERTV_303] = {
+ .name = "AVerTV 303 (M126)",
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x00ff,
+ .gpio1 = 0xe09f,
+ .gpio2 = 0x0010,
+ .gpio3 = 0x0000,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x00ff,
+ .gpio1 = 0xe05f,
+ .gpio2 = 0x0010,
+ .gpio3 = 0x0000,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x00ff,
+ .gpio1 = 0xe05f,
+ .gpio2 = 0x0010,
+ .gpio3 = 0x0000,
+ }},
+ },
+ [CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1] = {
+ .name = "Hauppauge Nova-S-Plus DVB-S",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .audio_chip = V4L2_IDENT_WM8775,
+ .i2sinputcntl = 2,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ /* 2: Line-In */
+ .audioroute = 2,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ /* 2: Line-In */
+ .audioroute = 2,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ /* 2: Line-In */
+ .audioroute = 2,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_HAUPPAUGE_NOVASE2_S1] = {
+ .name = "Hauppauge Nova-SE2 DVB-S",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_KWORLD_DVBS_100] = {
+ .name = "KWorld DVB-S 100",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .audio_chip = V4L2_IDENT_WM8775,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ /* 2: Line-In */
+ .audioroute = 2,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ /* 2: Line-In */
+ .audioroute = 2,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ /* 2: Line-In */
+ .audioroute = 2,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_HAUPPAUGE_HVR1100] = {
+ .name = "Hauppauge WinTV-HVR1100 DVB-T/Hybrid",
+ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ }},
+ /* fixme: Add radio support */
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_HAUPPAUGE_HVR1100LP] = {
+ .name = "Hauppauge WinTV-HVR1100 DVB-T/Hybrid (Low Profile)",
+ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ }},
+ /* fixme: Add radio support */
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_DNTV_LIVE_DVB_T_PRO] = {
+ .name = "digitalnow DNTV Live! DVB-T Pro",
+ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE |
+ TDA9887_PORT2_ACTIVE,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0xf80808,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0xf80808,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0xf80808,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0xf80808,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_KWORLD_DVB_T_CX22702] = {
+ /* Kworld V-stream Xpert DVB-T with Thomson tuner */
+ /* DTT 7579 Conexant CX22702-19 Conexant CX2388x */
+ /* Manenti Marco <marco_manenti@colman.it> */
+ .name = "KWorld/VStream XPert DVB-T with cx22702",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0700,
+ .gpio2 = 0x0101,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0700,
+ .gpio2 = 0x0101,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL] = {
+ .name = "DViCO FusionHDTV DVB-T Dual Digital",
+ .tuner_type = TUNER_ABSENT, /* No analog tuner */
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x000067df,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x000067df,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT] = {
+ .name = "KWorld HardwareMpegTV XPert",
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x3de2,
+ .gpio2 = 0x00ff,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x3de6,
+ .audioroute = 1,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x3de6,
+ .audioroute = 1,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x3de6,
+ .gpio2 = 0x00ff,
+ },
+ .mpeg = CX88_MPEG_BLACKBIRD,
+ },
+ [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID] = {
+ .name = "DViCO FusionHDTV DVB-T Hybrid",
+ .tuner_type = TUNER_THOMSON_FE6600,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0000a75f,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0000a75b,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0000a75b,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_PCHDTV_HD5500] = {
+ .name = "pcHDTV HD5500 HDTV",
+ .tuner_type = TUNER_LG_TDVS_H06XF, /* TDVS-H064F */
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x87fd,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x87f9,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x87f9,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_KWORLD_MCE200_DELUXE] = {
+ /* FIXME: tested TV input only, disabled composite,
+ svideo and radio until they can be tested also. */
+ .name = "Kworld MCE 200 Deluxe",
+ .tuner_type = TUNER_TENA_9533_DI,
+ .radio_type = UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0000BDE6
+ }},
+ .mpeg = CX88_MPEG_BLACKBIRD,
+ },
+ [CX88_BOARD_PIXELVIEW_PLAYTV_P7000] = {
+ /* FIXME: SVideo, Composite and FM inputs are untested */
+ .name = "PixelView PlayTV P7000",
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE |
+ TDA9887_PORT2_ACTIVE,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x5da6,
+ }},
+ .mpeg = CX88_MPEG_BLACKBIRD,
+ },
+ [CX88_BOARD_NPGTECH_REALTV_TOP10FM] = {
+ .name = "NPG Tech Real TV FM Top 10",
+ .tuner_type = TUNER_TNF_5335MF, /* Actually a TNF9535 */
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0788,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x078b,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x078b,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x074a,
+ },
+ },
+ [CX88_BOARD_WINFAST_DTV2000H] = {
+ .name = "WinFast DTV2000 H",
+ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x00017304,
+ .gpio1 = 0x00008203,
+ .gpio2 = 0x00017304,
+ .gpio3 = 0x02000000,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0001d701,
+ .gpio1 = 0x0000b207,
+ .gpio2 = 0x0001d701,
+ .gpio3 = 0x02000000,
+ }, {
+ .type = CX88_VMUX_COMPOSITE2,
+ .vmux = 2,
+ .gpio0 = 0x0001d503,
+ .gpio1 = 0x0000b207,
+ .gpio2 = 0x0001d503,
+ .gpio3 = 0x02000000,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 3,
+ .gpio0 = 0x0001d701,
+ .gpio1 = 0x0000b207,
+ .gpio2 = 0x0001d701,
+ .gpio3 = 0x02000000,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x00015702,
+ .gpio1 = 0x0000f207,
+ .gpio2 = 0x00015702,
+ .gpio3 = 0x02000000,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_WINFAST_DTV2000H_J] = {
+ .name = "WinFast DTV2000 H rev. J",
+ .tuner_type = TUNER_PHILIPS_FMD1216MEX_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x00017300,
+ .gpio1 = 0x00008207,
+ .gpio2 = 0x00000000,
+ .gpio3 = 0x02000000,
+ },{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x00018300,
+ .gpio1 = 0x0000f207,
+ .gpio2 = 0x00017304,
+ .gpio3 = 0x02000000,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x00018301,
+ .gpio1 = 0x0000f207,
+ .gpio2 = 0x00017304,
+ .gpio3 = 0x02000000,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x00018301,
+ .gpio1 = 0x0000f207,
+ .gpio2 = 0x00017304,
+ .gpio3 = 0x02000000,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x00015702,
+ .gpio1 = 0x0000f207,
+ .gpio2 = 0x00015702,
+ .gpio3 = 0x02000000,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_GENIATECH_DVBS] = {
+ .name = "Geniatech DVB-S",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_HAUPPAUGE_HVR3000] = {
+ .name = "Hauppauge WinTV-HVR3000 TriMode Analog/DVB-S/DVB-T",
+ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .audio_chip = V4L2_IDENT_WM8775,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x84bf,
+ /* 1: TV Audio / FM Mono */
+ .audioroute = 1,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x84bf,
+ /* 2: Line-In */
+ .audioroute = 2,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x84bf,
+ /* 2: Line-In */
+ .audioroute = 2,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x84bf,
+ /* 4: FM Stereo (untested) */
+ .audioroute = 8,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ .num_frontends = 2,
+ },
+ [CX88_BOARD_NORWOOD_MICRO] = {
+ .name = "Norwood Micro TV Tuner",
+ .tuner_type = TUNER_TNF_5335MF,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0709,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x070b,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x070b,
+ }},
+ },
+ [CX88_BOARD_TE_DTV_250_OEM_SWANN] = {
+ .name = "Shenzhen Tungsten Ages Tech TE-DTV-250 / Swann OEM",
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x003fffff,
+ .gpio1 = 0x00e00000,
+ .gpio2 = 0x003fffff,
+ .gpio3 = 0x02000000,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x003fffff,
+ .gpio1 = 0x00e00000,
+ .gpio2 = 0x003fffff,
+ .gpio3 = 0x02000000,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x003fffff,
+ .gpio1 = 0x00e00000,
+ .gpio2 = 0x003fffff,
+ .gpio3 = 0x02000000,
+ }},
+ },
+ [CX88_BOARD_HAUPPAUGE_HVR1300] = {
+ .name = "Hauppauge WinTV-HVR1300 DVB-T/Hybrid MPEG Encoder",
+ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .audio_chip = V4L2_IDENT_WM8775,
+ /*
+ * gpio0 as reported by Mike Crash <mike AT mikecrash.com>
+ */
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0xef88,
+ /* 1: TV Audio / FM Mono */
+ .audioroute = 1,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0xef88,
+ /* 2: Line-In */
+ .audioroute = 2,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0xef88,
+ /* 2: Line-In */
+ .audioroute = 2,
+ }},
+ .mpeg = CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD,
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0xef88,
+ /* 4: FM Stereo (untested) */
+ .audioroute = 8,
+ },
+ },
+ [CX88_BOARD_SAMSUNG_SMT_7020] = {
+ .name = "Samsung SMT 7020 DVB-S",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = { {
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_ADSTECH_PTV_390] = {
+ .name = "ADS Tech Instant Video PCI",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DEBUG,
+ .vmux = 3,
+ .gpio0 = 0x04ff,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x07fa,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x07fa,
+ }},
+ },
+ [CX88_BOARD_PINNACLE_PCTV_HD_800i] = {
+ .name = "Pinnacle PCTV HD 800i",
+ .tuner_type = TUNER_XC5000,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x04fb,
+ .gpio1 = 0x10ff,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x04fb,
+ .gpio1 = 0x10ef,
+ .audioroute = 1,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x04fb,
+ .gpio1 = 0x10ef,
+ .audioroute = 1,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO] = {
+ .name = "DViCO FusionHDTV 5 PCI nano",
+ /* xc3008 tuner, digital only for now */
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x000027df, /* Unconfirmed */
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x000027df, /* Unconfirmed */
+ .audioroute = 1,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x000027df, /* Unconfirmed */
+ .audioroute = 1,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_PINNACLE_HYBRID_PCTV] = {
+ .name = "Pinnacle Hybrid PCTV",
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0x61,
+ .radio_type = UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = { {
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x004ff,
+ .gpio1 = 0x010ff,
+ .gpio2 = 0x00001,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x004fb,
+ .gpio1 = 0x010ef,
+ .audioroute = 1,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x004fb,
+ .gpio1 = 0x010ef,
+ .audioroute = 1,
+ } },
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x004ff,
+ .gpio1 = 0x010ff,
+ .gpio2 = 0x0ff,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ /* Terry Wu <terrywu2009@gmail.com> */
+ /* TV Audio : set GPIO 2, 18, 19 value to 0, 1, 0 */
+ /* FM Audio : set GPIO 2, 18, 19 value to 0, 0, 0 */
+ /* Line-in Audio : set GPIO 2, 18, 19 value to 0, 1, 1 */
+ /* Mute Audio : set GPIO 2 value to 1 */
+ [CX88_BOARD_WINFAST_TV2000_XP_GLOBAL] = {
+ .name = "Leadtek TV2000 XP Global",
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0x61,
+ .radio_type = UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = { {
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x0000,
+ .gpio2 = 0x0C04, /* pin 18 = 1, pin 19 = 0 */
+ .gpio3 = 0x0000,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x0000,
+ .gpio2 = 0x0C0C, /* pin 18 = 1, pin 19 = 1 */
+ .gpio3 = 0x0000,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x0000,
+ .gpio2 = 0x0C0C, /* pin 18 = 1, pin 19 = 1 */
+ .gpio3 = 0x0000,
+ } },
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x0000,
+ .gpio2 = 0x0C00, /* pin 18 = 0, pin 19 = 0 */
+ .gpio3 = 0x0000,
+ },
+ },
+ [CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36] = {
+ .name = "Leadtek TV2000 XP Global (SC4100)",
+ .tuner_type = TUNER_XC4000,
+ .tuner_addr = 0x61,
+ .radio_type = UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = { {
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x0000,
+ .gpio2 = 0x0C04, /* pin 18 = 1, pin 19 = 0 */
+ .gpio3 = 0x0000,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x0000,
+ .gpio2 = 0x0C0C, /* pin 18 = 1, pin 19 = 1 */
+ .gpio3 = 0x0000,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x0000,
+ .gpio2 = 0x0C0C, /* pin 18 = 1, pin 19 = 1 */
+ .gpio3 = 0x0000,
+ } },
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x0000,
+ .gpio2 = 0x0C00, /* pin 18 = 0, pin 19 = 0 */
+ .gpio3 = 0x0000,
+ },
+ },
+ [CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43] = {
+ .name = "Leadtek TV2000 XP Global (XC4100)",
+ .tuner_type = TUNER_XC4000,
+ .tuner_addr = 0x61,
+ .radio_type = UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = { {
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x6040, /* pin 14 = 1, pin 13 = 0 */
+ .gpio2 = 0x0000,
+ .gpio3 = 0x0000,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x6060, /* pin 14 = 1, pin 13 = 1 */
+ .gpio2 = 0x0000,
+ .gpio3 = 0x0000,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x6060, /* pin 14 = 1, pin 13 = 1 */
+ .gpio2 = 0x0000,
+ .gpio3 = 0x0000,
+ } },
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x6000, /* pin 14 = 1, pin 13 = 0 */
+ .gpio2 = 0x0000,
+ .gpio3 = 0x0000,
+ },
+ },
+ [CX88_BOARD_POWERCOLOR_REAL_ANGEL] = {
+ .name = "PowerColor RA330", /* Long names may confuse LIRC. */
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0x61,
+ .input = { {
+ .type = CX88_VMUX_DEBUG,
+ .vmux = 3, /* Due to the way the cx88 driver is written, */
+ .gpio0 = 0x00ff, /* there is no way to deactivate audio pass- */
+ .gpio1 = 0xf39d, /* through without this entry. Furthermore, if */
+ .gpio3 = 0x0000, /* the TV mux entry is first, you get audio */
+ }, { /* from the tuner on boot for a little while. */
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x00ff,
+ .gpio1 = 0xf35d,
+ .gpio3 = 0x0000,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x00ff,
+ .gpio1 = 0xf37d,
+ .gpio3 = 0x0000,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x000ff,
+ .gpio1 = 0x0f37d,
+ .gpio3 = 0x00000,
+ } },
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x000ff,
+ .gpio1 = 0x0f35d,
+ .gpio3 = 0x00000,
+ },
+ },
+ [CX88_BOARD_GENIATECH_X8000_MT] = {
+ /* Also PowerColor Real Angel 330 and Geniatech X800 OEM */
+ .name = "Geniatech X8000-MT DVBT",
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0x61,
+ .input = { {
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x00000000,
+ .gpio1 = 0x00e3e341,
+ .gpio2 = 0x00000000,
+ .gpio3 = 0x00000000,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x00000000,
+ .gpio1 = 0x00e3e361,
+ .gpio2 = 0x00000000,
+ .gpio3 = 0x00000000,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x00000000,
+ .gpio1 = 0x00e3e361,
+ .gpio2 = 0x00000000,
+ .gpio3 = 0x00000000,
+ } },
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x00000000,
+ .gpio1 = 0x00e3e341,
+ .gpio2 = 0x00000000,
+ .gpio3 = 0x00000000,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO] = {
+ .name = "DViCO FusionHDTV DVB-T PRO",
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0x61,
+ .radio_type = UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = { {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x000067df,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x000067df,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD] = {
+ .name = "DViCO FusionHDTV 7 Gold",
+ .tuner_type = TUNER_XC5000,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x10df,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x16d9,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x16d9,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_PROLINK_PV_8000GT] = {
+ .name = "Prolink Pixelview MPEG 8000GT",
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0x61,
+ .input = { {
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0ff,
+ .gpio2 = 0x0cfb,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio2 = 0x0cfb,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio2 = 0x0cfb,
+ } },
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio2 = 0x0cfb,
+ },
+ },
+ [CX88_BOARD_PROLINK_PV_GLOBAL_XTREME] = {
+ .name = "Prolink Pixelview Global Extreme",
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0x61,
+ .input = { {
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x04fb,
+ .gpio1 = 0x04080,
+ .gpio2 = 0x0cf7,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x04fb,
+ .gpio1 = 0x04080,
+ .gpio2 = 0x0cfb,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x04fb,
+ .gpio1 = 0x04080,
+ .gpio2 = 0x0cfb,
+ } },
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x04ff,
+ .gpio1 = 0x04080,
+ .gpio2 = 0x0cf7,
+ },
+ },
+ /* Both radio, analog and ATSC work with this board.
+ However, for analog to work, s5h1409 gate should be open,
+ otherwise, tuner-xc3028 won't be detected.
+ A proper fix require using the newer i2c methods to add
+ tuner-xc3028 without doing an i2c probe.
+ */
+ [CX88_BOARD_KWORLD_ATSC_120] = {
+ .name = "Kworld PlusTV HD PCI 120 (ATSC 120)",
+ .tuner_type = TUNER_XC2028,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = { {
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x000000ff,
+ .gpio1 = 0x0000f35d,
+ .gpio2 = 0x00000000,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x000000ff,
+ .gpio1 = 0x0000f37e,
+ .gpio2 = 0x00000000,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x000000ff,
+ .gpio1 = 0x0000f37e,
+ .gpio2 = 0x00000000,
+ } },
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x000000ff,
+ .gpio1 = 0x0000f35d,
+ .gpio2 = 0x00000000,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_HAUPPAUGE_HVR4000] = {
+ .name = "Hauppauge WinTV-HVR4000 DVB-S/S2/T/Hybrid",
+ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .audio_chip = V4L2_IDENT_WM8775,
+ /*
+ * GPIO0 (WINTV2000)
+ *
+ * Analogue SAT DVB-T
+ * Antenna 0xc4bf 0xc4bb
+ * Composite 0xc4bf 0xc4bb
+ * S-Video 0xc4bf 0xc4bb
+ * Composite1 0xc4ff 0xc4fb
+ * S-Video1 0xc4ff 0xc4fb
+ *
+ * BIT VALUE FUNCTION GP{x}_IO
+ * 0 1 I:?
+ * 1 1 I:?
+ * 2 1 O:MPEG PORT 0=DVB-T 1=DVB-S
+ * 3 1 I:?
+ * 4 1 I:?
+ * 5 1 I:?
+ * 6 0 O:INPUT SELECTOR 0=INTERNAL 1=EXPANSION
+ * 7 1 O:DVB-T DEMOD RESET LOW
+ *
+ * BIT VALUE FUNCTION GP{x}_OE
+ * 8 0 I
+ * 9 0 I
+ * a 1 O
+ * b 0 I
+ * c 0 I
+ * d 0 I
+ * e 1 O
+ * f 1 O
+ *
+ * WM8775 ADC
+ *
+ * 1: TV Audio / FM Mono
+ * 2: Line-In
+ * 3: Line-In Expansion
+ * 4: FM Stereo
+ */
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0xc4bf,
+ /* 1: TV Audio / FM Mono */
+ .audioroute = 1,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0xc4bf,
+ /* 2: Line-In */
+ .audioroute = 2,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0xc4bf,
+ /* 2: Line-In */
+ .audioroute = 2,
+ } },
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0xc4bf,
+ /* 4: FM Stereo */
+ .audioroute = 8,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ .num_frontends = 2,
+ },
+ [CX88_BOARD_HAUPPAUGE_HVR4000LITE] = {
+ .name = "Hauppauge WinTV-HVR4000(Lite) DVB-S/S2",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_TEVII_S420] = {
+ .name = "TeVii S420 DVB-S",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_TEVII_S460] = {
+ .name = "TeVii S460 DVB-S/S2",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_TEVII_S464] = {
+ .name = "TeVii S464 DVB-S/S2",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_OMICOM_SS4_PCI] = {
+ .name = "Omicom SS4 DVB-S/S2 PCI",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_TBS_8910] = {
+ .name = "TBS 8910 DVB-S",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_TBS_8920] = {
+ .name = "TBS 8920 DVB-S/S2",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ .gpio0 = 0x8080,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_PROF_6200] = {
+ .name = "Prof 6200 DVB-S",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_PROF_7300] = {
+ .name = "PROF 7300 DVB-S/S2",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_SATTRADE_ST4200] = {
+ .name = "SATTRADE ST4200 DVB-S/S2",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII] = {
+ .name = "Terratec Cinergy HT PCI MKII",
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0x61,
+ .radio_type = UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = { {
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x004ff,
+ .gpio1 = 0x010ff,
+ .gpio2 = 0x00001,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x004fb,
+ .gpio1 = 0x010ef,
+ .audioroute = 1,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x004fb,
+ .gpio1 = 0x010ef,
+ .audioroute = 1,
+ } },
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x004ff,
+ .gpio1 = 0x010ff,
+ .gpio2 = 0x0ff,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_HAUPPAUGE_IRONLY] = {
+ .name = "Hauppauge WinTV-IR Only",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ },
+ [CX88_BOARD_WINFAST_DTV1800H] = {
+ .name = "Leadtek WinFast DTV1800 Hybrid",
+ .tuner_type = TUNER_XC2028,
+ .radio_type = UNSET,
+ .tuner_addr = 0x61,
+ .radio_addr = ADDR_UNSET,
+ /*
+ * GPIO setting
+ *
+ * 2: mute (0=off,1=on)
+ * 12: tuner reset pin
+ * 13: audio source (0=tuner audio,1=line in)
+ * 14: FM (0=on,1=off ???)
+ */
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x6040, /* pin 13 = 0, pin 14 = 1 */
+ .gpio2 = 0x0000,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x6060, /* pin 13 = 1, pin 14 = 1 */
+ .gpio2 = 0x0000,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x6060, /* pin 13 = 1, pin 14 = 1 */
+ .gpio2 = 0x0000,
+ } },
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x6000, /* pin 13 = 0, pin 14 = 0 */
+ .gpio2 = 0x0000,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_WINFAST_DTV1800H_XC4000] = {
+ .name = "Leadtek WinFast DTV1800 H (XC4000)",
+ .tuner_type = TUNER_XC4000,
+ .radio_type = UNSET,
+ .tuner_addr = 0x61,
+ .radio_addr = ADDR_UNSET,
+ /*
+ * GPIO setting
+ *
+ * 2: mute (0=off,1=on)
+ * 12: tuner reset pin
+ * 13: audio source (0=tuner audio,1=line in)
+ * 14: FM (0=on,1=off ???)
+ */
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x6040, /* pin 13 = 0, pin 14 = 1 */
+ .gpio2 = 0x0000,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x6060, /* pin 13 = 1, pin 14 = 1 */
+ .gpio2 = 0x0000,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x6060, /* pin 13 = 1, pin 14 = 1 */
+ .gpio2 = 0x0000,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x6000, /* pin 13 = 0, pin 14 = 0 */
+ .gpio2 = 0x0000,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_WINFAST_DTV2000H_PLUS] = {
+ .name = "Leadtek WinFast DTV2000 H PLUS",
+ .tuner_type = TUNER_XC4000,
+ .radio_type = UNSET,
+ .tuner_addr = 0x61,
+ .radio_addr = ADDR_UNSET,
+ /*
+ * GPIO
+ * 2: 1: mute audio
+ * 12: 0: reset XC4000
+ * 13: 1: audio input is line in (0: tuner)
+ * 14: 0: FM radio
+ * 16: 0: RF input is cable
+ */
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0403,
+ .gpio1 = 0xF0D7,
+ .gpio2 = 0x0101,
+ .gpio3 = 0x0000,
+ }, {
+ .type = CX88_VMUX_CABLE,
+ .vmux = 0,
+ .gpio0 = 0x0403,
+ .gpio1 = 0xF0D7,
+ .gpio2 = 0x0100,
+ .gpio3 = 0x0000,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0403, /* was 0x0407 */
+ .gpio1 = 0xF0F7,
+ .gpio2 = 0x0101,
+ .gpio3 = 0x0000,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0403, /* was 0x0407 */
+ .gpio1 = 0xF0F7,
+ .gpio2 = 0x0101,
+ .gpio3 = 0x0000,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x0403,
+ .gpio1 = 0xF097,
+ .gpio2 = 0x0100,
+ .gpio3 = 0x0000,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_PROF_7301] = {
+ .name = "Prof 7301 DVB-S/S2",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = { {
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_TWINHAN_VP1027_DVBS] = {
+ .name = "Twinhan VP-1027 DVB-S",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+};
+
+/* ------------------------------------------------------------------ */
+/* PCI subsystem IDs */
+
+static const struct cx88_subid cx88_subids[] = {
+ {
+ .subvendor = 0x0070,
+ .subdevice = 0x3400,
+ .card = CX88_BOARD_HAUPPAUGE,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x3401,
+ .card = CX88_BOARD_HAUPPAUGE,
+ },{
+ .subvendor = 0x14c7,
+ .subdevice = 0x0106,
+ .card = CX88_BOARD_GDI,
+ },{
+ .subvendor = 0x14c7,
+ .subdevice = 0x0107, /* with mpeg encoder */
+ .card = CX88_BOARD_GDI,
+ },{
+ .subvendor = PCI_VENDOR_ID_ATI,
+ .subdevice = 0x00f8,
+ .card = CX88_BOARD_ATI_WONDER_PRO,
+ }, {
+ .subvendor = PCI_VENDOR_ID_ATI,
+ .subdevice = 0x00f9,
+ .card = CX88_BOARD_ATI_WONDER_PRO,
+ }, {
+ .subvendor = 0x107d,
+ .subdevice = 0x6611,
+ .card = CX88_BOARD_WINFAST2000XP_EXPERT,
+ },{
+ .subvendor = 0x107d,
+ .subdevice = 0x6613, /* NTSC */
+ .card = CX88_BOARD_WINFAST2000XP_EXPERT,
+ },{
+ .subvendor = 0x107d,
+ .subdevice = 0x6620,
+ .card = CX88_BOARD_WINFAST_DV2000,
+ },{
+ .subvendor = 0x107d,
+ .subdevice = 0x663b,
+ .card = CX88_BOARD_LEADTEK_PVR2000,
+ },{
+ .subvendor = 0x107d,
+ .subdevice = 0x663c,
+ .card = CX88_BOARD_LEADTEK_PVR2000,
+ },{
+ .subvendor = 0x1461,
+ .subdevice = 0x000b,
+ .card = CX88_BOARD_AVERTV_STUDIO_303,
+ },{
+ .subvendor = 0x1462,
+ .subdevice = 0x8606,
+ .card = CX88_BOARD_MSI_TVANYWHERE_MASTER,
+ },{
+ .subvendor = 0x10fc,
+ .subdevice = 0xd003,
+ .card = CX88_BOARD_IODATA_GVVCP3PCI,
+ },{
+ .subvendor = 0x1043,
+ .subdevice = 0x4823, /* with mpeg encoder */
+ .card = CX88_BOARD_ASUS_PVR_416,
+ },{
+ .subvendor = 0x17de,
+ .subdevice = 0x08a6,
+ .card = CX88_BOARD_KWORLD_DVB_T,
+ },{
+ .subvendor = 0x18ac,
+ .subdevice = 0xd810,
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q,
+ },{
+ .subvendor = 0x18ac,
+ .subdevice = 0xd820,
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T,
+ },{
+ .subvendor = 0x18ac,
+ .subdevice = 0xdb00,
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9002,
+ .card = CX88_BOARD_HAUPPAUGE_DVB_T1,
+ },{
+ .subvendor = 0x14f1,
+ .subdevice = 0x0187,
+ .card = CX88_BOARD_CONEXANT_DVB_T1,
+ },{
+ .subvendor = 0x1540,
+ .subdevice = 0x2580,
+ .card = CX88_BOARD_PROVIDEO_PV259,
+ },{
+ .subvendor = 0x18ac,
+ .subdevice = 0xdb10,
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS,
+ },{
+ .subvendor = 0x1554,
+ .subdevice = 0x4811,
+ .card = CX88_BOARD_PIXELVIEW,
+ },{
+ .subvendor = 0x7063,
+ .subdevice = 0x3000, /* HD-3000 card */
+ .card = CX88_BOARD_PCHDTV_HD3000,
+ },{
+ .subvendor = 0x17de,
+ .subdevice = 0xa8a6,
+ .card = CX88_BOARD_DNTV_LIVE_DVB_T,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x2801,
+ .card = CX88_BOARD_HAUPPAUGE_ROSLYN,
+ },{
+ .subvendor = 0x14f1,
+ .subdevice = 0x0342,
+ .card = CX88_BOARD_DIGITALLOGIC_MEC,
+ },{
+ .subvendor = 0x10fc,
+ .subdevice = 0xd035,
+ .card = CX88_BOARD_IODATA_GVBCTV7E,
+ },{
+ .subvendor = 0x1421,
+ .subdevice = 0x0334,
+ .card = CX88_BOARD_ADSTECH_DVB_T_PCI,
+ },{
+ .subvendor = 0x153b,
+ .subdevice = 0x1166,
+ .card = CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1,
+ },{
+ .subvendor = 0x18ac,
+ .subdevice = 0xd500,
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD,
+ },{
+ .subvendor = 0x1461,
+ .subdevice = 0x8011,
+ .card = CX88_BOARD_AVERMEDIA_ULTRATV_MC_550,
+ },{
+ .subvendor = PCI_VENDOR_ID_ATI,
+ .subdevice = 0xa101,
+ .card = CX88_BOARD_ATI_HDTVWONDER,
+ },{
+ .subvendor = 0x107d,
+ .subdevice = 0x665f,
+ .card = CX88_BOARD_WINFAST_DTV1000,
+ },{
+ .subvendor = 0x1461,
+ .subdevice = 0x000a,
+ .card = CX88_BOARD_AVERTV_303,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9200,
+ .card = CX88_BOARD_HAUPPAUGE_NOVASE2_S1,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9201,
+ .card = CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9202,
+ .card = CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1,
+ },{
+ .subvendor = 0x17de,
+ .subdevice = 0x08b2,
+ .card = CX88_BOARD_KWORLD_DVBS_100,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9400,
+ .card = CX88_BOARD_HAUPPAUGE_HVR1100,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9402,
+ .card = CX88_BOARD_HAUPPAUGE_HVR1100,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9800,
+ .card = CX88_BOARD_HAUPPAUGE_HVR1100LP,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9802,
+ .card = CX88_BOARD_HAUPPAUGE_HVR1100LP,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9001,
+ .card = CX88_BOARD_HAUPPAUGE_DVB_T1,
+ },{
+ .subvendor = 0x1822,
+ .subdevice = 0x0025,
+ .card = CX88_BOARD_DNTV_LIVE_DVB_T_PRO,
+ },{
+ .subvendor = 0x17de,
+ .subdevice = 0x08a1,
+ .card = CX88_BOARD_KWORLD_DVB_T_CX22702,
+ },{
+ .subvendor = 0x18ac,
+ .subdevice = 0xdb50,
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL,
+ },{
+ .subvendor = 0x18ac,
+ .subdevice = 0xdb54,
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL,
+ /* Re-branded DViCO: DigitalNow DVB-T Dual */
+ },{
+ .subvendor = 0x18ac,
+ .subdevice = 0xdb11,
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS,
+ /* Re-branded DViCO: UltraView DVB-T Plus */
+ }, {
+ .subvendor = 0x18ac,
+ .subdevice = 0xdb30,
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO,
+ }, {
+ .subvendor = 0x17de,
+ .subdevice = 0x0840,
+ .card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT,
+ },{
+ .subvendor = 0x1421,
+ .subdevice = 0x0305,
+ .card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT,
+ },{
+ .subvendor = 0x18ac,
+ .subdevice = 0xdb40,
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID,
+ },{
+ .subvendor = 0x18ac,
+ .subdevice = 0xdb44,
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID,
+ },{
+ .subvendor = 0x7063,
+ .subdevice = 0x5500,
+ .card = CX88_BOARD_PCHDTV_HD5500,
+ },{
+ .subvendor = 0x17de,
+ .subdevice = 0x0841,
+ .card = CX88_BOARD_KWORLD_MCE200_DELUXE,
+ },{
+ .subvendor = 0x1822,
+ .subdevice = 0x0019,
+ .card = CX88_BOARD_DNTV_LIVE_DVB_T_PRO,
+ },{
+ .subvendor = 0x1554,
+ .subdevice = 0x4813,
+ .card = CX88_BOARD_PIXELVIEW_PLAYTV_P7000,
+ },{
+ .subvendor = 0x14f1,
+ .subdevice = 0x0842,
+ .card = CX88_BOARD_NPGTECH_REALTV_TOP10FM,
+ },{
+ .subvendor = 0x107d,
+ .subdevice = 0x665e,
+ .card = CX88_BOARD_WINFAST_DTV2000H,
+ },{
+ .subvendor = 0x107d,
+ .subdevice = 0x6f2b,
+ .card = CX88_BOARD_WINFAST_DTV2000H_J,
+ },{
+ .subvendor = 0x18ac,
+ .subdevice = 0xd800, /* FusionHDTV 3 Gold (original revision) */
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q,
+ },{
+ .subvendor = 0x14f1,
+ .subdevice = 0x0084,
+ .card = CX88_BOARD_GENIATECH_DVBS,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x1404,
+ .card = CX88_BOARD_HAUPPAUGE_HVR3000,
+ }, {
+ .subvendor = 0x18ac,
+ .subdevice = 0xdc00,
+ .card = CX88_BOARD_SAMSUNG_SMT_7020,
+ }, {
+ .subvendor = 0x18ac,
+ .subdevice = 0xdccd,
+ .card = CX88_BOARD_SAMSUNG_SMT_7020,
+ },{
+ .subvendor = 0x1461,
+ .subdevice = 0xc111, /* AverMedia M150-D */
+ /* This board is known to work with the ASUS PVR416 config */
+ .card = CX88_BOARD_ASUS_PVR_416,
+ },{
+ .subvendor = 0xc180,
+ .subdevice = 0xc980,
+ .card = CX88_BOARD_TE_DTV_250_OEM_SWANN,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9600,
+ .card = CX88_BOARD_HAUPPAUGE_HVR1300,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9601,
+ .card = CX88_BOARD_HAUPPAUGE_HVR1300,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9602,
+ .card = CX88_BOARD_HAUPPAUGE_HVR1300,
+ },{
+ .subvendor = 0x107d,
+ .subdevice = 0x6632,
+ .card = CX88_BOARD_LEADTEK_PVR2000,
+ },{
+ .subvendor = 0x12ab,
+ .subdevice = 0x2300, /* Club3D Zap TV2100 */
+ .card = CX88_BOARD_KWORLD_DVB_T_CX22702,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9000,
+ .card = CX88_BOARD_HAUPPAUGE_DVB_T1,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x1400,
+ .card = CX88_BOARD_HAUPPAUGE_HVR3000,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x1401,
+ .card = CX88_BOARD_HAUPPAUGE_HVR3000,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x1402,
+ .card = CX88_BOARD_HAUPPAUGE_HVR3000,
+ },{
+ .subvendor = 0x1421,
+ .subdevice = 0x0341, /* ADS Tech InstantTV DVB-S */
+ .card = CX88_BOARD_KWORLD_DVBS_100,
+ },{
+ .subvendor = 0x1421,
+ .subdevice = 0x0390,
+ .card = CX88_BOARD_ADSTECH_PTV_390,
+ },{
+ .subvendor = 0x11bd,
+ .subdevice = 0x0051,
+ .card = CX88_BOARD_PINNACLE_PCTV_HD_800i,
+ }, {
+ .subvendor = 0x18ac,
+ .subdevice = 0xd530,
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO,
+ }, {
+ .subvendor = 0x12ab,
+ .subdevice = 0x1788,
+ .card = CX88_BOARD_PINNACLE_HYBRID_PCTV,
+ }, {
+ .subvendor = 0x14f1,
+ .subdevice = 0xea3d,
+ .card = CX88_BOARD_POWERCOLOR_REAL_ANGEL,
+ }, {
+ .subvendor = 0x107d,
+ .subdevice = 0x6f18,
+ .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL,
+ }, {
+ .subvendor = 0x14f1,
+ .subdevice = 0x8852,
+ .card = CX88_BOARD_GENIATECH_X8000_MT,
+ }, {
+ .subvendor = 0x18ac,
+ .subdevice = 0xd610,
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD,
+ }, {
+ .subvendor = 0x1554,
+ .subdevice = 0x4935,
+ .card = CX88_BOARD_PROLINK_PV_8000GT,
+ }, {
+ .subvendor = 0x1554,
+ .subdevice = 0x4976,
+ .card = CX88_BOARD_PROLINK_PV_GLOBAL_XTREME,
+ }, {
+ .subvendor = 0x17de,
+ .subdevice = 0x08c1,
+ .card = CX88_BOARD_KWORLD_ATSC_120,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x6900,
+ .card = CX88_BOARD_HAUPPAUGE_HVR4000,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x6904,
+ .card = CX88_BOARD_HAUPPAUGE_HVR4000,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x6902,
+ .card = CX88_BOARD_HAUPPAUGE_HVR4000,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x6905,
+ .card = CX88_BOARD_HAUPPAUGE_HVR4000LITE,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x6906,
+ .card = CX88_BOARD_HAUPPAUGE_HVR4000LITE,
+ }, {
+ .subvendor = 0xd420,
+ .subdevice = 0x9022,
+ .card = CX88_BOARD_TEVII_S420,
+ }, {
+ .subvendor = 0xd460,
+ .subdevice = 0x9022,
+ .card = CX88_BOARD_TEVII_S460,
+ }, {
+ .subvendor = 0xd464,
+ .subdevice = 0x9022,
+ .card = CX88_BOARD_TEVII_S464,
+ }, {
+ .subvendor = 0xA044,
+ .subdevice = 0x2011,
+ .card = CX88_BOARD_OMICOM_SS4_PCI,
+ }, {
+ .subvendor = 0x8910,
+ .subdevice = 0x8888,
+ .card = CX88_BOARD_TBS_8910,
+ }, {
+ .subvendor = 0x8920,
+ .subdevice = 0x8888,
+ .card = CX88_BOARD_TBS_8920,
+ }, {
+ .subvendor = 0xb022,
+ .subdevice = 0x3022,
+ .card = CX88_BOARD_PROF_6200,
+ }, {
+ .subvendor = 0xB033,
+ .subdevice = 0x3033,
+ .card = CX88_BOARD_PROF_7300,
+ }, {
+ .subvendor = 0xb200,
+ .subdevice = 0x4200,
+ .card = CX88_BOARD_SATTRADE_ST4200,
+ }, {
+ .subvendor = 0x153b,
+ .subdevice = 0x1177,
+ .card = CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x9290,
+ .card = CX88_BOARD_HAUPPAUGE_IRONLY,
+ }, {
+ .subvendor = 0x107d,
+ .subdevice = 0x6654,
+ .card = CX88_BOARD_WINFAST_DTV1800H,
+ }, {
+ /* WinFast DTV1800 H with XC4000 tuner */
+ .subvendor = 0x107d,
+ .subdevice = 0x6f38,
+ .card = CX88_BOARD_WINFAST_DTV1800H_XC4000,
+ }, {
+ .subvendor = 0x107d,
+ .subdevice = 0x6f42,
+ .card = CX88_BOARD_WINFAST_DTV2000H_PLUS,
+ }, {
+ /* PVR2000 PAL Model [107d:6630] */
+ .subvendor = 0x107d,
+ .subdevice = 0x6630,
+ .card = CX88_BOARD_LEADTEK_PVR2000,
+ }, {
+ /* PVR2000 PAL Model [107d:6638] */
+ .subvendor = 0x107d,
+ .subdevice = 0x6638,
+ .card = CX88_BOARD_LEADTEK_PVR2000,
+ }, {
+ /* PVR2000 NTSC Model [107d:6631] */
+ .subvendor = 0x107d,
+ .subdevice = 0x6631,
+ .card = CX88_BOARD_LEADTEK_PVR2000,
+ }, {
+ /* PVR2000 NTSC Model [107d:6637] */
+ .subvendor = 0x107d,
+ .subdevice = 0x6637,
+ .card = CX88_BOARD_LEADTEK_PVR2000,
+ }, {
+ /* PVR2000 NTSC Model [107d:663d] */
+ .subvendor = 0x107d,
+ .subdevice = 0x663d,
+ .card = CX88_BOARD_LEADTEK_PVR2000,
+ }, {
+ /* DV2000 NTSC Model [107d:6621] */
+ .subvendor = 0x107d,
+ .subdevice = 0x6621,
+ .card = CX88_BOARD_WINFAST_DV2000,
+ }, {
+ /* TV2000 XP Global [107d:6618] */
+ .subvendor = 0x107d,
+ .subdevice = 0x6618,
+ .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL,
+ }, {
+ /* TV2000 XP Global [107d:6618] */
+ .subvendor = 0x107d,
+ .subdevice = 0x6619,
+ .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL,
+ }, {
+ /* WinFast TV2000 XP Global with XC4000 tuner */
+ .subvendor = 0x107d,
+ .subdevice = 0x6f36,
+ .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36,
+ }, {
+ /* WinFast TV2000 XP Global with XC4000 tuner and different GPIOs */
+ .subvendor = 0x107d,
+ .subdevice = 0x6f43,
+ .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43,
+ }, {
+ .subvendor = 0xb034,
+ .subdevice = 0x3034,
+ .card = CX88_BOARD_PROF_7301,
+ }, {
+ .subvendor = 0x1822,
+ .subdevice = 0x0023,
+ .card = CX88_BOARD_TWINHAN_VP1027_DVBS,
+ },
+};
+
+/* ----------------------------------------------------------------------- */
+/* some leadtek specific stuff */
+
+static void leadtek_eeprom(struct cx88_core *core, u8 *eeprom_data)
+{
+ if (eeprom_data[4] != 0x7d ||
+ eeprom_data[5] != 0x10 ||
+ eeprom_data[7] != 0x66) {
+ warn_printk(core, "Leadtek eeprom invalid.\n");
+ return;
+ }
+
+ /* Terry Wu <terrywu2009@gmail.com> */
+ switch (eeprom_data[6]) {
+ case 0x13: /* SSID 6613 for TV2000 XP Expert NTSC Model */
+ case 0x21: /* SSID 6621 for DV2000 NTSC Model */
+ case 0x31: /* SSID 6631 for PVR2000 NTSC Model */
+ case 0x37: /* SSID 6637 for PVR2000 NTSC Model */
+ case 0x3d: /* SSID 6637 for PVR2000 NTSC Model */
+ core->board.tuner_type = TUNER_PHILIPS_FM1236_MK3;
+ break;
+ default:
+ core->board.tuner_type = TUNER_PHILIPS_FM1216ME_MK3;
+ break;
+ }
+
+ info_printk(core, "Leadtek Winfast 2000XP Expert config: "
+ "tuner=%d, eeprom[0]=0x%02x\n",
+ core->board.tuner_type, eeprom_data[0]);
+}
+
+static void hauppauge_eeprom(struct cx88_core *core, u8 *eeprom_data)
+{
+ struct tveeprom tv;
+
+ tveeprom_hauppauge_analog(&core->i2c_client, &tv, eeprom_data);
+ core->board.tuner_type = tv.tuner_type;
+ core->tuner_formats = tv.tuner_formats;
+ core->board.radio.type = tv.has_radio ? CX88_RADIO : 0;
+
+ /* Make sure we support the board model */
+ switch (tv.model)
+ {
+ case 14009: /* WinTV-HVR3000 (Retail, IR, b/panel video, 3.5mm audio in) */
+ case 14019: /* WinTV-HVR3000 (Retail, IR Blaster, b/panel video, 3.5mm audio in) */
+ case 14029: /* WinTV-HVR3000 (Retail, IR, b/panel video, 3.5mm audio in - 880 bridge) */
+ case 14109: /* WinTV-HVR3000 (Retail, IR, b/panel video, 3.5mm audio in - low profile) */
+ case 14129: /* WinTV-HVR3000 (Retail, IR, b/panel video, 3.5mm audio in - 880 bridge - LP) */
+ case 14559: /* WinTV-HVR3000 (OEM, no IR, b/panel video, 3.5mm audio in) */
+ case 14569: /* WinTV-HVR3000 (OEM, no IR, no back panel video) */
+ case 14659: /* WinTV-HVR3000 (OEM, no IR, b/panel video, RCA audio in - Low profile) */
+ case 14669: /* WinTV-HVR3000 (OEM, no IR, no b/panel video - Low profile) */
+ case 28552: /* WinTV-PVR 'Roslyn' (No IR) */
+ case 34519: /* WinTV-PCI-FM */
+ case 69009:
+ /* WinTV-HVR4000 (DVBS/S2/T, Video and IR, back panel inputs) */
+ case 69100: /* WinTV-HVR4000LITE (DVBS/S2, IR) */
+ case 69500: /* WinTV-HVR4000LITE (DVBS/S2, No IR) */
+ case 69559:
+ /* WinTV-HVR4000 (DVBS/S2/T, Video no IR, back panel inputs) */
+ case 69569: /* WinTV-HVR4000 (DVBS/S2/T, Video no IR) */
+ case 90002: /* Nova-T-PCI (9002) */
+ case 92001: /* Nova-S-Plus (Video and IR) */
+ case 92002: /* Nova-S-Plus (Video and IR) */
+ case 90003: /* Nova-T-PCI (9002 No RF out) */
+ case 90500: /* Nova-T-PCI (oem) */
+ case 90501: /* Nova-T-PCI (oem/IR) */
+ case 92000: /* Nova-SE2 (OEM, No Video or IR) */
+ case 92900: /* WinTV-IROnly (No analog or digital Video inputs) */
+ case 94009: /* WinTV-HVR1100 (Video and IR Retail) */
+ case 94501: /* WinTV-HVR1100 (Video and IR OEM) */
+ case 96009: /* WinTV-HVR1300 (PAL Video, MPEG Video and IR RX) */
+ case 96019: /* WinTV-HVR1300 (PAL Video, MPEG Video and IR RX/TX) */
+ case 96559: /* WinTV-HVR1300 (PAL Video, MPEG Video no IR) */
+ case 96569: /* WinTV-HVR1300 () */
+ case 96659: /* WinTV-HVR1300 () */
+ case 98559: /* WinTV-HVR1100LP (Video no IR, Retail - Low Profile) */
+ /* known */
+ break;
+ case CX88_BOARD_SAMSUNG_SMT_7020:
+ cx_set(MO_GP0_IO, 0x008989FF);
+ break;
+ default:
+ warn_printk(core, "warning: unknown hauppauge model #%d\n",
+ tv.model);
+ break;
+ }
+
+ info_printk(core, "hauppauge eeprom: model=%d\n", tv.model);
+}
+
+/* ----------------------------------------------------------------------- */
+/* some GDI (was: Modular Technology) specific stuff */
+
+static const struct {
+ int id;
+ int fm;
+ const char *name;
+} gdi_tuner[] = {
+ [ 0x01 ] = { .id = TUNER_ABSENT,
+ .name = "NTSC_M" },
+ [ 0x02 ] = { .id = TUNER_ABSENT,
+ .name = "PAL_B" },
+ [ 0x03 ] = { .id = TUNER_ABSENT,
+ .name = "PAL_I" },
+ [ 0x04 ] = { .id = TUNER_ABSENT,
+ .name = "PAL_D" },
+ [ 0x05 ] = { .id = TUNER_ABSENT,
+ .name = "SECAM" },
+
+ [ 0x10 ] = { .id = TUNER_ABSENT,
+ .fm = 1,
+ .name = "TEMIC_4049" },
+ [ 0x11 ] = { .id = TUNER_TEMIC_4136FY5,
+ .name = "TEMIC_4136" },
+ [ 0x12 ] = { .id = TUNER_ABSENT,
+ .name = "TEMIC_4146" },
+
+ [ 0x20 ] = { .id = TUNER_PHILIPS_FQ1216ME,
+ .fm = 1,
+ .name = "PHILIPS_FQ1216_MK3" },
+ [ 0x21 ] = { .id = TUNER_ABSENT, .fm = 1,
+ .name = "PHILIPS_FQ1236_MK3" },
+ [ 0x22 ] = { .id = TUNER_ABSENT,
+ .name = "PHILIPS_FI1236_MK3" },
+ [ 0x23 ] = { .id = TUNER_ABSENT,
+ .name = "PHILIPS_FI1216_MK3" },
+};
+
+static void gdi_eeprom(struct cx88_core *core, u8 *eeprom_data)
+{
+ const char *name = (eeprom_data[0x0d] < ARRAY_SIZE(gdi_tuner))
+ ? gdi_tuner[eeprom_data[0x0d]].name : NULL;
+
+ info_printk(core, "GDI: tuner=%s\n", name ? name : "unknown");
+ if (NULL == name)
+ return;
+ core->board.tuner_type = gdi_tuner[eeprom_data[0x0d]].id;
+ core->board.radio.type = gdi_tuner[eeprom_data[0x0d]].fm ?
+ CX88_RADIO : 0;
+}
+
+/* ------------------------------------------------------------------- */
+/* some Divco specific stuff */
+static int cx88_dvico_xc2028_callback(struct cx88_core *core,
+ int command, int arg)
+{
+ switch (command) {
+ case XC2028_TUNER_RESET:
+ switch (core->boardnr) {
+ case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
+ /* GPIO-4 xc3028 tuner */
+
+ cx_set(MO_GP0_IO, 0x00001000);
+ cx_clear(MO_GP0_IO, 0x00000010);
+ msleep(100);
+ cx_set(MO_GP0_IO, 0x00000010);
+ msleep(100);
+ break;
+ default:
+ cx_write(MO_GP0_IO, 0x101000);
+ mdelay(5);
+ cx_set(MO_GP0_IO, 0x101010);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+/* some Geniatech specific stuff */
+
+static int cx88_xc3028_geniatech_tuner_callback(struct cx88_core *core,
+ int command, int mode)
+{
+ switch (command) {
+ case XC2028_TUNER_RESET:
+ switch (INPUT(core->input).type) {
+ case CX88_RADIO:
+ break;
+ case CX88_VMUX_DVB:
+ cx_write(MO_GP1_IO, 0x030302);
+ mdelay(50);
+ break;
+ default:
+ cx_write(MO_GP1_IO, 0x030301);
+ mdelay(50);
+ }
+ cx_write(MO_GP1_IO, 0x101010);
+ mdelay(50);
+ cx_write(MO_GP1_IO, 0x101000);
+ mdelay(50);
+ cx_write(MO_GP1_IO, 0x101010);
+ mdelay(50);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int cx88_xc3028_winfast1800h_callback(struct cx88_core *core,
+ int command, int arg)
+{
+ switch (command) {
+ case XC2028_TUNER_RESET:
+ /* GPIO 12 (xc3028 tuner reset) */
+ cx_set(MO_GP1_IO, 0x1010);
+ mdelay(50);
+ cx_clear(MO_GP1_IO, 0x10);
+ mdelay(50);
+ cx_set(MO_GP1_IO, 0x10);
+ mdelay(50);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int cx88_xc4000_winfast2000h_plus_callback(struct cx88_core *core,
+ int command, int arg)
+{
+ switch (command) {
+ case XC4000_TUNER_RESET:
+ /* GPIO 12 (xc4000 tuner reset) */
+ cx_set(MO_GP1_IO, 0x1010);
+ mdelay(50);
+ cx_clear(MO_GP1_IO, 0x10);
+ mdelay(75);
+ cx_set(MO_GP1_IO, 0x10);
+ mdelay(75);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/* ------------------------------------------------------------------- */
+/* some Divco specific stuff */
+static int cx88_pv_8000gt_callback(struct cx88_core *core,
+ int command, int arg)
+{
+ switch (command) {
+ case XC2028_TUNER_RESET:
+ cx_write(MO_GP2_IO, 0xcf7);
+ mdelay(50);
+ cx_write(MO_GP2_IO, 0xef5);
+ mdelay(50);
+ cx_write(MO_GP2_IO, 0xcf7);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+/* some DViCO specific stuff */
+
+static void dvico_fusionhdtv_hybrid_init(struct cx88_core *core)
+{
+ struct i2c_msg msg = { .addr = 0x45, .flags = 0 };
+ int i, err;
+ static u8 init_bufs[13][5] = {
+ { 0x10, 0x00, 0x20, 0x01, 0x03 },
+ { 0x10, 0x10, 0x01, 0x00, 0x21 },
+ { 0x10, 0x10, 0x10, 0x00, 0xCA },
+ { 0x10, 0x10, 0x12, 0x00, 0x08 },
+ { 0x10, 0x10, 0x13, 0x00, 0x0A },
+ { 0x10, 0x10, 0x16, 0x01, 0xC0 },
+ { 0x10, 0x10, 0x22, 0x01, 0x3D },
+ { 0x10, 0x10, 0x73, 0x01, 0x2E },
+ { 0x10, 0x10, 0x72, 0x00, 0xC5 },
+ { 0x10, 0x10, 0x71, 0x01, 0x97 },
+ { 0x10, 0x10, 0x70, 0x00, 0x0F },
+ { 0x10, 0x10, 0xB0, 0x00, 0x01 },
+ { 0x03, 0x0C },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(init_bufs); i++) {
+ msg.buf = init_bufs[i];
+ msg.len = (i != 12 ? 5 : 2);
+ err = i2c_transfer(&core->i2c_adap, &msg, 1);
+ if (err != 1) {
+ warn_printk(core, "dvico_fusionhdtv_hybrid_init buf %d "
+ "failed (err = %d)!\n", i, err);
+ return;
+ }
+ }
+}
+
+static int cx88_xc2028_tuner_callback(struct cx88_core *core,
+ int command, int arg)
+{
+ /* Board-specific callbacks */
+ switch (core->boardnr) {
+ case CX88_BOARD_POWERCOLOR_REAL_ANGEL:
+ case CX88_BOARD_GENIATECH_X8000_MT:
+ case CX88_BOARD_KWORLD_ATSC_120:
+ return cx88_xc3028_geniatech_tuner_callback(core,
+ command, arg);
+ case CX88_BOARD_PROLINK_PV_8000GT:
+ case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME:
+ return cx88_pv_8000gt_callback(core, command, arg);
+ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO:
+ case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
+ return cx88_dvico_xc2028_callback(core, command, arg);
+ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL:
+ case CX88_BOARD_WINFAST_DTV1800H:
+ return cx88_xc3028_winfast1800h_callback(core, command, arg);
+ }
+
+ switch (command) {
+ case XC2028_TUNER_RESET:
+ switch (INPUT(core->input).type) {
+ case CX88_RADIO:
+ info_printk(core, "setting GPIO to radio!\n");
+ cx_write(MO_GP0_IO, 0x4ff);
+ mdelay(250);
+ cx_write(MO_GP2_IO, 0xff);
+ mdelay(250);
+ break;
+ case CX88_VMUX_DVB: /* Digital TV*/
+ default: /* Analog TV */
+ info_printk(core, "setting GPIO to TV!\n");
+ break;
+ }
+ cx_write(MO_GP1_IO, 0x101010);
+ mdelay(250);
+ cx_write(MO_GP1_IO, 0x101000);
+ mdelay(250);
+ cx_write(MO_GP1_IO, 0x101010);
+ mdelay(250);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int cx88_xc4000_tuner_callback(struct cx88_core *core,
+ int command, int arg)
+{
+ /* Board-specific callbacks */
+ switch (core->boardnr) {
+ case CX88_BOARD_WINFAST_DTV1800H_XC4000:
+ case CX88_BOARD_WINFAST_DTV2000H_PLUS:
+ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36:
+ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43:
+ return cx88_xc4000_winfast2000h_plus_callback(core,
+ command, arg);
+ }
+ return -EINVAL;
+}
+
+/* ----------------------------------------------------------------------- */
+/* Tuner callback function. Currently only needed for the Pinnacle *
+ * PCTV HD 800i with an xc5000 sillicon tuner. This is used for both *
+ * analog tuner attach (tuner-core.c) and dvb tuner attach (cx88-dvb.c) */
+
+static int cx88_xc5000_tuner_callback(struct cx88_core *core,
+ int command, int arg)
+{
+ switch (core->boardnr) {
+ case CX88_BOARD_PINNACLE_PCTV_HD_800i:
+ if (command == 0) { /* This is the reset command from xc5000 */
+
+ /* djh - According to the engineer at PCTV Systems,
+ the xc5000 reset pin is supposed to be on GPIO12.
+ However, despite three nights of effort, pulling
+ that GPIO low didn't reset the xc5000. While
+ pulling MO_SRST_IO low does reset the xc5000, this
+ also resets in the s5h1409 being reset as well.
+ This causes tuning to always fail since the internal
+ state of the s5h1409 does not match the driver's
+ state. Given that the only two conditions in which
+ the driver performs a reset is during firmware load
+ and powering down the chip, I am taking out the
+ reset. We know that the chip is being reset
+ when the cx88 comes online, and not being able to
+ do power management for this board is worse than
+ not having any tuning at all. */
+ return 0;
+ } else {
+ err_printk(core, "xc5000: unknown tuner "
+ "callback command.\n");
+ return -EINVAL;
+ }
+ break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD:
+ if (command == 0) { /* This is the reset command from xc5000 */
+ cx_clear(MO_GP0_IO, 0x00000010);
+ msleep(10);
+ cx_set(MO_GP0_IO, 0x00000010);
+ return 0;
+ } else {
+ printk(KERN_ERR
+ "xc5000: unknown tuner callback command.\n");
+ return -EINVAL;
+ }
+ break;
+ }
+ return 0; /* Should never be here */
+}
+
+int cx88_tuner_callback(void *priv, int component, int command, int arg)
+{
+ struct i2c_algo_bit_data *i2c_algo = priv;
+ struct cx88_core *core;
+
+ if (!i2c_algo) {
+ printk(KERN_ERR "cx88: Error - i2c private data undefined.\n");
+ return -EINVAL;
+ }
+
+ core = i2c_algo->data;
+
+ if (!core) {
+ printk(KERN_ERR "cx88: Error - device struct undefined.\n");
+ return -EINVAL;
+ }
+
+ if (component != DVB_FRONTEND_COMPONENT_TUNER)
+ return -EINVAL;
+
+ switch (core->board.tuner_type) {
+ case TUNER_XC2028:
+ info_printk(core, "Calling XC2028/3028 callback\n");
+ return cx88_xc2028_tuner_callback(core, command, arg);
+ case TUNER_XC4000:
+ info_printk(core, "Calling XC4000 callback\n");
+ return cx88_xc4000_tuner_callback(core, command, arg);
+ case TUNER_XC5000:
+ info_printk(core, "Calling XC5000 callback\n");
+ return cx88_xc5000_tuner_callback(core, command, arg);
+ }
+ err_printk(core, "Error: Calling callback for tuner %d\n",
+ core->board.tuner_type);
+ return -EINVAL;
+}
+EXPORT_SYMBOL(cx88_tuner_callback);
+
+/* ----------------------------------------------------------------------- */
+
+static void cx88_card_list(struct cx88_core *core, struct pci_dev *pci)
+{
+ int i;
+
+ if (0 == pci->subsystem_vendor &&
+ 0 == pci->subsystem_device) {
+ printk(KERN_ERR
+ "%s: Your board has no valid PCI Subsystem ID and thus can't\n"
+ "%s: be autodetected. Please pass card=<n> insmod option to\n"
+ "%s: workaround that. Redirect complaints to the vendor of\n"
+ "%s: the TV card. Best regards,\n"
+ "%s: -- tux\n",
+ core->name,core->name,core->name,core->name,core->name);
+ } else {
+ printk(KERN_ERR
+ "%s: Your board isn't known (yet) to the driver. You can\n"
+ "%s: try to pick one of the existing card configs via\n"
+ "%s: card=<n> insmod option. Updating to the latest\n"
+ "%s: version might help as well.\n",
+ core->name,core->name,core->name,core->name);
+ }
+ err_printk(core, "Here is a list of valid choices for the card=<n> "
+ "insmod option:\n");
+ for (i = 0; i < ARRAY_SIZE(cx88_boards); i++)
+ printk(KERN_ERR "%s: card=%d -> %s\n",
+ core->name, i, cx88_boards[i].name);
+}
+
+static void cx88_card_setup_pre_i2c(struct cx88_core *core)
+{
+ switch (core->boardnr) {
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
+ /*
+ * Bring the 702 demod up before i2c scanning/attach or devices are hidden
+ * We leave here with the 702 on the bus
+ *
+ * "reset the IR receiver on GPIO[3]"
+ * Reported by Mike Crash <mike AT mikecrash.com>
+ */
+ cx_write(MO_GP0_IO, 0x0000ef88);
+ udelay(1000);
+ cx_clear(MO_GP0_IO, 0x00000088);
+ udelay(50);
+ cx_set(MO_GP0_IO, 0x00000088); /* 702 out of reset */
+ udelay(1000);
+ break;
+
+ case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME:
+ case CX88_BOARD_PROLINK_PV_8000GT:
+ cx_write(MO_GP2_IO, 0xcf7);
+ mdelay(50);
+ cx_write(MO_GP2_IO, 0xef5);
+ mdelay(50);
+ cx_write(MO_GP2_IO, 0xcf7);
+ msleep(10);
+ break;
+
+ case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD:
+ /* Enable the xc5000 tuner */
+ cx_set(MO_GP0_IO, 0x00001010);
+ break;
+
+ case CX88_BOARD_WINFAST_DTV2000H_J:
+ case CX88_BOARD_HAUPPAUGE_HVR3000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000:
+ /* Init GPIO */
+ cx_write(MO_GP0_IO, core->board.input[0].gpio0);
+ udelay(1000);
+ cx_clear(MO_GP0_IO, 0x00000080);
+ udelay(50);
+ cx_set(MO_GP0_IO, 0x00000080); /* 702 out of reset */
+ udelay(1000);
+ break;
+
+ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL:
+ case CX88_BOARD_WINFAST_DTV1800H:
+ cx88_xc3028_winfast1800h_callback(core, XC2028_TUNER_RESET, 0);
+ break;
+
+ case CX88_BOARD_WINFAST_DTV1800H_XC4000:
+ case CX88_BOARD_WINFAST_DTV2000H_PLUS:
+ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36:
+ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43:
+ cx88_xc4000_winfast2000h_plus_callback(core,
+ XC4000_TUNER_RESET, 0);
+ break;
+
+ case CX88_BOARD_TWINHAN_VP1027_DVBS:
+ cx_write(MO_GP0_IO, 0x00003230);
+ cx_write(MO_GP0_IO, 0x00003210);
+ msleep(1);
+ cx_write(MO_GP0_IO, 0x00001230);
+ break;
+ }
+}
+
+/*
+ * Sets board-dependent xc3028 configuration
+ */
+void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl)
+{
+ memset(ctl, 0, sizeof(*ctl));
+
+ ctl->fname = XC2028_DEFAULT_FIRMWARE;
+ ctl->max_len = 64;
+
+ switch (core->boardnr) {
+ case CX88_BOARD_POWERCOLOR_REAL_ANGEL:
+ /* Now works with firmware version 2.7 */
+ if (core->i2c_algo.udelay < 16)
+ core->i2c_algo.udelay = 16;
+ break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO:
+ case CX88_BOARD_WINFAST_DTV1800H:
+ ctl->demod = XC3028_FE_ZARLINK456;
+ break;
+ case CX88_BOARD_KWORLD_ATSC_120:
+ case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
+ ctl->demod = XC3028_FE_OREN538;
+ break;
+ case CX88_BOARD_GENIATECH_X8000_MT:
+ /* FIXME: For this board, the xc3028 never recovers after being
+ powered down (the reset GPIO probably is not set properly).
+ We don't have access to the hardware so we cannot determine
+ which GPIO is used for xc3028, so just disable power xc3028
+ power management for now */
+ ctl->disable_power_mgmt = 1;
+ break;
+ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL:
+ case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME:
+ case CX88_BOARD_PROLINK_PV_8000GT:
+ /*
+ * Those boards uses non-MTS firmware
+ */
+ break;
+ case CX88_BOARD_PINNACLE_HYBRID_PCTV:
+ case CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII:
+ ctl->demod = XC3028_FE_ZARLINK456;
+ ctl->mts = 1;
+ break;
+ default:
+ ctl->demod = XC3028_FE_OREN538;
+ ctl->mts = 1;
+ }
+}
+EXPORT_SYMBOL_GPL(cx88_setup_xc3028);
+
+static void cx88_card_setup(struct cx88_core *core)
+{
+ static u8 eeprom[256];
+ struct tuner_setup tun_setup;
+ unsigned int mode_mask = T_RADIO | T_ANALOG_TV;
+
+ memset(&tun_setup, 0, sizeof(tun_setup));
+
+ if (0 == core->i2c_rc) {
+ core->i2c_client.addr = 0xa0 >> 1;
+ tveeprom_read(&core->i2c_client, eeprom, sizeof(eeprom));
+ }
+
+ switch (core->boardnr) {
+ case CX88_BOARD_HAUPPAUGE:
+ case CX88_BOARD_HAUPPAUGE_ROSLYN:
+ if (0 == core->i2c_rc)
+ hauppauge_eeprom(core, eeprom+8);
+ break;
+ case CX88_BOARD_GDI:
+ if (0 == core->i2c_rc)
+ gdi_eeprom(core, eeprom);
+ break;
+ case CX88_BOARD_LEADTEK_PVR2000:
+ case CX88_BOARD_WINFAST_DV2000:
+ case CX88_BOARD_WINFAST2000XP_EXPERT:
+ if (0 == core->i2c_rc)
+ leadtek_eeprom(core, eeprom);
+ break;
+ case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
+ case CX88_BOARD_HAUPPAUGE_NOVASE2_S1:
+ case CX88_BOARD_HAUPPAUGE_DVB_T1:
+ case CX88_BOARD_HAUPPAUGE_HVR1100:
+ case CX88_BOARD_HAUPPAUGE_HVR1100LP:
+ case CX88_BOARD_HAUPPAUGE_HVR3000:
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
+ case CX88_BOARD_HAUPPAUGE_HVR4000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
+ case CX88_BOARD_HAUPPAUGE_IRONLY:
+ if (0 == core->i2c_rc)
+ hauppauge_eeprom(core, eeprom);
+ break;
+ case CX88_BOARD_KWORLD_DVBS_100:
+ cx_write(MO_GP0_IO, 0x000007f8);
+ cx_write(MO_GP1_IO, 0x00000001);
+ break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO:
+ /* GPIO0:0 is hooked to demod reset */
+ /* GPIO0:4 is hooked to xc3028 reset */
+ cx_write(MO_GP0_IO, 0x00111100);
+ msleep(1);
+ cx_write(MO_GP0_IO, 0x00111111);
+ break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL:
+ /* GPIO0:6 is hooked to FX2 reset pin */
+ cx_set(MO_GP0_IO, 0x00004040);
+ cx_clear(MO_GP0_IO, 0x00000040);
+ msleep(1000);
+ cx_set(MO_GP0_IO, 0x00004040);
+ /* FALLTHROUGH */
+ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1:
+ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS:
+ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID:
+ /* GPIO0:0 is hooked to mt352 reset pin */
+ cx_set(MO_GP0_IO, 0x00000101);
+ cx_clear(MO_GP0_IO, 0x00000001);
+ msleep(1);
+ cx_set(MO_GP0_IO, 0x00000101);
+ if (0 == core->i2c_rc &&
+ core->boardnr == CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID)
+ dvico_fusionhdtv_hybrid_init(core);
+ break;
+ case CX88_BOARD_KWORLD_DVB_T:
+ case CX88_BOARD_DNTV_LIVE_DVB_T:
+ cx_set(MO_GP0_IO, 0x00000707);
+ cx_set(MO_GP2_IO, 0x00000101);
+ cx_clear(MO_GP2_IO, 0x00000001);
+ msleep(1);
+ cx_clear(MO_GP0_IO, 0x00000007);
+ cx_set(MO_GP2_IO, 0x00000101);
+ break;
+ case CX88_BOARD_DNTV_LIVE_DVB_T_PRO:
+ cx_write(MO_GP0_IO, 0x00080808);
+ break;
+ case CX88_BOARD_ATI_HDTVWONDER:
+ if (0 == core->i2c_rc) {
+ /* enable tuner */
+ int i;
+ static const u8 buffer [][2] = {
+ {0x10,0x12},
+ {0x13,0x04},
+ {0x16,0x00},
+ {0x14,0x04},
+ {0x17,0x00}
+ };
+ core->i2c_client.addr = 0x0a;
+
+ for (i = 0; i < ARRAY_SIZE(buffer); i++)
+ if (2 != i2c_master_send(&core->i2c_client,
+ buffer[i],2))
+ warn_printk(core, "Unable to enable "
+ "tuner(%i).\n", i);
+ }
+ break;
+ case CX88_BOARD_MSI_TVANYWHERE_MASTER:
+ {
+ struct v4l2_priv_tun_config tea5767_cfg;
+ struct tea5767_ctrl ctl;
+
+ memset(&ctl, 0, sizeof(ctl));
+
+ ctl.high_cut = 1;
+ ctl.st_noise = 1;
+ ctl.deemph_75 = 1;
+ ctl.xtal_freq = TEA5767_HIGH_LO_13MHz;
+
+ tea5767_cfg.tuner = TUNER_TEA5767;
+ tea5767_cfg.priv = &ctl;
+
+ call_all(core, tuner, s_config, &tea5767_cfg);
+ break;
+ }
+ case CX88_BOARD_TEVII_S420:
+ case CX88_BOARD_TEVII_S460:
+ case CX88_BOARD_TEVII_S464:
+ case CX88_BOARD_OMICOM_SS4_PCI:
+ case CX88_BOARD_TBS_8910:
+ case CX88_BOARD_TBS_8920:
+ case CX88_BOARD_PROF_6200:
+ case CX88_BOARD_PROF_7300:
+ case CX88_BOARD_PROF_7301:
+ case CX88_BOARD_SATTRADE_ST4200:
+ cx_write(MO_GP0_IO, 0x8000);
+ msleep(100);
+ cx_write(MO_SRST_IO, 0);
+ msleep(10);
+ cx_write(MO_GP0_IO, 0x8080);
+ msleep(100);
+ cx_write(MO_SRST_IO, 1);
+ msleep(100);
+ break;
+ } /*end switch() */
+
+
+ /* Setup tuners */
+ if ((core->board.radio_type != UNSET)) {
+ tun_setup.mode_mask = T_RADIO;
+ tun_setup.type = core->board.radio_type;
+ tun_setup.addr = core->board.radio_addr;
+ tun_setup.tuner_callback = cx88_tuner_callback;
+ call_all(core, tuner, s_type_addr, &tun_setup);
+ mode_mask &= ~T_RADIO;
+ }
+
+ if (core->board.tuner_type != TUNER_ABSENT) {
+ tun_setup.mode_mask = mode_mask;
+ tun_setup.type = core->board.tuner_type;
+ tun_setup.addr = core->board.tuner_addr;
+ tun_setup.tuner_callback = cx88_tuner_callback;
+
+ call_all(core, tuner, s_type_addr, &tun_setup);
+ }
+
+ if (core->board.tda9887_conf) {
+ struct v4l2_priv_tun_config tda9887_cfg;
+
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &core->board.tda9887_conf;
+
+ call_all(core, tuner, s_config, &tda9887_cfg);
+ }
+
+ if (core->board.tuner_type == TUNER_XC2028) {
+ struct v4l2_priv_tun_config xc2028_cfg;
+ struct xc2028_ctrl ctl;
+
+ /* Fills device-dependent initialization parameters */
+ cx88_setup_xc3028(core, &ctl);
+
+ /* Sends parameters to xc2028/3028 tuner */
+ memset(&xc2028_cfg, 0, sizeof(xc2028_cfg));
+ xc2028_cfg.tuner = TUNER_XC2028;
+ xc2028_cfg.priv = &ctl;
+ info_printk(core, "Asking xc2028/3028 to load firmware %s\n",
+ ctl.fname);
+ call_all(core, tuner, s_config, &xc2028_cfg);
+ }
+ call_all(core, core, s_power, 0);
+}
+
+/* ------------------------------------------------------------------ */
+
+static int cx88_pci_quirks(const char *name, struct pci_dev *pci)
+{
+ unsigned int lat = UNSET;
+ u8 ctrl = 0;
+ u8 value;
+
+ /* check pci quirks */
+ if (pci_pci_problems & PCIPCI_TRITON) {
+ printk(KERN_INFO "%s: quirk: PCIPCI_TRITON -- set TBFX\n",
+ name);
+ ctrl |= CX88X_EN_TBFX;
+ }
+ if (pci_pci_problems & PCIPCI_NATOMA) {
+ printk(KERN_INFO "%s: quirk: PCIPCI_NATOMA -- set TBFX\n",
+ name);
+ ctrl |= CX88X_EN_TBFX;
+ }
+ if (pci_pci_problems & PCIPCI_VIAETBF) {
+ printk(KERN_INFO "%s: quirk: PCIPCI_VIAETBF -- set TBFX\n",
+ name);
+ ctrl |= CX88X_EN_TBFX;
+ }
+ if (pci_pci_problems & PCIPCI_VSFX) {
+ printk(KERN_INFO "%s: quirk: PCIPCI_VSFX -- set VSFX\n",
+ name);
+ ctrl |= CX88X_EN_VSFX;
+ }
+#ifdef PCIPCI_ALIMAGIK
+ if (pci_pci_problems & PCIPCI_ALIMAGIK) {
+ printk(KERN_INFO "%s: quirk: PCIPCI_ALIMAGIK -- latency fixup\n",
+ name);
+ lat = 0x0A;
+ }
+#endif
+
+ /* check insmod options */
+ if (UNSET != latency)
+ lat = latency;
+
+ /* apply stuff */
+ if (ctrl) {
+ pci_read_config_byte(pci, CX88X_DEVCTRL, &value);
+ value |= ctrl;
+ pci_write_config_byte(pci, CX88X_DEVCTRL, value);
+ }
+ if (UNSET != lat) {
+ printk(KERN_INFO "%s: setting pci latency timer to %d\n",
+ name, latency);
+ pci_write_config_byte(pci, PCI_LATENCY_TIMER, latency);
+ }
+ return 0;
+}
+
+int cx88_get_resources(const struct cx88_core *core, struct pci_dev *pci)
+{
+ if (request_mem_region(pci_resource_start(pci,0),
+ pci_resource_len(pci,0),
+ core->name))
+ return 0;
+ printk(KERN_ERR
+ "%s/%d: Can't get MMIO memory @ 0x%llx, subsystem: %04x:%04x\n",
+ core->name, PCI_FUNC(pci->devfn),
+ (unsigned long long)pci_resource_start(pci, 0),
+ pci->subsystem_vendor, pci->subsystem_device);
+ return -EBUSY;
+}
+
+/* Allocate and initialize the cx88 core struct. One should hold the
+ * devlist mutex before calling this. */
+struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr)
+{
+ struct cx88_core *core;
+ int i;
+
+ core = kzalloc(sizeof(*core), GFP_KERNEL);
+ if (core == NULL)
+ return NULL;
+
+ atomic_inc(&core->refcount);
+ core->pci_bus = pci->bus->number;
+ core->pci_slot = PCI_SLOT(pci->devfn);
+ core->pci_irqmask = PCI_INT_RISC_RD_BERRINT | PCI_INT_RISC_WR_BERRINT |
+ PCI_INT_BRDG_BERRINT | PCI_INT_SRC_DMA_BERRINT |
+ PCI_INT_DST_DMA_BERRINT | PCI_INT_IPB_DMA_BERRINT;
+ mutex_init(&core->lock);
+
+ core->nr = nr;
+ sprintf(core->name, "cx88[%d]", core->nr);
+
+ strcpy(core->v4l2_dev.name, core->name);
+ if (v4l2_device_register(NULL, &core->v4l2_dev)) {
+ kfree(core);
+ return NULL;
+ }
+
+ if (0 != cx88_get_resources(core, pci)) {
+ v4l2_device_unregister(&core->v4l2_dev);
+ kfree(core);
+ return NULL;
+ }
+
+ /* PCI stuff */
+ cx88_pci_quirks(core->name, pci);
+ core->lmmio = ioremap(pci_resource_start(pci, 0),
+ pci_resource_len(pci, 0));
+ core->bmmio = (u8 __iomem *)core->lmmio;
+
+ if (core->lmmio == NULL) {
+ kfree(core);
+ return NULL;
+ }
+
+ /* board config */
+ core->boardnr = UNSET;
+ if (card[core->nr] < ARRAY_SIZE(cx88_boards))
+ core->boardnr = card[core->nr];
+ for (i = 0; UNSET == core->boardnr && i < ARRAY_SIZE(cx88_subids); i++)
+ if (pci->subsystem_vendor == cx88_subids[i].subvendor &&
+ pci->subsystem_device == cx88_subids[i].subdevice)
+ core->boardnr = cx88_subids[i].card;
+ if (UNSET == core->boardnr) {
+ core->boardnr = CX88_BOARD_UNKNOWN;
+ cx88_card_list(core, pci);
+ }
+
+ memcpy(&core->board, &cx88_boards[core->boardnr], sizeof(core->board));
+
+ if (!core->board.num_frontends && (core->board.mpeg & CX88_MPEG_DVB))
+ core->board.num_frontends = 1;
+
+ info_printk(core, "subsystem: %04x:%04x, board: %s [card=%d,%s], frontend(s): %d\n",
+ pci->subsystem_vendor, pci->subsystem_device, core->board.name,
+ core->boardnr, card[core->nr] == core->boardnr ?
+ "insmod option" : "autodetected",
+ core->board.num_frontends);
+
+ if (tuner[core->nr] != UNSET)
+ core->board.tuner_type = tuner[core->nr];
+ if (radio[core->nr] != UNSET)
+ core->board.radio_type = radio[core->nr];
+
+ info_printk(core, "TV tuner type %d, Radio tuner type %d\n",
+ core->board.tuner_type, core->board.radio_type);
+
+ /* init hardware */
+ cx88_reset(core);
+ cx88_card_setup_pre_i2c(core);
+ cx88_i2c_init(core, pci);
+
+ /* load tuner module, if needed */
+ if (TUNER_ABSENT != core->board.tuner_type) {
+ /* Ignore 0x6b and 0x6f on cx88 boards.
+ * FusionHDTV5 RT Gold has an ir receiver at 0x6b
+ * and an RTC at 0x6f which can get corrupted if probed. */
+ static const unsigned short tv_addrs[] = {
+ 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x6c, 0x6d, 0x6e,
+ I2C_CLIENT_END
+ };
+ int has_demod = (core->board.tda9887_conf & TDA9887_PRESENT);
+
+ /* I don't trust the radio_type as is stored in the card
+ definitions, so we just probe for it.
+ The radio_type is sometimes missing, or set to UNSET but
+ later code configures a tea5767.
+ */
+ v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap,
+ "tuner", 0, v4l2_i2c_tuner_addrs(ADDRS_RADIO));
+ if (has_demod)
+ v4l2_i2c_new_subdev(&core->v4l2_dev,
+ &core->i2c_adap, "tuner",
+ 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
+ if (core->board.tuner_addr == ADDR_UNSET) {
+ v4l2_i2c_new_subdev(&core->v4l2_dev,
+ &core->i2c_adap, "tuner",
+ 0, has_demod ? tv_addrs + 4 : tv_addrs);
+ } else {
+ v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap,
+ "tuner", core->board.tuner_addr, NULL);
+ }
+ }
+
+ cx88_card_setup(core);
+ if (!disable_ir) {
+ cx88_i2c_init_ir(core);
+ cx88_ir_init(core, pci);
+ }
+
+ return core;
+}
diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c
new file mode 100644
index 00000000..fbfdd806
--- /dev/null
+++ b/drivers/media/video/cx88/cx88-core.c
@@ -0,0 +1,1126 @@
+/*
+ *
+ * device driver for Conexant 2388x based TV cards
+ * driver core
+ *
+ * (c) 2003 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org>
+ * - Multituner support
+ * - video_ioctl2 conversion
+ * - PAL/M fixes
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/kmod.h>
+#include <linux/sound.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <linux/mutex.h>
+
+#include "cx88.h"
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+
+MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards");
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int core_debug;
+module_param(core_debug,int,0644);
+MODULE_PARM_DESC(core_debug,"enable debug messages [core]");
+
+static unsigned int nicam;
+module_param(nicam,int,0644);
+MODULE_PARM_DESC(nicam,"tv audio is nicam");
+
+static unsigned int nocomb;
+module_param(nocomb,int,0644);
+MODULE_PARM_DESC(nocomb,"disable comb filter");
+
+#define dprintk(level,fmt, arg...) if (core_debug >= level) \
+ printk(KERN_DEBUG "%s: " fmt, core->name , ## arg)
+
+static unsigned int cx88_devcount;
+static LIST_HEAD(cx88_devlist);
+static DEFINE_MUTEX(devlist);
+
+#define NO_SYNC_LINE (-1U)
+
+/* @lpi: lines per IRQ, or 0 to not generate irqs. Note: IRQ to be
+ generated _after_ lpi lines are transferred. */
+static __le32* cx88_risc_field(__le32 *rp, struct scatterlist *sglist,
+ unsigned int offset, u32 sync_line,
+ unsigned int bpl, unsigned int padding,
+ unsigned int lines, unsigned int lpi)
+{
+ struct scatterlist *sg;
+ unsigned int line,todo,sol;
+
+ /* sync instruction */
+ if (sync_line != NO_SYNC_LINE)
+ *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
+
+ /* scan lines */
+ sg = sglist;
+ for (line = 0; line < lines; line++) {
+ while (offset && offset >= sg_dma_len(sg)) {
+ offset -= sg_dma_len(sg);
+ sg++;
+ }
+ if (lpi && line>0 && !(line % lpi))
+ sol = RISC_SOL | RISC_IRQ1 | RISC_CNT_INC;
+ else
+ sol = RISC_SOL;
+ if (bpl <= sg_dma_len(sg)-offset) {
+ /* fits into current chunk */
+ *(rp++)=cpu_to_le32(RISC_WRITE|sol|RISC_EOL|bpl);
+ *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
+ offset+=bpl;
+ } else {
+ /* scanline needs to be split */
+ todo = bpl;
+ *(rp++)=cpu_to_le32(RISC_WRITE|sol|
+ (sg_dma_len(sg)-offset));
+ *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
+ todo -= (sg_dma_len(sg)-offset);
+ offset = 0;
+ sg++;
+ while (todo > sg_dma_len(sg)) {
+ *(rp++)=cpu_to_le32(RISC_WRITE|
+ sg_dma_len(sg));
+ *(rp++)=cpu_to_le32(sg_dma_address(sg));
+ todo -= sg_dma_len(sg);
+ sg++;
+ }
+ *(rp++)=cpu_to_le32(RISC_WRITE|RISC_EOL|todo);
+ *(rp++)=cpu_to_le32(sg_dma_address(sg));
+ offset += todo;
+ }
+ offset += padding;
+ }
+
+ return rp;
+}
+
+int cx88_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+ struct scatterlist *sglist,
+ unsigned int top_offset, unsigned int bottom_offset,
+ unsigned int bpl, unsigned int padding, unsigned int lines)
+{
+ u32 instructions,fields;
+ __le32 *rp;
+ int rc;
+
+ fields = 0;
+ if (UNSET != top_offset)
+ fields++;
+ if (UNSET != bottom_offset)
+ fields++;
+
+ /* estimate risc mem: worst case is one write per page border +
+ one write per scan line + syncs + jump (all 2 dwords). Padding
+ can cause next bpl to start close to a page border. First DMA
+ region may be smaller than PAGE_SIZE */
+ instructions = fields * (1 + ((bpl + padding) * lines) / PAGE_SIZE + lines);
+ instructions += 2;
+ if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0)
+ return rc;
+
+ /* write risc instructions */
+ rp = risc->cpu;
+ if (UNSET != top_offset)
+ rp = cx88_risc_field(rp, sglist, top_offset, 0,
+ bpl, padding, lines, 0);
+ if (UNSET != bottom_offset)
+ rp = cx88_risc_field(rp, sglist, bottom_offset, 0x200,
+ bpl, padding, lines, 0);
+
+ /* save pointer to jmp instruction address */
+ risc->jmp = rp;
+ BUG_ON((risc->jmp - risc->cpu + 2) * sizeof (*risc->cpu) > risc->size);
+ return 0;
+}
+
+int cx88_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+ struct scatterlist *sglist, unsigned int bpl,
+ unsigned int lines, unsigned int lpi)
+{
+ u32 instructions;
+ __le32 *rp;
+ int rc;
+
+ /* estimate risc mem: worst case is one write per page border +
+ one write per scan line + syncs + jump (all 2 dwords). Here
+ there is no padding and no sync. First DMA region may be smaller
+ than PAGE_SIZE */
+ instructions = 1 + (bpl * lines) / PAGE_SIZE + lines;
+ instructions += 1;
+ if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0)
+ return rc;
+
+ /* write risc instructions */
+ rp = risc->cpu;
+ rp = cx88_risc_field(rp, sglist, 0, NO_SYNC_LINE, bpl, 0, lines, lpi);
+
+ /* save pointer to jmp instruction address */
+ risc->jmp = rp;
+ BUG_ON((risc->jmp - risc->cpu + 2) * sizeof (*risc->cpu) > risc->size);
+ return 0;
+}
+
+int cx88_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
+ u32 reg, u32 mask, u32 value)
+{
+ __le32 *rp;
+ int rc;
+
+ if ((rc = btcx_riscmem_alloc(pci, risc, 4*16)) < 0)
+ return rc;
+
+ /* write risc instructions */
+ rp = risc->cpu;
+ *(rp++) = cpu_to_le32(RISC_WRITECR | RISC_IRQ2 | RISC_IMM);
+ *(rp++) = cpu_to_le32(reg);
+ *(rp++) = cpu_to_le32(value);
+ *(rp++) = cpu_to_le32(mask);
+ *(rp++) = cpu_to_le32(RISC_JUMP);
+ *(rp++) = cpu_to_le32(risc->dma);
+ return 0;
+}
+
+void
+cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf)
+{
+ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+
+ BUG_ON(in_interrupt());
+ videobuf_waiton(q, &buf->vb, 0, 0);
+ videobuf_dma_unmap(q->dev, dma);
+ videobuf_dma_free(dma);
+ btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+/* ------------------------------------------------------------------ */
+/* our SRAM memory layout */
+
+/* we are going to put all thr risc programs into host memory, so we
+ * can use the whole SDRAM for the DMA fifos. To simplify things, we
+ * use a static memory layout. That surely will waste memory in case
+ * we don't use all DMA channels at the same time (which will be the
+ * case most of the time). But that still gives us enough FIFO space
+ * to be able to deal with insane long pci latencies ...
+ *
+ * FIFO space allocations:
+ * channel 21 (y video) - 10.0k
+ * channel 22 (u video) - 2.0k
+ * channel 23 (v video) - 2.0k
+ * channel 24 (vbi) - 4.0k
+ * channels 25+26 (audio) - 4.0k
+ * channel 28 (mpeg) - 4.0k
+ * channel 27 (audio rds)- 3.0k
+ * TOTAL = 29.0k
+ *
+ * Every channel has 160 bytes control data (64 bytes instruction
+ * queue and 6 CDT entries), which is close to 2k total.
+ *
+ * Address layout:
+ * 0x0000 - 0x03ff CMDs / reserved
+ * 0x0400 - 0x0bff instruction queues + CDs
+ * 0x0c00 - FIFOs
+ */
+
+const struct sram_channel const cx88_sram_channels[] = {
+ [SRAM_CH21] = {
+ .name = "video y / packed",
+ .cmds_start = 0x180040,
+ .ctrl_start = 0x180400,
+ .cdt = 0x180400 + 64,
+ .fifo_start = 0x180c00,
+ .fifo_size = 0x002800,
+ .ptr1_reg = MO_DMA21_PTR1,
+ .ptr2_reg = MO_DMA21_PTR2,
+ .cnt1_reg = MO_DMA21_CNT1,
+ .cnt2_reg = MO_DMA21_CNT2,
+ },
+ [SRAM_CH22] = {
+ .name = "video u",
+ .cmds_start = 0x180080,
+ .ctrl_start = 0x1804a0,
+ .cdt = 0x1804a0 + 64,
+ .fifo_start = 0x183400,
+ .fifo_size = 0x000800,
+ .ptr1_reg = MO_DMA22_PTR1,
+ .ptr2_reg = MO_DMA22_PTR2,
+ .cnt1_reg = MO_DMA22_CNT1,
+ .cnt2_reg = MO_DMA22_CNT2,
+ },
+ [SRAM_CH23] = {
+ .name = "video v",
+ .cmds_start = 0x1800c0,
+ .ctrl_start = 0x180540,
+ .cdt = 0x180540 + 64,
+ .fifo_start = 0x183c00,
+ .fifo_size = 0x000800,
+ .ptr1_reg = MO_DMA23_PTR1,
+ .ptr2_reg = MO_DMA23_PTR2,
+ .cnt1_reg = MO_DMA23_CNT1,
+ .cnt2_reg = MO_DMA23_CNT2,
+ },
+ [SRAM_CH24] = {
+ .name = "vbi",
+ .cmds_start = 0x180100,
+ .ctrl_start = 0x1805e0,
+ .cdt = 0x1805e0 + 64,
+ .fifo_start = 0x184400,
+ .fifo_size = 0x001000,
+ .ptr1_reg = MO_DMA24_PTR1,
+ .ptr2_reg = MO_DMA24_PTR2,
+ .cnt1_reg = MO_DMA24_CNT1,
+ .cnt2_reg = MO_DMA24_CNT2,
+ },
+ [SRAM_CH25] = {
+ .name = "audio from",
+ .cmds_start = 0x180140,
+ .ctrl_start = 0x180680,
+ .cdt = 0x180680 + 64,
+ .fifo_start = 0x185400,
+ .fifo_size = 0x001000,
+ .ptr1_reg = MO_DMA25_PTR1,
+ .ptr2_reg = MO_DMA25_PTR2,
+ .cnt1_reg = MO_DMA25_CNT1,
+ .cnt2_reg = MO_DMA25_CNT2,
+ },
+ [SRAM_CH26] = {
+ .name = "audio to",
+ .cmds_start = 0x180180,
+ .ctrl_start = 0x180720,
+ .cdt = 0x180680 + 64, /* same as audio IN */
+ .fifo_start = 0x185400, /* same as audio IN */
+ .fifo_size = 0x001000, /* same as audio IN */
+ .ptr1_reg = MO_DMA26_PTR1,
+ .ptr2_reg = MO_DMA26_PTR2,
+ .cnt1_reg = MO_DMA26_CNT1,
+ .cnt2_reg = MO_DMA26_CNT2,
+ },
+ [SRAM_CH28] = {
+ .name = "mpeg",
+ .cmds_start = 0x180200,
+ .ctrl_start = 0x1807C0,
+ .cdt = 0x1807C0 + 64,
+ .fifo_start = 0x186400,
+ .fifo_size = 0x001000,
+ .ptr1_reg = MO_DMA28_PTR1,
+ .ptr2_reg = MO_DMA28_PTR2,
+ .cnt1_reg = MO_DMA28_CNT1,
+ .cnt2_reg = MO_DMA28_CNT2,
+ },
+ [SRAM_CH27] = {
+ .name = "audio rds",
+ .cmds_start = 0x1801C0,
+ .ctrl_start = 0x180860,
+ .cdt = 0x180860 + 64,
+ .fifo_start = 0x187400,
+ .fifo_size = 0x000C00,
+ .ptr1_reg = MO_DMA27_PTR1,
+ .ptr2_reg = MO_DMA27_PTR2,
+ .cnt1_reg = MO_DMA27_CNT1,
+ .cnt2_reg = MO_DMA27_CNT2,
+ },
+};
+
+int cx88_sram_channel_setup(struct cx88_core *core,
+ const struct sram_channel *ch,
+ unsigned int bpl, u32 risc)
+{
+ unsigned int i,lines;
+ u32 cdt;
+
+ bpl = (bpl + 7) & ~7; /* alignment */
+ cdt = ch->cdt;
+ lines = ch->fifo_size / bpl;
+ if (lines > 6)
+ lines = 6;
+ BUG_ON(lines < 2);
+
+ /* write CDT */
+ for (i = 0; i < lines; i++)
+ cx_write(cdt + 16*i, ch->fifo_start + bpl*i);
+
+ /* write CMDS */
+ cx_write(ch->cmds_start + 0, risc);
+ cx_write(ch->cmds_start + 4, cdt);
+ cx_write(ch->cmds_start + 8, (lines*16) >> 3);
+ cx_write(ch->cmds_start + 12, ch->ctrl_start);
+ cx_write(ch->cmds_start + 16, 64 >> 2);
+ for (i = 20; i < 64; i += 4)
+ cx_write(ch->cmds_start + i, 0);
+
+ /* fill registers */
+ cx_write(ch->ptr1_reg, ch->fifo_start);
+ cx_write(ch->ptr2_reg, cdt);
+ cx_write(ch->cnt1_reg, (bpl >> 3) -1);
+ cx_write(ch->cnt2_reg, (lines*16) >> 3);
+
+ dprintk(2,"sram setup %s: bpl=%d lines=%d\n", ch->name, bpl, lines);
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+/* debug helper code */
+
+static int cx88_risc_decode(u32 risc)
+{
+ static const char * const instr[16] = {
+ [ RISC_SYNC >> 28 ] = "sync",
+ [ RISC_WRITE >> 28 ] = "write",
+ [ RISC_WRITEC >> 28 ] = "writec",
+ [ RISC_READ >> 28 ] = "read",
+ [ RISC_READC >> 28 ] = "readc",
+ [ RISC_JUMP >> 28 ] = "jump",
+ [ RISC_SKIP >> 28 ] = "skip",
+ [ RISC_WRITERM >> 28 ] = "writerm",
+ [ RISC_WRITECM >> 28 ] = "writecm",
+ [ RISC_WRITECR >> 28 ] = "writecr",
+ };
+ static int const incr[16] = {
+ [ RISC_WRITE >> 28 ] = 2,
+ [ RISC_JUMP >> 28 ] = 2,
+ [ RISC_WRITERM >> 28 ] = 3,
+ [ RISC_WRITECM >> 28 ] = 3,
+ [ RISC_WRITECR >> 28 ] = 4,
+ };
+ static const char * const bits[] = {
+ "12", "13", "14", "resync",
+ "cnt0", "cnt1", "18", "19",
+ "20", "21", "22", "23",
+ "irq1", "irq2", "eol", "sol",
+ };
+ int i;
+
+ printk("0x%08x [ %s", risc,
+ instr[risc >> 28] ? instr[risc >> 28] : "INVALID");
+ for (i = ARRAY_SIZE(bits)-1; i >= 0; i--)
+ if (risc & (1 << (i + 12)))
+ printk(" %s",bits[i]);
+ printk(" count=%d ]\n", risc & 0xfff);
+ return incr[risc >> 28] ? incr[risc >> 28] : 1;
+}
+
+
+void cx88_sram_channel_dump(struct cx88_core *core,
+ const struct sram_channel *ch)
+{
+ static const char * const name[] = {
+ "initial risc",
+ "cdt base",
+ "cdt size",
+ "iq base",
+ "iq size",
+ "risc pc",
+ "iq wr ptr",
+ "iq rd ptr",
+ "cdt current",
+ "pci target",
+ "line / byte",
+ };
+ u32 risc;
+ unsigned int i,j,n;
+
+ printk("%s: %s - dma channel status dump\n",
+ core->name,ch->name);
+ for (i = 0; i < ARRAY_SIZE(name); i++)
+ printk("%s: cmds: %-12s: 0x%08x\n",
+ core->name,name[i],
+ cx_read(ch->cmds_start + 4*i));
+ for (n = 1, i = 0; i < 4; i++) {
+ risc = cx_read(ch->cmds_start + 4 * (i+11));
+ printk("%s: risc%d: ", core->name, i);
+ if (--n)
+ printk("0x%08x [ arg #%d ]\n", risc, n);
+ else
+ n = cx88_risc_decode(risc);
+ }
+ for (i = 0; i < 16; i += n) {
+ risc = cx_read(ch->ctrl_start + 4 * i);
+ printk("%s: iq %x: ", core->name, i);
+ n = cx88_risc_decode(risc);
+ for (j = 1; j < n; j++) {
+ risc = cx_read(ch->ctrl_start + 4 * (i+j));
+ printk("%s: iq %x: 0x%08x [ arg #%d ]\n",
+ core->name, i+j, risc, j);
+ }
+ }
+
+ printk("%s: fifo: 0x%08x -> 0x%x\n",
+ core->name, ch->fifo_start, ch->fifo_start+ch->fifo_size);
+ printk("%s: ctrl: 0x%08x -> 0x%x\n",
+ core->name, ch->ctrl_start, ch->ctrl_start+6*16);
+ printk("%s: ptr1_reg: 0x%08x\n",
+ core->name,cx_read(ch->ptr1_reg));
+ printk("%s: ptr2_reg: 0x%08x\n",
+ core->name,cx_read(ch->ptr2_reg));
+ printk("%s: cnt1_reg: 0x%08x\n",
+ core->name,cx_read(ch->cnt1_reg));
+ printk("%s: cnt2_reg: 0x%08x\n",
+ core->name,cx_read(ch->cnt2_reg));
+}
+
+static const char *cx88_pci_irqs[32] = {
+ "vid", "aud", "ts", "vip", "hst", "5", "6", "tm1",
+ "src_dma", "dst_dma", "risc_rd_err", "risc_wr_err",
+ "brdg_err", "src_dma_err", "dst_dma_err", "ipb_dma_err",
+ "i2c", "i2c_rack", "ir_smp", "gpio0", "gpio1"
+};
+
+void cx88_print_irqbits(const char *name, const char *tag, const char *strings[],
+ int len, u32 bits, u32 mask)
+{
+ unsigned int i;
+
+ printk(KERN_DEBUG "%s: %s [0x%x]", name, tag, bits);
+ for (i = 0; i < len; i++) {
+ if (!(bits & (1 << i)))
+ continue;
+ if (strings[i])
+ printk(" %s", strings[i]);
+ else
+ printk(" %d", i);
+ if (!(mask & (1 << i)))
+ continue;
+ printk("*");
+ }
+ printk("\n");
+}
+
+/* ------------------------------------------------------------------ */
+
+int cx88_core_irq(struct cx88_core *core, u32 status)
+{
+ int handled = 0;
+
+ if (status & PCI_INT_IR_SMPINT) {
+ cx88_ir_irq(core);
+ handled++;
+ }
+ if (!handled)
+ cx88_print_irqbits(core->name, "irq pci",
+ cx88_pci_irqs, ARRAY_SIZE(cx88_pci_irqs),
+ status, core->pci_irqmask);
+ return handled;
+}
+
+void cx88_wakeup(struct cx88_core *core,
+ struct cx88_dmaqueue *q, u32 count)
+{
+ struct cx88_buffer *buf;
+ int bc;
+
+ for (bc = 0;; bc++) {
+ if (list_empty(&q->active))
+ break;
+ buf = list_entry(q->active.next,
+ struct cx88_buffer, vb.queue);
+ /* count comes from the hw and is is 16bit wide --
+ * this trick handles wrap-arounds correctly for
+ * up to 32767 buffers in flight... */
+ if ((s16) (count - buf->count) < 0)
+ break;
+ do_gettimeofday(&buf->vb.ts);
+ dprintk(2,"[%p/%d] wakeup reg=%d buf=%d\n",buf,buf->vb.i,
+ count, buf->count);
+ buf->vb.state = VIDEOBUF_DONE;
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+ }
+ if (list_empty(&q->active)) {
+ del_timer(&q->timeout);
+ } else {
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ }
+ if (bc != 1)
+ dprintk(2, "%s: %d buffers handled (should be 1)\n",
+ __func__, bc);
+}
+
+void cx88_shutdown(struct cx88_core *core)
+{
+ /* disable RISC controller + IRQs */
+ cx_write(MO_DEV_CNTRL2, 0);
+
+ /* stop dma transfers */
+ cx_write(MO_VID_DMACNTRL, 0x0);
+ cx_write(MO_AUD_DMACNTRL, 0x0);
+ cx_write(MO_TS_DMACNTRL, 0x0);
+ cx_write(MO_VIP_DMACNTRL, 0x0);
+ cx_write(MO_GPHST_DMACNTRL, 0x0);
+
+ /* stop interrupts */
+ cx_write(MO_PCI_INTMSK, 0x0);
+ cx_write(MO_VID_INTMSK, 0x0);
+ cx_write(MO_AUD_INTMSK, 0x0);
+ cx_write(MO_TS_INTMSK, 0x0);
+ cx_write(MO_VIP_INTMSK, 0x0);
+ cx_write(MO_GPHST_INTMSK, 0x0);
+
+ /* stop capturing */
+ cx_write(VID_CAPTURE_CONTROL, 0);
+}
+
+int cx88_reset(struct cx88_core *core)
+{
+ dprintk(1,"%s\n",__func__);
+ cx88_shutdown(core);
+
+ /* clear irq status */
+ cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int
+ cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int
+ cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int
+
+ /* wait a bit */
+ msleep(100);
+
+ /* init sram */
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH21], 720*4, 0);
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH22], 128, 0);
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH23], 128, 0);
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH24], 128, 0);
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], 128, 0);
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], 128, 0);
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28], 188*4, 0);
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH27], 128, 0);
+
+ /* misc init ... */
+ cx_write(MO_INPUT_FORMAT, ((1 << 13) | // agc enable
+ (1 << 12) | // agc gain
+ (1 << 11) | // adaptibe agc
+ (0 << 10) | // chroma agc
+ (0 << 9) | // ckillen
+ (7)));
+
+ /* setup image format */
+ cx_andor(MO_COLOR_CTRL, 0x4000, 0x4000);
+
+ /* setup FIFO Thresholds */
+ cx_write(MO_PDMA_STHRSH, 0x0807);
+ cx_write(MO_PDMA_DTHRSH, 0x0807);
+
+ /* fixes flashing of image */
+ cx_write(MO_AGC_SYNC_TIP1, 0x0380000F);
+ cx_write(MO_AGC_BACK_VBI, 0x00E00555);
+
+ cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int
+ cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int
+ cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int
+
+ /* Reset on-board parts */
+ cx_write(MO_SRST_IO, 0);
+ msleep(10);
+ cx_write(MO_SRST_IO, 1);
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int inline norm_swidth(v4l2_std_id norm)
+{
+ return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922;
+}
+
+static unsigned int inline norm_hdelay(v4l2_std_id norm)
+{
+ return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 135 : 186;
+}
+
+static unsigned int inline norm_vdelay(v4l2_std_id norm)
+{
+ return (norm & V4L2_STD_625_50) ? 0x24 : 0x18;
+}
+
+static unsigned int inline norm_fsc8(v4l2_std_id norm)
+{
+ if (norm & V4L2_STD_PAL_M)
+ return 28604892; // 3.575611 MHz
+
+ if (norm & (V4L2_STD_PAL_Nc))
+ return 28656448; // 3.582056 MHz
+
+ if (norm & V4L2_STD_NTSC) // All NTSC/M and variants
+ return 28636360; // 3.57954545 MHz +/- 10 Hz
+
+ /* SECAM have also different sub carrier for chroma,
+ but step_db and step_dr, at cx88_set_tvnorm already handles that.
+
+ The same FSC applies to PAL/BGDKIH, PAL/60, NTSC/4.43 and PAL/N
+ */
+
+ return 35468950; // 4.43361875 MHz +/- 5 Hz
+}
+
+static unsigned int inline norm_htotal(v4l2_std_id norm)
+{
+
+ unsigned int fsc4=norm_fsc8(norm)/2;
+
+ /* returns 4*FSC / vtotal / frames per seconds */
+ return (norm & V4L2_STD_625_50) ?
+ ((fsc4+312)/625+12)/25 :
+ ((fsc4+262)/525*1001+15000)/30000;
+}
+
+static unsigned int inline norm_vbipack(v4l2_std_id norm)
+{
+ return (norm & V4L2_STD_625_50) ? 511 : 400;
+}
+
+int cx88_set_scale(struct cx88_core *core, unsigned int width, unsigned int height,
+ enum v4l2_field field)
+{
+ unsigned int swidth = norm_swidth(core->tvnorm);
+ unsigned int sheight = norm_maxh(core->tvnorm);
+ u32 value;
+
+ dprintk(1,"set_scale: %dx%d [%s%s,%s]\n", width, height,
+ V4L2_FIELD_HAS_TOP(field) ? "T" : "",
+ V4L2_FIELD_HAS_BOTTOM(field) ? "B" : "",
+ v4l2_norm_to_name(core->tvnorm));
+ if (!V4L2_FIELD_HAS_BOTH(field))
+ height *= 2;
+
+ // recalc H delay and scale registers
+ value = (width * norm_hdelay(core->tvnorm)) / swidth;
+ value &= 0x3fe;
+ cx_write(MO_HDELAY_EVEN, value);
+ cx_write(MO_HDELAY_ODD, value);
+ dprintk(1,"set_scale: hdelay 0x%04x (width %d)\n", value,swidth);
+
+ value = (swidth * 4096 / width) - 4096;
+ cx_write(MO_HSCALE_EVEN, value);
+ cx_write(MO_HSCALE_ODD, value);
+ dprintk(1,"set_scale: hscale 0x%04x\n", value);
+
+ cx_write(MO_HACTIVE_EVEN, width);
+ cx_write(MO_HACTIVE_ODD, width);
+ dprintk(1,"set_scale: hactive 0x%04x\n", width);
+
+ // recalc V scale Register (delay is constant)
+ cx_write(MO_VDELAY_EVEN, norm_vdelay(core->tvnorm));
+ cx_write(MO_VDELAY_ODD, norm_vdelay(core->tvnorm));
+ dprintk(1,"set_scale: vdelay 0x%04x\n", norm_vdelay(core->tvnorm));
+
+ value = (0x10000 - (sheight * 512 / height - 512)) & 0x1fff;
+ cx_write(MO_VSCALE_EVEN, value);
+ cx_write(MO_VSCALE_ODD, value);
+ dprintk(1,"set_scale: vscale 0x%04x\n", value);
+
+ cx_write(MO_VACTIVE_EVEN, sheight);
+ cx_write(MO_VACTIVE_ODD, sheight);
+ dprintk(1,"set_scale: vactive 0x%04x\n", sheight);
+
+ // setup filters
+ value = 0;
+ value |= (1 << 19); // CFILT (default)
+ if (core->tvnorm & V4L2_STD_SECAM) {
+ value |= (1 << 15);
+ value |= (1 << 16);
+ }
+ if (INPUT(core->input).type == CX88_VMUX_SVIDEO)
+ value |= (1 << 13) | (1 << 5);
+ if (V4L2_FIELD_INTERLACED == field)
+ value |= (1 << 3); // VINT (interlaced vertical scaling)
+ if (width < 385)
+ value |= (1 << 0); // 3-tap interpolation
+ if (width < 193)
+ value |= (1 << 1); // 5-tap interpolation
+ if (nocomb)
+ value |= (3 << 5); // disable comb filter
+
+ cx_andor(MO_FILTER_EVEN, 0x7ffc7f, value); /* preserve PEAKEN, PSEL */
+ cx_andor(MO_FILTER_ODD, 0x7ffc7f, value);
+ dprintk(1,"set_scale: filter 0x%04x\n", value);
+
+ return 0;
+}
+
+static const u32 xtal = 28636363;
+
+static int set_pll(struct cx88_core *core, int prescale, u32 ofreq)
+{
+ static const u32 pre[] = { 0, 0, 0, 3, 2, 1 };
+ u64 pll;
+ u32 reg;
+ int i;
+
+ if (prescale < 2)
+ prescale = 2;
+ if (prescale > 5)
+ prescale = 5;
+
+ pll = ofreq * 8 * prescale * (u64)(1 << 20);
+ do_div(pll,xtal);
+ reg = (pll & 0x3ffffff) | (pre[prescale] << 26);
+ if (((reg >> 20) & 0x3f) < 14) {
+ printk("%s/0: pll out of range\n",core->name);
+ return -1;
+ }
+
+ dprintk(1,"set_pll: MO_PLL_REG 0x%08x [old=0x%08x,freq=%d]\n",
+ reg, cx_read(MO_PLL_REG), ofreq);
+ cx_write(MO_PLL_REG, reg);
+ for (i = 0; i < 100; i++) {
+ reg = cx_read(MO_DEVICE_STATUS);
+ if (reg & (1<<2)) {
+ dprintk(1,"pll locked [pre=%d,ofreq=%d]\n",
+ prescale,ofreq);
+ return 0;
+ }
+ dprintk(1,"pll not locked yet, waiting ...\n");
+ msleep(10);
+ }
+ dprintk(1,"pll NOT locked [pre=%d,ofreq=%d]\n",prescale,ofreq);
+ return -1;
+}
+
+int cx88_start_audio_dma(struct cx88_core *core)
+{
+ /* constant 128 made buzz in analog Nicam-stereo for bigger fifo_size */
+ int bpl = cx88_sram_channels[SRAM_CH25].fifo_size/4;
+
+ int rds_bpl = cx88_sram_channels[SRAM_CH27].fifo_size/AUD_RDS_LINES;
+
+ /* If downstream RISC is enabled, bail out; ALSA is managing DMA */
+ if (cx_read(MO_AUD_DMACNTRL) & 0x10)
+ return 0;
+
+ /* setup fifo + format */
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], bpl, 0);
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], bpl, 0);
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH27],
+ rds_bpl, 0);
+
+ cx_write(MO_AUDD_LNGTH, bpl); /* fifo bpl size */
+ cx_write(MO_AUDR_LNGTH, rds_bpl); /* fifo bpl size */
+
+ /* enable Up, Down and Audio RDS fifo */
+ cx_write(MO_AUD_DMACNTRL, 0x0007);
+
+ return 0;
+}
+
+int cx88_stop_audio_dma(struct cx88_core *core)
+{
+ /* If downstream RISC is enabled, bail out; ALSA is managing DMA */
+ if (cx_read(MO_AUD_DMACNTRL) & 0x10)
+ return 0;
+
+ /* stop dma */
+ cx_write(MO_AUD_DMACNTRL, 0x0000);
+
+ return 0;
+}
+
+static int set_tvaudio(struct cx88_core *core)
+{
+ v4l2_std_id norm = core->tvnorm;
+
+ if (CX88_VMUX_TELEVISION != INPUT(core->input).type &&
+ CX88_VMUX_CABLE != INPUT(core->input).type)
+ return 0;
+
+ if (V4L2_STD_PAL_BG & norm) {
+ core->tvaudio = WW_BG;
+
+ } else if (V4L2_STD_PAL_DK & norm) {
+ core->tvaudio = WW_DK;
+
+ } else if (V4L2_STD_PAL_I & norm) {
+ core->tvaudio = WW_I;
+
+ } else if (V4L2_STD_SECAM_L & norm) {
+ core->tvaudio = WW_L;
+
+ } else if ((V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H) & norm) {
+ core->tvaudio = WW_BG;
+
+ } else if (V4L2_STD_SECAM_DK & norm) {
+ core->tvaudio = WW_DK;
+
+ } else if ((V4L2_STD_NTSC_M & norm) ||
+ (V4L2_STD_PAL_M & norm)) {
+ core->tvaudio = WW_BTSC;
+
+ } else if (V4L2_STD_NTSC_M_JP & norm) {
+ core->tvaudio = WW_EIAJ;
+
+ } else {
+ printk("%s/0: tvaudio support needs work for this tv norm [%s], sorry\n",
+ core->name, v4l2_norm_to_name(core->tvnorm));
+ core->tvaudio = WW_NONE;
+ return 0;
+ }
+
+ cx_andor(MO_AFECFG_IO, 0x1f, 0x0);
+ cx88_set_tvaudio(core);
+ /* cx88_set_stereo(dev,V4L2_TUNER_MODE_STEREO); */
+
+/*
+ This should be needed only on cx88-alsa. It seems that some cx88 chips have
+ bugs and does require DMA enabled for it to work.
+ */
+ cx88_start_audio_dma(core);
+ return 0;
+}
+
+
+
+int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm)
+{
+ u32 fsc8;
+ u32 adc_clock;
+ u32 vdec_clock;
+ u32 step_db,step_dr;
+ u64 tmp64;
+ u32 bdelay,agcdelay,htotal;
+ u32 cxiformat, cxoformat;
+
+ core->tvnorm = norm;
+ fsc8 = norm_fsc8(norm);
+ adc_clock = xtal;
+ vdec_clock = fsc8;
+ step_db = fsc8;
+ step_dr = fsc8;
+
+ if (norm & V4L2_STD_NTSC_M_JP) {
+ cxiformat = VideoFormatNTSCJapan;
+ cxoformat = 0x181f0008;
+ } else if (norm & V4L2_STD_NTSC_443) {
+ cxiformat = VideoFormatNTSC443;
+ cxoformat = 0x181f0008;
+ } else if (norm & V4L2_STD_PAL_M) {
+ cxiformat = VideoFormatPALM;
+ cxoformat = 0x1c1f0008;
+ } else if (norm & V4L2_STD_PAL_N) {
+ cxiformat = VideoFormatPALN;
+ cxoformat = 0x1c1f0008;
+ } else if (norm & V4L2_STD_PAL_Nc) {
+ cxiformat = VideoFormatPALNC;
+ cxoformat = 0x1c1f0008;
+ } else if (norm & V4L2_STD_PAL_60) {
+ cxiformat = VideoFormatPAL60;
+ cxoformat = 0x181f0008;
+ } else if (norm & V4L2_STD_NTSC) {
+ cxiformat = VideoFormatNTSC;
+ cxoformat = 0x181f0008;
+ } else if (norm & V4L2_STD_SECAM) {
+ step_db = 4250000 * 8;
+ step_dr = 4406250 * 8;
+
+ cxiformat = VideoFormatSECAM;
+ cxoformat = 0x181f0008;
+ } else { /* PAL */
+ cxiformat = VideoFormatPAL;
+ cxoformat = 0x181f0008;
+ }
+
+ dprintk(1,"set_tvnorm: \"%s\" fsc8=%d adc=%d vdec=%d db/dr=%d/%d\n",
+ v4l2_norm_to_name(core->tvnorm), fsc8, adc_clock, vdec_clock,
+ step_db, step_dr);
+ set_pll(core,2,vdec_clock);
+
+ dprintk(1,"set_tvnorm: MO_INPUT_FORMAT 0x%08x [old=0x%08x]\n",
+ cxiformat, cx_read(MO_INPUT_FORMAT) & 0x0f);
+ /* Chroma AGC must be disabled if SECAM is used, we enable it
+ by default on PAL and NTSC */
+ cx_andor(MO_INPUT_FORMAT, 0x40f,
+ norm & V4L2_STD_SECAM ? cxiformat : cxiformat | 0x400);
+
+ // FIXME: as-is from DScaler
+ dprintk(1,"set_tvnorm: MO_OUTPUT_FORMAT 0x%08x [old=0x%08x]\n",
+ cxoformat, cx_read(MO_OUTPUT_FORMAT));
+ cx_write(MO_OUTPUT_FORMAT, cxoformat);
+
+ // MO_SCONV_REG = adc clock / video dec clock * 2^17
+ tmp64 = adc_clock * (u64)(1 << 17);
+ do_div(tmp64, vdec_clock);
+ dprintk(1,"set_tvnorm: MO_SCONV_REG 0x%08x [old=0x%08x]\n",
+ (u32)tmp64, cx_read(MO_SCONV_REG));
+ cx_write(MO_SCONV_REG, (u32)tmp64);
+
+ // MO_SUB_STEP = 8 * fsc / video dec clock * 2^22
+ tmp64 = step_db * (u64)(1 << 22);
+ do_div(tmp64, vdec_clock);
+ dprintk(1,"set_tvnorm: MO_SUB_STEP 0x%08x [old=0x%08x]\n",
+ (u32)tmp64, cx_read(MO_SUB_STEP));
+ cx_write(MO_SUB_STEP, (u32)tmp64);
+
+ // MO_SUB_STEP_DR = 8 * 4406250 / video dec clock * 2^22
+ tmp64 = step_dr * (u64)(1 << 22);
+ do_div(tmp64, vdec_clock);
+ dprintk(1,"set_tvnorm: MO_SUB_STEP_DR 0x%08x [old=0x%08x]\n",
+ (u32)tmp64, cx_read(MO_SUB_STEP_DR));
+ cx_write(MO_SUB_STEP_DR, (u32)tmp64);
+
+ // bdelay + agcdelay
+ bdelay = vdec_clock * 65 / 20000000 + 21;
+ agcdelay = vdec_clock * 68 / 20000000 + 15;
+ dprintk(1,"set_tvnorm: MO_AGC_BURST 0x%08x [old=0x%08x,bdelay=%d,agcdelay=%d]\n",
+ (bdelay << 8) | agcdelay, cx_read(MO_AGC_BURST), bdelay, agcdelay);
+ cx_write(MO_AGC_BURST, (bdelay << 8) | agcdelay);
+
+ // htotal
+ tmp64 = norm_htotal(norm) * (u64)vdec_clock;
+ do_div(tmp64, fsc8);
+ htotal = (u32)tmp64;
+ dprintk(1,"set_tvnorm: MO_HTOTAL 0x%08x [old=0x%08x,htotal=%d]\n",
+ htotal, cx_read(MO_HTOTAL), (u32)tmp64);
+ cx_andor(MO_HTOTAL, 0x07ff, htotal);
+
+ // vbi stuff, set vbi offset to 10 (for 20 Clk*2 pixels), this makes
+ // the effective vbi offset ~244 samples, the same as the Bt8x8
+ cx_write(MO_VBI_PACKET, (10<<11) | norm_vbipack(norm));
+
+ // this is needed as well to set all tvnorm parameter
+ cx88_set_scale(core, 320, 240, V4L2_FIELD_INTERLACED);
+
+ // audio
+ set_tvaudio(core);
+
+ // tell i2c chips
+ call_all(core, core, s_std, norm);
+
+ // done
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+struct video_device *cx88_vdev_init(struct cx88_core *core,
+ struct pci_dev *pci,
+ const struct video_device *template_,
+ const char *type)
+{
+ struct video_device *vfd;
+
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+ *vfd = *template_;
+ vfd->v4l2_dev = &core->v4l2_dev;
+ vfd->parent = &pci->dev;
+ vfd->release = video_device_release;
+ snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
+ core->name, type, core->board.name);
+ return vfd;
+}
+
+struct cx88_core* cx88_core_get(struct pci_dev *pci)
+{
+ struct cx88_core *core;
+
+ mutex_lock(&devlist);
+ list_for_each_entry(core, &cx88_devlist, devlist) {
+ if (pci->bus->number != core->pci_bus)
+ continue;
+ if (PCI_SLOT(pci->devfn) != core->pci_slot)
+ continue;
+
+ if (0 != cx88_get_resources(core, pci)) {
+ mutex_unlock(&devlist);
+ return NULL;
+ }
+ atomic_inc(&core->refcount);
+ mutex_unlock(&devlist);
+ return core;
+ }
+
+ core = cx88_core_create(pci, cx88_devcount);
+ if (NULL != core) {
+ cx88_devcount++;
+ list_add_tail(&core->devlist, &cx88_devlist);
+ }
+
+ mutex_unlock(&devlist);
+ return core;
+}
+
+void cx88_core_put(struct cx88_core *core, struct pci_dev *pci)
+{
+ release_mem_region(pci_resource_start(pci,0),
+ pci_resource_len(pci,0));
+
+ if (!atomic_dec_and_test(&core->refcount))
+ return;
+
+ mutex_lock(&devlist);
+ cx88_ir_fini(core);
+ if (0 == core->i2c_rc) {
+ if (core->i2c_rtc)
+ i2c_unregister_device(core->i2c_rtc);
+ i2c_del_adapter(&core->i2c_adap);
+ }
+ list_del(&core->devlist);
+ iounmap(core->lmmio);
+ cx88_devcount--;
+ mutex_unlock(&devlist);
+ v4l2_device_unregister(&core->v4l2_dev);
+ kfree(core);
+}
+
+/* ------------------------------------------------------------------ */
+
+EXPORT_SYMBOL(cx88_print_irqbits);
+
+EXPORT_SYMBOL(cx88_core_irq);
+EXPORT_SYMBOL(cx88_wakeup);
+EXPORT_SYMBOL(cx88_reset);
+EXPORT_SYMBOL(cx88_shutdown);
+
+EXPORT_SYMBOL(cx88_risc_buffer);
+EXPORT_SYMBOL(cx88_risc_databuffer);
+EXPORT_SYMBOL(cx88_risc_stopper);
+EXPORT_SYMBOL(cx88_free_buffer);
+
+EXPORT_SYMBOL(cx88_sram_channels);
+EXPORT_SYMBOL(cx88_sram_channel_setup);
+EXPORT_SYMBOL(cx88_sram_channel_dump);
+
+EXPORT_SYMBOL(cx88_set_tvnorm);
+EXPORT_SYMBOL(cx88_set_scale);
+
+EXPORT_SYMBOL(cx88_vdev_init);
+EXPORT_SYMBOL(cx88_core_get);
+EXPORT_SYMBOL(cx88_core_put);
+
+EXPORT_SYMBOL(cx88_ir_start);
+EXPORT_SYMBOL(cx88_ir_stop);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off
+ */
diff --git a/drivers/media/video/cx88/cx88-dsp.c b/drivers/media/video/cx88/cx88-dsp.c
new file mode 100644
index 00000000..a9907265
--- /dev/null
+++ b/drivers/media/video/cx88/cx88-dsp.c
@@ -0,0 +1,322 @@
+/*
+ *
+ * Stereo and SAP detection for cx88
+ *
+ * Copyright (c) 2009 Marton Balint <cus@fazekas.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <asm/div64.h>
+
+#include "cx88.h"
+#include "cx88-reg.h"
+
+#define INT_PI ((s32)(3.141592653589 * 32768.0))
+
+#define compat_remainder(a, b) \
+ ((float)(((s32)((a)*100))%((s32)((b)*100)))/100.0)
+
+#define baseband_freq(carrier, srate, tone) ((s32)( \
+ (compat_remainder(carrier + tone, srate)) / srate * 2 * INT_PI))
+
+/* We calculate the baseband frequencies of the carrier and the pilot tones
+ * based on the the sampling rate of the audio rds fifo. */
+
+#define FREQ_A2_CARRIER baseband_freq(54687.5, 2689.36, 0.0)
+#define FREQ_A2_DUAL baseband_freq(54687.5, 2689.36, 274.1)
+#define FREQ_A2_STEREO baseband_freq(54687.5, 2689.36, 117.5)
+
+/* The frequencies below are from the reference driver. They probably need
+ * further adjustments, because they are not tested at all. You may even need
+ * to play a bit with the registers of the chip to select the proper signal
+ * for the input of the audio rds fifo, and measure it's sampling rate to
+ * calculate the proper baseband frequencies... */
+
+#define FREQ_A2M_CARRIER ((s32)(2.114516 * 32768.0))
+#define FREQ_A2M_DUAL ((s32)(2.754916 * 32768.0))
+#define FREQ_A2M_STEREO ((s32)(2.462326 * 32768.0))
+
+#define FREQ_EIAJ_CARRIER ((s32)(1.963495 * 32768.0)) /* 5pi/8 */
+#define FREQ_EIAJ_DUAL ((s32)(2.562118 * 32768.0))
+#define FREQ_EIAJ_STEREO ((s32)(2.601053 * 32768.0))
+
+#define FREQ_BTSC_DUAL ((s32)(1.963495 * 32768.0)) /* 5pi/8 */
+#define FREQ_BTSC_DUAL_REF ((s32)(1.374446 * 32768.0)) /* 7pi/16 */
+
+#define FREQ_BTSC_SAP ((s32)(2.471532 * 32768.0))
+#define FREQ_BTSC_SAP_REF ((s32)(1.730072 * 32768.0))
+
+/* The spectrum of the signal should be empty between these frequencies. */
+#define FREQ_NOISE_START ((s32)(0.100000 * 32768.0))
+#define FREQ_NOISE_END ((s32)(1.200000 * 32768.0))
+
+static unsigned int dsp_debug;
+module_param(dsp_debug, int, 0644);
+MODULE_PARM_DESC(dsp_debug, "enable audio dsp debug messages");
+
+#define dprintk(level, fmt, arg...) if (dsp_debug >= level) \
+ printk(KERN_DEBUG "%s/0: " fmt, core->name , ## arg)
+
+static s32 int_cos(u32 x)
+{
+ u32 t2, t4, t6, t8;
+ s32 ret;
+ u16 period = x / INT_PI;
+ if (period % 2)
+ return -int_cos(x - INT_PI);
+ x = x % INT_PI;
+ if (x > INT_PI/2)
+ return -int_cos(INT_PI/2 - (x % (INT_PI/2)));
+ /* Now x is between 0 and INT_PI/2.
+ * To calculate cos(x) we use it's Taylor polinom. */
+ t2 = x*x/32768/2;
+ t4 = t2*x/32768*x/32768/3/4;
+ t6 = t4*x/32768*x/32768/5/6;
+ t8 = t6*x/32768*x/32768/7/8;
+ ret = 32768-t2+t4-t6+t8;
+ return ret;
+}
+
+static u32 int_goertzel(s16 x[], u32 N, u32 freq)
+{
+ /* We use the Goertzel algorithm to determine the power of the
+ * given frequency in the signal */
+ s32 s_prev = 0;
+ s32 s_prev2 = 0;
+ s32 coeff = 2*int_cos(freq);
+ u32 i;
+
+ u64 tmp;
+ u32 divisor;
+
+ for (i = 0; i < N; i++) {
+ s32 s = x[i] + ((s64)coeff*s_prev/32768) - s_prev2;
+ s_prev2 = s_prev;
+ s_prev = s;
+ }
+
+ tmp = (s64)s_prev2 * s_prev2 + (s64)s_prev * s_prev -
+ (s64)coeff * s_prev2 * s_prev / 32768;
+
+ /* XXX: N must be low enough so that N*N fits in s32.
+ * Else we need two divisions. */
+ divisor = N * N;
+ do_div(tmp, divisor);
+
+ return (u32) tmp;
+}
+
+static u32 freq_magnitude(s16 x[], u32 N, u32 freq)
+{
+ u32 sum = int_goertzel(x, N, freq);
+ return (u32)int_sqrt(sum);
+}
+
+static u32 noise_magnitude(s16 x[], u32 N, u32 freq_start, u32 freq_end)
+{
+ int i;
+ u32 sum = 0;
+ u32 freq_step;
+ int samples = 5;
+
+ if (N > 192) {
+ /* The last 192 samples are enough for noise detection */
+ x += (N-192);
+ N = 192;
+ }
+
+ freq_step = (freq_end - freq_start) / (samples - 1);
+
+ for (i = 0; i < samples; i++) {
+ sum += int_goertzel(x, N, freq_start);
+ freq_start += freq_step;
+ }
+
+ return (u32)int_sqrt(sum / samples);
+}
+
+static s32 detect_a2_a2m_eiaj(struct cx88_core *core, s16 x[], u32 N)
+{
+ s32 carrier, stereo, dual, noise;
+ s32 carrier_freq, stereo_freq, dual_freq;
+ s32 ret;
+
+ switch (core->tvaudio) {
+ case WW_BG:
+ case WW_DK:
+ carrier_freq = FREQ_A2_CARRIER;
+ stereo_freq = FREQ_A2_STEREO;
+ dual_freq = FREQ_A2_DUAL;
+ break;
+ case WW_M:
+ carrier_freq = FREQ_A2M_CARRIER;
+ stereo_freq = FREQ_A2M_STEREO;
+ dual_freq = FREQ_A2M_DUAL;
+ break;
+ case WW_EIAJ:
+ carrier_freq = FREQ_EIAJ_CARRIER;
+ stereo_freq = FREQ_EIAJ_STEREO;
+ dual_freq = FREQ_EIAJ_DUAL;
+ break;
+ default:
+ printk(KERN_WARNING "%s/0: unsupported audio mode %d for %s\n",
+ core->name, core->tvaudio, __func__);
+ return UNSET;
+ }
+
+ carrier = freq_magnitude(x, N, carrier_freq);
+ stereo = freq_magnitude(x, N, stereo_freq);
+ dual = freq_magnitude(x, N, dual_freq);
+ noise = noise_magnitude(x, N, FREQ_NOISE_START, FREQ_NOISE_END);
+
+ dprintk(1, "detect a2/a2m/eiaj: carrier=%d, stereo=%d, dual=%d, "
+ "noise=%d\n", carrier, stereo, dual, noise);
+
+ if (stereo > dual)
+ ret = V4L2_TUNER_SUB_STEREO;
+ else
+ ret = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+
+ if (core->tvaudio == WW_EIAJ) {
+ /* EIAJ checks may need adjustments */
+ if ((carrier > max(stereo, dual)*2) &&
+ (carrier < max(stereo, dual)*6) &&
+ (carrier > 20 && carrier < 200) &&
+ (max(stereo, dual) > min(stereo, dual))) {
+ /* For EIAJ the carrier is always present,
+ so we probably don't need noise detection */
+ return ret;
+ }
+ } else {
+ if ((carrier > max(stereo, dual)*2) &&
+ (carrier < max(stereo, dual)*8) &&
+ (carrier > 20 && carrier < 200) &&
+ (noise < 10) &&
+ (max(stereo, dual) > min(stereo, dual)*2)) {
+ return ret;
+ }
+ }
+ return V4L2_TUNER_SUB_MONO;
+}
+
+static s32 detect_btsc(struct cx88_core *core, s16 x[], u32 N)
+{
+ s32 sap_ref = freq_magnitude(x, N, FREQ_BTSC_SAP_REF);
+ s32 sap = freq_magnitude(x, N, FREQ_BTSC_SAP);
+ s32 dual_ref = freq_magnitude(x, N, FREQ_BTSC_DUAL_REF);
+ s32 dual = freq_magnitude(x, N, FREQ_BTSC_DUAL);
+ dprintk(1, "detect btsc: dual_ref=%d, dual=%d, sap_ref=%d, sap=%d"
+ "\n", dual_ref, dual, sap_ref, sap);
+ /* FIXME: Currently not supported */
+ return UNSET;
+}
+
+static s16 *read_rds_samples(struct cx88_core *core, u32 *N)
+{
+ const struct sram_channel *srch = &cx88_sram_channels[SRAM_CH27];
+ s16 *samples;
+
+ unsigned int i;
+ unsigned int bpl = srch->fifo_size/AUD_RDS_LINES;
+ unsigned int spl = bpl/4;
+ unsigned int sample_count = spl*(AUD_RDS_LINES-1);
+
+ u32 current_address = cx_read(srch->ptr1_reg);
+ u32 offset = (current_address - srch->fifo_start + bpl);
+
+ dprintk(1, "read RDS samples: current_address=%08x (offset=%08x), "
+ "sample_count=%d, aud_intstat=%08x\n", current_address,
+ current_address - srch->fifo_start, sample_count,
+ cx_read(MO_AUD_INTSTAT));
+
+ samples = kmalloc(sizeof(s16)*sample_count, GFP_KERNEL);
+ if (!samples)
+ return NULL;
+
+ *N = sample_count;
+
+ for (i = 0; i < sample_count; i++) {
+ offset = offset % (AUD_RDS_LINES*bpl);
+ samples[i] = cx_read(srch->fifo_start + offset);
+ offset += 4;
+ }
+
+ if (dsp_debug >= 2) {
+ dprintk(2, "RDS samples dump: ");
+ for (i = 0; i < sample_count; i++)
+ printk("%hd ", samples[i]);
+ printk(".\n");
+ }
+
+ return samples;
+}
+
+s32 cx88_dsp_detect_stereo_sap(struct cx88_core *core)
+{
+ s16 *samples;
+ u32 N = 0;
+ s32 ret = UNSET;
+
+ /* If audio RDS fifo is disabled, we can't read the samples */
+ if (!(cx_read(MO_AUD_DMACNTRL) & 0x04))
+ return ret;
+ if (!(cx_read(AUD_CTL) & EN_FMRADIO_EN_RDS))
+ return ret;
+
+ /* Wait at least 500 ms after an audio standard change */
+ if (time_before(jiffies, core->last_change + msecs_to_jiffies(500)))
+ return ret;
+
+ samples = read_rds_samples(core, &N);
+
+ if (!samples)
+ return ret;
+
+ switch (core->tvaudio) {
+ case WW_BG:
+ case WW_DK:
+ case WW_EIAJ:
+ case WW_M:
+ ret = detect_a2_a2m_eiaj(core, samples, N);
+ break;
+ case WW_BTSC:
+ ret = detect_btsc(core, samples, N);
+ break;
+ case WW_NONE:
+ case WW_I:
+ case WW_L:
+ case WW_I2SPT:
+ case WW_FM:
+ case WW_I2SADC:
+ break;
+ }
+
+ kfree(samples);
+
+ if (UNSET != ret)
+ dprintk(1, "stereo/sap detection result:%s%s%s\n",
+ (ret & V4L2_TUNER_SUB_MONO) ? " mono" : "",
+ (ret & V4L2_TUNER_SUB_STEREO) ? " stereo" : "",
+ (ret & V4L2_TUNER_SUB_LANG2) ? " dual" : "");
+
+ return ret;
+}
+EXPORT_SYMBOL(cx88_dsp_detect_stereo_sap);
+
diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c
new file mode 100644
index 00000000..003937cd
--- /dev/null
+++ b/drivers/media/video/cx88/cx88-dvb.c
@@ -0,0 +1,1778 @@
+/*
+ *
+ * device driver for Conexant 2388x based TV cards
+ * MPEG Transport Stream (DVB) routines
+ *
+ * (c) 2004, 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au>
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/kthread.h>
+#include <linux/file.h>
+#include <linux/suspend.h>
+
+#include "cx88.h"
+#include "dvb-pll.h"
+#include <media/v4l2-common.h>
+
+#include "mt352.h"
+#include "mt352_priv.h"
+#include "cx88-vp3054-i2c.h"
+#include "zl10353.h"
+#include "cx22702.h"
+#include "or51132.h"
+#include "lgdt330x.h"
+#include "s5h1409.h"
+#include "xc4000.h"
+#include "xc5000.h"
+#include "nxt200x.h"
+#include "cx24123.h"
+#include "isl6421.h"
+#include "tuner-simple.h"
+#include "tda9887.h"
+#include "s5h1411.h"
+#include "stv0299.h"
+#include "z0194a.h"
+#include "stv0288.h"
+#include "stb6000.h"
+#include "cx24116.h"
+#include "stv0900.h"
+#include "stb6100.h"
+#include "stb6100_proc.h"
+#include "mb86a16.h"
+#include "ds3000.h"
+
+MODULE_DESCRIPTION("driver for cx2388x based DVB cards");
+MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(CX88_VERSION);
+
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug,"enable debug messages [dvb]");
+
+static unsigned int dvb_buf_tscnt = 32;
+module_param(dvb_buf_tscnt, int, 0644);
+MODULE_PARM_DESC(dvb_buf_tscnt, "DVB Buffer TS count [dvb]");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define dprintk(level,fmt, arg...) if (debug >= level) \
+ printk(KERN_DEBUG "%s/2-dvb: " fmt, core->name, ## arg)
+
+/* ------------------------------------------------------------------ */
+
+static int dvb_buf_setup(struct videobuf_queue *q,
+ unsigned int *count, unsigned int *size)
+{
+ struct cx8802_dev *dev = q->priv_data;
+
+ dev->ts_packet_size = 188 * 4;
+ dev->ts_packet_count = dvb_buf_tscnt;
+
+ *size = dev->ts_packet_size * dev->ts_packet_count;
+ *count = dvb_buf_tscnt;
+ return 0;
+}
+
+static int dvb_buf_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb, enum v4l2_field field)
+{
+ struct cx8802_dev *dev = q->priv_data;
+ return cx8802_buf_prepare(q, dev, (struct cx88_buffer*)vb,field);
+}
+
+static void dvb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct cx8802_dev *dev = q->priv_data;
+ cx8802_buf_queue(dev, (struct cx88_buffer*)vb);
+}
+
+static void dvb_buf_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ cx88_free_buffer(q, (struct cx88_buffer*)vb);
+}
+
+static const struct videobuf_queue_ops dvb_qops = {
+ .buf_setup = dvb_buf_setup,
+ .buf_prepare = dvb_buf_prepare,
+ .buf_queue = dvb_buf_queue,
+ .buf_release = dvb_buf_release,
+};
+
+/* ------------------------------------------------------------------ */
+
+static int cx88_dvb_bus_ctrl(struct dvb_frontend* fe, int acquire)
+{
+ struct cx8802_dev *dev= fe->dvb->priv;
+ struct cx8802_driver *drv = NULL;
+ int ret = 0;
+ int fe_id;
+
+ fe_id = videobuf_dvb_find_frontend(&dev->frontends, fe);
+ if (!fe_id) {
+ printk(KERN_ERR "%s() No frontend found\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&dev->core->lock);
+ drv = cx8802_get_driver(dev, CX88_MPEG_DVB);
+ if (drv) {
+ if (acquire){
+ dev->frontends.active_fe_id = fe_id;
+ ret = drv->request_acquire(drv);
+ } else {
+ ret = drv->request_release(drv);
+ dev->frontends.active_fe_id = 0;
+ }
+ }
+ mutex_unlock(&dev->core->lock);
+
+ return ret;
+}
+
+static void cx88_dvb_gate_ctrl(struct cx88_core *core, int open)
+{
+ struct videobuf_dvb_frontends *f;
+ struct videobuf_dvb_frontend *fe;
+
+ if (!core->dvbdev)
+ return;
+
+ f = &core->dvbdev->frontends;
+
+ if (!f)
+ return;
+
+ if (f->gate <= 1) /* undefined or fe0 */
+ fe = videobuf_dvb_get_frontend(f, 1);
+ else
+ fe = videobuf_dvb_get_frontend(f, f->gate);
+
+ if (fe && fe->dvb.frontend && fe->dvb.frontend->ops.i2c_gate_ctrl)
+ fe->dvb.frontend->ops.i2c_gate_ctrl(fe->dvb.frontend, open);
+}
+
+/* ------------------------------------------------------------------ */
+
+static int dvico_fusionhdtv_demod_init(struct dvb_frontend* fe)
+{
+ static const u8 clock_config [] = { CLOCK_CTL, 0x38, 0x39 };
+ static const u8 reset [] = { RESET, 0x80 };
+ static const u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 };
+ static const u8 agc_cfg [] = { AGC_TARGET, 0x24, 0x20 };
+ static const u8 gpp_ctl_cfg [] = { GPP_CTL, 0x33 };
+ static const u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 };
+
+ mt352_write(fe, clock_config, sizeof(clock_config));
+ udelay(200);
+ mt352_write(fe, reset, sizeof(reset));
+ mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
+
+ mt352_write(fe, agc_cfg, sizeof(agc_cfg));
+ mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg));
+ mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+ return 0;
+}
+
+static int dvico_dual_demod_init(struct dvb_frontend *fe)
+{
+ static const u8 clock_config [] = { CLOCK_CTL, 0x38, 0x38 };
+ static const u8 reset [] = { RESET, 0x80 };
+ static const u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 };
+ static const u8 agc_cfg [] = { AGC_TARGET, 0x28, 0x20 };
+ static const u8 gpp_ctl_cfg [] = { GPP_CTL, 0x33 };
+ static const u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 };
+
+ mt352_write(fe, clock_config, sizeof(clock_config));
+ udelay(200);
+ mt352_write(fe, reset, sizeof(reset));
+ mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
+
+ mt352_write(fe, agc_cfg, sizeof(agc_cfg));
+ mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg));
+ mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+
+ return 0;
+}
+
+static int dntv_live_dvbt_demod_init(struct dvb_frontend* fe)
+{
+ static const u8 clock_config [] = { 0x89, 0x38, 0x39 };
+ static const u8 reset [] = { 0x50, 0x80 };
+ static const u8 adc_ctl_1_cfg [] = { 0x8E, 0x40 };
+ static const u8 agc_cfg [] = { 0x67, 0x10, 0x23, 0x00, 0xFF, 0xFF,
+ 0x00, 0xFF, 0x00, 0x40, 0x40 };
+ static const u8 dntv_extra[] = { 0xB5, 0x7A };
+ static const u8 capt_range_cfg[] = { 0x75, 0x32 };
+
+ mt352_write(fe, clock_config, sizeof(clock_config));
+ udelay(2000);
+ mt352_write(fe, reset, sizeof(reset));
+ mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
+
+ mt352_write(fe, agc_cfg, sizeof(agc_cfg));
+ udelay(2000);
+ mt352_write(fe, dntv_extra, sizeof(dntv_extra));
+ mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+
+ return 0;
+}
+
+static const struct mt352_config dvico_fusionhdtv = {
+ .demod_address = 0x0f,
+ .demod_init = dvico_fusionhdtv_demod_init,
+};
+
+static const struct mt352_config dntv_live_dvbt_config = {
+ .demod_address = 0x0f,
+ .demod_init = dntv_live_dvbt_demod_init,
+};
+
+static const struct mt352_config dvico_fusionhdtv_dual = {
+ .demod_address = 0x0f,
+ .demod_init = dvico_dual_demod_init,
+};
+
+static const struct zl10353_config cx88_terratec_cinergy_ht_pci_mkii_config = {
+ .demod_address = (0x1e >> 1),
+ .no_tuner = 1,
+ .if2 = 45600,
+};
+
+static struct mb86a16_config twinhan_vp1027 = {
+ .demod_address = 0x08,
+};
+
+#if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE))
+static int dntv_live_dvbt_pro_demod_init(struct dvb_frontend* fe)
+{
+ static const u8 clock_config [] = { 0x89, 0x38, 0x38 };
+ static const u8 reset [] = { 0x50, 0x80 };
+ static const u8 adc_ctl_1_cfg [] = { 0x8E, 0x40 };
+ static const u8 agc_cfg [] = { 0x67, 0x10, 0x20, 0x00, 0xFF, 0xFF,
+ 0x00, 0xFF, 0x00, 0x40, 0x40 };
+ static const u8 dntv_extra[] = { 0xB5, 0x7A };
+ static const u8 capt_range_cfg[] = { 0x75, 0x32 };
+
+ mt352_write(fe, clock_config, sizeof(clock_config));
+ udelay(2000);
+ mt352_write(fe, reset, sizeof(reset));
+ mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
+
+ mt352_write(fe, agc_cfg, sizeof(agc_cfg));
+ udelay(2000);
+ mt352_write(fe, dntv_extra, sizeof(dntv_extra));
+ mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+
+ return 0;
+}
+
+static const struct mt352_config dntv_live_dvbt_pro_config = {
+ .demod_address = 0x0f,
+ .no_tuner = 1,
+ .demod_init = dntv_live_dvbt_pro_demod_init,
+};
+#endif
+
+static const struct zl10353_config dvico_fusionhdtv_hybrid = {
+ .demod_address = 0x0f,
+ .no_tuner = 1,
+};
+
+static const struct zl10353_config dvico_fusionhdtv_xc3028 = {
+ .demod_address = 0x0f,
+ .if2 = 45600,
+ .no_tuner = 1,
+};
+
+static const struct mt352_config dvico_fusionhdtv_mt352_xc3028 = {
+ .demod_address = 0x0f,
+ .if2 = 4560,
+ .no_tuner = 1,
+ .demod_init = dvico_fusionhdtv_demod_init,
+};
+
+static const struct zl10353_config dvico_fusionhdtv_plus_v1_1 = {
+ .demod_address = 0x0f,
+};
+
+static const struct cx22702_config connexant_refboard_config = {
+ .demod_address = 0x43,
+ .output_mode = CX22702_SERIAL_OUTPUT,
+};
+
+static const struct cx22702_config hauppauge_hvr_config = {
+ .demod_address = 0x63,
+ .output_mode = CX22702_SERIAL_OUTPUT,
+};
+
+static int or51132_set_ts_param(struct dvb_frontend* fe, int is_punctured)
+{
+ struct cx8802_dev *dev= fe->dvb->priv;
+ dev->ts_gen_cntrl = is_punctured ? 0x04 : 0x00;
+ return 0;
+}
+
+static const struct or51132_config pchdtv_hd3000 = {
+ .demod_address = 0x15,
+ .set_ts_params = or51132_set_ts_param,
+};
+
+static int lgdt330x_pll_rf_set(struct dvb_frontend* fe, int index)
+{
+ struct cx8802_dev *dev= fe->dvb->priv;
+ struct cx88_core *core = dev->core;
+
+ dprintk(1, "%s: index = %d\n", __func__, index);
+ if (index == 0)
+ cx_clear(MO_GP0_IO, 8);
+ else
+ cx_set(MO_GP0_IO, 8);
+ return 0;
+}
+
+static int lgdt330x_set_ts_param(struct dvb_frontend* fe, int is_punctured)
+{
+ struct cx8802_dev *dev= fe->dvb->priv;
+ if (is_punctured)
+ dev->ts_gen_cntrl |= 0x04;
+ else
+ dev->ts_gen_cntrl &= ~0x04;
+ return 0;
+}
+
+static struct lgdt330x_config fusionhdtv_3_gold = {
+ .demod_address = 0x0e,
+ .demod_chip = LGDT3302,
+ .serial_mpeg = 0x04, /* TPSERIAL for 3302 in TOP_CONTROL */
+ .set_ts_params = lgdt330x_set_ts_param,
+};
+
+static const struct lgdt330x_config fusionhdtv_5_gold = {
+ .demod_address = 0x0e,
+ .demod_chip = LGDT3303,
+ .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */
+ .set_ts_params = lgdt330x_set_ts_param,
+};
+
+static const struct lgdt330x_config pchdtv_hd5500 = {
+ .demod_address = 0x59,
+ .demod_chip = LGDT3303,
+ .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */
+ .set_ts_params = lgdt330x_set_ts_param,
+};
+
+static int nxt200x_set_ts_param(struct dvb_frontend* fe, int is_punctured)
+{
+ struct cx8802_dev *dev= fe->dvb->priv;
+ dev->ts_gen_cntrl = is_punctured ? 0x04 : 0x00;
+ return 0;
+}
+
+static const struct nxt200x_config ati_hdtvwonder = {
+ .demod_address = 0x0a,
+ .set_ts_params = nxt200x_set_ts_param,
+};
+
+static int cx24123_set_ts_param(struct dvb_frontend* fe,
+ int is_punctured)
+{
+ struct cx8802_dev *dev= fe->dvb->priv;
+ dev->ts_gen_cntrl = 0x02;
+ return 0;
+}
+
+static int kworld_dvbs_100_set_voltage(struct dvb_frontend* fe,
+ fe_sec_voltage_t voltage)
+{
+ struct cx8802_dev *dev= fe->dvb->priv;
+ struct cx88_core *core = dev->core;
+
+ if (voltage == SEC_VOLTAGE_OFF)
+ cx_write(MO_GP0_IO, 0x000006fb);
+ else
+ cx_write(MO_GP0_IO, 0x000006f9);
+
+ if (core->prev_set_voltage)
+ return core->prev_set_voltage(fe, voltage);
+ return 0;
+}
+
+static int geniatech_dvbs_set_voltage(struct dvb_frontend *fe,
+ fe_sec_voltage_t voltage)
+{
+ struct cx8802_dev *dev= fe->dvb->priv;
+ struct cx88_core *core = dev->core;
+
+ if (voltage == SEC_VOLTAGE_OFF) {
+ dprintk(1,"LNB Voltage OFF\n");
+ cx_write(MO_GP0_IO, 0x0000efff);
+ }
+
+ if (core->prev_set_voltage)
+ return core->prev_set_voltage(fe, voltage);
+ return 0;
+}
+
+static int tevii_dvbs_set_voltage(struct dvb_frontend *fe,
+ fe_sec_voltage_t voltage)
+{
+ struct cx8802_dev *dev= fe->dvb->priv;
+ struct cx88_core *core = dev->core;
+
+ cx_set(MO_GP0_IO, 0x6040);
+ switch (voltage) {
+ case SEC_VOLTAGE_13:
+ cx_clear(MO_GP0_IO, 0x20);
+ break;
+ case SEC_VOLTAGE_18:
+ cx_set(MO_GP0_IO, 0x20);
+ break;
+ case SEC_VOLTAGE_OFF:
+ cx_clear(MO_GP0_IO, 0x20);
+ break;
+ }
+
+ if (core->prev_set_voltage)
+ return core->prev_set_voltage(fe, voltage);
+ return 0;
+}
+
+static int vp1027_set_voltage(struct dvb_frontend *fe,
+ fe_sec_voltage_t voltage)
+{
+ struct cx8802_dev *dev = fe->dvb->priv;
+ struct cx88_core *core = dev->core;
+
+ switch (voltage) {
+ case SEC_VOLTAGE_13:
+ dprintk(1, "LNB SEC Voltage=13\n");
+ cx_write(MO_GP0_IO, 0x00001220);
+ break;
+ case SEC_VOLTAGE_18:
+ dprintk(1, "LNB SEC Voltage=18\n");
+ cx_write(MO_GP0_IO, 0x00001222);
+ break;
+ case SEC_VOLTAGE_OFF:
+ dprintk(1, "LNB Voltage OFF\n");
+ cx_write(MO_GP0_IO, 0x00001230);
+ break;
+ }
+
+ if (core->prev_set_voltage)
+ return core->prev_set_voltage(fe, voltage);
+ return 0;
+}
+
+static const struct cx24123_config geniatech_dvbs_config = {
+ .demod_address = 0x55,
+ .set_ts_params = cx24123_set_ts_param,
+};
+
+static const struct cx24123_config hauppauge_novas_config = {
+ .demod_address = 0x55,
+ .set_ts_params = cx24123_set_ts_param,
+};
+
+static const struct cx24123_config kworld_dvbs_100_config = {
+ .demod_address = 0x15,
+ .set_ts_params = cx24123_set_ts_param,
+ .lnb_polarity = 1,
+};
+
+static const struct s5h1409_config pinnacle_pctv_hd_800i_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_PARALLEL_OUTPUT,
+ .gpio = S5H1409_GPIO_ON,
+ .qam_if = 44000,
+ .inversion = S5H1409_INVERSION_OFF,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK,
+};
+
+static const struct s5h1409_config dvico_hdtv5_pci_nano_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_SERIAL_OUTPUT,
+ .gpio = S5H1409_GPIO_OFF,
+ .inversion = S5H1409_INVERSION_OFF,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static const struct s5h1409_config kworld_atsc_120_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_SERIAL_OUTPUT,
+ .gpio = S5H1409_GPIO_OFF,
+ .inversion = S5H1409_INVERSION_OFF,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static const struct xc5000_config pinnacle_pctv_hd_800i_tuner_config = {
+ .i2c_address = 0x64,
+ .if_khz = 5380,
+};
+
+static const struct zl10353_config cx88_pinnacle_hybrid_pctv = {
+ .demod_address = (0x1e >> 1),
+ .no_tuner = 1,
+ .if2 = 45600,
+};
+
+static const struct zl10353_config cx88_geniatech_x8000_mt = {
+ .demod_address = (0x1e >> 1),
+ .no_tuner = 1,
+ .disable_i2c_gate_ctrl = 1,
+};
+
+static const struct s5h1411_config dvico_fusionhdtv7_config = {
+ .output_mode = S5H1411_SERIAL_OUTPUT,
+ .gpio = S5H1411_GPIO_ON,
+ .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+ .qam_if = S5H1411_IF_44000,
+ .vsb_if = S5H1411_IF_44000,
+ .inversion = S5H1411_INVERSION_OFF,
+ .status_mode = S5H1411_DEMODLOCKING
+};
+
+static const struct xc5000_config dvico_fusionhdtv7_tuner_config = {
+ .i2c_address = 0xc2 >> 1,
+ .if_khz = 5380,
+};
+
+static int attach_xc3028(u8 addr, struct cx8802_dev *dev)
+{
+ struct dvb_frontend *fe;
+ struct videobuf_dvb_frontend *fe0 = NULL;
+ struct xc2028_ctrl ctl;
+ struct xc2028_config cfg = {
+ .i2c_adap = &dev->core->i2c_adap,
+ .i2c_addr = addr,
+ .ctrl = &ctl,
+ };
+
+ /* Get the first frontend */
+ fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
+ if (!fe0)
+ return -EINVAL;
+
+ if (!fe0->dvb.frontend) {
+ printk(KERN_ERR "%s/2: dvb frontend not attached. "
+ "Can't attach xc3028\n",
+ dev->core->name);
+ return -EINVAL;
+ }
+
+ /*
+ * Some xc3028 devices may be hidden by an I2C gate. This is known
+ * to happen with some s5h1409-based devices.
+ * Now that I2C gate is open, sets up xc3028 configuration
+ */
+ cx88_setup_xc3028(dev->core, &ctl);
+
+ fe = dvb_attach(xc2028_attach, fe0->dvb.frontend, &cfg);
+ if (!fe) {
+ printk(KERN_ERR "%s/2: xc3028 attach failed\n",
+ dev->core->name);
+ dvb_frontend_detach(fe0->dvb.frontend);
+ dvb_unregister_frontend(fe0->dvb.frontend);
+ fe0->dvb.frontend = NULL;
+ return -EINVAL;
+ }
+
+ printk(KERN_INFO "%s/2: xc3028 attached\n",
+ dev->core->name);
+
+ return 0;
+}
+
+static int attach_xc4000(struct cx8802_dev *dev, struct xc4000_config *cfg)
+{
+ struct dvb_frontend *fe;
+ struct videobuf_dvb_frontend *fe0 = NULL;
+
+ /* Get the first frontend */
+ fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
+ if (!fe0)
+ return -EINVAL;
+
+ if (!fe0->dvb.frontend) {
+ printk(KERN_ERR "%s/2: dvb frontend not attached. "
+ "Can't attach xc4000\n",
+ dev->core->name);
+ return -EINVAL;
+ }
+
+ fe = dvb_attach(xc4000_attach, fe0->dvb.frontend, &dev->core->i2c_adap,
+ cfg);
+ if (!fe) {
+ printk(KERN_ERR "%s/2: xc4000 attach failed\n",
+ dev->core->name);
+ dvb_frontend_detach(fe0->dvb.frontend);
+ dvb_unregister_frontend(fe0->dvb.frontend);
+ fe0->dvb.frontend = NULL;
+ return -EINVAL;
+ }
+
+ printk(KERN_INFO "%s/2: xc4000 attached\n", dev->core->name);
+
+ return 0;
+}
+
+static int cx24116_set_ts_param(struct dvb_frontend *fe,
+ int is_punctured)
+{
+ struct cx8802_dev *dev = fe->dvb->priv;
+ dev->ts_gen_cntrl = 0x2;
+
+ return 0;
+}
+
+static int stv0900_set_ts_param(struct dvb_frontend *fe,
+ int is_punctured)
+{
+ struct cx8802_dev *dev = fe->dvb->priv;
+ dev->ts_gen_cntrl = 0;
+
+ return 0;
+}
+
+static int cx24116_reset_device(struct dvb_frontend *fe)
+{
+ struct cx8802_dev *dev = fe->dvb->priv;
+ struct cx88_core *core = dev->core;
+
+ /* Reset the part */
+ /* Put the cx24116 into reset */
+ cx_write(MO_SRST_IO, 0);
+ msleep(10);
+ /* Take the cx24116 out of reset */
+ cx_write(MO_SRST_IO, 1);
+ msleep(10);
+
+ return 0;
+}
+
+static const struct cx24116_config hauppauge_hvr4000_config = {
+ .demod_address = 0x05,
+ .set_ts_params = cx24116_set_ts_param,
+ .reset_device = cx24116_reset_device,
+};
+
+static const struct cx24116_config tevii_s460_config = {
+ .demod_address = 0x55,
+ .set_ts_params = cx24116_set_ts_param,
+ .reset_device = cx24116_reset_device,
+};
+
+static int ds3000_set_ts_param(struct dvb_frontend *fe,
+ int is_punctured)
+{
+ struct cx8802_dev *dev = fe->dvb->priv;
+ dev->ts_gen_cntrl = 4;
+
+ return 0;
+}
+
+static struct ds3000_config tevii_ds3000_config = {
+ .demod_address = 0x68,
+ .set_ts_params = ds3000_set_ts_param,
+};
+
+static const struct stv0900_config prof_7301_stv0900_config = {
+ .demod_address = 0x6a,
+/* demod_mode = 0,*/
+ .xtal = 27000000,
+ .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */
+ .diseqc_mode = 2,/* 2/3 PWM */
+ .tun1_maddress = 0,/* 0x60 */
+ .tun1_adc = 0,/* 2 Vpp */
+ .path1_mode = 3,
+ .set_ts_params = stv0900_set_ts_param,
+};
+
+static const struct stb6100_config prof_7301_stb6100_config = {
+ .tuner_address = 0x60,
+ .refclock = 27000000,
+};
+
+static const struct stv0299_config tevii_tuner_sharp_config = {
+ .demod_address = 0x68,
+ .inittab = sharp_z0194a_inittab,
+ .mclk = 88000000UL,
+ .invert = 1,
+ .skip_reinit = 0,
+ .lock_output = 1,
+ .volt13_op0_op1 = STV0299_VOLT13_OP1,
+ .min_delay_ms = 100,
+ .set_symbol_rate = sharp_z0194a_set_symbol_rate,
+ .set_ts_params = cx24116_set_ts_param,
+};
+
+static const struct stv0288_config tevii_tuner_earda_config = {
+ .demod_address = 0x68,
+ .min_delay_ms = 100,
+ .set_ts_params = cx24116_set_ts_param,
+};
+
+static int cx8802_alloc_frontends(struct cx8802_dev *dev)
+{
+ struct cx88_core *core = dev->core;
+ struct videobuf_dvb_frontend *fe = NULL;
+ int i;
+
+ mutex_init(&dev->frontends.lock);
+ INIT_LIST_HEAD(&dev->frontends.felist);
+
+ if (!core->board.num_frontends)
+ return -ENODEV;
+
+ printk(KERN_INFO "%s() allocating %d frontend(s)\n", __func__,
+ core->board.num_frontends);
+ for (i = 1; i <= core->board.num_frontends; i++) {
+ fe = videobuf_dvb_alloc_frontend(&dev->frontends, i);
+ if (!fe) {
+ printk(KERN_ERR "%s() failed to alloc\n", __func__);
+ videobuf_dvb_dealloc_frontends(&dev->frontends);
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+
+
+
+static const u8 samsung_smt_7020_inittab[] = {
+ 0x01, 0x15,
+ 0x02, 0x00,
+ 0x03, 0x00,
+ 0x04, 0x7D,
+ 0x05, 0x0F,
+ 0x06, 0x02,
+ 0x07, 0x00,
+ 0x08, 0x60,
+
+ 0x0A, 0xC2,
+ 0x0B, 0x00,
+ 0x0C, 0x01,
+ 0x0D, 0x81,
+ 0x0E, 0x44,
+ 0x0F, 0x09,
+ 0x10, 0x3C,
+ 0x11, 0x84,
+ 0x12, 0xDA,
+ 0x13, 0x99,
+ 0x14, 0x8D,
+ 0x15, 0xCE,
+ 0x16, 0xE8,
+ 0x17, 0x43,
+ 0x18, 0x1C,
+ 0x19, 0x1B,
+ 0x1A, 0x1D,
+
+ 0x1C, 0x12,
+ 0x1D, 0x00,
+ 0x1E, 0x00,
+ 0x1F, 0x00,
+ 0x20, 0x00,
+ 0x21, 0x00,
+ 0x22, 0x00,
+ 0x23, 0x00,
+
+ 0x28, 0x02,
+ 0x29, 0x28,
+ 0x2A, 0x14,
+ 0x2B, 0x0F,
+ 0x2C, 0x09,
+ 0x2D, 0x05,
+
+ 0x31, 0x1F,
+ 0x32, 0x19,
+ 0x33, 0xFC,
+ 0x34, 0x13,
+ 0xff, 0xff,
+};
+
+
+static int samsung_smt_7020_tuner_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct cx8802_dev *dev = fe->dvb->priv;
+ u8 buf[4];
+ u32 div;
+ struct i2c_msg msg = {
+ .addr = 0x61,
+ .flags = 0,
+ .buf = buf,
+ .len = sizeof(buf) };
+
+ div = c->frequency / 125;
+
+ buf[0] = (div >> 8) & 0x7f;
+ buf[1] = div & 0xff;
+ buf[2] = 0x84; /* 0xC4 */
+ buf[3] = 0x00;
+
+ if (c->frequency < 1500000)
+ buf[3] |= 0x10;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ if (i2c_transfer(&dev->core->i2c_adap, &msg, 1) != 1)
+ return -EIO;
+
+ return 0;
+}
+
+static int samsung_smt_7020_set_tone(struct dvb_frontend *fe,
+ fe_sec_tone_mode_t tone)
+{
+ struct cx8802_dev *dev = fe->dvb->priv;
+ struct cx88_core *core = dev->core;
+
+ cx_set(MO_GP0_IO, 0x0800);
+
+ switch (tone) {
+ case SEC_TONE_ON:
+ cx_set(MO_GP0_IO, 0x08);
+ break;
+ case SEC_TONE_OFF:
+ cx_clear(MO_GP0_IO, 0x08);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int samsung_smt_7020_set_voltage(struct dvb_frontend *fe,
+ fe_sec_voltage_t voltage)
+{
+ struct cx8802_dev *dev = fe->dvb->priv;
+ struct cx88_core *core = dev->core;
+
+ u8 data;
+ struct i2c_msg msg = {
+ .addr = 8,
+ .flags = 0,
+ .buf = &data,
+ .len = sizeof(data) };
+
+ cx_set(MO_GP0_IO, 0x8000);
+
+ switch (voltage) {
+ case SEC_VOLTAGE_OFF:
+ break;
+ case SEC_VOLTAGE_13:
+ data = ISL6421_EN1 | ISL6421_LLC1;
+ cx_clear(MO_GP0_IO, 0x80);
+ break;
+ case SEC_VOLTAGE_18:
+ data = ISL6421_EN1 | ISL6421_LLC1 | ISL6421_VSEL1;
+ cx_clear(MO_GP0_IO, 0x80);
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ return (i2c_transfer(&dev->core->i2c_adap, &msg, 1) == 1) ? 0 : -EIO;
+}
+
+static int samsung_smt_7020_stv0299_set_symbol_rate(struct dvb_frontend *fe,
+ u32 srate, u32 ratio)
+{
+ u8 aclk = 0;
+ u8 bclk = 0;
+
+ if (srate < 1500000) {
+ aclk = 0xb7;
+ bclk = 0x47;
+ } else if (srate < 3000000) {
+ aclk = 0xb7;
+ bclk = 0x4b;
+ } else if (srate < 7000000) {
+ aclk = 0xb7;
+ bclk = 0x4f;
+ } else if (srate < 14000000) {
+ aclk = 0xb7;
+ bclk = 0x53;
+ } else if (srate < 30000000) {
+ aclk = 0xb6;
+ bclk = 0x53;
+ } else if (srate < 45000000) {
+ aclk = 0xb4;
+ bclk = 0x51;
+ }
+
+ stv0299_writereg(fe, 0x13, aclk);
+ stv0299_writereg(fe, 0x14, bclk);
+ stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff);
+ stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff);
+ stv0299_writereg(fe, 0x21, ratio & 0xf0);
+
+ return 0;
+}
+
+
+static const struct stv0299_config samsung_stv0299_config = {
+ .demod_address = 0x68,
+ .inittab = samsung_smt_7020_inittab,
+ .mclk = 88000000UL,
+ .invert = 0,
+ .skip_reinit = 0,
+ .lock_output = STV0299_LOCKOUTPUT_LK,
+ .volt13_op0_op1 = STV0299_VOLT13_OP1,
+ .min_delay_ms = 100,
+ .set_symbol_rate = samsung_smt_7020_stv0299_set_symbol_rate,
+};
+
+static int dvb_register(struct cx8802_dev *dev)
+{
+ struct cx88_core *core = dev->core;
+ struct videobuf_dvb_frontend *fe0, *fe1 = NULL;
+ int mfe_shared = 0; /* bus not shared by default */
+ int res = -EINVAL;
+
+ if (0 != core->i2c_rc) {
+ printk(KERN_ERR "%s/2: no i2c-bus available, cannot attach dvb drivers\n", core->name);
+ goto frontend_detach;
+ }
+
+ /* Get the first frontend */
+ fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
+ if (!fe0)
+ goto frontend_detach;
+
+ /* multi-frontend gate control is undefined or defaults to fe0 */
+ dev->frontends.gate = 0;
+
+ /* Sets the gate control callback to be used by i2c command calls */
+ core->gate_ctrl = cx88_dvb_gate_ctrl;
+
+ /* init frontend(s) */
+ switch (core->boardnr) {
+ case CX88_BOARD_HAUPPAUGE_DVB_T1:
+ fe0->dvb.frontend = dvb_attach(cx22702_attach,
+ &connexant_refboard_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
+ 0x61, &core->i2c_adap,
+ DVB_PLL_THOMSON_DTT759X))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1:
+ case CX88_BOARD_CONEXANT_DVB_T1:
+ case CX88_BOARD_KWORLD_DVB_T_CX22702:
+ case CX88_BOARD_WINFAST_DTV1000:
+ fe0->dvb.frontend = dvb_attach(cx22702_attach,
+ &connexant_refboard_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
+ 0x60, &core->i2c_adap,
+ DVB_PLL_THOMSON_DTT7579))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_WINFAST_DTV2000H:
+ case CX88_BOARD_HAUPPAUGE_HVR1100:
+ case CX88_BOARD_HAUPPAUGE_HVR1100LP:
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
+ fe0->dvb.frontend = dvb_attach(cx22702_attach,
+ &hauppauge_hvr_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &core->i2c_adap, 0x61,
+ TUNER_PHILIPS_FMD1216ME_MK3))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_WINFAST_DTV2000H_J:
+ fe0->dvb.frontend = dvb_attach(cx22702_attach,
+ &hauppauge_hvr_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &core->i2c_adap, 0x61,
+ TUNER_PHILIPS_FMD1216MEX_MK3))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_HAUPPAUGE_HVR3000:
+ /* MFE frontend 1 */
+ mfe_shared = 1;
+ dev->frontends.gate = 2;
+ /* DVB-S init */
+ fe0->dvb.frontend = dvb_attach(cx24123_attach,
+ &hauppauge_novas_config,
+ &dev->core->i2c_adap);
+ if (fe0->dvb.frontend) {
+ if (!dvb_attach(isl6421_attach,
+ fe0->dvb.frontend,
+ &dev->core->i2c_adap,
+ 0x08, ISL6421_DCL, 0x00))
+ goto frontend_detach;
+ }
+ /* MFE frontend 2 */
+ fe1 = videobuf_dvb_get_frontend(&dev->frontends, 2);
+ if (!fe1)
+ goto frontend_detach;
+ /* DVB-T init */
+ fe1->dvb.frontend = dvb_attach(cx22702_attach,
+ &hauppauge_hvr_config,
+ &dev->core->i2c_adap);
+ if (fe1->dvb.frontend) {
+ fe1->dvb.frontend->id = 1;
+ if (!dvb_attach(simple_tuner_attach,
+ fe1->dvb.frontend,
+ &dev->core->i2c_adap,
+ 0x61, TUNER_PHILIPS_FMD1216ME_MK3))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS:
+ fe0->dvb.frontend = dvb_attach(mt352_attach,
+ &dvico_fusionhdtv,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
+ 0x60, NULL, DVB_PLL_THOMSON_DTT7579))
+ goto frontend_detach;
+ break;
+ }
+ /* ZL10353 replaces MT352 on later cards */
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &dvico_fusionhdtv_plus_v1_1,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
+ 0x60, NULL, DVB_PLL_THOMSON_DTT7579))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL:
+ /* The tin box says DEE1601, but it seems to be DTT7579
+ * compatible, with a slightly different MT352 AGC gain. */
+ fe0->dvb.frontend = dvb_attach(mt352_attach,
+ &dvico_fusionhdtv_dual,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
+ 0x61, NULL, DVB_PLL_THOMSON_DTT7579))
+ goto frontend_detach;
+ break;
+ }
+ /* ZL10353 replaces MT352 on later cards */
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &dvico_fusionhdtv_plus_v1_1,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
+ 0x61, NULL, DVB_PLL_THOMSON_DTT7579))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1:
+ fe0->dvb.frontend = dvb_attach(mt352_attach,
+ &dvico_fusionhdtv,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
+ 0x61, NULL, DVB_PLL_LG_Z201))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_KWORLD_DVB_T:
+ case CX88_BOARD_DNTV_LIVE_DVB_T:
+ case CX88_BOARD_ADSTECH_DVB_T_PCI:
+ fe0->dvb.frontend = dvb_attach(mt352_attach,
+ &dntv_live_dvbt_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
+ 0x61, NULL, DVB_PLL_UNKNOWN_1))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_DNTV_LIVE_DVB_T_PRO:
+#if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE))
+ /* MT352 is on a secondary I2C bus made from some GPIO lines */
+ fe0->dvb.frontend = dvb_attach(mt352_attach, &dntv_live_dvbt_pro_config,
+ &dev->vp3054->adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &core->i2c_adap, 0x61,
+ TUNER_PHILIPS_FMD1216ME_MK3))
+ goto frontend_detach;
+ }
+#else
+ printk(KERN_ERR "%s/2: built without vp3054 support\n",
+ core->name);
+#endif
+ break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID:
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &dvico_fusionhdtv_hybrid,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &core->i2c_adap, 0x61,
+ TUNER_THOMSON_FE6600))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO:
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &dvico_fusionhdtv_xc3028,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend == NULL)
+ fe0->dvb.frontend = dvb_attach(mt352_attach,
+ &dvico_fusionhdtv_mt352_xc3028,
+ &core->i2c_adap);
+ /*
+ * On this board, the demod provides the I2C bus pullup.
+ * We must not permit gate_ctrl to be performed, or
+ * the xc3028 cannot communicate on the bus.
+ */
+ if (fe0->dvb.frontend)
+ fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL;
+ if (attach_xc3028(0x61, dev) < 0)
+ goto frontend_detach;
+ break;
+ case CX88_BOARD_PCHDTV_HD3000:
+ fe0->dvb.frontend = dvb_attach(or51132_attach, &pchdtv_hd3000,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &core->i2c_adap, 0x61,
+ TUNER_THOMSON_DTT761X))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q:
+ dev->ts_gen_cntrl = 0x08;
+
+ /* Do a hardware reset of chip before using it. */
+ cx_clear(MO_GP0_IO, 1);
+ mdelay(100);
+ cx_set(MO_GP0_IO, 1);
+ mdelay(200);
+
+ /* Select RF connector callback */
+ fusionhdtv_3_gold.pll_rf_set = lgdt330x_pll_rf_set;
+ fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
+ &fusionhdtv_3_gold,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &core->i2c_adap, 0x61,
+ TUNER_MICROTUNE_4042FI5))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T:
+ dev->ts_gen_cntrl = 0x08;
+
+ /* Do a hardware reset of chip before using it. */
+ cx_clear(MO_GP0_IO, 1);
+ mdelay(100);
+ cx_set(MO_GP0_IO, 9);
+ mdelay(200);
+ fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
+ &fusionhdtv_3_gold,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &core->i2c_adap, 0x61,
+ TUNER_THOMSON_DTT761X))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD:
+ dev->ts_gen_cntrl = 0x08;
+
+ /* Do a hardware reset of chip before using it. */
+ cx_clear(MO_GP0_IO, 1);
+ mdelay(100);
+ cx_set(MO_GP0_IO, 1);
+ mdelay(200);
+ fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
+ &fusionhdtv_5_gold,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &core->i2c_adap, 0x61,
+ TUNER_LG_TDVS_H06XF))
+ goto frontend_detach;
+ if (!dvb_attach(tda9887_attach, fe0->dvb.frontend,
+ &core->i2c_adap, 0x43))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_PCHDTV_HD5500:
+ dev->ts_gen_cntrl = 0x08;
+
+ /* Do a hardware reset of chip before using it. */
+ cx_clear(MO_GP0_IO, 1);
+ mdelay(100);
+ cx_set(MO_GP0_IO, 1);
+ mdelay(200);
+ fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
+ &pchdtv_hd5500,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &core->i2c_adap, 0x61,
+ TUNER_LG_TDVS_H06XF))
+ goto frontend_detach;
+ if (!dvb_attach(tda9887_attach, fe0->dvb.frontend,
+ &core->i2c_adap, 0x43))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_ATI_HDTVWONDER:
+ fe0->dvb.frontend = dvb_attach(nxt200x_attach,
+ &ati_hdtvwonder,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &core->i2c_adap, 0x61,
+ TUNER_PHILIPS_TUV1236D))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
+ case CX88_BOARD_HAUPPAUGE_NOVASE2_S1:
+ fe0->dvb.frontend = dvb_attach(cx24123_attach,
+ &hauppauge_novas_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend) {
+ if (!dvb_attach(isl6421_attach, fe0->dvb.frontend,
+ &core->i2c_adap, 0x08, ISL6421_DCL, 0x00))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_KWORLD_DVBS_100:
+ fe0->dvb.frontend = dvb_attach(cx24123_attach,
+ &kworld_dvbs_100_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend) {
+ core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage;
+ fe0->dvb.frontend->ops.set_voltage = kworld_dvbs_100_set_voltage;
+ }
+ break;
+ case CX88_BOARD_GENIATECH_DVBS:
+ fe0->dvb.frontend = dvb_attach(cx24123_attach,
+ &geniatech_dvbs_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend) {
+ core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage;
+ fe0->dvb.frontend->ops.set_voltage = geniatech_dvbs_set_voltage;
+ }
+ break;
+ case CX88_BOARD_PINNACLE_PCTV_HD_800i:
+ fe0->dvb.frontend = dvb_attach(s5h1409_attach,
+ &pinnacle_pctv_hd_800i_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(xc5000_attach, fe0->dvb.frontend,
+ &core->i2c_adap,
+ &pinnacle_pctv_hd_800i_tuner_config))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
+ fe0->dvb.frontend = dvb_attach(s5h1409_attach,
+ &dvico_hdtv5_pci_nano_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ struct dvb_frontend *fe;
+ struct xc2028_config cfg = {
+ .i2c_adap = &core->i2c_adap,
+ .i2c_addr = 0x61,
+ };
+ static struct xc2028_ctrl ctl = {
+ .fname = XC2028_DEFAULT_FIRMWARE,
+ .max_len = 64,
+ .scode_table = XC3028_FE_OREN538,
+ };
+
+ fe = dvb_attach(xc2028_attach,
+ fe0->dvb.frontend, &cfg);
+ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+ fe->ops.tuner_ops.set_config(fe, &ctl);
+ }
+ break;
+ case CX88_BOARD_PINNACLE_HYBRID_PCTV:
+ case CX88_BOARD_WINFAST_DTV1800H:
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &cx88_pinnacle_hybrid_pctv,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend) {
+ fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL;
+ if (attach_xc3028(0x61, dev) < 0)
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_WINFAST_DTV1800H_XC4000:
+ case CX88_BOARD_WINFAST_DTV2000H_PLUS:
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &cx88_pinnacle_hybrid_pctv,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend) {
+ struct xc4000_config cfg = {
+ .i2c_address = 0x61,
+ .default_pm = 0,
+ .dvb_amplitude = 134,
+ .set_smoothedcvbs = 1,
+ .if_khz = 4560
+ };
+ fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL;
+ if (attach_xc4000(dev, &cfg) < 0)
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_GENIATECH_X8000_MT:
+ dev->ts_gen_cntrl = 0x00;
+
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &cx88_geniatech_x8000_mt,
+ &core->i2c_adap);
+ if (attach_xc3028(0x61, dev) < 0)
+ goto frontend_detach;
+ break;
+ case CX88_BOARD_KWORLD_ATSC_120:
+ fe0->dvb.frontend = dvb_attach(s5h1409_attach,
+ &kworld_atsc_120_config,
+ &core->i2c_adap);
+ if (attach_xc3028(0x61, dev) < 0)
+ goto frontend_detach;
+ break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD:
+ fe0->dvb.frontend = dvb_attach(s5h1411_attach,
+ &dvico_fusionhdtv7_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(xc5000_attach, fe0->dvb.frontend,
+ &core->i2c_adap,
+ &dvico_fusionhdtv7_tuner_config))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_HAUPPAUGE_HVR4000:
+ /* MFE frontend 1 */
+ mfe_shared = 1;
+ dev->frontends.gate = 2;
+ /* DVB-S/S2 Init */
+ fe0->dvb.frontend = dvb_attach(cx24116_attach,
+ &hauppauge_hvr4000_config,
+ &dev->core->i2c_adap);
+ if (fe0->dvb.frontend) {
+ if (!dvb_attach(isl6421_attach,
+ fe0->dvb.frontend,
+ &dev->core->i2c_adap,
+ 0x08, ISL6421_DCL, 0x00))
+ goto frontend_detach;
+ }
+ /* MFE frontend 2 */
+ fe1 = videobuf_dvb_get_frontend(&dev->frontends, 2);
+ if (!fe1)
+ goto frontend_detach;
+ /* DVB-T Init */
+ fe1->dvb.frontend = dvb_attach(cx22702_attach,
+ &hauppauge_hvr_config,
+ &dev->core->i2c_adap);
+ if (fe1->dvb.frontend) {
+ fe1->dvb.frontend->id = 1;
+ if (!dvb_attach(simple_tuner_attach,
+ fe1->dvb.frontend,
+ &dev->core->i2c_adap,
+ 0x61, TUNER_PHILIPS_FMD1216ME_MK3))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
+ fe0->dvb.frontend = dvb_attach(cx24116_attach,
+ &hauppauge_hvr4000_config,
+ &dev->core->i2c_adap);
+ if (fe0->dvb.frontend) {
+ if (!dvb_attach(isl6421_attach,
+ fe0->dvb.frontend,
+ &dev->core->i2c_adap,
+ 0x08, ISL6421_DCL, 0x00))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_PROF_6200:
+ case CX88_BOARD_TBS_8910:
+ case CX88_BOARD_TEVII_S420:
+ fe0->dvb.frontend = dvb_attach(stv0299_attach,
+ &tevii_tuner_sharp_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend, 0x60,
+ &core->i2c_adap, DVB_PLL_OPERA1))
+ goto frontend_detach;
+ core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage;
+ fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
+
+ } else {
+ fe0->dvb.frontend = dvb_attach(stv0288_attach,
+ &tevii_tuner_earda_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(stb6000_attach, fe0->dvb.frontend, 0x61,
+ &core->i2c_adap))
+ goto frontend_detach;
+ core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage;
+ fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
+ }
+ }
+ break;
+ case CX88_BOARD_TEVII_S460:
+ fe0->dvb.frontend = dvb_attach(cx24116_attach,
+ &tevii_s460_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL)
+ fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
+ break;
+ case CX88_BOARD_TEVII_S464:
+ fe0->dvb.frontend = dvb_attach(ds3000_attach,
+ &tevii_ds3000_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL)
+ fe0->dvb.frontend->ops.set_voltage =
+ tevii_dvbs_set_voltage;
+ break;
+ case CX88_BOARD_OMICOM_SS4_PCI:
+ case CX88_BOARD_TBS_8920:
+ case CX88_BOARD_PROF_7300:
+ case CX88_BOARD_SATTRADE_ST4200:
+ fe0->dvb.frontend = dvb_attach(cx24116_attach,
+ &hauppauge_hvr4000_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL)
+ fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
+ break;
+ case CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII:
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &cx88_terratec_cinergy_ht_pci_mkii_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend) {
+ fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL;
+ if (attach_xc3028(0x61, dev) < 0)
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_PROF_7301:{
+ struct dvb_tuner_ops *tuner_ops = NULL;
+
+ fe0->dvb.frontend = dvb_attach(stv0900_attach,
+ &prof_7301_stv0900_config,
+ &core->i2c_adap, 0);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(stb6100_attach, fe0->dvb.frontend,
+ &prof_7301_stb6100_config,
+ &core->i2c_adap))
+ goto frontend_detach;
+
+ tuner_ops = &fe0->dvb.frontend->ops.tuner_ops;
+ tuner_ops->set_frequency = stb6100_set_freq;
+ tuner_ops->get_frequency = stb6100_get_freq;
+ tuner_ops->set_bandwidth = stb6100_set_bandw;
+ tuner_ops->get_bandwidth = stb6100_get_bandw;
+
+ core->prev_set_voltage =
+ fe0->dvb.frontend->ops.set_voltage;
+ fe0->dvb.frontend->ops.set_voltage =
+ tevii_dvbs_set_voltage;
+ }
+ break;
+ }
+ case CX88_BOARD_SAMSUNG_SMT_7020:
+ dev->ts_gen_cntrl = 0x08;
+
+ cx_set(MO_GP0_IO, 0x0101);
+
+ cx_clear(MO_GP0_IO, 0x01);
+ mdelay(100);
+ cx_set(MO_GP0_IO, 0x01);
+ mdelay(200);
+
+ fe0->dvb.frontend = dvb_attach(stv0299_attach,
+ &samsung_stv0299_config,
+ &dev->core->i2c_adap);
+ if (fe0->dvb.frontend) {
+ fe0->dvb.frontend->ops.tuner_ops.set_params =
+ samsung_smt_7020_tuner_set_params;
+ fe0->dvb.frontend->tuner_priv =
+ &dev->core->i2c_adap;
+ fe0->dvb.frontend->ops.set_voltage =
+ samsung_smt_7020_set_voltage;
+ fe0->dvb.frontend->ops.set_tone =
+ samsung_smt_7020_set_tone;
+ }
+
+ break;
+ case CX88_BOARD_TWINHAN_VP1027_DVBS:
+ dev->ts_gen_cntrl = 0x00;
+ fe0->dvb.frontend = dvb_attach(mb86a16_attach,
+ &twinhan_vp1027,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend) {
+ core->prev_set_voltage =
+ fe0->dvb.frontend->ops.set_voltage;
+ fe0->dvb.frontend->ops.set_voltage =
+ vp1027_set_voltage;
+ }
+ break;
+
+ default:
+ printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n",
+ core->name);
+ break;
+ }
+
+ if ( (NULL == fe0->dvb.frontend) || (fe1 && NULL == fe1->dvb.frontend) ) {
+ printk(KERN_ERR
+ "%s/2: frontend initialization failed\n",
+ core->name);
+ goto frontend_detach;
+ }
+ /* define general-purpose callback pointer */
+ fe0->dvb.frontend->callback = cx88_tuner_callback;
+
+ /* Ensure all frontends negotiate bus access */
+ fe0->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl;
+ if (fe1)
+ fe1->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl;
+
+ /* Put the analog decoder in standby to keep it quiet */
+ call_all(core, core, s_power, 0);
+
+ /* register everything */
+ res = videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev,
+ &dev->pci->dev, adapter_nr, mfe_shared, NULL);
+ if (res)
+ goto frontend_detach;
+ return res;
+
+frontend_detach:
+ core->gate_ctrl = NULL;
+ videobuf_dvb_dealloc_frontends(&dev->frontends);
+ return res;
+}
+
+/* ----------------------------------------------------------- */
+
+/* CX8802 MPEG -> mini driver - We have been given the hardware */
+static int cx8802_dvb_advise_acquire(struct cx8802_driver *drv)
+{
+ struct cx88_core *core = drv->core;
+ int err = 0;
+ dprintk( 1, "%s\n", __func__);
+
+ switch (core->boardnr) {
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
+ /* We arrive here with either the cx23416 or the cx22702
+ * on the bus. Take the bus from the cx23416 and enable the
+ * cx22702 demod
+ */
+ /* Toggle reset on cx22702 leaving i2c active */
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ cx_clear(MO_GP0_IO, 0x00000080);
+ udelay(50);
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ /* enable the cx22702 pins */
+ cx_clear(MO_GP0_IO, 0x00000004);
+ udelay(1000);
+ break;
+
+ case CX88_BOARD_HAUPPAUGE_HVR3000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000:
+ /* Toggle reset on cx22702 leaving i2c active */
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ cx_clear(MO_GP0_IO, 0x00000080);
+ udelay(50);
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ switch (core->dvbdev->frontends.active_fe_id) {
+ case 1: /* DVB-S/S2 Enabled */
+ /* tri-state the cx22702 pins */
+ cx_set(MO_GP0_IO, 0x00000004);
+ /* Take the cx24116/cx24123 out of reset */
+ cx_write(MO_SRST_IO, 1);
+ core->dvbdev->ts_gen_cntrl = 0x02; /* Parallel IO */
+ break;
+ case 2: /* DVB-T Enabled */
+ /* Put the cx24116/cx24123 into reset */
+ cx_write(MO_SRST_IO, 0);
+ /* enable the cx22702 pins */
+ cx_clear(MO_GP0_IO, 0x00000004);
+ core->dvbdev->ts_gen_cntrl = 0x0c; /* Serial IO */
+ break;
+ }
+ udelay(1000);
+ break;
+
+ case CX88_BOARD_WINFAST_DTV2000H_PLUS:
+ /* set RF input to AIR for DVB-T (GPIO 16) */
+ cx_write(MO_GP2_IO, 0x0101);
+ break;
+
+ default:
+ err = -ENODEV;
+ }
+ return err;
+}
+
+/* CX8802 MPEG -> mini driver - We no longer have the hardware */
+static int cx8802_dvb_advise_release(struct cx8802_driver *drv)
+{
+ struct cx88_core *core = drv->core;
+ int err = 0;
+ dprintk( 1, "%s\n", __func__);
+
+ switch (core->boardnr) {
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
+ /* Do Nothing, leave the cx22702 on the bus. */
+ break;
+ case CX88_BOARD_HAUPPAUGE_HVR3000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000:
+ break;
+ default:
+ err = -ENODEV;
+ }
+ return err;
+}
+
+static int cx8802_dvb_probe(struct cx8802_driver *drv)
+{
+ struct cx88_core *core = drv->core;
+ struct cx8802_dev *dev = drv->core->dvbdev;
+ int err;
+ struct videobuf_dvb_frontend *fe;
+ int i;
+
+ dprintk( 1, "%s\n", __func__);
+ dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n",
+ core->boardnr,
+ core->name,
+ core->pci_bus,
+ core->pci_slot);
+
+ err = -ENODEV;
+ if (!(core->board.mpeg & CX88_MPEG_DVB))
+ goto fail_core;
+
+ /* If vp3054 isn't enabled, a stub will just return 0 */
+ err = vp3054_i2c_probe(dev);
+ if (0 != err)
+ goto fail_core;
+
+ /* dvb stuff */
+ printk(KERN_INFO "%s/2: cx2388x based DVB/ATSC card\n", core->name);
+ dev->ts_gen_cntrl = 0x0c;
+
+ err = cx8802_alloc_frontends(dev);
+ if (err)
+ goto fail_core;
+
+ err = -ENODEV;
+ for (i = 1; i <= core->board.num_frontends; i++) {
+ fe = videobuf_dvb_get_frontend(&core->dvbdev->frontends, i);
+ if (fe == NULL) {
+ printk(KERN_ERR "%s() failed to get frontend(%d)\n",
+ __func__, i);
+ goto fail_probe;
+ }
+ videobuf_queue_sg_init(&fe->dvb.dvbq, &dvb_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_TOP,
+ sizeof(struct cx88_buffer),
+ dev, NULL);
+ /* init struct videobuf_dvb */
+ fe->dvb.name = dev->core->name;
+ }
+
+ err = dvb_register(dev);
+ if (err)
+ /* frontends/adapter de-allocated in dvb_register */
+ printk(KERN_ERR "%s/2: dvb_register failed (err = %d)\n",
+ core->name, err);
+ return err;
+fail_probe:
+ videobuf_dvb_dealloc_frontends(&core->dvbdev->frontends);
+fail_core:
+ return err;
+}
+
+static int cx8802_dvb_remove(struct cx8802_driver *drv)
+{
+ struct cx88_core *core = drv->core;
+ struct cx8802_dev *dev = drv->core->dvbdev;
+
+ dprintk( 1, "%s\n", __func__);
+
+ videobuf_dvb_unregister_bus(&dev->frontends);
+
+ vp3054_i2c_remove(dev);
+
+ core->gate_ctrl = NULL;
+
+ return 0;
+}
+
+static struct cx8802_driver cx8802_dvb_driver = {
+ .type_id = CX88_MPEG_DVB,
+ .hw_access = CX8802_DRVCTL_SHARED,
+ .probe = cx8802_dvb_probe,
+ .remove = cx8802_dvb_remove,
+ .advise_acquire = cx8802_dvb_advise_acquire,
+ .advise_release = cx8802_dvb_advise_release,
+};
+
+static int __init dvb_init(void)
+{
+ printk(KERN_INFO "cx88/2: cx2388x dvb driver version %s loaded\n",
+ CX88_VERSION);
+ return cx8802_register_driver(&cx8802_dvb_driver);
+}
+
+static void __exit dvb_fini(void)
+{
+ cx8802_unregister_driver(&cx8802_dvb_driver);
+}
+
+module_init(dvb_init);
+module_exit(dvb_fini);
diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c
new file mode 100644
index 00000000..de0f1af7
--- /dev/null
+++ b/drivers/media/video/cx88/cx88-i2c.c
@@ -0,0 +1,184 @@
+
+/*
+
+ cx88-i2c.c -- all the i2c code is here
+
+ Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
+ & Marcus Metzler (mocm@thp.uni-koeln.de)
+ (c) 2002 Yurij Sysoev <yurij@naturesoft.net>
+ (c) 1999-2003 Gerd Knorr <kraxel@bytesex.org>
+
+ (c) 2005 Mauro Carvalho Chehab <mchehab@infradead.org>
+ - Multituner support and i2c address binding
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+
+#include "cx88.h"
+#include <media/v4l2-common.h>
+
+static unsigned int i2c_debug;
+module_param(i2c_debug, int, 0644);
+MODULE_PARM_DESC(i2c_debug,"enable debug messages [i2c]");
+
+static unsigned int i2c_scan;
+module_param(i2c_scan, int, 0444);
+MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time");
+
+static unsigned int i2c_udelay = 5;
+module_param(i2c_udelay, int, 0644);
+MODULE_PARM_DESC(i2c_udelay,"i2c delay at insmod time, in usecs "
+ "(should be 5 or higher). Lower value means higher bus speed.");
+
+#define dprintk(level,fmt, arg...) if (i2c_debug >= level) \
+ printk(KERN_DEBUG "%s: " fmt, core->name , ## arg)
+
+/* ----------------------------------------------------------------------- */
+
+static void cx8800_bit_setscl(void *data, int state)
+{
+ struct cx88_core *core = data;
+
+ if (state)
+ core->i2c_state |= 0x02;
+ else
+ core->i2c_state &= ~0x02;
+ cx_write(MO_I2C, core->i2c_state);
+ cx_read(MO_I2C);
+}
+
+static void cx8800_bit_setsda(void *data, int state)
+{
+ struct cx88_core *core = data;
+
+ if (state)
+ core->i2c_state |= 0x01;
+ else
+ core->i2c_state &= ~0x01;
+ cx_write(MO_I2C, core->i2c_state);
+ cx_read(MO_I2C);
+}
+
+static int cx8800_bit_getscl(void *data)
+{
+ struct cx88_core *core = data;
+ u32 state;
+
+ state = cx_read(MO_I2C);
+ return state & 0x02 ? 1 : 0;
+}
+
+static int cx8800_bit_getsda(void *data)
+{
+ struct cx88_core *core = data;
+ u32 state;
+
+ state = cx_read(MO_I2C);
+ return state & 0x01;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct i2c_algo_bit_data cx8800_i2c_algo_template = {
+ .setsda = cx8800_bit_setsda,
+ .setscl = cx8800_bit_setscl,
+ .getsda = cx8800_bit_getsda,
+ .getscl = cx8800_bit_getscl,
+ .udelay = 16,
+ .timeout = 200,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static const char * const i2c_devs[128] = {
+ [ 0x1c >> 1 ] = "lgdt330x",
+ [ 0x86 >> 1 ] = "tda9887/cx22702",
+ [ 0xa0 >> 1 ] = "eeprom",
+ [ 0xc0 >> 1 ] = "tuner (analog)",
+ [ 0xc2 >> 1 ] = "tuner (analog/dvb)",
+ [ 0xc8 >> 1 ] = "xc5000",
+};
+
+static void do_i2c_scan(const char *name, struct i2c_client *c)
+{
+ unsigned char buf;
+ int i,rc;
+
+ for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) {
+ c->addr = i;
+ rc = i2c_master_recv(c,&buf,0);
+ if (rc < 0)
+ continue;
+ printk("%s: i2c scan: found device @ 0x%x [%s]\n",
+ name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
+ }
+}
+
+/* init + register i2c adapter */
+int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci)
+{
+ /* Prevents usage of invalid delay values */
+ if (i2c_udelay<5)
+ i2c_udelay=5;
+
+ memcpy(&core->i2c_algo, &cx8800_i2c_algo_template,
+ sizeof(core->i2c_algo));
+
+
+ core->i2c_adap.dev.parent = &pci->dev;
+ strlcpy(core->i2c_adap.name,core->name,sizeof(core->i2c_adap.name));
+ core->i2c_adap.owner = THIS_MODULE;
+ core->i2c_algo.udelay = i2c_udelay;
+ core->i2c_algo.data = core;
+ i2c_set_adapdata(&core->i2c_adap, &core->v4l2_dev);
+ core->i2c_adap.algo_data = &core->i2c_algo;
+ core->i2c_client.adapter = &core->i2c_adap;
+ strlcpy(core->i2c_client.name, "cx88xx internal", I2C_NAME_SIZE);
+
+ cx8800_bit_setscl(core,1);
+ cx8800_bit_setsda(core,1);
+
+ core->i2c_rc = i2c_bit_add_bus(&core->i2c_adap);
+ if (0 == core->i2c_rc) {
+ static u8 tuner_data[] =
+ { 0x0b, 0xdc, 0x86, 0x52 };
+ static struct i2c_msg tuner_msg =
+ { .flags = 0, .addr = 0xc2 >> 1, .buf = tuner_data, .len = 4 };
+
+ dprintk(1, "i2c register ok\n");
+ switch( core->boardnr ) {
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
+ case CX88_BOARD_HAUPPAUGE_HVR3000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000:
+ printk("%s: i2c init: enabling analog demod on HVR1300/3000/4000 tuner\n",
+ core->name);
+ i2c_transfer(core->i2c_client.adapter, &tuner_msg, 1);
+ break;
+ default:
+ break;
+ }
+ if (i2c_scan)
+ do_i2c_scan(core->name,&core->i2c_client);
+ } else
+ printk("%s: i2c register FAILED\n", core->name);
+
+ return core->i2c_rc;
+}
diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c
new file mode 100644
index 00000000..ebf448c4
--- /dev/null
+++ b/drivers/media/video/cx88/cx88-input.c
@@ -0,0 +1,635 @@
+/*
+ *
+ * Device driver for GPIO attached remote control interfaces
+ * on Conexant 2388x based TV/DVB cards.
+ *
+ * Copyright (c) 2003 Pavel Machek
+ * Copyright (c) 2004 Gerd Knorr
+ * Copyright (c) 2004, 2005 Chris Pascoe
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/hrtimer.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#include "cx88.h"
+#include <media/rc-core.h>
+
+#define MODULE_NAME "cx88xx"
+
+/* ---------------------------------------------------------------------- */
+
+struct cx88_IR {
+ struct cx88_core *core;
+ struct rc_dev *dev;
+
+ int users;
+
+ char name[32];
+ char phys[32];
+
+ /* sample from gpio pin 16 */
+ u32 sampling;
+
+ /* poll external decoder */
+ int polling;
+ struct hrtimer timer;
+ u32 gpio_addr;
+ u32 last_gpio;
+ u32 mask_keycode;
+ u32 mask_keydown;
+ u32 mask_keyup;
+};
+
+static unsigned ir_samplerate = 4;
+module_param(ir_samplerate, uint, 0444);
+MODULE_PARM_DESC(ir_samplerate, "IR samplerate in kHz, 1 - 20, default 4");
+
+static int ir_debug;
+module_param(ir_debug, int, 0644); /* debug level [IR] */
+MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]");
+
+#define ir_dprintk(fmt, arg...) if (ir_debug) \
+ printk(KERN_DEBUG "%s IR: " fmt , ir->core->name , ##arg)
+
+#define dprintk(fmt, arg...) if (ir_debug) \
+ printk(KERN_DEBUG "cx88 IR: " fmt , ##arg)
+
+/* ---------------------------------------------------------------------- */
+
+static void cx88_ir_handle_key(struct cx88_IR *ir)
+{
+ struct cx88_core *core = ir->core;
+ u32 gpio, data, auxgpio;
+
+ /* read gpio value */
+ gpio = cx_read(ir->gpio_addr);
+ switch (core->boardnr) {
+ case CX88_BOARD_NPGTECH_REALTV_TOP10FM:
+ /* This board apparently uses a combination of 2 GPIO
+ to represent the keys. Additionally, the second GPIO
+ can be used for parity.
+
+ Example:
+
+ for key "5"
+ gpio = 0x758, auxgpio = 0xe5 or 0xf5
+ for key "Power"
+ gpio = 0x758, auxgpio = 0xed or 0xfd
+ */
+
+ auxgpio = cx_read(MO_GP1_IO);
+ /* Take out the parity part */
+ gpio=(gpio & 0x7fd) + (auxgpio & 0xef);
+ break;
+ case CX88_BOARD_WINFAST_DTV1000:
+ case CX88_BOARD_WINFAST_DTV1800H:
+ case CX88_BOARD_WINFAST_DTV1800H_XC4000:
+ case CX88_BOARD_WINFAST_DTV2000H_PLUS:
+ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL:
+ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36:
+ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43:
+ gpio = (gpio & 0x6ff) | ((cx_read(MO_GP1_IO) << 8) & 0x900);
+ auxgpio = gpio;
+ break;
+ default:
+ auxgpio = gpio;
+ }
+ if (ir->polling) {
+ if (ir->last_gpio == auxgpio)
+ return;
+ ir->last_gpio = auxgpio;
+ }
+
+ /* extract data */
+ data = ir_extract_bits(gpio, ir->mask_keycode);
+ ir_dprintk("irq gpio=0x%x code=%d | %s%s%s\n",
+ gpio, data,
+ ir->polling ? "poll" : "irq",
+ (gpio & ir->mask_keydown) ? " down" : "",
+ (gpio & ir->mask_keyup) ? " up" : "");
+
+ if (ir->core->boardnr == CX88_BOARD_NORWOOD_MICRO) {
+ u32 gpio_key = cx_read(MO_GP0_IO);
+
+ data = (data << 4) | ((gpio_key & 0xf0) >> 4);
+
+ rc_keydown(ir->dev, data, 0);
+
+ } else if (ir->mask_keydown) {
+ /* bit set on keydown */
+ if (gpio & ir->mask_keydown)
+ rc_keydown_notimeout(ir->dev, data, 0);
+ else
+ rc_keyup(ir->dev);
+
+ } else if (ir->mask_keyup) {
+ /* bit cleared on keydown */
+ if (0 == (gpio & ir->mask_keyup))
+ rc_keydown_notimeout(ir->dev, data, 0);
+ else
+ rc_keyup(ir->dev);
+
+ } else {
+ /* can't distinguish keydown/up :-/ */
+ rc_keydown_notimeout(ir->dev, data, 0);
+ rc_keyup(ir->dev);
+ }
+}
+
+static enum hrtimer_restart cx88_ir_work(struct hrtimer *timer)
+{
+ unsigned long missed;
+ struct cx88_IR *ir = container_of(timer, struct cx88_IR, timer);
+
+ cx88_ir_handle_key(ir);
+ missed = hrtimer_forward_now(&ir->timer,
+ ktime_set(0, ir->polling * 1000000));
+ if (missed > 1)
+ ir_dprintk("Missed ticks %ld\n", missed - 1);
+
+ return HRTIMER_RESTART;
+}
+
+static int __cx88_ir_start(void *priv)
+{
+ struct cx88_core *core = priv;
+ struct cx88_IR *ir;
+
+ if (!core || !core->ir)
+ return -EINVAL;
+
+ ir = core->ir;
+
+ if (ir->polling) {
+ hrtimer_init(&ir->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ir->timer.function = cx88_ir_work;
+ hrtimer_start(&ir->timer,
+ ktime_set(0, ir->polling * 1000000),
+ HRTIMER_MODE_REL);
+ }
+ if (ir->sampling) {
+ core->pci_irqmask |= PCI_INT_IR_SMPINT;
+ cx_write(MO_DDS_IO, 0x33F286 * ir_samplerate); /* samplerate */
+ cx_write(MO_DDSCFG_IO, 0x5); /* enable */
+ }
+ return 0;
+}
+
+static void __cx88_ir_stop(void *priv)
+{
+ struct cx88_core *core = priv;
+ struct cx88_IR *ir;
+
+ if (!core || !core->ir)
+ return;
+
+ ir = core->ir;
+ if (ir->sampling) {
+ cx_write(MO_DDSCFG_IO, 0x0);
+ core->pci_irqmask &= ~PCI_INT_IR_SMPINT;
+ }
+
+ if (ir->polling)
+ hrtimer_cancel(&ir->timer);
+}
+
+int cx88_ir_start(struct cx88_core *core)
+{
+ if (core->ir->users)
+ return __cx88_ir_start(core);
+
+ return 0;
+}
+
+void cx88_ir_stop(struct cx88_core *core)
+{
+ if (core->ir->users)
+ __cx88_ir_stop(core);
+}
+
+static int cx88_ir_open(struct rc_dev *rc)
+{
+ struct cx88_core *core = rc->priv;
+
+ core->ir->users++;
+ return __cx88_ir_start(core);
+}
+
+static void cx88_ir_close(struct rc_dev *rc)
+{
+ struct cx88_core *core = rc->priv;
+
+ core->ir->users--;
+ if (!core->ir->users)
+ __cx88_ir_stop(core);
+}
+
+/* ---------------------------------------------------------------------- */
+
+int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
+{
+ struct cx88_IR *ir;
+ struct rc_dev *dev;
+ char *ir_codes = NULL;
+ u64 rc_type = RC_TYPE_OTHER;
+ int err = -ENOMEM;
+ u32 hardware_mask = 0; /* For devices with a hardware mask, when
+ * used with a full-code IR table
+ */
+
+ ir = kzalloc(sizeof(*ir), GFP_KERNEL);
+ dev = rc_allocate_device();
+ if (!ir || !dev)
+ goto err_out_free;
+
+ ir->dev = dev;
+
+ /* detect & configure */
+ switch (core->boardnr) {
+ case CX88_BOARD_DNTV_LIVE_DVB_T:
+ case CX88_BOARD_KWORLD_DVB_T:
+ case CX88_BOARD_KWORLD_DVB_T_CX22702:
+ ir_codes = RC_MAP_DNTV_LIVE_DVB_T;
+ ir->gpio_addr = MO_GP1_IO;
+ ir->mask_keycode = 0x1f;
+ ir->mask_keyup = 0x60;
+ ir->polling = 50; /* ms */
+ break;
+ case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1:
+ ir_codes = RC_MAP_CINERGY_1400;
+ ir->sampling = 0xeb04; /* address */
+ break;
+ case CX88_BOARD_HAUPPAUGE:
+ case CX88_BOARD_HAUPPAUGE_DVB_T1:
+ case CX88_BOARD_HAUPPAUGE_NOVASE2_S1:
+ case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
+ case CX88_BOARD_HAUPPAUGE_HVR1100:
+ case CX88_BOARD_HAUPPAUGE_HVR3000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
+ case CX88_BOARD_PCHDTV_HD3000:
+ case CX88_BOARD_PCHDTV_HD5500:
+ case CX88_BOARD_HAUPPAUGE_IRONLY:
+ ir_codes = RC_MAP_HAUPPAUGE;
+ ir->sampling = 1;
+ break;
+ case CX88_BOARD_WINFAST_DTV2000H:
+ case CX88_BOARD_WINFAST_DTV2000H_J:
+ case CX88_BOARD_WINFAST_DTV1800H:
+ case CX88_BOARD_WINFAST_DTV1800H_XC4000:
+ case CX88_BOARD_WINFAST_DTV2000H_PLUS:
+ ir_codes = RC_MAP_WINFAST;
+ ir->gpio_addr = MO_GP0_IO;
+ ir->mask_keycode = 0x8f8;
+ ir->mask_keyup = 0x100;
+ ir->polling = 50; /* ms */
+ break;
+ case CX88_BOARD_WINFAST2000XP_EXPERT:
+ case CX88_BOARD_WINFAST_DTV1000:
+ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL:
+ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36:
+ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43:
+ ir_codes = RC_MAP_WINFAST;
+ ir->gpio_addr = MO_GP0_IO;
+ ir->mask_keycode = 0x8f8;
+ ir->mask_keyup = 0x100;
+ ir->polling = 1; /* ms */
+ break;
+ case CX88_BOARD_IODATA_GVBCTV7E:
+ ir_codes = RC_MAP_IODATA_BCTV7E;
+ ir->gpio_addr = MO_GP0_IO;
+ ir->mask_keycode = 0xfd;
+ ir->mask_keydown = 0x02;
+ ir->polling = 5; /* ms */
+ break;
+ case CX88_BOARD_PROLINK_PLAYTVPVR:
+ case CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO:
+ /*
+ * It seems that this hardware is paired with NEC extended
+ * address 0x866b. So, unfortunately, its usage with other
+ * IR's with different address won't work. Still, there are
+ * other IR's from the same manufacturer that works, like the
+ * 002-T mini RC, provided with newer PV hardware
+ */
+ ir_codes = RC_MAP_PIXELVIEW_MK12;
+ ir->gpio_addr = MO_GP1_IO;
+ ir->mask_keyup = 0x80;
+ ir->polling = 10; /* ms */
+ hardware_mask = 0x3f; /* Hardware returns only 6 bits from command part */
+ break;
+ case CX88_BOARD_PROLINK_PV_8000GT:
+ case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME:
+ ir_codes = RC_MAP_PIXELVIEW_NEW;
+ ir->gpio_addr = MO_GP1_IO;
+ ir->mask_keycode = 0x3f;
+ ir->mask_keyup = 0x80;
+ ir->polling = 1; /* ms */
+ break;
+ case CX88_BOARD_KWORLD_LTV883:
+ ir_codes = RC_MAP_PIXELVIEW;
+ ir->gpio_addr = MO_GP1_IO;
+ ir->mask_keycode = 0x1f;
+ ir->mask_keyup = 0x60;
+ ir->polling = 1; /* ms */
+ break;
+ case CX88_BOARD_ADSTECH_DVB_T_PCI:
+ ir_codes = RC_MAP_ADSTECH_DVB_T_PCI;
+ ir->gpio_addr = MO_GP1_IO;
+ ir->mask_keycode = 0xbf;
+ ir->mask_keyup = 0x40;
+ ir->polling = 50; /* ms */
+ break;
+ case CX88_BOARD_MSI_TVANYWHERE_MASTER:
+ ir_codes = RC_MAP_MSI_TVANYWHERE;
+ ir->gpio_addr = MO_GP1_IO;
+ ir->mask_keycode = 0x1f;
+ ir->mask_keyup = 0x40;
+ ir->polling = 1; /* ms */
+ break;
+ case CX88_BOARD_AVERTV_303:
+ case CX88_BOARD_AVERTV_STUDIO_303:
+ ir_codes = RC_MAP_AVERTV_303;
+ ir->gpio_addr = MO_GP2_IO;
+ ir->mask_keycode = 0xfb;
+ ir->mask_keydown = 0x02;
+ ir->polling = 50; /* ms */
+ break;
+ case CX88_BOARD_OMICOM_SS4_PCI:
+ case CX88_BOARD_SATTRADE_ST4200:
+ case CX88_BOARD_TBS_8920:
+ case CX88_BOARD_TBS_8910:
+ case CX88_BOARD_PROF_7300:
+ case CX88_BOARD_PROF_7301:
+ case CX88_BOARD_PROF_6200:
+ ir_codes = RC_MAP_TBS_NEC;
+ ir->sampling = 0xff00; /* address */
+ break;
+ case CX88_BOARD_TEVII_S464:
+ case CX88_BOARD_TEVII_S460:
+ case CX88_BOARD_TEVII_S420:
+ ir_codes = RC_MAP_TEVII_NEC;
+ ir->sampling = 0xff00; /* address */
+ break;
+ case CX88_BOARD_DNTV_LIVE_DVB_T_PRO:
+ ir_codes = RC_MAP_DNTV_LIVE_DVBT_PRO;
+ ir->sampling = 0xff00; /* address */
+ break;
+ case CX88_BOARD_NORWOOD_MICRO:
+ ir_codes = RC_MAP_NORWOOD;
+ ir->gpio_addr = MO_GP1_IO;
+ ir->mask_keycode = 0x0e;
+ ir->mask_keyup = 0x80;
+ ir->polling = 50; /* ms */
+ break;
+ case CX88_BOARD_NPGTECH_REALTV_TOP10FM:
+ ir_codes = RC_MAP_NPGTECH;
+ ir->gpio_addr = MO_GP0_IO;
+ ir->mask_keycode = 0xfa;
+ ir->polling = 50; /* ms */
+ break;
+ case CX88_BOARD_PINNACLE_PCTV_HD_800i:
+ ir_codes = RC_MAP_PINNACLE_PCTV_HD;
+ ir->sampling = 1;
+ break;
+ case CX88_BOARD_POWERCOLOR_REAL_ANGEL:
+ ir_codes = RC_MAP_POWERCOLOR_REAL_ANGEL;
+ ir->gpio_addr = MO_GP2_IO;
+ ir->mask_keycode = 0x7e;
+ ir->polling = 100; /* ms */
+ break;
+ case CX88_BOARD_TWINHAN_VP1027_DVBS:
+ ir_codes = RC_MAP_TWINHAN_VP1027_DVBS;
+ rc_type = RC_TYPE_NEC;
+ ir->sampling = 0xff00; /* address */
+ break;
+ }
+
+ if (!ir_codes) {
+ err = -ENODEV;
+ goto err_out_free;
+ }
+
+ /*
+ * The usage of mask_keycode were very convenient, due to several
+ * reasons. Among others, the scancode tables were using the scancode
+ * as the index elements. So, the less bits it was used, the smaller
+ * the table were stored. After the input changes, the better is to use
+ * the full scancodes, since it allows replacing the IR remote by
+ * another one. Unfortunately, there are still some hardware, like
+ * Pixelview Ultra Pro, where only part of the scancode is sent via
+ * GPIO. So, there's no way to get the full scancode. Due to that,
+ * hardware_mask were introduced here: it represents those hardware
+ * that has such limits.
+ */
+ if (hardware_mask && !ir->mask_keycode)
+ ir->mask_keycode = hardware_mask;
+
+ /* init input device */
+ snprintf(ir->name, sizeof(ir->name), "cx88 IR (%s)", core->board.name);
+ snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(pci));
+
+ dev->input_name = ir->name;
+ dev->input_phys = ir->phys;
+ dev->input_id.bustype = BUS_PCI;
+ dev->input_id.version = 1;
+ if (pci->subsystem_vendor) {
+ dev->input_id.vendor = pci->subsystem_vendor;
+ dev->input_id.product = pci->subsystem_device;
+ } else {
+ dev->input_id.vendor = pci->vendor;
+ dev->input_id.product = pci->device;
+ }
+ dev->dev.parent = &pci->dev;
+ dev->map_name = ir_codes;
+ dev->driver_name = MODULE_NAME;
+ dev->priv = core;
+ dev->open = cx88_ir_open;
+ dev->close = cx88_ir_close;
+ dev->scanmask = hardware_mask;
+
+ if (ir->sampling) {
+ dev->driver_type = RC_DRIVER_IR_RAW;
+ dev->timeout = 10 * 1000 * 1000; /* 10 ms */
+ } else {
+ dev->driver_type = RC_DRIVER_SCANCODE;
+ dev->allowed_protos = rc_type;
+ }
+
+ ir->core = core;
+ core->ir = ir;
+
+ /* all done */
+ err = rc_register_device(dev);
+ if (err)
+ goto err_out_free;
+
+ return 0;
+
+err_out_free:
+ rc_free_device(dev);
+ core->ir = NULL;
+ kfree(ir);
+ return err;
+}
+
+int cx88_ir_fini(struct cx88_core *core)
+{
+ struct cx88_IR *ir = core->ir;
+
+ /* skip detach on non attached boards */
+ if (NULL == ir)
+ return 0;
+
+ cx88_ir_stop(core);
+ rc_unregister_device(ir->dev);
+ kfree(ir);
+
+ /* done */
+ core->ir = NULL;
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void cx88_ir_irq(struct cx88_core *core)
+{
+ struct cx88_IR *ir = core->ir;
+ u32 samples;
+ unsigned todo, bits;
+ struct ir_raw_event ev;
+
+ if (!ir || !ir->sampling)
+ return;
+
+ /*
+ * Samples are stored in a 32 bit register, oldest sample in
+ * the msb. A set bit represents space and an unset bit
+ * represents a pulse.
+ */
+ samples = cx_read(MO_SAMPLE_IO);
+
+ if (samples == 0xff && ir->dev->idle)
+ return;
+
+ init_ir_raw_event(&ev);
+ for (todo = 32; todo > 0; todo -= bits) {
+ ev.pulse = samples & 0x80000000 ? false : true;
+ bits = min(todo, 32U - fls(ev.pulse ? samples : ~samples));
+ ev.duration = (bits * (NSEC_PER_SEC / 1000)) / ir_samplerate;
+ ir_raw_event_store_with_filter(ir->dev, &ev);
+ samples <<= bits;
+ }
+ ir_raw_event_handle(ir->dev);
+}
+
+static int get_key_pvr2000(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+ int flags, code;
+
+ /* poll IR chip */
+ flags = i2c_smbus_read_byte_data(ir->c, 0x10);
+ if (flags < 0) {
+ dprintk("read error\n");
+ return 0;
+ }
+ /* key pressed ? */
+ if (0 == (flags & 0x80))
+ return 0;
+
+ /* read actual key code */
+ code = i2c_smbus_read_byte_data(ir->c, 0x00);
+ if (code < 0) {
+ dprintk("read error\n");
+ return 0;
+ }
+
+ dprintk("IR Key/Flags: (0x%02x/0x%02x)\n",
+ code & 0xff, flags & 0xff);
+
+ *ir_key = code & 0xff;
+ *ir_raw = code;
+ return 1;
+}
+
+void cx88_i2c_init_ir(struct cx88_core *core)
+{
+ struct i2c_board_info info;
+ const unsigned short default_addr_list[] = {
+ 0x18, 0x6b, 0x71,
+ I2C_CLIENT_END
+ };
+ const unsigned short pvr2000_addr_list[] = {
+ 0x18, 0x1a,
+ I2C_CLIENT_END
+ };
+ const unsigned short *addr_list = default_addr_list;
+ const unsigned short *addrp;
+ /* Instantiate the IR receiver device, if present */
+ if (0 != core->i2c_rc)
+ return;
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
+
+ switch (core->boardnr) {
+ case CX88_BOARD_LEADTEK_PVR2000:
+ addr_list = pvr2000_addr_list;
+ core->init_data.name = "cx88 Leadtek PVR 2000 remote";
+ core->init_data.type = RC_TYPE_UNKNOWN;
+ core->init_data.get_key = get_key_pvr2000;
+ core->init_data.ir_codes = RC_MAP_EMPTY;
+ break;
+ }
+
+ /*
+ * We can't call i2c_new_probed_device() because it uses
+ * quick writes for probing and at least some RC receiver
+ * devices only reply to reads.
+ * Also, Hauppauge XVR needs to be specified, as address 0x71
+ * conflicts with another remote type used with saa7134
+ */
+ for (addrp = addr_list; *addrp != I2C_CLIENT_END; addrp++) {
+ info.platform_data = NULL;
+ memset(&core->init_data, 0, sizeof(core->init_data));
+
+ if (*addrp == 0x71) {
+ /* Hauppauge XVR */
+ core->init_data.name = "cx88 Hauppauge XVR remote";
+ core->init_data.ir_codes = RC_MAP_HAUPPAUGE;
+ core->init_data.type = RC_TYPE_RC5;
+ core->init_data.internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR;
+
+ info.platform_data = &core->init_data;
+ }
+ if (i2c_smbus_xfer(&core->i2c_adap, *addrp, 0,
+ I2C_SMBUS_READ, 0,
+ I2C_SMBUS_QUICK, NULL) >= 0) {
+ info.addr = *addrp;
+ i2c_new_device(&core->i2c_adap, &info);
+ break;
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+MODULE_AUTHOR("Gerd Knorr, Pavel Machek, Chris Pascoe");
+MODULE_DESCRIPTION("input driver for cx88 GPIO-based IR remote controls");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c
new file mode 100644
index 00000000..cd5386ee
--- /dev/null
+++ b/drivers/media/video/cx88/cx88-mpeg.c
@@ -0,0 +1,929 @@
+/*
+ *
+ * Support for the mpeg transport stream transfers
+ * PCI function #2 of the cx2388x.
+ *
+ * (c) 2004 Jelle Foks <jelle@foks.us>
+ * (c) 2004 Chris Pascoe <c.pascoe@itee.uq.edu.au>
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <asm/delay.h>
+
+#include "cx88.h"
+
+/* ------------------------------------------------------------------ */
+
+MODULE_DESCRIPTION("mpeg driver for cx2388x based TV cards");
+MODULE_AUTHOR("Jelle Foks <jelle@foks.us>");
+MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(CX88_VERSION);
+
+static unsigned int debug;
+module_param(debug,int,0644);
+MODULE_PARM_DESC(debug,"enable debug messages [mpeg]");
+
+#define dprintk(level,fmt, arg...) if (debug >= level) \
+ printk(KERN_DEBUG "%s/2-mpeg: " fmt, dev->core->name, ## arg)
+
+#define mpeg_dbg(level,fmt, arg...) if (debug >= level) \
+ printk(KERN_DEBUG "%s/2-mpeg: " fmt, core->name, ## arg)
+
+#if defined(CONFIG_MODULES) && defined(MODULE)
+static void request_module_async(struct work_struct *work)
+{
+ struct cx8802_dev *dev=container_of(work, struct cx8802_dev, request_module_wk);
+
+ if (dev->core->board.mpeg & CX88_MPEG_DVB)
+ request_module("cx88-dvb");
+ if (dev->core->board.mpeg & CX88_MPEG_BLACKBIRD)
+ request_module("cx88-blackbird");
+}
+
+static void request_modules(struct cx8802_dev *dev)
+{
+ INIT_WORK(&dev->request_module_wk, request_module_async);
+ schedule_work(&dev->request_module_wk);
+}
+
+static void flush_request_modules(struct cx8802_dev *dev)
+{
+ flush_work_sync(&dev->request_module_wk);
+}
+#else
+#define request_modules(dev)
+#define flush_request_modules(dev)
+#endif /* CONFIG_MODULES */
+
+
+static LIST_HEAD(cx8802_devlist);
+static DEFINE_MUTEX(cx8802_mutex);
+/* ------------------------------------------------------------------ */
+
+static int cx8802_start_dma(struct cx8802_dev *dev,
+ struct cx88_dmaqueue *q,
+ struct cx88_buffer *buf)
+{
+ struct cx88_core *core = dev->core;
+
+ dprintk(1, "cx8802_start_dma w: %d, h: %d, f: %d\n",
+ buf->vb.width, buf->vb.height, buf->vb.field);
+
+ /* setup fifo + format */
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28],
+ dev->ts_packet_size, buf->risc.dma);
+
+ /* write TS length to chip */
+ cx_write(MO_TS_LNGTH, buf->vb.width);
+
+ /* FIXME: this needs a review.
+ * also: move to cx88-blackbird + cx88-dvb source files? */
+
+ dprintk( 1, "core->active_type_id = 0x%08x\n", core->active_type_id);
+
+ if ( (core->active_type_id == CX88_MPEG_DVB) &&
+ (core->board.mpeg & CX88_MPEG_DVB) ) {
+
+ dprintk( 1, "cx8802_start_dma doing .dvb\n");
+ /* negedge driven & software reset */
+ cx_write(TS_GEN_CNTRL, 0x0040 | dev->ts_gen_cntrl);
+ udelay(100);
+ cx_write(MO_PINMUX_IO, 0x00);
+ cx_write(TS_HW_SOP_CNTRL, 0x47<<16|188<<4|0x01);
+ switch (core->boardnr) {
+ case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q:
+ case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T:
+ case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD:
+ case CX88_BOARD_PCHDTV_HD5500:
+ cx_write(TS_SOP_STAT, 1<<13);
+ break;
+ case CX88_BOARD_SAMSUNG_SMT_7020:
+ cx_write(TS_SOP_STAT, 0x00);
+ break;
+ case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
+ case CX88_BOARD_HAUPPAUGE_NOVASE2_S1:
+ cx_write(MO_PINMUX_IO, 0x88); /* Enable MPEG parallel IO and video signal pins */
+ udelay(100);
+ break;
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
+ /* Enable MPEG parallel IO and video signal pins */
+ cx_write(MO_PINMUX_IO, 0x88);
+ cx_write(TS_SOP_STAT, 0);
+ cx_write(TS_VALERR_CNTRL, 0);
+ break;
+ case CX88_BOARD_PINNACLE_PCTV_HD_800i:
+ /* Enable MPEG parallel IO and video signal pins */
+ cx_write(MO_PINMUX_IO, 0x88);
+ cx_write(TS_HW_SOP_CNTRL, (0x47 << 16) | (188 << 4));
+ dev->ts_gen_cntrl = 5;
+ cx_write(TS_SOP_STAT, 0);
+ cx_write(TS_VALERR_CNTRL, 0);
+ udelay(100);
+ break;
+ default:
+ cx_write(TS_SOP_STAT, 0x00);
+ break;
+ }
+ cx_write(TS_GEN_CNTRL, dev->ts_gen_cntrl);
+ udelay(100);
+ } else if ( (core->active_type_id == CX88_MPEG_BLACKBIRD) &&
+ (core->board.mpeg & CX88_MPEG_BLACKBIRD) ) {
+ dprintk( 1, "cx8802_start_dma doing .blackbird\n");
+ cx_write(MO_PINMUX_IO, 0x88); /* enable MPEG parallel IO */
+
+ cx_write(TS_GEN_CNTRL, 0x46); /* punctured clock TS & posedge driven & software reset */
+ udelay(100);
+
+ cx_write(TS_HW_SOP_CNTRL, 0x408); /* mpeg start byte */
+ cx_write(TS_VALERR_CNTRL, 0x2000);
+
+ cx_write(TS_GEN_CNTRL, 0x06); /* punctured clock TS & posedge driven */
+ udelay(100);
+ } else {
+ printk( "%s() Failed. Unsupported value in .mpeg (0x%08x)\n", __func__,
+ core->board.mpeg );
+ return -EINVAL;
+ }
+
+ /* reset counter */
+ cx_write(MO_TS_GPCNTRL, GP_COUNT_CONTROL_RESET);
+ q->count = 1;
+
+ /* enable irqs */
+ dprintk( 1, "setting the interrupt mask\n" );
+ cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_TSINT);
+ cx_set(MO_TS_INTMSK, 0x1f0011);
+
+ /* start dma */
+ cx_set(MO_DEV_CNTRL2, (1<<5));
+ cx_set(MO_TS_DMACNTRL, 0x11);
+ return 0;
+}
+
+static int cx8802_stop_dma(struct cx8802_dev *dev)
+{
+ struct cx88_core *core = dev->core;
+ dprintk( 1, "cx8802_stop_dma\n" );
+
+ /* stop dma */
+ cx_clear(MO_TS_DMACNTRL, 0x11);
+
+ /* disable irqs */
+ cx_clear(MO_PCI_INTMSK, PCI_INT_TSINT);
+ cx_clear(MO_TS_INTMSK, 0x1f0011);
+
+ /* Reset the controller */
+ cx_write(TS_GEN_CNTRL, 0xcd);
+ return 0;
+}
+
+static int cx8802_restart_queue(struct cx8802_dev *dev,
+ struct cx88_dmaqueue *q)
+{
+ struct cx88_buffer *buf;
+
+ dprintk( 1, "cx8802_restart_queue\n" );
+ if (list_empty(&q->active))
+ {
+ struct cx88_buffer *prev;
+ prev = NULL;
+
+ dprintk(1, "cx8802_restart_queue: queue is empty\n" );
+
+ for (;;) {
+ if (list_empty(&q->queued))
+ return 0;
+ buf = list_entry(q->queued.next, struct cx88_buffer, vb.queue);
+ if (NULL == prev) {
+ list_del(&buf->vb.queue);
+ list_add_tail(&buf->vb.queue,&q->active);
+ cx8802_start_dma(dev, q, buf);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ dprintk(1,"[%p/%d] restart_queue - first active\n",
+ buf,buf->vb.i);
+
+ } else if (prev->vb.width == buf->vb.width &&
+ prev->vb.height == buf->vb.height &&
+ prev->fmt == buf->fmt) {
+ list_del(&buf->vb.queue);
+ list_add_tail(&buf->vb.queue,&q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ dprintk(1,"[%p/%d] restart_queue - move to active\n",
+ buf,buf->vb.i);
+ } else {
+ return 0;
+ }
+ prev = buf;
+ }
+ return 0;
+ }
+
+ buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
+ dprintk(2,"restart_queue [%p/%d]: restart dma\n",
+ buf, buf->vb.i);
+ cx8802_start_dma(dev, q, buf);
+ list_for_each_entry(buf, &q->active, vb.queue)
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+int cx8802_buf_prepare(struct videobuf_queue *q, struct cx8802_dev *dev,
+ struct cx88_buffer *buf, enum v4l2_field field)
+{
+ int size = dev->ts_packet_size * dev->ts_packet_count;
+ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+ int rc;
+
+ dprintk(1, "%s: %p\n", __func__, buf);
+ if (0 != buf->vb.baddr && buf->vb.bsize < size)
+ return -EINVAL;
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ buf->vb.width = dev->ts_packet_size;
+ buf->vb.height = dev->ts_packet_count;
+ buf->vb.size = size;
+ buf->vb.field = field /*V4L2_FIELD_TOP*/;
+
+ if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL)))
+ goto fail;
+ cx88_risc_databuffer(dev->pci, &buf->risc,
+ dma->sglist,
+ buf->vb.width, buf->vb.height, 0);
+ }
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+ fail:
+ cx88_free_buffer(q,buf);
+ return rc;
+}
+
+void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf)
+{
+ struct cx88_buffer *prev;
+ struct cx88_dmaqueue *cx88q = &dev->mpegq;
+
+ dprintk( 1, "cx8802_buf_queue\n" );
+ /* add jump to stopper */
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
+ buf->risc.jmp[1] = cpu_to_le32(cx88q->stopper.dma);
+
+ if (list_empty(&cx88q->active)) {
+ dprintk( 1, "queue is empty - first active\n" );
+ list_add_tail(&buf->vb.queue,&cx88q->active);
+ cx8802_start_dma(dev, cx88q, buf);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = cx88q->count++;
+ mod_timer(&cx88q->timeout, jiffies+BUFFER_TIMEOUT);
+ dprintk(1,"[%p/%d] %s - first active\n",
+ buf, buf->vb.i, __func__);
+
+ } else {
+ dprintk( 1, "queue is not empty - append to active\n" );
+ prev = list_entry(cx88q->active.prev, struct cx88_buffer, vb.queue);
+ list_add_tail(&buf->vb.queue,&cx88q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = cx88q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ dprintk( 1, "[%p/%d] %s - append to active\n",
+ buf, buf->vb.i, __func__);
+ }
+}
+
+/* ----------------------------------------------------------- */
+
+static void do_cancel_buffers(struct cx8802_dev *dev, const char *reason, int restart)
+{
+ struct cx88_dmaqueue *q = &dev->mpegq;
+ struct cx88_buffer *buf;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->slock,flags);
+ while (!list_empty(&q->active)) {
+ buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
+ list_del(&buf->vb.queue);
+ buf->vb.state = VIDEOBUF_ERROR;
+ wake_up(&buf->vb.done);
+ dprintk(1,"[%p/%d] %s - dma=0x%08lx\n",
+ buf, buf->vb.i, reason, (unsigned long)buf->risc.dma);
+ }
+ if (restart)
+ {
+ dprintk(1, "restarting queue\n" );
+ cx8802_restart_queue(dev,q);
+ }
+ spin_unlock_irqrestore(&dev->slock,flags);
+}
+
+void cx8802_cancel_buffers(struct cx8802_dev *dev)
+{
+ struct cx88_dmaqueue *q = &dev->mpegq;
+
+ dprintk( 1, "cx8802_cancel_buffers" );
+ del_timer_sync(&q->timeout);
+ cx8802_stop_dma(dev);
+ do_cancel_buffers(dev,"cancel",0);
+}
+
+static void cx8802_timeout(unsigned long data)
+{
+ struct cx8802_dev *dev = (struct cx8802_dev*)data;
+
+ dprintk(1, "%s\n",__func__);
+
+ if (debug)
+ cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]);
+ cx8802_stop_dma(dev);
+ do_cancel_buffers(dev,"timeout",1);
+}
+
+static const char * cx88_mpeg_irqs[32] = {
+ "ts_risci1", NULL, NULL, NULL,
+ "ts_risci2", NULL, NULL, NULL,
+ "ts_oflow", NULL, NULL, NULL,
+ "ts_sync", NULL, NULL, NULL,
+ "opc_err", "par_err", "rip_err", "pci_abort",
+ "ts_err?",
+};
+
+static void cx8802_mpeg_irq(struct cx8802_dev *dev)
+{
+ struct cx88_core *core = dev->core;
+ u32 status, mask, count;
+
+ dprintk( 1, "cx8802_mpeg_irq\n" );
+ status = cx_read(MO_TS_INTSTAT);
+ mask = cx_read(MO_TS_INTMSK);
+ if (0 == (status & mask))
+ return;
+
+ cx_write(MO_TS_INTSTAT, status);
+
+ if (debug || (status & mask & ~0xff))
+ cx88_print_irqbits(core->name, "irq mpeg ",
+ cx88_mpeg_irqs, ARRAY_SIZE(cx88_mpeg_irqs),
+ status, mask);
+
+ /* risc op code error */
+ if (status & (1 << 16)) {
+ printk(KERN_WARNING "%s: mpeg risc op code error\n",core->name);
+ cx_clear(MO_TS_DMACNTRL, 0x11);
+ cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]);
+ }
+
+ /* risc1 y */
+ if (status & 0x01) {
+ dprintk( 1, "wake up\n" );
+ spin_lock(&dev->slock);
+ count = cx_read(MO_TS_GPCNT);
+ cx88_wakeup(dev->core, &dev->mpegq, count);
+ spin_unlock(&dev->slock);
+ }
+
+ /* risc2 y */
+ if (status & 0x10) {
+ spin_lock(&dev->slock);
+ cx8802_restart_queue(dev,&dev->mpegq);
+ spin_unlock(&dev->slock);
+ }
+
+ /* other general errors */
+ if (status & 0x1f0100) {
+ dprintk( 0, "general errors: 0x%08x\n", status & 0x1f0100 );
+ spin_lock(&dev->slock);
+ cx8802_stop_dma(dev);
+ cx8802_restart_queue(dev,&dev->mpegq);
+ spin_unlock(&dev->slock);
+ }
+}
+
+#define MAX_IRQ_LOOP 10
+
+static irqreturn_t cx8802_irq(int irq, void *dev_id)
+{
+ struct cx8802_dev *dev = dev_id;
+ struct cx88_core *core = dev->core;
+ u32 status;
+ int loop, handled = 0;
+
+ for (loop = 0; loop < MAX_IRQ_LOOP; loop++) {
+ status = cx_read(MO_PCI_INTSTAT) &
+ (core->pci_irqmask | PCI_INT_TSINT);
+ if (0 == status)
+ goto out;
+ dprintk( 1, "cx8802_irq\n" );
+ dprintk( 1, " loop: %d/%d\n", loop, MAX_IRQ_LOOP );
+ dprintk( 1, " status: %d\n", status );
+ handled = 1;
+ cx_write(MO_PCI_INTSTAT, status);
+
+ if (status & core->pci_irqmask)
+ cx88_core_irq(core,status);
+ if (status & PCI_INT_TSINT)
+ cx8802_mpeg_irq(dev);
+ };
+ if (MAX_IRQ_LOOP == loop) {
+ dprintk( 0, "clearing mask\n" );
+ printk(KERN_WARNING "%s/0: irq loop -- clearing mask\n",
+ core->name);
+ cx_write(MO_PCI_INTMSK,0);
+ }
+
+ out:
+ return IRQ_RETVAL(handled);
+}
+
+static int cx8802_init_common(struct cx8802_dev *dev)
+{
+ struct cx88_core *core = dev->core;
+ int err;
+
+ /* pci init */
+ if (pci_enable_device(dev->pci))
+ return -EIO;
+ pci_set_master(dev->pci);
+ if (!pci_dma_supported(dev->pci,DMA_BIT_MASK(32))) {
+ printk("%s/2: Oops: no 32bit PCI DMA ???\n",dev->core->name);
+ return -EIO;
+ }
+
+ dev->pci_rev = dev->pci->revision;
+ pci_read_config_byte(dev->pci, PCI_LATENCY_TIMER, &dev->pci_lat);
+ printk(KERN_INFO "%s/2: found at %s, rev: %d, irq: %d, "
+ "latency: %d, mmio: 0x%llx\n", dev->core->name,
+ pci_name(dev->pci), dev->pci_rev, dev->pci->irq,
+ dev->pci_lat,(unsigned long long)pci_resource_start(dev->pci,0));
+
+ /* initialize driver struct */
+ spin_lock_init(&dev->slock);
+
+ /* init dma queue */
+ INIT_LIST_HEAD(&dev->mpegq.active);
+ INIT_LIST_HEAD(&dev->mpegq.queued);
+ dev->mpegq.timeout.function = cx8802_timeout;
+ dev->mpegq.timeout.data = (unsigned long)dev;
+ init_timer(&dev->mpegq.timeout);
+ cx88_risc_stopper(dev->pci,&dev->mpegq.stopper,
+ MO_TS_DMACNTRL,0x11,0x00);
+
+ /* get irq */
+ err = request_irq(dev->pci->irq, cx8802_irq,
+ IRQF_SHARED | IRQF_DISABLED, dev->core->name, dev);
+ if (err < 0) {
+ printk(KERN_ERR "%s: can't get IRQ %d\n",
+ dev->core->name, dev->pci->irq);
+ return err;
+ }
+ cx_set(MO_PCI_INTMSK, core->pci_irqmask);
+
+ /* everything worked */
+ pci_set_drvdata(dev->pci,dev);
+ return 0;
+}
+
+static void cx8802_fini_common(struct cx8802_dev *dev)
+{
+ dprintk( 2, "cx8802_fini_common\n" );
+ cx8802_stop_dma(dev);
+ pci_disable_device(dev->pci);
+
+ /* unregister stuff */
+ free_irq(dev->pci->irq, dev);
+ pci_set_drvdata(dev->pci, NULL);
+
+ /* free memory */
+ btcx_riscmem_free(dev->pci,&dev->mpegq.stopper);
+}
+
+/* ----------------------------------------------------------- */
+
+static int cx8802_suspend_common(struct pci_dev *pci_dev, pm_message_t state)
+{
+ struct cx8802_dev *dev = pci_get_drvdata(pci_dev);
+ struct cx88_core *core = dev->core;
+
+ /* stop mpeg dma */
+ spin_lock(&dev->slock);
+ if (!list_empty(&dev->mpegq.active)) {
+ dprintk( 2, "suspend\n" );
+ printk("%s: suspend mpeg\n", core->name);
+ cx8802_stop_dma(dev);
+ del_timer(&dev->mpegq.timeout);
+ }
+ spin_unlock(&dev->slock);
+
+ /* FIXME -- shutdown device */
+ cx88_shutdown(dev->core);
+
+ pci_save_state(pci_dev);
+ if (0 != pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state))) {
+ pci_disable_device(pci_dev);
+ dev->state.disabled = 1;
+ }
+ return 0;
+}
+
+static int cx8802_resume_common(struct pci_dev *pci_dev)
+{
+ struct cx8802_dev *dev = pci_get_drvdata(pci_dev);
+ struct cx88_core *core = dev->core;
+ int err;
+
+ if (dev->state.disabled) {
+ err=pci_enable_device(pci_dev);
+ if (err) {
+ printk(KERN_ERR "%s: can't enable device\n",
+ dev->core->name);
+ return err;
+ }
+ dev->state.disabled = 0;
+ }
+ err=pci_set_power_state(pci_dev, PCI_D0);
+ if (err) {
+ printk(KERN_ERR "%s: can't enable device\n",
+ dev->core->name);
+ pci_disable_device(pci_dev);
+ dev->state.disabled = 1;
+
+ return err;
+ }
+ pci_restore_state(pci_dev);
+
+ /* FIXME: re-initialize hardware */
+ cx88_reset(dev->core);
+
+ /* restart video+vbi capture */
+ spin_lock(&dev->slock);
+ if (!list_empty(&dev->mpegq.active)) {
+ printk("%s: resume mpeg\n", core->name);
+ cx8802_restart_queue(dev,&dev->mpegq);
+ }
+ spin_unlock(&dev->slock);
+
+ return 0;
+}
+
+struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype)
+{
+ struct cx8802_driver *d;
+
+ list_for_each_entry(d, &dev->drvlist, drvlist)
+ if (d->type_id == btype)
+ return d;
+
+ return NULL;
+}
+
+/* Driver asked for hardware access. */
+static int cx8802_request_acquire(struct cx8802_driver *drv)
+{
+ struct cx88_core *core = drv->core;
+ unsigned int i;
+
+ /* Fail a request for hardware if the device is busy. */
+ if (core->active_type_id != CX88_BOARD_NONE &&
+ core->active_type_id != drv->type_id)
+ return -EBUSY;
+
+ if (drv->type_id == CX88_MPEG_DVB) {
+ /* When switching to DVB, always set the input to the tuner */
+ core->last_analog_input = core->input;
+ core->input = 0;
+ for (i = 0;
+ i < (sizeof(core->board.input) / sizeof(struct cx88_input));
+ i++) {
+ if (core->board.input[i].type == CX88_VMUX_DVB) {
+ core->input = i;
+ break;
+ }
+ }
+ }
+
+ if (drv->advise_acquire)
+ {
+ core->active_ref++;
+ if (core->active_type_id == CX88_BOARD_NONE) {
+ core->active_type_id = drv->type_id;
+ drv->advise_acquire(drv);
+ }
+
+ mpeg_dbg(1,"%s() Post acquire GPIO=%x\n", __func__, cx_read(MO_GP0_IO));
+ }
+
+ return 0;
+}
+
+/* Driver asked to release hardware. */
+static int cx8802_request_release(struct cx8802_driver *drv)
+{
+ struct cx88_core *core = drv->core;
+
+ if (drv->advise_release && --core->active_ref == 0)
+ {
+ if (drv->type_id == CX88_MPEG_DVB) {
+ /* If the DVB driver is releasing, reset the input
+ state to the last configured analog input */
+ core->input = core->last_analog_input;
+ }
+
+ drv->advise_release(drv);
+ core->active_type_id = CX88_BOARD_NONE;
+ mpeg_dbg(1,"%s() Post release GPIO=%x\n", __func__, cx_read(MO_GP0_IO));
+ }
+
+ return 0;
+}
+
+static int cx8802_check_driver(struct cx8802_driver *drv)
+{
+ if (drv == NULL)
+ return -ENODEV;
+
+ if ((drv->type_id != CX88_MPEG_DVB) &&
+ (drv->type_id != CX88_MPEG_BLACKBIRD))
+ return -EINVAL;
+
+ if ((drv->hw_access != CX8802_DRVCTL_SHARED) &&
+ (drv->hw_access != CX8802_DRVCTL_EXCLUSIVE))
+ return -EINVAL;
+
+ if ((drv->probe == NULL) ||
+ (drv->remove == NULL) ||
+ (drv->advise_acquire == NULL) ||
+ (drv->advise_release == NULL))
+ return -EINVAL;
+
+ return 0;
+}
+
+int cx8802_register_driver(struct cx8802_driver *drv)
+{
+ struct cx8802_dev *dev;
+ struct cx8802_driver *driver;
+ int err, i = 0;
+
+ printk(KERN_INFO
+ "cx88/2: registering cx8802 driver, type: %s access: %s\n",
+ drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird",
+ drv->hw_access == CX8802_DRVCTL_SHARED ? "shared" : "exclusive");
+
+ if ((err = cx8802_check_driver(drv)) != 0) {
+ printk(KERN_ERR "cx88/2: cx8802_driver is invalid\n");
+ return err;
+ }
+
+ mutex_lock(&cx8802_mutex);
+
+ list_for_each_entry(dev, &cx8802_devlist, devlist) {
+ printk(KERN_INFO
+ "%s/2: subsystem: %04x:%04x, board: %s [card=%d]\n",
+ dev->core->name, dev->pci->subsystem_vendor,
+ dev->pci->subsystem_device, dev->core->board.name,
+ dev->core->boardnr);
+
+ /* Bring up a new struct for each driver instance */
+ driver = kzalloc(sizeof(*drv),GFP_KERNEL);
+ if (driver == NULL) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ /* Snapshot of the driver registration data */
+ drv->core = dev->core;
+ drv->suspend = cx8802_suspend_common;
+ drv->resume = cx8802_resume_common;
+ drv->request_acquire = cx8802_request_acquire;
+ drv->request_release = cx8802_request_release;
+ memcpy(driver, drv, sizeof(*driver));
+
+ mutex_lock(&drv->core->lock);
+ err = drv->probe(driver);
+ if (err == 0) {
+ i++;
+ list_add_tail(&driver->drvlist, &dev->drvlist);
+ } else {
+ printk(KERN_ERR
+ "%s/2: cx8802 probe failed, err = %d\n",
+ dev->core->name, err);
+ }
+ mutex_unlock(&drv->core->lock);
+ }
+
+ err = i ? 0 : -ENODEV;
+out:
+ mutex_unlock(&cx8802_mutex);
+ return err;
+}
+
+int cx8802_unregister_driver(struct cx8802_driver *drv)
+{
+ struct cx8802_dev *dev;
+ struct cx8802_driver *d, *dtmp;
+ int err = 0;
+
+ printk(KERN_INFO
+ "cx88/2: unregistering cx8802 driver, type: %s access: %s\n",
+ drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird",
+ drv->hw_access == CX8802_DRVCTL_SHARED ? "shared" : "exclusive");
+
+ mutex_lock(&cx8802_mutex);
+
+ list_for_each_entry(dev, &cx8802_devlist, devlist) {
+ printk(KERN_INFO
+ "%s/2: subsystem: %04x:%04x, board: %s [card=%d]\n",
+ dev->core->name, dev->pci->subsystem_vendor,
+ dev->pci->subsystem_device, dev->core->board.name,
+ dev->core->boardnr);
+
+ mutex_lock(&dev->core->lock);
+
+ list_for_each_entry_safe(d, dtmp, &dev->drvlist, drvlist) {
+ /* only unregister the correct driver type */
+ if (d->type_id != drv->type_id)
+ continue;
+
+ err = d->remove(d);
+ if (err == 0) {
+ list_del(&d->drvlist);
+ kfree(d);
+ } else
+ printk(KERN_ERR "%s/2: cx8802 driver remove "
+ "failed (%d)\n", dev->core->name, err);
+ }
+
+ mutex_unlock(&dev->core->lock);
+ }
+
+ mutex_unlock(&cx8802_mutex);
+
+ return err;
+}
+
+/* ----------------------------------------------------------- */
+static int __devinit cx8802_probe(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_id)
+{
+ struct cx8802_dev *dev;
+ struct cx88_core *core;
+ int err;
+
+ /* general setup */
+ core = cx88_core_get(pci_dev);
+ if (NULL == core)
+ return -EINVAL;
+
+ printk("%s/2: cx2388x 8802 Driver Manager\n", core->name);
+
+ err = -ENODEV;
+ if (!core->board.mpeg)
+ goto fail_core;
+
+ err = -ENOMEM;
+ dev = kzalloc(sizeof(*dev),GFP_KERNEL);
+ if (NULL == dev)
+ goto fail_core;
+ dev->pci = pci_dev;
+ dev->core = core;
+
+ /* Maintain a reference so cx88-video can query the 8802 device. */
+ core->dvbdev = dev;
+
+ err = cx8802_init_common(dev);
+ if (err != 0)
+ goto fail_free;
+
+ INIT_LIST_HEAD(&dev->drvlist);
+ mutex_lock(&cx8802_mutex);
+ list_add_tail(&dev->devlist,&cx8802_devlist);
+ mutex_unlock(&cx8802_mutex);
+
+ /* now autoload cx88-dvb or cx88-blackbird */
+ request_modules(dev);
+ return 0;
+
+ fail_free:
+ kfree(dev);
+ fail_core:
+ core->dvbdev = NULL;
+ cx88_core_put(core,pci_dev);
+ return err;
+}
+
+static void __devexit cx8802_remove(struct pci_dev *pci_dev)
+{
+ struct cx8802_dev *dev;
+
+ dev = pci_get_drvdata(pci_dev);
+
+ dprintk( 1, "%s\n", __func__);
+
+ flush_request_modules(dev);
+
+ mutex_lock(&dev->core->lock);
+
+ if (!list_empty(&dev->drvlist)) {
+ struct cx8802_driver *drv, *tmp;
+ int err;
+
+ printk(KERN_WARNING "%s/2: Trying to remove cx8802 driver "
+ "while cx8802 sub-drivers still loaded?!\n",
+ dev->core->name);
+
+ list_for_each_entry_safe(drv, tmp, &dev->drvlist, drvlist) {
+ err = drv->remove(drv);
+ if (err == 0) {
+ list_del(&drv->drvlist);
+ } else
+ printk(KERN_ERR "%s/2: cx8802 driver remove "
+ "failed (%d)\n", dev->core->name, err);
+ kfree(drv);
+ }
+ }
+
+ mutex_unlock(&dev->core->lock);
+
+ /* Destroy any 8802 reference. */
+ dev->core->dvbdev = NULL;
+
+ /* common */
+ cx8802_fini_common(dev);
+ cx88_core_put(dev->core,dev->pci);
+ kfree(dev);
+}
+
+static const struct pci_device_id cx8802_pci_tbl[] = {
+ {
+ .vendor = 0x14f1,
+ .device = 0x8802,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },{
+ /* --- end of list --- */
+ }
+};
+MODULE_DEVICE_TABLE(pci, cx8802_pci_tbl);
+
+static struct pci_driver cx8802_pci_driver = {
+ .name = "cx88-mpeg driver manager",
+ .id_table = cx8802_pci_tbl,
+ .probe = cx8802_probe,
+ .remove = __devexit_p(cx8802_remove),
+};
+
+static int __init cx8802_init(void)
+{
+ printk(KERN_INFO "cx88/2: cx2388x MPEG-TS Driver Manager version %s loaded\n",
+ CX88_VERSION);
+ return pci_register_driver(&cx8802_pci_driver);
+}
+
+static void __exit cx8802_fini(void)
+{
+ pci_unregister_driver(&cx8802_pci_driver);
+}
+
+module_init(cx8802_init);
+module_exit(cx8802_fini);
+EXPORT_SYMBOL(cx8802_buf_prepare);
+EXPORT_SYMBOL(cx8802_buf_queue);
+EXPORT_SYMBOL(cx8802_cancel_buffers);
+
+EXPORT_SYMBOL(cx8802_register_driver);
+EXPORT_SYMBOL(cx8802_unregister_driver);
+EXPORT_SYMBOL(cx8802_get_driver);
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off
+ */
diff --git a/drivers/media/video/cx88/cx88-reg.h b/drivers/media/video/cx88/cx88-reg.h
new file mode 100644
index 00000000..2ec52d1c
--- /dev/null
+++ b/drivers/media/video/cx88/cx88-reg.h
@@ -0,0 +1,836 @@
+/*
+
+ cx88x-hw.h - CX2388x register offsets
+
+ Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
+ 2001 Michael Eskin
+ 2002 Yurij Sysoev <yurij@naturesoft.net>
+ 2003 Gerd Knorr <kraxel@bytesex.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _CX88_REG_H_
+#define _CX88_REG_H_
+
+/* ---------------------------------------------------------------------- */
+/* PCI IDs and config space */
+
+#ifndef PCI_VENDOR_ID_CONEXANT
+# define PCI_VENDOR_ID_CONEXANT 0x14F1
+#endif
+#ifndef PCI_DEVICE_ID_CX2300_VID
+# define PCI_DEVICE_ID_CX2300_VID 0x8800
+#endif
+
+#define CX88X_DEVCTRL 0x40
+#define CX88X_EN_TBFX 0x02
+#define CX88X_EN_VSFX 0x04
+
+/* ---------------------------------------------------------------------- */
+/* PCI controller registers */
+
+/* Command and Status Register */
+#define F0_CMD_STAT_MM 0x2f0004
+#define F1_CMD_STAT_MM 0x2f0104
+#define F2_CMD_STAT_MM 0x2f0204
+#define F3_CMD_STAT_MM 0x2f0304
+#define F4_CMD_STAT_MM 0x2f0404
+
+/* Device Control #1 */
+#define F0_DEV_CNTRL1_MM 0x2f0040
+#define F1_DEV_CNTRL1_MM 0x2f0140
+#define F2_DEV_CNTRL1_MM 0x2f0240
+#define F3_DEV_CNTRL1_MM 0x2f0340
+#define F4_DEV_CNTRL1_MM 0x2f0440
+
+/* Device Control #1 */
+#define F0_BAR0_MM 0x2f0010
+#define F1_BAR0_MM 0x2f0110
+#define F2_BAR0_MM 0x2f0210
+#define F3_BAR0_MM 0x2f0310
+#define F4_BAR0_MM 0x2f0410
+
+/* ---------------------------------------------------------------------- */
+/* DMA Controller registers */
+
+#define MO_PDMA_STHRSH 0x200000 // Source threshold
+#define MO_PDMA_STADRS 0x200004 // Source target address
+#define MO_PDMA_SIADRS 0x200008 // Source internal address
+#define MO_PDMA_SCNTRL 0x20000C // Source control
+#define MO_PDMA_DTHRSH 0x200010 // Destination threshold
+#define MO_PDMA_DTADRS 0x200014 // Destination target address
+#define MO_PDMA_DIADRS 0x200018 // Destination internal address
+#define MO_PDMA_DCNTRL 0x20001C // Destination control
+#define MO_LD_SSID 0x200030 // Load subsystem ID
+#define MO_DEV_CNTRL2 0x200034 // Device control
+#define MO_PCI_INTMSK 0x200040 // PCI interrupt mask
+#define MO_PCI_INTSTAT 0x200044 // PCI interrupt status
+#define MO_PCI_INTMSTAT 0x200048 // PCI interrupt masked status
+#define MO_VID_INTMSK 0x200050 // Video interrupt mask
+#define MO_VID_INTSTAT 0x200054 // Video interrupt status
+#define MO_VID_INTMSTAT 0x200058 // Video interrupt masked status
+#define MO_VID_INTSSTAT 0x20005C // Video interrupt set status
+#define MO_AUD_INTMSK 0x200060 // Audio interrupt mask
+#define MO_AUD_INTSTAT 0x200064 // Audio interrupt status
+#define MO_AUD_INTMSTAT 0x200068 // Audio interrupt masked status
+#define MO_AUD_INTSSTAT 0x20006C // Audio interrupt set status
+#define MO_TS_INTMSK 0x200070 // Transport stream interrupt mask
+#define MO_TS_INTSTAT 0x200074 // Transport stream interrupt status
+#define MO_TS_INTMSTAT 0x200078 // Transport stream interrupt mask status
+#define MO_TS_INTSSTAT 0x20007C // Transport stream interrupt set status
+#define MO_VIP_INTMSK 0x200080 // VIP interrupt mask
+#define MO_VIP_INTSTAT 0x200084 // VIP interrupt status
+#define MO_VIP_INTMSTAT 0x200088 // VIP interrupt masked status
+#define MO_VIP_INTSSTAT 0x20008C // VIP interrupt set status
+#define MO_GPHST_INTMSK 0x200090 // Host interrupt mask
+#define MO_GPHST_INTSTAT 0x200094 // Host interrupt status
+#define MO_GPHST_INTMSTAT 0x200098 // Host interrupt masked status
+#define MO_GPHST_INTSSTAT 0x20009C // Host interrupt set status
+
+// DMA Channels 1-6 belong to SPIPE
+#define MO_DMA7_PTR1 0x300018 // {24}RW* DMA Current Ptr : Ch#7
+#define MO_DMA8_PTR1 0x30001C // {24}RW* DMA Current Ptr : Ch#8
+
+// DMA Channels 9-20 belong to SPIPE
+#define MO_DMA21_PTR1 0x300080 // {24}R0* DMA Current Ptr : Ch#21
+#define MO_DMA22_PTR1 0x300084 // {24}R0* DMA Current Ptr : Ch#22
+#define MO_DMA23_PTR1 0x300088 // {24}R0* DMA Current Ptr : Ch#23
+#define MO_DMA24_PTR1 0x30008C // {24}R0* DMA Current Ptr : Ch#24
+#define MO_DMA25_PTR1 0x300090 // {24}R0* DMA Current Ptr : Ch#25
+#define MO_DMA26_PTR1 0x300094 // {24}R0* DMA Current Ptr : Ch#26
+#define MO_DMA27_PTR1 0x300098 // {24}R0* DMA Current Ptr : Ch#27
+#define MO_DMA28_PTR1 0x30009C // {24}R0* DMA Current Ptr : Ch#28
+#define MO_DMA29_PTR1 0x3000A0 // {24}R0* DMA Current Ptr : Ch#29
+#define MO_DMA30_PTR1 0x3000A4 // {24}R0* DMA Current Ptr : Ch#30
+#define MO_DMA31_PTR1 0x3000A8 // {24}R0* DMA Current Ptr : Ch#31
+#define MO_DMA32_PTR1 0x3000AC // {24}R0* DMA Current Ptr : Ch#32
+
+#define MO_DMA21_PTR2 0x3000C0 // {24}RW* DMA Tab Ptr : Ch#21
+#define MO_DMA22_PTR2 0x3000C4 // {24}RW* DMA Tab Ptr : Ch#22
+#define MO_DMA23_PTR2 0x3000C8 // {24}RW* DMA Tab Ptr : Ch#23
+#define MO_DMA24_PTR2 0x3000CC // {24}RW* DMA Tab Ptr : Ch#24
+#define MO_DMA25_PTR2 0x3000D0 // {24}RW* DMA Tab Ptr : Ch#25
+#define MO_DMA26_PTR2 0x3000D4 // {24}RW* DMA Tab Ptr : Ch#26
+#define MO_DMA27_PTR2 0x3000D8 // {24}RW* DMA Tab Ptr : Ch#27
+#define MO_DMA28_PTR2 0x3000DC // {24}RW* DMA Tab Ptr : Ch#28
+#define MO_DMA29_PTR2 0x3000E0 // {24}RW* DMA Tab Ptr : Ch#29
+#define MO_DMA30_PTR2 0x3000E4 // {24}RW* DMA Tab Ptr : Ch#30
+#define MO_DMA31_PTR2 0x3000E8 // {24}RW* DMA Tab Ptr : Ch#31
+#define MO_DMA32_PTR2 0x3000EC // {24}RW* DMA Tab Ptr : Ch#32
+
+#define MO_DMA21_CNT1 0x300100 // {11}RW* DMA Buffer Size : Ch#21
+#define MO_DMA22_CNT1 0x300104 // {11}RW* DMA Buffer Size : Ch#22
+#define MO_DMA23_CNT1 0x300108 // {11}RW* DMA Buffer Size : Ch#23
+#define MO_DMA24_CNT1 0x30010C // {11}RW* DMA Buffer Size : Ch#24
+#define MO_DMA25_CNT1 0x300110 // {11}RW* DMA Buffer Size : Ch#25
+#define MO_DMA26_CNT1 0x300114 // {11}RW* DMA Buffer Size : Ch#26
+#define MO_DMA27_CNT1 0x300118 // {11}RW* DMA Buffer Size : Ch#27
+#define MO_DMA28_CNT1 0x30011C // {11}RW* DMA Buffer Size : Ch#28
+#define MO_DMA29_CNT1 0x300120 // {11}RW* DMA Buffer Size : Ch#29
+#define MO_DMA30_CNT1 0x300124 // {11}RW* DMA Buffer Size : Ch#30
+#define MO_DMA31_CNT1 0x300128 // {11}RW* DMA Buffer Size : Ch#31
+#define MO_DMA32_CNT1 0x30012C // {11}RW* DMA Buffer Size : Ch#32
+
+#define MO_DMA21_CNT2 0x300140 // {11}RW* DMA Table Size : Ch#21
+#define MO_DMA22_CNT2 0x300144 // {11}RW* DMA Table Size : Ch#22
+#define MO_DMA23_CNT2 0x300148 // {11}RW* DMA Table Size : Ch#23
+#define MO_DMA24_CNT2 0x30014C // {11}RW* DMA Table Size : Ch#24
+#define MO_DMA25_CNT2 0x300150 // {11}RW* DMA Table Size : Ch#25
+#define MO_DMA26_CNT2 0x300154 // {11}RW* DMA Table Size : Ch#26
+#define MO_DMA27_CNT2 0x300158 // {11}RW* DMA Table Size : Ch#27
+#define MO_DMA28_CNT2 0x30015C // {11}RW* DMA Table Size : Ch#28
+#define MO_DMA29_CNT2 0x300160 // {11}RW* DMA Table Size : Ch#29
+#define MO_DMA30_CNT2 0x300164 // {11}RW* DMA Table Size : Ch#30
+#define MO_DMA31_CNT2 0x300168 // {11}RW* DMA Table Size : Ch#31
+#define MO_DMA32_CNT2 0x30016C // {11}RW* DMA Table Size : Ch#32
+
+
+/* ---------------------------------------------------------------------- */
+/* Video registers */
+
+#define MO_VIDY_DMA 0x310000 // {64}RWp Video Y
+#define MO_VIDU_DMA 0x310008 // {64}RWp Video U
+#define MO_VIDV_DMA 0x310010 // {64}RWp Video V
+#define MO_VBI_DMA 0x310018 // {64}RWp VBI (Vertical blanking interval)
+
+#define MO_DEVICE_STATUS 0x310100
+#define MO_INPUT_FORMAT 0x310104
+#define MO_AGC_BURST 0x31010c
+#define MO_CONTR_BRIGHT 0x310110
+#define MO_UV_SATURATION 0x310114
+#define MO_HUE 0x310118
+#define MO_HTOTAL 0x310120
+#define MO_HDELAY_EVEN 0x310124
+#define MO_HDELAY_ODD 0x310128
+#define MO_VDELAY_ODD 0x31012c
+#define MO_VDELAY_EVEN 0x310130
+#define MO_HACTIVE_EVEN 0x31013c
+#define MO_HACTIVE_ODD 0x310140
+#define MO_VACTIVE_EVEN 0x310144
+#define MO_VACTIVE_ODD 0x310148
+#define MO_HSCALE_EVEN 0x31014c
+#define MO_HSCALE_ODD 0x310150
+#define MO_VSCALE_EVEN 0x310154
+#define MO_FILTER_EVEN 0x31015c
+#define MO_VSCALE_ODD 0x310158
+#define MO_FILTER_ODD 0x310160
+#define MO_OUTPUT_FORMAT 0x310164
+
+#define MO_PLL_REG 0x310168 // PLL register
+#define MO_PLL_ADJ_CTRL 0x31016c // PLL adjust control register
+#define MO_SCONV_REG 0x310170 // sample rate conversion register
+#define MO_SCONV_FIFO 0x310174 // sample rate conversion fifo
+#define MO_SUB_STEP 0x310178 // subcarrier step size
+#define MO_SUB_STEP_DR 0x31017c // subcarrier step size for DR line
+
+#define MO_CAPTURE_CTRL 0x310180 // capture control
+#define MO_COLOR_CTRL 0x310184
+#define MO_VBI_PACKET 0x310188 // vbi packet size / delay
+#define MO_FIELD_COUNT 0x310190 // field counter
+#define MO_VIP_CONFIG 0x310194
+#define MO_VBOS_CONTROL 0x3101a8
+
+#define MO_AGC_BACK_VBI 0x310200
+#define MO_AGC_SYNC_TIP1 0x310208
+
+#define MO_VIDY_GPCNT 0x31C020 // {16}RO Video Y general purpose counter
+#define MO_VIDU_GPCNT 0x31C024 // {16}RO Video U general purpose counter
+#define MO_VIDV_GPCNT 0x31C028 // {16}RO Video V general purpose counter
+#define MO_VBI_GPCNT 0x31C02C // {16}RO VBI general purpose counter
+#define MO_VIDY_GPCNTRL 0x31C030 // {2}WO Video Y general purpose control
+#define MO_VIDU_GPCNTRL 0x31C034 // {2}WO Video U general purpose control
+#define MO_VIDV_GPCNTRL 0x31C038 // {2}WO Video V general purpose control
+#define MO_VBI_GPCNTRL 0x31C03C // {2}WO VBI general purpose counter
+#define MO_VID_DMACNTRL 0x31C040 // {8}RW Video DMA control
+#define MO_VID_XFR_STAT 0x31C044 // {1}RO Video transfer status
+
+
+/* ---------------------------------------------------------------------- */
+/* audio registers */
+
+#define MO_AUDD_DMA 0x320000 // {64}RWp Audio downstream
+#define MO_AUDU_DMA 0x320008 // {64}RWp Audio upstream
+#define MO_AUDR_DMA 0x320010 // {64}RWp Audio RDS (downstream)
+#define MO_AUDD_GPCNT 0x32C020 // {16}RO Audio down general purpose counter
+#define MO_AUDU_GPCNT 0x32C024 // {16}RO Audio up general purpose counter
+#define MO_AUDR_GPCNT 0x32C028 // {16}RO Audio RDS general purpose counter
+#define MO_AUDD_GPCNTRL 0x32C030 // {2}WO Audio down general purpose control
+#define MO_AUDU_GPCNTRL 0x32C034 // {2}WO Audio up general purpose control
+#define MO_AUDR_GPCNTRL 0x32C038 // {2}WO Audio RDS general purpose control
+#define MO_AUD_DMACNTRL 0x32C040 // {6}RW Audio DMA control
+#define MO_AUD_XFR_STAT 0x32C044 // {1}RO Audio transfer status
+#define MO_AUDD_LNGTH 0x32C048 // {12}RW Audio down line length
+#define MO_AUDR_LNGTH 0x32C04C // {12}RW Audio RDS line length
+
+#define AUD_INIT 0x320100
+#define AUD_INIT_LD 0x320104
+#define AUD_SOFT_RESET 0x320108
+#define AUD_I2SINPUTCNTL 0x320120
+#define AUD_BAUDRATE 0x320124
+#define AUD_I2SOUTPUTCNTL 0x320128
+#define AAGC_HYST 0x320134
+#define AAGC_GAIN 0x320138
+#define AAGC_DEF 0x32013c
+#define AUD_IIR1_0_SEL 0x320150
+#define AUD_IIR1_0_SHIFT 0x320154
+#define AUD_IIR1_1_SEL 0x320158
+#define AUD_IIR1_1_SHIFT 0x32015c
+#define AUD_IIR1_2_SEL 0x320160
+#define AUD_IIR1_2_SHIFT 0x320164
+#define AUD_IIR1_3_SEL 0x320168
+#define AUD_IIR1_3_SHIFT 0x32016c
+#define AUD_IIR1_4_SEL 0x320170
+#define AUD_IIR1_4_SHIFT 0x32017c
+#define AUD_IIR1_5_SEL 0x320180
+#define AUD_IIR1_5_SHIFT 0x320184
+#define AUD_IIR2_0_SEL 0x320190
+#define AUD_IIR2_0_SHIFT 0x320194
+#define AUD_IIR2_1_SEL 0x320198
+#define AUD_IIR2_1_SHIFT 0x32019c
+#define AUD_IIR2_2_SEL 0x3201a0
+#define AUD_IIR2_2_SHIFT 0x3201a4
+#define AUD_IIR2_3_SEL 0x3201a8
+#define AUD_IIR2_3_SHIFT 0x3201ac
+#define AUD_IIR3_0_SEL 0x3201c0
+#define AUD_IIR3_0_SHIFT 0x3201c4
+#define AUD_IIR3_1_SEL 0x3201c8
+#define AUD_IIR3_1_SHIFT 0x3201cc
+#define AUD_IIR3_2_SEL 0x3201d0
+#define AUD_IIR3_2_SHIFT 0x3201d4
+#define AUD_IIR4_0_SEL 0x3201e0
+#define AUD_IIR4_0_SHIFT 0x3201e4
+#define AUD_IIR4_1_SEL 0x3201e8
+#define AUD_IIR4_1_SHIFT 0x3201ec
+#define AUD_IIR4_2_SEL 0x3201f0
+#define AUD_IIR4_2_SHIFT 0x3201f4
+#define AUD_IIR4_0_CA0 0x320200
+#define AUD_IIR4_0_CA1 0x320204
+#define AUD_IIR4_0_CA2 0x320208
+#define AUD_IIR4_0_CB0 0x32020c
+#define AUD_IIR4_0_CB1 0x320210
+#define AUD_IIR4_1_CA0 0x320214
+#define AUD_IIR4_1_CA1 0x320218
+#define AUD_IIR4_1_CA2 0x32021c
+#define AUD_IIR4_1_CB0 0x320220
+#define AUD_IIR4_1_CB1 0x320224
+#define AUD_IIR4_2_CA0 0x320228
+#define AUD_IIR4_2_CA1 0x32022c
+#define AUD_IIR4_2_CA2 0x320230
+#define AUD_IIR4_2_CB0 0x320234
+#define AUD_IIR4_2_CB1 0x320238
+#define AUD_HP_MD_IIR4_1 0x320250
+#define AUD_HP_PROG_IIR4_1 0x320254
+#define AUD_FM_MODE_ENABLE 0x320258
+#define AUD_POLY0_DDS_CONSTANT 0x320270
+#define AUD_DN0_FREQ 0x320274
+#define AUD_DN1_FREQ 0x320278
+#define AUD_DN1_FREQ_SHIFT 0x32027c
+#define AUD_DN1_AFC 0x320280
+#define AUD_DN1_SRC_SEL 0x320284
+#define AUD_DN1_SHFT 0x320288
+#define AUD_DN2_FREQ 0x32028c
+#define AUD_DN2_FREQ_SHIFT 0x320290
+#define AUD_DN2_AFC 0x320294
+#define AUD_DN2_SRC_SEL 0x320298
+#define AUD_DN2_SHFT 0x32029c
+#define AUD_CRDC0_SRC_SEL 0x320300
+#define AUD_CRDC0_SHIFT 0x320304
+#define AUD_CORDIC_SHIFT_0 0x320308
+#define AUD_CRDC1_SRC_SEL 0x32030c
+#define AUD_CRDC1_SHIFT 0x320310
+#define AUD_CORDIC_SHIFT_1 0x320314
+#define AUD_DCOC_0_SRC 0x320320
+#define AUD_DCOC0_SHIFT 0x320324
+#define AUD_DCOC_0_SHIFT_IN0 0x320328
+#define AUD_DCOC_0_SHIFT_IN1 0x32032c
+#define AUD_DCOC_1_SRC 0x320330
+#define AUD_DCOC1_SHIFT 0x320334
+#define AUD_DCOC_1_SHIFT_IN0 0x320338
+#define AUD_DCOC_1_SHIFT_IN1 0x32033c
+#define AUD_DCOC_2_SRC 0x320340
+#define AUD_DCOC2_SHIFT 0x320344
+#define AUD_DCOC_2_SHIFT_IN0 0x320348
+#define AUD_DCOC_2_SHIFT_IN1 0x32034c
+#define AUD_DCOC_PASS_IN 0x320350
+#define AUD_PDET_SRC 0x320370
+#define AUD_PDET_SHIFT 0x320374
+#define AUD_PILOT_BQD_1_K0 0x320380
+#define AUD_PILOT_BQD_1_K1 0x320384
+#define AUD_PILOT_BQD_1_K2 0x320388
+#define AUD_PILOT_BQD_1_K3 0x32038c
+#define AUD_PILOT_BQD_1_K4 0x320390
+#define AUD_PILOT_BQD_2_K0 0x320394
+#define AUD_PILOT_BQD_2_K1 0x320398
+#define AUD_PILOT_BQD_2_K2 0x32039c
+#define AUD_PILOT_BQD_2_K3 0x3203a0
+#define AUD_PILOT_BQD_2_K4 0x3203a4
+#define AUD_THR_FR 0x3203c0
+#define AUD_X_PROG 0x3203c4
+#define AUD_Y_PROG 0x3203c8
+#define AUD_HARMONIC_MULT 0x3203cc
+#define AUD_C1_UP_THR 0x3203d0
+#define AUD_C1_LO_THR 0x3203d4
+#define AUD_C2_UP_THR 0x3203d8
+#define AUD_C2_LO_THR 0x3203dc
+#define AUD_PLL_EN 0x320400
+#define AUD_PLL_SRC 0x320404
+#define AUD_PLL_SHIFT 0x320408
+#define AUD_PLL_IF_SEL 0x32040c
+#define AUD_PLL_IF_SHIFT 0x320410
+#define AUD_BIQUAD_PLL_K0 0x320414
+#define AUD_BIQUAD_PLL_K1 0x320418
+#define AUD_BIQUAD_PLL_K2 0x32041c
+#define AUD_BIQUAD_PLL_K3 0x320420
+#define AUD_BIQUAD_PLL_K4 0x320424
+#define AUD_DEEMPH0_SRC_SEL 0x320440
+#define AUD_DEEMPH0_SHIFT 0x320444
+#define AUD_DEEMPH0_G0 0x320448
+#define AUD_DEEMPH0_A0 0x32044c
+#define AUD_DEEMPH0_B0 0x320450
+#define AUD_DEEMPH0_A1 0x320454
+#define AUD_DEEMPH0_B1 0x320458
+#define AUD_DEEMPH1_SRC_SEL 0x32045c
+#define AUD_DEEMPH1_SHIFT 0x320460
+#define AUD_DEEMPH1_G0 0x320464
+#define AUD_DEEMPH1_A0 0x320468
+#define AUD_DEEMPH1_B0 0x32046c
+#define AUD_DEEMPH1_A1 0x320470
+#define AUD_DEEMPH1_B1 0x320474
+#define AUD_OUT0_SEL 0x320490
+#define AUD_OUT0_SHIFT 0x320494
+#define AUD_OUT1_SEL 0x320498
+#define AUD_OUT1_SHIFT 0x32049c
+#define AUD_RDSI_SEL 0x3204a0
+#define AUD_RDSI_SHIFT 0x3204a4
+#define AUD_RDSQ_SEL 0x3204a8
+#define AUD_RDSQ_SHIFT 0x3204ac
+#define AUD_DBX_IN_GAIN 0x320500
+#define AUD_DBX_WBE_GAIN 0x320504
+#define AUD_DBX_SE_GAIN 0x320508
+#define AUD_DBX_RMS_WBE 0x32050c
+#define AUD_DBX_RMS_SE 0x320510
+#define AUD_DBX_SE_BYPASS 0x320514
+#define AUD_FAWDETCTL 0x320530
+#define AUD_FAWDETWINCTL 0x320534
+#define AUD_DEEMPHGAIN_R 0x320538
+#define AUD_DEEMPHNUMER1_R 0x32053c
+#define AUD_DEEMPHNUMER2_R 0x320540
+#define AUD_DEEMPHDENOM1_R 0x320544
+#define AUD_DEEMPHDENOM2_R 0x320548
+#define AUD_ERRLOGPERIOD_R 0x32054c
+#define AUD_ERRINTRPTTHSHLD1_R 0x320550
+#define AUD_ERRINTRPTTHSHLD2_R 0x320554
+#define AUD_ERRINTRPTTHSHLD3_R 0x320558
+#define AUD_NICAM_STATUS1 0x32055c
+#define AUD_NICAM_STATUS2 0x320560
+#define AUD_ERRLOG1 0x320564
+#define AUD_ERRLOG2 0x320568
+#define AUD_ERRLOG3 0x32056c
+#define AUD_DAC_BYPASS_L 0x320580
+#define AUD_DAC_BYPASS_R 0x320584
+#define AUD_DAC_BYPASS_CTL 0x320588
+#define AUD_CTL 0x32058c
+#define AUD_STATUS 0x320590
+#define AUD_VOL_CTL 0x320594
+#define AUD_BAL_CTL 0x320598
+#define AUD_START_TIMER 0x3205b0
+#define AUD_MODE_CHG_TIMER 0x3205b4
+#define AUD_POLYPH80SCALEFAC 0x3205b8
+#define AUD_DMD_RA_DDS 0x3205bc
+#define AUD_I2S_RA_DDS 0x3205c0
+#define AUD_RATE_THRES_DMD 0x3205d0
+#define AUD_RATE_THRES_I2S 0x3205d4
+#define AUD_RATE_ADJ1 0x3205d8
+#define AUD_RATE_ADJ2 0x3205dc
+#define AUD_RATE_ADJ3 0x3205e0
+#define AUD_RATE_ADJ4 0x3205e4
+#define AUD_RATE_ADJ5 0x3205e8
+#define AUD_APB_IN_RATE_ADJ 0x3205ec
+#define AUD_I2SCNTL 0x3205ec
+#define AUD_PHASE_FIX_CTL 0x3205f0
+#define AUD_PLL_PRESCALE 0x320600
+#define AUD_PLL_DDS 0x320604
+#define AUD_PLL_INT 0x320608
+#define AUD_PLL_FRAC 0x32060c
+#define AUD_PLL_JTAG 0x320620
+#define AUD_PLL_SPMP 0x320624
+#define AUD_AFE_12DB_EN 0x320628
+
+// Audio QAM Register Addresses
+#define AUD_PDF_DDS_CNST_BYTE2 0x320d01
+#define AUD_PDF_DDS_CNST_BYTE1 0x320d02
+#define AUD_PDF_DDS_CNST_BYTE0 0x320d03
+#define AUD_PHACC_FREQ_8MSB 0x320d2a
+#define AUD_PHACC_FREQ_8LSB 0x320d2b
+#define AUD_QAM_MODE 0x320d04
+
+
+/* ---------------------------------------------------------------------- */
+/* transport stream registers */
+
+#define MO_TS_DMA 0x330000 // {64}RWp Transport stream downstream
+#define MO_TS_GPCNT 0x33C020 // {16}RO TS general purpose counter
+#define MO_TS_GPCNTRL 0x33C030 // {2}WO TS general purpose control
+#define MO_TS_DMACNTRL 0x33C040 // {6}RW TS DMA control
+#define MO_TS_XFR_STAT 0x33C044 // {1}RO TS transfer status
+#define MO_TS_LNGTH 0x33C048 // {12}RW TS line length
+
+#define TS_HW_SOP_CNTRL 0x33C04C
+#define TS_GEN_CNTRL 0x33C050
+#define TS_BD_PKT_STAT 0x33C054
+#define TS_SOP_STAT 0x33C058
+#define TS_FIFO_OVFL_STAT 0x33C05C
+#define TS_VALERR_CNTRL 0x33C060
+
+
+/* ---------------------------------------------------------------------- */
+/* VIP registers */
+
+#define MO_VIPD_DMA 0x340000 // {64}RWp VIP downstream
+#define MO_VIPU_DMA 0x340008 // {64}RWp VIP upstream
+#define MO_VIPD_GPCNT 0x34C020 // {16}RO VIP down general purpose counter
+#define MO_VIPU_GPCNT 0x34C024 // {16}RO VIP up general purpose counter
+#define MO_VIPD_GPCNTRL 0x34C030 // {2}WO VIP down general purpose control
+#define MO_VIPU_GPCNTRL 0x34C034 // {2}WO VIP up general purpose control
+#define MO_VIP_DMACNTRL 0x34C040 // {6}RW VIP DMA control
+#define MO_VIP_XFR_STAT 0x34C044 // {1}RO VIP transfer status
+#define MO_VIP_CFG 0x340048 // VIP configuration
+#define MO_VIPU_CNTRL 0x34004C // VIP upstream control #1
+#define MO_VIPD_CNTRL 0x340050 // VIP downstream control #2
+#define MO_VIPD_LNGTH 0x340054 // VIP downstream line length
+#define MO_VIP_BRSTLN 0x340058 // VIP burst length
+#define MO_VIP_INTCNTRL 0x34C05C // VIP Interrupt Control
+#define MO_VIP_XFTERM 0x340060 // VIP transfer terminate
+
+
+/* ---------------------------------------------------------------------- */
+/* misc registers */
+
+#define MO_M2M_DMA 0x350000 // {64}RWp Mem2Mem DMA Bfr
+#define MO_GP0_IO 0x350010 // {32}RW* GPIOoutput enablesdata I/O
+#define MO_GP1_IO 0x350014 // {32}RW* GPIOoutput enablesdata I/O
+#define MO_GP2_IO 0x350018 // {32}RW* GPIOoutput enablesdata I/O
+#define MO_GP3_IO 0x35001C // {32}RW* GPIO Mode/Ctrloutput enables
+#define MO_GPIO 0x350020 // {32}RW* GPIO I2C Ctrldata I/O
+#define MO_GPOE 0x350024 // {32}RW GPIO I2C Ctrloutput enables
+#define MO_GP_ISM 0x350028 // {16}WO GPIO Intr Sens/Pol
+
+#define MO_PLL_B 0x35C008 // {32}RW* PLL Control for ASB bus clks
+#define MO_M2M_CNT 0x35C024 // {32}RW Mem2Mem DMA Cnt
+#define MO_M2M_XSUM 0x35C028 // {32}RO M2M XOR-Checksum
+#define MO_CRC 0x35C02C // {16}RW CRC16 init/result
+#define MO_CRC_D 0x35C030 // {32}WO CRC16 new data in
+#define MO_TM_CNT_LDW 0x35C034 // {32}RO Timer : Counter low dword
+#define MO_TM_CNT_UW 0x35C038 // {16}RO Timer : Counter high word
+#define MO_TM_LMT_LDW 0x35C03C // {32}RW Timer : Limit low dword
+#define MO_TM_LMT_UW 0x35C040 // {32}RW Timer : Limit high word
+#define MO_PINMUX_IO 0x35C044 // {8}RW Pin Mux Control
+#define MO_TSTSEL_IO 0x35C048 // {2}RW Pin Mux Control
+#define MO_AFECFG_IO 0x35C04C // AFE configuration reg
+#define MO_DDS_IO 0x35C050 // DDS Increment reg
+#define MO_DDSCFG_IO 0x35C054 // DDS Configuration reg
+#define MO_SAMPLE_IO 0x35C058 // IRIn sample reg
+#define MO_SRST_IO 0x35C05C // Output system reset reg
+
+#define MO_INT1_MSK 0x35C060 // DMA RISC interrupt mask
+#define MO_INT1_STAT 0x35C064 // DMA RISC interrupt status
+#define MO_INT1_MSTAT 0x35C068 // DMA RISC interrupt masked status
+
+
+/* ---------------------------------------------------------------------- */
+/* i2c bus registers */
+
+#define MO_I2C 0x368000 // I2C data/control
+#define MO_I2C_DIV (0xf<<4)
+#define MO_I2C_SYNC (1<<3)
+#define MO_I2C_W3B (1<<2)
+#define MO_I2C_SCL (1<<1)
+#define MO_I2C_SDA (1<<0)
+
+
+/* ---------------------------------------------------------------------- */
+/* general purpose host registers */
+/* FIXME: tyops? s/0x35/0x38/ ?? */
+
+#define MO_GPHSTD_DMA 0x350000 // {64}RWp Host downstream
+#define MO_GPHSTU_DMA 0x350008 // {64}RWp Host upstream
+#define MO_GPHSTU_CNTRL 0x380048 // Host upstream control #1
+#define MO_GPHSTD_CNTRL 0x38004C // Host downstream control #2
+#define MO_GPHSTD_LNGTH 0x380050 // Host downstream line length
+#define MO_GPHST_WSC 0x380054 // Host wait state control
+#define MO_GPHST_XFR 0x380058 // Host transfer control
+#define MO_GPHST_WDTH 0x38005C // Host interface width
+#define MO_GPHST_HDSHK 0x380060 // Host peripheral handshake
+#define MO_GPHST_MUX16 0x380064 // Host muxed 16-bit transfer parameters
+#define MO_GPHST_MODE 0x380068 // Host mode select
+
+#define MO_GPHSTD_GPCNT 0x35C020 // Host down general purpose counter
+#define MO_GPHSTU_GPCNT 0x35C024 // Host up general purpose counter
+#define MO_GPHSTD_GPCNTRL 0x38C030 // Host down general purpose control
+#define MO_GPHSTU_GPCNTRL 0x38C034 // Host up general purpose control
+#define MO_GPHST_DMACNTRL 0x38C040 // Host DMA control
+#define MO_GPHST_XFR_STAT 0x38C044 // Host transfer status
+#define MO_GPHST_SOFT_RST 0x38C06C // Host software reset
+
+
+/* ---------------------------------------------------------------------- */
+/* RISC instructions */
+
+#define RISC_SYNC 0x80000000
+#define RISC_SYNC_ODD 0x80000000
+#define RISC_SYNC_EVEN 0x80000200
+#define RISC_RESYNC 0x80008000
+#define RISC_RESYNC_ODD 0x80008000
+#define RISC_RESYNC_EVEN 0x80008200
+#define RISC_WRITE 0x10000000
+#define RISC_WRITEC 0x50000000
+#define RISC_READ 0x90000000
+#define RISC_READC 0xA0000000
+#define RISC_JUMP 0x70000000
+#define RISC_SKIP 0x20000000
+#define RISC_WRITERM 0xB0000000
+#define RISC_WRITECM 0xC0000000
+#define RISC_WRITECR 0xD0000000
+#define RISC_IMM 0x00000001
+
+#define RISC_SOL 0x08000000
+#define RISC_EOL 0x04000000
+
+#define RISC_IRQ2 0x02000000
+#define RISC_IRQ1 0x01000000
+
+#define RISC_CNT_NONE 0x00000000
+#define RISC_CNT_INC 0x00010000
+#define RISC_CNT_RSVR 0x00020000
+#define RISC_CNT_RESET 0x00030000
+#define RISC_JMP_SRP 0x01
+
+
+/* ---------------------------------------------------------------------- */
+/* various constants */
+
+// DMA
+/* Interrupt mask/status */
+#define PCI_INT_VIDINT (1 << 0)
+#define PCI_INT_AUDINT (1 << 1)
+#define PCI_INT_TSINT (1 << 2)
+#define PCI_INT_VIPINT (1 << 3)
+#define PCI_INT_HSTINT (1 << 4)
+#define PCI_INT_TM1INT (1 << 5)
+#define PCI_INT_SRCDMAINT (1 << 6)
+#define PCI_INT_DSTDMAINT (1 << 7)
+#define PCI_INT_RISC_RD_BERRINT (1 << 10)
+#define PCI_INT_RISC_WR_BERRINT (1 << 11)
+#define PCI_INT_BRDG_BERRINT (1 << 12)
+#define PCI_INT_SRC_DMA_BERRINT (1 << 13)
+#define PCI_INT_DST_DMA_BERRINT (1 << 14)
+#define PCI_INT_IPB_DMA_BERRINT (1 << 15)
+#define PCI_INT_I2CDONE (1 << 16)
+#define PCI_INT_I2CRACK (1 << 17)
+#define PCI_INT_IR_SMPINT (1 << 18)
+#define PCI_INT_GPIO_INT0 (1 << 19)
+#define PCI_INT_GPIO_INT1 (1 << 20)
+
+#define SEL_BTSC 0x01
+#define SEL_EIAJ 0x02
+#define SEL_A2 0x04
+#define SEL_SAP 0x08
+#define SEL_NICAM 0x10
+#define SEL_FMRADIO 0x20
+
+// AUD_CTL
+#define AUD_INT_DN_RISCI1 (1 << 0)
+#define AUD_INT_UP_RISCI1 (1 << 1)
+#define AUD_INT_RDS_DN_RISCI1 (1 << 2)
+#define AUD_INT_DN_RISCI2 (1 << 4) /* yes, 3 is skipped */
+#define AUD_INT_UP_RISCI2 (1 << 5)
+#define AUD_INT_RDS_DN_RISCI2 (1 << 6)
+#define AUD_INT_DN_SYNC (1 << 12)
+#define AUD_INT_UP_SYNC (1 << 13)
+#define AUD_INT_RDS_DN_SYNC (1 << 14)
+#define AUD_INT_OPC_ERR (1 << 16)
+#define AUD_INT_BER_IRQ (1 << 20)
+#define AUD_INT_MCHG_IRQ (1 << 21)
+
+#define EN_BTSC_FORCE_MONO 0
+#define EN_BTSC_FORCE_STEREO 1
+#define EN_BTSC_FORCE_SAP 2
+#define EN_BTSC_AUTO_STEREO 3
+#define EN_BTSC_AUTO_SAP 4
+
+#define EN_A2_FORCE_MONO1 8
+#define EN_A2_FORCE_MONO2 9
+#define EN_A2_FORCE_STEREO 10
+#define EN_A2_AUTO_MONO2 11
+#define EN_A2_AUTO_STEREO 12
+
+#define EN_EIAJ_FORCE_MONO1 16
+#define EN_EIAJ_FORCE_MONO2 17
+#define EN_EIAJ_FORCE_STEREO 18
+#define EN_EIAJ_AUTO_MONO2 19
+#define EN_EIAJ_AUTO_STEREO 20
+
+#define EN_NICAM_FORCE_MONO1 32
+#define EN_NICAM_FORCE_MONO2 33
+#define EN_NICAM_FORCE_STEREO 34
+#define EN_NICAM_AUTO_MONO2 35
+#define EN_NICAM_AUTO_STEREO 36
+
+#define EN_FMRADIO_FORCE_MONO 24
+#define EN_FMRADIO_FORCE_STEREO 25
+#define EN_FMRADIO_AUTO_STEREO 26
+
+#define EN_NICAM_AUTO_FALLBACK 0x00000040
+#define EN_FMRADIO_EN_RDS 0x00000200
+#define EN_NICAM_TRY_AGAIN_BIT 0x00000400
+#define EN_DAC_ENABLE 0x00001000
+#define EN_I2SOUT_ENABLE 0x00002000
+#define EN_I2SIN_STR2DAC 0x00004000
+#define EN_I2SIN_ENABLE 0x00008000
+
+#define EN_DMTRX_SUMDIFF (0 << 7)
+#define EN_DMTRX_SUMR (1 << 7)
+#define EN_DMTRX_LR (2 << 7)
+#define EN_DMTRX_MONO (3 << 7)
+#define EN_DMTRX_BYPASS (1 << 11)
+
+// Video
+#define VID_CAPTURE_CONTROL 0x310180
+
+#define CX23880_CAP_CTL_CAPTURE_VBI_ODD (1<<3)
+#define CX23880_CAP_CTL_CAPTURE_VBI_EVEN (1<<2)
+#define CX23880_CAP_CTL_CAPTURE_ODD (1<<1)
+#define CX23880_CAP_CTL_CAPTURE_EVEN (1<<0)
+
+#define VideoInputMux0 0x0
+#define VideoInputMux1 0x1
+#define VideoInputMux2 0x2
+#define VideoInputMux3 0x3
+#define VideoInputTuner 0x0
+#define VideoInputComposite 0x1
+#define VideoInputSVideo 0x2
+#define VideoInputOther 0x3
+
+#define Xtal0 0x1
+#define Xtal1 0x2
+#define XtalAuto 0x3
+
+#define VideoFormatAuto 0x0
+#define VideoFormatNTSC 0x1
+#define VideoFormatNTSCJapan 0x2
+#define VideoFormatNTSC443 0x3
+#define VideoFormatPAL 0x4
+#define VideoFormatPALB 0x4
+#define VideoFormatPALD 0x4
+#define VideoFormatPALG 0x4
+#define VideoFormatPALH 0x4
+#define VideoFormatPALI 0x4
+#define VideoFormatPALBDGHI 0x4
+#define VideoFormatPALM 0x5
+#define VideoFormatPALN 0x6
+#define VideoFormatPALNC 0x7
+#define VideoFormatPAL60 0x8
+#define VideoFormatSECAM 0x9
+
+#define VideoFormatAuto27MHz 0x10
+#define VideoFormatNTSC27MHz 0x11
+#define VideoFormatNTSCJapan27MHz 0x12
+#define VideoFormatNTSC44327MHz 0x13
+#define VideoFormatPAL27MHz 0x14
+#define VideoFormatPALB27MHz 0x14
+#define VideoFormatPALD27MHz 0x14
+#define VideoFormatPALG27MHz 0x14
+#define VideoFormatPALH27MHz 0x14
+#define VideoFormatPALI27MHz 0x14
+#define VideoFormatPALBDGHI27MHz 0x14
+#define VideoFormatPALM27MHz 0x15
+#define VideoFormatPALN27MHz 0x16
+#define VideoFormatPALNC27MHz 0x17
+#define VideoFormatPAL6027MHz 0x18
+#define VideoFormatSECAM27MHz 0x19
+
+#define NominalUSECAM 0x87
+#define NominalVSECAM 0x85
+#define NominalUNTSC 0xFE
+#define NominalVNTSC 0xB4
+
+#define NominalContrast 0xD8
+
+#define HFilterAutoFormat 0x0
+#define HFilterCIF 0x1
+#define HFilterQCIF 0x2
+#define HFilterICON 0x3
+
+#define VFilter2TapInterpolate 0
+#define VFilter3TapInterpolate 1
+#define VFilter4TapInterpolate 2
+#define VFilter5TapInterpolate 3
+#define VFilter2TapNoInterpolate 4
+#define VFilter3TapNoInterpolate 5
+#define VFilter4TapNoInterpolate 6
+#define VFilter5TapNoInterpolate 7
+
+#define ColorFormatRGB32 0x0000
+#define ColorFormatRGB24 0x0011
+#define ColorFormatRGB16 0x0022
+#define ColorFormatRGB15 0x0033
+#define ColorFormatYUY2 0x0044
+#define ColorFormatBTYUV 0x0055
+#define ColorFormatY8 0x0066
+#define ColorFormatRGB8 0x0077
+#define ColorFormatPL422 0x0088
+#define ColorFormatPL411 0x0099
+#define ColorFormatYUV12 0x00AA
+#define ColorFormatYUV9 0x00BB
+#define ColorFormatRAW 0x00EE
+#define ColorFormatBSWAP 0x0300
+#define ColorFormatWSWAP 0x0c00
+#define ColorFormatEvenMask 0x050f
+#define ColorFormatOddMask 0x0af0
+#define ColorFormatGamma 0x1000
+
+#define Interlaced 0x1
+#define NonInterlaced 0x0
+
+#define FieldEven 0x1
+#define FieldOdd 0x0
+
+#define TGReadWriteMode 0x0
+#define TGEnableMode 0x1
+
+#define DV_CbAlign 0x0
+#define DV_Y0Align 0x1
+#define DV_CrAlign 0x2
+#define DV_Y1Align 0x3
+
+#define DVF_Analog 0x0
+#define DVF_CCIR656 0x1
+#define DVF_ByteStream 0x2
+#define DVF_ExtVSYNC 0x4
+#define DVF_ExtField 0x5
+
+#define CHANNEL_VID_Y 0x1
+#define CHANNEL_VID_U 0x2
+#define CHANNEL_VID_V 0x3
+#define CHANNEL_VID_VBI 0x4
+#define CHANNEL_AUD_DN 0x5
+#define CHANNEL_AUD_UP 0x6
+#define CHANNEL_AUD_RDS_DN 0x7
+#define CHANNEL_MPEG_DN 0x8
+#define CHANNEL_VIP_DN 0x9
+#define CHANNEL_VIP_UP 0xA
+#define CHANNEL_HOST_DN 0xB
+#define CHANNEL_HOST_UP 0xC
+#define CHANNEL_FIRST 0x1
+#define CHANNEL_LAST 0xC
+
+#define GP_COUNT_CONTROL_NONE 0x0
+#define GP_COUNT_CONTROL_INC 0x1
+#define GP_COUNT_CONTROL_RESERVED 0x2
+#define GP_COUNT_CONTROL_RESET 0x3
+
+#define PLL_PRESCALE_BY_2 2
+#define PLL_PRESCALE_BY_3 3
+#define PLL_PRESCALE_BY_4 4
+#define PLL_PRESCALE_BY_5 5
+
+#define HLNotchFilter4xFsc 0
+#define HLNotchFilterSquare 1
+#define HLNotchFilter135NTSC 2
+#define HLNotchFilter135PAL 3
+
+#define NTSC_8x_SUB_CARRIER 28.63636E6
+#define PAL_8x_SUB_CARRIER 35.46895E6
+
+// Default analog settings
+#define DEFAULT_HUE_NTSC 0x00
+#define DEFAULT_BRIGHTNESS_NTSC 0x00
+#define DEFAULT_CONTRAST_NTSC 0x39
+#define DEFAULT_SAT_U_NTSC 0x7F
+#define DEFAULT_SAT_V_NTSC 0x5A
+
+typedef enum
+{
+ SOURCE_TUNER = 0,
+ SOURCE_COMPOSITE,
+ SOURCE_SVIDEO,
+ SOURCE_OTHER1,
+ SOURCE_OTHER2,
+ SOURCE_COMPVIASVIDEO,
+ SOURCE_CCIR656
+} VIDEOSOURCETYPE;
+
+#endif /* _CX88_REG_H_ */
diff --git a/drivers/media/video/cx88/cx88-tvaudio.c b/drivers/media/video/cx88/cx88-tvaudio.c
new file mode 100644
index 00000000..770ec05b
--- /dev/null
+++ b/drivers/media/video/cx88/cx88-tvaudio.c
@@ -0,0 +1,1059 @@
+/*
+
+ cx88x-audio.c - Conexant CX23880/23881 audio downstream driver driver
+
+ (c) 2001 Michael Eskin, Tom Zakrajsek [Windows version]
+ (c) 2002 Yurij Sysoev <yurij@naturesoft.net>
+ (c) 2003 Gerd Knorr <kraxel@bytesex.org>
+
+ -----------------------------------------------------------------------
+
+ Lot of voodoo here. Even the data sheet doesn't help to
+ understand what is going on here, the documentation for the audio
+ part of the cx2388x chip is *very* bad.
+
+ Some of this comes from party done linux driver sources I got from
+ [undocumented].
+
+ Some comes from the dscaler sources, one of the dscaler driver guy works
+ for Conexant ...
+
+ -----------------------------------------------------------------------
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/freezer.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/poll.h>
+#include <linux/signal.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+
+#include "cx88.h"
+
+static unsigned int audio_debug;
+module_param(audio_debug, int, 0644);
+MODULE_PARM_DESC(audio_debug, "enable debug messages [audio]");
+
+static unsigned int always_analog;
+module_param(always_analog,int,0644);
+MODULE_PARM_DESC(always_analog,"force analog audio out");
+
+static unsigned int radio_deemphasis;
+module_param(radio_deemphasis,int,0644);
+MODULE_PARM_DESC(radio_deemphasis, "Radio deemphasis time constant, "
+ "0=None, 1=50us (elsewhere), 2=75us (USA)");
+
+#define dprintk(fmt, arg...) if (audio_debug) \
+ printk(KERN_DEBUG "%s/0: " fmt, core->name , ## arg)
+
+/* ----------------------------------------------------------- */
+
+static const char * const aud_ctl_names[64] = {
+ [EN_BTSC_FORCE_MONO] = "BTSC_FORCE_MONO",
+ [EN_BTSC_FORCE_STEREO] = "BTSC_FORCE_STEREO",
+ [EN_BTSC_FORCE_SAP] = "BTSC_FORCE_SAP",
+ [EN_BTSC_AUTO_STEREO] = "BTSC_AUTO_STEREO",
+ [EN_BTSC_AUTO_SAP] = "BTSC_AUTO_SAP",
+ [EN_A2_FORCE_MONO1] = "A2_FORCE_MONO1",
+ [EN_A2_FORCE_MONO2] = "A2_FORCE_MONO2",
+ [EN_A2_FORCE_STEREO] = "A2_FORCE_STEREO",
+ [EN_A2_AUTO_MONO2] = "A2_AUTO_MONO2",
+ [EN_A2_AUTO_STEREO] = "A2_AUTO_STEREO",
+ [EN_EIAJ_FORCE_MONO1] = "EIAJ_FORCE_MONO1",
+ [EN_EIAJ_FORCE_MONO2] = "EIAJ_FORCE_MONO2",
+ [EN_EIAJ_FORCE_STEREO] = "EIAJ_FORCE_STEREO",
+ [EN_EIAJ_AUTO_MONO2] = "EIAJ_AUTO_MONO2",
+ [EN_EIAJ_AUTO_STEREO] = "EIAJ_AUTO_STEREO",
+ [EN_NICAM_FORCE_MONO1] = "NICAM_FORCE_MONO1",
+ [EN_NICAM_FORCE_MONO2] = "NICAM_FORCE_MONO2",
+ [EN_NICAM_FORCE_STEREO] = "NICAM_FORCE_STEREO",
+ [EN_NICAM_AUTO_MONO2] = "NICAM_AUTO_MONO2",
+ [EN_NICAM_AUTO_STEREO] = "NICAM_AUTO_STEREO",
+ [EN_FMRADIO_FORCE_MONO] = "FMRADIO_FORCE_MONO",
+ [EN_FMRADIO_FORCE_STEREO] = "FMRADIO_FORCE_STEREO",
+ [EN_FMRADIO_AUTO_STEREO] = "FMRADIO_AUTO_STEREO",
+};
+
+struct rlist {
+ u32 reg;
+ u32 val;
+};
+
+static void set_audio_registers(struct cx88_core *core, const struct rlist *l)
+{
+ int i;
+
+ for (i = 0; l[i].reg; i++) {
+ switch (l[i].reg) {
+ case AUD_PDF_DDS_CNST_BYTE2:
+ case AUD_PDF_DDS_CNST_BYTE1:
+ case AUD_PDF_DDS_CNST_BYTE0:
+ case AUD_QAM_MODE:
+ case AUD_PHACC_FREQ_8MSB:
+ case AUD_PHACC_FREQ_8LSB:
+ cx_writeb(l[i].reg, l[i].val);
+ break;
+ default:
+ cx_write(l[i].reg, l[i].val);
+ break;
+ }
+ }
+}
+
+static void set_audio_start(struct cx88_core *core, u32 mode)
+{
+ /* mute */
+ cx_write(AUD_VOL_CTL, (1 << 6));
+
+ /* start programming */
+ cx_write(AUD_INIT, mode);
+ cx_write(AUD_INIT_LD, 0x0001);
+ cx_write(AUD_SOFT_RESET, 0x0001);
+}
+
+static void set_audio_finish(struct cx88_core *core, u32 ctl)
+{
+ u32 volume;
+
+ /* restart dma; This avoids buzz in NICAM and is good in others */
+ cx88_stop_audio_dma(core);
+ cx_write(AUD_RATE_THRES_DMD, 0x000000C0);
+ cx88_start_audio_dma(core);
+
+ if (core->board.mpeg & CX88_MPEG_BLACKBIRD) {
+ cx_write(AUD_I2SINPUTCNTL, 4);
+ cx_write(AUD_BAUDRATE, 1);
+ /* 'pass-thru mode': this enables the i2s output to the mpeg encoder */
+ cx_set(AUD_CTL, EN_I2SOUT_ENABLE);
+ cx_write(AUD_I2SOUTPUTCNTL, 1);
+ cx_write(AUD_I2SCNTL, 0);
+ /* cx_write(AUD_APB_IN_RATE_ADJ, 0); */
+ }
+ if ((always_analog) || (!(core->board.mpeg & CX88_MPEG_BLACKBIRD))) {
+ ctl |= EN_DAC_ENABLE;
+ cx_write(AUD_CTL, ctl);
+ }
+
+ /* finish programming */
+ cx_write(AUD_SOFT_RESET, 0x0000);
+
+ /* unmute */
+ volume = cx_sread(SHADOW_AUD_VOL_CTL);
+ cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, volume);
+
+ core->last_change = jiffies;
+}
+
+/* ----------------------------------------------------------- */
+
+static void set_audio_standard_BTSC(struct cx88_core *core, unsigned int sap,
+ u32 mode)
+{
+ static const struct rlist btsc[] = {
+ {AUD_AFE_12DB_EN, 0x00000001},
+ {AUD_OUT1_SEL, 0x00000013},
+ {AUD_OUT1_SHIFT, 0x00000000},
+ {AUD_POLY0_DDS_CONSTANT, 0x0012010c},
+ {AUD_DMD_RA_DDS, 0x00c3e7aa},
+ {AUD_DBX_IN_GAIN, 0x00004734},
+ {AUD_DBX_WBE_GAIN, 0x00004640},
+ {AUD_DBX_SE_GAIN, 0x00008d31},
+ {AUD_DCOC_0_SRC, 0x0000001a},
+ {AUD_IIR1_4_SEL, 0x00000021},
+ {AUD_DCOC_PASS_IN, 0x00000003},
+ {AUD_DCOC_0_SHIFT_IN0, 0x0000000a},
+ {AUD_DCOC_0_SHIFT_IN1, 0x00000008},
+ {AUD_DCOC_1_SHIFT_IN0, 0x0000000a},
+ {AUD_DCOC_1_SHIFT_IN1, 0x00000008},
+ {AUD_DN0_FREQ, 0x0000283b},
+ {AUD_DN2_SRC_SEL, 0x00000008},
+ {AUD_DN2_FREQ, 0x00003000},
+ {AUD_DN2_AFC, 0x00000002},
+ {AUD_DN2_SHFT, 0x00000000},
+ {AUD_IIR2_2_SEL, 0x00000020},
+ {AUD_IIR2_2_SHIFT, 0x00000000},
+ {AUD_IIR2_3_SEL, 0x0000001f},
+ {AUD_IIR2_3_SHIFT, 0x00000000},
+ {AUD_CRDC1_SRC_SEL, 0x000003ce},
+ {AUD_CRDC1_SHIFT, 0x00000000},
+ {AUD_CORDIC_SHIFT_1, 0x00000007},
+ {AUD_DCOC_1_SRC, 0x0000001b},
+ {AUD_DCOC1_SHIFT, 0x00000000},
+ {AUD_RDSI_SEL, 0x00000008},
+ {AUD_RDSQ_SEL, 0x00000008},
+ {AUD_RDSI_SHIFT, 0x00000000},
+ {AUD_RDSQ_SHIFT, 0x00000000},
+ {AUD_POLYPH80SCALEFAC, 0x00000003},
+ { /* end of list */ },
+ };
+ static const struct rlist btsc_sap[] = {
+ {AUD_AFE_12DB_EN, 0x00000001},
+ {AUD_DBX_IN_GAIN, 0x00007200},
+ {AUD_DBX_WBE_GAIN, 0x00006200},
+ {AUD_DBX_SE_GAIN, 0x00006200},
+ {AUD_IIR1_1_SEL, 0x00000000},
+ {AUD_IIR1_3_SEL, 0x00000001},
+ {AUD_DN1_SRC_SEL, 0x00000007},
+ {AUD_IIR1_4_SHIFT, 0x00000006},
+ {AUD_IIR2_1_SHIFT, 0x00000000},
+ {AUD_IIR2_2_SHIFT, 0x00000000},
+ {AUD_IIR3_0_SHIFT, 0x00000000},
+ {AUD_IIR3_1_SHIFT, 0x00000000},
+ {AUD_IIR3_0_SEL, 0x0000000d},
+ {AUD_IIR3_1_SEL, 0x0000000e},
+ {AUD_DEEMPH1_SRC_SEL, 0x00000014},
+ {AUD_DEEMPH1_SHIFT, 0x00000000},
+ {AUD_DEEMPH1_G0, 0x00004000},
+ {AUD_DEEMPH1_A0, 0x00000000},
+ {AUD_DEEMPH1_B0, 0x00000000},
+ {AUD_DEEMPH1_A1, 0x00000000},
+ {AUD_DEEMPH1_B1, 0x00000000},
+ {AUD_OUT0_SEL, 0x0000003f},
+ {AUD_OUT1_SEL, 0x0000003f},
+ {AUD_DN1_AFC, 0x00000002},
+ {AUD_DCOC_0_SHIFT_IN0, 0x0000000a},
+ {AUD_DCOC_0_SHIFT_IN1, 0x00000008},
+ {AUD_DCOC_1_SHIFT_IN0, 0x0000000a},
+ {AUD_DCOC_1_SHIFT_IN1, 0x00000008},
+ {AUD_IIR1_0_SEL, 0x0000001d},
+ {AUD_IIR1_2_SEL, 0x0000001e},
+ {AUD_IIR2_1_SEL, 0x00000002},
+ {AUD_IIR2_2_SEL, 0x00000004},
+ {AUD_IIR3_2_SEL, 0x0000000f},
+ {AUD_DCOC2_SHIFT, 0x00000001},
+ {AUD_IIR3_2_SHIFT, 0x00000001},
+ {AUD_DEEMPH0_SRC_SEL, 0x00000014},
+ {AUD_CORDIC_SHIFT_1, 0x00000006},
+ {AUD_POLY0_DDS_CONSTANT, 0x000e4db2},
+ {AUD_DMD_RA_DDS, 0x00f696e6},
+ {AUD_IIR2_3_SEL, 0x00000025},
+ {AUD_IIR1_4_SEL, 0x00000021},
+ {AUD_DN1_FREQ, 0x0000c965},
+ {AUD_DCOC_PASS_IN, 0x00000003},
+ {AUD_DCOC_0_SRC, 0x0000001a},
+ {AUD_DCOC_1_SRC, 0x0000001b},
+ {AUD_DCOC1_SHIFT, 0x00000000},
+ {AUD_RDSI_SEL, 0x00000009},
+ {AUD_RDSQ_SEL, 0x00000009},
+ {AUD_RDSI_SHIFT, 0x00000000},
+ {AUD_RDSQ_SHIFT, 0x00000000},
+ {AUD_POLYPH80SCALEFAC, 0x00000003},
+ { /* end of list */ },
+ };
+
+ mode |= EN_FMRADIO_EN_RDS;
+
+ if (sap) {
+ dprintk("%s SAP (status: unknown)\n", __func__);
+ set_audio_start(core, SEL_SAP);
+ set_audio_registers(core, btsc_sap);
+ set_audio_finish(core, mode);
+ } else {
+ dprintk("%s (status: known-good)\n", __func__);
+ set_audio_start(core, SEL_BTSC);
+ set_audio_registers(core, btsc);
+ set_audio_finish(core, mode);
+ }
+}
+
+static void set_audio_standard_NICAM(struct cx88_core *core, u32 mode)
+{
+ static const struct rlist nicam_l[] = {
+ {AUD_AFE_12DB_EN, 0x00000001},
+ {AUD_RATE_ADJ1, 0x00000060},
+ {AUD_RATE_ADJ2, 0x000000F9},
+ {AUD_RATE_ADJ3, 0x000001CC},
+ {AUD_RATE_ADJ4, 0x000002B3},
+ {AUD_RATE_ADJ5, 0x00000726},
+ {AUD_DEEMPHDENOM1_R, 0x0000F3D0},
+ {AUD_DEEMPHDENOM2_R, 0x00000000},
+ {AUD_ERRLOGPERIOD_R, 0x00000064},
+ {AUD_ERRINTRPTTHSHLD1_R, 0x00000FFF},
+ {AUD_ERRINTRPTTHSHLD2_R, 0x0000001F},
+ {AUD_ERRINTRPTTHSHLD3_R, 0x0000000F},
+ {AUD_POLYPH80SCALEFAC, 0x00000003},
+ {AUD_DMD_RA_DDS, 0x00C00000},
+ {AUD_PLL_INT, 0x0000001E},
+ {AUD_PLL_DDS, 0x00000000},
+ {AUD_PLL_FRAC, 0x0000E542},
+ {AUD_START_TIMER, 0x00000000},
+ {AUD_DEEMPHNUMER1_R, 0x000353DE},
+ {AUD_DEEMPHNUMER2_R, 0x000001B1},
+ {AUD_PDF_DDS_CNST_BYTE2, 0x06},
+ {AUD_PDF_DDS_CNST_BYTE1, 0x82},
+ {AUD_PDF_DDS_CNST_BYTE0, 0x12},
+ {AUD_QAM_MODE, 0x05},
+ {AUD_PHACC_FREQ_8MSB, 0x34},
+ {AUD_PHACC_FREQ_8LSB, 0x4C},
+ {AUD_DEEMPHGAIN_R, 0x00006680},
+ {AUD_RATE_THRES_DMD, 0x000000C0},
+ { /* end of list */ },
+ };
+
+ static const struct rlist nicam_bgdki_common[] = {
+ {AUD_AFE_12DB_EN, 0x00000001},
+ {AUD_RATE_ADJ1, 0x00000010},
+ {AUD_RATE_ADJ2, 0x00000040},
+ {AUD_RATE_ADJ3, 0x00000100},
+ {AUD_RATE_ADJ4, 0x00000400},
+ {AUD_RATE_ADJ5, 0x00001000},
+ {AUD_ERRLOGPERIOD_R, 0x00000fff},
+ {AUD_ERRINTRPTTHSHLD1_R, 0x000003ff},
+ {AUD_ERRINTRPTTHSHLD2_R, 0x000000ff},
+ {AUD_ERRINTRPTTHSHLD3_R, 0x0000003f},
+ {AUD_POLYPH80SCALEFAC, 0x00000003},
+ {AUD_DEEMPHGAIN_R, 0x000023c2},
+ {AUD_DEEMPHNUMER1_R, 0x0002a7bc},
+ {AUD_DEEMPHNUMER2_R, 0x0003023e},
+ {AUD_DEEMPHDENOM1_R, 0x0000f3d0},
+ {AUD_DEEMPHDENOM2_R, 0x00000000},
+ {AUD_PDF_DDS_CNST_BYTE2, 0x06},
+ {AUD_PDF_DDS_CNST_BYTE1, 0x82},
+ {AUD_QAM_MODE, 0x05},
+ { /* end of list */ },
+ };
+
+ static const struct rlist nicam_i[] = {
+ {AUD_PDF_DDS_CNST_BYTE0, 0x12},
+ {AUD_PHACC_FREQ_8MSB, 0x3a},
+ {AUD_PHACC_FREQ_8LSB, 0x93},
+ { /* end of list */ },
+ };
+
+ static const struct rlist nicam_default[] = {
+ {AUD_PDF_DDS_CNST_BYTE0, 0x16},
+ {AUD_PHACC_FREQ_8MSB, 0x34},
+ {AUD_PHACC_FREQ_8LSB, 0x4c},
+ { /* end of list */ },
+ };
+
+ set_audio_start(core,SEL_NICAM);
+ switch (core->tvaudio) {
+ case WW_L:
+ dprintk("%s SECAM-L NICAM (status: devel)\n", __func__);
+ set_audio_registers(core, nicam_l);
+ break;
+ case WW_I:
+ dprintk("%s PAL-I NICAM (status: known-good)\n", __func__);
+ set_audio_registers(core, nicam_bgdki_common);
+ set_audio_registers(core, nicam_i);
+ break;
+ case WW_NONE:
+ case WW_BTSC:
+ case WW_BG:
+ case WW_DK:
+ case WW_EIAJ:
+ case WW_I2SPT:
+ case WW_FM:
+ case WW_I2SADC:
+ case WW_M:
+ dprintk("%s PAL-BGDK NICAM (status: known-good)\n", __func__);
+ set_audio_registers(core, nicam_bgdki_common);
+ set_audio_registers(core, nicam_default);
+ break;
+ };
+
+ mode |= EN_DMTRX_LR | EN_DMTRX_BYPASS;
+ set_audio_finish(core, mode);
+}
+
+static void set_audio_standard_A2(struct cx88_core *core, u32 mode)
+{
+ static const struct rlist a2_bgdk_common[] = {
+ {AUD_ERRLOGPERIOD_R, 0x00000064},
+ {AUD_ERRINTRPTTHSHLD1_R, 0x00000fff},
+ {AUD_ERRINTRPTTHSHLD2_R, 0x0000001f},
+ {AUD_ERRINTRPTTHSHLD3_R, 0x0000000f},
+ {AUD_PDF_DDS_CNST_BYTE2, 0x06},
+ {AUD_PDF_DDS_CNST_BYTE1, 0x82},
+ {AUD_PDF_DDS_CNST_BYTE0, 0x12},
+ {AUD_QAM_MODE, 0x05},
+ {AUD_PHACC_FREQ_8MSB, 0x34},
+ {AUD_PHACC_FREQ_8LSB, 0x4c},
+ {AUD_RATE_ADJ1, 0x00000100},
+ {AUD_RATE_ADJ2, 0x00000200},
+ {AUD_RATE_ADJ3, 0x00000300},
+ {AUD_RATE_ADJ4, 0x00000400},
+ {AUD_RATE_ADJ5, 0x00000500},
+ {AUD_THR_FR, 0x00000000},
+ {AAGC_HYST, 0x0000001a},
+ {AUD_PILOT_BQD_1_K0, 0x0000755b},
+ {AUD_PILOT_BQD_1_K1, 0x00551340},
+ {AUD_PILOT_BQD_1_K2, 0x006d30be},
+ {AUD_PILOT_BQD_1_K3, 0xffd394af},
+ {AUD_PILOT_BQD_1_K4, 0x00400000},
+ {AUD_PILOT_BQD_2_K0, 0x00040000},
+ {AUD_PILOT_BQD_2_K1, 0x002a4841},
+ {AUD_PILOT_BQD_2_K2, 0x00400000},
+ {AUD_PILOT_BQD_2_K3, 0x00000000},
+ {AUD_PILOT_BQD_2_K4, 0x00000000},
+ {AUD_MODE_CHG_TIMER, 0x00000040},
+ {AUD_AFE_12DB_EN, 0x00000001},
+ {AUD_CORDIC_SHIFT_0, 0x00000007},
+ {AUD_CORDIC_SHIFT_1, 0x00000007},
+ {AUD_DEEMPH0_G0, 0x00000380},
+ {AUD_DEEMPH1_G0, 0x00000380},
+ {AUD_DCOC_0_SRC, 0x0000001a},
+ {AUD_DCOC0_SHIFT, 0x00000000},
+ {AUD_DCOC_0_SHIFT_IN0, 0x0000000a},
+ {AUD_DCOC_0_SHIFT_IN1, 0x00000008},
+ {AUD_DCOC_PASS_IN, 0x00000003},
+ {AUD_IIR3_0_SEL, 0x00000021},
+ {AUD_DN2_AFC, 0x00000002},
+ {AUD_DCOC_1_SRC, 0x0000001b},
+ {AUD_DCOC1_SHIFT, 0x00000000},
+ {AUD_DCOC_1_SHIFT_IN0, 0x0000000a},
+ {AUD_DCOC_1_SHIFT_IN1, 0x00000008},
+ {AUD_IIR3_1_SEL, 0x00000023},
+ {AUD_RDSI_SEL, 0x00000017},
+ {AUD_RDSI_SHIFT, 0x00000000},
+ {AUD_RDSQ_SEL, 0x00000017},
+ {AUD_RDSQ_SHIFT, 0x00000000},
+ {AUD_PLL_INT, 0x0000001e},
+ {AUD_PLL_DDS, 0x00000000},
+ {AUD_PLL_FRAC, 0x0000e542},
+ {AUD_POLYPH80SCALEFAC, 0x00000001},
+ {AUD_START_TIMER, 0x00000000},
+ { /* end of list */ },
+ };
+
+ static const struct rlist a2_bg[] = {
+ {AUD_DMD_RA_DDS, 0x002a4f2f},
+ {AUD_C1_UP_THR, 0x00007000},
+ {AUD_C1_LO_THR, 0x00005400},
+ {AUD_C2_UP_THR, 0x00005400},
+ {AUD_C2_LO_THR, 0x00003000},
+ { /* end of list */ },
+ };
+
+ static const struct rlist a2_dk[] = {
+ {AUD_DMD_RA_DDS, 0x002a4f2f},
+ {AUD_C1_UP_THR, 0x00007000},
+ {AUD_C1_LO_THR, 0x00005400},
+ {AUD_C2_UP_THR, 0x00005400},
+ {AUD_C2_LO_THR, 0x00003000},
+ {AUD_DN0_FREQ, 0x00003a1c},
+ {AUD_DN2_FREQ, 0x0000d2e0},
+ { /* end of list */ },
+ };
+
+ static const struct rlist a1_i[] = {
+ {AUD_ERRLOGPERIOD_R, 0x00000064},
+ {AUD_ERRINTRPTTHSHLD1_R, 0x00000fff},
+ {AUD_ERRINTRPTTHSHLD2_R, 0x0000001f},
+ {AUD_ERRINTRPTTHSHLD3_R, 0x0000000f},
+ {AUD_PDF_DDS_CNST_BYTE2, 0x06},
+ {AUD_PDF_DDS_CNST_BYTE1, 0x82},
+ {AUD_PDF_DDS_CNST_BYTE0, 0x12},
+ {AUD_QAM_MODE, 0x05},
+ {AUD_PHACC_FREQ_8MSB, 0x3a},
+ {AUD_PHACC_FREQ_8LSB, 0x93},
+ {AUD_DMD_RA_DDS, 0x002a4f2f},
+ {AUD_PLL_INT, 0x0000001e},
+ {AUD_PLL_DDS, 0x00000004},
+ {AUD_PLL_FRAC, 0x0000e542},
+ {AUD_RATE_ADJ1, 0x00000100},
+ {AUD_RATE_ADJ2, 0x00000200},
+ {AUD_RATE_ADJ3, 0x00000300},
+ {AUD_RATE_ADJ4, 0x00000400},
+ {AUD_RATE_ADJ5, 0x00000500},
+ {AUD_THR_FR, 0x00000000},
+ {AUD_PILOT_BQD_1_K0, 0x0000755b},
+ {AUD_PILOT_BQD_1_K1, 0x00551340},
+ {AUD_PILOT_BQD_1_K2, 0x006d30be},
+ {AUD_PILOT_BQD_1_K3, 0xffd394af},
+ {AUD_PILOT_BQD_1_K4, 0x00400000},
+ {AUD_PILOT_BQD_2_K0, 0x00040000},
+ {AUD_PILOT_BQD_2_K1, 0x002a4841},
+ {AUD_PILOT_BQD_2_K2, 0x00400000},
+ {AUD_PILOT_BQD_2_K3, 0x00000000},
+ {AUD_PILOT_BQD_2_K4, 0x00000000},
+ {AUD_MODE_CHG_TIMER, 0x00000060},
+ {AUD_AFE_12DB_EN, 0x00000001},
+ {AAGC_HYST, 0x0000000a},
+ {AUD_CORDIC_SHIFT_0, 0x00000007},
+ {AUD_CORDIC_SHIFT_1, 0x00000007},
+ {AUD_C1_UP_THR, 0x00007000},
+ {AUD_C1_LO_THR, 0x00005400},
+ {AUD_C2_UP_THR, 0x00005400},
+ {AUD_C2_LO_THR, 0x00003000},
+ {AUD_DCOC_0_SRC, 0x0000001a},
+ {AUD_DCOC0_SHIFT, 0x00000000},
+ {AUD_DCOC_0_SHIFT_IN0, 0x0000000a},
+ {AUD_DCOC_0_SHIFT_IN1, 0x00000008},
+ {AUD_DCOC_PASS_IN, 0x00000003},
+ {AUD_IIR3_0_SEL, 0x00000021},
+ {AUD_DN2_AFC, 0x00000002},
+ {AUD_DCOC_1_SRC, 0x0000001b},
+ {AUD_DCOC1_SHIFT, 0x00000000},
+ {AUD_DCOC_1_SHIFT_IN0, 0x0000000a},
+ {AUD_DCOC_1_SHIFT_IN1, 0x00000008},
+ {AUD_IIR3_1_SEL, 0x00000023},
+ {AUD_DN0_FREQ, 0x000035a3},
+ {AUD_DN2_FREQ, 0x000029c7},
+ {AUD_CRDC0_SRC_SEL, 0x00000511},
+ {AUD_IIR1_0_SEL, 0x00000001},
+ {AUD_IIR1_1_SEL, 0x00000000},
+ {AUD_IIR3_2_SEL, 0x00000003},
+ {AUD_IIR3_2_SHIFT, 0x00000000},
+ {AUD_IIR3_0_SEL, 0x00000002},
+ {AUD_IIR2_0_SEL, 0x00000021},
+ {AUD_IIR2_0_SHIFT, 0x00000002},
+ {AUD_DEEMPH0_SRC_SEL, 0x0000000b},
+ {AUD_DEEMPH1_SRC_SEL, 0x0000000b},
+ {AUD_POLYPH80SCALEFAC, 0x00000001},
+ {AUD_START_TIMER, 0x00000000},
+ { /* end of list */ },
+ };
+
+ static const struct rlist am_l[] = {
+ {AUD_ERRLOGPERIOD_R, 0x00000064},
+ {AUD_ERRINTRPTTHSHLD1_R, 0x00000FFF},
+ {AUD_ERRINTRPTTHSHLD2_R, 0x0000001F},
+ {AUD_ERRINTRPTTHSHLD3_R, 0x0000000F},
+ {AUD_PDF_DDS_CNST_BYTE2, 0x48},
+ {AUD_PDF_DDS_CNST_BYTE1, 0x3D},
+ {AUD_QAM_MODE, 0x00},
+ {AUD_PDF_DDS_CNST_BYTE0, 0xf5},
+ {AUD_PHACC_FREQ_8MSB, 0x3a},
+ {AUD_PHACC_FREQ_8LSB, 0x4a},
+ {AUD_DEEMPHGAIN_R, 0x00006680},
+ {AUD_DEEMPHNUMER1_R, 0x000353DE},
+ {AUD_DEEMPHNUMER2_R, 0x000001B1},
+ {AUD_DEEMPHDENOM1_R, 0x0000F3D0},
+ {AUD_DEEMPHDENOM2_R, 0x00000000},
+ {AUD_FM_MODE_ENABLE, 0x00000007},
+ {AUD_POLYPH80SCALEFAC, 0x00000003},
+ {AUD_AFE_12DB_EN, 0x00000001},
+ {AAGC_GAIN, 0x00000000},
+ {AAGC_HYST, 0x00000018},
+ {AAGC_DEF, 0x00000020},
+ {AUD_DN0_FREQ, 0x00000000},
+ {AUD_POLY0_DDS_CONSTANT, 0x000E4DB2},
+ {AUD_DCOC_0_SRC, 0x00000021},
+ {AUD_IIR1_0_SEL, 0x00000000},
+ {AUD_IIR1_0_SHIFT, 0x00000007},
+ {AUD_IIR1_1_SEL, 0x00000002},
+ {AUD_IIR1_1_SHIFT, 0x00000000},
+ {AUD_DCOC_1_SRC, 0x00000003},
+ {AUD_DCOC1_SHIFT, 0x00000000},
+ {AUD_DCOC_PASS_IN, 0x00000000},
+ {AUD_IIR1_2_SEL, 0x00000023},
+ {AUD_IIR1_2_SHIFT, 0x00000000},
+ {AUD_IIR1_3_SEL, 0x00000004},
+ {AUD_IIR1_3_SHIFT, 0x00000007},
+ {AUD_IIR1_4_SEL, 0x00000005},
+ {AUD_IIR1_4_SHIFT, 0x00000007},
+ {AUD_IIR3_0_SEL, 0x00000007},
+ {AUD_IIR3_0_SHIFT, 0x00000000},
+ {AUD_DEEMPH0_SRC_SEL, 0x00000011},
+ {AUD_DEEMPH0_SHIFT, 0x00000000},
+ {AUD_DEEMPH0_G0, 0x00007000},
+ {AUD_DEEMPH0_A0, 0x00000000},
+ {AUD_DEEMPH0_B0, 0x00000000},
+ {AUD_DEEMPH0_A1, 0x00000000},
+ {AUD_DEEMPH0_B1, 0x00000000},
+ {AUD_DEEMPH1_SRC_SEL, 0x00000011},
+ {AUD_DEEMPH1_SHIFT, 0x00000000},
+ {AUD_DEEMPH1_G0, 0x00007000},
+ {AUD_DEEMPH1_A0, 0x00000000},
+ {AUD_DEEMPH1_B0, 0x00000000},
+ {AUD_DEEMPH1_A1, 0x00000000},
+ {AUD_DEEMPH1_B1, 0x00000000},
+ {AUD_OUT0_SEL, 0x0000003F},
+ {AUD_OUT1_SEL, 0x0000003F},
+ {AUD_DMD_RA_DDS, 0x00F5C285},
+ {AUD_PLL_INT, 0x0000001E},
+ {AUD_PLL_DDS, 0x00000000},
+ {AUD_PLL_FRAC, 0x0000E542},
+ {AUD_RATE_ADJ1, 0x00000100},
+ {AUD_RATE_ADJ2, 0x00000200},
+ {AUD_RATE_ADJ3, 0x00000300},
+ {AUD_RATE_ADJ4, 0x00000400},
+ {AUD_RATE_ADJ5, 0x00000500},
+ {AUD_RATE_THRES_DMD, 0x000000C0},
+ { /* end of list */ },
+ };
+
+ static const struct rlist a2_deemph50[] = {
+ {AUD_DEEMPH0_G0, 0x00000380},
+ {AUD_DEEMPH1_G0, 0x00000380},
+ {AUD_DEEMPHGAIN_R, 0x000011e1},
+ {AUD_DEEMPHNUMER1_R, 0x0002a7bc},
+ {AUD_DEEMPHNUMER2_R, 0x0003023c},
+ { /* end of list */ },
+ };
+
+ set_audio_start(core, SEL_A2);
+ switch (core->tvaudio) {
+ case WW_BG:
+ dprintk("%s PAL-BG A1/2 (status: known-good)\n", __func__);
+ set_audio_registers(core, a2_bgdk_common);
+ set_audio_registers(core, a2_bg);
+ set_audio_registers(core, a2_deemph50);
+ break;
+ case WW_DK:
+ dprintk("%s PAL-DK A1/2 (status: known-good)\n", __func__);
+ set_audio_registers(core, a2_bgdk_common);
+ set_audio_registers(core, a2_dk);
+ set_audio_registers(core, a2_deemph50);
+ break;
+ case WW_I:
+ dprintk("%s PAL-I A1 (status: known-good)\n", __func__);
+ set_audio_registers(core, a1_i);
+ set_audio_registers(core, a2_deemph50);
+ break;
+ case WW_L:
+ dprintk("%s AM-L (status: devel)\n", __func__);
+ set_audio_registers(core, am_l);
+ break;
+ case WW_NONE:
+ case WW_BTSC:
+ case WW_EIAJ:
+ case WW_I2SPT:
+ case WW_FM:
+ case WW_I2SADC:
+ case WW_M:
+ dprintk("%s Warning: wrong value\n", __func__);
+ return;
+ break;
+ };
+
+ mode |= EN_FMRADIO_EN_RDS | EN_DMTRX_SUMDIFF;
+ set_audio_finish(core, mode);
+}
+
+static void set_audio_standard_EIAJ(struct cx88_core *core)
+{
+ static const struct rlist eiaj[] = {
+ /* TODO: eiaj register settings are not there yet ... */
+
+ { /* end of list */ },
+ };
+ dprintk("%s (status: unknown)\n", __func__);
+
+ set_audio_start(core, SEL_EIAJ);
+ set_audio_registers(core, eiaj);
+ set_audio_finish(core, EN_EIAJ_AUTO_STEREO);
+}
+
+static void set_audio_standard_FM(struct cx88_core *core,
+ enum cx88_deemph_type deemph)
+{
+ static const struct rlist fm_deemph_50[] = {
+ {AUD_DEEMPH0_G0, 0x0C45},
+ {AUD_DEEMPH0_A0, 0x6262},
+ {AUD_DEEMPH0_B0, 0x1C29},
+ {AUD_DEEMPH0_A1, 0x3FC66},
+ {AUD_DEEMPH0_B1, 0x399A},
+
+ {AUD_DEEMPH1_G0, 0x0D80},
+ {AUD_DEEMPH1_A0, 0x6262},
+ {AUD_DEEMPH1_B0, 0x1C29},
+ {AUD_DEEMPH1_A1, 0x3FC66},
+ {AUD_DEEMPH1_B1, 0x399A},
+
+ {AUD_POLYPH80SCALEFAC, 0x0003},
+ { /* end of list */ },
+ };
+ static const struct rlist fm_deemph_75[] = {
+ {AUD_DEEMPH0_G0, 0x091B},
+ {AUD_DEEMPH0_A0, 0x6B68},
+ {AUD_DEEMPH0_B0, 0x11EC},
+ {AUD_DEEMPH0_A1, 0x3FC66},
+ {AUD_DEEMPH0_B1, 0x399A},
+
+ {AUD_DEEMPH1_G0, 0x0AA0},
+ {AUD_DEEMPH1_A0, 0x6B68},
+ {AUD_DEEMPH1_B0, 0x11EC},
+ {AUD_DEEMPH1_A1, 0x3FC66},
+ {AUD_DEEMPH1_B1, 0x399A},
+
+ {AUD_POLYPH80SCALEFAC, 0x0003},
+ { /* end of list */ },
+ };
+
+ /* It is enough to leave default values? */
+ /* No, it's not! The deemphasis registers are reset to the 75us
+ * values by default. Analyzing the spectrum of the decoded audio
+ * reveals that "no deemphasis" is the same as 75 us, while the 50 us
+ * setting results in less deemphasis. */
+ static const struct rlist fm_no_deemph[] = {
+
+ {AUD_POLYPH80SCALEFAC, 0x0003},
+ { /* end of list */ },
+ };
+
+ dprintk("%s (status: unknown)\n", __func__);
+ set_audio_start(core, SEL_FMRADIO);
+
+ switch (deemph) {
+ default:
+ case FM_NO_DEEMPH:
+ set_audio_registers(core, fm_no_deemph);
+ break;
+
+ case FM_DEEMPH_50:
+ set_audio_registers(core, fm_deemph_50);
+ break;
+
+ case FM_DEEMPH_75:
+ set_audio_registers(core, fm_deemph_75);
+ break;
+ }
+
+ set_audio_finish(core, EN_FMRADIO_AUTO_STEREO);
+}
+
+/* ----------------------------------------------------------- */
+
+static int cx88_detect_nicam(struct cx88_core *core)
+{
+ int i, j = 0;
+
+ dprintk("start nicam autodetect.\n");
+
+ for (i = 0; i < 6; i++) {
+ /* if bit1=1 then nicam is detected */
+ j += ((cx_read(AUD_NICAM_STATUS2) & 0x02) >> 1);
+
+ if (j == 1) {
+ dprintk("nicam is detected.\n");
+ return 1;
+ }
+
+ /* wait a little bit for next reading status */
+ msleep(10);
+ }
+
+ dprintk("nicam is not detected.\n");
+ return 0;
+}
+
+void cx88_set_tvaudio(struct cx88_core *core)
+{
+ switch (core->tvaudio) {
+ case WW_BTSC:
+ set_audio_standard_BTSC(core, 0, EN_BTSC_AUTO_STEREO);
+ break;
+ case WW_BG:
+ case WW_DK:
+ case WW_M:
+ case WW_I:
+ case WW_L:
+ /* prepare all dsp registers */
+ set_audio_standard_A2(core, EN_A2_FORCE_MONO1);
+
+ /* set nicam mode - otherwise
+ AUD_NICAM_STATUS2 contains wrong values */
+ set_audio_standard_NICAM(core, EN_NICAM_AUTO_STEREO);
+ if (0 == cx88_detect_nicam(core)) {
+ /* fall back to fm / am mono */
+ set_audio_standard_A2(core, EN_A2_FORCE_MONO1);
+ core->audiomode_current = V4L2_TUNER_MODE_MONO;
+ core->use_nicam = 0;
+ } else {
+ core->use_nicam = 1;
+ }
+ break;
+ case WW_EIAJ:
+ set_audio_standard_EIAJ(core);
+ break;
+ case WW_FM:
+ set_audio_standard_FM(core, radio_deemphasis);
+ break;
+ case WW_I2SADC:
+ set_audio_start(core, 0x01);
+ /*
+ * Slave/Philips/Autobaud
+ * NB on Nova-S bit1 NPhilipsSony appears to be inverted:
+ * 0= Sony, 1=Philips
+ */
+ cx_write(AUD_I2SINPUTCNTL, core->board.i2sinputcntl);
+ /* Switch to "I2S ADC mode" */
+ cx_write(AUD_I2SCNTL, 0x1);
+ set_audio_finish(core, EN_I2SIN_ENABLE);
+ break;
+ case WW_NONE:
+ case WW_I2SPT:
+ printk("%s/0: unknown tv audio mode [%d]\n",
+ core->name, core->tvaudio);
+ break;
+ }
+ return;
+}
+
+void cx88_newstation(struct cx88_core *core)
+{
+ core->audiomode_manual = UNSET;
+ core->last_change = jiffies;
+}
+
+void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t)
+{
+ static const char * const m[] = { "stereo", "dual mono", "mono", "sap" };
+ static const char * const p[] = { "no pilot", "pilot c1", "pilot c2", "?" };
+ u32 reg, mode, pilot;
+
+ reg = cx_read(AUD_STATUS);
+ mode = reg & 0x03;
+ pilot = (reg >> 2) & 0x03;
+
+ if (core->astat != reg)
+ dprintk("AUD_STATUS: 0x%x [%s/%s] ctl=%s\n",
+ reg, m[mode], p[pilot],
+ aud_ctl_names[cx_read(AUD_CTL) & 63]);
+ core->astat = reg;
+
+ t->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_SAP |
+ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
+ t->rxsubchans = UNSET;
+ t->audmode = V4L2_TUNER_MODE_MONO;
+
+ switch (mode) {
+ case 0:
+ t->audmode = V4L2_TUNER_MODE_STEREO;
+ break;
+ case 1:
+ t->audmode = V4L2_TUNER_MODE_LANG2;
+ break;
+ case 2:
+ t->audmode = V4L2_TUNER_MODE_MONO;
+ break;
+ case 3:
+ t->audmode = V4L2_TUNER_MODE_SAP;
+ break;
+ }
+
+ switch (core->tvaudio) {
+ case WW_BTSC:
+ case WW_BG:
+ case WW_DK:
+ case WW_M:
+ case WW_EIAJ:
+ if (!core->use_nicam) {
+ t->rxsubchans = cx88_dsp_detect_stereo_sap(core);
+ break;
+ }
+ break;
+ case WW_NONE:
+ case WW_I:
+ case WW_L:
+ case WW_I2SPT:
+ case WW_FM:
+ case WW_I2SADC:
+ /* nothing */
+ break;
+ }
+
+ /* If software stereo detection is not supported... */
+ if (UNSET == t->rxsubchans) {
+ t->rxsubchans = V4L2_TUNER_SUB_MONO;
+ /* If the hardware itself detected stereo, also return
+ stereo as an available subchannel */
+ if (V4L2_TUNER_MODE_STEREO == t->audmode)
+ t->rxsubchans |= V4L2_TUNER_SUB_STEREO;
+ }
+ return;
+}
+
+void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual)
+{
+ u32 ctl = UNSET;
+ u32 mask = UNSET;
+
+ if (manual) {
+ core->audiomode_manual = mode;
+ } else {
+ if (UNSET != core->audiomode_manual)
+ return;
+ }
+ core->audiomode_current = mode;
+
+ switch (core->tvaudio) {
+ case WW_BTSC:
+ switch (mode) {
+ case V4L2_TUNER_MODE_MONO:
+ set_audio_standard_BTSC(core, 0, EN_BTSC_FORCE_MONO);
+ break;
+ case V4L2_TUNER_MODE_LANG1:
+ set_audio_standard_BTSC(core, 0, EN_BTSC_AUTO_STEREO);
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ set_audio_standard_BTSC(core, 1, EN_BTSC_FORCE_SAP);
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ set_audio_standard_BTSC(core, 0, EN_BTSC_FORCE_STEREO);
+ break;
+ }
+ break;
+ case WW_BG:
+ case WW_DK:
+ case WW_M:
+ case WW_I:
+ case WW_L:
+ if (1 == core->use_nicam) {
+ switch (mode) {
+ case V4L2_TUNER_MODE_MONO:
+ case V4L2_TUNER_MODE_LANG1:
+ set_audio_standard_NICAM(core,
+ EN_NICAM_FORCE_MONO1);
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ set_audio_standard_NICAM(core,
+ EN_NICAM_FORCE_MONO2);
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ set_audio_standard_NICAM(core,
+ EN_NICAM_FORCE_STEREO);
+ break;
+ }
+ } else {
+ if ((core->tvaudio == WW_I) || (core->tvaudio == WW_L)) {
+ /* fall back to fm / am mono */
+ set_audio_standard_A2(core, EN_A2_FORCE_MONO1);
+ } else {
+ /* TODO: Add A2 autodection */
+ mask = 0x3f;
+ switch (mode) {
+ case V4L2_TUNER_MODE_MONO:
+ case V4L2_TUNER_MODE_LANG1:
+ ctl = EN_A2_FORCE_MONO1;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ ctl = EN_A2_FORCE_MONO2;
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ ctl = EN_A2_FORCE_STEREO;
+ break;
+ }
+ }
+ }
+ break;
+ case WW_FM:
+ switch (mode) {
+ case V4L2_TUNER_MODE_MONO:
+ ctl = EN_FMRADIO_FORCE_MONO;
+ mask = 0x3f;
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ ctl = EN_FMRADIO_AUTO_STEREO;
+ mask = 0x3f;
+ break;
+ }
+ break;
+ case WW_I2SADC:
+ case WW_NONE:
+ case WW_EIAJ:
+ case WW_I2SPT:
+ /* DO NOTHING */
+ break;
+ }
+
+ if (UNSET != ctl) {
+ dprintk("cx88_set_stereo: mask 0x%x, ctl 0x%x "
+ "[status=0x%x,ctl=0x%x,vol=0x%x]\n",
+ mask, ctl, cx_read(AUD_STATUS),
+ cx_read(AUD_CTL), cx_sread(SHADOW_AUD_VOL_CTL));
+ cx_andor(AUD_CTL, mask, ctl);
+ }
+ return;
+}
+
+int cx88_audio_thread(void *data)
+{
+ struct cx88_core *core = data;
+ struct v4l2_tuner t;
+ u32 mode = 0;
+
+ dprintk("cx88: tvaudio thread started\n");
+ set_freezable();
+ for (;;) {
+ msleep_interruptible(1000);
+ if (kthread_should_stop())
+ break;
+ try_to_freeze();
+
+ switch (core->tvaudio) {
+ case WW_BG:
+ case WW_DK:
+ case WW_M:
+ case WW_I:
+ case WW_L:
+ if (core->use_nicam)
+ goto hw_autodetect;
+
+ /* just monitor the audio status for now ... */
+ memset(&t, 0, sizeof(t));
+ cx88_get_stereo(core, &t);
+
+ if (UNSET != core->audiomode_manual)
+ /* manually set, don't do anything. */
+ continue;
+
+ /* monitor signal and set stereo if available */
+ if (t.rxsubchans & V4L2_TUNER_SUB_STEREO)
+ mode = V4L2_TUNER_MODE_STEREO;
+ else
+ mode = V4L2_TUNER_MODE_MONO;
+ if (mode == core->audiomode_current)
+ continue;
+ /* automatically switch to best available mode */
+ cx88_set_stereo(core, mode, 0);
+ break;
+ case WW_NONE:
+ case WW_BTSC:
+ case WW_EIAJ:
+ case WW_I2SPT:
+ case WW_FM:
+ case WW_I2SADC:
+hw_autodetect:
+ /* stereo autodetection is supported by hardware so
+ we don't need to do it manually. Do nothing. */
+ break;
+ }
+ }
+
+ dprintk("cx88: tvaudio thread exiting\n");
+ return 0;
+}
+
+/* ----------------------------------------------------------- */
+
+EXPORT_SYMBOL(cx88_set_tvaudio);
+EXPORT_SYMBOL(cx88_newstation);
+EXPORT_SYMBOL(cx88_set_stereo);
+EXPORT_SYMBOL(cx88_get_stereo);
+EXPORT_SYMBOL(cx88_audio_thread);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off
+ */
diff --git a/drivers/media/video/cx88/cx88-vbi.c b/drivers/media/video/cx88/cx88-vbi.c
new file mode 100644
index 00000000..f8f8389c
--- /dev/null
+++ b/drivers/media/video/cx88/cx88-vbi.c
@@ -0,0 +1,245 @@
+/*
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include "cx88.h"
+
+static unsigned int vbibufs = 4;
+module_param(vbibufs,int,0644);
+MODULE_PARM_DESC(vbibufs,"number of vbi buffers, range 2-32");
+
+static unsigned int vbi_debug;
+module_param(vbi_debug,int,0644);
+MODULE_PARM_DESC(vbi_debug,"enable debug messages [vbi]");
+
+#define dprintk(level,fmt, arg...) if (vbi_debug >= level) \
+ printk(KERN_DEBUG "%s: " fmt, dev->core->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+
+int cx8800_vbi_fmt (struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx8800_fh *fh = priv;
+ struct cx8800_dev *dev = fh->dev;
+
+ f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
+ f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+ f->fmt.vbi.offset = 244;
+ f->fmt.vbi.count[0] = VBI_LINE_COUNT;
+ f->fmt.vbi.count[1] = VBI_LINE_COUNT;
+
+ if (dev->core->tvnorm & V4L2_STD_525_60) {
+ /* ntsc */
+ f->fmt.vbi.sampling_rate = 28636363;
+ f->fmt.vbi.start[0] = 10;
+ f->fmt.vbi.start[1] = 273;
+
+ } else if (dev->core->tvnorm & V4L2_STD_625_50) {
+ /* pal */
+ f->fmt.vbi.sampling_rate = 35468950;
+ f->fmt.vbi.start[0] = 7 -1;
+ f->fmt.vbi.start[1] = 319 -1;
+ }
+ return 0;
+}
+
+static int cx8800_start_vbi_dma(struct cx8800_dev *dev,
+ struct cx88_dmaqueue *q,
+ struct cx88_buffer *buf)
+{
+ struct cx88_core *core = dev->core;
+
+ /* setup fifo + format */
+ cx88_sram_channel_setup(dev->core, &cx88_sram_channels[SRAM_CH24],
+ buf->vb.width, buf->risc.dma);
+
+ cx_write(MO_VBOS_CONTROL, ( (1 << 18) | // comb filter delay fixup
+ (1 << 15) | // enable vbi capture
+ (1 << 11) ));
+
+ /* reset counter */
+ cx_write(MO_VBI_GPCNTRL, GP_COUNT_CONTROL_RESET);
+ q->count = 1;
+
+ /* enable irqs */
+ cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_VIDINT);
+ cx_set(MO_VID_INTMSK, 0x0f0088);
+
+ /* enable capture */
+ cx_set(VID_CAPTURE_CONTROL,0x18);
+
+ /* start dma */
+ cx_set(MO_DEV_CNTRL2, (1<<5));
+ cx_set(MO_VID_DMACNTRL, 0x88);
+
+ return 0;
+}
+
+int cx8800_stop_vbi_dma(struct cx8800_dev *dev)
+{
+ struct cx88_core *core = dev->core;
+
+ /* stop dma */
+ cx_clear(MO_VID_DMACNTRL, 0x88);
+
+ /* disable capture */
+ cx_clear(VID_CAPTURE_CONTROL,0x18);
+
+ /* disable irqs */
+ cx_clear(MO_PCI_INTMSK, PCI_INT_VIDINT);
+ cx_clear(MO_VID_INTMSK, 0x0f0088);
+ return 0;
+}
+
+int cx8800_restart_vbi_queue(struct cx8800_dev *dev,
+ struct cx88_dmaqueue *q)
+{
+ struct cx88_buffer *buf;
+
+ if (list_empty(&q->active))
+ return 0;
+
+ buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
+ dprintk(2,"restart_queue [%p/%d]: restart dma\n",
+ buf, buf->vb.i);
+ cx8800_start_vbi_dma(dev, q, buf);
+ list_for_each_entry(buf, &q->active, vb.queue)
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ return 0;
+}
+
+void cx8800_vbi_timeout(unsigned long data)
+{
+ struct cx8800_dev *dev = (struct cx8800_dev*)data;
+ struct cx88_core *core = dev->core;
+ struct cx88_dmaqueue *q = &dev->vbiq;
+ struct cx88_buffer *buf;
+ unsigned long flags;
+
+ cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH24]);
+
+ cx_clear(MO_VID_DMACNTRL, 0x88);
+ cx_clear(VID_CAPTURE_CONTROL, 0x18);
+
+ spin_lock_irqsave(&dev->slock,flags);
+ while (!list_empty(&q->active)) {
+ buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
+ list_del(&buf->vb.queue);
+ buf->vb.state = VIDEOBUF_ERROR;
+ wake_up(&buf->vb.done);
+ printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", dev->core->name,
+ buf, buf->vb.i, (unsigned long)buf->risc.dma);
+ }
+ cx8800_restart_vbi_queue(dev,q);
+ spin_unlock_irqrestore(&dev->slock,flags);
+}
+
+/* ------------------------------------------------------------------ */
+
+static int
+vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+ *size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2;
+ if (0 == *count)
+ *count = vbibufs;
+ if (*count < 2)
+ *count = 2;
+ if (*count > 32)
+ *count = 32;
+ return 0;
+}
+
+static int
+vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct cx8800_fh *fh = q->priv_data;
+ struct cx8800_dev *dev = fh->dev;
+ struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb);
+ unsigned int size;
+ int rc;
+
+ size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2;
+ if (0 != buf->vb.baddr && buf->vb.bsize < size)
+ return -EINVAL;
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+ buf->vb.width = VBI_LINE_LENGTH;
+ buf->vb.height = VBI_LINE_COUNT;
+ buf->vb.size = size;
+ buf->vb.field = V4L2_FIELD_SEQ_TB;
+
+ if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL)))
+ goto fail;
+ cx88_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist,
+ 0, buf->vb.width * buf->vb.height,
+ buf->vb.width, 0,
+ buf->vb.height);
+ }
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+ fail:
+ cx88_free_buffer(q,buf);
+ return rc;
+}
+
+static void
+vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb);
+ struct cx88_buffer *prev;
+ struct cx8800_fh *fh = vq->priv_data;
+ struct cx8800_dev *dev = fh->dev;
+ struct cx88_dmaqueue *q = &dev->vbiq;
+
+ /* add jump to stopper */
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
+ buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
+
+ if (list_empty(&q->active)) {
+ list_add_tail(&buf->vb.queue,&q->active);
+ cx8800_start_vbi_dma(dev, q, buf);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ dprintk(2,"[%p/%d] vbi_queue - first active\n",
+ buf, buf->vb.i);
+
+ } else {
+ prev = list_entry(q->active.prev, struct cx88_buffer, vb.queue);
+ list_add_tail(&buf->vb.queue,&q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ dprintk(2,"[%p/%d] buffer_queue - append to active\n",
+ buf, buf->vb.i);
+ }
+}
+
+static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb);
+
+ cx88_free_buffer(q,buf);
+}
+
+const struct videobuf_queue_ops cx8800_vbi_qops = {
+ .buf_setup = vbi_setup,
+ .buf_prepare = vbi_prepare,
+ .buf_queue = vbi_queue,
+ .buf_release = vbi_release,
+};
+
+/* ------------------------------------------------------------------ */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c
new file mode 100644
index 00000000..921c56d1
--- /dev/null
+++ b/drivers/media/video/cx88/cx88-video.c
@@ -0,0 +1,2194 @@
+/*
+ *
+ * device driver for Conexant 2388x based TV cards
+ * video4linux video interface
+ *
+ * (c) 2003-04 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org>
+ * - Multituner support
+ * - video_ioctl2 conversion
+ * - PAL/M fixes
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <asm/div64.h>
+
+#include "cx88.h"
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/wm8775.h>
+
+MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards");
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(CX88_VERSION);
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int video_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
+static unsigned int vbi_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
+static unsigned int radio_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
+
+module_param_array(video_nr, int, NULL, 0444);
+module_param_array(vbi_nr, int, NULL, 0444);
+module_param_array(radio_nr, int, NULL, 0444);
+
+MODULE_PARM_DESC(video_nr,"video device numbers");
+MODULE_PARM_DESC(vbi_nr,"vbi device numbers");
+MODULE_PARM_DESC(radio_nr,"radio device numbers");
+
+static unsigned int video_debug;
+module_param(video_debug,int,0644);
+MODULE_PARM_DESC(video_debug,"enable debug messages [video]");
+
+static unsigned int irq_debug;
+module_param(irq_debug,int,0644);
+MODULE_PARM_DESC(irq_debug,"enable debug messages [IRQ handler]");
+
+static unsigned int vid_limit = 16;
+module_param(vid_limit,int,0644);
+MODULE_PARM_DESC(vid_limit,"capture memory limit in megabytes");
+
+#define dprintk(level,fmt, arg...) if (video_debug >= level) \
+ printk(KERN_DEBUG "%s/0: " fmt, core->name , ## arg)
+
+/* ------------------------------------------------------------------- */
+/* static data */
+
+static const struct cx8800_fmt formats[] = {
+ {
+ .name = "8 bpp, gray",
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .cxformat = ColorFormatY8,
+ .depth = 8,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "15 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .cxformat = ColorFormatRGB15,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "15 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB555X,
+ .cxformat = ColorFormatRGB15 | ColorFormatBSWAP,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "16 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .cxformat = ColorFormatRGB16,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "16 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+ .cxformat = ColorFormatRGB16 | ColorFormatBSWAP,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "24 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .cxformat = ColorFormatRGB24,
+ .depth = 24,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "32 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_BGR32,
+ .cxformat = ColorFormatRGB32,
+ .depth = 32,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "32 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .cxformat = ColorFormatRGB32 | ColorFormatBSWAP | ColorFormatWSWAP,
+ .depth = 32,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .cxformat = ColorFormatYUY2,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "4:2:2, packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .cxformat = ColorFormatYUY2 | ColorFormatBSWAP,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },
+};
+
+static const struct cx8800_fmt* format_by_fourcc(unsigned int fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(formats); i++)
+ if (formats[i].fourcc == fourcc)
+ return formats+i;
+ return NULL;
+}
+
+/* ------------------------------------------------------------------- */
+
+static const struct v4l2_queryctrl no_ctl = {
+ .name = "42",
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+};
+
+static const struct cx88_ctrl cx8800_ctls[] = {
+ /* --- video --- */
+ {
+ .v = {
+ .id = V4L2_CID_BRIGHTNESS,
+ .name = "Brightness",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 128,
+ .reg = MO_CONTR_BRIGHT,
+ .mask = 0x00ff,
+ .shift = 0,
+ },{
+ .v = {
+ .id = V4L2_CID_CONTRAST,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x3f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 0,
+ .reg = MO_CONTR_BRIGHT,
+ .mask = 0xff00,
+ .shift = 8,
+ },{
+ .v = {
+ .id = V4L2_CID_HUE,
+ .name = "Hue",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 128,
+ .reg = MO_HUE,
+ .mask = 0x00ff,
+ .shift = 0,
+ },{
+ /* strictly, this only describes only U saturation.
+ * V saturation is handled specially through code.
+ */
+ .v = {
+ .id = V4L2_CID_SATURATION,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 0,
+ .reg = MO_UV_SATURATION,
+ .mask = 0x00ff,
+ .shift = 0,
+ }, {
+ .v = {
+ .id = V4L2_CID_SHARPNESS,
+ .name = "Sharpness",
+ .minimum = 0,
+ .maximum = 4,
+ .step = 1,
+ .default_value = 0x0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 0,
+ /* NOTE: the value is converted and written to both even
+ and odd registers in the code */
+ .reg = MO_FILTER_ODD,
+ .mask = 7 << 7,
+ .shift = 7,
+ }, {
+ .v = {
+ .id = V4L2_CID_CHROMA_AGC,
+ .name = "Chroma AGC",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 0x1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },
+ .reg = MO_INPUT_FORMAT,
+ .mask = 1 << 10,
+ .shift = 10,
+ }, {
+ .v = {
+ .id = V4L2_CID_COLOR_KILLER,
+ .name = "Color killer",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 0x1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },
+ .reg = MO_INPUT_FORMAT,
+ .mask = 1 << 9,
+ .shift = 9,
+ }, {
+ .v = {
+ .id = V4L2_CID_BAND_STOP_FILTER,
+ .name = "Notch filter",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0x0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 0,
+ .reg = MO_HTOTAL,
+ .mask = 3 << 11,
+ .shift = 11,
+ }, {
+ /* --- audio --- */
+ .v = {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },
+ .reg = AUD_VOL_CTL,
+ .sreg = SHADOW_AUD_VOL_CTL,
+ .mask = (1 << 6),
+ .shift = 6,
+ },{
+ .v = {
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .name = "Volume",
+ .minimum = 0,
+ .maximum = 0x3f,
+ .step = 1,
+ .default_value = 0x3f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .reg = AUD_VOL_CTL,
+ .sreg = SHADOW_AUD_VOL_CTL,
+ .mask = 0x3f,
+ .shift = 0,
+ },{
+ .v = {
+ .id = V4L2_CID_AUDIO_BALANCE,
+ .name = "Balance",
+ .minimum = 0,
+ .maximum = 0x7f,
+ .step = 1,
+ .default_value = 0x40,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .reg = AUD_BAL_CTL,
+ .sreg = SHADOW_AUD_BAL_CTL,
+ .mask = 0x7f,
+ .shift = 0,
+ }
+};
+enum { CX8800_CTLS = ARRAY_SIZE(cx8800_ctls) };
+
+/* Must be sorted from low to high control ID! */
+const u32 cx88_user_ctrls[] = {
+ V4L2_CID_USER_CLASS,
+ V4L2_CID_BRIGHTNESS,
+ V4L2_CID_CONTRAST,
+ V4L2_CID_SATURATION,
+ V4L2_CID_HUE,
+ V4L2_CID_AUDIO_VOLUME,
+ V4L2_CID_AUDIO_BALANCE,
+ V4L2_CID_AUDIO_MUTE,
+ V4L2_CID_SHARPNESS,
+ V4L2_CID_CHROMA_AGC,
+ V4L2_CID_COLOR_KILLER,
+ V4L2_CID_BAND_STOP_FILTER,
+ 0
+};
+EXPORT_SYMBOL(cx88_user_ctrls);
+
+static const u32 * const ctrl_classes[] = {
+ cx88_user_ctrls,
+ NULL
+};
+
+int cx8800_ctrl_query(struct cx88_core *core, struct v4l2_queryctrl *qctrl)
+{
+ int i;
+
+ if (qctrl->id < V4L2_CID_BASE ||
+ qctrl->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+ for (i = 0; i < CX8800_CTLS; i++)
+ if (cx8800_ctls[i].v.id == qctrl->id)
+ break;
+ if (i == CX8800_CTLS) {
+ *qctrl = no_ctl;
+ return 0;
+ }
+ *qctrl = cx8800_ctls[i].v;
+ /* Report chroma AGC as inactive when SECAM is selected */
+ if (cx8800_ctls[i].v.id == V4L2_CID_CHROMA_AGC &&
+ core->tvnorm & V4L2_STD_SECAM)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+
+ return 0;
+}
+EXPORT_SYMBOL(cx8800_ctrl_query);
+
+/* ------------------------------------------------------------------- */
+/* resource management */
+
+static int res_get(struct cx8800_dev *dev, struct cx8800_fh *fh, unsigned int bit)
+{
+ struct cx88_core *core = dev->core;
+ if (fh->resources & bit)
+ /* have it already allocated */
+ return 1;
+
+ /* is it free? */
+ mutex_lock(&core->lock);
+ if (dev->resources & bit) {
+ /* no, someone else uses it */
+ mutex_unlock(&core->lock);
+ return 0;
+ }
+ /* it's free, grab it */
+ fh->resources |= bit;
+ dev->resources |= bit;
+ dprintk(1,"res: get %d\n",bit);
+ mutex_unlock(&core->lock);
+ return 1;
+}
+
+static
+int res_check(struct cx8800_fh *fh, unsigned int bit)
+{
+ return (fh->resources & bit);
+}
+
+static
+int res_locked(struct cx8800_dev *dev, unsigned int bit)
+{
+ return (dev->resources & bit);
+}
+
+static
+void res_free(struct cx8800_dev *dev, struct cx8800_fh *fh, unsigned int bits)
+{
+ struct cx88_core *core = dev->core;
+ BUG_ON((fh->resources & bits) != bits);
+
+ mutex_lock(&core->lock);
+ fh->resources &= ~bits;
+ dev->resources &= ~bits;
+ dprintk(1,"res: put %d\n",bits);
+ mutex_unlock(&core->lock);
+}
+
+/* ------------------------------------------------------------------ */
+
+int cx88_video_mux(struct cx88_core *core, unsigned int input)
+{
+ /* struct cx88_core *core = dev->core; */
+
+ dprintk(1,"video_mux: %d [vmux=%d,gpio=0x%x,0x%x,0x%x,0x%x]\n",
+ input, INPUT(input).vmux,
+ INPUT(input).gpio0,INPUT(input).gpio1,
+ INPUT(input).gpio2,INPUT(input).gpio3);
+ core->input = input;
+ cx_andor(MO_INPUT_FORMAT, 0x03 << 14, INPUT(input).vmux << 14);
+ cx_write(MO_GP3_IO, INPUT(input).gpio3);
+ cx_write(MO_GP0_IO, INPUT(input).gpio0);
+ cx_write(MO_GP1_IO, INPUT(input).gpio1);
+ cx_write(MO_GP2_IO, INPUT(input).gpio2);
+
+ switch (INPUT(input).type) {
+ case CX88_VMUX_SVIDEO:
+ cx_set(MO_AFECFG_IO, 0x00000001);
+ cx_set(MO_INPUT_FORMAT, 0x00010010);
+ cx_set(MO_FILTER_EVEN, 0x00002020);
+ cx_set(MO_FILTER_ODD, 0x00002020);
+ break;
+ default:
+ cx_clear(MO_AFECFG_IO, 0x00000001);
+ cx_clear(MO_INPUT_FORMAT, 0x00010010);
+ cx_clear(MO_FILTER_EVEN, 0x00002020);
+ cx_clear(MO_FILTER_ODD, 0x00002020);
+ break;
+ }
+
+ /* if there are audioroutes defined, we have an external
+ ADC to deal with audio */
+ if (INPUT(input).audioroute) {
+ /* The wm8775 module has the "2" route hardwired into
+ the initialization. Some boards may use different
+ routes for different inputs. HVR-1300 surely does */
+ if (core->board.audio_chip &&
+ core->board.audio_chip == V4L2_IDENT_WM8775) {
+ call_all(core, audio, s_routing,
+ INPUT(input).audioroute, 0, 0);
+ }
+ /* cx2388's C-ADC is connected to the tuner only.
+ When used with S-Video, that ADC is busy dealing with
+ chroma, so an external must be used for baseband audio */
+ if (INPUT(input).type != CX88_VMUX_TELEVISION &&
+ INPUT(input).type != CX88_VMUX_CABLE) {
+ /* "I2S ADC mode" */
+ core->tvaudio = WW_I2SADC;
+ cx88_set_tvaudio(core);
+ } else {
+ /* Normal mode */
+ cx_write(AUD_I2SCNTL, 0x0);
+ cx_clear(AUD_CTL, EN_I2SIN_ENABLE);
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(cx88_video_mux);
+
+/* ------------------------------------------------------------------ */
+
+static int start_video_dma(struct cx8800_dev *dev,
+ struct cx88_dmaqueue *q,
+ struct cx88_buffer *buf)
+{
+ struct cx88_core *core = dev->core;
+
+ /* setup fifo + format */
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH21],
+ buf->bpl, buf->risc.dma);
+ cx88_set_scale(core, buf->vb.width, buf->vb.height, buf->vb.field);
+ cx_write(MO_COLOR_CTRL, buf->fmt->cxformat | ColorFormatGamma);
+
+ /* reset counter */
+ cx_write(MO_VIDY_GPCNTRL,GP_COUNT_CONTROL_RESET);
+ q->count = 1;
+
+ /* enable irqs */
+ cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_VIDINT);
+
+ /* Enables corresponding bits at PCI_INT_STAT:
+ bits 0 to 4: video, audio, transport stream, VIP, Host
+ bit 7: timer
+ bits 8 and 9: DMA complete for: SRC, DST
+ bits 10 and 11: BERR signal asserted for RISC: RD, WR
+ bits 12 to 15: BERR signal asserted for: BRDG, SRC, DST, IPB
+ */
+ cx_set(MO_VID_INTMSK, 0x0f0011);
+
+ /* enable capture */
+ cx_set(VID_CAPTURE_CONTROL,0x06);
+
+ /* start dma */
+ cx_set(MO_DEV_CNTRL2, (1<<5));
+ cx_set(MO_VID_DMACNTRL, 0x11); /* Planar Y and packed FIFO and RISC enable */
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int stop_video_dma(struct cx8800_dev *dev)
+{
+ struct cx88_core *core = dev->core;
+
+ /* stop dma */
+ cx_clear(MO_VID_DMACNTRL, 0x11);
+
+ /* disable capture */
+ cx_clear(VID_CAPTURE_CONTROL,0x06);
+
+ /* disable irqs */
+ cx_clear(MO_PCI_INTMSK, PCI_INT_VIDINT);
+ cx_clear(MO_VID_INTMSK, 0x0f0011);
+ return 0;
+}
+#endif
+
+static int restart_video_queue(struct cx8800_dev *dev,
+ struct cx88_dmaqueue *q)
+{
+ struct cx88_core *core = dev->core;
+ struct cx88_buffer *buf, *prev;
+
+ if (!list_empty(&q->active)) {
+ buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
+ dprintk(2,"restart_queue [%p/%d]: restart dma\n",
+ buf, buf->vb.i);
+ start_video_dma(dev, q, buf);
+ list_for_each_entry(buf, &q->active, vb.queue)
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ return 0;
+ }
+
+ prev = NULL;
+ for (;;) {
+ if (list_empty(&q->queued))
+ return 0;
+ buf = list_entry(q->queued.next, struct cx88_buffer, vb.queue);
+ if (NULL == prev) {
+ list_move_tail(&buf->vb.queue, &q->active);
+ start_video_dma(dev, q, buf);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ dprintk(2,"[%p/%d] restart_queue - first active\n",
+ buf,buf->vb.i);
+
+ } else if (prev->vb.width == buf->vb.width &&
+ prev->vb.height == buf->vb.height &&
+ prev->fmt == buf->fmt) {
+ list_move_tail(&buf->vb.queue, &q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ dprintk(2,"[%p/%d] restart_queue - move to active\n",
+ buf,buf->vb.i);
+ } else {
+ return 0;
+ }
+ prev = buf;
+ }
+}
+
+/* ------------------------------------------------------------------ */
+
+static int
+buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+ struct cx8800_fh *fh = q->priv_data;
+
+ *size = fh->fmt->depth*fh->width*fh->height >> 3;
+ if (0 == *count)
+ *count = 32;
+ if (*size * *count > vid_limit * 1024 * 1024)
+ *count = (vid_limit * 1024 * 1024) / *size;
+ return 0;
+}
+
+static int
+buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct cx8800_fh *fh = q->priv_data;
+ struct cx8800_dev *dev = fh->dev;
+ struct cx88_core *core = dev->core;
+ struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb);
+ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+ int rc, init_buffer = 0;
+
+ BUG_ON(NULL == fh->fmt);
+ if (fh->width < 48 || fh->width > norm_maxw(core->tvnorm) ||
+ fh->height < 32 || fh->height > norm_maxh(core->tvnorm))
+ return -EINVAL;
+ buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3;
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+
+ if (buf->fmt != fh->fmt ||
+ buf->vb.width != fh->width ||
+ buf->vb.height != fh->height ||
+ buf->vb.field != field) {
+ buf->fmt = fh->fmt;
+ buf->vb.width = fh->width;
+ buf->vb.height = fh->height;
+ buf->vb.field = field;
+ init_buffer = 1;
+ }
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ init_buffer = 1;
+ if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL)))
+ goto fail;
+ }
+
+ if (init_buffer) {
+ buf->bpl = buf->vb.width * buf->fmt->depth >> 3;
+ switch (buf->vb.field) {
+ case V4L2_FIELD_TOP:
+ cx88_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist, 0, UNSET,
+ buf->bpl, 0, buf->vb.height);
+ break;
+ case V4L2_FIELD_BOTTOM:
+ cx88_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist, UNSET, 0,
+ buf->bpl, 0, buf->vb.height);
+ break;
+ case V4L2_FIELD_INTERLACED:
+ cx88_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist, 0, buf->bpl,
+ buf->bpl, buf->bpl,
+ buf->vb.height >> 1);
+ break;
+ case V4L2_FIELD_SEQ_TB:
+ cx88_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist,
+ 0, buf->bpl * (buf->vb.height >> 1),
+ buf->bpl, 0,
+ buf->vb.height >> 1);
+ break;
+ case V4L2_FIELD_SEQ_BT:
+ cx88_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist,
+ buf->bpl * (buf->vb.height >> 1), 0,
+ buf->bpl, 0,
+ buf->vb.height >> 1);
+ break;
+ default:
+ BUG();
+ }
+ }
+ dprintk(2,"[%p/%d] buffer_prepare - %dx%d %dbpp \"%s\" - dma=0x%08lx\n",
+ buf, buf->vb.i,
+ fh->width, fh->height, fh->fmt->depth, fh->fmt->name,
+ (unsigned long)buf->risc.dma);
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+ fail:
+ cx88_free_buffer(q,buf);
+ return rc;
+}
+
+static void
+buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb);
+ struct cx88_buffer *prev;
+ struct cx8800_fh *fh = vq->priv_data;
+ struct cx8800_dev *dev = fh->dev;
+ struct cx88_core *core = dev->core;
+ struct cx88_dmaqueue *q = &dev->vidq;
+
+ /* add jump to stopper */
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
+ buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
+
+ if (!list_empty(&q->queued)) {
+ list_add_tail(&buf->vb.queue,&q->queued);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ dprintk(2,"[%p/%d] buffer_queue - append to queued\n",
+ buf, buf->vb.i);
+
+ } else if (list_empty(&q->active)) {
+ list_add_tail(&buf->vb.queue,&q->active);
+ start_video_dma(dev, q, buf);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ dprintk(2,"[%p/%d] buffer_queue - first active\n",
+ buf, buf->vb.i);
+
+ } else {
+ prev = list_entry(q->active.prev, struct cx88_buffer, vb.queue);
+ if (prev->vb.width == buf->vb.width &&
+ prev->vb.height == buf->vb.height &&
+ prev->fmt == buf->fmt) {
+ list_add_tail(&buf->vb.queue,&q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ dprintk(2,"[%p/%d] buffer_queue - append to active\n",
+ buf, buf->vb.i);
+
+ } else {
+ list_add_tail(&buf->vb.queue,&q->queued);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ dprintk(2,"[%p/%d] buffer_queue - first queued\n",
+ buf, buf->vb.i);
+ }
+ }
+}
+
+static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb);
+
+ cx88_free_buffer(q,buf);
+}
+
+static const struct videobuf_queue_ops cx8800_video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/* ------------------------------------------------------------------ */
+
+
+/* ------------------------------------------------------------------ */
+
+static struct videobuf_queue* get_queue(struct cx8800_fh *fh)
+{
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return &fh->vidq;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ return &fh->vbiq;
+ default:
+ BUG();
+ return NULL;
+ }
+}
+
+static int get_ressource(struct cx8800_fh *fh)
+{
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return RESOURCE_VIDEO;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ return RESOURCE_VBI;
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+static int video_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct cx8800_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
+ struct cx8800_fh *fh;
+ enum v4l2_buf_type type = 0;
+ int radio = 0;
+
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ break;
+ case VFL_TYPE_VBI:
+ type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ break;
+ case VFL_TYPE_RADIO:
+ radio = 1;
+ break;
+ }
+
+ dprintk(1, "open dev=%s radio=%d type=%s\n",
+ video_device_node_name(vdev), radio, v4l2_type_names[type]);
+
+ /* allocate + initialize per filehandle data */
+ fh = kzalloc(sizeof(*fh),GFP_KERNEL);
+ if (unlikely(!fh))
+ return -ENOMEM;
+
+ file->private_data = fh;
+ fh->dev = dev;
+ fh->radio = radio;
+ fh->type = type;
+ fh->width = 320;
+ fh->height = 240;
+ fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
+
+ mutex_lock(&core->lock);
+
+ videobuf_queue_sg_init(&fh->vidq, &cx8800_video_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct cx88_buffer),
+ fh, NULL);
+ videobuf_queue_sg_init(&fh->vbiq, &cx8800_vbi_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_FIELD_SEQ_TB,
+ sizeof(struct cx88_buffer),
+ fh, NULL);
+
+ if (fh->radio) {
+ dprintk(1,"video_open: setting radio device\n");
+ cx_write(MO_GP3_IO, core->board.radio.gpio3);
+ cx_write(MO_GP0_IO, core->board.radio.gpio0);
+ cx_write(MO_GP1_IO, core->board.radio.gpio1);
+ cx_write(MO_GP2_IO, core->board.radio.gpio2);
+ if (core->board.radio.audioroute) {
+ if(core->board.audio_chip &&
+ core->board.audio_chip == V4L2_IDENT_WM8775) {
+ call_all(core, audio, s_routing,
+ core->board.radio.audioroute, 0, 0);
+ }
+ /* "I2S ADC mode" */
+ core->tvaudio = WW_I2SADC;
+ cx88_set_tvaudio(core);
+ } else {
+ /* FM Mode */
+ core->tvaudio = WW_FM;
+ cx88_set_tvaudio(core);
+ cx88_set_stereo(core,V4L2_TUNER_MODE_STEREO,1);
+ }
+ call_all(core, tuner, s_radio);
+ }
+
+ core->users++;
+ mutex_unlock(&core->lock);
+
+ return 0;
+}
+
+static ssize_t
+video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+{
+ struct cx8800_fh *fh = file->private_data;
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (res_locked(fh->dev,RESOURCE_VIDEO))
+ return -EBUSY;
+ return videobuf_read_one(&fh->vidq, data, count, ppos,
+ file->f_flags & O_NONBLOCK);
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ if (!res_get(fh->dev,fh,RESOURCE_VBI))
+ return -EBUSY;
+ return videobuf_read_stream(&fh->vbiq, data, count, ppos, 1,
+ file->f_flags & O_NONBLOCK);
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+static unsigned int
+video_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct cx8800_fh *fh = file->private_data;
+ struct cx88_buffer *buf;
+ unsigned int rc = POLLERR;
+
+ if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
+ if (!res_get(fh->dev,fh,RESOURCE_VBI))
+ return POLLERR;
+ return videobuf_poll_stream(file, &fh->vbiq, wait);
+ }
+
+ mutex_lock(&fh->vidq.vb_lock);
+ if (res_check(fh,RESOURCE_VIDEO)) {
+ /* streaming capture */
+ if (list_empty(&fh->vidq.stream))
+ goto done;
+ buf = list_entry(fh->vidq.stream.next,struct cx88_buffer,vb.stream);
+ } else {
+ /* read() capture */
+ buf = (struct cx88_buffer*)fh->vidq.read_buf;
+ if (NULL == buf)
+ goto done;
+ }
+ poll_wait(file, &buf->vb.done, wait);
+ if (buf->vb.state == VIDEOBUF_DONE ||
+ buf->vb.state == VIDEOBUF_ERROR)
+ rc = POLLIN|POLLRDNORM;
+ else
+ rc = 0;
+done:
+ mutex_unlock(&fh->vidq.vb_lock);
+ return rc;
+}
+
+static int video_release(struct file *file)
+{
+ struct cx8800_fh *fh = file->private_data;
+ struct cx8800_dev *dev = fh->dev;
+
+ /* turn off overlay */
+ if (res_check(fh, RESOURCE_OVERLAY)) {
+ /* FIXME */
+ res_free(dev,fh,RESOURCE_OVERLAY);
+ }
+
+ /* stop video capture */
+ if (res_check(fh, RESOURCE_VIDEO)) {
+ videobuf_queue_cancel(&fh->vidq);
+ res_free(dev,fh,RESOURCE_VIDEO);
+ }
+ if (fh->vidq.read_buf) {
+ buffer_release(&fh->vidq,fh->vidq.read_buf);
+ kfree(fh->vidq.read_buf);
+ }
+
+ /* stop vbi capture */
+ if (res_check(fh, RESOURCE_VBI)) {
+ videobuf_stop(&fh->vbiq);
+ res_free(dev,fh,RESOURCE_VBI);
+ }
+
+ videobuf_mmap_free(&fh->vidq);
+ videobuf_mmap_free(&fh->vbiq);
+
+ mutex_lock(&dev->core->lock);
+ file->private_data = NULL;
+ kfree(fh);
+
+ dev->core->users--;
+ if (!dev->core->users)
+ call_all(dev->core, core, s_power, 0);
+ mutex_unlock(&dev->core->lock);
+
+ return 0;
+}
+
+static int
+video_mmap(struct file *file, struct vm_area_struct * vma)
+{
+ struct cx8800_fh *fh = file->private_data;
+
+ return videobuf_mmap_mapper(get_queue(fh), vma);
+}
+
+/* ------------------------------------------------------------------ */
+/* VIDEO CTRL IOCTLS */
+
+int cx88_get_control (struct cx88_core *core, struct v4l2_control *ctl)
+{
+ const struct cx88_ctrl *c = NULL;
+ u32 value;
+ int i;
+
+ for (i = 0; i < CX8800_CTLS; i++)
+ if (cx8800_ctls[i].v.id == ctl->id)
+ c = &cx8800_ctls[i];
+ if (unlikely(NULL == c))
+ return -EINVAL;
+
+ value = c->sreg ? cx_sread(c->sreg) : cx_read(c->reg);
+ switch (ctl->id) {
+ case V4L2_CID_AUDIO_BALANCE:
+ ctl->value = ((value & 0x7f) < 0x40) ? ((value & 0x7f) + 0x40)
+ : (0x7f - (value & 0x7f));
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ ctl->value = 0x3f - (value & 0x3f);
+ break;
+ case V4L2_CID_SHARPNESS:
+ ctl->value = ((value & 0x0200) ? (((value & 0x0180) >> 7) + 1)
+ : 0);
+ break;
+ default:
+ ctl->value = ((value + (c->off << c->shift)) & c->mask) >> c->shift;
+ break;
+ }
+ dprintk(1,"get_control id=0x%X(%s) ctrl=0x%02x, reg=0x%02x val=0x%02x (mask 0x%02x)%s\n",
+ ctl->id, c->v.name, ctl->value, c->reg,
+ value,c->mask, c->sreg ? " [shadowed]" : "");
+ return 0;
+}
+EXPORT_SYMBOL(cx88_get_control);
+
+int cx88_set_control(struct cx88_core *core, struct v4l2_control *ctl)
+{
+ const struct cx88_ctrl *c = NULL;
+ u32 value,mask;
+ int i;
+
+ for (i = 0; i < CX8800_CTLS; i++) {
+ if (cx8800_ctls[i].v.id == ctl->id) {
+ c = &cx8800_ctls[i];
+ }
+ }
+ if (unlikely(NULL == c))
+ return -EINVAL;
+
+ if (ctl->value < c->v.minimum)
+ ctl->value = c->v.minimum;
+ if (ctl->value > c->v.maximum)
+ ctl->value = c->v.maximum;
+
+ /* Pass changes onto any WM8775 */
+ if (core->board.audio_chip == V4L2_IDENT_WM8775) {
+ struct v4l2_control client_ctl;
+ memset(&client_ctl, 0, sizeof(client_ctl));
+ client_ctl.id = ctl->id;
+
+ switch (ctl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ client_ctl.value = ctl->value;
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ client_ctl.value = (ctl->value) ?
+ (0x90 + ctl->value) << 8 : 0;
+ break;
+ case V4L2_CID_AUDIO_BALANCE:
+ client_ctl.value = ctl->value << 9;
+ break;
+ default:
+ client_ctl.id = 0;
+ break;
+ }
+ if (client_ctl.id)
+ call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
+ }
+
+ mask=c->mask;
+ switch (ctl->id) {
+ case V4L2_CID_AUDIO_BALANCE:
+ value = (ctl->value < 0x40) ? (0x7f - ctl->value) : (ctl->value - 0x40);
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ value = 0x3f - (ctl->value & 0x3f);
+ break;
+ case V4L2_CID_SATURATION:
+ /* special v_sat handling */
+
+ value = ((ctl->value - c->off) << c->shift) & c->mask;
+
+ if (core->tvnorm & V4L2_STD_SECAM) {
+ /* For SECAM, both U and V sat should be equal */
+ value=value<<8|value;
+ } else {
+ /* Keeps U Saturation proportional to V Sat */
+ value=(value*0x5a)/0x7f<<8|value;
+ }
+ mask=0xffff;
+ break;
+ case V4L2_CID_SHARPNESS:
+ /* 0b000, 0b100, 0b101, 0b110, or 0b111 */
+ value = (ctl->value < 1 ? 0 : ((ctl->value + 3) << 7));
+ /* needs to be set for both fields */
+ cx_andor(MO_FILTER_EVEN, mask, value);
+ break;
+ case V4L2_CID_CHROMA_AGC:
+ /* Do not allow chroma AGC to be enabled for SECAM */
+ value = ((ctl->value - c->off) << c->shift) & c->mask;
+ if (core->tvnorm & V4L2_STD_SECAM && value)
+ return -EINVAL;
+ break;
+ default:
+ value = ((ctl->value - c->off) << c->shift) & c->mask;
+ break;
+ }
+ dprintk(1,"set_control id=0x%X(%s) ctrl=0x%02x, reg=0x%02x val=0x%02x (mask 0x%02x)%s\n",
+ ctl->id, c->v.name, ctl->value, c->reg, value,
+ mask, c->sreg ? " [shadowed]" : "");
+ if (c->sreg) {
+ cx_sandor(c->sreg, c->reg, mask, value);
+ } else {
+ cx_andor(c->reg, mask, value);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(cx88_set_control);
+
+static void init_controls(struct cx88_core *core)
+{
+ struct v4l2_control ctrl;
+ int i;
+
+ for (i = 0; i < CX8800_CTLS; i++) {
+ ctrl.id=cx8800_ctls[i].v.id;
+ ctrl.value=cx8800_ctls[i].v.default_value;
+
+ cx88_set_control(core, &ctrl);
+ }
+}
+
+/* ------------------------------------------------------------------ */
+/* VIDEO IOCTLS */
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx8800_fh *fh = priv;
+
+ f->fmt.pix.width = fh->width;
+ f->fmt.pix.height = fh->height;
+ f->fmt.pix.field = fh->vidq.field;
+ f->fmt.pix.pixelformat = fh->fmt->fourcc;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fh->fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+ const struct cx8800_fmt *fmt;
+ enum v4l2_field field;
+ unsigned int maxw, maxh;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+
+ field = f->fmt.pix.field;
+ maxw = norm_maxw(core->tvnorm);
+ maxh = norm_maxh(core->tvnorm);
+
+ if (V4L2_FIELD_ANY == field) {
+ field = (f->fmt.pix.height > maxh/2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_BOTTOM;
+ }
+
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ f->fmt.pix.field = field;
+ v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2,
+ &f->fmt.pix.height, 32, maxh, 0, 0);
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx8800_fh *fh = priv;
+ int err = vidioc_try_fmt_vid_cap (file,priv,f);
+
+ if (0 != err)
+ return err;
+ fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ fh->width = f->fmt.pix.width;
+ fh->height = f->fmt.pix.height;
+ fh->vidq.field = f->fmt.pix.field;
+ return 0;
+}
+
+static int vidioc_querycap (struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct cx8800_dev *dev = ((struct cx8800_fh *)priv)->dev;
+ struct cx88_core *core = dev->core;
+
+ strcpy(cap->driver, "cx8800");
+ strlcpy(cap->card, core->board.name, sizeof(cap->card));
+ sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci));
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING |
+ V4L2_CAP_VBI_CAPTURE;
+ if (UNSET != core->board.tuner_type)
+ cap->capabilities |= V4L2_CAP_TUNER;
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (unlikely(f->index >= ARRAY_SIZE(formats)))
+ return -EINVAL;
+
+ strlcpy(f->description,formats[f->index].name,sizeof(f->description));
+ f->pixelformat = formats[f->index].fourcc;
+
+ return 0;
+}
+
+static int vidioc_reqbufs (struct file *file, void *priv, struct v4l2_requestbuffers *p)
+{
+ struct cx8800_fh *fh = priv;
+ return (videobuf_reqbufs(get_queue(fh), p));
+}
+
+static int vidioc_querybuf (struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct cx8800_fh *fh = priv;
+ return (videobuf_querybuf(get_queue(fh), p));
+}
+
+static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct cx8800_fh *fh = priv;
+ return (videobuf_qbuf(get_queue(fh), p));
+}
+
+static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct cx8800_fh *fh = priv;
+ return (videobuf_dqbuf(get_queue(fh), p,
+ file->f_flags & O_NONBLOCK));
+}
+
+static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct cx8800_fh *fh = priv;
+ struct cx8800_dev *dev = fh->dev;
+
+ /* We should remember that this driver also supports teletext, */
+ /* so we have to test if the v4l2_buf_type is VBI capture data. */
+ if (unlikely((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)))
+ return -EINVAL;
+
+ if (unlikely(i != fh->type))
+ return -EINVAL;
+
+ if (unlikely(!res_get(dev,fh,get_ressource(fh))))
+ return -EBUSY;
+ return videobuf_streamon(get_queue(fh));
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct cx8800_fh *fh = priv;
+ struct cx8800_dev *dev = fh->dev;
+ int err, res;
+
+ if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE))
+ return -EINVAL;
+
+ if (i != fh->type)
+ return -EINVAL;
+
+ res = get_ressource(fh);
+ err = videobuf_streamoff(get_queue(fh));
+ if (err < 0)
+ return err;
+ res_free(dev,fh,res);
+ return 0;
+}
+
+static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *tvnorms)
+{
+ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+
+ mutex_lock(&core->lock);
+ cx88_set_tvnorm(core,*tvnorms);
+ mutex_unlock(&core->lock);
+
+ return 0;
+}
+
+/* only one input in this sample driver */
+int cx88_enum_input (struct cx88_core *core,struct v4l2_input *i)
+{
+ static const char * const iname[] = {
+ [ CX88_VMUX_COMPOSITE1 ] = "Composite1",
+ [ CX88_VMUX_COMPOSITE2 ] = "Composite2",
+ [ CX88_VMUX_COMPOSITE3 ] = "Composite3",
+ [ CX88_VMUX_COMPOSITE4 ] = "Composite4",
+ [ CX88_VMUX_SVIDEO ] = "S-Video",
+ [ CX88_VMUX_TELEVISION ] = "Television",
+ [ CX88_VMUX_CABLE ] = "Cable TV",
+ [ CX88_VMUX_DVB ] = "DVB",
+ [ CX88_VMUX_DEBUG ] = "for debug only",
+ };
+ unsigned int n = i->index;
+
+ if (n >= 4)
+ return -EINVAL;
+ if (0 == INPUT(n).type)
+ return -EINVAL;
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ strcpy(i->name,iname[INPUT(n).type]);
+ if ((CX88_VMUX_TELEVISION == INPUT(n).type) ||
+ (CX88_VMUX_CABLE == INPUT(n).type)) {
+ i->type = V4L2_INPUT_TYPE_TUNER;
+ i->std = CX88_NORMS;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(cx88_enum_input);
+
+static int vidioc_enum_input (struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+ return cx88_enum_input (core,i);
+}
+
+static int vidioc_g_input (struct file *file, void *priv, unsigned int *i)
+{
+ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+
+ *i = core->input;
+ return 0;
+}
+
+static int vidioc_s_input (struct file *file, void *priv, unsigned int i)
+{
+ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+
+ if (i >= 4)
+ return -EINVAL;
+
+ mutex_lock(&core->lock);
+ cx88_newstation(core);
+ cx88_video_mux(core,i);
+ mutex_unlock(&core->lock);
+ return 0;
+}
+
+
+
+static int vidioc_queryctrl (struct file *file, void *priv,
+ struct v4l2_queryctrl *qctrl)
+{
+ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+
+ qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
+ if (unlikely(qctrl->id == 0))
+ return -EINVAL;
+ return cx8800_ctrl_query(core, qctrl);
+}
+
+static int vidioc_g_ctrl (struct file *file, void *priv,
+ struct v4l2_control *ctl)
+{
+ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+ return
+ cx88_get_control(core,ctl);
+}
+
+static int vidioc_s_ctrl (struct file *file, void *priv,
+ struct v4l2_control *ctl)
+{
+ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+ return
+ cx88_set_control(core,ctl);
+}
+
+static int vidioc_g_tuner (struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+ u32 reg;
+
+ if (unlikely(UNSET == core->board.tuner_type))
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+
+ strcpy(t->name, "Television");
+ t->type = V4L2_TUNER_ANALOG_TV;
+ t->capability = V4L2_TUNER_CAP_NORM;
+ t->rangehigh = 0xffffffffUL;
+
+ cx88_get_stereo(core ,t);
+ reg = cx_read(MO_DEVICE_STATUS);
+ t->signal = (reg & (1<<5)) ? 0xffff : 0x0000;
+ return 0;
+}
+
+static int vidioc_s_tuner (struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+
+ if (UNSET == core->board.tuner_type)
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+
+ cx88_set_stereo(core, t->audmode, 1);
+ return 0;
+}
+
+static int vidioc_g_frequency (struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct cx8800_fh *fh = priv;
+ struct cx88_core *core = fh->dev->core;
+
+ if (unlikely(UNSET == core->board.tuner_type))
+ return -EINVAL;
+
+ /* f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; */
+ f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ f->frequency = core->freq;
+
+ call_all(core, tuner, g_frequency, f);
+
+ return 0;
+}
+
+int cx88_set_freq (struct cx88_core *core,
+ struct v4l2_frequency *f)
+{
+ if (unlikely(UNSET == core->board.tuner_type))
+ return -EINVAL;
+ if (unlikely(f->tuner != 0))
+ return -EINVAL;
+
+ mutex_lock(&core->lock);
+ core->freq = f->frequency;
+ cx88_newstation(core);
+ call_all(core, tuner, s_frequency, f);
+
+ /* When changing channels it is required to reset TVAUDIO */
+ msleep (10);
+ cx88_set_tvaudio(core);
+
+ mutex_unlock(&core->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(cx88_set_freq);
+
+static int vidioc_s_frequency (struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct cx8800_fh *fh = priv;
+ struct cx88_core *core = fh->dev->core;
+
+ if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV))
+ return -EINVAL;
+ if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO))
+ return -EINVAL;
+
+ return
+ cx88_set_freq (core,f);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vidioc_g_register (struct file *file, void *fh,
+ struct v4l2_dbg_register *reg)
+{
+ struct cx88_core *core = ((struct cx8800_fh*)fh)->dev->core;
+
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+ /* cx2388x has a 24-bit register space */
+ reg->val = cx_read(reg->reg & 0xffffff);
+ reg->size = 4;
+ return 0;
+}
+
+static int vidioc_s_register (struct file *file, void *fh,
+ struct v4l2_dbg_register *reg)
+{
+ struct cx88_core *core = ((struct cx8800_fh*)fh)->dev->core;
+
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+ cx_write(reg->reg & 0xffffff, reg->val);
+ return 0;
+}
+#endif
+
+/* ----------------------------------------------------------- */
+/* RADIO ESPECIFIC IOCTLS */
+/* ----------------------------------------------------------- */
+
+static int radio_querycap (struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct cx8800_dev *dev = ((struct cx8800_fh *)priv)->dev;
+ struct cx88_core *core = dev->core;
+
+ strcpy(cap->driver, "cx8800");
+ strlcpy(cap->card, core->board.name, sizeof(cap->card));
+ sprintf(cap->bus_info,"PCI:%s", pci_name(dev->pci));
+ cap->capabilities = V4L2_CAP_TUNER;
+ return 0;
+}
+
+static int radio_g_tuner (struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+
+ if (unlikely(t->index > 0))
+ return -EINVAL;
+
+ strcpy(t->name, "Radio");
+ t->type = V4L2_TUNER_RADIO;
+
+ call_all(core, tuner, g_tuner, t);
+ return 0;
+}
+
+static int radio_enum_input (struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
+ strcpy(i->name,"Radio");
+ i->type = V4L2_INPUT_TYPE_TUNER;
+
+ return 0;
+}
+
+static int radio_g_audio (struct file *file, void *priv, struct v4l2_audio *a)
+{
+ if (unlikely(a->index))
+ return -EINVAL;
+
+ strcpy(a->name,"Radio");
+ return 0;
+}
+
+/* FIXME: Should add a standard for radio */
+
+static int radio_s_tuner (struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ call_all(core, tuner, s_tuner, t);
+
+ return 0;
+}
+
+static int radio_s_audio (struct file *file, void *fh,
+ struct v4l2_audio *a)
+{
+ return 0;
+}
+
+static int radio_s_input (struct file *file, void *fh, unsigned int i)
+{
+ return 0;
+}
+
+static int radio_queryctrl (struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ int i;
+
+ if (c->id < V4L2_CID_BASE ||
+ c->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+ if (c->id == V4L2_CID_AUDIO_MUTE ||
+ c->id == V4L2_CID_AUDIO_VOLUME ||
+ c->id == V4L2_CID_AUDIO_BALANCE) {
+ for (i = 0; i < CX8800_CTLS; i++) {
+ if (cx8800_ctls[i].v.id == c->id)
+ break;
+ }
+ if (i == CX8800_CTLS)
+ return -EINVAL;
+ *c = cx8800_ctls[i].v;
+ } else
+ *c = no_ctl;
+ return 0;
+}
+
+/* ----------------------------------------------------------- */
+
+static void cx8800_vid_timeout(unsigned long data)
+{
+ struct cx8800_dev *dev = (struct cx8800_dev*)data;
+ struct cx88_core *core = dev->core;
+ struct cx88_dmaqueue *q = &dev->vidq;
+ struct cx88_buffer *buf;
+ unsigned long flags;
+
+ cx88_sram_channel_dump(core, &cx88_sram_channels[SRAM_CH21]);
+
+ cx_clear(MO_VID_DMACNTRL, 0x11);
+ cx_clear(VID_CAPTURE_CONTROL, 0x06);
+
+ spin_lock_irqsave(&dev->slock,flags);
+ while (!list_empty(&q->active)) {
+ buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
+ list_del(&buf->vb.queue);
+ buf->vb.state = VIDEOBUF_ERROR;
+ wake_up(&buf->vb.done);
+ printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", core->name,
+ buf, buf->vb.i, (unsigned long)buf->risc.dma);
+ }
+ restart_video_queue(dev,q);
+ spin_unlock_irqrestore(&dev->slock,flags);
+}
+
+static const char *cx88_vid_irqs[32] = {
+ "y_risci1", "u_risci1", "v_risci1", "vbi_risc1",
+ "y_risci2", "u_risci2", "v_risci2", "vbi_risc2",
+ "y_oflow", "u_oflow", "v_oflow", "vbi_oflow",
+ "y_sync", "u_sync", "v_sync", "vbi_sync",
+ "opc_err", "par_err", "rip_err", "pci_abort",
+};
+
+static void cx8800_vid_irq(struct cx8800_dev *dev)
+{
+ struct cx88_core *core = dev->core;
+ u32 status, mask, count;
+
+ status = cx_read(MO_VID_INTSTAT);
+ mask = cx_read(MO_VID_INTMSK);
+ if (0 == (status & mask))
+ return;
+ cx_write(MO_VID_INTSTAT, status);
+ if (irq_debug || (status & mask & ~0xff))
+ cx88_print_irqbits(core->name, "irq vid",
+ cx88_vid_irqs, ARRAY_SIZE(cx88_vid_irqs),
+ status, mask);
+
+ /* risc op code error */
+ if (status & (1 << 16)) {
+ printk(KERN_WARNING "%s/0: video risc op code error\n",core->name);
+ cx_clear(MO_VID_DMACNTRL, 0x11);
+ cx_clear(VID_CAPTURE_CONTROL, 0x06);
+ cx88_sram_channel_dump(core, &cx88_sram_channels[SRAM_CH21]);
+ }
+
+ /* risc1 y */
+ if (status & 0x01) {
+ spin_lock(&dev->slock);
+ count = cx_read(MO_VIDY_GPCNT);
+ cx88_wakeup(core, &dev->vidq, count);
+ spin_unlock(&dev->slock);
+ }
+
+ /* risc1 vbi */
+ if (status & 0x08) {
+ spin_lock(&dev->slock);
+ count = cx_read(MO_VBI_GPCNT);
+ cx88_wakeup(core, &dev->vbiq, count);
+ spin_unlock(&dev->slock);
+ }
+
+ /* risc2 y */
+ if (status & 0x10) {
+ dprintk(2,"stopper video\n");
+ spin_lock(&dev->slock);
+ restart_video_queue(dev,&dev->vidq);
+ spin_unlock(&dev->slock);
+ }
+
+ /* risc2 vbi */
+ if (status & 0x80) {
+ dprintk(2,"stopper vbi\n");
+ spin_lock(&dev->slock);
+ cx8800_restart_vbi_queue(dev,&dev->vbiq);
+ spin_unlock(&dev->slock);
+ }
+}
+
+static irqreturn_t cx8800_irq(int irq, void *dev_id)
+{
+ struct cx8800_dev *dev = dev_id;
+ struct cx88_core *core = dev->core;
+ u32 status;
+ int loop, handled = 0;
+
+ for (loop = 0; loop < 10; loop++) {
+ status = cx_read(MO_PCI_INTSTAT) &
+ (core->pci_irqmask | PCI_INT_VIDINT);
+ if (0 == status)
+ goto out;
+ cx_write(MO_PCI_INTSTAT, status);
+ handled = 1;
+
+ if (status & core->pci_irqmask)
+ cx88_core_irq(core,status);
+ if (status & PCI_INT_VIDINT)
+ cx8800_vid_irq(dev);
+ };
+ if (10 == loop) {
+ printk(KERN_WARNING "%s/0: irq loop -- clearing mask\n",
+ core->name);
+ cx_write(MO_PCI_INTMSK,0);
+ }
+
+ out:
+ return IRQ_RETVAL(handled);
+}
+
+/* ----------------------------------------------------------- */
+/* exported stuff */
+
+static const struct v4l2_file_operations video_fops =
+{
+ .owner = THIS_MODULE,
+ .open = video_open,
+ .release = video_release,
+ .read = video_read,
+ .poll = video_poll,
+ .mmap = video_mmap,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops video_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_g_fmt_vbi_cap = cx8800_vbi_fmt,
+ .vidioc_try_fmt_vbi_cap = cx8800_vbi_fmt,
+ .vidioc_s_fmt_vbi_cap = cx8800_vbi_fmt,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+#endif
+};
+
+static struct video_device cx8800_vbi_template;
+
+static const struct video_device cx8800_video_template = {
+ .name = "cx8800-video",
+ .fops = &video_fops,
+ .ioctl_ops = &video_ioctl_ops,
+ .tvnorms = CX88_NORMS,
+ .current_norm = V4L2_STD_NTSC_M,
+};
+
+static const struct v4l2_file_operations radio_fops =
+{
+ .owner = THIS_MODULE,
+ .open = video_open,
+ .release = video_release,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops radio_ioctl_ops = {
+ .vidioc_querycap = radio_querycap,
+ .vidioc_g_tuner = radio_g_tuner,
+ .vidioc_enum_input = radio_enum_input,
+ .vidioc_g_audio = radio_g_audio,
+ .vidioc_s_tuner = radio_s_tuner,
+ .vidioc_s_audio = radio_s_audio,
+ .vidioc_s_input = radio_s_input,
+ .vidioc_queryctrl = radio_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+#endif
+};
+
+static const struct video_device cx8800_radio_template = {
+ .name = "cx8800-radio",
+ .fops = &radio_fops,
+ .ioctl_ops = &radio_ioctl_ops,
+};
+
+/* ----------------------------------------------------------- */
+
+static void cx8800_unregister_video(struct cx8800_dev *dev)
+{
+ if (dev->radio_dev) {
+ if (video_is_registered(dev->radio_dev))
+ video_unregister_device(dev->radio_dev);
+ else
+ video_device_release(dev->radio_dev);
+ dev->radio_dev = NULL;
+ }
+ if (dev->vbi_dev) {
+ if (video_is_registered(dev->vbi_dev))
+ video_unregister_device(dev->vbi_dev);
+ else
+ video_device_release(dev->vbi_dev);
+ dev->vbi_dev = NULL;
+ }
+ if (dev->video_dev) {
+ if (video_is_registered(dev->video_dev))
+ video_unregister_device(dev->video_dev);
+ else
+ video_device_release(dev->video_dev);
+ dev->video_dev = NULL;
+ }
+}
+
+static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_id)
+{
+ struct cx8800_dev *dev;
+ struct cx88_core *core;
+
+ int err;
+
+ dev = kzalloc(sizeof(*dev),GFP_KERNEL);
+ if (NULL == dev)
+ return -ENOMEM;
+
+ /* pci init */
+ dev->pci = pci_dev;
+ if (pci_enable_device(pci_dev)) {
+ err = -EIO;
+ goto fail_free;
+ }
+ core = cx88_core_get(dev->pci);
+ if (NULL == core) {
+ err = -EINVAL;
+ goto fail_free;
+ }
+ dev->core = core;
+
+ /* print pci info */
+ dev->pci_rev = pci_dev->revision;
+ pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat);
+ printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, "
+ "latency: %d, mmio: 0x%llx\n", core->name,
+ pci_name(pci_dev), dev->pci_rev, pci_dev->irq,
+ dev->pci_lat,(unsigned long long)pci_resource_start(pci_dev,0));
+
+ pci_set_master(pci_dev);
+ if (!pci_dma_supported(pci_dev,DMA_BIT_MASK(32))) {
+ printk("%s/0: Oops: no 32bit PCI DMA ???\n",core->name);
+ err = -EIO;
+ goto fail_core;
+ }
+
+ /* Initialize VBI template */
+ memcpy( &cx8800_vbi_template, &cx8800_video_template,
+ sizeof(cx8800_vbi_template) );
+ strcpy(cx8800_vbi_template.name,"cx8800-vbi");
+
+ /* initialize driver struct */
+ spin_lock_init(&dev->slock);
+ core->tvnorm = cx8800_video_template.current_norm;
+
+ /* init video dma queues */
+ INIT_LIST_HEAD(&dev->vidq.active);
+ INIT_LIST_HEAD(&dev->vidq.queued);
+ dev->vidq.timeout.function = cx8800_vid_timeout;
+ dev->vidq.timeout.data = (unsigned long)dev;
+ init_timer(&dev->vidq.timeout);
+ cx88_risc_stopper(dev->pci,&dev->vidq.stopper,
+ MO_VID_DMACNTRL,0x11,0x00);
+
+ /* init vbi dma queues */
+ INIT_LIST_HEAD(&dev->vbiq.active);
+ INIT_LIST_HEAD(&dev->vbiq.queued);
+ dev->vbiq.timeout.function = cx8800_vbi_timeout;
+ dev->vbiq.timeout.data = (unsigned long)dev;
+ init_timer(&dev->vbiq.timeout);
+ cx88_risc_stopper(dev->pci,&dev->vbiq.stopper,
+ MO_VID_DMACNTRL,0x88,0x00);
+
+ /* get irq */
+ err = request_irq(pci_dev->irq, cx8800_irq,
+ IRQF_SHARED | IRQF_DISABLED, core->name, dev);
+ if (err < 0) {
+ printk(KERN_ERR "%s/0: can't get IRQ %d\n",
+ core->name,pci_dev->irq);
+ goto fail_core;
+ }
+ cx_set(MO_PCI_INTMSK, core->pci_irqmask);
+
+ /* load and configure helper modules */
+
+ if (core->board.audio_chip == V4L2_IDENT_WM8775) {
+ struct i2c_board_info wm8775_info = {
+ .type = "wm8775",
+ .addr = 0x36 >> 1,
+ .platform_data = &core->wm8775_data,
+ };
+ struct v4l2_subdev *sd;
+
+ if (core->boardnr == CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1)
+ core->wm8775_data.is_nova_s = true;
+ else
+ core->wm8775_data.is_nova_s = false;
+
+ sd = v4l2_i2c_new_subdev_board(&core->v4l2_dev, &core->i2c_adap,
+ &wm8775_info, NULL);
+ if (sd != NULL)
+ sd->grp_id = WM8775_GID;
+ }
+
+ if (core->board.audio_chip == V4L2_IDENT_TVAUDIO) {
+ /* This probes for a tda9874 as is used on some
+ Pixelview Ultra boards. */
+ v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap,
+ "tvaudio", 0, I2C_ADDRS(0xb0 >> 1));
+ }
+
+ switch (core->boardnr) {
+ case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD:
+ case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD: {
+ static const struct i2c_board_info rtc_info = {
+ I2C_BOARD_INFO("isl1208", 0x6f)
+ };
+
+ request_module("rtc-isl1208");
+ core->i2c_rtc = i2c_new_device(&core->i2c_adap, &rtc_info);
+ }
+ /* break intentionally omitted */
+ case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
+ request_module("ir-kbd-i2c");
+ }
+
+ /* Sets device info at pci_dev */
+ pci_set_drvdata(pci_dev, dev);
+
+ /* initial device configuration */
+ mutex_lock(&core->lock);
+ cx88_set_tvnorm(core, core->tvnorm);
+ init_controls(core);
+ cx88_video_mux(core, 0);
+
+ /* register v4l devices */
+ dev->video_dev = cx88_vdev_init(core,dev->pci,
+ &cx8800_video_template,"video");
+ video_set_drvdata(dev->video_dev, dev);
+ err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER,
+ video_nr[core->nr]);
+ if (err < 0) {
+ printk(KERN_ERR "%s/0: can't register video device\n",
+ core->name);
+ goto fail_unreg;
+ }
+ printk(KERN_INFO "%s/0: registered device %s [v4l2]\n",
+ core->name, video_device_node_name(dev->video_dev));
+
+ dev->vbi_dev = cx88_vdev_init(core,dev->pci,&cx8800_vbi_template,"vbi");
+ video_set_drvdata(dev->vbi_dev, dev);
+ err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI,
+ vbi_nr[core->nr]);
+ if (err < 0) {
+ printk(KERN_ERR "%s/0: can't register vbi device\n",
+ core->name);
+ goto fail_unreg;
+ }
+ printk(KERN_INFO "%s/0: registered device %s\n",
+ core->name, video_device_node_name(dev->vbi_dev));
+
+ if (core->board.radio.type == CX88_RADIO) {
+ dev->radio_dev = cx88_vdev_init(core,dev->pci,
+ &cx8800_radio_template,"radio");
+ video_set_drvdata(dev->radio_dev, dev);
+ err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO,
+ radio_nr[core->nr]);
+ if (err < 0) {
+ printk(KERN_ERR "%s/0: can't register radio device\n",
+ core->name);
+ goto fail_unreg;
+ }
+ printk(KERN_INFO "%s/0: registered device %s\n",
+ core->name, video_device_node_name(dev->radio_dev));
+ }
+
+ /* start tvaudio thread */
+ if (core->board.tuner_type != TUNER_ABSENT) {
+ core->kthread = kthread_run(cx88_audio_thread, core, "cx88 tvaudio");
+ if (IS_ERR(core->kthread)) {
+ err = PTR_ERR(core->kthread);
+ printk(KERN_ERR "%s/0: failed to create cx88 audio thread, err=%d\n",
+ core->name, err);
+ }
+ }
+ mutex_unlock(&core->lock);
+
+ return 0;
+
+fail_unreg:
+ cx8800_unregister_video(dev);
+ free_irq(pci_dev->irq, dev);
+ mutex_unlock(&core->lock);
+fail_core:
+ cx88_core_put(core,dev->pci);
+fail_free:
+ kfree(dev);
+ return err;
+}
+
+static void __devexit cx8800_finidev(struct pci_dev *pci_dev)
+{
+ struct cx8800_dev *dev = pci_get_drvdata(pci_dev);
+ struct cx88_core *core = dev->core;
+
+ /* stop thread */
+ if (core->kthread) {
+ kthread_stop(core->kthread);
+ core->kthread = NULL;
+ }
+
+ if (core->ir)
+ cx88_ir_stop(core);
+
+ cx88_shutdown(core); /* FIXME */
+ pci_disable_device(pci_dev);
+
+ /* unregister stuff */
+
+ free_irq(pci_dev->irq, dev);
+ cx8800_unregister_video(dev);
+ pci_set_drvdata(pci_dev, NULL);
+
+ /* free memory */
+ btcx_riscmem_free(dev->pci,&dev->vidq.stopper);
+ cx88_core_put(core,dev->pci);
+ kfree(dev);
+}
+
+#ifdef CONFIG_PM
+static int cx8800_suspend(struct pci_dev *pci_dev, pm_message_t state)
+{
+ struct cx8800_dev *dev = pci_get_drvdata(pci_dev);
+ struct cx88_core *core = dev->core;
+
+ /* stop video+vbi capture */
+ spin_lock(&dev->slock);
+ if (!list_empty(&dev->vidq.active)) {
+ printk("%s/0: suspend video\n", core->name);
+ stop_video_dma(dev);
+ del_timer(&dev->vidq.timeout);
+ }
+ if (!list_empty(&dev->vbiq.active)) {
+ printk("%s/0: suspend vbi\n", core->name);
+ cx8800_stop_vbi_dma(dev);
+ del_timer(&dev->vbiq.timeout);
+ }
+ spin_unlock(&dev->slock);
+
+ if (core->ir)
+ cx88_ir_stop(core);
+ /* FIXME -- shutdown device */
+ cx88_shutdown(core);
+
+ pci_save_state(pci_dev);
+ if (0 != pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state))) {
+ pci_disable_device(pci_dev);
+ dev->state.disabled = 1;
+ }
+ return 0;
+}
+
+static int cx8800_resume(struct pci_dev *pci_dev)
+{
+ struct cx8800_dev *dev = pci_get_drvdata(pci_dev);
+ struct cx88_core *core = dev->core;
+ int err;
+
+ if (dev->state.disabled) {
+ err=pci_enable_device(pci_dev);
+ if (err) {
+ printk(KERN_ERR "%s/0: can't enable device\n",
+ core->name);
+ return err;
+ }
+
+ dev->state.disabled = 0;
+ }
+ err= pci_set_power_state(pci_dev, PCI_D0);
+ if (err) {
+ printk(KERN_ERR "%s/0: can't set power state\n", core->name);
+ pci_disable_device(pci_dev);
+ dev->state.disabled = 1;
+
+ return err;
+ }
+ pci_restore_state(pci_dev);
+
+ /* FIXME: re-initialize hardware */
+ cx88_reset(core);
+ if (core->ir)
+ cx88_ir_start(core);
+
+ cx_set(MO_PCI_INTMSK, core->pci_irqmask);
+
+ /* restart video+vbi capture */
+ spin_lock(&dev->slock);
+ if (!list_empty(&dev->vidq.active)) {
+ printk("%s/0: resume video\n", core->name);
+ restart_video_queue(dev,&dev->vidq);
+ }
+ if (!list_empty(&dev->vbiq.active)) {
+ printk("%s/0: resume vbi\n", core->name);
+ cx8800_restart_vbi_queue(dev,&dev->vbiq);
+ }
+ spin_unlock(&dev->slock);
+
+ return 0;
+}
+#endif
+
+/* ----------------------------------------------------------- */
+
+static const struct pci_device_id cx8800_pci_tbl[] = {
+ {
+ .vendor = 0x14f1,
+ .device = 0x8800,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },{
+ /* --- end of list --- */
+ }
+};
+MODULE_DEVICE_TABLE(pci, cx8800_pci_tbl);
+
+static struct pci_driver cx8800_pci_driver = {
+ .name = "cx8800",
+ .id_table = cx8800_pci_tbl,
+ .probe = cx8800_initdev,
+ .remove = __devexit_p(cx8800_finidev),
+#ifdef CONFIG_PM
+ .suspend = cx8800_suspend,
+ .resume = cx8800_resume,
+#endif
+};
+
+static int __init cx8800_init(void)
+{
+ printk(KERN_INFO "cx88/0: cx2388x v4l2 driver version %s loaded\n",
+ CX88_VERSION);
+ return pci_register_driver(&cx8800_pci_driver);
+}
+
+static void __exit cx8800_fini(void)
+{
+ pci_unregister_driver(&cx8800_pci_driver);
+}
+
+module_init(cx8800_init);
+module_exit(cx8800_fini);
diff --git a/drivers/media/video/cx88/cx88-vp3054-i2c.c b/drivers/media/video/cx88/cx88-vp3054-i2c.c
new file mode 100644
index 00000000..d77f8eca
--- /dev/null
+++ b/drivers/media/video/cx88/cx88-vp3054-i2c.c
@@ -0,0 +1,159 @@
+/*
+
+ cx88-vp3054-i2c.c -- support for the secondary I2C bus of the
+ DNTV Live! DVB-T Pro (VP-3054), wired as:
+ GPIO[0] -> SCL, GPIO[1] -> SDA
+
+ (c) 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+
+#include "cx88.h"
+#include "cx88-vp3054-i2c.h"
+
+MODULE_DESCRIPTION("driver for cx2388x VP3054 design");
+MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
+MODULE_LICENSE("GPL");
+
+/* ----------------------------------------------------------------------- */
+
+static void vp3054_bit_setscl(void *data, int state)
+{
+ struct cx8802_dev *dev = data;
+ struct cx88_core *core = dev->core;
+ struct vp3054_i2c_state *vp3054_i2c = dev->vp3054;
+
+ if (state) {
+ vp3054_i2c->state |= 0x0001; /* SCL high */
+ vp3054_i2c->state &= ~0x0100; /* external pullup */
+ } else {
+ vp3054_i2c->state &= ~0x0001; /* SCL low */
+ vp3054_i2c->state |= 0x0100; /* drive pin */
+ }
+ cx_write(MO_GP0_IO, 0x010000 | vp3054_i2c->state);
+ cx_read(MO_GP0_IO);
+}
+
+static void vp3054_bit_setsda(void *data, int state)
+{
+ struct cx8802_dev *dev = data;
+ struct cx88_core *core = dev->core;
+ struct vp3054_i2c_state *vp3054_i2c = dev->vp3054;
+
+ if (state) {
+ vp3054_i2c->state |= 0x0002; /* SDA high */
+ vp3054_i2c->state &= ~0x0200; /* tristate pin */
+ } else {
+ vp3054_i2c->state &= ~0x0002; /* SDA low */
+ vp3054_i2c->state |= 0x0200; /* drive pin */
+ }
+ cx_write(MO_GP0_IO, 0x020000 | vp3054_i2c->state);
+ cx_read(MO_GP0_IO);
+}
+
+static int vp3054_bit_getscl(void *data)
+{
+ struct cx8802_dev *dev = data;
+ struct cx88_core *core = dev->core;
+ u32 state;
+
+ state = cx_read(MO_GP0_IO);
+ return (state & 0x01) ? 1 : 0;
+}
+
+static int vp3054_bit_getsda(void *data)
+{
+ struct cx8802_dev *dev = data;
+ struct cx88_core *core = dev->core;
+ u32 state;
+
+ state = cx_read(MO_GP0_IO);
+ return (state & 0x02) ? 1 : 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct i2c_algo_bit_data vp3054_i2c_algo_template = {
+ .setsda = vp3054_bit_setsda,
+ .setscl = vp3054_bit_setscl,
+ .getsda = vp3054_bit_getsda,
+ .getscl = vp3054_bit_getscl,
+ .udelay = 16,
+ .timeout = 200,
+};
+
+/* ----------------------------------------------------------------------- */
+
+int vp3054_i2c_probe(struct cx8802_dev *dev)
+{
+ struct cx88_core *core = dev->core;
+ struct vp3054_i2c_state *vp3054_i2c;
+ int rc;
+
+ if (core->boardnr != CX88_BOARD_DNTV_LIVE_DVB_T_PRO)
+ return 0;
+
+ vp3054_i2c = kzalloc(sizeof(*vp3054_i2c), GFP_KERNEL);
+ if (vp3054_i2c == NULL)
+ return -ENOMEM;
+ dev->vp3054 = vp3054_i2c;
+
+ memcpy(&vp3054_i2c->algo, &vp3054_i2c_algo_template,
+ sizeof(vp3054_i2c->algo));
+
+ vp3054_i2c->adap.dev.parent = &dev->pci->dev;
+ strlcpy(vp3054_i2c->adap.name, core->name,
+ sizeof(vp3054_i2c->adap.name));
+ vp3054_i2c->adap.owner = THIS_MODULE;
+ vp3054_i2c->algo.data = dev;
+ i2c_set_adapdata(&vp3054_i2c->adap, dev);
+ vp3054_i2c->adap.algo_data = &vp3054_i2c->algo;
+
+ vp3054_bit_setscl(dev,1);
+ vp3054_bit_setsda(dev,1);
+
+ rc = i2c_bit_add_bus(&vp3054_i2c->adap);
+ if (0 != rc) {
+ printk("%s: vp3054_i2c register FAILED\n", core->name);
+
+ kfree(dev->vp3054);
+ dev->vp3054 = NULL;
+ }
+
+ return rc;
+}
+
+void vp3054_i2c_remove(struct cx8802_dev *dev)
+{
+ struct vp3054_i2c_state *vp3054_i2c = dev->vp3054;
+
+ if (vp3054_i2c == NULL ||
+ dev->core->boardnr != CX88_BOARD_DNTV_LIVE_DVB_T_PRO)
+ return;
+
+ i2c_del_adapter(&vp3054_i2c->adap);
+ kfree(vp3054_i2c);
+}
+
+EXPORT_SYMBOL(vp3054_i2c_probe);
+EXPORT_SYMBOL(vp3054_i2c_remove);
diff --git a/drivers/media/video/cx88/cx88-vp3054-i2c.h b/drivers/media/video/cx88/cx88-vp3054-i2c.h
new file mode 100644
index 00000000..be99c931
--- /dev/null
+++ b/drivers/media/video/cx88/cx88-vp3054-i2c.h
@@ -0,0 +1,41 @@
+/*
+
+ cx88-vp3054-i2c.h -- support for the secondary I2C bus of the
+ DNTV Live! DVB-T Pro (VP-3054), wired as:
+ GPIO[0] -> SCL, GPIO[1] -> SDA
+
+ (c) 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/* ----------------------------------------------------------------------- */
+struct vp3054_i2c_state {
+ struct i2c_adapter adap;
+ struct i2c_algo_bit_data algo;
+ u32 state;
+};
+
+/* ----------------------------------------------------------------------- */
+#if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE))
+int vp3054_i2c_probe(struct cx8802_dev *dev);
+void vp3054_i2c_remove(struct cx8802_dev *dev);
+#else
+static inline int vp3054_i2c_probe(struct cx8802_dev *dev)
+{ return 0; }
+static inline void vp3054_i2c_remove(struct cx8802_dev *dev)
+{ }
+#endif
diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h
new file mode 100644
index 00000000..c9659def
--- /dev/null
+++ b/drivers/media/video/cx88/cx88.h
@@ -0,0 +1,732 @@
+/*
+ *
+ * v4l2 device driver for cx2388x based TV cards
+ *
+ * (c) 2003,04 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/videodev2.h>
+#include <linux/kdev_t.h>
+
+#include <media/v4l2-device.h>
+#include <media/tuner.h>
+#include <media/tveeprom.h>
+#include <media/videobuf-dma-sg.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/cx2341x.h>
+#include <media/videobuf-dvb.h>
+#include <media/ir-kbd-i2c.h>
+#include <media/wm8775.h>
+
+#include "btcx-risc.h"
+#include "cx88-reg.h"
+#include "tuner-xc2028.h"
+
+#include <linux/mutex.h>
+
+#define CX88_VERSION "0.0.9"
+
+#define UNSET (-1U)
+
+#define CX88_MAXBOARDS 8
+
+/* Max number of inputs by card */
+#define MAX_CX88_INPUT 8
+
+/* ----------------------------------------------------------- */
+/* defines and enums */
+
+/* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM/LC */
+#define CX88_NORMS (V4L2_STD_ALL \
+ & ~V4L2_STD_PAL_H \
+ & ~V4L2_STD_NTSC_M_KR \
+ & ~V4L2_STD_SECAM_LC)
+
+#define FORMAT_FLAGS_PACKED 0x01
+#define FORMAT_FLAGS_PLANAR 0x02
+
+#define VBI_LINE_COUNT 17
+#define VBI_LINE_LENGTH 2048
+
+#define AUD_RDS_LINES 4
+
+/* need "shadow" registers for some write-only ones ... */
+#define SHADOW_AUD_VOL_CTL 1
+#define SHADOW_AUD_BAL_CTL 2
+#define SHADOW_MAX 3
+
+/* FM Radio deemphasis type */
+enum cx88_deemph_type {
+ FM_NO_DEEMPH = 0,
+ FM_DEEMPH_50,
+ FM_DEEMPH_75
+};
+
+enum cx88_board_type {
+ CX88_BOARD_NONE = 0,
+ CX88_MPEG_DVB,
+ CX88_MPEG_BLACKBIRD
+};
+
+enum cx8802_board_access {
+ CX8802_DRVCTL_SHARED = 1,
+ CX8802_DRVCTL_EXCLUSIVE = 2,
+};
+
+/* ----------------------------------------------------------- */
+/* tv norms */
+
+static unsigned int inline norm_maxw(v4l2_std_id norm)
+{
+ return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 720 : 768;
+}
+
+
+static unsigned int inline norm_maxh(v4l2_std_id norm)
+{
+ return (norm & V4L2_STD_625_50) ? 576 : 480;
+}
+
+/* ----------------------------------------------------------- */
+/* static data */
+
+struct cx8800_fmt {
+ const char *name;
+ u32 fourcc; /* v4l2 format id */
+ int depth;
+ int flags;
+ u32 cxformat;
+};
+
+struct cx88_ctrl {
+ struct v4l2_queryctrl v;
+ u32 off;
+ u32 reg;
+ u32 sreg;
+ u32 mask;
+ u32 shift;
+};
+
+/* ----------------------------------------------------------- */
+/* SRAM memory management data (see cx88-core.c) */
+
+#define SRAM_CH21 0 /* video */
+#define SRAM_CH22 1
+#define SRAM_CH23 2
+#define SRAM_CH24 3 /* vbi */
+#define SRAM_CH25 4 /* audio */
+#define SRAM_CH26 5
+#define SRAM_CH28 6 /* mpeg */
+#define SRAM_CH27 7 /* audio rds */
+/* more */
+
+struct sram_channel {
+ const char *name;
+ u32 cmds_start;
+ u32 ctrl_start;
+ u32 cdt;
+ u32 fifo_start;
+ u32 fifo_size;
+ u32 ptr1_reg;
+ u32 ptr2_reg;
+ u32 cnt1_reg;
+ u32 cnt2_reg;
+};
+extern const struct sram_channel const cx88_sram_channels[];
+
+/* ----------------------------------------------------------- */
+/* card configuration */
+
+#define CX88_BOARD_NOAUTO UNSET
+#define CX88_BOARD_UNKNOWN 0
+#define CX88_BOARD_HAUPPAUGE 1
+#define CX88_BOARD_GDI 2
+#define CX88_BOARD_PIXELVIEW 3
+#define CX88_BOARD_ATI_WONDER_PRO 4
+#define CX88_BOARD_WINFAST2000XP_EXPERT 5
+#define CX88_BOARD_AVERTV_STUDIO_303 6
+#define CX88_BOARD_MSI_TVANYWHERE_MASTER 7
+#define CX88_BOARD_WINFAST_DV2000 8
+#define CX88_BOARD_LEADTEK_PVR2000 9
+#define CX88_BOARD_IODATA_GVVCP3PCI 10
+#define CX88_BOARD_PROLINK_PLAYTVPVR 11
+#define CX88_BOARD_ASUS_PVR_416 12
+#define CX88_BOARD_MSI_TVANYWHERE 13
+#define CX88_BOARD_KWORLD_DVB_T 14
+#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1 15
+#define CX88_BOARD_KWORLD_LTV883 16
+#define CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q 17
+#define CX88_BOARD_HAUPPAUGE_DVB_T1 18
+#define CX88_BOARD_CONEXANT_DVB_T1 19
+#define CX88_BOARD_PROVIDEO_PV259 20
+#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS 21
+#define CX88_BOARD_PCHDTV_HD3000 22
+#define CX88_BOARD_DNTV_LIVE_DVB_T 23
+#define CX88_BOARD_HAUPPAUGE_ROSLYN 24
+#define CX88_BOARD_DIGITALLOGIC_MEC 25
+#define CX88_BOARD_IODATA_GVBCTV7E 26
+#define CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO 27
+#define CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T 28
+#define CX88_BOARD_ADSTECH_DVB_T_PCI 29
+#define CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1 30
+#define CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD 31
+#define CX88_BOARD_AVERMEDIA_ULTRATV_MC_550 32
+#define CX88_BOARD_KWORLD_VSTREAM_EXPERT_DVD 33
+#define CX88_BOARD_ATI_HDTVWONDER 34
+#define CX88_BOARD_WINFAST_DTV1000 35
+#define CX88_BOARD_AVERTV_303 36
+#define CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1 37
+#define CX88_BOARD_HAUPPAUGE_NOVASE2_S1 38
+#define CX88_BOARD_KWORLD_DVBS_100 39
+#define CX88_BOARD_HAUPPAUGE_HVR1100 40
+#define CX88_BOARD_HAUPPAUGE_HVR1100LP 41
+#define CX88_BOARD_DNTV_LIVE_DVB_T_PRO 42
+#define CX88_BOARD_KWORLD_DVB_T_CX22702 43
+#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL 44
+#define CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT 45
+#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID 46
+#define CX88_BOARD_PCHDTV_HD5500 47
+#define CX88_BOARD_KWORLD_MCE200_DELUXE 48
+#define CX88_BOARD_PIXELVIEW_PLAYTV_P7000 49
+#define CX88_BOARD_NPGTECH_REALTV_TOP10FM 50
+#define CX88_BOARD_WINFAST_DTV2000H 51
+#define CX88_BOARD_GENIATECH_DVBS 52
+#define CX88_BOARD_HAUPPAUGE_HVR3000 53
+#define CX88_BOARD_NORWOOD_MICRO 54
+#define CX88_BOARD_TE_DTV_250_OEM_SWANN 55
+#define CX88_BOARD_HAUPPAUGE_HVR1300 56
+#define CX88_BOARD_ADSTECH_PTV_390 57
+#define CX88_BOARD_PINNACLE_PCTV_HD_800i 58
+#define CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO 59
+#define CX88_BOARD_PINNACLE_HYBRID_PCTV 60
+#define CX88_BOARD_WINFAST_TV2000_XP_GLOBAL 61
+#define CX88_BOARD_POWERCOLOR_REAL_ANGEL 62
+#define CX88_BOARD_GENIATECH_X8000_MT 63
+#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO 64
+#define CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD 65
+#define CX88_BOARD_PROLINK_PV_8000GT 66
+#define CX88_BOARD_KWORLD_ATSC_120 67
+#define CX88_BOARD_HAUPPAUGE_HVR4000 68
+#define CX88_BOARD_HAUPPAUGE_HVR4000LITE 69
+#define CX88_BOARD_TEVII_S460 70
+#define CX88_BOARD_OMICOM_SS4_PCI 71
+#define CX88_BOARD_TBS_8920 72
+#define CX88_BOARD_TEVII_S420 73
+#define CX88_BOARD_PROLINK_PV_GLOBAL_XTREME 74
+#define CX88_BOARD_PROF_7300 75
+#define CX88_BOARD_SATTRADE_ST4200 76
+#define CX88_BOARD_TBS_8910 77
+#define CX88_BOARD_PROF_6200 78
+#define CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII 79
+#define CX88_BOARD_HAUPPAUGE_IRONLY 80
+#define CX88_BOARD_WINFAST_DTV1800H 81
+#define CX88_BOARD_WINFAST_DTV2000H_J 82
+#define CX88_BOARD_PROF_7301 83
+#define CX88_BOARD_SAMSUNG_SMT_7020 84
+#define CX88_BOARD_TWINHAN_VP1027_DVBS 85
+#define CX88_BOARD_TEVII_S464 86
+#define CX88_BOARD_WINFAST_DTV2000H_PLUS 87
+#define CX88_BOARD_WINFAST_DTV1800H_XC4000 88
+#define CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36 89
+#define CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43 90
+
+enum cx88_itype {
+ CX88_VMUX_COMPOSITE1 = 1,
+ CX88_VMUX_COMPOSITE2,
+ CX88_VMUX_COMPOSITE3,
+ CX88_VMUX_COMPOSITE4,
+ CX88_VMUX_SVIDEO,
+ CX88_VMUX_TELEVISION,
+ CX88_VMUX_CABLE,
+ CX88_VMUX_DVB,
+ CX88_VMUX_DEBUG,
+ CX88_RADIO,
+};
+
+struct cx88_input {
+ enum cx88_itype type;
+ u32 gpio0, gpio1, gpio2, gpio3;
+ unsigned int vmux:2;
+ unsigned int audioroute:4;
+};
+
+struct cx88_board {
+ const char *name;
+ unsigned int tuner_type;
+ unsigned int radio_type;
+ unsigned char tuner_addr;
+ unsigned char radio_addr;
+ int tda9887_conf;
+ struct cx88_input input[MAX_CX88_INPUT];
+ struct cx88_input radio;
+ enum cx88_board_type mpeg;
+ unsigned int audio_chip;
+ int num_frontends;
+
+ /* Used for I2S devices */
+ int i2sinputcntl;
+};
+
+struct cx88_subid {
+ u16 subvendor;
+ u16 subdevice;
+ u32 card;
+};
+
+enum cx88_tvaudio {
+ WW_NONE = 1,
+ WW_BTSC,
+ WW_BG,
+ WW_DK,
+ WW_I,
+ WW_L,
+ WW_EIAJ,
+ WW_I2SPT,
+ WW_FM,
+ WW_I2SADC,
+ WW_M
+};
+
+#define INPUT(nr) (core->board.input[nr])
+
+/* ----------------------------------------------------------- */
+/* device / file handle status */
+
+#define RESOURCE_OVERLAY 1
+#define RESOURCE_VIDEO 2
+#define RESOURCE_VBI 4
+
+#define BUFFER_TIMEOUT msecs_to_jiffies(2000)
+
+/* buffer for one video frame */
+struct cx88_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+
+ /* cx88 specific */
+ unsigned int bpl;
+ struct btcx_riscmem risc;
+ const struct cx8800_fmt *fmt;
+ u32 count;
+};
+
+struct cx88_dmaqueue {
+ struct list_head active;
+ struct list_head queued;
+ struct timer_list timeout;
+ struct btcx_riscmem stopper;
+ u32 count;
+};
+
+struct cx88_core {
+ struct list_head devlist;
+ atomic_t refcount;
+
+ /* board name */
+ int nr;
+ char name[32];
+
+ /* pci stuff */
+ int pci_bus;
+ int pci_slot;
+ u32 __iomem *lmmio;
+ u8 __iomem *bmmio;
+ u32 shadow[SHADOW_MAX];
+ int pci_irqmask;
+
+ /* i2c i/o */
+ struct i2c_adapter i2c_adap;
+ struct i2c_algo_bit_data i2c_algo;
+ struct i2c_client i2c_client;
+ u32 i2c_state, i2c_rc;
+
+ /* config info -- analog */
+ struct v4l2_device v4l2_dev;
+ struct i2c_client *i2c_rtc;
+ unsigned int boardnr;
+ struct cx88_board board;
+
+ /* Supported V4L _STD_ tuner formats */
+ unsigned int tuner_formats;
+
+ /* config info -- dvb */
+#if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE)
+ int (*prev_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage);
+#endif
+ void (*gate_ctrl)(struct cx88_core *core, int open);
+
+ /* state info */
+ struct task_struct *kthread;
+ v4l2_std_id tvnorm;
+ enum cx88_tvaudio tvaudio;
+ u32 audiomode_manual;
+ u32 audiomode_current;
+ u32 input;
+ u32 last_analog_input;
+ u32 astat;
+ u32 use_nicam;
+ unsigned long last_change;
+
+ /* IR remote control state */
+ struct cx88_IR *ir;
+
+ /* I2C remote data */
+ struct IR_i2c_init_data init_data;
+ struct wm8775_platform_data wm8775_data;
+
+ struct mutex lock;
+ /* various v4l controls */
+ u32 freq;
+ int users;
+ int mpeg_users;
+
+ /* cx88-video needs to access cx8802 for hybrid tuner pll access. */
+ struct cx8802_dev *dvbdev;
+ enum cx88_board_type active_type_id;
+ int active_ref;
+ int active_fe_id;
+};
+
+static inline struct cx88_core *to_core(struct v4l2_device *v4l2_dev)
+{
+ return container_of(v4l2_dev, struct cx88_core, v4l2_dev);
+}
+
+#define WM8775_GID (1 << 0)
+
+#define call_hw(core, grpid, o, f, args...) \
+ do { \
+ if (!core->i2c_rc) { \
+ if (core->gate_ctrl) \
+ core->gate_ctrl(core, 1); \
+ v4l2_device_call_all(&core->v4l2_dev, grpid, o, f, ##args); \
+ if (core->gate_ctrl) \
+ core->gate_ctrl(core, 0); \
+ } \
+ } while (0)
+
+#define call_all(core, o, f, args...) call_hw(core, 0, o, f, ##args)
+
+struct cx8800_dev;
+struct cx8802_dev;
+
+/* ----------------------------------------------------------- */
+/* function 0: video stuff */
+
+struct cx8800_fh {
+ struct cx8800_dev *dev;
+ enum v4l2_buf_type type;
+ int radio;
+ unsigned int resources;
+
+ /* video overlay */
+ struct v4l2_window win;
+ struct v4l2_clip *clips;
+ unsigned int nclips;
+
+ /* video capture */
+ const struct cx8800_fmt *fmt;
+ unsigned int width,height;
+ struct videobuf_queue vidq;
+
+ /* vbi capture */
+ struct videobuf_queue vbiq;
+};
+
+struct cx8800_suspend_state {
+ int disabled;
+};
+
+struct cx8800_dev {
+ struct cx88_core *core;
+ spinlock_t slock;
+
+ /* various device info */
+ unsigned int resources;
+ struct video_device *video_dev;
+ struct video_device *vbi_dev;
+ struct video_device *radio_dev;
+
+ /* pci i/o */
+ struct pci_dev *pci;
+ unsigned char pci_rev,pci_lat;
+
+
+ /* capture queues */
+ struct cx88_dmaqueue vidq;
+ struct cx88_dmaqueue vbiq;
+
+ /* various v4l controls */
+
+ /* other global state info */
+ struct cx8800_suspend_state state;
+};
+
+/* ----------------------------------------------------------- */
+/* function 1: audio/alsa stuff */
+/* =============> moved to cx88-alsa.c <====================== */
+
+
+/* ----------------------------------------------------------- */
+/* function 2: mpeg stuff */
+
+struct cx8802_fh {
+ struct cx8802_dev *dev;
+ struct videobuf_queue mpegq;
+};
+
+struct cx8802_suspend_state {
+ int disabled;
+};
+
+struct cx8802_driver {
+ struct cx88_core *core;
+
+ /* List of drivers attached to device */
+ struct list_head drvlist;
+
+ /* Type of driver and access required */
+ enum cx88_board_type type_id;
+ enum cx8802_board_access hw_access;
+
+ /* MPEG 8802 internal only */
+ int (*suspend)(struct pci_dev *pci_dev, pm_message_t state);
+ int (*resume)(struct pci_dev *pci_dev);
+
+ /* Callers to the following functions must hold core->lock */
+
+ /* MPEG 8802 -> mini driver - Driver probe and configuration */
+ int (*probe)(struct cx8802_driver *drv);
+ int (*remove)(struct cx8802_driver *drv);
+
+ /* MPEG 8802 -> mini driver - Access for hardware control */
+ int (*advise_acquire)(struct cx8802_driver *drv);
+ int (*advise_release)(struct cx8802_driver *drv);
+
+ /* MPEG 8802 <- mini driver - Access for hardware control */
+ int (*request_acquire)(struct cx8802_driver *drv);
+ int (*request_release)(struct cx8802_driver *drv);
+};
+
+struct cx8802_dev {
+ struct cx88_core *core;
+ spinlock_t slock;
+
+ /* pci i/o */
+ struct pci_dev *pci;
+ unsigned char pci_rev,pci_lat;
+
+ /* dma queues */
+ struct cx88_dmaqueue mpegq;
+ u32 ts_packet_size;
+ u32 ts_packet_count;
+
+ /* other global state info */
+ struct cx8802_suspend_state state;
+
+ /* for blackbird only */
+ struct list_head devlist;
+#if defined(CONFIG_VIDEO_CX88_BLACKBIRD) || \
+ defined(CONFIG_VIDEO_CX88_BLACKBIRD_MODULE)
+ struct video_device *mpeg_dev;
+ u32 mailbox;
+ int width;
+ int height;
+ unsigned char mpeg_active; /* nonzero if mpeg encoder is active */
+
+ /* mpeg params */
+ struct cx2341x_mpeg_params params;
+#endif
+
+#if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE)
+ /* for dvb only */
+ struct videobuf_dvb_frontends frontends;
+#endif
+
+#if defined(CONFIG_VIDEO_CX88_VP3054) || \
+ defined(CONFIG_VIDEO_CX88_VP3054_MODULE)
+ /* For VP3045 secondary I2C bus support */
+ struct vp3054_i2c_state *vp3054;
+#endif
+ /* for switching modulation types */
+ unsigned char ts_gen_cntrl;
+
+ /* List of attached drivers; must hold core->lock to access */
+ struct list_head drvlist;
+
+ struct work_struct request_module_wk;
+};
+
+/* ----------------------------------------------------------- */
+
+#define cx_read(reg) readl(core->lmmio + ((reg)>>2))
+#define cx_write(reg,value) writel((value), core->lmmio + ((reg)>>2))
+#define cx_writeb(reg,value) writeb((value), core->bmmio + (reg))
+
+#define cx_andor(reg,mask,value) \
+ writel((readl(core->lmmio+((reg)>>2)) & ~(mask)) |\
+ ((value) & (mask)), core->lmmio+((reg)>>2))
+#define cx_set(reg,bit) cx_andor((reg),(bit),(bit))
+#define cx_clear(reg,bit) cx_andor((reg),(bit),0)
+
+#define cx_wait(d) { if (need_resched()) schedule(); else udelay(d); }
+
+/* shadow registers */
+#define cx_sread(sreg) (core->shadow[sreg])
+#define cx_swrite(sreg,reg,value) \
+ (core->shadow[sreg] = value, \
+ writel(core->shadow[sreg], core->lmmio + ((reg)>>2)))
+#define cx_sandor(sreg,reg,mask,value) \
+ (core->shadow[sreg] = (core->shadow[sreg] & ~(mask)) | ((value) & (mask)), \
+ writel(core->shadow[sreg], core->lmmio + ((reg)>>2)))
+
+/* ----------------------------------------------------------- */
+/* cx88-core.c */
+
+extern void cx88_print_irqbits(const char *name, const char *tag, const char *strings[],
+ int len, u32 bits, u32 mask);
+
+extern int cx88_core_irq(struct cx88_core *core, u32 status);
+extern void cx88_wakeup(struct cx88_core *core,
+ struct cx88_dmaqueue *q, u32 count);
+extern void cx88_shutdown(struct cx88_core *core);
+extern int cx88_reset(struct cx88_core *core);
+
+extern int
+cx88_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+ struct scatterlist *sglist,
+ unsigned int top_offset, unsigned int bottom_offset,
+ unsigned int bpl, unsigned int padding, unsigned int lines);
+extern int
+cx88_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+ struct scatterlist *sglist, unsigned int bpl,
+ unsigned int lines, unsigned int lpi);
+extern int
+cx88_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
+ u32 reg, u32 mask, u32 value);
+extern void
+cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf);
+
+extern void cx88_risc_disasm(struct cx88_core *core,
+ struct btcx_riscmem *risc);
+extern int cx88_sram_channel_setup(struct cx88_core *core,
+ const struct sram_channel *ch,
+ unsigned int bpl, u32 risc);
+extern void cx88_sram_channel_dump(struct cx88_core *core,
+ const struct sram_channel *ch);
+
+extern int cx88_set_scale(struct cx88_core *core, unsigned int width,
+ unsigned int height, enum v4l2_field field);
+extern int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm);
+
+extern struct video_device *cx88_vdev_init(struct cx88_core *core,
+ struct pci_dev *pci,
+ const struct video_device *template_,
+ const char *type);
+extern struct cx88_core* cx88_core_get(struct pci_dev *pci);
+extern void cx88_core_put(struct cx88_core *core,
+ struct pci_dev *pci);
+
+extern int cx88_start_audio_dma(struct cx88_core *core);
+extern int cx88_stop_audio_dma(struct cx88_core *core);
+
+
+/* ----------------------------------------------------------- */
+/* cx88-vbi.c */
+
+/* Can be used as g_vbi_fmt, try_vbi_fmt and s_vbi_fmt */
+int cx8800_vbi_fmt (struct file *file, void *priv,
+ struct v4l2_format *f);
+
+/*
+int cx8800_start_vbi_dma(struct cx8800_dev *dev,
+ struct cx88_dmaqueue *q,
+ struct cx88_buffer *buf);
+*/
+int cx8800_stop_vbi_dma(struct cx8800_dev *dev);
+int cx8800_restart_vbi_queue(struct cx8800_dev *dev,
+ struct cx88_dmaqueue *q);
+void cx8800_vbi_timeout(unsigned long data);
+
+extern const struct videobuf_queue_ops cx8800_vbi_qops;
+
+/* ----------------------------------------------------------- */
+/* cx88-i2c.c */
+
+extern int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci);
+
+
+/* ----------------------------------------------------------- */
+/* cx88-cards.c */
+
+extern int cx88_tuner_callback(void *dev, int component, int command, int arg);
+extern int cx88_get_resources(const struct cx88_core *core,
+ struct pci_dev *pci);
+extern struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr);
+extern void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl);
+
+/* ----------------------------------------------------------- */
+/* cx88-tvaudio.c */
+
+void cx88_set_tvaudio(struct cx88_core *core);
+void cx88_newstation(struct cx88_core *core);
+void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t);
+void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual);
+int cx88_audio_thread(void *data);
+
+int cx8802_register_driver(struct cx8802_driver *drv);
+int cx8802_unregister_driver(struct cx8802_driver *drv);
+
+/* Caller must hold core->lock */
+struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype);
+
+/* ----------------------------------------------------------- */
+/* cx88-dsp.c */
+
+s32 cx88_dsp_detect_stereo_sap(struct cx88_core *core);
+
+/* ----------------------------------------------------------- */
+/* cx88-input.c */
+
+int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci);
+int cx88_ir_fini(struct cx88_core *core);
+void cx88_ir_irq(struct cx88_core *core);
+int cx88_ir_start(struct cx88_core *core);
+void cx88_ir_stop(struct cx88_core *core);
+extern void cx88_i2c_init_ir(struct cx88_core *core);
+
+/* ----------------------------------------------------------- */
+/* cx88-mpeg.c */
+
+int cx8802_buf_prepare(struct videobuf_queue *q,struct cx8802_dev *dev,
+ struct cx88_buffer *buf, enum v4l2_field field);
+void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf);
+void cx8802_cancel_buffers(struct cx8802_dev *dev);
+
+/* ----------------------------------------------------------- */
+/* cx88-video.c*/
+extern const u32 cx88_user_ctrls[];
+extern int cx8800_ctrl_query(struct cx88_core *core,
+ struct v4l2_queryctrl *qctrl);
+int cx88_enum_input (struct cx88_core *core,struct v4l2_input *i);
+int cx88_set_freq (struct cx88_core *core,struct v4l2_frequency *f);
+int cx88_get_control(struct cx88_core *core, struct v4l2_control *ctl);
+int cx88_set_control(struct cx88_core *core, struct v4l2_control *ctl);
+int cx88_video_mux(struct cx88_core *core, unsigned int input);
diff --git a/drivers/media/video/davinci/Kconfig b/drivers/media/video/davinci/Kconfig
new file mode 100644
index 00000000..60a456eb
--- /dev/null
+++ b/drivers/media/video/davinci/Kconfig
@@ -0,0 +1,116 @@
+config DISPLAY_DAVINCI_DM646X_EVM
+ tristate "DM646x EVM Video Display"
+ depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM
+ select VIDEOBUF_DMA_CONTIG
+ select VIDEO_DAVINCI_VPIF
+ select VIDEO_ADV7343
+ select VIDEO_THS7303
+ help
+ Support for DM6467 based display device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vpif_display.
+
+config CAPTURE_DAVINCI_DM646X_EVM
+ tristate "DM646x EVM Video Capture"
+ depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM
+ select VIDEOBUF_DMA_CONTIG
+ select VIDEO_DAVINCI_VPIF
+ help
+ Support for DM6467 based capture device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vpif_capture.
+
+config VIDEO_DAVINCI_VPIF
+ tristate "DaVinci VPIF Driver"
+ depends on DISPLAY_DAVINCI_DM646X_EVM
+ help
+ Support for DaVinci VPIF Driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vpif.
+
+config VIDEO_VPSS_SYSTEM
+ tristate "VPSS System module driver"
+ depends on ARCH_DAVINCI
+ help
+ Support for vpss system module for video driver
+
+config VIDEO_VPFE_CAPTURE
+ tristate "VPFE Video Capture Driver"
+ depends on VIDEO_V4L2 && (ARCH_DAVINCI || ARCH_OMAP3)
+ select VIDEOBUF_DMA_CONTIG
+ help
+ Support for DMx/AMx VPFE based frame grabber. This is the
+ common V4L2 module for following DMx/AMx SoCs from Texas
+ Instruments:- DM6446, DM365, DM355 & AM3517/05.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vpfe-capture.
+
+config VIDEO_DM6446_CCDC
+ tristate "DM6446 CCDC HW module"
+ depends on VIDEO_VPFE_CAPTURE
+ select VIDEO_VPSS_SYSTEM
+ default y
+ help
+ Enables DaVinci CCD hw module. DaVinci CCDC hw interfaces
+ with decoder modules such as TVP5146 over BT656 or
+ sensor module such as MT9T001 over a raw interface. This
+ module configures the interface and CCDC/ISIF to do
+ video frame capture from slave decoders.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vpfe.
+
+config VIDEO_DM355_CCDC
+ tristate "DM355 CCDC HW module"
+ depends on ARCH_DAVINCI_DM355 && VIDEO_VPFE_CAPTURE
+ select VIDEO_VPSS_SYSTEM
+ default y
+ help
+ Enables DM355 CCD hw module. DM355 CCDC hw interfaces
+ with decoder modules such as TVP5146 over BT656 or
+ sensor module such as MT9T001 over a raw interface. This
+ module configures the interface and CCDC/ISIF to do
+ video frame capture from a slave decoders
+
+ To compile this driver as a module, choose M here: the
+ module will be called vpfe.
+
+config VIDEO_ISIF
+ tristate "ISIF HW module"
+ depends on ARCH_DAVINCI_DM365 && VIDEO_VPFE_CAPTURE
+ select VIDEO_VPSS_SYSTEM
+ default y
+ help
+ Enables ISIF hw module. This is the hardware module for
+ configuring ISIF in VPFE to capture Raw Bayer RGB data from
+ a image sensor or YUV data from a YUV source.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vpfe.
+
+config VIDEO_DM644X_VPBE
+ tristate "DM644X VPBE HW module"
+ depends on ARCH_DAVINCI_DM644x
+ select VIDEO_VPSS_SYSTEM
+ select VIDEOBUF_DMA_CONTIG
+ help
+ Enables VPBE modules used for display on a DM644x
+ SoC.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vpbe.
+
+
+config VIDEO_VPBE_DISPLAY
+ tristate "VPBE V4L2 Display driver"
+ depends on ARCH_DAVINCI_DM644x
+ select VIDEO_DM644X_VPBE
+ help
+ Enables VPBE V4L2 Display driver on a DM644x device
+
+ To compile this driver as a module, choose M here: the
+ module will be called vpbe_display.
diff --git a/drivers/media/video/davinci/Makefile b/drivers/media/video/davinci/Makefile
new file mode 100644
index 00000000..ae7dafb6
--- /dev/null
+++ b/drivers/media/video/davinci/Makefile
@@ -0,0 +1,20 @@
+#
+# Makefile for the davinci video device drivers.
+#
+
+# VPIF
+obj-$(CONFIG_VIDEO_DAVINCI_VPIF) += vpif.o
+
+#DM646x EVM Display driver
+obj-$(CONFIG_DISPLAY_DAVINCI_DM646X_EVM) += vpif_display.o
+#DM646x EVM Capture driver
+obj-$(CONFIG_CAPTURE_DAVINCI_DM646X_EVM) += vpif_capture.o
+
+# Capture: DM6446 and DM355
+obj-$(CONFIG_VIDEO_VPSS_SYSTEM) += vpss.o
+obj-$(CONFIG_VIDEO_VPFE_CAPTURE) += vpfe_capture.o
+obj-$(CONFIG_VIDEO_DM6446_CCDC) += dm644x_ccdc.o
+obj-$(CONFIG_VIDEO_DM355_CCDC) += dm355_ccdc.o
+obj-$(CONFIG_VIDEO_ISIF) += isif.o
+obj-$(CONFIG_VIDEO_DM644X_VPBE) += vpbe.o vpbe_osd.o vpbe_venc.o
+obj-$(CONFIG_VIDEO_VPBE_DISPLAY) += vpbe_display.o
diff --git a/drivers/media/video/davinci/ccdc_hw_device.h b/drivers/media/video/davinci/ccdc_hw_device.h
new file mode 100644
index 00000000..86b9b351
--- /dev/null
+++ b/drivers/media/video/davinci/ccdc_hw_device.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2008-2009 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * ccdc device API
+ */
+#ifndef _CCDC_HW_DEVICE_H
+#define _CCDC_HW_DEVICE_H
+
+#ifdef __KERNEL__
+#include <linux/videodev2.h>
+#include <linux/device.h>
+#include <media/davinci/vpfe_types.h>
+#include <media/davinci/ccdc_types.h>
+
+/*
+ * ccdc hw operations
+ */
+struct ccdc_hw_ops {
+ /* Pointer to initialize function to initialize ccdc device */
+ int (*open) (struct device *dev);
+ /* Pointer to deinitialize function */
+ int (*close) (struct device *dev);
+ /* set ccdc base address */
+ void (*set_ccdc_base)(void *base, int size);
+ /* Pointer to function to enable or disable ccdc */
+ void (*enable) (int en);
+ /* reset sbl. only for 6446 */
+ void (*reset) (void);
+ /* enable output to sdram */
+ void (*enable_out_to_sdram) (int en);
+ /* Pointer to function to set hw parameters */
+ int (*set_hw_if_params) (struct vpfe_hw_if_param *param);
+ /* get interface parameters */
+ int (*get_hw_if_params) (struct vpfe_hw_if_param *param);
+ /*
+ * Pointer to function to set parameters. Used
+ * for implementing VPFE_S_CCDC_PARAMS
+ */
+ int (*set_params) (void *params);
+ /*
+ * Pointer to function to get parameter. Used
+ * for implementing VPFE_G_CCDC_PARAMS
+ */
+ int (*get_params) (void *params);
+ /* Pointer to function to configure ccdc */
+ int (*configure) (void);
+
+ /* Pointer to function to set buffer type */
+ int (*set_buftype) (enum ccdc_buftype buf_type);
+ /* Pointer to function to get buffer type */
+ enum ccdc_buftype (*get_buftype) (void);
+ /* Pointer to function to set frame format */
+ int (*set_frame_format) (enum ccdc_frmfmt frm_fmt);
+ /* Pointer to function to get frame format */
+ enum ccdc_frmfmt (*get_frame_format) (void);
+ /* enumerate hw pix formats */
+ int (*enum_pix)(u32 *hw_pix, int i);
+ /* Pointer to function to set buffer type */
+ u32 (*get_pixel_format) (void);
+ /* Pointer to function to get pixel format. */
+ int (*set_pixel_format) (u32 pixfmt);
+ /* Pointer to function to set image window */
+ int (*set_image_window) (struct v4l2_rect *win);
+ /* Pointer to function to set image window */
+ void (*get_image_window) (struct v4l2_rect *win);
+ /* Pointer to function to get line length */
+ unsigned int (*get_line_length) (void);
+
+ /* Query CCDC control IDs */
+ int (*queryctrl)(struct v4l2_queryctrl *qctrl);
+ /* Set CCDC control */
+ int (*set_control)(struct v4l2_control *ctrl);
+ /* Get CCDC control */
+ int (*get_control)(struct v4l2_control *ctrl);
+
+ /* Pointer to function to set frame buffer address */
+ void (*setfbaddr) (unsigned long addr);
+ /* Pointer to function to get field id */
+ int (*getfid) (void);
+};
+
+struct ccdc_hw_device {
+ /* ccdc device name */
+ char name[32];
+ /* module owner */
+ struct module *owner;
+ /* hw ops */
+ struct ccdc_hw_ops hw_ops;
+};
+
+/* Used by CCDC module to register & unregister with vpfe capture driver */
+int vpfe_register_ccdc_device(struct ccdc_hw_device *dev);
+void vpfe_unregister_ccdc_device(struct ccdc_hw_device *dev);
+
+#endif
+#endif
diff --git a/drivers/media/video/davinci/dm355_ccdc.c b/drivers/media/video/davinci/dm355_ccdc.c
new file mode 100644
index 00000000..5b68847d
--- /dev/null
+++ b/drivers/media/video/davinci/dm355_ccdc.c
@@ -0,0 +1,1072 @@
+/*
+ * Copyright (C) 2005-2009 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * CCDC hardware module for DM355
+ * ------------------------------
+ *
+ * This module is for configuring DM355 CCD controller of VPFE to capture
+ * Raw yuv or Bayer RGB data from a decoder. CCDC has several modules
+ * such as Defect Pixel Correction, Color Space Conversion etc to
+ * pre-process the Bayer RGB data, before writing it to SDRAM. This
+ * module also allows application to configure individual
+ * module parameters through VPFE_CMD_S_CCDC_RAW_PARAMS IOCTL.
+ * To do so, application include dm355_ccdc.h and vpfe_capture.h header
+ * files. The setparams() API is called by vpfe_capture driver
+ * to configure module parameters
+ *
+ * TODO: 1) Raw bayer parameter settings and bayer capture
+ * 2) Split module parameter structure to module specific ioctl structs
+ * 3) add support for lense shading correction
+ * 4) investigate if enum used for user space type definition
+ * to be replaced by #defines or integer
+ */
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/videodev2.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/module.h>
+
+#include <media/davinci/dm355_ccdc.h>
+#include <media/davinci/vpss.h>
+
+#include "dm355_ccdc_regs.h"
+#include "ccdc_hw_device.h"
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("CCDC Driver for DM355");
+MODULE_AUTHOR("Texas Instruments");
+
+static struct ccdc_oper_config {
+ struct device *dev;
+ /* CCDC interface type */
+ enum vpfe_hw_if_type if_type;
+ /* Raw Bayer configuration */
+ struct ccdc_params_raw bayer;
+ /* YCbCr configuration */
+ struct ccdc_params_ycbcr ycbcr;
+ /* Master clock */
+ struct clk *mclk;
+ /* slave clock */
+ struct clk *sclk;
+ /* ccdc base address */
+ void __iomem *base_addr;
+} ccdc_cfg = {
+ /* Raw configurations */
+ .bayer = {
+ .pix_fmt = CCDC_PIXFMT_RAW,
+ .frm_fmt = CCDC_FRMFMT_PROGRESSIVE,
+ .win = CCDC_WIN_VGA,
+ .fid_pol = VPFE_PINPOL_POSITIVE,
+ .vd_pol = VPFE_PINPOL_POSITIVE,
+ .hd_pol = VPFE_PINPOL_POSITIVE,
+ .gain = {
+ .r_ye = 256,
+ .gb_g = 256,
+ .gr_cy = 256,
+ .b_mg = 256
+ },
+ .config_params = {
+ .datasft = 2,
+ .mfilt1 = CCDC_NO_MEDIAN_FILTER1,
+ .mfilt2 = CCDC_NO_MEDIAN_FILTER2,
+ .alaw = {
+ .gama_wd = 2,
+ },
+ .blk_clamp = {
+ .sample_pixel = 1,
+ .dc_sub = 25
+ },
+ .col_pat_field0 = {
+ .olop = CCDC_GREEN_BLUE,
+ .olep = CCDC_BLUE,
+ .elop = CCDC_RED,
+ .elep = CCDC_GREEN_RED
+ },
+ .col_pat_field1 = {
+ .olop = CCDC_GREEN_BLUE,
+ .olep = CCDC_BLUE,
+ .elop = CCDC_RED,
+ .elep = CCDC_GREEN_RED
+ },
+ },
+ },
+ /* YCbCr configuration */
+ .ycbcr = {
+ .win = CCDC_WIN_PAL,
+ .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT,
+ .frm_fmt = CCDC_FRMFMT_INTERLACED,
+ .fid_pol = VPFE_PINPOL_POSITIVE,
+ .vd_pol = VPFE_PINPOL_POSITIVE,
+ .hd_pol = VPFE_PINPOL_POSITIVE,
+ .bt656_enable = 1,
+ .pix_order = CCDC_PIXORDER_CBYCRY,
+ .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED
+ },
+};
+
+
+/* Raw Bayer formats */
+static u32 ccdc_raw_bayer_pix_formats[] =
+ {V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16};
+
+/* Raw YUV formats */
+static u32 ccdc_raw_yuv_pix_formats[] =
+ {V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV};
+
+/* register access routines */
+static inline u32 regr(u32 offset)
+{
+ return __raw_readl(ccdc_cfg.base_addr + offset);
+}
+
+static inline void regw(u32 val, u32 offset)
+{
+ __raw_writel(val, ccdc_cfg.base_addr + offset);
+}
+
+static void ccdc_enable(int en)
+{
+ unsigned int temp;
+ temp = regr(SYNCEN);
+ temp &= (~CCDC_SYNCEN_VDHDEN_MASK);
+ temp |= (en & CCDC_SYNCEN_VDHDEN_MASK);
+ regw(temp, SYNCEN);
+}
+
+static void ccdc_enable_output_to_sdram(int en)
+{
+ unsigned int temp;
+ temp = regr(SYNCEN);
+ temp &= (~(CCDC_SYNCEN_WEN_MASK));
+ temp |= ((en << CCDC_SYNCEN_WEN_SHIFT) & CCDC_SYNCEN_WEN_MASK);
+ regw(temp, SYNCEN);
+}
+
+static void ccdc_config_gain_offset(void)
+{
+ /* configure gain */
+ regw(ccdc_cfg.bayer.gain.r_ye, RYEGAIN);
+ regw(ccdc_cfg.bayer.gain.gr_cy, GRCYGAIN);
+ regw(ccdc_cfg.bayer.gain.gb_g, GBGGAIN);
+ regw(ccdc_cfg.bayer.gain.b_mg, BMGGAIN);
+ /* configure offset */
+ regw(ccdc_cfg.bayer.ccdc_offset, OFFSET);
+}
+
+/*
+ * ccdc_restore_defaults()
+ * This function restore power on defaults in the ccdc registers
+ */
+static int ccdc_restore_defaults(void)
+{
+ int i;
+
+ dev_dbg(ccdc_cfg.dev, "\nstarting ccdc_restore_defaults...");
+ /* set all registers to zero */
+ for (i = 0; i <= CCDC_REG_LAST; i += 4)
+ regw(0, i);
+
+ /* now override the values with power on defaults in registers */
+ regw(MODESET_DEFAULT, MODESET);
+ /* no culling support */
+ regw(CULH_DEFAULT, CULH);
+ regw(CULV_DEFAULT, CULV);
+ /* Set default Gain and Offset */
+ ccdc_cfg.bayer.gain.r_ye = GAIN_DEFAULT;
+ ccdc_cfg.bayer.gain.gb_g = GAIN_DEFAULT;
+ ccdc_cfg.bayer.gain.gr_cy = GAIN_DEFAULT;
+ ccdc_cfg.bayer.gain.b_mg = GAIN_DEFAULT;
+ ccdc_config_gain_offset();
+ regw(OUTCLIP_DEFAULT, OUTCLIP);
+ regw(LSCCFG2_DEFAULT, LSCCFG2);
+ /* select ccdc input */
+ if (vpss_select_ccdc_source(VPSS_CCDCIN)) {
+ dev_dbg(ccdc_cfg.dev, "\ncouldn't select ccdc input source");
+ return -EFAULT;
+ }
+ /* select ccdc clock */
+ if (vpss_enable_clock(VPSS_CCDC_CLOCK, 1) < 0) {
+ dev_dbg(ccdc_cfg.dev, "\ncouldn't enable ccdc clock");
+ return -EFAULT;
+ }
+ dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_restore_defaults...");
+ return 0;
+}
+
+static int ccdc_open(struct device *device)
+{
+ return ccdc_restore_defaults();
+}
+
+static int ccdc_close(struct device *device)
+{
+ /* disable clock */
+ vpss_enable_clock(VPSS_CCDC_CLOCK, 0);
+ /* do nothing for now */
+ return 0;
+}
+/*
+ * ccdc_setwin()
+ * This function will configure the window size to
+ * be capture in CCDC reg.
+ */
+static void ccdc_setwin(struct v4l2_rect *image_win,
+ enum ccdc_frmfmt frm_fmt, int ppc)
+{
+ int horz_start, horz_nr_pixels;
+ int vert_start, vert_nr_lines;
+ int mid_img = 0;
+
+ dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_setwin...");
+
+ /*
+ * ppc - per pixel count. indicates how many pixels per cell
+ * output to SDRAM. example, for ycbcr, it is one y and one c, so 2.
+ * raw capture this is 1
+ */
+ horz_start = image_win->left << (ppc - 1);
+ horz_nr_pixels = ((image_win->width) << (ppc - 1)) - 1;
+
+ /* Writing the horizontal info into the registers */
+ regw(horz_start, SPH);
+ regw(horz_nr_pixels, NPH);
+ vert_start = image_win->top;
+
+ if (frm_fmt == CCDC_FRMFMT_INTERLACED) {
+ vert_nr_lines = (image_win->height >> 1) - 1;
+ vert_start >>= 1;
+ /* Since first line doesn't have any data */
+ vert_start += 1;
+ /* configure VDINT0 and VDINT1 */
+ regw(vert_start, VDINT0);
+ } else {
+ /* Since first line doesn't have any data */
+ vert_start += 1;
+ vert_nr_lines = image_win->height - 1;
+ /* configure VDINT0 and VDINT1 */
+ mid_img = vert_start + (image_win->height / 2);
+ regw(vert_start, VDINT0);
+ regw(mid_img, VDINT1);
+ }
+ regw(vert_start & CCDC_START_VER_ONE_MASK, SLV0);
+ regw(vert_start & CCDC_START_VER_TWO_MASK, SLV1);
+ regw(vert_nr_lines & CCDC_NUM_LINES_VER, NLV);
+ dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_setwin...");
+}
+
+static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam)
+{
+ if (ccdcparam->datasft < CCDC_DATA_NO_SHIFT ||
+ ccdcparam->datasft > CCDC_DATA_SHIFT_6BIT) {
+ dev_dbg(ccdc_cfg.dev, "Invalid value of data shift\n");
+ return -EINVAL;
+ }
+
+ if (ccdcparam->mfilt1 < CCDC_NO_MEDIAN_FILTER1 ||
+ ccdcparam->mfilt1 > CCDC_MEDIAN_FILTER1) {
+ dev_dbg(ccdc_cfg.dev, "Invalid value of median filter1\n");
+ return -EINVAL;
+ }
+
+ if (ccdcparam->mfilt2 < CCDC_NO_MEDIAN_FILTER2 ||
+ ccdcparam->mfilt2 > CCDC_MEDIAN_FILTER2) {
+ dev_dbg(ccdc_cfg.dev, "Invalid value of median filter2\n");
+ return -EINVAL;
+ }
+
+ if ((ccdcparam->med_filt_thres < 0) ||
+ (ccdcparam->med_filt_thres > CCDC_MED_FILT_THRESH)) {
+ dev_dbg(ccdc_cfg.dev,
+ "Invalid value of median filter threshold\n");
+ return -EINVAL;
+ }
+
+ if (ccdcparam->data_sz < CCDC_DATA_16BITS ||
+ ccdcparam->data_sz > CCDC_DATA_8BITS) {
+ dev_dbg(ccdc_cfg.dev, "Invalid value of data size\n");
+ return -EINVAL;
+ }
+
+ if (ccdcparam->alaw.enable) {
+ if (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_13_4 ||
+ ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) {
+ dev_dbg(ccdc_cfg.dev, "Invalid value of ALAW\n");
+ return -EINVAL;
+ }
+ }
+
+ if (ccdcparam->blk_clamp.b_clamp_enable) {
+ if (ccdcparam->blk_clamp.sample_pixel < CCDC_SAMPLE_1PIXELS ||
+ ccdcparam->blk_clamp.sample_pixel > CCDC_SAMPLE_16PIXELS) {
+ dev_dbg(ccdc_cfg.dev,
+ "Invalid value of sample pixel\n");
+ return -EINVAL;
+ }
+ if (ccdcparam->blk_clamp.sample_ln < CCDC_SAMPLE_1LINES ||
+ ccdcparam->blk_clamp.sample_ln > CCDC_SAMPLE_16LINES) {
+ dev_dbg(ccdc_cfg.dev,
+ "Invalid value of sample lines\n");
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+/* Parameter operations */
+static int ccdc_set_params(void __user *params)
+{
+ struct ccdc_config_params_raw ccdc_raw_params;
+ int x;
+
+ /* only raw module parameters can be set through the IOCTL */
+ if (ccdc_cfg.if_type != VPFE_RAW_BAYER)
+ return -EINVAL;
+
+ x = copy_from_user(&ccdc_raw_params, params, sizeof(ccdc_raw_params));
+ if (x) {
+ dev_dbg(ccdc_cfg.dev, "ccdc_set_params: error in copying ccdc"
+ "params, %d\n", x);
+ return -EFAULT;
+ }
+
+ if (!validate_ccdc_param(&ccdc_raw_params)) {
+ memcpy(&ccdc_cfg.bayer.config_params,
+ &ccdc_raw_params,
+ sizeof(ccdc_raw_params));
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/* This function will configure CCDC for YCbCr video capture */
+static void ccdc_config_ycbcr(void)
+{
+ struct ccdc_params_ycbcr *params = &ccdc_cfg.ycbcr;
+ u32 temp;
+
+ /* first set the CCDC power on defaults values in all registers */
+ dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_ycbcr...");
+ ccdc_restore_defaults();
+
+ /* configure pixel format & video frame format */
+ temp = (((params->pix_fmt & CCDC_INPUT_MODE_MASK) <<
+ CCDC_INPUT_MODE_SHIFT) |
+ ((params->frm_fmt & CCDC_FRM_FMT_MASK) <<
+ CCDC_FRM_FMT_SHIFT));
+
+ /* setup BT.656 sync mode */
+ if (params->bt656_enable) {
+ regw(CCDC_REC656IF_BT656_EN, REC656IF);
+ /*
+ * configure the FID, VD, HD pin polarity fld,hd pol positive,
+ * vd negative, 8-bit pack mode
+ */
+ temp |= CCDC_VD_POL_NEGATIVE;
+ } else { /* y/c external sync mode */
+ temp |= (((params->fid_pol & CCDC_FID_POL_MASK) <<
+ CCDC_FID_POL_SHIFT) |
+ ((params->hd_pol & CCDC_HD_POL_MASK) <<
+ CCDC_HD_POL_SHIFT) |
+ ((params->vd_pol & CCDC_VD_POL_MASK) <<
+ CCDC_VD_POL_SHIFT));
+ }
+
+ /* pack the data to 8-bit */
+ temp |= CCDC_DATA_PACK_ENABLE;
+
+ regw(temp, MODESET);
+
+ /* configure video window */
+ ccdc_setwin(&params->win, params->frm_fmt, 2);
+
+ /* configure the order of y cb cr in SD-RAM */
+ temp = (params->pix_order << CCDC_Y8POS_SHIFT);
+ temp |= CCDC_LATCH_ON_VSYNC_DISABLE | CCDC_CCDCFG_FIDMD_NO_LATCH_VSYNC;
+ regw(temp, CCDCFG);
+
+ /*
+ * configure the horizontal line offset. This is done by rounding up
+ * width to a multiple of 16 pixels and multiply by two to account for
+ * y:cb:cr 4:2:2 data
+ */
+ regw(((params->win.width * 2 + 31) >> 5), HSIZE);
+
+ /* configure the memory line offset */
+ if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) {
+ /* two fields are interleaved in memory */
+ regw(CCDC_SDOFST_FIELD_INTERLEAVED, SDOFST);
+ }
+
+ dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_config_ycbcr...\n");
+}
+
+/*
+ * ccdc_config_black_clamp()
+ * configure parameters for Optical Black Clamp
+ */
+static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp)
+{
+ u32 val;
+
+ if (!bclamp->b_clamp_enable) {
+ /* configure DCSub */
+ regw(bclamp->dc_sub & CCDC_BLK_DC_SUB_MASK, DCSUB);
+ regw(0x0000, CLAMP);
+ return;
+ }
+ /* Enable the Black clamping, set sample lines and pixels */
+ val = (bclamp->start_pixel & CCDC_BLK_ST_PXL_MASK) |
+ ((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) <<
+ CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE;
+ regw(val, CLAMP);
+
+ /* If Black clamping is enable then make dcsub 0 */
+ val = (bclamp->sample_ln & CCDC_NUM_LINE_CALC_MASK)
+ << CCDC_NUM_LINE_CALC_SHIFT;
+ regw(val, DCSUB);
+}
+
+/*
+ * ccdc_config_black_compense()
+ * configure parameters for Black Compensation
+ */
+static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp)
+{
+ u32 val;
+
+ val = (bcomp->b & CCDC_BLK_COMP_MASK) |
+ ((bcomp->gb & CCDC_BLK_COMP_MASK) <<
+ CCDC_BLK_COMP_GB_COMP_SHIFT);
+ regw(val, BLKCMP1);
+
+ val = ((bcomp->gr & CCDC_BLK_COMP_MASK) <<
+ CCDC_BLK_COMP_GR_COMP_SHIFT) |
+ ((bcomp->r & CCDC_BLK_COMP_MASK) <<
+ CCDC_BLK_COMP_R_COMP_SHIFT);
+ regw(val, BLKCMP0);
+}
+
+/*
+ * ccdc_write_dfc_entry()
+ * write an entry in the dfc table.
+ */
+int ccdc_write_dfc_entry(int index, struct ccdc_vertical_dft *dfc)
+{
+/* TODO This is to be re-visited and adjusted */
+#define DFC_WRITE_WAIT_COUNT 1000
+ u32 val, count = DFC_WRITE_WAIT_COUNT;
+
+ regw(dfc->dft_corr_vert[index], DFCMEM0);
+ regw(dfc->dft_corr_horz[index], DFCMEM1);
+ regw(dfc->dft_corr_sub1[index], DFCMEM2);
+ regw(dfc->dft_corr_sub2[index], DFCMEM3);
+ regw(dfc->dft_corr_sub3[index], DFCMEM4);
+ /* set WR bit to write */
+ val = regr(DFCMEMCTL) | CCDC_DFCMEMCTL_DFCMWR_MASK;
+ regw(val, DFCMEMCTL);
+
+ /*
+ * Assume, it is very short. If we get an error, we need to
+ * adjust this value
+ */
+ while (regr(DFCMEMCTL) & CCDC_DFCMEMCTL_DFCMWR_MASK)
+ count--;
+ /*
+ * TODO We expect the count to be non-zero to be successful. Adjust
+ * the count if write requires more time
+ */
+
+ if (count) {
+ dev_err(ccdc_cfg.dev, "defect table write timeout !!!\n");
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * ccdc_config_vdfc()
+ * configure parameters for Vertical Defect Correction
+ */
+static int ccdc_config_vdfc(struct ccdc_vertical_dft *dfc)
+{
+ u32 val;
+ int i;
+
+ /* Configure General Defect Correction. The table used is from IPIPE */
+ val = dfc->gen_dft_en & CCDC_DFCCTL_GDFCEN_MASK;
+
+ /* Configure Vertical Defect Correction if needed */
+ if (!dfc->ver_dft_en) {
+ /* Enable only General Defect Correction */
+ regw(val, DFCCTL);
+ return 0;
+ }
+
+ if (dfc->table_size > CCDC_DFT_TABLE_SIZE)
+ return -EINVAL;
+
+ val |= CCDC_DFCCTL_VDFC_DISABLE;
+ val |= (dfc->dft_corr_ctl.vdfcsl & CCDC_DFCCTL_VDFCSL_MASK) <<
+ CCDC_DFCCTL_VDFCSL_SHIFT;
+ val |= (dfc->dft_corr_ctl.vdfcuda & CCDC_DFCCTL_VDFCUDA_MASK) <<
+ CCDC_DFCCTL_VDFCUDA_SHIFT;
+ val |= (dfc->dft_corr_ctl.vdflsft & CCDC_DFCCTL_VDFLSFT_MASK) <<
+ CCDC_DFCCTL_VDFLSFT_SHIFT;
+ regw(val , DFCCTL);
+
+ /* clear address ptr to offset 0 */
+ val = CCDC_DFCMEMCTL_DFCMARST_MASK << CCDC_DFCMEMCTL_DFCMARST_SHIFT;
+
+ /* write defect table entries */
+ for (i = 0; i < dfc->table_size; i++) {
+ /* increment address for non zero index */
+ if (i != 0)
+ val = CCDC_DFCMEMCTL_INC_ADDR;
+ regw(val, DFCMEMCTL);
+ if (ccdc_write_dfc_entry(i, dfc) < 0)
+ return -EFAULT;
+ }
+
+ /* update saturation level and enable dfc */
+ regw(dfc->saturation_ctl & CCDC_VDC_DFCVSAT_MASK, DFCVSAT);
+ val = regr(DFCCTL) | (CCDC_DFCCTL_VDFCEN_MASK <<
+ CCDC_DFCCTL_VDFCEN_SHIFT);
+ regw(val, DFCCTL);
+ return 0;
+}
+
+/*
+ * ccdc_config_csc()
+ * configure parameters for color space conversion
+ * Each register CSCM0-7 has two values in S8Q5 format.
+ */
+static void ccdc_config_csc(struct ccdc_csc *csc)
+{
+ u32 val1, val2;
+ int i;
+
+ if (!csc->enable)
+ return;
+
+ /* Enable the CSC sub-module */
+ regw(CCDC_CSC_ENABLE, CSCCTL);
+
+ /* Converting the co-eff as per the format of the register */
+ for (i = 0; i < CCDC_CSC_COEFF_TABLE_SIZE; i++) {
+ if ((i % 2) == 0) {
+ /* CSCM - LSB */
+ val1 = (csc->coeff[i].integer &
+ CCDC_CSC_COEF_INTEG_MASK)
+ << CCDC_CSC_COEF_INTEG_SHIFT;
+ /*
+ * convert decimal part to binary. Use 2 decimal
+ * precision, user values range from .00 - 0.99
+ */
+ val1 |= (((csc->coeff[i].decimal &
+ CCDC_CSC_COEF_DECIMAL_MASK) *
+ CCDC_CSC_DEC_MAX) / 100);
+ } else {
+
+ /* CSCM - MSB */
+ val2 = (csc->coeff[i].integer &
+ CCDC_CSC_COEF_INTEG_MASK)
+ << CCDC_CSC_COEF_INTEG_SHIFT;
+ val2 |= (((csc->coeff[i].decimal &
+ CCDC_CSC_COEF_DECIMAL_MASK) *
+ CCDC_CSC_DEC_MAX) / 100);
+ val2 <<= CCDC_CSCM_MSB_SHIFT;
+ val2 |= val1;
+ regw(val2, (CSCM0 + ((i - 1) << 1)));
+ }
+ }
+}
+
+/*
+ * ccdc_config_color_patterns()
+ * configure parameters for color patterns
+ */
+static void ccdc_config_color_patterns(struct ccdc_col_pat *pat0,
+ struct ccdc_col_pat *pat1)
+{
+ u32 val;
+
+ val = (pat0->olop | (pat0->olep << 2) | (pat0->elop << 4) |
+ (pat0->elep << 6) | (pat1->olop << 8) | (pat1->olep << 10) |
+ (pat1->elop << 12) | (pat1->elep << 14));
+ regw(val, COLPTN);
+}
+
+/* This function will configure CCDC for Raw mode image capture */
+static int ccdc_config_raw(void)
+{
+ struct ccdc_params_raw *params = &ccdc_cfg.bayer;
+ struct ccdc_config_params_raw *config_params =
+ &ccdc_cfg.bayer.config_params;
+ unsigned int val;
+
+ dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_raw...");
+
+ /* restore power on defaults to register */
+ ccdc_restore_defaults();
+
+ /* CCDCFG register:
+ * set CCD Not to swap input since input is RAW data
+ * set FID detection function to Latch at V-Sync
+ * set WENLOG - ccdc valid area to AND
+ * set TRGSEL to WENBIT
+ * set EXTRG to DISABLE
+ * disable latching function on VSYNC - shadowed registers
+ */
+ regw(CCDC_YCINSWP_RAW | CCDC_CCDCFG_FIDMD_LATCH_VSYNC |
+ CCDC_CCDCFG_WENLOG_AND | CCDC_CCDCFG_TRGSEL_WEN |
+ CCDC_CCDCFG_EXTRG_DISABLE | CCDC_LATCH_ON_VSYNC_DISABLE, CCDCFG);
+
+ /*
+ * Set VDHD direction to input, input type to raw input
+ * normal data polarity, do not use external WEN
+ */
+ val = (CCDC_VDHDOUT_INPUT | CCDC_RAW_IP_MODE | CCDC_DATAPOL_NORMAL |
+ CCDC_EXWEN_DISABLE);
+
+ /*
+ * Configure the vertical sync polarity (MODESET.VDPOL), horizontal
+ * sync polarity (MODESET.HDPOL), field id polarity (MODESET.FLDPOL),
+ * frame format(progressive or interlace), & pixel format (Input mode)
+ */
+ val |= (((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT) |
+ ((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT) |
+ ((params->fid_pol & CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT) |
+ ((params->frm_fmt & CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) |
+ ((params->pix_fmt & CCDC_PIX_FMT_MASK) << CCDC_PIX_FMT_SHIFT));
+
+ /* set pack for alaw compression */
+ if ((config_params->data_sz == CCDC_DATA_8BITS) ||
+ config_params->alaw.enable)
+ val |= CCDC_DATA_PACK_ENABLE;
+
+ /* Configure for LPF */
+ if (config_params->lpf_enable)
+ val |= (config_params->lpf_enable & CCDC_LPF_MASK) <<
+ CCDC_LPF_SHIFT;
+
+ /* Configure the data shift */
+ val |= (config_params->datasft & CCDC_DATASFT_MASK) <<
+ CCDC_DATASFT_SHIFT;
+ regw(val , MODESET);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to MODESET...\n", val);
+
+ /* Configure the Median Filter threshold */
+ regw((config_params->med_filt_thres) & CCDC_MED_FILT_THRESH, MEDFILT);
+
+ /* Configure GAMMAWD register. defaur 11-2, and Mosaic cfa pattern */
+ val = CCDC_GAMMA_BITS_11_2 << CCDC_GAMMAWD_INPUT_SHIFT |
+ CCDC_CFA_MOSAIC;
+
+ /* Enable and configure aLaw register if needed */
+ if (config_params->alaw.enable) {
+ val |= (CCDC_ALAW_ENABLE |
+ ((config_params->alaw.gama_wd &
+ CCDC_ALAW_GAMA_WD_MASK) <<
+ CCDC_GAMMAWD_INPUT_SHIFT));
+ }
+
+ /* Configure Median filter1 & filter2 */
+ val |= ((config_params->mfilt1 << CCDC_MFILT1_SHIFT) |
+ (config_params->mfilt2 << CCDC_MFILT2_SHIFT));
+
+ regw(val, GAMMAWD);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to GAMMAWD...\n", val);
+
+ /* configure video window */
+ ccdc_setwin(&params->win, params->frm_fmt, 1);
+
+ /* Optical Clamp Averaging */
+ ccdc_config_black_clamp(&config_params->blk_clamp);
+
+ /* Black level compensation */
+ ccdc_config_black_compense(&config_params->blk_comp);
+
+ /* Vertical Defect Correction if needed */
+ if (ccdc_config_vdfc(&config_params->vertical_dft) < 0)
+ return -EFAULT;
+
+ /* color space conversion */
+ ccdc_config_csc(&config_params->csc);
+
+ /* color pattern */
+ ccdc_config_color_patterns(&config_params->col_pat_field0,
+ &config_params->col_pat_field1);
+
+ /* Configure the Gain & offset control */
+ ccdc_config_gain_offset();
+
+ dev_dbg(ccdc_cfg.dev, "\nWriting %x to COLPTN...\n", val);
+
+ /* Configure DATAOFST register */
+ val = (config_params->data_offset.horz_offset & CCDC_DATAOFST_MASK) <<
+ CCDC_DATAOFST_H_SHIFT;
+ val |= (config_params->data_offset.vert_offset & CCDC_DATAOFST_MASK) <<
+ CCDC_DATAOFST_V_SHIFT;
+ regw(val, DATAOFST);
+
+ /* configuring HSIZE register */
+ val = (params->horz_flip_enable & CCDC_HSIZE_FLIP_MASK) <<
+ CCDC_HSIZE_FLIP_SHIFT;
+
+ /* If pack 8 is enable then 1 pixel will take 1 byte */
+ if ((config_params->data_sz == CCDC_DATA_8BITS) ||
+ config_params->alaw.enable) {
+ val |= (((params->win.width) + 31) >> 5) &
+ CCDC_HSIZE_VAL_MASK;
+
+ /* adjust to multiple of 32 */
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to HSIZE...\n",
+ (((params->win.width) + 31) >> 5) &
+ CCDC_HSIZE_VAL_MASK);
+ } else {
+ /* else one pixel will take 2 byte */
+ val |= (((params->win.width * 2) + 31) >> 5) &
+ CCDC_HSIZE_VAL_MASK;
+
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to HSIZE...\n",
+ (((params->win.width * 2) + 31) >> 5) &
+ CCDC_HSIZE_VAL_MASK);
+ }
+ regw(val, HSIZE);
+
+ /* Configure SDOFST register */
+ if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) {
+ if (params->image_invert_enable) {
+ /* For interlace inverse mode */
+ regw(CCDC_SDOFST_INTERLACE_INVERSE, SDOFST);
+ dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n",
+ CCDC_SDOFST_INTERLACE_INVERSE);
+ } else {
+ /* For interlace non inverse mode */
+ regw(CCDC_SDOFST_INTERLACE_NORMAL, SDOFST);
+ dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n",
+ CCDC_SDOFST_INTERLACE_NORMAL);
+ }
+ } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) {
+ if (params->image_invert_enable) {
+ /* For progessive inverse mode */
+ regw(CCDC_SDOFST_PROGRESSIVE_INVERSE, SDOFST);
+ dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n",
+ CCDC_SDOFST_PROGRESSIVE_INVERSE);
+ } else {
+ /* For progessive non inverse mode */
+ regw(CCDC_SDOFST_PROGRESSIVE_NORMAL, SDOFST);
+ dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n",
+ CCDC_SDOFST_PROGRESSIVE_NORMAL);
+ }
+ }
+ dev_dbg(ccdc_cfg.dev, "\nend of ccdc_config_raw...");
+ return 0;
+}
+
+static int ccdc_configure(void)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ return ccdc_config_raw();
+ else
+ ccdc_config_ycbcr();
+ return 0;
+}
+
+static int ccdc_set_buftype(enum ccdc_buftype buf_type)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ ccdc_cfg.bayer.buf_type = buf_type;
+ else
+ ccdc_cfg.ycbcr.buf_type = buf_type;
+ return 0;
+}
+static enum ccdc_buftype ccdc_get_buftype(void)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ return ccdc_cfg.bayer.buf_type;
+ return ccdc_cfg.ycbcr.buf_type;
+}
+
+static int ccdc_enum_pix(u32 *pix, int i)
+{
+ int ret = -EINVAL;
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
+ if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) {
+ *pix = ccdc_raw_bayer_pix_formats[i];
+ ret = 0;
+ }
+ } else {
+ if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) {
+ *pix = ccdc_raw_yuv_pix_formats[i];
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
+static int ccdc_set_pixel_format(u32 pixfmt)
+{
+ struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw;
+
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
+ ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW;
+ if (pixfmt == V4L2_PIX_FMT_SBGGR8)
+ alaw->enable = 1;
+ else if (pixfmt != V4L2_PIX_FMT_SBGGR16)
+ return -EINVAL;
+ } else {
+ if (pixfmt == V4L2_PIX_FMT_YUYV)
+ ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR;
+ else if (pixfmt == V4L2_PIX_FMT_UYVY)
+ ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
+ else
+ return -EINVAL;
+ }
+ return 0;
+}
+static u32 ccdc_get_pixel_format(void)
+{
+ struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw;
+ u32 pixfmt;
+
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ if (alaw->enable)
+ pixfmt = V4L2_PIX_FMT_SBGGR8;
+ else
+ pixfmt = V4L2_PIX_FMT_SBGGR16;
+ else {
+ if (ccdc_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR)
+ pixfmt = V4L2_PIX_FMT_YUYV;
+ else
+ pixfmt = V4L2_PIX_FMT_UYVY;
+ }
+ return pixfmt;
+}
+static int ccdc_set_image_window(struct v4l2_rect *win)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ ccdc_cfg.bayer.win = *win;
+ else
+ ccdc_cfg.ycbcr.win = *win;
+ return 0;
+}
+
+static void ccdc_get_image_window(struct v4l2_rect *win)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ *win = ccdc_cfg.bayer.win;
+ else
+ *win = ccdc_cfg.ycbcr.win;
+}
+
+static unsigned int ccdc_get_line_length(void)
+{
+ struct ccdc_config_params_raw *config_params =
+ &ccdc_cfg.bayer.config_params;
+ unsigned int len;
+
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
+ if ((config_params->alaw.enable) ||
+ (config_params->data_sz == CCDC_DATA_8BITS))
+ len = ccdc_cfg.bayer.win.width;
+ else
+ len = ccdc_cfg.bayer.win.width * 2;
+ } else
+ len = ccdc_cfg.ycbcr.win.width * 2;
+ return ALIGN(len, 32);
+}
+
+static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ ccdc_cfg.bayer.frm_fmt = frm_fmt;
+ else
+ ccdc_cfg.ycbcr.frm_fmt = frm_fmt;
+ return 0;
+}
+
+static enum ccdc_frmfmt ccdc_get_frame_format(void)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ return ccdc_cfg.bayer.frm_fmt;
+ else
+ return ccdc_cfg.ycbcr.frm_fmt;
+}
+
+static int ccdc_getfid(void)
+{
+ return (regr(MODESET) >> 15) & 1;
+}
+
+/* misc operations */
+static inline void ccdc_setfbaddr(unsigned long addr)
+{
+ regw((addr >> 21) & 0x007f, STADRH);
+ regw((addr >> 5) & 0x0ffff, STADRL);
+}
+
+static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params)
+{
+ ccdc_cfg.if_type = params->if_type;
+
+ switch (params->if_type) {
+ case VPFE_BT656:
+ case VPFE_YCBCR_SYNC_16:
+ case VPFE_YCBCR_SYNC_8:
+ ccdc_cfg.ycbcr.vd_pol = params->vdpol;
+ ccdc_cfg.ycbcr.hd_pol = params->hdpol;
+ break;
+ default:
+ /* TODO add support for raw bayer here */
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static struct ccdc_hw_device ccdc_hw_dev = {
+ .name = "DM355 CCDC",
+ .owner = THIS_MODULE,
+ .hw_ops = {
+ .open = ccdc_open,
+ .close = ccdc_close,
+ .enable = ccdc_enable,
+ .enable_out_to_sdram = ccdc_enable_output_to_sdram,
+ .set_hw_if_params = ccdc_set_hw_if_params,
+ .set_params = ccdc_set_params,
+ .configure = ccdc_configure,
+ .set_buftype = ccdc_set_buftype,
+ .get_buftype = ccdc_get_buftype,
+ .enum_pix = ccdc_enum_pix,
+ .set_pixel_format = ccdc_set_pixel_format,
+ .get_pixel_format = ccdc_get_pixel_format,
+ .set_frame_format = ccdc_set_frame_format,
+ .get_frame_format = ccdc_get_frame_format,
+ .set_image_window = ccdc_set_image_window,
+ .get_image_window = ccdc_get_image_window,
+ .get_line_length = ccdc_get_line_length,
+ .setfbaddr = ccdc_setfbaddr,
+ .getfid = ccdc_getfid,
+ },
+};
+
+static int __init dm355_ccdc_probe(struct platform_device *pdev)
+{
+ void (*setup_pinmux)(void);
+ struct resource *res;
+ int status = 0;
+
+ /*
+ * first try to register with vpfe. If not correct platform, then we
+ * don't have to iomap
+ */
+ status = vpfe_register_ccdc_device(&ccdc_hw_dev);
+ if (status < 0)
+ return status;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ status = -ENODEV;
+ goto fail_nores;
+ }
+
+ res = request_mem_region(res->start, resource_size(res), res->name);
+ if (!res) {
+ status = -EBUSY;
+ goto fail_nores;
+ }
+
+ ccdc_cfg.base_addr = ioremap_nocache(res->start, resource_size(res));
+ if (!ccdc_cfg.base_addr) {
+ status = -ENOMEM;
+ goto fail_nomem;
+ }
+
+ /* Get and enable Master clock */
+ ccdc_cfg.mclk = clk_get(&pdev->dev, "master");
+ if (IS_ERR(ccdc_cfg.mclk)) {
+ status = PTR_ERR(ccdc_cfg.mclk);
+ goto fail_nomap;
+ }
+ if (clk_enable(ccdc_cfg.mclk)) {
+ status = -ENODEV;
+ goto fail_mclk;
+ }
+
+ /* Get and enable Slave clock */
+ ccdc_cfg.sclk = clk_get(&pdev->dev, "slave");
+ if (IS_ERR(ccdc_cfg.sclk)) {
+ status = PTR_ERR(ccdc_cfg.sclk);
+ goto fail_mclk;
+ }
+ if (clk_enable(ccdc_cfg.sclk)) {
+ status = -ENODEV;
+ goto fail_sclk;
+ }
+
+ /* Platform data holds setup_pinmux function ptr */
+ if (NULL == pdev->dev.platform_data) {
+ status = -ENODEV;
+ goto fail_sclk;
+ }
+ setup_pinmux = pdev->dev.platform_data;
+ /*
+ * setup Mux configuration for ccdc which may be different for
+ * different SoCs using this CCDC
+ */
+ setup_pinmux();
+ ccdc_cfg.dev = &pdev->dev;
+ printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name);
+ return 0;
+fail_sclk:
+ clk_put(ccdc_cfg.sclk);
+fail_mclk:
+ clk_put(ccdc_cfg.mclk);
+fail_nomap:
+ iounmap(ccdc_cfg.base_addr);
+fail_nomem:
+ release_mem_region(res->start, resource_size(res));
+fail_nores:
+ vpfe_unregister_ccdc_device(&ccdc_hw_dev);
+ return status;
+}
+
+static int dm355_ccdc_remove(struct platform_device *pdev)
+{
+ struct resource *res;
+
+ clk_put(ccdc_cfg.mclk);
+ clk_put(ccdc_cfg.sclk);
+ iounmap(ccdc_cfg.base_addr);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res)
+ release_mem_region(res->start, resource_size(res));
+ vpfe_unregister_ccdc_device(&ccdc_hw_dev);
+ return 0;
+}
+
+static struct platform_driver dm355_ccdc_driver = {
+ .driver = {
+ .name = "dm355_ccdc",
+ .owner = THIS_MODULE,
+ },
+ .remove = __devexit_p(dm355_ccdc_remove),
+ .probe = dm355_ccdc_probe,
+};
+
+module_platform_driver(dm355_ccdc_driver);
diff --git a/drivers/media/video/davinci/dm355_ccdc_regs.h b/drivers/media/video/davinci/dm355_ccdc_regs.h
new file mode 100644
index 00000000..d6d2ef05
--- /dev/null
+++ b/drivers/media/video/davinci/dm355_ccdc_regs.h
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2005-2009 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _DM355_CCDC_REGS_H
+#define _DM355_CCDC_REGS_H
+
+/**************************************************************************\
+* Register OFFSET Definitions
+\**************************************************************************/
+#define SYNCEN 0x00
+#define MODESET 0x04
+#define HDWIDTH 0x08
+#define VDWIDTH 0x0c
+#define PPLN 0x10
+#define LPFR 0x14
+#define SPH 0x18
+#define NPH 0x1c
+#define SLV0 0x20
+#define SLV1 0x24
+#define NLV 0x28
+#define CULH 0x2c
+#define CULV 0x30
+#define HSIZE 0x34
+#define SDOFST 0x38
+#define STADRH 0x3c
+#define STADRL 0x40
+#define CLAMP 0x44
+#define DCSUB 0x48
+#define COLPTN 0x4c
+#define BLKCMP0 0x50
+#define BLKCMP1 0x54
+#define MEDFILT 0x58
+#define RYEGAIN 0x5c
+#define GRCYGAIN 0x60
+#define GBGGAIN 0x64
+#define BMGGAIN 0x68
+#define OFFSET 0x6c
+#define OUTCLIP 0x70
+#define VDINT0 0x74
+#define VDINT1 0x78
+#define RSV0 0x7c
+#define GAMMAWD 0x80
+#define REC656IF 0x84
+#define CCDCFG 0x88
+#define FMTCFG 0x8c
+#define FMTPLEN 0x90
+#define FMTSPH 0x94
+#define FMTLNH 0x98
+#define FMTSLV 0x9c
+#define FMTLNV 0xa0
+#define FMTRLEN 0xa4
+#define FMTHCNT 0xa8
+#define FMT_ADDR_PTR_B 0xac
+#define FMT_ADDR_PTR(i) (FMT_ADDR_PTR_B + (i * 4))
+#define FMTPGM_VF0 0xcc
+#define FMTPGM_VF1 0xd0
+#define FMTPGM_AP0 0xd4
+#define FMTPGM_AP1 0xd8
+#define FMTPGM_AP2 0xdc
+#define FMTPGM_AP3 0xe0
+#define FMTPGM_AP4 0xe4
+#define FMTPGM_AP5 0xe8
+#define FMTPGM_AP6 0xec
+#define FMTPGM_AP7 0xf0
+#define LSCCFG1 0xf4
+#define LSCCFG2 0xf8
+#define LSCH0 0xfc
+#define LSCV0 0x100
+#define LSCKH 0x104
+#define LSCKV 0x108
+#define LSCMEMCTL 0x10c
+#define LSCMEMD 0x110
+#define LSCMEMQ 0x114
+#define DFCCTL 0x118
+#define DFCVSAT 0x11c
+#define DFCMEMCTL 0x120
+#define DFCMEM0 0x124
+#define DFCMEM1 0x128
+#define DFCMEM2 0x12c
+#define DFCMEM3 0x130
+#define DFCMEM4 0x134
+#define CSCCTL 0x138
+#define CSCM0 0x13c
+#define CSCM1 0x140
+#define CSCM2 0x144
+#define CSCM3 0x148
+#define CSCM4 0x14c
+#define CSCM5 0x150
+#define CSCM6 0x154
+#define CSCM7 0x158
+#define DATAOFST 0x15c
+#define CCDC_REG_LAST DATAOFST
+/**************************************************************
+* Define for various register bit mask and shifts for CCDC
+*
+**************************************************************/
+#define CCDC_RAW_IP_MODE 0
+#define CCDC_VDHDOUT_INPUT 0
+#define CCDC_YCINSWP_RAW (0 << 4)
+#define CCDC_EXWEN_DISABLE 0
+#define CCDC_DATAPOL_NORMAL 0
+#define CCDC_CCDCFG_FIDMD_LATCH_VSYNC 0
+#define CCDC_CCDCFG_FIDMD_NO_LATCH_VSYNC (1 << 6)
+#define CCDC_CCDCFG_WENLOG_AND 0
+#define CCDC_CCDCFG_TRGSEL_WEN 0
+#define CCDC_CCDCFG_EXTRG_DISABLE 0
+#define CCDC_CFA_MOSAIC 0
+#define CCDC_Y8POS_SHIFT 11
+
+#define CCDC_VDC_DFCVSAT_MASK 0x3fff
+#define CCDC_DATAOFST_MASK 0x0ff
+#define CCDC_DATAOFST_H_SHIFT 0
+#define CCDC_DATAOFST_V_SHIFT 8
+#define CCDC_GAMMAWD_CFA_MASK 1
+#define CCDC_GAMMAWD_CFA_SHIFT 5
+#define CCDC_GAMMAWD_INPUT_SHIFT 2
+#define CCDC_FID_POL_MASK 1
+#define CCDC_FID_POL_SHIFT 4
+#define CCDC_HD_POL_MASK 1
+#define CCDC_HD_POL_SHIFT 3
+#define CCDC_VD_POL_MASK 1
+#define CCDC_VD_POL_SHIFT 2
+#define CCDC_VD_POL_NEGATIVE (1 << 2)
+#define CCDC_FRM_FMT_MASK 1
+#define CCDC_FRM_FMT_SHIFT 7
+#define CCDC_DATA_SZ_MASK 7
+#define CCDC_DATA_SZ_SHIFT 8
+#define CCDC_VDHDOUT_MASK 1
+#define CCDC_VDHDOUT_SHIFT 0
+#define CCDC_EXWEN_MASK 1
+#define CCDC_EXWEN_SHIFT 5
+#define CCDC_INPUT_MODE_MASK 3
+#define CCDC_INPUT_MODE_SHIFT 12
+#define CCDC_PIX_FMT_MASK 3
+#define CCDC_PIX_FMT_SHIFT 12
+#define CCDC_DATAPOL_MASK 1
+#define CCDC_DATAPOL_SHIFT 6
+#define CCDC_WEN_ENABLE (1 << 1)
+#define CCDC_VDHDEN_ENABLE (1 << 16)
+#define CCDC_LPF_ENABLE (1 << 14)
+#define CCDC_ALAW_ENABLE 1
+#define CCDC_ALAW_GAMA_WD_MASK 7
+#define CCDC_REC656IF_BT656_EN 3
+
+#define CCDC_FMTCFG_FMTMODE_MASK 3
+#define CCDC_FMTCFG_FMTMODE_SHIFT 1
+#define CCDC_FMTCFG_LNUM_MASK 3
+#define CCDC_FMTCFG_LNUM_SHIFT 4
+#define CCDC_FMTCFG_ADDRINC_MASK 7
+#define CCDC_FMTCFG_ADDRINC_SHIFT 8
+
+#define CCDC_CCDCFG_FIDMD_SHIFT 6
+#define CCDC_CCDCFG_WENLOG_SHIFT 8
+#define CCDC_CCDCFG_TRGSEL_SHIFT 9
+#define CCDC_CCDCFG_EXTRG_SHIFT 10
+#define CCDC_CCDCFG_MSBINVI_SHIFT 13
+
+#define CCDC_HSIZE_FLIP_SHIFT 12
+#define CCDC_HSIZE_FLIP_MASK 1
+#define CCDC_HSIZE_VAL_MASK 0xFFF
+#define CCDC_SDOFST_FIELD_INTERLEAVED 0x249
+#define CCDC_SDOFST_INTERLACE_INVERSE 0x4B6D
+#define CCDC_SDOFST_INTERLACE_NORMAL 0x0B6D
+#define CCDC_SDOFST_PROGRESSIVE_INVERSE 0x4000
+#define CCDC_SDOFST_PROGRESSIVE_NORMAL 0
+#define CCDC_START_PX_HOR_MASK 0x7FFF
+#define CCDC_NUM_PX_HOR_MASK 0x7FFF
+#define CCDC_START_VER_ONE_MASK 0x7FFF
+#define CCDC_START_VER_TWO_MASK 0x7FFF
+#define CCDC_NUM_LINES_VER 0x7FFF
+
+#define CCDC_BLK_CLAMP_ENABLE (1 << 15)
+#define CCDC_BLK_SGAIN_MASK 0x1F
+#define CCDC_BLK_ST_PXL_MASK 0x1FFF
+#define CCDC_BLK_SAMPLE_LN_MASK 3
+#define CCDC_BLK_SAMPLE_LN_SHIFT 13
+
+#define CCDC_NUM_LINE_CALC_MASK 3
+#define CCDC_NUM_LINE_CALC_SHIFT 14
+
+#define CCDC_BLK_DC_SUB_MASK 0x3FFF
+#define CCDC_BLK_COMP_MASK 0xFF
+#define CCDC_BLK_COMP_GB_COMP_SHIFT 8
+#define CCDC_BLK_COMP_GR_COMP_SHIFT 0
+#define CCDC_BLK_COMP_R_COMP_SHIFT 8
+#define CCDC_LATCH_ON_VSYNC_DISABLE (1 << 15)
+#define CCDC_LATCH_ON_VSYNC_ENABLE (0 << 15)
+#define CCDC_FPC_ENABLE (1 << 15)
+#define CCDC_FPC_FPC_NUM_MASK 0x7FFF
+#define CCDC_DATA_PACK_ENABLE (1 << 11)
+#define CCDC_FMT_HORZ_FMTLNH_MASK 0x1FFF
+#define CCDC_FMT_HORZ_FMTSPH_MASK 0x1FFF
+#define CCDC_FMT_HORZ_FMTSPH_SHIFT 16
+#define CCDC_FMT_VERT_FMTLNV_MASK 0x1FFF
+#define CCDC_FMT_VERT_FMTSLV_MASK 0x1FFF
+#define CCDC_FMT_VERT_FMTSLV_SHIFT 16
+#define CCDC_VP_OUT_VERT_NUM_MASK 0x3FFF
+#define CCDC_VP_OUT_VERT_NUM_SHIFT 17
+#define CCDC_VP_OUT_HORZ_NUM_MASK 0x1FFF
+#define CCDC_VP_OUT_HORZ_NUM_SHIFT 4
+#define CCDC_VP_OUT_HORZ_ST_MASK 0xF
+
+#define CCDC_CSC_COEF_INTEG_MASK 7
+#define CCDC_CSC_COEF_DECIMAL_MASK 0x1f
+#define CCDC_CSC_COEF_INTEG_SHIFT 5
+#define CCDC_CSCM_MSB_SHIFT 8
+#define CCDC_CSC_ENABLE 1
+#define CCDC_CSC_DEC_MAX 32
+
+#define CCDC_MFILT1_SHIFT 10
+#define CCDC_MFILT2_SHIFT 8
+#define CCDC_MED_FILT_THRESH 0x3FFF
+#define CCDC_LPF_MASK 1
+#define CCDC_LPF_SHIFT 14
+#define CCDC_OFFSET_MASK 0x3FF
+#define CCDC_DATASFT_MASK 7
+#define CCDC_DATASFT_SHIFT 8
+
+#define CCDC_DF_ENABLE 1
+
+#define CCDC_FMTPLEN_P0_MASK 0xF
+#define CCDC_FMTPLEN_P1_MASK 0xF
+#define CCDC_FMTPLEN_P2_MASK 7
+#define CCDC_FMTPLEN_P3_MASK 7
+#define CCDC_FMTPLEN_P0_SHIFT 0
+#define CCDC_FMTPLEN_P1_SHIFT 4
+#define CCDC_FMTPLEN_P2_SHIFT 8
+#define CCDC_FMTPLEN_P3_SHIFT 12
+
+#define CCDC_FMTSPH_MASK 0x1FFF
+#define CCDC_FMTLNH_MASK 0x1FFF
+#define CCDC_FMTSLV_MASK 0x1FFF
+#define CCDC_FMTLNV_MASK 0x7FFF
+#define CCDC_FMTRLEN_MASK 0x1FFF
+#define CCDC_FMTHCNT_MASK 0x1FFF
+
+#define CCDC_ADP_INIT_MASK 0x1FFF
+#define CCDC_ADP_LINE_SHIFT 13
+#define CCDC_ADP_LINE_MASK 3
+#define CCDC_FMTPGN_APTR_MASK 7
+
+#define CCDC_DFCCTL_GDFCEN_MASK 1
+#define CCDC_DFCCTL_VDFCEN_MASK 1
+#define CCDC_DFCCTL_VDFC_DISABLE (0 << 4)
+#define CCDC_DFCCTL_VDFCEN_SHIFT 4
+#define CCDC_DFCCTL_VDFCSL_MASK 3
+#define CCDC_DFCCTL_VDFCSL_SHIFT 5
+#define CCDC_DFCCTL_VDFCUDA_MASK 1
+#define CCDC_DFCCTL_VDFCUDA_SHIFT 7
+#define CCDC_DFCCTL_VDFLSFT_MASK 3
+#define CCDC_DFCCTL_VDFLSFT_SHIFT 8
+#define CCDC_DFCMEMCTL_DFCMARST_MASK 1
+#define CCDC_DFCMEMCTL_DFCMARST_SHIFT 2
+#define CCDC_DFCMEMCTL_DFCMWR_MASK 1
+#define CCDC_DFCMEMCTL_DFCMWR_SHIFT 0
+#define CCDC_DFCMEMCTL_INC_ADDR (0 << 2)
+
+#define CCDC_LSCCFG_GFTSF_MASK 7
+#define CCDC_LSCCFG_GFTSF_SHIFT 1
+#define CCDC_LSCCFG_GFTINV_MASK 0xf
+#define CCDC_LSCCFG_GFTINV_SHIFT 4
+#define CCDC_LSC_GFTABLE_SEL_MASK 3
+#define CCDC_LSC_GFTABLE_EPEL_SHIFT 8
+#define CCDC_LSC_GFTABLE_OPEL_SHIFT 10
+#define CCDC_LSC_GFTABLE_EPOL_SHIFT 12
+#define CCDC_LSC_GFTABLE_OPOL_SHIFT 14
+#define CCDC_LSC_GFMODE_MASK 3
+#define CCDC_LSC_GFMODE_SHIFT 4
+#define CCDC_LSC_DISABLE 0
+#define CCDC_LSC_ENABLE 1
+#define CCDC_LSC_TABLE1_SLC 0
+#define CCDC_LSC_TABLE2_SLC 1
+#define CCDC_LSC_TABLE3_SLC 2
+#define CCDC_LSC_MEMADDR_RESET (1 << 2)
+#define CCDC_LSC_MEMADDR_INCR (0 << 2)
+#define CCDC_LSC_FRAC_MASK_T1 0xFF
+#define CCDC_LSC_INT_MASK 3
+#define CCDC_LSC_FRAC_MASK 0x3FFF
+#define CCDC_LSC_CENTRE_MASK 0x3FFF
+#define CCDC_LSC_COEF_MASK 0xff
+#define CCDC_LSC_COEFL_SHIFT 0
+#define CCDC_LSC_COEFU_SHIFT 8
+#define CCDC_GAIN_MASK 0x7FF
+#define CCDC_SYNCEN_VDHDEN_MASK (1 << 0)
+#define CCDC_SYNCEN_WEN_MASK (1 << 1)
+#define CCDC_SYNCEN_WEN_SHIFT 1
+
+/* Power on Defaults in hardware */
+#define MODESET_DEFAULT 0x200
+#define CULH_DEFAULT 0xFFFF
+#define CULV_DEFAULT 0xFF
+#define GAIN_DEFAULT 256
+#define OUTCLIP_DEFAULT 0x3FFF
+#define LSCCFG2_DEFAULT 0xE
+
+#endif
diff --git a/drivers/media/video/davinci/dm644x_ccdc.c b/drivers/media/video/davinci/dm644x_ccdc.c
new file mode 100644
index 00000000..9303fe55
--- /dev/null
+++ b/drivers/media/video/davinci/dm644x_ccdc.c
@@ -0,0 +1,1081 @@
+/*
+ * Copyright (C) 2006-2009 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * CCDC hardware module for DM6446
+ * ------------------------------
+ *
+ * This module is for configuring CCD controller of DM6446 VPFE to capture
+ * Raw yuv or Bayer RGB data from a decoder. CCDC has several modules
+ * such as Defect Pixel Correction, Color Space Conversion etc to
+ * pre-process the Raw Bayer RGB data, before writing it to SDRAM. This
+ * module also allows application to configure individual
+ * module parameters through VPFE_CMD_S_CCDC_RAW_PARAMS IOCTL.
+ * To do so, application includes dm644x_ccdc.h and vpfe_capture.h header
+ * files. The setparams() API is called by vpfe_capture driver
+ * to configure module parameters. This file is named DM644x so that other
+ * variants such DM6443 may be supported using the same module.
+ *
+ * TODO: Test Raw bayer parameter settings and bayer capture
+ * Split module parameter structure to module specific ioctl structs
+ * investigate if enum used for user space type definition
+ * to be replaced by #defines or integer
+ */
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/videodev2.h>
+#include <linux/gfp.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/module.h>
+
+#include <media/davinci/dm644x_ccdc.h>
+#include <media/davinci/vpss.h>
+
+#include "dm644x_ccdc_regs.h"
+#include "ccdc_hw_device.h"
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("CCDC Driver for DM6446");
+MODULE_AUTHOR("Texas Instruments");
+
+static struct ccdc_oper_config {
+ struct device *dev;
+ /* CCDC interface type */
+ enum vpfe_hw_if_type if_type;
+ /* Raw Bayer configuration */
+ struct ccdc_params_raw bayer;
+ /* YCbCr configuration */
+ struct ccdc_params_ycbcr ycbcr;
+ /* Master clock */
+ struct clk *mclk;
+ /* slave clock */
+ struct clk *sclk;
+ /* ccdc base address */
+ void __iomem *base_addr;
+} ccdc_cfg = {
+ /* Raw configurations */
+ .bayer = {
+ .pix_fmt = CCDC_PIXFMT_RAW,
+ .frm_fmt = CCDC_FRMFMT_PROGRESSIVE,
+ .win = CCDC_WIN_VGA,
+ .fid_pol = VPFE_PINPOL_POSITIVE,
+ .vd_pol = VPFE_PINPOL_POSITIVE,
+ .hd_pol = VPFE_PINPOL_POSITIVE,
+ .config_params = {
+ .data_sz = CCDC_DATA_10BITS,
+ },
+ },
+ .ycbcr = {
+ .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT,
+ .frm_fmt = CCDC_FRMFMT_INTERLACED,
+ .win = CCDC_WIN_PAL,
+ .fid_pol = VPFE_PINPOL_POSITIVE,
+ .vd_pol = VPFE_PINPOL_POSITIVE,
+ .hd_pol = VPFE_PINPOL_POSITIVE,
+ .bt656_enable = 1,
+ .pix_order = CCDC_PIXORDER_CBYCRY,
+ .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED
+ },
+};
+
+#define CCDC_MAX_RAW_YUV_FORMATS 2
+
+/* Raw Bayer formats */
+static u32 ccdc_raw_bayer_pix_formats[] =
+ {V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16};
+
+/* Raw YUV formats */
+static u32 ccdc_raw_yuv_pix_formats[] =
+ {V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV};
+
+/* CCDC Save/Restore context */
+static u32 ccdc_ctx[CCDC_REG_END / sizeof(u32)];
+
+/* register access routines */
+static inline u32 regr(u32 offset)
+{
+ return __raw_readl(ccdc_cfg.base_addr + offset);
+}
+
+static inline void regw(u32 val, u32 offset)
+{
+ __raw_writel(val, ccdc_cfg.base_addr + offset);
+}
+
+static void ccdc_enable(int flag)
+{
+ regw(flag, CCDC_PCR);
+}
+
+static void ccdc_enable_vport(int flag)
+{
+ if (flag)
+ /* enable video port */
+ regw(CCDC_ENABLE_VIDEO_PORT, CCDC_FMTCFG);
+ else
+ regw(CCDC_DISABLE_VIDEO_PORT, CCDC_FMTCFG);
+}
+
+/*
+ * ccdc_setwin()
+ * This function will configure the window size
+ * to be capture in CCDC reg
+ */
+void ccdc_setwin(struct v4l2_rect *image_win,
+ enum ccdc_frmfmt frm_fmt,
+ int ppc)
+{
+ int horz_start, horz_nr_pixels;
+ int vert_start, vert_nr_lines;
+ int val = 0, mid_img = 0;
+
+ dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_setwin...");
+ /*
+ * ppc - per pixel count. indicates how many pixels per cell
+ * output to SDRAM. example, for ycbcr, it is one y and one c, so 2.
+ * raw capture this is 1
+ */
+ horz_start = image_win->left << (ppc - 1);
+ horz_nr_pixels = (image_win->width << (ppc - 1)) - 1;
+ regw((horz_start << CCDC_HORZ_INFO_SPH_SHIFT) | horz_nr_pixels,
+ CCDC_HORZ_INFO);
+
+ vert_start = image_win->top;
+
+ if (frm_fmt == CCDC_FRMFMT_INTERLACED) {
+ vert_nr_lines = (image_win->height >> 1) - 1;
+ vert_start >>= 1;
+ /* Since first line doesn't have any data */
+ vert_start += 1;
+ /* configure VDINT0 */
+ val = (vert_start << CCDC_VDINT_VDINT0_SHIFT);
+ regw(val, CCDC_VDINT);
+
+ } else {
+ /* Since first line doesn't have any data */
+ vert_start += 1;
+ vert_nr_lines = image_win->height - 1;
+ /*
+ * configure VDINT0 and VDINT1. VDINT1 will be at half
+ * of image height
+ */
+ mid_img = vert_start + (image_win->height / 2);
+ val = (vert_start << CCDC_VDINT_VDINT0_SHIFT) |
+ (mid_img & CCDC_VDINT_VDINT1_MASK);
+ regw(val, CCDC_VDINT);
+
+ }
+ regw((vert_start << CCDC_VERT_START_SLV0_SHIFT) | vert_start,
+ CCDC_VERT_START);
+ regw(vert_nr_lines, CCDC_VERT_LINES);
+ dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_setwin...");
+}
+
+static void ccdc_readregs(void)
+{
+ unsigned int val = 0;
+
+ val = regr(CCDC_ALAW);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to ALAW...\n", val);
+ val = regr(CCDC_CLAMP);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to CLAMP...\n", val);
+ val = regr(CCDC_DCSUB);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to DCSUB...\n", val);
+ val = regr(CCDC_BLKCMP);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to BLKCMP...\n", val);
+ val = regr(CCDC_FPC_ADDR);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FPC_ADDR...\n", val);
+ val = regr(CCDC_FPC);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FPC...\n", val);
+ val = regr(CCDC_FMTCFG);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMTCFG...\n", val);
+ val = regr(CCDC_COLPTN);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to COLPTN...\n", val);
+ val = regr(CCDC_FMT_HORZ);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMT_HORZ...\n", val);
+ val = regr(CCDC_FMT_VERT);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMT_VERT...\n", val);
+ val = regr(CCDC_HSIZE_OFF);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to HSIZE_OFF...\n", val);
+ val = regr(CCDC_SDOFST);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to SDOFST...\n", val);
+ val = regr(CCDC_VP_OUT);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VP_OUT...\n", val);
+ val = regr(CCDC_SYN_MODE);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to SYN_MODE...\n", val);
+ val = regr(CCDC_HORZ_INFO);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to HORZ_INFO...\n", val);
+ val = regr(CCDC_VERT_START);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VERT_START...\n", val);
+ val = regr(CCDC_VERT_LINES);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VERT_LINES...\n", val);
+}
+
+static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam)
+{
+ if (ccdcparam->alaw.enable) {
+ if ((ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) ||
+ (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_15_6) ||
+ (ccdcparam->alaw.gama_wd < ccdcparam->data_sz)) {
+ dev_dbg(ccdc_cfg.dev, "\nInvalid data line select");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int ccdc_update_raw_params(struct ccdc_config_params_raw *raw_params)
+{
+ struct ccdc_config_params_raw *config_params =
+ &ccdc_cfg.bayer.config_params;
+ unsigned int *fpc_virtaddr = NULL;
+ unsigned int *fpc_physaddr = NULL;
+
+ memcpy(config_params, raw_params, sizeof(*raw_params));
+ /*
+ * allocate memory for fault pixel table and copy the user
+ * values to the table
+ */
+ if (!config_params->fault_pxl.enable)
+ return 0;
+
+ fpc_physaddr = (unsigned int *)config_params->fault_pxl.fpc_table_addr;
+ fpc_virtaddr = (unsigned int *)phys_to_virt(
+ (unsigned long)fpc_physaddr);
+ /*
+ * Allocate memory for FPC table if current
+ * FPC table buffer is not big enough to
+ * accommodate FPC Number requested
+ */
+ if (raw_params->fault_pxl.fp_num != config_params->fault_pxl.fp_num) {
+ if (fpc_physaddr != NULL) {
+ free_pages((unsigned long)fpc_physaddr,
+ get_order
+ (config_params->fault_pxl.fp_num *
+ FP_NUM_BYTES));
+ }
+
+ /* Allocate memory for FPC table */
+ fpc_virtaddr =
+ (unsigned int *)__get_free_pages(GFP_KERNEL | GFP_DMA,
+ get_order(raw_params->
+ fault_pxl.fp_num *
+ FP_NUM_BYTES));
+
+ if (fpc_virtaddr == NULL) {
+ dev_dbg(ccdc_cfg.dev,
+ "\nUnable to allocate memory for FPC");
+ return -EFAULT;
+ }
+ fpc_physaddr =
+ (unsigned int *)virt_to_phys((void *)fpc_virtaddr);
+ }
+
+ /* Copy number of fault pixels and FPC table */
+ config_params->fault_pxl.fp_num = raw_params->fault_pxl.fp_num;
+ if (copy_from_user(fpc_virtaddr,
+ (void __user *)raw_params->fault_pxl.fpc_table_addr,
+ config_params->fault_pxl.fp_num * FP_NUM_BYTES)) {
+ dev_dbg(ccdc_cfg.dev, "\n copy_from_user failed");
+ return -EFAULT;
+ }
+ config_params->fault_pxl.fpc_table_addr = (unsigned int)fpc_physaddr;
+ return 0;
+}
+
+static int ccdc_close(struct device *dev)
+{
+ struct ccdc_config_params_raw *config_params =
+ &ccdc_cfg.bayer.config_params;
+ unsigned int *fpc_physaddr = NULL, *fpc_virtaddr = NULL;
+
+ fpc_physaddr = (unsigned int *)config_params->fault_pxl.fpc_table_addr;
+
+ if (fpc_physaddr != NULL) {
+ fpc_virtaddr = (unsigned int *)
+ phys_to_virt((unsigned long)fpc_physaddr);
+ free_pages((unsigned long)fpc_virtaddr,
+ get_order(config_params->fault_pxl.fp_num *
+ FP_NUM_BYTES));
+ }
+ return 0;
+}
+
+/*
+ * ccdc_restore_defaults()
+ * This function will write defaults to all CCDC registers
+ */
+static void ccdc_restore_defaults(void)
+{
+ int i;
+
+ /* disable CCDC */
+ ccdc_enable(0);
+ /* set all registers to default value */
+ for (i = 4; i <= 0x94; i += 4)
+ regw(0, i);
+ regw(CCDC_NO_CULLING, CCDC_CULLING);
+ regw(CCDC_GAMMA_BITS_11_2, CCDC_ALAW);
+}
+
+static int ccdc_open(struct device *device)
+{
+ ccdc_restore_defaults();
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ ccdc_enable_vport(1);
+ return 0;
+}
+
+static void ccdc_sbl_reset(void)
+{
+ vpss_clear_wbl_overflow(VPSS_PCR_CCDC_WBL_O);
+}
+
+/* Parameter operations */
+static int ccdc_set_params(void __user *params)
+{
+ struct ccdc_config_params_raw ccdc_raw_params;
+ int x;
+
+ if (ccdc_cfg.if_type != VPFE_RAW_BAYER)
+ return -EINVAL;
+
+ x = copy_from_user(&ccdc_raw_params, params, sizeof(ccdc_raw_params));
+ if (x) {
+ dev_dbg(ccdc_cfg.dev, "ccdc_set_params: error in copying"
+ "ccdc params, %d\n", x);
+ return -EFAULT;
+ }
+
+ if (!validate_ccdc_param(&ccdc_raw_params)) {
+ if (!ccdc_update_raw_params(&ccdc_raw_params))
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/*
+ * ccdc_config_ycbcr()
+ * This function will configure CCDC for YCbCr video capture
+ */
+void ccdc_config_ycbcr(void)
+{
+ struct ccdc_params_ycbcr *params = &ccdc_cfg.ycbcr;
+ u32 syn_mode;
+
+ dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_ycbcr...");
+ /*
+ * first restore the CCDC registers to default values
+ * This is important since we assume default values to be set in
+ * a lot of registers that we didn't touch
+ */
+ ccdc_restore_defaults();
+
+ /*
+ * configure pixel format, frame format, configure video frame
+ * format, enable output to SDRAM, enable internal timing generator
+ * and 8bit pack mode
+ */
+ syn_mode = (((params->pix_fmt & CCDC_SYN_MODE_INPMOD_MASK) <<
+ CCDC_SYN_MODE_INPMOD_SHIFT) |
+ ((params->frm_fmt & CCDC_SYN_FLDMODE_MASK) <<
+ CCDC_SYN_FLDMODE_SHIFT) | CCDC_VDHDEN_ENABLE |
+ CCDC_WEN_ENABLE | CCDC_DATA_PACK_ENABLE);
+
+ /* setup BT.656 sync mode */
+ if (params->bt656_enable) {
+ regw(CCDC_REC656IF_BT656_EN, CCDC_REC656IF);
+
+ /*
+ * configure the FID, VD, HD pin polarity,
+ * fld,hd pol positive, vd negative, 8-bit data
+ */
+ syn_mode |= CCDC_SYN_MODE_VD_POL_NEGATIVE;
+ if (ccdc_cfg.if_type == VPFE_BT656_10BIT)
+ syn_mode |= CCDC_SYN_MODE_10BITS;
+ else
+ syn_mode |= CCDC_SYN_MODE_8BITS;
+ } else {
+ /* y/c external sync mode */
+ syn_mode |= (((params->fid_pol & CCDC_FID_POL_MASK) <<
+ CCDC_FID_POL_SHIFT) |
+ ((params->hd_pol & CCDC_HD_POL_MASK) <<
+ CCDC_HD_POL_SHIFT) |
+ ((params->vd_pol & CCDC_VD_POL_MASK) <<
+ CCDC_VD_POL_SHIFT));
+ }
+ regw(syn_mode, CCDC_SYN_MODE);
+
+ /* configure video window */
+ ccdc_setwin(&params->win, params->frm_fmt, 2);
+
+ /*
+ * configure the order of y cb cr in SDRAM, and disable latch
+ * internal register on vsync
+ */
+ if (ccdc_cfg.if_type == VPFE_BT656_10BIT)
+ regw((params->pix_order << CCDC_CCDCFG_Y8POS_SHIFT) |
+ CCDC_LATCH_ON_VSYNC_DISABLE | CCDC_CCDCFG_BW656_10BIT,
+ CCDC_CCDCFG);
+ else
+ regw((params->pix_order << CCDC_CCDCFG_Y8POS_SHIFT) |
+ CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG);
+
+ /*
+ * configure the horizontal line offset. This should be a
+ * on 32 byte boundary. So clear LSB 5 bits
+ */
+ regw(((params->win.width * 2 + 31) & ~0x1f), CCDC_HSIZE_OFF);
+
+ /* configure the memory line offset */
+ if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED)
+ /* two fields are interleaved in memory */
+ regw(CCDC_SDOFST_FIELD_INTERLEAVED, CCDC_SDOFST);
+
+ ccdc_sbl_reset();
+ dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_config_ycbcr...\n");
+}
+
+static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp)
+{
+ u32 val;
+
+ if (!bclamp->enable) {
+ /* configure DCSub */
+ val = (bclamp->dc_sub) & CCDC_BLK_DC_SUB_MASK;
+ regw(val, CCDC_DCSUB);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to DCSUB...\n", val);
+ regw(CCDC_CLAMP_DEFAULT_VAL, CCDC_CLAMP);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x0000 to CLAMP...\n");
+ return;
+ }
+ /*
+ * Configure gain, Start pixel, No of line to be avg,
+ * No of pixel/line to be avg, & Enable the Black clamping
+ */
+ val = ((bclamp->sgain & CCDC_BLK_SGAIN_MASK) |
+ ((bclamp->start_pixel & CCDC_BLK_ST_PXL_MASK) <<
+ CCDC_BLK_ST_PXL_SHIFT) |
+ ((bclamp->sample_ln & CCDC_BLK_SAMPLE_LINE_MASK) <<
+ CCDC_BLK_SAMPLE_LINE_SHIFT) |
+ ((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) <<
+ CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE);
+ regw(val, CCDC_CLAMP);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to CLAMP...\n", val);
+ /* If Black clamping is enable then make dcsub 0 */
+ regw(CCDC_DCSUB_DEFAULT_VAL, CCDC_DCSUB);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x00000000 to DCSUB...\n");
+}
+
+static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp)
+{
+ u32 val;
+
+ val = ((bcomp->b & CCDC_BLK_COMP_MASK) |
+ ((bcomp->gb & CCDC_BLK_COMP_MASK) <<
+ CCDC_BLK_COMP_GB_COMP_SHIFT) |
+ ((bcomp->gr & CCDC_BLK_COMP_MASK) <<
+ CCDC_BLK_COMP_GR_COMP_SHIFT) |
+ ((bcomp->r & CCDC_BLK_COMP_MASK) <<
+ CCDC_BLK_COMP_R_COMP_SHIFT));
+ regw(val, CCDC_BLKCMP);
+}
+
+static void ccdc_config_fpc(struct ccdc_fault_pixel *fpc)
+{
+ u32 val;
+
+ /* Initially disable FPC */
+ val = CCDC_FPC_DISABLE;
+ regw(val, CCDC_FPC);
+
+ if (!fpc->enable)
+ return;
+
+ /* Configure Fault pixel if needed */
+ regw(fpc->fpc_table_addr, CCDC_FPC_ADDR);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FPC_ADDR...\n",
+ (fpc->fpc_table_addr));
+ /* Write the FPC params with FPC disable */
+ val = fpc->fp_num & CCDC_FPC_FPC_NUM_MASK;
+ regw(val, CCDC_FPC);
+
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FPC...\n", val);
+ /* read the FPC register */
+ val = regr(CCDC_FPC) | CCDC_FPC_ENABLE;
+ regw(val, CCDC_FPC);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FPC...\n", val);
+}
+
+/*
+ * ccdc_config_raw()
+ * This function will configure CCDC for Raw capture mode
+ */
+void ccdc_config_raw(void)
+{
+ struct ccdc_params_raw *params = &ccdc_cfg.bayer;
+ struct ccdc_config_params_raw *config_params =
+ &ccdc_cfg.bayer.config_params;
+ unsigned int syn_mode = 0;
+ unsigned int val;
+
+ dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_raw...");
+
+ /* Reset CCDC */
+ ccdc_restore_defaults();
+
+ /* Disable latching function registers on VSYNC */
+ regw(CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG);
+
+ /*
+ * Configure the vertical sync polarity(SYN_MODE.VDPOL),
+ * horizontal sync polarity (SYN_MODE.HDPOL), frame id polarity
+ * (SYN_MODE.FLDPOL), frame format(progressive or interlace),
+ * data size(SYNMODE.DATSIZ), &pixel format (Input mode), output
+ * SDRAM, enable internal timing generator
+ */
+ syn_mode =
+ (((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT) |
+ ((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT) |
+ ((params->fid_pol & CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT) |
+ ((params->frm_fmt & CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) |
+ ((config_params->data_sz & CCDC_DATA_SZ_MASK) <<
+ CCDC_DATA_SZ_SHIFT) |
+ ((params->pix_fmt & CCDC_PIX_FMT_MASK) << CCDC_PIX_FMT_SHIFT) |
+ CCDC_WEN_ENABLE | CCDC_VDHDEN_ENABLE);
+
+ /* Enable and configure aLaw register if needed */
+ if (config_params->alaw.enable) {
+ val = ((config_params->alaw.gama_wd &
+ CCDC_ALAW_GAMA_WD_MASK) | CCDC_ALAW_ENABLE);
+ regw(val, CCDC_ALAW);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to ALAW...\n", val);
+ }
+
+ /* Configure video window */
+ ccdc_setwin(&params->win, params->frm_fmt, CCDC_PPC_RAW);
+
+ /* Configure Black Clamp */
+ ccdc_config_black_clamp(&config_params->blk_clamp);
+
+ /* Configure Black level compensation */
+ ccdc_config_black_compense(&config_params->blk_comp);
+
+ /* Configure Fault Pixel Correction */
+ ccdc_config_fpc(&config_params->fault_pxl);
+
+ /* If data size is 8 bit then pack the data */
+ if ((config_params->data_sz == CCDC_DATA_8BITS) ||
+ config_params->alaw.enable)
+ syn_mode |= CCDC_DATA_PACK_ENABLE;
+
+#ifdef CONFIG_DM644X_VIDEO_PORT_ENABLE
+ /* enable video port */
+ val = CCDC_ENABLE_VIDEO_PORT;
+#else
+ /* disable video port */
+ val = CCDC_DISABLE_VIDEO_PORT;
+#endif
+
+ if (config_params->data_sz == CCDC_DATA_8BITS)
+ val |= (CCDC_DATA_10BITS & CCDC_FMTCFG_VPIN_MASK)
+ << CCDC_FMTCFG_VPIN_SHIFT;
+ else
+ val |= (config_params->data_sz & CCDC_FMTCFG_VPIN_MASK)
+ << CCDC_FMTCFG_VPIN_SHIFT;
+ /* Write value in FMTCFG */
+ regw(val, CCDC_FMTCFG);
+
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMTCFG...\n", val);
+ /* Configure the color pattern according to mt9t001 sensor */
+ regw(CCDC_COLPTN_VAL, CCDC_COLPTN);
+
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0xBB11BB11 to COLPTN...\n");
+ /*
+ * Configure Data formatter(Video port) pixel selection
+ * (FMT_HORZ, FMT_VERT)
+ */
+ val = ((params->win.left & CCDC_FMT_HORZ_FMTSPH_MASK) <<
+ CCDC_FMT_HORZ_FMTSPH_SHIFT) |
+ (params->win.width & CCDC_FMT_HORZ_FMTLNH_MASK);
+ regw(val, CCDC_FMT_HORZ);
+
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMT_HORZ...\n", val);
+ val = (params->win.top & CCDC_FMT_VERT_FMTSLV_MASK)
+ << CCDC_FMT_VERT_FMTSLV_SHIFT;
+ if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE)
+ val |= (params->win.height) & CCDC_FMT_VERT_FMTLNV_MASK;
+ else
+ val |= (params->win.height >> 1) & CCDC_FMT_VERT_FMTLNV_MASK;
+
+ dev_dbg(ccdc_cfg.dev, "\nparams->win.height 0x%x ...\n",
+ params->win.height);
+ regw(val, CCDC_FMT_VERT);
+
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMT_VERT...\n", val);
+
+ dev_dbg(ccdc_cfg.dev, "\nbelow regw(val, FMT_VERT)...");
+
+ /*
+ * Configure Horizontal offset register. If pack 8 is enabled then
+ * 1 pixel will take 1 byte
+ */
+ if ((config_params->data_sz == CCDC_DATA_8BITS) ||
+ config_params->alaw.enable)
+ regw((params->win.width + CCDC_32BYTE_ALIGN_VAL) &
+ CCDC_HSIZE_OFF_MASK, CCDC_HSIZE_OFF);
+ else
+ /* else one pixel will take 2 byte */
+ regw(((params->win.width * CCDC_TWO_BYTES_PER_PIXEL) +
+ CCDC_32BYTE_ALIGN_VAL) & CCDC_HSIZE_OFF_MASK,
+ CCDC_HSIZE_OFF);
+
+ /* Set value for SDOFST */
+ if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) {
+ if (params->image_invert_enable) {
+ /* For intelace inverse mode */
+ regw(CCDC_INTERLACED_IMAGE_INVERT, CCDC_SDOFST);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x4B6D to SDOFST..\n");
+ }
+
+ else {
+ /* For intelace non inverse mode */
+ regw(CCDC_INTERLACED_NO_IMAGE_INVERT, CCDC_SDOFST);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x0249 to SDOFST..\n");
+ }
+ } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) {
+ regw(CCDC_PROGRESSIVE_NO_IMAGE_INVERT, CCDC_SDOFST);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x0000 to SDOFST...\n");
+ }
+
+ /*
+ * Configure video port pixel selection (VPOUT)
+ * Here -1 is to make the height value less than FMT_VERT.FMTLNV
+ */
+ if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE)
+ val = (((params->win.height - 1) & CCDC_VP_OUT_VERT_NUM_MASK))
+ << CCDC_VP_OUT_VERT_NUM_SHIFT;
+ else
+ val =
+ ((((params->win.height >> CCDC_INTERLACED_HEIGHT_SHIFT) -
+ 1) & CCDC_VP_OUT_VERT_NUM_MASK)) <<
+ CCDC_VP_OUT_VERT_NUM_SHIFT;
+
+ val |= ((((params->win.width))) & CCDC_VP_OUT_HORZ_NUM_MASK)
+ << CCDC_VP_OUT_HORZ_NUM_SHIFT;
+ val |= (params->win.left) & CCDC_VP_OUT_HORZ_ST_MASK;
+ regw(val, CCDC_VP_OUT);
+
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to VP_OUT...\n", val);
+ regw(syn_mode, CCDC_SYN_MODE);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to SYN_MODE...\n", syn_mode);
+
+ ccdc_sbl_reset();
+ dev_dbg(ccdc_cfg.dev, "\nend of ccdc_config_raw...");
+ ccdc_readregs();
+}
+
+static int ccdc_configure(void)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ ccdc_config_raw();
+ else
+ ccdc_config_ycbcr();
+ return 0;
+}
+
+static int ccdc_set_buftype(enum ccdc_buftype buf_type)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ ccdc_cfg.bayer.buf_type = buf_type;
+ else
+ ccdc_cfg.ycbcr.buf_type = buf_type;
+ return 0;
+}
+
+static enum ccdc_buftype ccdc_get_buftype(void)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ return ccdc_cfg.bayer.buf_type;
+ return ccdc_cfg.ycbcr.buf_type;
+}
+
+static int ccdc_enum_pix(u32 *pix, int i)
+{
+ int ret = -EINVAL;
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
+ if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) {
+ *pix = ccdc_raw_bayer_pix_formats[i];
+ ret = 0;
+ }
+ } else {
+ if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) {
+ *pix = ccdc_raw_yuv_pix_formats[i];
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
+static int ccdc_set_pixel_format(u32 pixfmt)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
+ ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW;
+ if (pixfmt == V4L2_PIX_FMT_SBGGR8)
+ ccdc_cfg.bayer.config_params.alaw.enable = 1;
+ else if (pixfmt != V4L2_PIX_FMT_SBGGR16)
+ return -EINVAL;
+ } else {
+ if (pixfmt == V4L2_PIX_FMT_YUYV)
+ ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR;
+ else if (pixfmt == V4L2_PIX_FMT_UYVY)
+ ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
+ else
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static u32 ccdc_get_pixel_format(void)
+{
+ struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw;
+ u32 pixfmt;
+
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ if (alaw->enable)
+ pixfmt = V4L2_PIX_FMT_SBGGR8;
+ else
+ pixfmt = V4L2_PIX_FMT_SBGGR16;
+ else {
+ if (ccdc_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR)
+ pixfmt = V4L2_PIX_FMT_YUYV;
+ else
+ pixfmt = V4L2_PIX_FMT_UYVY;
+ }
+ return pixfmt;
+}
+
+static int ccdc_set_image_window(struct v4l2_rect *win)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ ccdc_cfg.bayer.win = *win;
+ else
+ ccdc_cfg.ycbcr.win = *win;
+ return 0;
+}
+
+static void ccdc_get_image_window(struct v4l2_rect *win)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ *win = ccdc_cfg.bayer.win;
+ else
+ *win = ccdc_cfg.ycbcr.win;
+}
+
+static unsigned int ccdc_get_line_length(void)
+{
+ struct ccdc_config_params_raw *config_params =
+ &ccdc_cfg.bayer.config_params;
+ unsigned int len;
+
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
+ if ((config_params->alaw.enable) ||
+ (config_params->data_sz == CCDC_DATA_8BITS))
+ len = ccdc_cfg.bayer.win.width;
+ else
+ len = ccdc_cfg.bayer.win.width * 2;
+ } else
+ len = ccdc_cfg.ycbcr.win.width * 2;
+ return ALIGN(len, 32);
+}
+
+static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ ccdc_cfg.bayer.frm_fmt = frm_fmt;
+ else
+ ccdc_cfg.ycbcr.frm_fmt = frm_fmt;
+ return 0;
+}
+
+static enum ccdc_frmfmt ccdc_get_frame_format(void)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ return ccdc_cfg.bayer.frm_fmt;
+ else
+ return ccdc_cfg.ycbcr.frm_fmt;
+}
+
+static int ccdc_getfid(void)
+{
+ return (regr(CCDC_SYN_MODE) >> 15) & 1;
+}
+
+/* misc operations */
+static inline void ccdc_setfbaddr(unsigned long addr)
+{
+ regw(addr & 0xffffffe0, CCDC_SDR_ADDR);
+}
+
+static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params)
+{
+ ccdc_cfg.if_type = params->if_type;
+
+ switch (params->if_type) {
+ case VPFE_BT656:
+ case VPFE_YCBCR_SYNC_16:
+ case VPFE_YCBCR_SYNC_8:
+ case VPFE_BT656_10BIT:
+ ccdc_cfg.ycbcr.vd_pol = params->vdpol;
+ ccdc_cfg.ycbcr.hd_pol = params->hdpol;
+ break;
+ default:
+ /* TODO add support for raw bayer here */
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void ccdc_save_context(void)
+{
+ ccdc_ctx[CCDC_PCR >> 2] = regr(CCDC_PCR);
+ ccdc_ctx[CCDC_SYN_MODE >> 2] = regr(CCDC_SYN_MODE);
+ ccdc_ctx[CCDC_HD_VD_WID >> 2] = regr(CCDC_HD_VD_WID);
+ ccdc_ctx[CCDC_PIX_LINES >> 2] = regr(CCDC_PIX_LINES);
+ ccdc_ctx[CCDC_HORZ_INFO >> 2] = regr(CCDC_HORZ_INFO);
+ ccdc_ctx[CCDC_VERT_START >> 2] = regr(CCDC_VERT_START);
+ ccdc_ctx[CCDC_VERT_LINES >> 2] = regr(CCDC_VERT_LINES);
+ ccdc_ctx[CCDC_CULLING >> 2] = regr(CCDC_CULLING);
+ ccdc_ctx[CCDC_HSIZE_OFF >> 2] = regr(CCDC_HSIZE_OFF);
+ ccdc_ctx[CCDC_SDOFST >> 2] = regr(CCDC_SDOFST);
+ ccdc_ctx[CCDC_SDR_ADDR >> 2] = regr(CCDC_SDR_ADDR);
+ ccdc_ctx[CCDC_CLAMP >> 2] = regr(CCDC_CLAMP);
+ ccdc_ctx[CCDC_DCSUB >> 2] = regr(CCDC_DCSUB);
+ ccdc_ctx[CCDC_COLPTN >> 2] = regr(CCDC_COLPTN);
+ ccdc_ctx[CCDC_BLKCMP >> 2] = regr(CCDC_BLKCMP);
+ ccdc_ctx[CCDC_FPC >> 2] = regr(CCDC_FPC);
+ ccdc_ctx[CCDC_FPC_ADDR >> 2] = regr(CCDC_FPC_ADDR);
+ ccdc_ctx[CCDC_VDINT >> 2] = regr(CCDC_VDINT);
+ ccdc_ctx[CCDC_ALAW >> 2] = regr(CCDC_ALAW);
+ ccdc_ctx[CCDC_REC656IF >> 2] = regr(CCDC_REC656IF);
+ ccdc_ctx[CCDC_CCDCFG >> 2] = regr(CCDC_CCDCFG);
+ ccdc_ctx[CCDC_FMTCFG >> 2] = regr(CCDC_FMTCFG);
+ ccdc_ctx[CCDC_FMT_HORZ >> 2] = regr(CCDC_FMT_HORZ);
+ ccdc_ctx[CCDC_FMT_VERT >> 2] = regr(CCDC_FMT_VERT);
+ ccdc_ctx[CCDC_FMT_ADDR0 >> 2] = regr(CCDC_FMT_ADDR0);
+ ccdc_ctx[CCDC_FMT_ADDR1 >> 2] = regr(CCDC_FMT_ADDR1);
+ ccdc_ctx[CCDC_FMT_ADDR2 >> 2] = regr(CCDC_FMT_ADDR2);
+ ccdc_ctx[CCDC_FMT_ADDR3 >> 2] = regr(CCDC_FMT_ADDR3);
+ ccdc_ctx[CCDC_FMT_ADDR4 >> 2] = regr(CCDC_FMT_ADDR4);
+ ccdc_ctx[CCDC_FMT_ADDR5 >> 2] = regr(CCDC_FMT_ADDR5);
+ ccdc_ctx[CCDC_FMT_ADDR6 >> 2] = regr(CCDC_FMT_ADDR6);
+ ccdc_ctx[CCDC_FMT_ADDR7 >> 2] = regr(CCDC_FMT_ADDR7);
+ ccdc_ctx[CCDC_PRGEVEN_0 >> 2] = regr(CCDC_PRGEVEN_0);
+ ccdc_ctx[CCDC_PRGEVEN_1 >> 2] = regr(CCDC_PRGEVEN_1);
+ ccdc_ctx[CCDC_PRGODD_0 >> 2] = regr(CCDC_PRGODD_0);
+ ccdc_ctx[CCDC_PRGODD_1 >> 2] = regr(CCDC_PRGODD_1);
+ ccdc_ctx[CCDC_VP_OUT >> 2] = regr(CCDC_VP_OUT);
+}
+
+static void ccdc_restore_context(void)
+{
+ regw(ccdc_ctx[CCDC_SYN_MODE >> 2], CCDC_SYN_MODE);
+ regw(ccdc_ctx[CCDC_HD_VD_WID >> 2], CCDC_HD_VD_WID);
+ regw(ccdc_ctx[CCDC_PIX_LINES >> 2], CCDC_PIX_LINES);
+ regw(ccdc_ctx[CCDC_HORZ_INFO >> 2], CCDC_HORZ_INFO);
+ regw(ccdc_ctx[CCDC_VERT_START >> 2], CCDC_VERT_START);
+ regw(ccdc_ctx[CCDC_VERT_LINES >> 2], CCDC_VERT_LINES);
+ regw(ccdc_ctx[CCDC_CULLING >> 2], CCDC_CULLING);
+ regw(ccdc_ctx[CCDC_HSIZE_OFF >> 2], CCDC_HSIZE_OFF);
+ regw(ccdc_ctx[CCDC_SDOFST >> 2], CCDC_SDOFST);
+ regw(ccdc_ctx[CCDC_SDR_ADDR >> 2], CCDC_SDR_ADDR);
+ regw(ccdc_ctx[CCDC_CLAMP >> 2], CCDC_CLAMP);
+ regw(ccdc_ctx[CCDC_DCSUB >> 2], CCDC_DCSUB);
+ regw(ccdc_ctx[CCDC_COLPTN >> 2], CCDC_COLPTN);
+ regw(ccdc_ctx[CCDC_BLKCMP >> 2], CCDC_BLKCMP);
+ regw(ccdc_ctx[CCDC_FPC >> 2], CCDC_FPC);
+ regw(ccdc_ctx[CCDC_FPC_ADDR >> 2], CCDC_FPC_ADDR);
+ regw(ccdc_ctx[CCDC_VDINT >> 2], CCDC_VDINT);
+ regw(ccdc_ctx[CCDC_ALAW >> 2], CCDC_ALAW);
+ regw(ccdc_ctx[CCDC_REC656IF >> 2], CCDC_REC656IF);
+ regw(ccdc_ctx[CCDC_CCDCFG >> 2], CCDC_CCDCFG);
+ regw(ccdc_ctx[CCDC_FMTCFG >> 2], CCDC_FMTCFG);
+ regw(ccdc_ctx[CCDC_FMT_HORZ >> 2], CCDC_FMT_HORZ);
+ regw(ccdc_ctx[CCDC_FMT_VERT >> 2], CCDC_FMT_VERT);
+ regw(ccdc_ctx[CCDC_FMT_ADDR0 >> 2], CCDC_FMT_ADDR0);
+ regw(ccdc_ctx[CCDC_FMT_ADDR1 >> 2], CCDC_FMT_ADDR1);
+ regw(ccdc_ctx[CCDC_FMT_ADDR2 >> 2], CCDC_FMT_ADDR2);
+ regw(ccdc_ctx[CCDC_FMT_ADDR3 >> 2], CCDC_FMT_ADDR3);
+ regw(ccdc_ctx[CCDC_FMT_ADDR4 >> 2], CCDC_FMT_ADDR4);
+ regw(ccdc_ctx[CCDC_FMT_ADDR5 >> 2], CCDC_FMT_ADDR5);
+ regw(ccdc_ctx[CCDC_FMT_ADDR6 >> 2], CCDC_FMT_ADDR6);
+ regw(ccdc_ctx[CCDC_FMT_ADDR7 >> 2], CCDC_FMT_ADDR7);
+ regw(ccdc_ctx[CCDC_PRGEVEN_0 >> 2], CCDC_PRGEVEN_0);
+ regw(ccdc_ctx[CCDC_PRGEVEN_1 >> 2], CCDC_PRGEVEN_1);
+ regw(ccdc_ctx[CCDC_PRGODD_0 >> 2], CCDC_PRGODD_0);
+ regw(ccdc_ctx[CCDC_PRGODD_1 >> 2], CCDC_PRGODD_1);
+ regw(ccdc_ctx[CCDC_VP_OUT >> 2], CCDC_VP_OUT);
+ regw(ccdc_ctx[CCDC_PCR >> 2], CCDC_PCR);
+}
+static struct ccdc_hw_device ccdc_hw_dev = {
+ .name = "DM6446 CCDC",
+ .owner = THIS_MODULE,
+ .hw_ops = {
+ .open = ccdc_open,
+ .close = ccdc_close,
+ .reset = ccdc_sbl_reset,
+ .enable = ccdc_enable,
+ .set_hw_if_params = ccdc_set_hw_if_params,
+ .set_params = ccdc_set_params,
+ .configure = ccdc_configure,
+ .set_buftype = ccdc_set_buftype,
+ .get_buftype = ccdc_get_buftype,
+ .enum_pix = ccdc_enum_pix,
+ .set_pixel_format = ccdc_set_pixel_format,
+ .get_pixel_format = ccdc_get_pixel_format,
+ .set_frame_format = ccdc_set_frame_format,
+ .get_frame_format = ccdc_get_frame_format,
+ .set_image_window = ccdc_set_image_window,
+ .get_image_window = ccdc_get_image_window,
+ .get_line_length = ccdc_get_line_length,
+ .setfbaddr = ccdc_setfbaddr,
+ .getfid = ccdc_getfid,
+ },
+};
+
+static int __init dm644x_ccdc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int status = 0;
+
+ /*
+ * first try to register with vpfe. If not correct platform, then we
+ * don't have to iomap
+ */
+ status = vpfe_register_ccdc_device(&ccdc_hw_dev);
+ if (status < 0)
+ return status;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ status = -ENODEV;
+ goto fail_nores;
+ }
+
+ res = request_mem_region(res->start, resource_size(res), res->name);
+ if (!res) {
+ status = -EBUSY;
+ goto fail_nores;
+ }
+
+ ccdc_cfg.base_addr = ioremap_nocache(res->start, resource_size(res));
+ if (!ccdc_cfg.base_addr) {
+ status = -ENOMEM;
+ goto fail_nomem;
+ }
+
+ /* Get and enable Master clock */
+ ccdc_cfg.mclk = clk_get(&pdev->dev, "master");
+ if (IS_ERR(ccdc_cfg.mclk)) {
+ status = PTR_ERR(ccdc_cfg.mclk);
+ goto fail_nomap;
+ }
+ if (clk_enable(ccdc_cfg.mclk)) {
+ status = -ENODEV;
+ goto fail_mclk;
+ }
+
+ /* Get and enable Slave clock */
+ ccdc_cfg.sclk = clk_get(&pdev->dev, "slave");
+ if (IS_ERR(ccdc_cfg.sclk)) {
+ status = PTR_ERR(ccdc_cfg.sclk);
+ goto fail_mclk;
+ }
+ if (clk_enable(ccdc_cfg.sclk)) {
+ status = -ENODEV;
+ goto fail_sclk;
+ }
+ ccdc_cfg.dev = &pdev->dev;
+ printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name);
+ return 0;
+fail_sclk:
+ clk_put(ccdc_cfg.sclk);
+fail_mclk:
+ clk_put(ccdc_cfg.mclk);
+fail_nomap:
+ iounmap(ccdc_cfg.base_addr);
+fail_nomem:
+ release_mem_region(res->start, resource_size(res));
+fail_nores:
+ vpfe_unregister_ccdc_device(&ccdc_hw_dev);
+ return status;
+}
+
+static int dm644x_ccdc_remove(struct platform_device *pdev)
+{
+ struct resource *res;
+
+ clk_put(ccdc_cfg.mclk);
+ clk_put(ccdc_cfg.sclk);
+ iounmap(ccdc_cfg.base_addr);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res)
+ release_mem_region(res->start, resource_size(res));
+ vpfe_unregister_ccdc_device(&ccdc_hw_dev);
+ return 0;
+}
+
+static int dm644x_ccdc_suspend(struct device *dev)
+{
+ /* Save CCDC context */
+ ccdc_save_context();
+ /* Disable CCDC */
+ ccdc_enable(0);
+ /* Disable both master and slave clock */
+ clk_disable(ccdc_cfg.mclk);
+ clk_disable(ccdc_cfg.sclk);
+
+ return 0;
+}
+
+static int dm644x_ccdc_resume(struct device *dev)
+{
+ /* Enable both master and slave clock */
+ clk_enable(ccdc_cfg.mclk);
+ clk_enable(ccdc_cfg.sclk);
+ /* Restore CCDC context */
+ ccdc_restore_context();
+
+ return 0;
+}
+
+static const struct dev_pm_ops dm644x_ccdc_pm_ops = {
+ .suspend = dm644x_ccdc_suspend,
+ .resume = dm644x_ccdc_resume,
+};
+
+static struct platform_driver dm644x_ccdc_driver = {
+ .driver = {
+ .name = "dm644x_ccdc",
+ .owner = THIS_MODULE,
+ .pm = &dm644x_ccdc_pm_ops,
+ },
+ .remove = __devexit_p(dm644x_ccdc_remove),
+ .probe = dm644x_ccdc_probe,
+};
+
+module_platform_driver(dm644x_ccdc_driver);
diff --git a/drivers/media/video/davinci/dm644x_ccdc_regs.h b/drivers/media/video/davinci/dm644x_ccdc_regs.h
new file mode 100644
index 00000000..90370e41
--- /dev/null
+++ b/drivers/media/video/davinci/dm644x_ccdc_regs.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2006-2009 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _DM644X_CCDC_REGS_H
+#define _DM644X_CCDC_REGS_H
+
+/**************************************************************************\
+* Register OFFSET Definitions
+\**************************************************************************/
+#define CCDC_PID 0x0
+#define CCDC_PCR 0x4
+#define CCDC_SYN_MODE 0x8
+#define CCDC_HD_VD_WID 0xc
+#define CCDC_PIX_LINES 0x10
+#define CCDC_HORZ_INFO 0x14
+#define CCDC_VERT_START 0x18
+#define CCDC_VERT_LINES 0x1c
+#define CCDC_CULLING 0x20
+#define CCDC_HSIZE_OFF 0x24
+#define CCDC_SDOFST 0x28
+#define CCDC_SDR_ADDR 0x2c
+#define CCDC_CLAMP 0x30
+#define CCDC_DCSUB 0x34
+#define CCDC_COLPTN 0x38
+#define CCDC_BLKCMP 0x3c
+#define CCDC_FPC 0x40
+#define CCDC_FPC_ADDR 0x44
+#define CCDC_VDINT 0x48
+#define CCDC_ALAW 0x4c
+#define CCDC_REC656IF 0x50
+#define CCDC_CCDCFG 0x54
+#define CCDC_FMTCFG 0x58
+#define CCDC_FMT_HORZ 0x5c
+#define CCDC_FMT_VERT 0x60
+#define CCDC_FMT_ADDR0 0x64
+#define CCDC_FMT_ADDR1 0x68
+#define CCDC_FMT_ADDR2 0x6c
+#define CCDC_FMT_ADDR3 0x70
+#define CCDC_FMT_ADDR4 0x74
+#define CCDC_FMT_ADDR5 0x78
+#define CCDC_FMT_ADDR6 0x7c
+#define CCDC_FMT_ADDR7 0x80
+#define CCDC_PRGEVEN_0 0x84
+#define CCDC_PRGEVEN_1 0x88
+#define CCDC_PRGODD_0 0x8c
+#define CCDC_PRGODD_1 0x90
+#define CCDC_VP_OUT 0x94
+#define CCDC_REG_END 0x98
+
+/***************************************************************
+* Define for various register bit mask and shifts for CCDC
+****************************************************************/
+#define CCDC_FID_POL_MASK 1
+#define CCDC_FID_POL_SHIFT 4
+#define CCDC_HD_POL_MASK 1
+#define CCDC_HD_POL_SHIFT 3
+#define CCDC_VD_POL_MASK 1
+#define CCDC_VD_POL_SHIFT 2
+#define CCDC_HSIZE_OFF_MASK 0xffffffe0
+#define CCDC_32BYTE_ALIGN_VAL 31
+#define CCDC_FRM_FMT_MASK 0x1
+#define CCDC_FRM_FMT_SHIFT 7
+#define CCDC_DATA_SZ_MASK 7
+#define CCDC_DATA_SZ_SHIFT 8
+#define CCDC_PIX_FMT_MASK 3
+#define CCDC_PIX_FMT_SHIFT 12
+#define CCDC_VP2SDR_DISABLE 0xFFFBFFFF
+#define CCDC_WEN_ENABLE (1 << 17)
+#define CCDC_SDR2RSZ_DISABLE 0xFFF7FFFF
+#define CCDC_VDHDEN_ENABLE (1 << 16)
+#define CCDC_LPF_ENABLE (1 << 14)
+#define CCDC_ALAW_ENABLE (1 << 3)
+#define CCDC_ALAW_GAMA_WD_MASK 7
+#define CCDC_BLK_CLAMP_ENABLE (1 << 31)
+#define CCDC_BLK_SGAIN_MASK 0x1F
+#define CCDC_BLK_ST_PXL_MASK 0x7FFF
+#define CCDC_BLK_ST_PXL_SHIFT 10
+#define CCDC_BLK_SAMPLE_LN_MASK 7
+#define CCDC_BLK_SAMPLE_LN_SHIFT 28
+#define CCDC_BLK_SAMPLE_LINE_MASK 7
+#define CCDC_BLK_SAMPLE_LINE_SHIFT 25
+#define CCDC_BLK_DC_SUB_MASK 0x03FFF
+#define CCDC_BLK_COMP_MASK 0xFF
+#define CCDC_BLK_COMP_GB_COMP_SHIFT 8
+#define CCDC_BLK_COMP_GR_COMP_SHIFT 16
+#define CCDC_BLK_COMP_R_COMP_SHIFT 24
+#define CCDC_LATCH_ON_VSYNC_DISABLE (1 << 15)
+#define CCDC_FPC_ENABLE (1 << 15)
+#define CCDC_FPC_DISABLE 0
+#define CCDC_FPC_FPC_NUM_MASK 0x7FFF
+#define CCDC_DATA_PACK_ENABLE (1 << 11)
+#define CCDC_FMTCFG_VPIN_MASK 7
+#define CCDC_FMTCFG_VPIN_SHIFT 12
+#define CCDC_FMT_HORZ_FMTLNH_MASK 0x1FFF
+#define CCDC_FMT_HORZ_FMTSPH_MASK 0x1FFF
+#define CCDC_FMT_HORZ_FMTSPH_SHIFT 16
+#define CCDC_FMT_VERT_FMTLNV_MASK 0x1FFF
+#define CCDC_FMT_VERT_FMTSLV_MASK 0x1FFF
+#define CCDC_FMT_VERT_FMTSLV_SHIFT 16
+#define CCDC_VP_OUT_VERT_NUM_MASK 0x3FFF
+#define CCDC_VP_OUT_VERT_NUM_SHIFT 17
+#define CCDC_VP_OUT_HORZ_NUM_MASK 0x1FFF
+#define CCDC_VP_OUT_HORZ_NUM_SHIFT 4
+#define CCDC_VP_OUT_HORZ_ST_MASK 0xF
+#define CCDC_HORZ_INFO_SPH_SHIFT 16
+#define CCDC_VERT_START_SLV0_SHIFT 16
+#define CCDC_VDINT_VDINT0_SHIFT 16
+#define CCDC_VDINT_VDINT1_MASK 0xFFFF
+#define CCDC_PPC_RAW 1
+#define CCDC_DCSUB_DEFAULT_VAL 0
+#define CCDC_CLAMP_DEFAULT_VAL 0
+#define CCDC_ENABLE_VIDEO_PORT 0x8000
+#define CCDC_DISABLE_VIDEO_PORT 0
+#define CCDC_COLPTN_VAL 0xBB11BB11
+#define CCDC_TWO_BYTES_PER_PIXEL 2
+#define CCDC_INTERLACED_IMAGE_INVERT 0x4B6D
+#define CCDC_INTERLACED_NO_IMAGE_INVERT 0x0249
+#define CCDC_PROGRESSIVE_IMAGE_INVERT 0x4000
+#define CCDC_PROGRESSIVE_NO_IMAGE_INVERT 0
+#define CCDC_INTERLACED_HEIGHT_SHIFT 1
+#define CCDC_SYN_MODE_INPMOD_SHIFT 12
+#define CCDC_SYN_MODE_INPMOD_MASK 3
+#define CCDC_SYN_MODE_8BITS (7 << 8)
+#define CCDC_SYN_MODE_10BITS (6 << 8)
+#define CCDC_SYN_MODE_11BITS (5 << 8)
+#define CCDC_SYN_MODE_12BITS (4 << 8)
+#define CCDC_SYN_MODE_13BITS (3 << 8)
+#define CCDC_SYN_MODE_14BITS (2 << 8)
+#define CCDC_SYN_MODE_15BITS (1 << 8)
+#define CCDC_SYN_MODE_16BITS (0 << 8)
+#define CCDC_SYN_FLDMODE_MASK 1
+#define CCDC_SYN_FLDMODE_SHIFT 7
+#define CCDC_REC656IF_BT656_EN 3
+#define CCDC_SYN_MODE_VD_POL_NEGATIVE (1 << 2)
+#define CCDC_CCDCFG_Y8POS_SHIFT 11
+#define CCDC_CCDCFG_BW656_10BIT (1 << 5)
+#define CCDC_SDOFST_FIELD_INTERLEAVED 0x249
+#define CCDC_NO_CULLING 0xffff00ff
+#endif
diff --git a/drivers/media/video/davinci/isif.c b/drivers/media/video/davinci/isif.c
new file mode 100644
index 00000000..5278fe7d
--- /dev/null
+++ b/drivers/media/video/davinci/isif.c
@@ -0,0 +1,1162 @@
+/*
+ * Copyright (C) 2008-2009 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Image Sensor Interface (ISIF) driver
+ *
+ * This driver is for configuring the ISIF IP available on DM365 or any other
+ * TI SoCs. This is used for capturing yuv or bayer video or image data
+ * from a decoder or sensor. This IP is similar to the CCDC IP on DM355
+ * and DM6446, but with enhanced or additional ip blocks. The driver
+ * configures the ISIF upon commands from the vpfe bridge driver through
+ * ccdc_hw_device interface.
+ *
+ * TODO: 1) Raw bayer parameter settings and bayer capture
+ * 2) Add support for control ioctl
+ */
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/videodev2.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/module.h>
+
+#include <mach/mux.h>
+
+#include <media/davinci/isif.h>
+#include <media/davinci/vpss.h>
+
+#include "isif_regs.h"
+#include "ccdc_hw_device.h"
+
+/* Defaults for module configuration parameters */
+static struct isif_config_params_raw isif_config_defaults = {
+ .linearize = {
+ .en = 0,
+ .corr_shft = ISIF_NO_SHIFT,
+ .scale_fact = {1, 0},
+ },
+ .df_csc = {
+ .df_or_csc = 0,
+ .csc = {
+ .en = 0,
+ },
+ },
+ .dfc = {
+ .en = 0,
+ },
+ .bclamp = {
+ .en = 0,
+ },
+ .gain_offset = {
+ .gain = {
+ .r_ye = {1, 0},
+ .gr_cy = {1, 0},
+ .gb_g = {1, 0},
+ .b_mg = {1, 0},
+ },
+ },
+ .culling = {
+ .hcpat_odd = 0xff,
+ .hcpat_even = 0xff,
+ .vcpat = 0xff,
+ },
+ .compress = {
+ .alg = ISIF_ALAW,
+ },
+};
+
+/* ISIF operation configuration */
+static struct isif_oper_config {
+ struct device *dev;
+ enum vpfe_hw_if_type if_type;
+ struct isif_ycbcr_config ycbcr;
+ struct isif_params_raw bayer;
+ enum isif_data_pack data_pack;
+ /* Master clock */
+ struct clk *mclk;
+ /* ISIF base address */
+ void __iomem *base_addr;
+ /* ISIF Linear Table 0 */
+ void __iomem *linear_tbl0_addr;
+ /* ISIF Linear Table 1 */
+ void __iomem *linear_tbl1_addr;
+} isif_cfg = {
+ .ycbcr = {
+ .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT,
+ .frm_fmt = CCDC_FRMFMT_INTERLACED,
+ .win = ISIF_WIN_NTSC,
+ .fid_pol = VPFE_PINPOL_POSITIVE,
+ .vd_pol = VPFE_PINPOL_POSITIVE,
+ .hd_pol = VPFE_PINPOL_POSITIVE,
+ .pix_order = CCDC_PIXORDER_CBYCRY,
+ .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED,
+ },
+ .bayer = {
+ .pix_fmt = CCDC_PIXFMT_RAW,
+ .frm_fmt = CCDC_FRMFMT_PROGRESSIVE,
+ .win = ISIF_WIN_VGA,
+ .fid_pol = VPFE_PINPOL_POSITIVE,
+ .vd_pol = VPFE_PINPOL_POSITIVE,
+ .hd_pol = VPFE_PINPOL_POSITIVE,
+ .gain = {
+ .r_ye = {1, 0},
+ .gr_cy = {1, 0},
+ .gb_g = {1, 0},
+ .b_mg = {1, 0},
+ },
+ .cfa_pat = ISIF_CFA_PAT_MOSAIC,
+ .data_msb = ISIF_BIT_MSB_11,
+ .config_params = {
+ .data_shift = ISIF_NO_SHIFT,
+ .col_pat_field0 = {
+ .olop = ISIF_GREEN_BLUE,
+ .olep = ISIF_BLUE,
+ .elop = ISIF_RED,
+ .elep = ISIF_GREEN_RED,
+ },
+ .col_pat_field1 = {
+ .olop = ISIF_GREEN_BLUE,
+ .olep = ISIF_BLUE,
+ .elop = ISIF_RED,
+ .elep = ISIF_GREEN_RED,
+ },
+ .test_pat_gen = 0,
+ },
+ },
+ .data_pack = ISIF_DATA_PACK8,
+};
+
+/* Raw Bayer formats */
+static const u32 isif_raw_bayer_pix_formats[] = {
+ V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16};
+
+/* Raw YUV formats */
+static const u32 isif_raw_yuv_pix_formats[] = {
+ V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV};
+
+/* register access routines */
+static inline u32 regr(u32 offset)
+{
+ return __raw_readl(isif_cfg.base_addr + offset);
+}
+
+static inline void regw(u32 val, u32 offset)
+{
+ __raw_writel(val, isif_cfg.base_addr + offset);
+}
+
+/* reg_modify() - read, modify and write register */
+static inline u32 reg_modify(u32 mask, u32 val, u32 offset)
+{
+ u32 new_val = (regr(offset) & ~mask) | (val & mask);
+
+ regw(new_val, offset);
+ return new_val;
+}
+
+static inline void regw_lin_tbl(u32 val, u32 offset, int i)
+{
+ if (!i)
+ __raw_writel(val, isif_cfg.linear_tbl0_addr + offset);
+ else
+ __raw_writel(val, isif_cfg.linear_tbl1_addr + offset);
+}
+
+static void isif_disable_all_modules(void)
+{
+ /* disable BC */
+ regw(0, CLAMPCFG);
+ /* disable vdfc */
+ regw(0, DFCCTL);
+ /* disable CSC */
+ regw(0, CSCCTL);
+ /* disable linearization */
+ regw(0, LINCFG0);
+ /* disable other modules here as they are supported */
+}
+
+static void isif_enable(int en)
+{
+ if (!en) {
+ /* Before disable isif, disable all ISIF modules */
+ isif_disable_all_modules();
+ /*
+ * wait for next VD. Assume lowest scan rate is 12 Hz. So
+ * 100 msec delay is good enough
+ */
+ msleep(100);
+ }
+ reg_modify(ISIF_SYNCEN_VDHDEN_MASK, en, SYNCEN);
+}
+
+static void isif_enable_output_to_sdram(int en)
+{
+ reg_modify(ISIF_SYNCEN_WEN_MASK, en << ISIF_SYNCEN_WEN_SHIFT, SYNCEN);
+}
+
+static void isif_config_culling(struct isif_cul *cul)
+{
+ u32 val;
+
+ /* Horizontal pattern */
+ val = (cul->hcpat_even << CULL_PAT_EVEN_LINE_SHIFT) | cul->hcpat_odd;
+ regw(val, CULH);
+
+ /* vertical pattern */
+ regw(cul->vcpat, CULV);
+
+ /* LPF */
+ reg_modify(ISIF_LPF_MASK << ISIF_LPF_SHIFT,
+ cul->en_lpf << ISIF_LPF_SHIFT, MODESET);
+}
+
+static void isif_config_gain_offset(void)
+{
+ struct isif_gain_offsets_adj *gain_off_p =
+ &isif_cfg.bayer.config_params.gain_offset;
+ u32 val;
+
+ val = (!!gain_off_p->gain_sdram_en << GAIN_SDRAM_EN_SHIFT) |
+ (!!gain_off_p->gain_ipipe_en << GAIN_IPIPE_EN_SHIFT) |
+ (!!gain_off_p->gain_h3a_en << GAIN_H3A_EN_SHIFT) |
+ (!!gain_off_p->offset_sdram_en << OFST_SDRAM_EN_SHIFT) |
+ (!!gain_off_p->offset_ipipe_en << OFST_IPIPE_EN_SHIFT) |
+ (!!gain_off_p->offset_h3a_en << OFST_H3A_EN_SHIFT);
+
+ reg_modify(GAIN_OFFSET_EN_MASK, val, CGAMMAWD);
+
+ val = (gain_off_p->gain.r_ye.integer << GAIN_INTEGER_SHIFT) |
+ gain_off_p->gain.r_ye.decimal;
+ regw(val, CRGAIN);
+
+ val = (gain_off_p->gain.gr_cy.integer << GAIN_INTEGER_SHIFT) |
+ gain_off_p->gain.gr_cy.decimal;
+ regw(val, CGRGAIN);
+
+ val = (gain_off_p->gain.gb_g.integer << GAIN_INTEGER_SHIFT) |
+ gain_off_p->gain.gb_g.decimal;
+ regw(val, CGBGAIN);
+
+ val = (gain_off_p->gain.b_mg.integer << GAIN_INTEGER_SHIFT) |
+ gain_off_p->gain.b_mg.decimal;
+ regw(val, CBGAIN);
+
+ regw(gain_off_p->offset, COFSTA);
+}
+
+static void isif_restore_defaults(void)
+{
+ enum vpss_ccdc_source_sel source = VPSS_CCDCIN;
+
+ dev_dbg(isif_cfg.dev, "\nstarting isif_restore_defaults...");
+ isif_cfg.bayer.config_params = isif_config_defaults;
+ /* Enable clock to ISIF, IPIPEIF and BL */
+ vpss_enable_clock(VPSS_CCDC_CLOCK, 1);
+ vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1);
+ vpss_enable_clock(VPSS_BL_CLOCK, 1);
+ /* Set default offset and gain */
+ isif_config_gain_offset();
+ vpss_select_ccdc_source(source);
+ dev_dbg(isif_cfg.dev, "\nEnd of isif_restore_defaults...");
+}
+
+static int isif_open(struct device *device)
+{
+ isif_restore_defaults();
+ return 0;
+}
+
+/* This function will configure the window size to be capture in ISIF reg */
+static void isif_setwin(struct v4l2_rect *image_win,
+ enum ccdc_frmfmt frm_fmt, int ppc)
+{
+ int horz_start, horz_nr_pixels;
+ int vert_start, vert_nr_lines;
+ int mid_img = 0;
+
+ dev_dbg(isif_cfg.dev, "\nStarting isif_setwin...");
+ /*
+ * ppc - per pixel count. indicates how many pixels per cell
+ * output to SDRAM. example, for ycbcr, it is one y and one c, so 2.
+ * raw capture this is 1
+ */
+ horz_start = image_win->left << (ppc - 1);
+ horz_nr_pixels = ((image_win->width) << (ppc - 1)) - 1;
+
+ /* Writing the horizontal info into the registers */
+ regw(horz_start & START_PX_HOR_MASK, SPH);
+ regw(horz_nr_pixels & NUM_PX_HOR_MASK, LNH);
+ vert_start = image_win->top;
+
+ if (frm_fmt == CCDC_FRMFMT_INTERLACED) {
+ vert_nr_lines = (image_win->height >> 1) - 1;
+ vert_start >>= 1;
+ /* To account for VD since line 0 doesn't have any data */
+ vert_start += 1;
+ } else {
+ /* To account for VD since line 0 doesn't have any data */
+ vert_start += 1;
+ vert_nr_lines = image_win->height - 1;
+ /* configure VDINT0 and VDINT1 */
+ mid_img = vert_start + (image_win->height / 2);
+ regw(mid_img, VDINT1);
+ }
+
+ regw(0, VDINT0);
+ regw(vert_start & START_VER_ONE_MASK, SLV0);
+ regw(vert_start & START_VER_TWO_MASK, SLV1);
+ regw(vert_nr_lines & NUM_LINES_VER, LNV);
+}
+
+static void isif_config_bclamp(struct isif_black_clamp *bc)
+{
+ u32 val;
+
+ /*
+ * DC Offset is always added to image data irrespective of bc enable
+ * status
+ */
+ regw(bc->dc_offset, CLDCOFST);
+
+ if (bc->en) {
+ val = bc->bc_mode_color << ISIF_BC_MODE_COLOR_SHIFT;
+
+ /* Enable BC and horizontal clamp caculation paramaters */
+ val = val | 1 | (bc->horz.mode << ISIF_HORZ_BC_MODE_SHIFT);
+
+ regw(val, CLAMPCFG);
+
+ if (bc->horz.mode != ISIF_HORZ_BC_DISABLE) {
+ /*
+ * Window count for calculation
+ * Base window selection
+ * pixel limit
+ * Horizontal size of window
+ * vertical size of the window
+ * Horizontal start position of the window
+ * Vertical start position of the window
+ */
+ val = bc->horz.win_count_calc |
+ ((!!bc->horz.base_win_sel_calc) <<
+ ISIF_HORZ_BC_WIN_SEL_SHIFT) |
+ ((!!bc->horz.clamp_pix_limit) <<
+ ISIF_HORZ_BC_PIX_LIMIT_SHIFT) |
+ (bc->horz.win_h_sz_calc <<
+ ISIF_HORZ_BC_WIN_H_SIZE_SHIFT) |
+ (bc->horz.win_v_sz_calc <<
+ ISIF_HORZ_BC_WIN_V_SIZE_SHIFT);
+ regw(val, CLHWIN0);
+
+ regw(bc->horz.win_start_h_calc, CLHWIN1);
+ regw(bc->horz.win_start_v_calc, CLHWIN2);
+ }
+
+ /* vertical clamp caculation paramaters */
+
+ /* Reset clamp value sel for previous line */
+ val |=
+ (bc->vert.reset_val_sel << ISIF_VERT_BC_RST_VAL_SEL_SHIFT) |
+ (bc->vert.line_ave_coef << ISIF_VERT_BC_LINE_AVE_COEF_SHIFT);
+ regw(val, CLVWIN0);
+
+ /* Optical Black horizontal start position */
+ regw(bc->vert.ob_start_h, CLVWIN1);
+ /* Optical Black vertical start position */
+ regw(bc->vert.ob_start_v, CLVWIN2);
+ /* Optical Black vertical size for calculation */
+ regw(bc->vert.ob_v_sz_calc, CLVWIN3);
+ /* Vertical start position for BC subtraction */
+ regw(bc->vert_start_sub, CLSV);
+ }
+}
+
+static void isif_config_linearization(struct isif_linearize *linearize)
+{
+ u32 val, i;
+
+ if (!linearize->en) {
+ regw(0, LINCFG0);
+ return;
+ }
+
+ /* shift value for correction & enable linearization (set lsb) */
+ val = (linearize->corr_shft << ISIF_LIN_CORRSFT_SHIFT) | 1;
+ regw(val, LINCFG0);
+
+ /* Scale factor */
+ val = ((!!linearize->scale_fact.integer) <<
+ ISIF_LIN_SCALE_FACT_INTEG_SHIFT) |
+ linearize->scale_fact.decimal;
+ regw(val, LINCFG1);
+
+ for (i = 0; i < ISIF_LINEAR_TAB_SIZE; i++) {
+ if (i % 2)
+ regw_lin_tbl(linearize->table[i], ((i >> 1) << 2), 1);
+ else
+ regw_lin_tbl(linearize->table[i], ((i >> 1) << 2), 0);
+ }
+}
+
+static int isif_config_dfc(struct isif_dfc *vdfc)
+{
+ /* initialize retries to loop for max ~ 250 usec */
+ u32 val, count, retries = loops_per_jiffy / (4000/HZ);
+ int i;
+
+ if (!vdfc->en)
+ return 0;
+
+ /* Correction mode */
+ val = (vdfc->corr_mode << ISIF_VDFC_CORR_MOD_SHIFT);
+
+ /* Correct whole line or partial */
+ if (vdfc->corr_whole_line)
+ val |= 1 << ISIF_VDFC_CORR_WHOLE_LN_SHIFT;
+
+ /* level shift value */
+ val |= vdfc->def_level_shift << ISIF_VDFC_LEVEL_SHFT_SHIFT;
+
+ regw(val, DFCCTL);
+
+ /* Defect saturation level */
+ regw(vdfc->def_sat_level, VDFSATLV);
+
+ regw(vdfc->table[0].pos_vert, DFCMEM0);
+ regw(vdfc->table[0].pos_horz, DFCMEM1);
+ if (vdfc->corr_mode == ISIF_VDFC_NORMAL ||
+ vdfc->corr_mode == ISIF_VDFC_HORZ_INTERPOL_IF_SAT) {
+ regw(vdfc->table[0].level_at_pos, DFCMEM2);
+ regw(vdfc->table[0].level_up_pixels, DFCMEM3);
+ regw(vdfc->table[0].level_low_pixels, DFCMEM4);
+ }
+
+ /* set DFCMARST and set DFCMWR */
+ val = regr(DFCMEMCTL) | (1 << ISIF_DFCMEMCTL_DFCMARST_SHIFT) | 1;
+ regw(val, DFCMEMCTL);
+
+ count = retries;
+ while (count && (regr(DFCMEMCTL) & 0x1))
+ count--;
+
+ if (!count) {
+ dev_dbg(isif_cfg.dev, "defect table write timeout !!!\n");
+ return -1;
+ }
+
+ for (i = 1; i < vdfc->num_vdefects; i++) {
+ regw(vdfc->table[i].pos_vert, DFCMEM0);
+ regw(vdfc->table[i].pos_horz, DFCMEM1);
+ if (vdfc->corr_mode == ISIF_VDFC_NORMAL ||
+ vdfc->corr_mode == ISIF_VDFC_HORZ_INTERPOL_IF_SAT) {
+ regw(vdfc->table[i].level_at_pos, DFCMEM2);
+ regw(vdfc->table[i].level_up_pixels, DFCMEM3);
+ regw(vdfc->table[i].level_low_pixels, DFCMEM4);
+ }
+ val = regr(DFCMEMCTL);
+ /* clear DFCMARST and set DFCMWR */
+ val &= ~BIT(ISIF_DFCMEMCTL_DFCMARST_SHIFT);
+ val |= 1;
+ regw(val, DFCMEMCTL);
+
+ count = retries;
+ while (count && (regr(DFCMEMCTL) & 0x1))
+ count--;
+
+ if (!count) {
+ dev_err(isif_cfg.dev,
+ "defect table write timeout !!!\n");
+ return -1;
+ }
+ }
+ if (vdfc->num_vdefects < ISIF_VDFC_TABLE_SIZE) {
+ /* Extra cycle needed */
+ regw(0, DFCMEM0);
+ regw(0x1FFF, DFCMEM1);
+ regw(1, DFCMEMCTL);
+ }
+
+ /* enable VDFC */
+ reg_modify((1 << ISIF_VDFC_EN_SHIFT), (1 << ISIF_VDFC_EN_SHIFT),
+ DFCCTL);
+ return 0;
+}
+
+static void isif_config_csc(struct isif_df_csc *df_csc)
+{
+ u32 val1 = 0, val2 = 0, i;
+
+ if (!df_csc->csc.en) {
+ regw(0, CSCCTL);
+ return;
+ }
+ for (i = 0; i < ISIF_CSC_NUM_COEFF; i++) {
+ if ((i % 2) == 0) {
+ /* CSCM - LSB */
+ val1 = (df_csc->csc.coeff[i].integer <<
+ ISIF_CSC_COEF_INTEG_SHIFT) |
+ df_csc->csc.coeff[i].decimal;
+ } else {
+
+ /* CSCM - MSB */
+ val2 = (df_csc->csc.coeff[i].integer <<
+ ISIF_CSC_COEF_INTEG_SHIFT) |
+ df_csc->csc.coeff[i].decimal;
+ val2 <<= ISIF_CSCM_MSB_SHIFT;
+ val2 |= val1;
+ regw(val2, (CSCM0 + ((i - 1) << 1)));
+ }
+ }
+
+ /* program the active area */
+ regw(df_csc->start_pix, FMTSPH);
+ /*
+ * one extra pixel as required for CSC. Actually number of
+ * pixel - 1 should be configured in this register. So we
+ * need to subtract 1 before writing to FMTSPH, but we will
+ * not do this since csc requires one extra pixel
+ */
+ regw(df_csc->num_pixels, FMTLNH);
+ regw(df_csc->start_line, FMTSLV);
+ /*
+ * one extra line as required for CSC. See reason documented for
+ * num_pixels
+ */
+ regw(df_csc->num_lines, FMTLNV);
+
+ /* Enable CSC */
+ regw(1, CSCCTL);
+}
+
+static int isif_config_raw(void)
+{
+ struct isif_params_raw *params = &isif_cfg.bayer;
+ struct isif_config_params_raw *module_params =
+ &isif_cfg.bayer.config_params;
+ struct vpss_pg_frame_size frame_size;
+ struct vpss_sync_pol sync;
+ u32 val;
+
+ dev_dbg(isif_cfg.dev, "\nStarting isif_config_raw..\n");
+
+ /*
+ * Configure CCDCFG register:-
+ * Set CCD Not to swap input since input is RAW data
+ * Set FID detection function to Latch at V-Sync
+ * Set WENLOG - isif valid area
+ * Set TRGSEL
+ * Set EXTRG
+ * Packed to 8 or 16 bits
+ */
+
+ val = ISIF_YCINSWP_RAW | ISIF_CCDCFG_FIDMD_LATCH_VSYNC |
+ ISIF_CCDCFG_WENLOG_AND | ISIF_CCDCFG_TRGSEL_WEN |
+ ISIF_CCDCFG_EXTRG_DISABLE | isif_cfg.data_pack;
+
+ dev_dbg(isif_cfg.dev, "Writing 0x%x to ...CCDCFG \n", val);
+ regw(val, CCDCFG);
+
+ /*
+ * Configure the vertical sync polarity(MODESET.VDPOL)
+ * Configure the horizontal sync polarity (MODESET.HDPOL)
+ * Configure frame id polarity (MODESET.FLDPOL)
+ * Configure data polarity
+ * Configure External WEN Selection
+ * Configure frame format(progressive or interlace)
+ * Configure pixel format (Input mode)
+ * Configure the data shift
+ */
+
+ val = ISIF_VDHDOUT_INPUT | (params->vd_pol << ISIF_VD_POL_SHIFT) |
+ (params->hd_pol << ISIF_HD_POL_SHIFT) |
+ (params->fid_pol << ISIF_FID_POL_SHIFT) |
+ (ISIF_DATAPOL_NORMAL << ISIF_DATAPOL_SHIFT) |
+ (ISIF_EXWEN_DISABLE << ISIF_EXWEN_SHIFT) |
+ (params->frm_fmt << ISIF_FRM_FMT_SHIFT) |
+ (params->pix_fmt << ISIF_INPUT_SHIFT) |
+ (params->config_params.data_shift << ISIF_DATASFT_SHIFT);
+
+ regw(val, MODESET);
+ dev_dbg(isif_cfg.dev, "Writing 0x%x to MODESET...\n", val);
+
+ /*
+ * Configure GAMMAWD register
+ * CFA pattern setting
+ */
+ val = params->cfa_pat << ISIF_GAMMAWD_CFA_SHIFT;
+
+ /* Gamma msb */
+ if (module_params->compress.alg == ISIF_ALAW)
+ val |= ISIF_ALAW_ENABLE;
+
+ val |= (params->data_msb << ISIF_ALAW_GAMA_WD_SHIFT);
+ regw(val, CGAMMAWD);
+
+ /* Configure DPCM compression settings */
+ if (module_params->compress.alg == ISIF_DPCM) {
+ val = BIT(ISIF_DPCM_EN_SHIFT) |
+ (module_params->compress.pred <<
+ ISIF_DPCM_PREDICTOR_SHIFT);
+ }
+
+ regw(val, MISC);
+
+ /* Configure Gain & Offset */
+ isif_config_gain_offset();
+
+ /* Configure Color pattern */
+ val = (params->config_params.col_pat_field0.olop) |
+ (params->config_params.col_pat_field0.olep << 2) |
+ (params->config_params.col_pat_field0.elop << 4) |
+ (params->config_params.col_pat_field0.elep << 6) |
+ (params->config_params.col_pat_field1.olop << 8) |
+ (params->config_params.col_pat_field1.olep << 10) |
+ (params->config_params.col_pat_field1.elop << 12) |
+ (params->config_params.col_pat_field1.elep << 14);
+ regw(val, CCOLP);
+ dev_dbg(isif_cfg.dev, "Writing %x to CCOLP ...\n", val);
+
+ /* Configure HSIZE register */
+ val = (!!params->horz_flip_en) << ISIF_HSIZE_FLIP_SHIFT;
+
+ /* calculate line offset in 32 bytes based on pack value */
+ if (isif_cfg.data_pack == ISIF_PACK_8BIT)
+ val |= ((params->win.width + 31) >> 5);
+ else if (isif_cfg.data_pack == ISIF_PACK_12BIT)
+ val |= (((params->win.width +
+ (params->win.width >> 2)) + 31) >> 5);
+ else
+ val |= (((params->win.width * 2) + 31) >> 5);
+ regw(val, HSIZE);
+
+ /* Configure SDOFST register */
+ if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) {
+ if (params->image_invert_en) {
+ /* For interlace inverse mode */
+ regw(0x4B6D, SDOFST);
+ dev_dbg(isif_cfg.dev, "Writing 0x4B6D to SDOFST...\n");
+ } else {
+ /* For interlace non inverse mode */
+ regw(0x0B6D, SDOFST);
+ dev_dbg(isif_cfg.dev, "Writing 0x0B6D to SDOFST...\n");
+ }
+ } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) {
+ if (params->image_invert_en) {
+ /* For progressive inverse mode */
+ regw(0x4000, SDOFST);
+ dev_dbg(isif_cfg.dev, "Writing 0x4000 to SDOFST...\n");
+ } else {
+ /* For progressive non inverse mode */
+ regw(0x0000, SDOFST);
+ dev_dbg(isif_cfg.dev, "Writing 0x0000 to SDOFST...\n");
+ }
+ }
+
+ /* Configure video window */
+ isif_setwin(&params->win, params->frm_fmt, 1);
+
+ /* Configure Black Clamp */
+ isif_config_bclamp(&module_params->bclamp);
+
+ /* Configure Vertical Defection Pixel Correction */
+ if (isif_config_dfc(&module_params->dfc) < 0)
+ return -EFAULT;
+
+ if (!module_params->df_csc.df_or_csc)
+ /* Configure Color Space Conversion */
+ isif_config_csc(&module_params->df_csc);
+
+ isif_config_linearization(&module_params->linearize);
+
+ /* Configure Culling */
+ isif_config_culling(&module_params->culling);
+
+ /* Configure horizontal and vertical offsets(DFC,LSC,Gain) */
+ regw(module_params->horz_offset, DATAHOFST);
+ regw(module_params->vert_offset, DATAVOFST);
+
+ /* Setup test pattern if enabled */
+ if (params->config_params.test_pat_gen) {
+ /* Use the HD/VD pol settings from user */
+ sync.ccdpg_hdpol = params->hd_pol;
+ sync.ccdpg_vdpol = params->vd_pol;
+ dm365_vpss_set_sync_pol(sync);
+ frame_size.hlpfr = isif_cfg.bayer.win.width;
+ frame_size.pplen = isif_cfg.bayer.win.height;
+ dm365_vpss_set_pg_frame_size(frame_size);
+ vpss_select_ccdc_source(VPSS_PGLPBK);
+ }
+
+ dev_dbg(isif_cfg.dev, "\nEnd of isif_config_ycbcr...\n");
+ return 0;
+}
+
+static int isif_set_buftype(enum ccdc_buftype buf_type)
+{
+ if (isif_cfg.if_type == VPFE_RAW_BAYER)
+ isif_cfg.bayer.buf_type = buf_type;
+ else
+ isif_cfg.ycbcr.buf_type = buf_type;
+
+ return 0;
+
+}
+static enum ccdc_buftype isif_get_buftype(void)
+{
+ if (isif_cfg.if_type == VPFE_RAW_BAYER)
+ return isif_cfg.bayer.buf_type;
+
+ return isif_cfg.ycbcr.buf_type;
+}
+
+static int isif_enum_pix(u32 *pix, int i)
+{
+ int ret = -EINVAL;
+
+ if (isif_cfg.if_type == VPFE_RAW_BAYER) {
+ if (i < ARRAY_SIZE(isif_raw_bayer_pix_formats)) {
+ *pix = isif_raw_bayer_pix_formats[i];
+ ret = 0;
+ }
+ } else {
+ if (i < ARRAY_SIZE(isif_raw_yuv_pix_formats)) {
+ *pix = isif_raw_yuv_pix_formats[i];
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
+
+static int isif_set_pixel_format(unsigned int pixfmt)
+{
+ if (isif_cfg.if_type == VPFE_RAW_BAYER) {
+ if (pixfmt == V4L2_PIX_FMT_SBGGR8) {
+ if ((isif_cfg.bayer.config_params.compress.alg !=
+ ISIF_ALAW) &&
+ (isif_cfg.bayer.config_params.compress.alg !=
+ ISIF_DPCM)) {
+ dev_dbg(isif_cfg.dev,
+ "Either configure A-Law or DPCM\n");
+ return -EINVAL;
+ }
+ isif_cfg.data_pack = ISIF_PACK_8BIT;
+ } else if (pixfmt == V4L2_PIX_FMT_SBGGR16) {
+ isif_cfg.bayer.config_params.compress.alg =
+ ISIF_NO_COMPRESSION;
+ isif_cfg.data_pack = ISIF_PACK_16BIT;
+ } else
+ return -EINVAL;
+ isif_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW;
+ } else {
+ if (pixfmt == V4L2_PIX_FMT_YUYV)
+ isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR;
+ else if (pixfmt == V4L2_PIX_FMT_UYVY)
+ isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
+ else
+ return -EINVAL;
+ isif_cfg.data_pack = ISIF_PACK_8BIT;
+ }
+ return 0;
+}
+
+static u32 isif_get_pixel_format(void)
+{
+ u32 pixfmt;
+
+ if (isif_cfg.if_type == VPFE_RAW_BAYER)
+ if (isif_cfg.bayer.config_params.compress.alg == ISIF_ALAW ||
+ isif_cfg.bayer.config_params.compress.alg == ISIF_DPCM)
+ pixfmt = V4L2_PIX_FMT_SBGGR8;
+ else
+ pixfmt = V4L2_PIX_FMT_SBGGR16;
+ else {
+ if (isif_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR)
+ pixfmt = V4L2_PIX_FMT_YUYV;
+ else
+ pixfmt = V4L2_PIX_FMT_UYVY;
+ }
+ return pixfmt;
+}
+
+static int isif_set_image_window(struct v4l2_rect *win)
+{
+ if (isif_cfg.if_type == VPFE_RAW_BAYER) {
+ isif_cfg.bayer.win.top = win->top;
+ isif_cfg.bayer.win.left = win->left;
+ isif_cfg.bayer.win.width = win->width;
+ isif_cfg.bayer.win.height = win->height;
+ } else {
+ isif_cfg.ycbcr.win.top = win->top;
+ isif_cfg.ycbcr.win.left = win->left;
+ isif_cfg.ycbcr.win.width = win->width;
+ isif_cfg.ycbcr.win.height = win->height;
+ }
+ return 0;
+}
+
+static void isif_get_image_window(struct v4l2_rect *win)
+{
+ if (isif_cfg.if_type == VPFE_RAW_BAYER)
+ *win = isif_cfg.bayer.win;
+ else
+ *win = isif_cfg.ycbcr.win;
+}
+
+static unsigned int isif_get_line_length(void)
+{
+ unsigned int len;
+
+ if (isif_cfg.if_type == VPFE_RAW_BAYER) {
+ if (isif_cfg.data_pack == ISIF_PACK_8BIT)
+ len = ((isif_cfg.bayer.win.width));
+ else if (isif_cfg.data_pack == ISIF_PACK_12BIT)
+ len = (((isif_cfg.bayer.win.width * 2) +
+ (isif_cfg.bayer.win.width >> 2)));
+ else
+ len = (((isif_cfg.bayer.win.width * 2)));
+ } else
+ len = (((isif_cfg.ycbcr.win.width * 2)));
+ return ALIGN(len, 32);
+}
+
+static int isif_set_frame_format(enum ccdc_frmfmt frm_fmt)
+{
+ if (isif_cfg.if_type == VPFE_RAW_BAYER)
+ isif_cfg.bayer.frm_fmt = frm_fmt;
+ else
+ isif_cfg.ycbcr.frm_fmt = frm_fmt;
+ return 0;
+}
+static enum ccdc_frmfmt isif_get_frame_format(void)
+{
+ if (isif_cfg.if_type == VPFE_RAW_BAYER)
+ return isif_cfg.bayer.frm_fmt;
+ return isif_cfg.ycbcr.frm_fmt;
+}
+
+static int isif_getfid(void)
+{
+ return (regr(MODESET) >> 15) & 0x1;
+}
+
+/* misc operations */
+static void isif_setfbaddr(unsigned long addr)
+{
+ regw((addr >> 21) & 0x07ff, CADU);
+ regw((addr >> 5) & 0x0ffff, CADL);
+}
+
+static int isif_set_hw_if_params(struct vpfe_hw_if_param *params)
+{
+ isif_cfg.if_type = params->if_type;
+
+ switch (params->if_type) {
+ case VPFE_BT656:
+ case VPFE_BT656_10BIT:
+ case VPFE_YCBCR_SYNC_8:
+ isif_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_8BIT;
+ isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
+ break;
+ case VPFE_BT1120:
+ case VPFE_YCBCR_SYNC_16:
+ isif_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_16BIT;
+ isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
+ break;
+ case VPFE_RAW_BAYER:
+ isif_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW;
+ break;
+ default:
+ dev_dbg(isif_cfg.dev, "Invalid interface type\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* This function will configure ISIF for YCbCr parameters. */
+static int isif_config_ycbcr(void)
+{
+ struct isif_ycbcr_config *params = &isif_cfg.ycbcr;
+ struct vpss_pg_frame_size frame_size;
+ u32 modeset = 0, ccdcfg = 0;
+ struct vpss_sync_pol sync;
+
+ dev_dbg(isif_cfg.dev, "\nStarting isif_config_ycbcr...");
+
+ /* configure pixel format or input mode */
+ modeset = modeset | (params->pix_fmt << ISIF_INPUT_SHIFT) |
+ (params->frm_fmt << ISIF_FRM_FMT_SHIFT) |
+ (params->fid_pol << ISIF_FID_POL_SHIFT) |
+ (params->hd_pol << ISIF_HD_POL_SHIFT) |
+ (params->vd_pol << ISIF_VD_POL_SHIFT);
+
+ /* pack the data to 8-bit ISIFCFG */
+ switch (isif_cfg.if_type) {
+ case VPFE_BT656:
+ if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) {
+ dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ modeset |= (VPFE_PINPOL_NEGATIVE << ISIF_VD_POL_SHIFT);
+ regw(3, REC656IF);
+ ccdcfg = ccdcfg | ISIF_DATA_PACK8 | ISIF_YCINSWP_YCBCR;
+ break;
+ case VPFE_BT656_10BIT:
+ if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) {
+ dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ /* setup BT.656, embedded sync */
+ regw(3, REC656IF);
+ /* enable 10 bit mode in ccdcfg */
+ ccdcfg = ccdcfg | ISIF_DATA_PACK8 | ISIF_YCINSWP_YCBCR |
+ ISIF_BW656_ENABLE;
+ break;
+ case VPFE_BT1120:
+ if (params->pix_fmt != CCDC_PIXFMT_YCBCR_16BIT) {
+ dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ regw(3, REC656IF);
+ break;
+
+ case VPFE_YCBCR_SYNC_8:
+ ccdcfg |= ISIF_DATA_PACK8;
+ ccdcfg |= ISIF_YCINSWP_YCBCR;
+ if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) {
+ dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ break;
+ case VPFE_YCBCR_SYNC_16:
+ if (params->pix_fmt != CCDC_PIXFMT_YCBCR_16BIT) {
+ dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ /* should never come here */
+ dev_dbg(isif_cfg.dev, "Invalid interface type\n");
+ return -EINVAL;
+ }
+
+ regw(modeset, MODESET);
+
+ /* Set up pix order */
+ ccdcfg |= params->pix_order << ISIF_PIX_ORDER_SHIFT;
+
+ regw(ccdcfg, CCDCFG);
+
+ /* configure video window */
+ if ((isif_cfg.if_type == VPFE_BT1120) ||
+ (isif_cfg.if_type == VPFE_YCBCR_SYNC_16))
+ isif_setwin(&params->win, params->frm_fmt, 1);
+ else
+ isif_setwin(&params->win, params->frm_fmt, 2);
+
+ /*
+ * configure the horizontal line offset
+ * this is done by rounding up width to a multiple of 16 pixels
+ * and multiply by two to account for y:cb:cr 4:2:2 data
+ */
+ regw(((((params->win.width * 2) + 31) & 0xffffffe0) >> 5), HSIZE);
+
+ /* configure the memory line offset */
+ if ((params->frm_fmt == CCDC_FRMFMT_INTERLACED) &&
+ (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED))
+ /* two fields are interleaved in memory */
+ regw(0x00000249, SDOFST);
+
+ /* Setup test pattern if enabled */
+ if (isif_cfg.bayer.config_params.test_pat_gen) {
+ sync.ccdpg_hdpol = params->hd_pol;
+ sync.ccdpg_vdpol = params->vd_pol;
+ dm365_vpss_set_sync_pol(sync);
+ dm365_vpss_set_pg_frame_size(frame_size);
+ }
+ return 0;
+}
+
+static int isif_configure(void)
+{
+ if (isif_cfg.if_type == VPFE_RAW_BAYER)
+ return isif_config_raw();
+ return isif_config_ycbcr();
+}
+
+static int isif_close(struct device *device)
+{
+ /* copy defaults to module params */
+ isif_cfg.bayer.config_params = isif_config_defaults;
+ return 0;
+}
+
+static struct ccdc_hw_device isif_hw_dev = {
+ .name = "ISIF",
+ .owner = THIS_MODULE,
+ .hw_ops = {
+ .open = isif_open,
+ .close = isif_close,
+ .enable = isif_enable,
+ .enable_out_to_sdram = isif_enable_output_to_sdram,
+ .set_hw_if_params = isif_set_hw_if_params,
+ .configure = isif_configure,
+ .set_buftype = isif_set_buftype,
+ .get_buftype = isif_get_buftype,
+ .enum_pix = isif_enum_pix,
+ .set_pixel_format = isif_set_pixel_format,
+ .get_pixel_format = isif_get_pixel_format,
+ .set_frame_format = isif_set_frame_format,
+ .get_frame_format = isif_get_frame_format,
+ .set_image_window = isif_set_image_window,
+ .get_image_window = isif_get_image_window,
+ .get_line_length = isif_get_line_length,
+ .setfbaddr = isif_setfbaddr,
+ .getfid = isif_getfid,
+ },
+};
+
+static int __init isif_probe(struct platform_device *pdev)
+{
+ void (*setup_pinmux)(void);
+ struct resource *res;
+ void *__iomem addr;
+ int status = 0, i;
+
+ /*
+ * first try to register with vpfe. If not correct platform, then we
+ * don't have to iomap
+ */
+ status = vpfe_register_ccdc_device(&isif_hw_dev);
+ if (status < 0)
+ return status;
+
+ /* Get and enable Master clock */
+ isif_cfg.mclk = clk_get(&pdev->dev, "master");
+ if (IS_ERR(isif_cfg.mclk)) {
+ status = PTR_ERR(isif_cfg.mclk);
+ goto fail_mclk;
+ }
+ if (clk_enable(isif_cfg.mclk)) {
+ status = -ENODEV;
+ goto fail_mclk;
+ }
+
+ /* Platform data holds setup_pinmux function ptr */
+ if (NULL == pdev->dev.platform_data) {
+ status = -ENODEV;
+ goto fail_mclk;
+ }
+ setup_pinmux = pdev->dev.platform_data;
+ /*
+ * setup Mux configuration for ccdc which may be different for
+ * different SoCs using this CCDC
+ */
+ setup_pinmux();
+
+ i = 0;
+ /* Get the ISIF base address, linearization table0 and table1 addr. */
+ while (i < 3) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ if (!res) {
+ status = -ENODEV;
+ goto fail_nobase_res;
+ }
+ res = request_mem_region(res->start, resource_size(res),
+ res->name);
+ if (!res) {
+ status = -EBUSY;
+ goto fail_nobase_res;
+ }
+ addr = ioremap_nocache(res->start, resource_size(res));
+ if (!addr) {
+ status = -ENOMEM;
+ goto fail_base_iomap;
+ }
+ switch (i) {
+ case 0:
+ /* ISIF base address */
+ isif_cfg.base_addr = addr;
+ break;
+ case 1:
+ /* ISIF linear tbl0 address */
+ isif_cfg.linear_tbl0_addr = addr;
+ break;
+ default:
+ /* ISIF linear tbl0 address */
+ isif_cfg.linear_tbl1_addr = addr;
+ break;
+ }
+ i++;
+ }
+ isif_cfg.dev = &pdev->dev;
+
+ printk(KERN_NOTICE "%s is registered with vpfe.\n",
+ isif_hw_dev.name);
+ return 0;
+fail_base_iomap:
+ release_mem_region(res->start, resource_size(res));
+ i--;
+fail_nobase_res:
+ if (isif_cfg.base_addr)
+ iounmap(isif_cfg.base_addr);
+ if (isif_cfg.linear_tbl0_addr)
+ iounmap(isif_cfg.linear_tbl0_addr);
+
+ while (i >= 0) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ release_mem_region(res->start, resource_size(res));
+ i--;
+ }
+fail_mclk:
+ clk_put(isif_cfg.mclk);
+ vpfe_unregister_ccdc_device(&isif_hw_dev);
+ return status;
+}
+
+static int isif_remove(struct platform_device *pdev)
+{
+ struct resource *res;
+ int i = 0;
+
+ iounmap(isif_cfg.base_addr);
+ iounmap(isif_cfg.linear_tbl0_addr);
+ iounmap(isif_cfg.linear_tbl1_addr);
+ while (i < 3) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ if (res)
+ release_mem_region(res->start, resource_size(res));
+ i++;
+ }
+ vpfe_unregister_ccdc_device(&isif_hw_dev);
+ return 0;
+}
+
+static struct platform_driver isif_driver = {
+ .driver = {
+ .name = "isif",
+ .owner = THIS_MODULE,
+ },
+ .remove = __devexit_p(isif_remove),
+ .probe = isif_probe,
+};
+
+module_platform_driver(isif_driver);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/davinci/isif_regs.h b/drivers/media/video/davinci/isif_regs.h
new file mode 100644
index 00000000..aa69a463
--- /dev/null
+++ b/drivers/media/video/davinci/isif_regs.h
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2008-2009 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _ISIF_REGS_H
+#define _ISIF_REGS_H
+
+/* ISIF registers relative offsets */
+#define SYNCEN 0x00
+#define MODESET 0x04
+#define HDW 0x08
+#define VDW 0x0c
+#define PPLN 0x10
+#define LPFR 0x14
+#define SPH 0x18
+#define LNH 0x1c
+#define SLV0 0x20
+#define SLV1 0x24
+#define LNV 0x28
+#define CULH 0x2c
+#define CULV 0x30
+#define HSIZE 0x34
+#define SDOFST 0x38
+#define CADU 0x3c
+#define CADL 0x40
+#define LINCFG0 0x44
+#define LINCFG1 0x48
+#define CCOLP 0x4c
+#define CRGAIN 0x50
+#define CGRGAIN 0x54
+#define CGBGAIN 0x58
+#define CBGAIN 0x5c
+#define COFSTA 0x60
+#define FLSHCFG0 0x64
+#define FLSHCFG1 0x68
+#define FLSHCFG2 0x6c
+#define VDINT0 0x70
+#define VDINT1 0x74
+#define VDINT2 0x78
+#define MISC 0x7c
+#define CGAMMAWD 0x80
+#define REC656IF 0x84
+#define CCDCFG 0x88
+/*****************************************************
+* Defect Correction registers
+*****************************************************/
+#define DFCCTL 0x8c
+#define VDFSATLV 0x90
+#define DFCMEMCTL 0x94
+#define DFCMEM0 0x98
+#define DFCMEM1 0x9c
+#define DFCMEM2 0xa0
+#define DFCMEM3 0xa4
+#define DFCMEM4 0xa8
+/****************************************************
+* Black Clamp registers
+****************************************************/
+#define CLAMPCFG 0xac
+#define CLDCOFST 0xb0
+#define CLSV 0xb4
+#define CLHWIN0 0xb8
+#define CLHWIN1 0xbc
+#define CLHWIN2 0xc0
+#define CLVRV 0xc4
+#define CLVWIN0 0xc8
+#define CLVWIN1 0xcc
+#define CLVWIN2 0xd0
+#define CLVWIN3 0xd4
+/****************************************************
+* Lense Shading Correction
+****************************************************/
+#define DATAHOFST 0xd8
+#define DATAVOFST 0xdc
+#define LSCHVAL 0xe0
+#define LSCVVAL 0xe4
+#define TWODLSCCFG 0xe8
+#define TWODLSCOFST 0xec
+#define TWODLSCINI 0xf0
+#define TWODLSCGRBU 0xf4
+#define TWODLSCGRBL 0xf8
+#define TWODLSCGROF 0xfc
+#define TWODLSCORBU 0x100
+#define TWODLSCORBL 0x104
+#define TWODLSCOROF 0x108
+#define TWODLSCIRQEN 0x10c
+#define TWODLSCIRQST 0x110
+/****************************************************
+* Data formatter
+****************************************************/
+#define FMTCFG 0x114
+#define FMTPLEN 0x118
+#define FMTSPH 0x11c
+#define FMTLNH 0x120
+#define FMTSLV 0x124
+#define FMTLNV 0x128
+#define FMTRLEN 0x12c
+#define FMTHCNT 0x130
+#define FMTAPTR_BASE 0x134
+/* Below macro for addresses FMTAPTR0 - FMTAPTR15 */
+#define FMTAPTR(i) (FMTAPTR_BASE + (i * 4))
+#define FMTPGMVF0 0x174
+#define FMTPGMVF1 0x178
+#define FMTPGMAPU0 0x17c
+#define FMTPGMAPU1 0x180
+#define FMTPGMAPS0 0x184
+#define FMTPGMAPS1 0x188
+#define FMTPGMAPS2 0x18c
+#define FMTPGMAPS3 0x190
+#define FMTPGMAPS4 0x194
+#define FMTPGMAPS5 0x198
+#define FMTPGMAPS6 0x19c
+#define FMTPGMAPS7 0x1a0
+/************************************************
+* Color Space Converter
+************************************************/
+#define CSCCTL 0x1a4
+#define CSCM0 0x1a8
+#define CSCM1 0x1ac
+#define CSCM2 0x1b0
+#define CSCM3 0x1b4
+#define CSCM4 0x1b8
+#define CSCM5 0x1bc
+#define CSCM6 0x1c0
+#define CSCM7 0x1c4
+#define OBWIN0 0x1c8
+#define OBWIN1 0x1cc
+#define OBWIN2 0x1d0
+#define OBWIN3 0x1d4
+#define OBVAL0 0x1d8
+#define OBVAL1 0x1dc
+#define OBVAL2 0x1e0
+#define OBVAL3 0x1e4
+#define OBVAL4 0x1e8
+#define OBVAL5 0x1ec
+#define OBVAL6 0x1f0
+#define OBVAL7 0x1f4
+#define CLKCTL 0x1f8
+
+/* Masks & Shifts below */
+#define START_PX_HOR_MASK 0x7FFF
+#define NUM_PX_HOR_MASK 0x7FFF
+#define START_VER_ONE_MASK 0x7FFF
+#define START_VER_TWO_MASK 0x7FFF
+#define NUM_LINES_VER 0x7FFF
+
+/* gain - offset masks */
+#define GAIN_INTEGER_SHIFT 9
+#define OFFSET_MASK 0xFFF
+#define GAIN_SDRAM_EN_SHIFT 12
+#define GAIN_IPIPE_EN_SHIFT 13
+#define GAIN_H3A_EN_SHIFT 14
+#define OFST_SDRAM_EN_SHIFT 8
+#define OFST_IPIPE_EN_SHIFT 9
+#define OFST_H3A_EN_SHIFT 10
+#define GAIN_OFFSET_EN_MASK 0x7700
+
+/* Culling */
+#define CULL_PAT_EVEN_LINE_SHIFT 8
+
+/* CCDCFG register */
+#define ISIF_YCINSWP_RAW (0x00 << 4)
+#define ISIF_YCINSWP_YCBCR (0x01 << 4)
+#define ISIF_CCDCFG_FIDMD_LATCH_VSYNC (0x00 << 6)
+#define ISIF_CCDCFG_WENLOG_AND (0x00 << 8)
+#define ISIF_CCDCFG_TRGSEL_WEN (0x00 << 9)
+#define ISIF_CCDCFG_EXTRG_DISABLE (0x00 << 10)
+#define ISIF_LATCH_ON_VSYNC_DISABLE (0x01 << 15)
+#define ISIF_LATCH_ON_VSYNC_ENABLE (0x00 << 15)
+#define ISIF_DATA_PACK_MASK 3
+#define ISIF_DATA_PACK16 0
+#define ISIF_DATA_PACK12 1
+#define ISIF_DATA_PACK8 2
+#define ISIF_PIX_ORDER_SHIFT 11
+#define ISIF_BW656_ENABLE (0x01 << 5)
+
+/* MODESET registers */
+#define ISIF_VDHDOUT_INPUT (0x00 << 0)
+#define ISIF_INPUT_SHIFT 12
+#define ISIF_RAW_INPUT_MODE 0
+#define ISIF_FID_POL_SHIFT 4
+#define ISIF_HD_POL_SHIFT 3
+#define ISIF_VD_POL_SHIFT 2
+#define ISIF_DATAPOL_NORMAL 0
+#define ISIF_DATAPOL_SHIFT 6
+#define ISIF_EXWEN_DISABLE 0
+#define ISIF_EXWEN_SHIFT 5
+#define ISIF_FRM_FMT_SHIFT 7
+#define ISIF_DATASFT_SHIFT 8
+#define ISIF_LPF_SHIFT 14
+#define ISIF_LPF_MASK 1
+
+/* GAMMAWD registers */
+#define ISIF_ALAW_GAMA_WD_MASK 0xF
+#define ISIF_ALAW_GAMA_WD_SHIFT 1
+#define ISIF_ALAW_ENABLE 1
+#define ISIF_GAMMAWD_CFA_SHIFT 5
+
+/* HSIZE registers */
+#define ISIF_HSIZE_FLIP_MASK 1
+#define ISIF_HSIZE_FLIP_SHIFT 12
+
+/* MISC registers */
+#define ISIF_DPCM_EN_SHIFT 12
+#define ISIF_DPCM_PREDICTOR_SHIFT 13
+
+/* Black clamp related */
+#define ISIF_BC_MODE_COLOR_SHIFT 4
+#define ISIF_HORZ_BC_MODE_SHIFT 1
+#define ISIF_HORZ_BC_WIN_SEL_SHIFT 5
+#define ISIF_HORZ_BC_PIX_LIMIT_SHIFT 6
+#define ISIF_HORZ_BC_WIN_H_SIZE_SHIFT 8
+#define ISIF_HORZ_BC_WIN_V_SIZE_SHIFT 12
+#define ISIF_VERT_BC_RST_VAL_SEL_SHIFT 4
+#define ISIF_VERT_BC_LINE_AVE_COEF_SHIFT 8
+
+/* VDFC registers */
+#define ISIF_VDFC_EN_SHIFT 4
+#define ISIF_VDFC_CORR_MOD_SHIFT 5
+#define ISIF_VDFC_CORR_WHOLE_LN_SHIFT 7
+#define ISIF_VDFC_LEVEL_SHFT_SHIFT 8
+#define ISIF_VDFC_POS_MASK 0x1FFF
+#define ISIF_DFCMEMCTL_DFCMARST_SHIFT 2
+
+/* CSC registers */
+#define ISIF_CSC_COEF_INTEG_MASK 7
+#define ISIF_CSC_COEF_DECIMAL_MASK 0x1f
+#define ISIF_CSC_COEF_INTEG_SHIFT 5
+#define ISIF_CSCM_MSB_SHIFT 8
+#define ISIF_DF_CSC_SPH_MASK 0x1FFF
+#define ISIF_DF_CSC_LNH_MASK 0x1FFF
+#define ISIF_DF_CSC_SLV_MASK 0x1FFF
+#define ISIF_DF_CSC_LNV_MASK 0x1FFF
+#define ISIF_DF_NUMLINES 0x7FFF
+#define ISIF_DF_NUMPIX 0x1FFF
+
+/* Offsets for LSC/DFC/Gain */
+#define ISIF_DATA_H_OFFSET_MASK 0x1FFF
+#define ISIF_DATA_V_OFFSET_MASK 0x1FFF
+
+/* Linearization */
+#define ISIF_LIN_CORRSFT_SHIFT 4
+#define ISIF_LIN_SCALE_FACT_INTEG_SHIFT 10
+
+
+/* Pattern registers */
+#define ISIF_PG_EN (1 << 3)
+#define ISIF_SEL_PG_SRC (3 << 4)
+#define ISIF_PG_VD_POL_SHIFT 0
+#define ISIF_PG_HD_POL_SHIFT 1
+
+/*random other junk*/
+#define ISIF_SYNCEN_VDHDEN_MASK (1 << 0)
+#define ISIF_SYNCEN_WEN_MASK (1 << 1)
+#define ISIF_SYNCEN_WEN_SHIFT 1
+
+#endif
diff --git a/drivers/media/video/davinci/vpbe.c b/drivers/media/video/davinci/vpbe.c
new file mode 100644
index 00000000..c4a82a1a
--- /dev/null
+++ b/drivers/media/video/davinci/vpbe.c
@@ -0,0 +1,886 @@
+/*
+ * Copyright (C) 2010 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <media/v4l2-device.h>
+#include <media/davinci/vpbe_types.h>
+#include <media/davinci/vpbe.h>
+#include <media/davinci/vpss.h>
+#include <media/davinci/vpbe_venc.h>
+
+#define VPBE_DEFAULT_OUTPUT "Composite"
+#define VPBE_DEFAULT_MODE "ntsc"
+
+static char *def_output = VPBE_DEFAULT_OUTPUT;
+static char *def_mode = VPBE_DEFAULT_MODE;
+static int debug;
+
+module_param(def_output, charp, S_IRUGO);
+module_param(def_mode, charp, S_IRUGO);
+module_param(debug, int, 0644);
+
+MODULE_PARM_DESC(def_output, "vpbe output name (default:Composite)");
+MODULE_PARM_DESC(def_mode, "vpbe output mode name (default:ntsc");
+MODULE_PARM_DESC(debug, "Debug level 0-1");
+
+MODULE_DESCRIPTION("TI DMXXX VPBE Display controller");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Texas Instruments");
+
+/**
+ * vpbe_current_encoder_info - Get config info for current encoder
+ * @vpbe_dev - vpbe device ptr
+ *
+ * Return ptr to current encoder config info
+ */
+static struct encoder_config_info*
+vpbe_current_encoder_info(struct vpbe_device *vpbe_dev)
+{
+ struct vpbe_config *cfg = vpbe_dev->cfg;
+ int index = vpbe_dev->current_sd_index;
+
+ return ((index == 0) ? &cfg->venc :
+ &cfg->ext_encoders[index-1]);
+}
+
+/**
+ * vpbe_find_encoder_sd_index - Given a name find encoder sd index
+ *
+ * @vpbe_config - ptr to vpbe cfg
+ * @output_index - index used by application
+ *
+ * Return sd index of the encoder
+ */
+static int vpbe_find_encoder_sd_index(struct vpbe_config *cfg,
+ int index)
+{
+ char *encoder_name = cfg->outputs[index].subdev_name;
+ int i;
+
+ /* Venc is always first */
+ if (!strcmp(encoder_name, cfg->venc.module_name))
+ return 0;
+
+ for (i = 0; i < cfg->num_ext_encoders; i++) {
+ if (!strcmp(encoder_name,
+ cfg->ext_encoders[i].module_name))
+ return i+1;
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * vpbe_g_cropcap - Get crop capabilities of the display
+ * @vpbe_dev - vpbe device ptr
+ * @cropcap - cropcap is a ptr to struct v4l2_cropcap
+ *
+ * Update the crop capabilities in crop cap for current
+ * mode
+ */
+static int vpbe_g_cropcap(struct vpbe_device *vpbe_dev,
+ struct v4l2_cropcap *cropcap)
+{
+ if (NULL == cropcap)
+ return -EINVAL;
+ cropcap->bounds.left = 0;
+ cropcap->bounds.top = 0;
+ cropcap->bounds.width = vpbe_dev->current_timings.xres;
+ cropcap->bounds.height = vpbe_dev->current_timings.yres;
+ cropcap->defrect = cropcap->bounds;
+
+ return 0;
+}
+
+/**
+ * vpbe_enum_outputs - enumerate outputs
+ * @vpbe_dev - vpbe device ptr
+ * @output - ptr to v4l2_output structure
+ *
+ * Enumerates the outputs available at the vpbe display
+ * returns the status, -EINVAL if end of output list
+ */
+static int vpbe_enum_outputs(struct vpbe_device *vpbe_dev,
+ struct v4l2_output *output)
+{
+ struct vpbe_config *cfg = vpbe_dev->cfg;
+ int temp_index = output->index;
+
+ if (temp_index >= cfg->num_outputs)
+ return -EINVAL;
+
+ *output = cfg->outputs[temp_index].output;
+ output->index = temp_index;
+
+ return 0;
+}
+
+static int vpbe_get_mode_info(struct vpbe_device *vpbe_dev, char *mode,
+ int output_index)
+{
+ struct vpbe_config *cfg = vpbe_dev->cfg;
+ struct vpbe_enc_mode_info var;
+ int curr_output = output_index;
+ int i;
+
+ if (NULL == mode)
+ return -EINVAL;
+
+ for (i = 0; i < cfg->outputs[curr_output].num_modes; i++) {
+ var = cfg->outputs[curr_output].modes[i];
+ if (!strcmp(mode, var.name)) {
+ vpbe_dev->current_timings = var;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int vpbe_get_current_mode_info(struct vpbe_device *vpbe_dev,
+ struct vpbe_enc_mode_info *mode_info)
+{
+ if (NULL == mode_info)
+ return -EINVAL;
+
+ *mode_info = vpbe_dev->current_timings;
+
+ return 0;
+}
+
+static int vpbe_get_dv_preset_info(struct vpbe_device *vpbe_dev,
+ unsigned int dv_preset)
+{
+ struct vpbe_config *cfg = vpbe_dev->cfg;
+ struct vpbe_enc_mode_info var;
+ int curr_output = vpbe_dev->current_out_index;
+ int i;
+
+ for (i = 0; i < vpbe_dev->cfg->outputs[curr_output].num_modes; i++) {
+ var = cfg->outputs[curr_output].modes[i];
+ if ((var.timings_type & VPBE_ENC_DV_PRESET) &&
+ (var.timings.dv_preset == dv_preset)) {
+ vpbe_dev->current_timings = var;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+/* Get std by std id */
+static int vpbe_get_std_info(struct vpbe_device *vpbe_dev,
+ v4l2_std_id std_id)
+{
+ struct vpbe_config *cfg = vpbe_dev->cfg;
+ struct vpbe_enc_mode_info var;
+ int curr_output = vpbe_dev->current_out_index;
+ int i;
+
+ for (i = 0; i < vpbe_dev->cfg->outputs[curr_output].num_modes; i++) {
+ var = cfg->outputs[curr_output].modes[i];
+ if ((var.timings_type & VPBE_ENC_STD) &&
+ (var.timings.std_id & std_id)) {
+ vpbe_dev->current_timings = var;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int vpbe_get_std_info_by_name(struct vpbe_device *vpbe_dev,
+ char *std_name)
+{
+ struct vpbe_config *cfg = vpbe_dev->cfg;
+ struct vpbe_enc_mode_info var;
+ int curr_output = vpbe_dev->current_out_index;
+ int i;
+
+ for (i = 0; i < vpbe_dev->cfg->outputs[curr_output].num_modes; i++) {
+ var = cfg->outputs[curr_output].modes[i];
+ if (!strcmp(var.name, std_name)) {
+ vpbe_dev->current_timings = var;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * vpbe_set_output - Set output
+ * @vpbe_dev - vpbe device ptr
+ * @index - index of output
+ *
+ * Set vpbe output to the output specified by the index
+ */
+static int vpbe_set_output(struct vpbe_device *vpbe_dev, int index)
+{
+ struct encoder_config_info *curr_enc_info =
+ vpbe_current_encoder_info(vpbe_dev);
+ struct vpbe_config *cfg = vpbe_dev->cfg;
+ struct venc_platform_data *venc_device = vpbe_dev->venc_device;
+ enum v4l2_mbus_pixelcode if_params;
+ int enc_out_index;
+ int sd_index;
+ int ret = 0;
+
+ if (index >= cfg->num_outputs)
+ return -EINVAL;
+
+ mutex_lock(&vpbe_dev->lock);
+
+ sd_index = vpbe_dev->current_sd_index;
+ enc_out_index = cfg->outputs[index].output.index;
+ /*
+ * Currently we switch the encoder based on output selected
+ * by the application. If media controller is implemented later
+ * there is will be an API added to setup_link between venc
+ * and external encoder. So in that case below comparison always
+ * match and encoder will not be switched. But if application
+ * chose not to use media controller, then this provides current
+ * way of switching encoder at the venc output.
+ */
+ if (strcmp(curr_enc_info->module_name,
+ cfg->outputs[index].subdev_name)) {
+ /* Need to switch the encoder at the output */
+ sd_index = vpbe_find_encoder_sd_index(cfg, index);
+ if (sd_index < 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if_params = cfg->outputs[index].if_params;
+ venc_device->setup_if_config(if_params);
+ if (ret)
+ goto out;
+ }
+
+ /* Set output at the encoder */
+ ret = v4l2_subdev_call(vpbe_dev->encoders[sd_index], video,
+ s_routing, 0, enc_out_index, 0);
+ if (ret)
+ goto out;
+
+ /*
+ * It is assumed that venc or extenal encoder will set a default
+ * mode in the sub device. For external encoder or LCD pannel output,
+ * we also need to set up the lcd port for the required mode. So setup
+ * the lcd port for the default mode that is configured in the board
+ * arch/arm/mach-davinci/board-dm355-evm.setup file for the external
+ * encoder.
+ */
+ ret = vpbe_get_mode_info(vpbe_dev,
+ cfg->outputs[index].default_mode, index);
+ if (!ret) {
+ struct osd_state *osd_device = vpbe_dev->osd_device;
+
+ osd_device->ops.set_left_margin(osd_device,
+ vpbe_dev->current_timings.left_margin);
+ osd_device->ops.set_top_margin(osd_device,
+ vpbe_dev->current_timings.upper_margin);
+ vpbe_dev->current_sd_index = sd_index;
+ vpbe_dev->current_out_index = index;
+ }
+out:
+ mutex_unlock(&vpbe_dev->lock);
+ return ret;
+}
+
+static int vpbe_set_default_output(struct vpbe_device *vpbe_dev)
+{
+ struct vpbe_config *cfg = vpbe_dev->cfg;
+ int ret = 0;
+ int i;
+
+ for (i = 0; i < cfg->num_outputs; i++) {
+ if (!strcmp(def_output,
+ cfg->outputs[i].output.name)) {
+ ret = vpbe_set_output(vpbe_dev, i);
+ if (!ret)
+ vpbe_dev->current_out_index = i;
+ return ret;
+ }
+ }
+ return ret;
+}
+
+/**
+ * vpbe_get_output - Get output
+ * @vpbe_dev - vpbe device ptr
+ *
+ * return current vpbe output to the the index
+ */
+static unsigned int vpbe_get_output(struct vpbe_device *vpbe_dev)
+{
+ return vpbe_dev->current_out_index;
+}
+
+/**
+ * vpbe_s_dv_preset - Set the given preset timings in the encoder
+ *
+ * Sets the preset if supported by the current encoder. Return the status.
+ * 0 - success & -EINVAL on error
+ */
+static int vpbe_s_dv_preset(struct vpbe_device *vpbe_dev,
+ struct v4l2_dv_preset *dv_preset)
+{
+ struct vpbe_config *cfg = vpbe_dev->cfg;
+ int out_index = vpbe_dev->current_out_index;
+ int sd_index = vpbe_dev->current_sd_index;
+ int ret;
+
+
+ if (!(cfg->outputs[out_index].output.capabilities &
+ V4L2_OUT_CAP_PRESETS))
+ return -EINVAL;
+
+ ret = vpbe_get_dv_preset_info(vpbe_dev, dv_preset->preset);
+
+ if (ret)
+ return ret;
+
+ mutex_lock(&vpbe_dev->lock);
+
+
+ ret = v4l2_subdev_call(vpbe_dev->encoders[sd_index], video,
+ s_dv_preset, dv_preset);
+ if (!ret && (vpbe_dev->amp != NULL)) {
+ /* Call amplifier subdevice */
+ ret = v4l2_subdev_call(vpbe_dev->amp, video,
+ s_dv_preset, dv_preset);
+ }
+ /* set the lcd controller output for the given mode */
+ if (!ret) {
+ struct osd_state *osd_device = vpbe_dev->osd_device;
+
+ osd_device->ops.set_left_margin(osd_device,
+ vpbe_dev->current_timings.left_margin);
+ osd_device->ops.set_top_margin(osd_device,
+ vpbe_dev->current_timings.upper_margin);
+ }
+ mutex_unlock(&vpbe_dev->lock);
+
+ return ret;
+}
+
+/**
+ * vpbe_g_dv_preset - Get the preset in the current encoder
+ *
+ * Get the preset in the current encoder. Return the status. 0 - success
+ * -EINVAL on error
+ */
+static int vpbe_g_dv_preset(struct vpbe_device *vpbe_dev,
+ struct v4l2_dv_preset *dv_preset)
+{
+ if (vpbe_dev->current_timings.timings_type &
+ VPBE_ENC_DV_PRESET) {
+ dv_preset->preset = vpbe_dev->current_timings.timings.dv_preset;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * vpbe_enum_dv_presets - Enumerate the dv presets in the current encoder
+ *
+ * Get the preset in the current encoder. Return the status. 0 - success
+ * -EINVAL on error
+ */
+static int vpbe_enum_dv_presets(struct vpbe_device *vpbe_dev,
+ struct v4l2_dv_enum_preset *preset_info)
+{
+ struct vpbe_config *cfg = vpbe_dev->cfg;
+ int out_index = vpbe_dev->current_out_index;
+ struct vpbe_output *output = &cfg->outputs[out_index];
+ int j = 0;
+ int i;
+
+ if (!(output->output.capabilities & V4L2_OUT_CAP_PRESETS))
+ return -EINVAL;
+
+ for (i = 0; i < output->num_modes; i++) {
+ if (output->modes[i].timings_type == VPBE_ENC_DV_PRESET) {
+ if (j == preset_info->index)
+ break;
+ j++;
+ }
+ }
+
+ if (i == output->num_modes)
+ return -EINVAL;
+
+ return v4l_fill_dv_preset_info(output->modes[i].timings.dv_preset,
+ preset_info);
+}
+
+/**
+ * vpbe_s_std - Set the given standard in the encoder
+ *
+ * Sets the standard if supported by the current encoder. Return the status.
+ * 0 - success & -EINVAL on error
+ */
+static int vpbe_s_std(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id)
+{
+ struct vpbe_config *cfg = vpbe_dev->cfg;
+ int out_index = vpbe_dev->current_out_index;
+ int sd_index = vpbe_dev->current_sd_index;
+ int ret;
+
+ if (!(cfg->outputs[out_index].output.capabilities &
+ V4L2_OUT_CAP_STD))
+ return -EINVAL;
+
+ ret = vpbe_get_std_info(vpbe_dev, *std_id);
+ if (ret)
+ return ret;
+
+ mutex_lock(&vpbe_dev->lock);
+
+ ret = v4l2_subdev_call(vpbe_dev->encoders[sd_index], video,
+ s_std_output, *std_id);
+ /* set the lcd controller output for the given mode */
+ if (!ret) {
+ struct osd_state *osd_device = vpbe_dev->osd_device;
+
+ osd_device->ops.set_left_margin(osd_device,
+ vpbe_dev->current_timings.left_margin);
+ osd_device->ops.set_top_margin(osd_device,
+ vpbe_dev->current_timings.upper_margin);
+ }
+ mutex_unlock(&vpbe_dev->lock);
+
+ return ret;
+}
+
+/**
+ * vpbe_g_std - Get the standard in the current encoder
+ *
+ * Get the standard in the current encoder. Return the status. 0 - success
+ * -EINVAL on error
+ */
+static int vpbe_g_std(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id)
+{
+ struct vpbe_enc_mode_info cur_timings = vpbe_dev->current_timings;
+
+ if (cur_timings.timings_type & VPBE_ENC_STD) {
+ *std_id = cur_timings.timings.std_id;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * vpbe_set_mode - Set mode in the current encoder using mode info
+ *
+ * Use the mode string to decide what timings to set in the encoder
+ * This is typically useful when fbset command is used to change the current
+ * timings by specifying a string to indicate the timings.
+ */
+static int vpbe_set_mode(struct vpbe_device *vpbe_dev,
+ struct vpbe_enc_mode_info *mode_info)
+{
+ struct vpbe_enc_mode_info *preset_mode = NULL;
+ struct vpbe_config *cfg = vpbe_dev->cfg;
+ struct v4l2_dv_preset dv_preset;
+ struct osd_state *osd_device;
+ int out_index = vpbe_dev->current_out_index;
+ int ret = 0;
+ int i;
+
+ if ((NULL == mode_info) || (NULL == mode_info->name))
+ return -EINVAL;
+
+ for (i = 0; i < cfg->outputs[out_index].num_modes; i++) {
+ if (!strcmp(mode_info->name,
+ cfg->outputs[out_index].modes[i].name)) {
+ preset_mode = &cfg->outputs[out_index].modes[i];
+ /*
+ * it may be one of the 3 timings type. Check and
+ * invoke right API
+ */
+ if (preset_mode->timings_type & VPBE_ENC_STD)
+ return vpbe_s_std(vpbe_dev,
+ &preset_mode->timings.std_id);
+ if (preset_mode->timings_type & VPBE_ENC_DV_PRESET) {
+ dv_preset.preset =
+ preset_mode->timings.dv_preset;
+ return vpbe_s_dv_preset(vpbe_dev, &dv_preset);
+ }
+ }
+ }
+
+ /* Only custom timing should reach here */
+ if (preset_mode == NULL)
+ return -EINVAL;
+
+ mutex_lock(&vpbe_dev->lock);
+
+ osd_device = vpbe_dev->osd_device;
+ vpbe_dev->current_timings = *preset_mode;
+ osd_device->ops.set_left_margin(osd_device,
+ vpbe_dev->current_timings.left_margin);
+ osd_device->ops.set_top_margin(osd_device,
+ vpbe_dev->current_timings.upper_margin);
+
+ mutex_unlock(&vpbe_dev->lock);
+
+ return ret;
+}
+
+static int vpbe_set_default_mode(struct vpbe_device *vpbe_dev)
+{
+ int ret;
+
+ ret = vpbe_get_std_info_by_name(vpbe_dev, def_mode);
+ if (ret)
+ return ret;
+
+ /* set the default mode in the encoder */
+ return vpbe_set_mode(vpbe_dev, &vpbe_dev->current_timings);
+}
+
+static int platform_device_get(struct device *dev, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct vpbe_device *vpbe_dev = data;
+
+ if (strcmp("vpbe-osd", pdev->name) == 0)
+ vpbe_dev->osd_device = platform_get_drvdata(pdev);
+ if (strcmp("vpbe-venc", pdev->name) == 0)
+ vpbe_dev->venc_device = dev_get_platdata(&pdev->dev);
+
+ return 0;
+}
+
+/**
+ * vpbe_initialize() - Initialize the vpbe display controller
+ * @vpbe_dev - vpbe device ptr
+ *
+ * Master frame buffer device drivers calls this to initialize vpbe
+ * display controller. This will then registers v4l2 device and the sub
+ * devices and sets a current encoder sub device for display. v4l2 display
+ * device driver is the master and frame buffer display device driver is
+ * the slave. Frame buffer display driver checks the initialized during
+ * probe and exit if not initialized. Returns status.
+ */
+static int vpbe_initialize(struct device *dev, struct vpbe_device *vpbe_dev)
+{
+ struct encoder_config_info *enc_info;
+ struct amp_config_info *amp_info;
+ struct v4l2_subdev **enc_subdev;
+ struct osd_state *osd_device;
+ struct i2c_adapter *i2c_adap;
+ int output_index;
+ int num_encoders;
+ int ret = 0;
+ int err;
+ int i;
+
+ /*
+ * v4l2 abd FBDev frame buffer devices will get the vpbe_dev pointer
+ * from the platform device by iteration of platform drivers and
+ * matching with device name
+ */
+ if (NULL == vpbe_dev || NULL == dev) {
+ printk(KERN_ERR "Null device pointers.\n");
+ return -ENODEV;
+ }
+
+ if (vpbe_dev->initialized)
+ return 0;
+
+ mutex_lock(&vpbe_dev->lock);
+
+ if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0) {
+ /* We have dac clock available for platform */
+ vpbe_dev->dac_clk = clk_get(vpbe_dev->pdev, "vpss_dac");
+ if (IS_ERR(vpbe_dev->dac_clk)) {
+ ret = PTR_ERR(vpbe_dev->dac_clk);
+ goto vpbe_unlock;
+ }
+ if (clk_enable(vpbe_dev->dac_clk)) {
+ ret = -ENODEV;
+ goto vpbe_unlock;
+ }
+ }
+
+ /* first enable vpss clocks */
+ vpss_enable_clock(VPSS_VPBE_CLOCK, 1);
+
+ /* First register a v4l2 device */
+ ret = v4l2_device_register(dev, &vpbe_dev->v4l2_dev);
+ if (ret) {
+ v4l2_err(dev->driver,
+ "Unable to register v4l2 device.\n");
+ goto vpbe_fail_clock;
+ }
+ v4l2_info(&vpbe_dev->v4l2_dev, "vpbe v4l2 device registered\n");
+
+ err = bus_for_each_dev(&platform_bus_type, NULL, vpbe_dev,
+ platform_device_get);
+ if (err < 0)
+ return err;
+
+ vpbe_dev->venc = venc_sub_dev_init(&vpbe_dev->v4l2_dev,
+ vpbe_dev->cfg->venc.module_name);
+ /* register venc sub device */
+ if (vpbe_dev->venc == NULL) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "vpbe unable to init venc sub device\n");
+ ret = -ENODEV;
+ goto vpbe_fail_v4l2_device;
+ }
+ /* initialize osd device */
+ osd_device = vpbe_dev->osd_device;
+
+ if (NULL != osd_device->ops.initialize) {
+ err = osd_device->ops.initialize(osd_device);
+ if (err) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "unable to initialize the OSD device");
+ err = -ENOMEM;
+ goto vpbe_fail_v4l2_device;
+ }
+ }
+
+ /*
+ * Register any external encoders that are configured. At index 0 we
+ * store venc sd index.
+ */
+ num_encoders = vpbe_dev->cfg->num_ext_encoders + 1;
+ vpbe_dev->encoders = kmalloc(
+ sizeof(struct v4l2_subdev *)*num_encoders,
+ GFP_KERNEL);
+ if (NULL == vpbe_dev->encoders) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "unable to allocate memory for encoders sub devices");
+ ret = -ENOMEM;
+ goto vpbe_fail_v4l2_device;
+ }
+
+ i2c_adap = i2c_get_adapter(vpbe_dev->cfg->i2c_adapter_id);
+ for (i = 0; i < (vpbe_dev->cfg->num_ext_encoders + 1); i++) {
+ if (i == 0) {
+ /* venc is at index 0 */
+ enc_subdev = &vpbe_dev->encoders[i];
+ *enc_subdev = vpbe_dev->venc;
+ continue;
+ }
+ enc_info = &vpbe_dev->cfg->ext_encoders[i];
+ if (enc_info->is_i2c) {
+ enc_subdev = &vpbe_dev->encoders[i];
+ *enc_subdev = v4l2_i2c_new_subdev_board(
+ &vpbe_dev->v4l2_dev, i2c_adap,
+ &enc_info->board_info, NULL);
+ if (*enc_subdev)
+ v4l2_info(&vpbe_dev->v4l2_dev,
+ "v4l2 sub device %s registered\n",
+ enc_info->module_name);
+ else {
+ v4l2_err(&vpbe_dev->v4l2_dev, "encoder %s"
+ " failed to register",
+ enc_info->module_name);
+ ret = -ENODEV;
+ goto vpbe_fail_sd_register;
+ }
+ } else
+ v4l2_warn(&vpbe_dev->v4l2_dev, "non-i2c encoders"
+ " currently not supported");
+ }
+ /* Add amplifier subdevice for dm365 */
+ if ((strcmp(vpbe_dev->cfg->module_name, "dm365-vpbe-display") == 0) &&
+ vpbe_dev->cfg->amp != NULL) {
+ amp_info = vpbe_dev->cfg->amp;
+ if (amp_info->is_i2c) {
+ vpbe_dev->amp = v4l2_i2c_new_subdev_board(
+ &vpbe_dev->v4l2_dev, i2c_adap,
+ &amp_info->board_info, NULL);
+ if (!vpbe_dev->amp) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "amplifier %s failed to register",
+ amp_info->module_name);
+ ret = -ENODEV;
+ goto vpbe_fail_amp_register;
+ }
+ v4l2_info(&vpbe_dev->v4l2_dev,
+ "v4l2 sub device %s registered\n",
+ amp_info->module_name);
+ } else {
+ vpbe_dev->amp = NULL;
+ v4l2_warn(&vpbe_dev->v4l2_dev, "non-i2c amplifiers"
+ " currently not supported");
+ }
+ } else {
+ vpbe_dev->amp = NULL;
+ }
+
+ /* set the current encoder and output to that of venc by default */
+ vpbe_dev->current_sd_index = 0;
+ vpbe_dev->current_out_index = 0;
+ output_index = 0;
+
+ mutex_unlock(&vpbe_dev->lock);
+
+ printk(KERN_NOTICE "Setting default output to %s\n", def_output);
+ ret = vpbe_set_default_output(vpbe_dev);
+ if (ret) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Failed to set default output %s",
+ def_output);
+ return ret;
+ }
+
+ printk(KERN_NOTICE "Setting default mode to %s\n", def_mode);
+ ret = vpbe_set_default_mode(vpbe_dev);
+ if (ret) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Failed to set default mode %s",
+ def_mode);
+ return ret;
+ }
+ vpbe_dev->initialized = 1;
+ /* TBD handling of bootargs for default output and mode */
+ return 0;
+
+vpbe_fail_amp_register:
+ kfree(vpbe_dev->amp);
+vpbe_fail_sd_register:
+ kfree(vpbe_dev->encoders);
+vpbe_fail_v4l2_device:
+ v4l2_device_unregister(&vpbe_dev->v4l2_dev);
+vpbe_fail_clock:
+ if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0)
+ clk_put(vpbe_dev->dac_clk);
+vpbe_unlock:
+ mutex_unlock(&vpbe_dev->lock);
+ return ret;
+}
+
+/**
+ * vpbe_deinitialize() - de-initialize the vpbe display controller
+ * @dev - Master and slave device ptr
+ *
+ * vpbe_master and slave frame buffer devices calls this to de-initialize
+ * the display controller. It is called when master and slave device
+ * driver modules are removed and no longer requires the display controller.
+ */
+static void vpbe_deinitialize(struct device *dev, struct vpbe_device *vpbe_dev)
+{
+ v4l2_device_unregister(&vpbe_dev->v4l2_dev);
+ if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0)
+ clk_put(vpbe_dev->dac_clk);
+
+ kfree(vpbe_dev->amp);
+ kfree(vpbe_dev->encoders);
+ vpbe_dev->initialized = 0;
+ /* disable vpss clocks */
+ vpss_enable_clock(VPSS_VPBE_CLOCK, 0);
+}
+
+static struct vpbe_device_ops vpbe_dev_ops = {
+ .g_cropcap = vpbe_g_cropcap,
+ .enum_outputs = vpbe_enum_outputs,
+ .set_output = vpbe_set_output,
+ .get_output = vpbe_get_output,
+ .s_dv_preset = vpbe_s_dv_preset,
+ .g_dv_preset = vpbe_g_dv_preset,
+ .enum_dv_presets = vpbe_enum_dv_presets,
+ .s_std = vpbe_s_std,
+ .g_std = vpbe_g_std,
+ .initialize = vpbe_initialize,
+ .deinitialize = vpbe_deinitialize,
+ .get_mode_info = vpbe_get_current_mode_info,
+ .set_mode = vpbe_set_mode,
+};
+
+static __devinit int vpbe_probe(struct platform_device *pdev)
+{
+ struct vpbe_device *vpbe_dev;
+ struct vpbe_config *cfg;
+ int ret = -EINVAL;
+
+ if (pdev->dev.platform_data == NULL) {
+ v4l2_err(pdev->dev.driver, "No platform data\n");
+ return -ENODEV;
+ }
+ cfg = pdev->dev.platform_data;
+
+ if (!cfg->module_name[0] ||
+ !cfg->osd.module_name[0] ||
+ !cfg->venc.module_name[0]) {
+ v4l2_err(pdev->dev.driver, "vpbe display module names not"
+ " defined\n");
+ return ret;
+ }
+
+ vpbe_dev = kzalloc(sizeof(*vpbe_dev), GFP_KERNEL);
+ if (vpbe_dev == NULL) {
+ v4l2_err(pdev->dev.driver, "Unable to allocate memory"
+ " for vpbe_device\n");
+ return -ENOMEM;
+ }
+ vpbe_dev->cfg = cfg;
+ vpbe_dev->ops = vpbe_dev_ops;
+ vpbe_dev->pdev = &pdev->dev;
+
+ if (cfg->outputs->num_modes > 0)
+ vpbe_dev->current_timings = vpbe_dev->cfg->outputs[0].modes[0];
+ else {
+ kfree(vpbe_dev);
+ return -ENODEV;
+ }
+
+ /* set the driver data in platform device */
+ platform_set_drvdata(pdev, vpbe_dev);
+ mutex_init(&vpbe_dev->lock);
+
+ return 0;
+}
+
+static int vpbe_remove(struct platform_device *device)
+{
+ struct vpbe_device *vpbe_dev = platform_get_drvdata(device);
+
+ kfree(vpbe_dev);
+
+ return 0;
+}
+
+static struct platform_driver vpbe_driver = {
+ .driver = {
+ .name = "vpbe_controller",
+ .owner = THIS_MODULE,
+ },
+ .probe = vpbe_probe,
+ .remove = vpbe_remove,
+};
+
+module_platform_driver(vpbe_driver);
diff --git a/drivers/media/video/davinci/vpbe_display.c b/drivers/media/video/davinci/vpbe_display.c
new file mode 100644
index 00000000..1f3b1c72
--- /dev/null
+++ b/drivers/media/video/davinci/vpbe_display.c
@@ -0,0 +1,1824 @@
+/*
+ * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+
+#include <asm/pgtable.h>
+#include <mach/cputype.h>
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/davinci/vpbe_display.h>
+#include <media/davinci/vpbe_types.h>
+#include <media/davinci/vpbe.h>
+#include <media/davinci/vpbe_venc.h>
+#include <media/davinci/vpbe_osd.h>
+#include "vpbe_venc_regs.h"
+
+#define VPBE_DISPLAY_DRIVER "vpbe-v4l2"
+
+static int debug;
+
+#define VPBE_DEFAULT_NUM_BUFS 3
+
+module_param(debug, int, 0644);
+
+static int venc_is_second_field(struct vpbe_display *disp_dev)
+{
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+ int ret;
+ int val;
+
+ ret = v4l2_subdev_call(vpbe_dev->venc,
+ core,
+ ioctl,
+ VENC_GET_FLD,
+ &val);
+ if (ret < 0) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Error in getting Field ID 0\n");
+ }
+ return val;
+}
+
+static void vpbe_isr_even_field(struct vpbe_display *disp_obj,
+ struct vpbe_layer *layer)
+{
+ struct timespec timevalue;
+
+ if (layer->cur_frm == layer->next_frm)
+ return;
+ ktime_get_ts(&timevalue);
+ layer->cur_frm->ts.tv_sec = timevalue.tv_sec;
+ layer->cur_frm->ts.tv_usec = timevalue.tv_nsec / NSEC_PER_USEC;
+ layer->cur_frm->state = VIDEOBUF_DONE;
+ wake_up_interruptible(&layer->cur_frm->done);
+ /* Make cur_frm pointing to next_frm */
+ layer->cur_frm = layer->next_frm;
+}
+
+static void vpbe_isr_odd_field(struct vpbe_display *disp_obj,
+ struct vpbe_layer *layer)
+{
+ struct osd_state *osd_device = disp_obj->osd_device;
+ unsigned long addr;
+
+ spin_lock(&disp_obj->dma_queue_lock);
+ if (list_empty(&layer->dma_queue) ||
+ (layer->cur_frm != layer->next_frm)) {
+ spin_unlock(&disp_obj->dma_queue_lock);
+ return;
+ }
+ /*
+ * one field is displayed configure
+ * the next frame if it is available
+ * otherwise hold on current frame
+ * Get next from the buffer queue
+ */
+ layer->next_frm = list_entry(
+ layer->dma_queue.next,
+ struct videobuf_buffer,
+ queue);
+ /* Remove that from the buffer queue */
+ list_del(&layer->next_frm->queue);
+ spin_unlock(&disp_obj->dma_queue_lock);
+ /* Mark state of the frame to active */
+ layer->next_frm->state = VIDEOBUF_ACTIVE;
+ addr = videobuf_to_dma_contig(layer->next_frm);
+ osd_device->ops.start_layer(osd_device,
+ layer->layer_info.id,
+ addr,
+ disp_obj->cbcr_ofst);
+}
+
+/* interrupt service routine */
+static irqreturn_t venc_isr(int irq, void *arg)
+{
+ struct vpbe_display *disp_dev = (struct vpbe_display *)arg;
+ struct vpbe_layer *layer;
+ static unsigned last_event;
+ unsigned event = 0;
+ int fid;
+ int i;
+
+ if ((NULL == arg) || (NULL == disp_dev->dev[0]))
+ return IRQ_HANDLED;
+
+ if (venc_is_second_field(disp_dev))
+ event |= VENC_SECOND_FIELD;
+ else
+ event |= VENC_FIRST_FIELD;
+
+ if (event == (last_event & ~VENC_END_OF_FRAME)) {
+ /*
+ * If the display is non-interlaced, then we need to flag the
+ * end-of-frame event at every interrupt regardless of the
+ * value of the FIDST bit. We can conclude that the display is
+ * non-interlaced if the value of the FIDST bit is unchanged
+ * from the previous interrupt.
+ */
+ event |= VENC_END_OF_FRAME;
+ } else if (event == VENC_SECOND_FIELD) {
+ /* end-of-frame for interlaced display */
+ event |= VENC_END_OF_FRAME;
+ }
+ last_event = event;
+
+ for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
+ layer = disp_dev->dev[i];
+ /* If streaming is started in this layer */
+ if (!layer->started)
+ continue;
+
+ if (layer->layer_first_int) {
+ layer->layer_first_int = 0;
+ continue;
+ }
+ /* Check the field format */
+ if ((V4L2_FIELD_NONE == layer->pix_fmt.field) &&
+ (event & VENC_END_OF_FRAME)) {
+ /* Progressive mode */
+
+ vpbe_isr_even_field(disp_dev, layer);
+ vpbe_isr_odd_field(disp_dev, layer);
+ } else {
+ /* Interlaced mode */
+
+ layer->field_id ^= 1;
+ if (event & VENC_FIRST_FIELD)
+ fid = 0;
+ else
+ fid = 1;
+
+ /*
+ * If field id does not match with store
+ * field id
+ */
+ if (fid != layer->field_id) {
+ /* Make them in sync */
+ layer->field_id = fid;
+ continue;
+ }
+ /*
+ * device field id and local field id are
+ * in sync. If this is even field
+ */
+ if (0 == fid)
+ vpbe_isr_even_field(disp_dev, layer);
+ else /* odd field */
+ vpbe_isr_odd_field(disp_dev, layer);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * vpbe_buffer_prepare()
+ * This is the callback function called from videobuf_qbuf() function
+ * the buffer is prepared and user space virtual address is converted into
+ * physical address
+ */
+static int vpbe_buffer_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct vpbe_fh *fh = q->priv_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ unsigned long addr;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "vpbe_buffer_prepare\n");
+
+ /* If buffer is not initialized, initialize it */
+ if (VIDEOBUF_NEEDS_INIT == vb->state) {
+ vb->width = layer->pix_fmt.width;
+ vb->height = layer->pix_fmt.height;
+ vb->size = layer->pix_fmt.sizeimage;
+ vb->field = field;
+
+ ret = videobuf_iolock(q, vb, NULL);
+ if (ret < 0) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Failed to map \
+ user address\n");
+ return -EINVAL;
+ }
+
+ addr = videobuf_to_dma_contig(vb);
+
+ if (q->streaming) {
+ if (!IS_ALIGNED(addr, 8)) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "buffer_prepare:offset is \
+ not aligned to 32 bytes\n");
+ return -EINVAL;
+ }
+ }
+ vb->state = VIDEOBUF_PREPARED;
+ }
+ return 0;
+}
+
+/*
+ * vpbe_buffer_setup()
+ * This function allocates memory for the buffers
+ */
+static int vpbe_buffer_setup(struct videobuf_queue *q,
+ unsigned int *count,
+ unsigned int *size)
+{
+ /* Get the file handle object and layer object */
+ struct vpbe_fh *fh = q->priv_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_buffer_setup\n");
+
+ *size = layer->pix_fmt.sizeimage;
+
+ /* Store number of buffers allocated in numbuffer member */
+ if (*count < VPBE_DEFAULT_NUM_BUFS)
+ *count = layer->numbuffers = VPBE_DEFAULT_NUM_BUFS;
+
+ return 0;
+}
+
+/*
+ * vpbe_buffer_queue()
+ * This function adds the buffer to DMA queue
+ */
+static void vpbe_buffer_queue(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ /* Get the file handle object and layer object */
+ struct vpbe_fh *fh = q->priv_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_display *disp = fh->disp_dev;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ unsigned long flags;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "vpbe_buffer_queue\n");
+
+ /* add the buffer to the DMA queue */
+ spin_lock_irqsave(&disp->dma_queue_lock, flags);
+ list_add_tail(&vb->queue, &layer->dma_queue);
+ spin_unlock_irqrestore(&disp->dma_queue_lock, flags);
+ /* Change state of the buffer */
+ vb->state = VIDEOBUF_QUEUED;
+}
+
+/*
+ * vpbe_buffer_release()
+ * This function is called from the videobuf layer to free memory allocated to
+ * the buffers
+ */
+static void vpbe_buffer_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ /* Get the file handle object and layer object */
+ struct vpbe_fh *fh = q->priv_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "vpbe_buffer_release\n");
+
+ if (V4L2_MEMORY_USERPTR != layer->memory)
+ videobuf_dma_contig_free(q, vb);
+
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static struct videobuf_queue_ops video_qops = {
+ .buf_setup = vpbe_buffer_setup,
+ .buf_prepare = vpbe_buffer_prepare,
+ .buf_queue = vpbe_buffer_queue,
+ .buf_release = vpbe_buffer_release,
+};
+
+static
+struct vpbe_layer*
+_vpbe_display_get_other_win_layer(struct vpbe_display *disp_dev,
+ struct vpbe_layer *layer)
+{
+ enum vpbe_display_device_id thiswin, otherwin;
+ thiswin = layer->device_id;
+
+ otherwin = (thiswin == VPBE_DISPLAY_DEVICE_0) ?
+ VPBE_DISPLAY_DEVICE_1 : VPBE_DISPLAY_DEVICE_0;
+ return disp_dev->dev[otherwin];
+}
+
+static int vpbe_set_osd_display_params(struct vpbe_display *disp_dev,
+ struct vpbe_layer *layer)
+{
+ struct osd_layer_config *cfg = &layer->layer_info.config;
+ struct osd_state *osd_device = disp_dev->osd_device;
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+ unsigned long addr;
+ int ret;
+
+ addr = videobuf_to_dma_contig(layer->cur_frm);
+ /* Set address in the display registers */
+ osd_device->ops.start_layer(osd_device,
+ layer->layer_info.id,
+ addr,
+ disp_dev->cbcr_ofst);
+
+ ret = osd_device->ops.enable_layer(osd_device,
+ layer->layer_info.id, 0);
+ if (ret < 0) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Error in enabling osd window layer 0\n");
+ return -1;
+ }
+
+ /* Enable the window */
+ layer->layer_info.enable = 1;
+ if (cfg->pixfmt == PIXFMT_NV12) {
+ struct vpbe_layer *otherlayer =
+ _vpbe_display_get_other_win_layer(disp_dev, layer);
+
+ ret = osd_device->ops.enable_layer(osd_device,
+ otherlayer->layer_info.id, 1);
+ if (ret < 0) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Error in enabling osd window layer 1\n");
+ return -1;
+ }
+ otherlayer->layer_info.enable = 1;
+ }
+ return 0;
+}
+
+static void
+vpbe_disp_calculate_scale_factor(struct vpbe_display *disp_dev,
+ struct vpbe_layer *layer,
+ int expected_xsize, int expected_ysize)
+{
+ struct display_layer_info *layer_info = &layer->layer_info;
+ struct v4l2_pix_format *pixfmt = &layer->pix_fmt;
+ struct osd_layer_config *cfg = &layer->layer_info.config;
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+ int calculated_xsize;
+ int h_exp = 0;
+ int v_exp = 0;
+ int h_scale;
+ int v_scale;
+
+ v4l2_std_id standard_id = vpbe_dev->current_timings.timings.std_id;
+
+ /*
+ * Application initially set the image format. Current display
+ * size is obtained from the vpbe display controller. expected_xsize
+ * and expected_ysize are set through S_CROP ioctl. Based on this,
+ * driver will calculate the scale factors for vertical and
+ * horizontal direction so that the image is displayed scaled
+ * and expanded. Application uses expansion to display the image
+ * in a square pixel. Otherwise it is displayed using displays
+ * pixel aspect ratio.It is expected that application chooses
+ * the crop coordinates for cropped or scaled display. if crop
+ * size is less than the image size, it is displayed cropped or
+ * it is displayed scaled and/or expanded.
+ *
+ * to begin with, set the crop window same as expected. Later we
+ * will override with scaled window size
+ */
+
+ cfg->xsize = pixfmt->width;
+ cfg->ysize = pixfmt->height;
+ layer_info->h_zoom = ZOOM_X1; /* no horizontal zoom */
+ layer_info->v_zoom = ZOOM_X1; /* no horizontal zoom */
+ layer_info->h_exp = H_EXP_OFF; /* no horizontal zoom */
+ layer_info->v_exp = V_EXP_OFF; /* no horizontal zoom */
+
+ if (pixfmt->width < expected_xsize) {
+ h_scale = vpbe_dev->current_timings.xres / pixfmt->width;
+ if (h_scale < 2)
+ h_scale = 1;
+ else if (h_scale >= 4)
+ h_scale = 4;
+ else
+ h_scale = 2;
+ cfg->xsize *= h_scale;
+ if (cfg->xsize < expected_xsize) {
+ if ((standard_id & V4L2_STD_525_60) ||
+ (standard_id & V4L2_STD_625_50)) {
+ calculated_xsize = (cfg->xsize *
+ VPBE_DISPLAY_H_EXP_RATIO_N) /
+ VPBE_DISPLAY_H_EXP_RATIO_D;
+ if (calculated_xsize <= expected_xsize) {
+ h_exp = 1;
+ cfg->xsize = calculated_xsize;
+ }
+ }
+ }
+ if (h_scale == 2)
+ layer_info->h_zoom = ZOOM_X2;
+ else if (h_scale == 4)
+ layer_info->h_zoom = ZOOM_X4;
+ if (h_exp)
+ layer_info->h_exp = H_EXP_9_OVER_8;
+ } else {
+ /* no scaling, only cropping. Set display area to crop area */
+ cfg->xsize = expected_xsize;
+ }
+
+ if (pixfmt->height < expected_ysize) {
+ v_scale = expected_ysize / pixfmt->height;
+ if (v_scale < 2)
+ v_scale = 1;
+ else if (v_scale >= 4)
+ v_scale = 4;
+ else
+ v_scale = 2;
+ cfg->ysize *= v_scale;
+ if (cfg->ysize < expected_ysize) {
+ if ((standard_id & V4L2_STD_625_50)) {
+ calculated_xsize = (cfg->ysize *
+ VPBE_DISPLAY_V_EXP_RATIO_N) /
+ VPBE_DISPLAY_V_EXP_RATIO_D;
+ if (calculated_xsize <= expected_ysize) {
+ v_exp = 1;
+ cfg->ysize = calculated_xsize;
+ }
+ }
+ }
+ if (v_scale == 2)
+ layer_info->v_zoom = ZOOM_X2;
+ else if (v_scale == 4)
+ layer_info->v_zoom = ZOOM_X4;
+ if (v_exp)
+ layer_info->h_exp = V_EXP_6_OVER_5;
+ } else {
+ /* no scaling, only cropping. Set display area to crop area */
+ cfg->ysize = expected_ysize;
+ }
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "crop display xsize = %d, ysize = %d\n",
+ cfg->xsize, cfg->ysize);
+}
+
+static void vpbe_disp_adj_position(struct vpbe_display *disp_dev,
+ struct vpbe_layer *layer,
+ int top, int left)
+{
+ struct osd_layer_config *cfg = &layer->layer_info.config;
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+
+ cfg->xpos = min((unsigned int)left,
+ vpbe_dev->current_timings.xres - cfg->xsize);
+ cfg->ypos = min((unsigned int)top,
+ vpbe_dev->current_timings.yres - cfg->ysize);
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "new xpos = %d, ypos = %d\n",
+ cfg->xpos, cfg->ypos);
+}
+
+static void vpbe_disp_check_window_params(struct vpbe_display *disp_dev,
+ struct v4l2_rect *c)
+{
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+
+ if ((c->width == 0) ||
+ ((c->width + c->left) > vpbe_dev->current_timings.xres))
+ c->width = vpbe_dev->current_timings.xres - c->left;
+
+ if ((c->height == 0) || ((c->height + c->top) >
+ vpbe_dev->current_timings.yres))
+ c->height = vpbe_dev->current_timings.yres - c->top;
+
+ /* window height must be even for interlaced display */
+ if (vpbe_dev->current_timings.interlaced)
+ c->height &= (~0x01);
+
+}
+
+/**
+ * vpbe_try_format()
+ * If user application provides width and height, and have bytesperline set
+ * to zero, driver calculates bytesperline and sizeimage based on hardware
+ * limits.
+ */
+static int vpbe_try_format(struct vpbe_display *disp_dev,
+ struct v4l2_pix_format *pixfmt, int check)
+{
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+ int min_height = 1;
+ int min_width = 32;
+ int max_height;
+ int max_width;
+ int bpp;
+
+ if ((pixfmt->pixelformat != V4L2_PIX_FMT_UYVY) &&
+ (pixfmt->pixelformat != V4L2_PIX_FMT_NV12))
+ /* choose default as V4L2_PIX_FMT_UYVY */
+ pixfmt->pixelformat = V4L2_PIX_FMT_UYVY;
+
+ /* Check the field format */
+ if ((pixfmt->field != V4L2_FIELD_INTERLACED) &&
+ (pixfmt->field != V4L2_FIELD_NONE)) {
+ if (vpbe_dev->current_timings.interlaced)
+ pixfmt->field = V4L2_FIELD_INTERLACED;
+ else
+ pixfmt->field = V4L2_FIELD_NONE;
+ }
+
+ if (pixfmt->field == V4L2_FIELD_INTERLACED)
+ min_height = 2;
+
+ if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12)
+ bpp = 1;
+ else
+ bpp = 2;
+
+ max_width = vpbe_dev->current_timings.xres;
+ max_height = vpbe_dev->current_timings.yres;
+
+ min_width /= bpp;
+
+ if (!pixfmt->width || (pixfmt->width < min_width) ||
+ (pixfmt->width > max_width)) {
+ pixfmt->width = vpbe_dev->current_timings.xres;
+ }
+
+ if (!pixfmt->height || (pixfmt->height < min_height) ||
+ (pixfmt->height > max_height)) {
+ pixfmt->height = vpbe_dev->current_timings.yres;
+ }
+
+ if (pixfmt->bytesperline < (pixfmt->width * bpp))
+ pixfmt->bytesperline = pixfmt->width * bpp;
+
+ /* Make the bytesperline 32 byte aligned */
+ pixfmt->bytesperline = ((pixfmt->width * bpp + 31) & ~31);
+
+ if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12)
+ pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height +
+ (pixfmt->bytesperline * pixfmt->height >> 1);
+ else
+ pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
+
+ return 0;
+}
+
+static int vpbe_display_g_priority(struct file *file, void *priv,
+ enum v4l2_priority *p)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+
+ *p = v4l2_prio_max(&layer->prio);
+
+ return 0;
+}
+
+static int vpbe_display_s_priority(struct file *file, void *priv,
+ enum v4l2_priority p)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ int ret;
+
+ ret = v4l2_prio_change(&layer->prio, &fh->prio, p);
+
+ return ret;
+}
+
+static int vpbe_display_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ cap->version = VPBE_DISPLAY_VERSION_CODE;
+ cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+ strlcpy(cap->driver, VPBE_DISPLAY_DRIVER, sizeof(cap->driver));
+ strlcpy(cap->bus_info, "platform", sizeof(cap->bus_info));
+ strlcpy(cap->card, vpbe_dev->cfg->module_name, sizeof(cap->card));
+
+ return 0;
+}
+
+static int vpbe_display_s_crop(struct file *file, void *priv,
+ struct v4l2_crop *crop)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_display *disp_dev = fh->disp_dev;
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+ struct osd_layer_config *cfg = &layer->layer_info.config;
+ struct osd_state *osd_device = disp_dev->osd_device;
+ struct v4l2_rect *rect = &crop->c;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "VIDIOC_S_CROP, layer id = %d\n", layer->device_id);
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n");
+ return -EINVAL;
+ }
+
+ if (rect->top < 0)
+ rect->top = 0;
+ if (rect->left < 0)
+ rect->left = 0;
+
+ vpbe_disp_check_window_params(disp_dev, rect);
+
+ osd_device->ops.get_layer_config(osd_device,
+ layer->layer_info.id, cfg);
+
+ vpbe_disp_calculate_scale_factor(disp_dev, layer,
+ rect->width,
+ rect->height);
+ vpbe_disp_adj_position(disp_dev, layer, rect->top,
+ rect->left);
+ ret = osd_device->ops.set_layer_config(osd_device,
+ layer->layer_info.id, cfg);
+ if (ret < 0) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Error in set layer config:\n");
+ return -EINVAL;
+ }
+
+ /* apply zooming and h or v expansion */
+ osd_device->ops.set_zoom(osd_device,
+ layer->layer_info.id,
+ layer->layer_info.h_zoom,
+ layer->layer_info.v_zoom);
+ ret = osd_device->ops.set_vid_expansion(osd_device,
+ layer->layer_info.h_exp,
+ layer->layer_info.v_exp);
+ if (ret < 0) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Error in set vid expansion:\n");
+ return -EINVAL;
+ }
+
+ if ((layer->layer_info.h_zoom != ZOOM_X1) ||
+ (layer->layer_info.v_zoom != ZOOM_X1) ||
+ (layer->layer_info.h_exp != H_EXP_OFF) ||
+ (layer->layer_info.v_exp != V_EXP_OFF))
+ /* Enable expansion filter */
+ osd_device->ops.set_interpolation_filter(osd_device, 1);
+ else
+ osd_device->ops.set_interpolation_filter(osd_device, 0);
+
+ return 0;
+}
+
+static int vpbe_display_g_crop(struct file *file, void *priv,
+ struct v4l2_crop *crop)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct osd_layer_config *cfg = &layer->layer_info.config;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct osd_state *osd_device = fh->disp_dev->osd_device;
+ struct v4l2_rect *rect = &crop->c;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "VIDIOC_G_CROP, layer id = %d\n",
+ layer->device_id);
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n");
+ ret = -EINVAL;
+ }
+ osd_device->ops.get_layer_config(osd_device,
+ layer->layer_info.id, cfg);
+ rect->top = cfg->ypos;
+ rect->left = cfg->xpos;
+ rect->width = cfg->xsize;
+ rect->height = cfg->ysize;
+
+ return 0;
+}
+
+static int vpbe_display_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cropcap)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_CROPCAP ioctl\n");
+
+ cropcap->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ cropcap->bounds.left = 0;
+ cropcap->bounds.top = 0;
+ cropcap->bounds.width = vpbe_dev->current_timings.xres;
+ cropcap->bounds.height = vpbe_dev->current_timings.yres;
+ cropcap->pixelaspect = vpbe_dev->current_timings.aspect;
+ cropcap->defrect = cropcap->bounds;
+ return 0;
+}
+
+static int vpbe_display_g_fmt(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "VIDIOC_G_FMT, layer id = %d\n",
+ layer->device_id);
+
+ /* If buffer type is video output */
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n");
+ return -EINVAL;
+ }
+ /* Fill in the information about format */
+ fmt->fmt.pix = layer->pix_fmt;
+
+ return 0;
+}
+
+static int vpbe_display_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *fmt)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ unsigned int index = 0;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "VIDIOC_ENUM_FMT, layer id = %d\n",
+ layer->device_id);
+ if (fmt->index > 1) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid format index\n");
+ return -EINVAL;
+ }
+
+ /* Fill in the information about format */
+ index = fmt->index;
+ memset(fmt, 0, sizeof(*fmt));
+ fmt->index = index;
+ fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ if (index == 0) {
+ strcpy(fmt->description, "YUV 4:2:2 - UYVY");
+ fmt->pixelformat = V4L2_PIX_FMT_UYVY;
+ } else {
+ strcpy(fmt->description, "Y/CbCr 4:2:0");
+ fmt->pixelformat = V4L2_PIX_FMT_NV12;
+ }
+
+ return 0;
+}
+
+static int vpbe_display_s_fmt(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_display *disp_dev = fh->disp_dev;
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+ struct osd_layer_config *cfg = &layer->layer_info.config;
+ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+ struct osd_state *osd_device = disp_dev->osd_device;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "VIDIOC_S_FMT, layer id = %d\n",
+ layer->device_id);
+
+ /* If streaming is started, return error */
+ if (layer->started) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n");
+ return -EBUSY;
+ }
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) {
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "invalid type\n");
+ return -EINVAL;
+ }
+ /* Check for valid pixel format */
+ ret = vpbe_try_format(disp_dev, pixfmt, 1);
+ if (ret)
+ return ret;
+
+ /* YUV420 is requested, check availability of the
+ other video window */
+
+ layer->pix_fmt = *pixfmt;
+
+ /* Get osd layer config */
+ osd_device->ops.get_layer_config(osd_device,
+ layer->layer_info.id, cfg);
+ /* Store the pixel format in the layer object */
+ cfg->xsize = pixfmt->width;
+ cfg->ysize = pixfmt->height;
+ cfg->line_length = pixfmt->bytesperline;
+ cfg->ypos = 0;
+ cfg->xpos = 0;
+ cfg->interlaced = vpbe_dev->current_timings.interlaced;
+
+ if (V4L2_PIX_FMT_UYVY == pixfmt->pixelformat)
+ cfg->pixfmt = PIXFMT_YCbCrI;
+
+ /* Change of the default pixel format for both video windows */
+ if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) {
+ struct vpbe_layer *otherlayer;
+ cfg->pixfmt = PIXFMT_NV12;
+ otherlayer = _vpbe_display_get_other_win_layer(disp_dev,
+ layer);
+ otherlayer->layer_info.config.pixfmt = PIXFMT_NV12;
+ }
+
+ /* Set the layer config in the osd window */
+ ret = osd_device->ops.set_layer_config(osd_device,
+ layer->layer_info.id, cfg);
+ if (ret < 0) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Error in S_FMT params:\n");
+ return -EINVAL;
+ }
+
+ /* Readback and fill the local copy of current pix format */
+ osd_device->ops.get_layer_config(osd_device,
+ layer->layer_info.id, cfg);
+
+ return 0;
+}
+
+static int vpbe_display_try_fmt(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_display *disp_dev = fh->disp_dev;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_TRY_FMT\n");
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n");
+ return -EINVAL;
+ }
+
+ /* Check for valid field format */
+ return vpbe_try_format(disp_dev, pixfmt, 0);
+
+}
+
+/**
+ * vpbe_display_s_std - Set the given standard in the encoder
+ *
+ * Sets the standard if supported by the current encoder. Return the status.
+ * 0 - success & -EINVAL on error
+ */
+static int vpbe_display_s_std(struct file *file, void *priv,
+ v4l2_std_id *std_id)
+{
+ struct vpbe_fh *fh = priv;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_STD\n");
+
+ /* If streaming is started, return error */
+ if (layer->started) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n");
+ return -EBUSY;
+ }
+ if (NULL != vpbe_dev->ops.s_std) {
+ ret = vpbe_dev->ops.s_std(vpbe_dev, std_id);
+ if (ret) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Failed to set standard for sub devices\n");
+ return -EINVAL;
+ }
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * vpbe_display_g_std - Get the standard in the current encoder
+ *
+ * Get the standard in the current encoder. Return the status. 0 - success
+ * -EINVAL on error
+ */
+static int vpbe_display_g_std(struct file *file, void *priv,
+ v4l2_std_id *std_id)
+{
+ struct vpbe_fh *fh = priv;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_STD\n");
+
+ /* Get the standard from the current encoder */
+ if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) {
+ *std_id = vpbe_dev->current_timings.timings.std_id;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * vpbe_display_enum_output - enumerate outputs
+ *
+ * Enumerates the outputs available at the vpbe display
+ * returns the status, -EINVAL if end of output list
+ */
+static int vpbe_display_enum_output(struct file *file, void *priv,
+ struct v4l2_output *output)
+{
+ struct vpbe_fh *fh = priv;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_OUTPUT\n");
+
+ /* Enumerate outputs */
+
+ if (NULL == vpbe_dev->ops.enum_outputs)
+ return -EINVAL;
+
+ ret = vpbe_dev->ops.enum_outputs(vpbe_dev, output);
+ if (ret) {
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "Failed to enumerate outputs\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * vpbe_display_s_output - Set output to
+ * the output specified by the index
+ */
+static int vpbe_display_s_output(struct file *file, void *priv,
+ unsigned int i)
+{
+ struct vpbe_fh *fh = priv;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_OUTPUT\n");
+ /* If streaming is started, return error */
+ if (layer->started) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n");
+ return -EBUSY;
+ }
+ if (NULL == vpbe_dev->ops.set_output)
+ return -EINVAL;
+
+ ret = vpbe_dev->ops.set_output(vpbe_dev, i);
+ if (ret) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Failed to set output for sub devices\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * vpbe_display_g_output - Get output from subdevice
+ * for a given by the index
+ */
+static int vpbe_display_g_output(struct file *file, void *priv,
+ unsigned int *i)
+{
+ struct vpbe_fh *fh = priv;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_OUTPUT\n");
+ /* Get the standard from the current encoder */
+ *i = vpbe_dev->current_out_index;
+
+ return 0;
+}
+
+/**
+ * vpbe_display_enum_dv_presets - Enumerate the dv presets
+ *
+ * enum the preset in the current encoder. Return the status. 0 - success
+ * -EINVAL on error
+ */
+static int
+vpbe_display_enum_dv_presets(struct file *file, void *priv,
+ struct v4l2_dv_enum_preset *preset)
+{
+ struct vpbe_fh *fh = priv;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_DV_PRESETS\n");
+
+ /* Enumerate outputs */
+ if (NULL == vpbe_dev->ops.enum_dv_presets)
+ return -EINVAL;
+
+ ret = vpbe_dev->ops.enum_dv_presets(vpbe_dev, preset);
+ if (ret) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Failed to enumerate dv presets info\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * vpbe_display_s_dv_preset - Set the dv presets
+ *
+ * Set the preset in the current encoder. Return the status. 0 - success
+ * -EINVAL on error
+ */
+static int
+vpbe_display_s_dv_preset(struct file *file, void *priv,
+ struct v4l2_dv_preset *preset)
+{
+ struct vpbe_fh *fh = priv;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_DV_PRESETS\n");
+
+
+ /* If streaming is started, return error */
+ if (layer->started) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n");
+ return -EBUSY;
+ }
+
+ /* Set the given standard in the encoder */
+ if (NULL != vpbe_dev->ops.s_dv_preset)
+ return -EINVAL;
+
+ ret = vpbe_dev->ops.s_dv_preset(vpbe_dev, preset);
+ if (ret) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Failed to set the dv presets info\n");
+ return -EINVAL;
+ }
+ /* set the current norm to zero to be consistent. If STD is used
+ * v4l2 layer will set the norm properly on successful s_std call
+ */
+ layer->video_dev.current_norm = 0;
+
+ return 0;
+}
+
+/**
+ * vpbe_display_g_dv_preset - Set the dv presets
+ *
+ * Get the preset in the current encoder. Return the status. 0 - success
+ * -EINVAL on error
+ */
+static int
+vpbe_display_g_dv_preset(struct file *file, void *priv,
+ struct v4l2_dv_preset *dv_preset)
+{
+ struct vpbe_fh *fh = priv;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_DV_PRESETS\n");
+
+ /* Get the given standard in the encoder */
+
+ if (vpbe_dev->current_timings.timings_type &
+ VPBE_ENC_DV_PRESET) {
+ dv_preset->preset =
+ vpbe_dev->current_timings.timings.dv_preset;
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vpbe_display_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type buf_type)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct osd_state *osd_device = fh->disp_dev->osd_device;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "VIDIOC_STREAMOFF,layer id = %d\n",
+ layer->device_id);
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
+ return -EINVAL;
+ }
+
+ /* If io is allowed for this file handle, return error */
+ if (!fh->io_allowed) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n");
+ return -EACCES;
+ }
+
+ /* If streaming is not started, return error */
+ if (!layer->started) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "streaming not started in layer"
+ " id = %d\n", layer->device_id);
+ return -EINVAL;
+ }
+
+ osd_device->ops.disable_layer(osd_device,
+ layer->layer_info.id);
+ layer->started = 0;
+ ret = videobuf_streamoff(&layer->buffer_queue);
+
+ return ret;
+}
+
+static int vpbe_display_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type buf_type)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_display *disp_dev = fh->disp_dev;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct osd_state *osd_device = disp_dev->osd_device;
+ int ret;
+
+ osd_device->ops.disable_layer(osd_device,
+ layer->layer_info.id);
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_STREAMON, layerid=%d\n",
+ layer->device_id);
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
+ return -EINVAL;
+ }
+
+ /* If file handle is not allowed IO, return error */
+ if (!fh->io_allowed) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n");
+ return -EACCES;
+ }
+ /* If Streaming is already started, return error */
+ if (layer->started) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "layer is already streaming\n");
+ return -EBUSY;
+ }
+
+ /*
+ * Call videobuf_streamon to start streaming
+ * in videobuf
+ */
+ ret = videobuf_streamon(&layer->buffer_queue);
+ if (ret) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "error in videobuf_streamon\n");
+ return ret;
+ }
+ /* If buffer queue is empty, return error */
+ if (list_empty(&layer->dma_queue)) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "buffer queue is empty\n");
+ goto streamoff;
+ }
+ /* Get the next frame from the buffer queue */
+ layer->next_frm = layer->cur_frm = list_entry(layer->dma_queue.next,
+ struct videobuf_buffer, queue);
+ /* Remove buffer from the buffer queue */
+ list_del(&layer->cur_frm->queue);
+ /* Mark state of the current frame to active */
+ layer->cur_frm->state = VIDEOBUF_ACTIVE;
+ /* Initialize field_id and started member */
+ layer->field_id = 0;
+
+ /* Set parameters in OSD and VENC */
+ ret = vpbe_set_osd_display_params(disp_dev, layer);
+ if (ret < 0)
+ goto streamoff;
+
+ /*
+ * if request format is yuv420 semiplanar, need to
+ * enable both video windows
+ */
+ layer->started = 1;
+
+ layer->layer_first_int = 1;
+
+ return ret;
+streamoff:
+ ret = videobuf_streamoff(&layer->buffer_queue);
+ return ret;
+}
+
+static int vpbe_display_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "VIDIOC_DQBUF, layer id = %d\n",
+ layer->device_id);
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
+ return -EINVAL;
+ }
+ /* If this file handle is not allowed to do IO, return error */
+ if (!fh->io_allowed) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n");
+ return -EACCES;
+ }
+ if (file->f_flags & O_NONBLOCK)
+ /* Call videobuf_dqbuf for non blocking mode */
+ ret = videobuf_dqbuf(&layer->buffer_queue, buf, 1);
+ else
+ /* Call videobuf_dqbuf for blocking mode */
+ ret = videobuf_dqbuf(&layer->buffer_queue, buf, 0);
+
+ return ret;
+}
+
+static int vpbe_display_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "VIDIOC_QBUF, layer id = %d\n",
+ layer->device_id);
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != p->type) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
+ return -EINVAL;
+ }
+
+ /* If this file handle is not allowed to do IO, return error */
+ if (!fh->io_allowed) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n");
+ return -EACCES;
+ }
+
+ return videobuf_qbuf(&layer->buffer_queue, p);
+}
+
+static int vpbe_display_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "VIDIOC_QUERYBUF, layer id = %d\n",
+ layer->device_id);
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
+ return -EINVAL;
+ }
+
+ /* Call videobuf_querybuf to get information */
+ ret = videobuf_querybuf(&layer->buffer_queue, buf);
+
+ return ret;
+}
+
+static int vpbe_display_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *req_buf)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_reqbufs\n");
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != req_buf->type) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
+ return -EINVAL;
+ }
+
+ /* If io users of the layer is not zero, return error */
+ if (0 != layer->io_usrs) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "not IO user\n");
+ return -EBUSY;
+ }
+ /* Initialize videobuf queue as per the buffer type */
+ videobuf_queue_dma_contig_init(&layer->buffer_queue,
+ &video_qops,
+ vpbe_dev->pdev,
+ &layer->irqlock,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT,
+ layer->pix_fmt.field,
+ sizeof(struct videobuf_buffer),
+ fh, NULL);
+
+ /* Set io allowed member of file handle to TRUE */
+ fh->io_allowed = 1;
+ /* Increment io usrs member of layer object to 1 */
+ layer->io_usrs = 1;
+ /* Store type of memory requested in layer object */
+ layer->memory = req_buf->memory;
+ /* Initialize buffer queue */
+ INIT_LIST_HEAD(&layer->dma_queue);
+ /* Allocate buffers */
+ ret = videobuf_reqbufs(&layer->buffer_queue, req_buf);
+
+ return ret;
+}
+
+/*
+ * vpbe_display_mmap()
+ * It is used to map kernel space buffers into user spaces
+ */
+static int vpbe_display_mmap(struct file *filep, struct vm_area_struct *vma)
+{
+ /* Get the layer object and file handle object */
+ struct vpbe_fh *fh = filep->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_mmap\n");
+
+ return videobuf_mmap_mapper(&layer->buffer_queue, vma);
+}
+
+/* vpbe_display_poll(): It is used for select/poll system call
+ */
+static unsigned int vpbe_display_poll(struct file *filep, poll_table *wait)
+{
+ struct vpbe_fh *fh = filep->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ unsigned int err = 0;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_poll\n");
+ if (layer->started)
+ err = videobuf_poll_stream(filep, &layer->buffer_queue, wait);
+ return err;
+}
+
+/*
+ * vpbe_display_open()
+ * It creates object of file handle structure and stores it in private_data
+ * member of filepointer
+ */
+static int vpbe_display_open(struct file *file)
+{
+ struct vpbe_fh *fh = NULL;
+ struct vpbe_layer *layer = video_drvdata(file);
+ struct vpbe_display *disp_dev = layer->disp_dev;
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+ struct osd_state *osd_device = disp_dev->osd_device;
+ int err;
+
+ /* Allocate memory for the file handle object */
+ fh = kmalloc(sizeof(struct vpbe_fh), GFP_KERNEL);
+ if (fh == NULL) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "unable to allocate memory for file handle object\n");
+ return -ENOMEM;
+ }
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "vpbe display open plane = %d\n",
+ layer->device_id);
+
+ /* store pointer to fh in private_data member of filep */
+ file->private_data = fh;
+ fh->layer = layer;
+ fh->disp_dev = disp_dev;
+
+ if (!layer->usrs) {
+
+ /* First claim the layer for this device */
+ err = osd_device->ops.request_layer(osd_device,
+ layer->layer_info.id);
+ if (err < 0) {
+ /* Couldn't get layer */
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Display Manager failed to allocate layer\n");
+ kfree(fh);
+ return -EINVAL;
+ }
+ }
+ /* Increment layer usrs counter */
+ layer->usrs++;
+ /* Set io_allowed member to false */
+ fh->io_allowed = 0;
+ /* Initialize priority of this instance to default priority */
+ fh->prio = V4L2_PRIORITY_UNSET;
+ v4l2_prio_open(&layer->prio, &fh->prio);
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "vpbe display device opened successfully\n");
+ return 0;
+}
+
+/*
+ * vpbe_display_release()
+ * This function deletes buffer queue, frees the buffers and the davinci
+ * display file * handle
+ */
+static int vpbe_display_release(struct file *file)
+{
+ /* Get the layer object and file handle object */
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct osd_layer_config *cfg = &layer->layer_info.config;
+ struct vpbe_display *disp_dev = fh->disp_dev;
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+ struct osd_state *osd_device = disp_dev->osd_device;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_release\n");
+
+ /* if this instance is doing IO */
+ if (fh->io_allowed) {
+ /* Reset io_usrs member of layer object */
+ layer->io_usrs = 0;
+
+ osd_device->ops.disable_layer(osd_device,
+ layer->layer_info.id);
+ layer->started = 0;
+ /* Free buffers allocated */
+ videobuf_queue_cancel(&layer->buffer_queue);
+ videobuf_mmap_free(&layer->buffer_queue);
+ }
+
+ /* Decrement layer usrs counter */
+ layer->usrs--;
+ /* If this file handle has initialize encoder device, reset it */
+ if (!layer->usrs) {
+ if (cfg->pixfmt == PIXFMT_NV12) {
+ struct vpbe_layer *otherlayer;
+ otherlayer =
+ _vpbe_display_get_other_win_layer(disp_dev, layer);
+ osd_device->ops.disable_layer(osd_device,
+ otherlayer->layer_info.id);
+ osd_device->ops.release_layer(osd_device,
+ otherlayer->layer_info.id);
+ }
+ osd_device->ops.disable_layer(osd_device,
+ layer->layer_info.id);
+ osd_device->ops.release_layer(osd_device,
+ layer->layer_info.id);
+ }
+ /* Close the priority */
+ v4l2_prio_close(&layer->prio, fh->prio);
+ file->private_data = NULL;
+
+ /* Free memory allocated to file handle object */
+ kfree(fh);
+
+ disp_dev->cbcr_ofst = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vpbe_display_g_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct v4l2_dbg_match *match = &reg->match;
+
+ if (match->type >= 2) {
+ v4l2_subdev_call(vpbe_dev->venc,
+ core,
+ g_register,
+ reg);
+ }
+
+ return 0;
+}
+
+static int vpbe_display_s_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ return 0;
+}
+#endif
+
+/* vpbe capture ioctl operations */
+static const struct v4l2_ioctl_ops vpbe_ioctl_ops = {
+ .vidioc_querycap = vpbe_display_querycap,
+ .vidioc_g_fmt_vid_out = vpbe_display_g_fmt,
+ .vidioc_enum_fmt_vid_out = vpbe_display_enum_fmt,
+ .vidioc_s_fmt_vid_out = vpbe_display_s_fmt,
+ .vidioc_try_fmt_vid_out = vpbe_display_try_fmt,
+ .vidioc_reqbufs = vpbe_display_reqbufs,
+ .vidioc_querybuf = vpbe_display_querybuf,
+ .vidioc_qbuf = vpbe_display_qbuf,
+ .vidioc_dqbuf = vpbe_display_dqbuf,
+ .vidioc_streamon = vpbe_display_streamon,
+ .vidioc_streamoff = vpbe_display_streamoff,
+ .vidioc_cropcap = vpbe_display_cropcap,
+ .vidioc_g_crop = vpbe_display_g_crop,
+ .vidioc_s_crop = vpbe_display_s_crop,
+ .vidioc_g_priority = vpbe_display_g_priority,
+ .vidioc_s_priority = vpbe_display_s_priority,
+ .vidioc_s_std = vpbe_display_s_std,
+ .vidioc_g_std = vpbe_display_g_std,
+ .vidioc_enum_output = vpbe_display_enum_output,
+ .vidioc_s_output = vpbe_display_s_output,
+ .vidioc_g_output = vpbe_display_g_output,
+ .vidioc_s_dv_preset = vpbe_display_s_dv_preset,
+ .vidioc_g_dv_preset = vpbe_display_g_dv_preset,
+ .vidioc_enum_dv_presets = vpbe_display_enum_dv_presets,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vpbe_display_g_register,
+ .vidioc_s_register = vpbe_display_s_register,
+#endif
+};
+
+static struct v4l2_file_operations vpbe_fops = {
+ .owner = THIS_MODULE,
+ .open = vpbe_display_open,
+ .release = vpbe_display_release,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vpbe_display_mmap,
+ .poll = vpbe_display_poll
+};
+
+static int vpbe_device_get(struct device *dev, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct vpbe_display *vpbe_disp = data;
+
+ if (strcmp("vpbe_controller", pdev->name) == 0)
+ vpbe_disp->vpbe_dev = platform_get_drvdata(pdev);
+
+ if (strcmp("vpbe-osd", pdev->name) == 0)
+ vpbe_disp->osd_device = platform_get_drvdata(pdev);
+
+ return 0;
+}
+
+static __devinit int init_vpbe_layer(int i, struct vpbe_display *disp_dev,
+ struct platform_device *pdev)
+{
+ struct vpbe_layer *vpbe_display_layer = NULL;
+ struct video_device *vbd = NULL;
+
+ /* Allocate memory for four plane display objects */
+
+ disp_dev->dev[i] =
+ kzalloc(sizeof(struct vpbe_layer), GFP_KERNEL);
+
+ /* If memory allocation fails, return error */
+ if (!disp_dev->dev[i]) {
+ printk(KERN_ERR "ran out of memory\n");
+ return -ENOMEM;
+ }
+ spin_lock_init(&disp_dev->dev[i]->irqlock);
+ mutex_init(&disp_dev->dev[i]->opslock);
+
+ /* Get the pointer to the layer object */
+ vpbe_display_layer = disp_dev->dev[i];
+ vbd = &vpbe_display_layer->video_dev;
+ /* Initialize field of video device */
+ vbd->release = video_device_release_empty;
+ vbd->fops = &vpbe_fops;
+ vbd->ioctl_ops = &vpbe_ioctl_ops;
+ vbd->minor = -1;
+ vbd->v4l2_dev = &disp_dev->vpbe_dev->v4l2_dev;
+ vbd->lock = &vpbe_display_layer->opslock;
+
+ if (disp_dev->vpbe_dev->current_timings.timings_type &
+ VPBE_ENC_STD) {
+ vbd->tvnorms = (V4L2_STD_525_60 | V4L2_STD_625_50);
+ vbd->current_norm =
+ disp_dev->vpbe_dev->
+ current_timings.timings.std_id;
+ } else
+ vbd->current_norm = 0;
+
+ snprintf(vbd->name, sizeof(vbd->name),
+ "DaVinci_VPBE Display_DRIVER_V%d.%d.%d",
+ (VPBE_DISPLAY_VERSION_CODE >> 16) & 0xff,
+ (VPBE_DISPLAY_VERSION_CODE >> 8) & 0xff,
+ (VPBE_DISPLAY_VERSION_CODE) & 0xff);
+
+ vpbe_display_layer->device_id = i;
+
+ vpbe_display_layer->layer_info.id =
+ ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1);
+
+ /* Initialize prio member of layer object */
+ v4l2_prio_init(&vpbe_display_layer->prio);
+
+ return 0;
+}
+
+static __devinit int register_device(struct vpbe_layer *vpbe_display_layer,
+ struct vpbe_display *disp_dev,
+ struct platform_device *pdev) {
+ int err;
+
+ v4l2_info(&disp_dev->vpbe_dev->v4l2_dev,
+ "Trying to register VPBE display device.\n");
+ v4l2_info(&disp_dev->vpbe_dev->v4l2_dev,
+ "layer=%x,layer->video_dev=%x\n",
+ (int)vpbe_display_layer,
+ (int)&vpbe_display_layer->video_dev);
+
+ err = video_register_device(&vpbe_display_layer->video_dev,
+ VFL_TYPE_GRABBER,
+ -1);
+ if (err)
+ return -ENODEV;
+
+ vpbe_display_layer->disp_dev = disp_dev;
+ /* set the driver data in platform device */
+ platform_set_drvdata(pdev, disp_dev);
+ video_set_drvdata(&vpbe_display_layer->video_dev,
+ vpbe_display_layer);
+
+ return 0;
+}
+
+
+
+/*
+ * vpbe_display_probe()
+ * This function creates device entries by register itself to the V4L2 driver
+ * and initializes fields of each layer objects
+ */
+static __devinit int vpbe_display_probe(struct platform_device *pdev)
+{
+ struct vpbe_layer *vpbe_display_layer;
+ struct vpbe_display *disp_dev;
+ struct resource *res = NULL;
+ int k;
+ int i;
+ int err;
+ int irq;
+
+ printk(KERN_DEBUG "vpbe_display_probe\n");
+ /* Allocate memory for vpbe_display */
+ disp_dev = kzalloc(sizeof(struct vpbe_display), GFP_KERNEL);
+ if (!disp_dev) {
+ printk(KERN_ERR "ran out of memory\n");
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&disp_dev->dma_queue_lock);
+ /*
+ * Scan all the platform devices to find the vpbe
+ * controller device and get the vpbe_dev object
+ */
+ err = bus_for_each_dev(&platform_bus_type, NULL, disp_dev,
+ vpbe_device_get);
+ if (err < 0)
+ return err;
+ /* Initialize the vpbe display controller */
+ if (NULL != disp_dev->vpbe_dev->ops.initialize) {
+ err = disp_dev->vpbe_dev->ops.initialize(&pdev->dev,
+ disp_dev->vpbe_dev);
+ if (err) {
+ v4l2_err(&disp_dev->vpbe_dev->v4l2_dev,
+ "Error initing vpbe\n");
+ err = -ENOMEM;
+ goto probe_out;
+ }
+ }
+
+ for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
+ if (init_vpbe_layer(i, disp_dev, pdev)) {
+ err = -ENODEV;
+ goto probe_out;
+ }
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ v4l2_err(&disp_dev->vpbe_dev->v4l2_dev,
+ "Unable to get VENC interrupt resource\n");
+ err = -ENODEV;
+ goto probe_out;
+ }
+
+ irq = res->start;
+ if (request_irq(irq, venc_isr, IRQF_DISABLED, VPBE_DISPLAY_DRIVER,
+ disp_dev)) {
+ v4l2_err(&disp_dev->vpbe_dev->v4l2_dev,
+ "Unable to request interrupt\n");
+ err = -ENODEV;
+ goto probe_out;
+ }
+
+ for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
+ if (register_device(disp_dev->dev[i], disp_dev, pdev)) {
+ err = -ENODEV;
+ goto probe_out_irq;
+ }
+ }
+
+ printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 device\n");
+ return 0;
+
+probe_out_irq:
+ free_irq(res->start, disp_dev);
+probe_out:
+ for (k = 0; k < VPBE_DISPLAY_MAX_DEVICES; k++) {
+ /* Get the pointer to the layer object */
+ vpbe_display_layer = disp_dev->dev[k];
+ /* Unregister video device */
+ if (vpbe_display_layer) {
+ video_unregister_device(
+ &vpbe_display_layer->video_dev);
+ kfree(disp_dev->dev[k]);
+ }
+ }
+ kfree(disp_dev);
+ return err;
+}
+
+/*
+ * vpbe_display_remove()
+ * It un-register hardware layer from V4L2 driver
+ */
+static int vpbe_display_remove(struct platform_device *pdev)
+{
+ struct vpbe_layer *vpbe_display_layer;
+ struct vpbe_display *disp_dev = platform_get_drvdata(pdev);
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+ struct resource *res;
+ int i;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_remove\n");
+
+ /* unregister irq */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ free_irq(res->start, disp_dev);
+
+ /* deinitialize the vpbe display controller */
+ if (NULL != vpbe_dev->ops.deinitialize)
+ vpbe_dev->ops.deinitialize(&pdev->dev, vpbe_dev);
+ /* un-register device */
+ for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
+ /* Get the pointer to the layer object */
+ vpbe_display_layer = disp_dev->dev[i];
+ /* Unregister video device */
+ video_unregister_device(&vpbe_display_layer->video_dev);
+
+ }
+ for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
+ kfree(disp_dev->dev[i]);
+ disp_dev->dev[i] = NULL;
+ }
+
+ return 0;
+}
+
+static struct platform_driver vpbe_display_driver = {
+ .driver = {
+ .name = VPBE_DISPLAY_DRIVER,
+ .owner = THIS_MODULE,
+ .bus = &platform_bus_type,
+ },
+ .probe = vpbe_display_probe,
+ .remove = __devexit_p(vpbe_display_remove),
+};
+
+module_platform_driver(vpbe_display_driver);
+
+MODULE_DESCRIPTION("TI DM644x/DM355/DM365 VPBE Display controller");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Texas Instruments");
diff --git a/drivers/media/video/davinci/vpbe_osd.c b/drivers/media/video/davinci/vpbe_osd.c
new file mode 100644
index 00000000..bba299db
--- /dev/null
+++ b/drivers/media/video/davinci/vpbe_osd.c
@@ -0,0 +1,1605 @@
+/*
+ * Copyright (C) 2007-2010 Texas Instruments Inc
+ * Copyright (C) 2007 MontaVista Software, Inc.
+ *
+ * Andy Lowe (alowe@mvista.com), MontaVista Software
+ * - Initial version
+ * Murali Karicheri (mkaricheri@gmail.com), Texas Instruments Ltd.
+ * - ported to sub device interface
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+
+#include <mach/cputype.h>
+#include <mach/hardware.h>
+
+#include <media/davinci/vpss.h>
+#include <media/v4l2-device.h>
+#include <media/davinci/vpbe_types.h>
+#include <media/davinci/vpbe_osd.h>
+
+#include <linux/io.h>
+#include "vpbe_osd_regs.h"
+
+#define MODULE_NAME VPBE_OSD_SUBDEV_NAME
+
+/* register access routines */
+static inline u32 osd_read(struct osd_state *sd, u32 offset)
+{
+ struct osd_state *osd = sd;
+
+ return readl(osd->osd_base + offset);
+}
+
+static inline u32 osd_write(struct osd_state *sd, u32 val, u32 offset)
+{
+ struct osd_state *osd = sd;
+
+ writel(val, osd->osd_base + offset);
+
+ return val;
+}
+
+static inline u32 osd_set(struct osd_state *sd, u32 mask, u32 offset)
+{
+ struct osd_state *osd = sd;
+
+ u32 addr = osd->osd_base + offset;
+ u32 val = readl(addr) | mask;
+
+ writel(val, addr);
+
+ return val;
+}
+
+static inline u32 osd_clear(struct osd_state *sd, u32 mask, u32 offset)
+{
+ struct osd_state *osd = sd;
+
+ u32 addr = osd->osd_base + offset;
+ u32 val = readl(addr) & ~mask;
+
+ writel(val, addr);
+
+ return val;
+}
+
+static inline u32 osd_modify(struct osd_state *sd, u32 mask, u32 val,
+ u32 offset)
+{
+ struct osd_state *osd = sd;
+
+ u32 addr = osd->osd_base + offset;
+ u32 new_val = (readl(addr) & ~mask) | (val & mask);
+
+ writel(new_val, addr);
+
+ return new_val;
+}
+
+/* define some macros for layer and pixfmt classification */
+#define is_osd_win(layer) (((layer) == WIN_OSD0) || ((layer) == WIN_OSD1))
+#define is_vid_win(layer) (((layer) == WIN_VID0) || ((layer) == WIN_VID1))
+#define is_rgb_pixfmt(pixfmt) \
+ (((pixfmt) == PIXFMT_RGB565) || ((pixfmt) == PIXFMT_RGB888))
+#define is_yc_pixfmt(pixfmt) \
+ (((pixfmt) == PIXFMT_YCbCrI) || ((pixfmt) == PIXFMT_YCrCbI) || \
+ ((pixfmt) == PIXFMT_NV12))
+#define MAX_WIN_SIZE OSD_VIDWIN0XP_V0X
+#define MAX_LINE_LENGTH (OSD_VIDWIN0OFST_V0LO << 5)
+
+/**
+ * _osd_dm6446_vid0_pingpong() - field inversion fix for DM6446
+ * @sd - ptr to struct osd_state
+ * @field_inversion - inversion flag
+ * @fb_base_phys - frame buffer address
+ * @lconfig - ptr to layer config
+ *
+ * This routine implements a workaround for the field signal inversion silicon
+ * erratum described in Advisory 1.3.8 for the DM6446. The fb_base_phys and
+ * lconfig parameters apply to the vid0 window. This routine should be called
+ * whenever the vid0 layer configuration or start address is modified, or when
+ * the OSD field inversion setting is modified.
+ * Returns: 1 if the ping-pong buffers need to be toggled in the vsync isr, or
+ * 0 otherwise
+ */
+static int _osd_dm6446_vid0_pingpong(struct osd_state *sd,
+ int field_inversion,
+ unsigned long fb_base_phys,
+ const struct osd_layer_config *lconfig)
+{
+ struct osd_platform_data *pdata;
+
+ pdata = (struct osd_platform_data *)sd->dev->platform_data;
+ if (pdata->field_inv_wa_enable) {
+
+ if (!field_inversion || !lconfig->interlaced) {
+ osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN0ADR);
+ osd_write(sd, fb_base_phys & ~0x1F, OSD_PPVWIN0ADR);
+ osd_modify(sd, OSD_MISCCTL_PPSW | OSD_MISCCTL_PPRV, 0,
+ OSD_MISCCTL);
+ return 0;
+ } else {
+ unsigned miscctl = OSD_MISCCTL_PPRV;
+
+ osd_write(sd,
+ (fb_base_phys & ~0x1F) - lconfig->line_length,
+ OSD_VIDWIN0ADR);
+ osd_write(sd,
+ (fb_base_phys & ~0x1F) + lconfig->line_length,
+ OSD_PPVWIN0ADR);
+ osd_modify(sd,
+ OSD_MISCCTL_PPSW | OSD_MISCCTL_PPRV, miscctl,
+ OSD_MISCCTL);
+
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void _osd_set_field_inversion(struct osd_state *sd, int enable)
+{
+ unsigned fsinv = 0;
+
+ if (enable)
+ fsinv = OSD_MODE_FSINV;
+
+ osd_modify(sd, OSD_MODE_FSINV, fsinv, OSD_MODE);
+}
+
+static void _osd_set_blink_attribute(struct osd_state *sd, int enable,
+ enum osd_blink_interval blink)
+{
+ u32 osdatrmd = 0;
+
+ if (enable) {
+ osdatrmd |= OSD_OSDATRMD_BLNK;
+ osdatrmd |= blink << OSD_OSDATRMD_BLNKINT_SHIFT;
+ }
+ /* caller must ensure that OSD1 is configured in attribute mode */
+ osd_modify(sd, OSD_OSDATRMD_BLNKINT | OSD_OSDATRMD_BLNK, osdatrmd,
+ OSD_OSDATRMD);
+}
+
+static void _osd_set_rom_clut(struct osd_state *sd,
+ enum osd_rom_clut rom_clut)
+{
+ if (rom_clut == ROM_CLUT0)
+ osd_clear(sd, OSD_MISCCTL_RSEL, OSD_MISCCTL);
+ else
+ osd_set(sd, OSD_MISCCTL_RSEL, OSD_MISCCTL);
+}
+
+static void _osd_set_palette_map(struct osd_state *sd,
+ enum osd_win_layer osdwin,
+ unsigned char pixel_value,
+ unsigned char clut_index,
+ enum osd_pix_format pixfmt)
+{
+ static const int map_2bpp[] = { 0, 5, 10, 15 };
+ static const int map_1bpp[] = { 0, 15 };
+ int bmp_offset;
+ int bmp_shift;
+ int bmp_mask;
+ int bmp_reg;
+
+ switch (pixfmt) {
+ case PIXFMT_1BPP:
+ bmp_reg = map_1bpp[pixel_value & 0x1];
+ break;
+ case PIXFMT_2BPP:
+ bmp_reg = map_2bpp[pixel_value & 0x3];
+ break;
+ case PIXFMT_4BPP:
+ bmp_reg = pixel_value & 0xf;
+ break;
+ default:
+ return;
+ }
+
+ switch (osdwin) {
+ case OSDWIN_OSD0:
+ bmp_offset = OSD_W0BMP01 + (bmp_reg >> 1) * sizeof(u32);
+ break;
+ case OSDWIN_OSD1:
+ bmp_offset = OSD_W1BMP01 + (bmp_reg >> 1) * sizeof(u32);
+ break;
+ default:
+ return;
+ }
+
+ if (bmp_reg & 1) {
+ bmp_shift = 8;
+ bmp_mask = 0xff << 8;
+ } else {
+ bmp_shift = 0;
+ bmp_mask = 0xff;
+ }
+
+ osd_modify(sd, bmp_mask, clut_index << bmp_shift, bmp_offset);
+}
+
+static void _osd_set_rec601_attenuation(struct osd_state *sd,
+ enum osd_win_layer osdwin, int enable)
+{
+ switch (osdwin) {
+ case OSDWIN_OSD0:
+ osd_modify(sd, OSD_OSDWIN0MD_ATN0E,
+ enable ? OSD_OSDWIN0MD_ATN0E : 0,
+ OSD_OSDWIN0MD);
+ if (sd->vpbe_type == VPBE_VERSION_1)
+ osd_modify(sd, OSD_OSDWIN0MD_ATN0E,
+ enable ? OSD_OSDWIN0MD_ATN0E : 0,
+ OSD_OSDWIN0MD);
+ else if ((sd->vpbe_type == VPBE_VERSION_3) ||
+ (sd->vpbe_type == VPBE_VERSION_2))
+ osd_modify(sd, OSD_EXTMODE_ATNOSD0EN,
+ enable ? OSD_EXTMODE_ATNOSD0EN : 0,
+ OSD_EXTMODE);
+ break;
+ case OSDWIN_OSD1:
+ osd_modify(sd, OSD_OSDWIN1MD_ATN1E,
+ enable ? OSD_OSDWIN1MD_ATN1E : 0,
+ OSD_OSDWIN1MD);
+ if (sd->vpbe_type == VPBE_VERSION_1)
+ osd_modify(sd, OSD_OSDWIN1MD_ATN1E,
+ enable ? OSD_OSDWIN1MD_ATN1E : 0,
+ OSD_OSDWIN1MD);
+ else if ((sd->vpbe_type == VPBE_VERSION_3) ||
+ (sd->vpbe_type == VPBE_VERSION_2))
+ osd_modify(sd, OSD_EXTMODE_ATNOSD1EN,
+ enable ? OSD_EXTMODE_ATNOSD1EN : 0,
+ OSD_EXTMODE);
+ break;
+ }
+}
+
+static void _osd_set_blending_factor(struct osd_state *sd,
+ enum osd_win_layer osdwin,
+ enum osd_blending_factor blend)
+{
+ switch (osdwin) {
+ case OSDWIN_OSD0:
+ osd_modify(sd, OSD_OSDWIN0MD_BLND0,
+ blend << OSD_OSDWIN0MD_BLND0_SHIFT, OSD_OSDWIN0MD);
+ break;
+ case OSDWIN_OSD1:
+ osd_modify(sd, OSD_OSDWIN1MD_BLND1,
+ blend << OSD_OSDWIN1MD_BLND1_SHIFT, OSD_OSDWIN1MD);
+ break;
+ }
+}
+
+static void _osd_enable_rgb888_pixblend(struct osd_state *sd,
+ enum osd_win_layer osdwin)
+{
+
+ osd_modify(sd, OSD_MISCCTL_BLDSEL, 0, OSD_MISCCTL);
+ switch (osdwin) {
+ case OSDWIN_OSD0:
+ osd_modify(sd, OSD_EXTMODE_OSD0BLDCHR,
+ OSD_EXTMODE_OSD0BLDCHR, OSD_EXTMODE);
+ break;
+ case OSDWIN_OSD1:
+ osd_modify(sd, OSD_EXTMODE_OSD1BLDCHR,
+ OSD_EXTMODE_OSD1BLDCHR, OSD_EXTMODE);
+ break;
+ }
+}
+
+static void _osd_enable_color_key(struct osd_state *sd,
+ enum osd_win_layer osdwin,
+ unsigned colorkey,
+ enum osd_pix_format pixfmt)
+{
+ switch (pixfmt) {
+ case PIXFMT_1BPP:
+ case PIXFMT_2BPP:
+ case PIXFMT_4BPP:
+ case PIXFMT_8BPP:
+ if (sd->vpbe_type == VPBE_VERSION_3) {
+ switch (osdwin) {
+ case OSDWIN_OSD0:
+ osd_modify(sd, OSD_TRANSPBMPIDX_BMP0,
+ colorkey <<
+ OSD_TRANSPBMPIDX_BMP0_SHIFT,
+ OSD_TRANSPBMPIDX);
+ break;
+ case OSDWIN_OSD1:
+ osd_modify(sd, OSD_TRANSPBMPIDX_BMP1,
+ colorkey <<
+ OSD_TRANSPBMPIDX_BMP1_SHIFT,
+ OSD_TRANSPBMPIDX);
+ break;
+ }
+ }
+ break;
+ case PIXFMT_RGB565:
+ if (sd->vpbe_type == VPBE_VERSION_1)
+ osd_write(sd, colorkey & OSD_TRANSPVAL_RGBTRANS,
+ OSD_TRANSPVAL);
+ else if (sd->vpbe_type == VPBE_VERSION_3)
+ osd_write(sd, colorkey & OSD_TRANSPVALL_RGBL,
+ OSD_TRANSPVALL);
+ break;
+ case PIXFMT_YCbCrI:
+ case PIXFMT_YCrCbI:
+ if (sd->vpbe_type == VPBE_VERSION_3)
+ osd_modify(sd, OSD_TRANSPVALU_Y, colorkey,
+ OSD_TRANSPVALU);
+ break;
+ case PIXFMT_RGB888:
+ if (sd->vpbe_type == VPBE_VERSION_3) {
+ osd_write(sd, colorkey & OSD_TRANSPVALL_RGBL,
+ OSD_TRANSPVALL);
+ osd_modify(sd, OSD_TRANSPVALU_RGBU, colorkey >> 16,
+ OSD_TRANSPVALU);
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (osdwin) {
+ case OSDWIN_OSD0:
+ osd_set(sd, OSD_OSDWIN0MD_TE0, OSD_OSDWIN0MD);
+ break;
+ case OSDWIN_OSD1:
+ osd_set(sd, OSD_OSDWIN1MD_TE1, OSD_OSDWIN1MD);
+ break;
+ }
+}
+
+static void _osd_disable_color_key(struct osd_state *sd,
+ enum osd_win_layer osdwin)
+{
+ switch (osdwin) {
+ case OSDWIN_OSD0:
+ osd_clear(sd, OSD_OSDWIN0MD_TE0, OSD_OSDWIN0MD);
+ break;
+ case OSDWIN_OSD1:
+ osd_clear(sd, OSD_OSDWIN1MD_TE1, OSD_OSDWIN1MD);
+ break;
+ }
+}
+
+static void _osd_set_osd_clut(struct osd_state *sd,
+ enum osd_win_layer osdwin,
+ enum osd_clut clut)
+{
+ u32 winmd = 0;
+
+ switch (osdwin) {
+ case OSDWIN_OSD0:
+ if (clut == RAM_CLUT)
+ winmd |= OSD_OSDWIN0MD_CLUTS0;
+ osd_modify(sd, OSD_OSDWIN0MD_CLUTS0, winmd, OSD_OSDWIN0MD);
+ break;
+ case OSDWIN_OSD1:
+ if (clut == RAM_CLUT)
+ winmd |= OSD_OSDWIN1MD_CLUTS1;
+ osd_modify(sd, OSD_OSDWIN1MD_CLUTS1, winmd, OSD_OSDWIN1MD);
+ break;
+ }
+}
+
+static void _osd_set_zoom(struct osd_state *sd, enum osd_layer layer,
+ enum osd_zoom_factor h_zoom,
+ enum osd_zoom_factor v_zoom)
+{
+ u32 winmd = 0;
+
+ switch (layer) {
+ case WIN_OSD0:
+ winmd |= (h_zoom << OSD_OSDWIN0MD_OHZ0_SHIFT);
+ winmd |= (v_zoom << OSD_OSDWIN0MD_OVZ0_SHIFT);
+ osd_modify(sd, OSD_OSDWIN0MD_OHZ0 | OSD_OSDWIN0MD_OVZ0, winmd,
+ OSD_OSDWIN0MD);
+ break;
+ case WIN_VID0:
+ winmd |= (h_zoom << OSD_VIDWINMD_VHZ0_SHIFT);
+ winmd |= (v_zoom << OSD_VIDWINMD_VVZ0_SHIFT);
+ osd_modify(sd, OSD_VIDWINMD_VHZ0 | OSD_VIDWINMD_VVZ0, winmd,
+ OSD_VIDWINMD);
+ break;
+ case WIN_OSD1:
+ winmd |= (h_zoom << OSD_OSDWIN1MD_OHZ1_SHIFT);
+ winmd |= (v_zoom << OSD_OSDWIN1MD_OVZ1_SHIFT);
+ osd_modify(sd, OSD_OSDWIN1MD_OHZ1 | OSD_OSDWIN1MD_OVZ1, winmd,
+ OSD_OSDWIN1MD);
+ break;
+ case WIN_VID1:
+ winmd |= (h_zoom << OSD_VIDWINMD_VHZ1_SHIFT);
+ winmd |= (v_zoom << OSD_VIDWINMD_VVZ1_SHIFT);
+ osd_modify(sd, OSD_VIDWINMD_VHZ1 | OSD_VIDWINMD_VVZ1, winmd,
+ OSD_VIDWINMD);
+ break;
+ }
+}
+
+static void _osd_disable_layer(struct osd_state *sd, enum osd_layer layer)
+{
+ switch (layer) {
+ case WIN_OSD0:
+ osd_clear(sd, OSD_OSDWIN0MD_OACT0, OSD_OSDWIN0MD);
+ break;
+ case WIN_VID0:
+ osd_clear(sd, OSD_VIDWINMD_ACT0, OSD_VIDWINMD);
+ break;
+ case WIN_OSD1:
+ /* disable attribute mode as well as disabling the window */
+ osd_clear(sd, OSD_OSDWIN1MD_OASW | OSD_OSDWIN1MD_OACT1,
+ OSD_OSDWIN1MD);
+ break;
+ case WIN_VID1:
+ osd_clear(sd, OSD_VIDWINMD_ACT1, OSD_VIDWINMD);
+ break;
+ }
+}
+
+static void osd_disable_layer(struct osd_state *sd, enum osd_layer layer)
+{
+ struct osd_state *osd = sd;
+ struct osd_window_state *win = &osd->win[layer];
+ unsigned long flags;
+
+ spin_lock_irqsave(&osd->lock, flags);
+
+ if (!win->is_enabled) {
+ spin_unlock_irqrestore(&osd->lock, flags);
+ return;
+ }
+ win->is_enabled = 0;
+
+ _osd_disable_layer(sd, layer);
+
+ spin_unlock_irqrestore(&osd->lock, flags);
+}
+
+static void _osd_enable_attribute_mode(struct osd_state *sd)
+{
+ /* enable attribute mode for OSD1 */
+ osd_set(sd, OSD_OSDWIN1MD_OASW, OSD_OSDWIN1MD);
+}
+
+static void _osd_enable_layer(struct osd_state *sd, enum osd_layer layer)
+{
+ switch (layer) {
+ case WIN_OSD0:
+ osd_set(sd, OSD_OSDWIN0MD_OACT0, OSD_OSDWIN0MD);
+ break;
+ case WIN_VID0:
+ osd_set(sd, OSD_VIDWINMD_ACT0, OSD_VIDWINMD);
+ break;
+ case WIN_OSD1:
+ /* enable OSD1 and disable attribute mode */
+ osd_modify(sd, OSD_OSDWIN1MD_OASW | OSD_OSDWIN1MD_OACT1,
+ OSD_OSDWIN1MD_OACT1, OSD_OSDWIN1MD);
+ break;
+ case WIN_VID1:
+ osd_set(sd, OSD_VIDWINMD_ACT1, OSD_VIDWINMD);
+ break;
+ }
+}
+
+static int osd_enable_layer(struct osd_state *sd, enum osd_layer layer,
+ int otherwin)
+{
+ struct osd_state *osd = sd;
+ struct osd_window_state *win = &osd->win[layer];
+ struct osd_layer_config *cfg = &win->lconfig;
+ unsigned long flags;
+
+ spin_lock_irqsave(&osd->lock, flags);
+
+ /*
+ * use otherwin flag to know this is the other vid window
+ * in YUV420 mode, if is, skip this check
+ */
+ if (!otherwin && (!win->is_allocated ||
+ !win->fb_base_phys ||
+ !cfg->line_length ||
+ !cfg->xsize ||
+ !cfg->ysize)) {
+ spin_unlock_irqrestore(&osd->lock, flags);
+ return -1;
+ }
+
+ if (win->is_enabled) {
+ spin_unlock_irqrestore(&osd->lock, flags);
+ return 0;
+ }
+ win->is_enabled = 1;
+
+ if (cfg->pixfmt != PIXFMT_OSD_ATTR)
+ _osd_enable_layer(sd, layer);
+ else {
+ _osd_enable_attribute_mode(sd);
+ _osd_set_blink_attribute(sd, osd->is_blinking, osd->blink);
+ }
+
+ spin_unlock_irqrestore(&osd->lock, flags);
+
+ return 0;
+}
+
+#define OSD_SRC_ADDR_HIGH4 0x7800000
+#define OSD_SRC_ADDR_HIGH7 0x7F0000
+#define OSD_SRCADD_OFSET_SFT 23
+#define OSD_SRCADD_ADD_SFT 16
+#define OSD_WINADL_MASK 0xFFFF
+#define OSD_WINOFST_MASK 0x1000
+#define VPBE_REG_BASE 0x80000000
+
+static void _osd_start_layer(struct osd_state *sd, enum osd_layer layer,
+ unsigned long fb_base_phys,
+ unsigned long cbcr_ofst)
+{
+
+ if (sd->vpbe_type == VPBE_VERSION_1) {
+ switch (layer) {
+ case WIN_OSD0:
+ osd_write(sd, fb_base_phys & ~0x1F, OSD_OSDWIN0ADR);
+ break;
+ case WIN_VID0:
+ osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN0ADR);
+ break;
+ case WIN_OSD1:
+ osd_write(sd, fb_base_phys & ~0x1F, OSD_OSDWIN1ADR);
+ break;
+ case WIN_VID1:
+ osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN1ADR);
+ break;
+ }
+ } else if (sd->vpbe_type == VPBE_VERSION_3) {
+ unsigned long fb_offset_32 =
+ (fb_base_phys - VPBE_REG_BASE) >> 5;
+
+ switch (layer) {
+ case WIN_OSD0:
+ osd_modify(sd, OSD_OSDWINADH_O0AH,
+ fb_offset_32 >> (OSD_SRCADD_ADD_SFT -
+ OSD_OSDWINADH_O0AH_SHIFT),
+ OSD_OSDWINADH);
+ osd_write(sd, fb_offset_32 & OSD_OSDWIN0ADL_O0AL,
+ OSD_OSDWIN0ADL);
+ break;
+ case WIN_VID0:
+ osd_modify(sd, OSD_VIDWINADH_V0AH,
+ fb_offset_32 >> (OSD_SRCADD_ADD_SFT -
+ OSD_VIDWINADH_V0AH_SHIFT),
+ OSD_VIDWINADH);
+ osd_write(sd, fb_offset_32 & OSD_VIDWIN0ADL_V0AL,
+ OSD_VIDWIN0ADL);
+ break;
+ case WIN_OSD1:
+ osd_modify(sd, OSD_OSDWINADH_O1AH,
+ fb_offset_32 >> (OSD_SRCADD_ADD_SFT -
+ OSD_OSDWINADH_O1AH_SHIFT),
+ OSD_OSDWINADH);
+ osd_write(sd, fb_offset_32 & OSD_OSDWIN1ADL_O1AL,
+ OSD_OSDWIN1ADL);
+ break;
+ case WIN_VID1:
+ osd_modify(sd, OSD_VIDWINADH_V1AH,
+ fb_offset_32 >> (OSD_SRCADD_ADD_SFT -
+ OSD_VIDWINADH_V1AH_SHIFT),
+ OSD_VIDWINADH);
+ osd_write(sd, fb_offset_32 & OSD_VIDWIN1ADL_V1AL,
+ OSD_VIDWIN1ADL);
+ break;
+ }
+ } else if (sd->vpbe_type == VPBE_VERSION_2) {
+ struct osd_window_state *win = &sd->win[layer];
+ unsigned long fb_offset_32, cbcr_offset_32;
+
+ fb_offset_32 = fb_base_phys - VPBE_REG_BASE;
+ if (cbcr_ofst)
+ cbcr_offset_32 = cbcr_ofst;
+ else
+ cbcr_offset_32 = win->lconfig.line_length *
+ win->lconfig.ysize;
+ cbcr_offset_32 += fb_offset_32;
+ fb_offset_32 = fb_offset_32 >> 5;
+ cbcr_offset_32 = cbcr_offset_32 >> 5;
+ /*
+ * DM365: start address is 27-bit long address b26 - b23 are
+ * in offset register b12 - b9, and * bit 26 has to be '1'
+ */
+ if (win->lconfig.pixfmt == PIXFMT_NV12) {
+ switch (layer) {
+ case WIN_VID0:
+ case WIN_VID1:
+ /* Y is in VID0 */
+ osd_modify(sd, OSD_VIDWIN0OFST_V0AH,
+ ((fb_offset_32 & OSD_SRC_ADDR_HIGH4) >>
+ (OSD_SRCADD_OFSET_SFT -
+ OSD_WINOFST_AH_SHIFT)) |
+ OSD_WINOFST_MASK, OSD_VIDWIN0OFST);
+ osd_modify(sd, OSD_VIDWINADH_V0AH,
+ (fb_offset_32 & OSD_SRC_ADDR_HIGH7) >>
+ (OSD_SRCADD_ADD_SFT -
+ OSD_VIDWINADH_V0AH_SHIFT),
+ OSD_VIDWINADH);
+ osd_write(sd, fb_offset_32 & OSD_WINADL_MASK,
+ OSD_VIDWIN0ADL);
+ /* CbCr is in VID1 */
+ osd_modify(sd, OSD_VIDWIN1OFST_V1AH,
+ ((cbcr_offset_32 &
+ OSD_SRC_ADDR_HIGH4) >>
+ (OSD_SRCADD_OFSET_SFT -
+ OSD_WINOFST_AH_SHIFT)) |
+ OSD_WINOFST_MASK, OSD_VIDWIN1OFST);
+ osd_modify(sd, OSD_VIDWINADH_V1AH,
+ (cbcr_offset_32 &
+ OSD_SRC_ADDR_HIGH7) >>
+ (OSD_SRCADD_ADD_SFT -
+ OSD_VIDWINADH_V1AH_SHIFT),
+ OSD_VIDWINADH);
+ osd_write(sd, cbcr_offset_32 & OSD_WINADL_MASK,
+ OSD_VIDWIN1ADL);
+ break;
+ default:
+ break;
+ }
+ }
+
+ switch (layer) {
+ case WIN_OSD0:
+ osd_modify(sd, OSD_OSDWIN0OFST_O0AH,
+ ((fb_offset_32 & OSD_SRC_ADDR_HIGH4) >>
+ (OSD_SRCADD_OFSET_SFT -
+ OSD_WINOFST_AH_SHIFT)) | OSD_WINOFST_MASK,
+ OSD_OSDWIN0OFST);
+ osd_modify(sd, OSD_OSDWINADH_O0AH,
+ (fb_offset_32 & OSD_SRC_ADDR_HIGH7) >>
+ (OSD_SRCADD_ADD_SFT -
+ OSD_OSDWINADH_O0AH_SHIFT), OSD_OSDWINADH);
+ osd_write(sd, fb_offset_32 & OSD_WINADL_MASK,
+ OSD_OSDWIN0ADL);
+ break;
+ case WIN_VID0:
+ if (win->lconfig.pixfmt != PIXFMT_NV12) {
+ osd_modify(sd, OSD_VIDWIN0OFST_V0AH,
+ ((fb_offset_32 & OSD_SRC_ADDR_HIGH4) >>
+ (OSD_SRCADD_OFSET_SFT -
+ OSD_WINOFST_AH_SHIFT)) |
+ OSD_WINOFST_MASK, OSD_VIDWIN0OFST);
+ osd_modify(sd, OSD_VIDWINADH_V0AH,
+ (fb_offset_32 & OSD_SRC_ADDR_HIGH7) >>
+ (OSD_SRCADD_ADD_SFT -
+ OSD_VIDWINADH_V0AH_SHIFT),
+ OSD_VIDWINADH);
+ osd_write(sd, fb_offset_32 & OSD_WINADL_MASK,
+ OSD_VIDWIN0ADL);
+ }
+ break;
+ case WIN_OSD1:
+ osd_modify(sd, OSD_OSDWIN1OFST_O1AH,
+ ((fb_offset_32 & OSD_SRC_ADDR_HIGH4) >>
+ (OSD_SRCADD_OFSET_SFT -
+ OSD_WINOFST_AH_SHIFT)) | OSD_WINOFST_MASK,
+ OSD_OSDWIN1OFST);
+ osd_modify(sd, OSD_OSDWINADH_O1AH,
+ (fb_offset_32 & OSD_SRC_ADDR_HIGH7) >>
+ (OSD_SRCADD_ADD_SFT -
+ OSD_OSDWINADH_O1AH_SHIFT),
+ OSD_OSDWINADH);
+ osd_write(sd, fb_offset_32 & OSD_WINADL_MASK,
+ OSD_OSDWIN1ADL);
+ break;
+ case WIN_VID1:
+ if (win->lconfig.pixfmt != PIXFMT_NV12) {
+ osd_modify(sd, OSD_VIDWIN1OFST_V1AH,
+ ((fb_offset_32 & OSD_SRC_ADDR_HIGH4) >>
+ (OSD_SRCADD_OFSET_SFT -
+ OSD_WINOFST_AH_SHIFT)) |
+ OSD_WINOFST_MASK, OSD_VIDWIN1OFST);
+ osd_modify(sd, OSD_VIDWINADH_V1AH,
+ (fb_offset_32 & OSD_SRC_ADDR_HIGH7) >>
+ (OSD_SRCADD_ADD_SFT -
+ OSD_VIDWINADH_V1AH_SHIFT),
+ OSD_VIDWINADH);
+ osd_write(sd, fb_offset_32 & OSD_WINADL_MASK,
+ OSD_VIDWIN1ADL);
+ }
+ break;
+ }
+ }
+}
+
+static void osd_start_layer(struct osd_state *sd, enum osd_layer layer,
+ unsigned long fb_base_phys,
+ unsigned long cbcr_ofst)
+{
+ struct osd_state *osd = sd;
+ struct osd_window_state *win = &osd->win[layer];
+ struct osd_layer_config *cfg = &win->lconfig;
+ unsigned long flags;
+
+ spin_lock_irqsave(&osd->lock, flags);
+
+ win->fb_base_phys = fb_base_phys & ~0x1F;
+ _osd_start_layer(sd, layer, fb_base_phys, cbcr_ofst);
+
+ if (layer == WIN_VID0) {
+ osd->pingpong =
+ _osd_dm6446_vid0_pingpong(sd, osd->field_inversion,
+ win->fb_base_phys,
+ cfg);
+ }
+
+ spin_unlock_irqrestore(&osd->lock, flags);
+}
+
+static void osd_get_layer_config(struct osd_state *sd, enum osd_layer layer,
+ struct osd_layer_config *lconfig)
+{
+ struct osd_state *osd = sd;
+ struct osd_window_state *win = &osd->win[layer];
+ unsigned long flags;
+
+ spin_lock_irqsave(&osd->lock, flags);
+
+ *lconfig = win->lconfig;
+
+ spin_unlock_irqrestore(&osd->lock, flags);
+}
+
+/**
+ * try_layer_config() - Try a specific configuration for the layer
+ * @sd - ptr to struct osd_state
+ * @layer - layer to configure
+ * @lconfig - layer configuration to try
+ *
+ * If the requested lconfig is completely rejected and the value of lconfig on
+ * exit is the current lconfig, then try_layer_config() returns 1. Otherwise,
+ * try_layer_config() returns 0. A return value of 0 does not necessarily mean
+ * that the value of lconfig on exit is identical to the value of lconfig on
+ * entry, but merely that it represents a change from the current lconfig.
+ */
+static int try_layer_config(struct osd_state *sd, enum osd_layer layer,
+ struct osd_layer_config *lconfig)
+{
+ struct osd_state *osd = sd;
+ struct osd_window_state *win = &osd->win[layer];
+ int bad_config = 0;
+
+ /* verify that the pixel format is compatible with the layer */
+ switch (lconfig->pixfmt) {
+ case PIXFMT_1BPP:
+ case PIXFMT_2BPP:
+ case PIXFMT_4BPP:
+ case PIXFMT_8BPP:
+ case PIXFMT_RGB565:
+ if (osd->vpbe_type == VPBE_VERSION_1)
+ bad_config = !is_vid_win(layer);
+ break;
+ case PIXFMT_YCbCrI:
+ case PIXFMT_YCrCbI:
+ bad_config = !is_vid_win(layer);
+ break;
+ case PIXFMT_RGB888:
+ if (osd->vpbe_type == VPBE_VERSION_1)
+ bad_config = !is_vid_win(layer);
+ else if ((osd->vpbe_type == VPBE_VERSION_3) ||
+ (osd->vpbe_type == VPBE_VERSION_2))
+ bad_config = !is_osd_win(layer);
+ break;
+ case PIXFMT_NV12:
+ if (osd->vpbe_type != VPBE_VERSION_2)
+ bad_config = 1;
+ else
+ bad_config = is_osd_win(layer);
+ break;
+ case PIXFMT_OSD_ATTR:
+ bad_config = (layer != WIN_OSD1);
+ break;
+ default:
+ bad_config = 1;
+ break;
+ }
+ if (bad_config) {
+ /*
+ * The requested pixel format is incompatible with the layer,
+ * so keep the current layer configuration.
+ */
+ *lconfig = win->lconfig;
+ return bad_config;
+ }
+
+ /* DM6446: */
+ /* only one OSD window at a time can use RGB pixel formats */
+ if ((osd->vpbe_type == VPBE_VERSION_1) &&
+ is_osd_win(layer) && is_rgb_pixfmt(lconfig->pixfmt)) {
+ enum osd_pix_format pixfmt;
+ if (layer == WIN_OSD0)
+ pixfmt = osd->win[WIN_OSD1].lconfig.pixfmt;
+ else
+ pixfmt = osd->win[WIN_OSD0].lconfig.pixfmt;
+
+ if (is_rgb_pixfmt(pixfmt)) {
+ /*
+ * The other OSD window is already configured for an
+ * RGB, so keep the current layer configuration.
+ */
+ *lconfig = win->lconfig;
+ return 1;
+ }
+ }
+
+ /* DM6446: only one video window at a time can use RGB888 */
+ if ((osd->vpbe_type == VPBE_VERSION_1) && is_vid_win(layer) &&
+ lconfig->pixfmt == PIXFMT_RGB888) {
+ enum osd_pix_format pixfmt;
+
+ if (layer == WIN_VID0)
+ pixfmt = osd->win[WIN_VID1].lconfig.pixfmt;
+ else
+ pixfmt = osd->win[WIN_VID0].lconfig.pixfmt;
+
+ if (pixfmt == PIXFMT_RGB888) {
+ /*
+ * The other video window is already configured for
+ * RGB888, so keep the current layer configuration.
+ */
+ *lconfig = win->lconfig;
+ return 1;
+ }
+ }
+
+ /* window dimensions must be non-zero */
+ if (!lconfig->line_length || !lconfig->xsize || !lconfig->ysize) {
+ *lconfig = win->lconfig;
+ return 1;
+ }
+
+ /* round line_length up to a multiple of 32 */
+ lconfig->line_length = ((lconfig->line_length + 31) / 32) * 32;
+ lconfig->line_length =
+ min(lconfig->line_length, (unsigned)MAX_LINE_LENGTH);
+ lconfig->xsize = min(lconfig->xsize, (unsigned)MAX_WIN_SIZE);
+ lconfig->ysize = min(lconfig->ysize, (unsigned)MAX_WIN_SIZE);
+ lconfig->xpos = min(lconfig->xpos, (unsigned)MAX_WIN_SIZE);
+ lconfig->ypos = min(lconfig->ypos, (unsigned)MAX_WIN_SIZE);
+ lconfig->interlaced = (lconfig->interlaced != 0);
+ if (lconfig->interlaced) {
+ /* ysize and ypos must be even for interlaced displays */
+ lconfig->ysize &= ~1;
+ lconfig->ypos &= ~1;
+ }
+
+ return 0;
+}
+
+static void _osd_disable_vid_rgb888(struct osd_state *sd)
+{
+ /*
+ * The DM6446 supports RGB888 pixel format in a single video window.
+ * This routine disables RGB888 pixel format for both video windows.
+ * The caller must ensure that neither video window is currently
+ * configured for RGB888 pixel format.
+ */
+ if (sd->vpbe_type == VPBE_VERSION_1)
+ osd_clear(sd, OSD_MISCCTL_RGBEN, OSD_MISCCTL);
+}
+
+static void _osd_enable_vid_rgb888(struct osd_state *sd,
+ enum osd_layer layer)
+{
+ /*
+ * The DM6446 supports RGB888 pixel format in a single video window.
+ * This routine enables RGB888 pixel format for the specified video
+ * window. The caller must ensure that the other video window is not
+ * currently configured for RGB888 pixel format, as this routine will
+ * disable RGB888 pixel format for the other window.
+ */
+ if (sd->vpbe_type == VPBE_VERSION_1) {
+ if (layer == WIN_VID0)
+ osd_modify(sd, OSD_MISCCTL_RGBEN | OSD_MISCCTL_RGBWIN,
+ OSD_MISCCTL_RGBEN, OSD_MISCCTL);
+ else if (layer == WIN_VID1)
+ osd_modify(sd, OSD_MISCCTL_RGBEN | OSD_MISCCTL_RGBWIN,
+ OSD_MISCCTL_RGBEN | OSD_MISCCTL_RGBWIN,
+ OSD_MISCCTL);
+ }
+}
+
+static void _osd_set_cbcr_order(struct osd_state *sd,
+ enum osd_pix_format pixfmt)
+{
+ /*
+ * The caller must ensure that all windows using YC pixfmt use the same
+ * Cb/Cr order.
+ */
+ if (pixfmt == PIXFMT_YCbCrI)
+ osd_clear(sd, OSD_MODE_CS, OSD_MODE);
+ else if (pixfmt == PIXFMT_YCrCbI)
+ osd_set(sd, OSD_MODE_CS, OSD_MODE);
+}
+
+static void _osd_set_layer_config(struct osd_state *sd, enum osd_layer layer,
+ const struct osd_layer_config *lconfig)
+{
+ u32 winmd = 0, winmd_mask = 0, bmw = 0;
+
+ _osd_set_cbcr_order(sd, lconfig->pixfmt);
+
+ switch (layer) {
+ case WIN_OSD0:
+ if (sd->vpbe_type == VPBE_VERSION_1) {
+ winmd_mask |= OSD_OSDWIN0MD_RGB0E;
+ if (lconfig->pixfmt == PIXFMT_RGB565)
+ winmd |= OSD_OSDWIN0MD_RGB0E;
+ } else if ((sd->vpbe_type == VPBE_VERSION_3) ||
+ (sd->vpbe_type == VPBE_VERSION_2)) {
+ winmd_mask |= OSD_OSDWIN0MD_BMP0MD;
+ switch (lconfig->pixfmt) {
+ case PIXFMT_RGB565:
+ winmd |= (1 <<
+ OSD_OSDWIN0MD_BMP0MD_SHIFT);
+ break;
+ case PIXFMT_RGB888:
+ winmd |= (2 << OSD_OSDWIN0MD_BMP0MD_SHIFT);
+ _osd_enable_rgb888_pixblend(sd, OSDWIN_OSD0);
+ break;
+ case PIXFMT_YCbCrI:
+ case PIXFMT_YCrCbI:
+ winmd |= (3 << OSD_OSDWIN0MD_BMP0MD_SHIFT);
+ break;
+ default:
+ break;
+ }
+ }
+
+ winmd_mask |= OSD_OSDWIN0MD_BMW0 | OSD_OSDWIN0MD_OFF0;
+
+ switch (lconfig->pixfmt) {
+ case PIXFMT_1BPP:
+ bmw = 0;
+ break;
+ case PIXFMT_2BPP:
+ bmw = 1;
+ break;
+ case PIXFMT_4BPP:
+ bmw = 2;
+ break;
+ case PIXFMT_8BPP:
+ bmw = 3;
+ break;
+ default:
+ break;
+ }
+ winmd |= (bmw << OSD_OSDWIN0MD_BMW0_SHIFT);
+
+ if (lconfig->interlaced)
+ winmd |= OSD_OSDWIN0MD_OFF0;
+
+ osd_modify(sd, winmd_mask, winmd, OSD_OSDWIN0MD);
+ osd_write(sd, lconfig->line_length >> 5, OSD_OSDWIN0OFST);
+ osd_write(sd, lconfig->xpos, OSD_OSDWIN0XP);
+ osd_write(sd, lconfig->xsize, OSD_OSDWIN0XL);
+ if (lconfig->interlaced) {
+ osd_write(sd, lconfig->ypos >> 1, OSD_OSDWIN0YP);
+ osd_write(sd, lconfig->ysize >> 1, OSD_OSDWIN0YL);
+ } else {
+ osd_write(sd, lconfig->ypos, OSD_OSDWIN0YP);
+ osd_write(sd, lconfig->ysize, OSD_OSDWIN0YL);
+ }
+ break;
+ case WIN_VID0:
+ winmd_mask |= OSD_VIDWINMD_VFF0;
+ if (lconfig->interlaced)
+ winmd |= OSD_VIDWINMD_VFF0;
+
+ osd_modify(sd, winmd_mask, winmd, OSD_VIDWINMD);
+ osd_write(sd, lconfig->line_length >> 5, OSD_VIDWIN0OFST);
+ osd_write(sd, lconfig->xpos, OSD_VIDWIN0XP);
+ osd_write(sd, lconfig->xsize, OSD_VIDWIN0XL);
+ /*
+ * For YUV420P format the register contents are
+ * duplicated in both VID registers
+ */
+ if ((sd->vpbe_type == VPBE_VERSION_2) &&
+ (lconfig->pixfmt == PIXFMT_NV12)) {
+ /* other window also */
+ if (lconfig->interlaced) {
+ winmd_mask |= OSD_VIDWINMD_VFF1;
+ winmd |= OSD_VIDWINMD_VFF1;
+ osd_modify(sd, winmd_mask, winmd,
+ OSD_VIDWINMD);
+ }
+
+ osd_modify(sd, OSD_MISCCTL_S420D,
+ OSD_MISCCTL_S420D, OSD_MISCCTL);
+ osd_write(sd, lconfig->line_length >> 5,
+ OSD_VIDWIN1OFST);
+ osd_write(sd, lconfig->xpos, OSD_VIDWIN1XP);
+ osd_write(sd, lconfig->xsize, OSD_VIDWIN1XL);
+ /*
+ * if NV21 pixfmt and line length not 32B
+ * aligned (e.g. NTSC), Need to set window
+ * X pixel size to be 32B aligned as well
+ */
+ if (lconfig->xsize % 32) {
+ osd_write(sd,
+ ((lconfig->xsize + 31) & ~31),
+ OSD_VIDWIN1XL);
+ osd_write(sd,
+ ((lconfig->xsize + 31) & ~31),
+ OSD_VIDWIN0XL);
+ }
+ } else if ((sd->vpbe_type == VPBE_VERSION_2) &&
+ (lconfig->pixfmt != PIXFMT_NV12)) {
+ osd_modify(sd, OSD_MISCCTL_S420D, ~OSD_MISCCTL_S420D,
+ OSD_MISCCTL);
+ }
+
+ if (lconfig->interlaced) {
+ osd_write(sd, lconfig->ypos >> 1, OSD_VIDWIN0YP);
+ osd_write(sd, lconfig->ysize >> 1, OSD_VIDWIN0YL);
+ if ((sd->vpbe_type == VPBE_VERSION_2) &&
+ lconfig->pixfmt == PIXFMT_NV12) {
+ osd_write(sd, lconfig->ypos >> 1,
+ OSD_VIDWIN1YP);
+ osd_write(sd, lconfig->ysize >> 1,
+ OSD_VIDWIN1YL);
+ }
+ } else {
+ osd_write(sd, lconfig->ypos, OSD_VIDWIN0YP);
+ osd_write(sd, lconfig->ysize, OSD_VIDWIN0YL);
+ if ((sd->vpbe_type == VPBE_VERSION_2) &&
+ lconfig->pixfmt == PIXFMT_NV12) {
+ osd_write(sd, lconfig->ypos, OSD_VIDWIN1YP);
+ osd_write(sd, lconfig->ysize, OSD_VIDWIN1YL);
+ }
+ }
+ break;
+ case WIN_OSD1:
+ /*
+ * The caller must ensure that OSD1 is disabled prior to
+ * switching from a normal mode to attribute mode or from
+ * attribute mode to a normal mode.
+ */
+ if (lconfig->pixfmt == PIXFMT_OSD_ATTR) {
+ if (sd->vpbe_type == VPBE_VERSION_1) {
+ winmd_mask |= OSD_OSDWIN1MD_ATN1E |
+ OSD_OSDWIN1MD_RGB1E | OSD_OSDWIN1MD_CLUTS1 |
+ OSD_OSDWIN1MD_BLND1 | OSD_OSDWIN1MD_TE1;
+ } else {
+ winmd_mask |= OSD_OSDWIN1MD_BMP1MD |
+ OSD_OSDWIN1MD_CLUTS1 | OSD_OSDWIN1MD_BLND1 |
+ OSD_OSDWIN1MD_TE1;
+ }
+ } else {
+ if (sd->vpbe_type == VPBE_VERSION_1) {
+ winmd_mask |= OSD_OSDWIN1MD_RGB1E;
+ if (lconfig->pixfmt == PIXFMT_RGB565)
+ winmd |= OSD_OSDWIN1MD_RGB1E;
+ } else if ((sd->vpbe_type == VPBE_VERSION_3)
+ || (sd->vpbe_type == VPBE_VERSION_2)) {
+ winmd_mask |= OSD_OSDWIN1MD_BMP1MD;
+ switch (lconfig->pixfmt) {
+ case PIXFMT_RGB565:
+ winmd |=
+ (1 << OSD_OSDWIN1MD_BMP1MD_SHIFT);
+ break;
+ case PIXFMT_RGB888:
+ winmd |=
+ (2 << OSD_OSDWIN1MD_BMP1MD_SHIFT);
+ _osd_enable_rgb888_pixblend(sd,
+ OSDWIN_OSD1);
+ break;
+ case PIXFMT_YCbCrI:
+ case PIXFMT_YCrCbI:
+ winmd |=
+ (3 << OSD_OSDWIN1MD_BMP1MD_SHIFT);
+ break;
+ default:
+ break;
+ }
+ }
+
+ winmd_mask |= OSD_OSDWIN1MD_BMW1;
+ switch (lconfig->pixfmt) {
+ case PIXFMT_1BPP:
+ bmw = 0;
+ break;
+ case PIXFMT_2BPP:
+ bmw = 1;
+ break;
+ case PIXFMT_4BPP:
+ bmw = 2;
+ break;
+ case PIXFMT_8BPP:
+ bmw = 3;
+ break;
+ default:
+ break;
+ }
+ winmd |= (bmw << OSD_OSDWIN1MD_BMW1_SHIFT);
+ }
+
+ winmd_mask |= OSD_OSDWIN1MD_OFF1;
+ if (lconfig->interlaced)
+ winmd |= OSD_OSDWIN1MD_OFF1;
+
+ osd_modify(sd, winmd_mask, winmd, OSD_OSDWIN1MD);
+ osd_write(sd, lconfig->line_length >> 5, OSD_OSDWIN1OFST);
+ osd_write(sd, lconfig->xpos, OSD_OSDWIN1XP);
+ osd_write(sd, lconfig->xsize, OSD_OSDWIN1XL);
+ if (lconfig->interlaced) {
+ osd_write(sd, lconfig->ypos >> 1, OSD_OSDWIN1YP);
+ osd_write(sd, lconfig->ysize >> 1, OSD_OSDWIN1YL);
+ } else {
+ osd_write(sd, lconfig->ypos, OSD_OSDWIN1YP);
+ osd_write(sd, lconfig->ysize, OSD_OSDWIN1YL);
+ }
+ break;
+ case WIN_VID1:
+ winmd_mask |= OSD_VIDWINMD_VFF1;
+ if (lconfig->interlaced)
+ winmd |= OSD_VIDWINMD_VFF1;
+
+ osd_modify(sd, winmd_mask, winmd, OSD_VIDWINMD);
+ osd_write(sd, lconfig->line_length >> 5, OSD_VIDWIN1OFST);
+ osd_write(sd, lconfig->xpos, OSD_VIDWIN1XP);
+ osd_write(sd, lconfig->xsize, OSD_VIDWIN1XL);
+ /*
+ * For YUV420P format the register contents are
+ * duplicated in both VID registers
+ */
+ if (sd->vpbe_type == VPBE_VERSION_2) {
+ if (lconfig->pixfmt == PIXFMT_NV12) {
+ /* other window also */
+ if (lconfig->interlaced) {
+ winmd_mask |= OSD_VIDWINMD_VFF0;
+ winmd |= OSD_VIDWINMD_VFF0;
+ osd_modify(sd, winmd_mask, winmd,
+ OSD_VIDWINMD);
+ }
+ osd_modify(sd, OSD_MISCCTL_S420D,
+ OSD_MISCCTL_S420D, OSD_MISCCTL);
+ osd_write(sd, lconfig->line_length >> 5,
+ OSD_VIDWIN0OFST);
+ osd_write(sd, lconfig->xpos, OSD_VIDWIN0XP);
+ osd_write(sd, lconfig->xsize, OSD_VIDWIN0XL);
+ } else {
+ osd_modify(sd, OSD_MISCCTL_S420D,
+ ~OSD_MISCCTL_S420D, OSD_MISCCTL);
+ }
+ }
+
+ if (lconfig->interlaced) {
+ osd_write(sd, lconfig->ypos >> 1, OSD_VIDWIN1YP);
+ osd_write(sd, lconfig->ysize >> 1, OSD_VIDWIN1YL);
+ if ((sd->vpbe_type == VPBE_VERSION_2) &&
+ lconfig->pixfmt == PIXFMT_NV12) {
+ osd_write(sd, lconfig->ypos >> 1,
+ OSD_VIDWIN0YP);
+ osd_write(sd, lconfig->ysize >> 1,
+ OSD_VIDWIN0YL);
+ }
+ } else {
+ osd_write(sd, lconfig->ypos, OSD_VIDWIN1YP);
+ osd_write(sd, lconfig->ysize, OSD_VIDWIN1YL);
+ if ((sd->vpbe_type == VPBE_VERSION_2) &&
+ lconfig->pixfmt == PIXFMT_NV12) {
+ osd_write(sd, lconfig->ypos, OSD_VIDWIN0YP);
+ osd_write(sd, lconfig->ysize, OSD_VIDWIN0YL);
+ }
+ }
+ break;
+ }
+}
+
+static int osd_set_layer_config(struct osd_state *sd, enum osd_layer layer,
+ struct osd_layer_config *lconfig)
+{
+ struct osd_state *osd = sd;
+ struct osd_window_state *win = &osd->win[layer];
+ struct osd_layer_config *cfg = &win->lconfig;
+ unsigned long flags;
+ int reject_config;
+
+ spin_lock_irqsave(&osd->lock, flags);
+
+ reject_config = try_layer_config(sd, layer, lconfig);
+ if (reject_config) {
+ spin_unlock_irqrestore(&osd->lock, flags);
+ return reject_config;
+ }
+
+ /* update the current Cb/Cr order */
+ if (is_yc_pixfmt(lconfig->pixfmt))
+ osd->yc_pixfmt = lconfig->pixfmt;
+
+ /*
+ * If we are switching OSD1 from normal mode to attribute mode or from
+ * attribute mode to normal mode, then we must disable the window.
+ */
+ if (layer == WIN_OSD1) {
+ if (((lconfig->pixfmt == PIXFMT_OSD_ATTR) &&
+ (cfg->pixfmt != PIXFMT_OSD_ATTR)) ||
+ ((lconfig->pixfmt != PIXFMT_OSD_ATTR) &&
+ (cfg->pixfmt == PIXFMT_OSD_ATTR))) {
+ win->is_enabled = 0;
+ _osd_disable_layer(sd, layer);
+ }
+ }
+
+ _osd_set_layer_config(sd, layer, lconfig);
+
+ if (layer == WIN_OSD1) {
+ struct osd_osdwin_state *osdwin_state =
+ &osd->osdwin[OSDWIN_OSD1];
+
+ if ((lconfig->pixfmt != PIXFMT_OSD_ATTR) &&
+ (cfg->pixfmt == PIXFMT_OSD_ATTR)) {
+ /*
+ * We just switched OSD1 from attribute mode to normal
+ * mode, so we must initialize the CLUT select, the
+ * blend factor, transparency colorkey enable, and
+ * attenuation enable (DM6446 only) bits in the
+ * OSDWIN1MD register.
+ */
+ _osd_set_osd_clut(sd, OSDWIN_OSD1,
+ osdwin_state->clut);
+ _osd_set_blending_factor(sd, OSDWIN_OSD1,
+ osdwin_state->blend);
+ if (osdwin_state->colorkey_blending) {
+ _osd_enable_color_key(sd, OSDWIN_OSD1,
+ osdwin_state->
+ colorkey,
+ lconfig->pixfmt);
+ } else
+ _osd_disable_color_key(sd, OSDWIN_OSD1);
+ _osd_set_rec601_attenuation(sd, OSDWIN_OSD1,
+ osdwin_state->
+ rec601_attenuation);
+ } else if ((lconfig->pixfmt == PIXFMT_OSD_ATTR) &&
+ (cfg->pixfmt != PIXFMT_OSD_ATTR)) {
+ /*
+ * We just switched OSD1 from normal mode to attribute
+ * mode, so we must initialize the blink enable and
+ * blink interval bits in the OSDATRMD register.
+ */
+ _osd_set_blink_attribute(sd, osd->is_blinking,
+ osd->blink);
+ }
+ }
+
+ /*
+ * If we just switched to a 1-, 2-, or 4-bits-per-pixel bitmap format
+ * then configure a default palette map.
+ */
+ if ((lconfig->pixfmt != cfg->pixfmt) &&
+ ((lconfig->pixfmt == PIXFMT_1BPP) ||
+ (lconfig->pixfmt == PIXFMT_2BPP) ||
+ (lconfig->pixfmt == PIXFMT_4BPP))) {
+ enum osd_win_layer osdwin =
+ ((layer == WIN_OSD0) ? OSDWIN_OSD0 : OSDWIN_OSD1);
+ struct osd_osdwin_state *osdwin_state =
+ &osd->osdwin[osdwin];
+ unsigned char clut_index;
+ unsigned char clut_entries = 0;
+
+ switch (lconfig->pixfmt) {
+ case PIXFMT_1BPP:
+ clut_entries = 2;
+ break;
+ case PIXFMT_2BPP:
+ clut_entries = 4;
+ break;
+ case PIXFMT_4BPP:
+ clut_entries = 16;
+ break;
+ default:
+ break;
+ }
+ /*
+ * The default palette map maps the pixel value to the clut
+ * index, i.e. pixel value 0 maps to clut entry 0, pixel value
+ * 1 maps to clut entry 1, etc.
+ */
+ for (clut_index = 0; clut_index < 16; clut_index++) {
+ osdwin_state->palette_map[clut_index] = clut_index;
+ if (clut_index < clut_entries) {
+ _osd_set_palette_map(sd, osdwin, clut_index,
+ clut_index,
+ lconfig->pixfmt);
+ }
+ }
+ }
+
+ *cfg = *lconfig;
+ /* DM6446: configure the RGB888 enable and window selection */
+ if (osd->win[WIN_VID0].lconfig.pixfmt == PIXFMT_RGB888)
+ _osd_enable_vid_rgb888(sd, WIN_VID0);
+ else if (osd->win[WIN_VID1].lconfig.pixfmt == PIXFMT_RGB888)
+ _osd_enable_vid_rgb888(sd, WIN_VID1);
+ else
+ _osd_disable_vid_rgb888(sd);
+
+ if (layer == WIN_VID0) {
+ osd->pingpong =
+ _osd_dm6446_vid0_pingpong(sd, osd->field_inversion,
+ win->fb_base_phys,
+ cfg);
+ }
+
+ spin_unlock_irqrestore(&osd->lock, flags);
+
+ return 0;
+}
+
+static void osd_init_layer(struct osd_state *sd, enum osd_layer layer)
+{
+ struct osd_state *osd = sd;
+ struct osd_window_state *win = &osd->win[layer];
+ enum osd_win_layer osdwin;
+ struct osd_osdwin_state *osdwin_state;
+ struct osd_layer_config *cfg = &win->lconfig;
+ unsigned long flags;
+
+ spin_lock_irqsave(&osd->lock, flags);
+
+ win->is_enabled = 0;
+ _osd_disable_layer(sd, layer);
+
+ win->h_zoom = ZOOM_X1;
+ win->v_zoom = ZOOM_X1;
+ _osd_set_zoom(sd, layer, win->h_zoom, win->v_zoom);
+
+ win->fb_base_phys = 0;
+ _osd_start_layer(sd, layer, win->fb_base_phys, 0);
+
+ cfg->line_length = 0;
+ cfg->xsize = 0;
+ cfg->ysize = 0;
+ cfg->xpos = 0;
+ cfg->ypos = 0;
+ cfg->interlaced = 0;
+ switch (layer) {
+ case WIN_OSD0:
+ case WIN_OSD1:
+ osdwin = (layer == WIN_OSD0) ? OSDWIN_OSD0 : OSDWIN_OSD1;
+ osdwin_state = &osd->osdwin[osdwin];
+ /*
+ * Other code relies on the fact that OSD windows default to a
+ * bitmap pixel format when they are deallocated, so don't
+ * change this default pixel format.
+ */
+ cfg->pixfmt = PIXFMT_8BPP;
+ _osd_set_layer_config(sd, layer, cfg);
+ osdwin_state->clut = RAM_CLUT;
+ _osd_set_osd_clut(sd, osdwin, osdwin_state->clut);
+ osdwin_state->colorkey_blending = 0;
+ _osd_disable_color_key(sd, osdwin);
+ osdwin_state->blend = OSD_8_VID_0;
+ _osd_set_blending_factor(sd, osdwin, osdwin_state->blend);
+ osdwin_state->rec601_attenuation = 0;
+ _osd_set_rec601_attenuation(sd, osdwin,
+ osdwin_state->
+ rec601_attenuation);
+ if (osdwin == OSDWIN_OSD1) {
+ osd->is_blinking = 0;
+ osd->blink = BLINK_X1;
+ }
+ break;
+ case WIN_VID0:
+ case WIN_VID1:
+ cfg->pixfmt = osd->yc_pixfmt;
+ _osd_set_layer_config(sd, layer, cfg);
+ break;
+ }
+
+ spin_unlock_irqrestore(&osd->lock, flags);
+}
+
+static void osd_release_layer(struct osd_state *sd, enum osd_layer layer)
+{
+ struct osd_state *osd = sd;
+ struct osd_window_state *win = &osd->win[layer];
+ unsigned long flags;
+
+ spin_lock_irqsave(&osd->lock, flags);
+
+ if (!win->is_allocated) {
+ spin_unlock_irqrestore(&osd->lock, flags);
+ return;
+ }
+
+ spin_unlock_irqrestore(&osd->lock, flags);
+ osd_init_layer(sd, layer);
+ spin_lock_irqsave(&osd->lock, flags);
+
+ win->is_allocated = 0;
+
+ spin_unlock_irqrestore(&osd->lock, flags);
+}
+
+static int osd_request_layer(struct osd_state *sd, enum osd_layer layer)
+{
+ struct osd_state *osd = sd;
+ struct osd_window_state *win = &osd->win[layer];
+ unsigned long flags;
+
+ spin_lock_irqsave(&osd->lock, flags);
+
+ if (win->is_allocated) {
+ spin_unlock_irqrestore(&osd->lock, flags);
+ return -1;
+ }
+ win->is_allocated = 1;
+
+ spin_unlock_irqrestore(&osd->lock, flags);
+
+ return 0;
+}
+
+static void _osd_init(struct osd_state *sd)
+{
+ osd_write(sd, 0, OSD_MODE);
+ osd_write(sd, 0, OSD_VIDWINMD);
+ osd_write(sd, 0, OSD_OSDWIN0MD);
+ osd_write(sd, 0, OSD_OSDWIN1MD);
+ osd_write(sd, 0, OSD_RECTCUR);
+ osd_write(sd, 0, OSD_MISCCTL);
+ if (sd->vpbe_type == VPBE_VERSION_3) {
+ osd_write(sd, 0, OSD_VBNDRY);
+ osd_write(sd, 0, OSD_EXTMODE);
+ osd_write(sd, OSD_MISCCTL_DMANG, OSD_MISCCTL);
+ }
+}
+
+static void osd_set_left_margin(struct osd_state *sd, u32 val)
+{
+ osd_write(sd, val, OSD_BASEPX);
+}
+
+static void osd_set_top_margin(struct osd_state *sd, u32 val)
+{
+ osd_write(sd, val, OSD_BASEPY);
+}
+
+static int osd_initialize(struct osd_state *osd)
+{
+ if (osd == NULL)
+ return -ENODEV;
+ _osd_init(osd);
+
+ /* set default Cb/Cr order */
+ osd->yc_pixfmt = PIXFMT_YCbCrI;
+
+ if (osd->vpbe_type == VPBE_VERSION_3) {
+ /*
+ * ROM CLUT1 on the DM355 is similar (identical?) to ROM CLUT0
+ * on the DM6446, so make ROM_CLUT1 the default on the DM355.
+ */
+ osd->rom_clut = ROM_CLUT1;
+ }
+
+ _osd_set_field_inversion(osd, osd->field_inversion);
+ _osd_set_rom_clut(osd, osd->rom_clut);
+
+ osd_init_layer(osd, WIN_OSD0);
+ osd_init_layer(osd, WIN_VID0);
+ osd_init_layer(osd, WIN_OSD1);
+ osd_init_layer(osd, WIN_VID1);
+
+ return 0;
+}
+
+static const struct vpbe_osd_ops osd_ops = {
+ .initialize = osd_initialize,
+ .request_layer = osd_request_layer,
+ .release_layer = osd_release_layer,
+ .enable_layer = osd_enable_layer,
+ .disable_layer = osd_disable_layer,
+ .set_layer_config = osd_set_layer_config,
+ .get_layer_config = osd_get_layer_config,
+ .start_layer = osd_start_layer,
+ .set_left_margin = osd_set_left_margin,
+ .set_top_margin = osd_set_top_margin,
+};
+
+static int osd_probe(struct platform_device *pdev)
+{
+ struct osd_platform_data *pdata;
+ struct osd_state *osd;
+ struct resource *res;
+ int ret = 0;
+
+ osd = kzalloc(sizeof(struct osd_state), GFP_KERNEL);
+ if (osd == NULL)
+ return -ENOMEM;
+
+ osd->dev = &pdev->dev;
+ pdata = (struct osd_platform_data *)pdev->dev.platform_data;
+ osd->vpbe_type = (enum vpbe_version)pdata->vpbe_type;
+ if (NULL == pdev->dev.platform_data) {
+ dev_err(osd->dev, "No platform data defined for OSD"
+ " sub device\n");
+ ret = -ENOENT;
+ goto free_mem;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(osd->dev, "Unable to get OSD register address map\n");
+ ret = -ENODEV;
+ goto free_mem;
+ }
+ osd->osd_base_phys = res->start;
+ osd->osd_size = resource_size(res);
+ if (!request_mem_region(osd->osd_base_phys, osd->osd_size,
+ MODULE_NAME)) {
+ dev_err(osd->dev, "Unable to reserve OSD MMIO region\n");
+ ret = -ENODEV;
+ goto free_mem;
+ }
+ osd->osd_base = (unsigned long)ioremap_nocache(res->start,
+ osd->osd_size);
+ if (!osd->osd_base) {
+ dev_err(osd->dev, "Unable to map the OSD region\n");
+ ret = -ENODEV;
+ goto release_mem_region;
+ }
+ spin_lock_init(&osd->lock);
+ osd->ops = osd_ops;
+ platform_set_drvdata(pdev, osd);
+ dev_notice(osd->dev, "OSD sub device probe success\n");
+ return ret;
+
+release_mem_region:
+ release_mem_region(osd->osd_base_phys, osd->osd_size);
+free_mem:
+ kfree(osd);
+ return ret;
+}
+
+static int osd_remove(struct platform_device *pdev)
+{
+ struct osd_state *osd = platform_get_drvdata(pdev);
+
+ iounmap((void *)osd->osd_base);
+ release_mem_region(osd->osd_base_phys, osd->osd_size);
+ kfree(osd);
+ return 0;
+}
+
+static struct platform_driver osd_driver = {
+ .probe = osd_probe,
+ .remove = osd_remove,
+ .driver = {
+ .name = MODULE_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(osd_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("DaVinci OSD Manager Driver");
+MODULE_AUTHOR("Texas Instruments");
diff --git a/drivers/media/video/davinci/vpbe_osd_regs.h b/drivers/media/video/davinci/vpbe_osd_regs.h
new file mode 100644
index 00000000..584520f3
--- /dev/null
+++ b/drivers/media/video/davinci/vpbe_osd_regs.h
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2006-2010 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _VPBE_OSD_REGS_H
+#define _VPBE_OSD_REGS_H
+
+/* VPBE Global Registers */
+#define VPBE_PID 0x0
+#define VPBE_PCR 0x4
+
+/* VPSS CLock Registers */
+#define VPSSCLK_PID 0x00
+#define VPSSCLK_CLKCTRL 0x04
+
+/* VPSS Buffer Logic Registers */
+#define VPSSBL_PID 0x00
+#define VPSSBL_PCR 0x04
+#define VPSSBL_BCR 0x08
+#define VPSSBL_INTSTAT 0x0C
+#define VPSSBL_INTSEL 0x10
+#define VPSSBL_EVTSEL 0x14
+#define VPSSBL_MEMCTRL 0x18
+#define VPSSBL_CCDCMUX 0x1C
+
+/* DM365 ISP5 system configuration */
+#define ISP5_PID 0x0
+#define ISP5_PCCR 0x4
+#define ISP5_BCR 0x8
+#define ISP5_INTSTAT 0xC
+#define ISP5_INTSEL1 0x10
+#define ISP5_INTSEL2 0x14
+#define ISP5_INTSEL3 0x18
+#define ISP5_EVTSEL 0x1c
+#define ISP5_CCDCMUX 0x20
+
+/* VPBE On-Screen Display Subsystem Registers (OSD) */
+#define OSD_MODE 0x00
+#define OSD_VIDWINMD 0x04
+#define OSD_OSDWIN0MD 0x08
+#define OSD_OSDWIN1MD 0x0C
+#define OSD_OSDATRMD 0x0C
+#define OSD_RECTCUR 0x10
+#define OSD_VIDWIN0OFST 0x18
+#define OSD_VIDWIN1OFST 0x1C
+#define OSD_OSDWIN0OFST 0x20
+#define OSD_OSDWIN1OFST 0x24
+#define OSD_VIDWINADH 0x28
+#define OSD_VIDWIN0ADL 0x2C
+#define OSD_VIDWIN0ADR 0x2C
+#define OSD_VIDWIN1ADL 0x30
+#define OSD_VIDWIN1ADR 0x30
+#define OSD_OSDWINADH 0x34
+#define OSD_OSDWIN0ADL 0x38
+#define OSD_OSDWIN0ADR 0x38
+#define OSD_OSDWIN1ADL 0x3C
+#define OSD_OSDWIN1ADR 0x3C
+#define OSD_BASEPX 0x40
+#define OSD_BASEPY 0x44
+#define OSD_VIDWIN0XP 0x48
+#define OSD_VIDWIN0YP 0x4C
+#define OSD_VIDWIN0XL 0x50
+#define OSD_VIDWIN0YL 0x54
+#define OSD_VIDWIN1XP 0x58
+#define OSD_VIDWIN1YP 0x5C
+#define OSD_VIDWIN1XL 0x60
+#define OSD_VIDWIN1YL 0x64
+#define OSD_OSDWIN0XP 0x68
+#define OSD_OSDWIN0YP 0x6C
+#define OSD_OSDWIN0XL 0x70
+#define OSD_OSDWIN0YL 0x74
+#define OSD_OSDWIN1XP 0x78
+#define OSD_OSDWIN1YP 0x7C
+#define OSD_OSDWIN1XL 0x80
+#define OSD_OSDWIN1YL 0x84
+#define OSD_CURXP 0x88
+#define OSD_CURYP 0x8C
+#define OSD_CURXL 0x90
+#define OSD_CURYL 0x94
+#define OSD_W0BMP01 0xA0
+#define OSD_W0BMP23 0xA4
+#define OSD_W0BMP45 0xA8
+#define OSD_W0BMP67 0xAC
+#define OSD_W0BMP89 0xB0
+#define OSD_W0BMPAB 0xB4
+#define OSD_W0BMPCD 0xB8
+#define OSD_W0BMPEF 0xBC
+#define OSD_W1BMP01 0xC0
+#define OSD_W1BMP23 0xC4
+#define OSD_W1BMP45 0xC8
+#define OSD_W1BMP67 0xCC
+#define OSD_W1BMP89 0xD0
+#define OSD_W1BMPAB 0xD4
+#define OSD_W1BMPCD 0xD8
+#define OSD_W1BMPEF 0xDC
+#define OSD_VBNDRY 0xE0
+#define OSD_EXTMODE 0xE4
+#define OSD_MISCCTL 0xE8
+#define OSD_CLUTRAMYCB 0xEC
+#define OSD_CLUTRAMCR 0xF0
+#define OSD_TRANSPVAL 0xF4
+#define OSD_TRANSPVALL 0xF4
+#define OSD_TRANSPVALU 0xF8
+#define OSD_TRANSPBMPIDX 0xFC
+#define OSD_PPVWIN0ADR 0xFC
+
+/* bit definitions */
+#define VPBE_PCR_VENC_DIV (1 << 1)
+#define VPBE_PCR_CLK_OFF (1 << 0)
+
+#define VPSSBL_INTSTAT_HSSIINT (1 << 14)
+#define VPSSBL_INTSTAT_CFALDINT (1 << 13)
+#define VPSSBL_INTSTAT_IPIPE_INT5 (1 << 12)
+#define VPSSBL_INTSTAT_IPIPE_INT4 (1 << 11)
+#define VPSSBL_INTSTAT_IPIPE_INT3 (1 << 10)
+#define VPSSBL_INTSTAT_IPIPE_INT2 (1 << 9)
+#define VPSSBL_INTSTAT_IPIPE_INT1 (1 << 8)
+#define VPSSBL_INTSTAT_IPIPE_INT0 (1 << 7)
+#define VPSSBL_INTSTAT_IPIPEIFINT (1 << 6)
+#define VPSSBL_INTSTAT_OSDINT (1 << 5)
+#define VPSSBL_INTSTAT_VENCINT (1 << 4)
+#define VPSSBL_INTSTAT_H3AINT (1 << 3)
+#define VPSSBL_INTSTAT_CCDC_VDINT2 (1 << 2)
+#define VPSSBL_INTSTAT_CCDC_VDINT1 (1 << 1)
+#define VPSSBL_INTSTAT_CCDC_VDINT0 (1 << 0)
+
+/* DM365 ISP5 bit definitions */
+#define ISP5_INTSTAT_VENCINT (1 << 21)
+#define ISP5_INTSTAT_OSDINT (1 << 20)
+
+/* VMOD TVTYP options for HDMD=0 */
+#define SDTV_NTSC 0
+#define SDTV_PAL 1
+/* VMOD TVTYP options for HDMD=1 */
+#define HDTV_525P 0
+#define HDTV_625P 1
+#define HDTV_1080I 2
+#define HDTV_720P 3
+
+#define OSD_MODE_CS (1 << 15)
+#define OSD_MODE_OVRSZ (1 << 14)
+#define OSD_MODE_OHRSZ (1 << 13)
+#define OSD_MODE_EF (1 << 12)
+#define OSD_MODE_VVRSZ (1 << 11)
+#define OSD_MODE_VHRSZ (1 << 10)
+#define OSD_MODE_FSINV (1 << 9)
+#define OSD_MODE_BCLUT (1 << 8)
+#define OSD_MODE_CABG_SHIFT 0
+#define OSD_MODE_CABG (0xff << 0)
+
+#define OSD_VIDWINMD_VFINV (1 << 15)
+#define OSD_VIDWINMD_V1EFC (1 << 14)
+#define OSD_VIDWINMD_VHZ1_SHIFT 12
+#define OSD_VIDWINMD_VHZ1 (3 << 12)
+#define OSD_VIDWINMD_VVZ1_SHIFT 10
+#define OSD_VIDWINMD_VVZ1 (3 << 10)
+#define OSD_VIDWINMD_VFF1 (1 << 9)
+#define OSD_VIDWINMD_ACT1 (1 << 8)
+#define OSD_VIDWINMD_V0EFC (1 << 6)
+#define OSD_VIDWINMD_VHZ0_SHIFT 4
+#define OSD_VIDWINMD_VHZ0 (3 << 4)
+#define OSD_VIDWINMD_VVZ0_SHIFT 2
+#define OSD_VIDWINMD_VVZ0 (3 << 2)
+#define OSD_VIDWINMD_VFF0 (1 << 1)
+#define OSD_VIDWINMD_ACT0 (1 << 0)
+
+#define OSD_OSDWIN0MD_ATN0E (1 << 14)
+#define OSD_OSDWIN0MD_RGB0E (1 << 13)
+#define OSD_OSDWIN0MD_BMP0MD_SHIFT 13
+#define OSD_OSDWIN0MD_BMP0MD (3 << 13)
+#define OSD_OSDWIN0MD_CLUTS0 (1 << 12)
+#define OSD_OSDWIN0MD_OHZ0_SHIFT 10
+#define OSD_OSDWIN0MD_OHZ0 (3 << 10)
+#define OSD_OSDWIN0MD_OVZ0_SHIFT 8
+#define OSD_OSDWIN0MD_OVZ0 (3 << 8)
+#define OSD_OSDWIN0MD_BMW0_SHIFT 6
+#define OSD_OSDWIN0MD_BMW0 (3 << 6)
+#define OSD_OSDWIN0MD_BLND0_SHIFT 3
+#define OSD_OSDWIN0MD_BLND0 (7 << 3)
+#define OSD_OSDWIN0MD_TE0 (1 << 2)
+#define OSD_OSDWIN0MD_OFF0 (1 << 1)
+#define OSD_OSDWIN0MD_OACT0 (1 << 0)
+
+#define OSD_OSDWIN1MD_OASW (1 << 15)
+#define OSD_OSDWIN1MD_ATN1E (1 << 14)
+#define OSD_OSDWIN1MD_RGB1E (1 << 13)
+#define OSD_OSDWIN1MD_BMP1MD_SHIFT 13
+#define OSD_OSDWIN1MD_BMP1MD (3 << 13)
+#define OSD_OSDWIN1MD_CLUTS1 (1 << 12)
+#define OSD_OSDWIN1MD_OHZ1_SHIFT 10
+#define OSD_OSDWIN1MD_OHZ1 (3 << 10)
+#define OSD_OSDWIN1MD_OVZ1_SHIFT 8
+#define OSD_OSDWIN1MD_OVZ1 (3 << 8)
+#define OSD_OSDWIN1MD_BMW1_SHIFT 6
+#define OSD_OSDWIN1MD_BMW1 (3 << 6)
+#define OSD_OSDWIN1MD_BLND1_SHIFT 3
+#define OSD_OSDWIN1MD_BLND1 (7 << 3)
+#define OSD_OSDWIN1MD_TE1 (1 << 2)
+#define OSD_OSDWIN1MD_OFF1 (1 << 1)
+#define OSD_OSDWIN1MD_OACT1 (1 << 0)
+
+#define OSD_OSDATRMD_OASW (1 << 15)
+#define OSD_OSDATRMD_OHZA_SHIFT 10
+#define OSD_OSDATRMD_OHZA (3 << 10)
+#define OSD_OSDATRMD_OVZA_SHIFT 8
+#define OSD_OSDATRMD_OVZA (3 << 8)
+#define OSD_OSDATRMD_BLNKINT_SHIFT 6
+#define OSD_OSDATRMD_BLNKINT (3 << 6)
+#define OSD_OSDATRMD_OFFA (1 << 1)
+#define OSD_OSDATRMD_BLNK (1 << 0)
+
+#define OSD_RECTCUR_RCAD_SHIFT 8
+#define OSD_RECTCUR_RCAD (0xff << 8)
+#define OSD_RECTCUR_CLUTSR (1 << 7)
+#define OSD_RECTCUR_RCHW_SHIFT 4
+#define OSD_RECTCUR_RCHW (7 << 4)
+#define OSD_RECTCUR_RCVW_SHIFT 1
+#define OSD_RECTCUR_RCVW (7 << 1)
+#define OSD_RECTCUR_RCACT (1 << 0)
+
+#define OSD_VIDWIN0OFST_V0LO (0x1ff << 0)
+
+#define OSD_VIDWIN1OFST_V1LO (0x1ff << 0)
+
+#define OSD_OSDWIN0OFST_O0LO (0x1ff << 0)
+
+#define OSD_OSDWIN1OFST_O1LO (0x1ff << 0)
+
+#define OSD_WINOFST_AH_SHIFT 9
+
+#define OSD_VIDWIN0OFST_V0AH (0xf << 9)
+#define OSD_VIDWIN1OFST_V1AH (0xf << 9)
+#define OSD_OSDWIN0OFST_O0AH (0xf << 9)
+#define OSD_OSDWIN1OFST_O1AH (0xf << 9)
+
+#define OSD_VIDWINADH_V1AH_SHIFT 8
+#define OSD_VIDWINADH_V1AH (0x7f << 8)
+#define OSD_VIDWINADH_V0AH_SHIFT 0
+#define OSD_VIDWINADH_V0AH (0x7f << 0)
+
+#define OSD_VIDWIN0ADL_V0AL (0xffff << 0)
+
+#define OSD_VIDWIN1ADL_V1AL (0xffff << 0)
+
+#define OSD_OSDWINADH_O1AH_SHIFT 8
+#define OSD_OSDWINADH_O1AH (0x7f << 8)
+#define OSD_OSDWINADH_O0AH_SHIFT 0
+#define OSD_OSDWINADH_O0AH (0x7f << 0)
+
+#define OSD_OSDWIN0ADL_O0AL (0xffff << 0)
+
+#define OSD_OSDWIN1ADL_O1AL (0xffff << 0)
+
+#define OSD_BASEPX_BPX (0x3ff << 0)
+
+#define OSD_BASEPY_BPY (0x1ff << 0)
+
+#define OSD_VIDWIN0XP_V0X (0x7ff << 0)
+
+#define OSD_VIDWIN0YP_V0Y (0x7ff << 0)
+
+#define OSD_VIDWIN0XL_V0W (0x7ff << 0)
+
+#define OSD_VIDWIN0YL_V0H (0x7ff << 0)
+
+#define OSD_VIDWIN1XP_V1X (0x7ff << 0)
+
+#define OSD_VIDWIN1YP_V1Y (0x7ff << 0)
+
+#define OSD_VIDWIN1XL_V1W (0x7ff << 0)
+
+#define OSD_VIDWIN1YL_V1H (0x7ff << 0)
+
+#define OSD_OSDWIN0XP_W0X (0x7ff << 0)
+
+#define OSD_OSDWIN0YP_W0Y (0x7ff << 0)
+
+#define OSD_OSDWIN0XL_W0W (0x7ff << 0)
+
+#define OSD_OSDWIN0YL_W0H (0x7ff << 0)
+
+#define OSD_OSDWIN1XP_W1X (0x7ff << 0)
+
+#define OSD_OSDWIN1YP_W1Y (0x7ff << 0)
+
+#define OSD_OSDWIN1XL_W1W (0x7ff << 0)
+
+#define OSD_OSDWIN1YL_W1H (0x7ff << 0)
+
+#define OSD_CURXP_RCSX (0x7ff << 0)
+
+#define OSD_CURYP_RCSY (0x7ff << 0)
+
+#define OSD_CURXL_RCSW (0x7ff << 0)
+
+#define OSD_CURYL_RCSH (0x7ff << 0)
+
+#define OSD_EXTMODE_EXPMDSEL (1 << 15)
+#define OSD_EXTMODE_SCRNHEXP_SHIFT 13
+#define OSD_EXTMODE_SCRNHEXP (3 << 13)
+#define OSD_EXTMODE_SCRNVEXP (1 << 12)
+#define OSD_EXTMODE_OSD1BLDCHR (1 << 11)
+#define OSD_EXTMODE_OSD0BLDCHR (1 << 10)
+#define OSD_EXTMODE_ATNOSD1EN (1 << 9)
+#define OSD_EXTMODE_ATNOSD0EN (1 << 8)
+#define OSD_EXTMODE_OSDHRSZ15 (1 << 7)
+#define OSD_EXTMODE_VIDHRSZ15 (1 << 6)
+#define OSD_EXTMODE_ZMFILV1HEN (1 << 5)
+#define OSD_EXTMODE_ZMFILV1VEN (1 << 4)
+#define OSD_EXTMODE_ZMFILV0HEN (1 << 3)
+#define OSD_EXTMODE_ZMFILV0VEN (1 << 2)
+#define OSD_EXTMODE_EXPFILHEN (1 << 1)
+#define OSD_EXTMODE_EXPFILVEN (1 << 0)
+
+#define OSD_MISCCTL_BLDSEL (1 << 15)
+#define OSD_MISCCTL_S420D (1 << 14)
+#define OSD_MISCCTL_BMAPT (1 << 13)
+#define OSD_MISCCTL_DM365M (1 << 12)
+#define OSD_MISCCTL_RGBEN (1 << 7)
+#define OSD_MISCCTL_RGBWIN (1 << 6)
+#define OSD_MISCCTL_DMANG (1 << 6)
+#define OSD_MISCCTL_TMON (1 << 5)
+#define OSD_MISCCTL_RSEL (1 << 4)
+#define OSD_MISCCTL_CPBSY (1 << 3)
+#define OSD_MISCCTL_PPSW (1 << 2)
+#define OSD_MISCCTL_PPRV (1 << 1)
+
+#define OSD_CLUTRAMYCB_Y_SHIFT 8
+#define OSD_CLUTRAMYCB_Y (0xff << 8)
+#define OSD_CLUTRAMYCB_CB_SHIFT 0
+#define OSD_CLUTRAMYCB_CB (0xff << 0)
+
+#define OSD_CLUTRAMCR_CR_SHIFT 8
+#define OSD_CLUTRAMCR_CR (0xff << 8)
+#define OSD_CLUTRAMCR_CADDR_SHIFT 0
+#define OSD_CLUTRAMCR_CADDR (0xff << 0)
+
+#define OSD_TRANSPVAL_RGBTRANS (0xffff << 0)
+
+#define OSD_TRANSPVALL_RGBL (0xffff << 0)
+
+#define OSD_TRANSPVALU_Y_SHIFT 8
+#define OSD_TRANSPVALU_Y (0xff << 8)
+#define OSD_TRANSPVALU_RGBU_SHIFT 0
+#define OSD_TRANSPVALU_RGBU (0xff << 0)
+
+#define OSD_TRANSPBMPIDX_BMP1_SHIFT 8
+#define OSD_TRANSPBMPIDX_BMP1 (0xff << 8)
+#define OSD_TRANSPBMPIDX_BMP0_SHIFT 0
+#define OSD_TRANSPBMPIDX_BMP0 0xff
+
+#endif /* _DAVINCI_VPBE_H_ */
diff --git a/drivers/media/video/davinci/vpbe_venc.c b/drivers/media/video/davinci/vpbe_venc.c
new file mode 100644
index 00000000..b21ecc8d
--- /dev/null
+++ b/drivers/media/video/davinci/vpbe_venc.c
@@ -0,0 +1,706 @@
+/*
+ * Copyright (C) 2010 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+
+#include <mach/hardware.h>
+#include <mach/mux.h>
+#include <mach/i2c.h>
+
+#include <linux/io.h>
+
+#include <media/davinci/vpbe_types.h>
+#include <media/davinci/vpbe_venc.h>
+#include <media/davinci/vpss.h>
+#include <media/v4l2-device.h>
+
+#include "vpbe_venc_regs.h"
+
+#define MODULE_NAME VPBE_VENC_SUBDEV_NAME
+
+static int debug = 2;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level 0-2");
+
+struct venc_state {
+ struct v4l2_subdev sd;
+ struct venc_callback *callback;
+ struct venc_platform_data *pdata;
+ struct device *pdev;
+ u32 output;
+ v4l2_std_id std;
+ spinlock_t lock;
+ void __iomem *venc_base;
+ void __iomem *vdaccfg_reg;
+};
+
+static inline struct venc_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct venc_state, sd);
+}
+
+static inline u32 venc_read(struct v4l2_subdev *sd, u32 offset)
+{
+ struct venc_state *venc = to_state(sd);
+
+ return readl(venc->venc_base + offset);
+}
+
+static inline u32 venc_write(struct v4l2_subdev *sd, u32 offset, u32 val)
+{
+ struct venc_state *venc = to_state(sd);
+
+ writel(val, (venc->venc_base + offset));
+
+ return val;
+}
+
+static inline u32 venc_modify(struct v4l2_subdev *sd, u32 offset,
+ u32 val, u32 mask)
+{
+ u32 new_val = (venc_read(sd, offset) & ~mask) | (val & mask);
+
+ venc_write(sd, offset, new_val);
+
+ return new_val;
+}
+
+static inline u32 vdaccfg_write(struct v4l2_subdev *sd, u32 val)
+{
+ struct venc_state *venc = to_state(sd);
+
+ writel(val, venc->vdaccfg_reg);
+
+ val = readl(venc->vdaccfg_reg);
+
+ return val;
+}
+
+#define VDAC_COMPONENT 0x543
+#define VDAC_S_VIDEO 0x210
+/* This function sets the dac of the VPBE for various outputs
+ */
+static int venc_set_dac(struct v4l2_subdev *sd, u32 out_index)
+{
+ switch (out_index) {
+ case 0:
+ v4l2_dbg(debug, 1, sd, "Setting output to Composite\n");
+ venc_write(sd, VENC_DACSEL, 0);
+ break;
+ case 1:
+ v4l2_dbg(debug, 1, sd, "Setting output to Component\n");
+ venc_write(sd, VENC_DACSEL, VDAC_COMPONENT);
+ break;
+ case 2:
+ v4l2_dbg(debug, 1, sd, "Setting output to S-video\n");
+ venc_write(sd, VENC_DACSEL, VDAC_S_VIDEO);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void venc_enabledigitaloutput(struct v4l2_subdev *sd, int benable)
+{
+ struct venc_state *venc = to_state(sd);
+ struct venc_platform_data *pdata = venc->pdata;
+ v4l2_dbg(debug, 2, sd, "venc_enabledigitaloutput\n");
+
+ if (benable) {
+ venc_write(sd, VENC_VMOD, 0);
+ venc_write(sd, VENC_CVBS, 0);
+ venc_write(sd, VENC_LCDOUT, 0);
+ venc_write(sd, VENC_HSPLS, 0);
+ venc_write(sd, VENC_HSTART, 0);
+ venc_write(sd, VENC_HVALID, 0);
+ venc_write(sd, VENC_HINT, 0);
+ venc_write(sd, VENC_VSPLS, 0);
+ venc_write(sd, VENC_VSTART, 0);
+ venc_write(sd, VENC_VVALID, 0);
+ venc_write(sd, VENC_VINT, 0);
+ venc_write(sd, VENC_YCCCTL, 0);
+ venc_write(sd, VENC_DACSEL, 0);
+
+ } else {
+ venc_write(sd, VENC_VMOD, 0);
+ /* disable VCLK output pin enable */
+ venc_write(sd, VENC_VIDCTL, 0x141);
+
+ /* Disable output sync pins */
+ venc_write(sd, VENC_SYNCCTL, 0);
+
+ /* Disable DCLOCK */
+ venc_write(sd, VENC_DCLKCTL, 0);
+ venc_write(sd, VENC_DRGBX1, 0x0000057C);
+
+ /* Disable LCD output control (accepting default polarity) */
+ venc_write(sd, VENC_LCDOUT, 0);
+ if (pdata->venc_type != VPBE_VERSION_3)
+ venc_write(sd, VENC_CMPNT, 0x100);
+ venc_write(sd, VENC_HSPLS, 0);
+ venc_write(sd, VENC_HINT, 0);
+ venc_write(sd, VENC_HSTART, 0);
+ venc_write(sd, VENC_HVALID, 0);
+
+ venc_write(sd, VENC_VSPLS, 0);
+ venc_write(sd, VENC_VINT, 0);
+ venc_write(sd, VENC_VSTART, 0);
+ venc_write(sd, VENC_VVALID, 0);
+
+ venc_write(sd, VENC_HSDLY, 0);
+ venc_write(sd, VENC_VSDLY, 0);
+
+ venc_write(sd, VENC_YCCCTL, 0);
+ venc_write(sd, VENC_VSTARTA, 0);
+
+ /* Set OSD clock and OSD Sync Adavance registers */
+ venc_write(sd, VENC_OSDCLK0, 1);
+ venc_write(sd, VENC_OSDCLK1, 2);
+ }
+}
+
+#define VDAC_CONFIG_SD_V3 0x0E21A6B6
+#define VDAC_CONFIG_SD_V2 0x081141CF
+/*
+ * setting NTSC mode
+ */
+static int venc_set_ntsc(struct v4l2_subdev *sd)
+{
+ u32 val;
+ struct venc_state *venc = to_state(sd);
+ struct venc_platform_data *pdata = venc->pdata;
+
+ v4l2_dbg(debug, 2, sd, "venc_set_ntsc\n");
+
+ /* Setup clock at VPSS & VENC for SD */
+ vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 1);
+ if (pdata->setup_clock(VPBE_ENC_STD, V4L2_STD_525_60) < 0)
+ return -EINVAL;
+
+ venc_enabledigitaloutput(sd, 0);
+
+ if (pdata->venc_type == VPBE_VERSION_3) {
+ venc_write(sd, VENC_CLKCTL, 0x01);
+ venc_write(sd, VENC_VIDCTL, 0);
+ val = vdaccfg_write(sd, VDAC_CONFIG_SD_V3);
+ } else if (pdata->venc_type == VPBE_VERSION_2) {
+ venc_write(sd, VENC_CLKCTL, 0x01);
+ venc_write(sd, VENC_VIDCTL, 0);
+ vdaccfg_write(sd, VDAC_CONFIG_SD_V2);
+ } else {
+ /* to set VENC CLK DIV to 1 - final clock is 54 MHz */
+ venc_modify(sd, VENC_VIDCTL, 0, 1 << 1);
+ /* Set REC656 Mode */
+ venc_write(sd, VENC_YCCCTL, 0x1);
+ venc_modify(sd, VENC_VDPRO, 0, VENC_VDPRO_DAFRQ);
+ venc_modify(sd, VENC_VDPRO, 0, VENC_VDPRO_DAUPS);
+ }
+
+ venc_write(sd, VENC_VMOD, 0);
+ venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT),
+ VENC_VMOD_VIE);
+ venc_modify(sd, VENC_VMOD, (0 << VENC_VMOD_VMD), VENC_VMOD_VMD);
+ venc_modify(sd, VENC_VMOD, (0 << VENC_VMOD_TVTYP_SHIFT),
+ VENC_VMOD_TVTYP);
+ venc_write(sd, VENC_DACTST, 0x0);
+ venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
+
+ return 0;
+}
+
+/*
+ * setting PAL mode
+ */
+static int venc_set_pal(struct v4l2_subdev *sd)
+{
+ struct venc_state *venc = to_state(sd);
+ struct venc_platform_data *pdata = venc->pdata;
+
+ v4l2_dbg(debug, 2, sd, "venc_set_pal\n");
+
+ /* Setup clock at VPSS & VENC for SD */
+ vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 1);
+ if (venc->pdata->setup_clock(VPBE_ENC_STD, V4L2_STD_625_50) < 0)
+ return -EINVAL;
+
+ venc_enabledigitaloutput(sd, 0);
+
+ if (pdata->venc_type == VPBE_VERSION_3) {
+ venc_write(sd, VENC_CLKCTL, 0x1);
+ venc_write(sd, VENC_VIDCTL, 0);
+ vdaccfg_write(sd, VDAC_CONFIG_SD_V3);
+ } else if (pdata->venc_type == VPBE_VERSION_2) {
+ venc_write(sd, VENC_CLKCTL, 0x1);
+ venc_write(sd, VENC_VIDCTL, 0);
+ vdaccfg_write(sd, VDAC_CONFIG_SD_V2);
+ } else {
+ /* to set VENC CLK DIV to 1 - final clock is 54 MHz */
+ venc_modify(sd, VENC_VIDCTL, 0, 1 << 1);
+ /* Set REC656 Mode */
+ venc_write(sd, VENC_YCCCTL, 0x1);
+ }
+
+ venc_modify(sd, VENC_SYNCCTL, 1 << VENC_SYNCCTL_OVD_SHIFT,
+ VENC_SYNCCTL_OVD);
+ venc_write(sd, VENC_VMOD, 0);
+ venc_modify(sd, VENC_VMOD,
+ (1 << VENC_VMOD_VIE_SHIFT),
+ VENC_VMOD_VIE);
+ venc_modify(sd, VENC_VMOD,
+ (0 << VENC_VMOD_VMD), VENC_VMOD_VMD);
+ venc_modify(sd, VENC_VMOD,
+ (1 << VENC_VMOD_TVTYP_SHIFT),
+ VENC_VMOD_TVTYP);
+ venc_write(sd, VENC_DACTST, 0x0);
+ venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
+
+ return 0;
+}
+
+#define VDAC_CONFIG_HD_V2 0x081141EF
+/*
+ * venc_set_480p59_94
+ *
+ * This function configures the video encoder to EDTV(525p) component setting.
+ */
+static int venc_set_480p59_94(struct v4l2_subdev *sd)
+{
+ struct venc_state *venc = to_state(sd);
+ struct venc_platform_data *pdata = venc->pdata;
+
+ v4l2_dbg(debug, 2, sd, "venc_set_480p59_94\n");
+ if ((pdata->venc_type != VPBE_VERSION_1) &&
+ (pdata->venc_type != VPBE_VERSION_2))
+ return -EINVAL;
+
+ /* Setup clock at VPSS & VENC for SD */
+ if (pdata->setup_clock(VPBE_ENC_DV_PRESET, V4L2_DV_480P59_94) < 0)
+ return -EINVAL;
+
+ venc_enabledigitaloutput(sd, 0);
+
+ if (pdata->venc_type == VPBE_VERSION_2)
+ vdaccfg_write(sd, VDAC_CONFIG_HD_V2);
+ venc_write(sd, VENC_OSDCLK0, 0);
+ venc_write(sd, VENC_OSDCLK1, 1);
+
+ if (pdata->venc_type == VPBE_VERSION_1) {
+ venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ,
+ VENC_VDPRO_DAFRQ);
+ venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS,
+ VENC_VDPRO_DAUPS);
+ }
+
+ venc_write(sd, VENC_VMOD, 0);
+ venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT),
+ VENC_VMOD_VIE);
+ venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD);
+ venc_modify(sd, VENC_VMOD, (HDTV_525P << VENC_VMOD_TVTYP_SHIFT),
+ VENC_VMOD_TVTYP);
+ venc_modify(sd, VENC_VMOD, VENC_VMOD_VDMD_YCBCR8 <<
+ VENC_VMOD_VDMD_SHIFT, VENC_VMOD_VDMD);
+
+ venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
+
+ return 0;
+}
+
+/*
+ * venc_set_625p
+ *
+ * This function configures the video encoder to HDTV(625p) component setting
+ */
+static int venc_set_576p50(struct v4l2_subdev *sd)
+{
+ struct venc_state *venc = to_state(sd);
+ struct venc_platform_data *pdata = venc->pdata;
+
+ v4l2_dbg(debug, 2, sd, "venc_set_576p50\n");
+
+ if ((pdata->venc_type != VPBE_VERSION_1) &&
+ (pdata->venc_type != VPBE_VERSION_2))
+ return -EINVAL;
+ /* Setup clock at VPSS & VENC for SD */
+ if (pdata->setup_clock(VPBE_ENC_DV_PRESET, V4L2_DV_576P50) < 0)
+ return -EINVAL;
+
+ venc_enabledigitaloutput(sd, 0);
+
+ if (pdata->venc_type == VPBE_VERSION_2)
+ vdaccfg_write(sd, VDAC_CONFIG_HD_V2);
+
+ venc_write(sd, VENC_OSDCLK0, 0);
+ venc_write(sd, VENC_OSDCLK1, 1);
+
+ if (pdata->venc_type == VPBE_VERSION_1) {
+ venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ,
+ VENC_VDPRO_DAFRQ);
+ venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS,
+ VENC_VDPRO_DAUPS);
+ }
+
+ venc_write(sd, VENC_VMOD, 0);
+ venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT),
+ VENC_VMOD_VIE);
+ venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD);
+ venc_modify(sd, VENC_VMOD, (HDTV_625P << VENC_VMOD_TVTYP_SHIFT),
+ VENC_VMOD_TVTYP);
+
+ venc_modify(sd, VENC_VMOD, VENC_VMOD_VDMD_YCBCR8 <<
+ VENC_VMOD_VDMD_SHIFT, VENC_VMOD_VDMD);
+ venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
+
+ return 0;
+}
+
+/*
+ * venc_set_720p60_internal - Setup 720p60 in venc for dm365 only
+ */
+static int venc_set_720p60_internal(struct v4l2_subdev *sd)
+{
+ struct venc_state *venc = to_state(sd);
+ struct venc_platform_data *pdata = venc->pdata;
+
+ if (pdata->setup_clock(VPBE_ENC_DV_PRESET, V4L2_DV_720P60) < 0)
+ return -EINVAL;
+
+ venc_enabledigitaloutput(sd, 0);
+
+ venc_write(sd, VENC_OSDCLK0, 0);
+ venc_write(sd, VENC_OSDCLK1, 1);
+
+ venc_write(sd, VENC_VMOD, 0);
+ /* DM365 component HD mode */
+ venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT),
+ VENC_VMOD_VIE);
+ venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD);
+ venc_modify(sd, VENC_VMOD, (HDTV_720P << VENC_VMOD_TVTYP_SHIFT),
+ VENC_VMOD_TVTYP);
+ venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
+ venc_write(sd, VENC_XHINTVL, 0);
+ return 0;
+}
+
+/*
+ * venc_set_1080i30_internal - Setup 1080i30 in venc for dm365 only
+ */
+static int venc_set_1080i30_internal(struct v4l2_subdev *sd)
+{
+ struct venc_state *venc = to_state(sd);
+ struct venc_platform_data *pdata = venc->pdata;
+
+ if (pdata->setup_clock(VPBE_ENC_DV_PRESET, V4L2_DV_1080P30) < 0)
+ return -EINVAL;
+
+ venc_enabledigitaloutput(sd, 0);
+
+ venc_write(sd, VENC_OSDCLK0, 0);
+ venc_write(sd, VENC_OSDCLK1, 1);
+
+
+ venc_write(sd, VENC_VMOD, 0);
+ /* DM365 component HD mode */
+ venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT),
+ VENC_VMOD_VIE);
+ venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD);
+ venc_modify(sd, VENC_VMOD, (HDTV_1080I << VENC_VMOD_TVTYP_SHIFT),
+ VENC_VMOD_TVTYP);
+ venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
+ venc_write(sd, VENC_XHINTVL, 0);
+ return 0;
+}
+
+static int venc_s_std_output(struct v4l2_subdev *sd, v4l2_std_id norm)
+{
+ v4l2_dbg(debug, 1, sd, "venc_s_std_output\n");
+
+ if (norm & V4L2_STD_525_60)
+ return venc_set_ntsc(sd);
+ else if (norm & V4L2_STD_625_50)
+ return venc_set_pal(sd);
+
+ return -EINVAL;
+}
+
+static int venc_s_dv_preset(struct v4l2_subdev *sd,
+ struct v4l2_dv_preset *dv_preset)
+{
+ struct venc_state *venc = to_state(sd);
+ int ret;
+
+ v4l2_dbg(debug, 1, sd, "venc_s_dv_preset\n");
+
+ if (dv_preset->preset == V4L2_DV_576P50)
+ return venc_set_576p50(sd);
+ else if (dv_preset->preset == V4L2_DV_480P59_94)
+ return venc_set_480p59_94(sd);
+ else if ((dv_preset->preset == V4L2_DV_720P60) &&
+ (venc->pdata->venc_type == VPBE_VERSION_2)) {
+ /* TBD setup internal 720p mode here */
+ ret = venc_set_720p60_internal(sd);
+ /* for DM365 VPBE, there is DAC inside */
+ vdaccfg_write(sd, VDAC_CONFIG_HD_V2);
+ return ret;
+ } else if ((dv_preset->preset == V4L2_DV_1080I30) &&
+ (venc->pdata->venc_type == VPBE_VERSION_2)) {
+ /* TBD setup internal 1080i mode here */
+ ret = venc_set_1080i30_internal(sd);
+ /* for DM365 VPBE, there is DAC inside */
+ vdaccfg_write(sd, VDAC_CONFIG_HD_V2);
+ return ret;
+ }
+ return -EINVAL;
+}
+
+static int venc_s_routing(struct v4l2_subdev *sd, u32 input, u32 output,
+ u32 config)
+{
+ struct venc_state *venc = to_state(sd);
+ int ret;
+
+ v4l2_dbg(debug, 1, sd, "venc_s_routing\n");
+
+ ret = venc_set_dac(sd, output);
+ if (!ret)
+ venc->output = output;
+
+ return ret;
+}
+
+static long venc_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd,
+ void *arg)
+{
+ u32 val;
+
+ switch (cmd) {
+ case VENC_GET_FLD:
+ val = venc_read(sd, VENC_VSTAT);
+ *((int *)arg) = ((val & VENC_VSTAT_FIDST) ==
+ VENC_VSTAT_FIDST);
+ break;
+ default:
+ v4l2_err(sd, "Wrong IOCTL cmd\n");
+ break;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_subdev_core_ops venc_core_ops = {
+ .ioctl = venc_ioctl,
+};
+
+static const struct v4l2_subdev_video_ops venc_video_ops = {
+ .s_routing = venc_s_routing,
+ .s_std_output = venc_s_std_output,
+ .s_dv_preset = venc_s_dv_preset,
+};
+
+static const struct v4l2_subdev_ops venc_ops = {
+ .core = &venc_core_ops,
+ .video = &venc_video_ops,
+};
+
+static int venc_initialize(struct v4l2_subdev *sd)
+{
+ struct venc_state *venc = to_state(sd);
+ int ret;
+
+ /* Set default to output to composite and std to NTSC */
+ venc->output = 0;
+ venc->std = V4L2_STD_525_60;
+
+ ret = venc_s_routing(sd, 0, venc->output, 0);
+ if (ret < 0) {
+ v4l2_err(sd, "Error setting output during init\n");
+ return -EINVAL;
+ }
+
+ ret = venc_s_std_output(sd, venc->std);
+ if (ret < 0) {
+ v4l2_err(sd, "Error setting std during init\n");
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int venc_device_get(struct device *dev, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct venc_state **venc = data;
+
+ if (strcmp(MODULE_NAME, pdev->name) == 0)
+ *venc = platform_get_drvdata(pdev);
+
+ return 0;
+}
+
+struct v4l2_subdev *venc_sub_dev_init(struct v4l2_device *v4l2_dev,
+ const char *venc_name)
+{
+ struct venc_state *venc;
+ int err;
+
+ err = bus_for_each_dev(&platform_bus_type, NULL, &venc,
+ venc_device_get);
+ if (venc == NULL)
+ return NULL;
+
+ v4l2_subdev_init(&venc->sd, &venc_ops);
+
+ strcpy(venc->sd.name, venc_name);
+ if (v4l2_device_register_subdev(v4l2_dev, &venc->sd) < 0) {
+ v4l2_err(v4l2_dev,
+ "vpbe unable to register venc sub device\n");
+ return NULL;
+ }
+ if (venc_initialize(&venc->sd)) {
+ v4l2_err(v4l2_dev,
+ "vpbe venc initialization failed\n");
+ return NULL;
+ }
+
+ return &venc->sd;
+}
+EXPORT_SYMBOL(venc_sub_dev_init);
+
+static int venc_probe(struct platform_device *pdev)
+{
+ struct venc_state *venc;
+ struct resource *res;
+ int ret;
+
+ venc = kzalloc(sizeof(struct venc_state), GFP_KERNEL);
+ if (venc == NULL)
+ return -ENOMEM;
+
+ venc->pdev = &pdev->dev;
+ venc->pdata = pdev->dev.platform_data;
+ if (NULL == venc->pdata) {
+ dev_err(venc->pdev, "Unable to get platform data for"
+ " VENC sub device");
+ ret = -ENOENT;
+ goto free_mem;
+ }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(venc->pdev,
+ "Unable to get VENC register address map\n");
+ ret = -ENODEV;
+ goto free_mem;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res), "venc")) {
+ dev_err(venc->pdev, "Unable to reserve VENC MMIO region\n");
+ ret = -ENODEV;
+ goto free_mem;
+ }
+
+ venc->venc_base = ioremap_nocache(res->start, resource_size(res));
+ if (!venc->venc_base) {
+ dev_err(venc->pdev, "Unable to map VENC IO space\n");
+ ret = -ENODEV;
+ goto release_venc_mem_region;
+ }
+
+ if (venc->pdata->venc_type != VPBE_VERSION_1) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res) {
+ dev_err(venc->pdev,
+ "Unable to get VDAC_CONFIG address map\n");
+ ret = -ENODEV;
+ goto unmap_venc_io;
+ }
+
+ if (!request_mem_region(res->start,
+ resource_size(res), "venc")) {
+ dev_err(venc->pdev,
+ "Unable to reserve VDAC_CONFIG MMIO region\n");
+ ret = -ENODEV;
+ goto unmap_venc_io;
+ }
+
+ venc->vdaccfg_reg = ioremap_nocache(res->start,
+ resource_size(res));
+ if (!venc->vdaccfg_reg) {
+ dev_err(venc->pdev,
+ "Unable to map VDAC_CONFIG IO space\n");
+ ret = -ENODEV;
+ goto release_vdaccfg_mem_region;
+ }
+ }
+ spin_lock_init(&venc->lock);
+ platform_set_drvdata(pdev, venc);
+ dev_notice(venc->pdev, "VENC sub device probe success\n");
+ return 0;
+
+release_vdaccfg_mem_region:
+ release_mem_region(res->start, resource_size(res));
+unmap_venc_io:
+ iounmap(venc->venc_base);
+release_venc_mem_region:
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, resource_size(res));
+free_mem:
+ kfree(venc);
+ return ret;
+}
+
+static int venc_remove(struct platform_device *pdev)
+{
+ struct venc_state *venc = platform_get_drvdata(pdev);
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ iounmap((void *)venc->venc_base);
+ release_mem_region(res->start, resource_size(res));
+ if (venc->pdata->venc_type != VPBE_VERSION_1) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ iounmap((void *)venc->vdaccfg_reg);
+ release_mem_region(res->start, resource_size(res));
+ }
+ kfree(venc);
+
+ return 0;
+}
+
+static struct platform_driver venc_driver = {
+ .probe = venc_probe,
+ .remove = venc_remove,
+ .driver = {
+ .name = MODULE_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(venc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("VPBE VENC Driver");
+MODULE_AUTHOR("Texas Instruments");
diff --git a/drivers/media/video/davinci/vpbe_venc_regs.h b/drivers/media/video/davinci/vpbe_venc_regs.h
new file mode 100644
index 00000000..947cb151
--- /dev/null
+++ b/drivers/media/video/davinci/vpbe_venc_regs.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2006-2010 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2..
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _VPBE_VENC_REGS_H
+#define _VPBE_VENC_REGS_H
+
+/* VPBE Video Encoder / Digital LCD Subsystem Registers (VENC) */
+#define VENC_VMOD 0x00
+#define VENC_VIDCTL 0x04
+#define VENC_VDPRO 0x08
+#define VENC_SYNCCTL 0x0C
+#define VENC_HSPLS 0x10
+#define VENC_VSPLS 0x14
+#define VENC_HINT 0x18
+#define VENC_HSTART 0x1C
+#define VENC_HVALID 0x20
+#define VENC_VINT 0x24
+#define VENC_VSTART 0x28
+#define VENC_VVALID 0x2C
+#define VENC_HSDLY 0x30
+#define VENC_VSDLY 0x34
+#define VENC_YCCCTL 0x38
+#define VENC_RGBCTL 0x3C
+#define VENC_RGBCLP 0x40
+#define VENC_LINECTL 0x44
+#define VENC_CULLLINE 0x48
+#define VENC_LCDOUT 0x4C
+#define VENC_BRTS 0x50
+#define VENC_BRTW 0x54
+#define VENC_ACCTL 0x58
+#define VENC_PWMP 0x5C
+#define VENC_PWMW 0x60
+#define VENC_DCLKCTL 0x64
+#define VENC_DCLKPTN0 0x68
+#define VENC_DCLKPTN1 0x6C
+#define VENC_DCLKPTN2 0x70
+#define VENC_DCLKPTN3 0x74
+#define VENC_DCLKPTN0A 0x78
+#define VENC_DCLKPTN1A 0x7C
+#define VENC_DCLKPTN2A 0x80
+#define VENC_DCLKPTN3A 0x84
+#define VENC_DCLKHS 0x88
+#define VENC_DCLKHSA 0x8C
+#define VENC_DCLKHR 0x90
+#define VENC_DCLKVS 0x94
+#define VENC_DCLKVR 0x98
+#define VENC_CAPCTL 0x9C
+#define VENC_CAPDO 0xA0
+#define VENC_CAPDE 0xA4
+#define VENC_ATR0 0xA8
+#define VENC_ATR1 0xAC
+#define VENC_ATR2 0xB0
+#define VENC_VSTAT 0xB8
+#define VENC_RAMADR 0xBC
+#define VENC_RAMPORT 0xC0
+#define VENC_DACTST 0xC4
+#define VENC_YCOLVL 0xC8
+#define VENC_SCPROG 0xCC
+#define VENC_CVBS 0xDC
+#define VENC_CMPNT 0xE0
+#define VENC_ETMG0 0xE4
+#define VENC_ETMG1 0xE8
+#define VENC_ETMG2 0xEC
+#define VENC_ETMG3 0xF0
+#define VENC_DACSEL 0xF4
+#define VENC_ARGBX0 0x100
+#define VENC_ARGBX1 0x104
+#define VENC_ARGBX2 0x108
+#define VENC_ARGBX3 0x10C
+#define VENC_ARGBX4 0x110
+#define VENC_DRGBX0 0x114
+#define VENC_DRGBX1 0x118
+#define VENC_DRGBX2 0x11C
+#define VENC_DRGBX3 0x120
+#define VENC_DRGBX4 0x124
+#define VENC_VSTARTA 0x128
+#define VENC_OSDCLK0 0x12C
+#define VENC_OSDCLK1 0x130
+#define VENC_HVLDCL0 0x134
+#define VENC_HVLDCL1 0x138
+#define VENC_OSDHADV 0x13C
+#define VENC_CLKCTL 0x140
+#define VENC_GAMCTL 0x144
+#define VENC_XHINTVL 0x174
+
+/* bit definitions */
+#define VPBE_PCR_VENC_DIV (1 << 1)
+#define VPBE_PCR_CLK_OFF (1 << 0)
+
+#define VENC_VMOD_VDMD_SHIFT 12
+#define VENC_VMOD_VDMD_YCBCR16 0
+#define VENC_VMOD_VDMD_YCBCR8 1
+#define VENC_VMOD_VDMD_RGB666 2
+#define VENC_VMOD_VDMD_RGB8 3
+#define VENC_VMOD_VDMD_EPSON 4
+#define VENC_VMOD_VDMD_CASIO 5
+#define VENC_VMOD_VDMD_UDISPQVGA 6
+#define VENC_VMOD_VDMD_STNLCD 7
+#define VENC_VMOD_VIE_SHIFT 1
+#define VENC_VMOD_VDMD (7 << 12)
+#define VENC_VMOD_ITLCL (1 << 11)
+#define VENC_VMOD_ITLC (1 << 10)
+#define VENC_VMOD_NSIT (1 << 9)
+#define VENC_VMOD_HDMD (1 << 8)
+#define VENC_VMOD_TVTYP_SHIFT 6
+#define VENC_VMOD_TVTYP (3 << 6)
+#define VENC_VMOD_SLAVE (1 << 5)
+#define VENC_VMOD_VMD (1 << 4)
+#define VENC_VMOD_BLNK (1 << 3)
+#define VENC_VMOD_VIE (1 << 1)
+#define VENC_VMOD_VENC (1 << 0)
+
+/* VMOD TVTYP options for HDMD=0 */
+#define SDTV_NTSC 0
+#define SDTV_PAL 1
+/* VMOD TVTYP options for HDMD=1 */
+#define HDTV_525P 0
+#define HDTV_625P 1
+#define HDTV_1080I 2
+#define HDTV_720P 3
+
+#define VENC_VIDCTL_VCLKP (1 << 14)
+#define VENC_VIDCTL_VCLKE_SHIFT 13
+#define VENC_VIDCTL_VCLKE (1 << 13)
+#define VENC_VIDCTL_VCLKZ_SHIFT 12
+#define VENC_VIDCTL_VCLKZ (1 << 12)
+#define VENC_VIDCTL_SYDIR_SHIFT 8
+#define VENC_VIDCTL_SYDIR (1 << 8)
+#define VENC_VIDCTL_DOMD_SHIFT 4
+#define VENC_VIDCTL_DOMD (3 << 4)
+#define VENC_VIDCTL_YCDIR_SHIFT 0
+#define VENC_VIDCTL_YCDIR (1 << 0)
+
+#define VENC_VDPRO_ATYCC_SHIFT 5
+#define VENC_VDPRO_ATYCC (1 << 5)
+#define VENC_VDPRO_ATCOM_SHIFT 4
+#define VENC_VDPRO_ATCOM (1 << 4)
+#define VENC_VDPRO_DAFRQ (1 << 3)
+#define VENC_VDPRO_DAUPS (1 << 2)
+#define VENC_VDPRO_CUPS (1 << 1)
+#define VENC_VDPRO_YUPS (1 << 0)
+
+#define VENC_SYNCCTL_VPL_SHIFT 3
+#define VENC_SYNCCTL_VPL (1 << 3)
+#define VENC_SYNCCTL_HPL_SHIFT 2
+#define VENC_SYNCCTL_HPL (1 << 2)
+#define VENC_SYNCCTL_SYEV_SHIFT 1
+#define VENC_SYNCCTL_SYEV (1 << 1)
+#define VENC_SYNCCTL_SYEH_SHIFT 0
+#define VENC_SYNCCTL_SYEH (1 << 0)
+#define VENC_SYNCCTL_OVD_SHIFT 14
+#define VENC_SYNCCTL_OVD (1 << 14)
+
+#define VENC_DCLKCTL_DCKEC_SHIFT 11
+#define VENC_DCLKCTL_DCKEC (1 << 11)
+#define VENC_DCLKCTL_DCKPW_SHIFT 0
+#define VENC_DCLKCTL_DCKPW (0x3f << 0)
+
+#define VENC_VSTAT_FIDST (1 << 4)
+
+#define VENC_CMPNT_MRGB_SHIFT 14
+#define VENC_CMPNT_MRGB (1 << 14)
+
+#endif /* _VPBE_VENC_REGS_H */
diff --git a/drivers/media/video/davinci/vpfe_capture.c b/drivers/media/video/davinci/vpfe_capture.c
new file mode 100644
index 00000000..20cf271a
--- /dev/null
+++ b/drivers/media/video/davinci/vpfe_capture.c
@@ -0,0 +1,2079 @@
+/*
+ * Copyright (C) 2008-2009 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Driver name : VPFE Capture driver
+ * VPFE Capture driver allows applications to capture and stream video
+ * frames on DaVinci SoCs (DM6446, DM355 etc) from a YUV source such as
+ * TVP5146 or Raw Bayer RGB image data from an image sensor
+ * such as Microns' MT9T001, MT9T031 etc.
+ *
+ * These SoCs have, in common, a Video Processing Subsystem (VPSS) that
+ * consists of a Video Processing Front End (VPFE) for capturing
+ * video/raw image data and Video Processing Back End (VPBE) for displaying
+ * YUV data through an in-built analog encoder or Digital LCD port. This
+ * driver is for capture through VPFE. A typical EVM using these SoCs have
+ * following high level configuration.
+ *
+ *
+ * decoder(TVP5146/ YUV/
+ * MT9T001) --> Raw Bayer RGB ---> MUX -> VPFE (CCDC/ISIF)
+ * data input | |
+ * V |
+ * SDRAM |
+ * V
+ * Image Processor
+ * |
+ * V
+ * SDRAM
+ * The data flow happens from a decoder connected to the VPFE over a
+ * YUV embedded (BT.656/BT.1120) or separate sync or raw bayer rgb interface
+ * and to the input of VPFE through an optional MUX (if more inputs are
+ * to be interfaced on the EVM). The input data is first passed through
+ * CCDC (CCD Controller, a.k.a Image Sensor Interface, ISIF). The CCDC
+ * does very little or no processing on YUV data and does pre-process Raw
+ * Bayer RGB data through modules such as Defect Pixel Correction (DFC)
+ * Color Space Conversion (CSC), data gain/offset etc. After this, data
+ * can be written to SDRAM or can be connected to the image processing
+ * block such as IPIPE (on DM355 only).
+ *
+ * Features supported
+ * - MMAP IO
+ * - Capture using TVP5146 over BT.656
+ * - support for interfacing decoders using sub device model
+ * - Work with DM355 or DM6446 CCDC to do Raw Bayer RGB/YUV
+ * data capture to SDRAM.
+ * TODO list
+ * - Support multiple REQBUF after open
+ * - Support for de-allocating buffers through REQBUF
+ * - Support for Raw Bayer RGB capture
+ * - Support for chaining Image Processor
+ * - Support for static allocation of buffers
+ * - Support for USERPTR IO
+ * - Support for STREAMON before QBUF
+ * - Support for control ioctls
+ */
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <media/v4l2-common.h>
+#include <linux/io.h>
+#include <media/davinci/vpfe_capture.h>
+#include "ccdc_hw_device.h"
+
+static int debug;
+static u32 numbuffers = 3;
+static u32 bufsize = (720 * 576 * 2);
+
+module_param(numbuffers, uint, S_IRUGO);
+module_param(bufsize, uint, S_IRUGO);
+module_param(debug, int, 0644);
+
+MODULE_PARM_DESC(numbuffers, "buffer count (default:3)");
+MODULE_PARM_DESC(bufsize, "buffer size in bytes (default:720 x 576 x 2)");
+MODULE_PARM_DESC(debug, "Debug level 0-1");
+
+MODULE_DESCRIPTION("VPFE Video for Linux Capture Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Texas Instruments");
+
+/* standard information */
+struct vpfe_standard {
+ v4l2_std_id std_id;
+ unsigned int width;
+ unsigned int height;
+ struct v4l2_fract pixelaspect;
+ /* 0 - progressive, 1 - interlaced */
+ int frame_format;
+};
+
+/* ccdc configuration */
+struct ccdc_config {
+ /* This make sure vpfe is probed and ready to go */
+ int vpfe_probed;
+ /* name of ccdc device */
+ char name[32];
+};
+
+/* data structures */
+static struct vpfe_config_params config_params = {
+ .min_numbuffers = 3,
+ .numbuffers = 3,
+ .min_bufsize = 720 * 480 * 2,
+ .device_bufsize = 720 * 576 * 2,
+};
+
+/* ccdc device registered */
+static struct ccdc_hw_device *ccdc_dev;
+/* lock for accessing ccdc information */
+static DEFINE_MUTEX(ccdc_lock);
+/* ccdc configuration */
+static struct ccdc_config *ccdc_cfg;
+
+const struct vpfe_standard vpfe_standards[] = {
+ {V4L2_STD_525_60, 720, 480, {11, 10}, 1},
+ {V4L2_STD_625_50, 720, 576, {54, 59}, 1},
+};
+
+/* Used when raw Bayer image from ccdc is directly captured to SDRAM */
+static const struct vpfe_pixel_format vpfe_pix_fmts[] = {
+ {
+ .fmtdesc = {
+ .index = 0,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .description = "Bayer GrRBGb 8bit A-Law compr.",
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ },
+ .bpp = 1,
+ },
+ {
+ .fmtdesc = {
+ .index = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .description = "Bayer GrRBGb - 16bit",
+ .pixelformat = V4L2_PIX_FMT_SBGGR16,
+ },
+ .bpp = 2,
+ },
+ {
+ .fmtdesc = {
+ .index = 2,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .description = "Bayer GrRBGb 8bit DPCM compr.",
+ .pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8,
+ },
+ .bpp = 1,
+ },
+ {
+ .fmtdesc = {
+ .index = 3,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .description = "YCbCr 4:2:2 Interleaved UYVY",
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ },
+ .bpp = 2,
+ },
+ {
+ .fmtdesc = {
+ .index = 4,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .description = "YCbCr 4:2:2 Interleaved YUYV",
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ },
+ .bpp = 2,
+ },
+ {
+ .fmtdesc = {
+ .index = 5,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .description = "Y/CbCr 4:2:0 - Semi planar",
+ .pixelformat = V4L2_PIX_FMT_NV12,
+ },
+ .bpp = 1,
+ },
+};
+
+/*
+ * vpfe_lookup_pix_format()
+ * lookup an entry in the vpfe pix format table based on pix_format
+ */
+static const struct vpfe_pixel_format *vpfe_lookup_pix_format(u32 pix_format)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vpfe_pix_fmts); i++) {
+ if (pix_format == vpfe_pix_fmts[i].fmtdesc.pixelformat)
+ return &vpfe_pix_fmts[i];
+ }
+ return NULL;
+}
+
+/*
+ * vpfe_register_ccdc_device. CCDC module calls this to
+ * register with vpfe capture
+ */
+int vpfe_register_ccdc_device(struct ccdc_hw_device *dev)
+{
+ int ret = 0;
+ printk(KERN_NOTICE "vpfe_register_ccdc_device: %s\n", dev->name);
+
+ BUG_ON(!dev->hw_ops.open);
+ BUG_ON(!dev->hw_ops.enable);
+ BUG_ON(!dev->hw_ops.set_hw_if_params);
+ BUG_ON(!dev->hw_ops.configure);
+ BUG_ON(!dev->hw_ops.set_buftype);
+ BUG_ON(!dev->hw_ops.get_buftype);
+ BUG_ON(!dev->hw_ops.enum_pix);
+ BUG_ON(!dev->hw_ops.set_frame_format);
+ BUG_ON(!dev->hw_ops.get_frame_format);
+ BUG_ON(!dev->hw_ops.get_pixel_format);
+ BUG_ON(!dev->hw_ops.set_pixel_format);
+ BUG_ON(!dev->hw_ops.set_image_window);
+ BUG_ON(!dev->hw_ops.get_image_window);
+ BUG_ON(!dev->hw_ops.get_line_length);
+ BUG_ON(!dev->hw_ops.getfid);
+
+ mutex_lock(&ccdc_lock);
+ if (NULL == ccdc_cfg) {
+ /*
+ * TODO. Will this ever happen? if so, we need to fix it.
+ * Proabably we need to add the request to a linked list and
+ * walk through it during vpfe probe
+ */
+ printk(KERN_ERR "vpfe capture not initialized\n");
+ ret = -EFAULT;
+ goto unlock;
+ }
+
+ if (strcmp(dev->name, ccdc_cfg->name)) {
+ /* ignore this ccdc */
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ if (ccdc_dev) {
+ printk(KERN_ERR "ccdc already registered\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ ccdc_dev = dev;
+unlock:
+ mutex_unlock(&ccdc_lock);
+ return ret;
+}
+EXPORT_SYMBOL(vpfe_register_ccdc_device);
+
+/*
+ * vpfe_unregister_ccdc_device. CCDC module calls this to
+ * unregister with vpfe capture
+ */
+void vpfe_unregister_ccdc_device(struct ccdc_hw_device *dev)
+{
+ if (NULL == dev) {
+ printk(KERN_ERR "invalid ccdc device ptr\n");
+ return;
+ }
+
+ printk(KERN_NOTICE "vpfe_unregister_ccdc_device, dev->name = %s\n",
+ dev->name);
+
+ if (strcmp(dev->name, ccdc_cfg->name)) {
+ /* ignore this ccdc */
+ return;
+ }
+
+ mutex_lock(&ccdc_lock);
+ ccdc_dev = NULL;
+ mutex_unlock(&ccdc_lock);
+ return;
+}
+EXPORT_SYMBOL(vpfe_unregister_ccdc_device);
+
+/*
+ * vpfe_get_ccdc_image_format - Get image parameters based on CCDC settings
+ */
+static int vpfe_get_ccdc_image_format(struct vpfe_device *vpfe_dev,
+ struct v4l2_format *f)
+{
+ struct v4l2_rect image_win;
+ enum ccdc_buftype buf_type;
+ enum ccdc_frmfmt frm_fmt;
+
+ memset(f, 0, sizeof(*f));
+ f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ ccdc_dev->hw_ops.get_image_window(&image_win);
+ f->fmt.pix.width = image_win.width;
+ f->fmt.pix.height = image_win.height;
+ f->fmt.pix.bytesperline = ccdc_dev->hw_ops.get_line_length();
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
+ f->fmt.pix.height;
+ buf_type = ccdc_dev->hw_ops.get_buftype();
+ f->fmt.pix.pixelformat = ccdc_dev->hw_ops.get_pixel_format();
+ frm_fmt = ccdc_dev->hw_ops.get_frame_format();
+ if (frm_fmt == CCDC_FRMFMT_PROGRESSIVE)
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ else if (frm_fmt == CCDC_FRMFMT_INTERLACED) {
+ if (buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED)
+ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
+ else if (buf_type == CCDC_BUFTYPE_FLD_SEPARATED)
+ f->fmt.pix.field = V4L2_FIELD_SEQ_TB;
+ else {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf_type\n");
+ return -EINVAL;
+ }
+ } else {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid frm_fmt\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * vpfe_config_ccdc_image_format()
+ * For a pix format, configure ccdc to setup the capture
+ */
+static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe_dev)
+{
+ enum ccdc_frmfmt frm_fmt = CCDC_FRMFMT_INTERLACED;
+ int ret = 0;
+
+ if (ccdc_dev->hw_ops.set_pixel_format(
+ vpfe_dev->fmt.fmt.pix.pixelformat) < 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "couldn't set pix format in ccdc\n");
+ return -EINVAL;
+ }
+ /* configure the image window */
+ ccdc_dev->hw_ops.set_image_window(&vpfe_dev->crop);
+
+ switch (vpfe_dev->fmt.fmt.pix.field) {
+ case V4L2_FIELD_INTERLACED:
+ /* do nothing, since it is default */
+ ret = ccdc_dev->hw_ops.set_buftype(
+ CCDC_BUFTYPE_FLD_INTERLEAVED);
+ break;
+ case V4L2_FIELD_NONE:
+ frm_fmt = CCDC_FRMFMT_PROGRESSIVE;
+ /* buffer type only applicable for interlaced scan */
+ break;
+ case V4L2_FIELD_SEQ_TB:
+ ret = ccdc_dev->hw_ops.set_buftype(
+ CCDC_BUFTYPE_FLD_SEPARATED);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set the frame format */
+ if (!ret)
+ ret = ccdc_dev->hw_ops.set_frame_format(frm_fmt);
+ return ret;
+}
+/*
+ * vpfe_config_image_format()
+ * For a given standard, this functions sets up the default
+ * pix format & crop values in the vpfe device and ccdc. It first
+ * starts with defaults based values from the standard table.
+ * It then checks if sub device support g_mbus_fmt and then override the
+ * values based on that.Sets crop values to match with scan resolution
+ * starting at 0,0. It calls vpfe_config_ccdc_image_format() set the
+ * values in ccdc
+ */
+static int vpfe_config_image_format(struct vpfe_device *vpfe_dev,
+ const v4l2_std_id *std_id)
+{
+ struct vpfe_subdev_info *sdinfo = vpfe_dev->current_subdev;
+ struct v4l2_mbus_framefmt mbus_fmt;
+ struct v4l2_pix_format *pix = &vpfe_dev->fmt.fmt.pix;
+ int i, ret = 0;
+
+ for (i = 0; i < ARRAY_SIZE(vpfe_standards); i++) {
+ if (vpfe_standards[i].std_id & *std_id) {
+ vpfe_dev->std_info.active_pixels =
+ vpfe_standards[i].width;
+ vpfe_dev->std_info.active_lines =
+ vpfe_standards[i].height;
+ vpfe_dev->std_info.frame_format =
+ vpfe_standards[i].frame_format;
+ vpfe_dev->std_index = i;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(vpfe_standards)) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "standard not supported\n");
+ return -EINVAL;
+ }
+
+ vpfe_dev->crop.top = 0;
+ vpfe_dev->crop.left = 0;
+ vpfe_dev->crop.width = vpfe_dev->std_info.active_pixels;
+ vpfe_dev->crop.height = vpfe_dev->std_info.active_lines;
+ pix->width = vpfe_dev->crop.width;
+ pix->height = vpfe_dev->crop.height;
+
+ /* first field and frame format based on standard frame format */
+ if (vpfe_dev->std_info.frame_format) {
+ pix->field = V4L2_FIELD_INTERLACED;
+ /* assume V4L2_PIX_FMT_UYVY as default */
+ pix->pixelformat = V4L2_PIX_FMT_UYVY;
+ v4l2_fill_mbus_format(&mbus_fmt, pix,
+ V4L2_MBUS_FMT_YUYV10_2X10);
+ } else {
+ pix->field = V4L2_FIELD_NONE;
+ /* assume V4L2_PIX_FMT_SBGGR8 */
+ pix->pixelformat = V4L2_PIX_FMT_SBGGR8;
+ v4l2_fill_mbus_format(&mbus_fmt, pix,
+ V4L2_MBUS_FMT_SBGGR8_1X8);
+ }
+
+ /* if sub device supports g_mbus_fmt, override the defaults */
+ ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev,
+ sdinfo->grp_id, video, g_mbus_fmt, &mbus_fmt);
+
+ if (ret && ret != -ENOIOCTLCMD) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "error in getting g_mbus_fmt from sub device\n");
+ return ret;
+ }
+ v4l2_fill_pix_format(pix, &mbus_fmt);
+ pix->bytesperline = pix->width * 2;
+ pix->sizeimage = pix->bytesperline * pix->height;
+
+ /* Sets the values in CCDC */
+ ret = vpfe_config_ccdc_image_format(vpfe_dev);
+ if (ret)
+ return ret;
+
+ /* Update the values of sizeimage and bytesperline */
+ if (!ret) {
+ pix->bytesperline = ccdc_dev->hw_ops.get_line_length();
+ pix->sizeimage = pix->bytesperline * pix->height;
+ }
+ return ret;
+}
+
+static int vpfe_initialize_device(struct vpfe_device *vpfe_dev)
+{
+ int ret = 0;
+
+ /* set first input of current subdevice as the current input */
+ vpfe_dev->current_input = 0;
+
+ /* set default standard */
+ vpfe_dev->std_index = 0;
+
+ /* Configure the default format information */
+ ret = vpfe_config_image_format(vpfe_dev,
+ &vpfe_standards[vpfe_dev->std_index].std_id);
+ if (ret)
+ return ret;
+
+ /* now open the ccdc device to initialize it */
+ mutex_lock(&ccdc_lock);
+ if (NULL == ccdc_dev) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "ccdc device not registered\n");
+ ret = -ENODEV;
+ goto unlock;
+ }
+
+ if (!try_module_get(ccdc_dev->owner)) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Couldn't lock ccdc module\n");
+ ret = -ENODEV;
+ goto unlock;
+ }
+ ret = ccdc_dev->hw_ops.open(vpfe_dev->pdev);
+ if (!ret)
+ vpfe_dev->initialized = 1;
+
+ /* Clear all VPFE/CCDC interrupts */
+ if (vpfe_dev->cfg->clr_intr)
+ vpfe_dev->cfg->clr_intr(-1);
+
+unlock:
+ mutex_unlock(&ccdc_lock);
+ return ret;
+}
+
+/*
+ * vpfe_open : It creates object of file handle structure and
+ * stores it in private_data member of filepointer
+ */
+static int vpfe_open(struct file *file)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_fh *fh;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_open\n");
+
+ if (!vpfe_dev->cfg->num_subdevs) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "No decoder registered\n");
+ return -ENODEV;
+ }
+
+ /* Allocate memory for the file handle object */
+ fh = kmalloc(sizeof(struct vpfe_fh), GFP_KERNEL);
+ if (NULL == fh) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "unable to allocate memory for file handle object\n");
+ return -ENOMEM;
+ }
+ /* store pointer to fh in private_data member of file */
+ file->private_data = fh;
+ fh->vpfe_dev = vpfe_dev;
+ mutex_lock(&vpfe_dev->lock);
+ /* If decoder is not initialized. initialize it */
+ if (!vpfe_dev->initialized) {
+ if (vpfe_initialize_device(vpfe_dev)) {
+ mutex_unlock(&vpfe_dev->lock);
+ return -ENODEV;
+ }
+ }
+ /* Increment device usrs counter */
+ vpfe_dev->usrs++;
+ /* Set io_allowed member to false */
+ fh->io_allowed = 0;
+ /* Initialize priority of this instance to default priority */
+ fh->prio = V4L2_PRIORITY_UNSET;
+ v4l2_prio_open(&vpfe_dev->prio, &fh->prio);
+ mutex_unlock(&vpfe_dev->lock);
+ return 0;
+}
+
+static void vpfe_schedule_next_buffer(struct vpfe_device *vpfe_dev)
+{
+ unsigned long addr;
+
+ vpfe_dev->next_frm = list_entry(vpfe_dev->dma_queue.next,
+ struct videobuf_buffer, queue);
+ list_del(&vpfe_dev->next_frm->queue);
+ vpfe_dev->next_frm->state = VIDEOBUF_ACTIVE;
+ addr = videobuf_to_dma_contig(vpfe_dev->next_frm);
+
+ ccdc_dev->hw_ops.setfbaddr(addr);
+}
+
+static void vpfe_schedule_bottom_field(struct vpfe_device *vpfe_dev)
+{
+ unsigned long addr;
+
+ addr = videobuf_to_dma_contig(vpfe_dev->cur_frm);
+ addr += vpfe_dev->field_off;
+ ccdc_dev->hw_ops.setfbaddr(addr);
+}
+
+static void vpfe_process_buffer_complete(struct vpfe_device *vpfe_dev)
+{
+ struct timeval timevalue;
+
+ do_gettimeofday(&timevalue);
+ vpfe_dev->cur_frm->ts = timevalue;
+ vpfe_dev->cur_frm->state = VIDEOBUF_DONE;
+ vpfe_dev->cur_frm->size = vpfe_dev->fmt.fmt.pix.sizeimage;
+ wake_up_interruptible(&vpfe_dev->cur_frm->done);
+ vpfe_dev->cur_frm = vpfe_dev->next_frm;
+}
+
+/* ISR for VINT0*/
+static irqreturn_t vpfe_isr(int irq, void *dev_id)
+{
+ struct vpfe_device *vpfe_dev = dev_id;
+ enum v4l2_field field;
+ int fid;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "\nStarting vpfe_isr...\n");
+ field = vpfe_dev->fmt.fmt.pix.field;
+
+ /* if streaming not started, don't do anything */
+ if (!vpfe_dev->started)
+ goto clear_intr;
+
+ /* only for 6446 this will be applicable */
+ if (NULL != ccdc_dev->hw_ops.reset)
+ ccdc_dev->hw_ops.reset();
+
+ if (field == V4L2_FIELD_NONE) {
+ /* handle progressive frame capture */
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
+ "frame format is progressive...\n");
+ if (vpfe_dev->cur_frm != vpfe_dev->next_frm)
+ vpfe_process_buffer_complete(vpfe_dev);
+ goto clear_intr;
+ }
+
+ /* interlaced or TB capture check which field we are in hardware */
+ fid = ccdc_dev->hw_ops.getfid();
+
+ /* switch the software maintained field id */
+ vpfe_dev->field_id ^= 1;
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "field id = %x:%x.\n",
+ fid, vpfe_dev->field_id);
+ if (fid == vpfe_dev->field_id) {
+ /* we are in-sync here,continue */
+ if (fid == 0) {
+ /*
+ * One frame is just being captured. If the next frame
+ * is available, release the current frame and move on
+ */
+ if (vpfe_dev->cur_frm != vpfe_dev->next_frm)
+ vpfe_process_buffer_complete(vpfe_dev);
+ /*
+ * based on whether the two fields are stored
+ * interleavely or separately in memory, reconfigure
+ * the CCDC memory address
+ */
+ if (field == V4L2_FIELD_SEQ_TB) {
+ vpfe_schedule_bottom_field(vpfe_dev);
+ }
+ goto clear_intr;
+ }
+ /*
+ * if one field is just being captured configure
+ * the next frame get the next frame from the empty
+ * queue if no frame is available hold on to the
+ * current buffer
+ */
+ spin_lock(&vpfe_dev->dma_queue_lock);
+ if (!list_empty(&vpfe_dev->dma_queue) &&
+ vpfe_dev->cur_frm == vpfe_dev->next_frm)
+ vpfe_schedule_next_buffer(vpfe_dev);
+ spin_unlock(&vpfe_dev->dma_queue_lock);
+ } else if (fid == 0) {
+ /*
+ * out of sync. Recover from any hardware out-of-sync.
+ * May loose one frame
+ */
+ vpfe_dev->field_id = fid;
+ }
+clear_intr:
+ if (vpfe_dev->cfg->clr_intr)
+ vpfe_dev->cfg->clr_intr(irq);
+
+ return IRQ_HANDLED;
+}
+
+/* vdint1_isr - isr handler for VINT1 interrupt */
+static irqreturn_t vdint1_isr(int irq, void *dev_id)
+{
+ struct vpfe_device *vpfe_dev = dev_id;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "\nInside vdint1_isr...\n");
+
+ /* if streaming not started, don't do anything */
+ if (!vpfe_dev->started) {
+ if (vpfe_dev->cfg->clr_intr)
+ vpfe_dev->cfg->clr_intr(irq);
+ return IRQ_HANDLED;
+ }
+
+ spin_lock(&vpfe_dev->dma_queue_lock);
+ if ((vpfe_dev->fmt.fmt.pix.field == V4L2_FIELD_NONE) &&
+ !list_empty(&vpfe_dev->dma_queue) &&
+ vpfe_dev->cur_frm == vpfe_dev->next_frm)
+ vpfe_schedule_next_buffer(vpfe_dev);
+ spin_unlock(&vpfe_dev->dma_queue_lock);
+
+ if (vpfe_dev->cfg->clr_intr)
+ vpfe_dev->cfg->clr_intr(irq);
+
+ return IRQ_HANDLED;
+}
+
+static void vpfe_detach_irq(struct vpfe_device *vpfe_dev)
+{
+ enum ccdc_frmfmt frame_format;
+
+ frame_format = ccdc_dev->hw_ops.get_frame_format();
+ if (frame_format == CCDC_FRMFMT_PROGRESSIVE)
+ free_irq(vpfe_dev->ccdc_irq1, vpfe_dev);
+}
+
+static int vpfe_attach_irq(struct vpfe_device *vpfe_dev)
+{
+ enum ccdc_frmfmt frame_format;
+
+ frame_format = ccdc_dev->hw_ops.get_frame_format();
+ if (frame_format == CCDC_FRMFMT_PROGRESSIVE) {
+ return request_irq(vpfe_dev->ccdc_irq1, vdint1_isr,
+ IRQF_DISABLED, "vpfe_capture1",
+ vpfe_dev);
+ }
+ return 0;
+}
+
+/* vpfe_stop_ccdc_capture: stop streaming in ccdc/isif */
+static void vpfe_stop_ccdc_capture(struct vpfe_device *vpfe_dev)
+{
+ vpfe_dev->started = 0;
+ ccdc_dev->hw_ops.enable(0);
+ if (ccdc_dev->hw_ops.enable_out_to_sdram)
+ ccdc_dev->hw_ops.enable_out_to_sdram(0);
+}
+
+/*
+ * vpfe_release : This function deletes buffer queue, frees the
+ * buffers and the vpfe file handle
+ */
+static int vpfe_release(struct file *file)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_fh *fh = file->private_data;
+ struct vpfe_subdev_info *sdinfo;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_release\n");
+
+ /* Get the device lock */
+ mutex_lock(&vpfe_dev->lock);
+ /* if this instance is doing IO */
+ if (fh->io_allowed) {
+ if (vpfe_dev->started) {
+ sdinfo = vpfe_dev->current_subdev;
+ ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev,
+ sdinfo->grp_id,
+ video, s_stream, 0);
+ if (ret && (ret != -ENOIOCTLCMD))
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "stream off failed in subdev\n");
+ vpfe_stop_ccdc_capture(vpfe_dev);
+ vpfe_detach_irq(vpfe_dev);
+ videobuf_streamoff(&vpfe_dev->buffer_queue);
+ }
+ vpfe_dev->io_usrs = 0;
+ vpfe_dev->numbuffers = config_params.numbuffers;
+ }
+
+ /* Decrement device usrs counter */
+ vpfe_dev->usrs--;
+ /* Close the priority */
+ v4l2_prio_close(&vpfe_dev->prio, fh->prio);
+ /* If this is the last file handle */
+ if (!vpfe_dev->usrs) {
+ vpfe_dev->initialized = 0;
+ if (ccdc_dev->hw_ops.close)
+ ccdc_dev->hw_ops.close(vpfe_dev->pdev);
+ module_put(ccdc_dev->owner);
+ }
+ mutex_unlock(&vpfe_dev->lock);
+ file->private_data = NULL;
+ /* Free memory allocated to file handle object */
+ kfree(fh);
+ return 0;
+}
+
+/*
+ * vpfe_mmap : It is used to map kernel space buffers
+ * into user spaces
+ */
+static int vpfe_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ /* Get the device object and file handle object */
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_mmap\n");
+
+ return videobuf_mmap_mapper(&vpfe_dev->buffer_queue, vma);
+}
+
+/*
+ * vpfe_poll: It is used for select/poll system call
+ */
+static unsigned int vpfe_poll(struct file *file, poll_table *wait)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_poll\n");
+
+ if (vpfe_dev->started)
+ return videobuf_poll_stream(file,
+ &vpfe_dev->buffer_queue, wait);
+ return 0;
+}
+
+/* vpfe capture driver file operations */
+static const struct v4l2_file_operations vpfe_fops = {
+ .owner = THIS_MODULE,
+ .open = vpfe_open,
+ .release = vpfe_release,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vpfe_mmap,
+ .poll = vpfe_poll
+};
+
+/*
+ * vpfe_check_format()
+ * This function adjust the input pixel format as per hardware
+ * capabilities and update the same in pixfmt.
+ * Following algorithm used :-
+ *
+ * If given pixformat is not in the vpfe list of pix formats or not
+ * supported by the hardware, current value of pixformat in the device
+ * is used
+ * If given field is not supported, then current field is used. If field
+ * is different from current, then it is matched with that from sub device.
+ * Minimum height is 2 lines for interlaced or tb field and 1 line for
+ * progressive. Maximum height is clamped to active active lines of scan
+ * Minimum width is 32 bytes in memory and width is clamped to active
+ * pixels of scan.
+ * bytesperline is a multiple of 32.
+ */
+static const struct vpfe_pixel_format *
+ vpfe_check_format(struct vpfe_device *vpfe_dev,
+ struct v4l2_pix_format *pixfmt)
+{
+ u32 min_height = 1, min_width = 32, max_width, max_height;
+ const struct vpfe_pixel_format *vpfe_pix_fmt;
+ u32 pix;
+ int temp, found;
+
+ vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat);
+ if (NULL == vpfe_pix_fmt) {
+ /*
+ * use current pixel format in the vpfe device. We
+ * will find this pix format in the table
+ */
+ pixfmt->pixelformat = vpfe_dev->fmt.fmt.pix.pixelformat;
+ vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat);
+ }
+
+ /* check if hw supports it */
+ temp = 0;
+ found = 0;
+ while (ccdc_dev->hw_ops.enum_pix(&pix, temp) >= 0) {
+ if (vpfe_pix_fmt->fmtdesc.pixelformat == pix) {
+ found = 1;
+ break;
+ }
+ temp++;
+ }
+
+ if (!found) {
+ /* use current pixel format */
+ pixfmt->pixelformat = vpfe_dev->fmt.fmt.pix.pixelformat;
+ /*
+ * Since this is currently used in the vpfe device, we
+ * will find this pix format in the table
+ */
+ vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat);
+ }
+
+ /* check what field format is supported */
+ if (pixfmt->field == V4L2_FIELD_ANY) {
+ /* if field is any, use current value as default */
+ pixfmt->field = vpfe_dev->fmt.fmt.pix.field;
+ }
+
+ /*
+ * if field is not same as current field in the vpfe device
+ * try matching the field with the sub device field
+ */
+ if (vpfe_dev->fmt.fmt.pix.field != pixfmt->field) {
+ /*
+ * If field value is not in the supported fields, use current
+ * field used in the device as default
+ */
+ switch (pixfmt->field) {
+ case V4L2_FIELD_INTERLACED:
+ case V4L2_FIELD_SEQ_TB:
+ /* if sub device is supporting progressive, use that */
+ if (!vpfe_dev->std_info.frame_format)
+ pixfmt->field = V4L2_FIELD_NONE;
+ break;
+ case V4L2_FIELD_NONE:
+ if (vpfe_dev->std_info.frame_format)
+ pixfmt->field = V4L2_FIELD_INTERLACED;
+ break;
+
+ default:
+ /* use current field as default */
+ pixfmt->field = vpfe_dev->fmt.fmt.pix.field;
+ break;
+ }
+ }
+
+ /* Now adjust image resolutions supported */
+ if (pixfmt->field == V4L2_FIELD_INTERLACED ||
+ pixfmt->field == V4L2_FIELD_SEQ_TB)
+ min_height = 2;
+
+ max_width = vpfe_dev->std_info.active_pixels;
+ max_height = vpfe_dev->std_info.active_lines;
+ min_width /= vpfe_pix_fmt->bpp;
+
+ v4l2_info(&vpfe_dev->v4l2_dev, "width = %d, height = %d, bpp = %d\n",
+ pixfmt->width, pixfmt->height, vpfe_pix_fmt->bpp);
+
+ pixfmt->width = clamp((pixfmt->width), min_width, max_width);
+ pixfmt->height = clamp((pixfmt->height), min_height, max_height);
+
+ /* If interlaced, adjust height to be a multiple of 2 */
+ if (pixfmt->field == V4L2_FIELD_INTERLACED)
+ pixfmt->height &= (~1);
+ /*
+ * recalculate bytesperline and sizeimage since width
+ * and height might have changed
+ */
+ pixfmt->bytesperline = (((pixfmt->width * vpfe_pix_fmt->bpp) + 31)
+ & ~31);
+ if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12)
+ pixfmt->sizeimage =
+ pixfmt->bytesperline * pixfmt->height +
+ ((pixfmt->bytesperline * pixfmt->height) >> 1);
+ else
+ pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
+
+ v4l2_info(&vpfe_dev->v4l2_dev, "adjusted width = %d, height ="
+ " %d, bpp = %d, bytesperline = %d, sizeimage = %d\n",
+ pixfmt->width, pixfmt->height, vpfe_pix_fmt->bpp,
+ pixfmt->bytesperline, pixfmt->sizeimage);
+ return vpfe_pix_fmt;
+}
+
+static int vpfe_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querycap\n");
+
+ cap->version = VPFE_CAPTURE_VERSION_CODE;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver));
+ strlcpy(cap->bus_info, "VPFE", sizeof(cap->bus_info));
+ strlcpy(cap->card, vpfe_dev->cfg->card_name, sizeof(cap->card));
+ return 0;
+}
+
+static int vpfe_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ int ret = 0;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_fmt_vid_cap\n");
+ /* Fill in the information about format */
+ *fmt = vpfe_dev->fmt;
+ return ret;
+}
+
+static int vpfe_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *fmt)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ const struct vpfe_pixel_format *pix_fmt;
+ int temp_index;
+ u32 pix;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_fmt_vid_cap\n");
+
+ if (ccdc_dev->hw_ops.enum_pix(&pix, fmt->index) < 0)
+ return -EINVAL;
+
+ /* Fill in the information about format */
+ pix_fmt = vpfe_lookup_pix_format(pix);
+ if (NULL != pix_fmt) {
+ temp_index = fmt->index;
+ *fmt = pix_fmt->fmtdesc;
+ fmt->index = temp_index;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int vpfe_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ const struct vpfe_pixel_format *pix_fmts;
+ int ret = 0;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_fmt_vid_cap\n");
+
+ /* If streaming is started, return error */
+ if (vpfe_dev->started) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is started\n");
+ return -EBUSY;
+ }
+
+ /* Check for valid frame format */
+ pix_fmts = vpfe_check_format(vpfe_dev, &fmt->fmt.pix);
+
+ if (NULL == pix_fmts)
+ return -EINVAL;
+
+ /* store the pixel format in the device object */
+ ret = mutex_lock_interruptible(&vpfe_dev->lock);
+ if (ret)
+ return ret;
+
+ /* First detach any IRQ if currently attached */
+ vpfe_detach_irq(vpfe_dev);
+ vpfe_dev->fmt = *fmt;
+ /* set image capture parameters in the ccdc */
+ ret = vpfe_config_ccdc_image_format(vpfe_dev);
+ mutex_unlock(&vpfe_dev->lock);
+ return ret;
+}
+
+static int vpfe_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ const struct vpfe_pixel_format *pix_fmts;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_try_fmt_vid_cap\n");
+
+ pix_fmts = vpfe_check_format(vpfe_dev, &f->fmt.pix);
+ if (NULL == pix_fmts)
+ return -EINVAL;
+ return 0;
+}
+
+/*
+ * vpfe_get_subdev_input_index - Get subdev index and subdev input index for a
+ * given app input index
+ */
+static int vpfe_get_subdev_input_index(struct vpfe_device *vpfe_dev,
+ int *subdev_index,
+ int *subdev_input_index,
+ int app_input_index)
+{
+ struct vpfe_config *cfg = vpfe_dev->cfg;
+ struct vpfe_subdev_info *sdinfo;
+ int i, j = 0;
+
+ for (i = 0; i < cfg->num_subdevs; i++) {
+ sdinfo = &cfg->sub_devs[i];
+ if (app_input_index < (j + sdinfo->num_inputs)) {
+ *subdev_index = i;
+ *subdev_input_index = app_input_index - j;
+ return 0;
+ }
+ j += sdinfo->num_inputs;
+ }
+ return -EINVAL;
+}
+
+/*
+ * vpfe_get_app_input - Get app input index for a given subdev input index
+ * driver stores the input index of the current sub device and translate it
+ * when application request the current input
+ */
+static int vpfe_get_app_input_index(struct vpfe_device *vpfe_dev,
+ int *app_input_index)
+{
+ struct vpfe_config *cfg = vpfe_dev->cfg;
+ struct vpfe_subdev_info *sdinfo;
+ int i, j = 0;
+
+ for (i = 0; i < cfg->num_subdevs; i++) {
+ sdinfo = &cfg->sub_devs[i];
+ if (!strcmp(sdinfo->name, vpfe_dev->current_subdev->name)) {
+ if (vpfe_dev->current_input >= sdinfo->num_inputs)
+ return -1;
+ *app_input_index = j + vpfe_dev->current_input;
+ return 0;
+ }
+ j += sdinfo->num_inputs;
+ }
+ return -EINVAL;
+}
+
+static int vpfe_enum_input(struct file *file, void *priv,
+ struct v4l2_input *inp)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_subdev_info *sdinfo;
+ int subdev, index ;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_input\n");
+
+ if (vpfe_get_subdev_input_index(vpfe_dev,
+ &subdev,
+ &index,
+ inp->index) < 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "input information not found"
+ " for the subdev\n");
+ return -EINVAL;
+ }
+ sdinfo = &vpfe_dev->cfg->sub_devs[subdev];
+ memcpy(inp, &sdinfo->inputs[index], sizeof(struct v4l2_input));
+ return 0;
+}
+
+static int vpfe_g_input(struct file *file, void *priv, unsigned int *index)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_input\n");
+
+ return vpfe_get_app_input_index(vpfe_dev, index);
+}
+
+
+static int vpfe_s_input(struct file *file, void *priv, unsigned int index)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_subdev_info *sdinfo;
+ int subdev_index, inp_index;
+ struct vpfe_route *route;
+ u32 input = 0, output = 0;
+ int ret = -EINVAL;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_input\n");
+
+ ret = mutex_lock_interruptible(&vpfe_dev->lock);
+ if (ret)
+ return ret;
+
+ /*
+ * If streaming is started return device busy
+ * error
+ */
+ if (vpfe_dev->started) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is on\n");
+ ret = -EBUSY;
+ goto unlock_out;
+ }
+
+ if (vpfe_get_subdev_input_index(vpfe_dev,
+ &subdev_index,
+ &inp_index,
+ index) < 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "invalid input index\n");
+ goto unlock_out;
+ }
+
+ sdinfo = &vpfe_dev->cfg->sub_devs[subdev_index];
+ route = &sdinfo->routes[inp_index];
+ if (route && sdinfo->can_route) {
+ input = route->input;
+ output = route->output;
+ }
+
+ ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
+ video, s_routing, input, output, 0);
+
+ if (ret) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "vpfe_doioctl:error in setting input in decoder\n");
+ ret = -EINVAL;
+ goto unlock_out;
+ }
+ vpfe_dev->current_subdev = sdinfo;
+ vpfe_dev->current_input = index;
+ vpfe_dev->std_index = 0;
+
+ /* set the bus/interface parameter for the sub device in ccdc */
+ ret = ccdc_dev->hw_ops.set_hw_if_params(&sdinfo->ccdc_if_params);
+ if (ret)
+ goto unlock_out;
+
+ /* set the default image parameters in the device */
+ ret = vpfe_config_image_format(vpfe_dev,
+ &vpfe_standards[vpfe_dev->std_index].std_id);
+unlock_out:
+ mutex_unlock(&vpfe_dev->lock);
+ return ret;
+}
+
+static int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_subdev_info *sdinfo;
+ int ret = 0;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querystd\n");
+
+ ret = mutex_lock_interruptible(&vpfe_dev->lock);
+ sdinfo = vpfe_dev->current_subdev;
+ if (ret)
+ return ret;
+ /* Call querystd function of decoder device */
+ ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
+ video, querystd, std_id);
+ mutex_unlock(&vpfe_dev->lock);
+ return ret;
+}
+
+static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id *std_id)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_subdev_info *sdinfo;
+ int ret = 0;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_std\n");
+
+ /* Call decoder driver function to set the standard */
+ ret = mutex_lock_interruptible(&vpfe_dev->lock);
+ if (ret)
+ return ret;
+
+ sdinfo = vpfe_dev->current_subdev;
+ /* If streaming is started, return device busy error */
+ if (vpfe_dev->started) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "streaming is started\n");
+ ret = -EBUSY;
+ goto unlock_out;
+ }
+
+ ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
+ core, s_std, *std_id);
+ if (ret < 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n");
+ goto unlock_out;
+ }
+ ret = vpfe_config_image_format(vpfe_dev, std_id);
+
+unlock_out:
+ mutex_unlock(&vpfe_dev->lock);
+ return ret;
+}
+
+static int vpfe_g_std(struct file *file, void *priv, v4l2_std_id *std_id)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_std\n");
+
+ *std_id = vpfe_standards[vpfe_dev->std_index].std_id;
+ return 0;
+}
+/*
+ * Videobuf operations
+ */
+static int vpfe_videobuf_setup(struct videobuf_queue *vq,
+ unsigned int *count,
+ unsigned int *size)
+{
+ struct vpfe_fh *fh = vq->priv_data;
+ struct vpfe_device *vpfe_dev = fh->vpfe_dev;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_setup\n");
+ *size = vpfe_dev->fmt.fmt.pix.sizeimage;
+ if (vpfe_dev->memory == V4L2_MEMORY_MMAP &&
+ vpfe_dev->fmt.fmt.pix.sizeimage > config_params.device_bufsize)
+ *size = config_params.device_bufsize;
+
+ if (*count < config_params.min_numbuffers)
+ *count = config_params.min_numbuffers;
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
+ "count=%d, size=%d\n", *count, *size);
+ return 0;
+}
+
+static int vpfe_videobuf_prepare(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct vpfe_fh *fh = vq->priv_data;
+ struct vpfe_device *vpfe_dev = fh->vpfe_dev;
+ unsigned long addr;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_prepare\n");
+
+ /* If buffer is not initialized, initialize it */
+ if (VIDEOBUF_NEEDS_INIT == vb->state) {
+ vb->width = vpfe_dev->fmt.fmt.pix.width;
+ vb->height = vpfe_dev->fmt.fmt.pix.height;
+ vb->size = vpfe_dev->fmt.fmt.pix.sizeimage;
+ vb->field = field;
+
+ ret = videobuf_iolock(vq, vb, NULL);
+ if (ret < 0)
+ return ret;
+
+ addr = videobuf_to_dma_contig(vb);
+ /* Make sure user addresses are aligned to 32 bytes */
+ if (!ALIGN(addr, 32))
+ return -EINVAL;
+
+ vb->state = VIDEOBUF_PREPARED;
+ }
+ return 0;
+}
+
+static void vpfe_videobuf_queue(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ /* Get the file handle object and device object */
+ struct vpfe_fh *fh = vq->priv_data;
+ struct vpfe_device *vpfe_dev = fh->vpfe_dev;
+ unsigned long flags;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_queue\n");
+
+ /* add the buffer to the DMA queue */
+ spin_lock_irqsave(&vpfe_dev->dma_queue_lock, flags);
+ list_add_tail(&vb->queue, &vpfe_dev->dma_queue);
+ spin_unlock_irqrestore(&vpfe_dev->dma_queue_lock, flags);
+
+ /* Change state of the buffer */
+ vb->state = VIDEOBUF_QUEUED;
+}
+
+static void vpfe_videobuf_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct vpfe_fh *fh = vq->priv_data;
+ struct vpfe_device *vpfe_dev = fh->vpfe_dev;
+ unsigned long flags;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_videobuf_release\n");
+
+ /*
+ * We need to flush the buffer from the dma queue since
+ * they are de-allocated
+ */
+ spin_lock_irqsave(&vpfe_dev->dma_queue_lock, flags);
+ INIT_LIST_HEAD(&vpfe_dev->dma_queue);
+ spin_unlock_irqrestore(&vpfe_dev->dma_queue_lock, flags);
+ videobuf_dma_contig_free(vq, vb);
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static struct videobuf_queue_ops vpfe_videobuf_qops = {
+ .buf_setup = vpfe_videobuf_setup,
+ .buf_prepare = vpfe_videobuf_prepare,
+ .buf_queue = vpfe_videobuf_queue,
+ .buf_release = vpfe_videobuf_release,
+};
+
+/*
+ * vpfe_reqbufs. currently support REQBUF only once opening
+ * the device.
+ */
+static int vpfe_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *req_buf)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_fh *fh = file->private_data;
+ int ret = 0;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_reqbufs\n");
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != req_buf->type) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buffer type\n");
+ return -EINVAL;
+ }
+
+ ret = mutex_lock_interruptible(&vpfe_dev->lock);
+ if (ret)
+ return ret;
+
+ if (vpfe_dev->io_usrs != 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Only one IO user allowed\n");
+ ret = -EBUSY;
+ goto unlock_out;
+ }
+
+ vpfe_dev->memory = req_buf->memory;
+ videobuf_queue_dma_contig_init(&vpfe_dev->buffer_queue,
+ &vpfe_videobuf_qops,
+ vpfe_dev->pdev,
+ &vpfe_dev->irqlock,
+ req_buf->type,
+ vpfe_dev->fmt.fmt.pix.field,
+ sizeof(struct videobuf_buffer),
+ fh, NULL);
+
+ fh->io_allowed = 1;
+ vpfe_dev->io_usrs = 1;
+ INIT_LIST_HEAD(&vpfe_dev->dma_queue);
+ ret = videobuf_reqbufs(&vpfe_dev->buffer_queue, req_buf);
+unlock_out:
+ mutex_unlock(&vpfe_dev->lock);
+ return ret;
+}
+
+static int vpfe_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querybuf\n");
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n");
+ return -EINVAL;
+ }
+
+ if (vpfe_dev->memory != V4L2_MEMORY_MMAP) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid memory\n");
+ return -EINVAL;
+ }
+ /* Call videobuf_querybuf to get information */
+ return videobuf_querybuf(&vpfe_dev->buffer_queue, buf);
+}
+
+static int vpfe_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_fh *fh = file->private_data;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_qbuf\n");
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != p->type) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n");
+ return -EINVAL;
+ }
+
+ /*
+ * If this file handle is not allowed to do IO,
+ * return error
+ */
+ if (!fh->io_allowed) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n");
+ return -EACCES;
+ }
+ return videobuf_qbuf(&vpfe_dev->buffer_queue, p);
+}
+
+static int vpfe_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_dqbuf\n");
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n");
+ return -EINVAL;
+ }
+ return videobuf_dqbuf(&vpfe_dev->buffer_queue,
+ buf, file->f_flags & O_NONBLOCK);
+}
+
+static int vpfe_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qctrl)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_subdev_info *sdinfo;
+
+ sdinfo = vpfe_dev->current_subdev;
+
+ return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
+ core, queryctrl, qctrl);
+
+}
+
+static int vpfe_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_subdev_info *sdinfo;
+
+ sdinfo = vpfe_dev->current_subdev;
+
+ return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
+ core, g_ctrl, ctrl);
+}
+
+static int vpfe_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_subdev_info *sdinfo;
+
+ sdinfo = vpfe_dev->current_subdev;
+
+ return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
+ core, s_ctrl, ctrl);
+}
+
+/*
+ * vpfe_calculate_offsets : This function calculates buffers offset
+ * for top and bottom field
+ */
+static void vpfe_calculate_offsets(struct vpfe_device *vpfe_dev)
+{
+ struct v4l2_rect image_win;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_calculate_offsets\n");
+
+ ccdc_dev->hw_ops.get_image_window(&image_win);
+ vpfe_dev->field_off = image_win.height * image_win.width;
+}
+
+/* vpfe_start_ccdc_capture: start streaming in ccdc/isif */
+static void vpfe_start_ccdc_capture(struct vpfe_device *vpfe_dev)
+{
+ ccdc_dev->hw_ops.enable(1);
+ if (ccdc_dev->hw_ops.enable_out_to_sdram)
+ ccdc_dev->hw_ops.enable_out_to_sdram(1);
+ vpfe_dev->started = 1;
+}
+
+/*
+ * vpfe_streamon. Assume the DMA queue is not empty.
+ * application is expected to call QBUF before calling
+ * this ioctl. If not, driver returns error
+ */
+static int vpfe_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type buf_type)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_fh *fh = file->private_data;
+ struct vpfe_subdev_info *sdinfo;
+ unsigned long addr;
+ int ret = 0;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamon\n");
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n");
+ return -EINVAL;
+ }
+
+ /* If file handle is not allowed IO, return error */
+ if (!fh->io_allowed) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n");
+ return -EACCES;
+ }
+
+ sdinfo = vpfe_dev->current_subdev;
+ ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
+ video, s_stream, 1);
+
+ if (ret && (ret != -ENOIOCTLCMD)) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "stream on failed in subdev\n");
+ return -EINVAL;
+ }
+
+ /* If buffer queue is empty, return error */
+ if (list_empty(&vpfe_dev->buffer_queue.stream)) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "buffer queue is empty\n");
+ return -EIO;
+ }
+
+ /* Call videobuf_streamon to start streaming * in videobuf */
+ ret = videobuf_streamon(&vpfe_dev->buffer_queue);
+ if (ret)
+ return ret;
+
+
+ ret = mutex_lock_interruptible(&vpfe_dev->lock);
+ if (ret)
+ goto streamoff;
+ /* Get the next frame from the buffer queue */
+ vpfe_dev->next_frm = list_entry(vpfe_dev->dma_queue.next,
+ struct videobuf_buffer, queue);
+ vpfe_dev->cur_frm = vpfe_dev->next_frm;
+ /* Remove buffer from the buffer queue */
+ list_del(&vpfe_dev->cur_frm->queue);
+ /* Mark state of the current frame to active */
+ vpfe_dev->cur_frm->state = VIDEOBUF_ACTIVE;
+ /* Initialize field_id and started member */
+ vpfe_dev->field_id = 0;
+ addr = videobuf_to_dma_contig(vpfe_dev->cur_frm);
+
+ /* Calculate field offset */
+ vpfe_calculate_offsets(vpfe_dev);
+
+ if (vpfe_attach_irq(vpfe_dev) < 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "Error in attaching interrupt handle\n");
+ ret = -EFAULT;
+ goto unlock_out;
+ }
+ if (ccdc_dev->hw_ops.configure() < 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "Error in configuring ccdc\n");
+ ret = -EINVAL;
+ goto unlock_out;
+ }
+ ccdc_dev->hw_ops.setfbaddr((unsigned long)(addr));
+ vpfe_start_ccdc_capture(vpfe_dev);
+ mutex_unlock(&vpfe_dev->lock);
+ return ret;
+unlock_out:
+ mutex_unlock(&vpfe_dev->lock);
+streamoff:
+ ret = videobuf_streamoff(&vpfe_dev->buffer_queue);
+ return ret;
+}
+
+static int vpfe_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type buf_type)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_fh *fh = file->private_data;
+ struct vpfe_subdev_info *sdinfo;
+ int ret = 0;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamoff\n");
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n");
+ return -EINVAL;
+ }
+
+ /* If io is allowed for this file handle, return error */
+ if (!fh->io_allowed) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n");
+ return -EACCES;
+ }
+
+ /* If streaming is not started, return error */
+ if (!vpfe_dev->started) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "device started\n");
+ return -EINVAL;
+ }
+
+ ret = mutex_lock_interruptible(&vpfe_dev->lock);
+ if (ret)
+ return ret;
+
+ vpfe_stop_ccdc_capture(vpfe_dev);
+ vpfe_detach_irq(vpfe_dev);
+
+ sdinfo = vpfe_dev->current_subdev;
+ ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
+ video, s_stream, 0);
+
+ if (ret && (ret != -ENOIOCTLCMD))
+ v4l2_err(&vpfe_dev->v4l2_dev, "stream off failed in subdev\n");
+ ret = videobuf_streamoff(&vpfe_dev->buffer_queue);
+ mutex_unlock(&vpfe_dev->lock);
+ return ret;
+}
+
+static int vpfe_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *crop)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_cropcap\n");
+
+ if (vpfe_dev->std_index >= ARRAY_SIZE(vpfe_standards))
+ return -EINVAL;
+
+ memset(crop, 0, sizeof(struct v4l2_cropcap));
+ crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ crop->bounds.width = crop->defrect.width =
+ vpfe_standards[vpfe_dev->std_index].width;
+ crop->bounds.height = crop->defrect.height =
+ vpfe_standards[vpfe_dev->std_index].height;
+ crop->pixelaspect = vpfe_standards[vpfe_dev->std_index].pixelaspect;
+ return 0;
+}
+
+static int vpfe_g_crop(struct file *file, void *priv,
+ struct v4l2_crop *crop)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_crop\n");
+
+ crop->c = vpfe_dev->crop;
+ return 0;
+}
+
+static int vpfe_s_crop(struct file *file, void *priv,
+ struct v4l2_crop *crop)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ int ret = 0;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_crop\n");
+
+ if (vpfe_dev->started) {
+ /* make sure streaming is not started */
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "Cannot change crop when streaming is ON\n");
+ return -EBUSY;
+ }
+
+ ret = mutex_lock_interruptible(&vpfe_dev->lock);
+ if (ret)
+ return ret;
+
+ if (crop->c.top < 0 || crop->c.left < 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "doesn't support negative values for top & left\n");
+ ret = -EINVAL;
+ goto unlock_out;
+ }
+
+ /* adjust the width to 16 pixel boundary */
+ crop->c.width = ((crop->c.width + 15) & ~0xf);
+
+ /* make sure parameters are valid */
+ if ((crop->c.left + crop->c.width >
+ vpfe_dev->std_info.active_pixels) ||
+ (crop->c.top + crop->c.height >
+ vpfe_dev->std_info.active_lines)) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Error in S_CROP params\n");
+ ret = -EINVAL;
+ goto unlock_out;
+ }
+ ccdc_dev->hw_ops.set_image_window(&crop->c);
+ vpfe_dev->fmt.fmt.pix.width = crop->c.width;
+ vpfe_dev->fmt.fmt.pix.height = crop->c.height;
+ vpfe_dev->fmt.fmt.pix.bytesperline =
+ ccdc_dev->hw_ops.get_line_length();
+ vpfe_dev->fmt.fmt.pix.sizeimage =
+ vpfe_dev->fmt.fmt.pix.bytesperline *
+ vpfe_dev->fmt.fmt.pix.height;
+ vpfe_dev->crop = crop->c;
+unlock_out:
+ mutex_unlock(&vpfe_dev->lock);
+ return ret;
+}
+
+
+static long vpfe_param_handler(struct file *file, void *priv,
+ bool valid_prio, int cmd, void *param)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ int ret = 0;
+
+ v4l2_dbg(2, debug, &vpfe_dev->v4l2_dev, "vpfe_param_handler\n");
+
+ if (vpfe_dev->started) {
+ /* only allowed if streaming is not started */
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
+ "device already started\n");
+ return -EBUSY;
+ }
+
+ ret = mutex_lock_interruptible(&vpfe_dev->lock);
+ if (ret)
+ return ret;
+
+ switch (cmd) {
+ case VPFE_CMD_S_CCDC_RAW_PARAMS:
+ v4l2_warn(&vpfe_dev->v4l2_dev,
+ "VPFE_CMD_S_CCDC_RAW_PARAMS: experimental ioctl\n");
+ if (ccdc_dev->hw_ops.set_params) {
+ ret = ccdc_dev->hw_ops.set_params(param);
+ if (ret) {
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
+ "Error setting parameters in CCDC\n");
+ goto unlock_out;
+ }
+ if (vpfe_get_ccdc_image_format(vpfe_dev,
+ &vpfe_dev->fmt) < 0) {
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
+ "Invalid image format at CCDC\n");
+ goto unlock_out;
+ }
+ } else {
+ ret = -EINVAL;
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
+ "VPFE_CMD_S_CCDC_RAW_PARAMS not supported\n");
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ }
+unlock_out:
+ mutex_unlock(&vpfe_dev->lock);
+ return ret;
+}
+
+
+/* vpfe capture ioctl operations */
+static const struct v4l2_ioctl_ops vpfe_ioctl_ops = {
+ .vidioc_querycap = vpfe_querycap,
+ .vidioc_g_fmt_vid_cap = vpfe_g_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_cap = vpfe_enum_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vpfe_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vpfe_try_fmt_vid_cap,
+ .vidioc_enum_input = vpfe_enum_input,
+ .vidioc_g_input = vpfe_g_input,
+ .vidioc_s_input = vpfe_s_input,
+ .vidioc_querystd = vpfe_querystd,
+ .vidioc_s_std = vpfe_s_std,
+ .vidioc_g_std = vpfe_g_std,
+ .vidioc_queryctrl = vpfe_queryctrl,
+ .vidioc_g_ctrl = vpfe_g_ctrl,
+ .vidioc_s_ctrl = vpfe_s_ctrl,
+ .vidioc_reqbufs = vpfe_reqbufs,
+ .vidioc_querybuf = vpfe_querybuf,
+ .vidioc_qbuf = vpfe_qbuf,
+ .vidioc_dqbuf = vpfe_dqbuf,
+ .vidioc_streamon = vpfe_streamon,
+ .vidioc_streamoff = vpfe_streamoff,
+ .vidioc_cropcap = vpfe_cropcap,
+ .vidioc_g_crop = vpfe_g_crop,
+ .vidioc_s_crop = vpfe_s_crop,
+ .vidioc_default = vpfe_param_handler,
+};
+
+static struct vpfe_device *vpfe_initialize(void)
+{
+ struct vpfe_device *vpfe_dev;
+
+ /* Default number of buffers should be 3 */
+ if ((numbuffers > 0) &&
+ (numbuffers < config_params.min_numbuffers))
+ numbuffers = config_params.min_numbuffers;
+
+ /*
+ * Set buffer size to min buffers size if invalid buffer size is
+ * given
+ */
+ if (bufsize < config_params.min_bufsize)
+ bufsize = config_params.min_bufsize;
+
+ config_params.numbuffers = numbuffers;
+
+ if (numbuffers)
+ config_params.device_bufsize = bufsize;
+
+ /* Allocate memory for device objects */
+ vpfe_dev = kzalloc(sizeof(*vpfe_dev), GFP_KERNEL);
+
+ return vpfe_dev;
+}
+
+/*
+ * vpfe_probe : This function creates device entries by register
+ * itself to the V4L2 driver and initializes fields of each
+ * device objects
+ */
+static __init int vpfe_probe(struct platform_device *pdev)
+{
+ struct vpfe_subdev_info *sdinfo;
+ struct vpfe_config *vpfe_cfg;
+ struct resource *res1;
+ struct vpfe_device *vpfe_dev;
+ struct i2c_adapter *i2c_adap;
+ struct video_device *vfd;
+ int ret = -ENOMEM, i, j;
+ int num_subdevs = 0;
+
+ /* Get the pointer to the device object */
+ vpfe_dev = vpfe_initialize();
+
+ if (!vpfe_dev) {
+ v4l2_err(pdev->dev.driver,
+ "Failed to allocate memory for vpfe_dev\n");
+ return ret;
+ }
+
+ vpfe_dev->pdev = &pdev->dev;
+
+ if (NULL == pdev->dev.platform_data) {
+ v4l2_err(pdev->dev.driver, "Unable to get vpfe config\n");
+ ret = -ENODEV;
+ goto probe_free_dev_mem;
+ }
+
+ vpfe_cfg = pdev->dev.platform_data;
+ vpfe_dev->cfg = vpfe_cfg;
+ if (NULL == vpfe_cfg->ccdc ||
+ NULL == vpfe_cfg->card_name ||
+ NULL == vpfe_cfg->sub_devs) {
+ v4l2_err(pdev->dev.driver, "null ptr in vpfe_cfg\n");
+ ret = -ENOENT;
+ goto probe_free_dev_mem;
+ }
+
+ /* Allocate memory for ccdc configuration */
+ ccdc_cfg = kmalloc(sizeof(struct ccdc_config), GFP_KERNEL);
+ if (NULL == ccdc_cfg) {
+ v4l2_err(pdev->dev.driver,
+ "Memory allocation failed for ccdc_cfg\n");
+ goto probe_free_lock;
+ }
+
+ mutex_lock(&ccdc_lock);
+
+ strncpy(ccdc_cfg->name, vpfe_cfg->ccdc, 32);
+ /* Get VINT0 irq resource */
+ res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res1) {
+ v4l2_err(pdev->dev.driver,
+ "Unable to get interrupt for VINT0\n");
+ ret = -ENODEV;
+ goto probe_free_ccdc_cfg_mem;
+ }
+ vpfe_dev->ccdc_irq0 = res1->start;
+
+ /* Get VINT1 irq resource */
+ res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ if (!res1) {
+ v4l2_err(pdev->dev.driver,
+ "Unable to get interrupt for VINT1\n");
+ ret = -ENODEV;
+ goto probe_free_ccdc_cfg_mem;
+ }
+ vpfe_dev->ccdc_irq1 = res1->start;
+
+ ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, IRQF_DISABLED,
+ "vpfe_capture0", vpfe_dev);
+
+ if (0 != ret) {
+ v4l2_err(pdev->dev.driver, "Unable to request interrupt\n");
+ goto probe_free_ccdc_cfg_mem;
+ }
+
+ /* Allocate memory for video device */
+ vfd = video_device_alloc();
+ if (NULL == vfd) {
+ ret = -ENOMEM;
+ v4l2_err(pdev->dev.driver, "Unable to alloc video device\n");
+ goto probe_out_release_irq;
+ }
+
+ /* Initialize field of video device */
+ vfd->release = video_device_release;
+ vfd->fops = &vpfe_fops;
+ vfd->ioctl_ops = &vpfe_ioctl_ops;
+ vfd->tvnorms = 0;
+ vfd->current_norm = V4L2_STD_PAL;
+ vfd->v4l2_dev = &vpfe_dev->v4l2_dev;
+ snprintf(vfd->name, sizeof(vfd->name),
+ "%s_V%d.%d.%d",
+ CAPTURE_DRV_NAME,
+ (VPFE_CAPTURE_VERSION_CODE >> 16) & 0xff,
+ (VPFE_CAPTURE_VERSION_CODE >> 8) & 0xff,
+ (VPFE_CAPTURE_VERSION_CODE) & 0xff);
+ /* Set video_dev to the video device */
+ vpfe_dev->video_dev = vfd;
+
+ ret = v4l2_device_register(&pdev->dev, &vpfe_dev->v4l2_dev);
+ if (ret) {
+ v4l2_err(pdev->dev.driver,
+ "Unable to register v4l2 device.\n");
+ goto probe_out_video_release;
+ }
+ v4l2_info(&vpfe_dev->v4l2_dev, "v4l2 device registered\n");
+ spin_lock_init(&vpfe_dev->irqlock);
+ spin_lock_init(&vpfe_dev->dma_queue_lock);
+ mutex_init(&vpfe_dev->lock);
+
+ /* Initialize field of the device objects */
+ vpfe_dev->numbuffers = config_params.numbuffers;
+
+ /* Initialize prio member of device object */
+ v4l2_prio_init(&vpfe_dev->prio);
+ /* register video device */
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
+ "trying to register vpfe device.\n");
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
+ "video_dev=%x\n", (int)&vpfe_dev->video_dev);
+ vpfe_dev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ ret = video_register_device(vpfe_dev->video_dev,
+ VFL_TYPE_GRABBER, -1);
+
+ if (ret) {
+ v4l2_err(pdev->dev.driver,
+ "Unable to register video device.\n");
+ goto probe_out_v4l2_unregister;
+ }
+
+ v4l2_info(&vpfe_dev->v4l2_dev, "video device registered\n");
+ /* set the driver data in platform device */
+ platform_set_drvdata(pdev, vpfe_dev);
+ /* set driver private data */
+ video_set_drvdata(vpfe_dev->video_dev, vpfe_dev);
+ i2c_adap = i2c_get_adapter(vpfe_cfg->i2c_adapter_id);
+ num_subdevs = vpfe_cfg->num_subdevs;
+ vpfe_dev->sd = kmalloc(sizeof(struct v4l2_subdev *) * num_subdevs,
+ GFP_KERNEL);
+ if (NULL == vpfe_dev->sd) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "unable to allocate memory for subdevice pointers\n");
+ ret = -ENOMEM;
+ goto probe_out_video_unregister;
+ }
+
+ for (i = 0; i < num_subdevs; i++) {
+ struct v4l2_input *inps;
+
+ sdinfo = &vpfe_cfg->sub_devs[i];
+
+ /* Load up the subdevice */
+ vpfe_dev->sd[i] =
+ v4l2_i2c_new_subdev_board(&vpfe_dev->v4l2_dev,
+ i2c_adap,
+ &sdinfo->board_info,
+ NULL);
+ if (vpfe_dev->sd[i]) {
+ v4l2_info(&vpfe_dev->v4l2_dev,
+ "v4l2 sub device %s registered\n",
+ sdinfo->name);
+ vpfe_dev->sd[i]->grp_id = sdinfo->grp_id;
+ /* update tvnorms from the sub devices */
+ for (j = 0; j < sdinfo->num_inputs; j++) {
+ inps = &sdinfo->inputs[j];
+ vfd->tvnorms |= inps->std;
+ }
+ } else {
+ v4l2_info(&vpfe_dev->v4l2_dev,
+ "v4l2 sub device %s register fails\n",
+ sdinfo->name);
+ goto probe_sd_out;
+ }
+ }
+
+ /* set first sub device as current one */
+ vpfe_dev->current_subdev = &vpfe_cfg->sub_devs[0];
+
+ /* We have at least one sub device to work with */
+ mutex_unlock(&ccdc_lock);
+ return 0;
+
+probe_sd_out:
+ kfree(vpfe_dev->sd);
+probe_out_video_unregister:
+ video_unregister_device(vpfe_dev->video_dev);
+probe_out_v4l2_unregister:
+ v4l2_device_unregister(&vpfe_dev->v4l2_dev);
+probe_out_video_release:
+ if (!video_is_registered(vpfe_dev->video_dev))
+ video_device_release(vpfe_dev->video_dev);
+probe_out_release_irq:
+ free_irq(vpfe_dev->ccdc_irq0, vpfe_dev);
+probe_free_ccdc_cfg_mem:
+ kfree(ccdc_cfg);
+probe_free_lock:
+ mutex_unlock(&ccdc_lock);
+probe_free_dev_mem:
+ kfree(vpfe_dev);
+ return ret;
+}
+
+/*
+ * vpfe_remove : It un-register device from V4L2 driver
+ */
+static int __devexit vpfe_remove(struct platform_device *pdev)
+{
+ struct vpfe_device *vpfe_dev = platform_get_drvdata(pdev);
+
+ v4l2_info(pdev->dev.driver, "vpfe_remove\n");
+
+ free_irq(vpfe_dev->ccdc_irq0, vpfe_dev);
+ kfree(vpfe_dev->sd);
+ v4l2_device_unregister(&vpfe_dev->v4l2_dev);
+ video_unregister_device(vpfe_dev->video_dev);
+ kfree(vpfe_dev);
+ kfree(ccdc_cfg);
+ return 0;
+}
+
+static int vpfe_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int vpfe_resume(struct device *dev)
+{
+ return 0;
+}
+
+static const struct dev_pm_ops vpfe_dev_pm_ops = {
+ .suspend = vpfe_suspend,
+ .resume = vpfe_resume,
+};
+
+static struct platform_driver vpfe_driver = {
+ .driver = {
+ .name = CAPTURE_DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &vpfe_dev_pm_ops,
+ },
+ .probe = vpfe_probe,
+ .remove = __devexit_p(vpfe_remove),
+};
+
+module_platform_driver(vpfe_driver);
diff --git a/drivers/media/video/davinci/vpif.c b/drivers/media/video/davinci/vpif.c
new file mode 100644
index 00000000..af968027
--- /dev/null
+++ b/drivers/media/video/davinci/vpif.c
@@ -0,0 +1,473 @@
+/*
+ * vpif - DM646x Video Port Interface driver
+ * VPIF is a receiver and transmitter for video data. It has two channels(0, 1)
+ * that receiveing video byte stream and two channels(2, 3) for video output.
+ * The hardware supports SDTV, HDTV formats, raw data capture.
+ * Currently, the driver supports NTSC and PAL standards.
+ *
+ * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed .as is. WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
+
+#include "vpif.h"
+
+MODULE_DESCRIPTION("TI DaVinci Video Port Interface driver");
+MODULE_LICENSE("GPL");
+
+#define VPIF_CH0_MAX_MODES (22)
+#define VPIF_CH1_MAX_MODES (02)
+#define VPIF_CH2_MAX_MODES (15)
+#define VPIF_CH3_MAX_MODES (02)
+
+static resource_size_t res_len;
+static struct resource *res;
+spinlock_t vpif_lock;
+
+void __iomem *vpif_base;
+
+/**
+ * ch_params: video standard configuration parameters for vpif
+ * The table must include all presets from supported subdevices.
+ */
+const struct vpif_channel_config_params ch_params[] = {
+ /* HDTV formats */
+ {
+ .name = "480p59_94",
+ .width = 720,
+ .height = 480,
+ .frm_fmt = 1,
+ .ycmux_mode = 0,
+ .eav2sav = 138-8,
+ .sav2eav = 720,
+ .l1 = 1,
+ .l3 = 43,
+ .l5 = 523,
+ .vsize = 525,
+ .capture_format = 0,
+ .vbi_supported = 0,
+ .hd_sd = 1,
+ .dv_preset = V4L2_DV_480P59_94,
+ },
+ {
+ .name = "576p50",
+ .width = 720,
+ .height = 576,
+ .frm_fmt = 1,
+ .ycmux_mode = 0,
+ .eav2sav = 144-8,
+ .sav2eav = 720,
+ .l1 = 1,
+ .l3 = 45,
+ .l5 = 621,
+ .vsize = 625,
+ .capture_format = 0,
+ .vbi_supported = 0,
+ .hd_sd = 1,
+ .dv_preset = V4L2_DV_576P50,
+ },
+ {
+ .name = "720p50",
+ .width = 1280,
+ .height = 720,
+ .frm_fmt = 1,
+ .ycmux_mode = 0,
+ .eav2sav = 700-8,
+ .sav2eav = 1280,
+ .l1 = 1,
+ .l3 = 26,
+ .l5 = 746,
+ .vsize = 750,
+ .capture_format = 0,
+ .vbi_supported = 0,
+ .hd_sd = 1,
+ .dv_preset = V4L2_DV_720P50,
+ },
+ {
+ .name = "720p60",
+ .width = 1280,
+ .height = 720,
+ .frm_fmt = 1,
+ .ycmux_mode = 0,
+ .eav2sav = 370 - 8,
+ .sav2eav = 1280,
+ .l1 = 1,
+ .l3 = 26,
+ .l5 = 746,
+ .vsize = 750,
+ .capture_format = 0,
+ .vbi_supported = 0,
+ .hd_sd = 1,
+ .dv_preset = V4L2_DV_720P60,
+ },
+ {
+ .name = "1080I50",
+ .width = 1920,
+ .height = 1080,
+ .frm_fmt = 0,
+ .ycmux_mode = 0,
+ .eav2sav = 720 - 8,
+ .sav2eav = 1920,
+ .l1 = 1,
+ .l3 = 21,
+ .l5 = 561,
+ .l7 = 563,
+ .l9 = 584,
+ .l11 = 1124,
+ .vsize = 1125,
+ .capture_format = 0,
+ .vbi_supported = 0,
+ .hd_sd = 1,
+ .dv_preset = V4L2_DV_1080I50,
+ },
+ {
+ .name = "1080I60",
+ .width = 1920,
+ .height = 1080,
+ .frm_fmt = 0,
+ .ycmux_mode = 0,
+ .eav2sav = 280 - 8,
+ .sav2eav = 1920,
+ .l1 = 1,
+ .l3 = 21,
+ .l5 = 561,
+ .l7 = 563,
+ .l9 = 584,
+ .l11 = 1124,
+ .vsize = 1125,
+ .capture_format = 0,
+ .vbi_supported = 0,
+ .hd_sd = 1,
+ .dv_preset = V4L2_DV_1080I60,
+ },
+ {
+ .name = "1080p60",
+ .width = 1920,
+ .height = 1080,
+ .frm_fmt = 1,
+ .ycmux_mode = 0,
+ .eav2sav = 280 - 8,
+ .sav2eav = 1920,
+ .l1 = 1,
+ .l3 = 42,
+ .l5 = 1122,
+ .vsize = 1125,
+ .capture_format = 0,
+ .vbi_supported = 0,
+ .hd_sd = 1,
+ .dv_preset = V4L2_DV_1080P60,
+ },
+
+ /* SDTV formats */
+ {
+ .name = "NTSC_M",
+ .width = 720,
+ .height = 480,
+ .frm_fmt = 0,
+ .ycmux_mode = 1,
+ .eav2sav = 268,
+ .sav2eav = 1440,
+ .l1 = 1,
+ .l3 = 23,
+ .l5 = 263,
+ .l7 = 266,
+ .l9 = 286,
+ .l11 = 525,
+ .vsize = 525,
+ .capture_format = 0,
+ .vbi_supported = 1,
+ .hd_sd = 0,
+ .stdid = V4L2_STD_525_60,
+ },
+ {
+ .name = "PAL_BDGHIK",
+ .width = 720,
+ .height = 576,
+ .frm_fmt = 0,
+ .ycmux_mode = 1,
+ .eav2sav = 280,
+ .sav2eav = 1440,
+ .l1 = 1,
+ .l3 = 23,
+ .l5 = 311,
+ .l7 = 313,
+ .l9 = 336,
+ .l11 = 624,
+ .vsize = 625,
+ .capture_format = 0,
+ .vbi_supported = 1,
+ .hd_sd = 0,
+ .stdid = V4L2_STD_625_50,
+ },
+};
+
+const unsigned int vpif_ch_params_count = ARRAY_SIZE(ch_params);
+
+static inline void vpif_wr_bit(u32 reg, u32 bit, u32 val)
+{
+ if (val)
+ vpif_set_bit(reg, bit);
+ else
+ vpif_clr_bit(reg, bit);
+}
+
+/* This structure is used to keep track of VPIF size register's offsets */
+struct vpif_registers {
+ u32 h_cfg, v_cfg_00, v_cfg_01, v_cfg_02, v_cfg, ch_ctrl;
+ u32 line_offset, vanc0_strt, vanc0_size, vanc1_strt;
+ u32 vanc1_size, width_mask, len_mask;
+ u8 max_modes;
+};
+
+static const struct vpif_registers vpifregs[VPIF_NUM_CHANNELS] = {
+ /* Channel0 */
+ {
+ VPIF_CH0_H_CFG, VPIF_CH0_V_CFG_00, VPIF_CH0_V_CFG_01,
+ VPIF_CH0_V_CFG_02, VPIF_CH0_V_CFG_03, VPIF_CH0_CTRL,
+ VPIF_CH0_IMG_ADD_OFST, 0, 0, 0, 0, 0x1FFF, 0xFFF,
+ VPIF_CH0_MAX_MODES,
+ },
+ /* Channel1 */
+ {
+ VPIF_CH1_H_CFG, VPIF_CH1_V_CFG_00, VPIF_CH1_V_CFG_01,
+ VPIF_CH1_V_CFG_02, VPIF_CH1_V_CFG_03, VPIF_CH1_CTRL,
+ VPIF_CH1_IMG_ADD_OFST, 0, 0, 0, 0, 0x1FFF, 0xFFF,
+ VPIF_CH1_MAX_MODES,
+ },
+ /* Channel2 */
+ {
+ VPIF_CH2_H_CFG, VPIF_CH2_V_CFG_00, VPIF_CH2_V_CFG_01,
+ VPIF_CH2_V_CFG_02, VPIF_CH2_V_CFG_03, VPIF_CH2_CTRL,
+ VPIF_CH2_IMG_ADD_OFST, VPIF_CH2_VANC0_STRT, VPIF_CH2_VANC0_SIZE,
+ VPIF_CH2_VANC1_STRT, VPIF_CH2_VANC1_SIZE, 0x7FF, 0x7FF,
+ VPIF_CH2_MAX_MODES
+ },
+ /* Channel3 */
+ {
+ VPIF_CH3_H_CFG, VPIF_CH3_V_CFG_00, VPIF_CH3_V_CFG_01,
+ VPIF_CH3_V_CFG_02, VPIF_CH3_V_CFG_03, VPIF_CH3_CTRL,
+ VPIF_CH3_IMG_ADD_OFST, VPIF_CH3_VANC0_STRT, VPIF_CH3_VANC0_SIZE,
+ VPIF_CH3_VANC1_STRT, VPIF_CH3_VANC1_SIZE, 0x7FF, 0x7FF,
+ VPIF_CH3_MAX_MODES
+ },
+};
+
+/* vpif_set_mode_info:
+ * This function is used to set horizontal and vertical config parameters
+ * As per the standard in the channel, configure the values of L1, L3,
+ * L5, L7 L9, L11 in VPIF Register , also write width and height
+ */
+static void vpif_set_mode_info(const struct vpif_channel_config_params *config,
+ u8 channel_id, u8 config_channel_id)
+{
+ u32 value;
+
+ value = (config->eav2sav & vpifregs[config_channel_id].width_mask);
+ value <<= VPIF_CH_LEN_SHIFT;
+ value |= (config->sav2eav & vpifregs[config_channel_id].width_mask);
+ regw(value, vpifregs[channel_id].h_cfg);
+
+ value = (config->l1 & vpifregs[config_channel_id].len_mask);
+ value <<= VPIF_CH_LEN_SHIFT;
+ value |= (config->l3 & vpifregs[config_channel_id].len_mask);
+ regw(value, vpifregs[channel_id].v_cfg_00);
+
+ value = (config->l5 & vpifregs[config_channel_id].len_mask);
+ value <<= VPIF_CH_LEN_SHIFT;
+ value |= (config->l7 & vpifregs[config_channel_id].len_mask);
+ regw(value, vpifregs[channel_id].v_cfg_01);
+
+ value = (config->l9 & vpifregs[config_channel_id].len_mask);
+ value <<= VPIF_CH_LEN_SHIFT;
+ value |= (config->l11 & vpifregs[config_channel_id].len_mask);
+ regw(value, vpifregs[channel_id].v_cfg_02);
+
+ value = (config->vsize & vpifregs[config_channel_id].len_mask);
+ regw(value, vpifregs[channel_id].v_cfg);
+}
+
+/* config_vpif_params
+ * Function to set the parameters of a channel
+ * Mainly modifies the channel ciontrol register
+ * It sets frame format, yc mux mode
+ */
+static void config_vpif_params(struct vpif_params *vpifparams,
+ u8 channel_id, u8 found)
+{
+ const struct vpif_channel_config_params *config = &vpifparams->std_info;
+ u32 value, ch_nip, reg;
+ u8 start, end;
+ int i;
+
+ start = channel_id;
+ end = channel_id + found;
+
+ for (i = start; i < end; i++) {
+ reg = vpifregs[i].ch_ctrl;
+ if (channel_id < 2)
+ ch_nip = VPIF_CAPTURE_CH_NIP;
+ else
+ ch_nip = VPIF_DISPLAY_CH_NIP;
+
+ vpif_wr_bit(reg, ch_nip, config->frm_fmt);
+ vpif_wr_bit(reg, VPIF_CH_YC_MUX_BIT, config->ycmux_mode);
+ vpif_wr_bit(reg, VPIF_CH_INPUT_FIELD_FRAME_BIT,
+ vpifparams->video_params.storage_mode);
+
+ /* Set raster scanning SDR Format */
+ vpif_clr_bit(reg, VPIF_CH_SDR_FMT_BIT);
+ vpif_wr_bit(reg, VPIF_CH_DATA_MODE_BIT, config->capture_format);
+
+ if (channel_id > 1) /* Set the Pixel enable bit */
+ vpif_set_bit(reg, VPIF_DISPLAY_PIX_EN_BIT);
+ else if (config->capture_format) {
+ /* Set the polarity of various pins */
+ vpif_wr_bit(reg, VPIF_CH_FID_POLARITY_BIT,
+ vpifparams->iface.fid_pol);
+ vpif_wr_bit(reg, VPIF_CH_V_VALID_POLARITY_BIT,
+ vpifparams->iface.vd_pol);
+ vpif_wr_bit(reg, VPIF_CH_H_VALID_POLARITY_BIT,
+ vpifparams->iface.hd_pol);
+
+ value = regr(reg);
+ /* Set data width */
+ value &= ((~(unsigned int)(0x3)) <<
+ VPIF_CH_DATA_WIDTH_BIT);
+ value |= ((vpifparams->params.data_sz) <<
+ VPIF_CH_DATA_WIDTH_BIT);
+ regw(value, reg);
+ }
+
+ /* Write the pitch in the driver */
+ regw((vpifparams->video_params.hpitch),
+ vpifregs[i].line_offset);
+ }
+}
+
+/* vpif_set_video_params
+ * This function is used to set video parameters in VPIF register
+ */
+int vpif_set_video_params(struct vpif_params *vpifparams, u8 channel_id)
+{
+ const struct vpif_channel_config_params *config = &vpifparams->std_info;
+ int found = 1;
+
+ vpif_set_mode_info(config, channel_id, channel_id);
+ if (!config->ycmux_mode) {
+ /* YC are on separate channels (HDTV formats) */
+ vpif_set_mode_info(config, channel_id + 1, channel_id);
+ found = 2;
+ }
+
+ config_vpif_params(vpifparams, channel_id, found);
+
+ regw(0x80, VPIF_REQ_SIZE);
+ regw(0x01, VPIF_EMULATION_CTRL);
+
+ return found;
+}
+EXPORT_SYMBOL(vpif_set_video_params);
+
+void vpif_set_vbi_display_params(struct vpif_vbi_params *vbiparams,
+ u8 channel_id)
+{
+ u32 value;
+
+ value = 0x3F8 & (vbiparams->hstart0);
+ value |= 0x3FFFFFF & ((vbiparams->vstart0) << 16);
+ regw(value, vpifregs[channel_id].vanc0_strt);
+
+ value = 0x3F8 & (vbiparams->hstart1);
+ value |= 0x3FFFFFF & ((vbiparams->vstart1) << 16);
+ regw(value, vpifregs[channel_id].vanc1_strt);
+
+ value = 0x3F8 & (vbiparams->hsize0);
+ value |= 0x3FFFFFF & ((vbiparams->vsize0) << 16);
+ regw(value, vpifregs[channel_id].vanc0_size);
+
+ value = 0x3F8 & (vbiparams->hsize1);
+ value |= 0x3FFFFFF & ((vbiparams->vsize1) << 16);
+ regw(value, vpifregs[channel_id].vanc1_size);
+
+}
+EXPORT_SYMBOL(vpif_set_vbi_display_params);
+
+int vpif_channel_getfid(u8 channel_id)
+{
+ return (regr(vpifregs[channel_id].ch_ctrl) & VPIF_CH_FID_MASK)
+ >> VPIF_CH_FID_SHIFT;
+}
+EXPORT_SYMBOL(vpif_channel_getfid);
+
+static int __init vpif_probe(struct platform_device *pdev)
+{
+ int status = 0;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENOENT;
+
+ res_len = resource_size(res);
+
+ res = request_mem_region(res->start, res_len, res->name);
+ if (!res)
+ return -EBUSY;
+
+ vpif_base = ioremap(res->start, res_len);
+ if (!vpif_base) {
+ status = -EBUSY;
+ goto fail;
+ }
+
+ spin_lock_init(&vpif_lock);
+ dev_info(&pdev->dev, "vpif probe success\n");
+ return 0;
+
+fail:
+ release_mem_region(res->start, res_len);
+ return status;
+}
+
+static int __devexit vpif_remove(struct platform_device *pdev)
+{
+ iounmap(vpif_base);
+ release_mem_region(res->start, res_len);
+ return 0;
+}
+
+static struct platform_driver vpif_driver = {
+ .driver = {
+ .name = "vpif",
+ .owner = THIS_MODULE,
+ },
+ .remove = __devexit_p(vpif_remove),
+ .probe = vpif_probe,
+};
+
+static void vpif_exit(void)
+{
+ platform_driver_unregister(&vpif_driver);
+}
+
+static int __init vpif_init(void)
+{
+ return platform_driver_register(&vpif_driver);
+}
+subsys_initcall(vpif_init);
+module_exit(vpif_exit);
+
diff --git a/drivers/media/video/davinci/vpif.h b/drivers/media/video/davinci/vpif.h
new file mode 100644
index 00000000..8bcac65f
--- /dev/null
+++ b/drivers/media/video/davinci/vpif.h
@@ -0,0 +1,643 @@
+/*
+ * VPIF header file
+ *
+ * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed .as is. WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef VPIF_H
+#define VPIF_H
+
+#include <linux/io.h>
+#include <linux/videodev2.h>
+#include <media/davinci/vpif_types.h>
+
+/* Maximum channel allowed */
+#define VPIF_NUM_CHANNELS (4)
+#define VPIF_CAPTURE_NUM_CHANNELS (2)
+#define VPIF_DISPLAY_NUM_CHANNELS (2)
+
+/* Macros to read/write registers */
+extern void __iomem *vpif_base;
+extern spinlock_t vpif_lock;
+
+#define regr(reg) readl((reg) + vpif_base)
+#define regw(value, reg) writel(value, (reg + vpif_base))
+
+/* Register Address Offsets */
+#define VPIF_PID (0x0000)
+#define VPIF_CH0_CTRL (0x0004)
+#define VPIF_CH1_CTRL (0x0008)
+#define VPIF_CH2_CTRL (0x000C)
+#define VPIF_CH3_CTRL (0x0010)
+
+#define VPIF_INTEN (0x0020)
+#define VPIF_INTEN_SET (0x0024)
+#define VPIF_INTEN_CLR (0x0028)
+#define VPIF_STATUS (0x002C)
+#define VPIF_STATUS_CLR (0x0030)
+#define VPIF_EMULATION_CTRL (0x0034)
+#define VPIF_REQ_SIZE (0x0038)
+
+#define VPIF_CH0_TOP_STRT_ADD_LUMA (0x0040)
+#define VPIF_CH0_BTM_STRT_ADD_LUMA (0x0044)
+#define VPIF_CH0_TOP_STRT_ADD_CHROMA (0x0048)
+#define VPIF_CH0_BTM_STRT_ADD_CHROMA (0x004c)
+#define VPIF_CH0_TOP_STRT_ADD_HANC (0x0050)
+#define VPIF_CH0_BTM_STRT_ADD_HANC (0x0054)
+#define VPIF_CH0_TOP_STRT_ADD_VANC (0x0058)
+#define VPIF_CH0_BTM_STRT_ADD_VANC (0x005c)
+#define VPIF_CH0_SP_CFG (0x0060)
+#define VPIF_CH0_IMG_ADD_OFST (0x0064)
+#define VPIF_CH0_HANC_ADD_OFST (0x0068)
+#define VPIF_CH0_H_CFG (0x006c)
+#define VPIF_CH0_V_CFG_00 (0x0070)
+#define VPIF_CH0_V_CFG_01 (0x0074)
+#define VPIF_CH0_V_CFG_02 (0x0078)
+#define VPIF_CH0_V_CFG_03 (0x007c)
+
+#define VPIF_CH1_TOP_STRT_ADD_LUMA (0x0080)
+#define VPIF_CH1_BTM_STRT_ADD_LUMA (0x0084)
+#define VPIF_CH1_TOP_STRT_ADD_CHROMA (0x0088)
+#define VPIF_CH1_BTM_STRT_ADD_CHROMA (0x008c)
+#define VPIF_CH1_TOP_STRT_ADD_HANC (0x0090)
+#define VPIF_CH1_BTM_STRT_ADD_HANC (0x0094)
+#define VPIF_CH1_TOP_STRT_ADD_VANC (0x0098)
+#define VPIF_CH1_BTM_STRT_ADD_VANC (0x009c)
+#define VPIF_CH1_SP_CFG (0x00a0)
+#define VPIF_CH1_IMG_ADD_OFST (0x00a4)
+#define VPIF_CH1_HANC_ADD_OFST (0x00a8)
+#define VPIF_CH1_H_CFG (0x00ac)
+#define VPIF_CH1_V_CFG_00 (0x00b0)
+#define VPIF_CH1_V_CFG_01 (0x00b4)
+#define VPIF_CH1_V_CFG_02 (0x00b8)
+#define VPIF_CH1_V_CFG_03 (0x00bc)
+
+#define VPIF_CH2_TOP_STRT_ADD_LUMA (0x00c0)
+#define VPIF_CH2_BTM_STRT_ADD_LUMA (0x00c4)
+#define VPIF_CH2_TOP_STRT_ADD_CHROMA (0x00c8)
+#define VPIF_CH2_BTM_STRT_ADD_CHROMA (0x00cc)
+#define VPIF_CH2_TOP_STRT_ADD_HANC (0x00d0)
+#define VPIF_CH2_BTM_STRT_ADD_HANC (0x00d4)
+#define VPIF_CH2_TOP_STRT_ADD_VANC (0x00d8)
+#define VPIF_CH2_BTM_STRT_ADD_VANC (0x00dc)
+#define VPIF_CH2_SP_CFG (0x00e0)
+#define VPIF_CH2_IMG_ADD_OFST (0x00e4)
+#define VPIF_CH2_HANC_ADD_OFST (0x00e8)
+#define VPIF_CH2_H_CFG (0x00ec)
+#define VPIF_CH2_V_CFG_00 (0x00f0)
+#define VPIF_CH2_V_CFG_01 (0x00f4)
+#define VPIF_CH2_V_CFG_02 (0x00f8)
+#define VPIF_CH2_V_CFG_03 (0x00fc)
+#define VPIF_CH2_HANC0_STRT (0x0100)
+#define VPIF_CH2_HANC0_SIZE (0x0104)
+#define VPIF_CH2_HANC1_STRT (0x0108)
+#define VPIF_CH2_HANC1_SIZE (0x010c)
+#define VPIF_CH2_VANC0_STRT (0x0110)
+#define VPIF_CH2_VANC0_SIZE (0x0114)
+#define VPIF_CH2_VANC1_STRT (0x0118)
+#define VPIF_CH2_VANC1_SIZE (0x011c)
+
+#define VPIF_CH3_TOP_STRT_ADD_LUMA (0x0140)
+#define VPIF_CH3_BTM_STRT_ADD_LUMA (0x0144)
+#define VPIF_CH3_TOP_STRT_ADD_CHROMA (0x0148)
+#define VPIF_CH3_BTM_STRT_ADD_CHROMA (0x014c)
+#define VPIF_CH3_TOP_STRT_ADD_HANC (0x0150)
+#define VPIF_CH3_BTM_STRT_ADD_HANC (0x0154)
+#define VPIF_CH3_TOP_STRT_ADD_VANC (0x0158)
+#define VPIF_CH3_BTM_STRT_ADD_VANC (0x015c)
+#define VPIF_CH3_SP_CFG (0x0160)
+#define VPIF_CH3_IMG_ADD_OFST (0x0164)
+#define VPIF_CH3_HANC_ADD_OFST (0x0168)
+#define VPIF_CH3_H_CFG (0x016c)
+#define VPIF_CH3_V_CFG_00 (0x0170)
+#define VPIF_CH3_V_CFG_01 (0x0174)
+#define VPIF_CH3_V_CFG_02 (0x0178)
+#define VPIF_CH3_V_CFG_03 (0x017c)
+#define VPIF_CH3_HANC0_STRT (0x0180)
+#define VPIF_CH3_HANC0_SIZE (0x0184)
+#define VPIF_CH3_HANC1_STRT (0x0188)
+#define VPIF_CH3_HANC1_SIZE (0x018c)
+#define VPIF_CH3_VANC0_STRT (0x0190)
+#define VPIF_CH3_VANC0_SIZE (0x0194)
+#define VPIF_CH3_VANC1_STRT (0x0198)
+#define VPIF_CH3_VANC1_SIZE (0x019c)
+
+#define VPIF_IODFT_CTRL (0x01c0)
+
+/* Functions for bit Manipulation */
+static inline void vpif_set_bit(u32 reg, u32 bit)
+{
+ regw((regr(reg)) | (0x01 << bit), reg);
+}
+
+static inline void vpif_clr_bit(u32 reg, u32 bit)
+{
+ regw(((regr(reg)) & ~(0x01 << bit)), reg);
+}
+
+/* Macro for Generating mask */
+#ifdef GENERATE_MASK
+#undef GENERATE_MASK
+#endif
+
+#define GENERATE_MASK(bits, pos) \
+ ((((0xFFFFFFFF) << (32 - bits)) >> (32 - bits)) << pos)
+
+/* Bit positions in the channel control registers */
+#define VPIF_CH_DATA_MODE_BIT (2)
+#define VPIF_CH_YC_MUX_BIT (3)
+#define VPIF_CH_SDR_FMT_BIT (4)
+#define VPIF_CH_HANC_EN_BIT (8)
+#define VPIF_CH_VANC_EN_BIT (9)
+
+#define VPIF_CAPTURE_CH_NIP (10)
+#define VPIF_DISPLAY_CH_NIP (11)
+
+#define VPIF_DISPLAY_PIX_EN_BIT (10)
+
+#define VPIF_CH_INPUT_FIELD_FRAME_BIT (12)
+
+#define VPIF_CH_FID_POLARITY_BIT (15)
+#define VPIF_CH_V_VALID_POLARITY_BIT (14)
+#define VPIF_CH_H_VALID_POLARITY_BIT (13)
+#define VPIF_CH_DATA_WIDTH_BIT (28)
+
+#define VPIF_CH_CLK_EDGE_CTRL_BIT (31)
+
+/* Mask various length */
+#define VPIF_CH_EAVSAV_MASK GENERATE_MASK(13, 0)
+#define VPIF_CH_LEN_MASK GENERATE_MASK(12, 0)
+#define VPIF_CH_WIDTH_MASK GENERATE_MASK(13, 0)
+#define VPIF_CH_LEN_SHIFT (16)
+
+/* VPIF masks for registers */
+#define VPIF_REQ_SIZE_MASK (0x1ff)
+
+/* bit posotion of interrupt vpif_ch_intr register */
+#define VPIF_INTEN_FRAME_CH0 (0x00000001)
+#define VPIF_INTEN_FRAME_CH1 (0x00000002)
+#define VPIF_INTEN_FRAME_CH2 (0x00000004)
+#define VPIF_INTEN_FRAME_CH3 (0x00000008)
+
+/* bit position of clock and channel enable in vpif_chn_ctrl register */
+
+#define VPIF_CH0_CLK_EN (0x00000002)
+#define VPIF_CH0_EN (0x00000001)
+#define VPIF_CH1_CLK_EN (0x00000002)
+#define VPIF_CH1_EN (0x00000001)
+#define VPIF_CH2_CLK_EN (0x00000002)
+#define VPIF_CH2_EN (0x00000001)
+#define VPIF_CH3_CLK_EN (0x00000002)
+#define VPIF_CH3_EN (0x00000001)
+#define VPIF_CH_CLK_EN (0x00000002)
+#define VPIF_CH_EN (0x00000001)
+
+#define VPIF_INT_TOP (0x00)
+#define VPIF_INT_BOTTOM (0x01)
+#define VPIF_INT_BOTH (0x02)
+
+#define VPIF_CH0_INT_CTRL_SHIFT (6)
+#define VPIF_CH1_INT_CTRL_SHIFT (6)
+#define VPIF_CH2_INT_CTRL_SHIFT (6)
+#define VPIF_CH3_INT_CTRL_SHIFT (6)
+#define VPIF_CH_INT_CTRL_SHIFT (6)
+
+/* enabled interrupt on both the fields on vpid_ch0_ctrl register */
+#define channel0_intr_assert() (regw((regr(VPIF_CH0_CTRL)|\
+ (VPIF_INT_BOTH << VPIF_CH0_INT_CTRL_SHIFT)), VPIF_CH0_CTRL))
+
+/* enabled interrupt on both the fields on vpid_ch1_ctrl register */
+#define channel1_intr_assert() (regw((regr(VPIF_CH1_CTRL)|\
+ (VPIF_INT_BOTH << VPIF_CH1_INT_CTRL_SHIFT)), VPIF_CH1_CTRL))
+
+/* enabled interrupt on both the fields on vpid_ch0_ctrl register */
+#define channel2_intr_assert() (regw((regr(VPIF_CH2_CTRL)|\
+ (VPIF_INT_BOTH << VPIF_CH2_INT_CTRL_SHIFT)), VPIF_CH2_CTRL))
+
+/* enabled interrupt on both the fields on vpid_ch1_ctrl register */
+#define channel3_intr_assert() (regw((regr(VPIF_CH3_CTRL)|\
+ (VPIF_INT_BOTH << VPIF_CH3_INT_CTRL_SHIFT)), VPIF_CH3_CTRL))
+
+#define VPIF_CH_FID_MASK (0x20)
+#define VPIF_CH_FID_SHIFT (5)
+
+#define VPIF_NTSC_VBI_START_FIELD0 (1)
+#define VPIF_NTSC_VBI_START_FIELD1 (263)
+#define VPIF_PAL_VBI_START_FIELD0 (624)
+#define VPIF_PAL_VBI_START_FIELD1 (311)
+
+#define VPIF_NTSC_HBI_START_FIELD0 (1)
+#define VPIF_NTSC_HBI_START_FIELD1 (263)
+#define VPIF_PAL_HBI_START_FIELD0 (624)
+#define VPIF_PAL_HBI_START_FIELD1 (311)
+
+#define VPIF_NTSC_VBI_COUNT_FIELD0 (20)
+#define VPIF_NTSC_VBI_COUNT_FIELD1 (19)
+#define VPIF_PAL_VBI_COUNT_FIELD0 (24)
+#define VPIF_PAL_VBI_COUNT_FIELD1 (25)
+
+#define VPIF_NTSC_HBI_COUNT_FIELD0 (263)
+#define VPIF_NTSC_HBI_COUNT_FIELD1 (262)
+#define VPIF_PAL_HBI_COUNT_FIELD0 (312)
+#define VPIF_PAL_HBI_COUNT_FIELD1 (313)
+
+#define VPIF_NTSC_VBI_SAMPLES_PER_LINE (720)
+#define VPIF_PAL_VBI_SAMPLES_PER_LINE (720)
+#define VPIF_NTSC_HBI_SAMPLES_PER_LINE (268)
+#define VPIF_PAL_HBI_SAMPLES_PER_LINE (280)
+
+#define VPIF_CH_VANC_EN (0x20)
+#define VPIF_DMA_REQ_SIZE (0x080)
+#define VPIF_EMULATION_DISABLE (0x01)
+
+extern u8 irq_vpif_capture_channel[VPIF_NUM_CHANNELS];
+
+/* inline function to enable/disable channel0 */
+static inline void enable_channel0(int enable)
+{
+ if (enable)
+ regw((regr(VPIF_CH0_CTRL) | (VPIF_CH0_EN)), VPIF_CH0_CTRL);
+ else
+ regw((regr(VPIF_CH0_CTRL) & (~VPIF_CH0_EN)), VPIF_CH0_CTRL);
+}
+
+/* inline function to enable/disable channel1 */
+static inline void enable_channel1(int enable)
+{
+ if (enable)
+ regw((regr(VPIF_CH1_CTRL) | (VPIF_CH1_EN)), VPIF_CH1_CTRL);
+ else
+ regw((regr(VPIF_CH1_CTRL) & (~VPIF_CH1_EN)), VPIF_CH1_CTRL);
+}
+
+/* inline function to enable interrupt for channel0 */
+static inline void channel0_intr_enable(int enable)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&vpif_lock, flags);
+
+ if (enable) {
+ regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN);
+ regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET);
+
+ regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH0), VPIF_INTEN);
+ regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH0),
+ VPIF_INTEN_SET);
+ } else {
+ regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH0)), VPIF_INTEN);
+ regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH0),
+ VPIF_INTEN_SET);
+ }
+ spin_unlock_irqrestore(&vpif_lock, flags);
+}
+
+/* inline function to enable interrupt for channel1 */
+static inline void channel1_intr_enable(int enable)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&vpif_lock, flags);
+
+ if (enable) {
+ regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN);
+ regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET);
+
+ regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH1), VPIF_INTEN);
+ regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH1),
+ VPIF_INTEN_SET);
+ } else {
+ regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH1)), VPIF_INTEN);
+ regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH1),
+ VPIF_INTEN_SET);
+ }
+ spin_unlock_irqrestore(&vpif_lock, flags);
+}
+
+/* inline function to set buffer addresses in case of Y/C non mux mode */
+static inline void ch0_set_videobuf_addr_yc_nmux(unsigned long top_strt_luma,
+ unsigned long btm_strt_luma,
+ unsigned long top_strt_chroma,
+ unsigned long btm_strt_chroma)
+{
+ regw(top_strt_luma, VPIF_CH0_TOP_STRT_ADD_LUMA);
+ regw(btm_strt_luma, VPIF_CH0_BTM_STRT_ADD_LUMA);
+ regw(top_strt_chroma, VPIF_CH1_TOP_STRT_ADD_CHROMA);
+ regw(btm_strt_chroma, VPIF_CH1_BTM_STRT_ADD_CHROMA);
+}
+
+/* inline function to set buffer addresses in VPIF registers for video data */
+static inline void ch0_set_videobuf_addr(unsigned long top_strt_luma,
+ unsigned long btm_strt_luma,
+ unsigned long top_strt_chroma,
+ unsigned long btm_strt_chroma)
+{
+ regw(top_strt_luma, VPIF_CH0_TOP_STRT_ADD_LUMA);
+ regw(btm_strt_luma, VPIF_CH0_BTM_STRT_ADD_LUMA);
+ regw(top_strt_chroma, VPIF_CH0_TOP_STRT_ADD_CHROMA);
+ regw(btm_strt_chroma, VPIF_CH0_BTM_STRT_ADD_CHROMA);
+}
+
+static inline void ch1_set_videobuf_addr(unsigned long top_strt_luma,
+ unsigned long btm_strt_luma,
+ unsigned long top_strt_chroma,
+ unsigned long btm_strt_chroma)
+{
+
+ regw(top_strt_luma, VPIF_CH1_TOP_STRT_ADD_LUMA);
+ regw(btm_strt_luma, VPIF_CH1_BTM_STRT_ADD_LUMA);
+ regw(top_strt_chroma, VPIF_CH1_TOP_STRT_ADD_CHROMA);
+ regw(btm_strt_chroma, VPIF_CH1_BTM_STRT_ADD_CHROMA);
+}
+
+static inline void ch0_set_vbi_addr(unsigned long top_vbi,
+ unsigned long btm_vbi, unsigned long a, unsigned long b)
+{
+ regw(top_vbi, VPIF_CH0_TOP_STRT_ADD_VANC);
+ regw(btm_vbi, VPIF_CH0_BTM_STRT_ADD_VANC);
+}
+
+static inline void ch0_set_hbi_addr(unsigned long top_vbi,
+ unsigned long btm_vbi, unsigned long a, unsigned long b)
+{
+ regw(top_vbi, VPIF_CH0_TOP_STRT_ADD_HANC);
+ regw(btm_vbi, VPIF_CH0_BTM_STRT_ADD_HANC);
+}
+
+static inline void ch1_set_vbi_addr(unsigned long top_vbi,
+ unsigned long btm_vbi, unsigned long a, unsigned long b)
+{
+ regw(top_vbi, VPIF_CH1_TOP_STRT_ADD_VANC);
+ regw(btm_vbi, VPIF_CH1_BTM_STRT_ADD_VANC);
+}
+
+static inline void ch1_set_hbi_addr(unsigned long top_vbi,
+ unsigned long btm_vbi, unsigned long a, unsigned long b)
+{
+ regw(top_vbi, VPIF_CH1_TOP_STRT_ADD_HANC);
+ regw(btm_vbi, VPIF_CH1_BTM_STRT_ADD_HANC);
+}
+
+/* Inline function to enable raw vbi in the given channel */
+static inline void disable_raw_feature(u8 channel_id, u8 index)
+{
+ u32 ctrl_reg;
+ if (0 == channel_id)
+ ctrl_reg = VPIF_CH0_CTRL;
+ else
+ ctrl_reg = VPIF_CH1_CTRL;
+
+ if (1 == index)
+ vpif_clr_bit(ctrl_reg, VPIF_CH_VANC_EN_BIT);
+ else
+ vpif_clr_bit(ctrl_reg, VPIF_CH_HANC_EN_BIT);
+}
+
+static inline void enable_raw_feature(u8 channel_id, u8 index)
+{
+ u32 ctrl_reg;
+ if (0 == channel_id)
+ ctrl_reg = VPIF_CH0_CTRL;
+ else
+ ctrl_reg = VPIF_CH1_CTRL;
+
+ if (1 == index)
+ vpif_set_bit(ctrl_reg, VPIF_CH_VANC_EN_BIT);
+ else
+ vpif_set_bit(ctrl_reg, VPIF_CH_HANC_EN_BIT);
+}
+
+/* inline function to enable/disable channel2 */
+static inline void enable_channel2(int enable)
+{
+ if (enable) {
+ regw((regr(VPIF_CH2_CTRL) | (VPIF_CH2_CLK_EN)), VPIF_CH2_CTRL);
+ regw((regr(VPIF_CH2_CTRL) | (VPIF_CH2_EN)), VPIF_CH2_CTRL);
+ } else {
+ regw((regr(VPIF_CH2_CTRL) & (~VPIF_CH2_CLK_EN)), VPIF_CH2_CTRL);
+ regw((regr(VPIF_CH2_CTRL) & (~VPIF_CH2_EN)), VPIF_CH2_CTRL);
+ }
+}
+
+/* inline function to enable/disable channel3 */
+static inline void enable_channel3(int enable)
+{
+ if (enable) {
+ regw((regr(VPIF_CH3_CTRL) | (VPIF_CH3_CLK_EN)), VPIF_CH3_CTRL);
+ regw((regr(VPIF_CH3_CTRL) | (VPIF_CH3_EN)), VPIF_CH3_CTRL);
+ } else {
+ regw((regr(VPIF_CH3_CTRL) & (~VPIF_CH3_CLK_EN)), VPIF_CH3_CTRL);
+ regw((regr(VPIF_CH3_CTRL) & (~VPIF_CH3_EN)), VPIF_CH3_CTRL);
+ }
+}
+
+/* inline function to enable interrupt for channel2 */
+static inline void channel2_intr_enable(int enable)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&vpif_lock, flags);
+
+ if (enable) {
+ regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN);
+ regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET);
+ regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH2), VPIF_INTEN);
+ regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH2),
+ VPIF_INTEN_SET);
+ } else {
+ regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH2)), VPIF_INTEN);
+ regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH2),
+ VPIF_INTEN_SET);
+ }
+ spin_unlock_irqrestore(&vpif_lock, flags);
+}
+
+/* inline function to enable interrupt for channel3 */
+static inline void channel3_intr_enable(int enable)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&vpif_lock, flags);
+
+ if (enable) {
+ regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN);
+ regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET);
+
+ regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH3), VPIF_INTEN);
+ regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH3),
+ VPIF_INTEN_SET);
+ } else {
+ regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH3)), VPIF_INTEN);
+ regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH3),
+ VPIF_INTEN_SET);
+ }
+ spin_unlock_irqrestore(&vpif_lock, flags);
+}
+
+/* inline function to enable raw vbi data for channel2 */
+static inline void channel2_raw_enable(int enable, u8 index)
+{
+ u32 mask;
+
+ if (1 == index)
+ mask = VPIF_CH_VANC_EN_BIT;
+ else
+ mask = VPIF_CH_HANC_EN_BIT;
+
+ if (enable)
+ vpif_set_bit(VPIF_CH2_CTRL, mask);
+ else
+ vpif_clr_bit(VPIF_CH2_CTRL, mask);
+}
+
+/* inline function to enable raw vbi data for channel3*/
+static inline void channel3_raw_enable(int enable, u8 index)
+{
+ u32 mask;
+
+ if (1 == index)
+ mask = VPIF_CH_VANC_EN_BIT;
+ else
+ mask = VPIF_CH_HANC_EN_BIT;
+
+ if (enable)
+ vpif_set_bit(VPIF_CH3_CTRL, mask);
+ else
+ vpif_clr_bit(VPIF_CH3_CTRL, mask);
+}
+
+/* inline function to set buffer addresses in case of Y/C non mux mode */
+static inline void ch2_set_videobuf_addr_yc_nmux(unsigned long top_strt_luma,
+ unsigned long btm_strt_luma,
+ unsigned long top_strt_chroma,
+ unsigned long btm_strt_chroma)
+{
+ regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_LUMA);
+ regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_LUMA);
+ regw(top_strt_chroma, VPIF_CH3_TOP_STRT_ADD_CHROMA);
+ regw(btm_strt_chroma, VPIF_CH3_BTM_STRT_ADD_CHROMA);
+}
+
+/* inline function to set buffer addresses in VPIF registers for video data */
+static inline void ch2_set_videobuf_addr(unsigned long top_strt_luma,
+ unsigned long btm_strt_luma,
+ unsigned long top_strt_chroma,
+ unsigned long btm_strt_chroma)
+{
+ regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_LUMA);
+ regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_LUMA);
+ regw(top_strt_chroma, VPIF_CH2_TOP_STRT_ADD_CHROMA);
+ regw(btm_strt_chroma, VPIF_CH2_BTM_STRT_ADD_CHROMA);
+}
+
+static inline void ch3_set_videobuf_addr(unsigned long top_strt_luma,
+ unsigned long btm_strt_luma,
+ unsigned long top_strt_chroma,
+ unsigned long btm_strt_chroma)
+{
+ regw(top_strt_luma, VPIF_CH3_TOP_STRT_ADD_LUMA);
+ regw(btm_strt_luma, VPIF_CH3_BTM_STRT_ADD_LUMA);
+ regw(top_strt_chroma, VPIF_CH3_TOP_STRT_ADD_CHROMA);
+ regw(btm_strt_chroma, VPIF_CH3_BTM_STRT_ADD_CHROMA);
+}
+
+/* inline function to set buffer addresses in VPIF registers for vbi data */
+static inline void ch2_set_vbi_addr(unsigned long top_strt_luma,
+ unsigned long btm_strt_luma,
+ unsigned long top_strt_chroma,
+ unsigned long btm_strt_chroma)
+{
+ regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_VANC);
+ regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_VANC);
+}
+
+static inline void ch3_set_vbi_addr(unsigned long top_strt_luma,
+ unsigned long btm_strt_luma,
+ unsigned long top_strt_chroma,
+ unsigned long btm_strt_chroma)
+{
+ regw(top_strt_luma, VPIF_CH3_TOP_STRT_ADD_VANC);
+ regw(btm_strt_luma, VPIF_CH3_BTM_STRT_ADD_VANC);
+}
+
+#define VPIF_MAX_NAME (30)
+
+/* This structure will store size parameters as per the mode selected by user */
+struct vpif_channel_config_params {
+ char name[VPIF_MAX_NAME]; /* Name of the mode */
+ u16 width; /* Indicates width of the image */
+ u16 height; /* Indicates height of the image */
+ u8 frm_fmt; /* Interlaced (0) or progressive (1) */
+ u8 ycmux_mode; /* This mode requires one (0) or two (1)
+ channels */
+ u16 eav2sav; /* length of eav 2 sav */
+ u16 sav2eav; /* length of sav 2 eav */
+ u16 l1, l3, l5, l7, l9, l11; /* Other parameter configurations */
+ u16 vsize; /* Vertical size of the image */
+ u8 capture_format; /* Indicates whether capture format
+ * is in BT or in CCD/CMOS */
+ u8 vbi_supported; /* Indicates whether this mode
+ * supports capturing vbi or not */
+ u8 hd_sd; /* HDTV (1) or SDTV (0) format */
+ v4l2_std_id stdid; /* SDTV format */
+ u32 dv_preset; /* HDTV format */
+};
+
+extern const unsigned int vpif_ch_params_count;
+extern const struct vpif_channel_config_params ch_params[];
+
+struct vpif_video_params;
+struct vpif_params;
+struct vpif_vbi_params;
+
+int vpif_set_video_params(struct vpif_params *vpifparams, u8 channel_id);
+void vpif_set_vbi_display_params(struct vpif_vbi_params *vbiparams,
+ u8 channel_id);
+int vpif_channel_getfid(u8 channel_id);
+
+enum data_size {
+ _8BITS = 0,
+ _10BITS,
+ _12BITS,
+};
+
+/* Structure for vpif parameters for raw vbi data */
+struct vpif_vbi_params {
+ __u32 hstart0; /* Horizontal start of raw vbi data for first field */
+ __u32 vstart0; /* Vertical start of raw vbi data for first field */
+ __u32 hsize0; /* Horizontal size of raw vbi data for first field */
+ __u32 vsize0; /* Vertical size of raw vbi data for first field */
+ __u32 hstart1; /* Horizontal start of raw vbi data for second field */
+ __u32 vstart1; /* Vertical start of raw vbi data for second field */
+ __u32 hsize1; /* Horizontal size of raw vbi data for second field */
+ __u32 vsize1; /* Vertical size of raw vbi data for second field */
+};
+
+/* structure for vpif parameters */
+struct vpif_video_params {
+ __u8 storage_mode; /* Indicates field or frame mode */
+ unsigned long hpitch;
+ v4l2_std_id stdid;
+};
+
+struct vpif_params {
+ struct vpif_interface iface;
+ struct vpif_video_params video_params;
+ struct vpif_channel_config_params std_info;
+ union param {
+ struct vpif_vbi_params vbi_params;
+ enum data_size data_sz;
+ } params;
+};
+
+#endif /* End of #ifndef VPIF_H */
+
diff --git a/drivers/media/video/davinci/vpif_capture.c b/drivers/media/video/davinci/vpif_capture.c
new file mode 100644
index 00000000..6504e40a
--- /dev/null
+++ b/drivers/media/video/davinci/vpif_capture.c
@@ -0,0 +1,2412 @@
+/*
+ * Copyright (C) 2009 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * TODO : add support for VBI & HBI data service
+ * add static buffer allocation
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/string.h>
+#include <linux/videodev2.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-chip-ident.h>
+
+#include "vpif_capture.h"
+#include "vpif.h"
+
+MODULE_DESCRIPTION("TI DaVinci VPIF Capture driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(VPIF_CAPTURE_VERSION);
+
+#define vpif_err(fmt, arg...) v4l2_err(&vpif_obj.v4l2_dev, fmt, ## arg)
+#define vpif_dbg(level, debug, fmt, arg...) \
+ v4l2_dbg(level, debug, &vpif_obj.v4l2_dev, fmt, ## arg)
+
+static int debug = 1;
+static u32 ch0_numbuffers = 3;
+static u32 ch1_numbuffers = 3;
+static u32 ch0_bufsize = 1920 * 1080 * 2;
+static u32 ch1_bufsize = 720 * 576 * 2;
+
+module_param(debug, int, 0644);
+module_param(ch0_numbuffers, uint, S_IRUGO);
+module_param(ch1_numbuffers, uint, S_IRUGO);
+module_param(ch0_bufsize, uint, S_IRUGO);
+module_param(ch1_bufsize, uint, S_IRUGO);
+
+MODULE_PARM_DESC(debug, "Debug level 0-1");
+MODULE_PARM_DESC(ch2_numbuffers, "Channel0 buffer count (default:3)");
+MODULE_PARM_DESC(ch3_numbuffers, "Channel1 buffer count (default:3)");
+MODULE_PARM_DESC(ch2_bufsize, "Channel0 buffer size (default:1920 x 1080 x 2)");
+MODULE_PARM_DESC(ch3_bufsize, "Channel1 buffer size (default:720 x 576 x 2)");
+
+static struct vpif_config_params config_params = {
+ .min_numbuffers = 3,
+ .numbuffers[0] = 3,
+ .numbuffers[1] = 3,
+ .min_bufsize[0] = 720 * 480 * 2,
+ .min_bufsize[1] = 720 * 480 * 2,
+ .channel_bufsize[0] = 1920 * 1080 * 2,
+ .channel_bufsize[1] = 720 * 576 * 2,
+};
+
+/* global variables */
+static struct vpif_device vpif_obj = { {NULL} };
+static struct device *vpif_dev;
+
+/**
+ * vpif_uservirt_to_phys : translate user/virtual address to phy address
+ * @virtp: user/virtual address
+ *
+ * This inline function is used to convert user space virtual address to
+ * physical address.
+ */
+static inline u32 vpif_uservirt_to_phys(u32 virtp)
+{
+ unsigned long physp = 0;
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *vma;
+
+ vma = find_vma(mm, virtp);
+
+ /* For kernel direct-mapped memory, take the easy way */
+ if (virtp >= PAGE_OFFSET)
+ physp = virt_to_phys((void *)virtp);
+ else if (vma && (vma->vm_flags & VM_IO) && (vma->vm_pgoff))
+ /**
+ * this will catch, kernel-allocated, mmaped-to-usermode
+ * addresses
+ */
+ physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start);
+ else {
+ /* otherwise, use get_user_pages() for general userland pages */
+ int res, nr_pages = 1;
+ struct page *pages;
+
+ down_read(&current->mm->mmap_sem);
+
+ res = get_user_pages(current, current->mm,
+ virtp, nr_pages, 1, 0, &pages, NULL);
+ up_read(&current->mm->mmap_sem);
+
+ if (res == nr_pages)
+ physp = __pa(page_address(&pages[0]) +
+ (virtp & ~PAGE_MASK));
+ else {
+ vpif_err("get_user_pages failed\n");
+ return 0;
+ }
+ }
+ return physp;
+}
+
+/**
+ * buffer_prepare : callback function for buffer prepare
+ * @q : buffer queue ptr
+ * @vb: ptr to video buffer
+ * @field: field info
+ *
+ * This is the callback function for buffer prepare when videobuf_qbuf()
+ * function is called. The buffer is prepared and user space virtual address
+ * or user address is converted into physical address
+ */
+static int vpif_buffer_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ /* Get the file handle object and channel object */
+ struct vpif_fh *fh = q->priv_data;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common;
+ unsigned long addr;
+
+
+ vpif_dbg(2, debug, "vpif_buffer_prepare\n");
+
+ common = &ch->common[VPIF_VIDEO_INDEX];
+
+ /* If buffer is not initialized, initialize it */
+ if (VIDEOBUF_NEEDS_INIT == vb->state) {
+ vb->width = common->width;
+ vb->height = common->height;
+ vb->size = vb->width * vb->height;
+ vb->field = field;
+ }
+ vb->state = VIDEOBUF_PREPARED;
+ /**
+ * if user pointer memory mechanism is used, get the physical
+ * address of the buffer
+ */
+ if (V4L2_MEMORY_USERPTR == common->memory) {
+ if (0 == vb->baddr) {
+ vpif_dbg(1, debug, "buffer address is 0\n");
+ return -EINVAL;
+
+ }
+ vb->boff = vpif_uservirt_to_phys(vb->baddr);
+ if (!IS_ALIGNED(vb->boff, 8))
+ goto exit;
+ }
+
+ addr = vb->boff;
+ if (q->streaming) {
+ if (!IS_ALIGNED((addr + common->ytop_off), 8) ||
+ !IS_ALIGNED((addr + common->ybtm_off), 8) ||
+ !IS_ALIGNED((addr + common->ctop_off), 8) ||
+ !IS_ALIGNED((addr + common->cbtm_off), 8))
+ goto exit;
+ }
+ return 0;
+exit:
+ vpif_dbg(1, debug, "buffer_prepare:offset is not aligned to 8 bytes\n");
+ return -EINVAL;
+}
+
+/**
+ * vpif_buffer_setup : Callback function for buffer setup.
+ * @q: buffer queue ptr
+ * @count: number of buffers
+ * @size: size of the buffer
+ *
+ * This callback function is called when reqbuf() is called to adjust
+ * the buffer count and buffer size
+ */
+static int vpif_buffer_setup(struct videobuf_queue *q, unsigned int *count,
+ unsigned int *size)
+{
+ /* Get the file handle object and channel object */
+ struct vpif_fh *fh = q->priv_data;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common;
+
+ common = &ch->common[VPIF_VIDEO_INDEX];
+
+ vpif_dbg(2, debug, "vpif_buffer_setup\n");
+
+ /* If memory type is not mmap, return */
+ if (V4L2_MEMORY_MMAP != common->memory)
+ return 0;
+
+ /* Calculate the size of the buffer */
+ *size = config_params.channel_bufsize[ch->channel_id];
+
+ if (*count < config_params.min_numbuffers)
+ *count = config_params.min_numbuffers;
+ return 0;
+}
+
+/**
+ * vpif_buffer_queue : Callback function to add buffer to DMA queue
+ * @q: ptr to videobuf_queue
+ * @vb: ptr to videobuf_buffer
+ */
+static void vpif_buffer_queue(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ /* Get the file handle object and channel object */
+ struct vpif_fh *fh = q->priv_data;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common;
+
+ common = &ch->common[VPIF_VIDEO_INDEX];
+
+ vpif_dbg(2, debug, "vpif_buffer_queue\n");
+
+ /* add the buffer to the DMA queue */
+ list_add_tail(&vb->queue, &common->dma_queue);
+ /* Change state of the buffer */
+ vb->state = VIDEOBUF_QUEUED;
+}
+
+/**
+ * vpif_buffer_release : Callback function to free buffer
+ * @q: buffer queue ptr
+ * @vb: ptr to video buffer
+ *
+ * This function is called from the videobuf layer to free memory
+ * allocated to the buffers
+ */
+static void vpif_buffer_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ /* Get the file handle object and channel object */
+ struct vpif_fh *fh = q->priv_data;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common;
+
+ common = &ch->common[VPIF_VIDEO_INDEX];
+
+ videobuf_dma_contig_free(q, vb);
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static struct videobuf_queue_ops video_qops = {
+ .buf_setup = vpif_buffer_setup,
+ .buf_prepare = vpif_buffer_prepare,
+ .buf_queue = vpif_buffer_queue,
+ .buf_release = vpif_buffer_release,
+};
+
+static u8 channel_first_int[VPIF_NUMBER_OF_OBJECTS][2] =
+ { {1, 1} };
+
+/**
+ * vpif_process_buffer_complete: process a completed buffer
+ * @common: ptr to common channel object
+ *
+ * This function time stamp the buffer and mark it as DONE. It also
+ * wake up any process waiting on the QUEUE and set the next buffer
+ * as current
+ */
+static void vpif_process_buffer_complete(struct common_obj *common)
+{
+ do_gettimeofday(&common->cur_frm->ts);
+ common->cur_frm->state = VIDEOBUF_DONE;
+ wake_up_interruptible(&common->cur_frm->done);
+ /* Make curFrm pointing to nextFrm */
+ common->cur_frm = common->next_frm;
+}
+
+/**
+ * vpif_schedule_next_buffer: set next buffer address for capture
+ * @common : ptr to common channel object
+ *
+ * This function will get next buffer from the dma queue and
+ * set the buffer address in the vpif register for capture.
+ * the buffer is marked active
+ */
+static void vpif_schedule_next_buffer(struct common_obj *common)
+{
+ unsigned long addr = 0;
+
+ common->next_frm = list_entry(common->dma_queue.next,
+ struct videobuf_buffer, queue);
+ /* Remove that buffer from the buffer queue */
+ list_del(&common->next_frm->queue);
+ common->next_frm->state = VIDEOBUF_ACTIVE;
+ if (V4L2_MEMORY_USERPTR == common->memory)
+ addr = common->next_frm->boff;
+ else
+ addr = videobuf_to_dma_contig(common->next_frm);
+
+ /* Set top and bottom field addresses in VPIF registers */
+ common->set_addr(addr + common->ytop_off,
+ addr + common->ybtm_off,
+ addr + common->ctop_off,
+ addr + common->cbtm_off);
+}
+
+/**
+ * vpif_channel_isr : ISR handler for vpif capture
+ * @irq: irq number
+ * @dev_id: dev_id ptr
+ *
+ * It changes status of the captured buffer, takes next buffer from the queue
+ * and sets its address in VPIF registers
+ */
+static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
+{
+ struct vpif_device *dev = &vpif_obj;
+ struct common_obj *common;
+ struct channel_obj *ch;
+ enum v4l2_field field;
+ int channel_id = 0;
+ int fid = -1, i;
+
+ channel_id = *(int *)(dev_id);
+ ch = dev->dev[channel_id];
+
+ field = ch->common[VPIF_VIDEO_INDEX].fmt.fmt.pix.field;
+
+ for (i = 0; i < VPIF_NUMBER_OF_OBJECTS; i++) {
+ common = &ch->common[i];
+ /* skip If streaming is not started in this channel */
+ if (0 == common->started)
+ continue;
+
+ /* Check the field format */
+ if (1 == ch->vpifparams.std_info.frm_fmt) {
+ /* Progressive mode */
+ if (list_empty(&common->dma_queue))
+ continue;
+
+ if (!channel_first_int[i][channel_id])
+ vpif_process_buffer_complete(common);
+
+ channel_first_int[i][channel_id] = 0;
+
+ vpif_schedule_next_buffer(common);
+
+
+ channel_first_int[i][channel_id] = 0;
+ } else {
+ /**
+ * Interlaced mode. If it is first interrupt, ignore
+ * it
+ */
+ if (channel_first_int[i][channel_id]) {
+ channel_first_int[i][channel_id] = 0;
+ continue;
+ }
+ if (0 == i) {
+ ch->field_id ^= 1;
+ /* Get field id from VPIF registers */
+ fid = vpif_channel_getfid(ch->channel_id);
+ if (fid != ch->field_id) {
+ /**
+ * If field id does not match stored
+ * field id, make them in sync
+ */
+ if (0 == fid)
+ ch->field_id = fid;
+ return IRQ_HANDLED;
+ }
+ }
+ /* device field id and local field id are in sync */
+ if (0 == fid) {
+ /* this is even field */
+ if (common->cur_frm == common->next_frm)
+ continue;
+
+ /* mark the current buffer as done */
+ vpif_process_buffer_complete(common);
+ } else if (1 == fid) {
+ /* odd field */
+ if (list_empty(&common->dma_queue) ||
+ (common->cur_frm != common->next_frm))
+ continue;
+
+ vpif_schedule_next_buffer(common);
+ }
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+/**
+ * vpif_update_std_info() - update standard related info
+ * @ch: ptr to channel object
+ *
+ * For a given standard selected by application, update values
+ * in the device data structures
+ */
+static int vpif_update_std_info(struct channel_obj *ch)
+{
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct vpif_params *vpifparams = &ch->vpifparams;
+ const struct vpif_channel_config_params *config;
+ struct vpif_channel_config_params *std_info = &vpifparams->std_info;
+ struct video_obj *vid_ch = &ch->video;
+ int index;
+
+ vpif_dbg(2, debug, "vpif_update_std_info\n");
+
+ for (index = 0; index < vpif_ch_params_count; index++) {
+ config = &ch_params[index];
+ if (config->hd_sd == 0) {
+ vpif_dbg(2, debug, "SD format\n");
+ if (config->stdid & vid_ch->stdid) {
+ memcpy(std_info, config, sizeof(*config));
+ break;
+ }
+ } else {
+ vpif_dbg(2, debug, "HD format\n");
+ if (config->dv_preset == vid_ch->dv_preset) {
+ memcpy(std_info, config, sizeof(*config));
+ break;
+ }
+ }
+ }
+
+ /* standard not found */
+ if (index == vpif_ch_params_count)
+ return -EINVAL;
+
+ common->fmt.fmt.pix.width = std_info->width;
+ common->width = std_info->width;
+ common->fmt.fmt.pix.height = std_info->height;
+ common->height = std_info->height;
+ common->fmt.fmt.pix.bytesperline = std_info->width;
+ vpifparams->video_params.hpitch = std_info->width;
+ vpifparams->video_params.storage_mode = std_info->frm_fmt;
+
+ return 0;
+}
+
+/**
+ * vpif_calculate_offsets : This function calculates buffers offsets
+ * @ch : ptr to channel object
+ *
+ * This function calculates buffer offsets for Y and C in the top and
+ * bottom field
+ */
+static void vpif_calculate_offsets(struct channel_obj *ch)
+{
+ unsigned int hpitch, vpitch, sizeimage;
+ struct video_obj *vid_ch = &(ch->video);
+ struct vpif_params *vpifparams = &ch->vpifparams;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ enum v4l2_field field = common->fmt.fmt.pix.field;
+
+ vpif_dbg(2, debug, "vpif_calculate_offsets\n");
+
+ if (V4L2_FIELD_ANY == field) {
+ if (vpifparams->std_info.frm_fmt)
+ vid_ch->buf_field = V4L2_FIELD_NONE;
+ else
+ vid_ch->buf_field = V4L2_FIELD_INTERLACED;
+ } else
+ vid_ch->buf_field = common->fmt.fmt.pix.field;
+
+ if (V4L2_MEMORY_USERPTR == common->memory)
+ sizeimage = common->fmt.fmt.pix.sizeimage;
+ else
+ sizeimage = config_params.channel_bufsize[ch->channel_id];
+
+ hpitch = common->fmt.fmt.pix.bytesperline;
+ vpitch = sizeimage / (hpitch * 2);
+
+ if ((V4L2_FIELD_NONE == vid_ch->buf_field) ||
+ (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) {
+ /* Calculate offsets for Y top, Y Bottom, C top and C Bottom */
+ common->ytop_off = 0;
+ common->ybtm_off = hpitch;
+ common->ctop_off = sizeimage / 2;
+ common->cbtm_off = sizeimage / 2 + hpitch;
+ } else if (V4L2_FIELD_SEQ_TB == vid_ch->buf_field) {
+ /* Calculate offsets for Y top, Y Bottom, C top and C Bottom */
+ common->ytop_off = 0;
+ common->ybtm_off = sizeimage / 4;
+ common->ctop_off = sizeimage / 2;
+ common->cbtm_off = common->ctop_off + sizeimage / 4;
+ } else if (V4L2_FIELD_SEQ_BT == vid_ch->buf_field) {
+ /* Calculate offsets for Y top, Y Bottom, C top and C Bottom */
+ common->ybtm_off = 0;
+ common->ytop_off = sizeimage / 4;
+ common->cbtm_off = sizeimage / 2;
+ common->ctop_off = common->cbtm_off + sizeimage / 4;
+ }
+ if ((V4L2_FIELD_NONE == vid_ch->buf_field) ||
+ (V4L2_FIELD_INTERLACED == vid_ch->buf_field))
+ vpifparams->video_params.storage_mode = 1;
+ else
+ vpifparams->video_params.storage_mode = 0;
+
+ if (1 == vpifparams->std_info.frm_fmt)
+ vpifparams->video_params.hpitch =
+ common->fmt.fmt.pix.bytesperline;
+ else {
+ if ((field == V4L2_FIELD_ANY)
+ || (field == V4L2_FIELD_INTERLACED))
+ vpifparams->video_params.hpitch =
+ common->fmt.fmt.pix.bytesperline * 2;
+ else
+ vpifparams->video_params.hpitch =
+ common->fmt.fmt.pix.bytesperline;
+ }
+
+ ch->vpifparams.video_params.stdid = vpifparams->std_info.stdid;
+}
+
+/**
+ * vpif_config_format: configure default frame format in the device
+ * ch : ptr to channel object
+ */
+static void vpif_config_format(struct channel_obj *ch)
+{
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+
+ vpif_dbg(2, debug, "vpif_config_format\n");
+
+ common->fmt.fmt.pix.field = V4L2_FIELD_ANY;
+ if (config_params.numbuffers[ch->channel_id] == 0)
+ common->memory = V4L2_MEMORY_USERPTR;
+ else
+ common->memory = V4L2_MEMORY_MMAP;
+
+ common->fmt.fmt.pix.sizeimage
+ = config_params.channel_bufsize[ch->channel_id];
+
+ if (ch->vpifparams.iface.if_type == VPIF_IF_RAW_BAYER)
+ common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8;
+ else
+ common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P;
+ common->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+}
+
+/**
+ * vpif_get_default_field() - Get default field type based on interface
+ * @vpif_params - ptr to vpif params
+ */
+static inline enum v4l2_field vpif_get_default_field(
+ struct vpif_interface *iface)
+{
+ return (iface->if_type == VPIF_IF_RAW_BAYER) ? V4L2_FIELD_NONE :
+ V4L2_FIELD_INTERLACED;
+}
+
+/**
+ * vpif_check_format() - check given pixel format for compatibility
+ * @ch - channel ptr
+ * @pixfmt - Given pixel format
+ * @update - update the values as per hardware requirement
+ *
+ * Check the application pixel format for S_FMT and update the input
+ * values as per hardware limits for TRY_FMT. The default pixel and
+ * field format is selected based on interface type.
+ */
+static int vpif_check_format(struct channel_obj *ch,
+ struct v4l2_pix_format *pixfmt,
+ int update)
+{
+ struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]);
+ struct vpif_params *vpif_params = &ch->vpifparams;
+ enum v4l2_field field = pixfmt->field;
+ u32 sizeimage, hpitch, vpitch;
+ int ret = -EINVAL;
+
+ vpif_dbg(2, debug, "vpif_check_format\n");
+ /**
+ * first check for the pixel format. If if_type is Raw bayer,
+ * only V4L2_PIX_FMT_SBGGR8 format is supported. Otherwise only
+ * V4L2_PIX_FMT_YUV422P is supported
+ */
+ if (vpif_params->iface.if_type == VPIF_IF_RAW_BAYER) {
+ if (pixfmt->pixelformat != V4L2_PIX_FMT_SBGGR8) {
+ if (!update) {
+ vpif_dbg(2, debug, "invalid pix format\n");
+ goto exit;
+ }
+ pixfmt->pixelformat = V4L2_PIX_FMT_SBGGR8;
+ }
+ } else {
+ if (pixfmt->pixelformat != V4L2_PIX_FMT_YUV422P) {
+ if (!update) {
+ vpif_dbg(2, debug, "invalid pixel format\n");
+ goto exit;
+ }
+ pixfmt->pixelformat = V4L2_PIX_FMT_YUV422P;
+ }
+ }
+
+ if (!(VPIF_VALID_FIELD(field))) {
+ if (!update) {
+ vpif_dbg(2, debug, "invalid field format\n");
+ goto exit;
+ }
+ /**
+ * By default use FIELD_NONE for RAW Bayer capture
+ * and FIELD_INTERLACED for other interfaces
+ */
+ field = vpif_get_default_field(&vpif_params->iface);
+ } else if (field == V4L2_FIELD_ANY)
+ /* unsupported field. Use default */
+ field = vpif_get_default_field(&vpif_params->iface);
+
+ /* validate the hpitch */
+ hpitch = pixfmt->bytesperline;
+ if (hpitch < vpif_params->std_info.width) {
+ if (!update) {
+ vpif_dbg(2, debug, "invalid hpitch\n");
+ goto exit;
+ }
+ hpitch = vpif_params->std_info.width;
+ }
+
+ if (V4L2_MEMORY_USERPTR == common->memory)
+ sizeimage = pixfmt->sizeimage;
+ else
+ sizeimage = config_params.channel_bufsize[ch->channel_id];
+
+ vpitch = sizeimage / (hpitch * 2);
+
+ /* validate the vpitch */
+ if (vpitch < vpif_params->std_info.height) {
+ if (!update) {
+ vpif_dbg(2, debug, "Invalid vpitch\n");
+ goto exit;
+ }
+ vpitch = vpif_params->std_info.height;
+ }
+
+ /* Check for 8 byte alignment */
+ if (!ALIGN(hpitch, 8)) {
+ if (!update) {
+ vpif_dbg(2, debug, "invalid pitch alignment\n");
+ goto exit;
+ }
+ /* adjust to next 8 byte boundary */
+ hpitch = (((hpitch + 7) / 8) * 8);
+ }
+ /* if update is set, modify the bytesperline and sizeimage */
+ if (update) {
+ pixfmt->bytesperline = hpitch;
+ pixfmt->sizeimage = hpitch * vpitch * 2;
+ }
+ /**
+ * Image width and height is always based on current standard width and
+ * height
+ */
+ pixfmt->width = common->fmt.fmt.pix.width;
+ pixfmt->height = common->fmt.fmt.pix.height;
+ return 0;
+exit:
+ return ret;
+}
+
+/**
+ * vpif_config_addr() - function to configure buffer address in vpif
+ * @ch - channel ptr
+ * @muxmode - channel mux mode
+ */
+static void vpif_config_addr(struct channel_obj *ch, int muxmode)
+{
+ struct common_obj *common;
+
+ vpif_dbg(2, debug, "vpif_config_addr\n");
+
+ common = &(ch->common[VPIF_VIDEO_INDEX]);
+
+ if (VPIF_CHANNEL1_VIDEO == ch->channel_id)
+ common->set_addr = ch1_set_videobuf_addr;
+ else if (2 == muxmode)
+ common->set_addr = ch0_set_videobuf_addr_yc_nmux;
+ else
+ common->set_addr = ch0_set_videobuf_addr;
+}
+
+/**
+ * vpfe_mmap : It is used to map kernel space buffers into user spaces
+ * @filep: file pointer
+ * @vma: ptr to vm_area_struct
+ */
+static int vpif_mmap(struct file *filep, struct vm_area_struct *vma)
+{
+ /* Get the channel object and file handle object */
+ struct vpif_fh *fh = filep->private_data;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]);
+
+ vpif_dbg(2, debug, "vpif_mmap\n");
+
+ return videobuf_mmap_mapper(&common->buffer_queue, vma);
+}
+
+/**
+ * vpif_poll: It is used for select/poll system call
+ * @filep: file pointer
+ * @wait: poll table to wait
+ */
+static unsigned int vpif_poll(struct file *filep, poll_table * wait)
+{
+ struct vpif_fh *fh = filep->private_data;
+ struct channel_obj *channel = fh->channel;
+ struct common_obj *common = &(channel->common[VPIF_VIDEO_INDEX]);
+
+ vpif_dbg(2, debug, "vpif_poll\n");
+
+ if (common->started)
+ return videobuf_poll_stream(filep, &common->buffer_queue, wait);
+ return 0;
+}
+
+/**
+ * vpif_open : vpif open handler
+ * @filep: file ptr
+ *
+ * It creates object of file handle structure and stores it in private_data
+ * member of filepointer
+ */
+static int vpif_open(struct file *filep)
+{
+ struct vpif_capture_config *config = vpif_dev->platform_data;
+ struct video_device *vdev = video_devdata(filep);
+ struct common_obj *common;
+ struct video_obj *vid_ch;
+ struct channel_obj *ch;
+ struct vpif_fh *fh;
+ int i;
+
+ vpif_dbg(2, debug, "vpif_open\n");
+
+ ch = video_get_drvdata(vdev);
+
+ vid_ch = &ch->video;
+ common = &ch->common[VPIF_VIDEO_INDEX];
+
+ if (NULL == ch->curr_subdev_info) {
+ /**
+ * search through the sub device to see a registered
+ * sub device and make it as current sub device
+ */
+ for (i = 0; i < config->subdev_count; i++) {
+ if (vpif_obj.sd[i]) {
+ /* the sub device is registered */
+ ch->curr_subdev_info = &config->subdev_info[i];
+ /* make first input as the current input */
+ vid_ch->input_idx = 0;
+ break;
+ }
+ }
+ if (i == config->subdev_count) {
+ vpif_err("No sub device registered\n");
+ return -ENOENT;
+ }
+ }
+
+ /* Allocate memory for the file handle object */
+ fh = kzalloc(sizeof(struct vpif_fh), GFP_KERNEL);
+ if (NULL == fh) {
+ vpif_err("unable to allocate memory for file handle object\n");
+ return -ENOMEM;
+ }
+
+ /* store pointer to fh in private_data member of filep */
+ filep->private_data = fh;
+ fh->channel = ch;
+ fh->initialized = 0;
+ /* If decoder is not initialized. initialize it */
+ if (!ch->initialized) {
+ fh->initialized = 1;
+ ch->initialized = 1;
+ memset(&(ch->vpifparams), 0, sizeof(struct vpif_params));
+ }
+ /* Increment channel usrs counter */
+ ch->usrs++;
+ /* Set io_allowed member to false */
+ fh->io_allowed[VPIF_VIDEO_INDEX] = 0;
+ /* Initialize priority of this instance to default priority */
+ fh->prio = V4L2_PRIORITY_UNSET;
+ v4l2_prio_open(&ch->prio, &fh->prio);
+ return 0;
+}
+
+/**
+ * vpif_release : function to clean up file close
+ * @filep: file pointer
+ *
+ * This function deletes buffer queue, frees the buffers and the vpfe file
+ * handle
+ */
+static int vpif_release(struct file *filep)
+{
+ struct vpif_fh *fh = filep->private_data;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common;
+
+ vpif_dbg(2, debug, "vpif_release\n");
+
+ common = &ch->common[VPIF_VIDEO_INDEX];
+
+ /* if this instance is doing IO */
+ if (fh->io_allowed[VPIF_VIDEO_INDEX]) {
+ /* Reset io_usrs member of channel object */
+ common->io_usrs = 0;
+ /* Disable channel as per its device type and channel id */
+ if (VPIF_CHANNEL0_VIDEO == ch->channel_id) {
+ enable_channel0(0);
+ channel0_intr_enable(0);
+ }
+ if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) ||
+ (2 == common->started)) {
+ enable_channel1(0);
+ channel1_intr_enable(0);
+ }
+ common->started = 0;
+ /* Free buffers allocated */
+ videobuf_queue_cancel(&common->buffer_queue);
+ videobuf_mmap_free(&common->buffer_queue);
+ }
+
+ /* Decrement channel usrs counter */
+ ch->usrs--;
+
+ /* Close the priority */
+ v4l2_prio_close(&ch->prio, fh->prio);
+
+ if (fh->initialized)
+ ch->initialized = 0;
+
+ filep->private_data = NULL;
+ kfree(fh);
+ return 0;
+}
+
+/**
+ * vpif_reqbufs() - request buffer handler
+ * @file: file ptr
+ * @priv: file handle
+ * @reqbuf: request buffer structure ptr
+ */
+static int vpif_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbuf)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common;
+ u8 index = 0;
+
+ vpif_dbg(2, debug, "vpif_reqbufs\n");
+
+ /**
+ * This file handle has not initialized the channel,
+ * It is not allowed to do settings
+ */
+ if ((VPIF_CHANNEL0_VIDEO == ch->channel_id)
+ || (VPIF_CHANNEL1_VIDEO == ch->channel_id)) {
+ if (!fh->initialized) {
+ vpif_dbg(1, debug, "Channel Busy\n");
+ return -EBUSY;
+ }
+ }
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != reqbuf->type)
+ return -EINVAL;
+
+ index = VPIF_VIDEO_INDEX;
+
+ common = &ch->common[index];
+
+ if (0 != common->io_usrs)
+ return -EBUSY;
+
+ /* Initialize videobuf queue as per the buffer type */
+ videobuf_queue_dma_contig_init(&common->buffer_queue,
+ &video_qops, NULL,
+ &common->irqlock,
+ reqbuf->type,
+ common->fmt.fmt.pix.field,
+ sizeof(struct videobuf_buffer), fh,
+ &common->lock);
+
+ /* Set io allowed member of file handle to TRUE */
+ fh->io_allowed[index] = 1;
+ /* Increment io usrs member of channel object to 1 */
+ common->io_usrs = 1;
+ /* Store type of memory requested in channel object */
+ common->memory = reqbuf->memory;
+ INIT_LIST_HEAD(&common->dma_queue);
+
+ /* Allocate buffers */
+ return videobuf_reqbufs(&common->buffer_queue, reqbuf);
+}
+
+/**
+ * vpif_querybuf() - query buffer handler
+ * @file: file ptr
+ * @priv: file handle
+ * @buf: v4l2 buffer structure ptr
+ */
+static int vpif_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+
+ vpif_dbg(2, debug, "vpif_querybuf\n");
+
+ if (common->fmt.type != buf->type)
+ return -EINVAL;
+
+ if (common->memory != V4L2_MEMORY_MMAP) {
+ vpif_dbg(1, debug, "Invalid memory\n");
+ return -EINVAL;
+ }
+
+ return videobuf_querybuf(&common->buffer_queue, buf);
+}
+
+/**
+ * vpif_qbuf() - query buffer handler
+ * @file: file ptr
+ * @priv: file handle
+ * @buf: v4l2 buffer structure ptr
+ */
+static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct v4l2_buffer tbuf = *buf;
+ struct videobuf_buffer *buf1;
+ unsigned long addr = 0;
+ unsigned long flags;
+ int ret = 0;
+
+ vpif_dbg(2, debug, "vpif_qbuf\n");
+
+ if (common->fmt.type != tbuf.type) {
+ vpif_err("invalid buffer type\n");
+ return -EINVAL;
+ }
+
+ if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
+ vpif_err("fh io not allowed \n");
+ return -EACCES;
+ }
+
+ if (!(list_empty(&common->dma_queue)) ||
+ (common->cur_frm != common->next_frm) ||
+ !common->started ||
+ (common->started && (0 == ch->field_id)))
+ return videobuf_qbuf(&common->buffer_queue, buf);
+
+ /* bufferqueue is empty store buffer address in VPIF registers */
+ mutex_lock(&common->buffer_queue.vb_lock);
+ buf1 = common->buffer_queue.bufs[tbuf.index];
+
+ if ((buf1->state == VIDEOBUF_QUEUED) ||
+ (buf1->state == VIDEOBUF_ACTIVE)) {
+ vpif_err("invalid state\n");
+ goto qbuf_exit;
+ }
+
+ switch (buf1->memory) {
+ case V4L2_MEMORY_MMAP:
+ if (buf1->baddr == 0)
+ goto qbuf_exit;
+ break;
+
+ case V4L2_MEMORY_USERPTR:
+ if (tbuf.length < buf1->bsize)
+ goto qbuf_exit;
+
+ if ((VIDEOBUF_NEEDS_INIT != buf1->state)
+ && (buf1->baddr != tbuf.m.userptr)) {
+ vpif_buffer_release(&common->buffer_queue, buf1);
+ buf1->baddr = tbuf.m.userptr;
+ }
+ break;
+
+ default:
+ goto qbuf_exit;
+ }
+
+ local_irq_save(flags);
+ ret = vpif_buffer_prepare(&common->buffer_queue, buf1,
+ common->buffer_queue.field);
+ if (ret < 0) {
+ local_irq_restore(flags);
+ goto qbuf_exit;
+ }
+
+ buf1->state = VIDEOBUF_ACTIVE;
+
+ if (V4L2_MEMORY_USERPTR == common->memory)
+ addr = buf1->boff;
+ else
+ addr = videobuf_to_dma_contig(buf1);
+
+ common->next_frm = buf1;
+ common->set_addr(addr + common->ytop_off,
+ addr + common->ybtm_off,
+ addr + common->ctop_off,
+ addr + common->cbtm_off);
+
+ local_irq_restore(flags);
+ list_add_tail(&buf1->stream, &common->buffer_queue.stream);
+ mutex_unlock(&common->buffer_queue.vb_lock);
+ return 0;
+
+qbuf_exit:
+ mutex_unlock(&common->buffer_queue.vb_lock);
+ return -EINVAL;
+}
+
+/**
+ * vpif_dqbuf() - query buffer handler
+ * @file: file ptr
+ * @priv: file handle
+ * @buf: v4l2 buffer structure ptr
+ */
+static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+
+ vpif_dbg(2, debug, "vpif_dqbuf\n");
+
+ return videobuf_dqbuf(&common->buffer_queue, buf,
+ file->f_flags & O_NONBLOCK);
+}
+
+/**
+ * vpif_streamon() - streamon handler
+ * @file: file ptr
+ * @priv: file handle
+ * @buftype: v4l2 buffer type
+ */
+static int vpif_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type buftype)
+{
+
+ struct vpif_capture_config *config = vpif_dev->platform_data;
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id];
+ struct vpif_params *vpif;
+ unsigned long addr = 0;
+ int ret = 0;
+
+ vpif_dbg(2, debug, "vpif_streamon\n");
+
+ vpif = &ch->vpifparams;
+
+ if (buftype != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ vpif_dbg(1, debug, "buffer type not supported\n");
+ return -EINVAL;
+ }
+
+ /* If file handle is not allowed IO, return error */
+ if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
+ vpif_dbg(1, debug, "io not allowed\n");
+ return -EACCES;
+ }
+
+ /* If Streaming is already started, return error */
+ if (common->started) {
+ vpif_dbg(1, debug, "channel->started\n");
+ return -EBUSY;
+ }
+
+ if ((ch->channel_id == VPIF_CHANNEL0_VIDEO &&
+ oth_ch->common[VPIF_VIDEO_INDEX].started &&
+ vpif->std_info.ycmux_mode == 0) ||
+ ((ch->channel_id == VPIF_CHANNEL1_VIDEO) &&
+ (2 == oth_ch->common[VPIF_VIDEO_INDEX].started))) {
+ vpif_dbg(1, debug, "other channel is being used\n");
+ return -EBUSY;
+ }
+
+ ret = vpif_check_format(ch, &common->fmt.fmt.pix, 0);
+ if (ret)
+ return ret;
+
+ /* Enable streamon on the sub device */
+ ret = v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], video,
+ s_stream, 1);
+
+ if (ret && (ret != -ENOIOCTLCMD)) {
+ vpif_dbg(1, debug, "stream on failed in subdev\n");
+ return ret;
+ }
+
+ /* Call videobuf_streamon to start streaming in videobuf */
+ ret = videobuf_streamon(&common->buffer_queue);
+ if (ret) {
+ vpif_dbg(1, debug, "videobuf_streamon\n");
+ return ret;
+ }
+
+ /* If buffer queue is empty, return error */
+ if (list_empty(&common->dma_queue)) {
+ vpif_dbg(1, debug, "buffer queue is empty\n");
+ ret = -EIO;
+ goto exit;
+ }
+
+ /* Get the next frame from the buffer queue */
+ common->cur_frm = list_entry(common->dma_queue.next,
+ struct videobuf_buffer, queue);
+ common->next_frm = common->cur_frm;
+
+ /* Remove buffer from the buffer queue */
+ list_del(&common->cur_frm->queue);
+ /* Mark state of the current frame to active */
+ common->cur_frm->state = VIDEOBUF_ACTIVE;
+ /* Initialize field_id and started member */
+ ch->field_id = 0;
+ common->started = 1;
+
+ if (V4L2_MEMORY_USERPTR == common->memory)
+ addr = common->cur_frm->boff;
+ else
+ addr = videobuf_to_dma_contig(common->cur_frm);
+
+ /* Calculate the offset for Y and C data in the buffer */
+ vpif_calculate_offsets(ch);
+
+ if ((vpif->std_info.frm_fmt &&
+ ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE) &&
+ (common->fmt.fmt.pix.field != V4L2_FIELD_ANY))) ||
+ (!vpif->std_info.frm_fmt &&
+ (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) {
+ vpif_dbg(1, debug, "conflict in field format and std format\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ /* configure 1 or 2 channel mode */
+ ret = config->setup_input_channel_mode(vpif->std_info.ycmux_mode);
+
+ if (ret < 0) {
+ vpif_dbg(1, debug, "can't set vpif channel mode\n");
+ goto exit;
+ }
+
+ /* Call vpif_set_params function to set the parameters and addresses */
+ ret = vpif_set_video_params(vpif, ch->channel_id);
+
+ if (ret < 0) {
+ vpif_dbg(1, debug, "can't set video params\n");
+ goto exit;
+ }
+
+ common->started = ret;
+ vpif_config_addr(ch, ret);
+
+ common->set_addr(addr + common->ytop_off,
+ addr + common->ybtm_off,
+ addr + common->ctop_off,
+ addr + common->cbtm_off);
+
+ /**
+ * Set interrupt for both the fields in VPIF Register enable channel in
+ * VPIF register
+ */
+ if ((VPIF_CHANNEL0_VIDEO == ch->channel_id)) {
+ channel0_intr_assert();
+ channel0_intr_enable(1);
+ enable_channel0(1);
+ }
+ if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) ||
+ (common->started == 2)) {
+ channel1_intr_assert();
+ channel1_intr_enable(1);
+ enable_channel1(1);
+ }
+ channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1;
+ return ret;
+
+exit:
+ videobuf_streamoff(&common->buffer_queue);
+ return ret;
+}
+
+/**
+ * vpif_streamoff() - streamoff handler
+ * @file: file ptr
+ * @priv: file handle
+ * @buftype: v4l2 buffer type
+ */
+static int vpif_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type buftype)
+{
+
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ int ret;
+
+ vpif_dbg(2, debug, "vpif_streamoff\n");
+
+ if (buftype != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ vpif_dbg(1, debug, "buffer type not supported\n");
+ return -EINVAL;
+ }
+
+ /* If io is allowed for this file handle, return error */
+ if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
+ vpif_dbg(1, debug, "io not allowed\n");
+ return -EACCES;
+ }
+
+ /* If streaming is not started, return error */
+ if (!common->started) {
+ vpif_dbg(1, debug, "channel->started\n");
+ return -EINVAL;
+ }
+
+ /* disable channel */
+ if (VPIF_CHANNEL0_VIDEO == ch->channel_id) {
+ enable_channel0(0);
+ channel0_intr_enable(0);
+ } else {
+ enable_channel1(0);
+ channel1_intr_enable(0);
+ }
+
+ common->started = 0;
+
+ ret = v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], video,
+ s_stream, 0);
+
+ if (ret && (ret != -ENOIOCTLCMD))
+ vpif_dbg(1, debug, "stream off failed in subdev\n");
+
+ return videobuf_streamoff(&common->buffer_queue);
+}
+
+/**
+ * vpif_map_sub_device_to_input() - Maps sub device to input
+ * @ch - ptr to channel
+ * @config - ptr to capture configuration
+ * @input_index - Given input index from application
+ * @sub_device_index - index into sd table
+ *
+ * lookup the sub device information for a given input index.
+ * we report all the inputs to application. inputs table also
+ * has sub device name for the each input
+ */
+static struct vpif_subdev_info *vpif_map_sub_device_to_input(
+ struct channel_obj *ch,
+ struct vpif_capture_config *vpif_cfg,
+ int input_index,
+ int *sub_device_index)
+{
+ struct vpif_capture_chan_config *chan_cfg;
+ struct vpif_subdev_info *subdev_info = NULL;
+ const char *subdev_name = NULL;
+ int i;
+
+ vpif_dbg(2, debug, "vpif_map_sub_device_to_input\n");
+
+ chan_cfg = &vpif_cfg->chan_config[ch->channel_id];
+
+ /**
+ * search through the inputs to find the sub device supporting
+ * the input
+ */
+ for (i = 0; i < chan_cfg->input_count; i++) {
+ /* For each sub device, loop through input */
+ if (i == input_index) {
+ subdev_name = chan_cfg->inputs[i].subdev_name;
+ break;
+ }
+ }
+
+ /* if reached maximum. return null */
+ if (i == chan_cfg->input_count || (NULL == subdev_name))
+ return subdev_info;
+
+ /* loop through the sub device list to get the sub device info */
+ for (i = 0; i < vpif_cfg->subdev_count; i++) {
+ subdev_info = &vpif_cfg->subdev_info[i];
+ if (!strcmp(subdev_info->name, subdev_name))
+ break;
+ }
+
+ if (i == vpif_cfg->subdev_count)
+ return subdev_info;
+
+ /* check if the sub device is registered */
+ if (NULL == vpif_obj.sd[i])
+ return NULL;
+
+ *sub_device_index = i;
+ return subdev_info;
+}
+
+/**
+ * vpif_querystd() - querystd handler
+ * @file: file ptr
+ * @priv: file handle
+ * @std_id: ptr to std id
+ *
+ * This function is called to detect standard at the selected input
+ */
+static int vpif_querystd(struct file *file, void *priv, v4l2_std_id *std_id)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ int ret = 0;
+
+ vpif_dbg(2, debug, "vpif_querystd\n");
+
+ /* Call querystd function of decoder device */
+ ret = v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], video,
+ querystd, std_id);
+ if (ret < 0)
+ vpif_dbg(1, debug, "Failed to set standard for sub devices\n");
+
+ return ret;
+}
+
+/**
+ * vpif_g_std() - get STD handler
+ * @file: file ptr
+ * @priv: file handle
+ * @std_id: ptr to std id
+ */
+static int vpif_g_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+
+ vpif_dbg(2, debug, "vpif_g_std\n");
+
+ *std = ch->video.stdid;
+ return 0;
+}
+
+/**
+ * vpif_s_std() - set STD handler
+ * @file: file ptr
+ * @priv: file handle
+ * @std_id: ptr to std id
+ */
+static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ int ret = 0;
+
+ vpif_dbg(2, debug, "vpif_s_std\n");
+
+ if (common->started) {
+ vpif_err("streaming in progress\n");
+ return -EBUSY;
+ }
+
+ if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) ||
+ (VPIF_CHANNEL1_VIDEO == ch->channel_id)) {
+ if (!fh->initialized) {
+ vpif_dbg(1, debug, "Channel Busy\n");
+ return -EBUSY;
+ }
+ }
+
+ ret = v4l2_prio_check(&ch->prio, fh->prio);
+ if (0 != ret)
+ return ret;
+
+ fh->initialized = 1;
+
+ /* Call encoder subdevice function to set the standard */
+ ch->video.stdid = *std_id;
+ ch->video.dv_preset = V4L2_DV_INVALID;
+ memset(&ch->video.bt_timings, 0, sizeof(ch->video.bt_timings));
+
+ /* Get the information about the standard */
+ if (vpif_update_std_info(ch)) {
+ vpif_err("Error getting the standard info\n");
+ return -EINVAL;
+ }
+
+ /* Configure the default format information */
+ vpif_config_format(ch);
+
+ /* set standard in the sub device */
+ ret = v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], core,
+ s_std, *std_id);
+ if (ret < 0)
+ vpif_dbg(1, debug, "Failed to set standard for sub devices\n");
+ return ret;
+}
+
+/**
+ * vpif_enum_input() - ENUMINPUT handler
+ * @file: file ptr
+ * @priv: file handle
+ * @input: ptr to input structure
+ */
+static int vpif_enum_input(struct file *file, void *priv,
+ struct v4l2_input *input)
+{
+
+ struct vpif_capture_config *config = vpif_dev->platform_data;
+ struct vpif_capture_chan_config *chan_cfg;
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+
+ chan_cfg = &config->chan_config[ch->channel_id];
+
+ if (input->index >= chan_cfg->input_count) {
+ vpif_dbg(1, debug, "Invalid input index\n");
+ return -EINVAL;
+ }
+
+ memcpy(input, &chan_cfg->inputs[input->index].input,
+ sizeof(*input));
+ return 0;
+}
+
+/**
+ * vpif_g_input() - Get INPUT handler
+ * @file: file ptr
+ * @priv: file handle
+ * @index: ptr to input index
+ */
+static int vpif_g_input(struct file *file, void *priv, unsigned int *index)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct video_obj *vid_ch = &ch->video;
+
+ *index = vid_ch->input_idx;
+
+ return 0;
+}
+
+/**
+ * vpif_s_input() - Set INPUT handler
+ * @file: file ptr
+ * @priv: file handle
+ * @index: input index
+ */
+static int vpif_s_input(struct file *file, void *priv, unsigned int index)
+{
+ struct vpif_capture_config *config = vpif_dev->platform_data;
+ struct vpif_capture_chan_config *chan_cfg;
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct video_obj *vid_ch = &ch->video;
+ struct vpif_subdev_info *subdev_info;
+ int ret = 0, sd_index = 0;
+ u32 input = 0, output = 0;
+
+ chan_cfg = &config->chan_config[ch->channel_id];
+
+ if (common->started) {
+ vpif_err("Streaming in progress\n");
+ return -EBUSY;
+ }
+
+ if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) ||
+ (VPIF_CHANNEL1_VIDEO == ch->channel_id)) {
+ if (!fh->initialized) {
+ vpif_dbg(1, debug, "Channel Busy\n");
+ return -EBUSY;
+ }
+ }
+
+ ret = v4l2_prio_check(&ch->prio, fh->prio);
+ if (0 != ret)
+ return ret;
+
+ fh->initialized = 1;
+ subdev_info = vpif_map_sub_device_to_input(ch, config, index,
+ &sd_index);
+ if (NULL == subdev_info) {
+ vpif_dbg(1, debug,
+ "couldn't lookup sub device for the input index\n");
+ return -EINVAL;
+ }
+
+ /* first setup input path from sub device to vpif */
+ if (config->setup_input_path) {
+ ret = config->setup_input_path(ch->channel_id,
+ subdev_info->name);
+ if (ret < 0) {
+ vpif_dbg(1, debug, "couldn't setup input path for the"
+ " sub device %s, for input index %d\n",
+ subdev_info->name, index);
+ return ret;
+ }
+ }
+
+ if (subdev_info->can_route) {
+ input = subdev_info->input;
+ output = subdev_info->output;
+ ret = v4l2_subdev_call(vpif_obj.sd[sd_index], video, s_routing,
+ input, output, 0);
+ if (ret < 0) {
+ vpif_dbg(1, debug, "Failed to set input\n");
+ return ret;
+ }
+ }
+ vid_ch->input_idx = index;
+ ch->curr_subdev_info = subdev_info;
+ ch->curr_sd_index = sd_index;
+ /* copy interface parameters to vpif */
+ ch->vpifparams.iface = subdev_info->vpif_if;
+
+ /* update tvnorms from the sub device input info */
+ ch->video_dev->tvnorms = chan_cfg->inputs[index].input.std;
+ return ret;
+}
+
+/**
+ * vpif_enum_fmt_vid_cap() - ENUM_FMT handler
+ * @file: file ptr
+ * @priv: file handle
+ * @index: input index
+ */
+static int vpif_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *fmt)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+
+ if (fmt->index != 0) {
+ vpif_dbg(1, debug, "Invalid format index\n");
+ return -EINVAL;
+ }
+
+ /* Fill in the information about format */
+ if (ch->vpifparams.iface.if_type == VPIF_IF_RAW_BAYER) {
+ fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ strcpy(fmt->description, "Raw Mode -Bayer Pattern GrRBGb");
+ fmt->pixelformat = V4L2_PIX_FMT_SBGGR8;
+ } else {
+ fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ strcpy(fmt->description, "YCbCr4:2:2 YC Planar");
+ fmt->pixelformat = V4L2_PIX_FMT_YUV422P;
+ }
+ return 0;
+}
+
+/**
+ * vpif_try_fmt_vid_cap() - TRY_FMT handler
+ * @file: file ptr
+ * @priv: file handle
+ * @fmt: ptr to v4l2 format structure
+ */
+static int vpif_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+
+ return vpif_check_format(ch, pixfmt, 1);
+}
+
+
+/**
+ * vpif_g_fmt_vid_cap() - Set INPUT handler
+ * @file: file ptr
+ * @priv: file handle
+ * @fmt: ptr to v4l2 format structure
+ */
+static int vpif_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+
+ /* Check the validity of the buffer type */
+ if (common->fmt.type != fmt->type)
+ return -EINVAL;
+
+ /* Fill in the information about format */
+ *fmt = common->fmt;
+ return 0;
+}
+
+/**
+ * vpif_s_fmt_vid_cap() - Set FMT handler
+ * @file: file ptr
+ * @priv: file handle
+ * @fmt: ptr to v4l2 format structure
+ */
+static int vpif_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct v4l2_pix_format *pixfmt;
+ int ret = 0;
+
+ vpif_dbg(2, debug, "%s\n", __func__);
+
+ /* If streaming is started, return error */
+ if (common->started) {
+ vpif_dbg(1, debug, "Streaming is started\n");
+ return -EBUSY;
+ }
+
+ if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) ||
+ (VPIF_CHANNEL1_VIDEO == ch->channel_id)) {
+ if (!fh->initialized) {
+ vpif_dbg(1, debug, "Channel Busy\n");
+ return -EBUSY;
+ }
+ }
+
+ ret = v4l2_prio_check(&ch->prio, fh->prio);
+ if (0 != ret)
+ return ret;
+
+ fh->initialized = 1;
+
+ pixfmt = &fmt->fmt.pix;
+ /* Check for valid field format */
+ ret = vpif_check_format(ch, pixfmt, 0);
+
+ if (ret)
+ return ret;
+ /* store the format in the channel object */
+ common->fmt = *fmt;
+ return 0;
+}
+
+/**
+ * vpif_querycap() - QUERYCAP handler
+ * @file: file ptr
+ * @priv: file handle
+ * @cap: ptr to v4l2_capability structure
+ */
+static int vpif_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct vpif_capture_config *config = vpif_dev->platform_data;
+
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ strlcpy(cap->driver, "vpif capture", sizeof(cap->driver));
+ strlcpy(cap->bus_info, "DM646x Platform", sizeof(cap->bus_info));
+ strlcpy(cap->card, config->card_name, sizeof(cap->card));
+
+ return 0;
+}
+
+/**
+ * vpif_g_priority() - get priority handler
+ * @file: file ptr
+ * @priv: file handle
+ * @prio: ptr to v4l2_priority structure
+ */
+static int vpif_g_priority(struct file *file, void *priv,
+ enum v4l2_priority *prio)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+
+ *prio = v4l2_prio_max(&ch->prio);
+
+ return 0;
+}
+
+/**
+ * vpif_s_priority() - set priority handler
+ * @file: file ptr
+ * @priv: file handle
+ * @prio: ptr to v4l2_priority structure
+ */
+static int vpif_s_priority(struct file *file, void *priv, enum v4l2_priority p)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+
+ return v4l2_prio_change(&ch->prio, &fh->prio, p);
+}
+
+/**
+ * vpif_cropcap() - cropcap handler
+ * @file: file ptr
+ * @priv: file handle
+ * @crop: ptr to v4l2_cropcap structure
+ */
+static int vpif_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *crop)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != crop->type)
+ return -EINVAL;
+
+ crop->bounds.left = 0;
+ crop->bounds.top = 0;
+ crop->bounds.height = common->height;
+ crop->bounds.width = common->width;
+ crop->defrect = crop->bounds;
+ return 0;
+}
+
+/**
+ * vpif_enum_dv_presets() - ENUM_DV_PRESETS handler
+ * @file: file ptr
+ * @priv: file handle
+ * @preset: input preset
+ */
+static int vpif_enum_dv_presets(struct file *file, void *priv,
+ struct v4l2_dv_enum_preset *preset)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+
+ return v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index],
+ video, enum_dv_presets, preset);
+}
+
+/**
+ * vpif_query_dv_presets() - QUERY_DV_PRESET handler
+ * @file: file ptr
+ * @priv: file handle
+ * @preset: input preset
+ */
+static int vpif_query_dv_preset(struct file *file, void *priv,
+ struct v4l2_dv_preset *preset)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+
+ return v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index],
+ video, query_dv_preset, preset);
+}
+/**
+ * vpif_s_dv_presets() - S_DV_PRESETS handler
+ * @file: file ptr
+ * @priv: file handle
+ * @preset: input preset
+ */
+static int vpif_s_dv_preset(struct file *file, void *priv,
+ struct v4l2_dv_preset *preset)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ int ret = 0;
+
+ if (common->started) {
+ vpif_dbg(1, debug, "streaming in progress\n");
+ return -EBUSY;
+ }
+
+ if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) ||
+ (VPIF_CHANNEL1_VIDEO == ch->channel_id)) {
+ if (!fh->initialized) {
+ vpif_dbg(1, debug, "Channel Busy\n");
+ return -EBUSY;
+ }
+ }
+
+ ret = v4l2_prio_check(&ch->prio, fh->prio);
+ if (ret)
+ return ret;
+
+ fh->initialized = 1;
+
+ /* Call encoder subdevice function to set the standard */
+ if (mutex_lock_interruptible(&common->lock))
+ return -ERESTARTSYS;
+
+ ch->video.dv_preset = preset->preset;
+ ch->video.stdid = V4L2_STD_UNKNOWN;
+ memset(&ch->video.bt_timings, 0, sizeof(ch->video.bt_timings));
+
+ /* Get the information about the standard */
+ if (vpif_update_std_info(ch)) {
+ vpif_dbg(1, debug, "Error getting the standard info\n");
+ ret = -EINVAL;
+ } else {
+ /* Configure the default format information */
+ vpif_config_format(ch);
+
+ ret = v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index],
+ video, s_dv_preset, preset);
+ }
+
+ mutex_unlock(&common->lock);
+
+ return ret;
+}
+/**
+ * vpif_g_dv_presets() - G_DV_PRESETS handler
+ * @file: file ptr
+ * @priv: file handle
+ * @preset: input preset
+ */
+static int vpif_g_dv_preset(struct file *file, void *priv,
+ struct v4l2_dv_preset *preset)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+
+ preset->preset = ch->video.dv_preset;
+
+ return 0;
+}
+
+/**
+ * vpif_s_dv_timings() - S_DV_TIMINGS handler
+ * @file: file ptr
+ * @priv: file handle
+ * @timings: digital video timings
+ */
+static int vpif_s_dv_timings(struct file *file, void *priv,
+ struct v4l2_dv_timings *timings)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct vpif_params *vpifparams = &ch->vpifparams;
+ struct vpif_channel_config_params *std_info = &vpifparams->std_info;
+ struct video_obj *vid_ch = &ch->video;
+ struct v4l2_bt_timings *bt = &vid_ch->bt_timings;
+ int ret;
+
+ if (timings->type != V4L2_DV_BT_656_1120) {
+ vpif_dbg(2, debug, "Timing type not defined\n");
+ return -EINVAL;
+ }
+
+ /* Configure subdevice timings, if any */
+ ret = v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index],
+ video, s_dv_timings, timings);
+ if (ret == -ENOIOCTLCMD) {
+ vpif_dbg(2, debug, "Custom DV timings not supported by "
+ "subdevice\n");
+ return -EINVAL;
+ }
+ if (ret < 0) {
+ vpif_dbg(2, debug, "Error setting custom DV timings\n");
+ return ret;
+ }
+
+ if (!(timings->bt.width && timings->bt.height &&
+ (timings->bt.hbackporch ||
+ timings->bt.hfrontporch ||
+ timings->bt.hsync) &&
+ timings->bt.vfrontporch &&
+ (timings->bt.vbackporch ||
+ timings->bt.vsync))) {
+ vpif_dbg(2, debug, "Timings for width, height, "
+ "horizontal back porch, horizontal sync, "
+ "horizontal front porch, vertical back porch, "
+ "vertical sync and vertical back porch "
+ "must be defined\n");
+ return -EINVAL;
+ }
+
+ *bt = timings->bt;
+
+ /* Configure video port timings */
+
+ std_info->eav2sav = bt->hbackporch + bt->hfrontporch +
+ bt->hsync - 8;
+ std_info->sav2eav = bt->width;
+
+ std_info->l1 = 1;
+ std_info->l3 = bt->vsync + bt->vbackporch + 1;
+
+ if (bt->interlaced) {
+ if (bt->il_vbackporch || bt->il_vfrontporch || bt->il_vsync) {
+ std_info->vsize = bt->height * 2 +
+ bt->vfrontporch + bt->vsync + bt->vbackporch +
+ bt->il_vfrontporch + bt->il_vsync +
+ bt->il_vbackporch;
+ std_info->l5 = std_info->vsize/2 -
+ (bt->vfrontporch - 1);
+ std_info->l7 = std_info->vsize/2 + 1;
+ std_info->l9 = std_info->l7 + bt->il_vsync +
+ bt->il_vbackporch + 1;
+ std_info->l11 = std_info->vsize -
+ (bt->il_vfrontporch - 1);
+ } else {
+ vpif_dbg(2, debug, "Required timing values for "
+ "interlaced BT format missing\n");
+ return -EINVAL;
+ }
+ } else {
+ std_info->vsize = bt->height + bt->vfrontporch +
+ bt->vsync + bt->vbackporch;
+ std_info->l5 = std_info->vsize - (bt->vfrontporch - 1);
+ }
+ strncpy(std_info->name, "Custom timings BT656/1120", VPIF_MAX_NAME);
+ std_info->width = bt->width;
+ std_info->height = bt->height;
+ std_info->frm_fmt = bt->interlaced ? 0 : 1;
+ std_info->ycmux_mode = 0;
+ std_info->capture_format = 0;
+ std_info->vbi_supported = 0;
+ std_info->hd_sd = 1;
+ std_info->stdid = 0;
+ std_info->dv_preset = V4L2_DV_INVALID;
+
+ vid_ch->stdid = 0;
+ vid_ch->dv_preset = V4L2_DV_INVALID;
+ return 0;
+}
+
+/**
+ * vpif_g_dv_timings() - G_DV_TIMINGS handler
+ * @file: file ptr
+ * @priv: file handle
+ * @timings: digital video timings
+ */
+static int vpif_g_dv_timings(struct file *file, void *priv,
+ struct v4l2_dv_timings *timings)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct video_obj *vid_ch = &ch->video;
+ struct v4l2_bt_timings *bt = &vid_ch->bt_timings;
+
+ timings->bt = *bt;
+
+ return 0;
+}
+
+/*
+ * vpif_g_chip_ident() - Identify the chip
+ * @file: file ptr
+ * @priv: file handle
+ * @chip: chip identity
+ *
+ * Returns zero or -EINVAL if read operations fails.
+ */
+static int vpif_g_chip_ident(struct file *file, void *priv,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+ if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
+ chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) {
+ vpif_dbg(2, debug, "match_type is invalid.\n");
+ return -EINVAL;
+ }
+
+ return v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 0, core,
+ g_chip_ident, chip);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+/*
+ * vpif_dbg_g_register() - Read register
+ * @file: file ptr
+ * @priv: file handle
+ * @reg: register to be read
+ *
+ * Debugging only
+ * Returns zero or -EINVAL if read operations fails.
+ */
+static int vpif_dbg_g_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg){
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+
+ return v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], core,
+ g_register, reg);
+}
+
+/*
+ * vpif_dbg_s_register() - Write to register
+ * @file: file ptr
+ * @priv: file handle
+ * @reg: register to be modified
+ *
+ * Debugging only
+ * Returns zero or -EINVAL if write operations fails.
+ */
+static int vpif_dbg_s_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg){
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+
+ return v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], core,
+ s_register, reg);
+}
+#endif
+
+/*
+ * vpif_log_status() - Status information
+ * @file: file ptr
+ * @priv: file handle
+ *
+ * Returns zero.
+ */
+static int vpif_log_status(struct file *filep, void *priv)
+{
+ /* status for sub devices */
+ v4l2_device_call_all(&vpif_obj.v4l2_dev, 0, core, log_status);
+
+ return 0;
+}
+
+/* vpif capture ioctl operations */
+static const struct v4l2_ioctl_ops vpif_ioctl_ops = {
+ .vidioc_querycap = vpif_querycap,
+ .vidioc_g_priority = vpif_g_priority,
+ .vidioc_s_priority = vpif_s_priority,
+ .vidioc_enum_fmt_vid_cap = vpif_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vpif_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vpif_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vpif_try_fmt_vid_cap,
+ .vidioc_enum_input = vpif_enum_input,
+ .vidioc_s_input = vpif_s_input,
+ .vidioc_g_input = vpif_g_input,
+ .vidioc_reqbufs = vpif_reqbufs,
+ .vidioc_querybuf = vpif_querybuf,
+ .vidioc_querystd = vpif_querystd,
+ .vidioc_s_std = vpif_s_std,
+ .vidioc_g_std = vpif_g_std,
+ .vidioc_qbuf = vpif_qbuf,
+ .vidioc_dqbuf = vpif_dqbuf,
+ .vidioc_streamon = vpif_streamon,
+ .vidioc_streamoff = vpif_streamoff,
+ .vidioc_cropcap = vpif_cropcap,
+ .vidioc_enum_dv_presets = vpif_enum_dv_presets,
+ .vidioc_s_dv_preset = vpif_s_dv_preset,
+ .vidioc_g_dv_preset = vpif_g_dv_preset,
+ .vidioc_query_dv_preset = vpif_query_dv_preset,
+ .vidioc_s_dv_timings = vpif_s_dv_timings,
+ .vidioc_g_dv_timings = vpif_g_dv_timings,
+ .vidioc_g_chip_ident = vpif_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vpif_dbg_g_register,
+ .vidioc_s_register = vpif_dbg_s_register,
+#endif
+ .vidioc_log_status = vpif_log_status,
+};
+
+/* vpif file operations */
+static struct v4l2_file_operations vpif_fops = {
+ .owner = THIS_MODULE,
+ .open = vpif_open,
+ .release = vpif_release,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vpif_mmap,
+ .poll = vpif_poll
+};
+
+/* vpif video template */
+static struct video_device vpif_video_template = {
+ .name = "vpif",
+ .fops = &vpif_fops,
+ .minor = -1,
+ .ioctl_ops = &vpif_ioctl_ops,
+};
+
+/**
+ * initialize_vpif() - Initialize vpif data structures
+ *
+ * Allocate memory for data structures and initialize them
+ */
+static int initialize_vpif(void)
+{
+ int err = 0, i, j;
+ int free_channel_objects_index;
+
+ /* Default number of buffers should be 3 */
+ if ((ch0_numbuffers > 0) &&
+ (ch0_numbuffers < config_params.min_numbuffers))
+ ch0_numbuffers = config_params.min_numbuffers;
+ if ((ch1_numbuffers > 0) &&
+ (ch1_numbuffers < config_params.min_numbuffers))
+ ch1_numbuffers = config_params.min_numbuffers;
+
+ /* Set buffer size to min buffers size if it is invalid */
+ if (ch0_bufsize < config_params.min_bufsize[VPIF_CHANNEL0_VIDEO])
+ ch0_bufsize =
+ config_params.min_bufsize[VPIF_CHANNEL0_VIDEO];
+ if (ch1_bufsize < config_params.min_bufsize[VPIF_CHANNEL1_VIDEO])
+ ch1_bufsize =
+ config_params.min_bufsize[VPIF_CHANNEL1_VIDEO];
+
+ config_params.numbuffers[VPIF_CHANNEL0_VIDEO] = ch0_numbuffers;
+ config_params.numbuffers[VPIF_CHANNEL1_VIDEO] = ch1_numbuffers;
+ if (ch0_numbuffers) {
+ config_params.channel_bufsize[VPIF_CHANNEL0_VIDEO]
+ = ch0_bufsize;
+ }
+ if (ch1_numbuffers) {
+ config_params.channel_bufsize[VPIF_CHANNEL1_VIDEO]
+ = ch1_bufsize;
+ }
+
+ /* Allocate memory for six channel objects */
+ for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
+ vpif_obj.dev[i] =
+ kzalloc(sizeof(*vpif_obj.dev[i]), GFP_KERNEL);
+ /* If memory allocation fails, return error */
+ if (!vpif_obj.dev[i]) {
+ free_channel_objects_index = i;
+ err = -ENOMEM;
+ goto vpif_init_free_channel_objects;
+ }
+ }
+ return 0;
+
+vpif_init_free_channel_objects:
+ for (j = 0; j < free_channel_objects_index; j++)
+ kfree(vpif_obj.dev[j]);
+ return err;
+}
+
+/**
+ * vpif_probe : This function probes the vpif capture driver
+ * @pdev: platform device pointer
+ *
+ * This creates device entries by register itself to the V4L2 driver and
+ * initializes fields of each channel objects
+ */
+static __init int vpif_probe(struct platform_device *pdev)
+{
+ struct vpif_subdev_info *subdevdata;
+ struct vpif_capture_config *config;
+ int i, j, k, m, q, err;
+ struct i2c_adapter *i2c_adap;
+ struct channel_obj *ch;
+ struct common_obj *common;
+ struct video_device *vfd;
+ struct resource *res;
+ int subdev_count;
+
+ vpif_dev = &pdev->dev;
+
+ err = initialize_vpif();
+ if (err) {
+ v4l2_err(vpif_dev->driver, "Error initializing vpif\n");
+ return err;
+ }
+
+ err = v4l2_device_register(vpif_dev, &vpif_obj.v4l2_dev);
+ if (err) {
+ v4l2_err(vpif_dev->driver, "Error registering v4l2 device\n");
+ return err;
+ }
+
+ k = 0;
+ while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, k))) {
+ for (i = res->start; i <= res->end; i++) {
+ if (request_irq(i, vpif_channel_isr, IRQF_DISABLED,
+ "DM646x_Capture",
+ (void *)(&vpif_obj.dev[k]->channel_id))) {
+ err = -EBUSY;
+ i--;
+ goto vpif_int_err;
+ }
+ }
+ k++;
+ }
+
+ for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
+ /* Get the pointer to the channel object */
+ ch = vpif_obj.dev[i];
+ /* Allocate memory for video device */
+ vfd = video_device_alloc();
+ if (NULL == vfd) {
+ for (j = 0; j < i; j++) {
+ ch = vpif_obj.dev[j];
+ video_device_release(ch->video_dev);
+ }
+ err = -ENOMEM;
+ goto vpif_dev_alloc_err;
+ }
+
+ /* Initialize field of video device */
+ *vfd = vpif_video_template;
+ vfd->v4l2_dev = &vpif_obj.v4l2_dev;
+ vfd->release = video_device_release;
+ snprintf(vfd->name, sizeof(vfd->name),
+ "DM646x_VPIFCapture_DRIVER_V%s",
+ VPIF_CAPTURE_VERSION);
+ /* Set video_dev to the video device */
+ ch->video_dev = vfd;
+ }
+
+ for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) {
+ ch = vpif_obj.dev[j];
+ ch->channel_id = j;
+ common = &(ch->common[VPIF_VIDEO_INDEX]);
+ spin_lock_init(&common->irqlock);
+ mutex_init(&common->lock);
+ ch->video_dev->lock = &common->lock;
+ /* Initialize prio member of channel object */
+ v4l2_prio_init(&ch->prio);
+ err = video_register_device(ch->video_dev,
+ VFL_TYPE_GRABBER, (j ? 1 : 0));
+ if (err)
+ goto probe_out;
+
+ video_set_drvdata(ch->video_dev, ch);
+
+ }
+
+ i2c_adap = i2c_get_adapter(1);
+ config = pdev->dev.platform_data;
+
+ subdev_count = config->subdev_count;
+ vpif_obj.sd = kzalloc(sizeof(struct v4l2_subdev *) * subdev_count,
+ GFP_KERNEL);
+ if (vpif_obj.sd == NULL) {
+ vpif_err("unable to allocate memory for subdevice pointers\n");
+ err = -ENOMEM;
+ goto probe_out;
+ }
+
+ for (i = 0; i < subdev_count; i++) {
+ subdevdata = &config->subdev_info[i];
+ vpif_obj.sd[i] =
+ v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev,
+ i2c_adap,
+ &subdevdata->board_info,
+ NULL);
+
+ if (!vpif_obj.sd[i]) {
+ vpif_err("Error registering v4l2 subdevice\n");
+ goto probe_subdev_out;
+ }
+ v4l2_info(&vpif_obj.v4l2_dev, "registered sub device %s\n",
+ subdevdata->name);
+
+ if (vpif_obj.sd[i])
+ vpif_obj.sd[i]->grp_id = 1 << i;
+ }
+
+ v4l2_info(&vpif_obj.v4l2_dev,
+ "DM646x VPIF capture driver initialized\n");
+ return 0;
+
+probe_subdev_out:
+ /* free sub devices memory */
+ kfree(vpif_obj.sd);
+
+ j = VPIF_CAPTURE_MAX_DEVICES;
+probe_out:
+ for (k = 0; k < j; k++) {
+ /* Get the pointer to the channel object */
+ ch = vpif_obj.dev[k];
+ /* Unregister video device */
+ video_unregister_device(ch->video_dev);
+ }
+
+vpif_dev_alloc_err:
+ k = VPIF_CAPTURE_MAX_DEVICES-1;
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, k);
+ i = res->end;
+
+vpif_int_err:
+ for (q = k; q >= 0; q--) {
+ for (m = i; m >= (int)res->start; m--)
+ free_irq(m, (void *)(&vpif_obj.dev[q]->channel_id));
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, q-1);
+ if (res)
+ i = res->end;
+ }
+ v4l2_device_unregister(&vpif_obj.v4l2_dev);
+ return err;
+}
+
+/**
+ * vpif_remove() - driver remove handler
+ * @device: ptr to platform device structure
+ *
+ * The vidoe device is unregistered
+ */
+static int vpif_remove(struct platform_device *device)
+{
+ int i;
+ struct channel_obj *ch;
+
+ v4l2_device_unregister(&vpif_obj.v4l2_dev);
+
+ /* un-register device */
+ for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
+ /* Get the pointer to the channel object */
+ ch = vpif_obj.dev[i];
+ /* Unregister video device */
+ video_unregister_device(ch->video_dev);
+ }
+ return 0;
+}
+
+/**
+ * vpif_suspend: vpif device suspend
+ *
+ * TODO: Add suspend code here
+ */
+static int
+vpif_suspend(struct device *dev)
+{
+ return -1;
+}
+
+/**
+ * vpif_resume: vpif device suspend
+ *
+ * TODO: Add resume code here
+ */
+static int
+vpif_resume(struct device *dev)
+{
+ return -1;
+}
+
+static const struct dev_pm_ops vpif_dev_pm_ops = {
+ .suspend = vpif_suspend,
+ .resume = vpif_resume,
+};
+
+static __refdata struct platform_driver vpif_driver = {
+ .driver = {
+ .name = "vpif_capture",
+ .owner = THIS_MODULE,
+ .pm = &vpif_dev_pm_ops,
+ },
+ .probe = vpif_probe,
+ .remove = vpif_remove,
+};
+
+/**
+ * vpif_init: initialize the vpif driver
+ *
+ * This function registers device and driver to the kernel, requests irq
+ * handler and allocates memory
+ * for channel objects
+ */
+static __init int vpif_init(void)
+{
+ return platform_driver_register(&vpif_driver);
+}
+
+/**
+ * vpif_cleanup : This function clean up the vpif capture resources
+ *
+ * This will un-registers device and driver to the kernel, frees
+ * requested irq handler and de-allocates memory allocated for channel
+ * objects.
+ */
+static void vpif_cleanup(void)
+{
+ struct platform_device *pdev;
+ struct resource *res;
+ int irq_num;
+ int i = 0;
+
+ pdev = container_of(vpif_dev, struct platform_device, dev);
+ while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, i))) {
+ for (irq_num = res->start; irq_num <= res->end; irq_num++)
+ free_irq(irq_num,
+ (void *)(&vpif_obj.dev[i]->channel_id));
+ i++;
+ }
+
+ platform_driver_unregister(&vpif_driver);
+
+ kfree(vpif_obj.sd);
+ for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++)
+ kfree(vpif_obj.dev[i]);
+}
+
+/* Function for module initialization and cleanup */
+module_init(vpif_init);
+module_exit(vpif_cleanup);
diff --git a/drivers/media/video/davinci/vpif_capture.h b/drivers/media/video/davinci/vpif_capture.h
new file mode 100644
index 00000000..a693d4eb
--- /dev/null
+++ b/drivers/media/video/davinci/vpif_capture.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2009 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef VPIF_CAPTURE_H
+#define VPIF_CAPTURE_H
+
+#ifdef __KERNEL__
+
+/* Header files */
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf-core.h>
+#include <media/videobuf-dma-contig.h>
+#include <media/davinci/vpif_types.h>
+
+#include "vpif.h"
+
+/* Macros */
+#define VPIF_CAPTURE_VERSION "0.0.2"
+
+#define VPIF_VALID_FIELD(field) (((V4L2_FIELD_ANY == field) || \
+ (V4L2_FIELD_NONE == field)) || \
+ (((V4L2_FIELD_INTERLACED == field) || \
+ (V4L2_FIELD_SEQ_TB == field)) || \
+ (V4L2_FIELD_SEQ_BT == field)))
+
+#define VPIF_CAPTURE_MAX_DEVICES 2
+#define VPIF_VIDEO_INDEX 0
+#define VPIF_NUMBER_OF_OBJECTS 1
+
+/* Enumerated data type to give id to each device per channel */
+enum vpif_channel_id {
+ VPIF_CHANNEL0_VIDEO = 0,
+ VPIF_CHANNEL1_VIDEO,
+};
+
+struct video_obj {
+ enum v4l2_field buf_field;
+ /* Currently selected or default standard */
+ v4l2_std_id stdid;
+ u32 dv_preset;
+ struct v4l2_bt_timings bt_timings;
+ /* This is to track the last input that is passed to application */
+ u32 input_idx;
+};
+
+struct common_obj {
+ /* Pointer pointing to current v4l2_buffer */
+ struct videobuf_buffer *cur_frm;
+ /* Pointer pointing to current v4l2_buffer */
+ struct videobuf_buffer *next_frm;
+ /*
+ * This field keeps track of type of buffer exchange mechanism
+ * user has selected
+ */
+ enum v4l2_memory memory;
+ /* Used to store pixel format */
+ struct v4l2_format fmt;
+ /* Buffer queue used in video-buf */
+ struct videobuf_queue buffer_queue;
+ /* Queue of filled frames */
+ struct list_head dma_queue;
+ /* Used in video-buf */
+ spinlock_t irqlock;
+ /* lock used to access this structure */
+ struct mutex lock;
+ /* number of users performing IO */
+ u32 io_usrs;
+ /* Indicates whether streaming started */
+ u8 started;
+ /* Function pointer to set the addresses */
+ void (*set_addr) (unsigned long, unsigned long, unsigned long,
+ unsigned long);
+ /* offset where Y top starts from the starting of the buffer */
+ u32 ytop_off;
+ /* offset where Y bottom starts from the starting of the buffer */
+ u32 ybtm_off;
+ /* offset where C top starts from the starting of the buffer */
+ u32 ctop_off;
+ /* offset where C bottom starts from the starting of the buffer */
+ u32 cbtm_off;
+ /* Indicates width of the image data */
+ u32 width;
+ /* Indicates height of the image data */
+ u32 height;
+};
+
+struct channel_obj {
+ /* Identifies video device for this channel */
+ struct video_device *video_dev;
+ /* Used to keep track of state of the priority */
+ struct v4l2_prio_state prio;
+ /* number of open instances of the channel */
+ int usrs;
+ /* Indicates id of the field which is being displayed */
+ u32 field_id;
+ /* flag to indicate whether decoder is initialized */
+ u8 initialized;
+ /* Identifies channel */
+ enum vpif_channel_id channel_id;
+ /* index into sd table */
+ int curr_sd_index;
+ /* ptr to current sub device information */
+ struct vpif_subdev_info *curr_subdev_info;
+ /* vpif configuration params */
+ struct vpif_params vpifparams;
+ /* common object array */
+ struct common_obj common[VPIF_NUMBER_OF_OBJECTS];
+ /* video object */
+ struct video_obj video;
+};
+
+/* File handle structure */
+struct vpif_fh {
+ /* pointer to channel object for opened device */
+ struct channel_obj *channel;
+ /* Indicates whether this file handle is doing IO */
+ u8 io_allowed[VPIF_NUMBER_OF_OBJECTS];
+ /* Used to keep track priority of this instance */
+ enum v4l2_priority prio;
+ /* Used to indicate channel is initialize or not */
+ u8 initialized;
+};
+
+struct vpif_device {
+ struct v4l2_device v4l2_dev;
+ struct channel_obj *dev[VPIF_CAPTURE_NUM_CHANNELS];
+ struct v4l2_subdev **sd;
+};
+
+struct vpif_config_params {
+ u8 min_numbuffers;
+ u8 numbuffers[VPIF_CAPTURE_NUM_CHANNELS];
+ s8 device_type;
+ u32 min_bufsize[VPIF_CAPTURE_NUM_CHANNELS];
+ u32 channel_bufsize[VPIF_CAPTURE_NUM_CHANNELS];
+ u8 default_device[VPIF_CAPTURE_NUM_CHANNELS];
+ u8 max_device_type;
+};
+/* Struct which keeps track of the line numbers for the sliced vbi service */
+struct vpif_service_line {
+ u16 service_id;
+ u16 service_line[2];
+};
+#endif /* End of __KERNEL__ */
+#endif /* VPIF_CAPTURE_H */
diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c
new file mode 100644
index 00000000..7fa34b4f
--- /dev/null
+++ b/drivers/media/video/davinci/vpif_display.c
@@ -0,0 +1,1912 @@
+/*
+ * vpif-display - VPIF display driver
+ * Display driver for TI DaVinci VPIF
+ *
+ * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed .as is. WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/string.h>
+#include <linux/videodev2.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#include <asm/irq.h>
+#include <asm/page.h>
+
+#include <media/adv7343.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-chip-ident.h>
+
+#include "vpif_display.h"
+#include "vpif.h"
+
+MODULE_DESCRIPTION("TI DaVinci VPIF Display driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(VPIF_DISPLAY_VERSION);
+
+#define DM646X_V4L2_STD (V4L2_STD_525_60 | V4L2_STD_625_50)
+
+#define vpif_err(fmt, arg...) v4l2_err(&vpif_obj.v4l2_dev, fmt, ## arg)
+#define vpif_dbg(level, debug, fmt, arg...) \
+ v4l2_dbg(level, debug, &vpif_obj.v4l2_dev, fmt, ## arg)
+
+static int debug = 1;
+static u32 ch2_numbuffers = 3;
+static u32 ch3_numbuffers = 3;
+static u32 ch2_bufsize = 1920 * 1080 * 2;
+static u32 ch3_bufsize = 720 * 576 * 2;
+
+module_param(debug, int, 0644);
+module_param(ch2_numbuffers, uint, S_IRUGO);
+module_param(ch3_numbuffers, uint, S_IRUGO);
+module_param(ch2_bufsize, uint, S_IRUGO);
+module_param(ch3_bufsize, uint, S_IRUGO);
+
+MODULE_PARM_DESC(debug, "Debug level 0-1");
+MODULE_PARM_DESC(ch2_numbuffers, "Channel2 buffer count (default:3)");
+MODULE_PARM_DESC(ch3_numbuffers, "Channel3 buffer count (default:3)");
+MODULE_PARM_DESC(ch2_bufsize, "Channel2 buffer size (default:1920 x 1080 x 2)");
+MODULE_PARM_DESC(ch3_bufsize, "Channel3 buffer size (default:720 x 576 x 2)");
+
+static struct vpif_config_params config_params = {
+ .min_numbuffers = 3,
+ .numbuffers[0] = 3,
+ .numbuffers[1] = 3,
+ .min_bufsize[0] = 720 * 480 * 2,
+ .min_bufsize[1] = 720 * 480 * 2,
+ .channel_bufsize[0] = 1920 * 1080 * 2,
+ .channel_bufsize[1] = 720 * 576 * 2,
+};
+
+static struct vpif_device vpif_obj = { {NULL} };
+static struct device *vpif_dev;
+
+/*
+ * vpif_uservirt_to_phys: This function is used to convert user
+ * space virtual address to physical address.
+ */
+static u32 vpif_uservirt_to_phys(u32 virtp)
+{
+ struct mm_struct *mm = current->mm;
+ unsigned long physp = 0;
+ struct vm_area_struct *vma;
+
+ vma = find_vma(mm, virtp);
+
+ /* For kernel direct-mapped memory, take the easy way */
+ if (virtp >= PAGE_OFFSET) {
+ physp = virt_to_phys((void *)virtp);
+ } else if (vma && (vma->vm_flags & VM_IO) && (vma->vm_pgoff)) {
+ /* this will catch, kernel-allocated, mmaped-to-usermode addr */
+ physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start);
+ } else {
+ /* otherwise, use get_user_pages() for general userland pages */
+ int res, nr_pages = 1;
+ struct page *pages;
+ down_read(&current->mm->mmap_sem);
+
+ res = get_user_pages(current, current->mm,
+ virtp, nr_pages, 1, 0, &pages, NULL);
+ up_read(&current->mm->mmap_sem);
+
+ if (res == nr_pages) {
+ physp = __pa(page_address(&pages[0]) +
+ (virtp & ~PAGE_MASK));
+ } else {
+ vpif_err("get_user_pages failed\n");
+ return 0;
+ }
+ }
+
+ return physp;
+}
+
+/*
+ * buffer_prepare: This is the callback function called from videobuf_qbuf()
+ * function the buffer is prepared and user space virtual address is converted
+ * into physical address
+ */
+static int vpif_buffer_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct vpif_fh *fh = q->priv_data;
+ struct common_obj *common;
+ unsigned long addr;
+
+ common = &fh->channel->common[VPIF_VIDEO_INDEX];
+ if (VIDEOBUF_NEEDS_INIT == vb->state) {
+ vb->width = common->width;
+ vb->height = common->height;
+ vb->size = vb->width * vb->height;
+ vb->field = field;
+ }
+ vb->state = VIDEOBUF_PREPARED;
+
+ /* if user pointer memory mechanism is used, get the physical
+ * address of the buffer */
+ if (V4L2_MEMORY_USERPTR == common->memory) {
+ if (!vb->baddr) {
+ vpif_err("buffer_address is 0\n");
+ return -EINVAL;
+ }
+
+ vb->boff = vpif_uservirt_to_phys(vb->baddr);
+ if (!ISALIGNED(vb->boff))
+ goto buf_align_exit;
+ }
+
+ addr = vb->boff;
+ if (q->streaming && (V4L2_BUF_TYPE_SLICED_VBI_OUTPUT != q->type)) {
+ if (!ISALIGNED(addr + common->ytop_off) ||
+ !ISALIGNED(addr + common->ybtm_off) ||
+ !ISALIGNED(addr + common->ctop_off) ||
+ !ISALIGNED(addr + common->cbtm_off))
+ goto buf_align_exit;
+ }
+ return 0;
+
+buf_align_exit:
+ vpif_err("buffer offset not aligned to 8 bytes\n");
+ return -EINVAL;
+}
+
+/*
+ * vpif_buffer_setup: This function allocates memory for the buffers
+ */
+static int vpif_buffer_setup(struct videobuf_queue *q, unsigned int *count,
+ unsigned int *size)
+{
+ struct vpif_fh *fh = q->priv_data;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+
+ if (V4L2_MEMORY_MMAP != common->memory)
+ return 0;
+
+ *size = config_params.channel_bufsize[ch->channel_id];
+ if (*count < config_params.min_numbuffers)
+ *count = config_params.min_numbuffers;
+
+ return 0;
+}
+
+/*
+ * vpif_buffer_queue: This function adds the buffer to DMA queue
+ */
+static void vpif_buffer_queue(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ struct vpif_fh *fh = q->priv_data;
+ struct common_obj *common;
+
+ common = &fh->channel->common[VPIF_VIDEO_INDEX];
+
+ /* add the buffer to the DMA queue */
+ list_add_tail(&vb->queue, &common->dma_queue);
+ vb->state = VIDEOBUF_QUEUED;
+}
+
+/*
+ * vpif_buffer_release: This function is called from the videobuf layer to
+ * free memory allocated to the buffers
+ */
+static void vpif_buffer_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ struct vpif_fh *fh = q->priv_data;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common;
+ unsigned int buf_size = 0;
+
+ common = &ch->common[VPIF_VIDEO_INDEX];
+
+ videobuf_dma_contig_free(q, vb);
+ vb->state = VIDEOBUF_NEEDS_INIT;
+
+ if (V4L2_MEMORY_MMAP != common->memory)
+ return;
+
+ buf_size = config_params.channel_bufsize[ch->channel_id];
+}
+
+static struct videobuf_queue_ops video_qops = {
+ .buf_setup = vpif_buffer_setup,
+ .buf_prepare = vpif_buffer_prepare,
+ .buf_queue = vpif_buffer_queue,
+ .buf_release = vpif_buffer_release,
+};
+static u8 channel_first_int[VPIF_NUMOBJECTS][2] = { {1, 1} };
+
+static void process_progressive_mode(struct common_obj *common)
+{
+ unsigned long addr = 0;
+
+ /* Get the next buffer from buffer queue */
+ common->next_frm = list_entry(common->dma_queue.next,
+ struct videobuf_buffer, queue);
+ /* Remove that buffer from the buffer queue */
+ list_del(&common->next_frm->queue);
+ /* Mark status of the buffer as active */
+ common->next_frm->state = VIDEOBUF_ACTIVE;
+
+ /* Set top and bottom field addrs in VPIF registers */
+ addr = videobuf_to_dma_contig(common->next_frm);
+ common->set_addr(addr + common->ytop_off,
+ addr + common->ybtm_off,
+ addr + common->ctop_off,
+ addr + common->cbtm_off);
+}
+
+static void process_interlaced_mode(int fid, struct common_obj *common)
+{
+ /* device field id and local field id are in sync */
+ /* If this is even field */
+ if (0 == fid) {
+ if (common->cur_frm == common->next_frm)
+ return;
+
+ /* one frame is displayed If next frame is
+ * available, release cur_frm and move on */
+ /* Copy frame display time */
+ do_gettimeofday(&common->cur_frm->ts);
+ /* Change status of the cur_frm */
+ common->cur_frm->state = VIDEOBUF_DONE;
+ /* unlock semaphore on cur_frm */
+ wake_up_interruptible(&common->cur_frm->done);
+ /* Make cur_frm pointing to next_frm */
+ common->cur_frm = common->next_frm;
+
+ } else if (1 == fid) { /* odd field */
+ if (list_empty(&common->dma_queue)
+ || (common->cur_frm != common->next_frm)) {
+ return;
+ }
+ /* one field is displayed configure the next
+ * frame if it is available else hold on current
+ * frame */
+ /* Get next from the buffer queue */
+ process_progressive_mode(common);
+
+ }
+}
+
+/*
+ * vpif_channel_isr: It changes status of the displayed buffer, takes next
+ * buffer from the queue and sets its address in VPIF registers
+ */
+static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
+{
+ struct vpif_device *dev = &vpif_obj;
+ struct channel_obj *ch;
+ struct common_obj *common;
+ enum v4l2_field field;
+ int fid = -1, i;
+ int channel_id = 0;
+
+ channel_id = *(int *)(dev_id);
+ ch = dev->dev[channel_id];
+ field = ch->common[VPIF_VIDEO_INDEX].fmt.fmt.pix.field;
+ for (i = 0; i < VPIF_NUMOBJECTS; i++) {
+ common = &ch->common[i];
+ /* If streaming is started in this channel */
+ if (0 == common->started)
+ continue;
+
+ if (1 == ch->vpifparams.std_info.frm_fmt) {
+ if (list_empty(&common->dma_queue))
+ continue;
+
+ /* Progressive mode */
+ if (!channel_first_int[i][channel_id]) {
+ /* Mark status of the cur_frm to
+ * done and unlock semaphore on it */
+ do_gettimeofday(&common->cur_frm->ts);
+ common->cur_frm->state = VIDEOBUF_DONE;
+ wake_up_interruptible(&common->cur_frm->done);
+ /* Make cur_frm pointing to next_frm */
+ common->cur_frm = common->next_frm;
+ }
+
+ channel_first_int[i][channel_id] = 0;
+ process_progressive_mode(common);
+ } else {
+ /* Interlaced mode */
+ /* If it is first interrupt, ignore it */
+
+ if (channel_first_int[i][channel_id]) {
+ channel_first_int[i][channel_id] = 0;
+ continue;
+ }
+
+ if (0 == i) {
+ ch->field_id ^= 1;
+ /* Get field id from VPIF registers */
+ fid = vpif_channel_getfid(ch->channel_id + 2);
+ /* If fid does not match with stored field id */
+ if (fid != ch->field_id) {
+ /* Make them in sync */
+ if (0 == fid)
+ ch->field_id = fid;
+
+ return IRQ_HANDLED;
+ }
+ }
+ process_interlaced_mode(fid, common);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int vpif_update_std_info(struct channel_obj *ch)
+{
+ struct video_obj *vid_ch = &ch->video;
+ struct vpif_params *vpifparams = &ch->vpifparams;
+ struct vpif_channel_config_params *std_info = &vpifparams->std_info;
+ const struct vpif_channel_config_params *config;
+
+ int i;
+
+ for (i = 0; i < vpif_ch_params_count; i++) {
+ config = &ch_params[i];
+ if (config->hd_sd == 0) {
+ vpif_dbg(2, debug, "SD format\n");
+ if (config->stdid & vid_ch->stdid) {
+ memcpy(std_info, config, sizeof(*config));
+ break;
+ }
+ } else {
+ vpif_dbg(2, debug, "HD format\n");
+ if (config->dv_preset == vid_ch->dv_preset) {
+ memcpy(std_info, config, sizeof(*config));
+ break;
+ }
+ }
+ }
+
+ if (i == vpif_ch_params_count) {
+ vpif_dbg(1, debug, "Format not found\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vpif_update_resolution(struct channel_obj *ch)
+{
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct video_obj *vid_ch = &ch->video;
+ struct vpif_params *vpifparams = &ch->vpifparams;
+ struct vpif_channel_config_params *std_info = &vpifparams->std_info;
+
+ if (!vid_ch->stdid && !vid_ch->dv_preset && !vid_ch->bt_timings.height)
+ return -EINVAL;
+
+ if (vid_ch->stdid || vid_ch->dv_preset) {
+ if (vpif_update_std_info(ch))
+ return -EINVAL;
+ }
+
+ common->fmt.fmt.pix.width = std_info->width;
+ common->fmt.fmt.pix.height = std_info->height;
+ vpif_dbg(1, debug, "Pixel details: Width = %d,Height = %d\n",
+ common->fmt.fmt.pix.width, common->fmt.fmt.pix.height);
+
+ /* Set height and width paramateres */
+ common->height = std_info->height;
+ common->width = std_info->width;
+
+ return 0;
+}
+
+/*
+ * vpif_calculate_offsets: This function calculates buffers offset for Y and C
+ * in the top and bottom field
+ */
+static void vpif_calculate_offsets(struct channel_obj *ch)
+{
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct vpif_params *vpifparams = &ch->vpifparams;
+ enum v4l2_field field = common->fmt.fmt.pix.field;
+ struct video_obj *vid_ch = &ch->video;
+ unsigned int hpitch, vpitch, sizeimage;
+
+ if (V4L2_FIELD_ANY == common->fmt.fmt.pix.field) {
+ if (ch->vpifparams.std_info.frm_fmt)
+ vid_ch->buf_field = V4L2_FIELD_NONE;
+ else
+ vid_ch->buf_field = V4L2_FIELD_INTERLACED;
+ } else {
+ vid_ch->buf_field = common->fmt.fmt.pix.field;
+ }
+
+ if (V4L2_MEMORY_USERPTR == common->memory)
+ sizeimage = common->fmt.fmt.pix.sizeimage;
+ else
+ sizeimage = config_params.channel_bufsize[ch->channel_id];
+
+ hpitch = common->fmt.fmt.pix.bytesperline;
+ vpitch = sizeimage / (hpitch * 2);
+ if ((V4L2_FIELD_NONE == vid_ch->buf_field) ||
+ (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) {
+ common->ytop_off = 0;
+ common->ybtm_off = hpitch;
+ common->ctop_off = sizeimage / 2;
+ common->cbtm_off = sizeimage / 2 + hpitch;
+ } else if (V4L2_FIELD_SEQ_TB == vid_ch->buf_field) {
+ common->ytop_off = 0;
+ common->ybtm_off = sizeimage / 4;
+ common->ctop_off = sizeimage / 2;
+ common->cbtm_off = common->ctop_off + sizeimage / 4;
+ } else if (V4L2_FIELD_SEQ_BT == vid_ch->buf_field) {
+ common->ybtm_off = 0;
+ common->ytop_off = sizeimage / 4;
+ common->cbtm_off = sizeimage / 2;
+ common->ctop_off = common->cbtm_off + sizeimage / 4;
+ }
+
+ if ((V4L2_FIELD_NONE == vid_ch->buf_field) ||
+ (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) {
+ vpifparams->video_params.storage_mode = 1;
+ } else {
+ vpifparams->video_params.storage_mode = 0;
+ }
+
+ if (ch->vpifparams.std_info.frm_fmt == 1) {
+ vpifparams->video_params.hpitch =
+ common->fmt.fmt.pix.bytesperline;
+ } else {
+ if ((field == V4L2_FIELD_ANY) ||
+ (field == V4L2_FIELD_INTERLACED))
+ vpifparams->video_params.hpitch =
+ common->fmt.fmt.pix.bytesperline * 2;
+ else
+ vpifparams->video_params.hpitch =
+ common->fmt.fmt.pix.bytesperline;
+ }
+
+ ch->vpifparams.video_params.stdid = ch->vpifparams.std_info.stdid;
+}
+
+static void vpif_config_format(struct channel_obj *ch)
+{
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+
+ common->fmt.fmt.pix.field = V4L2_FIELD_ANY;
+ if (config_params.numbuffers[ch->channel_id] == 0)
+ common->memory = V4L2_MEMORY_USERPTR;
+ else
+ common->memory = V4L2_MEMORY_MMAP;
+
+ common->fmt.fmt.pix.sizeimage =
+ config_params.channel_bufsize[ch->channel_id];
+ common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P;
+ common->fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+}
+
+static int vpif_check_format(struct channel_obj *ch,
+ struct v4l2_pix_format *pixfmt)
+{
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ enum v4l2_field field = pixfmt->field;
+ u32 sizeimage, hpitch, vpitch;
+
+ if (pixfmt->pixelformat != V4L2_PIX_FMT_YUV422P)
+ goto invalid_fmt_exit;
+
+ if (!(VPIF_VALID_FIELD(field)))
+ goto invalid_fmt_exit;
+
+ if (pixfmt->bytesperline <= 0)
+ goto invalid_pitch_exit;
+
+ if (V4L2_MEMORY_USERPTR == common->memory)
+ sizeimage = pixfmt->sizeimage;
+ else
+ sizeimage = config_params.channel_bufsize[ch->channel_id];
+
+ if (vpif_update_resolution(ch))
+ return -EINVAL;
+
+ hpitch = pixfmt->bytesperline;
+ vpitch = sizeimage / (hpitch * 2);
+
+ /* Check for valid value of pitch */
+ if ((hpitch < ch->vpifparams.std_info.width) ||
+ (vpitch < ch->vpifparams.std_info.height))
+ goto invalid_pitch_exit;
+
+ /* Check for 8 byte alignment */
+ if (!ISALIGNED(hpitch)) {
+ vpif_err("invalid pitch alignment\n");
+ return -EINVAL;
+ }
+ pixfmt->width = common->fmt.fmt.pix.width;
+ pixfmt->height = common->fmt.fmt.pix.height;
+
+ return 0;
+
+invalid_fmt_exit:
+ vpif_err("invalid field format\n");
+ return -EINVAL;
+
+invalid_pitch_exit:
+ vpif_err("invalid pitch\n");
+ return -EINVAL;
+}
+
+static void vpif_config_addr(struct channel_obj *ch, int muxmode)
+{
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+
+ if (VPIF_CHANNEL3_VIDEO == ch->channel_id) {
+ common->set_addr = ch3_set_videobuf_addr;
+ } else {
+ if (2 == muxmode)
+ common->set_addr = ch2_set_videobuf_addr_yc_nmux;
+ else
+ common->set_addr = ch2_set_videobuf_addr;
+ }
+}
+
+/*
+ * vpif_mmap: It is used to map kernel space buffers into user spaces
+ */
+static int vpif_mmap(struct file *filep, struct vm_area_struct *vma)
+{
+ struct vpif_fh *fh = filep->private_data;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]);
+
+ vpif_dbg(2, debug, "vpif_mmap\n");
+
+ return videobuf_mmap_mapper(&common->buffer_queue, vma);
+}
+
+/*
+ * vpif_poll: It is used for select/poll system call
+ */
+static unsigned int vpif_poll(struct file *filep, poll_table *wait)
+{
+ struct vpif_fh *fh = filep->private_data;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+
+ if (common->started)
+ return videobuf_poll_stream(filep, &common->buffer_queue, wait);
+
+ return 0;
+}
+
+/*
+ * vpif_open: It creates object of file handle structure and stores it in
+ * private_data member of filepointer
+ */
+static int vpif_open(struct file *filep)
+{
+ struct video_device *vdev = video_devdata(filep);
+ struct channel_obj *ch = NULL;
+ struct vpif_fh *fh = NULL;
+
+ ch = video_get_drvdata(vdev);
+ /* Allocate memory for the file handle object */
+ fh = kzalloc(sizeof(struct vpif_fh), GFP_KERNEL);
+ if (fh == NULL) {
+ vpif_err("unable to allocate memory for file handle object\n");
+ return -ENOMEM;
+ }
+
+ /* store pointer to fh in private_data member of filep */
+ filep->private_data = fh;
+ fh->channel = ch;
+ fh->initialized = 0;
+ if (!ch->initialized) {
+ fh->initialized = 1;
+ ch->initialized = 1;
+ memset(&ch->vpifparams, 0, sizeof(ch->vpifparams));
+ }
+
+ /* Increment channel usrs counter */
+ atomic_inc(&ch->usrs);
+ /* Set io_allowed[VPIF_VIDEO_INDEX] member to false */
+ fh->io_allowed[VPIF_VIDEO_INDEX] = 0;
+ /* Initialize priority of this instance to default priority */
+ fh->prio = V4L2_PRIORITY_UNSET;
+ v4l2_prio_open(&ch->prio, &fh->prio);
+
+ return 0;
+}
+
+/*
+ * vpif_release: This function deletes buffer queue, frees the buffers and
+ * the vpif file handle
+ */
+static int vpif_release(struct file *filep)
+{
+ struct vpif_fh *fh = filep->private_data;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+
+ /* if this instance is doing IO */
+ if (fh->io_allowed[VPIF_VIDEO_INDEX]) {
+ /* Reset io_usrs member of channel object */
+ common->io_usrs = 0;
+ /* Disable channel */
+ if (VPIF_CHANNEL2_VIDEO == ch->channel_id) {
+ enable_channel2(0);
+ channel2_intr_enable(0);
+ }
+ if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) ||
+ (2 == common->started)) {
+ enable_channel3(0);
+ channel3_intr_enable(0);
+ }
+ common->started = 0;
+ /* Free buffers allocated */
+ videobuf_queue_cancel(&common->buffer_queue);
+ videobuf_mmap_free(&common->buffer_queue);
+ common->numbuffers =
+ config_params.numbuffers[ch->channel_id];
+ }
+
+ /* Decrement channel usrs counter */
+ atomic_dec(&ch->usrs);
+ /* If this file handle has initialize encoder device, reset it */
+ if (fh->initialized)
+ ch->initialized = 0;
+
+ /* Close the priority */
+ v4l2_prio_close(&ch->prio, fh->prio);
+ filep->private_data = NULL;
+ fh->initialized = 0;
+ kfree(fh);
+
+ return 0;
+}
+
+/* functions implementing ioctls */
+/**
+ * vpif_querycap() - QUERYCAP handler
+ * @file: file ptr
+ * @priv: file handle
+ * @cap: ptr to v4l2_capability structure
+ */
+static int vpif_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct vpif_display_config *config = vpif_dev->platform_data;
+
+ cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+ strlcpy(cap->driver, "vpif display", sizeof(cap->driver));
+ strlcpy(cap->bus_info, "Platform", sizeof(cap->bus_info));
+ strlcpy(cap->card, config->card_name, sizeof(cap->card));
+
+ return 0;
+}
+
+static int vpif_enum_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_fmtdesc *fmt)
+{
+ if (fmt->index != 0) {
+ vpif_err("Invalid format index\n");
+ return -EINVAL;
+ }
+
+ /* Fill in the information about format */
+ fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ strcpy(fmt->description, "YCbCr4:2:2 YC Planar");
+ fmt->pixelformat = V4L2_PIX_FMT_YUV422P;
+
+ return 0;
+}
+
+static int vpif_g_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+
+ /* Check the validity of the buffer type */
+ if (common->fmt.type != fmt->type)
+ return -EINVAL;
+
+ if (vpif_update_resolution(ch))
+ return -EINVAL;
+ *fmt = common->fmt;
+ return 0;
+}
+
+static int vpif_s_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpif_fh *fh = priv;
+ struct v4l2_pix_format *pixfmt;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ int ret = 0;
+
+ if ((VPIF_CHANNEL2_VIDEO == ch->channel_id)
+ || (VPIF_CHANNEL3_VIDEO == ch->channel_id)) {
+ if (!fh->initialized) {
+ vpif_dbg(1, debug, "Channel Busy\n");
+ return -EBUSY;
+ }
+
+ /* Check for the priority */
+ ret = v4l2_prio_check(&ch->prio, fh->prio);
+ if (0 != ret)
+ return ret;
+ fh->initialized = 1;
+ }
+
+ if (common->started) {
+ vpif_dbg(1, debug, "Streaming in progress\n");
+ return -EBUSY;
+ }
+
+ pixfmt = &fmt->fmt.pix;
+ /* Check for valid field format */
+ ret = vpif_check_format(ch, pixfmt);
+ if (ret)
+ return ret;
+
+ /* store the pix format in the channel object */
+ common->fmt.fmt.pix = *pixfmt;
+ /* store the format in the channel object */
+ common->fmt = *fmt;
+ return 0;
+}
+
+static int vpif_try_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+ int ret = 0;
+
+ ret = vpif_check_format(ch, pixfmt);
+ if (ret) {
+ *pixfmt = common->fmt.fmt.pix;
+ pixfmt->sizeimage = pixfmt->width * pixfmt->height * 2;
+ }
+
+ return ret;
+}
+
+static int vpif_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbuf)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common;
+ enum v4l2_field field;
+ u8 index = 0;
+
+ /* This file handle has not initialized the channel,
+ It is not allowed to do settings */
+ if ((VPIF_CHANNEL2_VIDEO == ch->channel_id)
+ || (VPIF_CHANNEL3_VIDEO == ch->channel_id)) {
+ if (!fh->initialized) {
+ vpif_err("Channel Busy\n");
+ return -EBUSY;
+ }
+ }
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != reqbuf->type)
+ return -EINVAL;
+
+ index = VPIF_VIDEO_INDEX;
+
+ common = &ch->common[index];
+
+ if (common->fmt.type != reqbuf->type)
+ return -EINVAL;
+
+ if (0 != common->io_usrs)
+ return -EBUSY;
+
+ if (reqbuf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ if (common->fmt.fmt.pix.field == V4L2_FIELD_ANY)
+ field = V4L2_FIELD_INTERLACED;
+ else
+ field = common->fmt.fmt.pix.field;
+ } else {
+ field = V4L2_VBI_INTERLACED;
+ }
+
+ /* Initialize videobuf queue as per the buffer type */
+ videobuf_queue_dma_contig_init(&common->buffer_queue,
+ &video_qops, NULL,
+ &common->irqlock,
+ reqbuf->type, field,
+ sizeof(struct videobuf_buffer), fh,
+ &common->lock);
+
+ /* Set io allowed member of file handle to TRUE */
+ fh->io_allowed[index] = 1;
+ /* Increment io usrs member of channel object to 1 */
+ common->io_usrs = 1;
+ /* Store type of memory requested in channel object */
+ common->memory = reqbuf->memory;
+ INIT_LIST_HEAD(&common->dma_queue);
+
+ /* Allocate buffers */
+ return videobuf_reqbufs(&common->buffer_queue, reqbuf);
+}
+
+static int vpif_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *tbuf)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+
+ if (common->fmt.type != tbuf->type)
+ return -EINVAL;
+
+ return videobuf_querybuf(&common->buffer_queue, tbuf);
+}
+
+static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct v4l2_buffer tbuf = *buf;
+ struct videobuf_buffer *buf1;
+ unsigned long addr = 0;
+ unsigned long flags;
+ int ret = 0;
+
+ if (common->fmt.type != tbuf.type)
+ return -EINVAL;
+
+ if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
+ vpif_err("fh->io_allowed\n");
+ return -EACCES;
+ }
+
+ if (!(list_empty(&common->dma_queue)) ||
+ (common->cur_frm != common->next_frm) ||
+ !(common->started) ||
+ (common->started && (0 == ch->field_id)))
+ return videobuf_qbuf(&common->buffer_queue, buf);
+
+ /* bufferqueue is empty store buffer address in VPIF registers */
+ mutex_lock(&common->buffer_queue.vb_lock);
+ buf1 = common->buffer_queue.bufs[tbuf.index];
+ if (buf1->memory != tbuf.memory) {
+ vpif_err("invalid buffer type\n");
+ goto qbuf_exit;
+ }
+
+ if ((buf1->state == VIDEOBUF_QUEUED) ||
+ (buf1->state == VIDEOBUF_ACTIVE)) {
+ vpif_err("invalid state\n");
+ goto qbuf_exit;
+ }
+
+ switch (buf1->memory) {
+ case V4L2_MEMORY_MMAP:
+ if (buf1->baddr == 0)
+ goto qbuf_exit;
+ break;
+
+ case V4L2_MEMORY_USERPTR:
+ if (tbuf.length < buf1->bsize)
+ goto qbuf_exit;
+
+ if ((VIDEOBUF_NEEDS_INIT != buf1->state)
+ && (buf1->baddr != tbuf.m.userptr)) {
+ vpif_buffer_release(&common->buffer_queue, buf1);
+ buf1->baddr = tbuf.m.userptr;
+ }
+ break;
+
+ default:
+ goto qbuf_exit;
+ }
+
+ local_irq_save(flags);
+ ret = vpif_buffer_prepare(&common->buffer_queue, buf1,
+ common->buffer_queue.field);
+ if (ret < 0) {
+ local_irq_restore(flags);
+ goto qbuf_exit;
+ }
+
+ buf1->state = VIDEOBUF_ACTIVE;
+ addr = buf1->boff;
+ common->next_frm = buf1;
+ if (tbuf.type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) {
+ common->set_addr((addr + common->ytop_off),
+ (addr + common->ybtm_off),
+ (addr + common->ctop_off),
+ (addr + common->cbtm_off));
+ }
+
+ local_irq_restore(flags);
+ list_add_tail(&buf1->stream, &common->buffer_queue.stream);
+ mutex_unlock(&common->buffer_queue.vb_lock);
+ return 0;
+
+qbuf_exit:
+ mutex_unlock(&common->buffer_queue.vb_lock);
+ return -EINVAL;
+}
+
+static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ int ret = 0;
+
+ if (!(*std_id & DM646X_V4L2_STD))
+ return -EINVAL;
+
+ if (common->started) {
+ vpif_err("streaming in progress\n");
+ return -EBUSY;
+ }
+
+ /* Call encoder subdevice function to set the standard */
+ ch->video.stdid = *std_id;
+ ch->video.dv_preset = V4L2_DV_INVALID;
+ memset(&ch->video.bt_timings, 0, sizeof(ch->video.bt_timings));
+
+ /* Get the information about the standard */
+ if (vpif_update_resolution(ch))
+ return -EINVAL;
+
+ if ((ch->vpifparams.std_info.width *
+ ch->vpifparams.std_info.height * 2) >
+ config_params.channel_bufsize[ch->channel_id]) {
+ vpif_err("invalid std for this size\n");
+ return -EINVAL;
+ }
+
+ common->fmt.fmt.pix.bytesperline = common->fmt.fmt.pix.width;
+ /* Configure the default format information */
+ vpif_config_format(ch);
+
+ ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, video,
+ s_std_output, *std_id);
+ if (ret < 0) {
+ vpif_err("Failed to set output standard\n");
+ return ret;
+ }
+
+ ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, core,
+ s_std, *std_id);
+ if (ret < 0)
+ vpif_err("Failed to set standard for sub devices\n");
+ return ret;
+}
+
+static int vpif_g_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+
+ *std = ch->video.stdid;
+ return 0;
+}
+
+static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+
+ return videobuf_dqbuf(&common->buffer_queue, p,
+ (file->f_flags & O_NONBLOCK));
+}
+
+static int vpif_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type buftype)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id];
+ struct vpif_params *vpif = &ch->vpifparams;
+ struct vpif_display_config *vpif_config_data =
+ vpif_dev->platform_data;
+ unsigned long addr = 0;
+ int ret = 0;
+
+ if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ vpif_err("buffer type not supported\n");
+ return -EINVAL;
+ }
+
+ if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
+ vpif_err("fh->io_allowed\n");
+ return -EACCES;
+ }
+
+ /* If Streaming is already started, return error */
+ if (common->started) {
+ vpif_err("channel->started\n");
+ return -EBUSY;
+ }
+
+ if ((ch->channel_id == VPIF_CHANNEL2_VIDEO
+ && oth_ch->common[VPIF_VIDEO_INDEX].started &&
+ ch->vpifparams.std_info.ycmux_mode == 0)
+ || ((ch->channel_id == VPIF_CHANNEL3_VIDEO)
+ && (2 == oth_ch->common[VPIF_VIDEO_INDEX].started))) {
+ vpif_err("other channel is using\n");
+ return -EBUSY;
+ }
+
+ ret = vpif_check_format(ch, &common->fmt.fmt.pix);
+ if (ret < 0)
+ return ret;
+
+ /* Call videobuf_streamon to start streaming in videobuf */
+ ret = videobuf_streamon(&common->buffer_queue);
+ if (ret < 0) {
+ vpif_err("videobuf_streamon\n");
+ return ret;
+ }
+
+ /* If buffer queue is empty, return error */
+ if (list_empty(&common->dma_queue)) {
+ vpif_err("buffer queue is empty\n");
+ return -EIO;
+ }
+
+ /* Get the next frame from the buffer queue */
+ common->next_frm = common->cur_frm =
+ list_entry(common->dma_queue.next,
+ struct videobuf_buffer, queue);
+
+ list_del(&common->cur_frm->queue);
+ /* Mark state of the current frame to active */
+ common->cur_frm->state = VIDEOBUF_ACTIVE;
+
+ /* Initialize field_id and started member */
+ ch->field_id = 0;
+ common->started = 1;
+ if (buftype == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ addr = common->cur_frm->boff;
+ /* Calculate the offset for Y and C data in the buffer */
+ vpif_calculate_offsets(ch);
+
+ if ((ch->vpifparams.std_info.frm_fmt &&
+ ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE)
+ && (common->fmt.fmt.pix.field != V4L2_FIELD_ANY)))
+ || (!ch->vpifparams.std_info.frm_fmt
+ && (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) {
+ vpif_err("conflict in field format and std format\n");
+ return -EINVAL;
+ }
+
+ /* clock settings */
+ ret =
+ vpif_config_data->set_clock(ch->vpifparams.std_info.ycmux_mode,
+ ch->vpifparams.std_info.hd_sd);
+ if (ret < 0) {
+ vpif_err("can't set clock\n");
+ return ret;
+ }
+
+ /* set the parameters and addresses */
+ ret = vpif_set_video_params(vpif, ch->channel_id + 2);
+ if (ret < 0)
+ return ret;
+
+ common->started = ret;
+ vpif_config_addr(ch, ret);
+ common->set_addr((addr + common->ytop_off),
+ (addr + common->ybtm_off),
+ (addr + common->ctop_off),
+ (addr + common->cbtm_off));
+
+ /* Set interrupt for both the fields in VPIF
+ Register enable channel in VPIF register */
+ if (VPIF_CHANNEL2_VIDEO == ch->channel_id) {
+ channel2_intr_assert();
+ channel2_intr_enable(1);
+ enable_channel2(1);
+ }
+
+ if ((VPIF_CHANNEL3_VIDEO == ch->channel_id)
+ || (common->started == 2)) {
+ channel3_intr_assert();
+ channel3_intr_enable(1);
+ enable_channel3(1);
+ }
+ channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1;
+ }
+ return ret;
+}
+
+static int vpif_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type buftype)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+
+ if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ vpif_err("buffer type not supported\n");
+ return -EINVAL;
+ }
+
+ if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
+ vpif_err("fh->io_allowed\n");
+ return -EACCES;
+ }
+
+ if (!common->started) {
+ vpif_err("channel->started\n");
+ return -EINVAL;
+ }
+
+ if (buftype == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ /* disable channel */
+ if (VPIF_CHANNEL2_VIDEO == ch->channel_id) {
+ enable_channel2(0);
+ channel2_intr_enable(0);
+ }
+ if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) ||
+ (2 == common->started)) {
+ enable_channel3(0);
+ channel3_intr_enable(0);
+ }
+ }
+
+ common->started = 0;
+ return videobuf_streamoff(&common->buffer_queue);
+}
+
+static int vpif_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *crop)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != crop->type)
+ return -EINVAL;
+
+ crop->bounds.left = crop->bounds.top = 0;
+ crop->defrect.left = crop->defrect.top = 0;
+ crop->defrect.height = crop->bounds.height = common->height;
+ crop->defrect.width = crop->bounds.width = common->width;
+
+ return 0;
+}
+
+static int vpif_enum_output(struct file *file, void *fh,
+ struct v4l2_output *output)
+{
+
+ struct vpif_display_config *config = vpif_dev->platform_data;
+
+ if (output->index >= config->output_count) {
+ vpif_dbg(1, debug, "Invalid output index\n");
+ return -EINVAL;
+ }
+
+ strcpy(output->name, config->output[output->index]);
+ output->type = V4L2_OUTPUT_TYPE_ANALOG;
+ output->std = DM646X_V4L2_STD;
+
+ return 0;
+}
+
+static int vpif_s_output(struct file *file, void *priv, unsigned int i)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct video_obj *vid_ch = &ch->video;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ int ret = 0;
+
+ if (common->started) {
+ vpif_err("Streaming in progress\n");
+ return -EBUSY;
+ }
+
+ ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, video,
+ s_routing, 0, i, 0);
+
+ if (ret < 0)
+ vpif_err("Failed to set output standard\n");
+
+ vid_ch->output_id = i;
+ return ret;
+}
+
+static int vpif_g_output(struct file *file, void *priv, unsigned int *i)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct video_obj *vid_ch = &ch->video;
+
+ *i = vid_ch->output_id;
+
+ return 0;
+}
+
+static int vpif_g_priority(struct file *file, void *priv, enum v4l2_priority *p)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+
+ *p = v4l2_prio_max(&ch->prio);
+
+ return 0;
+}
+
+static int vpif_s_priority(struct file *file, void *priv, enum v4l2_priority p)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+
+ return v4l2_prio_change(&ch->prio, &fh->prio, p);
+}
+
+/**
+ * vpif_enum_dv_presets() - ENUM_DV_PRESETS handler
+ * @file: file ptr
+ * @priv: file handle
+ * @preset: input preset
+ */
+static int vpif_enum_dv_presets(struct file *file, void *priv,
+ struct v4l2_dv_enum_preset *preset)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct video_obj *vid_ch = &ch->video;
+
+ return v4l2_subdev_call(vpif_obj.sd[vid_ch->output_id],
+ video, enum_dv_presets, preset);
+}
+
+/**
+ * vpif_s_dv_presets() - S_DV_PRESETS handler
+ * @file: file ptr
+ * @priv: file handle
+ * @preset: input preset
+ */
+static int vpif_s_dv_preset(struct file *file, void *priv,
+ struct v4l2_dv_preset *preset)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct video_obj *vid_ch = &ch->video;
+ int ret = 0;
+
+ if (common->started) {
+ vpif_dbg(1, debug, "streaming in progress\n");
+ return -EBUSY;
+ }
+
+ ret = v4l2_prio_check(&ch->prio, fh->prio);
+ if (ret != 0)
+ return ret;
+
+ fh->initialized = 1;
+
+ /* Call encoder subdevice function to set the standard */
+ if (mutex_lock_interruptible(&common->lock))
+ return -ERESTARTSYS;
+
+ ch->video.dv_preset = preset->preset;
+ ch->video.stdid = V4L2_STD_UNKNOWN;
+ memset(&ch->video.bt_timings, 0, sizeof(ch->video.bt_timings));
+
+ /* Get the information about the standard */
+ if (vpif_update_resolution(ch)) {
+ ret = -EINVAL;
+ } else {
+ /* Configure the default format information */
+ vpif_config_format(ch);
+
+ ret = v4l2_subdev_call(vpif_obj.sd[vid_ch->output_id],
+ video, s_dv_preset, preset);
+ }
+
+ mutex_unlock(&common->lock);
+
+ return ret;
+}
+/**
+ * vpif_g_dv_presets() - G_DV_PRESETS handler
+ * @file: file ptr
+ * @priv: file handle
+ * @preset: input preset
+ */
+static int vpif_g_dv_preset(struct file *file, void *priv,
+ struct v4l2_dv_preset *preset)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+
+ preset->preset = ch->video.dv_preset;
+
+ return 0;
+}
+/**
+ * vpif_s_dv_timings() - S_DV_TIMINGS handler
+ * @file: file ptr
+ * @priv: file handle
+ * @timings: digital video timings
+ */
+static int vpif_s_dv_timings(struct file *file, void *priv,
+ struct v4l2_dv_timings *timings)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct vpif_params *vpifparams = &ch->vpifparams;
+ struct vpif_channel_config_params *std_info = &vpifparams->std_info;
+ struct video_obj *vid_ch = &ch->video;
+ struct v4l2_bt_timings *bt = &vid_ch->bt_timings;
+ int ret;
+
+ if (timings->type != V4L2_DV_BT_656_1120) {
+ vpif_dbg(2, debug, "Timing type not defined\n");
+ return -EINVAL;
+ }
+
+ /* Configure subdevice timings, if any */
+ ret = v4l2_subdev_call(vpif_obj.sd[vid_ch->output_id],
+ video, s_dv_timings, timings);
+ if (ret == -ENOIOCTLCMD) {
+ vpif_dbg(2, debug, "Custom DV timings not supported by "
+ "subdevice\n");
+ return -EINVAL;
+ }
+ if (ret < 0) {
+ vpif_dbg(2, debug, "Error setting custom DV timings\n");
+ return ret;
+ }
+
+ if (!(timings->bt.width && timings->bt.height &&
+ (timings->bt.hbackporch ||
+ timings->bt.hfrontporch ||
+ timings->bt.hsync) &&
+ timings->bt.vfrontporch &&
+ (timings->bt.vbackporch ||
+ timings->bt.vsync))) {
+ vpif_dbg(2, debug, "Timings for width, height, "
+ "horizontal back porch, horizontal sync, "
+ "horizontal front porch, vertical back porch, "
+ "vertical sync and vertical back porch "
+ "must be defined\n");
+ return -EINVAL;
+ }
+
+ *bt = timings->bt;
+
+ /* Configure video port timings */
+
+ std_info->eav2sav = bt->hbackporch + bt->hfrontporch +
+ bt->hsync - 8;
+ std_info->sav2eav = bt->width;
+
+ std_info->l1 = 1;
+ std_info->l3 = bt->vsync + bt->vbackporch + 1;
+
+ if (bt->interlaced) {
+ if (bt->il_vbackporch || bt->il_vfrontporch || bt->il_vsync) {
+ std_info->vsize = bt->height * 2 +
+ bt->vfrontporch + bt->vsync + bt->vbackporch +
+ bt->il_vfrontporch + bt->il_vsync +
+ bt->il_vbackporch;
+ std_info->l5 = std_info->vsize/2 -
+ (bt->vfrontporch - 1);
+ std_info->l7 = std_info->vsize/2 + 1;
+ std_info->l9 = std_info->l7 + bt->il_vsync +
+ bt->il_vbackporch + 1;
+ std_info->l11 = std_info->vsize -
+ (bt->il_vfrontporch - 1);
+ } else {
+ vpif_dbg(2, debug, "Required timing values for "
+ "interlaced BT format missing\n");
+ return -EINVAL;
+ }
+ } else {
+ std_info->vsize = bt->height + bt->vfrontporch +
+ bt->vsync + bt->vbackporch;
+ std_info->l5 = std_info->vsize - (bt->vfrontporch - 1);
+ }
+ strncpy(std_info->name, "Custom timings BT656/1120",
+ VPIF_MAX_NAME);
+ std_info->width = bt->width;
+ std_info->height = bt->height;
+ std_info->frm_fmt = bt->interlaced ? 0 : 1;
+ std_info->ycmux_mode = 0;
+ std_info->capture_format = 0;
+ std_info->vbi_supported = 0;
+ std_info->hd_sd = 1;
+ std_info->stdid = 0;
+ std_info->dv_preset = V4L2_DV_INVALID;
+
+ vid_ch->stdid = 0;
+ vid_ch->dv_preset = V4L2_DV_INVALID;
+
+ return 0;
+}
+
+/**
+ * vpif_g_dv_timings() - G_DV_TIMINGS handler
+ * @file: file ptr
+ * @priv: file handle
+ * @timings: digital video timings
+ */
+static int vpif_g_dv_timings(struct file *file, void *priv,
+ struct v4l2_dv_timings *timings)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct video_obj *vid_ch = &ch->video;
+ struct v4l2_bt_timings *bt = &vid_ch->bt_timings;
+
+ timings->bt = *bt;
+
+ return 0;
+}
+
+/*
+ * vpif_g_chip_ident() - Identify the chip
+ * @file: file ptr
+ * @priv: file handle
+ * @chip: chip identity
+ *
+ * Returns zero or -EINVAL if read operations fails.
+ */
+static int vpif_g_chip_ident(struct file *file, void *priv,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+ if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
+ chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) {
+ vpif_dbg(2, debug, "match_type is invalid.\n");
+ return -EINVAL;
+ }
+
+ return v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 0, core,
+ g_chip_ident, chip);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+/*
+ * vpif_dbg_g_register() - Read register
+ * @file: file ptr
+ * @priv: file handle
+ * @reg: register to be read
+ *
+ * Debugging only
+ * Returns zero or -EINVAL if read operations fails.
+ */
+static int vpif_dbg_g_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg){
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct video_obj *vid_ch = &ch->video;
+
+ return v4l2_subdev_call(vpif_obj.sd[vid_ch->output_id], core,
+ g_register, reg);
+}
+
+/*
+ * vpif_dbg_s_register() - Write to register
+ * @file: file ptr
+ * @priv: file handle
+ * @reg: register to be modified
+ *
+ * Debugging only
+ * Returns zero or -EINVAL if write operations fails.
+ */
+static int vpif_dbg_s_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg){
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct video_obj *vid_ch = &ch->video;
+
+ return v4l2_subdev_call(vpif_obj.sd[vid_ch->output_id], core,
+ s_register, reg);
+}
+#endif
+
+/*
+ * vpif_log_status() - Status information
+ * @file: file ptr
+ * @priv: file handle
+ *
+ * Returns zero.
+ */
+static int vpif_log_status(struct file *filep, void *priv)
+{
+ /* status for sub devices */
+ v4l2_device_call_all(&vpif_obj.v4l2_dev, 0, core, log_status);
+
+ return 0;
+}
+
+/* vpif display ioctl operations */
+static const struct v4l2_ioctl_ops vpif_ioctl_ops = {
+ .vidioc_querycap = vpif_querycap,
+ .vidioc_g_priority = vpif_g_priority,
+ .vidioc_s_priority = vpif_s_priority,
+ .vidioc_enum_fmt_vid_out = vpif_enum_fmt_vid_out,
+ .vidioc_g_fmt_vid_out = vpif_g_fmt_vid_out,
+ .vidioc_s_fmt_vid_out = vpif_s_fmt_vid_out,
+ .vidioc_try_fmt_vid_out = vpif_try_fmt_vid_out,
+ .vidioc_reqbufs = vpif_reqbufs,
+ .vidioc_querybuf = vpif_querybuf,
+ .vidioc_qbuf = vpif_qbuf,
+ .vidioc_dqbuf = vpif_dqbuf,
+ .vidioc_streamon = vpif_streamon,
+ .vidioc_streamoff = vpif_streamoff,
+ .vidioc_s_std = vpif_s_std,
+ .vidioc_g_std = vpif_g_std,
+ .vidioc_enum_output = vpif_enum_output,
+ .vidioc_s_output = vpif_s_output,
+ .vidioc_g_output = vpif_g_output,
+ .vidioc_cropcap = vpif_cropcap,
+ .vidioc_enum_dv_presets = vpif_enum_dv_presets,
+ .vidioc_s_dv_preset = vpif_s_dv_preset,
+ .vidioc_g_dv_preset = vpif_g_dv_preset,
+ .vidioc_s_dv_timings = vpif_s_dv_timings,
+ .vidioc_g_dv_timings = vpif_g_dv_timings,
+ .vidioc_g_chip_ident = vpif_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vpif_dbg_g_register,
+ .vidioc_s_register = vpif_dbg_s_register,
+#endif
+ .vidioc_log_status = vpif_log_status,
+};
+
+static const struct v4l2_file_operations vpif_fops = {
+ .owner = THIS_MODULE,
+ .open = vpif_open,
+ .release = vpif_release,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vpif_mmap,
+ .poll = vpif_poll
+};
+
+static struct video_device vpif_video_template = {
+ .name = "vpif",
+ .fops = &vpif_fops,
+ .ioctl_ops = &vpif_ioctl_ops,
+ .tvnorms = DM646X_V4L2_STD,
+ .current_norm = V4L2_STD_625_50,
+
+};
+
+/*Configure the channels, buffer sizei, request irq */
+static int initialize_vpif(void)
+{
+ int free_channel_objects_index;
+ int free_buffer_channel_index;
+ int free_buffer_index;
+ int err = 0, i, j;
+
+ /* Default number of buffers should be 3 */
+ if ((ch2_numbuffers > 0) &&
+ (ch2_numbuffers < config_params.min_numbuffers))
+ ch2_numbuffers = config_params.min_numbuffers;
+ if ((ch3_numbuffers > 0) &&
+ (ch3_numbuffers < config_params.min_numbuffers))
+ ch3_numbuffers = config_params.min_numbuffers;
+
+ /* Set buffer size to min buffers size if invalid buffer size is
+ * given */
+ if (ch2_bufsize < config_params.min_bufsize[VPIF_CHANNEL2_VIDEO])
+ ch2_bufsize =
+ config_params.min_bufsize[VPIF_CHANNEL2_VIDEO];
+ if (ch3_bufsize < config_params.min_bufsize[VPIF_CHANNEL3_VIDEO])
+ ch3_bufsize =
+ config_params.min_bufsize[VPIF_CHANNEL3_VIDEO];
+
+ config_params.numbuffers[VPIF_CHANNEL2_VIDEO] = ch2_numbuffers;
+
+ if (ch2_numbuffers) {
+ config_params.channel_bufsize[VPIF_CHANNEL2_VIDEO] =
+ ch2_bufsize;
+ }
+ config_params.numbuffers[VPIF_CHANNEL3_VIDEO] = ch3_numbuffers;
+
+ if (ch3_numbuffers) {
+ config_params.channel_bufsize[VPIF_CHANNEL3_VIDEO] =
+ ch3_bufsize;
+ }
+
+ /* Allocate memory for six channel objects */
+ for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {
+ vpif_obj.dev[i] =
+ kzalloc(sizeof(struct channel_obj), GFP_KERNEL);
+ /* If memory allocation fails, return error */
+ if (!vpif_obj.dev[i]) {
+ free_channel_objects_index = i;
+ err = -ENOMEM;
+ goto vpif_init_free_channel_objects;
+ }
+ }
+
+ free_channel_objects_index = VPIF_DISPLAY_MAX_DEVICES;
+ free_buffer_channel_index = VPIF_DISPLAY_NUM_CHANNELS;
+ free_buffer_index = config_params.numbuffers[i - 1];
+
+ return 0;
+
+vpif_init_free_channel_objects:
+ for (j = 0; j < free_channel_objects_index; j++)
+ kfree(vpif_obj.dev[j]);
+ return err;
+}
+
+/*
+ * vpif_probe: This function creates device entries by register itself to the
+ * V4L2 driver and initializes fields of each channel objects
+ */
+static __init int vpif_probe(struct platform_device *pdev)
+{
+ struct vpif_subdev_info *subdevdata;
+ struct vpif_display_config *config;
+ int i, j = 0, k, q, m, err = 0;
+ struct i2c_adapter *i2c_adap;
+ struct common_obj *common;
+ struct channel_obj *ch;
+ struct video_device *vfd;
+ struct resource *res;
+ int subdev_count;
+
+ vpif_dev = &pdev->dev;
+
+ err = initialize_vpif();
+
+ if (err) {
+ v4l2_err(vpif_dev->driver, "Error initializing vpif\n");
+ return err;
+ }
+
+ err = v4l2_device_register(vpif_dev, &vpif_obj.v4l2_dev);
+ if (err) {
+ v4l2_err(vpif_dev->driver, "Error registering v4l2 device\n");
+ return err;
+ }
+
+ k = 0;
+ while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, k))) {
+ for (i = res->start; i <= res->end; i++) {
+ if (request_irq(i, vpif_channel_isr, IRQF_DISABLED,
+ "DM646x_Display",
+ (void *)(&vpif_obj.dev[k]->channel_id))) {
+ err = -EBUSY;
+ goto vpif_int_err;
+ }
+ }
+ k++;
+ }
+
+ for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {
+
+ /* Get the pointer to the channel object */
+ ch = vpif_obj.dev[i];
+
+ /* Allocate memory for video device */
+ vfd = video_device_alloc();
+ if (vfd == NULL) {
+ for (j = 0; j < i; j++) {
+ ch = vpif_obj.dev[j];
+ video_device_release(ch->video_dev);
+ }
+ err = -ENOMEM;
+ goto vpif_int_err;
+ }
+
+ /* Initialize field of video device */
+ *vfd = vpif_video_template;
+ vfd->v4l2_dev = &vpif_obj.v4l2_dev;
+ vfd->release = video_device_release;
+ snprintf(vfd->name, sizeof(vfd->name),
+ "DM646x_VPIFDisplay_DRIVER_V%s",
+ VPIF_DISPLAY_VERSION);
+
+ /* Set video_dev to the video device */
+ ch->video_dev = vfd;
+ }
+
+ for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) {
+ ch = vpif_obj.dev[j];
+ /* Initialize field of the channel objects */
+ atomic_set(&ch->usrs, 0);
+ for (k = 0; k < VPIF_NUMOBJECTS; k++) {
+ ch->common[k].numbuffers = 0;
+ common = &ch->common[k];
+ common->io_usrs = 0;
+ common->started = 0;
+ spin_lock_init(&common->irqlock);
+ mutex_init(&common->lock);
+ common->numbuffers = 0;
+ common->set_addr = NULL;
+ common->ytop_off = common->ybtm_off = 0;
+ common->ctop_off = common->cbtm_off = 0;
+ common->cur_frm = common->next_frm = NULL;
+ memset(&common->fmt, 0, sizeof(common->fmt));
+ common->numbuffers = config_params.numbuffers[k];
+
+ }
+ ch->initialized = 0;
+ ch->channel_id = j;
+ if (j < 2)
+ ch->common[VPIF_VIDEO_INDEX].numbuffers =
+ config_params.numbuffers[ch->channel_id];
+ else
+ ch->common[VPIF_VIDEO_INDEX].numbuffers = 0;
+
+ memset(&ch->vpifparams, 0, sizeof(ch->vpifparams));
+
+ /* Initialize prio member of channel object */
+ v4l2_prio_init(&ch->prio);
+ ch->common[VPIF_VIDEO_INDEX].fmt.type =
+ V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ ch->video_dev->lock = &common->lock;
+
+ /* register video device */
+ vpif_dbg(1, debug, "channel=%x,channel->video_dev=%x\n",
+ (int)ch, (int)&ch->video_dev);
+
+ err = video_register_device(ch->video_dev,
+ VFL_TYPE_GRABBER, (j ? 3 : 2));
+ if (err < 0)
+ goto probe_out;
+
+ video_set_drvdata(ch->video_dev, ch);
+ }
+
+ i2c_adap = i2c_get_adapter(1);
+ config = pdev->dev.platform_data;
+ subdev_count = config->subdev_count;
+ subdevdata = config->subdevinfo;
+ vpif_obj.sd = kzalloc(sizeof(struct v4l2_subdev *) * subdev_count,
+ GFP_KERNEL);
+ if (vpif_obj.sd == NULL) {
+ vpif_err("unable to allocate memory for subdevice pointers\n");
+ err = -ENOMEM;
+ goto probe_out;
+ }
+
+ for (i = 0; i < subdev_count; i++) {
+ vpif_obj.sd[i] = v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev,
+ i2c_adap,
+ &subdevdata[i].board_info,
+ NULL);
+ if (!vpif_obj.sd[i]) {
+ vpif_err("Error registering v4l2 subdevice\n");
+ goto probe_subdev_out;
+ }
+
+ if (vpif_obj.sd[i])
+ vpif_obj.sd[i]->grp_id = 1 << i;
+ }
+
+ v4l2_info(&vpif_obj.v4l2_dev,
+ "DM646x VPIF display driver initialized\n");
+ return 0;
+
+probe_subdev_out:
+ kfree(vpif_obj.sd);
+probe_out:
+ for (k = 0; k < j; k++) {
+ ch = vpif_obj.dev[k];
+ video_unregister_device(ch->video_dev);
+ video_device_release(ch->video_dev);
+ ch->video_dev = NULL;
+ }
+vpif_int_err:
+ v4l2_device_unregister(&vpif_obj.v4l2_dev);
+ vpif_err("VPIF IRQ request failed\n");
+ for (q = k; k >= 0; k--) {
+ for (m = i; m >= res->start; m--)
+ free_irq(m, (void *)(&vpif_obj.dev[k]->channel_id));
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, k-1);
+ m = res->end;
+ }
+
+ return err;
+}
+
+/*
+ * vpif_remove: It un-register channels from V4L2 driver
+ */
+static int vpif_remove(struct platform_device *device)
+{
+ struct channel_obj *ch;
+ int i;
+
+ v4l2_device_unregister(&vpif_obj.v4l2_dev);
+
+ /* un-register device */
+ for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {
+ /* Get the pointer to the channel object */
+ ch = vpif_obj.dev[i];
+ /* Unregister video device */
+ video_unregister_device(ch->video_dev);
+
+ ch->video_dev = NULL;
+ }
+
+ return 0;
+}
+
+static __refdata struct platform_driver vpif_driver = {
+ .driver = {
+ .name = "vpif_display",
+ .owner = THIS_MODULE,
+ },
+ .probe = vpif_probe,
+ .remove = vpif_remove,
+};
+
+static __init int vpif_init(void)
+{
+ return platform_driver_register(&vpif_driver);
+}
+
+/*
+ * vpif_cleanup: This function un-registers device and driver to the kernel,
+ * frees requested irq handler and de-allocates memory allocated for channel
+ * objects.
+ */
+static void vpif_cleanup(void)
+{
+ struct platform_device *pdev;
+ struct resource *res;
+ int irq_num;
+ int i = 0;
+
+ pdev = container_of(vpif_dev, struct platform_device, dev);
+
+ while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, i))) {
+ for (irq_num = res->start; irq_num <= res->end; irq_num++)
+ free_irq(irq_num,
+ (void *)(&vpif_obj.dev[i]->channel_id));
+ i++;
+ }
+
+ platform_driver_unregister(&vpif_driver);
+ kfree(vpif_obj.sd);
+ for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++)
+ kfree(vpif_obj.dev[i]);
+}
+
+module_init(vpif_init);
+module_exit(vpif_cleanup);
diff --git a/drivers/media/video/davinci/vpif_display.h b/drivers/media/video/davinci/vpif_display.h
new file mode 100644
index 00000000..56879d1a
--- /dev/null
+++ b/drivers/media/video/davinci/vpif_display.h
@@ -0,0 +1,172 @@
+/*
+ * DM646x display header file
+ *
+ * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed .as is. WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef DAVINCIHD_DISPLAY_H
+#define DAVINCIHD_DISPLAY_H
+
+/* Header files */
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf-core.h>
+#include <media/videobuf-dma-contig.h>
+#include <media/davinci/vpif_types.h>
+
+#include "vpif.h"
+
+/* Macros */
+#define VPIF_DISPLAY_VERSION "0.0.2"
+
+#define VPIF_VALID_FIELD(field) \
+ (((V4L2_FIELD_ANY == field) || (V4L2_FIELD_NONE == field)) || \
+ (((V4L2_FIELD_INTERLACED == field) || (V4L2_FIELD_SEQ_TB == field)) || \
+ (V4L2_FIELD_SEQ_BT == field)))
+
+#define VPIF_DISPLAY_MAX_DEVICES (2)
+#define VPIF_SLICED_BUF_SIZE (256)
+#define VPIF_SLICED_MAX_SERVICES (3)
+#define VPIF_VIDEO_INDEX (0)
+#define VPIF_VBI_INDEX (1)
+#define VPIF_HBI_INDEX (2)
+
+/* Setting it to 1 as HBI/VBI support yet to be added , else 3*/
+#define VPIF_NUMOBJECTS (1)
+
+/* Macros */
+#define ISALIGNED(a) (0 == ((a) & 7))
+
+/* enumerated data types */
+/* Enumerated data type to give id to each device per channel */
+enum vpif_channel_id {
+ VPIF_CHANNEL2_VIDEO = 0, /* Channel2 Video */
+ VPIF_CHANNEL3_VIDEO, /* Channel3 Video */
+};
+
+/* structures */
+
+struct video_obj {
+ enum v4l2_field buf_field;
+ u32 latest_only; /* indicate whether to return
+ * most recent displayed frame only */
+ v4l2_std_id stdid; /* Currently selected or default
+ * standard */
+ u32 dv_preset;
+ struct v4l2_bt_timings bt_timings;
+ u32 output_id; /* Current output id */
+};
+
+struct vbi_obj {
+ int num_services;
+ struct vpif_vbi_params vbiparams; /* vpif parameters for the raw
+ * vbi data */
+};
+
+struct common_obj {
+ /* Buffer specific parameters */
+ u8 *fbuffers[VIDEO_MAX_FRAME]; /* List of buffer pointers for
+ * storing frames */
+ u32 numbuffers; /* number of buffers */
+ struct videobuf_buffer *cur_frm; /* Pointer pointing to current
+ * videobuf_buffer */
+ struct videobuf_buffer *next_frm; /* Pointer pointing to next
+ * videobuf_buffer */
+ enum v4l2_memory memory; /* This field keeps track of
+ * type of buffer exchange
+ * method user has selected */
+ struct v4l2_format fmt; /* Used to store the format */
+ struct videobuf_queue buffer_queue; /* Buffer queue used in
+ * video-buf */
+ struct list_head dma_queue; /* Queue of filled frames */
+ spinlock_t irqlock; /* Used in video-buf */
+
+ /* channel specific parameters */
+ struct mutex lock; /* lock used to access this
+ * structure */
+ u32 io_usrs; /* number of users performing
+ * IO */
+ u8 started; /* Indicates whether streaming
+ * started */
+ u32 ytop_off; /* offset of Y top from the
+ * starting of the buffer */
+ u32 ybtm_off; /* offset of Y bottom from the
+ * starting of the buffer */
+ u32 ctop_off; /* offset of C top from the
+ * starting of the buffer */
+ u32 cbtm_off; /* offset of C bottom from the
+ * starting of the buffer */
+ /* Function pointer to set the addresses */
+ void (*set_addr) (unsigned long, unsigned long,
+ unsigned long, unsigned long);
+ u32 height;
+ u32 width;
+};
+
+struct channel_obj {
+ /* V4l2 specific parameters */
+ struct video_device *video_dev; /* Identifies video device for
+ * this channel */
+ struct v4l2_prio_state prio; /* Used to keep track of state of
+ * the priority */
+ atomic_t usrs; /* number of open instances of
+ * the channel */
+ u32 field_id; /* Indicates id of the field
+ * which is being displayed */
+ u8 initialized; /* flag to indicate whether
+ * encoder is initialized */
+
+ enum vpif_channel_id channel_id;/* Identifies channel */
+ struct vpif_params vpifparams;
+ struct common_obj common[VPIF_NUMOBJECTS];
+ struct video_obj video;
+ struct vbi_obj vbi;
+};
+
+/* File handle structure */
+struct vpif_fh {
+ struct channel_obj *channel; /* pointer to channel object for
+ * opened device */
+ u8 io_allowed[VPIF_NUMOBJECTS]; /* Indicates whether this file handle
+ * is doing IO */
+ enum v4l2_priority prio; /* Used to keep track priority of
+ * this instance */
+ u8 initialized; /* Used to keep track of whether this
+ * file handle has initialized
+ * channel or not */
+};
+
+/* vpif device structure */
+struct vpif_device {
+ struct v4l2_device v4l2_dev;
+ struct channel_obj *dev[VPIF_DISPLAY_NUM_CHANNELS];
+ struct v4l2_subdev **sd;
+
+};
+
+struct vpif_config_params {
+ u32 min_bufsize[VPIF_DISPLAY_NUM_CHANNELS];
+ u32 channel_bufsize[VPIF_DISPLAY_NUM_CHANNELS];
+ u8 numbuffers[VPIF_DISPLAY_NUM_CHANNELS];
+ u8 min_numbuffers;
+};
+
+/* Struct which keeps track of the line numbers for the sliced vbi service */
+struct vpif_service_line {
+ u16 service_id;
+ u16 service_line[2];
+ u16 enc_service_id;
+ u8 bytestowrite;
+};
+
+#endif /* DAVINCIHD_DISPLAY_H */
diff --git a/drivers/media/video/davinci/vpss.c b/drivers/media/video/davinci/vpss.c
new file mode 100644
index 00000000..3e5cf27e
--- /dev/null
+++ b/drivers/media/video/davinci/vpss.c
@@ -0,0 +1,482 @@
+/*
+ * Copyright (C) 2009 Texas Instruments.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * common vpss system module platform driver for all video drivers.
+ */
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/compiler.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
+#include <media/davinci/vpss.h>
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("VPSS Driver");
+MODULE_AUTHOR("Texas Instruments");
+
+/* DM644x defines */
+#define DM644X_SBL_PCR_VPSS (4)
+
+#define DM355_VPSSBL_INTSEL 0x10
+#define DM355_VPSSBL_EVTSEL 0x14
+/* vpss BL register offsets */
+#define DM355_VPSSBL_CCDCMUX 0x1c
+/* vpss CLK register offsets */
+#define DM355_VPSSCLK_CLKCTRL 0x04
+/* masks and shifts */
+#define VPSS_HSSISEL_SHIFT 4
+/*
+ * VDINT0 - vpss_int0, VDINT1 - vpss_int1, H3A - vpss_int4,
+ * IPIPE_INT1_SDR - vpss_int5
+ */
+#define DM355_VPSSBL_INTSEL_DEFAULT 0xff83ff10
+/* VENCINT - vpss_int8 */
+#define DM355_VPSSBL_EVTSEL_DEFAULT 0x4
+
+#define DM365_ISP5_PCCR 0x04
+#define DM365_ISP5_INTSEL1 0x10
+#define DM365_ISP5_INTSEL2 0x14
+#define DM365_ISP5_INTSEL3 0x18
+#define DM365_ISP5_CCDCMUX 0x20
+#define DM365_ISP5_PG_FRAME_SIZE 0x28
+#define DM365_VPBE_CLK_CTRL 0x00
+/*
+ * vpss interrupts. VDINT0 - vpss_int0, VDINT1 - vpss_int1,
+ * AF - vpss_int3
+ */
+#define DM365_ISP5_INTSEL1_DEFAULT 0x0b1f0100
+/* AEW - vpss_int6, RSZ_INT_DMA - vpss_int5 */
+#define DM365_ISP5_INTSEL2_DEFAULT 0x1f0a0f1f
+/* VENC - vpss_int8 */
+#define DM365_ISP5_INTSEL3_DEFAULT 0x00000015
+
+/* masks and shifts for DM365*/
+#define DM365_CCDC_PG_VD_POL_SHIFT 0
+#define DM365_CCDC_PG_HD_POL_SHIFT 1
+
+#define CCD_SRC_SEL_MASK (BIT_MASK(5) | BIT_MASK(4))
+#define CCD_SRC_SEL_SHIFT 4
+
+/* Different SoC platforms supported by this driver */
+enum vpss_platform_type {
+ DM644X,
+ DM355,
+ DM365,
+};
+
+/*
+ * vpss operations. Depends on platform. Not all functions are available
+ * on all platforms. The api, first check if a functio is available before
+ * invoking it. In the probe, the function ptrs are initialized based on
+ * vpss name. vpss name can be "dm355_vpss", "dm644x_vpss" etc.
+ */
+struct vpss_hw_ops {
+ /* enable clock */
+ int (*enable_clock)(enum vpss_clock_sel clock_sel, int en);
+ /* select input to ccdc */
+ void (*select_ccdc_source)(enum vpss_ccdc_source_sel src_sel);
+ /* clear wbl overflow bit */
+ int (*clear_wbl_overflow)(enum vpss_wbl_sel wbl_sel);
+};
+
+/* vpss configuration */
+struct vpss_oper_config {
+ __iomem void *vpss_regs_base0;
+ __iomem void *vpss_regs_base1;
+ enum vpss_platform_type platform;
+ spinlock_t vpss_lock;
+ struct vpss_hw_ops hw_ops;
+};
+
+static struct vpss_oper_config oper_cfg;
+
+/* register access routines */
+static inline u32 bl_regr(u32 offset)
+{
+ return __raw_readl(oper_cfg.vpss_regs_base0 + offset);
+}
+
+static inline void bl_regw(u32 val, u32 offset)
+{
+ __raw_writel(val, oper_cfg.vpss_regs_base0 + offset);
+}
+
+static inline u32 vpss_regr(u32 offset)
+{
+ return __raw_readl(oper_cfg.vpss_regs_base1 + offset);
+}
+
+static inline void vpss_regw(u32 val, u32 offset)
+{
+ __raw_writel(val, oper_cfg.vpss_regs_base1 + offset);
+}
+
+/* For DM365 only */
+static inline u32 isp5_read(u32 offset)
+{
+ return __raw_readl(oper_cfg.vpss_regs_base0 + offset);
+}
+
+/* For DM365 only */
+static inline void isp5_write(u32 val, u32 offset)
+{
+ __raw_writel(val, oper_cfg.vpss_regs_base0 + offset);
+}
+
+static void dm365_select_ccdc_source(enum vpss_ccdc_source_sel src_sel)
+{
+ u32 temp = isp5_read(DM365_ISP5_CCDCMUX) & ~CCD_SRC_SEL_MASK;
+
+ /* if we are using pattern generator, enable it */
+ if (src_sel == VPSS_PGLPBK || src_sel == VPSS_CCDCPG)
+ temp |= 0x08;
+
+ temp |= (src_sel << CCD_SRC_SEL_SHIFT);
+ isp5_write(temp, DM365_ISP5_CCDCMUX);
+}
+
+static void dm355_select_ccdc_source(enum vpss_ccdc_source_sel src_sel)
+{
+ bl_regw(src_sel << VPSS_HSSISEL_SHIFT, DM355_VPSSBL_CCDCMUX);
+}
+
+int vpss_select_ccdc_source(enum vpss_ccdc_source_sel src_sel)
+{
+ if (!oper_cfg.hw_ops.select_ccdc_source)
+ return -EINVAL;
+
+ oper_cfg.hw_ops.select_ccdc_source(src_sel);
+ return 0;
+}
+EXPORT_SYMBOL(vpss_select_ccdc_source);
+
+static int dm644x_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel)
+{
+ u32 mask = 1, val;
+
+ if (wbl_sel < VPSS_PCR_AEW_WBL_0 ||
+ wbl_sel > VPSS_PCR_CCDC_WBL_O)
+ return -EINVAL;
+
+ /* writing a 0 clear the overflow */
+ mask = ~(mask << wbl_sel);
+ val = bl_regr(DM644X_SBL_PCR_VPSS) & mask;
+ bl_regw(val, DM644X_SBL_PCR_VPSS);
+ return 0;
+}
+
+int vpss_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel)
+{
+ if (!oper_cfg.hw_ops.clear_wbl_overflow)
+ return -EINVAL;
+
+ return oper_cfg.hw_ops.clear_wbl_overflow(wbl_sel);
+}
+EXPORT_SYMBOL(vpss_clear_wbl_overflow);
+
+/*
+ * dm355_enable_clock - Enable VPSS Clock
+ * @clock_sel: CLock to be enabled/disabled
+ * @en: enable/disable flag
+ *
+ * This is called to enable or disable a vpss clock
+ */
+static int dm355_enable_clock(enum vpss_clock_sel clock_sel, int en)
+{
+ unsigned long flags;
+ u32 utemp, mask = 0x1, shift = 0;
+
+ switch (clock_sel) {
+ case VPSS_VPBE_CLOCK:
+ /* nothing since lsb */
+ break;
+ case VPSS_VENC_CLOCK_SEL:
+ shift = 2;
+ break;
+ case VPSS_CFALD_CLOCK:
+ shift = 3;
+ break;
+ case VPSS_H3A_CLOCK:
+ shift = 4;
+ break;
+ case VPSS_IPIPE_CLOCK:
+ shift = 5;
+ break;
+ case VPSS_CCDC_CLOCK:
+ shift = 6;
+ break;
+ default:
+ printk(KERN_ERR "dm355_enable_clock:"
+ " Invalid selector: %d\n", clock_sel);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&oper_cfg.vpss_lock, flags);
+ utemp = vpss_regr(DM355_VPSSCLK_CLKCTRL);
+ if (!en)
+ utemp &= ~(mask << shift);
+ else
+ utemp |= (mask << shift);
+
+ vpss_regw(utemp, DM355_VPSSCLK_CLKCTRL);
+ spin_unlock_irqrestore(&oper_cfg.vpss_lock, flags);
+ return 0;
+}
+
+static int dm365_enable_clock(enum vpss_clock_sel clock_sel, int en)
+{
+ unsigned long flags;
+ u32 utemp, mask = 0x1, shift = 0, offset = DM365_ISP5_PCCR;
+ u32 (*read)(u32 offset) = isp5_read;
+ void(*write)(u32 val, u32 offset) = isp5_write;
+
+ switch (clock_sel) {
+ case VPSS_BL_CLOCK:
+ break;
+ case VPSS_CCDC_CLOCK:
+ shift = 1;
+ break;
+ case VPSS_H3A_CLOCK:
+ shift = 2;
+ break;
+ case VPSS_RSZ_CLOCK:
+ shift = 3;
+ break;
+ case VPSS_IPIPE_CLOCK:
+ shift = 4;
+ break;
+ case VPSS_IPIPEIF_CLOCK:
+ shift = 5;
+ break;
+ case VPSS_PCLK_INTERNAL:
+ shift = 6;
+ break;
+ case VPSS_PSYNC_CLOCK_SEL:
+ shift = 7;
+ break;
+ case VPSS_VPBE_CLOCK:
+ read = vpss_regr;
+ write = vpss_regw;
+ offset = DM365_VPBE_CLK_CTRL;
+ break;
+ case VPSS_VENC_CLOCK_SEL:
+ shift = 2;
+ read = vpss_regr;
+ write = vpss_regw;
+ offset = DM365_VPBE_CLK_CTRL;
+ break;
+ case VPSS_LDC_CLOCK:
+ shift = 3;
+ read = vpss_regr;
+ write = vpss_regw;
+ offset = DM365_VPBE_CLK_CTRL;
+ break;
+ case VPSS_FDIF_CLOCK:
+ shift = 4;
+ read = vpss_regr;
+ write = vpss_regw;
+ offset = DM365_VPBE_CLK_CTRL;
+ break;
+ case VPSS_OSD_CLOCK_SEL:
+ shift = 6;
+ read = vpss_regr;
+ write = vpss_regw;
+ offset = DM365_VPBE_CLK_CTRL;
+ break;
+ case VPSS_LDC_CLOCK_SEL:
+ shift = 7;
+ read = vpss_regr;
+ write = vpss_regw;
+ offset = DM365_VPBE_CLK_CTRL;
+ break;
+ default:
+ printk(KERN_ERR "dm365_enable_clock: Invalid selector: %d\n",
+ clock_sel);
+ return -1;
+ }
+
+ spin_lock_irqsave(&oper_cfg.vpss_lock, flags);
+ utemp = read(offset);
+ if (!en) {
+ mask = ~mask;
+ utemp &= (mask << shift);
+ } else
+ utemp |= (mask << shift);
+
+ write(utemp, offset);
+ spin_unlock_irqrestore(&oper_cfg.vpss_lock, flags);
+
+ return 0;
+}
+
+int vpss_enable_clock(enum vpss_clock_sel clock_sel, int en)
+{
+ if (!oper_cfg.hw_ops.enable_clock)
+ return -EINVAL;
+
+ return oper_cfg.hw_ops.enable_clock(clock_sel, en);
+}
+EXPORT_SYMBOL(vpss_enable_clock);
+
+void dm365_vpss_set_sync_pol(struct vpss_sync_pol sync)
+{
+ int val = 0;
+ val = isp5_read(DM365_ISP5_CCDCMUX);
+
+ val |= (sync.ccdpg_hdpol << DM365_CCDC_PG_HD_POL_SHIFT);
+ val |= (sync.ccdpg_vdpol << DM365_CCDC_PG_VD_POL_SHIFT);
+
+ isp5_write(val, DM365_ISP5_CCDCMUX);
+}
+EXPORT_SYMBOL(dm365_vpss_set_sync_pol);
+
+void dm365_vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size)
+{
+ int current_reg = ((frame_size.hlpfr >> 1) - 1) << 16;
+
+ current_reg |= (frame_size.pplen - 1);
+ isp5_write(current_reg, DM365_ISP5_PG_FRAME_SIZE);
+}
+EXPORT_SYMBOL(dm365_vpss_set_pg_frame_size);
+
+static int __init vpss_probe(struct platform_device *pdev)
+{
+ struct resource *r1, *r2;
+ char *platform_name;
+ int status;
+
+ if (!pdev->dev.platform_data) {
+ dev_err(&pdev->dev, "no platform data\n");
+ return -ENOENT;
+ }
+
+ platform_name = pdev->dev.platform_data;
+ if (!strcmp(platform_name, "dm355_vpss"))
+ oper_cfg.platform = DM355;
+ else if (!strcmp(platform_name, "dm365_vpss"))
+ oper_cfg.platform = DM365;
+ else if (!strcmp(platform_name, "dm644x_vpss"))
+ oper_cfg.platform = DM644X;
+ else {
+ dev_err(&pdev->dev, "vpss driver not supported on"
+ " this platform\n");
+ return -ENODEV;
+ }
+
+ dev_info(&pdev->dev, "%s vpss probed\n", platform_name);
+ r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r1)
+ return -ENOENT;
+
+ r1 = request_mem_region(r1->start, resource_size(r1), r1->name);
+ if (!r1)
+ return -EBUSY;
+
+ oper_cfg.vpss_regs_base0 = ioremap(r1->start, resource_size(r1));
+ if (!oper_cfg.vpss_regs_base0) {
+ status = -EBUSY;
+ goto fail1;
+ }
+
+ if (oper_cfg.platform == DM355 || oper_cfg.platform == DM365) {
+ r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!r2) {
+ status = -ENOENT;
+ goto fail2;
+ }
+ r2 = request_mem_region(r2->start, resource_size(r2), r2->name);
+ if (!r2) {
+ status = -EBUSY;
+ goto fail2;
+ }
+
+ oper_cfg.vpss_regs_base1 = ioremap(r2->start,
+ resource_size(r2));
+ if (!oper_cfg.vpss_regs_base1) {
+ status = -EBUSY;
+ goto fail3;
+ }
+ }
+
+ if (oper_cfg.platform == DM355) {
+ oper_cfg.hw_ops.enable_clock = dm355_enable_clock;
+ oper_cfg.hw_ops.select_ccdc_source = dm355_select_ccdc_source;
+ /* Setup vpss interrupts */
+ bl_regw(DM355_VPSSBL_INTSEL_DEFAULT, DM355_VPSSBL_INTSEL);
+ bl_regw(DM355_VPSSBL_EVTSEL_DEFAULT, DM355_VPSSBL_EVTSEL);
+ } else if (oper_cfg.platform == DM365) {
+ oper_cfg.hw_ops.enable_clock = dm365_enable_clock;
+ oper_cfg.hw_ops.select_ccdc_source = dm365_select_ccdc_source;
+ /* Setup vpss interrupts */
+ isp5_write(DM365_ISP5_INTSEL1_DEFAULT, DM365_ISP5_INTSEL1);
+ isp5_write(DM365_ISP5_INTSEL2_DEFAULT, DM365_ISP5_INTSEL2);
+ isp5_write(DM365_ISP5_INTSEL3_DEFAULT, DM365_ISP5_INTSEL3);
+ } else
+ oper_cfg.hw_ops.clear_wbl_overflow = dm644x_clear_wbl_overflow;
+
+ spin_lock_init(&oper_cfg.vpss_lock);
+ dev_info(&pdev->dev, "%s vpss probe success\n", platform_name);
+ return 0;
+
+fail3:
+ release_mem_region(r2->start, resource_size(r2));
+fail2:
+ iounmap(oper_cfg.vpss_regs_base0);
+fail1:
+ release_mem_region(r1->start, resource_size(r1));
+ return status;
+}
+
+static int __devexit vpss_remove(struct platform_device *pdev)
+{
+ struct resource *res;
+
+ iounmap(oper_cfg.vpss_regs_base0);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, resource_size(res));
+ if (oper_cfg.platform == DM355 || oper_cfg.platform == DM365) {
+ iounmap(oper_cfg.vpss_regs_base1);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ release_mem_region(res->start, resource_size(res));
+ }
+ return 0;
+}
+
+static struct platform_driver vpss_driver = {
+ .driver = {
+ .name = "vpss",
+ .owner = THIS_MODULE,
+ },
+ .remove = __devexit_p(vpss_remove),
+ .probe = vpss_probe,
+};
+
+static void vpss_exit(void)
+{
+ platform_driver_unregister(&vpss_driver);
+}
+
+static int __init vpss_init(void)
+{
+ return platform_driver_register(&vpss_driver);
+}
+subsys_initcall(vpss_init);
+module_exit(vpss_exit);
diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig
new file mode 100644
index 00000000..f6f622e1
--- /dev/null
+++ b/drivers/media/video/em28xx/Kconfig
@@ -0,0 +1,58 @@
+config VIDEO_EM28XX
+ tristate "Empia EM28xx USB video capture support"
+ depends on VIDEO_DEV && I2C
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ select VIDEOBUF_VMALLOC
+ select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_TVP5150 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_MT9V011 if VIDEO_HELPER_CHIPS_AUTO
+
+ ---help---
+ This is a video4linux driver for Empia 28xx based TV cards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called em28xx
+
+config VIDEO_EM28XX_ALSA
+ depends on VIDEO_EM28XX && SND
+ select SND_PCM
+ tristate "Empia EM28xx ALSA audio module"
+ ---help---
+ This is an ALSA driver for some Empia 28xx based TV cards.
+
+ This is not required for em2800/em2820/em2821 boards. However,
+ newer em28xx devices uses Vendor Class for audio, instead of
+ implementing the USB Audio Class. For those chips, this module
+ will enable digital audio.
+
+ To compile this driver as a module, choose M here: the
+ module will be called em28xx-alsa
+
+config VIDEO_EM28XX_DVB
+ tristate "DVB/ATSC Support for em28xx based TV cards"
+ depends on VIDEO_EM28XX && DVB_CORE
+ select DVB_LGDT330X if !DVB_FE_CUSTOMISE
+ select DVB_ZL10353 if !DVB_FE_CUSTOMISE
+ select DVB_TDA10023 if !DVB_FE_CUSTOMISE
+ select DVB_S921 if !DVB_FE_CUSTOMISE
+ select DVB_DRXD if !DVB_FE_CUSTOMISE
+ select DVB_CXD2820R if !DVB_FE_CUSTOMISE
+ select DVB_DRXK if !DVB_FE_CUSTOMISE
+ select DVB_TDA18271C2DD if !DVB_FE_CUSTOMISE
+ select DVB_TDA10071 if !DVB_FE_CUSTOMISE
+ select DVB_A8293 if !DVB_FE_CUSTOMISE
+ select VIDEOBUF_DVB
+ ---help---
+ This adds support for DVB cards based on the
+ Empiatech em28xx chips.
+
+config VIDEO_EM28XX_RC
+ bool "EM28XX Remote Controller support"
+ depends on RC_CORE
+ depends on VIDEO_EM28XX
+ depends on !(RC_CORE=m && VIDEO_EM28XX=y)
+ default y
+ ---help---
+ Enables Remote Controller support on em28xx driver.
diff --git a/drivers/media/video/em28xx/Makefile b/drivers/media/video/em28xx/Makefile
new file mode 100644
index 00000000..2abdf76c
--- /dev/null
+++ b/drivers/media/video/em28xx/Makefile
@@ -0,0 +1,16 @@
+em28xx-y := em28xx-video.o em28xx-i2c.o em28xx-cards.o
+em28xx-y += em28xx-core.o em28xx-vbi.o
+
+em28xx-$(CONFIG_VIDEO_EM28XX_RC) += em28xx-input.o
+
+em28xx-alsa-objs := em28xx-audio.o
+
+obj-$(CONFIG_VIDEO_EM28XX) += em28xx.o
+obj-$(CONFIG_VIDEO_EM28XX_ALSA) += em28xx-alsa.o
+obj-$(CONFIG_VIDEO_EM28XX_DVB) += em28xx-dvb.o
+
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
+
diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c
new file mode 100644
index 00000000..e2a7b77c
--- /dev/null
+++ b/drivers/media/video/em28xx/em28xx-audio.c
@@ -0,0 +1,748 @@
+/*
+ * Empiatech em28x1 audio extension
+ *
+ * Copyright (C) 2006 Markus Rechberger <mrechberger@gmail.com>
+ *
+ * Copyright (C) 2007-2011 Mauro Carvalho Chehab <mchehab@redhat.com>
+ * - Port to work with the in-kernel driver
+ * - Cleanups, fixes, alsa-controls, etc.
+ *
+ * This driver is based on my previous au600 usb pstn audio driver
+ * and inherits all the copyrights
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/init.h>
+#include <linux/sound.h>
+#include <linux/spinlock.h>
+#include <linux/soundcard.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+#include <media/v4l2-common.h>
+#include "em28xx.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "activates debug info");
+
+#define dprintk(fmt, arg...) do { \
+ if (debug) \
+ printk(KERN_INFO "em28xx-audio %s: " fmt, \
+ __func__, ##arg); \
+ } while (0)
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+
+static int em28xx_deinit_isoc_audio(struct em28xx *dev)
+{
+ int i;
+
+ dprintk("Stopping isoc\n");
+ for (i = 0; i < EM28XX_AUDIO_BUFS; i++) {
+ if (!irqs_disabled())
+ usb_kill_urb(dev->adev.urb[i]);
+ else
+ usb_unlink_urb(dev->adev.urb[i]);
+
+ usb_free_urb(dev->adev.urb[i]);
+ dev->adev.urb[i] = NULL;
+
+ kfree(dev->adev.transfer_buffer[i]);
+ dev->adev.transfer_buffer[i] = NULL;
+ }
+
+ return 0;
+}
+
+static void em28xx_audio_isocirq(struct urb *urb)
+{
+ struct em28xx *dev = urb->context;
+ int i;
+ unsigned int oldptr;
+ int period_elapsed = 0;
+ int status;
+ unsigned char *cp;
+ unsigned int stride;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+
+ switch (urb->status) {
+ case 0: /* success */
+ case -ETIMEDOUT: /* NAK */
+ break;
+ case -ECONNRESET: /* kill */
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ default: /* error */
+ dprintk("urb completition error %d.\n", urb->status);
+ break;
+ }
+
+ if (atomic_read(&dev->stream_started) == 0)
+ return;
+
+ if (dev->adev.capture_pcm_substream) {
+ substream = dev->adev.capture_pcm_substream;
+ runtime = substream->runtime;
+ stride = runtime->frame_bits >> 3;
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ int length =
+ urb->iso_frame_desc[i].actual_length / stride;
+ cp = (unsigned char *)urb->transfer_buffer +
+ urb->iso_frame_desc[i].offset;
+
+ if (!length)
+ continue;
+
+ oldptr = dev->adev.hwptr_done_capture;
+ if (oldptr + length >= runtime->buffer_size) {
+ unsigned int cnt =
+ runtime->buffer_size - oldptr;
+ memcpy(runtime->dma_area + oldptr * stride, cp,
+ cnt * stride);
+ memcpy(runtime->dma_area, cp + cnt * stride,
+ length * stride - cnt * stride);
+ } else {
+ memcpy(runtime->dma_area + oldptr * stride, cp,
+ length * stride);
+ }
+
+ snd_pcm_stream_lock(substream);
+
+ dev->adev.hwptr_done_capture += length;
+ if (dev->adev.hwptr_done_capture >=
+ runtime->buffer_size)
+ dev->adev.hwptr_done_capture -=
+ runtime->buffer_size;
+
+ dev->adev.capture_transfer_done += length;
+ if (dev->adev.capture_transfer_done >=
+ runtime->period_size) {
+ dev->adev.capture_transfer_done -=
+ runtime->period_size;
+ period_elapsed = 1;
+ }
+
+ snd_pcm_stream_unlock(substream);
+ }
+ if (period_elapsed)
+ snd_pcm_period_elapsed(substream);
+ }
+ urb->status = 0;
+
+ status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (status < 0) {
+ em28xx_errdev("resubmit of audio urb failed (error=%i)\n",
+ status);
+ }
+ return;
+}
+
+static int em28xx_init_audio_isoc(struct em28xx *dev)
+{
+ int i, errCode;
+ const int sb_size = EM28XX_NUM_AUDIO_PACKETS *
+ EM28XX_AUDIO_MAX_PACKET_SIZE;
+
+ dprintk("Starting isoc transfers\n");
+
+ for (i = 0; i < EM28XX_AUDIO_BUFS; i++) {
+ struct urb *urb;
+ int j, k;
+
+ dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC);
+ if (!dev->adev.transfer_buffer[i])
+ return -ENOMEM;
+
+ memset(dev->adev.transfer_buffer[i], 0x80, sb_size);
+ urb = usb_alloc_urb(EM28XX_NUM_AUDIO_PACKETS, GFP_ATOMIC);
+ if (!urb) {
+ em28xx_errdev("usb_alloc_urb failed!\n");
+ for (j = 0; j < i; j++) {
+ usb_free_urb(dev->adev.urb[j]);
+ kfree(dev->adev.transfer_buffer[j]);
+ }
+ return -ENOMEM;
+ }
+
+ urb->dev = dev->udev;
+ urb->context = dev;
+ urb->pipe = usb_rcvisocpipe(dev->udev, EM28XX_EP_AUDIO);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->transfer_buffer = dev->adev.transfer_buffer[i];
+ urb->interval = 1;
+ urb->complete = em28xx_audio_isocirq;
+ urb->number_of_packets = EM28XX_NUM_AUDIO_PACKETS;
+ urb->transfer_buffer_length = sb_size;
+
+ for (j = k = 0; j < EM28XX_NUM_AUDIO_PACKETS;
+ j++, k += EM28XX_AUDIO_MAX_PACKET_SIZE) {
+ urb->iso_frame_desc[j].offset = k;
+ urb->iso_frame_desc[j].length =
+ EM28XX_AUDIO_MAX_PACKET_SIZE;
+ }
+ dev->adev.urb[i] = urb;
+ }
+
+ for (i = 0; i < EM28XX_AUDIO_BUFS; i++) {
+ errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC);
+ if (errCode) {
+ em28xx_errdev("submit of audio urb failed\n");
+ em28xx_deinit_isoc_audio(dev);
+ atomic_set(&dev->stream_started, 0);
+ return errCode;
+ }
+
+ }
+
+ return 0;
+}
+
+static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
+ size_t size)
+{
+ struct snd_pcm_runtime *runtime = subs->runtime;
+
+ dprintk("Allocating vbuffer\n");
+ if (runtime->dma_area) {
+ if (runtime->dma_bytes > size)
+ return 0;
+
+ vfree(runtime->dma_area);
+ }
+ runtime->dma_area = vmalloc(size);
+ if (!runtime->dma_area)
+ return -ENOMEM;
+
+ runtime->dma_bytes = size;
+
+ return 0;
+}
+
+static struct snd_pcm_hardware snd_em28xx_hw_capture = {
+ .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_MMAP_VALID,
+
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT,
+
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */
+ .period_bytes_min = 64, /* 12544/2, */
+ .period_bytes_max = 12544,
+ .periods_min = 2,
+ .periods_max = 98, /* 12544, */
+};
+
+static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
+{
+ struct em28xx *dev = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int ret = 0;
+
+ dprintk("opening device and trying to acquire exclusive lock\n");
+
+ if (!dev) {
+ em28xx_err("BUG: em28xx can't find device struct."
+ " Can't proceed with open\n");
+ return -ENODEV;
+ }
+
+ runtime->hw = snd_em28xx_hw_capture;
+ if ((dev->alt == 0 || dev->audio_ifnum) && dev->adev.users == 0) {
+ if (dev->audio_ifnum)
+ dev->alt = 1;
+ else
+ dev->alt = 7;
+
+ dprintk("changing alternate number on interface %d to %d\n",
+ dev->audio_ifnum, dev->alt);
+ usb_set_interface(dev->udev, dev->audio_ifnum, dev->alt);
+
+ /* Sets volume, mute, etc */
+ dev->mute = 0;
+ mutex_lock(&dev->lock);
+ ret = em28xx_audio_analog_set(dev);
+ if (ret < 0)
+ goto err;
+
+ dev->adev.users++;
+ mutex_unlock(&dev->lock);
+ }
+
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ dev->adev.capture_pcm_substream = substream;
+ runtime->private_data = dev;
+
+ return 0;
+err:
+ mutex_unlock(&dev->lock);
+
+ em28xx_err("Error while configuring em28xx mixer\n");
+ return ret;
+}
+
+static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct em28xx *dev = snd_pcm_substream_chip(substream);
+
+ dprintk("closing device\n");
+
+ dev->mute = 1;
+ mutex_lock(&dev->lock);
+ dev->adev.users--;
+ if (atomic_read(&dev->stream_started) > 0) {
+ atomic_set(&dev->stream_started, 0);
+ schedule_work(&dev->wq_trigger);
+ }
+
+ em28xx_audio_analog_set(dev);
+ if (substream->runtime->dma_area) {
+ dprintk("freeing\n");
+ vfree(substream->runtime->dma_area);
+ substream->runtime->dma_area = NULL;
+ }
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+
+static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ unsigned int channels, rate, format;
+ int ret;
+
+ dprintk("Setting capture parameters\n");
+
+ ret = snd_pcm_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (ret < 0)
+ return ret;
+ format = params_format(hw_params);
+ rate = params_rate(hw_params);
+ channels = params_channels(hw_params);
+
+ /* TODO: set up em28xx audio chip to deliver the correct audio format,
+ current default is 48000hz multiplexed => 96000hz mono
+ which shouldn't matter since analogue TV only supports mono */
+ return 0;
+}
+
+static int snd_em28xx_hw_capture_free(struct snd_pcm_substream *substream)
+{
+ struct em28xx *dev = snd_pcm_substream_chip(substream);
+
+ dprintk("Stop capture, if needed\n");
+
+ if (atomic_read(&dev->stream_started) > 0) {
+ atomic_set(&dev->stream_started, 0);
+ schedule_work(&dev->wq_trigger);
+ }
+
+ return 0;
+}
+
+static int snd_em28xx_prepare(struct snd_pcm_substream *substream)
+{
+ struct em28xx *dev = snd_pcm_substream_chip(substream);
+
+ dev->adev.hwptr_done_capture = 0;
+ dev->adev.capture_transfer_done = 0;
+
+ return 0;
+}
+
+static void audio_trigger(struct work_struct *work)
+{
+ struct em28xx *dev = container_of(work, struct em28xx, wq_trigger);
+
+ if (atomic_read(&dev->stream_started)) {
+ dprintk("starting capture");
+ em28xx_init_audio_isoc(dev);
+ } else {
+ dprintk("stopping capture");
+ em28xx_deinit_isoc_audio(dev);
+ }
+}
+
+static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct em28xx *dev = snd_pcm_substream_chip(substream);
+ int retval = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
+ case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
+ case SNDRV_PCM_TRIGGER_START:
+ atomic_set(&dev->stream_started, 1);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */
+ case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */
+ case SNDRV_PCM_TRIGGER_STOP:
+ atomic_set(&dev->stream_started, 0);
+ break;
+ default:
+ retval = -EINVAL;
+ }
+ schedule_work(&dev->wq_trigger);
+ return retval;
+}
+
+static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream
+ *substream)
+{
+ unsigned long flags;
+ struct em28xx *dev;
+ snd_pcm_uframes_t hwptr_done;
+
+ dev = snd_pcm_substream_chip(substream);
+ spin_lock_irqsave(&dev->adev.slock, flags);
+ hwptr_done = dev->adev.hwptr_done_capture;
+ spin_unlock_irqrestore(&dev->adev.slock, flags);
+
+ return hwptr_done;
+}
+
+static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
+ unsigned long offset)
+{
+ void *pageptr = subs->runtime->dma_area + offset;
+
+ return vmalloc_to_page(pageptr);
+}
+
+/*
+ * AC97 volume control support
+ */
+static int em28xx_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *info)
+{
+ info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ info->count = 2;
+ info->value.integer.min = 0;
+ info->value.integer.max = 0x1f;
+
+ return 0;
+}
+
+static int em28xx_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ struct em28xx *dev = snd_kcontrol_chip(kcontrol);
+ u16 val = (0x1f - (value->value.integer.value[0] & 0x1f)) |
+ (0x1f - (value->value.integer.value[1] & 0x1f)) << 8;
+ int rc;
+
+ mutex_lock(&dev->lock);
+ rc = em28xx_read_ac97(dev, kcontrol->private_value);
+ if (rc < 0)
+ goto err;
+
+ val |= rc & 0x8000; /* Preserve the mute flag */
+
+ rc = em28xx_write_ac97(dev, kcontrol->private_value, val);
+ if (rc < 0)
+ goto err;
+
+ dprintk("%sleft vol %d, right vol %d (0x%04x) to ac97 volume control 0x%04x\n",
+ (val & 0x8000) ? "muted " : "",
+ 0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f),
+ val, (int)kcontrol->private_value);
+
+err:
+ mutex_unlock(&dev->lock);
+ return rc;
+}
+
+static int em28xx_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ struct em28xx *dev = snd_kcontrol_chip(kcontrol);
+ int val;
+
+ mutex_lock(&dev->lock);
+ val = em28xx_read_ac97(dev, kcontrol->private_value);
+ mutex_unlock(&dev->lock);
+ if (val < 0)
+ return val;
+
+ dprintk("%sleft vol %d, right vol %d (0x%04x) from ac97 volume control 0x%04x\n",
+ (val & 0x8000) ? "muted " : "",
+ 0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f),
+ val, (int)kcontrol->private_value);
+
+ value->value.integer.value[0] = 0x1f - (val & 0x1f);
+ value->value.integer.value[1] = 0x1f - ((val << 8) & 0x1f);
+
+ return 0;
+}
+
+static int em28xx_vol_put_mute(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ struct em28xx *dev = snd_kcontrol_chip(kcontrol);
+ u16 val = value->value.integer.value[0];
+ int rc;
+
+ mutex_lock(&dev->lock);
+ rc = em28xx_read_ac97(dev, kcontrol->private_value);
+ if (rc < 0)
+ goto err;
+
+ if (val)
+ rc &= 0x1f1f;
+ else
+ rc |= 0x8000;
+
+ rc = em28xx_write_ac97(dev, kcontrol->private_value, rc);
+ if (rc < 0)
+ goto err;
+
+ dprintk("%sleft vol %d, right vol %d (0x%04x) to ac97 volume control 0x%04x\n",
+ (val & 0x8000) ? "muted " : "",
+ 0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f),
+ val, (int)kcontrol->private_value);
+
+err:
+ mutex_unlock(&dev->lock);
+ return rc;
+}
+
+static int em28xx_vol_get_mute(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ struct em28xx *dev = snd_kcontrol_chip(kcontrol);
+ int val;
+
+ mutex_lock(&dev->lock);
+ val = em28xx_read_ac97(dev, kcontrol->private_value);
+ mutex_unlock(&dev->lock);
+ if (val < 0)
+ return val;
+
+ if (val & 0x8000)
+ value->value.integer.value[0] = 0;
+ else
+ value->value.integer.value[0] = 1;
+
+ dprintk("%sleft vol %d, right vol %d (0x%04x) from ac97 volume control 0x%04x\n",
+ (val & 0x8000) ? "muted " : "",
+ 0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f),
+ val, (int)kcontrol->private_value);
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(em28xx_db_scale, -3450, 150, 0);
+
+static int em28xx_cvol_new(struct snd_card *card, struct em28xx *dev,
+ char *name, int id)
+{
+ int err;
+ char ctl_name[44];
+ struct snd_kcontrol *kctl;
+ struct snd_kcontrol_new tmp;
+
+ memset (&tmp, 0, sizeof(tmp));
+ tmp.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ tmp.private_value = id,
+ tmp.name = ctl_name,
+
+ /* Add Mute Control */
+ sprintf(ctl_name, "%s Switch", name);
+ tmp.get = em28xx_vol_get_mute;
+ tmp.put = em28xx_vol_put_mute;
+ tmp.info = snd_ctl_boolean_mono_info;
+ kctl = snd_ctl_new1(&tmp, dev);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ return err;
+ dprintk("Added control %s for ac97 volume control 0x%04x\n",
+ ctl_name, id);
+
+ memset (&tmp, 0, sizeof(tmp));
+ tmp.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ tmp.private_value = id,
+ tmp.name = ctl_name,
+
+ /* Add Volume Control */
+ sprintf(ctl_name, "%s Volume", name);
+ tmp.get = em28xx_vol_get;
+ tmp.put = em28xx_vol_put;
+ tmp.info = em28xx_vol_info;
+ tmp.tlv.p = em28xx_db_scale,
+ kctl = snd_ctl_new1(&tmp, dev);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ return err;
+ dprintk("Added control %s for ac97 volume control 0x%04x\n",
+ ctl_name, id);
+
+ return 0;
+}
+
+/*
+ * register/unregister code and data
+ */
+static struct snd_pcm_ops snd_em28xx_pcm_capture = {
+ .open = snd_em28xx_capture_open,
+ .close = snd_em28xx_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_em28xx_hw_capture_params,
+ .hw_free = snd_em28xx_hw_capture_free,
+ .prepare = snd_em28xx_prepare,
+ .trigger = snd_em28xx_capture_trigger,
+ .pointer = snd_em28xx_capture_pointer,
+ .page = snd_pcm_get_vmalloc_page,
+};
+
+static int em28xx_audio_init(struct em28xx *dev)
+{
+ struct em28xx_audio *adev = &dev->adev;
+ struct snd_pcm *pcm;
+ struct snd_card *card;
+ static int devnr;
+ int err;
+
+ if (!dev->has_alsa_audio || dev->audio_ifnum < 0) {
+ /* This device does not support the extension (in this case
+ the device is expecting the snd-usb-audio module or
+ doesn't have analog audio support at all) */
+ return 0;
+ }
+
+ printk(KERN_INFO "em28xx-audio.c: probing for em28xx Audio Vendor Class\n");
+ printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2006 Markus "
+ "Rechberger\n");
+ printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2007-2011 Mauro Carvalho Chehab\n");
+
+ err = snd_card_create(index[devnr], "Em28xx Audio", THIS_MODULE, 0,
+ &card);
+ if (err < 0)
+ return err;
+
+ spin_lock_init(&adev->slock);
+ err = snd_pcm_new(card, "Em28xx Audio", 0, 0, 1, &pcm);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_em28xx_pcm_capture);
+ pcm->info_flags = 0;
+ pcm->private_data = dev;
+ strcpy(pcm->name, "Empia 28xx Capture");
+
+ snd_card_set_dev(card, &dev->udev->dev);
+ strcpy(card->driver, "Em28xx-Audio");
+ strcpy(card->shortname, "Em28xx Audio");
+ strcpy(card->longname, "Empia Em28xx Audio");
+
+ INIT_WORK(&dev->wq_trigger, audio_trigger);
+
+ if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
+ em28xx_cvol_new(card, dev, "Video", AC97_VIDEO_VOL);
+ em28xx_cvol_new(card, dev, "Line In", AC97_LINEIN_VOL);
+ em28xx_cvol_new(card, dev, "Phone", AC97_PHONE_VOL);
+ em28xx_cvol_new(card, dev, "Microphone", AC97_PHONE_VOL);
+ em28xx_cvol_new(card, dev, "CD", AC97_CD_VOL);
+ em28xx_cvol_new(card, dev, "AUX", AC97_AUX_VOL);
+ em28xx_cvol_new(card, dev, "PCM", AC97_PCM_OUT_VOL);
+
+ em28xx_cvol_new(card, dev, "Master", AC97_MASTER_VOL);
+ em28xx_cvol_new(card, dev, "Line", AC97_LINE_LEVEL_VOL);
+ em28xx_cvol_new(card, dev, "Mono", AC97_MASTER_MONO_VOL);
+ em28xx_cvol_new(card, dev, "LFE", AC97_LFE_MASTER_VOL);
+ em28xx_cvol_new(card, dev, "Surround", AC97_SURR_MASTER_VOL);
+ }
+
+ err = snd_card_register(card);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ adev->sndcard = card;
+ adev->udev = dev->udev;
+
+ return 0;
+}
+
+static int em28xx_audio_fini(struct em28xx *dev)
+{
+ if (dev == NULL)
+ return 0;
+
+ if (dev->has_alsa_audio != 1) {
+ /* This device does not support the extension (in this case
+ the device is expecting the snd-usb-audio module or
+ doesn't have analog audio support at all) */
+ return 0;
+ }
+
+ if (dev->adev.sndcard) {
+ snd_card_free(dev->adev.sndcard);
+ dev->adev.sndcard = NULL;
+ }
+
+ return 0;
+}
+
+static struct em28xx_ops audio_ops = {
+ .id = EM28XX_AUDIO,
+ .name = "Em28xx Audio Extension",
+ .init = em28xx_audio_init,
+ .fini = em28xx_audio_fini,
+};
+
+static int __init em28xx_alsa_register(void)
+{
+ return em28xx_register_extension(&audio_ops);
+}
+
+static void __exit em28xx_alsa_unregister(void)
+{
+ em28xx_unregister_extension(&audio_ops);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Markus Rechberger <mrechberger@gmail.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_DESCRIPTION("Em28xx Audio driver");
+
+module_init(em28xx_alsa_register);
+module_exit(em28xx_alsa_unregister);
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
new file mode 100644
index 00000000..9fd8cc7d
--- /dev/null
+++ b/drivers/media/video/em28xx/em28xx-cards.c
@@ -0,0 +1,3490 @@
+/*
+ em28xx-cards.c - driver for Empia EM2800/EM2820/2840 USB
+ video capture devices
+
+ Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
+ Markus Rechberger <mrechberger@gmail.com>
+ Mauro Carvalho Chehab <mchehab@infradead.org>
+ Sascha Sommer <saschasommer@freenet.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/usb.h>
+#include <media/tuner.h>
+#include <media/msp3400.h>
+#include <media/saa7115.h>
+#include <media/tvp5150.h>
+#include <media/tvaudio.h>
+#include <media/mt9v011.h>
+#include <media/i2c-addr.h>
+#include <media/tveeprom.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
+
+#include "em28xx.h"
+
+#define DRIVER_NAME "em28xx"
+
+static int tuner = -1;
+module_param(tuner, int, 0444);
+MODULE_PARM_DESC(tuner, "tuner type");
+
+static unsigned int disable_ir;
+module_param(disable_ir, int, 0444);
+MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
+
+static unsigned int disable_usb_speed_check;
+module_param(disable_usb_speed_check, int, 0444);
+MODULE_PARM_DESC(disable_usb_speed_check,
+ "override min bandwidth requirement of 480M bps");
+
+static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
+module_param_array(card, int, NULL, 0444);
+MODULE_PARM_DESC(card, "card type");
+
+/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS - 1 */
+static unsigned long em28xx_devused;
+
+struct em28xx_hash_table {
+ unsigned long hash;
+ unsigned int model;
+ unsigned int tuner;
+};
+
+/*
+ * Reset sequences for analog/digital modes
+ */
+
+/* Reset for the most [analog] boards */
+static struct em28xx_reg_seq default_analog[] = {
+ {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Reset for the most [digital] boards */
+static struct em28xx_reg_seq default_digital[] = {
+ {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Board Hauppauge WinTV HVR 900 analog */
+static struct em28xx_reg_seq hauppauge_wintv_hvr_900_analog[] = {
+ {EM28XX_R08_GPIO, 0x2d, ~EM_GPIO_4, 10},
+ {0x05, 0xff, 0x10, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Board Hauppauge WinTV HVR 900 digital */
+static struct em28xx_reg_seq hauppauge_wintv_hvr_900_digital[] = {
+ {EM28XX_R08_GPIO, 0x2e, ~EM_GPIO_4, 10},
+ {EM2880_R04_GPO, 0x04, 0x0f, 10},
+ {EM2880_R04_GPO, 0x0c, 0x0f, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Board Hauppauge WinTV HVR 900 (R2) digital */
+static struct em28xx_reg_seq hauppauge_wintv_hvr_900R2_digital[] = {
+ {EM28XX_R08_GPIO, 0x2e, ~EM_GPIO_4, 10},
+ {EM2880_R04_GPO, 0x0c, 0x0f, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */
+static struct em28xx_reg_seq em2880_msi_digivox_ad_analog[] = {
+ {EM28XX_R08_GPIO, 0x69, ~EM_GPIO_4, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */
+
+/* Board - EM2870 Kworld 355u
+ Analog - No input analog */
+
+/* Board - EM2882 Kworld 315U digital */
+static struct em28xx_reg_seq em2882_kworld_315u_digital[] = {
+ {EM28XX_R08_GPIO, 0xff, 0xff, 10},
+ {EM28XX_R08_GPIO, 0xfe, 0xff, 10},
+ {EM2880_R04_GPO, 0x04, 0xff, 10},
+ {EM2880_R04_GPO, 0x0c, 0xff, 10},
+ {EM28XX_R08_GPIO, 0x7e, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq em2882_kworld_315u_tuner_gpio[] = {
+ {EM2880_R04_GPO, 0x08, 0xff, 10},
+ {EM2880_R04_GPO, 0x0c, 0xff, 10},
+ {EM2880_R04_GPO, 0x08, 0xff, 10},
+ {EM2880_R04_GPO, 0x0c, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq kworld_330u_analog[] = {
+ {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10},
+ {EM2880_R04_GPO, 0x00, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq kworld_330u_digital[] = {
+ {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10},
+ {EM2880_R04_GPO, 0x08, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Evga inDtube
+ GPIO0 - Enable digital power (s5h1409) - low to enable
+ GPIO1 - Enable analog power (tvp5150/emp202) - low to enable
+ GPIO4 - xc3028 reset
+ GOP3 - s5h1409 reset
+ */
+static struct em28xx_reg_seq evga_indtube_analog[] = {
+ {EM28XX_R08_GPIO, 0x79, 0xff, 60},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq evga_indtube_digital[] = {
+ {EM28XX_R08_GPIO, 0x7a, 0xff, 1},
+ {EM2880_R04_GPO, 0x04, 0xff, 10},
+ {EM2880_R04_GPO, 0x0c, 0xff, 1},
+ { -1, -1, -1, -1},
+};
+
+/*
+ * KWorld PlusTV 340U and UB435-Q (ATSC) GPIOs map:
+ * EM_GPIO_0 - currently unknown
+ * EM_GPIO_1 - LED disable/enable (1 = off, 0 = on)
+ * EM_GPIO_2 - currently unknown
+ * EM_GPIO_3 - currently unknown
+ * EM_GPIO_4 - TDA18271HD/C1 tuner (1 = active, 0 = in reset)
+ * EM_GPIO_5 - LGDT3304 ATSC/QAM demod (1 = active, 0 = in reset)
+ * EM_GPIO_6 - currently unknown
+ * EM_GPIO_7 - currently unknown
+ */
+static struct em28xx_reg_seq kworld_a340_digital[] = {
+ {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Pinnacle Hybrid Pro eb1a:2881 */
+static struct em28xx_reg_seq pinnacle_hybrid_pro_analog[] = {
+ {EM28XX_R08_GPIO, 0xfd, ~EM_GPIO_4, 10},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq pinnacle_hybrid_pro_digital[] = {
+ {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10},
+ {EM2880_R04_GPO, 0x04, 0xff, 100},/* zl10353 reset */
+ {EM2880_R04_GPO, 0x0c, 0xff, 1},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_analog[] = {
+ {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10},
+ {EM2880_R04_GPO, 0x00, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_digital[] = {
+ {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10},
+ {EM2880_R04_GPO, 0x08, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+
+/* eb1a:2868 Reddo DVB-C USB TV Box
+ GPIO4 - CU1216L NIM
+ Other GPIOs seems to be don't care. */
+static struct em28xx_reg_seq reddo_dvb_c_usb_box[] = {
+ {EM28XX_R08_GPIO, 0xfe, 0xff, 10},
+ {EM28XX_R08_GPIO, 0xde, 0xff, 10},
+ {EM28XX_R08_GPIO, 0xfe, 0xff, 10},
+ {EM28XX_R08_GPIO, 0xff, 0xff, 10},
+ {EM28XX_R08_GPIO, 0x7f, 0xff, 10},
+ {EM28XX_R08_GPIO, 0x6f, 0xff, 10},
+ {EM28XX_R08_GPIO, 0xff, 0xff, 10},
+ {-1, -1, -1, -1},
+};
+
+/* Callback for the most boards */
+static struct em28xx_reg_seq default_tuner_gpio[] = {
+ {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10},
+ {EM28XX_R08_GPIO, 0, EM_GPIO_4, 10},
+ {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Mute/unmute */
+static struct em28xx_reg_seq compro_unmute_tv_gpio[] = {
+ {EM28XX_R08_GPIO, 5, 7, 10},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq compro_unmute_svid_gpio[] = {
+ {EM28XX_R08_GPIO, 4, 7, 10},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq compro_mute_gpio[] = {
+ {EM28XX_R08_GPIO, 6, 7, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Terratec AV350 */
+static struct em28xx_reg_seq terratec_av350_mute_gpio[] = {
+ {EM28XX_R08_GPIO, 0xff, 0x7f, 10},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq terratec_av350_unmute_gpio[] = {
+ {EM28XX_R08_GPIO, 0xff, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq silvercrest_reg_seq[] = {
+ {EM28XX_R08_GPIO, 0xff, 0xff, 10},
+ {EM28XX_R08_GPIO, 0x01, 0xf7, 10},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq vc211a_enable[] = {
+ {EM28XX_R08_GPIO, 0xff, 0x07, 10},
+ {EM28XX_R08_GPIO, 0xff, 0x0f, 10},
+ {EM28XX_R08_GPIO, 0xff, 0x0b, 10},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq dikom_dk300_digital[] = {
+ {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10},
+ {EM2880_R04_GPO, 0x08, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+
+
+/* Reset for the most [digital] boards */
+static struct em28xx_reg_seq leadership_digital[] = {
+ {EM2874_R80_GPIO, 0x70, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq leadership_reset[] = {
+ {EM2874_R80_GPIO, 0xf0, 0xff, 10},
+ {EM2874_R80_GPIO, 0xb0, 0xff, 10},
+ {EM2874_R80_GPIO, 0xf0, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+
+/* 2013:024f PCTV nanoStick T2 290e
+ * GPIO_6 - demod reset
+ * GPIO_7 - LED
+ */
+static struct em28xx_reg_seq pctv_290e[] = {
+ {EM2874_R80_GPIO, 0x00, 0xff, 80},
+ {EM2874_R80_GPIO, 0x40, 0xff, 80}, /* GPIO_6 = 1 */
+ {EM2874_R80_GPIO, 0xc0, 0xff, 80}, /* GPIO_7 = 1 */
+ {-1, -1, -1, -1},
+};
+
+#if 0
+static struct em28xx_reg_seq terratec_h5_gpio[] = {
+ {EM28XX_R08_GPIO, 0xff, 0xff, 10},
+ {EM2874_R80_GPIO, 0xf6, 0xff, 100},
+ {EM2874_R80_GPIO, 0xf2, 0xff, 50},
+ {EM2874_R80_GPIO, 0xf6, 0xff, 50},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq terratec_h5_digital[] = {
+ {EM2874_R80_GPIO, 0xf6, 0xff, 10},
+ {EM2874_R80_GPIO, 0xe6, 0xff, 100},
+ {EM2874_R80_GPIO, 0xa6, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+#endif
+
+/* 2013:024f PCTV DVB-S2 Stick 460e
+ * GPIO_0 - POWER_ON
+ * GPIO_1 - BOOST
+ * GPIO_2 - VUV_LNB (red LED)
+ * GPIO_3 - EXT_12V
+ * GPIO_4 - INT_DEM (DEMOD GPIO_0)
+ * GPIO_5 - INT_LNB
+ * GPIO_6 - RESET_DEM
+ * GPIO_7 - LED (green LED)
+ */
+static struct em28xx_reg_seq pctv_460e[] = {
+ {EM2874_R80_GPIO, 0x01, 0xff, 50},
+ {0x0d, 0xff, 0xff, 50},
+ {EM2874_R80_GPIO, 0x41, 0xff, 50}, /* GPIO_6=1 */
+ {0x0d, 0x42, 0xff, 50},
+ {EM2874_R80_GPIO, 0x61, 0xff, 50}, /* GPIO_5=1 */
+ { -1, -1, -1, -1},
+};
+
+#if 0
+static struct em28xx_reg_seq hauppauge_930c_gpio[] = {
+ {EM2874_R80_GPIO, 0x6f, 0xff, 10},
+ {EM2874_R80_GPIO, 0x4f, 0xff, 10}, /* xc5000 reset */
+ {EM2874_R80_GPIO, 0x6f, 0xff, 10},
+ {EM2874_R80_GPIO, 0x4f, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq hauppauge_930c_digital[] = {
+ {EM2874_R80_GPIO, 0xf6, 0xff, 10},
+ {EM2874_R80_GPIO, 0xe6, 0xff, 100},
+ {EM2874_R80_GPIO, 0xa6, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+#endif
+
+/* 1b80:e425 MaxMedia UB425-TC
+ * GPIO_6 - demod reset, 0=active
+ * GPIO_7 - LED, 0=active
+ */
+static struct em28xx_reg_seq maxmedia_ub425_tc[] = {
+ {EM2874_R80_GPIO, 0x83, 0xff, 100},
+ {EM2874_R80_GPIO, 0xc3, 0xff, 100}, /* GPIO_6 = 1 */
+ {EM2874_R80_GPIO, 0x43, 0xff, 000}, /* GPIO_7 = 0 */
+ {-1, -1, -1, -1},
+};
+
+/* 2304:0242 PCTV QuatroStick (510e)
+ * GPIO_2: decoder reset, 0=active
+ * GPIO_4: decoder suspend, 0=active
+ * GPIO_6: demod reset, 0=active
+ * GPIO_7: LED, 1=active
+ */
+static struct em28xx_reg_seq pctv_510e[] = {
+ {EM2874_R80_GPIO, 0x10, 0xff, 100},
+ {EM2874_R80_GPIO, 0x14, 0xff, 100}, /* GPIO_2 = 1 */
+ {EM2874_R80_GPIO, 0x54, 0xff, 050}, /* GPIO_6 = 1 */
+ { -1, -1, -1, -1},
+};
+
+/* 2013:0251 PCTV QuatroStick nano (520e)
+ * GPIO_2: decoder reset, 0=active
+ * GPIO_4: decoder suspend, 0=active
+ * GPIO_6: demod reset, 0=active
+ * GPIO_7: LED, 1=active
+ */
+static struct em28xx_reg_seq pctv_520e[] = {
+ {EM2874_R80_GPIO, 0x10, 0xff, 100},
+ {EM2874_R80_GPIO, 0x14, 0xff, 100}, /* GPIO_2 = 1 */
+ {EM2874_R80_GPIO, 0x54, 0xff, 050}, /* GPIO_6 = 1 */
+ {EM2874_R80_GPIO, 0xd4, 0xff, 000}, /* GPIO_7 = 1 */
+ { -1, -1, -1, -1},
+};
+
+/*
+ * Board definitions
+ */
+struct em28xx_board em28xx_boards[] = {
+ [EM2750_BOARD_UNKNOWN] = {
+ .name = "EM2710/EM2750/EM2751 webcam grabber",
+ .xclk = EM28XX_XCLK_FREQUENCY_20MHZ,
+ .tuner_type = TUNER_ABSENT,
+ .is_webcam = 1,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = silvercrest_reg_seq,
+ } },
+ },
+ [EM2800_BOARD_UNKNOWN] = {
+ .name = "Unknown EM2800 video grabber",
+ .is_em2800 = 1,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .tuner_type = TUNER_ABSENT,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2820_BOARD_UNKNOWN] = {
+ .name = "Unknown EM2750/28xx video grabber",
+ .tuner_type = TUNER_ABSENT,
+ .is_webcam = 1, /* To enable sensor probe */
+ },
+ [EM2750_BOARD_DLCW_130] = {
+ /* Beijing Huaqi Information Digital Technology Co., Ltd */
+ .name = "Huaqi DLCW-130",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .xclk = EM28XX_XCLK_FREQUENCY_48MHZ,
+ .tuner_type = TUNER_ABSENT,
+ .is_webcam = 1,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ } },
+ },
+ [EM2820_BOARD_KWORLD_PVRTV2800RF] = {
+ .name = "Kworld PVR TV 2800 RF",
+ .tuner_type = TUNER_TEMIC_PAL,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2820_BOARD_GADMEI_TVR200] = {
+ .name = "Gadmei TVR200",
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2820_BOARD_TERRATEC_CINERGY_250] = {
+ .name = "Terratec Cinergy 250 USB",
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .has_ir_i2c = 1,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2820_BOARD_PINNACLE_USB_2] = {
+ .name = "Pinnacle PCTV USB 2",
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .has_ir_i2c = 1,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2820_BOARD_HAUPPAUGE_WINTV_USB_2] = {
+ .name = "Hauppauge WinTV USB 2",
+ .tuner_type = TUNER_PHILIPS_FM1236_MK3,
+ .tda9887_conf = TDA9887_PRESENT |
+ TDA9887_PORT1_ACTIVE |
+ TDA9887_PORT2_ACTIVE,
+ .decoder = EM28XX_TVP5150,
+ .has_msp34xx = 1,
+ .has_ir_i2c = 1,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = MSP_INPUT_DEFAULT,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1,
+ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART),
+ } },
+ },
+ [EM2820_BOARD_DLINK_USB_TV] = {
+ .name = "D-Link DUB-T210 TV Tuner",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2820_BOARD_HERCULES_SMART_TV_USB2] = {
+ .name = "Hercules Smart TV USB 2.0",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2820_BOARD_PINNACLE_USB_2_FM1216ME] = {
+ .name = "Pinnacle PCTV USB 2 (Philips FM1216ME)",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2820_BOARD_GADMEI_UTV310] = {
+ .name = "Gadmei UTV310",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_TNF_5335MF,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE] = {
+ .name = "Leadtek Winfast USB II Deluxe",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .has_ir_i2c = 1,
+ .tvaudio_addr = 0x58,
+ .tda9887_conf = TDA9887_PRESENT |
+ TDA9887_PORT2_ACTIVE |
+ TDA9887_QSS,
+ .decoder = EM28XX_SAA711X,
+ .adecoder = EM28XX_TVAUDIO,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE4,
+ .amux = EM28XX_AMUX_AUX,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE5,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ .radio = {
+ .type = EM28XX_RADIO,
+ .amux = EM28XX_AMUX_AUX,
+ }
+ },
+ [EM2820_BOARD_VIDEOLOGY_20K14XUSB] = {
+ .name = "Videology 20K14XUSB USB2.0",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_ABSENT,
+ .is_webcam = 1,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ } },
+ },
+ [EM2820_BOARD_SILVERCREST_WEBCAM] = {
+ .name = "Silvercrest Webcam 1.3mpix",
+ .tuner_type = TUNER_ABSENT,
+ .is_webcam = 1,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = silvercrest_reg_seq,
+ } },
+ },
+ [EM2821_BOARD_SUPERCOMP_USB_2] = {
+ .name = "Supercomp USB 2.0 TV",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_PHILIPS_FM1236_MK3,
+ .tda9887_conf = TDA9887_PRESENT |
+ TDA9887_PORT1_ACTIVE |
+ TDA9887_PORT2_ACTIVE,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2821_BOARD_USBGEAR_VD204] = {
+ .name = "Usbgear VD204v9",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_ABSENT, /* Capture only device */
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2860_BOARD_NETGMBH_CAM] = {
+ /* Beijing Huaqi Information Digital Technology Co., Ltd */
+ .name = "NetGMBH Cam",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_ABSENT,
+ .is_webcam = 1,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ } },
+ },
+ [EM2860_BOARD_TYPHOON_DVD_MAKER] = {
+ .name = "Typhoon DVD Maker",
+ .decoder = EM28XX_SAA711X,
+ .tuner_type = TUNER_ABSENT, /* Capture only device */
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2860_BOARD_GADMEI_UTV330] = {
+ .name = "Gadmei UTV330",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_TNF_5335MF,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2861_BOARD_GADMEI_UTV330PLUS] = {
+ .name = "Gadmei UTV330+",
+ .tuner_type = TUNER_TNF_5335MF,
+ .tda9887_conf = TDA9887_PRESENT,
+ .ir_codes = RC_MAP_GADMEI_RM008Z,
+ .decoder = EM28XX_SAA711X,
+ .xclk = EM28XX_XCLK_FREQUENCY_12MHZ,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2860_BOARD_TERRATEC_HYBRID_XS] = {
+ .name = "Terratec Cinergy A Hybrid XS",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ } },
+ },
+ [EM2861_BOARD_KWORLD_PVRTV_300U] = {
+ .name = "KWorld PVRTV 300U",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2861_BOARD_YAKUMO_MOVIE_MIXER] = {
+ .name = "Yakumo MovieMixer",
+ .tuner_type = TUNER_ABSENT, /* Capture only device */
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2860_BOARD_TVP5150_REFERENCE_DESIGN] = {
+ .name = "EM2860/TVP5150 Reference Design",
+ .tuner_type = TUNER_ABSENT, /* Capture only device */
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2861_BOARD_PLEXTOR_PX_TV100U] = {
+ .name = "Plextor ConvertX PX-TV100U",
+ .tuner_type = TUNER_TNF_5335MF,
+ .xclk = EM28XX_XCLK_I2S_MSB_TIMING |
+ EM28XX_XCLK_FREQUENCY_12MHZ,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_TVP5150,
+ .has_msp34xx = 1,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = pinnacle_hybrid_pro_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = pinnacle_hybrid_pro_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = pinnacle_hybrid_pro_analog,
+ } },
+ },
+
+ /* Those boards with em2870 are DVB Only*/
+
+ [EM2870_BOARD_TERRATEC_XS] = {
+ .name = "Terratec Cinergy T XS",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ },
+ [EM2870_BOARD_TERRATEC_XS_MT2060] = {
+ .name = "Terratec Cinergy T XS (MT2060)",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_ABSENT, /* MT2060 */
+ },
+ [EM2870_BOARD_KWORLD_350U] = {
+ .name = "Kworld 350 U DVB-T",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ },
+ [EM2870_BOARD_KWORLD_355U] = {
+ .name = "Kworld 355 U DVB-T",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_gpio = default_tuner_gpio,
+ .has_dvb = 1,
+ .dvb_gpio = default_digital,
+ },
+ [EM2870_BOARD_PINNACLE_PCTV_DVB] = {
+ .name = "Pinnacle PCTV DVB-T",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_ABSENT, /* MT2060 */
+ /* djh - I have serious doubts this is right... */
+ .xclk = EM28XX_XCLK_IR_RC5_MODE |
+ EM28XX_XCLK_FREQUENCY_10MHZ,
+ },
+ [EM2870_BOARD_COMPRO_VIDEOMATE] = {
+ .name = "Compro, VideoMate U3",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_ABSENT, /* MT2060 */
+ },
+
+ [EM2880_BOARD_TERRATEC_HYBRID_XS_FR] = {
+ .name = "Terratec Hybrid XS Secam",
+ .has_msp34xx = 1,
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+ .has_dvb = 1,
+ .dvb_gpio = terratec_cinergy_USB_XS_FR_digital,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = terratec_cinergy_USB_XS_FR_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = terratec_cinergy_USB_XS_FR_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = terratec_cinergy_USB_XS_FR_analog,
+ } },
+ },
+ [EM2884_BOARD_TERRATEC_H5] = {
+ .name = "Terratec Cinergy H5",
+ .has_dvb = 1,
+#if 0
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .tuner_addr = 0x41,
+ .dvb_gpio = terratec_h5_digital, /* FIXME: probably wrong */
+ .tuner_gpio = terratec_h5_gpio,
+#else
+ .tuner_type = TUNER_ABSENT,
+#endif
+ .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
+ EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_400_KHZ,
+ },
+ [EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C] = {
+ .name = "Hauppauge WinTV HVR 930C",
+ .has_dvb = 1,
+#if 0 /* FIXME: Add analog support */
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0x41,
+ .dvb_gpio = hauppauge_930c_digital,
+ .tuner_gpio = hauppauge_930c_gpio,
+#else
+ .tuner_type = TUNER_ABSENT,
+#endif
+ .ir_codes = RC_MAP_HAUPPAUGE,
+ .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
+ EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_400_KHZ,
+ },
+ [EM2884_BOARD_CINERGY_HTC_STICK] = {
+ .name = "Terratec Cinergy HTC Stick",
+ .has_dvb = 1,
+#if 0
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .tuner_addr = 0x41,
+ .dvb_gpio = terratec_h5_digital, /* FIXME: probably wrong */
+ .tuner_gpio = terratec_h5_gpio,
+#endif
+ .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
+ EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_400_KHZ,
+ },
+ [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900] = {
+ .name = "Hauppauge WinTV HVR 900",
+ .tda9887_conf = TDA9887_PRESENT,
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = hauppauge_wintv_hvr_900_digital,
+ .ir_codes = RC_MAP_HAUPPAUGE,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ } },
+ },
+ [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2] = {
+ .name = "Hauppauge WinTV HVR 900 (R2)",
+ .tda9887_conf = TDA9887_PRESENT,
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = hauppauge_wintv_hvr_900R2_digital,
+ .ir_codes = RC_MAP_HAUPPAUGE,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ } },
+ },
+ [EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850] = {
+ .name = "Hauppauge WinTV HVR 850",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = hauppauge_wintv_hvr_900_digital,
+ .ir_codes = RC_MAP_HAUPPAUGE,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ } },
+ },
+ [EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950] = {
+ .name = "Hauppauge WinTV HVR 950",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = hauppauge_wintv_hvr_900_digital,
+ .ir_codes = RC_MAP_HAUPPAUGE,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ } },
+ },
+ [EM2880_BOARD_PINNACLE_PCTV_HD_PRO] = {
+ .name = "Pinnacle PCTV HD Pro Stick",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = hauppauge_wintv_hvr_900_digital,
+ .ir_codes = RC_MAP_PINNACLE_PCTV_HD,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ } },
+ },
+ [EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600] = {
+ .name = "AMD ATI TV Wonder HD 600",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = hauppauge_wintv_hvr_900_digital,
+ .ir_codes = RC_MAP_ATI_TV_WONDER_HD_600,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ } },
+ },
+ [EM2880_BOARD_TERRATEC_HYBRID_XS] = {
+ .name = "Terratec Hybrid XS",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+ .has_dvb = 1,
+ .dvb_gpio = default_digital,
+ .ir_codes = RC_MAP_TERRATEC_CINERGY_XS,
+ .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = default_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
+ } },
+ },
+ /* maybe there's a reason behind it why Terratec sells the Hybrid XS
+ as Prodigy XS with a different PID, let's keep it separated for now
+ maybe we'll need it lateron */
+ [EM2880_BOARD_TERRATEC_PRODIGY_XS] = {
+ .name = "Terratec Prodigy XS",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ } },
+ },
+ [EM2820_BOARD_MSI_VOX_USB_2] = {
+ .name = "MSI VOX USB 2.0",
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .tda9887_conf = TDA9887_PRESENT |
+ TDA9887_PORT1_ACTIVE |
+ TDA9887_PORT2_ACTIVE,
+ .max_range_640_480 = 1,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE4,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2800_BOARD_TERRATEC_CINERGY_200] = {
+ .name = "Terratec Cinergy 200 USB",
+ .is_em2800 = 1,
+ .has_ir_i2c = 1,
+ .tuner_type = TUNER_LG_TALN,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2800_BOARD_GRABBEEX_USB2800] = {
+ .name = "eMPIA Technology, Inc. GrabBeeX+ Video Encoder",
+ .is_em2800 = 1,
+ .decoder = EM28XX_SAA711X,
+ .tuner_type = TUNER_ABSENT, /* capture only board */
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2800_BOARD_VC211A] = {
+ .name = "Actionmaster/LinXcel/Digitus VC211A",
+ .is_em2800 = 1,
+ .tuner_type = TUNER_ABSENT, /* Capture-only board */
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = vc211a_enable,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = vc211a_enable,
+ } },
+ },
+ [EM2800_BOARD_LEADTEK_WINFAST_USBII] = {
+ .name = "Leadtek Winfast USB II",
+ .is_em2800 = 1,
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2800_BOARD_KWORLD_USB2800] = {
+ .name = "Kworld USB2800",
+ .is_em2800 = 1,
+ .tuner_type = TUNER_PHILIPS_FCV1236D,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2820_BOARD_PINNACLE_DVC_90] = {
+ .name = "Pinnacle Dazzle DVC 90/100/101/107 / Kaiser Baas Video to DVD maker "
+ "/ Kworld DVD Maker 2 / Plextor ConvertX PX-AV100U",
+ .tuner_type = TUNER_ABSENT, /* capture only board */
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2800_BOARD_VGEAR_POCKETTV] = {
+ .name = "V-Gear PocketTV",
+ .is_em2800 = 1,
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2] = {
+ .name = "Pixelview PlayTV Box 4 USB 2.0",
+ .tda9887_conf = TDA9887_PRESENT,
+ .tuner_type = TUNER_YMEC_TVF_5533MF,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_VIDEO,
+ .aout = EM28XX_AOUT_MONO | /* I2S */
+ EM28XX_AOUT_MASTER, /* Line out pin */
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2820_BOARD_PROLINK_PLAYTV_USB2] = {
+ .name = "SIIG AVTuner-PVR / Pixelview Prolink PlayTV USB 2.0",
+ .has_snapshot_button = 1,
+ .tda9887_conf = TDA9887_PRESENT,
+ .tuner_type = TUNER_YMEC_TVF_5533MF,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_VIDEO,
+ .aout = EM28XX_AOUT_MONO | /* I2S */
+ EM28XX_AOUT_MASTER, /* Line out pin */
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2860_BOARD_SAA711X_REFERENCE_DESIGN] = {
+ .name = "EM2860/SAA711X Reference Design",
+ .has_snapshot_button = 1,
+ .tuner_type = TUNER_ABSENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ } },
+ },
+
+ [EM2874_BOARD_LEADERSHIP_ISDBT] = {
+ .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
+ EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_100_KHZ,
+ .xclk = EM28XX_XCLK_FREQUENCY_10MHZ,
+ .name = "EM2874 Leadership ISDBT",
+ .tuner_type = TUNER_ABSENT,
+ .tuner_gpio = leadership_reset,
+ .dvb_gpio = leadership_digital,
+ .has_dvb = 1,
+ },
+
+ [EM2880_BOARD_MSI_DIGIVOX_AD] = {
+ .name = "MSI DigiVox A/D",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = em2880_msi_digivox_ad_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = em2880_msi_digivox_ad_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = em2880_msi_digivox_ad_analog,
+ } },
+ },
+ [EM2880_BOARD_MSI_DIGIVOX_AD_II] = {
+ .name = "MSI DigiVox A/D II",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = em2880_msi_digivox_ad_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = em2880_msi_digivox_ad_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = em2880_msi_digivox_ad_analog,
+ } },
+ },
+ [EM2880_BOARD_KWORLD_DVB_305U] = {
+ .name = "KWorld DVB-T 305U",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2880_BOARD_KWORLD_DVB_310U] = {
+ .name = "KWorld DVB-T 310U",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .has_dvb = 1,
+ .dvb_gpio = default_digital,
+ .mts_firmware = 1,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = default_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
+ }, { /* S-video has not been tested yet */
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
+ } },
+ },
+ [EM2882_BOARD_KWORLD_ATSC_315U] = {
+ .name = "KWorld ATSC 315U HDTV TV Box",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_THOMSON_DTT761X,
+ .tuner_gpio = em2882_kworld_315u_tuner_gpio,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .has_dvb = 1,
+ .dvb_gpio = em2882_kworld_315u_digital,
+ .ir_codes = RC_MAP_KWORLD_315U,
+ .xclk = EM28XX_XCLK_FREQUENCY_12MHZ,
+ .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE,
+ /* Analog mode - still not ready */
+ /*.input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = em2882_kworld_315u_analog,
+ .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = em2882_kworld_315u_analog1,
+ .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = em2882_kworld_315u_analog1,
+ .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO,
+ } }, */
+ },
+ [EM2880_BOARD_EMPIRE_DUAL_TV] = {
+ .name = "Empire dual TV",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .has_dvb = 1,
+ .dvb_gpio = default_digital,
+ .mts_firmware = 1,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = default_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
+ } },
+ },
+ [EM2881_BOARD_DNT_DA2_HYBRID] = {
+ .name = "DNT DA2 Hybrid",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = default_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
+ } },
+ },
+ [EM2881_BOARD_PINNACLE_HYBRID_PRO] = {
+ .name = "Pinnacle Hybrid Pro",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+ .has_dvb = 1,
+ .dvb_gpio = pinnacle_hybrid_pro_digital,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = pinnacle_hybrid_pro_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = pinnacle_hybrid_pro_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = pinnacle_hybrid_pro_analog,
+ } },
+ },
+ [EM2882_BOARD_PINNACLE_HYBRID_PRO_330E] = {
+ .name = "Pinnacle Hybrid Pro (330e)",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = hauppauge_wintv_hvr_900R2_digital,
+ .ir_codes = RC_MAP_PINNACLE_PCTV_HD,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ } },
+ },
+ [EM2882_BOARD_KWORLD_VS_DVBT] = {
+ .name = "Kworld VS-DVB-T 323UR",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = kworld_330u_digital,
+ .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */
+ .ir_codes = RC_MAP_KWORLD_315U,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2882_BOARD_TERRATEC_HYBRID_XS] = {
+ .name = "Terratec Cinnergy Hybrid T USB XS (em2882)",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .mts_firmware = 1,
+ .decoder = EM28XX_TVP5150,
+ .has_dvb = 1,
+ .dvb_gpio = hauppauge_wintv_hvr_900_digital,
+ .ir_codes = RC_MAP_TERRATEC_CINERGY_XS,
+ .xclk = EM28XX_XCLK_FREQUENCY_12MHZ,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ } },
+ },
+ [EM2882_BOARD_DIKOM_DK300] = {
+ .name = "Dikom DK300",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = dikom_dk300_digital,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = default_analog,
+ } },
+ },
+ [EM2883_BOARD_KWORLD_HYBRID_330U] = {
+ .name = "Kworld PlusTV HD Hybrid 330",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = kworld_330u_digital,
+ .xclk = EM28XX_XCLK_FREQUENCY_12MHZ,
+ .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_EEPROM_ON_BOARD |
+ EM28XX_I2C_EEPROM_KEY_VALID,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = kworld_330u_analog,
+ .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = kworld_330u_analog,
+ .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = kworld_330u_analog,
+ } },
+ },
+ [EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU] = {
+ .name = "Compro VideoMate ForYou/Stereo",
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .tvaudio_addr = 0xb0,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_TVP5150,
+ .adecoder = EM28XX_TVAUDIO,
+ .mute_gpio = compro_mute_gpio,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = compro_unmute_tv_gpio,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = compro_unmute_svid_gpio,
+ } },
+ },
+ [EM2860_BOARD_KAIOMY_TVNPC_U2] = {
+ .name = "Kaiomy TVnPC U2",
+ .vchannels = 3,
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0x61,
+ .mts_firmware = 1,
+ .decoder = EM28XX_TVP5150,
+ .tuner_gpio = default_tuner_gpio,
+ .ir_codes = RC_MAP_KAIOMY,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ .radio = {
+ .type = EM28XX_RADIO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }
+ },
+ [EM2860_BOARD_EASYCAP] = {
+ .name = "Easy Cap Capture DC-60",
+ .vchannels = 2,
+ .tuner_type = TUNER_ABSENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2820_BOARD_IODATA_GVMVP_SZ] = {
+ .name = "IO-DATA GV-MVP/SZ",
+ .tuner_type = TUNER_PHILIPS_FM1236_MK3,
+ .tuner_gpio = default_tuner_gpio,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, { /* Composite has not been tested yet */
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, { /* S-video has not been tested yet */
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_VIDEO,
+ } },
+ },
+ [EM2860_BOARD_TERRATEC_GRABBY] = {
+ .name = "Terratec Grabby",
+ .vchannels = 2,
+ .tuner_type = TUNER_ABSENT,
+ .decoder = EM28XX_SAA711X,
+ .xclk = EM28XX_XCLK_FREQUENCY_12MHZ,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2860_BOARD_TERRATEC_AV350] = {
+ .name = "Terratec AV350",
+ .vchannels = 2,
+ .tuner_type = TUNER_ABSENT,
+ .decoder = EM28XX_TVP5150,
+ .xclk = EM28XX_XCLK_FREQUENCY_12MHZ,
+ .mute_gpio = terratec_av350_mute_gpio,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AUDIO_SRC_LINE,
+ .gpio = terratec_av350_unmute_gpio,
+
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AUDIO_SRC_LINE,
+ .gpio = terratec_av350_unmute_gpio,
+ } },
+ },
+
+ [EM2860_BOARD_ELGATO_VIDEO_CAPTURE] = {
+ .name = "Elgato Video Capture",
+ .decoder = EM28XX_SAA711X,
+ .tuner_type = TUNER_ABSENT, /* Capture only device */
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+
+ [EM2882_BOARD_EVGA_INDTUBE] = {
+ .name = "Evga inDtube",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+ .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = evga_indtube_digital,
+ .ir_codes = RC_MAP_EVGA_INDTUBE,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = evga_indtube_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = evga_indtube_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = evga_indtube_analog,
+ } },
+ },
+ /* eb1a:2868 Empia EM2870 + Philips CU1216L NIM (Philips TDA10023 +
+ Infineon TUA6034) */
+ [EM2870_BOARD_REDDO_DVB_C_USB_BOX] = {
+ .name = "Reddo DVB-C USB TV Box",
+ .tuner_type = TUNER_ABSENT,
+ .tuner_gpio = reddo_dvb_c_usb_box,
+ .has_dvb = 1,
+ },
+ /* 1b80:a340 - Empia EM2870, NXP TDA18271HD and LG DT3304, sold
+ * initially as the KWorld PlusTV 340U, then as the UB435-Q.
+ * Early variants have a TDA18271HD/C1, later ones a TDA18271HD/C2 */
+ [EM2870_BOARD_KWORLD_A340] = {
+ .name = "KWorld PlusTV 340U or UB435-Q (ATSC)",
+ .tuner_type = TUNER_ABSENT, /* Digital-only TDA18271HD */
+ .has_dvb = 1,
+ .dvb_gpio = kworld_a340_digital,
+ .tuner_gpio = default_tuner_gpio,
+ },
+ /* 2013:024f PCTV nanoStick T2 290e.
+ * Empia EM28174, Sony CXD2820R and NXP TDA18271HD/C2 */
+ [EM28174_BOARD_PCTV_290E] = {
+ .name = "PCTV nanoStick T2 290e",
+ .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
+ EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_gpio = pctv_290e,
+ .has_dvb = 1,
+ .ir_codes = RC_MAP_PINNACLE_PCTV_HD,
+ },
+ /* 2013:024f PCTV DVB-S2 Stick 460e
+ * Empia EM28174, NXP TDA10071, Conexant CX24118A and Allegro A8293 */
+ [EM28174_BOARD_PCTV_460E] = {
+ .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
+ EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ,
+ .name = "PCTV DVB-S2 Stick (460e)",
+ .tuner_type = TUNER_ABSENT,
+ .tuner_gpio = pctv_460e,
+ .has_dvb = 1,
+ .ir_codes = RC_MAP_PINNACLE_PCTV_HD,
+ },
+ /* eb1a:5006 Honestech VIDBOX NW03
+ * Empia EM2860, Philips SAA7113, Empia EMP202, No Tuner */
+ [EM2860_BOARD_HT_VIDBOX_NW03] = {
+ .name = "Honestech Vidbox NW03",
+ .tuner_type = TUNER_ABSENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3, /* S-VIDEO needs confirming */
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ /* 1b80:e425 MaxMedia UB425-TC
+ * Empia EM2874B + Micronas DRX 3913KA2 + NXP TDA18271HDC2 */
+ [EM2874_BOARD_MAXMEDIA_UB425_TC] = {
+ .name = "MaxMedia UB425-TC",
+ .tuner_type = TUNER_ABSENT,
+ .tuner_gpio = maxmedia_ub425_tc,
+ .has_dvb = 1,
+ .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
+ EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_400_KHZ,
+ },
+ /* 2304:0242 PCTV QuatroStick (510e)
+ * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2 */
+ [EM2884_BOARD_PCTV_510E] = {
+ .name = "PCTV QuatroStick (510e)",
+ .tuner_type = TUNER_ABSENT,
+ .tuner_gpio = pctv_510e,
+ .has_dvb = 1,
+ .ir_codes = RC_MAP_PINNACLE_PCTV_HD,
+ .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
+ EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_400_KHZ,
+ },
+ /* 2013:0251 PCTV QuatroStick nano (520e)
+ * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2 */
+ [EM2884_BOARD_PCTV_520E] = {
+ .name = "PCTV QuatroStick nano (520e)",
+ .tuner_type = TUNER_ABSENT,
+ .tuner_gpio = pctv_520e,
+ .has_dvb = 1,
+ .ir_codes = RC_MAP_PINNACLE_PCTV_HD,
+ .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
+ EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_400_KHZ,
+ },
+};
+const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
+
+/* table of devices that work with this driver */
+struct usb_device_id em28xx_id_table[] = {
+ { USB_DEVICE(0xeb1a, 0x2750),
+ .driver_info = EM2750_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2751),
+ .driver_info = EM2750_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2800),
+ .driver_info = EM2800_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2710),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2820),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2821),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2860),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2861),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2862),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2863),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2870),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2881),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2883),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2868),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2875),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0xe300),
+ .driver_info = EM2861_BOARD_KWORLD_PVRTV_300U },
+ { USB_DEVICE(0xeb1a, 0xe303),
+ .driver_info = EM2860_BOARD_KAIOMY_TVNPC_U2 },
+ { USB_DEVICE(0xeb1a, 0xe305),
+ .driver_info = EM2880_BOARD_KWORLD_DVB_305U },
+ { USB_DEVICE(0xeb1a, 0xe310),
+ .driver_info = EM2880_BOARD_MSI_DIGIVOX_AD },
+ { USB_DEVICE(0xeb1a, 0xa313),
+ .driver_info = EM2882_BOARD_KWORLD_ATSC_315U },
+ { USB_DEVICE(0xeb1a, 0xa316),
+ .driver_info = EM2883_BOARD_KWORLD_HYBRID_330U },
+ { USB_DEVICE(0xeb1a, 0xe320),
+ .driver_info = EM2880_BOARD_MSI_DIGIVOX_AD_II },
+ { USB_DEVICE(0xeb1a, 0xe323),
+ .driver_info = EM2882_BOARD_KWORLD_VS_DVBT },
+ { USB_DEVICE(0xeb1a, 0xe350),
+ .driver_info = EM2870_BOARD_KWORLD_350U },
+ { USB_DEVICE(0xeb1a, 0xe355),
+ .driver_info = EM2870_BOARD_KWORLD_355U },
+ { USB_DEVICE(0xeb1a, 0x2801),
+ .driver_info = EM2800_BOARD_GRABBEEX_USB2800 },
+ { USB_DEVICE(0xeb1a, 0xe357),
+ .driver_info = EM2870_BOARD_KWORLD_355U },
+ { USB_DEVICE(0xeb1a, 0xe359),
+ .driver_info = EM2870_BOARD_KWORLD_355U },
+ { USB_DEVICE(0x1b80, 0xe302),
+ .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kaiser Baas Video to DVD maker */
+ { USB_DEVICE(0x1b80, 0xe304),
+ .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kworld DVD Maker 2 */
+ { USB_DEVICE(0x0ccd, 0x0036),
+ .driver_info = EM2820_BOARD_TERRATEC_CINERGY_250 },
+ { USB_DEVICE(0x0ccd, 0x004c),
+ .driver_info = EM2880_BOARD_TERRATEC_HYBRID_XS_FR },
+ { USB_DEVICE(0x0ccd, 0x004f),
+ .driver_info = EM2860_BOARD_TERRATEC_HYBRID_XS },
+ { USB_DEVICE(0x0ccd, 0x005e),
+ .driver_info = EM2882_BOARD_TERRATEC_HYBRID_XS },
+ { USB_DEVICE(0x0ccd, 0x0042),
+ .driver_info = EM2882_BOARD_TERRATEC_HYBRID_XS },
+ { USB_DEVICE(0x0ccd, 0x0043),
+ .driver_info = EM2870_BOARD_TERRATEC_XS },
+ { USB_DEVICE(0x0ccd, 0x008e), /* Cinergy HTC USB XS Rev. 1 */
+ .driver_info = EM2884_BOARD_TERRATEC_H5 },
+ { USB_DEVICE(0x0ccd, 0x00ac), /* Cinergy HTC USB XS Rev. 2 */
+ .driver_info = EM2884_BOARD_TERRATEC_H5 },
+ { USB_DEVICE(0x0ccd, 0x10a2), /* H5 Rev. 1 */
+ .driver_info = EM2884_BOARD_TERRATEC_H5 },
+ { USB_DEVICE(0x0ccd, 0x10ad), /* H5 Rev. 2 */
+ .driver_info = EM2884_BOARD_TERRATEC_H5 },
+ { USB_DEVICE(0x0ccd, 0x0084),
+ .driver_info = EM2860_BOARD_TERRATEC_AV350 },
+ { USB_DEVICE(0x0ccd, 0x0096),
+ .driver_info = EM2860_BOARD_TERRATEC_GRABBY },
+ { USB_DEVICE(0x0ccd, 0x10AF),
+ .driver_info = EM2860_BOARD_TERRATEC_GRABBY },
+ { USB_DEVICE(0x0ccd, 0x00b2),
+ .driver_info = EM2884_BOARD_CINERGY_HTC_STICK },
+ { USB_DEVICE(0x0fd9, 0x0033),
+ .driver_info = EM2860_BOARD_ELGATO_VIDEO_CAPTURE},
+ { USB_DEVICE(0x185b, 0x2870),
+ .driver_info = EM2870_BOARD_COMPRO_VIDEOMATE },
+ { USB_DEVICE(0x185b, 0x2041),
+ .driver_info = EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU },
+ { USB_DEVICE(0x2040, 0x4200),
+ .driver_info = EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 },
+ { USB_DEVICE(0x2040, 0x4201),
+ .driver_info = EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 },
+ { USB_DEVICE(0x2040, 0x6500),
+ .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 },
+ { USB_DEVICE(0x2040, 0x6502),
+ .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2 },
+ { USB_DEVICE(0x2040, 0x6513), /* HCW HVR-980 */
+ .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 },
+ { USB_DEVICE(0x2040, 0x6517), /* HP HVR-950 */
+ .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 },
+ { USB_DEVICE(0x2040, 0x651b), /* RP HVR-950 */
+ .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 },
+ { USB_DEVICE(0x2040, 0x651f),
+ .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 },
+ { USB_DEVICE(0x0438, 0xb002),
+ .driver_info = EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 },
+ { USB_DEVICE(0x2001, 0xf112),
+ .driver_info = EM2820_BOARD_DLINK_USB_TV },
+ { USB_DEVICE(0x2304, 0x0207),
+ .driver_info = EM2820_BOARD_PINNACLE_DVC_90 },
+ { USB_DEVICE(0x2304, 0x0208),
+ .driver_info = EM2820_BOARD_PINNACLE_USB_2 },
+ { USB_DEVICE(0x2304, 0x021a),
+ .driver_info = EM2820_BOARD_PINNACLE_DVC_90 },
+ { USB_DEVICE(0x2304, 0x0226),
+ .driver_info = EM2882_BOARD_PINNACLE_HYBRID_PRO_330E },
+ { USB_DEVICE(0x2304, 0x0227),
+ .driver_info = EM2880_BOARD_PINNACLE_PCTV_HD_PRO },
+ { USB_DEVICE(0x0413, 0x6023),
+ .driver_info = EM2800_BOARD_LEADTEK_WINFAST_USBII },
+ { USB_DEVICE(0x093b, 0xa003),
+ .driver_info = EM2820_BOARD_PINNACLE_DVC_90 },
+ { USB_DEVICE(0x093b, 0xa005),
+ .driver_info = EM2861_BOARD_PLEXTOR_PX_TV100U },
+ { USB_DEVICE(0x04bb, 0x0515),
+ .driver_info = EM2820_BOARD_IODATA_GVMVP_SZ },
+ { USB_DEVICE(0xeb1a, 0x50a6),
+ .driver_info = EM2860_BOARD_GADMEI_UTV330 },
+ { USB_DEVICE(0x1b80, 0xa340),
+ .driver_info = EM2870_BOARD_KWORLD_A340 },
+ { USB_DEVICE(0x2013, 0x024f),
+ .driver_info = EM28174_BOARD_PCTV_290E },
+ { USB_DEVICE(0x2013, 0x024c),
+ .driver_info = EM28174_BOARD_PCTV_460E },
+ { USB_DEVICE(0x2040, 0x1605),
+ .driver_info = EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C },
+ { USB_DEVICE(0xeb1a, 0x5006),
+ .driver_info = EM2860_BOARD_HT_VIDBOX_NW03 },
+ { USB_DEVICE(0x1b80, 0xe309), /* Sveon STV40 */
+ .driver_info = EM2860_BOARD_EASYCAP },
+ { USB_DEVICE(0x1b80, 0xe425),
+ .driver_info = EM2874_BOARD_MAXMEDIA_UB425_TC },
+ { USB_DEVICE(0x2304, 0x0242),
+ .driver_info = EM2884_BOARD_PCTV_510E },
+ { USB_DEVICE(0x2013, 0x0251),
+ .driver_info = EM2884_BOARD_PCTV_520E },
+ { },
+};
+MODULE_DEVICE_TABLE(usb, em28xx_id_table);
+
+/*
+ * EEPROM hash table for devices with generic USB IDs
+ */
+static struct em28xx_hash_table em28xx_eeprom_hash[] = {
+ /* P/N: SA 60002070465 Tuner: TVF7533-MF */
+ {0x6ce05a8f, EM2820_BOARD_PROLINK_PLAYTV_USB2, TUNER_YMEC_TVF_5533MF},
+ {0x72cc5a8b, EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2, TUNER_YMEC_TVF_5533MF},
+ {0x966a0441, EM2880_BOARD_KWORLD_DVB_310U, TUNER_XC2028},
+ {0x166a0441, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028},
+ {0xcee44a99, EM2882_BOARD_EVGA_INDTUBE, TUNER_XC2028},
+ {0xb8846b20, EM2881_BOARD_PINNACLE_HYBRID_PRO, TUNER_XC2028},
+ {0x63f653bd, EM2870_BOARD_REDDO_DVB_C_USB_BOX, TUNER_ABSENT},
+ {0x4e913442, EM2882_BOARD_DIKOM_DK300, TUNER_XC2028},
+};
+
+/* I2C devicelist hash table for devices with generic USB IDs */
+static struct em28xx_hash_table em28xx_i2c_hash[] = {
+ {0xb06a32c3, EM2800_BOARD_TERRATEC_CINERGY_200, TUNER_LG_PAL_NEW_TAPC},
+ {0xf51200e3, EM2800_BOARD_VGEAR_POCKETTV, TUNER_LG_PAL_NEW_TAPC},
+ {0x1ba50080, EM2860_BOARD_SAA711X_REFERENCE_DESIGN, TUNER_ABSENT},
+ {0x77800080, EM2860_BOARD_TVP5150_REFERENCE_DESIGN, TUNER_ABSENT},
+ {0xc51200e3, EM2820_BOARD_GADMEI_TVR200, TUNER_LG_PAL_NEW_TAPC},
+ {0x4ba50080, EM2861_BOARD_GADMEI_UTV330PLUS, TUNER_TNF_5335MF},
+ {0x6b800080, EM2874_BOARD_LEADERSHIP_ISDBT, TUNER_ABSENT},
+};
+
+/* I2C possible address to saa7115, tvp5150, msp3400, tvaudio */
+static unsigned short saa711x_addrs[] = {
+ 0x4a >> 1, 0x48 >> 1, /* SAA7111, SAA7111A and SAA7113 */
+ 0x42 >> 1, 0x40 >> 1, /* SAA7114, SAA7115 and SAA7118 */
+ I2C_CLIENT_END };
+
+static unsigned short tvp5150_addrs[] = {
+ 0xb8 >> 1,
+ 0xba >> 1,
+ I2C_CLIENT_END
+};
+
+static unsigned short msp3400_addrs[] = {
+ 0x80 >> 1,
+ 0x88 >> 1,
+ I2C_CLIENT_END
+};
+
+int em28xx_tuner_callback(void *ptr, int component, int command, int arg)
+{
+ int rc = 0;
+ struct em28xx *dev = ptr;
+
+ if (dev->tuner_type != TUNER_XC2028 && dev->tuner_type != TUNER_XC5000)
+ return 0;
+
+ if (command != XC2028_TUNER_RESET && command != XC5000_TUNER_RESET)
+ return 0;
+
+ rc = em28xx_gpio_set(dev, dev->board.tuner_gpio);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(em28xx_tuner_callback);
+
+static inline void em28xx_set_model(struct em28xx *dev)
+{
+ memcpy(&dev->board, &em28xx_boards[dev->model], sizeof(dev->board));
+
+ /* Those are the default values for the majority of boards
+ Use those values if not specified otherwise at boards entry
+ */
+ if (!dev->board.xclk)
+ dev->board.xclk = EM28XX_XCLK_IR_RC5_MODE |
+ EM28XX_XCLK_FREQUENCY_12MHZ;
+
+ if (!dev->board.i2c_speed)
+ dev->board.i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_100_KHZ;
+}
+
+
+/* FIXME: Should be replaced by a proper mt9m111 driver */
+static int em28xx_initialize_mt9m111(struct em28xx *dev)
+{
+ int i;
+ unsigned char regs[][3] = {
+ { 0x0d, 0x00, 0x01, }, /* reset and use defaults */
+ { 0x0d, 0x00, 0x00, },
+ { 0x0a, 0x00, 0x21, },
+ { 0x21, 0x04, 0x00, }, /* full readout speed, no row/col skipping */
+ };
+
+ for (i = 0; i < ARRAY_SIZE(regs); i++)
+ i2c_master_send(&dev->i2c_client, &regs[i][0], 3);
+
+ return 0;
+}
+
+
+/* FIXME: Should be replaced by a proper mt9m001 driver */
+static int em28xx_initialize_mt9m001(struct em28xx *dev)
+{
+ int i;
+ unsigned char regs[][3] = {
+ { 0x0d, 0x00, 0x01, },
+ { 0x0d, 0x00, 0x00, },
+ { 0x04, 0x05, 0x00, }, /* hres = 1280 */
+ { 0x03, 0x04, 0x00, }, /* vres = 1024 */
+ { 0x20, 0x11, 0x00, },
+ { 0x06, 0x00, 0x10, },
+ { 0x2b, 0x00, 0x24, },
+ { 0x2e, 0x00, 0x24, },
+ { 0x35, 0x00, 0x24, },
+ { 0x2d, 0x00, 0x20, },
+ { 0x2c, 0x00, 0x20, },
+ { 0x09, 0x0a, 0xd4, },
+ { 0x35, 0x00, 0x57, },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(regs); i++)
+ i2c_master_send(&dev->i2c_client, &regs[i][0], 3);
+
+ return 0;
+}
+
+/* HINT method: webcam I2C chips
+ *
+ * This method works for webcams with Micron sensors
+ */
+static int em28xx_hint_sensor(struct em28xx *dev)
+{
+ int rc;
+ char *sensor_name;
+ unsigned char cmd;
+ __be16 version_be;
+ u16 version;
+
+ /* Micron sensor detection */
+ dev->i2c_client.addr = 0xba >> 1;
+ cmd = 0;
+ i2c_master_send(&dev->i2c_client, &cmd, 1);
+ rc = i2c_master_recv(&dev->i2c_client, (char *)&version_be, 2);
+ if (rc != 2)
+ return -EINVAL;
+
+ version = be16_to_cpu(version_be);
+ switch (version) {
+ case 0x8232: /* mt9v011 640x480 1.3 Mpix sensor */
+ case 0x8243: /* mt9v011 rev B 640x480 1.3 Mpix sensor */
+ dev->model = EM2820_BOARD_SILVERCREST_WEBCAM;
+ em28xx_set_model(dev);
+
+ sensor_name = "mt9v011";
+ dev->em28xx_sensor = EM28XX_MT9V011;
+ dev->sensor_xres = 640;
+ dev->sensor_yres = 480;
+ /*
+ * FIXME: mt9v011 uses I2S speed as xtal clk - at least with
+ * the Silvercrest cam I have here for testing - for higher
+ * resolutions, a high clock cause horizontal artifacts, so we
+ * need to use a lower xclk frequency.
+ * Yet, it would be possible to adjust xclk depending on the
+ * desired resolution, since this affects directly the
+ * frame rate.
+ */
+ dev->board.xclk = EM28XX_XCLK_FREQUENCY_4_3MHZ;
+ dev->sensor_xtal = 4300000;
+
+ /* probably means GRGB 16 bit bayer */
+ dev->vinmode = 0x0d;
+ dev->vinctl = 0x00;
+
+ break;
+
+ case 0x143a: /* MT9M111 as found in the ECS G200 */
+ dev->model = EM2750_BOARD_UNKNOWN;
+ em28xx_set_model(dev);
+
+ sensor_name = "mt9m111";
+ dev->board.xclk = EM28XX_XCLK_FREQUENCY_48MHZ;
+ dev->em28xx_sensor = EM28XX_MT9M111;
+ em28xx_initialize_mt9m111(dev);
+ dev->sensor_xres = 640;
+ dev->sensor_yres = 512;
+
+ dev->vinmode = 0x0a;
+ dev->vinctl = 0x00;
+
+ break;
+
+ case 0x8431:
+ dev->model = EM2750_BOARD_UNKNOWN;
+ em28xx_set_model(dev);
+
+ sensor_name = "mt9m001";
+ dev->em28xx_sensor = EM28XX_MT9M001;
+ em28xx_initialize_mt9m001(dev);
+ dev->sensor_xres = 1280;
+ dev->sensor_yres = 1024;
+
+ /* probably means BGGR 16 bit bayer */
+ dev->vinmode = 0x0c;
+ dev->vinctl = 0x00;
+
+ break;
+ default:
+ printk("Unknown Micron Sensor 0x%04x\n", version);
+ return -EINVAL;
+ }
+
+ /* Setup webcam defaults */
+ em28xx_pre_card_setup(dev);
+
+ em28xx_errdev("Sensor is %s, using model %s entry.\n",
+ sensor_name, em28xx_boards[dev->model].name);
+
+ return 0;
+}
+
+/* Since em28xx_pre_card_setup() requires a proper dev->model,
+ * this won't work for boards with generic PCI IDs
+ */
+void em28xx_pre_card_setup(struct em28xx *dev)
+{
+ /* Set the initial XCLK and I2C clock values based on the board
+ definition */
+ em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk & 0x7f);
+ if (!dev->board.is_em2800)
+ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed);
+ msleep(50);
+
+ /* request some modules */
+ switch (dev->model) {
+ case EM2861_BOARD_PLEXTOR_PX_TV100U:
+ /* Sets the msp34xx I2S speed */
+ dev->i2s_speed = 2048000;
+ break;
+ case EM2861_BOARD_KWORLD_PVRTV_300U:
+ case EM2880_BOARD_KWORLD_DVB_305U:
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x6d);
+ msleep(10);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x7d);
+ msleep(10);
+ break;
+ case EM2870_BOARD_COMPRO_VIDEOMATE:
+ /* TODO: someone can do some cleanup here...
+ not everything's needed */
+ em28xx_write_reg(dev, EM2880_R04_GPO, 0x00);
+ msleep(10);
+ em28xx_write_reg(dev, EM2880_R04_GPO, 0x01);
+ msleep(10);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
+ mdelay(70);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfc);
+ mdelay(70);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xdc);
+ mdelay(70);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfc);
+ mdelay(70);
+ break;
+ case EM2870_BOARD_TERRATEC_XS_MT2060:
+ /* this device needs some gpio writes to get the DVB-T
+ demod work */
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+ mdelay(70);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xde);
+ mdelay(70);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+ mdelay(70);
+ break;
+ case EM2870_BOARD_PINNACLE_PCTV_DVB:
+ /* this device needs some gpio writes to get the
+ DVB-T demod work */
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+ mdelay(70);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xde);
+ mdelay(70);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+ mdelay(70);
+ break;
+ case EM2820_BOARD_GADMEI_UTV310:
+ case EM2820_BOARD_MSI_VOX_USB_2:
+ /* enables audio for that devices */
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
+ break;
+
+ case EM2882_BOARD_KWORLD_ATSC_315U:
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
+ msleep(10);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+ msleep(10);
+ em28xx_write_reg(dev, EM2880_R04_GPO, 0x00);
+ msleep(10);
+ em28xx_write_reg(dev, EM2880_R04_GPO, 0x08);
+ msleep(10);
+ break;
+
+ case EM2860_BOARD_KAIOMY_TVNPC_U2:
+ em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x07", 1);
+ em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
+ em28xx_write_regs(dev, 0x0d, "\x42", 1);
+ em28xx_write_regs(dev, 0x08, "\xfd", 1);
+ msleep(10);
+ em28xx_write_regs(dev, 0x08, "\xff", 1);
+ msleep(10);
+ em28xx_write_regs(dev, 0x08, "\x7f", 1);
+ msleep(10);
+ em28xx_write_regs(dev, 0x08, "\x6b", 1);
+
+ break;
+ case EM2860_BOARD_EASYCAP:
+ em28xx_write_regs(dev, 0x08, "\xf8", 1);
+ break;
+
+ case EM2820_BOARD_IODATA_GVMVP_SZ:
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
+ msleep(70);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7);
+ msleep(10);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+ msleep(70);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
+ msleep(70);
+ break;
+ }
+
+ em28xx_gpio_set(dev, dev->board.tuner_gpio);
+ em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
+
+ /* Unlock device */
+ em28xx_set_mode(dev, EM28XX_SUSPEND);
+}
+
+static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl)
+{
+ memset(ctl, 0, sizeof(*ctl));
+
+ ctl->fname = XC2028_DEFAULT_FIRMWARE;
+ ctl->max_len = 64;
+ ctl->mts = em28xx_boards[dev->model].mts_firmware;
+
+ switch (dev->model) {
+ case EM2880_BOARD_EMPIRE_DUAL_TV:
+ case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
+ case EM2882_BOARD_TERRATEC_HYBRID_XS:
+ ctl->demod = XC3028_FE_ZARLINK456;
+ break;
+ case EM2880_BOARD_TERRATEC_HYBRID_XS:
+ case EM2880_BOARD_TERRATEC_HYBRID_XS_FR:
+ case EM2881_BOARD_PINNACLE_HYBRID_PRO:
+ ctl->demod = XC3028_FE_ZARLINK456;
+ break;
+ case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
+ case EM2882_BOARD_PINNACLE_HYBRID_PRO_330E:
+ ctl->demod = XC3028_FE_DEFAULT;
+ break;
+ case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600:
+ ctl->demod = XC3028_FE_DEFAULT;
+ ctl->fname = XC3028L_DEFAULT_FIRMWARE;
+ break;
+ case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850:
+ case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
+ case EM2880_BOARD_PINNACLE_PCTV_HD_PRO:
+ /* FIXME: Better to specify the needed IF */
+ ctl->demod = XC3028_FE_DEFAULT;
+ break;
+ case EM2883_BOARD_KWORLD_HYBRID_330U:
+ case EM2882_BOARD_DIKOM_DK300:
+ case EM2882_BOARD_KWORLD_VS_DVBT:
+ ctl->demod = XC3028_FE_CHINA;
+ ctl->fname = XC2028_DEFAULT_FIRMWARE;
+ break;
+ case EM2882_BOARD_EVGA_INDTUBE:
+ ctl->demod = XC3028_FE_CHINA;
+ ctl->fname = XC3028L_DEFAULT_FIRMWARE;
+ break;
+ default:
+ ctl->demod = XC3028_FE_OREN538;
+ }
+}
+
+static void em28xx_tuner_setup(struct em28xx *dev)
+{
+ struct tuner_setup tun_setup;
+ struct v4l2_frequency f;
+
+ if (dev->tuner_type == TUNER_ABSENT)
+ return;
+
+ memset(&tun_setup, 0, sizeof(tun_setup));
+
+ tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
+ tun_setup.tuner_callback = em28xx_tuner_callback;
+
+ if (dev->board.radio.type) {
+ tun_setup.type = dev->board.radio.type;
+ tun_setup.addr = dev->board.radio_addr;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup);
+ }
+
+ if ((dev->tuner_type != TUNER_ABSENT) && (dev->tuner_type)) {
+ tun_setup.type = dev->tuner_type;
+ tun_setup.addr = dev->tuner_addr;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup);
+ }
+
+ if (dev->tda9887_conf) {
+ struct v4l2_priv_tun_config tda9887_cfg;
+
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &dev->tda9887_conf;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &tda9887_cfg);
+ }
+
+ if (dev->tuner_type == TUNER_XC2028) {
+ struct v4l2_priv_tun_config xc2028_cfg;
+ struct xc2028_ctrl ctl;
+
+ memset(&xc2028_cfg, 0, sizeof(xc2028_cfg));
+ memset(&ctl, 0, sizeof(ctl));
+
+ em28xx_setup_xc3028(dev, &ctl);
+
+ xc2028_cfg.tuner = TUNER_XC2028;
+ xc2028_cfg.priv = &ctl;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &xc2028_cfg);
+ }
+
+ /* configure tuner */
+ f.tuner = 0;
+ f.type = V4L2_TUNER_ANALOG_TV;
+ f.frequency = 9076; /* just a magic number */
+ dev->ctl_freq = f.frequency;
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
+}
+
+static int em28xx_hint_board(struct em28xx *dev)
+{
+ int i;
+
+ /* HINT method: EEPROM
+ *
+ * This method works only for boards with eeprom.
+ * Uses a hash of all eeprom bytes. The hash should be
+ * unique for a vendor/tuner pair.
+ * There are a high chance that tuners for different
+ * video standards produce different hashes.
+ */
+ for (i = 0; i < ARRAY_SIZE(em28xx_eeprom_hash); i++) {
+ if (dev->hash == em28xx_eeprom_hash[i].hash) {
+ dev->model = em28xx_eeprom_hash[i].model;
+ dev->tuner_type = em28xx_eeprom_hash[i].tuner;
+
+ em28xx_errdev("Your board has no unique USB ID.\n");
+ em28xx_errdev("A hint were successfully done, "
+ "based on eeprom hash.\n");
+ em28xx_errdev("This method is not 100%% failproof.\n");
+ em28xx_errdev("If the board were missdetected, "
+ "please email this log to:\n");
+ em28xx_errdev("\tV4L Mailing List "
+ " <linux-media@vger.kernel.org>\n");
+ em28xx_errdev("Board detected as %s\n",
+ em28xx_boards[dev->model].name);
+
+ return 0;
+ }
+ }
+
+ /* HINT method: I2C attached devices
+ *
+ * This method works for all boards.
+ * Uses a hash of i2c scanned devices.
+ * Devices with the same i2c attached chips will
+ * be considered equal.
+ * This method is less precise than the eeprom one.
+ */
+
+ /* user did not request i2c scanning => do it now */
+ if (!dev->i2c_hash)
+ em28xx_do_i2c_scan(dev);
+
+ for (i = 0; i < ARRAY_SIZE(em28xx_i2c_hash); i++) {
+ if (dev->i2c_hash == em28xx_i2c_hash[i].hash) {
+ dev->model = em28xx_i2c_hash[i].model;
+ dev->tuner_type = em28xx_i2c_hash[i].tuner;
+ em28xx_errdev("Your board has no unique USB ID.\n");
+ em28xx_errdev("A hint were successfully done, "
+ "based on i2c devicelist hash.\n");
+ em28xx_errdev("This method is not 100%% failproof.\n");
+ em28xx_errdev("If the board were missdetected, "
+ "please email this log to:\n");
+ em28xx_errdev("\tV4L Mailing List "
+ " <linux-media@vger.kernel.org>\n");
+ em28xx_errdev("Board detected as %s\n",
+ em28xx_boards[dev->model].name);
+
+ return 0;
+ }
+ }
+
+ em28xx_errdev("Your board has no unique USB ID and thus need a "
+ "hint to be detected.\n");
+ em28xx_errdev("You may try to use card=<n> insmod option to "
+ "workaround that.\n");
+ em28xx_errdev("Please send an email with this log to:\n");
+ em28xx_errdev("\tV4L Mailing List <linux-media@vger.kernel.org>\n");
+ em28xx_errdev("Board eeprom hash is 0x%08lx\n", dev->hash);
+ em28xx_errdev("Board i2c devicelist hash is 0x%08lx\n", dev->i2c_hash);
+
+ em28xx_errdev("Here is a list of valid choices for the card=<n>"
+ " insmod option:\n");
+ for (i = 0; i < em28xx_bcount; i++) {
+ em28xx_errdev(" card=%d -> %s\n",
+ i, em28xx_boards[i].name);
+ }
+ return -1;
+}
+
+/* ----------------------------------------------------------------------- */
+void em28xx_register_i2c_ir(struct em28xx *dev)
+{
+ /* Leadtek winfast tv USBII deluxe can find a non working IR-device */
+ /* at address 0x18, so if that address is needed for another board in */
+ /* the future, please put it after 0x1f. */
+ struct i2c_board_info info;
+ const unsigned short addr_list[] = {
+ 0x1f, 0x30, 0x47, I2C_CLIENT_END
+ };
+
+ if (disable_ir)
+ return;
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ memset(&dev->init_data, 0, sizeof(dev->init_data));
+ strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
+
+ /* detect & configure */
+ switch (dev->model) {
+ case EM2800_BOARD_TERRATEC_CINERGY_200:
+ case EM2820_BOARD_TERRATEC_CINERGY_250:
+ dev->init_data.ir_codes = RC_MAP_EM_TERRATEC;
+ dev->init_data.get_key = em28xx_get_key_terratec;
+ dev->init_data.name = "i2c IR (EM28XX Terratec)";
+ break;
+ case EM2820_BOARD_PINNACLE_USB_2:
+ dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY;
+ dev->init_data.get_key = em28xx_get_key_pinnacle_usb_grey;
+ dev->init_data.name = "i2c IR (EM28XX Pinnacle PCTV)";
+ break;
+ case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2:
+ dev->init_data.ir_codes = RC_MAP_HAUPPAUGE;
+ dev->init_data.get_key = em28xx_get_key_em_haup;
+ dev->init_data.name = "i2c IR (EM2840 Hauppauge)";
+ break;
+ case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE:
+ dev->init_data.ir_codes = RC_MAP_WINFAST_USBII_DELUXE;
+ dev->init_data.get_key = em28xx_get_key_winfast_usbii_deluxe;
+ dev->init_data.name = "i2c IR (EM2820 Winfast TV USBII Deluxe)";
+ break;
+ }
+
+ if (dev->init_data.name)
+ info.platform_data = &dev->init_data;
+ i2c_new_probed_device(&dev->i2c_adap, &info, addr_list, NULL);
+}
+
+void em28xx_card_setup(struct em28xx *dev)
+{
+ /*
+ * If the device can be a webcam, seek for a sensor.
+ * If sensor is not found, then it isn't a webcam.
+ */
+ if (dev->board.is_webcam) {
+ if (em28xx_hint_sensor(dev) < 0)
+ dev->board.is_webcam = 0;
+ else
+ dev->progressive = 1;
+ }
+
+ if (!dev->board.is_webcam) {
+ switch (dev->model) {
+ case EM2820_BOARD_UNKNOWN:
+ case EM2800_BOARD_UNKNOWN:
+ /*
+ * The K-WORLD DVB-T 310U is detected as an MSI Digivox AD.
+ *
+ * This occurs because they share identical USB vendor and
+ * product IDs.
+ *
+ * What we do here is look up the EEPROM hash of the K-WORLD
+ * and if it is found then we decide that we do not have
+ * a DIGIVOX and reset the device to the K-WORLD instead.
+ *
+ * This solution is only valid if they do not share eeprom
+ * hash identities which has not been determined as yet.
+ */
+ if (em28xx_hint_board(dev) < 0)
+ em28xx_errdev("Board not discovered\n");
+ else {
+ em28xx_set_model(dev);
+ em28xx_pre_card_setup(dev);
+ }
+ break;
+ default:
+ em28xx_set_model(dev);
+ }
+ }
+
+ em28xx_info("Identified as %s (card=%d)\n",
+ dev->board.name, dev->model);
+
+ dev->tuner_type = em28xx_boards[dev->model].tuner_type;
+ if (em28xx_boards[dev->model].tuner_addr)
+ dev->tuner_addr = em28xx_boards[dev->model].tuner_addr;
+
+ if (em28xx_boards[dev->model].tda9887_conf)
+ dev->tda9887_conf = em28xx_boards[dev->model].tda9887_conf;
+
+ /* request some modules */
+ switch (dev->model) {
+ case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2:
+ case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
+ case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
+ case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850:
+ case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
+ {
+ struct tveeprom tv;
+#if defined(CONFIG_MODULES) && defined(MODULE)
+ request_module("tveeprom");
+#endif
+ /* Call first TVeeprom */
+
+ dev->i2c_client.addr = 0xa0 >> 1;
+ tveeprom_hauppauge_analog(&dev->i2c_client, &tv, dev->eedata);
+
+ dev->tuner_type = tv.tuner_type;
+
+ if (tv.audio_processor == V4L2_IDENT_MSPX4XX) {
+ dev->i2s_speed = 2048000;
+ dev->board.has_msp34xx = 1;
+ }
+ break;
+ }
+ case EM2882_BOARD_KWORLD_ATSC_315U:
+ em28xx_write_reg(dev, 0x0d, 0x42);
+ msleep(10);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
+ msleep(10);
+ break;
+ case EM2820_BOARD_KWORLD_PVRTV2800RF:
+ /* GPIO enables sound on KWORLD PVR TV 2800RF */
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf9);
+ break;
+ case EM2820_BOARD_UNKNOWN:
+ case EM2800_BOARD_UNKNOWN:
+ /*
+ * The K-WORLD DVB-T 310U is detected as an MSI Digivox AD.
+ *
+ * This occurs because they share identical USB vendor and
+ * product IDs.
+ *
+ * What we do here is look up the EEPROM hash of the K-WORLD
+ * and if it is found then we decide that we do not have
+ * a DIGIVOX and reset the device to the K-WORLD instead.
+ *
+ * This solution is only valid if they do not share eeprom
+ * hash identities which has not been determined as yet.
+ */
+ case EM2880_BOARD_MSI_DIGIVOX_AD:
+ if (!em28xx_hint_board(dev))
+ em28xx_set_model(dev);
+
+ /* In cases where we had to use a board hint, the call to
+ em28xx_set_mode() in em28xx_pre_card_setup() was a no-op,
+ so make the call now so the analog GPIOs are set properly
+ before probing the i2c bus. */
+ em28xx_gpio_set(dev, dev->board.tuner_gpio);
+ em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
+ break;
+
+/*
+ * The Dikom DK300 is detected as an Kworld VS-DVB-T 323UR.
+ *
+ * This occurs because they share identical USB vendor and
+ * product IDs.
+ *
+ * What we do here is look up the EEPROM hash of the Dikom
+ * and if it is found then we decide that we do not have
+ * a Kworld and reset the device to the Dikom instead.
+ *
+ * This solution is only valid if they do not share eeprom
+ * hash identities which has not been determined as yet.
+ */
+ case EM2882_BOARD_KWORLD_VS_DVBT:
+ if (!em28xx_hint_board(dev))
+ em28xx_set_model(dev);
+
+ /* In cases where we had to use a board hint, the call to
+ em28xx_set_mode() in em28xx_pre_card_setup() was a no-op,
+ so make the call now so the analog GPIOs are set properly
+ before probing the i2c bus. */
+ em28xx_gpio_set(dev, dev->board.tuner_gpio);
+ em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
+ break;
+ }
+
+#if defined(CONFIG_MODULES) && defined(MODULE)
+ if (dev->board.has_ir_i2c && !disable_ir)
+ request_module("ir-kbd-i2c");
+#endif
+ if (dev->board.has_snapshot_button)
+ em28xx_register_snapshot_button(dev);
+
+ if (dev->board.valid == EM28XX_BOARD_NOT_VALIDATED) {
+ em28xx_errdev("\n\n");
+ em28xx_errdev("The support for this board weren't "
+ "valid yet.\n");
+ em28xx_errdev("Please send a report of having this working\n");
+ em28xx_errdev("not to V4L mailing list (and/or to other "
+ "addresses)\n\n");
+ }
+
+ /* Allow override tuner type by a module parameter */
+ if (tuner >= 0)
+ dev->tuner_type = tuner;
+
+ /* request some modules */
+ if (dev->board.has_msp34xx)
+ v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+ "msp3400", 0, msp3400_addrs);
+
+ if (dev->board.decoder == EM28XX_SAA711X)
+ v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+ "saa7115_auto", 0, saa711x_addrs);
+
+ if (dev->board.decoder == EM28XX_TVP5150)
+ v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+ "tvp5150", 0, tvp5150_addrs);
+
+ if (dev->em28xx_sensor == EM28XX_MT9V011) {
+ struct mt9v011_platform_data pdata;
+ struct i2c_board_info mt9v011_info = {
+ .type = "mt9v011",
+ .addr = 0xba >> 1,
+ .platform_data = &pdata,
+ };
+
+ pdata.xtal = dev->sensor_xtal;
+ v4l2_i2c_new_subdev_board(&dev->v4l2_dev, &dev->i2c_adap,
+ &mt9v011_info, NULL);
+ }
+
+
+ if (dev->board.adecoder == EM28XX_TVAUDIO)
+ v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+ "tvaudio", dev->board.tvaudio_addr, NULL);
+
+ if (dev->board.tuner_type != TUNER_ABSENT) {
+ int has_demod = (dev->tda9887_conf & TDA9887_PRESENT);
+
+ if (dev->board.radio.type)
+ v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+ "tuner", dev->board.radio_addr, NULL);
+
+ if (has_demod)
+ v4l2_i2c_new_subdev(&dev->v4l2_dev,
+ &dev->i2c_adap, "tuner",
+ 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
+ if (dev->tuner_addr == 0) {
+ enum v4l2_i2c_tuner_type type =
+ has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV;
+ struct v4l2_subdev *sd;
+
+ sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
+ &dev->i2c_adap, "tuner",
+ 0, v4l2_i2c_tuner_addrs(type));
+
+ if (sd)
+ dev->tuner_addr = v4l2_i2c_subdev_addr(sd);
+ } else {
+ v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+ "tuner", dev->tuner_addr, NULL);
+ }
+ }
+
+ em28xx_tuner_setup(dev);
+
+ if(!disable_ir)
+ em28xx_ir_init(dev);
+}
+
+
+#if defined(CONFIG_MODULES) && defined(MODULE)
+static void request_module_async(struct work_struct *work)
+{
+ struct em28xx *dev = container_of(work,
+ struct em28xx, request_module_wk);
+
+ if (dev->has_audio_class)
+ request_module("snd-usb-audio");
+ else if (dev->has_alsa_audio)
+ request_module("em28xx-alsa");
+
+ if (dev->board.has_dvb)
+ request_module("em28xx-dvb");
+}
+
+static void request_modules(struct em28xx *dev)
+{
+ INIT_WORK(&dev->request_module_wk, request_module_async);
+ schedule_work(&dev->request_module_wk);
+}
+
+static void flush_request_modules(struct em28xx *dev)
+{
+ flush_work_sync(&dev->request_module_wk);
+}
+#else
+#define request_modules(dev)
+#define flush_request_modules(dev)
+#endif /* CONFIG_MODULES */
+
+/*
+ * em28xx_release_resources()
+ * unregisters the v4l2,i2c and usb devices
+ * called when the device gets disconnected or at module unload
+*/
+void em28xx_release_resources(struct em28xx *dev)
+{
+ if (dev->sbutton_input_dev)
+ em28xx_deregister_snapshot_button(dev);
+
+ if (dev->ir)
+ em28xx_ir_fini(dev);
+
+ /*FIXME: I2C IR should be disconnected */
+
+ em28xx_release_analog_resources(dev);
+
+ em28xx_i2c_unregister(dev);
+
+ v4l2_device_unregister(&dev->v4l2_dev);
+
+ usb_put_dev(dev->udev);
+
+ /* Mark device as unused */
+ clear_bit(dev->devno, &em28xx_devused);
+};
+
+/*
+ * em28xx_init_dev()
+ * allocates and inits the device structs, registers i2c bus and v4l device
+ */
+static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev,
+ struct usb_interface *interface,
+ int minor)
+{
+ int retval;
+
+ dev->udev = udev;
+ mutex_init(&dev->ctrl_urb_lock);
+ spin_lock_init(&dev->slock);
+ init_waitqueue_head(&dev->open);
+ init_waitqueue_head(&dev->wait_frame);
+ init_waitqueue_head(&dev->wait_stream);
+
+ dev->em28xx_write_regs = em28xx_write_regs;
+ dev->em28xx_read_reg = em28xx_read_reg;
+ dev->em28xx_read_reg_req_len = em28xx_read_reg_req_len;
+ dev->em28xx_write_regs_req = em28xx_write_regs_req;
+ dev->em28xx_read_reg_req = em28xx_read_reg_req;
+ dev->board.is_em2800 = em28xx_boards[dev->model].is_em2800;
+
+ em28xx_set_model(dev);
+
+ /* Set the default GPO/GPIO for legacy devices */
+ dev->reg_gpo_num = EM2880_R04_GPO;
+ dev->reg_gpio_num = EM28XX_R08_GPIO;
+
+ dev->wait_after_write = 5;
+
+ /* Based on the Chip ID, set the device configuration */
+ retval = em28xx_read_reg(dev, EM28XX_R0A_CHIPID);
+ if (retval > 0) {
+ dev->chip_id = retval;
+
+ switch (dev->chip_id) {
+ case CHIP_ID_EM2800:
+ em28xx_info("chip ID is em2800\n");
+ break;
+ case CHIP_ID_EM2710:
+ em28xx_info("chip ID is em2710\n");
+ break;
+ case CHIP_ID_EM2750:
+ em28xx_info("chip ID is em2750\n");
+ break;
+ case CHIP_ID_EM2820:
+ em28xx_info("chip ID is em2820 (or em2710)\n");
+ break;
+ case CHIP_ID_EM2840:
+ em28xx_info("chip ID is em2840\n");
+ break;
+ case CHIP_ID_EM2860:
+ em28xx_info("chip ID is em2860\n");
+ break;
+ case CHIP_ID_EM2870:
+ em28xx_info("chip ID is em2870\n");
+ dev->wait_after_write = 0;
+ break;
+ case CHIP_ID_EM2874:
+ em28xx_info("chip ID is em2874\n");
+ dev->reg_gpio_num = EM2874_R80_GPIO;
+ dev->wait_after_write = 0;
+ break;
+ case CHIP_ID_EM28174:
+ em28xx_info("chip ID is em28174\n");
+ dev->reg_gpio_num = EM2874_R80_GPIO;
+ dev->wait_after_write = 0;
+ break;
+ case CHIP_ID_EM2883:
+ em28xx_info("chip ID is em2882/em2883\n");
+ dev->wait_after_write = 0;
+ break;
+ case CHIP_ID_EM2884:
+ em28xx_info("chip ID is em2884\n");
+ dev->reg_gpio_num = EM2874_R80_GPIO;
+ dev->wait_after_write = 0;
+ break;
+ default:
+ em28xx_info("em28xx chip ID = %d\n", dev->chip_id);
+ }
+ }
+
+ if (dev->is_audio_only) {
+ retval = em28xx_audio_setup(dev);
+ if (retval)
+ return -ENODEV;
+ em28xx_init_extension(dev);
+
+ return 0;
+ }
+
+ /* Prepopulate cached GPO register content */
+ retval = em28xx_read_reg(dev, dev->reg_gpo_num);
+ if (retval >= 0)
+ dev->reg_gpo = retval;
+
+ em28xx_pre_card_setup(dev);
+
+ if (!dev->board.is_em2800) {
+ /* Resets I2C speed */
+ retval = em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed);
+ if (retval < 0) {
+ em28xx_errdev("%s: em28xx_write_reg failed!"
+ " retval [%d]\n",
+ __func__, retval);
+ return retval;
+ }
+ }
+
+ retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev);
+ if (retval < 0) {
+ em28xx_errdev("Call to v4l2_device_register() failed!\n");
+ return retval;
+ }
+
+ /* register i2c bus */
+ retval = em28xx_i2c_register(dev);
+ if (retval < 0) {
+ em28xx_errdev("%s: em28xx_i2c_register - error [%d]!\n",
+ __func__, retval);
+ goto unregister_dev;
+ }
+
+ /*
+ * Default format, used for tvp5150 or saa711x output formats
+ */
+ dev->vinmode = 0x10;
+ dev->vinctl = EM28XX_VINCTRL_INTERLACED |
+ EM28XX_VINCTRL_CCIR656_ENABLE;
+
+ /* Do board specific init and eeprom reading */
+ em28xx_card_setup(dev);
+
+ /* Configure audio */
+ retval = em28xx_audio_setup(dev);
+ if (retval < 0) {
+ em28xx_errdev("%s: Error while setting audio - error [%d]!\n",
+ __func__, retval);
+ goto fail;
+ }
+
+ /* wake i2c devices */
+ em28xx_wake_i2c(dev);
+
+ /* init video dma queues */
+ INIT_LIST_HEAD(&dev->vidq.active);
+ INIT_LIST_HEAD(&dev->vidq.queued);
+ INIT_LIST_HEAD(&dev->vbiq.active);
+ INIT_LIST_HEAD(&dev->vbiq.queued);
+
+ if (dev->board.has_msp34xx) {
+ /* Send a reset to other chips via gpio */
+ retval = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7);
+ if (retval < 0) {
+ em28xx_errdev("%s: em28xx_write_reg - "
+ "msp34xx(1) failed! error [%d]\n",
+ __func__, retval);
+ goto fail;
+ }
+ msleep(3);
+
+ retval = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
+ if (retval < 0) {
+ em28xx_errdev("%s: em28xx_write_reg - "
+ "msp34xx(2) failed! error [%d]\n",
+ __func__, retval);
+ goto fail;
+ }
+ msleep(3);
+ }
+
+ retval = em28xx_register_analog_devices(dev);
+ if (retval < 0) {
+ goto fail;
+ }
+
+ /* Save some power by putting tuner to sleep */
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0);
+
+ return 0;
+
+fail:
+ em28xx_i2c_unregister(dev);
+
+unregister_dev:
+ v4l2_device_unregister(&dev->v4l2_dev);
+
+ return retval;
+}
+
+/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */
+#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
+
+/*
+ * em28xx_usb_probe()
+ * checks for supported devices
+ */
+static int em28xx_usb_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev;
+ struct em28xx *dev = NULL;
+ int retval;
+ bool has_audio = false, has_video = false, has_dvb = false;
+ int i, nr;
+ const int ifnum = interface->altsetting[0].desc.bInterfaceNumber;
+ char *speed;
+
+ udev = usb_get_dev(interface_to_usbdev(interface));
+
+ /* Check to see next free device and mark as used */
+ do {
+ nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS);
+ if (nr >= EM28XX_MAXBOARDS) {
+ /* No free device slots */
+ printk(DRIVER_NAME ": Supports only %i em28xx boards.\n",
+ EM28XX_MAXBOARDS);
+ retval = -ENOMEM;
+ goto err_no_slot;
+ }
+ } while (test_and_set_bit(nr, &em28xx_devused));
+
+ /* Don't register audio interfaces */
+ if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
+ em28xx_err(DRIVER_NAME " audio device (%04x:%04x): "
+ "interface %i, class %i\n",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct),
+ ifnum,
+ interface->altsetting[0].desc.bInterfaceClass);
+
+ retval = -ENODEV;
+ goto err;
+ }
+
+ /* allocate memory for our device state and initialize it */
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL) {
+ em28xx_err(DRIVER_NAME ": out of memory!\n");
+ retval = -ENOMEM;
+ goto err;
+ }
+
+ /* compute alternate max packet sizes */
+ dev->alt_max_pkt_size = kmalloc(sizeof(dev->alt_max_pkt_size[0]) *
+ interface->num_altsetting, GFP_KERNEL);
+ if (dev->alt_max_pkt_size == NULL) {
+ em28xx_errdev("out of memory!\n");
+ kfree(dev);
+ retval = -ENOMEM;
+ goto err;
+ }
+
+ /* Get endpoints */
+ for (i = 0; i < interface->num_altsetting; i++) {
+ int ep;
+
+ for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) {
+ const struct usb_endpoint_descriptor *e;
+ int sizedescr, size;
+
+ e = &interface->altsetting[i].endpoint[ep].desc;
+
+ sizedescr = le16_to_cpu(e->wMaxPacketSize);
+ size = sizedescr & 0x7ff;
+
+ if (udev->speed == USB_SPEED_HIGH)
+ size = size * hb_mult(sizedescr);
+
+ if (usb_endpoint_xfer_isoc(e) &&
+ usb_endpoint_dir_in(e)) {
+ switch (e->bEndpointAddress) {
+ case EM28XX_EP_AUDIO:
+ has_audio = true;
+ break;
+ case EM28XX_EP_ANALOG:
+ has_video = true;
+ dev->alt_max_pkt_size[i] = size;
+ break;
+ case EM28XX_EP_DIGITAL:
+ has_dvb = true;
+ if (size > dev->dvb_max_pkt_size) {
+ dev->dvb_max_pkt_size = size;
+ dev->dvb_alt = i;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ if (!(has_audio || has_video || has_dvb)) {
+ retval = -ENODEV;
+ goto err_free;
+ }
+
+ switch (udev->speed) {
+ case USB_SPEED_LOW:
+ speed = "1.5";
+ break;
+ case USB_SPEED_UNKNOWN:
+ case USB_SPEED_FULL:
+ speed = "12";
+ break;
+ case USB_SPEED_HIGH:
+ speed = "480";
+ break;
+ default:
+ speed = "unknown";
+ }
+
+ printk(KERN_INFO DRIVER_NAME
+ ": New device %s %s @ %s Mbps "
+ "(%04x:%04x, interface %d, class %d)\n",
+ udev->manufacturer ? udev->manufacturer : "",
+ udev->product ? udev->product : "",
+ speed,
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct),
+ ifnum,
+ interface->altsetting->desc.bInterfaceNumber);
+
+ if (has_audio)
+ printk(KERN_INFO DRIVER_NAME
+ ": Audio Vendor Class interface %i found\n",
+ ifnum);
+ if (has_video)
+ printk(KERN_INFO DRIVER_NAME
+ ": Video interface %i found\n",
+ ifnum);
+ if (has_dvb)
+ printk(KERN_INFO DRIVER_NAME
+ ": DVB interface %i found\n",
+ ifnum);
+
+ /*
+ * Make sure we have 480 Mbps of bandwidth, otherwise things like
+ * video stream wouldn't likely work, since 12 Mbps is generally
+ * not enough even for most Digital TV streams.
+ */
+ if (udev->speed != USB_SPEED_HIGH && disable_usb_speed_check == 0) {
+ printk(DRIVER_NAME ": Device initialization failed.\n");
+ printk(DRIVER_NAME ": Device must be connected to a high-speed"
+ " USB 2.0 port.\n");
+ retval = -ENODEV;
+ goto err_free;
+ }
+
+ snprintf(dev->name, sizeof(dev->name), "em28xx #%d", nr);
+ dev->devno = nr;
+ dev->model = id->driver_info;
+ dev->alt = -1;
+ dev->is_audio_only = has_audio && !(has_video || has_dvb);
+ dev->has_alsa_audio = has_audio;
+ dev->audio_ifnum = ifnum;
+
+ /* Checks if audio is provided by some interface */
+ for (i = 0; i < udev->config->desc.bNumInterfaces; i++) {
+ struct usb_interface *uif = udev->config->interface[i];
+ if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
+ dev->has_audio_class = 1;
+ break;
+ }
+ }
+
+ dev->num_alt = interface->num_altsetting;
+
+ if ((card[nr] >= 0) && (card[nr] < em28xx_bcount))
+ dev->model = card[nr];
+
+ /* save our data pointer in this interface device */
+ usb_set_intfdata(interface, dev);
+
+ /* allocate device struct */
+ mutex_init(&dev->lock);
+ mutex_lock(&dev->lock);
+ retval = em28xx_init_dev(dev, udev, interface, nr);
+ if (retval) {
+ goto unlock_and_free;
+ }
+
+ if (has_dvb) {
+ /* pre-allocate DVB isoc transfer buffers */
+ retval = em28xx_alloc_isoc(dev, EM28XX_DIGITAL_MODE,
+ EM28XX_DVB_MAX_PACKETS,
+ EM28XX_DVB_NUM_BUFS,
+ dev->dvb_max_pkt_size);
+ if (retval) {
+ goto unlock_and_free;
+ }
+ }
+
+ request_modules(dev);
+
+ /* Should be the last thing to do, to avoid newer udev's to
+ open the device before fully initializing it
+ */
+ mutex_unlock(&dev->lock);
+
+ /*
+ * These extensions can be modules. If the modules are already
+ * loaded then we can initialise the device now, otherwise we
+ * will initialise it when the modules load instead.
+ */
+ em28xx_init_extension(dev);
+
+ return 0;
+
+unlock_and_free:
+ mutex_unlock(&dev->lock);
+
+err_free:
+ kfree(dev->alt_max_pkt_size);
+ kfree(dev);
+
+err:
+ clear_bit(nr, &em28xx_devused);
+
+err_no_slot:
+ usb_put_dev(udev);
+ return retval;
+}
+
+/*
+ * em28xx_usb_disconnect()
+ * called when the device gets disconnected
+ * video device will be unregistered on v4l2_close in case it is still open
+ */
+static void em28xx_usb_disconnect(struct usb_interface *interface)
+{
+ struct em28xx *dev;
+
+ dev = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+
+ if (!dev)
+ return;
+
+ if (dev->is_audio_only) {
+ mutex_lock(&dev->lock);
+ em28xx_close_extension(dev);
+ mutex_unlock(&dev->lock);
+ return;
+ }
+
+ em28xx_info("disconnecting %s\n", dev->vdev->name);
+
+ flush_request_modules(dev);
+
+ /* wait until all current v4l2 io is finished then deallocate
+ resources */
+ mutex_lock(&dev->lock);
+
+ wake_up_interruptible_all(&dev->open);
+
+ v4l2_device_disconnect(&dev->v4l2_dev);
+
+ if (dev->users) {
+ em28xx_warn
+ ("device %s is open! Deregistration and memory "
+ "deallocation are deferred on close.\n",
+ video_device_node_name(dev->vdev));
+
+ dev->state |= DEV_MISCONFIGURED;
+ em28xx_uninit_isoc(dev, dev->mode);
+ dev->state |= DEV_DISCONNECTED;
+ wake_up_interruptible(&dev->wait_frame);
+ wake_up_interruptible(&dev->wait_stream);
+ } else {
+ dev->state |= DEV_DISCONNECTED;
+ em28xx_release_resources(dev);
+ }
+
+ /* free DVB isoc buffers */
+ em28xx_uninit_isoc(dev, EM28XX_DIGITAL_MODE);
+
+ mutex_unlock(&dev->lock);
+
+ em28xx_close_extension(dev);
+
+ if (!dev->users) {
+ kfree(dev->alt_max_pkt_size);
+ kfree(dev);
+ }
+}
+
+static struct usb_driver em28xx_usb_driver = {
+ .name = "em28xx",
+ .probe = em28xx_usb_probe,
+ .disconnect = em28xx_usb_disconnect,
+ .id_table = em28xx_id_table,
+};
+
+module_usb_driver(em28xx_usb_driver);
diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c
new file mode 100644
index 00000000..53a9fb91
--- /dev/null
+++ b/drivers/media/video/em28xx/em28xx-core.c
@@ -0,0 +1,1237 @@
+/*
+ em28xx-core.c - driver for Empia EM2800/EM2820/2840 USB video capture devices
+
+ Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
+ Markus Rechberger <mrechberger@gmail.com>
+ Mauro Carvalho Chehab <mchehab@infradead.org>
+ Sascha Sommer <saschasommer@freenet.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/vmalloc.h>
+#include <media/v4l2-common.h>
+
+#include "em28xx.h"
+
+/* #define ENABLE_DEBUG_ISOC_FRAMES */
+
+static unsigned int core_debug;
+module_param(core_debug, int, 0644);
+MODULE_PARM_DESC(core_debug, "enable debug messages [core]");
+
+#define em28xx_coredbg(fmt, arg...) do {\
+ if (core_debug) \
+ printk(KERN_INFO "%s %s :"fmt, \
+ dev->name, __func__ , ##arg); } while (0)
+
+static unsigned int reg_debug;
+module_param(reg_debug, int, 0644);
+MODULE_PARM_DESC(reg_debug, "enable debug messages [URB reg]");
+
+#define em28xx_regdbg(fmt, arg...) do {\
+ if (reg_debug) \
+ printk(KERN_INFO "%s %s :"fmt, \
+ dev->name, __func__ , ##arg); } while (0)
+
+static int alt;
+module_param(alt, int, 0644);
+MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint");
+
+static unsigned int disable_vbi;
+module_param(disable_vbi, int, 0644);
+MODULE_PARM_DESC(disable_vbi, "disable vbi support");
+
+/* FIXME */
+#define em28xx_isocdbg(fmt, arg...) do {\
+ if (core_debug) \
+ printk(KERN_INFO "%s %s :"fmt, \
+ dev->name, __func__ , ##arg); } while (0)
+
+/*
+ * em28xx_read_reg_req()
+ * reads data from the usb device specifying bRequest
+ */
+int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg,
+ char *buf, int len)
+{
+ int ret;
+ int pipe = usb_rcvctrlpipe(dev->udev, 0);
+
+ if (dev->state & DEV_DISCONNECTED)
+ return -ENODEV;
+
+ if (len > URB_MAX_CTRL_SIZE)
+ return -EINVAL;
+
+ if (reg_debug) {
+ printk(KERN_DEBUG "(pipe 0x%08x): "
+ "IN: %02x %02x %02x %02x %02x %02x %02x %02x ",
+ pipe,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ req, 0, 0,
+ reg & 0xff, reg >> 8,
+ len & 0xff, len >> 8);
+ }
+
+ mutex_lock(&dev->ctrl_urb_lock);
+ ret = usb_control_msg(dev->udev, pipe, req,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x0000, reg, dev->urb_buf, len, HZ);
+ if (ret < 0) {
+ if (reg_debug)
+ printk(" failed!\n");
+ mutex_unlock(&dev->ctrl_urb_lock);
+ return ret;
+ }
+
+ if (len)
+ memcpy(buf, dev->urb_buf, len);
+
+ mutex_unlock(&dev->ctrl_urb_lock);
+
+ if (reg_debug) {
+ int byte;
+
+ printk("<<<");
+ for (byte = 0; byte < len; byte++)
+ printk(" %02x", (unsigned char)buf[byte]);
+ printk("\n");
+ }
+
+ return ret;
+}
+
+/*
+ * em28xx_read_reg_req()
+ * reads data from the usb device specifying bRequest
+ */
+int em28xx_read_reg_req(struct em28xx *dev, u8 req, u16 reg)
+{
+ int ret;
+ u8 val;
+
+ ret = em28xx_read_reg_req_len(dev, req, reg, &val, 1);
+ if (ret < 0)
+ return ret;
+
+ return val;
+}
+
+int em28xx_read_reg(struct em28xx *dev, u16 reg)
+{
+ return em28xx_read_reg_req(dev, USB_REQ_GET_STATUS, reg);
+}
+
+/*
+ * em28xx_write_regs_req()
+ * sends data to the usb device, specifying bRequest
+ */
+int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf,
+ int len)
+{
+ int ret;
+ int pipe = usb_sndctrlpipe(dev->udev, 0);
+
+ if (dev->state & DEV_DISCONNECTED)
+ return -ENODEV;
+
+ if ((len < 1) || (len > URB_MAX_CTRL_SIZE))
+ return -EINVAL;
+
+ if (reg_debug) {
+ int byte;
+
+ printk(KERN_DEBUG "(pipe 0x%08x): "
+ "OUT: %02x %02x %02x %02x %02x %02x %02x %02x >>>",
+ pipe,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ req, 0, 0,
+ reg & 0xff, reg >> 8,
+ len & 0xff, len >> 8);
+
+ for (byte = 0; byte < len; byte++)
+ printk(" %02x", (unsigned char)buf[byte]);
+ printk("\n");
+ }
+
+ mutex_lock(&dev->ctrl_urb_lock);
+ memcpy(dev->urb_buf, buf, len);
+ ret = usb_control_msg(dev->udev, pipe, req,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x0000, reg, dev->urb_buf, len, HZ);
+ mutex_unlock(&dev->ctrl_urb_lock);
+
+ if (dev->wait_after_write)
+ msleep(dev->wait_after_write);
+
+ return ret;
+}
+
+int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len)
+{
+ int rc;
+
+ rc = em28xx_write_regs_req(dev, USB_REQ_GET_STATUS, reg, buf, len);
+
+ /* Stores GPO/GPIO values at the cache, if changed
+ Only write values should be stored, since input on a GPIO
+ register will return the input bits.
+ Not sure what happens on reading GPO register.
+ */
+ if (rc >= 0) {
+ if (reg == dev->reg_gpo_num)
+ dev->reg_gpo = buf[0];
+ else if (reg == dev->reg_gpio_num)
+ dev->reg_gpio = buf[0];
+ }
+
+ return rc;
+}
+
+/* Write a single register */
+int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val)
+{
+ return em28xx_write_regs(dev, reg, &val, 1);
+}
+EXPORT_SYMBOL_GPL(em28xx_write_reg);
+
+/*
+ * em28xx_write_reg_bits()
+ * sets only some bits (specified by bitmask) of a register, by first reading
+ * the actual value
+ */
+int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
+ u8 bitmask)
+{
+ int oldval;
+ u8 newval;
+
+ /* Uses cache for gpo/gpio registers */
+ if (reg == dev->reg_gpo_num)
+ oldval = dev->reg_gpo;
+ else if (reg == dev->reg_gpio_num)
+ oldval = dev->reg_gpio;
+ else
+ oldval = em28xx_read_reg(dev, reg);
+
+ if (oldval < 0)
+ return oldval;
+
+ newval = (((u8) oldval) & ~bitmask) | (val & bitmask);
+
+ return em28xx_write_regs(dev, reg, &newval, 1);
+}
+
+/*
+ * em28xx_is_ac97_ready()
+ * Checks if ac97 is ready
+ */
+static int em28xx_is_ac97_ready(struct em28xx *dev)
+{
+ int ret, i;
+
+ /* Wait up to 50 ms for AC97 command to complete */
+ for (i = 0; i < 10; i++, msleep(5)) {
+ ret = em28xx_read_reg(dev, EM28XX_R43_AC97BUSY);
+ if (ret < 0)
+ return ret;
+
+ if (!(ret & 0x01))
+ return 0;
+ }
+
+ em28xx_warn("AC97 command still being executed: not handled properly!\n");
+ return -EBUSY;
+}
+
+/*
+ * em28xx_read_ac97()
+ * write a 16 bit value to the specified AC97 address (LSB first!)
+ */
+int em28xx_read_ac97(struct em28xx *dev, u8 reg)
+{
+ int ret;
+ u8 addr = (reg & 0x7f) | 0x80;
+ u16 val;
+
+ ret = em28xx_is_ac97_ready(dev);
+ if (ret < 0)
+ return ret;
+
+ ret = em28xx_write_regs(dev, EM28XX_R42_AC97ADDR, &addr, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = dev->em28xx_read_reg_req_len(dev, 0, EM28XX_R40_AC97LSB,
+ (u8 *)&val, sizeof(val));
+
+ if (ret < 0)
+ return ret;
+ return le16_to_cpu(val);
+}
+EXPORT_SYMBOL_GPL(em28xx_read_ac97);
+
+/*
+ * em28xx_write_ac97()
+ * write a 16 bit value to the specified AC97 address (LSB first!)
+ */
+int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val)
+{
+ int ret;
+ u8 addr = reg & 0x7f;
+ __le16 value;
+
+ value = cpu_to_le16(val);
+
+ ret = em28xx_is_ac97_ready(dev);
+ if (ret < 0)
+ return ret;
+
+ ret = em28xx_write_regs(dev, EM28XX_R40_AC97LSB, (u8 *) &value, 2);
+ if (ret < 0)
+ return ret;
+
+ ret = em28xx_write_regs(dev, EM28XX_R42_AC97ADDR, &addr, 1);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(em28xx_write_ac97);
+
+struct em28xx_vol_itable {
+ enum em28xx_amux mux;
+ u8 reg;
+};
+
+static struct em28xx_vol_itable inputs[] = {
+ { EM28XX_AMUX_VIDEO, AC97_VIDEO_VOL },
+ { EM28XX_AMUX_LINE_IN, AC97_LINEIN_VOL },
+ { EM28XX_AMUX_PHONE, AC97_PHONE_VOL },
+ { EM28XX_AMUX_MIC, AC97_MIC_VOL },
+ { EM28XX_AMUX_CD, AC97_CD_VOL },
+ { EM28XX_AMUX_AUX, AC97_AUX_VOL },
+ { EM28XX_AMUX_PCM_OUT, AC97_PCM_OUT_VOL },
+};
+
+static int set_ac97_input(struct em28xx *dev)
+{
+ int ret, i;
+ enum em28xx_amux amux = dev->ctl_ainput;
+
+ /* EM28XX_AMUX_VIDEO2 is a special case used to indicate that
+ em28xx should point to LINE IN, while AC97 should use VIDEO
+ */
+ if (amux == EM28XX_AMUX_VIDEO2)
+ amux = EM28XX_AMUX_VIDEO;
+
+ /* Mute all entres but the one that were selected */
+ for (i = 0; i < ARRAY_SIZE(inputs); i++) {
+ if (amux == inputs[i].mux)
+ ret = em28xx_write_ac97(dev, inputs[i].reg, 0x0808);
+ else
+ ret = em28xx_write_ac97(dev, inputs[i].reg, 0x8000);
+
+ if (ret < 0)
+ em28xx_warn("couldn't setup AC97 register %d\n",
+ inputs[i].reg);
+ }
+ return 0;
+}
+
+static int em28xx_set_audio_source(struct em28xx *dev)
+{
+ int ret;
+ u8 input;
+
+ if (dev->board.is_em2800) {
+ if (dev->ctl_ainput == EM28XX_AMUX_VIDEO)
+ input = EM2800_AUDIO_SRC_TUNER;
+ else
+ input = EM2800_AUDIO_SRC_LINE;
+
+ ret = em28xx_write_regs(dev, EM2800_R08_AUDIOSRC, &input, 1);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (dev->board.has_msp34xx)
+ input = EM28XX_AUDIO_SRC_TUNER;
+ else {
+ switch (dev->ctl_ainput) {
+ case EM28XX_AMUX_VIDEO:
+ input = EM28XX_AUDIO_SRC_TUNER;
+ break;
+ default:
+ input = EM28XX_AUDIO_SRC_LINE;
+ break;
+ }
+ }
+
+ if (dev->board.mute_gpio && dev->mute)
+ em28xx_gpio_set(dev, dev->board.mute_gpio);
+ else
+ em28xx_gpio_set(dev, INPUT(dev->ctl_input)->gpio);
+
+ ret = em28xx_write_reg_bits(dev, EM28XX_R0E_AUDIOSRC, input, 0xc0);
+ if (ret < 0)
+ return ret;
+ msleep(5);
+
+ switch (dev->audio_mode.ac97) {
+ case EM28XX_NO_AC97:
+ break;
+ default:
+ ret = set_ac97_input(dev);
+ }
+
+ return ret;
+}
+
+struct em28xx_vol_otable {
+ enum em28xx_aout mux;
+ u8 reg;
+};
+
+static const struct em28xx_vol_otable outputs[] = {
+ { EM28XX_AOUT_MASTER, AC97_MASTER_VOL },
+ { EM28XX_AOUT_LINE, AC97_LINE_LEVEL_VOL },
+ { EM28XX_AOUT_MONO, AC97_MASTER_MONO_VOL },
+ { EM28XX_AOUT_LFE, AC97_LFE_MASTER_VOL },
+ { EM28XX_AOUT_SURR, AC97_SURR_MASTER_VOL },
+};
+
+int em28xx_audio_analog_set(struct em28xx *dev)
+{
+ int ret, i;
+ u8 xclk;
+
+ if (!dev->audio_mode.has_audio)
+ return 0;
+
+ /* It is assumed that all devices use master volume for output.
+ It would be possible to use also line output.
+ */
+ if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
+ /* Mute all outputs */
+ for (i = 0; i < ARRAY_SIZE(outputs); i++) {
+ ret = em28xx_write_ac97(dev, outputs[i].reg, 0x8000);
+ if (ret < 0)
+ em28xx_warn("couldn't setup AC97 register %d\n",
+ outputs[i].reg);
+ }
+ }
+
+ xclk = dev->board.xclk & 0x7f;
+ if (!dev->mute)
+ xclk |= EM28XX_XCLK_AUDIO_UNMUTE;
+
+ ret = em28xx_write_reg(dev, EM28XX_R0F_XCLK, xclk);
+ if (ret < 0)
+ return ret;
+ msleep(10);
+
+ /* Selects the proper audio input */
+ ret = em28xx_set_audio_source(dev);
+
+ /* Sets volume */
+ if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
+ int vol;
+
+ em28xx_write_ac97(dev, AC97_POWER_DOWN_CTRL, 0x4200);
+ em28xx_write_ac97(dev, AC97_EXT_AUD_CTRL, 0x0031);
+ em28xx_write_ac97(dev, AC97_PCM_IN_SRATE, 0xbb80);
+
+ /* LSB: left channel - both channels with the same level */
+ vol = (0x1f - dev->volume) | ((0x1f - dev->volume) << 8);
+
+ /* Mute device, if needed */
+ if (dev->mute)
+ vol |= 0x8000;
+
+ /* Sets volume */
+ for (i = 0; i < ARRAY_SIZE(outputs); i++) {
+ if (dev->ctl_aoutput & outputs[i].mux)
+ ret = em28xx_write_ac97(dev, outputs[i].reg,
+ vol);
+ if (ret < 0)
+ em28xx_warn("couldn't setup AC97 register %d\n",
+ outputs[i].reg);
+ }
+
+ if (dev->ctl_aoutput & EM28XX_AOUT_PCM_IN) {
+ int sel = ac97_return_record_select(dev->ctl_aoutput);
+
+ /* Use the same input for both left and right
+ channels */
+ sel |= (sel << 8);
+
+ em28xx_write_ac97(dev, AC97_RECORD_SELECT, sel);
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(em28xx_audio_analog_set);
+
+int em28xx_audio_setup(struct em28xx *dev)
+{
+ int vid1, vid2, feat, cfg;
+ u32 vid;
+
+ if (dev->chip_id == CHIP_ID_EM2870 || dev->chip_id == CHIP_ID_EM2874
+ || dev->chip_id == CHIP_ID_EM28174) {
+ /* Digital only device - don't load any alsa module */
+ dev->audio_mode.has_audio = false;
+ dev->has_audio_class = false;
+ dev->has_alsa_audio = false;
+ return 0;
+ }
+
+ dev->audio_mode.has_audio = true;
+
+ /* See how this device is configured */
+ cfg = em28xx_read_reg(dev, EM28XX_R00_CHIPCFG);
+ em28xx_info("Config register raw data: 0x%02x\n", cfg);
+ if (cfg < 0) {
+ /* Register read error? */
+ cfg = EM28XX_CHIPCFG_AC97; /* Be conservative */
+ } else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) == 0x00) {
+ /* The device doesn't have vendor audio at all */
+ dev->has_alsa_audio = false;
+ dev->audio_mode.has_audio = false;
+ return 0;
+ } else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) ==
+ EM28XX_CHIPCFG_I2S_3_SAMPRATES) {
+ em28xx_info("I2S Audio (3 sample rates)\n");
+ dev->audio_mode.i2s_3rates = 1;
+ } else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) ==
+ EM28XX_CHIPCFG_I2S_5_SAMPRATES) {
+ em28xx_info("I2S Audio (5 sample rates)\n");
+ dev->audio_mode.i2s_5rates = 1;
+ }
+
+ if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) != EM28XX_CHIPCFG_AC97) {
+ /* Skip the code that does AC97 vendor detection */
+ dev->audio_mode.ac97 = EM28XX_NO_AC97;
+ goto init_audio;
+ }
+
+ dev->audio_mode.ac97 = EM28XX_AC97_OTHER;
+
+ vid1 = em28xx_read_ac97(dev, AC97_VENDOR_ID1);
+ if (vid1 < 0) {
+ /*
+ * Device likely doesn't support AC97
+ * Note: (some) em2800 devices without eeprom reports 0x91 on
+ * CHIPCFG register, even not having an AC97 chip
+ */
+ em28xx_warn("AC97 chip type couldn't be determined\n");
+ dev->audio_mode.ac97 = EM28XX_NO_AC97;
+ dev->has_alsa_audio = false;
+ dev->audio_mode.has_audio = false;
+ goto init_audio;
+ }
+
+ vid2 = em28xx_read_ac97(dev, AC97_VENDOR_ID2);
+ if (vid2 < 0)
+ goto init_audio;
+
+ vid = vid1 << 16 | vid2;
+
+ dev->audio_mode.ac97_vendor_id = vid;
+ em28xx_warn("AC97 vendor ID = 0x%08x\n", vid);
+
+ feat = em28xx_read_ac97(dev, AC97_RESET);
+ if (feat < 0)
+ goto init_audio;
+
+ dev->audio_mode.ac97_feat = feat;
+ em28xx_warn("AC97 features = 0x%04x\n", feat);
+
+ /* Try to identify what audio processor we have */
+ if (((vid == 0xffffffff) || (vid == 0x83847650)) && (feat == 0x6a90))
+ dev->audio_mode.ac97 = EM28XX_AC97_EM202;
+ else if ((vid >> 8) == 0x838476)
+ dev->audio_mode.ac97 = EM28XX_AC97_SIGMATEL;
+
+init_audio:
+ /* Reports detected AC97 processor */
+ switch (dev->audio_mode.ac97) {
+ case EM28XX_NO_AC97:
+ em28xx_info("No AC97 audio processor\n");
+ break;
+ case EM28XX_AC97_EM202:
+ em28xx_info("Empia 202 AC97 audio processor detected\n");
+ break;
+ case EM28XX_AC97_SIGMATEL:
+ em28xx_info("Sigmatel audio processor detected(stac 97%02x)\n",
+ dev->audio_mode.ac97_vendor_id & 0xff);
+ break;
+ case EM28XX_AC97_OTHER:
+ em28xx_warn("Unknown AC97 audio processor detected!\n");
+ break;
+ default:
+ break;
+ }
+
+ return em28xx_audio_analog_set(dev);
+}
+EXPORT_SYMBOL_GPL(em28xx_audio_setup);
+
+int em28xx_colorlevels_set_default(struct em28xx *dev)
+{
+ em28xx_write_reg(dev, EM28XX_R20_YGAIN, 0x10); /* contrast */
+ em28xx_write_reg(dev, EM28XX_R21_YOFFSET, 0x00); /* brightness */
+ em28xx_write_reg(dev, EM28XX_R22_UVGAIN, 0x10); /* saturation */
+ em28xx_write_reg(dev, EM28XX_R23_UOFFSET, 0x00);
+ em28xx_write_reg(dev, EM28XX_R24_VOFFSET, 0x00);
+ em28xx_write_reg(dev, EM28XX_R25_SHARPNESS, 0x00);
+
+ em28xx_write_reg(dev, EM28XX_R14_GAMMA, 0x20);
+ em28xx_write_reg(dev, EM28XX_R15_RGAIN, 0x20);
+ em28xx_write_reg(dev, EM28XX_R16_GGAIN, 0x20);
+ em28xx_write_reg(dev, EM28XX_R17_BGAIN, 0x20);
+ em28xx_write_reg(dev, EM28XX_R18_ROFFSET, 0x00);
+ em28xx_write_reg(dev, EM28XX_R19_GOFFSET, 0x00);
+ return em28xx_write_reg(dev, EM28XX_R1A_BOFFSET, 0x00);
+}
+
+int em28xx_capture_start(struct em28xx *dev, int start)
+{
+ int rc;
+
+ if (dev->chip_id == CHIP_ID_EM2874 ||
+ dev->chip_id == CHIP_ID_EM2884 ||
+ dev->chip_id == CHIP_ID_EM28174) {
+ /* The Transport Stream Enable Register moved in em2874 */
+ if (!start) {
+ rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE,
+ 0x00,
+ EM2874_TS1_CAPTURE_ENABLE);
+ return rc;
+ }
+
+ /* Enable Transport Stream */
+ rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE,
+ EM2874_TS1_CAPTURE_ENABLE,
+ EM2874_TS1_CAPTURE_ENABLE);
+ return rc;
+ }
+
+
+ /* FIXME: which is the best order? */
+ /* video registers are sampled by VREF */
+ rc = em28xx_write_reg_bits(dev, EM28XX_R0C_USBSUSP,
+ start ? 0x10 : 0x00, 0x10);
+ if (rc < 0)
+ return rc;
+
+ if (!start) {
+ /* disable video capture */
+ rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x27);
+ return rc;
+ }
+
+ if (dev->board.is_webcam)
+ rc = em28xx_write_reg(dev, 0x13, 0x0c);
+
+ /* enable video capture */
+ rc = em28xx_write_reg(dev, 0x48, 0x00);
+
+ if (dev->mode == EM28XX_ANALOG_MODE)
+ rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67);
+ else
+ rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37);
+
+ msleep(6);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(em28xx_capture_start);
+
+int em28xx_vbi_supported(struct em28xx *dev)
+{
+ /* Modprobe option to manually disable */
+ if (disable_vbi == 1)
+ return 0;
+
+ if (dev->chip_id == CHIP_ID_EM2860 ||
+ dev->chip_id == CHIP_ID_EM2883)
+ return 1;
+
+ /* Version of em28xx that does not support VBI */
+ return 0;
+}
+
+int em28xx_set_outfmt(struct em28xx *dev)
+{
+ int ret;
+ u8 vinctrl;
+
+ ret = em28xx_write_reg_bits(dev, EM28XX_R27_OUTFMT,
+ dev->format->reg | 0x20, 0xff);
+ if (ret < 0)
+ return ret;
+
+ ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, dev->vinmode);
+ if (ret < 0)
+ return ret;
+
+ vinctrl = dev->vinctl;
+ if (em28xx_vbi_supported(dev) == 1) {
+ vinctrl |= EM28XX_VINCTRL_VBI_RAW;
+ em28xx_write_reg(dev, EM28XX_R34_VBI_START_H, 0x00);
+ em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH, dev->vbi_width/4);
+ em28xx_write_reg(dev, EM28XX_R37_VBI_HEIGHT, dev->vbi_height);
+ if (dev->norm & V4L2_STD_525_60) {
+ /* NTSC */
+ em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x09);
+ } else if (dev->norm & V4L2_STD_625_50) {
+ /* PAL */
+ em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x07);
+ }
+ }
+
+ return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, vinctrl);
+}
+
+static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax,
+ u8 ymin, u8 ymax)
+{
+ em28xx_coredbg("em28xx Scale: (%d,%d)-(%d,%d)\n",
+ xmin, ymin, xmax, ymax);
+
+ em28xx_write_regs(dev, EM28XX_R28_XMIN, &xmin, 1);
+ em28xx_write_regs(dev, EM28XX_R29_XMAX, &xmax, 1);
+ em28xx_write_regs(dev, EM28XX_R2A_YMIN, &ymin, 1);
+ return em28xx_write_regs(dev, EM28XX_R2B_YMAX, &ymax, 1);
+}
+
+static int em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart,
+ u16 width, u16 height)
+{
+ u8 cwidth = width;
+ u8 cheight = height;
+ u8 overflow = (height >> 7 & 0x02) | (width >> 8 & 0x01);
+
+ em28xx_coredbg("em28xx Area Set: (%d,%d)\n",
+ (width | (overflow & 2) << 7),
+ (height | (overflow & 1) << 8));
+
+ em28xx_write_regs(dev, EM28XX_R1C_HSTART, &hstart, 1);
+ em28xx_write_regs(dev, EM28XX_R1D_VSTART, &vstart, 1);
+ em28xx_write_regs(dev, EM28XX_R1E_CWIDTH, &cwidth, 1);
+ em28xx_write_regs(dev, EM28XX_R1F_CHEIGHT, &cheight, 1);
+ return em28xx_write_regs(dev, EM28XX_R1B_OFLOW, &overflow, 1);
+}
+
+static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v)
+{
+ u8 mode;
+ /* the em2800 scaler only supports scaling down to 50% */
+
+ if (dev->board.is_em2800) {
+ mode = (v ? 0x20 : 0x00) | (h ? 0x10 : 0x00);
+ } else {
+ u8 buf[2];
+
+ buf[0] = h;
+ buf[1] = h >> 8;
+ em28xx_write_regs(dev, EM28XX_R30_HSCALELOW, (char *)buf, 2);
+
+ buf[0] = v;
+ buf[1] = v >> 8;
+ em28xx_write_regs(dev, EM28XX_R32_VSCALELOW, (char *)buf, 2);
+ /* it seems that both H and V scalers must be active
+ to work correctly */
+ mode = (h || v) ? 0x30 : 0x00;
+ }
+ return em28xx_write_reg_bits(dev, EM28XX_R26_COMPR, mode, 0x30);
+}
+
+/* FIXME: this only function read values from dev */
+int em28xx_resolution_set(struct em28xx *dev)
+{
+ int width, height;
+ width = norm_maxw(dev);
+ height = norm_maxh(dev);
+
+ /* Properly setup VBI */
+ dev->vbi_width = 720;
+ if (dev->norm & V4L2_STD_525_60)
+ dev->vbi_height = 12;
+ else
+ dev->vbi_height = 18;
+
+ if (!dev->progressive)
+ height >>= norm_maxh(dev);
+
+ em28xx_set_outfmt(dev);
+
+
+ em28xx_accumulator_set(dev, 1, (width - 4) >> 2, 1, (height - 4) >> 2);
+
+ /* If we don't set the start position to 2 in VBI mode, we end up
+ with line 20/21 being YUYV encoded instead of being in 8-bit
+ greyscale. The core of the issue is that line 21 (and line 23 for
+ PAL WSS) are inside of active video region, and as a result they
+ get the pixelformatting associated with that area. So by cropping
+ it out, we end up with the same format as the rest of the VBI
+ region */
+ if (em28xx_vbi_supported(dev) == 1)
+ em28xx_capture_area_set(dev, 0, 2, width >> 2, height >> 2);
+ else
+ em28xx_capture_area_set(dev, 0, 0, width >> 2, height >> 2);
+
+ return em28xx_scaler_set(dev, dev->hscale, dev->vscale);
+}
+
+int em28xx_set_alternate(struct em28xx *dev)
+{
+ int errCode, prev_alt = dev->alt;
+ int i;
+ unsigned int min_pkt_size = dev->width * 2 + 4;
+
+ /*
+ * alt = 0 is used only for control messages, so, only values
+ * greater than 0 can be used for streaming.
+ */
+ if (alt && alt < dev->num_alt) {
+ em28xx_coredbg("alternate forced to %d\n", dev->alt);
+ dev->alt = alt;
+ goto set_alt;
+ }
+
+ /* When image size is bigger than a certain value,
+ the frame size should be increased, otherwise, only
+ green screen will be received.
+ */
+ if (dev->width * 2 * dev->height > 720 * 240 * 2)
+ min_pkt_size *= 2;
+
+ for (i = 0; i < dev->num_alt; i++) {
+ /* stop when the selected alt setting offers enough bandwidth */
+ if (dev->alt_max_pkt_size[i] >= min_pkt_size) {
+ dev->alt = i;
+ break;
+ /* otherwise make sure that we end up with the maximum bandwidth
+ because the min_pkt_size equation might be wrong...
+ */
+ } else if (dev->alt_max_pkt_size[i] >
+ dev->alt_max_pkt_size[dev->alt])
+ dev->alt = i;
+ }
+
+set_alt:
+ if (dev->alt != prev_alt) {
+ em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n",
+ min_pkt_size, dev->alt);
+ dev->max_pkt_size = dev->alt_max_pkt_size[dev->alt];
+ em28xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n",
+ dev->alt, dev->max_pkt_size);
+ errCode = usb_set_interface(dev->udev, 0, dev->alt);
+ if (errCode < 0) {
+ em28xx_errdev("cannot change alternate number to %d (error=%i)\n",
+ dev->alt, errCode);
+ return errCode;
+ }
+ }
+ return 0;
+}
+
+int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio)
+{
+ int rc = 0;
+
+ if (!gpio)
+ return rc;
+
+ if (dev->mode != EM28XX_SUSPEND) {
+ em28xx_write_reg(dev, 0x48, 0x00);
+ if (dev->mode == EM28XX_ANALOG_MODE)
+ em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67);
+ else
+ em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37);
+ msleep(6);
+ }
+
+ /* Send GPIO reset sequences specified at board entry */
+ while (gpio->sleep >= 0) {
+ if (gpio->reg >= 0) {
+ rc = em28xx_write_reg_bits(dev,
+ gpio->reg,
+ gpio->val,
+ gpio->mask);
+ if (rc < 0)
+ return rc;
+ }
+ if (gpio->sleep > 0)
+ msleep(gpio->sleep);
+
+ gpio++;
+ }
+ return rc;
+}
+EXPORT_SYMBOL_GPL(em28xx_gpio_set);
+
+int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode)
+{
+ if (dev->mode == set_mode)
+ return 0;
+
+ if (set_mode == EM28XX_SUSPEND) {
+ dev->mode = set_mode;
+
+ /* FIXME: add suspend support for ac97 */
+
+ return em28xx_gpio_set(dev, dev->board.suspend_gpio);
+ }
+
+ dev->mode = set_mode;
+
+ if (dev->mode == EM28XX_DIGITAL_MODE)
+ return em28xx_gpio_set(dev, dev->board.dvb_gpio);
+ else
+ return em28xx_gpio_set(dev, INPUT(dev->ctl_input)->gpio);
+}
+EXPORT_SYMBOL_GPL(em28xx_set_mode);
+
+/* ------------------------------------------------------------------
+ URB control
+ ------------------------------------------------------------------*/
+
+/*
+ * IRQ callback, called by URB callback
+ */
+static void em28xx_irq_callback(struct urb *urb)
+{
+ struct em28xx *dev = urb->context;
+ int i;
+
+ switch (urb->status) {
+ case 0: /* success */
+ case -ETIMEDOUT: /* NAK */
+ break;
+ case -ECONNRESET: /* kill */
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ default: /* error */
+ em28xx_isocdbg("urb completition error %d.\n", urb->status);
+ break;
+ }
+
+ /* Copy data from URB */
+ spin_lock(&dev->slock);
+ dev->isoc_ctl.isoc_copy(dev, urb);
+ spin_unlock(&dev->slock);
+
+ /* Reset urb buffers */
+ for (i = 0; i < urb->number_of_packets; i++) {
+ urb->iso_frame_desc[i].status = 0;
+ urb->iso_frame_desc[i].actual_length = 0;
+ }
+ urb->status = 0;
+
+ urb->status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (urb->status) {
+ em28xx_isocdbg("urb resubmit failed (error=%i)\n",
+ urb->status);
+ }
+}
+
+/*
+ * Stop and Deallocate URBs
+ */
+void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode)
+{
+ struct urb *urb;
+ struct em28xx_usb_isoc_bufs *isoc_bufs;
+ int i;
+
+ em28xx_isocdbg("em28xx: called em28xx_uninit_isoc in mode %d\n", mode);
+
+ if (mode == EM28XX_DIGITAL_MODE)
+ isoc_bufs = &dev->isoc_ctl.digital_bufs;
+ else
+ isoc_bufs = &dev->isoc_ctl.analog_bufs;
+
+ dev->isoc_ctl.nfields = -1;
+ for (i = 0; i < isoc_bufs->num_bufs; i++) {
+ urb = isoc_bufs->urb[i];
+ if (urb) {
+ if (!irqs_disabled())
+ usb_kill_urb(urb);
+ else
+ usb_unlink_urb(urb);
+
+ if (isoc_bufs->transfer_buffer[i]) {
+ usb_free_coherent(dev->udev,
+ urb->transfer_buffer_length,
+ isoc_bufs->transfer_buffer[i],
+ urb->transfer_dma);
+ }
+ usb_free_urb(urb);
+ isoc_bufs->urb[i] = NULL;
+ }
+ isoc_bufs->transfer_buffer[i] = NULL;
+ }
+
+ kfree(isoc_bufs->urb);
+ kfree(isoc_bufs->transfer_buffer);
+
+ isoc_bufs->urb = NULL;
+ isoc_bufs->transfer_buffer = NULL;
+ isoc_bufs->num_bufs = 0;
+
+ em28xx_capture_start(dev, 0);
+}
+EXPORT_SYMBOL_GPL(em28xx_uninit_isoc);
+
+/*
+ * Allocate URBs
+ */
+int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode,
+ int max_packets, int num_bufs, int max_pkt_size)
+{
+ struct em28xx_usb_isoc_bufs *isoc_bufs;
+ int i;
+ int sb_size, pipe;
+ struct urb *urb;
+ int j, k;
+
+ em28xx_isocdbg("em28xx: called em28xx_alloc_isoc in mode %d\n", mode);
+
+ if (mode == EM28XX_DIGITAL_MODE)
+ isoc_bufs = &dev->isoc_ctl.digital_bufs;
+ else
+ isoc_bufs = &dev->isoc_ctl.analog_bufs;
+
+ /* De-allocates all pending stuff */
+ em28xx_uninit_isoc(dev, mode);
+
+ isoc_bufs->num_bufs = num_bufs;
+
+ isoc_bufs->urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL);
+ if (!isoc_bufs->urb) {
+ em28xx_errdev("cannot alloc memory for usb buffers\n");
+ return -ENOMEM;
+ }
+
+ isoc_bufs->transfer_buffer = kzalloc(sizeof(void *)*num_bufs,
+ GFP_KERNEL);
+ if (!isoc_bufs->transfer_buffer) {
+ em28xx_errdev("cannot allocate memory for usb transfer\n");
+ kfree(isoc_bufs->urb);
+ return -ENOMEM;
+ }
+
+ isoc_bufs->max_pkt_size = max_pkt_size;
+ isoc_bufs->num_packets = max_packets;
+ dev->isoc_ctl.vid_buf = NULL;
+ dev->isoc_ctl.vbi_buf = NULL;
+
+ sb_size = isoc_bufs->num_packets * isoc_bufs->max_pkt_size;
+
+ /* allocate urbs and transfer buffers */
+ for (i = 0; i < isoc_bufs->num_bufs; i++) {
+ urb = usb_alloc_urb(isoc_bufs->num_packets, GFP_KERNEL);
+ if (!urb) {
+ em28xx_err("cannot alloc isoc_ctl.urb %i\n", i);
+ em28xx_uninit_isoc(dev, mode);
+ return -ENOMEM;
+ }
+ isoc_bufs->urb[i] = urb;
+
+ isoc_bufs->transfer_buffer[i] = usb_alloc_coherent(dev->udev,
+ sb_size, GFP_KERNEL, &urb->transfer_dma);
+ if (!isoc_bufs->transfer_buffer[i]) {
+ em28xx_err("unable to allocate %i bytes for transfer"
+ " buffer %i%s\n",
+ sb_size, i,
+ in_interrupt() ? " while in int" : "");
+ em28xx_uninit_isoc(dev, mode);
+ return -ENOMEM;
+ }
+ memset(isoc_bufs->transfer_buffer[i], 0, sb_size);
+
+ /* FIXME: this is a hack - should be
+ 'desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK'
+ should also be using 'desc.bInterval'
+ */
+ pipe = usb_rcvisocpipe(dev->udev,
+ mode == EM28XX_ANALOG_MODE ?
+ EM28XX_EP_ANALOG : EM28XX_EP_DIGITAL);
+
+ usb_fill_int_urb(urb, dev->udev, pipe,
+ isoc_bufs->transfer_buffer[i], sb_size,
+ em28xx_irq_callback, dev, 1);
+
+ urb->number_of_packets = isoc_bufs->num_packets;
+ urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+
+ k = 0;
+ for (j = 0; j < isoc_bufs->num_packets; j++) {
+ urb->iso_frame_desc[j].offset = k;
+ urb->iso_frame_desc[j].length =
+ isoc_bufs->max_pkt_size;
+ k += isoc_bufs->max_pkt_size;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(em28xx_alloc_isoc);
+
+/*
+ * Allocate URBs and start IRQ
+ */
+int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode,
+ int max_packets, int num_bufs, int max_pkt_size,
+ int (*isoc_copy) (struct em28xx *dev, struct urb *urb))
+{
+ struct em28xx_dmaqueue *dma_q = &dev->vidq;
+ struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq;
+ struct em28xx_usb_isoc_bufs *isoc_bufs;
+ int i;
+ int rc;
+ int alloc;
+
+ em28xx_isocdbg("em28xx: called em28xx_init_isoc in mode %d\n", mode);
+
+ dev->isoc_ctl.isoc_copy = isoc_copy;
+
+ if (mode == EM28XX_DIGITAL_MODE) {
+ isoc_bufs = &dev->isoc_ctl.digital_bufs;
+ /* no need to free/alloc isoc buffers in digital mode */
+ alloc = 0;
+ } else {
+ isoc_bufs = &dev->isoc_ctl.analog_bufs;
+ alloc = 1;
+ }
+
+ if (alloc) {
+ rc = em28xx_alloc_isoc(dev, mode, max_packets,
+ num_bufs, max_pkt_size);
+ if (rc)
+ return rc;
+ }
+
+ init_waitqueue_head(&dma_q->wq);
+ init_waitqueue_head(&vbi_dma_q->wq);
+
+ em28xx_capture_start(dev, 1);
+
+ /* submit urbs and enables IRQ */
+ for (i = 0; i < isoc_bufs->num_bufs; i++) {
+ rc = usb_submit_urb(isoc_bufs->urb[i], GFP_ATOMIC);
+ if (rc) {
+ em28xx_err("submit of urb %i failed (error=%i)\n", i,
+ rc);
+ em28xx_uninit_isoc(dev, mode);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(em28xx_init_isoc);
+
+/*
+ * em28xx_wake_i2c()
+ * configure i2c attached devices
+ */
+void em28xx_wake_i2c(struct em28xx *dev)
+{
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, reset, 0);
+ v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing,
+ INPUT(dev->ctl_input)->vmux, 0, 0);
+ v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
+}
+
+/*
+ * Device control list
+ */
+
+static LIST_HEAD(em28xx_devlist);
+static DEFINE_MUTEX(em28xx_devlist_mutex);
+
+/*
+ * Extension interface
+ */
+
+static LIST_HEAD(em28xx_extension_devlist);
+
+int em28xx_register_extension(struct em28xx_ops *ops)
+{
+ struct em28xx *dev = NULL;
+
+ mutex_lock(&em28xx_devlist_mutex);
+ list_add_tail(&ops->next, &em28xx_extension_devlist);
+ list_for_each_entry(dev, &em28xx_devlist, devlist) {
+ ops->init(dev);
+ }
+ mutex_unlock(&em28xx_devlist_mutex);
+ printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name);
+ return 0;
+}
+EXPORT_SYMBOL(em28xx_register_extension);
+
+void em28xx_unregister_extension(struct em28xx_ops *ops)
+{
+ struct em28xx *dev = NULL;
+
+ mutex_lock(&em28xx_devlist_mutex);
+ list_for_each_entry(dev, &em28xx_devlist, devlist) {
+ ops->fini(dev);
+ }
+ list_del(&ops->next);
+ mutex_unlock(&em28xx_devlist_mutex);
+ printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name);
+}
+EXPORT_SYMBOL(em28xx_unregister_extension);
+
+void em28xx_init_extension(struct em28xx *dev)
+{
+ const struct em28xx_ops *ops = NULL;
+
+ mutex_lock(&em28xx_devlist_mutex);
+ list_add_tail(&dev->devlist, &em28xx_devlist);
+ list_for_each_entry(ops, &em28xx_extension_devlist, next) {
+ if (ops->init)
+ ops->init(dev);
+ }
+ mutex_unlock(&em28xx_devlist_mutex);
+}
+
+void em28xx_close_extension(struct em28xx *dev)
+{
+ const struct em28xx_ops *ops = NULL;
+
+ mutex_lock(&em28xx_devlist_mutex);
+ list_for_each_entry(ops, &em28xx_extension_devlist, next) {
+ if (ops->fini)
+ ops->fini(dev);
+ }
+ list_del(&dev->devlist);
+ mutex_unlock(&em28xx_devlist_mutex);
+}
diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c
new file mode 100644
index 00000000..503a8d5b
--- /dev/null
+++ b/drivers/media/video/em28xx/em28xx-dvb.c
@@ -0,0 +1,1109 @@
+/*
+ DVB device driver for em28xx
+
+ (c) 2008-2011 Mauro Carvalho Chehab <mchehab@infradead.org>
+
+ (c) 2008 Devin Heitmueller <devin.heitmueller@gmail.com>
+ - Fixes for the driver to properly work with HVR-950
+ - Fixes for the driver to properly work with Pinnacle PCTV HD Pro Stick
+ - Fixes for the driver to properly work with AMD ATI TV Wonder HD 600
+
+ (c) 2008 Aidan Thornton <makosoft@googlemail.com>
+
+ Based on cx88-dvb, saa7134-dvb and videobuf-dvb originally written by:
+ (c) 2004, 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au>
+ (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#include "em28xx.h"
+#include <media/v4l2-common.h>
+#include <media/videobuf-vmalloc.h>
+#include <media/tuner.h>
+#include "tuner-simple.h"
+
+#include "lgdt330x.h"
+#include "lgdt3305.h"
+#include "zl10353.h"
+#include "s5h1409.h"
+#include "mt352.h"
+#include "mt352_priv.h" /* FIXME */
+#include "tda1002x.h"
+#include "tda18271.h"
+#include "s921.h"
+#include "drxd.h"
+#include "cxd2820r.h"
+#include "tda18271c2dd.h"
+#include "drxk.h"
+#include "tda10071.h"
+#include "a8293.h"
+#include "qt1010.h"
+
+MODULE_DESCRIPTION("driver for em28xx based DVB cards");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
+MODULE_LICENSE("GPL");
+
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable debug messages [dvb]");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define dprintk(level, fmt, arg...) do { \
+if (debug >= level) \
+ printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->name, ## arg); \
+} while (0)
+
+struct em28xx_dvb {
+ struct dvb_frontend *fe[2];
+
+ /* feed count management */
+ struct mutex lock;
+ int nfeeds;
+
+ /* general boilerplate stuff */
+ struct dvb_adapter adapter;
+ struct dvb_demux demux;
+ struct dmxdev dmxdev;
+ struct dmx_frontend fe_hw;
+ struct dmx_frontend fe_mem;
+ struct dvb_net net;
+
+ /* Due to DRX-K - probably need changes */
+ int (*gate_ctrl)(struct dvb_frontend *, int);
+ struct semaphore pll_mutex;
+ bool dont_attach_fe1;
+};
+
+
+static inline void print_err_status(struct em28xx *dev,
+ int packet, int status)
+{
+ char *errmsg = "Unknown";
+
+ switch (status) {
+ case -ENOENT:
+ errmsg = "unlinked synchronuously";
+ break;
+ case -ECONNRESET:
+ errmsg = "unlinked asynchronuously";
+ break;
+ case -ENOSR:
+ errmsg = "Buffer error (overrun)";
+ break;
+ case -EPIPE:
+ errmsg = "Stalled (device not responding)";
+ break;
+ case -EOVERFLOW:
+ errmsg = "Babble (bad cable?)";
+ break;
+ case -EPROTO:
+ errmsg = "Bit-stuff error (bad cable?)";
+ break;
+ case -EILSEQ:
+ errmsg = "CRC/Timeout (could be anything)";
+ break;
+ case -ETIME:
+ errmsg = "Device does not respond";
+ break;
+ }
+ if (packet < 0) {
+ dprintk(1, "URB status %d [%s].\n", status, errmsg);
+ } else {
+ dprintk(1, "URB packet %d, status %d [%s].\n",
+ packet, status, errmsg);
+ }
+}
+
+static inline int em28xx_dvb_isoc_copy(struct em28xx *dev, struct urb *urb)
+{
+ int i;
+
+ if (!dev)
+ return 0;
+
+ if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED))
+ return 0;
+
+ if (urb->status < 0) {
+ print_err_status(dev, -1, urb->status);
+ if (urb->status == -ENOENT)
+ return 0;
+ }
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ int status = urb->iso_frame_desc[i].status;
+
+ if (status < 0) {
+ print_err_status(dev, i, status);
+ if (urb->iso_frame_desc[i].status != -EPROTO)
+ continue;
+ }
+
+ dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer +
+ urb->iso_frame_desc[i].offset,
+ urb->iso_frame_desc[i].actual_length);
+ }
+
+ return 0;
+}
+
+static int em28xx_start_streaming(struct em28xx_dvb *dvb)
+{
+ int rc;
+ struct em28xx *dev = dvb->adapter.priv;
+ int max_dvb_packet_size;
+
+ usb_set_interface(dev->udev, 0, dev->dvb_alt);
+ rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
+ if (rc < 0)
+ return rc;
+
+ max_dvb_packet_size = dev->dvb_max_pkt_size;
+ if (max_dvb_packet_size < 0)
+ return max_dvb_packet_size;
+ dprintk(1, "Using %d buffers each with %d x %d bytes\n",
+ EM28XX_DVB_NUM_BUFS,
+ EM28XX_DVB_MAX_PACKETS,
+ max_dvb_packet_size);
+
+ return em28xx_init_isoc(dev, EM28XX_DIGITAL_MODE,
+ EM28XX_DVB_MAX_PACKETS, EM28XX_DVB_NUM_BUFS,
+ max_dvb_packet_size, em28xx_dvb_isoc_copy);
+}
+
+static int em28xx_stop_streaming(struct em28xx_dvb *dvb)
+{
+ struct em28xx *dev = dvb->adapter.priv;
+
+ em28xx_capture_start(dev, 0);
+
+ em28xx_set_mode(dev, EM28XX_SUSPEND);
+
+ return 0;
+}
+
+static int em28xx_start_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct em28xx_dvb *dvb = demux->priv;
+ int rc, ret;
+
+ if (!demux->dmx.frontend)
+ return -EINVAL;
+
+ mutex_lock(&dvb->lock);
+ dvb->nfeeds++;
+ rc = dvb->nfeeds;
+
+ if (dvb->nfeeds == 1) {
+ ret = em28xx_start_streaming(dvb);
+ if (ret < 0)
+ rc = ret;
+ }
+
+ mutex_unlock(&dvb->lock);
+ return rc;
+}
+
+static int em28xx_stop_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct em28xx_dvb *dvb = demux->priv;
+ int err = 0;
+
+ mutex_lock(&dvb->lock);
+ dvb->nfeeds--;
+
+ if (0 == dvb->nfeeds)
+ err = em28xx_stop_streaming(dvb);
+
+ mutex_unlock(&dvb->lock);
+ return err;
+}
+
+
+
+/* ------------------------------------------------------------------ */
+static int em28xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire)
+{
+ struct em28xx *dev = fe->dvb->priv;
+
+ if (acquire)
+ return em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
+ else
+ return em28xx_set_mode(dev, EM28XX_SUSPEND);
+}
+
+/* ------------------------------------------------------------------ */
+
+static struct lgdt330x_config em2880_lgdt3303_dev = {
+ .demod_address = 0x0e,
+ .demod_chip = LGDT3303,
+};
+
+static struct lgdt3305_config em2870_lgdt3304_dev = {
+ .i2c_addr = 0x0e,
+ .demod_chip = LGDT3304,
+ .spectral_inversion = 1,
+ .deny_i2c_rptr = 1,
+ .mpeg_mode = LGDT3305_MPEG_PARALLEL,
+ .tpclk_edge = LGDT3305_TPCLK_FALLING_EDGE,
+ .tpvalid_polarity = LGDT3305_TP_VALID_HIGH,
+ .vsb_if_khz = 3250,
+ .qam_if_khz = 4000,
+};
+
+static struct s921_config sharp_isdbt = {
+ .demod_address = 0x30 >> 1
+};
+
+static struct zl10353_config em28xx_zl10353_with_xc3028 = {
+ .demod_address = (0x1e >> 1),
+ .no_tuner = 1,
+ .parallel_ts = 1,
+ .if2 = 45600,
+};
+
+static struct s5h1409_config em28xx_s5h1409_with_xc3028 = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_PARALLEL_OUTPUT,
+ .gpio = S5H1409_GPIO_OFF,
+ .inversion = S5H1409_INVERSION_OFF,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK
+};
+
+static struct tda18271_std_map kworld_a340_std_map = {
+ .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 0,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 1,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+};
+
+static struct tda18271_config kworld_a340_config = {
+ .std_map = &kworld_a340_std_map,
+};
+
+static struct zl10353_config em28xx_zl10353_xc3028_no_i2c_gate = {
+ .demod_address = (0x1e >> 1),
+ .no_tuner = 1,
+ .disable_i2c_gate_ctrl = 1,
+ .parallel_ts = 1,
+ .if2 = 45600,
+};
+
+static struct drxd_config em28xx_drxd = {
+ .demod_address = 0x70,
+ .demod_revision = 0xa2,
+ .pll_type = DRXD_PLL_NONE,
+ .clock = 12000,
+ .insert_rs_byte = 1,
+ .IF = 42800000,
+ .disable_i2c_gate_ctrl = 1,
+};
+
+struct drxk_config terratec_h5_drxk = {
+ .adr = 0x29,
+ .single_master = 1,
+ .no_i2c_bridge = 1,
+ .microcode_name = "dvb-usb-terratec-h5-drxk.fw",
+};
+
+struct drxk_config hauppauge_930c_drxk = {
+ .adr = 0x29,
+ .single_master = 1,
+ .no_i2c_bridge = 1,
+ .microcode_name = "dvb-usb-hauppauge-hvr930c-drxk.fw",
+ .chunk_size = 56,
+};
+
+struct drxk_config maxmedia_ub425_tc_drxk = {
+ .adr = 0x29,
+ .single_master = 1,
+ .no_i2c_bridge = 1,
+};
+
+struct drxk_config pctv_520e_drxk = {
+ .adr = 0x29,
+ .single_master = 1,
+ .microcode_name = "dvb-demod-drxk-pctv.fw",
+ .chunk_size = 58,
+};
+
+static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct em28xx_dvb *dvb = fe->sec_priv;
+ int status;
+
+ if (!dvb)
+ return -EINVAL;
+
+ if (enable) {
+ down(&dvb->pll_mutex);
+ status = dvb->gate_ctrl(fe, 1);
+ } else {
+ status = dvb->gate_ctrl(fe, 0);
+ up(&dvb->pll_mutex);
+ }
+ return status;
+}
+
+static void hauppauge_hvr930c_init(struct em28xx *dev)
+{
+ int i;
+
+ struct em28xx_reg_seq hauppauge_hvr930c_init[] = {
+ {EM2874_R80_GPIO, 0xff, 0xff, 0x65},
+ {EM2874_R80_GPIO, 0xfb, 0xff, 0x32},
+ {EM2874_R80_GPIO, 0xff, 0xff, 0xb8},
+ { -1, -1, -1, -1},
+ };
+ struct em28xx_reg_seq hauppauge_hvr930c_end[] = {
+ {EM2874_R80_GPIO, 0xef, 0xff, 0x01},
+ {EM2874_R80_GPIO, 0xaf, 0xff, 0x65},
+ {EM2874_R80_GPIO, 0xef, 0xff, 0x76},
+ {EM2874_R80_GPIO, 0xef, 0xff, 0x01},
+ {EM2874_R80_GPIO, 0xcf, 0xff, 0x0b},
+ {EM2874_R80_GPIO, 0xef, 0xff, 0x40},
+
+ {EM2874_R80_GPIO, 0xcf, 0xff, 0x65},
+ {EM2874_R80_GPIO, 0xef, 0xff, 0x65},
+ {EM2874_R80_GPIO, 0xcf, 0xff, 0x0b},
+ {EM2874_R80_GPIO, 0xef, 0xff, 0x65},
+
+ { -1, -1, -1, -1},
+ };
+
+ struct {
+ unsigned char r[4];
+ int len;
+ } regs[] = {
+ {{ 0x06, 0x02, 0x00, 0x31 }, 4},
+ {{ 0x01, 0x02 }, 2},
+ {{ 0x01, 0x02, 0x00, 0xc6 }, 4},
+ {{ 0x01, 0x00 }, 2},
+ {{ 0x01, 0x00, 0xff, 0xaf }, 4},
+ {{ 0x01, 0x00, 0x03, 0xa0 }, 4},
+ {{ 0x01, 0x00 }, 2},
+ {{ 0x01, 0x00, 0x73, 0xaf }, 4},
+ {{ 0x04, 0x00 }, 2},
+ {{ 0x00, 0x04 }, 2},
+ {{ 0x00, 0x04, 0x00, 0x0a }, 4},
+ {{ 0x04, 0x14 }, 2},
+ {{ 0x04, 0x14, 0x00, 0x00 }, 4},
+ };
+
+ em28xx_gpio_set(dev, hauppauge_hvr930c_init);
+ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40);
+ msleep(10);
+ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44);
+ msleep(10);
+
+ dev->i2c_client.addr = 0x82 >> 1;
+
+ for (i = 0; i < ARRAY_SIZE(regs); i++)
+ i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len);
+ em28xx_gpio_set(dev, hauppauge_hvr930c_end);
+
+ msleep(100);
+
+ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44);
+ msleep(30);
+
+ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x45);
+ msleep(10);
+
+}
+
+static void terratec_h5_init(struct em28xx *dev)
+{
+ int i;
+ struct em28xx_reg_seq terratec_h5_init[] = {
+ {EM28XX_R08_GPIO, 0xff, 0xff, 10},
+ {EM2874_R80_GPIO, 0xf6, 0xff, 100},
+ {EM2874_R80_GPIO, 0xf2, 0xff, 50},
+ {EM2874_R80_GPIO, 0xf6, 0xff, 100},
+ { -1, -1, -1, -1},
+ };
+ struct em28xx_reg_seq terratec_h5_end[] = {
+ {EM2874_R80_GPIO, 0xe6, 0xff, 100},
+ {EM2874_R80_GPIO, 0xa6, 0xff, 50},
+ {EM2874_R80_GPIO, 0xe6, 0xff, 100},
+ { -1, -1, -1, -1},
+ };
+ struct {
+ unsigned char r[4];
+ int len;
+ } regs[] = {
+ {{ 0x06, 0x02, 0x00, 0x31 }, 4},
+ {{ 0x01, 0x02 }, 2},
+ {{ 0x01, 0x02, 0x00, 0xc6 }, 4},
+ {{ 0x01, 0x00 }, 2},
+ {{ 0x01, 0x00, 0xff, 0xaf }, 4},
+ {{ 0x01, 0x00, 0x03, 0xa0 }, 4},
+ {{ 0x01, 0x00 }, 2},
+ {{ 0x01, 0x00, 0x73, 0xaf }, 4},
+ {{ 0x04, 0x00 }, 2},
+ {{ 0x00, 0x04 }, 2},
+ {{ 0x00, 0x04, 0x00, 0x0a }, 4},
+ {{ 0x04, 0x14 }, 2},
+ {{ 0x04, 0x14, 0x00, 0x00 }, 4},
+ };
+
+ em28xx_gpio_set(dev, terratec_h5_init);
+ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40);
+ msleep(10);
+ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x45);
+ msleep(10);
+
+ dev->i2c_client.addr = 0x82 >> 1;
+
+ for (i = 0; i < ARRAY_SIZE(regs); i++)
+ i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len);
+ em28xx_gpio_set(dev, terratec_h5_end);
+};
+
+static void pctv_520e_init(struct em28xx *dev)
+{
+ /*
+ * Init TDA8295(?) analog demodulator. Looks like I2C traffic to
+ * digital demodulator and tuner are routed via TDA8295.
+ */
+ int i;
+ struct {
+ unsigned char r[4];
+ int len;
+ } regs[] = {
+ {{ 0x06, 0x02, 0x00, 0x31 }, 4},
+ {{ 0x01, 0x02 }, 2},
+ {{ 0x01, 0x02, 0x00, 0xc6 }, 4},
+ {{ 0x01, 0x00 }, 2},
+ {{ 0x01, 0x00, 0xff, 0xaf }, 4},
+ {{ 0x01, 0x00, 0x03, 0xa0 }, 4},
+ {{ 0x01, 0x00 }, 2},
+ {{ 0x01, 0x00, 0x73, 0xaf }, 4},
+ };
+
+ dev->i2c_client.addr = 0x82 >> 1; /* 0x41 */
+
+ for (i = 0; i < ARRAY_SIZE(regs); i++)
+ i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len);
+};
+
+static int em28xx_mt352_terratec_xs_init(struct dvb_frontend *fe)
+{
+ /* Values extracted from a USB trace of the Terratec Windows driver */
+ static u8 clock_config[] = { CLOCK_CTL, 0x38, 0x2c };
+ static u8 reset[] = { RESET, 0x80 };
+ static u8 adc_ctl_1_cfg[] = { ADC_CTL_1, 0x40 };
+ static u8 agc_cfg[] = { AGC_TARGET, 0x28, 0xa0 };
+ static u8 input_freq_cfg[] = { INPUT_FREQ_1, 0x31, 0xb8 };
+ static u8 rs_err_cfg[] = { RS_ERR_PER_1, 0x00, 0x4d };
+ static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 };
+ static u8 trl_nom_cfg[] = { TRL_NOMINAL_RATE_1, 0x64, 0x00 };
+ static u8 tps_given_cfg[] = { TPS_GIVEN_1, 0x40, 0x80, 0x50 };
+ static u8 tuner_go[] = { TUNER_GO, 0x01};
+
+ mt352_write(fe, clock_config, sizeof(clock_config));
+ udelay(200);
+ mt352_write(fe, reset, sizeof(reset));
+ mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
+ mt352_write(fe, agc_cfg, sizeof(agc_cfg));
+ mt352_write(fe, input_freq_cfg, sizeof(input_freq_cfg));
+ mt352_write(fe, rs_err_cfg, sizeof(rs_err_cfg));
+ mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+ mt352_write(fe, trl_nom_cfg, sizeof(trl_nom_cfg));
+ mt352_write(fe, tps_given_cfg, sizeof(tps_given_cfg));
+ mt352_write(fe, tuner_go, sizeof(tuner_go));
+ return 0;
+}
+
+static struct mt352_config terratec_xs_mt352_cfg = {
+ .demod_address = (0x1e >> 1),
+ .no_tuner = 1,
+ .if2 = 45600,
+ .demod_init = em28xx_mt352_terratec_xs_init,
+};
+
+static struct tda10023_config em28xx_tda10023_config = {
+ .demod_address = 0x0c,
+ .invert = 1,
+};
+
+static struct cxd2820r_config em28xx_cxd2820r_config = {
+ .i2c_address = (0xd8 >> 1),
+ .ts_mode = CXD2820R_TS_SERIAL,
+
+ /* enable LNA for DVB-T2 and DVB-C */
+ .gpio_dvbt2[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L,
+ .gpio_dvbc[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L,
+};
+
+static struct tda18271_config em28xx_cxd2820r_tda18271_config = {
+ .output_opt = TDA18271_OUTPUT_LT_OFF,
+ .gate = TDA18271_GATE_DIGITAL,
+};
+
+static const struct tda10071_config em28xx_tda10071_config = {
+ .i2c_address = 0x55, /* (0xaa >> 1) */
+ .i2c_wr_max = 64,
+ .ts_mode = TDA10071_TS_SERIAL,
+ .spec_inv = 0,
+ .xtal = 40444000, /* 40.444 MHz */
+ .pll_multiplier = 20,
+};
+
+static const struct a8293_config em28xx_a8293_config = {
+ .i2c_addr = 0x08, /* (0x10 >> 1) */
+};
+
+static struct zl10353_config em28xx_zl10353_no_i2c_gate_dev = {
+ .demod_address = (0x1e >> 1),
+ .disable_i2c_gate_ctrl = 1,
+ .no_tuner = 1,
+ .parallel_ts = 1,
+};
+static struct qt1010_config em28xx_qt1010_config = {
+ .i2c_address = 0x62
+
+};
+
+/* ------------------------------------------------------------------ */
+
+static int em28xx_attach_xc3028(u8 addr, struct em28xx *dev)
+{
+ struct dvb_frontend *fe;
+ struct xc2028_config cfg;
+
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.i2c_adap = &dev->i2c_adap;
+ cfg.i2c_addr = addr;
+
+ if (!dev->dvb->fe[0]) {
+ em28xx_errdev("/2: dvb frontend not attached. "
+ "Can't attach xc3028\n");
+ return -EINVAL;
+ }
+
+ fe = dvb_attach(xc2028_attach, dev->dvb->fe[0], &cfg);
+ if (!fe) {
+ em28xx_errdev("/2: xc3028 attach failed\n");
+ dvb_frontend_detach(dev->dvb->fe[0]);
+ dev->dvb->fe[0] = NULL;
+ return -EINVAL;
+ }
+
+ em28xx_info("%s/2: xc3028 attached\n", dev->name);
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int em28xx_register_dvb(struct em28xx_dvb *dvb, struct module *module,
+ struct em28xx *dev, struct device *device)
+{
+ int result;
+
+ mutex_init(&dvb->lock);
+
+ /* register adapter */
+ result = dvb_register_adapter(&dvb->adapter, dev->name, module, device,
+ adapter_nr);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: dvb_register_adapter failed (errno = %d)\n",
+ dev->name, result);
+ goto fail_adapter;
+ }
+
+ /* Ensure all frontends negotiate bus access */
+ dvb->fe[0]->ops.ts_bus_ctrl = em28xx_dvb_bus_ctrl;
+ if (dvb->fe[1])
+ dvb->fe[1]->ops.ts_bus_ctrl = em28xx_dvb_bus_ctrl;
+
+ dvb->adapter.priv = dev;
+
+ /* register frontend */
+ result = dvb_register_frontend(&dvb->adapter, dvb->fe[0]);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: dvb_register_frontend failed (errno = %d)\n",
+ dev->name, result);
+ goto fail_frontend0;
+ }
+
+ /* register 2nd frontend */
+ if (dvb->fe[1]) {
+ result = dvb_register_frontend(&dvb->adapter, dvb->fe[1]);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: 2nd dvb_register_frontend failed (errno = %d)\n",
+ dev->name, result);
+ goto fail_frontend1;
+ }
+ }
+
+ /* register demux stuff */
+ dvb->demux.dmx.capabilities =
+ DMX_TS_FILTERING | DMX_SECTION_FILTERING |
+ DMX_MEMORY_BASED_FILTERING;
+ dvb->demux.priv = dvb;
+ dvb->demux.filternum = 256;
+ dvb->demux.feednum = 256;
+ dvb->demux.start_feed = em28xx_start_feed;
+ dvb->demux.stop_feed = em28xx_stop_feed;
+
+ result = dvb_dmx_init(&dvb->demux);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n",
+ dev->name, result);
+ goto fail_dmx;
+ }
+
+ dvb->dmxdev.filternum = 256;
+ dvb->dmxdev.demux = &dvb->demux.dmx;
+ dvb->dmxdev.capabilities = 0;
+ result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n",
+ dev->name, result);
+ goto fail_dmxdev;
+ }
+
+ dvb->fe_hw.source = DMX_FRONTEND_0;
+ result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n",
+ dev->name, result);
+ goto fail_fe_hw;
+ }
+
+ dvb->fe_mem.source = DMX_MEMORY_FE;
+ result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
+ dev->name, result);
+ goto fail_fe_mem;
+ }
+
+ result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: connect_frontend failed (errno = %d)\n",
+ dev->name, result);
+ goto fail_fe_conn;
+ }
+
+ /* register network adapter */
+ dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx);
+ return 0;
+
+fail_fe_conn:
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+fail_fe_mem:
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+fail_fe_hw:
+ dvb_dmxdev_release(&dvb->dmxdev);
+fail_dmxdev:
+ dvb_dmx_release(&dvb->demux);
+fail_dmx:
+ if (dvb->fe[1])
+ dvb_unregister_frontend(dvb->fe[1]);
+ dvb_unregister_frontend(dvb->fe[0]);
+fail_frontend1:
+ if (dvb->fe[1])
+ dvb_frontend_detach(dvb->fe[1]);
+fail_frontend0:
+ dvb_frontend_detach(dvb->fe[0]);
+ dvb_unregister_adapter(&dvb->adapter);
+fail_adapter:
+ return result;
+}
+
+static void em28xx_unregister_dvb(struct em28xx_dvb *dvb)
+{
+ dvb_net_release(&dvb->net);
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+ dvb_dmxdev_release(&dvb->dmxdev);
+ dvb_dmx_release(&dvb->demux);
+ if (dvb->fe[1])
+ dvb_unregister_frontend(dvb->fe[1]);
+ dvb_unregister_frontend(dvb->fe[0]);
+ if (dvb->fe[1] && !dvb->dont_attach_fe1)
+ dvb_frontend_detach(dvb->fe[1]);
+ dvb_frontend_detach(dvb->fe[0]);
+ dvb_unregister_adapter(&dvb->adapter);
+}
+
+static int em28xx_dvb_init(struct em28xx *dev)
+{
+ int result = 0, mfe_shared = 0;
+ struct em28xx_dvb *dvb;
+
+ if (!dev->board.has_dvb) {
+ /* This device does not support the extension */
+ printk(KERN_INFO "em28xx_dvb: This device does not support the extension\n");
+ return 0;
+ }
+
+ dvb = kzalloc(sizeof(struct em28xx_dvb), GFP_KERNEL);
+
+ if (dvb == NULL) {
+ em28xx_info("em28xx_dvb: memory allocation failed\n");
+ return -ENOMEM;
+ }
+ dev->dvb = dvb;
+ dvb->fe[0] = dvb->fe[1] = NULL;
+
+ mutex_lock(&dev->lock);
+ em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
+ /* init frontend */
+ switch (dev->model) {
+ case EM2874_BOARD_LEADERSHIP_ISDBT:
+ dvb->fe[0] = dvb_attach(s921_attach,
+ &sharp_isdbt, &dev->i2c_adap);
+
+ if (!dvb->fe[0]) {
+ result = -EINVAL;
+ goto out_free;
+ }
+
+ break;
+ case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850:
+ case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
+ case EM2880_BOARD_PINNACLE_PCTV_HD_PRO:
+ case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600:
+ dvb->fe[0] = dvb_attach(lgdt330x_attach,
+ &em2880_lgdt3303_dev,
+ &dev->i2c_adap);
+ if (em28xx_attach_xc3028(0x61, dev) < 0) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ break;
+ case EM2880_BOARD_KWORLD_DVB_310U:
+ dvb->fe[0] = dvb_attach(zl10353_attach,
+ &em28xx_zl10353_with_xc3028,
+ &dev->i2c_adap);
+ if (em28xx_attach_xc3028(0x61, dev) < 0) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ break;
+ case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
+ case EM2882_BOARD_TERRATEC_HYBRID_XS:
+ case EM2880_BOARD_EMPIRE_DUAL_TV:
+ dvb->fe[0] = dvb_attach(zl10353_attach,
+ &em28xx_zl10353_xc3028_no_i2c_gate,
+ &dev->i2c_adap);
+ if (em28xx_attach_xc3028(0x61, dev) < 0) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ break;
+ case EM2880_BOARD_TERRATEC_HYBRID_XS:
+ case EM2880_BOARD_TERRATEC_HYBRID_XS_FR:
+ case EM2881_BOARD_PINNACLE_HYBRID_PRO:
+ case EM2882_BOARD_DIKOM_DK300:
+ case EM2882_BOARD_KWORLD_VS_DVBT:
+ dvb->fe[0] = dvb_attach(zl10353_attach,
+ &em28xx_zl10353_xc3028_no_i2c_gate,
+ &dev->i2c_adap);
+ if (dvb->fe[0] == NULL) {
+ /* This board could have either a zl10353 or a mt352.
+ If the chip id isn't for zl10353, try mt352 */
+ dvb->fe[0] = dvb_attach(mt352_attach,
+ &terratec_xs_mt352_cfg,
+ &dev->i2c_adap);
+ }
+
+ if (em28xx_attach_xc3028(0x61, dev) < 0) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ break;
+ case EM2870_BOARD_KWORLD_355U:
+ dvb->fe[0] = dvb_attach(zl10353_attach,
+ &em28xx_zl10353_no_i2c_gate_dev,
+ &dev->i2c_adap);
+ if (dvb->fe[0] != NULL)
+ dvb_attach(qt1010_attach, dvb->fe[0],
+ &dev->i2c_adap, &em28xx_qt1010_config);
+ break;
+ case EM2883_BOARD_KWORLD_HYBRID_330U:
+ case EM2882_BOARD_EVGA_INDTUBE:
+ dvb->fe[0] = dvb_attach(s5h1409_attach,
+ &em28xx_s5h1409_with_xc3028,
+ &dev->i2c_adap);
+ if (em28xx_attach_xc3028(0x61, dev) < 0) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ break;
+ case EM2882_BOARD_KWORLD_ATSC_315U:
+ dvb->fe[0] = dvb_attach(lgdt330x_attach,
+ &em2880_lgdt3303_dev,
+ &dev->i2c_adap);
+ if (dvb->fe[0] != NULL) {
+ if (!dvb_attach(simple_tuner_attach, dvb->fe[0],
+ &dev->i2c_adap, 0x61, TUNER_THOMSON_DTT761X)) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ }
+ break;
+ case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
+ case EM2882_BOARD_PINNACLE_HYBRID_PRO_330E:
+ dvb->fe[0] = dvb_attach(drxd_attach, &em28xx_drxd, NULL,
+ &dev->i2c_adap, &dev->udev->dev);
+ if (em28xx_attach_xc3028(0x61, dev) < 0) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ break;
+ case EM2870_BOARD_REDDO_DVB_C_USB_BOX:
+ /* Philips CU1216L NIM (Philips TDA10023 + Infineon TUA6034) */
+ dvb->fe[0] = dvb_attach(tda10023_attach,
+ &em28xx_tda10023_config,
+ &dev->i2c_adap, 0x48);
+ if (dvb->fe[0]) {
+ if (!dvb_attach(simple_tuner_attach, dvb->fe[0],
+ &dev->i2c_adap, 0x60, TUNER_PHILIPS_CU1216L)) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ }
+ break;
+ case EM2870_BOARD_KWORLD_A340:
+ dvb->fe[0] = dvb_attach(lgdt3305_attach,
+ &em2870_lgdt3304_dev,
+ &dev->i2c_adap);
+ if (dvb->fe[0] != NULL)
+ dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
+ &dev->i2c_adap, &kworld_a340_config);
+ break;
+ case EM28174_BOARD_PCTV_290E:
+ dvb->fe[0] = dvb_attach(cxd2820r_attach,
+ &em28xx_cxd2820r_config,
+ &dev->i2c_adap);
+ if (dvb->fe[0]) {
+ /* FE 0 attach tuner */
+ if (!dvb_attach(tda18271_attach,
+ dvb->fe[0],
+ 0x60,
+ &dev->i2c_adap,
+ &em28xx_cxd2820r_tda18271_config)) {
+
+ dvb_frontend_detach(dvb->fe[0]);
+ result = -EINVAL;
+ goto out_free;
+ }
+ }
+ break;
+ case EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C:
+ {
+ struct xc5000_config cfg;
+ hauppauge_hvr930c_init(dev);
+
+ dvb->fe[0] = dvb_attach(drxk_attach,
+ &hauppauge_930c_drxk, &dev->i2c_adap);
+ if (!dvb->fe[0]) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ /* FIXME: do we need a pll semaphore? */
+ dvb->fe[0]->sec_priv = dvb;
+ sema_init(&dvb->pll_mutex, 1);
+ dvb->gate_ctrl = dvb->fe[0]->ops.i2c_gate_ctrl;
+ dvb->fe[0]->ops.i2c_gate_ctrl = drxk_gate_ctrl;
+
+ /* Attach xc5000 */
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.i2c_address = 0x61;
+ cfg.if_khz = 4000;
+
+ if (dvb->fe[0]->ops.i2c_gate_ctrl)
+ dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 1);
+ if (!dvb_attach(xc5000_attach, dvb->fe[0], &dev->i2c_adap,
+ &cfg)) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ if (dvb->fe[0]->ops.i2c_gate_ctrl)
+ dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 0);
+
+ break;
+ }
+ case EM2884_BOARD_TERRATEC_H5:
+ case EM2884_BOARD_CINERGY_HTC_STICK:
+ terratec_h5_init(dev);
+
+ dvb->fe[0] = dvb_attach(drxk_attach, &terratec_h5_drxk, &dev->i2c_adap);
+ if (!dvb->fe[0]) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ /* FIXME: do we need a pll semaphore? */
+ dvb->fe[0]->sec_priv = dvb;
+ sema_init(&dvb->pll_mutex, 1);
+ dvb->gate_ctrl = dvb->fe[0]->ops.i2c_gate_ctrl;
+ dvb->fe[0]->ops.i2c_gate_ctrl = drxk_gate_ctrl;
+
+ /* Attach tda18271 to DVB-C frontend */
+ if (dvb->fe[0]->ops.i2c_gate_ctrl)
+ dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 1);
+ if (!dvb_attach(tda18271c2dd_attach, dvb->fe[0], &dev->i2c_adap, 0x60)) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ if (dvb->fe[0]->ops.i2c_gate_ctrl)
+ dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 0);
+
+ break;
+ case EM28174_BOARD_PCTV_460E:
+ /* attach demod */
+ dvb->fe[0] = dvb_attach(tda10071_attach,
+ &em28xx_tda10071_config, &dev->i2c_adap);
+
+ /* attach SEC */
+ if (dvb->fe[0])
+ dvb_attach(a8293_attach, dvb->fe[0], &dev->i2c_adap,
+ &em28xx_a8293_config);
+ break;
+ case EM2874_BOARD_MAXMEDIA_UB425_TC:
+ /* attach demodulator */
+ dvb->fe[0] = dvb_attach(drxk_attach, &maxmedia_ub425_tc_drxk,
+ &dev->i2c_adap);
+
+ if (dvb->fe[0]) {
+ /* disable I2C-gate */
+ dvb->fe[0]->ops.i2c_gate_ctrl = NULL;
+
+ /* attach tuner */
+ if (!dvb_attach(tda18271c2dd_attach, dvb->fe[0],
+ &dev->i2c_adap, 0x60)) {
+ dvb_frontend_detach(dvb->fe[0]);
+ result = -EINVAL;
+ goto out_free;
+ }
+ }
+
+ /* TODO: we need drx-3913k firmware in order to support DVB-T */
+ em28xx_info("MaxMedia UB425-TC: only DVB-C supported by that " \
+ "driver version\n");
+
+ break;
+ case EM2884_BOARD_PCTV_510E:
+ case EM2884_BOARD_PCTV_520E:
+ pctv_520e_init(dev);
+
+ /* attach demodulator */
+ dvb->fe[0] = dvb_attach(drxk_attach, &pctv_520e_drxk,
+ &dev->i2c_adap);
+
+ if (dvb->fe[0]) {
+ /* attach tuner */
+ if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
+ &dev->i2c_adap,
+ &em28xx_cxd2820r_tda18271_config)) {
+ dvb_frontend_detach(dvb->fe[0]);
+ result = -EINVAL;
+ goto out_free;
+ }
+ }
+ break;
+ default:
+ em28xx_errdev("/2: The frontend of your DVB/ATSC card"
+ " isn't supported yet\n");
+ break;
+ }
+ if (NULL == dvb->fe[0]) {
+ em28xx_errdev("/2: frontend initialization failed\n");
+ result = -EINVAL;
+ goto out_free;
+ }
+ /* define general-purpose callback pointer */
+ dvb->fe[0]->callback = em28xx_tuner_callback;
+ if (dvb->fe[1])
+ dvb->fe[1]->callback = em28xx_tuner_callback;
+
+ /* register everything */
+ result = em28xx_register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev);
+
+ if (result < 0)
+ goto out_free;
+
+ /* MFE lock */
+ dvb->adapter.mfe_shared = mfe_shared;
+
+ em28xx_info("Successfully loaded em28xx-dvb\n");
+ret:
+ em28xx_set_mode(dev, EM28XX_SUSPEND);
+ mutex_unlock(&dev->lock);
+ return result;
+
+out_free:
+ kfree(dvb);
+ dev->dvb = NULL;
+ goto ret;
+}
+
+static inline void prevent_sleep(struct dvb_frontend_ops *ops)
+{
+ ops->set_voltage = NULL;
+ ops->sleep = NULL;
+ ops->tuner_ops.sleep = NULL;
+}
+
+static int em28xx_dvb_fini(struct em28xx *dev)
+{
+ if (!dev->board.has_dvb) {
+ /* This device does not support the extension */
+ return 0;
+ }
+
+ if (dev->dvb) {
+ struct em28xx_dvb *dvb = dev->dvb;
+
+ if (dev->state & DEV_DISCONNECTED) {
+ /* We cannot tell the device to sleep
+ * once it has been unplugged. */
+ if (dvb->fe[0])
+ prevent_sleep(&dvb->fe[0]->ops);
+ if (dvb->fe[1])
+ prevent_sleep(&dvb->fe[1]->ops);
+ }
+
+ em28xx_unregister_dvb(dvb);
+ kfree(dvb);
+ dev->dvb = NULL;
+ }
+
+ return 0;
+}
+
+static struct em28xx_ops dvb_ops = {
+ .id = EM28XX_DVB,
+ .name = "Em28xx dvb Extension",
+ .init = em28xx_dvb_init,
+ .fini = em28xx_dvb_fini,
+};
+
+static int __init em28xx_dvb_register(void)
+{
+ return em28xx_register_extension(&dvb_ops);
+}
+
+static void __exit em28xx_dvb_unregister(void)
+{
+ em28xx_unregister_extension(&dvb_ops);
+}
+
+module_init(em28xx_dvb_register);
+module_exit(em28xx_dvb_unregister);
diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c
new file mode 100644
index 00000000..a88e169d
--- /dev/null
+++ b/drivers/media/video/em28xx/em28xx-i2c.c
@@ -0,0 +1,570 @@
+/*
+ em28xx-i2c.c - driver for Empia EM2800/EM2820/2840 USB video capture devices
+
+ Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
+ Markus Rechberger <mrechberger@gmail.com>
+ Mauro Carvalho Chehab <mchehab@infradead.org>
+ Sascha Sommer <saschasommer@freenet.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+
+#include "em28xx.h"
+#include "tuner-xc2028.h"
+#include <media/v4l2-common.h>
+#include <media/tuner.h>
+
+/* ----------------------------------------------------------- */
+
+static unsigned int i2c_scan;
+module_param(i2c_scan, int, 0444);
+MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time");
+
+static unsigned int i2c_debug;
+module_param(i2c_debug, int, 0644);
+MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
+
+#define dprintk2(lvl, fmt, args...) \
+do { \
+ if (i2c_debug >= lvl) { \
+ printk(KERN_DEBUG "%s at %s: " fmt, \
+ dev->name, __func__ , ##args); \
+ } \
+} while (0)
+
+/*
+ * em2800_i2c_send_max4()
+ * send up to 4 bytes to the i2c device
+ */
+static int em2800_i2c_send_max4(struct em28xx *dev, unsigned char addr,
+ char *buf, int len)
+{
+ int ret;
+ int write_timeout;
+ unsigned char b2[6];
+ BUG_ON(len < 1 || len > 4);
+ b2[5] = 0x80 + len - 1;
+ b2[4] = addr;
+ b2[3] = buf[0];
+ if (len > 1)
+ b2[2] = buf[1];
+ if (len > 2)
+ b2[1] = buf[2];
+ if (len > 3)
+ b2[0] = buf[3];
+
+ ret = dev->em28xx_write_regs(dev, 4 - len, &b2[4 - len], 2 + len);
+ if (ret != 2 + len) {
+ em28xx_warn("writing to i2c device failed (error=%i)\n", ret);
+ return -EIO;
+ }
+ for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0;
+ write_timeout -= 5) {
+ ret = dev->em28xx_read_reg(dev, 0x05);
+ if (ret == 0x80 + len - 1)
+ return len;
+ msleep(5);
+ }
+ em28xx_warn("i2c write timed out\n");
+ return -EIO;
+}
+
+/*
+ * em2800_i2c_send_bytes()
+ */
+static int em2800_i2c_send_bytes(void *data, unsigned char addr, char *buf,
+ short len)
+{
+ char *bufPtr = buf;
+ int ret;
+ int wrcount = 0;
+ int count;
+ int maxLen = 4;
+ struct em28xx *dev = (struct em28xx *)data;
+ while (len > 0) {
+ count = (len > maxLen) ? maxLen : len;
+ ret = em2800_i2c_send_max4(dev, addr, bufPtr, count);
+ if (ret > 0) {
+ len -= count;
+ bufPtr += count;
+ wrcount += count;
+ } else
+ return (ret < 0) ? ret : -EFAULT;
+ }
+ return wrcount;
+}
+
+/*
+ * em2800_i2c_check_for_device()
+ * check if there is a i2c_device at the supplied address
+ */
+static int em2800_i2c_check_for_device(struct em28xx *dev, unsigned char addr)
+{
+ char msg;
+ int ret;
+ int write_timeout;
+ msg = addr;
+ ret = dev->em28xx_write_regs(dev, 0x04, &msg, 1);
+ if (ret < 0) {
+ em28xx_warn("setting i2c device address failed (error=%i)\n",
+ ret);
+ return ret;
+ }
+ msg = 0x84;
+ ret = dev->em28xx_write_regs(dev, 0x05, &msg, 1);
+ if (ret < 0) {
+ em28xx_warn("preparing i2c read failed (error=%i)\n", ret);
+ return ret;
+ }
+ for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0;
+ write_timeout -= 5) {
+ unsigned reg = dev->em28xx_read_reg(dev, 0x5);
+
+ if (reg == 0x94)
+ return -ENODEV;
+ else if (reg == 0x84)
+ return 0;
+ msleep(5);
+ }
+ return -ENODEV;
+}
+
+/*
+ * em2800_i2c_recv_bytes()
+ * read from the i2c device
+ */
+static int em2800_i2c_recv_bytes(struct em28xx *dev, unsigned char addr,
+ char *buf, int len)
+{
+ int ret;
+ /* check for the device and set i2c read address */
+ ret = em2800_i2c_check_for_device(dev, addr);
+ if (ret) {
+ em28xx_warn
+ ("preparing read at i2c address 0x%x failed (error=%i)\n",
+ addr, ret);
+ return ret;
+ }
+ ret = dev->em28xx_read_reg_req_len(dev, 0x0, 0x3, buf, len);
+ if (ret < 0) {
+ em28xx_warn("reading from i2c device at 0x%x failed (error=%i)",
+ addr, ret);
+ return ret;
+ }
+ return ret;
+}
+
+/*
+ * em28xx_i2c_send_bytes()
+ */
+static int em28xx_i2c_send_bytes(void *data, unsigned char addr, char *buf,
+ short len, int stop)
+{
+ int wrcount = 0;
+ struct em28xx *dev = (struct em28xx *)data;
+ int write_timeout, ret;
+
+ wrcount = dev->em28xx_write_regs_req(dev, stop ? 2 : 3, addr, buf, len);
+
+ /* Seems to be required after a write */
+ for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0;
+ write_timeout -= 5) {
+ ret = dev->em28xx_read_reg(dev, 0x05);
+ if (!ret)
+ break;
+ msleep(5);
+ }
+
+ return wrcount;
+}
+
+/*
+ * em28xx_i2c_recv_bytes()
+ * read a byte from the i2c device
+ */
+static int em28xx_i2c_recv_bytes(struct em28xx *dev, unsigned char addr,
+ char *buf, int len)
+{
+ int ret;
+ ret = dev->em28xx_read_reg_req_len(dev, 2, addr, buf, len);
+ if (ret < 0) {
+ em28xx_warn("reading i2c device failed (error=%i)\n", ret);
+ return ret;
+ }
+ if (dev->em28xx_read_reg(dev, 0x5) != 0)
+ return -ENODEV;
+ return ret;
+}
+
+/*
+ * em28xx_i2c_check_for_device()
+ * check if there is a i2c_device at the supplied address
+ */
+static int em28xx_i2c_check_for_device(struct em28xx *dev, unsigned char addr)
+{
+ int ret;
+
+ ret = dev->em28xx_read_reg_req(dev, 2, addr);
+ if (ret < 0) {
+ em28xx_warn("reading from i2c device failed (error=%i)\n", ret);
+ return ret;
+ }
+ if (dev->em28xx_read_reg(dev, 0x5) != 0)
+ return -ENODEV;
+ return 0;
+}
+
+/*
+ * em28xx_i2c_xfer()
+ * the main i2c transfer function
+ */
+static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg msgs[], int num)
+{
+ struct em28xx *dev = i2c_adap->algo_data;
+ int addr, rc, i, byte;
+
+ if (num <= 0)
+ return 0;
+ for (i = 0; i < num; i++) {
+ addr = msgs[i].addr << 1;
+ dprintk2(2, "%s %s addr=%x len=%d:",
+ (msgs[i].flags & I2C_M_RD) ? "read" : "write",
+ i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len);
+ if (!msgs[i].len) { /* no len: check only for device presence */
+ if (dev->board.is_em2800)
+ rc = em2800_i2c_check_for_device(dev, addr);
+ else
+ rc = em28xx_i2c_check_for_device(dev, addr);
+ if (rc < 0) {
+ dprintk2(2, " no device\n");
+ return rc;
+ }
+
+ } else if (msgs[i].flags & I2C_M_RD) {
+ /* read bytes */
+ if (dev->board.is_em2800)
+ rc = em2800_i2c_recv_bytes(dev, addr,
+ msgs[i].buf,
+ msgs[i].len);
+ else
+ rc = em28xx_i2c_recv_bytes(dev, addr,
+ msgs[i].buf,
+ msgs[i].len);
+ if (i2c_debug >= 2) {
+ for (byte = 0; byte < msgs[i].len; byte++)
+ printk(" %02x", msgs[i].buf[byte]);
+ }
+ } else {
+ /* write bytes */
+ if (i2c_debug >= 2) {
+ for (byte = 0; byte < msgs[i].len; byte++)
+ printk(" %02x", msgs[i].buf[byte]);
+ }
+ if (dev->board.is_em2800)
+ rc = em2800_i2c_send_bytes(dev, addr,
+ msgs[i].buf,
+ msgs[i].len);
+ else
+ rc = em28xx_i2c_send_bytes(dev, addr,
+ msgs[i].buf,
+ msgs[i].len,
+ i == num - 1);
+ }
+ if (rc < 0)
+ goto err;
+ if (i2c_debug >= 2)
+ printk("\n");
+ }
+
+ return num;
+err:
+ dprintk2(2, " ERROR: %i\n", rc);
+ return rc;
+}
+
+/* based on linux/sunrpc/svcauth.h and linux/hash.h
+ * The original hash function returns a different value, if arch is x86_64
+ * or i386.
+ */
+static inline unsigned long em28xx_hash_mem(char *buf, int length, int bits)
+{
+ unsigned long hash = 0;
+ unsigned long l = 0;
+ int len = 0;
+ unsigned char c;
+ do {
+ if (len == length) {
+ c = (char)len;
+ len = -1;
+ } else
+ c = *buf++;
+ l = (l << 8) | c;
+ len++;
+ if ((len & (32 / 8 - 1)) == 0)
+ hash = ((hash^l) * 0x9e370001UL);
+ } while (len);
+
+ return (hash >> (32 - bits)) & 0xffffffffUL;
+}
+
+static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len)
+{
+ unsigned char buf, *p = eedata;
+ struct em28xx_eeprom *em_eeprom = (void *)eedata;
+ int i, err, size = len, block;
+
+ if (dev->chip_id == CHIP_ID_EM2874 ||
+ dev->chip_id == CHIP_ID_EM28174 ||
+ dev->chip_id == CHIP_ID_EM2884) {
+ /* Empia switched to a 16-bit addressable eeprom in newer
+ devices. While we could certainly write a routine to read
+ the eeprom, there is nothing of use in there that cannot be
+ accessed through registers, and there is the risk that we
+ could corrupt the eeprom (since a 16-bit read call is
+ interpreted as a write call by 8-bit eeproms).
+ */
+ return 0;
+ }
+
+ dev->i2c_client.addr = 0xa0 >> 1;
+
+ /* Check if board has eeprom */
+ err = i2c_master_recv(&dev->i2c_client, &buf, 0);
+ if (err < 0) {
+ em28xx_errdev("board has no eeprom\n");
+ memset(eedata, 0, len);
+ return -ENODEV;
+ }
+
+ buf = 0;
+
+ err = i2c_master_send(&dev->i2c_client, &buf, 1);
+ if (err != 1) {
+ printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n",
+ dev->name, err);
+ return err;
+ }
+ while (size > 0) {
+ if (size > 16)
+ block = 16;
+ else
+ block = size;
+
+ if (block !=
+ (err = i2c_master_recv(&dev->i2c_client, p, block))) {
+ printk(KERN_WARNING
+ "%s: i2c eeprom read error (err=%d)\n",
+ dev->name, err);
+ return err;
+ }
+ size -= block;
+ p += block;
+ }
+ for (i = 0; i < len; i++) {
+ if (0 == (i % 16))
+ printk(KERN_INFO "%s: i2c eeprom %02x:", dev->name, i);
+ printk(" %02x", eedata[i]);
+ if (15 == (i % 16))
+ printk("\n");
+ }
+
+ if (em_eeprom->id == 0x9567eb1a)
+ dev->hash = em28xx_hash_mem(eedata, len, 32);
+
+ printk(KERN_INFO "%s: EEPROM ID= 0x%08x, EEPROM hash = 0x%08lx\n",
+ dev->name, em_eeprom->id, dev->hash);
+
+ printk(KERN_INFO "%s: EEPROM info:\n", dev->name);
+
+ switch (em_eeprom->chip_conf >> 4 & 0x3) {
+ case 0:
+ printk(KERN_INFO "%s:\tNo audio on board.\n", dev->name);
+ break;
+ case 1:
+ printk(KERN_INFO "%s:\tAC97 audio (5 sample rates)\n",
+ dev->name);
+ break;
+ case 2:
+ printk(KERN_INFO "%s:\tI2S audio, sample rate=32k\n",
+ dev->name);
+ break;
+ case 3:
+ printk(KERN_INFO "%s:\tI2S audio, 3 sample rates\n",
+ dev->name);
+ break;
+ }
+
+ if (em_eeprom->chip_conf & 1 << 3)
+ printk(KERN_INFO "%s:\tUSB Remote wakeup capable\n", dev->name);
+
+ if (em_eeprom->chip_conf & 1 << 2)
+ printk(KERN_INFO "%s:\tUSB Self power capable\n", dev->name);
+
+ switch (em_eeprom->chip_conf & 0x3) {
+ case 0:
+ printk(KERN_INFO "%s:\t500mA max power\n", dev->name);
+ break;
+ case 1:
+ printk(KERN_INFO "%s:\t400mA max power\n", dev->name);
+ break;
+ case 2:
+ printk(KERN_INFO "%s:\t300mA max power\n", dev->name);
+ break;
+ case 3:
+ printk(KERN_INFO "%s:\t200mA max power\n", dev->name);
+ break;
+ }
+ printk(KERN_INFO "%s:\tTable at 0x%02x, strings=0x%04x, 0x%04x, 0x%04x\n",
+ dev->name,
+ em_eeprom->string_idx_table,
+ em_eeprom->string1,
+ em_eeprom->string2,
+ em_eeprom->string3);
+
+ return 0;
+}
+
+/* ----------------------------------------------------------- */
+
+/*
+ * functionality()
+ */
+static u32 functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_EMUL;
+}
+
+static struct i2c_algorithm em28xx_algo = {
+ .master_xfer = em28xx_i2c_xfer,
+ .functionality = functionality,
+};
+
+static struct i2c_adapter em28xx_adap_template = {
+ .owner = THIS_MODULE,
+ .name = "em28xx",
+ .algo = &em28xx_algo,
+};
+
+static struct i2c_client em28xx_client_template = {
+ .name = "em28xx internal",
+};
+
+/* ----------------------------------------------------------- */
+
+/*
+ * i2c_devs
+ * incomplete list of known devices
+ */
+static char *i2c_devs[128] = {
+ [0x4a >> 1] = "saa7113h",
+ [0x60 >> 1] = "remote IR sensor",
+ [0x8e >> 1] = "remote IR sensor",
+ [0x86 >> 1] = "tda9887",
+ [0x80 >> 1] = "msp34xx",
+ [0x88 >> 1] = "msp34xx",
+ [0xa0 >> 1] = "eeprom",
+ [0xb0 >> 1] = "tda9874",
+ [0xb8 >> 1] = "tvp5150a",
+ [0xba >> 1] = "webcam sensor or tvp5150a",
+ [0xc0 >> 1] = "tuner (analog)",
+ [0xc2 >> 1] = "tuner (analog)",
+ [0xc4 >> 1] = "tuner (analog)",
+ [0xc6 >> 1] = "tuner (analog)",
+};
+
+/*
+ * do_i2c_scan()
+ * check i2c address range for devices
+ */
+void em28xx_do_i2c_scan(struct em28xx *dev)
+{
+ u8 i2c_devicelist[128];
+ unsigned char buf;
+ int i, rc;
+
+ memset(i2c_devicelist, 0, ARRAY_SIZE(i2c_devicelist));
+
+ for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) {
+ dev->i2c_client.addr = i;
+ rc = i2c_master_recv(&dev->i2c_client, &buf, 0);
+ if (rc < 0)
+ continue;
+ i2c_devicelist[i] = i;
+ printk(KERN_INFO "%s: found i2c device @ 0x%x [%s]\n",
+ dev->name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
+ }
+
+ dev->i2c_hash = em28xx_hash_mem(i2c_devicelist,
+ ARRAY_SIZE(i2c_devicelist), 32);
+}
+
+/*
+ * em28xx_i2c_register()
+ * register i2c bus
+ */
+int em28xx_i2c_register(struct em28xx *dev)
+{
+ int retval;
+
+ BUG_ON(!dev->em28xx_write_regs || !dev->em28xx_read_reg);
+ BUG_ON(!dev->em28xx_write_regs_req || !dev->em28xx_read_reg_req);
+ dev->i2c_adap = em28xx_adap_template;
+ dev->i2c_adap.dev.parent = &dev->udev->dev;
+ strcpy(dev->i2c_adap.name, dev->name);
+ dev->i2c_adap.algo_data = dev;
+ i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev);
+
+ retval = i2c_add_adapter(&dev->i2c_adap);
+ if (retval < 0) {
+ em28xx_errdev("%s: i2c_add_adapter failed! retval [%d]\n",
+ __func__, retval);
+ return retval;
+ }
+
+ dev->i2c_client = em28xx_client_template;
+ dev->i2c_client.adapter = &dev->i2c_adap;
+
+ retval = em28xx_i2c_eeprom(dev, dev->eedata, sizeof(dev->eedata));
+ if ((retval < 0) && (retval != -ENODEV)) {
+ em28xx_errdev("%s: em28xx_i2_eeprom failed! retval [%d]\n",
+ __func__, retval);
+
+ return retval;
+ }
+
+ if (i2c_scan)
+ em28xx_do_i2c_scan(dev);
+
+ /* Instantiate the IR receiver device, if present */
+ em28xx_register_i2c_ir(dev);
+
+ return 0;
+}
+
+/*
+ * em28xx_i2c_unregister()
+ * unregister i2c_bus
+ */
+int em28xx_i2c_unregister(struct em28xx *dev)
+{
+ i2c_del_adapter(&dev->i2c_adap);
+ return 0;
+}
diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c
new file mode 100644
index 00000000..2630b265
--- /dev/null
+++ b/drivers/media/video/em28xx/em28xx-input.c
@@ -0,0 +1,563 @@
+/*
+ handle em28xx IR remotes via linux kernel input layer.
+
+ Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
+ Markus Rechberger <mrechberger@gmail.com>
+ Mauro Carvalho Chehab <mchehab@infradead.org>
+ Sascha Sommer <saschasommer@freenet.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+#include <linux/slab.h>
+
+#include "em28xx.h"
+
+#define EM28XX_SNAPSHOT_KEY KEY_CAMERA
+#define EM28XX_SBUTTON_QUERY_INTERVAL 500
+#define EM28XX_R0C_USBSUSP_SNAPSHOT 0x20
+
+static unsigned int ir_debug;
+module_param(ir_debug, int, 0644);
+MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]");
+
+#define MODULE_NAME "em28xx"
+
+#define i2cdprintk(fmt, arg...) \
+ if (ir_debug) { \
+ printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \
+ }
+
+#define dprintk(fmt, arg...) \
+ if (ir_debug) { \
+ printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \
+ }
+
+/**********************************************************
+ Polling structure used by em28xx IR's
+ **********************************************************/
+
+struct em28xx_ir_poll_result {
+ unsigned int toggle_bit:1;
+ unsigned int read_count:7;
+ u8 rc_address;
+ u8 rc_data[4]; /* 1 byte on em2860/2880, 4 on em2874 */
+};
+
+struct em28xx_IR {
+ struct em28xx *dev;
+ struct rc_dev *rc;
+ char name[32];
+ char phys[32];
+
+ /* poll external decoder */
+ int polling;
+ struct delayed_work work;
+ unsigned int full_code:1;
+ unsigned int last_readcount;
+
+ int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *);
+};
+
+/**********************************************************
+ I2C IR based get keycodes - should be used with ir-kbd-i2c
+ **********************************************************/
+
+int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+ unsigned char b;
+
+ /* poll IR chip */
+ if (1 != i2c_master_recv(ir->c, &b, 1)) {
+ i2cdprintk("read error\n");
+ return -EIO;
+ }
+
+ /* it seems that 0xFE indicates that a button is still hold
+ down, while 0xff indicates that no button is hold
+ down. 0xfe sequences are sometimes interrupted by 0xFF */
+
+ i2cdprintk("key %02x\n", b);
+
+ if (b == 0xff)
+ return 0;
+
+ if (b == 0xfe)
+ /* keep old data */
+ return 1;
+
+ *ir_key = b;
+ *ir_raw = b;
+ return 1;
+}
+
+int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+ unsigned char buf[2];
+ u16 code;
+ int size;
+
+ /* poll IR chip */
+ size = i2c_master_recv(ir->c, buf, sizeof(buf));
+
+ if (size != 2)
+ return -EIO;
+
+ /* Does eliminate repeated parity code */
+ if (buf[1] == 0xff)
+ return 0;
+
+ ir->old = buf[1];
+
+ /*
+ * Rearranges bits to the right order.
+ * The bit order were determined experimentally by using
+ * The original Hauppauge Grey IR and another RC5 that uses addr=0x08
+ * The RC5 code has 14 bits, but we've experimentally determined
+ * the meaning for only 11 bits.
+ * So, the code translation is not complete. Yet, it is enough to
+ * work with the provided RC5 IR.
+ */
+ code =
+ ((buf[0] & 0x01) ? 0x0020 : 0) | /* 0010 0000 */
+ ((buf[0] & 0x02) ? 0x0010 : 0) | /* 0001 0000 */
+ ((buf[0] & 0x04) ? 0x0008 : 0) | /* 0000 1000 */
+ ((buf[0] & 0x08) ? 0x0004 : 0) | /* 0000 0100 */
+ ((buf[0] & 0x10) ? 0x0002 : 0) | /* 0000 0010 */
+ ((buf[0] & 0x20) ? 0x0001 : 0) | /* 0000 0001 */
+ ((buf[1] & 0x08) ? 0x1000 : 0) | /* 0001 0000 */
+ ((buf[1] & 0x10) ? 0x0800 : 0) | /* 0000 1000 */
+ ((buf[1] & 0x20) ? 0x0400 : 0) | /* 0000 0100 */
+ ((buf[1] & 0x40) ? 0x0200 : 0) | /* 0000 0010 */
+ ((buf[1] & 0x80) ? 0x0100 : 0); /* 0000 0001 */
+
+ i2cdprintk("ir hauppauge (em2840): code=0x%02x (rcv=0x%02x%02x)\n",
+ code, buf[1], buf[0]);
+
+ /* return key */
+ *ir_key = code;
+ *ir_raw = code;
+ return 1;
+}
+
+int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
+ u32 *ir_raw)
+{
+ unsigned char buf[3];
+
+ /* poll IR chip */
+
+ if (3 != i2c_master_recv(ir->c, buf, 3)) {
+ i2cdprintk("read error\n");
+ return -EIO;
+ }
+
+ i2cdprintk("key %02x\n", buf[2]&0x3f);
+ if (buf[0] != 0x00)
+ return 0;
+
+ *ir_key = buf[2]&0x3f;
+ *ir_raw = buf[2]&0x3f;
+
+ return 1;
+}
+
+int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+ unsigned char subaddr, keydetect, key;
+
+ struct i2c_msg msg[] = { { .addr = ir->c->addr, .flags = 0, .buf = &subaddr, .len = 1},
+
+ { .addr = ir->c->addr, .flags = I2C_M_RD, .buf = &keydetect, .len = 1} };
+
+ subaddr = 0x10;
+ if (2 != i2c_transfer(ir->c->adapter, msg, 2)) {
+ i2cdprintk("read error\n");
+ return -EIO;
+ }
+ if (keydetect == 0x00)
+ return 0;
+
+ subaddr = 0x00;
+ msg[1].buf = &key;
+ if (2 != i2c_transfer(ir->c->adapter, msg, 2)) {
+ i2cdprintk("read error\n");
+ return -EIO;
+ }
+ if (key == 0x00)
+ return 0;
+
+ *ir_key = key;
+ *ir_raw = key;
+ return 1;
+}
+
+/**********************************************************
+ Poll based get keycode functions
+ **********************************************************/
+
+/* This is for the em2860/em2880 */
+static int default_polling_getkey(struct em28xx_IR *ir,
+ struct em28xx_ir_poll_result *poll_result)
+{
+ struct em28xx *dev = ir->dev;
+ int rc;
+ u8 msg[3] = { 0, 0, 0 };
+
+ /* Read key toggle, brand, and key code
+ on registers 0x45, 0x46 and 0x47
+ */
+ rc = dev->em28xx_read_reg_req_len(dev, 0, EM28XX_R45_IR,
+ msg, sizeof(msg));
+ if (rc < 0)
+ return rc;
+
+ /* Infrared toggle (Reg 0x45[7]) */
+ poll_result->toggle_bit = (msg[0] >> 7);
+
+ /* Infrared read count (Reg 0x45[6:0] */
+ poll_result->read_count = (msg[0] & 0x7f);
+
+ /* Remote Control Address (Reg 0x46) */
+ poll_result->rc_address = msg[1];
+
+ /* Remote Control Data (Reg 0x47) */
+ poll_result->rc_data[0] = msg[2];
+
+ return 0;
+}
+
+static int em2874_polling_getkey(struct em28xx_IR *ir,
+ struct em28xx_ir_poll_result *poll_result)
+{
+ struct em28xx *dev = ir->dev;
+ int rc;
+ u8 msg[5] = { 0, 0, 0, 0, 0 };
+
+ /* Read key toggle, brand, and key code
+ on registers 0x51-55
+ */
+ rc = dev->em28xx_read_reg_req_len(dev, 0, EM2874_R51_IR,
+ msg, sizeof(msg));
+ if (rc < 0)
+ return rc;
+
+ /* Infrared toggle (Reg 0x51[7]) */
+ poll_result->toggle_bit = (msg[0] >> 7);
+
+ /* Infrared read count (Reg 0x51[6:0] */
+ poll_result->read_count = (msg[0] & 0x7f);
+
+ /* Remote Control Address (Reg 0x52) */
+ poll_result->rc_address = msg[1];
+
+ /* Remote Control Data (Reg 0x53-55) */
+ poll_result->rc_data[0] = msg[2];
+ poll_result->rc_data[1] = msg[3];
+ poll_result->rc_data[2] = msg[4];
+
+ return 0;
+}
+
+/**********************************************************
+ Polling code for em28xx
+ **********************************************************/
+
+static void em28xx_ir_handle_key(struct em28xx_IR *ir)
+{
+ int result;
+ struct em28xx_ir_poll_result poll_result;
+
+ /* read the registers containing the IR status */
+ result = ir->get_key(ir, &poll_result);
+ if (unlikely(result < 0)) {
+ dprintk("ir->get_key() failed %d\n", result);
+ return;
+ }
+
+ if (unlikely(poll_result.read_count != ir->last_readcount)) {
+ dprintk("%s: toggle: %d, count: %d, key 0x%02x%02x\n", __func__,
+ poll_result.toggle_bit, poll_result.read_count,
+ poll_result.rc_address, poll_result.rc_data[0]);
+ if (ir->full_code)
+ rc_keydown(ir->rc,
+ poll_result.rc_address << 8 |
+ poll_result.rc_data[0],
+ poll_result.toggle_bit);
+ else
+ rc_keydown(ir->rc,
+ poll_result.rc_data[0],
+ poll_result.toggle_bit);
+
+ if (ir->dev->chip_id == CHIP_ID_EM2874 ||
+ ir->dev->chip_id == CHIP_ID_EM2884)
+ /* The em2874 clears the readcount field every time the
+ register is read. The em2860/2880 datasheet says that it
+ is supposed to clear the readcount, but it doesn't. So with
+ the em2874, we are looking for a non-zero read count as
+ opposed to a readcount that is incrementing */
+ ir->last_readcount = 0;
+ else
+ ir->last_readcount = poll_result.read_count;
+ }
+}
+
+static void em28xx_ir_work(struct work_struct *work)
+{
+ struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work.work);
+
+ em28xx_ir_handle_key(ir);
+ schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling));
+}
+
+static int em28xx_ir_start(struct rc_dev *rc)
+{
+ struct em28xx_IR *ir = rc->priv;
+
+ INIT_DELAYED_WORK(&ir->work, em28xx_ir_work);
+ schedule_delayed_work(&ir->work, 0);
+
+ return 0;
+}
+
+static void em28xx_ir_stop(struct rc_dev *rc)
+{
+ struct em28xx_IR *ir = rc->priv;
+
+ cancel_delayed_work_sync(&ir->work);
+}
+
+int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 rc_type)
+{
+ int rc = 0;
+ struct em28xx_IR *ir = rc_dev->priv;
+ struct em28xx *dev = ir->dev;
+ u8 ir_config = EM2874_IR_RC5;
+
+ /* Adjust xclk based o IR table for RC5/NEC tables */
+
+ if (rc_type == RC_TYPE_RC5) {
+ dev->board.xclk |= EM28XX_XCLK_IR_RC5_MODE;
+ ir->full_code = 1;
+ } else if (rc_type == RC_TYPE_NEC) {
+ dev->board.xclk &= ~EM28XX_XCLK_IR_RC5_MODE;
+ ir_config = EM2874_IR_NEC;
+ ir->full_code = 1;
+ } else if (rc_type != RC_TYPE_UNKNOWN)
+ rc = -EINVAL;
+
+ em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, dev->board.xclk,
+ EM28XX_XCLK_IR_RC5_MODE);
+
+ /* Setup the proper handler based on the chip */
+ switch (dev->chip_id) {
+ case CHIP_ID_EM2860:
+ case CHIP_ID_EM2883:
+ ir->get_key = default_polling_getkey;
+ break;
+ case CHIP_ID_EM2884:
+ case CHIP_ID_EM2874:
+ case CHIP_ID_EM28174:
+ ir->get_key = em2874_polling_getkey;
+ em28xx_write_regs(dev, EM2874_R50_IR_CONFIG, &ir_config, 1);
+ break;
+ default:
+ printk("Unrecognized em28xx chip id 0x%02x: IR not supported\n",
+ dev->chip_id);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+int em28xx_ir_init(struct em28xx *dev)
+{
+ struct em28xx_IR *ir;
+ struct rc_dev *rc;
+ int err = -ENOMEM;
+
+ if (dev->board.ir_codes == NULL) {
+ /* No remote control support */
+ return 0;
+ }
+
+ ir = kzalloc(sizeof(*ir), GFP_KERNEL);
+ rc = rc_allocate_device();
+ if (!ir || !rc)
+ goto err_out_free;
+
+ /* record handles to ourself */
+ ir->dev = dev;
+ dev->ir = ir;
+ ir->rc = rc;
+
+ /*
+ * em2874 supports more protocols. For now, let's just announce
+ * the two protocols that were already tested
+ */
+ rc->allowed_protos = RC_TYPE_RC5 | RC_TYPE_NEC;
+ rc->priv = ir;
+ rc->change_protocol = em28xx_ir_change_protocol;
+ rc->open = em28xx_ir_start;
+ rc->close = em28xx_ir_stop;
+
+ /* By default, keep protocol field untouched */
+ err = em28xx_ir_change_protocol(rc, RC_TYPE_UNKNOWN);
+ if (err)
+ goto err_out_free;
+
+ /* This is how often we ask the chip for IR information */
+ ir->polling = 100; /* ms */
+
+ /* init input device */
+ snprintf(ir->name, sizeof(ir->name), "em28xx IR (%s)",
+ dev->name);
+
+ usb_make_path(dev->udev, ir->phys, sizeof(ir->phys));
+ strlcat(ir->phys, "/input0", sizeof(ir->phys));
+
+ rc->input_name = ir->name;
+ rc->input_phys = ir->phys;
+ rc->input_id.bustype = BUS_USB;
+ rc->input_id.version = 1;
+ rc->input_id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
+ rc->input_id.product = le16_to_cpu(dev->udev->descriptor.idProduct);
+ rc->dev.parent = &dev->udev->dev;
+ rc->map_name = dev->board.ir_codes;
+ rc->driver_name = MODULE_NAME;
+
+ /* all done */
+ err = rc_register_device(rc);
+ if (err)
+ goto err_out_stop;
+
+ return 0;
+
+ err_out_stop:
+ dev->ir = NULL;
+ err_out_free:
+ rc_free_device(rc);
+ kfree(ir);
+ return err;
+}
+
+int em28xx_ir_fini(struct em28xx *dev)
+{
+ struct em28xx_IR *ir = dev->ir;
+
+ /* skip detach on non attached boards */
+ if (!ir)
+ return 0;
+
+ if (ir->rc)
+ rc_unregister_device(ir->rc);
+
+ /* done */
+ kfree(ir);
+ dev->ir = NULL;
+ return 0;
+}
+
+/**********************************************************
+ Handle Webcam snapshot button
+ **********************************************************/
+
+static void em28xx_query_sbutton(struct work_struct *work)
+{
+ /* Poll the register and see if the button is depressed */
+ struct em28xx *dev =
+ container_of(work, struct em28xx, sbutton_query_work.work);
+ int ret;
+
+ ret = em28xx_read_reg(dev, EM28XX_R0C_USBSUSP);
+
+ if (ret & EM28XX_R0C_USBSUSP_SNAPSHOT) {
+ u8 cleared;
+ /* Button is depressed, clear the register */
+ cleared = ((u8) ret) & ~EM28XX_R0C_USBSUSP_SNAPSHOT;
+ em28xx_write_regs(dev, EM28XX_R0C_USBSUSP, &cleared, 1);
+
+ /* Not emulate the keypress */
+ input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY,
+ 1);
+ /* Now unpress the key */
+ input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY,
+ 0);
+ }
+
+ /* Schedule next poll */
+ schedule_delayed_work(&dev->sbutton_query_work,
+ msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL));
+}
+
+void em28xx_register_snapshot_button(struct em28xx *dev)
+{
+ struct input_dev *input_dev;
+ int err;
+
+ em28xx_info("Registering snapshot button...\n");
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ em28xx_errdev("input_allocate_device failed\n");
+ return;
+ }
+
+ usb_make_path(dev->udev, dev->snapshot_button_path,
+ sizeof(dev->snapshot_button_path));
+ strlcat(dev->snapshot_button_path, "/sbutton",
+ sizeof(dev->snapshot_button_path));
+ INIT_DELAYED_WORK(&dev->sbutton_query_work, em28xx_query_sbutton);
+
+ input_dev->name = "em28xx snapshot button";
+ input_dev->phys = dev->snapshot_button_path;
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+ set_bit(EM28XX_SNAPSHOT_KEY, input_dev->keybit);
+ input_dev->keycodesize = 0;
+ input_dev->keycodemax = 0;
+ input_dev->id.bustype = BUS_USB;
+ input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
+ input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct);
+ input_dev->id.version = 1;
+ input_dev->dev.parent = &dev->udev->dev;
+
+ err = input_register_device(input_dev);
+ if (err) {
+ em28xx_errdev("input_register_device failed\n");
+ input_free_device(input_dev);
+ return;
+ }
+
+ dev->sbutton_input_dev = input_dev;
+ schedule_delayed_work(&dev->sbutton_query_work,
+ msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL));
+ return;
+
+}
+
+void em28xx_deregister_snapshot_button(struct em28xx *dev)
+{
+ if (dev->sbutton_input_dev != NULL) {
+ em28xx_info("Deregistering snapshot button\n");
+ cancel_delayed_work_sync(&dev->sbutton_query_work);
+ input_unregister_device(dev->sbutton_input_dev);
+ dev->sbutton_input_dev = NULL;
+ }
+ return;
+}
diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h
new file mode 100644
index 00000000..2f626850
--- /dev/null
+++ b/drivers/media/video/em28xx/em28xx-reg.h
@@ -0,0 +1,275 @@
+#define EM_GPIO_0 (1 << 0)
+#define EM_GPIO_1 (1 << 1)
+#define EM_GPIO_2 (1 << 2)
+#define EM_GPIO_3 (1 << 3)
+#define EM_GPIO_4 (1 << 4)
+#define EM_GPIO_5 (1 << 5)
+#define EM_GPIO_6 (1 << 6)
+#define EM_GPIO_7 (1 << 7)
+
+#define EM_GPO_0 (1 << 0)
+#define EM_GPO_1 (1 << 1)
+#define EM_GPO_2 (1 << 2)
+#define EM_GPO_3 (1 << 3)
+
+/* em28xx endpoints */
+#define EM28XX_EP_ANALOG 0x82
+#define EM28XX_EP_AUDIO 0x83
+#define EM28XX_EP_DIGITAL 0x84
+
+/* em2800 registers */
+#define EM2800_R08_AUDIOSRC 0x08
+
+/* em28xx registers */
+
+#define EM28XX_R00_CHIPCFG 0x00
+
+/* em28xx Chip Configuration 0x00 */
+#define EM28XX_CHIPCFG_VENDOR_AUDIO 0x80
+#define EM28XX_CHIPCFG_I2S_VOLUME_CAPABLE 0x40
+#define EM28XX_CHIPCFG_I2S_5_SAMPRATES 0x30
+#define EM28XX_CHIPCFG_I2S_3_SAMPRATES 0x20
+#define EM28XX_CHIPCFG_AC97 0x10
+#define EM28XX_CHIPCFG_AUDIOMASK 0x30
+
+#define EM28XX_R01_CHIPCFG2 0x01
+
+/* em28xx Chip Configuration 2 0x01 */
+#define EM28XX_CHIPCFG2_TS_PRESENT 0x10
+#define EM28XX_CHIPCFG2_TS_REQ_INTERVAL_MASK 0x0c /* bits 3-2 */
+#define EM28XX_CHIPCFG2_TS_REQ_INTERVAL_1MF 0x00
+#define EM28XX_CHIPCFG2_TS_REQ_INTERVAL_2MF 0x04
+#define EM28XX_CHIPCFG2_TS_REQ_INTERVAL_4MF 0x08
+#define EM28XX_CHIPCFG2_TS_REQ_INTERVAL_8MF 0x0c
+#define EM28XX_CHIPCFG2_TS_PACKETSIZE_MASK 0x03 /* bits 0-1 */
+#define EM28XX_CHIPCFG2_TS_PACKETSIZE_188 0x00
+#define EM28XX_CHIPCFG2_TS_PACKETSIZE_376 0x01
+#define EM28XX_CHIPCFG2_TS_PACKETSIZE_564 0x02
+#define EM28XX_CHIPCFG2_TS_PACKETSIZE_752 0x03
+
+
+ /* GPIO/GPO registers */
+#define EM2880_R04_GPO 0x04 /* em2880-em2883 only */
+#define EM28XX_R08_GPIO 0x08 /* em2820 or upper */
+
+#define EM28XX_R06_I2C_CLK 0x06
+
+/* em28xx I2C Clock Register (0x06) */
+#define EM28XX_I2C_CLK_ACK_LAST_READ 0x80
+#define EM28XX_I2C_CLK_WAIT_ENABLE 0x40
+#define EM28XX_I2C_EEPROM_ON_BOARD 0x08
+#define EM28XX_I2C_EEPROM_KEY_VALID 0x04
+#define EM2874_I2C_SECONDARY_BUS_SELECT 0x04 /* em2874 has two i2c busses */
+#define EM28XX_I2C_FREQ_1_5_MHZ 0x03 /* bus frequency (bits [1-0]) */
+#define EM28XX_I2C_FREQ_25_KHZ 0x02
+#define EM28XX_I2C_FREQ_400_KHZ 0x01
+#define EM28XX_I2C_FREQ_100_KHZ 0x00
+
+
+#define EM28XX_R0A_CHIPID 0x0a
+#define EM28XX_R0C_USBSUSP 0x0c /* */
+
+#define EM28XX_R0E_AUDIOSRC 0x0e
+#define EM28XX_R0F_XCLK 0x0f
+
+/* em28xx XCLK Register (0x0f) */
+#define EM28XX_XCLK_AUDIO_UNMUTE 0x80 /* otherwise audio muted */
+#define EM28XX_XCLK_I2S_MSB_TIMING 0x40 /* otherwise standard timing */
+#define EM28XX_XCLK_IR_RC5_MODE 0x20 /* otherwise NEC mode */
+#define EM28XX_XCLK_IR_NEC_CHK_PARITY 0x10
+#define EM28XX_XCLK_FREQUENCY_30MHZ 0x00 /* Freq. select (bits [3-0]) */
+#define EM28XX_XCLK_FREQUENCY_15MHZ 0x01
+#define EM28XX_XCLK_FREQUENCY_10MHZ 0x02
+#define EM28XX_XCLK_FREQUENCY_7_5MHZ 0x03
+#define EM28XX_XCLK_FREQUENCY_6MHZ 0x04
+#define EM28XX_XCLK_FREQUENCY_5MHZ 0x05
+#define EM28XX_XCLK_FREQUENCY_4_3MHZ 0x06
+#define EM28XX_XCLK_FREQUENCY_12MHZ 0x07
+#define EM28XX_XCLK_FREQUENCY_20MHZ 0x08
+#define EM28XX_XCLK_FREQUENCY_20MHZ_2 0x09
+#define EM28XX_XCLK_FREQUENCY_48MHZ 0x0a
+#define EM28XX_XCLK_FREQUENCY_24MHZ 0x0b
+
+#define EM28XX_R10_VINMODE 0x10
+
+#define EM28XX_R11_VINCTRL 0x11
+
+/* em28xx Video Input Control Register 0x11 */
+#define EM28XX_VINCTRL_VBI_SLICED 0x80
+#define EM28XX_VINCTRL_VBI_RAW 0x40
+#define EM28XX_VINCTRL_VOUT_MODE_IN 0x20 /* HREF,VREF,VACT in output */
+#define EM28XX_VINCTRL_CCIR656_ENABLE 0x10
+#define EM28XX_VINCTRL_VBI_16BIT_RAW 0x08 /* otherwise 8-bit raw */
+#define EM28XX_VINCTRL_FID_ON_HREF 0x04
+#define EM28XX_VINCTRL_DUAL_EDGE_STROBE 0x02
+#define EM28XX_VINCTRL_INTERLACED 0x01
+
+#define EM28XX_R12_VINENABLE 0x12 /* */
+
+#define EM28XX_R14_GAMMA 0x14
+#define EM28XX_R15_RGAIN 0x15
+#define EM28XX_R16_GGAIN 0x16
+#define EM28XX_R17_BGAIN 0x17
+#define EM28XX_R18_ROFFSET 0x18
+#define EM28XX_R19_GOFFSET 0x19
+#define EM28XX_R1A_BOFFSET 0x1a
+
+#define EM28XX_R1B_OFLOW 0x1b
+#define EM28XX_R1C_HSTART 0x1c
+#define EM28XX_R1D_VSTART 0x1d
+#define EM28XX_R1E_CWIDTH 0x1e
+#define EM28XX_R1F_CHEIGHT 0x1f
+
+#define EM28XX_R20_YGAIN 0x20
+#define EM28XX_R21_YOFFSET 0x21
+#define EM28XX_R22_UVGAIN 0x22
+#define EM28XX_R23_UOFFSET 0x23
+#define EM28XX_R24_VOFFSET 0x24
+#define EM28XX_R25_SHARPNESS 0x25
+
+#define EM28XX_R26_COMPR 0x26
+#define EM28XX_R27_OUTFMT 0x27
+
+/* em28xx Output Format Register (0x27) */
+#define EM28XX_OUTFMT_RGB_8_RGRG 0x00
+#define EM28XX_OUTFMT_RGB_8_GRGR 0x01
+#define EM28XX_OUTFMT_RGB_8_GBGB 0x02
+#define EM28XX_OUTFMT_RGB_8_BGBG 0x03
+#define EM28XX_OUTFMT_RGB_16_656 0x04
+#define EM28XX_OUTFMT_RGB_8_BAYER 0x08 /* Pattern in Reg 0x10[1-0] */
+#define EM28XX_OUTFMT_YUV211 0x10
+#define EM28XX_OUTFMT_YUV422_Y0UY1V 0x14
+#define EM28XX_OUTFMT_YUV422_Y1UY0V 0x15
+#define EM28XX_OUTFMT_YUV411 0x18
+
+
+#define EM28XX_R28_XMIN 0x28
+#define EM28XX_R29_XMAX 0x29
+#define EM28XX_R2A_YMIN 0x2a
+#define EM28XX_R2B_YMAX 0x2b
+
+#define EM28XX_R30_HSCALELOW 0x30
+#define EM28XX_R31_HSCALEHIGH 0x31
+#define EM28XX_R32_VSCALELOW 0x32
+#define EM28XX_R33_VSCALEHIGH 0x33
+#define EM28XX_R34_VBI_START_H 0x34
+#define EM28XX_R35_VBI_START_V 0x35
+#define EM28XX_R36_VBI_WIDTH 0x36
+#define EM28XX_R37_VBI_HEIGHT 0x37
+
+#define EM28XX_R40_AC97LSB 0x40
+#define EM28XX_R41_AC97MSB 0x41
+#define EM28XX_R42_AC97ADDR 0x42
+#define EM28XX_R43_AC97BUSY 0x43
+
+#define EM28XX_R45_IR 0x45
+ /* 0x45 bit 7 - parity bit
+ bits 6-0 - count
+ 0x46 IR brand
+ 0x47 IR data
+ */
+
+/* em2874 registers */
+#define EM2874_R50_IR_CONFIG 0x50
+#define EM2874_R51_IR 0x51
+#define EM2874_R5F_TS_ENABLE 0x5f
+#define EM2874_R80_GPIO 0x80
+
+/* em2874 IR config register (0x50) */
+#define EM2874_IR_NEC 0x00
+#define EM2874_IR_RC5 0x04
+#define EM2874_IR_RC6_MODE_0 0x08
+#define EM2874_IR_RC6_MODE_6A 0x0b
+
+/* em2874 Transport Stream Enable Register (0x5f) */
+#define EM2874_TS1_CAPTURE_ENABLE (1 << 0)
+#define EM2874_TS1_FILTER_ENABLE (1 << 1)
+#define EM2874_TS1_NULL_DISCARD (1 << 2)
+#define EM2874_TS2_CAPTURE_ENABLE (1 << 4)
+#define EM2874_TS2_FILTER_ENABLE (1 << 5)
+#define EM2874_TS2_NULL_DISCARD (1 << 6)
+
+/* register settings */
+#define EM2800_AUDIO_SRC_TUNER 0x0d
+#define EM2800_AUDIO_SRC_LINE 0x0c
+#define EM28XX_AUDIO_SRC_TUNER 0xc0
+#define EM28XX_AUDIO_SRC_LINE 0x80
+
+/* FIXME: Need to be populated with the other chip ID's */
+enum em28xx_chip_id {
+ CHIP_ID_EM2800 = 7,
+ CHIP_ID_EM2710 = 17,
+ CHIP_ID_EM2820 = 18, /* Also used by some em2710 */
+ CHIP_ID_EM2840 = 20,
+ CHIP_ID_EM2750 = 33,
+ CHIP_ID_EM2860 = 34,
+ CHIP_ID_EM2870 = 35,
+ CHIP_ID_EM2883 = 36,
+ CHIP_ID_EM2874 = 65,
+ CHIP_ID_EM2884 = 68,
+ CHIP_ID_EM28174 = 113,
+};
+
+/*
+ * Registers used by em202 and other AC97 chips
+ */
+
+/* Standard AC97 registers */
+#define AC97_RESET 0x00
+
+ /* Output volumes */
+#define AC97_MASTER_VOL 0x02
+#define AC97_LINE_LEVEL_VOL 0x04 /* Some devices use for headphones */
+#define AC97_MASTER_MONO_VOL 0x06
+
+ /* Input volumes */
+#define AC97_PC_BEEP_VOL 0x0a
+#define AC97_PHONE_VOL 0x0c
+#define AC97_MIC_VOL 0x0e
+#define AC97_LINEIN_VOL 0x10
+#define AC97_CD_VOL 0x12
+#define AC97_VIDEO_VOL 0x14
+#define AC97_AUX_VOL 0x16
+#define AC97_PCM_OUT_VOL 0x18
+
+ /* capture registers */
+#define AC97_RECORD_SELECT 0x1a
+#define AC97_RECORD_GAIN 0x1c
+
+ /* control registers */
+#define AC97_GENERAL_PURPOSE 0x20
+#define AC97_3D_CTRL 0x22
+#define AC97_AUD_INT_AND_PAG 0x24
+#define AC97_POWER_DOWN_CTRL 0x26
+#define AC97_EXT_AUD_ID 0x28
+#define AC97_EXT_AUD_CTRL 0x2a
+
+/* Supported rate varies for each AC97 device
+ if write an unsupported value, it will return the closest one
+ */
+#define AC97_PCM_OUT_FRONT_SRATE 0x2c
+#define AC97_PCM_OUT_SURR_SRATE 0x2e
+#define AC97_PCM_OUT_LFE_SRATE 0x30
+#define AC97_PCM_IN_SRATE 0x32
+
+ /* For devices with more than 2 channels, extra output volumes */
+#define AC97_LFE_MASTER_VOL 0x36
+#define AC97_SURR_MASTER_VOL 0x38
+
+ /* Digital SPDIF output control */
+#define AC97_SPDIF_OUT_CTRL 0x3a
+
+ /* Vendor ID identifier */
+#define AC97_VENDOR_ID1 0x7c
+#define AC97_VENDOR_ID2 0x7e
+
+/* EMP202 vendor registers */
+#define EM202_EXT_MODEM_CTRL 0x3e
+#define EM202_GPIO_CONF 0x4c
+#define EM202_GPIO_POLARITY 0x4e
+#define EM202_GPIO_STICKY 0x50
+#define EM202_GPIO_MASK 0x52
+#define EM202_GPIO_STATUS 0x54
+#define EM202_SPDIF_OUT_SEL 0x6a
+#define EM202_ANTIPOP 0x72
+#define EM202_EAPD_GPIO_ACCESS 0x74
diff --git a/drivers/media/video/em28xx/em28xx-vbi.c b/drivers/media/video/em28xx/em28xx-vbi.c
new file mode 100644
index 00000000..2b4c9cba
--- /dev/null
+++ b/drivers/media/video/em28xx/em28xx-vbi.c
@@ -0,0 +1,145 @@
+/*
+ em28xx-vbi.c - VBI driver for em28xx
+
+ Copyright (C) 2009 Devin Heitmueller <dheitmueller@kernellabs.com>
+
+ This work was sponsored by EyeMagnet Limited.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/hardirq.h>
+#include <linux/init.h>
+
+#include "em28xx.h"
+
+static unsigned int vbibufs = 5;
+module_param(vbibufs, int, 0644);
+MODULE_PARM_DESC(vbibufs, "number of vbi buffers, range 2-32");
+
+static unsigned int vbi_debug;
+module_param(vbi_debug, int, 0644);
+MODULE_PARM_DESC(vbi_debug, "enable debug messages [vbi]");
+
+#define dprintk(level, fmt, arg...) if (vbi_debug >= level) \
+ printk(KERN_DEBUG "%s: " fmt, dev->core->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+
+static void
+free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf)
+{
+ struct em28xx_fh *fh = vq->priv_data;
+ struct em28xx *dev = fh->dev;
+ unsigned long flags = 0;
+ if (in_interrupt())
+ BUG();
+
+ /* We used to wait for the buffer to finish here, but this didn't work
+ because, as we were keeping the state as VIDEOBUF_QUEUED,
+ videobuf_queue_cancel marked it as finished for us.
+ (Also, it could wedge forever if the hardware was misconfigured.)
+
+ This should be safe; by the time we get here, the buffer isn't
+ queued anymore. If we ever start marking the buffers as
+ VIDEOBUF_ACTIVE, it won't be, though.
+ */
+ spin_lock_irqsave(&dev->slock, flags);
+ if (dev->isoc_ctl.vbi_buf == buf)
+ dev->isoc_ctl.vbi_buf = NULL;
+ spin_unlock_irqrestore(&dev->slock, flags);
+
+ videobuf_vmalloc_free(&buf->vb);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int
+vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+ struct em28xx_fh *fh = q->priv_data;
+ struct em28xx *dev = fh->dev;
+
+ *size = dev->vbi_width * dev->vbi_height * 2;
+
+ if (0 == *count)
+ *count = vbibufs;
+ if (*count < 2)
+ *count = 2;
+ if (*count > 32)
+ *count = 32;
+ return 0;
+}
+
+static int
+vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct em28xx_fh *fh = q->priv_data;
+ struct em28xx *dev = fh->dev;
+ struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb);
+ int rc = 0;
+
+ buf->vb.size = dev->vbi_width * dev->vbi_height * 2;
+
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+
+ buf->vb.width = dev->vbi_width;
+ buf->vb.height = dev->vbi_height;
+ buf->vb.field = field;
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ rc = videobuf_iolock(q, &buf->vb, NULL);
+ if (rc < 0)
+ goto fail;
+ }
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+fail:
+ free_buffer(q, buf);
+ return rc;
+}
+
+static void
+vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct em28xx_buffer *buf = container_of(vb,
+ struct em28xx_buffer,
+ vb);
+ struct em28xx_fh *fh = vq->priv_data;
+ struct em28xx *dev = fh->dev;
+ struct em28xx_dmaqueue *vbiq = &dev->vbiq;
+
+ buf->vb.state = VIDEOBUF_QUEUED;
+ list_add_tail(&buf->vb.queue, &vbiq->active);
+}
+
+static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb);
+ free_buffer(q, buf);
+}
+
+struct videobuf_queue_ops em28xx_vbi_qops = {
+ .buf_setup = vbi_setup,
+ .buf_prepare = vbi_prepare,
+ .buf_queue = vbi_queue,
+ .buf_release = vbi_release,
+};
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
new file mode 100644
index 00000000..324b695c
--- /dev/null
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -0,0 +1,2605 @@
+/*
+ em28xx-video.c - driver for Empia EM2800/EM2820/2840 USB
+ video capture devices
+
+ Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
+ Markus Rechberger <mrechberger@gmail.com>
+ Mauro Carvalho Chehab <mchehab@infradead.org>
+ Sascha Sommer <saschasommer@freenet.de>
+
+ Some parts based on SN9C10x PC Camera Controllers GPL driver made
+ by Luca Risolia <luca.risolia@studio.unibo.it>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/bitmap.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+#include "em28xx.h"
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/msp3400.h>
+#include <media/tuner.h>
+
+#define DRIVER_AUTHOR "Ludovico Cavedon <cavedon@sssup.it>, " \
+ "Markus Rechberger <mrechberger@gmail.com>, " \
+ "Mauro Carvalho Chehab <mchehab@infradead.org>, " \
+ "Sascha Sommer <saschasommer@freenet.de>"
+
+#define DRIVER_DESC "Empia em28xx based USB video device driver"
+
+#define EM28XX_VERSION "0.1.3"
+
+#define em28xx_videodbg(fmt, arg...) do {\
+ if (video_debug) \
+ printk(KERN_INFO "%s %s :"fmt, \
+ dev->name, __func__ , ##arg); } while (0)
+
+static unsigned int isoc_debug;
+module_param(isoc_debug, int, 0644);
+MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]");
+
+#define em28xx_isocdbg(fmt, arg...) \
+do {\
+ if (isoc_debug) { \
+ printk(KERN_INFO "%s %s :"fmt, \
+ dev->name, __func__ , ##arg); \
+ } \
+ } while (0)
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(EM28XX_VERSION);
+
+static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
+static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
+static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
+
+module_param_array(video_nr, int, NULL, 0444);
+module_param_array(vbi_nr, int, NULL, 0444);
+module_param_array(radio_nr, int, NULL, 0444);
+MODULE_PARM_DESC(video_nr, "video device numbers");
+MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
+MODULE_PARM_DESC(radio_nr, "radio device numbers");
+
+static unsigned int video_debug;
+module_param(video_debug, int, 0644);
+MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
+
+/* supported video standards */
+static struct em28xx_fmt format[] = {
+ {
+ .name = "16 bpp YUY2, 4:2:2, packed",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .reg = EM28XX_OUTFMT_YUV422_Y0UY1V,
+ }, {
+ .name = "16 bpp RGB 565, LE",
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .depth = 16,
+ .reg = EM28XX_OUTFMT_RGB_16_656,
+ }, {
+ .name = "8 bpp Bayer BGBG..GRGR",
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .depth = 8,
+ .reg = EM28XX_OUTFMT_RGB_8_BGBG,
+ }, {
+ .name = "8 bpp Bayer GRGR..BGBG",
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .depth = 8,
+ .reg = EM28XX_OUTFMT_RGB_8_GRGR,
+ }, {
+ .name = "8 bpp Bayer GBGB..RGRG",
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ .depth = 8,
+ .reg = EM28XX_OUTFMT_RGB_8_GBGB,
+ }, {
+ .name = "12 bpp YUV411",
+ .fourcc = V4L2_PIX_FMT_YUV411P,
+ .depth = 12,
+ .reg = EM28XX_OUTFMT_YUV411,
+ },
+};
+
+/* supported controls */
+/* Common to all boards */
+static struct v4l2_queryctrl ac97_qctrl[] = {
+ {
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Volume",
+ .minimum = 0x0,
+ .maximum = 0x1f,
+ .step = 0x1,
+ .default_value = 0x1f,
+ .flags = V4L2_CTRL_FLAG_SLIDER,
+ }, {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ .flags = 0,
+ }
+};
+
+/* ------------------------------------------------------------------
+ DMA and thread functions
+ ------------------------------------------------------------------*/
+
+/*
+ * Announces that a buffer were filled and request the next
+ */
+static inline void buffer_filled(struct em28xx *dev,
+ struct em28xx_dmaqueue *dma_q,
+ struct em28xx_buffer *buf)
+{
+ /* Advice that buffer was filled */
+ em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i);
+ buf->vb.state = VIDEOBUF_DONE;
+ buf->vb.field_count++;
+ do_gettimeofday(&buf->vb.ts);
+
+ dev->isoc_ctl.vid_buf = NULL;
+
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+}
+
+static inline void vbi_buffer_filled(struct em28xx *dev,
+ struct em28xx_dmaqueue *dma_q,
+ struct em28xx_buffer *buf)
+{
+ /* Advice that buffer was filled */
+ em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i);
+
+ buf->vb.state = VIDEOBUF_DONE;
+ buf->vb.field_count++;
+ do_gettimeofday(&buf->vb.ts);
+
+ dev->isoc_ctl.vbi_buf = NULL;
+
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+}
+
+/*
+ * Identify the buffer header type and properly handles
+ */
+static void em28xx_copy_video(struct em28xx *dev,
+ struct em28xx_dmaqueue *dma_q,
+ struct em28xx_buffer *buf,
+ unsigned char *p,
+ unsigned char *outp, unsigned long len)
+{
+ void *fieldstart, *startwrite, *startread;
+ int linesdone, currlinedone, offset, lencopy, remain;
+ int bytesperline = dev->width << 1;
+
+ if (dma_q->pos + len > buf->vb.size)
+ len = buf->vb.size - dma_q->pos;
+
+ startread = p;
+ remain = len;
+
+ if (dev->progressive)
+ fieldstart = outp;
+ else {
+ /* Interlaces two half frames */
+ if (buf->top_field)
+ fieldstart = outp;
+ else
+ fieldstart = outp + bytesperline;
+ }
+
+ linesdone = dma_q->pos / bytesperline;
+ currlinedone = dma_q->pos % bytesperline;
+
+ if (dev->progressive)
+ offset = linesdone * bytesperline + currlinedone;
+ else
+ offset = linesdone * bytesperline * 2 + currlinedone;
+
+ startwrite = fieldstart + offset;
+ lencopy = bytesperline - currlinedone;
+ lencopy = lencopy > remain ? remain : lencopy;
+
+ if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) {
+ em28xx_isocdbg("Overflow of %zi bytes past buffer end (1)\n",
+ ((char *)startwrite + lencopy) -
+ ((char *)outp + buf->vb.size));
+ remain = (char *)outp + buf->vb.size - (char *)startwrite;
+ lencopy = remain;
+ }
+ if (lencopy <= 0)
+ return;
+ memcpy(startwrite, startread, lencopy);
+
+ remain -= lencopy;
+
+ while (remain > 0) {
+ startwrite += lencopy + bytesperline;
+ startread += lencopy;
+ if (bytesperline > remain)
+ lencopy = remain;
+ else
+ lencopy = bytesperline;
+
+ if ((char *)startwrite + lencopy > (char *)outp +
+ buf->vb.size) {
+ em28xx_isocdbg("Overflow of %zi bytes past buffer end"
+ "(2)\n",
+ ((char *)startwrite + lencopy) -
+ ((char *)outp + buf->vb.size));
+ lencopy = remain = (char *)outp + buf->vb.size -
+ (char *)startwrite;
+ }
+ if (lencopy <= 0)
+ break;
+
+ memcpy(startwrite, startread, lencopy);
+
+ remain -= lencopy;
+ }
+
+ dma_q->pos += len;
+}
+
+static void em28xx_copy_vbi(struct em28xx *dev,
+ struct em28xx_dmaqueue *dma_q,
+ struct em28xx_buffer *buf,
+ unsigned char *p,
+ unsigned char *outp, unsigned long len)
+{
+ void *startwrite, *startread;
+ int offset;
+ int bytesperline;
+
+ if (dev == NULL) {
+ em28xx_isocdbg("dev is null\n");
+ return;
+ }
+ bytesperline = dev->vbi_width;
+
+ if (dma_q == NULL) {
+ em28xx_isocdbg("dma_q is null\n");
+ return;
+ }
+ if (buf == NULL) {
+ return;
+ }
+ if (p == NULL) {
+ em28xx_isocdbg("p is null\n");
+ return;
+ }
+ if (outp == NULL) {
+ em28xx_isocdbg("outp is null\n");
+ return;
+ }
+
+ if (dma_q->pos + len > buf->vb.size)
+ len = buf->vb.size - dma_q->pos;
+
+ startread = p;
+
+ startwrite = outp + dma_q->pos;
+ offset = dma_q->pos;
+
+ /* Make sure the bottom field populates the second half of the frame */
+ if (buf->top_field == 0) {
+ startwrite += bytesperline * dev->vbi_height;
+ offset += bytesperline * dev->vbi_height;
+ }
+
+ memcpy(startwrite, startread, len);
+ dma_q->pos += len;
+}
+
+static inline void print_err_status(struct em28xx *dev,
+ int packet, int status)
+{
+ char *errmsg = "Unknown";
+
+ switch (status) {
+ case -ENOENT:
+ errmsg = "unlinked synchronuously";
+ break;
+ case -ECONNRESET:
+ errmsg = "unlinked asynchronuously";
+ break;
+ case -ENOSR:
+ errmsg = "Buffer error (overrun)";
+ break;
+ case -EPIPE:
+ errmsg = "Stalled (device not responding)";
+ break;
+ case -EOVERFLOW:
+ errmsg = "Babble (bad cable?)";
+ break;
+ case -EPROTO:
+ errmsg = "Bit-stuff error (bad cable?)";
+ break;
+ case -EILSEQ:
+ errmsg = "CRC/Timeout (could be anything)";
+ break;
+ case -ETIME:
+ errmsg = "Device does not respond";
+ break;
+ }
+ if (packet < 0) {
+ em28xx_isocdbg("URB status %d [%s].\n", status, errmsg);
+ } else {
+ em28xx_isocdbg("URB packet %d, status %d [%s].\n",
+ packet, status, errmsg);
+ }
+}
+
+/*
+ * video-buf generic routine to get the next available buffer
+ */
+static inline void get_next_buf(struct em28xx_dmaqueue *dma_q,
+ struct em28xx_buffer **buf)
+{
+ struct em28xx *dev = container_of(dma_q, struct em28xx, vidq);
+ char *outp;
+
+ if (list_empty(&dma_q->active)) {
+ em28xx_isocdbg("No active queue to serve\n");
+ dev->isoc_ctl.vid_buf = NULL;
+ *buf = NULL;
+ return;
+ }
+
+ /* Get the next buffer */
+ *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue);
+
+ /* Cleans up buffer - Useful for testing for frame/URB loss */
+ outp = videobuf_to_vmalloc(&(*buf)->vb);
+ memset(outp, 0, (*buf)->vb.size);
+
+ dev->isoc_ctl.vid_buf = *buf;
+
+ return;
+}
+
+/*
+ * video-buf generic routine to get the next available VBI buffer
+ */
+static inline void vbi_get_next_buf(struct em28xx_dmaqueue *dma_q,
+ struct em28xx_buffer **buf)
+{
+ struct em28xx *dev = container_of(dma_q, struct em28xx, vbiq);
+ char *outp;
+
+ if (list_empty(&dma_q->active)) {
+ em28xx_isocdbg("No active queue to serve\n");
+ dev->isoc_ctl.vbi_buf = NULL;
+ *buf = NULL;
+ return;
+ }
+
+ /* Get the next buffer */
+ *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue);
+ /* Cleans up buffer - Useful for testing for frame/URB loss */
+ outp = videobuf_to_vmalloc(&(*buf)->vb);
+ memset(outp, 0x00, (*buf)->vb.size);
+
+ dev->isoc_ctl.vbi_buf = *buf;
+
+ return;
+}
+
+/*
+ * Controls the isoc copy of each urb packet
+ */
+static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb)
+{
+ struct em28xx_buffer *buf;
+ struct em28xx_dmaqueue *dma_q = &dev->vidq;
+ unsigned char *outp = NULL;
+ int i, len = 0, rc = 1;
+ unsigned char *p;
+
+ if (!dev)
+ return 0;
+
+ if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED))
+ return 0;
+
+ if (urb->status < 0) {
+ print_err_status(dev, -1, urb->status);
+ if (urb->status == -ENOENT)
+ return 0;
+ }
+
+ buf = dev->isoc_ctl.vid_buf;
+ if (buf != NULL)
+ outp = videobuf_to_vmalloc(&buf->vb);
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ int status = urb->iso_frame_desc[i].status;
+
+ if (status < 0) {
+ print_err_status(dev, i, status);
+ if (urb->iso_frame_desc[i].status != -EPROTO)
+ continue;
+ }
+
+ len = urb->iso_frame_desc[i].actual_length - 4;
+
+ if (urb->iso_frame_desc[i].actual_length <= 0) {
+ /* em28xx_isocdbg("packet %d is empty",i); - spammy */
+ continue;
+ }
+ if (urb->iso_frame_desc[i].actual_length >
+ dev->max_pkt_size) {
+ em28xx_isocdbg("packet bigger than packet size");
+ continue;
+ }
+
+ p = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+
+ /* FIXME: incomplete buffer checks where removed to make
+ logic simpler. Impacts of those changes should be evaluated
+ */
+ if (p[0] == 0x33 && p[1] == 0x95 && p[2] == 0x00) {
+ em28xx_isocdbg("VBI HEADER!!!\n");
+ /* FIXME: Should add vbi copy */
+ continue;
+ }
+ if (p[0] == 0x22 && p[1] == 0x5a) {
+ em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2],
+ len, (p[2] & 1) ? "odd" : "even");
+
+ if (dev->progressive || !(p[2] & 1)) {
+ if (buf != NULL)
+ buffer_filled(dev, dma_q, buf);
+ get_next_buf(dma_q, &buf);
+ if (buf == NULL)
+ outp = NULL;
+ else
+ outp = videobuf_to_vmalloc(&buf->vb);
+ }
+
+ if (buf != NULL) {
+ if (p[2] & 1)
+ buf->top_field = 0;
+ else
+ buf->top_field = 1;
+ }
+
+ dma_q->pos = 0;
+ }
+ if (buf != NULL) {
+ if (p[0] != 0x88 && p[0] != 0x22) {
+ em28xx_isocdbg("frame is not complete\n");
+ len += 4;
+ } else {
+ p += 4;
+ }
+ em28xx_copy_video(dev, dma_q, buf, p, outp, len);
+ }
+ }
+ return rc;
+}
+
+/* Version of isoc handler that takes into account a mixture of video and
+ VBI data */
+static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb)
+{
+ struct em28xx_buffer *buf, *vbi_buf;
+ struct em28xx_dmaqueue *dma_q = &dev->vidq;
+ struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq;
+ unsigned char *outp = NULL;
+ unsigned char *vbioutp = NULL;
+ int i, len = 0, rc = 1;
+ unsigned char *p;
+ int vbi_size;
+
+ if (!dev)
+ return 0;
+
+ if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED))
+ return 0;
+
+ if (urb->status < 0) {
+ print_err_status(dev, -1, urb->status);
+ if (urb->status == -ENOENT)
+ return 0;
+ }
+
+ buf = dev->isoc_ctl.vid_buf;
+ if (buf != NULL)
+ outp = videobuf_to_vmalloc(&buf->vb);
+
+ vbi_buf = dev->isoc_ctl.vbi_buf;
+ if (vbi_buf != NULL)
+ vbioutp = videobuf_to_vmalloc(&vbi_buf->vb);
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ int status = urb->iso_frame_desc[i].status;
+
+ if (status < 0) {
+ print_err_status(dev, i, status);
+ if (urb->iso_frame_desc[i].status != -EPROTO)
+ continue;
+ }
+
+ len = urb->iso_frame_desc[i].actual_length;
+ if (urb->iso_frame_desc[i].actual_length <= 0) {
+ /* em28xx_isocdbg("packet %d is empty",i); - spammy */
+ continue;
+ }
+ if (urb->iso_frame_desc[i].actual_length >
+ dev->max_pkt_size) {
+ em28xx_isocdbg("packet bigger than packet size");
+ continue;
+ }
+
+ p = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+
+ /* capture type 0 = vbi start
+ capture type 1 = video start
+ capture type 2 = video in progress */
+ if (p[0] == 0x33 && p[1] == 0x95) {
+ dev->capture_type = 0;
+ dev->vbi_read = 0;
+ em28xx_isocdbg("VBI START HEADER!!!\n");
+ dev->cur_field = p[2];
+ p += 4;
+ len -= 4;
+ } else if (p[0] == 0x88 && p[1] == 0x88 &&
+ p[2] == 0x88 && p[3] == 0x88) {
+ /* continuation */
+ p += 4;
+ len -= 4;
+ } else if (p[0] == 0x22 && p[1] == 0x5a) {
+ /* start video */
+ p += 4;
+ len -= 4;
+ }
+
+ vbi_size = dev->vbi_width * dev->vbi_height;
+
+ if (dev->capture_type == 0) {
+ if (dev->vbi_read >= vbi_size) {
+ /* We've already read all the VBI data, so
+ treat the rest as video */
+ em28xx_isocdbg("dev->vbi_read > vbi_size\n");
+ } else if ((dev->vbi_read + len) < vbi_size) {
+ /* This entire frame is VBI data */
+ if (dev->vbi_read == 0 &&
+ (!(dev->cur_field & 1))) {
+ /* Brand new frame */
+ if (vbi_buf != NULL)
+ vbi_buffer_filled(dev,
+ vbi_dma_q,
+ vbi_buf);
+ vbi_get_next_buf(vbi_dma_q, &vbi_buf);
+ if (vbi_buf == NULL)
+ vbioutp = NULL;
+ else
+ vbioutp = videobuf_to_vmalloc(
+ &vbi_buf->vb);
+ }
+
+ if (dev->vbi_read == 0) {
+ vbi_dma_q->pos = 0;
+ if (vbi_buf != NULL) {
+ if (dev->cur_field & 1)
+ vbi_buf->top_field = 0;
+ else
+ vbi_buf->top_field = 1;
+ }
+ }
+
+ dev->vbi_read += len;
+ em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p,
+ vbioutp, len);
+ } else {
+ /* Some of this frame is VBI data and some is
+ video data */
+ int vbi_data_len = vbi_size - dev->vbi_read;
+ dev->vbi_read += vbi_data_len;
+ em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p,
+ vbioutp, vbi_data_len);
+ dev->capture_type = 1;
+ p += vbi_data_len;
+ len -= vbi_data_len;
+ }
+ }
+
+ if (dev->capture_type == 1) {
+ dev->capture_type = 2;
+ if (dev->progressive || !(dev->cur_field & 1)) {
+ if (buf != NULL)
+ buffer_filled(dev, dma_q, buf);
+ get_next_buf(dma_q, &buf);
+ if (buf == NULL)
+ outp = NULL;
+ else
+ outp = videobuf_to_vmalloc(&buf->vb);
+ }
+ if (buf != NULL) {
+ if (dev->cur_field & 1)
+ buf->top_field = 0;
+ else
+ buf->top_field = 1;
+ }
+
+ dma_q->pos = 0;
+ }
+
+ if (buf != NULL && dev->capture_type == 2) {
+ if (len >= 4 && p[0] == 0x88 && p[1] == 0x88 &&
+ p[2] == 0x88 && p[3] == 0x88) {
+ p += 4;
+ len -= 4;
+ }
+ if (len >= 4 && p[0] == 0x22 && p[1] == 0x5a) {
+ em28xx_isocdbg("Video frame %d, len=%i, %s\n",
+ p[2], len, (p[2] & 1) ?
+ "odd" : "even");
+ p += 4;
+ len -= 4;
+ }
+
+ if (len > 0)
+ em28xx_copy_video(dev, dma_q, buf, p, outp,
+ len);
+ }
+ }
+ return rc;
+}
+
+
+/* ------------------------------------------------------------------
+ Videobuf operations
+ ------------------------------------------------------------------*/
+
+static int
+buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
+{
+ struct em28xx_fh *fh = vq->priv_data;
+ struct em28xx *dev = fh->dev;
+ struct v4l2_frequency f;
+
+ *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7)
+ >> 3;
+
+ if (0 == *count)
+ *count = EM28XX_DEF_BUF;
+
+ if (*count < EM28XX_MIN_BUF)
+ *count = EM28XX_MIN_BUF;
+
+ /* Ask tuner to go to analog or radio mode */
+ memset(&f, 0, sizeof(f));
+ f.frequency = dev->ctl_freq;
+ f.type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
+
+ return 0;
+}
+
+/* This is called *without* dev->slock held; please keep it that way */
+static void free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf)
+{
+ struct em28xx_fh *fh = vq->priv_data;
+ struct em28xx *dev = fh->dev;
+ unsigned long flags = 0;
+ if (in_interrupt())
+ BUG();
+
+ /* We used to wait for the buffer to finish here, but this didn't work
+ because, as we were keeping the state as VIDEOBUF_QUEUED,
+ videobuf_queue_cancel marked it as finished for us.
+ (Also, it could wedge forever if the hardware was misconfigured.)
+
+ This should be safe; by the time we get here, the buffer isn't
+ queued anymore. If we ever start marking the buffers as
+ VIDEOBUF_ACTIVE, it won't be, though.
+ */
+ spin_lock_irqsave(&dev->slock, flags);
+ if (dev->isoc_ctl.vid_buf == buf)
+ dev->isoc_ctl.vid_buf = NULL;
+ spin_unlock_irqrestore(&dev->slock, flags);
+
+ videobuf_vmalloc_free(&buf->vb);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int
+buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct em28xx_fh *fh = vq->priv_data;
+ struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb);
+ struct em28xx *dev = fh->dev;
+ int rc = 0, urb_init = 0;
+
+ buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth
+ + 7) >> 3;
+
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+
+ buf->vb.width = dev->width;
+ buf->vb.height = dev->height;
+ buf->vb.field = field;
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ rc = videobuf_iolock(vq, &buf->vb, NULL);
+ if (rc < 0)
+ goto fail;
+ }
+
+ if (!dev->isoc_ctl.analog_bufs.num_bufs)
+ urb_init = 1;
+
+ if (urb_init) {
+ if (em28xx_vbi_supported(dev) == 1)
+ rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE,
+ EM28XX_NUM_PACKETS,
+ EM28XX_NUM_BUFS,
+ dev->max_pkt_size,
+ em28xx_isoc_copy_vbi);
+ else
+ rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE,
+ EM28XX_NUM_PACKETS,
+ EM28XX_NUM_BUFS,
+ dev->max_pkt_size,
+ em28xx_isoc_copy);
+ if (rc < 0)
+ goto fail;
+ }
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+fail:
+ free_buffer(vq, buf);
+ return rc;
+}
+
+static void
+buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct em28xx_buffer *buf = container_of(vb,
+ struct em28xx_buffer,
+ vb);
+ struct em28xx_fh *fh = vq->priv_data;
+ struct em28xx *dev = fh->dev;
+ struct em28xx_dmaqueue *vidq = &dev->vidq;
+
+ buf->vb.state = VIDEOBUF_QUEUED;
+ list_add_tail(&buf->vb.queue, &vidq->active);
+
+}
+
+static void buffer_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct em28xx_buffer *buf = container_of(vb,
+ struct em28xx_buffer,
+ vb);
+ struct em28xx_fh *fh = vq->priv_data;
+ struct em28xx *dev = (struct em28xx *)fh->dev;
+
+ em28xx_isocdbg("em28xx: called buffer_release\n");
+
+ free_buffer(vq, buf);
+}
+
+static struct videobuf_queue_ops em28xx_video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/********************* v4l2 interface **************************************/
+
+static void video_mux(struct em28xx *dev, int index)
+{
+ dev->ctl_input = index;
+ dev->ctl_ainput = INPUT(index)->amux;
+ dev->ctl_aoutput = INPUT(index)->aout;
+
+ if (!dev->ctl_aoutput)
+ dev->ctl_aoutput = EM28XX_AOUT_MASTER;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing,
+ INPUT(index)->vmux, 0, 0);
+
+ if (dev->board.has_msp34xx) {
+ if (dev->i2s_speed) {
+ v4l2_device_call_all(&dev->v4l2_dev, 0, audio,
+ s_i2s_clock_freq, dev->i2s_speed);
+ }
+ /* Note: this is msp3400 specific */
+ v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing,
+ dev->ctl_ainput, MSP_OUTPUT(MSP_SC_IN_DSP_SCART1), 0);
+ }
+
+ if (dev->board.adecoder != EM28XX_NOADECODER) {
+ v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing,
+ dev->ctl_ainput, dev->ctl_aoutput, 0);
+ }
+
+ em28xx_audio_analog_set(dev);
+}
+
+/* Usage lock check functions */
+static int res_get(struct em28xx_fh *fh, unsigned int bit)
+{
+ struct em28xx *dev = fh->dev;
+
+ if (fh->resources & bit)
+ /* have it already allocated */
+ return 1;
+
+ /* is it free? */
+ if (dev->resources & bit) {
+ /* no, someone else uses it */
+ return 0;
+ }
+ /* it's free, grab it */
+ fh->resources |= bit;
+ dev->resources |= bit;
+ em28xx_videodbg("res: get %d\n", bit);
+ return 1;
+}
+
+static int res_check(struct em28xx_fh *fh, unsigned int bit)
+{
+ return fh->resources & bit;
+}
+
+static int res_locked(struct em28xx *dev, unsigned int bit)
+{
+ return dev->resources & bit;
+}
+
+static void res_free(struct em28xx_fh *fh, unsigned int bits)
+{
+ struct em28xx *dev = fh->dev;
+
+ BUG_ON((fh->resources & bits) != bits);
+
+ fh->resources &= ~bits;
+ dev->resources &= ~bits;
+ em28xx_videodbg("res: put %d\n", bits);
+}
+
+static int get_ressource(struct em28xx_fh *fh)
+{
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return EM28XX_RESOURCE_VIDEO;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ return EM28XX_RESOURCE_VBI;
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+/*
+ * ac97_queryctrl()
+ * return the ac97 supported controls
+ */
+static int ac97_queryctrl(struct v4l2_queryctrl *qc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) {
+ if (qc->id && qc->id == ac97_qctrl[i].id) {
+ memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc));
+ return 0;
+ }
+ }
+
+ /* Control is not ac97 related */
+ return 1;
+}
+
+/*
+ * ac97_get_ctrl()
+ * return the current values for ac97 mute and volume
+ */
+static int ac97_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value = dev->mute;
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+ ctrl->value = dev->volume;
+ return 0;
+ default:
+ /* Control is not ac97 related */
+ return 1;
+ }
+}
+
+/*
+ * ac97_set_ctrl()
+ * set values for ac97 mute and volume
+ */
+static int ac97_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++)
+ if (ctrl->id == ac97_qctrl[i].id)
+ goto handle;
+
+ /* Announce that hasn't handle it */
+ return 1;
+
+handle:
+ if (ctrl->value < ac97_qctrl[i].minimum ||
+ ctrl->value > ac97_qctrl[i].maximum)
+ return -ERANGE;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ dev->mute = ctrl->value;
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ dev->volume = ctrl->value;
+ break;
+ }
+
+ return em28xx_audio_analog_set(dev);
+}
+
+static int check_dev(struct em28xx *dev)
+{
+ if (dev->state & DEV_DISCONNECTED) {
+ em28xx_errdev("v4l2 ioctl: device not present\n");
+ return -ENODEV;
+ }
+
+ if (dev->state & DEV_MISCONFIGURED) {
+ em28xx_errdev("v4l2 ioctl: device is misconfigured; "
+ "close and open it again\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+static void get_scale(struct em28xx *dev,
+ unsigned int width, unsigned int height,
+ unsigned int *hscale, unsigned int *vscale)
+{
+ unsigned int maxw = norm_maxw(dev);
+ unsigned int maxh = norm_maxh(dev);
+
+ *hscale = (((unsigned long)maxw) << 12) / width - 4096L;
+ if (*hscale >= 0x4000)
+ *hscale = 0x3fff;
+
+ *vscale = (((unsigned long)maxh) << 12) / height - 4096L;
+ if (*vscale >= 0x4000)
+ *vscale = 0x3fff;
+}
+
+/* ------------------------------------------------------------------
+ IOCTL vidioc handling
+ ------------------------------------------------------------------*/
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ f->fmt.pix.width = dev->width;
+ f->fmt.pix.height = dev->height;
+ f->fmt.pix.pixelformat = dev->format->fourcc;
+ f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3;
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * dev->height;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+ /* FIXME: TOP? NONE? BOTTOM? ALTENATE? */
+ if (dev->progressive)
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ else
+ f->fmt.pix.field = dev->interlaced ?
+ V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP;
+ return 0;
+}
+
+static struct em28xx_fmt *format_by_fourcc(unsigned int fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(format); i++)
+ if (format[i].fourcc == fourcc)
+ return &format[i];
+
+ return NULL;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ unsigned int width = f->fmt.pix.width;
+ unsigned int height = f->fmt.pix.height;
+ unsigned int maxw = norm_maxw(dev);
+ unsigned int maxh = norm_maxh(dev);
+ unsigned int hscale, vscale;
+ struct em28xx_fmt *fmt;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (!fmt) {
+ em28xx_videodbg("Fourcc format (%08x) invalid.\n",
+ f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ if (dev->board.is_em2800) {
+ /* the em2800 can only scale down to 50% */
+ height = height > (3 * maxh / 4) ? maxh : maxh / 2;
+ width = width > (3 * maxw / 4) ? maxw : maxw / 2;
+ /* MaxPacketSize for em2800 is too small to capture at full resolution
+ * use half of maxw as the scaler can only scale to 50% */
+ if (width == maxw && height == maxh)
+ width /= 2;
+ } else {
+ /* width must even because of the YUYV format
+ height must be even because of interlacing */
+ v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh,
+ 1, 0);
+ }
+
+ get_scale(dev, width, height, &hscale, &vscale);
+
+ width = (((unsigned long)maxw) << 12) / (hscale + 4096L);
+ height = (((unsigned long)maxh) << 12) / (vscale + 4096L);
+
+ f->fmt.pix.width = width;
+ f->fmt.pix.height = height;
+ f->fmt.pix.pixelformat = fmt->fourcc;
+ f->fmt.pix.bytesperline = (dev->width * fmt->depth + 7) >> 3;
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ if (dev->progressive)
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ else
+ f->fmt.pix.field = dev->interlaced ?
+ V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP;
+
+ return 0;
+}
+
+static int em28xx_set_video_format(struct em28xx *dev, unsigned int fourcc,
+ unsigned width, unsigned height)
+{
+ struct em28xx_fmt *fmt;
+
+ fmt = format_by_fourcc(fourcc);
+ if (!fmt)
+ return -EINVAL;
+
+ dev->format = fmt;
+ dev->width = width;
+ dev->height = height;
+
+ /* set new image size */
+ get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
+
+ em28xx_set_alternate(dev);
+ em28xx_resolution_set(dev);
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ vidioc_try_fmt_vid_cap(file, priv, f);
+
+ if (videobuf_queue_is_busy(&fh->vb_vidq)) {
+ em28xx_errdev("%s queue busy\n", __func__);
+ return -EBUSY;
+ }
+
+ return em28xx_set_video_format(dev, f->fmt.pix.pixelformat,
+ f->fmt.pix.width, f->fmt.pix.height);
+}
+
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ *norm = dev->norm;
+
+ return 0;
+}
+
+static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, video, querystd, norm);
+
+ return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ struct v4l2_format f;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ dev->norm = *norm;
+
+ /* Adjusts width/height, if needed */
+ f.fmt.pix.width = dev->width;
+ f.fmt.pix.height = dev->height;
+ vidioc_try_fmt_vid_cap(file, priv, &f);
+
+ /* set new image size */
+ dev->width = f.fmt.pix.width;
+ dev->height = f.fmt.pix.height;
+ get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
+
+ em28xx_resolution_set(dev);
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
+
+ return 0;
+}
+
+static int vidioc_g_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *p)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc = 0;
+
+ if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (dev->board.is_webcam)
+ rc = v4l2_device_call_until_err(&dev->v4l2_dev, 0,
+ video, g_parm, p);
+ else
+ v4l2_video_std_frame_period(dev->norm,
+ &p->parm.capture.timeperframe);
+
+ return rc;
+}
+
+static int vidioc_s_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *p)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ if (!dev->board.is_webcam)
+ return -EINVAL;
+
+ if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ return v4l2_device_call_until_err(&dev->v4l2_dev, 0, video, s_parm, p);
+}
+
+static const char *iname[] = {
+ [EM28XX_VMUX_COMPOSITE1] = "Composite1",
+ [EM28XX_VMUX_COMPOSITE2] = "Composite2",
+ [EM28XX_VMUX_COMPOSITE3] = "Composite3",
+ [EM28XX_VMUX_COMPOSITE4] = "Composite4",
+ [EM28XX_VMUX_SVIDEO] = "S-Video",
+ [EM28XX_VMUX_TELEVISION] = "Television",
+ [EM28XX_VMUX_CABLE] = "Cable TV",
+ [EM28XX_VMUX_DVB] = "DVB",
+ [EM28XX_VMUX_DEBUG] = "for debug only",
+};
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ unsigned int n;
+
+ n = i->index;
+ if (n >= MAX_EM28XX_INPUT)
+ return -EINVAL;
+ if (0 == INPUT(n)->type)
+ return -EINVAL;
+
+ i->index = n;
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+
+ strcpy(i->name, iname[INPUT(n)->type]);
+
+ if ((EM28XX_VMUX_TELEVISION == INPUT(n)->type) ||
+ (EM28XX_VMUX_CABLE == INPUT(n)->type))
+ i->type = V4L2_INPUT_TYPE_TUNER;
+
+ i->std = dev->vdev->tvnorms;
+
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ *i = dev->ctl_input;
+
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (i >= MAX_EM28XX_INPUT)
+ return -EINVAL;
+ if (0 == INPUT(i)->type)
+ return -EINVAL;
+
+ dev->ctl_input = i;
+
+ video_mux(dev, dev->ctl_input);
+ return 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ if (!dev->audio_mode.has_audio)
+ return -EINVAL;
+
+ switch (a->index) {
+ case EM28XX_AMUX_VIDEO:
+ strcpy(a->name, "Television");
+ break;
+ case EM28XX_AMUX_LINE_IN:
+ strcpy(a->name, "Line In");
+ break;
+ case EM28XX_AMUX_VIDEO2:
+ strcpy(a->name, "Television alt");
+ break;
+ case EM28XX_AMUX_PHONE:
+ strcpy(a->name, "Phone");
+ break;
+ case EM28XX_AMUX_MIC:
+ strcpy(a->name, "Mic");
+ break;
+ case EM28XX_AMUX_CD:
+ strcpy(a->name, "CD");
+ break;
+ case EM28XX_AMUX_AUX:
+ strcpy(a->name, "Aux");
+ break;
+ case EM28XX_AMUX_PCM_OUT:
+ strcpy(a->name, "PCM");
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ a->index = dev->ctl_ainput;
+ a->capability = V4L2_AUDCAP_STEREO;
+
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+
+ if (!dev->audio_mode.has_audio)
+ return -EINVAL;
+
+ if (a->index >= MAX_EM28XX_INPUT)
+ return -EINVAL;
+ if (0 == INPUT(a->index)->type)
+ return -EINVAL;
+
+ dev->ctl_ainput = INPUT(a->index)->amux;
+ dev->ctl_aoutput = INPUT(a->index)->aout;
+
+ if (!dev->ctl_aoutput)
+ dev->ctl_aoutput = EM28XX_AOUT_MASTER;
+
+ return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int id = qc->id;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ memset(qc, 0, sizeof(*qc));
+
+ qc->id = id;
+
+ /* enumerate AC97 controls */
+ if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
+ rc = ac97_queryctrl(qc);
+ if (!rc)
+ return 0;
+ }
+
+ /* enumerate V4L2 device controls */
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, qc);
+
+ if (qc->type)
+ return 0;
+ else
+ return -EINVAL;
+}
+
+/*
+ * FIXME: This is an indirect way to check if a control exists at a
+ * subdev. Instead of that hack, maybe the better would be to change all
+ * subdevs to return -ENOIOCTLCMD, if an ioctl is not supported.
+ */
+static int check_subdev_ctrl(struct em28xx *dev, int id)
+{
+ struct v4l2_queryctrl qc;
+
+ memset(&qc, 0, sizeof(qc));
+ qc.id = id;
+
+ /* enumerate V4L2 device controls */
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, &qc);
+
+ if (qc.type)
+ return 0;
+ else
+ return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+ rc = 0;
+
+ /* Set an AC97 control */
+ if (dev->audio_mode.ac97 != EM28XX_NO_AC97)
+ rc = ac97_get_ctrl(dev, ctrl);
+ else
+ rc = 1;
+
+ /* It were not an AC97 control. Sends it to the v4l2 dev interface */
+ if (rc == 1) {
+ if (check_subdev_ctrl(dev, ctrl->id))
+ return -EINVAL;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ /* Set an AC97 control */
+ if (dev->audio_mode.ac97 != EM28XX_NO_AC97)
+ rc = ac97_set_ctrl(dev, ctrl);
+ else
+ rc = 1;
+
+ /* It isn't an AC97 control. Sends it to the v4l2 dev interface */
+ if (rc == 1) {
+ rc = check_subdev_ctrl(dev, ctrl->id);
+ if (!rc)
+ v4l2_device_call_all(&dev->v4l2_dev, 0,
+ core, s_ctrl, ctrl);
+ /*
+ * In the case of non-AC97 volume controls, we still need
+ * to do some setups at em28xx, in order to mute/unmute
+ * and to adjust audio volume. However, the value ranges
+ * should be checked by the corresponding V4L subdriver.
+ */
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ dev->mute = ctrl->value;
+ rc = em28xx_audio_analog_set(dev);
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ dev->volume = ctrl->value;
+ rc = em28xx_audio_analog_set(dev);
+ }
+ }
+ return (rc < 0) ? rc : 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ strcpy(t->name, "Tuner");
+ t->type = V4L2_TUNER_ANALOG_TV;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t);
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t);
+ return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ f->frequency = dev->ctl_freq;
+ return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (0 != f->tuner)
+ return -EINVAL;
+
+ if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV))
+ return -EINVAL;
+ if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO))
+ return -EINVAL;
+
+ dev->ctl_freq = f->frequency;
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f);
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int em28xx_reg_len(int reg)
+{
+ switch (reg) {
+ case EM28XX_R40_AC97LSB:
+ case EM28XX_R30_HSCALELOW:
+ case EM28XX_R32_VSCALELOW:
+ return 2;
+ default:
+ return 1;
+ }
+}
+
+static int vidioc_g_chip_ident(struct file *file, void *priv,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_chip_ident, chip);
+
+ return 0;
+}
+
+
+static int vidioc_g_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int ret;
+
+ switch (reg->match.type) {
+ case V4L2_CHIP_MATCH_AC97:
+ ret = em28xx_read_ac97(dev, reg->reg);
+ if (ret < 0)
+ return ret;
+
+ reg->val = ret;
+ reg->size = 1;
+ return 0;
+ case V4L2_CHIP_MATCH_I2C_DRIVER:
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg);
+ return 0;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ /* TODO: is this correct? */
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg);
+ return 0;
+ default:
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+ }
+
+ /* Match host */
+ reg->size = em28xx_reg_len(reg->reg);
+ if (reg->size == 1) {
+ ret = em28xx_read_reg(dev, reg->reg);
+
+ if (ret < 0)
+ return ret;
+
+ reg->val = ret;
+ } else {
+ __le16 val = 0;
+ ret = em28xx_read_reg_req_len(dev, USB_REQ_GET_STATUS,
+ reg->reg, (char *)&val, 2);
+ if (ret < 0)
+ return ret;
+
+ reg->val = le16_to_cpu(val);
+ }
+
+ return 0;
+}
+
+static int vidioc_s_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ __le16 buf;
+
+ switch (reg->match.type) {
+ case V4L2_CHIP_MATCH_AC97:
+ return em28xx_write_ac97(dev, reg->reg, reg->val);
+ case V4L2_CHIP_MATCH_I2C_DRIVER:
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg);
+ return 0;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ /* TODO: is this correct? */
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg);
+ return 0;
+ default:
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+ }
+
+ /* Match host */
+ buf = cpu_to_le16(reg->val);
+
+ return em28xx_write_regs(dev, reg->reg, (char *)&buf,
+ em28xx_reg_len(reg->reg));
+}
+#endif
+
+
+static int vidioc_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cc)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ cc->bounds.left = 0;
+ cc->bounds.top = 0;
+ cc->bounds.width = dev->width;
+ cc->bounds.height = dev->height;
+ cc->defrect = cc->bounds;
+ cc->pixelaspect.numerator = 54; /* 4:3 FIXME: remove magic numbers */
+ cc->pixelaspect.denominator = 59;
+
+ return 0;
+}
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc = -EINVAL;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (unlikely(type != fh->type))
+ return -EINVAL;
+
+ em28xx_videodbg("vidioc_streamon fh=%p t=%d fh->res=%d dev->res=%d\n",
+ fh, type, fh->resources, dev->resources);
+
+ if (unlikely(!res_get(fh, get_ressource(fh))))
+ return -EBUSY;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ rc = videobuf_streamon(&fh->vb_vidq);
+ else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ rc = videobuf_streamon(&fh->vb_vbiq);
+
+ return rc;
+}
+
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)
+ return -EINVAL;
+ if (type != fh->type)
+ return -EINVAL;
+
+ em28xx_videodbg("vidioc_streamoff fh=%p t=%d fh->res=%d dev->res=%d\n",
+ fh, type, fh->resources, dev->resources);
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (res_check(fh, EM28XX_RESOURCE_VIDEO)) {
+ videobuf_streamoff(&fh->vb_vidq);
+ res_free(fh, EM28XX_RESOURCE_VIDEO);
+ }
+ } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ if (res_check(fh, EM28XX_RESOURCE_VBI)) {
+ videobuf_streamoff(&fh->vb_vbiq);
+ res_free(fh, EM28XX_RESOURCE_VBI);
+ }
+ }
+
+ return 0;
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
+ strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
+ usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
+
+ cap->capabilities =
+ V4L2_CAP_SLICED_VBI_CAPTURE |
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+
+ if (dev->vbi_dev)
+ cap->capabilities |= V4L2_CAP_VBI_CAPTURE;
+
+ if (dev->audio_mode.has_audio)
+ cap->capabilities |= V4L2_CAP_AUDIO;
+
+ if (dev->tuner_type != TUNER_ABSENT)
+ cap->capabilities |= V4L2_CAP_TUNER;
+
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (unlikely(f->index >= ARRAY_SIZE(format)))
+ return -EINVAL;
+
+ strlcpy(f->description, format[f->index].name, sizeof(f->description));
+ f->pixelformat = format[f->index].fourcc;
+
+ return 0;
+}
+
+static int vidioc_enum_framesizes(struct file *file, void *priv,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ struct em28xx_fmt *fmt;
+ unsigned int maxw = norm_maxw(dev);
+ unsigned int maxh = norm_maxh(dev);
+
+ fmt = format_by_fourcc(fsize->pixel_format);
+ if (!fmt) {
+ em28xx_videodbg("Fourcc format (%08x) invalid.\n",
+ fsize->pixel_format);
+ return -EINVAL;
+ }
+
+ if (dev->board.is_em2800) {
+ if (fsize->index > 1)
+ return -EINVAL;
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = maxw / (1 + fsize->index);
+ fsize->discrete.height = maxh / (1 + fsize->index);
+ return 0;
+ }
+
+ if (fsize->index != 0)
+ return -EINVAL;
+
+ /* Report a continuous range */
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise.min_width = 48;
+ fsize->stepwise.min_height = 32;
+ fsize->stepwise.max_width = maxw;
+ fsize->stepwise.max_height = maxh;
+ fsize->stepwise.step_width = 1;
+ fsize->stepwise.step_height = 1;
+ return 0;
+}
+
+/* Sliced VBI ioctls */
+static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ f->fmt.sliced.service_set = 0;
+ v4l2_device_call_all(&dev->v4l2_dev, 0, vbi, g_sliced_fmt, &f->fmt.sliced);
+
+ if (f->fmt.sliced.service_set == 0)
+ rc = -EINVAL;
+
+ return rc;
+}
+
+static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, vbi, g_sliced_fmt, &f->fmt.sliced);
+
+ if (f->fmt.sliced.service_set == 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+/* RAW VBI ioctls */
+
+static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
+ struct v4l2_format *format)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ format->fmt.vbi.samples_per_line = dev->vbi_width;
+ format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+ format->fmt.vbi.offset = 0;
+ format->fmt.vbi.flags = 0;
+ format->fmt.vbi.sampling_rate = 6750000 * 4 / 2;
+ format->fmt.vbi.count[0] = dev->vbi_height;
+ format->fmt.vbi.count[1] = dev->vbi_height;
+
+ /* Varies by video standard (NTSC, PAL, etc.) */
+ if (dev->norm & V4L2_STD_525_60) {
+ /* NTSC */
+ format->fmt.vbi.start[0] = 10;
+ format->fmt.vbi.start[1] = 273;
+ } else if (dev->norm & V4L2_STD_625_50) {
+ /* PAL */
+ format->fmt.vbi.start[0] = 6;
+ format->fmt.vbi.start[1] = 318;
+ }
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vbi_cap(struct file *file, void *priv,
+ struct v4l2_format *format)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ format->fmt.vbi.samples_per_line = dev->vbi_width;
+ format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+ format->fmt.vbi.offset = 0;
+ format->fmt.vbi.flags = 0;
+ format->fmt.vbi.sampling_rate = 6750000 * 4 / 2;
+ format->fmt.vbi.count[0] = dev->vbi_height;
+ format->fmt.vbi.count[1] = dev->vbi_height;
+
+ /* Varies by video standard (NTSC, PAL, etc.) */
+ if (dev->norm & V4L2_STD_525_60) {
+ /* NTSC */
+ format->fmt.vbi.start[0] = 10;
+ format->fmt.vbi.start[1] = 273;
+ } else if (dev->norm & V4L2_STD_625_50) {
+ /* PAL */
+ format->fmt.vbi.start[0] = 6;
+ format->fmt.vbi.start[1] = 318;
+ }
+
+ return 0;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *rb)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return videobuf_reqbufs(&fh->vb_vidq, rb);
+ else
+ return videobuf_reqbufs(&fh->vb_vbiq, rb);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return videobuf_querybuf(&fh->vb_vidq, b);
+ else {
+ /* FIXME: I'm not sure yet whether this is a bug in zvbi or
+ the videobuf framework, but we probably shouldn't be
+ returning a buffer larger than that which was asked for.
+ At a minimum, it causes a crash in zvbi since it does
+ a memcpy based on the source buffer length */
+ int result = videobuf_querybuf(&fh->vb_vbiq, b);
+ b->length = dev->vbi_width * dev->vbi_height * 2;
+
+ return result;
+ }
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return videobuf_qbuf(&fh->vb_vidq, b);
+ else
+ return videobuf_qbuf(&fh->vb_vbiq, b);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags &
+ O_NONBLOCK);
+ else
+ return videobuf_dqbuf(&fh->vb_vbiq, b, file->f_flags &
+ O_NONBLOCK);
+}
+
+/* ----------------------------------------------------------- */
+/* RADIO ESPECIFIC IOCTLS */
+/* ----------------------------------------------------------- */
+
+static int radio_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct em28xx *dev = ((struct em28xx_fh *)priv)->dev;
+
+ strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
+ strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
+ usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
+
+ cap->capabilities = V4L2_CAP_TUNER;
+ return 0;
+}
+
+static int radio_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct em28xx *dev = ((struct em28xx_fh *)priv)->dev;
+
+ if (unlikely(t->index > 0))
+ return -EINVAL;
+
+ strcpy(t->name, "Radio");
+ t->type = V4L2_TUNER_RADIO;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t);
+
+ return 0;
+}
+
+static int radio_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
+ strcpy(i->name, "Radio");
+ i->type = V4L2_INPUT_TYPE_TUNER;
+
+ return 0;
+}
+
+static int radio_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ if (unlikely(a->index))
+ return -EINVAL;
+
+ strcpy(a->name, "Radio");
+ return 0;
+}
+
+static int radio_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct em28xx *dev = ((struct em28xx_fh *)priv)->dev;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t);
+
+ return 0;
+}
+
+static int radio_s_audio(struct file *file, void *fh,
+ struct v4l2_audio *a)
+{
+ return 0;
+}
+
+static int radio_s_input(struct file *file, void *fh, unsigned int i)
+{
+ return 0;
+}
+
+static int radio_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ int i;
+
+ if (qc->id < V4L2_CID_BASE ||
+ qc->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) {
+ if (qc->id && qc->id == ac97_qctrl[i].id) {
+ memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc));
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * em28xx_v4l2_open()
+ * inits the device and starts isoc transfer
+ */
+static int em28xx_v4l2_open(struct file *filp)
+{
+ int errCode = 0, radio = 0;
+ struct video_device *vdev = video_devdata(filp);
+ struct em28xx *dev = video_drvdata(filp);
+ enum v4l2_buf_type fh_type = 0;
+ struct em28xx_fh *fh;
+ enum v4l2_field field;
+
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
+ fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ break;
+ case VFL_TYPE_VBI:
+ fh_type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ break;
+ case VFL_TYPE_RADIO:
+ radio = 1;
+ break;
+ }
+
+ em28xx_videodbg("open dev=%s type=%s users=%d\n",
+ video_device_node_name(vdev), v4l2_type_names[fh_type],
+ dev->users);
+
+
+ fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL);
+ if (!fh) {
+ em28xx_errdev("em28xx-video.c: Out of memory?!\n");
+ return -ENOMEM;
+ }
+ fh->dev = dev;
+ fh->radio = radio;
+ fh->type = fh_type;
+ filp->private_data = fh;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
+ em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
+ em28xx_set_alternate(dev);
+ em28xx_resolution_set(dev);
+
+ /* Needed, since GPIO might have disabled power of
+ some i2c device
+ */
+ em28xx_wake_i2c(dev);
+
+ }
+ if (fh->radio) {
+ em28xx_videodbg("video_open: setting radio device\n");
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio);
+ }
+
+ dev->users++;
+
+ if (dev->progressive)
+ field = V4L2_FIELD_NONE;
+ else
+ field = V4L2_FIELD_INTERLACED;
+
+ videobuf_queue_vmalloc_init(&fh->vb_vidq, &em28xx_video_qops,
+ NULL, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE, field,
+ sizeof(struct em28xx_buffer), fh, &dev->lock);
+
+ videobuf_queue_vmalloc_init(&fh->vb_vbiq, &em28xx_vbi_qops,
+ NULL, &dev->slock,
+ V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_FIELD_SEQ_TB,
+ sizeof(struct em28xx_buffer), fh, &dev->lock);
+
+ return errCode;
+}
+
+/*
+ * em28xx_realease_resources()
+ * unregisters the v4l2,i2c and usb devices
+ * called when the device gets disconected or at module unload
+*/
+void em28xx_release_analog_resources(struct em28xx *dev)
+{
+
+ /*FIXME: I2C IR should be disconnected */
+
+ if (dev->radio_dev) {
+ if (video_is_registered(dev->radio_dev))
+ video_unregister_device(dev->radio_dev);
+ else
+ video_device_release(dev->radio_dev);
+ dev->radio_dev = NULL;
+ }
+ if (dev->vbi_dev) {
+ em28xx_info("V4L2 device %s deregistered\n",
+ video_device_node_name(dev->vbi_dev));
+ if (video_is_registered(dev->vbi_dev))
+ video_unregister_device(dev->vbi_dev);
+ else
+ video_device_release(dev->vbi_dev);
+ dev->vbi_dev = NULL;
+ }
+ if (dev->vdev) {
+ em28xx_info("V4L2 device %s deregistered\n",
+ video_device_node_name(dev->vdev));
+ if (video_is_registered(dev->vdev))
+ video_unregister_device(dev->vdev);
+ else
+ video_device_release(dev->vdev);
+ dev->vdev = NULL;
+ }
+}
+
+/*
+ * em28xx_v4l2_close()
+ * stops streaming and deallocates all resources allocated by the v4l2
+ * calls and ioctls
+ */
+static int em28xx_v4l2_close(struct file *filp)
+{
+ struct em28xx_fh *fh = filp->private_data;
+ struct em28xx *dev = fh->dev;
+ int errCode;
+
+ em28xx_videodbg("users=%d\n", dev->users);
+
+ if (res_check(fh, EM28XX_RESOURCE_VIDEO)) {
+ videobuf_stop(&fh->vb_vidq);
+ res_free(fh, EM28XX_RESOURCE_VIDEO);
+ }
+
+ if (res_check(fh, EM28XX_RESOURCE_VBI)) {
+ videobuf_stop(&fh->vb_vbiq);
+ res_free(fh, EM28XX_RESOURCE_VBI);
+ }
+
+ if (dev->users == 1) {
+ /* the device is already disconnect,
+ free the remaining resources */
+ if (dev->state & DEV_DISCONNECTED) {
+ em28xx_release_resources(dev);
+ kfree(dev->alt_max_pkt_size);
+ kfree(dev);
+ return 0;
+ }
+
+ /* Save some power by putting tuner to sleep */
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0);
+
+ /* do this before setting alternate! */
+ em28xx_uninit_isoc(dev, EM28XX_ANALOG_MODE);
+ em28xx_set_mode(dev, EM28XX_SUSPEND);
+
+ /* set alternate 0 */
+ dev->alt = 0;
+ em28xx_videodbg("setting alternate 0\n");
+ errCode = usb_set_interface(dev->udev, 0, 0);
+ if (errCode < 0) {
+ em28xx_errdev("cannot change alternate number to "
+ "0 (error=%i)\n", errCode);
+ }
+ }
+
+ videobuf_mmap_free(&fh->vb_vidq);
+ videobuf_mmap_free(&fh->vb_vbiq);
+ kfree(fh);
+ dev->users--;
+ wake_up_interruptible_nr(&dev->open, 1);
+ return 0;
+}
+
+/*
+ * em28xx_v4l2_read()
+ * will allocate buffers when called for the first time
+ */
+static ssize_t
+em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct em28xx_fh *fh = filp->private_data;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ /* FIXME: read() is not prepared to allow changing the video
+ resolution while streaming. Seems a bug at em28xx_set_fmt
+ */
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (res_locked(dev, EM28XX_RESOURCE_VIDEO))
+ return -EBUSY;
+
+ return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0,
+ filp->f_flags & O_NONBLOCK);
+ }
+
+
+ if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ if (!res_get(fh, EM28XX_RESOURCE_VBI))
+ return -EBUSY;
+
+ return videobuf_read_stream(&fh->vb_vbiq, buf, count, pos, 0,
+ filp->f_flags & O_NONBLOCK);
+ }
+
+ return 0;
+}
+
+/*
+ * em28xx_v4l2_poll()
+ * will allocate buffers when called for the first time
+ */
+static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table *wait)
+{
+ struct em28xx_fh *fh = filp->private_data;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (!res_get(fh, EM28XX_RESOURCE_VIDEO))
+ return POLLERR;
+ return videobuf_poll_stream(filp, &fh->vb_vidq, wait);
+ } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ if (!res_get(fh, EM28XX_RESOURCE_VBI))
+ return POLLERR;
+ return videobuf_poll_stream(filp, &fh->vb_vbiq, wait);
+ } else {
+ return POLLERR;
+ }
+}
+
+/*
+ * em28xx_v4l2_mmap()
+ */
+static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct em28xx_fh *fh = filp->private_data;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ rc = videobuf_mmap_mapper(&fh->vb_vidq, vma);
+ else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ rc = videobuf_mmap_mapper(&fh->vb_vbiq, vma);
+
+ em28xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n",
+ (unsigned long)vma->vm_start,
+ (unsigned long)vma->vm_end-(unsigned long)vma->vm_start,
+ rc);
+
+ return rc;
+}
+
+static const struct v4l2_file_operations em28xx_v4l_fops = {
+ .owner = THIS_MODULE,
+ .open = em28xx_v4l2_open,
+ .release = em28xx_v4l2_close,
+ .read = em28xx_v4l2_read,
+ .poll = em28xx_v4l2_poll,
+ .mmap = em28xx_v4l2_mmap,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops video_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
+ .vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap,
+ .vidioc_enum_framesizes = vidioc_enum_framesizes,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_cropcap = vidioc_cropcap,
+ .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap,
+ .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap,
+ .vidioc_s_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap,
+
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_querystd = vidioc_querystd,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_g_parm = vidioc_g_parm,
+ .vidioc_s_parm = vidioc_s_parm,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+ .vidioc_g_chip_ident = vidioc_g_chip_ident,
+#endif
+};
+
+static const struct video_device em28xx_video_template = {
+ .fops = &em28xx_v4l_fops,
+ .release = video_device_release,
+ .ioctl_ops = &video_ioctl_ops,
+
+ .tvnorms = V4L2_STD_ALL,
+ .current_norm = V4L2_STD_PAL,
+};
+
+static const struct v4l2_file_operations radio_fops = {
+ .owner = THIS_MODULE,
+ .open = em28xx_v4l2_open,
+ .release = em28xx_v4l2_close,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops radio_ioctl_ops = {
+ .vidioc_querycap = radio_querycap,
+ .vidioc_g_tuner = radio_g_tuner,
+ .vidioc_enum_input = radio_enum_input,
+ .vidioc_g_audio = radio_g_audio,
+ .vidioc_s_tuner = radio_s_tuner,
+ .vidioc_s_audio = radio_s_audio,
+ .vidioc_s_input = radio_s_input,
+ .vidioc_queryctrl = radio_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+#endif
+};
+
+static struct video_device em28xx_radio_template = {
+ .name = "em28xx-radio",
+ .fops = &radio_fops,
+ .ioctl_ops = &radio_ioctl_ops,
+};
+
+/******************************** usb interface ******************************/
+
+
+
+static struct video_device *em28xx_vdev_init(struct em28xx *dev,
+ const struct video_device *template,
+ const char *type_name)
+{
+ struct video_device *vfd;
+
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+
+ *vfd = *template;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+ vfd->release = video_device_release;
+ vfd->debug = video_debug;
+ vfd->lock = &dev->lock;
+
+ snprintf(vfd->name, sizeof(vfd->name), "%s %s",
+ dev->name, type_name);
+
+ video_set_drvdata(vfd, dev);
+ return vfd;
+}
+
+int em28xx_register_analog_devices(struct em28xx *dev)
+{
+ u8 val;
+ int ret;
+ unsigned int maxw;
+
+ printk(KERN_INFO "%s: v4l2 driver version %s\n",
+ dev->name, EM28XX_VERSION);
+
+ /* set default norm */
+ dev->norm = em28xx_video_template.current_norm;
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
+ dev->interlaced = EM28XX_INTERLACED_DEFAULT;
+ dev->ctl_input = 0;
+
+ /* Analog specific initialization */
+ dev->format = &format[0];
+
+ maxw = norm_maxw(dev);
+ /* MaxPacketSize for em2800 is too small to capture at full resolution
+ * use half of maxw as the scaler can only scale to 50% */
+ if (dev->board.is_em2800)
+ maxw /= 2;
+
+ em28xx_set_video_format(dev, format[0].fourcc,
+ maxw, norm_maxh(dev));
+
+ video_mux(dev, dev->ctl_input);
+
+ /* Audio defaults */
+ dev->mute = 1;
+ dev->volume = 0x1f;
+
+/* em28xx_write_reg(dev, EM28XX_R0E_AUDIOSRC, 0xc0); audio register */
+ val = (u8)em28xx_read_reg(dev, EM28XX_R0F_XCLK);
+ em28xx_write_reg(dev, EM28XX_R0F_XCLK,
+ (EM28XX_XCLK_AUDIO_UNMUTE | val));
+
+ em28xx_set_outfmt(dev);
+ em28xx_colorlevels_set_default(dev);
+ em28xx_compression_disable(dev);
+
+ /* allocate and fill video video_device struct */
+ dev->vdev = em28xx_vdev_init(dev, &em28xx_video_template, "video");
+ if (!dev->vdev) {
+ em28xx_errdev("cannot allocate video_device.\n");
+ return -ENODEV;
+ }
+
+ /* register v4l2 video video_device */
+ ret = video_register_device(dev->vdev, VFL_TYPE_GRABBER,
+ video_nr[dev->devno]);
+ if (ret) {
+ em28xx_errdev("unable to register video device (error=%i).\n",
+ ret);
+ return ret;
+ }
+
+ /* Allocate and fill vbi video_device struct */
+ if (em28xx_vbi_supported(dev) == 1) {
+ dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template,
+ "vbi");
+
+ /* register v4l2 vbi video_device */
+ ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
+ vbi_nr[dev->devno]);
+ if (ret < 0) {
+ em28xx_errdev("unable to register vbi device\n");
+ return ret;
+ }
+ }
+
+ if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) {
+ dev->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template,
+ "radio");
+ if (!dev->radio_dev) {
+ em28xx_errdev("cannot allocate video_device.\n");
+ return -ENODEV;
+ }
+ ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO,
+ radio_nr[dev->devno]);
+ if (ret < 0) {
+ em28xx_errdev("can't register radio device\n");
+ return ret;
+ }
+ em28xx_info("Registered radio device as %s\n",
+ video_device_node_name(dev->radio_dev));
+ }
+
+ em28xx_info("V4L2 video device registered as %s\n",
+ video_device_node_name(dev->vdev));
+
+ if (dev->vbi_dev)
+ em28xx_info("V4L2 VBI device registered as %s\n",
+ video_device_node_name(dev->vbi_dev));
+
+ return 0;
+}
diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
new file mode 100644
index 00000000..2868b19f
--- /dev/null
+++ b/drivers/media/video/em28xx/em28xx.h
@@ -0,0 +1,865 @@
+/*
+ em28xx.h - driver for Empia EM2800/EM2820/2840 USB video capture devices
+
+ Copyright (C) 2005 Markus Rechberger <mrechberger@gmail.com>
+ Ludovico Cavedon <cavedon@sssup.it>
+ Mauro Carvalho Chehab <mchehab@infradead.org>
+
+ Based on the em2800 driver from Sascha Sommer <saschasommer@freenet.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _EM28XX_H
+#define _EM28XX_H
+
+#include <linux/workqueue.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+
+#include <media/videobuf-vmalloc.h>
+#include <media/v4l2-device.h>
+#include <media/ir-kbd-i2c.h>
+#include <media/rc-core.h>
+#if defined(CONFIG_VIDEO_EM28XX_DVB) || defined(CONFIG_VIDEO_EM28XX_DVB_MODULE)
+#include <media/videobuf-dvb.h>
+#endif
+#include "tuner-xc2028.h"
+#include "xc5000.h"
+#include "em28xx-reg.h"
+
+/* Boards supported by driver */
+#define EM2800_BOARD_UNKNOWN 0
+#define EM2820_BOARD_UNKNOWN 1
+#define EM2820_BOARD_TERRATEC_CINERGY_250 2
+#define EM2820_BOARD_PINNACLE_USB_2 3
+#define EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 4
+#define EM2820_BOARD_MSI_VOX_USB_2 5
+#define EM2800_BOARD_TERRATEC_CINERGY_200 6
+#define EM2800_BOARD_LEADTEK_WINFAST_USBII 7
+#define EM2800_BOARD_KWORLD_USB2800 8
+#define EM2820_BOARD_PINNACLE_DVC_90 9
+#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 10
+#define EM2880_BOARD_TERRATEC_HYBRID_XS 11
+#define EM2820_BOARD_KWORLD_PVRTV2800RF 12
+#define EM2880_BOARD_TERRATEC_PRODIGY_XS 13
+#define EM2820_BOARD_PROLINK_PLAYTV_USB2 14
+#define EM2800_BOARD_VGEAR_POCKETTV 15
+#define EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 16
+#define EM2880_BOARD_PINNACLE_PCTV_HD_PRO 17
+#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2 18
+#define EM2860_BOARD_SAA711X_REFERENCE_DESIGN 19
+#define EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 20
+#define EM2800_BOARD_GRABBEEX_USB2800 21
+#define EM2750_BOARD_UNKNOWN 22
+#define EM2750_BOARD_DLCW_130 23
+#define EM2820_BOARD_DLINK_USB_TV 24
+#define EM2820_BOARD_GADMEI_UTV310 25
+#define EM2820_BOARD_HERCULES_SMART_TV_USB2 26
+#define EM2820_BOARD_PINNACLE_USB_2_FM1216ME 27
+#define EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE 28
+#define EM2860_BOARD_TVP5150_REFERENCE_DESIGN 29
+#define EM2820_BOARD_VIDEOLOGY_20K14XUSB 30
+#define EM2821_BOARD_USBGEAR_VD204 31
+#define EM2821_BOARD_SUPERCOMP_USB_2 32
+#define EM2860_BOARD_ELGATO_VIDEO_CAPTURE 33
+#define EM2860_BOARD_TERRATEC_HYBRID_XS 34
+#define EM2860_BOARD_TYPHOON_DVD_MAKER 35
+#define EM2860_BOARD_NETGMBH_CAM 36
+#define EM2860_BOARD_GADMEI_UTV330 37
+#define EM2861_BOARD_YAKUMO_MOVIE_MIXER 38
+#define EM2861_BOARD_KWORLD_PVRTV_300U 39
+#define EM2861_BOARD_PLEXTOR_PX_TV100U 40
+#define EM2870_BOARD_KWORLD_350U 41
+#define EM2870_BOARD_KWORLD_355U 42
+#define EM2870_BOARD_TERRATEC_XS 43
+#define EM2870_BOARD_TERRATEC_XS_MT2060 44
+#define EM2870_BOARD_PINNACLE_PCTV_DVB 45
+#define EM2870_BOARD_COMPRO_VIDEOMATE 46
+#define EM2880_BOARD_KWORLD_DVB_305U 47
+#define EM2880_BOARD_KWORLD_DVB_310U 48
+#define EM2880_BOARD_MSI_DIGIVOX_AD 49
+#define EM2880_BOARD_MSI_DIGIVOX_AD_II 50
+#define EM2880_BOARD_TERRATEC_HYBRID_XS_FR 51
+#define EM2881_BOARD_DNT_DA2_HYBRID 52
+#define EM2881_BOARD_PINNACLE_HYBRID_PRO 53
+#define EM2882_BOARD_KWORLD_VS_DVBT 54
+#define EM2882_BOARD_TERRATEC_HYBRID_XS 55
+#define EM2882_BOARD_PINNACLE_HYBRID_PRO_330E 56
+#define EM2883_BOARD_KWORLD_HYBRID_330U 57
+#define EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU 58
+#define EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 60
+#define EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2 61
+#define EM2820_BOARD_GADMEI_TVR200 62
+#define EM2860_BOARD_KAIOMY_TVNPC_U2 63
+#define EM2860_BOARD_EASYCAP 64
+#define EM2820_BOARD_IODATA_GVMVP_SZ 65
+#define EM2880_BOARD_EMPIRE_DUAL_TV 66
+#define EM2860_BOARD_TERRATEC_GRABBY 67
+#define EM2860_BOARD_TERRATEC_AV350 68
+#define EM2882_BOARD_KWORLD_ATSC_315U 69
+#define EM2882_BOARD_EVGA_INDTUBE 70
+#define EM2820_BOARD_SILVERCREST_WEBCAM 71
+#define EM2861_BOARD_GADMEI_UTV330PLUS 72
+#define EM2870_BOARD_REDDO_DVB_C_USB_BOX 73
+#define EM2800_BOARD_VC211A 74
+#define EM2882_BOARD_DIKOM_DK300 75
+#define EM2870_BOARD_KWORLD_A340 76
+#define EM2874_BOARD_LEADERSHIP_ISDBT 77
+#define EM28174_BOARD_PCTV_290E 78
+#define EM2884_BOARD_TERRATEC_H5 79
+#define EM28174_BOARD_PCTV_460E 80
+#define EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C 81
+#define EM2884_BOARD_CINERGY_HTC_STICK 82
+#define EM2860_BOARD_HT_VIDBOX_NW03 83
+#define EM2874_BOARD_MAXMEDIA_UB425_TC 84
+#define EM2884_BOARD_PCTV_510E 85
+#define EM2884_BOARD_PCTV_520E 86
+
+/* Limits minimum and default number of buffers */
+#define EM28XX_MIN_BUF 4
+#define EM28XX_DEF_BUF 8
+
+/*Limits the max URB message size */
+#define URB_MAX_CTRL_SIZE 80
+
+/* Params for validated field */
+#define EM28XX_BOARD_NOT_VALIDATED 1
+#define EM28XX_BOARD_VALIDATED 0
+
+/* Params for em28xx_cmd() audio */
+#define EM28XX_START_AUDIO 1
+#define EM28XX_STOP_AUDIO 0
+
+/* maximum number of em28xx boards */
+#define EM28XX_MAXBOARDS 4 /*FIXME: should be bigger */
+
+/* maximum number of frames that can be queued */
+#define EM28XX_NUM_FRAMES 5
+/* number of frames that get used for v4l2_read() */
+#define EM28XX_NUM_READ_FRAMES 2
+
+/* number of buffers for isoc transfers */
+#define EM28XX_NUM_BUFS 5
+#define EM28XX_DVB_NUM_BUFS 5
+
+/* number of packets for each buffer
+ windows requests only 64 packets .. so we better do the same
+ this is what I found out for all alternate numbers there!
+ */
+#define EM28XX_NUM_PACKETS 64
+#define EM28XX_DVB_MAX_PACKETS 64
+
+#define EM28XX_INTERLACED_DEFAULT 1
+
+/*
+#define (use usbview if you want to get the other alternate number infos)
+#define
+#define alternate number 2
+#define Endpoint Address: 82
+ Direction: in
+ Attribute: 1
+ Type: Isoc
+ Max Packet Size: 1448
+ Interval: 125us
+
+ alternate number 7
+
+ Endpoint Address: 82
+ Direction: in
+ Attribute: 1
+ Type: Isoc
+ Max Packet Size: 3072
+ Interval: 125us
+*/
+
+/* time to wait when stopping the isoc transfer */
+#define EM28XX_URB_TIMEOUT \
+ msecs_to_jiffies(EM28XX_NUM_BUFS * EM28XX_NUM_PACKETS)
+
+/* time in msecs to wait for i2c writes to finish */
+#define EM2800_I2C_WRITE_TIMEOUT 20
+
+enum em28xx_mode {
+ EM28XX_SUSPEND,
+ EM28XX_ANALOG_MODE,
+ EM28XX_DIGITAL_MODE,
+};
+
+
+struct em28xx;
+
+struct em28xx_usb_isoc_bufs {
+ /* max packet size of isoc transaction */
+ int max_pkt_size;
+
+ /* number of packets in each buffer */
+ int num_packets;
+
+ /* number of allocated urbs */
+ int num_bufs;
+
+ /* urb for isoc transfers */
+ struct urb **urb;
+
+ /* transfer buffers for isoc transfer */
+ char **transfer_buffer;
+};
+
+struct em28xx_usb_isoc_ctl {
+ /* isoc transfer buffers for analog mode */
+ struct em28xx_usb_isoc_bufs analog_bufs;
+
+ /* isoc transfer buffers for digital mode */
+ struct em28xx_usb_isoc_bufs digital_bufs;
+
+ /* Last buffer command and region */
+ u8 cmd;
+ int pos, size, pktsize;
+
+ /* Last field: ODD or EVEN? */
+ int field;
+
+ /* Stores incomplete commands */
+ u32 tmp_buf;
+ int tmp_buf_len;
+
+ /* Stores already requested buffers */
+ struct em28xx_buffer *vid_buf;
+ struct em28xx_buffer *vbi_buf;
+
+ /* Stores the number of received fields */
+ int nfields;
+
+ /* isoc urb callback */
+ int (*isoc_copy) (struct em28xx *dev, struct urb *urb);
+
+};
+
+/* Struct to enumberate video formats */
+struct em28xx_fmt {
+ char *name;
+ u32 fourcc; /* v4l2 format id */
+ int depth;
+ int reg;
+};
+
+/* buffer for one video frame */
+struct em28xx_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+
+ struct list_head frame;
+ int top_field;
+ int receiving;
+};
+
+struct em28xx_dmaqueue {
+ struct list_head active;
+ struct list_head queued;
+
+ wait_queue_head_t wq;
+
+ /* Counters to control buffer fill */
+ int pos;
+};
+
+/* io methods */
+enum em28xx_io_method {
+ IO_NONE,
+ IO_READ,
+ IO_MMAP,
+};
+
+/* inputs */
+
+#define MAX_EM28XX_INPUT 4
+enum enum28xx_itype {
+ EM28XX_VMUX_COMPOSITE1 = 1,
+ EM28XX_VMUX_COMPOSITE2,
+ EM28XX_VMUX_COMPOSITE3,
+ EM28XX_VMUX_COMPOSITE4,
+ EM28XX_VMUX_SVIDEO,
+ EM28XX_VMUX_TELEVISION,
+ EM28XX_VMUX_CABLE,
+ EM28XX_VMUX_DVB,
+ EM28XX_VMUX_DEBUG,
+ EM28XX_RADIO,
+};
+
+enum em28xx_ac97_mode {
+ EM28XX_NO_AC97 = 0,
+ EM28XX_AC97_EM202,
+ EM28XX_AC97_SIGMATEL,
+ EM28XX_AC97_OTHER,
+};
+
+struct em28xx_audio_mode {
+ enum em28xx_ac97_mode ac97;
+
+ u16 ac97_feat;
+ u32 ac97_vendor_id;
+
+ unsigned int has_audio:1;
+
+ unsigned int i2s_3rates:1;
+ unsigned int i2s_5rates:1;
+};
+
+/* em28xx has two audio inputs: tuner and line in.
+ However, on most devices, an auxiliary AC97 codec device is used.
+ The AC97 device may have several different inputs and outputs,
+ depending on their model. So, it is possible to use AC97 mixer to
+ address more than two different entries.
+ */
+enum em28xx_amux {
+ /* This is the only entry for em28xx tuner input */
+ EM28XX_AMUX_VIDEO, /* em28xx tuner, AC97 mixer Video */
+
+ EM28XX_AMUX_LINE_IN, /* AC97 mixer Line In */
+
+ /* Some less-common mixer setups */
+ EM28XX_AMUX_VIDEO2, /* em28xx Line in, AC97 mixer Video */
+ EM28XX_AMUX_PHONE,
+ EM28XX_AMUX_MIC,
+ EM28XX_AMUX_CD,
+ EM28XX_AMUX_AUX,
+ EM28XX_AMUX_PCM_OUT,
+};
+
+enum em28xx_aout {
+ /* AC97 outputs */
+ EM28XX_AOUT_MASTER = 1 << 0,
+ EM28XX_AOUT_LINE = 1 << 1,
+ EM28XX_AOUT_MONO = 1 << 2,
+ EM28XX_AOUT_LFE = 1 << 3,
+ EM28XX_AOUT_SURR = 1 << 4,
+
+ /* PCM IN Mixer - used by AC97_RECORD_SELECT register */
+ EM28XX_AOUT_PCM_IN = 1 << 7,
+
+ /* Bits 10-8 are used to indicate the PCM IN record select */
+ EM28XX_AOUT_PCM_MIC_PCM = 0 << 8,
+ EM28XX_AOUT_PCM_CD = 1 << 8,
+ EM28XX_AOUT_PCM_VIDEO = 2 << 8,
+ EM28XX_AOUT_PCM_AUX = 3 << 8,
+ EM28XX_AOUT_PCM_LINE = 4 << 8,
+ EM28XX_AOUT_PCM_STEREO = 5 << 8,
+ EM28XX_AOUT_PCM_MONO = 6 << 8,
+ EM28XX_AOUT_PCM_PHONE = 7 << 8,
+};
+
+static inline int ac97_return_record_select(int a_out)
+{
+ return (a_out & 0x700) >> 8;
+}
+
+struct em28xx_reg_seq {
+ int reg;
+ unsigned char val, mask;
+ int sleep;
+};
+
+struct em28xx_input {
+ enum enum28xx_itype type;
+ unsigned int vmux;
+ enum em28xx_amux amux;
+ enum em28xx_aout aout;
+ struct em28xx_reg_seq *gpio;
+};
+
+#define INPUT(nr) (&em28xx_boards[dev->model].input[nr])
+
+enum em28xx_decoder {
+ EM28XX_NODECODER = 0,
+ EM28XX_TVP5150,
+ EM28XX_SAA711X,
+};
+
+enum em28xx_sensor {
+ EM28XX_NOSENSOR = 0,
+ EM28XX_MT9V011,
+ EM28XX_MT9M001,
+ EM28XX_MT9M111,
+};
+
+enum em28xx_adecoder {
+ EM28XX_NOADECODER = 0,
+ EM28XX_TVAUDIO,
+};
+
+struct em28xx_board {
+ char *name;
+ int vchannels;
+ int tuner_type;
+ int tuner_addr;
+
+ /* i2c flags */
+ unsigned int tda9887_conf;
+
+ /* GPIO sequences */
+ struct em28xx_reg_seq *dvb_gpio;
+ struct em28xx_reg_seq *suspend_gpio;
+ struct em28xx_reg_seq *tuner_gpio;
+ struct em28xx_reg_seq *mute_gpio;
+
+ unsigned int is_em2800:1;
+ unsigned int has_msp34xx:1;
+ unsigned int mts_firmware:1;
+ unsigned int max_range_640_480:1;
+ unsigned int has_dvb:1;
+ unsigned int has_snapshot_button:1;
+ unsigned int is_webcam:1;
+ unsigned int valid:1;
+ unsigned int has_ir_i2c:1;
+
+ unsigned char xclk, i2c_speed;
+ unsigned char radio_addr;
+ unsigned short tvaudio_addr;
+
+ enum em28xx_decoder decoder;
+ enum em28xx_adecoder adecoder;
+
+ struct em28xx_input input[MAX_EM28XX_INPUT];
+ struct em28xx_input radio;
+ char *ir_codes;
+};
+
+struct em28xx_eeprom {
+ u32 id; /* 0x9567eb1a */
+ u16 vendor_ID;
+ u16 product_ID;
+
+ u16 chip_conf;
+
+ u16 board_conf;
+
+ u16 string1, string2, string3;
+
+ u8 string_idx_table;
+};
+
+/* device states */
+enum em28xx_dev_state {
+ DEV_INITIALIZED = 0x01,
+ DEV_DISCONNECTED = 0x02,
+ DEV_MISCONFIGURED = 0x04,
+};
+
+#define EM28XX_AUDIO_BUFS 5
+#define EM28XX_NUM_AUDIO_PACKETS 64
+#define EM28XX_AUDIO_MAX_PACKET_SIZE 196 /* static value */
+#define EM28XX_CAPTURE_STREAM_EN 1
+
+/* em28xx extensions */
+#define EM28XX_AUDIO 0x10
+#define EM28XX_DVB 0x20
+
+/* em28xx resource types (used for res_get/res_lock etc */
+#define EM28XX_RESOURCE_VIDEO 0x01
+#define EM28XX_RESOURCE_VBI 0x02
+
+struct em28xx_audio {
+ char name[50];
+ char *transfer_buffer[EM28XX_AUDIO_BUFS];
+ struct urb *urb[EM28XX_AUDIO_BUFS];
+ struct usb_device *udev;
+ unsigned int capture_transfer_done;
+ struct snd_pcm_substream *capture_pcm_substream;
+
+ unsigned int hwptr_done_capture;
+ struct snd_card *sndcard;
+
+ int users;
+ spinlock_t slock;
+};
+
+struct em28xx;
+
+struct em28xx_fh {
+ struct em28xx *dev;
+ int radio;
+ unsigned int resources;
+
+ struct videobuf_queue vb_vidq;
+ struct videobuf_queue vb_vbiq;
+
+ enum v4l2_buf_type type;
+};
+
+/* main device struct */
+struct em28xx {
+ /* generic device properties */
+ char name[30]; /* name (including minor) of the device */
+ int model; /* index in the device_data struct */
+ int devno; /* marks the number of this device */
+ enum em28xx_chip_id chip_id;
+
+ int audio_ifnum;
+
+ struct v4l2_device v4l2_dev;
+ struct em28xx_board board;
+
+ /* Webcam specific fields */
+ enum em28xx_sensor em28xx_sensor;
+ int sensor_xres, sensor_yres;
+ int sensor_xtal;
+
+ /* Allows progressive (e. g. non-interlaced) mode */
+ int progressive;
+
+ /* Vinmode/Vinctl used at the driver */
+ int vinmode, vinctl;
+
+ unsigned int has_audio_class:1;
+ unsigned int has_alsa_audio:1;
+ unsigned int is_audio_only:1;
+
+ /* Controls audio streaming */
+ struct work_struct wq_trigger; /* Trigger to start/stop audio for alsa module */
+ atomic_t stream_started; /* stream should be running if true */
+
+ struct em28xx_fmt *format;
+
+ struct em28xx_IR *ir;
+
+ /* Some older em28xx chips needs a waiting time after writing */
+ unsigned int wait_after_write;
+
+ struct list_head devlist;
+
+ u32 i2s_speed; /* I2S speed for audio digital stream */
+
+ struct em28xx_audio_mode audio_mode;
+
+ int tuner_type; /* type of the tuner */
+ int tuner_addr; /* tuner address */
+ int tda9887_conf;
+ /* i2c i/o */
+ struct i2c_adapter i2c_adap;
+ struct i2c_client i2c_client;
+ /* video for linux */
+ int users; /* user count for exclusive use */
+ struct video_device *vdev; /* video for linux device struct */
+ v4l2_std_id norm; /* selected tv norm */
+ int ctl_freq; /* selected frequency */
+ unsigned int ctl_input; /* selected input */
+ unsigned int ctl_ainput;/* selected audio input */
+ unsigned int ctl_aoutput;/* selected audio output */
+ int mute;
+ int volume;
+ /* frame properties */
+ int width; /* current frame width */
+ int height; /* current frame height */
+ unsigned hscale; /* horizontal scale factor (see datasheet) */
+ unsigned vscale; /* vertical scale factor (see datasheet) */
+ int interlaced; /* 1=interlace fileds, 0=just top fileds */
+ unsigned int video_bytesread; /* Number of bytes read */
+
+ unsigned long hash; /* eeprom hash - for boards with generic ID */
+ unsigned long i2c_hash; /* i2c devicelist hash -
+ for boards with generic ID */
+
+ struct em28xx_audio adev;
+
+ /* states */
+ enum em28xx_dev_state state;
+ enum em28xx_io_method io;
+
+ /* vbi related state tracking */
+ int capture_type;
+ int vbi_read;
+ unsigned char cur_field;
+ unsigned int vbi_width;
+ unsigned int vbi_height; /* lines per field */
+
+ struct work_struct request_module_wk;
+
+ /* locks */
+ struct mutex lock;
+ struct mutex ctrl_urb_lock; /* protects urb_buf */
+ /* spinlock_t queue_lock; */
+ struct list_head inqueue, outqueue;
+ wait_queue_head_t open, wait_frame, wait_stream;
+ struct video_device *vbi_dev;
+ struct video_device *radio_dev;
+
+ /* resources in use */
+ unsigned int resources;
+
+ unsigned char eedata[256];
+
+ /* Isoc control struct */
+ struct em28xx_dmaqueue vidq;
+ struct em28xx_dmaqueue vbiq;
+ struct em28xx_usb_isoc_ctl isoc_ctl;
+ spinlock_t slock;
+
+ /* usb transfer */
+ struct usb_device *udev; /* the usb device */
+ int alt; /* alternate */
+ int max_pkt_size; /* max packet size of isoc transaction */
+ int num_alt; /* Number of alternative settings */
+ unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */
+ int dvb_alt; /* alternate for DVB */
+ unsigned int dvb_max_pkt_size; /* wMaxPacketSize for DVB */
+ char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */
+
+ /* helper funcs that call usb_control_msg */
+ int (*em28xx_write_regs) (struct em28xx *dev, u16 reg,
+ char *buf, int len);
+ int (*em28xx_read_reg) (struct em28xx *dev, u16 reg);
+ int (*em28xx_read_reg_req_len) (struct em28xx *dev, u8 req, u16 reg,
+ char *buf, int len);
+ int (*em28xx_write_regs_req) (struct em28xx *dev, u8 req, u16 reg,
+ char *buf, int len);
+ int (*em28xx_read_reg_req) (struct em28xx *dev, u8 req, u16 reg);
+
+ enum em28xx_mode mode;
+
+ /* register numbers for GPO/GPIO registers */
+ u16 reg_gpo_num, reg_gpio_num;
+
+ /* Caches GPO and GPIO registers */
+ unsigned char reg_gpo, reg_gpio;
+
+ /* Snapshot button */
+ char snapshot_button_path[30]; /* path of the input dev */
+ struct input_dev *sbutton_input_dev;
+ struct delayed_work sbutton_query_work;
+
+ struct em28xx_dvb *dvb;
+
+ /* I2C keyboard data */
+ struct IR_i2c_init_data init_data;
+};
+
+struct em28xx_ops {
+ struct list_head next;
+ char *name;
+ int id;
+ int (*init)(struct em28xx *);
+ int (*fini)(struct em28xx *);
+};
+
+/* Provided by em28xx-i2c.c */
+void em28xx_do_i2c_scan(struct em28xx *dev);
+int em28xx_i2c_register(struct em28xx *dev);
+int em28xx_i2c_unregister(struct em28xx *dev);
+
+/* Provided by em28xx-core.c */
+
+u32 em28xx_request_buffers(struct em28xx *dev, u32 count);
+void em28xx_queue_unusedframes(struct em28xx *dev);
+void em28xx_release_buffers(struct em28xx *dev);
+
+int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg,
+ char *buf, int len);
+int em28xx_read_reg_req(struct em28xx *dev, u8 req, u16 reg);
+int em28xx_read_reg(struct em28xx *dev, u16 reg);
+int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf,
+ int len);
+int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len);
+int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val);
+int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
+ u8 bitmask);
+
+int em28xx_read_ac97(struct em28xx *dev, u8 reg);
+int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val);
+
+int em28xx_audio_analog_set(struct em28xx *dev);
+int em28xx_audio_setup(struct em28xx *dev);
+
+int em28xx_colorlevels_set_default(struct em28xx *dev);
+int em28xx_capture_start(struct em28xx *dev, int start);
+int em28xx_vbi_supported(struct em28xx *dev);
+int em28xx_set_outfmt(struct em28xx *dev);
+int em28xx_resolution_set(struct em28xx *dev);
+int em28xx_set_alternate(struct em28xx *dev);
+int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode,
+ int max_packets, int num_bufs, int max_pkt_size);
+int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode,
+ int max_packets, int num_bufs, int max_pkt_size,
+ int (*isoc_copy) (struct em28xx *dev, struct urb *urb));
+void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode);
+int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev);
+int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode);
+int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio);
+void em28xx_wake_i2c(struct em28xx *dev);
+int em28xx_register_extension(struct em28xx_ops *dev);
+void em28xx_unregister_extension(struct em28xx_ops *dev);
+void em28xx_init_extension(struct em28xx *dev);
+void em28xx_close_extension(struct em28xx *dev);
+
+/* Provided by em28xx-video.c */
+int em28xx_register_analog_devices(struct em28xx *dev);
+void em28xx_release_analog_resources(struct em28xx *dev);
+
+/* Provided by em28xx-cards.c */
+extern int em2800_variant_detect(struct usb_device *udev, int model);
+extern void em28xx_pre_card_setup(struct em28xx *dev);
+extern void em28xx_card_setup(struct em28xx *dev);
+extern struct em28xx_board em28xx_boards[];
+extern struct usb_device_id em28xx_id_table[];
+extern const unsigned int em28xx_bcount;
+void em28xx_register_i2c_ir(struct em28xx *dev);
+int em28xx_tuner_callback(void *ptr, int component, int command, int arg);
+void em28xx_release_resources(struct em28xx *dev);
+
+/* Provided by em28xx-input.c */
+
+#ifdef CONFIG_VIDEO_EM28XX_RC
+
+int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw);
+int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw);
+int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
+ u32 *ir_raw);
+int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key,
+ u32 *ir_raw);
+void em28xx_register_snapshot_button(struct em28xx *dev);
+void em28xx_deregister_snapshot_button(struct em28xx *dev);
+
+int em28xx_ir_init(struct em28xx *dev);
+int em28xx_ir_fini(struct em28xx *dev);
+
+#else
+
+#define em28xx_get_key_terratec NULL
+#define em28xx_get_key_em_haup NULL
+#define em28xx_get_key_pinnacle_usb_grey NULL
+#define em28xx_get_key_winfast_usbii_deluxe NULL
+
+static inline void em28xx_register_snapshot_button(struct em28xx *dev) {}
+static inline void em28xx_deregister_snapshot_button(struct em28xx *dev) {}
+static inline int em28xx_ir_init(struct em28xx *dev) { return 0; }
+static inline int em28xx_ir_fini(struct em28xx *dev) { return 0; }
+
+#endif
+
+/* Provided by em28xx-vbi.c */
+extern struct videobuf_queue_ops em28xx_vbi_qops;
+
+/* printk macros */
+
+#define em28xx_err(fmt, arg...) do {\
+ printk(KERN_ERR fmt , ##arg); } while (0)
+
+#define em28xx_errdev(fmt, arg...) do {\
+ printk(KERN_ERR "%s: "fmt,\
+ dev->name , ##arg); } while (0)
+
+#define em28xx_info(fmt, arg...) do {\
+ printk(KERN_INFO "%s: "fmt,\
+ dev->name , ##arg); } while (0)
+#define em28xx_warn(fmt, arg...) do {\
+ printk(KERN_WARNING "%s: "fmt,\
+ dev->name , ##arg); } while (0)
+
+static inline int em28xx_compression_disable(struct em28xx *dev)
+{
+ /* side effect of disabling scaler and mixer */
+ return em28xx_write_reg(dev, EM28XX_R26_COMPR, 0x00);
+}
+
+static inline int em28xx_contrast_get(struct em28xx *dev)
+{
+ return em28xx_read_reg(dev, EM28XX_R20_YGAIN) & 0x1f;
+}
+
+static inline int em28xx_brightness_get(struct em28xx *dev)
+{
+ return em28xx_read_reg(dev, EM28XX_R21_YOFFSET);
+}
+
+static inline int em28xx_saturation_get(struct em28xx *dev)
+{
+ return em28xx_read_reg(dev, EM28XX_R22_UVGAIN) & 0x1f;
+}
+
+static inline int em28xx_u_balance_get(struct em28xx *dev)
+{
+ return em28xx_read_reg(dev, EM28XX_R23_UOFFSET);
+}
+
+static inline int em28xx_v_balance_get(struct em28xx *dev)
+{
+ return em28xx_read_reg(dev, EM28XX_R24_VOFFSET);
+}
+
+static inline int em28xx_gamma_get(struct em28xx *dev)
+{
+ return em28xx_read_reg(dev, EM28XX_R14_GAMMA) & 0x3f;
+}
+
+static inline int em28xx_contrast_set(struct em28xx *dev, s32 val)
+{
+ u8 tmp = (u8) val;
+ return em28xx_write_regs(dev, EM28XX_R20_YGAIN, &tmp, 1);
+}
+
+static inline int em28xx_brightness_set(struct em28xx *dev, s32 val)
+{
+ u8 tmp = (u8) val;
+ return em28xx_write_regs(dev, EM28XX_R21_YOFFSET, &tmp, 1);
+}
+
+static inline int em28xx_saturation_set(struct em28xx *dev, s32 val)
+{
+ u8 tmp = (u8) val;
+ return em28xx_write_regs(dev, EM28XX_R22_UVGAIN, &tmp, 1);
+}
+
+static inline int em28xx_u_balance_set(struct em28xx *dev, s32 val)
+{
+ u8 tmp = (u8) val;
+ return em28xx_write_regs(dev, EM28XX_R23_UOFFSET, &tmp, 1);
+}
+
+static inline int em28xx_v_balance_set(struct em28xx *dev, s32 val)
+{
+ u8 tmp = (u8) val;
+ return em28xx_write_regs(dev, EM28XX_R24_VOFFSET, &tmp, 1);
+}
+
+static inline int em28xx_gamma_set(struct em28xx *dev, s32 val)
+{
+ u8 tmp = (u8) val;
+ return em28xx_write_regs(dev, EM28XX_R14_GAMMA, &tmp, 1);
+}
+
+/*FIXME: maxw should be dependent of alt mode */
+static inline unsigned int norm_maxw(struct em28xx *dev)
+{
+ if (dev->board.is_webcam)
+ return dev->sensor_xres;
+
+ if (dev->board.max_range_640_480)
+ return 640;
+
+ return 720;
+}
+
+static inline unsigned int norm_maxh(struct em28xx *dev)
+{
+ if (dev->board.is_webcam)
+ return dev->sensor_yres;
+
+ if (dev->board.max_range_640_480)
+ return 480;
+
+ return (dev->norm & V4L2_STD_625_50) ? 576 : 480;
+}
+#endif
diff --git a/drivers/media/video/et61x251/Kconfig b/drivers/media/video/et61x251/Kconfig
new file mode 100644
index 00000000..87981b07
--- /dev/null
+++ b/drivers/media/video/et61x251/Kconfig
@@ -0,0 +1,18 @@
+config USB_ET61X251
+ tristate "USB ET61X[12]51 PC Camera Controller support (DEPRECATED)"
+ depends on VIDEO_V4L2
+ default n
+ ---help---
+ This driver is DEPRECATED please use the gspca zc3xx module
+ instead.
+
+ Say Y here if you want support for cameras based on Etoms ET61X151
+ or ET61X251 PC Camera Controllers.
+
+ See <file:Documentation/video4linux/et61x251.txt> for more info.
+
+ This driver uses the Video For Linux API. You must say Y or M to
+ "Video For Linux" to use this driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called et61x251.
diff --git a/drivers/media/video/et61x251/Makefile b/drivers/media/video/et61x251/Makefile
new file mode 100644
index 00000000..2ff4db9e
--- /dev/null
+++ b/drivers/media/video/et61x251/Makefile
@@ -0,0 +1,4 @@
+et61x251-objs := et61x251_core.o et61x251_tas5130d1b.o
+
+obj-$(CONFIG_USB_ET61X251) += et61x251.o
+
diff --git a/drivers/media/video/et61x251/et61x251.h b/drivers/media/video/et61x251/et61x251.h
new file mode 100644
index 00000000..337ded4a
--- /dev/null
+++ b/drivers/media/video/et61x251/et61x251.h
@@ -0,0 +1,213 @@
+/***************************************************************************
+ * V4L2 driver for ET61X[12]51 PC Camera Controllers *
+ * *
+ * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#ifndef _ET61X251_H_
+#define _ET61X251_H_
+
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/types.h>
+#include <linux/param.h>
+#include <linux/rwsem.h>
+#include <linux/mutex.h>
+#include <linux/stddef.h>
+#include <linux/string.h>
+#include <linux/kref.h>
+
+#include "et61x251_sensor.h"
+
+/*****************************************************************************/
+
+#define ET61X251_DEBUG
+#define ET61X251_DEBUG_LEVEL 2
+#define ET61X251_MAX_DEVICES 64
+#define ET61X251_PRESERVE_IMGSCALE 0
+#define ET61X251_FORCE_MUNMAP 0
+#define ET61X251_MAX_FRAMES 32
+#define ET61X251_COMPRESSION_QUALITY 0
+#define ET61X251_URBS 2
+#define ET61X251_ISO_PACKETS 7
+#define ET61X251_ALTERNATE_SETTING 13
+#define ET61X251_URB_TIMEOUT msecs_to_jiffies(2 * ET61X251_ISO_PACKETS)
+#define ET61X251_CTRL_TIMEOUT 100
+#define ET61X251_FRAME_TIMEOUT 2
+
+/*****************************************************************************/
+
+static const struct usb_device_id et61x251_id_table[] = {
+ { USB_DEVICE(0x102c, 0x6251), },
+ { }
+};
+
+ET61X251_SENSOR_TABLE
+
+/*****************************************************************************/
+
+enum et61x251_frame_state {
+ F_UNUSED,
+ F_QUEUED,
+ F_GRABBING,
+ F_DONE,
+ F_ERROR,
+};
+
+struct et61x251_frame_t {
+ void* bufmem;
+ struct v4l2_buffer buf;
+ enum et61x251_frame_state state;
+ struct list_head frame;
+ unsigned long vma_use_count;
+};
+
+enum et61x251_dev_state {
+ DEV_INITIALIZED = 0x01,
+ DEV_DISCONNECTED = 0x02,
+ DEV_MISCONFIGURED = 0x04,
+};
+
+enum et61x251_io_method {
+ IO_NONE,
+ IO_READ,
+ IO_MMAP,
+};
+
+enum et61x251_stream_state {
+ STREAM_OFF,
+ STREAM_INTERRUPT,
+ STREAM_ON,
+};
+
+struct et61x251_sysfs_attr {
+ u8 reg, i2c_reg;
+};
+
+struct et61x251_module_param {
+ u8 force_munmap;
+ u16 frame_timeout;
+};
+
+static DEFINE_MUTEX(et61x251_sysfs_lock);
+static DECLARE_RWSEM(et61x251_dev_lock);
+
+struct et61x251_device {
+ struct video_device* v4ldev;
+
+ struct et61x251_sensor sensor;
+
+ struct usb_device* usbdev;
+ struct urb* urb[ET61X251_URBS];
+ void* transfer_buffer[ET61X251_URBS];
+ u8* control_buffer;
+
+ struct et61x251_frame_t *frame_current, frame[ET61X251_MAX_FRAMES];
+ struct list_head inqueue, outqueue;
+ u32 frame_count, nbuffers, nreadbuffers;
+
+ enum et61x251_io_method io;
+ enum et61x251_stream_state stream;
+
+ struct v4l2_jpegcompression compression;
+
+ struct et61x251_sysfs_attr sysfs;
+ struct et61x251_module_param module_param;
+
+ struct kref kref;
+ enum et61x251_dev_state state;
+ u8 users;
+
+ struct completion probe;
+ struct mutex open_mutex, fileop_mutex;
+ spinlock_t queue_lock;
+ wait_queue_head_t wait_open, wait_frame, wait_stream;
+};
+
+/*****************************************************************************/
+
+struct et61x251_device*
+et61x251_match_id(struct et61x251_device* cam, const struct usb_device_id *id)
+{
+ return usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id) ? cam : NULL;
+}
+
+
+void
+et61x251_attach_sensor(struct et61x251_device* cam,
+ const struct et61x251_sensor* sensor)
+{
+ memcpy(&cam->sensor, sensor, sizeof(struct et61x251_sensor));
+}
+
+/*****************************************************************************/
+
+#undef DBG
+#undef KDBG
+#ifdef ET61X251_DEBUG
+#define DBG(level, fmt, ...) \
+do { \
+ if (debug >= (level)) { \
+ if ((level) == 1) \
+ dev_err(&cam->usbdev->dev, fmt "\n", \
+ ##__VA_ARGS__); \
+ else if ((level) == 2) \
+ dev_info(&cam->usbdev->dev, fmt "\n", \
+ ##__VA_ARGS__); \
+ else if ((level) >= 3) \
+ dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", \
+ __FILE__, __func__, __LINE__, \
+ ##__VA_ARGS__); \
+ } \
+} while (0)
+#define KDBG(level, fmt, ...) \
+do { \
+ if (debug >= (level)) { \
+ if ((level) == 1 || (level) == 2) \
+ pr_info(fmt "\n", ##__VA_ARGS__); \
+ else if ((level) == 3) \
+ pr_debug("[%s:%s:%d] " fmt "\n", \
+ __FILE__, __func__, __LINE__, \
+ ##__VA_ARGS__); \
+ } \
+} while (0)
+#define V4LDBG(level, name, cmd) \
+do { \
+ if (debug >= (level)) \
+ v4l_print_ioctl(name, cmd); \
+} while (0)
+#else
+#define DBG(level, fmt, ...) do {;} while(0)
+#define KDBG(level, fmt, ...) do {;} while(0)
+#define V4LDBG(level, name, cmd) do {;} while(0)
+#endif
+
+#undef PDBG
+#define PDBG(fmt, ...) \
+ dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", \
+ __FILE__, __func__, __LINE__, ##__VA_ARGS__)
+
+#undef PDBGG
+#define PDBGG(fmt, args...) do {;} while (0) /* placeholder */
+
+#endif /* _ET61X251_H_ */
diff --git a/drivers/media/video/et61x251/et61x251_core.c b/drivers/media/video/et61x251/et61x251_core.c
new file mode 100644
index 00000000..5539f094
--- /dev/null
+++ b/drivers/media/video/et61x251/et61x251_core.c
@@ -0,0 +1,2683 @@
+/***************************************************************************
+ * V4L2 driver for ET61X[12]51 PC Camera Controllers *
+ * *
+ * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/compiler.h>
+#include <linux/ioctl.h>
+#include <linux/poll.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/page-flags.h>
+#include <media/v4l2-ioctl.h>
+#include <asm/byteorder.h>
+#include <asm/page.h>
+#include <asm/uaccess.h>
+
+#include "et61x251.h"
+
+/*****************************************************************************/
+
+#define ET61X251_MODULE_NAME "V4L2 driver for ET61X[12]51 " \
+ "PC Camera Controllers"
+#define ET61X251_MODULE_AUTHOR "(C) 2006-2007 Luca Risolia"
+#define ET61X251_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
+#define ET61X251_MODULE_LICENSE "GPL"
+#define ET61X251_MODULE_VERSION "1.1.10"
+
+/*****************************************************************************/
+
+MODULE_DEVICE_TABLE(usb, et61x251_id_table);
+
+MODULE_AUTHOR(ET61X251_MODULE_AUTHOR " " ET61X251_AUTHOR_EMAIL);
+MODULE_DESCRIPTION(ET61X251_MODULE_NAME);
+MODULE_VERSION(ET61X251_MODULE_VERSION);
+MODULE_LICENSE(ET61X251_MODULE_LICENSE);
+
+static short video_nr[] = {[0 ... ET61X251_MAX_DEVICES-1] = -1};
+module_param_array(video_nr, short, NULL, 0444);
+MODULE_PARM_DESC(video_nr,
+ "\n<-1|n[,...]> Specify V4L2 minor mode number."
+ "\n -1 = use next available (default)"
+ "\n n = use minor number n (integer >= 0)"
+ "\nYou can specify up to "
+ __MODULE_STRING(ET61X251_MAX_DEVICES) " cameras this way."
+ "\nFor example:"
+ "\nvideo_nr=-1,2,-1 would assign minor number 2 to"
+ "\nthe second registered camera and use auto for the first"
+ "\none and for every other camera."
+ "\n");
+
+static bool force_munmap[] = {[0 ... ET61X251_MAX_DEVICES-1] =
+ ET61X251_FORCE_MUNMAP};
+module_param_array(force_munmap, bool, NULL, 0444);
+MODULE_PARM_DESC(force_munmap,
+ "\n<0|1[,...]> Force the application to unmap previously"
+ "\nmapped buffer memory before calling any VIDIOC_S_CROP or"
+ "\nVIDIOC_S_FMT ioctl's. Not all the applications support"
+ "\nthis feature. This parameter is specific for each"
+ "\ndetected camera."
+ "\n 0 = do not force memory unmapping"
+ "\n 1 = force memory unmapping (save memory)"
+ "\nDefault value is "__MODULE_STRING(ET61X251_FORCE_MUNMAP)"."
+ "\n");
+
+static unsigned int frame_timeout[] = {[0 ... ET61X251_MAX_DEVICES-1] =
+ ET61X251_FRAME_TIMEOUT};
+module_param_array(frame_timeout, uint, NULL, 0644);
+MODULE_PARM_DESC(frame_timeout,
+ "\n<n[,...]> Timeout for a video frame in seconds."
+ "\nThis parameter is specific for each detected camera."
+ "\nDefault value is "
+ __MODULE_STRING(ET61X251_FRAME_TIMEOUT)"."
+ "\n");
+
+#ifdef ET61X251_DEBUG
+static unsigned short debug = ET61X251_DEBUG_LEVEL;
+module_param(debug, ushort, 0644);
+MODULE_PARM_DESC(debug,
+ "\n<n> Debugging information level, from 0 to 3:"
+ "\n0 = none (use carefully)"
+ "\n1 = critical errors"
+ "\n2 = significant informations"
+ "\n3 = more verbose messages"
+ "\nLevel 3 is useful for testing only, when only "
+ "one device is used."
+ "\nDefault value is "__MODULE_STRING(ET61X251_DEBUG_LEVEL)"."
+ "\n");
+#endif
+
+/*****************************************************************************/
+
+static u32
+et61x251_request_buffers(struct et61x251_device* cam, u32 count,
+ enum et61x251_io_method io)
+{
+ struct v4l2_pix_format* p = &(cam->sensor.pix_format);
+ struct v4l2_rect* r = &(cam->sensor.cropcap.bounds);
+ const size_t imagesize = cam->module_param.force_munmap ||
+ io == IO_READ ?
+ (p->width * p->height * p->priv) / 8 :
+ (r->width * r->height * p->priv) / 8;
+ void* buff = NULL;
+ u32 i;
+
+ if (count > ET61X251_MAX_FRAMES)
+ count = ET61X251_MAX_FRAMES;
+
+ cam->nbuffers = count;
+ while (cam->nbuffers > 0) {
+ if ((buff = vmalloc_32_user(cam->nbuffers *
+ PAGE_ALIGN(imagesize))))
+ break;
+ cam->nbuffers--;
+ }
+
+ for (i = 0; i < cam->nbuffers; i++) {
+ cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize);
+ cam->frame[i].buf.index = i;
+ cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize);
+ cam->frame[i].buf.length = imagesize;
+ cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cam->frame[i].buf.sequence = 0;
+ cam->frame[i].buf.field = V4L2_FIELD_NONE;
+ cam->frame[i].buf.memory = V4L2_MEMORY_MMAP;
+ cam->frame[i].buf.flags = 0;
+ }
+
+ return cam->nbuffers;
+}
+
+
+static void et61x251_release_buffers(struct et61x251_device* cam)
+{
+ if (cam->nbuffers) {
+ vfree(cam->frame[0].bufmem);
+ cam->nbuffers = 0;
+ }
+ cam->frame_current = NULL;
+}
+
+
+static void et61x251_empty_framequeues(struct et61x251_device* cam)
+{
+ u32 i;
+
+ INIT_LIST_HEAD(&cam->inqueue);
+ INIT_LIST_HEAD(&cam->outqueue);
+
+ for (i = 0; i < ET61X251_MAX_FRAMES; i++) {
+ cam->frame[i].state = F_UNUSED;
+ cam->frame[i].buf.bytesused = 0;
+ }
+}
+
+
+static void et61x251_requeue_outqueue(struct et61x251_device* cam)
+{
+ struct et61x251_frame_t *i;
+
+ list_for_each_entry(i, &cam->outqueue, frame) {
+ i->state = F_QUEUED;
+ list_add(&i->frame, &cam->inqueue);
+ }
+
+ INIT_LIST_HEAD(&cam->outqueue);
+}
+
+
+static void et61x251_queue_unusedframes(struct et61x251_device* cam)
+{
+ unsigned long lock_flags;
+ u32 i;
+
+ for (i = 0; i < cam->nbuffers; i++)
+ if (cam->frame[i].state == F_UNUSED) {
+ cam->frame[i].state = F_QUEUED;
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ list_add_tail(&cam->frame[i].frame, &cam->inqueue);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+ }
+}
+
+/*****************************************************************************/
+
+int et61x251_write_reg(struct et61x251_device* cam, u8 value, u16 index)
+{
+ struct usb_device* udev = cam->usbdev;
+ u8* buff = cam->control_buffer;
+ int res;
+
+ *buff = value;
+
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
+ 0, index, buff, 1, ET61X251_CTRL_TIMEOUT);
+ if (res < 0) {
+ DBG(3, "Failed to write a register (value 0x%02X, index "
+ "0x%02X, error %d)", value, index, res);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int et61x251_read_reg(struct et61x251_device* cam, u16 index)
+{
+ struct usb_device* udev = cam->usbdev;
+ u8* buff = cam->control_buffer;
+ int res;
+
+ res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1,
+ 0, index, buff, 1, ET61X251_CTRL_TIMEOUT);
+ if (res < 0)
+ DBG(3, "Failed to read a register (index 0x%02X, error %d)",
+ index, res);
+
+ return (res >= 0) ? (int)(*buff) : -1;
+}
+
+
+static int
+et61x251_i2c_wait(struct et61x251_device* cam,
+ const struct et61x251_sensor* sensor)
+{
+ int i, r;
+
+ for (i = 1; i <= 8; i++) {
+ if (sensor->interface == ET61X251_I2C_3WIRES) {
+ r = et61x251_read_reg(cam, 0x8e);
+ if (!(r & 0x02) && (r >= 0))
+ return 0;
+ } else {
+ r = et61x251_read_reg(cam, 0x8b);
+ if (!(r & 0x01) && (r >= 0))
+ return 0;
+ }
+ if (r < 0)
+ return -EIO;
+ udelay(8*8); /* minimum for sensors at 400kHz */
+ }
+
+ return -EBUSY;
+}
+
+
+int
+et61x251_i2c_raw_write(struct et61x251_device* cam, u8 n, u8 data1, u8 data2,
+ u8 data3, u8 data4, u8 data5, u8 data6, u8 data7,
+ u8 data8, u8 address)
+{
+ struct usb_device* udev = cam->usbdev;
+ u8* data = cam->control_buffer;
+ int err = 0, res;
+
+ data[0] = data2;
+ data[1] = data3;
+ data[2] = data4;
+ data[3] = data5;
+ data[4] = data6;
+ data[5] = data7;
+ data[6] = data8;
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
+ 0, 0x81, data, n-1, ET61X251_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ data[0] = address;
+ data[1] = cam->sensor.i2c_slave_id;
+ data[2] = cam->sensor.rsta | 0x02 | (n << 4);
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
+ 0, 0x88, data, 3, ET61X251_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ /* Start writing through the serial interface */
+ data[0] = data1;
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
+ 0, 0x80, data, 1, ET61X251_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ err += et61x251_i2c_wait(cam, &cam->sensor);
+
+ if (err)
+ DBG(3, "I2C raw write failed for %s image sensor",
+ cam->sensor.name);
+
+ PDBGG("I2C raw write: %u bytes, address = 0x%02X, data1 = 0x%02X, "
+ "data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X,"
+ " data6 = 0x%02X, data7 = 0x%02X, data8 = 0x%02X", n, address,
+ data1, data2, data3, data4, data5, data6, data7, data8);
+
+ return err ? -1 : 0;
+
+}
+
+
+/*****************************************************************************/
+
+static void et61x251_urb_complete(struct urb *urb)
+{
+ struct et61x251_device* cam = urb->context;
+ struct et61x251_frame_t** f;
+ size_t imagesize;
+ u8 i;
+ int err = 0;
+
+ if (urb->status == -ENOENT)
+ return;
+
+ f = &cam->frame_current;
+
+ if (cam->stream == STREAM_INTERRUPT) {
+ cam->stream = STREAM_OFF;
+ if ((*f))
+ (*f)->state = F_QUEUED;
+ DBG(3, "Stream interrupted");
+ wake_up(&cam->wait_stream);
+ }
+
+ if (cam->state & DEV_DISCONNECTED)
+ return;
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ wake_up_interruptible(&cam->wait_frame);
+ return;
+ }
+
+ if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue))
+ goto resubmit_urb;
+
+ if (!(*f))
+ (*f) = list_entry(cam->inqueue.next, struct et61x251_frame_t,
+ frame);
+
+ imagesize = (cam->sensor.pix_format.width *
+ cam->sensor.pix_format.height *
+ cam->sensor.pix_format.priv) / 8;
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ unsigned int len, status;
+ void *pos;
+ u8* b1, * b2, sof;
+ const u8 VOID_BYTES = 6;
+ size_t imglen;
+
+ len = urb->iso_frame_desc[i].actual_length;
+ status = urb->iso_frame_desc[i].status;
+ pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer;
+
+ if (status) {
+ DBG(3, "Error in isochronous frame");
+ (*f)->state = F_ERROR;
+ continue;
+ }
+
+ b1 = pos++;
+ b2 = pos++;
+ sof = ((*b1 & 0x3f) == 63);
+ imglen = ((*b1 & 0xc0) << 2) | *b2;
+
+ PDBGG("Isochrnous frame: length %u, #%u i, image length %zu",
+ len, i, imglen);
+
+ if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR)
+start_of_frame:
+ if (sof) {
+ (*f)->state = F_GRABBING;
+ (*f)->buf.bytesused = 0;
+ do_gettimeofday(&(*f)->buf.timestamp);
+ pos += 22;
+ DBG(3, "SOF detected: new video frame");
+ }
+
+ if ((*f)->state == F_GRABBING) {
+ if (sof && (*f)->buf.bytesused) {
+ if (cam->sensor.pix_format.pixelformat ==
+ V4L2_PIX_FMT_ET61X251)
+ goto end_of_frame;
+ else {
+ DBG(3, "Not expected SOF detected "
+ "after %lu bytes",
+ (unsigned long)(*f)->buf.bytesused);
+ (*f)->state = F_ERROR;
+ continue;
+ }
+ }
+
+ if ((*f)->buf.bytesused + imglen > imagesize) {
+ DBG(3, "Video frame size exceeded");
+ (*f)->state = F_ERROR;
+ continue;
+ }
+
+ pos += VOID_BYTES;
+
+ memcpy((*f)->bufmem+(*f)->buf.bytesused, pos, imglen);
+ (*f)->buf.bytesused += imglen;
+
+ if ((*f)->buf.bytesused == imagesize) {
+ u32 b;
+end_of_frame:
+ b = (*f)->buf.bytesused;
+ (*f)->state = F_DONE;
+ (*f)->buf.sequence= ++cam->frame_count;
+ spin_lock(&cam->queue_lock);
+ list_move_tail(&(*f)->frame, &cam->outqueue);
+ if (!list_empty(&cam->inqueue))
+ (*f) = list_entry(cam->inqueue.next,
+ struct et61x251_frame_t,
+ frame);
+ else
+ (*f) = NULL;
+ spin_unlock(&cam->queue_lock);
+ DBG(3, "Video frame captured: : %lu bytes",
+ (unsigned long)(b));
+
+ if (!(*f))
+ goto resubmit_urb;
+
+ if (sof &&
+ cam->sensor.pix_format.pixelformat ==
+ V4L2_PIX_FMT_ET61X251)
+ goto start_of_frame;
+ }
+ }
+ }
+
+resubmit_urb:
+ urb->dev = cam->usbdev;
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0 && err != -EPERM) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "usb_submit_urb() failed");
+ }
+
+ wake_up_interruptible(&cam->wait_frame);
+}
+
+
+static int et61x251_start_transfer(struct et61x251_device* cam)
+{
+ struct usb_device *udev = cam->usbdev;
+ struct urb* urb;
+ struct usb_host_interface* altsetting = usb_altnum_to_altsetting(
+ usb_ifnum_to_if(udev, 0),
+ ET61X251_ALTERNATE_SETTING);
+ const unsigned int psz = le16_to_cpu(altsetting->
+ endpoint[0].desc.wMaxPacketSize);
+ s8 i, j;
+ int err = 0;
+
+ for (i = 0; i < ET61X251_URBS; i++) {
+ cam->transfer_buffer[i] = kzalloc(ET61X251_ISO_PACKETS * psz,
+ GFP_KERNEL);
+ if (!cam->transfer_buffer[i]) {
+ err = -ENOMEM;
+ DBG(1, "Not enough memory");
+ goto free_buffers;
+ }
+ }
+
+ for (i = 0; i < ET61X251_URBS; i++) {
+ urb = usb_alloc_urb(ET61X251_ISO_PACKETS, GFP_KERNEL);
+ cam->urb[i] = urb;
+ if (!urb) {
+ err = -ENOMEM;
+ DBG(1, "usb_alloc_urb() failed");
+ goto free_urbs;
+ }
+ urb->dev = udev;
+ urb->context = cam;
+ urb->pipe = usb_rcvisocpipe(udev, 1);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->number_of_packets = ET61X251_ISO_PACKETS;
+ urb->complete = et61x251_urb_complete;
+ urb->transfer_buffer = cam->transfer_buffer[i];
+ urb->transfer_buffer_length = psz * ET61X251_ISO_PACKETS;
+ urb->interval = 1;
+ for (j = 0; j < ET61X251_ISO_PACKETS; j++) {
+ urb->iso_frame_desc[j].offset = psz * j;
+ urb->iso_frame_desc[j].length = psz;
+ }
+ }
+
+ err = et61x251_write_reg(cam, 0x01, 0x03);
+ err = et61x251_write_reg(cam, 0x00, 0x03);
+ err = et61x251_write_reg(cam, 0x08, 0x03);
+ if (err) {
+ err = -EIO;
+ DBG(1, "I/O hardware error");
+ goto free_urbs;
+ }
+
+ err = usb_set_interface(udev, 0, ET61X251_ALTERNATE_SETTING);
+ if (err) {
+ DBG(1, "usb_set_interface() failed");
+ goto free_urbs;
+ }
+
+ cam->frame_current = NULL;
+
+ for (i = 0; i < ET61X251_URBS; i++) {
+ err = usb_submit_urb(cam->urb[i], GFP_KERNEL);
+ if (err) {
+ for (j = i-1; j >= 0; j--)
+ usb_kill_urb(cam->urb[j]);
+ DBG(1, "usb_submit_urb() failed, error %d", err);
+ goto free_urbs;
+ }
+ }
+
+ return 0;
+
+free_urbs:
+ for (i = 0; (i < ET61X251_URBS) && cam->urb[i]; i++)
+ usb_free_urb(cam->urb[i]);
+
+free_buffers:
+ for (i = 0; (i < ET61X251_URBS) && cam->transfer_buffer[i]; i++)
+ kfree(cam->transfer_buffer[i]);
+
+ return err;
+}
+
+
+static int et61x251_stop_transfer(struct et61x251_device* cam)
+{
+ struct usb_device *udev = cam->usbdev;
+ s8 i;
+ int err = 0;
+
+ if (cam->state & DEV_DISCONNECTED)
+ return 0;
+
+ for (i = ET61X251_URBS-1; i >= 0; i--) {
+ usb_kill_urb(cam->urb[i]);
+ usb_free_urb(cam->urb[i]);
+ kfree(cam->transfer_buffer[i]);
+ }
+
+ err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */
+ if (err)
+ DBG(3, "usb_set_interface() failed");
+
+ return err;
+}
+
+
+static int et61x251_stream_interrupt(struct et61x251_device* cam)
+{
+ long timeout;
+
+ cam->stream = STREAM_INTERRUPT;
+ timeout = wait_event_timeout(cam->wait_stream,
+ (cam->stream == STREAM_OFF) ||
+ (cam->state & DEV_DISCONNECTED),
+ ET61X251_URB_TIMEOUT);
+ if (cam->state & DEV_DISCONNECTED)
+ return -ENODEV;
+ else if (cam->stream != STREAM_OFF) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "URB timeout reached. The camera is misconfigured. To "
+ "use it, close and open %s again.",
+ video_device_node_name(cam->v4ldev));
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+
+static int et61x251_i2c_try_read(struct et61x251_device* cam,
+ const struct et61x251_sensor* sensor,
+ u8 address)
+{
+ struct usb_device* udev = cam->usbdev;
+ u8* data = cam->control_buffer;
+ int err = 0, res;
+
+ data[0] = address;
+ data[1] = cam->sensor.i2c_slave_id;
+ data[2] = cam->sensor.rsta | 0x10;
+ data[3] = !(et61x251_read_reg(cam, 0x8b) & 0x02);
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
+ 0, 0x88, data, 4, ET61X251_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ err += et61x251_i2c_wait(cam, sensor);
+
+ res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1,
+ 0, 0x80, data, 8, ET61X251_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ if (err)
+ DBG(3, "I2C read failed for %s image sensor", sensor->name);
+
+ PDBGG("I2C read: address 0x%02X, value: 0x%02X", address, data[0]);
+
+ return err ? -1 : (int)data[0];
+}
+
+
+static int et61x251_i2c_try_write(struct et61x251_device* cam,
+ const struct et61x251_sensor* sensor,
+ u8 address, u8 value)
+{
+ struct usb_device* udev = cam->usbdev;
+ u8* data = cam->control_buffer;
+ int err = 0, res;
+
+ data[0] = address;
+ data[1] = cam->sensor.i2c_slave_id;
+ data[2] = cam->sensor.rsta | 0x12;
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
+ 0, 0x88, data, 3, ET61X251_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ data[0] = value;
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
+ 0, 0x80, data, 1, ET61X251_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ err += et61x251_i2c_wait(cam, sensor);
+
+ if (err)
+ DBG(3, "I2C write failed for %s image sensor", sensor->name);
+
+ PDBGG("I2C write: address 0x%02X, value: 0x%02X", address, value);
+
+ return err ? -1 : 0;
+}
+
+static int et61x251_i2c_read(struct et61x251_device* cam, u8 address)
+{
+ return et61x251_i2c_try_read(cam, &cam->sensor, address);
+}
+
+static int et61x251_i2c_write(struct et61x251_device* cam,
+ u8 address, u8 value)
+{
+ return et61x251_i2c_try_write(cam, &cam->sensor, address, value);
+}
+
+static u8 et61x251_strtou8(const char* buff, size_t len, ssize_t* count)
+{
+ char str[5];
+ char* endp;
+ unsigned long val;
+
+ if (len < 4) {
+ strncpy(str, buff, len);
+ str[len] = '\0';
+ } else {
+ strncpy(str, buff, 4);
+ str[4] = '\0';
+ }
+
+ val = simple_strtoul(str, &endp, 0);
+
+ *count = 0;
+ if (val <= 0xff)
+ *count = (ssize_t)(endp - str);
+ if ((*count) && (len == *count+1) && (buff[*count] == '\n'))
+ *count += 1;
+
+ return (u8)val;
+}
+
+/*
+ NOTE 1: being inside one of the following methods implies that the v4l
+ device exists for sure (see kobjects and reference counters)
+ NOTE 2: buffers are PAGE_SIZE long
+*/
+
+static ssize_t et61x251_show_reg(struct device* cd,
+ struct device_attribute *attr, char* buf)
+{
+ struct et61x251_device* cam;
+ ssize_t count;
+
+ if (mutex_lock_interruptible(&et61x251_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -ENODEV;
+ }
+
+ count = sprintf(buf, "%u\n", cam->sysfs.reg);
+
+ mutex_unlock(&et61x251_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t
+et61x251_store_reg(struct device* cd,
+ struct device_attribute *attr, const char* buf, size_t len)
+{
+ struct et61x251_device* cam;
+ u8 index;
+ ssize_t count;
+
+ if (mutex_lock_interruptible(&et61x251_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -ENODEV;
+ }
+
+ index = et61x251_strtou8(buf, len, &count);
+ if (index > 0x8e || !count) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -EINVAL;
+ }
+
+ cam->sysfs.reg = index;
+
+ DBG(2, "Moved ET61X[12]51 register index to 0x%02X", cam->sysfs.reg);
+ DBG(3, "Written bytes: %zd", count);
+
+ mutex_unlock(&et61x251_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t et61x251_show_val(struct device* cd,
+ struct device_attribute *attr, char* buf)
+{
+ struct et61x251_device* cam;
+ ssize_t count;
+ int val;
+
+ if (mutex_lock_interruptible(&et61x251_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -ENODEV;
+ }
+
+ if ((val = et61x251_read_reg(cam, cam->sysfs.reg)) < 0) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -EIO;
+ }
+
+ count = sprintf(buf, "%d\n", val);
+
+ DBG(3, "Read bytes: %zd", count);
+
+ mutex_unlock(&et61x251_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t
+et61x251_store_val(struct device* cd, struct device_attribute *attr,
+ const char* buf, size_t len)
+{
+ struct et61x251_device* cam;
+ u8 value;
+ ssize_t count;
+ int err;
+
+ if (mutex_lock_interruptible(&et61x251_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -ENODEV;
+ }
+
+ value = et61x251_strtou8(buf, len, &count);
+ if (!count) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -EINVAL;
+ }
+
+ err = et61x251_write_reg(cam, value, cam->sysfs.reg);
+ if (err) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -EIO;
+ }
+
+ DBG(2, "Written ET61X[12]51 reg. 0x%02X, val. 0x%02X",
+ cam->sysfs.reg, value);
+ DBG(3, "Written bytes: %zd", count);
+
+ mutex_unlock(&et61x251_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t et61x251_show_i2c_reg(struct device* cd,
+ struct device_attribute *attr, char* buf)
+{
+ struct et61x251_device* cam;
+ ssize_t count;
+
+ if (mutex_lock_interruptible(&et61x251_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -ENODEV;
+ }
+
+ count = sprintf(buf, "%u\n", cam->sysfs.i2c_reg);
+
+ DBG(3, "Read bytes: %zd", count);
+
+ mutex_unlock(&et61x251_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t
+et61x251_store_i2c_reg(struct device* cd, struct device_attribute *attr,
+ const char* buf, size_t len)
+{
+ struct et61x251_device* cam;
+ u8 index;
+ ssize_t count;
+
+ if (mutex_lock_interruptible(&et61x251_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -ENODEV;
+ }
+
+ index = et61x251_strtou8(buf, len, &count);
+ if (!count) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -EINVAL;
+ }
+
+ cam->sysfs.i2c_reg = index;
+
+ DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg);
+ DBG(3, "Written bytes: %zd", count);
+
+ mutex_unlock(&et61x251_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t et61x251_show_i2c_val(struct device* cd,
+ struct device_attribute *attr, char* buf)
+{
+ struct et61x251_device* cam;
+ ssize_t count;
+ int val;
+
+ if (mutex_lock_interruptible(&et61x251_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -ENODEV;
+ }
+
+ if (!(cam->sensor.sysfs_ops & ET61X251_I2C_READ)) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -ENOSYS;
+ }
+
+ if ((val = et61x251_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -EIO;
+ }
+
+ count = sprintf(buf, "%d\n", val);
+
+ DBG(3, "Read bytes: %zd", count);
+
+ mutex_unlock(&et61x251_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t
+et61x251_store_i2c_val(struct device* cd, struct device_attribute *attr,
+ const char* buf, size_t len)
+{
+ struct et61x251_device* cam;
+ u8 value;
+ ssize_t count;
+ int err;
+
+ if (mutex_lock_interruptible(&et61x251_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -ENODEV;
+ }
+
+ if (!(cam->sensor.sysfs_ops & ET61X251_I2C_READ)) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -ENOSYS;
+ }
+
+ value = et61x251_strtou8(buf, len, &count);
+ if (!count) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -EINVAL;
+ }
+
+ err = et61x251_i2c_write(cam, cam->sysfs.i2c_reg, value);
+ if (err) {
+ mutex_unlock(&et61x251_sysfs_lock);
+ return -EIO;
+ }
+
+ DBG(2, "Written sensor reg. 0x%02X, val. 0x%02X",
+ cam->sysfs.i2c_reg, value);
+ DBG(3, "Written bytes: %zd", count);
+
+ mutex_unlock(&et61x251_sysfs_lock);
+
+ return count;
+}
+
+
+static DEVICE_ATTR(reg, S_IRUGO | S_IWUSR,
+ et61x251_show_reg, et61x251_store_reg);
+static DEVICE_ATTR(val, S_IRUGO | S_IWUSR,
+ et61x251_show_val, et61x251_store_val);
+static DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR,
+ et61x251_show_i2c_reg, et61x251_store_i2c_reg);
+static DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR,
+ et61x251_show_i2c_val, et61x251_store_i2c_val);
+
+
+static int et61x251_create_sysfs(struct et61x251_device* cam)
+{
+ struct device *classdev = &(cam->v4ldev->dev);
+ int err = 0;
+
+ if ((err = device_create_file(classdev, &dev_attr_reg)))
+ goto err_out;
+ if ((err = device_create_file(classdev, &dev_attr_val)))
+ goto err_reg;
+
+ if (cam->sensor.sysfs_ops) {
+ if ((err = device_create_file(classdev, &dev_attr_i2c_reg)))
+ goto err_val;
+ if ((err = device_create_file(classdev, &dev_attr_i2c_val)))
+ goto err_i2c_reg;
+ }
+
+err_i2c_reg:
+ if (cam->sensor.sysfs_ops)
+ device_remove_file(classdev, &dev_attr_i2c_reg);
+err_val:
+ device_remove_file(classdev, &dev_attr_val);
+err_reg:
+ device_remove_file(classdev, &dev_attr_reg);
+err_out:
+ return err;
+}
+#endif /* CONFIG_VIDEO_ADV_DEBUG */
+
+/*****************************************************************************/
+
+static int
+et61x251_set_pix_format(struct et61x251_device* cam,
+ struct v4l2_pix_format* pix)
+{
+ int r, err = 0;
+
+ if ((r = et61x251_read_reg(cam, 0x12)) < 0)
+ err += r;
+ if (pix->pixelformat == V4L2_PIX_FMT_ET61X251)
+ err += et61x251_write_reg(cam, r & 0xfd, 0x12);
+ else
+ err += et61x251_write_reg(cam, r | 0x02, 0x12);
+
+ return err ? -EIO : 0;
+}
+
+
+static int
+et61x251_set_compression(struct et61x251_device* cam,
+ struct v4l2_jpegcompression* compression)
+{
+ int r, err = 0;
+
+ if ((r = et61x251_read_reg(cam, 0x12)) < 0)
+ err += r;
+ if (compression->quality == 0)
+ err += et61x251_write_reg(cam, r & 0xfb, 0x12);
+ else
+ err += et61x251_write_reg(cam, r | 0x04, 0x12);
+
+ return err ? -EIO : 0;
+}
+
+
+static int et61x251_set_scale(struct et61x251_device* cam, u8 scale)
+{
+ int r = 0, err = 0;
+
+ r = et61x251_read_reg(cam, 0x12);
+ if (r < 0)
+ err += r;
+
+ if (scale == 1)
+ err += et61x251_write_reg(cam, r & ~0x01, 0x12);
+ else if (scale == 2)
+ err += et61x251_write_reg(cam, r | 0x01, 0x12);
+
+ if (err)
+ return -EIO;
+
+ PDBGG("Scaling factor: %u", scale);
+
+ return 0;
+}
+
+
+static int
+et61x251_set_crop(struct et61x251_device* cam, struct v4l2_rect* rect)
+{
+ struct et61x251_sensor* s = &cam->sensor;
+ u16 fmw_sx = (u16)(rect->left - s->cropcap.bounds.left +
+ s->active_pixel.left),
+ fmw_sy = (u16)(rect->top - s->cropcap.bounds.top +
+ s->active_pixel.top),
+ fmw_length = (u16)(rect->width),
+ fmw_height = (u16)(rect->height);
+ int err = 0;
+
+ err += et61x251_write_reg(cam, fmw_sx & 0xff, 0x69);
+ err += et61x251_write_reg(cam, fmw_sy & 0xff, 0x6a);
+ err += et61x251_write_reg(cam, fmw_length & 0xff, 0x6b);
+ err += et61x251_write_reg(cam, fmw_height & 0xff, 0x6c);
+ err += et61x251_write_reg(cam, (fmw_sx >> 8) | ((fmw_sy & 0x300) >> 6)
+ | ((fmw_length & 0x300) >> 4)
+ | ((fmw_height & 0x300) >> 2), 0x6d);
+ if (err)
+ return -EIO;
+
+ PDBGG("fmw_sx, fmw_sy, fmw_length, fmw_height: %u %u %u %u",
+ fmw_sx, fmw_sy, fmw_length, fmw_height);
+
+ return 0;
+}
+
+
+static int et61x251_init(struct et61x251_device* cam)
+{
+ struct et61x251_sensor* s = &cam->sensor;
+ struct v4l2_control ctrl;
+ struct v4l2_queryctrl *qctrl;
+ struct v4l2_rect* rect;
+ u8 i = 0;
+ int err = 0;
+
+ if (!(cam->state & DEV_INITIALIZED)) {
+ mutex_init(&cam->open_mutex);
+ init_waitqueue_head(&cam->wait_open);
+ qctrl = s->qctrl;
+ rect = &(s->cropcap.defrect);
+ cam->compression.quality = ET61X251_COMPRESSION_QUALITY;
+ } else { /* use current values */
+ qctrl = s->_qctrl;
+ rect = &(s->_rect);
+ }
+
+ err += et61x251_set_scale(cam, rect->width / s->pix_format.width);
+ err += et61x251_set_crop(cam, rect);
+ if (err)
+ return err;
+
+ if (s->init) {
+ err = s->init(cam);
+ if (err) {
+ DBG(3, "Sensor initialization failed");
+ return err;
+ }
+ }
+
+ err += et61x251_set_compression(cam, &cam->compression);
+ err += et61x251_set_pix_format(cam, &s->pix_format);
+ if (s->set_pix_format)
+ err += s->set_pix_format(cam, &s->pix_format);
+ if (err)
+ return err;
+
+ if (s->pix_format.pixelformat == V4L2_PIX_FMT_ET61X251)
+ DBG(3, "Compressed video format is active, quality %d",
+ cam->compression.quality);
+ else
+ DBG(3, "Uncompressed video format is active");
+
+ if (s->set_crop)
+ if ((err = s->set_crop(cam, rect))) {
+ DBG(3, "set_crop() failed");
+ return err;
+ }
+
+ if (s->set_ctrl) {
+ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
+ if (s->qctrl[i].id != 0 &&
+ !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) {
+ ctrl.id = s->qctrl[i].id;
+ ctrl.value = qctrl[i].default_value;
+ err = s->set_ctrl(cam, &ctrl);
+ if (err) {
+ DBG(3, "Set %s control failed",
+ s->qctrl[i].name);
+ return err;
+ }
+ DBG(3, "Image sensor supports '%s' control",
+ s->qctrl[i].name);
+ }
+ }
+
+ if (!(cam->state & DEV_INITIALIZED)) {
+ mutex_init(&cam->fileop_mutex);
+ spin_lock_init(&cam->queue_lock);
+ init_waitqueue_head(&cam->wait_frame);
+ init_waitqueue_head(&cam->wait_stream);
+ cam->nreadbuffers = 2;
+ memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl));
+ memcpy(&(s->_rect), &(s->cropcap.defrect),
+ sizeof(struct v4l2_rect));
+ cam->state |= DEV_INITIALIZED;
+ }
+
+ DBG(2, "Initialization succeeded");
+ return 0;
+}
+
+/*****************************************************************************/
+
+static void et61x251_release_resources(struct kref *kref)
+{
+ struct et61x251_device *cam;
+
+ mutex_lock(&et61x251_sysfs_lock);
+
+ cam = container_of(kref, struct et61x251_device, kref);
+
+ DBG(2, "V4L2 device %s deregistered",
+ video_device_node_name(cam->v4ldev));
+ video_set_drvdata(cam->v4ldev, NULL);
+ video_unregister_device(cam->v4ldev);
+ usb_put_dev(cam->usbdev);
+ kfree(cam->control_buffer);
+ kfree(cam);
+
+ mutex_unlock(&et61x251_sysfs_lock);
+}
+
+
+static int et61x251_open(struct file *filp)
+{
+ struct et61x251_device* cam;
+ int err = 0;
+
+ if (!down_read_trylock(&et61x251_dev_lock))
+ return -ERESTARTSYS;
+
+ cam = video_drvdata(filp);
+
+ if (wait_for_completion_interruptible(&cam->probe)) {
+ up_read(&et61x251_dev_lock);
+ return -ERESTARTSYS;
+ }
+
+ kref_get(&cam->kref);
+
+ if (mutex_lock_interruptible(&cam->open_mutex)) {
+ kref_put(&cam->kref, et61x251_release_resources);
+ up_read(&et61x251_dev_lock);
+ return -ERESTARTSYS;
+ }
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present");
+ err = -ENODEV;
+ goto out;
+ }
+
+ if (cam->users) {
+ DBG(2, "Device %s is already in use",
+ video_device_node_name(cam->v4ldev));
+ DBG(3, "Simultaneous opens are not supported");
+ if ((filp->f_flags & O_NONBLOCK) ||
+ (filp->f_flags & O_NDELAY)) {
+ err = -EWOULDBLOCK;
+ goto out;
+ }
+ DBG(2, "A blocking open() has been requested. Wait for the "
+ "device to be released...");
+ up_read(&et61x251_dev_lock);
+ err = wait_event_interruptible_exclusive(cam->wait_open,
+ (cam->state & DEV_DISCONNECTED)
+ || !cam->users);
+ down_read(&et61x251_dev_lock);
+ if (err)
+ goto out;
+ if (cam->state & DEV_DISCONNECTED) {
+ err = -ENODEV;
+ goto out;
+ }
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ err = et61x251_init(cam);
+ if (err) {
+ DBG(1, "Initialization failed again. "
+ "I will retry on next open().");
+ goto out;
+ }
+ cam->state &= ~DEV_MISCONFIGURED;
+ }
+
+ if ((err = et61x251_start_transfer(cam)))
+ goto out;
+
+ filp->private_data = cam;
+ cam->users++;
+ cam->io = IO_NONE;
+ cam->stream = STREAM_OFF;
+ cam->nbuffers = 0;
+ cam->frame_count = 0;
+ et61x251_empty_framequeues(cam);
+
+ DBG(3, "Video device %s is open",
+ video_device_node_name(cam->v4ldev));
+
+out:
+ mutex_unlock(&cam->open_mutex);
+ if (err)
+ kref_put(&cam->kref, et61x251_release_resources);
+ up_read(&et61x251_dev_lock);
+ return err;
+}
+
+
+static int et61x251_release(struct file *filp)
+{
+ struct et61x251_device* cam;
+
+ down_write(&et61x251_dev_lock);
+
+ cam = video_drvdata(filp);
+
+ et61x251_stop_transfer(cam);
+ et61x251_release_buffers(cam);
+ cam->users--;
+ wake_up_interruptible_nr(&cam->wait_open, 1);
+
+ DBG(3, "Video device %s closed",
+ video_device_node_name(cam->v4ldev));
+
+ kref_put(&cam->kref, et61x251_release_resources);
+
+ up_write(&et61x251_dev_lock);
+
+ return 0;
+}
+
+
+static ssize_t
+et61x251_read(struct file* filp, char __user * buf,
+ size_t count, loff_t* f_pos)
+{
+ struct et61x251_device *cam = video_drvdata(filp);
+ struct et61x251_frame_t* f, * i;
+ unsigned long lock_flags;
+ long timeout;
+ int err = 0;
+
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
+ return -ERESTARTSYS;
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present");
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ DBG(1, "The camera is misconfigured. Close and open it "
+ "again.");
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+
+ if (cam->io == IO_MMAP) {
+ DBG(3, "Close and open the device again to choose the read "
+ "method");
+ mutex_unlock(&cam->fileop_mutex);
+ return -EBUSY;
+ }
+
+ if (cam->io == IO_NONE) {
+ if (!et61x251_request_buffers(cam, cam->nreadbuffers,
+ IO_READ)) {
+ DBG(1, "read() failed, not enough memory");
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENOMEM;
+ }
+ cam->io = IO_READ;
+ cam->stream = STREAM_ON;
+ }
+
+ if (list_empty(&cam->inqueue)) {
+ if (!list_empty(&cam->outqueue))
+ et61x251_empty_framequeues(cam);
+ et61x251_queue_unusedframes(cam);
+ }
+
+ if (!count) {
+ mutex_unlock(&cam->fileop_mutex);
+ return 0;
+ }
+
+ if (list_empty(&cam->outqueue)) {
+ if (filp->f_flags & O_NONBLOCK) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EAGAIN;
+ }
+ timeout = wait_event_interruptible_timeout
+ ( cam->wait_frame,
+ (!list_empty(&cam->outqueue)) ||
+ (cam->state & DEV_DISCONNECTED) ||
+ (cam->state & DEV_MISCONFIGURED),
+ msecs_to_jiffies(
+ cam->module_param.frame_timeout * 1000
+ )
+ );
+ if (timeout < 0) {
+ mutex_unlock(&cam->fileop_mutex);
+ return timeout;
+ }
+ if (cam->state & DEV_DISCONNECTED) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+ if (!timeout || (cam->state & DEV_MISCONFIGURED)) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+ }
+
+ f = list_entry(cam->outqueue.prev, struct et61x251_frame_t, frame);
+
+ if (count > f->buf.bytesused)
+ count = f->buf.bytesused;
+
+ if (copy_to_user(buf, f->bufmem, count)) {
+ err = -EFAULT;
+ goto exit;
+ }
+ *f_pos += count;
+
+exit:
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ list_for_each_entry(i, &cam->outqueue, frame)
+ i->state = F_UNUSED;
+ INIT_LIST_HEAD(&cam->outqueue);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+ et61x251_queue_unusedframes(cam);
+
+ PDBGG("Frame #%lu, bytes read: %zu",
+ (unsigned long)f->buf.index, count);
+
+ mutex_unlock(&cam->fileop_mutex);
+
+ return err ? err : count;
+}
+
+
+static unsigned int et61x251_poll(struct file *filp, poll_table *wait)
+{
+ struct et61x251_device *cam = video_drvdata(filp);
+ struct et61x251_frame_t* f;
+ unsigned long lock_flags;
+ unsigned int mask = 0;
+
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
+ return POLLERR;
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present");
+ goto error;
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ DBG(1, "The camera is misconfigured. Close and open it "
+ "again.");
+ goto error;
+ }
+
+ if (cam->io == IO_NONE) {
+ if (!et61x251_request_buffers(cam, cam->nreadbuffers,
+ IO_READ)) {
+ DBG(1, "poll() failed, not enough memory");
+ goto error;
+ }
+ cam->io = IO_READ;
+ cam->stream = STREAM_ON;
+ }
+
+ if (cam->io == IO_READ) {
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ list_for_each_entry(f, &cam->outqueue, frame)
+ f->state = F_UNUSED;
+ INIT_LIST_HEAD(&cam->outqueue);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+ et61x251_queue_unusedframes(cam);
+ }
+
+ poll_wait(filp, &cam->wait_frame, wait);
+
+ if (!list_empty(&cam->outqueue))
+ mask |= POLLIN | POLLRDNORM;
+
+ mutex_unlock(&cam->fileop_mutex);
+
+ return mask;
+
+error:
+ mutex_unlock(&cam->fileop_mutex);
+ return POLLERR;
+}
+
+
+static void et61x251_vm_open(struct vm_area_struct* vma)
+{
+ struct et61x251_frame_t* f = vma->vm_private_data;
+ f->vma_use_count++;
+}
+
+
+static void et61x251_vm_close(struct vm_area_struct* vma)
+{
+ /* NOTE: buffers are not freed here */
+ struct et61x251_frame_t* f = vma->vm_private_data;
+ f->vma_use_count--;
+}
+
+
+static const struct vm_operations_struct et61x251_vm_ops = {
+ .open = et61x251_vm_open,
+ .close = et61x251_vm_close,
+};
+
+
+static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma)
+{
+ struct et61x251_device *cam = video_drvdata(filp);
+ unsigned long size = vma->vm_end - vma->vm_start,
+ start = vma->vm_start;
+ void *pos;
+ u32 i;
+
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
+ return -ERESTARTSYS;
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present");
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ DBG(1, "The camera is misconfigured. Close and open it "
+ "again.");
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+
+ if (!(vma->vm_flags & (VM_WRITE | VM_READ))) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EACCES;
+ }
+
+ if (cam->io != IO_MMAP ||
+ size != PAGE_ALIGN(cam->frame[0].buf.length)) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < cam->nbuffers; i++) {
+ if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff)
+ break;
+ }
+ if (i == cam->nbuffers) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EINVAL;
+ }
+
+ vma->vm_flags |= VM_IO;
+ vma->vm_flags |= VM_RESERVED;
+
+ pos = cam->frame[i].bufmem;
+ while (size > 0) { /* size is page-aligned */
+ if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EAGAIN;
+ }
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ vma->vm_ops = &et61x251_vm_ops;
+ vma->vm_private_data = &cam->frame[i];
+ et61x251_vm_open(vma);
+
+ mutex_unlock(&cam->fileop_mutex);
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+static int
+et61x251_vidioc_querycap(struct et61x251_device* cam, void __user * arg)
+{
+ struct v4l2_capability cap = {
+ .driver = "et61x251",
+ .version = LINUX_VERSION_CODE,
+ .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING,
+ };
+
+ strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card));
+ if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0)
+ strlcpy(cap.bus_info, dev_name(&cam->usbdev->dev),
+ sizeof(cap.bus_info));
+
+ if (copy_to_user(arg, &cap, sizeof(cap)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_enuminput(struct et61x251_device* cam, void __user * arg)
+{
+ struct v4l2_input i;
+
+ if (copy_from_user(&i, arg, sizeof(i)))
+ return -EFAULT;
+
+ if (i.index)
+ return -EINVAL;
+
+ memset(&i, 0, sizeof(i));
+ strcpy(i.name, "Camera");
+ i.type = V4L2_INPUT_TYPE_CAMERA;
+ i.capabilities = V4L2_IN_CAP_STD;
+
+ if (copy_to_user(arg, &i, sizeof(i)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_g_input(struct et61x251_device* cam, void __user * arg)
+{
+ int index = 0;
+
+ if (copy_to_user(arg, &index, sizeof(index)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_s_input(struct et61x251_device* cam, void __user * arg)
+{
+ int index;
+
+ if (copy_from_user(&index, arg, sizeof(index)))
+ return -EFAULT;
+
+ if (index != 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_query_ctrl(struct et61x251_device* cam, void __user * arg)
+{
+ struct et61x251_sensor* s = &cam->sensor;
+ struct v4l2_queryctrl qc;
+ u8 i;
+
+ if (copy_from_user(&qc, arg, sizeof(qc)))
+ return -EFAULT;
+
+ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
+ if (qc.id && qc.id == s->qctrl[i].id) {
+ memcpy(&qc, &(s->qctrl[i]), sizeof(qc));
+ if (copy_to_user(arg, &qc, sizeof(qc)))
+ return -EFAULT;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+
+static int
+et61x251_vidioc_g_ctrl(struct et61x251_device* cam, void __user * arg)
+{
+ struct et61x251_sensor* s = &cam->sensor;
+ struct v4l2_control ctrl;
+ int err = 0;
+ u8 i;
+
+ if (!s->get_ctrl && !s->set_ctrl)
+ return -EINVAL;
+
+ if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
+ return -EFAULT;
+
+ if (!s->get_ctrl) {
+ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
+ if (ctrl.id == s->qctrl[i].id) {
+ ctrl.value = s->_qctrl[i].default_value;
+ goto exit;
+ }
+ return -EINVAL;
+ } else
+ err = s->get_ctrl(cam, &ctrl);
+
+exit:
+ if (copy_to_user(arg, &ctrl, sizeof(ctrl)))
+ return -EFAULT;
+
+ return err;
+}
+
+
+static int
+et61x251_vidioc_s_ctrl(struct et61x251_device* cam, void __user * arg)
+{
+ struct et61x251_sensor* s = &cam->sensor;
+ struct v4l2_control ctrl;
+ u8 i;
+ int err = 0;
+
+ if (!s->set_ctrl)
+ return -EINVAL;
+
+ if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
+ return -EFAULT;
+
+ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) {
+ if (ctrl.id == s->qctrl[i].id) {
+ if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)
+ return -EINVAL;
+ if (ctrl.value < s->qctrl[i].minimum ||
+ ctrl.value > s->qctrl[i].maximum)
+ return -ERANGE;
+ ctrl.value -= ctrl.value % s->qctrl[i].step;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(s->qctrl))
+ return -EINVAL;
+ if ((err = s->set_ctrl(cam, &ctrl)))
+ return err;
+
+ s->_qctrl[i].default_value = ctrl.value;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_cropcap(struct et61x251_device* cam, void __user * arg)
+{
+ struct v4l2_cropcap* cc = &(cam->sensor.cropcap);
+
+ cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cc->pixelaspect.numerator = 1;
+ cc->pixelaspect.denominator = 1;
+
+ if (copy_to_user(arg, cc, sizeof(*cc)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_g_crop(struct et61x251_device* cam, void __user * arg)
+{
+ struct et61x251_sensor* s = &cam->sensor;
+ struct v4l2_crop crop = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ };
+
+ memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect));
+
+ if (copy_to_user(arg, &crop, sizeof(crop)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_s_crop(struct et61x251_device* cam, void __user * arg)
+{
+ struct et61x251_sensor* s = &cam->sensor;
+ struct v4l2_crop crop;
+ struct v4l2_rect* rect;
+ struct v4l2_rect* bounds = &(s->cropcap.bounds);
+ struct v4l2_pix_format* pix_format = &(s->pix_format);
+ u8 scale;
+ const enum et61x251_stream_state stream = cam->stream;
+ const u32 nbuffers = cam->nbuffers;
+ u32 i;
+ int err = 0;
+
+ if (copy_from_user(&crop, arg, sizeof(crop)))
+ return -EFAULT;
+
+ rect = &(crop.c);
+
+ if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (cam->module_param.force_munmap)
+ for (i = 0; i < cam->nbuffers; i++)
+ if (cam->frame[i].vma_use_count) {
+ DBG(3, "VIDIOC_S_CROP failed. "
+ "Unmap the buffers first.");
+ return -EBUSY;
+ }
+
+ /* Preserve R,G or B origin */
+ rect->left = (s->_rect.left & 1L) ? rect->left | 1L : rect->left & ~1L;
+ rect->top = (s->_rect.top & 1L) ? rect->top | 1L : rect->top & ~1L;
+
+ if (rect->width < 16)
+ rect->width = 16;
+ if (rect->height < 16)
+ rect->height = 16;
+ if (rect->width > bounds->width)
+ rect->width = bounds->width;
+ if (rect->height > bounds->height)
+ rect->height = bounds->height;
+ if (rect->left < bounds->left)
+ rect->left = bounds->left;
+ if (rect->top < bounds->top)
+ rect->top = bounds->top;
+ if (rect->left + rect->width > bounds->left + bounds->width)
+ rect->left = bounds->left+bounds->width - rect->width;
+ if (rect->top + rect->height > bounds->top + bounds->height)
+ rect->top = bounds->top+bounds->height - rect->height;
+
+ rect->width &= ~15L;
+ rect->height &= ~15L;
+
+ if (ET61X251_PRESERVE_IMGSCALE) {
+ /* Calculate the actual scaling factor */
+ u32 a, b;
+ a = rect->width * rect->height;
+ b = pix_format->width * pix_format->height;
+ scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1;
+ } else
+ scale = 1;
+
+ if (cam->stream == STREAM_ON)
+ if ((err = et61x251_stream_interrupt(cam)))
+ return err;
+
+ if (copy_to_user(arg, &crop, sizeof(crop))) {
+ cam->stream = stream;
+ return -EFAULT;
+ }
+
+ if (cam->module_param.force_munmap || cam->io == IO_READ)
+ et61x251_release_buffers(cam);
+
+ err = et61x251_set_crop(cam, rect);
+ if (s->set_crop)
+ err += s->set_crop(cam, rect);
+ err += et61x251_set_scale(cam, scale);
+
+ if (err) { /* atomic, no rollback in ioctl() */
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To "
+ "use the camera, close and open %s again.",
+ video_device_node_name(cam->v4ldev));
+ return -EIO;
+ }
+
+ s->pix_format.width = rect->width/scale;
+ s->pix_format.height = rect->height/scale;
+ memcpy(&(s->_rect), rect, sizeof(*rect));
+
+ if ((cam->module_param.force_munmap || cam->io == IO_READ) &&
+ nbuffers != et61x251_request_buffers(cam, nbuffers, cam->io)) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To "
+ "use the camera, close and open %s again.",
+ video_device_node_name(cam->v4ldev));
+ return -ENOMEM;
+ }
+
+ if (cam->io == IO_READ)
+ et61x251_empty_framequeues(cam);
+ else if (cam->module_param.force_munmap)
+ et61x251_requeue_outqueue(cam);
+
+ cam->stream = stream;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_enum_framesizes(struct et61x251_device* cam, void __user * arg)
+{
+ struct v4l2_frmsizeenum frmsize;
+
+ if (copy_from_user(&frmsize, arg, sizeof(frmsize)))
+ return -EFAULT;
+
+ if (frmsize.index != 0)
+ return -EINVAL;
+
+ if (frmsize.pixel_format != V4L2_PIX_FMT_ET61X251 &&
+ frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8)
+ return -EINVAL;
+
+ frmsize.type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ frmsize.stepwise.min_width = frmsize.stepwise.step_width = 16;
+ frmsize.stepwise.min_height = frmsize.stepwise.step_height = 16;
+ frmsize.stepwise.max_width = cam->sensor.cropcap.bounds.width;
+ frmsize.stepwise.max_height = cam->sensor.cropcap.bounds.height;
+ memset(&frmsize.reserved, 0, sizeof(frmsize.reserved));
+
+ if (copy_to_user(arg, &frmsize, sizeof(frmsize)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_enum_fmt(struct et61x251_device* cam, void __user * arg)
+{
+ struct v4l2_fmtdesc fmtd;
+
+ if (copy_from_user(&fmtd, arg, sizeof(fmtd)))
+ return -EFAULT;
+
+ if (fmtd.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (fmtd.index == 0) {
+ strcpy(fmtd.description, "bayer rgb");
+ fmtd.pixelformat = V4L2_PIX_FMT_SBGGR8;
+ } else if (fmtd.index == 1) {
+ strcpy(fmtd.description, "compressed");
+ fmtd.pixelformat = V4L2_PIX_FMT_ET61X251;
+ fmtd.flags = V4L2_FMT_FLAG_COMPRESSED;
+ } else
+ return -EINVAL;
+
+ fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ memset(&fmtd.reserved, 0, sizeof(fmtd.reserved));
+
+ if (copy_to_user(arg, &fmtd, sizeof(fmtd)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_g_fmt(struct et61x251_device* cam, void __user * arg)
+{
+ struct v4l2_format format;
+ struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format);
+
+ if (copy_from_user(&format, arg, sizeof(format)))
+ return -EFAULT;
+
+ if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ pfmt->colorspace = (pfmt->pixelformat == V4L2_PIX_FMT_ET61X251) ?
+ 0 : V4L2_COLORSPACE_SRGB;
+ pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_ET61X251)
+ ? 0 : (pfmt->width * pfmt->priv) / 8;
+ pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8);
+ pfmt->field = V4L2_FIELD_NONE;
+ memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt));
+
+ if (copy_to_user(arg, &format, sizeof(format)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_try_s_fmt(struct et61x251_device* cam, unsigned int cmd,
+ void __user * arg)
+{
+ struct et61x251_sensor* s = &cam->sensor;
+ struct v4l2_format format;
+ struct v4l2_pix_format* pix;
+ struct v4l2_pix_format* pfmt = &(s->pix_format);
+ struct v4l2_rect* bounds = &(s->cropcap.bounds);
+ struct v4l2_rect rect;
+ u8 scale;
+ const enum et61x251_stream_state stream = cam->stream;
+ const u32 nbuffers = cam->nbuffers;
+ u32 i;
+ int err = 0;
+
+ if (copy_from_user(&format, arg, sizeof(format)))
+ return -EFAULT;
+
+ pix = &(format.fmt.pix);
+
+ if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ memcpy(&rect, &(s->_rect), sizeof(rect));
+
+ { /* calculate the actual scaling factor */
+ u32 a, b;
+ a = rect.width * rect.height;
+ b = pix->width * pix->height;
+ scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1;
+ }
+
+ rect.width = scale * pix->width;
+ rect.height = scale * pix->height;
+
+ if (rect.width < 16)
+ rect.width = 16;
+ if (rect.height < 16)
+ rect.height = 16;
+ if (rect.width > bounds->left + bounds->width - rect.left)
+ rect.width = bounds->left + bounds->width - rect.left;
+ if (rect.height > bounds->top + bounds->height - rect.top)
+ rect.height = bounds->top + bounds->height - rect.top;
+
+ rect.width &= ~15L;
+ rect.height &= ~15L;
+
+ { /* adjust the scaling factor */
+ u32 a, b;
+ a = rect.width * rect.height;
+ b = pix->width * pix->height;
+ scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1;
+ }
+
+ pix->width = rect.width / scale;
+ pix->height = rect.height / scale;
+
+ if (pix->pixelformat != V4L2_PIX_FMT_ET61X251 &&
+ pix->pixelformat != V4L2_PIX_FMT_SBGGR8)
+ pix->pixelformat = pfmt->pixelformat;
+ pix->priv = pfmt->priv; /* bpp */
+ pix->colorspace = (pix->pixelformat == V4L2_PIX_FMT_ET61X251) ?
+ 0 : V4L2_COLORSPACE_SRGB;
+ pix->colorspace = pfmt->colorspace;
+ pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_ET61X251)
+ ? 0 : (pix->width * pix->priv) / 8;
+ pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8);
+ pix->field = V4L2_FIELD_NONE;
+
+ if (cmd == VIDIOC_TRY_FMT) {
+ if (copy_to_user(arg, &format, sizeof(format)))
+ return -EFAULT;
+ return 0;
+ }
+
+ if (cam->module_param.force_munmap)
+ for (i = 0; i < cam->nbuffers; i++)
+ if (cam->frame[i].vma_use_count) {
+ DBG(3, "VIDIOC_S_FMT failed. "
+ "Unmap the buffers first.");
+ return -EBUSY;
+ }
+
+ if (cam->stream == STREAM_ON)
+ if ((err = et61x251_stream_interrupt(cam)))
+ return err;
+
+ if (copy_to_user(arg, &format, sizeof(format))) {
+ cam->stream = stream;
+ return -EFAULT;
+ }
+
+ if (cam->module_param.force_munmap || cam->io == IO_READ)
+ et61x251_release_buffers(cam);
+
+ err += et61x251_set_pix_format(cam, pix);
+ err += et61x251_set_crop(cam, &rect);
+ if (s->set_pix_format)
+ err += s->set_pix_format(cam, pix);
+ if (s->set_crop)
+ err += s->set_crop(cam, &rect);
+ err += et61x251_set_scale(cam, scale);
+
+ if (err) { /* atomic, no rollback in ioctl() */
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To "
+ "use the camera, close and open %s again.",
+ video_device_node_name(cam->v4ldev));
+ return -EIO;
+ }
+
+ memcpy(pfmt, pix, sizeof(*pix));
+ memcpy(&(s->_rect), &rect, sizeof(rect));
+
+ if ((cam->module_param.force_munmap || cam->io == IO_READ) &&
+ nbuffers != et61x251_request_buffers(cam, nbuffers, cam->io)) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To "
+ "use the camera, close and open %s again.",
+ video_device_node_name(cam->v4ldev));
+ return -ENOMEM;
+ }
+
+ if (cam->io == IO_READ)
+ et61x251_empty_framequeues(cam);
+ else if (cam->module_param.force_munmap)
+ et61x251_requeue_outqueue(cam);
+
+ cam->stream = stream;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_g_jpegcomp(struct et61x251_device* cam, void __user * arg)
+{
+ if (copy_to_user(arg, &cam->compression,
+ sizeof(cam->compression)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_s_jpegcomp(struct et61x251_device* cam, void __user * arg)
+{
+ struct v4l2_jpegcompression jc;
+ const enum et61x251_stream_state stream = cam->stream;
+ int err = 0;
+
+ if (copy_from_user(&jc, arg, sizeof(jc)))
+ return -EFAULT;
+
+ if (jc.quality != 0 && jc.quality != 1)
+ return -EINVAL;
+
+ if (cam->stream == STREAM_ON)
+ if ((err = et61x251_stream_interrupt(cam)))
+ return err;
+
+ err += et61x251_set_compression(cam, &jc);
+ if (err) { /* atomic, no rollback in ioctl() */
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware "
+ "problems. To use the camera, close and open "
+ "%s again.", video_device_node_name(cam->v4ldev));
+ return -EIO;
+ }
+
+ cam->compression.quality = jc.quality;
+
+ cam->stream = stream;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_reqbufs(struct et61x251_device* cam, void __user * arg)
+{
+ struct v4l2_requestbuffers rb;
+ u32 i;
+ int err;
+
+ if (copy_from_user(&rb, arg, sizeof(rb)))
+ return -EFAULT;
+
+ if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ rb.memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+ if (cam->io == IO_READ) {
+ DBG(3, "Close and open the device again to choose the mmap "
+ "I/O method");
+ return -EBUSY;
+ }
+
+ for (i = 0; i < cam->nbuffers; i++)
+ if (cam->frame[i].vma_use_count) {
+ DBG(3, "VIDIOC_REQBUFS failed. "
+ "Previous buffers are still mapped.");
+ return -EBUSY;
+ }
+
+ if (cam->stream == STREAM_ON)
+ if ((err = et61x251_stream_interrupt(cam)))
+ return err;
+
+ et61x251_empty_framequeues(cam);
+
+ et61x251_release_buffers(cam);
+ if (rb.count)
+ rb.count = et61x251_request_buffers(cam, rb.count, IO_MMAP);
+
+ if (copy_to_user(arg, &rb, sizeof(rb))) {
+ et61x251_release_buffers(cam);
+ cam->io = IO_NONE;
+ return -EFAULT;
+ }
+
+ cam->io = rb.count ? IO_MMAP : IO_NONE;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_querybuf(struct et61x251_device* cam, void __user * arg)
+{
+ struct v4l2_buffer b;
+
+ if (copy_from_user(&b, arg, sizeof(b)))
+ return -EFAULT;
+
+ if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ b.index >= cam->nbuffers || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ memcpy(&b, &cam->frame[b.index].buf, sizeof(b));
+
+ if (cam->frame[b.index].vma_use_count)
+ b.flags |= V4L2_BUF_FLAG_MAPPED;
+
+ if (cam->frame[b.index].state == F_DONE)
+ b.flags |= V4L2_BUF_FLAG_DONE;
+ else if (cam->frame[b.index].state != F_UNUSED)
+ b.flags |= V4L2_BUF_FLAG_QUEUED;
+
+ if (copy_to_user(arg, &b, sizeof(b)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_qbuf(struct et61x251_device* cam, void __user * arg)
+{
+ struct v4l2_buffer b;
+ unsigned long lock_flags;
+
+ if (copy_from_user(&b, arg, sizeof(b)))
+ return -EFAULT;
+
+ if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ b.index >= cam->nbuffers || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ if (cam->frame[b.index].state != F_UNUSED)
+ return -EINVAL;
+
+ cam->frame[b.index].state = F_QUEUED;
+
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ list_add_tail(&cam->frame[b.index].frame, &cam->inqueue);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+ PDBGG("Frame #%lu queued", (unsigned long)b.index);
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_dqbuf(struct et61x251_device* cam, struct file* filp,
+ void __user * arg)
+{
+ struct v4l2_buffer b;
+ struct et61x251_frame_t *f;
+ unsigned long lock_flags;
+ long timeout;
+
+ if (copy_from_user(&b, arg, sizeof(b)))
+ return -EFAULT;
+
+ if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io!= IO_MMAP)
+ return -EINVAL;
+
+ if (list_empty(&cam->outqueue)) {
+ if (cam->stream == STREAM_OFF)
+ return -EINVAL;
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ timeout = wait_event_interruptible_timeout
+ ( cam->wait_frame,
+ (!list_empty(&cam->outqueue)) ||
+ (cam->state & DEV_DISCONNECTED) ||
+ (cam->state & DEV_MISCONFIGURED),
+ cam->module_param.frame_timeout *
+ 1000 * msecs_to_jiffies(1) );
+ if (timeout < 0)
+ return timeout;
+ if (cam->state & DEV_DISCONNECTED)
+ return -ENODEV;
+ if (!timeout || (cam->state & DEV_MISCONFIGURED))
+ return -EIO;
+ }
+
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ f = list_entry(cam->outqueue.next, struct et61x251_frame_t, frame);
+ list_del(cam->outqueue.next);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+ f->state = F_UNUSED;
+
+ memcpy(&b, &f->buf, sizeof(b));
+ if (f->vma_use_count)
+ b.flags |= V4L2_BUF_FLAG_MAPPED;
+
+ if (copy_to_user(arg, &b, sizeof(b)))
+ return -EFAULT;
+
+ PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index);
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_streamon(struct et61x251_device* cam, void __user * arg)
+{
+ int type;
+
+ if (copy_from_user(&type, arg, sizeof(type)))
+ return -EFAULT;
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ cam->stream = STREAM_ON;
+
+ DBG(3, "Stream on");
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_streamoff(struct et61x251_device* cam, void __user * arg)
+{
+ int type, err;
+
+ if (copy_from_user(&type, arg, sizeof(type)))
+ return -EFAULT;
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ if (cam->stream == STREAM_ON)
+ if ((err = et61x251_stream_interrupt(cam)))
+ return err;
+
+ et61x251_empty_framequeues(cam);
+
+ DBG(3, "Stream off");
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_g_parm(struct et61x251_device* cam, void __user * arg)
+{
+ struct v4l2_streamparm sp;
+
+ if (copy_from_user(&sp, arg, sizeof(sp)))
+ return -EFAULT;
+
+ if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ sp.parm.capture.extendedmode = 0;
+ sp.parm.capture.readbuffers = cam->nreadbuffers;
+
+ if (copy_to_user(arg, &sp, sizeof(sp)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+et61x251_vidioc_s_parm(struct et61x251_device* cam, void __user * arg)
+{
+ struct v4l2_streamparm sp;
+
+ if (copy_from_user(&sp, arg, sizeof(sp)))
+ return -EFAULT;
+
+ if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ sp.parm.capture.extendedmode = 0;
+
+ if (sp.parm.capture.readbuffers == 0)
+ sp.parm.capture.readbuffers = cam->nreadbuffers;
+
+ if (sp.parm.capture.readbuffers > ET61X251_MAX_FRAMES)
+ sp.parm.capture.readbuffers = ET61X251_MAX_FRAMES;
+
+ if (copy_to_user(arg, &sp, sizeof(sp)))
+ return -EFAULT;
+
+ cam->nreadbuffers = sp.parm.capture.readbuffers;
+
+ return 0;
+}
+
+
+static long et61x251_ioctl_v4l2(struct file *filp,
+ unsigned int cmd, void __user *arg)
+{
+ struct et61x251_device *cam = video_drvdata(filp);
+
+ switch (cmd) {
+
+ case VIDIOC_QUERYCAP:
+ return et61x251_vidioc_querycap(cam, arg);
+
+ case VIDIOC_ENUMINPUT:
+ return et61x251_vidioc_enuminput(cam, arg);
+
+ case VIDIOC_G_INPUT:
+ return et61x251_vidioc_g_input(cam, arg);
+
+ case VIDIOC_S_INPUT:
+ return et61x251_vidioc_s_input(cam, arg);
+
+ case VIDIOC_QUERYCTRL:
+ return et61x251_vidioc_query_ctrl(cam, arg);
+
+ case VIDIOC_G_CTRL:
+ return et61x251_vidioc_g_ctrl(cam, arg);
+
+ case VIDIOC_S_CTRL:
+ return et61x251_vidioc_s_ctrl(cam, arg);
+
+ case VIDIOC_CROPCAP:
+ return et61x251_vidioc_cropcap(cam, arg);
+
+ case VIDIOC_G_CROP:
+ return et61x251_vidioc_g_crop(cam, arg);
+
+ case VIDIOC_S_CROP:
+ return et61x251_vidioc_s_crop(cam, arg);
+
+ case VIDIOC_ENUM_FMT:
+ return et61x251_vidioc_enum_fmt(cam, arg);
+
+ case VIDIOC_G_FMT:
+ return et61x251_vidioc_g_fmt(cam, arg);
+
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_S_FMT:
+ return et61x251_vidioc_try_s_fmt(cam, cmd, arg);
+
+ case VIDIOC_ENUM_FRAMESIZES:
+ return et61x251_vidioc_enum_framesizes(cam, arg);
+
+ case VIDIOC_G_JPEGCOMP:
+ return et61x251_vidioc_g_jpegcomp(cam, arg);
+
+ case VIDIOC_S_JPEGCOMP:
+ return et61x251_vidioc_s_jpegcomp(cam, arg);
+
+ case VIDIOC_REQBUFS:
+ return et61x251_vidioc_reqbufs(cam, arg);
+
+ case VIDIOC_QUERYBUF:
+ return et61x251_vidioc_querybuf(cam, arg);
+
+ case VIDIOC_QBUF:
+ return et61x251_vidioc_qbuf(cam, arg);
+
+ case VIDIOC_DQBUF:
+ return et61x251_vidioc_dqbuf(cam, filp, arg);
+
+ case VIDIOC_STREAMON:
+ return et61x251_vidioc_streamon(cam, arg);
+
+ case VIDIOC_STREAMOFF:
+ return et61x251_vidioc_streamoff(cam, arg);
+
+ case VIDIOC_G_PARM:
+ return et61x251_vidioc_g_parm(cam, arg);
+
+ case VIDIOC_S_PARM:
+ return et61x251_vidioc_s_parm(cam, arg);
+
+ default:
+ return -ENOTTY;
+
+ }
+}
+
+
+static long et61x251_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct et61x251_device *cam = video_drvdata(filp);
+ long err = 0;
+
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
+ return -ERESTARTSYS;
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present");
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ DBG(1, "The camera is misconfigured. Close and open it "
+ "again.");
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+
+ V4LDBG(3, "et61x251", cmd);
+
+ err = et61x251_ioctl_v4l2(filp, cmd, (void __user *)arg);
+
+ mutex_unlock(&cam->fileop_mutex);
+
+ return err;
+}
+
+
+static const struct v4l2_file_operations et61x251_fops = {
+ .owner = THIS_MODULE,
+ .open = et61x251_open,
+ .release = et61x251_release,
+ .unlocked_ioctl = et61x251_ioctl,
+ .read = et61x251_read,
+ .poll = et61x251_poll,
+ .mmap = et61x251_mmap,
+};
+
+/*****************************************************************************/
+
+/* It exists a single interface only. We do not need to validate anything. */
+static int
+et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct et61x251_device* cam;
+ static unsigned int dev_nr;
+ unsigned int i;
+ int err = 0;
+
+ if (!(cam = kzalloc(sizeof(struct et61x251_device), GFP_KERNEL)))
+ return -ENOMEM;
+
+ cam->usbdev = udev;
+
+ if (!(cam->control_buffer = kzalloc(8, GFP_KERNEL))) {
+ DBG(1, "kmalloc() failed");
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ if (!(cam->v4ldev = video_device_alloc())) {
+ DBG(1, "video_device_alloc() failed");
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ DBG(2, "ET61X[12]51 PC Camera Controller detected "
+ "(vid/pid 0x%04X:0x%04X)",id->idVendor, id->idProduct);
+
+ for (i = 0; et61x251_sensor_table[i]; i++) {
+ err = et61x251_sensor_table[i](cam);
+ if (!err)
+ break;
+ }
+
+ if (!err)
+ DBG(2, "%s image sensor detected", cam->sensor.name);
+ else {
+ DBG(1, "No supported image sensor detected");
+ err = -ENODEV;
+ goto fail;
+ }
+
+ if (et61x251_init(cam)) {
+ DBG(1, "Initialization failed. I will retry on open().");
+ cam->state |= DEV_MISCONFIGURED;
+ }
+
+ strcpy(cam->v4ldev->name, "ET61X[12]51 PC Camera");
+ cam->v4ldev->fops = &et61x251_fops;
+ cam->v4ldev->release = video_device_release;
+ cam->v4ldev->parent = &udev->dev;
+ video_set_drvdata(cam->v4ldev, cam);
+
+ init_completion(&cam->probe);
+
+ err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER,
+ video_nr[dev_nr]);
+ if (err) {
+ DBG(1, "V4L2 device registration failed");
+ if (err == -ENFILE && video_nr[dev_nr] == -1)
+ DBG(1, "Free /dev/videoX node not found");
+ video_nr[dev_nr] = -1;
+ dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0;
+ complete_all(&cam->probe);
+ goto fail;
+ }
+
+ DBG(2, "V4L2 device registered as %s",
+ video_device_node_name(cam->v4ldev));
+
+ cam->module_param.force_munmap = force_munmap[dev_nr];
+ cam->module_param.frame_timeout = frame_timeout[dev_nr];
+
+ dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0;
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ err = et61x251_create_sysfs(cam);
+ if (!err)
+ DBG(2, "Optional device control through 'sysfs' "
+ "interface ready");
+ else
+ DBG(2, "Failed to create 'sysfs' interface for optional "
+ "device controlling. Error #%d", err);
+#else
+ DBG(2, "Optional device control through 'sysfs' interface disabled");
+ DBG(3, "Compile the kernel with the 'CONFIG_VIDEO_ADV_DEBUG' "
+ "configuration option to enable it.");
+#endif
+
+ usb_set_intfdata(intf, cam);
+ kref_init(&cam->kref);
+ usb_get_dev(cam->usbdev);
+
+ complete_all(&cam->probe);
+
+ return 0;
+
+fail:
+ if (cam) {
+ kfree(cam->control_buffer);
+ if (cam->v4ldev)
+ video_device_release(cam->v4ldev);
+ kfree(cam);
+ }
+ return err;
+}
+
+
+static void et61x251_usb_disconnect(struct usb_interface* intf)
+{
+ struct et61x251_device* cam;
+
+ down_write(&et61x251_dev_lock);
+
+ cam = usb_get_intfdata(intf);
+
+ DBG(2, "Disconnecting %s...", cam->v4ldev->name);
+
+ if (cam->users) {
+ DBG(2, "Device %s is open! Deregistration and memory "
+ "deallocation are deferred.",
+ video_device_node_name(cam->v4ldev));
+ cam->state |= DEV_MISCONFIGURED;
+ et61x251_stop_transfer(cam);
+ cam->state |= DEV_DISCONNECTED;
+ wake_up_interruptible(&cam->wait_frame);
+ wake_up(&cam->wait_stream);
+ } else
+ cam->state |= DEV_DISCONNECTED;
+
+ wake_up_interruptible_all(&cam->wait_open);
+
+ kref_put(&cam->kref, et61x251_release_resources);
+
+ up_write(&et61x251_dev_lock);
+}
+
+
+static struct usb_driver et61x251_usb_driver = {
+ .name = "et61x251",
+ .id_table = et61x251_id_table,
+ .probe = et61x251_usb_probe,
+ .disconnect = et61x251_usb_disconnect,
+};
+
+module_usb_driver(et61x251_usb_driver);
diff --git a/drivers/media/video/et61x251/et61x251_sensor.h b/drivers/media/video/et61x251/et61x251_sensor.h
new file mode 100644
index 00000000..71a03148
--- /dev/null
+++ b/drivers/media/video/et61x251/et61x251_sensor.h
@@ -0,0 +1,108 @@
+/***************************************************************************
+ * API for image sensors connected to ET61X[12]51 PC Camera Controllers *
+ * *
+ * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#ifndef _ET61X251_SENSOR_H_
+#define _ET61X251_SENSOR_H_
+
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <linux/device.h>
+#include <linux/stddef.h>
+#include <linux/errno.h>
+#include <asm/types.h>
+
+struct et61x251_device;
+struct et61x251_sensor;
+
+/*****************************************************************************/
+
+extern int et61x251_probe_tas5130d1b(struct et61x251_device* cam);
+
+#define ET61X251_SENSOR_TABLE \
+/* Weak detections must go at the end of the list */ \
+static int (*et61x251_sensor_table[])(struct et61x251_device*) = { \
+ &et61x251_probe_tas5130d1b, \
+ NULL, \
+};
+
+extern struct et61x251_device*
+et61x251_match_id(struct et61x251_device* cam, const struct usb_device_id *id);
+
+extern void
+et61x251_attach_sensor(struct et61x251_device* cam,
+ const struct et61x251_sensor* sensor);
+
+/*****************************************************************************/
+
+extern int et61x251_write_reg(struct et61x251_device*, u8 value, u16 index);
+extern int et61x251_i2c_raw_write(struct et61x251_device*, u8 n, u8 data1,
+ u8 data2, u8 data3, u8 data4, u8 data5,
+ u8 data6, u8 data7, u8 data8, u8 address);
+
+/*****************************************************************************/
+
+enum et61x251_i2c_sysfs_ops {
+ ET61X251_I2C_READ = 0x01,
+ ET61X251_I2C_WRITE = 0x02,
+};
+
+enum et61x251_i2c_interface {
+ ET61X251_I2C_2WIRES,
+ ET61X251_I2C_3WIRES,
+};
+
+/* Repeat start condition when RSTA is high */
+enum et61x251_i2c_rsta {
+ ET61X251_I2C_RSTA_STOP = 0x00, /* stop then start */
+ ET61X251_I2C_RSTA_REPEAT = 0x01, /* repeat start */
+};
+
+#define ET61X251_MAX_CTRLS (V4L2_CID_LASTP1-V4L2_CID_BASE+10)
+
+struct et61x251_sensor {
+ char name[32];
+
+ enum et61x251_i2c_sysfs_ops sysfs_ops;
+
+ enum et61x251_i2c_interface interface;
+ u8 i2c_slave_id;
+ enum et61x251_i2c_rsta rsta;
+ struct v4l2_rect active_pixel; /* left and top define FVSX and FVSY */
+
+ struct v4l2_queryctrl qctrl[ET61X251_MAX_CTRLS];
+ struct v4l2_cropcap cropcap;
+ struct v4l2_pix_format pix_format;
+
+ int (*init)(struct et61x251_device* cam);
+ int (*get_ctrl)(struct et61x251_device* cam,
+ struct v4l2_control* ctrl);
+ int (*set_ctrl)(struct et61x251_device* cam,
+ const struct v4l2_control* ctrl);
+ int (*set_crop)(struct et61x251_device* cam,
+ const struct v4l2_rect* rect);
+ int (*set_pix_format)(struct et61x251_device* cam,
+ const struct v4l2_pix_format* pix);
+
+ /* Private */
+ struct v4l2_queryctrl _qctrl[ET61X251_MAX_CTRLS];
+ struct v4l2_rect _rect;
+};
+
+#endif /* _ET61X251_SENSOR_H_ */
diff --git a/drivers/media/video/et61x251/et61x251_tas5130d1b.c b/drivers/media/video/et61x251/et61x251_tas5130d1b.c
new file mode 100644
index 00000000..ced2e167
--- /dev/null
+++ b/drivers/media/video/et61x251/et61x251_tas5130d1b.c
@@ -0,0 +1,143 @@
+/***************************************************************************
+ * Plug-in for TAS5130D1B image sensor connected to the ET61X[12]51 *
+ * PC Camera Controllers *
+ * *
+ * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "et61x251_sensor.h"
+
+
+static int tas5130d1b_init(struct et61x251_device* cam)
+{
+ int err = 0;
+
+ err += et61x251_write_reg(cam, 0x14, 0x01);
+ err += et61x251_write_reg(cam, 0x1b, 0x02);
+ err += et61x251_write_reg(cam, 0x02, 0x12);
+ err += et61x251_write_reg(cam, 0x0e, 0x60);
+ err += et61x251_write_reg(cam, 0x80, 0x61);
+ err += et61x251_write_reg(cam, 0xf0, 0x62);
+ err += et61x251_write_reg(cam, 0x03, 0x63);
+ err += et61x251_write_reg(cam, 0x14, 0x64);
+ err += et61x251_write_reg(cam, 0xf4, 0x65);
+ err += et61x251_write_reg(cam, 0x01, 0x66);
+ err += et61x251_write_reg(cam, 0x05, 0x67);
+ err += et61x251_write_reg(cam, 0x8f, 0x68);
+ err += et61x251_write_reg(cam, 0x0f, 0x8d);
+ err += et61x251_write_reg(cam, 0x08, 0x8e);
+
+ return err;
+}
+
+
+static int tas5130d1b_set_ctrl(struct et61x251_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ err += et61x251_i2c_raw_write(cam, 2, 0x20,
+ 0xf6-ctrl->value, 0, 0, 0,
+ 0, 0, 0, 0);
+ break;
+ case V4L2_CID_EXPOSURE:
+ err += et61x251_i2c_raw_write(cam, 2, 0x40,
+ 0x47-ctrl->value, 0, 0, 0,
+ 0, 0, 0, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return err ? -EIO : 0;
+}
+
+
+static const struct et61x251_sensor tas5130d1b = {
+ .name = "TAS5130D1B",
+ .interface = ET61X251_I2C_3WIRES,
+ .rsta = ET61X251_I2C_RSTA_STOP,
+ .active_pixel = {
+ .left = 106,
+ .top = 13,
+ },
+ .init = &tas5130d1b_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "global gain",
+ .minimum = 0x00,
+ .maximum = 0xf6,
+ .step = 0x02,
+ .default_value = 0x0d,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x00,
+ .maximum = 0x47,
+ .step = 0x01,
+ .default_value = 0x23,
+ .flags = 0,
+ },
+ },
+ .set_ctrl = &tas5130d1b_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ .pix_format = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8,
+ },
+};
+
+
+int et61x251_probe_tas5130d1b(struct et61x251_device* cam)
+{
+ const struct usb_device_id tas5130d1b_id_table[] = {
+ { USB_DEVICE(0x102c, 0x6251), },
+ { }
+ };
+
+ /* Sensor detection is based on USB pid/vid */
+ if (!et61x251_match_id(cam, tas5130d1b_id_table))
+ return -ENODEV;
+
+ et61x251_attach_sensor(cam, &tas5130d1b);
+
+ return 0;
+}
diff --git a/drivers/media/video/fsl-viu.c b/drivers/media/video/fsl-viu.c
new file mode 100644
index 00000000..27e3e0c0
--- /dev/null
+++ b/drivers/media/video/fsl-viu.c
@@ -0,0 +1,1669 @@
+/*
+ * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * Freescale VIU video driver
+ *
+ * Authors: Hongjun Chen <hong-jun.chen@freescale.com>
+ * Porting to 2.6.35 by DENX Software Engineering,
+ * Anatolij Gustschin <agust@denx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf-dma-contig.h>
+
+#define DRV_NAME "fsl_viu"
+#define VIU_VERSION "0.5.1"
+
+#define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */
+
+#define VIU_VID_MEM_LIMIT 4 /* Video memory limit, in Mb */
+
+/* I2C address of video decoder chip is 0x4A */
+#define VIU_VIDEO_DECODER_ADDR 0x25
+
+/* supported controls */
+static struct v4l2_queryctrl viu_qctrl[] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 127,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 0x1,
+ .default_value = 0x10,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 0x1,
+ .default_value = 127,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_HUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Hue",
+ .minimum = -128,
+ .maximum = 127,
+ .step = 0x1,
+ .default_value = 0,
+ .flags = 0,
+ }
+};
+
+static int qctl_regs[ARRAY_SIZE(viu_qctrl)];
+
+static int info_level;
+
+#define dprintk(level, fmt, arg...) \
+ do { \
+ if (level <= info_level) \
+ printk(KERN_DEBUG "viu: " fmt , ## arg); \
+ } while (0)
+
+/*
+ * Basic structures
+ */
+struct viu_fmt {
+ char name[32];
+ u32 fourcc; /* v4l2 format id */
+ u32 pixelformat;
+ int depth;
+};
+
+static struct viu_fmt formats[] = {
+ {
+ .name = "RGB-16 (5/B-6/G-5/R)",
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .pixelformat = V4L2_PIX_FMT_RGB565,
+ .depth = 16,
+ }, {
+ .name = "RGB-32 (A-R-G-B)",
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .pixelformat = V4L2_PIX_FMT_RGB32,
+ .depth = 32,
+ }
+};
+
+struct viu_dev;
+struct viu_buf;
+
+/* buffer for one video frame */
+struct viu_buf {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+ struct viu_fmt *fmt;
+};
+
+struct viu_dmaqueue {
+ struct viu_dev *dev;
+ struct list_head active;
+ struct list_head queued;
+ struct timer_list timeout;
+};
+
+struct viu_status {
+ u32 field_irq;
+ u32 vsync_irq;
+ u32 hsync_irq;
+ u32 vstart_irq;
+ u32 dma_end_irq;
+ u32 error_irq;
+};
+
+struct viu_reg {
+ u32 status_cfg;
+ u32 luminance;
+ u32 chroma_r;
+ u32 chroma_g;
+ u32 chroma_b;
+ u32 field_base_addr;
+ u32 dma_inc;
+ u32 picture_count;
+ u32 req_alarm;
+ u32 alpha;
+} __attribute__ ((packed));
+
+struct viu_dev {
+ struct v4l2_device v4l2_dev;
+ struct mutex lock;
+ spinlock_t slock;
+ int users;
+
+ struct device *dev;
+ /* various device info */
+ struct video_device *vdev;
+ struct viu_dmaqueue vidq;
+ enum v4l2_field capfield;
+ int field;
+ int first;
+ int dma_done;
+
+ /* Hardware register area */
+ struct viu_reg *vr;
+
+ /* Interrupt vector */
+ int irq;
+ struct viu_status irqs;
+
+ /* video overlay */
+ struct v4l2_framebuffer ovbuf;
+ struct viu_fmt *ovfmt;
+ unsigned int ovenable;
+ enum v4l2_field ovfield;
+
+ /* crop */
+ struct v4l2_rect crop_current;
+
+ /* clock pointer */
+ struct clk *clk;
+
+ /* decoder */
+ struct v4l2_subdev *decoder;
+
+ v4l2_std_id std;
+};
+
+struct viu_fh {
+ struct viu_dev *dev;
+
+ /* video capture */
+ struct videobuf_queue vb_vidq;
+ spinlock_t vbq_lock; /* spinlock for the videobuf queue */
+
+ /* video overlay */
+ struct v4l2_window win;
+ struct v4l2_clip clips[1];
+
+ /* video capture */
+ struct viu_fmt *fmt;
+ int width, height, sizeimage;
+ enum v4l2_buf_type type;
+};
+
+static struct viu_reg reg_val;
+
+/*
+ * Macro definitions of VIU registers
+ */
+
+/* STATUS_CONFIG register */
+enum status_config {
+ SOFT_RST = 1 << 0,
+
+ ERR_MASK = 0x0f << 4, /* Error code mask */
+ ERR_NO = 0x00, /* No error */
+ ERR_DMA_V = 0x01 << 4, /* DMA in vertical active */
+ ERR_DMA_VB = 0x02 << 4, /* DMA in vertical blanking */
+ ERR_LINE_TOO_LONG = 0x04 << 4, /* Line too long */
+ ERR_TOO_MANG_LINES = 0x05 << 4, /* Too many lines in field */
+ ERR_LINE_TOO_SHORT = 0x06 << 4, /* Line too short */
+ ERR_NOT_ENOUGH_LINE = 0x07 << 4, /* Not enough lines in field */
+ ERR_FIFO_OVERFLOW = 0x08 << 4, /* FIFO overflow */
+ ERR_FIFO_UNDERFLOW = 0x09 << 4, /* FIFO underflow */
+ ERR_1bit_ECC = 0x0a << 4, /* One bit ECC error */
+ ERR_MORE_ECC = 0x0b << 4, /* Two/more bits ECC error */
+
+ INT_FIELD_EN = 0x01 << 8, /* Enable field interrupt */
+ INT_VSYNC_EN = 0x01 << 9, /* Enable vsync interrupt */
+ INT_HSYNC_EN = 0x01 << 10, /* Enable hsync interrupt */
+ INT_VSTART_EN = 0x01 << 11, /* Enable vstart interrupt */
+ INT_DMA_END_EN = 0x01 << 12, /* Enable DMA end interrupt */
+ INT_ERROR_EN = 0x01 << 13, /* Enable error interrupt */
+ INT_ECC_EN = 0x01 << 14, /* Enable ECC interrupt */
+
+ INT_FIELD_STATUS = 0x01 << 16, /* field interrupt status */
+ INT_VSYNC_STATUS = 0x01 << 17, /* vsync interrupt status */
+ INT_HSYNC_STATUS = 0x01 << 18, /* hsync interrupt status */
+ INT_VSTART_STATUS = 0x01 << 19, /* vstart interrupt status */
+ INT_DMA_END_STATUS = 0x01 << 20, /* DMA end interrupt status */
+ INT_ERROR_STATUS = 0x01 << 21, /* error interrupt status */
+
+ DMA_ACT = 0x01 << 27, /* Enable DMA transfer */
+ FIELD_NO = 0x01 << 28, /* Field number */
+ DITHER_ON = 0x01 << 29, /* Dithering is on */
+ ROUND_ON = 0x01 << 30, /* Round is on */
+ MODE_32BIT = 0x01 << 31, /* Data in RGBa888,
+ * 0 in RGB565
+ */
+};
+
+#define norm_maxw() 720
+#define norm_maxh() 576
+
+#define INT_ALL_STATUS (INT_FIELD_STATUS | INT_VSYNC_STATUS | \
+ INT_HSYNC_STATUS | INT_VSTART_STATUS | \
+ INT_DMA_END_STATUS | INT_ERROR_STATUS)
+
+#define NUM_FORMATS ARRAY_SIZE(formats)
+
+static irqreturn_t viu_intr(int irq, void *dev_id);
+
+struct viu_fmt *format_by_fourcc(int fourcc)
+{
+ int i;
+
+ for (i = 0; i < NUM_FORMATS; i++) {
+ if (formats[i].pixelformat == fourcc)
+ return formats + i;
+ }
+
+ dprintk(0, "unknown pixelformat:'%4.4s'\n", (char *)&fourcc);
+ return NULL;
+}
+
+void viu_start_dma(struct viu_dev *dev)
+{
+ struct viu_reg *vr = dev->vr;
+
+ dev->field = 0;
+
+ /* Enable DMA operation */
+ out_be32(&vr->status_cfg, SOFT_RST);
+ out_be32(&vr->status_cfg, INT_FIELD_EN);
+}
+
+void viu_stop_dma(struct viu_dev *dev)
+{
+ struct viu_reg *vr = dev->vr;
+ int cnt = 100;
+ u32 status_cfg;
+
+ out_be32(&vr->status_cfg, 0);
+
+ /* Clear pending interrupts */
+ status_cfg = in_be32(&vr->status_cfg);
+ if (status_cfg & 0x3f0000)
+ out_be32(&vr->status_cfg, status_cfg & 0x3f0000);
+
+ if (status_cfg & DMA_ACT) {
+ do {
+ status_cfg = in_be32(&vr->status_cfg);
+ if (status_cfg & INT_DMA_END_STATUS)
+ break;
+ } while (cnt--);
+
+ if (cnt < 0) {
+ /* timed out, issue soft reset */
+ out_be32(&vr->status_cfg, SOFT_RST);
+ out_be32(&vr->status_cfg, 0);
+ } else {
+ /* clear DMA_END and other pending irqs */
+ out_be32(&vr->status_cfg, status_cfg & 0x3f0000);
+ }
+ }
+
+ dev->field = 0;
+}
+
+static int restart_video_queue(struct viu_dmaqueue *vidq)
+{
+ struct viu_buf *buf, *prev;
+
+ dprintk(1, "%s vidq=0x%08lx\n", __func__, (unsigned long)vidq);
+ if (!list_empty(&vidq->active)) {
+ buf = list_entry(vidq->active.next, struct viu_buf, vb.queue);
+ dprintk(2, "restart_queue [%p/%d]: restart dma\n",
+ buf, buf->vb.i);
+
+ viu_stop_dma(vidq->dev);
+
+ /* cancel all outstanding capture requests */
+ list_for_each_entry_safe(buf, prev, &vidq->active, vb.queue) {
+ list_del(&buf->vb.queue);
+ buf->vb.state = VIDEOBUF_ERROR;
+ wake_up(&buf->vb.done);
+ }
+ mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT);
+ return 0;
+ }
+
+ prev = NULL;
+ for (;;) {
+ if (list_empty(&vidq->queued))
+ return 0;
+ buf = list_entry(vidq->queued.next, struct viu_buf, vb.queue);
+ if (prev == NULL) {
+ list_del(&buf->vb.queue);
+ list_add_tail(&buf->vb.queue, &vidq->active);
+
+ dprintk(1, "Restarting video dma\n");
+ viu_stop_dma(vidq->dev);
+ viu_start_dma(vidq->dev);
+
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT);
+ dprintk(2, "[%p/%d] restart_queue - first active\n",
+ buf, buf->vb.i);
+
+ } else if (prev->vb.width == buf->vb.width &&
+ prev->vb.height == buf->vb.height &&
+ prev->fmt == buf->fmt) {
+ list_del(&buf->vb.queue);
+ list_add_tail(&buf->vb.queue, &vidq->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ dprintk(2, "[%p/%d] restart_queue - move to active\n",
+ buf, buf->vb.i);
+ } else {
+ return 0;
+ }
+ prev = buf;
+ }
+}
+
+static void viu_vid_timeout(unsigned long data)
+{
+ struct viu_dev *dev = (struct viu_dev *)data;
+ struct viu_buf *buf;
+ struct viu_dmaqueue *vidq = &dev->vidq;
+
+ while (!list_empty(&vidq->active)) {
+ buf = list_entry(vidq->active.next, struct viu_buf, vb.queue);
+ list_del(&buf->vb.queue);
+ buf->vb.state = VIDEOBUF_ERROR;
+ wake_up(&buf->vb.done);
+ dprintk(1, "viu/0: [%p/%d] timeout\n", buf, buf->vb.i);
+ }
+
+ restart_video_queue(vidq);
+}
+
+/*
+ * Videobuf operations
+ */
+static int buffer_setup(struct videobuf_queue *vq, unsigned int *count,
+ unsigned int *size)
+{
+ struct viu_fh *fh = vq->priv_data;
+
+ *size = fh->width * fh->height * fh->fmt->depth >> 3;
+ if (*count == 0)
+ *count = 32;
+
+ while (*size * *count > VIU_VID_MEM_LIMIT * 1024 * 1024)
+ (*count)--;
+
+ dprintk(1, "%s, count=%d, size=%d\n", __func__, *count, *size);
+ return 0;
+}
+
+static void free_buffer(struct videobuf_queue *vq, struct viu_buf *buf)
+{
+ struct videobuf_buffer *vb = &buf->vb;
+ void *vaddr = NULL;
+
+ BUG_ON(in_interrupt());
+
+ videobuf_waiton(vq, &buf->vb, 0, 0);
+
+ if (vq->int_ops && vq->int_ops->vaddr)
+ vaddr = vq->int_ops->vaddr(vb);
+
+ if (vaddr)
+ videobuf_dma_contig_free(vq, &buf->vb);
+
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+inline int buffer_activate(struct viu_dev *dev, struct viu_buf *buf)
+{
+ struct viu_reg *vr = dev->vr;
+ int bpp;
+
+ /* setup the DMA base address */
+ reg_val.field_base_addr = videobuf_to_dma_contig(&buf->vb);
+
+ dprintk(1, "buffer_activate [%p/%d]: dma addr 0x%lx\n",
+ buf, buf->vb.i, (unsigned long)reg_val.field_base_addr);
+
+ /* interlace is on by default, set horizontal DMA increment */
+ reg_val.status_cfg = 0;
+ bpp = buf->fmt->depth >> 3;
+ switch (bpp) {
+ case 2:
+ reg_val.status_cfg &= ~MODE_32BIT;
+ reg_val.dma_inc = buf->vb.width * 2;
+ break;
+ case 4:
+ reg_val.status_cfg |= MODE_32BIT;
+ reg_val.dma_inc = buf->vb.width * 4;
+ break;
+ default:
+ dprintk(0, "doesn't support color depth(%d)\n",
+ bpp * 8);
+ return -EINVAL;
+ }
+
+ /* setup picture_count register */
+ reg_val.picture_count = (buf->vb.height / 2) << 16 |
+ buf->vb.width;
+
+ reg_val.status_cfg |= DMA_ACT | INT_DMA_END_EN | INT_FIELD_EN;
+
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ dev->capfield = buf->vb.field;
+
+ /* reset dma increment if needed */
+ if (!V4L2_FIELD_HAS_BOTH(buf->vb.field))
+ reg_val.dma_inc = 0;
+
+ out_be32(&vr->dma_inc, reg_val.dma_inc);
+ out_be32(&vr->picture_count, reg_val.picture_count);
+ out_be32(&vr->field_base_addr, reg_val.field_base_addr);
+ mod_timer(&dev->vidq.timeout, jiffies + BUFFER_TIMEOUT);
+ return 0;
+}
+
+static int buffer_prepare(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct viu_fh *fh = vq->priv_data;
+ struct viu_buf *buf = container_of(vb, struct viu_buf, vb);
+ int rc;
+
+ BUG_ON(fh->fmt == NULL);
+
+ if (fh->width < 48 || fh->width > norm_maxw() ||
+ fh->height < 32 || fh->height > norm_maxh())
+ return -EINVAL;
+ buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3;
+ if (buf->vb.baddr != 0 && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+
+ if (buf->fmt != fh->fmt ||
+ buf->vb.width != fh->width ||
+ buf->vb.height != fh->height ||
+ buf->vb.field != field) {
+ buf->fmt = fh->fmt;
+ buf->vb.width = fh->width;
+ buf->vb.height = fh->height;
+ buf->vb.field = field;
+ }
+
+ if (buf->vb.state == VIDEOBUF_NEEDS_INIT) {
+ rc = videobuf_iolock(vq, &buf->vb, NULL);
+ if (rc != 0)
+ goto fail;
+
+ buf->vb.width = fh->width;
+ buf->vb.height = fh->height;
+ buf->vb.field = field;
+ buf->fmt = fh->fmt;
+ }
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+fail:
+ free_buffer(vq, buf);
+ return rc;
+}
+
+static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct viu_buf *buf = container_of(vb, struct viu_buf, vb);
+ struct viu_fh *fh = vq->priv_data;
+ struct viu_dev *dev = fh->dev;
+ struct viu_dmaqueue *vidq = &dev->vidq;
+ struct viu_buf *prev;
+
+ if (!list_empty(&vidq->queued)) {
+ dprintk(1, "adding vb queue=0x%08lx\n",
+ (unsigned long)&buf->vb.queue);
+ dprintk(1, "vidq pointer 0x%p, queued 0x%p\n",
+ vidq, &vidq->queued);
+ dprintk(1, "dev %p, queued: self %p, next %p, head %p\n",
+ dev, &vidq->queued, vidq->queued.next,
+ vidq->queued.prev);
+ list_add_tail(&buf->vb.queue, &vidq->queued);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ dprintk(2, "[%p/%d] buffer_queue - append to queued\n",
+ buf, buf->vb.i);
+ } else if (list_empty(&vidq->active)) {
+ dprintk(1, "adding vb active=0x%08lx\n",
+ (unsigned long)&buf->vb.queue);
+ list_add_tail(&buf->vb.queue, &vidq->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT);
+ dprintk(2, "[%p/%d] buffer_queue - first active\n",
+ buf, buf->vb.i);
+
+ buffer_activate(dev, buf);
+ } else {
+ dprintk(1, "adding vb queue2=0x%08lx\n",
+ (unsigned long)&buf->vb.queue);
+ prev = list_entry(vidq->active.prev, struct viu_buf, vb.queue);
+ if (prev->vb.width == buf->vb.width &&
+ prev->vb.height == buf->vb.height &&
+ prev->fmt == buf->fmt) {
+ list_add_tail(&buf->vb.queue, &vidq->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ dprintk(2, "[%p/%d] buffer_queue - append to active\n",
+ buf, buf->vb.i);
+ } else {
+ list_add_tail(&buf->vb.queue, &vidq->queued);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ dprintk(2, "[%p/%d] buffer_queue - first queued\n",
+ buf, buf->vb.i);
+ }
+ }
+}
+
+static void buffer_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct viu_buf *buf = container_of(vb, struct viu_buf, vb);
+ struct viu_fh *fh = vq->priv_data;
+ struct viu_dev *dev = (struct viu_dev *)fh->dev;
+
+ viu_stop_dma(dev);
+ free_buffer(vq, buf);
+}
+
+static struct videobuf_queue_ops viu_video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/*
+ * IOCTL vidioc handling
+ */
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strcpy(cap->driver, "viu");
+ strcpy(cap->card, "viu");
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_STREAMING |
+ V4L2_CAP_VIDEO_OVERLAY |
+ V4L2_CAP_READWRITE;
+ return 0;
+}
+
+static int vidioc_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ int index = f->index;
+
+ if (f->index > NUM_FORMATS)
+ return -EINVAL;
+
+ strlcpy(f->description, formats[index].name, sizeof(f->description));
+ f->pixelformat = formats[index].fourcc;
+ return 0;
+}
+
+static int vidioc_g_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct viu_fh *fh = priv;
+
+ f->fmt.pix.width = fh->width;
+ f->fmt.pix.height = fh->height;
+ f->fmt.pix.field = fh->vb_vidq.field;
+ f->fmt.pix.pixelformat = fh->fmt->pixelformat;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fh->fmt->depth) >> 3;
+ f->fmt.pix.sizeimage = fh->sizeimage;
+ return 0;
+}
+
+static int vidioc_try_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct viu_fmt *fmt;
+ enum v4l2_field field;
+ unsigned int maxw, maxh;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (!fmt) {
+ dprintk(1, "Fourcc format (0x%08x) invalid.",
+ f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ field = f->fmt.pix.field;
+
+ if (field == V4L2_FIELD_ANY) {
+ field = V4L2_FIELD_INTERLACED;
+ } else if (field != V4L2_FIELD_INTERLACED) {
+ dprintk(1, "Field type invalid.\n");
+ return -EINVAL;
+ }
+
+ maxw = norm_maxw();
+ maxh = norm_maxh();
+
+ f->fmt.pix.field = field;
+ if (f->fmt.pix.height < 32)
+ f->fmt.pix.height = 32;
+ if (f->fmt.pix.height > maxh)
+ f->fmt.pix.height = maxh;
+ if (f->fmt.pix.width < 48)
+ f->fmt.pix.width = 48;
+ if (f->fmt.pix.width > maxw)
+ f->fmt.pix.width = maxw;
+ f->fmt.pix.width &= ~0x03;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fmt->depth) >> 3;
+
+ return 0;
+}
+
+static int vidioc_s_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct viu_fh *fh = priv;
+ int ret;
+
+ ret = vidioc_try_fmt_cap(file, fh, f);
+ if (ret < 0)
+ return ret;
+
+ fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ fh->width = f->fmt.pix.width;
+ fh->height = f->fmt.pix.height;
+ fh->sizeimage = f->fmt.pix.sizeimage;
+ fh->vb_vidq.field = f->fmt.pix.field;
+ fh->type = f->type;
+ dprintk(1, "set to pixelformat '%4.6s'\n", (char *)&fh->fmt->name);
+ return 0;
+}
+
+static int vidioc_g_fmt_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct viu_fh *fh = priv;
+
+ f->fmt.win = fh->win;
+ return 0;
+}
+
+static int verify_preview(struct viu_dev *dev, struct v4l2_window *win)
+{
+ enum v4l2_field field;
+ int maxw, maxh;
+
+ if (dev->ovbuf.base == NULL)
+ return -EINVAL;
+ if (dev->ovfmt == NULL)
+ return -EINVAL;
+ if (win->w.width < 48 || win->w.height < 32)
+ return -EINVAL;
+
+ field = win->field;
+ maxw = dev->crop_current.width;
+ maxh = dev->crop_current.height;
+
+ if (field == V4L2_FIELD_ANY) {
+ field = (win->w.height > maxh/2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_TOP;
+ }
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ win->field = field;
+ if (win->w.width > maxw)
+ win->w.width = maxw;
+ if (win->w.height > maxh)
+ win->w.height = maxh;
+ return 0;
+}
+
+inline void viu_activate_overlay(struct viu_reg *viu_reg)
+{
+ struct viu_reg *vr = viu_reg;
+
+ out_be32(&vr->field_base_addr, reg_val.field_base_addr);
+ out_be32(&vr->dma_inc, reg_val.dma_inc);
+ out_be32(&vr->picture_count, reg_val.picture_count);
+}
+
+static int viu_setup_preview(struct viu_dev *dev, struct viu_fh *fh)
+{
+ int bpp;
+
+ dprintk(1, "%s %dx%d %s\n", __func__,
+ fh->win.w.width, fh->win.w.height, dev->ovfmt->name);
+
+ reg_val.status_cfg = 0;
+
+ /* setup window */
+ reg_val.picture_count = (fh->win.w.height / 2) << 16 |
+ fh->win.w.width;
+
+ /* setup color depth and dma increment */
+ bpp = dev->ovfmt->depth / 8;
+ switch (bpp) {
+ case 2:
+ reg_val.status_cfg &= ~MODE_32BIT;
+ reg_val.dma_inc = fh->win.w.width * 2;
+ break;
+ case 4:
+ reg_val.status_cfg |= MODE_32BIT;
+ reg_val.dma_inc = fh->win.w.width * 4;
+ break;
+ default:
+ dprintk(0, "device doesn't support color depth(%d)\n",
+ bpp * 8);
+ return -EINVAL;
+ }
+
+ dev->ovfield = fh->win.field;
+ if (!V4L2_FIELD_HAS_BOTH(dev->ovfield))
+ reg_val.dma_inc = 0;
+
+ reg_val.status_cfg |= DMA_ACT | INT_DMA_END_EN | INT_FIELD_EN;
+
+ /* setup the base address of the overlay buffer */
+ reg_val.field_base_addr = (u32)dev->ovbuf.base;
+
+ return 0;
+}
+
+static int vidioc_s_fmt_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct viu_fh *fh = priv;
+ struct viu_dev *dev = (struct viu_dev *)fh->dev;
+ unsigned long flags;
+ int err;
+
+ err = verify_preview(dev, &f->fmt.win);
+ if (err)
+ return err;
+
+ fh->win = f->fmt.win;
+
+ spin_lock_irqsave(&dev->slock, flags);
+ viu_setup_preview(dev, fh);
+ spin_unlock_irqrestore(&dev->slock, flags);
+ return 0;
+}
+
+static int vidioc_try_fmt_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ return 0;
+}
+
+static int vidioc_overlay(struct file *file, void *priv, unsigned int on)
+{
+ struct viu_fh *fh = priv;
+ struct viu_dev *dev = (struct viu_dev *)fh->dev;
+ unsigned long flags;
+
+ if (on) {
+ spin_lock_irqsave(&dev->slock, flags);
+ viu_activate_overlay(dev->vr);
+ dev->ovenable = 1;
+
+ /* start dma */
+ viu_start_dma(dev);
+ spin_unlock_irqrestore(&dev->slock, flags);
+ } else {
+ viu_stop_dma(dev);
+ dev->ovenable = 0;
+ }
+
+ return 0;
+}
+
+int vidioc_g_fbuf(struct file *file, void *priv, struct v4l2_framebuffer *arg)
+{
+ struct viu_fh *fh = priv;
+ struct viu_dev *dev = fh->dev;
+ struct v4l2_framebuffer *fb = arg;
+
+ *fb = dev->ovbuf;
+ fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+ return 0;
+}
+
+int vidioc_s_fbuf(struct file *file, void *priv, struct v4l2_framebuffer *arg)
+{
+ struct viu_fh *fh = priv;
+ struct viu_dev *dev = fh->dev;
+ struct v4l2_framebuffer *fb = arg;
+ struct viu_fmt *fmt;
+
+ if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ /* check args */
+ fmt = format_by_fourcc(fb->fmt.pixelformat);
+ if (fmt == NULL)
+ return -EINVAL;
+
+ /* ok, accept it */
+ dev->ovbuf = *fb;
+ dev->ovfmt = fmt;
+ if (dev->ovbuf.fmt.bytesperline == 0) {
+ dev->ovbuf.fmt.bytesperline =
+ dev->ovbuf.fmt.width * fmt->depth / 8;
+ }
+ return 0;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct viu_fh *fh = priv;
+
+ return videobuf_reqbufs(&fh->vb_vidq, p);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct viu_fh *fh = priv;
+
+ return videobuf_querybuf(&fh->vb_vidq, p);
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct viu_fh *fh = priv;
+
+ return videobuf_qbuf(&fh->vb_vidq, p);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct viu_fh *fh = priv;
+
+ return videobuf_dqbuf(&fh->vb_vidq, p,
+ file->f_flags & O_NONBLOCK);
+}
+
+static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct viu_fh *fh = priv;
+ struct viu_dev *dev = fh->dev;
+
+ if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (fh->type != i)
+ return -EINVAL;
+
+ if (dev->ovenable)
+ dev->ovenable = 0;
+
+ viu_start_dma(fh->dev);
+
+ return videobuf_streamon(&fh->vb_vidq);
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct viu_fh *fh = priv;
+
+ if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (fh->type != i)
+ return -EINVAL;
+
+ viu_stop_dma(fh->dev);
+
+ return videobuf_streamoff(&fh->vb_vidq);
+}
+
+#define decoder_call(viu, o, f, args...) \
+ v4l2_subdev_call(viu->decoder, o, f, ##args)
+
+static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std_id)
+{
+ struct viu_fh *fh = priv;
+
+ decoder_call(fh->dev, video, querystd, std_id);
+ return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct viu_fh *fh = priv;
+
+ fh->dev->std = *id;
+ decoder_call(fh->dev, core, s_std, *id);
+ return 0;
+}
+
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std_id)
+{
+ struct viu_fh *fh = priv;
+
+ *std_id = fh->dev->std;
+ return 0;
+}
+
+/* only one input in this driver */
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *inp)
+{
+ struct viu_fh *fh = priv;
+
+ if (inp->index != 0)
+ return -EINVAL;
+
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ inp->std = fh->dev->vdev->tvnorms;
+ strcpy(inp->name, "Camera");
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct viu_fh *fh = priv;
+
+ if (i > 1)
+ return -EINVAL;
+
+ decoder_call(fh->dev, video, s_routing, i, 0, 0);
+ return 0;
+}
+
+/* Controls */
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) {
+ if (qc->id && qc->id == viu_qctrl[i].id) {
+ memcpy(qc, &(viu_qctrl[i]), sizeof(*qc));
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) {
+ if (ctrl->id == viu_qctrl[i].id) {
+ ctrl->value = qctl_regs[i];
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) {
+ if (ctrl->id == viu_qctrl[i].id) {
+ if (ctrl->value < viu_qctrl[i].minimum
+ || ctrl->value > viu_qctrl[i].maximum)
+ return -ERANGE;
+ qctl_regs[i] = ctrl->value;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+inline void viu_activate_next_buf(struct viu_dev *dev,
+ struct viu_dmaqueue *viuq)
+{
+ struct viu_dmaqueue *vidq = viuq;
+ struct viu_buf *buf;
+
+ /* launch another DMA operation for an active/queued buffer */
+ if (!list_empty(&vidq->active)) {
+ buf = list_entry(vidq->active.next, struct viu_buf,
+ vb.queue);
+ dprintk(1, "start another queued buffer: 0x%p\n", buf);
+ buffer_activate(dev, buf);
+ } else if (!list_empty(&vidq->queued)) {
+ buf = list_entry(vidq->queued.next, struct viu_buf,
+ vb.queue);
+ list_del(&buf->vb.queue);
+
+ dprintk(1, "start another queued buffer: 0x%p\n", buf);
+ list_add_tail(&buf->vb.queue, &vidq->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buffer_activate(dev, buf);
+ }
+}
+
+inline void viu_default_settings(struct viu_reg *viu_reg)
+{
+ struct viu_reg *vr = viu_reg;
+
+ out_be32(&vr->luminance, 0x9512A254);
+ out_be32(&vr->chroma_r, 0x03310000);
+ out_be32(&vr->chroma_g, 0x06600F38);
+ out_be32(&vr->chroma_b, 0x00000409);
+ out_be32(&vr->alpha, 0x000000ff);
+ out_be32(&vr->req_alarm, 0x00000090);
+ dprintk(1, "status reg: 0x%08x, field base: 0x%08x\n",
+ in_be32(&vr->status_cfg), in_be32(&vr->field_base_addr));
+}
+
+static void viu_overlay_intr(struct viu_dev *dev, u32 status)
+{
+ struct viu_reg *vr = dev->vr;
+
+ if (status & INT_DMA_END_STATUS)
+ dev->dma_done = 1;
+
+ if (status & INT_FIELD_STATUS) {
+ if (dev->dma_done) {
+ u32 addr = reg_val.field_base_addr;
+
+ dev->dma_done = 0;
+ if (status & FIELD_NO)
+ addr += reg_val.dma_inc;
+
+ out_be32(&vr->field_base_addr, addr);
+ out_be32(&vr->dma_inc, reg_val.dma_inc);
+ out_be32(&vr->status_cfg,
+ (status & 0xffc0ffff) |
+ (status & INT_ALL_STATUS) |
+ reg_val.status_cfg);
+ } else if (status & INT_VSYNC_STATUS) {
+ out_be32(&vr->status_cfg,
+ (status & 0xffc0ffff) |
+ (status & INT_ALL_STATUS) |
+ reg_val.status_cfg);
+ }
+ }
+}
+
+static void viu_capture_intr(struct viu_dev *dev, u32 status)
+{
+ struct viu_dmaqueue *vidq = &dev->vidq;
+ struct viu_reg *vr = dev->vr;
+ struct viu_buf *buf;
+ int field_num;
+ int need_two;
+ int dma_done = 0;
+
+ field_num = status & FIELD_NO;
+ need_two = V4L2_FIELD_HAS_BOTH(dev->capfield);
+
+ if (status & INT_DMA_END_STATUS) {
+ dma_done = 1;
+ if (((field_num == 0) && (dev->field == 0)) ||
+ (field_num && (dev->field == 1)))
+ dev->field++;
+ }
+
+ if (status & INT_FIELD_STATUS) {
+ dprintk(1, "irq: field %d, done %d\n",
+ !!field_num, dma_done);
+ if (unlikely(dev->first)) {
+ if (field_num == 0) {
+ dev->first = 0;
+ dprintk(1, "activate first buf\n");
+ viu_activate_next_buf(dev, vidq);
+ } else
+ dprintk(1, "wait field 0\n");
+ return;
+ }
+
+ /* setup buffer address for next dma operation */
+ if (!list_empty(&vidq->active)) {
+ u32 addr = reg_val.field_base_addr;
+
+ if (field_num && need_two) {
+ addr += reg_val.dma_inc;
+ dprintk(1, "field 1, 0x%lx, dev field %d\n",
+ (unsigned long)addr, dev->field);
+ }
+ out_be32(&vr->field_base_addr, addr);
+ out_be32(&vr->dma_inc, reg_val.dma_inc);
+ out_be32(&vr->status_cfg,
+ (status & 0xffc0ffff) |
+ (status & INT_ALL_STATUS) |
+ reg_val.status_cfg);
+ return;
+ }
+ }
+
+ if (dma_done && field_num && (dev->field == 2)) {
+ dev->field = 0;
+ buf = list_entry(vidq->active.next,
+ struct viu_buf, vb.queue);
+ dprintk(1, "viu/0: [%p/%d] 0x%lx/0x%lx: dma complete\n",
+ buf, buf->vb.i,
+ (unsigned long)videobuf_to_dma_contig(&buf->vb),
+ (unsigned long)in_be32(&vr->field_base_addr));
+
+ if (waitqueue_active(&buf->vb.done)) {
+ list_del(&buf->vb.queue);
+ do_gettimeofday(&buf->vb.ts);
+ buf->vb.state = VIDEOBUF_DONE;
+ buf->vb.field_count++;
+ wake_up(&buf->vb.done);
+ }
+ /* activate next dma buffer */
+ viu_activate_next_buf(dev, vidq);
+ }
+}
+
+static irqreturn_t viu_intr(int irq, void *dev_id)
+{
+ struct viu_dev *dev = (struct viu_dev *)dev_id;
+ struct viu_reg *vr = dev->vr;
+ u32 status;
+ u32 error;
+
+ status = in_be32(&vr->status_cfg);
+
+ if (status & INT_ERROR_STATUS) {
+ dev->irqs.error_irq++;
+ error = status & ERR_MASK;
+ if (error)
+ dprintk(1, "Err: error(%d), times:%d!\n",
+ error >> 4, dev->irqs.error_irq);
+ /* Clear interrupt error bit and error flags */
+ out_be32(&vr->status_cfg,
+ (status & 0xffc0ffff) | INT_ERROR_STATUS);
+ }
+
+ if (status & INT_DMA_END_STATUS) {
+ dev->irqs.dma_end_irq++;
+ dev->dma_done = 1;
+ dprintk(2, "VIU DMA end interrupt times: %d\n",
+ dev->irqs.dma_end_irq);
+ }
+
+ if (status & INT_HSYNC_STATUS)
+ dev->irqs.hsync_irq++;
+
+ if (status & INT_FIELD_STATUS) {
+ dev->irqs.field_irq++;
+ dprintk(2, "VIU field interrupt times: %d\n",
+ dev->irqs.field_irq);
+ }
+
+ if (status & INT_VSTART_STATUS)
+ dev->irqs.vstart_irq++;
+
+ if (status & INT_VSYNC_STATUS) {
+ dev->irqs.vsync_irq++;
+ dprintk(2, "VIU vsync interrupt times: %d\n",
+ dev->irqs.vsync_irq);
+ }
+
+ /* clear all pending irqs */
+ status = in_be32(&vr->status_cfg);
+ out_be32(&vr->status_cfg,
+ (status & 0xffc0ffff) | (status & INT_ALL_STATUS));
+
+ if (dev->ovenable) {
+ viu_overlay_intr(dev, status);
+ return IRQ_HANDLED;
+ }
+
+ /* Capture mode */
+ viu_capture_intr(dev, status);
+ return IRQ_HANDLED;
+}
+
+/*
+ * File operations for the device
+ */
+static int viu_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct viu_dev *dev = video_get_drvdata(vdev);
+ struct viu_fh *fh;
+ struct viu_reg *vr;
+ int minor = vdev->minor;
+ u32 status_cfg;
+ int i;
+
+ dprintk(1, "viu: open (minor=%d)\n", minor);
+
+ dev->users++;
+ if (dev->users > 1) {
+ dev->users--;
+ return -EBUSY;
+ }
+
+ vr = dev->vr;
+
+ dprintk(1, "open minor=%d type=%s users=%d\n", minor,
+ v4l2_type_names[V4L2_BUF_TYPE_VIDEO_CAPTURE], dev->users);
+
+ /* allocate and initialize per filehandle data */
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (!fh) {
+ dev->users--;
+ return -ENOMEM;
+ }
+
+ file->private_data = fh;
+ fh->dev = dev;
+
+ fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ fh->fmt = format_by_fourcc(V4L2_PIX_FMT_RGB32);
+ fh->width = norm_maxw();
+ fh->height = norm_maxh();
+ dev->crop_current.width = fh->width;
+ dev->crop_current.height = fh->height;
+
+ /* Put all controls at a sane state */
+ for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++)
+ qctl_regs[i] = viu_qctrl[i].default_value;
+
+ dprintk(1, "Open: fh=0x%08lx, dev=0x%08lx, dev->vidq=0x%08lx\n",
+ (unsigned long)fh, (unsigned long)dev,
+ (unsigned long)&dev->vidq);
+ dprintk(1, "Open: list_empty queued=%d\n",
+ list_empty(&dev->vidq.queued));
+ dprintk(1, "Open: list_empty active=%d\n",
+ list_empty(&dev->vidq.active));
+
+ viu_default_settings(vr);
+
+ status_cfg = in_be32(&vr->status_cfg);
+ out_be32(&vr->status_cfg,
+ status_cfg & ~(INT_VSYNC_EN | INT_HSYNC_EN |
+ INT_FIELD_EN | INT_VSTART_EN |
+ INT_DMA_END_EN | INT_ERROR_EN | INT_ECC_EN));
+
+ status_cfg = in_be32(&vr->status_cfg);
+ out_be32(&vr->status_cfg, status_cfg | INT_ALL_STATUS);
+
+ spin_lock_init(&fh->vbq_lock);
+ videobuf_queue_dma_contig_init(&fh->vb_vidq, &viu_video_qops,
+ dev->dev, &fh->vbq_lock,
+ fh->type, V4L2_FIELD_INTERLACED,
+ sizeof(struct viu_buf), fh,
+ &fh->dev->lock);
+ return 0;
+}
+
+static ssize_t viu_read(struct file *file, char __user *data, size_t count,
+ loff_t *ppos)
+{
+ struct viu_fh *fh = file->private_data;
+ struct viu_dev *dev = fh->dev;
+ int ret = 0;
+
+ dprintk(2, "%s\n", __func__);
+ if (dev->ovenable)
+ dev->ovenable = 0;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ viu_start_dma(dev);
+ ret = videobuf_read_stream(&fh->vb_vidq, data, count,
+ ppos, 0, file->f_flags & O_NONBLOCK);
+ return ret;
+ }
+ return 0;
+}
+
+static unsigned int viu_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct viu_fh *fh = file->private_data;
+ struct videobuf_queue *q = &fh->vb_vidq;
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type)
+ return POLLERR;
+
+ return videobuf_poll_stream(file, q, wait);
+}
+
+static int viu_release(struct file *file)
+{
+ struct viu_fh *fh = file->private_data;
+ struct viu_dev *dev = fh->dev;
+ int minor = video_devdata(file)->minor;
+
+ viu_stop_dma(dev);
+ videobuf_stop(&fh->vb_vidq);
+ videobuf_mmap_free(&fh->vb_vidq);
+
+ kfree(fh);
+
+ dev->users--;
+ dprintk(1, "close (minor=%d, users=%d)\n",
+ minor, dev->users);
+ return 0;
+}
+
+void viu_reset(struct viu_reg *reg)
+{
+ out_be32(&reg->status_cfg, 0);
+ out_be32(&reg->luminance, 0x9512a254);
+ out_be32(&reg->chroma_r, 0x03310000);
+ out_be32(&reg->chroma_g, 0x06600f38);
+ out_be32(&reg->chroma_b, 0x00000409);
+ out_be32(&reg->field_base_addr, 0);
+ out_be32(&reg->dma_inc, 0);
+ out_be32(&reg->picture_count, 0x01e002d0);
+ out_be32(&reg->req_alarm, 0x00000090);
+ out_be32(&reg->alpha, 0x000000ff);
+}
+
+static int viu_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct viu_fh *fh = file->private_data;
+ int ret;
+
+ dprintk(1, "mmap called, vma=0x%08lx\n", (unsigned long)vma);
+
+ ret = videobuf_mmap_mapper(&fh->vb_vidq, vma);
+
+ dprintk(1, "vma start=0x%08lx, size=%ld, ret=%d\n",
+ (unsigned long)vma->vm_start,
+ (unsigned long)vma->vm_end-(unsigned long)vma->vm_start,
+ ret);
+
+ return ret;
+}
+
+static struct v4l2_file_operations viu_fops = {
+ .owner = THIS_MODULE,
+ .open = viu_open,
+ .release = viu_release,
+ .read = viu_read,
+ .poll = viu_poll,
+ .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
+ .mmap = viu_mmap,
+};
+
+static const struct v4l2_ioctl_ops viu_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_cap,
+ .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt,
+ .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_overlay,
+ .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_overlay,
+ .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_overlay,
+ .vidioc_overlay = vidioc_overlay,
+ .vidioc_g_fbuf = vidioc_g_fbuf,
+ .vidioc_s_fbuf = vidioc_s_fbuf,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_querystd = vidioc_querystd,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+};
+
+static struct video_device viu_template = {
+ .name = "FSL viu",
+ .fops = &viu_fops,
+ .minor = -1,
+ .ioctl_ops = &viu_ioctl_ops,
+ .release = video_device_release,
+
+ .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL,
+ .current_norm = V4L2_STD_NTSC_M,
+};
+
+static int __devinit viu_of_probe(struct platform_device *op)
+{
+ struct viu_dev *viu_dev;
+ struct video_device *vdev;
+ struct resource r;
+ struct viu_reg __iomem *viu_regs;
+ struct i2c_adapter *ad;
+ int ret, viu_irq;
+
+ ret = of_address_to_resource(op->dev.of_node, 0, &r);
+ if (ret) {
+ dev_err(&op->dev, "Can't parse device node resource\n");
+ return -ENODEV;
+ }
+
+ viu_irq = irq_of_parse_and_map(op->dev.of_node, 0);
+ if (viu_irq == NO_IRQ) {
+ dev_err(&op->dev, "Error while mapping the irq\n");
+ return -EINVAL;
+ }
+
+ /* request mem region */
+ if (!devm_request_mem_region(&op->dev, r.start,
+ sizeof(struct viu_reg), DRV_NAME)) {
+ dev_err(&op->dev, "Error while requesting mem region\n");
+ ret = -EBUSY;
+ goto err;
+ }
+
+ /* remap registers */
+ viu_regs = devm_ioremap(&op->dev, r.start, sizeof(struct viu_reg));
+ if (!viu_regs) {
+ dev_err(&op->dev, "Can't map register set\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ /* Prepare our private structure */
+ viu_dev = devm_kzalloc(&op->dev, sizeof(struct viu_dev), GFP_ATOMIC);
+ if (!viu_dev) {
+ dev_err(&op->dev, "Can't allocate private structure\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ viu_dev->vr = viu_regs;
+ viu_dev->irq = viu_irq;
+ viu_dev->dev = &op->dev;
+
+ /* init video dma queues */
+ INIT_LIST_HEAD(&viu_dev->vidq.active);
+ INIT_LIST_HEAD(&viu_dev->vidq.queued);
+
+ snprintf(viu_dev->v4l2_dev.name,
+ sizeof(viu_dev->v4l2_dev.name), "%s", "VIU");
+ ret = v4l2_device_register(viu_dev->dev, &viu_dev->v4l2_dev);
+ if (ret < 0) {
+ dev_err(&op->dev, "v4l2_device_register() failed: %d\n", ret);
+ goto err;
+ }
+
+ ad = i2c_get_adapter(0);
+ viu_dev->decoder = v4l2_i2c_new_subdev(&viu_dev->v4l2_dev, ad,
+ "saa7113", VIU_VIDEO_DECODER_ADDR, NULL);
+
+ viu_dev->vidq.timeout.function = viu_vid_timeout;
+ viu_dev->vidq.timeout.data = (unsigned long)viu_dev;
+ init_timer(&viu_dev->vidq.timeout);
+ viu_dev->first = 1;
+
+ /* Allocate memory for video device */
+ vdev = video_device_alloc();
+ if (vdev == NULL) {
+ ret = -ENOMEM;
+ goto err_vdev;
+ }
+
+ memcpy(vdev, &viu_template, sizeof(viu_template));
+
+ vdev->v4l2_dev = &viu_dev->v4l2_dev;
+
+ viu_dev->vdev = vdev;
+
+ /* initialize locks */
+ mutex_init(&viu_dev->lock);
+ viu_dev->vdev->lock = &viu_dev->lock;
+ spin_lock_init(&viu_dev->slock);
+
+ video_set_drvdata(viu_dev->vdev, viu_dev);
+
+ mutex_lock(&viu_dev->lock);
+
+ ret = video_register_device(viu_dev->vdev, VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ video_device_release(viu_dev->vdev);
+ goto err_vdev;
+ }
+
+ /* enable VIU clock */
+ viu_dev->clk = clk_get(&op->dev, "viu_clk");
+ if (IS_ERR(viu_dev->clk)) {
+ dev_err(&op->dev, "failed to find the clock module!\n");
+ ret = -ENODEV;
+ goto err_clk;
+ } else {
+ clk_enable(viu_dev->clk);
+ }
+
+ /* reset VIU module */
+ viu_reset(viu_dev->vr);
+
+ /* install interrupt handler */
+ if (request_irq(viu_dev->irq, viu_intr, 0, "viu", (void *)viu_dev)) {
+ dev_err(&op->dev, "Request VIU IRQ failed.\n");
+ ret = -ENODEV;
+ goto err_irq;
+ }
+
+ mutex_unlock(&viu_dev->lock);
+
+ dev_info(&op->dev, "Freescale VIU Video Capture Board\n");
+ return ret;
+
+err_irq:
+ clk_disable(viu_dev->clk);
+ clk_put(viu_dev->clk);
+err_clk:
+ video_unregister_device(viu_dev->vdev);
+err_vdev:
+ mutex_unlock(&viu_dev->lock);
+ i2c_put_adapter(ad);
+ v4l2_device_unregister(&viu_dev->v4l2_dev);
+err:
+ irq_dispose_mapping(viu_irq);
+ return ret;
+}
+
+static int __devexit viu_of_remove(struct platform_device *op)
+{
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev);
+ struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev);
+ struct v4l2_subdev *sdev = list_entry(v4l2_dev->subdevs.next,
+ struct v4l2_subdev, list);
+ struct i2c_client *client = v4l2_get_subdevdata(sdev);
+
+ free_irq(dev->irq, (void *)dev);
+ irq_dispose_mapping(dev->irq);
+
+ clk_disable(dev->clk);
+ clk_put(dev->clk);
+
+ video_unregister_device(dev->vdev);
+ i2c_put_adapter(client->adapter);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int viu_suspend(struct platform_device *op, pm_message_t state)
+{
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev);
+ struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev);
+
+ clk_disable(dev->clk);
+ return 0;
+}
+
+static int viu_resume(struct platform_device *op)
+{
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev);
+ struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev);
+
+ clk_enable(dev->clk);
+ return 0;
+}
+#endif
+
+/*
+ * Initialization and module stuff
+ */
+static struct of_device_id mpc512x_viu_of_match[] = {
+ {
+ .compatible = "fsl,mpc5121-viu",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mpc512x_viu_of_match);
+
+static struct platform_driver viu_of_platform_driver = {
+ .probe = viu_of_probe,
+ .remove = __devexit_p(viu_of_remove),
+#ifdef CONFIG_PM
+ .suspend = viu_suspend,
+ .resume = viu_resume,
+#endif
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = mpc512x_viu_of_match,
+ },
+};
+
+module_platform_driver(viu_of_platform_driver);
+
+MODULE_DESCRIPTION("Freescale Video-In(VIU)");
+MODULE_AUTHOR("Hongjun Chen");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(VIU_VERSION);
diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig
new file mode 100644
index 00000000..dfe268bf
--- /dev/null
+++ b/drivers/media/video/gspca/Kconfig
@@ -0,0 +1,425 @@
+menuconfig USB_GSPCA
+ tristate "GSPCA based webcams"
+ depends on VIDEO_V4L2
+ default m
+ ---help---
+ Say Y here if you want to enable selecting webcams based
+ on the GSPCA framework.
+
+ See <file:Documentation/video4linux/gspca.txt> for more info.
+
+ This driver uses the Video For Linux API. You must say Y or M to
+ "Video For Linux" to use this driver.
+
+ To compile this driver as modules, choose M here: the
+ module will be called gspca_main.
+
+
+if USB_GSPCA && VIDEO_V4L2
+
+source "drivers/media/video/gspca/m5602/Kconfig"
+source "drivers/media/video/gspca/stv06xx/Kconfig"
+source "drivers/media/video/gspca/gl860/Kconfig"
+
+config USB_GSPCA_BENQ
+ tristate "Benq USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for the Benq DC E300 camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_benq.
+
+config USB_GSPCA_CONEX
+ tristate "Conexant Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the Conexant chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_conex.
+
+config USB_GSPCA_CPIA1
+ tristate "cpia CPiA (version 1) Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for USB cameras based on the cpia
+ CPiA chip. Note that you need atleast version 0.6.4 of libv4l for
+ applications to understand the videoformat generated by this driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_cpia1.
+
+config USB_GSPCA_ETOMS
+ tristate "Etoms USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the Etoms chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_etoms.
+
+config USB_GSPCA_FINEPIX
+ tristate "Fujifilm FinePix USB V4L2 driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the FinePix chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_finepix.
+
+config USB_GSPCA_JEILINJ
+ tristate "Jeilin JPEG USB V4L2 driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on this Jeilin chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_jeilinj.
+
+config USB_GSPCA_JL2005BCD
+ tristate "JL2005B/C/D USB V4L2 driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based the
+ JL2005B, JL2005C, or JL2005D chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_jl2005bcd.
+
+config USB_GSPCA_KINECT
+ tristate "Kinect sensor device USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for the Microsoft Kinect sensor device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_kinect.
+
+config USB_GSPCA_KONICA
+ tristate "Konica USB Camera V4L2 driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the Konica chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_konica.
+
+config USB_GSPCA_MARS
+ tristate "Mars USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the Mars chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_mars.
+
+config USB_GSPCA_MR97310A
+ tristate "Mars-Semi MR97310A USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the MR97310A chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_mr97310a.
+
+config USB_GSPCA_NW80X
+ tristate "Divio based (NW80x) USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the NW80x chips.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_nw80x.
+
+config USB_GSPCA_OV519
+ tristate "OV51x / OVFX2 / W996xCF USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on one of these:
+ OV511(+), OV518(+), OV519, OVFX2, W9967CF, W9968CF
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_ov519.
+
+config USB_GSPCA_OV534
+ tristate "OV534 OV772x USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the OV534 chip
+ and sensor OV772x (e.g. Sony Playstation EYE)
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_ov534.
+
+config USB_GSPCA_OV534_9
+ tristate "OV534 OV965x USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the OV534 chip
+ and sensor OV965x (e.g. Hercules Dualpix)
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_ov534_9.
+
+config USB_GSPCA_PAC207
+ tristate "Pixart PAC207 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the PAC207 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_pac207.
+
+config USB_GSPCA_PAC7302
+ tristate "Pixart PAC7302 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the PAC7302 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_pac7302.
+
+config USB_GSPCA_PAC7311
+ tristate "Pixart PAC7311 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the PAC7311 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_pac7311.
+
+config USB_GSPCA_SE401
+ tristate "SE401 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the
+ Endpoints (formerly known as AOX) se401 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_se401.
+
+config USB_GSPCA_SN9C2028
+ tristate "SONIX Dual-Mode USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want streaming support for Sonix SN9C2028 cameras.
+ These are supported as stillcams in libgphoto2/camlibs/sonix.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_sn9c2028.
+
+config USB_GSPCA_SN9C20X
+ tristate "SN9C20X USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the
+ sn9c20x chips (SN9C201 and SN9C202).
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_sn9c20x.
+
+config USB_GSPCA_SONIXB
+ tristate "SONIX Bayer USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the Sonix
+ chips with Bayer format (SN9C101, SN9C102 and SN9C103).
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_sonixb.
+
+config USB_GSPCA_SONIXJ
+ tristate "SONIX JPEG USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the Sonix
+ chips with JPEG format (SN9C102P, SN9C105 and >= SN9C110).
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_sonixj
+
+config USB_GSPCA_SPCA500
+ tristate "SPCA500 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SPCA500 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca500.
+
+config USB_GSPCA_SPCA501
+ tristate "SPCA501 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SPCA501 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca501.
+
+config USB_GSPCA_SPCA505
+ tristate "SPCA505 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SPCA505 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca505.
+
+config USB_GSPCA_SPCA506
+ tristate "SPCA506 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SPCA506 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca506.
+
+config USB_GSPCA_SPCA508
+ tristate "SPCA508 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SPCA508 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca508.
+
+config USB_GSPCA_SPCA561
+ tristate "SPCA561 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SPCA561 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca561.
+
+config USB_GSPCA_SPCA1528
+ tristate "SPCA1528 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SPCA1528 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca1528.
+
+config USB_GSPCA_SQ905
+ tristate "SQ Technologies SQ905 based USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SQ905 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_sq905.
+
+config USB_GSPCA_SQ905C
+ tristate "SQ Technologies SQ905C based USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SQ905C chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_sq905c.
+
+config USB_GSPCA_SQ930X
+ tristate "SQ Technologies SQ930X based USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SQ930X chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_sq930x.
+
+config USB_GSPCA_STK014
+ tristate "Syntek DV4000 (STK014) USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the STK014 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_stk014.
+
+config USB_GSPCA_STV0680
+ tristate "STV0680 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the STV0680 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_stv0680.
+
+config USB_GSPCA_SUNPLUS
+ tristate "SUNPLUS USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the Sunplus
+ SPCA504(abc) SPCA533 SPCA536 chips.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_sunplus.
+
+config USB_GSPCA_T613
+ tristate "T613 (JPEG Compliance) USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the T613 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_t613.
+
+config USB_GSPCA_TOPRO
+ tristate "TOPRO USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the
+ TP6800 and TP6810 Topro chips.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_topro.
+
+config USB_GSPCA_TV8532
+ tristate "TV8532 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the TV8531 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_tv8532.
+
+config USB_GSPCA_VC032X
+ tristate "VC032X USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the VC032X chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_vc032x.
+
+config USB_GSPCA_VICAM
+ tristate "ViCam USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for the 3com homeconnect camera
+ (vicam).
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_vicam.
+
+config USB_GSPCA_XIRLINK_CIT
+ tristate "Xirlink C-It USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for Xirlink C-It bases cameras.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_xirlink_cit.
+
+config USB_GSPCA_ZC3XX
+ tristate "ZC3XX USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the ZC3XX chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_zc3xx.
+
+endif
diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile
new file mode 100644
index 00000000..79ebe46e
--- /dev/null
+++ b/drivers/media/video/gspca/Makefile
@@ -0,0 +1,93 @@
+obj-$(CONFIG_USB_GSPCA) += gspca_main.o
+obj-$(CONFIG_USB_GSPCA_BENQ) += gspca_benq.o
+obj-$(CONFIG_USB_GSPCA_CONEX) += gspca_conex.o
+obj-$(CONFIG_USB_GSPCA_CPIA1) += gspca_cpia1.o
+obj-$(CONFIG_USB_GSPCA_ETOMS) += gspca_etoms.o
+obj-$(CONFIG_USB_GSPCA_FINEPIX) += gspca_finepix.o
+obj-$(CONFIG_USB_GSPCA_JEILINJ) += gspca_jeilinj.o
+obj-$(CONFIG_USB_GSPCA_JL2005BCD) += gspca_jl2005bcd.o
+obj-$(CONFIG_USB_GSPCA_KINECT) += gspca_kinect.o
+obj-$(CONFIG_USB_GSPCA_KONICA) += gspca_konica.o
+obj-$(CONFIG_USB_GSPCA_MARS) += gspca_mars.o
+obj-$(CONFIG_USB_GSPCA_MR97310A) += gspca_mr97310a.o
+obj-$(CONFIG_USB_GSPCA_NW80X) += gspca_nw80x.o
+obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o
+obj-$(CONFIG_USB_GSPCA_OV534) += gspca_ov534.o
+obj-$(CONFIG_USB_GSPCA_OV534_9) += gspca_ov534_9.o
+obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o
+obj-$(CONFIG_USB_GSPCA_PAC7302) += gspca_pac7302.o
+obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.o
+obj-$(CONFIG_USB_GSPCA_SE401) += gspca_se401.o
+obj-$(CONFIG_USB_GSPCA_SN9C2028) += gspca_sn9c2028.o
+obj-$(CONFIG_USB_GSPCA_SN9C20X) += gspca_sn9c20x.o
+obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o
+obj-$(CONFIG_USB_GSPCA_SONIXJ) += gspca_sonixj.o
+obj-$(CONFIG_USB_GSPCA_SPCA500) += gspca_spca500.o
+obj-$(CONFIG_USB_GSPCA_SPCA501) += gspca_spca501.o
+obj-$(CONFIG_USB_GSPCA_SPCA505) += gspca_spca505.o
+obj-$(CONFIG_USB_GSPCA_SPCA506) += gspca_spca506.o
+obj-$(CONFIG_USB_GSPCA_SPCA508) += gspca_spca508.o
+obj-$(CONFIG_USB_GSPCA_SPCA561) += gspca_spca561.o
+obj-$(CONFIG_USB_GSPCA_SPCA1528) += gspca_spca1528.o
+obj-$(CONFIG_USB_GSPCA_SQ905) += gspca_sq905.o
+obj-$(CONFIG_USB_GSPCA_SQ905C) += gspca_sq905c.o
+obj-$(CONFIG_USB_GSPCA_SQ930X) += gspca_sq930x.o
+obj-$(CONFIG_USB_GSPCA_SUNPLUS) += gspca_sunplus.o
+obj-$(CONFIG_USB_GSPCA_STK014) += gspca_stk014.o
+obj-$(CONFIG_USB_GSPCA_STV0680) += gspca_stv0680.o
+obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o
+obj-$(CONFIG_USB_GSPCA_TOPRO) += gspca_topro.o
+obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o
+obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o
+obj-$(CONFIG_USB_GSPCA_VICAM) += gspca_vicam.o
+obj-$(CONFIG_USB_GSPCA_XIRLINK_CIT) += gspca_xirlink_cit.o
+obj-$(CONFIG_USB_GSPCA_ZC3XX) += gspca_zc3xx.o
+
+gspca_main-objs := gspca.o
+gspca_benq-objs := benq.o
+gspca_conex-objs := conex.o
+gspca_cpia1-objs := cpia1.o
+gspca_etoms-objs := etoms.o
+gspca_finepix-objs := finepix.o
+gspca_jeilinj-objs := jeilinj.o
+gspca_jl2005bcd-objs := jl2005bcd.o
+gspca_kinect-objs := kinect.o
+gspca_konica-objs := konica.o
+gspca_mars-objs := mars.o
+gspca_mr97310a-objs := mr97310a.o
+gspca_nw80x-objs := nw80x.o
+gspca_ov519-objs := ov519.o
+gspca_ov534-objs := ov534.o
+gspca_ov534_9-objs := ov534_9.o
+gspca_pac207-objs := pac207.o
+gspca_pac7302-objs := pac7302.o
+gspca_pac7311-objs := pac7311.o
+gspca_se401-objs := se401.o
+gspca_sn9c2028-objs := sn9c2028.o
+gspca_sn9c20x-objs := sn9c20x.o
+gspca_sonixb-objs := sonixb.o
+gspca_sonixj-objs := sonixj.o
+gspca_spca500-objs := spca500.o
+gspca_spca501-objs := spca501.o
+gspca_spca505-objs := spca505.o
+gspca_spca506-objs := spca506.o
+gspca_spca508-objs := spca508.o
+gspca_spca561-objs := spca561.o
+gspca_spca1528-objs := spca1528.o
+gspca_sq905-objs := sq905.o
+gspca_sq905c-objs := sq905c.o
+gspca_sq930x-objs := sq930x.o
+gspca_stk014-objs := stk014.o
+gspca_stv0680-objs := stv0680.o
+gspca_sunplus-objs := sunplus.o
+gspca_t613-objs := t613.o
+gspca_topro-objs := topro.o
+gspca_tv8532-objs := tv8532.o
+gspca_vc032x-objs := vc032x.o
+gspca_vicam-objs := vicam.o
+gspca_xirlink_cit-objs := xirlink_cit.o
+gspca_zc3xx-objs := zc3xx.o
+
+obj-$(CONFIG_USB_M5602) += m5602/
+obj-$(CONFIG_USB_STV06XX) += stv06xx/
+obj-$(CONFIG_USB_GL860) += gl860/
diff --git a/drivers/media/video/gspca/autogain_functions.h b/drivers/media/video/gspca/autogain_functions.h
new file mode 100644
index 00000000..46777eee
--- /dev/null
+++ b/drivers/media/video/gspca/autogain_functions.h
@@ -0,0 +1,179 @@
+/*
+ * Functions for auto gain.
+ *
+ * Copyright (C) 2010-2011 Hans de Goede <hdegoede@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* auto gain and exposure algorithm based on the knee algorithm described here:
+ http://ytse.tricolour.net/docs/LowLightOptimization.html
+
+ Returns 0 if no changes were made, 1 if the gain and or exposure settings
+ where changed. */
+static inline int auto_gain_n_exposure(
+ struct gspca_dev *gspca_dev,
+ int avg_lum,
+ int desired_avg_lum,
+ int deadzone,
+ int gain_knee,
+ int exposure_knee)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, steps, gain, orig_gain, exposure, orig_exposure;
+ int retval = 0;
+
+ orig_gain = gain = sd->ctrls[GAIN].val;
+ orig_exposure = exposure = sd->ctrls[EXPOSURE].val;
+
+ /* If we are of a multiple of deadzone, do multiple steps to reach the
+ desired lumination fast (with the risc of a slight overshoot) */
+ steps = abs(desired_avg_lum - avg_lum) / deadzone;
+
+ PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d",
+ avg_lum, desired_avg_lum, steps);
+
+ for (i = 0; i < steps; i++) {
+ if (avg_lum > desired_avg_lum) {
+ if (gain > gain_knee)
+ gain--;
+ else if (exposure > exposure_knee)
+ exposure--;
+ else if (gain > sd->ctrls[GAIN].def)
+ gain--;
+ else if (exposure > sd->ctrls[EXPOSURE].min)
+ exposure--;
+ else if (gain > sd->ctrls[GAIN].min)
+ gain--;
+ else
+ break;
+ } else {
+ if (gain < sd->ctrls[GAIN].def)
+ gain++;
+ else if (exposure < exposure_knee)
+ exposure++;
+ else if (gain < gain_knee)
+ gain++;
+ else if (exposure < sd->ctrls[EXPOSURE].max)
+ exposure++;
+ else if (gain < sd->ctrls[GAIN].max)
+ gain++;
+ else
+ break;
+ }
+ }
+
+ if (gain != orig_gain) {
+ sd->ctrls[GAIN].val = gain;
+ setgain(gspca_dev);
+ retval = 1;
+ }
+ if (exposure != orig_exposure) {
+ sd->ctrls[EXPOSURE].val = exposure;
+ setexposure(gspca_dev);
+ retval = 1;
+ }
+
+ if (retval)
+ PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d",
+ gain, exposure);
+ return retval;
+}
+
+/* Autogain + exposure algorithm for cameras with a coarse exposure control
+ (usually this means we can only control the clockdiv to change exposure)
+ As changing the clockdiv so that the fps drops from 30 to 15 fps for
+ example, will lead to a huge exposure change (it effectively doubles),
+ this algorithm normally tries to only adjust the gain (between 40 and
+ 80 %) and if that does not help, only then changes exposure. This leads
+ to a much more stable image then using the knee algorithm which at
+ certain points of the knee graph will only try to adjust exposure,
+ which leads to oscilating as one exposure step is huge.
+
+ Note this assumes that the sd struct for the cam in question has
+ exp_too_high_cnt and exp_too_high_cnt int members for use by this function.
+
+ Returns 0 if no changes were made, 1 if the gain and or exposure settings
+ where changed. */
+static inline int coarse_grained_expo_autogain(
+ struct gspca_dev *gspca_dev,
+ int avg_lum,
+ int desired_avg_lum,
+ int deadzone)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int steps, gain, orig_gain, exposure, orig_exposure;
+ int gain_low, gain_high;
+ int retval = 0;
+
+ orig_gain = gain = sd->ctrls[GAIN].val;
+ orig_exposure = exposure = sd->ctrls[EXPOSURE].val;
+
+ gain_low = (sd->ctrls[GAIN].max - sd->ctrls[GAIN].min) / 5 * 2;
+ gain_low += sd->ctrls[GAIN].min;
+ gain_high = (sd->ctrls[GAIN].max - sd->ctrls[GAIN].min) / 5 * 4;
+ gain_high += sd->ctrls[GAIN].min;
+
+ /* If we are of a multiple of deadzone, do multiple steps to reach the
+ desired lumination fast (with the risc of a slight overshoot) */
+ steps = (desired_avg_lum - avg_lum) / deadzone;
+
+ PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d",
+ avg_lum, desired_avg_lum, steps);
+
+ if ((gain + steps) > gain_high &&
+ exposure < sd->ctrls[EXPOSURE].max) {
+ gain = gain_high;
+ sd->exp_too_low_cnt++;
+ sd->exp_too_high_cnt = 0;
+ } else if ((gain + steps) < gain_low &&
+ exposure > sd->ctrls[EXPOSURE].min) {
+ gain = gain_low;
+ sd->exp_too_high_cnt++;
+ sd->exp_too_low_cnt = 0;
+ } else {
+ gain += steps;
+ if (gain > sd->ctrls[GAIN].max)
+ gain = sd->ctrls[GAIN].max;
+ else if (gain < sd->ctrls[GAIN].min)
+ gain = sd->ctrls[GAIN].min;
+ sd->exp_too_high_cnt = 0;
+ sd->exp_too_low_cnt = 0;
+ }
+
+ if (sd->exp_too_high_cnt > 3) {
+ exposure--;
+ sd->exp_too_high_cnt = 0;
+ } else if (sd->exp_too_low_cnt > 3) {
+ exposure++;
+ sd->exp_too_low_cnt = 0;
+ }
+
+ if (gain != orig_gain) {
+ sd->ctrls[GAIN].val = gain;
+ setgain(gspca_dev);
+ retval = 1;
+ }
+ if (exposure != orig_exposure) {
+ sd->ctrls[EXPOSURE].val = exposure;
+ setexposure(gspca_dev);
+ retval = 1;
+ }
+
+ if (retval)
+ PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d",
+ gain, exposure);
+ return retval;
+}
diff --git a/drivers/media/video/gspca/benq.c b/drivers/media/video/gspca/benq.c
new file mode 100644
index 00000000..9769f179
--- /dev/null
+++ b/drivers/media/video/gspca/benq.c
@@ -0,0 +1,294 @@
+/*
+ * Benq DC E300 subdriver
+ *
+ * Copyright (C) 2009 Jean-Francois Moine (http://moinejf.free.fr)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "benq"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
+MODULE_DESCRIPTION("Benq DC E300 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+};
+
+/* V4L2 controls supported by the driver */
+static const struct ctrl sd_ctrls[] = {
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+};
+
+static void sd_isoc_irq(struct urb *urb);
+
+/* -- write a register -- */
+static void reg_w(struct gspca_dev *gspca_dev,
+ u16 value, u16 index)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x02,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value,
+ index,
+ NULL,
+ 0,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_w err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ gspca_dev->cam.cam_mode = vga_mode;
+ gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
+ gspca_dev->cam.no_urb_create = 1;
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ return 0;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct urb *urb;
+ int i, n;
+
+ /* create 4 URBs - 2 on endpoint 0x83 and 2 on 0x082 */
+#if MAX_NURBS < 4
+#error "Not enough URBs in the gspca table"
+#endif
+#define SD_PKT_SZ 64
+#define SD_NPKT 32
+ for (n = 0; n < 4; n++) {
+ urb = usb_alloc_urb(SD_NPKT, GFP_KERNEL);
+ if (!urb) {
+ pr_err("usb_alloc_urb failed\n");
+ return -ENOMEM;
+ }
+ gspca_dev->urb[n] = urb;
+ urb->transfer_buffer = usb_alloc_coherent(gspca_dev->dev,
+ SD_PKT_SZ * SD_NPKT,
+ GFP_KERNEL,
+ &urb->transfer_dma);
+
+ if (urb->transfer_buffer == NULL) {
+ pr_err("usb_alloc_coherent failed\n");
+ return -ENOMEM;
+ }
+ urb->dev = gspca_dev->dev;
+ urb->context = gspca_dev;
+ urb->transfer_buffer_length = SD_PKT_SZ * SD_NPKT;
+ urb->pipe = usb_rcvisocpipe(gspca_dev->dev,
+ n & 1 ? 0x82 : 0x83);
+ urb->transfer_flags = URB_ISO_ASAP
+ | URB_NO_TRANSFER_DMA_MAP;
+ urb->interval = 1;
+ urb->complete = sd_isoc_irq;
+ urb->number_of_packets = SD_NPKT;
+ for (i = 0; i < SD_NPKT; i++) {
+ urb->iso_frame_desc[i].length = SD_PKT_SZ;
+ urb->iso_frame_desc[i].offset = SD_PKT_SZ * i;
+ }
+ }
+
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct usb_interface *intf;
+
+ reg_w(gspca_dev, 0x003c, 0x0003);
+ reg_w(gspca_dev, 0x003c, 0x0004);
+ reg_w(gspca_dev, 0x003c, 0x0005);
+ reg_w(gspca_dev, 0x003c, 0x0006);
+ reg_w(gspca_dev, 0x003c, 0x0007);
+
+ intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
+ usb_set_interface(gspca_dev->dev, gspca_dev->iface,
+ intf->num_altsetting - 1);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ /* unused */
+}
+
+/* reception of an URB */
+static void sd_isoc_irq(struct urb *urb)
+{
+ struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
+ struct urb *urb0;
+ u8 *data;
+ int i, st;
+
+ PDEBUG(D_PACK, "sd isoc irq");
+ if (!gspca_dev->streaming)
+ return;
+ if (urb->status != 0) {
+ if (urb->status == -ESHUTDOWN)
+ return; /* disconnection */
+#ifdef CONFIG_PM
+ if (gspca_dev->frozen)
+ return;
+#endif
+ pr_err("urb status: %d\n", urb->status);
+ return;
+ }
+
+ /* if this is a control URN (ep 0x83), wait */
+ if (urb == gspca_dev->urb[0] || urb == gspca_dev->urb[2])
+ return;
+
+ /* scan both received URBs */
+ if (urb == gspca_dev->urb[1])
+ urb0 = gspca_dev->urb[0];
+ else
+ urb0 = gspca_dev->urb[2];
+ for (i = 0; i < urb->number_of_packets; i++) {
+
+ /* check the packet status and length */
+ if (urb0->iso_frame_desc[i].actual_length != SD_PKT_SZ
+ || urb->iso_frame_desc[i].actual_length != SD_PKT_SZ) {
+ PDEBUG(D_ERR, "ISOC bad lengths %d / %d",
+ urb0->iso_frame_desc[i].actual_length,
+ urb->iso_frame_desc[i].actual_length);
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ continue;
+ }
+ st = urb0->iso_frame_desc[i].status;
+ if (st == 0)
+ st = urb->iso_frame_desc[i].status;
+ if (st) {
+ pr_err("ISOC data error: [%d] status=%d\n",
+ i, st);
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ continue;
+ }
+
+ /*
+ * The images are received in URBs of different endpoints
+ * (0x83 and 0x82).
+ * Image pieces in URBs of ep 0x83 are continuated in URBs of
+ * ep 0x82 of the same index.
+ * The packets in the URBs of endpoint 0x83 start with:
+ * - 80 ba/bb 00 00 = start of image followed by 'ff d8'
+ * - 04 ba/bb oo oo = image piece
+ * where 'oo oo' is the image offset
+ (not cheked)
+ * - (other -> bad frame)
+ * The images are JPEG encoded with full header and
+ * normal ff escape.
+ * The end of image ('ff d9') may occur in any URB.
+ * (not cheked)
+ */
+ data = (u8 *) urb0->transfer_buffer
+ + urb0->iso_frame_desc[i].offset;
+ if (data[0] == 0x80 && (data[1] & 0xfe) == 0xba) {
+
+ /* new image */
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ NULL, 0);
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ data + 4, SD_PKT_SZ - 4);
+ } else if (data[0] == 0x04 && (data[1] & 0xfe) == 0xba) {
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data + 4, SD_PKT_SZ - 4);
+ } else {
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ continue;
+ }
+ data = (u8 *) urb->transfer_buffer
+ + urb->iso_frame_desc[i].offset;
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data, SD_PKT_SZ);
+ }
+
+ /* resubmit the URBs */
+ st = usb_submit_urb(urb0, GFP_ATOMIC);
+ if (st < 0)
+ pr_err("usb_submit_urb(0) ret %d\n", st);
+ st = usb_submit_urb(urb, GFP_ATOMIC);
+ if (st < 0)
+ pr_err("usb_submit_urb() ret %d\n", st);
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x04a5, 0x3035)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c
new file mode 100644
index 00000000..ea17b5d9
--- /dev/null
+++ b/drivers/media/video/gspca/conex.c
@@ -0,0 +1,1070 @@
+/*
+ * Connexant Cx11646 library
+ * Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "conex"
+
+#include "gspca.h"
+#define CONEX_CAM 1 /* special JPEG header */
+#include "jpeg.h"
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("GSPCA USB Conexant Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ unsigned char brightness;
+ unsigned char contrast;
+ unsigned char colors;
+ u8 quality;
+#define QUALITY_MIN 30
+#define QUALITY_MAX 60
+#define QUALITY_DEF 40
+
+ u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+
+static const struct ctrl sd_ctrls[] = {
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+#define BRIGHTNESS_DEF 0xd4
+ .default_value = BRIGHTNESS_DEF,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+ {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0x0a,
+ .maximum = 0x1f,
+ .step = 1,
+#define CONTRAST_DEF 0x0c
+ .default_value = CONTRAST_DEF,
+ },
+ .set = sd_setcontrast,
+ .get = sd_getcontrast,
+ },
+ {
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Color",
+ .minimum = 0,
+ .maximum = 7,
+ .step = 1,
+#define COLOR_DEF 3
+ .default_value = COLOR_DEF,
+ },
+ .set = sd_setcolors,
+ .get = sd_getcolors,
+ },
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 3},
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 2},
+ {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
+/* the read bytes are found in gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev,
+ __u16 index,
+ __u16 len)
+{
+ struct usb_device *dev = gspca_dev->dev;
+
+#ifdef GSPCA_DEBUG
+ if (len > USB_BUF_SZ) {
+ pr_err("reg_r: buffer overflow\n");
+ return;
+ }
+#endif
+ usb_control_msg(dev,
+ usb_rcvctrlpipe(dev, 0),
+ 0,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0,
+ index, gspca_dev->usb_buf, len,
+ 500);
+ PDEBUG(D_USBI, "reg read [%02x] -> %02x ..",
+ index, gspca_dev->usb_buf[0]);
+}
+
+/* the bytes to write are in gspca_dev->usb_buf */
+static void reg_w_val(struct gspca_dev *gspca_dev,
+ __u16 index,
+ __u8 val)
+{
+ struct usb_device *dev = gspca_dev->dev;
+
+ gspca_dev->usb_buf[0] = val;
+ usb_control_msg(dev,
+ usb_sndctrlpipe(dev, 0),
+ 0,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0,
+ index, gspca_dev->usb_buf, 1, 500);
+}
+
+static void reg_w(struct gspca_dev *gspca_dev,
+ __u16 index,
+ const __u8 *buffer,
+ __u16 len)
+{
+ struct usb_device *dev = gspca_dev->dev;
+
+#ifdef GSPCA_DEBUG
+ if (len > USB_BUF_SZ) {
+ pr_err("reg_w: buffer overflow\n");
+ return;
+ }
+ PDEBUG(D_USBO, "reg write [%02x] = %02x..", index, *buffer);
+#endif
+ memcpy(gspca_dev->usb_buf, buffer, len);
+ usb_control_msg(dev,
+ usb_sndctrlpipe(dev, 0),
+ 0,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0,
+ index, gspca_dev->usb_buf, len, 500);
+}
+
+static const __u8 cx_sensor_init[][4] = {
+ {0x88, 0x11, 0x01, 0x01},
+ {0x88, 0x12, 0x70, 0x01},
+ {0x88, 0x0f, 0x00, 0x01},
+ {0x88, 0x05, 0x01, 0x01},
+ {}
+};
+
+static const __u8 cx11646_fw1[][3] = {
+ {0x00, 0x02, 0x00},
+ {0x01, 0x43, 0x00},
+ {0x02, 0xA7, 0x00},
+ {0x03, 0x8B, 0x01},
+ {0x04, 0xE9, 0x02},
+ {0x05, 0x08, 0x04},
+ {0x06, 0x08, 0x05},
+ {0x07, 0x07, 0x06},
+ {0x08, 0xE7, 0x06},
+ {0x09, 0xC6, 0x07},
+ {0x0A, 0x86, 0x08},
+ {0x0B, 0x46, 0x09},
+ {0x0C, 0x05, 0x0A},
+ {0x0D, 0xA5, 0x0A},
+ {0x0E, 0x45, 0x0B},
+ {0x0F, 0xE5, 0x0B},
+ {0x10, 0x85, 0x0C},
+ {0x11, 0x25, 0x0D},
+ {0x12, 0xC4, 0x0D},
+ {0x13, 0x45, 0x0E},
+ {0x14, 0xE4, 0x0E},
+ {0x15, 0x64, 0x0F},
+ {0x16, 0xE4, 0x0F},
+ {0x17, 0x64, 0x10},
+ {0x18, 0xE4, 0x10},
+ {0x19, 0x64, 0x11},
+ {0x1A, 0xE4, 0x11},
+ {0x1B, 0x64, 0x12},
+ {0x1C, 0xE3, 0x12},
+ {0x1D, 0x44, 0x13},
+ {0x1E, 0xC3, 0x13},
+ {0x1F, 0x24, 0x14},
+ {0x20, 0xA3, 0x14},
+ {0x21, 0x04, 0x15},
+ {0x22, 0x83, 0x15},
+ {0x23, 0xE3, 0x15},
+ {0x24, 0x43, 0x16},
+ {0x25, 0xA4, 0x16},
+ {0x26, 0x23, 0x17},
+ {0x27, 0x83, 0x17},
+ {0x28, 0xE3, 0x17},
+ {0x29, 0x43, 0x18},
+ {0x2A, 0xA3, 0x18},
+ {0x2B, 0x03, 0x19},
+ {0x2C, 0x63, 0x19},
+ {0x2D, 0xC3, 0x19},
+ {0x2E, 0x22, 0x1A},
+ {0x2F, 0x63, 0x1A},
+ {0x30, 0xC3, 0x1A},
+ {0x31, 0x23, 0x1B},
+ {0x32, 0x83, 0x1B},
+ {0x33, 0xE2, 0x1B},
+ {0x34, 0x23, 0x1C},
+ {0x35, 0x83, 0x1C},
+ {0x36, 0xE2, 0x1C},
+ {0x37, 0x23, 0x1D},
+ {0x38, 0x83, 0x1D},
+ {0x39, 0xE2, 0x1D},
+ {0x3A, 0x23, 0x1E},
+ {0x3B, 0x82, 0x1E},
+ {0x3C, 0xC3, 0x1E},
+ {0x3D, 0x22, 0x1F},
+ {0x3E, 0x63, 0x1F},
+ {0x3F, 0xC1, 0x1F},
+ {}
+};
+static void cx11646_fw(struct gspca_dev*gspca_dev)
+{
+ int i = 0;
+
+ reg_w_val(gspca_dev, 0x006a, 0x02);
+ while (cx11646_fw1[i][1]) {
+ reg_w(gspca_dev, 0x006b, cx11646_fw1[i], 3);
+ i++;
+ }
+ reg_w_val(gspca_dev, 0x006a, 0x00);
+}
+
+static const __u8 cxsensor[] = {
+ 0x88, 0x12, 0x70, 0x01,
+ 0x88, 0x0d, 0x02, 0x01,
+ 0x88, 0x0f, 0x00, 0x01,
+ 0x88, 0x03, 0x71, 0x01, 0x88, 0x04, 0x00, 0x01, /* 3 */
+ 0x88, 0x02, 0x10, 0x01,
+ 0x88, 0x00, 0xD4, 0x01, 0x88, 0x01, 0x01, 0x01, /* 5 */
+ 0x88, 0x0B, 0x00, 0x01,
+ 0x88, 0x0A, 0x0A, 0x01,
+ 0x88, 0x00, 0x08, 0x01, 0x88, 0x01, 0x00, 0x01, /* 8 */
+ 0x88, 0x05, 0x01, 0x01,
+ 0xA1, 0x18, 0x00, 0x01,
+ 0x00
+};
+
+static const __u8 reg20[] = { 0x10, 0x42, 0x81, 0x19, 0xd3, 0xff, 0xa7, 0xff };
+static const __u8 reg28[] = { 0x87, 0x00, 0x87, 0x00, 0x8f, 0xff, 0xea, 0xff };
+static const __u8 reg10[] = { 0xb1, 0xb1 };
+static const __u8 reg71a[] = { 0x08, 0x18, 0x0a, 0x1e }; /* 640 */
+static const __u8 reg71b[] = { 0x04, 0x0c, 0x05, 0x0f };
+ /* 352{0x04,0x0a,0x06,0x12}; //352{0x05,0x0e,0x06,0x11}; //352 */
+static const __u8 reg71c[] = { 0x02, 0x07, 0x03, 0x09 };
+ /* 320{0x04,0x0c,0x05,0x0f}; //320 */
+static const __u8 reg71d[] = { 0x02, 0x07, 0x03, 0x09 }; /* 176 */
+static const __u8 reg7b[] = { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff };
+
+static void cx_sensor(struct gspca_dev*gspca_dev)
+{
+ int i = 0;
+ int length;
+ const __u8 *ptsensor = cxsensor;
+
+ reg_w(gspca_dev, 0x0020, reg20, 8);
+ reg_w(gspca_dev, 0x0028, reg28, 8);
+ reg_w(gspca_dev, 0x0010, reg10, 8);
+ reg_w_val(gspca_dev, 0x0092, 0x03);
+
+ switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
+ case 0:
+ reg_w(gspca_dev, 0x0071, reg71a, 4);
+ break;
+ case 1:
+ reg_w(gspca_dev, 0x0071, reg71b, 4);
+ break;
+ default:
+/* case 2: */
+ reg_w(gspca_dev, 0x0071, reg71c, 4);
+ break;
+ case 3:
+ reg_w(gspca_dev, 0x0071, reg71d, 4);
+ break;
+ }
+ reg_w(gspca_dev, 0x007b, reg7b, 6);
+ reg_w_val(gspca_dev, 0x00f8, 0x00);
+ reg_w(gspca_dev, 0x0010, reg10, 8);
+ reg_w_val(gspca_dev, 0x0098, 0x41);
+ for (i = 0; i < 11; i++) {
+ if (i == 3 || i == 5 || i == 8)
+ length = 8;
+ else
+ length = 4;
+ reg_w(gspca_dev, 0x00e5, ptsensor, length);
+ if (length == 4)
+ reg_r(gspca_dev, 0x00e8, 1);
+ else
+ reg_r(gspca_dev, 0x00e8, length);
+ ptsensor += length;
+ }
+ reg_r(gspca_dev, 0x00e7, 8);
+}
+
+static const __u8 cx_inits_176[] = {
+ 0x33, 0x81, 0xB0, 0x00, 0x90, 0x00, 0x0A, 0x03, /* 176x144 */
+ 0x00, 0x03, 0x03, 0x03, 0x1B, 0x05, 0x30, 0x03,
+ 0x65, 0x15, 0x18, 0x25, 0x03, 0x25, 0x08, 0x30,
+ 0x3B, 0x25, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0xDC, 0xFF, 0xEE, 0xFF, 0xC5, 0xFF, 0xBF, 0xFF,
+ 0xF7, 0xFF, 0x88, 0xFF, 0x66, 0x02, 0x28, 0x02,
+ 0x1E, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+static const __u8 cx_inits_320[] = {
+ 0x7f, 0x7f, 0x40, 0x01, 0xf0, 0x00, 0x02, 0x01,
+ 0x00, 0x01, 0x01, 0x01, 0x10, 0x00, 0x02, 0x01,
+ 0x65, 0x45, 0xfa, 0x4c, 0x2c, 0xdf, 0xb9, 0x81,
+ 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0xe2, 0xff, 0xf1, 0xff, 0xc2, 0xff, 0xbc, 0xff,
+ 0xf5, 0xff, 0x6d, 0xff, 0xf6, 0x01, 0x43, 0x02,
+ 0xd3, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+static const __u8 cx_inits_352[] = {
+ 0x2e, 0x7c, 0x60, 0x01, 0x20, 0x01, 0x05, 0x03,
+ 0x00, 0x06, 0x03, 0x06, 0x1b, 0x10, 0x05, 0x3b,
+ 0x30, 0x25, 0x18, 0x25, 0x08, 0x30, 0x03, 0x25,
+ 0x3b, 0x30, 0x25, 0x1b, 0x10, 0x05, 0x00, 0x00,
+ 0xe3, 0xff, 0xf1, 0xff, 0xc2, 0xff, 0xbc, 0xff,
+ 0xf5, 0xff, 0x6b, 0xff, 0xee, 0x01, 0x43, 0x02,
+ 0xe4, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+static const __u8 cx_inits_640[] = {
+ 0x7e, 0x7e, 0x80, 0x02, 0xe0, 0x01, 0x01, 0x01,
+ 0x00, 0x02, 0x01, 0x02, 0x10, 0x30, 0x01, 0x01,
+ 0x65, 0x45, 0xf7, 0x52, 0x2c, 0xdf, 0xb9, 0x81,
+ 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0xe2, 0xff, 0xf1, 0xff, 0xc2, 0xff, 0xbc, 0xff,
+ 0xf6, 0xff, 0x7b, 0xff, 0x01, 0x02, 0x43, 0x02,
+ 0x77, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static void cx11646_initsize(struct gspca_dev *gspca_dev)
+{
+ const __u8 *cxinit;
+ static const __u8 reg12[] = { 0x08, 0x05, 0x07, 0x04, 0x24 };
+ static const __u8 reg17[] =
+ { 0x0a, 0x00, 0xf2, 0x01, 0x0f, 0x00, 0x97, 0x02 };
+
+ switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
+ case 0:
+ cxinit = cx_inits_640;
+ break;
+ case 1:
+ cxinit = cx_inits_352;
+ break;
+ default:
+/* case 2: */
+ cxinit = cx_inits_320;
+ break;
+ case 3:
+ cxinit = cx_inits_176;
+ break;
+ }
+ reg_w_val(gspca_dev, 0x009a, 0x01);
+ reg_w_val(gspca_dev, 0x0010, 0x10);
+ reg_w(gspca_dev, 0x0012, reg12, 5);
+ reg_w(gspca_dev, 0x0017, reg17, 8);
+ reg_w_val(gspca_dev, 0x00c0, 0x00);
+ reg_w_val(gspca_dev, 0x00c1, 0x04);
+ reg_w_val(gspca_dev, 0x00c2, 0x04);
+
+ reg_w(gspca_dev, 0x0061, cxinit, 8);
+ cxinit += 8;
+ reg_w(gspca_dev, 0x00ca, cxinit, 8);
+ cxinit += 8;
+ reg_w(gspca_dev, 0x00d2, cxinit, 8);
+ cxinit += 8;
+ reg_w(gspca_dev, 0x00da, cxinit, 6);
+ cxinit += 8;
+ reg_w(gspca_dev, 0x0041, cxinit, 8);
+ cxinit += 8;
+ reg_w(gspca_dev, 0x0049, cxinit, 8);
+ cxinit += 8;
+ reg_w(gspca_dev, 0x0051, cxinit, 2);
+
+ reg_r(gspca_dev, 0x0010, 1);
+}
+
+static const __u8 cx_jpeg_init[][8] = {
+ {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x15}, /* 1 */
+ {0x0f, 0x10, 0x12, 0x10, 0x0d, 0x15, 0x12, 0x11},
+ {0x12, 0x18, 0x16, 0x15, 0x19, 0x20, 0x35, 0x22},
+ {0x20, 0x1d, 0x1d, 0x20, 0x41, 0x2e, 0x31, 0x26},
+ {0x35, 0x4d, 0x43, 0x51, 0x4f, 0x4b, 0x43, 0x4a},
+ {0x49, 0x55, 0x5F, 0x79, 0x67, 0x55, 0x5A, 0x73},
+ {0x5B, 0x49, 0x4A, 0x6A, 0x90, 0x6B, 0x73, 0x7D},
+ {0x81, 0x88, 0x89, 0x88, 0x52, 0x66, 0x95, 0xA0},
+ {0x94, 0x84, 0x9E, 0x79, 0x85, 0x88, 0x83, 0x01},
+ {0x15, 0x0F, 0x10, 0x12, 0x10, 0x0D, 0x15, 0x12},
+ {0x11, 0x12, 0x18, 0x16, 0x15, 0x19, 0x20, 0x35},
+ {0x22, 0x20, 0x1D, 0x1D, 0x20, 0x41, 0x2E, 0x31},
+ {0x26, 0x35, 0x4D, 0x43, 0x51, 0x4F, 0x4B, 0x43},
+ {0x4A, 0x49, 0x55, 0x5F, 0x79, 0x67, 0x55, 0x5A},
+ {0x73, 0x5B, 0x49, 0x4A, 0x6A, 0x90, 0x6B, 0x73},
+ {0x7D, 0x81, 0x88, 0x89, 0x88, 0x52, 0x66, 0x95},
+ {0xA0, 0x94, 0x84, 0x9E, 0x79, 0x85, 0x88, 0x83},
+ {0xFF, 0xC4, 0x01, 0xA2, 0x00, 0x00, 0x01, 0x05},
+ {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02},
+ {0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A},
+ {0x0B, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01},
+ {0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05},
+ {0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x10, 0x00},
+ {0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05},
+ {0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01},
+ {0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21},
+ {0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22},
+ {0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23},
+ {0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24},
+ {0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17},
+ {0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29},
+ {0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A},
+ {0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A},
+ {0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A},
+ {0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A},
+ {0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A},
+ {0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A},
+ {0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99},
+ {0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8},
+ {0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7},
+ {0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6},
+ {0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5},
+ {0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3},
+ {0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1},
+ {0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9},
+ {0xFA, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04},
+ {0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01},
+ {0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04},
+ {0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07},
+ {0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14},
+ {0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33},
+ {0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16},
+ {0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19},
+ {0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36},
+ {0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46},
+ {0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56},
+ {0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66},
+ {0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76},
+ {0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85},
+ {0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94},
+ {0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3},
+ {0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2},
+ {0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA},
+ {0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9},
+ {0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8},
+ {0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7},
+ {0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6},
+ {0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0x20, 0x00, 0x1F},
+ {0x02, 0x0C, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x11, 0x00, 0x11, 0x22, 0x00, 0x22},
+ {0x22, 0x11, 0x22, 0x22, 0x11, 0x33, 0x33, 0x11},
+ {0x44, 0x66, 0x22, 0x55, 0x66, 0xFF, 0xDD, 0x00},
+ {0x04, 0x00, 0x14, 0xFF, 0xC0, 0x00, 0x11, 0x08},
+ {0x00, 0xF0, 0x01, 0x40, 0x03, 0x00, 0x21, 0x00},
+ {0x01, 0x11, 0x01, 0x02, 0x11, 0x01, 0xFF, 0xDA},
+ {0x00, 0x0C, 0x03, 0x00, 0x00, 0x01, 0x11, 0x02},
+ {0x11, 0x00, 0x3F, 0x00, 0xFF, 0xD9, 0x00, 0x00} /* 79 */
+};
+
+
+static const __u8 cxjpeg_640[][8] = {
+ {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x10}, /* 1 */
+ {0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, 0x0d},
+ {0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, 0x1a},
+ {0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, 0x1d},
+ {0x28, 0x3a, 0x33, 0x3D, 0x3C, 0x39, 0x33, 0x38},
+ {0x37, 0x40, 0x48, 0x5C, 0x4E, 0x40, 0x44, 0x57},
+ {0x45, 0x37, 0x38, 0x50, 0x6D, 0x51, 0x57, 0x5F},
+ {0x62, 0x67, 0x68, 0x67, 0x3E, 0x4D, 0x71, 0x79},
+ {0x70, 0x64, 0x78, 0x5C, 0x65, 0x67, 0x63, 0x01},
+ {0x10, 0x0B, 0x0C, 0x0E, 0x0C, 0x0A, 0x10, 0x0E},
+ {0x0D, 0x0E, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28},
+ {0x1A, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25},
+ {0x1D, 0x28, 0x3A, 0x33, 0x3D, 0x3C, 0x39, 0x33},
+ {0x38, 0x37, 0x40, 0x48, 0x5C, 0x4E, 0x40, 0x44},
+ {0x57, 0x45, 0x37, 0x38, 0x50, 0x6D, 0x51, 0x57},
+ {0x5F, 0x62, 0x67, 0x68, 0x67, 0x3E, 0x4D, 0x71},
+ {0x79, 0x70, 0x64, 0x78, 0x5C, 0x65, 0x67, 0x63},
+ {0xFF, 0x20, 0x00, 0x1F, 0x00, 0x83, 0x00, 0x00},
+ {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00},
+ {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22},
+ {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55},
+ {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x28, 0xFF},
+ {0xC0, 0x00, 0x11, 0x08, 0x01, 0xE0, 0x02, 0x80},
+ {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02},
+ {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00},
+ {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00},
+ {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} /* 27 */
+};
+static const __u8 cxjpeg_352[][8] = {
+ {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x0d},
+ {0x09, 0x09, 0x0b, 0x09, 0x08, 0x0D, 0x0b, 0x0a},
+ {0x0b, 0x0e, 0x0d, 0x0d, 0x0f, 0x13, 0x1f, 0x14},
+ {0x13, 0x11, 0x11, 0x13, 0x26, 0x1b, 0x1d, 0x17},
+ {0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28, 0x2C},
+ {0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35, 0x44},
+ {0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44, 0x4A},
+ {0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58, 0x5F},
+ {0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D, 0x01},
+ {0x0D, 0x09, 0x09, 0x0B, 0x09, 0x08, 0x0D, 0x0B},
+ {0x0A, 0x0B, 0x0E, 0x0D, 0x0D, 0x0F, 0x13, 0x1F},
+ {0x14, 0x13, 0x11, 0x11, 0x13, 0x26, 0x1B, 0x1D},
+ {0x17, 0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28},
+ {0x2C, 0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35},
+ {0x44, 0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44},
+ {0x4A, 0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58},
+ {0x5F, 0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D},
+ {0xFF, 0x20, 0x00, 0x1F, 0x01, 0x83, 0x00, 0x00},
+ {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00},
+ {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22},
+ {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55},
+ {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x16, 0xFF},
+ {0xC0, 0x00, 0x11, 0x08, 0x01, 0x20, 0x01, 0x60},
+ {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02},
+ {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00},
+ {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00},
+ {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+};
+static const __u8 cxjpeg_320[][8] = {
+ {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x05},
+ {0x03, 0x04, 0x04, 0x04, 0x03, 0x05, 0x04, 0x04},
+ {0x04, 0x05, 0x05, 0x05, 0x06, 0x07, 0x0c, 0x08},
+ {0x07, 0x07, 0x07, 0x07, 0x0f, 0x0b, 0x0b, 0x09},
+ {0x0C, 0x11, 0x0F, 0x12, 0x12, 0x11, 0x0f, 0x11},
+ {0x11, 0x13, 0x16, 0x1C, 0x17, 0x13, 0x14, 0x1A},
+ {0x15, 0x11, 0x11, 0x18, 0x21, 0x18, 0x1A, 0x1D},
+ {0x1D, 0x1F, 0x1F, 0x1F, 0x13, 0x17, 0x22, 0x24},
+ {0x22, 0x1E, 0x24, 0x1C, 0x1E, 0x1F, 0x1E, 0x01},
+ {0x05, 0x03, 0x04, 0x04, 0x04, 0x03, 0x05, 0x04},
+ {0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x07, 0x0C},
+ {0x08, 0x07, 0x07, 0x07, 0x07, 0x0F, 0x0B, 0x0B},
+ {0x09, 0x0C, 0x11, 0x0F, 0x12, 0x12, 0x11, 0x0F},
+ {0x11, 0x11, 0x13, 0x16, 0x1C, 0x17, 0x13, 0x14},
+ {0x1A, 0x15, 0x11, 0x11, 0x18, 0x21, 0x18, 0x1A},
+ {0x1D, 0x1D, 0x1F, 0x1F, 0x1F, 0x13, 0x17, 0x22},
+ {0x24, 0x22, 0x1E, 0x24, 0x1C, 0x1E, 0x1F, 0x1E},
+ {0xFF, 0x20, 0x00, 0x1F, 0x02, 0x0C, 0x00, 0x00},
+ {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00},
+ {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22},
+ {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55},
+ {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x14, 0xFF},
+ {0xC0, 0x00, 0x11, 0x08, 0x00, 0xF0, 0x01, 0x40},
+ {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02},
+ {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00},
+ {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00},
+ {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} /* 27 */
+};
+static const __u8 cxjpeg_176[][8] = {
+ {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x0d},
+ {0x09, 0x09, 0x0B, 0x09, 0x08, 0x0D, 0x0B, 0x0A},
+ {0x0B, 0x0E, 0x0D, 0x0D, 0x0F, 0x13, 0x1F, 0x14},
+ {0x13, 0x11, 0x11, 0x13, 0x26, 0x1B, 0x1D, 0x17},
+ {0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28, 0x2C},
+ {0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35, 0x44},
+ {0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44, 0x4A},
+ {0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58, 0x5F},
+ {0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D, 0x01},
+ {0x0D, 0x09, 0x09, 0x0B, 0x09, 0x08, 0x0D, 0x0B},
+ {0x0A, 0x0B, 0x0E, 0x0D, 0x0D, 0x0F, 0x13, 0x1F},
+ {0x14, 0x13, 0x11, 0x11, 0x13, 0x26, 0x1B, 0x1D},
+ {0x17, 0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28},
+ {0x2C, 0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35},
+ {0x44, 0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44},
+ {0x4A, 0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58},
+ {0x5F, 0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D},
+ {0xFF, 0x20, 0x00, 0x1F, 0x03, 0xA1, 0x00, 0x00},
+ {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00},
+ {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22},
+ {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55},
+ {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x0B, 0xFF},
+ {0xC0, 0x00, 0x11, 0x08, 0x00, 0x90, 0x00, 0xB0},
+ {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02},
+ {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00},
+ {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00},
+ {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+};
+/* 640 take with the zcx30x part */
+static const __u8 cxjpeg_qtable[][8] = {
+ {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x08},
+ {0x06, 0x06, 0x07, 0x06, 0x05, 0x08, 0x07, 0x07},
+ {0x07, 0x09, 0x09, 0x08, 0x0a, 0x0c, 0x14, 0x0a},
+ {0x0c, 0x0b, 0x0b, 0x0c, 0x19, 0x12, 0x13, 0x0f},
+ {0x14, 0x1d, 0x1a, 0x1f, 0x1e, 0x1d, 0x1a, 0x1c},
+ {0x1c, 0x20, 0x24, 0x2e, 0x27, 0x20, 0x22, 0x2c},
+ {0x23, 0x1c, 0x1c, 0x28, 0x37, 0x29, 0x2c, 0x30},
+ {0x31, 0x34, 0x34, 0x34, 0x1f, 0x27, 0x39, 0x3d},
+ {0x38, 0x32, 0x3c, 0x2e, 0x33, 0x34, 0x32, 0x01},
+ {0x09, 0x09, 0x09, 0x0c, 0x0b, 0x0c, 0x18, 0x0a},
+ {0x0a, 0x18, 0x32, 0x21, 0x1c, 0x21, 0x32, 0x32},
+ {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32},
+ {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32},
+ {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32},
+ {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32},
+ {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32},
+ {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32},
+ {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} /* 18 */
+};
+
+
+static void cx11646_jpegInit(struct gspca_dev*gspca_dev)
+{
+ int i;
+ int length;
+
+ reg_w_val(gspca_dev, 0x00c0, 0x01);
+ reg_w_val(gspca_dev, 0x00c3, 0x00);
+ reg_w_val(gspca_dev, 0x00c0, 0x00);
+ reg_r(gspca_dev, 0x0001, 1);
+ length = 8;
+ for (i = 0; i < 79; i++) {
+ if (i == 78)
+ length = 6;
+ reg_w(gspca_dev, 0x0008, cx_jpeg_init[i], length);
+ }
+ reg_r(gspca_dev, 0x0002, 1);
+ reg_w_val(gspca_dev, 0x0055, 0x14);
+}
+
+static const __u8 reg12[] = { 0x0a, 0x05, 0x07, 0x04, 0x19 };
+static const __u8 regE5_8[] =
+ { 0x88, 0x00, 0xd4, 0x01, 0x88, 0x01, 0x01, 0x01 };
+static const __u8 regE5a[] = { 0x88, 0x0a, 0x0c, 0x01 };
+static const __u8 regE5b[] = { 0x88, 0x0b, 0x12, 0x01 };
+static const __u8 regE5c[] = { 0x88, 0x05, 0x01, 0x01 };
+static const __u8 reg51[] = { 0x77, 0x03 };
+#define reg70 0x03
+
+static void cx11646_jpeg(struct gspca_dev*gspca_dev)
+{
+ int i;
+ int length;
+ __u8 Reg55;
+ int retry;
+
+ reg_w_val(gspca_dev, 0x00c0, 0x01);
+ reg_w_val(gspca_dev, 0x00c3, 0x00);
+ reg_w_val(gspca_dev, 0x00c0, 0x00);
+ reg_r(gspca_dev, 0x0001, 1);
+ length = 8;
+ switch (gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv) {
+ case 0:
+ for (i = 0; i < 27; i++) {
+ if (i == 26)
+ length = 2;
+ reg_w(gspca_dev, 0x0008, cxjpeg_640[i], length);
+ }
+ Reg55 = 0x28;
+ break;
+ case 1:
+ for (i = 0; i < 27; i++) {
+ if (i == 26)
+ length = 2;
+ reg_w(gspca_dev, 0x0008, cxjpeg_352[i], length);
+ }
+ Reg55 = 0x16;
+ break;
+ default:
+/* case 2: */
+ for (i = 0; i < 27; i++) {
+ if (i == 26)
+ length = 2;
+ reg_w(gspca_dev, 0x0008, cxjpeg_320[i], length);
+ }
+ Reg55 = 0x14;
+ break;
+ case 3:
+ for (i = 0; i < 27; i++) {
+ if (i == 26)
+ length = 2;
+ reg_w(gspca_dev, 0x0008, cxjpeg_176[i], length);
+ }
+ Reg55 = 0x0B;
+ break;
+ }
+
+ reg_r(gspca_dev, 0x0002, 1);
+ reg_w_val(gspca_dev, 0x0055, Reg55);
+ reg_r(gspca_dev, 0x0002, 1);
+ reg_w(gspca_dev, 0x0010, reg10, 2);
+ reg_w_val(gspca_dev, 0x0054, 0x02);
+ reg_w_val(gspca_dev, 0x0054, 0x01);
+ reg_w_val(gspca_dev, 0x0000, 0x94);
+ reg_w_val(gspca_dev, 0x0053, 0xc0);
+ reg_w_val(gspca_dev, 0x00fc, 0xe1);
+ reg_w_val(gspca_dev, 0x0000, 0x00);
+ /* wait for completion */
+ retry = 50;
+ do {
+ reg_r(gspca_dev, 0x0002, 1);
+ /* 0x07 until 0x00 */
+ if (gspca_dev->usb_buf[0] == 0x00)
+ break;
+ reg_w_val(gspca_dev, 0x0053, 0x00);
+ } while (--retry);
+ if (retry == 0)
+ PDEBUG(D_ERR, "Damned Errors sending jpeg Table");
+ /* send the qtable now */
+ reg_r(gspca_dev, 0x0001, 1); /* -> 0x18 */
+ length = 8;
+ for (i = 0; i < 18; i++) {
+ if (i == 17)
+ length = 2;
+ reg_w(gspca_dev, 0x0008, cxjpeg_qtable[i], length);
+
+ }
+ reg_r(gspca_dev, 0x0002, 1); /* 0x00 */
+ reg_r(gspca_dev, 0x0053, 1); /* 0x00 */
+ reg_w_val(gspca_dev, 0x0054, 0x02);
+ reg_w_val(gspca_dev, 0x0054, 0x01);
+ reg_w_val(gspca_dev, 0x0000, 0x94);
+ reg_w_val(gspca_dev, 0x0053, 0xc0);
+
+ reg_r(gspca_dev, 0x0038, 1); /* 0x40 */
+ reg_r(gspca_dev, 0x0038, 1); /* 0x40 */
+ reg_r(gspca_dev, 0x001f, 1); /* 0x38 */
+ reg_w(gspca_dev, 0x0012, reg12, 5);
+ reg_w(gspca_dev, 0x00e5, regE5_8, 8);
+ reg_r(gspca_dev, 0x00e8, 8);
+ reg_w(gspca_dev, 0x00e5, regE5a, 4);
+ reg_r(gspca_dev, 0x00e8, 1); /* 0x00 */
+ reg_w_val(gspca_dev, 0x009a, 0x01);
+ reg_w(gspca_dev, 0x00e5, regE5b, 4);
+ reg_r(gspca_dev, 0x00e8, 1); /* 0x00 */
+ reg_w(gspca_dev, 0x00e5, regE5c, 4);
+ reg_r(gspca_dev, 0x00e8, 1); /* 0x00 */
+
+ reg_w(gspca_dev, 0x0051, reg51, 2);
+ reg_w(gspca_dev, 0x0010, reg10, 2);
+ reg_w_val(gspca_dev, 0x0070, reg70);
+}
+
+static void cx11646_init1(struct gspca_dev *gspca_dev)
+{
+ int i = 0;
+
+ reg_w_val(gspca_dev, 0x0010, 0x00);
+ reg_w_val(gspca_dev, 0x0053, 0x00);
+ reg_w_val(gspca_dev, 0x0052, 0x00);
+ reg_w_val(gspca_dev, 0x009b, 0x2f);
+ reg_w_val(gspca_dev, 0x009c, 0x10);
+ reg_r(gspca_dev, 0x0098, 1);
+ reg_w_val(gspca_dev, 0x0098, 0x40);
+ reg_r(gspca_dev, 0x0099, 1);
+ reg_w_val(gspca_dev, 0x0099, 0x07);
+ reg_w_val(gspca_dev, 0x0039, 0x40);
+ reg_w_val(gspca_dev, 0x003c, 0xff);
+ reg_w_val(gspca_dev, 0x003f, 0x1f);
+ reg_w_val(gspca_dev, 0x003d, 0x40);
+/* reg_w_val(gspca_dev, 0x003d, 0x60); */
+ reg_r(gspca_dev, 0x0099, 1); /* ->0x07 */
+
+ while (cx_sensor_init[i][0]) {
+ reg_w_val(gspca_dev, 0x00e5, cx_sensor_init[i][0]);
+ reg_r(gspca_dev, 0x00e8, 1); /* -> 0x00 */
+ if (i == 1) {
+ reg_w_val(gspca_dev, 0x00ed, 0x01);
+ reg_r(gspca_dev, 0x00ed, 1); /* -> 0x01 */
+ }
+ i++;
+ }
+ reg_w_val(gspca_dev, 0x00c3, 0x00);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+
+ sd->brightness = BRIGHTNESS_DEF;
+ sd->contrast = CONTRAST_DEF;
+ sd->colors = COLOR_DEF;
+ sd->quality = QUALITY_DEF;
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ cx11646_init1(gspca_dev);
+ cx11646_initsize(gspca_dev);
+ cx11646_fw(gspca_dev);
+ cx_sensor(gspca_dev);
+ cx11646_jpegInit(gspca_dev);
+ return 0;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* create the JPEG header */
+ jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
+ 0x22); /* JPEG 411 */
+ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+
+ cx11646_initsize(gspca_dev);
+ cx11646_fw(gspca_dev);
+ cx_sensor(gspca_dev);
+ cx11646_jpeg(gspca_dev);
+ return 0;
+}
+
+/* called on streamoff with alt 0 and on disconnect */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ int retry = 50;
+
+ if (!gspca_dev->present)
+ return;
+ reg_w_val(gspca_dev, 0x0000, 0x00);
+ reg_r(gspca_dev, 0x0002, 1);
+ reg_w_val(gspca_dev, 0x0053, 0x00);
+
+ while (retry--) {
+/* reg_r(gspca_dev, 0x0002, 1);*/
+ reg_r(gspca_dev, 0x0053, 1);
+ if (gspca_dev->usb_buf[0] == 0)
+ break;
+ }
+ reg_w_val(gspca_dev, 0x0000, 0x00);
+ reg_r(gspca_dev, 0x0002, 1);
+
+ reg_w_val(gspca_dev, 0x0010, 0x00);
+ reg_r(gspca_dev, 0x0033, 1);
+ reg_w_val(gspca_dev, 0x00fc, 0xe0);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (data[0] == 0xff && data[1] == 0xd8) {
+
+ /* start of frame */
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+
+ /* put the JPEG header in the new frame */
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+ data += 2;
+ len -= 2;
+ }
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u8 regE5cbx[] = { 0x88, 0x00, 0xd4, 0x01, 0x88, 0x01, 0x01, 0x01 };
+ __u8 reg51c[2];
+ __u8 bright;
+ __u8 colors;
+
+ bright = sd->brightness;
+ regE5cbx[2] = bright;
+ reg_w(gspca_dev, 0x00e5, regE5cbx, 8);
+ reg_r(gspca_dev, 0x00e8, 8);
+ reg_w(gspca_dev, 0x00e5, regE5c, 4);
+ reg_r(gspca_dev, 0x00e8, 1); /* 0x00 */
+
+ colors = sd->colors;
+ reg51c[0] = 0x77;
+ reg51c[1] = colors;
+ reg_w(gspca_dev, 0x0051, reg51c, 2);
+ reg_w(gspca_dev, 0x0010, reg10, 2);
+ reg_w_val(gspca_dev, 0x0070, reg70);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u8 regE5acx[] = { 0x88, 0x0a, 0x0c, 0x01 }; /* seem MSB */
+/* __u8 regE5bcx[] = { 0x88, 0x0b, 0x12, 0x01}; * LSB */
+ __u8 reg51c[2];
+
+ regE5acx[2] = sd->contrast;
+ reg_w(gspca_dev, 0x00e5, regE5acx, 4);
+ reg_r(gspca_dev, 0x00e8, 1); /* 0x00 */
+ reg51c[0] = 0x77;
+ reg51c[1] = sd->colors;
+ reg_w(gspca_dev, 0x0051, reg51c, 2);
+ reg_w(gspca_dev, 0x0010, reg10, 2);
+ reg_w_val(gspca_dev, 0x0070, reg70);
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->brightness = val;
+ if (gspca_dev->streaming)
+ setbrightness(gspca_dev);
+ return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->brightness;
+ return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->contrast = val;
+ if (gspca_dev->streaming)
+ setcontrast(gspca_dev);
+ return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->contrast;
+ return 0;
+}
+
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->colors = val;
+ if (gspca_dev->streaming) {
+ setbrightness(gspca_dev);
+ setcontrast(gspca_dev);
+ }
+ return 0;
+}
+
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->colors;
+ return 0;
+}
+
+static int sd_set_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (jcomp->quality < QUALITY_MIN)
+ sd->quality = QUALITY_MIN;
+ else if (jcomp->quality > QUALITY_MAX)
+ sd->quality = QUALITY_MAX;
+ else
+ sd->quality = jcomp->quality;
+ if (gspca_dev->streaming)
+ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+ return 0;
+}
+
+static int sd_get_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ memset(jcomp, 0, sizeof *jcomp);
+ jcomp->quality = sd->quality;
+ jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
+ | V4L2_JPEG_MARKER_DQT;
+ return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+ .get_jcomp = sd_get_jcomp,
+ .set_jcomp = sd_set_jcomp,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x0572, 0x0041)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/cpia1.c b/drivers/media/video/gspca/cpia1.c
new file mode 100644
index 00000000..8f33bbd0
--- /dev/null
+++ b/drivers/media/video/gspca/cpia1.c
@@ -0,0 +1,2135 @@
+/*
+ * cpia CPiA (1) gspca driver
+ *
+ * Copyright (C) 2010-2011 Hans de Goede <hdegoede@redhat.com>
+ *
+ * This module is adapted from the in kernel v4l1 cpia driver which is :
+ *
+ * (C) Copyright 1999-2000 Peter Pregler
+ * (C) Copyright 1999-2000 Scott J. Bertin
+ * (C) Copyright 1999-2000 Johannes Erdfelt <johannes@erdfelt.com>
+ * (C) Copyright 2000 STMicroelectronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "cpia1"
+
+#include <linux/input.h>
+#include "gspca.h"
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Vision CPiA");
+MODULE_LICENSE("GPL");
+
+/* constant value's */
+#define MAGIC_0 0x19
+#define MAGIC_1 0x68
+#define DATA_IN 0xc0
+#define DATA_OUT 0x40
+#define VIDEOSIZE_QCIF 0 /* 176x144 */
+#define VIDEOSIZE_CIF 1 /* 352x288 */
+#define SUBSAMPLE_420 0
+#define SUBSAMPLE_422 1
+#define YUVORDER_YUYV 0
+#define YUVORDER_UYVY 1
+#define NOT_COMPRESSED 0
+#define COMPRESSED 1
+#define NO_DECIMATION 0
+#define DECIMATION_ENAB 1
+#define EOI 0xff /* End Of Image */
+#define EOL 0xfd /* End Of Line */
+#define FRAME_HEADER_SIZE 64
+
+/* Image grab modes */
+#define CPIA_GRAB_SINGLE 0
+#define CPIA_GRAB_CONTINEOUS 1
+
+/* Compression parameters */
+#define CPIA_COMPRESSION_NONE 0
+#define CPIA_COMPRESSION_AUTO 1
+#define CPIA_COMPRESSION_MANUAL 2
+#define CPIA_COMPRESSION_TARGET_QUALITY 0
+#define CPIA_COMPRESSION_TARGET_FRAMERATE 1
+
+/* Return offsets for GetCameraState */
+#define SYSTEMSTATE 0
+#define GRABSTATE 1
+#define STREAMSTATE 2
+#define FATALERROR 3
+#define CMDERROR 4
+#define DEBUGFLAGS 5
+#define VPSTATUS 6
+#define ERRORCODE 7
+
+/* SystemState */
+#define UNINITIALISED_STATE 0
+#define PASS_THROUGH_STATE 1
+#define LO_POWER_STATE 2
+#define HI_POWER_STATE 3
+#define WARM_BOOT_STATE 4
+
+/* GrabState */
+#define GRAB_IDLE 0
+#define GRAB_ACTIVE 1
+#define GRAB_DONE 2
+
+/* StreamState */
+#define STREAM_NOT_READY 0
+#define STREAM_READY 1
+#define STREAM_OPEN 2
+#define STREAM_PAUSED 3
+#define STREAM_FINISHED 4
+
+/* Fatal Error, CmdError, and DebugFlags */
+#define CPIA_FLAG 1
+#define SYSTEM_FLAG 2
+#define INT_CTRL_FLAG 4
+#define PROCESS_FLAG 8
+#define COM_FLAG 16
+#define VP_CTRL_FLAG 32
+#define CAPTURE_FLAG 64
+#define DEBUG_FLAG 128
+
+/* VPStatus */
+#define VP_STATE_OK 0x00
+
+#define VP_STATE_FAILED_VIDEOINIT 0x01
+#define VP_STATE_FAILED_AECACBINIT 0x02
+#define VP_STATE_AEC_MAX 0x04
+#define VP_STATE_ACB_BMAX 0x08
+
+#define VP_STATE_ACB_RMIN 0x10
+#define VP_STATE_ACB_GMIN 0x20
+#define VP_STATE_ACB_RMAX 0x40
+#define VP_STATE_ACB_GMAX 0x80
+
+/* default (minimum) compensation values */
+#define COMP_RED 220
+#define COMP_GREEN1 214
+#define COMP_GREEN2 COMP_GREEN1
+#define COMP_BLUE 230
+
+/* exposure status */
+#define EXPOSURE_VERY_LIGHT 0
+#define EXPOSURE_LIGHT 1
+#define EXPOSURE_NORMAL 2
+#define EXPOSURE_DARK 3
+#define EXPOSURE_VERY_DARK 4
+
+#define CPIA_MODULE_CPIA (0 << 5)
+#define CPIA_MODULE_SYSTEM (1 << 5)
+#define CPIA_MODULE_VP_CTRL (5 << 5)
+#define CPIA_MODULE_CAPTURE (6 << 5)
+#define CPIA_MODULE_DEBUG (7 << 5)
+
+#define INPUT (DATA_IN << 8)
+#define OUTPUT (DATA_OUT << 8)
+
+#define CPIA_COMMAND_GetCPIAVersion (INPUT | CPIA_MODULE_CPIA | 1)
+#define CPIA_COMMAND_GetPnPID (INPUT | CPIA_MODULE_CPIA | 2)
+#define CPIA_COMMAND_GetCameraStatus (INPUT | CPIA_MODULE_CPIA | 3)
+#define CPIA_COMMAND_GotoHiPower (OUTPUT | CPIA_MODULE_CPIA | 4)
+#define CPIA_COMMAND_GotoLoPower (OUTPUT | CPIA_MODULE_CPIA | 5)
+#define CPIA_COMMAND_GotoSuspend (OUTPUT | CPIA_MODULE_CPIA | 7)
+#define CPIA_COMMAND_GotoPassThrough (OUTPUT | CPIA_MODULE_CPIA | 8)
+#define CPIA_COMMAND_ModifyCameraStatus (OUTPUT | CPIA_MODULE_CPIA | 10)
+
+#define CPIA_COMMAND_ReadVCRegs (INPUT | CPIA_MODULE_SYSTEM | 1)
+#define CPIA_COMMAND_WriteVCReg (OUTPUT | CPIA_MODULE_SYSTEM | 2)
+#define CPIA_COMMAND_ReadMCPorts (INPUT | CPIA_MODULE_SYSTEM | 3)
+#define CPIA_COMMAND_WriteMCPort (OUTPUT | CPIA_MODULE_SYSTEM | 4)
+#define CPIA_COMMAND_SetBaudRate (OUTPUT | CPIA_MODULE_SYSTEM | 5)
+#define CPIA_COMMAND_SetECPTiming (OUTPUT | CPIA_MODULE_SYSTEM | 6)
+#define CPIA_COMMAND_ReadIDATA (INPUT | CPIA_MODULE_SYSTEM | 7)
+#define CPIA_COMMAND_WriteIDATA (OUTPUT | CPIA_MODULE_SYSTEM | 8)
+#define CPIA_COMMAND_GenericCall (OUTPUT | CPIA_MODULE_SYSTEM | 9)
+#define CPIA_COMMAND_I2CStart (OUTPUT | CPIA_MODULE_SYSTEM | 10)
+#define CPIA_COMMAND_I2CStop (OUTPUT | CPIA_MODULE_SYSTEM | 11)
+#define CPIA_COMMAND_I2CWrite (OUTPUT | CPIA_MODULE_SYSTEM | 12)
+#define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13)
+
+#define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1)
+#define CPIA_COMMAND_ResetFrameCounter (INPUT | CPIA_MODULE_VP_CTRL | 2)
+#define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3)
+#define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4)
+#define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6)
+#define CPIA_COMMAND_SetSensorFPS (OUTPUT | CPIA_MODULE_VP_CTRL | 7)
+#define CPIA_COMMAND_SetVPDefaults (OUTPUT | CPIA_MODULE_VP_CTRL | 8)
+#define CPIA_COMMAND_SetApcor (OUTPUT | CPIA_MODULE_VP_CTRL | 9)
+#define CPIA_COMMAND_SetFlickerCtrl (OUTPUT | CPIA_MODULE_VP_CTRL | 10)
+#define CPIA_COMMAND_SetVLOffset (OUTPUT | CPIA_MODULE_VP_CTRL | 11)
+#define CPIA_COMMAND_GetColourParams (INPUT | CPIA_MODULE_VP_CTRL | 16)
+#define CPIA_COMMAND_GetColourBalance (INPUT | CPIA_MODULE_VP_CTRL | 17)
+#define CPIA_COMMAND_GetExposure (INPUT | CPIA_MODULE_VP_CTRL | 18)
+#define CPIA_COMMAND_SetSensorMatrix (OUTPUT | CPIA_MODULE_VP_CTRL | 19)
+#define CPIA_COMMAND_ColourBars (OUTPUT | CPIA_MODULE_VP_CTRL | 25)
+#define CPIA_COMMAND_ReadVPRegs (INPUT | CPIA_MODULE_VP_CTRL | 30)
+#define CPIA_COMMAND_WriteVPReg (OUTPUT | CPIA_MODULE_VP_CTRL | 31)
+
+#define CPIA_COMMAND_GrabFrame (OUTPUT | CPIA_MODULE_CAPTURE | 1)
+#define CPIA_COMMAND_UploadFrame (OUTPUT | CPIA_MODULE_CAPTURE | 2)
+#define CPIA_COMMAND_SetGrabMode (OUTPUT | CPIA_MODULE_CAPTURE | 3)
+#define CPIA_COMMAND_InitStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 4)
+#define CPIA_COMMAND_FiniStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 5)
+#define CPIA_COMMAND_StartStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 6)
+#define CPIA_COMMAND_EndStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 7)
+#define CPIA_COMMAND_SetFormat (OUTPUT | CPIA_MODULE_CAPTURE | 8)
+#define CPIA_COMMAND_SetROI (OUTPUT | CPIA_MODULE_CAPTURE | 9)
+#define CPIA_COMMAND_SetCompression (OUTPUT | CPIA_MODULE_CAPTURE | 10)
+#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11)
+#define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12)
+#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13)
+#define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14)
+#define CPIA_COMMAND_GrabReset (OUTPUT | CPIA_MODULE_CAPTURE | 15)
+
+#define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1)
+#define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4)
+#define CPIA_COMMAND_SetDramPage (OUTPUT | CPIA_MODULE_DEBUG | 5)
+#define CPIA_COMMAND_StartDramUpload (OUTPUT | CPIA_MODULE_DEBUG | 6)
+#define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8)
+#define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9)
+#define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10)
+#define CPIA_COMMAND_Null (OUTPUT | CPIA_MODULE_DEBUG | 11)
+
+#define ROUND_UP_EXP_FOR_FLICKER 15
+
+/* Constants for automatic frame rate adjustment */
+#define MAX_EXP 302
+#define MAX_EXP_102 255
+#define LOW_EXP 140
+#define VERY_LOW_EXP 70
+#define TC 94
+#define EXP_ACC_DARK 50
+#define EXP_ACC_LIGHT 90
+#define HIGH_COMP_102 160
+#define MAX_COMP 239
+#define DARK_TIME 3
+#define LIGHT_TIME 3
+
+#define FIRMWARE_VERSION(x, y) (sd->params.version.firmwareVersion == (x) && \
+ sd->params.version.firmwareRevision == (y))
+
+/* Developer's Guide Table 5 p 3-34
+ * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
+static u8 flicker_jumps[2][2][4] =
+{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } },
+ { { 64, 32, 16, 8 }, { 76, 38, 19, 9} }
+};
+
+struct cam_params {
+ struct {
+ u8 firmwareVersion;
+ u8 firmwareRevision;
+ u8 vcVersion;
+ u8 vcRevision;
+ } version;
+ struct {
+ u16 vendor;
+ u16 product;
+ u16 deviceRevision;
+ } pnpID;
+ struct {
+ u8 vpVersion;
+ u8 vpRevision;
+ u16 cameraHeadID;
+ } vpVersion;
+ struct {
+ u8 systemState;
+ u8 grabState;
+ u8 streamState;
+ u8 fatalError;
+ u8 cmdError;
+ u8 debugFlags;
+ u8 vpStatus;
+ u8 errorCode;
+ } status;
+ struct {
+ u8 brightness;
+ u8 contrast;
+ u8 saturation;
+ } colourParams;
+ struct {
+ u8 gainMode;
+ u8 expMode;
+ u8 compMode;
+ u8 centreWeight;
+ u8 gain;
+ u8 fineExp;
+ u8 coarseExpLo;
+ u8 coarseExpHi;
+ u8 redComp;
+ u8 green1Comp;
+ u8 green2Comp;
+ u8 blueComp;
+ } exposure;
+ struct {
+ u8 balanceMode;
+ u8 redGain;
+ u8 greenGain;
+ u8 blueGain;
+ } colourBalance;
+ struct {
+ u8 divisor;
+ u8 baserate;
+ } sensorFps;
+ struct {
+ u8 gain1;
+ u8 gain2;
+ u8 gain4;
+ u8 gain8;
+ } apcor;
+ struct {
+ u8 disabled;
+ u8 flickerMode;
+ u8 coarseJump;
+ u8 allowableOverExposure;
+ } flickerControl;
+ struct {
+ u8 gain1;
+ u8 gain2;
+ u8 gain4;
+ u8 gain8;
+ } vlOffset;
+ struct {
+ u8 mode;
+ u8 decimation;
+ } compression;
+ struct {
+ u8 frTargeting;
+ u8 targetFR;
+ u8 targetQ;
+ } compressionTarget;
+ struct {
+ u8 yThreshold;
+ u8 uvThreshold;
+ } yuvThreshold;
+ struct {
+ u8 hysteresis;
+ u8 threshMax;
+ u8 smallStep;
+ u8 largeStep;
+ u8 decimationHysteresis;
+ u8 frDiffStepThresh;
+ u8 qDiffStepThresh;
+ u8 decimationThreshMod;
+ } compressionParams;
+ struct {
+ u8 videoSize; /* CIF/QCIF */
+ u8 subSample;
+ u8 yuvOrder;
+ } format;
+ struct { /* Intel QX3 specific data */
+ u8 qx3_detected; /* a QX3 is present */
+ u8 toplight; /* top light lit , R/W */
+ u8 bottomlight; /* bottom light lit, R/W */
+ u8 button; /* snapshot button pressed (R/O) */
+ u8 cradled; /* microscope is in cradle (R/O) */
+ } qx3;
+ struct {
+ u8 colStart; /* skip first 8*colStart pixels */
+ u8 colEnd; /* finish at 8*colEnd pixels */
+ u8 rowStart; /* skip first 4*rowStart lines */
+ u8 rowEnd; /* finish at 4*rowEnd lines */
+ } roi;
+ u8 ecpTiming;
+ u8 streamStartLine;
+};
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ struct cam_params params; /* camera settings */
+
+ atomic_t cam_exposure;
+ atomic_t fps;
+ int exposure_count;
+ u8 exposure_status;
+ u8 mainsFreq; /* 0 = 50hz, 1 = 60hz */
+ u8 first_frame;
+ u8 freq;
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setsaturation(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcomptarget(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcomptarget(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setilluminator1(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getilluminator1(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setilluminator2(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getilluminator2(struct gspca_dev *gspca_dev, __s32 *val);
+
+static const struct ctrl sd_ctrls[] = {
+ {
+#define BRIGHTNESS_IDX 0
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 100,
+ .step = 1,
+#define BRIGHTNESS_DEF 50
+ .default_value = BRIGHTNESS_DEF,
+ .flags = 0,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+#define CONTRAST_IDX 1
+ {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 96,
+ .step = 8,
+#define CONTRAST_DEF 48
+ .default_value = CONTRAST_DEF,
+ },
+ .set = sd_setcontrast,
+ .get = sd_getcontrast,
+ },
+#define SATURATION_IDX 2
+ {
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 100,
+ .step = 1,
+#define SATURATION_DEF 50
+ .default_value = SATURATION_DEF,
+ },
+ .set = sd_setsaturation,
+ .get = sd_getsaturation,
+ },
+#define POWER_LINE_FREQUENCY_IDX 3
+ {
+ {
+ .id = V4L2_CID_POWER_LINE_FREQUENCY,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Light frequency filter",
+ .minimum = 0,
+ .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */
+ .step = 1,
+#define FREQ_DEF 1
+ .default_value = FREQ_DEF,
+ },
+ .set = sd_setfreq,
+ .get = sd_getfreq,
+ },
+#define ILLUMINATORS_1_IDX 4
+ {
+ {
+ .id = V4L2_CID_ILLUMINATORS_1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Illuminator 1",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define ILLUMINATORS_1_DEF 0
+ .default_value = ILLUMINATORS_1_DEF,
+ },
+ .set = sd_setilluminator1,
+ .get = sd_getilluminator1,
+ },
+#define ILLUMINATORS_2_IDX 5
+ {
+ {
+ .id = V4L2_CID_ILLUMINATORS_2,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Illuminator 2",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define ILLUMINATORS_2_DEF 0
+ .default_value = ILLUMINATORS_2_DEF,
+ },
+ .set = sd_setilluminator2,
+ .get = sd_getilluminator2,
+ },
+#define COMP_TARGET_IDX 6
+ {
+ {
+#define V4L2_CID_COMP_TARGET V4L2_CID_PRIVATE_BASE
+ .id = V4L2_CID_COMP_TARGET,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Compression Target",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define COMP_TARGET_DEF CPIA_COMPRESSION_TARGET_QUALITY
+ .default_value = COMP_TARGET_DEF,
+ },
+ .set = sd_setcomptarget,
+ .get = sd_getcomptarget,
+ },
+};
+
+static const struct v4l2_pix_format mode[] = {
+ {160, 120, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
+ /* The sizeimage is trial and error, as with low framerates
+ the camera will pad out usb frames, making the image
+ data larger then strictly necessary */
+ .bytesperline = 160,
+ .sizeimage = 65536,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 3},
+ {176, 144, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
+ .bytesperline = 172,
+ .sizeimage = 65536,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 2},
+ {320, 240, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 262144,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {352, 288, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 262144,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+
+/**********************************************************************
+ *
+ * General functions
+ *
+ **********************************************************************/
+
+static int cpia_usb_transferCmd(struct gspca_dev *gspca_dev, u8 *command)
+{
+ u8 requesttype;
+ unsigned int pipe;
+ int ret, databytes = command[6] | (command[7] << 8);
+ /* Sometimes we see spurious EPIPE errors */
+ int retries = 3;
+
+ if (command[0] == DATA_IN) {
+ pipe = usb_rcvctrlpipe(gspca_dev->dev, 0);
+ requesttype = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+ } else if (command[0] == DATA_OUT) {
+ pipe = usb_sndctrlpipe(gspca_dev->dev, 0);
+ requesttype = USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+ } else {
+ PDEBUG(D_ERR, "Unexpected first byte of command: %x",
+ command[0]);
+ return -EINVAL;
+ }
+
+retry:
+ ret = usb_control_msg(gspca_dev->dev, pipe,
+ command[1],
+ requesttype,
+ command[2] | (command[3] << 8),
+ command[4] | (command[5] << 8),
+ gspca_dev->usb_buf, databytes, 1000);
+
+ if (ret < 0)
+ pr_err("usb_control_msg %02x, error %d\n", command[1], ret);
+
+ if (ret == -EPIPE && retries > 0) {
+ retries--;
+ goto retry;
+ }
+
+ return (ret < 0) ? ret : 0;
+}
+
+/* send an arbitrary command to the camera */
+static int do_command(struct gspca_dev *gspca_dev, u16 command,
+ u8 a, u8 b, u8 c, u8 d)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret, datasize;
+ u8 cmd[8];
+
+ switch (command) {
+ case CPIA_COMMAND_GetCPIAVersion:
+ case CPIA_COMMAND_GetPnPID:
+ case CPIA_COMMAND_GetCameraStatus:
+ case CPIA_COMMAND_GetVPVersion:
+ case CPIA_COMMAND_GetColourParams:
+ case CPIA_COMMAND_GetColourBalance:
+ case CPIA_COMMAND_GetExposure:
+ datasize = 8;
+ break;
+ case CPIA_COMMAND_ReadMCPorts:
+ case CPIA_COMMAND_ReadVCRegs:
+ datasize = 4;
+ break;
+ default:
+ datasize = 0;
+ break;
+ }
+
+ cmd[0] = command >> 8;
+ cmd[1] = command & 0xff;
+ cmd[2] = a;
+ cmd[3] = b;
+ cmd[4] = c;
+ cmd[5] = d;
+ cmd[6] = datasize;
+ cmd[7] = 0;
+
+ ret = cpia_usb_transferCmd(gspca_dev, cmd);
+ if (ret)
+ return ret;
+
+ switch (command) {
+ case CPIA_COMMAND_GetCPIAVersion:
+ sd->params.version.firmwareVersion = gspca_dev->usb_buf[0];
+ sd->params.version.firmwareRevision = gspca_dev->usb_buf[1];
+ sd->params.version.vcVersion = gspca_dev->usb_buf[2];
+ sd->params.version.vcRevision = gspca_dev->usb_buf[3];
+ break;
+ case CPIA_COMMAND_GetPnPID:
+ sd->params.pnpID.vendor =
+ gspca_dev->usb_buf[0] | (gspca_dev->usb_buf[1] << 8);
+ sd->params.pnpID.product =
+ gspca_dev->usb_buf[2] | (gspca_dev->usb_buf[3] << 8);
+ sd->params.pnpID.deviceRevision =
+ gspca_dev->usb_buf[4] | (gspca_dev->usb_buf[5] << 8);
+ break;
+ case CPIA_COMMAND_GetCameraStatus:
+ sd->params.status.systemState = gspca_dev->usb_buf[0];
+ sd->params.status.grabState = gspca_dev->usb_buf[1];
+ sd->params.status.streamState = gspca_dev->usb_buf[2];
+ sd->params.status.fatalError = gspca_dev->usb_buf[3];
+ sd->params.status.cmdError = gspca_dev->usb_buf[4];
+ sd->params.status.debugFlags = gspca_dev->usb_buf[5];
+ sd->params.status.vpStatus = gspca_dev->usb_buf[6];
+ sd->params.status.errorCode = gspca_dev->usb_buf[7];
+ break;
+ case CPIA_COMMAND_GetVPVersion:
+ sd->params.vpVersion.vpVersion = gspca_dev->usb_buf[0];
+ sd->params.vpVersion.vpRevision = gspca_dev->usb_buf[1];
+ sd->params.vpVersion.cameraHeadID =
+ gspca_dev->usb_buf[2] | (gspca_dev->usb_buf[3] << 8);
+ break;
+ case CPIA_COMMAND_GetColourParams:
+ sd->params.colourParams.brightness = gspca_dev->usb_buf[0];
+ sd->params.colourParams.contrast = gspca_dev->usb_buf[1];
+ sd->params.colourParams.saturation = gspca_dev->usb_buf[2];
+ break;
+ case CPIA_COMMAND_GetColourBalance:
+ sd->params.colourBalance.redGain = gspca_dev->usb_buf[0];
+ sd->params.colourBalance.greenGain = gspca_dev->usb_buf[1];
+ sd->params.colourBalance.blueGain = gspca_dev->usb_buf[2];
+ break;
+ case CPIA_COMMAND_GetExposure:
+ sd->params.exposure.gain = gspca_dev->usb_buf[0];
+ sd->params.exposure.fineExp = gspca_dev->usb_buf[1];
+ sd->params.exposure.coarseExpLo = gspca_dev->usb_buf[2];
+ sd->params.exposure.coarseExpHi = gspca_dev->usb_buf[3];
+ sd->params.exposure.redComp = gspca_dev->usb_buf[4];
+ sd->params.exposure.green1Comp = gspca_dev->usb_buf[5];
+ sd->params.exposure.green2Comp = gspca_dev->usb_buf[6];
+ sd->params.exposure.blueComp = gspca_dev->usb_buf[7];
+ break;
+
+ case CPIA_COMMAND_ReadMCPorts:
+ /* test button press */
+ a = ((gspca_dev->usb_buf[1] & 0x02) == 0);
+ if (a != sd->params.qx3.button) {
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, a);
+ input_sync(gspca_dev->input_dev);
+#endif
+ sd->params.qx3.button = a;
+ }
+ if (sd->params.qx3.button) {
+ /* button pressed - unlock the latch */
+ do_command(gspca_dev, CPIA_COMMAND_WriteMCPort,
+ 3, 0xdf, 0xdf, 0);
+ do_command(gspca_dev, CPIA_COMMAND_WriteMCPort,
+ 3, 0xff, 0xff, 0);
+ }
+
+ /* test whether microscope is cradled */
+ sd->params.qx3.cradled = ((gspca_dev->usb_buf[2] & 0x40) == 0);
+ break;
+ }
+
+ return 0;
+}
+
+/* send a command to the camera with an additional data transaction */
+static int do_command_extended(struct gspca_dev *gspca_dev, u16 command,
+ u8 a, u8 b, u8 c, u8 d,
+ u8 e, u8 f, u8 g, u8 h,
+ u8 i, u8 j, u8 k, u8 l)
+{
+ u8 cmd[8];
+
+ cmd[0] = command >> 8;
+ cmd[1] = command & 0xff;
+ cmd[2] = a;
+ cmd[3] = b;
+ cmd[4] = c;
+ cmd[5] = d;
+ cmd[6] = 8;
+ cmd[7] = 0;
+ gspca_dev->usb_buf[0] = e;
+ gspca_dev->usb_buf[1] = f;
+ gspca_dev->usb_buf[2] = g;
+ gspca_dev->usb_buf[3] = h;
+ gspca_dev->usb_buf[4] = i;
+ gspca_dev->usb_buf[5] = j;
+ gspca_dev->usb_buf[6] = k;
+ gspca_dev->usb_buf[7] = l;
+
+ return cpia_usb_transferCmd(gspca_dev, cmd);
+}
+
+/* find_over_exposure
+ * Finds a suitable value of OverExposure for use with SetFlickerCtrl
+ * Some calculation is required because this value changes with the brightness
+ * set with SetColourParameters
+ *
+ * Parameters: Brightness - last brightness value set with SetColourParameters
+ *
+ * Returns: OverExposure value to use with SetFlickerCtrl
+ */
+#define FLICKER_MAX_EXPOSURE 250
+#define FLICKER_ALLOWABLE_OVER_EXPOSURE 146
+#define FLICKER_BRIGHTNESS_CONSTANT 59
+static int find_over_exposure(int brightness)
+{
+ int MaxAllowableOverExposure, OverExposure;
+
+ MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness -
+ FLICKER_BRIGHTNESS_CONSTANT;
+
+ if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE)
+ OverExposure = MaxAllowableOverExposure;
+ else
+ OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE;
+
+ return OverExposure;
+}
+#undef FLICKER_MAX_EXPOSURE
+#undef FLICKER_ALLOWABLE_OVER_EXPOSURE
+#undef FLICKER_BRIGHTNESS_CONSTANT
+
+/* initialise cam_data structure */
+static void reset_camera_params(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam_params *params = &sd->params;
+
+ /* The following parameter values are the defaults from
+ * "Software Developer's Guide for CPiA Cameras". Any changes
+ * to the defaults are noted in comments. */
+ params->colourParams.brightness = BRIGHTNESS_DEF;
+ params->colourParams.contrast = CONTRAST_DEF;
+ params->colourParams.saturation = SATURATION_DEF;
+ params->exposure.gainMode = 4;
+ params->exposure.expMode = 2; /* AEC */
+ params->exposure.compMode = 1;
+ params->exposure.centreWeight = 1;
+ params->exposure.gain = 0;
+ params->exposure.fineExp = 0;
+ params->exposure.coarseExpLo = 185;
+ params->exposure.coarseExpHi = 0;
+ params->exposure.redComp = COMP_RED;
+ params->exposure.green1Comp = COMP_GREEN1;
+ params->exposure.green2Comp = COMP_GREEN2;
+ params->exposure.blueComp = COMP_BLUE;
+ params->colourBalance.balanceMode = 2; /* ACB */
+ params->colourBalance.redGain = 32;
+ params->colourBalance.greenGain = 6;
+ params->colourBalance.blueGain = 92;
+ params->apcor.gain1 = 0x18;
+ params->apcor.gain2 = 0x16;
+ params->apcor.gain4 = 0x24;
+ params->apcor.gain8 = 0x34;
+ params->flickerControl.flickerMode = 0;
+ params->flickerControl.disabled = 1;
+
+ params->flickerControl.coarseJump =
+ flicker_jumps[sd->mainsFreq]
+ [params->sensorFps.baserate]
+ [params->sensorFps.divisor];
+ params->flickerControl.allowableOverExposure =
+ find_over_exposure(params->colourParams.brightness);
+ params->vlOffset.gain1 = 20;
+ params->vlOffset.gain2 = 24;
+ params->vlOffset.gain4 = 26;
+ params->vlOffset.gain8 = 26;
+ params->compressionParams.hysteresis = 3;
+ params->compressionParams.threshMax = 11;
+ params->compressionParams.smallStep = 1;
+ params->compressionParams.largeStep = 3;
+ params->compressionParams.decimationHysteresis = 2;
+ params->compressionParams.frDiffStepThresh = 5;
+ params->compressionParams.qDiffStepThresh = 3;
+ params->compressionParams.decimationThreshMod = 2;
+ /* End of default values from Software Developer's Guide */
+
+ /* Set Sensor FPS to 15fps. This seems better than 30fps
+ * for indoor lighting. */
+ params->sensorFps.divisor = 1;
+ params->sensorFps.baserate = 1;
+
+ params->yuvThreshold.yThreshold = 6; /* From windows driver */
+ params->yuvThreshold.uvThreshold = 6; /* From windows driver */
+
+ params->format.subSample = SUBSAMPLE_420;
+ params->format.yuvOrder = YUVORDER_YUYV;
+
+ params->compression.mode = CPIA_COMPRESSION_AUTO;
+ params->compression.decimation = NO_DECIMATION;
+
+ params->compressionTarget.frTargeting = COMP_TARGET_DEF;
+ params->compressionTarget.targetFR = 15; /* From windows driver */
+ params->compressionTarget.targetQ = 5; /* From windows driver */
+
+ params->qx3.qx3_detected = 0;
+ params->qx3.toplight = 0;
+ params->qx3.bottomlight = 0;
+ params->qx3.button = 0;
+ params->qx3.cradled = 0;
+}
+
+static void printstatus(struct cam_params *params)
+{
+ PDEBUG(D_PROBE, "status: %02x %02x %02x %02x %02x %02x %02x %02x",
+ params->status.systemState, params->status.grabState,
+ params->status.streamState, params->status.fatalError,
+ params->status.cmdError, params->status.debugFlags,
+ params->status.vpStatus, params->status.errorCode);
+}
+
+static int goto_low_power(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
+
+ ret = do_command(gspca_dev, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ ret = do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ if (sd->params.status.systemState != LO_POWER_STATE) {
+ if (sd->params.status.systemState != WARM_BOOT_STATE) {
+ PDEBUG(D_ERR,
+ "unexpected state after lo power cmd: %02x",
+ sd->params.status.systemState);
+ printstatus(&sd->params);
+ }
+ return -EIO;
+ }
+
+ PDEBUG(D_CONF, "camera now in LOW power state");
+ return 0;
+}
+
+static int goto_high_power(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
+
+ ret = do_command(gspca_dev, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ msleep_interruptible(40); /* windows driver does it too */
+
+ if (signal_pending(current))
+ return -EINTR;
+
+ do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ if (sd->params.status.systemState != HI_POWER_STATE) {
+ PDEBUG(D_ERR, "unexpected state after hi power cmd: %02x",
+ sd->params.status.systemState);
+ printstatus(&sd->params);
+ return -EIO;
+ }
+
+ PDEBUG(D_CONF, "camera now in HIGH power state");
+ return 0;
+}
+
+static int get_version_information(struct gspca_dev *gspca_dev)
+{
+ int ret;
+
+ /* GetCPIAVersion */
+ ret = do_command(gspca_dev, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ /* GetPnPID */
+ return do_command(gspca_dev, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0);
+}
+
+static int save_camera_state(struct gspca_dev *gspca_dev)
+{
+ int ret;
+
+ ret = do_command(gspca_dev, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ return do_command(gspca_dev, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
+}
+
+static int command_setformat(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
+
+ ret = do_command(gspca_dev, CPIA_COMMAND_SetFormat,
+ sd->params.format.videoSize,
+ sd->params.format.subSample,
+ sd->params.format.yuvOrder, 0);
+ if (ret)
+ return ret;
+
+ return do_command(gspca_dev, CPIA_COMMAND_SetROI,
+ sd->params.roi.colStart, sd->params.roi.colEnd,
+ sd->params.roi.rowStart, sd->params.roi.rowEnd);
+}
+
+static int command_setcolourparams(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ return do_command(gspca_dev, CPIA_COMMAND_SetColourParams,
+ sd->params.colourParams.brightness,
+ sd->params.colourParams.contrast,
+ sd->params.colourParams.saturation, 0);
+}
+
+static int command_setapcor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ return do_command(gspca_dev, CPIA_COMMAND_SetApcor,
+ sd->params.apcor.gain1,
+ sd->params.apcor.gain2,
+ sd->params.apcor.gain4,
+ sd->params.apcor.gain8);
+}
+
+static int command_setvloffset(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ return do_command(gspca_dev, CPIA_COMMAND_SetVLOffset,
+ sd->params.vlOffset.gain1,
+ sd->params.vlOffset.gain2,
+ sd->params.vlOffset.gain4,
+ sd->params.vlOffset.gain8);
+}
+
+static int command_setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
+
+ ret = do_command_extended(gspca_dev, CPIA_COMMAND_SetExposure,
+ sd->params.exposure.gainMode,
+ 1,
+ sd->params.exposure.compMode,
+ sd->params.exposure.centreWeight,
+ sd->params.exposure.gain,
+ sd->params.exposure.fineExp,
+ sd->params.exposure.coarseExpLo,
+ sd->params.exposure.coarseExpHi,
+ sd->params.exposure.redComp,
+ sd->params.exposure.green1Comp,
+ sd->params.exposure.green2Comp,
+ sd->params.exposure.blueComp);
+ if (ret)
+ return ret;
+
+ if (sd->params.exposure.expMode != 1) {
+ ret = do_command_extended(gspca_dev, CPIA_COMMAND_SetExposure,
+ 0,
+ sd->params.exposure.expMode,
+ 0, 0,
+ sd->params.exposure.gain,
+ sd->params.exposure.fineExp,
+ sd->params.exposure.coarseExpLo,
+ sd->params.exposure.coarseExpHi,
+ 0, 0, 0, 0);
+ }
+
+ return ret;
+}
+
+static int command_setcolourbalance(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->params.colourBalance.balanceMode == 1) {
+ int ret;
+
+ ret = do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
+ 1,
+ sd->params.colourBalance.redGain,
+ sd->params.colourBalance.greenGain,
+ sd->params.colourBalance.blueGain);
+ if (ret)
+ return ret;
+
+ return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
+ 3, 0, 0, 0);
+ }
+ if (sd->params.colourBalance.balanceMode == 2) {
+ return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
+ 2, 0, 0, 0);
+ }
+ if (sd->params.colourBalance.balanceMode == 3) {
+ return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
+ 3, 0, 0, 0);
+ }
+
+ return -EINVAL;
+}
+
+static int command_setcompressiontarget(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return do_command(gspca_dev, CPIA_COMMAND_SetCompressionTarget,
+ sd->params.compressionTarget.frTargeting,
+ sd->params.compressionTarget.targetFR,
+ sd->params.compressionTarget.targetQ, 0);
+}
+
+static int command_setyuvtresh(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return do_command(gspca_dev, CPIA_COMMAND_SetYUVThresh,
+ sd->params.yuvThreshold.yThreshold,
+ sd->params.yuvThreshold.uvThreshold, 0, 0);
+}
+
+static int command_setcompressionparams(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return do_command_extended(gspca_dev,
+ CPIA_COMMAND_SetCompressionParams,
+ 0, 0, 0, 0,
+ sd->params.compressionParams.hysteresis,
+ sd->params.compressionParams.threshMax,
+ sd->params.compressionParams.smallStep,
+ sd->params.compressionParams.largeStep,
+ sd->params.compressionParams.decimationHysteresis,
+ sd->params.compressionParams.frDiffStepThresh,
+ sd->params.compressionParams.qDiffStepThresh,
+ sd->params.compressionParams.decimationThreshMod);
+}
+
+static int command_setcompression(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return do_command(gspca_dev, CPIA_COMMAND_SetCompression,
+ sd->params.compression.mode,
+ sd->params.compression.decimation, 0, 0);
+}
+
+static int command_setsensorfps(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return do_command(gspca_dev, CPIA_COMMAND_SetSensorFPS,
+ sd->params.sensorFps.divisor,
+ sd->params.sensorFps.baserate, 0, 0);
+}
+
+static int command_setflickerctrl(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return do_command(gspca_dev, CPIA_COMMAND_SetFlickerCtrl,
+ sd->params.flickerControl.flickerMode,
+ sd->params.flickerControl.coarseJump,
+ sd->params.flickerControl.allowableOverExposure,
+ 0);
+}
+
+static int command_setecptiming(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return do_command(gspca_dev, CPIA_COMMAND_SetECPTiming,
+ sd->params.ecpTiming, 0, 0, 0);
+}
+
+static int command_pause(struct gspca_dev *gspca_dev)
+{
+ return do_command(gspca_dev, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0);
+}
+
+static int command_resume(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return do_command(gspca_dev, CPIA_COMMAND_InitStreamCap,
+ 0, sd->params.streamStartLine, 0, 0);
+}
+
+static int command_setlights(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret, p1, p2;
+
+ if (!sd->params.qx3.qx3_detected)
+ return 0;
+
+ p1 = (sd->params.qx3.bottomlight == 0) << 1;
+ p2 = (sd->params.qx3.toplight == 0) << 3;
+
+ ret = do_command(gspca_dev, CPIA_COMMAND_WriteVCReg,
+ 0x90, 0x8f, 0x50, 0);
+ if (ret)
+ return ret;
+
+ return do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, 2, 0,
+ p1 | p2 | 0xe0, 0);
+}
+
+static int set_flicker(struct gspca_dev *gspca_dev, int on, int apply)
+{
+ /* Everything in here is from the Windows driver */
+/* define for compgain calculation */
+#if 0
+#define COMPGAIN(base, curexp, newexp) \
+ (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5)
+#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
+ (u16)((float)curexp * (float)(u8)(curcomp + 128) / \
+ (float)(u8)(basecomp - 128))
+#else
+ /* equivalent functions without floating point math */
+#define COMPGAIN(base, curexp, newexp) \
+ (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2 * newexp)))
+#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
+ (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128)))
+#endif
+
+ struct sd *sd = (struct sd *) gspca_dev;
+ int currentexp = sd->params.exposure.coarseExpLo +
+ sd->params.exposure.coarseExpHi * 256;
+ int ret, startexp;
+
+ if (on) {
+ int cj = sd->params.flickerControl.coarseJump;
+ sd->params.flickerControl.flickerMode = 1;
+ sd->params.flickerControl.disabled = 0;
+ if (sd->params.exposure.expMode != 2) {
+ sd->params.exposure.expMode = 2;
+ sd->exposure_status = EXPOSURE_NORMAL;
+ }
+ currentexp = currentexp << sd->params.exposure.gain;
+ sd->params.exposure.gain = 0;
+ /* round down current exposure to nearest value */
+ startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj;
+ if (startexp < 1)
+ startexp = 1;
+ startexp = (startexp * cj) - 1;
+ if (FIRMWARE_VERSION(1, 2))
+ while (startexp > MAX_EXP_102)
+ startexp -= cj;
+ else
+ while (startexp > MAX_EXP)
+ startexp -= cj;
+ sd->params.exposure.coarseExpLo = startexp & 0xff;
+ sd->params.exposure.coarseExpHi = startexp >> 8;
+ if (currentexp > startexp) {
+ if (currentexp > (2 * startexp))
+ currentexp = 2 * startexp;
+ sd->params.exposure.redComp =
+ COMPGAIN(COMP_RED, currentexp, startexp);
+ sd->params.exposure.green1Comp =
+ COMPGAIN(COMP_GREEN1, currentexp, startexp);
+ sd->params.exposure.green2Comp =
+ COMPGAIN(COMP_GREEN2, currentexp, startexp);
+ sd->params.exposure.blueComp =
+ COMPGAIN(COMP_BLUE, currentexp, startexp);
+ } else {
+ sd->params.exposure.redComp = COMP_RED;
+ sd->params.exposure.green1Comp = COMP_GREEN1;
+ sd->params.exposure.green2Comp = COMP_GREEN2;
+ sd->params.exposure.blueComp = COMP_BLUE;
+ }
+ if (FIRMWARE_VERSION(1, 2))
+ sd->params.exposure.compMode = 0;
+ else
+ sd->params.exposure.compMode = 1;
+
+ sd->params.apcor.gain1 = 0x18;
+ sd->params.apcor.gain2 = 0x18;
+ sd->params.apcor.gain4 = 0x16;
+ sd->params.apcor.gain8 = 0x14;
+ } else {
+ sd->params.flickerControl.flickerMode = 0;
+ sd->params.flickerControl.disabled = 1;
+ /* Average equivalent coarse for each comp channel */
+ startexp = EXP_FROM_COMP(COMP_RED,
+ sd->params.exposure.redComp, currentexp);
+ startexp += EXP_FROM_COMP(COMP_GREEN1,
+ sd->params.exposure.green1Comp, currentexp);
+ startexp += EXP_FROM_COMP(COMP_GREEN2,
+ sd->params.exposure.green2Comp, currentexp);
+ startexp += EXP_FROM_COMP(COMP_BLUE,
+ sd->params.exposure.blueComp, currentexp);
+ startexp = startexp >> 2;
+ while (startexp > MAX_EXP && sd->params.exposure.gain <
+ sd->params.exposure.gainMode - 1) {
+ startexp = startexp >> 1;
+ ++sd->params.exposure.gain;
+ }
+ if (FIRMWARE_VERSION(1, 2) && startexp > MAX_EXP_102)
+ startexp = MAX_EXP_102;
+ if (startexp > MAX_EXP)
+ startexp = MAX_EXP;
+ sd->params.exposure.coarseExpLo = startexp & 0xff;
+ sd->params.exposure.coarseExpHi = startexp >> 8;
+ sd->params.exposure.redComp = COMP_RED;
+ sd->params.exposure.green1Comp = COMP_GREEN1;
+ sd->params.exposure.green2Comp = COMP_GREEN2;
+ sd->params.exposure.blueComp = COMP_BLUE;
+ sd->params.exposure.compMode = 1;
+ sd->params.apcor.gain1 = 0x18;
+ sd->params.apcor.gain2 = 0x16;
+ sd->params.apcor.gain4 = 0x24;
+ sd->params.apcor.gain8 = 0x34;
+ }
+ sd->params.vlOffset.gain1 = 20;
+ sd->params.vlOffset.gain2 = 24;
+ sd->params.vlOffset.gain4 = 26;
+ sd->params.vlOffset.gain8 = 26;
+
+ if (apply) {
+ ret = command_setexposure(gspca_dev);
+ if (ret)
+ return ret;
+
+ ret = command_setapcor(gspca_dev);
+ if (ret)
+ return ret;
+
+ ret = command_setvloffset(gspca_dev);
+ if (ret)
+ return ret;
+
+ ret = command_setflickerctrl(gspca_dev);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+#undef EXP_FROM_COMP
+#undef COMPGAIN
+}
+
+/* monitor the exposure and adjust the sensor frame rate if needed */
+static void monitor_exposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 exp_acc, bcomp, cmd[8];
+ int ret, light_exp, dark_exp, very_dark_exp;
+ int old_exposure, new_exposure, framerate;
+ int setfps = 0, setexp = 0, setflicker = 0;
+
+ /* get necessary stats and register settings from camera */
+ /* do_command can't handle this, so do it ourselves */
+ cmd[0] = CPIA_COMMAND_ReadVPRegs >> 8;
+ cmd[1] = CPIA_COMMAND_ReadVPRegs & 0xff;
+ cmd[2] = 30;
+ cmd[3] = 4;
+ cmd[4] = 9;
+ cmd[5] = 8;
+ cmd[6] = 8;
+ cmd[7] = 0;
+ ret = cpia_usb_transferCmd(gspca_dev, cmd);
+ if (ret) {
+ pr_err("ReadVPRegs(30,4,9,8) - failed: %d\n", ret);
+ return;
+ }
+ exp_acc = gspca_dev->usb_buf[0];
+ bcomp = gspca_dev->usb_buf[1];
+
+ light_exp = sd->params.colourParams.brightness +
+ TC - 50 + EXP_ACC_LIGHT;
+ if (light_exp > 255)
+ light_exp = 255;
+ dark_exp = sd->params.colourParams.brightness +
+ TC - 50 - EXP_ACC_DARK;
+ if (dark_exp < 0)
+ dark_exp = 0;
+ very_dark_exp = dark_exp / 2;
+
+ old_exposure = sd->params.exposure.coarseExpHi * 256 +
+ sd->params.exposure.coarseExpLo;
+
+ if (!sd->params.flickerControl.disabled) {
+ /* Flicker control on */
+ int max_comp = FIRMWARE_VERSION(1, 2) ? MAX_COMP :
+ HIGH_COMP_102;
+ bcomp += 128; /* decode */
+ if (bcomp >= max_comp && exp_acc < dark_exp) {
+ /* dark */
+ if (exp_acc < very_dark_exp) {
+ /* very dark */
+ if (sd->exposure_status == EXPOSURE_VERY_DARK)
+ ++sd->exposure_count;
+ else {
+ sd->exposure_status =
+ EXPOSURE_VERY_DARK;
+ sd->exposure_count = 1;
+ }
+ } else {
+ /* just dark */
+ if (sd->exposure_status == EXPOSURE_DARK)
+ ++sd->exposure_count;
+ else {
+ sd->exposure_status = EXPOSURE_DARK;
+ sd->exposure_count = 1;
+ }
+ }
+ } else if (old_exposure <= LOW_EXP || exp_acc > light_exp) {
+ /* light */
+ if (old_exposure <= VERY_LOW_EXP) {
+ /* very light */
+ if (sd->exposure_status == EXPOSURE_VERY_LIGHT)
+ ++sd->exposure_count;
+ else {
+ sd->exposure_status =
+ EXPOSURE_VERY_LIGHT;
+ sd->exposure_count = 1;
+ }
+ } else {
+ /* just light */
+ if (sd->exposure_status == EXPOSURE_LIGHT)
+ ++sd->exposure_count;
+ else {
+ sd->exposure_status = EXPOSURE_LIGHT;
+ sd->exposure_count = 1;
+ }
+ }
+ } else {
+ /* not dark or light */
+ sd->exposure_status = EXPOSURE_NORMAL;
+ }
+ } else {
+ /* Flicker control off */
+ if (old_exposure >= MAX_EXP && exp_acc < dark_exp) {
+ /* dark */
+ if (exp_acc < very_dark_exp) {
+ /* very dark */
+ if (sd->exposure_status == EXPOSURE_VERY_DARK)
+ ++sd->exposure_count;
+ else {
+ sd->exposure_status =
+ EXPOSURE_VERY_DARK;
+ sd->exposure_count = 1;
+ }
+ } else {
+ /* just dark */
+ if (sd->exposure_status == EXPOSURE_DARK)
+ ++sd->exposure_count;
+ else {
+ sd->exposure_status = EXPOSURE_DARK;
+ sd->exposure_count = 1;
+ }
+ }
+ } else if (old_exposure <= LOW_EXP || exp_acc > light_exp) {
+ /* light */
+ if (old_exposure <= VERY_LOW_EXP) {
+ /* very light */
+ if (sd->exposure_status == EXPOSURE_VERY_LIGHT)
+ ++sd->exposure_count;
+ else {
+ sd->exposure_status =
+ EXPOSURE_VERY_LIGHT;
+ sd->exposure_count = 1;
+ }
+ } else {
+ /* just light */
+ if (sd->exposure_status == EXPOSURE_LIGHT)
+ ++sd->exposure_count;
+ else {
+ sd->exposure_status = EXPOSURE_LIGHT;
+ sd->exposure_count = 1;
+ }
+ }
+ } else {
+ /* not dark or light */
+ sd->exposure_status = EXPOSURE_NORMAL;
+ }
+ }
+
+ framerate = atomic_read(&sd->fps);
+ if (framerate > 30 || framerate < 1)
+ framerate = 1;
+
+ if (!sd->params.flickerControl.disabled) {
+ /* Flicker control on */
+ if ((sd->exposure_status == EXPOSURE_VERY_DARK ||
+ sd->exposure_status == EXPOSURE_DARK) &&
+ sd->exposure_count >= DARK_TIME * framerate &&
+ sd->params.sensorFps.divisor < 2) {
+
+ /* dark for too long */
+ ++sd->params.sensorFps.divisor;
+ setfps = 1;
+
+ sd->params.flickerControl.coarseJump =
+ flicker_jumps[sd->mainsFreq]
+ [sd->params.sensorFps.baserate]
+ [sd->params.sensorFps.divisor];
+ setflicker = 1;
+
+ new_exposure = sd->params.flickerControl.coarseJump-1;
+ while (new_exposure < old_exposure / 2)
+ new_exposure +=
+ sd->params.flickerControl.coarseJump;
+ sd->params.exposure.coarseExpLo = new_exposure & 0xff;
+ sd->params.exposure.coarseExpHi = new_exposure >> 8;
+ setexp = 1;
+ sd->exposure_status = EXPOSURE_NORMAL;
+ PDEBUG(D_CONF, "Automatically decreasing sensor_fps");
+
+ } else if ((sd->exposure_status == EXPOSURE_VERY_LIGHT ||
+ sd->exposure_status == EXPOSURE_LIGHT) &&
+ sd->exposure_count >= LIGHT_TIME * framerate &&
+ sd->params.sensorFps.divisor > 0) {
+
+ /* light for too long */
+ int max_exp = FIRMWARE_VERSION(1, 2) ? MAX_EXP_102 :
+ MAX_EXP;
+ --sd->params.sensorFps.divisor;
+ setfps = 1;
+
+ sd->params.flickerControl.coarseJump =
+ flicker_jumps[sd->mainsFreq]
+ [sd->params.sensorFps.baserate]
+ [sd->params.sensorFps.divisor];
+ setflicker = 1;
+
+ new_exposure = sd->params.flickerControl.coarseJump-1;
+ while (new_exposure < 2 * old_exposure &&
+ new_exposure +
+ sd->params.flickerControl.coarseJump < max_exp)
+ new_exposure +=
+ sd->params.flickerControl.coarseJump;
+ sd->params.exposure.coarseExpLo = new_exposure & 0xff;
+ sd->params.exposure.coarseExpHi = new_exposure >> 8;
+ setexp = 1;
+ sd->exposure_status = EXPOSURE_NORMAL;
+ PDEBUG(D_CONF, "Automatically increasing sensor_fps");
+ }
+ } else {
+ /* Flicker control off */
+ if ((sd->exposure_status == EXPOSURE_VERY_DARK ||
+ sd->exposure_status == EXPOSURE_DARK) &&
+ sd->exposure_count >= DARK_TIME * framerate &&
+ sd->params.sensorFps.divisor < 2) {
+
+ /* dark for too long */
+ ++sd->params.sensorFps.divisor;
+ setfps = 1;
+
+ if (sd->params.exposure.gain > 0) {
+ --sd->params.exposure.gain;
+ setexp = 1;
+ }
+ sd->exposure_status = EXPOSURE_NORMAL;
+ PDEBUG(D_CONF, "Automatically decreasing sensor_fps");
+
+ } else if ((sd->exposure_status == EXPOSURE_VERY_LIGHT ||
+ sd->exposure_status == EXPOSURE_LIGHT) &&
+ sd->exposure_count >= LIGHT_TIME * framerate &&
+ sd->params.sensorFps.divisor > 0) {
+
+ /* light for too long */
+ --sd->params.sensorFps.divisor;
+ setfps = 1;
+
+ if (sd->params.exposure.gain <
+ sd->params.exposure.gainMode - 1) {
+ ++sd->params.exposure.gain;
+ setexp = 1;
+ }
+ sd->exposure_status = EXPOSURE_NORMAL;
+ PDEBUG(D_CONF, "Automatically increasing sensor_fps");
+ }
+ }
+
+ if (setexp)
+ command_setexposure(gspca_dev);
+
+ if (setfps)
+ command_setsensorfps(gspca_dev);
+
+ if (setflicker)
+ command_setflickerctrl(gspca_dev);
+}
+
+/*-----------------------------------------------------------------*/
+/* if flicker is switched off, this function switches it back on.It checks,
+ however, that conditions are suitable before restarting it.
+ This should only be called for firmware version 1.2.
+
+ It also adjust the colour balance when an exposure step is detected - as
+ long as flicker is running
+*/
+static void restart_flicker(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int cam_exposure, old_exp;
+
+ if (!FIRMWARE_VERSION(1, 2))
+ return;
+
+ cam_exposure = atomic_read(&sd->cam_exposure);
+
+ if (sd->params.flickerControl.flickerMode == 0 ||
+ cam_exposure == 0)
+ return;
+
+ old_exp = sd->params.exposure.coarseExpLo +
+ sd->params.exposure.coarseExpHi*256;
+ /*
+ see how far away camera exposure is from a valid
+ flicker exposure value
+ */
+ cam_exposure %= sd->params.flickerControl.coarseJump;
+ if (!sd->params.flickerControl.disabled &&
+ cam_exposure <= sd->params.flickerControl.coarseJump - 3) {
+ /* Flicker control auto-disabled */
+ sd->params.flickerControl.disabled = 1;
+ }
+
+ if (sd->params.flickerControl.disabled &&
+ old_exp > sd->params.flickerControl.coarseJump +
+ ROUND_UP_EXP_FOR_FLICKER) {
+ /* exposure is now high enough to switch
+ flicker control back on */
+ set_flicker(gspca_dev, 1, 1);
+ }
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct cam *cam;
+
+ reset_camera_params(gspca_dev);
+
+ PDEBUG(D_PROBE, "cpia CPiA camera detected (vid/pid 0x%04X:0x%04X)",
+ id->idVendor, id->idProduct);
+
+ cam = &gspca_dev->cam;
+ cam->cam_mode = mode;
+ cam->nmodes = ARRAY_SIZE(mode);
+
+ sd_setfreq(gspca_dev, FREQ_DEF);
+
+ return 0;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int priv, ret;
+
+ /* Start the camera in low power mode */
+ if (goto_low_power(gspca_dev)) {
+ if (sd->params.status.systemState != WARM_BOOT_STATE) {
+ PDEBUG(D_ERR, "unexpected systemstate: %02x",
+ sd->params.status.systemState);
+ printstatus(&sd->params);
+ return -ENODEV;
+ }
+
+ /* FIXME: this is just dirty trial and error */
+ ret = goto_high_power(gspca_dev);
+ if (ret)
+ return ret;
+
+ ret = do_command(gspca_dev, CPIA_COMMAND_DiscardFrame,
+ 0, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ ret = goto_low_power(gspca_dev);
+ if (ret)
+ return ret;
+ }
+
+ /* procedure described in developer's guide p3-28 */
+
+ /* Check the firmware version. */
+ sd->params.version.firmwareVersion = 0;
+ get_version_information(gspca_dev);
+ if (sd->params.version.firmwareVersion != 1) {
+ PDEBUG(D_ERR, "only firmware version 1 is supported (got: %d)",
+ sd->params.version.firmwareVersion);
+ return -ENODEV;
+ }
+
+ /* A bug in firmware 1-02 limits gainMode to 2 */
+ if (sd->params.version.firmwareRevision <= 2 &&
+ sd->params.exposure.gainMode > 2) {
+ sd->params.exposure.gainMode = 2;
+ }
+
+ /* set QX3 detected flag */
+ sd->params.qx3.qx3_detected = (sd->params.pnpID.vendor == 0x0813 &&
+ sd->params.pnpID.product == 0x0001);
+
+ /* The fatal error checking should be done after
+ * the camera powers up (developer's guide p 3-38) */
+
+ /* Set streamState before transition to high power to avoid bug
+ * in firmware 1-02 */
+ ret = do_command(gspca_dev, CPIA_COMMAND_ModifyCameraStatus,
+ STREAMSTATE, 0, STREAM_NOT_READY, 0);
+ if (ret)
+ return ret;
+
+ /* GotoHiPower */
+ ret = goto_high_power(gspca_dev);
+ if (ret)
+ return ret;
+
+ /* Check the camera status */
+ ret = do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ if (sd->params.status.fatalError) {
+ PDEBUG(D_ERR, "fatal_error: %04x, vp_status: %04x",
+ sd->params.status.fatalError,
+ sd->params.status.vpStatus);
+ return -EIO;
+ }
+
+ /* VPVersion can't be retrieved before the camera is in HiPower,
+ * so get it here instead of in get_version_information. */
+ ret = do_command(gspca_dev, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ /* Determine video mode settings */
+ sd->params.streamStartLine = 120;
+
+ priv = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+ if (priv & 0x01) { /* crop */
+ sd->params.roi.colStart = 2;
+ sd->params.roi.rowStart = 6;
+ } else {
+ sd->params.roi.colStart = 0;
+ sd->params.roi.rowStart = 0;
+ }
+
+ if (priv & 0x02) { /* quarter */
+ sd->params.format.videoSize = VIDEOSIZE_QCIF;
+ sd->params.roi.colStart /= 2;
+ sd->params.roi.rowStart /= 2;
+ sd->params.streamStartLine /= 2;
+ } else
+ sd->params.format.videoSize = VIDEOSIZE_CIF;
+
+ sd->params.roi.colEnd = sd->params.roi.colStart +
+ (gspca_dev->width >> 3);
+ sd->params.roi.rowEnd = sd->params.roi.rowStart +
+ (gspca_dev->height >> 2);
+
+ /* And now set the camera to a known state */
+ ret = do_command(gspca_dev, CPIA_COMMAND_SetGrabMode,
+ CPIA_GRAB_CONTINEOUS, 0, 0, 0);
+ if (ret)
+ return ret;
+ /* We start with compression disabled, as we need one uncompressed
+ frame to handle later compressed frames */
+ ret = do_command(gspca_dev, CPIA_COMMAND_SetCompression,
+ CPIA_COMPRESSION_NONE,
+ NO_DECIMATION, 0, 0);
+ if (ret)
+ return ret;
+ ret = command_setcompressiontarget(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setcolourparams(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setformat(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setyuvtresh(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setecptiming(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setcompressionparams(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setexposure(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setcolourbalance(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setsensorfps(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setapcor(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setflickerctrl(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setvloffset(gspca_dev);
+ if (ret)
+ return ret;
+
+ /* Start stream */
+ ret = command_resume(gspca_dev);
+ if (ret)
+ return ret;
+
+ /* Wait 6 frames before turning compression on for the sensor to get
+ all settings and AEC/ACB to settle */
+ sd->first_frame = 6;
+ sd->exposure_status = EXPOSURE_NORMAL;
+ sd->exposure_count = 0;
+ atomic_set(&sd->cam_exposure, 0);
+ atomic_set(&sd->fps, 0);
+
+ return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ command_pause(gspca_dev);
+
+ /* save camera state for later open (developers guide ch 3.5.3) */
+ save_camera_state(gspca_dev);
+
+ /* GotoLoPower */
+ goto_low_power(gspca_dev);
+
+ /* Update the camera status */
+ do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ /* If the last button state is pressed, release it now! */
+ if (sd->params.qx3.button) {
+ /* The camera latch will hold the pressed state until we reset
+ the latch, so we do not reset sd->params.qx3.button now, to
+ avoid a false keypress being reported the next sd_start */
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ }
+#endif
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
+
+ /* Start / Stop the camera to make sure we are talking to
+ a supported camera, and to get some information from it
+ to print. */
+ ret = sd_start(gspca_dev);
+ if (ret)
+ return ret;
+
+ /* Ensure the QX3 illuminators' states are restored upon resume,
+ or disable the illuminator controls, if this isn't a QX3 */
+ if (sd->params.qx3.qx3_detected)
+ command_setlights(gspca_dev);
+ else
+ gspca_dev->ctrl_dis |=
+ ((1 << ILLUMINATORS_1_IDX) | (1 << ILLUMINATORS_2_IDX));
+
+ sd_stopN(gspca_dev);
+
+ PDEBUG(D_PROBE, "CPIA Version: %d.%02d (%d.%d)",
+ sd->params.version.firmwareVersion,
+ sd->params.version.firmwareRevision,
+ sd->params.version.vcVersion,
+ sd->params.version.vcRevision);
+ PDEBUG(D_PROBE, "CPIA PnP-ID: %04x:%04x:%04x",
+ sd->params.pnpID.vendor, sd->params.pnpID.product,
+ sd->params.pnpID.deviceRevision);
+ PDEBUG(D_PROBE, "VP-Version: %d.%d %04x",
+ sd->params.vpVersion.vpVersion,
+ sd->params.vpVersion.vpRevision,
+ sd->params.vpVersion.cameraHeadID);
+
+ return 0;
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data,
+ int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* Check for SOF */
+ if (len >= 64 &&
+ data[0] == MAGIC_0 && data[1] == MAGIC_1 &&
+ data[16] == sd->params.format.videoSize &&
+ data[17] == sd->params.format.subSample &&
+ data[18] == sd->params.format.yuvOrder &&
+ data[24] == sd->params.roi.colStart &&
+ data[25] == sd->params.roi.colEnd &&
+ data[26] == sd->params.roi.rowStart &&
+ data[27] == sd->params.roi.rowEnd) {
+ u8 *image;
+
+ atomic_set(&sd->cam_exposure, data[39] * 2);
+ atomic_set(&sd->fps, data[41]);
+
+ /* Check for proper EOF for last frame */
+ image = gspca_dev->image;
+ if (image != NULL &&
+ gspca_dev->image_len > 4 &&
+ image[gspca_dev->image_len - 4] == 0xff &&
+ image[gspca_dev->image_len - 3] == 0xff &&
+ image[gspca_dev->image_len - 2] == 0xff &&
+ image[gspca_dev->image_len - 1] == 0xff)
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ NULL, 0);
+
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+ return;
+ }
+
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static void sd_dq_callback(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* Set the normal compression settings once we have captured a
+ few uncompressed frames (and AEC has hopefully settled) */
+ if (sd->first_frame) {
+ sd->first_frame--;
+ if (sd->first_frame == 0)
+ command_setcompression(gspca_dev);
+ }
+
+ /* Switch flicker control back on if it got turned off */
+ restart_flicker(gspca_dev);
+
+ /* If AEC is enabled, monitor the exposure and
+ adjust the sensor frame rate if needed */
+ if (sd->params.exposure.expMode == 2)
+ monitor_exposure(gspca_dev);
+
+ /* Update our knowledge of the camera state */
+ do_command(gspca_dev, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
+ do_command(gspca_dev, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0);
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
+
+ sd->params.colourParams.brightness = val;
+ sd->params.flickerControl.allowableOverExposure =
+ find_over_exposure(sd->params.colourParams.brightness);
+ if (gspca_dev->streaming) {
+ ret = command_setcolourparams(gspca_dev);
+ if (ret)
+ return ret;
+ return command_setflickerctrl(gspca_dev);
+ }
+ return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->params.colourParams.brightness;
+ return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->params.colourParams.contrast = val;
+ if (gspca_dev->streaming)
+ return command_setcolourparams(gspca_dev);
+
+ return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->params.colourParams.contrast;
+ return 0;
+}
+
+static int sd_setsaturation(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->params.colourParams.saturation = val;
+ if (gspca_dev->streaming)
+ return command_setcolourparams(gspca_dev);
+
+ return 0;
+}
+
+static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->params.colourParams.saturation;
+ return 0;
+}
+
+static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int on;
+
+ switch (val) {
+ case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
+ on = 0;
+ break;
+ case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
+ on = 1;
+ sd->mainsFreq = 0;
+ break;
+ case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
+ on = 1;
+ sd->mainsFreq = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sd->freq = val;
+ sd->params.flickerControl.coarseJump =
+ flicker_jumps[sd->mainsFreq]
+ [sd->params.sensorFps.baserate]
+ [sd->params.sensorFps.divisor];
+
+ return set_flicker(gspca_dev, on, gspca_dev->streaming);
+}
+
+static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->freq;
+ return 0;
+}
+
+static int sd_setcomptarget(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->params.compressionTarget.frTargeting = val;
+ if (gspca_dev->streaming)
+ return command_setcompressiontarget(gspca_dev);
+
+ return 0;
+}
+
+static int sd_getcomptarget(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->params.compressionTarget.frTargeting;
+ return 0;
+}
+
+static int sd_setilluminator(struct gspca_dev *gspca_dev, __s32 val, int n)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
+
+ if (!sd->params.qx3.qx3_detected)
+ return -EINVAL;
+
+ switch (n) {
+ case 1:
+ sd->params.qx3.bottomlight = val ? 1 : 0;
+ break;
+ case 2:
+ sd->params.qx3.toplight = val ? 1 : 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = command_setlights(gspca_dev);
+ if (ret && ret != -EINVAL)
+ ret = -EBUSY;
+
+ return ret;
+}
+
+static int sd_setilluminator1(struct gspca_dev *gspca_dev, __s32 val)
+{
+ return sd_setilluminator(gspca_dev, val, 1);
+}
+
+static int sd_setilluminator2(struct gspca_dev *gspca_dev, __s32 val)
+{
+ return sd_setilluminator(gspca_dev, val, 2);
+}
+
+static int sd_getilluminator(struct gspca_dev *gspca_dev, __s32 *val, int n)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (!sd->params.qx3.qx3_detected)
+ return -EINVAL;
+
+ switch (n) {
+ case 1:
+ *val = sd->params.qx3.bottomlight;
+ break;
+ case 2:
+ *val = sd->params.qx3.toplight;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int sd_getilluminator1(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ return sd_getilluminator(gspca_dev, val, 1);
+}
+
+static int sd_getilluminator2(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ return sd_getilluminator(gspca_dev, val, 2);
+}
+
+static int sd_querymenu(struct gspca_dev *gspca_dev,
+ struct v4l2_querymenu *menu)
+{
+ switch (menu->id) {
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ switch (menu->index) {
+ case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
+ strcpy((char *) menu->name, "NoFliker");
+ return 0;
+ case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
+ strcpy((char *) menu->name, "50 Hz");
+ return 0;
+ case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
+ strcpy((char *) menu->name, "60 Hz");
+ return 0;
+ }
+ break;
+ case V4L2_CID_COMP_TARGET:
+ switch (menu->index) {
+ case CPIA_COMPRESSION_TARGET_QUALITY:
+ strcpy((char *) menu->name, "Quality");
+ return 0;
+ case CPIA_COMPRESSION_TARGET_FRAMERATE:
+ strcpy((char *) menu->name, "Framerate");
+ return 0;
+ }
+ break;
+ }
+ return -EINVAL;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .dq_callback = sd_dq_callback,
+ .pkt_scan = sd_pkt_scan,
+ .querymenu = sd_querymenu,
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ .other_input = 1,
+#endif
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x0553, 0x0002)},
+ {USB_DEVICE(0x0813, 0x0001)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/video/gspca/etoms.c
new file mode 100644
index 00000000..81a4adbd
--- /dev/null
+++ b/drivers/media/video/gspca/etoms.c
@@ -0,0 +1,898 @@
+/*
+ * Etoms Et61x151 GPL Linux driver by Michel Xhaard (09/09/2004)
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "etoms"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("Etoms USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ unsigned char brightness;
+ unsigned char contrast;
+ unsigned char colors;
+ unsigned char autogain;
+
+ char sensor;
+#define SENSOR_PAS106 0
+#define SENSOR_TAS5130CXX 1
+ signed char ag_cnt;
+#define AG_CNT_START 13
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+
+static const struct ctrl sd_ctrls[] = {
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 1,
+ .maximum = 127,
+ .step = 1,
+#define BRIGHTNESS_DEF 63
+ .default_value = BRIGHTNESS_DEF,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+ {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+#define CONTRAST_DEF 127
+ .default_value = CONTRAST_DEF,
+ },
+ .set = sd_setcontrast,
+ .get = sd_getcontrast,
+ },
+#define COLOR_IDX 2
+ {
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Color",
+ .minimum = 0,
+ .maximum = 15,
+ .step = 1,
+#define COLOR_DEF 7
+ .default_value = COLOR_DEF,
+ },
+ .set = sd_setcolors,
+ .get = sd_getcolors,
+ },
+ {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto Gain",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define AUTOGAIN_DEF 1
+ .default_value = AUTOGAIN_DEF,
+ },
+ .set = sd_setautogain,
+ .get = sd_getautogain,
+ },
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+/* {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0}, */
+};
+
+static const struct v4l2_pix_format sif_mode[] = {
+ {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {352, 288, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+
+#define ETOMS_ALT_SIZE_1000 12
+
+#define ET_GPIO_DIR_CTRL 0x04 /* Control IO bit[0..5] (0 in 1 out) */
+#define ET_GPIO_OUT 0x05 /* Only IO data */
+#define ET_GPIO_IN 0x06 /* Read Only IO data */
+#define ET_RESET_ALL 0x03
+#define ET_ClCK 0x01
+#define ET_CTRL 0x02 /* enable i2c OutClck Powerdown mode */
+
+#define ET_COMP 0x12 /* Compression register */
+#define ET_MAXQt 0x13
+#define ET_MINQt 0x14
+#define ET_COMP_VAL0 0x02
+#define ET_COMP_VAL1 0x03
+
+#define ET_REG1d 0x1d
+#define ET_REG1e 0x1e
+#define ET_REG1f 0x1f
+#define ET_REG20 0x20
+#define ET_REG21 0x21
+#define ET_REG22 0x22
+#define ET_REG23 0x23
+#define ET_REG24 0x24
+#define ET_REG25 0x25
+/* base registers for luma calculation */
+#define ET_LUMA_CENTER 0x39
+
+#define ET_G_RED 0x4d
+#define ET_G_GREEN1 0x4e
+#define ET_G_BLUE 0x4f
+#define ET_G_GREEN2 0x50
+#define ET_G_GR_H 0x51
+#define ET_G_GB_H 0x52
+
+#define ET_O_RED 0x34
+#define ET_O_GREEN1 0x35
+#define ET_O_BLUE 0x36
+#define ET_O_GREEN2 0x37
+
+#define ET_SYNCHRO 0x68
+#define ET_STARTX 0x69
+#define ET_STARTY 0x6a
+#define ET_WIDTH_LOW 0x6b
+#define ET_HEIGTH_LOW 0x6c
+#define ET_W_H_HEIGTH 0x6d
+
+#define ET_REG6e 0x6e /* OBW */
+#define ET_REG6f 0x6f /* OBW */
+#define ET_REG70 0x70 /* OBW_AWB */
+#define ET_REG71 0x71 /* OBW_AWB */
+#define ET_REG72 0x72 /* OBW_AWB */
+#define ET_REG73 0x73 /* Clkdelay ns */
+#define ET_REG74 0x74 /* test pattern */
+#define ET_REG75 0x75 /* test pattern */
+
+#define ET_I2C_CLK 0x8c
+#define ET_PXL_CLK 0x60
+
+#define ET_I2C_BASE 0x89
+#define ET_I2C_COUNT 0x8a
+#define ET_I2C_PREFETCH 0x8b
+#define ET_I2C_REG 0x88
+#define ET_I2C_DATA7 0x87
+#define ET_I2C_DATA6 0x86
+#define ET_I2C_DATA5 0x85
+#define ET_I2C_DATA4 0x84
+#define ET_I2C_DATA3 0x83
+#define ET_I2C_DATA2 0x82
+#define ET_I2C_DATA1 0x81
+#define ET_I2C_DATA0 0x80
+
+#define PAS106_REG2 0x02 /* pxlClk = systemClk/(reg2) */
+#define PAS106_REG3 0x03 /* line/frame H [11..4] */
+#define PAS106_REG4 0x04 /* line/frame L [3..0] */
+#define PAS106_REG5 0x05 /* exposure time line offset(default 5) */
+#define PAS106_REG6 0x06 /* exposure time pixel offset(default 6) */
+#define PAS106_REG7 0x07 /* signbit Dac (default 0) */
+#define PAS106_REG9 0x09
+#define PAS106_REG0e 0x0e /* global gain [4..0](default 0x0e) */
+#define PAS106_REG13 0x13 /* end i2c write */
+
+static const __u8 GainRGBG[] = { 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 };
+
+static const __u8 I2c2[] = { 0x08, 0x08, 0x08, 0x08, 0x0d };
+
+static const __u8 I2c3[] = { 0x12, 0x05 };
+
+static const __u8 I2c4[] = { 0x41, 0x08 };
+
+/* read 'len' bytes to gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev,
+ __u16 index,
+ __u16 len)
+{
+ struct usb_device *dev = gspca_dev->dev;
+
+#ifdef GSPCA_DEBUG
+ if (len > USB_BUF_SZ) {
+ pr_err("reg_r: buffer overflow\n");
+ return;
+ }
+#endif
+ usb_control_msg(dev,
+ usb_rcvctrlpipe(dev, 0),
+ 0,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ 0,
+ index, gspca_dev->usb_buf, len, 500);
+ PDEBUG(D_USBI, "reg read [%02x] -> %02x ..",
+ index, gspca_dev->usb_buf[0]);
+}
+
+static void reg_w_val(struct gspca_dev *gspca_dev,
+ __u16 index,
+ __u8 val)
+{
+ struct usb_device *dev = gspca_dev->dev;
+
+ gspca_dev->usb_buf[0] = val;
+ usb_control_msg(dev,
+ usb_sndctrlpipe(dev, 0),
+ 0,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ 0,
+ index, gspca_dev->usb_buf, 1, 500);
+}
+
+static void reg_w(struct gspca_dev *gspca_dev,
+ __u16 index,
+ const __u8 *buffer,
+ __u16 len)
+{
+ struct usb_device *dev = gspca_dev->dev;
+
+#ifdef GSPCA_DEBUG
+ if (len > USB_BUF_SZ) {
+ pr_err("reg_w: buffer overflow\n");
+ return;
+ }
+ PDEBUG(D_USBO, "reg write [%02x] = %02x..", index, *buffer);
+#endif
+ memcpy(gspca_dev->usb_buf, buffer, len);
+ usb_control_msg(dev,
+ usb_sndctrlpipe(dev, 0),
+ 0,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ 0, index, gspca_dev->usb_buf, len, 500);
+}
+
+static int i2c_w(struct gspca_dev *gspca_dev,
+ __u8 reg,
+ const __u8 *buffer,
+ int len, __u8 mode)
+{
+ /* buffer should be [D0..D7] */
+ __u8 ptchcount;
+
+ /* set the base address */
+ reg_w_val(gspca_dev, ET_I2C_BASE, 0x40);
+ /* sensor base for the pas106 */
+ /* set count and prefetch */
+ ptchcount = ((len & 0x07) << 4) | (mode & 0x03);
+ reg_w_val(gspca_dev, ET_I2C_COUNT, ptchcount);
+ /* set the register base */
+ reg_w_val(gspca_dev, ET_I2C_REG, reg);
+ while (--len >= 0)
+ reg_w_val(gspca_dev, ET_I2C_DATA0 + len, buffer[len]);
+ return 0;
+}
+
+static int i2c_r(struct gspca_dev *gspca_dev,
+ __u8 reg)
+{
+ /* set the base address */
+ reg_w_val(gspca_dev, ET_I2C_BASE, 0x40);
+ /* sensor base for the pas106 */
+ /* set count and prefetch (cnd: 4 bits - mode: 4 bits) */
+ reg_w_val(gspca_dev, ET_I2C_COUNT, 0x11);
+ reg_w_val(gspca_dev, ET_I2C_REG, reg); /* set the register base */
+ reg_w_val(gspca_dev, ET_I2C_PREFETCH, 0x02); /* prefetch */
+ reg_w_val(gspca_dev, ET_I2C_PREFETCH, 0x00);
+ reg_r(gspca_dev, ET_I2C_DATA0, 1); /* read one byte */
+ return 0;
+}
+
+static int Et_WaitStatus(struct gspca_dev *gspca_dev)
+{
+ int retry = 10;
+
+ while (retry--) {
+ reg_r(gspca_dev, ET_ClCK, 1);
+ if (gspca_dev->usb_buf[0] != 0)
+ return 1;
+ }
+ return 0;
+}
+
+static int et_video(struct gspca_dev *gspca_dev,
+ int on)
+{
+ int ret;
+
+ reg_w_val(gspca_dev, ET_GPIO_OUT,
+ on ? 0x10 /* startvideo - set Bit5 */
+ : 0); /* stopvideo */
+ ret = Et_WaitStatus(gspca_dev);
+ if (ret != 0)
+ PDEBUG(D_ERR, "timeout video on/off");
+ return ret;
+}
+
+static void Et_init2(struct gspca_dev *gspca_dev)
+{
+ __u8 value;
+ static const __u8 FormLine[] = { 0x84, 0x03, 0x14, 0xf4, 0x01, 0x05 };
+
+ PDEBUG(D_STREAM, "Open Init2 ET");
+ reg_w_val(gspca_dev, ET_GPIO_DIR_CTRL, 0x2f);
+ reg_w_val(gspca_dev, ET_GPIO_OUT, 0x10);
+ reg_r(gspca_dev, ET_GPIO_IN, 1);
+ reg_w_val(gspca_dev, ET_ClCK, 0x14); /* 0x14 // 0x16 enabled pattern */
+ reg_w_val(gspca_dev, ET_CTRL, 0x1b);
+
+ /* compression et subsampling */
+ if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv)
+ value = ET_COMP_VAL1; /* 320 */
+ else
+ value = ET_COMP_VAL0; /* 640 */
+ reg_w_val(gspca_dev, ET_COMP, value);
+ reg_w_val(gspca_dev, ET_MAXQt, 0x1f);
+ reg_w_val(gspca_dev, ET_MINQt, 0x04);
+ /* undocumented registers */
+ reg_w_val(gspca_dev, ET_REG1d, 0xff);
+ reg_w_val(gspca_dev, ET_REG1e, 0xff);
+ reg_w_val(gspca_dev, ET_REG1f, 0xff);
+ reg_w_val(gspca_dev, ET_REG20, 0x35);
+ reg_w_val(gspca_dev, ET_REG21, 0x01);
+ reg_w_val(gspca_dev, ET_REG22, 0x00);
+ reg_w_val(gspca_dev, ET_REG23, 0xff);
+ reg_w_val(gspca_dev, ET_REG24, 0xff);
+ reg_w_val(gspca_dev, ET_REG25, 0x0f);
+ /* colors setting */
+ reg_w_val(gspca_dev, 0x30, 0x11); /* 0x30 */
+ reg_w_val(gspca_dev, 0x31, 0x40);
+ reg_w_val(gspca_dev, 0x32, 0x00);
+ reg_w_val(gspca_dev, ET_O_RED, 0x00); /* 0x34 */
+ reg_w_val(gspca_dev, ET_O_GREEN1, 0x00);
+ reg_w_val(gspca_dev, ET_O_BLUE, 0x00);
+ reg_w_val(gspca_dev, ET_O_GREEN2, 0x00);
+ /*************/
+ reg_w_val(gspca_dev, ET_G_RED, 0x80); /* 0x4d */
+ reg_w_val(gspca_dev, ET_G_GREEN1, 0x80);
+ reg_w_val(gspca_dev, ET_G_BLUE, 0x80);
+ reg_w_val(gspca_dev, ET_G_GREEN2, 0x80);
+ reg_w_val(gspca_dev, ET_G_GR_H, 0x00);
+ reg_w_val(gspca_dev, ET_G_GB_H, 0x00); /* 0x52 */
+ /* Window control registers */
+ reg_w_val(gspca_dev, 0x61, 0x80); /* use cmc_out */
+ reg_w_val(gspca_dev, 0x62, 0x02);
+ reg_w_val(gspca_dev, 0x63, 0x03);
+ reg_w_val(gspca_dev, 0x64, 0x14);
+ reg_w_val(gspca_dev, 0x65, 0x0e);
+ reg_w_val(gspca_dev, 0x66, 0x02);
+ reg_w_val(gspca_dev, 0x67, 0x02);
+
+ /**************************************/
+ reg_w_val(gspca_dev, ET_SYNCHRO, 0x8f); /* 0x68 */
+ reg_w_val(gspca_dev, ET_STARTX, 0x69); /* 0x6a //0x69 */
+ reg_w_val(gspca_dev, ET_STARTY, 0x0d); /* 0x0d //0x0c */
+ reg_w_val(gspca_dev, ET_WIDTH_LOW, 0x80);
+ reg_w_val(gspca_dev, ET_HEIGTH_LOW, 0xe0);
+ reg_w_val(gspca_dev, ET_W_H_HEIGTH, 0x60); /* 6d */
+ reg_w_val(gspca_dev, ET_REG6e, 0x86);
+ reg_w_val(gspca_dev, ET_REG6f, 0x01);
+ reg_w_val(gspca_dev, ET_REG70, 0x26);
+ reg_w_val(gspca_dev, ET_REG71, 0x7a);
+ reg_w_val(gspca_dev, ET_REG72, 0x01);
+ /* Clock Pattern registers ***************** */
+ reg_w_val(gspca_dev, ET_REG73, 0x00);
+ reg_w_val(gspca_dev, ET_REG74, 0x18); /* 0x28 */
+ reg_w_val(gspca_dev, ET_REG75, 0x0f); /* 0x01 */
+ /**********************************************/
+ reg_w_val(gspca_dev, 0x8a, 0x20);
+ reg_w_val(gspca_dev, 0x8d, 0x0f);
+ reg_w_val(gspca_dev, 0x8e, 0x08);
+ /**************************************/
+ reg_w_val(gspca_dev, 0x03, 0x08);
+ reg_w_val(gspca_dev, ET_PXL_CLK, 0x03);
+ reg_w_val(gspca_dev, 0x81, 0xff);
+ reg_w_val(gspca_dev, 0x80, 0x00);
+ reg_w_val(gspca_dev, 0x81, 0xff);
+ reg_w_val(gspca_dev, 0x80, 0x20);
+ reg_w_val(gspca_dev, 0x03, 0x01);
+ reg_w_val(gspca_dev, 0x03, 0x00);
+ reg_w_val(gspca_dev, 0x03, 0x08);
+ /********************************************/
+
+/* reg_r(gspca_dev, ET_I2C_BASE, 1);
+ always 0x40 as the pas106 ??? */
+ /* set the sensor */
+ if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv)
+ value = 0x04; /* 320 */
+ else /* 640 */
+ value = 0x1e; /* 0x17 * setting PixelClock
+ * 0x03 mean 24/(3+1) = 6 Mhz
+ * 0x05 -> 24/(5+1) = 4 Mhz
+ * 0x0b -> 24/(11+1) = 2 Mhz
+ * 0x17 -> 24/(23+1) = 1 Mhz
+ */
+ reg_w_val(gspca_dev, ET_PXL_CLK, value);
+ /* now set by fifo the FormatLine setting */
+ reg_w(gspca_dev, 0x62, FormLine, 6);
+
+ /* set exposure times [ 0..0x78] 0->longvalue 0x78->shortvalue */
+ reg_w_val(gspca_dev, 0x81, 0x47); /* 0x47; */
+ reg_w_val(gspca_dev, 0x80, 0x40); /* 0x40; */
+ /* Pedro change */
+ /* Brightness change Brith+ decrease value */
+ /* Brigth- increase value */
+ /* original value = 0x70; */
+ reg_w_val(gspca_dev, 0x81, 0x30); /* 0x20; - set brightness */
+ reg_w_val(gspca_dev, 0x80, 0x20); /* 0x20; */
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+ __u8 brightness = sd->brightness;
+
+ for (i = 0; i < 4; i++)
+ reg_w_val(gspca_dev, ET_O_RED + i, brightness);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u8 RGBG[] = { 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 };
+ __u8 contrast = sd->contrast;
+
+ memset(RGBG, contrast, sizeof(RGBG) - 2);
+ reg_w(gspca_dev, ET_G_RED, RGBG, 6);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u8 I2cc[] = { 0x05, 0x02, 0x02, 0x05, 0x0d };
+ __u8 i2cflags = 0x01;
+ /* __u8 green = 0; */
+ __u8 colors = sd->colors;
+
+ I2cc[3] = colors; /* red */
+ I2cc[0] = 15 - colors; /* blue */
+ /* green = 15 - ((((7*I2cc[0]) >> 2 ) + I2cc[3]) >> 1); */
+ /* I2cc[1] = I2cc[2] = green; */
+ if (sd->sensor == SENSOR_PAS106) {
+ i2c_w(gspca_dev, PAS106_REG13, &i2cflags, 1, 3);
+ i2c_w(gspca_dev, PAS106_REG9, I2cc, sizeof I2cc, 1);
+ }
+/* PDEBUG(D_CONF , "Etoms red %d blue %d green %d",
+ I2cc[3], I2cc[0], green); */
+}
+
+static void getcolors(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor == SENSOR_PAS106) {
+/* i2c_r(gspca_dev, PAS106_REG9); * blue */
+ i2c_r(gspca_dev, PAS106_REG9 + 3); /* red */
+ sd->colors = gspca_dev->usb_buf[0] & 0x0f;
+ }
+}
+
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->autogain)
+ sd->ag_cnt = AG_CNT_START;
+ else
+ sd->ag_cnt = -1;
+}
+
+static void Et_init1(struct gspca_dev *gspca_dev)
+{
+ __u8 value;
+/* __u8 I2c0 [] = {0x0a, 0x12, 0x05, 0x22, 0xac, 0x00, 0x01, 0x00}; */
+ __u8 I2c0[] = { 0x0a, 0x12, 0x05, 0x6d, 0xcd, 0x00, 0x01, 0x00 };
+ /* try 1/120 0x6d 0xcd 0x40 */
+/* __u8 I2c0 [] = {0x0a, 0x12, 0x05, 0xfe, 0xfe, 0xc0, 0x01, 0x00};
+ * 1/60000 hmm ?? */
+
+ PDEBUG(D_STREAM, "Open Init1 ET");
+ reg_w_val(gspca_dev, ET_GPIO_DIR_CTRL, 7);
+ reg_r(gspca_dev, ET_GPIO_IN, 1);
+ reg_w_val(gspca_dev, ET_RESET_ALL, 1);
+ reg_w_val(gspca_dev, ET_RESET_ALL, 0);
+ reg_w_val(gspca_dev, ET_ClCK, 0x10);
+ reg_w_val(gspca_dev, ET_CTRL, 0x19);
+ /* compression et subsampling */
+ if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv)
+ value = ET_COMP_VAL1;
+ else
+ value = ET_COMP_VAL0;
+ PDEBUG(D_STREAM, "Open mode %d Compression %d",
+ gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv,
+ value);
+ reg_w_val(gspca_dev, ET_COMP, value);
+ reg_w_val(gspca_dev, ET_MAXQt, 0x1d);
+ reg_w_val(gspca_dev, ET_MINQt, 0x02);
+ /* undocumented registers */
+ reg_w_val(gspca_dev, ET_REG1d, 0xff);
+ reg_w_val(gspca_dev, ET_REG1e, 0xff);
+ reg_w_val(gspca_dev, ET_REG1f, 0xff);
+ reg_w_val(gspca_dev, ET_REG20, 0x35);
+ reg_w_val(gspca_dev, ET_REG21, 0x01);
+ reg_w_val(gspca_dev, ET_REG22, 0x00);
+ reg_w_val(gspca_dev, ET_REG23, 0xf7);
+ reg_w_val(gspca_dev, ET_REG24, 0xff);
+ reg_w_val(gspca_dev, ET_REG25, 0x07);
+ /* colors setting */
+ reg_w_val(gspca_dev, ET_G_RED, 0x80);
+ reg_w_val(gspca_dev, ET_G_GREEN1, 0x80);
+ reg_w_val(gspca_dev, ET_G_BLUE, 0x80);
+ reg_w_val(gspca_dev, ET_G_GREEN2, 0x80);
+ reg_w_val(gspca_dev, ET_G_GR_H, 0x00);
+ reg_w_val(gspca_dev, ET_G_GB_H, 0x00);
+ /* Window control registers */
+ reg_w_val(gspca_dev, ET_SYNCHRO, 0xf0);
+ reg_w_val(gspca_dev, ET_STARTX, 0x56); /* 0x56 */
+ reg_w_val(gspca_dev, ET_STARTY, 0x05); /* 0x04 */
+ reg_w_val(gspca_dev, ET_WIDTH_LOW, 0x60);
+ reg_w_val(gspca_dev, ET_HEIGTH_LOW, 0x20);
+ reg_w_val(gspca_dev, ET_W_H_HEIGTH, 0x50);
+ reg_w_val(gspca_dev, ET_REG6e, 0x86);
+ reg_w_val(gspca_dev, ET_REG6f, 0x01);
+ reg_w_val(gspca_dev, ET_REG70, 0x86);
+ reg_w_val(gspca_dev, ET_REG71, 0x14);
+ reg_w_val(gspca_dev, ET_REG72, 0x00);
+ /* Clock Pattern registers */
+ reg_w_val(gspca_dev, ET_REG73, 0x00);
+ reg_w_val(gspca_dev, ET_REG74, 0x00);
+ reg_w_val(gspca_dev, ET_REG75, 0x0a);
+ reg_w_val(gspca_dev, ET_I2C_CLK, 0x04);
+ reg_w_val(gspca_dev, ET_PXL_CLK, 0x01);
+ /* set the sensor */
+ if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
+ I2c0[0] = 0x06;
+ i2c_w(gspca_dev, PAS106_REG2, I2c0, sizeof I2c0, 1);
+ i2c_w(gspca_dev, PAS106_REG9, I2c2, sizeof I2c2, 1);
+ value = 0x06;
+ i2c_w(gspca_dev, PAS106_REG2, &value, 1, 1);
+ i2c_w(gspca_dev, PAS106_REG3, I2c3, sizeof I2c3, 1);
+ /* value = 0x1f; */
+ value = 0x04;
+ i2c_w(gspca_dev, PAS106_REG0e, &value, 1, 1);
+ } else {
+ I2c0[0] = 0x0a;
+
+ i2c_w(gspca_dev, PAS106_REG2, I2c0, sizeof I2c0, 1);
+ i2c_w(gspca_dev, PAS106_REG9, I2c2, sizeof I2c2, 1);
+ value = 0x0a;
+ i2c_w(gspca_dev, PAS106_REG2, &value, 1, 1);
+ i2c_w(gspca_dev, PAS106_REG3, I2c3, sizeof I2c3, 1);
+ value = 0x04;
+ /* value = 0x10; */
+ i2c_w(gspca_dev, PAS106_REG0e, &value, 1, 1);
+ /* bit 2 enable bit 1:2 select 0 1 2 3
+ value = 0x07; * curve 0 *
+ i2c_w(gspca_dev, PAS106_REG0f, &value, 1, 1);
+ */
+ }
+
+/* value = 0x01; */
+/* value = 0x22; */
+/* i2c_w(gspca_dev, PAS106_REG5, &value, 1, 1); */
+ /* magnetude and sign bit for DAC */
+ i2c_w(gspca_dev, PAS106_REG7, I2c4, sizeof I2c4, 1);
+ /* now set by fifo the whole colors setting */
+ reg_w(gspca_dev, ET_G_RED, GainRGBG, 6);
+ getcolors(gspca_dev);
+ setcolors(gspca_dev);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+ sd->sensor = id->driver_info;
+ if (sd->sensor == SENSOR_PAS106) {
+ cam->cam_mode = sif_mode;
+ cam->nmodes = ARRAY_SIZE(sif_mode);
+ } else {
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+ gspca_dev->ctrl_dis = (1 << COLOR_IDX);
+ }
+ sd->brightness = BRIGHTNESS_DEF;
+ sd->contrast = CONTRAST_DEF;
+ sd->colors = COLOR_DEF;
+ sd->autogain = AUTOGAIN_DEF;
+ sd->ag_cnt = -1;
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor == SENSOR_PAS106)
+ Et_init1(gspca_dev);
+ else
+ Et_init2(gspca_dev);
+ reg_w_val(gspca_dev, ET_RESET_ALL, 0x08);
+ et_video(gspca_dev, 0); /* video off */
+ return 0;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor == SENSOR_PAS106)
+ Et_init1(gspca_dev);
+ else
+ Et_init2(gspca_dev);
+
+ setautogain(gspca_dev);
+
+ reg_w_val(gspca_dev, ET_RESET_ALL, 0x08);
+ et_video(gspca_dev, 1); /* video on */
+ return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ et_video(gspca_dev, 0); /* video off */
+}
+
+static __u8 Et_getgainG(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor == SENSOR_PAS106) {
+ i2c_r(gspca_dev, PAS106_REG0e);
+ PDEBUG(D_CONF, "Etoms gain G %d", gspca_dev->usb_buf[0]);
+ return gspca_dev->usb_buf[0];
+ }
+ return 0x1f;
+}
+
+static void Et_setgainG(struct gspca_dev *gspca_dev, __u8 gain)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor == SENSOR_PAS106) {
+ __u8 i2cflags = 0x01;
+
+ i2c_w(gspca_dev, PAS106_REG13, &i2cflags, 1, 3);
+ i2c_w(gspca_dev, PAS106_REG0e, &gain, 1, 1);
+ }
+}
+
+#define BLIMIT(bright) \
+ (u8)((bright > 0x1f) ? 0x1f : ((bright < 4) ? 3 : bright))
+#define LIMIT(color) \
+ (u8)((color > 0xff) ? 0xff : ((color < 0) ? 0 : color))
+
+static void do_autogain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u8 luma;
+ __u8 luma_mean = 128;
+ __u8 luma_delta = 20;
+ __u8 spring = 4;
+ int Gbright;
+ __u8 r, g, b;
+
+ if (sd->ag_cnt < 0)
+ return;
+ if (--sd->ag_cnt >= 0)
+ return;
+ sd->ag_cnt = AG_CNT_START;
+
+ Gbright = Et_getgainG(gspca_dev);
+ reg_r(gspca_dev, ET_LUMA_CENTER, 4);
+ g = (gspca_dev->usb_buf[0] + gspca_dev->usb_buf[3]) >> 1;
+ r = gspca_dev->usb_buf[1];
+ b = gspca_dev->usb_buf[2];
+ r = ((r << 8) - (r << 4) - (r << 3)) >> 10;
+ b = ((b << 7) >> 10);
+ g = ((g << 9) + (g << 7) + (g << 5)) >> 10;
+ luma = LIMIT(r + g + b);
+ PDEBUG(D_FRAM, "Etoms luma G %d", luma);
+ if (luma < luma_mean - luma_delta || luma > luma_mean + luma_delta) {
+ Gbright += (luma_mean - luma) >> spring;
+ Gbright = BLIMIT(Gbright);
+ PDEBUG(D_FRAM, "Etoms Gbright %d", Gbright);
+ Et_setgainG(gspca_dev, (__u8) Gbright);
+ }
+}
+
+#undef BLIMIT
+#undef LIMIT
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ int seqframe;
+
+ seqframe = data[0] & 0x3f;
+ len = (int) (((data[0] & 0xc0) << 2) | data[1]);
+ if (seqframe == 0x3f) {
+ PDEBUG(D_FRAM,
+ "header packet found datalength %d !!", len);
+ PDEBUG(D_FRAM, "G %d R %d G %d B %d",
+ data[2], data[3], data[4], data[5]);
+ data += 30;
+ /* don't change datalength as the chips provided it */
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+ return;
+ }
+ if (len) {
+ data += 8;
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ } else { /* Drop Packet */
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ }
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->brightness = val;
+ if (gspca_dev->streaming)
+ setbrightness(gspca_dev);
+ return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->brightness;
+ return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->contrast = val;
+ if (gspca_dev->streaming)
+ setcontrast(gspca_dev);
+ return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->contrast;
+ return 0;
+}
+
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->colors = val;
+ if (gspca_dev->streaming)
+ setcolors(gspca_dev);
+ return 0;
+}
+
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->colors;
+ return 0;
+}
+
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->autogain = val;
+ if (gspca_dev->streaming)
+ setautogain(gspca_dev);
+ return 0;
+}
+
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->autogain;
+ return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+ .dq_callback = do_autogain,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x102c, 0x6151), .driver_info = SENSOR_PAS106},
+#if !defined CONFIG_USB_ET61X251 && !defined CONFIG_USB_ET61X251_MODULE
+ {USB_DEVICE(0x102c, 0x6251), .driver_info = SENSOR_TAS5130CXX},
+#endif
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/finepix.c b/drivers/media/video/gspca/finepix.c
new file mode 100644
index 00000000..0107513c
--- /dev/null
+++ b/drivers/media/video/gspca/finepix.c
@@ -0,0 +1,293 @@
+/*
+ * Fujifilm Finepix subdriver
+ *
+ * Copyright (C) 2008 Frank Zago
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "finepix"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Frank Zago <frank@zago.net>");
+MODULE_DESCRIPTION("Fujifilm FinePix USB V4L2 driver");
+MODULE_LICENSE("GPL");
+
+/* Default timeout, in ms */
+#define FPIX_TIMEOUT 250
+
+/* Maximum transfer size to use. The windows driver reads by chunks of
+ * 0x2000 bytes, so do the same. Note: reading more seems to work
+ * too. */
+#define FPIX_MAX_TRANSFER 0x2000
+
+/* Structure to hold all of our device specific stuff */
+struct usb_fpix {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ struct work_struct work_struct;
+ struct workqueue_struct *work_thread;
+};
+
+/* Delay after which claim the next frame. If the delay is too small,
+ * the camera will return old frames. On the 4800Z, 20ms is bad, 25ms
+ * will fail every 4 or 5 frames, but 30ms is perfect. On the A210,
+ * 30ms is bad while 35ms is perfect. */
+#define NEXT_FRAME_DELAY 35
+
+/* These cameras only support 320x200. */
+static const struct v4l2_pix_format fpix_mode[1] = {
+ { 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0}
+};
+
+/* send a command to the webcam */
+static int command(struct gspca_dev *gspca_dev,
+ int order) /* 0: reset, 1: frame request */
+{
+ static u8 order_values[2][12] = {
+ {0xc6, 0, 0, 0, 0, 0, 0, 0, 0x20, 0, 0, 0}, /* reset */
+ {0xd3, 0, 0, 0, 0, 0, 0, 0x01, 0, 0, 0, 0}, /* fr req */
+ };
+
+ memcpy(gspca_dev->usb_buf, order_values[order], 12);
+ return usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ USB_REQ_GET_STATUS,
+ USB_DIR_OUT | USB_TYPE_CLASS |
+ USB_RECIP_INTERFACE, 0, 0, gspca_dev->usb_buf,
+ 12, FPIX_TIMEOUT);
+}
+
+/* workqueue */
+static void dostream(struct work_struct *work)
+{
+ struct usb_fpix *dev = container_of(work, struct usb_fpix, work_struct);
+ struct gspca_dev *gspca_dev = &dev->gspca_dev;
+ struct urb *urb = gspca_dev->urb[0];
+ u8 *data = urb->transfer_buffer;
+ int ret = 0;
+ int len;
+
+ /* synchronize with the main driver */
+ mutex_lock(&gspca_dev->usb_lock);
+ mutex_unlock(&gspca_dev->usb_lock);
+ PDEBUG(D_STREAM, "dostream started");
+
+ /* loop reading a frame */
+again:
+ while (gspca_dev->present && gspca_dev->streaming) {
+
+ /* request a frame */
+ mutex_lock(&gspca_dev->usb_lock);
+ ret = command(gspca_dev, 1);
+ mutex_unlock(&gspca_dev->usb_lock);
+ if (ret < 0)
+ break;
+ if (!gspca_dev->present || !gspca_dev->streaming)
+ break;
+
+ /* the frame comes in parts */
+ for (;;) {
+ ret = usb_bulk_msg(gspca_dev->dev,
+ urb->pipe,
+ data,
+ FPIX_MAX_TRANSFER,
+ &len, FPIX_TIMEOUT);
+ if (ret < 0) {
+ /* Most of the time we get a timeout
+ * error. Just restart. */
+ goto again;
+ }
+ if (!gspca_dev->present || !gspca_dev->streaming)
+ goto out;
+ if (len < FPIX_MAX_TRANSFER ||
+ (data[len - 2] == 0xff &&
+ data[len - 1] == 0xd9)) {
+
+ /* If the result is less than what was asked
+ * for, then it's the end of the
+ * frame. Sometimes the jpeg is not complete,
+ * but there's nothing we can do. We also end
+ * here if the the jpeg ends right at the end
+ * of the frame. */
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ data, len);
+ break;
+ }
+
+ /* got a partial image */
+ gspca_frame_add(gspca_dev,
+ gspca_dev->last_packet_type
+ == LAST_PACKET
+ ? FIRST_PACKET : INTER_PACKET,
+ data, len);
+ }
+
+ /* We must wait before trying reading the next
+ * frame. If we don't, or if the delay is too short,
+ * the camera will disconnect. */
+ msleep(NEXT_FRAME_DELAY);
+ }
+
+out:
+ PDEBUG(D_STREAM, "dostream stopped");
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct usb_fpix *dev = (struct usb_fpix *) gspca_dev;
+ struct cam *cam = &gspca_dev->cam;
+
+ cam->cam_mode = fpix_mode;
+ cam->nmodes = 1;
+ cam->bulk = 1;
+ cam->bulk_size = FPIX_MAX_TRANSFER;
+
+ INIT_WORK(&dev->work_struct, dostream);
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ return 0;
+}
+
+/* start the camera */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct usb_fpix *dev = (struct usb_fpix *) gspca_dev;
+ int ret, len;
+
+ /* Init the device */
+ ret = command(gspca_dev, 0);
+ if (ret < 0) {
+ pr_err("init failed %d\n", ret);
+ return ret;
+ }
+
+ /* Read the result of the command. Ignore the result, for it
+ * varies with the device. */
+ ret = usb_bulk_msg(gspca_dev->dev,
+ gspca_dev->urb[0]->pipe,
+ gspca_dev->urb[0]->transfer_buffer,
+ FPIX_MAX_TRANSFER, &len,
+ FPIX_TIMEOUT);
+ if (ret < 0) {
+ pr_err("usb_bulk_msg failed %d\n", ret);
+ return ret;
+ }
+
+ /* Request a frame, but don't read it */
+ ret = command(gspca_dev, 1);
+ if (ret < 0) {
+ pr_err("frame request failed %d\n", ret);
+ return ret;
+ }
+
+ /* Again, reset bulk in endpoint */
+ usb_clear_halt(gspca_dev->dev, gspca_dev->urb[0]->pipe);
+
+ /* Start the workqueue function to do the streaming */
+ dev->work_thread = create_singlethread_workqueue(MODULE_NAME);
+ queue_work(dev->work_thread, &dev->work_struct);
+
+ return 0;
+}
+
+/* called on streamoff with alt==0 and on disconnect */
+/* the usb_lock is held at entry - restore on exit */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ struct usb_fpix *dev = (struct usb_fpix *) gspca_dev;
+
+ /* wait for the work queue to terminate */
+ mutex_unlock(&gspca_dev->usb_lock);
+ destroy_workqueue(dev->work_thread);
+ mutex_lock(&gspca_dev->usb_lock);
+ dev->work_thread = NULL;
+}
+
+/* Table of supported USB devices */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x04cb, 0x0104)},
+ {USB_DEVICE(0x04cb, 0x0109)},
+ {USB_DEVICE(0x04cb, 0x010b)},
+ {USB_DEVICE(0x04cb, 0x010f)},
+ {USB_DEVICE(0x04cb, 0x0111)},
+ {USB_DEVICE(0x04cb, 0x0113)},
+ {USB_DEVICE(0x04cb, 0x0115)},
+ {USB_DEVICE(0x04cb, 0x0117)},
+ {USB_DEVICE(0x04cb, 0x0119)},
+ {USB_DEVICE(0x04cb, 0x011b)},
+ {USB_DEVICE(0x04cb, 0x011d)},
+ {USB_DEVICE(0x04cb, 0x0121)},
+ {USB_DEVICE(0x04cb, 0x0123)},
+ {USB_DEVICE(0x04cb, 0x0125)},
+ {USB_DEVICE(0x04cb, 0x0127)},
+ {USB_DEVICE(0x04cb, 0x0129)},
+ {USB_DEVICE(0x04cb, 0x012b)},
+ {USB_DEVICE(0x04cb, 0x012d)},
+ {USB_DEVICE(0x04cb, 0x012f)},
+ {USB_DEVICE(0x04cb, 0x0131)},
+ {USB_DEVICE(0x04cb, 0x013b)},
+ {USB_DEVICE(0x04cb, 0x013d)},
+ {USB_DEVICE(0x04cb, 0x013f)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stop0 = sd_stop0,
+};
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id,
+ &sd_desc,
+ sizeof(struct usb_fpix),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/gl860/Kconfig b/drivers/media/video/gspca/gl860/Kconfig
new file mode 100644
index 00000000..22772f53
--- /dev/null
+++ b/drivers/media/video/gspca/gl860/Kconfig
@@ -0,0 +1,8 @@
+config USB_GL860
+ tristate "GL860 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the GL860 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_gl860.
diff --git a/drivers/media/video/gspca/gl860/Makefile b/drivers/media/video/gspca/gl860/Makefile
new file mode 100644
index 00000000..773ea342
--- /dev/null
+++ b/drivers/media/video/gspca/gl860/Makefile
@@ -0,0 +1,10 @@
+obj-$(CONFIG_USB_GL860) += gspca_gl860.o
+
+gspca_gl860-objs := gl860.o \
+ gl860-mi1320.o \
+ gl860-ov2640.o \
+ gl860-ov9655.o \
+ gl860-mi2020.o
+
+ccflags-y += -I$(srctree)/drivers/media/video/gspca
+
diff --git a/drivers/media/video/gspca/gl860/gl860-mi1320.c b/drivers/media/video/gspca/gl860/gl860-mi1320.c
new file mode 100644
index 00000000..b57160e0
--- /dev/null
+++ b/drivers/media/video/gspca/gl860/gl860-mi1320.c
@@ -0,0 +1,536 @@
+/* Subdriver for the GL860 chip with the MI1320 sensor
+ * Author Olivier LORIN from own logs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Sensor : MI1320 */
+
+#include "gl860.h"
+
+static struct validx tbl_common[] = {
+ {0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xba51, 0x0066}, {0xba02, 0x00f1},
+ {0xba05, 0x0067}, {0xba05, 0x00f1}, {0xbaa0, 0x0065}, {0xba00, 0x00f1},
+ {0xffff, 0xffff},
+ {0xba00, 0x00f0}, {0xba02, 0x00f1}, {0xbafa, 0x0028}, {0xba02, 0x00f1},
+ {0xba00, 0x00f0}, {0xba01, 0x00f1}, {0xbaf0, 0x0006}, {0xba0e, 0x00f1},
+ {0xba70, 0x0006}, {0xba0e, 0x00f1},
+ {0xffff, 0xffff},
+ {0xba74, 0x0006}, {0xba0e, 0x00f1},
+ {0xffff, 0xffff},
+ {0x0061, 0x0000}, {0x0068, 0x000d},
+};
+
+static struct validx tbl_init_at_startup[] = {
+ {0x0000, 0x0000}, {0x0010, 0x0010},
+ {35, 0xffff},
+ {0x0008, 0x00c0}, {0x0001, 0x00c1}, {0x0001, 0x00c2}, {0x0020, 0x0006},
+ {0x006a, 0x000d},
+};
+
+static struct validx tbl_sensor_settings_common[] = {
+ {0x0010, 0x0010}, {0x0003, 0x00c1}, {0x0042, 0x00c2}, {0x0040, 0x0000},
+ {0x006a, 0x0007}, {0x006a, 0x000d}, {0x0063, 0x0006},
+};
+static struct validx tbl_sensor_settings_1280[] = {
+ {0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xba5a, 0x0066}, {0xba02, 0x00f1},
+ {0xba05, 0x0067}, {0xba05, 0x00f1}, {0xba20, 0x0065}, {0xba00, 0x00f1},
+};
+static struct validx tbl_sensor_settings_800[] = {
+ {0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xba5a, 0x0066}, {0xba02, 0x00f1},
+ {0xba05, 0x0067}, {0xba05, 0x00f1}, {0xba20, 0x0065}, {0xba00, 0x00f1},
+};
+static struct validx tbl_sensor_settings_640[] = {
+ {0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xbaa0, 0x0065}, {0xba00, 0x00f1},
+ {0xba51, 0x0066}, {0xba02, 0x00f1}, {0xba05, 0x0067}, {0xba05, 0x00f1},
+ {0xba20, 0x0065}, {0xba00, 0x00f1},
+};
+static struct validx tbl_post_unset_alt[] = {
+ {0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xbaa0, 0x0065}, {0xba00, 0x00f1},
+ {0x0061, 0x0000}, {0x0068, 0x000d},
+};
+
+static u8 *tbl_1280[] = {
+ "\x0d\x80\xf1\x08\x03\x04\xf1\x00" "\x04\x05\xf1\x02\x05\x00\xf1\xf1"
+ "\x06\x00\xf1\x0d\x20\x01\xf1\x00" "\x21\x84\xf1\x00\x0d\x00\xf1\x08"
+ "\xf0\x00\xf1\x01\x34\x00\xf1\x00" "\x9b\x43\xf1\x00\xa6\x05\xf1\x00"
+ "\xa9\x04\xf1\x00\xa1\x05\xf1\x00" "\xa4\x04\xf1\x00\xae\x0a\xf1\x08"
+ ,
+ "\xf0\x00\xf1\x02\x3a\x05\xf1\xf1" "\x3c\x05\xf1\xf1\x59\x01\xf1\x47"
+ "\x5a\x01\xf1\x88\x5c\x0a\xf1\x06" "\x5d\x0e\xf1\x0a\x64\x5e\xf1\x1c"
+ "\xd2\x00\xf1\xcf\xcb\x00\xf1\x01"
+ ,
+ "\xd3\x02\xd4\x28\xd5\x01\xd0\x02" "\xd1\x18\xd2\xc1"
+};
+
+static u8 *tbl_800[] = {
+ "\x0d\x80\xf1\x08\x03\x03\xf1\xc0" "\x04\x05\xf1\x02\x05\x00\xf1\xf1"
+ "\x06\x00\xf1\x0d\x20\x01\xf1\x00" "\x21\x84\xf1\x00\x0d\x00\xf1\x08"
+ "\xf0\x00\xf1\x01\x34\x00\xf1\x00" "\x9b\x43\xf1\x00\xa6\x05\xf1\x00"
+ "\xa9\x03\xf1\xc0\xa1\x03\xf1\x20" "\xa4\x02\xf1\x5a\xae\x0a\xf1\x08"
+ ,
+ "\xf0\x00\xf1\x02\x3a\x05\xf1\xf1" "\x3c\x05\xf1\xf1\x59\x01\xf1\x47"
+ "\x5a\x01\xf1\x88\x5c\x0a\xf1\x06" "\x5d\x0e\xf1\x0a\x64\x5e\xf1\x1c"
+ "\xd2\x00\xf1\xcf\xcb\x00\xf1\x01"
+ ,
+ "\xd3\x02\xd4\x18\xd5\x21\xd0\x02" "\xd1\x10\xd2\x59"
+};
+
+static u8 *tbl_640[] = {
+ "\x0d\x80\xf1\x08\x03\x04\xf1\x04" "\x04\x05\xf1\x02\x07\x01\xf1\x7c"
+ "\x08\x00\xf1\x0e\x21\x80\xf1\x00" "\x0d\x00\xf1\x08\xf0\x00\xf1\x01"
+ "\x34\x10\xf1\x10\x3a\x43\xf1\x00" "\xa6\x05\xf1\x02\xa9\x04\xf1\x04"
+ "\xa7\x02\xf1\x81\xaa\x01\xf1\xe2" "\xae\x0c\xf1\x09"
+ ,
+ "\xf0\x00\xf1\x02\x39\x03\xf1\xfc" "\x3b\x04\xf1\x04\x57\x01\xf1\xb6"
+ "\x58\x02\xf1\x0d\x5c\x1f\xf1\x19" "\x5d\x24\xf1\x1e\x64\x5e\xf1\x1c"
+ "\xd2\x00\xf1\x00\xcb\x00\xf1\x01"
+ ,
+ "\xd3\x02\xd4\x10\xd5\x81\xd0\x02" "\xd1\x08\xd2\xe1"
+};
+
+static s32 tbl_sat[] = {0x25, 0x1d, 0x15, 0x0d, 0x05, 0x4d, 0x55, 0x5d, 0x2d};
+static s32 tbl_bright[] = {0, 8, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70};
+static s32 tbl_backlight[] = {0x0e, 0x06, 0x02};
+
+static s32 tbl_cntr1[] = {
+ 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0, 0xc8, 0xd0, 0xe0, 0xf0};
+static s32 tbl_cntr2[] = {
+ 0x70, 0x68, 0x60, 0x58, 0x50, 0x48, 0x40, 0x38, 0x30, 0x20, 0x10};
+
+static u8 dat_wbalNL[] =
+ "\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x3b\x04\xf1\x2a\x47\x10\xf1\x10"
+ "\x9d\x3c\xf1\xae\xaf\x10\xf1\x00" "\xf0\x00\xf1\x02\x2f\x91\xf1\x20"
+ "\x9c\x91\xf1\x20\x37\x03\xf1\x00" "\x9d\xc5\xf1\x0f\xf0\x00\xf1\x00";
+
+static u8 dat_wbalLL[] =
+ "\xf0\x00\xf1\x01\x05\x00\xf1\x0c" "\x3b\x04\xf1\x2a\x47\x40\xf1\x40"
+ "\x9d\x20\xf1\xae\xaf\x10\xf1\x00" "\xf0\x00\xf1\x02\x2f\xd1\xf1\x00"
+ "\x9c\xd1\xf1\x00\x37\x03\xf1\x00" "\x9d\xc5\xf1\x3f\xf0\x00\xf1\x00";
+
+static u8 dat_wbalBL[] =
+ "\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x47\x10\xf1\x30\x9d\x3c\xf1\xae"
+ "\xaf\x10\xf1\x00\xf0\x00\xf1\x02" "\x2f\x91\xf1\x20\x9c\x91\xf1\x20"
+ "\x37\x03\xf1\x00\x9d\xc5\xf1\x2f" "\xf0\x00\xf1\x00";
+
+static u8 dat_hvflip1[] = {0xf0, 0x00, 0xf1, 0x00};
+
+static u8 dat_common00[] =
+ "\x00\x01\x07\x6a\x06\x63\x0d\x6a" "\xc0\x00\x10\x10\xc1\x03\xc2\x42"
+ "\xd8\x04\x58\x00\x04\x02";
+static u8 dat_common01[] =
+ "\x0d\x00\xf1\x0b\x0d\x00\xf1\x08" "\x35\x00\xf1\x22\x68\x00\xf1\x5d"
+ "\xf0\x00\xf1\x01\x06\x70\xf1\x0e" "\xf0\x00\xf1\x02\xdd\x18\xf1\xe0";
+static u8 dat_common02[] =
+ "\x05\x01\xf1\x84\x06\x00\xf1\x44" "\x07\x00\xf1\xbe\x08\x00\xf1\x1e"
+ "\x20\x01\xf1\x03\x21\x84\xf1\x00" "\x22\x0d\xf1\x0f\x24\x80\xf1\x00"
+ "\x34\x18\xf1\x2d\x35\x00\xf1\x22" "\x43\x83\xf1\x83\x59\x00\xf1\xff";
+static u8 dat_common03[] =
+ "\xf0\x00\xf1\x02\x39\x06\xf1\x8c" "\x3a\x06\xf1\x8c\x3b\x03\xf1\xda"
+ "\x3c\x05\xf1\x30\x57\x01\xf1\x0c" "\x58\x01\xf1\x42\x59\x01\xf1\x0c"
+ "\x5a\x01\xf1\x42\x5c\x13\xf1\x0e" "\x5d\x17\xf1\x12\x64\x1e\xf1\x1c";
+static u8 dat_common04[] =
+ "\xf0\x00\xf1\x02\x24\x5f\xf1\x20" "\x28\xea\xf1\x02\x5f\x41\xf1\x43";
+static u8 dat_common05[] =
+ "\x02\x00\xf1\xee\x03\x29\xf1\x1a" "\x04\x02\xf1\xa4\x09\x00\xf1\x68"
+ "\x0a\x00\xf1\x2a\x0b\x00\xf1\x04" "\x0c\x00\xf1\x93\x0d\x00\xf1\x82"
+ "\x0e\x00\xf1\x40\x0f\x00\xf1\x5f" "\x10\x00\xf1\x4e\x11\x00\xf1\x5b";
+static u8 dat_common06[] =
+ "\x15\x00\xf1\xc9\x16\x00\xf1\x5e" "\x17\x00\xf1\x9d\x18\x00\xf1\x06"
+ "\x19\x00\xf1\x89\x1a\x00\xf1\x12" "\x1b\x00\xf1\xa1\x1c\x00\xf1\xe4"
+ "\x1d\x00\xf1\x7a\x1e\x00\xf1\x64" "\xf6\x00\xf1\x5f";
+static u8 dat_common07[] =
+ "\xf0\x00\xf1\x01\x53\x09\xf1\x03" "\x54\x3d\xf1\x1c\x55\x99\xf1\x72"
+ "\x56\xc1\xf1\xb1\x57\xd8\xf1\xce" "\x58\xe0\xf1\x00\xdc\x0a\xf1\x03"
+ "\xdd\x45\xf1\x20\xde\xae\xf1\x82" "\xdf\xdc\xf1\xc9\xe0\xf6\xf1\xea"
+ "\xe1\xff\xf1\x00";
+static u8 dat_common08[] =
+ "\xf0\x00\xf1\x01\x80\x00\xf1\x06" "\x81\xf6\xf1\x08\x82\xfb\xf1\xf7"
+ "\x83\x00\xf1\xfe\xb6\x07\xf1\x03" "\xb7\x18\xf1\x0c\x84\xfb\xf1\x06"
+ "\x85\xfb\xf1\xf9\x86\x00\xf1\xff" "\xb8\x07\xf1\x04\xb9\x16\xf1\x0a";
+static u8 dat_common09[] =
+ "\x87\xfa\xf1\x05\x88\xfc\xf1\xf9" "\x89\x00\xf1\xff\xba\x06\xf1\x03"
+ "\xbb\x17\xf1\x09\x8a\xe8\xf1\x14" "\x8b\xf7\xf1\xf0\x8c\xfd\xf1\xfa"
+ "\x8d\x00\xf1\x00\xbc\x05\xf1\x01" "\xbd\x0c\xf1\x08\xbe\x00\xf1\x14";
+static u8 dat_common10[] =
+ "\x8e\xea\xf1\x13\x8f\xf7\xf1\xf2" "\x90\xfd\xf1\xfa\x91\x00\xf1\x00"
+ "\xbf\x05\xf1\x01\xc0\x0a\xf1\x08" "\xc1\x00\xf1\x0c\x92\xed\xf1\x0f"
+ "\x93\xf9\xf1\xf4\x94\xfe\xf1\xfb" "\x95\x00\xf1\x00\xc2\x04\xf1\x01"
+ "\xc3\x0a\xf1\x07\xc4\x00\xf1\x10";
+static u8 dat_common11[] =
+ "\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x25\x00\xf1\x55\x34\x10\xf1\x10"
+ "\x35\xf0\xf1\x10\x3a\x02\xf1\x03" "\x3b\x04\xf1\x2a\x9b\x43\xf1\x00"
+ "\xa4\x03\xf1\xc0\xa7\x02\xf1\x81";
+
+static int mi1320_init_at_startup(struct gspca_dev *gspca_dev);
+static int mi1320_configure_alt(struct gspca_dev *gspca_dev);
+static int mi1320_init_pre_alt(struct gspca_dev *gspca_dev);
+static int mi1320_init_post_alt(struct gspca_dev *gspca_dev);
+static void mi1320_post_unset_alt(struct gspca_dev *gspca_dev);
+static int mi1320_sensor_settings(struct gspca_dev *gspca_dev);
+static int mi1320_camera_settings(struct gspca_dev *gspca_dev);
+/*==========================================================================*/
+
+void mi1320_init_settings(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->vcur.backlight = 0;
+ sd->vcur.brightness = 0;
+ sd->vcur.sharpness = 6;
+ sd->vcur.contrast = 10;
+ sd->vcur.gamma = 20;
+ sd->vcur.hue = 0;
+ sd->vcur.saturation = 6;
+ sd->vcur.whitebal = 0;
+ sd->vcur.mirror = 0;
+ sd->vcur.flip = 0;
+ sd->vcur.AC50Hz = 1;
+
+ sd->vmax.backlight = 2;
+ sd->vmax.brightness = 8;
+ sd->vmax.sharpness = 7;
+ sd->vmax.contrast = 0; /* 10 but not working with this driver */
+ sd->vmax.gamma = 40;
+ sd->vmax.hue = 5 + 1;
+ sd->vmax.saturation = 8;
+ sd->vmax.whitebal = 2;
+ sd->vmax.mirror = 1;
+ sd->vmax.flip = 1;
+ sd->vmax.AC50Hz = 1;
+
+ sd->dev_camera_settings = mi1320_camera_settings;
+ sd->dev_init_at_startup = mi1320_init_at_startup;
+ sd->dev_configure_alt = mi1320_configure_alt;
+ sd->dev_init_pre_alt = mi1320_init_pre_alt;
+ sd->dev_post_unset_alt = mi1320_post_unset_alt;
+}
+
+/*==========================================================================*/
+
+static void common(struct gspca_dev *gspca_dev)
+{
+ s32 n; /* reserved for FETCH functions */
+
+ ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 22, dat_common00);
+ ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 32, dat_common01);
+ n = fetch_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common));
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common02);
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common03);
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 16, dat_common04);
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common05);
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 44, dat_common06);
+ keep_on_fetching_validx(gspca_dev, tbl_common,
+ ARRAY_SIZE(tbl_common), n);
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 52, dat_common07);
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common08);
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common09);
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 56, dat_common10);
+ keep_on_fetching_validx(gspca_dev, tbl_common,
+ ARRAY_SIZE(tbl_common), n);
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, dat_common11);
+ keep_on_fetching_validx(gspca_dev, tbl_common,
+ ARRAY_SIZE(tbl_common), n);
+}
+
+static int mi1320_init_at_startup(struct gspca_dev *gspca_dev)
+{
+ fetch_validx(gspca_dev, tbl_init_at_startup,
+ ARRAY_SIZE(tbl_init_at_startup));
+
+ common(gspca_dev);
+
+/* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL); */
+
+ return 0;
+}
+
+static int mi1320_init_pre_alt(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->mirrorMask = 0;
+
+ sd->vold.backlight = -1;
+ sd->vold.brightness = -1;
+ sd->vold.sharpness = -1;
+ sd->vold.contrast = -1;
+ sd->vold.saturation = -1;
+ sd->vold.gamma = -1;
+ sd->vold.hue = -1;
+ sd->vold.whitebal = -1;
+ sd->vold.mirror = -1;
+ sd->vold.flip = -1;
+ sd->vold.AC50Hz = -1;
+
+ common(gspca_dev);
+
+ mi1320_sensor_settings(gspca_dev);
+
+ mi1320_init_post_alt(gspca_dev);
+
+ return 0;
+}
+
+static int mi1320_init_post_alt(struct gspca_dev *gspca_dev)
+{
+ mi1320_camera_settings(gspca_dev);
+
+ return 0;
+}
+
+static int mi1320_sensor_settings(struct gspca_dev *gspca_dev)
+{
+ s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
+
+ ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL);
+
+ fetch_validx(gspca_dev, tbl_sensor_settings_common,
+ ARRAY_SIZE(tbl_sensor_settings_common));
+
+ switch (reso) {
+ case IMAGE_1280:
+ fetch_validx(gspca_dev, tbl_sensor_settings_1280,
+ ARRAY_SIZE(tbl_sensor_settings_1280));
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 64, tbl_1280[0]);
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, tbl_1280[1]);
+ ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, tbl_1280[2]);
+ break;
+
+ case IMAGE_800:
+ fetch_validx(gspca_dev, tbl_sensor_settings_800,
+ ARRAY_SIZE(tbl_sensor_settings_800));
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 64, tbl_800[0]);
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, tbl_800[1]);
+ ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, tbl_800[2]);
+ break;
+
+ default:
+ fetch_validx(gspca_dev, tbl_sensor_settings_640,
+ ARRAY_SIZE(tbl_sensor_settings_640));
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 60, tbl_640[0]);
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, tbl_640[1]);
+ ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, tbl_640[2]);
+ break;
+ }
+ return 0;
+}
+
+static int mi1320_configure_alt(struct gspca_dev *gspca_dev)
+{
+ s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
+
+ switch (reso) {
+ case IMAGE_640:
+ gspca_dev->alt = 3 + 1;
+ break;
+
+ case IMAGE_800:
+ case IMAGE_1280:
+ gspca_dev->alt = 1 + 1;
+ break;
+ }
+ return 0;
+}
+
+static int mi1320_camera_settings(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ s32 backlight = sd->vcur.backlight;
+ s32 bright = sd->vcur.brightness;
+ s32 sharp = sd->vcur.sharpness;
+ s32 cntr = sd->vcur.contrast;
+ s32 gam = sd->vcur.gamma;
+ s32 hue = sd->vcur.hue;
+ s32 sat = sd->vcur.saturation;
+ s32 wbal = sd->vcur.whitebal;
+ s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) > 0);
+ s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) > 0);
+ s32 freq = (sd->vcur.AC50Hz > 0);
+ s32 i;
+
+ if (freq != sd->vold.AC50Hz) {
+ sd->vold.AC50Hz = freq;
+
+ freq = 2 * (freq == 0);
+ ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0xba02, 0x00f1, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0xba00 , 0x005b, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0xba01 + freq, 0x00f1, 0, NULL);
+ }
+
+ if (wbal != sd->vold.whitebal) {
+ sd->vold.whitebal = wbal;
+ if (wbal < 0 || wbal > sd->vmax.whitebal)
+ wbal = 0;
+
+ for (i = 0; i < 2; i++) {
+ if (wbal == 0) { /* Normal light */
+ ctrl_out(gspca_dev, 0x40, 1,
+ 0x0010, 0x0010, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1,
+ 0x0003, 0x00c1, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1,
+ 0x0042, 0x00c2, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 3,
+ 0xba00, 0x0200, 48, dat_wbalNL);
+ }
+
+ if (wbal == 1) { /* Low light */
+ ctrl_out(gspca_dev, 0x40, 1,
+ 0x0010, 0x0010, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1,
+ 0x0004, 0x00c1, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1,
+ 0x0043, 0x00c2, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 3,
+ 0xba00, 0x0200, 48, dat_wbalLL);
+ }
+
+ if (wbal == 2) { /* Back light */
+ ctrl_out(gspca_dev, 0x40, 1,
+ 0x0010, 0x0010, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1,
+ 0x0003, 0x00c1, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1,
+ 0x0042, 0x00c2, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 3,
+ 0xba00, 0x0200, 44, dat_wbalBL);
+ }
+ }
+ }
+
+ if (bright != sd->vold.brightness) {
+ sd->vold.brightness = bright;
+ if (bright < 0 || bright > sd->vmax.brightness)
+ bright = 0;
+
+ bright = tbl_bright[bright];
+ ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0xba00 + bright, 0x0034, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0xba00 + bright, 0x00f1, 0, NULL);
+ }
+
+ if (sat != sd->vold.saturation) {
+ sd->vold.saturation = sat;
+ if (sat < 0 || sat > sd->vmax.saturation)
+ sat = 0;
+
+ sat = tbl_sat[sat];
+ ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0xba00 , 0x0025, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0xba00 + sat, 0x00f1, 0, NULL);
+ }
+
+ if (sharp != sd->vold.sharpness) {
+ sd->vold.sharpness = sharp;
+ if (sharp < 0 || sharp > sd->vmax.sharpness)
+ sharp = 0;
+
+ ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0xba00 , 0x0005, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0xba00 + sharp, 0x00f1, 0, NULL);
+ }
+
+ if (hue != sd->vold.hue) {
+ /* 0=normal 1=NB 2="sepia" 3=negative 4=other 5=other2 */
+ if (hue < 0 || hue > sd->vmax.hue)
+ hue = 0;
+ if (hue == sd->vmax.hue)
+ sd->swapRB = 1;
+ else
+ sd->swapRB = 0;
+
+ ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0xba70, 0x00e2, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0xba00 + hue * (hue < 6), 0x00f1,
+ 0, NULL);
+ }
+
+ if (backlight != sd->vold.backlight) {
+ sd->vold.backlight = backlight;
+ if (backlight < 0 || backlight > sd->vmax.backlight)
+ backlight = 0;
+
+ backlight = tbl_backlight[backlight];
+ for (i = 0; i < 2; i++) {
+ ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0xba74, 0x0006, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0xba80 + backlight, 0x00f1,
+ 0, NULL);
+ }
+ }
+
+ if (hue != sd->vold.hue) {
+ sd->vold.hue = hue;
+
+ ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0xba70, 0x00e2, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0xba00 + hue * (hue < 6), 0x00f1,
+ 0, NULL);
+ }
+
+ if (mirror != sd->vold.mirror || flip != sd->vold.flip) {
+ u8 dat_hvflip2[4] = {0x20, 0x01, 0xf1, 0x00};
+ sd->vold.mirror = mirror;
+ sd->vold.flip = flip;
+
+ dat_hvflip2[3] = flip + 2 * mirror;
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 4, dat_hvflip1);
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 4, dat_hvflip2);
+ }
+
+ if (gam != sd->vold.gamma) {
+ sd->vold.gamma = gam;
+ if (gam < 0 || gam > sd->vmax.gamma)
+ gam = 0;
+
+ gam = 2 * gam;
+ ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0xba04 , 0x003b, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0xba02 + gam, 0x00f1, 0, NULL);
+ }
+
+ if (cntr != sd->vold.contrast) {
+ sd->vold.contrast = cntr;
+ if (cntr < 0 || cntr > sd->vmax.contrast)
+ cntr = 0;
+
+ ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0xba00 + tbl_cntr1[cntr], 0x0035,
+ 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0xba00 + tbl_cntr2[cntr], 0x00f1,
+ 0, NULL);
+ }
+
+ return 0;
+}
+
+static void mi1320_post_unset_alt(struct gspca_dev *gspca_dev)
+{
+ ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL);
+
+ fetch_validx(gspca_dev, tbl_post_unset_alt,
+ ARRAY_SIZE(tbl_post_unset_alt));
+}
diff --git a/drivers/media/video/gspca/gl860/gl860-mi2020.c b/drivers/media/video/gspca/gl860/gl860-mi2020.c
new file mode 100644
index 00000000..2edda6b7
--- /dev/null
+++ b/drivers/media/video/gspca/gl860/gl860-mi2020.c
@@ -0,0 +1,733 @@
+/* Subdriver for the GL860 chip with the MI2020 sensor
+ * Author Olivier LORIN, from logs by Iceman/Soro2005 + Fret_saw/Hulkie/Tricid
+ * with the help of Kytrix/BUGabundo/Blazercist.
+ * Driver achieved thanks to a webcam gift by Kytrix.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Sensor : MI2020 */
+
+#include "gl860.h"
+
+static u8 dat_wbal1[] = {0x8c, 0xa2, 0x0c};
+
+static u8 dat_bright1[] = {0x8c, 0xa2, 0x06};
+static u8 dat_bright3[] = {0x8c, 0xa1, 0x02};
+static u8 dat_bright4[] = {0x90, 0x00, 0x0f};
+static u8 dat_bright5[] = {0x8c, 0xa1, 0x03};
+static u8 dat_bright6[] = {0x90, 0x00, 0x05};
+
+static u8 dat_hvflip1[] = {0x8c, 0x27, 0x19};
+static u8 dat_hvflip3[] = {0x8c, 0x27, 0x3b};
+static u8 dat_hvflip5[] = {0x8c, 0xa1, 0x03};
+static u8 dat_hvflip6[] = {0x90, 0x00, 0x06};
+
+static struct idxdata tbl_middle_hvflip_low[] = {
+ {0x33, "\x90\x00\x06"},
+ {6, "\xff\xff\xff"},
+ {0x33, "\x90\x00\x06"},
+ {6, "\xff\xff\xff"},
+ {0x33, "\x90\x00\x06"},
+ {6, "\xff\xff\xff"},
+ {0x33, "\x90\x00\x06"},
+ {6, "\xff\xff\xff"},
+};
+
+static struct idxdata tbl_middle_hvflip_big[] = {
+ {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x01"}, {0x33, "\x8c\xa1\x20"},
+ {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x00"},
+ {102, "\xff\xff\xff"},
+ {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa1\x20"},
+ {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"},
+};
+
+static struct idxdata tbl_end_hvflip[] = {
+ {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"},
+ {6, "\xff\xff\xff"},
+ {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"},
+ {6, "\xff\xff\xff"},
+ {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"},
+ {6, "\xff\xff\xff"},
+ {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"},
+};
+
+static u8 dat_freq1[] = { 0x8c, 0xa4, 0x04 };
+
+static u8 dat_multi5[] = { 0x8c, 0xa1, 0x03 };
+static u8 dat_multi6[] = { 0x90, 0x00, 0x05 };
+
+static struct validx tbl_init_at_startup[] = {
+ {0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001, 0x00c1},
+ {0x0001, 0x00c2}, {0x0020, 0x0006}, {0x006a, 0x000d},
+ {53, 0xffff},
+ {0x0040, 0x0000}, {0x0063, 0x0006},
+};
+
+static struct validx tbl_common_0B[] = {
+ {0x0002, 0x0004}, {0x006a, 0x0007}, {0x00ef, 0x0006}, {0x006a, 0x000d},
+ {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0003, 0x00c1}, {0x0042, 0x00c2},
+ {0x0004, 0x00d8}, {0x0000, 0x0058}, {0x0041, 0x0000},
+};
+
+static struct idxdata tbl_common_3B[] = {
+ {0x33, "\x86\x25\x01"}, {0x33, "\x86\x25\x00"},
+ {2, "\xff\xff\xff"},
+ {0x30, "\x1a\x0a\xcc"}, {0x32, "\x02\x00\x08"}, {0x33, "\xf4\x03\x1d"},
+ {6, "\xff\xff\xff"}, /* 12 */
+ {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"},
+ {2, "\xff\xff\xff"}, /* - */
+ {0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\x22\x23"},
+ {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa2\x0f"}, {0x33, "\x90\x00\x0d"},
+ {0x33, "\x8c\xa2\x10"}, {0x33, "\x90\x00\x0b"}, {0x33, "\x8c\xa2\x11"},
+ {0x33, "\x90\x00\x07"}, {0x33, "\xf4\x03\x1d"}, {0x35, "\xa2\x00\xe2"},
+ {0x33, "\x8c\xab\x05"}, {0x33, "\x90\x00\x01"}, {0x32, "\x6e\x00\x86"},
+ {0x32, "\x70\x0f\xaa"}, {0x32, "\x72\x0f\xe4"}, {0x33, "\x8c\xa3\x4a"},
+ {0x33, "\x90\x00\x5a"}, {0x33, "\x8c\xa3\x4b"}, {0x33, "\x90\x00\xa6"},
+ {0x33, "\x8c\xa3\x61"}, {0x33, "\x90\x00\xc8"}, {0x33, "\x8c\xa3\x62"},
+ {0x33, "\x90\x00\xe1"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"},
+ {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"},
+ {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"},
+ {0x34, "\xde\x01\x5b"}, {0x34, "\xe6\x01\x13"}, {0x34, "\xee\x0b\xf0"},
+ {0x34, "\xf6\x0b\xa4"}, {0x35, "\x00\xf6\xe7"}, {0x35, "\x08\x0d\xfd"},
+ {0x35, "\x10\x25\x63"}, {0x35, "\x18\x35\x6c"}, {0x35, "\x20\x42\x7e"},
+ {0x35, "\x28\x19\x44"}, {0x35, "\x30\x39\xd4"}, {0x35, "\x38\xf5\xa8"},
+ {0x35, "\x4c\x07\x90"}, {0x35, "\x44\x07\xb8"}, {0x35, "\x5c\x06\x88"},
+ {0x35, "\x54\x07\xff"}, {0x34, "\xe0\x01\x52"}, {0x34, "\xe8\x00\xcc"},
+ {0x34, "\xf0\x0d\x83"}, {0x34, "\xf8\x0c\xb3"}, {0x35, "\x02\xfe\xba"},
+ {0x35, "\x0a\x04\xe0"}, {0x35, "\x12\x1c\x63"}, {0x35, "\x1a\x2b\x5a"},
+ {0x35, "\x22\x32\x5e"}, {0x35, "\x2a\x0d\x28"}, {0x35, "\x32\x2c\x02"},
+ {0x35, "\x3a\xf4\xfa"}, {0x35, "\x4e\x07\xef"}, {0x35, "\x46\x07\x88"},
+ {0x35, "\x5e\x07\xc1"}, {0x35, "\x56\x04\x64"}, {0x34, "\xe4\x01\x15"},
+ {0x34, "\xec\x00\x82"}, {0x34, "\xf4\x0c\xce"}, {0x34, "\xfc\x0c\xba"},
+ {0x35, "\x06\x1f\x02"}, {0x35, "\x0e\x02\xe3"}, {0x35, "\x16\x1a\x50"},
+ {0x35, "\x1e\x24\x39"}, {0x35, "\x26\x23\x4c"}, {0x35, "\x2e\xf9\x1b"},
+ {0x35, "\x36\x23\x19"}, {0x35, "\x3e\x12\x08"}, {0x35, "\x52\x07\x22"},
+ {0x35, "\x4a\x03\xd3"}, {0x35, "\x62\x06\x54"}, {0x35, "\x5a\x04\x5d"},
+ {0x34, "\xe2\x01\x04"}, {0x34, "\xea\x00\xa0"}, {0x34, "\xf2\x0c\xbc"},
+ {0x34, "\xfa\x0c\x5b"}, {0x35, "\x04\x17\xf2"}, {0x35, "\x0c\x02\x08"},
+ {0x35, "\x14\x28\x43"}, {0x35, "\x1c\x28\x62"}, {0x35, "\x24\x2b\x60"},
+ {0x35, "\x2c\x07\x33"}, {0x35, "\x34\x1f\xb0"}, {0x35, "\x3c\xed\xcd"},
+ {0x35, "\x50\x00\x06"}, {0x35, "\x48\x07\xff"}, {0x35, "\x60\x05\x89"},
+ {0x35, "\x58\x07\xff"}, {0x35, "\x40\x00\xa0"}, {0x35, "\x42\x00\x00"},
+ {0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, {0x33, "\x90\x00\x3c"},
+ {0x33, "\x78\x00\x00"},
+ {2, "\xff\xff\xff"},
+ {0x35, "\xb8\x1f\x20"}, {0x33, "\x8c\xa2\x06"}, {0x33, "\x90\x00\x10"},
+ {0x33, "\x8c\xa2\x07"}, {0x33, "\x90\x00\x08"}, {0x33, "\x8c\xa2\x42"},
+ {0x33, "\x90\x00\x0b"}, {0x33, "\x8c\xa2\x4a"}, {0x33, "\x90\x00\x8c"},
+ {0x35, "\xba\xfa\x08"}, {0x33, "\x8c\xa2\x02"}, {0x33, "\x90\x00\x22"},
+ {0x33, "\x8c\xa2\x03"}, {0x33, "\x90\x00\xbb"}, {0x33, "\x8c\xa4\x04"},
+ {0x33, "\x90\x00\x80"}, {0x33, "\x8c\xa7\x9d"}, {0x33, "\x90\x00\x00"},
+ {0x33, "\x8c\xa7\x9e"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa2\x0c"},
+ {0x33, "\x90\x00\x17"}, {0x33, "\x8c\xa2\x15"}, {0x33, "\x90\x00\x04"},
+ {0x33, "\x8c\xa2\x14"}, {0x33, "\x90\x00\x20"}, {0x33, "\x8c\xa1\x03"},
+ {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x17"}, {0x33, "\x90\x21\x11"},
+ {0x33, "\x8c\x27\x1b"}, {0x33, "\x90\x02\x4f"}, {0x33, "\x8c\x27\x25"},
+ {0x33, "\x90\x06\x0f"}, {0x33, "\x8c\x27\x39"}, {0x33, "\x90\x21\x11"},
+ {0x33, "\x8c\x27\x3d"}, {0x33, "\x90\x01\x20"}, {0x33, "\x8c\x27\x47"},
+ {0x33, "\x90\x09\x4c"}, {0x33, "\x8c\x27\x03"}, {0x33, "\x90\x02\x84"},
+ {0x33, "\x8c\x27\x05"}, {0x33, "\x90\x01\xe2"}, {0x33, "\x8c\x27\x07"},
+ {0x33, "\x90\x06\x40"}, {0x33, "\x8c\x27\x09"}, {0x33, "\x90\x04\xb0"},
+ {0x33, "\x8c\x27\x0d"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x0f"},
+ {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x11"}, {0x33, "\x90\x04\xbd"},
+ {0x33, "\x8c\x27\x13"}, {0x33, "\x90\x06\x4d"}, {0x33, "\x8c\x27\x15"},
+ {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x17"}, {0x33, "\x90\x21\x11"},
+ {0x33, "\x8c\x27\x19"}, {0x33, "\x90\x04\x6c"}, {0x33, "\x8c\x27\x1b"},
+ {0x33, "\x90\x02\x4f"}, {0x33, "\x8c\x27\x1d"}, {0x33, "\x90\x01\x02"},
+ {0x33, "\x8c\x27\x1f"}, {0x33, "\x90\x02\x79"}, {0x33, "\x8c\x27\x21"},
+ {0x33, "\x90\x01\x55"}, {0x33, "\x8c\x27\x23"}, {0x33, "\x90\x02\x85"},
+ {0x33, "\x8c\x27\x25"}, {0x33, "\x90\x06\x0f"}, {0x33, "\x8c\x27\x27"},
+ {0x33, "\x90\x20\x20"}, {0x33, "\x8c\x27\x29"}, {0x33, "\x90\x20\x20"},
+ {0x33, "\x8c\x27\x2b"}, {0x33, "\x90\x10\x20"}, {0x33, "\x8c\x27\x2d"},
+ {0x33, "\x90\x20\x07"}, {0x33, "\x8c\x27\x2f"}, {0x33, "\x90\x00\x04"},
+ {0x33, "\x8c\x27\x31"}, {0x33, "\x90\x00\x04"}, {0x33, "\x8c\x27\x33"},
+ {0x33, "\x90\x04\xbb"}, {0x33, "\x8c\x27\x35"}, {0x33, "\x90\x06\x4b"},
+ {0x33, "\x8c\x27\x37"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x39"},
+ {0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x3b"}, {0x33, "\x90\x00\x24"},
+ {0x33, "\x8c\x27\x3d"}, {0x33, "\x90\x01\x20"}, {0x33, "\x8c\x27\x41"},
+ {0x33, "\x90\x01\x69"}, {0x33, "\x8c\x27\x45"}, {0x33, "\x90\x04\xed"},
+ {0x33, "\x8c\x27\x47"}, {0x33, "\x90\x09\x4c"}, {0x33, "\x8c\x27\x51"},
+ {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x53"}, {0x33, "\x90\x03\x20"},
+ {0x33, "\x8c\x27\x55"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x57"},
+ {0x33, "\x90\x02\x58"}, {0x33, "\x8c\x27\x5f"}, {0x33, "\x90\x00\x00"},
+ {0x33, "\x8c\x27\x61"}, {0x33, "\x90\x06\x40"}, {0x33, "\x8c\x27\x63"},
+ {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x65"}, {0x33, "\x90\x04\xb0"},
+ {0x33, "\x8c\x22\x2e"}, {0x33, "\x90\x00\xa1"}, {0x33, "\x8c\xa4\x08"},
+ {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x21"},
+ {0x33, "\x8c\xa4\x0a"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\xa4\x0b"},
+ {0x33, "\x90\x00\x27"}, {0x33, "\x8c\x24\x11"}, {0x33, "\x90\x00\xa1"},
+ {0x33, "\x8c\x24\x13"}, {0x33, "\x90\x00\xc1"}, {0x33, "\x8c\x24\x15"},
+ {0x33, "\x90\x00\x6a"}, {0x33, "\x8c\x24\x17"}, {0x33, "\x90\x00\x80"},
+ {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
+ {2, "\xff\xff\xff"},
+ {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"},
+ {3, "\xff\xff\xff"},
+};
+
+static struct idxdata tbl_init_post_alt_low1[] = {
+ {0x33, "\x8c\x27\x15"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\x22\x2e"},
+ {0x33, "\x90\x00\x81"}, {0x33, "\x8c\xa4\x08"}, {0x33, "\x90\x00\x17"},
+ {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x1a"}, {0x33, "\x8c\xa4\x0a"},
+ {0x33, "\x90\x00\x1d"}, {0x33, "\x8c\xa4\x0b"}, {0x33, "\x90\x00\x20"},
+ {0x33, "\x8c\x24\x11"}, {0x33, "\x90\x00\x81"}, {0x33, "\x8c\x24\x13"},
+ {0x33, "\x90\x00\x9b"},
+};
+
+static struct idxdata tbl_init_post_alt_low2[] = {
+ {0x33, "\x8c\x27\x03"}, {0x33, "\x90\x03\x24"}, {0x33, "\x8c\x27\x05"},
+ {0x33, "\x90\x02\x58"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
+ {2, "\xff\xff\xff"},
+ {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"},
+ {2, "\xff\xff\xff"},
+};
+
+static struct idxdata tbl_init_post_alt_low3[] = {
+ {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"},
+ {2, "\xff\xff\xff"},
+ {0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\xa1\x20"},
+ {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x01"},
+ {0x33, "\x2e\x01\x00"}, {0x34, "\x04\x00\x2a"}, {0x33, "\x8c\xa7\x02"},
+ {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x95"}, {0x33, "\x90\x01\x00"},
+ {2, "\xff\xff\xff"},
+ {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa1\x03"},
+ {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"},
+ {2, "\xff\xff\xff"},
+ {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa1\x03"},
+ {0x33, "\x90\x00\x01"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x00"},
+ {2, "\xff\xff\xff"},
+ {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
+ {2, "\xff\xff\xff"},
+ {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"},
+ {2, "\xff\xff\xff"},
+ {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
+ {2, "\xff\xff\xff"},
+ {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"},
+};
+
+static struct idxdata tbl_init_post_alt_big[] = {
+ {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
+ {2, "\xff\xff\xff"},
+ {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"},
+ {2, "\xff\xff\xff"},
+ {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"},
+ {2, "\xff\xff\xff"},
+ {0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\xa1\x03"},
+ {0x33, "\x90\x00\x05"},
+ {2, "\xff\xff\xff"},
+ {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"},
+ {2, "\xff\xff\xff"},
+ {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
+ {2, "\xff\xff\xff"},
+ {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, {0x33, "\x8c\xa1\x20"},
+ {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa1\x30"}, {0x33, "\x90\x00\x03"},
+ {0x33, "\x8c\xa1\x31"}, {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa1\x32"},
+ {0x33, "\x90\x00\x03"}, {0x33, "\x8c\xa1\x34"}, {0x33, "\x90\x00\x03"},
+ {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x02"}, {0x33, "\x2e\x01\x00"},
+ {0x34, "\x04\x00\x2a"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"},
+ {0x33, "\x8c\x27\x97"}, {0x33, "\x90\x01\x00"},
+ {51, "\xff\xff\xff"},
+ {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa1\x03"},
+ {0x33, "\x90\x00\x01"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x00"},
+ {51, "\xff\xff\xff"},
+ {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa1\x03"},
+ {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"},
+ {51, "\xff\xff\xff"},
+};
+
+static struct idxdata tbl_init_post_alt_3B[] = {
+ {0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"},
+ {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"},
+ {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"},
+ {0x34, "\xde\x01\x5b"}, {0x34, "\xe6\x01\x13"}, {0x34, "\xee\x0b\xf0"},
+ {0x34, "\xf6\x0b\xa4"}, {0x35, "\x00\xf6\xe7"}, {0x35, "\x08\x0d\xfd"},
+ {0x35, "\x10\x25\x63"}, {0x35, "\x18\x35\x6c"}, {0x35, "\x20\x42\x7e"},
+ {0x35, "\x28\x19\x44"}, {0x35, "\x30\x39\xd4"}, {0x35, "\x38\xf5\xa8"},
+ {0x35, "\x4c\x07\x90"}, {0x35, "\x44\x07\xb8"}, {0x35, "\x5c\x06\x88"},
+ {0x35, "\x54\x07\xff"}, {0x34, "\xe0\x01\x52"}, {0x34, "\xe8\x00\xcc"},
+ {0x34, "\xf0\x0d\x83"}, {0x34, "\xf8\x0c\xb3"}, {0x35, "\x02\xfe\xba"},
+ {0x35, "\x0a\x04\xe0"}, {0x35, "\x12\x1c\x63"}, {0x35, "\x1a\x2b\x5a"},
+ {0x35, "\x22\x32\x5e"}, {0x35, "\x2a\x0d\x28"}, {0x35, "\x32\x2c\x02"},
+ {0x35, "\x3a\xf4\xfa"}, {0x35, "\x4e\x07\xef"}, {0x35, "\x46\x07\x88"},
+ {0x35, "\x5e\x07\xc1"}, {0x35, "\x56\x04\x64"}, {0x34, "\xe4\x01\x15"},
+ {0x34, "\xec\x00\x82"}, {0x34, "\xf4\x0c\xce"}, {0x34, "\xfc\x0c\xba"},
+ {0x35, "\x06\x1f\x02"}, {0x35, "\x0e\x02\xe3"}, {0x35, "\x16\x1a\x50"},
+ {0x35, "\x1e\x24\x39"}, {0x35, "\x26\x23\x4c"}, {0x35, "\x2e\xf9\x1b"},
+ {0x35, "\x36\x23\x19"}, {0x35, "\x3e\x12\x08"}, {0x35, "\x52\x07\x22"},
+ {0x35, "\x4a\x03\xd3"}, {0x35, "\x62\x06\x54"}, {0x35, "\x5a\x04\x5d"},
+ {0x34, "\xe2\x01\x04"}, {0x34, "\xea\x00\xa0"}, {0x34, "\xf2\x0c\xbc"},
+ {0x34, "\xfa\x0c\x5b"}, {0x35, "\x04\x17\xf2"}, {0x35, "\x0c\x02\x08"},
+ {0x35, "\x14\x28\x43"}, {0x35, "\x1c\x28\x62"}, {0x35, "\x24\x2b\x60"},
+ {0x35, "\x2c\x07\x33"}, {0x35, "\x34\x1f\xb0"}, {0x35, "\x3c\xed\xcd"},
+ {0x35, "\x50\x00\x06"}, {0x35, "\x48\x07\xff"}, {0x35, "\x60\x05\x89"},
+ {0x35, "\x58\x07\xff"}, {0x35, "\x40\x00\xa0"}, {0x35, "\x42\x00\x00"},
+ {0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, {0x33, "\x90\x00\x3c"},
+};
+
+static u8 *dat_640 = "\xd0\x02\xd1\x08\xd2\xe1\xd3\x02\xd4\x10\xd5\x81";
+static u8 *dat_800 = "\xd0\x02\xd1\x10\xd2\x57\xd3\x02\xd4\x18\xd5\x21";
+static u8 *dat_1280 = "\xd0\x02\xd1\x20\xd2\x01\xd3\x02\xd4\x28\xd5\x01";
+static u8 *dat_1600 = "\xd0\x02\xd1\x20\xd2\xaf\xd3\x02\xd4\x30\xd5\x41";
+
+static int mi2020_init_at_startup(struct gspca_dev *gspca_dev);
+static int mi2020_configure_alt(struct gspca_dev *gspca_dev);
+static int mi2020_init_pre_alt(struct gspca_dev *gspca_dev);
+static int mi2020_init_post_alt(struct gspca_dev *gspca_dev);
+static void mi2020_post_unset_alt(struct gspca_dev *gspca_dev);
+static int mi2020_camera_settings(struct gspca_dev *gspca_dev);
+/*==========================================================================*/
+
+void mi2020_init_settings(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->vcur.backlight = 0;
+ sd->vcur.brightness = 70;
+ sd->vcur.sharpness = 20;
+ sd->vcur.contrast = 0;
+ sd->vcur.gamma = 0;
+ sd->vcur.hue = 0;
+ sd->vcur.saturation = 60;
+ sd->vcur.whitebal = 0; /* 50, not done by hardware */
+ sd->vcur.mirror = 0;
+ sd->vcur.flip = 0;
+ sd->vcur.AC50Hz = 1;
+
+ sd->vmax.backlight = 64;
+ sd->vmax.brightness = 128;
+ sd->vmax.sharpness = 40;
+ sd->vmax.contrast = 3;
+ sd->vmax.gamma = 2;
+ sd->vmax.hue = 0 + 1; /* 200, not done by hardware */
+ sd->vmax.saturation = 0; /* 100, not done by hardware */
+ sd->vmax.whitebal = 2; /* 100, not done by hardware */
+ sd->vmax.mirror = 1;
+ sd->vmax.flip = 1;
+ sd->vmax.AC50Hz = 1;
+
+ sd->dev_camera_settings = mi2020_camera_settings;
+ sd->dev_init_at_startup = mi2020_init_at_startup;
+ sd->dev_configure_alt = mi2020_configure_alt;
+ sd->dev_init_pre_alt = mi2020_init_pre_alt;
+ sd->dev_post_unset_alt = mi2020_post_unset_alt;
+}
+
+/*==========================================================================*/
+
+static void common(struct gspca_dev *gspca_dev)
+{
+ fetch_validx(gspca_dev, tbl_common_0B, ARRAY_SIZE(tbl_common_0B));
+ fetch_idxdata(gspca_dev, tbl_common_3B, ARRAY_SIZE(tbl_common_3B));
+ ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL);
+}
+
+static int mi2020_init_at_startup(struct gspca_dev *gspca_dev)
+{
+ u8 c;
+
+ ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &c);
+ ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &c);
+
+ fetch_validx(gspca_dev, tbl_init_at_startup,
+ ARRAY_SIZE(tbl_init_at_startup));
+
+ ctrl_out(gspca_dev, 0x40, 1, 0x7a00, 0x8030, 0, NULL);
+ ctrl_in(gspca_dev, 0xc0, 2, 0x7a00, 0x8030, 1, &c);
+
+ common(gspca_dev);
+
+ msleep(61);
+/* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL); */
+/* msleep(36); */
+ ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0000, 0, NULL);
+
+ return 0;
+}
+
+static int mi2020_init_pre_alt(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->mirrorMask = 0;
+ sd->vold.hue = -1;
+
+ /* These controls need to be reset */
+ sd->vold.brightness = -1;
+ sd->vold.sharpness = -1;
+
+ /* If not different from default, they do not need to be set */
+ sd->vold.contrast = 0;
+ sd->vold.gamma = 0;
+ sd->vold.backlight = 0;
+
+ mi2020_init_post_alt(gspca_dev);
+
+ return 0;
+}
+
+static int mi2020_init_post_alt(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
+
+ s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) > 0);
+ s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) > 0);
+ s32 freq = (sd->vcur.AC50Hz > 0);
+ s32 wbal = sd->vcur.whitebal;
+
+ u8 dat_freq2[] = {0x90, 0x00, 0x80};
+ u8 dat_multi1[] = {0x8c, 0xa7, 0x00};
+ u8 dat_multi2[] = {0x90, 0x00, 0x00};
+ u8 dat_multi3[] = {0x8c, 0xa7, 0x00};
+ u8 dat_multi4[] = {0x90, 0x00, 0x00};
+ u8 dat_hvflip2[] = {0x90, 0x04, 0x6c};
+ u8 dat_hvflip4[] = {0x90, 0x00, 0x24};
+ u8 dat_wbal2[] = {0x90, 0x00, 0x00};
+ u8 c;
+
+ sd->nbIm = -1;
+
+ dat_freq2[2] = freq ? 0xc0 : 0x80;
+ dat_multi1[2] = 0x9d;
+ dat_multi3[2] = dat_multi1[2] + 1;
+ if (wbal == 0) {
+ dat_multi4[2] = dat_multi2[2] = 0;
+ dat_wbal2[2] = 0x17;
+ } else if (wbal == 1) {
+ dat_multi4[2] = dat_multi2[2] = 0;
+ dat_wbal2[2] = 0x35;
+ } else if (wbal == 2) {
+ dat_multi4[2] = dat_multi2[2] = 0x20;
+ dat_wbal2[2] = 0x17;
+ }
+ dat_hvflip2[2] = 0x6c + 2 * (1 - flip) + (1 - mirror);
+ dat_hvflip4[2] = 0x24 + 2 * (1 - flip) + (1 - mirror);
+
+ msleep(200);
+ ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL);
+ msleep(2);
+
+ common(gspca_dev);
+
+ msleep(142);
+ ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x0003, 0x00c1, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x0042, 0x00c2, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x006a, 0x000d, 0, NULL);
+
+ switch (reso) {
+ case IMAGE_640:
+ case IMAGE_800:
+ if (reso != IMAGE_800)
+ ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200,
+ 12, dat_640);
+ else
+ ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200,
+ 12, dat_800);
+
+ fetch_idxdata(gspca_dev, tbl_init_post_alt_low1,
+ ARRAY_SIZE(tbl_init_post_alt_low1));
+
+ if (reso == IMAGE_800)
+ fetch_idxdata(gspca_dev, tbl_init_post_alt_low2,
+ ARRAY_SIZE(tbl_init_post_alt_low2));
+
+ fetch_idxdata(gspca_dev, tbl_init_post_alt_low3,
+ ARRAY_SIZE(tbl_init_post_alt_low3));
+
+ ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL);
+ msleep(120);
+ break;
+
+ case IMAGE_1280:
+ case IMAGE_1600:
+ if (reso == IMAGE_1280) {
+ ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200,
+ 12, dat_1280);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
+ 3, "\x8c\x27\x07");
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
+ 3, "\x90\x05\x04");
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
+ 3, "\x8c\x27\x09");
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
+ 3, "\x90\x04\x02");
+ } else {
+ ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200,
+ 12, dat_1600);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
+ 3, "\x8c\x27\x07");
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
+ 3, "\x90\x06\x40");
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
+ 3, "\x8c\x27\x09");
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
+ 3, "\x90\x04\xb0");
+ }
+
+ fetch_idxdata(gspca_dev, tbl_init_post_alt_big,
+ ARRAY_SIZE(tbl_init_post_alt_big));
+
+ ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL);
+ msleep(1850);
+ }
+
+ ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL);
+ msleep(40);
+
+ /* AC power frequency */
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq1);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq2);
+ msleep(33);
+ /* light source */
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_wbal1);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_wbal2);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6);
+ msleep(7);
+ ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, &c);
+
+ fetch_idxdata(gspca_dev, tbl_init_post_alt_3B,
+ ARRAY_SIZE(tbl_init_post_alt_3B));
+
+ /* hvflip */
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip1);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip2);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip3);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip4);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip5);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip6);
+ msleep(250);
+
+ if (reso == IMAGE_640 || reso == IMAGE_800)
+ fetch_idxdata(gspca_dev, tbl_middle_hvflip_low,
+ ARRAY_SIZE(tbl_middle_hvflip_low));
+ else
+ fetch_idxdata(gspca_dev, tbl_middle_hvflip_big,
+ ARRAY_SIZE(tbl_middle_hvflip_big));
+
+ fetch_idxdata(gspca_dev, tbl_end_hvflip,
+ ARRAY_SIZE(tbl_end_hvflip));
+
+ sd->nbIm = 0;
+
+ sd->vold.mirror = mirror;
+ sd->vold.flip = flip;
+ sd->vold.AC50Hz = freq;
+ sd->vold.whitebal = wbal;
+
+ mi2020_camera_settings(gspca_dev);
+
+ return 0;
+}
+
+static int mi2020_configure_alt(struct gspca_dev *gspca_dev)
+{
+ s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
+
+ switch (reso) {
+ case IMAGE_640:
+ gspca_dev->alt = 3 + 1;
+ break;
+
+ case IMAGE_800:
+ case IMAGE_1280:
+ case IMAGE_1600:
+ gspca_dev->alt = 1 + 1;
+ break;
+ }
+ return 0;
+}
+
+static int mi2020_camera_settings(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
+
+ s32 backlight = sd->vcur.backlight;
+ s32 bright = sd->vcur.brightness;
+ s32 sharp = sd->vcur.sharpness;
+ s32 cntr = sd->vcur.contrast;
+ s32 gam = sd->vcur.gamma;
+ s32 hue = (sd->vcur.hue > 0);
+ s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) > 0);
+ s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) > 0);
+ s32 freq = (sd->vcur.AC50Hz > 0);
+ s32 wbal = sd->vcur.whitebal;
+
+ u8 dat_sharp[] = {0x6c, 0x00, 0x08};
+ u8 dat_bright2[] = {0x90, 0x00, 0x00};
+ u8 dat_freq2[] = {0x90, 0x00, 0x80};
+ u8 dat_multi1[] = {0x8c, 0xa7, 0x00};
+ u8 dat_multi2[] = {0x90, 0x00, 0x00};
+ u8 dat_multi3[] = {0x8c, 0xa7, 0x00};
+ u8 dat_multi4[] = {0x90, 0x00, 0x00};
+ u8 dat_hvflip2[] = {0x90, 0x04, 0x6c};
+ u8 dat_hvflip4[] = {0x90, 0x00, 0x24};
+ u8 dat_wbal2[] = {0x90, 0x00, 0x00};
+
+ /* Less than 4 images received -> too early to set the settings */
+ if (sd->nbIm < 4) {
+ sd->waitSet = 1;
+ return 0;
+ }
+ sd->waitSet = 0;
+
+ if (freq != sd->vold.AC50Hz) {
+ sd->vold.AC50Hz = freq;
+
+ dat_freq2[2] = freq ? 0xc0 : 0x80;
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq1);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq2);
+ msleep(20);
+ }
+
+ if (wbal != sd->vold.whitebal) {
+ sd->vold.whitebal = wbal;
+ if (wbal < 0 || wbal > sd->vmax.whitebal)
+ wbal = 0;
+
+ dat_multi1[2] = 0x9d;
+ dat_multi3[2] = dat_multi1[2] + 1;
+ if (wbal == 0) {
+ dat_multi4[2] = dat_multi2[2] = 0;
+ dat_wbal2[2] = 0x17;
+ } else if (wbal == 1) {
+ dat_multi4[2] = dat_multi2[2] = 0;
+ dat_wbal2[2] = 0x35;
+ } else if (wbal == 2) {
+ dat_multi4[2] = dat_multi2[2] = 0x20;
+ dat_wbal2[2] = 0x17;
+ }
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_wbal1);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_wbal2);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6);
+ }
+
+ if (mirror != sd->vold.mirror || flip != sd->vold.flip) {
+ sd->vold.mirror = mirror;
+ sd->vold.flip = flip;
+
+ dat_hvflip2[2] = 0x6c + 2 * (1 - flip) + (1 - mirror);
+ dat_hvflip4[2] = 0x24 + 2 * (1 - flip) + (1 - mirror);
+
+ fetch_idxdata(gspca_dev, tbl_init_post_alt_3B,
+ ARRAY_SIZE(tbl_init_post_alt_3B));
+
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip1);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip2);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip3);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip4);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip5);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip6);
+ msleep(40);
+
+ if (reso == IMAGE_640 || reso == IMAGE_800)
+ fetch_idxdata(gspca_dev, tbl_middle_hvflip_low,
+ ARRAY_SIZE(tbl_middle_hvflip_low));
+ else
+ fetch_idxdata(gspca_dev, tbl_middle_hvflip_big,
+ ARRAY_SIZE(tbl_middle_hvflip_big));
+
+ fetch_idxdata(gspca_dev, tbl_end_hvflip,
+ ARRAY_SIZE(tbl_end_hvflip));
+ }
+
+ if (bright != sd->vold.brightness) {
+ sd->vold.brightness = bright;
+ if (bright < 0 || bright > sd->vmax.brightness)
+ bright = 0;
+
+ dat_bright2[2] = bright;
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright1);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright2);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright3);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright4);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright5);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright6);
+ }
+
+ if (cntr != sd->vold.contrast || gam != sd->vold.gamma) {
+ sd->vold.contrast = cntr;
+ if (cntr < 0 || cntr > sd->vmax.contrast)
+ cntr = 0;
+ sd->vold.gamma = gam;
+ if (gam < 0 || gam > sd->vmax.gamma)
+ gam = 0;
+
+ dat_multi1[2] = 0x6d;
+ dat_multi3[2] = dat_multi1[2] + 1;
+ if (cntr == 0)
+ cntr = 4;
+ dat_multi4[2] = dat_multi2[2] = cntr * 0x10 + 2 - gam;
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6);
+ }
+
+ if (backlight != sd->vold.backlight) {
+ sd->vold.backlight = backlight;
+ if (backlight < 0 || backlight > sd->vmax.backlight)
+ backlight = 0;
+
+ dat_multi1[2] = 0x9d;
+ dat_multi3[2] = dat_multi1[2] + 1;
+ dat_multi4[2] = dat_multi2[2] = backlight;
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5);
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6);
+ }
+
+ if (sharp != sd->vold.sharpness) {
+ sd->vold.sharpness = sharp;
+ if (sharp < 0 || sharp > sd->vmax.sharpness)
+ sharp = 0;
+
+ dat_sharp[1] = sharp;
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0032, 3, dat_sharp);
+ }
+
+ if (hue != sd->vold.hue) {
+ sd->swapRB = hue;
+ sd->vold.hue = hue;
+ }
+
+ return 0;
+}
+
+static void mi2020_post_unset_alt(struct gspca_dev *gspca_dev)
+{
+ ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL);
+ msleep(40);
+ ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0000, 0, NULL);
+}
diff --git a/drivers/media/video/gspca/gl860/gl860-ov2640.c b/drivers/media/video/gspca/gl860/gl860-ov2640.c
new file mode 100644
index 00000000..768cac5c
--- /dev/null
+++ b/drivers/media/video/gspca/gl860/gl860-ov2640.c
@@ -0,0 +1,489 @@
+/* Subdriver for the GL860 chip with the OV2640 sensor
+ * Author Olivier LORIN, from Malmostoso's logs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Sensor : OV2640 */
+
+#include "gl860.h"
+
+static u8 dat_init1[] = "\x00\x41\x07\x6a\x06\x61\x0d\x6a" "\x10\x10\xc1\x01";
+
+static u8 c61[] = {0x61}; /* expected */
+static u8 c51[] = {0x51}; /* expected */
+static u8 c50[] = {0x50}; /* expected */
+static u8 c28[] = {0x28}; /* expected */
+static u8 ca8[] = {0xa8}; /* expected */
+
+static u8 dat_post[] =
+ "\x00\x41\x07\x6a\x06\xef\x0d\x6a" "\x10\x10\xc1\x01";
+
+static u8 dat_640[] = "\xd0\x01\xd1\x08\xd2\xe0\xd3\x02\xd4\x10\xd5\x81";
+static u8 dat_800[] = "\xd0\x01\xd1\x10\xd2\x58\xd3\x02\xd4\x18\xd5\x21";
+static u8 dat_1280[] = "\xd0\x01\xd1\x18\xd2\xc0\xd3\x02\xd4\x28\xd5\x01";
+static u8 dat_1600[] = "\xd0\x01\xd1\x20\xd2\xb0\xd3\x02\xd4\x30\xd5\x41";
+
+static struct validx tbl_init_at_startup[] = {
+ {0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001, 0x00c1},
+ {0x0001, 0x00c2}, {0x0020, 0x0006}, {0x006a, 0x000d},
+ {0x0050, 0x0000}, {0x0041, 0x0000}, {0x006a, 0x0007}, {0x0061, 0x0006},
+ {0x006a, 0x000d}, {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0001, 0x00c1},
+ {0x0041, 0x00c2}, {0x0004, 0x00d8}, {0x0012, 0x0004}, {0x0000, 0x0058},
+ {0x0041, 0x0000}, {0x0061, 0x0000},
+};
+
+static struct validx tbl_common[] = {
+ {0x6000, 0x00ff}, {0x60ff, 0x002c}, {0x60df, 0x002e}, {0x6001, 0x00ff},
+ {0x6080, 0x0012}, {0x6000, 0x0000}, {0x6000, 0x0045}, {0x6000, 0x0010},
+ {0x6035, 0x003c}, {0x6000, 0x0011}, {0x6028, 0x0004}, {0x60e5, 0x0013},
+ {0x6088, 0x0014}, {0x600c, 0x002c}, {0x6078, 0x0033}, {0x60f7, 0x003b},
+ {0x6000, 0x003e}, {0x6011, 0x0043}, {0x6010, 0x0016}, {0x6082, 0x0039},
+ {0x6088, 0x0035}, {0x600a, 0x0022}, {0x6040, 0x0037}, {0x6000, 0x0023},
+ {0x60a0, 0x0034}, {0x601a, 0x0036}, {0x6002, 0x0006}, {0x60c0, 0x0007},
+ {0x60b7, 0x000d}, {0x6001, 0x000e}, {0x6000, 0x004c}, {0x6081, 0x004a},
+ {0x6099, 0x0021}, {0x6002, 0x0009}, {0x603e, 0x0024}, {0x6034, 0x0025},
+ {0x6081, 0x0026}, {0x6000, 0x0000}, {0x6000, 0x0045}, {0x6000, 0x0010},
+ {0x6000, 0x005c}, {0x6000, 0x0063}, {0x6000, 0x007c}, {0x6070, 0x0061},
+ {0x6080, 0x0062}, {0x6080, 0x0020}, {0x6030, 0x0028}, {0x6000, 0x006c},
+ {0x6000, 0x006e}, {0x6002, 0x0070}, {0x6094, 0x0071}, {0x60c1, 0x0073},
+ {0x6034, 0x003d}, {0x6057, 0x005a}, {0x60bb, 0x004f}, {0x609c, 0x0050},
+ {0x6080, 0x006d}, {0x6002, 0x0039}, {0x6033, 0x003a}, {0x60f1, 0x003b},
+ {0x6031, 0x003c}, {0x6000, 0x00ff}, {0x6014, 0x00e0}, {0x60ff, 0x0076},
+ {0x60a0, 0x0033}, {0x6020, 0x0042}, {0x6018, 0x0043}, {0x6000, 0x004c},
+ {0x60d0, 0x0087}, {0x600f, 0x0088}, {0x6003, 0x00d7}, {0x6010, 0x00d9},
+ {0x6005, 0x00da}, {0x6082, 0x00d3}, {0x60c0, 0x00f9}, {0x6006, 0x0044},
+ {0x6007, 0x00d1}, {0x6002, 0x00d2}, {0x6000, 0x00d2}, {0x6011, 0x00d8},
+ {0x6008, 0x00c8}, {0x6080, 0x00c9}, {0x6008, 0x007c}, {0x6020, 0x007d},
+ {0x6020, 0x007d}, {0x6000, 0x0090}, {0x600e, 0x0091}, {0x601a, 0x0091},
+ {0x6031, 0x0091}, {0x605a, 0x0091}, {0x6069, 0x0091}, {0x6075, 0x0091},
+ {0x607e, 0x0091}, {0x6088, 0x0091}, {0x608f, 0x0091}, {0x6096, 0x0091},
+ {0x60a3, 0x0091}, {0x60af, 0x0091}, {0x60c4, 0x0091}, {0x60d7, 0x0091},
+ {0x60e8, 0x0091}, {0x6020, 0x0091}, {0x6000, 0x0092}, {0x6006, 0x0093},
+ {0x60e3, 0x0093}, {0x6005, 0x0093}, {0x6005, 0x0093}, {0x6000, 0x0093},
+ {0x6004, 0x0093}, {0x6000, 0x0093}, {0x6000, 0x0093}, {0x6000, 0x0093},
+ {0x6000, 0x0093}, {0x6000, 0x0093}, {0x6000, 0x0093}, {0x6000, 0x0093},
+ {0x6000, 0x0096}, {0x6008, 0x0097}, {0x6019, 0x0097}, {0x6002, 0x0097},
+ {0x600c, 0x0097}, {0x6024, 0x0097}, {0x6030, 0x0097}, {0x6028, 0x0097},
+ {0x6026, 0x0097}, {0x6002, 0x0097}, {0x6098, 0x0097}, {0x6080, 0x0097},
+ {0x6000, 0x0097}, {0x6000, 0x0097}, {0x60ed, 0x00c3}, {0x609a, 0x00c4},
+ {0x6000, 0x00a4}, {0x6011, 0x00c5}, {0x6051, 0x00c6}, {0x6010, 0x00c7},
+ {0x6066, 0x00b6}, {0x60a5, 0x00b8}, {0x6064, 0x00b7}, {0x607c, 0x00b9},
+ {0x60af, 0x00b3}, {0x6097, 0x00b4}, {0x60ff, 0x00b5}, {0x60c5, 0x00b0},
+ {0x6094, 0x00b1}, {0x600f, 0x00b2}, {0x605c, 0x00c4}, {0x6000, 0x00a8},
+ {0x60c8, 0x00c0}, {0x6096, 0x00c1}, {0x601d, 0x0086}, {0x6000, 0x0050},
+ {0x6090, 0x0051}, {0x6018, 0x0052}, {0x6000, 0x0053}, {0x6000, 0x0054},
+ {0x6088, 0x0055}, {0x6000, 0x0057}, {0x6090, 0x005a}, {0x6018, 0x005b},
+ {0x6005, 0x005c}, {0x60ed, 0x00c3}, {0x6000, 0x007f}, {0x6005, 0x00da},
+ {0x601f, 0x00e5}, {0x6067, 0x00e1}, {0x6000, 0x00e0}, {0x60ff, 0x00dd},
+ {0x6000, 0x0005}, {0x6001, 0x00ff}, {0x6000, 0x0000}, {0x6000, 0x0045},
+ {0x6000, 0x0010},
+};
+
+static struct validx tbl_sensor_settings_common1[] = {
+ {0x0041, 0x0000}, {0x006a, 0x0007}, {0x00ef, 0x0006}, {0x006a, 0x000d},
+ {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0001, 0x00c1}, {0x0041, 0x00c2},
+ {0x0004, 0x00d8}, {0x0012, 0x0004}, {0x0000, 0x0058}, {0x0041, 0x0000},
+ {50, 0xffff},
+ {0x0061, 0x0000},
+ {0xffff, 0xffff},
+ {0x6000, 0x00ff}, {0x6000, 0x007c}, {0x6007, 0x007d},
+ {30, 0xffff},
+ {0x0040, 0x0000},
+};
+
+static struct validx tbl_sensor_settings_common2[] = {
+ {0x6001, 0x00ff}, {0x6038, 0x000c},
+ {10, 0xffff},
+ {0x6000, 0x0011},
+};
+
+static struct validx tbl_640[] = {
+ {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, {0x6067, 0x00e1},
+ {0x6004, 0x00da}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0},
+ {0x6001, 0x00ff}, {0x6000, 0x0012}, {0x6000, 0x0011}, {0x6011, 0x0017},
+ {0x6075, 0x0018}, {0x6001, 0x0019}, {0x6097, 0x001a}, {0x6036, 0x0032},
+ {0x60bb, 0x004f}, {0x6057, 0x005a}, {0x609c, 0x0050}, {0x6080, 0x006d},
+ {0x6092, 0x0026}, {0x60ff, 0x0020}, {0x6000, 0x0027}, {0x6000, 0x00ff},
+ {0x60c8, 0x00c0}, {0x6096, 0x00c1}, {0x6000, 0x008c}, {0x603d, 0x0086},
+ {0x6089, 0x0050}, {0x6090, 0x0051}, {0x602c, 0x0052}, {0x6000, 0x0053},
+ {0x6000, 0x0054}, {0x6088, 0x0055}, {0x6000, 0x0057}, {0x60a0, 0x005a},
+ {0x6078, 0x005b}, {0x6000, 0x005c}, {0x6004, 0x00d3}, {0x6000, 0x00e0},
+ {0x60ff, 0x00dd}, {0x60a1, 0x005a},
+};
+
+static struct validx tbl_800[] = {
+ {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, {0x6067, 0x00e1},
+ {0x6004, 0x00da}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0},
+ {0x6001, 0x00ff}, {0x6040, 0x0012}, {0x6000, 0x0011}, {0x6011, 0x0017},
+ {0x6043, 0x0018}, {0x6000, 0x0019}, {0x604b, 0x001a}, {0x6009, 0x0032},
+ {0x60ca, 0x004f}, {0x60a8, 0x0050}, {0x6000, 0x006d}, {0x6038, 0x003d},
+ {0x60c8, 0x0035}, {0x6000, 0x0022}, {0x6092, 0x0026}, {0x60ff, 0x0020},
+ {0x6000, 0x0027}, {0x6000, 0x00ff}, {0x6064, 0x00c0}, {0x604b, 0x00c1},
+ {0x6000, 0x008c}, {0x601d, 0x0086}, {0x6082, 0x00d3}, {0x6000, 0x00e0},
+ {0x60ff, 0x00dd}, {0x6020, 0x008c}, {0x6001, 0x00ff}, {0x6044, 0x0018},
+};
+
+static struct validx tbl_big1[] = {
+ {0x0002, 0x00c1}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0},
+ {0x6001, 0x00ff}, {0x6000, 0x0012}, {0x6000, 0x0000}, {0x6000, 0x0045},
+ {0x6000, 0x0010}, {0x6000, 0x0011}, {0x6011, 0x0017}, {0x6075, 0x0018},
+ {0x6001, 0x0019}, {0x6097, 0x001a}, {0x6036, 0x0032}, {0x60bb, 0x004f},
+ {0x609c, 0x0050}, {0x6057, 0x005a}, {0x6080, 0x006d}, {0x6043, 0x000f},
+ {0x608f, 0x0003}, {0x6005, 0x007c}, {0x6081, 0x0026}, {0x6000, 0x00ff},
+ {0x60c8, 0x00c0}, {0x6096, 0x00c1}, {0x6000, 0x008c},
+};
+
+static struct validx tbl_big2[] = {
+ {0x603d, 0x0086}, {0x6000, 0x0050}, {0x6090, 0x0051}, {0x602c, 0x0052},
+ {0x6000, 0x0053}, {0x6000, 0x0054}, {0x6088, 0x0055}, {0x6000, 0x0057},
+ {0x6040, 0x005a}, {0x60f0, 0x005b}, {0x6001, 0x005c}, {0x6082, 0x00d3},
+ {0x6000, 0x008e},
+};
+
+static struct validx tbl_big3[] = {
+ {0x6004, 0x00da}, {0x6000, 0x00e0}, {0x6067, 0x00e1}, {0x60ff, 0x00dd},
+ {0x6001, 0x00ff}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0},
+ {0x6001, 0x00ff}, {0x6000, 0x0011}, {0x6000, 0x00ff}, {0x6010, 0x00c7},
+ {0x6000, 0x0092}, {0x6006, 0x0093}, {0x60e3, 0x0093}, {0x6005, 0x0093},
+ {0x6005, 0x0093}, {0x60ed, 0x00c3}, {0x6000, 0x00a4}, {0x60d0, 0x0087},
+ {0x6003, 0x0096}, {0x600c, 0x0097}, {0x6024, 0x0097}, {0x6030, 0x0097},
+ {0x6028, 0x0097}, {0x6026, 0x0097}, {0x6002, 0x0097}, {0x6001, 0x00ff},
+ {0x6043, 0x000f}, {0x608f, 0x0003}, {0x6000, 0x002d}, {0x6000, 0x002e},
+ {0x600a, 0x0022}, {0x6002, 0x0070}, {0x6008, 0x0014}, {0x6048, 0x0014},
+ {0x6000, 0x00ff}, {0x6000, 0x00e0}, {0x60ff, 0x00dd},
+};
+
+static struct validx tbl_post_unset_alt[] = {
+ {0x006a, 0x000d}, {0x6001, 0x00ff}, {0x6081, 0x0026}, {0x6000, 0x0000},
+ {0x6000, 0x0045}, {0x6000, 0x0010}, {0x6068, 0x000d},
+ {50, 0xffff},
+ {0x0021, 0x0000},
+};
+
+static int ov2640_init_at_startup(struct gspca_dev *gspca_dev);
+static int ov2640_configure_alt(struct gspca_dev *gspca_dev);
+static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev);
+static int ov2640_init_post_alt(struct gspca_dev *gspca_dev);
+static void ov2640_post_unset_alt(struct gspca_dev *gspca_dev);
+static int ov2640_camera_settings(struct gspca_dev *gspca_dev);
+/*==========================================================================*/
+
+void ov2640_init_settings(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->vcur.backlight = 32;
+ sd->vcur.brightness = 0;
+ sd->vcur.sharpness = 6;
+ sd->vcur.contrast = 0;
+ sd->vcur.gamma = 32;
+ sd->vcur.hue = 0;
+ sd->vcur.saturation = 128;
+ sd->vcur.whitebal = 64;
+ sd->vcur.mirror = 0;
+ sd->vcur.flip = 0;
+
+ sd->vmax.backlight = 64;
+ sd->vmax.brightness = 255;
+ sd->vmax.sharpness = 31;
+ sd->vmax.contrast = 255;
+ sd->vmax.gamma = 64;
+ sd->vmax.hue = 254 + 2;
+ sd->vmax.saturation = 255;
+ sd->vmax.whitebal = 128;
+ sd->vmax.mirror = 1;
+ sd->vmax.flip = 1;
+ sd->vmax.AC50Hz = 0;
+
+ sd->dev_camera_settings = ov2640_camera_settings;
+ sd->dev_init_at_startup = ov2640_init_at_startup;
+ sd->dev_configure_alt = ov2640_configure_alt;
+ sd->dev_init_pre_alt = ov2640_init_pre_alt;
+ sd->dev_post_unset_alt = ov2640_post_unset_alt;
+}
+
+/*==========================================================================*/
+
+static void common(struct gspca_dev *gspca_dev)
+{
+ fetch_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common));
+}
+
+static int ov2640_init_at_startup(struct gspca_dev *gspca_dev)
+{
+ fetch_validx(gspca_dev, tbl_init_at_startup,
+ ARRAY_SIZE(tbl_init_at_startup));
+
+ ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_init1);
+
+ common(gspca_dev);
+
+ ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0006, 1, c61);
+
+ ctrl_out(gspca_dev, 0x40, 1, 0x00ef, 0x0006, 0, NULL);
+
+ ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c51);
+
+ ctrl_out(gspca_dev, 0x40, 1, 0x0051, 0x0000, 0, NULL);
+/* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL); */
+
+ return 0;
+}
+
+static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->mirrorMask = 0;
+
+ sd->vold.backlight = -1;
+ sd->vold.brightness = -1;
+ sd->vold.sharpness = -1;
+ sd->vold.contrast = -1;
+ sd->vold.saturation = -1;
+ sd->vold.gamma = -1;
+ sd->vold.hue = -1;
+ sd->vold.whitebal = -1;
+ sd->vold.mirror = -1;
+ sd->vold.flip = -1;
+
+ ov2640_init_post_alt(gspca_dev);
+
+ return 0;
+}
+
+static int ov2640_init_post_alt(struct gspca_dev *gspca_dev)
+{
+ s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
+ s32 n; /* reserved for FETCH functions */
+
+ ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL);
+
+ n = fetch_validx(gspca_dev, tbl_sensor_settings_common1,
+ ARRAY_SIZE(tbl_sensor_settings_common1));
+ ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_post);
+ common(gspca_dev);
+ keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common1,
+ ARRAY_SIZE(tbl_sensor_settings_common1), n);
+
+ switch (reso) {
+ case IMAGE_640:
+ n = fetch_validx(gspca_dev, tbl_640, ARRAY_SIZE(tbl_640));
+ ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_640);
+ break;
+
+ case IMAGE_800:
+ n = fetch_validx(gspca_dev, tbl_800, ARRAY_SIZE(tbl_800));
+ ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_800);
+ break;
+
+ case IMAGE_1600:
+ case IMAGE_1280:
+ n = fetch_validx(gspca_dev, tbl_big1, ARRAY_SIZE(tbl_big1));
+
+ if (reso == IMAGE_1280) {
+ n = fetch_validx(gspca_dev, tbl_big2,
+ ARRAY_SIZE(tbl_big2));
+ } else {
+ ctrl_out(gspca_dev, 0x40, 1, 0x601d, 0x0086, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00d7, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6082, 0x00d3, 0, NULL);
+ }
+
+ n = fetch_validx(gspca_dev, tbl_big3, ARRAY_SIZE(tbl_big3));
+
+ if (reso == IMAGE_1280) {
+ ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200,
+ 12, dat_1280);
+ } else {
+ ctrl_out(gspca_dev, 0x40, 1, 0x6020, 0x008c, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6076, 0x0018, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200,
+ 12, dat_1600);
+ }
+ break;
+ }
+
+ n = fetch_validx(gspca_dev, tbl_sensor_settings_common2,
+ ARRAY_SIZE(tbl_sensor_settings_common2));
+
+ ov2640_camera_settings(gspca_dev);
+
+ return 0;
+}
+
+static int ov2640_configure_alt(struct gspca_dev *gspca_dev)
+{
+ s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
+
+ switch (reso) {
+ case IMAGE_640:
+ gspca_dev->alt = 3 + 1;
+ break;
+
+ case IMAGE_800:
+ case IMAGE_1280:
+ case IMAGE_1600:
+ gspca_dev->alt = 1 + 1;
+ break;
+ }
+ return 0;
+}
+
+static int ov2640_camera_settings(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ s32 backlight = sd->vcur.backlight;
+ s32 bright = sd->vcur.brightness;
+ s32 sharp = sd->vcur.sharpness;
+ s32 gam = sd->vcur.gamma;
+ s32 cntr = sd->vcur.contrast;
+ s32 sat = sd->vcur.saturation;
+ s32 hue = sd->vcur.hue;
+ s32 wbal = sd->vcur.whitebal;
+ s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) == 0);
+ s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) == 0);
+
+ if (backlight != sd->vold.backlight) {
+ /* No sd->vold.backlight=backlight; (to be done again later) */
+ if (backlight < 0 || backlight > sd->vmax.backlight)
+ backlight = 0;
+
+ ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x00ff,
+ 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight , 0x0024,
+ 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight - 10, 0x0025,
+ 0, NULL);
+ }
+
+ if (bright != sd->vold.brightness) {
+ sd->vold.brightness = bright;
+ if (bright < 0 || bright > sd->vmax.brightness)
+ bright = 0;
+
+ ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6009 , 0x007c, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6000 + bright, 0x007d, 0, NULL);
+ }
+
+ if (wbal != sd->vold.whitebal) {
+ sd->vold.whitebal = wbal;
+ if (wbal < 0 || wbal > sd->vmax.whitebal)
+ wbal = 0;
+
+ ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6003 , 0x007c, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6000 + wbal, 0x007d, 0, NULL);
+ }
+
+ if (cntr != sd->vold.contrast) {
+ sd->vold.contrast = cntr;
+ if (cntr < 0 || cntr > sd->vmax.contrast)
+ cntr = 0;
+
+ ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6007 , 0x007c, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6000 + cntr, 0x007d, 0, NULL);
+ }
+
+ if (sat != sd->vold.saturation) {
+ sd->vold.saturation = sat;
+ if (sat < 0 || sat > sd->vmax.saturation)
+ sat = 0;
+
+ ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x007c, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6000 + sat, 0x007d, 0, NULL);
+ }
+
+ if (sharp != sd->vold.sharpness) {
+ sd->vold.sharpness = sharp;
+ if (sharp < 0 || sharp > sd->vmax.sharpness)
+ sharp = 0;
+
+ ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x0092, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x60c0 + sharp, 0x0093, 0, NULL);
+ }
+
+ if (hue != sd->vold.hue) {
+ sd->vold.hue = hue;
+ if (hue < 0 || hue > sd->vmax.hue)
+ hue = 0;
+
+ ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6002 , 0x007c, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6000 + hue * (hue < 255), 0x007d,
+ 0, NULL);
+ if (hue >= 255)
+ sd->swapRB = 1;
+ else
+ sd->swapRB = 0;
+ }
+
+ if (gam != sd->vold.gamma) {
+ sd->vold.gamma = gam;
+ if (gam < 0 || gam > sd->vmax.gamma)
+ gam = 0;
+
+ ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6008 , 0x007c, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6000 + gam, 0x007d, 0, NULL);
+ }
+
+ if (mirror != sd->vold.mirror || flip != sd->vold.flip) {
+ sd->vold.mirror = mirror;
+ sd->vold.flip = flip;
+
+ mirror = 0x80 * mirror;
+ ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x8004, 0, NULL);
+ ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, c28);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6028 + mirror, 0x0004, 0, NULL);
+
+ flip = 0x50 * flip + mirror;
+ ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x8004, 0, NULL);
+ ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, ca8);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6028 + flip, 0x0004, 0, NULL);
+
+ ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50);
+ }
+
+ if (backlight != sd->vold.backlight) {
+ sd->vold.backlight = backlight;
+
+ ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x00ff,
+ 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight , 0x0024,
+ 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight - 10, 0x0025,
+ 0, NULL);
+ }
+
+ return 0;
+}
+
+static void ov2640_post_unset_alt(struct gspca_dev *gspca_dev)
+{
+ ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL);
+ msleep(20);
+ fetch_validx(gspca_dev, tbl_post_unset_alt,
+ ARRAY_SIZE(tbl_post_unset_alt));
+}
diff --git a/drivers/media/video/gspca/gl860/gl860-ov9655.c b/drivers/media/video/gspca/gl860/gl860-ov9655.c
new file mode 100644
index 00000000..5ae9619d
--- /dev/null
+++ b/drivers/media/video/gspca/gl860/gl860-ov9655.c
@@ -0,0 +1,336 @@
+/* Subdriver for the GL860 chip with the OV9655 sensor
+ * Author Olivier LORIN, from logs done by Simon (Sur3) and Almighurt
+ * on dsd's weblog
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Sensor : OV9655 */
+
+#include "gl860.h"
+
+static struct validx tbl_init_at_startup[] = {
+ {0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001, 0x00c1},
+ {0x0001, 0x00c2}, {0x0020, 0x0006}, {0x006a, 0x000d},
+
+ {0x0040, 0x0000},
+};
+
+static struct validx tbl_commmon[] = {
+ {0x0041, 0x0000}, {0x006a, 0x0007}, {0x0063, 0x0006}, {0x006a, 0x000d},
+ {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0001, 0x00c1}, {0x0041, 0x00c2},
+ {0x0004, 0x00d8}, {0x0012, 0x0004}, {0x0000, 0x0058}, {0x0040, 0x0000},
+ {0x00f3, 0x0006}, {0x0058, 0x0000}, {0x0048, 0x0000}, {0x0061, 0x0000},
+};
+
+static s32 tbl_length[] = {12, 56, 52, 54, 56, 42, 32, 12};
+
+static u8 *tbl_640[] = {
+ "\x00\x40\x07\x6a\x06\xf3\x0d\x6a" "\x10\x10\xc1\x01"
+ ,
+ "\x12\x80\x00\x00\x01\x98\x02\x80" "\x03\x12\x04\x03\x0b\x57\x0e\x61"
+ "\x0f\x42\x11\x01\x12\x60\x13\x00" "\x14\x3a\x16\x24\x17\x14\x18\x00"
+ "\x19\x01\x1a\x3d\x1e\x04\x24\x3c" "\x25\x36\x26\x72\x27\x08\x28\x08"
+ "\x29\x15\x2a\x00\x2b\x00\x2c\x08"
+ ,
+ "\x32\xff\x33\x00\x34\x3d\x35\x00" "\x36\xfa\x38\x72\x39\x57\x3a\x00"
+ "\x3b\x0c\x3d\x99\x3e\x0c\x3f\xc1" "\x40\xc0\x41\x00\x42\xc0\x43\x0a"
+ "\x44\xf0\x45\x46\x46\x62\x47\x2a" "\x48\x3c\x4a\xee\x4b\xe7\x4c\xe7"
+ "\x4d\xe7\x4e\xe7"
+ ,
+ "\x4f\x98\x50\x98\x51\x00\x52\x28" "\x53\x70\x54\x98\x58\x1a\x59\x85"
+ "\x5a\xa9\x5b\x64\x5c\x84\x5d\x53" "\x5e\x0e\x5f\xf0\x60\xf0\x61\xf0"
+ "\x62\x00\x63\x00\x64\x02\x65\x20" "\x66\x00\x69\x0a\x6b\x5a\x6c\x04"
+ "\x6d\x55\x6e\x00\x6f\x9d"
+ ,
+ "\x70\x15\x71\x78\x72\x00\x73\x00" "\x74\x3a\x75\x35\x76\x01\x77\x02"
+ "\x7a\x24\x7b\x04\x7c\x07\x7d\x10" "\x7e\x28\x7f\x36\x80\x44\x81\x52"
+ "\x82\x60\x83\x6c\x84\x78\x85\x8c" "\x86\x9e\x87\xbb\x88\xd2\x89\xe5"
+ "\x8a\x23\x8c\x8d\x90\x7c\x91\x7b"
+ ,
+ "\x9d\x02\x9e\x02\x9f\x74\xa0\x73" "\xa1\x40\xa4\x50\xa5\x68\xa6\x70"
+ "\xa8\xc1\xa9\xef\xaa\x92\xab\x04" "\xac\x80\xad\x80\xae\x80\xaf\x80"
+ "\xb2\xf2\xb3\x20\xb4\x20\xb5\x00" "\xb6\xaf"
+ ,
+ "\xbb\xae\xbc\x4f\xbd\x4e\xbe\x6a" "\xbf\x68\xc0\xaa\xc1\xc0\xc2\x01"
+ "\xc3\x4e\xc6\x85\xc7\x81\xc9\xe0" "\xca\xe8\xcb\xf0\xcc\xd8\xcd\x93"
+ ,
+ "\xd0\x01\xd1\x08\xd2\xe0\xd3\x01" "\xd4\x10\xd5\x80"
+};
+
+static u8 *tbl_1280[] = {
+ "\x00\x40\x07\x6a\x06\xf3\x0d\x6a" "\x10\x10\xc1\x01"
+ ,
+ "\x12\x80\x00\x00\x01\x98\x02\x80" "\x03\x12\x04\x01\x0b\x57\x0e\x61"
+ "\x0f\x42\x11\x00\x12\x00\x13\x00" "\x14\x3a\x16\x24\x17\x1b\x18\xbb"
+ "\x19\x01\x1a\x81\x1e\x04\x24\x3c" "\x25\x36\x26\x72\x27\x08\x28\x08"
+ "\x29\x15\x2a\x00\x2b\x00\x2c\x08"
+ ,
+ "\x32\xa4\x33\x00\x34\x3d\x35\x00" "\x36\xf8\x38\x72\x39\x57\x3a\x00"
+ "\x3b\x0c\x3d\x99\x3e\x0c\x3f\xc2" "\x40\xc0\x41\x00\x42\xc0\x43\x0a"
+ "\x44\xf0\x45\x46\x46\x62\x47\x2a" "\x48\x3c\x4a\xec\x4b\xe8\x4c\xe8"
+ "\x4d\xe8\x4e\xe8"
+ ,
+ "\x4f\x98\x50\x98\x51\x00\x52\x28" "\x53\x70\x54\x98\x58\x1a\x59\x85"
+ "\x5a\xa9\x5b\x64\x5c\x84\x5d\x53" "\x5e\x0e\x5f\xf0\x60\xf0\x61\xf0"
+ "\x62\x00\x63\x00\x64\x02\x65\x20" "\x66\x00\x69\x02\x6b\x5a\x6c\x04"
+ "\x6d\x55\x6e\x00\x6f\x9d"
+ ,
+ "\x70\x08\x71\x78\x72\x00\x73\x01" "\x74\x3a\x75\x35\x76\x01\x77\x02"
+ "\x7a\x24\x7b\x04\x7c\x07\x7d\x10" "\x7e\x28\x7f\x36\x80\x44\x81\x52"
+ "\x82\x60\x83\x6c\x84\x78\x85\x8c" "\x86\x9e\x87\xbb\x88\xd2\x89\xe5"
+ "\x8a\x23\x8c\x0d\x90\x90\x91\x90"
+ ,
+ "\x9d\x02\x9e\x02\x9f\x94\xa0\x94" "\xa1\x01\xa4\x50\xa5\x68\xa6\x70"
+ "\xa8\xc1\xa9\xef\xaa\x92\xab\x04" "\xac\x80\xad\x80\xae\x80\xaf\x80"
+ "\xb2\xf2\xb3\x20\xb4\x20\xb5\x00" "\xb6\xaf"
+ ,
+ "\xbb\xae\xbc\x38\xbd\x39\xbe\x01" "\xbf\x01\xc0\xe2\xc1\xc0\xc2\x01"
+ "\xc3\x4e\xc6\x85\xc7\x81\xc9\xe0" "\xca\xe8\xcb\xf0\xcc\xd8\xcd\x93"
+ ,
+ "\xd0\x21\xd1\x18\xd2\xe0\xd3\x01" "\xd4\x28\xd5\x00"
+};
+
+static u8 c04[] = {0x04};
+static u8 dat_post1[] = "\x04\x00\x10\x20\xa1\x00\x00\x02";
+static u8 dat_post2[] = "\x10\x10\xc1\x02";
+static u8 dat_post3[] = "\x04\x00\x10\x7c\xa1\x00\x00\x04";
+static u8 dat_post4[] = "\x10\x02\xc1\x06";
+static u8 dat_post5[] = "\x04\x00\x10\x7b\xa1\x00\x00\x08";
+static u8 dat_post6[] = "\x10\x10\xc1\x05";
+static u8 dat_post7[] = "\x04\x00\x10\x7c\xa1\x00\x00\x08";
+static u8 dat_post8[] = "\x04\x00\x10\x7c\xa1\x00\x00\x09";
+
+static struct validx tbl_init_post_alt[] = {
+ {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x603c, 0x00ff},
+ {0x6003, 0x00ff}, {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x6001, 0x00ff},
+ {0x6000, 0x801e},
+ {0xffff, 0xffff},
+ {0x6004, 0x001e}, {0x6000, 0x801e},
+ {0xffff, 0xffff},
+ {0x6004, 0x001e}, {0x6012, 0x0003}, {0x6000, 0x801e},
+ {0xffff, 0xffff},
+ {0x6004, 0x001e}, {0x6000, 0x801e},
+ {0xffff, 0xffff},
+ {0x6004, 0x001e}, {0x6012, 0x0003},
+ {0xffff, 0xffff},
+ {0x6000, 0x801e},
+ {0xffff, 0xffff},
+ {0x6004, 0x001e}, {0x6000, 0x801e},
+ {0xffff, 0xffff},
+ {0x6004, 0x001e}, {0x6012, 0x0003}, {0x6000, 0x801e},
+ {0xffff, 0xffff},
+ {0x6004, 0x001e}, {0x6000, 0x801e},
+ {0xffff, 0xffff},
+ {0x6004, 0x001e}, {0x6012, 0x0003},
+ {0xffff, 0xffff},
+ {0x6000, 0x801e},
+ {0xffff, 0xffff},
+ {0x6004, 0x001e}, {0x6000, 0x801e},
+ {0xffff, 0xffff},
+ {0x6004, 0x001e}, {0x6012, 0x0003},
+};
+
+static int ov9655_init_at_startup(struct gspca_dev *gspca_dev);
+static int ov9655_configure_alt(struct gspca_dev *gspca_dev);
+static int ov9655_init_pre_alt(struct gspca_dev *gspca_dev);
+static int ov9655_init_post_alt(struct gspca_dev *gspca_dev);
+static void ov9655_post_unset_alt(struct gspca_dev *gspca_dev);
+static int ov9655_camera_settings(struct gspca_dev *gspca_dev);
+/*==========================================================================*/
+
+void ov9655_init_settings(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->vcur.backlight = 0;
+ sd->vcur.brightness = 128;
+ sd->vcur.sharpness = 0;
+ sd->vcur.contrast = 0;
+ sd->vcur.gamma = 0;
+ sd->vcur.hue = 0;
+ sd->vcur.saturation = 0;
+ sd->vcur.whitebal = 0;
+
+ sd->vmax.backlight = 0;
+ sd->vmax.brightness = 255;
+ sd->vmax.sharpness = 0;
+ sd->vmax.contrast = 0;
+ sd->vmax.gamma = 0;
+ sd->vmax.hue = 0 + 1;
+ sd->vmax.saturation = 0;
+ sd->vmax.whitebal = 0;
+ sd->vmax.mirror = 0;
+ sd->vmax.flip = 0;
+ sd->vmax.AC50Hz = 0;
+
+ sd->dev_camera_settings = ov9655_camera_settings;
+ sd->dev_init_at_startup = ov9655_init_at_startup;
+ sd->dev_configure_alt = ov9655_configure_alt;
+ sd->dev_init_pre_alt = ov9655_init_pre_alt;
+ sd->dev_post_unset_alt = ov9655_post_unset_alt;
+}
+
+/*==========================================================================*/
+
+static int ov9655_init_at_startup(struct gspca_dev *gspca_dev)
+{
+ fetch_validx(gspca_dev, tbl_init_at_startup,
+ ARRAY_SIZE(tbl_init_at_startup));
+ fetch_validx(gspca_dev, tbl_commmon, ARRAY_SIZE(tbl_commmon));
+/* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL);*/
+
+ return 0;
+}
+
+static int ov9655_init_pre_alt(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->vold.brightness = -1;
+ sd->vold.hue = -1;
+
+ fetch_validx(gspca_dev, tbl_commmon, ARRAY_SIZE(tbl_commmon));
+
+ ov9655_init_post_alt(gspca_dev);
+
+ return 0;
+}
+
+static int ov9655_init_post_alt(struct gspca_dev *gspca_dev)
+{
+ s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
+ s32 n; /* reserved for FETCH functions */
+ s32 i;
+ u8 **tbl;
+
+ ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL);
+
+ tbl = (reso == IMAGE_640) ? tbl_640 : tbl_1280;
+
+ ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200,
+ tbl_length[0], tbl[0]);
+ for (i = 1; i < 7; i++)
+ ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200,
+ tbl_length[i], tbl[i]);
+ ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200,
+ tbl_length[7], tbl[7]);
+
+ n = fetch_validx(gspca_dev, tbl_init_post_alt,
+ ARRAY_SIZE(tbl_init_post_alt));
+
+ ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
+ keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+ ARRAY_SIZE(tbl_init_post_alt), n);
+ ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
+ keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+ ARRAY_SIZE(tbl_init_post_alt), n);
+ ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
+ keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+ ARRAY_SIZE(tbl_init_post_alt), n);
+ ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
+ keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+ ARRAY_SIZE(tbl_init_post_alt), n);
+ ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1);
+ keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+ ARRAY_SIZE(tbl_init_post_alt), n);
+
+ ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
+ keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+ ARRAY_SIZE(tbl_init_post_alt), n);
+ ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
+ keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+ ARRAY_SIZE(tbl_init_post_alt), n);
+ ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
+ keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+ ARRAY_SIZE(tbl_init_post_alt), n);
+ ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
+ keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+ ARRAY_SIZE(tbl_init_post_alt), n);
+ ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1);
+ keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+ ARRAY_SIZE(tbl_init_post_alt), n);
+
+ ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
+ keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+ ARRAY_SIZE(tbl_init_post_alt), n);
+ ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
+ keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+ ARRAY_SIZE(tbl_init_post_alt), n);
+
+ ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1);
+
+ ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post2);
+ ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post3);
+
+ ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post4);
+ ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post5);
+
+ ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post6);
+ ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post7);
+
+ ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post8);
+
+ ov9655_camera_settings(gspca_dev);
+
+ return 0;
+}
+
+static int ov9655_configure_alt(struct gspca_dev *gspca_dev)
+{
+ s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
+
+ switch (reso) {
+ case IMAGE_640:
+ gspca_dev->alt = 1 + 1;
+ break;
+
+ default:
+ gspca_dev->alt = 1 + 1;
+ break;
+ }
+ return 0;
+}
+
+static int ov9655_camera_settings(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ u8 dat_bright[] = "\x04\x00\x10\x7c\xa1\x00\x00\x70";
+
+ s32 bright = sd->vcur.brightness;
+ s32 hue = sd->vcur.hue;
+
+ if (bright != sd->vold.brightness) {
+ sd->vold.brightness = bright;
+ if (bright < 0 || bright > sd->vmax.brightness)
+ bright = 0;
+
+ dat_bright[3] = bright;
+ ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_bright);
+ }
+
+ if (hue != sd->vold.hue) {
+ sd->vold.hue = hue;
+ sd->swapRB = (hue != 0);
+ }
+
+ return 0;
+}
+
+static void ov9655_post_unset_alt(struct gspca_dev *gspca_dev)
+{
+ ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x0061, 0x0000, 0, NULL);
+}
diff --git a/drivers/media/video/gspca/gl860/gl860.c b/drivers/media/video/gspca/gl860/gl860.c
new file mode 100644
index 00000000..c84e2600
--- /dev/null
+++ b/drivers/media/video/gspca/gl860/gl860.c
@@ -0,0 +1,721 @@
+/* GSPCA subdrivers for Genesys Logic webcams with the GL860 chip
+ * Subdriver core
+ *
+ * 2009/09/24 Olivier Lorin <o.lorin@laposte.net>
+ * GSPCA by Jean-Francois Moine <http://moinejf.free.fr>
+ * Thanks BUGabundo and Malmostoso for your amazing help!
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "gspca.h"
+#include "gl860.h"
+
+MODULE_AUTHOR("Olivier Lorin <o.lorin@laposte.net>");
+MODULE_DESCRIPTION("Genesys Logic USB PC Camera Driver");
+MODULE_LICENSE("GPL");
+
+/*======================== static function declarations ====================*/
+
+static void (*dev_init_settings)(struct gspca_dev *gspca_dev);
+
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id);
+static int sd_init(struct gspca_dev *gspca_dev);
+static int sd_isoc_init(struct gspca_dev *gspca_dev);
+static int sd_start(struct gspca_dev *gspca_dev);
+static void sd_stop0(struct gspca_dev *gspca_dev);
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, int len);
+static void sd_callback(struct gspca_dev *gspca_dev);
+
+static int gl860_guess_sensor(struct gspca_dev *gspca_dev,
+ u16 vendor_id, u16 product_id);
+
+/*============================ driver options ==============================*/
+
+static s32 AC50Hz = 0xff;
+module_param(AC50Hz, int, 0644);
+MODULE_PARM_DESC(AC50Hz, " Does AC power frequency is 50Hz? (0/1)");
+
+static char sensor[7];
+module_param_string(sensor, sensor, sizeof(sensor), 0644);
+MODULE_PARM_DESC(sensor,
+ " Driver sensor ('MI1320'/'MI2020'/'OV9655'/'OV2640')");
+
+/*============================ webcam controls =============================*/
+
+/* Functions to get and set a control value */
+#define SD_SETGET(thename) \
+static int sd_set_##thename(struct gspca_dev *gspca_dev, s32 val)\
+{\
+ struct sd *sd = (struct sd *) gspca_dev;\
+\
+ sd->vcur.thename = val;\
+ if (gspca_dev->streaming)\
+ sd->waitSet = 1;\
+ return 0;\
+} \
+static int sd_get_##thename(struct gspca_dev *gspca_dev, s32 *val)\
+{\
+ struct sd *sd = (struct sd *) gspca_dev;\
+\
+ *val = sd->vcur.thename;\
+ return 0;\
+}
+
+SD_SETGET(mirror)
+SD_SETGET(flip)
+SD_SETGET(AC50Hz)
+SD_SETGET(backlight)
+SD_SETGET(brightness)
+SD_SETGET(gamma)
+SD_SETGET(hue)
+SD_SETGET(saturation)
+SD_SETGET(sharpness)
+SD_SETGET(whitebal)
+SD_SETGET(contrast)
+
+#define GL860_NCTRLS 11
+
+/* control table */
+static struct ctrl sd_ctrls_mi1320[GL860_NCTRLS];
+static struct ctrl sd_ctrls_mi2020[GL860_NCTRLS];
+static struct ctrl sd_ctrls_ov2640[GL860_NCTRLS];
+static struct ctrl sd_ctrls_ov9655[GL860_NCTRLS];
+
+#define SET_MY_CTRL(theid, \
+ thetype, thelabel, thename) \
+ if (sd->vmax.thename != 0) {\
+ sd_ctrls[nCtrls].qctrl.id = theid;\
+ sd_ctrls[nCtrls].qctrl.type = thetype;\
+ strcpy(sd_ctrls[nCtrls].qctrl.name, thelabel);\
+ sd_ctrls[nCtrls].qctrl.minimum = 0;\
+ sd_ctrls[nCtrls].qctrl.maximum = sd->vmax.thename;\
+ sd_ctrls[nCtrls].qctrl.default_value = sd->vcur.thename;\
+ sd_ctrls[nCtrls].qctrl.step = \
+ (sd->vmax.thename < 16) ? 1 : sd->vmax.thename/16;\
+ sd_ctrls[nCtrls].set = sd_set_##thename;\
+ sd_ctrls[nCtrls].get = sd_get_##thename;\
+ nCtrls++;\
+ }
+
+static int gl860_build_control_table(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct ctrl *sd_ctrls;
+ int nCtrls = 0;
+
+ if (_MI1320_)
+ sd_ctrls = sd_ctrls_mi1320;
+ else if (_MI2020_)
+ sd_ctrls = sd_ctrls_mi2020;
+ else if (_OV2640_)
+ sd_ctrls = sd_ctrls_ov2640;
+ else if (_OV9655_)
+ sd_ctrls = sd_ctrls_ov9655;
+ else
+ return 0;
+
+ memset(sd_ctrls, 0, GL860_NCTRLS * sizeof(struct ctrl));
+
+ SET_MY_CTRL(V4L2_CID_BRIGHTNESS,
+ V4L2_CTRL_TYPE_INTEGER, "Brightness", brightness)
+ SET_MY_CTRL(V4L2_CID_SHARPNESS,
+ V4L2_CTRL_TYPE_INTEGER, "Sharpness", sharpness)
+ SET_MY_CTRL(V4L2_CID_CONTRAST,
+ V4L2_CTRL_TYPE_INTEGER, "Contrast", contrast)
+ SET_MY_CTRL(V4L2_CID_GAMMA,
+ V4L2_CTRL_TYPE_INTEGER, "Gamma", gamma)
+ SET_MY_CTRL(V4L2_CID_HUE,
+ V4L2_CTRL_TYPE_INTEGER, "Palette", hue)
+ SET_MY_CTRL(V4L2_CID_SATURATION,
+ V4L2_CTRL_TYPE_INTEGER, "Saturation", saturation)
+ SET_MY_CTRL(V4L2_CID_WHITE_BALANCE_TEMPERATURE,
+ V4L2_CTRL_TYPE_INTEGER, "White Bal.", whitebal)
+ SET_MY_CTRL(V4L2_CID_BACKLIGHT_COMPENSATION,
+ V4L2_CTRL_TYPE_INTEGER, "Backlight" , backlight)
+
+ SET_MY_CTRL(V4L2_CID_HFLIP,
+ V4L2_CTRL_TYPE_BOOLEAN, "Mirror", mirror)
+ SET_MY_CTRL(V4L2_CID_VFLIP,
+ V4L2_CTRL_TYPE_BOOLEAN, "Flip", flip)
+ SET_MY_CTRL(V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CTRL_TYPE_BOOLEAN, "AC power 50Hz", AC50Hz)
+
+ return nCtrls;
+}
+
+/*==================== sud-driver structure initialisation =================*/
+
+static const struct sd_desc sd_desc_mi1320 = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls_mi1320,
+ .nctrls = GL860_NCTRLS,
+ .config = sd_config,
+ .init = sd_init,
+ .isoc_init = sd_isoc_init,
+ .start = sd_start,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+ .dq_callback = sd_callback,
+};
+
+static const struct sd_desc sd_desc_mi2020 = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls_mi2020,
+ .nctrls = GL860_NCTRLS,
+ .config = sd_config,
+ .init = sd_init,
+ .isoc_init = sd_isoc_init,
+ .start = sd_start,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+ .dq_callback = sd_callback,
+};
+
+static const struct sd_desc sd_desc_ov2640 = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls_ov2640,
+ .nctrls = GL860_NCTRLS,
+ .config = sd_config,
+ .init = sd_init,
+ .isoc_init = sd_isoc_init,
+ .start = sd_start,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+ .dq_callback = sd_callback,
+};
+
+static const struct sd_desc sd_desc_ov9655 = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls_ov9655,
+ .nctrls = GL860_NCTRLS,
+ .config = sd_config,
+ .init = sd_init,
+ .isoc_init = sd_isoc_init,
+ .start = sd_start,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+ .dq_callback = sd_callback,
+};
+
+/*=========================== sub-driver image sizes =======================*/
+
+static struct v4l2_pix_format mi2020_mode[] = {
+ { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0
+ },
+ { 800, 598, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+ .bytesperline = 800,
+ .sizeimage = 800 * 598,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1
+ },
+ {1280, 1024, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+ .bytesperline = 1280,
+ .sizeimage = 1280 * 1024,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 2
+ },
+ {1600, 1198, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+ .bytesperline = 1600,
+ .sizeimage = 1600 * 1198,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 3
+ },
+};
+
+static struct v4l2_pix_format ov2640_mode[] = {
+ { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0
+ },
+ { 800, 600, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+ .bytesperline = 800,
+ .sizeimage = 800 * 600,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1
+ },
+ {1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+ .bytesperline = 1280,
+ .sizeimage = 1280 * 960,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 2
+ },
+ {1600, 1200, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+ .bytesperline = 1600,
+ .sizeimage = 1600 * 1200,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 3
+ },
+};
+
+static struct v4l2_pix_format mi1320_mode[] = {
+ { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0
+ },
+ { 800, 600, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+ .bytesperline = 800,
+ .sizeimage = 800 * 600,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1
+ },
+ {1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+ .bytesperline = 1280,
+ .sizeimage = 1280 * 960,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 2
+ },
+};
+
+static struct v4l2_pix_format ov9655_mode[] = {
+ { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0
+ },
+ {1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+ .bytesperline = 1280,
+ .sizeimage = 1280 * 960,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1
+ },
+};
+
+/*========================= sud-driver functions ===========================*/
+
+/* This function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+ u16 vendor_id, product_id;
+
+ /* Get USB VendorID and ProductID */
+ vendor_id = id->idVendor;
+ product_id = id->idProduct;
+
+ sd->nbRightUp = 1;
+ sd->nbIm = -1;
+
+ sd->sensor = 0xff;
+ if (strcmp(sensor, "MI1320") == 0)
+ sd->sensor = ID_MI1320;
+ else if (strcmp(sensor, "OV2640") == 0)
+ sd->sensor = ID_OV2640;
+ else if (strcmp(sensor, "OV9655") == 0)
+ sd->sensor = ID_OV9655;
+ else if (strcmp(sensor, "MI2020") == 0)
+ sd->sensor = ID_MI2020;
+
+ /* Get sensor and set the suitable init/start/../stop functions */
+ if (gl860_guess_sensor(gspca_dev, vendor_id, product_id) == -1)
+ return -1;
+
+ cam = &gspca_dev->cam;
+
+ switch (sd->sensor) {
+ case ID_MI1320:
+ gspca_dev->sd_desc = &sd_desc_mi1320;
+ cam->cam_mode = mi1320_mode;
+ cam->nmodes = ARRAY_SIZE(mi1320_mode);
+ dev_init_settings = mi1320_init_settings;
+ break;
+
+ case ID_MI2020:
+ gspca_dev->sd_desc = &sd_desc_mi2020;
+ cam->cam_mode = mi2020_mode;
+ cam->nmodes = ARRAY_SIZE(mi2020_mode);
+ dev_init_settings = mi2020_init_settings;
+ break;
+
+ case ID_OV2640:
+ gspca_dev->sd_desc = &sd_desc_ov2640;
+ cam->cam_mode = ov2640_mode;
+ cam->nmodes = ARRAY_SIZE(ov2640_mode);
+ dev_init_settings = ov2640_init_settings;
+ break;
+
+ case ID_OV9655:
+ gspca_dev->sd_desc = &sd_desc_ov9655;
+ cam->cam_mode = ov9655_mode;
+ cam->nmodes = ARRAY_SIZE(ov9655_mode);
+ dev_init_settings = ov9655_init_settings;
+ break;
+ }
+
+ dev_init_settings(gspca_dev);
+ if (AC50Hz != 0xff)
+ ((struct sd *) gspca_dev)->vcur.AC50Hz = AC50Hz;
+ gl860_build_control_table(gspca_dev);
+
+ return 0;
+}
+
+/* This function is called at probe time after sd_config */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return sd->dev_init_at_startup(gspca_dev);
+}
+
+/* This function is called before to choose the alt setting */
+static int sd_isoc_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return sd->dev_configure_alt(gspca_dev);
+}
+
+/* This function is called to start the webcam */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return sd->dev_init_pre_alt(gspca_dev);
+}
+
+/* This function is called to stop the webcam */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return sd->dev_post_unset_alt(gspca_dev);
+}
+
+/* This function is called when an image is being received */
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ static s32 nSkipped;
+
+ s32 mode = (s32) gspca_dev->curr_mode;
+ s32 nToSkip =
+ sd->swapRB * (gspca_dev->cam.cam_mode[mode].bytesperline + 1);
+
+ /* Test only against 0202h, so endianess does not matter */
+ switch (*(s16 *) data) {
+ case 0x0202: /* End of frame, start a new one */
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+ nSkipped = 0;
+ if (sd->nbIm >= 0 && sd->nbIm < 10)
+ sd->nbIm++;
+ gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+ break;
+
+ default:
+ data += 2;
+ len -= 2;
+ if (nSkipped + len <= nToSkip)
+ nSkipped += len;
+ else {
+ if (nSkipped < nToSkip && nSkipped + len > nToSkip) {
+ data += nToSkip - nSkipped;
+ len -= nToSkip - nSkipped;
+ nSkipped = nToSkip + 1;
+ }
+ gspca_frame_add(gspca_dev,
+ INTER_PACKET, data, len);
+ }
+ break;
+ }
+}
+
+/* This function is called when an image has been read */
+/* This function is used to monitor webcam orientation */
+static void sd_callback(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (!_OV9655_) {
+ u8 state;
+ u8 upsideDown;
+
+ /* Probe sensor orientation */
+ ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, (void *)&state);
+
+ /* C8/40 means upside-down (looking backwards) */
+ /* D8/50 means right-up (looking onwards) */
+ upsideDown = (state == 0xc8 || state == 0x40);
+
+ if (upsideDown && sd->nbRightUp > -4) {
+ if (sd->nbRightUp > 0)
+ sd->nbRightUp = 0;
+ if (sd->nbRightUp == -3) {
+ sd->mirrorMask = 1;
+ sd->waitSet = 1;
+ }
+ sd->nbRightUp--;
+ }
+ if (!upsideDown && sd->nbRightUp < 4) {
+ if (sd->nbRightUp < 0)
+ sd->nbRightUp = 0;
+ if (sd->nbRightUp == 3) {
+ sd->mirrorMask = 0;
+ sd->waitSet = 1;
+ }
+ sd->nbRightUp++;
+ }
+ }
+
+ if (sd->waitSet)
+ sd->dev_camera_settings(gspca_dev);
+}
+
+/*=================== USB driver structure initialisation ==================*/
+
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x05e3, 0x0503)},
+ {USB_DEVICE(0x05e3, 0xf191)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id,
+ &sd_desc_mi1320, sizeof(struct sd), THIS_MODULE);
+}
+
+static void sd_disconnect(struct usb_interface *intf)
+{
+ gspca_disconnect(intf);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = sd_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+/*====================== Init and Exit module functions ====================*/
+
+module_usb_driver(sd_driver);
+
+/*==========================================================================*/
+
+int gl860_RTx(struct gspca_dev *gspca_dev,
+ unsigned char pref, u32 req, u16 val, u16 index,
+ s32 len, void *pdata)
+{
+ struct usb_device *udev = gspca_dev->dev;
+ s32 r = 0;
+
+ if (pref == 0x40) { /* Send */
+ if (len > 0) {
+ memcpy(gspca_dev->usb_buf, pdata, len);
+ r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ req, pref, val, index,
+ gspca_dev->usb_buf,
+ len, 400 + 200 * (len > 1));
+ } else {
+ r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ req, pref, val, index, NULL, len, 400);
+ }
+ } else { /* Receive */
+ if (len > 0) {
+ r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ req, pref, val, index,
+ gspca_dev->usb_buf,
+ len, 400 + 200 * (len > 1));
+ memcpy(pdata, gspca_dev->usb_buf, len);
+ } else {
+ r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ req, pref, val, index, NULL, len, 400);
+ }
+ }
+
+ if (r < 0)
+ pr_err("ctrl transfer failed %4d [p%02x r%d v%04x i%04x len%d]\n",
+ r, pref, req, val, index, len);
+ else if (len > 1 && r < len)
+ PDEBUG(D_ERR, "short ctrl transfer %d/%d", r, len);
+
+ msleep(1);
+
+ return r;
+}
+
+int fetch_validx(struct gspca_dev *gspca_dev, struct validx *tbl, int len)
+{
+ int n;
+
+ for (n = 0; n < len; n++) {
+ if (tbl[n].idx != 0xffff)
+ ctrl_out(gspca_dev, 0x40, 1, tbl[n].val,
+ tbl[n].idx, 0, NULL);
+ else if (tbl[n].val == 0xffff)
+ break;
+ else
+ msleep(tbl[n].val);
+ }
+ return n;
+}
+
+int keep_on_fetching_validx(struct gspca_dev *gspca_dev, struct validx *tbl,
+ int len, int n)
+{
+ while (++n < len) {
+ if (tbl[n].idx != 0xffff)
+ ctrl_out(gspca_dev, 0x40, 1, tbl[n].val, tbl[n].idx,
+ 0, NULL);
+ else if (tbl[n].val == 0xffff)
+ break;
+ else
+ msleep(tbl[n].val);
+ }
+ return n;
+}
+
+void fetch_idxdata(struct gspca_dev *gspca_dev, struct idxdata *tbl, int len)
+{
+ int n;
+
+ for (n = 0; n < len; n++) {
+ if (memcmp(tbl[n].data, "\xff\xff\xff", 3) != 0)
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, tbl[n].idx,
+ 3, tbl[n].data);
+ else
+ msleep(tbl[n].idx);
+ }
+}
+
+static int gl860_guess_sensor(struct gspca_dev *gspca_dev,
+ u16 vendor_id, u16 product_id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 probe, nb26, nb96, nOV, ntry;
+
+ if (product_id == 0xf191)
+ sd->sensor = ID_MI1320;
+
+ if (sd->sensor == 0xff) {
+ ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &probe);
+ ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &probe);
+
+ ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x0000, 0, NULL);
+ msleep(3);
+ ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL);
+ msleep(3);
+ ctrl_out(gspca_dev, 0x40, 1, 0x0008, 0x00c0, 0, NULL);
+ msleep(3);
+ ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x00c1, 0, NULL);
+ msleep(3);
+ ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x00c2, 0, NULL);
+ msleep(3);
+ ctrl_out(gspca_dev, 0x40, 1, 0x0020, 0x0006, 0, NULL);
+ msleep(3);
+ ctrl_out(gspca_dev, 0x40, 1, 0x006a, 0x000d, 0, NULL);
+ msleep(56);
+
+ PDEBUG(D_PROBE, "probing for sensor MI2020 or OVXXXX");
+ nOV = 0;
+ for (ntry = 0; ntry < 4; ntry++) {
+ ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL);
+ msleep(3);
+ ctrl_out(gspca_dev, 0x40, 1, 0x0063, 0x0006, 0, NULL);
+ msleep(3);
+ ctrl_out(gspca_dev, 0x40, 1, 0x7a00, 0x8030, 0, NULL);
+ msleep(10);
+ ctrl_in(gspca_dev, 0xc0, 2, 0x7a00, 0x8030, 1, &probe);
+ PDEBUG(D_PROBE, "probe=0x%02x", probe);
+ if (probe == 0xff)
+ nOV++;
+ }
+
+ if (nOV) {
+ PDEBUG(D_PROBE, "0xff -> OVXXXX");
+ PDEBUG(D_PROBE, "probing for sensor OV2640 or OV9655");
+
+ nb26 = nb96 = 0;
+ for (ntry = 0; ntry < 4; ntry++) {
+ ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000,
+ 0, NULL);
+ msleep(3);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x800a,
+ 0, NULL);
+ msleep(10);
+
+ /* Wait for 26(OV2640) or 96(OV9655) */
+ ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x800a,
+ 1, &probe);
+
+ if (probe == 0x26 || probe == 0x40) {
+ PDEBUG(D_PROBE,
+ "probe=0x%02x -> OV2640",
+ probe);
+ sd->sensor = ID_OV2640;
+ nb26 += 4;
+ break;
+ }
+ if (probe == 0x96 || probe == 0x55) {
+ PDEBUG(D_PROBE,
+ "probe=0x%02x -> OV9655",
+ probe);
+ sd->sensor = ID_OV9655;
+ nb96 += 4;
+ break;
+ }
+ PDEBUG(D_PROBE, "probe=0x%02x", probe);
+ if (probe == 0x00)
+ nb26++;
+ if (probe == 0xff)
+ nb96++;
+ msleep(3);
+ }
+ if (nb26 < 4 && nb96 < 4)
+ return -1;
+ } else {
+ PDEBUG(D_PROBE, "Not any 0xff -> MI2020");
+ sd->sensor = ID_MI2020;
+ }
+ }
+
+ if (_MI1320_) {
+ PDEBUG(D_PROBE, "05e3:f191 sensor MI1320 (1.3M)");
+ } else if (_MI2020_) {
+ PDEBUG(D_PROBE, "05e3:0503 sensor MI2020 (2.0M)");
+ } else if (_OV9655_) {
+ PDEBUG(D_PROBE, "05e3:0503 sensor OV9655 (1.3M)");
+ } else if (_OV2640_) {
+ PDEBUG(D_PROBE, "05e3:0503 sensor OV2640 (2.0M)");
+ } else {
+ PDEBUG(D_PROBE, "***** Unknown sensor *****");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/drivers/media/video/gspca/gl860/gl860.h b/drivers/media/video/gspca/gl860/gl860.h
new file mode 100644
index 00000000..0330a029
--- /dev/null
+++ b/drivers/media/video/gspca/gl860/gl860.h
@@ -0,0 +1,105 @@
+/* GSPCA subdrivers for Genesys Logic webcams with the GL860 chip
+ * Subdriver declarations
+ *
+ * 2009/10/14 Olivier LORIN <o.lorin@laposte.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef GL860_DEV_H
+#define GL860_DEV_H
+
+#include "gspca.h"
+
+#define MODULE_NAME "gspca_gl860"
+#define DRIVER_VERSION "0.9d10"
+
+#define ctrl_in gl860_RTx
+#define ctrl_out gl860_RTx
+
+#define ID_MI1320 1
+#define ID_OV2640 2
+#define ID_OV9655 4
+#define ID_MI2020 8
+
+#define _MI1320_ (((struct sd *) gspca_dev)->sensor == ID_MI1320)
+#define _MI2020_ (((struct sd *) gspca_dev)->sensor == ID_MI2020)
+#define _OV2640_ (((struct sd *) gspca_dev)->sensor == ID_OV2640)
+#define _OV9655_ (((struct sd *) gspca_dev)->sensor == ID_OV9655)
+
+#define IMAGE_640 0
+#define IMAGE_800 1
+#define IMAGE_1280 2
+#define IMAGE_1600 3
+
+struct sd_gl860 {
+ u16 backlight;
+ u16 brightness;
+ u16 sharpness;
+ u16 contrast;
+ u16 gamma;
+ u16 hue;
+ u16 saturation;
+ u16 whitebal;
+ u8 mirror;
+ u8 flip;
+ u8 AC50Hz;
+};
+
+/* Specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ struct sd_gl860 vcur;
+ struct sd_gl860 vold;
+ struct sd_gl860 vmax;
+
+ int (*dev_configure_alt) (struct gspca_dev *);
+ int (*dev_init_at_startup)(struct gspca_dev *);
+ int (*dev_init_pre_alt) (struct gspca_dev *);
+ void (*dev_post_unset_alt) (struct gspca_dev *);
+ int (*dev_camera_settings)(struct gspca_dev *);
+
+ u8 swapRB;
+ u8 mirrorMask;
+ u8 sensor;
+ s32 nbIm;
+ s32 nbRightUp;
+ u8 waitSet;
+};
+
+struct validx {
+ u16 val;
+ u16 idx;
+};
+
+struct idxdata {
+ u8 idx;
+ u8 data[3];
+};
+
+int fetch_validx(struct gspca_dev *gspca_dev, struct validx *tbl, int len);
+int keep_on_fetching_validx(struct gspca_dev *gspca_dev, struct validx *tbl,
+ int len, int n);
+void fetch_idxdata(struct gspca_dev *gspca_dev, struct idxdata *tbl, int len);
+
+int gl860_RTx(struct gspca_dev *gspca_dev,
+ unsigned char pref, u32 req, u16 val, u16 index,
+ s32 len, void *pdata);
+
+void mi1320_init_settings(struct gspca_dev *);
+void ov2640_init_settings(struct gspca_dev *);
+void ov9655_init_settings(struct gspca_dev *);
+void mi2020_init_settings(struct gspca_dev *);
+
+#endif
diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c
new file mode 100644
index 00000000..4dc88527
--- /dev/null
+++ b/drivers/media/video/gspca/gspca.c
@@ -0,0 +1,2613 @@
+/*
+ * Main USB camera driver
+ *
+ * Copyright (C) 2008-2011 Jean-François Moine <http://moinejf.free.fr>
+ *
+ * Camera button input handling by Márton Németh
+ * Copyright (C) 2009-2010 Márton Németh <nm127@freemail.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define GSPCA_VERSION "2.14.0"
+
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/pagemap.h>
+#include <linux/io.h>
+#include <asm/page.h>
+#include <linux/uaccess.h>
+#include <linux/ktime.h>
+#include <media/v4l2-ioctl.h>
+
+#include "gspca.h"
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+#include <linux/input.h>
+#include <linux/usb/input.h>
+#endif
+
+/* global values */
+#define DEF_NURBS 3 /* default number of URBs */
+#if DEF_NURBS > MAX_NURBS
+#error "DEF_NURBS too big"
+#endif
+
+MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>");
+MODULE_DESCRIPTION("GSPCA USB Camera Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(GSPCA_VERSION);
+
+#ifdef GSPCA_DEBUG
+int gspca_debug = D_ERR | D_PROBE;
+EXPORT_SYMBOL(gspca_debug);
+
+static void PDEBUG_MODE(char *txt, __u32 pixfmt, int w, int h)
+{
+ if ((pixfmt >> 24) >= '0' && (pixfmt >> 24) <= 'z') {
+ PDEBUG(D_CONF|D_STREAM, "%s %c%c%c%c %dx%d",
+ txt,
+ pixfmt & 0xff,
+ (pixfmt >> 8) & 0xff,
+ (pixfmt >> 16) & 0xff,
+ pixfmt >> 24,
+ w, h);
+ } else {
+ PDEBUG(D_CONF|D_STREAM, "%s 0x%08x %dx%d",
+ txt,
+ pixfmt,
+ w, h);
+ }
+}
+#else
+#define PDEBUG_MODE(txt, pixfmt, w, h)
+#endif
+
+/* specific memory types - !! should be different from V4L2_MEMORY_xxx */
+#define GSPCA_MEMORY_NO 0 /* V4L2_MEMORY_xxx starts from 1 */
+#define GSPCA_MEMORY_READ 7
+
+#define BUF_ALL_FLAGS (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE)
+
+/*
+ * VMA operations.
+ */
+static void gspca_vm_open(struct vm_area_struct *vma)
+{
+ struct gspca_frame *frame = vma->vm_private_data;
+
+ frame->vma_use_count++;
+ frame->v4l2_buf.flags |= V4L2_BUF_FLAG_MAPPED;
+}
+
+static void gspca_vm_close(struct vm_area_struct *vma)
+{
+ struct gspca_frame *frame = vma->vm_private_data;
+
+ if (--frame->vma_use_count <= 0)
+ frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_MAPPED;
+}
+
+static const struct vm_operations_struct gspca_vm_ops = {
+ .open = gspca_vm_open,
+ .close = gspca_vm_close,
+};
+
+/*
+ * Input and interrupt endpoint handling functions
+ */
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+static void int_irq(struct urb *urb)
+{
+ struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
+ int ret;
+
+ ret = urb->status;
+ switch (ret) {
+ case 0:
+ if (gspca_dev->sd_desc->int_pkt_scan(gspca_dev,
+ urb->transfer_buffer, urb->actual_length) < 0) {
+ PDEBUG(D_ERR, "Unknown packet received");
+ }
+ break;
+
+ case -ENOENT:
+ case -ECONNRESET:
+ case -ENODEV:
+ case -ESHUTDOWN:
+ /* Stop is requested either by software or hardware is gone,
+ * keep the ret value non-zero and don't resubmit later.
+ */
+ break;
+
+ default:
+ PDEBUG(D_ERR, "URB error %i, resubmitting", urb->status);
+ urb->status = 0;
+ ret = 0;
+ }
+
+ if (ret == 0) {
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret < 0)
+ pr_err("Resubmit URB failed with error %i\n", ret);
+ }
+}
+
+static int gspca_input_connect(struct gspca_dev *dev)
+{
+ struct input_dev *input_dev;
+ int err = 0;
+
+ dev->input_dev = NULL;
+ if (dev->sd_desc->int_pkt_scan || dev->sd_desc->other_input) {
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ return -ENOMEM;
+
+ usb_make_path(dev->dev, dev->phys, sizeof(dev->phys));
+ strlcat(dev->phys, "/input0", sizeof(dev->phys));
+
+ input_dev->name = dev->sd_desc->name;
+ input_dev->phys = dev->phys;
+
+ usb_to_input_id(dev->dev, &input_dev->id);
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY);
+ input_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA);
+ input_dev->dev.parent = &dev->dev->dev;
+
+ err = input_register_device(input_dev);
+ if (err) {
+ pr_err("Input device registration failed with error %i\n",
+ err);
+ input_dev->dev.parent = NULL;
+ input_free_device(input_dev);
+ } else {
+ dev->input_dev = input_dev;
+ }
+ }
+
+ return err;
+}
+
+static int alloc_and_submit_int_urb(struct gspca_dev *gspca_dev,
+ struct usb_endpoint_descriptor *ep)
+{
+ unsigned int buffer_len;
+ int interval;
+ struct urb *urb;
+ struct usb_device *dev;
+ void *buffer = NULL;
+ int ret = -EINVAL;
+
+ buffer_len = le16_to_cpu(ep->wMaxPacketSize);
+ interval = ep->bInterval;
+ PDEBUG(D_CONF, "found int in endpoint: 0x%x, "
+ "buffer_len=%u, interval=%u",
+ ep->bEndpointAddress, buffer_len, interval);
+
+ dev = gspca_dev->dev;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ buffer = usb_alloc_coherent(dev, buffer_len,
+ GFP_KERNEL, &urb->transfer_dma);
+ if (!buffer) {
+ ret = -ENOMEM;
+ goto error_buffer;
+ }
+ usb_fill_int_urb(urb, dev,
+ usb_rcvintpipe(dev, ep->bEndpointAddress),
+ buffer, buffer_len,
+ int_irq, (void *)gspca_dev, interval);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ ret = usb_submit_urb(urb, GFP_KERNEL);
+ if (ret < 0) {
+ PDEBUG(D_ERR, "submit int URB failed with error %i", ret);
+ goto error_submit;
+ }
+ gspca_dev->int_urb = urb;
+ return ret;
+
+error_submit:
+ usb_free_coherent(dev,
+ urb->transfer_buffer_length,
+ urb->transfer_buffer,
+ urb->transfer_dma);
+error_buffer:
+ usb_free_urb(urb);
+error:
+ return ret;
+}
+
+static void gspca_input_create_urb(struct gspca_dev *gspca_dev)
+{
+ struct usb_interface *intf;
+ struct usb_host_interface *intf_desc;
+ struct usb_endpoint_descriptor *ep;
+ int i;
+
+ if (gspca_dev->sd_desc->int_pkt_scan) {
+ intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
+ intf_desc = intf->cur_altsetting;
+ for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) {
+ ep = &intf_desc->endpoint[i].desc;
+ if (usb_endpoint_dir_in(ep) &&
+ usb_endpoint_xfer_int(ep)) {
+
+ alloc_and_submit_int_urb(gspca_dev, ep);
+ break;
+ }
+ }
+ }
+}
+
+static void gspca_input_destroy_urb(struct gspca_dev *gspca_dev)
+{
+ struct urb *urb;
+
+ urb = gspca_dev->int_urb;
+ if (urb) {
+ gspca_dev->int_urb = NULL;
+ usb_kill_urb(urb);
+ usb_free_coherent(gspca_dev->dev,
+ urb->transfer_buffer_length,
+ urb->transfer_buffer,
+ urb->transfer_dma);
+ usb_free_urb(urb);
+ }
+}
+#else
+static inline void gspca_input_destroy_urb(struct gspca_dev *gspca_dev)
+{
+}
+
+static inline void gspca_input_create_urb(struct gspca_dev *gspca_dev)
+{
+}
+
+static inline int gspca_input_connect(struct gspca_dev *dev)
+{
+ return 0;
+}
+#endif
+
+/*
+ * fill a video frame from an URB and resubmit
+ */
+static void fill_frame(struct gspca_dev *gspca_dev,
+ struct urb *urb)
+{
+ u8 *data; /* address of data in the iso message */
+ int i, len, st;
+ cam_pkt_op pkt_scan;
+
+ if (urb->status != 0) {
+ if (urb->status == -ESHUTDOWN)
+ return; /* disconnection */
+#ifdef CONFIG_PM
+ if (gspca_dev->frozen)
+ return;
+#endif
+ PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
+ urb->status = 0;
+ goto resubmit;
+ }
+ pkt_scan = gspca_dev->sd_desc->pkt_scan;
+ for (i = 0; i < urb->number_of_packets; i++) {
+ len = urb->iso_frame_desc[i].actual_length;
+
+ /* check the packet status and length */
+ st = urb->iso_frame_desc[i].status;
+ if (st) {
+ pr_err("ISOC data error: [%d] len=%d, status=%d\n",
+ i, len, st);
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ continue;
+ }
+ if (len == 0) {
+ if (gspca_dev->empty_packet == 0)
+ gspca_dev->empty_packet = 1;
+ continue;
+ }
+
+ /* let the packet be analyzed by the subdriver */
+ PDEBUG(D_PACK, "packet [%d] o:%d l:%d",
+ i, urb->iso_frame_desc[i].offset, len);
+ data = (u8 *) urb->transfer_buffer
+ + urb->iso_frame_desc[i].offset;
+ pkt_scan(gspca_dev, data, len);
+ }
+
+resubmit:
+ /* resubmit the URB */
+ st = usb_submit_urb(urb, GFP_ATOMIC);
+ if (st < 0)
+ pr_err("usb_submit_urb() ret %d\n", st);
+}
+
+/*
+ * ISOC message interrupt from the USB device
+ *
+ * Analyse each packet and call the subdriver for copy to the frame buffer.
+ */
+static void isoc_irq(struct urb *urb)
+{
+ struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
+
+ PDEBUG(D_PACK, "isoc irq");
+ if (!gspca_dev->streaming)
+ return;
+ fill_frame(gspca_dev, urb);
+}
+
+/*
+ * bulk message interrupt from the USB device
+ */
+static void bulk_irq(struct urb *urb)
+{
+ struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
+ int st;
+
+ PDEBUG(D_PACK, "bulk irq");
+ if (!gspca_dev->streaming)
+ return;
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ESHUTDOWN:
+ return; /* disconnection */
+ default:
+#ifdef CONFIG_PM
+ if (gspca_dev->frozen)
+ return;
+#endif
+ PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
+ urb->status = 0;
+ goto resubmit;
+ }
+
+ PDEBUG(D_PACK, "packet l:%d", urb->actual_length);
+ gspca_dev->sd_desc->pkt_scan(gspca_dev,
+ urb->transfer_buffer,
+ urb->actual_length);
+
+resubmit:
+ /* resubmit the URB */
+ if (gspca_dev->cam.bulk_nurbs != 0) {
+ st = usb_submit_urb(urb, GFP_ATOMIC);
+ if (st < 0)
+ pr_err("usb_submit_urb() ret %d\n", st);
+ }
+}
+
+/*
+ * add data to the current frame
+ *
+ * This function is called by the subdrivers at interrupt level.
+ *
+ * To build a frame, these ones must add
+ * - one FIRST_PACKET
+ * - 0 or many INTER_PACKETs
+ * - one LAST_PACKET
+ * DISCARD_PACKET invalidates the whole frame.
+ */
+void gspca_frame_add(struct gspca_dev *gspca_dev,
+ enum gspca_packet_type packet_type,
+ const u8 *data,
+ int len)
+{
+ struct gspca_frame *frame;
+ int i, j;
+
+ PDEBUG(D_PACK, "add t:%d l:%d", packet_type, len);
+
+ if (packet_type == FIRST_PACKET) {
+ i = atomic_read(&gspca_dev->fr_i);
+
+ /* if there are no queued buffer, discard the whole frame */
+ if (i == atomic_read(&gspca_dev->fr_q)) {
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ gspca_dev->sequence++;
+ return;
+ }
+ j = gspca_dev->fr_queue[i];
+ frame = &gspca_dev->frame[j];
+ frame->v4l2_buf.timestamp = ktime_to_timeval(ktime_get());
+ frame->v4l2_buf.sequence = gspca_dev->sequence++;
+ gspca_dev->image = frame->data;
+ gspca_dev->image_len = 0;
+ } else {
+ switch (gspca_dev->last_packet_type) {
+ case DISCARD_PACKET:
+ if (packet_type == LAST_PACKET) {
+ gspca_dev->last_packet_type = packet_type;
+ gspca_dev->image = NULL;
+ gspca_dev->image_len = 0;
+ }
+ return;
+ case LAST_PACKET:
+ return;
+ }
+ }
+
+ /* append the packet to the frame buffer */
+ if (len > 0) {
+ if (gspca_dev->image_len + len > gspca_dev->frsz) {
+ PDEBUG(D_ERR|D_PACK, "frame overflow %d > %d",
+ gspca_dev->image_len + len,
+ gspca_dev->frsz);
+ packet_type = DISCARD_PACKET;
+ } else {
+/* !! image is NULL only when last pkt is LAST or DISCARD
+ if (gspca_dev->image == NULL) {
+ pr_err("gspca_frame_add() image == NULL\n");
+ return;
+ }
+ */
+ memcpy(gspca_dev->image + gspca_dev->image_len,
+ data, len);
+ gspca_dev->image_len += len;
+ }
+ }
+ gspca_dev->last_packet_type = packet_type;
+
+ /* if last packet, invalidate packet concatenation until
+ * next first packet, wake up the application and advance
+ * in the queue */
+ if (packet_type == LAST_PACKET) {
+ i = atomic_read(&gspca_dev->fr_i);
+ j = gspca_dev->fr_queue[i];
+ frame = &gspca_dev->frame[j];
+ frame->v4l2_buf.bytesused = gspca_dev->image_len;
+ frame->v4l2_buf.flags = (frame->v4l2_buf.flags
+ | V4L2_BUF_FLAG_DONE)
+ & ~V4L2_BUF_FLAG_QUEUED;
+ i = (i + 1) % GSPCA_MAX_FRAMES;
+ atomic_set(&gspca_dev->fr_i, i);
+ wake_up_interruptible(&gspca_dev->wq); /* event = new frame */
+ PDEBUG(D_FRAM, "frame complete len:%d",
+ frame->v4l2_buf.bytesused);
+ gspca_dev->image = NULL;
+ gspca_dev->image_len = 0;
+ }
+}
+EXPORT_SYMBOL(gspca_frame_add);
+
+static int frame_alloc(struct gspca_dev *gspca_dev, struct file *file,
+ enum v4l2_memory memory, unsigned int count)
+{
+ struct gspca_frame *frame;
+ unsigned int frsz;
+ int i;
+
+ i = gspca_dev->curr_mode;
+ frsz = gspca_dev->cam.cam_mode[i].sizeimage;
+ PDEBUG(D_STREAM, "frame alloc frsz: %d", frsz);
+ frsz = PAGE_ALIGN(frsz);
+ if (count >= GSPCA_MAX_FRAMES)
+ count = GSPCA_MAX_FRAMES - 1;
+ gspca_dev->frbuf = vmalloc_32(frsz * count);
+ if (!gspca_dev->frbuf) {
+ pr_err("frame alloc failed\n");
+ return -ENOMEM;
+ }
+ gspca_dev->capt_file = file;
+ gspca_dev->memory = memory;
+ gspca_dev->frsz = frsz;
+ gspca_dev->nframes = count;
+ for (i = 0; i < count; i++) {
+ frame = &gspca_dev->frame[i];
+ frame->v4l2_buf.index = i;
+ frame->v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ frame->v4l2_buf.flags = 0;
+ frame->v4l2_buf.field = V4L2_FIELD_NONE;
+ frame->v4l2_buf.length = frsz;
+ frame->v4l2_buf.memory = memory;
+ frame->v4l2_buf.sequence = 0;
+ frame->data = gspca_dev->frbuf + i * frsz;
+ frame->v4l2_buf.m.offset = i * frsz;
+ }
+ atomic_set(&gspca_dev->fr_q, 0);
+ atomic_set(&gspca_dev->fr_i, 0);
+ gspca_dev->fr_o = 0;
+ return 0;
+}
+
+static void frame_free(struct gspca_dev *gspca_dev)
+{
+ int i;
+
+ PDEBUG(D_STREAM, "frame free");
+ if (gspca_dev->frbuf != NULL) {
+ vfree(gspca_dev->frbuf);
+ gspca_dev->frbuf = NULL;
+ for (i = 0; i < gspca_dev->nframes; i++)
+ gspca_dev->frame[i].data = NULL;
+ }
+ gspca_dev->nframes = 0;
+ gspca_dev->frsz = 0;
+ gspca_dev->capt_file = NULL;
+ gspca_dev->memory = GSPCA_MEMORY_NO;
+}
+
+static void destroy_urbs(struct gspca_dev *gspca_dev)
+{
+ struct urb *urb;
+ unsigned int i;
+
+ PDEBUG(D_STREAM, "kill transfer");
+ for (i = 0; i < MAX_NURBS; i++) {
+ urb = gspca_dev->urb[i];
+ if (urb == NULL)
+ break;
+
+ gspca_dev->urb[i] = NULL;
+ usb_kill_urb(urb);
+ if (urb->transfer_buffer != NULL)
+ usb_free_coherent(gspca_dev->dev,
+ urb->transfer_buffer_length,
+ urb->transfer_buffer,
+ urb->transfer_dma);
+ usb_free_urb(urb);
+ }
+}
+
+static int gspca_set_alt0(struct gspca_dev *gspca_dev)
+{
+ int ret;
+
+ if (gspca_dev->alt == 0)
+ return 0;
+ ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0);
+ if (ret < 0)
+ pr_err("set alt 0 err %d\n", ret);
+ return ret;
+}
+
+/* Note: both the queue and the usb locks should be held when calling this */
+static void gspca_stream_off(struct gspca_dev *gspca_dev)
+{
+ gspca_dev->streaming = 0;
+ if (gspca_dev->present) {
+ if (gspca_dev->sd_desc->stopN)
+ gspca_dev->sd_desc->stopN(gspca_dev);
+ destroy_urbs(gspca_dev);
+ gspca_input_destroy_urb(gspca_dev);
+ gspca_set_alt0(gspca_dev);
+ gspca_input_create_urb(gspca_dev);
+ }
+
+ /* always call stop0 to free the subdriver's resources */
+ if (gspca_dev->sd_desc->stop0)
+ gspca_dev->sd_desc->stop0(gspca_dev);
+ PDEBUG(D_STREAM, "stream off OK");
+}
+
+/*
+ * look for an input transfer endpoint in an alternate setting
+ */
+static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt,
+ int xfer)
+{
+ struct usb_host_endpoint *ep;
+ int i, attr;
+
+ for (i = 0; i < alt->desc.bNumEndpoints; i++) {
+ ep = &alt->endpoint[i];
+ attr = ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+ if (attr == xfer
+ && ep->desc.wMaxPacketSize != 0
+ && usb_endpoint_dir_in(&ep->desc))
+ return ep;
+ }
+ return NULL;
+}
+
+/* compute the minimum bandwidth for the current transfer */
+static u32 which_bandwidth(struct gspca_dev *gspca_dev)
+{
+ u32 bandwidth;
+ int i;
+
+ /* get the (max) image size */
+ i = gspca_dev->curr_mode;
+ bandwidth = gspca_dev->cam.cam_mode[i].sizeimage;
+
+ /* if the image is compressed, estimate its mean size */
+ if (!gspca_dev->cam.needs_full_bandwidth &&
+ bandwidth < gspca_dev->cam.cam_mode[i].width *
+ gspca_dev->cam.cam_mode[i].height)
+ bandwidth = bandwidth * 3 / 8; /* 0.375 */
+
+ /* estimate the frame rate */
+ if (gspca_dev->sd_desc->get_streamparm) {
+ struct v4l2_streamparm parm;
+
+ gspca_dev->sd_desc->get_streamparm(gspca_dev, &parm);
+ bandwidth *= parm.parm.capture.timeperframe.denominator;
+ bandwidth /= parm.parm.capture.timeperframe.numerator;
+ } else {
+
+ /* don't hope more than 15 fps with USB 1.1 and
+ * image resolution >= 640x480 */
+ if (gspca_dev->width >= 640
+ && gspca_dev->dev->speed == USB_SPEED_FULL)
+ bandwidth *= 15; /* 15 fps */
+ else
+ bandwidth *= 30; /* 30 fps */
+ }
+
+ PDEBUG(D_STREAM, "min bandwidth: %d", bandwidth);
+ return bandwidth;
+}
+
+/* endpoint table */
+#define MAX_ALT 16
+struct ep_tb_s {
+ u32 alt;
+ u32 bandwidth;
+};
+
+/*
+ * build the table of the endpoints
+ * and compute the minimum bandwidth for the image transfer
+ */
+static int build_isoc_ep_tb(struct gspca_dev *gspca_dev,
+ struct usb_interface *intf,
+ struct ep_tb_s *ep_tb)
+{
+ struct usb_host_endpoint *ep;
+ int i, j, nbalt, psize, found;
+ u32 bandwidth, last_bw;
+
+ nbalt = intf->num_altsetting;
+ if (nbalt > MAX_ALT)
+ nbalt = MAX_ALT; /* fixme: should warn */
+
+ /* build the endpoint table */
+ i = 0;
+ last_bw = 0;
+ for (;;) {
+ ep_tb->bandwidth = 2000 * 2000 * 120;
+ found = 0;
+ for (j = 0; j < nbalt; j++) {
+ ep = alt_xfer(&intf->altsetting[j],
+ USB_ENDPOINT_XFER_ISOC);
+ if (ep == NULL)
+ continue;
+ if (ep->desc.bInterval == 0) {
+ pr_err("alt %d iso endp with 0 interval\n", j);
+ continue;
+ }
+ psize = le16_to_cpu(ep->desc.wMaxPacketSize);
+ psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
+ bandwidth = psize * 1000;
+ if (gspca_dev->dev->speed == USB_SPEED_HIGH
+ || gspca_dev->dev->speed == USB_SPEED_SUPER)
+ bandwidth *= 8;
+ bandwidth /= 1 << (ep->desc.bInterval - 1);
+ if (bandwidth <= last_bw)
+ continue;
+ if (bandwidth < ep_tb->bandwidth) {
+ ep_tb->bandwidth = bandwidth;
+ ep_tb->alt = j;
+ found = 1;
+ }
+ }
+ if (!found)
+ break;
+ PDEBUG(D_STREAM, "alt %d bandwidth %d",
+ ep_tb->alt, ep_tb->bandwidth);
+ last_bw = ep_tb->bandwidth;
+ i++;
+ ep_tb++;
+ }
+
+ /*
+ * If the camera:
+ * has a usb audio class interface (a built in usb mic); and
+ * is a usb 1 full speed device; and
+ * uses the max full speed iso bandwidth; and
+ * and has more than 1 alt setting
+ * then skip the highest alt setting to spare bandwidth for the mic
+ */
+ if (gspca_dev->audio &&
+ gspca_dev->dev->speed == USB_SPEED_FULL &&
+ last_bw >= 1000000 &&
+ i > 1) {
+ PDEBUG(D_STREAM, "dev has usb audio, skipping highest alt");
+ i--;
+ ep_tb--;
+ }
+
+ /* get the requested bandwidth and start at the highest atlsetting */
+ bandwidth = which_bandwidth(gspca_dev);
+ ep_tb--;
+ while (i > 1) {
+ ep_tb--;
+ if (ep_tb->bandwidth < bandwidth)
+ break;
+ i--;
+ }
+ return i;
+}
+
+/*
+ * create the URBs for image transfer
+ */
+static int create_urbs(struct gspca_dev *gspca_dev,
+ struct usb_host_endpoint *ep)
+{
+ struct urb *urb;
+ int n, nurbs, i, psize, npkt, bsize;
+
+ /* calculate the packet size and the number of packets */
+ psize = le16_to_cpu(ep->desc.wMaxPacketSize);
+
+ if (!gspca_dev->cam.bulk) { /* isoc */
+
+ /* See paragraph 5.9 / table 5-11 of the usb 2.0 spec. */
+ if (gspca_dev->pkt_size == 0)
+ psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
+ else
+ psize = gspca_dev->pkt_size;
+ npkt = gspca_dev->cam.npkt;
+ if (npkt == 0)
+ npkt = 32; /* default value */
+ bsize = psize * npkt;
+ PDEBUG(D_STREAM,
+ "isoc %d pkts size %d = bsize:%d",
+ npkt, psize, bsize);
+ nurbs = DEF_NURBS;
+ } else { /* bulk */
+ npkt = 0;
+ bsize = gspca_dev->cam.bulk_size;
+ if (bsize == 0)
+ bsize = psize;
+ PDEBUG(D_STREAM, "bulk bsize:%d", bsize);
+ if (gspca_dev->cam.bulk_nurbs != 0)
+ nurbs = gspca_dev->cam.bulk_nurbs;
+ else
+ nurbs = 1;
+ }
+
+ for (n = 0; n < nurbs; n++) {
+ urb = usb_alloc_urb(npkt, GFP_KERNEL);
+ if (!urb) {
+ pr_err("usb_alloc_urb failed\n");
+ return -ENOMEM;
+ }
+ gspca_dev->urb[n] = urb;
+ urb->transfer_buffer = usb_alloc_coherent(gspca_dev->dev,
+ bsize,
+ GFP_KERNEL,
+ &urb->transfer_dma);
+
+ if (urb->transfer_buffer == NULL) {
+ pr_err("usb_alloc_coherent failed\n");
+ return -ENOMEM;
+ }
+ urb->dev = gspca_dev->dev;
+ urb->context = gspca_dev;
+ urb->transfer_buffer_length = bsize;
+ if (npkt != 0) { /* ISOC */
+ urb->pipe = usb_rcvisocpipe(gspca_dev->dev,
+ ep->desc.bEndpointAddress);
+ urb->transfer_flags = URB_ISO_ASAP
+ | URB_NO_TRANSFER_DMA_MAP;
+ urb->interval = 1 << (ep->desc.bInterval - 1);
+ urb->complete = isoc_irq;
+ urb->number_of_packets = npkt;
+ for (i = 0; i < npkt; i++) {
+ urb->iso_frame_desc[i].length = psize;
+ urb->iso_frame_desc[i].offset = psize * i;
+ }
+ } else { /* bulk */
+ urb->pipe = usb_rcvbulkpipe(gspca_dev->dev,
+ ep->desc.bEndpointAddress);
+ urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+ urb->complete = bulk_irq;
+ }
+ }
+ return 0;
+}
+
+/*
+ * start the USB transfer
+ */
+static int gspca_init_transfer(struct gspca_dev *gspca_dev)
+{
+ struct usb_interface *intf;
+ struct usb_host_endpoint *ep;
+ struct urb *urb;
+ struct ep_tb_s ep_tb[MAX_ALT];
+ int n, ret, xfer, alt, alt_idx;
+
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+
+ if (!gspca_dev->present) {
+ ret = -ENODEV;
+ goto unlock;
+ }
+
+ /* reset the streaming variables */
+ gspca_dev->image = NULL;
+ gspca_dev->image_len = 0;
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ gspca_dev->sequence = 0;
+
+ gspca_dev->usb_err = 0;
+
+ /* do the specific subdriver stuff before endpoint selection */
+ intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
+ gspca_dev->alt = gspca_dev->cam.bulk ? intf->num_altsetting : 0;
+ if (gspca_dev->sd_desc->isoc_init) {
+ ret = gspca_dev->sd_desc->isoc_init(gspca_dev);
+ if (ret < 0)
+ goto unlock;
+ }
+ xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK
+ : USB_ENDPOINT_XFER_ISOC;
+
+ /* if bulk or the subdriver forced an altsetting, get the endpoint */
+ if (gspca_dev->alt != 0) {
+ gspca_dev->alt--; /* (previous version compatibility) */
+ ep = alt_xfer(&intf->altsetting[gspca_dev->alt], xfer);
+ if (ep == NULL) {
+ pr_err("bad altsetting %d\n", gspca_dev->alt);
+ ret = -EIO;
+ goto out;
+ }
+ ep_tb[0].alt = gspca_dev->alt;
+ alt_idx = 1;
+ } else {
+
+ /* else, compute the minimum bandwidth
+ * and build the endpoint table */
+ alt_idx = build_isoc_ep_tb(gspca_dev, intf, ep_tb);
+ if (alt_idx <= 0) {
+ pr_err("no transfer endpoint found\n");
+ ret = -EIO;
+ goto unlock;
+ }
+ }
+
+ /* set the highest alternate setting and
+ * loop until urb submit succeeds */
+ gspca_input_destroy_urb(gspca_dev);
+
+ gspca_dev->alt = ep_tb[--alt_idx].alt;
+ alt = -1;
+ for (;;) {
+ if (alt != gspca_dev->alt) {
+ alt = gspca_dev->alt;
+ if (intf->num_altsetting > 1) {
+ ret = usb_set_interface(gspca_dev->dev,
+ gspca_dev->iface,
+ alt);
+ if (ret < 0) {
+ if (ret == -ENOSPC)
+ goto retry; /*fixme: ugly*/
+ pr_err("set alt %d err %d\n", alt, ret);
+ goto out;
+ }
+ }
+ }
+ if (!gspca_dev->cam.no_urb_create) {
+ PDEBUG(D_STREAM, "init transfer alt %d", alt);
+ ret = create_urbs(gspca_dev,
+ alt_xfer(&intf->altsetting[alt], xfer));
+ if (ret < 0) {
+ destroy_urbs(gspca_dev);
+ goto out;
+ }
+ }
+
+ /* clear the bulk endpoint */
+ if (gspca_dev->cam.bulk)
+ usb_clear_halt(gspca_dev->dev,
+ gspca_dev->urb[0]->pipe);
+
+ /* start the cam */
+ ret = gspca_dev->sd_desc->start(gspca_dev);
+ if (ret < 0) {
+ destroy_urbs(gspca_dev);
+ goto out;
+ }
+ gspca_dev->streaming = 1;
+
+ /* some bulk transfers are started by the subdriver */
+ if (gspca_dev->cam.bulk && gspca_dev->cam.bulk_nurbs == 0)
+ break;
+
+ /* submit the URBs */
+ for (n = 0; n < MAX_NURBS; n++) {
+ urb = gspca_dev->urb[n];
+ if (urb == NULL)
+ break;
+ ret = usb_submit_urb(urb, GFP_KERNEL);
+ if (ret < 0)
+ break;
+ }
+ if (ret >= 0)
+ break; /* transfer is started */
+
+ /* something when wrong
+ * stop the webcam and free the transfer resources */
+ gspca_stream_off(gspca_dev);
+ if (ret != -ENOSPC) {
+ pr_err("usb_submit_urb alt %d err %d\n",
+ gspca_dev->alt, ret);
+ goto out;
+ }
+
+ /* the bandwidth is not wide enough
+ * negotiate or try a lower alternate setting */
+retry:
+ PDEBUG(D_ERR|D_STREAM,
+ "alt %d - bandwidth not wide enough - trying again",
+ alt);
+ msleep(20); /* wait for kill complete */
+ if (gspca_dev->sd_desc->isoc_nego) {
+ ret = gspca_dev->sd_desc->isoc_nego(gspca_dev);
+ if (ret < 0)
+ goto out;
+ } else {
+ if (alt_idx <= 0) {
+ pr_err("no transfer endpoint found\n");
+ ret = -EIO;
+ goto out;
+ }
+ gspca_dev->alt = ep_tb[--alt_idx].alt;
+ }
+ }
+out:
+ gspca_input_create_urb(gspca_dev);
+unlock:
+ mutex_unlock(&gspca_dev->usb_lock);
+ return ret;
+}
+
+static void gspca_set_default_mode(struct gspca_dev *gspca_dev)
+{
+ struct gspca_ctrl *ctrl;
+ int i;
+
+ i = gspca_dev->cam.nmodes - 1; /* take the highest mode */
+ gspca_dev->curr_mode = i;
+ gspca_dev->width = gspca_dev->cam.cam_mode[i].width;
+ gspca_dev->height = gspca_dev->cam.cam_mode[i].height;
+ gspca_dev->pixfmt = gspca_dev->cam.cam_mode[i].pixelformat;
+
+ /* set the current control values to their default values
+ * which may have changed in sd_init() */
+ ctrl = gspca_dev->cam.ctrls;
+ if (ctrl != NULL) {
+ for (i = 0;
+ i < gspca_dev->sd_desc->nctrls;
+ i++, ctrl++)
+ ctrl->val = ctrl->def;
+ }
+}
+
+static int wxh_to_mode(struct gspca_dev *gspca_dev,
+ int width, int height)
+{
+ int i;
+
+ for (i = gspca_dev->cam.nmodes; --i > 0; ) {
+ if (width >= gspca_dev->cam.cam_mode[i].width
+ && height >= gspca_dev->cam.cam_mode[i].height)
+ break;
+ }
+ return i;
+}
+
+/*
+ * search a mode with the right pixel format
+ */
+static int gspca_get_mode(struct gspca_dev *gspca_dev,
+ int mode,
+ int pixfmt)
+{
+ int modeU, modeD;
+
+ modeU = modeD = mode;
+ while ((modeU < gspca_dev->cam.nmodes) || modeD >= 0) {
+ if (--modeD >= 0) {
+ if (gspca_dev->cam.cam_mode[modeD].pixelformat
+ == pixfmt)
+ return modeD;
+ }
+ if (++modeU < gspca_dev->cam.nmodes) {
+ if (gspca_dev->cam.cam_mode[modeU].pixelformat
+ == pixfmt)
+ return modeU;
+ }
+ }
+ return -EINVAL;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vidioc_g_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ int ret;
+ struct gspca_dev *gspca_dev = priv;
+
+ if (!gspca_dev->sd_desc->get_chip_ident)
+ return -EINVAL;
+
+ if (!gspca_dev->sd_desc->get_register)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+ gspca_dev->usb_err = 0;
+ if (gspca_dev->present)
+ ret = gspca_dev->sd_desc->get_register(gspca_dev, reg);
+ else
+ ret = -ENODEV;
+ mutex_unlock(&gspca_dev->usb_lock);
+
+ return ret;
+}
+
+static int vidioc_s_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ int ret;
+ struct gspca_dev *gspca_dev = priv;
+
+ if (!gspca_dev->sd_desc->get_chip_ident)
+ return -EINVAL;
+
+ if (!gspca_dev->sd_desc->set_register)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+ gspca_dev->usb_err = 0;
+ if (gspca_dev->present)
+ ret = gspca_dev->sd_desc->set_register(gspca_dev, reg);
+ else
+ ret = -ENODEV;
+ mutex_unlock(&gspca_dev->usb_lock);
+
+ return ret;
+}
+#endif
+
+static int vidioc_g_chip_ident(struct file *file, void *priv,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ int ret;
+ struct gspca_dev *gspca_dev = priv;
+
+ if (!gspca_dev->sd_desc->get_chip_ident)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+ gspca_dev->usb_err = 0;
+ if (gspca_dev->present)
+ ret = gspca_dev->sd_desc->get_chip_ident(gspca_dev, chip);
+ else
+ ret = -ENODEV;
+ mutex_unlock(&gspca_dev->usb_lock);
+
+ return ret;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *fmtdesc)
+{
+ struct gspca_dev *gspca_dev = priv;
+ int i, j, index;
+ __u32 fmt_tb[8];
+
+ /* give an index to each format */
+ index = 0;
+ j = 0;
+ for (i = gspca_dev->cam.nmodes; --i >= 0; ) {
+ fmt_tb[index] = gspca_dev->cam.cam_mode[i].pixelformat;
+ j = 0;
+ for (;;) {
+ if (fmt_tb[j] == fmt_tb[index])
+ break;
+ j++;
+ }
+ if (j == index) {
+ if (fmtdesc->index == index)
+ break; /* new format */
+ index++;
+ if (index >= ARRAY_SIZE(fmt_tb))
+ return -EINVAL;
+ }
+ }
+ if (i < 0)
+ return -EINVAL; /* no more format */
+
+ fmtdesc->pixelformat = fmt_tb[index];
+ if (gspca_dev->cam.cam_mode[i].sizeimage <
+ gspca_dev->cam.cam_mode[i].width *
+ gspca_dev->cam.cam_mode[i].height)
+ fmtdesc->flags = V4L2_FMT_FLAG_COMPRESSED;
+ fmtdesc->description[0] = fmtdesc->pixelformat & 0xff;
+ fmtdesc->description[1] = (fmtdesc->pixelformat >> 8) & 0xff;
+ fmtdesc->description[2] = (fmtdesc->pixelformat >> 16) & 0xff;
+ fmtdesc->description[3] = fmtdesc->pixelformat >> 24;
+ fmtdesc->description[4] = '\0';
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct gspca_dev *gspca_dev = priv;
+ int mode;
+
+ mode = gspca_dev->curr_mode;
+ memcpy(&fmt->fmt.pix, &gspca_dev->cam.cam_mode[mode],
+ sizeof fmt->fmt.pix);
+ return 0;
+}
+
+static int try_fmt_vid_cap(struct gspca_dev *gspca_dev,
+ struct v4l2_format *fmt)
+{
+ int w, h, mode, mode2;
+
+ w = fmt->fmt.pix.width;
+ h = fmt->fmt.pix.height;
+
+#ifdef GSPCA_DEBUG
+ if (gspca_debug & D_CONF)
+ PDEBUG_MODE("try fmt cap", fmt->fmt.pix.pixelformat, w, h);
+#endif
+ /* search the closest mode for width and height */
+ mode = wxh_to_mode(gspca_dev, w, h);
+
+ /* OK if right palette */
+ if (gspca_dev->cam.cam_mode[mode].pixelformat
+ != fmt->fmt.pix.pixelformat) {
+
+ /* else, search the closest mode with the same pixel format */
+ mode2 = gspca_get_mode(gspca_dev, mode,
+ fmt->fmt.pix.pixelformat);
+ if (mode2 >= 0)
+ mode = mode2;
+/* else
+ ; * no chance, return this mode */
+ }
+ memcpy(&fmt->fmt.pix, &gspca_dev->cam.cam_mode[mode],
+ sizeof fmt->fmt.pix);
+ return mode; /* used when s_fmt */
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file,
+ void *priv,
+ struct v4l2_format *fmt)
+{
+ struct gspca_dev *gspca_dev = priv;
+ int ret;
+
+ ret = try_fmt_vid_cap(gspca_dev, fmt);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct gspca_dev *gspca_dev = priv;
+ int ret;
+
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+ return -ERESTARTSYS;
+
+ ret = try_fmt_vid_cap(gspca_dev, fmt);
+ if (ret < 0)
+ goto out;
+
+ if (gspca_dev->nframes != 0
+ && fmt->fmt.pix.sizeimage > gspca_dev->frsz) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (ret == gspca_dev->curr_mode) {
+ ret = 0;
+ goto out; /* same mode */
+ }
+
+ if (gspca_dev->streaming) {
+ ret = -EBUSY;
+ goto out;
+ }
+ gspca_dev->width = fmt->fmt.pix.width;
+ gspca_dev->height = fmt->fmt.pix.height;
+ gspca_dev->pixfmt = fmt->fmt.pix.pixelformat;
+ gspca_dev->curr_mode = ret;
+
+ ret = 0;
+out:
+ mutex_unlock(&gspca_dev->queue_lock);
+ return ret;
+}
+
+static int vidioc_enum_framesizes(struct file *file, void *priv,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct gspca_dev *gspca_dev = priv;
+ int i;
+ __u32 index = 0;
+
+ for (i = 0; i < gspca_dev->cam.nmodes; i++) {
+ if (fsize->pixel_format !=
+ gspca_dev->cam.cam_mode[i].pixelformat)
+ continue;
+
+ if (fsize->index == index) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width =
+ gspca_dev->cam.cam_mode[i].width;
+ fsize->discrete.height =
+ gspca_dev->cam.cam_mode[i].height;
+ return 0;
+ }
+ index++;
+ }
+
+ return -EINVAL;
+}
+
+static int vidioc_enum_frameintervals(struct file *filp, void *priv,
+ struct v4l2_frmivalenum *fival)
+{
+ struct gspca_dev *gspca_dev = priv;
+ int mode = wxh_to_mode(gspca_dev, fival->width, fival->height);
+ __u32 i;
+
+ if (gspca_dev->cam.mode_framerates == NULL ||
+ gspca_dev->cam.mode_framerates[mode].nrates == 0)
+ return -EINVAL;
+
+ if (fival->pixel_format !=
+ gspca_dev->cam.cam_mode[mode].pixelformat)
+ return -EINVAL;
+
+ for (i = 0; i < gspca_dev->cam.mode_framerates[mode].nrates; i++) {
+ if (fival->index == i) {
+ fival->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fival->discrete.numerator = 1;
+ fival->discrete.denominator =
+ gspca_dev->cam.mode_framerates[mode].rates[i];
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static void gspca_release(struct video_device *vfd)
+{
+ struct gspca_dev *gspca_dev = container_of(vfd, struct gspca_dev, vdev);
+
+ PDEBUG(D_PROBE, "%s released",
+ video_device_node_name(&gspca_dev->vdev));
+
+ kfree(gspca_dev->usb_buf);
+ kfree(gspca_dev);
+}
+
+static int dev_open(struct file *file)
+{
+ struct gspca_dev *gspca_dev;
+
+ PDEBUG(D_STREAM, "[%s] open", current->comm);
+ gspca_dev = (struct gspca_dev *) video_devdata(file);
+ if (!gspca_dev->present)
+ return -ENODEV;
+
+ /* protect the subdriver against rmmod */
+ if (!try_module_get(gspca_dev->module))
+ return -ENODEV;
+
+ file->private_data = gspca_dev;
+#ifdef GSPCA_DEBUG
+ /* activate the v4l2 debug */
+ if (gspca_debug & D_V4L2)
+ gspca_dev->vdev.debug |= V4L2_DEBUG_IOCTL
+ | V4L2_DEBUG_IOCTL_ARG;
+ else
+ gspca_dev->vdev.debug &= ~(V4L2_DEBUG_IOCTL
+ | V4L2_DEBUG_IOCTL_ARG);
+#endif
+ return 0;
+}
+
+static int dev_close(struct file *file)
+{
+ struct gspca_dev *gspca_dev = file->private_data;
+
+ PDEBUG(D_STREAM, "[%s] close", current->comm);
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+ return -ERESTARTSYS;
+
+ /* if the file did the capture, free the streaming resources */
+ if (gspca_dev->capt_file == file) {
+ if (gspca_dev->streaming) {
+ mutex_lock(&gspca_dev->usb_lock);
+ gspca_dev->usb_err = 0;
+ gspca_stream_off(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
+ }
+ frame_free(gspca_dev);
+ }
+ file->private_data = NULL;
+ module_put(gspca_dev->module);
+ mutex_unlock(&gspca_dev->queue_lock);
+
+ PDEBUG(D_STREAM, "close done");
+
+ return 0;
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct gspca_dev *gspca_dev = priv;
+ int ret;
+
+ /* protect the access to the usb device */
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+ if (!gspca_dev->present) {
+ ret = -ENODEV;
+ goto out;
+ }
+ strlcpy((char *) cap->driver, gspca_dev->sd_desc->name,
+ sizeof cap->driver);
+ if (gspca_dev->dev->product != NULL) {
+ strlcpy((char *) cap->card, gspca_dev->dev->product,
+ sizeof cap->card);
+ } else {
+ snprintf((char *) cap->card, sizeof cap->card,
+ "USB Camera (%04x:%04x)",
+ le16_to_cpu(gspca_dev->dev->descriptor.idVendor),
+ le16_to_cpu(gspca_dev->dev->descriptor.idProduct));
+ }
+ usb_make_path(gspca_dev->dev, (char *) cap->bus_info,
+ sizeof(cap->bus_info));
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
+ | V4L2_CAP_STREAMING
+ | V4L2_CAP_READWRITE;
+ ret = 0;
+out:
+ mutex_unlock(&gspca_dev->usb_lock);
+ return ret;
+}
+
+static int get_ctrl(struct gspca_dev *gspca_dev,
+ int id)
+{
+ const struct ctrl *ctrls;
+ int i;
+
+ for (i = 0, ctrls = gspca_dev->sd_desc->ctrls;
+ i < gspca_dev->sd_desc->nctrls;
+ i++, ctrls++) {
+ if (gspca_dev->ctrl_dis & (1 << i))
+ continue;
+ if (id == ctrls->qctrl.id)
+ return i;
+ }
+ return -1;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *q_ctrl)
+{
+ struct gspca_dev *gspca_dev = priv;
+ const struct ctrl *ctrls;
+ struct gspca_ctrl *gspca_ctrl;
+ int i, idx;
+ u32 id;
+
+ id = q_ctrl->id;
+ if (id & V4L2_CTRL_FLAG_NEXT_CTRL) {
+ id &= V4L2_CTRL_ID_MASK;
+ id++;
+ idx = -1;
+ for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) {
+ if (gspca_dev->ctrl_dis & (1 << i))
+ continue;
+ if (gspca_dev->sd_desc->ctrls[i].qctrl.id < id)
+ continue;
+ if (idx >= 0
+ && gspca_dev->sd_desc->ctrls[i].qctrl.id
+ > gspca_dev->sd_desc->ctrls[idx].qctrl.id)
+ continue;
+ idx = i;
+ }
+ } else {
+ idx = get_ctrl(gspca_dev, id);
+ }
+ if (idx < 0)
+ return -EINVAL;
+ ctrls = &gspca_dev->sd_desc->ctrls[idx];
+ memcpy(q_ctrl, &ctrls->qctrl, sizeof *q_ctrl);
+ if (gspca_dev->cam.ctrls != NULL) {
+ gspca_ctrl = &gspca_dev->cam.ctrls[idx];
+ q_ctrl->default_value = gspca_ctrl->def;
+ q_ctrl->minimum = gspca_ctrl->min;
+ q_ctrl->maximum = gspca_ctrl->max;
+ }
+ if (gspca_dev->ctrl_inac & (1 << idx))
+ q_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct gspca_dev *gspca_dev = priv;
+ const struct ctrl *ctrls;
+ struct gspca_ctrl *gspca_ctrl;
+ int idx, ret;
+
+ idx = get_ctrl(gspca_dev, ctrl->id);
+ if (idx < 0)
+ return -EINVAL;
+ if (gspca_dev->ctrl_inac & (1 << idx))
+ return -EINVAL;
+ ctrls = &gspca_dev->sd_desc->ctrls[idx];
+ if (gspca_dev->cam.ctrls != NULL) {
+ gspca_ctrl = &gspca_dev->cam.ctrls[idx];
+ if (ctrl->value < gspca_ctrl->min
+ || ctrl->value > gspca_ctrl->max)
+ return -ERANGE;
+ } else {
+ gspca_ctrl = NULL;
+ if (ctrl->value < ctrls->qctrl.minimum
+ || ctrl->value > ctrls->qctrl.maximum)
+ return -ERANGE;
+ }
+ PDEBUG(D_CONF, "set ctrl [%08x] = %d", ctrl->id, ctrl->value);
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+ if (!gspca_dev->present) {
+ ret = -ENODEV;
+ goto out;
+ }
+ gspca_dev->usb_err = 0;
+ if (ctrls->set != NULL) {
+ ret = ctrls->set(gspca_dev, ctrl->value);
+ goto out;
+ }
+ if (gspca_ctrl != NULL) {
+ gspca_ctrl->val = ctrl->value;
+ if (ctrls->set_control != NULL
+ && gspca_dev->streaming)
+ ctrls->set_control(gspca_dev);
+ }
+ ret = gspca_dev->usb_err;
+out:
+ mutex_unlock(&gspca_dev->usb_lock);
+ return ret;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct gspca_dev *gspca_dev = priv;
+ const struct ctrl *ctrls;
+ int idx, ret;
+
+ idx = get_ctrl(gspca_dev, ctrl->id);
+ if (idx < 0)
+ return -EINVAL;
+ ctrls = &gspca_dev->sd_desc->ctrls[idx];
+
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+ if (!gspca_dev->present) {
+ ret = -ENODEV;
+ goto out;
+ }
+ gspca_dev->usb_err = 0;
+ if (ctrls->get != NULL) {
+ ret = ctrls->get(gspca_dev, &ctrl->value);
+ goto out;
+ }
+ if (gspca_dev->cam.ctrls != NULL)
+ ctrl->value = gspca_dev->cam.ctrls[idx].val;
+ ret = 0;
+out:
+ mutex_unlock(&gspca_dev->usb_lock);
+ return ret;
+}
+
+static int vidioc_querymenu(struct file *file, void *priv,
+ struct v4l2_querymenu *qmenu)
+{
+ struct gspca_dev *gspca_dev = priv;
+
+ if (!gspca_dev->sd_desc->querymenu)
+ return -EINVAL;
+ return gspca_dev->sd_desc->querymenu(gspca_dev, qmenu);
+}
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *input)
+{
+ struct gspca_dev *gspca_dev = priv;
+
+ if (input->index != 0)
+ return -EINVAL;
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+ input->status = gspca_dev->cam.input_flags;
+ strlcpy(input->name, gspca_dev->sd_desc->name,
+ sizeof input->name);
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ if (i > 0)
+ return -EINVAL;
+ return (0);
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *rb)
+{
+ struct gspca_dev *gspca_dev = priv;
+ int i, ret = 0, streaming;
+
+ i = rb->memory; /* (avoid compilation warning) */
+ switch (i) {
+ case GSPCA_MEMORY_READ: /* (internal call) */
+ case V4L2_MEMORY_MMAP:
+ case V4L2_MEMORY_USERPTR:
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+ return -ERESTARTSYS;
+
+ if (gspca_dev->memory != GSPCA_MEMORY_NO
+ && gspca_dev->memory != GSPCA_MEMORY_READ
+ && gspca_dev->memory != rb->memory) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* only one file may do the capture */
+ if (gspca_dev->capt_file != NULL
+ && gspca_dev->capt_file != file) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* if allocated, the buffers must not be mapped */
+ for (i = 0; i < gspca_dev->nframes; i++) {
+ if (gspca_dev->frame[i].vma_use_count) {
+ ret = -EBUSY;
+ goto out;
+ }
+ }
+
+ /* stop streaming */
+ streaming = gspca_dev->streaming;
+ if (streaming) {
+ mutex_lock(&gspca_dev->usb_lock);
+ gspca_dev->usb_err = 0;
+ gspca_stream_off(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
+
+ /* Don't restart the stream when switching from read
+ * to mmap mode */
+ if (gspca_dev->memory == GSPCA_MEMORY_READ)
+ streaming = 0;
+ }
+
+ /* free the previous allocated buffers, if any */
+ if (gspca_dev->nframes != 0)
+ frame_free(gspca_dev);
+ if (rb->count == 0) /* unrequest */
+ goto out;
+ ret = frame_alloc(gspca_dev, file, rb->memory, rb->count);
+ if (ret == 0) {
+ rb->count = gspca_dev->nframes;
+ if (streaming)
+ ret = gspca_init_transfer(gspca_dev);
+ }
+out:
+ mutex_unlock(&gspca_dev->queue_lock);
+ PDEBUG(D_STREAM, "reqbufs st:%d c:%d", ret, rb->count);
+ return ret;
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *v4l2_buf)
+{
+ struct gspca_dev *gspca_dev = priv;
+ struct gspca_frame *frame;
+
+ if (v4l2_buf->index < 0
+ || v4l2_buf->index >= gspca_dev->nframes)
+ return -EINVAL;
+
+ frame = &gspca_dev->frame[v4l2_buf->index];
+ memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf);
+ return 0;
+}
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type buf_type)
+{
+ struct gspca_dev *gspca_dev = priv;
+ int ret;
+
+ if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+ return -ERESTARTSYS;
+
+ /* check the capture file */
+ if (gspca_dev->capt_file != file) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ if (gspca_dev->nframes == 0
+ || !(gspca_dev->frame[0].v4l2_buf.flags & V4L2_BUF_FLAG_QUEUED)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ if (!gspca_dev->streaming) {
+ ret = gspca_init_transfer(gspca_dev);
+ if (ret < 0)
+ goto out;
+ }
+#ifdef GSPCA_DEBUG
+ if (gspca_debug & D_STREAM) {
+ PDEBUG_MODE("stream on OK",
+ gspca_dev->pixfmt,
+ gspca_dev->width,
+ gspca_dev->height);
+ }
+#endif
+ ret = 0;
+out:
+ mutex_unlock(&gspca_dev->queue_lock);
+ return ret;
+}
+
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type buf_type)
+{
+ struct gspca_dev *gspca_dev = priv;
+ int i, ret;
+
+ if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+ return -ERESTARTSYS;
+
+ if (!gspca_dev->streaming) {
+ ret = 0;
+ goto out;
+ }
+
+ /* check the capture file */
+ if (gspca_dev->capt_file != file) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* stop streaming */
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock)) {
+ ret = -ERESTARTSYS;
+ goto out;
+ }
+ gspca_dev->usb_err = 0;
+ gspca_stream_off(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
+ /* In case another thread is waiting in dqbuf */
+ wake_up_interruptible(&gspca_dev->wq);
+
+ /* empty the transfer queues */
+ for (i = 0; i < gspca_dev->nframes; i++)
+ gspca_dev->frame[i].v4l2_buf.flags &= ~BUF_ALL_FLAGS;
+ atomic_set(&gspca_dev->fr_q, 0);
+ atomic_set(&gspca_dev->fr_i, 0);
+ gspca_dev->fr_o = 0;
+ ret = 0;
+out:
+ mutex_unlock(&gspca_dev->queue_lock);
+ return ret;
+}
+
+static int vidioc_g_jpegcomp(struct file *file, void *priv,
+ struct v4l2_jpegcompression *jpegcomp)
+{
+ struct gspca_dev *gspca_dev = priv;
+ int ret;
+
+ if (!gspca_dev->sd_desc->get_jcomp)
+ return -EINVAL;
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+ gspca_dev->usb_err = 0;
+ if (gspca_dev->present)
+ ret = gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp);
+ else
+ ret = -ENODEV;
+ mutex_unlock(&gspca_dev->usb_lock);
+ return ret;
+}
+
+static int vidioc_s_jpegcomp(struct file *file, void *priv,
+ struct v4l2_jpegcompression *jpegcomp)
+{
+ struct gspca_dev *gspca_dev = priv;
+ int ret;
+
+ if (!gspca_dev->sd_desc->set_jcomp)
+ return -EINVAL;
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+ gspca_dev->usb_err = 0;
+ if (gspca_dev->present)
+ ret = gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp);
+ else
+ ret = -ENODEV;
+ mutex_unlock(&gspca_dev->usb_lock);
+ return ret;
+}
+
+static int vidioc_g_parm(struct file *filp, void *priv,
+ struct v4l2_streamparm *parm)
+{
+ struct gspca_dev *gspca_dev = priv;
+
+ parm->parm.capture.readbuffers = gspca_dev->nbufread;
+
+ if (gspca_dev->sd_desc->get_streamparm) {
+ int ret;
+
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+ if (gspca_dev->present) {
+ gspca_dev->usb_err = 0;
+ gspca_dev->sd_desc->get_streamparm(gspca_dev, parm);
+ ret = gspca_dev->usb_err;
+ } else {
+ ret = -ENODEV;
+ }
+ mutex_unlock(&gspca_dev->usb_lock);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int vidioc_s_parm(struct file *filp, void *priv,
+ struct v4l2_streamparm *parm)
+{
+ struct gspca_dev *gspca_dev = priv;
+ int n;
+
+ n = parm->parm.capture.readbuffers;
+ if (n == 0 || n >= GSPCA_MAX_FRAMES)
+ parm->parm.capture.readbuffers = gspca_dev->nbufread;
+ else
+ gspca_dev->nbufread = n;
+
+ if (gspca_dev->sd_desc->set_streamparm) {
+ int ret;
+
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+ if (gspca_dev->present) {
+ gspca_dev->usb_err = 0;
+ gspca_dev->sd_desc->set_streamparm(gspca_dev, parm);
+ ret = gspca_dev->usb_err;
+ } else {
+ ret = -ENODEV;
+ }
+ mutex_unlock(&gspca_dev->usb_lock);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dev_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct gspca_dev *gspca_dev = file->private_data;
+ struct gspca_frame *frame;
+ struct page *page;
+ unsigned long addr, start, size;
+ int i, ret;
+
+ start = vma->vm_start;
+ size = vma->vm_end - vma->vm_start;
+ PDEBUG(D_STREAM, "mmap start:%08x size:%d", (int) start, (int) size);
+
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+ return -ERESTARTSYS;
+ if (!gspca_dev->present) {
+ ret = -ENODEV;
+ goto out;
+ }
+ if (gspca_dev->capt_file != file) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ frame = NULL;
+ for (i = 0; i < gspca_dev->nframes; ++i) {
+ if (gspca_dev->frame[i].v4l2_buf.memory != V4L2_MEMORY_MMAP) {
+ PDEBUG(D_STREAM, "mmap bad memory type");
+ break;
+ }
+ if ((gspca_dev->frame[i].v4l2_buf.m.offset >> PAGE_SHIFT)
+ == vma->vm_pgoff) {
+ frame = &gspca_dev->frame[i];
+ break;
+ }
+ }
+ if (frame == NULL) {
+ PDEBUG(D_STREAM, "mmap no frame buffer found");
+ ret = -EINVAL;
+ goto out;
+ }
+ if (size != frame->v4l2_buf.length) {
+ PDEBUG(D_STREAM, "mmap bad size");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * - VM_IO marks the area as being a mmaped region for I/O to a
+ * device. It also prevents the region from being core dumped.
+ */
+ vma->vm_flags |= VM_IO;
+
+ addr = (unsigned long) frame->data;
+ while (size > 0) {
+ page = vmalloc_to_page((void *) addr);
+ ret = vm_insert_page(vma, start, page);
+ if (ret < 0)
+ goto out;
+ start += PAGE_SIZE;
+ addr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ vma->vm_ops = &gspca_vm_ops;
+ vma->vm_private_data = frame;
+ gspca_vm_open(vma);
+ ret = 0;
+out:
+ mutex_unlock(&gspca_dev->queue_lock);
+ return ret;
+}
+
+static int frame_ready_nolock(struct gspca_dev *gspca_dev, struct file *file,
+ enum v4l2_memory memory)
+{
+ if (!gspca_dev->present)
+ return -ENODEV;
+ if (gspca_dev->capt_file != file || gspca_dev->memory != memory ||
+ !gspca_dev->streaming)
+ return -EINVAL;
+
+ /* check if a frame is ready */
+ return gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i);
+}
+
+static int frame_ready(struct gspca_dev *gspca_dev, struct file *file,
+ enum v4l2_memory memory)
+{
+ int ret;
+
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+ return -ERESTARTSYS;
+ ret = frame_ready_nolock(gspca_dev, file, memory);
+ mutex_unlock(&gspca_dev->queue_lock);
+ return ret;
+}
+
+/*
+ * dequeue a video buffer
+ *
+ * If nonblock_ing is false, block until a buffer is available.
+ */
+static int vidioc_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *v4l2_buf)
+{
+ struct gspca_dev *gspca_dev = priv;
+ struct gspca_frame *frame;
+ int i, j, ret;
+
+ PDEBUG(D_FRAM, "dqbuf");
+
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+ return -ERESTARTSYS;
+
+ for (;;) {
+ ret = frame_ready_nolock(gspca_dev, file, v4l2_buf->memory);
+ if (ret < 0)
+ goto out;
+ if (ret > 0)
+ break;
+
+ mutex_unlock(&gspca_dev->queue_lock);
+
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ /* wait till a frame is ready */
+ ret = wait_event_interruptible_timeout(gspca_dev->wq,
+ frame_ready(gspca_dev, file, v4l2_buf->memory),
+ msecs_to_jiffies(3000));
+ if (ret < 0)
+ return ret;
+ if (ret == 0)
+ return -EIO;
+
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+ return -ERESTARTSYS;
+ }
+
+ i = gspca_dev->fr_o;
+ j = gspca_dev->fr_queue[i];
+ frame = &gspca_dev->frame[j];
+
+ gspca_dev->fr_o = (i + 1) % GSPCA_MAX_FRAMES;
+
+ if (gspca_dev->sd_desc->dq_callback) {
+ mutex_lock(&gspca_dev->usb_lock);
+ gspca_dev->usb_err = 0;
+ if (gspca_dev->present)
+ gspca_dev->sd_desc->dq_callback(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
+ }
+
+ frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE;
+ memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf);
+ PDEBUG(D_FRAM, "dqbuf %d", j);
+ ret = 0;
+
+ if (gspca_dev->memory == V4L2_MEMORY_USERPTR) {
+ if (copy_to_user((__u8 __user *) frame->v4l2_buf.m.userptr,
+ frame->data,
+ frame->v4l2_buf.bytesused)) {
+ PDEBUG(D_ERR|D_STREAM,
+ "dqbuf cp to user failed");
+ ret = -EFAULT;
+ }
+ }
+out:
+ mutex_unlock(&gspca_dev->queue_lock);
+ return ret;
+}
+
+/*
+ * queue a video buffer
+ *
+ * Attempting to queue a buffer that has already been
+ * queued will return -EINVAL.
+ */
+static int vidioc_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *v4l2_buf)
+{
+ struct gspca_dev *gspca_dev = priv;
+ struct gspca_frame *frame;
+ int i, index, ret;
+
+ PDEBUG(D_FRAM, "qbuf %d", v4l2_buf->index);
+
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+ return -ERESTARTSYS;
+
+ index = v4l2_buf->index;
+ if ((unsigned) index >= gspca_dev->nframes) {
+ PDEBUG(D_FRAM,
+ "qbuf idx %d >= %d", index, gspca_dev->nframes);
+ ret = -EINVAL;
+ goto out;
+ }
+ if (v4l2_buf->memory != gspca_dev->memory) {
+ PDEBUG(D_FRAM, "qbuf bad memory type");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ frame = &gspca_dev->frame[index];
+ if (frame->v4l2_buf.flags & BUF_ALL_FLAGS) {
+ PDEBUG(D_FRAM, "qbuf bad state");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ frame->v4l2_buf.flags |= V4L2_BUF_FLAG_QUEUED;
+
+ if (frame->v4l2_buf.memory == V4L2_MEMORY_USERPTR) {
+ frame->v4l2_buf.m.userptr = v4l2_buf->m.userptr;
+ frame->v4l2_buf.length = v4l2_buf->length;
+ }
+
+ /* put the buffer in the 'queued' queue */
+ i = atomic_read(&gspca_dev->fr_q);
+ gspca_dev->fr_queue[i] = index;
+ atomic_set(&gspca_dev->fr_q, (i + 1) % GSPCA_MAX_FRAMES);
+
+ v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED;
+ v4l2_buf->flags &= ~V4L2_BUF_FLAG_DONE;
+ ret = 0;
+out:
+ mutex_unlock(&gspca_dev->queue_lock);
+ return ret;
+}
+
+/*
+ * allocate the resources for read()
+ */
+static int read_alloc(struct gspca_dev *gspca_dev,
+ struct file *file)
+{
+ struct v4l2_buffer v4l2_buf;
+ int i, ret;
+
+ PDEBUG(D_STREAM, "read alloc");
+ if (gspca_dev->nframes == 0) {
+ struct v4l2_requestbuffers rb;
+
+ memset(&rb, 0, sizeof rb);
+ rb.count = gspca_dev->nbufread;
+ rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ rb.memory = GSPCA_MEMORY_READ;
+ ret = vidioc_reqbufs(file, gspca_dev, &rb);
+ if (ret != 0) {
+ PDEBUG(D_STREAM, "read reqbuf err %d", ret);
+ return ret;
+ }
+ memset(&v4l2_buf, 0, sizeof v4l2_buf);
+ v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ v4l2_buf.memory = GSPCA_MEMORY_READ;
+ for (i = 0; i < gspca_dev->nbufread; i++) {
+ v4l2_buf.index = i;
+ ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf);
+ if (ret != 0) {
+ PDEBUG(D_STREAM, "read qbuf err: %d", ret);
+ return ret;
+ }
+ }
+ gspca_dev->memory = GSPCA_MEMORY_READ;
+ }
+
+ /* start streaming */
+ ret = vidioc_streamon(file, gspca_dev, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ if (ret != 0)
+ PDEBUG(D_STREAM, "read streamon err %d", ret);
+ return ret;
+}
+
+static unsigned int dev_poll(struct file *file, poll_table *wait)
+{
+ struct gspca_dev *gspca_dev = file->private_data;
+ int ret;
+
+ PDEBUG(D_FRAM, "poll");
+
+ poll_wait(file, &gspca_dev->wq, wait);
+
+ /* if reqbufs is not done, the user would use read() */
+ if (gspca_dev->memory == GSPCA_MEMORY_NO) {
+ ret = read_alloc(gspca_dev, file);
+ if (ret != 0)
+ return POLLERR;
+ }
+
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock) != 0)
+ return POLLERR;
+
+ /* check if an image has been received */
+ if (gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i))
+ ret = POLLIN | POLLRDNORM; /* yes */
+ else
+ ret = 0;
+ mutex_unlock(&gspca_dev->queue_lock);
+ if (!gspca_dev->present)
+ return POLLHUP;
+ return ret;
+}
+
+static ssize_t dev_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct gspca_dev *gspca_dev = file->private_data;
+ struct gspca_frame *frame;
+ struct v4l2_buffer v4l2_buf;
+ struct timeval timestamp;
+ int n, ret, ret2;
+
+ PDEBUG(D_FRAM, "read (%zd)", count);
+ if (!gspca_dev->present)
+ return -ENODEV;
+ if (gspca_dev->memory == GSPCA_MEMORY_NO) { /* first time ? */
+ ret = read_alloc(gspca_dev, file);
+ if (ret != 0)
+ return ret;
+ }
+
+ /* get a frame */
+ timestamp = ktime_to_timeval(ktime_get());
+ timestamp.tv_sec--;
+ n = 2;
+ for (;;) {
+ memset(&v4l2_buf, 0, sizeof v4l2_buf);
+ v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ v4l2_buf.memory = GSPCA_MEMORY_READ;
+ ret = vidioc_dqbuf(file, gspca_dev, &v4l2_buf);
+ if (ret != 0) {
+ PDEBUG(D_STREAM, "read dqbuf err %d", ret);
+ return ret;
+ }
+
+ /* if the process slept for more than 1 second,
+ * get a newer frame */
+ frame = &gspca_dev->frame[v4l2_buf.index];
+ if (--n < 0)
+ break; /* avoid infinite loop */
+ if (frame->v4l2_buf.timestamp.tv_sec >= timestamp.tv_sec)
+ break;
+ ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf);
+ if (ret != 0) {
+ PDEBUG(D_STREAM, "read qbuf err %d", ret);
+ return ret;
+ }
+ }
+
+ /* copy the frame */
+ if (count > frame->v4l2_buf.bytesused)
+ count = frame->v4l2_buf.bytesused;
+ ret = copy_to_user(data, frame->data, count);
+ if (ret != 0) {
+ PDEBUG(D_ERR|D_STREAM,
+ "read cp to user lack %d / %zd", ret, count);
+ ret = -EFAULT;
+ goto out;
+ }
+ ret = count;
+out:
+ /* in each case, requeue the buffer */
+ ret2 = vidioc_qbuf(file, gspca_dev, &v4l2_buf);
+ if (ret2 != 0)
+ return ret2;
+ return ret;
+}
+
+static struct v4l2_file_operations dev_fops = {
+ .owner = THIS_MODULE,
+ .open = dev_open,
+ .release = dev_close,
+ .read = dev_read,
+ .mmap = dev_mmap,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = dev_poll,
+};
+
+static const struct v4l2_ioctl_ops dev_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_querymenu = vidioc_querymenu,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_jpegcomp = vidioc_g_jpegcomp,
+ .vidioc_s_jpegcomp = vidioc_s_jpegcomp,
+ .vidioc_g_parm = vidioc_g_parm,
+ .vidioc_s_parm = vidioc_s_parm,
+ .vidioc_enum_framesizes = vidioc_enum_framesizes,
+ .vidioc_enum_frameintervals = vidioc_enum_frameintervals,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+#endif
+ .vidioc_g_chip_ident = vidioc_g_chip_ident,
+};
+
+static const struct video_device gspca_template = {
+ .name = "gspca main driver",
+ .fops = &dev_fops,
+ .ioctl_ops = &dev_ioctl_ops,
+ .release = gspca_release,
+};
+
+/* initialize the controls */
+static void ctrls_init(struct gspca_dev *gspca_dev)
+{
+ struct gspca_ctrl *ctrl;
+ int i;
+
+ for (i = 0, ctrl = gspca_dev->cam.ctrls;
+ i < gspca_dev->sd_desc->nctrls;
+ i++, ctrl++) {
+ ctrl->def = gspca_dev->sd_desc->ctrls[i].qctrl.default_value;
+ ctrl->val = ctrl->def;
+ ctrl->min = gspca_dev->sd_desc->ctrls[i].qctrl.minimum;
+ ctrl->max = gspca_dev->sd_desc->ctrls[i].qctrl.maximum;
+ }
+}
+
+/*
+ * probe and create a new gspca device
+ *
+ * This function must be called by the sub-driver when it is
+ * called for probing a new device.
+ */
+int gspca_dev_probe2(struct usb_interface *intf,
+ const struct usb_device_id *id,
+ const struct sd_desc *sd_desc,
+ int dev_size,
+ struct module *module)
+{
+ struct gspca_dev *gspca_dev;
+ struct usb_device *dev = interface_to_usbdev(intf);
+ int ret;
+
+ pr_info("%s-" GSPCA_VERSION " probing %04x:%04x\n",
+ sd_desc->name, id->idVendor, id->idProduct);
+
+ /* create the device */
+ if (dev_size < sizeof *gspca_dev)
+ dev_size = sizeof *gspca_dev;
+ gspca_dev = kzalloc(dev_size, GFP_KERNEL);
+ if (!gspca_dev) {
+ pr_err("couldn't kzalloc gspca struct\n");
+ return -ENOMEM;
+ }
+ gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL);
+ if (!gspca_dev->usb_buf) {
+ pr_err("out of memory\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ gspca_dev->dev = dev;
+ gspca_dev->iface = intf->cur_altsetting->desc.bInterfaceNumber;
+
+ /* check if any audio device */
+ if (dev->actconfig->desc.bNumInterfaces != 1) {
+ int i;
+ struct usb_interface *intf2;
+
+ for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {
+ intf2 = dev->actconfig->interface[i];
+ if (intf2 != NULL
+ && intf2->altsetting != NULL
+ && intf2->altsetting->desc.bInterfaceClass ==
+ USB_CLASS_AUDIO) {
+ gspca_dev->audio = 1;
+ break;
+ }
+ }
+ }
+
+ gspca_dev->sd_desc = sd_desc;
+ gspca_dev->nbufread = 2;
+ gspca_dev->empty_packet = -1; /* don't check the empty packets */
+
+ /* configure the subdriver and initialize the USB device */
+ ret = sd_desc->config(gspca_dev, id);
+ if (ret < 0)
+ goto out;
+ if (gspca_dev->cam.ctrls != NULL)
+ ctrls_init(gspca_dev);
+ ret = sd_desc->init(gspca_dev);
+ if (ret < 0)
+ goto out;
+ gspca_set_default_mode(gspca_dev);
+
+ ret = gspca_input_connect(gspca_dev);
+ if (ret)
+ goto out;
+
+ mutex_init(&gspca_dev->usb_lock);
+ mutex_init(&gspca_dev->queue_lock);
+ init_waitqueue_head(&gspca_dev->wq);
+
+ /* init video stuff */
+ memcpy(&gspca_dev->vdev, &gspca_template, sizeof gspca_template);
+ gspca_dev->vdev.parent = &intf->dev;
+ gspca_dev->module = module;
+ gspca_dev->present = 1;
+ ret = video_register_device(&gspca_dev->vdev,
+ VFL_TYPE_GRABBER,
+ -1);
+ if (ret < 0) {
+ pr_err("video_register_device err %d\n", ret);
+ goto out;
+ }
+
+ usb_set_intfdata(intf, gspca_dev);
+ PDEBUG(D_PROBE, "%s created", video_device_node_name(&gspca_dev->vdev));
+
+ gspca_input_create_urb(gspca_dev);
+
+ return 0;
+out:
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ if (gspca_dev->input_dev)
+ input_unregister_device(gspca_dev->input_dev);
+#endif
+ kfree(gspca_dev->usb_buf);
+ kfree(gspca_dev);
+ return ret;
+}
+EXPORT_SYMBOL(gspca_dev_probe2);
+
+/* same function as the previous one, but check the interface */
+int gspca_dev_probe(struct usb_interface *intf,
+ const struct usb_device_id *id,
+ const struct sd_desc *sd_desc,
+ int dev_size,
+ struct module *module)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+
+ /* we don't handle multi-config cameras */
+ if (dev->descriptor.bNumConfigurations != 1) {
+ pr_err("%04x:%04x too many config\n",
+ id->idVendor, id->idProduct);
+ return -ENODEV;
+ }
+
+ /* the USB video interface must be the first one */
+ if (dev->actconfig->desc.bNumInterfaces != 1
+ && intf->cur_altsetting->desc.bInterfaceNumber != 0)
+ return -ENODEV;
+
+ return gspca_dev_probe2(intf, id, sd_desc, dev_size, module);
+}
+EXPORT_SYMBOL(gspca_dev_probe);
+
+/*
+ * USB disconnection
+ *
+ * This function must be called by the sub-driver
+ * when the device disconnects, after the specific resources are freed.
+ */
+void gspca_disconnect(struct usb_interface *intf)
+{
+ struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ struct input_dev *input_dev;
+#endif
+
+ PDEBUG(D_PROBE, "%s disconnect",
+ video_device_node_name(&gspca_dev->vdev));
+ mutex_lock(&gspca_dev->usb_lock);
+
+ gspca_dev->present = 0;
+ wake_up_interruptible(&gspca_dev->wq);
+
+ destroy_urbs(gspca_dev);
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ gspca_input_destroy_urb(gspca_dev);
+ input_dev = gspca_dev->input_dev;
+ if (input_dev) {
+ gspca_dev->input_dev = NULL;
+ input_unregister_device(input_dev);
+ }
+#endif
+
+ /* the device is freed at exit of this function */
+ gspca_dev->dev = NULL;
+ mutex_unlock(&gspca_dev->usb_lock);
+
+ usb_set_intfdata(intf, NULL);
+
+ /* release the device */
+ /* (this will call gspca_release() immediately or on last close) */
+ video_unregister_device(&gspca_dev->vdev);
+
+/* PDEBUG(D_PROBE, "disconnect complete"); */
+}
+EXPORT_SYMBOL(gspca_disconnect);
+
+#ifdef CONFIG_PM
+int gspca_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
+
+ if (!gspca_dev->streaming)
+ return 0;
+ gspca_dev->frozen = 1; /* avoid urb error messages */
+ if (gspca_dev->sd_desc->stopN)
+ gspca_dev->sd_desc->stopN(gspca_dev);
+ destroy_urbs(gspca_dev);
+ gspca_input_destroy_urb(gspca_dev);
+ gspca_set_alt0(gspca_dev);
+ if (gspca_dev->sd_desc->stop0)
+ gspca_dev->sd_desc->stop0(gspca_dev);
+ return 0;
+}
+EXPORT_SYMBOL(gspca_suspend);
+
+int gspca_resume(struct usb_interface *intf)
+{
+ struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
+
+ gspca_dev->frozen = 0;
+ gspca_dev->sd_desc->init(gspca_dev);
+ gspca_input_create_urb(gspca_dev);
+ if (gspca_dev->streaming)
+ return gspca_init_transfer(gspca_dev);
+ return 0;
+}
+EXPORT_SYMBOL(gspca_resume);
+#endif
+/* -- cam driver utility functions -- */
+
+/* auto gain and exposure algorithm based on the knee algorithm described here:
+ http://ytse.tricolour.net/docs/LowLightOptimization.html
+
+ Returns 0 if no changes were made, 1 if the gain and or exposure settings
+ where changed. */
+int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum,
+ int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee)
+{
+ int i, steps, gain, orig_gain, exposure, orig_exposure, autogain;
+ const struct ctrl *gain_ctrl = NULL;
+ const struct ctrl *exposure_ctrl = NULL;
+ const struct ctrl *autogain_ctrl = NULL;
+ int retval = 0;
+
+ for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) {
+ if (gspca_dev->ctrl_dis & (1 << i))
+ continue;
+ if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_GAIN)
+ gain_ctrl = &gspca_dev->sd_desc->ctrls[i];
+ if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_EXPOSURE)
+ exposure_ctrl = &gspca_dev->sd_desc->ctrls[i];
+ if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_AUTOGAIN)
+ autogain_ctrl = &gspca_dev->sd_desc->ctrls[i];
+ }
+ if (!gain_ctrl || !exposure_ctrl || !autogain_ctrl) {
+ PDEBUG(D_ERR, "Error: gspca_auto_gain_n_exposure called "
+ "on cam without (auto)gain/exposure");
+ return 0;
+ }
+
+ if (gain_ctrl->get(gspca_dev, &gain) ||
+ exposure_ctrl->get(gspca_dev, &exposure) ||
+ autogain_ctrl->get(gspca_dev, &autogain) || !autogain)
+ return 0;
+
+ orig_gain = gain;
+ orig_exposure = exposure;
+
+ /* If we are of a multiple of deadzone, do multiple steps to reach the
+ desired lumination fast (with the risc of a slight overshoot) */
+ steps = abs(desired_avg_lum - avg_lum) / deadzone;
+
+ PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d",
+ avg_lum, desired_avg_lum, steps);
+
+ for (i = 0; i < steps; i++) {
+ if (avg_lum > desired_avg_lum) {
+ if (gain > gain_knee)
+ gain--;
+ else if (exposure > exposure_knee)
+ exposure--;
+ else if (gain > gain_ctrl->qctrl.default_value)
+ gain--;
+ else if (exposure > exposure_ctrl->qctrl.minimum)
+ exposure--;
+ else if (gain > gain_ctrl->qctrl.minimum)
+ gain--;
+ else
+ break;
+ } else {
+ if (gain < gain_ctrl->qctrl.default_value)
+ gain++;
+ else if (exposure < exposure_knee)
+ exposure++;
+ else if (gain < gain_knee)
+ gain++;
+ else if (exposure < exposure_ctrl->qctrl.maximum)
+ exposure++;
+ else if (gain < gain_ctrl->qctrl.maximum)
+ gain++;
+ else
+ break;
+ }
+ }
+
+ if (gain != orig_gain) {
+ gain_ctrl->set(gspca_dev, gain);
+ retval = 1;
+ }
+ if (exposure != orig_exposure) {
+ exposure_ctrl->set(gspca_dev, exposure);
+ retval = 1;
+ }
+
+ return retval;
+}
+EXPORT_SYMBOL(gspca_auto_gain_n_exposure);
+
+/* -- module insert / remove -- */
+static int __init gspca_init(void)
+{
+ pr_info("v" GSPCA_VERSION " registered\n");
+ return 0;
+}
+static void __exit gspca_exit(void)
+{
+}
+
+module_init(gspca_init);
+module_exit(gspca_exit);
+
+#ifdef GSPCA_DEBUG
+module_param_named(debug, gspca_debug, int, 0644);
+MODULE_PARM_DESC(debug,
+ "Debug (bit) 0x01:error 0x02:probe 0x04:config"
+ " 0x08:stream 0x10:frame 0x20:packet"
+ " 0x0100: v4l2");
+#endif
diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h
new file mode 100644
index 00000000..589009f4
--- /dev/null
+++ b/drivers/media/video/gspca/gspca.h
@@ -0,0 +1,237 @@
+#ifndef GSPCAV2_H
+#define GSPCAV2_H
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <linux/mutex.h>
+
+/* compilation option */
+/*#define GSPCA_DEBUG 1*/
+
+#ifdef GSPCA_DEBUG
+/* GSPCA our debug messages */
+extern int gspca_debug;
+#define PDEBUG(level, fmt, ...) \
+do { \
+ if (gspca_debug & (level)) \
+ pr_info(fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define D_ERR 0x01
+#define D_PROBE 0x02
+#define D_CONF 0x04
+#define D_STREAM 0x08
+#define D_FRAM 0x10
+#define D_PACK 0x20
+#define D_USBI 0x00
+#define D_USBO 0x00
+#define D_V4L2 0x0100
+#else
+#define PDEBUG(level, fmt, ...)
+#endif
+
+#define GSPCA_MAX_FRAMES 16 /* maximum number of video frame buffers */
+/* image transfers */
+#define MAX_NURBS 4 /* max number of URBs */
+
+
+/* used to list framerates supported by a camera mode (resolution) */
+struct framerates {
+ const u8 *rates;
+ int nrates;
+};
+
+/* control definition */
+struct gspca_ctrl {
+ s16 val; /* current value */
+ s16 def; /* default value */
+ s16 min, max; /* minimum and maximum values */
+};
+
+/* device information - set at probe time */
+struct cam {
+ const struct v4l2_pix_format *cam_mode; /* size nmodes */
+ const struct framerates *mode_framerates; /* must have size nmodes,
+ * just like cam_mode */
+ struct gspca_ctrl *ctrls; /* control table - size nctrls */
+ /* may be NULL */
+ u32 bulk_size; /* buffer size when image transfer by bulk */
+ u32 input_flags; /* value for ENUM_INPUT status flags */
+ u8 nmodes; /* size of cam_mode */
+ u8 no_urb_create; /* don't create transfer URBs */
+ u8 bulk_nurbs; /* number of URBs in bulk mode
+ * - cannot be > MAX_NURBS
+ * - when 0 and bulk_size != 0 means
+ * 1 URB and submit done by subdriver */
+ u8 bulk; /* image transfer by 0:isoc / 1:bulk */
+ u8 npkt; /* number of packets in an ISOC message
+ * 0 is the default value: 32 packets */
+ u8 needs_full_bandwidth;/* Set this flag to notify the bandwidth calc.
+ * code that the cam fills all image buffers to
+ * the max, even when using compression. */
+};
+
+struct gspca_dev;
+struct gspca_frame;
+
+/* subdriver operations */
+typedef int (*cam_op) (struct gspca_dev *);
+typedef void (*cam_v_op) (struct gspca_dev *);
+typedef int (*cam_cf_op) (struct gspca_dev *, const struct usb_device_id *);
+typedef int (*cam_jpg_op) (struct gspca_dev *,
+ struct v4l2_jpegcompression *);
+typedef int (*cam_reg_op) (struct gspca_dev *,
+ struct v4l2_dbg_register *);
+typedef int (*cam_ident_op) (struct gspca_dev *,
+ struct v4l2_dbg_chip_ident *);
+typedef void (*cam_streamparm_op) (struct gspca_dev *,
+ struct v4l2_streamparm *);
+typedef int (*cam_qmnu_op) (struct gspca_dev *,
+ struct v4l2_querymenu *);
+typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev,
+ u8 *data,
+ int len);
+typedef int (*cam_int_pkt_op) (struct gspca_dev *gspca_dev,
+ u8 *data,
+ int len);
+
+struct ctrl {
+ struct v4l2_queryctrl qctrl;
+ int (*set)(struct gspca_dev *, __s32);
+ int (*get)(struct gspca_dev *, __s32 *);
+ cam_v_op set_control;
+};
+
+/* subdriver description */
+struct sd_desc {
+/* information */
+ const char *name; /* sub-driver name */
+/* controls */
+ const struct ctrl *ctrls; /* static control definition */
+ int nctrls;
+/* mandatory operations */
+ cam_cf_op config; /* called on probe */
+ cam_op init; /* called on probe and resume */
+ cam_op start; /* called on stream on after URBs creation */
+ cam_pkt_op pkt_scan;
+/* optional operations */
+ cam_op isoc_init; /* called on stream on before getting the EP */
+ cam_op isoc_nego; /* called when URB submit failed with NOSPC */
+ cam_v_op stopN; /* called on stream off - main alt */
+ cam_v_op stop0; /* called on stream off & disconnect - alt 0 */
+ cam_v_op dq_callback; /* called when a frame has been dequeued */
+ cam_jpg_op get_jcomp;
+ cam_jpg_op set_jcomp;
+ cam_qmnu_op querymenu;
+ cam_streamparm_op get_streamparm;
+ cam_streamparm_op set_streamparm;
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ cam_reg_op set_register;
+ cam_reg_op get_register;
+#endif
+ cam_ident_op get_chip_ident;
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ cam_int_pkt_op int_pkt_scan;
+ /* other_input makes the gspca core create gspca_dev->input even when
+ int_pkt_scan is NULL, for cams with non interrupt driven buttons */
+ u8 other_input;
+#endif
+};
+
+/* packet types when moving from iso buf to frame buf */
+enum gspca_packet_type {
+ DISCARD_PACKET,
+ FIRST_PACKET,
+ INTER_PACKET,
+ LAST_PACKET
+};
+
+struct gspca_frame {
+ __u8 *data; /* frame buffer */
+ int vma_use_count;
+ struct v4l2_buffer v4l2_buf;
+};
+
+struct gspca_dev {
+ struct video_device vdev; /* !! must be the first item */
+ struct module *module; /* subdriver handling the device */
+ struct usb_device *dev;
+ struct file *capt_file; /* file doing video capture */
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ struct input_dev *input_dev;
+ char phys[64]; /* physical device path */
+#endif
+
+ struct cam cam; /* device information */
+ const struct sd_desc *sd_desc; /* subdriver description */
+ unsigned ctrl_dis; /* disabled controls (bit map) */
+ unsigned ctrl_inac; /* inactive controls (bit map) */
+
+#define USB_BUF_SZ 64
+ __u8 *usb_buf; /* buffer for USB exchanges */
+ struct urb *urb[MAX_NURBS];
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ struct urb *int_urb;
+#endif
+
+ __u8 *frbuf; /* buffer for nframes */
+ struct gspca_frame frame[GSPCA_MAX_FRAMES];
+ u8 *image; /* image beeing filled */
+ __u32 frsz; /* frame size */
+ u32 image_len; /* current length of image */
+ atomic_t fr_q; /* next frame to queue */
+ atomic_t fr_i; /* frame being filled */
+ signed char fr_queue[GSPCA_MAX_FRAMES]; /* frame queue */
+ char nframes; /* number of frames */
+ u8 fr_o; /* next frame to dequeue */
+ __u8 last_packet_type;
+ __s8 empty_packet; /* if (-1) don't check empty packets */
+ __u8 streaming;
+
+ __u8 curr_mode; /* current camera mode */
+ __u32 pixfmt; /* current mode parameters */
+ __u16 width;
+ __u16 height;
+ __u32 sequence; /* frame sequence number */
+
+ wait_queue_head_t wq; /* wait queue */
+ struct mutex usb_lock; /* usb exchange protection */
+ struct mutex queue_lock; /* ISOC queue protection */
+ int usb_err; /* USB error - protected by usb_lock */
+ u16 pkt_size; /* ISOC packet size */
+#ifdef CONFIG_PM
+ char frozen; /* suspend - resume */
+#endif
+ char present; /* device connected */
+ char nbufread; /* number of buffers for read() */
+ char memory; /* memory type (V4L2_MEMORY_xxx) */
+ __u8 iface; /* USB interface number */
+ __u8 alt; /* USB alternate setting */
+ u8 audio; /* presence of audio device */
+};
+
+int gspca_dev_probe(struct usb_interface *intf,
+ const struct usb_device_id *id,
+ const struct sd_desc *sd_desc,
+ int dev_size,
+ struct module *module);
+int gspca_dev_probe2(struct usb_interface *intf,
+ const struct usb_device_id *id,
+ const struct sd_desc *sd_desc,
+ int dev_size,
+ struct module *module);
+void gspca_disconnect(struct usb_interface *intf);
+void gspca_frame_add(struct gspca_dev *gspca_dev,
+ enum gspca_packet_type packet_type,
+ const u8 *data,
+ int len);
+#ifdef CONFIG_PM
+int gspca_suspend(struct usb_interface *intf, pm_message_t message);
+int gspca_resume(struct usb_interface *intf);
+#endif
+int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum,
+ int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee);
+#endif /* GSPCAV2_H */
diff --git a/drivers/media/video/gspca/jeilinj.c b/drivers/media/video/gspca/jeilinj.c
new file mode 100644
index 00000000..5ab3f7e1
--- /dev/null
+++ b/drivers/media/video/gspca/jeilinj.c
@@ -0,0 +1,585 @@
+/*
+ * Jeilinj subdriver
+ *
+ * Supports some Jeilin dual-mode cameras which use bulk transport and
+ * download raw JPEG data.
+ *
+ * Copyright (C) 2009 Theodore Kilgore
+ *
+ * Sportscam DV15 support and control settings are
+ * Copyright (C) 2011 Patrice Chotard
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "jeilinj"
+
+#include <linux/slab.h>
+#include "gspca.h"
+#include "jpeg.h"
+
+MODULE_AUTHOR("Theodore Kilgore <kilgota@auburn.edu>");
+MODULE_DESCRIPTION("GSPCA/JEILINJ USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* Default timeouts, in ms */
+#define JEILINJ_CMD_TIMEOUT 500
+#define JEILINJ_CMD_DELAY 160
+#define JEILINJ_DATA_TIMEOUT 1000
+
+/* Maximum transfer size to use. */
+#define JEILINJ_MAX_TRANSFER 0x200
+#define FRAME_HEADER_LEN 0x10
+#define FRAME_START 0xFFFFFFFF
+
+enum {
+ SAKAR_57379,
+ SPORTSCAM_DV15,
+};
+
+#define CAMQUALITY_MIN 0 /* highest cam quality */
+#define CAMQUALITY_MAX 97 /* lowest cam quality */
+
+enum e_ctrl {
+ LIGHTFREQ,
+ AUTOGAIN,
+ RED,
+ GREEN,
+ BLUE,
+ NCTRLS /* number of controls */
+};
+
+/* Structure to hold all of our device specific stuff */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ struct gspca_ctrl ctrls[NCTRLS];
+ int blocks_left;
+ const struct v4l2_pix_format *cap_mode;
+ /* Driver stuff */
+ u8 type;
+ u8 quality; /* image quality */
+#define QUALITY_MIN 35
+#define QUALITY_MAX 85
+#define QUALITY_DEF 85
+ u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+
+struct jlj_command {
+ unsigned char instruction[2];
+ unsigned char ack_wanted;
+ unsigned char delay;
+};
+
+/* AFAICT these cameras will only do 320x240. */
+static struct v4l2_pix_format jlj_mode[] = {
+ { 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+ { 640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0}
+};
+
+/*
+ * cam uses endpoint 0x03 to send commands, 0x84 for read commands,
+ * and 0x82 for bulk transfer.
+ */
+
+/* All commands are two bytes only */
+static void jlj_write2(struct gspca_dev *gspca_dev, unsigned char *command)
+{
+ int retval;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ memcpy(gspca_dev->usb_buf, command, 2);
+ retval = usb_bulk_msg(gspca_dev->dev,
+ usb_sndbulkpipe(gspca_dev->dev, 3),
+ gspca_dev->usb_buf, 2, NULL, 500);
+ if (retval < 0) {
+ pr_err("command write [%02x] error %d\n",
+ gspca_dev->usb_buf[0], retval);
+ gspca_dev->usb_err = retval;
+ }
+}
+
+/* Responses are one byte only */
+static void jlj_read1(struct gspca_dev *gspca_dev, unsigned char response)
+{
+ int retval;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ retval = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x84),
+ gspca_dev->usb_buf, 1, NULL, 500);
+ response = gspca_dev->usb_buf[0];
+ if (retval < 0) {
+ pr_err("read command [%02x] error %d\n",
+ gspca_dev->usb_buf[0], retval);
+ gspca_dev->usb_err = retval;
+ }
+}
+
+static void setfreq(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 freq_commands[][2] = {
+ {0x71, 0x80},
+ {0x70, 0x07}
+ };
+
+ freq_commands[0][1] |= (sd->ctrls[LIGHTFREQ].val >> 1);
+
+ jlj_write2(gspca_dev, freq_commands[0]);
+ jlj_write2(gspca_dev, freq_commands[1]);
+}
+
+static void setcamquality(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 quality_commands[][2] = {
+ {0x71, 0x1E},
+ {0x70, 0x06}
+ };
+ u8 camquality;
+
+ /* adapt camera quality from jpeg quality */
+ camquality = ((QUALITY_MAX - sd->quality) * CAMQUALITY_MAX)
+ / (QUALITY_MAX - QUALITY_MIN);
+ quality_commands[0][1] += camquality;
+
+ jlj_write2(gspca_dev, quality_commands[0]);
+ jlj_write2(gspca_dev, quality_commands[1]);
+}
+
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 autogain_commands[][2] = {
+ {0x94, 0x02},
+ {0xcf, 0x00}
+ };
+
+ autogain_commands[1][1] = (sd->ctrls[AUTOGAIN].val << 4);
+
+ jlj_write2(gspca_dev, autogain_commands[0]);
+ jlj_write2(gspca_dev, autogain_commands[1]);
+}
+
+static void setred(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 setred_commands[][2] = {
+ {0x94, 0x02},
+ {0xe6, 0x00}
+ };
+
+ setred_commands[1][1] = sd->ctrls[RED].val;
+
+ jlj_write2(gspca_dev, setred_commands[0]);
+ jlj_write2(gspca_dev, setred_commands[1]);
+}
+
+static void setgreen(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 setgreen_commands[][2] = {
+ {0x94, 0x02},
+ {0xe7, 0x00}
+ };
+
+ setgreen_commands[1][1] = sd->ctrls[GREEN].val;
+
+ jlj_write2(gspca_dev, setgreen_commands[0]);
+ jlj_write2(gspca_dev, setgreen_commands[1]);
+}
+
+static void setblue(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 setblue_commands[][2] = {
+ {0x94, 0x02},
+ {0xe9, 0x00}
+ };
+
+ setblue_commands[1][1] = sd->ctrls[BLUE].val;
+
+ jlj_write2(gspca_dev, setblue_commands[0]);
+ jlj_write2(gspca_dev, setblue_commands[1]);
+}
+
+static const struct ctrl sd_ctrls[NCTRLS] = {
+[LIGHTFREQ] = {
+ {
+ .id = V4L2_CID_POWER_LINE_FREQUENCY,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Light frequency filter",
+ .minimum = V4L2_CID_POWER_LINE_FREQUENCY_DISABLED, /* 1 */
+ .maximum = V4L2_CID_POWER_LINE_FREQUENCY_60HZ, /* 2 */
+ .step = 1,
+ .default_value = V4L2_CID_POWER_LINE_FREQUENCY_60HZ,
+ },
+ .set_control = setfreq
+ },
+[AUTOGAIN] = {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Automatic Gain (and Exposure)",
+ .minimum = 0,
+ .maximum = 3,
+ .step = 1,
+#define AUTOGAIN_DEF 0
+ .default_value = AUTOGAIN_DEF,
+ },
+ .set_control = setautogain
+ },
+[RED] = {
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "red balance",
+ .minimum = 0,
+ .maximum = 3,
+ .step = 1,
+#define RED_BALANCE_DEF 2
+ .default_value = RED_BALANCE_DEF,
+ },
+ .set_control = setred
+ },
+
+[GREEN] = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "green balance",
+ .minimum = 0,
+ .maximum = 3,
+ .step = 1,
+#define GREEN_BALANCE_DEF 2
+ .default_value = GREEN_BALANCE_DEF,
+ },
+ .set_control = setgreen
+ },
+[BLUE] = {
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "blue balance",
+ .minimum = 0,
+ .maximum = 3,
+ .step = 1,
+#define BLUE_BALANCE_DEF 2
+ .default_value = BLUE_BALANCE_DEF,
+ },
+ .set_control = setblue
+ },
+};
+
+static int jlj_start(struct gspca_dev *gspca_dev)
+{
+ int i;
+ int start_commands_size;
+ u8 response = 0xff;
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct jlj_command start_commands[] = {
+ {{0x71, 0x81}, 0, 0},
+ {{0x70, 0x05}, 0, JEILINJ_CMD_DELAY},
+ {{0x95, 0x70}, 1, 0},
+ {{0x71, 0x81 - gspca_dev->curr_mode}, 0, 0},
+ {{0x70, 0x04}, 0, JEILINJ_CMD_DELAY},
+ {{0x95, 0x70}, 1, 0},
+ {{0x71, 0x00}, 0, 0}, /* start streaming ??*/
+ {{0x70, 0x08}, 0, JEILINJ_CMD_DELAY},
+ {{0x95, 0x70}, 1, 0},
+#define SPORTSCAM_DV15_CMD_SIZE 9
+ {{0x94, 0x02}, 0, 0},
+ {{0xde, 0x24}, 0, 0},
+ {{0x94, 0x02}, 0, 0},
+ {{0xdd, 0xf0}, 0, 0},
+ {{0x94, 0x02}, 0, 0},
+ {{0xe3, 0x2c}, 0, 0},
+ {{0x94, 0x02}, 0, 0},
+ {{0xe4, 0x00}, 0, 0},
+ {{0x94, 0x02}, 0, 0},
+ {{0xe5, 0x00}, 0, 0},
+ {{0x94, 0x02}, 0, 0},
+ {{0xe6, 0x2c}, 0, 0},
+ {{0x94, 0x03}, 0, 0},
+ {{0xaa, 0x00}, 0, 0}
+ };
+
+ sd->blocks_left = 0;
+ /* Under Windows, USB spy shows that only the 9 first start
+ * commands are used for SPORTSCAM_DV15 webcam
+ */
+ if (sd->type == SPORTSCAM_DV15)
+ start_commands_size = SPORTSCAM_DV15_CMD_SIZE;
+ else
+ start_commands_size = ARRAY_SIZE(start_commands);
+
+ for (i = 0; i < start_commands_size; i++) {
+ jlj_write2(gspca_dev, start_commands[i].instruction);
+ if (start_commands[i].delay)
+ msleep(start_commands[i].delay);
+ if (start_commands[i].ack_wanted)
+ jlj_read1(gspca_dev, response);
+ }
+ setcamquality(gspca_dev);
+ msleep(2);
+ setfreq(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ PDEBUG(D_ERR, "Start streaming command failed");
+ return gspca_dev->usb_err;
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int packet_type;
+ u32 header_marker;
+
+ PDEBUG(D_STREAM, "Got %d bytes out of %d for Block 0",
+ len, JEILINJ_MAX_TRANSFER);
+ if (len != JEILINJ_MAX_TRANSFER) {
+ PDEBUG(D_PACK, "bad length");
+ goto discard;
+ }
+ /* check if it's start of frame */
+ header_marker = ((u32 *)data)[0];
+ if (header_marker == FRAME_START) {
+ sd->blocks_left = data[0x0a] - 1;
+ PDEBUG(D_STREAM, "blocks_left = 0x%x", sd->blocks_left);
+ /* Start a new frame, and add the JPEG header, first thing */
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+ /* Toss line 0 of data block 0, keep the rest. */
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data + FRAME_HEADER_LEN,
+ JEILINJ_MAX_TRANSFER - FRAME_HEADER_LEN);
+ } else if (sd->blocks_left > 0) {
+ PDEBUG(D_STREAM, "%d blocks remaining for frame",
+ sd->blocks_left);
+ sd->blocks_left -= 1;
+ if (sd->blocks_left == 0)
+ packet_type = LAST_PACKET;
+ else
+ packet_type = INTER_PACKET;
+ gspca_frame_add(gspca_dev, packet_type,
+ data, JEILINJ_MAX_TRANSFER);
+ } else
+ goto discard;
+ return;
+discard:
+ /* Discard data until a new frame starts. */
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+}
+
+/* This function is called at probe time just before sd_init */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct cam *cam = &gspca_dev->cam;
+ struct sd *dev = (struct sd *) gspca_dev;
+
+ dev->type = id->driver_info;
+ gspca_dev->cam.ctrls = dev->ctrls;
+ dev->quality = QUALITY_DEF;
+
+ cam->cam_mode = jlj_mode;
+ cam->nmodes = ARRAY_SIZE(jlj_mode);
+ cam->bulk = 1;
+ cam->bulk_nurbs = 1;
+ cam->bulk_size = JEILINJ_MAX_TRANSFER;
+ return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ int i;
+ u8 *buf;
+ static u8 stop_commands[][2] = {
+ {0x71, 0x00},
+ {0x70, 0x09},
+ {0x71, 0x80},
+ {0x70, 0x05}
+ };
+
+ for (;;) {
+ /* get the image remaining blocks */
+ usb_bulk_msg(gspca_dev->dev,
+ gspca_dev->urb[0]->pipe,
+ gspca_dev->urb[0]->transfer_buffer,
+ JEILINJ_MAX_TRANSFER, NULL,
+ JEILINJ_DATA_TIMEOUT);
+
+ /* search for 0xff 0xd9 (EOF for JPEG) */
+ i = 0;
+ buf = gspca_dev->urb[0]->transfer_buffer;
+ while ((i < (JEILINJ_MAX_TRANSFER - 1)) &&
+ ((buf[i] != 0xff) || (buf[i+1] != 0xd9)))
+ i++;
+
+ if (i != (JEILINJ_MAX_TRANSFER - 1))
+ /* last remaining block found */
+ break;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(stop_commands); i++)
+ jlj_write2(gspca_dev, stop_commands[i]);
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ return gspca_dev->usb_err;
+}
+
+/* Set up for getting frames. */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *dev = (struct sd *) gspca_dev;
+
+ /* create the JPEG header */
+ jpeg_define(dev->jpeg_hdr, gspca_dev->height, gspca_dev->width,
+ 0x21); /* JPEG 422 */
+ jpeg_set_qual(dev->jpeg_hdr, dev->quality);
+ PDEBUG(D_STREAM, "Start streaming at %dx%d",
+ gspca_dev->height, gspca_dev->width);
+ jlj_start(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+/* Table of supported USB devices */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x0979, 0x0280), .driver_info = SAKAR_57379},
+ {USB_DEVICE(0x0979, 0x0270), .driver_info = SPORTSCAM_DV15},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+static int sd_querymenu(struct gspca_dev *gspca_dev,
+ struct v4l2_querymenu *menu)
+{
+ switch (menu->id) {
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ switch (menu->index) {
+ case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
+ strcpy((char *) menu->name, "disable");
+ return 0;
+ case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
+ strcpy((char *) menu->name, "50 Hz");
+ return 0;
+ case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
+ strcpy((char *) menu->name, "60 Hz");
+ return 0;
+ }
+ break;
+ }
+ return -EINVAL;
+}
+
+static int sd_set_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (jcomp->quality < QUALITY_MIN)
+ sd->quality = QUALITY_MIN;
+ else if (jcomp->quality > QUALITY_MAX)
+ sd->quality = QUALITY_MAX;
+ else
+ sd->quality = jcomp->quality;
+ if (gspca_dev->streaming) {
+ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+ setcamquality(gspca_dev);
+ }
+ return 0;
+}
+
+static int sd_get_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ memset(jcomp, 0, sizeof *jcomp);
+ jcomp->quality = sd->quality;
+ jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
+ | V4L2_JPEG_MARKER_DQT;
+ return 0;
+}
+
+
+/* sub-driver description */
+static const struct sd_desc sd_desc_sakar_57379 = {
+ .name = MODULE_NAME,
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+};
+
+/* sub-driver description */
+static const struct sd_desc sd_desc_sportscam_dv15 = {
+ .name = MODULE_NAME,
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .querymenu = sd_querymenu,
+ .get_jcomp = sd_get_jcomp,
+ .set_jcomp = sd_set_jcomp,
+};
+
+static const struct sd_desc *sd_desc[2] = {
+ &sd_desc_sakar_57379,
+ &sd_desc_sportscam_dv15
+};
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id,
+ sd_desc[id->driver_info],
+ sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/jl2005bcd.c b/drivers/media/video/gspca/jl2005bcd.c
new file mode 100644
index 00000000..53f58ef3
--- /dev/null
+++ b/drivers/media/video/gspca/jl2005bcd.c
@@ -0,0 +1,554 @@
+/*
+ * Jeilin JL2005B/C/D library
+ *
+ * Copyright (C) 2011 Theodore Kilgore <kilgota@auburn.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define MODULE_NAME "jl2005bcd"
+
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include "gspca.h"
+
+
+MODULE_AUTHOR("Theodore Kilgore <kilgota@auburn.edu>");
+MODULE_DESCRIPTION("JL2005B/C/D USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* Default timeouts, in ms */
+#define JL2005C_CMD_TIMEOUT 500
+#define JL2005C_DATA_TIMEOUT 1000
+
+/* Maximum transfer size to use. */
+#define JL2005C_MAX_TRANSFER 0x200
+#define FRAME_HEADER_LEN 16
+
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ unsigned char firmware_id[6];
+ const struct v4l2_pix_format *cap_mode;
+ /* Driver stuff */
+ struct work_struct work_struct;
+ struct workqueue_struct *work_thread;
+ u8 frame_brightness;
+ int block_size; /* block size of camera */
+ int vga; /* 1 if vga cam, 0 if cif cam */
+};
+
+
+/* Camera has two resolution settings. What they are depends on model. */
+static const struct v4l2_pix_format cif_mode[] = {
+ {176, 144, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+ {352, 288, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {320, 240, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+ {640, 480, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+
+/*
+ * cam uses endpoint 0x03 to send commands, 0x84 for read commands,
+ * and 0x82 for bulk data transfer.
+ */
+
+/* All commands are two bytes only */
+static int jl2005c_write2(struct gspca_dev *gspca_dev, unsigned char *command)
+{
+ int retval;
+
+ memcpy(gspca_dev->usb_buf, command, 2);
+ retval = usb_bulk_msg(gspca_dev->dev,
+ usb_sndbulkpipe(gspca_dev->dev, 3),
+ gspca_dev->usb_buf, 2, NULL, 500);
+ if (retval < 0)
+ pr_err("command write [%02x] error %d\n",
+ gspca_dev->usb_buf[0], retval);
+ return retval;
+}
+
+/* Response to a command is one byte in usb_buf[0], only if requested. */
+static int jl2005c_read1(struct gspca_dev *gspca_dev)
+{
+ int retval;
+
+ retval = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x84),
+ gspca_dev->usb_buf, 1, NULL, 500);
+ if (retval < 0)
+ pr_err("read command [0x%02x] error %d\n",
+ gspca_dev->usb_buf[0], retval);
+ return retval;
+}
+
+/* Response appears in gspca_dev->usb_buf[0] */
+static int jl2005c_read_reg(struct gspca_dev *gspca_dev, unsigned char reg)
+{
+ int retval;
+
+ static u8 instruction[2] = {0x95, 0x00};
+ /* put register to read in byte 1 */
+ instruction[1] = reg;
+ /* Send the read request */
+ retval = jl2005c_write2(gspca_dev, instruction);
+ if (retval < 0)
+ return retval;
+ retval = jl2005c_read1(gspca_dev);
+
+ return retval;
+}
+
+static int jl2005c_start_new_frame(struct gspca_dev *gspca_dev)
+{
+ int i;
+ int retval;
+ int frame_brightness = 0;
+
+ static u8 instruction[2] = {0x7f, 0x01};
+
+ retval = jl2005c_write2(gspca_dev, instruction);
+ if (retval < 0)
+ return retval;
+
+ i = 0;
+ while (i < 20 && !frame_brightness) {
+ /* If we tried 20 times, give up. */
+ retval = jl2005c_read_reg(gspca_dev, 0x7e);
+ if (retval < 0)
+ return retval;
+ frame_brightness = gspca_dev->usb_buf[0];
+ retval = jl2005c_read_reg(gspca_dev, 0x7d);
+ if (retval < 0)
+ return retval;
+ i++;
+ }
+ PDEBUG(D_FRAM, "frame_brightness is 0x%02x", gspca_dev->usb_buf[0]);
+ return retval;
+}
+
+static int jl2005c_write_reg(struct gspca_dev *gspca_dev, unsigned char reg,
+ unsigned char value)
+{
+ int retval;
+ u8 instruction[2];
+
+ instruction[0] = reg;
+ instruction[1] = value;
+
+ retval = jl2005c_write2(gspca_dev, instruction);
+ if (retval < 0)
+ return retval;
+
+ return retval;
+}
+
+static int jl2005c_get_firmware_id(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *)gspca_dev;
+ int i = 0;
+ int retval = -1;
+ unsigned char regs_to_read[] = {0x57, 0x02, 0x03, 0x5d, 0x5e, 0x5f};
+
+ PDEBUG(D_PROBE, "Running jl2005c_get_firmware_id");
+ /* Read the first ID byte once for warmup */
+ retval = jl2005c_read_reg(gspca_dev, regs_to_read[0]);
+ PDEBUG(D_PROBE, "response is %02x", gspca_dev->usb_buf[0]);
+ if (retval < 0)
+ return retval;
+ /* Now actually get the ID string */
+ for (i = 0; i < 6; i++) {
+ retval = jl2005c_read_reg(gspca_dev, regs_to_read[i]);
+ if (retval < 0)
+ return retval;
+ sd->firmware_id[i] = gspca_dev->usb_buf[0];
+ }
+ PDEBUG(D_PROBE, "firmware ID is %02x%02x%02x%02x%02x%02x",
+ sd->firmware_id[0],
+ sd->firmware_id[1],
+ sd->firmware_id[2],
+ sd->firmware_id[3],
+ sd->firmware_id[4],
+ sd->firmware_id[5]);
+ return 0;
+}
+
+static int jl2005c_stream_start_vga_lg
+ (struct gspca_dev *gspca_dev)
+{
+ int i;
+ int retval = -1;
+ static u8 instruction[][2] = {
+ {0x05, 0x00},
+ {0x7c, 0x00},
+ {0x7d, 0x18},
+ {0x02, 0x00},
+ {0x01, 0x00},
+ {0x04, 0x52},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(instruction); i++) {
+ msleep(60);
+ retval = jl2005c_write2(gspca_dev, instruction[i]);
+ if (retval < 0)
+ return retval;
+ }
+ msleep(60);
+ return retval;
+}
+
+static int jl2005c_stream_start_vga_small(struct gspca_dev *gspca_dev)
+{
+ int i;
+ int retval = -1;
+ static u8 instruction[][2] = {
+ {0x06, 0x00},
+ {0x7c, 0x00},
+ {0x7d, 0x1a},
+ {0x02, 0x00},
+ {0x01, 0x00},
+ {0x04, 0x52},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(instruction); i++) {
+ msleep(60);
+ retval = jl2005c_write2(gspca_dev, instruction[i]);
+ if (retval < 0)
+ return retval;
+ }
+ msleep(60);
+ return retval;
+}
+
+static int jl2005c_stream_start_cif_lg(struct gspca_dev *gspca_dev)
+{
+ int i;
+ int retval = -1;
+ static u8 instruction[][2] = {
+ {0x05, 0x00},
+ {0x7c, 0x00},
+ {0x7d, 0x30},
+ {0x02, 0x00},
+ {0x01, 0x00},
+ {0x04, 0x42},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(instruction); i++) {
+ msleep(60);
+ retval = jl2005c_write2(gspca_dev, instruction[i]);
+ if (retval < 0)
+ return retval;
+ }
+ msleep(60);
+ return retval;
+}
+
+static int jl2005c_stream_start_cif_small(struct gspca_dev *gspca_dev)
+{
+ int i;
+ int retval = -1;
+ static u8 instruction[][2] = {
+ {0x06, 0x00},
+ {0x7c, 0x00},
+ {0x7d, 0x32},
+ {0x02, 0x00},
+ {0x01, 0x00},
+ {0x04, 0x42},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(instruction); i++) {
+ msleep(60);
+ retval = jl2005c_write2(gspca_dev, instruction[i]);
+ if (retval < 0)
+ return retval;
+ }
+ msleep(60);
+ return retval;
+}
+
+
+static int jl2005c_stop(struct gspca_dev *gspca_dev)
+{
+ int retval;
+
+ retval = jl2005c_write_reg(gspca_dev, 0x07, 0x00);
+ return retval;
+}
+
+/* This function is called as a workqueue function and runs whenever the camera
+ * is streaming data. Because it is a workqueue function it is allowed to sleep
+ * so we can use synchronous USB calls. To avoid possible collisions with other
+ * threads attempting to use the camera's USB interface the gspca usb_lock is
+ * used when performing the one USB control operation inside the workqueue,
+ * which tells the camera to close the stream. In practice the only thing
+ * which needs to be protected against is the usb_set_interface call that
+ * gspca makes during stream_off. Otherwise the camera doesn't provide any
+ * controls that the user could try to change.
+ */
+static void jl2005c_dostream(struct work_struct *work)
+{
+ struct sd *dev = container_of(work, struct sd, work_struct);
+ struct gspca_dev *gspca_dev = &dev->gspca_dev;
+ int bytes_left = 0; /* bytes remaining in current frame. */
+ int data_len; /* size to use for the next read. */
+ int header_read = 0;
+ unsigned char header_sig[2] = {0x4a, 0x4c};
+ int act_len;
+ int packet_type;
+ int ret;
+ u8 *buffer;
+
+ buffer = kmalloc(JL2005C_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
+ if (!buffer) {
+ pr_err("Couldn't allocate USB buffer\n");
+ goto quit_stream;
+ }
+
+ while (gspca_dev->present && gspca_dev->streaming) {
+ /* Check if this is a new frame. If so, start the frame first */
+ if (!header_read) {
+ mutex_lock(&gspca_dev->usb_lock);
+ ret = jl2005c_start_new_frame(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
+ if (ret < 0)
+ goto quit_stream;
+ ret = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x82),
+ buffer, JL2005C_MAX_TRANSFER, &act_len,
+ JL2005C_DATA_TIMEOUT);
+ PDEBUG(D_PACK,
+ "Got %d bytes out of %d for header",
+ act_len, JL2005C_MAX_TRANSFER);
+ if (ret < 0 || act_len < JL2005C_MAX_TRANSFER)
+ goto quit_stream;
+ /* Check whether we actually got the first blodk */
+ if (memcmp(header_sig, buffer, 2) != 0) {
+ pr_err("First block is not the first block\n");
+ goto quit_stream;
+ }
+ /* total size to fetch is byte 7, times blocksize
+ * of which we already got act_len */
+ bytes_left = buffer[0x07] * dev->block_size - act_len;
+ PDEBUG(D_PACK, "bytes_left = 0x%x", bytes_left);
+ /* We keep the header. It has other information, too.*/
+ packet_type = FIRST_PACKET;
+ gspca_frame_add(gspca_dev, packet_type,
+ buffer, act_len);
+ header_read = 1;
+ }
+ while (bytes_left > 0 && gspca_dev->present) {
+ data_len = bytes_left > JL2005C_MAX_TRANSFER ?
+ JL2005C_MAX_TRANSFER : bytes_left;
+ ret = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x82),
+ buffer, data_len, &act_len,
+ JL2005C_DATA_TIMEOUT);
+ if (ret < 0 || act_len < data_len)
+ goto quit_stream;
+ PDEBUG(D_PACK,
+ "Got %d bytes out of %d for frame",
+ data_len, bytes_left);
+ bytes_left -= data_len;
+ if (bytes_left == 0) {
+ packet_type = LAST_PACKET;
+ header_read = 0;
+ } else
+ packet_type = INTER_PACKET;
+ gspca_frame_add(gspca_dev, packet_type,
+ buffer, data_len);
+ }
+ }
+quit_stream:
+ if (gspca_dev->present) {
+ mutex_lock(&gspca_dev->usb_lock);
+ jl2005c_stop(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
+ }
+ kfree(buffer);
+}
+
+
+
+
+/* This function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct cam *cam;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ cam = &gspca_dev->cam;
+ /* We don't use the buffer gspca allocates so make it small. */
+ cam->bulk_size = 64;
+ cam->bulk = 1;
+ /* For the rest, the camera needs to be detected */
+ jl2005c_get_firmware_id(gspca_dev);
+ /* Here are some known firmware IDs
+ * First some JL2005B cameras
+ * {0x41, 0x07, 0x04, 0x2c, 0xe8, 0xf2} Sakar KidzCam
+ * {0x45, 0x02, 0x08, 0xb9, 0x00, 0xd2} No-name JL2005B
+ * JL2005C cameras
+ * {0x01, 0x0c, 0x16, 0x10, 0xf8, 0xc8} Argus DC-1512
+ * {0x12, 0x04, 0x03, 0xc0, 0x00, 0xd8} ICarly
+ * {0x86, 0x08, 0x05, 0x02, 0x00, 0xd4} Jazz
+ *
+ * Based upon this scanty evidence, we can detect a CIF camera by
+ * testing byte 0 for 0x4x.
+ */
+ if ((sd->firmware_id[0] & 0xf0) == 0x40) {
+ cam->cam_mode = cif_mode;
+ cam->nmodes = ARRAY_SIZE(cif_mode);
+ sd->block_size = 0x80;
+ } else {
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+ sd->block_size = 0x200;
+ }
+
+ INIT_WORK(&sd->work_struct, jl2005c_dostream);
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ return 0;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+
+ struct sd *sd = (struct sd *) gspca_dev;
+ sd->cap_mode = gspca_dev->cam.cam_mode;
+
+ switch (gspca_dev->width) {
+ case 640:
+ PDEBUG(D_STREAM, "Start streaming at vga resolution");
+ jl2005c_stream_start_vga_lg(gspca_dev);
+ break;
+ case 320:
+ PDEBUG(D_STREAM, "Start streaming at qvga resolution");
+ jl2005c_stream_start_vga_small(gspca_dev);
+ break;
+ case 352:
+ PDEBUG(D_STREAM, "Start streaming at cif resolution");
+ jl2005c_stream_start_cif_lg(gspca_dev);
+ break;
+ case 176:
+ PDEBUG(D_STREAM, "Start streaming at qcif resolution");
+ jl2005c_stream_start_cif_small(gspca_dev);
+ break;
+ default:
+ pr_err("Unknown resolution specified\n");
+ return -1;
+ }
+
+ /* Start the workqueue function to do the streaming */
+ sd->work_thread = create_singlethread_workqueue(MODULE_NAME);
+ queue_work(sd->work_thread, &sd->work_struct);
+
+ return 0;
+}
+
+/* called on streamoff with alt==0 and on disconnect */
+/* the usb_lock is held at entry - restore on exit */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ struct sd *dev = (struct sd *) gspca_dev;
+
+ /* wait for the work queue to terminate */
+ mutex_unlock(&gspca_dev->usb_lock);
+ /* This waits for sq905c_dostream to finish */
+ destroy_workqueue(dev->work_thread);
+ dev->work_thread = NULL;
+ mutex_lock(&gspca_dev->usb_lock);
+}
+
+
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ /* .ctrls = none have been detected */
+ /* .nctrls = ARRAY_SIZE(sd_ctrls), */
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stop0 = sd_stop0,
+};
+
+/* -- module initialisation -- */
+static const __devinitdata struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x0979, 0x0227)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+ int ret;
+
+ ret = usb_register(&sd_driver);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+ usb_deregister(&sd_driver);
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/jpeg.h b/drivers/media/video/gspca/jpeg.h
new file mode 100644
index 00000000..ab549104
--- /dev/null
+++ b/drivers/media/video/gspca/jpeg.h
@@ -0,0 +1,168 @@
+#ifndef JPEG_H
+#define JPEG_H 1
+/*
+ * Insert a JPEG header at start of frame
+ *
+ * This module is used by the gspca subdrivers.
+ * A special case is done for Conexant webcams.
+ *
+ * Copyright (C) Jean-Francois Moine (http://moinejf.free.fr)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ * generation options
+ * CONEX_CAM Conexant if present
+ */
+
+/* JPEG header */
+static const u8 jpeg_head[] = {
+ 0xff, 0xd8, /* jpeg */
+
+/* quantization table quality 50% */
+ 0xff, 0xdb, 0x00, 0x84, /* DQT */
+0,
+#define JPEG_QT0_OFFSET 7
+ 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e,
+ 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28,
+ 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25,
+ 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33,
+ 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44,
+ 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57,
+ 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71,
+ 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63,
+1,
+#define JPEG_QT1_OFFSET 72
+ 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a,
+ 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+
+/* huffman table */
+ 0xff, 0xc4, 0x01, 0xa2,
+ 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x01, 0x00, 0x03,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03,
+ 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00,
+ 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04,
+ 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13,
+ 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81,
+ 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15,
+ 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82,
+ 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25,
+ 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36,
+ 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46,
+ 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56,
+ 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66,
+ 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76,
+ 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86,
+ 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95,
+ 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4,
+ 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3,
+ 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2,
+ 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca,
+ 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
+ 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+ 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5,
+ 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x11, 0x00, 0x02,
+ 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05,
+ 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01,
+ 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06,
+ 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22,
+ 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1,
+ 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62,
+ 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25,
+ 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a,
+ 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
+ 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a,
+ 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a,
+ 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,
+ 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
+ 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
+ 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+ 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
+ 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
+ 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
+ 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3,
+ 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2,
+ 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa,
+#ifdef CONEX_CAM
+/* the Conexant frames start with SOF0 */
+#define JPEG_HDR_SZ 556
+#else
+ 0xff, 0xc0, 0x00, 0x11, /* SOF0 (start of frame 0 */
+ 0x08, /* data precision */
+#define JPEG_HEIGHT_OFFSET 561
+ 0x01, 0xe0, /* height */
+ 0x02, 0x80, /* width */
+ 0x03, /* component number */
+ 0x01,
+ 0x21, /* samples Y */
+ 0x00, /* quant Y */
+ 0x02, 0x11, 0x01, /* samples CbCr - quant CbCr */
+ 0x03, 0x11, 0x01,
+
+ 0xff, 0xda, 0x00, 0x0c, /* SOS (start of scan) */
+ 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00
+#define JPEG_HDR_SZ 589
+#endif
+};
+
+/* define the JPEG header */
+static void jpeg_define(u8 *jpeg_hdr,
+ int height,
+ int width,
+ int samplesY)
+{
+ memcpy(jpeg_hdr, jpeg_head, sizeof jpeg_head);
+#ifndef CONEX_CAM
+ jpeg_hdr[JPEG_HEIGHT_OFFSET + 0] = height >> 8;
+ jpeg_hdr[JPEG_HEIGHT_OFFSET + 1] = height;
+ jpeg_hdr[JPEG_HEIGHT_OFFSET + 2] = width >> 8;
+ jpeg_hdr[JPEG_HEIGHT_OFFSET + 3] = width;
+ jpeg_hdr[JPEG_HEIGHT_OFFSET + 6] = samplesY;
+#endif
+}
+
+/* set the JPEG quality */
+static void jpeg_set_qual(u8 *jpeg_hdr,
+ int quality)
+{
+ int i, sc;
+
+ if (quality < 50)
+ sc = 5000 / quality;
+ else
+ sc = 200 - quality * 2;
+ for (i = 0; i < 64; i++) {
+ jpeg_hdr[JPEG_QT0_OFFSET + i] =
+ (jpeg_head[JPEG_QT0_OFFSET + i] * sc + 50) / 100;
+ jpeg_hdr[JPEG_QT1_OFFSET + i] =
+ (jpeg_head[JPEG_QT1_OFFSET + i] * sc + 50) / 100;
+ }
+}
+#endif
diff --git a/drivers/media/video/gspca/kinect.c b/drivers/media/video/gspca/kinect.c
new file mode 100644
index 00000000..e8e8f2fe
--- /dev/null
+++ b/drivers/media/video/gspca/kinect.c
@@ -0,0 +1,416 @@
+/*
+ * kinect sensor device camera, gspca driver
+ *
+ * Copyright (C) 2011 Antonio Ospite <ospite@studenti.unina.it>
+ *
+ * Based on the OpenKinect project and libfreenect
+ * http://openkinect.org/wiki/Init_Analysis
+ *
+ * Special thanks to Steven Toth and kernellabs.com for sponsoring a Kinect
+ * sensor device which I tested the driver on.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "kinect"
+
+#include "gspca.h"
+
+#define CTRL_TIMEOUT 500
+
+MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>");
+MODULE_DESCRIPTION("GSPCA/Kinect Sensor Device USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+struct pkt_hdr {
+ uint8_t magic[2];
+ uint8_t pad;
+ uint8_t flag;
+ uint8_t unk1;
+ uint8_t seq;
+ uint8_t unk2;
+ uint8_t unk3;
+ uint32_t timestamp;
+};
+
+struct cam_hdr {
+ uint8_t magic[2];
+ uint16_t len;
+ uint16_t cmd;
+ uint16_t tag;
+};
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ uint16_t cam_tag; /* a sequence number for packets */
+ uint8_t stream_flag; /* to identify different stream types */
+ uint8_t obuf[0x400]; /* output buffer for control commands */
+ uint8_t ibuf[0x200]; /* input buffer for control commands */
+};
+
+/* V4L2 controls supported by the driver */
+/* controls prototypes here */
+
+static const struct ctrl sd_ctrls[] = {
+};
+
+#define MODE_640x480 0x0001
+#define MODE_640x488 0x0002
+#define MODE_1280x1024 0x0004
+
+#define FORMAT_BAYER 0x0010
+#define FORMAT_UYVY 0x0020
+#define FORMAT_Y10B 0x0040
+
+#define FPS_HIGH 0x0100
+
+static const struct v4l2_pix_format video_camera_mode[] = {
+ {640, 480, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = MODE_640x480 | FORMAT_BAYER | FPS_HIGH},
+ {640, 480, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE,
+ .bytesperline = 640 * 2,
+ .sizeimage = 640 * 480 * 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = MODE_640x480 | FORMAT_UYVY},
+ {1280, 1024, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+ .bytesperline = 1280,
+ .sizeimage = 1280 * 1024,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = MODE_1280x1024 | FORMAT_BAYER},
+ {640, 488, V4L2_PIX_FMT_Y10BPACK, V4L2_FIELD_NONE,
+ .bytesperline = 640 * 10 / 8,
+ .sizeimage = 640 * 488 * 10 / 8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = MODE_640x488 | FORMAT_Y10B | FPS_HIGH},
+ {1280, 1024, V4L2_PIX_FMT_Y10BPACK, V4L2_FIELD_NONE,
+ .bytesperline = 1280 * 10 / 8,
+ .sizeimage = 1280 * 1024 * 10 / 8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = MODE_1280x1024 | FORMAT_Y10B},
+};
+
+static int kinect_write(struct usb_device *udev, uint8_t *data,
+ uint16_t wLength)
+{
+ return usb_control_msg(udev,
+ usb_sndctrlpipe(udev, 0),
+ 0x00,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, 0, data, wLength, CTRL_TIMEOUT);
+}
+
+static int kinect_read(struct usb_device *udev, uint8_t *data, uint16_t wLength)
+{
+ return usb_control_msg(udev,
+ usb_rcvctrlpipe(udev, 0),
+ 0x00,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, 0, data, wLength, CTRL_TIMEOUT);
+}
+
+static int send_cmd(struct gspca_dev *gspca_dev, uint16_t cmd, void *cmdbuf,
+ unsigned int cmd_len, void *replybuf, unsigned int reply_len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct usb_device *udev = gspca_dev->dev;
+ int res, actual_len;
+ uint8_t *obuf = sd->obuf;
+ uint8_t *ibuf = sd->ibuf;
+ struct cam_hdr *chdr = (void *)obuf;
+ struct cam_hdr *rhdr = (void *)ibuf;
+
+ if (cmd_len & 1 || cmd_len > (0x400 - sizeof(*chdr))) {
+ pr_err("send_cmd: Invalid command length (0x%x)\n", cmd_len);
+ return -1;
+ }
+
+ chdr->magic[0] = 0x47;
+ chdr->magic[1] = 0x4d;
+ chdr->cmd = cpu_to_le16(cmd);
+ chdr->tag = cpu_to_le16(sd->cam_tag);
+ chdr->len = cpu_to_le16(cmd_len / 2);
+
+ memcpy(obuf+sizeof(*chdr), cmdbuf, cmd_len);
+
+ res = kinect_write(udev, obuf, cmd_len + sizeof(*chdr));
+ PDEBUG(D_USBO, "Control cmd=%04x tag=%04x len=%04x: %d", cmd,
+ sd->cam_tag, cmd_len, res);
+ if (res < 0) {
+ pr_err("send_cmd: Output control transfer failed (%d)\n", res);
+ return res;
+ }
+
+ do {
+ actual_len = kinect_read(udev, ibuf, 0x200);
+ } while (actual_len == 0);
+ PDEBUG(D_USBO, "Control reply: %d", res);
+ if (actual_len < sizeof(*rhdr)) {
+ pr_err("send_cmd: Input control transfer failed (%d)\n", res);
+ return res;
+ }
+ actual_len -= sizeof(*rhdr);
+
+ if (rhdr->magic[0] != 0x52 || rhdr->magic[1] != 0x42) {
+ pr_err("send_cmd: Bad magic %02x %02x\n",
+ rhdr->magic[0], rhdr->magic[1]);
+ return -1;
+ }
+ if (rhdr->cmd != chdr->cmd) {
+ pr_err("send_cmd: Bad cmd %02x != %02x\n",
+ rhdr->cmd, chdr->cmd);
+ return -1;
+ }
+ if (rhdr->tag != chdr->tag) {
+ pr_err("send_cmd: Bad tag %04x != %04x\n",
+ rhdr->tag, chdr->tag);
+ return -1;
+ }
+ if (cpu_to_le16(rhdr->len) != (actual_len/2)) {
+ pr_err("send_cmd: Bad len %04x != %04x\n",
+ cpu_to_le16(rhdr->len), (int)(actual_len/2));
+ return -1;
+ }
+
+ if (actual_len > reply_len) {
+ pr_warn("send_cmd: Data buffer is %d bytes long, but got %d bytes\n",
+ reply_len, actual_len);
+ memcpy(replybuf, ibuf+sizeof(*rhdr), reply_len);
+ } else {
+ memcpy(replybuf, ibuf+sizeof(*rhdr), actual_len);
+ }
+
+ sd->cam_tag++;
+
+ return actual_len;
+}
+
+static int write_register(struct gspca_dev *gspca_dev, uint16_t reg,
+ uint16_t data)
+{
+ uint16_t reply[2];
+ uint16_t cmd[2];
+ int res;
+
+ cmd[0] = cpu_to_le16(reg);
+ cmd[1] = cpu_to_le16(data);
+
+ PDEBUG(D_USBO, "Write Reg 0x%04x <= 0x%02x", reg, data);
+ res = send_cmd(gspca_dev, 0x03, cmd, 4, reply, 4);
+ if (res < 0)
+ return res;
+ if (res != 2) {
+ pr_warn("send_cmd returned %d [%04x %04x], 0000 expected\n",
+ res, reply[0], reply[1]);
+ }
+ return 0;
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+ sd->cam_tag = 0;
+
+ /* Only video stream is supported for now,
+ * which has stream flag = 0x80 */
+ sd->stream_flag = 0x80;
+
+ cam = &gspca_dev->cam;
+
+ cam->cam_mode = video_camera_mode;
+ cam->nmodes = ARRAY_SIZE(video_camera_mode);
+
+#if 0
+ /* Setting those values is not needed for video stream */
+ cam->npkt = 15;
+ gspca_dev->pkt_size = 960 * 2;
+#endif
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ PDEBUG(D_PROBE, "Kinect Camera device.");
+
+ return 0;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ int mode;
+ uint8_t fmt_reg, fmt_val;
+ uint8_t res_reg, res_val;
+ uint8_t fps_reg, fps_val;
+ uint8_t mode_val;
+
+ mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+
+ if (mode & FORMAT_Y10B) {
+ fmt_reg = 0x19;
+ res_reg = 0x1a;
+ fps_reg = 0x1b;
+ mode_val = 0x03;
+ } else {
+ fmt_reg = 0x0c;
+ res_reg = 0x0d;
+ fps_reg = 0x0e;
+ mode_val = 0x01;
+ }
+
+ /* format */
+ if (mode & FORMAT_UYVY)
+ fmt_val = 0x05;
+ else
+ fmt_val = 0x00;
+
+ if (mode & MODE_1280x1024)
+ res_val = 0x02;
+ else
+ res_val = 0x01;
+
+ if (mode & FPS_HIGH)
+ fps_val = 0x1e;
+ else
+ fps_val = 0x0f;
+
+
+ /* turn off IR-reset function */
+ write_register(gspca_dev, 0x105, 0x00);
+
+ /* Reset video stream */
+ write_register(gspca_dev, 0x05, 0x00);
+
+ /* Due to some ridiculous condition in the firmware, we have to start
+ * and stop the depth stream before the camera will hand us 1280x1024
+ * IR. This is a stupid workaround, but we've yet to find a better
+ * solution.
+ *
+ * Thanks to Drew Fisher for figuring this out.
+ */
+ if (mode & (FORMAT_Y10B | MODE_1280x1024)) {
+ write_register(gspca_dev, 0x13, 0x01);
+ write_register(gspca_dev, 0x14, 0x1e);
+ write_register(gspca_dev, 0x06, 0x02);
+ write_register(gspca_dev, 0x06, 0x00);
+ }
+
+ write_register(gspca_dev, fmt_reg, fmt_val);
+ write_register(gspca_dev, res_reg, res_val);
+ write_register(gspca_dev, fps_reg, fps_val);
+
+ /* Start video stream */
+ write_register(gspca_dev, 0x05, mode_val);
+
+ /* disable Hflip */
+ write_register(gspca_dev, 0x47, 0x00);
+
+ return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ /* reset video stream */
+ write_register(gspca_dev, 0x05, 0x00);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *__data, int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ struct pkt_hdr *hdr = (void *)__data;
+ uint8_t *data = __data + sizeof(*hdr);
+ int datalen = len - sizeof(*hdr);
+
+ uint8_t sof = sd->stream_flag | 1;
+ uint8_t mof = sd->stream_flag | 2;
+ uint8_t eof = sd->stream_flag | 5;
+
+ if (len < 12)
+ return;
+
+ if (hdr->magic[0] != 'R' || hdr->magic[1] != 'B') {
+ pr_warn("[Stream %02x] Invalid magic %02x%02x\n",
+ sd->stream_flag, hdr->magic[0], hdr->magic[1]);
+ return;
+ }
+
+ if (hdr->flag == sof)
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, datalen);
+
+ else if (hdr->flag == mof)
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, datalen);
+
+ else if (hdr->flag == eof)
+ gspca_frame_add(gspca_dev, LAST_PACKET, data, datalen);
+
+ else
+ pr_warn("Packet type not recognized...\n");
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+ /*
+ .querymenu = sd_querymenu,
+ .get_streamparm = sd_get_streamparm,
+ .set_streamparm = sd_set_streamparm,
+ */
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x045e, 0x02ae)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/konica.c b/drivers/media/video/gspca/konica.c
new file mode 100644
index 00000000..f0c0d74d
--- /dev/null
+++ b/drivers/media/video/gspca/konica.c
@@ -0,0 +1,634 @@
+/*
+ * Driver for USB webcams based on Konica chipset. This
+ * chipset is used in Intel YC76 camera.
+ *
+ * Copyright (C) 2010 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Based on the usbvideo v4l1 konicawc driver which is:
+ *
+ * Copyright (C) 2002 Simon Evans <spse@secret.org.uk>
+ *
+ * The code for making gspca work with a webcam with 2 isoc endpoints was
+ * taken from the benq gspca subdriver which is:
+ *
+ * Copyright (C) 2009 Jean-Francois Moine (http://moinejf.free.fr)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "konica"
+
+#include <linux/input.h>
+#include "gspca.h"
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Konica chipset USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+#define WHITEBAL_REG 0x01
+#define BRIGHTNESS_REG 0x02
+#define SHARPNESS_REG 0x03
+#define CONTRAST_REG 0x04
+#define SATURATION_REG 0x05
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ struct urb *last_data_urb;
+ u8 snapshot_pressed;
+ u8 brightness;
+ u8 contrast;
+ u8 saturation;
+ u8 whitebal;
+ u8 sharpness;
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setsaturation(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setwhitebal(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getwhitebal(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
+
+static const struct ctrl sd_ctrls[] = {
+#define SD_BRIGHTNESS 0
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 9,
+ .step = 1,
+#define BRIGHTNESS_DEFAULT 4
+ .default_value = BRIGHTNESS_DEFAULT,
+ .flags = 0,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+#define SD_CONTRAST 1
+ {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 9,
+ .step = 4,
+#define CONTRAST_DEFAULT 10
+ .default_value = CONTRAST_DEFAULT,
+ .flags = 0,
+ },
+ .set = sd_setcontrast,
+ .get = sd_getcontrast,
+ },
+#define SD_SATURATION 2
+ {
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 9,
+ .step = 1,
+#define SATURATION_DEFAULT 4
+ .default_value = SATURATION_DEFAULT,
+ .flags = 0,
+ },
+ .set = sd_setsaturation,
+ .get = sd_getsaturation,
+ },
+#define SD_WHITEBAL 3
+ {
+ {
+ .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "White Balance",
+ .minimum = 0,
+ .maximum = 33,
+ .step = 1,
+#define WHITEBAL_DEFAULT 25
+ .default_value = WHITEBAL_DEFAULT,
+ .flags = 0,
+ },
+ .set = sd_setwhitebal,
+ .get = sd_getwhitebal,
+ },
+#define SD_SHARPNESS 4
+ {
+ {
+ .id = V4L2_CID_SHARPNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Sharpness",
+ .minimum = 0,
+ .maximum = 9,
+ .step = 1,
+#define SHARPNESS_DEFAULT 4
+ .default_value = SHARPNESS_DEFAULT,
+ .flags = 0,
+ },
+ .set = sd_setsharpness,
+ .get = sd_getsharpness,
+ },
+};
+
+/* .priv is what goes to register 8 for this mode, known working values:
+ 0x00 -> 176x144, cropped
+ 0x01 -> 176x144, cropped
+ 0x02 -> 176x144, cropped
+ 0x03 -> 176x144, cropped
+ 0x04 -> 176x144, binned
+ 0x05 -> 320x240
+ 0x06 -> 320x240
+ 0x07 -> 160x120, cropped
+ 0x08 -> 160x120, cropped
+ 0x09 -> 160x120, binned (note has 136 lines)
+ 0x0a -> 160x120, binned (note has 136 lines)
+ 0x0b -> 160x120, cropped
+*/
+static const struct v4l2_pix_format vga_mode[] = {
+ {160, 120, V4L2_PIX_FMT_KONICA420, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 136 * 3 / 2 + 960,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0x0a},
+ {176, 144, V4L2_PIX_FMT_KONICA420, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144 * 3 / 2 + 960,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0x04},
+ {320, 240, V4L2_PIX_FMT_KONICA420, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 2 + 960,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0x05},
+};
+
+static void sd_isoc_irq(struct urb *urb);
+
+static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x02,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value,
+ index,
+ NULL,
+ 0,
+ 1000);
+ if (ret < 0) {
+ pr_err("reg_w err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static void reg_r(struct gspca_dev *gspca_dev, u16 value, u16 index)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ 0x03,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value,
+ index,
+ gspca_dev->usb_buf,
+ 2,
+ 1000);
+ if (ret < 0) {
+ pr_err("reg_w err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static void konica_stream_on(struct gspca_dev *gspca_dev)
+{
+ reg_w(gspca_dev, 1, 0x0b);
+}
+
+static void konica_stream_off(struct gspca_dev *gspca_dev)
+{
+ reg_w(gspca_dev, 0, 0x0b);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ gspca_dev->cam.cam_mode = vga_mode;
+ gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
+ gspca_dev->cam.no_urb_create = 1;
+
+ sd->brightness = BRIGHTNESS_DEFAULT;
+ sd->contrast = CONTRAST_DEFAULT;
+ sd->saturation = SATURATION_DEFAULT;
+ sd->whitebal = WHITEBAL_DEFAULT;
+ sd->sharpness = SHARPNESS_DEFAULT;
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ /* HDG not sure if these 2 reads are needed */
+ reg_r(gspca_dev, 0, 0x10);
+ PDEBUG(D_PROBE, "Reg 0x10 reads: %02x %02x",
+ gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]);
+ reg_r(gspca_dev, 0, 0x10);
+ PDEBUG(D_PROBE, "Reg 0x10 reads: %02x %02x",
+ gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]);
+ reg_w(gspca_dev, 0, 0x0d);
+
+ return 0;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct urb *urb;
+ int i, n, packet_size;
+ struct usb_host_interface *alt;
+ struct usb_interface *intf;
+
+ intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
+ alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
+ if (!alt) {
+ pr_err("Couldn't get altsetting\n");
+ return -EIO;
+ }
+
+ packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+
+ reg_w(gspca_dev, sd->brightness, BRIGHTNESS_REG);
+ reg_w(gspca_dev, sd->whitebal, WHITEBAL_REG);
+ reg_w(gspca_dev, sd->contrast, CONTRAST_REG);
+ reg_w(gspca_dev, sd->saturation, SATURATION_REG);
+ reg_w(gspca_dev, sd->sharpness, SHARPNESS_REG);
+
+ n = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+ reg_w(gspca_dev, n, 0x08);
+
+ konica_stream_on(gspca_dev);
+
+ if (gspca_dev->usb_err)
+ return gspca_dev->usb_err;
+
+ /* create 4 URBs - 2 on endpoint 0x83 and 2 on 0x082 */
+#if MAX_NURBS < 4
+#error "Not enough URBs in the gspca table"
+#endif
+#define SD_NPKT 32
+ for (n = 0; n < 4; n++) {
+ i = n & 1 ? 0 : 1;
+ packet_size =
+ le16_to_cpu(alt->endpoint[i].desc.wMaxPacketSize);
+ urb = usb_alloc_urb(SD_NPKT, GFP_KERNEL);
+ if (!urb) {
+ pr_err("usb_alloc_urb failed\n");
+ return -ENOMEM;
+ }
+ gspca_dev->urb[n] = urb;
+ urb->transfer_buffer = usb_alloc_coherent(gspca_dev->dev,
+ packet_size * SD_NPKT,
+ GFP_KERNEL,
+ &urb->transfer_dma);
+ if (urb->transfer_buffer == NULL) {
+ pr_err("usb_buffer_alloc failed\n");
+ return -ENOMEM;
+ }
+
+ urb->dev = gspca_dev->dev;
+ urb->context = gspca_dev;
+ urb->transfer_buffer_length = packet_size * SD_NPKT;
+ urb->pipe = usb_rcvisocpipe(gspca_dev->dev,
+ n & 1 ? 0x81 : 0x82);
+ urb->transfer_flags = URB_ISO_ASAP
+ | URB_NO_TRANSFER_DMA_MAP;
+ urb->interval = 1;
+ urb->complete = sd_isoc_irq;
+ urb->number_of_packets = SD_NPKT;
+ for (i = 0; i < SD_NPKT; i++) {
+ urb->iso_frame_desc[i].length = packet_size;
+ urb->iso_frame_desc[i].offset = packet_size * i;
+ }
+ }
+
+ return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ konica_stream_off(gspca_dev);
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ /* Don't keep the button in the pressed state "forever" if it was
+ pressed when streaming is stopped */
+ if (sd->snapshot_pressed) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ sd->snapshot_pressed = 0;
+ }
+#endif
+}
+
+/* reception of an URB */
+static void sd_isoc_irq(struct urb *urb)
+{
+ struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct urb *data_urb, *status_urb;
+ u8 *data;
+ int i, st;
+
+ PDEBUG(D_PACK, "sd isoc irq");
+ if (!gspca_dev->streaming)
+ return;
+
+ if (urb->status != 0) {
+ if (urb->status == -ESHUTDOWN)
+ return; /* disconnection */
+#ifdef CONFIG_PM
+ if (gspca_dev->frozen)
+ return;
+#endif
+ PDEBUG(D_ERR, "urb status: %d", urb->status);
+ st = usb_submit_urb(urb, GFP_ATOMIC);
+ if (st < 0)
+ pr_err("resubmit urb error %d\n", st);
+ return;
+ }
+
+ /* if this is a data URB (ep 0x82), wait */
+ if (urb->transfer_buffer_length > 32) {
+ sd->last_data_urb = urb;
+ return;
+ }
+
+ status_urb = urb;
+ data_urb = sd->last_data_urb;
+ sd->last_data_urb = NULL;
+
+ if (!data_urb || data_urb->start_frame != status_urb->start_frame) {
+ PDEBUG(D_ERR|D_PACK, "lost sync on frames");
+ goto resubmit;
+ }
+
+ if (data_urb->number_of_packets != status_urb->number_of_packets) {
+ PDEBUG(D_ERR|D_PACK,
+ "no packets does not match, data: %d, status: %d",
+ data_urb->number_of_packets,
+ status_urb->number_of_packets);
+ goto resubmit;
+ }
+
+ for (i = 0; i < status_urb->number_of_packets; i++) {
+ if (data_urb->iso_frame_desc[i].status ||
+ status_urb->iso_frame_desc[i].status) {
+ PDEBUG(D_ERR|D_PACK,
+ "pkt %d data-status %d, status-status %d", i,
+ data_urb->iso_frame_desc[i].status,
+ status_urb->iso_frame_desc[i].status);
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ continue;
+ }
+
+ if (status_urb->iso_frame_desc[i].actual_length != 1) {
+ PDEBUG(D_ERR|D_PACK,
+ "bad status packet length %d",
+ status_urb->iso_frame_desc[i].actual_length);
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ continue;
+ }
+
+ st = *((u8 *)status_urb->transfer_buffer
+ + status_urb->iso_frame_desc[i].offset);
+
+ data = (u8 *)data_urb->transfer_buffer
+ + data_urb->iso_frame_desc[i].offset;
+
+ /* st: 0x80-0xff: frame start with frame number (ie 0-7f)
+ * otherwise:
+ * bit 0 0: keep packet
+ * 1: drop packet (padding data)
+ *
+ * bit 4 0 button not clicked
+ * 1 button clicked
+ * button is used to `take a picture' (in software)
+ */
+ if (st & 0x80) {
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+ gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+ } else {
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ u8 button_state = st & 0x40 ? 1 : 0;
+ if (sd->snapshot_pressed != button_state) {
+ input_report_key(gspca_dev->input_dev,
+ KEY_CAMERA,
+ button_state);
+ input_sync(gspca_dev->input_dev);
+ sd->snapshot_pressed = button_state;
+ }
+#endif
+ if (st & 0x01)
+ continue;
+ }
+ gspca_frame_add(gspca_dev, INTER_PACKET, data,
+ data_urb->iso_frame_desc[i].actual_length);
+ }
+
+resubmit:
+ if (data_urb) {
+ st = usb_submit_urb(data_urb, GFP_ATOMIC);
+ if (st < 0)
+ PDEBUG(D_ERR|D_PACK,
+ "usb_submit_urb(data_urb) ret %d", st);
+ }
+ st = usb_submit_urb(status_urb, GFP_ATOMIC);
+ if (st < 0)
+ pr_err("usb_submit_urb(status_urb) ret %d\n", st);
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->brightness = val;
+ if (gspca_dev->streaming) {
+ konica_stream_off(gspca_dev);
+ reg_w(gspca_dev, sd->brightness, BRIGHTNESS_REG);
+ konica_stream_on(gspca_dev);
+ }
+
+ return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->brightness;
+
+ return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->contrast = val;
+ if (gspca_dev->streaming) {
+ konica_stream_off(gspca_dev);
+ reg_w(gspca_dev, sd->contrast, CONTRAST_REG);
+ konica_stream_on(gspca_dev);
+ }
+
+ return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->contrast;
+
+ return 0;
+}
+
+static int sd_setsaturation(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->saturation = val;
+ if (gspca_dev->streaming) {
+ konica_stream_off(gspca_dev);
+ reg_w(gspca_dev, sd->saturation, SATURATION_REG);
+ konica_stream_on(gspca_dev);
+ }
+ return 0;
+}
+
+static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->saturation;
+
+ return 0;
+}
+
+static int sd_setwhitebal(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->whitebal = val;
+ if (gspca_dev->streaming) {
+ konica_stream_off(gspca_dev);
+ reg_w(gspca_dev, sd->whitebal, WHITEBAL_REG);
+ konica_stream_on(gspca_dev);
+ }
+ return 0;
+}
+
+static int sd_getwhitebal(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->whitebal;
+
+ return 0;
+}
+
+static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->sharpness = val;
+ if (gspca_dev->streaming) {
+ konica_stream_off(gspca_dev);
+ reg_w(gspca_dev, sd->sharpness, SHARPNESS_REG);
+ konica_stream_on(gspca_dev);
+ }
+ return 0;
+}
+
+static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->sharpness;
+
+ return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ .other_input = 1,
+#endif
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x04c8, 0x0720)}, /* Intel YC 76 */
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/m5602/Kconfig b/drivers/media/video/gspca/m5602/Kconfig
new file mode 100644
index 00000000..5a69016e
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/Kconfig
@@ -0,0 +1,11 @@
+config USB_M5602
+ tristate "ALi USB m5602 Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the
+ ALi m5602 connected to various image sensors.
+
+ See <file:Documentation/video4linux/m5602.txt> for more info.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_m5602.
diff --git a/drivers/media/video/gspca/m5602/Makefile b/drivers/media/video/gspca/m5602/Makefile
new file mode 100644
index 00000000..575b75ba
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/Makefile
@@ -0,0 +1,11 @@
+obj-$(CONFIG_USB_M5602) += gspca_m5602.o
+
+gspca_m5602-objs := m5602_core.o \
+ m5602_ov9650.o \
+ m5602_ov7660.o \
+ m5602_mt9m111.o \
+ m5602_po1030.o \
+ m5602_s5k83a.o \
+ m5602_s5k4aa.o
+
+ccflags-y += -I$(srctree)/drivers/media/video/gspca
diff --git a/drivers/media/video/gspca/m5602/m5602_bridge.h b/drivers/media/video/gspca/m5602/m5602_bridge.h
new file mode 100644
index 00000000..51af3ee3
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/m5602_bridge.h
@@ -0,0 +1,163 @@
+/*
+ * USB Driver for ALi m5602 based webcams
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef M5602_BRIDGE_H_
+#define M5602_BRIDGE_H_
+
+#include <linux/slab.h>
+#include "gspca.h"
+
+#define MODULE_NAME "ALi m5602"
+
+/*****************************************************************************/
+
+#define M5602_XB_SENSOR_TYPE 0x00
+#define M5602_XB_SENSOR_CTRL 0x01
+#define M5602_XB_LINE_OF_FRAME_H 0x02
+#define M5602_XB_LINE_OF_FRAME_L 0x03
+#define M5602_XB_PIX_OF_LINE_H 0x04
+#define M5602_XB_PIX_OF_LINE_L 0x05
+#define M5602_XB_VSYNC_PARA 0x06
+#define M5602_XB_HSYNC_PARA 0x07
+#define M5602_XB_TEST_MODE_1 0x08
+#define M5602_XB_TEST_MODE_2 0x09
+#define M5602_XB_SIG_INI 0x0a
+#define M5602_XB_DS_PARA 0x0e
+#define M5602_XB_TRIG_PARA 0x0f
+#define M5602_XB_CLK_PD 0x10
+#define M5602_XB_MCU_CLK_CTRL 0x12
+#define M5602_XB_MCU_CLK_DIV 0x13
+#define M5602_XB_SEN_CLK_CTRL 0x14
+#define M5602_XB_SEN_CLK_DIV 0x15
+#define M5602_XB_AUD_CLK_CTRL 0x16
+#define M5602_XB_AUD_CLK_DIV 0x17
+#define M5602_OB_AC_LINK_STATE 0x22
+#define M5602_OB_PCM_SLOT_INDEX 0x24
+#define M5602_OB_GPIO_SLOT_INDEX 0x25
+#define M5602_OB_ACRX_STATUS_ADDRESS_H 0x28
+#define M5602_OB_ACRX_STATUS_DATA_L 0x29
+#define M5602_OB_ACRX_STATUS_DATA_H 0x2a
+#define M5602_OB_ACTX_COMMAND_ADDRESS 0x31
+#define M5602_OB_ACRX_COMMAND_DATA_L 0x32
+#define M5602_OB_ACTX_COMMAND_DATA_H 0X33
+#define M5602_XB_DEVCTR1 0x41
+#define M5602_XB_EPSETR0 0x42
+#define M5602_XB_EPAFCTR 0x47
+#define M5602_XB_EPBFCTR 0x49
+#define M5602_XB_EPEFCTR 0x4f
+#define M5602_XB_TEST_REG 0x53
+#define M5602_XB_ALT2SIZE 0x54
+#define M5602_XB_ALT3SIZE 0x55
+#define M5602_XB_OBSFRAME 0x56
+#define M5602_XB_PWR_CTL 0x59
+#define M5602_XB_ADC_CTRL 0x60
+#define M5602_XB_ADC_DATA 0x61
+#define M5602_XB_MISC_CTRL 0x62
+#define M5602_XB_SNAPSHOT 0x63
+#define M5602_XB_SCRATCH_1 0x64
+#define M5602_XB_SCRATCH_2 0x65
+#define M5602_XB_SCRATCH_3 0x66
+#define M5602_XB_SCRATCH_4 0x67
+#define M5602_XB_I2C_CTRL 0x68
+#define M5602_XB_I2C_CLK_DIV 0x69
+#define M5602_XB_I2C_DEV_ADDR 0x6a
+#define M5602_XB_I2C_REG_ADDR 0x6b
+#define M5602_XB_I2C_DATA 0x6c
+#define M5602_XB_I2C_STATUS 0x6d
+#define M5602_XB_GPIO_DAT_H 0x70
+#define M5602_XB_GPIO_DAT_L 0x71
+#define M5602_XB_GPIO_DIR_H 0x72
+#define M5602_XB_GPIO_DIR_L 0x73
+#define M5602_XB_GPIO_EN_H 0x74
+#define M5602_XB_GPIO_EN_L 0x75
+#define M5602_XB_GPIO_DAT 0x76
+#define M5602_XB_GPIO_DIR 0x77
+#define M5602_XB_SEN_CLK_CONTROL 0x80
+#define M5602_XB_SEN_CLK_DIVISION 0x81
+#define M5602_XB_CPR_CLK_CONTROL 0x82
+#define M5602_XB_CPR_CLK_DIVISION 0x83
+#define M5602_XB_MCU_CLK_CONTROL 0x84
+#define M5602_XB_MCU_CLK_DIVISION 0x85
+#define M5602_XB_DCT_CLK_CONTROL 0x86
+#define M5602_XB_DCT_CLK_DIVISION 0x87
+#define M5602_XB_EC_CLK_CONTROL 0x88
+#define M5602_XB_EC_CLK_DIVISION 0x89
+#define M5602_XB_LBUF_CLK_CONTROL 0x8a
+#define M5602_XB_LBUF_CLK_DIVISION 0x8b
+
+#define I2C_BUSY 0x80
+
+/*****************************************************************************/
+
+/* Driver info */
+#define DRIVER_AUTHOR "ALi m5602 Linux Driver Project"
+#define DRIVER_DESC "ALi m5602 webcam driver"
+
+#define M5602_ISOC_ENDPOINT_ADDR 0x81
+#define M5602_INTR_ENDPOINT_ADDR 0x82
+
+#define M5602_URB_MSG_TIMEOUT 5000
+
+/*****************************************************************************/
+
+/* A skeleton used for sending messages to the m5602 bridge */
+static const unsigned char bridge_urb_skeleton[] = {
+ 0x13, 0x00, 0x81, 0x00
+};
+
+/* A skeleton used for sending messages to the sensor */
+static const unsigned char sensor_urb_skeleton[] = {
+ 0x23, M5602_XB_GPIO_EN_H, 0x81, 0x06,
+ 0x23, M5602_XB_MISC_CTRL, 0x81, 0x80,
+ 0x13, M5602_XB_I2C_DEV_ADDR, 0x81, 0x00,
+ 0x13, M5602_XB_I2C_REG_ADDR, 0x81, 0x00,
+ 0x13, M5602_XB_I2C_DATA, 0x81, 0x00,
+ 0x13, M5602_XB_I2C_CTRL, 0x81, 0x11
+};
+
+struct sd {
+ struct gspca_dev gspca_dev;
+
+ /* A pointer to the currently connected sensor */
+ const struct m5602_sensor *sensor;
+
+ struct sd_desc *desc;
+
+ /* Sensor private data */
+ void *sensor_priv;
+
+ /* The current frame's id, used to detect frame boundaries */
+ u8 frame_id;
+
+ /* The current frame count */
+ u32 frame_count;
+};
+
+int m5602_read_bridge(
+ struct sd *sd, const u8 address, u8 *i2c_data);
+
+int m5602_write_bridge(
+ struct sd *sd, const u8 address, const u8 i2c_data);
+
+int m5602_write_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+int m5602_read_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+#endif
diff --git a/drivers/media/video/gspca/m5602/m5602_core.c b/drivers/media/video/gspca/m5602/m5602_core.c
new file mode 100644
index 00000000..0c449367
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/m5602_core.c
@@ -0,0 +1,423 @@
+ /*
+ * USB Driver for ALi m5602 based webcams
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "m5602_ov9650.h"
+#include "m5602_ov7660.h"
+#include "m5602_mt9m111.h"
+#include "m5602_po1030.h"
+#include "m5602_s5k83a.h"
+#include "m5602_s5k4aa.h"
+
+/* Kernel module parameters */
+int force_sensor;
+static bool dump_bridge;
+bool dump_sensor;
+
+static const struct usb_device_id m5602_table[] = {
+ {USB_DEVICE(0x0402, 0x5602)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, m5602_table);
+
+/* Reads a byte from the m5602 */
+int m5602_read_bridge(struct sd *sd, const u8 address, u8 *i2c_data)
+{
+ int err;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ 0x04, 0xc0, 0x14,
+ 0x8100 + address, buf,
+ 1, M5602_URB_MSG_TIMEOUT);
+ *i2c_data = buf[0];
+
+ PDEBUG(D_CONF, "Reading bridge register 0x%x containing 0x%x",
+ address, *i2c_data);
+
+ /* usb_control_msg(...) returns the number of bytes sent upon success,
+ mask that and return zero instead*/
+ return (err < 0) ? err : 0;
+}
+
+/* Writes a byte to the m5602 */
+int m5602_write_bridge(struct sd *sd, const u8 address, const u8 i2c_data)
+{
+ int err;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ PDEBUG(D_CONF, "Writing bridge register 0x%x with 0x%x",
+ address, i2c_data);
+
+ memcpy(buf, bridge_urb_skeleton,
+ sizeof(bridge_urb_skeleton));
+ buf[1] = address;
+ buf[3] = i2c_data;
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x19,
+ 0x0000, buf,
+ 4, M5602_URB_MSG_TIMEOUT);
+
+ /* usb_control_msg(...) returns the number of bytes sent upon success,
+ mask that and return zero instead */
+ return (err < 0) ? err : 0;
+}
+
+static int m5602_wait_for_i2c(struct sd *sd)
+{
+ int err;
+ u8 data;
+
+ do {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, &data);
+ } while ((data & I2C_BUSY) && !err);
+ return err;
+}
+
+int m5602_read_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len)
+{
+ int err, i;
+
+ if (!len || len > sd->sensor->i2c_regW)
+ return -EINVAL;
+
+ err = m5602_wait_for_i2c(sd);
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
+ sd->sensor->i2c_slave_id);
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
+ if (err < 0)
+ return err;
+
+ /* Sensors with registers that are of only
+ one byte width are differently read */
+
+ /* FIXME: This works with the ov9650, but has issues with the po1030 */
+ if (sd->sensor->i2c_regW == 1) {
+ err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 1);
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x08);
+ } else {
+ err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x18 + len);
+ }
+
+ for (i = 0; (i < len) && !err; i++) {
+ err = m5602_wait_for_i2c(sd);
+ if (err < 0)
+ return err;
+
+ err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
+
+ PDEBUG(D_CONF, "Reading sensor register "
+ "0x%x containing 0x%x ", address, *i2c_data);
+ }
+ return err;
+}
+
+int m5602_write_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len)
+{
+ int err, i;
+ u8 *p;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ /* No sensor with a data width larger than 16 bits has yet been seen */
+ if (len > sd->sensor->i2c_regW || !len)
+ return -EINVAL;
+
+ memcpy(buf, sensor_urb_skeleton,
+ sizeof(sensor_urb_skeleton));
+
+ buf[11] = sd->sensor->i2c_slave_id;
+ buf[15] = address;
+
+ /* Special case larger sensor writes */
+ p = buf + 16;
+
+ /* Copy a four byte write sequence for each byte to be written to */
+ for (i = 0; i < len; i++) {
+ memcpy(p, sensor_urb_skeleton + 16, 4);
+ p[3] = i2c_data[i];
+ p += 4;
+ PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x",
+ address, i2c_data[i]);
+ }
+
+ /* Copy the tailer */
+ memcpy(p, sensor_urb_skeleton + 20, 4);
+
+ /* Set the total length */
+ p[3] = 0x10 + len;
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x19,
+ 0x0000, buf,
+ 20 + len * 4, M5602_URB_MSG_TIMEOUT);
+
+ return (err < 0) ? err : 0;
+}
+
+/* Dump all the registers of the m5602 bridge,
+ unfortunately this breaks the camera until it's power cycled */
+static void m5602_dump_bridge(struct sd *sd)
+{
+ int i;
+ for (i = 0; i < 0x80; i++) {
+ unsigned char val = 0;
+ m5602_read_bridge(sd, i, &val);
+ pr_info("ALi m5602 address 0x%x contains 0x%x\n", i, val);
+ }
+ pr_info("Warning: The ALi m5602 webcam probably won't work until it's power cycled\n");
+}
+
+static int m5602_probe_sensor(struct sd *sd)
+{
+ /* Try the po1030 */
+ sd->sensor = &po1030;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ /* Try the mt9m111 sensor */
+ sd->sensor = &mt9m111;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ /* Try the s5k4aa */
+ sd->sensor = &s5k4aa;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ /* Try the ov9650 */
+ sd->sensor = &ov9650;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ /* Try the ov7660 */
+ sd->sensor = &ov7660;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ /* Try the s5k83a */
+ sd->sensor = &s5k83a;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ /* More sensor probe function goes here */
+ pr_info("Failed to find a sensor\n");
+ sd->sensor = NULL;
+ return -ENODEV;
+}
+
+static int m5602_configure(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id);
+
+static int m5602_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int err;
+
+ PDEBUG(D_CONF, "Initializing ALi m5602 webcam");
+ /* Run the init sequence */
+ err = sd->sensor->init(sd);
+
+ return err;
+}
+
+static int m5602_start_transfer(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+ int err;
+
+ /* Send start command to the camera */
+ const u8 buffer[4] = {0x13, 0xf9, 0x0f, 0x01};
+
+ if (sd->sensor->start)
+ sd->sensor->start(sd);
+
+ memcpy(buf, buffer, sizeof(buffer));
+ err = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0x04, 0x40, 0x19, 0x0000, buf,
+ sizeof(buffer), M5602_URB_MSG_TIMEOUT);
+
+ PDEBUG(D_STREAM, "Transfer started");
+ return (err < 0) ? err : 0;
+}
+
+static void m5602_urb_complete(struct gspca_dev *gspca_dev,
+ u8 *data, int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (len < 6) {
+ PDEBUG(D_PACK, "Packet is less than 6 bytes");
+ return;
+ }
+
+ /* Frame delimiter: ff xx xx xx ff ff */
+ if (data[0] == 0xff && data[4] == 0xff && data[5] == 0xff &&
+ data[2] != sd->frame_id) {
+ PDEBUG(D_FRAM, "Frame delimiter detected");
+ sd->frame_id = data[2];
+
+ /* Remove the extra fluff appended on each header */
+ data += 6;
+ len -= 6;
+
+ /* Complete the last frame (if any) */
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ NULL, 0);
+ sd->frame_count++;
+
+ /* Create a new frame */
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+
+ PDEBUG(D_FRAM, "Starting new frame %d",
+ sd->frame_count);
+
+ } else {
+ int cur_frame_len;
+
+ cur_frame_len = gspca_dev->image_len;
+ /* Remove urb header */
+ data += 4;
+ len -= 4;
+
+ if (cur_frame_len + len <= gspca_dev->frsz) {
+ PDEBUG(D_FRAM, "Continuing frame %d copying %d bytes",
+ sd->frame_count, len);
+
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data, len);
+ } else {
+ /* Add the remaining data up to frame size */
+ gspca_frame_add(gspca_dev, INTER_PACKET, data,
+ gspca_dev->frsz - cur_frame_len);
+ }
+ }
+}
+
+static void m5602_stop_transfer(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* Run the sensor specific end transfer sequence */
+ if (sd->sensor->stop)
+ sd->sensor->stop(sd);
+}
+
+/* sub-driver description, the ctrl and nctrl is filled at probe time */
+static struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .config = m5602_configure,
+ .init = m5602_init,
+ .start = m5602_start_transfer,
+ .stopN = m5602_stop_transfer,
+ .pkt_scan = m5602_urb_complete
+};
+
+/* this function is called at probe time */
+static int m5602_configure(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+ int err;
+
+ cam = &gspca_dev->cam;
+ sd->desc = &sd_desc;
+
+ if (dump_bridge)
+ m5602_dump_bridge(sd);
+
+ /* Probe sensor */
+ err = m5602_probe_sensor(sd);
+ if (err)
+ goto fail;
+
+ return 0;
+
+fail:
+ PDEBUG(D_ERR, "ALi m5602 webcam failed");
+ cam->cam_mode = NULL;
+ cam->nmodes = 0;
+
+ return err;
+}
+
+static int m5602_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static void m5602_disconnect(struct usb_interface *intf)
+{
+ struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor->disconnect)
+ sd->sensor->disconnect(sd);
+
+ gspca_disconnect(intf);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = m5602_table,
+ .probe = m5602_probe,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+ .disconnect = m5602_disconnect
+};
+
+module_usb_driver(sd_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+module_param(force_sensor, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(force_sensor,
+ "forces detection of a sensor, "
+ "1 = OV9650, 2 = S5K83A, 3 = S5K4AA, "
+ "4 = MT9M111, 5 = PO1030, 6 = OV7660");
+
+module_param(dump_bridge, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dump_bridge, "Dumps all usb bridge registers at startup");
+
+module_param(dump_sensor, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dump_sensor, "Dumps all usb sensor registers "
+ "at startup providing a sensor is found");
diff --git a/drivers/media/video/gspca/m5602/m5602_mt9m111.c b/drivers/media/video/gspca/m5602/m5602_mt9m111.c
new file mode 100644
index 00000000..6268aa24
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/m5602_mt9m111.c
@@ -0,0 +1,647 @@
+/*
+ * Driver for the mt9m111 sensor
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "m5602_mt9m111.h"
+
+static int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
+static int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
+static int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
+static int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val);
+static int mt9m111_set_auto_white_balance(struct gspca_dev *gspca_dev,
+ __s32 val);
+static int mt9m111_get_auto_white_balance(struct gspca_dev *gspca_dev,
+ __s32 *val);
+static int mt9m111_get_green_balance(struct gspca_dev *gspca_dev, __s32 *val);
+static int mt9m111_set_green_balance(struct gspca_dev *gspca_dev, __s32 val);
+static int mt9m111_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val);
+static int mt9m111_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val);
+static int mt9m111_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val);
+static int mt9m111_set_red_balance(struct gspca_dev *gspca_dev, __s32 val);
+
+static struct v4l2_pix_format mt9m111_modes[] = {
+ {
+ 640,
+ 480,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage = 640 * 480,
+ .bytesperline = 640,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0
+ }
+};
+
+static const struct ctrl mt9m111_ctrls[] = {
+#define VFLIP_IDX 0
+ {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "vertical flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = mt9m111_set_vflip,
+ .get = mt9m111_get_vflip
+ },
+#define HFLIP_IDX 1
+ {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "horizontal flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = mt9m111_set_hflip,
+ .get = mt9m111_get_hflip
+ },
+#define GAIN_IDX 2
+ {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "gain",
+ .minimum = 0,
+ .maximum = (INITIAL_MAX_GAIN - 1) * 2 * 2 * 2,
+ .step = 1,
+ .default_value = MT9M111_DEFAULT_GAIN,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = mt9m111_set_gain,
+ .get = mt9m111_get_gain
+ },
+#define AUTO_WHITE_BALANCE_IDX 3
+ {
+ {
+ .id = V4L2_CID_AUTO_WHITE_BALANCE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "auto white balance",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set = mt9m111_set_auto_white_balance,
+ .get = mt9m111_get_auto_white_balance
+ },
+#define GREEN_BALANCE_IDX 4
+ {
+ {
+ .id = M5602_V4L2_CID_GREEN_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "green balance",
+ .minimum = 0x00,
+ .maximum = 0x7ff,
+ .step = 0x1,
+ .default_value = MT9M111_GREEN_GAIN_DEFAULT,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = mt9m111_set_green_balance,
+ .get = mt9m111_get_green_balance
+ },
+#define BLUE_BALANCE_IDX 5
+ {
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "blue balance",
+ .minimum = 0x00,
+ .maximum = 0x7ff,
+ .step = 0x1,
+ .default_value = MT9M111_BLUE_GAIN_DEFAULT,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = mt9m111_set_blue_balance,
+ .get = mt9m111_get_blue_balance
+ },
+#define RED_BALANCE_IDX 5
+ {
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "red balance",
+ .minimum = 0x00,
+ .maximum = 0x7ff,
+ .step = 0x1,
+ .default_value = MT9M111_RED_GAIN_DEFAULT,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = mt9m111_set_red_balance,
+ .get = mt9m111_get_red_balance
+ },
+};
+
+static void mt9m111_dump_registers(struct sd *sd);
+
+int mt9m111_probe(struct sd *sd)
+{
+ u8 data[2] = {0x00, 0x00};
+ int i;
+ s32 *sensor_settings;
+
+ if (force_sensor) {
+ if (force_sensor == MT9M111_SENSOR) {
+ pr_info("Forcing a %s sensor\n", mt9m111.name);
+ goto sensor_found;
+ }
+ /* If we want to force another sensor, don't try to probe this
+ * one */
+ return -ENODEV;
+ }
+
+ PDEBUG(D_PROBE, "Probing for a mt9m111 sensor");
+
+ /* Do the preinit */
+ for (i = 0; i < ARRAY_SIZE(preinit_mt9m111); i++) {
+ if (preinit_mt9m111[i][0] == BRIDGE) {
+ m5602_write_bridge(sd,
+ preinit_mt9m111[i][1],
+ preinit_mt9m111[i][2]);
+ } else {
+ data[0] = preinit_mt9m111[i][2];
+ data[1] = preinit_mt9m111[i][3];
+ m5602_write_sensor(sd,
+ preinit_mt9m111[i][1], data, 2);
+ }
+ }
+
+ if (m5602_read_sensor(sd, MT9M111_SC_CHIPVER, data, 2))
+ return -ENODEV;
+
+ if ((data[0] == 0x14) && (data[1] == 0x3a)) {
+ pr_info("Detected a mt9m111 sensor\n");
+ goto sensor_found;
+ }
+
+ return -ENODEV;
+
+sensor_found:
+ sensor_settings = kmalloc(ARRAY_SIZE(mt9m111_ctrls) * sizeof(s32),
+ GFP_KERNEL);
+ if (!sensor_settings)
+ return -ENOMEM;
+
+ sd->gspca_dev.cam.cam_mode = mt9m111_modes;
+ sd->gspca_dev.cam.nmodes = ARRAY_SIZE(mt9m111_modes);
+ sd->desc->ctrls = mt9m111_ctrls;
+ sd->desc->nctrls = ARRAY_SIZE(mt9m111_ctrls);
+
+ for (i = 0; i < ARRAY_SIZE(mt9m111_ctrls); i++)
+ sensor_settings[i] = mt9m111_ctrls[i].qctrl.default_value;
+ sd->sensor_priv = sensor_settings;
+
+ return 0;
+}
+
+int mt9m111_init(struct sd *sd)
+{
+ int i, err = 0;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ /* Init the sensor */
+ for (i = 0; i < ARRAY_SIZE(init_mt9m111) && !err; i++) {
+ u8 data[2];
+
+ if (init_mt9m111[i][0] == BRIDGE) {
+ err = m5602_write_bridge(sd,
+ init_mt9m111[i][1],
+ init_mt9m111[i][2]);
+ } else {
+ data[0] = init_mt9m111[i][2];
+ data[1] = init_mt9m111[i][3];
+ err = m5602_write_sensor(sd,
+ init_mt9m111[i][1], data, 2);
+ }
+ }
+
+ if (dump_sensor)
+ mt9m111_dump_registers(sd);
+
+ err = mt9m111_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]);
+ if (err < 0)
+ return err;
+
+ err = mt9m111_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]);
+ if (err < 0)
+ return err;
+
+ err = mt9m111_set_green_balance(&sd->gspca_dev,
+ sensor_settings[GREEN_BALANCE_IDX]);
+ if (err < 0)
+ return err;
+
+ err = mt9m111_set_blue_balance(&sd->gspca_dev,
+ sensor_settings[BLUE_BALANCE_IDX]);
+ if (err < 0)
+ return err;
+
+ err = mt9m111_set_red_balance(&sd->gspca_dev,
+ sensor_settings[RED_BALANCE_IDX]);
+ if (err < 0)
+ return err;
+
+ return mt9m111_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]);
+}
+
+int mt9m111_start(struct sd *sd)
+{
+ int i, err = 0;
+ u8 data[2];
+ struct cam *cam = &sd->gspca_dev.cam;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ int width = cam->cam_mode[sd->gspca_dev.curr_mode].width - 1;
+ int height = cam->cam_mode[sd->gspca_dev.curr_mode].height;
+
+ for (i = 0; i < ARRAY_SIZE(start_mt9m111) && !err; i++) {
+ if (start_mt9m111[i][0] == BRIDGE) {
+ err = m5602_write_bridge(sd,
+ start_mt9m111[i][1],
+ start_mt9m111[i][2]);
+ } else {
+ data[0] = start_mt9m111[i][2];
+ data[1] = start_mt9m111[i][3];
+ err = m5602_write_sensor(sd,
+ start_mt9m111[i][1], data, 2);
+ }
+ }
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height >> 8) & 0xff);
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height & 0xff));
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < 2 && !err; i++)
+ err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0);
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 2);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < 2 && !err; i++)
+ err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, 0);
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA,
+ (width >> 8) & 0xff);
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, width & 0xff);
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);
+ if (err < 0)
+ return err;
+
+ switch (width) {
+ case 640:
+ PDEBUG(D_V4L2, "Configuring camera for VGA mode");
+ data[0] = MT9M111_RMB_OVER_SIZED;
+ data[1] = MT9M111_RMB_ROW_SKIP_2X |
+ MT9M111_RMB_COLUMN_SKIP_2X |
+ (sensor_settings[VFLIP_IDX] << 0) |
+ (sensor_settings[HFLIP_IDX] << 1);
+
+ err = m5602_write_sensor(sd,
+ MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
+ break;
+
+ case 320:
+ PDEBUG(D_V4L2, "Configuring camera for QVGA mode");
+ data[0] = MT9M111_RMB_OVER_SIZED;
+ data[1] = MT9M111_RMB_ROW_SKIP_4X |
+ MT9M111_RMB_COLUMN_SKIP_4X |
+ (sensor_settings[VFLIP_IDX] << 0) |
+ (sensor_settings[HFLIP_IDX] << 1);
+ err = m5602_write_sensor(sd,
+ MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
+ break;
+ }
+ return err;
+}
+
+void mt9m111_disconnect(struct sd *sd)
+{
+ sd->sensor = NULL;
+ kfree(sd->sensor_priv);
+}
+
+static int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[VFLIP_IDX];
+ PDEBUG(D_V4L2, "Read vertical flip %d", *val);
+
+ return 0;
+}
+
+static int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 data[2] = {0x00, 0x00};
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ PDEBUG(D_V4L2, "Set vertical flip to %d", val);
+
+ sensor_settings[VFLIP_IDX] = val;
+
+ /* The mt9m111 is flipped by default */
+ val = !val;
+
+ /* Set the correct page map */
+ err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
+ if (err < 0)
+ return err;
+
+ err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
+ if (err < 0)
+ return err;
+
+ data[1] = (data[1] & 0xfe) | val;
+ err = m5602_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
+ data, 2);
+ return err;
+}
+
+static int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[HFLIP_IDX];
+ PDEBUG(D_V4L2, "Read horizontal flip %d", *val);
+
+ return 0;
+}
+
+static int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 data[2] = {0x00, 0x00};
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ PDEBUG(D_V4L2, "Set horizontal flip to %d", val);
+
+ sensor_settings[HFLIP_IDX] = val;
+
+ /* The mt9m111 is flipped by default */
+ val = !val;
+
+ /* Set the correct page map */
+ err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
+ if (err < 0)
+ return err;
+
+ err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
+ if (err < 0)
+ return err;
+
+ data[1] = (data[1] & 0xfd) | ((val << 1) & 0x02);
+ err = m5602_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
+ data, 2);
+ return err;
+}
+
+static int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[GAIN_IDX];
+ PDEBUG(D_V4L2, "Read gain %d", *val);
+
+ return 0;
+}
+
+static int mt9m111_set_auto_white_balance(struct gspca_dev *gspca_dev,
+ __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+ int err;
+ u8 data[2];
+
+ err = m5602_read_sensor(sd, MT9M111_CP_OPERATING_MODE_CTL, data, 2);
+ if (err < 0)
+ return err;
+
+ sensor_settings[AUTO_WHITE_BALANCE_IDX] = val & 0x01;
+ data[1] = ((data[1] & 0xfd) | ((val & 0x01) << 1));
+
+ err = m5602_write_sensor(sd, MT9M111_CP_OPERATING_MODE_CTL, data, 2);
+
+ PDEBUG(D_V4L2, "Set auto white balance %d", val);
+ return err;
+}
+
+static int mt9m111_get_auto_white_balance(struct gspca_dev *gspca_dev,
+ __s32 *val) {
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[AUTO_WHITE_BALANCE_IDX];
+ PDEBUG(D_V4L2, "Read auto white balance %d", *val);
+ return 0;
+}
+
+static int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err, tmp;
+ u8 data[2] = {0x00, 0x00};
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ sensor_settings[GAIN_IDX] = val;
+
+ /* Set the correct page map */
+ err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
+ if (err < 0)
+ return err;
+
+ if (val >= INITIAL_MAX_GAIN * 2 * 2 * 2)
+ return -EINVAL;
+
+ if ((val >= INITIAL_MAX_GAIN * 2 * 2) &&
+ (val < (INITIAL_MAX_GAIN - 1) * 2 * 2 * 2))
+ tmp = (1 << 10) | (val << 9) |
+ (val << 8) | (val / 8);
+ else if ((val >= INITIAL_MAX_GAIN * 2) &&
+ (val < INITIAL_MAX_GAIN * 2 * 2))
+ tmp = (1 << 9) | (1 << 8) | (val / 4);
+ else if ((val >= INITIAL_MAX_GAIN) &&
+ (val < INITIAL_MAX_GAIN * 2))
+ tmp = (1 << 8) | (val / 2);
+ else
+ tmp = val;
+
+ data[1] = (tmp & 0xff);
+ data[0] = (tmp & 0xff00) >> 8;
+ PDEBUG(D_V4L2, "tmp=%d, data[1]=%d, data[0]=%d", tmp,
+ data[1], data[0]);
+
+ err = m5602_write_sensor(sd, MT9M111_SC_GLOBAL_GAIN,
+ data, 2);
+
+ return err;
+}
+
+static int mt9m111_set_green_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 data[2];
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ sensor_settings[GREEN_BALANCE_IDX] = val;
+ data[1] = (val & 0xff);
+ data[0] = (val & 0xff00) >> 8;
+
+ PDEBUG(D_V4L2, "Set green balance %d", val);
+ err = m5602_write_sensor(sd, MT9M111_SC_GREEN_1_GAIN,
+ data, 2);
+ if (err < 0)
+ return err;
+
+ return m5602_write_sensor(sd, MT9M111_SC_GREEN_2_GAIN,
+ data, 2);
+}
+
+static int mt9m111_get_green_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[GREEN_BALANCE_IDX];
+ PDEBUG(D_V4L2, "Read green balance %d", *val);
+ return 0;
+}
+
+static int mt9m111_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ u8 data[2];
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ sensor_settings[BLUE_BALANCE_IDX] = val;
+ data[1] = (val & 0xff);
+ data[0] = (val & 0xff00) >> 8;
+
+ PDEBUG(D_V4L2, "Set blue balance %d", val);
+
+ return m5602_write_sensor(sd, MT9M111_SC_BLUE_GAIN,
+ data, 2);
+}
+
+static int mt9m111_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[BLUE_BALANCE_IDX];
+ PDEBUG(D_V4L2, "Read blue balance %d", *val);
+ return 0;
+}
+
+static int mt9m111_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ u8 data[2];
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ sensor_settings[RED_BALANCE_IDX] = val;
+ data[1] = (val & 0xff);
+ data[0] = (val & 0xff00) >> 8;
+
+ PDEBUG(D_V4L2, "Set red balance %d", val);
+
+ return m5602_write_sensor(sd, MT9M111_SC_RED_GAIN,
+ data, 2);
+}
+
+static int mt9m111_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[RED_BALANCE_IDX];
+ PDEBUG(D_V4L2, "Read red balance %d", *val);
+ return 0;
+}
+
+static void mt9m111_dump_registers(struct sd *sd)
+{
+ u8 address, value[2] = {0x00, 0x00};
+
+ pr_info("Dumping the mt9m111 register state\n");
+
+ pr_info("Dumping the mt9m111 sensor core registers\n");
+ value[1] = MT9M111_SENSOR_CORE;
+ m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
+ for (address = 0; address < 0xff; address++) {
+ m5602_read_sensor(sd, address, value, 2);
+ pr_info("register 0x%x contains 0x%x%x\n",
+ address, value[0], value[1]);
+ }
+
+ pr_info("Dumping the mt9m111 color pipeline registers\n");
+ value[1] = MT9M111_COLORPIPE;
+ m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
+ for (address = 0; address < 0xff; address++) {
+ m5602_read_sensor(sd, address, value, 2);
+ pr_info("register 0x%x contains 0x%x%x\n",
+ address, value[0], value[1]);
+ }
+
+ pr_info("Dumping the mt9m111 camera control registers\n");
+ value[1] = MT9M111_CAMERA_CONTROL;
+ m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
+ for (address = 0; address < 0xff; address++) {
+ m5602_read_sensor(sd, address, value, 2);
+ pr_info("register 0x%x contains 0x%x%x\n",
+ address, value[0], value[1]);
+ }
+
+ pr_info("mt9m111 register state dump complete\n");
+}
diff --git a/drivers/media/video/gspca/m5602/m5602_mt9m111.h b/drivers/media/video/gspca/m5602/m5602_mt9m111.h
new file mode 100644
index 00000000..8c672b5c
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/m5602_mt9m111.h
@@ -0,0 +1,271 @@
+/*
+ * Driver for the mt9m111 sensor
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * Some defines taken from the mt9m111 sensor driver
+ * Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef M5602_MT9M111_H_
+#define M5602_MT9M111_H_
+
+#include "m5602_sensor.h"
+
+/*****************************************************************************/
+
+#define MT9M111_SC_CHIPVER 0x00
+#define MT9M111_SC_ROWSTART 0x01
+#define MT9M111_SC_COLSTART 0x02
+#define MT9M111_SC_WINDOW_HEIGHT 0x03
+#define MT9M111_SC_WINDOW_WIDTH 0x04
+#define MT9M111_SC_HBLANK_CONTEXT_B 0x05
+#define MT9M111_SC_VBLANK_CONTEXT_B 0x06
+#define MT9M111_SC_HBLANK_CONTEXT_A 0x07
+#define MT9M111_SC_VBLANK_CONTEXT_A 0x08
+#define MT9M111_SC_SHUTTER_WIDTH 0x09
+#define MT9M111_SC_ROW_SPEED 0x0a
+#define MT9M111_SC_EXTRA_DELAY 0x0b
+#define MT9M111_SC_SHUTTER_DELAY 0x0c
+#define MT9M111_SC_RESET 0x0d
+#define MT9M111_SC_R_MODE_CONTEXT_B 0x20
+#define MT9M111_SC_R_MODE_CONTEXT_A 0x21
+#define MT9M111_SC_FLASH_CONTROL 0x23
+#define MT9M111_SC_GREEN_1_GAIN 0x2b
+#define MT9M111_SC_BLUE_GAIN 0x2c
+#define MT9M111_SC_RED_GAIN 0x2d
+#define MT9M111_SC_GREEN_2_GAIN 0x2e
+#define MT9M111_SC_GLOBAL_GAIN 0x2f
+
+#define MT9M111_CONTEXT_CONTROL 0xc8
+#define MT9M111_PAGE_MAP 0xf0
+#define MT9M111_BYTEWISE_ADDRESS 0xf1
+
+#define MT9M111_CP_OPERATING_MODE_CTL 0x06
+#define MT9M111_CP_LUMA_OFFSET 0x34
+#define MT9M111_CP_LUMA_CLIP 0x35
+#define MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A 0x3a
+#define MT9M111_CP_LENS_CORRECTION_1 0x3b
+#define MT9M111_CP_DEFECT_CORR_CONTEXT_A 0x4c
+#define MT9M111_CP_DEFECT_CORR_CONTEXT_B 0x4d
+#define MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B 0x9b
+#define MT9M111_CP_GLOBAL_CLK_CONTROL 0xb3
+
+#define MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18 0x65
+#define MT9M111_CC_AWB_PARAMETER_7 0x28
+
+#define MT9M111_SENSOR_CORE 0x00
+#define MT9M111_COLORPIPE 0x01
+#define MT9M111_CAMERA_CONTROL 0x02
+
+#define MT9M111_RESET (1 << 0)
+#define MT9M111_RESTART (1 << 1)
+#define MT9M111_ANALOG_STANDBY (1 << 2)
+#define MT9M111_CHIP_ENABLE (1 << 3)
+#define MT9M111_CHIP_DISABLE (0 << 3)
+#define MT9M111_OUTPUT_DISABLE (1 << 4)
+#define MT9M111_SHOW_BAD_FRAMES (1 << 0)
+#define MT9M111_RESTART_BAD_FRAMES (1 << 1)
+#define MT9M111_SYNCHRONIZE_CHANGES (1 << 7)
+
+#define MT9M111_RMB_OVER_SIZED (1 << 0)
+#define MT9M111_RMB_MIRROR_ROWS (1 << 0)
+#define MT9M111_RMB_MIRROR_COLS (1 << 1)
+#define MT9M111_RMB_ROW_SKIP_2X (1 << 2)
+#define MT9M111_RMB_COLUMN_SKIP_2X (1 << 3)
+#define MT9M111_RMB_ROW_SKIP_4X (1 << 4)
+#define MT9M111_RMB_COLUMN_SKIP_4X (1 << 5)
+
+#define MT9M111_COLOR_MATRIX_BYPASS (1 << 4)
+#define MT9M111_SEL_CONTEXT_B (1 << 3)
+
+#define MT9M111_TRISTATE_PIN_IN_STANDBY (1 << 1)
+#define MT9M111_SOC_SOFT_STANDBY (1 << 0)
+
+#define MT9M111_2D_DEFECT_CORRECTION_ENABLE (1 << 0)
+
+#define INITIAL_MAX_GAIN 64
+#define MT9M111_DEFAULT_GAIN 283
+#define MT9M111_GREEN_GAIN_DEFAULT 0x20
+#define MT9M111_BLUE_GAIN_DEFAULT 0x20
+#define MT9M111_RED_GAIN_DEFAULT 0x20
+
+/*****************************************************************************/
+
+/* Kernel module parameters */
+extern int force_sensor;
+extern bool dump_sensor;
+
+int mt9m111_probe(struct sd *sd);
+int mt9m111_init(struct sd *sd);
+int mt9m111_start(struct sd *sd);
+void mt9m111_disconnect(struct sd *sd);
+
+static const struct m5602_sensor mt9m111 = {
+ .name = "MT9M111",
+
+ .i2c_slave_id = 0xba,
+ .i2c_regW = 2,
+
+ .probe = mt9m111_probe,
+ .init = mt9m111_init,
+ .disconnect = mt9m111_disconnect,
+ .start = mt9m111_start,
+};
+
+static const unsigned char preinit_mt9m111[][4] = {
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_RESET,
+ MT9M111_RESET |
+ MT9M111_RESTART |
+ MT9M111_ANALOG_STANDBY |
+ MT9M111_CHIP_DISABLE,
+ MT9M111_SHOW_BAD_FRAMES |
+ MT9M111_RESTART_BAD_FRAMES |
+ MT9M111_SYNCHRONIZE_CHANGES},
+
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00}
+};
+
+static const unsigned char init_mt9m111[][4] = {
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00},
+
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00,
+ MT9M111_CP_OPERATING_MODE_CTL},
+ {SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a},
+ {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00,
+ MT9M111_2D_DEFECT_CORRECTION_ENABLE},
+ {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00,
+ MT9M111_2D_DEFECT_CORRECTION_ENABLE},
+ {SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00},
+ {SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00},
+ {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00},
+ {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00},
+ {SENSOR, 0xcd, 0x00, 0x0e},
+ {SENSOR, 0xd0, 0x00, 0x40},
+
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02},
+ {SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00},
+ {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03},
+
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, 0x33, 0x03, 0x49},
+ {SENSOR, 0x34, 0xc0, 0x19},
+ {SENSOR, 0x3f, 0x20, 0x20},
+ {SENSOR, 0x40, 0x20, 0x20},
+ {SENSOR, 0x5a, 0xc0, 0x0a},
+ {SENSOR, 0x70, 0x7b, 0x0a},
+ {SENSOR, 0x71, 0xff, 0x00},
+ {SENSOR, 0x72, 0x19, 0x0e},
+ {SENSOR, 0x73, 0x18, 0x0f},
+ {SENSOR, 0x74, 0x57, 0x32},
+ {SENSOR, 0x75, 0x56, 0x34},
+ {SENSOR, 0x76, 0x73, 0x35},
+ {SENSOR, 0x77, 0x30, 0x12},
+ {SENSOR, 0x78, 0x79, 0x02},
+ {SENSOR, 0x79, 0x75, 0x06},
+ {SENSOR, 0x7a, 0x77, 0x0a},
+ {SENSOR, 0x7b, 0x78, 0x09},
+ {SENSOR, 0x7c, 0x7d, 0x06},
+ {SENSOR, 0x7d, 0x31, 0x10},
+ {SENSOR, 0x7e, 0x00, 0x7e},
+ {SENSOR, 0x80, 0x59, 0x04},
+ {SENSOR, 0x81, 0x59, 0x04},
+ {SENSOR, 0x82, 0x57, 0x0a},
+ {SENSOR, 0x83, 0x58, 0x0b},
+ {SENSOR, 0x84, 0x47, 0x0c},
+ {SENSOR, 0x85, 0x48, 0x0e},
+ {SENSOR, 0x86, 0x5b, 0x02},
+ {SENSOR, 0x87, 0x00, 0x5c},
+ {SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, MT9M111_SEL_CONTEXT_B},
+ {SENSOR, 0x60, 0x00, 0x80},
+ {SENSOR, 0x61, 0x00, 0x00},
+ {SENSOR, 0x62, 0x00, 0x00},
+ {SENSOR, 0x63, 0x00, 0x00},
+ {SENSOR, 0x64, 0x00, 0x00},
+
+ {SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d}, /* 13 */
+ {SENSOR, MT9M111_SC_COLSTART, 0x00, 0x12}, /* 18 */
+ {SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x00}, /* 1024 */
+ {SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x10}, /* 1296 */
+ {SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x60}, /* 352 */
+ {SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11}, /* 17 */
+ {SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x60}, /* 352 */
+ {SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11}, /* 17 */
+ {SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x0f}, /* 271 */
+ {SENSOR, 0x30, 0x04, 0x00},
+ /* Set number of blank rows chosen to 400 */
+ {SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0x90},
+};
+
+static const unsigned char start_mt9m111[][4] = {
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+};
+#endif
diff --git a/drivers/media/video/gspca/m5602/m5602_ov7660.c b/drivers/media/video/gspca/m5602/m5602_ov7660.c
new file mode 100644
index 00000000..9a14835c
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/m5602_ov7660.c
@@ -0,0 +1,488 @@
+/*
+ * Driver for the ov7660 sensor
+ *
+ * Copyright (C) 2009 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "m5602_ov7660.h"
+
+static int ov7660_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
+static int ov7660_set_gain(struct gspca_dev *gspca_dev, __s32 val);
+static int ov7660_get_auto_white_balance(struct gspca_dev *gspca_dev,
+ __s32 *val);
+static int ov7660_set_auto_white_balance(struct gspca_dev *gspca_dev,
+ __s32 val);
+static int ov7660_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val);
+static int ov7660_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val);
+static int ov7660_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val);
+static int ov7660_set_auto_exposure(struct gspca_dev *gspca_dev, __s32 val);
+static int ov7660_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int ov7660_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
+static int ov7660_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int ov7660_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
+
+static const struct ctrl ov7660_ctrls[] = {
+#define GAIN_IDX 1
+ {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "gain",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x1,
+ .default_value = OV7660_DEFAULT_GAIN,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = ov7660_set_gain,
+ .get = ov7660_get_gain
+ },
+#define BLUE_BALANCE_IDX 2
+#define RED_BALANCE_IDX 3
+#define AUTO_WHITE_BALANCE_IDX 4
+ {
+ {
+ .id = V4L2_CID_AUTO_WHITE_BALANCE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "auto white balance",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1
+ },
+ .set = ov7660_set_auto_white_balance,
+ .get = ov7660_get_auto_white_balance
+ },
+#define AUTO_GAIN_CTRL_IDX 5
+ {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "auto gain control",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1
+ },
+ .set = ov7660_set_auto_gain,
+ .get = ov7660_get_auto_gain
+ },
+#define AUTO_EXPOSURE_IDX 6
+ {
+ {
+ .id = V4L2_CID_EXPOSURE_AUTO,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "auto exposure",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1
+ },
+ .set = ov7660_set_auto_exposure,
+ .get = ov7660_get_auto_exposure
+ },
+#define HFLIP_IDX 7
+ {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "horizontal flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = ov7660_set_hflip,
+ .get = ov7660_get_hflip
+ },
+#define VFLIP_IDX 8
+ {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "vertical flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = ov7660_set_vflip,
+ .get = ov7660_get_vflip
+ },
+
+};
+
+static struct v4l2_pix_format ov7660_modes[] = {
+ {
+ 640,
+ 480,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ 640 * 480,
+ .bytesperline = 640,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0
+ }
+};
+
+static void ov7660_dump_registers(struct sd *sd);
+
+int ov7660_probe(struct sd *sd)
+{
+ int err = 0, i;
+ u8 prod_id = 0, ver_id = 0;
+
+ s32 *sensor_settings;
+
+ if (force_sensor) {
+ if (force_sensor == OV7660_SENSOR) {
+ pr_info("Forcing an %s sensor\n", ov7660.name);
+ goto sensor_found;
+ }
+ /* If we want to force another sensor,
+ don't try to probe this one */
+ return -ENODEV;
+ }
+
+ /* Do the preinit */
+ for (i = 0; i < ARRAY_SIZE(preinit_ov7660) && !err; i++) {
+ u8 data[2];
+
+ if (preinit_ov7660[i][0] == BRIDGE) {
+ err = m5602_write_bridge(sd,
+ preinit_ov7660[i][1],
+ preinit_ov7660[i][2]);
+ } else {
+ data[0] = preinit_ov7660[i][2];
+ err = m5602_write_sensor(sd,
+ preinit_ov7660[i][1], data, 1);
+ }
+ }
+ if (err < 0)
+ return err;
+
+ if (m5602_read_sensor(sd, OV7660_PID, &prod_id, 1))
+ return -ENODEV;
+
+ if (m5602_read_sensor(sd, OV7660_VER, &ver_id, 1))
+ return -ENODEV;
+
+ pr_info("Sensor reported 0x%x%x\n", prod_id, ver_id);
+
+ if ((prod_id == 0x76) && (ver_id == 0x60)) {
+ pr_info("Detected a ov7660 sensor\n");
+ goto sensor_found;
+ }
+ return -ENODEV;
+
+sensor_found:
+ sensor_settings = kmalloc(
+ ARRAY_SIZE(ov7660_ctrls) * sizeof(s32), GFP_KERNEL);
+ if (!sensor_settings)
+ return -ENOMEM;
+
+ sd->gspca_dev.cam.cam_mode = ov7660_modes;
+ sd->gspca_dev.cam.nmodes = ARRAY_SIZE(ov7660_modes);
+ sd->desc->ctrls = ov7660_ctrls;
+ sd->desc->nctrls = ARRAY_SIZE(ov7660_ctrls);
+
+ for (i = 0; i < ARRAY_SIZE(ov7660_ctrls); i++)
+ sensor_settings[i] = ov7660_ctrls[i].qctrl.default_value;
+ sd->sensor_priv = sensor_settings;
+
+ return 0;
+}
+
+int ov7660_init(struct sd *sd)
+{
+ int i, err = 0;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ /* Init the sensor */
+ for (i = 0; i < ARRAY_SIZE(init_ov7660); i++) {
+ u8 data[2];
+
+ if (init_ov7660[i][0] == BRIDGE) {
+ err = m5602_write_bridge(sd,
+ init_ov7660[i][1],
+ init_ov7660[i][2]);
+ } else {
+ data[0] = init_ov7660[i][2];
+ err = m5602_write_sensor(sd,
+ init_ov7660[i][1], data, 1);
+ }
+ }
+
+ if (dump_sensor)
+ ov7660_dump_registers(sd);
+
+ err = ov7660_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]);
+ if (err < 0)
+ return err;
+
+ err = ov7660_set_auto_white_balance(&sd->gspca_dev,
+ sensor_settings[AUTO_WHITE_BALANCE_IDX]);
+ if (err < 0)
+ return err;
+
+ err = ov7660_set_auto_gain(&sd->gspca_dev,
+ sensor_settings[AUTO_GAIN_CTRL_IDX]);
+ if (err < 0)
+ return err;
+
+ err = ov7660_set_auto_exposure(&sd->gspca_dev,
+ sensor_settings[AUTO_EXPOSURE_IDX]);
+ if (err < 0)
+ return err;
+ err = ov7660_set_hflip(&sd->gspca_dev,
+ sensor_settings[HFLIP_IDX]);
+ if (err < 0)
+ return err;
+
+ err = ov7660_set_vflip(&sd->gspca_dev,
+ sensor_settings[VFLIP_IDX]);
+
+ return err;
+}
+
+int ov7660_start(struct sd *sd)
+{
+ return 0;
+}
+
+int ov7660_stop(struct sd *sd)
+{
+ return 0;
+}
+
+void ov7660_disconnect(struct sd *sd)
+{
+ ov7660_stop(sd);
+
+ sd->sensor = NULL;
+ kfree(sd->sensor_priv);
+}
+
+static int ov7660_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[GAIN_IDX];
+ PDEBUG(D_V4L2, "Read gain %d", *val);
+ return 0;
+}
+
+static int ov7660_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ PDEBUG(D_V4L2, "Setting gain to %d", val);
+
+ sensor_settings[GAIN_IDX] = val;
+
+ err = m5602_write_sensor(sd, OV7660_GAIN, &i2c_data, 1);
+ return err;
+}
+
+
+static int ov7660_get_auto_white_balance(struct gspca_dev *gspca_dev,
+ __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[AUTO_WHITE_BALANCE_IDX];
+ return 0;
+}
+
+static int ov7660_set_auto_white_balance(struct gspca_dev *gspca_dev,
+ __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ PDEBUG(D_V4L2, "Set auto white balance to %d", val);
+
+ sensor_settings[AUTO_WHITE_BALANCE_IDX] = val;
+ err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1);
+ if (err < 0)
+ return err;
+
+ i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1));
+ err = m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1);
+
+ return err;
+}
+
+static int ov7660_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[AUTO_GAIN_CTRL_IDX];
+ PDEBUG(D_V4L2, "Read auto gain control %d", *val);
+ return 0;
+}
+
+static int ov7660_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ PDEBUG(D_V4L2, "Set auto gain control to %d", val);
+
+ sensor_settings[AUTO_GAIN_CTRL_IDX] = val;
+ err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1);
+ if (err < 0)
+ return err;
+
+ i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2));
+
+ return m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1);
+}
+
+static int ov7660_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[AUTO_EXPOSURE_IDX];
+ PDEBUG(D_V4L2, "Read auto exposure control %d", *val);
+ return 0;
+}
+
+static int ov7660_set_auto_exposure(struct gspca_dev *gspca_dev,
+ __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ PDEBUG(D_V4L2, "Set auto exposure control to %d", val);
+
+ sensor_settings[AUTO_EXPOSURE_IDX] = val;
+ err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1);
+ if (err < 0)
+ return err;
+
+ i2c_data = ((i2c_data & 0xfe) | ((val & 0x01) << 0));
+
+ return m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1);
+}
+
+static int ov7660_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[HFLIP_IDX];
+ PDEBUG(D_V4L2, "Read horizontal flip %d", *val);
+ return 0;
+}
+
+static int ov7660_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ PDEBUG(D_V4L2, "Set horizontal flip to %d", val);
+
+ sensor_settings[HFLIP_IDX] = val;
+
+ i2c_data = ((val & 0x01) << 5) |
+ (sensor_settings[VFLIP_IDX] << 4);
+
+ err = m5602_write_sensor(sd, OV7660_MVFP, &i2c_data, 1);
+
+ return err;
+}
+
+static int ov7660_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[VFLIP_IDX];
+ PDEBUG(D_V4L2, "Read vertical flip %d", *val);
+
+ return 0;
+}
+
+static int ov7660_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ PDEBUG(D_V4L2, "Set vertical flip to %d", val);
+ sensor_settings[VFLIP_IDX] = val;
+
+ i2c_data = ((val & 0x01) << 4) | (sensor_settings[VFLIP_IDX] << 5);
+ err = m5602_write_sensor(sd, OV7660_MVFP, &i2c_data, 1);
+ if (err < 0)
+ return err;
+
+ /* When vflip is toggled we need to readjust the bridge hsync/vsync */
+ if (gspca_dev->streaming)
+ err = ov7660_start(sd);
+
+ return err;
+}
+
+static void ov7660_dump_registers(struct sd *sd)
+{
+ int address;
+ pr_info("Dumping the ov7660 register state\n");
+ for (address = 0; address < 0xa9; address++) {
+ u8 value;
+ m5602_read_sensor(sd, address, &value, 1);
+ pr_info("register 0x%x contains 0x%x\n", address, value);
+ }
+
+ pr_info("ov7660 register state dump complete\n");
+
+ pr_info("Probing for which registers that are read/write\n");
+ for (address = 0; address < 0xff; address++) {
+ u8 old_value, ctrl_value;
+ u8 test_value[2] = {0xff, 0xff};
+
+ m5602_read_sensor(sd, address, &old_value, 1);
+ m5602_write_sensor(sd, address, test_value, 1);
+ m5602_read_sensor(sd, address, &ctrl_value, 1);
+
+ if (ctrl_value == test_value[0])
+ pr_info("register 0x%x is writeable\n", address);
+ else
+ pr_info("register 0x%x is read only\n", address);
+
+ /* Restore original value */
+ m5602_write_sensor(sd, address, &old_value, 1);
+ }
+}
diff --git a/drivers/media/video/gspca/m5602/m5602_ov7660.h b/drivers/media/video/gspca/m5602/m5602_ov7660.h
new file mode 100644
index 00000000..2b6a13b5
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/m5602_ov7660.h
@@ -0,0 +1,260 @@
+/*
+ * Driver for the ov7660 sensor
+ *
+ * Copyright (C) 2009 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef M5602_OV7660_H_
+#define M5602_OV7660_H_
+
+#include "m5602_sensor.h"
+
+#define OV7660_GAIN 0x00
+#define OV7660_BLUE_GAIN 0x01
+#define OV7660_RED_GAIN 0x02
+#define OV7660_VREF 0x03
+#define OV7660_COM1 0x04
+#define OV7660_BAVE 0x05
+#define OV7660_GEAVE 0x06
+#define OV7660_AECHH 0x07
+#define OV7660_RAVE 0x08
+#define OV7660_COM2 0x09
+#define OV7660_PID 0x0a
+#define OV7660_VER 0x0b
+#define OV7660_COM3 0x0c
+#define OV7660_COM4 0x0d
+#define OV7660_COM5 0x0e
+#define OV7660_COM6 0x0f
+#define OV7660_AECH 0x10
+#define OV7660_CLKRC 0x11
+#define OV7660_COM7 0x12
+#define OV7660_COM8 0x13
+#define OV7660_COM9 0x14
+#define OV7660_COM10 0x15
+#define OV7660_RSVD16 0x16
+#define OV7660_HSTART 0x17
+#define OV7660_HSTOP 0x18
+#define OV7660_VSTART 0x19
+#define OV7660_VSTOP 0x1a
+#define OV7660_PSHFT 0x1b
+#define OV7660_MIDH 0x1c
+#define OV7660_MIDL 0x1d
+#define OV7660_MVFP 0x1e
+#define OV7660_LAEC 0x1f
+#define OV7660_BOS 0x20
+#define OV7660_GBOS 0x21
+#define OV7660_GROS 0x22
+#define OV7660_ROS 0x23
+#define OV7660_AEW 0x24
+#define OV7660_AEB 0x25
+#define OV7660_VPT 0x26
+#define OV7660_BBIAS 0x27
+#define OV7660_GbBIAS 0x28
+#define OV7660_RSVD29 0x29
+#define OV7660_RBIAS 0x2c
+#define OV7660_HREF 0x32
+#define OV7660_ADC 0x37
+#define OV7660_OFON 0x39
+#define OV7660_TSLB 0x3a
+#define OV7660_COM12 0x3c
+#define OV7660_COM13 0x3d
+#define OV7660_LCC1 0x62
+#define OV7660_LCC2 0x63
+#define OV7660_LCC3 0x64
+#define OV7660_LCC4 0x65
+#define OV7660_LCC5 0x66
+#define OV7660_HV 0x69
+#define OV7660_RSVDA1 0xa1
+
+#define OV7660_DEFAULT_GAIN 0x0e
+#define OV7660_DEFAULT_RED_GAIN 0x80
+#define OV7660_DEFAULT_BLUE_GAIN 0x80
+#define OV7660_DEFAULT_SATURATION 0x00
+#define OV7660_DEFAULT_EXPOSURE 0x20
+
+/* Kernel module parameters */
+extern int force_sensor;
+extern bool dump_sensor;
+
+int ov7660_probe(struct sd *sd);
+int ov7660_init(struct sd *sd);
+int ov7660_start(struct sd *sd);
+int ov7660_stop(struct sd *sd);
+void ov7660_disconnect(struct sd *sd);
+
+static const struct m5602_sensor ov7660 = {
+ .name = "ov7660",
+ .i2c_slave_id = 0x42,
+ .i2c_regW = 1,
+ .probe = ov7660_probe,
+ .init = ov7660_init,
+ .start = ov7660_start,
+ .stop = ov7660_stop,
+ .disconnect = ov7660_disconnect,
+};
+
+static const unsigned char preinit_ov7660[][4] = {
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d},
+ {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x03},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x03},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+
+ {SENSOR, OV7660_OFON, 0x0c},
+ {SENSOR, OV7660_COM2, 0x11},
+ {SENSOR, OV7660_COM7, 0x05},
+
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x01},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00}
+};
+
+static const unsigned char init_ov7660[][4] = {
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d},
+ {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x01},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x01},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
+ {SENSOR, OV7660_COM7, 0x80},
+ {SENSOR, OV7660_CLKRC, 0x80},
+ {SENSOR, OV7660_COM9, 0x4c},
+ {SENSOR, OV7660_OFON, 0x43},
+ {SENSOR, OV7660_COM12, 0x28},
+ {SENSOR, OV7660_COM8, 0x00},
+ {SENSOR, OV7660_COM10, 0x40},
+ {SENSOR, OV7660_HSTART, 0x0c},
+ {SENSOR, OV7660_HSTOP, 0x61},
+ {SENSOR, OV7660_HREF, 0xa4},
+ {SENSOR, OV7660_PSHFT, 0x0b},
+ {SENSOR, OV7660_VSTART, 0x01},
+ {SENSOR, OV7660_VSTOP, 0x7a},
+ {SENSOR, OV7660_VSTOP, 0x00},
+ {SENSOR, OV7660_COM7, 0x05},
+ {SENSOR, OV7660_COM6, 0x42},
+ {SENSOR, OV7660_BBIAS, 0x94},
+ {SENSOR, OV7660_GbBIAS, 0x94},
+ {SENSOR, OV7660_RSVD29, 0x94},
+ {SENSOR, OV7660_RBIAS, 0x94},
+ {SENSOR, OV7660_COM1, 0x00},
+ {SENSOR, OV7660_AECH, 0x00},
+ {SENSOR, OV7660_AECHH, 0x00},
+ {SENSOR, OV7660_ADC, 0x05},
+ {SENSOR, OV7660_COM13, 0x00},
+ {SENSOR, OV7660_RSVDA1, 0x23},
+ {SENSOR, OV7660_TSLB, 0x0d},
+ {SENSOR, OV7660_HV, 0x80},
+ {SENSOR, OV7660_LCC1, 0x00},
+ {SENSOR, OV7660_LCC2, 0x00},
+ {SENSOR, OV7660_LCC3, 0x10},
+ {SENSOR, OV7660_LCC4, 0x40},
+ {SENSOR, OV7660_LCC5, 0x01},
+
+ {SENSOR, OV7660_AECH, 0x20},
+ {SENSOR, OV7660_COM1, 0x00},
+ {SENSOR, OV7660_OFON, 0x0c},
+ {SENSOR, OV7660_COM2, 0x11},
+ {SENSOR, OV7660_COM7, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x01},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
+ {SENSOR, OV7660_AECH, 0x5f},
+ {SENSOR, OV7660_COM1, 0x03},
+ {SENSOR, OV7660_OFON, 0x0c},
+ {SENSOR, OV7660_COM2, 0x11},
+ {SENSOR, OV7660_COM7, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x01},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x08},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xec},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x02},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x27},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0xa7},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+};
+#endif
diff --git a/drivers/media/video/gspca/m5602/m5602_ov9650.c b/drivers/media/video/gspca/m5602/m5602_ov9650.c
new file mode 100644
index 00000000..2114a8b9
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/m5602_ov9650.c
@@ -0,0 +1,881 @@
+/*
+ * Driver for the ov9650 sensor
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "m5602_ov9650.h"
+
+static int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
+static int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
+static int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
+static int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val);
+static int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val);
+static int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val);
+static int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val);
+static int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val);
+static int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
+static int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
+static int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev,
+ __s32 *val);
+static int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev,
+ __s32 val);
+static int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val);
+static int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val);
+static int ov9650_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val);
+static int ov9650_set_auto_exposure(struct gspca_dev *gspca_dev, __s32 val);
+
+/* Vertically and horizontally flips the image if matched, needed for machines
+ where the sensor is mounted upside down */
+static
+ const
+ struct dmi_system_id ov9650_flip_dmi_table[] = {
+ {
+ .ident = "ASUS A6Ja",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A6J")
+ }
+ },
+ {
+ .ident = "ASUS A6JC",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A6JC")
+ }
+ },
+ {
+ .ident = "ASUS A6K",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A6K")
+ }
+ },
+ {
+ .ident = "ASUS A6Kt",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A6Kt")
+ }
+ },
+ {
+ .ident = "ASUS A6VA",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A6VA")
+ }
+ },
+ {
+
+ .ident = "ASUS A6VC",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A6VC")
+ }
+ },
+ {
+ .ident = "ASUS A6VM",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A6VM")
+ }
+ },
+ {
+ .ident = "ASUS A7V",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A7V")
+ }
+ },
+ {
+ .ident = "Alienware Aurora m9700",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aurora m9700")
+ }
+ },
+ {}
+};
+
+static const struct ctrl ov9650_ctrls[] = {
+#define EXPOSURE_IDX 0
+ {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x00,
+ .maximum = 0x1ff,
+ .step = 0x4,
+ .default_value = EXPOSURE_DEFAULT,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = ov9650_set_exposure,
+ .get = ov9650_get_exposure
+ },
+#define GAIN_IDX 1
+ {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "gain",
+ .minimum = 0x00,
+ .maximum = 0x3ff,
+ .step = 0x1,
+ .default_value = GAIN_DEFAULT,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = ov9650_set_gain,
+ .get = ov9650_get_gain
+ },
+#define RED_BALANCE_IDX 2
+ {
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "red balance",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x1,
+ .default_value = RED_GAIN_DEFAULT,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = ov9650_set_red_balance,
+ .get = ov9650_get_red_balance
+ },
+#define BLUE_BALANCE_IDX 3
+ {
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "blue balance",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x1,
+ .default_value = BLUE_GAIN_DEFAULT,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = ov9650_set_blue_balance,
+ .get = ov9650_get_blue_balance
+ },
+#define HFLIP_IDX 4
+ {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "horizontal flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = ov9650_set_hflip,
+ .get = ov9650_get_hflip
+ },
+#define VFLIP_IDX 5
+ {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "vertical flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = ov9650_set_vflip,
+ .get = ov9650_get_vflip
+ },
+#define AUTO_WHITE_BALANCE_IDX 6
+ {
+ {
+ .id = V4L2_CID_AUTO_WHITE_BALANCE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "auto white balance",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1
+ },
+ .set = ov9650_set_auto_white_balance,
+ .get = ov9650_get_auto_white_balance
+ },
+#define AUTO_GAIN_CTRL_IDX 7
+ {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "auto gain control",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1
+ },
+ .set = ov9650_set_auto_gain,
+ .get = ov9650_get_auto_gain
+ },
+#define AUTO_EXPOSURE_IDX 8
+ {
+ {
+ .id = V4L2_CID_EXPOSURE_AUTO,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "auto exposure",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1
+ },
+ .set = ov9650_set_auto_exposure,
+ .get = ov9650_get_auto_exposure
+ }
+
+};
+
+static struct v4l2_pix_format ov9650_modes[] = {
+ {
+ 176,
+ 144,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ 176 * 144,
+ .bytesperline = 176,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 9
+ }, {
+ 320,
+ 240,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ 320 * 240,
+ .bytesperline = 320,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 8
+ }, {
+ 352,
+ 288,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ 352 * 288,
+ .bytesperline = 352,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 9
+ }, {
+ 640,
+ 480,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ 640 * 480,
+ .bytesperline = 640,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 9
+ }
+};
+
+static void ov9650_dump_registers(struct sd *sd);
+
+int ov9650_probe(struct sd *sd)
+{
+ int err = 0;
+ u8 prod_id = 0, ver_id = 0, i;
+ s32 *sensor_settings;
+
+ if (force_sensor) {
+ if (force_sensor == OV9650_SENSOR) {
+ pr_info("Forcing an %s sensor\n", ov9650.name);
+ goto sensor_found;
+ }
+ /* If we want to force another sensor,
+ don't try to probe this one */
+ return -ENODEV;
+ }
+
+ PDEBUG(D_PROBE, "Probing for an ov9650 sensor");
+
+ /* Run the pre-init before probing the sensor */
+ for (i = 0; i < ARRAY_SIZE(preinit_ov9650) && !err; i++) {
+ u8 data = preinit_ov9650[i][2];
+ if (preinit_ov9650[i][0] == SENSOR)
+ err = m5602_write_sensor(sd,
+ preinit_ov9650[i][1], &data, 1);
+ else
+ err = m5602_write_bridge(sd,
+ preinit_ov9650[i][1], data);
+ }
+
+ if (err < 0)
+ return err;
+
+ if (m5602_read_sensor(sd, OV9650_PID, &prod_id, 1))
+ return -ENODEV;
+
+ if (m5602_read_sensor(sd, OV9650_VER, &ver_id, 1))
+ return -ENODEV;
+
+ if ((prod_id == 0x96) && (ver_id == 0x52)) {
+ pr_info("Detected an ov9650 sensor\n");
+ goto sensor_found;
+ }
+ return -ENODEV;
+
+sensor_found:
+ sensor_settings = kmalloc(
+ ARRAY_SIZE(ov9650_ctrls) * sizeof(s32), GFP_KERNEL);
+ if (!sensor_settings)
+ return -ENOMEM;
+
+ sd->gspca_dev.cam.cam_mode = ov9650_modes;
+ sd->gspca_dev.cam.nmodes = ARRAY_SIZE(ov9650_modes);
+ sd->desc->ctrls = ov9650_ctrls;
+ sd->desc->nctrls = ARRAY_SIZE(ov9650_ctrls);
+
+ for (i = 0; i < ARRAY_SIZE(ov9650_ctrls); i++)
+ sensor_settings[i] = ov9650_ctrls[i].qctrl.default_value;
+ sd->sensor_priv = sensor_settings;
+ return 0;
+}
+
+int ov9650_init(struct sd *sd)
+{
+ int i, err = 0;
+ u8 data;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ if (dump_sensor)
+ ov9650_dump_registers(sd);
+
+ for (i = 0; i < ARRAY_SIZE(init_ov9650) && !err; i++) {
+ data = init_ov9650[i][2];
+ if (init_ov9650[i][0] == SENSOR)
+ err = m5602_write_sensor(sd, init_ov9650[i][1],
+ &data, 1);
+ else
+ err = m5602_write_bridge(sd, init_ov9650[i][1], data);
+ }
+
+ err = ov9650_set_exposure(&sd->gspca_dev,
+ sensor_settings[EXPOSURE_IDX]);
+ if (err < 0)
+ return err;
+
+ err = ov9650_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]);
+ if (err < 0)
+ return err;
+
+ err = ov9650_set_red_balance(&sd->gspca_dev,
+ sensor_settings[RED_BALANCE_IDX]);
+ if (err < 0)
+ return err;
+
+ err = ov9650_set_blue_balance(&sd->gspca_dev,
+ sensor_settings[BLUE_BALANCE_IDX]);
+ if (err < 0)
+ return err;
+
+ err = ov9650_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]);
+ if (err < 0)
+ return err;
+
+ err = ov9650_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]);
+ if (err < 0)
+ return err;
+
+ err = ov9650_set_auto_exposure(&sd->gspca_dev,
+ sensor_settings[AUTO_EXPOSURE_IDX]);
+ if (err < 0)
+ return err;
+
+ err = ov9650_set_auto_white_balance(&sd->gspca_dev,
+ sensor_settings[AUTO_WHITE_BALANCE_IDX]);
+ if (err < 0)
+ return err;
+
+ err = ov9650_set_auto_gain(&sd->gspca_dev,
+ sensor_settings[AUTO_GAIN_CTRL_IDX]);
+ return err;
+}
+
+int ov9650_start(struct sd *sd)
+{
+ u8 data;
+ int i, err = 0;
+ struct cam *cam = &sd->gspca_dev.cam;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ int width = cam->cam_mode[sd->gspca_dev.curr_mode].width;
+ int height = cam->cam_mode[sd->gspca_dev.curr_mode].height;
+ int ver_offs = cam->cam_mode[sd->gspca_dev.curr_mode].priv;
+ int hor_offs = OV9650_LEFT_OFFSET;
+
+ if ((!dmi_check_system(ov9650_flip_dmi_table) &&
+ sensor_settings[VFLIP_IDX]) ||
+ (dmi_check_system(ov9650_flip_dmi_table) &&
+ !sensor_settings[VFLIP_IDX]))
+ ver_offs--;
+
+ if (width <= 320)
+ hor_offs /= 2;
+
+ /* Synthesize the vsync/hsync setup */
+ for (i = 0; i < ARRAY_SIZE(res_init_ov9650) && !err; i++) {
+ if (res_init_ov9650[i][0] == BRIDGE)
+ err = m5602_write_bridge(sd, res_init_ov9650[i][1],
+ res_init_ov9650[i][2]);
+ else if (res_init_ov9650[i][0] == SENSOR) {
+ data = res_init_ov9650[i][2];
+ err = m5602_write_sensor(sd,
+ res_init_ov9650[i][1], &data, 1);
+ }
+ }
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA,
+ ((ver_offs >> 8) & 0xff));
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (ver_offs & 0xff));
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0);
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height >> 8) & 0xff);
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height & 0xff));
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < 2 && !err; i++)
+ err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0);
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 2);
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA,
+ (hor_offs >> 8) & 0xff);
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, hor_offs & 0xff);
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA,
+ ((width + hor_offs) >> 8) & 0xff);
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA,
+ ((width + hor_offs) & 0xff));
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);
+ if (err < 0)
+ return err;
+
+ switch (width) {
+ case 640:
+ PDEBUG(D_V4L2, "Configuring camera for VGA mode");
+
+ data = OV9650_VGA_SELECT | OV9650_RGB_SELECT |
+ OV9650_RAW_RGB_SELECT;
+ err = m5602_write_sensor(sd, OV9650_COM7, &data, 1);
+ break;
+
+ case 352:
+ PDEBUG(D_V4L2, "Configuring camera for CIF mode");
+
+ data = OV9650_CIF_SELECT | OV9650_RGB_SELECT |
+ OV9650_RAW_RGB_SELECT;
+ err = m5602_write_sensor(sd, OV9650_COM7, &data, 1);
+ break;
+
+ case 320:
+ PDEBUG(D_V4L2, "Configuring camera for QVGA mode");
+
+ data = OV9650_QVGA_SELECT | OV9650_RGB_SELECT |
+ OV9650_RAW_RGB_SELECT;
+ err = m5602_write_sensor(sd, OV9650_COM7, &data, 1);
+ break;
+
+ case 176:
+ PDEBUG(D_V4L2, "Configuring camera for QCIF mode");
+
+ data = OV9650_QCIF_SELECT | OV9650_RGB_SELECT |
+ OV9650_RAW_RGB_SELECT;
+ err = m5602_write_sensor(sd, OV9650_COM7, &data, 1);
+ break;
+ }
+ return err;
+}
+
+int ov9650_stop(struct sd *sd)
+{
+ u8 data = OV9650_SOFT_SLEEP | OV9650_OUTPUT_DRIVE_2X;
+ return m5602_write_sensor(sd, OV9650_COM2, &data, 1);
+}
+
+void ov9650_disconnect(struct sd *sd)
+{
+ ov9650_stop(sd);
+
+ sd->sensor = NULL;
+ kfree(sd->sensor_priv);
+}
+
+static int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[EXPOSURE_IDX];
+ PDEBUG(D_V4L2, "Read exposure %d", *val);
+ return 0;
+}
+
+static int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+ u8 i2c_data;
+ int err;
+
+ PDEBUG(D_V4L2, "Set exposure to %d", val);
+
+ sensor_settings[EXPOSURE_IDX] = val;
+ /* The 6 MSBs */
+ i2c_data = (val >> 10) & 0x3f;
+ err = m5602_write_sensor(sd, OV9650_AECHM,
+ &i2c_data, 1);
+ if (err < 0)
+ return err;
+
+ /* The 8 middle bits */
+ i2c_data = (val >> 2) & 0xff;
+ err = m5602_write_sensor(sd, OV9650_AECH,
+ &i2c_data, 1);
+ if (err < 0)
+ return err;
+
+ /* The 2 LSBs */
+ i2c_data = val & 0x03;
+ err = m5602_write_sensor(sd, OV9650_COM1, &i2c_data, 1);
+ return err;
+}
+
+static int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[GAIN_IDX];
+ PDEBUG(D_V4L2, "Read gain %d", *val);
+ return 0;
+}
+
+static int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ PDEBUG(D_V4L2, "Setting gain to %d", val);
+
+ sensor_settings[GAIN_IDX] = val;
+
+ /* The 2 MSB */
+ /* Read the OV9650_VREF register first to avoid
+ corrupting the VREF high and low bits */
+ err = m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ if (err < 0)
+ return err;
+
+ /* Mask away all uninteresting bits */
+ i2c_data = ((val & 0x0300) >> 2) |
+ (i2c_data & 0x3f);
+ err = m5602_write_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ if (err < 0)
+ return err;
+
+ /* The 8 LSBs */
+ i2c_data = val & 0xff;
+ err = m5602_write_sensor(sd, OV9650_GAIN, &i2c_data, 1);
+ return err;
+}
+
+static int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[RED_BALANCE_IDX];
+ PDEBUG(D_V4L2, "Read red gain %d", *val);
+ return 0;
+}
+
+static int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ PDEBUG(D_V4L2, "Set red gain to %d", val);
+
+ sensor_settings[RED_BALANCE_IDX] = val;
+
+ i2c_data = val & 0xff;
+ err = m5602_write_sensor(sd, OV9650_RED, &i2c_data, 1);
+ return err;
+}
+
+static int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[BLUE_BALANCE_IDX];
+ PDEBUG(D_V4L2, "Read blue gain %d", *val);
+
+ return 0;
+}
+
+static int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ PDEBUG(D_V4L2, "Set blue gain to %d", val);
+
+ sensor_settings[BLUE_BALANCE_IDX] = val;
+
+ i2c_data = val & 0xff;
+ err = m5602_write_sensor(sd, OV9650_BLUE, &i2c_data, 1);
+ return err;
+}
+
+static int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[HFLIP_IDX];
+ PDEBUG(D_V4L2, "Read horizontal flip %d", *val);
+ return 0;
+}
+
+static int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ PDEBUG(D_V4L2, "Set horizontal flip to %d", val);
+
+ sensor_settings[HFLIP_IDX] = val;
+
+ if (!dmi_check_system(ov9650_flip_dmi_table))
+ i2c_data = ((val & 0x01) << 5) |
+ (sensor_settings[VFLIP_IDX] << 4);
+ else
+ i2c_data = ((val & 0x01) << 5) |
+ (!sensor_settings[VFLIP_IDX] << 4);
+
+ err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+
+ return err;
+}
+
+static int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[VFLIP_IDX];
+ PDEBUG(D_V4L2, "Read vertical flip %d", *val);
+
+ return 0;
+}
+
+static int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ PDEBUG(D_V4L2, "Set vertical flip to %d", val);
+ sensor_settings[VFLIP_IDX] = val;
+
+ if (dmi_check_system(ov9650_flip_dmi_table))
+ val = !val;
+
+ i2c_data = ((val & 0x01) << 4) | (sensor_settings[VFLIP_IDX] << 5);
+ err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+ if (err < 0)
+ return err;
+
+ /* When vflip is toggled we need to readjust the bridge hsync/vsync */
+ if (gspca_dev->streaming)
+ err = ov9650_start(sd);
+
+ return err;
+}
+
+static int ov9650_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[AUTO_EXPOSURE_IDX];
+ PDEBUG(D_V4L2, "Read auto exposure control %d", *val);
+ return 0;
+}
+
+static int ov9650_set_auto_exposure(struct gspca_dev *gspca_dev,
+ __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ PDEBUG(D_V4L2, "Set auto exposure control to %d", val);
+
+ sensor_settings[AUTO_EXPOSURE_IDX] = val;
+ err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ if (err < 0)
+ return err;
+
+ i2c_data = ((i2c_data & 0xfe) | ((val & 0x01) << 0));
+
+ return m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
+}
+
+static int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev,
+ __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[AUTO_WHITE_BALANCE_IDX];
+ return 0;
+}
+
+static int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev,
+ __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ PDEBUG(D_V4L2, "Set auto white balance to %d", val);
+
+ sensor_settings[AUTO_WHITE_BALANCE_IDX] = val;
+ err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ if (err < 0)
+ return err;
+
+ i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1));
+ err = m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
+
+ return err;
+}
+
+static int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[AUTO_GAIN_CTRL_IDX];
+ PDEBUG(D_V4L2, "Read auto gain control %d", *val);
+ return 0;
+}
+
+static int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ PDEBUG(D_V4L2, "Set auto gain control to %d", val);
+
+ sensor_settings[AUTO_GAIN_CTRL_IDX] = val;
+ err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ if (err < 0)
+ return err;
+
+ i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2));
+
+ return m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
+}
+
+static void ov9650_dump_registers(struct sd *sd)
+{
+ int address;
+ pr_info("Dumping the ov9650 register state\n");
+ for (address = 0; address < 0xa9; address++) {
+ u8 value;
+ m5602_read_sensor(sd, address, &value, 1);
+ pr_info("register 0x%x contains 0x%x\n", address, value);
+ }
+
+ pr_info("ov9650 register state dump complete\n");
+
+ pr_info("Probing for which registers that are read/write\n");
+ for (address = 0; address < 0xff; address++) {
+ u8 old_value, ctrl_value;
+ u8 test_value[2] = {0xff, 0xff};
+
+ m5602_read_sensor(sd, address, &old_value, 1);
+ m5602_write_sensor(sd, address, test_value, 1);
+ m5602_read_sensor(sd, address, &ctrl_value, 1);
+
+ if (ctrl_value == test_value[0])
+ pr_info("register 0x%x is writeable\n", address);
+ else
+ pr_info("register 0x%x is read only\n", address);
+
+ /* Restore original value */
+ m5602_write_sensor(sd, address, &old_value, 1);
+ }
+}
diff --git a/drivers/media/video/gspca/m5602/m5602_ov9650.h b/drivers/media/video/gspca/m5602/m5602_ov9650.h
new file mode 100644
index 00000000..f7aa5bf6
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/m5602_ov9650.h
@@ -0,0 +1,307 @@
+/*
+ * Driver for the ov9650 sensor
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef M5602_OV9650_H_
+#define M5602_OV9650_H_
+
+#include <linux/dmi.h>
+#include "m5602_sensor.h"
+
+/*****************************************************************************/
+
+#define OV9650_GAIN 0x00
+#define OV9650_BLUE 0x01
+#define OV9650_RED 0x02
+#define OV9650_VREF 0x03
+#define OV9650_COM1 0x04
+#define OV9650_BAVE 0x05
+#define OV9650_GEAVE 0x06
+#define OV9650_RSVD7 0x07
+#define OV9650_COM2 0x09
+#define OV9650_PID 0x0a
+#define OV9650_VER 0x0b
+#define OV9650_COM3 0x0c
+#define OV9650_COM4 0x0d
+#define OV9650_COM5 0x0e
+#define OV9650_COM6 0x0f
+#define OV9650_AECH 0x10
+#define OV9650_CLKRC 0x11
+#define OV9650_COM7 0x12
+#define OV9650_COM8 0x13
+#define OV9650_COM9 0x14
+#define OV9650_COM10 0x15
+#define OV9650_RSVD16 0x16
+#define OV9650_HSTART 0x17
+#define OV9650_HSTOP 0x18
+#define OV9650_VSTRT 0x19
+#define OV9650_VSTOP 0x1a
+#define OV9650_PSHFT 0x1b
+#define OV9650_MVFP 0x1e
+#define OV9650_AEW 0x24
+#define OV9650_AEB 0x25
+#define OV9650_VPT 0x26
+#define OV9650_BBIAS 0x27
+#define OV9650_GbBIAS 0x28
+#define OV9650_Gr_COM 0x29
+#define OV9650_RBIAS 0x2c
+#define OV9650_HREF 0x32
+#define OV9650_CHLF 0x33
+#define OV9650_ARBLM 0x34
+#define OV9650_RSVD35 0x35
+#define OV9650_RSVD36 0x36
+#define OV9650_ADC 0x37
+#define OV9650_ACOM38 0x38
+#define OV9650_OFON 0x39
+#define OV9650_TSLB 0x3a
+#define OV9650_COM12 0x3c
+#define OV9650_COM13 0x3d
+#define OV9650_COM15 0x40
+#define OV9650_COM16 0x41
+#define OV9650_LCC1 0x62
+#define OV9650_LCC2 0x63
+#define OV9650_LCC3 0x64
+#define OV9650_LCC4 0x65
+#define OV9650_LCC5 0x66
+#define OV9650_HV 0x69
+#define OV9650_DBLV 0x6b
+#define OV9650_COM21 0x8b
+#define OV9650_COM22 0x8c
+#define OV9650_COM24 0x8e
+#define OV9650_DBLC1 0x8f
+#define OV9650_RSVD94 0x94
+#define OV9650_RSVD95 0x95
+#define OV9650_RSVD96 0x96
+#define OV9650_LCCFB 0x9d
+#define OV9650_LCCFR 0x9e
+#define OV9650_AECHM 0xa1
+#define OV9650_COM26 0xa5
+#define OV9650_ACOMA8 0xa8
+#define OV9650_ACOMA9 0xa9
+
+#define OV9650_REGISTER_RESET (1 << 7)
+#define OV9650_VGA_SELECT (1 << 6)
+#define OV9650_CIF_SELECT (1 << 5)
+#define OV9650_QVGA_SELECT (1 << 4)
+#define OV9650_QCIF_SELECT (1 << 3)
+#define OV9650_RGB_SELECT (1 << 2)
+#define OV9650_RAW_RGB_SELECT (1 << 0)
+
+#define OV9650_FAST_AGC_AEC (1 << 7)
+#define OV9650_AEC_UNLIM_STEP_SIZE (1 << 6)
+#define OV9650_BANDING (1 << 5)
+#define OV9650_AGC_EN (1 << 2)
+#define OV9650_AWB_EN (1 << 1)
+#define OV9650_AEC_EN (1 << 0)
+
+#define OV9650_VARIOPIXEL (1 << 2)
+#define OV9650_SYSTEM_CLK_SEL (1 << 7)
+#define OV9650_SLAM_MODE (1 << 4)
+
+#define OV9650_QVGA_VARIOPIXEL (1 << 7)
+
+#define OV9650_VFLIP (1 << 4)
+#define OV9650_HFLIP (1 << 5)
+
+#define OV9650_SOFT_SLEEP (1 << 4)
+#define OV9650_OUTPUT_DRIVE_2X (1 << 0)
+
+#define OV9650_DENOISE_ENABLE (1 << 5)
+#define OV9650_WHITE_PIXEL_ENABLE (1 << 1)
+#define OV9650_WHITE_PIXEL_OPTION (1 << 0)
+
+#define OV9650_LEFT_OFFSET 0x62
+
+#define GAIN_DEFAULT 0x14
+#define RED_GAIN_DEFAULT 0x70
+#define BLUE_GAIN_DEFAULT 0x20
+#define EXPOSURE_DEFAULT 0x1ff
+
+/*****************************************************************************/
+
+/* Kernel module parameters */
+extern int force_sensor;
+extern bool dump_sensor;
+
+int ov9650_probe(struct sd *sd);
+int ov9650_init(struct sd *sd);
+int ov9650_start(struct sd *sd);
+int ov9650_stop(struct sd *sd);
+void ov9650_disconnect(struct sd *sd);
+
+static const struct m5602_sensor ov9650 = {
+ .name = "OV9650",
+ .i2c_slave_id = 0x60,
+ .i2c_regW = 1,
+ .probe = ov9650_probe,
+ .init = ov9650_init,
+ .start = ov9650_start,
+ .stop = ov9650_stop,
+ .disconnect = ov9650_disconnect,
+};
+
+static const unsigned char preinit_ov9650[][3] = {
+ /* [INITCAM] */
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a},
+ /* Reset chip */
+ {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET},
+ /* Enable double clock */
+ {SENSOR, OV9650_CLKRC, 0x80},
+ /* Do something out of spec with the power */
+ {SENSOR, OV9650_OFON, 0x40}
+};
+
+static const unsigned char init_ov9650[][3] = {
+ /* [INITCAM] */
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a},
+
+ /* Reset chip */
+ {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET},
+ /* One extra reset is needed in order to make the sensor behave
+ properly when resuming from ram, could be a timing issue */
+ {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET},
+
+ /* Enable double clock */
+ {SENSOR, OV9650_CLKRC, 0x80},
+ /* Do something out of spec with the power */
+ {SENSOR, OV9650_OFON, 0x40},
+
+ /* Set fast AGC/AEC algorithm with unlimited step size */
+ {SENSOR, OV9650_COM8, OV9650_FAST_AGC_AEC |
+ OV9650_AEC_UNLIM_STEP_SIZE},
+
+ {SENSOR, OV9650_CHLF, 0x10},
+ {SENSOR, OV9650_ARBLM, 0xbf},
+ {SENSOR, OV9650_ACOM38, 0x81},
+ /* Turn off color matrix coefficient double option */
+ {SENSOR, OV9650_COM16, 0x00},
+ /* Enable color matrix for RGB/YUV, Delay Y channel,
+ set output Y/UV delay to 1 */
+ {SENSOR, OV9650_COM13, 0x19},
+ /* Enable digital BLC, Set output mode to U Y V Y */
+ {SENSOR, OV9650_TSLB, 0x0c},
+ /* Limit the AGC/AEC stable upper region */
+ {SENSOR, OV9650_COM24, 0x00},
+ /* Enable HREF and some out of spec things */
+ {SENSOR, OV9650_COM12, 0x73},
+ /* Set all DBLC offset signs to positive and
+ do some out of spec stuff */
+ {SENSOR, OV9650_DBLC1, 0xdf},
+ {SENSOR, OV9650_COM21, 0x06},
+ {SENSOR, OV9650_RSVD35, 0x91},
+ /* Necessary, no camera stream without it */
+ {SENSOR, OV9650_RSVD16, 0x06},
+ {SENSOR, OV9650_RSVD94, 0x99},
+ {SENSOR, OV9650_RSVD95, 0x99},
+ {SENSOR, OV9650_RSVD96, 0x04},
+ /* Enable full range output */
+ {SENSOR, OV9650_COM15, 0x0},
+ /* Enable HREF at optical black, enable ADBLC bias,
+ enable ADBLC, reset timings at format change */
+ {SENSOR, OV9650_COM6, 0x4b},
+ /* Subtract 32 from the B channel bias */
+ {SENSOR, OV9650_BBIAS, 0xa0},
+ /* Subtract 32 from the Gb channel bias */
+ {SENSOR, OV9650_GbBIAS, 0xa0},
+ /* Do not bypass the analog BLC and to some out of spec stuff */
+ {SENSOR, OV9650_Gr_COM, 0x00},
+ /* Subtract 32 from the R channel bias */
+ {SENSOR, OV9650_RBIAS, 0xa0},
+ /* Subtract 32 from the R channel bias */
+ {SENSOR, OV9650_RBIAS, 0x0},
+ {SENSOR, OV9650_COM26, 0x80},
+ {SENSOR, OV9650_ACOMA9, 0x98},
+ /* Set the AGC/AEC stable region upper limit */
+ {SENSOR, OV9650_AEW, 0x68},
+ /* Set the AGC/AEC stable region lower limit */
+ {SENSOR, OV9650_AEB, 0x5c},
+ /* Set the high and low limit nibbles to 3 */
+ {SENSOR, OV9650_VPT, 0xc3},
+ /* Set the Automatic Gain Ceiling (AGC) to 128x,
+ drop VSYNC at frame drop,
+ limit exposure timing,
+ drop frame when the AEC step is larger than the exposure gap */
+ {SENSOR, OV9650_COM9, 0x6e},
+ /* Set VSYNC negative, Set RESET to SLHS (slave mode horizontal sync)
+ and set PWDN to SLVS (slave mode vertical sync) */
+ {SENSOR, OV9650_COM10, 0x42},
+ /* Set horizontal column start high to default value */
+ {SENSOR, OV9650_HSTART, 0x1a}, /* 210 */
+ /* Set horizontal column end */
+ {SENSOR, OV9650_HSTOP, 0xbf}, /* 1534 */
+ /* Complementing register to the two writes above */
+ {SENSOR, OV9650_HREF, 0xb2},
+ /* Set vertical row start high bits */
+ {SENSOR, OV9650_VSTRT, 0x02},
+ /* Set vertical row end low bits */
+ {SENSOR, OV9650_VSTOP, 0x7e},
+ /* Set complementing vertical frame control */
+ {SENSOR, OV9650_VREF, 0x10},
+ {SENSOR, OV9650_ADC, 0x04},
+ {SENSOR, OV9650_HV, 0x40},
+
+ /* Enable denoise, and white-pixel erase */
+ {SENSOR, OV9650_COM22, OV9650_DENOISE_ENABLE |
+ OV9650_WHITE_PIXEL_ENABLE |
+ OV9650_WHITE_PIXEL_OPTION},
+
+ /* Enable VARIOPIXEL */
+ {SENSOR, OV9650_COM3, OV9650_VARIOPIXEL},
+ {SENSOR, OV9650_COM4, OV9650_QVGA_VARIOPIXEL},
+
+ /* Put the sensor in soft sleep mode */
+ {SENSOR, OV9650_COM2, OV9650_SOFT_SLEEP | OV9650_OUTPUT_DRIVE_2X},
+};
+
+static const unsigned char res_init_ov9650[][3] = {
+ {SENSOR, OV9650_COM2, OV9650_OUTPUT_DRIVE_2X},
+
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x82},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_L, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_L, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01}
+};
+#endif
diff --git a/drivers/media/video/gspca/m5602/m5602_po1030.c b/drivers/media/video/gspca/m5602/m5602_po1030.c
new file mode 100644
index 00000000..b8771698
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/m5602_po1030.c
@@ -0,0 +1,763 @@
+/*
+ * Driver for the po1030 sensor
+ *
+ * Copyright (c) 2008 Erik Andrén
+ * Copyright (c) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (c) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "m5602_po1030.h"
+
+static int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
+static int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
+static int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
+static int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val);
+static int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val);
+static int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val);
+static int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val);
+static int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val);
+static int po1030_get_green_balance(struct gspca_dev *gspca_dev, __s32 *val);
+static int po1030_set_green_balance(struct gspca_dev *gspca_dev, __s32 val);
+static int po1030_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int po1030_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
+static int po1030_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int po1030_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
+static int po1030_set_auto_white_balance(struct gspca_dev *gspca_dev,
+ __s32 val);
+static int po1030_get_auto_white_balance(struct gspca_dev *gspca_dev,
+ __s32 *val);
+static int po1030_set_auto_exposure(struct gspca_dev *gspca_dev,
+ __s32 val);
+static int po1030_get_auto_exposure(struct gspca_dev *gspca_dev,
+ __s32 *val);
+
+static struct v4l2_pix_format po1030_modes[] = {
+ {
+ 640,
+ 480,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage = 640 * 480,
+ .bytesperline = 640,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 2
+ }
+};
+
+static const struct ctrl po1030_ctrls[] = {
+#define GAIN_IDX 0
+ {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "gain",
+ .minimum = 0x00,
+ .maximum = 0x4f,
+ .step = 0x1,
+ .default_value = PO1030_GLOBAL_GAIN_DEFAULT,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = po1030_set_gain,
+ .get = po1030_get_gain
+ },
+#define EXPOSURE_IDX 1
+ {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x00,
+ .maximum = 0x02ff,
+ .step = 0x1,
+ .default_value = PO1030_EXPOSURE_DEFAULT,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = po1030_set_exposure,
+ .get = po1030_get_exposure
+ },
+#define RED_BALANCE_IDX 2
+ {
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "red balance",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x1,
+ .default_value = PO1030_RED_GAIN_DEFAULT,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = po1030_set_red_balance,
+ .get = po1030_get_red_balance
+ },
+#define BLUE_BALANCE_IDX 3
+ {
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "blue balance",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x1,
+ .default_value = PO1030_BLUE_GAIN_DEFAULT,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = po1030_set_blue_balance,
+ .get = po1030_get_blue_balance
+ },
+#define HFLIP_IDX 4
+ {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "horizontal flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set = po1030_set_hflip,
+ .get = po1030_get_hflip
+ },
+#define VFLIP_IDX 5
+ {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "vertical flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set = po1030_set_vflip,
+ .get = po1030_get_vflip
+ },
+#define AUTO_WHITE_BALANCE_IDX 6
+ {
+ {
+ .id = V4L2_CID_AUTO_WHITE_BALANCE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "auto white balance",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set = po1030_set_auto_white_balance,
+ .get = po1030_get_auto_white_balance
+ },
+#define AUTO_EXPOSURE_IDX 7
+ {
+ {
+ .id = V4L2_CID_EXPOSURE_AUTO,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "auto exposure",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set = po1030_set_auto_exposure,
+ .get = po1030_get_auto_exposure
+ },
+#define GREEN_BALANCE_IDX 8
+ {
+ {
+ .id = M5602_V4L2_CID_GREEN_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "green balance",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x1,
+ .default_value = PO1030_GREEN_GAIN_DEFAULT,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = po1030_set_green_balance,
+ .get = po1030_get_green_balance
+ },
+};
+
+static void po1030_dump_registers(struct sd *sd);
+
+int po1030_probe(struct sd *sd)
+{
+ u8 dev_id_h = 0, i;
+ s32 *sensor_settings;
+
+ if (force_sensor) {
+ if (force_sensor == PO1030_SENSOR) {
+ pr_info("Forcing a %s sensor\n", po1030.name);
+ goto sensor_found;
+ }
+ /* If we want to force another sensor, don't try to probe this
+ * one */
+ return -ENODEV;
+ }
+
+ PDEBUG(D_PROBE, "Probing for a po1030 sensor");
+
+ /* Run the pre-init to actually probe the unit */
+ for (i = 0; i < ARRAY_SIZE(preinit_po1030); i++) {
+ u8 data = preinit_po1030[i][2];
+ if (preinit_po1030[i][0] == SENSOR)
+ m5602_write_sensor(sd,
+ preinit_po1030[i][1], &data, 1);
+ else
+ m5602_write_bridge(sd, preinit_po1030[i][1], data);
+ }
+
+ if (m5602_read_sensor(sd, PO1030_DEVID_H, &dev_id_h, 1))
+ return -ENODEV;
+
+ if (dev_id_h == 0x30) {
+ pr_info("Detected a po1030 sensor\n");
+ goto sensor_found;
+ }
+ return -ENODEV;
+
+sensor_found:
+ sensor_settings = kmalloc(
+ ARRAY_SIZE(po1030_ctrls) * sizeof(s32), GFP_KERNEL);
+ if (!sensor_settings)
+ return -ENOMEM;
+
+ sd->gspca_dev.cam.cam_mode = po1030_modes;
+ sd->gspca_dev.cam.nmodes = ARRAY_SIZE(po1030_modes);
+ sd->desc->ctrls = po1030_ctrls;
+ sd->desc->nctrls = ARRAY_SIZE(po1030_ctrls);
+
+ for (i = 0; i < ARRAY_SIZE(po1030_ctrls); i++)
+ sensor_settings[i] = po1030_ctrls[i].qctrl.default_value;
+ sd->sensor_priv = sensor_settings;
+
+ return 0;
+}
+
+int po1030_init(struct sd *sd)
+{
+ s32 *sensor_settings = sd->sensor_priv;
+ int i, err = 0;
+
+ /* Init the sensor */
+ for (i = 0; i < ARRAY_SIZE(init_po1030) && !err; i++) {
+ u8 data[2] = {0x00, 0x00};
+
+ switch (init_po1030[i][0]) {
+ case BRIDGE:
+ err = m5602_write_bridge(sd,
+ init_po1030[i][1],
+ init_po1030[i][2]);
+ break;
+
+ case SENSOR:
+ data[0] = init_po1030[i][2];
+ err = m5602_write_sensor(sd,
+ init_po1030[i][1], data, 1);
+ break;
+
+ default:
+ pr_info("Invalid stream command, exiting init\n");
+ return -EINVAL;
+ }
+ }
+ if (err < 0)
+ return err;
+
+ if (dump_sensor)
+ po1030_dump_registers(sd);
+
+ err = po1030_set_exposure(&sd->gspca_dev,
+ sensor_settings[EXPOSURE_IDX]);
+ if (err < 0)
+ return err;
+
+ err = po1030_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]);
+ if (err < 0)
+ return err;
+
+ err = po1030_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]);
+ if (err < 0)
+ return err;
+
+ err = po1030_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]);
+ if (err < 0)
+ return err;
+
+ err = po1030_set_red_balance(&sd->gspca_dev,
+ sensor_settings[RED_BALANCE_IDX]);
+ if (err < 0)
+ return err;
+
+ err = po1030_set_blue_balance(&sd->gspca_dev,
+ sensor_settings[BLUE_BALANCE_IDX]);
+ if (err < 0)
+ return err;
+
+ err = po1030_set_green_balance(&sd->gspca_dev,
+ sensor_settings[GREEN_BALANCE_IDX]);
+ if (err < 0)
+ return err;
+
+ err = po1030_set_auto_white_balance(&sd->gspca_dev,
+ sensor_settings[AUTO_WHITE_BALANCE_IDX]);
+ if (err < 0)
+ return err;
+
+ err = po1030_set_auto_exposure(&sd->gspca_dev,
+ sensor_settings[AUTO_EXPOSURE_IDX]);
+ return err;
+}
+
+int po1030_start(struct sd *sd)
+{
+ struct cam *cam = &sd->gspca_dev.cam;
+ int i, err = 0;
+ int width = cam->cam_mode[sd->gspca_dev.curr_mode].width;
+ int height = cam->cam_mode[sd->gspca_dev.curr_mode].height;
+ int ver_offs = cam->cam_mode[sd->gspca_dev.curr_mode].priv;
+ u8 data;
+
+ switch (width) {
+ case 320:
+ data = PO1030_SUBSAMPLING;
+ err = m5602_write_sensor(sd, PO1030_CONTROL3, &data, 1);
+ if (err < 0)
+ return err;
+
+ data = ((width + 3) >> 8) & 0xff;
+ err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_H, &data, 1);
+ if (err < 0)
+ return err;
+
+ data = (width + 3) & 0xff;
+ err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_L, &data, 1);
+ if (err < 0)
+ return err;
+
+ data = ((height + 1) >> 8) & 0xff;
+ err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_H, &data, 1);
+ if (err < 0)
+ return err;
+
+ data = (height + 1) & 0xff;
+ err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_L, &data, 1);
+
+ height += 6;
+ width -= 1;
+ break;
+
+ case 640:
+ data = 0;
+ err = m5602_write_sensor(sd, PO1030_CONTROL3, &data, 1);
+ if (err < 0)
+ return err;
+
+ data = ((width + 7) >> 8) & 0xff;
+ err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_H, &data, 1);
+ if (err < 0)
+ return err;
+
+ data = (width + 7) & 0xff;
+ err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_L, &data, 1);
+ if (err < 0)
+ return err;
+
+ data = ((height + 3) >> 8) & 0xff;
+ err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_H, &data, 1);
+ if (err < 0)
+ return err;
+
+ data = (height + 3) & 0xff;
+ err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_L, &data, 1);
+
+ height += 12;
+ width -= 2;
+ break;
+ }
+ err = m5602_write_bridge(sd, M5602_XB_SENSOR_TYPE, 0x0c);
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_LINE_OF_FRAME_H, 0x81);
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_PIX_OF_LINE_H, 0x82);
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0x01);
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA,
+ ((ver_offs >> 8) & 0xff));
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (ver_offs & 0xff));
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < 2 && !err; i++)
+ err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0);
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height >> 8) & 0xff);
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height & 0xff));
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < 2 && !err; i++)
+ err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0);
+
+ for (i = 0; i < 2 && !err; i++)
+ err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);
+
+ for (i = 0; i < 2 && !err; i++)
+ err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, 0);
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, (width >> 8) & 0xff);
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, (width & 0xff));
+ if (err < 0)
+ return err;
+
+ err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);
+ return err;
+}
+
+static int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[EXPOSURE_IDX];
+ PDEBUG(D_V4L2, "Exposure read as %d", *val);
+ return 0;
+}
+
+static int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+ u8 i2c_data;
+ int err;
+
+ sensor_settings[EXPOSURE_IDX] = val;
+ PDEBUG(D_V4L2, "Set exposure to %d", val & 0xffff);
+
+ i2c_data = ((val & 0xff00) >> 8);
+ PDEBUG(D_V4L2, "Set exposure to high byte to 0x%x",
+ i2c_data);
+
+ err = m5602_write_sensor(sd, PO1030_INTEGLINES_H,
+ &i2c_data, 1);
+ if (err < 0)
+ return err;
+
+ i2c_data = (val & 0xff);
+ PDEBUG(D_V4L2, "Set exposure to low byte to 0x%x",
+ i2c_data);
+ err = m5602_write_sensor(sd, PO1030_INTEGLINES_M,
+ &i2c_data, 1);
+
+ return err;
+}
+
+static int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[GAIN_IDX];
+ PDEBUG(D_V4L2, "Read global gain %d", *val);
+ return 0;
+}
+
+static int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+ u8 i2c_data;
+ int err;
+
+ sensor_settings[GAIN_IDX] = val;
+
+ i2c_data = val & 0xff;
+ PDEBUG(D_V4L2, "Set global gain to %d", i2c_data);
+ err = m5602_write_sensor(sd, PO1030_GLOBALGAIN,
+ &i2c_data, 1);
+ return err;
+}
+
+static int po1030_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[HFLIP_IDX];
+ PDEBUG(D_V4L2, "Read hflip %d", *val);
+
+ return 0;
+}
+
+static int po1030_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+ u8 i2c_data;
+ int err;
+
+ sensor_settings[HFLIP_IDX] = val;
+
+ PDEBUG(D_V4L2, "Set hflip %d", val);
+ err = m5602_read_sensor(sd, PO1030_CONTROL2, &i2c_data, 1);
+ if (err < 0)
+ return err;
+
+ i2c_data = (0x7f & i2c_data) | ((val & 0x01) << 7);
+
+ err = m5602_write_sensor(sd, PO1030_CONTROL2,
+ &i2c_data, 1);
+
+ return err;
+}
+
+static int po1030_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[VFLIP_IDX];
+ PDEBUG(D_V4L2, "Read vflip %d", *val);
+
+ return 0;
+}
+
+static int po1030_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+ u8 i2c_data;
+ int err;
+
+ sensor_settings[VFLIP_IDX] = val;
+
+ PDEBUG(D_V4L2, "Set vflip %d", val);
+ err = m5602_read_sensor(sd, PO1030_CONTROL2, &i2c_data, 1);
+ if (err < 0)
+ return err;
+
+ i2c_data = (i2c_data & 0xbf) | ((val & 0x01) << 6);
+
+ err = m5602_write_sensor(sd, PO1030_CONTROL2,
+ &i2c_data, 1);
+
+ return err;
+}
+
+static int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[RED_BALANCE_IDX];
+ PDEBUG(D_V4L2, "Read red gain %d", *val);
+ return 0;
+}
+
+static int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+ u8 i2c_data;
+ int err;
+
+ sensor_settings[RED_BALANCE_IDX] = val;
+
+ i2c_data = val & 0xff;
+ PDEBUG(D_V4L2, "Set red gain to %d", i2c_data);
+ err = m5602_write_sensor(sd, PO1030_RED_GAIN,
+ &i2c_data, 1);
+ return err;
+}
+
+static int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[BLUE_BALANCE_IDX];
+ PDEBUG(D_V4L2, "Read blue gain %d", *val);
+
+ return 0;
+}
+
+static int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+ u8 i2c_data;
+ int err;
+
+ sensor_settings[BLUE_BALANCE_IDX] = val;
+
+ i2c_data = val & 0xff;
+ PDEBUG(D_V4L2, "Set blue gain to %d", i2c_data);
+ err = m5602_write_sensor(sd, PO1030_BLUE_GAIN,
+ &i2c_data, 1);
+
+ return err;
+}
+
+static int po1030_get_green_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[GREEN_BALANCE_IDX];
+ PDEBUG(D_V4L2, "Read green gain %d", *val);
+
+ return 0;
+}
+
+static int po1030_set_green_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+ u8 i2c_data;
+ int err;
+
+ sensor_settings[GREEN_BALANCE_IDX] = val;
+ i2c_data = val & 0xff;
+ PDEBUG(D_V4L2, "Set green gain to %d", i2c_data);
+
+ err = m5602_write_sensor(sd, PO1030_GREEN_1_GAIN,
+ &i2c_data, 1);
+ if (err < 0)
+ return err;
+
+ return m5602_write_sensor(sd, PO1030_GREEN_2_GAIN,
+ &i2c_data, 1);
+}
+
+static int po1030_get_auto_white_balance(struct gspca_dev *gspca_dev,
+ __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[AUTO_WHITE_BALANCE_IDX];
+ PDEBUG(D_V4L2, "Auto white balancing is %d", *val);
+
+ return 0;
+}
+
+static int po1030_set_auto_white_balance(struct gspca_dev *gspca_dev,
+ __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+ u8 i2c_data;
+ int err;
+
+ sensor_settings[AUTO_WHITE_BALANCE_IDX] = val;
+
+ err = m5602_read_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1);
+ if (err < 0)
+ return err;
+
+ PDEBUG(D_V4L2, "Set auto white balance to %d", val);
+ i2c_data = (i2c_data & 0xfe) | (val & 0x01);
+ err = m5602_write_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1);
+ return err;
+}
+
+static int po1030_get_auto_exposure(struct gspca_dev *gspca_dev,
+ __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[AUTO_EXPOSURE_IDX];
+ PDEBUG(D_V4L2, "Auto exposure is %d", *val);
+ return 0;
+}
+
+static int po1030_set_auto_exposure(struct gspca_dev *gspca_dev,
+ __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+ u8 i2c_data;
+ int err;
+
+ sensor_settings[AUTO_EXPOSURE_IDX] = val;
+ err = m5602_read_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1);
+ if (err < 0)
+ return err;
+
+ PDEBUG(D_V4L2, "Set auto exposure to %d", val);
+ i2c_data = (i2c_data & 0xfd) | ((val & 0x01) << 1);
+ return m5602_write_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1);
+}
+
+void po1030_disconnect(struct sd *sd)
+{
+ sd->sensor = NULL;
+ kfree(sd->sensor_priv);
+}
+
+static void po1030_dump_registers(struct sd *sd)
+{
+ int address;
+ u8 value = 0;
+
+ pr_info("Dumping the po1030 sensor core registers\n");
+ for (address = 0; address < 0x7f; address++) {
+ m5602_read_sensor(sd, address, &value, 1);
+ pr_info("register 0x%x contains 0x%x\n", address, value);
+ }
+
+ pr_info("po1030 register state dump complete\n");
+
+ pr_info("Probing for which registers that are read/write\n");
+ for (address = 0; address < 0xff; address++) {
+ u8 old_value, ctrl_value;
+ u8 test_value[2] = {0xff, 0xff};
+
+ m5602_read_sensor(sd, address, &old_value, 1);
+ m5602_write_sensor(sd, address, test_value, 1);
+ m5602_read_sensor(sd, address, &ctrl_value, 1);
+
+ if (ctrl_value == test_value[0])
+ pr_info("register 0x%x is writeable\n", address);
+ else
+ pr_info("register 0x%x is read only\n", address);
+
+ /* Restore original value */
+ m5602_write_sensor(sd, address, &old_value, 1);
+ }
+}
diff --git a/drivers/media/video/gspca/m5602/m5602_po1030.h b/drivers/media/video/gspca/m5602/m5602_po1030.h
new file mode 100644
index 00000000..81a2bcb8
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/m5602_po1030.h
@@ -0,0 +1,272 @@
+/*
+ * Driver for the po1030 sensor.
+ *
+ * Copyright (c) 2008 Erik Andrén
+ * Copyright (c) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (c) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * Register defines taken from Pascal Stangs Procyon Armlib
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef M5602_PO1030_H_
+#define M5602_PO1030_H_
+
+#include "m5602_sensor.h"
+
+/*****************************************************************************/
+
+#define PO1030_DEVID_H 0x00
+#define PO1030_DEVID_L 0x01
+#define PO1030_FRAMEWIDTH_H 0x04
+#define PO1030_FRAMEWIDTH_L 0x05
+#define PO1030_FRAMEHEIGHT_H 0x06
+#define PO1030_FRAMEHEIGHT_L 0x07
+#define PO1030_WINDOWX_H 0x08
+#define PO1030_WINDOWX_L 0x09
+#define PO1030_WINDOWY_H 0x0a
+#define PO1030_WINDOWY_L 0x0b
+#define PO1030_WINDOWWIDTH_H 0x0c
+#define PO1030_WINDOWWIDTH_L 0x0d
+#define PO1030_WINDOWHEIGHT_H 0x0e
+#define PO1030_WINDOWHEIGHT_L 0x0f
+
+#define PO1030_GLOBALIBIAS 0x12
+#define PO1030_PIXELIBIAS 0x13
+
+#define PO1030_GLOBALGAIN 0x15
+#define PO1030_RED_GAIN 0x16
+#define PO1030_GREEN_1_GAIN 0x17
+#define PO1030_BLUE_GAIN 0x18
+#define PO1030_GREEN_2_GAIN 0x19
+
+#define PO1030_INTEGLINES_H 0x1a
+#define PO1030_INTEGLINES_M 0x1b
+#define PO1030_INTEGLINES_L 0x1c
+
+#define PO1030_CONTROL1 0x1d
+#define PO1030_CONTROL2 0x1e
+#define PO1030_CONTROL3 0x1f
+#define PO1030_CONTROL4 0x20
+
+#define PO1030_PERIOD50_H 0x23
+#define PO1030_PERIOD50_L 0x24
+#define PO1030_PERIOD60_H 0x25
+#define PO1030_PERIOD60_L 0x26
+#define PO1030_REGCLK167 0x27
+#define PO1030_FLICKER_DELTA50 0x28
+#define PO1030_FLICKERDELTA60 0x29
+
+#define PO1030_ADCOFFSET 0x2c
+
+/* Gamma Correction Coeffs */
+#define PO1030_GC0 0x2d
+#define PO1030_GC1 0x2e
+#define PO1030_GC2 0x2f
+#define PO1030_GC3 0x30
+#define PO1030_GC4 0x31
+#define PO1030_GC5 0x32
+#define PO1030_GC6 0x33
+#define PO1030_GC7 0x34
+
+/* Color Transform Matrix */
+#define PO1030_CT0 0x35
+#define PO1030_CT1 0x36
+#define PO1030_CT2 0x37
+#define PO1030_CT3 0x38
+#define PO1030_CT4 0x39
+#define PO1030_CT5 0x3a
+#define PO1030_CT6 0x3b
+#define PO1030_CT7 0x3c
+#define PO1030_CT8 0x3d
+
+#define PO1030_AUTOCTRL1 0x3e
+#define PO1030_AUTOCTRL2 0x3f
+
+#define PO1030_YTARGET 0x40
+#define PO1030_GLOBALGAINMIN 0x41
+#define PO1030_GLOBALGAINMAX 0x42
+
+#define PO1030_AWB_RED_TUNING 0x47
+#define PO1030_AWB_BLUE_TUNING 0x48
+
+/* Output format control */
+#define PO1030_OUTFORMCTRL1 0x5a
+#define PO1030_OUTFORMCTRL2 0x5b
+#define PO1030_OUTFORMCTRL3 0x5c
+#define PO1030_OUTFORMCTRL4 0x5d
+#define PO1030_OUTFORMCTRL5 0x5e
+
+#define PO1030_EDGE_ENH_OFF 0x5f
+#define PO1030_EGA 0x60
+
+#define PO1030_Cb_U_GAIN 0x63
+#define PO1030_Cr_V_GAIN 0x64
+
+#define PO1030_YCONTRAST 0x74
+#define PO1030_YSATURATION 0x75
+
+#define PO1030_HFLIP (1 << 7)
+#define PO1030_VFLIP (1 << 6)
+
+#define PO1030_HREF_ENABLE (1 << 6)
+
+#define PO1030_RAW_RGB_BAYER 0x4
+
+#define PO1030_FRAME_EQUAL (1 << 3)
+#define PO1030_AUTO_SUBSAMPLING (1 << 4)
+
+#define PO1030_WEIGHT_WIN_2X (1 << 3)
+
+#define PO1030_SHUTTER_MODE (1 << 6)
+#define PO1030_AUTO_SUBSAMPLING (1 << 4)
+#define PO1030_FRAME_EQUAL (1 << 3)
+
+#define PO1030_SENSOR_RESET (1 << 5)
+
+#define PO1030_SUBSAMPLING (1 << 6)
+
+/*****************************************************************************/
+
+#define PO1030_GLOBAL_GAIN_DEFAULT 0x12
+#define PO1030_EXPOSURE_DEFAULT 0x0085
+#define PO1030_BLUE_GAIN_DEFAULT 0x36
+#define PO1030_RED_GAIN_DEFAULT 0x36
+#define PO1030_GREEN_GAIN_DEFAULT 0x40
+
+/*****************************************************************************/
+
+/* Kernel module parameters */
+extern int force_sensor;
+extern bool dump_sensor;
+
+int po1030_probe(struct sd *sd);
+int po1030_init(struct sd *sd);
+int po1030_start(struct sd *sd);
+void po1030_disconnect(struct sd *sd);
+
+static const struct m5602_sensor po1030 = {
+ .name = "PO1030",
+
+ .i2c_slave_id = 0xdc,
+ .i2c_regW = 1,
+
+ .probe = po1030_probe,
+ .init = po1030_init,
+ .start = po1030_start,
+ .disconnect = po1030_disconnect,
+};
+
+static const unsigned char preinit_po1030[][3] = {
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
+
+ {SENSOR, PO1030_AUTOCTRL2, PO1030_SENSOR_RESET | (1 << 2)},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x00}
+};
+
+static const unsigned char init_po1030[][3] = {
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+
+ {SENSOR, PO1030_AUTOCTRL2, PO1030_SENSOR_RESET | (1 << 2)},
+
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+
+ {SENSOR, PO1030_AUTOCTRL2, 0x04},
+
+ {SENSOR, PO1030_OUTFORMCTRL2, PO1030_RAW_RGB_BAYER},
+ {SENSOR, PO1030_AUTOCTRL1, PO1030_WEIGHT_WIN_2X},
+
+ {SENSOR, PO1030_CONTROL2, 0x03},
+ {SENSOR, 0x21, 0x90},
+ {SENSOR, PO1030_YTARGET, 0x60},
+ {SENSOR, 0x59, 0x13},
+ {SENSOR, PO1030_OUTFORMCTRL1, PO1030_HREF_ENABLE},
+ {SENSOR, PO1030_EDGE_ENH_OFF, 0x00},
+ {SENSOR, PO1030_EGA, 0x80},
+ {SENSOR, 0x78, 0x14},
+ {SENSOR, 0x6f, 0x01},
+ {SENSOR, PO1030_GLOBALGAINMAX, 0x14},
+ {SENSOR, PO1030_Cb_U_GAIN, 0x38},
+ {SENSOR, PO1030_Cr_V_GAIN, 0x38},
+ {SENSOR, PO1030_CONTROL1, PO1030_SHUTTER_MODE |
+ PO1030_AUTO_SUBSAMPLING |
+ PO1030_FRAME_EQUAL},
+ {SENSOR, PO1030_GC0, 0x10},
+ {SENSOR, PO1030_GC1, 0x20},
+ {SENSOR, PO1030_GC2, 0x40},
+ {SENSOR, PO1030_GC3, 0x60},
+ {SENSOR, PO1030_GC4, 0x80},
+ {SENSOR, PO1030_GC5, 0xa0},
+ {SENSOR, PO1030_GC6, 0xc0},
+ {SENSOR, PO1030_GC7, 0xff},
+
+ /* Set the width to 751 */
+ {SENSOR, PO1030_FRAMEWIDTH_H, 0x02},
+ {SENSOR, PO1030_FRAMEWIDTH_L, 0xef},
+
+ /* Set the height to 540 */
+ {SENSOR, PO1030_FRAMEHEIGHT_H, 0x02},
+ {SENSOR, PO1030_FRAMEHEIGHT_L, 0x1c},
+
+ /* Set the x window to 1 */
+ {SENSOR, PO1030_WINDOWX_H, 0x00},
+ {SENSOR, PO1030_WINDOWX_L, 0x01},
+
+ /* Set the y window to 1 */
+ {SENSOR, PO1030_WINDOWY_H, 0x00},
+ {SENSOR, PO1030_WINDOWY_L, 0x01},
+
+ /* with a very low lighted environment increase the exposure but
+ * decrease the FPS (Frame Per Second) */
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
+};
+#endif
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
new file mode 100644
index 00000000..cc8ec3f7
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
@@ -0,0 +1,726 @@
+/*
+ * Driver for the s5k4aa sensor
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "m5602_s5k4aa.h"
+
+static int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
+static int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
+static int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
+static int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
+static int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
+static int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val);
+static int s5k4aa_get_noise(struct gspca_dev *gspca_dev, __s32 *val);
+static int s5k4aa_set_noise(struct gspca_dev *gspca_dev, __s32 val);
+static int s5k4aa_get_brightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int s5k4aa_set_brightness(struct gspca_dev *gspca_dev, __s32 val);
+
+static
+ const
+ struct dmi_system_id s5k4aa_vflip_dmi_table[] = {
+ {
+ .ident = "BRUNEINIT",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "BRUNENIT"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "BRUNENIT"),
+ DMI_MATCH(DMI_BOARD_VERSION, "00030D0000000001")
+ }
+ }, {
+ .ident = "Fujitsu-Siemens Amilo Xa 2528",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xa 2528")
+ }
+ }, {
+ .ident = "Fujitsu-Siemens Amilo Xi 2428",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2428")
+ }
+ }, {
+ .ident = "Fujitsu-Siemens Amilo Xi 2528",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2528")
+ }
+ }, {
+ .ident = "Fujitsu-Siemens Amilo Xi 2550",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2550")
+ }
+ }, {
+ .ident = "Fujitsu-Siemens Amilo Pa 2548",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pa 2548")
+ }
+ }, {
+ .ident = "MSI GX700",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),
+ DMI_MATCH(DMI_BIOS_DATE, "12/02/2008")
+ }
+ }, {
+ .ident = "MSI GX700",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),
+ DMI_MATCH(DMI_BIOS_DATE, "07/26/2007")
+ }
+ }, {
+ .ident = "MSI GX700",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),
+ DMI_MATCH(DMI_BIOS_DATE, "07/19/2007")
+ }
+ }, {
+ .ident = "MSI GX700/GX705/EX700",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "GX700/GX705/EX700")
+ }
+ }, {
+ .ident = "MSI L735",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MS-1717X")
+ }
+ }, {
+ .ident = "Lenovo Y300",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "L3000 Y300"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Y300")
+ }
+ },
+ { }
+};
+
+static struct v4l2_pix_format s5k4aa_modes[] = {
+ {
+ 640,
+ 480,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ 640 * 480,
+ .bytesperline = 640,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0
+ },
+ {
+ 1280,
+ 1024,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ 1280 * 1024,
+ .bytesperline = 1280,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0
+ }
+};
+
+static const struct ctrl s5k4aa_ctrls[] = {
+#define VFLIP_IDX 0
+ {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "vertical flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = s5k4aa_set_vflip,
+ .get = s5k4aa_get_vflip
+ },
+#define HFLIP_IDX 1
+ {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "horizontal flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = s5k4aa_set_hflip,
+ .get = s5k4aa_get_hflip
+ },
+#define GAIN_IDX 2
+ {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+ .default_value = S5K4AA_DEFAULT_GAIN,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = s5k4aa_set_gain,
+ .get = s5k4aa_get_gain
+ },
+#define EXPOSURE_IDX 3
+ {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 13,
+ .maximum = 0xfff,
+ .step = 1,
+ .default_value = 0x100,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = s5k4aa_set_exposure,
+ .get = s5k4aa_get_exposure
+ },
+#define NOISE_SUPP_IDX 4
+ {
+ {
+ .id = V4L2_CID_PRIVATE_BASE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Noise suppression (smoothing)",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ },
+ .set = s5k4aa_set_noise,
+ .get = s5k4aa_get_noise
+ },
+#define BRIGHTNESS_IDX 5
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 0x1f,
+ .step = 1,
+ .default_value = S5K4AA_DEFAULT_BRIGHTNESS,
+ },
+ .set = s5k4aa_set_brightness,
+ .get = s5k4aa_get_brightness
+ },
+
+};
+
+static void s5k4aa_dump_registers(struct sd *sd);
+
+int s5k4aa_probe(struct sd *sd)
+{
+ u8 prod_id[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ const u8 expected_prod_id[6] = {0x00, 0x10, 0x00, 0x4b, 0x33, 0x75};
+ int i, err = 0;
+ s32 *sensor_settings;
+
+ if (force_sensor) {
+ if (force_sensor == S5K4AA_SENSOR) {
+ pr_info("Forcing a %s sensor\n", s5k4aa.name);
+ goto sensor_found;
+ }
+ /* If we want to force another sensor, don't try to probe this
+ * one */
+ return -ENODEV;
+ }
+
+ PDEBUG(D_PROBE, "Probing for a s5k4aa sensor");
+
+ /* Preinit the sensor */
+ for (i = 0; i < ARRAY_SIZE(preinit_s5k4aa) && !err; i++) {
+ u8 data[2] = {0x00, 0x00};
+
+ switch (preinit_s5k4aa[i][0]) {
+ case BRIDGE:
+ err = m5602_write_bridge(sd,
+ preinit_s5k4aa[i][1],
+ preinit_s5k4aa[i][2]);
+ break;
+
+ case SENSOR:
+ data[0] = preinit_s5k4aa[i][2];
+ err = m5602_write_sensor(sd,
+ preinit_s5k4aa[i][1],
+ data, 1);
+ break;
+
+ case SENSOR_LONG:
+ data[0] = preinit_s5k4aa[i][2];
+ data[1] = preinit_s5k4aa[i][3];
+ err = m5602_write_sensor(sd,
+ preinit_s5k4aa[i][1],
+ data, 2);
+ break;
+ default:
+ pr_info("Invalid stream command, exiting init\n");
+ return -EINVAL;
+ }
+ }
+
+ /* Test some registers, but we don't know their exact meaning yet */
+ if (m5602_read_sensor(sd, 0x00, prod_id, 2))
+ return -ENODEV;
+ if (m5602_read_sensor(sd, 0x02, prod_id+2, 2))
+ return -ENODEV;
+ if (m5602_read_sensor(sd, 0x04, prod_id+4, 2))
+ return -ENODEV;
+
+ if (memcmp(prod_id, expected_prod_id, sizeof(prod_id)))
+ return -ENODEV;
+ else
+ pr_info("Detected a s5k4aa sensor\n");
+
+sensor_found:
+ sensor_settings = kmalloc(
+ ARRAY_SIZE(s5k4aa_ctrls) * sizeof(s32), GFP_KERNEL);
+ if (!sensor_settings)
+ return -ENOMEM;
+
+ sd->gspca_dev.cam.cam_mode = s5k4aa_modes;
+ sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k4aa_modes);
+ sd->desc->ctrls = s5k4aa_ctrls;
+ sd->desc->nctrls = ARRAY_SIZE(s5k4aa_ctrls);
+
+ for (i = 0; i < ARRAY_SIZE(s5k4aa_ctrls); i++)
+ sensor_settings[i] = s5k4aa_ctrls[i].qctrl.default_value;
+ sd->sensor_priv = sensor_settings;
+
+ return 0;
+}
+
+int s5k4aa_start(struct sd *sd)
+{
+ int i, err = 0;
+ u8 data[2];
+ struct cam *cam = &sd->gspca_dev.cam;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ switch (cam->cam_mode[sd->gspca_dev.curr_mode].width) {
+ case 1280:
+ PDEBUG(D_V4L2, "Configuring camera for SXGA mode");
+
+ for (i = 0; i < ARRAY_SIZE(SXGA_s5k4aa); i++) {
+ switch (SXGA_s5k4aa[i][0]) {
+ case BRIDGE:
+ err = m5602_write_bridge(sd,
+ SXGA_s5k4aa[i][1],
+ SXGA_s5k4aa[i][2]);
+ break;
+
+ case SENSOR:
+ data[0] = SXGA_s5k4aa[i][2];
+ err = m5602_write_sensor(sd,
+ SXGA_s5k4aa[i][1],
+ data, 1);
+ break;
+
+ case SENSOR_LONG:
+ data[0] = SXGA_s5k4aa[i][2];
+ data[1] = SXGA_s5k4aa[i][3];
+ err = m5602_write_sensor(sd,
+ SXGA_s5k4aa[i][1],
+ data, 2);
+ break;
+
+ default:
+ pr_err("Invalid stream command, exiting init\n");
+ return -EINVAL;
+ }
+ }
+ err = s5k4aa_set_noise(&sd->gspca_dev, 0);
+ if (err < 0)
+ return err;
+ break;
+
+ case 640:
+ PDEBUG(D_V4L2, "Configuring camera for VGA mode");
+
+ for (i = 0; i < ARRAY_SIZE(VGA_s5k4aa); i++) {
+ switch (VGA_s5k4aa[i][0]) {
+ case BRIDGE:
+ err = m5602_write_bridge(sd,
+ VGA_s5k4aa[i][1],
+ VGA_s5k4aa[i][2]);
+ break;
+
+ case SENSOR:
+ data[0] = VGA_s5k4aa[i][2];
+ err = m5602_write_sensor(sd,
+ VGA_s5k4aa[i][1],
+ data, 1);
+ break;
+
+ case SENSOR_LONG:
+ data[0] = VGA_s5k4aa[i][2];
+ data[1] = VGA_s5k4aa[i][3];
+ err = m5602_write_sensor(sd,
+ VGA_s5k4aa[i][1],
+ data, 2);
+ break;
+
+ default:
+ pr_err("Invalid stream command, exiting init\n");
+ return -EINVAL;
+ }
+ }
+ err = s5k4aa_set_noise(&sd->gspca_dev, 1);
+ if (err < 0)
+ return err;
+ break;
+ }
+ if (err < 0)
+ return err;
+
+ err = s5k4aa_set_exposure(&sd->gspca_dev,
+ sensor_settings[EXPOSURE_IDX]);
+ if (err < 0)
+ return err;
+
+ err = s5k4aa_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]);
+ if (err < 0)
+ return err;
+
+ err = s5k4aa_set_brightness(&sd->gspca_dev,
+ sensor_settings[BRIGHTNESS_IDX]);
+ if (err < 0)
+ return err;
+
+ err = s5k4aa_set_noise(&sd->gspca_dev, sensor_settings[NOISE_SUPP_IDX]);
+ if (err < 0)
+ return err;
+
+ err = s5k4aa_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]);
+ if (err < 0)
+ return err;
+
+ return s5k4aa_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]);
+}
+
+int s5k4aa_init(struct sd *sd)
+{
+ int i, err = 0;
+
+ for (i = 0; i < ARRAY_SIZE(init_s5k4aa) && !err; i++) {
+ u8 data[2] = {0x00, 0x00};
+
+ switch (init_s5k4aa[i][0]) {
+ case BRIDGE:
+ err = m5602_write_bridge(sd,
+ init_s5k4aa[i][1],
+ init_s5k4aa[i][2]);
+ break;
+
+ case SENSOR:
+ data[0] = init_s5k4aa[i][2];
+ err = m5602_write_sensor(sd,
+ init_s5k4aa[i][1], data, 1);
+ break;
+
+ case SENSOR_LONG:
+ data[0] = init_s5k4aa[i][2];
+ data[1] = init_s5k4aa[i][3];
+ err = m5602_write_sensor(sd,
+ init_s5k4aa[i][1], data, 2);
+ break;
+ default:
+ pr_info("Invalid stream command, exiting init\n");
+ return -EINVAL;
+ }
+ }
+
+ if (dump_sensor)
+ s5k4aa_dump_registers(sd);
+
+ return err;
+}
+
+static int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[EXPOSURE_IDX];
+ PDEBUG(D_V4L2, "Read exposure %d", *val);
+
+ return 0;
+}
+
+static int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+ u8 data = S5K4AA_PAGE_MAP_2;
+ int err;
+
+ sensor_settings[EXPOSURE_IDX] = val;
+ PDEBUG(D_V4L2, "Set exposure to %d", val);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ if (err < 0)
+ return err;
+ data = (val >> 8) & 0xff;
+ err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1);
+ if (err < 0)
+ return err;
+ data = val & 0xff;
+ err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1);
+
+ return err;
+}
+
+static int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[VFLIP_IDX];
+ PDEBUG(D_V4L2, "Read vertical flip %d", *val);
+
+ return 0;
+}
+
+static int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+ u8 data = S5K4AA_PAGE_MAP_2;
+ int err;
+
+ sensor_settings[VFLIP_IDX] = val;
+
+ PDEBUG(D_V4L2, "Set vertical flip to %d", val);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ if (err < 0)
+ return err;
+
+ err = m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ if (err < 0)
+ return err;
+
+ if (dmi_check_system(s5k4aa_vflip_dmi_table))
+ val = !val;
+
+ data = ((data & ~S5K4AA_RM_V_FLIP) | ((val & 0x01) << 7));
+ err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ if (err < 0)
+ return err;
+
+ err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ if (err < 0)
+ return err;
+ if (val)
+ data &= 0xfe;
+ else
+ data |= 0x01;
+ err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ return err;
+}
+
+static int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[HFLIP_IDX];
+ PDEBUG(D_V4L2, "Read horizontal flip %d", *val);
+
+ return 0;
+}
+
+static int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+ u8 data = S5K4AA_PAGE_MAP_2;
+ int err;
+
+ sensor_settings[HFLIP_IDX] = val;
+
+ PDEBUG(D_V4L2, "Set horizontal flip to %d", val);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ if (err < 0)
+ return err;
+
+ err = m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ if (err < 0)
+ return err;
+
+ if (dmi_check_system(s5k4aa_vflip_dmi_table))
+ val = !val;
+
+ data = ((data & ~S5K4AA_RM_H_FLIP) | ((val & 0x01) << 6));
+ err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ if (err < 0)
+ return err;
+
+ err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ if (err < 0)
+ return err;
+ if (val)
+ data &= 0xfe;
+ else
+ data |= 0x01;
+ err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ return err;
+}
+
+static int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[GAIN_IDX];
+ PDEBUG(D_V4L2, "Read gain %d", *val);
+ return 0;
+}
+
+static int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+ u8 data = S5K4AA_PAGE_MAP_2;
+ int err;
+
+ sensor_settings[GAIN_IDX] = val;
+
+ PDEBUG(D_V4L2, "Set gain to %d", val);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ if (err < 0)
+ return err;
+
+ data = val & 0xff;
+ err = m5602_write_sensor(sd, S5K4AA_GAIN, &data, 1);
+
+ return err;
+}
+
+static int s5k4aa_get_brightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[BRIGHTNESS_IDX];
+ PDEBUG(D_V4L2, "Read brightness %d", *val);
+ return 0;
+}
+
+static int s5k4aa_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+ u8 data = S5K4AA_PAGE_MAP_2;
+ int err;
+
+ sensor_settings[BRIGHTNESS_IDX] = val;
+
+ PDEBUG(D_V4L2, "Set brightness to %d", val);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ if (err < 0)
+ return err;
+
+ data = val & 0xff;
+ return m5602_write_sensor(sd, S5K4AA_BRIGHTNESS, &data, 1);
+}
+
+static int s5k4aa_get_noise(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[NOISE_SUPP_IDX];
+ PDEBUG(D_V4L2, "Read noise %d", *val);
+ return 0;
+}
+
+static int s5k4aa_set_noise(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+ u8 data = S5K4AA_PAGE_MAP_2;
+ int err;
+
+ sensor_settings[NOISE_SUPP_IDX] = val;
+
+ PDEBUG(D_V4L2, "Set noise to %d", val);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ if (err < 0)
+ return err;
+
+ data = val & 0x01;
+ return m5602_write_sensor(sd, S5K4AA_NOISE_SUPP, &data, 1);
+}
+
+void s5k4aa_disconnect(struct sd *sd)
+{
+ sd->sensor = NULL;
+ kfree(sd->sensor_priv);
+}
+
+static void s5k4aa_dump_registers(struct sd *sd)
+{
+ int address;
+ u8 page, old_page;
+ m5602_read_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
+ for (page = 0; page < 16; page++) {
+ m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
+ pr_info("Dumping the s5k4aa register state for page 0x%x\n",
+ page);
+ for (address = 0; address <= 0xff; address++) {
+ u8 value = 0;
+ m5602_read_sensor(sd, address, &value, 1);
+ pr_info("register 0x%x contains 0x%x\n",
+ address, value);
+ }
+ }
+ pr_info("s5k4aa register state dump complete\n");
+
+ for (page = 0; page < 16; page++) {
+ m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
+ pr_info("Probing for which registers that are read/write for page 0x%x\n",
+ page);
+ for (address = 0; address <= 0xff; address++) {
+ u8 old_value, ctrl_value, test_value = 0xff;
+
+ m5602_read_sensor(sd, address, &old_value, 1);
+ m5602_write_sensor(sd, address, &test_value, 1);
+ m5602_read_sensor(sd, address, &ctrl_value, 1);
+
+ if (ctrl_value == test_value)
+ pr_info("register 0x%x is writeable\n",
+ address);
+ else
+ pr_info("register 0x%x is read only\n",
+ address);
+
+ /* Restore original value */
+ m5602_write_sensor(sd, address, &old_value, 1);
+ }
+ }
+ pr_info("Read/write register probing complete\n");
+ m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
+}
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.h b/drivers/media/video/gspca/m5602/m5602_s5k4aa.h
new file mode 100644
index 00000000..8e0035e7
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/m5602_s5k4aa.h
@@ -0,0 +1,283 @@
+/*
+ * Driver for the s5k4aa sensor
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef M5602_S5K4AA_H_
+#define M5602_S5K4AA_H_
+
+#include <linux/dmi.h>
+
+#include "m5602_sensor.h"
+
+/*****************************************************************************/
+
+#define S5K4AA_PAGE_MAP 0xec
+
+#define S5K4AA_PAGE_MAP_0 0x00
+#define S5K4AA_PAGE_MAP_1 0x01
+#define S5K4AA_PAGE_MAP_2 0x02
+
+/* Sensor register definitions for page 0x02 */
+#define S5K4AA_READ_MODE 0x03
+#define S5K4AA_ROWSTART_HI 0x04
+#define S5K4AA_ROWSTART_LO 0x05
+#define S5K4AA_COLSTART_HI 0x06
+#define S5K4AA_COLSTART_LO 0x07
+#define S5K4AA_WINDOW_HEIGHT_HI 0x08
+#define S5K4AA_WINDOW_HEIGHT_LO 0x09
+#define S5K4AA_WINDOW_WIDTH_HI 0x0a
+#define S5K4AA_WINDOW_WIDTH_LO 0x0b
+#define S5K4AA_GLOBAL_GAIN__ 0x0f
+/* sync lost, if too low, reduces frame rate if too high */
+#define S5K4AA_H_BLANK_HI__ 0x1d
+#define S5K4AA_H_BLANK_LO__ 0x1e
+#define S5K4AA_EXPOSURE_HI 0x17
+#define S5K4AA_EXPOSURE_LO 0x18
+#define S5K4AA_BRIGHTNESS 0x1f /* (digital?) gain : 5 bits */
+#define S5K4AA_GAIN 0x20 /* (analogue?) gain : 7 bits */
+#define S5K4AA_NOISE_SUPP 0x37
+
+#define S5K4AA_RM_ROW_SKIP_4X 0x08
+#define S5K4AA_RM_ROW_SKIP_2X 0x04
+#define S5K4AA_RM_COL_SKIP_4X 0x02
+#define S5K4AA_RM_COL_SKIP_2X 0x01
+#define S5K4AA_RM_H_FLIP 0x40
+#define S5K4AA_RM_V_FLIP 0x80
+
+#define S5K4AA_DEFAULT_GAIN 0x5f
+#define S5K4AA_DEFAULT_BRIGHTNESS 0x10
+
+/*****************************************************************************/
+
+/* Kernel module parameters */
+extern int force_sensor;
+extern bool dump_sensor;
+
+int s5k4aa_probe(struct sd *sd);
+int s5k4aa_init(struct sd *sd);
+int s5k4aa_start(struct sd *sd);
+void s5k4aa_disconnect(struct sd *sd);
+
+static const struct m5602_sensor s5k4aa = {
+ .name = "S5K4AA",
+ .i2c_slave_id = 0x5a,
+ .i2c_regW = 2,
+
+ .probe = s5k4aa_probe,
+ .init = s5k4aa_init,
+ .start = s5k4aa_start,
+ .disconnect = s5k4aa_disconnect,
+};
+
+static const unsigned char preinit_s5k4aa[][4] = {
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
+
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
+
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x14, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
+
+ {SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00}
+};
+
+static const unsigned char init_s5k4aa[][4] = {
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
+
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
+
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x14, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
+
+ {SENSOR, S5K4AA_PAGE_MAP, 0x07, 0x00},
+ {SENSOR, 0x36, 0x01, 0x00},
+ {SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, 0x7b, 0xff, 0x00},
+ {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+ {SENSOR, 0x0c, 0x05, 0x00},
+ {SENSOR, 0x02, 0x0e, 0x00},
+ {SENSOR, S5K4AA_READ_MODE, 0xa0, 0x00},
+ {SENSOR, 0x37, 0x00, 0x00},
+};
+
+static const unsigned char VGA_s5k4aa[][4] = {
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ /* VSYNC_PARA, VSYNC_PARA : img height 480 = 0x01e0 */
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xe0, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ /* HSYNC_PARA, HSYNC_PARA : img width 640 = 0x0280 */
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x80, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, /* 48 MHz */
+
+ {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+ {SENSOR, S5K4AA_READ_MODE, S5K4AA_RM_H_FLIP | S5K4AA_RM_ROW_SKIP_2X
+ | S5K4AA_RM_COL_SKIP_2X, 0x00},
+ /* 0x37 : Fix image stability when light is too bright and improves
+ * image quality in 640x480, but worsens it in 1280x1024 */
+ {SENSOR, 0x37, 0x01, 0x00},
+ /* ROWSTART_HI, ROWSTART_LO : 10 + (1024-960)/2 = 42 = 0x002a */
+ {SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00},
+ {SENSOR, S5K4AA_ROWSTART_LO, 0x29, 0x00},
+ {SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00},
+ {SENSOR, S5K4AA_COLSTART_LO, 0x0c, 0x00},
+ /* window_height_hi, window_height_lo : 960 = 0x03c0 */
+ {SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x03, 0x00},
+ {SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0xc0, 0x00},
+ /* window_width_hi, window_width_lo : 1280 = 0x0500 */
+ {SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00},
+ {SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x00, 0x00},
+ {SENSOR, S5K4AA_H_BLANK_HI__, 0x00, 0x00},
+ {SENSOR, S5K4AA_H_BLANK_LO__, 0xa8, 0x00}, /* helps to sync... */
+ {SENSOR, S5K4AA_EXPOSURE_HI, 0x01, 0x00},
+ {SENSOR, S5K4AA_EXPOSURE_LO, 0x00, 0x00},
+ {SENSOR, 0x11, 0x04, 0x00},
+ {SENSOR, 0x12, 0xc3, 0x00},
+ {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+ {SENSOR, 0x02, 0x0e, 0x00},
+};
+
+static const unsigned char SXGA_s5k4aa[][4] = {
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ /* VSYNC_PARA, VSYNC_PARA : img height 1024 = 0x0400 */
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ /* HSYNC_PARA, HSYNC_PARA : img width 1280 = 0x0500 */
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, /* 48 MHz */
+
+ {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+ {SENSOR, S5K4AA_READ_MODE, S5K4AA_RM_H_FLIP, 0x00},
+ {SENSOR, 0x37, 0x01, 0x00},
+ {SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00},
+ {SENSOR, S5K4AA_ROWSTART_LO, 0x09, 0x00},
+ {SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00},
+ {SENSOR, S5K4AA_COLSTART_LO, 0x0a, 0x00},
+ {SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x04, 0x00},
+ {SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0x00, 0x00},
+ {SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00},
+ {SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x00, 0x00},
+ {SENSOR, S5K4AA_H_BLANK_HI__, 0x01, 0x00},
+ {SENSOR, S5K4AA_H_BLANK_LO__, 0xa8, 0x00},
+ {SENSOR, S5K4AA_EXPOSURE_HI, 0x01, 0x00},
+ {SENSOR, S5K4AA_EXPOSURE_LO, 0x00, 0x00},
+ {SENSOR, 0x11, 0x04, 0x00},
+ {SENSOR, 0x12, 0xc3, 0x00},
+ {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+ {SENSOR, 0x02, 0x0e, 0x00},
+};
+#endif
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k83a.c b/drivers/media/video/gspca/m5602/m5602_s5k83a.c
new file mode 100644
index 00000000..1de743a0
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/m5602_s5k83a.c
@@ -0,0 +1,605 @@
+/*
+ * Driver for the s5k83a sensor
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kthread.h>
+#include "m5602_s5k83a.h"
+
+static int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val);
+static int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
+static int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val);
+static int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int s5k83a_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
+static int s5k83a_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
+static int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
+static int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
+
+static struct v4l2_pix_format s5k83a_modes[] = {
+ {
+ 640,
+ 480,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ 640 * 480,
+ .bytesperline = 640,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0
+ }
+};
+
+static const struct ctrl s5k83a_ctrls[] = {
+#define GAIN_IDX 0
+ {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "gain",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x01,
+ .default_value = S5K83A_DEFAULT_GAIN,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = s5k83a_set_gain,
+ .get = s5k83a_get_gain
+
+ },
+#define BRIGHTNESS_IDX 1
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "brightness",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x01,
+ .default_value = S5K83A_DEFAULT_BRIGHTNESS,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = s5k83a_set_brightness,
+ .get = s5k83a_get_brightness,
+ },
+#define EXPOSURE_IDX 2
+ {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x00,
+ .maximum = S5K83A_MAXIMUM_EXPOSURE,
+ .step = 0x01,
+ .default_value = S5K83A_DEFAULT_EXPOSURE,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = s5k83a_set_exposure,
+ .get = s5k83a_get_exposure
+ },
+#define HFLIP_IDX 3
+ {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "horizontal flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = s5k83a_set_hflip,
+ .get = s5k83a_get_hflip
+ },
+#define VFLIP_IDX 4
+ {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "vertical flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = s5k83a_set_vflip,
+ .get = s5k83a_get_vflip
+ }
+};
+
+static void s5k83a_dump_registers(struct sd *sd);
+static int s5k83a_get_rotation(struct sd *sd, u8 *reg_data);
+static int s5k83a_set_led_indication(struct sd *sd, u8 val);
+static int s5k83a_set_flip_real(struct gspca_dev *gspca_dev,
+ __s32 vflip, __s32 hflip);
+
+int s5k83a_probe(struct sd *sd)
+{
+ struct s5k83a_priv *sens_priv;
+ u8 prod_id = 0, ver_id = 0;
+ int i, err = 0;
+
+ if (force_sensor) {
+ if (force_sensor == S5K83A_SENSOR) {
+ pr_info("Forcing a %s sensor\n", s5k83a.name);
+ goto sensor_found;
+ }
+ /* If we want to force another sensor, don't try to probe this
+ * one */
+ return -ENODEV;
+ }
+
+ PDEBUG(D_PROBE, "Probing for a s5k83a sensor");
+
+ /* Preinit the sensor */
+ for (i = 0; i < ARRAY_SIZE(preinit_s5k83a) && !err; i++) {
+ u8 data[2] = {preinit_s5k83a[i][2], preinit_s5k83a[i][3]};
+ if (preinit_s5k83a[i][0] == SENSOR)
+ err = m5602_write_sensor(sd, preinit_s5k83a[i][1],
+ data, 2);
+ else
+ err = m5602_write_bridge(sd, preinit_s5k83a[i][1],
+ data[0]);
+ }
+
+ /* We don't know what register (if any) that contain the product id
+ * Just pick the first addresses that seem to produce the same results
+ * on multiple machines */
+ if (m5602_read_sensor(sd, 0x00, &prod_id, 1))
+ return -ENODEV;
+
+ if (m5602_read_sensor(sd, 0x01, &ver_id, 1))
+ return -ENODEV;
+
+ if ((prod_id == 0xff) || (ver_id == 0xff))
+ return -ENODEV;
+ else
+ pr_info("Detected a s5k83a sensor\n");
+
+sensor_found:
+ sens_priv = kmalloc(
+ sizeof(struct s5k83a_priv), GFP_KERNEL);
+ if (!sens_priv)
+ return -ENOMEM;
+
+ sens_priv->settings =
+ kmalloc(sizeof(s32)*ARRAY_SIZE(s5k83a_ctrls), GFP_KERNEL);
+ if (!sens_priv->settings) {
+ kfree(sens_priv);
+ return -ENOMEM;
+ }
+
+ sd->gspca_dev.cam.cam_mode = s5k83a_modes;
+ sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k83a_modes);
+ sd->desc->ctrls = s5k83a_ctrls;
+ sd->desc->nctrls = ARRAY_SIZE(s5k83a_ctrls);
+
+ /* null the pointer! thread is't running now */
+ sens_priv->rotation_thread = NULL;
+
+ for (i = 0; i < ARRAY_SIZE(s5k83a_ctrls); i++)
+ sens_priv->settings[i] = s5k83a_ctrls[i].qctrl.default_value;
+
+ sd->sensor_priv = sens_priv;
+ return 0;
+}
+
+int s5k83a_init(struct sd *sd)
+{
+ int i, err = 0;
+ s32 *sensor_settings =
+ ((struct s5k83a_priv *) sd->sensor_priv)->settings;
+
+ for (i = 0; i < ARRAY_SIZE(init_s5k83a) && !err; i++) {
+ u8 data[2] = {0x00, 0x00};
+
+ switch (init_s5k83a[i][0]) {
+ case BRIDGE:
+ err = m5602_write_bridge(sd,
+ init_s5k83a[i][1],
+ init_s5k83a[i][2]);
+ break;
+
+ case SENSOR:
+ data[0] = init_s5k83a[i][2];
+ err = m5602_write_sensor(sd,
+ init_s5k83a[i][1], data, 1);
+ break;
+
+ case SENSOR_LONG:
+ data[0] = init_s5k83a[i][2];
+ data[1] = init_s5k83a[i][3];
+ err = m5602_write_sensor(sd,
+ init_s5k83a[i][1], data, 2);
+ break;
+ default:
+ pr_info("Invalid stream command, exiting init\n");
+ return -EINVAL;
+ }
+ }
+
+ if (dump_sensor)
+ s5k83a_dump_registers(sd);
+
+ err = s5k83a_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]);
+ if (err < 0)
+ return err;
+
+ err = s5k83a_set_brightness(&sd->gspca_dev,
+ sensor_settings[BRIGHTNESS_IDX]);
+ if (err < 0)
+ return err;
+
+ err = s5k83a_set_exposure(&sd->gspca_dev,
+ sensor_settings[EXPOSURE_IDX]);
+ if (err < 0)
+ return err;
+
+ err = s5k83a_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]);
+ if (err < 0)
+ return err;
+
+ err = s5k83a_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]);
+
+ return err;
+}
+
+static int rotation_thread_function(void *data)
+{
+ struct sd *sd = (struct sd *) data;
+ struct s5k83a_priv *sens_priv = sd->sensor_priv;
+ u8 reg, previous_rotation = 0;
+ __s32 vflip, hflip;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ while (!schedule_timeout(100)) {
+ if (mutex_lock_interruptible(&sd->gspca_dev.usb_lock))
+ break;
+
+ s5k83a_get_rotation(sd, &reg);
+ if (previous_rotation != reg) {
+ previous_rotation = reg;
+ pr_info("Camera was flipped\n");
+
+ s5k83a_get_vflip((struct gspca_dev *) sd, &vflip);
+ s5k83a_get_hflip((struct gspca_dev *) sd, &hflip);
+
+ if (reg) {
+ vflip = !vflip;
+ hflip = !hflip;
+ }
+ s5k83a_set_flip_real((struct gspca_dev *) sd,
+ vflip, hflip);
+ }
+
+ mutex_unlock(&sd->gspca_dev.usb_lock);
+ set_current_state(TASK_INTERRUPTIBLE);
+ }
+
+ /* return to "front" flip */
+ if (previous_rotation) {
+ s5k83a_get_vflip((struct gspca_dev *) sd, &vflip);
+ s5k83a_get_hflip((struct gspca_dev *) sd, &hflip);
+ s5k83a_set_flip_real((struct gspca_dev *) sd, vflip, hflip);
+ }
+
+ sens_priv->rotation_thread = NULL;
+ return 0;
+}
+
+int s5k83a_start(struct sd *sd)
+{
+ int i, err = 0;
+ struct s5k83a_priv *sens_priv = sd->sensor_priv;
+
+ /* Create another thread, polling the GPIO ports of the camera to check
+ if it got rotated. This is how the windows driver does it so we have
+ to assume that there is no better way of accomplishing this */
+ sens_priv->rotation_thread = kthread_create(rotation_thread_function,
+ sd, "rotation thread");
+ wake_up_process(sens_priv->rotation_thread);
+
+ /* Preinit the sensor */
+ for (i = 0; i < ARRAY_SIZE(start_s5k83a) && !err; i++) {
+ u8 data[2] = {start_s5k83a[i][2], start_s5k83a[i][3]};
+ if (start_s5k83a[i][0] == SENSOR)
+ err = m5602_write_sensor(sd, start_s5k83a[i][1],
+ data, 2);
+ else
+ err = m5602_write_bridge(sd, start_s5k83a[i][1],
+ data[0]);
+ }
+ if (err < 0)
+ return err;
+
+ return s5k83a_set_led_indication(sd, 1);
+}
+
+int s5k83a_stop(struct sd *sd)
+{
+ struct s5k83a_priv *sens_priv = sd->sensor_priv;
+
+ if (sens_priv->rotation_thread)
+ kthread_stop(sens_priv->rotation_thread);
+
+ return s5k83a_set_led_indication(sd, 0);
+}
+
+void s5k83a_disconnect(struct sd *sd)
+{
+ struct s5k83a_priv *sens_priv = sd->sensor_priv;
+
+ s5k83a_stop(sd);
+
+ sd->sensor = NULL;
+ kfree(sens_priv->settings);
+ kfree(sens_priv);
+}
+
+static int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct s5k83a_priv *sens_priv = sd->sensor_priv;
+
+ *val = sens_priv->settings[GAIN_IDX];
+ return 0;
+}
+
+static int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 data[2];
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct s5k83a_priv *sens_priv = sd->sensor_priv;
+
+ sens_priv->settings[GAIN_IDX] = val;
+
+ data[0] = 0x00;
+ data[1] = 0x20;
+ err = m5602_write_sensor(sd, 0x14, data, 2);
+ if (err < 0)
+ return err;
+
+ data[0] = 0x01;
+ data[1] = 0x00;
+ err = m5602_write_sensor(sd, 0x0d, data, 2);
+ if (err < 0)
+ return err;
+
+ /* FIXME: This is not sane, we need to figure out the composition
+ of these registers */
+ data[0] = val >> 3; /* gain, high 5 bits */
+ data[1] = val >> 1; /* gain, high 7 bits */
+ err = m5602_write_sensor(sd, S5K83A_GAIN, data, 2);
+
+ return err;
+}
+
+static int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct s5k83a_priv *sens_priv = sd->sensor_priv;
+
+ *val = sens_priv->settings[BRIGHTNESS_IDX];
+ return 0;
+}
+
+static int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 data[1];
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct s5k83a_priv *sens_priv = sd->sensor_priv;
+
+ sens_priv->settings[BRIGHTNESS_IDX] = val;
+ data[0] = val;
+ err = m5602_write_sensor(sd, S5K83A_BRIGHTNESS, data, 1);
+ return err;
+}
+
+static int s5k83a_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct s5k83a_priv *sens_priv = sd->sensor_priv;
+
+ *val = sens_priv->settings[EXPOSURE_IDX];
+ return 0;
+}
+
+static int s5k83a_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 data[2];
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct s5k83a_priv *sens_priv = sd->sensor_priv;
+
+ sens_priv->settings[EXPOSURE_IDX] = val;
+ data[0] = 0;
+ data[1] = val;
+ err = m5602_write_sensor(sd, S5K83A_EXPOSURE, data, 2);
+ return err;
+}
+
+static int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct s5k83a_priv *sens_priv = sd->sensor_priv;
+
+ *val = sens_priv->settings[VFLIP_IDX];
+ return 0;
+}
+
+static int s5k83a_set_flip_real(struct gspca_dev *gspca_dev,
+ __s32 vflip, __s32 hflip)
+{
+ int err;
+ u8 data[1];
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ data[0] = 0x05;
+ err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
+ if (err < 0)
+ return err;
+
+ /* six bit is vflip, seven is hflip */
+ data[0] = S5K83A_FLIP_MASK;
+ data[0] = (vflip) ? data[0] | 0x40 : data[0];
+ data[0] = (hflip) ? data[0] | 0x80 : data[0];
+
+ err = m5602_write_sensor(sd, S5K83A_FLIP, data, 1);
+ if (err < 0)
+ return err;
+
+ data[0] = (vflip) ? 0x0b : 0x0a;
+ err = m5602_write_sensor(sd, S5K83A_VFLIP_TUNE, data, 1);
+ if (err < 0)
+ return err;
+
+ data[0] = (hflip) ? 0x0a : 0x0b;
+ err = m5602_write_sensor(sd, S5K83A_HFLIP_TUNE, data, 1);
+ return err;
+}
+
+static int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 reg;
+ __s32 hflip;
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct s5k83a_priv *sens_priv = sd->sensor_priv;
+
+ sens_priv->settings[VFLIP_IDX] = val;
+
+ s5k83a_get_hflip(gspca_dev, &hflip);
+
+ err = s5k83a_get_rotation(sd, &reg);
+ if (err < 0)
+ return err;
+ if (reg) {
+ val = !val;
+ hflip = !hflip;
+ }
+
+ err = s5k83a_set_flip_real(gspca_dev, val, hflip);
+ return err;
+}
+
+static int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct s5k83a_priv *sens_priv = sd->sensor_priv;
+
+ *val = sens_priv->settings[HFLIP_IDX];
+ return 0;
+}
+
+static int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 reg;
+ __s32 vflip;
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct s5k83a_priv *sens_priv = sd->sensor_priv;
+
+ sens_priv->settings[HFLIP_IDX] = val;
+
+ s5k83a_get_vflip(gspca_dev, &vflip);
+
+ err = s5k83a_get_rotation(sd, &reg);
+ if (err < 0)
+ return err;
+ if (reg) {
+ val = !val;
+ vflip = !vflip;
+ }
+
+ err = s5k83a_set_flip_real(gspca_dev, vflip, val);
+ return err;
+}
+
+static int s5k83a_set_led_indication(struct sd *sd, u8 val)
+{
+ int err = 0;
+ u8 data[1];
+
+ err = m5602_read_bridge(sd, M5602_XB_GPIO_DAT, data);
+ if (err < 0)
+ return err;
+
+ if (val)
+ data[0] = data[0] | S5K83A_GPIO_LED_MASK;
+ else
+ data[0] = data[0] & ~S5K83A_GPIO_LED_MASK;
+
+ err = m5602_write_bridge(sd, M5602_XB_GPIO_DAT, data[0]);
+
+ return err;
+}
+
+/* Get camera rotation on Acer notebooks */
+static int s5k83a_get_rotation(struct sd *sd, u8 *reg_data)
+{
+ int err = m5602_read_bridge(sd, M5602_XB_GPIO_DAT, reg_data);
+ *reg_data = (*reg_data & S5K83A_GPIO_ROTATION_MASK) ? 0 : 1;
+ return err;
+}
+
+static void s5k83a_dump_registers(struct sd *sd)
+{
+ int address;
+ u8 page, old_page;
+ m5602_read_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
+
+ for (page = 0; page < 16; page++) {
+ m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
+ pr_info("Dumping the s5k83a register state for page 0x%x\n",
+ page);
+ for (address = 0; address <= 0xff; address++) {
+ u8 val = 0;
+ m5602_read_sensor(sd, address, &val, 1);
+ pr_info("register 0x%x contains 0x%x\n", address, val);
+ }
+ }
+ pr_info("s5k83a register state dump complete\n");
+
+ for (page = 0; page < 16; page++) {
+ m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
+ pr_info("Probing for which registers that are read/write for page 0x%x\n",
+ page);
+ for (address = 0; address <= 0xff; address++) {
+ u8 old_val, ctrl_val, test_val = 0xff;
+
+ m5602_read_sensor(sd, address, &old_val, 1);
+ m5602_write_sensor(sd, address, &test_val, 1);
+ m5602_read_sensor(sd, address, &ctrl_val, 1);
+
+ if (ctrl_val == test_val)
+ pr_info("register 0x%x is writeable\n",
+ address);
+ else
+ pr_info("register 0x%x is read only\n",
+ address);
+
+ /* Restore original val */
+ m5602_write_sensor(sd, address, &old_val, 1);
+ }
+ }
+ pr_info("Read/write register probing complete\n");
+ m5602_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
+}
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k83a.h b/drivers/media/video/gspca/m5602/m5602_s5k83a.h
new file mode 100644
index 00000000..79952247
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/m5602_s5k83a.h
@@ -0,0 +1,193 @@
+/*
+ * Driver for the s5k83a sensor
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef M5602_S5K83A_H_
+#define M5602_S5K83A_H_
+
+#include "m5602_sensor.h"
+
+#define S5K83A_FLIP 0x01
+#define S5K83A_HFLIP_TUNE 0x03
+#define S5K83A_VFLIP_TUNE 0x05
+#define S5K83A_BRIGHTNESS 0x0a
+#define S5K83A_EXPOSURE 0x18
+#define S5K83A_GAIN 0x1b
+#define S5K83A_PAGE_MAP 0xec
+
+#define S5K83A_DEFAULT_GAIN 0x71
+#define S5K83A_DEFAULT_BRIGHTNESS 0x7e
+#define S5K83A_DEFAULT_EXPOSURE 0x00
+#define S5K83A_MAXIMUM_EXPOSURE 0x3c
+#define S5K83A_FLIP_MASK 0x10
+#define S5K83A_GPIO_LED_MASK 0x10
+#define S5K83A_GPIO_ROTATION_MASK 0x40
+
+/*****************************************************************************/
+
+/* Kernel module parameters */
+extern int force_sensor;
+extern bool dump_sensor;
+
+int s5k83a_probe(struct sd *sd);
+int s5k83a_init(struct sd *sd);
+int s5k83a_start(struct sd *sd);
+int s5k83a_stop(struct sd *sd);
+void s5k83a_disconnect(struct sd *sd);
+
+static const struct m5602_sensor s5k83a = {
+ .name = "S5K83A",
+ .probe = s5k83a_probe,
+ .init = s5k83a_init,
+ .start = s5k83a_start,
+ .stop = s5k83a_stop,
+ .disconnect = s5k83a_disconnect,
+ .i2c_slave_id = 0x5a,
+ .i2c_regW = 2,
+};
+
+struct s5k83a_priv {
+ /* We use another thread periodically
+ probing the orientation of the camera */
+ struct task_struct *rotation_thread;
+ s32 *settings;
+};
+
+static const unsigned char preinit_s5k83a[][4] = {
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
+
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
+};
+
+/* This could probably be considerably shortened.
+ I don't have the hardware to experiment with it, patches welcome
+*/
+static const unsigned char init_s5k83a[][4] = {
+ /* The following sequence is useless after a clean boot
+ but is necessary after resume from suspend */
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
+
+ {SENSOR, S5K83A_PAGE_MAP, 0x04, 0x00},
+ {SENSOR, 0xaf, 0x01, 0x00},
+ {SENSOR, S5K83A_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, 0x7b, 0xff, 0x00},
+ {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+ {SENSOR, 0x01, 0x50, 0x00},
+ {SENSOR, 0x12, 0x20, 0x00},
+ {SENSOR, 0x17, 0x40, 0x00},
+ {SENSOR, 0x1c, 0x00, 0x00},
+ {SENSOR, 0x02, 0x70, 0x00},
+ {SENSOR, 0x03, 0x0b, 0x00},
+ {SENSOR, 0x04, 0xf0, 0x00},
+ {SENSOR, 0x05, 0x0b, 0x00},
+ {SENSOR, 0x06, 0x71, 0x00},
+ {SENSOR, 0x07, 0xe8, 0x00}, /* 488 */
+ {SENSOR, 0x08, 0x02, 0x00},
+ {SENSOR, 0x09, 0x88, 0x00}, /* 648 */
+ {SENSOR, 0x14, 0x00, 0x00},
+ {SENSOR, 0x15, 0x20, 0x00}, /* 32 */
+ {SENSOR, 0x19, 0x00, 0x00},
+ {SENSOR, 0x1a, 0x98, 0x00}, /* 152 */
+ {SENSOR, 0x0f, 0x02, 0x00},
+ {SENSOR, 0x10, 0xe5, 0x00}, /* 741 */
+ /* normal colors
+ (this is value after boot, but after tries can be different) */
+ {SENSOR, 0x00, 0x06, 0x00},
+};
+
+static const unsigned char start_s5k83a[][4] = {
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00}, /* 484 */
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00}, /* 639 */
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+};
+#endif
diff --git a/drivers/media/video/gspca/m5602/m5602_sensor.h b/drivers/media/video/gspca/m5602/m5602_sensor.h
new file mode 100644
index 00000000..edff4f1f
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/m5602_sensor.h
@@ -0,0 +1,70 @@
+/*
+ * USB Driver for ALi m5602 based webcams
+ *
+ * Copyright (C) 2008 Erik Andrén
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#ifndef M5602_SENSOR_H_
+#define M5602_SENSOR_H_
+
+#include "m5602_bridge.h"
+
+#define M5602_V4L2_CID_GREEN_BALANCE (V4L2_CID_PRIVATE_BASE + 0)
+#define M5602_V4L2_CID_NOISE_SUPPRESION (V4L2_CID_PRIVATE_BASE + 1)
+
+/* Enumerates all supported sensors */
+enum sensors {
+ OV9650_SENSOR = 1,
+ S5K83A_SENSOR = 2,
+ S5K4AA_SENSOR = 3,
+ MT9M111_SENSOR = 4,
+ PO1030_SENSOR = 5,
+ OV7660_SENSOR = 6,
+};
+
+/* Enumerates all possible instruction types */
+enum instruction {
+ BRIDGE,
+ SENSOR,
+ SENSOR_LONG
+};
+
+struct m5602_sensor {
+ /* Defines the name of a sensor */
+ char name[32];
+
+ /* What i2c address the sensor is connected to */
+ u8 i2c_slave_id;
+
+ /* Width of each i2c register (in bytes) */
+ u8 i2c_regW;
+
+ /* Probes if the sensor is connected */
+ int (*probe)(struct sd *sd);
+
+ /* Performs a initialization sequence */
+ int (*init)(struct sd *sd);
+
+ /* Executed when the camera starts to send data */
+ int (*start)(struct sd *sd);
+
+ /* Executed when the camera ends to send data */
+ int (*stop)(struct sd *sd);
+
+ /* Executed when the device is disconnected */
+ void (*disconnect)(struct sd *sd);
+};
+
+#endif
diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c
new file mode 100644
index 00000000..b0231465
--- /dev/null
+++ b/drivers/media/video/gspca/mars.c
@@ -0,0 +1,519 @@
+/*
+ * Mars-Semi MR97311A library
+ * Copyright (C) 2005 <bradlch@hotmail.com>
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "mars"
+
+#include "gspca.h"
+#include "jpeg.h"
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("GSPCA/Mars USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* controls */
+enum e_ctrl {
+ BRIGHTNESS,
+ COLORS,
+ GAMMA,
+ SHARPNESS,
+ ILLUM_TOP,
+ ILLUM_BOT,
+ NCTRLS /* number of controls */
+};
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ struct gspca_ctrl ctrls[NCTRLS];
+
+ u8 quality;
+#define QUALITY_MIN 40
+#define QUALITY_MAX 70
+#define QUALITY_DEF 50
+
+ u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+
+/* V4L2 controls supported by the driver */
+static void setbrightness(struct gspca_dev *gspca_dev);
+static void setcolors(struct gspca_dev *gspca_dev);
+static void setgamma(struct gspca_dev *gspca_dev);
+static void setsharpness(struct gspca_dev *gspca_dev);
+static int sd_setilluminator1(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_setilluminator2(struct gspca_dev *gspca_dev, __s32 val);
+
+static const struct ctrl sd_ctrls[NCTRLS] = {
+[BRIGHTNESS] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 30,
+ .step = 1,
+ .default_value = 15,
+ },
+ .set_control = setbrightness
+ },
+[COLORS] = {
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Color",
+ .minimum = 1,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 200,
+ },
+ .set_control = setcolors
+ },
+[GAMMA] = {
+ {
+ .id = V4L2_CID_GAMMA,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gamma",
+ .minimum = 0,
+ .maximum = 3,
+ .step = 1,
+ .default_value = 1,
+ },
+ .set_control = setgamma
+ },
+[SHARPNESS] = {
+ {
+ .id = V4L2_CID_SHARPNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Sharpness",
+ .minimum = 0,
+ .maximum = 2,
+ .step = 1,
+ .default_value = 1,
+ },
+ .set_control = setsharpness
+ },
+[ILLUM_TOP] = {
+ {
+ .id = V4L2_CID_ILLUMINATORS_1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Top illuminator",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = V4L2_CTRL_FLAG_UPDATE,
+ },
+ .set = sd_setilluminator1
+ },
+[ILLUM_BOT] = {
+ {
+ .id = V4L2_CID_ILLUMINATORS_2,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Bottom illuminator",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = V4L2_CTRL_FLAG_UPDATE,
+ },
+ .set = sd_setilluminator2
+ },
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 2},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+};
+
+static const __u8 mi_data[0x20] = {
+/* 01 02 03 04 05 06 07 08 */
+ 0x48, 0x22, 0x01, 0x47, 0x10, 0x00, 0x00, 0x00,
+/* 09 0a 0b 0c 0d 0e 0f 10 */
+ 0x00, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01,
+/* 11 12 13 14 15 16 17 18 */
+ 0x30, 0x00, 0x04, 0x00, 0x06, 0x01, 0xe2, 0x02,
+/* 19 1a 1b 1c 1d 1e 1f 20 */
+ 0x82, 0x00, 0x20, 0x17, 0x80, 0x08, 0x0c, 0x00
+};
+
+/* write <len> bytes from gspca_dev->usb_buf */
+static void reg_w(struct gspca_dev *gspca_dev,
+ int len)
+{
+ int alen, ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+
+ ret = usb_bulk_msg(gspca_dev->dev,
+ usb_sndbulkpipe(gspca_dev->dev, 4),
+ gspca_dev->usb_buf,
+ len,
+ &alen,
+ 500); /* timeout in milliseconds */
+ if (ret < 0) {
+ pr_err("reg write [%02x] error %d\n",
+ gspca_dev->usb_buf[0], ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static void mi_w(struct gspca_dev *gspca_dev,
+ u8 addr,
+ u8 value)
+{
+ gspca_dev->usb_buf[0] = 0x1f;
+ gspca_dev->usb_buf[1] = 0; /* control byte */
+ gspca_dev->usb_buf[2] = addr;
+ gspca_dev->usb_buf[3] = value;
+
+ reg_w(gspca_dev, 4);
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ gspca_dev->usb_buf[0] = 0x61;
+ gspca_dev->usb_buf[1] = sd->ctrls[BRIGHTNESS].val;
+ reg_w(gspca_dev, 2);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s16 val;
+
+ val = sd->ctrls[COLORS].val;
+ gspca_dev->usb_buf[0] = 0x5f;
+ gspca_dev->usb_buf[1] = val << 3;
+ gspca_dev->usb_buf[2] = ((val >> 2) & 0xf8) | 0x04;
+ reg_w(gspca_dev, 3);
+}
+
+static void setgamma(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ gspca_dev->usb_buf[0] = 0x06;
+ gspca_dev->usb_buf[1] = sd->ctrls[GAMMA].val * 0x40;
+ reg_w(gspca_dev, 2);
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ gspca_dev->usb_buf[0] = 0x67;
+ gspca_dev->usb_buf[1] = sd->ctrls[SHARPNESS].val * 4 + 3;
+ reg_w(gspca_dev, 2);
+}
+
+static void setilluminators(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ gspca_dev->usb_buf[0] = 0x22;
+ if (sd->ctrls[ILLUM_TOP].val)
+ gspca_dev->usb_buf[1] = 0x76;
+ else if (sd->ctrls[ILLUM_BOT].val)
+ gspca_dev->usb_buf[1] = 0x7a;
+ else
+ gspca_dev->usb_buf[1] = 0x7e;
+ reg_w(gspca_dev, 2);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+ cam->ctrls = sd->ctrls;
+ sd->quality = QUALITY_DEF;
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ gspca_dev->ctrl_inac = (1 << ILLUM_TOP) | (1 << ILLUM_BOT);
+ return 0;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 *data;
+ int i;
+
+ /* create the JPEG header */
+ jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
+ 0x21); /* JPEG 422 */
+ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+
+ data = gspca_dev->usb_buf;
+
+ data[0] = 0x01; /* address */
+ data[1] = 0x01;
+ reg_w(gspca_dev, 2);
+
+ /*
+ Initialize the MR97113 chip register
+ */
+ data[0] = 0x00; /* address */
+ data[1] = 0x0c | 0x01; /* reg 0 */
+ data[2] = 0x01; /* reg 1 */
+ data[3] = gspca_dev->width / 8; /* h_size , reg 2 */
+ data[4] = gspca_dev->height / 8; /* v_size , reg 3 */
+ data[5] = 0x30; /* reg 4, MI, PAS5101 :
+ * 0x30 for 24mhz , 0x28 for 12mhz */
+ data[6] = 0x02; /* reg 5, H start - was 0x04 */
+ data[7] = sd->ctrls[GAMMA].val * 0x40; /* reg 0x06: gamma */
+ data[8] = 0x01; /* reg 7, V start - was 0x03 */
+/* if (h_size == 320 ) */
+/* data[9]= 0x56; * reg 8, 24MHz, 2:1 scale down */
+/* else */
+ data[9] = 0x52; /* reg 8, 24MHz, no scale down */
+/*jfm: from win trace*/
+ data[10] = 0x18;
+
+ reg_w(gspca_dev, 11);
+
+ data[0] = 0x23; /* address */
+ data[1] = 0x09; /* reg 35, append frame header */
+
+ reg_w(gspca_dev, 2);
+
+ data[0] = 0x3c; /* address */
+/* if (gspca_dev->width == 1280) */
+/* data[1] = 200; * reg 60, pc-cam frame size
+ * (unit: 4KB) 800KB */
+/* else */
+ data[1] = 50; /* 50 reg 60, pc-cam frame size
+ * (unit: 4KB) 200KB */
+ reg_w(gspca_dev, 2);
+
+ /* auto dark-gain */
+ data[0] = 0x5e; /* address */
+ data[1] = 0; /* reg 94, Y Gain (auto) */
+/*jfm: from win trace*/
+ /* reg 0x5f/0x60 (LE) = saturation */
+ /* h (60): xxxx x100
+ * l (5f): xxxx x000 */
+ data[2] = sd->ctrls[COLORS].val << 3;
+ data[3] = ((sd->ctrls[COLORS].val >> 2) & 0xf8) | 0x04;
+ data[4] = sd->ctrls[BRIGHTNESS].val; /* reg 0x61 = brightness */
+ data[5] = 0x00;
+
+ reg_w(gspca_dev, 6);
+
+ data[0] = 0x67;
+/*jfm: from win trace*/
+ data[1] = sd->ctrls[SHARPNESS].val * 4 + 3;
+ data[2] = 0x14;
+ reg_w(gspca_dev, 3);
+
+ data[0] = 0x69;
+ data[1] = 0x2f;
+ data[2] = 0x28;
+ data[3] = 0x42;
+ reg_w(gspca_dev, 4);
+
+ data[0] = 0x63;
+ data[1] = 0x07;
+ reg_w(gspca_dev, 2);
+/*jfm: win trace - many writes here to reg 0x64*/
+
+ /* initialize the MI sensor */
+ for (i = 0; i < sizeof mi_data; i++)
+ mi_w(gspca_dev, i + 1, mi_data[i]);
+
+ data[0] = 0x00;
+ data[1] = 0x4d; /* ISOC transferring enable... */
+ reg_w(gspca_dev, 2);
+
+ gspca_dev->ctrl_inac = 0; /* activate the illuminator controls */
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ gspca_dev->ctrl_inac = (1 << ILLUM_TOP) | (1 << ILLUM_BOT);
+ if (sd->ctrls[ILLUM_TOP].val || sd->ctrls[ILLUM_BOT].val) {
+ sd->ctrls[ILLUM_TOP].val = 0;
+ sd->ctrls[ILLUM_BOT].val = 0;
+ setilluminators(gspca_dev);
+ msleep(20);
+ }
+
+ gspca_dev->usb_buf[0] = 1;
+ gspca_dev->usb_buf[1] = 0;
+ reg_w(gspca_dev, 2);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int p;
+
+ if (len < 6) {
+/* gspca_dev->last_packet_type = DISCARD_PACKET; */
+ return;
+ }
+ for (p = 0; p < len - 6; p++) {
+ if (data[0 + p] == 0xff
+ && data[1 + p] == 0xff
+ && data[2 + p] == 0x00
+ && data[3 + p] == 0xff
+ && data[4 + p] == 0x96) {
+ if (data[5 + p] == 0x64
+ || data[5 + p] == 0x65
+ || data[5 + p] == 0x66
+ || data[5 + p] == 0x67) {
+ PDEBUG(D_PACK, "sof offset: %d len: %d",
+ p, len);
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ data, p);
+
+ /* put the JPEG header */
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+ data += p + 16;
+ len -= p + 16;
+ break;
+ }
+ }
+ }
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static int sd_setilluminator1(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* only one illuminator may be on */
+ sd->ctrls[ILLUM_TOP].val = val;
+ if (val)
+ sd->ctrls[ILLUM_BOT].val = 0;
+ setilluminators(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_setilluminator2(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* only one illuminator may be on */
+ sd->ctrls[ILLUM_BOT].val = val;
+ if (val)
+ sd->ctrls[ILLUM_TOP].val = 0;
+ setilluminators(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_set_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (jcomp->quality < QUALITY_MIN)
+ sd->quality = QUALITY_MIN;
+ else if (jcomp->quality > QUALITY_MAX)
+ sd->quality = QUALITY_MAX;
+ else
+ sd->quality = jcomp->quality;
+ if (gspca_dev->streaming)
+ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+ return 0;
+}
+
+static int sd_get_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ memset(jcomp, 0, sizeof *jcomp);
+ jcomp->quality = sd->quality;
+ jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
+ | V4L2_JPEG_MARKER_DQT;
+ return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = NCTRLS,
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+ .get_jcomp = sd_get_jcomp,
+ .set_jcomp = sd_set_jcomp,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x093a, 0x050f)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/mr97310a.c b/drivers/media/video/gspca/mr97310a.c
new file mode 100644
index 00000000..d73e5bd3
--- /dev/null
+++ b/drivers/media/video/gspca/mr97310a.c
@@ -0,0 +1,1262 @@
+/*
+ * Mars MR97310A library
+ *
+ * The original mr97310a driver, which supported the Aiptek Pencam VGA+, is
+ * Copyright (C) 2009 Kyle Guinn <elyk03@gmail.com>
+ *
+ * Support for the MR97310A cameras in addition to the Aiptek Pencam VGA+
+ * and for the routines for detecting and classifying these various cameras,
+ * is Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu>
+ *
+ * Support for the control settings for the CIF cameras is
+ * Copyright (C) 2009 Hans de Goede <hdegoede@redhat.com> and
+ * Thomas Kaiser <thomas@kaiser-linux.li>
+ *
+ * Support for the control settings for the VGA cameras is
+ * Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu>
+ *
+ * Several previously unsupported cameras are owned and have been tested by
+ * Hans de Goede <hdegoede@redhat.com> and
+ * Thomas Kaiser <thomas@kaiser-linux.li> and
+ * Theodore Kilgore <kilgota@auburn.edu> and
+ * Edmond Rodriguez <erodrig_97@yahoo.com> and
+ * Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * The MR97311A support in gspca/mars.c has been helpful in understanding some
+ * of the registers in these cameras.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "mr97310a"
+
+#include "gspca.h"
+
+#define CAM_TYPE_CIF 0
+#define CAM_TYPE_VGA 1
+
+#define MR97310A_BRIGHTNESS_DEFAULT 0
+
+#define MR97310A_EXPOSURE_MIN 0
+#define MR97310A_EXPOSURE_MAX 4095
+#define MR97310A_EXPOSURE_DEFAULT 1000
+
+#define MR97310A_GAIN_MIN 0
+#define MR97310A_GAIN_MAX 31
+#define MR97310A_GAIN_DEFAULT 25
+
+#define MR97310A_CONTRAST_MIN 0
+#define MR97310A_CONTRAST_MAX 31
+#define MR97310A_CONTRAST_DEFAULT 23
+
+#define MR97310A_CS_GAIN_MIN 0
+#define MR97310A_CS_GAIN_MAX 0x7ff
+#define MR97310A_CS_GAIN_DEFAULT 0x110
+
+#define MR97310A_MIN_CLOCKDIV_MIN 3
+#define MR97310A_MIN_CLOCKDIV_MAX 8
+#define MR97310A_MIN_CLOCKDIV_DEFAULT 3
+
+MODULE_AUTHOR("Kyle Guinn <elyk03@gmail.com>,"
+ "Theodore Kilgore <kilgota@auburn.edu>");
+MODULE_DESCRIPTION("GSPCA/Mars-Semi MR97310A USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* global parameters */
+static int force_sensor_type = -1;
+module_param(force_sensor_type, int, 0644);
+MODULE_PARM_DESC(force_sensor_type, "Force sensor type (-1 (auto), 0 or 1)");
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ u8 sof_read;
+ u8 cam_type; /* 0 is CIF and 1 is VGA */
+ u8 sensor_type; /* We use 0 and 1 here, too. */
+ u8 do_lcd_stop;
+ u8 adj_colors;
+
+ int brightness;
+ u16 exposure;
+ u32 gain;
+ u8 contrast;
+ u8 min_clockdiv;
+};
+
+struct sensor_w_data {
+ u8 reg;
+ u8 flags;
+ u8 data[16];
+ int len;
+};
+
+static void sd_stopN(struct gspca_dev *gspca_dev);
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setmin_clockdiv(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getmin_clockdiv(struct gspca_dev *gspca_dev, __s32 *val);
+static void setbrightness(struct gspca_dev *gspca_dev);
+static void setexposure(struct gspca_dev *gspca_dev);
+static void setgain(struct gspca_dev *gspca_dev);
+static void setcontrast(struct gspca_dev *gspca_dev);
+
+/* V4L2 controls supported by the driver */
+static const struct ctrl sd_ctrls[] = {
+/* Separate brightness control description for Argus QuickClix as it has
+ * different limits from the other mr97310a cameras, and separate gain
+ * control for Sakar CyberPix camera. */
+ {
+#define NORM_BRIGHTNESS_IDX 0
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = -254,
+ .maximum = 255,
+ .step = 1,
+ .default_value = MR97310A_BRIGHTNESS_DEFAULT,
+ .flags = 0,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+ {
+#define ARGUS_QC_BRIGHTNESS_IDX 1
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 15,
+ .step = 1,
+ .default_value = MR97310A_BRIGHTNESS_DEFAULT,
+ .flags = 0,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+ {
+#define EXPOSURE_IDX 2
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = MR97310A_EXPOSURE_MIN,
+ .maximum = MR97310A_EXPOSURE_MAX,
+ .step = 1,
+ .default_value = MR97310A_EXPOSURE_DEFAULT,
+ .flags = 0,
+ },
+ .set = sd_setexposure,
+ .get = sd_getexposure,
+ },
+ {
+#define GAIN_IDX 3
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = MR97310A_GAIN_MIN,
+ .maximum = MR97310A_GAIN_MAX,
+ .step = 1,
+ .default_value = MR97310A_GAIN_DEFAULT,
+ .flags = 0,
+ },
+ .set = sd_setgain,
+ .get = sd_getgain,
+ },
+ {
+#define SAKAR_CS_GAIN_IDX 4
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = MR97310A_CS_GAIN_MIN,
+ .maximum = MR97310A_CS_GAIN_MAX,
+ .step = 1,
+ .default_value = MR97310A_CS_GAIN_DEFAULT,
+ .flags = 0,
+ },
+ .set = sd_setgain,
+ .get = sd_getgain,
+ },
+ {
+#define CONTRAST_IDX 5
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = MR97310A_CONTRAST_MIN,
+ .maximum = MR97310A_CONTRAST_MAX,
+ .step = 1,
+ .default_value = MR97310A_CONTRAST_DEFAULT,
+ .flags = 0,
+ },
+ .set = sd_setcontrast,
+ .get = sd_getcontrast,
+ },
+ {
+#define MIN_CLOCKDIV_IDX 6
+ {
+ .id = V4L2_CID_PRIVATE_BASE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Minimum Clock Divider",
+ .minimum = MR97310A_MIN_CLOCKDIV_MIN,
+ .maximum = MR97310A_MIN_CLOCKDIV_MAX,
+ .step = 1,
+ .default_value = MR97310A_MIN_CLOCKDIV_DEFAULT,
+ .flags = 0,
+ },
+ .set = sd_setmin_clockdiv,
+ .get = sd_getmin_clockdiv,
+ },
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {160, 120, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 4},
+ {176, 144, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 3},
+ {320, 240, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 2},
+ {352, 288, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+
+/* the bytes to write are in gspca_dev->usb_buf */
+static int mr_write(struct gspca_dev *gspca_dev, int len)
+{
+ int rc;
+
+ rc = usb_bulk_msg(gspca_dev->dev,
+ usb_sndbulkpipe(gspca_dev->dev, 4),
+ gspca_dev->usb_buf, len, NULL, 500);
+ if (rc < 0)
+ pr_err("reg write [%02x] error %d\n",
+ gspca_dev->usb_buf[0], rc);
+ return rc;
+}
+
+/* the bytes are read into gspca_dev->usb_buf */
+static int mr_read(struct gspca_dev *gspca_dev, int len)
+{
+ int rc;
+
+ rc = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 3),
+ gspca_dev->usb_buf, len, NULL, 500);
+ if (rc < 0)
+ pr_err("reg read [%02x] error %d\n",
+ gspca_dev->usb_buf[0], rc);
+ return rc;
+}
+
+static int sensor_write_reg(struct gspca_dev *gspca_dev, u8 reg, u8 flags,
+ const u8 *data, int len)
+{
+ gspca_dev->usb_buf[0] = 0x1f;
+ gspca_dev->usb_buf[1] = flags;
+ gspca_dev->usb_buf[2] = reg;
+ memcpy(gspca_dev->usb_buf + 3, data, len);
+
+ return mr_write(gspca_dev, len + 3);
+}
+
+static int sensor_write_regs(struct gspca_dev *gspca_dev,
+ const struct sensor_w_data *data, int len)
+{
+ int i, rc;
+
+ for (i = 0; i < len; i++) {
+ rc = sensor_write_reg(gspca_dev, data[i].reg, data[i].flags,
+ data[i].data, data[i].len);
+ if (rc < 0)
+ return rc;
+ }
+
+ return 0;
+}
+
+static int sensor_write1(struct gspca_dev *gspca_dev, u8 reg, u8 data)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 buf, confirm_reg;
+ int rc;
+
+ buf = data;
+ if (sd->cam_type == CAM_TYPE_CIF) {
+ rc = sensor_write_reg(gspca_dev, reg, 0x01, &buf, 1);
+ confirm_reg = sd->sensor_type ? 0x13 : 0x11;
+ } else {
+ rc = sensor_write_reg(gspca_dev, reg, 0x00, &buf, 1);
+ confirm_reg = 0x11;
+ }
+ if (rc < 0)
+ return rc;
+
+ buf = 0x01;
+ rc = sensor_write_reg(gspca_dev, confirm_reg, 0x00, &buf, 1);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+static int cam_get_response16(struct gspca_dev *gspca_dev, u8 reg, int verbose)
+{
+ int err_code;
+
+ gspca_dev->usb_buf[0] = reg;
+ err_code = mr_write(gspca_dev, 1);
+ if (err_code < 0)
+ return err_code;
+
+ err_code = mr_read(gspca_dev, 16);
+ if (err_code < 0)
+ return err_code;
+
+ if (verbose)
+ PDEBUG(D_PROBE, "Register: %02x reads %02x%02x%02x", reg,
+ gspca_dev->usb_buf[0],
+ gspca_dev->usb_buf[1],
+ gspca_dev->usb_buf[2]);
+
+ return 0;
+}
+
+static int zero_the_pointer(struct gspca_dev *gspca_dev)
+{
+ __u8 *data = gspca_dev->usb_buf;
+ int err_code;
+ u8 status = 0;
+ int tries = 0;
+
+ err_code = cam_get_response16(gspca_dev, 0x21, 0);
+ if (err_code < 0)
+ return err_code;
+
+ data[0] = 0x19;
+ data[1] = 0x51;
+ err_code = mr_write(gspca_dev, 2);
+ if (err_code < 0)
+ return err_code;
+
+ err_code = cam_get_response16(gspca_dev, 0x21, 0);
+ if (err_code < 0)
+ return err_code;
+
+ data[0] = 0x19;
+ data[1] = 0xba;
+ err_code = mr_write(gspca_dev, 2);
+ if (err_code < 0)
+ return err_code;
+
+ err_code = cam_get_response16(gspca_dev, 0x21, 0);
+ if (err_code < 0)
+ return err_code;
+
+ data[0] = 0x19;
+ data[1] = 0x00;
+ err_code = mr_write(gspca_dev, 2);
+ if (err_code < 0)
+ return err_code;
+
+ err_code = cam_get_response16(gspca_dev, 0x21, 0);
+ if (err_code < 0)
+ return err_code;
+
+ data[0] = 0x19;
+ data[1] = 0x00;
+ err_code = mr_write(gspca_dev, 2);
+ if (err_code < 0)
+ return err_code;
+
+ while (status != 0x0a && tries < 256) {
+ err_code = cam_get_response16(gspca_dev, 0x21, 0);
+ status = data[0];
+ tries++;
+ if (err_code < 0)
+ return err_code;
+ }
+ if (status != 0x0a)
+ PDEBUG(D_ERR, "status is %02x", status);
+
+ tries = 0;
+ while (tries < 4) {
+ data[0] = 0x19;
+ data[1] = 0x00;
+ err_code = mr_write(gspca_dev, 2);
+ if (err_code < 0)
+ return err_code;
+
+ err_code = cam_get_response16(gspca_dev, 0x21, 0);
+ status = data[0];
+ tries++;
+ if (err_code < 0)
+ return err_code;
+ }
+
+ data[0] = 0x19;
+ err_code = mr_write(gspca_dev, 1);
+ if (err_code < 0)
+ return err_code;
+
+ err_code = mr_read(gspca_dev, 16);
+ if (err_code < 0)
+ return err_code;
+
+ return 0;
+}
+
+static int stream_start(struct gspca_dev *gspca_dev)
+{
+ gspca_dev->usb_buf[0] = 0x01;
+ gspca_dev->usb_buf[1] = 0x01;
+ return mr_write(gspca_dev, 2);
+}
+
+static void stream_stop(struct gspca_dev *gspca_dev)
+{
+ gspca_dev->usb_buf[0] = 0x01;
+ gspca_dev->usb_buf[1] = 0x00;
+ if (mr_write(gspca_dev, 2) < 0)
+ PDEBUG(D_ERR, "Stream Stop failed");
+}
+
+static void lcd_stop(struct gspca_dev *gspca_dev)
+{
+ gspca_dev->usb_buf[0] = 0x19;
+ gspca_dev->usb_buf[1] = 0x54;
+ if (mr_write(gspca_dev, 2) < 0)
+ PDEBUG(D_ERR, "LCD Stop failed");
+}
+
+static int isoc_enable(struct gspca_dev *gspca_dev)
+{
+ gspca_dev->usb_buf[0] = 0x00;
+ gspca_dev->usb_buf[1] = 0x4d; /* ISOC transferring enable... */
+ return mr_write(gspca_dev, 2);
+}
+
+/* This function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+ int gain_default = MR97310A_GAIN_DEFAULT;
+ int err_code;
+
+ cam = &gspca_dev->cam;
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+ sd->do_lcd_stop = 0;
+
+ /* Several of the supported CIF cameras share the same USB ID but
+ * require different initializations and different control settings.
+ * The same is true of the VGA cameras. Therefore, we are forced
+ * to start the initialization process in order to determine which
+ * camera is present. Some of the supported cameras require the
+ * memory pointer to be set to 0 as the very first item of business
+ * or else they will not stream. So we do that immediately.
+ */
+ err_code = zero_the_pointer(gspca_dev);
+ if (err_code < 0)
+ return err_code;
+
+ err_code = stream_start(gspca_dev);
+ if (err_code < 0)
+ return err_code;
+
+ /* Now, the query for sensor type. */
+ err_code = cam_get_response16(gspca_dev, 0x07, 1);
+ if (err_code < 0)
+ return err_code;
+
+ if (id->idProduct == 0x0110 || id->idProduct == 0x010e) {
+ sd->cam_type = CAM_TYPE_CIF;
+ cam->nmodes--;
+ /*
+ * All but one of the known CIF cameras share the same USB ID,
+ * but two different init routines are in use, and the control
+ * settings are different, too. We need to detect which camera
+ * of the two known varieties is connected!
+ *
+ * A list of known CIF cameras follows. They all report either
+ * 0200 for type 0 or 0300 for type 1.
+ * If you have another to report, please do
+ *
+ * Name sd->sensor_type reported by
+ *
+ * Sakar 56379 Spy-shot 0 T. Kilgore
+ * Innovage 0 T. Kilgore
+ * Vivitar Mini 0 H. De Goede
+ * Vivitar Mini 0 E. Rodriguez
+ * Vivitar Mini 1 T. Kilgore
+ * Elta-Media 8212dc 1 T. Kaiser
+ * Philips dig. keych. 1 T. Kilgore
+ * Trust Spyc@m 100 1 A. Jacobs
+ */
+ switch (gspca_dev->usb_buf[0]) {
+ case 2:
+ sd->sensor_type = 0;
+ break;
+ case 3:
+ sd->sensor_type = 1;
+ break;
+ default:
+ pr_err("Unknown CIF Sensor id : %02x\n",
+ gspca_dev->usb_buf[1]);
+ return -ENODEV;
+ }
+ PDEBUG(D_PROBE, "MR97310A CIF camera detected, sensor: %d",
+ sd->sensor_type);
+ } else {
+ sd->cam_type = CAM_TYPE_VGA;
+
+ /*
+ * Here is a table of the responses to the query for sensor
+ * type, from the known MR97310A VGA cameras. Six different
+ * cameras of which five share the same USB ID.
+ *
+ * Name gspca_dev->usb_buf[] sd->sensor_type
+ * sd->do_lcd_stop
+ * Aiptek Pencam VGA+ 0300 0 1
+ * ION digital 0300 0 1
+ * Argus DC-1620 0450 1 0
+ * Argus QuickClix 0420 1 1
+ * Sakar 77379 Digital 0350 0 1
+ * Sakar 1638x CyberPix 0120 0 2
+ *
+ * Based upon these results, we assume default settings
+ * and then correct as necessary, as follows.
+ *
+ */
+
+ sd->sensor_type = 1;
+ sd->do_lcd_stop = 0;
+ sd->adj_colors = 0;
+ if (gspca_dev->usb_buf[0] == 0x01) {
+ sd->sensor_type = 2;
+ } else if ((gspca_dev->usb_buf[0] != 0x03) &&
+ (gspca_dev->usb_buf[0] != 0x04)) {
+ pr_err("Unknown VGA Sensor id Byte 0: %02x\n",
+ gspca_dev->usb_buf[0]);
+ pr_err("Defaults assumed, may not work\n");
+ pr_err("Please report this\n");
+ }
+ /* Sakar Digital color needs to be adjusted. */
+ if ((gspca_dev->usb_buf[0] == 0x03) &&
+ (gspca_dev->usb_buf[1] == 0x50))
+ sd->adj_colors = 1;
+ if (gspca_dev->usb_buf[0] == 0x04) {
+ sd->do_lcd_stop = 1;
+ switch (gspca_dev->usb_buf[1]) {
+ case 0x50:
+ sd->sensor_type = 0;
+ PDEBUG(D_PROBE, "sensor_type corrected to 0");
+ break;
+ case 0x20:
+ /* Nothing to do here. */
+ break;
+ default:
+ pr_err("Unknown VGA Sensor id Byte 1: %02x\n",
+ gspca_dev->usb_buf[1]);
+ pr_err("Defaults assumed, may not work\n");
+ pr_err("Please report this\n");
+ }
+ }
+ PDEBUG(D_PROBE, "MR97310A VGA camera detected, sensor: %d",
+ sd->sensor_type);
+ }
+ /* Stop streaming as we've started it only to probe the sensor type. */
+ sd_stopN(gspca_dev);
+
+ if (force_sensor_type != -1) {
+ sd->sensor_type = !!force_sensor_type;
+ PDEBUG(D_PROBE, "Forcing sensor type to: %d",
+ sd->sensor_type);
+ }
+
+ /* Setup controls depending on camera type */
+ if (sd->cam_type == CAM_TYPE_CIF) {
+ /* No brightness for sensor_type 0 */
+ if (sd->sensor_type == 0)
+ gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
+ (1 << ARGUS_QC_BRIGHTNESS_IDX) |
+ (1 << CONTRAST_IDX) |
+ (1 << SAKAR_CS_GAIN_IDX);
+ else
+ gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX) |
+ (1 << CONTRAST_IDX) |
+ (1 << SAKAR_CS_GAIN_IDX) |
+ (1 << MIN_CLOCKDIV_IDX);
+ } else {
+ /* All controls need to be disabled if VGA sensor_type is 0 */
+ if (sd->sensor_type == 0)
+ gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
+ (1 << ARGUS_QC_BRIGHTNESS_IDX) |
+ (1 << EXPOSURE_IDX) |
+ (1 << GAIN_IDX) |
+ (1 << CONTRAST_IDX) |
+ (1 << SAKAR_CS_GAIN_IDX) |
+ (1 << MIN_CLOCKDIV_IDX);
+ else if (sd->sensor_type == 2) {
+ gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
+ (1 << ARGUS_QC_BRIGHTNESS_IDX) |
+ (1 << GAIN_IDX) |
+ (1 << MIN_CLOCKDIV_IDX);
+ gain_default = MR97310A_CS_GAIN_DEFAULT;
+ } else if (sd->do_lcd_stop)
+ /* Argus QuickClix has different brightness limits */
+ gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
+ (1 << CONTRAST_IDX) |
+ (1 << SAKAR_CS_GAIN_IDX);
+ else
+ gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX) |
+ (1 << CONTRAST_IDX) |
+ (1 << SAKAR_CS_GAIN_IDX);
+ }
+
+ sd->brightness = MR97310A_BRIGHTNESS_DEFAULT;
+ sd->exposure = MR97310A_EXPOSURE_DEFAULT;
+ sd->gain = gain_default;
+ sd->contrast = MR97310A_CONTRAST_DEFAULT;
+ sd->min_clockdiv = MR97310A_MIN_CLOCKDIV_DEFAULT;
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ return 0;
+}
+
+static int start_cif_cam(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u8 *data = gspca_dev->usb_buf;
+ int err_code;
+ static const __u8 startup_string[] = {
+ 0x00,
+ 0x0d,
+ 0x01,
+ 0x00, /* Hsize/8 for 352 or 320 */
+ 0x00, /* Vsize/4 for 288 or 240 */
+ 0x13, /* or 0xbb, depends on sensor */
+ 0x00, /* Hstart, depends on res. */
+ 0x00, /* reserved ? */
+ 0x00, /* Vstart, depends on res. and sensor */
+ 0x50, /* 0x54 to get 176 or 160 */
+ 0xc0
+ };
+
+ /* Note: Some of the above descriptions guessed from MR97113A driver */
+
+ memcpy(data, startup_string, 11);
+ if (sd->sensor_type)
+ data[5] = 0xbb;
+
+ switch (gspca_dev->width) {
+ case 160:
+ data[9] |= 0x04; /* reg 8, 2:1 scale down from 320 */
+ /* fall thru */
+ case 320:
+ default:
+ data[3] = 0x28; /* reg 2, H size/8 */
+ data[4] = 0x3c; /* reg 3, V size/4 */
+ data[6] = 0x14; /* reg 5, H start */
+ data[8] = 0x1a + sd->sensor_type; /* reg 7, V start */
+ break;
+ case 176:
+ data[9] |= 0x04; /* reg 8, 2:1 scale down from 352 */
+ /* fall thru */
+ case 352:
+ data[3] = 0x2c; /* reg 2, H size/8 */
+ data[4] = 0x48; /* reg 3, V size/4 */
+ data[6] = 0x06; /* reg 5, H start */
+ data[8] = 0x06 - sd->sensor_type; /* reg 7, V start */
+ break;
+ }
+ err_code = mr_write(gspca_dev, 11);
+ if (err_code < 0)
+ return err_code;
+
+ if (!sd->sensor_type) {
+ static const struct sensor_w_data cif_sensor0_init_data[] = {
+ {0x02, 0x00, {0x03, 0x5a, 0xb5, 0x01,
+ 0x0f, 0x14, 0x0f, 0x10}, 8},
+ {0x0c, 0x00, {0x04, 0x01, 0x01, 0x00, 0x1f}, 5},
+ {0x12, 0x00, {0x07}, 1},
+ {0x1f, 0x00, {0x06}, 1},
+ {0x27, 0x00, {0x04}, 1},
+ {0x29, 0x00, {0x0c}, 1},
+ {0x40, 0x00, {0x40, 0x00, 0x04}, 3},
+ {0x50, 0x00, {0x60}, 1},
+ {0x60, 0x00, {0x06}, 1},
+ {0x6b, 0x00, {0x85, 0x85, 0xc8, 0xc8, 0xc8, 0xc8}, 6},
+ {0x72, 0x00, {0x1e, 0x56}, 2},
+ {0x75, 0x00, {0x58, 0x40, 0xa2, 0x02, 0x31, 0x02,
+ 0x31, 0x80, 0x00}, 9},
+ {0x11, 0x00, {0x01}, 1},
+ {0, 0, {0}, 0}
+ };
+ err_code = sensor_write_regs(gspca_dev, cif_sensor0_init_data,
+ ARRAY_SIZE(cif_sensor0_init_data));
+ } else { /* sd->sensor_type = 1 */
+ static const struct sensor_w_data cif_sensor1_init_data[] = {
+ /* Reg 3,4, 7,8 get set by the controls */
+ {0x02, 0x00, {0x10}, 1},
+ {0x05, 0x01, {0x22}, 1}, /* 5/6 also seen as 65h/32h */
+ {0x06, 0x01, {0x00}, 1},
+ {0x09, 0x02, {0x0e}, 1},
+ {0x0a, 0x02, {0x05}, 1},
+ {0x0b, 0x02, {0x05}, 1},
+ {0x0c, 0x02, {0x0f}, 1},
+ {0x0d, 0x02, {0x07}, 1},
+ {0x0e, 0x02, {0x0c}, 1},
+ {0x0f, 0x00, {0x00}, 1},
+ {0x10, 0x00, {0x06}, 1},
+ {0x11, 0x00, {0x07}, 1},
+ {0x12, 0x00, {0x00}, 1},
+ {0x13, 0x00, {0x01}, 1},
+ {0, 0, {0}, 0}
+ };
+ /* Without this command the cam won't work with USB-UHCI */
+ gspca_dev->usb_buf[0] = 0x0a;
+ gspca_dev->usb_buf[1] = 0x00;
+ err_code = mr_write(gspca_dev, 2);
+ if (err_code < 0)
+ return err_code;
+ err_code = sensor_write_regs(gspca_dev, cif_sensor1_init_data,
+ ARRAY_SIZE(cif_sensor1_init_data));
+ }
+ return err_code;
+}
+
+static int start_vga_cam(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u8 *data = gspca_dev->usb_buf;
+ int err_code;
+ static const __u8 startup_string[] =
+ {0x00, 0x0d, 0x01, 0x00, 0x00, 0x2b, 0x00, 0x00,
+ 0x00, 0x50, 0xc0};
+ /* What some of these mean is explained in start_cif_cam(), above */
+
+ memcpy(data, startup_string, 11);
+ if (!sd->sensor_type) {
+ data[5] = 0x00;
+ data[10] = 0x91;
+ }
+ if (sd->sensor_type == 2) {
+ data[5] = 0x00;
+ data[10] = 0x18;
+ }
+
+ switch (gspca_dev->width) {
+ case 160:
+ data[9] |= 0x0c; /* reg 8, 4:1 scale down */
+ /* fall thru */
+ case 320:
+ data[9] |= 0x04; /* reg 8, 2:1 scale down */
+ /* fall thru */
+ case 640:
+ default:
+ data[3] = 0x50; /* reg 2, H size/8 */
+ data[4] = 0x78; /* reg 3, V size/4 */
+ data[6] = 0x04; /* reg 5, H start */
+ data[8] = 0x03; /* reg 7, V start */
+ if (sd->sensor_type == 2) {
+ data[6] = 2;
+ data[8] = 1;
+ }
+ if (sd->do_lcd_stop)
+ data[8] = 0x04; /* Bayer tile shifted */
+ break;
+
+ case 176:
+ data[9] |= 0x04; /* reg 8, 2:1 scale down */
+ /* fall thru */
+ case 352:
+ data[3] = 0x2c; /* reg 2, H size */
+ data[4] = 0x48; /* reg 3, V size */
+ data[6] = 0x94; /* reg 5, H start */
+ data[8] = 0x63; /* reg 7, V start */
+ if (sd->do_lcd_stop)
+ data[8] = 0x64; /* Bayer tile shifted */
+ break;
+ }
+
+ err_code = mr_write(gspca_dev, 11);
+ if (err_code < 0)
+ return err_code;
+
+ if (!sd->sensor_type) {
+ static const struct sensor_w_data vga_sensor0_init_data[] = {
+ {0x01, 0x00, {0x0c, 0x00, 0x04}, 3},
+ {0x14, 0x00, {0x01, 0xe4, 0x02, 0x84}, 4},
+ {0x20, 0x00, {0x00, 0x80, 0x00, 0x08}, 4},
+ {0x25, 0x00, {0x03, 0xa9, 0x80}, 3},
+ {0x30, 0x00, {0x30, 0x18, 0x10, 0x18}, 4},
+ {0, 0, {0}, 0}
+ };
+ err_code = sensor_write_regs(gspca_dev, vga_sensor0_init_data,
+ ARRAY_SIZE(vga_sensor0_init_data));
+ } else if (sd->sensor_type == 1) {
+ static const struct sensor_w_data color_adj[] = {
+ {0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00,
+ /* adjusted blue, green, red gain correct
+ too much blue from the Sakar Digital */
+ 0x05, 0x01, 0x04}, 8}
+ };
+
+ static const struct sensor_w_data color_no_adj[] = {
+ {0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00,
+ /* default blue, green, red gain settings */
+ 0x07, 0x00, 0x01}, 8}
+ };
+
+ static const struct sensor_w_data vga_sensor1_init_data[] = {
+ {0x11, 0x04, {0x01}, 1},
+ {0x0a, 0x00, {0x00, 0x01, 0x00, 0x00, 0x01,
+ /* These settings may be better for some cameras */
+ /* {0x0a, 0x00, {0x01, 0x06, 0x00, 0x00, 0x01, */
+ 0x00, 0x0a}, 7},
+ {0x11, 0x04, {0x01}, 1},
+ {0x12, 0x00, {0x00, 0x63, 0x00, 0x70, 0x00, 0x00}, 6},
+ {0x11, 0x04, {0x01}, 1},
+ {0, 0, {0}, 0}
+ };
+
+ if (sd->adj_colors)
+ err_code = sensor_write_regs(gspca_dev, color_adj,
+ ARRAY_SIZE(color_adj));
+ else
+ err_code = sensor_write_regs(gspca_dev, color_no_adj,
+ ARRAY_SIZE(color_no_adj));
+
+ if (err_code < 0)
+ return err_code;
+
+ err_code = sensor_write_regs(gspca_dev, vga_sensor1_init_data,
+ ARRAY_SIZE(vga_sensor1_init_data));
+ } else { /* sensor type == 2 */
+ static const struct sensor_w_data vga_sensor2_init_data[] = {
+
+ {0x01, 0x00, {0x48}, 1},
+ {0x02, 0x00, {0x22}, 1},
+ /* Reg 3 msb and 4 is lsb of the exposure setting*/
+ {0x05, 0x00, {0x10}, 1},
+ {0x06, 0x00, {0x00}, 1},
+ {0x07, 0x00, {0x00}, 1},
+ {0x08, 0x00, {0x00}, 1},
+ {0x09, 0x00, {0x00}, 1},
+ /* The following are used in the gain control
+ * which is BTW completely borked in the OEM driver
+ * The values for each color go from 0 to 0x7ff
+ *{0x0a, 0x00, {0x01}, 1}, green1 gain msb
+ *{0x0b, 0x00, {0x10}, 1}, green1 gain lsb
+ *{0x0c, 0x00, {0x01}, 1}, red gain msb
+ *{0x0d, 0x00, {0x10}, 1}, red gain lsb
+ *{0x0e, 0x00, {0x01}, 1}, blue gain msb
+ *{0x0f, 0x00, {0x10}, 1}, blue gain lsb
+ *{0x10, 0x00, {0x01}, 1}, green2 gain msb
+ *{0x11, 0x00, {0x10}, 1}, green2 gain lsb
+ */
+ {0x12, 0x00, {0x00}, 1},
+ {0x13, 0x00, {0x04}, 1}, /* weird effect on colors */
+ {0x14, 0x00, {0x00}, 1},
+ {0x15, 0x00, {0x06}, 1},
+ {0x16, 0x00, {0x01}, 1},
+ {0x17, 0x00, {0xe2}, 1}, /* vertical alignment */
+ {0x18, 0x00, {0x02}, 1},
+ {0x19, 0x00, {0x82}, 1}, /* don't mess with */
+ {0x1a, 0x00, {0x00}, 1},
+ {0x1b, 0x00, {0x20}, 1},
+ /* {0x1c, 0x00, {0x17}, 1}, contrast control */
+ {0x1d, 0x00, {0x80}, 1}, /* moving causes a mess */
+ {0x1e, 0x00, {0x08}, 1}, /* moving jams the camera */
+ {0x1f, 0x00, {0x0c}, 1},
+ {0x20, 0x00, {0x00}, 1},
+ {0, 0, {0}, 0}
+ };
+ err_code = sensor_write_regs(gspca_dev, vga_sensor2_init_data,
+ ARRAY_SIZE(vga_sensor2_init_data));
+ }
+ return err_code;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int err_code;
+
+ sd->sof_read = 0;
+
+ /* Some of the VGA cameras require the memory pointer
+ * to be set to 0 again. We have been forced to start the
+ * stream in sd_config() to detect the hardware, and closed it.
+ * Thus, we need here to do a completely fresh and clean start. */
+ err_code = zero_the_pointer(gspca_dev);
+ if (err_code < 0)
+ return err_code;
+
+ err_code = stream_start(gspca_dev);
+ if (err_code < 0)
+ return err_code;
+
+ if (sd->cam_type == CAM_TYPE_CIF) {
+ err_code = start_cif_cam(gspca_dev);
+ } else {
+ err_code = start_vga_cam(gspca_dev);
+ }
+ if (err_code < 0)
+ return err_code;
+
+ setbrightness(gspca_dev);
+ setcontrast(gspca_dev);
+ setexposure(gspca_dev);
+ setgain(gspca_dev);
+
+ return isoc_enable(gspca_dev);
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ stream_stop(gspca_dev);
+ /* Not all the cams need this, but even if not, probably a good idea */
+ zero_the_pointer(gspca_dev);
+ if (sd->do_lcd_stop)
+ lcd_stop(gspca_dev);
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
+ u8 sign_reg = 7; /* This reg and the next one used on CIF cams. */
+ u8 value_reg = 8; /* VGA cams seem to use regs 0x0b and 0x0c */
+ static const u8 quick_clix_table[] =
+ /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */
+ { 0, 4, 8, 12, 1, 2, 3, 5, 6, 9, 7, 10, 13, 11, 14, 15};
+ /*
+ * This control is disabled for CIF type 1 and VGA type 0 cameras.
+ * It does not quite act linearly for the Argus QuickClix camera,
+ * but it does control brightness. The values are 0 - 15 only, and
+ * the table above makes them act consecutively.
+ */
+ if ((gspca_dev->ctrl_dis & (1 << NORM_BRIGHTNESS_IDX)) &&
+ (gspca_dev->ctrl_dis & (1 << ARGUS_QC_BRIGHTNESS_IDX)))
+ return;
+
+ if (sd->cam_type == CAM_TYPE_VGA) {
+ sign_reg += 4;
+ value_reg += 4;
+ }
+
+ /* Note register 7 is also seen as 0x8x or 0xCx in some dumps */
+ if (sd->brightness > 0) {
+ sensor_write1(gspca_dev, sign_reg, 0x00);
+ val = sd->brightness;
+ } else {
+ sensor_write1(gspca_dev, sign_reg, 0x01);
+ val = (257 - sd->brightness);
+ }
+ /* Use lookup table for funky Argus QuickClix brightness */
+ if (sd->do_lcd_stop)
+ val = quick_clix_table[val];
+
+ sensor_write1(gspca_dev, value_reg, val);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int exposure = MR97310A_EXPOSURE_DEFAULT;
+ u8 buf[2];
+
+ if (gspca_dev->ctrl_dis & (1 << EXPOSURE_IDX))
+ return;
+
+ if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) {
+ /* This cam does not like exposure settings < 300,
+ so scale 0 - 4095 to 300 - 4095 */
+ exposure = (sd->exposure * 9267) / 10000 + 300;
+ sensor_write1(gspca_dev, 3, exposure >> 4);
+ sensor_write1(gspca_dev, 4, exposure & 0x0f);
+ } else if (sd->sensor_type == 2) {
+ exposure = sd->exposure;
+ exposure >>= 3;
+ sensor_write1(gspca_dev, 3, exposure >> 8);
+ sensor_write1(gspca_dev, 4, exposure & 0xff);
+ } else {
+ /* We have both a clock divider and an exposure register.
+ We first calculate the clock divider, as that determines
+ the maximum exposure and then we calculate the exposure
+ register setting (which goes from 0 - 511).
+
+ Note our 0 - 4095 exposure is mapped to 0 - 511
+ milliseconds exposure time */
+ u8 clockdiv = (60 * sd->exposure + 7999) / 8000;
+
+ /* Limit framerate to not exceed usb bandwidth */
+ if (clockdiv < sd->min_clockdiv && gspca_dev->width >= 320)
+ clockdiv = sd->min_clockdiv;
+ else if (clockdiv < 2)
+ clockdiv = 2;
+
+ if (sd->cam_type == CAM_TYPE_VGA && clockdiv < 4)
+ clockdiv = 4;
+
+ /* Frame exposure time in ms = 1000 * clockdiv / 60 ->
+ exposure = (sd->exposure / 8) * 511 / (1000 * clockdiv / 60) */
+ exposure = (60 * 511 * sd->exposure) / (8000 * clockdiv);
+ if (exposure > 511)
+ exposure = 511;
+
+ /* exposure register value is reversed! */
+ exposure = 511 - exposure;
+
+ buf[0] = exposure & 0xff;
+ buf[1] = exposure >> 8;
+ sensor_write_reg(gspca_dev, 0x0e, 0, buf, 2);
+ sensor_write1(gspca_dev, 0x02, clockdiv);
+ }
+}
+
+static void setgain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 gainreg;
+
+ if ((gspca_dev->ctrl_dis & (1 << GAIN_IDX)) &&
+ (gspca_dev->ctrl_dis & (1 << SAKAR_CS_GAIN_IDX)))
+ return;
+
+ if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1)
+ sensor_write1(gspca_dev, 0x0e, sd->gain);
+ else if (sd->cam_type == CAM_TYPE_VGA && sd->sensor_type == 2)
+ for (gainreg = 0x0a; gainreg < 0x11; gainreg += 2) {
+ sensor_write1(gspca_dev, gainreg, sd->gain >> 8);
+ sensor_write1(gspca_dev, gainreg + 1, sd->gain & 0xff);
+ }
+ else
+ sensor_write1(gspca_dev, 0x10, sd->gain);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (gspca_dev->ctrl_dis & (1 << CONTRAST_IDX))
+ return;
+
+ sensor_write1(gspca_dev, 0x1c, sd->contrast);
+}
+
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->brightness = val;
+ if (gspca_dev->streaming)
+ setbrightness(gspca_dev);
+ return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->brightness;
+ return 0;
+}
+
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->exposure = val;
+ if (gspca_dev->streaming)
+ setexposure(gspca_dev);
+ return 0;
+}
+
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->exposure;
+ return 0;
+}
+
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->gain = val;
+ if (gspca_dev->streaming)
+ setgain(gspca_dev);
+ return 0;
+}
+
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->gain;
+ return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->contrast = val;
+ if (gspca_dev->streaming)
+ setcontrast(gspca_dev);
+ return 0;
+}
+
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->contrast;
+ return 0;
+}
+
+static int sd_setmin_clockdiv(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->min_clockdiv = val;
+ if (gspca_dev->streaming)
+ setexposure(gspca_dev);
+ return 0;
+}
+
+static int sd_getmin_clockdiv(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->min_clockdiv;
+ return 0;
+}
+
+/* Include pac common sof detection functions */
+#include "pac_common.h"
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ unsigned char *sof;
+
+ sof = pac_find_sof(&sd->sof_read, data, len);
+ if (sof) {
+ int n;
+
+ /* finish decoding current frame */
+ n = sof - data;
+ if (n > sizeof pac_sof_marker)
+ n -= sizeof pac_sof_marker;
+ else
+ n = 0;
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ data, n);
+ /* Start next frame. */
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ pac_sof_marker, sizeof pac_sof_marker);
+ len -= sof - data;
+ data = sof;
+ }
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x08ca, 0x0110)}, /* Trust Spyc@m 100 */
+ {USB_DEVICE(0x08ca, 0x0111)}, /* Aiptek Pencam VGA+ */
+ {USB_DEVICE(0x093a, 0x010f)}, /* All other known MR97310A VGA cams */
+ {USB_DEVICE(0x093a, 0x010e)}, /* All known MR97310A CIF cams */
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/nw80x.c b/drivers/media/video/gspca/nw80x.c
new file mode 100644
index 00000000..7167cac7
--- /dev/null
+++ b/drivers/media/video/gspca/nw80x.c
@@ -0,0 +1,2137 @@
+/*
+ * DivIO nw80x subdriver
+ *
+ * Copyright (C) 2011 Jean-François Moine (http://moinejf.free.fr)
+ * Copyright (C) 2003 Sylvain Munaut <tnt@246tNt.com>
+ * Kjell Claesson <keyson@users.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "nw80x"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
+MODULE_DESCRIPTION("NW80x USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+static int webcam;
+
+/* controls */
+enum e_ctrl {
+ GAIN,
+ EXPOSURE,
+ AUTOGAIN,
+ NCTRLS /* number of controls */
+};
+
+#define AUTOGAIN_DEF 1
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ struct gspca_ctrl ctrls[NCTRLS];
+
+ u32 ae_res;
+ s8 ag_cnt;
+#define AG_CNT_START 13
+ u8 exp_too_low_cnt;
+ u8 exp_too_high_cnt;
+
+ u8 bridge;
+ u8 webcam;
+};
+
+enum bridges {
+ BRIDGE_NW800, /* and et31x110 */
+ BRIDGE_NW801,
+ BRIDGE_NW802,
+};
+enum webcams {
+ Generic800,
+ SpaceCam, /* Trust 120 SpaceCam */
+ SpaceCam2, /* other Trust 120 SpaceCam */
+ Cvideopro, /* Conceptronic Video Pro */
+ Dlink350c,
+ DS3303u,
+ Kr651us,
+ Kritter,
+ Mustek300,
+ Proscope,
+ Twinkle,
+ DvcV6,
+ P35u,
+ Generic802,
+ NWEBCAMS /* number of webcams */
+};
+
+static const u8 webcam_chip[NWEBCAMS] = {
+ [Generic800] = BRIDGE_NW800, /* 06a5:0000
+ * Typhoon Webcam 100 USB */
+
+ [SpaceCam] = BRIDGE_NW800, /* 06a5:d800
+ * Trust SpaceCam120 or SpaceCam100 PORTABLE */
+
+ [SpaceCam2] = BRIDGE_NW800, /* 06a5:d800 - pas106
+ * other Trust SpaceCam120 or SpaceCam100 PORTABLE */
+
+ [Cvideopro] = BRIDGE_NW802, /* 06a5:d001
+ * Conceptronic Video Pro 'CVIDEOPRO USB Webcam CCD' */
+
+ [Dlink350c] = BRIDGE_NW802, /* 06a5:d001
+ * D-Link NetQam Pro 250plus */
+
+ [DS3303u] = BRIDGE_NW801, /* 06a5:d001
+ * Plustek Opticam 500U or ProLink DS3303u */
+
+ [Kr651us] = BRIDGE_NW802, /* 06a5:d001
+ * Panasonic GP-KR651US */
+
+ [Kritter] = BRIDGE_NW802, /* 06a5:d001
+ * iRez Kritter cam */
+
+ [Mustek300] = BRIDGE_NW802, /* 055f:d001
+ * Mustek Wcam 300 mini */
+
+ [Proscope] = BRIDGE_NW802, /* 06a5:d001
+ * Scalar USB Microscope (ProScope) */
+
+ [Twinkle] = BRIDGE_NW800, /* 06a5:d800 - hv7121b? (seems pas106)
+ * Divio Chicony TwinkleCam
+ * DSB-C110 */
+
+ [DvcV6] = BRIDGE_NW802, /* 0502:d001
+ * DVC V6 */
+
+ [P35u] = BRIDGE_NW801, /* 052b:d001, 06a5:d001 and 06be:d001
+ * EZCam Pro p35u */
+
+ [Generic802] = BRIDGE_NW802,
+};
+/*
+ * other webcams:
+ * - nw801 046d:d001
+ * Logitech QuickCam Pro (dark focus ring)
+ * - nw801 0728:d001
+ * AVerMedia Camguard
+ * - nw??? 06a5:d001
+ * D-Link NetQam Pro 250plus
+ * - nw800 065a:d800
+ * Showcam NGS webcam
+ * - nw??? ????:????
+ * Sceptre svc300
+ */
+
+/*
+ * registers
+ * nw800/et31x110 nw801 nw802
+ * 0000..009e 0000..00a1 0000..009e
+ * 0200..0211 id id
+ * 0300..0302 id id
+ * 0400..0406 (inex) 0400..0406
+ * 0500..0505 0500..0506 (inex)
+ * 0600..061a 0600..0601 0600..0601
+ * 0800..0814 id id
+ * 1000..109c 1000..10a1 1000..109a
+ */
+
+/* resolutions
+ * nw800: 320x240, 352x288
+ * nw801/802: 320x240, 640x480
+ */
+static const struct v4l2_pix_format cif_mode[] = {
+ {320, 240, V4L2_PIX_FMT_JPGL, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 4 / 8,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+ {352, 288, V4L2_PIX_FMT_JPGL, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288 * 4 / 8,
+ .colorspace = V4L2_COLORSPACE_JPEG}
+};
+static const struct v4l2_pix_format vga_mode[] = {
+ {320, 240, V4L2_PIX_FMT_JPGL, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 4 / 8,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+ {640, 480, V4L2_PIX_FMT_JPGL, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 8,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+};
+
+/*
+ * The sequences below contain:
+ * - 1st and 2nd bytes: either
+ * - register number (BE)
+ * - I2C0 + i2c address
+ * - 3rd byte: data length (=0 for end of sequence)
+ * - n bytes: data
+ */
+#define I2C0 0xff
+
+static const u8 nw800_init[] = {
+ 0x04, 0x05, 0x01, 0x61,
+ 0x04, 0x04, 0x01, 0x01,
+ 0x04, 0x06, 0x01, 0x04,
+ 0x04, 0x04, 0x03, 0x00, 0x00, 0x00,
+ 0x05, 0x05, 0x01, 0x00,
+ 0, 0, 0
+};
+static const u8 nw800_start[] = {
+ 0x04, 0x06, 0x01, 0xc0,
+ 0x00, 0x00, 0x40, 0x10, 0x43, 0x00, 0xb4, 0x01, 0x10, 0x00, 0x4f,
+ 0xef, 0x0e, 0x00, 0x74, 0x01, 0x01, 0x00, 0x19,
+ 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+ 0x00, 0x01, 0x00, 0x19, 0x00, 0x3e, 0x00, 0x24,
+ 0x03, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86,
+ 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e,
+ 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+ 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+ 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46,
+ 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0,
+ 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e,
+ 0x00, 0x80, 0x1f, 0xa0, 0x48, 0xc3, 0x02, 0x88, 0x0c, 0x68, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xa8, 0x06, 0x00, 0x08,
+ 0x00, 0x32, 0x01, 0x01, 0x00, 0x16, 0x00, 0x04,
+ 0x00, 0x4b, 0x00, 0x76, 0x00, 0x86, 0x00,
+ 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+ 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0,
+ 0x40, 0x20,
+ 0x03, 0x00, 0x03, 0x03, 0x00, 0x00,
+ 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x61, 0xc0,
+ 0x05, 0x00, 0x06, 0xe8, 0x00, 0x00, 0x00, 0x20, 0x20,
+ 0x06, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x40, 0x83, 0x02, 0x20, 0x00, 0x13, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a,
+ 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x49, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06,
+ 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80,
+ 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99,
+ 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc,
+ 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+ 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+ 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+ 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+ 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32,
+ 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3,
+ 0x10, 0x80, 0x1d, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x62,
+ 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20,
+ 0x01, 0x60, 0x01, 0x00, 0x00,
+
+ 0x04, 0x04, 0x01, 0xff,
+ 0x04, 0x06, 0x01, 0xc4,
+
+ 0x04, 0x06, 0x01, 0xc0,
+ 0x00, 0x00, 0x40, 0x10, 0x43, 0x00, 0xb4, 0x01, 0x10, 0x00, 0x4f,
+ 0xef, 0x0e, 0x00, 0x74, 0x01, 0x01, 0x00, 0x19,
+ 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+ 0x00, 0x01, 0x00, 0x19, 0x00, 0x3e, 0x00, 0x24,
+ 0x03, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86,
+ 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e,
+ 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+ 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+ 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46,
+ 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0,
+ 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e,
+ 0x00, 0x80, 0x1f, 0xa0, 0x48, 0xc3, 0x02, 0x88, 0x0c, 0x68, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xa8, 0x06, 0x00, 0x08,
+ 0x00, 0x32, 0x01, 0x01, 0x00, 0x16, 0x00, 0x04,
+ 0x00, 0x4b, 0x00, 0x76, 0x00, 0x86, 0x00,
+ 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+ 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0,
+ 0x40, 0x20,
+ 0x03, 0x00, 0x03, 0x03, 0x00, 0x00,
+ 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x61, 0xc0,
+ 0x05, 0x00, 0x06, 0xe8, 0x00, 0x00, 0x00, 0x20, 0x20,
+ 0x06, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x40, 0x83, 0x02, 0x20, 0x00, 0x13, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a,
+ 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x49, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06,
+ 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80,
+ 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99,
+ 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc,
+ 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+ 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+ 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+ 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+ 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32,
+ 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3,
+ 0x10, 0x80, 0x1d, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x62,
+ 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20,
+ 0x01, 0x60, 0x01, 0x00, 0x00,
+
+ 0x02, 0x00, 0x11, 0x48, 0x58, 0x9e, 0x48, 0x58, 0x00, 0x00, 0x00,
+ 0x00, 0x84, 0x36, 0x05, 0x01, 0xf2, 0x86, 0x65,
+ 0x40,
+ 0x00, 0x80, 0x01, 0xa0,
+ 0x10, 0x1a, 0x01, 0x00,
+ 0x00, 0x91, 0x02, 0x6c, 0x01,
+ 0x00, 0x03, 0x02, 0xc8, 0x01,
+ 0x10, 0x1a, 0x01, 0x00,
+ 0x10, 0x00, 0x01, 0x83,
+ 0x10, 0x8f, 0x0c, 0x62, 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01,
+ 0x20, 0x01, 0x60, 0x01,
+ 0x10, 0x85, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01,
+ 0x10, 0x1b, 0x02, 0x69, 0x00,
+ 0x10, 0x11, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01,
+ 0x05, 0x02, 0x01, 0x02,
+ 0x06, 0x00, 0x02, 0x04, 0xd9,
+ 0x05, 0x05, 0x01, 0x20,
+ 0x05, 0x05, 0x01, 0x21,
+ 0x10, 0x0e, 0x01, 0x08,
+ 0x10, 0x41, 0x11, 0x00, 0x08, 0x21, 0x3d, 0x52, 0x63, 0x75, 0x83,
+ 0x91, 0x9e, 0xaa, 0xb6, 0xc1, 0xcc, 0xd6, 0xe0,
+ 0xea,
+ 0x10, 0x03, 0x01, 0x00,
+ 0x10, 0x0f, 0x02, 0x13, 0x13,
+ 0x10, 0x03, 0x01, 0x14,
+ 0x10, 0x41, 0x11, 0x00, 0x08, 0x21, 0x3d, 0x52, 0x63, 0x75, 0x83,
+ 0x91, 0x9e, 0xaa, 0xb6, 0xc1, 0xcc, 0xd6, 0xe0,
+ 0xea,
+ 0x10, 0x0b, 0x01, 0x14,
+ 0x10, 0x0d, 0x01, 0x20,
+ 0x10, 0x0c, 0x01, 0x34,
+ 0x04, 0x06, 0x01, 0xc3,
+ 0x04, 0x04, 0x01, 0x00,
+ 0x05, 0x02, 0x01, 0x02,
+ 0x06, 0x00, 0x02, 0x00, 0x48,
+ 0x05, 0x05, 0x01, 0x20,
+ 0x05, 0x05, 0x01, 0x21,
+ 0, 0, 0
+};
+
+/* 06a5:d001 - nw801 - Panasonic
+ * P35u */
+static const u8 nw801_start_1[] = {
+ 0x05, 0x06, 0x01, 0x04,
+ 0x00, 0x00, 0x40, 0x0e, 0x00, 0x00, 0xf9, 0x02, 0x11, 0x00, 0x0e,
+ 0x01, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19,
+ 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+ 0x00, 0x01, 0x00, 0x19, 0x00, 0xce, 0x00, 0xf4,
+ 0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86,
+ 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e,
+ 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+ 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+ 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46,
+ 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0,
+ 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e,
+ 0x00, 0x80, 0x22, 0xb4, 0x6f, 0x3f, 0x0f, 0x88, 0x20, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x69, 0xa8, 0x1f, 0x00,
+ 0x0d, 0x02, 0x07, 0x00, 0x01, 0x00, 0x19, 0x00,
+ 0xf2, 0x00, 0x18, 0x06, 0x10, 0x06, 0x10, 0x00,
+ 0x36, 0x00,
+ 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+ 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0,
+ 0x40, 0x20,
+ 0x03, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x02, 0x09, 0x99,
+ 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x40, 0x22, 0x02, 0x80, 0x00, 0x1e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0a, 0x15, 0x08, 0x08, 0x0a,
+ 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x35, 0xfd, 0x07, 0x3d, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x14, 0x02,
+ 0x00, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0x40, 0x20, 0x10, 0x06,
+ 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, 0xf7,
+ 0x10, 0x40, 0x40, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, 0x80,
+ 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99, 0xa4,
+ 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc, 0xcf,
+ 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, 0x64,
+ 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, 0xe2,
+ 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+ 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+ 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+ 0x10, 0x80, 0x22, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+ 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x82, 0x02,
+ 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, 0x01,
+ 0xf0, 0x00,
+ 0, 0, 0,
+};
+static const u8 nw801_start_qvga[] = {
+ 0x02, 0x00, 0x10, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00,
+ 0x00, 0x78, 0x18, 0x0b, 0x06, 0xa2, 0x86, 0x78,
+ 0x02, 0x0f, 0x01, 0x6b,
+ 0x10, 0x1a, 0x01, 0x15,
+ 0x00, 0x00, 0x01, 0x1e,
+ 0x10, 0x00, 0x01, 0x2f,
+ 0x10, 0x8c, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00,
+ 0x10, 0x11, 0x08, 0x29, 0x00, 0x18, 0x01, 0x1f, 0x00, 0xd2, 0x00,
+ /* AE window */
+ 0, 0, 0,
+};
+static const u8 nw801_start_vga[] = {
+ 0x02, 0x00, 0x10, 0x78, 0xa0, 0x97, 0x78, 0xa0, 0x00, 0x00, 0x00,
+ 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xf0,
+ 0x02, 0x0f, 0x01, 0xd5,
+ 0x10, 0x1a, 0x01, 0x15,
+ 0x00, 0x00, 0x01, 0x0e,
+ 0x10, 0x00, 0x01, 0x22,
+ 0x10, 0x8c, 0x08, 0x00, 0x00, 0x7f, 0x02, 0x00, 0x00, 0xdf, 0x01,
+ 0x10, 0x11, 0x08, 0x51, 0x00, 0x30, 0x02, 0x3d, 0x00, 0xa4, 0x01,
+ 0, 0, 0,
+};
+static const u8 nw801_start_2[] = {
+ 0x10, 0x04, 0x01, 0x1a,
+ 0x10, 0x19, 0x01, 0x09, /* clock */
+ 0x10, 0x24, 0x06, 0xc0, 0x00, 0x3f, 0x02, 0x00, 0x01,
+ /* .. gain .. */
+ 0x00, 0x03, 0x02, 0x92, 0x03,
+ 0x00, 0x1d, 0x04, 0xf2, 0x00, 0x24, 0x07,
+ 0x00, 0x7b, 0x01, 0xcf,
+ 0x10, 0x94, 0x01, 0x07,
+ 0x05, 0x05, 0x01, 0x01,
+ 0x05, 0x04, 0x01, 0x01,
+ 0x10, 0x0e, 0x01, 0x08,
+ 0x10, 0x48, 0x11, 0x00, 0x37, 0x55, 0x6b, 0x7d, 0x8d, 0x9b, 0xa8,
+ 0xb4, 0xbf, 0xca, 0xd4, 0xdd, 0xe6, 0xef, 0xf0,
+ 0xf0,
+ 0x10, 0x03, 0x01, 0x00,
+ 0x10, 0x0f, 0x02, 0x0c, 0x0c,
+ 0x10, 0x03, 0x01, 0x08,
+ 0x10, 0x48, 0x11, 0x00, 0x37, 0x55, 0x6b, 0x7d, 0x8d, 0x9b, 0xa8,
+ 0xb4, 0xbf, 0xca, 0xd4, 0xdd, 0xe6, 0xef, 0xf0,
+ 0xf0,
+ 0x10, 0x0b, 0x01, 0x0b,
+ 0x10, 0x0d, 0x01, 0x0b,
+ 0x10, 0x0c, 0x01, 0x1f,
+ 0x05, 0x06, 0x01, 0x03,
+ 0, 0, 0
+};
+
+/* nw802 (sharp IR3Y38M?) */
+static const u8 nw802_start[] = {
+ 0x04, 0x06, 0x01, 0x04,
+ 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0xf9, 0x02, 0x10, 0x00, 0x4d,
+ 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19,
+ 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+ 0x00, 0x01, 0x00, 0x19, 0x00, 0xce, 0x00, 0xf4,
+ 0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86,
+ 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e,
+ 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+ 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+ 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46,
+ 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0,
+ 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e,
+ 0x00, 0x80, 0x1f, 0xb4, 0x6f, 0x3f, 0x0f, 0x88, 0x20, 0x68, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11,
+ 0x00, 0x0c, 0x02, 0x01, 0x00, 0x16, 0x00, 0x94,
+ 0x00, 0x10, 0x06, 0x08, 0x00, 0x18, 0x00,
+ 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+ 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0,
+ 0x40, 0x20,
+ 0x03, 0x00, 0x03, 0x03, 0x00, 0x00,
+ 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00,
+ 0x06, 0x00, 0x02, 0x09, 0x99,
+ 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x40, 0xa1, 0x02, 0x80, 0x00, 0x1d, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a,
+ 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x49, 0x13, 0xff, 0x01, 0xc0, 0x00, 0x14,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06,
+ 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80,
+ 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99,
+ 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc,
+ 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+ 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+ 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+ 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+ 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32,
+ 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3,
+ 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x05, 0x82,
+ 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40,
+ 0x01, 0xf0, 0x00,
+ 0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00,
+ 0x00, 0x78, 0x3f, 0x10, 0x02, 0xf2, 0x8f, 0x78,
+ 0x40,
+ 0x10, 0x1a, 0x01, 0x00,
+ 0x10, 0x00, 0x01, 0xad,
+ 0x00, 0x00, 0x01, 0x08,
+ 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00,
+ 0x10, 0x1b, 0x02, 0x00, 0x00,
+ 0x10, 0x11, 0x08, 0x51, 0x00, 0xf0, 0x00, 0x3d, 0x00, 0xb4, 0x00,
+ 0x10, 0x1d, 0x08, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0,
+ 0x10, 0x0e, 0x01, 0x27,
+ 0x10, 0x41, 0x11, 0x00, 0x0e, 0x35, 0x4f, 0x62, 0x71, 0x7f, 0x8b,
+ 0x96, 0xa0, 0xa9, 0xb2, 0xbb, 0xc3, 0xca, 0xd2,
+ 0xd8,
+ 0x10, 0x03, 0x01, 0x00,
+ 0x10, 0x0f, 0x02, 0x14, 0x14,
+ 0x10, 0x03, 0x01, 0x0c,
+ 0x10, 0x41, 0x11, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, 0x64, 0x74,
+ 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, 0xe2, 0xf1,
+ 0xff,
+/* 0x00, 0x0e, 0x35, 0x4f, 0x62, 0x71, 0x7f, 0x8b,
+ * 0x96, 0xa0, 0xa9, 0xb2, 0xbb, 0xc3, 0xca, 0xd2,
+ * 0xd8, */
+ 0x10, 0x0b, 0x01, 0x10,
+ 0x10, 0x0d, 0x01, 0x11,
+ 0x10, 0x0c, 0x01, 0x1c,
+ 0x04, 0x06, 0x01, 0x03,
+ 0x04, 0x04, 0x01, 0x00,
+ 0, 0, 0
+};
+/* et31x110 - Trust 120 SpaceCam */
+static const u8 spacecam_init[] = {
+ 0x04, 0x05, 0x01, 0x01,
+ 0x04, 0x04, 0x01, 0x01,
+ 0x04, 0x06, 0x01, 0x04,
+ 0x04, 0x04, 0x03, 0x00, 0x00, 0x00,
+ 0x05, 0x05, 0x01, 0x00,
+ 0, 0, 0
+};
+static const u8 spacecam_start[] = {
+ 0x04, 0x06, 0x01, 0x44,
+ 0x00, 0x00, 0x40, 0x10, 0x43, 0x00, 0xb4, 0x01, 0x10, 0x00, 0x4f,
+ 0xef, 0x0e, 0x00, 0x74, 0x01, 0x01, 0x00, 0x19,
+ 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+ 0x00, 0x01, 0x00, 0x19, 0x00, 0x3e, 0x00, 0x24,
+ 0x03, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86,
+ 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e,
+ 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+ 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+ 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46,
+ 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0,
+ 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e,
+ 0x00, 0x80, 0x1f, 0xa0, 0x48, 0xc3, 0x02, 0x88, 0x0c, 0x68, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xa8, 0x06, 0x00, 0x08,
+ 0x00, 0x32, 0x01, 0x01, 0x00, 0x16, 0x00, 0x04,
+ 0x00, 0x4b, 0x00, 0x7c, 0x00, 0x80, 0x00,
+ 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+ 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0,
+ 0x40, 0x20,
+ 0x03, 0x00, 0x03, 0x03, 0x00, 0x00,
+ 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x06, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x40, 0x83, 0x02, 0x20, 0x00, 0x11, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a,
+ 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x49, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06,
+ 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80,
+ 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99,
+ 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc,
+ 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+ 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+ 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+ 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+ 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32,
+ 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3,
+ 0x10, 0x80, 0x1d, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x62,
+ 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20,
+ 0x01, 0x60, 0x01, 0x00, 0x00,
+ 0x04, 0x06, 0x01, 0xc0,
+ 0x10, 0x85, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01,
+ 0x02, 0x00, 0x11, 0x48, 0x58, 0x9e, 0x48, 0x58, 0x00, 0x00, 0x00,
+ 0x00, 0x84, 0x36, 0x05, 0x01, 0xf2, 0x86, 0x65,
+ 0x40,
+ 0x00, 0x80, 0x01, 0xa0,
+ 0x10, 0x1a, 0x01, 0x00,
+ 0x00, 0x91, 0x02, 0x32, 0x01,
+ 0x00, 0x03, 0x02, 0x08, 0x02,
+ 0x10, 0x00, 0x01, 0x83,
+ 0x10, 0x8f, 0x0c, 0x62, 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01,
+ 0x20, 0x01, 0x60, 0x01,
+ 0x10, 0x11, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01,
+ 0x10, 0x0e, 0x01, 0x08,
+ 0x10, 0x41, 0x11, 0x00, 0x64, 0x99, 0xc0, 0xe2, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xf9,
+ 0x10, 0x03, 0x01, 0x00,
+ 0x10, 0x0f, 0x02, 0x13, 0x13,
+ 0x10, 0x03, 0x01, 0x06,
+ 0x10, 0x41, 0x11, 0x00, 0x64, 0x99, 0xc0, 0xe2, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xf9,
+ 0x10, 0x0b, 0x01, 0x08,
+ 0x10, 0x0d, 0x01, 0x10,
+ 0x10, 0x0c, 0x01, 0x1f,
+ 0x04, 0x06, 0x01, 0xc3,
+ 0x04, 0x05, 0x01, 0x40,
+ 0x04, 0x04, 0x01, 0x40,
+ 0, 0, 0
+};
+/* et31x110 - pas106 - other Trust SpaceCam120 */
+static const u8 spacecam2_start[] = {
+ 0x04, 0x06, 0x01, 0x44,
+ 0x04, 0x06, 0x01, 0x00,
+ 0x00, 0x00, 0x40, 0x14, 0x83, 0x00, 0xba, 0x01, 0x10, 0x00, 0x4f,
+ 0xef, 0x00, 0x00, 0x60, 0x00, 0x01, 0x00, 0x19,
+ 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+ 0x00, 0x01, 0x00, 0x19, 0x00, 0x06, 0x00, 0xfc,
+ 0x01, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86,
+ 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e,
+ 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+ 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+ 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46,
+ 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0,
+ 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e,
+ 0x00, 0x80, 0x1f, 0xb8, 0x48, 0x0f, 0x04, 0x88, 0x14, 0x68, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xa8, 0x01, 0x00, 0x03,
+ 0x00, 0x24, 0x01, 0x01, 0x00, 0x16, 0x00, 0x04,
+ 0x00, 0x4b, 0x00, 0x76, 0x00, 0x86, 0x00,
+ 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+ 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0,
+ 0x40, 0x20,
+ 0x03, 0x00, 0x03, 0x03, 0x00, 0x00,
+ 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x61, 0x00,
+ 0x05, 0x00, 0x06, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x40, 0x80, 0x02, 0x20, 0x00, 0x13, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a,
+ 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x49, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06,
+ 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80,
+ 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99,
+ 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc,
+ 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+ 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+ 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+ 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+ 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32,
+ 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3,
+ 0x10, 0x80, 0x1d, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x62,
+ 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20,
+ 0x01, 0x60, 0x01, 0x00, 0x00,
+ 0x10, 0x85, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01,
+ 0x04, 0x04, 0x01, 0x40,
+ 0x04, 0x04, 0x01, 0x00,
+ I2C0, 0x40, 0x0c, 0x02, 0x0c, 0x12, 0x07, 0x00, 0x00, 0x00, 0x05,
+ 0x00, 0x00, 0x05, 0x05,
+ I2C0, 0x40, 0x02, 0x11, 0x06,
+ I2C0, 0x40, 0x02, 0x14, 0x00,
+ I2C0, 0x40, 0x02, 0x13, 0x01, /* i2c end */
+ 0x02, 0x00, 0x11, 0x48, 0x58, 0x9e, 0x48, 0x58, 0x00, 0x00, 0x00,
+ 0x00, 0x84, 0x36, 0x05, 0x01, 0xf2, 0x86, 0x65,
+ 0x40,
+ I2C0, 0x40, 0x02, 0x02, 0x0c, /* pixel clock */
+ I2C0, 0x40, 0x02, 0x0f, 0x00,
+ I2C0, 0x40, 0x02, 0x13, 0x01, /* i2c end */
+ 0x10, 0x00, 0x01, 0x01,
+ 0x10, 0x8f, 0x0c, 0x62, 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01,
+ 0x20, 0x01, 0x60, 0x01,
+ I2C0, 0x40, 0x02, 0x05, 0x0f, /* exposure */
+ I2C0, 0x40, 0x02, 0x13, 0x01, /* i2c end */
+ I2C0, 0x40, 0x07, 0x09, 0x0b, 0x0f, 0x05, 0x05, 0x0f, 0x00,
+ /* gains */
+ I2C0, 0x40, 0x03, 0x12, 0x04, 0x01,
+ 0x10, 0x11, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01,
+ 0x10, 0x0e, 0x01, 0x08,
+ 0x10, 0x41, 0x11, 0x00, 0x17, 0x3f, 0x69, 0x7b, 0x8c, 0x9a, 0xa7,
+ 0xb3, 0xbf, 0xc9, 0xd3, 0xdd, 0xe6, 0xef, 0xf7,
+ 0xf9,
+ 0x10, 0x03, 0x01, 0x00,
+ 0x10, 0x0f, 0x02, 0x13, 0x13,
+ 0x10, 0x03, 0x01, 0x06,
+ 0x10, 0x41, 0x11, 0x00, 0x17, 0x3f, 0x69, 0x7b, 0x8c, 0x9a, 0xa7,
+ 0xb3, 0xbf, 0xc9, 0xd3, 0xdd, 0xe6, 0xef, 0xf7,
+ 0xf9,
+ 0x10, 0x0b, 0x01, 0x11,
+ 0x10, 0x0d, 0x01, 0x10,
+ 0x10, 0x0c, 0x01, 0x14,
+ 0x04, 0x06, 0x01, 0x03,
+ 0x04, 0x05, 0x01, 0x61,
+ 0x04, 0x04, 0x01, 0x00,
+ 0, 0, 0
+};
+
+/* nw802 - Conceptronic Video Pro */
+static const u8 cvideopro_start[] = {
+ 0x04, 0x06, 0x01, 0x04,
+ 0x00, 0x00, 0x40, 0x54, 0x96, 0x98, 0xf9, 0x02, 0x18, 0x00, 0x4c,
+ 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19,
+ 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+ 0x00, 0x0b, 0x00, 0x1b, 0x00, 0xc8, 0x00, 0xf4,
+ 0x05, 0xb4, 0x00, 0xcc, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0xa2, 0x00, 0xc6, 0x00, 0x60, 0x00, 0xc6,
+ 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x40, 0x40, 0x00, 0xae, 0x00, 0xd2, 0x00, 0xae, 0x00, 0xd2,
+ 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0xa8, 0x00, 0xc0, 0x00, 0x66, 0x00, 0xc0,
+ 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x0a, 0x00, 0x54, 0x00, 0x0a, 0x00, 0x54,
+ 0x00, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6,
+ 0x00, 0x5d, 0x00, 0xc7, 0x00, 0x7e, 0x00, 0x30,
+ 0x00, 0x80, 0x1f, 0x98, 0x43, 0x3f, 0x0d, 0x88, 0x20, 0x80, 0x3f,
+ 0x47, 0xaf, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11,
+ 0x00, 0x0c, 0x02, 0x0c, 0x00, 0x1c, 0x00, 0x94,
+ 0x00, 0x10, 0x06, 0x24, 0x00, 0x4a, 0x00,
+ 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+ 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0,
+ 0x40, 0x20,
+ 0x03, 0x00, 0x03, 0x03, 0x00, 0x00,
+ 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0xff, 0x00,
+ 0x06, 0x00, 0x02, 0x09, 0x99,
+ 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x40, 0xa0, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a,
+ 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x49, 0x13, 0x00, 0x00, 0xe0, 0x00, 0x0c,
+ 0x00, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06,
+ 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80,
+ 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99,
+ 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc,
+ 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+ 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+ 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+ 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+ 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32,
+ 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3,
+ 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x82,
+ 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40,
+ 0x01, 0xf0, 0x00,
+ 0x02, 0x00, 0x11, 0x3c, 0x50, 0x8c, 0x3c, 0x50, 0x00, 0x00, 0x00,
+ 0x00, 0x78, 0x3f, 0x3f, 0x06, 0xf2, 0x8f, 0xf0,
+ 0x40,
+ 0x10, 0x1a, 0x01, 0x03,
+ 0x10, 0x00, 0x01, 0xac,
+ 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00,
+ 0x10, 0x1b, 0x02, 0x3b, 0x01,
+ 0x10, 0x11, 0x08, 0x61, 0x00, 0xe0, 0x00, 0x49, 0x00, 0xa8, 0x00,
+ 0x10, 0x1f, 0x06, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00,
+ 0x10, 0x1d, 0x02, 0x40, 0x06,
+ 0x10, 0x0e, 0x01, 0x08,
+ 0x10, 0x41, 0x11, 0x00, 0x0f, 0x46, 0x62, 0x76, 0x86, 0x94, 0xa0,
+ 0xab, 0xb6, 0xbf, 0xc8, 0xcf, 0xd7, 0xdc, 0xdc,
+ 0xdc,
+ 0x10, 0x03, 0x01, 0x00,
+ 0x10, 0x0f, 0x02, 0x12, 0x12,
+ 0x10, 0x03, 0x01, 0x0c,
+ 0x10, 0x41, 0x11, 0x00, 0x0f, 0x46, 0x62, 0x76, 0x86, 0x94, 0xa0,
+ 0xab, 0xb6, 0xbf, 0xc8, 0xcf, 0xd7, 0xdc, 0xdc,
+ 0xdc,
+ 0x10, 0x0b, 0x01, 0x09,
+ 0x10, 0x0d, 0x01, 0x10,
+ 0x10, 0x0c, 0x01, 0x2f,
+ 0x04, 0x06, 0x01, 0x03,
+ 0x04, 0x04, 0x01, 0x00,
+ 0, 0, 0
+};
+
+/* nw802 - D-link dru-350c cam */
+static const u8 dlink_start[] = {
+ 0x04, 0x06, 0x01, 0x04,
+ 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0x92, 0x03, 0x10, 0x00, 0x4d,
+ 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19,
+ 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+ 0x00, 0x01, 0x00, 0x19, 0x00, 0xce, 0x00, 0xf4,
+ 0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86,
+ 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e,
+ 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+ 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+ 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46,
+ 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0,
+ 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e,
+ 0x00, 0x80, 0x1f, 0xb4, 0x6f, 0x3f, 0x0f, 0x88, 0x20, 0x68, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11,
+ 0x00, 0x0c, 0x02, 0x01, 0x00, 0x16, 0x00, 0x94,
+ 0x00, 0x10, 0x06, 0x10, 0x00, 0x36, 0x00,
+ 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+ 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0,
+ 0x40, 0x20,
+ 0x03, 0x00, 0x03, 0x03, 0x00, 0x00,
+ 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00,
+ 0x06, 0x00, 0x02, 0x09, 0x99,
+ 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x40, 0xa1, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a,
+ 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x49, 0x13, 0x00, 0x00, 0xc0, 0x00, 0x14,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06,
+ 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80,
+ 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99,
+ 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc,
+ 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+ 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+ 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+ 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+ 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32,
+ 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3,
+ 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x01, 0x82,
+ 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40,
+ 0x01, 0xf0, 0x00,
+ 0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00,
+ 0x00, 0x78, 0x3f, 0x10, 0x02, 0xf2, 0x8f, 0x78,
+ 0x40,
+ 0x10, 0x1a, 0x01, 0x00,
+ 0x10, 0x00, 0x01, 0xad,
+ 0x00, 0x00, 0x01, 0x08,
+ 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00,
+ 0x10, 0x1b, 0x02, 0x00, 0x00,
+ 0x10, 0x11, 0x08, 0x51, 0x00, 0xf0, 0x00, 0x3d, 0x00, 0xb4, 0x00,
+ 0x10, 0x1d, 0x08, 0x40, 0x06, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00,
+ 0x10, 0x0e, 0x01, 0x20,
+ 0x10, 0x41, 0x11, 0x00, 0x07, 0x1e, 0x38, 0x4d, 0x60, 0x70, 0x7f,
+ 0x8e, 0x9b, 0xa8, 0xb4, 0xbf, 0xca, 0xd5, 0xdf,
+ 0xea,
+ 0x10, 0x03, 0x01, 0x00,
+ 0x10, 0x0f, 0x02, 0x11, 0x11,
+ 0x10, 0x03, 0x01, 0x10,
+ 0x10, 0x41, 0x11, 0x00, 0x07, 0x1e, 0x38, 0x4d, 0x60, 0x70, 0x7f,
+ 0x8e, 0x9b, 0xa8, 0xb4, 0xbf, 0xca, 0xd5, 0xdf,
+ 0xea,
+ 0x10, 0x0b, 0x01, 0x19,
+ 0x10, 0x0d, 0x01, 0x10,
+ 0x10, 0x0c, 0x01, 0x1e,
+ 0x04, 0x06, 0x01, 0x03,
+ 0x04, 0x04, 0x01, 0x00,
+ 0, 0, 0
+};
+
+/* 06a5:d001 - nw801 - Sony
+ * Plustek Opticam 500U or ProLink DS3303u (Hitachi HD49322BF) */
+/*fixme: 320x240 only*/
+static const u8 ds3303_start[] = {
+ 0x05, 0x06, 0x01, 0x04,
+ 0x00, 0x00, 0x40, 0x16, 0x00, 0x00, 0xf9, 0x02, 0x11, 0x00, 0x0e,
+ 0x01, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19,
+ 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+ 0x00, 0x01, 0x00, 0x19, 0x00, 0xce, 0x00, 0xf4,
+ 0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86,
+ 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e,
+ 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+ 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+ 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46,
+ 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0,
+ 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e,
+ 0x00, 0x80, 0x22, 0xb4, 0x6f, 0x3f, 0x0f, 0x88, 0x20, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xa9, 0xa8, 0x1f, 0x00,
+ 0x0d, 0x02, 0x07, 0x00, 0x01, 0x00, 0x19, 0x00,
+ 0xf2, 0x00, 0x18, 0x06, 0x10, 0x06, 0x10, 0x00,
+ 0x36, 0x00,
+ 0x02, 0x00, 0x12, 0x03, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+ 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0x50,
+ 0x40, 0x20,
+ 0x03, 0x00, 0x03, 0x03, 0x00, 0x00,
+ 0x05, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0xff, 0x00,
+ 0x06, 0x00, 0x02, 0x09, 0x99,
+ 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x40, 0x2f, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x1f, 0x10, 0x08, 0x0a,
+ 0x0a, 0x51, 0x00, 0xf1, 0x00, 0x3c, 0x00, 0xb4,
+ 0x00, 0x01, 0x15, 0xfd, 0x07, 0x3d, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x8c, 0x04, 0x01, 0x20,
+ 0x02, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06, 0xf7,
+ 0x10, 0x40, 0x40, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80, 0x80,
+ 0x00, 0x2d, 0x46, 0x58, 0x67, 0x74, 0x7f, 0x88,
+ 0x94, 0x9d, 0xa6, 0xae, 0xb5, 0xbd, 0xc4, 0xcb,
+ 0xd1, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54, 0x64,
+ 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2, 0xe2,
+ 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+ 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+ 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+ 0x10, 0x80, 0x22, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+ 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x3f, 0x01,
+ 0x00, 0x00, 0xef, 0x00, 0x02, 0x0a, 0x82, 0x02,
+ 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40, 0x01,
+ 0xf0, 0x00,
+
+ 0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00,
+ 0x00, 0x78, 0x3f, 0x3f, 0x00, 0xf2, 0x8f, 0x81,
+ 0x40,
+ 0x10, 0x1a, 0x01, 0x15,
+ 0x10, 0x00, 0x01, 0x2f,
+ 0x10, 0x8c, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00,
+ 0x10, 0x1b, 0x02, 0x00, 0x00,
+ 0x10, 0x11, 0x08, 0x61, 0x00, 0xe0, 0x00, 0x49, 0x00, 0xa8, 0x00,
+ 0x10, 0x26, 0x06, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00,
+ 0x10, 0x24, 0x02, 0x40, 0x06,
+ 0x10, 0x0e, 0x01, 0x08,
+ 0x10, 0x48, 0x11, 0x00, 0x15, 0x40, 0x67, 0x84, 0x9d, 0xb2, 0xc6,
+ 0xd6, 0xe7, 0xf6, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xf9,
+ 0x10, 0x03, 0x01, 0x00,
+ 0x10, 0x0f, 0x02, 0x16, 0x16,
+ 0x10, 0x03, 0x01, 0x0c,
+ 0x10, 0x48, 0x11, 0x00, 0x15, 0x40, 0x67, 0x84, 0x9d, 0xb2, 0xc6,
+ 0xd6, 0xe7, 0xf6, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xf9,
+ 0x10, 0x0b, 0x01, 0x26,
+ 0x10, 0x0d, 0x01, 0x10,
+ 0x10, 0x0c, 0x01, 0x1c,
+ 0x05, 0x06, 0x01, 0x03,
+ 0x05, 0x04, 0x01, 0x00,
+ 0, 0, 0
+};
+
+/* 06a5:d001 - nw802 - Panasonic
+ * GP-KR651US (Philips TDA8786) */
+static const u8 kr651_start_1[] = {
+ 0x04, 0x06, 0x01, 0x04,
+ 0x00, 0x00, 0x40, 0x44, 0x96, 0x98, 0xf9, 0x02, 0x18, 0x00, 0x48,
+ 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19,
+ 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+ 0x00, 0x0b, 0x00, 0x1b, 0x00, 0xc8, 0x00, 0xf4,
+ 0x05, 0xb4, 0x00, 0xcc, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0xa2, 0x00, 0xc6, 0x00, 0x60, 0x00, 0xc6,
+ 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x40, 0x40, 0x00, 0xae, 0x00, 0xd2, 0x00, 0xae, 0x00, 0xd2,
+ 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0xa8, 0x00, 0xc0, 0x00, 0x66, 0x00, 0xc0,
+ 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x0a, 0x00, 0x54, 0x00, 0x0a, 0x00, 0x54,
+ 0x00, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6,
+ 0x00, 0x5d, 0x00, 0xc7, 0x00, 0x7e, 0x00, 0x30,
+ 0x00, 0x80, 0x1f, 0x18, 0x43, 0x3f, 0x0d, 0x88, 0x20, 0x80, 0x3f,
+ 0x47, 0xaf, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11,
+ 0x00, 0x0c, 0x02, 0x0c, 0x00, 0x1c, 0x00, 0x94,
+ 0x00, 0x10, 0x06, 0x24, 0x00, 0x4a, 0x00,
+ 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+ 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0,
+ 0x40, 0x20,
+ 0x03, 0x00, 0x03, 0x02, 0x00, 0x00,
+ 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00,
+ 0x06, 0x00, 0x02, 0x09, 0x99,
+ 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x40, 0xa0, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a,
+ 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x49, 0x13, 0x00, 0x00, 0xe0, 0x00, 0x0c,
+ 0x00, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06,
+ 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80,
+ 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99,
+ 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc,
+ 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+ 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+ 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+ 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+ 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32,
+ 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3,
+ 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x82,
+ 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40,
+ 0x01, 0xf0, 0x00,
+ 0, 0, 0
+};
+static const u8 kr651_start_qvga[] = {
+ 0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00,
+ 0x00, 0x78, 0x3f, 0x10, 0x02, 0xf2, 0x8f, 0x78,
+ 0x40,
+ 0x10, 0x1a, 0x01, 0x03,
+ 0x10, 0x00, 0x01, 0xac,
+ 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00,
+ 0x10, 0x1b, 0x02, 0x00, 0x00,
+ 0x10, 0x11, 0x08, 0x29, 0x00, 0x18, 0x01, 0x1f, 0x00, 0xd2, 0x00,
+ 0x10, 0x1d, 0x06, 0xe0, 0x00, 0x0c, 0x00, 0x52, 0x00,
+ 0x10, 0x1d, 0x02, 0x28, 0x01,
+ 0, 0, 0
+};
+static const u8 kr651_start_vga[] = {
+ 0x02, 0x00, 0x11, 0x78, 0xa0, 0x8c, 0x78, 0xa0, 0x00, 0x00, 0x00,
+ 0x00, 0xf0, 0x30, 0x03, 0x01, 0x82, 0x82, 0x98,
+ 0x80,
+ 0x10, 0x1a, 0x01, 0x03,
+ 0x10, 0x00, 0x01, 0xa0,
+ 0x10, 0x85, 0x08, 0x00, 0x00, 0x7f, 0x02, 0x00, 0x00, 0xdf, 0x01,
+ 0x10, 0x1b, 0x02, 0x00, 0x00,
+ 0x10, 0x11, 0x08, 0x51, 0x00, 0x30, 0x02, 0x3d, 0x00, 0xa4, 0x01,
+ 0x10, 0x1d, 0x06, 0xe0, 0x00, 0x0c, 0x00, 0x52, 0x00,
+ 0x10, 0x1d, 0x02, 0x68, 0x00,
+};
+static const u8 kr651_start_2[] = {
+ 0x10, 0x0e, 0x01, 0x08,
+ 0x10, 0x41, 0x11, 0x00, 0x11, 0x3c, 0x5c, 0x74, 0x88, 0x99, 0xa8,
+ 0xb7, 0xc4, 0xd0, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+ 0xdc,
+ 0x10, 0x03, 0x01, 0x00,
+ 0x10, 0x0f, 0x02, 0x0c, 0x0c,
+ 0x10, 0x03, 0x01, 0x0c,
+ 0x10, 0x41, 0x11, 0x00, 0x11, 0x3c, 0x5c, 0x74, 0x88, 0x99, 0xa8,
+ 0xb7, 0xc4, 0xd0, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+ 0xdc,
+ 0x10, 0x0b, 0x01, 0x10,
+ 0x10, 0x0d, 0x01, 0x10,
+ 0x10, 0x0c, 0x01, 0x2d,
+ 0x04, 0x06, 0x01, 0x03,
+ 0x04, 0x04, 0x01, 0x00,
+ 0, 0, 0
+};
+
+/* nw802 - iRez Kritter cam */
+static const u8 kritter_start[] = {
+ 0x04, 0x06, 0x01, 0x06,
+ 0x00, 0x00, 0x40, 0x44, 0x96, 0x98, 0x94, 0x03, 0x18, 0x00, 0x48,
+ 0x0f, 0x1e, 0x00, 0x0c, 0x02, 0x01, 0x00, 0x19,
+ 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+ 0x00, 0x0b, 0x00, 0x1b, 0x00, 0x0a, 0x01, 0x28,
+ 0x07, 0xb4, 0x00, 0xcc, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0xa2, 0x00, 0xc6, 0x00, 0x60, 0x00, 0xc6,
+ 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x40, 0x40, 0x00, 0xae, 0x00, 0xd2, 0x00, 0xae, 0x00, 0xd2,
+ 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0xa8, 0x00, 0xc0, 0x00, 0x66, 0x00, 0xc0,
+ 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x0a, 0x00, 0x54, 0x00, 0x0a, 0x00, 0x54,
+ 0x00, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6,
+ 0x00, 0x5d, 0x00, 0x0e, 0x00, 0x7e, 0x00, 0x30,
+ 0x00, 0x80, 0x1f, 0x18, 0x43, 0x3f, 0x0d, 0x88, 0x20, 0x80, 0x3f,
+ 0x47, 0xaf, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11,
+ 0x00, 0x0b, 0x02, 0x0c, 0x00, 0x1c, 0x00, 0x94,
+ 0x00, 0x10, 0x06, 0x24, 0x00, 0x4a, 0x00,
+ 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+ 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0,
+ 0x40, 0x20,
+ 0x03, 0x00, 0x03, 0x02, 0x00, 0x00,
+ 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0xff, 0x00,
+ 0x06, 0x00, 0x02, 0x09, 0x99,
+ 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x40, 0xa0, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a,
+ 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x49, 0x13, 0x00, 0x00, 0xe0, 0x00, 0x0c,
+ 0x00, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06,
+ 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80,
+ 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99,
+ 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc,
+ 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+ 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+ 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+ 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+ 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32,
+ 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3,
+ 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x82,
+ 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40,
+ 0x01, 0xf0, 0x00,
+ 0x02, 0x00, 0x11, 0x3c, 0x50, 0x8c, 0x3c, 0x50, 0x00, 0x00, 0x00,
+ 0x00, 0x78, 0x3f, 0x3f, 0x06, 0xf2, 0x8f, 0xf0,
+ 0x40,
+ 0x10, 0x1a, 0x01, 0x03,
+ 0x10, 0x00, 0x01, 0xaf,
+ 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00,
+ 0x10, 0x1b, 0x02, 0x3b, 0x01,
+ 0x10, 0x11, 0x08, 0x61, 0x00, 0xe0, 0x00, 0x49, 0x00, 0xa8, 0x00,
+ 0x10, 0x1d, 0x06, 0xe0, 0x00, 0x0c, 0x00, 0x52, 0x00,
+ 0x10, 0x1d, 0x02, 0x00, 0x00,
+ 0x10, 0x0e, 0x01, 0x08,
+ 0x10, 0x41, 0x11, 0x00, 0x0d, 0x36, 0x4e, 0x60, 0x6f, 0x7b, 0x86,
+ 0x90, 0x98, 0xa1, 0xa9, 0xb1, 0xb7, 0xbe, 0xc4,
+ 0xcb,
+ 0x10, 0x03, 0x01, 0x00,
+ 0x10, 0x0f, 0x02, 0x0d, 0x0d,
+ 0x10, 0x03, 0x01, 0x02,
+ 0x10, 0x41, 0x11, 0x00, 0x0d, 0x36, 0x4e, 0x60, 0x6f, 0x7b, 0x86,
+ 0x90, 0x98, 0xa1, 0xa9, 0xb1, 0xb7, 0xbe, 0xc4,
+ 0xcb,
+ 0x10, 0x0b, 0x01, 0x17,
+ 0x10, 0x0d, 0x01, 0x10,
+ 0x10, 0x0c, 0x01, 0x1e,
+ 0x04, 0x06, 0x01, 0x03,
+ 0x04, 0x04, 0x01, 0x00,
+ 0, 0, 0
+};
+
+/* nw802 - Mustek Wcam 300 mini */
+static const u8 mustek_start[] = {
+ 0x04, 0x06, 0x01, 0x04,
+ 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0x92, 0x03, 0x10, 0x00, 0x4d,
+ 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19,
+ 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+ 0x00, 0x01, 0x00, 0x19, 0x00, 0xce, 0x00, 0xf4,
+ 0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86,
+ 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e,
+ 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+ 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+ 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46,
+ 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0,
+ 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e,
+ 0x00, 0x80, 0x1f, 0xb4, 0x6f, 0x3f, 0x0f, 0x88, 0x20, 0x68, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11,
+ 0x00, 0x0c, 0x02, 0x01, 0x00, 0x16, 0x00, 0x94,
+ 0x00, 0x10, 0x06, 0xfc, 0x05, 0x0c, 0x06,
+ 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+ 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0,
+ 0x40, 0x20,
+ 0x03, 0x00, 0x03, 0x03, 0x00, 0x00,
+ 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00,
+ 0x06, 0x00, 0x02, 0x09, 0x99,
+ 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x40, 0xa1, 0x02, 0x80, 0x00, 0x13, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a,
+ 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x49, 0x13, 0x00, 0x00, 0xc0, 0x00, 0x14,
+ 0x02, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06,
+ 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80,
+ 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99,
+ 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc,
+ 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+ 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+ 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+ 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+ 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32,
+ 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3,
+ 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x01, 0x82,
+ 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40,
+ 0x01, 0xf0, 0x00,
+ 0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00,
+ 0x00, 0x78, 0x3f, 0x10, 0x02, 0xf2, 0x8f, 0x78,
+ 0x40,
+ 0x10, 0x1a, 0x01, 0x00,
+ 0x10, 0x00, 0x01, 0xad,
+ 0x00, 0x00, 0x01, 0x08,
+ 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00,
+ 0x10, 0x1b, 0x02, 0x00, 0x00,
+ 0x10, 0x11, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00,
+ 0x10, 0x1d, 0x08, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20,
+ 0x10, 0x0e, 0x01, 0x0f,
+ 0x10, 0x41, 0x11, 0x00, 0x0f, 0x29, 0x4a, 0x64, 0x7a, 0x8c, 0x9e,
+ 0xad, 0xba, 0xc7, 0xd3, 0xde, 0xe8, 0xf1, 0xf9,
+ 0xff,
+ 0x10, 0x0f, 0x02, 0x11, 0x11,
+ 0x10, 0x03, 0x01, 0x0c,
+ 0x10, 0x41, 0x11, 0x00, 0x0f, 0x29, 0x4a, 0x64, 0x7a, 0x8c, 0x9e,
+ 0xad, 0xba, 0xc7, 0xd3, 0xde, 0xe8, 0xf1, 0xf9,
+ 0xff,
+ 0x10, 0x0b, 0x01, 0x1c,
+ 0x10, 0x0d, 0x01, 0x1a,
+ 0x10, 0x0c, 0x01, 0x34,
+ 0x04, 0x05, 0x01, 0x61,
+ 0x04, 0x04, 0x01, 0x40,
+ 0x04, 0x06, 0x01, 0x03,
+ 0, 0, 0
+};
+
+/* nw802 - Scope USB Microscope M2 (ProScope) (Hitachi HD49322BF) */
+static const u8 proscope_init[] = {
+ 0x04, 0x05, 0x01, 0x21,
+ 0x04, 0x04, 0x01, 0x01,
+ 0, 0, 0
+};
+static const u8 proscope_start_1[] = {
+ 0x04, 0x06, 0x01, 0x04,
+ 0x00, 0x00, 0x40, 0x10, 0x01, 0x00, 0xf9, 0x02, 0x10, 0x00, 0x04,
+ 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19,
+ 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+ 0x00, 0x08, 0x00, 0x17, 0x00, 0xce, 0x00, 0xf4,
+ 0x05, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86,
+ 0x00, 0xce, 0x00, 0xf8, 0x03, 0x3e, 0x00, 0x86,
+ 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e,
+ 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0xb6,
+ 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+ 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0xf6, 0x03, 0x34, 0x04, 0xf6, 0x03, 0x34,
+ 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xe8,
+ 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e,
+ 0x00, 0x80, 0x1f, 0xb4, 0x6f, 0x1f, 0x0f, 0x08, 0x20, 0xa8, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11,
+ 0x00, 0x0c, 0x02, 0x01, 0x00, 0x19, 0x00, 0x94,
+ 0x00, 0x10, 0x06, 0x10, 0x00, 0x36, 0x00,
+ 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+ 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0,
+ 0x40, 0x20,
+ 0x03, 0x00, 0x03, 0x03, 0x00, 0x00,
+ 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00,
+ 0x06, 0x00, 0x02, 0x09, 0x99,
+ 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x40, 0xad, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x1f, 0x10, 0x08, 0x0a,
+ 0x0a, 0x51, 0x00, 0xf1, 0x00, 0x3c, 0x00, 0xb4,
+ 0x00, 0x49, 0x13, 0x00, 0x00, 0x8c, 0x04, 0x01,
+ 0x20, 0x02, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06,
+ 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80,
+ 0x10, 0x40, 0x40, 0x80, 0x00, 0x2d, 0x46, 0x58, 0x67, 0x74, 0x7f,
+ 0x88, 0x94, 0x9d, 0xa6, 0xae, 0xb5, 0xbd, 0xc4,
+ 0xcb, 0xd1, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+ 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+ 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+ 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+ 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32,
+ 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3,
+ 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x3f,
+ 0x01, 0x00, 0x00, 0xef, 0x00, 0x09, 0x05, 0x82,
+ 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40,
+ 0x01, 0xf0, 0x00,
+ 0, 0, 0
+};
+static const u8 proscope_start_qvga[] = {
+ 0x02, 0x00, 0x11, 0x3c, 0x50, 0x9e, 0x3c, 0x50, 0x00, 0x00, 0x00,
+ 0x00, 0x78, 0x3f, 0x10, 0x02, 0xf2, 0x8f, 0x78,
+ 0x40,
+ 0x10, 0x1a, 0x01, 0x06,
+ 0x00, 0x03, 0x02, 0xf9, 0x02,
+ 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00,
+ 0x10, 0x1b, 0x02, 0x00, 0x00,
+ 0x10, 0x11, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00,
+ 0x10, 0x1d, 0x08, 0xc0, 0x0d, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00,
+ 0x10, 0x0e, 0x01, 0x10,
+ 0, 0, 0
+};
+static const u8 proscope_start_vga[] = {
+ 0x00, 0x03, 0x02, 0xf9, 0x02,
+ 0x10, 0x85, 0x08, 0x00, 0x00, 0x7f, 0x02, 0x00, 0x00, 0xdf, 0x01,
+ 0x02, 0x00, 0x11, 0x78, 0xa0, 0x8c, 0x78, 0xa0, 0x00, 0x00, 0x00,
+ 0x00, 0xf0, 0x16, 0x00, 0x00, 0x82, 0x84, 0x00,
+ 0x80,
+ 0x10, 0x1a, 0x01, 0x06,
+ 0x10, 0x00, 0x01, 0xa1,
+ 0x10, 0x1b, 0x02, 0x00, 0x00,
+ 0x10, 0x1d, 0x08, 0xc0, 0x0d, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00,
+ 0x10, 0x11, 0x08, 0x00, 0x00, 0x7f, 0x02, 0x00, 0x00, 0xdf, 0x01,
+ 0x10, 0x0e, 0x01, 0x10,
+ 0x10, 0x41, 0x11, 0x00, 0x10, 0x51, 0x6e, 0x83, 0x93, 0xa1, 0xae,
+ 0xb9, 0xc3, 0xcc, 0xd4, 0xdd, 0xe4, 0xeb, 0xf2,
+ 0xf9,
+ 0x10, 0x03, 0x01, 0x00,
+ 0, 0, 0
+};
+static const u8 proscope_start_2[] = {
+ 0x10, 0x0f, 0x02, 0x0c, 0x0c,
+ 0x10, 0x03, 0x01, 0x0c,
+ 0x10, 0x41, 0x11, 0x00, 0x10, 0x51, 0x6e, 0x83, 0x93, 0xa1, 0xae,
+ 0xb9, 0xc3, 0xcc, 0xd4, 0xdd, 0xe4, 0xeb, 0xf2,
+ 0xf9,
+ 0x10, 0x0b, 0x01, 0x0b,
+ 0x10, 0x0d, 0x01, 0x10,
+ 0x10, 0x0c, 0x01, 0x1b,
+ 0x04, 0x06, 0x01, 0x03,
+ 0x04, 0x05, 0x01, 0x21,
+ 0x04, 0x04, 0x01, 0x00,
+ 0, 0, 0
+};
+
+/* nw800 - hv7121b? (seems pas106) - Divio Chicony TwinkleCam */
+static const u8 twinkle_start[] = {
+ 0x04, 0x06, 0x01, 0x44,
+ 0x04, 0x06, 0x01, 0x00,
+ 0x00, 0x00, 0x40, 0x14, 0x83, 0x00, 0xba, 0x01, 0x10, 0x00, 0x4f,
+ 0xef, 0x00, 0x00, 0x60, 0x00, 0x01, 0x00, 0x19,
+ 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+ 0x00, 0x01, 0x00, 0x19, 0x00, 0x06, 0x00, 0xfc,
+ 0x01, 0x3e, 0x00, 0x86, 0x00, 0x3e, 0x00, 0x86,
+ 0x00, 0x3e, 0x00, 0x86, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x56, 0x00, 0x9e, 0x00, 0x56, 0x00, 0x9e,
+ 0x00, 0x56, 0x00, 0x9e, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x40, 0x40, 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+ 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x6e, 0x00, 0xb6, 0x00, 0x6e, 0x00, 0x78,
+ 0x04, 0x6e, 0x00, 0xb6, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0xca, 0x03, 0x46, 0x04, 0xca, 0x03, 0x46,
+ 0x04, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0xf0,
+ 0x00, 0x3e, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x2e,
+ 0x00, 0x80, 0x1f, 0xb8, 0x48, 0x0f, 0x04, 0x88, 0x14, 0x68, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xa8, 0x01, 0x00, 0x03,
+ 0x00, 0x24, 0x01, 0x01, 0x00, 0x16, 0x00, 0x04,
+ 0x00, 0x4b, 0x00, 0x76, 0x00, 0x86, 0x00,
+ 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+ 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0,
+ 0x40, 0x20,
+ 0x03, 0x00, 0x03, 0x03, 0x00, 0x00,
+ 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0x61, 0x00,
+ 0x05, 0x00, 0x06, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x40, 0x80, 0x02, 0x20, 0x00, 0x11, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x08,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x49, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08,
+ 0x03, 0x00, 0x00, 0x10, 0x00, 0x20, 0x10, 0x06,
+ 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x00, 0x80,
+ 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99,
+ 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc,
+ 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+ 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+ 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+ 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+ 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32,
+ 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3,
+ 0x10, 0x80, 0x1d, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x62,
+ 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01, 0x20,
+ 0x01, 0x60, 0x01, 0x00, 0x00,
+
+ 0x10, 0x85, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01,
+ 0x04, 0x04, 0x01, 0x10,
+ 0x04, 0x04, 0x01, 0x00,
+ 0x04, 0x05, 0x01, 0x61,
+ 0x04, 0x04, 0x01, 0x01,
+ I2C0, 0x40, 0x0c, 0x02, 0x0c, 0x12, 0x07, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0a,
+ I2C0, 0x40, 0x02, 0x11, 0x06,
+ I2C0, 0x40, 0x02, 0x14, 0x00,
+ I2C0, 0x40, 0x02, 0x13, 0x01, /* i2c end */
+ I2C0, 0x40, 0x02, 0x07, 0x01,
+ 0x02, 0x00, 0x11, 0x48, 0x58, 0x9e, 0x48, 0x58, 0x00, 0x00, 0x00,
+ 0x00, 0x84, 0x36, 0x05, 0x01, 0xf2, 0x86, 0x65,
+ 0x40,
+ I2C0, 0x40, 0x02, 0x02, 0x0c,
+ I2C0, 0x40, 0x02, 0x13, 0x01,
+ 0x10, 0x00, 0x01, 0x01,
+ 0x10, 0x8f, 0x0c, 0x62, 0x01, 0x24, 0x01, 0x62, 0x01, 0x24, 0x01,
+ 0x20, 0x01, 0x60, 0x01,
+ I2C0, 0x40, 0x02, 0x05, 0x0f,
+ I2C0, 0x40, 0x02, 0x13, 0x01,
+ I2C0, 0x40, 0x08, 0x08, 0x04, 0x0b, 0x01, 0x01, 0x02, 0x00, 0x17,
+ I2C0, 0x40, 0x03, 0x12, 0x00, 0x01,
+ 0x10, 0x11, 0x08, 0x00, 0x00, 0x5f, 0x01, 0x00, 0x00, 0x1f, 0x01,
+ I2C0, 0x40, 0x02, 0x12, 0x00,
+ I2C0, 0x40, 0x02, 0x0e, 0x00,
+ I2C0, 0x40, 0x02, 0x11, 0x06,
+ 0x10, 0x41, 0x11, 0x00, 0x17, 0x3f, 0x69, 0x7b, 0x8c, 0x9a, 0xa7,
+ 0xb3, 0xbf, 0xc9, 0xd3, 0xdd, 0xe6, 0xef, 0xf7,
+ 0xf9,
+ 0x10, 0x03, 0x01, 0x00,
+ 0x10, 0x0f, 0x02, 0x0c, 0x0c,
+ 0x10, 0x03, 0x01, 0x06,
+ 0x10, 0x41, 0x11, 0x00, 0x17, 0x3f, 0x69, 0x7b, 0x8c, 0x9a, 0xa7,
+ 0xb3, 0xbf, 0xc9, 0xd3, 0xdd, 0xe6, 0xef, 0xf7,
+ 0xf9,
+ 0x10, 0x0b, 0x01, 0x19,
+ 0x10, 0x0d, 0x01, 0x10,
+ 0x10, 0x0c, 0x01, 0x0d,
+ 0x04, 0x06, 0x01, 0x03,
+ 0x04, 0x05, 0x01, 0x61,
+ 0x04, 0x04, 0x01, 0x41,
+ 0, 0, 0
+};
+
+/* nw802 dvc-v6 */
+static const u8 dvcv6_start[] = {
+ 0x04, 0x06, 0x01, 0x06,
+ 0x00, 0x00, 0x40, 0x54, 0x96, 0x98, 0xf9, 0x02, 0x18, 0x00, 0x4c,
+ 0x0f, 0x1f, 0x00, 0x0d, 0x02, 0x01, 0x00, 0x19,
+ 0x00, 0x01, 0x00, 0x19, 0x00, 0x01, 0x00, 0x19,
+ 0x00, 0x0b, 0x00, 0x1b, 0x00, 0xc8, 0x00, 0xf4,
+ 0x05, 0xb4, 0x00, 0xcc, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0xa2, 0x00, 0xc6, 0x00, 0x60, 0x00, 0xc6,
+ 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x40, 0x40, 0x00, 0xae, 0x00, 0xd2, 0x00, 0xae, 0x00, 0xd2,
+ 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0xa8, 0x00, 0xc0, 0x00, 0x66, 0x00, 0xc0,
+ 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x0a, 0x00, 0x54, 0x00, 0x0a, 0x00, 0x54,
+ 0x00, 0x10, 0x00, 0x36, 0x00, 0xd2, 0x00, 0xee,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6,
+ 0x00, 0x5d, 0x00, 0xc7, 0x00, 0x7e, 0x00, 0x30,
+ 0x00, 0x80, 0x1f, 0x98, 0x43, 0x3f, 0x0d, 0x88, 0x20, 0x80, 0x3f,
+ 0x47, 0xaf, 0x00, 0x00, 0xa8, 0x08, 0x00, 0x11,
+ 0x00, 0x0c, 0x02, 0x0c, 0x00, 0x1c, 0x00, 0x94,
+ 0x00, 0x10, 0x06, 0x24, 0x00, 0x4a, 0x00,
+ 0x02, 0x00, 0x12, 0x78, 0xa0, 0x9e, 0x78, 0xa0, 0x00, 0x00, 0x00,
+ 0x00, 0xf0, 0x18, 0x0b, 0x06, 0x62, 0x82, 0xa0,
+ 0x40, 0x20,
+ 0x03, 0x00, 0x03, 0x03, 0x00, 0x00,
+ 0x04, 0x00, 0x07, 0x01, 0x10, 0x00, 0x00, 0x00, 0xff, 0x00,
+ 0x06, 0x00, 0x02, 0x09, 0x99,
+ 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x40, 0xa0, 0x02, 0x80, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x08, 0x0a,
+ 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x49, 0x13, 0x00, 0x00, 0xe0, 0x00, 0x0c,
+ 0x00, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x06,
+ 0xf7, 0xee, 0x1c, 0x1c, 0xe9, 0xfc, 0x10, 0x80,
+ 0x10, 0x40, 0x40, 0x80, 0x00, 0x05, 0x35, 0x5e, 0x78, 0x8b, 0x99,
+ 0xa4, 0xae, 0xb5, 0xbc, 0xc1, 0xc6, 0xc9, 0xcc,
+ 0xcf, 0xd0, 0x00, 0x11, 0x22, 0x32, 0x43, 0x54,
+ 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3, 0xd2,
+ 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32, 0x43,
+ 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3, 0xc3,
+ 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x11, 0x22, 0x32,
+ 0x43, 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb3,
+ 0x10, 0x80, 0x1b, 0xc3, 0xd2, 0xe2, 0xf1, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x82,
+ 0x02, 0xe4, 0x01, 0x40, 0x01, 0xf0, 0x00, 0x40,
+ 0x01, 0xf0, 0x00,
+ 0x00, 0x03, 0x02, 0x94, 0x03,
+ 0x00, 0x1d, 0x04, 0x0a, 0x01, 0x28, 0x07,
+ 0x00, 0x7b, 0x02, 0xe0, 0x00,
+ 0x10, 0x8d, 0x01, 0x00,
+ 0x00, 0x09, 0x04, 0x1e, 0x00, 0x0c, 0x02,
+ 0x00, 0x91, 0x02, 0x0b, 0x02,
+ 0x10, 0x00, 0x01, 0xaf,
+ 0x02, 0x00, 0x11, 0x3c, 0x50, 0x8f, 0x3c, 0x50, 0x00, 0x00, 0x00,
+ 0x00, 0x78, 0x3f, 0x3f, 0x06, 0xf2, 0x8f, 0xf0,
+ 0x40,
+ 0x10, 0x1a, 0x01, 0x02,
+ 0x10, 0x00, 0x01, 0xaf,
+ 0x10, 0x85, 0x08, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0xef, 0x00,
+ 0x10, 0x1b, 0x02, 0x07, 0x01,
+ 0x10, 0x11, 0x08, 0x61, 0x00, 0xe0, 0x00, 0x49, 0x00, 0xa8, 0x00,
+ 0x10, 0x1f, 0x06, 0x01, 0x20, 0x02, 0xe8, 0x03, 0x00,
+ 0x10, 0x1d, 0x02, 0x40, 0x06,
+ 0x10, 0x0e, 0x01, 0x08,
+ 0x10, 0x41, 0x11, 0x00, 0x0f, 0x54, 0x6f, 0x82, 0x91, 0x9f, 0xaa,
+ 0xb4, 0xbd, 0xc5, 0xcd, 0xd5, 0xdb, 0xdc, 0xdc,
+ 0xdc,
+ 0x10, 0x03, 0x01, 0x00,
+ 0x10, 0x0f, 0x02, 0x12, 0x12,
+ 0x10, 0x03, 0x01, 0x11,
+ 0x10, 0x41, 0x11, 0x00, 0x0f, 0x54, 0x6f, 0x82, 0x91, 0x9f, 0xaa,
+ 0xb4, 0xbd, 0xc5, 0xcd, 0xd5, 0xdb, 0xdc, 0xdc,
+ 0xdc,
+ 0x10, 0x0b, 0x01, 0x16,
+ 0x10, 0x0d, 0x01, 0x10,
+ 0x10, 0x0c, 0x01, 0x1a,
+ 0x04, 0x06, 0x01, 0x03,
+ 0x04, 0x04, 0x01, 0x00,
+};
+
+static const u8 *webcam_start[] = {
+ [Generic800] = nw800_start,
+ [SpaceCam] = spacecam_start,
+ [SpaceCam2] = spacecam2_start,
+ [Cvideopro] = cvideopro_start,
+ [Dlink350c] = dlink_start,
+ [DS3303u] = ds3303_start,
+ [Kr651us] = kr651_start_1,
+ [Kritter] = kritter_start,
+ [Mustek300] = mustek_start,
+ [Proscope] = proscope_start_1,
+ [Twinkle] = twinkle_start,
+ [DvcV6] = dvcv6_start,
+ [P35u] = nw801_start_1,
+ [Generic802] = nw802_start,
+};
+
+/* -- write a register -- */
+static void reg_w(struct gspca_dev *gspca_dev,
+ u16 index,
+ const u8 *data,
+ int len)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ if (len == 1)
+ PDEBUG(D_USBO, "SET 00 0000 %04x %02x", index, *data);
+ else
+ PDEBUG(D_USBO, "SET 00 0000 %04x %02x %02x ...",
+ index, *data, data[1]);
+ memcpy(gspca_dev->usb_buf, data, len);
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x00,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x00, /* value */
+ index,
+ gspca_dev->usb_buf,
+ len,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_w err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+/* -- read registers in usb_buf -- */
+static void reg_r(struct gspca_dev *gspca_dev,
+ u16 index,
+ int len)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ 0x00,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x00, index,
+ gspca_dev->usb_buf, len, 500);
+ if (ret < 0) {
+ pr_err("reg_r err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ return;
+ }
+ if (len == 1)
+ PDEBUG(D_USBI, "GET 00 0000 %04x %02x",
+ index, gspca_dev->usb_buf[0]);
+ else
+ PDEBUG(D_USBI, "GET 00 0000 %04x %02x %02x ..",
+ index, gspca_dev->usb_buf[0],
+ gspca_dev->usb_buf[1]);
+}
+
+static void i2c_w(struct gspca_dev *gspca_dev,
+ u8 i2c_addr,
+ const u8 *data,
+ int len)
+{
+ u8 val[2];
+ int i;
+
+ reg_w(gspca_dev, 0x0600, data + 1, len - 1);
+ reg_w(gspca_dev, 0x0600, data, len);
+ val[0] = len;
+ val[1] = i2c_addr;
+ reg_w(gspca_dev, 0x0502, val, 2);
+ val[0] = 0x01;
+ reg_w(gspca_dev, 0x0501, val, 1);
+ for (i = 5; --i >= 0; ) {
+ msleep(4);
+ reg_r(gspca_dev, 0x0505, 1);
+ if (gspca_dev->usb_err < 0)
+ return;
+ if (gspca_dev->usb_buf[0] == 0)
+ return;
+ }
+ gspca_dev->usb_err = -ETIME;
+}
+
+static void reg_w_buf(struct gspca_dev *gspca_dev,
+ const u8 *cmd)
+{
+ u16 reg;
+ int len;
+
+ for (;;) {
+ reg = *cmd++ << 8;
+ reg += *cmd++;
+ len = *cmd++;
+ if (len == 0)
+ break;
+ if (cmd[-3] != I2C0)
+ reg_w(gspca_dev, reg, cmd, len);
+ else
+ i2c_w(gspca_dev, reg, cmd, len);
+ cmd += len;
+ }
+}
+
+static int swap_bits(int v)
+{
+ int r, i;
+
+ r = 0;
+ for (i = 0; i < 8; i++) {
+ r <<= 1;
+ if (v & 1)
+ r++;
+ v >>= 1;
+ }
+ return r;
+}
+
+static void setgain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val, v[2];
+
+ val = sd->ctrls[GAIN].val;
+ switch (sd->webcam) {
+ case P35u:
+ /* Note the control goes from 0-255 not 0-127, but anything
+ above 127 just means amplifying noise */
+ val >>= 1; /* 0 - 255 -> 0 - 127 */
+ reg_w(gspca_dev, 0x1026, &val, 1);
+ break;
+ case Kr651us:
+ /* 0 - 253 */
+ val = swap_bits(val);
+ v[0] = val << 3;
+ v[1] = val >> 5;
+ reg_w(gspca_dev, 0x101d, v, 2); /* SIF reg0/1 (AGC) */
+ break;
+ }
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s16 val;
+ u8 v[2];
+
+ val = sd->ctrls[EXPOSURE].val;
+ switch (sd->webcam) {
+ case P35u:
+ v[0] = ((9 - val) << 3) | 0x01;
+ reg_w(gspca_dev, 0x1019, v, 1);
+ break;
+ case Cvideopro:
+ case DvcV6:
+ case Kritter:
+ case Kr651us:
+ v[0] = val;
+ v[1] = val >> 8;
+ reg_w(gspca_dev, 0x101b, v, 2);
+ break;
+ }
+}
+
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int w, h;
+
+ if (gspca_dev->ctrl_dis & (1 << AUTOGAIN))
+ return;
+ if (!sd->ctrls[AUTOGAIN].val) {
+ sd->ag_cnt = -1;
+ return;
+ }
+ sd->ag_cnt = AG_CNT_START;
+
+ reg_r(gspca_dev, 0x1004, 1);
+ if (gspca_dev->usb_buf[0] & 0x04) { /* if AE_FULL_FRM */
+ sd->ae_res = gspca_dev->width * gspca_dev->height;
+ } else { /* get the AE window size */
+ reg_r(gspca_dev, 0x1011, 8);
+ w = (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0]
+ - (gspca_dev->usb_buf[3] << 8) - gspca_dev->usb_buf[2];
+ h = (gspca_dev->usb_buf[5] << 8) + gspca_dev->usb_buf[4]
+ - (gspca_dev->usb_buf[7] << 8) - gspca_dev->usb_buf[6];
+ sd->ae_res = h * w;
+ if (sd->ae_res == 0)
+ sd->ae_res = gspca_dev->width * gspca_dev->height;
+ }
+}
+
+static int nw802_test_reg(struct gspca_dev *gspca_dev,
+ u16 index,
+ u8 value)
+{
+ /* write the value */
+ reg_w(gspca_dev, index, &value, 1);
+
+ /* read it */
+ reg_r(gspca_dev, index, 1);
+
+ return gspca_dev->usb_buf[0] == value;
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if ((unsigned) webcam >= NWEBCAMS)
+ webcam = 0;
+ sd->webcam = webcam;
+ gspca_dev->cam.ctrls = sd->ctrls;
+ gspca_dev->cam.needs_full_bandwidth = 1;
+ sd->ag_cnt = -1;
+
+ /*
+ * Autodetect sequence inspired from some log.
+ * We try to detect what registers exist or not.
+ * If 0x0500 does not exist => NW802
+ * If it does, test 0x109b. If it doesn't exist,
+ * then it's a NW801. Else, a NW800
+ * If a et31x110 (nw800 and 06a5:d800)
+ * get the sensor ID
+ */
+ if (!nw802_test_reg(gspca_dev, 0x0500, 0x55)) {
+ sd->bridge = BRIDGE_NW802;
+ if (sd->webcam == Generic800)
+ sd->webcam = Generic802;
+ } else if (!nw802_test_reg(gspca_dev, 0x109b, 0xaa)) {
+ sd->bridge = BRIDGE_NW801;
+ if (sd->webcam == Generic800)
+ sd->webcam = P35u;
+ } else if (id->idVendor == 0x06a5 && id->idProduct == 0xd800) {
+ reg_r(gspca_dev, 0x0403, 1); /* GPIO */
+ PDEBUG(D_PROBE, "et31x110 sensor type %02x",
+ gspca_dev->usb_buf[0]);
+ switch (gspca_dev->usb_buf[0] >> 1) {
+ case 0x00: /* ?? */
+ if (sd->webcam == Generic800)
+ sd->webcam = SpaceCam;
+ break;
+ case 0x01: /* Hynix? */
+ if (sd->webcam == Generic800)
+ sd->webcam = Twinkle;
+ break;
+ case 0x0a: /* Pixart */
+ if (sd->webcam == Generic800)
+ sd->webcam = SpaceCam2;
+ break;
+ }
+ }
+ if (webcam_chip[sd->webcam] != sd->bridge) {
+ pr_err("Bad webcam type %d for NW80%d\n",
+ sd->webcam, sd->bridge);
+ gspca_dev->usb_err = -ENODEV;
+ return gspca_dev->usb_err;
+ }
+ PDEBUG(D_PROBE, "Bridge nw80%d - type: %d", sd->bridge, sd->webcam);
+
+ if (sd->bridge == BRIDGE_NW800) {
+ switch (sd->webcam) {
+ case DS3303u:
+ gspca_dev->cam.cam_mode = cif_mode; /* qvga */
+ break;
+ default:
+ gspca_dev->cam.cam_mode = &cif_mode[1]; /* cif */
+ break;
+ }
+ gspca_dev->cam.nmodes = 1;
+ } else {
+ gspca_dev->cam.cam_mode = vga_mode;
+ switch (sd->webcam) {
+ case Kr651us:
+ case Proscope:
+ case P35u:
+ gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
+ break;
+ default:
+ gspca_dev->cam.nmodes = 1; /* qvga only */
+ break;
+ }
+ }
+ switch (sd->webcam) {
+ case P35u:
+/* sd->ctrls[EXPOSURE].max = 9;
+ * sd->ctrls[EXPOSURE].def = 9; */
+ /* coarse expo auto gain function gain minimum, to avoid
+ * a large settings jump the first auto adjustment */
+ sd->ctrls[GAIN].def = 255 / 5 * 2;
+ break;
+ case Cvideopro:
+ case DvcV6:
+ case Kritter:
+ gspca_dev->ctrl_dis = (1 << GAIN) | (1 << AUTOGAIN);
+ /* fall thru */
+ case Kr651us:
+ sd->ctrls[EXPOSURE].max = 315;
+ sd->ctrls[EXPOSURE].def = 150;
+ break;
+ default:
+ gspca_dev->ctrl_dis = (1 << GAIN) | (1 << EXPOSURE)
+ | (1 << AUTOGAIN);
+ break;
+ }
+
+#if AUTOGAIN_DEF
+ if (!(gspca_dev->ctrl_dis & (1 << AUTOGAIN)))
+ gspca_dev->ctrl_inac = (1 << GAIN) | (1 << EXPOSURE);
+#endif
+ return gspca_dev->usb_err;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->bridge) {
+ case BRIDGE_NW800:
+ switch (sd->webcam) {
+ case SpaceCam:
+ reg_w_buf(gspca_dev, spacecam_init);
+ break;
+ default:
+ reg_w_buf(gspca_dev, nw800_init);
+ break;
+ }
+ break;
+ default:
+ switch (sd->webcam) {
+ case Mustek300:
+ case P35u:
+ case Proscope:
+ reg_w_buf(gspca_dev, proscope_init);
+ break;
+ }
+ break;
+ }
+ return gspca_dev->usb_err;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ const u8 *cmd;
+
+ cmd = webcam_start[sd->webcam];
+ reg_w_buf(gspca_dev, cmd);
+ switch (sd->webcam) {
+ case P35u:
+ if (gspca_dev->width == 320)
+ reg_w_buf(gspca_dev, nw801_start_qvga);
+ else
+ reg_w_buf(gspca_dev, nw801_start_vga);
+ reg_w_buf(gspca_dev, nw801_start_2);
+ break;
+ case Kr651us:
+ if (gspca_dev->width == 320)
+ reg_w_buf(gspca_dev, kr651_start_qvga);
+ else
+ reg_w_buf(gspca_dev, kr651_start_vga);
+ reg_w_buf(gspca_dev, kr651_start_2);
+ break;
+ case Proscope:
+ if (gspca_dev->width == 320)
+ reg_w_buf(gspca_dev, proscope_start_qvga);
+ else
+ reg_w_buf(gspca_dev, proscope_start_vga);
+ reg_w_buf(gspca_dev, proscope_start_2);
+ break;
+ }
+
+ setgain(gspca_dev);
+ setexposure(gspca_dev);
+ setautogain(gspca_dev);
+ sd->exp_too_high_cnt = 0;
+ sd->exp_too_low_cnt = 0;
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 value;
+
+ /* 'go' off */
+ if (sd->bridge != BRIDGE_NW801) {
+ value = 0x02;
+ reg_w(gspca_dev, 0x0406, &value, 1);
+ }
+
+ /* LED off */
+ switch (sd->webcam) {
+ case Cvideopro:
+ case Kr651us:
+ case DvcV6:
+ case Kritter:
+ value = 0xff;
+ break;
+ case Dlink350c:
+ value = 0x21;
+ break;
+ case SpaceCam:
+ case SpaceCam2:
+ case Proscope:
+ case Twinkle:
+ value = 0x01;
+ break;
+ default:
+ return;
+ }
+ reg_w(gspca_dev, 0x0404, &value, 1);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ /*
+ * frame header = '00 00 hh ww ss xx ff ff'
+ * with:
+ * - 'hh': height / 4
+ * - 'ww': width / 4
+ * - 'ss': frame sequence number c0..dd
+ */
+ if (data[0] == 0x00 && data[1] == 0x00
+ && data[6] == 0xff && data[7] == 0xff) {
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data + 8, len - 8);
+ } else {
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ }
+}
+
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->ctrls[AUTOGAIN].val = val;
+ if (val)
+ gspca_dev->ctrl_inac = (1 << GAIN) | (1 << EXPOSURE);
+ else
+ gspca_dev->ctrl_inac = 0;
+ if (gspca_dev->streaming)
+ setautogain(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+#include "autogain_functions.h"
+
+static void do_autogain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int luma;
+
+ if (sd->ag_cnt < 0)
+ return;
+ if (--sd->ag_cnt >= 0)
+ return;
+ sd->ag_cnt = AG_CNT_START;
+
+ /* get the average luma */
+ reg_r(gspca_dev, sd->bridge == BRIDGE_NW801 ? 0x080d : 0x080c, 4);
+ luma = (gspca_dev->usb_buf[3] << 24) + (gspca_dev->usb_buf[2] << 16)
+ + (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0];
+ luma /= sd->ae_res;
+
+ switch (sd->webcam) {
+ case P35u:
+ coarse_grained_expo_autogain(gspca_dev, luma, 100, 5);
+ break;
+ default:
+ auto_gain_n_exposure(gspca_dev, luma, 100, 5, 230, 0);
+ break;
+ }
+}
+
+/* V4L2 controls supported by the driver */
+static const struct ctrl sd_ctrls[NCTRLS] = {
+[GAIN] = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 253,
+ .step = 1,
+ .default_value = 128
+ },
+ .set_control = setgain
+ },
+[EXPOSURE] = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 0,
+ .maximum = 9,
+ .step = 1,
+ .default_value = 9
+ },
+ .set_control = setexposure
+ },
+[AUTOGAIN] = {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto Gain",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = AUTOGAIN_DEF,
+ .flags = V4L2_CTRL_FLAG_UPDATE
+ },
+ .set = sd_setautogain
+ },
+};
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+ .dq_callback = do_autogain,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x046d, 0xd001)},
+ {USB_DEVICE(0x0502, 0xd001)},
+ {USB_DEVICE(0x052b, 0xd001)},
+ {USB_DEVICE(0x055f, 0xd001)},
+ {USB_DEVICE(0x06a5, 0x0000)},
+ {USB_DEVICE(0x06a5, 0xd001)},
+ {USB_DEVICE(0x06a5, 0xd800)},
+ {USB_DEVICE(0x06be, 0xd001)},
+ {USB_DEVICE(0x0728, 0xd001)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
+
+module_param(webcam, int, 0644);
+MODULE_PARM_DESC(webcam,
+ "Webcam type\n"
+ "0: generic\n"
+ "1: Trust 120 SpaceCam\n"
+ "2: other Trust 120 SpaceCam\n"
+ "3: Conceptronic Video Pro\n"
+ "4: D-link dru-350c\n"
+ "5: Plustek Opticam 500U\n"
+ "6: Panasonic GP-KR651US\n"
+ "7: iRez Kritter\n"
+ "8: Mustek Wcam 300 mini\n"
+ "9: Scalar USB Microscope M2 (Proscope)\n"
+ "10: Divio Chicony TwinkleCam\n"
+ "11: DVC-V6\n");
diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c
new file mode 100644
index 00000000..739e8a2a
--- /dev/null
+++ b/drivers/media/video/gspca/ov519.c
@@ -0,0 +1,5061 @@
+/**
+ * OV519 driver
+ *
+ * Copyright (C) 2008-2011 Jean-François Moine <moinejf@free.fr>
+ * Copyright (C) 2009 Hans de Goede <hdegoede@redhat.com>
+ *
+ * This module is adapted from the ov51x-jpeg package, which itself
+ * was adapted from the ov511 driver.
+ *
+ * Original copyright for the ov511 driver is:
+ *
+ * Copyright (c) 1999-2006 Mark W. McClelland
+ * Support for OV519, OV8610 Copyright (c) 2003 Joerg Heckenbach
+ * Many improvements by Bret Wallach <bwallac1@san.rr.com>
+ * Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000)
+ * OV7620 fixes by Charl P. Botha <cpbotha@ieee.org>
+ * Changes by Claudio Matsuoka <claudio@conectiva.com>
+ *
+ * ov51x-jpeg original copyright is:
+ *
+ * Copyright (c) 2004-2007 Romain Beauxis <toots@rastageeks.org>
+ * Support for OV7670 sensors was contributed by Sam Skipsey <aoanla@yahoo.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "ov519"
+
+#include <linux/input.h>
+#include "gspca.h"
+
+/* The jpeg_hdr is used by w996Xcf only */
+/* The CONEX_CAM define for jpeg.h needs renaming, now its used here too */
+#define CONEX_CAM
+#include "jpeg.h"
+
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
+MODULE_DESCRIPTION("OV519 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* global parameters */
+static int frame_rate;
+
+/* Number of times to retry a failed I2C transaction. Increase this if you
+ * are getting "Failed to read sensor ID..." */
+static int i2c_detect_tries = 10;
+
+/* controls */
+enum e_ctrl {
+ BRIGHTNESS,
+ CONTRAST,
+ EXPOSURE,
+ COLORS,
+ HFLIP,
+ VFLIP,
+ AUTOBRIGHT,
+ AUTOGAIN,
+ FREQ,
+ NCTRL /* number of controls */
+};
+
+/* ov519 device descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ struct gspca_ctrl ctrls[NCTRL];
+
+ u8 packet_nr;
+
+ char bridge;
+#define BRIDGE_OV511 0
+#define BRIDGE_OV511PLUS 1
+#define BRIDGE_OV518 2
+#define BRIDGE_OV518PLUS 3
+#define BRIDGE_OV519 4 /* = ov530 */
+#define BRIDGE_OVFX2 5
+#define BRIDGE_W9968CF 6
+#define BRIDGE_MASK 7
+
+ char invert_led;
+#define BRIDGE_INVERT_LED 8
+
+ char snapshot_pressed;
+ char snapshot_needs_reset;
+
+ /* Determined by sensor type */
+ u8 sif;
+
+ u8 quality;
+#define QUALITY_MIN 50
+#define QUALITY_MAX 70
+#define QUALITY_DEF 50
+
+ u8 stopped; /* Streaming is temporarily paused */
+ u8 first_frame;
+
+ u8 frame_rate; /* current Framerate */
+ u8 clockdiv; /* clockdiv override */
+
+ s8 sensor; /* Type of image sensor chip (SEN_*) */
+
+ u8 sensor_addr;
+ u16 sensor_width;
+ u16 sensor_height;
+ s16 sensor_reg_cache[256];
+
+ u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+enum sensors {
+ SEN_OV2610,
+ SEN_OV2610AE,
+ SEN_OV3610,
+ SEN_OV6620,
+ SEN_OV6630,
+ SEN_OV66308AF,
+ SEN_OV7610,
+ SEN_OV7620,
+ SEN_OV7620AE,
+ SEN_OV7640,
+ SEN_OV7648,
+ SEN_OV7660,
+ SEN_OV7670,
+ SEN_OV76BE,
+ SEN_OV8610,
+ SEN_OV9600,
+};
+
+/* Note this is a bit of a hack, but the w9968cf driver needs the code for all
+ the ov sensors which is already present here. When we have the time we
+ really should move the sensor drivers to v4l2 sub drivers. */
+#include "w996Xcf.c"
+
+/* V4L2 controls supported by the driver */
+static void setbrightness(struct gspca_dev *gspca_dev);
+static void setcontrast(struct gspca_dev *gspca_dev);
+static void setexposure(struct gspca_dev *gspca_dev);
+static void setcolors(struct gspca_dev *gspca_dev);
+static void sethvflip(struct gspca_dev *gspca_dev);
+static void setautobright(struct gspca_dev *gspca_dev);
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static void setfreq(struct gspca_dev *gspca_dev);
+static void setfreq_i(struct sd *sd);
+
+static const struct ctrl sd_ctrls[] = {
+[BRIGHTNESS] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 127,
+ },
+ .set_control = setbrightness,
+ },
+[CONTRAST] = {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 127,
+ },
+ .set_control = setcontrast,
+ },
+[EXPOSURE] = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 127,
+ },
+ .set_control = setexposure,
+ },
+[COLORS] = {
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Color",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 127,
+ },
+ .set_control = setcolors,
+ },
+/* The flip controls work for sensors ov7660 and ov7670 only */
+[HFLIP] = {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mirror",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set_control = sethvflip,
+ },
+[VFLIP] = {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Vflip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set_control = sethvflip,
+ },
+[AUTOBRIGHT] = {
+ {
+ .id = V4L2_CID_AUTOBRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto Brightness",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ },
+ .set_control = setautobright,
+ },
+[AUTOGAIN] = {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto Gain",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ .flags = V4L2_CTRL_FLAG_UPDATE
+ },
+ .set = sd_setautogain,
+ },
+[FREQ] = {
+ {
+ .id = V4L2_CID_POWER_LINE_FREQUENCY,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Light frequency filter",
+ .minimum = 0,
+ .maximum = 2, /* 0: no flicker, 1: 50Hz, 2:60Hz, 3: auto */
+ .step = 1,
+ .default_value = 0,
+ },
+ .set_control = setfreq,
+ },
+};
+
+/* table of the disabled controls */
+static const unsigned ctrl_dis[] = {
+[SEN_OV2610] = ((1 << NCTRL) - 1) /* no control */
+ ^ ((1 << EXPOSURE) /* but exposure */
+ | (1 << AUTOGAIN)), /* and autogain */
+
+[SEN_OV2610AE] = ((1 << NCTRL) - 1) /* no control */
+ ^ ((1 << EXPOSURE) /* but exposure */
+ | (1 << AUTOGAIN)), /* and autogain */
+
+[SEN_OV3610] = (1 << NCTRL) - 1, /* no control */
+
+[SEN_OV6620] = (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << EXPOSURE) |
+ (1 << AUTOGAIN),
+
+[SEN_OV6630] = (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << EXPOSURE) |
+ (1 << AUTOGAIN),
+
+[SEN_OV66308AF] = (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << EXPOSURE) |
+ (1 << AUTOGAIN),
+
+[SEN_OV7610] = (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << EXPOSURE) |
+ (1 << AUTOGAIN),
+
+[SEN_OV7620] = (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << EXPOSURE) |
+ (1 << AUTOGAIN),
+
+[SEN_OV7620AE] = (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << EXPOSURE) |
+ (1 << AUTOGAIN),
+
+[SEN_OV7640] = (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << AUTOBRIGHT) |
+ (1 << CONTRAST) |
+ (1 << EXPOSURE) |
+ (1 << AUTOGAIN),
+
+[SEN_OV7648] = (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << AUTOBRIGHT) |
+ (1 << CONTRAST) |
+ (1 << EXPOSURE) |
+ (1 << AUTOGAIN),
+
+[SEN_OV7660] = (1 << AUTOBRIGHT) |
+ (1 << EXPOSURE) |
+ (1 << AUTOGAIN),
+
+[SEN_OV7670] = (1 << COLORS) |
+ (1 << AUTOBRIGHT) |
+ (1 << EXPOSURE) |
+ (1 << AUTOGAIN),
+
+[SEN_OV76BE] = (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << EXPOSURE) |
+ (1 << AUTOGAIN),
+
+[SEN_OV8610] = (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << EXPOSURE) |
+ (1 << AUTOGAIN) |
+ (1 << FREQ),
+[SEN_OV9600] = ((1 << NCTRL) - 1) /* no control */
+ ^ ((1 << EXPOSURE) /* but exposure */
+ | (1 << AUTOGAIN)), /* and autogain */
+
+};
+
+static const struct v4l2_pix_format ov519_vga_mode[] = {
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+static const struct v4l2_pix_format ov519_sif_mode[] = {
+ {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 3},
+ {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 2},
+ {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
+/* Note some of the sizeimage values for the ov511 / ov518 may seem
+ larger then necessary, however they need to be this big as the ov511 /
+ ov518 always fills the entire isoc frame, using 0 padding bytes when
+ it doesn't have any data. So with low framerates the amount of data
+ transferred can become quite large (libv4l will remove all the 0 padding
+ in userspace). */
+static const struct v4l2_pix_format ov518_vga_mode[] = {
+ {320, 240, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 2,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+static const struct v4l2_pix_format ov518_sif_mode[] = {
+ {160, 120, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 70000,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 3},
+ {176, 144, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 70000,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {320, 240, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 2},
+ {352, 288, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288 * 3,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
+static const struct v4l2_pix_format ov511_vga_mode[] = {
+ {320, 240, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 2,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+static const struct v4l2_pix_format ov511_sif_mode[] = {
+ {160, 120, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 70000,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 3},
+ {176, 144, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 70000,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {320, 240, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 2},
+ {352, 288, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288 * 3,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
+static const struct v4l2_pix_format ovfx2_vga_mode[] = {
+ {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+static const struct v4l2_pix_format ovfx2_cif_mode[] = {
+ {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 3},
+ {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 2},
+ {352, 288, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+static const struct v4l2_pix_format ovfx2_ov2610_mode[] = {
+ {800, 600, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 800,
+ .sizeimage = 800 * 600,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {1600, 1200, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 1600,
+ .sizeimage = 1600 * 1200,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+};
+static const struct v4l2_pix_format ovfx2_ov3610_mode[] = {
+ {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {800, 600, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 800,
+ .sizeimage = 800 * 600,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {1024, 768, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 1024,
+ .sizeimage = 1024 * 768,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {1600, 1200, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 1600,
+ .sizeimage = 1600 * 1200,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+ {2048, 1536, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 2048,
+ .sizeimage = 2048 * 1536,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+static const struct v4l2_pix_format ovfx2_ov9600_mode[] = {
+ {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {1280, 1024, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 1280,
+ .sizeimage = 1280 * 1024,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+};
+
+/* Registers common to OV511 / OV518 */
+#define R51x_FIFO_PSIZE 0x30 /* 2 bytes wide w/ OV518(+) */
+#define R51x_SYS_RESET 0x50
+ /* Reset type flags */
+ #define OV511_RESET_OMNICE 0x08
+#define R51x_SYS_INIT 0x53
+#define R51x_SYS_SNAP 0x52
+#define R51x_SYS_CUST_ID 0x5f
+#define R51x_COMP_LUT_BEGIN 0x80
+
+/* OV511 Camera interface register numbers */
+#define R511_CAM_DELAY 0x10
+#define R511_CAM_EDGE 0x11
+#define R511_CAM_PXCNT 0x12
+#define R511_CAM_LNCNT 0x13
+#define R511_CAM_PXDIV 0x14
+#define R511_CAM_LNDIV 0x15
+#define R511_CAM_UV_EN 0x16
+#define R511_CAM_LINE_MODE 0x17
+#define R511_CAM_OPTS 0x18
+
+#define R511_SNAP_FRAME 0x19
+#define R511_SNAP_PXCNT 0x1a
+#define R511_SNAP_LNCNT 0x1b
+#define R511_SNAP_PXDIV 0x1c
+#define R511_SNAP_LNDIV 0x1d
+#define R511_SNAP_UV_EN 0x1e
+#define R511_SNAP_OPTS 0x1f
+
+#define R511_DRAM_FLOW_CTL 0x20
+#define R511_FIFO_OPTS 0x31
+#define R511_I2C_CTL 0x40
+#define R511_SYS_LED_CTL 0x55 /* OV511+ only */
+#define R511_COMP_EN 0x78
+#define R511_COMP_LUT_EN 0x79
+
+/* OV518 Camera interface register numbers */
+#define R518_GPIO_OUT 0x56 /* OV518(+) only */
+#define R518_GPIO_CTL 0x57 /* OV518(+) only */
+
+/* OV519 Camera interface register numbers */
+#define OV519_R10_H_SIZE 0x10
+#define OV519_R11_V_SIZE 0x11
+#define OV519_R12_X_OFFSETL 0x12
+#define OV519_R13_X_OFFSETH 0x13
+#define OV519_R14_Y_OFFSETL 0x14
+#define OV519_R15_Y_OFFSETH 0x15
+#define OV519_R16_DIVIDER 0x16
+#define OV519_R20_DFR 0x20
+#define OV519_R25_FORMAT 0x25
+
+/* OV519 System Controller register numbers */
+#define OV519_R51_RESET1 0x51
+#define OV519_R54_EN_CLK1 0x54
+#define OV519_R57_SNAPSHOT 0x57
+
+#define OV519_GPIO_DATA_OUT0 0x71
+#define OV519_GPIO_IO_CTRL0 0x72
+
+/*#define OV511_ENDPOINT_ADDRESS 1 * Isoc endpoint number */
+
+/*
+ * The FX2 chip does not give us a zero length read at end of frame.
+ * It does, however, give a short read at the end of a frame, if
+ * necessary, rather than run two frames together.
+ *
+ * By choosing the right bulk transfer size, we are guaranteed to always
+ * get a short read for the last read of each frame. Frame sizes are
+ * always a composite number (width * height, or a multiple) so if we
+ * choose a prime number, we are guaranteed that the last read of a
+ * frame will be short.
+ *
+ * But it isn't that easy: the 2.6 kernel requires a multiple of 4KB,
+ * otherwise EOVERFLOW "babbling" errors occur. I have not been able
+ * to figure out why. [PMiller]
+ *
+ * The constant (13 * 4096) is the largest "prime enough" number less than 64KB.
+ *
+ * It isn't enough to know the number of bytes per frame, in case we
+ * have data dropouts or buffer overruns (even though the FX2 double
+ * buffers, there are some pretty strict real time constraints for
+ * isochronous transfer for larger frame sizes).
+ */
+/*jfm: this value does not work for 800x600 - see isoc_init */
+#define OVFX2_BULK_SIZE (13 * 4096)
+
+/* I2C registers */
+#define R51x_I2C_W_SID 0x41
+#define R51x_I2C_SADDR_3 0x42
+#define R51x_I2C_SADDR_2 0x43
+#define R51x_I2C_R_SID 0x44
+#define R51x_I2C_DATA 0x45
+#define R518_I2C_CTL 0x47 /* OV518(+) only */
+#define OVFX2_I2C_ADDR 0x00
+
+/* I2C ADDRESSES */
+#define OV7xx0_SID 0x42
+#define OV_HIRES_SID 0x60 /* OV9xxx / OV2xxx / OV3xxx */
+#define OV8xx0_SID 0xa0
+#define OV6xx0_SID 0xc0
+
+/* OV7610 registers */
+#define OV7610_REG_GAIN 0x00 /* gain setting (5:0) */
+#define OV7610_REG_BLUE 0x01 /* blue channel balance */
+#define OV7610_REG_RED 0x02 /* red channel balance */
+#define OV7610_REG_SAT 0x03 /* saturation */
+#define OV8610_REG_HUE 0x04 /* 04 reserved */
+#define OV7610_REG_CNT 0x05 /* Y contrast */
+#define OV7610_REG_BRT 0x06 /* Y brightness */
+#define OV7610_REG_COM_C 0x14 /* misc common regs */
+#define OV7610_REG_ID_HIGH 0x1c /* manufacturer ID MSB */
+#define OV7610_REG_ID_LOW 0x1d /* manufacturer ID LSB */
+#define OV7610_REG_COM_I 0x29 /* misc settings */
+
+/* OV7660 and OV7670 registers */
+#define OV7670_R00_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */
+#define OV7670_R01_BLUE 0x01 /* blue gain */
+#define OV7670_R02_RED 0x02 /* red gain */
+#define OV7670_R03_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */
+#define OV7670_R04_COM1 0x04 /* Control 1 */
+/*#define OV7670_R07_AECHH 0x07 * AEC MS 5 bits */
+#define OV7670_R0C_COM3 0x0c /* Control 3 */
+#define OV7670_R0D_COM4 0x0d /* Control 4 */
+#define OV7670_R0E_COM5 0x0e /* All "reserved" */
+#define OV7670_R0F_COM6 0x0f /* Control 6 */
+#define OV7670_R10_AECH 0x10 /* More bits of AEC value */
+#define OV7670_R11_CLKRC 0x11 /* Clock control */
+#define OV7670_R12_COM7 0x12 /* Control 7 */
+#define OV7670_COM7_FMT_VGA 0x00
+/*#define OV7670_COM7_YUV 0x00 * YUV */
+#define OV7670_COM7_FMT_QVGA 0x10 /* QVGA format */
+#define OV7670_COM7_FMT_MASK 0x38
+#define OV7670_COM7_RESET 0x80 /* Register reset */
+#define OV7670_R13_COM8 0x13 /* Control 8 */
+#define OV7670_COM8_AEC 0x01 /* Auto exposure enable */
+#define OV7670_COM8_AWB 0x02 /* White balance enable */
+#define OV7670_COM8_AGC 0x04 /* Auto gain enable */
+#define OV7670_COM8_BFILT 0x20 /* Band filter enable */
+#define OV7670_COM8_AECSTEP 0x40 /* Unlimited AEC step size */
+#define OV7670_COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */
+#define OV7670_R14_COM9 0x14 /* Control 9 - gain ceiling */
+#define OV7670_R15_COM10 0x15 /* Control 10 */
+#define OV7670_R17_HSTART 0x17 /* Horiz start high bits */
+#define OV7670_R18_HSTOP 0x18 /* Horiz stop high bits */
+#define OV7670_R19_VSTART 0x19 /* Vert start high bits */
+#define OV7670_R1A_VSTOP 0x1a /* Vert stop high bits */
+#define OV7670_R1E_MVFP 0x1e /* Mirror / vflip */
+#define OV7670_MVFP_VFLIP 0x10 /* vertical flip */
+#define OV7670_MVFP_MIRROR 0x20 /* Mirror image */
+#define OV7670_R24_AEW 0x24 /* AGC upper limit */
+#define OV7670_R25_AEB 0x25 /* AGC lower limit */
+#define OV7670_R26_VPT 0x26 /* AGC/AEC fast mode op region */
+#define OV7670_R32_HREF 0x32 /* HREF pieces */
+#define OV7670_R3A_TSLB 0x3a /* lots of stuff */
+#define OV7670_R3B_COM11 0x3b /* Control 11 */
+#define OV7670_COM11_EXP 0x02
+#define OV7670_COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */
+#define OV7670_R3C_COM12 0x3c /* Control 12 */
+#define OV7670_R3D_COM13 0x3d /* Control 13 */
+#define OV7670_COM13_GAMMA 0x80 /* Gamma enable */
+#define OV7670_COM13_UVSAT 0x40 /* UV saturation auto adjustment */
+#define OV7670_R3E_COM14 0x3e /* Control 14 */
+#define OV7670_R3F_EDGE 0x3f /* Edge enhancement factor */
+#define OV7670_R40_COM15 0x40 /* Control 15 */
+/*#define OV7670_COM15_R00FF 0xc0 * 00 to FF */
+#define OV7670_R41_COM16 0x41 /* Control 16 */
+#define OV7670_COM16_AWBGAIN 0x08 /* AWB gain enable */
+/* end of ov7660 common registers */
+#define OV7670_R55_BRIGHT 0x55 /* Brightness */
+#define OV7670_R56_CONTRAS 0x56 /* Contrast control */
+#define OV7670_R69_GFIX 0x69 /* Fix gain control */
+/*#define OV7670_R8C_RGB444 0x8c * RGB 444 control */
+#define OV7670_R9F_HAECC1 0x9f /* Hist AEC/AGC control 1 */
+#define OV7670_RA0_HAECC2 0xa0 /* Hist AEC/AGC control 2 */
+#define OV7670_RA5_BD50MAX 0xa5 /* 50hz banding step limit */
+#define OV7670_RA6_HAECC3 0xa6 /* Hist AEC/AGC control 3 */
+#define OV7670_RA7_HAECC4 0xa7 /* Hist AEC/AGC control 4 */
+#define OV7670_RA8_HAECC5 0xa8 /* Hist AEC/AGC control 5 */
+#define OV7670_RA9_HAECC6 0xa9 /* Hist AEC/AGC control 6 */
+#define OV7670_RAA_HAECC7 0xaa /* Hist AEC/AGC control 7 */
+#define OV7670_RAB_BD60MAX 0xab /* 60hz banding step limit */
+
+struct ov_regvals {
+ u8 reg;
+ u8 val;
+};
+struct ov_i2c_regvals {
+ u8 reg;
+ u8 val;
+};
+
+/* Settings for OV2610 camera chip */
+static const struct ov_i2c_regvals norm_2610[] = {
+ { 0x12, 0x80 }, /* reset */
+};
+
+static const struct ov_i2c_regvals norm_2610ae[] = {
+ {0x12, 0x80}, /* reset */
+ {0x13, 0xcd},
+ {0x09, 0x01},
+ {0x0d, 0x00},
+ {0x11, 0x80},
+ {0x12, 0x20}, /* 1600x1200 */
+ {0x33, 0x0c},
+ {0x35, 0x90},
+ {0x36, 0x37},
+/* ms-win traces */
+ {0x11, 0x83}, /* clock / 3 ? */
+ {0x2d, 0x00}, /* 60 Hz filter */
+ {0x24, 0xb0}, /* normal colors */
+ {0x25, 0x90},
+ {0x10, 0x43},
+};
+
+static const struct ov_i2c_regvals norm_3620b[] = {
+ /*
+ * From the datasheet: "Note that after writing to register COMH
+ * (0x12) to change the sensor mode, registers related to the
+ * sensor’s cropping window will be reset back to their default
+ * values."
+ *
+ * "wait 4096 external clock ... to make sure the sensor is
+ * stable and ready to access registers" i.e. 160us at 24MHz
+ */
+ { 0x12, 0x80 }, /* COMH reset */
+ { 0x12, 0x00 }, /* QXGA, master */
+
+ /*
+ * 11 CLKRC "Clock Rate Control"
+ * [7] internal frequency doublers: on
+ * [6] video port mode: master
+ * [5:0] clock divider: 1
+ */
+ { 0x11, 0x80 },
+
+ /*
+ * 13 COMI "Common Control I"
+ * = 192 (0xC0) 11000000
+ * COMI[7] "AEC speed selection"
+ * = 1 (0x01) 1....... "Faster AEC correction"
+ * COMI[6] "AEC speed step selection"
+ * = 1 (0x01) .1...... "Big steps, fast"
+ * COMI[5] "Banding filter on off"
+ * = 0 (0x00) ..0..... "Off"
+ * COMI[4] "Banding filter option"
+ * = 0 (0x00) ...0.... "Main clock is 48 MHz and
+ * the PLL is ON"
+ * COMI[3] "Reserved"
+ * = 0 (0x00) ....0...
+ * COMI[2] "AGC auto manual control selection"
+ * = 0 (0x00) .....0.. "Manual"
+ * COMI[1] "AWB auto manual control selection"
+ * = 0 (0x00) ......0. "Manual"
+ * COMI[0] "Exposure control"
+ * = 0 (0x00) .......0 "Manual"
+ */
+ { 0x13, 0xc0 },
+
+ /*
+ * 09 COMC "Common Control C"
+ * = 8 (0x08) 00001000
+ * COMC[7:5] "Reserved"
+ * = 0 (0x00) 000.....
+ * COMC[4] "Sleep Mode Enable"
+ * = 0 (0x00) ...0.... "Normal mode"
+ * COMC[3:2] "Sensor sampling reset timing selection"
+ * = 2 (0x02) ....10.. "Longer reset time"
+ * COMC[1:0] "Output drive current select"
+ * = 0 (0x00) ......00 "Weakest"
+ */
+ { 0x09, 0x08 },
+
+ /*
+ * 0C COMD "Common Control D"
+ * = 8 (0x08) 00001000
+ * COMD[7] "Reserved"
+ * = 0 (0x00) 0.......
+ * COMD[6] "Swap MSB and LSB at the output port"
+ * = 0 (0x00) .0...... "False"
+ * COMD[5:3] "Reserved"
+ * = 1 (0x01) ..001...
+ * COMD[2] "Output Average On Off"
+ * = 0 (0x00) .....0.. "Output Normal"
+ * COMD[1] "Sensor precharge voltage selection"
+ * = 0 (0x00) ......0. "Selects internal
+ * reference precharge
+ * voltage"
+ * COMD[0] "Snapshot option"
+ * = 0 (0x00) .......0 "Enable live video output
+ * after snapshot sequence"
+ */
+ { 0x0c, 0x08 },
+
+ /*
+ * 0D COME "Common Control E"
+ * = 161 (0xA1) 10100001
+ * COME[7] "Output average option"
+ * = 1 (0x01) 1....... "Output average of 4 pixels"
+ * COME[6] "Anti-blooming control"
+ * = 0 (0x00) .0...... "Off"
+ * COME[5:3] "Reserved"
+ * = 4 (0x04) ..100...
+ * COME[2] "Clock output power down pin status"
+ * = 0 (0x00) .....0.. "Tri-state data output pin
+ * on power down"
+ * COME[1] "Data output pin status selection at power down"
+ * = 0 (0x00) ......0. "Tri-state VSYNC, PCLK,
+ * HREF, and CHSYNC pins on
+ * power down"
+ * COME[0] "Auto zero circuit select"
+ * = 1 (0x01) .......1 "On"
+ */
+ { 0x0d, 0xa1 },
+
+ /*
+ * 0E COMF "Common Control F"
+ * = 112 (0x70) 01110000
+ * COMF[7] "System clock selection"
+ * = 0 (0x00) 0....... "Use 24 MHz system clock"
+ * COMF[6:4] "Reserved"
+ * = 7 (0x07) .111....
+ * COMF[3] "Manual auto negative offset canceling selection"
+ * = 0 (0x00) ....0... "Auto detect negative
+ * offset and cancel it"
+ * COMF[2:0] "Reserved"
+ * = 0 (0x00) .....000
+ */
+ { 0x0e, 0x70 },
+
+ /*
+ * 0F COMG "Common Control G"
+ * = 66 (0x42) 01000010
+ * COMG[7] "Optical black output selection"
+ * = 0 (0x00) 0....... "Disable"
+ * COMG[6] "Black level calibrate selection"
+ * = 1 (0x01) .1...... "Use optical black pixels
+ * to calibrate"
+ * COMG[5:4] "Reserved"
+ * = 0 (0x00) ..00....
+ * COMG[3] "Channel offset adjustment"
+ * = 0 (0x00) ....0... "Disable offset adjustment"
+ * COMG[2] "ADC black level calibration option"
+ * = 0 (0x00) .....0.. "Use B/G line and G/R
+ * line to calibrate each
+ * channel's black level"
+ * COMG[1] "Reserved"
+ * = 1 (0x01) ......1.
+ * COMG[0] "ADC black level calibration enable"
+ * = 0 (0x00) .......0 "Disable"
+ */
+ { 0x0f, 0x42 },
+
+ /*
+ * 14 COMJ "Common Control J"
+ * = 198 (0xC6) 11000110
+ * COMJ[7:6] "AGC gain ceiling"
+ * = 3 (0x03) 11...... "8x"
+ * COMJ[5:4] "Reserved"
+ * = 0 (0x00) ..00....
+ * COMJ[3] "Auto banding filter"
+ * = 0 (0x00) ....0... "Banding filter is always
+ * on off depending on
+ * COMI[5] setting"
+ * COMJ[2] "VSYNC drop option"
+ * = 1 (0x01) .....1.. "SYNC is dropped if frame
+ * data is dropped"
+ * COMJ[1] "Frame data drop"
+ * = 1 (0x01) ......1. "Drop frame data if
+ * exposure is not within
+ * tolerance. In AEC mode,
+ * data is normally dropped
+ * when data is out of
+ * range."
+ * COMJ[0] "Reserved"
+ * = 0 (0x00) .......0
+ */
+ { 0x14, 0xc6 },
+
+ /*
+ * 15 COMK "Common Control K"
+ * = 2 (0x02) 00000010
+ * COMK[7] "CHSYNC pin output swap"
+ * = 0 (0x00) 0....... "CHSYNC"
+ * COMK[6] "HREF pin output swap"
+ * = 0 (0x00) .0...... "HREF"
+ * COMK[5] "PCLK output selection"
+ * = 0 (0x00) ..0..... "PCLK always output"
+ * COMK[4] "PCLK edge selection"
+ * = 0 (0x00) ...0.... "Data valid on falling edge"
+ * COMK[3] "HREF output polarity"
+ * = 0 (0x00) ....0... "positive"
+ * COMK[2] "Reserved"
+ * = 0 (0x00) .....0..
+ * COMK[1] "VSYNC polarity"
+ * = 1 (0x01) ......1. "negative"
+ * COMK[0] "HSYNC polarity"
+ * = 0 (0x00) .......0 "positive"
+ */
+ { 0x15, 0x02 },
+
+ /*
+ * 33 CHLF "Current Control"
+ * = 9 (0x09) 00001001
+ * CHLF[7:6] "Sensor current control"
+ * = 0 (0x00) 00......
+ * CHLF[5] "Sensor current range control"
+ * = 0 (0x00) ..0..... "normal range"
+ * CHLF[4] "Sensor current"
+ * = 0 (0x00) ...0.... "normal current"
+ * CHLF[3] "Sensor buffer current control"
+ * = 1 (0x01) ....1... "half current"
+ * CHLF[2] "Column buffer current control"
+ * = 0 (0x00) .....0.. "normal current"
+ * CHLF[1] "Analog DSP current control"
+ * = 0 (0x00) ......0. "normal current"
+ * CHLF[1] "ADC current control"
+ * = 0 (0x00) ......0. "normal current"
+ */
+ { 0x33, 0x09 },
+
+ /*
+ * 34 VBLM "Blooming Control"
+ * = 80 (0x50) 01010000
+ * VBLM[7] "Hard soft reset switch"
+ * = 0 (0x00) 0....... "Hard reset"
+ * VBLM[6:4] "Blooming voltage selection"
+ * = 5 (0x05) .101....
+ * VBLM[3:0] "Sensor current control"
+ * = 0 (0x00) ....0000
+ */
+ { 0x34, 0x50 },
+
+ /*
+ * 36 VCHG "Sensor Precharge Voltage Control"
+ * = 0 (0x00) 00000000
+ * VCHG[7] "Reserved"
+ * = 0 (0x00) 0.......
+ * VCHG[6:4] "Sensor precharge voltage control"
+ * = 0 (0x00) .000....
+ * VCHG[3:0] "Sensor array common reference"
+ * = 0 (0x00) ....0000
+ */
+ { 0x36, 0x00 },
+
+ /*
+ * 37 ADC "ADC Reference Control"
+ * = 4 (0x04) 00000100
+ * ADC[7:4] "Reserved"
+ * = 0 (0x00) 0000....
+ * ADC[3] "ADC input signal range"
+ * = 0 (0x00) ....0... "Input signal 1.0x"
+ * ADC[2:0] "ADC range control"
+ * = 4 (0x04) .....100
+ */
+ { 0x37, 0x04 },
+
+ /*
+ * 38 ACOM "Analog Common Ground"
+ * = 82 (0x52) 01010010
+ * ACOM[7] "Analog gain control"
+ * = 0 (0x00) 0....... "Gain 1x"
+ * ACOM[6] "Analog black level calibration"
+ * = 1 (0x01) .1...... "On"
+ * ACOM[5:0] "Reserved"
+ * = 18 (0x12) ..010010
+ */
+ { 0x38, 0x52 },
+
+ /*
+ * 3A FREFA "Internal Reference Adjustment"
+ * = 0 (0x00) 00000000
+ * FREFA[7:0] "Range"
+ * = 0 (0x00) 00000000
+ */
+ { 0x3a, 0x00 },
+
+ /*
+ * 3C FVOPT "Internal Reference Adjustment"
+ * = 31 (0x1F) 00011111
+ * FVOPT[7:0] "Range"
+ * = 31 (0x1F) 00011111
+ */
+ { 0x3c, 0x1f },
+
+ /*
+ * 44 Undocumented = 0 (0x00) 00000000
+ * 44[7:0] "It's a secret"
+ * = 0 (0x00) 00000000
+ */
+ { 0x44, 0x00 },
+
+ /*
+ * 40 Undocumented = 0 (0x00) 00000000
+ * 40[7:0] "It's a secret"
+ * = 0 (0x00) 00000000
+ */
+ { 0x40, 0x00 },
+
+ /*
+ * 41 Undocumented = 0 (0x00) 00000000
+ * 41[7:0] "It's a secret"
+ * = 0 (0x00) 00000000
+ */
+ { 0x41, 0x00 },
+
+ /*
+ * 42 Undocumented = 0 (0x00) 00000000
+ * 42[7:0] "It's a secret"
+ * = 0 (0x00) 00000000
+ */
+ { 0x42, 0x00 },
+
+ /*
+ * 43 Undocumented = 0 (0x00) 00000000
+ * 43[7:0] "It's a secret"
+ * = 0 (0x00) 00000000
+ */
+ { 0x43, 0x00 },
+
+ /*
+ * 45 Undocumented = 128 (0x80) 10000000
+ * 45[7:0] "It's a secret"
+ * = 128 (0x80) 10000000
+ */
+ { 0x45, 0x80 },
+
+ /*
+ * 48 Undocumented = 192 (0xC0) 11000000
+ * 48[7:0] "It's a secret"
+ * = 192 (0xC0) 11000000
+ */
+ { 0x48, 0xc0 },
+
+ /*
+ * 49 Undocumented = 25 (0x19) 00011001
+ * 49[7:0] "It's a secret"
+ * = 25 (0x19) 00011001
+ */
+ { 0x49, 0x19 },
+
+ /*
+ * 4B Undocumented = 128 (0x80) 10000000
+ * 4B[7:0] "It's a secret"
+ * = 128 (0x80) 10000000
+ */
+ { 0x4b, 0x80 },
+
+ /*
+ * 4D Undocumented = 196 (0xC4) 11000100
+ * 4D[7:0] "It's a secret"
+ * = 196 (0xC4) 11000100
+ */
+ { 0x4d, 0xc4 },
+
+ /*
+ * 35 VREF "Reference Voltage Control"
+ * = 76 (0x4c) 01001100
+ * VREF[7:5] "Column high reference control"
+ * = 2 (0x02) 010..... "higher voltage"
+ * VREF[4:2] "Column low reference control"
+ * = 3 (0x03) ...011.. "Highest voltage"
+ * VREF[1:0] "Reserved"
+ * = 0 (0x00) ......00
+ */
+ { 0x35, 0x4c },
+
+ /*
+ * 3D Undocumented = 0 (0x00) 00000000
+ * 3D[7:0] "It's a secret"
+ * = 0 (0x00) 00000000
+ */
+ { 0x3d, 0x00 },
+
+ /*
+ * 3E Undocumented = 0 (0x00) 00000000
+ * 3E[7:0] "It's a secret"
+ * = 0 (0x00) 00000000
+ */
+ { 0x3e, 0x00 },
+
+ /*
+ * 3B FREFB "Internal Reference Adjustment"
+ * = 24 (0x18) 00011000
+ * FREFB[7:0] "Range"
+ * = 24 (0x18) 00011000
+ */
+ { 0x3b, 0x18 },
+
+ /*
+ * 33 CHLF "Current Control"
+ * = 25 (0x19) 00011001
+ * CHLF[7:6] "Sensor current control"
+ * = 0 (0x00) 00......
+ * CHLF[5] "Sensor current range control"
+ * = 0 (0x00) ..0..... "normal range"
+ * CHLF[4] "Sensor current"
+ * = 1 (0x01) ...1.... "double current"
+ * CHLF[3] "Sensor buffer current control"
+ * = 1 (0x01) ....1... "half current"
+ * CHLF[2] "Column buffer current control"
+ * = 0 (0x00) .....0.. "normal current"
+ * CHLF[1] "Analog DSP current control"
+ * = 0 (0x00) ......0. "normal current"
+ * CHLF[1] "ADC current control"
+ * = 0 (0x00) ......0. "normal current"
+ */
+ { 0x33, 0x19 },
+
+ /*
+ * 34 VBLM "Blooming Control"
+ * = 90 (0x5A) 01011010
+ * VBLM[7] "Hard soft reset switch"
+ * = 0 (0x00) 0....... "Hard reset"
+ * VBLM[6:4] "Blooming voltage selection"
+ * = 5 (0x05) .101....
+ * VBLM[3:0] "Sensor current control"
+ * = 10 (0x0A) ....1010
+ */
+ { 0x34, 0x5a },
+
+ /*
+ * 3B FREFB "Internal Reference Adjustment"
+ * = 0 (0x00) 00000000
+ * FREFB[7:0] "Range"
+ * = 0 (0x00) 00000000
+ */
+ { 0x3b, 0x00 },
+
+ /*
+ * 33 CHLF "Current Control"
+ * = 9 (0x09) 00001001
+ * CHLF[7:6] "Sensor current control"
+ * = 0 (0x00) 00......
+ * CHLF[5] "Sensor current range control"
+ * = 0 (0x00) ..0..... "normal range"
+ * CHLF[4] "Sensor current"
+ * = 0 (0x00) ...0.... "normal current"
+ * CHLF[3] "Sensor buffer current control"
+ * = 1 (0x01) ....1... "half current"
+ * CHLF[2] "Column buffer current control"
+ * = 0 (0x00) .....0.. "normal current"
+ * CHLF[1] "Analog DSP current control"
+ * = 0 (0x00) ......0. "normal current"
+ * CHLF[1] "ADC current control"
+ * = 0 (0x00) ......0. "normal current"
+ */
+ { 0x33, 0x09 },
+
+ /*
+ * 34 VBLM "Blooming Control"
+ * = 80 (0x50) 01010000
+ * VBLM[7] "Hard soft reset switch"
+ * = 0 (0x00) 0....... "Hard reset"
+ * VBLM[6:4] "Blooming voltage selection"
+ * = 5 (0x05) .101....
+ * VBLM[3:0] "Sensor current control"
+ * = 0 (0x00) ....0000
+ */
+ { 0x34, 0x50 },
+
+ /*
+ * 12 COMH "Common Control H"
+ * = 64 (0x40) 01000000
+ * COMH[7] "SRST"
+ * = 0 (0x00) 0....... "No-op"
+ * COMH[6:4] "Resolution selection"
+ * = 4 (0x04) .100.... "XGA"
+ * COMH[3] "Master slave selection"
+ * = 0 (0x00) ....0... "Master mode"
+ * COMH[2] "Internal B/R channel option"
+ * = 0 (0x00) .....0.. "B/R use same channel"
+ * COMH[1] "Color bar test pattern"
+ * = 0 (0x00) ......0. "Off"
+ * COMH[0] "Reserved"
+ * = 0 (0x00) .......0
+ */
+ { 0x12, 0x40 },
+
+ /*
+ * 17 HREFST "Horizontal window start"
+ * = 31 (0x1F) 00011111
+ * HREFST[7:0] "Horizontal window start, 8 MSBs"
+ * = 31 (0x1F) 00011111
+ */
+ { 0x17, 0x1f },
+
+ /*
+ * 18 HREFEND "Horizontal window end"
+ * = 95 (0x5F) 01011111
+ * HREFEND[7:0] "Horizontal Window End, 8 MSBs"
+ * = 95 (0x5F) 01011111
+ */
+ { 0x18, 0x5f },
+
+ /*
+ * 19 VSTRT "Vertical window start"
+ * = 0 (0x00) 00000000
+ * VSTRT[7:0] "Vertical Window Start, 8 MSBs"
+ * = 0 (0x00) 00000000
+ */
+ { 0x19, 0x00 },
+
+ /*
+ * 1A VEND "Vertical window end"
+ * = 96 (0x60) 01100000
+ * VEND[7:0] "Vertical Window End, 8 MSBs"
+ * = 96 (0x60) 01100000
+ */
+ { 0x1a, 0x60 },
+
+ /*
+ * 32 COMM "Common Control M"
+ * = 18 (0x12) 00010010
+ * COMM[7:6] "Pixel clock divide option"
+ * = 0 (0x00) 00...... "/1"
+ * COMM[5:3] "Horizontal window end position, 3 LSBs"
+ * = 2 (0x02) ..010...
+ * COMM[2:0] "Horizontal window start position, 3 LSBs"
+ * = 2 (0x02) .....010
+ */
+ { 0x32, 0x12 },
+
+ /*
+ * 03 COMA "Common Control A"
+ * = 74 (0x4A) 01001010
+ * COMA[7:4] "AWB Update Threshold"
+ * = 4 (0x04) 0100....
+ * COMA[3:2] "Vertical window end line control 2 LSBs"
+ * = 2 (0x02) ....10..
+ * COMA[1:0] "Vertical window start line control 2 LSBs"
+ * = 2 (0x02) ......10
+ */
+ { 0x03, 0x4a },
+
+ /*
+ * 11 CLKRC "Clock Rate Control"
+ * = 128 (0x80) 10000000
+ * CLKRC[7] "Internal frequency doublers on off seclection"
+ * = 1 (0x01) 1....... "On"
+ * CLKRC[6] "Digital video master slave selection"
+ * = 0 (0x00) .0...... "Master mode, sensor
+ * provides PCLK"
+ * CLKRC[5:0] "Clock divider { CLK = PCLK/(1+CLKRC[5:0]) }"
+ * = 0 (0x00) ..000000
+ */
+ { 0x11, 0x80 },
+
+ /*
+ * 12 COMH "Common Control H"
+ * = 0 (0x00) 00000000
+ * COMH[7] "SRST"
+ * = 0 (0x00) 0....... "No-op"
+ * COMH[6:4] "Resolution selection"
+ * = 0 (0x00) .000.... "QXGA"
+ * COMH[3] "Master slave selection"
+ * = 0 (0x00) ....0... "Master mode"
+ * COMH[2] "Internal B/R channel option"
+ * = 0 (0x00) .....0.. "B/R use same channel"
+ * COMH[1] "Color bar test pattern"
+ * = 0 (0x00) ......0. "Off"
+ * COMH[0] "Reserved"
+ * = 0 (0x00) .......0
+ */
+ { 0x12, 0x00 },
+
+ /*
+ * 12 COMH "Common Control H"
+ * = 64 (0x40) 01000000
+ * COMH[7] "SRST"
+ * = 0 (0x00) 0....... "No-op"
+ * COMH[6:4] "Resolution selection"
+ * = 4 (0x04) .100.... "XGA"
+ * COMH[3] "Master slave selection"
+ * = 0 (0x00) ....0... "Master mode"
+ * COMH[2] "Internal B/R channel option"
+ * = 0 (0x00) .....0.. "B/R use same channel"
+ * COMH[1] "Color bar test pattern"
+ * = 0 (0x00) ......0. "Off"
+ * COMH[0] "Reserved"
+ * = 0 (0x00) .......0
+ */
+ { 0x12, 0x40 },
+
+ /*
+ * 17 HREFST "Horizontal window start"
+ * = 31 (0x1F) 00011111
+ * HREFST[7:0] "Horizontal window start, 8 MSBs"
+ * = 31 (0x1F) 00011111
+ */
+ { 0x17, 0x1f },
+
+ /*
+ * 18 HREFEND "Horizontal window end"
+ * = 95 (0x5F) 01011111
+ * HREFEND[7:0] "Horizontal Window End, 8 MSBs"
+ * = 95 (0x5F) 01011111
+ */
+ { 0x18, 0x5f },
+
+ /*
+ * 19 VSTRT "Vertical window start"
+ * = 0 (0x00) 00000000
+ * VSTRT[7:0] "Vertical Window Start, 8 MSBs"
+ * = 0 (0x00) 00000000
+ */
+ { 0x19, 0x00 },
+
+ /*
+ * 1A VEND "Vertical window end"
+ * = 96 (0x60) 01100000
+ * VEND[7:0] "Vertical Window End, 8 MSBs"
+ * = 96 (0x60) 01100000
+ */
+ { 0x1a, 0x60 },
+
+ /*
+ * 32 COMM "Common Control M"
+ * = 18 (0x12) 00010010
+ * COMM[7:6] "Pixel clock divide option"
+ * = 0 (0x00) 00...... "/1"
+ * COMM[5:3] "Horizontal window end position, 3 LSBs"
+ * = 2 (0x02) ..010...
+ * COMM[2:0] "Horizontal window start position, 3 LSBs"
+ * = 2 (0x02) .....010
+ */
+ { 0x32, 0x12 },
+
+ /*
+ * 03 COMA "Common Control A"
+ * = 74 (0x4A) 01001010
+ * COMA[7:4] "AWB Update Threshold"
+ * = 4 (0x04) 0100....
+ * COMA[3:2] "Vertical window end line control 2 LSBs"
+ * = 2 (0x02) ....10..
+ * COMA[1:0] "Vertical window start line control 2 LSBs"
+ * = 2 (0x02) ......10
+ */
+ { 0x03, 0x4a },
+
+ /*
+ * 02 RED "Red Gain Control"
+ * = 175 (0xAF) 10101111
+ * RED[7] "Action"
+ * = 1 (0x01) 1....... "gain = 1/(1+bitrev([6:0]))"
+ * RED[6:0] "Value"
+ * = 47 (0x2F) .0101111
+ */
+ { 0x02, 0xaf },
+
+ /*
+ * 2D ADDVSL "VSYNC Pulse Width"
+ * = 210 (0xD2) 11010010
+ * ADDVSL[7:0] "VSYNC pulse width, LSB"
+ * = 210 (0xD2) 11010010
+ */
+ { 0x2d, 0xd2 },
+
+ /*
+ * 00 GAIN = 24 (0x18) 00011000
+ * GAIN[7:6] "Reserved"
+ * = 0 (0x00) 00......
+ * GAIN[5] "Double"
+ * = 0 (0x00) ..0..... "False"
+ * GAIN[4] "Double"
+ * = 1 (0x01) ...1.... "True"
+ * GAIN[3:0] "Range"
+ * = 8 (0x08) ....1000
+ */
+ { 0x00, 0x18 },
+
+ /*
+ * 01 BLUE "Blue Gain Control"
+ * = 240 (0xF0) 11110000
+ * BLUE[7] "Action"
+ * = 1 (0x01) 1....... "gain = 1/(1+bitrev([6:0]))"
+ * BLUE[6:0] "Value"
+ * = 112 (0x70) .1110000
+ */
+ { 0x01, 0xf0 },
+
+ /*
+ * 10 AEC "Automatic Exposure Control"
+ * = 10 (0x0A) 00001010
+ * AEC[7:0] "Automatic Exposure Control, 8 MSBs"
+ * = 10 (0x0A) 00001010
+ */
+ { 0x10, 0x0a },
+
+ { 0xe1, 0x67 },
+ { 0xe3, 0x03 },
+ { 0xe4, 0x26 },
+ { 0xe5, 0x3e },
+ { 0xf8, 0x01 },
+ { 0xff, 0x01 },
+};
+
+static const struct ov_i2c_regvals norm_6x20[] = {
+ { 0x12, 0x80 }, /* reset */
+ { 0x11, 0x01 },
+ { 0x03, 0x60 },
+ { 0x05, 0x7f }, /* For when autoadjust is off */
+ { 0x07, 0xa8 },
+ /* The ratio of 0x0c and 0x0d controls the white point */
+ { 0x0c, 0x24 },
+ { 0x0d, 0x24 },
+ { 0x0f, 0x15 }, /* COMS */
+ { 0x10, 0x75 }, /* AEC Exposure time */
+ { 0x12, 0x24 }, /* Enable AGC */
+ { 0x14, 0x04 },
+ /* 0x16: 0x06 helps frame stability with moving objects */
+ { 0x16, 0x06 },
+/* { 0x20, 0x30 }, * Aperture correction enable */
+ { 0x26, 0xb2 }, /* BLC enable */
+ /* 0x28: 0x05 Selects RGB format if RGB on */
+ { 0x28, 0x05 },
+ { 0x2a, 0x04 }, /* Disable framerate adjust */
+/* { 0x2b, 0xac }, * Framerate; Set 2a[7] first */
+ { 0x2d, 0x85 },
+ { 0x33, 0xa0 }, /* Color Processing Parameter */
+ { 0x34, 0xd2 }, /* Max A/D range */
+ { 0x38, 0x8b },
+ { 0x39, 0x40 },
+
+ { 0x3c, 0x39 }, /* Enable AEC mode changing */
+ { 0x3c, 0x3c }, /* Change AEC mode */
+ { 0x3c, 0x24 }, /* Disable AEC mode changing */
+
+ { 0x3d, 0x80 },
+ /* These next two registers (0x4a, 0x4b) are undocumented.
+ * They control the color balance */
+ { 0x4a, 0x80 },
+ { 0x4b, 0x80 },
+ { 0x4d, 0xd2 }, /* This reduces noise a bit */
+ { 0x4e, 0xc1 },
+ { 0x4f, 0x04 },
+/* Do 50-53 have any effect? */
+/* Toggle 0x12[2] off and on here? */
+};
+
+static const struct ov_i2c_regvals norm_6x30[] = {
+ { 0x12, 0x80 }, /* Reset */
+ { 0x00, 0x1f }, /* Gain */
+ { 0x01, 0x99 }, /* Blue gain */
+ { 0x02, 0x7c }, /* Red gain */
+ { 0x03, 0xc0 }, /* Saturation */
+ { 0x05, 0x0a }, /* Contrast */
+ { 0x06, 0x95 }, /* Brightness */
+ { 0x07, 0x2d }, /* Sharpness */
+ { 0x0c, 0x20 },
+ { 0x0d, 0x20 },
+ { 0x0e, 0xa0 }, /* Was 0x20, bit7 enables a 2x gain which we need */
+ { 0x0f, 0x05 },
+ { 0x10, 0x9a },
+ { 0x11, 0x00 }, /* Pixel clock = fastest */
+ { 0x12, 0x24 }, /* Enable AGC and AWB */
+ { 0x13, 0x21 },
+ { 0x14, 0x80 },
+ { 0x15, 0x01 },
+ { 0x16, 0x03 },
+ { 0x17, 0x38 },
+ { 0x18, 0xea },
+ { 0x19, 0x04 },
+ { 0x1a, 0x93 },
+ { 0x1b, 0x00 },
+ { 0x1e, 0xc4 },
+ { 0x1f, 0x04 },
+ { 0x20, 0x20 },
+ { 0x21, 0x10 },
+ { 0x22, 0x88 },
+ { 0x23, 0xc0 }, /* Crystal circuit power level */
+ { 0x25, 0x9a }, /* Increase AEC black ratio */
+ { 0x26, 0xb2 }, /* BLC enable */
+ { 0x27, 0xa2 },
+ { 0x28, 0x00 },
+ { 0x29, 0x00 },
+ { 0x2a, 0x84 }, /* 60 Hz power */
+ { 0x2b, 0xa8 }, /* 60 Hz power */
+ { 0x2c, 0xa0 },
+ { 0x2d, 0x95 }, /* Enable auto-brightness */
+ { 0x2e, 0x88 },
+ { 0x33, 0x26 },
+ { 0x34, 0x03 },
+ { 0x36, 0x8f },
+ { 0x37, 0x80 },
+ { 0x38, 0x83 },
+ { 0x39, 0x80 },
+ { 0x3a, 0x0f },
+ { 0x3b, 0x3c },
+ { 0x3c, 0x1a },
+ { 0x3d, 0x80 },
+ { 0x3e, 0x80 },
+ { 0x3f, 0x0e },
+ { 0x40, 0x00 }, /* White bal */
+ { 0x41, 0x00 }, /* White bal */
+ { 0x42, 0x80 },
+ { 0x43, 0x3f }, /* White bal */
+ { 0x44, 0x80 },
+ { 0x45, 0x20 },
+ { 0x46, 0x20 },
+ { 0x47, 0x80 },
+ { 0x48, 0x7f },
+ { 0x49, 0x00 },
+ { 0x4a, 0x00 },
+ { 0x4b, 0x80 },
+ { 0x4c, 0xd0 },
+ { 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */
+ { 0x4e, 0x40 },
+ { 0x4f, 0x07 }, /* UV avg., col. killer: max */
+ { 0x50, 0xff },
+ { 0x54, 0x23 }, /* Max AGC gain: 18dB */
+ { 0x55, 0xff },
+ { 0x56, 0x12 },
+ { 0x57, 0x81 },
+ { 0x58, 0x75 },
+ { 0x59, 0x01 }, /* AGC dark current comp.: +1 */
+ { 0x5a, 0x2c },
+ { 0x5b, 0x0f }, /* AWB chrominance levels */
+ { 0x5c, 0x10 },
+ { 0x3d, 0x80 },
+ { 0x27, 0xa6 },
+ { 0x12, 0x20 }, /* Toggle AWB */
+ { 0x12, 0x24 },
+};
+
+/* Lawrence Glaister <lg@jfm.bc.ca> reports:
+ *
+ * Register 0x0f in the 7610 has the following effects:
+ *
+ * 0x85 (AEC method 1): Best overall, good contrast range
+ * 0x45 (AEC method 2): Very overexposed
+ * 0xa5 (spec sheet default): Ok, but the black level is
+ * shifted resulting in loss of contrast
+ * 0x05 (old driver setting): very overexposed, too much
+ * contrast
+ */
+static const struct ov_i2c_regvals norm_7610[] = {
+ { 0x10, 0xff },
+ { 0x16, 0x06 },
+ { 0x28, 0x24 },
+ { 0x2b, 0xac },
+ { 0x12, 0x00 },
+ { 0x38, 0x81 },
+ { 0x28, 0x24 }, /* 0c */
+ { 0x0f, 0x85 }, /* lg's setting */
+ { 0x15, 0x01 },
+ { 0x20, 0x1c },
+ { 0x23, 0x2a },
+ { 0x24, 0x10 },
+ { 0x25, 0x8a },
+ { 0x26, 0xa2 },
+ { 0x27, 0xc2 },
+ { 0x2a, 0x04 },
+ { 0x2c, 0xfe },
+ { 0x2d, 0x93 },
+ { 0x30, 0x71 },
+ { 0x31, 0x60 },
+ { 0x32, 0x26 },
+ { 0x33, 0x20 },
+ { 0x34, 0x48 },
+ { 0x12, 0x24 },
+ { 0x11, 0x01 },
+ { 0x0c, 0x24 },
+ { 0x0d, 0x24 },
+};
+
+static const struct ov_i2c_regvals norm_7620[] = {
+ { 0x12, 0x80 }, /* reset */
+ { 0x00, 0x00 }, /* gain */
+ { 0x01, 0x80 }, /* blue gain */
+ { 0x02, 0x80 }, /* red gain */
+ { 0x03, 0xc0 }, /* OV7670_R03_VREF */
+ { 0x06, 0x60 },
+ { 0x07, 0x00 },
+ { 0x0c, 0x24 },
+ { 0x0c, 0x24 },
+ { 0x0d, 0x24 },
+ { 0x11, 0x01 },
+ { 0x12, 0x24 },
+ { 0x13, 0x01 },
+ { 0x14, 0x84 },
+ { 0x15, 0x01 },
+ { 0x16, 0x03 },
+ { 0x17, 0x2f },
+ { 0x18, 0xcf },
+ { 0x19, 0x06 },
+ { 0x1a, 0xf5 },
+ { 0x1b, 0x00 },
+ { 0x20, 0x18 },
+ { 0x21, 0x80 },
+ { 0x22, 0x80 },
+ { 0x23, 0x00 },
+ { 0x26, 0xa2 },
+ { 0x27, 0xea },
+ { 0x28, 0x22 }, /* Was 0x20, bit1 enables a 2x gain which we need */
+ { 0x29, 0x00 },
+ { 0x2a, 0x10 },
+ { 0x2b, 0x00 },
+ { 0x2c, 0x88 },
+ { 0x2d, 0x91 },
+ { 0x2e, 0x80 },
+ { 0x2f, 0x44 },
+ { 0x60, 0x27 },
+ { 0x61, 0x02 },
+ { 0x62, 0x5f },
+ { 0x63, 0xd5 },
+ { 0x64, 0x57 },
+ { 0x65, 0x83 },
+ { 0x66, 0x55 },
+ { 0x67, 0x92 },
+ { 0x68, 0xcf },
+ { 0x69, 0x76 },
+ { 0x6a, 0x22 },
+ { 0x6b, 0x00 },
+ { 0x6c, 0x02 },
+ { 0x6d, 0x44 },
+ { 0x6e, 0x80 },
+ { 0x6f, 0x1d },
+ { 0x70, 0x8b },
+ { 0x71, 0x00 },
+ { 0x72, 0x14 },
+ { 0x73, 0x54 },
+ { 0x74, 0x00 },
+ { 0x75, 0x8e },
+ { 0x76, 0x00 },
+ { 0x77, 0xff },
+ { 0x78, 0x80 },
+ { 0x79, 0x80 },
+ { 0x7a, 0x80 },
+ { 0x7b, 0xe2 },
+ { 0x7c, 0x00 },
+};
+
+/* 7640 and 7648. The defaults should be OK for most registers. */
+static const struct ov_i2c_regvals norm_7640[] = {
+ { 0x12, 0x80 },
+ { 0x12, 0x14 },
+};
+
+static const struct ov_regvals init_519_ov7660[] = {
+ { 0x5d, 0x03 }, /* Turn off suspend mode */
+ { 0x53, 0x9b }, /* 0x9f enables the (unused) microcontroller */
+ { 0x54, 0x0f }, /* bit2 (jpeg enable) */
+ { 0xa2, 0x20 }, /* a2-a5 are undocumented */
+ { 0xa3, 0x18 },
+ { 0xa4, 0x04 },
+ { 0xa5, 0x28 },
+ { 0x37, 0x00 }, /* SetUsbInit */
+ { 0x55, 0x02 }, /* 4.096 Mhz audio clock */
+ /* Enable both fields, YUV Input, disable defect comp (why?) */
+ { 0x20, 0x0c }, /* 0x0d does U <-> V swap */
+ { 0x21, 0x38 },
+ { 0x22, 0x1d },
+ { 0x17, 0x50 }, /* undocumented */
+ { 0x37, 0x00 }, /* undocumented */
+ { 0x40, 0xff }, /* I2C timeout counter */
+ { 0x46, 0x00 }, /* I2C clock prescaler */
+};
+static const struct ov_i2c_regvals norm_7660[] = {
+ {OV7670_R12_COM7, OV7670_COM7_RESET},
+ {OV7670_R11_CLKRC, 0x81},
+ {0x92, 0x00}, /* DM_LNL */
+ {0x93, 0x00}, /* DM_LNH */
+ {0x9d, 0x4c}, /* BD50ST */
+ {0x9e, 0x3f}, /* BD60ST */
+ {OV7670_R3B_COM11, 0x02},
+ {OV7670_R13_COM8, 0xf5},
+ {OV7670_R10_AECH, 0x00},
+ {OV7670_R00_GAIN, 0x00},
+ {OV7670_R01_BLUE, 0x7c},
+ {OV7670_R02_RED, 0x9d},
+ {OV7670_R12_COM7, 0x00},
+ {OV7670_R04_COM1, 00},
+ {OV7670_R18_HSTOP, 0x01},
+ {OV7670_R17_HSTART, 0x13},
+ {OV7670_R32_HREF, 0x92},
+ {OV7670_R19_VSTART, 0x02},
+ {OV7670_R1A_VSTOP, 0x7a},
+ {OV7670_R03_VREF, 0x00},
+ {OV7670_R0E_COM5, 0x04},
+ {OV7670_R0F_COM6, 0x62},
+ {OV7670_R15_COM10, 0x00},
+ {0x16, 0x02}, /* RSVD */
+ {0x1b, 0x00}, /* PSHFT */
+ {OV7670_R1E_MVFP, 0x01},
+ {0x29, 0x3c}, /* RSVD */
+ {0x33, 0x00}, /* CHLF */
+ {0x34, 0x07}, /* ARBLM */
+ {0x35, 0x84}, /* RSVD */
+ {0x36, 0x00}, /* RSVD */
+ {0x37, 0x04}, /* ADC */
+ {0x39, 0x43}, /* OFON */
+ {OV7670_R3A_TSLB, 0x00},
+ {OV7670_R3C_COM12, 0x6c},
+ {OV7670_R3D_COM13, 0x98},
+ {OV7670_R3F_EDGE, 0x23},
+ {OV7670_R40_COM15, 0xc1},
+ {OV7670_R41_COM16, 0x22},
+ {0x6b, 0x0a}, /* DBLV */
+ {0xa1, 0x08}, /* RSVD */
+ {0x69, 0x80}, /* HV */
+ {0x43, 0xf0}, /* RSVD.. */
+ {0x44, 0x10},
+ {0x45, 0x78},
+ {0x46, 0xa8},
+ {0x47, 0x60},
+ {0x48, 0x80},
+ {0x59, 0xba},
+ {0x5a, 0x9a},
+ {0x5b, 0x22},
+ {0x5c, 0xb9},
+ {0x5d, 0x9b},
+ {0x5e, 0x10},
+ {0x5f, 0xe0},
+ {0x60, 0x85},
+ {0x61, 0x60},
+ {0x9f, 0x9d}, /* RSVD */
+ {0xa0, 0xa0}, /* DSPC2 */
+ {0x4f, 0x60}, /* matrix */
+ {0x50, 0x64},
+ {0x51, 0x04},
+ {0x52, 0x18},
+ {0x53, 0x3c},
+ {0x54, 0x54},
+ {0x55, 0x40},
+ {0x56, 0x40},
+ {0x57, 0x40},
+ {0x58, 0x0d}, /* matrix sign */
+ {0x8b, 0xcc}, /* RSVD */
+ {0x8c, 0xcc},
+ {0x8d, 0xcf},
+ {0x6c, 0x40}, /* gamma curve */
+ {0x6d, 0xe0},
+ {0x6e, 0xa0},
+ {0x6f, 0x80},
+ {0x70, 0x70},
+ {0x71, 0x80},
+ {0x72, 0x60},
+ {0x73, 0x60},
+ {0x74, 0x50},
+ {0x75, 0x40},
+ {0x76, 0x38},
+ {0x77, 0x3c},
+ {0x78, 0x32},
+ {0x79, 0x1a},
+ {0x7a, 0x28},
+ {0x7b, 0x24},
+ {0x7c, 0x04}, /* gamma curve */
+ {0x7d, 0x12},
+ {0x7e, 0x26},
+ {0x7f, 0x46},
+ {0x80, 0x54},
+ {0x81, 0x64},
+ {0x82, 0x70},
+ {0x83, 0x7c},
+ {0x84, 0x86},
+ {0x85, 0x8e},
+ {0x86, 0x9c},
+ {0x87, 0xab},
+ {0x88, 0xc4},
+ {0x89, 0xd1},
+ {0x8a, 0xe5},
+ {OV7670_R14_COM9, 0x1e},
+ {OV7670_R24_AEW, 0x80},
+ {OV7670_R25_AEB, 0x72},
+ {OV7670_R26_VPT, 0xb3},
+ {0x62, 0x80}, /* LCC1 */
+ {0x63, 0x80}, /* LCC2 */
+ {0x64, 0x06}, /* LCC3 */
+ {0x65, 0x00}, /* LCC4 */
+ {0x66, 0x01}, /* LCC5 */
+ {0x94, 0x0e}, /* RSVD.. */
+ {0x95, 0x14},
+ {OV7670_R13_COM8, OV7670_COM8_FASTAEC
+ | OV7670_COM8_AECSTEP
+ | OV7670_COM8_BFILT
+ | 0x10
+ | OV7670_COM8_AGC
+ | OV7670_COM8_AWB
+ | OV7670_COM8_AEC},
+ {0xa1, 0xc8}
+};
+static const struct ov_i2c_regvals norm_9600[] = {
+ {0x12, 0x80},
+ {0x0c, 0x28},
+ {0x11, 0x80},
+ {0x13, 0xb5},
+ {0x14, 0x3e},
+ {0x1b, 0x04},
+ {0x24, 0xb0},
+ {0x25, 0x90},
+ {0x26, 0x94},
+ {0x35, 0x90},
+ {0x37, 0x07},
+ {0x38, 0x08},
+ {0x01, 0x8e},
+ {0x02, 0x85}
+};
+
+/* 7670. Defaults taken from OmniVision provided data,
+* as provided by Jonathan Corbet of OLPC */
+static const struct ov_i2c_regvals norm_7670[] = {
+ { OV7670_R12_COM7, OV7670_COM7_RESET },
+ { OV7670_R3A_TSLB, 0x04 }, /* OV */
+ { OV7670_R12_COM7, OV7670_COM7_FMT_VGA }, /* VGA */
+ { OV7670_R11_CLKRC, 0x01 },
+/*
+ * Set the hardware window. These values from OV don't entirely
+ * make sense - hstop is less than hstart. But they work...
+ */
+ { OV7670_R17_HSTART, 0x13 },
+ { OV7670_R18_HSTOP, 0x01 },
+ { OV7670_R32_HREF, 0xb6 },
+ { OV7670_R19_VSTART, 0x02 },
+ { OV7670_R1A_VSTOP, 0x7a },
+ { OV7670_R03_VREF, 0x0a },
+
+ { OV7670_R0C_COM3, 0x00 },
+ { OV7670_R3E_COM14, 0x00 },
+/* Mystery scaling numbers */
+ { 0x70, 0x3a },
+ { 0x71, 0x35 },
+ { 0x72, 0x11 },
+ { 0x73, 0xf0 },
+ { 0xa2, 0x02 },
+/* { OV7670_R15_COM10, 0x0 }, */
+
+/* Gamma curve values */
+ { 0x7a, 0x20 },
+ { 0x7b, 0x10 },
+ { 0x7c, 0x1e },
+ { 0x7d, 0x35 },
+ { 0x7e, 0x5a },
+ { 0x7f, 0x69 },
+ { 0x80, 0x76 },
+ { 0x81, 0x80 },
+ { 0x82, 0x88 },
+ { 0x83, 0x8f },
+ { 0x84, 0x96 },
+ { 0x85, 0xa3 },
+ { 0x86, 0xaf },
+ { 0x87, 0xc4 },
+ { 0x88, 0xd7 },
+ { 0x89, 0xe8 },
+
+/* AGC and AEC parameters. Note we start by disabling those features,
+ then turn them only after tweaking the values. */
+ { OV7670_R13_COM8, OV7670_COM8_FASTAEC
+ | OV7670_COM8_AECSTEP
+ | OV7670_COM8_BFILT },
+ { OV7670_R00_GAIN, 0x00 },
+ { OV7670_R10_AECH, 0x00 },
+ { OV7670_R0D_COM4, 0x40 }, /* magic reserved bit */
+ { OV7670_R14_COM9, 0x18 }, /* 4x gain + magic rsvd bit */
+ { OV7670_RA5_BD50MAX, 0x05 },
+ { OV7670_RAB_BD60MAX, 0x07 },
+ { OV7670_R24_AEW, 0x95 },
+ { OV7670_R25_AEB, 0x33 },
+ { OV7670_R26_VPT, 0xe3 },
+ { OV7670_R9F_HAECC1, 0x78 },
+ { OV7670_RA0_HAECC2, 0x68 },
+ { 0xa1, 0x03 }, /* magic */
+ { OV7670_RA6_HAECC3, 0xd8 },
+ { OV7670_RA7_HAECC4, 0xd8 },
+ { OV7670_RA8_HAECC5, 0xf0 },
+ { OV7670_RA9_HAECC6, 0x90 },
+ { OV7670_RAA_HAECC7, 0x94 },
+ { OV7670_R13_COM8, OV7670_COM8_FASTAEC
+ | OV7670_COM8_AECSTEP
+ | OV7670_COM8_BFILT
+ | OV7670_COM8_AGC
+ | OV7670_COM8_AEC },
+
+/* Almost all of these are magic "reserved" values. */
+ { OV7670_R0E_COM5, 0x61 },
+ { OV7670_R0F_COM6, 0x4b },
+ { 0x16, 0x02 },
+ { OV7670_R1E_MVFP, 0x07 },
+ { 0x21, 0x02 },
+ { 0x22, 0x91 },
+ { 0x29, 0x07 },
+ { 0x33, 0x0b },
+ { 0x35, 0x0b },
+ { 0x37, 0x1d },
+ { 0x38, 0x71 },
+ { 0x39, 0x2a },
+ { OV7670_R3C_COM12, 0x78 },
+ { 0x4d, 0x40 },
+ { 0x4e, 0x20 },
+ { OV7670_R69_GFIX, 0x00 },
+ { 0x6b, 0x4a },
+ { 0x74, 0x10 },
+ { 0x8d, 0x4f },
+ { 0x8e, 0x00 },
+ { 0x8f, 0x00 },
+ { 0x90, 0x00 },
+ { 0x91, 0x00 },
+ { 0x96, 0x00 },
+ { 0x9a, 0x00 },
+ { 0xb0, 0x84 },
+ { 0xb1, 0x0c },
+ { 0xb2, 0x0e },
+ { 0xb3, 0x82 },
+ { 0xb8, 0x0a },
+
+/* More reserved magic, some of which tweaks white balance */
+ { 0x43, 0x0a },
+ { 0x44, 0xf0 },
+ { 0x45, 0x34 },
+ { 0x46, 0x58 },
+ { 0x47, 0x28 },
+ { 0x48, 0x3a },
+ { 0x59, 0x88 },
+ { 0x5a, 0x88 },
+ { 0x5b, 0x44 },
+ { 0x5c, 0x67 },
+ { 0x5d, 0x49 },
+ { 0x5e, 0x0e },
+ { 0x6c, 0x0a },
+ { 0x6d, 0x55 },
+ { 0x6e, 0x11 },
+ { 0x6f, 0x9f }, /* "9e for advance AWB" */
+ { 0x6a, 0x40 },
+ { OV7670_R01_BLUE, 0x40 },
+ { OV7670_R02_RED, 0x60 },
+ { OV7670_R13_COM8, OV7670_COM8_FASTAEC
+ | OV7670_COM8_AECSTEP
+ | OV7670_COM8_BFILT
+ | OV7670_COM8_AGC
+ | OV7670_COM8_AEC
+ | OV7670_COM8_AWB },
+
+/* Matrix coefficients */
+ { 0x4f, 0x80 },
+ { 0x50, 0x80 },
+ { 0x51, 0x00 },
+ { 0x52, 0x22 },
+ { 0x53, 0x5e },
+ { 0x54, 0x80 },
+ { 0x58, 0x9e },
+
+ { OV7670_R41_COM16, OV7670_COM16_AWBGAIN },
+ { OV7670_R3F_EDGE, 0x00 },
+ { 0x75, 0x05 },
+ { 0x76, 0xe1 },
+ { 0x4c, 0x00 },
+ { 0x77, 0x01 },
+ { OV7670_R3D_COM13, OV7670_COM13_GAMMA
+ | OV7670_COM13_UVSAT
+ | 2}, /* was 3 */
+ { 0x4b, 0x09 },
+ { 0xc9, 0x60 },
+ { OV7670_R41_COM16, 0x38 },
+ { 0x56, 0x40 },
+
+ { 0x34, 0x11 },
+ { OV7670_R3B_COM11, OV7670_COM11_EXP|OV7670_COM11_HZAUTO },
+ { 0xa4, 0x88 },
+ { 0x96, 0x00 },
+ { 0x97, 0x30 },
+ { 0x98, 0x20 },
+ { 0x99, 0x30 },
+ { 0x9a, 0x84 },
+ { 0x9b, 0x29 },
+ { 0x9c, 0x03 },
+ { 0x9d, 0x4c },
+ { 0x9e, 0x3f },
+ { 0x78, 0x04 },
+
+/* Extra-weird stuff. Some sort of multiplexor register */
+ { 0x79, 0x01 },
+ { 0xc8, 0xf0 },
+ { 0x79, 0x0f },
+ { 0xc8, 0x00 },
+ { 0x79, 0x10 },
+ { 0xc8, 0x7e },
+ { 0x79, 0x0a },
+ { 0xc8, 0x80 },
+ { 0x79, 0x0b },
+ { 0xc8, 0x01 },
+ { 0x79, 0x0c },
+ { 0xc8, 0x0f },
+ { 0x79, 0x0d },
+ { 0xc8, 0x20 },
+ { 0x79, 0x09 },
+ { 0xc8, 0x80 },
+ { 0x79, 0x02 },
+ { 0xc8, 0xc0 },
+ { 0x79, 0x03 },
+ { 0xc8, 0x40 },
+ { 0x79, 0x05 },
+ { 0xc8, 0x30 },
+ { 0x79, 0x26 },
+};
+
+static const struct ov_i2c_regvals norm_8610[] = {
+ { 0x12, 0x80 },
+ { 0x00, 0x00 },
+ { 0x01, 0x80 },
+ { 0x02, 0x80 },
+ { 0x03, 0xc0 },
+ { 0x04, 0x30 },
+ { 0x05, 0x30 }, /* was 0x10, new from windrv 090403 */
+ { 0x06, 0x70 }, /* was 0x80, new from windrv 090403 */
+ { 0x0a, 0x86 },
+ { 0x0b, 0xb0 },
+ { 0x0c, 0x20 },
+ { 0x0d, 0x20 },
+ { 0x11, 0x01 },
+ { 0x12, 0x25 },
+ { 0x13, 0x01 },
+ { 0x14, 0x04 },
+ { 0x15, 0x01 }, /* Lin and Win think different about UV order */
+ { 0x16, 0x03 },
+ { 0x17, 0x38 }, /* was 0x2f, new from windrv 090403 */
+ { 0x18, 0xea }, /* was 0xcf, new from windrv 090403 */
+ { 0x19, 0x02 }, /* was 0x06, new from windrv 090403 */
+ { 0x1a, 0xf5 },
+ { 0x1b, 0x00 },
+ { 0x20, 0xd0 }, /* was 0x90, new from windrv 090403 */
+ { 0x23, 0xc0 }, /* was 0x00, new from windrv 090403 */
+ { 0x24, 0x30 }, /* was 0x1d, new from windrv 090403 */
+ { 0x25, 0x50 }, /* was 0x57, new from windrv 090403 */
+ { 0x26, 0xa2 },
+ { 0x27, 0xea },
+ { 0x28, 0x00 },
+ { 0x29, 0x00 },
+ { 0x2a, 0x80 },
+ { 0x2b, 0xc8 }, /* was 0xcc, new from windrv 090403 */
+ { 0x2c, 0xac },
+ { 0x2d, 0x45 }, /* was 0xd5, new from windrv 090403 */
+ { 0x2e, 0x80 },
+ { 0x2f, 0x14 }, /* was 0x01, new from windrv 090403 */
+ { 0x4c, 0x00 },
+ { 0x4d, 0x30 }, /* was 0x10, new from windrv 090403 */
+ { 0x60, 0x02 }, /* was 0x01, new from windrv 090403 */
+ { 0x61, 0x00 }, /* was 0x09, new from windrv 090403 */
+ { 0x62, 0x5f }, /* was 0xd7, new from windrv 090403 */
+ { 0x63, 0xff },
+ { 0x64, 0x53 }, /* new windrv 090403 says 0x57,
+ * maybe thats wrong */
+ { 0x65, 0x00 },
+ { 0x66, 0x55 },
+ { 0x67, 0xb0 },
+ { 0x68, 0xc0 }, /* was 0xaf, new from windrv 090403 */
+ { 0x69, 0x02 },
+ { 0x6a, 0x22 },
+ { 0x6b, 0x00 },
+ { 0x6c, 0x99 }, /* was 0x80, old windrv says 0x00, but
+ * deleting bit7 colors the first images red */
+ { 0x6d, 0x11 }, /* was 0x00, new from windrv 090403 */
+ { 0x6e, 0x11 }, /* was 0x00, new from windrv 090403 */
+ { 0x6f, 0x01 },
+ { 0x70, 0x8b },
+ { 0x71, 0x00 },
+ { 0x72, 0x14 },
+ { 0x73, 0x54 },
+ { 0x74, 0x00 },/* 0x60? - was 0x00, new from windrv 090403 */
+ { 0x75, 0x0e },
+ { 0x76, 0x02 }, /* was 0x02, new from windrv 090403 */
+ { 0x77, 0xff },
+ { 0x78, 0x80 },
+ { 0x79, 0x80 },
+ { 0x7a, 0x80 },
+ { 0x7b, 0x10 }, /* was 0x13, new from windrv 090403 */
+ { 0x7c, 0x00 },
+ { 0x7d, 0x08 }, /* was 0x09, new from windrv 090403 */
+ { 0x7e, 0x08 }, /* was 0xc0, new from windrv 090403 */
+ { 0x7f, 0xfb },
+ { 0x80, 0x28 },
+ { 0x81, 0x00 },
+ { 0x82, 0x23 },
+ { 0x83, 0x0b },
+ { 0x84, 0x00 },
+ { 0x85, 0x62 }, /* was 0x61, new from windrv 090403 */
+ { 0x86, 0xc9 },
+ { 0x87, 0x00 },
+ { 0x88, 0x00 },
+ { 0x89, 0x01 },
+ { 0x12, 0x20 },
+ { 0x12, 0x25 }, /* was 0x24, new from windrv 090403 */
+};
+
+static unsigned char ov7670_abs_to_sm(unsigned char v)
+{
+ if (v > 127)
+ return v & 0x7f;
+ return (128 - v) | 0x80;
+}
+
+/* Write a OV519 register */
+static void reg_w(struct sd *sd, u16 index, u16 value)
+{
+ int ret, req = 0;
+
+ if (sd->gspca_dev.usb_err < 0)
+ return;
+
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ req = 2;
+ break;
+ case BRIDGE_OVFX2:
+ req = 0x0a;
+ /* fall through */
+ case BRIDGE_W9968CF:
+ PDEBUG(D_USBO, "SET %02x %04x %04x",
+ req, value, index);
+ ret = usb_control_msg(sd->gspca_dev.dev,
+ usb_sndctrlpipe(sd->gspca_dev.dev, 0),
+ req,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0, 500);
+ goto leave;
+ default:
+ req = 1;
+ }
+
+ PDEBUG(D_USBO, "SET %02x 0000 %04x %02x",
+ req, index, value);
+ sd->gspca_dev.usb_buf[0] = value;
+ ret = usb_control_msg(sd->gspca_dev.dev,
+ usb_sndctrlpipe(sd->gspca_dev.dev, 0),
+ req,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index,
+ sd->gspca_dev.usb_buf, 1, 500);
+leave:
+ if (ret < 0) {
+ pr_err("reg_w %02x failed %d\n", index, ret);
+ sd->gspca_dev.usb_err = ret;
+ return;
+ }
+}
+
+/* Read from a OV519 register, note not valid for the w9968cf!! */
+/* returns: negative is error, pos or zero is data */
+static int reg_r(struct sd *sd, u16 index)
+{
+ int ret;
+ int req;
+
+ if (sd->gspca_dev.usb_err < 0)
+ return -1;
+
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ req = 3;
+ break;
+ case BRIDGE_OVFX2:
+ req = 0x0b;
+ break;
+ default:
+ req = 1;
+ }
+
+ ret = usb_control_msg(sd->gspca_dev.dev,
+ usb_rcvctrlpipe(sd->gspca_dev.dev, 0),
+ req,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index, sd->gspca_dev.usb_buf, 1, 500);
+
+ if (ret >= 0) {
+ ret = sd->gspca_dev.usb_buf[0];
+ PDEBUG(D_USBI, "GET %02x 0000 %04x %02x",
+ req, index, ret);
+ } else {
+ pr_err("reg_r %02x failed %d\n", index, ret);
+ sd->gspca_dev.usb_err = ret;
+ }
+
+ return ret;
+}
+
+/* Read 8 values from a OV519 register */
+static int reg_r8(struct sd *sd,
+ u16 index)
+{
+ int ret;
+
+ if (sd->gspca_dev.usb_err < 0)
+ return -1;
+
+ ret = usb_control_msg(sd->gspca_dev.dev,
+ usb_rcvctrlpipe(sd->gspca_dev.dev, 0),
+ 1, /* REQ_IO */
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index, sd->gspca_dev.usb_buf, 8, 500);
+
+ if (ret >= 0) {
+ ret = sd->gspca_dev.usb_buf[0];
+ } else {
+ pr_err("reg_r8 %02x failed %d\n", index, ret);
+ sd->gspca_dev.usb_err = ret;
+ }
+
+ return ret;
+}
+
+/*
+ * Writes bits at positions specified by mask to an OV51x reg. Bits that are in
+ * the same position as 1's in "mask" are cleared and set to "value". Bits
+ * that are in the same position as 0's in "mask" are preserved, regardless
+ * of their respective state in "value".
+ */
+static void reg_w_mask(struct sd *sd,
+ u16 index,
+ u8 value,
+ u8 mask)
+{
+ int ret;
+ u8 oldval;
+
+ if (mask != 0xff) {
+ value &= mask; /* Enforce mask on value */
+ ret = reg_r(sd, index);
+ if (ret < 0)
+ return;
+
+ oldval = ret & ~mask; /* Clear the masked bits */
+ value |= oldval; /* Set the desired bits */
+ }
+ reg_w(sd, index, value);
+}
+
+/*
+ * Writes multiple (n) byte value to a single register. Only valid with certain
+ * registers (0x30 and 0xc4 - 0xce).
+ */
+static void ov518_reg_w32(struct sd *sd, u16 index, u32 value, int n)
+{
+ int ret;
+
+ if (sd->gspca_dev.usb_err < 0)
+ return;
+
+ *((__le32 *) sd->gspca_dev.usb_buf) = __cpu_to_le32(value);
+
+ ret = usb_control_msg(sd->gspca_dev.dev,
+ usb_sndctrlpipe(sd->gspca_dev.dev, 0),
+ 1 /* REG_IO */,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index,
+ sd->gspca_dev.usb_buf, n, 500);
+ if (ret < 0) {
+ pr_err("reg_w32 %02x failed %d\n", index, ret);
+ sd->gspca_dev.usb_err = ret;
+ }
+}
+
+static void ov511_i2c_w(struct sd *sd, u8 reg, u8 value)
+{
+ int rc, retries;
+
+ PDEBUG(D_USBO, "ov511_i2c_w %02x %02x", reg, value);
+
+ /* Three byte write cycle */
+ for (retries = 6; ; ) {
+ /* Select camera register */
+ reg_w(sd, R51x_I2C_SADDR_3, reg);
+
+ /* Write "value" to I2C data port of OV511 */
+ reg_w(sd, R51x_I2C_DATA, value);
+
+ /* Initiate 3-byte write cycle */
+ reg_w(sd, R511_I2C_CTL, 0x01);
+
+ do {
+ rc = reg_r(sd, R511_I2C_CTL);
+ } while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */
+
+ if (rc < 0)
+ return;
+
+ if ((rc & 2) == 0) /* Ack? */
+ break;
+ if (--retries < 0) {
+ PDEBUG(D_USBO, "i2c write retries exhausted");
+ return;
+ }
+ }
+}
+
+static int ov511_i2c_r(struct sd *sd, u8 reg)
+{
+ int rc, value, retries;
+
+ /* Two byte write cycle */
+ for (retries = 6; ; ) {
+ /* Select camera register */
+ reg_w(sd, R51x_I2C_SADDR_2, reg);
+
+ /* Initiate 2-byte write cycle */
+ reg_w(sd, R511_I2C_CTL, 0x03);
+
+ do {
+ rc = reg_r(sd, R511_I2C_CTL);
+ } while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */
+
+ if (rc < 0)
+ return rc;
+
+ if ((rc & 2) == 0) /* Ack? */
+ break;
+
+ /* I2C abort */
+ reg_w(sd, R511_I2C_CTL, 0x10);
+
+ if (--retries < 0) {
+ PDEBUG(D_USBI, "i2c write retries exhausted");
+ return -1;
+ }
+ }
+
+ /* Two byte read cycle */
+ for (retries = 6; ; ) {
+ /* Initiate 2-byte read cycle */
+ reg_w(sd, R511_I2C_CTL, 0x05);
+
+ do {
+ rc = reg_r(sd, R511_I2C_CTL);
+ } while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */
+
+ if (rc < 0)
+ return rc;
+
+ if ((rc & 2) == 0) /* Ack? */
+ break;
+
+ /* I2C abort */
+ reg_w(sd, R511_I2C_CTL, 0x10);
+
+ if (--retries < 0) {
+ PDEBUG(D_USBI, "i2c read retries exhausted");
+ return -1;
+ }
+ }
+
+ value = reg_r(sd, R51x_I2C_DATA);
+
+ PDEBUG(D_USBI, "ov511_i2c_r %02x %02x", reg, value);
+
+ /* This is needed to make i2c_w() work */
+ reg_w(sd, R511_I2C_CTL, 0x05);
+
+ return value;
+}
+
+/*
+ * The OV518 I2C I/O procedure is different, hence, this function.
+ * This is normally only called from i2c_w(). Note that this function
+ * always succeeds regardless of whether the sensor is present and working.
+ */
+static void ov518_i2c_w(struct sd *sd,
+ u8 reg,
+ u8 value)
+{
+ PDEBUG(D_USBO, "ov518_i2c_w %02x %02x", reg, value);
+
+ /* Select camera register */
+ reg_w(sd, R51x_I2C_SADDR_3, reg);
+
+ /* Write "value" to I2C data port of OV511 */
+ reg_w(sd, R51x_I2C_DATA, value);
+
+ /* Initiate 3-byte write cycle */
+ reg_w(sd, R518_I2C_CTL, 0x01);
+
+ /* wait for write complete */
+ msleep(4);
+ reg_r8(sd, R518_I2C_CTL);
+}
+
+/*
+ * returns: negative is error, pos or zero is data
+ *
+ * The OV518 I2C I/O procedure is different, hence, this function.
+ * This is normally only called from i2c_r(). Note that this function
+ * always succeeds regardless of whether the sensor is present and working.
+ */
+static int ov518_i2c_r(struct sd *sd, u8 reg)
+{
+ int value;
+
+ /* Select camera register */
+ reg_w(sd, R51x_I2C_SADDR_2, reg);
+
+ /* Initiate 2-byte write cycle */
+ reg_w(sd, R518_I2C_CTL, 0x03);
+ reg_r8(sd, R518_I2C_CTL);
+
+ /* Initiate 2-byte read cycle */
+ reg_w(sd, R518_I2C_CTL, 0x05);
+ reg_r8(sd, R518_I2C_CTL);
+
+ value = reg_r(sd, R51x_I2C_DATA);
+ PDEBUG(D_USBI, "ov518_i2c_r %02x %02x", reg, value);
+ return value;
+}
+
+static void ovfx2_i2c_w(struct sd *sd, u8 reg, u8 value)
+{
+ int ret;
+
+ if (sd->gspca_dev.usb_err < 0)
+ return;
+
+ ret = usb_control_msg(sd->gspca_dev.dev,
+ usb_sndctrlpipe(sd->gspca_dev.dev, 0),
+ 0x02,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ (u16) value, (u16) reg, NULL, 0, 500);
+
+ if (ret < 0) {
+ pr_err("ovfx2_i2c_w %02x failed %d\n", reg, ret);
+ sd->gspca_dev.usb_err = ret;
+ }
+
+ PDEBUG(D_USBO, "ovfx2_i2c_w %02x %02x", reg, value);
+}
+
+static int ovfx2_i2c_r(struct sd *sd, u8 reg)
+{
+ int ret;
+
+ if (sd->gspca_dev.usb_err < 0)
+ return -1;
+
+ ret = usb_control_msg(sd->gspca_dev.dev,
+ usb_rcvctrlpipe(sd->gspca_dev.dev, 0),
+ 0x03,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, (u16) reg, sd->gspca_dev.usb_buf, 1, 500);
+
+ if (ret >= 0) {
+ ret = sd->gspca_dev.usb_buf[0];
+ PDEBUG(D_USBI, "ovfx2_i2c_r %02x %02x", reg, ret);
+ } else {
+ pr_err("ovfx2_i2c_r %02x failed %d\n", reg, ret);
+ sd->gspca_dev.usb_err = ret;
+ }
+
+ return ret;
+}
+
+static void i2c_w(struct sd *sd, u8 reg, u8 value)
+{
+ if (sd->sensor_reg_cache[reg] == value)
+ return;
+
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ ov511_i2c_w(sd, reg, value);
+ break;
+ case BRIDGE_OV518:
+ case BRIDGE_OV518PLUS:
+ case BRIDGE_OV519:
+ ov518_i2c_w(sd, reg, value);
+ break;
+ case BRIDGE_OVFX2:
+ ovfx2_i2c_w(sd, reg, value);
+ break;
+ case BRIDGE_W9968CF:
+ w9968cf_i2c_w(sd, reg, value);
+ break;
+ }
+
+ if (sd->gspca_dev.usb_err >= 0) {
+ /* Up on sensor reset empty the register cache */
+ if (reg == 0x12 && (value & 0x80))
+ memset(sd->sensor_reg_cache, -1,
+ sizeof(sd->sensor_reg_cache));
+ else
+ sd->sensor_reg_cache[reg] = value;
+ }
+}
+
+static int i2c_r(struct sd *sd, u8 reg)
+{
+ int ret = -1;
+
+ if (sd->sensor_reg_cache[reg] != -1)
+ return sd->sensor_reg_cache[reg];
+
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ ret = ov511_i2c_r(sd, reg);
+ break;
+ case BRIDGE_OV518:
+ case BRIDGE_OV518PLUS:
+ case BRIDGE_OV519:
+ ret = ov518_i2c_r(sd, reg);
+ break;
+ case BRIDGE_OVFX2:
+ ret = ovfx2_i2c_r(sd, reg);
+ break;
+ case BRIDGE_W9968CF:
+ ret = w9968cf_i2c_r(sd, reg);
+ break;
+ }
+
+ if (ret >= 0)
+ sd->sensor_reg_cache[reg] = ret;
+
+ return ret;
+}
+
+/* Writes bits at positions specified by mask to an I2C reg. Bits that are in
+ * the same position as 1's in "mask" are cleared and set to "value". Bits
+ * that are in the same position as 0's in "mask" are preserved, regardless
+ * of their respective state in "value".
+ */
+static void i2c_w_mask(struct sd *sd,
+ u8 reg,
+ u8 value,
+ u8 mask)
+{
+ int rc;
+ u8 oldval;
+
+ value &= mask; /* Enforce mask on value */
+ rc = i2c_r(sd, reg);
+ if (rc < 0)
+ return;
+ oldval = rc & ~mask; /* Clear the masked bits */
+ value |= oldval; /* Set the desired bits */
+ i2c_w(sd, reg, value);
+}
+
+/* Temporarily stops OV511 from functioning. Must do this before changing
+ * registers while the camera is streaming */
+static inline void ov51x_stop(struct sd *sd)
+{
+ PDEBUG(D_STREAM, "stopping");
+ sd->stopped = 1;
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ reg_w(sd, R51x_SYS_RESET, 0x3d);
+ break;
+ case BRIDGE_OV518:
+ case BRIDGE_OV518PLUS:
+ reg_w_mask(sd, R51x_SYS_RESET, 0x3a, 0x3a);
+ break;
+ case BRIDGE_OV519:
+ reg_w(sd, OV519_R51_RESET1, 0x0f);
+ reg_w(sd, OV519_R51_RESET1, 0x00);
+ reg_w(sd, 0x22, 0x00); /* FRAR */
+ break;
+ case BRIDGE_OVFX2:
+ reg_w_mask(sd, 0x0f, 0x00, 0x02);
+ break;
+ case BRIDGE_W9968CF:
+ reg_w(sd, 0x3c, 0x0a05); /* stop USB transfer */
+ break;
+ }
+}
+
+/* Restarts OV511 after ov511_stop() is called. Has no effect if it is not
+ * actually stopped (for performance). */
+static inline void ov51x_restart(struct sd *sd)
+{
+ PDEBUG(D_STREAM, "restarting");
+ if (!sd->stopped)
+ return;
+ sd->stopped = 0;
+
+ /* Reinitialize the stream */
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ reg_w(sd, R51x_SYS_RESET, 0x00);
+ break;
+ case BRIDGE_OV518:
+ case BRIDGE_OV518PLUS:
+ reg_w(sd, 0x2f, 0x80);
+ reg_w(sd, R51x_SYS_RESET, 0x00);
+ break;
+ case BRIDGE_OV519:
+ reg_w(sd, OV519_R51_RESET1, 0x0f);
+ reg_w(sd, OV519_R51_RESET1, 0x00);
+ reg_w(sd, 0x22, 0x1d); /* FRAR */
+ break;
+ case BRIDGE_OVFX2:
+ reg_w_mask(sd, 0x0f, 0x02, 0x02);
+ break;
+ case BRIDGE_W9968CF:
+ reg_w(sd, 0x3c, 0x8a05); /* USB FIFO enable */
+ break;
+ }
+}
+
+static void ov51x_set_slave_ids(struct sd *sd, u8 slave);
+
+/* This does an initial reset of an OmniVision sensor and ensures that I2C
+ * is synchronized. Returns <0 on failure.
+ */
+static int init_ov_sensor(struct sd *sd, u8 slave)
+{
+ int i;
+
+ ov51x_set_slave_ids(sd, slave);
+
+ /* Reset the sensor */
+ i2c_w(sd, 0x12, 0x80);
+
+ /* Wait for it to initialize */
+ msleep(150);
+
+ for (i = 0; i < i2c_detect_tries; i++) {
+ if (i2c_r(sd, OV7610_REG_ID_HIGH) == 0x7f &&
+ i2c_r(sd, OV7610_REG_ID_LOW) == 0xa2) {
+ PDEBUG(D_PROBE, "I2C synced in %d attempt(s)", i);
+ return 0;
+ }
+
+ /* Reset the sensor */
+ i2c_w(sd, 0x12, 0x80);
+
+ /* Wait for it to initialize */
+ msleep(150);
+
+ /* Dummy read to sync I2C */
+ if (i2c_r(sd, 0x00) < 0)
+ return -1;
+ }
+ return -1;
+}
+
+/* Set the read and write slave IDs. The "slave" argument is the write slave,
+ * and the read slave will be set to (slave + 1).
+ * This should not be called from outside the i2c I/O functions.
+ * Sets I2C read and write slave IDs. Returns <0 for error
+ */
+static void ov51x_set_slave_ids(struct sd *sd,
+ u8 slave)
+{
+ switch (sd->bridge) {
+ case BRIDGE_OVFX2:
+ reg_w(sd, OVFX2_I2C_ADDR, slave);
+ return;
+ case BRIDGE_W9968CF:
+ sd->sensor_addr = slave;
+ return;
+ }
+
+ reg_w(sd, R51x_I2C_W_SID, slave);
+ reg_w(sd, R51x_I2C_R_SID, slave + 1);
+}
+
+static void write_regvals(struct sd *sd,
+ const struct ov_regvals *regvals,
+ int n)
+{
+ while (--n >= 0) {
+ reg_w(sd, regvals->reg, regvals->val);
+ regvals++;
+ }
+}
+
+static void write_i2c_regvals(struct sd *sd,
+ const struct ov_i2c_regvals *regvals,
+ int n)
+{
+ while (--n >= 0) {
+ i2c_w(sd, regvals->reg, regvals->val);
+ regvals++;
+ }
+}
+
+/****************************************************************************
+ *
+ * OV511 and sensor configuration
+ *
+ ***************************************************************************/
+
+/* This initializes the OV2x10 / OV3610 / OV3620 / OV9600 */
+static void ov_hires_configure(struct sd *sd)
+{
+ int high, low;
+
+ if (sd->bridge != BRIDGE_OVFX2) {
+ pr_err("error hires sensors only supported with ovfx2\n");
+ return;
+ }
+
+ PDEBUG(D_PROBE, "starting ov hires configuration");
+
+ /* Detect sensor (sub)type */
+ high = i2c_r(sd, 0x0a);
+ low = i2c_r(sd, 0x0b);
+ /* info("%x, %x", high, low); */
+ switch (high) {
+ case 0x96:
+ switch (low) {
+ case 0x40:
+ PDEBUG(D_PROBE, "Sensor is a OV2610");
+ sd->sensor = SEN_OV2610;
+ return;
+ case 0x41:
+ PDEBUG(D_PROBE, "Sensor is a OV2610AE");
+ sd->sensor = SEN_OV2610AE;
+ return;
+ case 0xb1:
+ PDEBUG(D_PROBE, "Sensor is a OV9600");
+ sd->sensor = SEN_OV9600;
+ return;
+ }
+ break;
+ case 0x36:
+ if ((low & 0x0f) == 0x00) {
+ PDEBUG(D_PROBE, "Sensor is a OV3610");
+ sd->sensor = SEN_OV3610;
+ return;
+ }
+ break;
+ }
+ pr_err("Error unknown sensor type: %02x%02x\n", high, low);
+}
+
+/* This initializes the OV8110, OV8610 sensor. The OV8110 uses
+ * the same register settings as the OV8610, since they are very similar.
+ */
+static void ov8xx0_configure(struct sd *sd)
+{
+ int rc;
+
+ PDEBUG(D_PROBE, "starting ov8xx0 configuration");
+
+ /* Detect sensor (sub)type */
+ rc = i2c_r(sd, OV7610_REG_COM_I);
+ if (rc < 0) {
+ PDEBUG(D_ERR, "Error detecting sensor type");
+ return;
+ }
+ if ((rc & 3) == 1)
+ sd->sensor = SEN_OV8610;
+ else
+ pr_err("Unknown image sensor version: %d\n", rc & 3);
+}
+
+/* This initializes the OV7610, OV7620, or OV76BE sensor. The OV76BE uses
+ * the same register settings as the OV7610, since they are very similar.
+ */
+static void ov7xx0_configure(struct sd *sd)
+{
+ int rc, high, low;
+
+ PDEBUG(D_PROBE, "starting OV7xx0 configuration");
+
+ /* Detect sensor (sub)type */
+ rc = i2c_r(sd, OV7610_REG_COM_I);
+
+ /* add OV7670 here
+ * it appears to be wrongly detected as a 7610 by default */
+ if (rc < 0) {
+ PDEBUG(D_ERR, "Error detecting sensor type");
+ return;
+ }
+ if ((rc & 3) == 3) {
+ /* quick hack to make OV7670s work */
+ high = i2c_r(sd, 0x0a);
+ low = i2c_r(sd, 0x0b);
+ /* info("%x, %x", high, low); */
+ if (high == 0x76 && (low & 0xf0) == 0x70) {
+ PDEBUG(D_PROBE, "Sensor is an OV76%02x", low);
+ sd->sensor = SEN_OV7670;
+ } else {
+ PDEBUG(D_PROBE, "Sensor is an OV7610");
+ sd->sensor = SEN_OV7610;
+ }
+ } else if ((rc & 3) == 1) {
+ /* I don't know what's different about the 76BE yet. */
+ if (i2c_r(sd, 0x15) & 1) {
+ PDEBUG(D_PROBE, "Sensor is an OV7620AE");
+ sd->sensor = SEN_OV7620AE;
+ } else {
+ PDEBUG(D_PROBE, "Sensor is an OV76BE");
+ sd->sensor = SEN_OV76BE;
+ }
+ } else if ((rc & 3) == 0) {
+ /* try to read product id registers */
+ high = i2c_r(sd, 0x0a);
+ if (high < 0) {
+ PDEBUG(D_ERR, "Error detecting camera chip PID");
+ return;
+ }
+ low = i2c_r(sd, 0x0b);
+ if (low < 0) {
+ PDEBUG(D_ERR, "Error detecting camera chip VER");
+ return;
+ }
+ if (high == 0x76) {
+ switch (low) {
+ case 0x30:
+ pr_err("Sensor is an OV7630/OV7635\n");
+ pr_err("7630 is not supported by this driver\n");
+ return;
+ case 0x40:
+ PDEBUG(D_PROBE, "Sensor is an OV7645");
+ sd->sensor = SEN_OV7640; /* FIXME */
+ break;
+ case 0x45:
+ PDEBUG(D_PROBE, "Sensor is an OV7645B");
+ sd->sensor = SEN_OV7640; /* FIXME */
+ break;
+ case 0x48:
+ PDEBUG(D_PROBE, "Sensor is an OV7648");
+ sd->sensor = SEN_OV7648;
+ break;
+ case 0x60:
+ PDEBUG(D_PROBE, "Sensor is a OV7660");
+ sd->sensor = SEN_OV7660;
+ break;
+ default:
+ PDEBUG(D_PROBE, "Unknown sensor: 0x76%x", low);
+ return;
+ }
+ } else {
+ PDEBUG(D_PROBE, "Sensor is an OV7620");
+ sd->sensor = SEN_OV7620;
+ }
+ } else {
+ pr_err("Unknown image sensor version: %d\n", rc & 3);
+ }
+}
+
+/* This initializes the OV6620, OV6630, OV6630AE, or OV6630AF sensor. */
+static void ov6xx0_configure(struct sd *sd)
+{
+ int rc;
+ PDEBUG(D_PROBE, "starting OV6xx0 configuration");
+
+ /* Detect sensor (sub)type */
+ rc = i2c_r(sd, OV7610_REG_COM_I);
+ if (rc < 0) {
+ PDEBUG(D_ERR, "Error detecting sensor type");
+ return;
+ }
+
+ /* Ugh. The first two bits are the version bits, but
+ * the entire register value must be used. I guess OVT
+ * underestimated how many variants they would make. */
+ switch (rc) {
+ case 0x00:
+ sd->sensor = SEN_OV6630;
+ pr_warn("WARNING: Sensor is an OV66308. Your camera may have been misdetected in previous driver versions.\n");
+ break;
+ case 0x01:
+ sd->sensor = SEN_OV6620;
+ PDEBUG(D_PROBE, "Sensor is an OV6620");
+ break;
+ case 0x02:
+ sd->sensor = SEN_OV6630;
+ PDEBUG(D_PROBE, "Sensor is an OV66308AE");
+ break;
+ case 0x03:
+ sd->sensor = SEN_OV66308AF;
+ PDEBUG(D_PROBE, "Sensor is an OV66308AF");
+ break;
+ case 0x90:
+ sd->sensor = SEN_OV6630;
+ pr_warn("WARNING: Sensor is an OV66307. Your camera may have been misdetected in previous driver versions.\n");
+ break;
+ default:
+ pr_err("FATAL: Unknown sensor version: 0x%02x\n", rc);
+ return;
+ }
+
+ /* Set sensor-specific vars */
+ sd->sif = 1;
+}
+
+/* Turns on or off the LED. Only has an effect with OV511+/OV518(+)/OV519 */
+static void ov51x_led_control(struct sd *sd, int on)
+{
+ if (sd->invert_led)
+ on = !on;
+
+ switch (sd->bridge) {
+ /* OV511 has no LED control */
+ case BRIDGE_OV511PLUS:
+ reg_w(sd, R511_SYS_LED_CTL, on);
+ break;
+ case BRIDGE_OV518:
+ case BRIDGE_OV518PLUS:
+ reg_w_mask(sd, R518_GPIO_OUT, 0x02 * on, 0x02);
+ break;
+ case BRIDGE_OV519:
+ reg_w_mask(sd, OV519_GPIO_DATA_OUT0, on, 1);
+ break;
+ }
+}
+
+static void sd_reset_snapshot(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (!sd->snapshot_needs_reset)
+ return;
+
+ /* Note it is important that we clear sd->snapshot_needs_reset,
+ before actually clearing the snapshot state in the bridge
+ otherwise we might race with the pkt_scan interrupt handler */
+ sd->snapshot_needs_reset = 0;
+
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ reg_w(sd, R51x_SYS_SNAP, 0x02);
+ reg_w(sd, R51x_SYS_SNAP, 0x00);
+ break;
+ case BRIDGE_OV518:
+ case BRIDGE_OV518PLUS:
+ reg_w(sd, R51x_SYS_SNAP, 0x02); /* Reset */
+ reg_w(sd, R51x_SYS_SNAP, 0x01); /* Enable */
+ break;
+ case BRIDGE_OV519:
+ reg_w(sd, R51x_SYS_RESET, 0x40);
+ reg_w(sd, R51x_SYS_RESET, 0x00);
+ break;
+ }
+}
+
+static void ov51x_upload_quan_tables(struct sd *sd)
+{
+ const unsigned char yQuanTable511[] = {
+ 0, 1, 1, 2, 2, 3, 3, 4,
+ 1, 1, 1, 2, 2, 3, 4, 4,
+ 1, 1, 2, 2, 3, 4, 4, 4,
+ 2, 2, 2, 3, 4, 4, 4, 4,
+ 2, 2, 3, 4, 4, 5, 5, 5,
+ 3, 3, 4, 4, 5, 5, 5, 5,
+ 3, 4, 4, 4, 5, 5, 5, 5,
+ 4, 4, 4, 4, 5, 5, 5, 5
+ };
+
+ const unsigned char uvQuanTable511[] = {
+ 0, 2, 2, 3, 4, 4, 4, 4,
+ 2, 2, 2, 4, 4, 4, 4, 4,
+ 2, 2, 3, 4, 4, 4, 4, 4,
+ 3, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4
+ };
+
+ /* OV518 quantization tables are 8x4 (instead of 8x8) */
+ const unsigned char yQuanTable518[] = {
+ 5, 4, 5, 6, 6, 7, 7, 7,
+ 5, 5, 5, 5, 6, 7, 7, 7,
+ 6, 6, 6, 6, 7, 7, 7, 8,
+ 7, 7, 6, 7, 7, 7, 8, 8
+ };
+ const unsigned char uvQuanTable518[] = {
+ 6, 6, 6, 7, 7, 7, 7, 7,
+ 6, 6, 6, 7, 7, 7, 7, 7,
+ 6, 6, 6, 7, 7, 7, 7, 8,
+ 7, 7, 7, 7, 7, 7, 8, 8
+ };
+
+ const unsigned char *pYTable, *pUVTable;
+ unsigned char val0, val1;
+ int i, size, reg = R51x_COMP_LUT_BEGIN;
+
+ PDEBUG(D_PROBE, "Uploading quantization tables");
+
+ if (sd->bridge == BRIDGE_OV511 || sd->bridge == BRIDGE_OV511PLUS) {
+ pYTable = yQuanTable511;
+ pUVTable = uvQuanTable511;
+ size = 32;
+ } else {
+ pYTable = yQuanTable518;
+ pUVTable = uvQuanTable518;
+ size = 16;
+ }
+
+ for (i = 0; i < size; i++) {
+ val0 = *pYTable++;
+ val1 = *pYTable++;
+ val0 &= 0x0f;
+ val1 &= 0x0f;
+ val0 |= val1 << 4;
+ reg_w(sd, reg, val0);
+
+ val0 = *pUVTable++;
+ val1 = *pUVTable++;
+ val0 &= 0x0f;
+ val1 &= 0x0f;
+ val0 |= val1 << 4;
+ reg_w(sd, reg + size, val0);
+
+ reg++;
+ }
+}
+
+/* This initializes the OV511/OV511+ and the sensor */
+static void ov511_configure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* For 511 and 511+ */
+ const struct ov_regvals init_511[] = {
+ { R51x_SYS_RESET, 0x7f },
+ { R51x_SYS_INIT, 0x01 },
+ { R51x_SYS_RESET, 0x7f },
+ { R51x_SYS_INIT, 0x01 },
+ { R51x_SYS_RESET, 0x3f },
+ { R51x_SYS_INIT, 0x01 },
+ { R51x_SYS_RESET, 0x3d },
+ };
+
+ const struct ov_regvals norm_511[] = {
+ { R511_DRAM_FLOW_CTL, 0x01 },
+ { R51x_SYS_SNAP, 0x00 },
+ { R51x_SYS_SNAP, 0x02 },
+ { R51x_SYS_SNAP, 0x00 },
+ { R511_FIFO_OPTS, 0x1f },
+ { R511_COMP_EN, 0x00 },
+ { R511_COMP_LUT_EN, 0x03 },
+ };
+
+ const struct ov_regvals norm_511_p[] = {
+ { R511_DRAM_FLOW_CTL, 0xff },
+ { R51x_SYS_SNAP, 0x00 },
+ { R51x_SYS_SNAP, 0x02 },
+ { R51x_SYS_SNAP, 0x00 },
+ { R511_FIFO_OPTS, 0xff },
+ { R511_COMP_EN, 0x00 },
+ { R511_COMP_LUT_EN, 0x03 },
+ };
+
+ const struct ov_regvals compress_511[] = {
+ { 0x70, 0x1f },
+ { 0x71, 0x05 },
+ { 0x72, 0x06 },
+ { 0x73, 0x06 },
+ { 0x74, 0x14 },
+ { 0x75, 0x03 },
+ { 0x76, 0x04 },
+ { 0x77, 0x04 },
+ };
+
+ PDEBUG(D_PROBE, "Device custom id %x", reg_r(sd, R51x_SYS_CUST_ID));
+
+ write_regvals(sd, init_511, ARRAY_SIZE(init_511));
+
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ write_regvals(sd, norm_511, ARRAY_SIZE(norm_511));
+ break;
+ case BRIDGE_OV511PLUS:
+ write_regvals(sd, norm_511_p, ARRAY_SIZE(norm_511_p));
+ break;
+ }
+
+ /* Init compression */
+ write_regvals(sd, compress_511, ARRAY_SIZE(compress_511));
+
+ ov51x_upload_quan_tables(sd);
+}
+
+/* This initializes the OV518/OV518+ and the sensor */
+static void ov518_configure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* For 518 and 518+ */
+ const struct ov_regvals init_518[] = {
+ { R51x_SYS_RESET, 0x40 },
+ { R51x_SYS_INIT, 0xe1 },
+ { R51x_SYS_RESET, 0x3e },
+ { R51x_SYS_INIT, 0xe1 },
+ { R51x_SYS_RESET, 0x00 },
+ { R51x_SYS_INIT, 0xe1 },
+ { 0x46, 0x00 },
+ { 0x5d, 0x03 },
+ };
+
+ const struct ov_regvals norm_518[] = {
+ { R51x_SYS_SNAP, 0x02 }, /* Reset */
+ { R51x_SYS_SNAP, 0x01 }, /* Enable */
+ { 0x31, 0x0f },
+ { 0x5d, 0x03 },
+ { 0x24, 0x9f },
+ { 0x25, 0x90 },
+ { 0x20, 0x00 },
+ { 0x51, 0x04 },
+ { 0x71, 0x19 },
+ { 0x2f, 0x80 },
+ };
+
+ const struct ov_regvals norm_518_p[] = {
+ { R51x_SYS_SNAP, 0x02 }, /* Reset */
+ { R51x_SYS_SNAP, 0x01 }, /* Enable */
+ { 0x31, 0x0f },
+ { 0x5d, 0x03 },
+ { 0x24, 0x9f },
+ { 0x25, 0x90 },
+ { 0x20, 0x60 },
+ { 0x51, 0x02 },
+ { 0x71, 0x19 },
+ { 0x40, 0xff },
+ { 0x41, 0x42 },
+ { 0x46, 0x00 },
+ { 0x33, 0x04 },
+ { 0x21, 0x19 },
+ { 0x3f, 0x10 },
+ { 0x2f, 0x80 },
+ };
+
+ /* First 5 bits of custom ID reg are a revision ID on OV518 */
+ PDEBUG(D_PROBE, "Device revision %d",
+ 0x1f & reg_r(sd, R51x_SYS_CUST_ID));
+
+ write_regvals(sd, init_518, ARRAY_SIZE(init_518));
+
+ /* Set LED GPIO pin to output mode */
+ reg_w_mask(sd, R518_GPIO_CTL, 0x00, 0x02);
+
+ switch (sd->bridge) {
+ case BRIDGE_OV518:
+ write_regvals(sd, norm_518, ARRAY_SIZE(norm_518));
+ break;
+ case BRIDGE_OV518PLUS:
+ write_regvals(sd, norm_518_p, ARRAY_SIZE(norm_518_p));
+ break;
+ }
+
+ ov51x_upload_quan_tables(sd);
+
+ reg_w(sd, 0x2f, 0x80);
+}
+
+static void ov519_configure(struct sd *sd)
+{
+ static const struct ov_regvals init_519[] = {
+ { 0x5a, 0x6d }, /* EnableSystem */
+ { 0x53, 0x9b }, /* don't enable the microcontroller */
+ { OV519_R54_EN_CLK1, 0xff }, /* set bit2 to enable jpeg */
+ { 0x5d, 0x03 },
+ { 0x49, 0x01 },
+ { 0x48, 0x00 },
+ /* Set LED pin to output mode. Bit 4 must be cleared or sensor
+ * detection will fail. This deserves further investigation. */
+ { OV519_GPIO_IO_CTRL0, 0xee },
+ { OV519_R51_RESET1, 0x0f },
+ { OV519_R51_RESET1, 0x00 },
+ { 0x22, 0x00 },
+ /* windows reads 0x55 at this point*/
+ };
+
+ write_regvals(sd, init_519, ARRAY_SIZE(init_519));
+}
+
+static void ovfx2_configure(struct sd *sd)
+{
+ static const struct ov_regvals init_fx2[] = {
+ { 0x00, 0x60 },
+ { 0x02, 0x01 },
+ { 0x0f, 0x1d },
+ { 0xe9, 0x82 },
+ { 0xea, 0xc7 },
+ { 0xeb, 0x10 },
+ { 0xec, 0xf6 },
+ };
+
+ sd->stopped = 1;
+
+ write_regvals(sd, init_fx2, ARRAY_SIZE(init_fx2));
+}
+
+/* set the mode */
+/* This function works for ov7660 only */
+static void ov519_set_mode(struct sd *sd)
+{
+ static const struct ov_regvals bridge_ov7660[2][10] = {
+ {{0x10, 0x14}, {0x11, 0x1e}, {0x12, 0x00}, {0x13, 0x00},
+ {0x14, 0x00}, {0x15, 0x00}, {0x16, 0x00}, {0x20, 0x0c},
+ {0x25, 0x01}, {0x26, 0x00}},
+ {{0x10, 0x28}, {0x11, 0x3c}, {0x12, 0x00}, {0x13, 0x00},
+ {0x14, 0x00}, {0x15, 0x00}, {0x16, 0x00}, {0x20, 0x0c},
+ {0x25, 0x03}, {0x26, 0x00}}
+ };
+ static const struct ov_i2c_regvals sensor_ov7660[2][3] = {
+ {{0x12, 0x00}, {0x24, 0x00}, {0x0c, 0x0c}},
+ {{0x12, 0x00}, {0x04, 0x00}, {0x0c, 0x00}}
+ };
+ static const struct ov_i2c_regvals sensor_ov7660_2[] = {
+ {OV7670_R17_HSTART, 0x13},
+ {OV7670_R18_HSTOP, 0x01},
+ {OV7670_R32_HREF, 0x92},
+ {OV7670_R19_VSTART, 0x02},
+ {OV7670_R1A_VSTOP, 0x7a},
+ {OV7670_R03_VREF, 0x00},
+/* {0x33, 0x00}, */
+/* {0x34, 0x07}, */
+/* {0x36, 0x00}, */
+/* {0x6b, 0x0a}, */
+ };
+
+ write_regvals(sd, bridge_ov7660[sd->gspca_dev.curr_mode],
+ ARRAY_SIZE(bridge_ov7660[0]));
+ write_i2c_regvals(sd, sensor_ov7660[sd->gspca_dev.curr_mode],
+ ARRAY_SIZE(sensor_ov7660[0]));
+ write_i2c_regvals(sd, sensor_ov7660_2,
+ ARRAY_SIZE(sensor_ov7660_2));
+}
+
+/* set the frame rate */
+/* This function works for sensors ov7640, ov7648 ov7660 and ov7670 only */
+static void ov519_set_fr(struct sd *sd)
+{
+ int fr;
+ u8 clock;
+ /* frame rate table with indices:
+ * - mode = 0: 320x240, 1: 640x480
+ * - fr rate = 0: 30, 1: 25, 2: 20, 3: 15, 4: 10, 5: 5
+ * - reg = 0: bridge a4, 1: bridge 23, 2: sensor 11 (clock)
+ */
+ static const u8 fr_tb[2][6][3] = {
+ {{0x04, 0xff, 0x00},
+ {0x04, 0x1f, 0x00},
+ {0x04, 0x1b, 0x00},
+ {0x04, 0x15, 0x00},
+ {0x04, 0x09, 0x00},
+ {0x04, 0x01, 0x00}},
+ {{0x0c, 0xff, 0x00},
+ {0x0c, 0x1f, 0x00},
+ {0x0c, 0x1b, 0x00},
+ {0x04, 0xff, 0x01},
+ {0x04, 0x1f, 0x01},
+ {0x04, 0x1b, 0x01}},
+ };
+
+ if (frame_rate > 0)
+ sd->frame_rate = frame_rate;
+ if (sd->frame_rate >= 30)
+ fr = 0;
+ else if (sd->frame_rate >= 25)
+ fr = 1;
+ else if (sd->frame_rate >= 20)
+ fr = 2;
+ else if (sd->frame_rate >= 15)
+ fr = 3;
+ else if (sd->frame_rate >= 10)
+ fr = 4;
+ else
+ fr = 5;
+ reg_w(sd, 0xa4, fr_tb[sd->gspca_dev.curr_mode][fr][0]);
+ reg_w(sd, 0x23, fr_tb[sd->gspca_dev.curr_mode][fr][1]);
+ clock = fr_tb[sd->gspca_dev.curr_mode][fr][2];
+ if (sd->sensor == SEN_OV7660)
+ clock |= 0x80; /* enable double clock */
+ ov518_i2c_w(sd, OV7670_R11_CLKRC, clock);
+}
+
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ i2c_w_mask(sd, 0x13, sd->ctrls[AUTOGAIN].val ? 0x05 : 0x00, 0x05);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam = &gspca_dev->cam;
+
+ sd->bridge = id->driver_info & BRIDGE_MASK;
+ sd->invert_led = (id->driver_info & BRIDGE_INVERT_LED) != 0;
+
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ cam->cam_mode = ov511_vga_mode;
+ cam->nmodes = ARRAY_SIZE(ov511_vga_mode);
+ break;
+ case BRIDGE_OV518:
+ case BRIDGE_OV518PLUS:
+ cam->cam_mode = ov518_vga_mode;
+ cam->nmodes = ARRAY_SIZE(ov518_vga_mode);
+ break;
+ case BRIDGE_OV519:
+ cam->cam_mode = ov519_vga_mode;
+ cam->nmodes = ARRAY_SIZE(ov519_vga_mode);
+ break;
+ case BRIDGE_OVFX2:
+ cam->cam_mode = ov519_vga_mode;
+ cam->nmodes = ARRAY_SIZE(ov519_vga_mode);
+ cam->bulk_size = OVFX2_BULK_SIZE;
+ cam->bulk_nurbs = MAX_NURBS;
+ cam->bulk = 1;
+ break;
+ case BRIDGE_W9968CF:
+ cam->cam_mode = w9968cf_vga_mode;
+ cam->nmodes = ARRAY_SIZE(w9968cf_vga_mode);
+ break;
+ }
+
+ gspca_dev->cam.ctrls = sd->ctrls;
+ sd->quality = QUALITY_DEF;
+ sd->frame_rate = 15;
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam = &gspca_dev->cam;
+
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ ov511_configure(gspca_dev);
+ break;
+ case BRIDGE_OV518:
+ case BRIDGE_OV518PLUS:
+ ov518_configure(gspca_dev);
+ break;
+ case BRIDGE_OV519:
+ ov519_configure(sd);
+ break;
+ case BRIDGE_OVFX2:
+ ovfx2_configure(sd);
+ break;
+ case BRIDGE_W9968CF:
+ w9968cf_configure(sd);
+ break;
+ }
+
+ /* The OV519 must be more aggressive about sensor detection since
+ * I2C write will never fail if the sensor is not present. We have
+ * to try to initialize the sensor to detect its presence */
+ sd->sensor = -1;
+
+ /* Test for 76xx */
+ if (init_ov_sensor(sd, OV7xx0_SID) >= 0) {
+ ov7xx0_configure(sd);
+
+ /* Test for 6xx0 */
+ } else if (init_ov_sensor(sd, OV6xx0_SID) >= 0) {
+ ov6xx0_configure(sd);
+
+ /* Test for 8xx0 */
+ } else if (init_ov_sensor(sd, OV8xx0_SID) >= 0) {
+ ov8xx0_configure(sd);
+
+ /* Test for 3xxx / 2xxx */
+ } else if (init_ov_sensor(sd, OV_HIRES_SID) >= 0) {
+ ov_hires_configure(sd);
+ } else {
+ pr_err("Can't determine sensor slave IDs\n");
+ goto error;
+ }
+
+ if (sd->sensor < 0)
+ goto error;
+
+ ov51x_led_control(sd, 0); /* turn LED off */
+
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ if (sd->sif) {
+ cam->cam_mode = ov511_sif_mode;
+ cam->nmodes = ARRAY_SIZE(ov511_sif_mode);
+ }
+ break;
+ case BRIDGE_OV518:
+ case BRIDGE_OV518PLUS:
+ if (sd->sif) {
+ cam->cam_mode = ov518_sif_mode;
+ cam->nmodes = ARRAY_SIZE(ov518_sif_mode);
+ }
+ break;
+ case BRIDGE_OV519:
+ if (sd->sif) {
+ cam->cam_mode = ov519_sif_mode;
+ cam->nmodes = ARRAY_SIZE(ov519_sif_mode);
+ }
+ break;
+ case BRIDGE_OVFX2:
+ switch (sd->sensor) {
+ case SEN_OV2610:
+ case SEN_OV2610AE:
+ cam->cam_mode = ovfx2_ov2610_mode;
+ cam->nmodes = ARRAY_SIZE(ovfx2_ov2610_mode);
+ break;
+ case SEN_OV3610:
+ cam->cam_mode = ovfx2_ov3610_mode;
+ cam->nmodes = ARRAY_SIZE(ovfx2_ov3610_mode);
+ break;
+ case SEN_OV9600:
+ cam->cam_mode = ovfx2_ov9600_mode;
+ cam->nmodes = ARRAY_SIZE(ovfx2_ov9600_mode);
+ break;
+ default:
+ if (sd->sif) {
+ cam->cam_mode = ov519_sif_mode;
+ cam->nmodes = ARRAY_SIZE(ov519_sif_mode);
+ }
+ break;
+ }
+ break;
+ case BRIDGE_W9968CF:
+ if (sd->sif)
+ cam->nmodes = ARRAY_SIZE(w9968cf_vga_mode) - 1;
+
+ /* w9968cf needs initialisation once the sensor is known */
+ w9968cf_init(sd);
+ break;
+ }
+
+ gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
+
+ /* initialize the sensor */
+ switch (sd->sensor) {
+ case SEN_OV2610:
+ write_i2c_regvals(sd, norm_2610, ARRAY_SIZE(norm_2610));
+
+ /* Enable autogain, autoexpo, awb, bandfilter */
+ i2c_w_mask(sd, 0x13, 0x27, 0x27);
+ break;
+ case SEN_OV2610AE:
+ write_i2c_regvals(sd, norm_2610ae, ARRAY_SIZE(norm_2610ae));
+
+ /* enable autoexpo */
+ i2c_w_mask(sd, 0x13, 0x05, 0x05);
+ break;
+ case SEN_OV3610:
+ write_i2c_regvals(sd, norm_3620b, ARRAY_SIZE(norm_3620b));
+
+ /* Enable autogain, autoexpo, awb, bandfilter */
+ i2c_w_mask(sd, 0x13, 0x27, 0x27);
+ break;
+ case SEN_OV6620:
+ write_i2c_regvals(sd, norm_6x20, ARRAY_SIZE(norm_6x20));
+ break;
+ case SEN_OV6630:
+ case SEN_OV66308AF:
+ sd->ctrls[CONTRAST].def = 200;
+ /* The default is too low for the ov6630 */
+ write_i2c_regvals(sd, norm_6x30, ARRAY_SIZE(norm_6x30));
+ break;
+ default:
+/* case SEN_OV7610: */
+/* case SEN_OV76BE: */
+ write_i2c_regvals(sd, norm_7610, ARRAY_SIZE(norm_7610));
+ i2c_w_mask(sd, 0x0e, 0x00, 0x40);
+ break;
+ case SEN_OV7620:
+ case SEN_OV7620AE:
+ write_i2c_regvals(sd, norm_7620, ARRAY_SIZE(norm_7620));
+ break;
+ case SEN_OV7640:
+ case SEN_OV7648:
+ write_i2c_regvals(sd, norm_7640, ARRAY_SIZE(norm_7640));
+ break;
+ case SEN_OV7660:
+ i2c_w(sd, OV7670_R12_COM7, OV7670_COM7_RESET);
+ msleep(14);
+ reg_w(sd, OV519_R57_SNAPSHOT, 0x23);
+ write_regvals(sd, init_519_ov7660,
+ ARRAY_SIZE(init_519_ov7660));
+ write_i2c_regvals(sd, norm_7660, ARRAY_SIZE(norm_7660));
+ sd->gspca_dev.curr_mode = 1; /* 640x480 */
+ ov519_set_mode(sd);
+ ov519_set_fr(sd);
+ sd->ctrls[COLORS].max = 4; /* 0..4 */
+ sd->ctrls[COLORS].val =
+ sd->ctrls[COLORS].def = 2;
+ setcolors(gspca_dev);
+ sd->ctrls[CONTRAST].max = 6; /* 0..6 */
+ sd->ctrls[CONTRAST].val =
+ sd->ctrls[CONTRAST].def = 3;
+ setcontrast(gspca_dev);
+ sd->ctrls[BRIGHTNESS].max = 6; /* 0..6 */
+ sd->ctrls[BRIGHTNESS].val =
+ sd->ctrls[BRIGHTNESS].def = 3;
+ setbrightness(gspca_dev);
+ sd_reset_snapshot(gspca_dev);
+ ov51x_restart(sd);
+ ov51x_stop(sd); /* not in win traces */
+ ov51x_led_control(sd, 0);
+ break;
+ case SEN_OV7670:
+ sd->ctrls[FREQ].max = 3; /* auto */
+ sd->ctrls[FREQ].def = 3;
+ write_i2c_regvals(sd, norm_7670, ARRAY_SIZE(norm_7670));
+ break;
+ case SEN_OV8610:
+ write_i2c_regvals(sd, norm_8610, ARRAY_SIZE(norm_8610));
+ break;
+ case SEN_OV9600:
+ write_i2c_regvals(sd, norm_9600, ARRAY_SIZE(norm_9600));
+
+ /* enable autoexpo */
+/* i2c_w_mask(sd, 0x13, 0x05, 0x05); */
+ break;
+ }
+ return gspca_dev->usb_err;
+error:
+ PDEBUG(D_ERR, "OV519 Config failed");
+ return -EINVAL;
+}
+
+/* function called at start time before URB creation */
+static int sd_isoc_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->bridge) {
+ case BRIDGE_OVFX2:
+ if (gspca_dev->width != 800)
+ gspca_dev->cam.bulk_size = OVFX2_BULK_SIZE;
+ else
+ gspca_dev->cam.bulk_size = 7 * 4096;
+ break;
+ }
+ return 0;
+}
+
+/* Set up the OV511/OV511+ with the given image parameters.
+ *
+ * Do not put any sensor-specific code in here (including I2C I/O functions)
+ */
+static void ov511_mode_init_regs(struct sd *sd)
+{
+ int hsegs, vsegs, packet_size, fps, needed;
+ int interlaced = 0;
+ struct usb_host_interface *alt;
+ struct usb_interface *intf;
+
+ intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
+ alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
+ if (!alt) {
+ pr_err("Couldn't get altsetting\n");
+ sd->gspca_dev.usb_err = -EIO;
+ return;
+ }
+
+ packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+ reg_w(sd, R51x_FIFO_PSIZE, packet_size >> 5);
+
+ reg_w(sd, R511_CAM_UV_EN, 0x01);
+ reg_w(sd, R511_SNAP_UV_EN, 0x01);
+ reg_w(sd, R511_SNAP_OPTS, 0x03);
+
+ /* Here I'm assuming that snapshot size == image size.
+ * I hope that's always true. --claudio
+ */
+ hsegs = (sd->gspca_dev.width >> 3) - 1;
+ vsegs = (sd->gspca_dev.height >> 3) - 1;
+
+ reg_w(sd, R511_CAM_PXCNT, hsegs);
+ reg_w(sd, R511_CAM_LNCNT, vsegs);
+ reg_w(sd, R511_CAM_PXDIV, 0x00);
+ reg_w(sd, R511_CAM_LNDIV, 0x00);
+
+ /* YUV420, low pass filter on */
+ reg_w(sd, R511_CAM_OPTS, 0x03);
+
+ /* Snapshot additions */
+ reg_w(sd, R511_SNAP_PXCNT, hsegs);
+ reg_w(sd, R511_SNAP_LNCNT, vsegs);
+ reg_w(sd, R511_SNAP_PXDIV, 0x00);
+ reg_w(sd, R511_SNAP_LNDIV, 0x00);
+
+ /******** Set the framerate ********/
+ if (frame_rate > 0)
+ sd->frame_rate = frame_rate;
+
+ switch (sd->sensor) {
+ case SEN_OV6620:
+ /* No framerate control, doesn't like higher rates yet */
+ sd->clockdiv = 3;
+ break;
+
+ /* Note once the FIXME's in mode_init_ov_sensor_regs() are fixed
+ for more sensors we need to do this for them too */
+ case SEN_OV7620:
+ case SEN_OV7620AE:
+ case SEN_OV7640:
+ case SEN_OV7648:
+ case SEN_OV76BE:
+ if (sd->gspca_dev.width == 320)
+ interlaced = 1;
+ /* Fall through */
+ case SEN_OV6630:
+ case SEN_OV7610:
+ case SEN_OV7670:
+ switch (sd->frame_rate) {
+ case 30:
+ case 25:
+ /* Not enough bandwidth to do 640x480 @ 30 fps */
+ if (sd->gspca_dev.width != 640) {
+ sd->clockdiv = 0;
+ break;
+ }
+ /* Fall through for 640x480 case */
+ default:
+/* case 20: */
+/* case 15: */
+ sd->clockdiv = 1;
+ break;
+ case 10:
+ sd->clockdiv = 2;
+ break;
+ case 5:
+ sd->clockdiv = 5;
+ break;
+ }
+ if (interlaced) {
+ sd->clockdiv = (sd->clockdiv + 1) * 2 - 1;
+ /* Higher then 10 does not work */
+ if (sd->clockdiv > 10)
+ sd->clockdiv = 10;
+ }
+ break;
+
+ case SEN_OV8610:
+ /* No framerate control ?? */
+ sd->clockdiv = 0;
+ break;
+ }
+
+ /* Check if we have enough bandwidth to disable compression */
+ fps = (interlaced ? 60 : 30) / (sd->clockdiv + 1) + 1;
+ needed = fps * sd->gspca_dev.width * sd->gspca_dev.height * 3 / 2;
+ /* 1000 isoc packets/sec */
+ if (needed > 1000 * packet_size) {
+ /* Enable Y and UV quantization and compression */
+ reg_w(sd, R511_COMP_EN, 0x07);
+ reg_w(sd, R511_COMP_LUT_EN, 0x03);
+ } else {
+ reg_w(sd, R511_COMP_EN, 0x06);
+ reg_w(sd, R511_COMP_LUT_EN, 0x00);
+ }
+
+ reg_w(sd, R51x_SYS_RESET, OV511_RESET_OMNICE);
+ reg_w(sd, R51x_SYS_RESET, 0);
+}
+
+/* Sets up the OV518/OV518+ with the given image parameters
+ *
+ * OV518 needs a completely different approach, until we can figure out what
+ * the individual registers do. Also, only 15 FPS is supported now.
+ *
+ * Do not put any sensor-specific code in here (including I2C I/O functions)
+ */
+static void ov518_mode_init_regs(struct sd *sd)
+{
+ int hsegs, vsegs, packet_size;
+ struct usb_host_interface *alt;
+ struct usb_interface *intf;
+
+ intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
+ alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
+ if (!alt) {
+ pr_err("Couldn't get altsetting\n");
+ sd->gspca_dev.usb_err = -EIO;
+ return;
+ }
+
+ packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+ ov518_reg_w32(sd, R51x_FIFO_PSIZE, packet_size & ~7, 2);
+
+ /******** Set the mode ********/
+ reg_w(sd, 0x2b, 0);
+ reg_w(sd, 0x2c, 0);
+ reg_w(sd, 0x2d, 0);
+ reg_w(sd, 0x2e, 0);
+ reg_w(sd, 0x3b, 0);
+ reg_w(sd, 0x3c, 0);
+ reg_w(sd, 0x3d, 0);
+ reg_w(sd, 0x3e, 0);
+
+ if (sd->bridge == BRIDGE_OV518) {
+ /* Set 8-bit (YVYU) input format */
+ reg_w_mask(sd, 0x20, 0x08, 0x08);
+
+ /* Set 12-bit (4:2:0) output format */
+ reg_w_mask(sd, 0x28, 0x80, 0xf0);
+ reg_w_mask(sd, 0x38, 0x80, 0xf0);
+ } else {
+ reg_w(sd, 0x28, 0x80);
+ reg_w(sd, 0x38, 0x80);
+ }
+
+ hsegs = sd->gspca_dev.width / 16;
+ vsegs = sd->gspca_dev.height / 4;
+
+ reg_w(sd, 0x29, hsegs);
+ reg_w(sd, 0x2a, vsegs);
+
+ reg_w(sd, 0x39, hsegs);
+ reg_w(sd, 0x3a, vsegs);
+
+ /* Windows driver does this here; who knows why */
+ reg_w(sd, 0x2f, 0x80);
+
+ /******** Set the framerate ********/
+ sd->clockdiv = 1;
+
+ /* Mode independent, but framerate dependent, regs */
+ /* 0x51: Clock divider; Only works on some cams which use 2 crystals */
+ reg_w(sd, 0x51, 0x04);
+ reg_w(sd, 0x22, 0x18);
+ reg_w(sd, 0x23, 0xff);
+
+ if (sd->bridge == BRIDGE_OV518PLUS) {
+ switch (sd->sensor) {
+ case SEN_OV7620AE:
+ if (sd->gspca_dev.width == 320) {
+ reg_w(sd, 0x20, 0x00);
+ reg_w(sd, 0x21, 0x19);
+ } else {
+ reg_w(sd, 0x20, 0x60);
+ reg_w(sd, 0x21, 0x1f);
+ }
+ break;
+ case SEN_OV7620:
+ reg_w(sd, 0x20, 0x00);
+ reg_w(sd, 0x21, 0x19);
+ break;
+ default:
+ reg_w(sd, 0x21, 0x19);
+ }
+ } else
+ reg_w(sd, 0x71, 0x17); /* Compression-related? */
+
+ /* FIXME: Sensor-specific */
+ /* Bit 5 is what matters here. Of course, it is "reserved" */
+ i2c_w(sd, 0x54, 0x23);
+
+ reg_w(sd, 0x2f, 0x80);
+
+ if (sd->bridge == BRIDGE_OV518PLUS) {
+ reg_w(sd, 0x24, 0x94);
+ reg_w(sd, 0x25, 0x90);
+ ov518_reg_w32(sd, 0xc4, 400, 2); /* 190h */
+ ov518_reg_w32(sd, 0xc6, 540, 2); /* 21ch */
+ ov518_reg_w32(sd, 0xc7, 540, 2); /* 21ch */
+ ov518_reg_w32(sd, 0xc8, 108, 2); /* 6ch */
+ ov518_reg_w32(sd, 0xca, 131098, 3); /* 2001ah */
+ ov518_reg_w32(sd, 0xcb, 532, 2); /* 214h */
+ ov518_reg_w32(sd, 0xcc, 2400, 2); /* 960h */
+ ov518_reg_w32(sd, 0xcd, 32, 2); /* 20h */
+ ov518_reg_w32(sd, 0xce, 608, 2); /* 260h */
+ } else {
+ reg_w(sd, 0x24, 0x9f);
+ reg_w(sd, 0x25, 0x90);
+ ov518_reg_w32(sd, 0xc4, 400, 2); /* 190h */
+ ov518_reg_w32(sd, 0xc6, 381, 2); /* 17dh */
+ ov518_reg_w32(sd, 0xc7, 381, 2); /* 17dh */
+ ov518_reg_w32(sd, 0xc8, 128, 2); /* 80h */
+ ov518_reg_w32(sd, 0xca, 183331, 3); /* 2cc23h */
+ ov518_reg_w32(sd, 0xcb, 746, 2); /* 2eah */
+ ov518_reg_w32(sd, 0xcc, 1750, 2); /* 6d6h */
+ ov518_reg_w32(sd, 0xcd, 45, 2); /* 2dh */
+ ov518_reg_w32(sd, 0xce, 851, 2); /* 353h */
+ }
+
+ reg_w(sd, 0x2f, 0x80);
+}
+
+/* Sets up the OV519 with the given image parameters
+ *
+ * OV519 needs a completely different approach, until we can figure out what
+ * the individual registers do.
+ *
+ * Do not put any sensor-specific code in here (including I2C I/O functions)
+ */
+static void ov519_mode_init_regs(struct sd *sd)
+{
+ static const struct ov_regvals mode_init_519_ov7670[] = {
+ { 0x5d, 0x03 }, /* Turn off suspend mode */
+ { 0x53, 0x9f }, /* was 9b in 1.65-1.08 */
+ { OV519_R54_EN_CLK1, 0x0f }, /* bit2 (jpeg enable) */
+ { 0xa2, 0x20 }, /* a2-a5 are undocumented */
+ { 0xa3, 0x18 },
+ { 0xa4, 0x04 },
+ { 0xa5, 0x28 },
+ { 0x37, 0x00 }, /* SetUsbInit */
+ { 0x55, 0x02 }, /* 4.096 Mhz audio clock */
+ /* Enable both fields, YUV Input, disable defect comp (why?) */
+ { 0x20, 0x0c },
+ { 0x21, 0x38 },
+ { 0x22, 0x1d },
+ { 0x17, 0x50 }, /* undocumented */
+ { 0x37, 0x00 }, /* undocumented */
+ { 0x40, 0xff }, /* I2C timeout counter */
+ { 0x46, 0x00 }, /* I2C clock prescaler */
+ { 0x59, 0x04 }, /* new from windrv 090403 */
+ { 0xff, 0x00 }, /* undocumented */
+ /* windows reads 0x55 at this point, why? */
+ };
+
+ static const struct ov_regvals mode_init_519[] = {
+ { 0x5d, 0x03 }, /* Turn off suspend mode */
+ { 0x53, 0x9f }, /* was 9b in 1.65-1.08 */
+ { OV519_R54_EN_CLK1, 0x0f }, /* bit2 (jpeg enable) */
+ { 0xa2, 0x20 }, /* a2-a5 are undocumented */
+ { 0xa3, 0x18 },
+ { 0xa4, 0x04 },
+ { 0xa5, 0x28 },
+ { 0x37, 0x00 }, /* SetUsbInit */
+ { 0x55, 0x02 }, /* 4.096 Mhz audio clock */
+ /* Enable both fields, YUV Input, disable defect comp (why?) */
+ { 0x22, 0x1d },
+ { 0x17, 0x50 }, /* undocumented */
+ { 0x37, 0x00 }, /* undocumented */
+ { 0x40, 0xff }, /* I2C timeout counter */
+ { 0x46, 0x00 }, /* I2C clock prescaler */
+ { 0x59, 0x04 }, /* new from windrv 090403 */
+ { 0xff, 0x00 }, /* undocumented */
+ /* windows reads 0x55 at this point, why? */
+ };
+
+ /******** Set the mode ********/
+ switch (sd->sensor) {
+ default:
+ write_regvals(sd, mode_init_519, ARRAY_SIZE(mode_init_519));
+ if (sd->sensor == SEN_OV7640 ||
+ sd->sensor == SEN_OV7648) {
+ /* Select 8-bit input mode */
+ reg_w_mask(sd, OV519_R20_DFR, 0x10, 0x10);
+ }
+ break;
+ case SEN_OV7660:
+ return; /* done by ov519_set_mode/fr() */
+ case SEN_OV7670:
+ write_regvals(sd, mode_init_519_ov7670,
+ ARRAY_SIZE(mode_init_519_ov7670));
+ break;
+ }
+
+ reg_w(sd, OV519_R10_H_SIZE, sd->gspca_dev.width >> 4);
+ reg_w(sd, OV519_R11_V_SIZE, sd->gspca_dev.height >> 3);
+ if (sd->sensor == SEN_OV7670 &&
+ sd->gspca_dev.cam.cam_mode[sd->gspca_dev.curr_mode].priv)
+ reg_w(sd, OV519_R12_X_OFFSETL, 0x04);
+ else if (sd->sensor == SEN_OV7648 &&
+ sd->gspca_dev.cam.cam_mode[sd->gspca_dev.curr_mode].priv)
+ reg_w(sd, OV519_R12_X_OFFSETL, 0x01);
+ else
+ reg_w(sd, OV519_R12_X_OFFSETL, 0x00);
+ reg_w(sd, OV519_R13_X_OFFSETH, 0x00);
+ reg_w(sd, OV519_R14_Y_OFFSETL, 0x00);
+ reg_w(sd, OV519_R15_Y_OFFSETH, 0x00);
+ reg_w(sd, OV519_R16_DIVIDER, 0x00);
+ reg_w(sd, OV519_R25_FORMAT, 0x03); /* YUV422 */
+ reg_w(sd, 0x26, 0x00); /* Undocumented */
+
+ /******** Set the framerate ********/
+ if (frame_rate > 0)
+ sd->frame_rate = frame_rate;
+
+/* FIXME: These are only valid at the max resolution. */
+ sd->clockdiv = 0;
+ switch (sd->sensor) {
+ case SEN_OV7640:
+ case SEN_OV7648:
+ switch (sd->frame_rate) {
+ default:
+/* case 30: */
+ reg_w(sd, 0xa4, 0x0c);
+ reg_w(sd, 0x23, 0xff);
+ break;
+ case 25:
+ reg_w(sd, 0xa4, 0x0c);
+ reg_w(sd, 0x23, 0x1f);
+ break;
+ case 20:
+ reg_w(sd, 0xa4, 0x0c);
+ reg_w(sd, 0x23, 0x1b);
+ break;
+ case 15:
+ reg_w(sd, 0xa4, 0x04);
+ reg_w(sd, 0x23, 0xff);
+ sd->clockdiv = 1;
+ break;
+ case 10:
+ reg_w(sd, 0xa4, 0x04);
+ reg_w(sd, 0x23, 0x1f);
+ sd->clockdiv = 1;
+ break;
+ case 5:
+ reg_w(sd, 0xa4, 0x04);
+ reg_w(sd, 0x23, 0x1b);
+ sd->clockdiv = 1;
+ break;
+ }
+ break;
+ case SEN_OV8610:
+ switch (sd->frame_rate) {
+ default: /* 15 fps */
+/* case 15: */
+ reg_w(sd, 0xa4, 0x06);
+ reg_w(sd, 0x23, 0xff);
+ break;
+ case 10:
+ reg_w(sd, 0xa4, 0x06);
+ reg_w(sd, 0x23, 0x1f);
+ break;
+ case 5:
+ reg_w(sd, 0xa4, 0x06);
+ reg_w(sd, 0x23, 0x1b);
+ break;
+ }
+ break;
+ case SEN_OV7670: /* guesses, based on 7640 */
+ PDEBUG(D_STREAM, "Setting framerate to %d fps",
+ (sd->frame_rate == 0) ? 15 : sd->frame_rate);
+ reg_w(sd, 0xa4, 0x10);
+ switch (sd->frame_rate) {
+ case 30:
+ reg_w(sd, 0x23, 0xff);
+ break;
+ case 20:
+ reg_w(sd, 0x23, 0x1b);
+ break;
+ default:
+/* case 15: */
+ reg_w(sd, 0x23, 0xff);
+ sd->clockdiv = 1;
+ break;
+ }
+ break;
+ }
+}
+
+static void mode_init_ov_sensor_regs(struct sd *sd)
+{
+ struct gspca_dev *gspca_dev;
+ int qvga, xstart, xend, ystart, yend;
+ u8 v;
+
+ gspca_dev = &sd->gspca_dev;
+ qvga = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv & 1;
+
+ /******** Mode (VGA/QVGA) and sensor specific regs ********/
+ switch (sd->sensor) {
+ case SEN_OV2610:
+ i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+ i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20);
+ i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a);
+ i2c_w(sd, 0x25, qvga ? 0x30 : 0x60);
+ i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40);
+ i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0);
+ i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20);
+ return;
+ case SEN_OV2610AE: {
+ u8 v;
+
+ /* frame rates:
+ * 10fps / 5 fps for 1600x1200
+ * 40fps / 20fps for 800x600
+ */
+ v = 80;
+ if (qvga) {
+ if (sd->frame_rate < 25)
+ v = 0x81;
+ } else {
+ if (sd->frame_rate < 10)
+ v = 0x81;
+ }
+ i2c_w(sd, 0x11, v);
+ i2c_w(sd, 0x12, qvga ? 0x60 : 0x20);
+ return;
+ }
+ case SEN_OV3610:
+ if (qvga) {
+ xstart = (1040 - gspca_dev->width) / 2 + (0x1f << 4);
+ ystart = (776 - gspca_dev->height) / 2;
+ } else {
+ xstart = (2076 - gspca_dev->width) / 2 + (0x10 << 4);
+ ystart = (1544 - gspca_dev->height) / 2;
+ }
+ xend = xstart + gspca_dev->width;
+ yend = ystart + gspca_dev->height;
+ /* Writing to the COMH register resets the other windowing regs
+ to their default values, so we must do this first. */
+ i2c_w_mask(sd, 0x12, qvga ? 0x40 : 0x00, 0xf0);
+ i2c_w_mask(sd, 0x32,
+ (((xend >> 1) & 7) << 3) | ((xstart >> 1) & 7),
+ 0x3f);
+ i2c_w_mask(sd, 0x03,
+ (((yend >> 1) & 3) << 2) | ((ystart >> 1) & 3),
+ 0x0f);
+ i2c_w(sd, 0x17, xstart >> 4);
+ i2c_w(sd, 0x18, xend >> 4);
+ i2c_w(sd, 0x19, ystart >> 3);
+ i2c_w(sd, 0x1a, yend >> 3);
+ return;
+ case SEN_OV8610:
+ /* For OV8610 qvga means qsvga */
+ i2c_w_mask(sd, OV7610_REG_COM_C, qvga ? (1 << 5) : 0, 1 << 5);
+ i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
+ i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
+ i2c_w_mask(sd, 0x2d, 0x00, 0x40); /* from windrv 090403 */
+ i2c_w_mask(sd, 0x28, 0x20, 0x20); /* progressive mode on */
+ break;
+ case SEN_OV7610:
+ i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+ i2c_w(sd, 0x35, qvga ? 0x1e : 0x9e);
+ i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
+ i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
+ break;
+ case SEN_OV7620:
+ case SEN_OV7620AE:
+ case SEN_OV76BE:
+ i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+ i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20);
+ i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a);
+ i2c_w(sd, 0x25, qvga ? 0x30 : 0x60);
+ i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40);
+ i2c_w_mask(sd, 0x67, qvga ? 0xb0 : 0x90, 0xf0);
+ i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20);
+ i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
+ i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
+ if (sd->sensor == SEN_OV76BE)
+ i2c_w(sd, 0x35, qvga ? 0x1e : 0x9e);
+ break;
+ case SEN_OV7640:
+ case SEN_OV7648:
+ i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+ i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20);
+ /* Setting this undocumented bit in qvga mode removes a very
+ annoying vertical shaking of the image */
+ i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40);
+ /* Unknown */
+ i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0);
+ /* Allow higher automatic gain (to allow higher framerates) */
+ i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20);
+ i2c_w_mask(sd, 0x12, 0x04, 0x04); /* AWB: 1 */
+ break;
+ case SEN_OV7670:
+ /* set COM7_FMT_VGA or COM7_FMT_QVGA
+ * do we need to set anything else?
+ * HSTART etc are set in set_ov_sensor_window itself */
+ i2c_w_mask(sd, OV7670_R12_COM7,
+ qvga ? OV7670_COM7_FMT_QVGA : OV7670_COM7_FMT_VGA,
+ OV7670_COM7_FMT_MASK);
+ i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
+ i2c_w_mask(sd, OV7670_R13_COM8, OV7670_COM8_AWB,
+ OV7670_COM8_AWB);
+ if (qvga) { /* QVGA from ov7670.c by
+ * Jonathan Corbet */
+ xstart = 164;
+ xend = 28;
+ ystart = 14;
+ yend = 494;
+ } else { /* VGA */
+ xstart = 158;
+ xend = 14;
+ ystart = 10;
+ yend = 490;
+ }
+ /* OV7670 hardware window registers are split across
+ * multiple locations */
+ i2c_w(sd, OV7670_R17_HSTART, xstart >> 3);
+ i2c_w(sd, OV7670_R18_HSTOP, xend >> 3);
+ v = i2c_r(sd, OV7670_R32_HREF);
+ v = (v & 0xc0) | ((xend & 0x7) << 3) | (xstart & 0x07);
+ msleep(10); /* need to sleep between read and write to
+ * same reg! */
+ i2c_w(sd, OV7670_R32_HREF, v);
+
+ i2c_w(sd, OV7670_R19_VSTART, ystart >> 2);
+ i2c_w(sd, OV7670_R1A_VSTOP, yend >> 2);
+ v = i2c_r(sd, OV7670_R03_VREF);
+ v = (v & 0xc0) | ((yend & 0x3) << 2) | (ystart & 0x03);
+ msleep(10); /* need to sleep between read and write to
+ * same reg! */
+ i2c_w(sd, OV7670_R03_VREF, v);
+ break;
+ case SEN_OV6620:
+ i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+ i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
+ i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
+ break;
+ case SEN_OV6630:
+ case SEN_OV66308AF:
+ i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+ i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
+ break;
+ case SEN_OV9600: {
+ const struct ov_i2c_regvals *vals;
+ static const struct ov_i2c_regvals sxga_15[] = {
+ {0x11, 0x80}, {0x14, 0x3e}, {0x24, 0x85}, {0x25, 0x75}
+ };
+ static const struct ov_i2c_regvals sxga_7_5[] = {
+ {0x11, 0x81}, {0x14, 0x3e}, {0x24, 0x85}, {0x25, 0x75}
+ };
+ static const struct ov_i2c_regvals vga_30[] = {
+ {0x11, 0x81}, {0x14, 0x7e}, {0x24, 0x70}, {0x25, 0x60}
+ };
+ static const struct ov_i2c_regvals vga_15[] = {
+ {0x11, 0x83}, {0x14, 0x3e}, {0x24, 0x80}, {0x25, 0x70}
+ };
+
+ /* frame rates:
+ * 15fps / 7.5 fps for 1280x1024
+ * 30fps / 15fps for 640x480
+ */
+ i2c_w_mask(sd, 0x12, qvga ? 0x40 : 0x00, 0x40);
+ if (qvga)
+ vals = sd->frame_rate < 30 ? vga_15 : vga_30;
+ else
+ vals = sd->frame_rate < 15 ? sxga_7_5 : sxga_15;
+ write_i2c_regvals(sd, vals, ARRAY_SIZE(sxga_15));
+ return;
+ }
+ default:
+ return;
+ }
+
+ /******** Clock programming ********/
+ i2c_w(sd, 0x11, sd->clockdiv);
+}
+
+/* this function works for bridge ov519 and sensors ov7660 and ov7670 only */
+static void sethvflip(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->gspca_dev.streaming)
+ reg_w(sd, OV519_R51_RESET1, 0x0f); /* block stream */
+ i2c_w_mask(sd, OV7670_R1E_MVFP,
+ OV7670_MVFP_MIRROR * sd->ctrls[HFLIP].val
+ | OV7670_MVFP_VFLIP * sd->ctrls[VFLIP].val,
+ OV7670_MVFP_MIRROR | OV7670_MVFP_VFLIP);
+ if (sd->gspca_dev.streaming)
+ reg_w(sd, OV519_R51_RESET1, 0x00); /* restart stream */
+}
+
+static void set_ov_sensor_window(struct sd *sd)
+{
+ struct gspca_dev *gspca_dev;
+ int qvga, crop;
+ int hwsbase, hwebase, vwsbase, vwebase, hwscale, vwscale;
+
+ /* mode setup is fully handled in mode_init_ov_sensor_regs for these */
+ switch (sd->sensor) {
+ case SEN_OV2610:
+ case SEN_OV2610AE:
+ case SEN_OV3610:
+ case SEN_OV7670:
+ case SEN_OV9600:
+ mode_init_ov_sensor_regs(sd);
+ return;
+ case SEN_OV7660:
+ ov519_set_mode(sd);
+ ov519_set_fr(sd);
+ return;
+ }
+
+ gspca_dev = &sd->gspca_dev;
+ qvga = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv & 1;
+ crop = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv & 2;
+
+ /* The different sensor ICs handle setting up of window differently.
+ * IF YOU SET IT WRONG, YOU WILL GET ALL ZERO ISOC DATA FROM OV51x!! */
+ switch (sd->sensor) {
+ case SEN_OV8610:
+ hwsbase = 0x1e;
+ hwebase = 0x1e;
+ vwsbase = 0x02;
+ vwebase = 0x02;
+ break;
+ case SEN_OV7610:
+ case SEN_OV76BE:
+ hwsbase = 0x38;
+ hwebase = 0x3a;
+ vwsbase = vwebase = 0x05;
+ break;
+ case SEN_OV6620:
+ case SEN_OV6630:
+ case SEN_OV66308AF:
+ hwsbase = 0x38;
+ hwebase = 0x3a;
+ vwsbase = 0x05;
+ vwebase = 0x06;
+ if (sd->sensor == SEN_OV66308AF && qvga)
+ /* HDG: this fixes U and V getting swapped */
+ hwsbase++;
+ if (crop) {
+ hwsbase += 8;
+ hwebase += 8;
+ vwsbase += 11;
+ vwebase += 11;
+ }
+ break;
+ case SEN_OV7620:
+ case SEN_OV7620AE:
+ hwsbase = 0x2f; /* From 7620.SET (spec is wrong) */
+ hwebase = 0x2f;
+ vwsbase = vwebase = 0x05;
+ break;
+ case SEN_OV7640:
+ case SEN_OV7648:
+ hwsbase = 0x1a;
+ hwebase = 0x1a;
+ vwsbase = vwebase = 0x03;
+ break;
+ default:
+ return;
+ }
+
+ switch (sd->sensor) {
+ case SEN_OV6620:
+ case SEN_OV6630:
+ case SEN_OV66308AF:
+ if (qvga) { /* QCIF */
+ hwscale = 0;
+ vwscale = 0;
+ } else { /* CIF */
+ hwscale = 1;
+ vwscale = 1; /* The datasheet says 0;
+ * it's wrong */
+ }
+ break;
+ case SEN_OV8610:
+ if (qvga) { /* QSVGA */
+ hwscale = 1;
+ vwscale = 1;
+ } else { /* SVGA */
+ hwscale = 2;
+ vwscale = 2;
+ }
+ break;
+ default: /* SEN_OV7xx0 */
+ if (qvga) { /* QVGA */
+ hwscale = 1;
+ vwscale = 0;
+ } else { /* VGA */
+ hwscale = 2;
+ vwscale = 1;
+ }
+ }
+
+ mode_init_ov_sensor_regs(sd);
+
+ i2c_w(sd, 0x17, hwsbase);
+ i2c_w(sd, 0x18, hwebase + (sd->sensor_width >> hwscale));
+ i2c_w(sd, 0x19, vwsbase);
+ i2c_w(sd, 0x1a, vwebase + (sd->sensor_height >> vwscale));
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* Default for most bridges, allow bridge_mode_init_regs to override */
+ sd->sensor_width = sd->gspca_dev.width;
+ sd->sensor_height = sd->gspca_dev.height;
+
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ ov511_mode_init_regs(sd);
+ break;
+ case BRIDGE_OV518:
+ case BRIDGE_OV518PLUS:
+ ov518_mode_init_regs(sd);
+ break;
+ case BRIDGE_OV519:
+ ov519_mode_init_regs(sd);
+ break;
+ /* case BRIDGE_OVFX2: nothing to do */
+ case BRIDGE_W9968CF:
+ w9968cf_mode_init_regs(sd);
+ break;
+ }
+
+ set_ov_sensor_window(sd);
+
+ if (!(sd->gspca_dev.ctrl_dis & (1 << CONTRAST)))
+ setcontrast(gspca_dev);
+ if (!(sd->gspca_dev.ctrl_dis & (1 << BRIGHTNESS)))
+ setbrightness(gspca_dev);
+ if (!(sd->gspca_dev.ctrl_dis & (1 << EXPOSURE)))
+ setexposure(gspca_dev);
+ if (!(sd->gspca_dev.ctrl_dis & (1 << COLORS)))
+ setcolors(gspca_dev);
+ if (!(sd->gspca_dev.ctrl_dis & ((1 << HFLIP) | (1 << VFLIP))))
+ sethvflip(gspca_dev);
+ if (!(sd->gspca_dev.ctrl_dis & (1 << AUTOBRIGHT)))
+ setautobright(gspca_dev);
+ if (!(sd->gspca_dev.ctrl_dis & (1 << AUTOGAIN)))
+ setautogain(gspca_dev);
+ if (!(sd->gspca_dev.ctrl_dis & (1 << FREQ)))
+ setfreq_i(sd);
+
+ /* Force clear snapshot state in case the snapshot button was
+ pressed while we weren't streaming */
+ sd->snapshot_needs_reset = 1;
+ sd_reset_snapshot(gspca_dev);
+
+ sd->first_frame = 3;
+
+ ov51x_restart(sd);
+ ov51x_led_control(sd, 1);
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ ov51x_stop(sd);
+ ov51x_led_control(sd, 0);
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (!sd->gspca_dev.present)
+ return;
+ if (sd->bridge == BRIDGE_W9968CF)
+ w9968cf_stop0(sd);
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ /* If the last button state is pressed, release it now! */
+ if (sd->snapshot_pressed) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ sd->snapshot_pressed = 0;
+ }
+#endif
+ if (sd->bridge == BRIDGE_OV519)
+ reg_w(sd, OV519_R57_SNAPSHOT, 0x23);
+}
+
+static void ov51x_handle_button(struct gspca_dev *gspca_dev, u8 state)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->snapshot_pressed != state) {
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, state);
+ input_sync(gspca_dev->input_dev);
+#endif
+ if (state)
+ sd->snapshot_needs_reset = 1;
+
+ sd->snapshot_pressed = state;
+ } else {
+ /* On the ov511 / ov519 we need to reset the button state
+ multiple times, as resetting does not work as long as the
+ button stays pressed */
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ case BRIDGE_OV519:
+ if (state)
+ sd->snapshot_needs_reset = 1;
+ break;
+ }
+ }
+}
+
+static void ov511_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *in, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* SOF/EOF packets have 1st to 8th bytes zeroed and the 9th
+ * byte non-zero. The EOF packet has image width/height in the
+ * 10th and 11th bytes. The 9th byte is given as follows:
+ *
+ * bit 7: EOF
+ * 6: compression enabled
+ * 5: 422/420/400 modes
+ * 4: 422/420/400 modes
+ * 3: 1
+ * 2: snapshot button on
+ * 1: snapshot frame
+ * 0: even/odd field
+ */
+ if (!(in[0] | in[1] | in[2] | in[3] | in[4] | in[5] | in[6] | in[7]) &&
+ (in[8] & 0x08)) {
+ ov51x_handle_button(gspca_dev, (in[8] >> 2) & 1);
+ if (in[8] & 0x80) {
+ /* Frame end */
+ if ((in[9] + 1) * 8 != gspca_dev->width ||
+ (in[10] + 1) * 8 != gspca_dev->height) {
+ PDEBUG(D_ERR, "Invalid frame size, got: %dx%d,"
+ " requested: %dx%d\n",
+ (in[9] + 1) * 8, (in[10] + 1) * 8,
+ gspca_dev->width, gspca_dev->height);
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
+ }
+ /* Add 11 byte footer to frame, might be useful */
+ gspca_frame_add(gspca_dev, LAST_PACKET, in, 11);
+ return;
+ } else {
+ /* Frame start */
+ gspca_frame_add(gspca_dev, FIRST_PACKET, in, 0);
+ sd->packet_nr = 0;
+ }
+ }
+
+ /* Ignore the packet number */
+ len--;
+
+ /* intermediate packet */
+ gspca_frame_add(gspca_dev, INTER_PACKET, in, len);
+}
+
+static void ov518_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* A false positive here is likely, until OVT gives me
+ * the definitive SOF/EOF format */
+ if ((!(data[0] | data[1] | data[2] | data[3] | data[5])) && data[6]) {
+ ov51x_handle_button(gspca_dev, (data[6] >> 1) & 1);
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+ gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+ sd->packet_nr = 0;
+ }
+
+ if (gspca_dev->last_packet_type == DISCARD_PACKET)
+ return;
+
+ /* Does this device use packet numbers ? */
+ if (len & 7) {
+ len--;
+ if (sd->packet_nr == data[len])
+ sd->packet_nr++;
+ /* The last few packets of the frame (which are all 0's
+ except that they may contain part of the footer), are
+ numbered 0 */
+ else if (sd->packet_nr == 0 || data[len]) {
+ PDEBUG(D_ERR, "Invalid packet nr: %d (expect: %d)",
+ (int)data[len], (int)sd->packet_nr);
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
+ }
+ }
+
+ /* intermediate packet */
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static void ov519_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ /* Header of ov519 is 16 bytes:
+ * Byte Value Description
+ * 0 0xff magic
+ * 1 0xff magic
+ * 2 0xff magic
+ * 3 0xXX 0x50 = SOF, 0x51 = EOF
+ * 9 0xXX 0x01 initial frame without data,
+ * 0x00 standard frame with image
+ * 14 Lo in EOF: length of image data / 8
+ * 15 Hi
+ */
+
+ if (data[0] == 0xff && data[1] == 0xff && data[2] == 0xff) {
+ switch (data[3]) {
+ case 0x50: /* start of frame */
+ /* Don't check the button state here, as the state
+ usually (always ?) changes at EOF and checking it
+ here leads to unnecessary snapshot state resets. */
+#define HDRSZ 16
+ data += HDRSZ;
+ len -= HDRSZ;
+#undef HDRSZ
+ if (data[0] == 0xff || data[1] == 0xd8)
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ data, len);
+ else
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
+ case 0x51: /* end of frame */
+ ov51x_handle_button(gspca_dev, data[11] & 1);
+ if (data[9] != 0)
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ NULL, 0);
+ return;
+ }
+ }
+
+ /* intermediate packet */
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static void ovfx2_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+
+ /* A short read signals EOF */
+ if (len < gspca_dev->cam.bulk_size) {
+ /* If the frame is short, and it is one of the first ones
+ the sensor and bridge are still syncing, so drop it. */
+ if (sd->first_frame) {
+ sd->first_frame--;
+ if (gspca_dev->image_len <
+ sd->gspca_dev.width * sd->gspca_dev.height)
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ }
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+ gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+ }
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ ov511_pkt_scan(gspca_dev, data, len);
+ break;
+ case BRIDGE_OV518:
+ case BRIDGE_OV518PLUS:
+ ov518_pkt_scan(gspca_dev, data, len);
+ break;
+ case BRIDGE_OV519:
+ ov519_pkt_scan(gspca_dev, data, len);
+ break;
+ case BRIDGE_OVFX2:
+ ovfx2_pkt_scan(gspca_dev, data, len);
+ break;
+ case BRIDGE_W9968CF:
+ w9968cf_pkt_scan(gspca_dev, data, len);
+ break;
+ }
+}
+
+/* -- management routines -- */
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int val;
+ static const struct ov_i2c_regvals brit_7660[][7] = {
+ {{0x0f, 0x6a}, {0x24, 0x40}, {0x25, 0x2b}, {0x26, 0x90},
+ {0x27, 0xe0}, {0x28, 0xe0}, {0x2c, 0xe0}},
+ {{0x0f, 0x6a}, {0x24, 0x50}, {0x25, 0x40}, {0x26, 0xa1},
+ {0x27, 0xc0}, {0x28, 0xc0}, {0x2c, 0xc0}},
+ {{0x0f, 0x6a}, {0x24, 0x68}, {0x25, 0x58}, {0x26, 0xc2},
+ {0x27, 0xa0}, {0x28, 0xa0}, {0x2c, 0xa0}},
+ {{0x0f, 0x6a}, {0x24, 0x70}, {0x25, 0x68}, {0x26, 0xd3},
+ {0x27, 0x80}, {0x28, 0x80}, {0x2c, 0x80}},
+ {{0x0f, 0x6a}, {0x24, 0x80}, {0x25, 0x70}, {0x26, 0xd3},
+ {0x27, 0x20}, {0x28, 0x20}, {0x2c, 0x20}},
+ {{0x0f, 0x6a}, {0x24, 0x88}, {0x25, 0x78}, {0x26, 0xd3},
+ {0x27, 0x40}, {0x28, 0x40}, {0x2c, 0x40}},
+ {{0x0f, 0x6a}, {0x24, 0x90}, {0x25, 0x80}, {0x26, 0xd4},
+ {0x27, 0x60}, {0x28, 0x60}, {0x2c, 0x60}}
+ };
+
+ val = sd->ctrls[BRIGHTNESS].val;
+ switch (sd->sensor) {
+ case SEN_OV8610:
+ case SEN_OV7610:
+ case SEN_OV76BE:
+ case SEN_OV6620:
+ case SEN_OV6630:
+ case SEN_OV66308AF:
+ case SEN_OV7640:
+ case SEN_OV7648:
+ i2c_w(sd, OV7610_REG_BRT, val);
+ break;
+ case SEN_OV7620:
+ case SEN_OV7620AE:
+ /* 7620 doesn't like manual changes when in auto mode */
+ if (!sd->ctrls[AUTOBRIGHT].val)
+ i2c_w(sd, OV7610_REG_BRT, val);
+ break;
+ case SEN_OV7660:
+ write_i2c_regvals(sd, brit_7660[val],
+ ARRAY_SIZE(brit_7660[0]));
+ break;
+ case SEN_OV7670:
+/*win trace
+ * i2c_w_mask(sd, OV7670_R13_COM8, 0, OV7670_COM8_AEC); */
+ i2c_w(sd, OV7670_R55_BRIGHT, ov7670_abs_to_sm(val));
+ break;
+ }
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int val;
+ static const struct ov_i2c_regvals contrast_7660[][31] = {
+ {{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf8}, {0x6f, 0xa0},
+ {0x70, 0x58}, {0x71, 0x38}, {0x72, 0x30}, {0x73, 0x30},
+ {0x74, 0x28}, {0x75, 0x28}, {0x76, 0x24}, {0x77, 0x24},
+ {0x78, 0x22}, {0x79, 0x28}, {0x7a, 0x2a}, {0x7b, 0x34},
+ {0x7c, 0x0f}, {0x7d, 0x1e}, {0x7e, 0x3d}, {0x7f, 0x65},
+ {0x80, 0x70}, {0x81, 0x77}, {0x82, 0x7d}, {0x83, 0x83},
+ {0x84, 0x88}, {0x85, 0x8d}, {0x86, 0x96}, {0x87, 0x9f},
+ {0x88, 0xb0}, {0x89, 0xc4}, {0x8a, 0xd9}},
+ {{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf8}, {0x6f, 0x94},
+ {0x70, 0x58}, {0x71, 0x40}, {0x72, 0x30}, {0x73, 0x30},
+ {0x74, 0x30}, {0x75, 0x30}, {0x76, 0x2c}, {0x77, 0x24},
+ {0x78, 0x22}, {0x79, 0x28}, {0x7a, 0x2a}, {0x7b, 0x31},
+ {0x7c, 0x0f}, {0x7d, 0x1e}, {0x7e, 0x3d}, {0x7f, 0x62},
+ {0x80, 0x6d}, {0x81, 0x75}, {0x82, 0x7b}, {0x83, 0x81},
+ {0x84, 0x87}, {0x85, 0x8d}, {0x86, 0x98}, {0x87, 0xa1},
+ {0x88, 0xb2}, {0x89, 0xc6}, {0x8a, 0xdb}},
+ {{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf0}, {0x6f, 0x84},
+ {0x70, 0x58}, {0x71, 0x48}, {0x72, 0x40}, {0x73, 0x40},
+ {0x74, 0x28}, {0x75, 0x28}, {0x76, 0x28}, {0x77, 0x24},
+ {0x78, 0x26}, {0x79, 0x28}, {0x7a, 0x28}, {0x7b, 0x34},
+ {0x7c, 0x0f}, {0x7d, 0x1e}, {0x7e, 0x3c}, {0x7f, 0x5d},
+ {0x80, 0x68}, {0x81, 0x71}, {0x82, 0x79}, {0x83, 0x81},
+ {0x84, 0x86}, {0x85, 0x8b}, {0x86, 0x95}, {0x87, 0x9e},
+ {0x88, 0xb1}, {0x89, 0xc5}, {0x8a, 0xd9}},
+ {{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf0}, {0x6f, 0x70},
+ {0x70, 0x58}, {0x71, 0x58}, {0x72, 0x48}, {0x73, 0x48},
+ {0x74, 0x38}, {0x75, 0x40}, {0x76, 0x34}, {0x77, 0x34},
+ {0x78, 0x2e}, {0x79, 0x28}, {0x7a, 0x24}, {0x7b, 0x22},
+ {0x7c, 0x0f}, {0x7d, 0x1e}, {0x7e, 0x3c}, {0x7f, 0x58},
+ {0x80, 0x63}, {0x81, 0x6e}, {0x82, 0x77}, {0x83, 0x80},
+ {0x84, 0x87}, {0x85, 0x8f}, {0x86, 0x9c}, {0x87, 0xa9},
+ {0x88, 0xc0}, {0x89, 0xd4}, {0x8a, 0xe6}},
+ {{0x6c, 0xa0}, {0x6d, 0xf0}, {0x6e, 0x90}, {0x6f, 0x80},
+ {0x70, 0x70}, {0x71, 0x80}, {0x72, 0x60}, {0x73, 0x60},
+ {0x74, 0x58}, {0x75, 0x60}, {0x76, 0x4c}, {0x77, 0x38},
+ {0x78, 0x38}, {0x79, 0x2a}, {0x7a, 0x20}, {0x7b, 0x0e},
+ {0x7c, 0x0a}, {0x7d, 0x14}, {0x7e, 0x26}, {0x7f, 0x46},
+ {0x80, 0x54}, {0x81, 0x64}, {0x82, 0x70}, {0x83, 0x7c},
+ {0x84, 0x87}, {0x85, 0x93}, {0x86, 0xa6}, {0x87, 0xb4},
+ {0x88, 0xd0}, {0x89, 0xe5}, {0x8a, 0xf5}},
+ {{0x6c, 0x60}, {0x6d, 0x80}, {0x6e, 0x60}, {0x6f, 0x80},
+ {0x70, 0x80}, {0x71, 0x80}, {0x72, 0x88}, {0x73, 0x30},
+ {0x74, 0x70}, {0x75, 0x68}, {0x76, 0x64}, {0x77, 0x50},
+ {0x78, 0x3c}, {0x79, 0x22}, {0x7a, 0x10}, {0x7b, 0x08},
+ {0x7c, 0x06}, {0x7d, 0x0e}, {0x7e, 0x1a}, {0x7f, 0x3a},
+ {0x80, 0x4a}, {0x81, 0x5a}, {0x82, 0x6b}, {0x83, 0x7b},
+ {0x84, 0x89}, {0x85, 0x96}, {0x86, 0xaf}, {0x87, 0xc3},
+ {0x88, 0xe1}, {0x89, 0xf2}, {0x8a, 0xfa}},
+ {{0x6c, 0x20}, {0x6d, 0x40}, {0x6e, 0x20}, {0x6f, 0x60},
+ {0x70, 0x88}, {0x71, 0xc8}, {0x72, 0xc0}, {0x73, 0xb8},
+ {0x74, 0xa8}, {0x75, 0xb8}, {0x76, 0x80}, {0x77, 0x5c},
+ {0x78, 0x26}, {0x79, 0x10}, {0x7a, 0x08}, {0x7b, 0x04},
+ {0x7c, 0x02}, {0x7d, 0x06}, {0x7e, 0x0a}, {0x7f, 0x22},
+ {0x80, 0x33}, {0x81, 0x4c}, {0x82, 0x64}, {0x83, 0x7b},
+ {0x84, 0x90}, {0x85, 0xa7}, {0x86, 0xc7}, {0x87, 0xde},
+ {0x88, 0xf1}, {0x89, 0xf9}, {0x8a, 0xfd}},
+ };
+
+ val = sd->ctrls[CONTRAST].val;
+ switch (sd->sensor) {
+ case SEN_OV7610:
+ case SEN_OV6620:
+ i2c_w(sd, OV7610_REG_CNT, val);
+ break;
+ case SEN_OV6630:
+ case SEN_OV66308AF:
+ i2c_w_mask(sd, OV7610_REG_CNT, val >> 4, 0x0f);
+ break;
+ case SEN_OV8610: {
+ static const u8 ctab[] = {
+ 0x03, 0x09, 0x0b, 0x0f, 0x53, 0x6f, 0x35, 0x7f
+ };
+
+ /* Use Y gamma control instead. Bit 0 enables it. */
+ i2c_w(sd, 0x64, ctab[val >> 5]);
+ break;
+ }
+ case SEN_OV7620:
+ case SEN_OV7620AE: {
+ static const u8 ctab[] = {
+ 0x01, 0x05, 0x09, 0x11, 0x15, 0x35, 0x37, 0x57,
+ 0x5b, 0xa5, 0xa7, 0xc7, 0xc9, 0xcf, 0xef, 0xff
+ };
+
+ /* Use Y gamma control instead. Bit 0 enables it. */
+ i2c_w(sd, 0x64, ctab[val >> 4]);
+ break;
+ }
+ case SEN_OV7660:
+ write_i2c_regvals(sd, contrast_7660[val],
+ ARRAY_SIZE(contrast_7660[0]));
+ break;
+ case SEN_OV7670:
+ /* check that this isn't just the same as ov7610 */
+ i2c_w(sd, OV7670_R56_CONTRAS, val >> 1);
+ break;
+ }
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (!sd->ctrls[AUTOGAIN].val)
+ i2c_w(sd, 0x10, sd->ctrls[EXPOSURE].val);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int val;
+ static const struct ov_i2c_regvals colors_7660[][6] = {
+ {{0x4f, 0x28}, {0x50, 0x2a}, {0x51, 0x02}, {0x52, 0x0a},
+ {0x53, 0x19}, {0x54, 0x23}},
+ {{0x4f, 0x47}, {0x50, 0x4a}, {0x51, 0x03}, {0x52, 0x11},
+ {0x53, 0x2c}, {0x54, 0x3e}},
+ {{0x4f, 0x66}, {0x50, 0x6b}, {0x51, 0x05}, {0x52, 0x19},
+ {0x53, 0x40}, {0x54, 0x59}},
+ {{0x4f, 0x84}, {0x50, 0x8b}, {0x51, 0x06}, {0x52, 0x20},
+ {0x53, 0x53}, {0x54, 0x73}},
+ {{0x4f, 0xa3}, {0x50, 0xab}, {0x51, 0x08}, {0x52, 0x28},
+ {0x53, 0x66}, {0x54, 0x8e}},
+ };
+
+ val = sd->ctrls[COLORS].val;
+ switch (sd->sensor) {
+ case SEN_OV8610:
+ case SEN_OV7610:
+ case SEN_OV76BE:
+ case SEN_OV6620:
+ case SEN_OV6630:
+ case SEN_OV66308AF:
+ i2c_w(sd, OV7610_REG_SAT, val);
+ break;
+ case SEN_OV7620:
+ case SEN_OV7620AE:
+ /* Use UV gamma control instead. Bits 0 & 7 are reserved. */
+/* rc = ov_i2c_write(sd->dev, 0x62, (val >> 9) & 0x7e);
+ if (rc < 0)
+ goto out; */
+ i2c_w(sd, OV7610_REG_SAT, val);
+ break;
+ case SEN_OV7640:
+ case SEN_OV7648:
+ i2c_w(sd, OV7610_REG_SAT, val & 0xf0);
+ break;
+ case SEN_OV7660:
+ write_i2c_regvals(sd, colors_7660[val],
+ ARRAY_SIZE(colors_7660[0]));
+ break;
+ case SEN_OV7670:
+ /* supported later once I work out how to do it
+ * transparently fail now! */
+ /* set REG_COM13 values for UV sat auto mode */
+ break;
+ }
+}
+
+static void setautobright(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ i2c_w_mask(sd, 0x2d, sd->ctrls[AUTOBRIGHT].val ? 0x10 : 0x00, 0x10);
+}
+
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->ctrls[AUTOGAIN].val = val;
+ if (val) {
+ gspca_dev->ctrl_inac |= (1 << EXPOSURE);
+ } else {
+ gspca_dev->ctrl_inac &= ~(1 << EXPOSURE);
+ sd->ctrls[EXPOSURE].val = i2c_r(sd, 0x10);
+ }
+ if (gspca_dev->streaming)
+ setautogain(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static void setfreq_i(struct sd *sd)
+{
+ if (sd->sensor == SEN_OV7660
+ || sd->sensor == SEN_OV7670) {
+ switch (sd->ctrls[FREQ].val) {
+ case 0: /* Banding filter disabled */
+ i2c_w_mask(sd, OV7670_R13_COM8, 0, OV7670_COM8_BFILT);
+ break;
+ case 1: /* 50 hz */
+ i2c_w_mask(sd, OV7670_R13_COM8, OV7670_COM8_BFILT,
+ OV7670_COM8_BFILT);
+ i2c_w_mask(sd, OV7670_R3B_COM11, 0x08, 0x18);
+ break;
+ case 2: /* 60 hz */
+ i2c_w_mask(sd, OV7670_R13_COM8, OV7670_COM8_BFILT,
+ OV7670_COM8_BFILT);
+ i2c_w_mask(sd, OV7670_R3B_COM11, 0x00, 0x18);
+ break;
+ case 3: /* Auto hz - ov7670 only */
+ i2c_w_mask(sd, OV7670_R13_COM8, OV7670_COM8_BFILT,
+ OV7670_COM8_BFILT);
+ i2c_w_mask(sd, OV7670_R3B_COM11, OV7670_COM11_HZAUTO,
+ 0x18);
+ break;
+ }
+ } else {
+ switch (sd->ctrls[FREQ].val) {
+ case 0: /* Banding filter disabled */
+ i2c_w_mask(sd, 0x2d, 0x00, 0x04);
+ i2c_w_mask(sd, 0x2a, 0x00, 0x80);
+ break;
+ case 1: /* 50 hz (filter on and framerate adj) */
+ i2c_w_mask(sd, 0x2d, 0x04, 0x04);
+ i2c_w_mask(sd, 0x2a, 0x80, 0x80);
+ /* 20 fps -> 16.667 fps */
+ if (sd->sensor == SEN_OV6620 ||
+ sd->sensor == SEN_OV6630 ||
+ sd->sensor == SEN_OV66308AF)
+ i2c_w(sd, 0x2b, 0x5e);
+ else
+ i2c_w(sd, 0x2b, 0xac);
+ break;
+ case 2: /* 60 hz (filter on, ...) */
+ i2c_w_mask(sd, 0x2d, 0x04, 0x04);
+ if (sd->sensor == SEN_OV6620 ||
+ sd->sensor == SEN_OV6630 ||
+ sd->sensor == SEN_OV66308AF) {
+ /* 20 fps -> 15 fps */
+ i2c_w_mask(sd, 0x2a, 0x80, 0x80);
+ i2c_w(sd, 0x2b, 0xa8);
+ } else {
+ /* no framerate adj. */
+ i2c_w_mask(sd, 0x2a, 0x00, 0x80);
+ }
+ break;
+ }
+ }
+}
+static void setfreq(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ setfreq_i(sd);
+
+ /* Ugly but necessary */
+ if (sd->bridge == BRIDGE_W9968CF)
+ w9968cf_set_crop_window(sd);
+}
+
+static int sd_querymenu(struct gspca_dev *gspca_dev,
+ struct v4l2_querymenu *menu)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (menu->id) {
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ switch (menu->index) {
+ case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
+ strcpy((char *) menu->name, "NoFliker");
+ return 0;
+ case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
+ strcpy((char *) menu->name, "50 Hz");
+ return 0;
+ case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
+ strcpy((char *) menu->name, "60 Hz");
+ return 0;
+ case 3:
+ if (sd->sensor != SEN_OV7670)
+ return -EINVAL;
+
+ strcpy((char *) menu->name, "Automatic");
+ return 0;
+ }
+ break;
+ }
+ return -EINVAL;
+}
+
+static int sd_get_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->bridge != BRIDGE_W9968CF)
+ return -EINVAL;
+
+ memset(jcomp, 0, sizeof *jcomp);
+ jcomp->quality = sd->quality;
+ jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT |
+ V4L2_JPEG_MARKER_DRI;
+ return 0;
+}
+
+static int sd_set_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->bridge != BRIDGE_W9968CF)
+ return -EINVAL;
+
+ if (gspca_dev->streaming)
+ return -EBUSY;
+
+ if (jcomp->quality < QUALITY_MIN)
+ sd->quality = QUALITY_MIN;
+ else if (jcomp->quality > QUALITY_MAX)
+ sd->quality = QUALITY_MAX;
+ else
+ sd->quality = jcomp->quality;
+
+ /* Return resulting jcomp params to app */
+ sd_get_jcomp(gspca_dev, jcomp);
+
+ return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .isoc_init = sd_isoc_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+ .dq_callback = sd_reset_snapshot,
+ .querymenu = sd_querymenu,
+ .get_jcomp = sd_get_jcomp,
+ .set_jcomp = sd_set_jcomp,
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ .other_input = 1,
+#endif
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x041e, 0x4003), .driver_info = BRIDGE_W9968CF },
+ {USB_DEVICE(0x041e, 0x4052),
+ .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
+ {USB_DEVICE(0x041e, 0x405f), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x041e, 0x4060), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x041e, 0x4061), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x041e, 0x4064), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x041e, 0x4067), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x041e, 0x4068), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x045e, 0x028c),
+ .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
+ {USB_DEVICE(0x054c, 0x0154), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x054c, 0x0155), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x05a9, 0x0511), .driver_info = BRIDGE_OV511 },
+ {USB_DEVICE(0x05a9, 0x0518), .driver_info = BRIDGE_OV518 },
+ {USB_DEVICE(0x05a9, 0x0519),
+ .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
+ {USB_DEVICE(0x05a9, 0x0530),
+ .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
+ {USB_DEVICE(0x05a9, 0x2800), .driver_info = BRIDGE_OVFX2 },
+ {USB_DEVICE(0x05a9, 0x4519), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x05a9, 0x8519), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x05a9, 0xa511), .driver_info = BRIDGE_OV511PLUS },
+ {USB_DEVICE(0x05a9, 0xa518), .driver_info = BRIDGE_OV518PLUS },
+ {USB_DEVICE(0x0813, 0x0002), .driver_info = BRIDGE_OV511PLUS },
+ {USB_DEVICE(0x0b62, 0x0059), .driver_info = BRIDGE_OVFX2 },
+ {USB_DEVICE(0x0e96, 0xc001), .driver_info = BRIDGE_OVFX2 },
+ {USB_DEVICE(0x1046, 0x9967), .driver_info = BRIDGE_W9968CF },
+ {USB_DEVICE(0x8020, 0xef04), .driver_info = BRIDGE_OVFX2 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
+
+module_param(frame_rate, int, 0644);
+MODULE_PARM_DESC(frame_rate, "Frame rate (5, 10, 15, 20 or 30 fps)");
diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c
new file mode 100644
index 00000000..04753391
--- /dev/null
+++ b/drivers/media/video/gspca/ov534.c
@@ -0,0 +1,1536 @@
+/*
+ * ov534-ov7xxx gspca driver
+ *
+ * Copyright (C) 2008 Antonio Ospite <ospite@studenti.unina.it>
+ * Copyright (C) 2008 Jim Paris <jim@jtan.com>
+ * Copyright (C) 2009 Jean-Francois Moine http://moinejf.free.fr
+ *
+ * Based on a prototype written by Mark Ferrell <majortrips@gmail.com>
+ * USB protocol reverse engineered by Jim Paris <jim@jtan.com>
+ * https://jim.sh/svn/jim/devl/playstation/ps3/eye/test/
+ *
+ * PS3 Eye camera enhanced by Richard Kaswy http://kaswy.free.fr
+ * PS3 Eye camera - brightness, contrast, awb, agc, aec controls
+ * added by Max Thrun <bear24rw@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "ov534"
+
+#include "gspca.h"
+
+#define OV534_REG_ADDRESS 0xf1 /* sensor address */
+#define OV534_REG_SUBADDR 0xf2
+#define OV534_REG_WRITE 0xf3
+#define OV534_REG_READ 0xf4
+#define OV534_REG_OPERATION 0xf5
+#define OV534_REG_STATUS 0xf6
+
+#define OV534_OP_WRITE_3 0x37
+#define OV534_OP_WRITE_2 0x33
+#define OV534_OP_READ_2 0xf9
+
+#define CTRL_TIMEOUT 500
+
+MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>");
+MODULE_DESCRIPTION("GSPCA/OV534 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* controls */
+enum e_ctrl {
+ BRIGHTNESS,
+ CONTRAST,
+ GAIN,
+ EXPOSURE,
+ AGC,
+ AWB,
+ AEC,
+ SHARPNESS,
+ HFLIP,
+ VFLIP,
+ COLORS,
+ LIGHTFREQ,
+ NCTRLS /* number of controls */
+};
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ struct gspca_ctrl ctrls[NCTRLS];
+
+ __u32 last_pts;
+ u16 last_fid;
+ u8 frame_rate;
+
+ u8 sensor;
+};
+enum sensors {
+ SENSOR_OV767x,
+ SENSOR_OV772x,
+ NSENSORS
+};
+
+/* V4L2 controls supported by the driver */
+static void setbrightness(struct gspca_dev *gspca_dev);
+static void setcontrast(struct gspca_dev *gspca_dev);
+static void setgain(struct gspca_dev *gspca_dev);
+static void setexposure(struct gspca_dev *gspca_dev);
+static int sd_setagc(struct gspca_dev *gspca_dev, __s32 val);
+static void setawb(struct gspca_dev *gspca_dev);
+static void setaec(struct gspca_dev *gspca_dev);
+static void setsharpness(struct gspca_dev *gspca_dev);
+static void sethvflip(struct gspca_dev *gspca_dev);
+static void setcolors(struct gspca_dev *gspca_dev);
+static void setlightfreq(struct gspca_dev *gspca_dev);
+
+static int sd_start(struct gspca_dev *gspca_dev);
+static void sd_stopN(struct gspca_dev *gspca_dev);
+
+static const struct ctrl sd_ctrls[] = {
+[BRIGHTNESS] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set_control = setbrightness
+ },
+[CONTRAST] = {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 32,
+ },
+ .set_control = setcontrast
+ },
+[GAIN] = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Main Gain",
+ .minimum = 0,
+ .maximum = 63,
+ .step = 1,
+ .default_value = 20,
+ },
+ .set_control = setgain
+ },
+[EXPOSURE] = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 120,
+ },
+ .set_control = setexposure
+ },
+[AGC] = {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto Gain",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ },
+ .set = sd_setagc
+ },
+[AWB] = {
+ {
+ .id = V4L2_CID_AUTO_WHITE_BALANCE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto White Balance",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ },
+ .set_control = setawb
+ },
+[AEC] = {
+ {
+ .id = V4L2_CID_EXPOSURE_AUTO,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto Exposure",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ },
+ .set_control = setaec
+ },
+[SHARPNESS] = {
+ {
+ .id = V4L2_CID_SHARPNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Sharpness",
+ .minimum = 0,
+ .maximum = 63,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set_control = setsharpness
+ },
+[HFLIP] = {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "HFlip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set_control = sethvflip
+ },
+[VFLIP] = {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "VFlip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set_control = sethvflip
+ },
+[COLORS] = {
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 6,
+ .step = 1,
+ .default_value = 3,
+ },
+ .set_control = setcolors
+ },
+[LIGHTFREQ] = {
+ {
+ .id = V4L2_CID_POWER_LINE_FREQUENCY,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Light Frequency Filter",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set_control = setlightfreq
+ },
+};
+
+static const struct v4l2_pix_format ov772x_mode[] = {
+ {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+ .bytesperline = 320 * 2,
+ .sizeimage = 320 * 240 * 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+ .bytesperline = 640 * 2,
+ .sizeimage = 640 * 480 * 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+static const struct v4l2_pix_format ov767x_mode[] = {
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+};
+
+static const u8 qvga_rates[] = {125, 100, 75, 60, 50, 40, 30};
+static const u8 vga_rates[] = {60, 50, 40, 30, 15};
+
+static const struct framerates ov772x_framerates[] = {
+ { /* 320x240 */
+ .rates = qvga_rates,
+ .nrates = ARRAY_SIZE(qvga_rates),
+ },
+ { /* 640x480 */
+ .rates = vga_rates,
+ .nrates = ARRAY_SIZE(vga_rates),
+ },
+};
+
+struct reg_array {
+ const u8 (*val)[2];
+ int len;
+};
+
+static const u8 bridge_init_767x[][2] = {
+/* comments from the ms-win file apollo7670.set */
+/* str1 */
+ {0xf1, 0x42},
+ {0x88, 0xf8},
+ {0x89, 0xff},
+ {0x76, 0x03},
+ {0x92, 0x03},
+ {0x95, 0x10},
+ {0xe2, 0x00},
+ {0xe7, 0x3e},
+ {0x8d, 0x1c},
+ {0x8e, 0x00},
+ {0x8f, 0x00},
+ {0x1f, 0x00},
+ {0xc3, 0xf9},
+ {0x89, 0xff},
+ {0x88, 0xf8},
+ {0x76, 0x03},
+ {0x92, 0x01},
+ {0x93, 0x18},
+ {0x1c, 0x00},
+ {0x1d, 0x48},
+ {0x1d, 0x00},
+ {0x1d, 0xff},
+ {0x1d, 0x02},
+ {0x1d, 0x58},
+ {0x1d, 0x00},
+ {0x1c, 0x0a},
+ {0x1d, 0x0a},
+ {0x1d, 0x0e},
+ {0xc0, 0x50}, /* HSize 640 */
+ {0xc1, 0x3c}, /* VSize 480 */
+ {0x34, 0x05}, /* enable Audio Suspend mode */
+ {0xc2, 0x0c}, /* Input YUV */
+ {0xc3, 0xf9}, /* enable PRE */
+ {0x34, 0x05}, /* enable Audio Suspend mode */
+ {0xe7, 0x2e}, /* this solves failure of "SuspendResumeTest" */
+ {0x31, 0xf9}, /* enable 1.8V Suspend */
+ {0x35, 0x02}, /* turn on JPEG */
+ {0xd9, 0x10},
+ {0x25, 0x42}, /* GPIO[8]:Input */
+ {0x94, 0x11}, /* If the default setting is loaded when
+ * system boots up, this flag is closed here */
+};
+static const u8 sensor_init_767x[][2] = {
+ {0x12, 0x80},
+ {0x11, 0x03},
+ {0x3a, 0x04},
+ {0x12, 0x00},
+ {0x17, 0x13},
+ {0x18, 0x01},
+ {0x32, 0xb6},
+ {0x19, 0x02},
+ {0x1a, 0x7a},
+ {0x03, 0x0a},
+ {0x0c, 0x00},
+ {0x3e, 0x00},
+ {0x70, 0x3a},
+ {0x71, 0x35},
+ {0x72, 0x11},
+ {0x73, 0xf0},
+ {0xa2, 0x02},
+ {0x7a, 0x2a}, /* set Gamma=1.6 below */
+ {0x7b, 0x12},
+ {0x7c, 0x1d},
+ {0x7d, 0x2d},
+ {0x7e, 0x45},
+ {0x7f, 0x50},
+ {0x80, 0x59},
+ {0x81, 0x62},
+ {0x82, 0x6b},
+ {0x83, 0x73},
+ {0x84, 0x7b},
+ {0x85, 0x8a},
+ {0x86, 0x98},
+ {0x87, 0xb2},
+ {0x88, 0xca},
+ {0x89, 0xe0},
+ {0x13, 0xe0},
+ {0x00, 0x00},
+ {0x10, 0x00},
+ {0x0d, 0x40},
+ {0x14, 0x38}, /* gain max 16x */
+ {0xa5, 0x05},
+ {0xab, 0x07},
+ {0x24, 0x95},
+ {0x25, 0x33},
+ {0x26, 0xe3},
+ {0x9f, 0x78},
+ {0xa0, 0x68},
+ {0xa1, 0x03},
+ {0xa6, 0xd8},
+ {0xa7, 0xd8},
+ {0xa8, 0xf0},
+ {0xa9, 0x90},
+ {0xaa, 0x94},
+ {0x13, 0xe5},
+ {0x0e, 0x61},
+ {0x0f, 0x4b},
+ {0x16, 0x02},
+ {0x21, 0x02},
+ {0x22, 0x91},
+ {0x29, 0x07},
+ {0x33, 0x0b},
+ {0x35, 0x0b},
+ {0x37, 0x1d},
+ {0x38, 0x71},
+ {0x39, 0x2a},
+ {0x3c, 0x78},
+ {0x4d, 0x40},
+ {0x4e, 0x20},
+ {0x69, 0x00},
+ {0x6b, 0x4a},
+ {0x74, 0x10},
+ {0x8d, 0x4f},
+ {0x8e, 0x00},
+ {0x8f, 0x00},
+ {0x90, 0x00},
+ {0x91, 0x00},
+ {0x96, 0x00},
+ {0x9a, 0x80},
+ {0xb0, 0x84},
+ {0xb1, 0x0c},
+ {0xb2, 0x0e},
+ {0xb3, 0x82},
+ {0xb8, 0x0a},
+ {0x43, 0x0a},
+ {0x44, 0xf0},
+ {0x45, 0x34},
+ {0x46, 0x58},
+ {0x47, 0x28},
+ {0x48, 0x3a},
+ {0x59, 0x88},
+ {0x5a, 0x88},
+ {0x5b, 0x44},
+ {0x5c, 0x67},
+ {0x5d, 0x49},
+ {0x5e, 0x0e},
+ {0x6c, 0x0a},
+ {0x6d, 0x55},
+ {0x6e, 0x11},
+ {0x6f, 0x9f},
+ {0x6a, 0x40},
+ {0x01, 0x40},
+ {0x02, 0x40},
+ {0x13, 0xe7},
+ {0x4f, 0x80},
+ {0x50, 0x80},
+ {0x51, 0x00},
+ {0x52, 0x22},
+ {0x53, 0x5e},
+ {0x54, 0x80},
+ {0x58, 0x9e},
+ {0x41, 0x08},
+ {0x3f, 0x00},
+ {0x75, 0x04},
+ {0x76, 0xe1},
+ {0x4c, 0x00},
+ {0x77, 0x01},
+ {0x3d, 0xc2},
+ {0x4b, 0x09},
+ {0xc9, 0x60},
+ {0x41, 0x38}, /* jfm: auto sharpness + auto de-noise */
+ {0x56, 0x40},
+ {0x34, 0x11},
+ {0x3b, 0xc2},
+ {0xa4, 0x8a}, /* Night mode trigger point */
+ {0x96, 0x00},
+ {0x97, 0x30},
+ {0x98, 0x20},
+ {0x99, 0x20},
+ {0x9a, 0x84},
+ {0x9b, 0x29},
+ {0x9c, 0x03},
+ {0x9d, 0x4c},
+ {0x9e, 0x3f},
+ {0x78, 0x04},
+ {0x79, 0x01},
+ {0xc8, 0xf0},
+ {0x79, 0x0f},
+ {0xc8, 0x00},
+ {0x79, 0x10},
+ {0xc8, 0x7e},
+ {0x79, 0x0a},
+ {0xc8, 0x80},
+ {0x79, 0x0b},
+ {0xc8, 0x01},
+ {0x79, 0x0c},
+ {0xc8, 0x0f},
+ {0x79, 0x0d},
+ {0xc8, 0x20},
+ {0x79, 0x09},
+ {0xc8, 0x80},
+ {0x79, 0x02},
+ {0xc8, 0xc0},
+ {0x79, 0x03},
+ {0xc8, 0x20},
+ {0x79, 0x26},
+};
+static const u8 bridge_start_vga_767x[][2] = {
+/* str59 JPG */
+ {0x94, 0xaa},
+ {0xf1, 0x42},
+ {0xe5, 0x04},
+ {0xc0, 0x50},
+ {0xc1, 0x3c},
+ {0xc2, 0x0c},
+ {0x35, 0x02}, /* turn on JPEG */
+ {0xd9, 0x10},
+ {0xda, 0x00}, /* for higher clock rate(30fps) */
+ {0x34, 0x05}, /* enable Audio Suspend mode */
+ {0xc3, 0xf9}, /* enable PRE */
+ {0x8c, 0x00}, /* CIF VSize LSB[2:0] */
+ {0x8d, 0x1c}, /* output YUV */
+/* {0x34, 0x05}, * enable Audio Suspend mode (?) */
+ {0x50, 0x00}, /* H/V divider=0 */
+ {0x51, 0xa0}, /* input H=640/4 */
+ {0x52, 0x3c}, /* input V=480/4 */
+ {0x53, 0x00}, /* offset X=0 */
+ {0x54, 0x00}, /* offset Y=0 */
+ {0x55, 0x00}, /* H/V size[8]=0 */
+ {0x57, 0x00}, /* H-size[9]=0 */
+ {0x5c, 0x00}, /* output size[9:8]=0 */
+ {0x5a, 0xa0}, /* output H=640/4 */
+ {0x5b, 0x78}, /* output V=480/4 */
+ {0x1c, 0x0a},
+ {0x1d, 0x0a},
+ {0x94, 0x11},
+};
+static const u8 sensor_start_vga_767x[][2] = {
+ {0x11, 0x01},
+ {0x1e, 0x04},
+ {0x19, 0x02},
+ {0x1a, 0x7a},
+};
+static const u8 bridge_start_qvga_767x[][2] = {
+/* str86 JPG */
+ {0x94, 0xaa},
+ {0xf1, 0x42},
+ {0xe5, 0x04},
+ {0xc0, 0x80},
+ {0xc1, 0x60},
+ {0xc2, 0x0c},
+ {0x35, 0x02}, /* turn on JPEG */
+ {0xd9, 0x10},
+ {0xc0, 0x50}, /* CIF HSize 640 */
+ {0xc1, 0x3c}, /* CIF VSize 480 */
+ {0x8c, 0x00}, /* CIF VSize LSB[2:0] */
+ {0x8d, 0x1c}, /* output YUV */
+ {0x34, 0x05}, /* enable Audio Suspend mode */
+ {0xc2, 0x4c}, /* output YUV and Enable DCW */
+ {0xc3, 0xf9}, /* enable PRE */
+ {0x1c, 0x00}, /* indirect addressing */
+ {0x1d, 0x48}, /* output YUV422 */
+ {0x50, 0x89}, /* H/V divider=/2; plus DCW AVG */
+ {0x51, 0xa0}, /* DCW input H=640/4 */
+ {0x52, 0x78}, /* DCW input V=480/4 */
+ {0x53, 0x00}, /* offset X=0 */
+ {0x54, 0x00}, /* offset Y=0 */
+ {0x55, 0x00}, /* H/V size[8]=0 */
+ {0x57, 0x00}, /* H-size[9]=0 */
+ {0x5c, 0x00}, /* DCW output size[9:8]=0 */
+ {0x5a, 0x50}, /* DCW output H=320/4 */
+ {0x5b, 0x3c}, /* DCW output V=240/4 */
+ {0x1c, 0x0a},
+ {0x1d, 0x0a},
+ {0x94, 0x11},
+};
+static const u8 sensor_start_qvga_767x[][2] = {
+ {0x11, 0x01},
+ {0x1e, 0x04},
+ {0x19, 0x02},
+ {0x1a, 0x7a},
+};
+
+static const u8 bridge_init_772x[][2] = {
+ { 0xc2, 0x0c },
+ { 0x88, 0xf8 },
+ { 0xc3, 0x69 },
+ { 0x89, 0xff },
+ { 0x76, 0x03 },
+ { 0x92, 0x01 },
+ { 0x93, 0x18 },
+ { 0x94, 0x10 },
+ { 0x95, 0x10 },
+ { 0xe2, 0x00 },
+ { 0xe7, 0x3e },
+
+ { 0x96, 0x00 },
+
+ { 0x97, 0x20 },
+ { 0x97, 0x20 },
+ { 0x97, 0x20 },
+ { 0x97, 0x0a },
+ { 0x97, 0x3f },
+ { 0x97, 0x4a },
+ { 0x97, 0x20 },
+ { 0x97, 0x15 },
+ { 0x97, 0x0b },
+
+ { 0x8e, 0x40 },
+ { 0x1f, 0x81 },
+ { 0x34, 0x05 },
+ { 0xe3, 0x04 },
+ { 0x88, 0x00 },
+ { 0x89, 0x00 },
+ { 0x76, 0x00 },
+ { 0xe7, 0x2e },
+ { 0x31, 0xf9 },
+ { 0x25, 0x42 },
+ { 0x21, 0xf0 },
+
+ { 0x1c, 0x00 },
+ { 0x1d, 0x40 },
+ { 0x1d, 0x02 }, /* payload size 0x0200 * 4 = 2048 bytes */
+ { 0x1d, 0x00 }, /* payload size */
+
+ { 0x1d, 0x02 }, /* frame size 0x025800 * 4 = 614400 */
+ { 0x1d, 0x58 }, /* frame size */
+ { 0x1d, 0x00 }, /* frame size */
+
+ { 0x1c, 0x0a },
+ { 0x1d, 0x08 }, /* turn on UVC header */
+ { 0x1d, 0x0e }, /* .. */
+
+ { 0x8d, 0x1c },
+ { 0x8e, 0x80 },
+ { 0xe5, 0x04 },
+
+ { 0xc0, 0x50 },
+ { 0xc1, 0x3c },
+ { 0xc2, 0x0c },
+};
+static const u8 sensor_init_772x[][2] = {
+ { 0x12, 0x80 },
+ { 0x11, 0x01 },
+/*fixme: better have a delay?*/
+ { 0x11, 0x01 },
+ { 0x11, 0x01 },
+ { 0x11, 0x01 },
+ { 0x11, 0x01 },
+ { 0x11, 0x01 },
+ { 0x11, 0x01 },
+ { 0x11, 0x01 },
+ { 0x11, 0x01 },
+ { 0x11, 0x01 },
+ { 0x11, 0x01 },
+
+ { 0x3d, 0x03 },
+ { 0x17, 0x26 },
+ { 0x18, 0xa0 },
+ { 0x19, 0x07 },
+ { 0x1a, 0xf0 },
+ { 0x32, 0x00 },
+ { 0x29, 0xa0 },
+ { 0x2c, 0xf0 },
+ { 0x65, 0x20 },
+ { 0x11, 0x01 },
+ { 0x42, 0x7f },
+ { 0x63, 0xaa }, /* AWB - was e0 */
+ { 0x64, 0xff },
+ { 0x66, 0x00 },
+ { 0x13, 0xf0 }, /* com8 */
+ { 0x0d, 0x41 },
+ { 0x0f, 0xc5 },
+ { 0x14, 0x11 },
+
+ { 0x22, 0x7f },
+ { 0x23, 0x03 },
+ { 0x24, 0x40 },
+ { 0x25, 0x30 },
+ { 0x26, 0xa1 },
+ { 0x2a, 0x00 },
+ { 0x2b, 0x00 },
+ { 0x6b, 0xaa },
+ { 0x13, 0xff }, /* AWB */
+
+ { 0x90, 0x05 },
+ { 0x91, 0x01 },
+ { 0x92, 0x03 },
+ { 0x93, 0x00 },
+ { 0x94, 0x60 },
+ { 0x95, 0x3c },
+ { 0x96, 0x24 },
+ { 0x97, 0x1e },
+ { 0x98, 0x62 },
+ { 0x99, 0x80 },
+ { 0x9a, 0x1e },
+ { 0x9b, 0x08 },
+ { 0x9c, 0x20 },
+ { 0x9e, 0x81 },
+
+ { 0xa6, 0x04 },
+ { 0x7e, 0x0c },
+ { 0x7f, 0x16 },
+ { 0x80, 0x2a },
+ { 0x81, 0x4e },
+ { 0x82, 0x61 },
+ { 0x83, 0x6f },
+ { 0x84, 0x7b },
+ { 0x85, 0x86 },
+ { 0x86, 0x8e },
+ { 0x87, 0x97 },
+ { 0x88, 0xa4 },
+ { 0x89, 0xaf },
+ { 0x8a, 0xc5 },
+ { 0x8b, 0xd7 },
+ { 0x8c, 0xe8 },
+ { 0x8d, 0x20 },
+
+ { 0x0c, 0x90 },
+
+ { 0x2b, 0x00 },
+ { 0x22, 0x7f },
+ { 0x23, 0x03 },
+ { 0x11, 0x01 },
+ { 0x0c, 0xd0 },
+ { 0x64, 0xff },
+ { 0x0d, 0x41 },
+
+ { 0x14, 0x41 },
+ { 0x0e, 0xcd },
+ { 0xac, 0xbf },
+ { 0x8e, 0x00 }, /* De-noise threshold */
+ { 0x0c, 0xd0 }
+};
+static const u8 bridge_start_vga_772x[][2] = {
+ {0x1c, 0x00},
+ {0x1d, 0x40},
+ {0x1d, 0x02},
+ {0x1d, 0x00},
+ {0x1d, 0x02},
+ {0x1d, 0x58},
+ {0x1d, 0x00},
+ {0xc0, 0x50},
+ {0xc1, 0x3c},
+};
+static const u8 sensor_start_vga_772x[][2] = {
+ {0x12, 0x00},
+ {0x17, 0x26},
+ {0x18, 0xa0},
+ {0x19, 0x07},
+ {0x1a, 0xf0},
+ {0x29, 0xa0},
+ {0x2c, 0xf0},
+ {0x65, 0x20},
+};
+static const u8 bridge_start_qvga_772x[][2] = {
+ {0x1c, 0x00},
+ {0x1d, 0x40},
+ {0x1d, 0x02},
+ {0x1d, 0x00},
+ {0x1d, 0x01},
+ {0x1d, 0x4b},
+ {0x1d, 0x00},
+ {0xc0, 0x28},
+ {0xc1, 0x1e},
+};
+static const u8 sensor_start_qvga_772x[][2] = {
+ {0x12, 0x40},
+ {0x17, 0x3f},
+ {0x18, 0x50},
+ {0x19, 0x03},
+ {0x1a, 0x78},
+ {0x29, 0x50},
+ {0x2c, 0x78},
+ {0x65, 0x2f},
+};
+
+static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val)
+{
+ struct usb_device *udev = gspca_dev->dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+
+ PDEBUG(D_USBO, "SET 01 0000 %04x %02x", reg, val);
+ gspca_dev->usb_buf[0] = val;
+ ret = usb_control_msg(udev,
+ usb_sndctrlpipe(udev, 0),
+ 0x01,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
+ if (ret < 0) {
+ pr_err("write failed %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static u8 ov534_reg_read(struct gspca_dev *gspca_dev, u16 reg)
+{
+ struct usb_device *udev = gspca_dev->dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return 0;
+ ret = usb_control_msg(udev,
+ usb_rcvctrlpipe(udev, 0),
+ 0x01,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
+ PDEBUG(D_USBI, "GET 01 0000 %04x %02x", reg, gspca_dev->usb_buf[0]);
+ if (ret < 0) {
+ pr_err("read failed %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+ return gspca_dev->usb_buf[0];
+}
+
+/* Two bits control LED: 0x21 bit 7 and 0x23 bit 7.
+ * (direction and output)? */
+static void ov534_set_led(struct gspca_dev *gspca_dev, int status)
+{
+ u8 data;
+
+ PDEBUG(D_CONF, "led status: %d", status);
+
+ data = ov534_reg_read(gspca_dev, 0x21);
+ data |= 0x80;
+ ov534_reg_write(gspca_dev, 0x21, data);
+
+ data = ov534_reg_read(gspca_dev, 0x23);
+ if (status)
+ data |= 0x80;
+ else
+ data &= ~0x80;
+
+ ov534_reg_write(gspca_dev, 0x23, data);
+
+ if (!status) {
+ data = ov534_reg_read(gspca_dev, 0x21);
+ data &= ~0x80;
+ ov534_reg_write(gspca_dev, 0x21, data);
+ }
+}
+
+static int sccb_check_status(struct gspca_dev *gspca_dev)
+{
+ u8 data;
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ data = ov534_reg_read(gspca_dev, OV534_REG_STATUS);
+
+ switch (data) {
+ case 0x00:
+ return 1;
+ case 0x04:
+ return 0;
+ case 0x03:
+ break;
+ default:
+ PDEBUG(D_ERR, "sccb status 0x%02x, attempt %d/5",
+ data, i + 1);
+ }
+ }
+ return 0;
+}
+
+static void sccb_reg_write(struct gspca_dev *gspca_dev, u8 reg, u8 val)
+{
+ PDEBUG(D_USBO, "sccb write: %02x %02x", reg, val);
+ ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg);
+ ov534_reg_write(gspca_dev, OV534_REG_WRITE, val);
+ ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3);
+
+ if (!sccb_check_status(gspca_dev)) {
+ pr_err("sccb_reg_write failed\n");
+ gspca_dev->usb_err = -EIO;
+ }
+}
+
+static u8 sccb_reg_read(struct gspca_dev *gspca_dev, u16 reg)
+{
+ ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg);
+ ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2);
+ if (!sccb_check_status(gspca_dev))
+ pr_err("sccb_reg_read failed 1\n");
+
+ ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2);
+ if (!sccb_check_status(gspca_dev))
+ pr_err("sccb_reg_read failed 2\n");
+
+ return ov534_reg_read(gspca_dev, OV534_REG_READ);
+}
+
+/* output a bridge sequence (reg - val) */
+static void reg_w_array(struct gspca_dev *gspca_dev,
+ const u8 (*data)[2], int len)
+{
+ while (--len >= 0) {
+ ov534_reg_write(gspca_dev, (*data)[0], (*data)[1]);
+ data++;
+ }
+}
+
+/* output a sensor sequence (reg - val) */
+static void sccb_w_array(struct gspca_dev *gspca_dev,
+ const u8 (*data)[2], int len)
+{
+ while (--len >= 0) {
+ if ((*data)[0] != 0xff) {
+ sccb_reg_write(gspca_dev, (*data)[0], (*data)[1]);
+ } else {
+ sccb_reg_read(gspca_dev, (*data)[1]);
+ sccb_reg_write(gspca_dev, 0xff, 0x00);
+ }
+ data++;
+ }
+}
+
+/* ov772x specific controls */
+static void set_frame_rate(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+ struct rate_s {
+ u8 fps;
+ u8 r11;
+ u8 r0d;
+ u8 re5;
+ };
+ const struct rate_s *r;
+ static const struct rate_s rate_0[] = { /* 640x480 */
+ {60, 0x01, 0xc1, 0x04},
+ {50, 0x01, 0x41, 0x02},
+ {40, 0x02, 0xc1, 0x04},
+ {30, 0x04, 0x81, 0x02},
+ {15, 0x03, 0x41, 0x04},
+ };
+ static const struct rate_s rate_1[] = { /* 320x240 */
+ {125, 0x02, 0x81, 0x02},
+ {100, 0x02, 0xc1, 0x04},
+ {75, 0x03, 0xc1, 0x04},
+ {60, 0x04, 0xc1, 0x04},
+ {50, 0x02, 0x41, 0x04},
+ {40, 0x03, 0x41, 0x04},
+ {30, 0x04, 0x41, 0x04},
+ };
+
+ if (sd->sensor != SENSOR_OV772x)
+ return;
+ if (gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv == 0) {
+ r = rate_0;
+ i = ARRAY_SIZE(rate_0);
+ } else {
+ r = rate_1;
+ i = ARRAY_SIZE(rate_1);
+ }
+ while (--i > 0) {
+ if (sd->frame_rate >= r->fps)
+ break;
+ r++;
+ }
+
+ sccb_reg_write(gspca_dev, 0x11, r->r11);
+ sccb_reg_write(gspca_dev, 0x0d, r->r0d);
+ ov534_reg_write(gspca_dev, 0xe5, r->re5);
+
+ PDEBUG(D_PROBE, "frame_rate: %d", r->fps);
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int val;
+
+ val = sd->ctrls[BRIGHTNESS].val;
+ if (sd->sensor == SENSOR_OV767x) {
+ if (val < 0)
+ val = 0x80 - val;
+ sccb_reg_write(gspca_dev, 0x55, val); /* bright */
+ } else {
+ sccb_reg_write(gspca_dev, 0x9b, val);
+ }
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
+
+ val = sd->ctrls[CONTRAST].val;
+ if (sd->sensor == SENSOR_OV767x)
+ sccb_reg_write(gspca_dev, 0x56, val); /* contras */
+ else
+ sccb_reg_write(gspca_dev, 0x9c, val);
+}
+
+static void setgain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
+
+ if (sd->ctrls[AGC].val)
+ return;
+
+ val = sd->ctrls[GAIN].val;
+ switch (val & 0x30) {
+ case 0x00:
+ val &= 0x0f;
+ break;
+ case 0x10:
+ val &= 0x0f;
+ val |= 0x30;
+ break;
+ case 0x20:
+ val &= 0x0f;
+ val |= 0x70;
+ break;
+ default:
+/* case 0x30: */
+ val &= 0x0f;
+ val |= 0xf0;
+ break;
+ }
+ sccb_reg_write(gspca_dev, 0x00, val);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
+
+ if (sd->ctrls[AEC].val)
+ return;
+
+ val = sd->ctrls[EXPOSURE].val;
+ if (sd->sensor == SENSOR_OV767x) {
+
+ /* set only aec[9:2] */
+ sccb_reg_write(gspca_dev, 0x10, val); /* aech */
+ } else {
+
+ /* 'val' is one byte and represents half of the exposure value
+ * we are going to set into registers, a two bytes value:
+ *
+ * MSB: ((u16) val << 1) >> 8 == val >> 7
+ * LSB: ((u16) val << 1) & 0xff == val << 1
+ */
+ sccb_reg_write(gspca_dev, 0x08, val >> 7);
+ sccb_reg_write(gspca_dev, 0x10, val << 1);
+ }
+}
+
+static void setagc(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->ctrls[AGC].val) {
+ sccb_reg_write(gspca_dev, 0x13,
+ sccb_reg_read(gspca_dev, 0x13) | 0x04);
+ sccb_reg_write(gspca_dev, 0x64,
+ sccb_reg_read(gspca_dev, 0x64) | 0x03);
+ } else {
+ sccb_reg_write(gspca_dev, 0x13,
+ sccb_reg_read(gspca_dev, 0x13) & ~0x04);
+ sccb_reg_write(gspca_dev, 0x64,
+ sccb_reg_read(gspca_dev, 0x64) & ~0x03);
+
+ setgain(gspca_dev);
+ }
+}
+
+static void setawb(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->ctrls[AWB].val) {
+ sccb_reg_write(gspca_dev, 0x13,
+ sccb_reg_read(gspca_dev, 0x13) | 0x02);
+ if (sd->sensor == SENSOR_OV772x)
+ sccb_reg_write(gspca_dev, 0x63,
+ sccb_reg_read(gspca_dev, 0x63) | 0xc0);
+ } else {
+ sccb_reg_write(gspca_dev, 0x13,
+ sccb_reg_read(gspca_dev, 0x13) & ~0x02);
+ if (sd->sensor == SENSOR_OV772x)
+ sccb_reg_write(gspca_dev, 0x63,
+ sccb_reg_read(gspca_dev, 0x63) & ~0xc0);
+ }
+}
+
+static void setaec(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data;
+
+ data = sd->sensor == SENSOR_OV767x ?
+ 0x05 : /* agc + aec */
+ 0x01; /* agc */
+ if (sd->ctrls[AEC].val)
+ sccb_reg_write(gspca_dev, 0x13,
+ sccb_reg_read(gspca_dev, 0x13) | data);
+ else {
+ sccb_reg_write(gspca_dev, 0x13,
+ sccb_reg_read(gspca_dev, 0x13) & ~data);
+ if (sd->sensor == SENSOR_OV767x)
+ sd->ctrls[EXPOSURE].val =
+ sccb_reg_read(gspca_dev, 10); /* aech */
+ else
+ setexposure(gspca_dev);
+ }
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
+
+ val = sd->ctrls[SHARPNESS].val;
+ sccb_reg_write(gspca_dev, 0x91, val); /* Auto de-noise threshold */
+ sccb_reg_write(gspca_dev, 0x8e, val); /* De-noise threshold */
+}
+
+static void sethvflip(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
+
+ if (sd->sensor == SENSOR_OV767x) {
+ val = sccb_reg_read(gspca_dev, 0x1e); /* mvfp */
+ val &= ~0x30;
+ if (sd->ctrls[HFLIP].val)
+ val |= 0x20;
+ if (sd->ctrls[VFLIP].val)
+ val |= 0x10;
+ sccb_reg_write(gspca_dev, 0x1e, val);
+ } else {
+ val = sccb_reg_read(gspca_dev, 0x0c);
+ val &= ~0xc0;
+ if (sd->ctrls[HFLIP].val == 0)
+ val |= 0x40;
+ if (sd->ctrls[VFLIP].val == 0)
+ val |= 0x80;
+ sccb_reg_write(gspca_dev, 0x0c, val);
+ }
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
+ int i;
+ static u8 color_tb[][6] = {
+ {0x42, 0x42, 0x00, 0x11, 0x30, 0x41},
+ {0x52, 0x52, 0x00, 0x16, 0x3c, 0x52},
+ {0x66, 0x66, 0x00, 0x1b, 0x4b, 0x66},
+ {0x80, 0x80, 0x00, 0x22, 0x5e, 0x80},
+ {0x9a, 0x9a, 0x00, 0x29, 0x71, 0x9a},
+ {0xb8, 0xb8, 0x00, 0x31, 0x87, 0xb8},
+ {0xdd, 0xdd, 0x00, 0x3b, 0xa2, 0xdd},
+ };
+
+ val = sd->ctrls[COLORS].val;
+ for (i = 0; i < ARRAY_SIZE(color_tb[0]); i++)
+ sccb_reg_write(gspca_dev, 0x4f + i, color_tb[val][i]);
+}
+
+static void setlightfreq(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
+
+ val = sd->ctrls[LIGHTFREQ].val ? 0x9e : 0x00;
+ if (sd->sensor == SENSOR_OV767x) {
+ sccb_reg_write(gspca_dev, 0x2a, 0x00);
+ if (val)
+ val = 0x9d; /* insert dummy to 25fps for 50Hz */
+ }
+ sccb_reg_write(gspca_dev, 0x2b, val);
+}
+
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+
+ cam->ctrls = sd->ctrls;
+
+ /* the auto white balance control works only when auto gain is set */
+ if (sd_ctrls[AGC].qctrl.default_value == 0)
+ gspca_dev->ctrl_inac |= (1 << AWB);
+
+ cam->cam_mode = ov772x_mode;
+ cam->nmodes = ARRAY_SIZE(ov772x_mode);
+
+ sd->frame_rate = 30;
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 sensor_id;
+ static const struct reg_array bridge_init[NSENSORS] = {
+ [SENSOR_OV767x] = {bridge_init_767x, ARRAY_SIZE(bridge_init_767x)},
+ [SENSOR_OV772x] = {bridge_init_772x, ARRAY_SIZE(bridge_init_772x)},
+ };
+ static const struct reg_array sensor_init[NSENSORS] = {
+ [SENSOR_OV767x] = {sensor_init_767x, ARRAY_SIZE(sensor_init_767x)},
+ [SENSOR_OV772x] = {sensor_init_772x, ARRAY_SIZE(sensor_init_772x)},
+ };
+
+ /* reset bridge */
+ ov534_reg_write(gspca_dev, 0xe7, 0x3a);
+ ov534_reg_write(gspca_dev, 0xe0, 0x08);
+ msleep(100);
+
+ /* initialize the sensor address */
+ ov534_reg_write(gspca_dev, OV534_REG_ADDRESS, 0x42);
+
+ /* reset sensor */
+ sccb_reg_write(gspca_dev, 0x12, 0x80);
+ msleep(10);
+
+ /* probe the sensor */
+ sccb_reg_read(gspca_dev, 0x0a);
+ sensor_id = sccb_reg_read(gspca_dev, 0x0a) << 8;
+ sccb_reg_read(gspca_dev, 0x0b);
+ sensor_id |= sccb_reg_read(gspca_dev, 0x0b);
+ PDEBUG(D_PROBE, "Sensor ID: %04x", sensor_id);
+
+ if ((sensor_id & 0xfff0) == 0x7670) {
+ sd->sensor = SENSOR_OV767x;
+ gspca_dev->ctrl_dis = (1 << GAIN) |
+ (1 << AGC) |
+ (1 << SHARPNESS); /* auto */
+ sd->ctrls[BRIGHTNESS].min = -127;
+ sd->ctrls[BRIGHTNESS].max = 127;
+ sd->ctrls[BRIGHTNESS].def = 0;
+ sd->ctrls[CONTRAST].max = 0x80;
+ sd->ctrls[CONTRAST].def = 0x40;
+ sd->ctrls[EXPOSURE].min = 0x08;
+ sd->ctrls[EXPOSURE].max = 0x60;
+ sd->ctrls[EXPOSURE].def = 0x13;
+ sd->ctrls[SHARPNESS].max = 9;
+ sd->ctrls[SHARPNESS].def = 4;
+ sd->ctrls[HFLIP].def = 1;
+ gspca_dev->cam.cam_mode = ov767x_mode;
+ gspca_dev->cam.nmodes = ARRAY_SIZE(ov767x_mode);
+ } else {
+ sd->sensor = SENSOR_OV772x;
+ gspca_dev->ctrl_dis = (1 << COLORS);
+ gspca_dev->cam.bulk = 1;
+ gspca_dev->cam.bulk_size = 16384;
+ gspca_dev->cam.bulk_nurbs = 2;
+ gspca_dev->cam.mode_framerates = ov772x_framerates;
+ }
+
+ /* initialize */
+ reg_w_array(gspca_dev, bridge_init[sd->sensor].val,
+ bridge_init[sd->sensor].len);
+ ov534_set_led(gspca_dev, 1);
+ sccb_w_array(gspca_dev, sensor_init[sd->sensor].val,
+ sensor_init[sd->sensor].len);
+ if (sd->sensor == SENSOR_OV767x)
+ sd_start(gspca_dev);
+ sd_stopN(gspca_dev);
+/* set_frame_rate(gspca_dev); */
+
+ return gspca_dev->usb_err;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int mode;
+ static const struct reg_array bridge_start[NSENSORS][2] = {
+ [SENSOR_OV767x] = {{bridge_start_qvga_767x,
+ ARRAY_SIZE(bridge_start_qvga_767x)},
+ {bridge_start_vga_767x,
+ ARRAY_SIZE(bridge_start_vga_767x)}},
+ [SENSOR_OV772x] = {{bridge_start_qvga_772x,
+ ARRAY_SIZE(bridge_start_qvga_772x)},
+ {bridge_start_vga_772x,
+ ARRAY_SIZE(bridge_start_vga_772x)}},
+ };
+ static const struct reg_array sensor_start[NSENSORS][2] = {
+ [SENSOR_OV767x] = {{sensor_start_qvga_767x,
+ ARRAY_SIZE(sensor_start_qvga_767x)},
+ {sensor_start_vga_767x,
+ ARRAY_SIZE(sensor_start_vga_767x)}},
+ [SENSOR_OV772x] = {{sensor_start_qvga_772x,
+ ARRAY_SIZE(sensor_start_qvga_772x)},
+ {sensor_start_vga_772x,
+ ARRAY_SIZE(sensor_start_vga_772x)}},
+ };
+
+ /* (from ms-win trace) */
+ if (sd->sensor == SENSOR_OV767x)
+ sccb_reg_write(gspca_dev, 0x1e, 0x04);
+ /* black sun enable ? */
+
+ mode = gspca_dev->curr_mode; /* 0: 320x240, 1: 640x480 */
+ reg_w_array(gspca_dev, bridge_start[sd->sensor][mode].val,
+ bridge_start[sd->sensor][mode].len);
+ sccb_w_array(gspca_dev, sensor_start[sd->sensor][mode].val,
+ sensor_start[sd->sensor][mode].len);
+
+ set_frame_rate(gspca_dev);
+
+ if (!(gspca_dev->ctrl_dis & (1 << AGC)))
+ setagc(gspca_dev);
+ setawb(gspca_dev);
+ setaec(gspca_dev);
+ if (!(gspca_dev->ctrl_dis & (1 << GAIN)))
+ setgain(gspca_dev);
+ setexposure(gspca_dev);
+ setbrightness(gspca_dev);
+ setcontrast(gspca_dev);
+ if (!(gspca_dev->ctrl_dis & (1 << SHARPNESS)))
+ setsharpness(gspca_dev);
+ sethvflip(gspca_dev);
+ if (!(gspca_dev->ctrl_dis & (1 << COLORS)))
+ setcolors(gspca_dev);
+ setlightfreq(gspca_dev);
+
+ ov534_set_led(gspca_dev, 1);
+ ov534_reg_write(gspca_dev, 0xe0, 0x00);
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ ov534_reg_write(gspca_dev, 0xe0, 0x09);
+ ov534_set_led(gspca_dev, 0);
+}
+
+/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */
+#define UVC_STREAM_EOH (1 << 7)
+#define UVC_STREAM_ERR (1 << 6)
+#define UVC_STREAM_STI (1 << 5)
+#define UVC_STREAM_RES (1 << 4)
+#define UVC_STREAM_SCR (1 << 3)
+#define UVC_STREAM_PTS (1 << 2)
+#define UVC_STREAM_EOF (1 << 1)
+#define UVC_STREAM_FID (1 << 0)
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u32 this_pts;
+ u16 this_fid;
+ int remaining_len = len;
+ int payload_len;
+
+ payload_len = gspca_dev->cam.bulk ? 2048 : 2040;
+ do {
+ len = min(remaining_len, payload_len);
+
+ /* Payloads are prefixed with a UVC-style header. We
+ consider a frame to start when the FID toggles, or the PTS
+ changes. A frame ends when EOF is set, and we've received
+ the correct number of bytes. */
+
+ /* Verify UVC header. Header length is always 12 */
+ if (data[0] != 12 || len < 12) {
+ PDEBUG(D_PACK, "bad header");
+ goto discard;
+ }
+
+ /* Check errors */
+ if (data[1] & UVC_STREAM_ERR) {
+ PDEBUG(D_PACK, "payload error");
+ goto discard;
+ }
+
+ /* Extract PTS and FID */
+ if (!(data[1] & UVC_STREAM_PTS)) {
+ PDEBUG(D_PACK, "PTS not present");
+ goto discard;
+ }
+ this_pts = (data[5] << 24) | (data[4] << 16)
+ | (data[3] << 8) | data[2];
+ this_fid = (data[1] & UVC_STREAM_FID) ? 1 : 0;
+
+ /* If PTS or FID has changed, start a new frame. */
+ if (this_pts != sd->last_pts || this_fid != sd->last_fid) {
+ if (gspca_dev->last_packet_type == INTER_PACKET)
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ NULL, 0);
+ sd->last_pts = this_pts;
+ sd->last_fid = this_fid;
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ data + 12, len - 12);
+ /* If this packet is marked as EOF, end the frame */
+ } else if (data[1] & UVC_STREAM_EOF) {
+ sd->last_pts = 0;
+ if (gspca_dev->pixfmt == V4L2_PIX_FMT_YUYV
+ && gspca_dev->image_len + len - 12 !=
+ gspca_dev->width * gspca_dev->height * 2) {
+ PDEBUG(D_PACK, "wrong sized frame");
+ goto discard;
+ }
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ data + 12, len - 12);
+ } else {
+
+ /* Add the data from this payload */
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data + 12, len - 12);
+ }
+
+ /* Done this payload */
+ goto scan_next;
+
+discard:
+ /* Discard data until a new frame starts. */
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+
+scan_next:
+ remaining_len -= len;
+ data += len;
+ } while (remaining_len > 0);
+}
+
+static int sd_setagc(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->ctrls[AGC].val = val;
+
+ /* the auto white balance control works only
+ * when auto gain is set */
+ if (val) {
+ gspca_dev->ctrl_inac &= ~(1 << AWB);
+ } else {
+ gspca_dev->ctrl_inac |= (1 << AWB);
+ if (sd->ctrls[AWB].val) {
+ sd->ctrls[AWB].val = 0;
+ if (gspca_dev->streaming)
+ setawb(gspca_dev);
+ }
+ }
+ if (gspca_dev->streaming)
+ setagc(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_querymenu(struct gspca_dev *gspca_dev,
+ struct v4l2_querymenu *menu)
+{
+ switch (menu->id) {
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ switch (menu->index) {
+ case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
+ strcpy((char *) menu->name, "Disabled");
+ return 0;
+ case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
+ strcpy((char *) menu->name, "50 Hz");
+ return 0;
+ }
+ break;
+ }
+
+ return -EINVAL;
+}
+
+/* get stream parameters (framerate) */
+static void sd_get_streamparm(struct gspca_dev *gspca_dev,
+ struct v4l2_streamparm *parm)
+{
+ struct v4l2_captureparm *cp = &parm->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ cp->capability |= V4L2_CAP_TIMEPERFRAME;
+ tpf->numerator = 1;
+ tpf->denominator = sd->frame_rate;
+}
+
+/* set stream parameters (framerate) */
+static void sd_set_streamparm(struct gspca_dev *gspca_dev,
+ struct v4l2_streamparm *parm)
+{
+ struct v4l2_captureparm *cp = &parm->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* Set requested framerate */
+ sd->frame_rate = tpf->denominator / tpf->numerator;
+ if (gspca_dev->streaming)
+ set_frame_rate(gspca_dev);
+
+ /* Return the actual framerate */
+ tpf->numerator = 1;
+ tpf->denominator = sd->frame_rate;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+ .querymenu = sd_querymenu,
+ .get_streamparm = sd_get_streamparm,
+ .set_streamparm = sd_set_streamparm,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x1415, 0x2000)},
+ {USB_DEVICE(0x06f8, 0x3002)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/ov534_9.c b/drivers/media/video/gspca/ov534_9.c
new file mode 100644
index 00000000..e6601b88
--- /dev/null
+++ b/drivers/media/video/gspca/ov534_9.c
@@ -0,0 +1,1605 @@
+/*
+ * ov534-ov9xxx gspca driver
+ *
+ * Copyright (C) 2009-2011 Jean-Francois Moine http://moinejf.free.fr
+ * Copyright (C) 2008 Antonio Ospite <ospite@studenti.unina.it>
+ * Copyright (C) 2008 Jim Paris <jim@jtan.com>
+ *
+ * Based on a prototype written by Mark Ferrell <majortrips@gmail.com>
+ * USB protocol reverse engineered by Jim Paris <jim@jtan.com>
+ * https://jim.sh/svn/jim/devl/playstation/ps3/eye/test/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "ov534_9"
+
+#include "gspca.h"
+
+#define OV534_REG_ADDRESS 0xf1 /* sensor address */
+#define OV534_REG_SUBADDR 0xf2
+#define OV534_REG_WRITE 0xf3
+#define OV534_REG_READ 0xf4
+#define OV534_REG_OPERATION 0xf5
+#define OV534_REG_STATUS 0xf6
+
+#define OV534_OP_WRITE_3 0x37
+#define OV534_OP_WRITE_2 0x33
+#define OV534_OP_READ_2 0xf9
+
+#define CTRL_TIMEOUT 500
+
+MODULE_AUTHOR("Jean-Francois Moine <moinejf@free.fr>");
+MODULE_DESCRIPTION("GSPCA/OV534_9 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* controls */
+enum e_ctrl {
+ BRIGHTNESS,
+ CONTRAST,
+ AUTOGAIN,
+ EXPOSURE,
+ SHARPNESS,
+ SATUR,
+ LIGHTFREQ,
+ NCTRLS /* number of controls */
+};
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ struct gspca_ctrl ctrls[NCTRLS];
+ __u32 last_pts;
+ u8 last_fid;
+
+ u8 sensor;
+};
+enum sensors {
+ SENSOR_OV965x, /* ov9657 */
+ SENSOR_OV971x, /* ov9712 */
+ SENSOR_OV562x, /* ov5621 */
+ NSENSORS
+};
+
+/* V4L2 controls supported by the driver */
+static void setbrightness(struct gspca_dev *gspca_dev);
+static void setcontrast(struct gspca_dev *gspca_dev);
+static void setautogain(struct gspca_dev *gspca_dev);
+static void setexposure(struct gspca_dev *gspca_dev);
+static void setsharpness(struct gspca_dev *gspca_dev);
+static void setsatur(struct gspca_dev *gspca_dev);
+static void setlightfreq(struct gspca_dev *gspca_dev);
+
+static const struct ctrl sd_ctrls[NCTRLS] = {
+[BRIGHTNESS] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 15,
+ .step = 1,
+ .default_value = 7
+ },
+ .set_control = setbrightness
+ },
+[CONTRAST] = {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 15,
+ .step = 1,
+ .default_value = 3
+ },
+ .set_control = setcontrast
+ },
+[AUTOGAIN] = {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Autogain",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define AUTOGAIN_DEF 1
+ .default_value = AUTOGAIN_DEF,
+ },
+ .set_control = setautogain
+ },
+[EXPOSURE] = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 0,
+ .maximum = 3,
+ .step = 1,
+ .default_value = 0
+ },
+ .set_control = setexposure
+ },
+[SHARPNESS] = {
+ {
+ .id = V4L2_CID_SHARPNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Sharpness",
+ .minimum = -1, /* -1 = auto */
+ .maximum = 4,
+ .step = 1,
+ .default_value = -1
+ },
+ .set_control = setsharpness
+ },
+[SATUR] = {
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 4,
+ .step = 1,
+ .default_value = 2
+ },
+ .set_control = setsatur
+ },
+[LIGHTFREQ] = {
+ {
+ .id = V4L2_CID_POWER_LINE_FREQUENCY,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Light frequency filter",
+ .minimum = 0,
+ .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */
+ .step = 1,
+ .default_value = 0
+ },
+ .set_control = setlightfreq
+ },
+};
+
+static const struct v4l2_pix_format ov965x_mode[] = {
+#define QVGA_MODE 0
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+#define VGA_MODE 1
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+#define SVGA_MODE 2
+ {800, 600, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 800,
+ .sizeimage = 800 * 600 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+#define XGA_MODE 3
+ {1024, 768, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 1024,
+ .sizeimage = 1024 * 768 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+#define SXGA_MODE 4
+ {1280, 1024, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 1280,
+ .sizeimage = 1280 * 1024 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+};
+
+static const struct v4l2_pix_format ov971x_mode[] = {
+ {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB
+ }
+};
+
+static const struct v4l2_pix_format ov562x_mode[] = {
+ {2592, 1680, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 2592,
+ .sizeimage = 2592 * 1680,
+ .colorspace = V4L2_COLORSPACE_SRGB
+ }
+};
+
+static const u8 bridge_init[][2] = {
+ {0x88, 0xf8},
+ {0x89, 0xff},
+ {0x76, 0x03},
+ {0x92, 0x03},
+ {0x95, 0x10},
+ {0xe2, 0x00},
+ {0xe7, 0x3e},
+ {0x8d, 0x1c},
+ {0x8e, 0x00},
+ {0x8f, 0x00},
+ {0x1f, 0x00},
+ {0xc3, 0xf9},
+ {0x89, 0xff},
+ {0x88, 0xf8},
+ {0x76, 0x03},
+ {0x92, 0x01},
+ {0x93, 0x18},
+ {0x1c, 0x0a},
+ {0x1d, 0x48},
+ {0xc0, 0x50},
+ {0xc1, 0x3c},
+ {0x34, 0x05},
+ {0xc2, 0x0c},
+ {0xc3, 0xf9},
+ {0x34, 0x05},
+ {0xe7, 0x2e},
+ {0x31, 0xf9},
+ {0x35, 0x02},
+ {0xd9, 0x10},
+ {0x25, 0x42},
+ {0x94, 0x11},
+};
+
+static const u8 ov965x_init[][2] = {
+ {0x12, 0x80}, /* com7 - SSCB reset */
+ {0x00, 0x00}, /* gain */
+ {0x01, 0x80}, /* blue */
+ {0x02, 0x80}, /* red */
+ {0x03, 0x1b}, /* vref */
+ {0x04, 0x03}, /* com1 - exposure low bits */
+ {0x0b, 0x57}, /* ver */
+ {0x0e, 0x61}, /* com5 */
+ {0x0f, 0x42}, /* com6 */
+ {0x11, 0x00}, /* clkrc */
+ {0x12, 0x02}, /* com7 - 15fps VGA YUYV */
+ {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
+ {0x14, 0x28}, /* com9 */
+ {0x16, 0x24}, /* reg16 */
+ {0x17, 0x1d}, /* hstart*/
+ {0x18, 0xbd}, /* hstop */
+ {0x19, 0x01}, /* vstrt */
+ {0x1a, 0x81}, /* vstop*/
+ {0x1e, 0x04}, /* mvfp */
+ {0x24, 0x3c}, /* aew */
+ {0x25, 0x36}, /* aeb */
+ {0x26, 0x71}, /* vpt */
+ {0x27, 0x08}, /* bbias */
+ {0x28, 0x08}, /* gbbias */
+ {0x29, 0x15}, /* gr com */
+ {0x2a, 0x00}, /* exhch */
+ {0x2b, 0x00}, /* exhcl */
+ {0x2c, 0x08}, /* rbias */
+ {0x32, 0xff}, /* href */
+ {0x33, 0x00}, /* chlf */
+ {0x34, 0x3f}, /* aref1 */
+ {0x35, 0x00}, /* aref2 */
+ {0x36, 0xf8}, /* aref3 */
+ {0x38, 0x72}, /* adc2 */
+ {0x39, 0x57}, /* aref4 */
+ {0x3a, 0x80}, /* tslb - yuyv */
+ {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */
+ {0x3d, 0x99}, /* com13 */
+ {0x3f, 0xc1}, /* edge */
+ {0x40, 0xc0}, /* com15 */
+ {0x41, 0x40}, /* com16 */
+ {0x42, 0xc0}, /* com17 */
+ {0x43, 0x0a}, /* rsvd */
+ {0x44, 0xf0},
+ {0x45, 0x46},
+ {0x46, 0x62},
+ {0x47, 0x2a},
+ {0x48, 0x3c},
+ {0x4a, 0xfc},
+ {0x4b, 0xfc},
+ {0x4c, 0x7f},
+ {0x4d, 0x7f},
+ {0x4e, 0x7f},
+ {0x4f, 0x98}, /* matrix */
+ {0x50, 0x98},
+ {0x51, 0x00},
+ {0x52, 0x28},
+ {0x53, 0x70},
+ {0x54, 0x98},
+ {0x58, 0x1a}, /* matrix coef sign */
+ {0x59, 0x85}, /* AWB control */
+ {0x5a, 0xa9},
+ {0x5b, 0x64},
+ {0x5c, 0x84},
+ {0x5d, 0x53},
+ {0x5e, 0x0e},
+ {0x5f, 0xf0}, /* AWB blue limit */
+ {0x60, 0xf0}, /* AWB red limit */
+ {0x61, 0xf0}, /* AWB green limit */
+ {0x62, 0x00}, /* lcc1 */
+ {0x63, 0x00}, /* lcc2 */
+ {0x64, 0x02}, /* lcc3 */
+ {0x65, 0x16}, /* lcc4 */
+ {0x66, 0x01}, /* lcc5 */
+ {0x69, 0x02}, /* hv */
+ {0x6b, 0x5a}, /* dbvl */
+ {0x6c, 0x04},
+ {0x6d, 0x55},
+ {0x6e, 0x00},
+ {0x6f, 0x9d},
+ {0x70, 0x21}, /* dnsth */
+ {0x71, 0x78},
+ {0x72, 0x00}, /* poidx */
+ {0x73, 0x01}, /* pckdv */
+ {0x74, 0x3a}, /* xindx */
+ {0x75, 0x35}, /* yindx */
+ {0x76, 0x01},
+ {0x77, 0x02},
+ {0x7a, 0x12}, /* gamma curve */
+ {0x7b, 0x08},
+ {0x7c, 0x16},
+ {0x7d, 0x30},
+ {0x7e, 0x5e},
+ {0x7f, 0x72},
+ {0x80, 0x82},
+ {0x81, 0x8e},
+ {0x82, 0x9a},
+ {0x83, 0xa4},
+ {0x84, 0xac},
+ {0x85, 0xb8},
+ {0x86, 0xc3},
+ {0x87, 0xd6},
+ {0x88, 0xe6},
+ {0x89, 0xf2},
+ {0x8a, 0x03},
+ {0x8c, 0x89}, /* com19 */
+ {0x14, 0x28}, /* com9 */
+ {0x90, 0x7d},
+ {0x91, 0x7b},
+ {0x9d, 0x03}, /* lcc6 */
+ {0x9e, 0x04}, /* lcc7 */
+ {0x9f, 0x7a},
+ {0xa0, 0x79},
+ {0xa1, 0x40}, /* aechm */
+ {0xa4, 0x50}, /* com21 */
+ {0xa5, 0x68}, /* com26 */
+ {0xa6, 0x4a}, /* AWB green */
+ {0xa8, 0xc1}, /* refa8 */
+ {0xa9, 0xef}, /* refa9 */
+ {0xaa, 0x92},
+ {0xab, 0x04},
+ {0xac, 0x80}, /* black level control */
+ {0xad, 0x80},
+ {0xae, 0x80},
+ {0xaf, 0x80},
+ {0xb2, 0xf2},
+ {0xb3, 0x20},
+ {0xb4, 0x20}, /* ctrlb4 */
+ {0xb5, 0x00},
+ {0xb6, 0xaf},
+ {0xbb, 0xae},
+ {0xbc, 0x7f}, /* ADC channel offsets */
+ {0xdb, 0x7f},
+ {0xbe, 0x7f},
+ {0xbf, 0x7f},
+ {0xc0, 0xe2},
+ {0xc1, 0xc0},
+ {0xc2, 0x01},
+ {0xc3, 0x4e},
+ {0xc6, 0x85},
+ {0xc7, 0x80}, /* com24 */
+ {0xc9, 0xe0},
+ {0xca, 0xe8},
+ {0xcb, 0xf0},
+ {0xcc, 0xd8},
+ {0xcd, 0xf1},
+ {0x4f, 0x98}, /* matrix */
+ {0x50, 0x98},
+ {0x51, 0x00},
+ {0x52, 0x28},
+ {0x53, 0x70},
+ {0x54, 0x98},
+ {0x58, 0x1a},
+ {0xff, 0x41}, /* read 41, write ff 00 */
+ {0x41, 0x40}, /* com16 */
+
+ {0xc5, 0x03}, /* 60 Hz banding filter */
+ {0x6a, 0x02}, /* 50 Hz banding filter */
+
+ {0x12, 0x62}, /* com7 - 30fps VGA YUV */
+ {0x36, 0xfa}, /* aref3 */
+ {0x69, 0x0a}, /* hv */
+ {0x8c, 0x89}, /* com22 */
+ {0x14, 0x28}, /* com9 */
+ {0x3e, 0x0c},
+ {0x41, 0x40}, /* com16 */
+ {0x72, 0x00},
+ {0x73, 0x00},
+ {0x74, 0x3a},
+ {0x75, 0x35},
+ {0x76, 0x01},
+ {0xc7, 0x80},
+ {0x03, 0x12}, /* vref */
+ {0x17, 0x16}, /* hstart */
+ {0x18, 0x02}, /* hstop */
+ {0x19, 0x01}, /* vstrt */
+ {0x1a, 0x3d}, /* vstop */
+ {0x32, 0xff}, /* href */
+ {0xc0, 0xaa},
+};
+
+static const u8 bridge_init_2[][2] = {
+ {0x94, 0xaa},
+ {0xf1, 0x60},
+ {0xe5, 0x04},
+ {0xc0, 0x50},
+ {0xc1, 0x3c},
+ {0x8c, 0x00},
+ {0x8d, 0x1c},
+ {0x34, 0x05},
+
+ {0xc2, 0x0c},
+ {0xc3, 0xf9},
+ {0xda, 0x01},
+ {0x50, 0x00},
+ {0x51, 0xa0},
+ {0x52, 0x3c},
+ {0x53, 0x00},
+ {0x54, 0x00},
+ {0x55, 0x00},
+ {0x57, 0x00},
+ {0x5c, 0x00},
+ {0x5a, 0xa0},
+ {0x5b, 0x78},
+ {0x35, 0x02},
+ {0xd9, 0x10},
+ {0x94, 0x11},
+};
+
+static const u8 ov965x_init_2[][2] = {
+ {0x3b, 0xc4},
+ {0x1e, 0x04}, /* mvfp */
+ {0x13, 0xe0}, /* com8 */
+ {0x00, 0x00}, /* gain */
+ {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
+ {0x11, 0x03}, /* clkrc */
+ {0x6b, 0x5a}, /* dblv */
+ {0x6a, 0x05},
+ {0xc5, 0x07},
+ {0xa2, 0x4b},
+ {0xa3, 0x3e},
+ {0x2d, 0x00},
+ {0xff, 0x42}, /* read 42, write ff 00 */
+ {0x42, 0xc0}, /* com17 */
+ {0x2d, 0x00},
+ {0xff, 0x42}, /* read 42, write ff 00 */
+ {0x42, 0xc1}, /* com17 */
+/* sharpness */
+ {0x3f, 0x01},
+ {0xff, 0x42}, /* read 42, write ff 00 */
+ {0x42, 0xc1}, /* com17 */
+/* saturation */
+ {0x4f, 0x98}, /* matrix */
+ {0x50, 0x98},
+ {0x51, 0x00},
+ {0x52, 0x28},
+ {0x53, 0x70},
+ {0x54, 0x98},
+ {0x58, 0x1a},
+ {0xff, 0x41}, /* read 41, write ff 00 */
+ {0x41, 0x40}, /* com16 */
+/* contrast */
+ {0x56, 0x40},
+/* brightness */
+ {0x55, 0x8f},
+/* expo */
+ {0x10, 0x25}, /* aech - exposure high bits */
+ {0xff, 0x13}, /* read 13, write ff 00 */
+ {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
+};
+
+static const u8 ov971x_init[][2] = {
+ {0x12, 0x80},
+ {0x09, 0x10},
+ {0x1e, 0x07},
+ {0x5f, 0x18},
+ {0x69, 0x04},
+ {0x65, 0x2a},
+ {0x68, 0x0a},
+ {0x39, 0x28},
+ {0x4d, 0x90},
+ {0xc1, 0x80},
+ {0x0c, 0x30},
+ {0x6d, 0x02},
+ {0x96, 0xf1},
+ {0xbc, 0x68},
+ {0x12, 0x00},
+ {0x3b, 0x00},
+ {0x97, 0x80},
+ {0x17, 0x25},
+ {0x18, 0xa2},
+ {0x19, 0x01},
+ {0x1a, 0xca},
+ {0x03, 0x0a},
+ {0x32, 0x07},
+ {0x98, 0x40}, /*{0x98, 0x00},*/
+ {0x99, 0xA0}, /*{0x99, 0x00},*/
+ {0x9a, 0x01}, /*{0x9a, 0x00},*/
+ {0x57, 0x00},
+ {0x58, 0x78}, /*{0x58, 0xc8},*/
+ {0x59, 0x50}, /*{0x59, 0xa0},*/
+ {0x4c, 0x13},
+ {0x4b, 0x36},
+ {0x3d, 0x3c},
+ {0x3e, 0x03},
+ {0xbd, 0x50}, /*{0xbd, 0xa0},*/
+ {0xbe, 0x78}, /*{0xbe, 0xc8},*/
+ {0x4e, 0x55},
+ {0x4f, 0x55},
+ {0x50, 0x55},
+ {0x51, 0x55},
+ {0x24, 0x55},
+ {0x25, 0x40},
+ {0x26, 0xa1},
+ {0x5c, 0x59},
+ {0x5d, 0x00},
+ {0x11, 0x00},
+ {0x2a, 0x98},
+ {0x2b, 0x06},
+ {0x2d, 0x00},
+ {0x2e, 0x00},
+ {0x13, 0xa5},
+ {0x14, 0x40},
+ {0x4a, 0x00},
+ {0x49, 0xce},
+ {0x22, 0x03},
+ {0x09, 0x00}
+};
+
+static const u8 ov965x_start_1_vga[][2] = { /* same for qvga */
+ {0x12, 0x62}, /* com7 - 30fps VGA YUV */
+ {0x36, 0xfa}, /* aref3 */
+ {0x69, 0x0a}, /* hv */
+ {0x8c, 0x89}, /* com22 */
+ {0x14, 0x28}, /* com9 */
+ {0x3e, 0x0c}, /* com14 */
+ {0x41, 0x40}, /* com16 */
+ {0x72, 0x00},
+ {0x73, 0x00},
+ {0x74, 0x3a},
+ {0x75, 0x35},
+ {0x76, 0x01},
+ {0xc7, 0x80}, /* com24 */
+ {0x03, 0x12}, /* vref */
+ {0x17, 0x16}, /* hstart */
+ {0x18, 0x02}, /* hstop */
+ {0x19, 0x01}, /* vstrt */
+ {0x1a, 0x3d}, /* vstop */
+ {0x32, 0xff}, /* href */
+ {0xc0, 0xaa},
+};
+
+static const u8 ov965x_start_1_svga[][2] = {
+ {0x12, 0x02}, /* com7 - YUYV - VGA 15 full resolution */
+ {0x36, 0xf8}, /* aref3 */
+ {0x69, 0x02}, /* hv */
+ {0x8c, 0x0d}, /* com22 */
+ {0x3e, 0x0c}, /* com14 */
+ {0x41, 0x40}, /* com16 */
+ {0x72, 0x00},
+ {0x73, 0x01},
+ {0x74, 0x3a},
+ {0x75, 0x35},
+ {0x76, 0x01},
+ {0xc7, 0x80}, /* com24 */
+ {0x03, 0x1b}, /* vref */
+ {0x17, 0x1d}, /* hstart */
+ {0x18, 0xbd}, /* hstop */
+ {0x19, 0x01}, /* vstrt */
+ {0x1a, 0x81}, /* vstop */
+ {0x32, 0xff}, /* href */
+ {0xc0, 0xe2},
+};
+
+static const u8 ov965x_start_1_xga[][2] = {
+ {0x12, 0x02}, /* com7 */
+ {0x36, 0xf8}, /* aref3 */
+ {0x69, 0x02}, /* hv */
+ {0x8c, 0x89}, /* com22 */
+ {0x14, 0x28}, /* com9 */
+ {0x3e, 0x0c}, /* com14 */
+ {0x41, 0x40}, /* com16 */
+ {0x72, 0x00},
+ {0x73, 0x01},
+ {0x74, 0x3a},
+ {0x75, 0x35},
+ {0x76, 0x01},
+ {0xc7, 0x80}, /* com24 */
+ {0x03, 0x1b}, /* vref */
+ {0x17, 0x1d}, /* hstart */
+ {0x18, 0xbd}, /* hstop */
+ {0x19, 0x01}, /* vstrt */
+ {0x1a, 0x81}, /* vstop */
+ {0x32, 0xff}, /* href */
+ {0xc0, 0xe2},
+};
+
+static const u8 ov965x_start_1_sxga[][2] = {
+ {0x12, 0x02}, /* com7 */
+ {0x36, 0xf8}, /* aref3 */
+ {0x69, 0x02}, /* hv */
+ {0x8c, 0x89}, /* com22 */
+ {0x14, 0x28}, /* com9 */
+ {0x3e, 0x0c}, /* com14 */
+ {0x41, 0x40}, /* com16 */
+ {0x72, 0x00},
+ {0x73, 0x01},
+ {0x74, 0x3a},
+ {0x75, 0x35},
+ {0x76, 0x01},
+ {0xc7, 0x80}, /* com24 */
+ {0x03, 0x1b}, /* vref */
+ {0x17, 0x1d}, /* hstart */
+ {0x18, 0x02}, /* hstop */
+ {0x19, 0x01}, /* vstrt */
+ {0x1a, 0x81}, /* vstop */
+ {0x32, 0xff}, /* href */
+ {0xc0, 0xe2},
+};
+
+static const u8 bridge_start_qvga[][2] = {
+ {0x94, 0xaa},
+ {0xf1, 0x60},
+ {0xe5, 0x04},
+ {0xc0, 0x50},
+ {0xc1, 0x3c},
+ {0x8c, 0x00},
+ {0x8d, 0x1c},
+ {0x34, 0x05},
+
+ {0xc2, 0x4c},
+ {0xc3, 0xf9},
+ {0xda, 0x00},
+ {0x50, 0x00},
+ {0x51, 0xa0},
+ {0x52, 0x78},
+ {0x53, 0x00},
+ {0x54, 0x00},
+ {0x55, 0x00},
+ {0x57, 0x00},
+ {0x5c, 0x00},
+ {0x5a, 0x50},
+ {0x5b, 0x3c},
+ {0x35, 0x02},
+ {0xd9, 0x10},
+ {0x94, 0x11},
+};
+
+static const u8 bridge_start_vga[][2] = {
+ {0x94, 0xaa},
+ {0xf1, 0x60},
+ {0xe5, 0x04},
+ {0xc0, 0x50},
+ {0xc1, 0x3c},
+ {0x8c, 0x00},
+ {0x8d, 0x1c},
+ {0x34, 0x05},
+ {0xc2, 0x0c},
+ {0xc3, 0xf9},
+ {0xda, 0x01},
+ {0x50, 0x00},
+ {0x51, 0xa0},
+ {0x52, 0x3c},
+ {0x53, 0x00},
+ {0x54, 0x00},
+ {0x55, 0x00},
+ {0x57, 0x00},
+ {0x5c, 0x00},
+ {0x5a, 0xa0},
+ {0x5b, 0x78},
+ {0x35, 0x02},
+ {0xd9, 0x10},
+ {0x94, 0x11},
+};
+
+static const u8 bridge_start_svga[][2] = {
+ {0x94, 0xaa},
+ {0xf1, 0x60},
+ {0xe5, 0x04},
+ {0xc0, 0xa0},
+ {0xc1, 0x80},
+ {0x8c, 0x00},
+ {0x8d, 0x1c},
+ {0x34, 0x05},
+ {0xc2, 0x4c},
+ {0xc3, 0xf9},
+ {0x50, 0x00},
+ {0x51, 0x40},
+ {0x52, 0x00},
+ {0x53, 0x00},
+ {0x54, 0x00},
+ {0x55, 0x88},
+ {0x57, 0x00},
+ {0x5c, 0x00},
+ {0x5a, 0xc8},
+ {0x5b, 0x96},
+ {0x35, 0x02},
+ {0xd9, 0x10},
+ {0xda, 0x00},
+ {0x94, 0x11},
+};
+
+static const u8 bridge_start_xga[][2] = {
+ {0x94, 0xaa},
+ {0xf1, 0x60},
+ {0xe5, 0x04},
+ {0xc0, 0xa0},
+ {0xc1, 0x80},
+ {0x8c, 0x00},
+ {0x8d, 0x1c},
+ {0x34, 0x05},
+ {0xc2, 0x4c},
+ {0xc3, 0xf9},
+ {0x50, 0x00},
+ {0x51, 0x40},
+ {0x52, 0x00},
+ {0x53, 0x00},
+ {0x54, 0x00},
+ {0x55, 0x88},
+ {0x57, 0x00},
+ {0x5c, 0x01},
+ {0x5a, 0x00},
+ {0x5b, 0xc0},
+ {0x35, 0x02},
+ {0xd9, 0x10},
+ {0xda, 0x01},
+ {0x94, 0x11},
+};
+
+static const u8 bridge_start_sxga[][2] = {
+ {0x94, 0xaa},
+ {0xf1, 0x60},
+ {0xe5, 0x04},
+ {0xc0, 0xa0},
+ {0xc1, 0x80},
+ {0x8c, 0x00},
+ {0x8d, 0x1c},
+ {0x34, 0x05},
+ {0xc2, 0x0c},
+ {0xc3, 0xf9},
+ {0xda, 0x00},
+ {0x35, 0x02},
+ {0xd9, 0x10},
+ {0x94, 0x11},
+};
+
+static const u8 ov965x_start_2_qvga[][2] = {
+ {0x3b, 0xe4}, /* com11 - night mode 1/4 frame rate */
+ {0x1e, 0x04}, /* mvfp */
+ {0x13, 0xe0}, /* com8 */
+ {0x00, 0x00},
+ {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
+ {0x11, 0x01}, /* clkrc */
+ {0x6b, 0x5a}, /* dblv */
+ {0x6a, 0x02}, /* 50 Hz banding filter */
+ {0xc5, 0x03}, /* 60 Hz banding filter */
+ {0xa2, 0x96}, /* bd50 */
+ {0xa3, 0x7d}, /* bd60 */
+
+ {0xff, 0x13}, /* read 13, write ff 00 */
+ {0x13, 0xe7},
+ {0x3a, 0x80}, /* tslb - yuyv */
+};
+
+static const u8 ov965x_start_2_vga[][2] = {
+ {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */
+ {0x1e, 0x04}, /* mvfp */
+ {0x13, 0xe0}, /* com8 */
+ {0x00, 0x00},
+ {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
+ {0x11, 0x03}, /* clkrc */
+ {0x6b, 0x5a}, /* dblv */
+ {0x6a, 0x05}, /* 50 Hz banding filter */
+ {0xc5, 0x07}, /* 60 Hz banding filter */
+ {0xa2, 0x4b}, /* bd50 */
+ {0xa3, 0x3e}, /* bd60 */
+
+ {0x2d, 0x00}, /* advfl */
+};
+
+static const u8 ov965x_start_2_svga[][2] = { /* same for xga */
+ {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */
+ {0x1e, 0x04}, /* mvfp */
+ {0x13, 0xe0}, /* com8 */
+ {0x00, 0x00},
+ {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
+ {0x11, 0x01}, /* clkrc */
+ {0x6b, 0x5a}, /* dblv */
+ {0x6a, 0x0c}, /* 50 Hz banding filter */
+ {0xc5, 0x0f}, /* 60 Hz banding filter */
+ {0xa2, 0x4e}, /* bd50 */
+ {0xa3, 0x41}, /* bd60 */
+};
+
+static const u8 ov965x_start_2_sxga[][2] = {
+ {0x13, 0xe0}, /* com8 */
+ {0x00, 0x00},
+ {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
+ {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */
+ {0x1e, 0x04}, /* mvfp */
+ {0x11, 0x01}, /* clkrc */
+ {0x6b, 0x5a}, /* dblv */
+ {0x6a, 0x0c}, /* 50 Hz banding filter */
+ {0xc5, 0x0f}, /* 60 Hz banding filter */
+ {0xa2, 0x4e}, /* bd50 */
+ {0xa3, 0x41}, /* bd60 */
+};
+
+static const u8 ov562x_init[][2] = {
+ {0x88, 0x20},
+ {0x89, 0x0a},
+ {0x8a, 0x90},
+ {0x8b, 0x06},
+ {0x8c, 0x01},
+ {0x8d, 0x10},
+ {0x1c, 0x00},
+ {0x1d, 0x48},
+ {0x1d, 0x00},
+ {0x1d, 0xff},
+ {0x1c, 0x0a},
+ {0x1d, 0x2e},
+ {0x1d, 0x1e},
+};
+
+static const u8 ov562x_init_2[][2] = {
+ {0x12, 0x80},
+ {0x11, 0x41},
+ {0x13, 0x00},
+ {0x10, 0x1e},
+ {0x3b, 0x07},
+ {0x5b, 0x40},
+ {0x39, 0x07},
+ {0x53, 0x02},
+ {0x54, 0x60},
+ {0x04, 0x20},
+ {0x27, 0x04},
+ {0x3d, 0x40},
+ {0x36, 0x00},
+ {0xc5, 0x04},
+ {0x4e, 0x00},
+ {0x4f, 0x93},
+ {0x50, 0x7b},
+ {0xca, 0x0c},
+ {0xcb, 0x0f},
+ {0x39, 0x07},
+ {0x4a, 0x10},
+ {0x3e, 0x0a},
+ {0x3d, 0x00},
+ {0x0c, 0x38},
+ {0x38, 0x90},
+ {0x46, 0x30},
+ {0x4f, 0x93},
+ {0x50, 0x7b},
+ {0xab, 0x00},
+ {0xca, 0x0c},
+ {0xcb, 0x0f},
+ {0x37, 0x02},
+ {0x44, 0x48},
+ {0x8d, 0x44},
+ {0x2a, 0x00},
+ {0x2b, 0x00},
+ {0x32, 0x00},
+ {0x38, 0x90},
+ {0x53, 0x02},
+ {0x54, 0x60},
+ {0x12, 0x00},
+ {0x17, 0x12},
+ {0x18, 0xb4},
+ {0x19, 0x0c},
+ {0x1a, 0xf4},
+ {0x03, 0x4a},
+ {0x89, 0x20},
+ {0x83, 0x80},
+ {0xb7, 0x9d},
+ {0xb6, 0x11},
+ {0xb5, 0x55},
+ {0xb4, 0x00},
+ {0xa9, 0xf0},
+ {0xa8, 0x0a},
+ {0xb8, 0xf0},
+ {0xb9, 0xf0},
+ {0xba, 0xf0},
+ {0x81, 0x07},
+ {0x63, 0x44},
+ {0x13, 0xc7},
+ {0x14, 0x60},
+ {0x33, 0x75},
+ {0x2c, 0x00},
+ {0x09, 0x00},
+ {0x35, 0x30},
+ {0x27, 0x04},
+ {0x3c, 0x07},
+ {0x3a, 0x0a},
+ {0x3b, 0x07},
+ {0x01, 0x40},
+ {0x02, 0x40},
+ {0x16, 0x40},
+ {0x52, 0xb0},
+ {0x51, 0x83},
+ {0x21, 0xbb},
+ {0x22, 0x10},
+ {0x23, 0x03},
+ {0x35, 0x38},
+ {0x20, 0x90},
+ {0x28, 0x30},
+ {0x73, 0xe1},
+ {0x6c, 0x00},
+ {0x6d, 0x80},
+ {0x6e, 0x00},
+ {0x70, 0x04},
+ {0x71, 0x00},
+ {0x8d, 0x04},
+ {0x64, 0x00},
+ {0x65, 0x00},
+ {0x66, 0x00},
+ {0x67, 0x00},
+ {0x68, 0x00},
+ {0x69, 0x00},
+ {0x6a, 0x00},
+ {0x6b, 0x00},
+ {0x71, 0x94},
+ {0x74, 0x20},
+ {0x80, 0x09},
+ {0x85, 0xc0},
+};
+
+static void reg_w_i(struct gspca_dev *gspca_dev, u16 reg, u8 val)
+{
+ struct usb_device *udev = gspca_dev->dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ gspca_dev->usb_buf[0] = val;
+ ret = usb_control_msg(udev,
+ usb_sndctrlpipe(udev, 0),
+ 0x01,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
+ if (ret < 0) {
+ pr_err("reg_w failed %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static void reg_w(struct gspca_dev *gspca_dev, u16 reg, u8 val)
+{
+ PDEBUG(D_USBO, "reg_w [%04x] = %02x", reg, val);
+ reg_w_i(gspca_dev, reg, val);
+}
+
+static u8 reg_r(struct gspca_dev *gspca_dev, u16 reg)
+{
+ struct usb_device *udev = gspca_dev->dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return 0;
+ ret = usb_control_msg(udev,
+ usb_rcvctrlpipe(udev, 0),
+ 0x01,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
+ PDEBUG(D_USBI, "reg_r [%04x] -> %02x", reg, gspca_dev->usb_buf[0]);
+ if (ret < 0) {
+ pr_err("reg_r err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+ return gspca_dev->usb_buf[0];
+}
+
+static int sccb_check_status(struct gspca_dev *gspca_dev)
+{
+ u8 data;
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ data = reg_r(gspca_dev, OV534_REG_STATUS);
+
+ switch (data) {
+ case 0x00:
+ return 1;
+ case 0x04:
+ return 0;
+ case 0x03:
+ break;
+ default:
+ PDEBUG(D_USBI|D_USBO,
+ "sccb status 0x%02x, attempt %d/5",
+ data, i + 1);
+ }
+ }
+ return 0;
+}
+
+static void sccb_write(struct gspca_dev *gspca_dev, u8 reg, u8 val)
+{
+ PDEBUG(D_USBO, "sccb_write [%02x] = %02x", reg, val);
+ reg_w_i(gspca_dev, OV534_REG_SUBADDR, reg);
+ reg_w_i(gspca_dev, OV534_REG_WRITE, val);
+ reg_w_i(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3);
+
+ if (!sccb_check_status(gspca_dev))
+ pr_err("sccb_write failed\n");
+}
+
+static u8 sccb_read(struct gspca_dev *gspca_dev, u16 reg)
+{
+ reg_w(gspca_dev, OV534_REG_SUBADDR, reg);
+ reg_w(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2);
+ if (!sccb_check_status(gspca_dev))
+ pr_err("sccb_read failed 1\n");
+
+ reg_w(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2);
+ if (!sccb_check_status(gspca_dev))
+ pr_err("sccb_read failed 2\n");
+
+ return reg_r(gspca_dev, OV534_REG_READ);
+}
+
+/* output a bridge sequence (reg - val) */
+static void reg_w_array(struct gspca_dev *gspca_dev,
+ const u8 (*data)[2], int len)
+{
+ while (--len >= 0) {
+ reg_w(gspca_dev, (*data)[0], (*data)[1]);
+ data++;
+ }
+}
+
+/* output a sensor sequence (reg - val) */
+static void sccb_w_array(struct gspca_dev *gspca_dev,
+ const u8 (*data)[2], int len)
+{
+ while (--len >= 0) {
+ if ((*data)[0] != 0xff) {
+ sccb_write(gspca_dev, (*data)[0], (*data)[1]);
+ } else {
+ sccb_read(gspca_dev, (*data)[1]);
+ sccb_write(gspca_dev, 0xff, 0x00);
+ }
+ data++;
+ }
+}
+
+/* Two bits control LED: 0x21 bit 7 and 0x23 bit 7.
+ * (direction and output)? */
+static void set_led(struct gspca_dev *gspca_dev, int status)
+{
+ u8 data;
+
+ PDEBUG(D_CONF, "led status: %d", status);
+
+ data = reg_r(gspca_dev, 0x21);
+ data |= 0x80;
+ reg_w(gspca_dev, 0x21, data);
+
+ data = reg_r(gspca_dev, 0x23);
+ if (status)
+ data |= 0x80;
+ else
+ data &= ~0x80;
+
+ reg_w(gspca_dev, 0x23, data);
+
+ if (!status) {
+ data = reg_r(gspca_dev, 0x21);
+ data &= ~0x80;
+ reg_w(gspca_dev, 0x21, data);
+ }
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
+ s8 sval;
+
+ if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS))
+ return;
+ if (sd->sensor == SENSOR_OV562x) {
+ sval = sd->ctrls[BRIGHTNESS].val;
+ val = 0x76;
+ val += sval;
+ sccb_write(gspca_dev, 0x24, val);
+ val = 0x6a;
+ val += sval;
+ sccb_write(gspca_dev, 0x25, val);
+ if (sval < -40)
+ val = 0x71;
+ else if (sval < 20)
+ val = 0x94;
+ else
+ val = 0xe6;
+ sccb_write(gspca_dev, 0x26, val);
+ } else {
+ val = sd->ctrls[BRIGHTNESS].val;
+ if (val < 8)
+ val = 15 - val; /* f .. 8 */
+ else
+ val = val - 8; /* 0 .. 7 */
+ sccb_write(gspca_dev, 0x55, /* brtn - brightness adjustment */
+ 0x0f | (val << 4));
+ }
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (gspca_dev->ctrl_dis & (1 << CONTRAST))
+ return;
+ sccb_write(gspca_dev, 0x56, /* cnst1 - contrast 1 ctrl coeff */
+ sd->ctrls[CONTRAST].val << 4);
+}
+
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
+
+ if (gspca_dev->ctrl_dis & (1 << AUTOGAIN))
+ return;
+/*fixme: should adjust agc/awb/aec by different controls */
+ val = sccb_read(gspca_dev, 0x13); /* com8 */
+ sccb_write(gspca_dev, 0xff, 0x00);
+ if (sd->ctrls[AUTOGAIN].val)
+ val |= 0x05; /* agc & aec */
+ else
+ val &= 0xfa;
+ sccb_write(gspca_dev, 0x13, val);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
+ static const u8 expo[4] = {0x00, 0x25, 0x38, 0x5e};
+
+ if (gspca_dev->ctrl_dis & (1 << EXPOSURE))
+ return;
+ sccb_write(gspca_dev, 0x10, /* aec[9:2] */
+ expo[sd->ctrls[EXPOSURE].val]);
+
+ val = sccb_read(gspca_dev, 0x13); /* com8 */
+ sccb_write(gspca_dev, 0xff, 0x00);
+ sccb_write(gspca_dev, 0x13, val);
+
+ val = sccb_read(gspca_dev, 0xa1); /* aech */
+ sccb_write(gspca_dev, 0xff, 0x00);
+ sccb_write(gspca_dev, 0xa1, val & 0xe0); /* aec[15:10] = 0 */
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s8 val;
+
+ if (gspca_dev->ctrl_dis & (1 << SHARPNESS))
+ return;
+ val = sd->ctrls[SHARPNESS].val;
+ if (val < 0) { /* auto */
+ val = sccb_read(gspca_dev, 0x42); /* com17 */
+ sccb_write(gspca_dev, 0xff, 0x00);
+ sccb_write(gspca_dev, 0x42, val | 0x40);
+ /* Edge enhancement strength auto adjust */
+ return;
+ }
+ if (val != 0)
+ val = 1 << (val - 1);
+ sccb_write(gspca_dev, 0x3f, /* edge - edge enhance. factor */
+ val);
+ val = sccb_read(gspca_dev, 0x42); /* com17 */
+ sccb_write(gspca_dev, 0xff, 0x00);
+ sccb_write(gspca_dev, 0x42, val & 0xbf);
+}
+
+static void setsatur(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val1, val2, val3;
+ static const u8 matrix[5][2] = {
+ {0x14, 0x38},
+ {0x1e, 0x54},
+ {0x28, 0x70},
+ {0x32, 0x8c},
+ {0x48, 0x90}
+ };
+
+ if (gspca_dev->ctrl_dis & (1 << SATUR))
+ return;
+ val1 = matrix[sd->ctrls[SATUR].val][0];
+ val2 = matrix[sd->ctrls[SATUR].val][1];
+ val3 = val1 + val2;
+ sccb_write(gspca_dev, 0x4f, val3); /* matrix coeff */
+ sccb_write(gspca_dev, 0x50, val3);
+ sccb_write(gspca_dev, 0x51, 0x00);
+ sccb_write(gspca_dev, 0x52, val1);
+ sccb_write(gspca_dev, 0x53, val2);
+ sccb_write(gspca_dev, 0x54, val3);
+ sccb_write(gspca_dev, 0x58, 0x1a); /* mtxs - coeff signs */
+
+ val1 = sccb_read(gspca_dev, 0x41); /* com16 */
+ sccb_write(gspca_dev, 0xff, 0x00);
+ sccb_write(gspca_dev, 0x41, val1);
+}
+
+static void setlightfreq(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
+
+ if (gspca_dev->ctrl_dis & (1 << LIGHTFREQ))
+ return;
+ val = sccb_read(gspca_dev, 0x13); /* com8 */
+ sccb_write(gspca_dev, 0xff, 0x00);
+ if (sd->ctrls[LIGHTFREQ].val == 0) {
+ sccb_write(gspca_dev, 0x13, val & 0xdf);
+ return;
+ }
+ sccb_write(gspca_dev, 0x13, val | 0x20);
+
+ val = sccb_read(gspca_dev, 0x42); /* com17 */
+ sccb_write(gspca_dev, 0xff, 0x00);
+ if (sd->ctrls[LIGHTFREQ].val == 1)
+ val |= 0x01;
+ else
+ val &= 0xfe;
+ sccb_write(gspca_dev, 0x42, val);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ gspca_dev->cam.ctrls = sd->ctrls;
+
+#if AUTOGAIN_DEF != 0
+ gspca_dev->ctrl_inac |= (1 << EXPOSURE);
+#endif
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 sensor_id;
+
+ /* reset bridge */
+ reg_w(gspca_dev, 0xe7, 0x3a);
+ reg_w(gspca_dev, 0xe0, 0x08);
+ msleep(100);
+
+ /* initialize the sensor address */
+ reg_w(gspca_dev, OV534_REG_ADDRESS, 0x60);
+
+ /* reset sensor */
+ sccb_write(gspca_dev, 0x12, 0x80);
+ msleep(10);
+
+ /* probe the sensor */
+ sccb_read(gspca_dev, 0x0a);
+ sensor_id = sccb_read(gspca_dev, 0x0a) << 8;
+ sccb_read(gspca_dev, 0x0b);
+ sensor_id |= sccb_read(gspca_dev, 0x0b);
+ PDEBUG(D_PROBE, "Sensor ID: %04x", sensor_id);
+
+ /* initialize */
+ if ((sensor_id & 0xfff0) == 0x9650) {
+ sd->sensor = SENSOR_OV965x;
+
+ gspca_dev->cam.cam_mode = ov965x_mode;
+ gspca_dev->cam.nmodes = ARRAY_SIZE(ov965x_mode);
+
+ reg_w_array(gspca_dev, bridge_init,
+ ARRAY_SIZE(bridge_init));
+ sccb_w_array(gspca_dev, ov965x_init,
+ ARRAY_SIZE(ov965x_init));
+ reg_w_array(gspca_dev, bridge_init_2,
+ ARRAY_SIZE(bridge_init_2));
+ sccb_w_array(gspca_dev, ov965x_init_2,
+ ARRAY_SIZE(ov965x_init_2));
+ reg_w(gspca_dev, 0xe0, 0x00);
+ reg_w(gspca_dev, 0xe0, 0x01);
+ set_led(gspca_dev, 0);
+ reg_w(gspca_dev, 0xe0, 0x00);
+ } else if ((sensor_id & 0xfff0) == 0x9710) {
+ const char *p;
+ int l;
+
+ sd->sensor = SENSOR_OV971x;
+
+ gspca_dev->cam.cam_mode = ov971x_mode;
+ gspca_dev->cam.nmodes = ARRAY_SIZE(ov971x_mode);
+
+ /* no control yet */
+ gspca_dev->ctrl_dis = (1 << NCTRLS) - 1;
+
+ gspca_dev->cam.bulk = 1;
+ gspca_dev->cam.bulk_size = 16384;
+ gspca_dev->cam.bulk_nurbs = 2;
+
+ sccb_w_array(gspca_dev, ov971x_init,
+ ARRAY_SIZE(ov971x_init));
+
+ /* set video format on bridge processor */
+ /* access bridge processor's video format registers at: 0x00 */
+ reg_w(gspca_dev, 0x1c, 0x00);
+ /*set register: 0x00 is 'RAW8', 0x40 is 'YUV422' (YUYV?)*/
+ reg_w(gspca_dev, 0x1d, 0x00);
+
+ /* Will W. specific stuff
+ * set VSYNC to
+ * output (0x1f) if first webcam
+ * input (0x17) if 2nd or 3rd webcam */
+ p = video_device_node_name(&gspca_dev->vdev);
+ l = strlen(p) - 1;
+ if (p[l] == '0')
+ reg_w(gspca_dev, 0x56, 0x1f);
+ else
+ reg_w(gspca_dev, 0x56, 0x17);
+ } else if ((sensor_id & 0xfff0) == 0x5620) {
+ sd->sensor = SENSOR_OV562x;
+ gspca_dev->ctrl_dis = (1 << CONTRAST) |
+ (1 << AUTOGAIN) |
+ (1 << EXPOSURE) |
+ (1 << SHARPNESS) |
+ (1 << SATUR) |
+ (1 << LIGHTFREQ);
+
+ sd->ctrls[BRIGHTNESS].min = -90;
+ sd->ctrls[BRIGHTNESS].max = 90;
+ sd->ctrls[BRIGHTNESS].def = 0;
+ gspca_dev->cam.cam_mode = ov562x_mode;
+ gspca_dev->cam.nmodes = ARRAY_SIZE(ov562x_mode);
+
+ reg_w_array(gspca_dev, ov562x_init,
+ ARRAY_SIZE(ov562x_init));
+ sccb_w_array(gspca_dev, ov562x_init_2,
+ ARRAY_SIZE(ov562x_init_2));
+ reg_w(gspca_dev, 0xe0, 0x00);
+ } else {
+ err("Unknown sensor %04x", sensor_id);
+ return -EINVAL;
+ }
+
+ return gspca_dev->usb_err;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor == SENSOR_OV971x)
+ return gspca_dev->usb_err;
+ else if (sd->sensor == SENSOR_OV562x) {
+ setbrightness(gspca_dev);
+ return gspca_dev->usb_err;
+ }
+ switch (gspca_dev->curr_mode) {
+ case QVGA_MODE: /* 320x240 */
+ sccb_w_array(gspca_dev, ov965x_start_1_vga,
+ ARRAY_SIZE(ov965x_start_1_vga));
+ reg_w_array(gspca_dev, bridge_start_qvga,
+ ARRAY_SIZE(bridge_start_qvga));
+ sccb_w_array(gspca_dev, ov965x_start_2_qvga,
+ ARRAY_SIZE(ov965x_start_2_qvga));
+ break;
+ case VGA_MODE: /* 640x480 */
+ sccb_w_array(gspca_dev, ov965x_start_1_vga,
+ ARRAY_SIZE(ov965x_start_1_vga));
+ reg_w_array(gspca_dev, bridge_start_vga,
+ ARRAY_SIZE(bridge_start_vga));
+ sccb_w_array(gspca_dev, ov965x_start_2_vga,
+ ARRAY_SIZE(ov965x_start_2_vga));
+ break;
+ case SVGA_MODE: /* 800x600 */
+ sccb_w_array(gspca_dev, ov965x_start_1_svga,
+ ARRAY_SIZE(ov965x_start_1_svga));
+ reg_w_array(gspca_dev, bridge_start_svga,
+ ARRAY_SIZE(bridge_start_svga));
+ sccb_w_array(gspca_dev, ov965x_start_2_svga,
+ ARRAY_SIZE(ov965x_start_2_svga));
+ break;
+ case XGA_MODE: /* 1024x768 */
+ sccb_w_array(gspca_dev, ov965x_start_1_xga,
+ ARRAY_SIZE(ov965x_start_1_xga));
+ reg_w_array(gspca_dev, bridge_start_xga,
+ ARRAY_SIZE(bridge_start_xga));
+ sccb_w_array(gspca_dev, ov965x_start_2_svga,
+ ARRAY_SIZE(ov965x_start_2_svga));
+ break;
+ default:
+/* case SXGA_MODE: * 1280x1024 */
+ sccb_w_array(gspca_dev, ov965x_start_1_sxga,
+ ARRAY_SIZE(ov965x_start_1_sxga));
+ reg_w_array(gspca_dev, bridge_start_sxga,
+ ARRAY_SIZE(bridge_start_sxga));
+ sccb_w_array(gspca_dev, ov965x_start_2_sxga,
+ ARRAY_SIZE(ov965x_start_2_sxga));
+ break;
+ }
+ setlightfreq(gspca_dev);
+ setautogain(gspca_dev);
+ setbrightness(gspca_dev);
+ setcontrast(gspca_dev);
+ setexposure(gspca_dev);
+ setsharpness(gspca_dev);
+ setsatur(gspca_dev);
+
+ reg_w(gspca_dev, 0xe0, 0x00);
+ reg_w(gspca_dev, 0xe0, 0x00);
+ set_led(gspca_dev, 1);
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ reg_w(gspca_dev, 0xe0, 0x01);
+ set_led(gspca_dev, 0);
+ reg_w(gspca_dev, 0xe0, 0x00);
+}
+
+/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */
+#define UVC_STREAM_EOH (1 << 7)
+#define UVC_STREAM_ERR (1 << 6)
+#define UVC_STREAM_STI (1 << 5)
+#define UVC_STREAM_RES (1 << 4)
+#define UVC_STREAM_SCR (1 << 3)
+#define UVC_STREAM_PTS (1 << 2)
+#define UVC_STREAM_EOF (1 << 1)
+#define UVC_STREAM_FID (1 << 0)
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u32 this_pts;
+ u8 this_fid;
+ int remaining_len = len;
+ int payload_len;
+
+ payload_len = gspca_dev->cam.bulk ? 2048 : 2040;
+ do {
+ len = min(remaining_len, payload_len);
+
+ /* Payloads are prefixed with a UVC-style header. We
+ consider a frame to start when the FID toggles, or the PTS
+ changes. A frame ends when EOF is set, and we've received
+ the correct number of bytes. */
+
+ /* Verify UVC header. Header length is always 12 */
+ if (data[0] != 12 || len < 12) {
+ PDEBUG(D_PACK, "bad header");
+ goto discard;
+ }
+
+ /* Check errors */
+ if (data[1] & UVC_STREAM_ERR) {
+ PDEBUG(D_PACK, "payload error");
+ goto discard;
+ }
+
+ /* Extract PTS and FID */
+ if (!(data[1] & UVC_STREAM_PTS)) {
+ PDEBUG(D_PACK, "PTS not present");
+ goto discard;
+ }
+ this_pts = (data[5] << 24) | (data[4] << 16)
+ | (data[3] << 8) | data[2];
+ this_fid = data[1] & UVC_STREAM_FID;
+
+ /* If PTS or FID has changed, start a new frame. */
+ if (this_pts != sd->last_pts || this_fid != sd->last_fid) {
+ if (gspca_dev->last_packet_type == INTER_PACKET)
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ NULL, 0);
+ sd->last_pts = this_pts;
+ sd->last_fid = this_fid;
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ data + 12, len - 12);
+ /* If this packet is marked as EOF, end the frame */
+ } else if (data[1] & UVC_STREAM_EOF) {
+ sd->last_pts = 0;
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ data + 12, len - 12);
+ } else {
+
+ /* Add the data from this payload */
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data + 12, len - 12);
+ }
+
+ /* Done this payload */
+ goto scan_next;
+
+discard:
+ /* Discard data until a new frame starts. */
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+
+scan_next:
+ remaining_len -= len;
+ data += len;
+ } while (remaining_len > 0);
+}
+
+static int sd_querymenu(struct gspca_dev *gspca_dev,
+ struct v4l2_querymenu *menu)
+{
+ switch (menu->id) {
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ switch (menu->index) {
+ case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
+ strcpy((char *) menu->name, "NoFliker");
+ return 0;
+ case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
+ strcpy((char *) menu->name, "50 Hz");
+ return 0;
+ case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
+ strcpy((char *) menu->name, "60 Hz");
+ return 0;
+ }
+ break;
+ }
+ return -EINVAL;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = NCTRLS,
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+ .querymenu = sd_querymenu,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x05a9, 0x8065)},
+ {USB_DEVICE(0x06f8, 0x3003)},
+ {USB_DEVICE(0x05a9, 0x1550)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c
new file mode 100644
index 00000000..3844c49f
--- /dev/null
+++ b/drivers/media/video/gspca/pac207.c
@@ -0,0 +1,572 @@
+/*
+ * Pixart PAC207BCA library
+ *
+ * Copyright (C) 2008 Hans de Goede <hdegoede@redhat.com>
+ * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
+ * Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "pac207"
+
+#include <linux/input.h>
+#include "gspca.h"
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Pixart PAC207");
+MODULE_LICENSE("GPL");
+
+#define PAC207_CTRL_TIMEOUT 100 /* ms */
+
+#define PAC207_BRIGHTNESS_MIN 0
+#define PAC207_BRIGHTNESS_MAX 255
+#define PAC207_BRIGHTNESS_DEFAULT 46
+
+#define PAC207_EXPOSURE_MIN 3
+#define PAC207_EXPOSURE_MAX 90 /* 1 sec expo time / 1 fps */
+#define PAC207_EXPOSURE_DEFAULT 5 /* power on default: 3 */
+#define PAC207_EXPOSURE_KNEE 9 /* fps: 90 / exposure -> 9: 10 fps */
+
+#define PAC207_GAIN_MIN 0
+#define PAC207_GAIN_MAX 31
+#define PAC207_GAIN_DEFAULT 7 /* power on default: 9 */
+#define PAC207_GAIN_KNEE 15
+
+#define PAC207_AUTOGAIN_DEADZONE 30
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ u8 mode;
+
+ u8 brightness;
+ u8 exposure;
+ u8 autogain;
+ u8 gain;
+
+ u8 sof_read;
+ u8 header_read;
+ u8 autogain_ignore_frames;
+
+ atomic_t avg_lum;
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
+
+static const struct ctrl sd_ctrls[] = {
+#define SD_BRIGHTNESS 0
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = PAC207_BRIGHTNESS_MIN,
+ .maximum = PAC207_BRIGHTNESS_MAX,
+ .step = 1,
+ .default_value = PAC207_BRIGHTNESS_DEFAULT,
+ .flags = 0,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+#define SD_EXPOSURE 1
+ {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = PAC207_EXPOSURE_MIN,
+ .maximum = PAC207_EXPOSURE_MAX,
+ .step = 1,
+ .default_value = PAC207_EXPOSURE_DEFAULT,
+ .flags = 0,
+ },
+ .set = sd_setexposure,
+ .get = sd_getexposure,
+ },
+#define SD_AUTOGAIN 2
+ {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto Gain",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define AUTOGAIN_DEF 1
+ .default_value = AUTOGAIN_DEF,
+ .flags = 0,
+ },
+ .set = sd_setautogain,
+ .get = sd_getautogain,
+ },
+#define SD_GAIN 3
+ {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = PAC207_GAIN_MIN,
+ .maximum = PAC207_GAIN_MAX,
+ .step = 1,
+ .default_value = PAC207_GAIN_DEFAULT,
+ .flags = 0,
+ },
+ .set = sd_setgain,
+ .get = sd_getgain,
+ },
+};
+
+static const struct v4l2_pix_format sif_mode[] = {
+ {176, 144, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = (176 + 2) * 144,
+ /* uncompressed, add 2 bytes / line for line header */
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {352, 288, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ /* compressed, but only when needed (not compressed
+ when the framerate is low) */
+ .sizeimage = (352 + 2) * 288,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+
+static const __u8 pac207_sensor_init[][8] = {
+ {0x10, 0x12, 0x0d, 0x12, 0x0c, 0x01, 0x29, 0x84},
+ {0x49, 0x64, 0x64, 0x64, 0x04, 0x10, 0xf0, 0x30},
+ {0x00, 0x00, 0x00, 0x70, 0xa0, 0xf8, 0x00, 0x00},
+ {0x32, 0x00, 0x96, 0x00, 0xa2, 0x02, 0xaf, 0x00},
+};
+
+static int pac207_write_regs(struct gspca_dev *gspca_dev, u16 index,
+ const u8 *buffer, u16 length)
+{
+ struct usb_device *udev = gspca_dev->dev;
+ int err;
+
+ memcpy(gspca_dev->usb_buf, buffer, length);
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x01,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ 0x00, index,
+ gspca_dev->usb_buf, length, PAC207_CTRL_TIMEOUT);
+ if (err < 0)
+ pr_err("Failed to write registers to index 0x%04X, error %d\n",
+ index, err);
+
+ return err;
+}
+
+
+static int pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value)
+{
+ struct usb_device *udev = gspca_dev->dev;
+ int err;
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ value, index, NULL, 0, PAC207_CTRL_TIMEOUT);
+ if (err)
+ pr_err("Failed to write a register (index 0x%04X, value 0x%02X, error %d)\n",
+ index, value, err);
+
+ return err;
+}
+
+static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index)
+{
+ struct usb_device *udev = gspca_dev->dev;
+ int res;
+
+ res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ 0x00, index,
+ gspca_dev->usb_buf, 1, PAC207_CTRL_TIMEOUT);
+ if (res < 0) {
+ pr_err("Failed to read a register (index 0x%04X, error %d)\n",
+ index, res);
+ return res;
+ }
+
+ return gspca_dev->usb_buf[0];
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+ u8 idreg[2];
+
+ idreg[0] = pac207_read_reg(gspca_dev, 0x0000);
+ idreg[1] = pac207_read_reg(gspca_dev, 0x0001);
+ idreg[0] = ((idreg[0] & 0x0f) << 4) | ((idreg[1] & 0xf0) >> 4);
+ idreg[1] = idreg[1] & 0x0f;
+ PDEBUG(D_PROBE, "Pixart Sensor ID 0x%02X Chips ID 0x%02X",
+ idreg[0], idreg[1]);
+
+ if (idreg[0] != 0x27) {
+ PDEBUG(D_PROBE, "Error invalid sensor ID!");
+ return -ENODEV;
+ }
+
+ PDEBUG(D_PROBE,
+ "Pixart PAC207BCA Image Processor and Control Chip detected"
+ " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
+
+ cam = &gspca_dev->cam;
+ cam->cam_mode = sif_mode;
+ cam->nmodes = ARRAY_SIZE(sif_mode);
+ sd->brightness = PAC207_BRIGHTNESS_DEFAULT;
+ sd->exposure = PAC207_EXPOSURE_DEFAULT;
+ sd->gain = PAC207_GAIN_DEFAULT;
+ sd->autogain = AUTOGAIN_DEF;
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ pac207_write_reg(gspca_dev, 0x41, 0x00);
+ /* Bit_0=Image Format,
+ * Bit_1=LED,
+ * Bit_2=Compression test mode enable */
+ pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */
+
+ return 0;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u8 mode;
+
+ pac207_write_reg(gspca_dev, 0x0f, 0x10); /* Power control (Bit 6-0) */
+ pac207_write_regs(gspca_dev, 0x0002, pac207_sensor_init[0], 8);
+ pac207_write_regs(gspca_dev, 0x000a, pac207_sensor_init[1], 8);
+ pac207_write_regs(gspca_dev, 0x0012, pac207_sensor_init[2], 8);
+ pac207_write_regs(gspca_dev, 0x0042, pac207_sensor_init[3], 8);
+
+ /* Compression Balance */
+ if (gspca_dev->width == 176)
+ pac207_write_reg(gspca_dev, 0x4a, 0xff);
+ else
+ pac207_write_reg(gspca_dev, 0x4a, 0x30);
+ pac207_write_reg(gspca_dev, 0x4b, 0x00); /* Sram test value */
+ pac207_write_reg(gspca_dev, 0x08, sd->brightness);
+
+ /* PGA global gain (Bit 4-0) */
+ pac207_write_reg(gspca_dev, 0x0e, sd->gain);
+ pac207_write_reg(gspca_dev, 0x02, sd->exposure); /* PXCK = 12MHz /n */
+
+ mode = 0x02; /* Image Format (Bit 0), LED (1), Compr. test mode (2) */
+ if (gspca_dev->width == 176) { /* 176x144 */
+ mode |= 0x01;
+ PDEBUG(D_STREAM, "pac207_start mode 176x144");
+ } else { /* 352x288 */
+ PDEBUG(D_STREAM, "pac207_start mode 352x288");
+ }
+ pac207_write_reg(gspca_dev, 0x41, mode);
+
+ pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */
+ pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
+ msleep(10);
+ pac207_write_reg(gspca_dev, 0x40, 0x01); /* Start ISO pipe */
+
+ sd->sof_read = 0;
+ sd->autogain_ignore_frames = 0;
+ atomic_set(&sd->avg_lum, -1);
+ return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ pac207_write_reg(gspca_dev, 0x40, 0x00); /* Stop ISO pipe */
+ pac207_write_reg(gspca_dev, 0x41, 0x00); /* Turn of LED */
+ pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */
+}
+
+/* Include pac common sof detection functions */
+#include "pac_common.h"
+
+static void pac207_do_auto_gain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int avg_lum = atomic_read(&sd->avg_lum);
+
+ if (avg_lum == -1)
+ return;
+
+ if (sd->autogain_ignore_frames > 0)
+ sd->autogain_ignore_frames--;
+ else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum,
+ 90, PAC207_AUTOGAIN_DEADZONE,
+ PAC207_GAIN_KNEE, PAC207_EXPOSURE_KNEE))
+ sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data,
+ int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ unsigned char *sof;
+
+ sof = pac_find_sof(&sd->sof_read, data, len);
+ if (sof) {
+ int n;
+
+ /* finish decoding current frame */
+ n = sof - data;
+ if (n > sizeof pac_sof_marker)
+ n -= sizeof pac_sof_marker;
+ else
+ n = 0;
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ data, n);
+ sd->header_read = 0;
+ gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+ len -= sof - data;
+ data = sof;
+ }
+ if (sd->header_read < 11) {
+ int needed;
+
+ /* get average lumination from frame header (byte 5) */
+ if (sd->header_read < 5) {
+ needed = 5 - sd->header_read;
+ if (len >= needed)
+ atomic_set(&sd->avg_lum, data[needed - 1]);
+ }
+ /* skip the rest of the header */
+ needed = 11 - sd->header_read;
+ if (len <= needed) {
+ sd->header_read += len;
+ return;
+ }
+ data += needed;
+ len -= needed;
+ sd->header_read = 11;
+ }
+
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ pac207_write_reg(gspca_dev, 0x08, sd->brightness);
+ pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */
+ pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ pac207_write_reg(gspca_dev, 0x02, sd->exposure);
+ pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */
+ pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
+}
+
+static void setgain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ pac207_write_reg(gspca_dev, 0x0e, sd->gain);
+ pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */
+ pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->brightness = val;
+ if (gspca_dev->streaming)
+ setbrightness(gspca_dev);
+ return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->brightness;
+ return 0;
+}
+
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->exposure = val;
+ if (gspca_dev->streaming)
+ setexposure(gspca_dev);
+ return 0;
+}
+
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->exposure;
+ return 0;
+}
+
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->gain = val;
+ if (gspca_dev->streaming)
+ setgain(gspca_dev);
+ return 0;
+}
+
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->gain;
+ return 0;
+}
+
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->autogain = val;
+ /* when switching to autogain set defaults to make sure
+ we are on a valid point of the autogain gain /
+ exposure knee graph, and give this change time to
+ take effect before doing autogain. */
+ if (sd->autogain) {
+ sd->exposure = PAC207_EXPOSURE_DEFAULT;
+ sd->gain = PAC207_GAIN_DEFAULT;
+ if (gspca_dev->streaming) {
+ sd->autogain_ignore_frames =
+ PAC_AUTOGAIN_IGNORE_FRAMES;
+ setexposure(gspca_dev);
+ setgain(gspca_dev);
+ }
+ }
+
+ return 0;
+}
+
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->autogain;
+ return 0;
+}
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* interrupt packet data */
+ int len) /* interrput packet length */
+{
+ int ret = -EINVAL;
+
+ if (len == 2 && data[0] == 0x5a && data[1] == 0x5a) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+ input_sync(gspca_dev->input_dev);
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ ret = 0;
+ }
+
+ return ret;
+}
+#endif
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .dq_callback = pac207_do_auto_gain,
+ .pkt_scan = sd_pkt_scan,
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ .int_pkt_scan = sd_int_pkt_scan,
+#endif
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x041e, 0x4028)},
+ {USB_DEVICE(0x093a, 0x2460)},
+ {USB_DEVICE(0x093a, 0x2461)},
+ {USB_DEVICE(0x093a, 0x2463)},
+ {USB_DEVICE(0x093a, 0x2464)},
+ {USB_DEVICE(0x093a, 0x2468)},
+ {USB_DEVICE(0x093a, 0x2470)},
+ {USB_DEVICE(0x093a, 0x2471)},
+ {USB_DEVICE(0x093a, 0x2472)},
+ {USB_DEVICE(0x093a, 0x2474)},
+ {USB_DEVICE(0x093a, 0x2476)},
+ {USB_DEVICE(0x145f, 0x013a)},
+ {USB_DEVICE(0x2001, 0xf115)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c
new file mode 100644
index 00000000..30662fcc
--- /dev/null
+++ b/drivers/media/video/gspca/pac7302.c
@@ -0,0 +1,971 @@
+/*
+ * Pixart PAC7302 driver
+ *
+ * Copyright (C) 2008-2012 Jean-Francois Moine <http://moinejf.free.fr>
+ * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
+ *
+ * Separated from Pixart PAC7311 library by Márton Németh
+ * Camera button input handling by Márton Németh <nm127@freemail.hu>
+ * Copyright (C) 2009-2010 Márton Németh <nm127@freemail.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* Some documentation about various registers as determined by trial and error.
+
+ Register page 1:
+
+ Address Description
+ 0x78 Global control, bit 6 controls the LED (inverted)
+
+ Register page 3:
+
+ Address Description
+ 0x02 Clock divider 3-63, fps = 90 / val. Must be a multiple of 3 on
+ the 7302, so one of 3, 6, 9, ..., except when between 6 and 12?
+ 0x03 Variable framerate ctrl reg2==3: 0 -> ~30 fps, 255 -> ~22fps
+ 0x04 Another var framerate ctrl reg2==3, reg3==0: 0 -> ~30 fps,
+ 63 -> ~27 fps, the 2 msb's must always be 1 !!
+ 0x05 Another var framerate ctrl reg2==3, reg3==0, reg4==0xc0:
+ 1 -> ~30 fps, 2 -> ~20 fps
+ 0x0e Exposure bits 0-7, 0-448, 0 = use full frame time
+ 0x0f Exposure bit 8, 0-448, 448 = no exposure at all
+ 0x10 Master gain 0-31
+ 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused
+
+ The registers are accessed in the following functions:
+
+ Page | Register | Function
+ -----+------------+---------------------------------------------------
+ 0 | 0x0f..0x20 | setcolors()
+ 0 | 0xa2..0xab | setbrightcont()
+ 0 | 0xc5 | setredbalance()
+ 0 | 0xc6 | setwhitebalance()
+ 0 | 0xc7 | setbluebalance()
+ 0 | 0xdc | setbrightcont(), setcolors()
+ 3 | 0x02 | setexposure()
+ 3 | 0x10 | setgain()
+ 3 | 0x11 | setcolors(), setgain(), setexposure(), sethvflip()
+ 3 | 0x21 | sethvflip()
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/input.h>
+#include <media/v4l2-chip-ident.h>
+#include "gspca.h"
+/* Include pac common sof detection functions */
+#include "pac_common.h"
+
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>, "
+ "Thomas Kaiser thomas@kaiser-linux.li");
+MODULE_DESCRIPTION("Pixart PAC7302");
+MODULE_LICENSE("GPL");
+
+enum e_ctrl {
+ BRIGHTNESS,
+ CONTRAST,
+ COLORS,
+ WHITE_BALANCE,
+ RED_BALANCE,
+ BLUE_BALANCE,
+ GAIN,
+ AUTOGAIN,
+ EXPOSURE,
+ VFLIP,
+ HFLIP,
+ NCTRLS /* number of controls */
+};
+
+/* specific webcam descriptor for pac7302 */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ struct gspca_ctrl ctrls[NCTRLS];
+
+ u8 flags;
+#define FL_HFLIP 0x01 /* mirrored by default */
+#define FL_VFLIP 0x02 /* vertical flipped by default */
+
+ u8 sof_read;
+ s8 autogain_ignore_frames;
+
+ atomic_t avg_lum;
+};
+
+/* V4L2 controls supported by the driver */
+static void setbrightcont(struct gspca_dev *gspca_dev);
+static void setcolors(struct gspca_dev *gspca_dev);
+static void setwhitebalance(struct gspca_dev *gspca_dev);
+static void setredbalance(struct gspca_dev *gspca_dev);
+static void setbluebalance(struct gspca_dev *gspca_dev);
+static void setgain(struct gspca_dev *gspca_dev);
+static void setexposure(struct gspca_dev *gspca_dev);
+static void setautogain(struct gspca_dev *gspca_dev);
+static void sethvflip(struct gspca_dev *gspca_dev);
+
+static const struct ctrl sd_ctrls[] = {
+[BRIGHTNESS] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+#define BRIGHTNESS_MAX 0x20
+ .maximum = BRIGHTNESS_MAX,
+ .step = 1,
+ .default_value = 0x10,
+ },
+ .set_control = setbrightcont
+ },
+[CONTRAST] = {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+#define CONTRAST_MAX 255
+ .maximum = CONTRAST_MAX,
+ .step = 1,
+ .default_value = 127,
+ },
+ .set_control = setbrightcont
+ },
+[COLORS] = {
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 0,
+#define COLOR_MAX 255
+ .maximum = COLOR_MAX,
+ .step = 1,
+ .default_value = 127
+ },
+ .set_control = setcolors
+ },
+[WHITE_BALANCE] = {
+ {
+ .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "White Balance",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 4,
+ },
+ .set_control = setwhitebalance
+ },
+[RED_BALANCE] = {
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red",
+ .minimum = 0,
+ .maximum = 3,
+ .step = 1,
+ .default_value = 1,
+ },
+ .set_control = setredbalance
+ },
+[BLUE_BALANCE] = {
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue",
+ .minimum = 0,
+ .maximum = 3,
+ .step = 1,
+ .default_value = 1,
+ },
+ .set_control = setbluebalance
+ },
+[GAIN] = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+#define GAIN_DEF 127
+#define GAIN_KNEE 255 /* Gain seems to cause little noise on the pac73xx */
+ .default_value = GAIN_DEF,
+ },
+ .set_control = setgain
+ },
+[EXPOSURE] = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 0,
+ .maximum = 1023,
+ .step = 1,
+#define EXPOSURE_DEF 66 /* 33 ms / 30 fps */
+#define EXPOSURE_KNEE 133 /* 66 ms / 15 fps */
+ .default_value = EXPOSURE_DEF,
+ },
+ .set_control = setexposure
+ },
+[AUTOGAIN] = {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto Gain",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define AUTOGAIN_DEF 1
+ .default_value = AUTOGAIN_DEF,
+ },
+ .set_control = setautogain,
+ },
+[HFLIP] = {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mirror",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set_control = sethvflip,
+ },
+[VFLIP] = {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Vflip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set_control = sethvflip
+ },
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+};
+
+#define LOAD_PAGE3 255
+#define END_OF_SEQUENCE 0
+
+/* pac 7302 */
+static const u8 init_7302[] = {
+/* index,value */
+ 0xff, 0x01, /* page 1 */
+ 0x78, 0x00, /* deactivate */
+ 0xff, 0x01,
+ 0x78, 0x40, /* led off */
+};
+static const u8 start_7302[] = {
+/* index, len, [value]* */
+ 0xff, 1, 0x00, /* page 0 */
+ 0x00, 12, 0x01, 0x40, 0x40, 0x40, 0x01, 0xe0, 0x02, 0x80,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x0d, 24, 0x03, 0x01, 0x00, 0xb5, 0x07, 0xcb, 0x00, 0x00,
+ 0x07, 0xc8, 0x00, 0xea, 0x07, 0xcf, 0x07, 0xf7,
+ 0x07, 0x7e, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x11,
+ 0x26, 2, 0xaa, 0xaa,
+ 0x2e, 1, 0x31,
+ 0x38, 1, 0x01,
+ 0x3a, 3, 0x14, 0xff, 0x5a,
+ 0x43, 11, 0x00, 0x0a, 0x18, 0x11, 0x01, 0x2c, 0x88, 0x11,
+ 0x00, 0x54, 0x11,
+ 0x55, 1, 0x00,
+ 0x62, 4, 0x10, 0x1e, 0x1e, 0x18,
+ 0x6b, 1, 0x00,
+ 0x6e, 3, 0x08, 0x06, 0x00,
+ 0x72, 3, 0x00, 0xff, 0x00,
+ 0x7d, 23, 0x01, 0x01, 0x58, 0x46, 0x50, 0x3c, 0x50, 0x3c,
+ 0x54, 0x46, 0x54, 0x56, 0x52, 0x50, 0x52, 0x50,
+ 0x56, 0x64, 0xa4, 0x00, 0xda, 0x00, 0x00,
+ 0xa2, 10, 0x22, 0x2c, 0x3c, 0x54, 0x69, 0x7c, 0x9c, 0xb9,
+ 0xd2, 0xeb,
+ 0xaf, 1, 0x02,
+ 0xb5, 2, 0x08, 0x08,
+ 0xb8, 2, 0x08, 0x88,
+ 0xc4, 4, 0xae, 0x01, 0x04, 0x01,
+ 0xcc, 1, 0x00,
+ 0xd1, 11, 0x01, 0x30, 0x49, 0x5e, 0x6f, 0x7f, 0x8e, 0xa9,
+ 0xc1, 0xd7, 0xec,
+ 0xdc, 1, 0x01,
+ 0xff, 1, 0x01, /* page 1 */
+ 0x12, 3, 0x02, 0x00, 0x01,
+ 0x3e, 2, 0x00, 0x00,
+ 0x76, 5, 0x01, 0x20, 0x40, 0x00, 0xf2,
+ 0x7c, 1, 0x00,
+ 0x7f, 10, 0x4b, 0x0f, 0x01, 0x2c, 0x02, 0x58, 0x03, 0x20,
+ 0x02, 0x00,
+ 0x96, 5, 0x01, 0x10, 0x04, 0x01, 0x04,
+ 0xc8, 14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
+ 0x07, 0x00, 0x01, 0x07, 0x04, 0x01,
+ 0xd8, 1, 0x01,
+ 0xdb, 2, 0x00, 0x01,
+ 0xde, 7, 0x00, 0x01, 0x04, 0x04, 0x00, 0x00, 0x00,
+ 0xe6, 4, 0x00, 0x00, 0x00, 0x01,
+ 0xeb, 1, 0x00,
+ 0xff, 1, 0x02, /* page 2 */
+ 0x22, 1, 0x00,
+ 0xff, 1, 0x03, /* page 3 */
+ 0, LOAD_PAGE3, /* load the page 3 */
+ 0x11, 1, 0x01,
+ 0xff, 1, 0x02, /* page 2 */
+ 0x13, 1, 0x00,
+ 0x22, 4, 0x1f, 0xa4, 0xf0, 0x96,
+ 0x27, 2, 0x14, 0x0c,
+ 0x2a, 5, 0xc8, 0x00, 0x18, 0x12, 0x22,
+ 0x64, 8, 0x00, 0x00, 0xf0, 0x01, 0x14, 0x44, 0x44, 0x44,
+ 0x6e, 1, 0x08,
+ 0xff, 1, 0x01, /* page 1 */
+ 0x78, 1, 0x00,
+ 0, END_OF_SEQUENCE /* end of sequence */
+};
+
+#define SKIP 0xaa
+/* page 3 - the value SKIP says skip the index - see reg_w_page() */
+static const u8 page3_7302[] = {
+ 0x90, 0x40, 0x03, 0x00, 0xc0, 0x01, 0x14, 0x16,
+ 0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00,
+ 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x47, 0x01, 0xb3, 0x01, 0x00,
+ 0x00, 0x08, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x21,
+ 0x00, 0x00, 0x00, 0x54, 0xf4, 0x02, 0x52, 0x54,
+ 0xa4, 0xb8, 0xe0, 0x2a, 0xf6, 0x00, 0x00, 0x00,
+ 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xfc, 0x00, 0xf2, 0x1f, 0x04, 0x00, 0x00,
+ SKIP, 0x00, 0x00, 0xc0, 0xc0, 0x10, 0x00, 0x00,
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x40, 0xff, 0x03, 0x19, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xc8, 0xc8,
+ 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50,
+ 0x08, 0x10, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x02, 0x47, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0xfa, 0x00, 0x64, 0x5a, 0x28, 0x00,
+ 0x00
+};
+
+static void reg_w_buf(struct gspca_dev *gspca_dev,
+ u8 index,
+ const u8 *buffer, int len)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ memcpy(gspca_dev->usb_buf, buffer, len);
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, /* value */
+ index, gspca_dev->usb_buf, len,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_w_buf failed i: %02x error %d\n",
+ index, ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+
+static void reg_w(struct gspca_dev *gspca_dev,
+ u8 index,
+ u8 value)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ gspca_dev->usb_buf[0] = value;
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index, gspca_dev->usb_buf, 1,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_w() failed i: %02x v: %02x error %d\n",
+ index, value, ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static void reg_w_seq(struct gspca_dev *gspca_dev,
+ const u8 *seq, int len)
+{
+ while (--len >= 0) {
+ reg_w(gspca_dev, seq[0], seq[1]);
+ seq += 2;
+ }
+}
+
+/* load the beginning of a page */
+static void reg_w_page(struct gspca_dev *gspca_dev,
+ const u8 *page, int len)
+{
+ int index;
+ int ret = 0;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ for (index = 0; index < len; index++) {
+ if (page[index] == SKIP) /* skip this index */
+ continue;
+ gspca_dev->usb_buf[0] = page[index];
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index, gspca_dev->usb_buf, 1,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_w_page() failed i: %02x v: %02x error %d\n",
+ index, page[index], ret);
+ gspca_dev->usb_err = ret;
+ break;
+ }
+ }
+}
+
+/* output a variable sequence */
+static void reg_w_var(struct gspca_dev *gspca_dev,
+ const u8 *seq,
+ const u8 *page3, unsigned int page3_len)
+{
+ int index, len;
+
+ for (;;) {
+ index = *seq++;
+ len = *seq++;
+ switch (len) {
+ case END_OF_SEQUENCE:
+ return;
+ case LOAD_PAGE3:
+ reg_w_page(gspca_dev, page3, page3_len);
+ break;
+ default:
+#ifdef GSPCA_DEBUG
+ if (len > USB_BUF_SZ) {
+ PDEBUG(D_ERR|D_STREAM,
+ "Incorrect variable sequence");
+ return;
+ }
+#endif
+ while (len > 0) {
+ if (len < 8) {
+ reg_w_buf(gspca_dev,
+ index, seq, len);
+ seq += len;
+ break;
+ }
+ reg_w_buf(gspca_dev, index, seq, 8);
+ seq += 8;
+ index += 8;
+ len -= 8;
+ }
+ }
+ }
+ /* not reached */
+}
+
+/* this function is called at probe time for pac7302 */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+
+ cam->cam_mode = vga_mode; /* only 640x480 */
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+
+ gspca_dev->cam.ctrls = sd->ctrls;
+
+ sd->flags = id->driver_info;
+ return 0;
+}
+
+/* This function is used by pac7302 only */
+static void setbrightcont(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, v;
+ static const u8 max[10] =
+ {0x29, 0x33, 0x42, 0x5a, 0x6e, 0x80, 0x9f, 0xbb,
+ 0xd4, 0xec};
+ static const u8 delta[10] =
+ {0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17,
+ 0x11, 0x0b};
+
+ reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
+ for (i = 0; i < 10; i++) {
+ v = max[i];
+ v += (sd->ctrls[BRIGHTNESS].val - BRIGHTNESS_MAX)
+ * 150 / BRIGHTNESS_MAX; /* 200 ? */
+ v -= delta[i] * sd->ctrls[CONTRAST].val / CONTRAST_MAX;
+ if (v < 0)
+ v = 0;
+ else if (v > 0xff)
+ v = 0xff;
+ reg_w(gspca_dev, 0xa2 + i, v);
+ }
+ reg_w(gspca_dev, 0xdc, 0x01);
+}
+
+/* This function is used by pac7302 only */
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, v;
+ static const int a[9] =
+ {217, -212, 0, -101, 170, -67, -38, -315, 355};
+ static const int b[9] =
+ {19, 106, 0, 19, 106, 1, 19, 106, 1};
+
+ reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
+ reg_w(gspca_dev, 0x11, 0x01);
+ reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
+ for (i = 0; i < 9; i++) {
+ v = a[i] * sd->ctrls[COLORS].val / COLOR_MAX + b[i];
+ reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07);
+ reg_w(gspca_dev, 0x0f + 2 * i + 1, v);
+ }
+ reg_w(gspca_dev, 0xdc, 0x01);
+}
+
+static void setwhitebalance(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
+ reg_w(gspca_dev, 0xc6, sd->ctrls[WHITE_BALANCE].val);
+
+ reg_w(gspca_dev, 0xdc, 0x01);
+}
+
+static void setredbalance(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
+ reg_w(gspca_dev, 0xc5, sd->ctrls[RED_BALANCE].val);
+
+ reg_w(gspca_dev, 0xdc, 0x01);
+}
+
+static void setbluebalance(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
+ reg_w(gspca_dev, 0xc7, sd->ctrls[BLUE_BALANCE].val);
+
+ reg_w(gspca_dev, 0xdc, 0x01);
+}
+
+static void setgain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
+ reg_w(gspca_dev, 0x10, sd->ctrls[GAIN].val >> 3);
+
+ /* load registers to sensor (Bit 0, auto clear) */
+ reg_w(gspca_dev, 0x11, 0x01);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 clockdiv;
+ u16 exposure;
+
+ /* register 2 of frame 3 contains the clock divider configuring the
+ no fps according to the formula: 90 / reg. sd->exposure is the
+ desired exposure time in 0.5 ms. */
+ clockdiv = (90 * sd->ctrls[EXPOSURE].val + 1999) / 2000;
+
+ /* Note clockdiv = 3 also works, but when running at 30 fps, depending
+ on the scene being recorded, the camera switches to another
+ quantization table for certain JPEG blocks, and we don't know how
+ to decompress these blocks. So we cap the framerate at 15 fps */
+ if (clockdiv < 6)
+ clockdiv = 6;
+ else if (clockdiv > 63)
+ clockdiv = 63;
+
+ /* reg2 MUST be a multiple of 3, except when between 6 and 12?
+ Always round up, otherwise we cannot get the desired frametime
+ using the partial frame time exposure control */
+ if (clockdiv < 6 || clockdiv > 12)
+ clockdiv = ((clockdiv + 2) / 3) * 3;
+
+ /* frame exposure time in ms = 1000 * clockdiv / 90 ->
+ exposure = (sd->exposure / 2) * 448 / (1000 * clockdiv / 90) */
+ exposure = (sd->ctrls[EXPOSURE].val * 45 * 448) / (1000 * clockdiv);
+ /* 0 = use full frametime, 448 = no exposure, reverse it */
+ exposure = 448 - exposure;
+
+ reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
+ reg_w(gspca_dev, 0x02, clockdiv);
+ reg_w(gspca_dev, 0x0e, exposure & 0xff);
+ reg_w(gspca_dev, 0x0f, exposure >> 8);
+
+ /* load registers to sensor (Bit 0, auto clear) */
+ reg_w(gspca_dev, 0x11, 0x01);
+}
+
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* when switching to autogain set defaults to make sure
+ we are on a valid point of the autogain gain /
+ exposure knee graph, and give this change time to
+ take effect before doing autogain. */
+ if (sd->ctrls[AUTOGAIN].val) {
+ sd->ctrls[EXPOSURE].val = EXPOSURE_DEF;
+ sd->ctrls[GAIN].val = GAIN_DEF;
+ sd->autogain_ignore_frames =
+ PAC_AUTOGAIN_IGNORE_FRAMES;
+ } else {
+ sd->autogain_ignore_frames = -1;
+ }
+ setexposure(gspca_dev);
+ setgain(gspca_dev);
+}
+
+static void sethvflip(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data, hflip, vflip;
+
+ hflip = sd->ctrls[HFLIP].val;
+ if (sd->flags & FL_HFLIP)
+ hflip = !hflip;
+ vflip = sd->ctrls[VFLIP].val;
+ if (sd->flags & FL_VFLIP)
+ vflip = !vflip;
+
+ reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
+ data = (hflip ? 0x08 : 0x00) | (vflip ? 0x04 : 0x00);
+ reg_w(gspca_dev, 0x21, data);
+
+ /* load registers to sensor (Bit 0, auto clear) */
+ reg_w(gspca_dev, 0x11, 0x01);
+}
+
+/* this function is called at probe and resume time for pac7302 */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ reg_w_seq(gspca_dev, init_7302, sizeof(init_7302)/2);
+ return gspca_dev->usb_err;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w_var(gspca_dev, start_7302,
+ page3_7302, sizeof(page3_7302));
+ setbrightcont(gspca_dev);
+ setcolors(gspca_dev);
+ setwhitebalance(gspca_dev);
+ setredbalance(gspca_dev);
+ setbluebalance(gspca_dev);
+ setautogain(gspca_dev);
+ sethvflip(gspca_dev);
+
+ /* only resolution 640x480 is supported for pac7302 */
+
+ sd->sof_read = 0;
+ atomic_set(&sd->avg_lum, 270 + sd->ctrls[BRIGHTNESS].val);
+
+ /* start stream */
+ reg_w(gspca_dev, 0xff, 0x01);
+ reg_w(gspca_dev, 0x78, 0x01);
+
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+
+ /* stop stream */
+ reg_w(gspca_dev, 0xff, 0x01);
+ reg_w(gspca_dev, 0x78, 0x00);
+}
+
+/* called on streamoff with alt 0 and on disconnect for pac7302 */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ if (!gspca_dev->present)
+ return;
+ reg_w(gspca_dev, 0xff, 0x01);
+ reg_w(gspca_dev, 0x78, 0x40);
+}
+
+/* !! coarse_grained_expo_autogain is not used !! */
+#define exp_too_low_cnt flags
+#define exp_too_high_cnt sof_read
+#include "autogain_functions.h"
+
+static void do_autogain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int avg_lum = atomic_read(&sd->avg_lum);
+ int desired_lum;
+ const int deadzone = 30;
+
+ if (sd->autogain_ignore_frames < 0)
+ return;
+
+ if (sd->autogain_ignore_frames > 0) {
+ sd->autogain_ignore_frames--;
+ } else {
+ desired_lum = 270 + sd->ctrls[BRIGHTNESS].val;
+
+ auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum,
+ deadzone, GAIN_KNEE, EXPOSURE_KNEE);
+ sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
+ }
+}
+
+/* JPEG header */
+static const u8 jpeg_header[] = {
+ 0xff, 0xd8, /* SOI: Start of Image */
+
+ 0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */
+ 0x00, 0x11, /* length = 17 bytes (including this length field) */
+ 0x08, /* Precision: 8 */
+ 0x02, 0x80, /* height = 640 (image rotated) */
+ 0x01, 0xe0, /* width = 480 */
+ 0x03, /* Number of image components: 3 */
+ 0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */
+ 0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */
+ 0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */
+
+ 0xff, 0xda, /* SOS: Start Of Scan */
+ 0x00, 0x0c, /* length = 12 bytes (including this length field) */
+ 0x03, /* number of components: 3 */
+ 0x01, 0x00, /* selector 1, table 0x00 */
+ 0x02, 0x11, /* selector 2, table 0x11 */
+ 0x03, 0x11, /* selector 3, table 0x11 */
+ 0x00, 0x3f, /* Spectral selection: 0 .. 63 */
+ 0x00 /* Successive approximation: 0 */
+};
+
+/* this function is run at interrupt level */
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 *image;
+ u8 *sof;
+
+ sof = pac_find_sof(&sd->sof_read, data, len);
+ if (sof) {
+ int n, lum_offset, footer_length;
+
+ /* 6 bytes after the FF D9 EOF marker a number of lumination
+ bytes are send corresponding to different parts of the
+ image, the 14th and 15th byte after the EOF seem to
+ correspond to the center of the image */
+ lum_offset = 61 + sizeof pac_sof_marker;
+ footer_length = 74;
+
+ /* Finish decoding current frame */
+ n = (sof - data) - (footer_length + sizeof pac_sof_marker);
+ if (n < 0) {
+ gspca_dev->image_len += n;
+ n = 0;
+ } else {
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, n);
+ }
+
+ image = gspca_dev->image;
+ if (image != NULL
+ && image[gspca_dev->image_len - 2] == 0xff
+ && image[gspca_dev->image_len - 1] == 0xd9)
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+
+ n = sof - data;
+ len -= n;
+ data = sof;
+
+ /* Get average lumination */
+ if (gspca_dev->last_packet_type == LAST_PACKET &&
+ n >= lum_offset)
+ atomic_set(&sd->avg_lum, data[-lum_offset] +
+ data[-lum_offset + 1]);
+
+ /* Start the new frame with the jpeg header */
+ /* The PAC7302 has the image rotated 90 degrees */
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ jpeg_header, sizeof jpeg_header);
+ }
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int sd_dbg_s_register(struct gspca_dev *gspca_dev,
+ struct v4l2_dbg_register *reg)
+{
+ u8 index;
+ u8 value;
+
+ /* reg->reg: bit0..15: reserved for register index (wIndex is 16bit
+ long on the USB bus)
+ */
+ if (reg->match.type == V4L2_CHIP_MATCH_HOST &&
+ reg->match.addr == 0 &&
+ (reg->reg < 0x000000ff) &&
+ (reg->val <= 0x000000ff)
+ ) {
+ /* Currently writing to page 0 is only supported. */
+ /* reg_w() only supports 8bit index */
+ index = reg->reg;
+ value = reg->val;
+
+ /* Note that there shall be no access to other page
+ by any other function between the page swith and
+ the actual register write */
+ reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
+ reg_w(gspca_dev, index, value);
+
+ reg_w(gspca_dev, 0xdc, 0x01);
+ }
+ return gspca_dev->usb_err;
+}
+
+static int sd_chip_ident(struct gspca_dev *gspca_dev,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ int ret = -EINVAL;
+
+ if (chip->match.type == V4L2_CHIP_MATCH_HOST &&
+ chip->match.addr == 0) {
+ chip->revision = 0;
+ chip->ident = V4L2_IDENT_UNKNOWN;
+ ret = 0;
+ }
+ return ret;
+}
+#endif
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* interrupt packet data */
+ int len) /* interrput packet length */
+{
+ int ret = -EINVAL;
+ u8 data0, data1;
+
+ if (len == 2) {
+ data0 = data[0];
+ data1 = data[1];
+ if ((data0 == 0x00 && data1 == 0x11) ||
+ (data0 == 0x22 && data1 == 0x33) ||
+ (data0 == 0x44 && data1 == 0x55) ||
+ (data0 == 0x66 && data1 == 0x77) ||
+ (data0 == 0x88 && data1 == 0x99) ||
+ (data0 == 0xaa && data1 == 0xbb) ||
+ (data0 == 0xcc && data1 == 0xdd) ||
+ (data0 == 0xee && data1 == 0xff)) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+ input_sync(gspca_dev->input_dev);
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
+#endif
+
+/* sub-driver description for pac7302 */
+static const struct sd_desc sd_desc = {
+ .name = KBUILD_MODNAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+ .dq_callback = do_autogain,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .set_register = sd_dbg_s_register,
+ .get_chip_ident = sd_chip_ident,
+#endif
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ .int_pkt_scan = sd_int_pkt_scan,
+#endif
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x06f8, 0x3009)},
+ {USB_DEVICE(0x06f8, 0x301b)},
+ {USB_DEVICE(0x093a, 0x2620)},
+ {USB_DEVICE(0x093a, 0x2621)},
+ {USB_DEVICE(0x093a, 0x2622), .driver_info = FL_VFLIP},
+ {USB_DEVICE(0x093a, 0x2624), .driver_info = FL_VFLIP},
+ {USB_DEVICE(0x093a, 0x2625)},
+ {USB_DEVICE(0x093a, 0x2626)},
+ {USB_DEVICE(0x093a, 0x2628)},
+ {USB_DEVICE(0x093a, 0x2629), .driver_info = FL_VFLIP},
+ {USB_DEVICE(0x093a, 0x262a)},
+ {USB_DEVICE(0x093a, 0x262c)},
+ {USB_DEVICE(0x145f, 0x013c)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c
new file mode 100644
index 00000000..1ac11117
--- /dev/null
+++ b/drivers/media/video/gspca/pac7311.c
@@ -0,0 +1,871 @@
+/*
+ * Pixart PAC7311 library
+ * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* Some documentation about various registers as determined by trial and error.
+ When the register addresses differ between the 7202 and the 7311 the 2
+ different addresses are written as 7302addr/7311addr, when one of the 2
+ addresses is a - sign that register description is not valid for the
+ matching IC.
+
+ Register page 1:
+
+ Address Description
+ -/0x08 Unknown compressor related, must always be 8 except when not
+ in 640x480 resolution and page 4 reg 2 <= 3 then set it to 9 !
+ -/0x1b Auto white balance related, bit 0 is AWB enable (inverted)
+ bits 345 seem to toggle per color gains on/off (inverted)
+ 0x78 Global control, bit 6 controls the LED (inverted)
+ -/0x80 JPEG compression ratio ? Best not touched
+
+ Register page 3/4:
+
+ Address Description
+ 0x02 Clock divider 2-63, fps =~ 60 / val. Must be a multiple of 3 on
+ the 7302, so one of 3, 6, 9, ..., except when between 6 and 12?
+ -/0x0f Master gain 1-245, low value = high gain
+ 0x10/- Master gain 0-31
+ -/0x10 Another gain 0-15, limited influence (1-2x gain I guess)
+ 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused
+ -/0x27 Seems to toggle various gains on / off, Setting bit 7 seems to
+ completely disable the analog amplification block. Set to 0x68
+ for max gain, 0x14 for minimal gain.
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "pac7311"
+
+#include <linux/input.h>
+#include "gspca.h"
+
+MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li");
+MODULE_DESCRIPTION("Pixart PAC7311");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor for pac7311 */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ unsigned char contrast;
+ unsigned char gain;
+ unsigned char exposure;
+ unsigned char autogain;
+ __u8 hflip;
+ __u8 vflip;
+
+ u8 sof_read;
+ u8 autogain_ignore_frames;
+
+ atomic_t avg_lum;
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
+
+static const struct ctrl sd_ctrls[] = {
+/* This control is for both the 7302 and the 7311 */
+ {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+#define CONTRAST_MAX 255
+ .maximum = CONTRAST_MAX,
+ .step = 1,
+#define CONTRAST_DEF 127
+ .default_value = CONTRAST_DEF,
+ },
+ .set = sd_setcontrast,
+ .get = sd_getcontrast,
+ },
+/* All controls below are for both the 7302 and the 7311 */
+ {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+#define GAIN_MAX 255
+ .maximum = GAIN_MAX,
+ .step = 1,
+#define GAIN_DEF 127
+#define GAIN_KNEE 255 /* Gain seems to cause little noise on the pac73xx */
+ .default_value = GAIN_DEF,
+ },
+ .set = sd_setgain,
+ .get = sd_getgain,
+ },
+ {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 0,
+#define EXPOSURE_MAX 255
+ .maximum = EXPOSURE_MAX,
+ .step = 1,
+#define EXPOSURE_DEF 16 /* 32 ms / 30 fps */
+#define EXPOSURE_KNEE 50 /* 100 ms / 10 fps */
+ .default_value = EXPOSURE_DEF,
+ },
+ .set = sd_setexposure,
+ .get = sd_getexposure,
+ },
+ {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto Gain",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define AUTOGAIN_DEF 1
+ .default_value = AUTOGAIN_DEF,
+ },
+ .set = sd_setautogain,
+ .get = sd_getautogain,
+ },
+ {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mirror",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define HFLIP_DEF 0
+ .default_value = HFLIP_DEF,
+ },
+ .set = sd_sethflip,
+ .get = sd_gethflip,
+ },
+ {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Vflip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define VFLIP_DEF 0
+ .default_value = VFLIP_DEF,
+ },
+ .set = sd_setvflip,
+ .get = sd_getvflip,
+ },
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {160, 120, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 2},
+ {320, 240, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
+#define LOAD_PAGE4 254
+#define END_OF_SEQUENCE 0
+
+/* pac 7311 */
+static const __u8 init_7311[] = {
+ 0x78, 0x40, /* Bit_0=start stream, Bit_6=LED */
+ 0x78, 0x40, /* Bit_0=start stream, Bit_6=LED */
+ 0x78, 0x44, /* Bit_0=start stream, Bit_6=LED */
+ 0xff, 0x04,
+ 0x27, 0x80,
+ 0x28, 0xca,
+ 0x29, 0x53,
+ 0x2a, 0x0e,
+ 0xff, 0x01,
+ 0x3e, 0x20,
+};
+
+static const __u8 start_7311[] = {
+/* index, len, [value]* */
+ 0xff, 1, 0x01, /* page 1 */
+ 0x02, 43, 0x48, 0x0a, 0x40, 0x08, 0x00, 0x00, 0x08, 0x00,
+ 0x06, 0xff, 0x11, 0xff, 0x5a, 0x30, 0x90, 0x4c,
+ 0x00, 0x07, 0x00, 0x0a, 0x10, 0x00, 0xa0, 0x10,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00,
+ 0x3e, 42, 0x00, 0x00, 0x78, 0x52, 0x4a, 0x52, 0x78, 0x6e,
+ 0x48, 0x46, 0x48, 0x6e, 0x5f, 0x49, 0x42, 0x49,
+ 0x5f, 0x5f, 0x49, 0x42, 0x49, 0x5f, 0x6e, 0x48,
+ 0x46, 0x48, 0x6e, 0x78, 0x52, 0x4a, 0x52, 0x78,
+ 0x00, 0x00, 0x09, 0x1b, 0x34, 0x49, 0x5c, 0x9b,
+ 0xd0, 0xff,
+ 0x78, 6, 0x44, 0x00, 0xf2, 0x01, 0x01, 0x80,
+ 0x7f, 18, 0x2a, 0x1c, 0x00, 0xc8, 0x02, 0x58, 0x03, 0x84,
+ 0x12, 0x00, 0x1a, 0x04, 0x08, 0x0c, 0x10, 0x14,
+ 0x18, 0x20,
+ 0x96, 3, 0x01, 0x08, 0x04,
+ 0xa0, 4, 0x44, 0x44, 0x44, 0x04,
+ 0xf0, 13, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x20, 0x00,
+ 0x3f, 0x00, 0x0a, 0x01, 0x00,
+ 0xff, 1, 0x04, /* page 4 */
+ 0, LOAD_PAGE4, /* load the page 4 */
+ 0x11, 1, 0x01,
+ 0, END_OF_SEQUENCE /* end of sequence */
+};
+
+#define SKIP 0xaa
+/* page 4 - the value SKIP says skip the index - see reg_w_page() */
+static const __u8 page4_7311[] = {
+ SKIP, SKIP, 0x04, 0x54, 0x07, 0x2b, 0x09, 0x0f,
+ 0x09, 0x00, SKIP, SKIP, 0x07, 0x00, 0x00, 0x62,
+ 0x08, SKIP, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0xf4, SKIP,
+ SKIP, 0x00, 0x08, SKIP, 0x03, SKIP, 0x00, 0x68,
+ 0xca, 0x10, 0x06, 0x78, 0x00, 0x00, 0x00, 0x00,
+ 0x23, 0x28, 0x04, 0x11, 0x00, 0x00
+};
+
+static void reg_w_buf(struct gspca_dev *gspca_dev,
+ __u8 index,
+ const u8 *buffer, int len)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ memcpy(gspca_dev->usb_buf, buffer, len);
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, /* value */
+ index, gspca_dev->usb_buf, len,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_w_buf() failed index 0x%02x, error %d\n",
+ index, ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+
+static void reg_w(struct gspca_dev *gspca_dev,
+ __u8 index,
+ __u8 value)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ gspca_dev->usb_buf[0] = value;
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index, gspca_dev->usb_buf, 1,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_w() failed index 0x%02x, value 0x%02x, error %d\n",
+ index, value, ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static void reg_w_seq(struct gspca_dev *gspca_dev,
+ const __u8 *seq, int len)
+{
+ while (--len >= 0) {
+ reg_w(gspca_dev, seq[0], seq[1]);
+ seq += 2;
+ }
+}
+
+/* load the beginning of a page */
+static void reg_w_page(struct gspca_dev *gspca_dev,
+ const __u8 *page, int len)
+{
+ int index;
+ int ret = 0;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ for (index = 0; index < len; index++) {
+ if (page[index] == SKIP) /* skip this index */
+ continue;
+ gspca_dev->usb_buf[0] = page[index];
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index, gspca_dev->usb_buf, 1,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_w_page() failed index 0x%02x, value 0x%02x, error %d\n",
+ index, page[index], ret);
+ gspca_dev->usb_err = ret;
+ break;
+ }
+ }
+}
+
+/* output a variable sequence */
+static void reg_w_var(struct gspca_dev *gspca_dev,
+ const __u8 *seq,
+ const __u8 *page4, unsigned int page4_len)
+{
+ int index, len;
+
+ for (;;) {
+ index = *seq++;
+ len = *seq++;
+ switch (len) {
+ case END_OF_SEQUENCE:
+ return;
+ case LOAD_PAGE4:
+ reg_w_page(gspca_dev, page4, page4_len);
+ break;
+ default:
+ if (len > USB_BUF_SZ) {
+ PDEBUG(D_ERR|D_STREAM,
+ "Incorrect variable sequence");
+ return;
+ }
+ while (len > 0) {
+ if (len < 8) {
+ reg_w_buf(gspca_dev,
+ index, seq, len);
+ seq += len;
+ break;
+ }
+ reg_w_buf(gspca_dev, index, seq, 8);
+ seq += 8;
+ index += 8;
+ len -= 8;
+ }
+ }
+ }
+ /* not reached */
+}
+
+/* this function is called at probe time for pac7311 */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+
+ PDEBUG(D_CONF, "Find Sensor PAC7311");
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+
+ sd->contrast = CONTRAST_DEF;
+ sd->gain = GAIN_DEF;
+ sd->exposure = EXPOSURE_DEF;
+ sd->autogain = AUTOGAIN_DEF;
+ sd->hflip = HFLIP_DEF;
+ sd->vflip = VFLIP_DEF;
+ return 0;
+}
+
+/* This function is used by pac7311 only */
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w(gspca_dev, 0xff, 0x04);
+ reg_w(gspca_dev, 0x10, sd->contrast >> 4);
+ /* load registers to sensor (Bit 0, auto clear) */
+ reg_w(gspca_dev, 0x11, 0x01);
+}
+
+static void setgain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int gain = GAIN_MAX - sd->gain;
+
+ if (gain < 1)
+ gain = 1;
+ else if (gain > 245)
+ gain = 245;
+ reg_w(gspca_dev, 0xff, 0x04); /* page 4 */
+ reg_w(gspca_dev, 0x0e, 0x00);
+ reg_w(gspca_dev, 0x0f, gain);
+
+ /* load registers to sensor (Bit 0, auto clear) */
+ reg_w(gspca_dev, 0x11, 0x01);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u8 reg;
+
+ /* register 2 of frame 3/4 contains the clock divider configuring the
+ no fps according to the formula: 60 / reg. sd->exposure is the
+ desired exposure time in ms. */
+ reg = 120 * sd->exposure / 1000;
+ if (reg < 2)
+ reg = 2;
+ else if (reg > 63)
+ reg = 63;
+
+ reg_w(gspca_dev, 0xff, 0x04); /* page 4 */
+ reg_w(gspca_dev, 0x02, reg);
+
+ /* Page 1 register 8 must always be 0x08 except when not in
+ 640x480 mode and Page3/4 reg 2 <= 3 then it must be 9 */
+ reg_w(gspca_dev, 0xff, 0x01);
+ if (gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv &&
+ reg <= 3) {
+ reg_w(gspca_dev, 0x08, 0x09);
+ } else {
+ reg_w(gspca_dev, 0x08, 0x08);
+ }
+
+ /* load registers to sensor (Bit 0, auto clear) */
+ reg_w(gspca_dev, 0x11, 0x01);
+}
+
+static void sethvflip(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u8 data;
+
+ reg_w(gspca_dev, 0xff, 0x04); /* page 4 */
+ data = (sd->hflip ? 0x04 : 0x00) | (sd->vflip ? 0x08 : 0x00);
+ reg_w(gspca_dev, 0x21, data);
+
+ /* load registers to sensor (Bit 0, auto clear) */
+ reg_w(gspca_dev, 0x11, 0x01);
+}
+
+/* this function is called at probe and resume time for pac7311 */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ reg_w_seq(gspca_dev, init_7311, sizeof(init_7311)/2);
+ return gspca_dev->usb_err;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->sof_read = 0;
+
+ reg_w_var(gspca_dev, start_7311,
+ page4_7311, sizeof(page4_7311));
+ setcontrast(gspca_dev);
+ setgain(gspca_dev);
+ setexposure(gspca_dev);
+ sethvflip(gspca_dev);
+
+ /* set correct resolution */
+ switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
+ case 2: /* 160x120 pac7311 */
+ reg_w(gspca_dev, 0xff, 0x01);
+ reg_w(gspca_dev, 0x17, 0x20);
+ reg_w(gspca_dev, 0x87, 0x10);
+ break;
+ case 1: /* 320x240 pac7311 */
+ reg_w(gspca_dev, 0xff, 0x01);
+ reg_w(gspca_dev, 0x17, 0x30);
+ reg_w(gspca_dev, 0x87, 0x11);
+ break;
+ case 0: /* 640x480 */
+ reg_w(gspca_dev, 0xff, 0x01);
+ reg_w(gspca_dev, 0x17, 0x00);
+ reg_w(gspca_dev, 0x87, 0x12);
+ break;
+ }
+
+ sd->sof_read = 0;
+ sd->autogain_ignore_frames = 0;
+ atomic_set(&sd->avg_lum, -1);
+
+ /* start stream */
+ reg_w(gspca_dev, 0xff, 0x01);
+ reg_w(gspca_dev, 0x78, 0x05);
+
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ reg_w(gspca_dev, 0xff, 0x04);
+ reg_w(gspca_dev, 0x27, 0x80);
+ reg_w(gspca_dev, 0x28, 0xca);
+ reg_w(gspca_dev, 0x29, 0x53);
+ reg_w(gspca_dev, 0x2a, 0x0e);
+ reg_w(gspca_dev, 0xff, 0x01);
+ reg_w(gspca_dev, 0x3e, 0x20);
+ reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
+ reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
+ reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
+}
+
+/* called on streamoff with alt 0 and on disconnect for 7311 */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+}
+
+/* Include pac common sof detection functions */
+#include "pac_common.h"
+
+static void do_autogain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int avg_lum = atomic_read(&sd->avg_lum);
+ int desired_lum, deadzone;
+
+ if (avg_lum == -1)
+ return;
+
+ desired_lum = 200;
+ deadzone = 20;
+
+ if (sd->autogain_ignore_frames > 0)
+ sd->autogain_ignore_frames--;
+ else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum,
+ deadzone, GAIN_KNEE, EXPOSURE_KNEE))
+ sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
+}
+
+/* JPEG header, part 1 */
+static const unsigned char pac_jpeg_header1[] = {
+ 0xff, 0xd8, /* SOI: Start of Image */
+
+ 0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */
+ 0x00, 0x11, /* length = 17 bytes (including this length field) */
+ 0x08 /* Precision: 8 */
+ /* 2 bytes is placed here: number of image lines */
+ /* 2 bytes is placed here: samples per line */
+};
+
+/* JPEG header, continued */
+static const unsigned char pac_jpeg_header2[] = {
+ 0x03, /* Number of image components: 3 */
+ 0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */
+ 0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */
+ 0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */
+
+ 0xff, 0xda, /* SOS: Start Of Scan */
+ 0x00, 0x0c, /* length = 12 bytes (including this length field) */
+ 0x03, /* number of components: 3 */
+ 0x01, 0x00, /* selector 1, table 0x00 */
+ 0x02, 0x11, /* selector 2, table 0x11 */
+ 0x03, 0x11, /* selector 3, table 0x11 */
+ 0x00, 0x3f, /* Spectral selection: 0 .. 63 */
+ 0x00 /* Successive approximation: 0 */
+};
+
+static void pac_start_frame(struct gspca_dev *gspca_dev,
+ __u16 lines, __u16 samples_per_line)
+{
+ unsigned char tmpbuf[4];
+
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ pac_jpeg_header1, sizeof(pac_jpeg_header1));
+
+ tmpbuf[0] = lines >> 8;
+ tmpbuf[1] = lines & 0xff;
+ tmpbuf[2] = samples_per_line >> 8;
+ tmpbuf[3] = samples_per_line & 0xff;
+
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ tmpbuf, sizeof(tmpbuf));
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ pac_jpeg_header2, sizeof(pac_jpeg_header2));
+}
+
+/* this function is run at interrupt level */
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 *image;
+ unsigned char *sof;
+
+ sof = pac_find_sof(&sd->sof_read, data, len);
+ if (sof) {
+ int n, lum_offset, footer_length;
+
+ /* 6 bytes after the FF D9 EOF marker a number of lumination
+ bytes are send corresponding to different parts of the
+ image, the 14th and 15th byte after the EOF seem to
+ correspond to the center of the image */
+ lum_offset = 24 + sizeof pac_sof_marker;
+ footer_length = 26;
+
+ /* Finish decoding current frame */
+ n = (sof - data) - (footer_length + sizeof pac_sof_marker);
+ if (n < 0) {
+ gspca_dev->image_len += n;
+ n = 0;
+ } else {
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, n);
+ }
+ image = gspca_dev->image;
+ if (image != NULL
+ && image[gspca_dev->image_len - 2] == 0xff
+ && image[gspca_dev->image_len - 1] == 0xd9)
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+
+ n = sof - data;
+ len -= n;
+ data = sof;
+
+ /* Get average lumination */
+ if (gspca_dev->last_packet_type == LAST_PACKET &&
+ n >= lum_offset)
+ atomic_set(&sd->avg_lum, data[-lum_offset] +
+ data[-lum_offset + 1]);
+ else
+ atomic_set(&sd->avg_lum, -1);
+
+ /* Start the new frame with the jpeg header */
+ pac_start_frame(gspca_dev,
+ gspca_dev->height, gspca_dev->width);
+ }
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->contrast = val;
+ if (gspca_dev->streaming)
+ setcontrast(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->contrast;
+ return 0;
+}
+
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->gain = val;
+ if (gspca_dev->streaming)
+ setgain(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->gain;
+ return 0;
+}
+
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->exposure = val;
+ if (gspca_dev->streaming)
+ setexposure(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->exposure;
+ return 0;
+}
+
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->autogain = val;
+ /* when switching to autogain set defaults to make sure
+ we are on a valid point of the autogain gain /
+ exposure knee graph, and give this change time to
+ take effect before doing autogain. */
+ if (sd->autogain) {
+ sd->exposure = EXPOSURE_DEF;
+ sd->gain = GAIN_DEF;
+ if (gspca_dev->streaming) {
+ sd->autogain_ignore_frames =
+ PAC_AUTOGAIN_IGNORE_FRAMES;
+ setexposure(gspca_dev);
+ setgain(gspca_dev);
+ }
+ }
+
+ return gspca_dev->usb_err;
+}
+
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->autogain;
+ return 0;
+}
+
+static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->hflip = val;
+ if (gspca_dev->streaming)
+ sethvflip(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->hflip;
+ return 0;
+}
+
+static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->vflip = val;
+ if (gspca_dev->streaming)
+ sethvflip(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->vflip;
+ return 0;
+}
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* interrupt packet data */
+ int len) /* interrupt packet length */
+{
+ int ret = -EINVAL;
+ u8 data0, data1;
+
+ if (len == 2) {
+ data0 = data[0];
+ data1 = data[1];
+ if ((data0 == 0x00 && data1 == 0x11) ||
+ (data0 == 0x22 && data1 == 0x33) ||
+ (data0 == 0x44 && data1 == 0x55) ||
+ (data0 == 0x66 && data1 == 0x77) ||
+ (data0 == 0x88 && data1 == 0x99) ||
+ (data0 == 0xaa && data1 == 0xbb) ||
+ (data0 == 0xcc && data1 == 0xdd) ||
+ (data0 == 0xee && data1 == 0xff)) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+ input_sync(gspca_dev->input_dev);
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
+#endif
+
+/* sub-driver description for pac7311 */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+ .dq_callback = do_autogain,
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ .int_pkt_scan = sd_int_pkt_scan,
+#endif
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x093a, 0x2600)},
+ {USB_DEVICE(0x093a, 0x2601)},
+ {USB_DEVICE(0x093a, 0x2603)},
+ {USB_DEVICE(0x093a, 0x2608)},
+ {USB_DEVICE(0x093a, 0x260e)},
+ {USB_DEVICE(0x093a, 0x260f)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/pac_common.h b/drivers/media/video/gspca/pac_common.h
new file mode 100644
index 00000000..8462a7c1
--- /dev/null
+++ b/drivers/media/video/gspca/pac_common.h
@@ -0,0 +1,134 @@
+/*
+ * Pixart PAC207BCA / PAC73xx common functions
+ *
+ * Copyright (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl>
+ * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
+ * Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* We calculate the autogain at the end of the transfer of a frame, at this
+ moment a frame with the old settings is being captured and transmitted. So
+ if we adjust the gain or exposure we must ignore atleast the next frame for
+ the new settings to come into effect before doing any other adjustments. */
+#define PAC_AUTOGAIN_IGNORE_FRAMES 2
+
+static const unsigned char pac_sof_marker[5] =
+ { 0xff, 0xff, 0x00, 0xff, 0x96 };
+
+/*
+ The following state machine finds the SOF marker sequence
+ 0xff, 0xff, 0x00, 0xff, 0x96 in a byte stream.
+
+ +----------+
+ | 0: START |<---------------\
+ +----------+<-\ |
+ | \---/otherwise |
+ v 0xff |
+ +----------+ otherwise |
+ | 1 |--------------->*
+ | | ^
+ +----------+ |
+ | |
+ v 0xff |
+ +----------+<-\0xff |
+ /->| |--/ |
+ | | 2 |--------------->*
+ | | | otherwise ^
+ | +----------+ |
+ | | |
+ | v 0x00 |
+ | +----------+ |
+ | | 3 | |
+ | | |--------------->*
+ | +----------+ otherwise ^
+ | | |
+ 0xff | v 0xff |
+ | +----------+ |
+ \--| 4 | |
+ | |----------------/
+ +----------+ otherwise
+ |
+ v 0x96
+ +----------+
+ | FOUND |
+ +----------+
+*/
+
+static unsigned char *pac_find_sof(u8 *sof_read,
+ unsigned char *m, int len)
+{
+ int i;
+
+ /* Search for the SOF marker (fixed part) in the header */
+ for (i = 0; i < len; i++) {
+ switch (*sof_read) {
+ case 0:
+ if (m[i] == 0xff)
+ *sof_read = 1;
+ break;
+ case 1:
+ if (m[i] == 0xff)
+ *sof_read = 2;
+ else
+ *sof_read = 0;
+ break;
+ case 2:
+ switch (m[i]) {
+ case 0x00:
+ *sof_read = 3;
+ break;
+ case 0xff:
+ /* stay in this state */
+ break;
+ default:
+ *sof_read = 0;
+ }
+ break;
+ case 3:
+ if (m[i] == 0xff)
+ *sof_read = 4;
+ else
+ *sof_read = 0;
+ break;
+ case 4:
+ switch (m[i]) {
+ case 0x96:
+ /* Pattern found */
+ PDEBUG(D_FRAM,
+ "SOF found, bytes to analyze: %u."
+ " Frame starts at byte #%u",
+ len, i + 1);
+ *sof_read = 0;
+ return m + i + 1;
+ break;
+ case 0xff:
+ *sof_read = 2;
+ break;
+ default:
+ *sof_read = 0;
+ }
+ break;
+ default:
+ *sof_read = 0;
+ }
+ }
+
+ return NULL;
+}
diff --git a/drivers/media/video/gspca/se401.c b/drivers/media/video/gspca/se401.c
new file mode 100644
index 00000000..bb70092c
--- /dev/null
+++ b/drivers/media/video/gspca/se401.c
@@ -0,0 +1,777 @@
+/*
+ * GSPCA Endpoints (formerly known as AOX) se401 USB Camera sub Driver
+ *
+ * Copyright (C) 2011 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Based on the v4l1 se401 driver which is:
+ *
+ * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.org)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "se401"
+
+#define BULK_SIZE 4096
+#define PACKET_SIZE 1024
+#define READ_REQ_SIZE 64
+#define MAX_MODES ((READ_REQ_SIZE - 6) / 4)
+/* The se401 compression algorithm uses a fixed quant factor, which
+ can be configured by setting the high nibble of the SE401_OPERATINGMODE
+ feature. This needs to exactly match what is in libv4l! */
+#define SE401_QUANT_FACT 8
+
+#include <linux/input.h>
+#include <linux/slab.h>
+#include "gspca.h"
+#include "se401.h"
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Endpoints se401");
+MODULE_LICENSE("GPL");
+
+/* controls */
+enum e_ctrl {
+ BRIGHTNESS,
+ GAIN,
+ EXPOSURE,
+ FREQ,
+ NCTRL /* number of controls */
+};
+
+/* exposure change state machine states */
+enum {
+ EXPO_CHANGED,
+ EXPO_DROP_FRAME,
+ EXPO_NO_CHANGE,
+};
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ struct gspca_ctrl ctrls[NCTRL];
+ struct v4l2_pix_format fmts[MAX_MODES];
+ int pixels_read;
+ int packet_read;
+ u8 packet[PACKET_SIZE];
+ u8 restart_stream;
+ u8 button_state;
+ u8 resetlevel;
+ u8 resetlevel_frame_count;
+ int resetlevel_adjust_dir;
+ int expo_change_state;
+};
+
+static void setbrightness(struct gspca_dev *gspca_dev);
+static void setgain(struct gspca_dev *gspca_dev);
+static void setexposure(struct gspca_dev *gspca_dev);
+
+static const struct ctrl sd_ctrls[NCTRL] = {
+[BRIGHTNESS] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 15,
+ },
+ .set_control = setbrightness
+ },
+[GAIN] = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 50, /* Really 63 but > 50 is not pretty */
+ .step = 1,
+ .default_value = 25,
+ },
+ .set_control = setgain
+ },
+[EXPOSURE] = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 0,
+ .maximum = 32767,
+ .step = 1,
+ .default_value = 15000,
+ },
+ .set_control = setexposure
+ },
+[FREQ] = {
+ {
+ .id = V4L2_CID_POWER_LINE_FREQUENCY,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Light frequency filter",
+ .minimum = 0,
+ .maximum = 2,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set_control = setexposure
+ },
+};
+
+static void se401_write_req(struct gspca_dev *gspca_dev, u16 req, u16 value,
+ int silent)
+{
+ int err;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+
+ err = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0), req,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, 0, NULL, 0, 1000);
+ if (err < 0) {
+ if (!silent)
+ pr_err("write req failed req %#04x val %#04x error %d\n",
+ req, value, err);
+ gspca_dev->usb_err = err;
+ }
+}
+
+static void se401_read_req(struct gspca_dev *gspca_dev, u16 req, int silent)
+{
+ int err;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+
+ if (USB_BUF_SZ < READ_REQ_SIZE) {
+ pr_err("USB_BUF_SZ too small!!\n");
+ gspca_dev->usb_err = -ENOBUFS;
+ return;
+ }
+
+ err = usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0), req,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, 0, gspca_dev->usb_buf, READ_REQ_SIZE, 1000);
+ if (err < 0) {
+ if (!silent)
+ pr_err("read req failed req %#04x error %d\n",
+ req, err);
+ gspca_dev->usb_err = err;
+ }
+}
+
+static void se401_set_feature(struct gspca_dev *gspca_dev,
+ u16 selector, u16 param)
+{
+ int err;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+
+ err = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ SE401_REQ_SET_EXT_FEATURE,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ param, selector, NULL, 0, 1000);
+ if (err < 0) {
+ pr_err("set feature failed sel %#04x param %#04x error %d\n",
+ selector, param, err);
+ gspca_dev->usb_err = err;
+ }
+}
+
+static int se401_get_feature(struct gspca_dev *gspca_dev, u16 selector)
+{
+ int err;
+
+ if (gspca_dev->usb_err < 0)
+ return gspca_dev->usb_err;
+
+ if (USB_BUF_SZ < 2) {
+ pr_err("USB_BUF_SZ too small!!\n");
+ gspca_dev->usb_err = -ENOBUFS;
+ return gspca_dev->usb_err;
+ }
+
+ err = usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ SE401_REQ_GET_EXT_FEATURE,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, selector, gspca_dev->usb_buf, 2, 1000);
+ if (err < 0) {
+ pr_err("get feature failed sel %#04x error %d\n",
+ selector, err);
+ gspca_dev->usb_err = err;
+ return err;
+ }
+ return gspca_dev->usb_buf[0] | (gspca_dev->usb_buf[1] << 8);
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS))
+ return;
+
+ /* HDG: this does not seem to do anything on my cam */
+ se401_write_req(gspca_dev, SE401_REQ_SET_BRT,
+ sd->ctrls[BRIGHTNESS].val, 0);
+}
+
+static void setgain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 gain = 63 - sd->ctrls[GAIN].val;
+
+ /* red color gain */
+ se401_set_feature(gspca_dev, HV7131_REG_ARCG, gain);
+ /* green color gain */
+ se401_set_feature(gspca_dev, HV7131_REG_AGCG, gain);
+ /* blue color gain */
+ se401_set_feature(gspca_dev, HV7131_REG_ABCG, gain);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int integration = sd->ctrls[EXPOSURE].val << 6;
+ u8 expose_h, expose_m, expose_l;
+
+ /* Do this before the set_feature calls, for proper timing wrt
+ the interrupt driven pkt_scan. Note we may still race but that
+ is not a big issue, the expo change state machine is merely for
+ avoiding underexposed frames getting send out, if one sneaks
+ through so be it */
+ sd->expo_change_state = EXPO_CHANGED;
+
+ if (sd->ctrls[FREQ].val == V4L2_CID_POWER_LINE_FREQUENCY_50HZ)
+ integration = integration - integration % 106667;
+ if (sd->ctrls[FREQ].val == V4L2_CID_POWER_LINE_FREQUENCY_60HZ)
+ integration = integration - integration % 88889;
+
+ expose_h = (integration >> 16);
+ expose_m = (integration >> 8);
+ expose_l = integration;
+
+ /* integration time low */
+ se401_set_feature(gspca_dev, HV7131_REG_TITL, expose_l);
+ /* integration time mid */
+ se401_set_feature(gspca_dev, HV7131_REG_TITM, expose_m);
+ /* integration time high */
+ se401_set_feature(gspca_dev, HV7131_REG_TITU, expose_h);
+}
+
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *)gspca_dev;
+ struct cam *cam = &gspca_dev->cam;
+ u8 *cd = gspca_dev->usb_buf;
+ int i, j, n;
+ int widths[MAX_MODES], heights[MAX_MODES];
+
+ /* Read the camera descriptor */
+ se401_read_req(gspca_dev, SE401_REQ_GET_CAMERA_DESCRIPTOR, 1);
+ if (gspca_dev->usb_err) {
+ /* Sometimes after being idle for a while the se401 won't
+ respond and needs a good kicking */
+ usb_reset_device(gspca_dev->dev);
+ gspca_dev->usb_err = 0;
+ se401_read_req(gspca_dev, SE401_REQ_GET_CAMERA_DESCRIPTOR, 0);
+ }
+
+ /* Some cameras start with their LED on */
+ se401_write_req(gspca_dev, SE401_REQ_LED_CONTROL, 0, 0);
+ if (gspca_dev->usb_err)
+ return gspca_dev->usb_err;
+
+ if (cd[1] != 0x41) {
+ pr_err("Wrong descriptor type\n");
+ return -ENODEV;
+ }
+
+ if (!(cd[2] & SE401_FORMAT_BAYER)) {
+ pr_err("Bayer format not supported!\n");
+ return -ENODEV;
+ }
+
+ if (cd[3])
+ pr_info("ExtraFeatures: %d\n", cd[3]);
+
+ n = cd[4] | (cd[5] << 8);
+ if (n > MAX_MODES) {
+ pr_err("Too many frame sizes\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; i < n ; i++) {
+ widths[i] = cd[6 + i * 4 + 0] | (cd[6 + i * 4 + 1] << 8);
+ heights[i] = cd[6 + i * 4 + 2] | (cd[6 + i * 4 + 3] << 8);
+ }
+
+ for (i = 0; i < n ; i++) {
+ sd->fmts[i].width = widths[i];
+ sd->fmts[i].height = heights[i];
+ sd->fmts[i].field = V4L2_FIELD_NONE;
+ sd->fmts[i].colorspace = V4L2_COLORSPACE_SRGB;
+ sd->fmts[i].priv = 1;
+
+ /* janggu compression only works for 1/4th or 1/16th res */
+ for (j = 0; j < n; j++) {
+ if (widths[j] / 2 == widths[i] &&
+ heights[j] / 2 == heights[i]) {
+ sd->fmts[i].priv = 2;
+ break;
+ }
+ }
+ /* 1/16th if available too is better then 1/4th, because
+ we then use a larger area of the sensor */
+ for (j = 0; j < n; j++) {
+ if (widths[j] / 4 == widths[i] &&
+ heights[j] / 4 == heights[i]) {
+ sd->fmts[i].priv = 4;
+ break;
+ }
+ }
+
+ if (sd->fmts[i].priv == 1) {
+ /* Not a 1/4th or 1/16th res, use bayer */
+ sd->fmts[i].pixelformat = V4L2_PIX_FMT_SBGGR8;
+ sd->fmts[i].bytesperline = widths[i];
+ sd->fmts[i].sizeimage = widths[i] * heights[i];
+ pr_info("Frame size: %dx%d bayer\n",
+ widths[i], heights[i]);
+ } else {
+ /* Found a match use janggu compression */
+ sd->fmts[i].pixelformat = V4L2_PIX_FMT_SE401;
+ sd->fmts[i].bytesperline = 0;
+ sd->fmts[i].sizeimage = widths[i] * heights[i] * 3;
+ pr_info("Frame size: %dx%d 1/%dth janggu\n",
+ widths[i], heights[i],
+ sd->fmts[i].priv * sd->fmts[i].priv);
+ }
+ }
+
+ cam->cam_mode = sd->fmts;
+ cam->nmodes = n;
+ cam->bulk = 1;
+ cam->bulk_size = BULK_SIZE;
+ cam->bulk_nurbs = 4;
+ cam->ctrls = sd->ctrls;
+ sd->resetlevel = 0x2d; /* Set initial resetlevel */
+
+ /* See if the camera supports brightness */
+ se401_read_req(gspca_dev, SE401_REQ_GET_BRT, 1);
+ if (gspca_dev->usb_err) {
+ gspca_dev->ctrl_dis = (1 << BRIGHTNESS);
+ gspca_dev->usb_err = 0;
+ }
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ return 0;
+}
+
+/* function called at start time before URB creation */
+static int sd_isoc_init(struct gspca_dev *gspca_dev)
+{
+ gspca_dev->alt = 1; /* Ignore the bogus isoc alt settings */
+
+ return gspca_dev->usb_err;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *)gspca_dev;
+ int mult = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+ int mode = 0;
+
+ se401_write_req(gspca_dev, SE401_REQ_CAMERA_POWER, 1, 1);
+ if (gspca_dev->usb_err) {
+ /* Sometimes after being idle for a while the se401 won't
+ respond and needs a good kicking */
+ usb_reset_device(gspca_dev->dev);
+ gspca_dev->usb_err = 0;
+ se401_write_req(gspca_dev, SE401_REQ_CAMERA_POWER, 1, 0);
+ }
+ se401_write_req(gspca_dev, SE401_REQ_LED_CONTROL, 1, 0);
+
+ se401_set_feature(gspca_dev, HV7131_REG_MODE_B, 0x05);
+
+ /* set size + mode */
+ se401_write_req(gspca_dev, SE401_REQ_SET_WIDTH,
+ gspca_dev->width * mult, 0);
+ se401_write_req(gspca_dev, SE401_REQ_SET_HEIGHT,
+ gspca_dev->height * mult, 0);
+ /*
+ * HDG: disabled this as it does not seem to do anything
+ * se401_write_req(gspca_dev, SE401_REQ_SET_OUTPUT_MODE,
+ * SE401_FORMAT_BAYER, 0);
+ */
+
+ switch (mult) {
+ case 1: /* Raw bayer */
+ mode = 0x03; break;
+ case 2: /* 1/4th janggu */
+ mode = SE401_QUANT_FACT << 4; break;
+ case 4: /* 1/16th janggu */
+ mode = (SE401_QUANT_FACT << 4) | 0x02; break;
+ }
+ se401_set_feature(gspca_dev, SE401_OPERATINGMODE, mode);
+
+ setbrightness(gspca_dev);
+ setgain(gspca_dev);
+ setexposure(gspca_dev);
+ se401_set_feature(gspca_dev, HV7131_REG_ARLV, sd->resetlevel);
+
+ sd->packet_read = 0;
+ sd->pixels_read = 0;
+ sd->restart_stream = 0;
+ sd->resetlevel_frame_count = 0;
+ sd->resetlevel_adjust_dir = 0;
+ sd->expo_change_state = EXPO_NO_CHANGE;
+
+ se401_write_req(gspca_dev, SE401_REQ_START_CONTINUOUS_CAPTURE, 0, 0);
+
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ se401_write_req(gspca_dev, SE401_REQ_STOP_CONTINUOUS_CAPTURE, 0, 0);
+ se401_write_req(gspca_dev, SE401_REQ_LED_CONTROL, 0, 0);
+ se401_write_req(gspca_dev, SE401_REQ_CAMERA_POWER, 0, 0);
+}
+
+static void sd_dq_callback(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *)gspca_dev;
+ unsigned int ahrc, alrc;
+ int oldreset, adjust_dir;
+
+ /* Restart the stream if requested do so by pkt_scan */
+ if (sd->restart_stream) {
+ sd_stopN(gspca_dev);
+ sd_start(gspca_dev);
+ sd->restart_stream = 0;
+ }
+
+ /* Automatically adjust sensor reset level
+ Hyundai have some really nice docs about this and other sensor
+ related stuff on their homepage: www.hei.co.kr */
+ sd->resetlevel_frame_count++;
+ if (sd->resetlevel_frame_count < 20)
+ return;
+
+ /* For some reason this normally read-only register doesn't get reset
+ to zero after reading them just once... */
+ se401_get_feature(gspca_dev, HV7131_REG_HIREFNOH);
+ se401_get_feature(gspca_dev, HV7131_REG_HIREFNOL);
+ se401_get_feature(gspca_dev, HV7131_REG_LOREFNOH);
+ se401_get_feature(gspca_dev, HV7131_REG_LOREFNOL);
+ ahrc = 256*se401_get_feature(gspca_dev, HV7131_REG_HIREFNOH) +
+ se401_get_feature(gspca_dev, HV7131_REG_HIREFNOL);
+ alrc = 256*se401_get_feature(gspca_dev, HV7131_REG_LOREFNOH) +
+ se401_get_feature(gspca_dev, HV7131_REG_LOREFNOL);
+
+ /* Not an exact science, but it seems to work pretty well... */
+ oldreset = sd->resetlevel;
+ if (alrc > 10) {
+ while (alrc >= 10 && sd->resetlevel < 63) {
+ sd->resetlevel++;
+ alrc /= 2;
+ }
+ } else if (ahrc > 20) {
+ while (ahrc >= 20 && sd->resetlevel > 0) {
+ sd->resetlevel--;
+ ahrc /= 2;
+ }
+ }
+ /* Detect ping-pong-ing and halve adjustment to avoid overshoot */
+ if (sd->resetlevel > oldreset)
+ adjust_dir = 1;
+ else
+ adjust_dir = -1;
+ if (sd->resetlevel_adjust_dir &&
+ sd->resetlevel_adjust_dir != adjust_dir)
+ sd->resetlevel = oldreset + (sd->resetlevel - oldreset) / 2;
+
+ if (sd->resetlevel != oldreset) {
+ sd->resetlevel_adjust_dir = adjust_dir;
+ se401_set_feature(gspca_dev, HV7131_REG_ARLV, sd->resetlevel);
+ }
+
+ sd->resetlevel_frame_count = 0;
+}
+
+static void sd_complete_frame(struct gspca_dev *gspca_dev, u8 *data, int len)
+{
+ struct sd *sd = (struct sd *)gspca_dev;
+
+ switch (sd->expo_change_state) {
+ case EXPO_CHANGED:
+ /* The exposure was changed while this frame
+ was being send, so this frame is ok */
+ sd->expo_change_state = EXPO_DROP_FRAME;
+ break;
+ case EXPO_DROP_FRAME:
+ /* The exposure was changed while this frame
+ was being captured, drop it! */
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ sd->expo_change_state = EXPO_NO_CHANGE;
+ break;
+ case EXPO_NO_CHANGE:
+ break;
+ }
+ gspca_frame_add(gspca_dev, LAST_PACKET, data, len);
+}
+
+static void sd_pkt_scan_janggu(struct gspca_dev *gspca_dev, u8 *data, int len)
+{
+ struct sd *sd = (struct sd *)gspca_dev;
+ int imagesize = gspca_dev->width * gspca_dev->height;
+ int i, plen, bits, pixels, info, count;
+
+ if (sd->restart_stream)
+ return;
+
+ /* Sometimes a 1024 bytes garbage bulk packet is send between frames */
+ if (gspca_dev->last_packet_type == LAST_PACKET && len == 1024) {
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
+ }
+
+ i = 0;
+ while (i < len) {
+ /* Read header if not already be present from prev bulk pkt */
+ if (sd->packet_read < 4) {
+ count = 4 - sd->packet_read;
+ if (count > len - i)
+ count = len - i;
+ memcpy(&sd->packet[sd->packet_read], &data[i], count);
+ sd->packet_read += count;
+ i += count;
+ if (sd->packet_read < 4)
+ break;
+ }
+ bits = sd->packet[3] + (sd->packet[2] << 8);
+ pixels = sd->packet[1] + ((sd->packet[0] & 0x3f) << 8);
+ info = (sd->packet[0] & 0xc0) >> 6;
+ plen = ((bits + 47) >> 4) << 1;
+ /* Sanity checks */
+ if (plen > 1024) {
+ pr_err("invalid packet len %d restarting stream\n",
+ plen);
+ goto error;
+ }
+ if (info == 3) {
+ pr_err("unknown frame info value restarting stream\n");
+ goto error;
+ }
+
+ /* Read (remainder of) packet contents */
+ count = plen - sd->packet_read;
+ if (count > len - i)
+ count = len - i;
+ memcpy(&sd->packet[sd->packet_read], &data[i], count);
+ sd->packet_read += count;
+ i += count;
+ if (sd->packet_read < plen)
+ break;
+
+ sd->pixels_read += pixels;
+ sd->packet_read = 0;
+
+ switch (info) {
+ case 0: /* Frame data */
+ gspca_frame_add(gspca_dev, INTER_PACKET, sd->packet,
+ plen);
+ break;
+ case 1: /* EOF */
+ if (sd->pixels_read != imagesize) {
+ pr_err("frame size %d expected %d\n",
+ sd->pixels_read, imagesize);
+ goto error;
+ }
+ sd_complete_frame(gspca_dev, sd->packet, plen);
+ return; /* Discard the rest of the bulk packet !! */
+ case 2: /* SOF */
+ gspca_frame_add(gspca_dev, FIRST_PACKET, sd->packet,
+ plen);
+ sd->pixels_read = pixels;
+ break;
+ }
+ }
+ return;
+
+error:
+ sd->restart_stream = 1;
+ /* Give userspace a 0 bytes frame, so our dq callback gets
+ called and it can restart the stream */
+ gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+}
+
+static void sd_pkt_scan_bayer(struct gspca_dev *gspca_dev, u8 *data, int len)
+{
+ struct cam *cam = &gspca_dev->cam;
+ int imagesize = cam->cam_mode[gspca_dev->curr_mode].sizeimage;
+
+ if (gspca_dev->image_len == 0) {
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+ return;
+ }
+
+ if (gspca_dev->image_len + len >= imagesize) {
+ sd_complete_frame(gspca_dev, data, len);
+ return;
+ }
+
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, int len)
+{
+ int mult = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+
+ if (len == 0)
+ return;
+
+ if (mult == 1) /* mult == 1 means raw bayer */
+ sd_pkt_scan_bayer(gspca_dev, data, len);
+ else
+ sd_pkt_scan_janggu(gspca_dev, data, len);
+}
+
+static int sd_querymenu(struct gspca_dev *gspca_dev,
+ struct v4l2_querymenu *menu)
+{
+ switch (menu->id) {
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ switch (menu->index) {
+ case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED:
+ strcpy((char *) menu->name, "NoFliker");
+ return 0;
+ case V4L2_CID_POWER_LINE_FREQUENCY_50HZ:
+ strcpy((char *) menu->name, "50 Hz");
+ return 0;
+ case V4L2_CID_POWER_LINE_FREQUENCY_60HZ:
+ strcpy((char *) menu->name, "60 Hz");
+ return 0;
+ }
+ break;
+ }
+ return -EINVAL;
+}
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, int len)
+{
+ struct sd *sd = (struct sd *)gspca_dev;
+ u8 state;
+
+ if (len != 2)
+ return -EINVAL;
+
+ switch (data[0]) {
+ case 0:
+ case 1:
+ state = data[0];
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (sd->button_state != state) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, state);
+ input_sync(gspca_dev->input_dev);
+ sd->button_state = state;
+ }
+
+ return 0;
+}
+#endif
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .isoc_init = sd_isoc_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .dq_callback = sd_dq_callback,
+ .pkt_scan = sd_pkt_scan,
+ .querymenu = sd_querymenu,
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ .int_pkt_scan = sd_int_pkt_scan,
+#endif
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x03e8, 0x0004)}, /* Endpoints/Aox SE401 */
+ {USB_DEVICE(0x0471, 0x030b)}, /* Philips PCVC665K */
+ {USB_DEVICE(0x047d, 0x5001)}, /* Kensington 67014 */
+ {USB_DEVICE(0x047d, 0x5002)}, /* Kensington 6701(5/7) */
+ {USB_DEVICE(0x047d, 0x5003)}, /* Kensington 67016 */
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static int sd_pre_reset(struct usb_interface *intf)
+{
+ return 0;
+}
+
+static int sd_post_reset(struct usb_interface *intf)
+{
+ return 0;
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+ .pre_reset = sd_pre_reset,
+ .post_reset = sd_post_reset,
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/se401.h b/drivers/media/video/gspca/se401.h
new file mode 100644
index 00000000..96d8ebf3
--- /dev/null
+++ b/drivers/media/video/gspca/se401.h
@@ -0,0 +1,90 @@
+/*
+ * GSPCA Endpoints (formerly known as AOX) se401 USB Camera sub Driver
+ *
+ * Copyright (C) 2011 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Based on the v4l1 se401 driver which is:
+ *
+ * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.org)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define SE401_REQ_GET_CAMERA_DESCRIPTOR 0x06
+#define SE401_REQ_START_CONTINUOUS_CAPTURE 0x41
+#define SE401_REQ_STOP_CONTINUOUS_CAPTURE 0x42
+#define SE401_REQ_CAPTURE_FRAME 0x43
+#define SE401_REQ_GET_BRT 0x44
+#define SE401_REQ_SET_BRT 0x45
+#define SE401_REQ_GET_WIDTH 0x4c
+#define SE401_REQ_SET_WIDTH 0x4d
+#define SE401_REQ_GET_HEIGHT 0x4e
+#define SE401_REQ_SET_HEIGHT 0x4f
+#define SE401_REQ_GET_OUTPUT_MODE 0x50
+#define SE401_REQ_SET_OUTPUT_MODE 0x51
+#define SE401_REQ_GET_EXT_FEATURE 0x52
+#define SE401_REQ_SET_EXT_FEATURE 0x53
+#define SE401_REQ_CAMERA_POWER 0x56
+#define SE401_REQ_LED_CONTROL 0x57
+#define SE401_REQ_BIOS 0xff
+
+#define SE401_BIOS_READ 0x07
+
+#define SE401_FORMAT_BAYER 0x40
+
+/* Hyundai hv7131b registers
+ 7121 and 7141 should be the same (haven't really checked...) */
+/* Mode registers: */
+#define HV7131_REG_MODE_A 0x00
+#define HV7131_REG_MODE_B 0x01
+#define HV7131_REG_MODE_C 0x02
+/* Frame registers: */
+#define HV7131_REG_FRSU 0x10
+#define HV7131_REG_FRSL 0x11
+#define HV7131_REG_FCSU 0x12
+#define HV7131_REG_FCSL 0x13
+#define HV7131_REG_FWHU 0x14
+#define HV7131_REG_FWHL 0x15
+#define HV7131_REG_FWWU 0x16
+#define HV7131_REG_FWWL 0x17
+/* Timing registers: */
+#define HV7131_REG_THBU 0x20
+#define HV7131_REG_THBL 0x21
+#define HV7131_REG_TVBU 0x22
+#define HV7131_REG_TVBL 0x23
+#define HV7131_REG_TITU 0x25
+#define HV7131_REG_TITM 0x26
+#define HV7131_REG_TITL 0x27
+#define HV7131_REG_TMCD 0x28
+/* Adjust Registers: */
+#define HV7131_REG_ARLV 0x30
+#define HV7131_REG_ARCG 0x31
+#define HV7131_REG_AGCG 0x32
+#define HV7131_REG_ABCG 0x33
+#define HV7131_REG_APBV 0x34
+#define HV7131_REG_ASLP 0x54
+/* Offset Registers: */
+#define HV7131_REG_OFSR 0x50
+#define HV7131_REG_OFSG 0x51
+#define HV7131_REG_OFSB 0x52
+/* REset level statistics registers: */
+#define HV7131_REG_LOREFNOH 0x57
+#define HV7131_REG_LOREFNOL 0x58
+#define HV7131_REG_HIREFNOH 0x59
+#define HV7131_REG_HIREFNOL 0x5a
+
+/* se401 registers */
+#define SE401_OPERATINGMODE 0x2000
diff --git a/drivers/media/video/gspca/sn9c2028.c b/drivers/media/video/gspca/sn9c2028.c
new file mode 100644
index 00000000..478533cb
--- /dev/null
+++ b/drivers/media/video/gspca/sn9c2028.c
@@ -0,0 +1,740 @@
+/*
+ * SN9C2028 library
+ *
+ * Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "sn9c2028"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Theodore Kilgore");
+MODULE_DESCRIPTION("Sonix SN9C2028 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ u8 sof_read;
+ u16 model;
+};
+
+struct init_command {
+ unsigned char instruction[6];
+ unsigned char to_read; /* length to read. 0 means no reply requested */
+};
+
+/* V4L2 controls supported by the driver */
+static const struct ctrl sd_ctrls[] = {
+};
+
+/* How to change the resolution of any of the VGA cams is unknown */
+static const struct v4l2_pix_format vga_mode[] = {
+ {640, 480, V4L2_PIX_FMT_SN9C2028, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 4,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+
+/* No way to change the resolution of the CIF cams is known */
+static const struct v4l2_pix_format cif_mode[] = {
+ {352, 288, V4L2_PIX_FMT_SN9C2028, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288 * 3 / 4,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+
+/* the bytes to write are in gspca_dev->usb_buf */
+static int sn9c2028_command(struct gspca_dev *gspca_dev, u8 *command)
+{
+ int rc;
+
+ PDEBUG(D_USBO, "sending command %02x%02x%02x%02x%02x%02x", command[0],
+ command[1], command[2], command[3], command[4], command[5]);
+
+ memcpy(gspca_dev->usb_buf, command, 6);
+ rc = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ USB_REQ_GET_CONFIGURATION,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ 2, 0, gspca_dev->usb_buf, 6, 500);
+ if (rc < 0) {
+ pr_err("command write [%02x] error %d\n",
+ gspca_dev->usb_buf[0], rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int sn9c2028_read1(struct gspca_dev *gspca_dev)
+{
+ int rc;
+
+ rc = usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ USB_REQ_GET_STATUS,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ 1, 0, gspca_dev->usb_buf, 1, 500);
+ if (rc != 1) {
+ pr_err("read1 error %d\n", rc);
+ return (rc < 0) ? rc : -EIO;
+ }
+ PDEBUG(D_USBI, "read1 response %02x", gspca_dev->usb_buf[0]);
+ return gspca_dev->usb_buf[0];
+}
+
+static int sn9c2028_read4(struct gspca_dev *gspca_dev, u8 *reading)
+{
+ int rc;
+ rc = usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ USB_REQ_GET_STATUS,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ 4, 0, gspca_dev->usb_buf, 4, 500);
+ if (rc != 4) {
+ pr_err("read4 error %d\n", rc);
+ return (rc < 0) ? rc : -EIO;
+ }
+ memcpy(reading, gspca_dev->usb_buf, 4);
+ PDEBUG(D_USBI, "read4 response %02x%02x%02x%02x", reading[0],
+ reading[1], reading[2], reading[3]);
+ return rc;
+}
+
+static int sn9c2028_long_command(struct gspca_dev *gspca_dev, u8 *command)
+{
+ int i, status;
+ __u8 reading[4];
+
+ status = sn9c2028_command(gspca_dev, command);
+ if (status < 0)
+ return status;
+
+ status = -1;
+ for (i = 0; i < 256 && status < 2; i++)
+ status = sn9c2028_read1(gspca_dev);
+ if (status != 2) {
+ pr_err("long command status read error %d\n", status);
+ return (status < 0) ? status : -EIO;
+ }
+
+ memset(reading, 0, 4);
+ status = sn9c2028_read4(gspca_dev, reading);
+ if (status < 0)
+ return status;
+
+ /* in general, the first byte of the response is the first byte of
+ * the command, or'ed with 8 */
+ status = sn9c2028_read1(gspca_dev);
+ if (status < 0)
+ return status;
+
+ return 0;
+}
+
+static int sn9c2028_short_command(struct gspca_dev *gspca_dev, u8 *command)
+{
+ int err_code;
+
+ err_code = sn9c2028_command(gspca_dev, command);
+ if (err_code < 0)
+ return err_code;
+
+ err_code = sn9c2028_read1(gspca_dev);
+ if (err_code < 0)
+ return err_code;
+
+ return 0;
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam = &gspca_dev->cam;
+
+ PDEBUG(D_PROBE, "SN9C2028 camera detected (vid/pid 0x%04X:0x%04X)",
+ id->idVendor, id->idProduct);
+
+ sd->model = id->idProduct;
+
+ switch (sd->model) {
+ case 0x7005:
+ PDEBUG(D_PROBE, "Genius Smart 300 camera");
+ break;
+ case 0x8000:
+ PDEBUG(D_PROBE, "DC31VC");
+ break;
+ case 0x8001:
+ PDEBUG(D_PROBE, "Spy camera");
+ break;
+ case 0x8003:
+ PDEBUG(D_PROBE, "CIF camera");
+ break;
+ case 0x8008:
+ PDEBUG(D_PROBE, "Mini-Shotz ms-350 camera");
+ break;
+ case 0x800a:
+ PDEBUG(D_PROBE, "Vivitar 3350b type camera");
+ cam->input_flags = V4L2_IN_ST_VFLIP | V4L2_IN_ST_HFLIP;
+ break;
+ }
+
+ switch (sd->model) {
+ case 0x8000:
+ case 0x8001:
+ case 0x8003:
+ cam->cam_mode = cif_mode;
+ cam->nmodes = ARRAY_SIZE(cif_mode);
+ break;
+ default:
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+ }
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ int status = -1;
+
+ sn9c2028_read1(gspca_dev);
+ sn9c2028_read1(gspca_dev);
+ status = sn9c2028_read1(gspca_dev);
+
+ return (status < 0) ? status : 0;
+}
+
+static int run_start_commands(struct gspca_dev *gspca_dev,
+ struct init_command *cam_commands, int n)
+{
+ int i, err_code = -1;
+
+ for (i = 0; i < n; i++) {
+ switch (cam_commands[i].to_read) {
+ case 4:
+ err_code = sn9c2028_long_command(gspca_dev,
+ cam_commands[i].instruction);
+ break;
+ case 1:
+ err_code = sn9c2028_short_command(gspca_dev,
+ cam_commands[i].instruction);
+ break;
+ case 0:
+ err_code = sn9c2028_command(gspca_dev,
+ cam_commands[i].instruction);
+ break;
+ }
+ if (err_code < 0)
+ return err_code;
+ }
+ return 0;
+}
+
+static int start_spy_cam(struct gspca_dev *gspca_dev)
+{
+ struct init_command spy_start_commands[] = {
+ {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x22, 0x01, 0x04, 0x00, 0x00}, 4},
+ {{0x13, 0x23, 0x01, 0x03, 0x00, 0x00}, 4},
+ {{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4}, /* width 352 */
+ {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4}, /* height 288 */
+ /* {{0x13, 0x27, 0x01, 0x28, 0x00, 0x00}, 4}, */
+ {{0x13, 0x27, 0x01, 0x68, 0x00, 0x00}, 4},
+ {{0x13, 0x28, 0x01, 0x09, 0x00, 0x00}, 4}, /* red gain ?*/
+ /* {{0x13, 0x28, 0x01, 0x00, 0x00, 0x00}, 4}, */
+ {{0x13, 0x29, 0x01, 0x00, 0x00, 0x00}, 4},
+ /* {{0x13, 0x29, 0x01, 0x0c, 0x00, 0x00}, 4}, */
+ {{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4},
+ /* {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, */
+ {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
+ {{0x13, 0x2d, 0x01, 0x02, 0x00, 0x00}, 4},
+ /* {{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4}, */
+ {{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4},
+ {{0x13, 0x2f, 0x01, 0x07, 0x00, 0x00}, 4},
+ {{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4},
+ {{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x02, 0x06, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x03, 0x13, 0x00, 0x00, 0x00}, 4}, /*don't mess with*/
+ /*{{0x11, 0x04, 0x06, 0x00, 0x00, 0x00}, 4}, observed */
+ {{0x11, 0x04, 0x00, 0x00, 0x00, 0x00}, 4}, /* brighter */
+ /*{{0x11, 0x05, 0x65, 0x00, 0x00, 0x00}, 4}, observed */
+ {{0x11, 0x05, 0x00, 0x00, 0x00, 0x00}, 4}, /* brighter */
+ {{0x11, 0x06, 0xb1, 0x00, 0x00, 0x00}, 4}, /* observed */
+ {{0x11, 0x07, 0x00, 0x00, 0x00, 0x00}, 4},
+ /*{{0x11, 0x08, 0x06, 0x00, 0x00, 0x00}, 4}, observed */
+ {{0x11, 0x08, 0x0b, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x09, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x0a, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x0b, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x0c, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x0d, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x0e, 0x04, 0x00, 0x00, 0x00}, 4},
+ /* {{0x11, 0x0f, 0x00, 0x00, 0x00, 0x00}, 4}, */
+ /* brightness or gain. 0 is default. 4 is good
+ * indoors at night with incandescent lighting */
+ {{0x11, 0x0f, 0x04, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x10, 0x06, 0x00, 0x00, 0x00}, 4}, /*hstart or hoffs*/
+ {{0x11, 0x11, 0x06, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x14, 0x02, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x13, 0x01, 0x00, 0x00, 0x00}, 4},
+ /* {{0x1b, 0x02, 0x06, 0x00, 0x00, 0x00}, 1}, observed */
+ {{0x1b, 0x02, 0x11, 0x00, 0x00, 0x00}, 1}, /* brighter */
+ /* {{0x1b, 0x13, 0x01, 0x00, 0x00, 0x00}, 1}, observed */
+ {{0x1b, 0x13, 0x11, 0x00, 0x00, 0x00}, 1},
+ {{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 1}, /* compresses */
+ /* Camera should start to capture now. */
+ };
+
+ return run_start_commands(gspca_dev, spy_start_commands,
+ ARRAY_SIZE(spy_start_commands));
+}
+
+static int start_cif_cam(struct gspca_dev *gspca_dev)
+{
+ struct init_command cif_start_commands[] = {
+ {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
+ /* The entire sequence below seems redundant */
+ /* {{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x22, 0x01, 0x06, 0x00, 0x00}, 4},
+ {{0x13, 0x23, 0x01, 0x02, 0x00, 0x00}, 4},
+ {{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4}, width?
+ {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4}, height?
+ {{0x13, 0x27, 0x01, 0x68, 0x00, 0x00}, 4}, subsample?
+ {{0x13, 0x28, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x29, 0x01, 0x20, 0x00, 0x00}, 4},
+ {{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
+ {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4},
+ {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4},
+ {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4},
+ {{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4},
+ {{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4},*/
+ {{0x1b, 0x21, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x17, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x19, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x02, 0x06, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x03, 0x5a, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x04, 0x27, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x05, 0x01, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x12, 0x14, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x13, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x14, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x15, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x16, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x77, 0xa2, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x06, 0x0f, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x07, 0x14, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x08, 0x0f, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x09, 0x10, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x0e, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x0f, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x12, 0x07, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x10, 0x1f, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 1},
+ {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 1}, /* width/8 */
+ {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 1}, /* height/8 */
+ /* {{0x13, 0x27, 0x01, 0x68, 0x00, 0x00}, 4}, subsample?
+ * {{0x13, 0x28, 0x01, 0x1e, 0x00, 0x00}, 4}, does nothing
+ * {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4}, */
+ /* {{0x13, 0x29, 0x01, 0x22, 0x00, 0x00}, 4},
+ * causes subsampling
+ * but not a change in the resolution setting! */
+ {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
+ {{0x13, 0x2d, 0x01, 0x01, 0x00, 0x00}, 4},
+ {{0x13, 0x2e, 0x01, 0x08, 0x00, 0x00}, 4},
+ {{0x13, 0x2f, 0x01, 0x06, 0x00, 0x00}, 4},
+ {{0x13, 0x28, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x1b, 0x04, 0x6d, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x05, 0x03, 0x00, 0x00, 0x00}, 1},
+ {{0x20, 0x36, 0x06, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x0e, 0x01, 0x00, 0x00, 0x00}, 1},
+ {{0x12, 0x27, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x1b, 0x0f, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x20, 0x36, 0x05, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x10, 0x0f, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x02, 0x06, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 1},
+ {{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 1},/* use compression */
+ /* Camera should start to capture now. */
+ };
+
+ return run_start_commands(gspca_dev, cif_start_commands,
+ ARRAY_SIZE(cif_start_commands));
+}
+
+static int start_ms350_cam(struct gspca_dev *gspca_dev)
+{
+ struct init_command ms350_start_commands[] = {
+ {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x16, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x22, 0x01, 0x04, 0x00, 0x00}, 4},
+ {{0x13, 0x23, 0x01, 0x03, 0x00, 0x00}, 4},
+ {{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4},
+ {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4},
+ {{0x13, 0x27, 0x01, 0x28, 0x00, 0x00}, 4},
+ {{0x13, 0x28, 0x01, 0x09, 0x00, 0x00}, 4},
+ {{0x13, 0x29, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
+ {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4},
+ {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4},
+ {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4},
+ {{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4},
+ {{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x00, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x01, 0x70, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x02, 0x05, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x03, 0x5d, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x04, 0x07, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x05, 0x25, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x06, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x07, 0x09, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x08, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x09, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x0a, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x0b, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x0c, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x0d, 0x0c, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x0e, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x0f, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x11, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x13, 0x63, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x15, 0x70, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x18, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x11, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4}, /* width */
+ {{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4}, /* height */
+ {{0x13, 0x28, 0x01, 0x09, 0x00, 0x00}, 4}, /* vstart? */
+ {{0x13, 0x27, 0x01, 0x28, 0x00, 0x00}, 4},
+ {{0x13, 0x29, 0x01, 0x40, 0x00, 0x00}, 4}, /* hstart? */
+ {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
+ {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4},
+ {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4},
+ {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4},
+ {{0x1b, 0x02, 0x05, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 1},
+ {{0x20, 0x18, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x02, 0x0a, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 0},
+ /* Camera should start to capture now. */
+ };
+
+ return run_start_commands(gspca_dev, ms350_start_commands,
+ ARRAY_SIZE(ms350_start_commands));
+}
+
+static int start_genius_cam(struct gspca_dev *gspca_dev)
+{
+ struct init_command genius_start_commands[] = {
+ {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x16, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x10, 0x00, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4},
+ {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4},
+ /* "preliminary" width and height settings */
+ {{0x13, 0x28, 0x01, 0x0e, 0x00, 0x00}, 4},
+ {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4},
+ {{0x13, 0x29, 0x01, 0x22, 0x00, 0x00}, 4},
+ {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
+ {{0x13, 0x2d, 0x01, 0x02, 0x00, 0x00}, 4},
+ {{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4},
+ {{0x13, 0x2f, 0x01, 0x07, 0x00, 0x00}, 4},
+ {{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x21, 0x2d, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x23, 0x03, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x11, 0x64, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x13, 0x91, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x14, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x15, 0x20, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x16, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x17, 0x60, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x21, 0x2d, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x23, 0x03, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x25, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x26, 0x02, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x27, 0x88, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x30, 0x38, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x31, 0x2a, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x32, 0x2a, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x33, 0x2a, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x34, 0x02, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x5b, 0x0a, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4}, /* real width */
+ {{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4}, /* real height */
+ {{0x13, 0x28, 0x01, 0x0e, 0x00, 0x00}, 4},
+ {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4},
+ {{0x13, 0x29, 0x01, 0x62, 0x00, 0x00}, 4},
+ {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
+ {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4},
+ {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4},
+ {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4},
+ {{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x21, 0x2a, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x23, 0x28, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x11, 0x04, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x13, 0x03, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x14, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x15, 0xe0, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x16, 0x02, 0x00, 0x00, 0x00}, 4},
+ {{0x11, 0x17, 0x80, 0x00, 0x00, 0x00}, 4},
+ {{0x1c, 0x20, 0x00, 0x2a, 0x00, 0x00}, 1},
+ {{0x1c, 0x20, 0x00, 0x2a, 0x00, 0x00}, 1},
+ {{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 0}
+ /* Camera should start to capture now. */
+ };
+
+ return run_start_commands(gspca_dev, genius_start_commands,
+ ARRAY_SIZE(genius_start_commands));
+}
+
+static int start_vivitar_cam(struct gspca_dev *gspca_dev)
+{
+ struct init_command vivitar_start_commands[] = {
+ {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x22, 0x01, 0x01, 0x00, 0x00}, 4},
+ {{0x13, 0x23, 0x01, 0x01, 0x00, 0x00}, 4},
+ {{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4},
+ {{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4},
+ {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4},
+ {{0x13, 0x28, 0x01, 0x0a, 0x00, 0x00}, 4},
+ /*
+ * Above is changed from OEM 0x0b. Fixes Bayer tiling.
+ * Presumably gives a vertical shift of one row.
+ */
+ {{0x13, 0x29, 0x01, 0x20, 0x00, 0x00}, 4},
+ /* Above seems to do horizontal shift. */
+ {{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
+ {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4},
+ {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4},
+ {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4},
+ /* Above three commands seem to relate to brightness. */
+ {{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4},
+ {{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x1b, 0x12, 0x80, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x01, 0x77, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x02, 0x3a, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x12, 0x78, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x13, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x14, 0x80, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x15, 0x34, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x1b, 0x04, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x20, 0x44, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x23, 0xee, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x26, 0xa0, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x27, 0x9a, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x28, 0xa0, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x29, 0x30, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x2a, 0x80, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x2b, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x2f, 0x3d, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x30, 0x24, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x32, 0x86, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x60, 0xa9, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x61, 0x42, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x65, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x69, 0x38, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x6f, 0x88, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x70, 0x0b, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x71, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x74, 0x21, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x75, 0x86, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x76, 0x00, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x7d, 0xf3, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x17, 0x1c, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x18, 0xc0, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x19, 0x05, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x1a, 0xf6, 0x00, 0x00, 0x00}, 1},
+ /* {{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4},
+ {{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4},
+ {{0x13, 0x28, 0x01, 0x0b, 0x00, 0x00}, 4}, */
+ {{0x20, 0x36, 0x06, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x10, 0x26, 0x00, 0x00, 0x00}, 1},
+ {{0x12, 0x27, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x1b, 0x76, 0x03, 0x00, 0x00, 0x00}, 1},
+ {{0x20, 0x36, 0x05, 0x00, 0x00, 0x00}, 1},
+ {{0x1b, 0x00, 0x3f, 0x00, 0x00, 0x00}, 1},
+ /* Above is brightness; OEM driver setting is 0x10 */
+ {{0x12, 0x27, 0x01, 0x00, 0x00, 0x00}, 4},
+ {{0x20, 0x29, 0x30, 0x00, 0x00, 0x00}, 1},
+ {{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 1}
+ };
+
+ return run_start_commands(gspca_dev, vivitar_start_commands,
+ ARRAY_SIZE(vivitar_start_commands));
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int err_code;
+
+ sd->sof_read = 0;
+
+ switch (sd->model) {
+ case 0x7005:
+ err_code = start_genius_cam(gspca_dev);
+ break;
+ case 0x8001:
+ err_code = start_spy_cam(gspca_dev);
+ break;
+ case 0x8003:
+ err_code = start_cif_cam(gspca_dev);
+ break;
+ case 0x8008:
+ err_code = start_ms350_cam(gspca_dev);
+ break;
+ case 0x800a:
+ err_code = start_vivitar_cam(gspca_dev);
+ break;
+ default:
+ pr_err("Starting unknown camera, please report this\n");
+ return -ENXIO;
+ }
+
+ return err_code;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ int result;
+ __u8 data[6];
+
+ result = sn9c2028_read1(gspca_dev);
+ if (result < 0)
+ PDEBUG(D_ERR, "Camera Stop read failed");
+
+ memset(data, 0, 6);
+ data[0] = 0x14;
+ result = sn9c2028_command(gspca_dev, data);
+ if (result < 0)
+ PDEBUG(D_ERR, "Camera Stop command failed");
+}
+
+/* Include sn9c2028 sof detection functions */
+#include "sn9c2028.h"
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ __u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ unsigned char *sof;
+
+ sof = sn9c2028_find_sof(gspca_dev, data, len);
+ if (sof) {
+ int n;
+
+ /* finish decoding current frame */
+ n = sof - data;
+ if (n > sizeof sn9c2028_sof_marker)
+ n -= sizeof sn9c2028_sof_marker;
+ else
+ n = 0;
+ gspca_frame_add(gspca_dev, LAST_PACKET, data, n);
+ /* Start next frame. */
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sn9c2028_sof_marker, sizeof sn9c2028_sof_marker);
+ len -= sof - data;
+ data = sof;
+ }
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x0458, 0x7005)}, /* Genius Smart 300, version 2 */
+ /* The Genius Smart is untested. I can't find an owner ! */
+ /* {USB_DEVICE(0x0c45, 0x8000)}, DC31VC, Don't know this camera */
+ {USB_DEVICE(0x0c45, 0x8001)}, /* Wild Planet digital spy cam */
+ {USB_DEVICE(0x0c45, 0x8003)}, /* Several small CIF cameras */
+ /* {USB_DEVICE(0x0c45, 0x8006)}, Unknown VGA camera */
+ {USB_DEVICE(0x0c45, 0x8008)}, /* Mini-Shotz ms-350 */
+ {USB_DEVICE(0x0c45, 0x800a)}, /* Vivicam 3350B */
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/sn9c2028.h b/drivers/media/video/gspca/sn9c2028.h
new file mode 100644
index 00000000..8fd1d3e0
--- /dev/null
+++ b/drivers/media/video/gspca/sn9c2028.h
@@ -0,0 +1,51 @@
+/*
+ * SN9C2028 common functions
+ *
+ * Copyright (C) 2009 Theodore Kilgore <kilgota@auburn,edu>
+ *
+ * Based closely upon the file gspca/pac_common.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+static const unsigned char sn9c2028_sof_marker[5] =
+ { 0xff, 0xff, 0x00, 0xc4, 0xc4 };
+
+static unsigned char *sn9c2028_find_sof(struct gspca_dev *gspca_dev,
+ unsigned char *m, int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+
+ /* Search for the SOF marker (fixed part) in the header */
+ for (i = 0; i < len; i++) {
+ if (m[i] == sn9c2028_sof_marker[sd->sof_read]) {
+ sd->sof_read++;
+ if (sd->sof_read == sizeof(sn9c2028_sof_marker)) {
+ PDEBUG(D_FRAM,
+ "SOF found, bytes to analyze: %u."
+ " Frame starts at byte #%u",
+ len, i + 1);
+ sd->sof_read = 0;
+ return m + i + 1;
+ }
+ } else {
+ sd->sof_read = 0;
+ }
+ }
+
+ return NULL;
+}
diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c
new file mode 100644
index 00000000..7e71aa2d
--- /dev/null
+++ b/drivers/media/video/gspca/sn9c20x.c
@@ -0,0 +1,2463 @@
+/*
+ * Sonix sn9c201 sn9c202 library
+ *
+ * Copyright (C) 2012 Jean-Francois Moine <http://moinejf.free.fr>
+ * Copyright (C) 2008-2009 microdia project <microdia@googlegroups.com>
+ * Copyright (C) 2009 Brian Johnson <brijohn@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/input.h>
+
+#include "gspca.h"
+#include "jpeg.h"
+
+#include <media/v4l2-chip-ident.h>
+#include <linux/dmi.h>
+
+MODULE_AUTHOR("Brian Johnson <brijohn@gmail.com>, "
+ "microdia project <microdia@googlegroups.com>");
+MODULE_DESCRIPTION("GSPCA/SN9C20X USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/*
+ * Pixel format private data
+ */
+#define SCALE_MASK 0x0f
+#define SCALE_160x120 0
+#define SCALE_320x240 1
+#define SCALE_640x480 2
+#define SCALE_1280x1024 3
+#define MODE_RAW 0x10
+#define MODE_JPEG 0x20
+#define MODE_SXGA 0x80
+
+#define SENSOR_OV9650 0
+#define SENSOR_OV9655 1
+#define SENSOR_SOI968 2
+#define SENSOR_OV7660 3
+#define SENSOR_OV7670 4
+#define SENSOR_MT9V011 5
+#define SENSOR_MT9V111 6
+#define SENSOR_MT9V112 7
+#define SENSOR_MT9M001 8
+#define SENSOR_MT9M111 9
+#define SENSOR_MT9M112 10
+#define SENSOR_HV7131R 11
+#define SENSOR_MT9VPRB 20
+
+/* camera flags */
+#define HAS_NO_BUTTON 0x1
+#define LED_REVERSE 0x2 /* some cameras unset gpio to turn on leds */
+#define FLIP_DETECT 0x4
+
+enum e_ctrl {
+ BRIGHTNESS,
+ CONTRAST,
+ SATURATION,
+ HUE,
+ GAMMA,
+ BLUE,
+ RED,
+ VFLIP,
+ HFLIP,
+ EXPOSURE,
+ GAIN,
+ AUTOGAIN,
+ QUALITY,
+ NCTRLS /* number of controls */
+};
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev;
+
+ struct gspca_ctrl ctrls[NCTRLS];
+
+ struct work_struct work;
+ struct workqueue_struct *work_thread;
+
+ u32 pktsz; /* (used by pkt_scan) */
+ u16 npkt;
+ s8 nchg;
+ u8 fmt; /* (used for JPEG QTAB update */
+
+#define MIN_AVG_LUM 80
+#define MAX_AVG_LUM 130
+ atomic_t avg_lum;
+ u8 old_step;
+ u8 older_step;
+ u8 exposure_step;
+
+ u8 i2c_addr;
+ u8 sensor;
+ u8 hstart;
+ u8 vstart;
+
+ u8 jpeg_hdr[JPEG_HDR_SZ];
+
+ u8 flags;
+};
+
+static void qual_upd(struct work_struct *work);
+
+struct i2c_reg_u8 {
+ u8 reg;
+ u8 val;
+};
+
+struct i2c_reg_u16 {
+ u8 reg;
+ u16 val;
+};
+
+static const struct dmi_system_id flip_dmi_table[] = {
+ {
+ .ident = "MSI MS-1034",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MS-1034"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "0341")
+ }
+ },
+ {
+ .ident = "MSI MS-1632",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
+ DMI_MATCH(DMI_BOARD_NAME, "MS-1632")
+ }
+ },
+ {
+ .ident = "MSI MS-1633X",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
+ DMI_MATCH(DMI_BOARD_NAME, "MS-1633X")
+ }
+ },
+ {
+ .ident = "MSI MS-1635X",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
+ DMI_MATCH(DMI_BOARD_NAME, "MS-1635X")
+ }
+ },
+ {
+ .ident = "ASUSTeK W7J",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_BOARD_NAME, "W7J ")
+ }
+ },
+ {}
+};
+
+static void set_cmatrix(struct gspca_dev *gspca_dev);
+static void set_gamma(struct gspca_dev *gspca_dev);
+static void set_redblue(struct gspca_dev *gspca_dev);
+static void set_hvflip(struct gspca_dev *gspca_dev);
+static void set_exposure(struct gspca_dev *gspca_dev);
+static void set_gain(struct gspca_dev *gspca_dev);
+static void set_quality(struct gspca_dev *gspca_dev);
+
+static const struct ctrl sd_ctrls[NCTRLS] = {
+[BRIGHTNESS] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7f
+ },
+ .set_control = set_cmatrix
+ },
+[CONTRAST] = {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7f
+ },
+ .set_control = set_cmatrix
+ },
+[SATURATION] = {
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7f
+ },
+ .set_control = set_cmatrix
+ },
+[HUE] = {
+ {
+ .id = V4L2_CID_HUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Hue",
+ .minimum = -180,
+ .maximum = 180,
+ .step = 1,
+ .default_value = 0
+ },
+ .set_control = set_cmatrix
+ },
+[GAMMA] = {
+ {
+ .id = V4L2_CID_GAMMA,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gamma",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x10
+ },
+ .set_control = set_gamma
+ },
+[BLUE] = {
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Balance",
+ .minimum = 0,
+ .maximum = 0x7f,
+ .step = 1,
+ .default_value = 0x28
+ },
+ .set_control = set_redblue
+ },
+[RED] = {
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Balance",
+ .minimum = 0,
+ .maximum = 0x7f,
+ .step = 1,
+ .default_value = 0x28
+ },
+ .set_control = set_redblue
+ },
+[HFLIP] = {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Horizontal Flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set_control = set_hvflip
+ },
+[VFLIP] = {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Vertical Flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set_control = set_hvflip
+ },
+[EXPOSURE] = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 0,
+ .maximum = 0x1780,
+ .step = 1,
+ .default_value = 0x33,
+ },
+ .set_control = set_exposure
+ },
+[GAIN] = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 28,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set_control = set_gain
+ },
+[AUTOGAIN] = {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto Exposure",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ },
+ },
+[QUALITY] = {
+ {
+ .id = V4L2_CID_JPEG_COMPRESSION_QUALITY,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Compression Quality",
+#define QUALITY_MIN 50
+#define QUALITY_MAX 90
+#define QUALITY_DEF 80
+ .minimum = QUALITY_MIN,
+ .maximum = QUALITY_MAX,
+ .step = 1,
+ .default_value = QUALITY_DEF,
+ },
+ .set_control = set_quality
+ },
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120 * 4 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = SCALE_160x120 | MODE_JPEG},
+ {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_160x120 | MODE_RAW},
+ {160, 120, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 240 * 120,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_160x120},
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 4 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = SCALE_320x240 | MODE_JPEG},
+ {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 ,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_320x240 | MODE_RAW},
+ {320, 240, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 480 * 240 ,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_320x240},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 4 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = SCALE_640x480 | MODE_JPEG},
+ {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_640x480 | MODE_RAW},
+ {640, 480, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 960 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_640x480},
+};
+
+static const struct v4l2_pix_format sxga_mode[] = {
+ {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120 * 4 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = SCALE_160x120 | MODE_JPEG},
+ {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_160x120 | MODE_RAW},
+ {160, 120, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 240 * 120,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_160x120},
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 4 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = SCALE_320x240 | MODE_JPEG},
+ {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 ,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_320x240 | MODE_RAW},
+ {320, 240, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 480 * 240 ,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_320x240},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 4 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = SCALE_640x480 | MODE_JPEG},
+ {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_640x480 | MODE_RAW},
+ {640, 480, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 960 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_640x480},
+ {1280, 1024, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 1280,
+ .sizeimage = 1280 * 1024,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_1280x1024 | MODE_RAW | MODE_SXGA},
+};
+
+static const struct v4l2_pix_format mono_mode[] = {
+ {160, 120, V4L2_PIX_FMT_GREY, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_160x120 | MODE_RAW},
+ {320, 240, V4L2_PIX_FMT_GREY, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 ,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_320x240 | MODE_RAW},
+ {640, 480, V4L2_PIX_FMT_GREY, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_640x480 | MODE_RAW},
+ {1280, 1024, V4L2_PIX_FMT_GREY, V4L2_FIELD_NONE,
+ .bytesperline = 1280,
+ .sizeimage = 1280 * 1024,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_1280x1024 | MODE_RAW | MODE_SXGA},
+};
+
+static const s16 hsv_red_x[] = {
+ 41, 44, 46, 48, 50, 52, 54, 56,
+ 58, 60, 62, 64, 66, 68, 70, 72,
+ 74, 76, 78, 80, 81, 83, 85, 87,
+ 88, 90, 92, 93, 95, 97, 98, 100,
+ 101, 102, 104, 105, 107, 108, 109, 110,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 123, 124, 125, 125,
+ 126, 127, 127, 128, 128, 129, 129, 129,
+ 130, 130, 130, 130, 131, 131, 131, 131,
+ 131, 131, 131, 131, 130, 130, 130, 130,
+ 129, 129, 129, 128, 128, 127, 127, 126,
+ 125, 125, 124, 123, 122, 122, 121, 120,
+ 119, 118, 117, 116, 115, 114, 112, 111,
+ 110, 109, 107, 106, 105, 103, 102, 101,
+ 99, 98, 96, 94, 93, 91, 90, 88,
+ 86, 84, 83, 81, 79, 77, 75, 74,
+ 72, 70, 68, 66, 64, 62, 60, 58,
+ 56, 54, 52, 49, 47, 45, 43, 41,
+ 39, 36, 34, 32, 30, 28, 25, 23,
+ 21, 19, 16, 14, 12, 9, 7, 5,
+ 3, 0, -1, -3, -6, -8, -10, -12,
+ -15, -17, -19, -22, -24, -26, -28, -30,
+ -33, -35, -37, -39, -41, -44, -46, -48,
+ -50, -52, -54, -56, -58, -60, -62, -64,
+ -66, -68, -70, -72, -74, -76, -78, -80,
+ -81, -83, -85, -87, -88, -90, -92, -93,
+ -95, -97, -98, -100, -101, -102, -104, -105,
+ -107, -108, -109, -110, -112, -113, -114, -115,
+ -116, -117, -118, -119, -120, -121, -122, -123,
+ -123, -124, -125, -125, -126, -127, -127, -128,
+ -128, -128, -128, -128, -128, -128, -128, -128,
+ -128, -128, -128, -128, -128, -128, -128, -128,
+ -128, -128, -128, -128, -128, -128, -128, -128,
+ -128, -127, -127, -126, -125, -125, -124, -123,
+ -122, -122, -121, -120, -119, -118, -117, -116,
+ -115, -114, -112, -111, -110, -109, -107, -106,
+ -105, -103, -102, -101, -99, -98, -96, -94,
+ -93, -91, -90, -88, -86, -84, -83, -81,
+ -79, -77, -75, -74, -72, -70, -68, -66,
+ -64, -62, -60, -58, -56, -54, -52, -49,
+ -47, -45, -43, -41, -39, -36, -34, -32,
+ -30, -28, -25, -23, -21, -19, -16, -14,
+ -12, -9, -7, -5, -3, 0, 1, 3,
+ 6, 8, 10, 12, 15, 17, 19, 22,
+ 24, 26, 28, 30, 33, 35, 37, 39, 41
+};
+
+static const s16 hsv_red_y[] = {
+ 82, 80, 78, 76, 74, 73, 71, 69,
+ 67, 65, 63, 61, 58, 56, 54, 52,
+ 50, 48, 46, 44, 41, 39, 37, 35,
+ 32, 30, 28, 26, 23, 21, 19, 16,
+ 14, 12, 10, 7, 5, 3, 0, -1,
+ -3, -6, -8, -10, -13, -15, -17, -19,
+ -22, -24, -26, -29, -31, -33, -35, -38,
+ -40, -42, -44, -46, -48, -51, -53, -55,
+ -57, -59, -61, -63, -65, -67, -69, -71,
+ -73, -75, -77, -79, -81, -82, -84, -86,
+ -88, -89, -91, -93, -94, -96, -98, -99,
+ -101, -102, -104, -105, -106, -108, -109, -110,
+ -112, -113, -114, -115, -116, -117, -119, -120,
+ -120, -121, -122, -123, -124, -125, -126, -126,
+ -127, -128, -128, -128, -128, -128, -128, -128,
+ -128, -128, -128, -128, -128, -128, -128, -128,
+ -128, -128, -128, -128, -128, -128, -128, -128,
+ -128, -128, -128, -128, -128, -128, -128, -128,
+ -127, -127, -126, -125, -125, -124, -123, -122,
+ -121, -120, -119, -118, -117, -116, -115, -114,
+ -113, -111, -110, -109, -107, -106, -105, -103,
+ -102, -100, -99, -97, -96, -94, -92, -91,
+ -89, -87, -85, -84, -82, -80, -78, -76,
+ -74, -73, -71, -69, -67, -65, -63, -61,
+ -58, -56, -54, -52, -50, -48, -46, -44,
+ -41, -39, -37, -35, -32, -30, -28, -26,
+ -23, -21, -19, -16, -14, -12, -10, -7,
+ -5, -3, 0, 1, 3, 6, 8, 10,
+ 13, 15, 17, 19, 22, 24, 26, 29,
+ 31, 33, 35, 38, 40, 42, 44, 46,
+ 48, 51, 53, 55, 57, 59, 61, 63,
+ 65, 67, 69, 71, 73, 75, 77, 79,
+ 81, 82, 84, 86, 88, 89, 91, 93,
+ 94, 96, 98, 99, 101, 102, 104, 105,
+ 106, 108, 109, 110, 112, 113, 114, 115,
+ 116, 117, 119, 120, 120, 121, 122, 123,
+ 124, 125, 126, 126, 127, 128, 128, 129,
+ 129, 130, 130, 131, 131, 131, 131, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 131, 131, 131, 130, 130,
+ 130, 129, 129, 128, 127, 127, 126, 125,
+ 125, 124, 123, 122, 121, 120, 119, 118,
+ 117, 116, 115, 114, 113, 111, 110, 109,
+ 107, 106, 105, 103, 102, 100, 99, 97,
+ 96, 94, 92, 91, 89, 87, 85, 84, 82
+};
+
+static const s16 hsv_green_x[] = {
+ -124, -124, -125, -125, -125, -125, -125, -125,
+ -125, -126, -126, -125, -125, -125, -125, -125,
+ -125, -124, -124, -124, -123, -123, -122, -122,
+ -121, -121, -120, -120, -119, -118, -117, -117,
+ -116, -115, -114, -113, -112, -111, -110, -109,
+ -108, -107, -105, -104, -103, -102, -100, -99,
+ -98, -96, -95, -93, -92, -91, -89, -87,
+ -86, -84, -83, -81, -79, -77, -76, -74,
+ -72, -70, -69, -67, -65, -63, -61, -59,
+ -57, -55, -53, -51, -49, -47, -45, -43,
+ -41, -39, -37, -35, -33, -30, -28, -26,
+ -24, -22, -20, -18, -15, -13, -11, -9,
+ -7, -4, -2, 0, 1, 3, 6, 8,
+ 10, 12, 14, 17, 19, 21, 23, 25,
+ 27, 29, 32, 34, 36, 38, 40, 42,
+ 44, 46, 48, 50, 52, 54, 56, 58,
+ 60, 62, 64, 66, 68, 70, 71, 73,
+ 75, 77, 78, 80, 82, 83, 85, 87,
+ 88, 90, 91, 93, 94, 96, 97, 98,
+ 100, 101, 102, 104, 105, 106, 107, 108,
+ 109, 111, 112, 113, 113, 114, 115, 116,
+ 117, 118, 118, 119, 120, 120, 121, 122,
+ 122, 123, 123, 124, 124, 124, 125, 125,
+ 125, 125, 125, 125, 125, 126, 126, 125,
+ 125, 125, 125, 125, 125, 124, 124, 124,
+ 123, 123, 122, 122, 121, 121, 120, 120,
+ 119, 118, 117, 117, 116, 115, 114, 113,
+ 112, 111, 110, 109, 108, 107, 105, 104,
+ 103, 102, 100, 99, 98, 96, 95, 93,
+ 92, 91, 89, 87, 86, 84, 83, 81,
+ 79, 77, 76, 74, 72, 70, 69, 67,
+ 65, 63, 61, 59, 57, 55, 53, 51,
+ 49, 47, 45, 43, 41, 39, 37, 35,
+ 33, 30, 28, 26, 24, 22, 20, 18,
+ 15, 13, 11, 9, 7, 4, 2, 0,
+ -1, -3, -6, -8, -10, -12, -14, -17,
+ -19, -21, -23, -25, -27, -29, -32, -34,
+ -36, -38, -40, -42, -44, -46, -48, -50,
+ -52, -54, -56, -58, -60, -62, -64, -66,
+ -68, -70, -71, -73, -75, -77, -78, -80,
+ -82, -83, -85, -87, -88, -90, -91, -93,
+ -94, -96, -97, -98, -100, -101, -102, -104,
+ -105, -106, -107, -108, -109, -111, -112, -113,
+ -113, -114, -115, -116, -117, -118, -118, -119,
+ -120, -120, -121, -122, -122, -123, -123, -124, -124
+};
+
+static const s16 hsv_green_y[] = {
+ -100, -99, -98, -97, -95, -94, -93, -91,
+ -90, -89, -87, -86, -84, -83, -81, -80,
+ -78, -76, -75, -73, -71, -70, -68, -66,
+ -64, -63, -61, -59, -57, -55, -53, -51,
+ -49, -48, -46, -44, -42, -40, -38, -36,
+ -34, -32, -30, -27, -25, -23, -21, -19,
+ -17, -15, -13, -11, -9, -7, -4, -2,
+ 0, 1, 3, 5, 7, 9, 11, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46,
+ 48, 50, 52, 54, 56, 58, 59, 61,
+ 63, 65, 67, 68, 70, 72, 74, 75,
+ 77, 78, 80, 82, 83, 85, 86, 88,
+ 89, 90, 92, 93, 95, 96, 97, 98,
+ 100, 101, 102, 103, 104, 105, 106, 107,
+ 108, 109, 110, 111, 112, 112, 113, 114,
+ 115, 115, 116, 116, 117, 117, 118, 118,
+ 119, 119, 119, 120, 120, 120, 120, 120,
+ 121, 121, 121, 121, 121, 121, 120, 120,
+ 120, 120, 120, 119, 119, 119, 118, 118,
+ 117, 117, 116, 116, 115, 114, 114, 113,
+ 112, 111, 111, 110, 109, 108, 107, 106,
+ 105, 104, 103, 102, 100, 99, 98, 97,
+ 95, 94, 93, 91, 90, 89, 87, 86,
+ 84, 83, 81, 80, 78, 76, 75, 73,
+ 71, 70, 68, 66, 64, 63, 61, 59,
+ 57, 55, 53, 51, 49, 48, 46, 44,
+ 42, 40, 38, 36, 34, 32, 30, 27,
+ 25, 23, 21, 19, 17, 15, 13, 11,
+ 9, 7, 4, 2, 0, -1, -3, -5,
+ -7, -9, -11, -14, -16, -18, -20, -22,
+ -24, -26, -28, -30, -32, -34, -36, -38,
+ -40, -42, -44, -46, -48, -50, -52, -54,
+ -56, -58, -59, -61, -63, -65, -67, -68,
+ -70, -72, -74, -75, -77, -78, -80, -82,
+ -83, -85, -86, -88, -89, -90, -92, -93,
+ -95, -96, -97, -98, -100, -101, -102, -103,
+ -104, -105, -106, -107, -108, -109, -110, -111,
+ -112, -112, -113, -114, -115, -115, -116, -116,
+ -117, -117, -118, -118, -119, -119, -119, -120,
+ -120, -120, -120, -120, -121, -121, -121, -121,
+ -121, -121, -120, -120, -120, -120, -120, -119,
+ -119, -119, -118, -118, -117, -117, -116, -116,
+ -115, -114, -114, -113, -112, -111, -111, -110,
+ -109, -108, -107, -106, -105, -104, -103, -102, -100
+};
+
+static const s16 hsv_blue_x[] = {
+ 112, 113, 114, 114, 115, 116, 117, 117,
+ 118, 118, 119, 119, 120, 120, 120, 121,
+ 121, 121, 122, 122, 122, 122, 122, 122,
+ 122, 122, 122, 122, 122, 122, 121, 121,
+ 121, 120, 120, 120, 119, 119, 118, 118,
+ 117, 116, 116, 115, 114, 113, 113, 112,
+ 111, 110, 109, 108, 107, 106, 105, 104,
+ 103, 102, 100, 99, 98, 97, 95, 94,
+ 93, 91, 90, 88, 87, 85, 84, 82,
+ 80, 79, 77, 76, 74, 72, 70, 69,
+ 67, 65, 63, 61, 60, 58, 56, 54,
+ 52, 50, 48, 46, 44, 42, 40, 38,
+ 36, 34, 32, 30, 28, 26, 24, 22,
+ 19, 17, 15, 13, 11, 9, 7, 5,
+ 2, 0, -1, -3, -5, -7, -9, -12,
+ -14, -16, -18, -20, -22, -24, -26, -28,
+ -31, -33, -35, -37, -39, -41, -43, -45,
+ -47, -49, -51, -53, -54, -56, -58, -60,
+ -62, -64, -66, -67, -69, -71, -73, -74,
+ -76, -78, -79, -81, -83, -84, -86, -87,
+ -89, -90, -92, -93, -94, -96, -97, -98,
+ -99, -101, -102, -103, -104, -105, -106, -107,
+ -108, -109, -110, -111, -112, -113, -114, -114,
+ -115, -116, -117, -117, -118, -118, -119, -119,
+ -120, -120, -120, -121, -121, -121, -122, -122,
+ -122, -122, -122, -122, -122, -122, -122, -122,
+ -122, -122, -121, -121, -121, -120, -120, -120,
+ -119, -119, -118, -118, -117, -116, -116, -115,
+ -114, -113, -113, -112, -111, -110, -109, -108,
+ -107, -106, -105, -104, -103, -102, -100, -99,
+ -98, -97, -95, -94, -93, -91, -90, -88,
+ -87, -85, -84, -82, -80, -79, -77, -76,
+ -74, -72, -70, -69, -67, -65, -63, -61,
+ -60, -58, -56, -54, -52, -50, -48, -46,
+ -44, -42, -40, -38, -36, -34, -32, -30,
+ -28, -26, -24, -22, -19, -17, -15, -13,
+ -11, -9, -7, -5, -2, 0, 1, 3,
+ 5, 7, 9, 12, 14, 16, 18, 20,
+ 22, 24, 26, 28, 31, 33, 35, 37,
+ 39, 41, 43, 45, 47, 49, 51, 53,
+ 54, 56, 58, 60, 62, 64, 66, 67,
+ 69, 71, 73, 74, 76, 78, 79, 81,
+ 83, 84, 86, 87, 89, 90, 92, 93,
+ 94, 96, 97, 98, 99, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111, 112
+};
+
+static const s16 hsv_blue_y[] = {
+ -11, -13, -15, -17, -19, -21, -23, -25,
+ -27, -29, -31, -33, -35, -37, -39, -41,
+ -43, -45, -46, -48, -50, -52, -54, -55,
+ -57, -59, -61, -62, -64, -66, -67, -69,
+ -71, -72, -74, -75, -77, -78, -80, -81,
+ -83, -84, -86, -87, -88, -90, -91, -92,
+ -93, -95, -96, -97, -98, -99, -100, -101,
+ -102, -103, -104, -105, -106, -106, -107, -108,
+ -109, -109, -110, -111, -111, -112, -112, -113,
+ -113, -114, -114, -114, -115, -115, -115, -115,
+ -116, -116, -116, -116, -116, -116, -116, -116,
+ -116, -115, -115, -115, -115, -114, -114, -114,
+ -113, -113, -112, -112, -111, -111, -110, -110,
+ -109, -108, -108, -107, -106, -105, -104, -103,
+ -102, -101, -100, -99, -98, -97, -96, -95,
+ -94, -93, -91, -90, -89, -88, -86, -85,
+ -84, -82, -81, -79, -78, -76, -75, -73,
+ -71, -70, -68, -67, -65, -63, -62, -60,
+ -58, -56, -55, -53, -51, -49, -47, -45,
+ -44, -42, -40, -38, -36, -34, -32, -30,
+ -28, -26, -24, -22, -20, -18, -16, -14,
+ -12, -10, -8, -6, -4, -2, 0, 1,
+ 3, 5, 7, 9, 11, 13, 15, 17,
+ 19, 21, 23, 25, 27, 29, 31, 33,
+ 35, 37, 39, 41, 43, 45, 46, 48,
+ 50, 52, 54, 55, 57, 59, 61, 62,
+ 64, 66, 67, 69, 71, 72, 74, 75,
+ 77, 78, 80, 81, 83, 84, 86, 87,
+ 88, 90, 91, 92, 93, 95, 96, 97,
+ 98, 99, 100, 101, 102, 103, 104, 105,
+ 106, 106, 107, 108, 109, 109, 110, 111,
+ 111, 112, 112, 113, 113, 114, 114, 114,
+ 115, 115, 115, 115, 116, 116, 116, 116,
+ 116, 116, 116, 116, 116, 115, 115, 115,
+ 115, 114, 114, 114, 113, 113, 112, 112,
+ 111, 111, 110, 110, 109, 108, 108, 107,
+ 106, 105, 104, 103, 102, 101, 100, 99,
+ 98, 97, 96, 95, 94, 93, 91, 90,
+ 89, 88, 86, 85, 84, 82, 81, 79,
+ 78, 76, 75, 73, 71, 70, 68, 67,
+ 65, 63, 62, 60, 58, 56, 55, 53,
+ 51, 49, 47, 45, 44, 42, 40, 38,
+ 36, 34, 32, 30, 28, 26, 24, 22,
+ 20, 18, 16, 14, 12, 10, 8, 6,
+ 4, 2, 0, -1, -3, -5, -7, -9, -11
+};
+
+static u16 i2c_ident[] = {
+ V4L2_IDENT_OV9650,
+ V4L2_IDENT_OV9655,
+ V4L2_IDENT_SOI968,
+ V4L2_IDENT_OV7660,
+ V4L2_IDENT_OV7670,
+ V4L2_IDENT_MT9V011,
+ V4L2_IDENT_MT9V111,
+ V4L2_IDENT_MT9V112,
+ V4L2_IDENT_MT9M001C12ST,
+ V4L2_IDENT_MT9M111,
+ V4L2_IDENT_MT9M112,
+ V4L2_IDENT_HV7131R,
+};
+
+static u16 bridge_init[][2] = {
+ {0x1000, 0x78}, {0x1001, 0x40}, {0x1002, 0x1c},
+ {0x1020, 0x80}, {0x1061, 0x01}, {0x1067, 0x40},
+ {0x1068, 0x30}, {0x1069, 0x20}, {0x106a, 0x10},
+ {0x106b, 0x08}, {0x1188, 0x87}, {0x11a1, 0x00},
+ {0x11a2, 0x00}, {0x11a3, 0x6a}, {0x11a4, 0x50},
+ {0x11ab, 0x00}, {0x11ac, 0x00}, {0x11ad, 0x50},
+ {0x11ae, 0x3c}, {0x118a, 0x04}, {0x0395, 0x04},
+ {0x11b8, 0x3a}, {0x118b, 0x0e}, {0x10f7, 0x05},
+ {0x10f8, 0x14}, {0x10fa, 0xff}, {0x10f9, 0x00},
+ {0x11ba, 0x0a}, {0x11a5, 0x2d}, {0x11a6, 0x2d},
+ {0x11a7, 0x3a}, {0x11a8, 0x05}, {0x11a9, 0x04},
+ {0x11aa, 0x3f}, {0x11af, 0x28}, {0x11b0, 0xd8},
+ {0x11b1, 0x14}, {0x11b2, 0xec}, {0x11b3, 0x32},
+ {0x11b4, 0xdd}, {0x11b5, 0x32}, {0x11b6, 0xdd},
+ {0x10e0, 0x2c}, {0x11bc, 0x40}, {0x11bd, 0x01},
+ {0x11be, 0xf0}, {0x11bf, 0x00}, {0x118c, 0x1f},
+ {0x118d, 0x1f}, {0x118e, 0x1f}, {0x118f, 0x1f},
+ {0x1180, 0x01}, {0x1181, 0x00}, {0x1182, 0x01},
+ {0x1183, 0x00}, {0x1184, 0x50}, {0x1185, 0x80},
+ {0x1007, 0x00}
+};
+
+/* Gain = (bit[3:0] / 16 + 1) * (bit[4] + 1) * (bit[5] + 1) * (bit[6] + 1) */
+static u8 ov_gain[] = {
+ 0x00 /* 1x */, 0x04 /* 1.25x */, 0x08 /* 1.5x */, 0x0c /* 1.75x */,
+ 0x10 /* 2x */, 0x12 /* 2.25x */, 0x14 /* 2.5x */, 0x16 /* 2.75x */,
+ 0x18 /* 3x */, 0x1a /* 3.25x */, 0x1c /* 3.5x */, 0x1e /* 3.75x */,
+ 0x30 /* 4x */, 0x31 /* 4.25x */, 0x32 /* 4.5x */, 0x33 /* 4.75x */,
+ 0x34 /* 5x */, 0x35 /* 5.25x */, 0x36 /* 5.5x */, 0x37 /* 5.75x */,
+ 0x38 /* 6x */, 0x39 /* 6.25x */, 0x3a /* 6.5x */, 0x3b /* 6.75x */,
+ 0x3c /* 7x */, 0x3d /* 7.25x */, 0x3e /* 7.5x */, 0x3f /* 7.75x */,
+ 0x70 /* 8x */
+};
+
+/* Gain = (bit[8] + 1) * (bit[7] + 1) * (bit[6:0] * 0.03125) */
+static u16 micron1_gain[] = {
+ /* 1x 1.25x 1.5x 1.75x */
+ 0x0020, 0x0028, 0x0030, 0x0038,
+ /* 2x 2.25x 2.5x 2.75x */
+ 0x00a0, 0x00a4, 0x00a8, 0x00ac,
+ /* 3x 3.25x 3.5x 3.75x */
+ 0x00b0, 0x00b4, 0x00b8, 0x00bc,
+ /* 4x 4.25x 4.5x 4.75x */
+ 0x00c0, 0x00c4, 0x00c8, 0x00cc,
+ /* 5x 5.25x 5.5x 5.75x */
+ 0x00d0, 0x00d4, 0x00d8, 0x00dc,
+ /* 6x 6.25x 6.5x 6.75x */
+ 0x00e0, 0x00e4, 0x00e8, 0x00ec,
+ /* 7x 7.25x 7.5x 7.75x */
+ 0x00f0, 0x00f4, 0x00f8, 0x00fc,
+ /* 8x */
+ 0x01c0
+};
+
+/* mt9m001 sensor uses a different gain formula then other micron sensors */
+/* Gain = (bit[6] + 1) * (bit[5-0] * 0.125) */
+static u16 micron2_gain[] = {
+ /* 1x 1.25x 1.5x 1.75x */
+ 0x0008, 0x000a, 0x000c, 0x000e,
+ /* 2x 2.25x 2.5x 2.75x */
+ 0x0010, 0x0012, 0x0014, 0x0016,
+ /* 3x 3.25x 3.5x 3.75x */
+ 0x0018, 0x001a, 0x001c, 0x001e,
+ /* 4x 4.25x 4.5x 4.75x */
+ 0x0020, 0x0051, 0x0052, 0x0053,
+ /* 5x 5.25x 5.5x 5.75x */
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ /* 6x 6.25x 6.5x 6.75x */
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ /* 7x 7.25x 7.5x 7.75x */
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 8x */
+ 0x0060
+};
+
+/* Gain = .5 + bit[7:0] / 16 */
+static u8 hv7131r_gain[] = {
+ 0x08 /* 1x */, 0x0c /* 1.25x */, 0x10 /* 1.5x */, 0x14 /* 1.75x */,
+ 0x18 /* 2x */, 0x1c /* 2.25x */, 0x20 /* 2.5x */, 0x24 /* 2.75x */,
+ 0x28 /* 3x */, 0x2c /* 3.25x */, 0x30 /* 3.5x */, 0x34 /* 3.75x */,
+ 0x38 /* 4x */, 0x3c /* 4.25x */, 0x40 /* 4.5x */, 0x44 /* 4.75x */,
+ 0x48 /* 5x */, 0x4c /* 5.25x */, 0x50 /* 5.5x */, 0x54 /* 5.75x */,
+ 0x58 /* 6x */, 0x5c /* 6.25x */, 0x60 /* 6.5x */, 0x64 /* 6.75x */,
+ 0x68 /* 7x */, 0x6c /* 7.25x */, 0x70 /* 7.5x */, 0x74 /* 7.75x */,
+ 0x78 /* 8x */
+};
+
+static struct i2c_reg_u8 soi968_init[] = {
+ {0x0c, 0x00}, {0x0f, 0x1f},
+ {0x11, 0x80}, {0x38, 0x52}, {0x1e, 0x00},
+ {0x33, 0x08}, {0x35, 0x8c}, {0x36, 0x0c},
+ {0x37, 0x04}, {0x45, 0x04}, {0x47, 0xff},
+ {0x3e, 0x00}, {0x3f, 0x00}, {0x3b, 0x20},
+ {0x3a, 0x96}, {0x3d, 0x0a}, {0x14, 0x8e},
+ {0x13, 0x8b}, {0x12, 0x40}, {0x17, 0x13},
+ {0x18, 0x63}, {0x19, 0x01}, {0x1a, 0x79},
+ {0x32, 0x24}, {0x03, 0x00}, {0x11, 0x40},
+ {0x2a, 0x10}, {0x2b, 0xe0}, {0x10, 0x32},
+ {0x00, 0x00}, {0x01, 0x80}, {0x02, 0x80},
+};
+
+static struct i2c_reg_u8 ov7660_init[] = {
+ {0x0e, 0x80}, {0x0d, 0x08}, {0x0f, 0xc3},
+ {0x04, 0xc3}, {0x10, 0x40}, {0x11, 0x40},
+ {0x12, 0x05}, {0x13, 0xba}, {0x14, 0x2a},
+ /* HDG Set hstart and hstop, datasheet default 0x11, 0x61, using
+ 0x10, 0x61 and sd->hstart, vstart = 3, fixes ugly colored borders */
+ {0x17, 0x10}, {0x18, 0x61},
+ {0x37, 0x0f}, {0x38, 0x02}, {0x39, 0x43},
+ {0x3a, 0x00}, {0x69, 0x90}, {0x2d, 0xf6},
+ {0x2e, 0x0b}, {0x01, 0x78}, {0x02, 0x50},
+};
+
+static struct i2c_reg_u8 ov7670_init[] = {
+ {0x11, 0x80}, {0x3a, 0x04}, {0x12, 0x01},
+ {0x32, 0xb6}, {0x03, 0x0a}, {0x0c, 0x00}, {0x3e, 0x00},
+ {0x70, 0x3a}, {0x71, 0x35}, {0x72, 0x11}, {0x73, 0xf0},
+ {0xa2, 0x02}, {0x13, 0xe0}, {0x00, 0x00}, {0x10, 0x00},
+ {0x0d, 0x40}, {0x14, 0x28}, {0xa5, 0x05}, {0xab, 0x07},
+ {0x24, 0x95}, {0x25, 0x33}, {0x26, 0xe3}, {0x9f, 0x75},
+ {0xa0, 0x65}, {0xa1, 0x0b}, {0xa6, 0xd8}, {0xa7, 0xd8},
+ {0xa8, 0xf0}, {0xa9, 0x90}, {0xaa, 0x94}, {0x13, 0xe5},
+ {0x0e, 0x61}, {0x0f, 0x4b}, {0x16, 0x02}, {0x1e, 0x27},
+ {0x21, 0x02}, {0x22, 0x91}, {0x29, 0x07}, {0x33, 0x0b},
+ {0x35, 0x0b}, {0x37, 0x1d}, {0x38, 0x71}, {0x39, 0x2a},
+ {0x3c, 0x78}, {0x4d, 0x40}, {0x4e, 0x20}, {0x69, 0x00},
+ {0x74, 0x19}, {0x8d, 0x4f}, {0x8e, 0x00}, {0x8f, 0x00},
+ {0x90, 0x00}, {0x91, 0x00}, {0x96, 0x00}, {0x9a, 0x80},
+ {0xb0, 0x84}, {0xb1, 0x0c}, {0xb2, 0x0e}, {0xb3, 0x82},
+ {0xb8, 0x0a}, {0x43, 0x0a}, {0x44, 0xf0}, {0x45, 0x20},
+ {0x46, 0x7d}, {0x47, 0x29}, {0x48, 0x4a}, {0x59, 0x8c},
+ {0x5a, 0xa5}, {0x5b, 0xde}, {0x5c, 0x96}, {0x5d, 0x66},
+ {0x5e, 0x10}, {0x6c, 0x0a}, {0x6d, 0x55}, {0x6e, 0x11},
+ {0x6f, 0x9e}, {0x6a, 0x40}, {0x01, 0x40}, {0x02, 0x40},
+ {0x13, 0xe7}, {0x4f, 0x6e}, {0x50, 0x70}, {0x51, 0x02},
+ {0x52, 0x1d}, {0x53, 0x56}, {0x54, 0x73}, {0x55, 0x0a},
+ {0x56, 0x55}, {0x57, 0x80}, {0x58, 0x9e}, {0x41, 0x08},
+ {0x3f, 0x02}, {0x75, 0x03}, {0x76, 0x63}, {0x4c, 0x04},
+ {0x77, 0x06}, {0x3d, 0x02}, {0x4b, 0x09}, {0xc9, 0x30},
+ {0x41, 0x08}, {0x56, 0x48}, {0x34, 0x11}, {0xa4, 0x88},
+ {0x96, 0x00}, {0x97, 0x30}, {0x98, 0x20}, {0x99, 0x30},
+ {0x9a, 0x84}, {0x9b, 0x29}, {0x9c, 0x03}, {0x9d, 0x99},
+ {0x9e, 0x7f}, {0x78, 0x04}, {0x79, 0x01}, {0xc8, 0xf0},
+ {0x79, 0x0f}, {0xc8, 0x00}, {0x79, 0x10}, {0xc8, 0x7e},
+ {0x79, 0x0a}, {0xc8, 0x80}, {0x79, 0x0b}, {0xc8, 0x01},
+ {0x79, 0x0c}, {0xc8, 0x0f}, {0x79, 0x0d}, {0xc8, 0x20},
+ {0x79, 0x09}, {0xc8, 0x80}, {0x79, 0x02}, {0xc8, 0xc0},
+ {0x79, 0x03}, {0xc8, 0x40}, {0x79, 0x05}, {0xc8, 0x30},
+ {0x79, 0x26}, {0x62, 0x20}, {0x63, 0x00}, {0x64, 0x06},
+ {0x65, 0x00}, {0x66, 0x05}, {0x94, 0x05}, {0x95, 0x0a},
+ {0x17, 0x13}, {0x18, 0x01}, {0x19, 0x02}, {0x1a, 0x7a},
+ {0x46, 0x59}, {0x47, 0x30}, {0x58, 0x9a}, {0x59, 0x84},
+ {0x5a, 0x91}, {0x5b, 0x57}, {0x5c, 0x75}, {0x5d, 0x6d},
+ {0x5e, 0x13}, {0x64, 0x07}, {0x94, 0x07}, {0x95, 0x0d},
+ {0xa6, 0xdf}, {0xa7, 0xdf}, {0x48, 0x4d}, {0x51, 0x00},
+ {0x6b, 0x0a}, {0x11, 0x80}, {0x2a, 0x00}, {0x2b, 0x00},
+ {0x92, 0x00}, {0x93, 0x00}, {0x55, 0x0a}, {0x56, 0x60},
+ {0x4f, 0x6e}, {0x50, 0x70}, {0x51, 0x00}, {0x52, 0x1d},
+ {0x53, 0x56}, {0x54, 0x73}, {0x58, 0x9a}, {0x4f, 0x6e},
+ {0x50, 0x70}, {0x51, 0x00}, {0x52, 0x1d}, {0x53, 0x56},
+ {0x54, 0x73}, {0x58, 0x9a}, {0x3f, 0x01}, {0x7b, 0x03},
+ {0x7c, 0x09}, {0x7d, 0x16}, {0x7e, 0x38}, {0x7f, 0x47},
+ {0x80, 0x53}, {0x81, 0x5e}, {0x82, 0x6a}, {0x83, 0x74},
+ {0x84, 0x80}, {0x85, 0x8c}, {0x86, 0x9b}, {0x87, 0xb2},
+ {0x88, 0xcc}, {0x89, 0xe5}, {0x7a, 0x24}, {0x3b, 0x00},
+ {0x9f, 0x76}, {0xa0, 0x65}, {0x13, 0xe2}, {0x6b, 0x0a},
+ {0x11, 0x80}, {0x2a, 0x00}, {0x2b, 0x00}, {0x92, 0x00},
+ {0x93, 0x00},
+};
+
+static struct i2c_reg_u8 ov9650_init[] = {
+ {0x00, 0x00}, {0x01, 0x78},
+ {0x02, 0x78}, {0x03, 0x36}, {0x04, 0x03},
+ {0x05, 0x00}, {0x06, 0x00}, {0x08, 0x00},
+ {0x09, 0x01}, {0x0c, 0x00}, {0x0d, 0x00},
+ {0x0e, 0xa0}, {0x0f, 0x52}, {0x10, 0x7c},
+ {0x11, 0x80}, {0x12, 0x45}, {0x13, 0xc2},
+ {0x14, 0x2e}, {0x15, 0x00}, {0x16, 0x07},
+ {0x17, 0x24}, {0x18, 0xc5}, {0x19, 0x00},
+ {0x1a, 0x3c}, {0x1b, 0x00}, {0x1e, 0x04},
+ {0x1f, 0x00}, {0x24, 0x78}, {0x25, 0x68},
+ {0x26, 0xd4}, {0x27, 0x80}, {0x28, 0x80},
+ {0x29, 0x30}, {0x2a, 0x00}, {0x2b, 0x00},
+ {0x2c, 0x80}, {0x2d, 0x00}, {0x2e, 0x00},
+ {0x2f, 0x00}, {0x30, 0x08}, {0x31, 0x30},
+ {0x32, 0x84}, {0x33, 0xe2}, {0x34, 0xbf},
+ {0x35, 0x81}, {0x36, 0xf9}, {0x37, 0x00},
+ {0x38, 0x93}, {0x39, 0x50}, {0x3a, 0x01},
+ {0x3b, 0x01}, {0x3c, 0x73}, {0x3d, 0x19},
+ {0x3e, 0x0b}, {0x3f, 0x80}, {0x40, 0xc1},
+ {0x41, 0x00}, {0x42, 0x08}, {0x67, 0x80},
+ {0x68, 0x80}, {0x69, 0x40}, {0x6a, 0x00},
+ {0x6b, 0x0a}, {0x8b, 0x06}, {0x8c, 0x20},
+ {0x8d, 0x00}, {0x8e, 0x00}, {0x8f, 0xdf},
+ {0x92, 0x00}, {0x93, 0x00}, {0x94, 0x88},
+ {0x95, 0x88}, {0x96, 0x04}, {0xa1, 0x00},
+ {0xa5, 0x80}, {0xa8, 0x80}, {0xa9, 0xb8},
+ {0xaa, 0x92}, {0xab, 0x0a},
+};
+
+static struct i2c_reg_u8 ov9655_init[] = {
+ {0x0e, 0x61}, {0x11, 0x80}, {0x13, 0xba},
+ {0x14, 0x2e}, {0x16, 0x24}, {0x1e, 0x04}, {0x27, 0x08},
+ {0x28, 0x08}, {0x29, 0x15}, {0x2c, 0x08}, {0x34, 0x3d},
+ {0x35, 0x00}, {0x38, 0x12}, {0x0f, 0x42}, {0x39, 0x57},
+ {0x3a, 0x00}, {0x3b, 0xcc}, {0x3c, 0x0c}, {0x3d, 0x19},
+ {0x3e, 0x0c}, {0x3f, 0x01}, {0x41, 0x40}, {0x42, 0x80},
+ {0x45, 0x46}, {0x46, 0x62}, {0x47, 0x2a}, {0x48, 0x3c},
+ {0x4a, 0xf0}, {0x4b, 0xdc}, {0x4c, 0xdc}, {0x4d, 0xdc},
+ {0x4e, 0xdc}, {0x6c, 0x04}, {0x6f, 0x9e}, {0x70, 0x05},
+ {0x71, 0x78}, {0x77, 0x02}, {0x8a, 0x23}, {0x90, 0x7e},
+ {0x91, 0x7c}, {0x9f, 0x6e}, {0xa0, 0x6e}, {0xa5, 0x68},
+ {0xa6, 0x60}, {0xa8, 0xc1}, {0xa9, 0xfa}, {0xaa, 0x92},
+ {0xab, 0x04}, {0xac, 0x80}, {0xad, 0x80}, {0xae, 0x80},
+ {0xaf, 0x80}, {0xb2, 0xf2}, {0xb3, 0x20}, {0xb5, 0x00},
+ {0xb6, 0xaf}, {0xbb, 0xae}, {0xbc, 0x44}, {0xbd, 0x44},
+ {0xbe, 0x3b}, {0xbf, 0x3a}, {0xc1, 0xc8}, {0xc2, 0x01},
+ {0xc4, 0x00}, {0xc6, 0x85}, {0xc7, 0x81}, {0xc9, 0xe0},
+ {0xca, 0xe8}, {0xcc, 0xd8}, {0xcd, 0x93}, {0x2d, 0x00},
+ {0x2e, 0x00}, {0x01, 0x80}, {0x02, 0x80}, {0x12, 0x61},
+ {0x36, 0xfa}, {0x8c, 0x8d}, {0xc0, 0xaa}, {0x69, 0x0a},
+ {0x03, 0x09}, {0x17, 0x16}, {0x18, 0x6e}, {0x19, 0x01},
+ {0x1a, 0x3e}, {0x32, 0x09}, {0x2a, 0x10}, {0x2b, 0x0a},
+ {0x92, 0x00}, {0x93, 0x00}, {0xa1, 0x00}, {0x10, 0x7c},
+ {0x04, 0x03}, {0x00, 0x13},
+};
+
+static struct i2c_reg_u16 mt9v112_init[] = {
+ {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0020},
+ {0x34, 0xc019}, {0x0a, 0x0011}, {0x0b, 0x000b},
+ {0x20, 0x0703}, {0x35, 0x2022}, {0xf0, 0x0001},
+ {0x05, 0x0000}, {0x06, 0x340c}, {0x3b, 0x042a},
+ {0x3c, 0x0400}, {0xf0, 0x0002}, {0x2e, 0x0c58},
+ {0x5b, 0x0001}, {0xc8, 0x9f0b}, {0xf0, 0x0001},
+ {0x9b, 0x5300}, {0xf0, 0x0000}, {0x2b, 0x0020},
+ {0x2c, 0x002a}, {0x2d, 0x0032}, {0x2e, 0x0020},
+ {0x09, 0x01dc}, {0x01, 0x000c}, {0x02, 0x0020},
+ {0x03, 0x01e0}, {0x04, 0x0280}, {0x06, 0x000c},
+ {0x05, 0x0098}, {0x20, 0x0703}, {0x09, 0x01f2},
+ {0x2b, 0x00a0}, {0x2c, 0x00a0}, {0x2d, 0x00a0},
+ {0x2e, 0x00a0}, {0x01, 0x000c}, {0x02, 0x0020},
+ {0x03, 0x01e0}, {0x04, 0x0280}, {0x06, 0x000c},
+ {0x05, 0x0098}, {0x09, 0x01c1}, {0x2b, 0x00ae},
+ {0x2c, 0x00ae}, {0x2d, 0x00ae}, {0x2e, 0x00ae},
+};
+
+static struct i2c_reg_u16 mt9v111_init[] = {
+ {0x01, 0x0004}, {0x0d, 0x0001}, {0x0d, 0x0000},
+ {0x01, 0x0001}, {0x05, 0x0004}, {0x2d, 0xe0a0},
+ {0x2e, 0x0c64}, {0x2f, 0x0064}, {0x06, 0x600e},
+ {0x08, 0x0480}, {0x01, 0x0004}, {0x02, 0x0016},
+ {0x03, 0x01e7}, {0x04, 0x0287}, {0x05, 0x0004},
+ {0x06, 0x002d}, {0x07, 0x3002}, {0x08, 0x0008},
+ {0x0e, 0x0008}, {0x20, 0x0000}
+};
+
+static struct i2c_reg_u16 mt9v011_init[] = {
+ {0x07, 0x0002}, {0x0d, 0x0001}, {0x0d, 0x0000},
+ {0x01, 0x0008}, {0x02, 0x0016}, {0x03, 0x01e1},
+ {0x04, 0x0281}, {0x05, 0x0083}, {0x06, 0x0006},
+ {0x0d, 0x0002}, {0x0a, 0x0000}, {0x0b, 0x0000},
+ {0x0c, 0x0000}, {0x0d, 0x0000}, {0x0e, 0x0000},
+ {0x0f, 0x0000}, {0x10, 0x0000}, {0x11, 0x0000},
+ {0x12, 0x0000}, {0x13, 0x0000}, {0x14, 0x0000},
+ {0x15, 0x0000}, {0x16, 0x0000}, {0x17, 0x0000},
+ {0x18, 0x0000}, {0x19, 0x0000}, {0x1a, 0x0000},
+ {0x1b, 0x0000}, {0x1c, 0x0000}, {0x1d, 0x0000},
+ {0x32, 0x0000}, {0x20, 0x1101}, {0x21, 0x0000},
+ {0x22, 0x0000}, {0x23, 0x0000}, {0x24, 0x0000},
+ {0x25, 0x0000}, {0x26, 0x0000}, {0x27, 0x0024},
+ {0x2f, 0xf7b0}, {0x30, 0x0005}, {0x31, 0x0000},
+ {0x32, 0x0000}, {0x33, 0x0000}, {0x34, 0x0100},
+ {0x3d, 0x068f}, {0x40, 0x01e0}, {0x41, 0x00d1},
+ {0x44, 0x0082}, {0x5a, 0x0000}, {0x5b, 0x0000},
+ {0x5c, 0x0000}, {0x5d, 0x0000}, {0x5e, 0x0000},
+ {0x5f, 0xa31d}, {0x62, 0x0611}, {0x0a, 0x0000},
+ {0x06, 0x0029}, {0x05, 0x0009}, {0x20, 0x1101},
+ {0x20, 0x1101}, {0x09, 0x0064}, {0x07, 0x0003},
+ {0x2b, 0x0033}, {0x2c, 0x00a0}, {0x2d, 0x00a0},
+ {0x2e, 0x0033}, {0x07, 0x0002}, {0x06, 0x0000},
+ {0x06, 0x0029}, {0x05, 0x0009},
+};
+
+static struct i2c_reg_u16 mt9m001_init[] = {
+ {0x0d, 0x0001},
+ {0x0d, 0x0000},
+ {0x04, 0x0500}, /* hres = 1280 */
+ {0x03, 0x0400}, /* vres = 1024 */
+ {0x20, 0x1100},
+ {0x06, 0x0010},
+ {0x2b, 0x0024},
+ {0x2e, 0x0024},
+ {0x35, 0x0024},
+ {0x2d, 0x0020},
+ {0x2c, 0x0020},
+ {0x09, 0x0ad4},
+ {0x35, 0x0057},
+};
+
+static struct i2c_reg_u16 mt9m111_init[] = {
+ {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0008},
+ {0xf0, 0x0001}, {0x3a, 0x4300}, {0x9b, 0x4300},
+ {0x06, 0x708e}, {0xf0, 0x0002}, {0x2e, 0x0a1e},
+ {0xf0, 0x0000},
+};
+
+static struct i2c_reg_u16 mt9m112_init[] = {
+ {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0008},
+ {0xf0, 0x0001}, {0x3a, 0x4300}, {0x9b, 0x4300},
+ {0x06, 0x708e}, {0xf0, 0x0002}, {0x2e, 0x0a1e},
+ {0xf0, 0x0000},
+};
+
+static struct i2c_reg_u8 hv7131r_init[] = {
+ {0x02, 0x08}, {0x02, 0x00}, {0x01, 0x08},
+ {0x02, 0x00}, {0x20, 0x00}, {0x21, 0xd0},
+ {0x22, 0x00}, {0x23, 0x09}, {0x01, 0x08},
+ {0x01, 0x08}, {0x01, 0x08}, {0x25, 0x07},
+ {0x26, 0xc3}, {0x27, 0x50}, {0x30, 0x62},
+ {0x31, 0x10}, {0x32, 0x06}, {0x33, 0x10},
+ {0x20, 0x00}, {0x21, 0xd0}, {0x22, 0x00},
+ {0x23, 0x09}, {0x01, 0x08},
+};
+
+static void reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int result;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ 0x00,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ reg,
+ 0x00,
+ gspca_dev->usb_buf,
+ length,
+ 500);
+ if (unlikely(result < 0 || result != length)) {
+ pr_err("Read register %02x failed %d\n", reg, result);
+ gspca_dev->usb_err = result;
+ }
+}
+
+static void reg_w(struct gspca_dev *gspca_dev, u16 reg,
+ const u8 *buffer, int length)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int result;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ memcpy(gspca_dev->usb_buf, buffer, length);
+ result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x08,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ reg,
+ 0x00,
+ gspca_dev->usb_buf,
+ length,
+ 500);
+ if (unlikely(result < 0 || result != length)) {
+ pr_err("Write register %02x failed %d\n", reg, result);
+ gspca_dev->usb_err = result;
+ }
+}
+
+static void reg_w1(struct gspca_dev *gspca_dev, u16 reg, const u8 value)
+{
+ reg_w(gspca_dev, reg, &value, 1);
+}
+
+static void i2c_w(struct gspca_dev *gspca_dev, const u8 *buffer)
+{
+ int i;
+
+ reg_w(gspca_dev, 0x10c0, buffer, 8);
+ for (i = 0; i < 5; i++) {
+ reg_r(gspca_dev, 0x10c0, 1);
+ if (gspca_dev->usb_err < 0)
+ return;
+ if (gspca_dev->usb_buf[0] & 0x04) {
+ if (gspca_dev->usb_buf[0] & 0x08) {
+ pr_err("i2c_w error\n");
+ gspca_dev->usb_err = -EIO;
+ }
+ return;
+ }
+ msleep(10);
+ }
+ pr_err("i2c_w reg %02x no response\n", buffer[2]);
+/* gspca_dev->usb_err = -EIO; fixme: may occur */
+}
+
+static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 row[8];
+
+ /*
+ * from the point of view of the bridge, the length
+ * includes the address
+ */
+ row[0] = 0x81 | (2 << 4);
+ row[1] = sd->i2c_addr;
+ row[2] = reg;
+ row[3] = val;
+ row[4] = 0x00;
+ row[5] = 0x00;
+ row[6] = 0x00;
+ row[7] = 0x10;
+
+ i2c_w(gspca_dev, row);
+}
+
+static void i2c_w1_buf(struct gspca_dev *gspca_dev,
+ struct i2c_reg_u8 *buf, int sz)
+{
+ while (--sz >= 0) {
+ i2c_w1(gspca_dev, buf->reg, buf->val);
+ buf++;
+ }
+}
+
+static void i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 row[8];
+
+ /*
+ * from the point of view of the bridge, the length
+ * includes the address
+ */
+ row[0] = 0x81 | (3 << 4);
+ row[1] = sd->i2c_addr;
+ row[2] = reg;
+ row[3] = val >> 8;
+ row[4] = val;
+ row[5] = 0x00;
+ row[6] = 0x00;
+ row[7] = 0x10;
+
+ i2c_w(gspca_dev, row);
+}
+
+static void i2c_w2_buf(struct gspca_dev *gspca_dev,
+ struct i2c_reg_u16 *buf, int sz)
+{
+ while (--sz >= 0) {
+ i2c_w2(gspca_dev, buf->reg, buf->val);
+ buf++;
+ }
+}
+
+static void i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 row[8];
+
+ row[0] = 0x81 | (1 << 4);
+ row[1] = sd->i2c_addr;
+ row[2] = reg;
+ row[3] = 0;
+ row[4] = 0;
+ row[5] = 0;
+ row[6] = 0;
+ row[7] = 0x10;
+ i2c_w(gspca_dev, row);
+ row[0] = 0x81 | (1 << 4) | 0x02;
+ row[2] = 0;
+ i2c_w(gspca_dev, row);
+ reg_r(gspca_dev, 0x10c2, 5);
+ *val = gspca_dev->usb_buf[4];
+}
+
+static void i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 row[8];
+
+ row[0] = 0x81 | (1 << 4);
+ row[1] = sd->i2c_addr;
+ row[2] = reg;
+ row[3] = 0;
+ row[4] = 0;
+ row[5] = 0;
+ row[6] = 0;
+ row[7] = 0x10;
+ i2c_w(gspca_dev, row);
+ row[0] = 0x81 | (2 << 4) | 0x02;
+ row[2] = 0;
+ i2c_w(gspca_dev, row);
+ reg_r(gspca_dev, 0x10c2, 5);
+ *val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
+}
+
+static void ov9650_init_sensor(struct gspca_dev *gspca_dev)
+{
+ u16 id;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ i2c_r2(gspca_dev, 0x1c, &id);
+ if (gspca_dev->usb_err < 0)
+ return;
+
+ if (id != 0x7fa2) {
+ pr_err("sensor id for ov9650 doesn't match (0x%04x)\n", id);
+ gspca_dev->usb_err = -ENODEV;
+ return;
+ }
+
+ i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */
+ msleep(200);
+ i2c_w1_buf(gspca_dev, ov9650_init, ARRAY_SIZE(ov9650_init));
+ if (gspca_dev->usb_err < 0)
+ pr_err("OV9650 sensor initialization failed\n");
+ sd->hstart = 1;
+ sd->vstart = 7;
+}
+
+static void ov9655_init_sensor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */
+ msleep(200);
+ i2c_w1_buf(gspca_dev, ov9655_init, ARRAY_SIZE(ov9655_init));
+ if (gspca_dev->usb_err < 0)
+ pr_err("OV9655 sensor initialization failed\n");
+
+ /* disable hflip and vflip */
+ gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP);
+ sd->hstart = 1;
+ sd->vstart = 2;
+}
+
+static void soi968_init_sensor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */
+ msleep(200);
+ i2c_w1_buf(gspca_dev, soi968_init, ARRAY_SIZE(soi968_init));
+ if (gspca_dev->usb_err < 0)
+ pr_err("SOI968 sensor initialization failed\n");
+
+ /* disable hflip and vflip */
+ gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP)
+ | (1 << EXPOSURE);
+ sd->hstart = 60;
+ sd->vstart = 11;
+}
+
+static void ov7660_init_sensor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */
+ msleep(200);
+ i2c_w1_buf(gspca_dev, ov7660_init, ARRAY_SIZE(ov7660_init));
+ if (gspca_dev->usb_err < 0)
+ pr_err("OV7660 sensor initialization failed\n");
+ sd->hstart = 3;
+ sd->vstart = 3;
+}
+
+static void ov7670_init_sensor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */
+ msleep(200);
+ i2c_w1_buf(gspca_dev, ov7670_init, ARRAY_SIZE(ov7670_init));
+ if (gspca_dev->usb_err < 0)
+ pr_err("OV7670 sensor initialization failed\n");
+
+ /* disable hflip and vflip */
+ gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP);
+ sd->hstart = 0;
+ sd->vstart = 1;
+}
+
+static void mt9v_init_sensor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 value;
+
+ sd->i2c_addr = 0x5d;
+ i2c_r2(gspca_dev, 0xff, &value);
+ if (gspca_dev->usb_err >= 0
+ && value == 0x8243) {
+ i2c_w2_buf(gspca_dev, mt9v011_init, ARRAY_SIZE(mt9v011_init));
+ if (gspca_dev->usb_err < 0) {
+ pr_err("MT9V011 sensor initialization failed\n");
+ return;
+ }
+ sd->hstart = 2;
+ sd->vstart = 2;
+ sd->sensor = SENSOR_MT9V011;
+ pr_info("MT9V011 sensor detected\n");
+ return;
+ }
+
+ gspca_dev->usb_err = 0;
+ sd->i2c_addr = 0x5c;
+ i2c_w2(gspca_dev, 0x01, 0x0004);
+ i2c_r2(gspca_dev, 0xff, &value);
+ if (gspca_dev->usb_err >= 0
+ && value == 0x823a) {
+ i2c_w2_buf(gspca_dev, mt9v111_init, ARRAY_SIZE(mt9v111_init));
+ if (gspca_dev->usb_err < 0) {
+ pr_err("MT9V111 sensor initialization failed\n");
+ return;
+ }
+ gspca_dev->ctrl_dis = (1 << EXPOSURE)
+ | (1 << AUTOGAIN)
+ | (1 << GAIN);
+ sd->hstart = 2;
+ sd->vstart = 2;
+ sd->sensor = SENSOR_MT9V111;
+ pr_info("MT9V111 sensor detected\n");
+ return;
+ }
+
+ gspca_dev->usb_err = 0;
+ sd->i2c_addr = 0x5d;
+ i2c_w2(gspca_dev, 0xf0, 0x0000);
+ if (gspca_dev->usb_err < 0) {
+ gspca_dev->usb_err = 0;
+ sd->i2c_addr = 0x48;
+ i2c_w2(gspca_dev, 0xf0, 0x0000);
+ }
+ i2c_r2(gspca_dev, 0x00, &value);
+ if (gspca_dev->usb_err >= 0
+ && value == 0x1229) {
+ i2c_w2_buf(gspca_dev, mt9v112_init, ARRAY_SIZE(mt9v112_init));
+ if (gspca_dev->usb_err < 0) {
+ pr_err("MT9V112 sensor initialization failed\n");
+ return;
+ }
+ sd->hstart = 6;
+ sd->vstart = 2;
+ sd->sensor = SENSOR_MT9V112;
+ pr_info("MT9V112 sensor detected\n");
+ return;
+ }
+
+ gspca_dev->usb_err = -ENODEV;
+}
+
+static void mt9m112_init_sensor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ i2c_w2_buf(gspca_dev, mt9m112_init, ARRAY_SIZE(mt9m112_init));
+ if (gspca_dev->usb_err < 0)
+ pr_err("MT9M112 sensor initialization failed\n");
+
+ gspca_dev->ctrl_dis = (1 << EXPOSURE) | (1 << AUTOGAIN)
+ | (1 << GAIN);
+ sd->hstart = 0;
+ sd->vstart = 2;
+}
+
+static void mt9m111_init_sensor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ i2c_w2_buf(gspca_dev, mt9m111_init, ARRAY_SIZE(mt9m111_init));
+ if (gspca_dev->usb_err < 0)
+ pr_err("MT9M111 sensor initialization failed\n");
+
+ gspca_dev->ctrl_dis = (1 << EXPOSURE) | (1 << AUTOGAIN)
+ | (1 << GAIN);
+ sd->hstart = 0;
+ sd->vstart = 2;
+}
+
+static void mt9m001_init_sensor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 id;
+
+ i2c_r2(gspca_dev, 0x00, &id);
+ if (gspca_dev->usb_err < 0)
+ return;
+
+ /* must be 0x8411 or 0x8421 for colour sensor and 8431 for bw */
+ switch (id) {
+ case 0x8411:
+ case 0x8421:
+ pr_info("MT9M001 color sensor detected\n");
+ break;
+ case 0x8431:
+ pr_info("MT9M001 mono sensor detected\n");
+ break;
+ default:
+ pr_err("No MT9M001 chip detected, ID = %x\n\n", id);
+ gspca_dev->usb_err = -ENODEV;
+ return;
+ }
+
+ i2c_w2_buf(gspca_dev, mt9m001_init, ARRAY_SIZE(mt9m001_init));
+ if (gspca_dev->usb_err < 0)
+ pr_err("MT9M001 sensor initialization failed\n");
+
+ /* disable hflip and vflip */
+ gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP);
+ sd->hstart = 1;
+ sd->vstart = 1;
+}
+
+static void hv7131r_init_sensor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ i2c_w1_buf(gspca_dev, hv7131r_init, ARRAY_SIZE(hv7131r_init));
+ if (gspca_dev->usb_err < 0)
+ pr_err("HV7131R Sensor initialization failed\n");
+
+ sd->hstart = 0;
+ sd->vstart = 1;
+}
+
+static void set_cmatrix(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int satur;
+ s32 hue_coord, hue_index = 180 + sd->ctrls[HUE].val;
+ u8 cmatrix[21];
+
+ memset(cmatrix, 0, sizeof cmatrix);
+ cmatrix[2] = (sd->ctrls[CONTRAST].val * 0x25 / 0x100) + 0x26;
+ cmatrix[0] = 0x13 + (cmatrix[2] - 0x26) * 0x13 / 0x25;
+ cmatrix[4] = 0x07 + (cmatrix[2] - 0x26) * 0x07 / 0x25;
+ cmatrix[18] = sd->ctrls[BRIGHTNESS].val - 0x80;
+
+ satur = sd->ctrls[SATURATION].val;
+ hue_coord = (hsv_red_x[hue_index] * satur) >> 8;
+ cmatrix[6] = hue_coord;
+ cmatrix[7] = (hue_coord >> 8) & 0x0f;
+
+ hue_coord = (hsv_red_y[hue_index] * satur) >> 8;
+ cmatrix[8] = hue_coord;
+ cmatrix[9] = (hue_coord >> 8) & 0x0f;
+
+ hue_coord = (hsv_green_x[hue_index] * satur) >> 8;
+ cmatrix[10] = hue_coord;
+ cmatrix[11] = (hue_coord >> 8) & 0x0f;
+
+ hue_coord = (hsv_green_y[hue_index] * satur) >> 8;
+ cmatrix[12] = hue_coord;
+ cmatrix[13] = (hue_coord >> 8) & 0x0f;
+
+ hue_coord = (hsv_blue_x[hue_index] * satur) >> 8;
+ cmatrix[14] = hue_coord;
+ cmatrix[15] = (hue_coord >> 8) & 0x0f;
+
+ hue_coord = (hsv_blue_y[hue_index] * satur) >> 8;
+ cmatrix[16] = hue_coord;
+ cmatrix[17] = (hue_coord >> 8) & 0x0f;
+
+ reg_w(gspca_dev, 0x10e1, cmatrix, 21);
+}
+
+static void set_gamma(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 gamma[17];
+ u8 gval = sd->ctrls[GAMMA].val * 0xb8 / 0x100;
+
+ gamma[0] = 0x0a;
+ gamma[1] = 0x13 + (gval * (0xcb - 0x13) / 0xb8);
+ gamma[2] = 0x25 + (gval * (0xee - 0x25) / 0xb8);
+ gamma[3] = 0x37 + (gval * (0xfa - 0x37) / 0xb8);
+ gamma[4] = 0x45 + (gval * (0xfc - 0x45) / 0xb8);
+ gamma[5] = 0x55 + (gval * (0xfb - 0x55) / 0xb8);
+ gamma[6] = 0x65 + (gval * (0xfc - 0x65) / 0xb8);
+ gamma[7] = 0x74 + (gval * (0xfd - 0x74) / 0xb8);
+ gamma[8] = 0x83 + (gval * (0xfe - 0x83) / 0xb8);
+ gamma[9] = 0x92 + (gval * (0xfc - 0x92) / 0xb8);
+ gamma[10] = 0xa1 + (gval * (0xfc - 0xa1) / 0xb8);
+ gamma[11] = 0xb0 + (gval * (0xfc - 0xb0) / 0xb8);
+ gamma[12] = 0xbf + (gval * (0xfb - 0xbf) / 0xb8);
+ gamma[13] = 0xce + (gval * (0xfb - 0xce) / 0xb8);
+ gamma[14] = 0xdf + (gval * (0xfd - 0xdf) / 0xb8);
+ gamma[15] = 0xea + (gval * (0xf9 - 0xea) / 0xb8);
+ gamma[16] = 0xf5;
+
+ reg_w(gspca_dev, 0x1190, gamma, 17);
+}
+
+static void set_redblue(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w1(gspca_dev, 0x118c, sd->ctrls[RED].val);
+ reg_w1(gspca_dev, 0x118f, sd->ctrls[BLUE].val);
+}
+
+static void set_hvflip(struct gspca_dev *gspca_dev)
+{
+ u8 value, tslb, hflip, vflip;
+ u16 value2;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if ((sd->flags & FLIP_DETECT) && dmi_check_system(flip_dmi_table)) {
+ hflip = !sd->ctrls[HFLIP].val;
+ vflip = !sd->ctrls[VFLIP].val;
+ } else {
+ hflip = sd->ctrls[HFLIP].val;
+ vflip = sd->ctrls[VFLIP].val;
+ }
+
+ switch (sd->sensor) {
+ case SENSOR_OV7660:
+ value = 0x01;
+ if (hflip)
+ value |= 0x20;
+ if (vflip) {
+ value |= 0x10;
+ sd->vstart = 2;
+ } else {
+ sd->vstart = 3;
+ }
+ reg_w1(gspca_dev, 0x1182, sd->vstart);
+ i2c_w1(gspca_dev, 0x1e, value);
+ break;
+ case SENSOR_OV9650:
+ i2c_r1(gspca_dev, 0x1e, &value);
+ value &= ~0x30;
+ tslb = 0x01;
+ if (hflip)
+ value |= 0x20;
+ if (vflip) {
+ value |= 0x10;
+ tslb = 0x49;
+ }
+ i2c_w1(gspca_dev, 0x1e, value);
+ i2c_w1(gspca_dev, 0x3a, tslb);
+ break;
+ case SENSOR_MT9V111:
+ case SENSOR_MT9V011:
+ i2c_r2(gspca_dev, 0x20, &value2);
+ value2 &= ~0xc0a0;
+ if (hflip)
+ value2 |= 0x8080;
+ if (vflip)
+ value2 |= 0x4020;
+ i2c_w2(gspca_dev, 0x20, value2);
+ break;
+ case SENSOR_MT9M112:
+ case SENSOR_MT9M111:
+ case SENSOR_MT9V112:
+ i2c_r2(gspca_dev, 0x20, &value2);
+ value2 &= ~0x0003;
+ if (hflip)
+ value2 |= 0x0002;
+ if (vflip)
+ value2 |= 0x0001;
+ i2c_w2(gspca_dev, 0x20, value2);
+ break;
+ case SENSOR_HV7131R:
+ i2c_r1(gspca_dev, 0x01, &value);
+ value &= ~0x03;
+ if (vflip)
+ value |= 0x01;
+ if (hflip)
+ value |= 0x02;
+ i2c_w1(gspca_dev, 0x01, value);
+ break;
+ }
+}
+
+static void set_exposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 exp[8] = {0x81, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e};
+ int expo;
+
+ expo = sd->ctrls[EXPOSURE].val;
+ switch (sd->sensor) {
+ case SENSOR_OV7660:
+ case SENSOR_OV7670:
+ case SENSOR_OV9655:
+ case SENSOR_OV9650:
+ exp[0] |= (3 << 4);
+ exp[2] = 0x2d;
+ exp[3] = expo;
+ exp[4] = expo >> 8;
+ break;
+ case SENSOR_MT9M001:
+ case SENSOR_MT9V112:
+ case SENSOR_MT9V011:
+ exp[0] |= (3 << 4);
+ exp[2] = 0x09;
+ exp[3] = expo >> 8;
+ exp[4] = expo;
+ break;
+ case SENSOR_HV7131R:
+ exp[0] |= (4 << 4);
+ exp[2] = 0x25;
+ exp[3] = expo >> 5;
+ exp[4] = expo << 3;
+ exp[5] = 0;
+ break;
+ default:
+ return;
+ }
+ i2c_w(gspca_dev, exp);
+}
+
+static void set_gain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 gain[8] = {0x81, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d};
+ int g;
+
+ g = sd->ctrls[GAIN].val;
+ switch (sd->sensor) {
+ case SENSOR_OV7660:
+ case SENSOR_OV7670:
+ case SENSOR_SOI968:
+ case SENSOR_OV9655:
+ case SENSOR_OV9650:
+ gain[0] |= (2 << 4);
+ gain[3] = ov_gain[g];
+ break;
+ case SENSOR_MT9V011:
+ gain[0] |= (3 << 4);
+ gain[2] = 0x35;
+ gain[3] = micron1_gain[g] >> 8;
+ gain[4] = micron1_gain[g];
+ break;
+ case SENSOR_MT9V112:
+ gain[0] |= (3 << 4);
+ gain[2] = 0x2f;
+ gain[3] = micron1_gain[g] >> 8;
+ gain[4] = micron1_gain[g];
+ break;
+ case SENSOR_MT9M001:
+ gain[0] |= (3 << 4);
+ gain[2] = 0x2f;
+ gain[3] = micron2_gain[g] >> 8;
+ gain[4] = micron2_gain[g];
+ break;
+ case SENSOR_HV7131R:
+ gain[0] |= (2 << 4);
+ gain[2] = 0x30;
+ gain[3] = hv7131r_gain[g];
+ break;
+ default:
+ return;
+ }
+ i2c_w(gspca_dev, gain);
+}
+
+static void set_quality(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val);
+ reg_w1(gspca_dev, 0x1061, 0x01); /* stop transfer */
+ reg_w1(gspca_dev, 0x10e0, sd->fmt | 0x20); /* write QTAB */
+ reg_w(gspca_dev, 0x1100, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64);
+ reg_w(gspca_dev, 0x1140, &sd->jpeg_hdr[JPEG_QT1_OFFSET], 64);
+ reg_w1(gspca_dev, 0x1061, 0x03); /* restart transfer */
+ reg_w1(gspca_dev, 0x10e0, sd->fmt);
+ sd->fmt ^= 0x0c; /* invert QTAB use + write */
+ reg_w1(gspca_dev, 0x10e0, sd->fmt);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int sd_dbg_g_register(struct gspca_dev *gspca_dev,
+ struct v4l2_dbg_register *reg)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (reg->match.type) {
+ case V4L2_CHIP_MATCH_HOST:
+ if (reg->match.addr != 0)
+ return -EINVAL;
+ if (reg->reg < 0x1000 || reg->reg > 0x11ff)
+ return -EINVAL;
+ reg_r(gspca_dev, reg->reg, 1);
+ reg->val = gspca_dev->usb_buf[0];
+ return gspca_dev->usb_err;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ if (reg->match.addr != sd->i2c_addr)
+ return -EINVAL;
+ if (sd->sensor >= SENSOR_MT9V011 &&
+ sd->sensor <= SENSOR_MT9M112) {
+ i2c_r2(gspca_dev, reg->reg, (u16 *) &reg->val);
+ } else {
+ i2c_r1(gspca_dev, reg->reg, (u8 *) &reg->val);
+ }
+ return gspca_dev->usb_err;
+ }
+ return -EINVAL;
+}
+
+static int sd_dbg_s_register(struct gspca_dev *gspca_dev,
+ struct v4l2_dbg_register *reg)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (reg->match.type) {
+ case V4L2_CHIP_MATCH_HOST:
+ if (reg->match.addr != 0)
+ return -EINVAL;
+ if (reg->reg < 0x1000 || reg->reg > 0x11ff)
+ return -EINVAL;
+ reg_w1(gspca_dev, reg->reg, reg->val);
+ return gspca_dev->usb_err;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ if (reg->match.addr != sd->i2c_addr)
+ return -EINVAL;
+ if (sd->sensor >= SENSOR_MT9V011 &&
+ sd->sensor <= SENSOR_MT9M112) {
+ i2c_w2(gspca_dev, reg->reg, reg->val);
+ } else {
+ i2c_w1(gspca_dev, reg->reg, reg->val);
+ }
+ return gspca_dev->usb_err;
+ }
+ return -EINVAL;
+}
+#endif
+
+static int sd_chip_ident(struct gspca_dev *gspca_dev,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (chip->match.type) {
+ case V4L2_CHIP_MATCH_HOST:
+ if (chip->match.addr != 0)
+ return -EINVAL;
+ chip->revision = 0;
+ chip->ident = V4L2_IDENT_SN9C20X;
+ return 0;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ if (chip->match.addr != sd->i2c_addr)
+ return -EINVAL;
+ chip->revision = 0;
+ chip->ident = i2c_ident[sd->sensor];
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+ cam->needs_full_bandwidth = 1;
+
+ sd->sensor = id->driver_info >> 8;
+ sd->i2c_addr = id->driver_info;
+ sd->flags = id->driver_info >> 16;
+
+ switch (sd->sensor) {
+ case SENSOR_MT9M112:
+ case SENSOR_MT9M111:
+ case SENSOR_OV9650:
+ case SENSOR_SOI968:
+ cam->cam_mode = sxga_mode;
+ cam->nmodes = ARRAY_SIZE(sxga_mode);
+ break;
+ case SENSOR_MT9M001:
+ cam->cam_mode = mono_mode;
+ cam->nmodes = ARRAY_SIZE(mono_mode);
+ break;
+ default:
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+ break;
+ }
+
+ sd->old_step = 0;
+ sd->older_step = 0;
+ sd->exposure_step = 16;
+
+ gspca_dev->cam.ctrls = sd->ctrls;
+
+ INIT_WORK(&sd->work, qual_upd);
+
+ return 0;
+}
+
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+ u8 value;
+ u8 i2c_init[9] =
+ {0x80, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03};
+
+ for (i = 0; i < ARRAY_SIZE(bridge_init); i++) {
+ value = bridge_init[i][1];
+ reg_w(gspca_dev, bridge_init[i][0], &value, 1);
+ if (gspca_dev->usb_err < 0) {
+ pr_err("Device initialization failed\n");
+ return gspca_dev->usb_err;
+ }
+ }
+
+ if (sd->flags & LED_REVERSE)
+ reg_w1(gspca_dev, 0x1006, 0x00);
+ else
+ reg_w1(gspca_dev, 0x1006, 0x20);
+
+ reg_w(gspca_dev, 0x10c0, i2c_init, 9);
+ if (gspca_dev->usb_err < 0) {
+ pr_err("Device initialization failed\n");
+ return gspca_dev->usb_err;
+ }
+
+ switch (sd->sensor) {
+ case SENSOR_OV9650:
+ ov9650_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
+ pr_info("OV9650 sensor detected\n");
+ break;
+ case SENSOR_OV9655:
+ ov9655_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
+ pr_info("OV9655 sensor detected\n");
+ break;
+ case SENSOR_SOI968:
+ soi968_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
+ pr_info("SOI968 sensor detected\n");
+ break;
+ case SENSOR_OV7660:
+ ov7660_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
+ pr_info("OV7660 sensor detected\n");
+ break;
+ case SENSOR_OV7670:
+ ov7670_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
+ pr_info("OV7670 sensor detected\n");
+ break;
+ case SENSOR_MT9VPRB:
+ mt9v_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
+ pr_info("MT9VPRB sensor detected\n");
+ break;
+ case SENSOR_MT9M111:
+ mt9m111_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
+ pr_info("MT9M111 sensor detected\n");
+ break;
+ case SENSOR_MT9M112:
+ mt9m112_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
+ pr_info("MT9M112 sensor detected\n");
+ break;
+ case SENSOR_MT9M001:
+ mt9m001_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
+ break;
+ case SENSOR_HV7131R:
+ hv7131r_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
+ pr_info("HV7131R sensor detected\n");
+ break;
+ default:
+ pr_err("Unsupported sensor\n");
+ gspca_dev->usb_err = -ENODEV;
+ }
+
+ return gspca_dev->usb_err;
+}
+
+static void configure_sensor_output(struct gspca_dev *gspca_dev, int mode)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 value;
+
+ switch (sd->sensor) {
+ case SENSOR_SOI968:
+ if (mode & MODE_SXGA) {
+ i2c_w1(gspca_dev, 0x17, 0x1d);
+ i2c_w1(gspca_dev, 0x18, 0xbd);
+ i2c_w1(gspca_dev, 0x19, 0x01);
+ i2c_w1(gspca_dev, 0x1a, 0x81);
+ i2c_w1(gspca_dev, 0x12, 0x00);
+ sd->hstart = 140;
+ sd->vstart = 19;
+ } else {
+ i2c_w1(gspca_dev, 0x17, 0x13);
+ i2c_w1(gspca_dev, 0x18, 0x63);
+ i2c_w1(gspca_dev, 0x19, 0x01);
+ i2c_w1(gspca_dev, 0x1a, 0x79);
+ i2c_w1(gspca_dev, 0x12, 0x40);
+ sd->hstart = 60;
+ sd->vstart = 11;
+ }
+ break;
+ case SENSOR_OV9650:
+ if (mode & MODE_SXGA) {
+ i2c_w1(gspca_dev, 0x17, 0x1b);
+ i2c_w1(gspca_dev, 0x18, 0xbc);
+ i2c_w1(gspca_dev, 0x19, 0x01);
+ i2c_w1(gspca_dev, 0x1a, 0x82);
+ i2c_r1(gspca_dev, 0x12, &value);
+ i2c_w1(gspca_dev, 0x12, value & 0x07);
+ } else {
+ i2c_w1(gspca_dev, 0x17, 0x24);
+ i2c_w1(gspca_dev, 0x18, 0xc5);
+ i2c_w1(gspca_dev, 0x19, 0x00);
+ i2c_w1(gspca_dev, 0x1a, 0x3c);
+ i2c_r1(gspca_dev, 0x12, &value);
+ i2c_w1(gspca_dev, 0x12, (value & 0x7) | 0x40);
+ }
+ break;
+ case SENSOR_MT9M112:
+ case SENSOR_MT9M111:
+ if (mode & MODE_SXGA) {
+ i2c_w2(gspca_dev, 0xf0, 0x0002);
+ i2c_w2(gspca_dev, 0xc8, 0x970b);
+ i2c_w2(gspca_dev, 0xf0, 0x0000);
+ } else {
+ i2c_w2(gspca_dev, 0xf0, 0x0002);
+ i2c_w2(gspca_dev, 0xc8, 0x8000);
+ i2c_w2(gspca_dev, 0xf0, 0x0000);
+ }
+ break;
+ }
+}
+
+static int sd_isoc_init(struct gspca_dev *gspca_dev)
+{
+ struct usb_interface *intf;
+ u32 flags = gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv;
+
+ /*
+ * When using the SN9C20X_I420 fmt the sn9c20x needs more bandwidth
+ * than our regular bandwidth calculations reserve, so we force the
+ * use of a specific altsetting when using the SN9C20X_I420 fmt.
+ */
+ if (!(flags & (MODE_RAW | MODE_JPEG))) {
+ intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
+
+ if (intf->num_altsetting != 9) {
+ pr_warn("sn9c20x camera with unknown number of alt "
+ "settings (%d), please report!\n",
+ intf->num_altsetting);
+ gspca_dev->alt = intf->num_altsetting;
+ return 0;
+ }
+
+ switch (gspca_dev->width) {
+ case 160: /* 160x120 */
+ gspca_dev->alt = 2;
+ break;
+ case 320: /* 320x240 */
+ gspca_dev->alt = 6;
+ break;
+ default: /* >= 640x480 */
+ gspca_dev->alt = 9;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+#define HW_WIN(mode, hstart, vstart) \
+((const u8 []){hstart, 0, vstart, 0, \
+(mode & MODE_SXGA ? 1280 >> 4 : 640 >> 4), \
+(mode & MODE_SXGA ? 1024 >> 3 : 480 >> 3)})
+
+#define CLR_WIN(width, height) \
+((const u8 [])\
+{0, width >> 2, 0, height >> 1,\
+((width >> 10) & 0x01) | ((height >> 8) & 0x6)})
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+ int width = gspca_dev->width;
+ int height = gspca_dev->height;
+ u8 fmt, scale = 0;
+
+ jpeg_define(sd->jpeg_hdr, height, width,
+ 0x21);
+ jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val);
+
+ if (mode & MODE_RAW)
+ fmt = 0x2d;
+ else if (mode & MODE_JPEG)
+ fmt = 0x24;
+ else
+ fmt = 0x2f; /* YUV 420 */
+ sd->fmt = fmt;
+
+ switch (mode & SCALE_MASK) {
+ case SCALE_1280x1024:
+ scale = 0xc0;
+ pr_info("Set 1280x1024\n");
+ break;
+ case SCALE_640x480:
+ scale = 0x80;
+ pr_info("Set 640x480\n");
+ break;
+ case SCALE_320x240:
+ scale = 0x90;
+ pr_info("Set 320x240\n");
+ break;
+ case SCALE_160x120:
+ scale = 0xa0;
+ pr_info("Set 160x120\n");
+ break;
+ }
+
+ configure_sensor_output(gspca_dev, mode);
+ reg_w(gspca_dev, 0x1100, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64);
+ reg_w(gspca_dev, 0x1140, &sd->jpeg_hdr[JPEG_QT1_OFFSET], 64);
+ reg_w(gspca_dev, 0x10fb, CLR_WIN(width, height), 5);
+ reg_w(gspca_dev, 0x1180, HW_WIN(mode, sd->hstart, sd->vstart), 6);
+ reg_w1(gspca_dev, 0x1189, scale);
+ reg_w1(gspca_dev, 0x10e0, fmt);
+
+ set_cmatrix(gspca_dev);
+ set_gamma(gspca_dev);
+ set_redblue(gspca_dev);
+ set_gain(gspca_dev);
+ set_exposure(gspca_dev);
+ set_hvflip(gspca_dev);
+
+ reg_w1(gspca_dev, 0x1007, 0x20);
+ reg_w1(gspca_dev, 0x1061, 0x03);
+
+ /* if JPEG, prepare the compression quality update */
+ if (mode & MODE_JPEG) {
+ sd->pktsz = sd->npkt = 0;
+ sd->nchg = 0;
+ sd->work_thread =
+ create_singlethread_workqueue(KBUILD_MODNAME);
+ }
+
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ reg_w1(gspca_dev, 0x1007, 0x00);
+ reg_w1(gspca_dev, 0x1061, 0x01);
+}
+
+/* called on streamoff with alt==0 and on disconnect */
+/* the usb_lock is held at entry - restore on exit */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->work_thread != NULL) {
+ mutex_unlock(&gspca_dev->usb_lock);
+ destroy_workqueue(sd->work_thread);
+ mutex_lock(&gspca_dev->usb_lock);
+ sd->work_thread = NULL;
+ }
+}
+
+static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s16 new_exp;
+
+ /*
+ * some hardcoded values are present
+ * like those for maximal/minimal exposure
+ * and exposure steps
+ */
+ if (avg_lum < MIN_AVG_LUM) {
+ if (sd->ctrls[EXPOSURE].val > 0x1770)
+ return;
+
+ new_exp = sd->ctrls[EXPOSURE].val + sd->exposure_step;
+ if (new_exp > 0x1770)
+ new_exp = 0x1770;
+ if (new_exp < 0x10)
+ new_exp = 0x10;
+ sd->ctrls[EXPOSURE].val = new_exp;
+ set_exposure(gspca_dev);
+
+ sd->older_step = sd->old_step;
+ sd->old_step = 1;
+
+ if (sd->old_step ^ sd->older_step)
+ sd->exposure_step /= 2;
+ else
+ sd->exposure_step += 2;
+ }
+ if (avg_lum > MAX_AVG_LUM) {
+ if (sd->ctrls[EXPOSURE].val < 0x10)
+ return;
+ new_exp = sd->ctrls[EXPOSURE].val - sd->exposure_step;
+ if (new_exp > 0x1700)
+ new_exp = 0x1770;
+ if (new_exp < 0x10)
+ new_exp = 0x10;
+ sd->ctrls[EXPOSURE].val = new_exp;
+ set_exposure(gspca_dev);
+ sd->older_step = sd->old_step;
+ sd->old_step = 0;
+
+ if (sd->old_step ^ sd->older_step)
+ sd->exposure_step /= 2;
+ else
+ sd->exposure_step += 2;
+ }
+}
+
+static void do_autogain(struct gspca_dev *gspca_dev, u16 avg_lum)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (avg_lum < MIN_AVG_LUM) {
+ if (sd->ctrls[GAIN].val + 1 <= 28) {
+ sd->ctrls[GAIN].val++;
+ set_gain(gspca_dev);
+ }
+ }
+ if (avg_lum > MAX_AVG_LUM) {
+ if (sd->ctrls[GAIN].val > 0) {
+ sd->ctrls[GAIN].val--;
+ set_gain(gspca_dev);
+ }
+ }
+}
+
+static void sd_dqcallback(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int avg_lum;
+
+ if (!sd->ctrls[AUTOGAIN].val)
+ return;
+
+ avg_lum = atomic_read(&sd->avg_lum);
+ if (sd->sensor == SENSOR_SOI968)
+ do_autogain(gspca_dev, avg_lum);
+ else
+ do_autoexposure(gspca_dev, avg_lum);
+}
+
+/* JPEG quality update */
+/* This function is executed from a work queue. */
+static void qual_upd(struct work_struct *work)
+{
+ struct sd *sd = container_of(work, struct sd, work);
+ struct gspca_dev *gspca_dev = &sd->gspca_dev;
+
+ mutex_lock(&gspca_dev->usb_lock);
+ PDEBUG(D_STREAM, "qual_upd %d%%", sd->ctrls[QUALITY].val);
+ set_quality(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
+}
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* interrupt packet */
+ int len) /* interrupt packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (!(sd->flags & HAS_NO_BUTTON) && len == 1) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+ input_sync(gspca_dev->input_dev);
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ return 0;
+ }
+ return -EINVAL;
+}
+#endif
+
+/* check the JPEG compression */
+static void transfer_check(struct gspca_dev *gspca_dev,
+ u8 *data)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int new_qual, r;
+
+ new_qual = 0;
+
+ /* if USB error, discard the frame and decrease the quality */
+ if (data[6] & 0x08) { /* USB FIFO full */
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ new_qual = -5;
+ } else {
+
+ /* else, compute the filling rate and a new JPEG quality */
+ r = (sd->pktsz * 100) /
+ (sd->npkt *
+ gspca_dev->urb[0]->iso_frame_desc[0].length);
+ if (r >= 85)
+ new_qual = -3;
+ else if (r < 75)
+ new_qual = 2;
+ }
+ if (new_qual != 0) {
+ sd->nchg += new_qual;
+ if (sd->nchg < -6 || sd->nchg >= 12) {
+ sd->nchg = 0;
+ new_qual += sd->ctrls[QUALITY].val;
+ if (new_qual < QUALITY_MIN)
+ new_qual = QUALITY_MIN;
+ else if (new_qual > QUALITY_MAX)
+ new_qual = QUALITY_MAX;
+ if (new_qual != sd->ctrls[QUALITY].val) {
+ sd->ctrls[QUALITY].val = new_qual;
+ queue_work(sd->work_thread, &sd->work);
+ }
+ }
+ } else {
+ sd->nchg = 0;
+ }
+ sd->pktsz = sd->npkt = 0;
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int avg_lum, is_jpeg;
+ static u8 frame_header[] =
+ {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96};
+
+ is_jpeg = (sd->fmt & 0x03) == 0;
+ if (len >= 64 && memcmp(data, frame_header, 6) == 0) {
+ avg_lum = ((data[35] >> 2) & 3) |
+ (data[20] << 2) |
+ (data[19] << 10);
+ avg_lum += ((data[35] >> 4) & 3) |
+ (data[22] << 2) |
+ (data[21] << 10);
+ avg_lum += ((data[35] >> 6) & 3) |
+ (data[24] << 2) |
+ (data[23] << 10);
+ avg_lum += (data[36] & 3) |
+ (data[26] << 2) |
+ (data[25] << 10);
+ avg_lum += ((data[36] >> 2) & 3) |
+ (data[28] << 2) |
+ (data[27] << 10);
+ avg_lum += ((data[36] >> 4) & 3) |
+ (data[30] << 2) |
+ (data[29] << 10);
+ avg_lum += ((data[36] >> 6) & 3) |
+ (data[32] << 2) |
+ (data[31] << 10);
+ avg_lum += ((data[44] >> 4) & 3) |
+ (data[34] << 2) |
+ (data[33] << 10);
+ avg_lum >>= 9;
+ atomic_set(&sd->avg_lum, avg_lum);
+
+ if (is_jpeg)
+ transfer_check(gspca_dev, data);
+
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+ len -= 64;
+ if (len == 0)
+ return;
+ data += 64;
+ }
+ if (gspca_dev->last_packet_type == LAST_PACKET) {
+ if (is_jpeg) {
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data, len);
+ } else {
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ data, len);
+ }
+ } else {
+ /* if JPEG, count the packets and their size */
+ if (is_jpeg) {
+ sd->npkt++;
+ sd->pktsz += len;
+ }
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ }
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = KBUILD_MODNAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .isoc_init = sd_isoc_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ .int_pkt_scan = sd_int_pkt_scan,
+#endif
+ .dq_callback = sd_dqcallback,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .set_register = sd_dbg_s_register,
+ .get_register = sd_dbg_g_register,
+#endif
+ .get_chip_ident = sd_chip_ident,
+};
+
+#define SN9C20X(sensor, i2c_addr, flags) \
+ .driver_info = ((flags & 0xff) << 16) \
+ | (SENSOR_ ## sensor << 8) \
+ | (i2c_addr)
+
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x0c45, 0x6240), SN9C20X(MT9M001, 0x5d, 0)},
+ {USB_DEVICE(0x0c45, 0x6242), SN9C20X(MT9M111, 0x5d, 0)},
+ {USB_DEVICE(0x0c45, 0x6248), SN9C20X(OV9655, 0x30, 0)},
+ {USB_DEVICE(0x0c45, 0x624c), SN9C20X(MT9M112, 0x5d, 0)},
+ {USB_DEVICE(0x0c45, 0x624e), SN9C20X(SOI968, 0x30, LED_REVERSE)},
+ {USB_DEVICE(0x0c45, 0x624f), SN9C20X(OV9650, 0x30,
+ (FLIP_DETECT | HAS_NO_BUTTON))},
+ {USB_DEVICE(0x0c45, 0x6251), SN9C20X(OV9650, 0x30, 0)},
+ {USB_DEVICE(0x0c45, 0x6253), SN9C20X(OV9650, 0x30, 0)},
+ {USB_DEVICE(0x0c45, 0x6260), SN9C20X(OV7670, 0x21, 0)},
+ {USB_DEVICE(0x0c45, 0x6270), SN9C20X(MT9VPRB, 0x00, 0)},
+ {USB_DEVICE(0x0c45, 0x627b), SN9C20X(OV7660, 0x21, FLIP_DETECT)},
+ {USB_DEVICE(0x0c45, 0x627c), SN9C20X(HV7131R, 0x11, 0)},
+ {USB_DEVICE(0x0c45, 0x627f), SN9C20X(OV9650, 0x30, 0)},
+ {USB_DEVICE(0x0c45, 0x6280), SN9C20X(MT9M001, 0x5d, 0)},
+ {USB_DEVICE(0x0c45, 0x6282), SN9C20X(MT9M111, 0x5d, 0)},
+ {USB_DEVICE(0x0c45, 0x6288), SN9C20X(OV9655, 0x30, 0)},
+ {USB_DEVICE(0x0c45, 0x628c), SN9C20X(MT9M112, 0x5d, 0)},
+ {USB_DEVICE(0x0c45, 0x628e), SN9C20X(SOI968, 0x30, 0)},
+ {USB_DEVICE(0x0c45, 0x628f), SN9C20X(OV9650, 0x30, 0)},
+ {USB_DEVICE(0x0c45, 0x62a0), SN9C20X(OV7670, 0x21, 0)},
+ {USB_DEVICE(0x0c45, 0x62b0), SN9C20X(MT9VPRB, 0x00, 0)},
+ {USB_DEVICE(0x0c45, 0x62b3), SN9C20X(OV9655, 0x30, LED_REVERSE)},
+ {USB_DEVICE(0x0c45, 0x62bb), SN9C20X(OV7660, 0x21, LED_REVERSE)},
+ {USB_DEVICE(0x0c45, 0x62bc), SN9C20X(HV7131R, 0x11, 0)},
+ {USB_DEVICE(0x045e, 0x00f4), SN9C20X(OV9650, 0x30, 0)},
+ {USB_DEVICE(0x145f, 0x013d), SN9C20X(OV7660, 0x21, 0)},
+ {USB_DEVICE(0x0458, 0x7029), SN9C20X(HV7131R, 0x11, 0)},
+ {USB_DEVICE(0x0458, 0x704a), SN9C20X(MT9M112, 0x5d, 0)},
+ {USB_DEVICE(0x0458, 0x704c), SN9C20X(MT9M112, 0x5d, 0)},
+ {USB_DEVICE(0xa168, 0x0610), SN9C20X(HV7131R, 0x11, 0)},
+ {USB_DEVICE(0xa168, 0x0611), SN9C20X(HV7131R, 0x11, 0)},
+ {USB_DEVICE(0xa168, 0x0613), SN9C20X(HV7131R, 0x11, 0)},
+ {USB_DEVICE(0xa168, 0x0618), SN9C20X(HV7131R, 0x11, 0)},
+ {USB_DEVICE(0xa168, 0x0614), SN9C20X(MT9M111, 0x5d, 0)},
+ {USB_DEVICE(0xa168, 0x0615), SN9C20X(MT9M111, 0x5d, 0)},
+ {USB_DEVICE(0xa168, 0x0617), SN9C20X(MT9M111, 0x5d, 0)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+ .reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c
new file mode 100644
index 00000000..6a1148d7
--- /dev/null
+++ b/drivers/media/video/gspca/sonixb.c
@@ -0,0 +1,1533 @@
+/*
+ * sonix sn9c102 (bayer) library
+ *
+ * Copyright (C) 2009-2011 Jean-François Moine <http://moinejf.free.fr>
+ * Copyright (C) 2003 2004 Michel Xhaard mxhaard@magic.fr
+ * Add Pas106 Stefano Mozzi (C) 2004
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* Some documentation on known sonixb registers:
+
+Reg Use
+sn9c101 / sn9c102:
+0x10 high nibble red gain low nibble blue gain
+0x11 low nibble green gain
+sn9c103:
+0x05 red gain 0-127
+0x06 blue gain 0-127
+0x07 green gain 0-127
+all:
+0x08-0x0f i2c / 3wire registers
+0x12 hstart
+0x13 vstart
+0x15 hsize (hsize = register-value * 16)
+0x16 vsize (vsize = register-value * 16)
+0x17 bit 0 toggle compression quality (according to sn9c102 driver)
+0x18 bit 7 enables compression, bit 4-5 set image down scaling:
+ 00 scale 1, 01 scale 1/2, 10, scale 1/4
+0x19 high-nibble is sensor clock divider, changes exposure on sensors which
+ use a clock generated by the bridge. Some sensors have their own clock.
+0x1c auto_exposure area (for avg_lum) startx (startx = register-value * 32)
+0x1d auto_exposure area (for avg_lum) starty (starty = register-value * 32)
+0x1e auto_exposure area (for avg_lum) stopx (hsize = (0x1e - 0x1c) * 32)
+0x1f auto_exposure area (for avg_lum) stopy (vsize = (0x1f - 0x1d) * 32)
+*/
+
+#define MODULE_NAME "sonixb"
+
+#include <linux/input.h>
+#include "gspca.h"
+
+MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>");
+MODULE_DESCRIPTION("GSPCA/SN9C102 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* controls */
+enum e_ctrl {
+ BRIGHTNESS,
+ GAIN,
+ EXPOSURE,
+ AUTOGAIN,
+ FREQ,
+ NCTRLS /* number of controls */
+};
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ struct gspca_ctrl ctrls[NCTRLS];
+
+ atomic_t avg_lum;
+ int prev_avg_lum;
+ int exp_too_low_cnt;
+ int exp_too_high_cnt;
+ int header_read;
+ u8 header[12]; /* Header without sof marker */
+
+ unsigned char autogain_ignore_frames;
+ unsigned char frames_to_drop;
+
+ __u8 bridge; /* Type of bridge */
+#define BRIDGE_101 0
+#define BRIDGE_102 0 /* We make no difference between 101 and 102 */
+#define BRIDGE_103 1
+
+ __u8 sensor; /* Type of image sensor chip */
+#define SENSOR_HV7131D 0
+#define SENSOR_HV7131R 1
+#define SENSOR_OV6650 2
+#define SENSOR_OV7630 3
+#define SENSOR_PAS106 4
+#define SENSOR_PAS202 5
+#define SENSOR_TAS5110C 6
+#define SENSOR_TAS5110D 7
+#define SENSOR_TAS5130CXX 8
+ __u8 reg11;
+};
+
+typedef const __u8 sensor_init_t[8];
+
+struct sensor_data {
+ const __u8 *bridge_init;
+ sensor_init_t *sensor_init;
+ int sensor_init_size;
+ int flags;
+ unsigned ctrl_dis;
+ __u8 sensor_addr;
+};
+
+/* sensor_data flags */
+#define F_GAIN 0x01 /* has gain */
+#define F_SIF 0x02 /* sif or vga */
+#define F_COARSE_EXPO 0x04 /* exposure control is coarse */
+
+/* priv field of struct v4l2_pix_format flags (do not use low nibble!) */
+#define MODE_RAW 0x10 /* raw bayer mode */
+#define MODE_REDUCED_SIF 0x20 /* vga mode (320x240 / 160x120) on sif cam */
+
+/* ctrl_dis helper macros */
+#define NO_EXPO ((1 << EXPOSURE) | (1 << AUTOGAIN))
+#define NO_FREQ (1 << FREQ)
+#define NO_BRIGHTNESS (1 << BRIGHTNESS)
+
+#define COMP 0xc7 /* 0x87 //0x07 */
+#define COMP1 0xc9 /* 0x89 //0x09 */
+
+#define MCK_INIT 0x63
+#define MCK_INIT1 0x20 /*fixme: Bayer - 0x50 for JPEG ??*/
+
+#define SYS_CLK 0x04
+
+#define SENS(bridge, sensor, _flags, _ctrl_dis, _sensor_addr) \
+{ \
+ .bridge_init = bridge, \
+ .sensor_init = sensor, \
+ .sensor_init_size = sizeof(sensor), \
+ .flags = _flags, .ctrl_dis = _ctrl_dis, .sensor_addr = _sensor_addr \
+}
+
+/* We calculate the autogain at the end of the transfer of a frame, at this
+ moment a frame with the old settings is being captured and transmitted. So
+ if we adjust the gain or exposure we must ignore atleast the next frame for
+ the new settings to come into effect before doing any other adjustments. */
+#define AUTOGAIN_IGNORE_FRAMES 1
+
+/* V4L2 controls supported by the driver */
+static void setbrightness(struct gspca_dev *gspca_dev);
+static void setgain(struct gspca_dev *gspca_dev);
+static void setexposure(struct gspca_dev *gspca_dev);
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static void setfreq(struct gspca_dev *gspca_dev);
+
+static const struct ctrl sd_ctrls[NCTRLS] = {
+[BRIGHTNESS] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 127,
+ },
+ .set_control = setbrightness
+ },
+[GAIN] = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+#define GAIN_KNEE 230
+ .default_value = 127,
+ },
+ .set_control = setgain
+ },
+[EXPOSURE] = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 0,
+ .maximum = 1023,
+ .step = 1,
+ .default_value = 66,
+ /* 33 ms / 30 fps (except on PASXXX) */
+#define EXPOSURE_KNEE 200 /* 100 ms / 10 fps (except on PASXXX) */
+ .flags = 0,
+ },
+ .set_control = setexposure
+ },
+/* for coarse exposure */
+#define COARSE_EXPOSURE_MIN 2
+#define COARSE_EXPOSURE_MAX 15
+#define COARSE_EXPOSURE_DEF 2 /* 30 fps */
+[AUTOGAIN] = {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Automatic Gain (and Exposure)",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define AUTOGAIN_DEF 1
+ .default_value = AUTOGAIN_DEF,
+ .flags = V4L2_CTRL_FLAG_UPDATE
+ },
+ .set = sd_setautogain,
+ },
+[FREQ] = {
+ {
+ .id = V4L2_CID_POWER_LINE_FREQUENCY,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Light frequency filter",
+ .minimum = 0,
+ .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */
+ .step = 1,
+#define FREQ_DEF 0
+ .default_value = FREQ_DEF,
+ },
+ .set_control = setfreq
+ },
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 2 | MODE_RAW},
+ {160, 120, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120 * 5 / 4,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 2},
+ {320, 240, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 5 / 4,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 5 / 4,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+static const struct v4l2_pix_format sif_mode[] = {
+ {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1 | MODE_RAW | MODE_REDUCED_SIF},
+ {160, 120, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120 * 5 / 4,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1 | MODE_REDUCED_SIF},
+ {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1 | MODE_RAW},
+ {176, 144, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144 * 5 / 4,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {320, 240, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 5 / 4,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0 | MODE_REDUCED_SIF},
+ {352, 288, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288 * 5 / 4,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+
+static const __u8 initHv7131d[] = {
+ 0x04, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x02, 0x00,
+ 0x28, 0x1e, 0x60, 0x8e, 0x42,
+};
+static const __u8 hv7131d_sensor_init[][8] = {
+ {0xa0, 0x11, 0x01, 0x04, 0x00, 0x00, 0x00, 0x17},
+ {0xa0, 0x11, 0x02, 0x00, 0x00, 0x00, 0x00, 0x17},
+ {0xa0, 0x11, 0x28, 0x00, 0x00, 0x00, 0x00, 0x17},
+ {0xa0, 0x11, 0x30, 0x30, 0x00, 0x00, 0x00, 0x17}, /* reset level */
+ {0xa0, 0x11, 0x34, 0x02, 0x00, 0x00, 0x00, 0x17}, /* pixel bias volt */
+};
+
+static const __u8 initHv7131r[] = {
+ 0x46, 0x77, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x01, 0x00,
+ 0x28, 0x1e, 0x60, 0x8a, 0x20,
+};
+static const __u8 hv7131r_sensor_init[][8] = {
+ {0xc0, 0x11, 0x31, 0x38, 0x2a, 0x2e, 0x00, 0x10},
+ {0xa0, 0x11, 0x01, 0x08, 0x2a, 0x2e, 0x00, 0x10},
+ {0xb0, 0x11, 0x20, 0x00, 0xd0, 0x2e, 0x00, 0x10},
+ {0xc0, 0x11, 0x25, 0x03, 0x0e, 0x28, 0x00, 0x16},
+ {0xa0, 0x11, 0x30, 0x10, 0x0e, 0x28, 0x00, 0x15},
+};
+static const __u8 initOv6650[] = {
+ 0x44, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x01, 0x0a, 0x16, 0x12, 0x68, 0x8b,
+ 0x10,
+};
+static const __u8 ov6650_sensor_init[][8] = {
+ /* Bright, contrast, etc are set through SCBB interface.
+ * AVCAP on win2 do not send any data on this controls. */
+ /* Anyway, some registers appears to alter bright and constrat */
+
+ /* Reset sensor */
+ {0xa0, 0x60, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10},
+ /* Set clock register 0x11 low nibble is clock divider */
+ {0xd0, 0x60, 0x11, 0xc0, 0x1b, 0x18, 0xc1, 0x10},
+ /* Next some unknown stuff */
+ {0xb0, 0x60, 0x15, 0x00, 0x02, 0x18, 0xc1, 0x10},
+/* {0xa0, 0x60, 0x1b, 0x01, 0x02, 0x18, 0xc1, 0x10},
+ * THIS SET GREEN SCREEN
+ * (pixels could be innverted in decode kind of "brg",
+ * but blue wont be there. Avoid this data ... */
+ {0xd0, 0x60, 0x26, 0x01, 0x14, 0xd8, 0xa4, 0x10}, /* format out? */
+ {0xd0, 0x60, 0x26, 0x01, 0x14, 0xd8, 0xa4, 0x10},
+ {0xa0, 0x60, 0x30, 0x3d, 0x0a, 0xd8, 0xa4, 0x10},
+ /* Enable rgb brightness control */
+ {0xa0, 0x60, 0x61, 0x08, 0x00, 0x00, 0x00, 0x10},
+ /* HDG: Note windows uses the line below, which sets both register 0x60
+ and 0x61 I believe these registers of the ov6650 are identical as
+ those of the ov7630, because if this is true the windows settings
+ add a bit additional red gain and a lot additional blue gain, which
+ matches my findings that the windows settings make blue much too
+ blue and red a little too red.
+ {0xb0, 0x60, 0x60, 0x66, 0x68, 0xd8, 0xa4, 0x10}, */
+ /* Some more unknown stuff */
+ {0xa0, 0x60, 0x68, 0x04, 0x68, 0xd8, 0xa4, 0x10},
+ {0xd0, 0x60, 0x17, 0x24, 0xd6, 0x04, 0x94, 0x10}, /* Clipreg */
+};
+
+static const __u8 initOv7630[] = {
+ 0x04, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, /* r01 .. r08 */
+ 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* r09 .. r10 */
+ 0x00, 0x01, 0x01, 0x0a, /* r11 .. r14 */
+ 0x28, 0x1e, /* H & V sizes r15 .. r16 */
+ 0x68, 0x8f, MCK_INIT1, /* r17 .. r19 */
+};
+static const __u8 ov7630_sensor_init[][8] = {
+ {0xa0, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xb0, 0x21, 0x01, 0x77, 0x3a, 0x00, 0x00, 0x10},
+/* {0xd0, 0x21, 0x12, 0x7c, 0x01, 0x80, 0x34, 0x10}, jfm */
+ {0xd0, 0x21, 0x12, 0x5c, 0x00, 0x80, 0x34, 0x10}, /* jfm */
+ {0xa0, 0x21, 0x1b, 0x04, 0x00, 0x80, 0x34, 0x10},
+ {0xa0, 0x21, 0x20, 0x44, 0x00, 0x80, 0x34, 0x10},
+ {0xa0, 0x21, 0x23, 0xee, 0x00, 0x80, 0x34, 0x10},
+ {0xd0, 0x21, 0x26, 0xa0, 0x9a, 0xa0, 0x30, 0x10},
+ {0xb0, 0x21, 0x2a, 0x80, 0x00, 0xa0, 0x30, 0x10},
+ {0xb0, 0x21, 0x2f, 0x3d, 0x24, 0xa0, 0x30, 0x10},
+ {0xa0, 0x21, 0x32, 0x86, 0x24, 0xa0, 0x30, 0x10},
+ {0xb0, 0x21, 0x60, 0xa9, 0x4a, 0xa0, 0x30, 0x10},
+/* {0xb0, 0x21, 0x60, 0xa9, 0x42, 0xa0, 0x30, 0x10}, * jfm */
+ {0xa0, 0x21, 0x65, 0x00, 0x42, 0xa0, 0x30, 0x10},
+ {0xa0, 0x21, 0x69, 0x38, 0x42, 0xa0, 0x30, 0x10},
+ {0xc0, 0x21, 0x6f, 0x88, 0x0b, 0x00, 0x30, 0x10},
+ {0xc0, 0x21, 0x74, 0x21, 0x8e, 0x00, 0x30, 0x10},
+ {0xa0, 0x21, 0x7d, 0xf7, 0x8e, 0x00, 0x30, 0x10},
+ {0xd0, 0x21, 0x17, 0x1c, 0xbd, 0x06, 0xf6, 0x10},
+};
+
+static const __u8 initPas106[] = {
+ 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x04, 0x01, 0x00,
+ 0x16, 0x12, 0x24, COMP1, MCK_INIT1,
+};
+/* compression 0x86 mckinit1 0x2b */
+
+/* "Known" PAS106B registers:
+ 0x02 clock divider
+ 0x03 Variable framerate bits 4-11
+ 0x04 Var framerate bits 0-3, one must leave the 4 msb's at 0 !!
+ The variable framerate control must never be set lower then 300,
+ which sets the framerate at 90 / reg02, otherwise vsync is lost.
+ 0x05 Shutter Time Line Offset, this can be used as an exposure control:
+ 0 = use full frame time, 255 = no exposure at all
+ Note this may never be larger then "var-framerate control" / 2 - 2.
+ When var-framerate control is < 514, no exposure is reached at the max
+ allowed value for the framerate control value, rather then at 255.
+ 0x06 Shutter Time Pixel Offset, like reg05 this influences exposure, but
+ only a very little bit, leave at 0xcd
+ 0x07 offset sign bit (bit0 1 > negative offset)
+ 0x08 offset
+ 0x09 Blue Gain
+ 0x0a Green1 Gain
+ 0x0b Green2 Gain
+ 0x0c Red Gain
+ 0x0e Global gain
+ 0x13 Write 1 to commit settings to sensor
+*/
+
+static const __u8 pas106_sensor_init[][8] = {
+ /* Pixel Clock Divider 6 */
+ { 0xa1, 0x40, 0x02, 0x04, 0x00, 0x00, 0x00, 0x14 },
+ /* Frame Time MSB (also seen as 0x12) */
+ { 0xa1, 0x40, 0x03, 0x13, 0x00, 0x00, 0x00, 0x14 },
+ /* Frame Time LSB (also seen as 0x05) */
+ { 0xa1, 0x40, 0x04, 0x06, 0x00, 0x00, 0x00, 0x14 },
+ /* Shutter Time Line Offset (also seen as 0x6d) */
+ { 0xa1, 0x40, 0x05, 0x65, 0x00, 0x00, 0x00, 0x14 },
+ /* Shutter Time Pixel Offset (also seen as 0xb1) */
+ { 0xa1, 0x40, 0x06, 0xcd, 0x00, 0x00, 0x00, 0x14 },
+ /* Black Level Subtract Sign (also seen 0x00) */
+ { 0xa1, 0x40, 0x07, 0xc1, 0x00, 0x00, 0x00, 0x14 },
+ /* Black Level Subtract Level (also seen 0x01) */
+ { 0xa1, 0x40, 0x08, 0x06, 0x00, 0x00, 0x00, 0x14 },
+ { 0xa1, 0x40, 0x08, 0x06, 0x00, 0x00, 0x00, 0x14 },
+ /* Color Gain B Pixel 5 a */
+ { 0xa1, 0x40, 0x09, 0x05, 0x00, 0x00, 0x00, 0x14 },
+ /* Color Gain G1 Pixel 1 5 */
+ { 0xa1, 0x40, 0x0a, 0x04, 0x00, 0x00, 0x00, 0x14 },
+ /* Color Gain G2 Pixel 1 0 5 */
+ { 0xa1, 0x40, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x14 },
+ /* Color Gain R Pixel 3 1 */
+ { 0xa1, 0x40, 0x0c, 0x05, 0x00, 0x00, 0x00, 0x14 },
+ /* Color GainH Pixel */
+ { 0xa1, 0x40, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x14 },
+ /* Global Gain */
+ { 0xa1, 0x40, 0x0e, 0x0e, 0x00, 0x00, 0x00, 0x14 },
+ /* Contrast */
+ { 0xa1, 0x40, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x14 },
+ /* H&V synchro polarity */
+ { 0xa1, 0x40, 0x10, 0x06, 0x00, 0x00, 0x00, 0x14 },
+ /* ?default */
+ { 0xa1, 0x40, 0x11, 0x06, 0x00, 0x00, 0x00, 0x14 },
+ /* DAC scale */
+ { 0xa1, 0x40, 0x12, 0x06, 0x00, 0x00, 0x00, 0x14 },
+ /* ?default */
+ { 0xa1, 0x40, 0x14, 0x02, 0x00, 0x00, 0x00, 0x14 },
+ /* Validate Settings */
+ { 0xa1, 0x40, 0x13, 0x01, 0x00, 0x00, 0x00, 0x14 },
+};
+
+static const __u8 initPas202[] = {
+ 0x44, 0x44, 0x21, 0x30, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x03, 0x0a,
+ 0x28, 0x1e, 0x20, 0x89, 0x20,
+};
+
+/* "Known" PAS202BCB registers:
+ 0x02 clock divider
+ 0x04 Variable framerate bits 6-11 (*)
+ 0x05 Var framerate bits 0-5, one must leave the 2 msb's at 0 !!
+ 0x07 Blue Gain
+ 0x08 Green Gain
+ 0x09 Red Gain
+ 0x0b offset sign bit (bit0 1 > negative offset)
+ 0x0c offset
+ 0x0e Unknown image is slightly brighter when bit 0 is 0, if reg0f is 0 too,
+ leave at 1 otherwise we get a jump in our exposure control
+ 0x0f Exposure 0-255, 0 = use full frame time, 255 = no exposure at all
+ 0x10 Master gain 0 - 31
+ 0x11 write 1 to apply changes
+ (*) The variable framerate control must never be set lower then 500
+ which sets the framerate at 30 / reg02, otherwise vsync is lost.
+*/
+static const __u8 pas202_sensor_init[][8] = {
+ /* Set the clock divider to 4 -> 30 / 4 = 7.5 fps, we would like
+ to set it lower, but for some reason the bridge starts missing
+ vsync's then */
+ {0xa0, 0x40, 0x02, 0x04, 0x00, 0x00, 0x00, 0x10},
+ {0xd0, 0x40, 0x04, 0x07, 0x34, 0x00, 0x09, 0x10},
+ {0xd0, 0x40, 0x08, 0x01, 0x00, 0x00, 0x01, 0x10},
+ {0xd0, 0x40, 0x0c, 0x00, 0x0c, 0x01, 0x32, 0x10},
+ {0xd0, 0x40, 0x10, 0x00, 0x01, 0x00, 0x63, 0x10},
+ {0xa0, 0x40, 0x15, 0x70, 0x01, 0x00, 0x63, 0x10},
+ {0xa0, 0x40, 0x18, 0x00, 0x01, 0x00, 0x63, 0x10},
+ {0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10},
+ {0xa0, 0x40, 0x03, 0x56, 0x01, 0x00, 0x63, 0x10},
+ {0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10},
+};
+
+static const __u8 initTas5110c[] = {
+ 0x44, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x45, 0x09, 0x0a,
+ 0x16, 0x12, 0x60, 0x86, 0x2b,
+};
+/* Same as above, except a different hstart */
+static const __u8 initTas5110d[] = {
+ 0x44, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x41, 0x09, 0x0a,
+ 0x16, 0x12, 0x60, 0x86, 0x2b,
+};
+/* tas5110c is 3 wire, tas5110d is 2 wire (regular i2c) */
+static const __u8 tas5110c_sensor_init[][8] = {
+ {0x30, 0x11, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x10},
+ {0x30, 0x11, 0x02, 0x20, 0xa9, 0x00, 0x00, 0x10},
+};
+/* Known TAS5110D registers
+ * reg02: gain, bit order reversed!! 0 == max gain, 255 == min gain
+ * reg03: bit3: vflip, bit4: ~hflip, bit7: ~gainboost (~ == inverted)
+ * Note: writing reg03 seems to only work when written together with 02
+ */
+static const __u8 tas5110d_sensor_init[][8] = {
+ {0xa0, 0x61, 0x9a, 0xca, 0x00, 0x00, 0x00, 0x17}, /* reset */
+};
+
+static const __u8 initTas5130[] = {
+ 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x68, 0x0c, 0x0a,
+ 0x28, 0x1e, 0x60, COMP, MCK_INIT,
+};
+static const __u8 tas5130_sensor_init[][8] = {
+/* {0x30, 0x11, 0x00, 0x40, 0x47, 0x00, 0x00, 0x10},
+ * shutter 0x47 short exposure? */
+ {0x30, 0x11, 0x00, 0x40, 0x01, 0x00, 0x00, 0x10},
+ /* shutter 0x01 long exposure */
+ {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10},
+};
+
+static const struct sensor_data sensor_data[] = {
+SENS(initHv7131d, hv7131d_sensor_init, F_GAIN, NO_BRIGHTNESS|NO_FREQ, 0),
+SENS(initHv7131r, hv7131r_sensor_init, 0, NO_BRIGHTNESS|NO_EXPO|NO_FREQ, 0),
+SENS(initOv6650, ov6650_sensor_init, F_GAIN|F_SIF, 0, 0x60),
+SENS(initOv7630, ov7630_sensor_init, F_GAIN, 0, 0x21),
+SENS(initPas106, pas106_sensor_init, F_GAIN|F_SIF, NO_FREQ, 0),
+SENS(initPas202, pas202_sensor_init, F_GAIN, NO_FREQ, 0),
+SENS(initTas5110c, tas5110c_sensor_init, F_GAIN|F_SIF|F_COARSE_EXPO,
+ NO_BRIGHTNESS|NO_FREQ, 0),
+SENS(initTas5110d, tas5110d_sensor_init, F_GAIN|F_SIF|F_COARSE_EXPO,
+ NO_BRIGHTNESS|NO_FREQ, 0),
+SENS(initTas5130, tas5130_sensor_init, F_GAIN,
+ NO_BRIGHTNESS|NO_EXPO|NO_FREQ, 0),
+};
+
+/* get one byte in gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev,
+ __u16 value)
+{
+ usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ 0, /* request */
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ value,
+ 0, /* index */
+ gspca_dev->usb_buf, 1,
+ 500);
+}
+
+static void reg_w(struct gspca_dev *gspca_dev,
+ __u16 value,
+ const __u8 *buffer,
+ int len)
+{
+#ifdef GSPCA_DEBUG
+ if (len > USB_BUF_SZ) {
+ PDEBUG(D_ERR|D_PACK, "reg_w: buffer overflow");
+ return;
+ }
+#endif
+ memcpy(gspca_dev->usb_buf, buffer, len);
+ usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0x08, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ value,
+ 0, /* index */
+ gspca_dev->usb_buf, len,
+ 500);
+}
+
+static int i2c_w(struct gspca_dev *gspca_dev, const __u8 *buffer)
+{
+ int retry = 60;
+
+ /* is i2c ready */
+ reg_w(gspca_dev, 0x08, buffer, 8);
+ while (retry--) {
+ msleep(10);
+ reg_r(gspca_dev, 0x08);
+ if (gspca_dev->usb_buf[0] & 0x04) {
+ if (gspca_dev->usb_buf[0] & 0x08)
+ return -1;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static void i2c_w_vector(struct gspca_dev *gspca_dev,
+ const __u8 buffer[][8], int len)
+{
+ for (;;) {
+ reg_w(gspca_dev, 0x08, *buffer, 8);
+ len -= 8;
+ if (len <= 0)
+ break;
+ buffer++;
+ }
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->sensor) {
+ case SENSOR_OV6650:
+ case SENSOR_OV7630: {
+ __u8 i2cOV[] =
+ {0xa0, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10};
+
+ /* change reg 0x06 */
+ i2cOV[1] = sensor_data[sd->sensor].sensor_addr;
+ i2cOV[3] = sd->ctrls[BRIGHTNESS].val;
+ if (i2c_w(gspca_dev, i2cOV) < 0)
+ goto err;
+ break;
+ }
+ case SENSOR_PAS106:
+ case SENSOR_PAS202: {
+ __u8 i2cpbright[] =
+ {0xb0, 0x40, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x16};
+ __u8 i2cpdoit[] =
+ {0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16};
+
+ /* PAS106 uses reg 7 and 8 instead of b and c */
+ if (sd->sensor == SENSOR_PAS106) {
+ i2cpbright[2] = 7;
+ i2cpdoit[2] = 0x13;
+ }
+
+ if (sd->ctrls[BRIGHTNESS].val < 127) {
+ /* change reg 0x0b, signreg */
+ i2cpbright[3] = 0x01;
+ /* set reg 0x0c, offset */
+ i2cpbright[4] = 127 - sd->ctrls[BRIGHTNESS].val;
+ } else
+ i2cpbright[4] = sd->ctrls[BRIGHTNESS].val - 127;
+
+ if (i2c_w(gspca_dev, i2cpbright) < 0)
+ goto err;
+ if (i2c_w(gspca_dev, i2cpdoit) < 0)
+ goto err;
+ break;
+ }
+ }
+ return;
+err:
+ PDEBUG(D_ERR, "i2c error brightness");
+}
+
+static void setsensorgain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 gain = sd->ctrls[GAIN].val;
+
+ switch (sd->sensor) {
+ case SENSOR_HV7131D: {
+ __u8 i2c[] =
+ {0xc0, 0x11, 0x31, 0x00, 0x00, 0x00, 0x00, 0x17};
+
+ i2c[3] = 0x3f - (gain / 4);
+ i2c[4] = 0x3f - (gain / 4);
+ i2c[5] = 0x3f - (gain / 4);
+
+ if (i2c_w(gspca_dev, i2c) < 0)
+ goto err;
+ break;
+ }
+ case SENSOR_TAS5110C:
+ case SENSOR_TAS5130CXX: {
+ __u8 i2c[] =
+ {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10};
+
+ i2c[4] = 255 - gain;
+ if (i2c_w(gspca_dev, i2c) < 0)
+ goto err;
+ break;
+ }
+ case SENSOR_TAS5110D: {
+ __u8 i2c[] = {
+ 0xb0, 0x61, 0x02, 0x00, 0x10, 0x00, 0x00, 0x17 };
+ gain = 255 - gain;
+ /* The bits in the register are the wrong way around!! */
+ i2c[3] |= (gain & 0x80) >> 7;
+ i2c[3] |= (gain & 0x40) >> 5;
+ i2c[3] |= (gain & 0x20) >> 3;
+ i2c[3] |= (gain & 0x10) >> 1;
+ i2c[3] |= (gain & 0x08) << 1;
+ i2c[3] |= (gain & 0x04) << 3;
+ i2c[3] |= (gain & 0x02) << 5;
+ i2c[3] |= (gain & 0x01) << 7;
+ if (i2c_w(gspca_dev, i2c) < 0)
+ goto err;
+ break;
+ }
+
+ case SENSOR_OV6650:
+ gain >>= 1;
+ /* fall thru */
+ case SENSOR_OV7630: {
+ __u8 i2c[] = {0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10};
+
+ i2c[1] = sensor_data[sd->sensor].sensor_addr;
+ i2c[3] = gain >> 2;
+ if (i2c_w(gspca_dev, i2c) < 0)
+ goto err;
+ break;
+ }
+ case SENSOR_PAS106:
+ case SENSOR_PAS202: {
+ __u8 i2cpgain[] =
+ {0xa0, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x15};
+ __u8 i2cpcolorgain[] =
+ {0xc0, 0x40, 0x07, 0x00, 0x00, 0x00, 0x00, 0x15};
+ __u8 i2cpdoit[] =
+ {0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16};
+
+ /* PAS106 uses different regs (and has split green gains) */
+ if (sd->sensor == SENSOR_PAS106) {
+ i2cpgain[2] = 0x0e;
+ i2cpcolorgain[0] = 0xd0;
+ i2cpcolorgain[2] = 0x09;
+ i2cpdoit[2] = 0x13;
+ }
+
+ i2cpgain[3] = gain >> 3;
+ i2cpcolorgain[3] = gain >> 4;
+ i2cpcolorgain[4] = gain >> 4;
+ i2cpcolorgain[5] = gain >> 4;
+ i2cpcolorgain[6] = gain >> 4;
+
+ if (i2c_w(gspca_dev, i2cpgain) < 0)
+ goto err;
+ if (i2c_w(gspca_dev, i2cpcolorgain) < 0)
+ goto err;
+ if (i2c_w(gspca_dev, i2cpdoit) < 0)
+ goto err;
+ break;
+ }
+ }
+ return;
+err:
+ PDEBUG(D_ERR, "i2c error gain");
+}
+
+static void setgain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u8 gain;
+ __u8 buf[3] = { 0, 0, 0 };
+
+ if (sensor_data[sd->sensor].flags & F_GAIN) {
+ /* Use the sensor gain to do the actual gain */
+ setsensorgain(gspca_dev);
+ return;
+ }
+
+ if (sd->bridge == BRIDGE_103) {
+ gain = sd->ctrls[GAIN].val >> 1;
+ buf[0] = gain; /* Red */
+ buf[1] = gain; /* Green */
+ buf[2] = gain; /* Blue */
+ reg_w(gspca_dev, 0x05, buf, 3);
+ } else {
+ gain = sd->ctrls[GAIN].val >> 4;
+ buf[0] = gain << 4 | gain; /* Red and blue */
+ buf[1] = gain; /* Green */
+ reg_w(gspca_dev, 0x10, buf, 2);
+ }
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->sensor) {
+ case SENSOR_HV7131D: {
+ /* Note the datasheet wrongly says line mode exposure uses reg
+ 0x26 and 0x27, testing has shown 0x25 + 0x26 */
+ __u8 i2c[] = {0xc0, 0x11, 0x25, 0x00, 0x00, 0x00, 0x00, 0x17};
+ /* The HV7131D's exposure goes from 0 - 65535, we scale our
+ exposure of 0-1023 to 0-6138. There are 2 reasons for this:
+ 1) This puts our exposure knee of 200 at approx the point
+ where the framerate starts dropping
+ 2) At 6138 the framerate has already dropped to 2 fps,
+ going any lower makes little sense */
+ u16 reg = sd->ctrls[EXPOSURE].val * 6;
+
+ i2c[3] = reg >> 8;
+ i2c[4] = reg & 0xff;
+ if (i2c_w(gspca_dev, i2c) != 0)
+ goto err;
+ break;
+ }
+ case SENSOR_TAS5110C:
+ case SENSOR_TAS5110D: {
+ /* register 19's high nibble contains the sn9c10x clock divider
+ The high nibble configures the no fps according to the
+ formula: 60 / high_nibble. With a maximum of 30 fps */
+ u8 reg = sd->ctrls[EXPOSURE].val;
+
+ reg = (reg << 4) | 0x0b;
+ reg_w(gspca_dev, 0x19, &reg, 1);
+ break;
+ }
+ case SENSOR_OV6650:
+ case SENSOR_OV7630: {
+ /* The ov6650 / ov7630 have 2 registers which both influence
+ exposure, register 11, whose low nibble sets the nr off fps
+ according to: fps = 30 / (low_nibble + 1)
+
+ The fps configures the maximum exposure setting, but it is
+ possible to use less exposure then what the fps maximum
+ allows by setting register 10. register 10 configures the
+ actual exposure as quotient of the full exposure, with 0
+ being no exposure at all (not very useful) and reg10_max
+ being max exposure possible at that framerate.
+
+ The code maps our 0 - 510 ms exposure ctrl to these 2
+ registers, trying to keep fps as high as possible.
+ */
+ __u8 i2c[] = {0xb0, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10};
+ int reg10, reg11, reg10_max;
+
+ /* ov6645 datasheet says reg10_max is 9a, but that uses
+ tline * 2 * reg10 as formula for calculating texpo, the
+ ov6650 probably uses the same formula as the 7730 which uses
+ tline * 4 * reg10, which explains why the reg10max we've
+ found experimentally for the ov6650 is exactly half that of
+ the ov6645. The ov7630 datasheet says the max is 0x41. */
+ if (sd->sensor == SENSOR_OV6650) {
+ reg10_max = 0x4d;
+ i2c[4] = 0xc0; /* OV6650 needs non default vsync pol */
+ } else
+ reg10_max = 0x41;
+
+ reg11 = (15 * sd->ctrls[EXPOSURE].val + 999) / 1000;
+ if (reg11 < 1)
+ reg11 = 1;
+ else if (reg11 > 16)
+ reg11 = 16;
+
+ /* In 640x480, if the reg11 has less than 4, the image is
+ unstable (the bridge goes into a higher compression mode
+ which we have not reverse engineered yet). */
+ if (gspca_dev->width == 640 && reg11 < 4)
+ reg11 = 4;
+
+ /* frame exposure time in ms = 1000 * reg11 / 30 ->
+ reg10 = (sd->ctrls[EXPOSURE].val / 2) * reg10_max
+ / (1000 * reg11 / 30) */
+ reg10 = (sd->ctrls[EXPOSURE].val * 15 * reg10_max)
+ / (1000 * reg11);
+
+ /* Don't allow this to get below 10 when using autogain, the
+ steps become very large (relatively) when below 10 causing
+ the image to oscilate from much too dark, to much too bright
+ and back again. */
+ if (sd->ctrls[AUTOGAIN].val && reg10 < 10)
+ reg10 = 10;
+ else if (reg10 > reg10_max)
+ reg10 = reg10_max;
+
+ /* Write reg 10 and reg11 low nibble */
+ i2c[1] = sensor_data[sd->sensor].sensor_addr;
+ i2c[3] = reg10;
+ i2c[4] |= reg11 - 1;
+
+ /* If register 11 didn't change, don't change it */
+ if (sd->reg11 == reg11)
+ i2c[0] = 0xa0;
+
+ if (i2c_w(gspca_dev, i2c) == 0)
+ sd->reg11 = reg11;
+ else
+ goto err;
+ break;
+ }
+ case SENSOR_PAS202: {
+ __u8 i2cpframerate[] =
+ {0xb0, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x16};
+ __u8 i2cpexpo[] =
+ {0xa0, 0x40, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x16};
+ const __u8 i2cpdoit[] =
+ {0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16};
+ int framerate_ctrl;
+
+ /* The exposure knee for the autogain algorithm is 200
+ (100 ms / 10 fps on other sensors), for values below this
+ use the control for setting the partial frame expose time,
+ above that use variable framerate. This way we run at max
+ framerate (640x480@7.5 fps, 320x240@10fps) until the knee
+ is reached. Using the variable framerate control above 200
+ is better then playing around with both clockdiv + partial
+ frame exposure times (like we are doing with the ov chips),
+ as that sometimes leads to jumps in the exposure control,
+ which are bad for auto exposure. */
+ if (sd->ctrls[EXPOSURE].val < 200) {
+ i2cpexpo[3] = 255 - (sd->ctrls[EXPOSURE].val * 255)
+ / 200;
+ framerate_ctrl = 500;
+ } else {
+ /* The PAS202's exposure control goes from 0 - 4095,
+ but anything below 500 causes vsync issues, so scale
+ our 200-1023 to 500-4095 */
+ framerate_ctrl = (sd->ctrls[EXPOSURE].val - 200)
+ * 1000 / 229 + 500;
+ }
+
+ i2cpframerate[3] = framerate_ctrl >> 6;
+ i2cpframerate[4] = framerate_ctrl & 0x3f;
+ if (i2c_w(gspca_dev, i2cpframerate) < 0)
+ goto err;
+ if (i2c_w(gspca_dev, i2cpexpo) < 0)
+ goto err;
+ if (i2c_w(gspca_dev, i2cpdoit) < 0)
+ goto err;
+ break;
+ }
+ case SENSOR_PAS106: {
+ __u8 i2cpframerate[] =
+ {0xb1, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x14};
+ __u8 i2cpexpo[] =
+ {0xa1, 0x40, 0x05, 0x00, 0x00, 0x00, 0x00, 0x14};
+ const __u8 i2cpdoit[] =
+ {0xa1, 0x40, 0x13, 0x01, 0x00, 0x00, 0x00, 0x14};
+ int framerate_ctrl;
+
+ /* For values below 150 use partial frame exposure, above
+ that use framerate ctrl */
+ if (sd->ctrls[EXPOSURE].val < 150) {
+ i2cpexpo[3] = 150 - sd->ctrls[EXPOSURE].val;
+ framerate_ctrl = 300;
+ } else {
+ /* The PAS106's exposure control goes from 0 - 4095,
+ but anything below 300 causes vsync issues, so scale
+ our 150-1023 to 300-4095 */
+ framerate_ctrl = (sd->ctrls[EXPOSURE].val - 150)
+ * 1000 / 230 + 300;
+ }
+
+ i2cpframerate[3] = framerate_ctrl >> 4;
+ i2cpframerate[4] = framerate_ctrl & 0x0f;
+ if (i2c_w(gspca_dev, i2cpframerate) < 0)
+ goto err;
+ if (i2c_w(gspca_dev, i2cpexpo) < 0)
+ goto err;
+ if (i2c_w(gspca_dev, i2cpdoit) < 0)
+ goto err;
+ break;
+ }
+ }
+ return;
+err:
+ PDEBUG(D_ERR, "i2c error exposure");
+}
+
+static void setfreq(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->sensor) {
+ case SENSOR_OV6650:
+ case SENSOR_OV7630: {
+ /* Framerate adjust register for artificial light 50 hz flicker
+ compensation, for the ov6650 this is identical to ov6630
+ 0x2b register, see ov6630 datasheet.
+ 0x4f / 0x8a -> (30 fps -> 25 fps), 0x00 -> no adjustment */
+ __u8 i2c[] = {0xa0, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10};
+ switch (sd->ctrls[FREQ].val) {
+ default:
+/* case 0: * no filter*/
+/* case 2: * 60 hz */
+ i2c[3] = 0;
+ break;
+ case 1: /* 50 hz */
+ i2c[3] = (sd->sensor == SENSOR_OV6650)
+ ? 0x4f : 0x8a;
+ break;
+ }
+ i2c[1] = sensor_data[sd->sensor].sensor_addr;
+ if (i2c_w(gspca_dev, i2c) < 0)
+ PDEBUG(D_ERR, "i2c error setfreq");
+ break;
+ }
+ }
+}
+
+#include "autogain_functions.h"
+
+static void do_autogain(struct gspca_dev *gspca_dev)
+{
+ int deadzone, desired_avg_lum, result;
+ struct sd *sd = (struct sd *) gspca_dev;
+ int avg_lum = atomic_read(&sd->avg_lum);
+
+ if ((gspca_dev->ctrl_dis & (1 << AUTOGAIN)) ||
+ avg_lum == -1 || !sd->ctrls[AUTOGAIN].val)
+ return;
+
+ if (sd->autogain_ignore_frames > 0) {
+ sd->autogain_ignore_frames--;
+ return;
+ }
+
+ /* SIF / VGA sensors have a different autoexposure area and thus
+ different avg_lum values for the same picture brightness */
+ if (sensor_data[sd->sensor].flags & F_SIF) {
+ deadzone = 500;
+ /* SIF sensors tend to overexpose, so keep this small */
+ desired_avg_lum = 5000;
+ } else {
+ deadzone = 1500;
+ desired_avg_lum = 13000;
+ }
+
+ if (sensor_data[sd->sensor].flags & F_COARSE_EXPO)
+ result = coarse_grained_expo_autogain(gspca_dev, avg_lum,
+ sd->ctrls[BRIGHTNESS].val
+ * desired_avg_lum / 127,
+ deadzone);
+ else
+ result = auto_gain_n_exposure(gspca_dev, avg_lum,
+ sd->ctrls[BRIGHTNESS].val
+ * desired_avg_lum / 127,
+ deadzone, GAIN_KNEE, EXPOSURE_KNEE);
+
+ if (result) {
+ PDEBUG(D_FRAM, "autogain: gain changed: gain: %d expo: %d",
+ (int) sd->ctrls[GAIN].val,
+ (int) sd->ctrls[EXPOSURE].val);
+ sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
+ }
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+ reg_r(gspca_dev, 0x00);
+ if (gspca_dev->usb_buf[0] != 0x10)
+ return -ENODEV;
+
+ /* copy the webcam info from the device id */
+ sd->sensor = id->driver_info >> 8;
+ sd->bridge = id->driver_info & 0xff;
+
+ gspca_dev->ctrl_dis = sensor_data[sd->sensor].ctrl_dis;
+#if AUTOGAIN_DEF
+ if (!(gspca_dev->ctrl_dis & (1 << AUTOGAIN)))
+ gspca_dev->ctrl_inac = (1 << GAIN) | (1 << EXPOSURE);
+#endif
+
+ cam = &gspca_dev->cam;
+ cam->ctrls = sd->ctrls;
+ if (!(sensor_data[sd->sensor].flags & F_SIF)) {
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+ } else {
+ cam->cam_mode = sif_mode;
+ cam->nmodes = ARRAY_SIZE(sif_mode);
+ }
+ cam->npkt = 36; /* 36 packets per ISOC message */
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ const __u8 stop = 0x09; /* Disable stream turn of LED */
+
+ if (sensor_data[sd->sensor].flags & F_COARSE_EXPO) {
+ sd->ctrls[EXPOSURE].min = COARSE_EXPOSURE_MIN;
+ sd->ctrls[EXPOSURE].max = COARSE_EXPOSURE_MAX;
+ sd->ctrls[EXPOSURE].def = COARSE_EXPOSURE_DEF;
+ if (sd->ctrls[EXPOSURE].val > COARSE_EXPOSURE_MAX)
+ sd->ctrls[EXPOSURE].val = COARSE_EXPOSURE_DEF;
+ }
+
+ reg_w(gspca_dev, 0x01, &stop, 1);
+
+ return 0;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam = &gspca_dev->cam;
+ int i, mode;
+ __u8 regs[0x31];
+
+ mode = cam->cam_mode[gspca_dev->curr_mode].priv & 0x07;
+ /* Copy registers 0x01 - 0x19 from the template */
+ memcpy(&regs[0x01], sensor_data[sd->sensor].bridge_init, 0x19);
+ /* Set the mode */
+ regs[0x18] |= mode << 4;
+
+ /* Set bridge gain to 1.0 */
+ if (sd->bridge == BRIDGE_103) {
+ regs[0x05] = 0x20; /* Red */
+ regs[0x06] = 0x20; /* Green */
+ regs[0x07] = 0x20; /* Blue */
+ } else {
+ regs[0x10] = 0x00; /* Red and blue */
+ regs[0x11] = 0x00; /* Green */
+ }
+
+ /* Setup pixel numbers and auto exposure window */
+ if (sensor_data[sd->sensor].flags & F_SIF) {
+ regs[0x1a] = 0x14; /* HO_SIZE 640, makes no sense */
+ regs[0x1b] = 0x0a; /* VO_SIZE 320, makes no sense */
+ regs[0x1c] = 0x02; /* AE H-start 64 */
+ regs[0x1d] = 0x02; /* AE V-start 64 */
+ regs[0x1e] = 0x09; /* AE H-end 288 */
+ regs[0x1f] = 0x07; /* AE V-end 224 */
+ } else {
+ regs[0x1a] = 0x1d; /* HO_SIZE 960, makes no sense */
+ regs[0x1b] = 0x10; /* VO_SIZE 512, makes no sense */
+ regs[0x1c] = 0x05; /* AE H-start 160 */
+ regs[0x1d] = 0x03; /* AE V-start 96 */
+ regs[0x1e] = 0x0f; /* AE H-end 480 */
+ regs[0x1f] = 0x0c; /* AE V-end 384 */
+ }
+
+ /* Setup the gamma table (only used with the sn9c103 bridge) */
+ for (i = 0; i < 16; i++)
+ regs[0x20 + i] = i * 16;
+ regs[0x20 + i] = 255;
+
+ /* Special cases where some regs depend on mode or bridge */
+ switch (sd->sensor) {
+ case SENSOR_TAS5130CXX:
+ /* FIXME / TESTME
+ probably not mode specific at all most likely the upper
+ nibble of 0x19 is exposure (clock divider) just as with
+ the tas5110, we need someone to test this. */
+ regs[0x19] = mode ? 0x23 : 0x43;
+ break;
+ case SENSOR_OV7630:
+ /* FIXME / TESTME for some reason with the 101/102 bridge the
+ clock is set to 12 Mhz (reg1 == 0x04), rather then 24.
+ Also the hstart needs to go from 1 to 2 when using a 103,
+ which is likely related. This does not seem right. */
+ if (sd->bridge == BRIDGE_103) {
+ regs[0x01] = 0x44; /* Select 24 Mhz clock */
+ regs[0x12] = 0x02; /* Set hstart to 2 */
+ }
+ }
+ /* Disable compression when the raw bayer format has been selected */
+ if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_RAW)
+ regs[0x18] &= ~0x80;
+
+ /* Vga mode emulation on SIF sensor? */
+ if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_REDUCED_SIF) {
+ regs[0x12] += 16; /* hstart adjust */
+ regs[0x13] += 24; /* vstart adjust */
+ regs[0x15] = 320 / 16; /* hsize */
+ regs[0x16] = 240 / 16; /* vsize */
+ }
+
+ /* reg 0x01 bit 2 video transfert on */
+ reg_w(gspca_dev, 0x01, &regs[0x01], 1);
+ /* reg 0x17 SensorClk enable inv Clk 0x60 */
+ reg_w(gspca_dev, 0x17, &regs[0x17], 1);
+ /* Set the registers from the template */
+ reg_w(gspca_dev, 0x01, &regs[0x01],
+ (sd->bridge == BRIDGE_103) ? 0x30 : 0x1f);
+
+ /* Init the sensor */
+ i2c_w_vector(gspca_dev, sensor_data[sd->sensor].sensor_init,
+ sensor_data[sd->sensor].sensor_init_size);
+
+ /* Mode / bridge specific sensor setup */
+ switch (sd->sensor) {
+ case SENSOR_PAS202: {
+ const __u8 i2cpclockdiv[] =
+ {0xa0, 0x40, 0x02, 0x03, 0x00, 0x00, 0x00, 0x10};
+ /* clockdiv from 4 to 3 (7.5 -> 10 fps) when in low res mode */
+ if (mode)
+ i2c_w(gspca_dev, i2cpclockdiv);
+ break;
+ }
+ case SENSOR_OV7630:
+ /* FIXME / TESTME We should be able to handle this identical
+ for the 101/102 and the 103 case */
+ if (sd->bridge == BRIDGE_103) {
+ const __u8 i2c[] = { 0xa0, 0x21, 0x13,
+ 0x80, 0x00, 0x00, 0x00, 0x10 };
+ i2c_w(gspca_dev, i2c);
+ }
+ break;
+ }
+ /* H_size V_size 0x28, 0x1e -> 640x480. 0x16, 0x12 -> 352x288 */
+ reg_w(gspca_dev, 0x15, &regs[0x15], 2);
+ /* compression register */
+ reg_w(gspca_dev, 0x18, &regs[0x18], 1);
+ /* H_start */
+ reg_w(gspca_dev, 0x12, &regs[0x12], 1);
+ /* V_START */
+ reg_w(gspca_dev, 0x13, &regs[0x13], 1);
+ /* reset 0x17 SensorClk enable inv Clk 0x60 */
+ /*fixme: ov7630 [17]=68 8f (+20 if 102)*/
+ reg_w(gspca_dev, 0x17, &regs[0x17], 1);
+ /*MCKSIZE ->3 */ /*fixme: not ov7630*/
+ reg_w(gspca_dev, 0x19, &regs[0x19], 1);
+ /* AE_STRX AE_STRY AE_ENDX AE_ENDY */
+ reg_w(gspca_dev, 0x1c, &regs[0x1c], 4);
+ /* Enable video transfert */
+ reg_w(gspca_dev, 0x01, &regs[0x01], 1);
+ /* Compression */
+ reg_w(gspca_dev, 0x18, &regs[0x18], 2);
+ msleep(20);
+
+ sd->reg11 = -1;
+
+ setgain(gspca_dev);
+ setbrightness(gspca_dev);
+ setexposure(gspca_dev);
+ setfreq(gspca_dev);
+
+ sd->frames_to_drop = 0;
+ sd->autogain_ignore_frames = 0;
+ sd->exp_too_high_cnt = 0;
+ sd->exp_too_low_cnt = 0;
+ atomic_set(&sd->avg_lum, -1);
+ return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ sd_init(gspca_dev);
+}
+
+static u8* find_sof(struct gspca_dev *gspca_dev, u8 *data, int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, header_size = (sd->bridge == BRIDGE_103) ? 18 : 12;
+
+ /* frames start with:
+ * ff ff 00 c4 c4 96 synchro
+ * 00 (unknown)
+ * xx (frame sequence / size / compression)
+ * (xx) (idem - extra byte for sn9c103)
+ * ll mm brightness sum inside auto exposure
+ * ll mm brightness sum outside auto exposure
+ * (xx xx xx xx xx) audio values for snc103
+ */
+ for (i = 0; i < len; i++) {
+ switch (sd->header_read) {
+ case 0:
+ if (data[i] == 0xff)
+ sd->header_read++;
+ break;
+ case 1:
+ if (data[i] == 0xff)
+ sd->header_read++;
+ else
+ sd->header_read = 0;
+ break;
+ case 2:
+ if (data[i] == 0x00)
+ sd->header_read++;
+ else if (data[i] != 0xff)
+ sd->header_read = 0;
+ break;
+ case 3:
+ if (data[i] == 0xc4)
+ sd->header_read++;
+ else if (data[i] == 0xff)
+ sd->header_read = 1;
+ else
+ sd->header_read = 0;
+ break;
+ case 4:
+ if (data[i] == 0xc4)
+ sd->header_read++;
+ else if (data[i] == 0xff)
+ sd->header_read = 1;
+ else
+ sd->header_read = 0;
+ break;
+ case 5:
+ if (data[i] == 0x96)
+ sd->header_read++;
+ else if (data[i] == 0xff)
+ sd->header_read = 1;
+ else
+ sd->header_read = 0;
+ break;
+ default:
+ sd->header[sd->header_read - 6] = data[i];
+ sd->header_read++;
+ if (sd->header_read == header_size) {
+ sd->header_read = 0;
+ return data + i + 1;
+ }
+ }
+ }
+ return NULL;
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ int fr_h_sz = 0, lum_offset = 0, len_after_sof = 0;
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam = &gspca_dev->cam;
+ u8 *sof;
+
+ sof = find_sof(gspca_dev, data, len);
+ if (sof) {
+ if (sd->bridge == BRIDGE_103) {
+ fr_h_sz = 18;
+ lum_offset = 3;
+ } else {
+ fr_h_sz = 12;
+ lum_offset = 2;
+ }
+
+ len_after_sof = len - (sof - data);
+ len = (sof - data) - fr_h_sz;
+ if (len < 0)
+ len = 0;
+ }
+
+ if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_RAW) {
+ /* In raw mode we sometimes get some garbage after the frame
+ ignore this */
+ int used;
+ int size = cam->cam_mode[gspca_dev->curr_mode].sizeimage;
+
+ used = gspca_dev->image_len;
+ if (used + len > size)
+ len = size - used;
+ }
+
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+
+ if (sof) {
+ int lum = sd->header[lum_offset] +
+ (sd->header[lum_offset + 1] << 8);
+
+ /* When exposure changes midway a frame we
+ get a lum of 0 in this case drop 2 frames
+ as the frames directly after an exposure
+ change have an unstable image. Sometimes lum
+ *really* is 0 (cam used in low light with
+ low exposure setting), so do not drop frames
+ if the previous lum was 0 too. */
+ if (lum == 0 && sd->prev_avg_lum != 0) {
+ lum = -1;
+ sd->frames_to_drop = 2;
+ sd->prev_avg_lum = 0;
+ } else
+ sd->prev_avg_lum = lum;
+ atomic_set(&sd->avg_lum, lum);
+
+ if (sd->frames_to_drop)
+ sd->frames_to_drop--;
+ else
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+
+ gspca_frame_add(gspca_dev, FIRST_PACKET, sof, len_after_sof);
+ }
+}
+
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->ctrls[AUTOGAIN].val = val;
+ sd->exp_too_high_cnt = 0;
+ sd->exp_too_low_cnt = 0;
+
+ /* when switching to autogain set defaults to make sure
+ we are on a valid point of the autogain gain /
+ exposure knee graph, and give this change time to
+ take effect before doing autogain. */
+ if (sd->ctrls[AUTOGAIN].val
+ && !(sensor_data[sd->sensor].flags & F_COARSE_EXPO)) {
+ sd->ctrls[EXPOSURE].val = sd->ctrls[EXPOSURE].def;
+ sd->ctrls[GAIN].val = sd->ctrls[GAIN].def;
+ if (gspca_dev->streaming) {
+ sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
+ setexposure(gspca_dev);
+ setgain(gspca_dev);
+ }
+ }
+
+ if (sd->ctrls[AUTOGAIN].val)
+ gspca_dev->ctrl_inac = (1 << GAIN) | (1 << EXPOSURE);
+ else
+ gspca_dev->ctrl_inac = 0;
+
+ return 0;
+}
+
+static int sd_querymenu(struct gspca_dev *gspca_dev,
+ struct v4l2_querymenu *menu)
+{
+ switch (menu->id) {
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ switch (menu->index) {
+ case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
+ strcpy((char *) menu->name, "NoFliker");
+ return 0;
+ case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
+ strcpy((char *) menu->name, "50 Hz");
+ return 0;
+ case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
+ strcpy((char *) menu->name, "60 Hz");
+ return 0;
+ }
+ break;
+ }
+ return -EINVAL;
+}
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* interrupt packet data */
+ int len) /* interrupt packet length */
+{
+ int ret = -EINVAL;
+
+ if (len == 1 && data[0] == 1) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+ input_sync(gspca_dev->input_dev);
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ ret = 0;
+ }
+
+ return ret;
+}
+#endif
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+ .querymenu = sd_querymenu,
+ .dq_callback = do_autogain,
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ .int_pkt_scan = sd_int_pkt_scan,
+#endif
+};
+
+/* -- module initialisation -- */
+#define SB(sensor, bridge) \
+ .driver_info = (SENSOR_ ## sensor << 8) | BRIDGE_ ## bridge
+
+
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x0c45, 0x6001), SB(TAS5110C, 102)}, /* TAS5110C1B */
+ {USB_DEVICE(0x0c45, 0x6005), SB(TAS5110C, 101)}, /* TAS5110C1B */
+ {USB_DEVICE(0x0c45, 0x6007), SB(TAS5110D, 101)}, /* TAS5110D */
+ {USB_DEVICE(0x0c45, 0x6009), SB(PAS106, 101)},
+ {USB_DEVICE(0x0c45, 0x600d), SB(PAS106, 101)},
+ {USB_DEVICE(0x0c45, 0x6011), SB(OV6650, 101)},
+ {USB_DEVICE(0x0c45, 0x6019), SB(OV7630, 101)},
+#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
+ {USB_DEVICE(0x0c45, 0x6024), SB(TAS5130CXX, 102)},
+ {USB_DEVICE(0x0c45, 0x6025), SB(TAS5130CXX, 102)},
+#endif
+ {USB_DEVICE(0x0c45, 0x6028), SB(PAS202, 102)},
+ {USB_DEVICE(0x0c45, 0x6029), SB(PAS106, 102)},
+ {USB_DEVICE(0x0c45, 0x602a), SB(HV7131D, 102)},
+ /* {USB_DEVICE(0x0c45, 0x602b), SB(MI0343, 102)}, */
+ {USB_DEVICE(0x0c45, 0x602c), SB(OV7630, 102)},
+ {USB_DEVICE(0x0c45, 0x602d), SB(HV7131R, 102)},
+ {USB_DEVICE(0x0c45, 0x602e), SB(OV7630, 102)},
+ /* {USB_DEVICE(0x0c45, 0x6030), SB(MI03XX, 102)}, */ /* MI0343 MI0360 MI0330 */
+ /* {USB_DEVICE(0x0c45, 0x6082), SB(MI03XX, 103)}, */ /* MI0343 MI0360 */
+ {USB_DEVICE(0x0c45, 0x6083), SB(HV7131D, 103)},
+ {USB_DEVICE(0x0c45, 0x608c), SB(HV7131R, 103)},
+ /* {USB_DEVICE(0x0c45, 0x608e), SB(CISVF10, 103)}, */
+ {USB_DEVICE(0x0c45, 0x608f), SB(OV7630, 103)},
+ {USB_DEVICE(0x0c45, 0x60a8), SB(PAS106, 103)},
+ {USB_DEVICE(0x0c45, 0x60aa), SB(TAS5130CXX, 103)},
+ {USB_DEVICE(0x0c45, 0x60af), SB(PAS202, 103)},
+ {USB_DEVICE(0x0c45, 0x60b0), SB(OV7630, 103)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c
new file mode 100644
index 00000000..863c755d
--- /dev/null
+++ b/drivers/media/video/gspca/sonixj.c
@@ -0,0 +1,3208 @@
+/*
+ * Sonix sn9c102p sn9c105 sn9c120 (jpeg) subdriver
+ *
+ * Copyright (C) 2009-2011 Jean-François Moine <http://moinejf.free.fr>
+ * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "sonixj"
+
+#include <linux/input.h>
+#include "gspca.h"
+#include "jpeg.h"
+
+MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>");
+MODULE_DESCRIPTION("GSPCA/SONIX JPEG USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* controls */
+enum e_ctrl {
+ BRIGHTNESS,
+ CONTRAST,
+ COLORS,
+ BLUE,
+ RED,
+ GAMMA,
+ EXPOSURE,
+ AUTOGAIN,
+ GAIN,
+ HFLIP,
+ VFLIP,
+ SHARPNESS,
+ ILLUM,
+ FREQ,
+ NCTRLS /* number of controls */
+};
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ struct gspca_ctrl ctrls[NCTRLS];
+
+ atomic_t avg_lum;
+ u32 exposure;
+
+ struct work_struct work;
+ struct workqueue_struct *work_thread;
+
+ u32 pktsz; /* (used by pkt_scan) */
+ u16 npkt;
+ s8 nchg;
+ s8 short_mark;
+
+ u8 quality; /* image quality */
+#define QUALITY_MIN 25
+#define QUALITY_MAX 90
+#define QUALITY_DEF 70
+
+ u8 reg01;
+ u8 reg17;
+ u8 reg18;
+ u8 flags;
+
+ s8 ag_cnt;
+#define AG_CNT_START 13
+
+ u8 bridge;
+#define BRIDGE_SN9C102P 0
+#define BRIDGE_SN9C105 1
+#define BRIDGE_SN9C110 2
+#define BRIDGE_SN9C120 3
+ u8 sensor; /* Type of image sensor chip */
+ u8 i2c_addr;
+
+ u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+enum sensors {
+ SENSOR_ADCM1700,
+ SENSOR_GC0307,
+ SENSOR_HV7131R,
+ SENSOR_MI0360,
+ SENSOR_MI0360B,
+ SENSOR_MO4000,
+ SENSOR_MT9V111,
+ SENSOR_OM6802,
+ SENSOR_OV7630,
+ SENSOR_OV7648,
+ SENSOR_OV7660,
+ SENSOR_PO1030,
+ SENSOR_PO2030N,
+ SENSOR_SOI768,
+ SENSOR_SP80708,
+};
+
+static void qual_upd(struct work_struct *work);
+
+/* device flags */
+#define F_PDN_INV 0x01 /* inverse pin S_PWR_DN / sn_xxx tables */
+#define F_ILLUM 0x02 /* presence of illuminator */
+
+/* sn9c1xx definitions */
+/* register 0x01 */
+#define S_PWR_DN 0x01 /* sensor power down */
+#define S_PDN_INV 0x02 /* inverse pin S_PWR_DN */
+#define V_TX_EN 0x04 /* video transfer enable */
+#define LED 0x08 /* output to pin LED */
+#define SCL_SEL_OD 0x20 /* open-drain mode */
+#define SYS_SEL_48M 0x40 /* system clock 0: 24MHz, 1: 48MHz */
+/* register 0x17 */
+#define MCK_SIZE_MASK 0x1f /* sensor master clock */
+#define SEN_CLK_EN 0x20 /* enable sensor clock */
+#define DEF_EN 0x80 /* defect pixel by 0: soft, 1: hard */
+
+/* V4L2 controls supported by the driver */
+static void setbrightness(struct gspca_dev *gspca_dev);
+static void setcontrast(struct gspca_dev *gspca_dev);
+static void setcolors(struct gspca_dev *gspca_dev);
+static void setredblue(struct gspca_dev *gspca_dev);
+static void setgamma(struct gspca_dev *gspca_dev);
+static void setexposure(struct gspca_dev *gspca_dev);
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static void setgain(struct gspca_dev *gspca_dev);
+static void sethvflip(struct gspca_dev *gspca_dev);
+static void setsharpness(struct gspca_dev *gspca_dev);
+static void setillum(struct gspca_dev *gspca_dev);
+static void setfreq(struct gspca_dev *gspca_dev);
+
+static const struct ctrl sd_ctrls[NCTRLS] = {
+[BRIGHTNESS] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x80,
+ },
+ .set_control = setbrightness
+ },
+[CONTRAST] = {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+#define CONTRAST_MAX 127
+ .maximum = CONTRAST_MAX,
+ .step = 1,
+ .default_value = 20,
+ },
+ .set_control = setcontrast
+ },
+[COLORS] = {
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 40,
+ .step = 1,
+#define COLORS_DEF 25
+ .default_value = COLORS_DEF,
+ },
+ .set_control = setcolors
+ },
+[BLUE] = {
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Balance",
+ .minimum = 24,
+ .maximum = 40,
+ .step = 1,
+ .default_value = 32,
+ },
+ .set_control = setredblue
+ },
+[RED] = {
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Balance",
+ .minimum = 24,
+ .maximum = 40,
+ .step = 1,
+ .default_value = 32,
+ },
+ .set_control = setredblue
+ },
+[GAMMA] = {
+ {
+ .id = V4L2_CID_GAMMA,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gamma",
+ .minimum = 0,
+ .maximum = 40,
+ .step = 1,
+#define GAMMA_DEF 20
+ .default_value = GAMMA_DEF,
+ },
+ .set_control = setgamma
+ },
+[EXPOSURE] = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 500,
+ .maximum = 1500,
+ .step = 1,
+ .default_value = 1024
+ },
+ .set_control = setexposure
+ },
+[AUTOGAIN] = {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto Gain",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1
+ },
+ .set = sd_setautogain,
+ },
+[GAIN] = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 4,
+ .maximum = 49,
+ .step = 1,
+ .default_value = 15
+ },
+ .set_control = setgain
+ },
+[HFLIP] = {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mirror",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set_control = sethvflip
+ },
+[VFLIP] = {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Vflip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set_control = sethvflip
+ },
+[SHARPNESS] = {
+ {
+ .id = V4L2_CID_SHARPNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Sharpness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 90,
+ },
+ .set_control = setsharpness
+ },
+[ILLUM] = {
+ {
+ .id = V4L2_CID_ILLUMINATORS_1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Illuminator / infrared",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set_control = setillum
+ },
+/* ov7630/ov7648/ov7660 only */
+[FREQ] = {
+ {
+ .id = V4L2_CID_POWER_LINE_FREQUENCY,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Light frequency filter",
+ .minimum = 0,
+ .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */
+ .step = 1,
+ .default_value = 1,
+ },
+ .set_control = setfreq
+ },
+};
+
+/* table of the disabled controls */
+static const __u32 ctrl_dis[] = {
+[SENSOR_ADCM1700] = (1 << EXPOSURE) |
+ (1 << AUTOGAIN) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << FREQ),
+
+[SENSOR_GC0307] = (1 << EXPOSURE) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << FREQ),
+
+[SENSOR_HV7131R] = (1 << EXPOSURE) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
+ (1 << FREQ),
+
+[SENSOR_MI0360] = (1 << EXPOSURE) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << FREQ),
+
+[SENSOR_MI0360B] = (1 << EXPOSURE) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << FREQ),
+
+[SENSOR_MO4000] = (1 << EXPOSURE) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << FREQ),
+
+[SENSOR_MT9V111] = (1 << EXPOSURE) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << FREQ),
+
+[SENSOR_OM6802] = (1 << EXPOSURE) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << FREQ),
+
+[SENSOR_OV7630] = (1 << EXPOSURE) |
+ (1 << GAIN) |
+ (1 << HFLIP),
+
+[SENSOR_OV7648] = (1 << EXPOSURE) |
+ (1 << GAIN) |
+ (1 << HFLIP),
+
+[SENSOR_OV7660] = (1 << EXPOSURE) |
+ (1 << AUTOGAIN) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
+ (1 << VFLIP),
+
+[SENSOR_PO1030] = (1 << EXPOSURE) |
+ (1 << AUTOGAIN) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << FREQ),
+
+[SENSOR_PO2030N] = (1 << FREQ),
+
+[SENSOR_SOI768] = (1 << EXPOSURE) |
+ (1 << AUTOGAIN) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << FREQ),
+
+[SENSOR_SP80708] = (1 << EXPOSURE) |
+ (1 << AUTOGAIN) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << FREQ),
+};
+
+static const struct v4l2_pix_format cif_mode[] = {
+ {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288 * 4 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+static const struct v4l2_pix_format vga_mode[] = {
+ {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120 * 4 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 2},
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ /* Note 3 / 8 is not large enough, not even 5 / 8 is ?! */
+ .sizeimage = 640 * 480 * 3 / 4 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
+static const u8 sn_adcm1700[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x43, 0x60, 0x00, 0x1a, 0x00, 0x00, 0x00,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x80, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x05, 0x01, 0x05, 0x16, 0x12, 0x42,
+/* reg18 reg19 reg1a reg1b */
+ 0x06, 0x00, 0x00, 0x00
+};
+
+static const u8 sn_gc0307[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x61, 0x62, 0x00, 0x1a, 0x00, 0x00, 0x00,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x80, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x03, 0x01, 0x08, 0x28, 0x1e, 0x02,
+/* reg18 reg19 reg1a reg1b */
+ 0x06, 0x00, 0x00, 0x00
+};
+
+static const u8 sn_hv7131[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x03, 0x60, 0x00, 0x1a, 0x20, 0x20, 0x20,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x81, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x00, 0x01, 0x03, 0x28, 0x1e, 0x41,
+/* reg18 reg19 reg1a reg1b */
+ 0x0a, 0x00, 0x00, 0x00
+};
+
+static const u8 sn_mi0360[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x63, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x81, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x00, 0x02, 0x0a, 0x28, 0x1e, 0x61,
+/* reg18 reg19 reg1a reg1b */
+ 0x06, 0x00, 0x00, 0x00
+};
+
+static const u8 sn_mi0360b[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x61, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x81, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x00, 0x02, 0x0a, 0x28, 0x1e, 0x40,
+/* reg18 reg19 reg1a reg1b */
+ 0x06, 0x00, 0x00, 0x00
+};
+
+static const u8 sn_mo4000[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x23, 0x60, 0x00, 0x1a, 0x00, 0x20, 0x18,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x0b, 0x0f, 0x14, 0x28, 0x1e, 0x40,
+/* reg18 reg19 reg1a reg1b */
+ 0x08, 0x00, 0x00, 0x00
+};
+
+static const u8 sn_mt9v111[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x61, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x81, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x00, 0x02, 0x1c, 0x28, 0x1e, 0x40,
+/* reg18 reg19 reg1a reg1b */
+ 0x06, 0x00, 0x00, 0x00
+};
+
+static const u8 sn_om6802[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x23, 0x72, 0x00, 0x1a, 0x20, 0x20, 0x19,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x80, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x51, 0x01, 0x00, 0x28, 0x1e, 0x40,
+/* reg18 reg19 reg1a reg1b */
+ 0x05, 0x00, 0x00, 0x00
+};
+
+static const u8 sn_ov7630[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x21, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x04, 0x01, 0x0a, 0x28, 0x1e, 0xc2,
+/* reg18 reg19 reg1a reg1b */
+ 0x0b, 0x00, 0x00, 0x00
+};
+
+static const u8 sn_ov7648[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x63, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x00, 0x01, 0x00, 0x28, 0x1e, 0x00,
+/* reg18 reg19 reg1a reg1b */
+ 0x0b, 0x00, 0x00, 0x00
+};
+
+static const u8 sn_ov7660[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x61, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x01, 0x01, 0x08, 0x28, 0x1e, 0x20,
+/* reg18 reg19 reg1a reg1b */
+ 0x07, 0x00, 0x00, 0x00
+};
+
+static const u8 sn_po1030[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x21, 0x62, 0x00, 0x1a, 0x20, 0x20, 0x20,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x81, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x00, 0x06, 0x06, 0x28, 0x1e, 0x00,
+/* reg18 reg19 reg1a reg1b */
+ 0x07, 0x00, 0x00, 0x00
+};
+
+static const u8 sn_po2030n[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x63, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x81, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x00, 0x01, 0x14, 0x28, 0x1e, 0x00,
+/* reg18 reg19 reg1a reg1b */
+ 0x07, 0x00, 0x00, 0x00
+};
+
+static const u8 sn_soi768[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x21, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x00, 0x01, 0x08, 0x28, 0x1e, 0x00,
+/* reg18 reg19 reg1a reg1b */
+ 0x07, 0x00, 0x00, 0x00
+};
+
+static const u8 sn_sp80708[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x63, 0x60, 0x00, 0x1a, 0x20, 0x20, 0x20,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x81, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x00, 0x03, 0x04, 0x28, 0x1e, 0x00,
+/* reg18 reg19 reg1a reg1b */
+ 0x07, 0x00, 0x00, 0x00
+};
+
+/* sequence specific to the sensors - !! index = SENSOR_xxx */
+static const u8 *sn_tb[] = {
+[SENSOR_ADCM1700] = sn_adcm1700,
+[SENSOR_GC0307] = sn_gc0307,
+[SENSOR_HV7131R] = sn_hv7131,
+[SENSOR_MI0360] = sn_mi0360,
+[SENSOR_MI0360B] = sn_mi0360b,
+[SENSOR_MO4000] = sn_mo4000,
+[SENSOR_MT9V111] = sn_mt9v111,
+[SENSOR_OM6802] = sn_om6802,
+[SENSOR_OV7630] = sn_ov7630,
+[SENSOR_OV7648] = sn_ov7648,
+[SENSOR_OV7660] = sn_ov7660,
+[SENSOR_PO1030] = sn_po1030,
+[SENSOR_PO2030N] = sn_po2030n,
+[SENSOR_SOI768] = sn_soi768,
+[SENSOR_SP80708] = sn_sp80708,
+};
+
+/* default gamma table */
+static const u8 gamma_def[17] = {
+ 0x00, 0x2d, 0x46, 0x5a, 0x6c, 0x7c, 0x8b, 0x99,
+ 0xa6, 0xb2, 0xbf, 0xca, 0xd5, 0xe0, 0xeb, 0xf5, 0xff
+};
+/* gamma for sensor ADCM1700 */
+static const u8 gamma_spec_0[17] = {
+ 0x0f, 0x39, 0x5a, 0x74, 0x86, 0x95, 0xa6, 0xb4,
+ 0xbd, 0xc4, 0xcc, 0xd4, 0xd5, 0xde, 0xe4, 0xed, 0xf5
+};
+/* gamma for sensors HV7131R and MT9V111 */
+static const u8 gamma_spec_1[17] = {
+ 0x08, 0x3a, 0x52, 0x65, 0x75, 0x83, 0x91, 0x9d,
+ 0xa9, 0xb4, 0xbe, 0xc8, 0xd2, 0xdb, 0xe4, 0xed, 0xf5
+};
+/* gamma for sensor GC0307 */
+static const u8 gamma_spec_2[17] = {
+ 0x14, 0x37, 0x50, 0x6a, 0x7c, 0x8d, 0x9d, 0xab,
+ 0xb5, 0xbf, 0xc2, 0xcb, 0xd1, 0xd6, 0xdb, 0xe1, 0xeb
+};
+/* gamma for sensor SP80708 */
+static const u8 gamma_spec_3[17] = {
+ 0x0a, 0x2d, 0x4e, 0x68, 0x7d, 0x8f, 0x9f, 0xab,
+ 0xb7, 0xc2, 0xcc, 0xd3, 0xd8, 0xde, 0xe2, 0xe5, 0xe6
+};
+
+/* color matrix and offsets */
+static const u8 reg84[] = {
+ 0x14, 0x00, 0x27, 0x00, 0x07, 0x00, /* YR YG YB gains */
+ 0xe8, 0x0f, 0xda, 0x0f, 0x40, 0x00, /* UR UG UB */
+ 0x3e, 0x00, 0xcd, 0x0f, 0xf7, 0x0f, /* VR VG VB */
+ 0x00, 0x00, 0x00 /* YUV offsets */
+};
+
+#define DELAY 0xdd
+
+static const u8 adcm1700_sensor_init[][8] = {
+ {0xa0, 0x51, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x04, 0x08, 0x00, 0x00, 0x00, 0x10}, /* reset */
+ {DELAY, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xb0, 0x51, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {DELAY, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xb0, 0x51, 0x0c, 0xe0, 0x2e, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x10, 0x02, 0x02, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x14, 0x0e, 0x0e, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x1c, 0x00, 0x80, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x20, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {DELAY, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xb0, 0x51, 0x04, 0x04, 0x00, 0x00, 0x00, 0x10},
+ {DELAY, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xb0, 0x51, 0x04, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x51, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x14, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+static const u8 adcm1700_sensor_param1[][8] = {
+ {0xb0, 0x51, 0x26, 0xf9, 0x01, 0x00, 0x00, 0x10}, /* exposure? */
+ {0xd0, 0x51, 0x1e, 0x8e, 0x8e, 0x8e, 0x8e, 0x10},
+
+ {0xa0, 0x51, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x51, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x32, 0x00, 0x72, 0x00, 0x00, 0x10},
+ {0xd0, 0x51, 0x1e, 0xbe, 0xd7, 0xe8, 0xbe, 0x10}, /* exposure? */
+
+ {0xa0, 0x51, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x51, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x32, 0x00, 0xa2, 0x00, 0x00, 0x10},
+ {}
+};
+static const u8 gc0307_sensor_init[][8] = {
+ {0xa0, 0x21, 0x43, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x44, 0xa2, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x01, 0x6a, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x02, 0x70, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x11, 0x05, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x08, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x09, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x0a, 0xe8, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x0b, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x0c, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x0d, 0x22, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x0e, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x0f, 0xb2, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x12, 0x70, 0x00, 0x00, 0x00, 0x10},
+ {DELAY, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*delay 10ms*/
+ {0xa0, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x15, 0xb8, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x16, 0x13, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x17, 0x52, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x18, 0x50, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x1e, 0x0d, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x1f, 0x32, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x61, 0x90, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x63, 0x70, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x65, 0x98, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x67, 0x90, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x04, 0x96, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x45, 0x27, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x47, 0x2c, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x43, 0x47, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x44, 0xd8, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+static const u8 gc0307_sensor_param1[][8] = {
+ {0xa0, 0x21, 0x68, 0x13, 0x00, 0x00, 0x00, 0x10},
+ {0xd0, 0x21, 0x61, 0x80, 0x00, 0x80, 0x00, 0x10},
+ {0xc0, 0x21, 0x65, 0x80, 0x00, 0x80, 0x00, 0x10},
+ {0xc0, 0x21, 0x63, 0xa0, 0x00, 0xa6, 0x00, 0x10},
+/*param3*/
+ {0xa0, 0x21, 0x01, 0x6e, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x02, 0x88, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+
+static const u8 hv7131r_sensor_init[][8] = {
+ {0xc1, 0x11, 0x01, 0x08, 0x01, 0x00, 0x00, 0x10},
+ {0xb1, 0x11, 0x34, 0x17, 0x7f, 0x00, 0x00, 0x10},
+ {0xd1, 0x11, 0x40, 0xff, 0x7f, 0x7f, 0x7f, 0x10},
+/* {0x91, 0x11, 0x44, 0x00, 0x00, 0x00, 0x00, 0x10}, */
+ {0xd1, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x11, 0x14, 0x01, 0xe2, 0x02, 0x82, 0x10},
+/* {0x91, 0x11, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10}, */
+
+ {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
+ {0xc1, 0x11, 0x25, 0x00, 0x61, 0xa8, 0x00, 0x10},
+ {0xa1, 0x11, 0x30, 0x22, 0x00, 0x00, 0x00, 0x10},
+ {0xc1, 0x11, 0x31, 0x20, 0x2e, 0x20, 0x00, 0x10},
+ {0xc1, 0x11, 0x25, 0x00, 0xc3, 0x50, 0x00, 0x10},
+ {0xa1, 0x11, 0x30, 0x07, 0x00, 0x00, 0x00, 0x10}, /* gain14 */
+ {0xc1, 0x11, 0x31, 0x10, 0x10, 0x10, 0x00, 0x10}, /* r g b 101a10 */
+
+ {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x11, 0x21, 0xd0, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x11, 0x23, 0x09, 0x00, 0x00, 0x00, 0x10},
+
+ {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x11, 0x21, 0xd0, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x11, 0x23, 0x10, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x11, 0x01, 0x18, 0x00, 0x00, 0x00, 0x10},
+ /* set sensor clock */
+ {}
+};
+static const u8 mi0360_sensor_init[][8] = {
+ {0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x01, 0x00, 0x08, 0x00, 0x16, 0x10},
+ {0xd1, 0x5d, 0x03, 0x01, 0xe2, 0x02, 0x82, 0x10},
+ {0xd1, 0x5d, 0x05, 0x00, 0x09, 0x00, 0x53, 0x10},
+ {0xb1, 0x5d, 0x0d, 0x00, 0x02, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x14, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x24, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x26, 0x00, 0x00, 0x00, 0x24, 0x10},
+ {0xd1, 0x5d, 0x2f, 0xf7, 0xb0, 0x00, 0x04, 0x10},
+ {0xd1, 0x5d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x33, 0x00, 0x00, 0x01, 0x00, 0x10},
+ {0xb1, 0x5d, 0x3d, 0x06, 0x8f, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x40, 0x01, 0xe0, 0x00, 0xd1, 0x10},
+ {0xb1, 0x5d, 0x44, 0x00, 0x82, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x58, 0x00, 0x78, 0x00, 0x43, 0x10},
+ {0xd1, 0x5d, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x5e, 0x00, 0x00, 0xa3, 0x1d, 0x10},
+ {0xb1, 0x5d, 0x62, 0x04, 0x11, 0x00, 0x00, 0x10},
+
+ {0xb1, 0x5d, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x09, 0x00, 0x64, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x2b, 0x00, 0xa0, 0x00, 0xb0, 0x10},
+ {0xd1, 0x5d, 0x2d, 0x00, 0xa0, 0x00, 0xa0, 0x10},
+
+ {0xb1, 0x5d, 0x0a, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor clck ?2 */
+ {0xb1, 0x5d, 0x06, 0x00, 0x30, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x09, 0x02, 0x35, 0x00, 0x00, 0x10}, /* exposure 2 */
+
+ {0xd1, 0x5d, 0x2b, 0x00, 0xb9, 0x00, 0xe3, 0x10},
+ {0xd1, 0x5d, 0x2d, 0x00, 0x5f, 0x00, 0xb9, 0x10}, /* 42 */
+/* {0xb1, 0x5d, 0x35, 0x00, 0x67, 0x00, 0x00, 0x10}, * gain orig */
+/* {0xb1, 0x5d, 0x35, 0x00, 0x20, 0x00, 0x00, 0x10}, * gain */
+ {0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10}, /* update */
+ {0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor on */
+ {}
+};
+static const u8 mi0360b_sensor_init[][8] = {
+ {0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10},
+ {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*delay 20ms*/
+ {0xb1, 0x5d, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*delay 20ms*/
+ {0xd1, 0x5d, 0x01, 0x00, 0x08, 0x00, 0x16, 0x10},
+ {0xd1, 0x5d, 0x03, 0x01, 0xe2, 0x02, 0x82, 0x10},
+ {0xd1, 0x5d, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x0d, 0x00, 0x02, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x14, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x24, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x26, 0x00, 0x00, 0x00, 0x24, 0x10},
+ {0xd1, 0x5d, 0x2f, 0xf7, 0xb0, 0x00, 0x04, 0x10},
+ {0xd1, 0x5d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x33, 0x00, 0x00, 0x01, 0x00, 0x10},
+ {0xb1, 0x5d, 0x3d, 0x06, 0x8f, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x40, 0x01, 0xe0, 0x00, 0xd1, 0x10},
+ {0xb1, 0x5d, 0x44, 0x00, 0x82, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x58, 0x00, 0x78, 0x00, 0x43, 0x10},
+ {0xd1, 0x5d, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x5e, 0x00, 0x00, 0xa3, 0x1d, 0x10},
+ {0xb1, 0x5d, 0x62, 0x04, 0x11, 0x00, 0x00, 0x10},
+
+ {0xb1, 0x5d, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x09, 0x00, 0x64, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x2b, 0x00, 0x33, 0x00, 0xa0, 0x10},
+ {0xd1, 0x5d, 0x2d, 0x00, 0xa0, 0x00, 0x33, 0x10},
+ {}
+};
+static const u8 mi0360b_sensor_param1[][8] = {
+ {0xb1, 0x5d, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x06, 0x00, 0x53, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x05, 0x00, 0x09, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x09, 0x02, 0x35, 0x00, 0x00, 0x10}, /* exposure 2 */
+
+ {0xd1, 0x5d, 0x2b, 0x00, 0xd1, 0x01, 0xc9, 0x10},
+ {0xd1, 0x5d, 0x2d, 0x00, 0xed, 0x00, 0xd1, 0x10},
+ {0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10}, /* update */
+ {0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor on */
+ {}
+};
+static const u8 mo4000_sensor_init[][8] = {
+ {0xa1, 0x21, 0x01, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x05, 0x04, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x06, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x06, 0x81, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x11, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x11, 0x20, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x11, 0x30, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x0f, 0x20, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+static const u8 mt9v111_sensor_init[][8] = {
+ {0xb1, 0x5c, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10}, /* reset? */
+ {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+ {0xb1, 0x5c, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x5c, 0x01, 0x00, 0x01, 0x00, 0x00, 0x10}, /* IFP select */
+ {0xb1, 0x5c, 0x08, 0x04, 0x80, 0x00, 0x00, 0x10}, /* output fmt ctrl */
+ {0xb1, 0x5c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10}, /* op mode ctrl */
+ {0xb1, 0x5c, 0x01, 0x00, 0x04, 0x00, 0x00, 0x10}, /* sensor select */
+ {0xb1, 0x5c, 0x08, 0x00, 0x08, 0x00, 0x00, 0x10}, /* row start */
+ {0xb1, 0x5c, 0x02, 0x00, 0x16, 0x00, 0x00, 0x10}, /* col start */
+ {0xb1, 0x5c, 0x03, 0x01, 0xe7, 0x00, 0x00, 0x10}, /* window height */
+ {0xb1, 0x5c, 0x04, 0x02, 0x87, 0x00, 0x00, 0x10}, /* window width */
+ {0xb1, 0x5c, 0x07, 0x30, 0x02, 0x00, 0x00, 0x10}, /* output ctrl */
+ {0xb1, 0x5c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10}, /* shutter delay */
+ {0xb1, 0x5c, 0x12, 0x00, 0xb0, 0x00, 0x00, 0x10}, /* zoom col start */
+ {0xb1, 0x5c, 0x13, 0x00, 0x7c, 0x00, 0x00, 0x10}, /* zoom row start */
+ {0xb1, 0x5c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x10}, /* digital zoom */
+ {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, /* read mode */
+ {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+static const u8 mt9v111_sensor_param1[][8] = {
+ {0xd1, 0x5c, 0x2b, 0x00, 0x33, 0x00, 0xad, 0x10}, /* G1 and B gains */
+ {0xd1, 0x5c, 0x2d, 0x00, 0xad, 0x00, 0x33, 0x10}, /* R and G2 gains */
+ {0xb1, 0x5c, 0x06, 0x00, 0x40, 0x00, 0x00, 0x10}, /* vert blanking */
+ {0xb1, 0x5c, 0x05, 0x00, 0x09, 0x00, 0x00, 0x10}, /* horiz blanking */
+ {0xb1, 0x5c, 0x35, 0x01, 0xc0, 0x00, 0x00, 0x10}, /* global gain */
+ {}
+};
+static const u8 om6802_init0[2][8] = {
+/*fixme: variable*/
+ {0xa0, 0x34, 0x29, 0x0e, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x34, 0x23, 0xb0, 0x00, 0x00, 0x00, 0x10},
+};
+static const u8 om6802_sensor_init[][8] = {
+ {0xa0, 0x34, 0xdf, 0x6d, 0x00, 0x00, 0x00, 0x10},
+ /* factory mode */
+ {0xa0, 0x34, 0xdd, 0x18, 0x00, 0x00, 0x00, 0x10},
+ /* output raw RGB */
+ {0xa0, 0x34, 0x5a, 0xc0, 0x00, 0x00, 0x00, 0x10},
+/* {0xa0, 0x34, 0xfb, 0x11, 0x00, 0x00, 0x00, 0x10}, */
+ {0xa0, 0x34, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x10},
+ /* auto-exposure speed (0) / white balance mode (auto RGB) */
+/* {0xa0, 0x34, 0xf1, 0x02, 0x00, 0x00, 0x00, 0x10},
+ * set color mode */
+/* {0xa0, 0x34, 0xfe, 0x5b, 0x00, 0x00, 0x00, 0x10},
+ * max AGC value in AE */
+/* {0xa0, 0x34, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x10},
+ * preset AGC */
+/* {0xa0, 0x34, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x10},
+ * preset brightness */
+/* {0xa0, 0x34, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x10},
+ * preset contrast */
+/* {0xa0, 0x34, 0xe8, 0x31, 0x00, 0x00, 0x00, 0x10},
+ * preset gamma */
+ {0xa0, 0x34, 0xe9, 0x0f, 0x00, 0x00, 0x00, 0x10},
+ /* luminance mode (0x4f -> AutoExpo on) */
+ {0xa0, 0x34, 0xe4, 0xff, 0x00, 0x00, 0x00, 0x10},
+ /* preset shutter */
+/* {0xa0, 0x34, 0xef, 0x00, 0x00, 0x00, 0x00, 0x10},
+ * auto frame rate */
+/* {0xa0, 0x34, 0xfb, 0xee, 0x00, 0x00, 0x00, 0x10}, */
+ {0xa0, 0x34, 0x5d, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+static const u8 om6802_sensor_param1[][8] = {
+ {0xa0, 0x34, 0x71, 0x84, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x34, 0x72, 0x05, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x34, 0x68, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x34, 0x69, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+static const u8 ov7630_sensor_init[][8] = {
+ {0xa1, 0x21, 0x76, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10},
+ {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+ {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10},
+ {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+ {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10},
+/* win: i2c_r from 00 to 80 */
+ {0xd1, 0x21, 0x03, 0x80, 0x10, 0x20, 0x80, 0x10},
+ {0xb1, 0x21, 0x0c, 0x20, 0x20, 0x00, 0x00, 0x10},
+/* HDG: 0x11 was 0x00 change to 0x01 for better exposure (15 fps instead of 30)
+ 0x13 was 0xc0 change to 0xc3 for auto gain and exposure */
+ {0xd1, 0x21, 0x11, 0x01, 0x48, 0xc3, 0x00, 0x10},
+ {0xb1, 0x21, 0x15, 0x80, 0x03, 0x00, 0x00, 0x10},
+ {0xd1, 0x21, 0x17, 0x1b, 0xbd, 0x05, 0xf6, 0x10},
+ {0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x21, 0x1f, 0x00, 0x80, 0x80, 0x80, 0x10},
+ {0xd1, 0x21, 0x23, 0xde, 0x10, 0x8a, 0xa0, 0x10},
+ {0xc1, 0x21, 0x27, 0xca, 0xa2, 0x74, 0x00, 0x10},
+ {0xd1, 0x21, 0x2a, 0x88, 0x00, 0x88, 0x01, 0x10},
+ {0xc1, 0x21, 0x2e, 0x80, 0x00, 0x18, 0x00, 0x10},
+ {0xa1, 0x21, 0x21, 0x08, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x21, 0x32, 0xc2, 0x08, 0x00, 0x00, 0x10},
+ {0xb1, 0x21, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x21, 0x60, 0x05, 0x40, 0x12, 0x57, 0x10},
+ {0xa1, 0x21, 0x64, 0x73, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x21, 0x65, 0x00, 0x55, 0x01, 0xac, 0x10},
+ {0xa1, 0x21, 0x69, 0x38, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x21, 0x6f, 0x1f, 0x01, 0x00, 0x10, 0x10},
+ {0xd1, 0x21, 0x73, 0x50, 0x20, 0x02, 0x01, 0x10},
+ {0xd1, 0x21, 0x77, 0xf3, 0x90, 0x98, 0x98, 0x10},
+ {0xc1, 0x21, 0x7b, 0x00, 0x4c, 0xf7, 0x00, 0x10},
+ {0xd1, 0x21, 0x17, 0x1b, 0xbd, 0x05, 0xf6, 0x10},
+ {0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+static const u8 ov7630_sensor_param1[][8] = {
+ {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10},
+/*fixme: + 0x12, 0x04*/
+/* {0xa1, 0x21, 0x75, 0x82, 0x00, 0x00, 0x00, 0x10}, * COMN
+ * set by setvflip */
+ {0xa1, 0x21, 0x10, 0x32, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x21, 0x01, 0x80, 0x80, 0x00, 0x00, 0x10},
+/* */
+/* {0xa1, 0x21, 0x2a, 0x88, 0x00, 0x00, 0x00, 0x10}, * set by setfreq */
+/* {0xa1, 0x21, 0x2b, 0x34, 0x00, 0x00, 0x00, 0x10}, * set by setfreq */
+/* */
+ {0xa1, 0x21, 0x10, 0x83, 0x00, 0x00, 0x00, 0x10},
+/* {0xb1, 0x21, 0x01, 0x88, 0x70, 0x00, 0x00, 0x10}, */
+ {}
+};
+
+static const u8 ov7648_sensor_init[][8] = {
+ {0xa1, 0x21, 0x76, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset */
+ {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+ {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x21, 0x03, 0xa4, 0x30, 0x88, 0x00, 0x10},
+ {0xb1, 0x21, 0x11, 0x80, 0x08, 0x00, 0x00, 0x10},
+ {0xc1, 0x21, 0x13, 0xa0, 0x04, 0x84, 0x00, 0x10},
+ {0xd1, 0x21, 0x17, 0x1a, 0x02, 0xba, 0xf4, 0x10},
+ {0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x21, 0x1f, 0x41, 0xc0, 0x80, 0x80, 0x10},
+ {0xd1, 0x21, 0x23, 0xde, 0xa0, 0x80, 0x32, 0x10},
+ {0xd1, 0x21, 0x27, 0xfe, 0xa0, 0x00, 0x91, 0x10},
+ {0xd1, 0x21, 0x2b, 0x00, 0x88, 0x85, 0x80, 0x10},
+ {0xc1, 0x21, 0x2f, 0x9c, 0x00, 0xc4, 0x00, 0x10},
+ {0xd1, 0x21, 0x60, 0xa6, 0x60, 0x88, 0x12, 0x10},
+ {0xd1, 0x21, 0x64, 0x88, 0x00, 0x00, 0x94, 0x10},
+ {0xd1, 0x21, 0x68, 0x7a, 0x0c, 0x00, 0x00, 0x10},
+ {0xd1, 0x21, 0x6c, 0x11, 0x33, 0x22, 0x00, 0x10},
+ {0xd1, 0x21, 0x70, 0x11, 0x00, 0x10, 0x50, 0x10},
+ {0xd1, 0x21, 0x74, 0x20, 0x06, 0x00, 0xb5, 0x10},
+ {0xd1, 0x21, 0x78, 0x8a, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x21, 0x7c, 0x00, 0x43, 0x00, 0x00, 0x10},
+
+ {0xd1, 0x21, 0x21, 0x86, 0x00, 0xde, 0xa0, 0x10},
+/* {0xd1, 0x21, 0x25, 0x80, 0x32, 0xfe, 0xa0, 0x10}, jfm done */
+/* {0xd1, 0x21, 0x29, 0x00, 0x91, 0x00, 0x88, 0x10}, jfm done */
+/* {0xb1, 0x21, 0x2d, 0x85, 0x00, 0x00, 0x00, 0x10}, set by setfreq */
+ {}
+};
+static const u8 ov7648_sensor_param1[][8] = {
+/* {0xa1, 0x21, 0x12, 0x08, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/* {0xa1, 0x21, 0x75, 0x06, 0x00, 0x00, 0x00, 0x10}, * COMN
+ * set by setvflip */
+ {0xa1, 0x21, 0x19, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x10, 0x32, 0x00, 0x00, 0x00, 0x10},
+/* {0xa1, 0x21, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/* {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, * GAIN - def */
+/* {0xb1, 0x21, 0x01, 0x6c, 0x6c, 0x00, 0x00, 0x10}, * B R - def: 80 */
+/*...*/
+ {0xa1, 0x21, 0x11, 0x81, 0x00, 0x00, 0x00, 0x10}, /* CLKRC */
+/* {0xa1, 0x21, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/* {0xa1, 0x21, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/* {0xa1, 0x21, 0x2a, 0x91, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/* {0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/* {0xb1, 0x21, 0x01, 0x64, 0x84, 0x00, 0x00, 0x10}, * B R - def: 80 */
+
+ {}
+};
+
+static const u8 ov7660_sensor_init[][8] = {
+ {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset SCCB */
+ {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+ {0xa1, 0x21, 0x12, 0x05, 0x00, 0x00, 0x00, 0x10},
+ /* Outformat = rawRGB */
+ {0xa1, 0x21, 0x13, 0xb8, 0x00, 0x00, 0x00, 0x10}, /* init COM8 */
+ {0xd1, 0x21, 0x00, 0x01, 0x74, 0x92, 0x00, 0x10},
+ /* GAIN BLUE RED VREF */
+ {0xd1, 0x21, 0x04, 0x00, 0x7d, 0x62, 0x00, 0x10},
+ /* COM 1 BAVE GEAVE AECHH */
+ {0xb1, 0x21, 0x08, 0x83, 0x01, 0x00, 0x00, 0x10}, /* RAVE COM2 */
+ {0xd1, 0x21, 0x0c, 0x00, 0x08, 0x04, 0x4f, 0x10}, /* COM 3 4 5 6 */
+ {0xd1, 0x21, 0x10, 0x7f, 0x40, 0x05, 0xff, 0x10},
+ /* AECH CLKRC COM7 COM8 */
+ {0xc1, 0x21, 0x14, 0x2c, 0x00, 0x02, 0x00, 0x10}, /* COM9 COM10 */
+ {0xd1, 0x21, 0x17, 0x10, 0x60, 0x02, 0x7b, 0x10},
+ /* HSTART HSTOP VSTRT VSTOP */
+ {0xa1, 0x21, 0x1b, 0x02, 0x00, 0x00, 0x00, 0x10}, /* PSHFT */
+ {0xb1, 0x21, 0x1e, 0x01, 0x0e, 0x00, 0x00, 0x10}, /* MVFP LAEC */
+ {0xd1, 0x21, 0x20, 0x07, 0x07, 0x07, 0x07, 0x10},
+ /* BOS GBOS GROS ROS (BGGR offset) */
+/* {0xd1, 0x21, 0x24, 0x68, 0x58, 0xd4, 0x80, 0x10}, */
+ {0xd1, 0x21, 0x24, 0x78, 0x68, 0xd4, 0x80, 0x10},
+ /* AEW AEB VPT BBIAS */
+ {0xd1, 0x21, 0x28, 0x80, 0x30, 0x00, 0x00, 0x10},
+ /* GbBIAS RSVD EXHCH EXHCL */
+ {0xd1, 0x21, 0x2c, 0x80, 0x00, 0x00, 0x62, 0x10},
+ /* RBIAS ADVFL ASDVFH YAVE */
+ {0xc1, 0x21, 0x30, 0x08, 0x30, 0xb4, 0x00, 0x10},
+ /* HSYST HSYEN HREF */
+ {0xd1, 0x21, 0x33, 0x00, 0x07, 0x84, 0x00, 0x10}, /* reserved */
+ {0xd1, 0x21, 0x37, 0x0c, 0x02, 0x43, 0x00, 0x10},
+ /* ADC ACOM OFON TSLB */
+ {0xd1, 0x21, 0x3b, 0x02, 0x6c, 0x19, 0x0e, 0x10},
+ /* COM11 COM12 COM13 COM14 */
+ {0xd1, 0x21, 0x3f, 0x41, 0xc1, 0x22, 0x08, 0x10},
+ /* EDGE COM15 COM16 COM17 */
+ {0xd1, 0x21, 0x43, 0xf0, 0x10, 0x78, 0xa8, 0x10}, /* reserved */
+ {0xd1, 0x21, 0x47, 0x60, 0x80, 0x00, 0x00, 0x10}, /* reserved */
+ {0xd1, 0x21, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x10}, /* reserved */
+ {0xd1, 0x21, 0x4f, 0x46, 0x36, 0x0f, 0x17, 0x10}, /* MTX 1 2 3 4 */
+ {0xd1, 0x21, 0x53, 0x7f, 0x96, 0x40, 0x40, 0x10}, /* MTX 5 6 7 8 */
+ {0xb1, 0x21, 0x57, 0x40, 0x0f, 0x00, 0x00, 0x10}, /* MTX9 MTXS */
+ {0xd1, 0x21, 0x59, 0xba, 0x9a, 0x22, 0xb9, 0x10}, /* reserved */
+ {0xd1, 0x21, 0x5d, 0x9b, 0x10, 0xf0, 0x05, 0x10}, /* reserved */
+ {0xa1, 0x21, 0x61, 0x60, 0x00, 0x00, 0x00, 0x10}, /* reserved */
+ {0xd1, 0x21, 0x62, 0x00, 0x00, 0x50, 0x30, 0x10},
+ /* LCC1 LCC2 LCC3 LCC4 */
+ {0xa1, 0x21, 0x66, 0x00, 0x00, 0x00, 0x00, 0x10}, /* LCC5 */
+ {0xd1, 0x21, 0x67, 0x80, 0x7a, 0x90, 0x80, 0x10}, /* MANU */
+ {0xa1, 0x21, 0x6b, 0x0a, 0x00, 0x00, 0x00, 0x10},
+ /* band gap reference [0:3] DBLV */
+ {0xd1, 0x21, 0x6c, 0x30, 0x48, 0x80, 0x74, 0x10}, /* gamma curve */
+ {0xd1, 0x21, 0x70, 0x64, 0x60, 0x5c, 0x58, 0x10}, /* gamma curve */
+ {0xd1, 0x21, 0x74, 0x54, 0x4c, 0x40, 0x38, 0x10}, /* gamma curve */
+ {0xd1, 0x21, 0x78, 0x34, 0x30, 0x2f, 0x2b, 0x10}, /* gamma curve */
+ {0xd1, 0x21, 0x7c, 0x03, 0x07, 0x17, 0x34, 0x10}, /* gamma curve */
+ {0xd1, 0x21, 0x80, 0x41, 0x4d, 0x58, 0x63, 0x10}, /* gamma curve */
+ {0xd1, 0x21, 0x84, 0x6e, 0x77, 0x87, 0x95, 0x10}, /* gamma curve */
+ {0xc1, 0x21, 0x88, 0xaf, 0xc7, 0xdf, 0x00, 0x10}, /* gamma curve */
+ {0xc1, 0x21, 0x8b, 0x99, 0x99, 0xcf, 0x00, 0x10}, /* reserved */
+ {0xb1, 0x21, 0x92, 0x00, 0x00, 0x00, 0x00, 0x10}, /* DM_LNL/H */
+/* not in all ms-win traces*/
+ {0xa1, 0x21, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+static const u8 ov7660_sensor_param1[][8] = {
+ {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10}, /* MVFP */
+ /* bits[3..0]reserved */
+ {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10},
+ /* VREF vertical frame ctrl */
+ {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10}, /* AECH 0x20 */
+ {0xa1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10}, /* ADVFL */
+ {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10}, /* ADVFH */
+ {0xa1, 0x21, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x10}, /* GAIN */
+/* {0xb1, 0x21, 0x01, 0x78, 0x78, 0x00, 0x00, 0x10}, * BLUE */
+/****** (some exchanges in the win trace) ******/
+/*fixme:param2*/
+ {0xa1, 0x21, 0x93, 0x00, 0x00, 0x00, 0x00, 0x10},/* dummy line hight */
+ {0xa1, 0x21, 0x92, 0x25, 0x00, 0x00, 0x00, 0x10}, /* dummy line low */
+ {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10}, /* EXHCH */
+ {0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}, /* EXHCL */
+/* {0xa1, 0x21, 0x02, 0x90, 0x00, 0x00, 0x00, 0x10}, * RED */
+/****** (some exchanges in the win trace) ******/
+/******!! startsensor KO if changed !!****/
+/*fixme: param3*/
+ {0xa1, 0x21, 0x93, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x92, 0xff, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x2b, 0xc3, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+
+static const u8 po1030_sensor_init[][8] = {
+/* the sensor registers are described in m5602/m5602_po1030.h */
+ {0xa1, 0x6e, 0x3f, 0x20, 0x00, 0x00, 0x00, 0x10}, /* sensor reset */
+ {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+ {0xa1, 0x6e, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x6e, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x04, 0x02, 0xb1, 0x02, 0x39, 0x10},
+ {0xd1, 0x6e, 0x08, 0x00, 0x01, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x0c, 0x02, 0x7f, 0x01, 0xe0, 0x10},
+ {0xd1, 0x6e, 0x12, 0x03, 0x02, 0x00, 0x03, 0x10},
+ {0xd1, 0x6e, 0x16, 0x85, 0x40, 0x4a, 0x40, 0x10}, /* r/g1/b/g2 gains */
+ {0xc1, 0x6e, 0x1a, 0x00, 0x80, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x1d, 0x08, 0x03, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x23, 0x00, 0xb0, 0x00, 0x94, 0x10},
+ {0xd1, 0x6e, 0x27, 0x58, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x6e, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x2d, 0x14, 0x35, 0x61, 0x84, 0x10}, /* gamma corr */
+ {0xd1, 0x6e, 0x31, 0xa2, 0xbd, 0xd8, 0xff, 0x10},
+ {0xd1, 0x6e, 0x35, 0x06, 0x1e, 0x12, 0x02, 0x10}, /* color matrix */
+ {0xd1, 0x6e, 0x39, 0xaa, 0x53, 0x37, 0xd5, 0x10},
+ {0xa1, 0x6e, 0x3d, 0xf2, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x3e, 0x00, 0x00, 0x80, 0x03, 0x10},
+ {0xd1, 0x6e, 0x42, 0x03, 0x00, 0x00, 0x00, 0x10},
+ {0xc1, 0x6e, 0x46, 0x00, 0x80, 0x80, 0x00, 0x10},
+ {0xd1, 0x6e, 0x4b, 0x02, 0xef, 0x08, 0xcd, 0x10},
+ {0xd1, 0x6e, 0x4f, 0x00, 0xd0, 0x00, 0xa0, 0x10},
+ {0xd1, 0x6e, 0x53, 0x01, 0xaa, 0x01, 0x40, 0x10},
+ {0xd1, 0x6e, 0x5a, 0x50, 0x04, 0x30, 0x03, 0x10}, /* raw rgb bayer */
+ {0xa1, 0x6e, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x5f, 0x10, 0x40, 0xff, 0x00, 0x10},
+
+ {0xd1, 0x6e, 0x63, 0x40, 0x40, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xc1, 0x6e, 0x73, 0x10, 0x80, 0xeb, 0x00, 0x10},
+ {}
+};
+static const u8 po1030_sensor_param1[][8] = {
+/* from ms-win traces - these values change with auto gain/expo/wb.. */
+ {0xa1, 0x6e, 0x1e, 0x03, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x6e, 0x1e, 0x03, 0x00, 0x00, 0x00, 0x10},
+/* mean values */
+ {0xc1, 0x6e, 0x1a, 0x02, 0xd4, 0xa4, 0x00, 0x10}, /* integlines */
+ {0xa1, 0x6e, 0x15, 0x04, 0x00, 0x00, 0x00, 0x10}, /* global gain */
+ {0xc1, 0x6e, 0x16, 0x40, 0x40, 0x40, 0x00, 0x10}, /* r/g1/b gains */
+
+ {0xa1, 0x6e, 0x1d, 0x08, 0x00, 0x00, 0x00, 0x10}, /* control1 */
+ {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, /* frameheight */
+ {0xa1, 0x6e, 0x07, 0xd5, 0x00, 0x00, 0x00, 0x10},
+/* {0xc1, 0x6e, 0x16, 0x49, 0x40, 0x45, 0x00, 0x10}, */
+ {}
+};
+
+static const u8 po2030n_sensor_init[][8] = {
+ {0xa1, 0x6e, 0x1e, 0x1a, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x6e, 0x1f, 0x99, 0x00, 0x00, 0x00, 0x10},
+ {DELAY, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 10ms */
+ {0xa1, 0x6e, 0x1e, 0x0a, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x6e, 0x1f, 0x19, 0x00, 0x00, 0x00, 0x10},
+ {DELAY, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 10ms */
+ {0xa1, 0x6e, 0x20, 0x44, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x6e, 0x04, 0x03, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x6e, 0x05, 0x70, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x6e, 0x07, 0x25, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x08, 0x00, 0xd0, 0x00, 0x08, 0x10},
+ {0xd1, 0x6e, 0x0c, 0x03, 0x50, 0x01, 0xe8, 0x10},
+ {0xd1, 0x6e, 0x1d, 0x20, 0x0a, 0x19, 0x44, 0x10},
+ {0xd1, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x25, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x29, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x35, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x39, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x41, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x45, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x49, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x4d, 0x00, 0x00, 0x00, 0xed, 0x10},
+ {0xd1, 0x6e, 0x51, 0x17, 0x4a, 0x2f, 0xc0, 0x10},
+ {0xd1, 0x6e, 0x55, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x59, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x61, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x65, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x69, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x71, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x75, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x79, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x81, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x85, 0x00, 0x00, 0x00, 0x08, 0x10},
+ {0xd1, 0x6e, 0x89, 0x01, 0xe8, 0x00, 0x01, 0x10},
+ {0xa1, 0x6e, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x25, 0x00, 0x00, 0x00, 0x01, 0x10},
+ {0xd1, 0x6e, 0x29, 0xe6, 0x00, 0xbd, 0x03, 0x10},
+ {0xd1, 0x6e, 0x2d, 0x41, 0x38, 0x68, 0x40, 0x10},
+ {0xd1, 0x6e, 0x31, 0x2b, 0x00, 0x36, 0x00, 0x10},
+ {0xd1, 0x6e, 0x35, 0x30, 0x30, 0x08, 0x00, 0x10},
+ {0xd1, 0x6e, 0x39, 0x00, 0x00, 0x33, 0x06, 0x10},
+ {0xb1, 0x6e, 0x3d, 0x06, 0x02, 0x00, 0x00, 0x10},
+ {}
+};
+static const u8 po2030n_sensor_param1[][8] = {
+ {0xa1, 0x6e, 0x1a, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {DELAY, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 8ms */
+ {0xa1, 0x6e, 0x1b, 0xf4, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x6e, 0x15, 0x04, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x16, 0x40, 0x40, 0x40, 0x40, 0x10}, /* RGBG gains */
+/*param2*/
+ {0xa1, 0x6e, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x6e, 0x04, 0x03, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x6e, 0x05, 0x6f, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x6e, 0x07, 0x25, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+
+static const u8 soi768_sensor_init[][8] = {
+ {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset */
+ {DELAY, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 96ms */
+ {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x13, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x0f, 0x03, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x19, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+static const u8 soi768_sensor_param1[][8] = {
+ {0xa1, 0x21, 0x10, 0x10, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x21, 0x01, 0x7f, 0x7f, 0x00, 0x00, 0x10},
+/* */
+/* {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10}, */
+/* {0xa1, 0x21, 0x2d, 0x25, 0x00, 0x00, 0x00, 0x10}, */
+ {0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10},
+/* {0xb1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10}, */
+ {0xa1, 0x21, 0x02, 0x8d, 0x00, 0x00, 0x00, 0x10},
+/* the next sequence should be used for auto gain */
+ {0xa1, 0x21, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10},
+ /* global gain ? : 07 - change with 0x15 at the end */
+ {0xa1, 0x21, 0x10, 0x3f, 0x00, 0x00, 0x00, 0x10}, /* ???? : 063f */
+ {0xa1, 0x21, 0x04, 0x06, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x21, 0x2d, 0x63, 0x03, 0x00, 0x00, 0x10},
+ /* exposure ? : 0200 - change with 0x1e at the end */
+ {}
+};
+
+static const u8 sp80708_sensor_init[][8] = {
+ {0xa1, 0x18, 0x06, 0xf9, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x09, 0x1f, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x0d, 0xc0, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x10, 0x40, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x11, 0x4e, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x12, 0x53, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x15, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x19, 0x18, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x1a, 0x10, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x1b, 0x10, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x1c, 0x28, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x1d, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x1e, 0x10, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x26, 0x04, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x27, 0x1e, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x28, 0x5a, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x29, 0x28, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x2a, 0x78, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x2b, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x2c, 0xf7, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x2d, 0x2d, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x2e, 0xd5, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x39, 0x42, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x3a, 0x67, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x3b, 0x87, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x3c, 0xa3, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x3d, 0xb0, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x3e, 0xbc, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x3f, 0xc8, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x40, 0xd4, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x41, 0xdf, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x42, 0xea, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x43, 0xf5, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x45, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x46, 0x60, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x47, 0x50, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x48, 0x30, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x49, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x4d, 0xae, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x4e, 0x03, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x4f, 0x66, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x50, 0x1c, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x44, 0x10, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x4a, 0x30, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x51, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x52, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x53, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x54, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x55, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x56, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x57, 0xe0, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x58, 0xc0, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x59, 0xab, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x5a, 0xa0, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x5b, 0x99, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x5c, 0x90, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x5e, 0x24, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x60, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x61, 0x73, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x63, 0x42, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x64, 0x42, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x65, 0x42, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x66, 0x24, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x67, 0x24, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x68, 0x08, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x2f, 0xc9, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+static const u8 sp80708_sensor_param1[][8] = {
+ {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x03, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x04, 0xa4, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x14, 0x3f, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x5d, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x18, 0x11, 0x40, 0x40, 0x00, 0x00, 0x10},
+ {}
+};
+
+static const u8 (*sensor_init[])[8] = {
+[SENSOR_ADCM1700] = adcm1700_sensor_init,
+[SENSOR_GC0307] = gc0307_sensor_init,
+[SENSOR_HV7131R] = hv7131r_sensor_init,
+[SENSOR_MI0360] = mi0360_sensor_init,
+[SENSOR_MI0360B] = mi0360b_sensor_init,
+[SENSOR_MO4000] = mo4000_sensor_init,
+[SENSOR_MT9V111] = mt9v111_sensor_init,
+[SENSOR_OM6802] = om6802_sensor_init,
+[SENSOR_OV7630] = ov7630_sensor_init,
+[SENSOR_OV7648] = ov7648_sensor_init,
+[SENSOR_OV7660] = ov7660_sensor_init,
+[SENSOR_PO1030] = po1030_sensor_init,
+[SENSOR_PO2030N] = po2030n_sensor_init,
+[SENSOR_SOI768] = soi768_sensor_init,
+[SENSOR_SP80708] = sp80708_sensor_init,
+};
+
+/* read <len> bytes to gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev,
+ u16 value, int len)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+#ifdef GSPCA_DEBUG
+ if (len > USB_BUF_SZ) {
+ pr_err("reg_r: buffer overflow\n");
+ return;
+ }
+#endif
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ 0,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ value, 0,
+ gspca_dev->usb_buf, len,
+ 500);
+ PDEBUG(D_USBI, "reg_r [%02x] -> %02x", value, gspca_dev->usb_buf[0]);
+ if (ret < 0) {
+ pr_err("reg_r err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static void reg_w1(struct gspca_dev *gspca_dev,
+ u16 value,
+ u8 data)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ PDEBUG(D_USBO, "reg_w1 [%04x] = %02x", value, data);
+ gspca_dev->usb_buf[0] = data;
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0x08,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ value,
+ 0,
+ gspca_dev->usb_buf, 1,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_w1 err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+static void reg_w(struct gspca_dev *gspca_dev,
+ u16 value,
+ const u8 *buffer,
+ int len)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ PDEBUG(D_USBO, "reg_w [%04x] = %02x %02x ..",
+ value, buffer[0], buffer[1]);
+#ifdef GSPCA_DEBUG
+ if (len > USB_BUF_SZ) {
+ pr_err("reg_w: buffer overflow\n");
+ return;
+ }
+#endif
+ memcpy(gspca_dev->usb_buf, buffer, len);
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0x08,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ value, 0,
+ gspca_dev->usb_buf, len,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_w err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+/* I2C write 1 byte */
+static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ PDEBUG(D_USBO, "i2c_w1 [%02x] = %02x", reg, val);
+ switch (sd->sensor) {
+ case SENSOR_ADCM1700:
+ case SENSOR_OM6802:
+ case SENSOR_GC0307: /* i2c command = a0 (100 kHz) */
+ gspca_dev->usb_buf[0] = 0x80 | (2 << 4);
+ break;
+ default: /* i2c command = a1 (400 kHz) */
+ gspca_dev->usb_buf[0] = 0x81 | (2 << 4);
+ break;
+ }
+ gspca_dev->usb_buf[1] = sd->i2c_addr;
+ gspca_dev->usb_buf[2] = reg;
+ gspca_dev->usb_buf[3] = val;
+ gspca_dev->usb_buf[4] = 0;
+ gspca_dev->usb_buf[5] = 0;
+ gspca_dev->usb_buf[6] = 0;
+ gspca_dev->usb_buf[7] = 0x10;
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0x08,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ 0x08, /* value = i2c */
+ 0,
+ gspca_dev->usb_buf, 8,
+ 500);
+ if (ret < 0) {
+ pr_err("i2c_w1 err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+/* I2C write 8 bytes */
+static void i2c_w8(struct gspca_dev *gspca_dev,
+ const u8 *buffer)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ PDEBUG(D_USBO, "i2c_w8 [%02x] = %02x ..",
+ buffer[2], buffer[3]);
+ memcpy(gspca_dev->usb_buf, buffer, 8);
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0x08,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ 0x08, 0, /* value, index */
+ gspca_dev->usb_buf, 8,
+ 500);
+ msleep(2);
+ if (ret < 0) {
+ pr_err("i2c_w8 err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+/* sensor read 'len' (1..5) bytes in gspca_dev->usb_buf */
+static void i2c_r(struct gspca_dev *gspca_dev, u8 reg, int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 mode[8];
+
+ switch (sd->sensor) {
+ case SENSOR_ADCM1700:
+ case SENSOR_OM6802:
+ case SENSOR_GC0307: /* i2c command = a0 (100 kHz) */
+ mode[0] = 0x80 | 0x10;
+ break;
+ default: /* i2c command = 91 (400 kHz) */
+ mode[0] = 0x81 | 0x10;
+ break;
+ }
+ mode[1] = sd->i2c_addr;
+ mode[2] = reg;
+ mode[3] = 0;
+ mode[4] = 0;
+ mode[5] = 0;
+ mode[6] = 0;
+ mode[7] = 0x10;
+ i2c_w8(gspca_dev, mode);
+ msleep(2);
+ mode[0] = (mode[0] & 0x81) | (len << 4) | 0x02;
+ mode[2] = 0;
+ i2c_w8(gspca_dev, mode);
+ msleep(2);
+ reg_r(gspca_dev, 0x0a, 5);
+}
+
+static void i2c_w_seq(struct gspca_dev *gspca_dev,
+ const u8 (*data)[8])
+{
+ while ((*data)[0] != 0) {
+ if ((*data)[0] != DELAY)
+ i2c_w8(gspca_dev, *data);
+ else
+ msleep((*data)[1]);
+ data++;
+ }
+}
+
+/* check the ID of the hv7131 sensor */
+/* this sequence is needed because it activates the sensor */
+static void hv7131r_probe(struct gspca_dev *gspca_dev)
+{
+ i2c_w1(gspca_dev, 0x02, 0); /* sensor wakeup */
+ msleep(10);
+ reg_w1(gspca_dev, 0x02, 0x66); /* Gpio on */
+ msleep(10);
+ i2c_r(gspca_dev, 0, 5); /* read sensor id */
+ if (gspca_dev->usb_buf[0] == 0x02 /* chip ID (02 is R) */
+ && gspca_dev->usb_buf[1] == 0x09
+ && gspca_dev->usb_buf[2] == 0x01) {
+ PDEBUG(D_PROBE, "Sensor HV7131R found");
+ return;
+ }
+ pr_warn("Erroneous HV7131R ID 0x%02x 0x%02x 0x%02x\n",
+ gspca_dev->usb_buf[0], gspca_dev->usb_buf[1],
+ gspca_dev->usb_buf[2]);
+}
+
+static void mi0360_probe(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, j;
+ u16 val = 0;
+ static const u8 probe_tb[][4][8] = {
+ { /* mi0360 */
+ {0xb0, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10},
+ {0x90, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa2, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xb0, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10}
+ },
+ { /* mt9v111 */
+ {0xb0, 0x5c, 0x01, 0x00, 0x04, 0x00, 0x00, 0x10},
+ {0x90, 0x5c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa2, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {}
+ },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(probe_tb); i++) {
+ reg_w1(gspca_dev, 0x17, 0x62);
+ reg_w1(gspca_dev, 0x01, 0x08);
+ for (j = 0; j < 3; j++)
+ i2c_w8(gspca_dev, probe_tb[i][j]);
+ msleep(2);
+ reg_r(gspca_dev, 0x0a, 5);
+ val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
+ if (probe_tb[i][3][0] != 0)
+ i2c_w8(gspca_dev, probe_tb[i][3]);
+ reg_w1(gspca_dev, 0x01, 0x29);
+ reg_w1(gspca_dev, 0x17, 0x42);
+ if (val != 0xffff)
+ break;
+ }
+ if (gspca_dev->usb_err < 0)
+ return;
+ switch (val) {
+ case 0x8221:
+ PDEBUG(D_PROBE, "Sensor mi0360b");
+ sd->sensor = SENSOR_MI0360B;
+ break;
+ case 0x823a:
+ PDEBUG(D_PROBE, "Sensor mt9v111");
+ sd->sensor = SENSOR_MT9V111;
+ break;
+ case 0x8243:
+ PDEBUG(D_PROBE, "Sensor mi0360");
+ break;
+ default:
+ PDEBUG(D_PROBE, "Unknown sensor %04x - forced to mi0360", val);
+ break;
+ }
+}
+
+static void ov7630_probe(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 val;
+
+ /* check ov76xx */
+ reg_w1(gspca_dev, 0x17, 0x62);
+ reg_w1(gspca_dev, 0x01, 0x08);
+ sd->i2c_addr = 0x21;
+ i2c_r(gspca_dev, 0x0a, 2);
+ val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
+ reg_w1(gspca_dev, 0x01, 0x29);
+ reg_w1(gspca_dev, 0x17, 0x42);
+ if (gspca_dev->usb_err < 0)
+ return;
+ if (val == 0x7628) { /* soi768 */
+ sd->sensor = SENSOR_SOI768;
+/*fixme: only valid for 0c45:613e?*/
+ gspca_dev->cam.input_flags =
+ V4L2_IN_ST_VFLIP | V4L2_IN_ST_HFLIP;
+ PDEBUG(D_PROBE, "Sensor soi768");
+ return;
+ }
+ PDEBUG(D_PROBE, "Sensor ov%04x", val);
+}
+
+static void ov7648_probe(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 val;
+
+ /* check ov76xx */
+ reg_w1(gspca_dev, 0x17, 0x62);
+ reg_w1(gspca_dev, 0x01, 0x08);
+ sd->i2c_addr = 0x21;
+ i2c_r(gspca_dev, 0x0a, 2);
+ val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
+ reg_w1(gspca_dev, 0x01, 0x29);
+ reg_w1(gspca_dev, 0x17, 0x42);
+ if ((val & 0xff00) == 0x7600) { /* ov76xx */
+ PDEBUG(D_PROBE, "Sensor ov%04x", val);
+ return;
+ }
+
+ /* check po1030 */
+ reg_w1(gspca_dev, 0x17, 0x62);
+ reg_w1(gspca_dev, 0x01, 0x08);
+ sd->i2c_addr = 0x6e;
+ i2c_r(gspca_dev, 0x00, 2);
+ val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
+ reg_w1(gspca_dev, 0x01, 0x29);
+ reg_w1(gspca_dev, 0x17, 0x42);
+ if (gspca_dev->usb_err < 0)
+ return;
+ if (val == 0x1030) { /* po1030 */
+ PDEBUG(D_PROBE, "Sensor po1030");
+ sd->sensor = SENSOR_PO1030;
+ return;
+ }
+ pr_err("Unknown sensor %04x\n", val);
+}
+
+/* 0c45:6142 sensor may be po2030n, gc0305 or gc0307 */
+static void po2030n_probe(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 val;
+
+ /* check gc0307 */
+ reg_w1(gspca_dev, 0x17, 0x62);
+ reg_w1(gspca_dev, 0x01, 0x08);
+ reg_w1(gspca_dev, 0x02, 0x22);
+ sd->i2c_addr = 0x21;
+ i2c_r(gspca_dev, 0x00, 1);
+ val = gspca_dev->usb_buf[4];
+ reg_w1(gspca_dev, 0x01, 0x29); /* reset */
+ reg_w1(gspca_dev, 0x17, 0x42);
+ if (val == 0x99) { /* gc0307 (?) */
+ PDEBUG(D_PROBE, "Sensor gc0307");
+ sd->sensor = SENSOR_GC0307;
+ return;
+ }
+
+ /* check po2030n */
+ reg_w1(gspca_dev, 0x17, 0x62);
+ reg_w1(gspca_dev, 0x01, 0x0a);
+ sd->i2c_addr = 0x6e;
+ i2c_r(gspca_dev, 0x00, 2);
+ val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
+ reg_w1(gspca_dev, 0x01, 0x29);
+ reg_w1(gspca_dev, 0x17, 0x42);
+ if (gspca_dev->usb_err < 0)
+ return;
+ if (val == 0x2030) {
+ PDEBUG(D_PROBE, "Sensor po2030n");
+/* sd->sensor = SENSOR_PO2030N; */
+ } else {
+ pr_err("Unknown sensor ID %04x\n", val);
+ }
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+ sd->bridge = id->driver_info >> 16;
+ sd->sensor = id->driver_info >> 8;
+ sd->flags = id->driver_info;
+
+ cam = &gspca_dev->cam;
+ if (sd->sensor == SENSOR_ADCM1700) {
+ cam->cam_mode = cif_mode;
+ cam->nmodes = ARRAY_SIZE(cif_mode);
+ } else {
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+ }
+ cam->npkt = 24; /* 24 packets per ISOC message */
+ cam->ctrls = sd->ctrls;
+
+ sd->ag_cnt = -1;
+ sd->quality = QUALITY_DEF;
+
+ INIT_WORK(&sd->work, qual_upd);
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ const u8 *sn9c1xx;
+ u8 regGpio[] = { 0x29, 0x70 }; /* no audio */
+ u8 regF1;
+
+ /* setup a selector by bridge */
+ reg_w1(gspca_dev, 0xf1, 0x01);
+ reg_r(gspca_dev, 0x00, 1);
+ reg_w1(gspca_dev, 0xf1, 0x00);
+ reg_r(gspca_dev, 0x00, 1); /* get sonix chip id */
+ regF1 = gspca_dev->usb_buf[0];
+ if (gspca_dev->usb_err < 0)
+ return gspca_dev->usb_err;
+ PDEBUG(D_PROBE, "Sonix chip id: %02x", regF1);
+ if (gspca_dev->audio)
+ regGpio[1] |= 0x04; /* with audio */
+ switch (sd->bridge) {
+ case BRIDGE_SN9C102P:
+ case BRIDGE_SN9C105:
+ if (regF1 != 0x11)
+ return -ENODEV;
+ break;
+ default:
+/* case BRIDGE_SN9C110: */
+/* case BRIDGE_SN9C120: */
+ if (regF1 != 0x12)
+ return -ENODEV;
+ }
+
+ switch (sd->sensor) {
+ case SENSOR_MI0360:
+ mi0360_probe(gspca_dev);
+ break;
+ case SENSOR_OV7630:
+ ov7630_probe(gspca_dev);
+ break;
+ case SENSOR_OV7648:
+ ov7648_probe(gspca_dev);
+ break;
+ case SENSOR_PO2030N:
+ po2030n_probe(gspca_dev);
+ break;
+ }
+
+ switch (sd->bridge) {
+ case BRIDGE_SN9C102P:
+ reg_w1(gspca_dev, 0x02, regGpio[1]);
+ break;
+ default:
+ reg_w(gspca_dev, 0x01, regGpio, 2);
+ break;
+ }
+
+ if (sd->sensor == SENSOR_OM6802)
+ sd->ctrls[SHARPNESS].def = 0x10;
+
+ /* Note we do not disable the sensor clock here (power saving mode),
+ as that also disables the button on the cam. */
+ reg_w1(gspca_dev, 0xf1, 0x00);
+
+ /* set the i2c address */
+ sn9c1xx = sn_tb[sd->sensor];
+ sd->i2c_addr = sn9c1xx[9];
+
+ gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
+ if (!(sd->flags & F_ILLUM))
+ gspca_dev->ctrl_dis |= (1 << ILLUM);
+
+ return gspca_dev->usb_err;
+}
+
+static u32 expo_adjust(struct gspca_dev *gspca_dev,
+ u32 expo)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->sensor) {
+ case SENSOR_GC0307: {
+ int a, b;
+
+ /* expo = 0..255 -> a = 19..43 */
+ a = 19 + expo * 25 / 256;
+ i2c_w1(gspca_dev, 0x68, a);
+ a -= 12;
+ b = a * a * 4; /* heuristic */
+ i2c_w1(gspca_dev, 0x03, b >> 8);
+ i2c_w1(gspca_dev, 0x04, b);
+ break;
+ }
+ case SENSOR_HV7131R: {
+ u8 Expodoit[] =
+ { 0xc1, 0x11, 0x25, 0x00, 0x00, 0x00, 0x00, 0x16 };
+
+ Expodoit[3] = expo >> 16;
+ Expodoit[4] = expo >> 8;
+ Expodoit[5] = expo;
+ i2c_w8(gspca_dev, Expodoit);
+ break;
+ }
+ case SENSOR_MI0360:
+ case SENSOR_MI0360B: {
+ u8 expoMi[] = /* exposure 0x0635 -> 4 fp/s 0x10 */
+ { 0xb1, 0x5d, 0x09, 0x00, 0x00, 0x00, 0x00, 0x16 };
+ static const u8 doit[] = /* update sensor */
+ { 0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10 };
+ static const u8 sensorgo[] = /* sensor on */
+ { 0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10 };
+
+ if (expo > 0x0635)
+ expo = 0x0635;
+ else if (expo < 0x0001)
+ expo = 0x0001;
+ expoMi[3] = expo >> 8;
+ expoMi[4] = expo;
+ i2c_w8(gspca_dev, expoMi);
+ i2c_w8(gspca_dev, doit);
+ i2c_w8(gspca_dev, sensorgo);
+ break;
+ }
+ case SENSOR_MO4000: {
+ u8 expoMof[] =
+ { 0xa1, 0x21, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x10 };
+ u8 expoMo10[] =
+ { 0xa1, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10 };
+ static const u8 gainMo[] =
+ { 0xa1, 0x21, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1d };
+
+ if (expo > 0x1fff)
+ expo = 0x1fff;
+ else if (expo < 0x0001)
+ expo = 0x0001;
+ expoMof[3] = (expo & 0x03fc) >> 2;
+ i2c_w8(gspca_dev, expoMof);
+ expoMo10[3] = ((expo & 0x1c00) >> 10)
+ | ((expo & 0x0003) << 4);
+ i2c_w8(gspca_dev, expoMo10);
+ i2c_w8(gspca_dev, gainMo);
+ PDEBUG(D_FRAM, "set exposure %d",
+ ((expoMo10[3] & 0x07) << 10)
+ | (expoMof[3] << 2)
+ | ((expoMo10[3] & 0x30) >> 4));
+ break;
+ }
+ case SENSOR_MT9V111: {
+ u8 expo_c1[] =
+ { 0xb1, 0x5c, 0x09, 0x00, 0x00, 0x00, 0x00, 0x10 };
+
+ if (expo > 0x0390)
+ expo = 0x0390;
+ else if (expo < 0x0060)
+ expo = 0x0060;
+ expo_c1[3] = expo >> 8;
+ expo_c1[4] = expo;
+ i2c_w8(gspca_dev, expo_c1);
+ break;
+ }
+ case SENSOR_OM6802: {
+ u8 gainOm[] =
+ { 0xa0, 0x34, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x10 };
+ /* preset AGC - works when AutoExpo = off */
+
+ if (expo > 0x03ff)
+ expo = 0x03ff;
+ if (expo < 0x0001)
+ expo = 0x0001;
+ gainOm[3] = expo >> 2;
+ i2c_w8(gspca_dev, gainOm);
+ reg_w1(gspca_dev, 0x96, expo >> 5);
+ PDEBUG(D_FRAM, "set exposure %d", gainOm[3]);
+ break;
+ }
+ }
+ return expo;
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ unsigned int expo;
+ int brightness;
+ u8 k2;
+
+ brightness = sd->ctrls[BRIGHTNESS].val;
+ k2 = (brightness - 0x80) >> 2;
+ switch (sd->sensor) {
+ case SENSOR_ADCM1700:
+ if (k2 > 0x1f)
+ k2 = 0; /* only positive Y offset */
+ break;
+ case SENSOR_HV7131R:
+ expo = brightness << 12;
+ if (expo > 0x002dc6c0)
+ expo = 0x002dc6c0;
+ else if (expo < 0x02a0)
+ expo = 0x02a0;
+ sd->exposure = expo_adjust(gspca_dev, expo);
+ break;
+ case SENSOR_MI0360:
+ case SENSOR_MO4000:
+ expo = brightness << 4;
+ sd->exposure = expo_adjust(gspca_dev, expo);
+ break;
+ case SENSOR_MI0360B:
+ expo = brightness << 2;
+ sd->exposure = expo_adjust(gspca_dev, expo);
+ break;
+ case SENSOR_GC0307:
+ expo = brightness;
+ sd->exposure = expo_adjust(gspca_dev, expo);
+ return; /* don't set the Y offset */
+ case SENSOR_MT9V111:
+ expo = brightness << 2;
+ sd->exposure = expo_adjust(gspca_dev, expo);
+ return; /* don't set the Y offset */
+ case SENSOR_OM6802:
+ expo = brightness << 2;
+ sd->exposure = expo_adjust(gspca_dev, expo);
+ return; /* Y offset already set */
+ }
+
+ reg_w1(gspca_dev, 0x96, k2); /* color matrix Y offset */
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 k2;
+ u8 contrast[6];
+
+ k2 = sd->ctrls[CONTRAST].val * 37 / (CONTRAST_MAX + 1)
+ + 37; /* 37..73 */
+ contrast[0] = (k2 + 1) / 2; /* red */
+ contrast[1] = 0;
+ contrast[2] = k2; /* green */
+ contrast[3] = 0;
+ contrast[4] = k2 / 5; /* blue */
+ contrast[5] = 0;
+ reg_w(gspca_dev, 0x84, contrast, sizeof contrast);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, v, colors;
+ const s16 *uv;
+ u8 reg8a[12]; /* U & V gains */
+ static const s16 uv_com[6] = { /* same as reg84 in signed decimal */
+ -24, -38, 64, /* UR UG UB */
+ 62, -51, -9 /* VR VG VB */
+ };
+ static const s16 uv_mi0360b[6] = {
+ -20, -38, 64, /* UR UG UB */
+ 60, -51, -9 /* VR VG VB */
+ };
+
+ colors = sd->ctrls[COLORS].val;
+ if (sd->sensor == SENSOR_MI0360B)
+ uv = uv_mi0360b;
+ else
+ uv = uv_com;
+ for (i = 0; i < 6; i++) {
+ v = uv[i] * colors / COLORS_DEF;
+ reg8a[i * 2] = v;
+ reg8a[i * 2 + 1] = (v >> 8) & 0x0f;
+ }
+ reg_w(gspca_dev, 0x8a, reg8a, sizeof reg8a);
+}
+
+static void setredblue(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor == SENSOR_PO2030N) {
+ u8 rg1b[] = /* red green1 blue (no g2) */
+ {0xc1, 0x6e, 0x16, 0x00, 0x40, 0x00, 0x00, 0x10};
+
+ /* 0x40 = normal value = gain x 1 */
+ rg1b[3] = sd->ctrls[RED].val * 2;
+ rg1b[5] = sd->ctrls[BLUE].val * 2;
+ i2c_w8(gspca_dev, rg1b);
+ return;
+ }
+ reg_w1(gspca_dev, 0x05, sd->ctrls[RED].val);
+/* reg_w1(gspca_dev, 0x07, 32); */
+ reg_w1(gspca_dev, 0x06, sd->ctrls[BLUE].val);
+}
+
+static void setgamma(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, val;
+ u8 gamma[17];
+ const u8 *gamma_base;
+ static const u8 delta[17] = {
+ 0x00, 0x14, 0x1c, 0x1c, 0x1c, 0x1c, 0x1b, 0x1a,
+ 0x18, 0x13, 0x10, 0x0e, 0x08, 0x07, 0x04, 0x02, 0x00
+ };
+
+ switch (sd->sensor) {
+ case SENSOR_ADCM1700:
+ gamma_base = gamma_spec_0;
+ break;
+ case SENSOR_HV7131R:
+ case SENSOR_MI0360B:
+ case SENSOR_MT9V111:
+ gamma_base = gamma_spec_1;
+ break;
+ case SENSOR_GC0307:
+ gamma_base = gamma_spec_2;
+ break;
+ case SENSOR_SP80708:
+ gamma_base = gamma_spec_3;
+ break;
+ default:
+ gamma_base = gamma_def;
+ break;
+ }
+
+ val = sd->ctrls[GAMMA].val;
+ for (i = 0; i < sizeof gamma; i++)
+ gamma[i] = gamma_base[i]
+ + delta[i] * (val - GAMMA_DEF) / 32;
+ reg_w(gspca_dev, 0x20, gamma, sizeof gamma);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor == SENSOR_PO2030N) {
+ u8 rexpo[] = /* 1a: expo H, 1b: expo M */
+ {0xa1, 0x6e, 0x1a, 0x00, 0x40, 0x00, 0x00, 0x10};
+
+ rexpo[3] = sd->ctrls[EXPOSURE].val >> 8;
+ i2c_w8(gspca_dev, rexpo);
+ msleep(6);
+ rexpo[2] = 0x1b;
+ rexpo[3] = sd->ctrls[EXPOSURE].val;
+ i2c_w8(gspca_dev, rexpo);
+ }
+}
+
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (gspca_dev->ctrl_dis & (1 << AUTOGAIN))
+ return;
+ switch (sd->sensor) {
+ case SENSOR_OV7630:
+ case SENSOR_OV7648: {
+ u8 comb;
+
+ if (sd->sensor == SENSOR_OV7630)
+ comb = 0xc0;
+ else
+ comb = 0xa0;
+ if (sd->ctrls[AUTOGAIN].val)
+ comb |= 0x03;
+ i2c_w1(&sd->gspca_dev, 0x13, comb);
+ return;
+ }
+ }
+ if (sd->ctrls[AUTOGAIN].val)
+ sd->ag_cnt = AG_CNT_START;
+ else
+ sd->ag_cnt = -1;
+}
+
+static void setgain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor == SENSOR_PO2030N) {
+ u8 rgain[] = /* 15: gain */
+ {0xa1, 0x6e, 0x15, 0x00, 0x40, 0x00, 0x00, 0x15};
+
+ rgain[3] = sd->ctrls[GAIN].val;
+ i2c_w8(gspca_dev, rgain);
+ }
+}
+
+static void sethvflip(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 comn;
+
+ switch (sd->sensor) {
+ case SENSOR_HV7131R:
+ comn = 0x18; /* clkdiv = 1, ablcen = 1 */
+ if (sd->ctrls[VFLIP].val)
+ comn |= 0x01;
+ i2c_w1(gspca_dev, 0x01, comn); /* sctra */
+ break;
+ case SENSOR_OV7630:
+ comn = 0x02;
+ if (!sd->ctrls[VFLIP].val)
+ comn |= 0x80;
+ i2c_w1(gspca_dev, 0x75, comn);
+ break;
+ case SENSOR_OV7648:
+ comn = 0x06;
+ if (sd->ctrls[VFLIP].val)
+ comn |= 0x80;
+ i2c_w1(gspca_dev, 0x75, comn);
+ break;
+ case SENSOR_PO2030N:
+ /* Reg. 0x1E: Timing Generator Control Register 2 (Tgcontrol2)
+ * (reset value: 0x0A)
+ * bit7: HM: Horizontal Mirror: 0: disable, 1: enable
+ * bit6: VM: Vertical Mirror: 0: disable, 1: enable
+ * bit5: ST: Shutter Selection: 0: electrical, 1: mechanical
+ * bit4: FT: Single Frame Transfer: 0: disable, 1: enable
+ * bit3-0: X
+ */
+ comn = 0x0a;
+ if (sd->ctrls[HFLIP].val)
+ comn |= 0x80;
+ if (sd->ctrls[VFLIP].val)
+ comn |= 0x40;
+ i2c_w1(&sd->gspca_dev, 0x1e, comn);
+ break;
+ }
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w1(gspca_dev, 0x99, sd->ctrls[SHARPNESS].val);
+}
+
+static void setillum(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (gspca_dev->ctrl_dis & (1 << ILLUM))
+ return;
+ switch (sd->sensor) {
+ case SENSOR_ADCM1700:
+ reg_w1(gspca_dev, 0x02, /* gpio */
+ sd->ctrls[ILLUM].val ? 0x64 : 0x60);
+ break;
+ case SENSOR_MT9V111:
+ reg_w1(gspca_dev, 0x02,
+ sd->ctrls[ILLUM].val ? 0x77 : 0x74);
+/* should have been: */
+/* 0x55 : 0x54); * 370i */
+/* 0x66 : 0x64); * Clip */
+ break;
+ }
+}
+
+static void setfreq(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (gspca_dev->ctrl_dis & (1 << FREQ))
+ return;
+ if (sd->sensor == SENSOR_OV7660) {
+ u8 com8;
+
+ com8 = 0xdf; /* auto gain/wb/expo */
+ switch (sd->ctrls[FREQ].val) {
+ case 0: /* Banding filter disabled */
+ i2c_w1(gspca_dev, 0x13, com8 | 0x20);
+ break;
+ case 1: /* 50 hz */
+ i2c_w1(gspca_dev, 0x13, com8);
+ i2c_w1(gspca_dev, 0x3b, 0x0a);
+ break;
+ case 2: /* 60 hz */
+ i2c_w1(gspca_dev, 0x13, com8);
+ i2c_w1(gspca_dev, 0x3b, 0x02);
+ break;
+ }
+ } else {
+ u8 reg2a = 0, reg2b = 0, reg2d = 0;
+
+ /* Get reg2a / reg2d base values */
+ switch (sd->sensor) {
+ case SENSOR_OV7630:
+ reg2a = 0x08;
+ reg2d = 0x01;
+ break;
+ case SENSOR_OV7648:
+ reg2a = 0x11;
+ reg2d = 0x81;
+ break;
+ }
+
+ switch (sd->ctrls[FREQ].val) {
+ case 0: /* Banding filter disabled */
+ break;
+ case 1: /* 50 hz (filter on and framerate adj) */
+ reg2a |= 0x80;
+ reg2b = 0xac;
+ reg2d |= 0x04;
+ break;
+ case 2: /* 60 hz (filter on, no framerate adj) */
+ reg2a |= 0x80;
+ reg2d |= 0x04;
+ break;
+ }
+ i2c_w1(gspca_dev, 0x2a, reg2a);
+ i2c_w1(gspca_dev, 0x2b, reg2b);
+ i2c_w1(gspca_dev, 0x2d, reg2d);
+ }
+}
+
+static void setjpegqual(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+#if USB_BUF_SZ < 64
+#error "No room enough in usb_buf for quantization table"
+#endif
+ memcpy(gspca_dev->usb_buf, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64);
+ usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0x08,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ 0x0100, 0,
+ gspca_dev->usb_buf, 64,
+ 500);
+ memcpy(gspca_dev->usb_buf, &sd->jpeg_hdr[JPEG_QT1_OFFSET], 64);
+ usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0x08,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ 0x0140, 0,
+ gspca_dev->usb_buf, 64,
+ 500);
+
+ sd->reg18 ^= 0x40;
+ reg_w1(gspca_dev, 0x18, sd->reg18);
+}
+
+/* JPEG quality update */
+/* This function is executed from a work queue. */
+static void qual_upd(struct work_struct *work)
+{
+ struct sd *sd = container_of(work, struct sd, work);
+ struct gspca_dev *gspca_dev = &sd->gspca_dev;
+
+ mutex_lock(&gspca_dev->usb_lock);
+ PDEBUG(D_STREAM, "qual_upd %d%%", sd->quality);
+ setjpegqual(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+ u8 reg01, reg17;
+ u8 reg0102[2];
+ const u8 *sn9c1xx;
+ const u8 (*init)[8];
+ const u8 *reg9a;
+ int mode;
+ static const u8 reg9a_def[] =
+ {0x00, 0x40, 0x20, 0x00, 0x00, 0x00};
+ static const u8 reg9a_spec[] =
+ {0x00, 0x40, 0x38, 0x30, 0x00, 0x20};
+ static const u8 regd4[] = {0x60, 0x00, 0x00};
+ static const u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f };
+ static const u8 CA[] = { 0x28, 0xd8, 0x14, 0xec };
+ static const u8 CA_adcm1700[] =
+ { 0x14, 0xec, 0x0a, 0xf6 };
+ static const u8 CA_po2030n[] =
+ { 0x1e, 0xe2, 0x14, 0xec };
+ static const u8 CE[] = { 0x32, 0xdd, 0x2d, 0xdd }; /* MI0360 */
+ static const u8 CE_gc0307[] =
+ { 0x32, 0xce, 0x2d, 0xd3 };
+ static const u8 CE_ov76xx[] =
+ { 0x32, 0xdd, 0x32, 0xdd };
+ static const u8 CE_po2030n[] =
+ { 0x14, 0xe7, 0x1e, 0xdd };
+
+ /* create the JPEG header */
+ jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
+ 0x21); /* JPEG 422 */
+
+ /* initialize the bridge */
+ sn9c1xx = sn_tb[sd->sensor];
+
+ /* sensor clock already enabled in sd_init */
+ /* reg_w1(gspca_dev, 0xf1, 0x00); */
+ reg01 = sn9c1xx[1];
+ if (sd->flags & F_PDN_INV)
+ reg01 ^= S_PDN_INV; /* power down inverted */
+ reg_w1(gspca_dev, 0x01, reg01);
+
+ /* configure gpio */
+ reg0102[0] = reg01;
+ reg0102[1] = sn9c1xx[2];
+ if (gspca_dev->audio)
+ reg0102[1] |= 0x04; /* keep the audio connection */
+ reg_w(gspca_dev, 0x01, reg0102, 2);
+ reg_w(gspca_dev, 0x08, &sn9c1xx[8], 2);
+ reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 5);
+ switch (sd->sensor) {
+ case SENSOR_GC0307:
+ case SENSOR_OV7660:
+ case SENSOR_PO1030:
+ case SENSOR_PO2030N:
+ case SENSOR_SOI768:
+ case SENSOR_SP80708:
+ reg9a = reg9a_spec;
+ break;
+ default:
+ reg9a = reg9a_def;
+ break;
+ }
+ reg_w(gspca_dev, 0x9a, reg9a, 6);
+
+ reg_w(gspca_dev, 0xd4, regd4, sizeof regd4);
+
+ reg_w(gspca_dev, 0x03, &sn9c1xx[3], 0x0f);
+
+ reg17 = sn9c1xx[0x17];
+ switch (sd->sensor) {
+ case SENSOR_GC0307:
+ msleep(50); /*fixme: is it useful? */
+ break;
+ case SENSOR_OM6802:
+ msleep(10);
+ reg_w1(gspca_dev, 0x02, 0x73);
+ reg17 |= SEN_CLK_EN;
+ reg_w1(gspca_dev, 0x17, reg17);
+ reg_w1(gspca_dev, 0x01, 0x22);
+ msleep(100);
+ reg01 = SCL_SEL_OD | S_PDN_INV;
+ reg17 &= ~MCK_SIZE_MASK;
+ reg17 |= 0x04; /* clock / 4 */
+ break;
+ }
+ reg01 |= SYS_SEL_48M;
+ reg_w1(gspca_dev, 0x01, reg01);
+ reg17 |= SEN_CLK_EN;
+ reg_w1(gspca_dev, 0x17, reg17);
+ reg01 &= ~S_PWR_DN; /* sensor power on */
+ reg_w1(gspca_dev, 0x01, reg01);
+ reg01 &= ~SCL_SEL_OD; /* remove open-drain mode */
+ reg_w1(gspca_dev, 0x01, reg01);
+
+ switch (sd->sensor) {
+ case SENSOR_HV7131R:
+ hv7131r_probe(gspca_dev); /*fixme: is it useful? */
+ break;
+ case SENSOR_OM6802:
+ msleep(10);
+ reg_w1(gspca_dev, 0x01, reg01);
+ i2c_w8(gspca_dev, om6802_init0[0]);
+ i2c_w8(gspca_dev, om6802_init0[1]);
+ msleep(15);
+ reg_w1(gspca_dev, 0x02, 0x71);
+ msleep(150);
+ break;
+ case SENSOR_SP80708:
+ msleep(100);
+ reg_w1(gspca_dev, 0x02, 0x62);
+ break;
+ }
+
+ /* initialize the sensor */
+ i2c_w_seq(gspca_dev, sensor_init[sd->sensor]);
+
+ reg_w1(gspca_dev, 0x15, sn9c1xx[0x15]);
+ reg_w1(gspca_dev, 0x16, sn9c1xx[0x16]);
+ reg_w1(gspca_dev, 0x12, sn9c1xx[0x12]);
+ reg_w1(gspca_dev, 0x13, sn9c1xx[0x13]);
+ reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]);
+ if (sd->sensor == SENSOR_ADCM1700) {
+ reg_w1(gspca_dev, 0xd2, 0x3a); /* AE_H_SIZE = 116 */
+ reg_w1(gspca_dev, 0xd3, 0x30); /* AE_V_SIZE = 96 */
+ } else {
+ reg_w1(gspca_dev, 0xd2, 0x6a); /* AE_H_SIZE = 212 */
+ reg_w1(gspca_dev, 0xd3, 0x50); /* AE_V_SIZE = 160 */
+ }
+ reg_w1(gspca_dev, 0xc6, 0x00);
+ reg_w1(gspca_dev, 0xc7, 0x00);
+ if (sd->sensor == SENSOR_ADCM1700) {
+ reg_w1(gspca_dev, 0xc8, 0x2c); /* AW_H_STOP = 352 */
+ reg_w1(gspca_dev, 0xc9, 0x24); /* AW_V_STOP = 288 */
+ } else {
+ reg_w1(gspca_dev, 0xc8, 0x50); /* AW_H_STOP = 640 */
+ reg_w1(gspca_dev, 0xc9, 0x3c); /* AW_V_STOP = 480 */
+ }
+ reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]);
+ switch (sd->sensor) {
+ case SENSOR_OM6802:
+/* case SENSOR_OV7648: * fixme: sometimes */
+ break;
+ default:
+ reg17 |= DEF_EN;
+ break;
+ }
+ reg_w1(gspca_dev, 0x17, reg17);
+
+ reg_w1(gspca_dev, 0x05, 0x00); /* red */
+ reg_w1(gspca_dev, 0x07, 0x00); /* green */
+ reg_w1(gspca_dev, 0x06, 0x00); /* blue */
+ reg_w1(gspca_dev, 0x14, sn9c1xx[0x14]);
+
+ setgamma(gspca_dev);
+
+/*fixme: 8 times with all zeroes and 1 or 2 times with normal values */
+ for (i = 0; i < 8; i++)
+ reg_w(gspca_dev, 0x84, reg84, sizeof reg84);
+ switch (sd->sensor) {
+ case SENSOR_ADCM1700:
+ case SENSOR_OV7660:
+ case SENSOR_SP80708:
+ reg_w1(gspca_dev, 0x9a, 0x05);
+ break;
+ case SENSOR_GC0307:
+ case SENSOR_MT9V111:
+ case SENSOR_MI0360B:
+ reg_w1(gspca_dev, 0x9a, 0x07);
+ break;
+ case SENSOR_OV7630:
+ case SENSOR_OV7648:
+ reg_w1(gspca_dev, 0x9a, 0x0a);
+ break;
+ case SENSOR_PO2030N:
+ case SENSOR_SOI768:
+ reg_w1(gspca_dev, 0x9a, 0x06);
+ break;
+ default:
+ reg_w1(gspca_dev, 0x9a, 0x08);
+ break;
+ }
+ setsharpness(gspca_dev);
+
+ reg_w(gspca_dev, 0x84, reg84, sizeof reg84);
+ reg_w1(gspca_dev, 0x05, 0x20); /* red */
+ reg_w1(gspca_dev, 0x07, 0x20); /* green */
+ reg_w1(gspca_dev, 0x06, 0x20); /* blue */
+
+ init = NULL;
+ mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+ reg01 |= SYS_SEL_48M | V_TX_EN;
+ reg17 &= ~MCK_SIZE_MASK;
+ reg17 |= 0x02; /* clock / 2 */
+ switch (sd->sensor) {
+ case SENSOR_ADCM1700:
+ init = adcm1700_sensor_param1;
+ break;
+ case SENSOR_GC0307:
+ init = gc0307_sensor_param1;
+ break;
+ case SENSOR_HV7131R:
+ case SENSOR_MI0360:
+ if (!mode)
+ reg01 &= ~SYS_SEL_48M; /* 640x480: clk 24Mhz */
+ reg17 &= ~MCK_SIZE_MASK;
+ reg17 |= 0x01; /* clock / 1 */
+ break;
+ case SENSOR_MI0360B:
+ init = mi0360b_sensor_param1;
+ break;
+ case SENSOR_MO4000:
+ if (mode) { /* if 320x240 */
+ reg01 &= ~SYS_SEL_48M; /* clk 24Mz */
+ reg17 &= ~MCK_SIZE_MASK;
+ reg17 |= 0x01; /* clock / 1 */
+ }
+ break;
+ case SENSOR_MT9V111:
+ init = mt9v111_sensor_param1;
+ break;
+ case SENSOR_OM6802:
+ init = om6802_sensor_param1;
+ if (!mode) { /* if 640x480 */
+ reg17 &= ~MCK_SIZE_MASK;
+ reg17 |= 0x04; /* clock / 4 */
+ } else {
+ reg01 &= ~SYS_SEL_48M; /* clk 24Mz */
+ reg17 &= ~MCK_SIZE_MASK;
+ reg17 |= 0x02; /* clock / 2 */
+ }
+ break;
+ case SENSOR_OV7630:
+ init = ov7630_sensor_param1;
+ break;
+ case SENSOR_OV7648:
+ init = ov7648_sensor_param1;
+ reg17 &= ~MCK_SIZE_MASK;
+ reg17 |= 0x01; /* clock / 1 */
+ break;
+ case SENSOR_OV7660:
+ init = ov7660_sensor_param1;
+ break;
+ case SENSOR_PO1030:
+ init = po1030_sensor_param1;
+ break;
+ case SENSOR_PO2030N:
+ init = po2030n_sensor_param1;
+ break;
+ case SENSOR_SOI768:
+ init = soi768_sensor_param1;
+ break;
+ case SENSOR_SP80708:
+ init = sp80708_sensor_param1;
+ break;
+ }
+
+ /* more sensor initialization - param1 */
+ if (init != NULL) {
+ i2c_w_seq(gspca_dev, init);
+/* init = NULL; */
+ }
+
+ reg_w(gspca_dev, 0xc0, C0, 6);
+ switch (sd->sensor) {
+ case SENSOR_ADCM1700:
+ case SENSOR_GC0307:
+ case SENSOR_SOI768:
+ reg_w(gspca_dev, 0xca, CA_adcm1700, 4);
+ break;
+ case SENSOR_PO2030N:
+ reg_w(gspca_dev, 0xca, CA_po2030n, 4);
+ break;
+ default:
+ reg_w(gspca_dev, 0xca, CA, 4);
+ break;
+ }
+ switch (sd->sensor) {
+ case SENSOR_ADCM1700:
+ case SENSOR_OV7630:
+ case SENSOR_OV7648:
+ case SENSOR_OV7660:
+ case SENSOR_SOI768:
+ reg_w(gspca_dev, 0xce, CE_ov76xx, 4);
+ break;
+ case SENSOR_GC0307:
+ reg_w(gspca_dev, 0xce, CE_gc0307, 4);
+ break;
+ case SENSOR_PO2030N:
+ reg_w(gspca_dev, 0xce, CE_po2030n, 4);
+ break;
+ default:
+ reg_w(gspca_dev, 0xce, CE, 4);
+ /* ?? {0x1e, 0xdd, 0x2d, 0xe7} */
+ break;
+ }
+
+ /* here change size mode 0 -> VGA; 1 -> CIF */
+ sd->reg18 = sn9c1xx[0x18] | (mode << 4) | 0x40;
+ reg_w1(gspca_dev, 0x18, sd->reg18);
+ setjpegqual(gspca_dev);
+
+ reg_w1(gspca_dev, 0x17, reg17);
+ reg_w1(gspca_dev, 0x01, reg01);
+ sd->reg01 = reg01;
+ sd->reg17 = reg17;
+
+ sethvflip(gspca_dev);
+ setbrightness(gspca_dev);
+ setcontrast(gspca_dev);
+ setcolors(gspca_dev);
+ setautogain(gspca_dev);
+ if (!(gspca_dev->ctrl_inac & ((1 << EXPOSURE) | (1 << GAIN)))) {
+ setexposure(gspca_dev);
+ setgain(gspca_dev);
+ }
+ setfreq(gspca_dev);
+
+ sd->pktsz = sd->npkt = 0;
+ sd->nchg = sd->short_mark = 0;
+ sd->work_thread = create_singlethread_workqueue(MODULE_NAME);
+
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ static const u8 stophv7131[] =
+ { 0xa1, 0x11, 0x02, 0x09, 0x00, 0x00, 0x00, 0x10 };
+ static const u8 stopmi0360[] =
+ { 0xb1, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10 };
+ static const u8 stopov7648[] =
+ { 0xa1, 0x21, 0x76, 0x20, 0x00, 0x00, 0x00, 0x10 };
+ static const u8 stopsoi768[] =
+ { 0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10 };
+ u8 reg01;
+ u8 reg17;
+
+ reg01 = sd->reg01;
+ reg17 = sd->reg17 & ~SEN_CLK_EN;
+ switch (sd->sensor) {
+ case SENSOR_ADCM1700:
+ case SENSOR_GC0307:
+ case SENSOR_PO2030N:
+ case SENSOR_SP80708:
+ reg01 |= LED;
+ reg_w1(gspca_dev, 0x01, reg01);
+ reg01 &= ~(LED | V_TX_EN);
+ reg_w1(gspca_dev, 0x01, reg01);
+/* reg_w1(gspca_dev, 0x02, 0x??); * LED off ? */
+ break;
+ case SENSOR_HV7131R:
+ reg01 &= ~V_TX_EN;
+ reg_w1(gspca_dev, 0x01, reg01);
+ i2c_w8(gspca_dev, stophv7131);
+ break;
+ case SENSOR_MI0360:
+ case SENSOR_MI0360B:
+ reg01 &= ~V_TX_EN;
+ reg_w1(gspca_dev, 0x01, reg01);
+/* reg_w1(gspca_dev, 0x02, 0x40); * LED off ? */
+ i2c_w8(gspca_dev, stopmi0360);
+ break;
+ case SENSOR_MT9V111:
+ case SENSOR_OM6802:
+ case SENSOR_PO1030:
+ reg01 &= ~V_TX_EN;
+ reg_w1(gspca_dev, 0x01, reg01);
+ break;
+ case SENSOR_OV7630:
+ case SENSOR_OV7648:
+ reg01 &= ~V_TX_EN;
+ reg_w1(gspca_dev, 0x01, reg01);
+ i2c_w8(gspca_dev, stopov7648);
+ break;
+ case SENSOR_OV7660:
+ reg01 &= ~V_TX_EN;
+ reg_w1(gspca_dev, 0x01, reg01);
+ break;
+ case SENSOR_SOI768:
+ i2c_w8(gspca_dev, stopsoi768);
+ break;
+ }
+
+ reg01 |= SCL_SEL_OD;
+ reg_w1(gspca_dev, 0x01, reg01);
+ reg01 |= S_PWR_DN; /* sensor power down */
+ reg_w1(gspca_dev, 0x01, reg01);
+ reg_w1(gspca_dev, 0x17, reg17);
+ reg01 &= ~SYS_SEL_48M; /* clock 24MHz */
+ reg_w1(gspca_dev, 0x01, reg01);
+ reg01 |= LED;
+ reg_w1(gspca_dev, 0x01, reg01);
+ /* Don't disable sensor clock as that disables the button on the cam */
+ /* reg_w1(gspca_dev, 0xf1, 0x01); */
+}
+
+/* called on streamoff with alt==0 and on disconnect */
+/* the usb_lock is held at entry - restore on exit */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->work_thread != NULL) {
+ mutex_unlock(&gspca_dev->usb_lock);
+ destroy_workqueue(sd->work_thread);
+ mutex_lock(&gspca_dev->usb_lock);
+ sd->work_thread = NULL;
+ }
+}
+
+/* !! coarse_grained_expo_autogain is not used !! */
+#define exp_too_low_cnt bridge
+#define exp_too_high_cnt sensor
+
+#include "autogain_functions.h"
+
+static void do_autogain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int delta;
+ int expotimes;
+ u8 luma_mean = 130;
+ u8 luma_delta = 20;
+
+ /* Thanks S., without your advice, autobright should not work :) */
+ if (sd->ag_cnt < 0)
+ return;
+ if (--sd->ag_cnt >= 0)
+ return;
+ sd->ag_cnt = AG_CNT_START;
+
+ delta = atomic_read(&sd->avg_lum);
+ PDEBUG(D_FRAM, "mean lum %d", delta);
+
+ if (sd->sensor == SENSOR_PO2030N) {
+ auto_gain_n_exposure(gspca_dev, delta, luma_mean, luma_delta,
+ 15, 1024);
+ return;
+ }
+
+ if (delta < luma_mean - luma_delta ||
+ delta > luma_mean + luma_delta) {
+ switch (sd->sensor) {
+ case SENSOR_GC0307:
+ expotimes = sd->exposure;
+ expotimes += (luma_mean - delta) >> 6;
+ if (expotimes < 0)
+ expotimes = 0;
+ sd->exposure = expo_adjust(gspca_dev,
+ (unsigned int) expotimes);
+ break;
+ case SENSOR_HV7131R:
+ expotimes = sd->exposure >> 8;
+ expotimes += (luma_mean - delta) >> 4;
+ if (expotimes < 0)
+ expotimes = 0;
+ sd->exposure = expo_adjust(gspca_dev,
+ (unsigned int) (expotimes << 8));
+ break;
+ case SENSOR_OM6802:
+ case SENSOR_MT9V111:
+ expotimes = sd->exposure;
+ expotimes += (luma_mean - delta) >> 2;
+ if (expotimes < 0)
+ expotimes = 0;
+ sd->exposure = expo_adjust(gspca_dev,
+ (unsigned int) expotimes);
+ setredblue(gspca_dev);
+ break;
+ default:
+/* case SENSOR_MO4000: */
+/* case SENSOR_MI0360: */
+/* case SENSOR_MI0360B: */
+ expotimes = sd->exposure;
+ expotimes += (luma_mean - delta) >> 6;
+ if (expotimes < 0)
+ expotimes = 0;
+ sd->exposure = expo_adjust(gspca_dev,
+ (unsigned int) expotimes);
+ setredblue(gspca_dev);
+ break;
+ }
+ }
+}
+
+/* set the average luminosity from an isoc marker */
+static void set_lum(struct sd *sd,
+ u8 *data)
+{
+ int avg_lum;
+
+ /* w0 w1 w2
+ * w3 w4 w5
+ * w6 w7 w8
+ */
+ avg_lum = (data[27] << 8) + data[28] /* w3 */
+
+ + (data[31] << 8) + data[32] /* w5 */
+
+ + (data[23] << 8) + data[24] /* w1 */
+
+ + (data[35] << 8) + data[36] /* w7 */
+
+ + (data[29] << 10) + (data[30] << 2); /* w4 * 4 */
+ avg_lum >>= 10;
+ atomic_set(&sd->avg_lum, avg_lum);
+}
+
+/* scan the URB packets */
+/* This function is run at interrupt level. */
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, new_qual;
+
+ /*
+ * A frame ends on the marker
+ * ff ff 00 c4 c4 96 ..
+ * which is 62 bytes long and is followed by various information
+ * including statuses and luminosity.
+ *
+ * A marker may be splitted on two packets.
+ *
+ * The 6th byte of a marker contains the bits:
+ * 0x08: USB full
+ * 0xc0: frame sequence
+ * When the bit 'USB full' is set, the frame must be discarded;
+ * this is also the case when the 2 bytes before the marker are
+ * not the JPEG end of frame ('ff d9').
+ */
+
+ /* count the packets and their size */
+ sd->npkt++;
+ sd->pktsz += len;
+
+/*fixme: assumption about the following code:
+ * - there can be only one marker in a packet
+ */
+
+ /* skip the remaining bytes of a short marker */
+ i = sd->short_mark;
+ if (i != 0) {
+ sd->short_mark = 0;
+ if (i < 0 /* if 'ff' at end of previous packet */
+ && data[0] == 0xff
+ && data[1] == 0x00)
+ goto marker_found;
+ if (data[0] == 0xff && data[1] == 0xff) {
+ i = 0;
+ goto marker_found;
+ }
+ len -= i;
+ if (len <= 0)
+ return;
+ data += i;
+ }
+
+ /* search backwards if there is a marker in the packet */
+ for (i = len - 1; --i >= 0; ) {
+ if (data[i] != 0xff) {
+ i--;
+ continue;
+ }
+ if (data[i + 1] == 0xff) {
+
+ /* (there may be 'ff ff' inside a marker) */
+ if (i + 2 >= len || data[i + 2] == 0x00)
+ goto marker_found;
+ }
+ }
+
+ /* no marker found */
+ /* add the JPEG header if first fragment */
+ if (data[len - 1] == 0xff)
+ sd->short_mark = -1;
+ if (gspca_dev->last_packet_type == LAST_PACKET)
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ return;
+
+ /* marker found */
+ /* if some error, discard the frame and decrease the quality */
+marker_found:
+ new_qual = 0;
+ if (i > 2) {
+ if (data[i - 2] != 0xff || data[i - 1] != 0xd9) {
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ new_qual = -3;
+ }
+ } else if (i + 6 < len) {
+ if (data[i + 6] & 0x08) {
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ new_qual = -5;
+ }
+ }
+
+ gspca_frame_add(gspca_dev, LAST_PACKET, data, i);
+
+ /* compute the filling rate and a new JPEG quality */
+ if (new_qual == 0) {
+ int r;
+
+ r = (sd->pktsz * 100) /
+ (sd->npkt *
+ gspca_dev->urb[0]->iso_frame_desc[0].length);
+ if (r >= 85)
+ new_qual = -3;
+ else if (r < 75)
+ new_qual = 2;
+ }
+ if (new_qual != 0) {
+ sd->nchg += new_qual;
+ if (sd->nchg < -6 || sd->nchg >= 12) {
+ sd->nchg = 0;
+ new_qual += sd->quality;
+ if (new_qual < QUALITY_MIN)
+ new_qual = QUALITY_MIN;
+ else if (new_qual > QUALITY_MAX)
+ new_qual = QUALITY_MAX;
+ if (new_qual != sd->quality) {
+ sd->quality = new_qual;
+ queue_work(sd->work_thread, &sd->work);
+ }
+ }
+ } else {
+ sd->nchg = 0;
+ }
+ sd->pktsz = sd->npkt = 0;
+
+ /* if the marker is smaller than 62 bytes,
+ * memorize the number of bytes to skip in the next packet */
+ if (i + 62 > len) { /* no more usable data */
+ sd->short_mark = i + 62 - len;
+ return;
+ }
+ if (sd->ag_cnt >= 0)
+ set_lum(sd, data + i);
+
+ /* if more data, start a new frame */
+ i += 62;
+ if (i < len) {
+ data += i;
+ len -= i;
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ }
+}
+
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->ctrls[AUTOGAIN].val = val;
+ if (val)
+ gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN);
+ else
+ gspca_dev->ctrl_inac &= ~(1 << EXPOSURE) & ~(1 << GAIN);
+ if (gspca_dev->streaming)
+ setautogain(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_querymenu(struct gspca_dev *gspca_dev,
+ struct v4l2_querymenu *menu)
+{
+ switch (menu->id) {
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ switch (menu->index) {
+ case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
+ strcpy((char *) menu->name, "NoFliker");
+ return 0;
+ case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
+ strcpy((char *) menu->name, "50 Hz");
+ return 0;
+ case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
+ strcpy((char *) menu->name, "60 Hz");
+ return 0;
+ }
+ break;
+ }
+ return -EINVAL;
+}
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* interrupt packet data */
+ int len) /* interrupt packet length */
+{
+ int ret = -EINVAL;
+
+ if (len == 1 && data[0] == 1) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+ input_sync(gspca_dev->input_dev);
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ ret = 0;
+ }
+
+ return ret;
+}
+#endif
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = NCTRLS,
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+ .dq_callback = do_autogain,
+ .querymenu = sd_querymenu,
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ .int_pkt_scan = sd_int_pkt_scan,
+#endif
+};
+
+/* -- module initialisation -- */
+#define BS(bridge, sensor) \
+ .driver_info = (BRIDGE_ ## bridge << 16) \
+ | (SENSOR_ ## sensor << 8)
+#define BSF(bridge, sensor, flags) \
+ .driver_info = (BRIDGE_ ## bridge << 16) \
+ | (SENSOR_ ## sensor << 8) \
+ | (flags)
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x0458, 0x7025), BS(SN9C120, MI0360)},
+ {USB_DEVICE(0x0458, 0x702e), BS(SN9C120, OV7660)},
+ {USB_DEVICE(0x045e, 0x00f5), BSF(SN9C105, OV7660, F_PDN_INV)},
+ {USB_DEVICE(0x045e, 0x00f7), BSF(SN9C105, OV7660, F_PDN_INV)},
+ {USB_DEVICE(0x0471, 0x0327), BS(SN9C105, MI0360)},
+ {USB_DEVICE(0x0471, 0x0328), BS(SN9C105, MI0360)},
+ {USB_DEVICE(0x0471, 0x0330), BS(SN9C105, MI0360)},
+ {USB_DEVICE(0x06f8, 0x3004), BS(SN9C105, OV7660)},
+ {USB_DEVICE(0x06f8, 0x3008), BS(SN9C105, OV7660)},
+/* {USB_DEVICE(0x0c45, 0x603a), BS(SN9C102P, OV7648)}, */
+ {USB_DEVICE(0x0c45, 0x6040), BS(SN9C102P, HV7131R)},
+/* {USB_DEVICE(0x0c45, 0x607a), BS(SN9C102P, OV7648)}, */
+/* {USB_DEVICE(0x0c45, 0x607b), BS(SN9C102P, OV7660)}, */
+ {USB_DEVICE(0x0c45, 0x607c), BS(SN9C102P, HV7131R)},
+/* {USB_DEVICE(0x0c45, 0x607e), BS(SN9C102P, OV7630)}, */
+ {USB_DEVICE(0x0c45, 0x60c0), BSF(SN9C105, MI0360, F_ILLUM)},
+ /* or MT9V111 */
+/* {USB_DEVICE(0x0c45, 0x60c2), BS(SN9C105, P1030xC)}, */
+/* {USB_DEVICE(0x0c45, 0x60c8), BS(SN9C105, OM6802)}, */
+/* {USB_DEVICE(0x0c45, 0x60cc), BS(SN9C105, HV7131GP)}, */
+ {USB_DEVICE(0x0c45, 0x60ce), BS(SN9C105, SP80708)},
+ {USB_DEVICE(0x0c45, 0x60ec), BS(SN9C105, MO4000)},
+/* {USB_DEVICE(0x0c45, 0x60ef), BS(SN9C105, ICM105C)}, */
+/* {USB_DEVICE(0x0c45, 0x60fa), BS(SN9C105, OV7648)}, */
+/* {USB_DEVICE(0x0c45, 0x60f2), BS(SN9C105, OV7660)}, */
+ {USB_DEVICE(0x0c45, 0x60fb), BS(SN9C105, OV7660)},
+ {USB_DEVICE(0x0c45, 0x60fc), BS(SN9C105, HV7131R)},
+ {USB_DEVICE(0x0c45, 0x60fe), BS(SN9C105, OV7630)},
+ {USB_DEVICE(0x0c45, 0x6100), BS(SN9C120, MI0360)}, /*sn9c128*/
+ {USB_DEVICE(0x0c45, 0x6102), BS(SN9C120, PO2030N)}, /* /GC0305*/
+/* {USB_DEVICE(0x0c45, 0x6108), BS(SN9C120, OM6802)}, */
+ {USB_DEVICE(0x0c45, 0x610a), BS(SN9C120, OV7648)}, /*sn9c128*/
+ {USB_DEVICE(0x0c45, 0x610b), BS(SN9C120, OV7660)}, /*sn9c128*/
+ {USB_DEVICE(0x0c45, 0x610c), BS(SN9C120, HV7131R)}, /*sn9c128*/
+ {USB_DEVICE(0x0c45, 0x610e), BS(SN9C120, OV7630)}, /*sn9c128*/
+/* {USB_DEVICE(0x0c45, 0x610f), BS(SN9C120, S5K53BEB)}, */
+/* {USB_DEVICE(0x0c45, 0x6122), BS(SN9C110, ICM105C)}, */
+/* {USB_DEVICE(0x0c45, 0x6123), BS(SN9C110, SanyoCCD)}, */
+ {USB_DEVICE(0x0c45, 0x6128), BS(SN9C120, OM6802)}, /*sn9c325?*/
+/*bw600.inf:*/
+ {USB_DEVICE(0x0c45, 0x612a), BS(SN9C120, OV7648)}, /*sn9c325?*/
+ {USB_DEVICE(0x0c45, 0x612b), BS(SN9C110, ADCM1700)},
+ {USB_DEVICE(0x0c45, 0x612c), BS(SN9C110, MO4000)},
+ {USB_DEVICE(0x0c45, 0x612e), BS(SN9C110, OV7630)},
+/* {USB_DEVICE(0x0c45, 0x612f), BS(SN9C110, ICM105C)}, */
+ {USB_DEVICE(0x0c45, 0x6130), BS(SN9C120, MI0360)},
+ /* or MT9V111 / MI0360B */
+/* {USB_DEVICE(0x0c45, 0x6132), BS(SN9C120, OV7670)}, */
+ {USB_DEVICE(0x0c45, 0x6138), BS(SN9C120, MO4000)},
+ {USB_DEVICE(0x0c45, 0x613a), BS(SN9C120, OV7648)},
+ {USB_DEVICE(0x0c45, 0x613b), BS(SN9C120, OV7660)},
+ {USB_DEVICE(0x0c45, 0x613c), BS(SN9C120, HV7131R)},
+ {USB_DEVICE(0x0c45, 0x613e), BS(SN9C120, OV7630)},
+ {USB_DEVICE(0x0c45, 0x6142), BS(SN9C120, PO2030N)}, /*sn9c120b*/
+ /* or GC0305 / GC0307 */
+ {USB_DEVICE(0x0c45, 0x6143), BS(SN9C120, SP80708)}, /*sn9c120b*/
+ {USB_DEVICE(0x0c45, 0x6148), BS(SN9C120, OM6802)}, /*sn9c120b*/
+ {USB_DEVICE(0x0c45, 0x614a), BSF(SN9C120, ADCM1700, F_ILLUM)},
+/* {USB_DEVICE(0x0c45, 0x614c), BS(SN9C120, GC0306)}, */ /*sn9c120b*/
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/spca1528.c b/drivers/media/video/gspca/spca1528.c
new file mode 100644
index 00000000..070b9c33
--- /dev/null
+++ b/drivers/media/video/gspca/spca1528.c
@@ -0,0 +1,593 @@
+/*
+ * spca1528 subdriver
+ *
+ * Copyright (C) 2010-2011 Jean-Francois Moine (http://moinejf.free.fr)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "spca1528"
+
+#include "gspca.h"
+#include "jpeg.h"
+
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
+MODULE_DESCRIPTION("SPCA1528 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ u8 brightness;
+ u8 contrast;
+ u8 hue;
+ u8 color;
+ u8 sharpness;
+
+ u8 pkt_seq;
+
+ u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcolor(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcolor(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
+
+static const struct ctrl sd_ctrls[] = {
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+#define BRIGHTNESS_DEF 128
+ .default_value = BRIGHTNESS_DEF,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+ {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 8,
+ .step = 1,
+#define CONTRAST_DEF 1
+ .default_value = CONTRAST_DEF,
+ },
+ .set = sd_setcontrast,
+ .get = sd_getcontrast,
+ },
+ {
+ {
+ .id = V4L2_CID_HUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Hue",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+#define HUE_DEF 0
+ .default_value = HUE_DEF,
+ },
+ .set = sd_sethue,
+ .get = sd_gethue,
+ },
+ {
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 8,
+ .step = 1,
+#define COLOR_DEF 1
+ .default_value = COLOR_DEF,
+ },
+ .set = sd_setcolor,
+ .get = sd_getcolor,
+ },
+ {
+ {
+ .id = V4L2_CID_SHARPNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Sharpness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+#define SHARPNESS_DEF 0
+ .default_value = SHARPNESS_DEF,
+ },
+ .set = sd_setsharpness,
+ .get = sd_getsharpness,
+ },
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+/* (does not work correctly)
+ {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144 * 5 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 3},
+*/
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 4 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 2},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+};
+
+/* read <len> bytes to gspca usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev,
+ u8 req,
+ u16 index,
+ int len)
+{
+#if USB_BUF_SZ < 64
+#error "USB buffer too small"
+#endif
+ struct usb_device *dev = gspca_dev->dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ req,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x0000, /* value */
+ index,
+ gspca_dev->usb_buf, len,
+ 500);
+ PDEBUG(D_USBI, "GET %02x 0000 %04x %02x", req, index,
+ gspca_dev->usb_buf[0]);
+ if (ret < 0) {
+ pr_err("reg_r err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static void reg_w(struct gspca_dev *gspca_dev,
+ u8 req,
+ u16 value,
+ u16 index)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ PDEBUG(D_USBO, "SET %02x %04x %04x", req, value, index);
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ req,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index,
+ NULL, 0, 500);
+ if (ret < 0) {
+ pr_err("reg_w err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static void reg_wb(struct gspca_dev *gspca_dev,
+ u8 req,
+ u16 value,
+ u16 index,
+ u8 byte)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ PDEBUG(D_USBO, "SET %02x %04x %04x %02x", req, value, index, byte);
+ gspca_dev->usb_buf[0] = byte;
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ req,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index,
+ gspca_dev->usb_buf, 1, 500);
+ if (ret < 0) {
+ pr_err("reg_w err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static void wait_status_0(struct gspca_dev *gspca_dev)
+{
+ int i, w;
+
+ i = 16;
+ w = 0;
+ do {
+ reg_r(gspca_dev, 0x21, 0x0000, 1);
+ if (gspca_dev->usb_buf[0] == 0)
+ return;
+ w += 15;
+ msleep(w);
+ } while (--i > 0);
+ PDEBUG(D_ERR, "wait_status_0 timeout");
+ gspca_dev->usb_err = -ETIME;
+}
+
+static void wait_status_1(struct gspca_dev *gspca_dev)
+{
+ int i;
+
+ i = 10;
+ do {
+ reg_r(gspca_dev, 0x21, 0x0001, 1);
+ msleep(10);
+ if (gspca_dev->usb_buf[0] == 1) {
+ reg_wb(gspca_dev, 0x21, 0x0000, 0x0001, 0x00);
+ reg_r(gspca_dev, 0x21, 0x0001, 1);
+ return;
+ }
+ } while (--i > 0);
+ PDEBUG(D_ERR, "wait_status_1 timeout");
+ gspca_dev->usb_err = -ETIME;
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_wb(gspca_dev, 0xc0, 0x0000, 0x00c0, sd->brightness);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_wb(gspca_dev, 0xc1, 0x0000, 0x00c1, sd->contrast);
+}
+
+static void sethue(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_wb(gspca_dev, 0xc2, 0x0000, 0x0000, sd->hue);
+}
+
+static void setcolor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_wb(gspca_dev, 0xc3, 0x0000, 0x00c3, sd->color);
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_wb(gspca_dev, 0xc4, 0x0000, 0x00c4, sd->sharpness);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ gspca_dev->cam.cam_mode = vga_mode;
+ gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
+ gspca_dev->cam.npkt = 128; /* number of packets per ISOC message */
+ /*fixme: 256 in ms-win traces*/
+
+ sd->brightness = BRIGHTNESS_DEF;
+ sd->contrast = CONTRAST_DEF;
+ sd->hue = HUE_DEF;
+ sd->color = COLOR_DEF;
+ sd->sharpness = SHARPNESS_DEF;
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ reg_w(gspca_dev, 0x00, 0x0001, 0x2067);
+ reg_w(gspca_dev, 0x00, 0x00d0, 0x206b);
+ reg_w(gspca_dev, 0x00, 0x0000, 0x206c);
+ reg_w(gspca_dev, 0x00, 0x0001, 0x2069);
+ msleep(8);
+ reg_w(gspca_dev, 0x00, 0x00c0, 0x206b);
+ reg_w(gspca_dev, 0x00, 0x0000, 0x206c);
+ reg_w(gspca_dev, 0x00, 0x0001, 0x2069);
+
+ reg_r(gspca_dev, 0x20, 0x0000, 1);
+ reg_r(gspca_dev, 0x20, 0x0000, 5);
+ reg_r(gspca_dev, 0x23, 0x0000, 64);
+ PDEBUG(D_PROBE, "%s%s", &gspca_dev->usb_buf[0x1c],
+ &gspca_dev->usb_buf[0x30]);
+ reg_r(gspca_dev, 0x23, 0x0001, 64);
+ return gspca_dev->usb_err;
+}
+
+/* function called at start time before URB creation */
+static int sd_isoc_init(struct gspca_dev *gspca_dev)
+{
+ u8 mode;
+
+ reg_r(gspca_dev, 0x00, 0x2520, 1);
+ wait_status_0(gspca_dev);
+ reg_w(gspca_dev, 0xc5, 0x0003, 0x0000);
+ wait_status_1(gspca_dev);
+
+ wait_status_0(gspca_dev);
+ mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+ reg_wb(gspca_dev, 0x25, 0x0000, 0x0004, mode);
+ reg_r(gspca_dev, 0x25, 0x0004, 1);
+ reg_wb(gspca_dev, 0x27, 0x0000, 0x0000, 0x06); /* 420 */
+ reg_r(gspca_dev, 0x27, 0x0000, 1);
+
+/* not useful..
+ gspca_dev->alt = 4; * use alternate setting 3 */
+
+ return gspca_dev->usb_err;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* initialize the JPEG header */
+ jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
+ 0x22); /* JPEG 411 */
+
+ /* the JPEG quality shall be 85% */
+ jpeg_set_qual(sd->jpeg_hdr, 85);
+
+ /* set the controls */
+ setbrightness(gspca_dev);
+ setcontrast(gspca_dev);
+ sethue(gspca_dev);
+ setcolor(gspca_dev);
+ setsharpness(gspca_dev);
+
+ msleep(5);
+ reg_r(gspca_dev, 0x00, 0x2520, 1);
+ msleep(8);
+
+ /* start the capture */
+ wait_status_0(gspca_dev);
+ reg_w(gspca_dev, 0x31, 0x0000, 0x0004); /* start request */
+ wait_status_1(gspca_dev);
+ wait_status_0(gspca_dev);
+ msleep(200);
+
+ sd->pkt_seq = 0;
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ /* stop the capture */
+ wait_status_0(gspca_dev);
+ reg_w(gspca_dev, 0x31, 0x0000, 0x0000); /* stop request */
+ wait_status_1(gspca_dev);
+ wait_status_0(gspca_dev);
+}
+
+/* move a packet adding 0x00 after 0xff */
+static void add_packet(struct gspca_dev *gspca_dev,
+ u8 *data,
+ int len)
+{
+ int i;
+
+ i = 0;
+ do {
+ if (data[i] == 0xff) {
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data, i + 1);
+ len -= i;
+ data += i;
+ *data = 0x00;
+ i = 0;
+ }
+ } while (++i < len);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ static const u8 ffd9[] = {0xff, 0xd9};
+
+ /* image packets start with:
+ * 02 8n
+ * with <n> bit:
+ * 0x01: even (0) / odd (1) image
+ * 0x02: end of image when set
+ */
+ if (len < 3)
+ return; /* empty packet */
+ if (*data == 0x02) {
+ if (data[1] & 0x02) {
+ sd->pkt_seq = !(data[1] & 1);
+ add_packet(gspca_dev, data + 2, len - 2);
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ ffd9, 2);
+ return;
+ }
+ if ((data[1] & 1) != sd->pkt_seq)
+ goto err;
+ if (gspca_dev->last_packet_type == LAST_PACKET)
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+ add_packet(gspca_dev, data + 2, len - 2);
+ return;
+ }
+err:
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->brightness = val;
+ if (gspca_dev->streaming)
+ setbrightness(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->brightness;
+ return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->contrast = val;
+ if (gspca_dev->streaming)
+ setcontrast(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->contrast;
+ return 0;
+}
+
+static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->hue = val;
+ if (gspca_dev->streaming)
+ sethue(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->hue;
+ return 0;
+}
+
+static int sd_setcolor(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->color = val;
+ if (gspca_dev->streaming)
+ setcolor(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_getcolor(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->color;
+ return 0;
+}
+
+static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->sharpness = val;
+ if (gspca_dev->streaming)
+ setsharpness(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->sharpness;
+ return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .isoc_init = sd_isoc_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x04fc, 0x1528)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ /* the video interface for isochronous transfer is 1 */
+ if (intf->cur_altsetting->desc.bInterfaceNumber != 1)
+ return -ENODEV;
+
+ return gspca_dev_probe2(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c
new file mode 100644
index 00000000..10398470
--- /dev/null
+++ b/drivers/media/video/gspca/spca500.c
@@ -0,0 +1,1095 @@
+/*
+ * SPCA500 chip based cameras initialization data
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "spca500"
+
+#include "gspca.h"
+#include "jpeg.h"
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("GSPCA/SPCA500 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ unsigned char brightness;
+ unsigned char contrast;
+ unsigned char colors;
+ u8 quality;
+#define QUALITY_MIN 70
+#define QUALITY_MAX 95
+#define QUALITY_DEF 85
+
+ char subtype;
+#define AgfaCl20 0
+#define AiptekPocketDV 1
+#define BenqDC1016 2
+#define CreativePCCam300 3
+#define DLinkDSC350 4
+#define Gsmartmini 5
+#define IntelPocketPCCamera 6
+#define KodakEZ200 7
+#define LogitechClickSmart310 8
+#define LogitechClickSmart510 9
+#define LogitechTraveler 10
+#define MustekGsmart300 11
+#define Optimedia 12
+#define PalmPixDC85 13
+#define ToptroIndus 14
+
+ u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+
+static const struct ctrl sd_ctrls[] = {
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+#define BRIGHTNESS_DEF 127
+ .default_value = BRIGHTNESS_DEF,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+ {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 63,
+ .step = 1,
+#define CONTRAST_DEF 31
+ .default_value = CONTRAST_DEF,
+ },
+ .set = sd_setcontrast,
+ .get = sd_getcontrast,
+ },
+ {
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Color",
+ .minimum = 0,
+ .maximum = 63,
+ .step = 1,
+#define COLOR_DEF 31
+ .default_value = COLOR_DEF,
+ },
+ .set = sd_setcolors,
+ .get = sd_getcolors,
+ },
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
+static const struct v4l2_pix_format sif_mode[] = {
+ {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
+/* Frame packet header offsets for the spca500 */
+#define SPCA500_OFFSET_PADDINGLB 2
+#define SPCA500_OFFSET_PADDINGHB 3
+#define SPCA500_OFFSET_MODE 4
+#define SPCA500_OFFSET_IMGWIDTH 5
+#define SPCA500_OFFSET_IMGHEIGHT 6
+#define SPCA500_OFFSET_IMGMODE 7
+#define SPCA500_OFFSET_QTBLINDEX 8
+#define SPCA500_OFFSET_FRAMSEQ 9
+#define SPCA500_OFFSET_CDSPINFO 10
+#define SPCA500_OFFSET_GPIO 11
+#define SPCA500_OFFSET_AUGPIO 12
+#define SPCA500_OFFSET_DATA 16
+
+
+static const __u16 spca500_visual_defaults[][3] = {
+ {0x00, 0x0003, 0x816b}, /* SSI not active sync with vsync,
+ * hue (H byte) = 0,
+ * saturation/hue enable,
+ * brightness/contrast enable.
+ */
+ {0x00, 0x0000, 0x8167}, /* brightness = 0 */
+ {0x00, 0x0020, 0x8168}, /* contrast = 0 */
+ {0x00, 0x0003, 0x816b}, /* SSI not active sync with vsync,
+ * hue (H byte) = 0, saturation/hue enable,
+ * brightness/contrast enable.
+ * was 0x0003, now 0x0000.
+ */
+ {0x00, 0x0000, 0x816a}, /* hue (L byte) = 0 */
+ {0x00, 0x0020, 0x8169}, /* saturation = 0x20 */
+ {0x00, 0x0050, 0x8157}, /* edge gain high threshold */
+ {0x00, 0x0030, 0x8158}, /* edge gain low threshold */
+ {0x00, 0x0028, 0x8159}, /* edge bandwidth high threshold */
+ {0x00, 0x000a, 0x815a}, /* edge bandwidth low threshold */
+ {0x00, 0x0001, 0x8202}, /* clock rate compensation = 1/25 sec/frame */
+ {0x0c, 0x0004, 0x0000},
+ /* set interface */
+ {}
+};
+static const __u16 Clicksmart510_defaults[][3] = {
+ {0x00, 0x00, 0x8211},
+ {0x00, 0x01, 0x82c0},
+ {0x00, 0x10, 0x82cb},
+ {0x00, 0x0f, 0x800d},
+ {0x00, 0x82, 0x8225},
+ {0x00, 0x21, 0x8228},
+ {0x00, 0x00, 0x8203},
+ {0x00, 0x00, 0x8204},
+ {0x00, 0x08, 0x8205},
+ {0x00, 0xf8, 0x8206},
+ {0x00, 0x28, 0x8207},
+ {0x00, 0xa0, 0x8208},
+ {0x00, 0x08, 0x824a},
+ {0x00, 0x08, 0x8214},
+ {0x00, 0x80, 0x82c1},
+ {0x00, 0x00, 0x82c2},
+ {0x00, 0x00, 0x82ca},
+ {0x00, 0x80, 0x82c1},
+ {0x00, 0x04, 0x82c2},
+ {0x00, 0x00, 0x82ca},
+ {0x00, 0xfc, 0x8100},
+ {0x00, 0xfc, 0x8105},
+ {0x00, 0x30, 0x8101},
+ {0x00, 0x00, 0x8102},
+ {0x00, 0x00, 0x8103},
+ {0x00, 0x66, 0x8107},
+ {0x00, 0x00, 0x816b},
+ {0x00, 0x00, 0x8155},
+ {0x00, 0x01, 0x8156},
+ {0x00, 0x60, 0x8157},
+ {0x00, 0x40, 0x8158},
+ {0x00, 0x0a, 0x8159},
+ {0x00, 0x06, 0x815a},
+ {0x00, 0x00, 0x813f},
+ {0x00, 0x00, 0x8200},
+ {0x00, 0x19, 0x8201},
+ {0x00, 0x00, 0x82c1},
+ {0x00, 0xa0, 0x82c2},
+ {0x00, 0x00, 0x82ca},
+ {0x00, 0x00, 0x8117},
+ {0x00, 0x00, 0x8118},
+ {0x00, 0x65, 0x8119},
+ {0x00, 0x00, 0x811a},
+ {0x00, 0x00, 0x811b},
+ {0x00, 0x55, 0x811c},
+ {0x00, 0x65, 0x811d},
+ {0x00, 0x55, 0x811e},
+ {0x00, 0x16, 0x811f},
+ {0x00, 0x19, 0x8120},
+ {0x00, 0x80, 0x8103},
+ {0x00, 0x83, 0x816b},
+ {0x00, 0x25, 0x8168},
+ {0x00, 0x01, 0x820f},
+ {0x00, 0xff, 0x8115},
+ {0x00, 0x48, 0x8116},
+ {0x00, 0x50, 0x8151},
+ {0x00, 0x40, 0x8152},
+ {0x00, 0x78, 0x8153},
+ {0x00, 0x40, 0x8154},
+ {0x00, 0x00, 0x8167},
+ {0x00, 0x20, 0x8168},
+ {0x00, 0x00, 0x816a},
+ {0x00, 0x03, 0x816b},
+ {0x00, 0x20, 0x8169},
+ {0x00, 0x60, 0x8157},
+ {0x00, 0x00, 0x8190},
+ {0x00, 0x00, 0x81a1},
+ {0x00, 0x00, 0x81b2},
+ {0x00, 0x27, 0x8191},
+ {0x00, 0x27, 0x81a2},
+ {0x00, 0x27, 0x81b3},
+ {0x00, 0x4b, 0x8192},
+ {0x00, 0x4b, 0x81a3},
+ {0x00, 0x4b, 0x81b4},
+ {0x00, 0x66, 0x8193},
+ {0x00, 0x66, 0x81a4},
+ {0x00, 0x66, 0x81b5},
+ {0x00, 0x79, 0x8194},
+ {0x00, 0x79, 0x81a5},
+ {0x00, 0x79, 0x81b6},
+ {0x00, 0x8a, 0x8195},
+ {0x00, 0x8a, 0x81a6},
+ {0x00, 0x8a, 0x81b7},
+ {0x00, 0x9b, 0x8196},
+ {0x00, 0x9b, 0x81a7},
+ {0x00, 0x9b, 0x81b8},
+ {0x00, 0xa6, 0x8197},
+ {0x00, 0xa6, 0x81a8},
+ {0x00, 0xa6, 0x81b9},
+ {0x00, 0xb2, 0x8198},
+ {0x00, 0xb2, 0x81a9},
+ {0x00, 0xb2, 0x81ba},
+ {0x00, 0xbe, 0x8199},
+ {0x00, 0xbe, 0x81aa},
+ {0x00, 0xbe, 0x81bb},
+ {0x00, 0xc8, 0x819a},
+ {0x00, 0xc8, 0x81ab},
+ {0x00, 0xc8, 0x81bc},
+ {0x00, 0xd2, 0x819b},
+ {0x00, 0xd2, 0x81ac},
+ {0x00, 0xd2, 0x81bd},
+ {0x00, 0xdb, 0x819c},
+ {0x00, 0xdb, 0x81ad},
+ {0x00, 0xdb, 0x81be},
+ {0x00, 0xe4, 0x819d},
+ {0x00, 0xe4, 0x81ae},
+ {0x00, 0xe4, 0x81bf},
+ {0x00, 0xed, 0x819e},
+ {0x00, 0xed, 0x81af},
+ {0x00, 0xed, 0x81c0},
+ {0x00, 0xf7, 0x819f},
+ {0x00, 0xf7, 0x81b0},
+ {0x00, 0xf7, 0x81c1},
+ {0x00, 0xff, 0x81a0},
+ {0x00, 0xff, 0x81b1},
+ {0x00, 0xff, 0x81c2},
+ {0x00, 0x03, 0x8156},
+ {0x00, 0x00, 0x8211},
+ {0x00, 0x20, 0x8168},
+ {0x00, 0x01, 0x8202},
+ {0x00, 0x30, 0x8101},
+ {0x00, 0x00, 0x8111},
+ {0x00, 0x00, 0x8112},
+ {0x00, 0x00, 0x8113},
+ {0x00, 0x00, 0x8114},
+ {}
+};
+
+static const __u8 qtable_creative_pccam[2][64] = {
+ { /* Q-table Y-components */
+ 0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12,
+ 0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11,
+ 0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11,
+ 0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13,
+ 0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17,
+ 0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c,
+ 0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e,
+ 0x16, 0x1c, 0x1d, 0x1d, 0x22, 0x1e, 0x1f, 0x1e},
+ { /* Q-table C-components */
+ 0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e}
+};
+
+static const __u8 qtable_kodak_ez200[2][64] = {
+ { /* Q-table Y-components */
+ 0x02, 0x01, 0x01, 0x02, 0x02, 0x04, 0x05, 0x06,
+ 0x01, 0x01, 0x01, 0x02, 0x03, 0x06, 0x06, 0x06,
+ 0x01, 0x01, 0x02, 0x02, 0x04, 0x06, 0x07, 0x06,
+ 0x01, 0x02, 0x02, 0x03, 0x05, 0x09, 0x08, 0x06,
+ 0x02, 0x02, 0x04, 0x06, 0x07, 0x0b, 0x0a, 0x08,
+ 0x02, 0x04, 0x06, 0x06, 0x08, 0x0a, 0x0b, 0x09,
+ 0x05, 0x06, 0x08, 0x09, 0x0a, 0x0c, 0x0c, 0x0a,
+ 0x07, 0x09, 0x0a, 0x0a, 0x0b, 0x0a, 0x0a, 0x0a},
+ { /* Q-table C-components */
+ 0x02, 0x02, 0x02, 0x05, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x02, 0x02, 0x03, 0x07, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x02, 0x03, 0x06, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x05, 0x07, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a}
+};
+
+static const __u8 qtable_pocketdv[2][64] = {
+ { /* Q-table Y-components start registers 0x8800 */
+ 0x06, 0x04, 0x04, 0x06, 0x0a, 0x10, 0x14, 0x18,
+ 0x05, 0x05, 0x06, 0x08, 0x0a, 0x17, 0x18, 0x16,
+ 0x06, 0x05, 0x06, 0x0a, 0x10, 0x17, 0x1c, 0x16,
+ 0x06, 0x07, 0x09, 0x0c, 0x14, 0x23, 0x20, 0x19,
+ 0x07, 0x09, 0x0f, 0x16, 0x1b, 0x2c, 0x29, 0x1f,
+ 0x0a, 0x0e, 0x16, 0x1a, 0x20, 0x2a, 0x2d, 0x25,
+ 0x14, 0x1a, 0x1f, 0x23, 0x29, 0x30, 0x30, 0x28,
+ 0x1d, 0x25, 0x26, 0x27, 0x2d, 0x28, 0x29, 0x28,
+ },
+ { /* Q-table C-components start registers 0x8840 */
+ 0x07, 0x07, 0x0a, 0x13, 0x28, 0x28, 0x28, 0x28,
+ 0x07, 0x08, 0x0a, 0x1a, 0x28, 0x28, 0x28, 0x28,
+ 0x0a, 0x0a, 0x16, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x13, 0x1a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28}
+};
+
+/* read 'len' bytes to gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev,
+ __u16 index,
+ __u16 length)
+{
+ usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ 0,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, /* value */
+ index, gspca_dev->usb_buf, length, 500);
+}
+
+static int reg_w(struct gspca_dev *gspca_dev,
+ __u16 req, __u16 index, __u16 value)
+{
+ int ret;
+
+ PDEBUG(D_USBO, "reg write: [0x%02x] = 0x%02x", index, value);
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ req,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0, 500);
+ if (ret < 0)
+ pr_err("reg write: error %d\n", ret);
+ return ret;
+}
+
+/* returns: negative is error, pos or zero is data */
+static int reg_r_12(struct gspca_dev *gspca_dev,
+ __u16 req, /* bRequest */
+ __u16 index, /* wIndex */
+ __u16 length) /* wLength (1 or 2 only) */
+{
+ int ret;
+
+ gspca_dev->usb_buf[1] = 0;
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ req,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, /* value */
+ index,
+ gspca_dev->usb_buf, length,
+ 500); /* timeout */
+ if (ret < 0) {
+ pr_err("reg_r_12 err %d\n", ret);
+ return ret;
+ }
+ return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0];
+}
+
+/*
+ * Simple function to wait for a given 8-bit value to be returned from
+ * a reg_read call.
+ * Returns: negative is error or timeout, zero is success.
+ */
+static int reg_r_wait(struct gspca_dev *gspca_dev,
+ __u16 reg, __u16 index, __u16 value)
+{
+ int ret, cnt = 20;
+
+ while (--cnt > 0) {
+ ret = reg_r_12(gspca_dev, reg, index, 1);
+ if (ret == value)
+ return 0;
+ msleep(50);
+ }
+ return -EIO;
+}
+
+static int write_vector(struct gspca_dev *gspca_dev,
+ const __u16 data[][3])
+{
+ int ret, i = 0;
+
+ while (data[i][0] != 0 || data[i][1] != 0 || data[i][2] != 0) {
+ ret = reg_w(gspca_dev, data[i][0], data[i][2], data[i][1]);
+ if (ret < 0)
+ return ret;
+ i++;
+ }
+ return 0;
+}
+
+static int spca50x_setup_qtable(struct gspca_dev *gspca_dev,
+ unsigned int request,
+ unsigned int ybase,
+ unsigned int cbase,
+ const __u8 qtable[2][64])
+{
+ int i, err;
+
+ /* loop over y components */
+ for (i = 0; i < 64; i++) {
+ err = reg_w(gspca_dev, request, ybase + i, qtable[0][i]);
+ if (err < 0)
+ return err;
+ }
+
+ /* loop over c components */
+ for (i = 0; i < 64; i++) {
+ err = reg_w(gspca_dev, request, cbase + i, qtable[1][i]);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static void spca500_ping310(struct gspca_dev *gspca_dev)
+{
+ reg_r(gspca_dev, 0x0d04, 2);
+ PDEBUG(D_STREAM, "ClickSmart310 ping 0x0d04 0x%02x 0x%02x",
+ gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]);
+}
+
+static void spca500_clksmart310_init(struct gspca_dev *gspca_dev)
+{
+ reg_r(gspca_dev, 0x0d05, 2);
+ PDEBUG(D_STREAM, "ClickSmart310 init 0x0d05 0x%02x 0x%02x",
+ gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]);
+ reg_w(gspca_dev, 0x00, 0x8167, 0x5a);
+ spca500_ping310(gspca_dev);
+
+ reg_w(gspca_dev, 0x00, 0x8168, 0x22);
+ reg_w(gspca_dev, 0x00, 0x816a, 0xc0);
+ reg_w(gspca_dev, 0x00, 0x816b, 0x0b);
+ reg_w(gspca_dev, 0x00, 0x8169, 0x25);
+ reg_w(gspca_dev, 0x00, 0x8157, 0x5b);
+ reg_w(gspca_dev, 0x00, 0x8158, 0x5b);
+ reg_w(gspca_dev, 0x00, 0x813f, 0x03);
+ reg_w(gspca_dev, 0x00, 0x8151, 0x4a);
+ reg_w(gspca_dev, 0x00, 0x8153, 0x78);
+ reg_w(gspca_dev, 0x00, 0x0d01, 0x04);
+ /* 00 for adjust shutter */
+ reg_w(gspca_dev, 0x00, 0x0d02, 0x01);
+ reg_w(gspca_dev, 0x00, 0x8169, 0x25);
+ reg_w(gspca_dev, 0x00, 0x0d01, 0x02);
+}
+
+static void spca500_setmode(struct gspca_dev *gspca_dev,
+ __u8 xmult, __u8 ymult)
+{
+ int mode;
+
+ /* set x multiplier */
+ reg_w(gspca_dev, 0, 0x8001, xmult);
+
+ /* set y multiplier */
+ reg_w(gspca_dev, 0, 0x8002, ymult);
+
+ /* use compressed mode, VGA, with mode specific subsample */
+ mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+ reg_w(gspca_dev, 0, 0x8003, mode << 4);
+}
+
+static int spca500_full_reset(struct gspca_dev *gspca_dev)
+{
+ int err;
+
+ /* send the reset command */
+ err = reg_w(gspca_dev, 0xe0, 0x0001, 0x0000);
+ if (err < 0)
+ return err;
+
+ /* wait for the reset to complete */
+ err = reg_r_wait(gspca_dev, 0x06, 0x0000, 0x0000);
+ if (err < 0)
+ return err;
+ err = reg_w(gspca_dev, 0xe0, 0x0000, 0x0000);
+ if (err < 0)
+ return err;
+ err = reg_r_wait(gspca_dev, 0x06, 0, 0);
+ if (err < 0) {
+ PDEBUG(D_ERR, "reg_r_wait() failed");
+ return err;
+ }
+ /* all ok */
+ return 0;
+}
+
+/* Synchro the Bridge with sensor */
+/* Maybe that will work on all spca500 chip */
+/* because i only own a clicksmart310 try for that chip */
+/* using spca50x_set_packet_size() cause an Ooops here */
+/* usb_set_interface from kernel 2.6.x clear all the urb stuff */
+/* up-port the same feature as in 2.4.x kernel */
+static int spca500_synch310(struct gspca_dev *gspca_dev)
+{
+ if (usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0) < 0) {
+ PDEBUG(D_ERR, "Set packet size: set interface error");
+ goto error;
+ }
+ spca500_ping310(gspca_dev);
+
+ reg_r(gspca_dev, 0x0d00, 1);
+
+ /* need alt setting here */
+ PDEBUG(D_PACK, "ClickSmart310 sync alt: %d", gspca_dev->alt);
+
+ /* Windoze use pipe with altsetting 6 why 7 here */
+ if (usb_set_interface(gspca_dev->dev,
+ gspca_dev->iface,
+ gspca_dev->alt) < 0) {
+ PDEBUG(D_ERR, "Set packet size: set interface error");
+ goto error;
+ }
+ return 0;
+error:
+ return -EBUSY;
+}
+
+static void spca500_reinit(struct gspca_dev *gspca_dev)
+{
+ int err;
+ __u8 Data;
+
+ /* some unknown command from Aiptek pocket dv and family300 */
+
+ reg_w(gspca_dev, 0x00, 0x0d01, 0x01);
+ reg_w(gspca_dev, 0x00, 0x0d03, 0x00);
+ reg_w(gspca_dev, 0x00, 0x0d02, 0x01);
+
+ /* enable drop packet */
+ reg_w(gspca_dev, 0x00, 0x850a, 0x0001);
+
+ err = spca50x_setup_qtable(gspca_dev, 0x00, 0x8800, 0x8840,
+ qtable_pocketdv);
+ if (err < 0)
+ PDEBUG(D_ERR|D_STREAM, "spca50x_setup_qtable failed on init");
+
+ /* set qtable index */
+ reg_w(gspca_dev, 0x00, 0x8880, 2);
+ /* family cam Quicksmart stuff */
+ reg_w(gspca_dev, 0x00, 0x800a, 0x00);
+ /* Set agc transfer: synced between frames */
+ reg_w(gspca_dev, 0x00, 0x820f, 0x01);
+ /* Init SDRAM - needed for SDRAM access */
+ reg_w(gspca_dev, 0x00, 0x870a, 0x04);
+ /*Start init sequence or stream */
+ reg_w(gspca_dev, 0, 0x8003, 0x00);
+ /* switch to video camera mode */
+ reg_w(gspca_dev, 0x00, 0x8000, 0x0004);
+ msleep(2000);
+ if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0) {
+ reg_r(gspca_dev, 0x816b, 1);
+ Data = gspca_dev->usb_buf[0];
+ reg_w(gspca_dev, 0x00, 0x816b, Data);
+ }
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+ sd->subtype = id->driver_info;
+ if (sd->subtype != LogitechClickSmart310) {
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+ } else {
+ cam->cam_mode = sif_mode;
+ cam->nmodes = ARRAY_SIZE(sif_mode);
+ }
+ sd->brightness = BRIGHTNESS_DEF;
+ sd->contrast = CONTRAST_DEF;
+ sd->colors = COLOR_DEF;
+ sd->quality = QUALITY_DEF;
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* initialisation of spca500 based cameras is deferred */
+ PDEBUG(D_STREAM, "SPCA500 init");
+ if (sd->subtype == LogitechClickSmart310)
+ spca500_clksmart310_init(gspca_dev);
+/* else
+ spca500_initialise(gspca_dev); */
+ PDEBUG(D_STREAM, "SPCA500 init done");
+ return 0;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int err;
+ __u8 Data;
+ __u8 xmult, ymult;
+
+ /* create the JPEG header */
+ jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
+ 0x22); /* JPEG 411 */
+ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+
+ if (sd->subtype == LogitechClickSmart310) {
+ xmult = 0x16;
+ ymult = 0x12;
+ } else {
+ xmult = 0x28;
+ ymult = 0x1e;
+ }
+
+ /* is there a sensor here ? */
+ reg_r(gspca_dev, 0x8a04, 1);
+ PDEBUG(D_STREAM, "Spca500 Sensor Address 0x%02x",
+ gspca_dev->usb_buf[0]);
+ PDEBUG(D_STREAM, "Spca500 curr_mode: %d Xmult: 0x%02x, Ymult: 0x%02x",
+ gspca_dev->curr_mode, xmult, ymult);
+
+ /* setup qtable */
+ switch (sd->subtype) {
+ case LogitechClickSmart310:
+ spca500_setmode(gspca_dev, xmult, ymult);
+
+ /* enable drop packet */
+ reg_w(gspca_dev, 0x00, 0x850a, 0x0001);
+ reg_w(gspca_dev, 0x00, 0x8880, 3);
+ err = spca50x_setup_qtable(gspca_dev,
+ 0x00, 0x8800, 0x8840,
+ qtable_creative_pccam);
+ if (err < 0)
+ PDEBUG(D_ERR, "spca50x_setup_qtable failed");
+ /* Init SDRAM - needed for SDRAM access */
+ reg_w(gspca_dev, 0x00, 0x870a, 0x04);
+
+ /* switch to video camera mode */
+ reg_w(gspca_dev, 0x00, 0x8000, 0x0004);
+ msleep(500);
+ if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0)
+ PDEBUG(D_ERR, "reg_r_wait() failed");
+
+ reg_r(gspca_dev, 0x816b, 1);
+ Data = gspca_dev->usb_buf[0];
+ reg_w(gspca_dev, 0x00, 0x816b, Data);
+
+ spca500_synch310(gspca_dev);
+
+ write_vector(gspca_dev, spca500_visual_defaults);
+ spca500_setmode(gspca_dev, xmult, ymult);
+ /* enable drop packet */
+ err = reg_w(gspca_dev, 0x00, 0x850a, 0x0001);
+ if (err < 0)
+ PDEBUG(D_ERR, "failed to enable drop packet");
+ reg_w(gspca_dev, 0x00, 0x8880, 3);
+ err = spca50x_setup_qtable(gspca_dev,
+ 0x00, 0x8800, 0x8840,
+ qtable_creative_pccam);
+ if (err < 0)
+ PDEBUG(D_ERR, "spca50x_setup_qtable failed");
+
+ /* Init SDRAM - needed for SDRAM access */
+ reg_w(gspca_dev, 0x00, 0x870a, 0x04);
+
+ /* switch to video camera mode */
+ reg_w(gspca_dev, 0x00, 0x8000, 0x0004);
+
+ if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0)
+ PDEBUG(D_ERR, "reg_r_wait() failed");
+
+ reg_r(gspca_dev, 0x816b, 1);
+ Data = gspca_dev->usb_buf[0];
+ reg_w(gspca_dev, 0x00, 0x816b, Data);
+ break;
+ case CreativePCCam300: /* Creative PC-CAM 300 640x480 CCD */
+ case IntelPocketPCCamera: /* FIXME: Temporary fix for
+ * Intel Pocket PC Camera
+ * - NWG (Sat 29th March 2003) */
+
+ /* do a full reset */
+ err = spca500_full_reset(gspca_dev);
+ if (err < 0)
+ PDEBUG(D_ERR, "spca500_full_reset failed");
+
+ /* enable drop packet */
+ err = reg_w(gspca_dev, 0x00, 0x850a, 0x0001);
+ if (err < 0)
+ PDEBUG(D_ERR, "failed to enable drop packet");
+ reg_w(gspca_dev, 0x00, 0x8880, 3);
+ err = spca50x_setup_qtable(gspca_dev,
+ 0x00, 0x8800, 0x8840,
+ qtable_creative_pccam);
+ if (err < 0)
+ PDEBUG(D_ERR, "spca50x_setup_qtable failed");
+
+ spca500_setmode(gspca_dev, xmult, ymult);
+ reg_w(gspca_dev, 0x20, 0x0001, 0x0004);
+
+ /* switch to video camera mode */
+ reg_w(gspca_dev, 0x00, 0x8000, 0x0004);
+
+ if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0)
+ PDEBUG(D_ERR, "reg_r_wait() failed");
+
+ reg_r(gspca_dev, 0x816b, 1);
+ Data = gspca_dev->usb_buf[0];
+ reg_w(gspca_dev, 0x00, 0x816b, Data);
+
+/* write_vector(gspca_dev, spca500_visual_defaults); */
+ break;
+ case KodakEZ200: /* Kodak EZ200 */
+
+ /* do a full reset */
+ err = spca500_full_reset(gspca_dev);
+ if (err < 0)
+ PDEBUG(D_ERR, "spca500_full_reset failed");
+ /* enable drop packet */
+ reg_w(gspca_dev, 0x00, 0x850a, 0x0001);
+ reg_w(gspca_dev, 0x00, 0x8880, 0);
+ err = spca50x_setup_qtable(gspca_dev,
+ 0x00, 0x8800, 0x8840,
+ qtable_kodak_ez200);
+ if (err < 0)
+ PDEBUG(D_ERR, "spca50x_setup_qtable failed");
+ spca500_setmode(gspca_dev, xmult, ymult);
+
+ reg_w(gspca_dev, 0x20, 0x0001, 0x0004);
+
+ /* switch to video camera mode */
+ reg_w(gspca_dev, 0x00, 0x8000, 0x0004);
+
+ if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0)
+ PDEBUG(D_ERR, "reg_r_wait() failed");
+
+ reg_r(gspca_dev, 0x816b, 1);
+ Data = gspca_dev->usb_buf[0];
+ reg_w(gspca_dev, 0x00, 0x816b, Data);
+
+/* write_vector(gspca_dev, spca500_visual_defaults); */
+ break;
+
+ case BenqDC1016:
+ case DLinkDSC350: /* FamilyCam 300 */
+ case AiptekPocketDV: /* Aiptek PocketDV */
+ case Gsmartmini: /*Mustek Gsmart Mini */
+ case MustekGsmart300: /* Mustek Gsmart 300 */
+ case PalmPixDC85:
+ case Optimedia:
+ case ToptroIndus:
+ case AgfaCl20:
+ spca500_reinit(gspca_dev);
+ reg_w(gspca_dev, 0x00, 0x0d01, 0x01);
+ /* enable drop packet */
+ reg_w(gspca_dev, 0x00, 0x850a, 0x0001);
+
+ err = spca50x_setup_qtable(gspca_dev,
+ 0x00, 0x8800, 0x8840, qtable_pocketdv);
+ if (err < 0)
+ PDEBUG(D_ERR, "spca50x_setup_qtable failed");
+ reg_w(gspca_dev, 0x00, 0x8880, 2);
+
+ /* familycam Quicksmart pocketDV stuff */
+ reg_w(gspca_dev, 0x00, 0x800a, 0x00);
+ /* Set agc transfer: synced between frames */
+ reg_w(gspca_dev, 0x00, 0x820f, 0x01);
+ /* Init SDRAM - needed for SDRAM access */
+ reg_w(gspca_dev, 0x00, 0x870a, 0x04);
+
+ spca500_setmode(gspca_dev, xmult, ymult);
+ /* switch to video camera mode */
+ reg_w(gspca_dev, 0x00, 0x8000, 0x0004);
+
+ reg_r_wait(gspca_dev, 0, 0x8000, 0x44);
+
+ reg_r(gspca_dev, 0x816b, 1);
+ Data = gspca_dev->usb_buf[0];
+ reg_w(gspca_dev, 0x00, 0x816b, Data);
+ break;
+ case LogitechTraveler:
+ case LogitechClickSmart510:
+ reg_w(gspca_dev, 0x02, 0x00, 0x00);
+ /* enable drop packet */
+ reg_w(gspca_dev, 0x00, 0x850a, 0x0001);
+
+ err = spca50x_setup_qtable(gspca_dev,
+ 0x00, 0x8800,
+ 0x8840, qtable_creative_pccam);
+ if (err < 0)
+ PDEBUG(D_ERR, "spca50x_setup_qtable failed");
+ reg_w(gspca_dev, 0x00, 0x8880, 3);
+ reg_w(gspca_dev, 0x00, 0x800a, 0x00);
+ /* Init SDRAM - needed for SDRAM access */
+ reg_w(gspca_dev, 0x00, 0x870a, 0x04);
+
+ spca500_setmode(gspca_dev, xmult, ymult);
+
+ /* switch to video camera mode */
+ reg_w(gspca_dev, 0x00, 0x8000, 0x0004);
+ reg_r_wait(gspca_dev, 0, 0x8000, 0x44);
+
+ reg_r(gspca_dev, 0x816b, 1);
+ Data = gspca_dev->usb_buf[0];
+ reg_w(gspca_dev, 0x00, 0x816b, Data);
+ write_vector(gspca_dev, Clicksmart510_defaults);
+ break;
+ }
+ return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ reg_w(gspca_dev, 0, 0x8003, 0x00);
+
+ /* switch to video camera mode */
+ reg_w(gspca_dev, 0x00, 0x8000, 0x0004);
+ reg_r(gspca_dev, 0x8000, 1);
+ PDEBUG(D_STREAM, "stop SPCA500 done reg8000: 0x%2x",
+ gspca_dev->usb_buf[0]);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+ static __u8 ffd9[] = {0xff, 0xd9};
+
+/* frames are jpeg 4.1.1 without 0xff escape */
+ if (data[0] == 0xff) {
+ if (data[1] != 0x01) { /* drop packet */
+/* gspca_dev->last_packet_type = DISCARD_PACKET; */
+ return;
+ }
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ ffd9, 2);
+
+ /* put the JPEG header in the new frame */
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+
+ data += SPCA500_OFFSET_DATA;
+ len -= SPCA500_OFFSET_DATA;
+ } else {
+ data += 1;
+ len -= 1;
+ }
+
+ /* add 0x00 after 0xff */
+ i = 0;
+ do {
+ if (data[i] == 0xff) {
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data, i + 1);
+ len -= i;
+ data += i;
+ *data = 0x00;
+ i = 0;
+ }
+ i++;
+ } while (i < len);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w(gspca_dev, 0x00, 0x8167,
+ (__u8) (sd->brightness - 128));
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w(gspca_dev, 0x00, 0x8168, sd->contrast);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w(gspca_dev, 0x00, 0x8169, sd->colors);
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->brightness = val;
+ if (gspca_dev->streaming)
+ setbrightness(gspca_dev);
+ return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->brightness;
+ return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->contrast = val;
+ if (gspca_dev->streaming)
+ setcontrast(gspca_dev);
+ return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->contrast;
+ return 0;
+}
+
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->colors = val;
+ if (gspca_dev->streaming)
+ setcolors(gspca_dev);
+ return 0;
+}
+
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->colors;
+ return 0;
+}
+
+static int sd_set_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (jcomp->quality < QUALITY_MIN)
+ sd->quality = QUALITY_MIN;
+ else if (jcomp->quality > QUALITY_MAX)
+ sd->quality = QUALITY_MAX;
+ else
+ sd->quality = jcomp->quality;
+ if (gspca_dev->streaming)
+ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+ return 0;
+}
+
+static int sd_get_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ memset(jcomp, 0, sizeof *jcomp);
+ jcomp->quality = sd->quality;
+ jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
+ | V4L2_JPEG_MARKER_DQT;
+ return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+ .get_jcomp = sd_get_jcomp,
+ .set_jcomp = sd_set_jcomp,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x040a, 0x0300), .driver_info = KodakEZ200},
+ {USB_DEVICE(0x041e, 0x400a), .driver_info = CreativePCCam300},
+ {USB_DEVICE(0x046d, 0x0890), .driver_info = LogitechTraveler},
+ {USB_DEVICE(0x046d, 0x0900), .driver_info = LogitechClickSmart310},
+ {USB_DEVICE(0x046d, 0x0901), .driver_info = LogitechClickSmart510},
+ {USB_DEVICE(0x04a5, 0x300c), .driver_info = BenqDC1016},
+ {USB_DEVICE(0x04fc, 0x7333), .driver_info = PalmPixDC85},
+ {USB_DEVICE(0x055f, 0xc200), .driver_info = MustekGsmart300},
+ {USB_DEVICE(0x055f, 0xc220), .driver_info = Gsmartmini},
+ {USB_DEVICE(0x06bd, 0x0404), .driver_info = AgfaCl20},
+ {USB_DEVICE(0x06be, 0x0800), .driver_info = Optimedia},
+ {USB_DEVICE(0x084d, 0x0003), .driver_info = DLinkDSC350},
+ {USB_DEVICE(0x08ca, 0x0103), .driver_info = AiptekPocketDV},
+ {USB_DEVICE(0x2899, 0x012c), .driver_info = ToptroIndus},
+ {USB_DEVICE(0x8086, 0x0630), .driver_info = IntelPocketPCCamera},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c
new file mode 100644
index 00000000..9c16821a
--- /dev/null
+++ b/drivers/media/video/gspca/spca501.c
@@ -0,0 +1,2191 @@
+/*
+ * SPCA501 chip based cameras initialization data
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "spca501"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("GSPCA/SPCA501 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ unsigned short contrast;
+ __u8 brightness;
+ __u8 colors;
+ __u8 blue_balance;
+ __u8 red_balance;
+
+ char subtype;
+#define Arowana300KCMOSCamera 0
+#define IntelCreateAndShare 1
+#define KodakDVC325 2
+#define MystFromOriUnknownCamera 3
+#define SmileIntlCamera 4
+#define ThreeComHomeConnectLite 5
+#define ViewQuestM318B 6
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val);
+
+static const struct ctrl sd_ctrls[] = {
+#define MY_BRIGHTNESS 0
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+#define MY_CONTRAST 1
+ {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 64725,
+ .step = 1,
+ .default_value = 64725,
+ },
+ .set = sd_setcontrast,
+ .get = sd_getcontrast,
+ },
+#define MY_COLOR 2
+ {
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Color",
+ .minimum = 0,
+ .maximum = 63,
+ .step = 1,
+ .default_value = 20,
+ },
+ .set = sd_setcolors,
+ .get = sd_getcolors,
+ },
+#define MY_BLUE_BALANCE 3
+ {
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Balance",
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set = sd_setblue_balance,
+ .get = sd_getblue_balance,
+ },
+#define MY_RED_BALANCE 4
+ {
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Balance",
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set = sd_setred_balance,
+ .get = sd_getred_balance,
+ },
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {160, 120, V4L2_PIX_FMT_SPCA501, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120 * 3 / 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 2},
+ {320, 240, V4L2_PIX_FMT_SPCA501, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_SPCA501, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+
+#define SPCA50X_REG_USB 0x2 /* spca505 501 */
+/*
+ * Data to initialize a SPCA501. From a capture file provided by Bill Roehl
+ * With SPCA501 chip description
+ */
+#define CCDSP_SET /* set CCDSP parameters */
+#define TG_SET /* set time generator set */
+#undef DSPWIN_SET /* set DSP windows parameters */
+#undef ALTER_GAMA /* Set alternate set to YUV transform coeffs. */
+#define SPCA501_SNAPBIT 0x80
+#define SPCA501_SNAPCTRL 0x10
+/* Frame packet header offsets for the spca501 */
+#define SPCA501_OFFSET_GPIO 1
+#define SPCA501_OFFSET_TYPE 2
+#define SPCA501_OFFSET_TURN3A 3
+#define SPCA501_OFFSET_FRAMSEQ 4
+#define SPCA501_OFFSET_COMPRESS 5
+#define SPCA501_OFFSET_QUANT 6
+#define SPCA501_OFFSET_QUANT2 7
+#define SPCA501_OFFSET_DATA 8
+
+#define SPCA501_PROP_COMP_ENABLE(d) ((d) & 1)
+#define SPCA501_PROP_SNAP(d) ((d) & 0x40)
+#define SPCA501_PROP_SNAP_CTRL(d) ((d) & 0x10)
+#define SPCA501_PROP_COMP_THRESH(d) (((d) & 0x0e) >> 1)
+#define SPCA501_PROP_COMP_QUANT(d) (((d) & 0x70) >> 4)
+
+/* SPCA501 CCDSP control */
+#define SPCA501_REG_CCDSP 0x01
+/* SPCA501 control/status registers */
+#define SPCA501_REG_CTLRL 0x02
+
+/* registers for color correction and YUV transformation */
+#define SPCA501_A11 0x08
+#define SPCA501_A12 0x09
+#define SPCA501_A13 0x0A
+#define SPCA501_A21 0x0B
+#define SPCA501_A22 0x0C
+#define SPCA501_A23 0x0D
+#define SPCA501_A31 0x0E
+#define SPCA501_A32 0x0F
+#define SPCA501_A33 0x10
+
+/* Data for video camera initialization before capturing */
+static const __u16 spca501_open_data[][3] = {
+ /* bmRequest,value,index */
+
+ {0x2, 0x50, 0x00}, /* C/S enable soft reset */
+ {0x2, 0x40, 0x00}, /* C/S disable soft reset */
+ {0x2, 0x02, 0x05}, /* C/S general purpose I/O data */
+ {0x2, 0x03, 0x05}, /* C/S general purpose I/O data */
+
+#ifdef CCDSP_SET
+ {0x1, 0x38, 0x01}, /* CCDSP options */
+ {0x1, 0x05, 0x02}, /* CCDSP Optical black level for user settings */
+ {0x1, 0xC0, 0x03}, /* CCDSP Optical black settings */
+
+ {0x1, 0x67, 0x07},
+ {0x1, 0x63, 0x3f}, /* CCDSP CCD gamma enable */
+ {0x1, 0x03, 0x56}, /* Add gamma correction */
+
+ {0x1, 0xFF, 0x15}, /* CCDSP High luminance for white balance */
+ {0x1, 0x01, 0x16}, /* CCDSP Low luminance for white balance */
+
+/* Color correction and RGB-to-YUV transformation coefficients changing */
+#ifdef ALTER_GAMA
+ {0x0, 0x00, 0x08}, /* A11 */
+ {0x0, 0x00, 0x09}, /* A12 */
+ {0x0, 0x90, 0x0A}, /* A13 */
+ {0x0, 0x12, 0x0B}, /* A21 */
+ {0x0, 0x00, 0x0C}, /* A22 */
+ {0x0, 0x00, 0x0D}, /* A23 */
+ {0x0, 0x00, 0x0E}, /* A31 */
+ {0x0, 0x02, 0x0F}, /* A32 */
+ {0x0, 0x00, 0x10}, /* A33 */
+#else
+ {0x1, 0x2a, 0x08}, /* A11 0x31 */
+ {0x1, 0xf8, 0x09}, /* A12 f8 */
+ {0x1, 0xf8, 0x0A}, /* A13 f8 */
+ {0x1, 0xf8, 0x0B}, /* A21 f8 */
+ {0x1, 0x14, 0x0C}, /* A22 0x14 */
+ {0x1, 0xf8, 0x0D}, /* A23 f8 */
+ {0x1, 0xf8, 0x0E}, /* A31 f8 */
+ {0x1, 0xf8, 0x0F}, /* A32 f8 */
+ {0x1, 0x20, 0x10}, /* A33 0x20 */
+#endif
+ {0x1, 0x00, 0x11}, /* R offset */
+ {0x1, 0x00, 0x12}, /* G offset */
+ {0x1, 0x00, 0x13}, /* B offset */
+ {0x1, 0x00, 0x14}, /* GB offset */
+
+#endif
+
+#ifdef TG_SET
+ /* Time generator manipulations */
+ {0x0, 0xfc, 0x0}, /* Set up high bits of shutter speed */
+ {0x0, 0x01, 0x1}, /* Set up low bits of shutter speed */
+
+ {0x0, 0xe4, 0x04}, /* DCLK*2 clock phase adjustment */
+ {0x0, 0x08, 0x05}, /* ADCK phase adjustment, inv. ext. VB */
+ {0x0, 0x03, 0x06}, /* FR phase adjustment */
+ {0x0, 0x01, 0x07}, /* FCDS phase adjustment */
+ {0x0, 0x39, 0x08}, /* FS phase adjustment */
+ {0x0, 0x88, 0x0a}, /* FH1 phase and delay adjustment */
+ {0x0, 0x03, 0x0f}, /* pixel identification */
+ {0x0, 0x00, 0x11}, /* clock source selection (default) */
+
+ /*VERY strange manipulations with
+ * select DMCLP or OBPX to be ADCLP output (0x0C)
+ * OPB always toggle or not (0x0D) but they allow
+ * us to set up brightness
+ */
+ {0x0, 0x01, 0x0c},
+ {0x0, 0xe0, 0x0d},
+ /* Done */
+#endif
+
+#ifdef DSPWIN_SET
+ {0x1, 0xa0, 0x01}, /* Setting image processing parameters */
+ {0x1, 0x1c, 0x17}, /* Changing Windows positions X1 */
+ {0x1, 0xe2, 0x19}, /* X2 */
+ {0x1, 0x1c, 0x1b}, /* X3 */
+ {0x1, 0xe2, 0x1d}, /* X4 */
+ {0x1, 0x5f, 0x1f}, /* X5 */
+ {0x1, 0x32, 0x20}, /* Y5 */
+ {0x1, 0x01, 0x10}, /* Changing A33 */
+#endif
+
+ {0x2, 0x204a, 0x07},/* Setting video compression & resolution 160x120 */
+ {0x2, 0x94, 0x06}, /* Setting video no compression */
+ {}
+};
+
+/*
+ The SPCAxxx docs from Sunplus document these values
+ in tables, one table per register number. In the data
+ below, dmRequest is the register number, index is the Addr,
+ and value is a combination of Bit values.
+ Bit Value (hex)
+ 0 01
+ 1 02
+ 2 04
+ 3 08
+ 4 10
+ 5 20
+ 6 40
+ 7 80
+ */
+
+/* Data for chip initialization (set default values) */
+static const __u16 spca501_init_data[][3] = {
+ /* Set all the values to powerup defaults */
+ /* bmRequest,value,index */
+ {0x0, 0xAA, 0x00},
+ {0x0, 0x02, 0x01},
+ {0x0, 0x01, 0x02},
+ {0x0, 0x02, 0x03},
+ {0x0, 0xCE, 0x04},
+ {0x0, 0x00, 0x05},
+ {0x0, 0x00, 0x06},
+ {0x0, 0x00, 0x07},
+ {0x0, 0x00, 0x08},
+ {0x0, 0x00, 0x09},
+ {0x0, 0x90, 0x0A},
+ {0x0, 0x12, 0x0B},
+ {0x0, 0x00, 0x0C},
+ {0x0, 0x00, 0x0D},
+ {0x0, 0x00, 0x0E},
+ {0x0, 0x02, 0x0F},
+ {0x0, 0x00, 0x10},
+ {0x0, 0x00, 0x11},
+ {0x0, 0x00, 0x12},
+ {0x0, 0x00, 0x13},
+ {0x0, 0x00, 0x14},
+ {0x0, 0x00, 0x15},
+ {0x0, 0x00, 0x16},
+ {0x0, 0x00, 0x17},
+ {0x0, 0x00, 0x18},
+ {0x0, 0x00, 0x19},
+ {0x0, 0x00, 0x1A},
+ {0x0, 0x00, 0x1B},
+ {0x0, 0x00, 0x1C},
+ {0x0, 0x00, 0x1D},
+ {0x0, 0x00, 0x1E},
+ {0x0, 0x00, 0x1F},
+ {0x0, 0x00, 0x20},
+ {0x0, 0x00, 0x21},
+ {0x0, 0x00, 0x22},
+ {0x0, 0x00, 0x23},
+ {0x0, 0x00, 0x24},
+ {0x0, 0x00, 0x25},
+ {0x0, 0x00, 0x26},
+ {0x0, 0x00, 0x27},
+ {0x0, 0x00, 0x28},
+ {0x0, 0x00, 0x29},
+ {0x0, 0x00, 0x2A},
+ {0x0, 0x00, 0x2B},
+ {0x0, 0x00, 0x2C},
+ {0x0, 0x00, 0x2D},
+ {0x0, 0x00, 0x2E},
+ {0x0, 0x00, 0x2F},
+ {0x0, 0x00, 0x30},
+ {0x0, 0x00, 0x31},
+ {0x0, 0x00, 0x32},
+ {0x0, 0x00, 0x33},
+ {0x0, 0x00, 0x34},
+ {0x0, 0x00, 0x35},
+ {0x0, 0x00, 0x36},
+ {0x0, 0x00, 0x37},
+ {0x0, 0x00, 0x38},
+ {0x0, 0x00, 0x39},
+ {0x0, 0x00, 0x3A},
+ {0x0, 0x00, 0x3B},
+ {0x0, 0x00, 0x3C},
+ {0x0, 0x00, 0x3D},
+ {0x0, 0x00, 0x3E},
+ {0x0, 0x00, 0x3F},
+ {0x0, 0x00, 0x40},
+ {0x0, 0x00, 0x41},
+ {0x0, 0x00, 0x42},
+ {0x0, 0x00, 0x43},
+ {0x0, 0x00, 0x44},
+ {0x0, 0x00, 0x45},
+ {0x0, 0x00, 0x46},
+ {0x0, 0x00, 0x47},
+ {0x0, 0x00, 0x48},
+ {0x0, 0x00, 0x49},
+ {0x0, 0x00, 0x4A},
+ {0x0, 0x00, 0x4B},
+ {0x0, 0x00, 0x4C},
+ {0x0, 0x00, 0x4D},
+ {0x0, 0x00, 0x4E},
+ {0x0, 0x00, 0x4F},
+ {0x0, 0x00, 0x50},
+ {0x0, 0x00, 0x51},
+ {0x0, 0x00, 0x52},
+ {0x0, 0x00, 0x53},
+ {0x0, 0x00, 0x54},
+ {0x0, 0x00, 0x55},
+ {0x0, 0x00, 0x56},
+ {0x0, 0x00, 0x57},
+ {0x0, 0x00, 0x58},
+ {0x0, 0x00, 0x59},
+ {0x0, 0x00, 0x5A},
+ {0x0, 0x00, 0x5B},
+ {0x0, 0x00, 0x5C},
+ {0x0, 0x00, 0x5D},
+ {0x0, 0x00, 0x5E},
+ {0x0, 0x00, 0x5F},
+ {0x0, 0x00, 0x60},
+ {0x0, 0x00, 0x61},
+ {0x0, 0x00, 0x62},
+ {0x0, 0x00, 0x63},
+ {0x0, 0x00, 0x64},
+ {0x0, 0x00, 0x65},
+ {0x0, 0x00, 0x66},
+ {0x0, 0x00, 0x67},
+ {0x0, 0x00, 0x68},
+ {0x0, 0x00, 0x69},
+ {0x0, 0x00, 0x6A},
+ {0x0, 0x00, 0x6B},
+ {0x0, 0x00, 0x6C},
+ {0x0, 0x00, 0x6D},
+ {0x0, 0x00, 0x6E},
+ {0x0, 0x00, 0x6F},
+ {0x0, 0x00, 0x70},
+ {0x0, 0x00, 0x71},
+ {0x0, 0x00, 0x72},
+ {0x0, 0x00, 0x73},
+ {0x0, 0x00, 0x74},
+ {0x0, 0x00, 0x75},
+ {0x0, 0x00, 0x76},
+ {0x0, 0x00, 0x77},
+ {0x0, 0x00, 0x78},
+ {0x0, 0x00, 0x79},
+ {0x0, 0x00, 0x7A},
+ {0x0, 0x00, 0x7B},
+ {0x0, 0x00, 0x7C},
+ {0x0, 0x00, 0x7D},
+ {0x0, 0x00, 0x7E},
+ {0x0, 0x00, 0x7F},
+ {0x0, 0x00, 0x80},
+ {0x0, 0x00, 0x81},
+ {0x0, 0x00, 0x82},
+ {0x0, 0x00, 0x83},
+ {0x0, 0x00, 0x84},
+ {0x0, 0x00, 0x85},
+ {0x0, 0x00, 0x86},
+ {0x0, 0x00, 0x87},
+ {0x0, 0x00, 0x88},
+ {0x0, 0x00, 0x89},
+ {0x0, 0x00, 0x8A},
+ {0x0, 0x00, 0x8B},
+ {0x0, 0x00, 0x8C},
+ {0x0, 0x00, 0x8D},
+ {0x0, 0x00, 0x8E},
+ {0x0, 0x00, 0x8F},
+ {0x0, 0x00, 0x90},
+ {0x0, 0x00, 0x91},
+ {0x0, 0x00, 0x92},
+ {0x0, 0x00, 0x93},
+ {0x0, 0x00, 0x94},
+ {0x0, 0x00, 0x95},
+ {0x0, 0x00, 0x96},
+ {0x0, 0x00, 0x97},
+ {0x0, 0x00, 0x98},
+ {0x0, 0x00, 0x99},
+ {0x0, 0x00, 0x9A},
+ {0x0, 0x00, 0x9B},
+ {0x0, 0x00, 0x9C},
+ {0x0, 0x00, 0x9D},
+ {0x0, 0x00, 0x9E},
+ {0x0, 0x00, 0x9F},
+ {0x0, 0x00, 0xA0},
+ {0x0, 0x00, 0xA1},
+ {0x0, 0x00, 0xA2},
+ {0x0, 0x00, 0xA3},
+ {0x0, 0x00, 0xA4},
+ {0x0, 0x00, 0xA5},
+ {0x0, 0x00, 0xA6},
+ {0x0, 0x00, 0xA7},
+ {0x0, 0x00, 0xA8},
+ {0x0, 0x00, 0xA9},
+ {0x0, 0x00, 0xAA},
+ {0x0, 0x00, 0xAB},
+ {0x0, 0x00, 0xAC},
+ {0x0, 0x00, 0xAD},
+ {0x0, 0x00, 0xAE},
+ {0x0, 0x00, 0xAF},
+ {0x0, 0x00, 0xB0},
+ {0x0, 0x00, 0xB1},
+ {0x0, 0x00, 0xB2},
+ {0x0, 0x00, 0xB3},
+ {0x0, 0x00, 0xB4},
+ {0x0, 0x00, 0xB5},
+ {0x0, 0x00, 0xB6},
+ {0x0, 0x00, 0xB7},
+ {0x0, 0x00, 0xB8},
+ {0x0, 0x00, 0xB9},
+ {0x0, 0x00, 0xBA},
+ {0x0, 0x00, 0xBB},
+ {0x0, 0x00, 0xBC},
+ {0x0, 0x00, 0xBD},
+ {0x0, 0x00, 0xBE},
+ {0x0, 0x00, 0xBF},
+ {0x0, 0x00, 0xC0},
+ {0x0, 0x00, 0xC1},
+ {0x0, 0x00, 0xC2},
+ {0x0, 0x00, 0xC3},
+ {0x0, 0x00, 0xC4},
+ {0x0, 0x00, 0xC5},
+ {0x0, 0x00, 0xC6},
+ {0x0, 0x00, 0xC7},
+ {0x0, 0x00, 0xC8},
+ {0x0, 0x00, 0xC9},
+ {0x0, 0x00, 0xCA},
+ {0x0, 0x00, 0xCB},
+ {0x0, 0x00, 0xCC},
+ {0x1, 0xF4, 0x00},
+ {0x1, 0x38, 0x01},
+ {0x1, 0x40, 0x02},
+ {0x1, 0x0A, 0x03},
+ {0x1, 0x40, 0x04},
+ {0x1, 0x40, 0x05},
+ {0x1, 0x40, 0x06},
+ {0x1, 0x67, 0x07},
+ {0x1, 0x31, 0x08},
+ {0x1, 0x00, 0x09},
+ {0x1, 0x00, 0x0A},
+ {0x1, 0x00, 0x0B},
+ {0x1, 0x14, 0x0C},
+ {0x1, 0x00, 0x0D},
+ {0x1, 0x00, 0x0E},
+ {0x1, 0x00, 0x0F},
+ {0x1, 0x1E, 0x10},
+ {0x1, 0x00, 0x11},
+ {0x1, 0x00, 0x12},
+ {0x1, 0x00, 0x13},
+ {0x1, 0x00, 0x14},
+ {0x1, 0xFF, 0x15},
+ {0x1, 0x01, 0x16},
+ {0x1, 0x32, 0x17},
+ {0x1, 0x23, 0x18},
+ {0x1, 0xCE, 0x19},
+ {0x1, 0x23, 0x1A},
+ {0x1, 0x32, 0x1B},
+ {0x1, 0x8D, 0x1C},
+ {0x1, 0xCE, 0x1D},
+ {0x1, 0x8D, 0x1E},
+ {0x1, 0x00, 0x1F},
+ {0x1, 0x00, 0x20},
+ {0x1, 0xFF, 0x3E},
+ {0x1, 0x02, 0x3F},
+ {0x1, 0x00, 0x40},
+ {0x1, 0x00, 0x41},
+ {0x1, 0x00, 0x42},
+ {0x1, 0x00, 0x43},
+ {0x1, 0x00, 0x44},
+ {0x1, 0x00, 0x45},
+ {0x1, 0x00, 0x46},
+ {0x1, 0x00, 0x47},
+ {0x1, 0x00, 0x48},
+ {0x1, 0x00, 0x49},
+ {0x1, 0x00, 0x4A},
+ {0x1, 0x00, 0x4B},
+ {0x1, 0x00, 0x4C},
+ {0x1, 0x00, 0x4D},
+ {0x1, 0x00, 0x4E},
+ {0x1, 0x00, 0x4F},
+ {0x1, 0x00, 0x50},
+ {0x1, 0x00, 0x51},
+ {0x1, 0x00, 0x52},
+ {0x1, 0x00, 0x53},
+ {0x1, 0x00, 0x54},
+ {0x1, 0x00, 0x55},
+ {0x1, 0x00, 0x56},
+ {0x1, 0x00, 0x57},
+ {0x1, 0x00, 0x58},
+ {0x1, 0x00, 0x59},
+ {0x1, 0x00, 0x5A},
+ {0x2, 0x03, 0x00},
+ {0x2, 0x00, 0x01},
+ {0x2, 0x00, 0x05},
+ {0x2, 0x00, 0x06},
+ {0x2, 0x00, 0x07},
+ {0x2, 0x00, 0x10},
+ {0x2, 0x00, 0x11},
+ /* Strange - looks like the 501 driver doesn't do anything
+ * at insert time except read the EEPROM
+ */
+ {}
+};
+
+/* Data for video camera init before capture.
+ * Capture and decoding by Colin Peart.
+ * This is is for the 3com HomeConnect Lite which is spca501a based.
+ */
+static const __u16 spca501_3com_open_data[][3] = {
+ /* bmRequest,value,index */
+ {0x2, 0x0050, 0x0000}, /* C/S Enable TG soft reset, timing mode=010 */
+ {0x2, 0x0043, 0x0000}, /* C/S Disable TG soft reset, timing mode=010 */
+ {0x2, 0x0002, 0x0005}, /* C/S GPIO */
+ {0x2, 0x0003, 0x0005}, /* C/S GPIO */
+
+#ifdef CCDSP_SET
+ {0x1, 0x0020, 0x0001}, /* CCDSP Options */
+
+ {0x1, 0x0020, 0x0002}, /* CCDSP Black Level */
+ {0x1, 0x006e, 0x0007}, /* CCDSP Gamma options */
+ {0x1, 0x0090, 0x0015}, /* CCDSP Luminance Low */
+ {0x1, 0x00ff, 0x0016}, /* CCDSP Luminance High */
+ {0x1, 0x0003, 0x003F}, /* CCDSP Gamma correction toggle */
+
+#ifdef ALTER_GAMMA
+ {0x1, 0x0010, 0x0008}, /* CCDSP YUV A11 */
+ {0x1, 0x0000, 0x0009}, /* CCDSP YUV A12 */
+ {0x1, 0x0000, 0x000a}, /* CCDSP YUV A13 */
+ {0x1, 0x0000, 0x000b}, /* CCDSP YUV A21 */
+ {0x1, 0x0010, 0x000c}, /* CCDSP YUV A22 */
+ {0x1, 0x0000, 0x000d}, /* CCDSP YUV A23 */
+ {0x1, 0x0000, 0x000e}, /* CCDSP YUV A31 */
+ {0x1, 0x0000, 0x000f}, /* CCDSP YUV A32 */
+ {0x1, 0x0010, 0x0010}, /* CCDSP YUV A33 */
+ {0x1, 0x0000, 0x0011}, /* CCDSP R Offset */
+ {0x1, 0x0000, 0x0012}, /* CCDSP G Offset */
+ {0x1, 0x0001, 0x0013}, /* CCDSP B Offset */
+ {0x1, 0x0001, 0x0014}, /* CCDSP BG Offset */
+ {0x1, 0x003f, 0x00C1}, /* CCDSP Gamma Correction Enable */
+#endif
+#endif
+
+#ifdef TG_SET
+ {0x0, 0x00fc, 0x0000}, /* TG Shutter Speed High Bits */
+ {0x0, 0x0000, 0x0001}, /* TG Shutter Speed Low Bits */
+ {0x0, 0x00e4, 0x0004}, /* TG DCLK*2 Adjust */
+ {0x0, 0x0008, 0x0005}, /* TG ADCK Adjust */
+ {0x0, 0x0003, 0x0006}, /* TG FR Phase Adjust */
+ {0x0, 0x0001, 0x0007}, /* TG FCDS Phase Adjust */
+ {0x0, 0x0039, 0x0008}, /* TG FS Phase Adjust */
+ {0x0, 0x0088, 0x000a}, /* TG MH1 */
+ {0x0, 0x0003, 0x000f}, /* TG Pixel ID */
+
+ /* Like below, unexplained toglleing */
+ {0x0, 0x0080, 0x000c},
+ {0x0, 0x0000, 0x000d},
+ {0x0, 0x0080, 0x000c},
+ {0x0, 0x0004, 0x000d},
+ {0x0, 0x0000, 0x000c},
+ {0x0, 0x0000, 0x000d},
+ {0x0, 0x0040, 0x000c},
+ {0x0, 0x0017, 0x000d},
+ {0x0, 0x00c0, 0x000c},
+ {0x0, 0x0000, 0x000d},
+ {0x0, 0x0080, 0x000c},
+ {0x0, 0x0006, 0x000d},
+ {0x0, 0x0080, 0x000c},
+ {0x0, 0x0004, 0x000d},
+ {0x0, 0x0002, 0x0003},
+#endif
+
+#ifdef DSPWIN_SET
+ {0x1, 0x001c, 0x0017}, /* CCDSP W1 Start X */
+ {0x1, 0x00e2, 0x0019}, /* CCDSP W2 Start X */
+ {0x1, 0x001c, 0x001b}, /* CCDSP W3 Start X */
+ {0x1, 0x00e2, 0x001d}, /* CCDSP W4 Start X */
+ {0x1, 0x00aa, 0x001f}, /* CCDSP W5 Start X */
+ {0x1, 0x0070, 0x0020}, /* CCDSP W5 Start Y */
+#endif
+ {0x0, 0x0001, 0x0010}, /* TG Start Clock */
+
+/* {0x2, 0x006a, 0x0001}, * C/S Enable ISOSYNCH Packet Engine */
+ {0x2, 0x0068, 0x0001}, /* C/S Diable ISOSYNCH Packet Engine */
+ {0x2, 0x0000, 0x0005},
+ {0x2, 0x0043, 0x0000}, /* C/S Set Timing Mode, Disable TG soft reset */
+ {0x2, 0x0043, 0x0000}, /* C/S Set Timing Mode, Disable TG soft reset */
+ {0x2, 0x0002, 0x0005}, /* C/S GPIO */
+ {0x2, 0x0003, 0x0005}, /* C/S GPIO */
+
+ {0x2, 0x006a, 0x0001}, /* C/S Enable ISOSYNCH Packet Engine */
+ {}
+};
+
+/*
+ * Data used to initialize a SPCA501C with HV7131B sensor.
+ * From a capture file taken with USBSnoop v 1.5
+ * I have a "SPCA501C pc camera chipset" manual by sunplus, but some
+ * of the value meanings are obscure or simply "reserved".
+ * to do list:
+ * 1) Understand what every value means
+ * 2) Understand why some values seem to appear more than once
+ * 3) Write a small comment for each line of the following arrays.
+ */
+static const __u16 spca501c_arowana_open_data[][3] = {
+ /* bmRequest,value,index */
+ {0x02, 0x0007, 0x0005},
+ {0x02, 0xa048, 0x0000},
+ {0x05, 0x0022, 0x0004},
+ {0x01, 0x0006, 0x0011},
+ {0x01, 0x00ff, 0x0012},
+ {0x01, 0x0014, 0x0013},
+ {0x01, 0x0000, 0x0014},
+ {0x01, 0x0042, 0x0051},
+ {0x01, 0x0040, 0x0052},
+ {0x01, 0x0051, 0x0053},
+ {0x01, 0x0040, 0x0054},
+ {0x01, 0x0000, 0x0055},
+ {0x00, 0x0025, 0x0000},
+ {0x00, 0x0026, 0x0000},
+ {0x00, 0x0001, 0x0000},
+ {0x00, 0x0027, 0x0000},
+ {0x00, 0x008a, 0x0000},
+ {}
+};
+
+static const __u16 spca501c_arowana_init_data[][3] = {
+ /* bmRequest,value,index */
+ {0x02, 0x0007, 0x0005},
+ {0x02, 0xa048, 0x0000},
+ {0x05, 0x0022, 0x0004},
+ {0x01, 0x0006, 0x0011},
+ {0x01, 0x00ff, 0x0012},
+ {0x01, 0x0014, 0x0013},
+ {0x01, 0x0000, 0x0014},
+ {0x01, 0x0042, 0x0051},
+ {0x01, 0x0040, 0x0052},
+ {0x01, 0x0051, 0x0053},
+ {0x01, 0x0040, 0x0054},
+ {0x01, 0x0000, 0x0055},
+ {0x00, 0x0025, 0x0000},
+ {0x00, 0x0026, 0x0000},
+ {0x00, 0x0001, 0x0000},
+ {0x00, 0x0027, 0x0000},
+ {0x00, 0x008a, 0x0000},
+ {0x02, 0x0000, 0x0005},
+ {0x02, 0x0007, 0x0005},
+ {0x02, 0x2000, 0x0000},
+ {0x05, 0x0022, 0x0004},
+ {0x05, 0x0015, 0x0001},
+ {0x05, 0x00ea, 0x0000},
+ {0x05, 0x0021, 0x0001},
+ {0x05, 0x00d2, 0x0000},
+ {0x05, 0x0023, 0x0001},
+ {0x05, 0x0003, 0x0000},
+ {0x05, 0x0030, 0x0001},
+ {0x05, 0x002b, 0x0000},
+ {0x05, 0x0031, 0x0001},
+ {0x05, 0x0023, 0x0000},
+ {0x05, 0x0032, 0x0001},
+ {0x05, 0x0023, 0x0000},
+ {0x05, 0x0033, 0x0001},
+ {0x05, 0x0023, 0x0000},
+ {0x05, 0x0034, 0x0001},
+ {0x05, 0x0002, 0x0000},
+ {0x05, 0x0050, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0051, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0052, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0054, 0x0001},
+ {0x05, 0x0001, 0x0000},
+ {0x00, 0x0000, 0x0001},
+ {0x00, 0x0000, 0x0002},
+ {0x00, 0x000c, 0x0003},
+ {0x00, 0x0000, 0x0004},
+ {0x00, 0x0090, 0x0005},
+ {0x00, 0x0000, 0x0006},
+ {0x00, 0x0040, 0x0007},
+ {0x00, 0x00c0, 0x0008},
+ {0x00, 0x004a, 0x0009},
+ {0x00, 0x0000, 0x000a},
+ {0x00, 0x0000, 0x000b},
+ {0x00, 0x0001, 0x000c},
+ {0x00, 0x0001, 0x000d},
+ {0x00, 0x0000, 0x000e},
+ {0x00, 0x0002, 0x000f},
+ {0x00, 0x0001, 0x0010},
+ {0x00, 0x0000, 0x0011},
+ {0x00, 0x0000, 0x0012},
+ {0x00, 0x0002, 0x0020},
+ {0x00, 0x0080, 0x0021},
+ {0x00, 0x0001, 0x0022},
+ {0x00, 0x00e0, 0x0023},
+ {0x00, 0x0000, 0x0024},
+ {0x00, 0x00d5, 0x0025},
+ {0x00, 0x0000, 0x0026},
+ {0x00, 0x000b, 0x0027},
+ {0x00, 0x0000, 0x0046},
+ {0x00, 0x0000, 0x0047},
+ {0x00, 0x0000, 0x0048},
+ {0x00, 0x0000, 0x0049},
+ {0x00, 0x0008, 0x004a},
+ {0xff, 0x0000, 0x00d0},
+ {0xff, 0x00d8, 0x00d1},
+ {0xff, 0x0000, 0x00d4},
+ {0xff, 0x0000, 0x00d5},
+ {0x01, 0x00a6, 0x0000},
+ {0x01, 0x0028, 0x0001},
+ {0x01, 0x0000, 0x0002},
+ {0x01, 0x000a, 0x0003},
+ {0x01, 0x0040, 0x0004},
+ {0x01, 0x0066, 0x0007},
+ {0x01, 0x0011, 0x0008},
+ {0x01, 0x0032, 0x0009},
+ {0x01, 0x00fd, 0x000a},
+ {0x01, 0x0038, 0x000b},
+ {0x01, 0x00d1, 0x000c},
+ {0x01, 0x00f7, 0x000d},
+ {0x01, 0x00ed, 0x000e},
+ {0x01, 0x00d8, 0x000f},
+ {0x01, 0x0038, 0x0010},
+ {0x01, 0x00ff, 0x0015},
+ {0x01, 0x0001, 0x0016},
+ {0x01, 0x0032, 0x0017},
+ {0x01, 0x0023, 0x0018},
+ {0x01, 0x00ce, 0x0019},
+ {0x01, 0x0023, 0x001a},
+ {0x01, 0x0032, 0x001b},
+ {0x01, 0x008d, 0x001c},
+ {0x01, 0x00ce, 0x001d},
+ {0x01, 0x008d, 0x001e},
+ {0x01, 0x0000, 0x001f},
+ {0x01, 0x0000, 0x0020},
+ {0x01, 0x00ff, 0x003e},
+ {0x01, 0x0003, 0x003f},
+ {0x01, 0x0000, 0x0040},
+ {0x01, 0x0035, 0x0041},
+ {0x01, 0x0053, 0x0042},
+ {0x01, 0x0069, 0x0043},
+ {0x01, 0x007c, 0x0044},
+ {0x01, 0x008c, 0x0045},
+ {0x01, 0x009a, 0x0046},
+ {0x01, 0x00a8, 0x0047},
+ {0x01, 0x00b4, 0x0048},
+ {0x01, 0x00bf, 0x0049},
+ {0x01, 0x00ca, 0x004a},
+ {0x01, 0x00d4, 0x004b},
+ {0x01, 0x00dd, 0x004c},
+ {0x01, 0x00e7, 0x004d},
+ {0x01, 0x00ef, 0x004e},
+ {0x01, 0x00f8, 0x004f},
+ {0x01, 0x00ff, 0x0050},
+ {0x01, 0x0001, 0x0056},
+ {0x01, 0x0060, 0x0057},
+ {0x01, 0x0040, 0x0058},
+ {0x01, 0x0011, 0x0059},
+ {0x01, 0x0001, 0x005a},
+ {0x02, 0x0007, 0x0005},
+ {0x02, 0xa048, 0x0000},
+ {0x02, 0x0007, 0x0005},
+ {0x02, 0x0015, 0x0006},
+ {0x02, 0x100a, 0x0007},
+ {0x02, 0xa048, 0x0000},
+ {0x02, 0xc002, 0x0001},
+ {0x02, 0x000f, 0x0005},
+ {0x02, 0xa048, 0x0000},
+ {0x05, 0x0022, 0x0004},
+ {0x05, 0x0025, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0026, 0x0001},
+ {0x05, 0x0001, 0x0000},
+ {0x05, 0x0027, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0001, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0021, 0x0001},
+ {0x05, 0x00d2, 0x0000},
+ {0x05, 0x0020, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x00, 0x0090, 0x0005},
+ {0x01, 0x00a6, 0x0000},
+ {0x02, 0x0007, 0x0005},
+ {0x02, 0x2000, 0x0000},
+ {0x05, 0x0022, 0x0004},
+ {0x05, 0x0015, 0x0001},
+ {0x05, 0x00ea, 0x0000},
+ {0x05, 0x0021, 0x0001},
+ {0x05, 0x00d2, 0x0000},
+ {0x05, 0x0023, 0x0001},
+ {0x05, 0x0003, 0x0000},
+ {0x05, 0x0030, 0x0001},
+ {0x05, 0x002b, 0x0000},
+ {0x05, 0x0031, 0x0001},
+ {0x05, 0x0023, 0x0000},
+ {0x05, 0x0032, 0x0001},
+ {0x05, 0x0023, 0x0000},
+ {0x05, 0x0033, 0x0001},
+ {0x05, 0x0023, 0x0000},
+ {0x05, 0x0034, 0x0001},
+ {0x05, 0x0002, 0x0000},
+ {0x05, 0x0050, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0051, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0052, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0054, 0x0001},
+ {0x05, 0x0001, 0x0000},
+ {0x00, 0x0000, 0x0001},
+ {0x00, 0x0000, 0x0002},
+ {0x00, 0x000c, 0x0003},
+ {0x00, 0x0000, 0x0004},
+ {0x00, 0x0090, 0x0005},
+ {0x00, 0x0000, 0x0006},
+ {0x00, 0x0040, 0x0007},
+ {0x00, 0x00c0, 0x0008},
+ {0x00, 0x004a, 0x0009},
+ {0x00, 0x0000, 0x000a},
+ {0x00, 0x0000, 0x000b},
+ {0x00, 0x0001, 0x000c},
+ {0x00, 0x0001, 0x000d},
+ {0x00, 0x0000, 0x000e},
+ {0x00, 0x0002, 0x000f},
+ {0x00, 0x0001, 0x0010},
+ {0x00, 0x0000, 0x0011},
+ {0x00, 0x0000, 0x0012},
+ {0x00, 0x0002, 0x0020},
+ {0x00, 0x0080, 0x0021},
+ {0x00, 0x0001, 0x0022},
+ {0x00, 0x00e0, 0x0023},
+ {0x00, 0x0000, 0x0024},
+ {0x00, 0x00d5, 0x0025},
+ {0x00, 0x0000, 0x0026},
+ {0x00, 0x000b, 0x0027},
+ {0x00, 0x0000, 0x0046},
+ {0x00, 0x0000, 0x0047},
+ {0x00, 0x0000, 0x0048},
+ {0x00, 0x0000, 0x0049},
+ {0x00, 0x0008, 0x004a},
+ {0xff, 0x0000, 0x00d0},
+ {0xff, 0x00d8, 0x00d1},
+ {0xff, 0x0000, 0x00d4},
+ {0xff, 0x0000, 0x00d5},
+ {0x01, 0x00a6, 0x0000},
+ {0x01, 0x0028, 0x0001},
+ {0x01, 0x0000, 0x0002},
+ {0x01, 0x000a, 0x0003},
+ {0x01, 0x0040, 0x0004},
+ {0x01, 0x0066, 0x0007},
+ {0x01, 0x0011, 0x0008},
+ {0x01, 0x0032, 0x0009},
+ {0x01, 0x00fd, 0x000a},
+ {0x01, 0x0038, 0x000b},
+ {0x01, 0x00d1, 0x000c},
+ {0x01, 0x00f7, 0x000d},
+ {0x01, 0x00ed, 0x000e},
+ {0x01, 0x00d8, 0x000f},
+ {0x01, 0x0038, 0x0010},
+ {0x01, 0x00ff, 0x0015},
+ {0x01, 0x0001, 0x0016},
+ {0x01, 0x0032, 0x0017},
+ {0x01, 0x0023, 0x0018},
+ {0x01, 0x00ce, 0x0019},
+ {0x01, 0x0023, 0x001a},
+ {0x01, 0x0032, 0x001b},
+ {0x01, 0x008d, 0x001c},
+ {0x01, 0x00ce, 0x001d},
+ {0x01, 0x008d, 0x001e},
+ {0x01, 0x0000, 0x001f},
+ {0x01, 0x0000, 0x0020},
+ {0x01, 0x00ff, 0x003e},
+ {0x01, 0x0003, 0x003f},
+ {0x01, 0x0000, 0x0040},
+ {0x01, 0x0035, 0x0041},
+ {0x01, 0x0053, 0x0042},
+ {0x01, 0x0069, 0x0043},
+ {0x01, 0x007c, 0x0044},
+ {0x01, 0x008c, 0x0045},
+ {0x01, 0x009a, 0x0046},
+ {0x01, 0x00a8, 0x0047},
+ {0x01, 0x00b4, 0x0048},
+ {0x01, 0x00bf, 0x0049},
+ {0x01, 0x00ca, 0x004a},
+ {0x01, 0x00d4, 0x004b},
+ {0x01, 0x00dd, 0x004c},
+ {0x01, 0x00e7, 0x004d},
+ {0x01, 0x00ef, 0x004e},
+ {0x01, 0x00f8, 0x004f},
+ {0x01, 0x00ff, 0x0050},
+ {0x01, 0x0001, 0x0056},
+ {0x01, 0x0060, 0x0057},
+ {0x01, 0x0040, 0x0058},
+ {0x01, 0x0011, 0x0059},
+ {0x01, 0x0001, 0x005a},
+ {0x02, 0x0007, 0x0005},
+ {0x02, 0xa048, 0x0000},
+ {0x02, 0x0007, 0x0005},
+ {0x02, 0x0015, 0x0006},
+ {0x02, 0x100a, 0x0007},
+ {0x02, 0xa048, 0x0000},
+ {0x02, 0xc002, 0x0001},
+ {0x02, 0x000f, 0x0005},
+ {0x02, 0xa048, 0x0000},
+ {0x05, 0x0022, 0x0004},
+ {0x05, 0x0025, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0026, 0x0001},
+ {0x05, 0x0001, 0x0000},
+ {0x05, 0x0027, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0001, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0021, 0x0001},
+ {0x05, 0x00d2, 0x0000},
+ {0x05, 0x0020, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x00, 0x0090, 0x0005},
+ {0x01, 0x00a6, 0x0000},
+ {0x01, 0x0003, 0x003f},
+ {0x01, 0x0001, 0x0056},
+ {0x01, 0x0011, 0x0008},
+ {0x01, 0x0032, 0x0009},
+ {0x01, 0xfffd, 0x000a},
+ {0x01, 0x0023, 0x000b},
+ {0x01, 0xffea, 0x000c},
+ {0x01, 0xfff4, 0x000d},
+ {0x01, 0xfffc, 0x000e},
+ {0x01, 0xffe3, 0x000f},
+ {0x01, 0x001f, 0x0010},
+ {0x01, 0x00a8, 0x0001},
+ {0x01, 0x0067, 0x0007},
+ {0x01, 0x0032, 0x0017},
+ {0x01, 0x0023, 0x0018},
+ {0x01, 0x00ce, 0x0019},
+ {0x01, 0x0023, 0x001a},
+ {0x01, 0x0032, 0x001b},
+ {0x01, 0x008d, 0x001c},
+ {0x01, 0x00ce, 0x001d},
+ {0x01, 0x008d, 0x001e},
+ {0x01, 0x00c8, 0x0015},
+ {0x01, 0x0032, 0x0016},
+ {0x01, 0x0000, 0x0011},
+ {0x01, 0x0000, 0x0012},
+ {0x01, 0x0000, 0x0013},
+ {0x01, 0x000a, 0x0003},
+ {0x02, 0xc002, 0x0001},
+ {0x02, 0x0007, 0x0005},
+ {0x02, 0xc000, 0x0001},
+ {0x02, 0x0000, 0x0005},
+ {0x02, 0x0007, 0x0005},
+ {0x02, 0x2000, 0x0000},
+ {0x05, 0x0022, 0x0004},
+ {0x05, 0x0015, 0x0001},
+ {0x05, 0x00ea, 0x0000},
+ {0x05, 0x0021, 0x0001},
+ {0x05, 0x00d2, 0x0000},
+ {0x05, 0x0023, 0x0001},
+ {0x05, 0x0003, 0x0000},
+ {0x05, 0x0030, 0x0001},
+ {0x05, 0x002b, 0x0000},
+ {0x05, 0x0031, 0x0001},
+ {0x05, 0x0023, 0x0000},
+ {0x05, 0x0032, 0x0001},
+ {0x05, 0x0023, 0x0000},
+ {0x05, 0x0033, 0x0001},
+ {0x05, 0x0023, 0x0000},
+ {0x05, 0x0034, 0x0001},
+ {0x05, 0x0002, 0x0000},
+ {0x05, 0x0050, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0051, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0052, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0054, 0x0001},
+ {0x05, 0x0001, 0x0000},
+ {0x00, 0x0000, 0x0001},
+ {0x00, 0x0000, 0x0002},
+ {0x00, 0x000c, 0x0003},
+ {0x00, 0x0000, 0x0004},
+ {0x00, 0x0090, 0x0005},
+ {0x00, 0x0000, 0x0006},
+ {0x00, 0x0040, 0x0007},
+ {0x00, 0x00c0, 0x0008},
+ {0x00, 0x004a, 0x0009},
+ {0x00, 0x0000, 0x000a},
+ {0x00, 0x0000, 0x000b},
+ {0x00, 0x0001, 0x000c},
+ {0x00, 0x0001, 0x000d},
+ {0x00, 0x0000, 0x000e},
+ {0x00, 0x0002, 0x000f},
+ {0x00, 0x0001, 0x0010},
+ {0x00, 0x0000, 0x0011},
+ {0x00, 0x0000, 0x0012},
+ {0x00, 0x0002, 0x0020},
+ {0x00, 0x0080, 0x0021},
+ {0x00, 0x0001, 0x0022},
+ {0x00, 0x00e0, 0x0023},
+ {0x00, 0x0000, 0x0024},
+ {0x00, 0x00d5, 0x0025},
+ {0x00, 0x0000, 0x0026},
+ {0x00, 0x000b, 0x0027},
+ {0x00, 0x0000, 0x0046},
+ {0x00, 0x0000, 0x0047},
+ {0x00, 0x0000, 0x0048},
+ {0x00, 0x0000, 0x0049},
+ {0x00, 0x0008, 0x004a},
+ {0xff, 0x0000, 0x00d0},
+ {0xff, 0x00d8, 0x00d1},
+ {0xff, 0x0000, 0x00d4},
+ {0xff, 0x0000, 0x00d5},
+ {0x01, 0x00a6, 0x0000},
+ {0x01, 0x0028, 0x0001},
+ {0x01, 0x0000, 0x0002},
+ {0x01, 0x000a, 0x0003},
+ {0x01, 0x0040, 0x0004},
+ {0x01, 0x0066, 0x0007},
+ {0x01, 0x0011, 0x0008},
+ {0x01, 0x0032, 0x0009},
+ {0x01, 0x00fd, 0x000a},
+ {0x01, 0x0038, 0x000b},
+ {0x01, 0x00d1, 0x000c},
+ {0x01, 0x00f7, 0x000d},
+ {0x01, 0x00ed, 0x000e},
+ {0x01, 0x00d8, 0x000f},
+ {0x01, 0x0038, 0x0010},
+ {0x01, 0x00ff, 0x0015},
+ {0x01, 0x0001, 0x0016},
+ {0x01, 0x0032, 0x0017},
+ {0x01, 0x0023, 0x0018},
+ {0x01, 0x00ce, 0x0019},
+ {0x01, 0x0023, 0x001a},
+ {0x01, 0x0032, 0x001b},
+ {0x01, 0x008d, 0x001c},
+ {0x01, 0x00ce, 0x001d},
+ {0x01, 0x008d, 0x001e},
+ {0x01, 0x0000, 0x001f},
+ {0x01, 0x0000, 0x0020},
+ {0x01, 0x00ff, 0x003e},
+ {0x01, 0x0003, 0x003f},
+ {0x01, 0x0000, 0x0040},
+ {0x01, 0x0035, 0x0041},
+ {0x01, 0x0053, 0x0042},
+ {0x01, 0x0069, 0x0043},
+ {0x01, 0x007c, 0x0044},
+ {0x01, 0x008c, 0x0045},
+ {0x01, 0x009a, 0x0046},
+ {0x01, 0x00a8, 0x0047},
+ {0x01, 0x00b4, 0x0048},
+ {0x01, 0x00bf, 0x0049},
+ {0x01, 0x00ca, 0x004a},
+ {0x01, 0x00d4, 0x004b},
+ {0x01, 0x00dd, 0x004c},
+ {0x01, 0x00e7, 0x004d},
+ {0x01, 0x00ef, 0x004e},
+ {0x01, 0x00f8, 0x004f},
+ {0x01, 0x00ff, 0x0050},
+ {0x01, 0x0001, 0x0056},
+ {0x01, 0x0060, 0x0057},
+ {0x01, 0x0040, 0x0058},
+ {0x01, 0x0011, 0x0059},
+ {0x01, 0x0001, 0x005a},
+ {0x02, 0x0007, 0x0005},
+ {0x02, 0xa048, 0x0000},
+ {0x02, 0x0007, 0x0005},
+ {0x02, 0x0015, 0x0006},
+ {0x02, 0x100a, 0x0007},
+ {0x02, 0xa048, 0x0000},
+ {0x02, 0xc002, 0x0001},
+ {0x02, 0x000f, 0x0005},
+ {0x02, 0xa048, 0x0000},
+ {0x05, 0x0022, 0x0004},
+ {0x05, 0x0025, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0026, 0x0001},
+ {0x05, 0x0001, 0x0000},
+ {0x05, 0x0027, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0001, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0021, 0x0001},
+ {0x05, 0x00d2, 0x0000},
+ {0x05, 0x0020, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x00, 0x0090, 0x0005},
+ {0x01, 0x00a6, 0x0000},
+ {0x02, 0x0007, 0x0005},
+ {0x02, 0x2000, 0x0000},
+ {0x05, 0x0022, 0x0004},
+ {0x05, 0x0015, 0x0001},
+ {0x05, 0x00ea, 0x0000},
+ {0x05, 0x0021, 0x0001},
+ {0x05, 0x00d2, 0x0000},
+ {0x05, 0x0023, 0x0001},
+ {0x05, 0x0003, 0x0000},
+ {0x05, 0x0030, 0x0001},
+ {0x05, 0x002b, 0x0000},
+ {0x05, 0x0031, 0x0001},
+ {0x05, 0x0023, 0x0000},
+ {0x05, 0x0032, 0x0001},
+ {0x05, 0x0023, 0x0000},
+ {0x05, 0x0033, 0x0001},
+ {0x05, 0x0023, 0x0000},
+ {0x05, 0x0034, 0x0001},
+ {0x05, 0x0002, 0x0000},
+ {0x05, 0x0050, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0051, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0052, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0054, 0x0001},
+ {0x05, 0x0001, 0x0000},
+ {0x00, 0x0000, 0x0001},
+ {0x00, 0x0000, 0x0002},
+ {0x00, 0x000c, 0x0003},
+ {0x00, 0x0000, 0x0004},
+ {0x00, 0x0090, 0x0005},
+ {0x00, 0x0000, 0x0006},
+ {0x00, 0x0040, 0x0007},
+ {0x00, 0x00c0, 0x0008},
+ {0x00, 0x004a, 0x0009},
+ {0x00, 0x0000, 0x000a},
+ {0x00, 0x0000, 0x000b},
+ {0x00, 0x0001, 0x000c},
+ {0x00, 0x0001, 0x000d},
+ {0x00, 0x0000, 0x000e},
+ {0x00, 0x0002, 0x000f},
+ {0x00, 0x0001, 0x0010},
+ {0x00, 0x0000, 0x0011},
+ {0x00, 0x0000, 0x0012},
+ {0x00, 0x0002, 0x0020},
+ {0x00, 0x0080, 0x0021},
+ {0x00, 0x0001, 0x0022},
+ {0x00, 0x00e0, 0x0023},
+ {0x00, 0x0000, 0x0024},
+ {0x00, 0x00d5, 0x0025},
+ {0x00, 0x0000, 0x0026},
+ {0x00, 0x000b, 0x0027},
+ {0x00, 0x0000, 0x0046},
+ {0x00, 0x0000, 0x0047},
+ {0x00, 0x0000, 0x0048},
+ {0x00, 0x0000, 0x0049},
+ {0x00, 0x0008, 0x004a},
+ {0xff, 0x0000, 0x00d0},
+ {0xff, 0x00d8, 0x00d1},
+ {0xff, 0x0000, 0x00d4},
+ {0xff, 0x0000, 0x00d5},
+ {0x01, 0x00a6, 0x0000},
+ {0x01, 0x0028, 0x0001},
+ {0x01, 0x0000, 0x0002},
+ {0x01, 0x000a, 0x0003},
+ {0x01, 0x0040, 0x0004},
+ {0x01, 0x0066, 0x0007},
+ {0x01, 0x0011, 0x0008},
+ {0x01, 0x0032, 0x0009},
+ {0x01, 0x00fd, 0x000a},
+ {0x01, 0x0038, 0x000b},
+ {0x01, 0x00d1, 0x000c},
+ {0x01, 0x00f7, 0x000d},
+ {0x01, 0x00ed, 0x000e},
+ {0x01, 0x00d8, 0x000f},
+ {0x01, 0x0038, 0x0010},
+ {0x01, 0x00ff, 0x0015},
+ {0x01, 0x0001, 0x0016},
+ {0x01, 0x0032, 0x0017},
+ {0x01, 0x0023, 0x0018},
+ {0x01, 0x00ce, 0x0019},
+ {0x01, 0x0023, 0x001a},
+ {0x01, 0x0032, 0x001b},
+ {0x01, 0x008d, 0x001c},
+ {0x01, 0x00ce, 0x001d},
+ {0x01, 0x008d, 0x001e},
+ {0x01, 0x0000, 0x001f},
+ {0x01, 0x0000, 0x0020},
+ {0x01, 0x00ff, 0x003e},
+ {0x01, 0x0003, 0x003f},
+ {0x01, 0x0000, 0x0040},
+ {0x01, 0x0035, 0x0041},
+ {0x01, 0x0053, 0x0042},
+ {0x01, 0x0069, 0x0043},
+ {0x01, 0x007c, 0x0044},
+ {0x01, 0x008c, 0x0045},
+ {0x01, 0x009a, 0x0046},
+ {0x01, 0x00a8, 0x0047},
+ {0x01, 0x00b4, 0x0048},
+ {0x01, 0x00bf, 0x0049},
+ {0x01, 0x00ca, 0x004a},
+ {0x01, 0x00d4, 0x004b},
+ {0x01, 0x00dd, 0x004c},
+ {0x01, 0x00e7, 0x004d},
+ {0x01, 0x00ef, 0x004e},
+ {0x01, 0x00f8, 0x004f},
+ {0x01, 0x00ff, 0x0050},
+ {0x01, 0x0001, 0x0056},
+ {0x01, 0x0060, 0x0057},
+ {0x01, 0x0040, 0x0058},
+ {0x01, 0x0011, 0x0059},
+ {0x01, 0x0001, 0x005a},
+ {0x02, 0x0007, 0x0005},
+ {0x02, 0xa048, 0x0000},
+ {0x02, 0x0007, 0x0005},
+ {0x02, 0x0015, 0x0006},
+ {0x02, 0x100a, 0x0007},
+ {0x02, 0xa048, 0x0000},
+ {0x02, 0xc002, 0x0001},
+ {0x02, 0x000f, 0x0005},
+ {0x02, 0xa048, 0x0000},
+ {0x05, 0x0022, 0x0004},
+ {0x05, 0x0025, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0026, 0x0001},
+ {0x05, 0x0001, 0x0000},
+ {0x05, 0x0027, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0001, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0021, 0x0001},
+ {0x05, 0x00d2, 0x0000},
+ {0x05, 0x0020, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x00, 0x0090, 0x0005},
+ {0x01, 0x00a6, 0x0000},
+ {0x05, 0x0026, 0x0001},
+ {0x05, 0x0001, 0x0000},
+ {0x05, 0x0027, 0x0001},
+ {0x05, 0x000f, 0x0000},
+ {0x01, 0x0003, 0x003f},
+ {0x01, 0x0001, 0x0056},
+ {0x01, 0x0011, 0x0008},
+ {0x01, 0x0032, 0x0009},
+ {0x01, 0xfffd, 0x000a},
+ {0x01, 0x0023, 0x000b},
+ {0x01, 0xffea, 0x000c},
+ {0x01, 0xfff4, 0x000d},
+ {0x01, 0xfffc, 0x000e},
+ {0x01, 0xffe3, 0x000f},
+ {0x01, 0x001f, 0x0010},
+ {0x01, 0x00a8, 0x0001},
+ {0x01, 0x0067, 0x0007},
+ {0x01, 0x0042, 0x0051},
+ {0x01, 0x0051, 0x0053},
+ {0x01, 0x000a, 0x0003},
+ {0x02, 0xc002, 0x0001},
+ {0x02, 0x0007, 0x0005},
+ {0x02, 0xc000, 0x0001},
+ {0x02, 0x0000, 0x0005},
+ {0x02, 0x0007, 0x0005},
+ {0x02, 0x2000, 0x0000},
+ {0x05, 0x0022, 0x0004},
+ {0x05, 0x0015, 0x0001},
+ {0x05, 0x00ea, 0x0000},
+ {0x05, 0x0021, 0x0001},
+ {0x05, 0x00d2, 0x0000},
+ {0x05, 0x0023, 0x0001},
+ {0x05, 0x0003, 0x0000},
+ {0x05, 0x0030, 0x0001},
+ {0x05, 0x002b, 0x0000},
+ {0x05, 0x0031, 0x0001},
+ {0x05, 0x0023, 0x0000},
+ {0x05, 0x0032, 0x0001},
+ {0x05, 0x0023, 0x0000},
+ {0x05, 0x0033, 0x0001},
+ {0x05, 0x0023, 0x0000},
+ {0x05, 0x0034, 0x0001},
+ {0x05, 0x0002, 0x0000},
+ {0x05, 0x0050, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0051, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0052, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0054, 0x0001},
+ {0x05, 0x0001, 0x0000},
+ {0x00, 0x0000, 0x0001},
+ {0x00, 0x0000, 0x0002},
+ {0x00, 0x000c, 0x0003},
+ {0x00, 0x0000, 0x0004},
+ {0x00, 0x0090, 0x0005},
+ {0x00, 0x0000, 0x0006},
+ {0x00, 0x0040, 0x0007},
+ {0x00, 0x00c0, 0x0008},
+ {0x00, 0x004a, 0x0009},
+ {0x00, 0x0000, 0x000a},
+ {0x00, 0x0000, 0x000b},
+ {0x00, 0x0001, 0x000c},
+ {0x00, 0x0001, 0x000d},
+ {0x00, 0x0000, 0x000e},
+ {0x00, 0x0002, 0x000f},
+ {0x00, 0x0001, 0x0010},
+ {0x00, 0x0000, 0x0011},
+ {0x00, 0x0000, 0x0012},
+ {0x00, 0x0002, 0x0020},
+ {0x00, 0x0080, 0x0021},
+ {0x00, 0x0001, 0x0022},
+ {0x00, 0x00e0, 0x0023},
+ {0x00, 0x0000, 0x0024},
+ {0x00, 0x00d5, 0x0025},
+ {0x00, 0x0000, 0x0026},
+ {0x00, 0x000b, 0x0027},
+ {0x00, 0x0000, 0x0046},
+ {0x00, 0x0000, 0x0047},
+ {0x00, 0x0000, 0x0048},
+ {0x00, 0x0000, 0x0049},
+ {0x00, 0x0008, 0x004a},
+ {0xff, 0x0000, 0x00d0},
+ {0xff, 0x00d8, 0x00d1},
+ {0xff, 0x0000, 0x00d4},
+ {0xff, 0x0000, 0x00d5},
+ {0x01, 0x00a6, 0x0000},
+ {0x01, 0x0028, 0x0001},
+ {0x01, 0x0000, 0x0002},
+ {0x01, 0x000a, 0x0003},
+ {0x01, 0x0040, 0x0004},
+ {0x01, 0x0066, 0x0007},
+ {0x01, 0x0011, 0x0008},
+ {0x01, 0x0032, 0x0009},
+ {0x01, 0x00fd, 0x000a},
+ {0x01, 0x0038, 0x000b},
+ {0x01, 0x00d1, 0x000c},
+ {0x01, 0x00f7, 0x000d},
+ {0x01, 0x00ed, 0x000e},
+ {0x01, 0x00d8, 0x000f},
+ {0x01, 0x0038, 0x0010},
+ {0x01, 0x00ff, 0x0015},
+ {0x01, 0x0001, 0x0016},
+ {0x01, 0x0032, 0x0017},
+ {0x01, 0x0023, 0x0018},
+ {0x01, 0x00ce, 0x0019},
+ {0x01, 0x0023, 0x001a},
+ {0x01, 0x0032, 0x001b},
+ {0x01, 0x008d, 0x001c},
+ {0x01, 0x00ce, 0x001d},
+ {0x01, 0x008d, 0x001e},
+ {0x01, 0x0000, 0x001f},
+ {0x01, 0x0000, 0x0020},
+ {0x01, 0x00ff, 0x003e},
+ {0x01, 0x0003, 0x003f},
+ {0x01, 0x0000, 0x0040},
+ {0x01, 0x0035, 0x0041},
+ {0x01, 0x0053, 0x0042},
+ {0x01, 0x0069, 0x0043},
+ {0x01, 0x007c, 0x0044},
+ {0x01, 0x008c, 0x0045},
+ {0x01, 0x009a, 0x0046},
+ {0x01, 0x00a8, 0x0047},
+ {0x01, 0x00b4, 0x0048},
+ {0x01, 0x00bf, 0x0049},
+ {0x01, 0x00ca, 0x004a},
+ {0x01, 0x00d4, 0x004b},
+ {0x01, 0x00dd, 0x004c},
+ {0x01, 0x00e7, 0x004d},
+ {0x01, 0x00ef, 0x004e},
+ {0x01, 0x00f8, 0x004f},
+ {0x01, 0x00ff, 0x0050},
+ {0x01, 0x0001, 0x0056},
+ {0x01, 0x0060, 0x0057},
+ {0x01, 0x0040, 0x0058},
+ {0x01, 0x0011, 0x0059},
+ {0x01, 0x0001, 0x005a},
+ {0x02, 0x0007, 0x0005},
+ {0x02, 0xa048, 0x0000},
+ {0x02, 0x0007, 0x0005},
+ {0x02, 0x0015, 0x0006},
+ {0x02, 0x100a, 0x0007},
+ {0x02, 0xa048, 0x0000},
+ {0x02, 0xc002, 0x0001},
+ {0x02, 0x000f, 0x0005},
+ {0x02, 0xa048, 0x0000},
+ {0x05, 0x0022, 0x0004},
+ {0x05, 0x0025, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0026, 0x0001},
+ {0x05, 0x0001, 0x0000},
+ {0x05, 0x0027, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0001, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0021, 0x0001},
+ {0x05, 0x00d2, 0x0000},
+ {0x05, 0x0020, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x00, 0x0090, 0x0005},
+ {0x01, 0x00a6, 0x0000},
+ {0x02, 0x0007, 0x0005},
+ {0x02, 0x2000, 0x0000},
+ {0x05, 0x0022, 0x0004},
+ {0x05, 0x0015, 0x0001},
+ {0x05, 0x00ea, 0x0000},
+ {0x05, 0x0021, 0x0001},
+ {0x05, 0x00d2, 0x0000},
+ {0x05, 0x0023, 0x0001},
+ {0x05, 0x0003, 0x0000},
+ {0x05, 0x0030, 0x0001},
+ {0x05, 0x002b, 0x0000},
+ {0x05, 0x0031, 0x0001},
+ {0x05, 0x0023, 0x0000},
+ {0x05, 0x0032, 0x0001},
+ {0x05, 0x0023, 0x0000},
+ {0x05, 0x0033, 0x0001},
+ {0x05, 0x0023, 0x0000},
+ {0x05, 0x0034, 0x0001},
+ {0x05, 0x0002, 0x0000},
+ {0x05, 0x0050, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0051, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0052, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0054, 0x0001},
+ {0x05, 0x0001, 0x0000},
+ {0x00, 0x0000, 0x0001},
+ {0x00, 0x0000, 0x0002},
+ {0x00, 0x000c, 0x0003},
+ {0x00, 0x0000, 0x0004},
+ {0x00, 0x0090, 0x0005},
+ {0x00, 0x0000, 0x0006},
+ {0x00, 0x0040, 0x0007},
+ {0x00, 0x00c0, 0x0008},
+ {0x00, 0x004a, 0x0009},
+ {0x00, 0x0000, 0x000a},
+ {0x00, 0x0000, 0x000b},
+ {0x00, 0x0001, 0x000c},
+ {0x00, 0x0001, 0x000d},
+ {0x00, 0x0000, 0x000e},
+ {0x00, 0x0002, 0x000f},
+ {0x00, 0x0001, 0x0010},
+ {0x00, 0x0000, 0x0011},
+ {0x00, 0x0000, 0x0012},
+ {0x00, 0x0002, 0x0020},
+ {0x00, 0x0080, 0x0021},
+ {0x00, 0x0001, 0x0022},
+ {0x00, 0x00e0, 0x0023},
+ {0x00, 0x0000, 0x0024},
+ {0x00, 0x00d5, 0x0025},
+ {0x00, 0x0000, 0x0026},
+ {0x00, 0x000b, 0x0027},
+ {0x00, 0x0000, 0x0046},
+ {0x00, 0x0000, 0x0047},
+ {0x00, 0x0000, 0x0048},
+ {0x00, 0x0000, 0x0049},
+ {0x00, 0x0008, 0x004a},
+ {0xff, 0x0000, 0x00d0},
+ {0xff, 0x00d8, 0x00d1},
+ {0xff, 0x0000, 0x00d4},
+ {0xff, 0x0000, 0x00d5},
+ {0x01, 0x00a6, 0x0000},
+ {0x01, 0x0028, 0x0001},
+ {0x01, 0x0000, 0x0002},
+ {0x01, 0x000a, 0x0003},
+ {0x01, 0x0040, 0x0004},
+ {0x01, 0x0066, 0x0007},
+ {0x01, 0x0011, 0x0008},
+ {0x01, 0x0032, 0x0009},
+ {0x01, 0x00fd, 0x000a},
+ {0x01, 0x0038, 0x000b},
+ {0x01, 0x00d1, 0x000c},
+ {0x01, 0x00f7, 0x000d},
+ {0x01, 0x00ed, 0x000e},
+ {0x01, 0x00d8, 0x000f},
+ {0x01, 0x0038, 0x0010},
+ {0x01, 0x00ff, 0x0015},
+ {0x01, 0x0001, 0x0016},
+ {0x01, 0x0032, 0x0017},
+ {0x01, 0x0023, 0x0018},
+ {0x01, 0x00ce, 0x0019},
+ {0x01, 0x0023, 0x001a},
+ {0x01, 0x0032, 0x001b},
+ {0x01, 0x008d, 0x001c},
+ {0x01, 0x00ce, 0x001d},
+ {0x01, 0x008d, 0x001e},
+ {0x01, 0x0000, 0x001f},
+ {0x01, 0x0000, 0x0020},
+ {0x01, 0x00ff, 0x003e},
+ {0x01, 0x0003, 0x003f},
+ {0x01, 0x0000, 0x0040},
+ {0x01, 0x0035, 0x0041},
+ {0x01, 0x0053, 0x0042},
+ {0x01, 0x0069, 0x0043},
+ {0x01, 0x007c, 0x0044},
+ {0x01, 0x008c, 0x0045},
+ {0x01, 0x009a, 0x0046},
+ {0x01, 0x00a8, 0x0047},
+ {0x01, 0x00b4, 0x0048},
+ {0x01, 0x00bf, 0x0049},
+ {0x01, 0x00ca, 0x004a},
+ {0x01, 0x00d4, 0x004b},
+ {0x01, 0x00dd, 0x004c},
+ {0x01, 0x00e7, 0x004d},
+ {0x01, 0x00ef, 0x004e},
+ {0x01, 0x00f8, 0x004f},
+ {0x01, 0x00ff, 0x0050},
+ {0x01, 0x0001, 0x0056},
+ {0x01, 0x0060, 0x0057},
+ {0x01, 0x0040, 0x0058},
+ {0x01, 0x0011, 0x0059},
+ {0x01, 0x0001, 0x005a},
+ {0x02, 0x0007, 0x0005},
+ {0x02, 0xa048, 0x0000},
+ {0x02, 0x0007, 0x0005},
+ {0x02, 0x0015, 0x0006},
+ {0x02, 0x100a, 0x0007},
+ {0x02, 0xa048, 0x0000},
+ {0x02, 0xc002, 0x0001},
+ {0x02, 0x000f, 0x0005},
+ {0x02, 0xa048, 0x0000},
+ {0x05, 0x0022, 0x0004},
+ {0x05, 0x0025, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0026, 0x0001},
+ {0x05, 0x0001, 0x0000},
+ {0x05, 0x0027, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0001, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0021, 0x0001},
+ {0x05, 0x00d2, 0x0000},
+ {0x05, 0x0020, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x00, 0x0090, 0x0005},
+ {0x01, 0x00a6, 0x0000},
+ {0x05, 0x0026, 0x0001},
+ {0x05, 0x0001, 0x0000},
+ {0x05, 0x0027, 0x0001},
+ {0x05, 0x001e, 0x0000},
+ {0x01, 0x0003, 0x003f},
+ {0x01, 0x0001, 0x0056},
+ {0x01, 0x0011, 0x0008},
+ {0x01, 0x0032, 0x0009},
+ {0x01, 0xfffd, 0x000a},
+ {0x01, 0x0023, 0x000b},
+ {0x01, 0xffea, 0x000c},
+ {0x01, 0xfff4, 0x000d},
+ {0x01, 0xfffc, 0x000e},
+ {0x01, 0xffe3, 0x000f},
+ {0x01, 0x001f, 0x0010},
+ {0x01, 0x00a8, 0x0001},
+ {0x01, 0x0067, 0x0007},
+ {0x01, 0x0042, 0x0051},
+ {0x01, 0x0051, 0x0053},
+ {0x01, 0x000a, 0x0003},
+ {0x02, 0xc002, 0x0001},
+ {0x02, 0x0007, 0x0005},
+ {0x01, 0x0042, 0x0051},
+ {0x01, 0x0051, 0x0053},
+ {0x05, 0x0026, 0x0001},
+ {0x05, 0x0001, 0x0000},
+ {0x05, 0x0027, 0x0001},
+ {0x05, 0x002d, 0x0000},
+ {0x01, 0x0003, 0x003f},
+ {0x01, 0x0001, 0x0056},
+ {0x02, 0xc000, 0x0001},
+ {0x02, 0x0000, 0x0005},
+ {}
+};
+
+/* Unknown camera from Ori Usbid 0x0000:0x0000 */
+/* Based on snoops from Ori Cohen */
+static const __u16 spca501c_mysterious_open_data[][3] = {
+ {0x02, 0x000f, 0x0005},
+ {0x02, 0xa048, 0x0000},
+ {0x05, 0x0022, 0x0004},
+/* DSP Registers */
+ {0x01, 0x0016, 0x0011}, /* RGB offset */
+ {0x01, 0x0000, 0x0012},
+ {0x01, 0x0006, 0x0013},
+ {0x01, 0x0078, 0x0051},
+ {0x01, 0x0040, 0x0052},
+ {0x01, 0x0046, 0x0053},
+ {0x01, 0x0040, 0x0054},
+ {0x00, 0x0025, 0x0000},
+/* {0x00, 0x0000, 0x0000 }, */
+/* Part 2 */
+/* TG Registers */
+ {0x00, 0x0026, 0x0000},
+ {0x00, 0x0001, 0x0000},
+ {0x00, 0x0027, 0x0000},
+ {0x00, 0x008a, 0x0000},
+ {0x02, 0x0007, 0x0005},
+ {0x02, 0x2000, 0x0000},
+ {0x05, 0x0022, 0x0004},
+ {0x05, 0x0015, 0x0001},
+ {0x05, 0x00ea, 0x0000},
+ {0x05, 0x0021, 0x0001},
+ {0x05, 0x00d2, 0x0000},
+ {0x05, 0x0023, 0x0001},
+ {0x05, 0x0003, 0x0000},
+ {0x05, 0x0030, 0x0001},
+ {0x05, 0x002b, 0x0000},
+ {0x05, 0x0031, 0x0001},
+ {0x05, 0x0023, 0x0000},
+ {0x05, 0x0032, 0x0001},
+ {0x05, 0x0023, 0x0000},
+ {0x05, 0x0033, 0x0001},
+ {0x05, 0x0023, 0x0000},
+ {0x05, 0x0034, 0x0001},
+ {0x05, 0x0002, 0x0000},
+ {0x05, 0x0050, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0051, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0052, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0054, 0x0001},
+ {0x05, 0x0001, 0x0000},
+ {}
+};
+
+/* Based on snoops from Ori Cohen */
+static const __u16 spca501c_mysterious_init_data[][3] = {
+/* Part 3 */
+/* TG registers */
+/* {0x00, 0x0000, 0x0000}, */
+ {0x00, 0x0000, 0x0001},
+ {0x00, 0x0000, 0x0002},
+ {0x00, 0x0006, 0x0003},
+ {0x00, 0x0000, 0x0004},
+ {0x00, 0x0090, 0x0005},
+ {0x00, 0x0000, 0x0006},
+ {0x00, 0x0040, 0x0007},
+ {0x00, 0x00c0, 0x0008},
+ {0x00, 0x004a, 0x0009},
+ {0x00, 0x0000, 0x000a},
+ {0x00, 0x0000, 0x000b},
+ {0x00, 0x0001, 0x000c},
+ {0x00, 0x0001, 0x000d},
+ {0x00, 0x0000, 0x000e},
+ {0x00, 0x0002, 0x000f},
+ {0x00, 0x0001, 0x0010},
+ {0x00, 0x0000, 0x0011},
+ {0x00, 0x0001, 0x0012},
+ {0x00, 0x0002, 0x0020},
+ {0x00, 0x0080, 0x0021}, /* 640 */
+ {0x00, 0x0001, 0x0022},
+ {0x00, 0x00e0, 0x0023}, /* 480 */
+ {0x00, 0x0000, 0x0024}, /* Offset H hight */
+ {0x00, 0x00d3, 0x0025}, /* low */
+ {0x00, 0x0000, 0x0026}, /* Offset V */
+ {0x00, 0x000d, 0x0027}, /* low */
+ {0x00, 0x0000, 0x0046},
+ {0x00, 0x0000, 0x0047},
+ {0x00, 0x0000, 0x0048},
+ {0x00, 0x0000, 0x0049},
+ {0x00, 0x0008, 0x004a},
+/* DSP Registers */
+ {0x01, 0x00a6, 0x0000},
+ {0x01, 0x0028, 0x0001},
+ {0x01, 0x0000, 0x0002},
+ {0x01, 0x000a, 0x0003}, /* Level Calc bit7 ->1 Auto */
+ {0x01, 0x0040, 0x0004},
+ {0x01, 0x0066, 0x0007},
+ {0x01, 0x000f, 0x0008}, /* A11 Color correction coeff */
+ {0x01, 0x002d, 0x0009}, /* A12 */
+ {0x01, 0x0005, 0x000a}, /* A13 */
+ {0x01, 0x0023, 0x000b}, /* A21 */
+ {0x01, 0x00e0, 0x000c}, /* A22 */
+ {0x01, 0x00fd, 0x000d}, /* A23 */
+ {0x01, 0x00f4, 0x000e}, /* A31 */
+ {0x01, 0x00e4, 0x000f}, /* A32 */
+ {0x01, 0x0028, 0x0010}, /* A33 */
+ {0x01, 0x00ff, 0x0015}, /* Reserved */
+ {0x01, 0x0001, 0x0016}, /* Reserved */
+ {0x01, 0x0032, 0x0017}, /* Win1 Start begin */
+ {0x01, 0x0023, 0x0018},
+ {0x01, 0x00ce, 0x0019},
+ {0x01, 0x0023, 0x001a},
+ {0x01, 0x0032, 0x001b},
+ {0x01, 0x008d, 0x001c},
+ {0x01, 0x00ce, 0x001d},
+ {0x01, 0x008d, 0x001e},
+ {0x01, 0x0000, 0x001f},
+ {0x01, 0x0000, 0x0020}, /* Win1 Start end */
+ {0x01, 0x00ff, 0x003e}, /* Reserved begin */
+ {0x01, 0x0002, 0x003f},
+ {0x01, 0x0000, 0x0040},
+ {0x01, 0x0035, 0x0041},
+ {0x01, 0x0053, 0x0042},
+ {0x01, 0x0069, 0x0043},
+ {0x01, 0x007c, 0x0044},
+ {0x01, 0x008c, 0x0045},
+ {0x01, 0x009a, 0x0046},
+ {0x01, 0x00a8, 0x0047},
+ {0x01, 0x00b4, 0x0048},
+ {0x01, 0x00bf, 0x0049},
+ {0x01, 0x00ca, 0x004a},
+ {0x01, 0x00d4, 0x004b},
+ {0x01, 0x00dd, 0x004c},
+ {0x01, 0x00e7, 0x004d},
+ {0x01, 0x00ef, 0x004e},
+ {0x01, 0x00f8, 0x004f},
+ {0x01, 0x00ff, 0x0050},
+ {0x01, 0x0003, 0x0056}, /* Reserved end */
+ {0x01, 0x0060, 0x0057}, /* Edge Gain */
+ {0x01, 0x0040, 0x0058},
+ {0x01, 0x0011, 0x0059}, /* Edge Bandwidth */
+ {0x01, 0x0001, 0x005a},
+ {0x02, 0x0007, 0x0005},
+ {0x02, 0xa048, 0x0000},
+ {0x02, 0x0007, 0x0005},
+ {0x02, 0x0015, 0x0006},
+ {0x02, 0x200a, 0x0007},
+ {0x02, 0xa048, 0x0000},
+ {0x02, 0xc000, 0x0001},
+ {0x02, 0x000f, 0x0005},
+ {0x02, 0xa048, 0x0000},
+ {0x05, 0x0022, 0x0004},
+ {0x05, 0x0025, 0x0001},
+ {0x05, 0x0000, 0x0000},
+/* Part 4 */
+ {0x05, 0x0026, 0x0001},
+ {0x05, 0x0001, 0x0000},
+ {0x05, 0x0027, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0001, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x05, 0x0021, 0x0001},
+ {0x05, 0x00d2, 0x0000},
+ {0x05, 0x0020, 0x0001},
+ {0x05, 0x0000, 0x0000},
+ {0x00, 0x0090, 0x0005},
+ {0x01, 0x00a6, 0x0000},
+ {0x02, 0x0000, 0x0005},
+ {0x05, 0x0026, 0x0001},
+ {0x05, 0x0001, 0x0000},
+ {0x05, 0x0027, 0x0001},
+ {0x05, 0x004e, 0x0000},
+/* Part 5 */
+ {0x01, 0x0003, 0x003f},
+ {0x01, 0x0001, 0x0056},
+ {0x01, 0x000f, 0x0008},
+ {0x01, 0x002d, 0x0009},
+ {0x01, 0x0005, 0x000a},
+ {0x01, 0x0023, 0x000b},
+ {0x01, 0xffe0, 0x000c},
+ {0x01, 0xfffd, 0x000d},
+ {0x01, 0xfff4, 0x000e},
+ {0x01, 0xffe4, 0x000f},
+ {0x01, 0x0028, 0x0010},
+ {0x01, 0x00a8, 0x0001},
+ {0x01, 0x0066, 0x0007},
+ {0x01, 0x0032, 0x0017},
+ {0x01, 0x0023, 0x0018},
+ {0x01, 0x00ce, 0x0019},
+ {0x01, 0x0023, 0x001a},
+ {0x01, 0x0032, 0x001b},
+ {0x01, 0x008d, 0x001c},
+ {0x01, 0x00ce, 0x001d},
+ {0x01, 0x008d, 0x001e},
+ {0x01, 0x00c8, 0x0015}, /* c8 Poids fort Luma */
+ {0x01, 0x0032, 0x0016}, /* 32 */
+ {0x01, 0x0016, 0x0011}, /* R 00 */
+ {0x01, 0x0016, 0x0012}, /* G 00 */
+ {0x01, 0x0016, 0x0013}, /* B 00 */
+ {0x01, 0x000a, 0x0003},
+ {0x02, 0xc002, 0x0001},
+ {0x02, 0x0007, 0x0005},
+ {}
+};
+
+static int reg_write(struct usb_device *dev,
+ __u16 req, __u16 index, __u16 value)
+{
+ int ret;
+
+ ret = usb_control_msg(dev,
+ usb_sndctrlpipe(dev, 0),
+ req,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0, 500);
+ PDEBUG(D_USBO, "reg write: 0x%02x 0x%02x 0x%02x",
+ req, index, value);
+ if (ret < 0)
+ pr_err("reg write: error %d\n", ret);
+ return ret;
+}
+
+
+static int write_vector(struct gspca_dev *gspca_dev,
+ const __u16 data[][3])
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int ret, i = 0;
+
+ while (data[i][0] != 0 || data[i][1] != 0 || data[i][2] != 0) {
+ ret = reg_write(dev, data[i][0], data[i][2], data[i][1]);
+ if (ret < 0) {
+ PDEBUG(D_ERR,
+ "Reg write failed for 0x%02x,0x%02x,0x%02x",
+ data[i][0], data[i][1], data[i][2]);
+ return ret;
+ }
+ i++;
+ }
+ return 0;
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x12, sd->brightness);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_write(gspca_dev->dev, 0x00, 0x00,
+ (sd->contrast >> 8) & 0xff);
+ reg_write(gspca_dev->dev, 0x00, 0x01,
+ sd->contrast & 0xff);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x0c, sd->colors);
+}
+
+static void setblue_balance(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x11, sd->blue_balance);
+}
+
+static void setred_balance(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x13, sd->red_balance);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+ sd->subtype = id->driver_info;
+ sd->brightness = sd_ctrls[MY_BRIGHTNESS].qctrl.default_value;
+ sd->contrast = sd_ctrls[MY_CONTRAST].qctrl.default_value;
+ sd->colors = sd_ctrls[MY_COLOR].qctrl.default_value;
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->subtype) {
+ case Arowana300KCMOSCamera:
+ case SmileIntlCamera:
+ /* Arowana 300k CMOS Camera data */
+ if (write_vector(gspca_dev, spca501c_arowana_init_data))
+ goto error;
+ break;
+ case MystFromOriUnknownCamera:
+ /* Unknown Ori CMOS Camera data */
+ if (write_vector(gspca_dev, spca501c_mysterious_open_data))
+ goto error;
+ break;
+ default:
+ /* generic spca501 init data */
+ if (write_vector(gspca_dev, spca501_init_data))
+ goto error;
+ break;
+ }
+ PDEBUG(D_STREAM, "Initializing SPCA501 finished");
+ return 0;
+error:
+ return -EINVAL;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct usb_device *dev = gspca_dev->dev;
+ int mode;
+
+ switch (sd->subtype) {
+ case ThreeComHomeConnectLite:
+ /* Special handling for 3com data */
+ write_vector(gspca_dev, spca501_3com_open_data);
+ break;
+ case Arowana300KCMOSCamera:
+ case SmileIntlCamera:
+ /* Arowana 300k CMOS Camera data */
+ write_vector(gspca_dev, spca501c_arowana_open_data);
+ break;
+ case MystFromOriUnknownCamera:
+ /* Unknown CMOS Camera data */
+ write_vector(gspca_dev, spca501c_mysterious_init_data);
+ break;
+ default:
+ /* Generic 501 open data */
+ write_vector(gspca_dev, spca501_open_data);
+ }
+
+ /* memorize the wanted pixel format */
+ mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+
+ /* Enable ISO packet machine CTRL reg=2,
+ * index=1 bitmask=0x2 (bit ordinal 1) */
+ reg_write(dev, SPCA50X_REG_USB, 0x6, 0x94);
+ switch (mode) {
+ case 0: /* 640x480 */
+ reg_write(dev, SPCA50X_REG_USB, 0x07, 0x004a);
+ break;
+ case 1: /* 320x240 */
+ reg_write(dev, SPCA50X_REG_USB, 0x07, 0x104a);
+ break;
+ default:
+/* case 2: * 160x120 */
+ reg_write(dev, SPCA50X_REG_USB, 0x07, 0x204a);
+ break;
+ }
+ reg_write(dev, SPCA501_REG_CTLRL, 0x01, 0x02);
+
+ /* HDG atleast the Intel CreateAndShare needs to have one of its
+ * brightness / contrast / color set otherwise it assumes what seems
+ * max contrast. Note that strange enough setting any of these is
+ * enough to fix the max contrast problem, to be sure we set all 3 */
+ setbrightness(gspca_dev);
+ setcontrast(gspca_dev);
+ setcolors(gspca_dev);
+ return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ /* Disable ISO packet
+ * machine CTRL reg=2, index=1 bitmask=0x0 (bit ordinal 1) */
+ reg_write(gspca_dev->dev, SPCA501_REG_CTLRL, 0x01, 0x00);
+}
+
+/* called on streamoff with alt 0 and on disconnect */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ if (!gspca_dev->present)
+ return;
+ reg_write(gspca_dev->dev, SPCA501_REG_CTLRL, 0x05, 0x00);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ switch (data[0]) {
+ case 0: /* start of frame */
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+ data += SPCA501_OFFSET_DATA;
+ len -= SPCA501_OFFSET_DATA;
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+ return;
+ case 0xff: /* drop */
+/* gspca_dev->last_packet_type = DISCARD_PACKET; */
+ return;
+ }
+ data++;
+ len--;
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->brightness = val;
+ if (gspca_dev->streaming)
+ setbrightness(gspca_dev);
+ return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->brightness;
+ return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->contrast = val;
+ if (gspca_dev->streaming)
+ setcontrast(gspca_dev);
+ return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->contrast;
+ return 0;
+}
+
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->colors = val;
+ if (gspca_dev->streaming)
+ setcolors(gspca_dev);
+ return 0;
+}
+
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->colors;
+ return 0;
+}
+
+static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->blue_balance = val;
+ if (gspca_dev->streaming)
+ setblue_balance(gspca_dev);
+ return 0;
+}
+
+static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->blue_balance;
+ return 0;
+}
+
+static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->red_balance = val;
+ if (gspca_dev->streaming)
+ setred_balance(gspca_dev);
+ return 0;
+}
+
+static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->red_balance;
+ return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x040a, 0x0002), .driver_info = KodakDVC325},
+ {USB_DEVICE(0x0497, 0xc001), .driver_info = SmileIntlCamera},
+ {USB_DEVICE(0x0506, 0x00df), .driver_info = ThreeComHomeConnectLite},
+ {USB_DEVICE(0x0733, 0x0401), .driver_info = IntelCreateAndShare},
+ {USB_DEVICE(0x0733, 0x0402), .driver_info = ViewQuestM318B},
+ {USB_DEVICE(0x1776, 0x501c), .driver_info = Arowana300KCMOSCamera},
+ {USB_DEVICE(0x0000, 0x0000), .driver_info = MystFromOriUnknownCamera},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c
new file mode 100644
index 00000000..1320f35e
--- /dev/null
+++ b/drivers/media/video/gspca/spca505.c
@@ -0,0 +1,818 @@
+/*
+ * SPCA505 chip based cameras initialization data
+ *
+ * V4L2 by Jean-Francis Moine <http://moinejf.free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "spca505"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("GSPCA/SPCA505 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ u8 brightness;
+
+ u8 subtype;
+#define IntelPCCameraPro 0
+#define Nxultra 1
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+
+static const struct ctrl sd_ctrls[] = {
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+#define BRIGHTNESS_DEF 127
+ .default_value = BRIGHTNESS_DEF,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120 * 3 / 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 4},
+ {176, 144, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144 * 3 / 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 3},
+ {320, 240, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 2},
+ {352, 288, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288 * 3 / 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+
+#define SPCA50X_OFFSET_DATA 10
+
+#define SPCA50X_REG_USB 0x02 /* spca505 501 */
+
+#define SPCA50X_USB_CTRL 0x00 /* spca505 */
+#define SPCA50X_CUSB_ENABLE 0x01 /* spca505 */
+
+#define SPCA50X_REG_GLOBAL 0x03 /* spca505 */
+#define SPCA50X_GMISC0_IDSEL 0x01 /* Global control device ID select spca505 */
+#define SPCA50X_GLOBAL_MISC0 0x00 /* Global control miscellaneous 0 spca505 */
+
+#define SPCA50X_GLOBAL_MISC1 0x01 /* 505 */
+#define SPCA50X_GLOBAL_MISC3 0x03 /* 505 */
+#define SPCA50X_GMISC3_SAA7113RST 0x20 /* Not sure about this one spca505 */
+
+/* Image format and compression control */
+#define SPCA50X_REG_COMPRESS 0x04
+
+/*
+ * Data to initialize a SPCA505. Common to the CCD and external modes
+ */
+static const u8 spca505_init_data[][3] = {
+ /* bmRequest,value,index */
+ {SPCA50X_REG_GLOBAL, SPCA50X_GMISC3_SAA7113RST, SPCA50X_GLOBAL_MISC3},
+ /* Sensor reset */
+ {SPCA50X_REG_GLOBAL, 0x00, SPCA50X_GLOBAL_MISC3},
+ {SPCA50X_REG_GLOBAL, 0x00, SPCA50X_GLOBAL_MISC1},
+ /* Block USB reset */
+ {SPCA50X_REG_GLOBAL, SPCA50X_GMISC0_IDSEL, SPCA50X_GLOBAL_MISC0},
+
+ {0x05, 0x01, 0x10},
+ /* Maybe power down some stuff */
+ {0x05, 0x0f, 0x11},
+
+ /* Setup internal CCD ? */
+ {0x06, 0x10, 0x08},
+ {0x06, 0x00, 0x09},
+ {0x06, 0x00, 0x0a},
+ {0x06, 0x00, 0x0b},
+ {0x06, 0x10, 0x0c},
+ {0x06, 0x00, 0x0d},
+ {0x06, 0x00, 0x0e},
+ {0x06, 0x00, 0x0f},
+ {0x06, 0x10, 0x10},
+ {0x06, 0x02, 0x11},
+ {0x06, 0x00, 0x12},
+ {0x06, 0x04, 0x13},
+ {0x06, 0x02, 0x14},
+ {0x06, 0x8a, 0x51},
+ {0x06, 0x40, 0x52},
+ {0x06, 0xb6, 0x53},
+ {0x06, 0x3d, 0x54},
+ {}
+};
+
+/*
+ * Data to initialize the camera using the internal CCD
+ */
+static const u8 spca505_open_data_ccd[][3] = {
+ /* bmRequest,value,index */
+ /* Internal CCD data set */
+ {0x03, 0x04, 0x01},
+ /* This could be a reset */
+ {0x03, 0x00, 0x01},
+
+ /* Setup compression and image registers. 0x6 and 0x7 seem to be
+ related to H&V hold, and are resolution mode specific */
+ {0x04, 0x10, 0x01},
+ /* DIFF(0x50), was (0x10) */
+ {0x04, 0x00, 0x04},
+ {0x04, 0x00, 0x05},
+ {0x04, 0x20, 0x06},
+ {0x04, 0x20, 0x07},
+
+ {0x08, 0x0a, 0x00},
+ /* DIFF (0x4a), was (0xa) */
+
+ {0x05, 0x00, 0x10},
+ {0x05, 0x00, 0x11},
+ {0x05, 0x00, 0x00},
+ /* DIFF not written */
+ {0x05, 0x00, 0x01},
+ /* DIFF not written */
+ {0x05, 0x00, 0x02},
+ /* DIFF not written */
+ {0x05, 0x00, 0x03},
+ /* DIFF not written */
+ {0x05, 0x00, 0x04},
+ /* DIFF not written */
+ {0x05, 0x80, 0x05},
+ /* DIFF not written */
+ {0x05, 0xe0, 0x06},
+ /* DIFF not written */
+ {0x05, 0x20, 0x07},
+ /* DIFF not written */
+ {0x05, 0xa0, 0x08},
+ /* DIFF not written */
+ {0x05, 0x0, 0x12},
+ /* DIFF not written */
+ {0x05, 0x02, 0x0f},
+ /* DIFF not written */
+ {0x05, 0x10, 0x46},
+ /* DIFF not written */
+ {0x05, 0x8, 0x4a},
+ /* DIFF not written */
+
+ {0x03, 0x08, 0x03},
+ /* DIFF (0x3,0x28,0x3) */
+ {0x03, 0x08, 0x01},
+ {0x03, 0x0c, 0x03},
+ /* DIFF not written */
+ {0x03, 0x21, 0x00},
+ /* DIFF (0x39) */
+
+/* Extra block copied from init to hopefully ensure CCD is in a sane state */
+ {0x06, 0x10, 0x08},
+ {0x06, 0x00, 0x09},
+ {0x06, 0x00, 0x0a},
+ {0x06, 0x00, 0x0b},
+ {0x06, 0x10, 0x0c},
+ {0x06, 0x00, 0x0d},
+ {0x06, 0x00, 0x0e},
+ {0x06, 0x00, 0x0f},
+ {0x06, 0x10, 0x10},
+ {0x06, 0x02, 0x11},
+ {0x06, 0x00, 0x12},
+ {0x06, 0x04, 0x13},
+ {0x06, 0x02, 0x14},
+ {0x06, 0x8a, 0x51},
+ {0x06, 0x40, 0x52},
+ {0x06, 0xb6, 0x53},
+ {0x06, 0x3d, 0x54},
+ /* End of extra block */
+
+ {0x06, 0x3f, 0x1},
+ /* Block skipped */
+ {0x06, 0x10, 0x02},
+ {0x06, 0x64, 0x07},
+ {0x06, 0x10, 0x08},
+ {0x06, 0x00, 0x09},
+ {0x06, 0x00, 0x0a},
+ {0x06, 0x00, 0x0b},
+ {0x06, 0x10, 0x0c},
+ {0x06, 0x00, 0x0d},
+ {0x06, 0x00, 0x0e},
+ {0x06, 0x00, 0x0f},
+ {0x06, 0x10, 0x10},
+ {0x06, 0x02, 0x11},
+ {0x06, 0x00, 0x12},
+ {0x06, 0x04, 0x13},
+ {0x06, 0x02, 0x14},
+ {0x06, 0x8a, 0x51},
+ {0x06, 0x40, 0x52},
+ {0x06, 0xb6, 0x53},
+ {0x06, 0x3d, 0x54},
+ {0x06, 0x60, 0x57},
+ {0x06, 0x20, 0x58},
+ {0x06, 0x15, 0x59},
+ {0x06, 0x05, 0x5a},
+
+ {0x05, 0x01, 0xc0},
+ {0x05, 0x10, 0xcb},
+ {0x05, 0x80, 0xc1},
+ /* */
+ {0x05, 0x0, 0xc2},
+ /* 4 was 0 */
+ {0x05, 0x00, 0xca},
+ {0x05, 0x80, 0xc1},
+ /* */
+ {0x05, 0x04, 0xc2},
+ {0x05, 0x00, 0xca},
+ {0x05, 0x0, 0xc1},
+ /* */
+ {0x05, 0x00, 0xc2},
+ {0x05, 0x00, 0xca},
+ {0x05, 0x40, 0xc1},
+ /* */
+ {0x05, 0x17, 0xc2},
+ {0x05, 0x00, 0xca},
+ {0x05, 0x80, 0xc1},
+ /* */
+ {0x05, 0x06, 0xc2},
+ {0x05, 0x00, 0xca},
+ {0x05, 0x80, 0xc1},
+ /* */
+ {0x05, 0x04, 0xc2},
+ {0x05, 0x00, 0xca},
+
+ {0x03, 0x4c, 0x3},
+ {0x03, 0x18, 0x1},
+
+ {0x06, 0x70, 0x51},
+ {0x06, 0xbe, 0x53},
+ {0x06, 0x71, 0x57},
+ {0x06, 0x20, 0x58},
+ {0x06, 0x05, 0x59},
+ {0x06, 0x15, 0x5a},
+
+ {0x04, 0x00, 0x08},
+ /* Compress = OFF (0x1 to turn on) */
+ {0x04, 0x12, 0x09},
+ {0x04, 0x21, 0x0a},
+ {0x04, 0x10, 0x0b},
+ {0x04, 0x21, 0x0c},
+ {0x04, 0x05, 0x00},
+ /* was 5 (Image Type ? ) */
+ {0x04, 0x00, 0x01},
+
+ {0x06, 0x3f, 0x01},
+
+ {0x04, 0x00, 0x04},
+ {0x04, 0x00, 0x05},
+ {0x04, 0x40, 0x06},
+ {0x04, 0x40, 0x07},
+
+ {0x06, 0x1c, 0x17},
+ {0x06, 0xe2, 0x19},
+ {0x06, 0x1c, 0x1b},
+ {0x06, 0xe2, 0x1d},
+ {0x06, 0xaa, 0x1f},
+ {0x06, 0x70, 0x20},
+
+ {0x05, 0x01, 0x10},
+ {0x05, 0x00, 0x11},
+ {0x05, 0x01, 0x00},
+ {0x05, 0x05, 0x01},
+ {0x05, 0x00, 0xc1},
+ /* */
+ {0x05, 0x00, 0xc2},
+ {0x05, 0x00, 0xca},
+
+ {0x06, 0x70, 0x51},
+ {0x06, 0xbe, 0x53},
+ {}
+};
+
+/*
+ * Made by Tomasz Zablocki (skalamandra@poczta.onet.pl)
+ * SPCA505b chip based cameras initialization data
+ */
+/* jfm */
+#define initial_brightness 0x7f /* 0x0(white)-0xff(black) */
+/* #define initial_brightness 0x0 //0x0(white)-0xff(black) */
+/*
+ * Data to initialize a SPCA505. Common to the CCD and external modes
+ */
+static const u8 spca505b_init_data[][3] = {
+/* start */
+ {0x02, 0x00, 0x00}, /* init */
+ {0x02, 0x00, 0x01},
+ {0x02, 0x00, 0x02},
+ {0x02, 0x00, 0x03},
+ {0x02, 0x00, 0x04},
+ {0x02, 0x00, 0x05},
+ {0x02, 0x00, 0x06},
+ {0x02, 0x00, 0x07},
+ {0x02, 0x00, 0x08},
+ {0x02, 0x00, 0x09},
+ {0x03, 0x00, 0x00},
+ {0x03, 0x00, 0x01},
+ {0x03, 0x00, 0x02},
+ {0x03, 0x00, 0x03},
+ {0x03, 0x00, 0x04},
+ {0x03, 0x00, 0x05},
+ {0x03, 0x00, 0x06},
+ {0x04, 0x00, 0x00},
+ {0x04, 0x00, 0x02},
+ {0x04, 0x00, 0x04},
+ {0x04, 0x00, 0x05},
+ {0x04, 0x00, 0x06},
+ {0x04, 0x00, 0x07},
+ {0x04, 0x00, 0x08},
+ {0x04, 0x00, 0x09},
+ {0x04, 0x00, 0x0a},
+ {0x04, 0x00, 0x0b},
+ {0x04, 0x00, 0x0c},
+ {0x07, 0x00, 0x00},
+ {0x07, 0x00, 0x03},
+ {0x08, 0x00, 0x00},
+ {0x08, 0x00, 0x01},
+ {0x08, 0x00, 0x02},
+ {0x06, 0x18, 0x08},
+ {0x06, 0xfc, 0x09},
+ {0x06, 0xfc, 0x0a},
+ {0x06, 0xfc, 0x0b},
+ {0x06, 0x18, 0x0c},
+ {0x06, 0xfc, 0x0d},
+ {0x06, 0xfc, 0x0e},
+ {0x06, 0xfc, 0x0f},
+ {0x06, 0x18, 0x10},
+ {0x06, 0xfe, 0x12},
+ {0x06, 0x00, 0x11},
+ {0x06, 0x00, 0x14},
+ {0x06, 0x00, 0x13},
+ {0x06, 0x28, 0x51},
+ {0x06, 0xff, 0x53},
+ {0x02, 0x00, 0x08},
+
+ {0x03, 0x00, 0x03},
+ {0x03, 0x10, 0x03},
+ {}
+};
+
+/*
+ * Data to initialize the camera using the internal CCD
+ */
+static const u8 spca505b_open_data_ccd[][3] = {
+
+/* {0x02,0x00,0x00}, */
+ {0x03, 0x04, 0x01}, /* rst */
+ {0x03, 0x00, 0x01},
+ {0x03, 0x00, 0x00},
+ {0x03, 0x21, 0x00},
+ {0x03, 0x00, 0x04},
+ {0x03, 0x00, 0x03},
+ {0x03, 0x18, 0x03},
+ {0x03, 0x08, 0x01},
+ {0x03, 0x1c, 0x03},
+ {0x03, 0x5c, 0x03},
+ {0x03, 0x5c, 0x03},
+ {0x03, 0x18, 0x01},
+
+/* same as 505 */
+ {0x04, 0x10, 0x01},
+ {0x04, 0x00, 0x04},
+ {0x04, 0x00, 0x05},
+ {0x04, 0x20, 0x06},
+ {0x04, 0x20, 0x07},
+
+ {0x08, 0x0a, 0x00},
+
+ {0x05, 0x00, 0x10},
+ {0x05, 0x00, 0x11},
+ {0x05, 0x00, 0x12},
+ {0x05, 0x6f, 0x00},
+ {0x05, initial_brightness >> 6, 0x00},
+ {0x05, (initial_brightness << 2) & 0xff, 0x01},
+ {0x05, 0x00, 0x02},
+ {0x05, 0x01, 0x03},
+ {0x05, 0x00, 0x04},
+ {0x05, 0x03, 0x05},
+ {0x05, 0xe0, 0x06},
+ {0x05, 0x20, 0x07},
+ {0x05, 0xa0, 0x08},
+ {0x05, 0x00, 0x12},
+ {0x05, 0x02, 0x0f},
+ {0x05, 0x80, 0x14}, /* max exposure off (0=on) */
+ {0x05, 0x01, 0xb0},
+ {0x05, 0x01, 0xbf},
+ {0x03, 0x02, 0x06},
+ {0x05, 0x10, 0x46},
+ {0x05, 0x08, 0x4a},
+
+ {0x06, 0x00, 0x01},
+ {0x06, 0x10, 0x02},
+ {0x06, 0x64, 0x07},
+ {0x06, 0x18, 0x08},
+ {0x06, 0xfc, 0x09},
+ {0x06, 0xfc, 0x0a},
+ {0x06, 0xfc, 0x0b},
+ {0x04, 0x00, 0x01},
+ {0x06, 0x18, 0x0c},
+ {0x06, 0xfc, 0x0d},
+ {0x06, 0xfc, 0x0e},
+ {0x06, 0xfc, 0x0f},
+ {0x06, 0x11, 0x10}, /* contrast */
+ {0x06, 0x00, 0x11},
+ {0x06, 0xfe, 0x12},
+ {0x06, 0x00, 0x13},
+ {0x06, 0x00, 0x14},
+ {0x06, 0x9d, 0x51},
+ {0x06, 0x40, 0x52},
+ {0x06, 0x7c, 0x53},
+ {0x06, 0x40, 0x54},
+ {0x06, 0x02, 0x57},
+ {0x06, 0x03, 0x58},
+ {0x06, 0x15, 0x59},
+ {0x06, 0x05, 0x5a},
+ {0x06, 0x03, 0x56},
+ {0x06, 0x02, 0x3f},
+ {0x06, 0x00, 0x40},
+ {0x06, 0x39, 0x41},
+ {0x06, 0x69, 0x42},
+ {0x06, 0x87, 0x43},
+ {0x06, 0x9e, 0x44},
+ {0x06, 0xb1, 0x45},
+ {0x06, 0xbf, 0x46},
+ {0x06, 0xcc, 0x47},
+ {0x06, 0xd5, 0x48},
+ {0x06, 0xdd, 0x49},
+ {0x06, 0xe3, 0x4a},
+ {0x06, 0xe8, 0x4b},
+ {0x06, 0xed, 0x4c},
+ {0x06, 0xf2, 0x4d},
+ {0x06, 0xf7, 0x4e},
+ {0x06, 0xfc, 0x4f},
+ {0x06, 0xff, 0x50},
+
+ {0x05, 0x01, 0xc0},
+ {0x05, 0x10, 0xcb},
+ {0x05, 0x40, 0xc1},
+ {0x05, 0x04, 0xc2},
+ {0x05, 0x00, 0xca},
+ {0x05, 0x40, 0xc1},
+ {0x05, 0x09, 0xc2},
+ {0x05, 0x00, 0xca},
+ {0x05, 0xc0, 0xc1},
+ {0x05, 0x09, 0xc2},
+ {0x05, 0x00, 0xca},
+ {0x05, 0x40, 0xc1},
+ {0x05, 0x59, 0xc2},
+ {0x05, 0x00, 0xca},
+ {0x04, 0x00, 0x01},
+ {0x05, 0x80, 0xc1},
+ {0x05, 0xec, 0xc2},
+ {0x05, 0x0, 0xca},
+
+ {0x06, 0x02, 0x57},
+ {0x06, 0x01, 0x58},
+ {0x06, 0x15, 0x59},
+ {0x06, 0x0a, 0x5a},
+ {0x06, 0x01, 0x57},
+ {0x06, 0x8a, 0x03},
+ {0x06, 0x0a, 0x6c},
+ {0x06, 0x30, 0x01},
+ {0x06, 0x20, 0x02},
+ {0x06, 0x00, 0x03},
+
+ {0x05, 0x8c, 0x25},
+
+ {0x06, 0x4d, 0x51}, /* maybe saturation (4d) */
+ {0x06, 0x84, 0x53}, /* making green (84) */
+ {0x06, 0x00, 0x57}, /* sharpness (1) */
+ {0x06, 0x18, 0x08},
+ {0x06, 0xfc, 0x09},
+ {0x06, 0xfc, 0x0a},
+ {0x06, 0xfc, 0x0b},
+ {0x06, 0x18, 0x0c}, /* maybe hue (18) */
+ {0x06, 0xfc, 0x0d},
+ {0x06, 0xfc, 0x0e},
+ {0x06, 0xfc, 0x0f},
+ {0x06, 0x18, 0x10}, /* maybe contrast (18) */
+
+ {0x05, 0x01, 0x02},
+
+ {0x04, 0x00, 0x08}, /* compression */
+ {0x04, 0x12, 0x09},
+ {0x04, 0x21, 0x0a},
+ {0x04, 0x10, 0x0b},
+ {0x04, 0x21, 0x0c},
+ {0x04, 0x1d, 0x00}, /* imagetype (1d) */
+ {0x04, 0x41, 0x01}, /* hardware snapcontrol */
+
+ {0x04, 0x00, 0x04},
+ {0x04, 0x00, 0x05},
+ {0x04, 0x10, 0x06},
+ {0x04, 0x10, 0x07},
+ {0x04, 0x40, 0x06},
+ {0x04, 0x40, 0x07},
+ {0x04, 0x00, 0x04},
+ {0x04, 0x00, 0x05},
+
+ {0x06, 0x1c, 0x17},
+ {0x06, 0xe2, 0x19},
+ {0x06, 0x1c, 0x1b},
+ {0x06, 0xe2, 0x1d},
+ {0x06, 0x5f, 0x1f},
+ {0x06, 0x32, 0x20},
+
+ {0x05, initial_brightness >> 6, 0x00},
+ {0x05, (initial_brightness << 2) & 0xff, 0x01},
+ {0x05, 0x06, 0xc1},
+ {0x05, 0x58, 0xc2},
+ {0x05, 0x00, 0xca},
+ {0x05, 0x00, 0x11},
+ {}
+};
+
+static int reg_write(struct usb_device *dev,
+ u16 req, u16 index, u16 value)
+{
+ int ret;
+
+ ret = usb_control_msg(dev,
+ usb_sndctrlpipe(dev, 0),
+ req,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0, 500);
+ PDEBUG(D_USBO, "reg write: 0x%02x,0x%02x:0x%02x, %d",
+ req, index, value, ret);
+ if (ret < 0)
+ pr_err("reg write: error %d\n", ret);
+ return ret;
+}
+
+/* returns: negative is error, pos or zero is data */
+static int reg_read(struct gspca_dev *gspca_dev,
+ u16 req, /* bRequest */
+ u16 index) /* wIndex */
+{
+ int ret;
+
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ req,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, /* value */
+ index,
+ gspca_dev->usb_buf, 2,
+ 500); /* timeout */
+ if (ret < 0)
+ return ret;
+ return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0];
+}
+
+static int write_vector(struct gspca_dev *gspca_dev,
+ const u8 data[][3])
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int ret, i = 0;
+
+ while (data[i][0] != 0) {
+ ret = reg_write(dev, data[i][0], data[i][2], data[i][1]);
+ if (ret < 0)
+ return ret;
+ i++;
+ }
+ return 0;
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+ cam->cam_mode = vga_mode;
+ sd->subtype = id->driver_info;
+ if (sd->subtype != IntelPCCameraPro)
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+ else /* no 640x480 for IntelPCCameraPro */
+ cam->nmodes = ARRAY_SIZE(vga_mode) - 1;
+ sd->brightness = BRIGHTNESS_DEF;
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (write_vector(gspca_dev,
+ sd->subtype == Nxultra
+ ? spca505b_init_data
+ : spca505_init_data))
+ return -EIO;
+ return 0;
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 brightness = sd->brightness;
+
+ reg_write(gspca_dev->dev, 0x05, 0x00, (255 - brightness) >> 6);
+ reg_write(gspca_dev->dev, 0x05, 0x01, (255 - brightness) << 2);
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct usb_device *dev = gspca_dev->dev;
+ int ret, mode;
+ static u8 mode_tb[][3] = {
+ /* r00 r06 r07 */
+ {0x00, 0x10, 0x10}, /* 640x480 */
+ {0x01, 0x1a, 0x1a}, /* 352x288 */
+ {0x02, 0x1c, 0x1d}, /* 320x240 */
+ {0x04, 0x34, 0x34}, /* 176x144 */
+ {0x05, 0x40, 0x40} /* 160x120 */
+ };
+
+ if (sd->subtype == Nxultra)
+ write_vector(gspca_dev, spca505b_open_data_ccd);
+ else
+ write_vector(gspca_dev, spca505_open_data_ccd);
+ ret = reg_read(gspca_dev, 0x06, 0x16);
+
+ if (ret < 0) {
+ PDEBUG(D_ERR|D_CONF,
+ "register read failed err: %d",
+ ret);
+ return ret;
+ }
+ if (ret != 0x0101) {
+ pr_err("After vector read returns 0x%04x should be 0x0101\n",
+ ret);
+ }
+
+ ret = reg_write(gspca_dev->dev, 0x06, 0x16, 0x0a);
+ if (ret < 0)
+ return ret;
+ reg_write(gspca_dev->dev, 0x05, 0xc2, 0x12);
+
+ /* necessary because without it we can see stream
+ * only once after loading module */
+ /* stopping usb registers Tomasz change */
+ reg_write(dev, 0x02, 0x00, 0x00);
+
+ mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+ reg_write(dev, SPCA50X_REG_COMPRESS, 0x00, mode_tb[mode][0]);
+ reg_write(dev, SPCA50X_REG_COMPRESS, 0x06, mode_tb[mode][1]);
+ reg_write(dev, SPCA50X_REG_COMPRESS, 0x07, mode_tb[mode][2]);
+
+ ret = reg_write(dev, SPCA50X_REG_USB,
+ SPCA50X_USB_CTRL,
+ SPCA50X_CUSB_ENABLE);
+
+ setbrightness(gspca_dev);
+
+ return ret;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ /* Disable ISO packet machine */
+ reg_write(gspca_dev->dev, 0x02, 0x00, 0x00);
+}
+
+/* called on streamoff with alt 0 and on disconnect */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ if (!gspca_dev->present)
+ return;
+
+ /* This maybe reset or power control */
+ reg_write(gspca_dev->dev, 0x03, 0x03, 0x20);
+ reg_write(gspca_dev->dev, 0x03, 0x01, 0x00);
+ reg_write(gspca_dev->dev, 0x03, 0x00, 0x01);
+ reg_write(gspca_dev->dev, 0x05, 0x10, 0x01);
+ reg_write(gspca_dev->dev, 0x05, 0x11, 0x0f);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ switch (data[0]) {
+ case 0: /* start of frame */
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+ data += SPCA50X_OFFSET_DATA;
+ len -= SPCA50X_OFFSET_DATA;
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+ break;
+ case 0xff: /* drop */
+ break;
+ default:
+ data += 1;
+ len -= 1;
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ break;
+ }
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->brightness = val;
+ if (gspca_dev->streaming)
+ setbrightness(gspca_dev);
+ return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->brightness;
+ return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x041e, 0x401d), .driver_info = Nxultra},
+ {USB_DEVICE(0x0733, 0x0430), .driver_info = IntelPCCameraPro},
+/*fixme: may be UsbGrabberPV321 BRIDGE_SPCA506 SENSOR_SAA7113 */
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/spca506.c b/drivers/media/video/gspca/spca506.c
new file mode 100644
index 00000000..54eed876
--- /dev/null
+++ b/drivers/media/video/gspca/spca506.c
@@ -0,0 +1,717 @@
+/*
+ * SPCA506 chip based cameras function
+ * M Xhaard 15/04/2004 based on different work Mark Taylor and others
+ * and my own snoopy file on a pv-321c donate by a german compagny
+ * "Firma Frank Gmbh" from Saarbruecken
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define MODULE_NAME "spca506"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("GSPCA/SPCA506 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ unsigned char brightness;
+ unsigned char contrast;
+ unsigned char colors;
+ unsigned char hue;
+ char norme;
+ char channel;
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val);
+
+static const struct ctrl sd_ctrls[] = {
+#define SD_BRIGHTNESS 0
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x80,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+#define SD_CONTRAST 1
+ {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x47,
+ },
+ .set = sd_setcontrast,
+ .get = sd_getcontrast,
+ },
+#define SD_COLOR 2
+ {
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x40,
+ },
+ .set = sd_setcolors,
+ .get = sd_getcolors,
+ },
+#define SD_HUE 3
+ {
+ {
+ .id = V4L2_CID_HUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Hue",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set = sd_sethue,
+ .get = sd_gethue,
+ },
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120 * 3 / 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 5},
+ {176, 144, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144 * 3 / 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 4},
+ {320, 240, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 2},
+ {352, 288, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288 * 3 / 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+
+#define SPCA50X_OFFSET_DATA 10
+
+#define SAA7113_bright 0x0a /* defaults 0x80 */
+#define SAA7113_contrast 0x0b /* defaults 0x47 */
+#define SAA7113_saturation 0x0c /* defaults 0x40 */
+#define SAA7113_hue 0x0d /* defaults 0x00 */
+#define SAA7113_I2C_BASE_WRITE 0x4a
+
+/* read 'len' bytes to gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev,
+ __u16 req,
+ __u16 index,
+ __u16 length)
+{
+ usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ req,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, /* value */
+ index, gspca_dev->usb_buf, length,
+ 500);
+}
+
+static void reg_w(struct usb_device *dev,
+ __u16 req,
+ __u16 value,
+ __u16 index)
+{
+ usb_control_msg(dev,
+ usb_sndctrlpipe(dev, 0),
+ req,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index,
+ NULL, 0, 500);
+}
+
+static void spca506_Initi2c(struct gspca_dev *gspca_dev)
+{
+ reg_w(gspca_dev->dev, 0x07, SAA7113_I2C_BASE_WRITE, 0x0004);
+}
+
+static void spca506_WriteI2c(struct gspca_dev *gspca_dev, __u16 valeur,
+ __u16 reg)
+{
+ int retry = 60;
+
+ reg_w(gspca_dev->dev, 0x07, reg, 0x0001);
+ reg_w(gspca_dev->dev, 0x07, valeur, 0x0000);
+ while (retry--) {
+ reg_r(gspca_dev, 0x07, 0x0003, 2);
+ if ((gspca_dev->usb_buf[0] | gspca_dev->usb_buf[1]) == 0x00)
+ break;
+ }
+}
+
+static void spca506_SetNormeInput(struct gspca_dev *gspca_dev,
+ __u16 norme,
+ __u16 channel)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+/* fixme: check if channel == 0..3 and 6..9 (8 values) */
+ __u8 setbit0 = 0x00;
+ __u8 setbit1 = 0x00;
+ __u8 videomask = 0x00;
+
+ PDEBUG(D_STREAM, "** Open Set Norme **");
+ spca506_Initi2c(gspca_dev);
+ /* NTSC bit0 -> 1(525 l) PAL SECAM bit0 -> 0 (625 l) */
+ /* Composite channel bit1 -> 1 S-video bit 1 -> 0 */
+ /* and exclude SAA7113 reserved channel set default 0 otherwise */
+ if (norme & V4L2_STD_NTSC)
+ setbit0 = 0x01;
+ if (channel == 4 || channel == 5 || channel > 9)
+ channel = 0;
+ if (channel < 4)
+ setbit1 = 0x02;
+ videomask = (0x48 | setbit0 | setbit1);
+ reg_w(gspca_dev->dev, 0x08, videomask, 0x0000);
+ spca506_WriteI2c(gspca_dev, (0xc0 | (channel & 0x0F)), 0x02);
+
+ if (norme & V4L2_STD_NTSC)
+ spca506_WriteI2c(gspca_dev, 0x33, 0x0e);
+ /* Chrominance Control NTSC N */
+ else if (norme & V4L2_STD_SECAM)
+ spca506_WriteI2c(gspca_dev, 0x53, 0x0e);
+ /* Chrominance Control SECAM */
+ else
+ spca506_WriteI2c(gspca_dev, 0x03, 0x0e);
+ /* Chrominance Control PAL BGHIV */
+
+ sd->norme = norme;
+ sd->channel = channel;
+ PDEBUG(D_STREAM, "Set Video Byte to 0x%2x", videomask);
+ PDEBUG(D_STREAM, "Set Norme: %08x Channel %d", norme, channel);
+}
+
+static void spca506_GetNormeInput(struct gspca_dev *gspca_dev,
+ __u16 *norme, __u16 *channel)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* Read the register is not so good value change so
+ we use your own copy in spca50x struct */
+ *norme = sd->norme;
+ *channel = sd->channel;
+ PDEBUG(D_STREAM, "Get Norme: %d Channel %d", *norme, *channel);
+}
+
+static void spca506_Setsize(struct gspca_dev *gspca_dev, __u16 code,
+ __u16 xmult, __u16 ymult)
+{
+ struct usb_device *dev = gspca_dev->dev;
+
+ PDEBUG(D_STREAM, "** SetSize **");
+ reg_w(dev, 0x04, (0x18 | (code & 0x07)), 0x0000);
+ /* Soft snap 0x40 Hard 0x41 */
+ reg_w(dev, 0x04, 0x41, 0x0001);
+ reg_w(dev, 0x04, 0x00, 0x0002);
+ /* reserved */
+ reg_w(dev, 0x04, 0x00, 0x0003);
+
+ /* reserved */
+ reg_w(dev, 0x04, 0x00, 0x0004);
+ /* reserved */
+ reg_w(dev, 0x04, 0x01, 0x0005);
+ /* reserced */
+ reg_w(dev, 0x04, xmult, 0x0006);
+ /* reserved */
+ reg_w(dev, 0x04, ymult, 0x0007);
+ /* compression 1 */
+ reg_w(dev, 0x04, 0x00, 0x0008);
+ /* T=64 -> 2 */
+ reg_w(dev, 0x04, 0x00, 0x0009);
+ /* threshold2D */
+ reg_w(dev, 0x04, 0x21, 0x000a);
+ /* quantization */
+ reg_w(dev, 0x04, 0x00, 0x000b);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+ sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value;
+ sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value;
+ sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value;
+ sd->hue = sd_ctrls[SD_HUE].qctrl.default_value;
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct usb_device *dev = gspca_dev->dev;
+
+ reg_w(dev, 0x03, 0x00, 0x0004);
+ reg_w(dev, 0x03, 0xFF, 0x0003);
+ reg_w(dev, 0x03, 0x00, 0x0000);
+ reg_w(dev, 0x03, 0x1c, 0x0001);
+ reg_w(dev, 0x03, 0x18, 0x0001);
+ /* Init on PAL and composite input0 */
+ spca506_SetNormeInput(gspca_dev, 0, 0);
+ reg_w(dev, 0x03, 0x1c, 0x0001);
+ reg_w(dev, 0x03, 0x18, 0x0001);
+ reg_w(dev, 0x05, 0x00, 0x0000);
+ reg_w(dev, 0x05, 0xef, 0x0001);
+ reg_w(dev, 0x05, 0x00, 0x00c1);
+ reg_w(dev, 0x05, 0x00, 0x00c2);
+ reg_w(dev, 0x06, 0x18, 0x0002);
+ reg_w(dev, 0x06, 0xf5, 0x0011);
+ reg_w(dev, 0x06, 0x02, 0x0012);
+ reg_w(dev, 0x06, 0xfb, 0x0013);
+ reg_w(dev, 0x06, 0x00, 0x0014);
+ reg_w(dev, 0x06, 0xa4, 0x0051);
+ reg_w(dev, 0x06, 0x40, 0x0052);
+ reg_w(dev, 0x06, 0x71, 0x0053);
+ reg_w(dev, 0x06, 0x40, 0x0054);
+ /************************************************/
+ reg_w(dev, 0x03, 0x00, 0x0004);
+ reg_w(dev, 0x03, 0x00, 0x0003);
+ reg_w(dev, 0x03, 0x00, 0x0004);
+ reg_w(dev, 0x03, 0xFF, 0x0003);
+ reg_w(dev, 0x02, 0x00, 0x0000);
+ reg_w(dev, 0x03, 0x60, 0x0000);
+ reg_w(dev, 0x03, 0x18, 0x0001);
+ /* for a better reading mx :) */
+ /*sdca506_WriteI2c(value,register) */
+ spca506_Initi2c(gspca_dev);
+ spca506_WriteI2c(gspca_dev, 0x08, 0x01);
+ spca506_WriteI2c(gspca_dev, 0xc0, 0x02);
+ /* input composite video */
+ spca506_WriteI2c(gspca_dev, 0x33, 0x03);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x04);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x05);
+ spca506_WriteI2c(gspca_dev, 0x0d, 0x06);
+ spca506_WriteI2c(gspca_dev, 0xf0, 0x07);
+ spca506_WriteI2c(gspca_dev, 0x98, 0x08);
+ spca506_WriteI2c(gspca_dev, 0x03, 0x09);
+ spca506_WriteI2c(gspca_dev, 0x80, 0x0a);
+ spca506_WriteI2c(gspca_dev, 0x47, 0x0b);
+ spca506_WriteI2c(gspca_dev, 0x48, 0x0c);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x0d);
+ spca506_WriteI2c(gspca_dev, 0x03, 0x0e); /* Chroma Pal adjust */
+ spca506_WriteI2c(gspca_dev, 0x2a, 0x0f);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x10);
+ spca506_WriteI2c(gspca_dev, 0x0c, 0x11);
+ spca506_WriteI2c(gspca_dev, 0xb8, 0x12);
+ spca506_WriteI2c(gspca_dev, 0x01, 0x13);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x14);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x15);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x16);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x17);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x18);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x19);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x1a);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x1b);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x1c);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x1d);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x1e);
+ spca506_WriteI2c(gspca_dev, 0xa1, 0x1f);
+ spca506_WriteI2c(gspca_dev, 0x02, 0x40);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x41);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x42);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x43);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x44);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x45);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x46);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x47);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x48);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x49);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x4a);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x4b);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x4c);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x4d);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x4e);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x4f);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x50);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x51);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x52);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x53);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x54);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x55);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x56);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x57);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x58);
+ spca506_WriteI2c(gspca_dev, 0x54, 0x59);
+ spca506_WriteI2c(gspca_dev, 0x07, 0x5a);
+ spca506_WriteI2c(gspca_dev, 0x83, 0x5b);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x5c);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x5d);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x5e);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x5f);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x60);
+ spca506_WriteI2c(gspca_dev, 0x05, 0x61);
+ spca506_WriteI2c(gspca_dev, 0x9f, 0x62);
+ PDEBUG(D_STREAM, "** Close Init *");
+ return 0;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ __u16 norme;
+ __u16 channel;
+
+ /**************************************/
+ reg_w(dev, 0x03, 0x00, 0x0004);
+ reg_w(dev, 0x03, 0x00, 0x0003);
+ reg_w(dev, 0x03, 0x00, 0x0004);
+ reg_w(dev, 0x03, 0xFF, 0x0003);
+ reg_w(dev, 0x02, 0x00, 0x0000);
+ reg_w(dev, 0x03, 0x60, 0x0000);
+ reg_w(dev, 0x03, 0x18, 0x0001);
+
+ /*sdca506_WriteI2c(value,register) */
+ spca506_Initi2c(gspca_dev);
+ spca506_WriteI2c(gspca_dev, 0x08, 0x01); /* Increment Delay */
+/* spca506_WriteI2c(gspca_dev, 0xc0, 0x02); * Analog Input Control 1 */
+ spca506_WriteI2c(gspca_dev, 0x33, 0x03);
+ /* Analog Input Control 2 */
+ spca506_WriteI2c(gspca_dev, 0x00, 0x04);
+ /* Analog Input Control 3 */
+ spca506_WriteI2c(gspca_dev, 0x00, 0x05);
+ /* Analog Input Control 4 */
+ spca506_WriteI2c(gspca_dev, 0x0d, 0x06);
+ /* Horizontal Sync Start 0xe9-0x0d */
+ spca506_WriteI2c(gspca_dev, 0xf0, 0x07);
+ /* Horizontal Sync Stop 0x0d-0xf0 */
+
+ spca506_WriteI2c(gspca_dev, 0x98, 0x08); /* Sync Control */
+/* Defaults value */
+ spca506_WriteI2c(gspca_dev, 0x03, 0x09); /* Luminance Control */
+ spca506_WriteI2c(gspca_dev, 0x80, 0x0a);
+ /* Luminance Brightness */
+ spca506_WriteI2c(gspca_dev, 0x47, 0x0b); /* Luminance Contrast */
+ spca506_WriteI2c(gspca_dev, 0x48, 0x0c);
+ /* Chrominance Saturation */
+ spca506_WriteI2c(gspca_dev, 0x00, 0x0d);
+ /* Chrominance Hue Control */
+ spca506_WriteI2c(gspca_dev, 0x2a, 0x0f);
+ /* Chrominance Gain Control */
+ /**************************************/
+ spca506_WriteI2c(gspca_dev, 0x00, 0x10);
+ /* Format/Delay Control */
+ spca506_WriteI2c(gspca_dev, 0x0c, 0x11); /* Output Control 1 */
+ spca506_WriteI2c(gspca_dev, 0xb8, 0x12); /* Output Control 2 */
+ spca506_WriteI2c(gspca_dev, 0x01, 0x13); /* Output Control 3 */
+ spca506_WriteI2c(gspca_dev, 0x00, 0x14); /* reserved */
+ spca506_WriteI2c(gspca_dev, 0x00, 0x15); /* VGATE START */
+ spca506_WriteI2c(gspca_dev, 0x00, 0x16); /* VGATE STOP */
+ spca506_WriteI2c(gspca_dev, 0x00, 0x17); /* VGATE Control (MSB) */
+ spca506_WriteI2c(gspca_dev, 0x00, 0x18);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x19);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x1a);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x1b);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x1c);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x1d);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x1e);
+ spca506_WriteI2c(gspca_dev, 0xa1, 0x1f);
+ spca506_WriteI2c(gspca_dev, 0x02, 0x40);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x41);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x42);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x43);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x44);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x45);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x46);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x47);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x48);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x49);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x4a);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x4b);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x4c);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x4d);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x4e);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x4f);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x50);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x51);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x52);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x53);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x54);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x55);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x56);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x57);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x58);
+ spca506_WriteI2c(gspca_dev, 0x54, 0x59);
+ spca506_WriteI2c(gspca_dev, 0x07, 0x5a);
+ spca506_WriteI2c(gspca_dev, 0x83, 0x5b);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x5c);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x5d);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x5e);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x5f);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x60);
+ spca506_WriteI2c(gspca_dev, 0x05, 0x61);
+ spca506_WriteI2c(gspca_dev, 0x9f, 0x62);
+ /**************************************/
+ reg_w(dev, 0x05, 0x00, 0x0003);
+ reg_w(dev, 0x05, 0x00, 0x0004);
+ reg_w(dev, 0x03, 0x10, 0x0001);
+ reg_w(dev, 0x03, 0x78, 0x0000);
+ switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
+ case 0:
+ spca506_Setsize(gspca_dev, 0, 0x10, 0x10);
+ break;
+ case 1:
+ spca506_Setsize(gspca_dev, 1, 0x1a, 0x1a);
+ break;
+ case 2:
+ spca506_Setsize(gspca_dev, 2, 0x1c, 0x1c);
+ break;
+ case 4:
+ spca506_Setsize(gspca_dev, 4, 0x34, 0x34);
+ break;
+ default:
+/* case 5: */
+ spca506_Setsize(gspca_dev, 5, 0x40, 0x40);
+ break;
+ }
+
+ /* compress setting and size */
+ /* set i2c luma */
+ reg_w(dev, 0x02, 0x01, 0x0000);
+ reg_w(dev, 0x03, 0x12, 0x0000);
+ reg_r(gspca_dev, 0x04, 0x0001, 2);
+ PDEBUG(D_STREAM, "webcam started");
+ spca506_GetNormeInput(gspca_dev, &norme, &channel);
+ spca506_SetNormeInput(gspca_dev, norme, channel);
+ return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct usb_device *dev = gspca_dev->dev;
+
+ reg_w(dev, 0x02, 0x00, 0x0000);
+ reg_w(dev, 0x03, 0x00, 0x0004);
+ reg_w(dev, 0x03, 0x00, 0x0003);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ switch (data[0]) {
+ case 0: /* start of frame */
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+ data += SPCA50X_OFFSET_DATA;
+ len -= SPCA50X_OFFSET_DATA;
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+ break;
+ case 0xff: /* drop */
+/* gspca_dev->last_packet_type = DISCARD_PACKET; */
+ break;
+ default:
+ data += 1;
+ len -= 1;
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ break;
+ }
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ spca506_Initi2c(gspca_dev);
+ spca506_WriteI2c(gspca_dev, sd->brightness, SAA7113_bright);
+ spca506_WriteI2c(gspca_dev, 0x01, 0x09);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ spca506_Initi2c(gspca_dev);
+ spca506_WriteI2c(gspca_dev, sd->contrast, SAA7113_contrast);
+ spca506_WriteI2c(gspca_dev, 0x01, 0x09);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ spca506_Initi2c(gspca_dev);
+ spca506_WriteI2c(gspca_dev, sd->colors, SAA7113_saturation);
+ spca506_WriteI2c(gspca_dev, 0x01, 0x09);
+}
+
+static void sethue(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ spca506_Initi2c(gspca_dev);
+ spca506_WriteI2c(gspca_dev, sd->hue, SAA7113_hue);
+ spca506_WriteI2c(gspca_dev, 0x01, 0x09);
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->brightness = val;
+ if (gspca_dev->streaming)
+ setbrightness(gspca_dev);
+ return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->brightness;
+ return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->contrast = val;
+ if (gspca_dev->streaming)
+ setcontrast(gspca_dev);
+ return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->contrast;
+ return 0;
+}
+
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->colors = val;
+ if (gspca_dev->streaming)
+ setcolors(gspca_dev);
+ return 0;
+}
+
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->colors;
+ return 0;
+}
+
+static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->hue = val;
+ if (gspca_dev->streaming)
+ sethue(gspca_dev);
+ return 0;
+}
+
+static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->hue;
+ return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] __devinitconst = {
+ {USB_DEVICE(0x06e1, 0xa190)},
+/*fixme: may be IntelPCCameraPro BRIDGE_SPCA505
+ {USB_DEVICE(0x0733, 0x0430)}, */
+ {USB_DEVICE(0x0734, 0x043b)},
+ {USB_DEVICE(0x99fa, 0x8988)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int __devinit sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c
new file mode 100644
index 00000000..df4e1699
--- /dev/null
+++ b/drivers/media/video/gspca/spca508.c
@@ -0,0 +1,1547 @@
+/*
+ * SPCA508 chip based cameras subdriver
+ *
+ * Copyright (C) 2009 Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "spca508"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("GSPCA/SPCA508 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ u8 brightness;
+
+ u8 subtype;
+#define CreativeVista 0
+#define HamaUSBSightcam 1
+#define HamaUSBSightcam2 2
+#define IntelEasyPCCamera 3
+#define MicroInnovationIC200 4
+#define ViewQuestVQ110 5
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+
+static const struct ctrl sd_ctrls[] = {
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+#define BRIGHTNESS_DEF 128
+ .default_value = BRIGHTNESS_DEF,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+};
+
+static const struct v4l2_pix_format sif_mode[] = {
+ {160, 120, V4L2_PIX_FMT_SPCA508, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120 * 3 / 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 3},
+ {176, 144, V4L2_PIX_FMT_SPCA508, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144 * 3 / 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 2},
+ {320, 240, V4L2_PIX_FMT_SPCA508, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {352, 288, V4L2_PIX_FMT_SPCA508, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288 * 3 / 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+
+/* Frame packet header offsets for the spca508 */
+#define SPCA508_OFFSET_DATA 37
+
+/*
+ * Initialization data: this is the first set-up data written to the
+ * device (before the open data).
+ */
+static const u16 spca508_init_data[][2] = {
+ {0x0000, 0x870b},
+
+ {0x0020, 0x8112}, /* Video drop enable, ISO streaming disable */
+ {0x0003, 0x8111}, /* Reset compression & memory */
+ {0x0000, 0x8110}, /* Disable all outputs */
+ /* READ {0x0000, 0x8114} -> 0000: 00 */
+ {0x0000, 0x8114}, /* SW GPIO data */
+ {0x0008, 0x8110}, /* Enable charge pump output */
+ {0x0002, 0x8116}, /* 200 kHz pump clock */
+ /* UNKNOWN DIRECTION (URB_FUNCTION_SELECT_INTERFACE:) */
+ {0x0003, 0x8111}, /* Reset compression & memory */
+ {0x0000, 0x8111}, /* Normal mode (not reset) */
+ {0x0098, 0x8110},
+ /* Enable charge pump output, sync.serial,external 2x clock */
+ {0x000d, 0x8114}, /* SW GPIO data */
+ {0x0002, 0x8116}, /* 200 kHz pump clock */
+ {0x0020, 0x8112}, /* Video drop enable, ISO streaming disable */
+/* --------------------------------------- */
+ {0x000f, 0x8402}, /* memory bank */
+ {0x0000, 0x8403}, /* ... address */
+/* --------------------------------------- */
+/* 0x88__ is Synchronous Serial Interface. */
+/* TBD: This table could be expressed more compactly */
+/* using spca508_write_i2c_vector(). */
+/* TBD: Should see if the values in spca50x_i2c_data */
+/* would work with the VQ110 instead of the values */
+/* below. */
+ {0x00c0, 0x8804}, /* SSI slave addr */
+ {0x0008, 0x8802}, /* 375 Khz SSI clock */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802}, /* 375 Khz SSI clock */
+ {0x0012, 0x8801}, /* SSI reg addr */
+ {0x0080, 0x8800}, /* SSI data to write */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802}, /* 375 Khz SSI clock */
+ {0x0012, 0x8801}, /* SSI reg addr */
+ {0x0000, 0x8800}, /* SSI data to write */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802}, /* 375 Khz SSI clock */
+ {0x0011, 0x8801}, /* SSI reg addr */
+ {0x0040, 0x8800}, /* SSI data to write */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x0013, 0x8801},
+ {0x0000, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x0014, 0x8801},
+ {0x0000, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x0015, 0x8801},
+ {0x0001, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x0016, 0x8801},
+ {0x0003, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x0017, 0x8801},
+ {0x0036, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x0018, 0x8801},
+ {0x00ec, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x001a, 0x8801},
+ {0x0094, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x001b, 0x8801},
+ {0x0000, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x0027, 0x8801},
+ {0x00a2, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x0028, 0x8801},
+ {0x0040, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x002a, 0x8801},
+ {0x0084, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x002b, 0x8801},
+ {0x00a8, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x002c, 0x8801},
+ {0x00fe, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x002d, 0x8801},
+ {0x0003, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x0038, 0x8801},
+ {0x0083, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x0033, 0x8801},
+ {0x0081, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x0034, 0x8801},
+ {0x004a, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x0039, 0x8801},
+ {0x0000, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x0010, 0x8801},
+ {0x00a8, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x0006, 0x8801},
+ {0x0058, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x0000, 0x8801},
+ {0x0004, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x0040, 0x8801},
+ {0x0080, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x0041, 0x8801},
+ {0x000c, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x0042, 0x8801},
+ {0x000c, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x0043, 0x8801},
+ {0x0028, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x0044, 0x8801},
+ {0x0080, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x0045, 0x8801},
+ {0x0020, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x0046, 0x8801},
+ {0x0020, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x0047, 0x8801},
+ {0x0080, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x0048, 0x8801},
+ {0x004c, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x0049, 0x8801},
+ {0x0084, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x004a, 0x8801},
+ {0x0084, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x0008, 0x8802},
+ {0x004b, 0x8801},
+ {0x0084, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* --------------------------------------- */
+ {0x0012, 0x8700}, /* Clock speed 48Mhz/(2+2)/2= 6 Mhz */
+ {0x0000, 0x8701}, /* CKx1 clock delay adj */
+ {0x0000, 0x8701}, /* CKx1 clock delay adj */
+ {0x0001, 0x870c}, /* CKOx2 output */
+ /* --------------------------------------- */
+ {0x0080, 0x8600}, /* Line memory read counter (L) */
+ {0x0001, 0x8606}, /* reserved */
+ {0x0064, 0x8607}, /* Line memory read counter (H) 0x6480=25,728 */
+ {0x002a, 0x8601}, /* CDSP sharp interpolation mode,
+ * line sel for color sep, edge enhance enab */
+ {0x0000, 0x8602}, /* optical black level for user settng = 0 */
+ {0x0080, 0x8600}, /* Line memory read counter (L) */
+ {0x000a, 0x8603}, /* optical black level calc mode:
+ * auto; optical black offset = 10 */
+ {0x00df, 0x865b}, /* Horiz offset for valid pixels (L)=0xdf */
+ {0x0012, 0x865c}, /* Vert offset for valid lines (L)=0x12 */
+
+/* The following two lines seem to be the "wrong" resolution. */
+/* But perhaps these indicate the actual size of the sensor */
+/* rather than the size of the current video mode. */
+ {0x0058, 0x865d}, /* Horiz valid pixels (*4) (L) = 352 */
+ {0x0048, 0x865e}, /* Vert valid lines (*4) (L) = 288 */
+
+ {0x0015, 0x8608}, /* A11 Coef ... */
+ {0x0030, 0x8609},
+ {0x00fb, 0x860a},
+ {0x003e, 0x860b},
+ {0x00ce, 0x860c},
+ {0x00f4, 0x860d},
+ {0x00eb, 0x860e},
+ {0x00dc, 0x860f},
+ {0x0039, 0x8610},
+ {0x0001, 0x8611}, /* R offset for white balance ... */
+ {0x0000, 0x8612},
+ {0x0001, 0x8613},
+ {0x0000, 0x8614},
+ {0x005b, 0x8651}, /* R gain for white balance ... */
+ {0x0040, 0x8652},
+ {0x0060, 0x8653},
+ {0x0040, 0x8654},
+ {0x0000, 0x8655},
+ {0x0001, 0x863f}, /* Fixed gamma correction enable, USB control,
+ * lum filter disable, lum noise clip disable */
+ {0x00a1, 0x8656}, /* Window1 size 256x256, Windows2 size 64x64,
+ * gamma look-up disable,
+ * new edge enhancement enable */
+ {0x0018, 0x8657}, /* Edge gain high thresh */
+ {0x0020, 0x8658}, /* Edge gain low thresh */
+ {0x000a, 0x8659}, /* Edge bandwidth high threshold */
+ {0x0005, 0x865a}, /* Edge bandwidth low threshold */
+ /* -------------------------------- */
+ {0x0030, 0x8112}, /* Video drop enable, ISO streaming enable */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0xa908, 0x8802},
+ {0x0034, 0x8801}, /* SSI reg addr */
+ {0x00ca, 0x8800},
+ /* SSI data to write */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0x1f08, 0x8802},
+ {0x0006, 0x8801},
+ {0x0080, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+
+/* ----- Read back coefs we wrote earlier. */
+ /* READ { 0x0000, 0x8608 } -> 0000: 15 */
+ /* READ { 0x0000, 0x8609 } -> 0000: 30 */
+ /* READ { 0x0000, 0x860a } -> 0000: fb */
+ /* READ { 0x0000, 0x860b } -> 0000: 3e */
+ /* READ { 0x0000, 0x860c } -> 0000: ce */
+ /* READ { 0x0000, 0x860d } -> 0000: f4 */
+ /* READ { 0x0000, 0x860e } -> 0000: eb */
+ /* READ { 0x0000, 0x860f } -> 0000: dc */
+ /* READ { 0x0000, 0x8610 } -> 0000: 39 */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 08 */
+ {0xb008, 0x8802},
+ {0x0006, 0x8801},
+ {0x007d, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+
+
+ /* This chunk is seemingly redundant with */
+ /* earlier commands (A11 Coef...), but if I disable it, */
+ /* the image appears too dark. Maybe there was some kind of */
+ /* reset since the earlier commands, so this is necessary again. */
+ {0x0015, 0x8608},
+ {0x0030, 0x8609},
+ {0xfffb, 0x860a},
+ {0x003e, 0x860b},
+ {0xffce, 0x860c},
+ {0xfff4, 0x860d},
+ {0xffeb, 0x860e},
+ {0xffdc, 0x860f},
+ {0x0039, 0x8610},
+ {0x0018, 0x8657},
+
+ {0x0000, 0x8508}, /* Disable compression. */
+ /* Previous line was:
+ {0x0021, 0x8508}, * Enable compression. */
+ {0x0032, 0x850b}, /* compression stuff */
+ {0x0003, 0x8509}, /* compression stuff */
+ {0x0011, 0x850a}, /* compression stuff */
+ {0x0021, 0x850d}, /* compression stuff */
+ {0x0010, 0x850c}, /* compression stuff */
+ {0x0003, 0x8500}, /* *** Video mode: 160x120 */
+ {0x0001, 0x8501}, /* Hardware-dominated snap control */
+ {0x0061, 0x8656}, /* Window1 size 128x128, Windows2 size 128x128,
+ * gamma look-up disable,
+ * new edge enhancement enable */
+ {0x0018, 0x8617}, /* Window1 start X (*2) */
+ {0x0008, 0x8618}, /* Window1 start Y (*2) */
+ {0x0061, 0x8656}, /* Window1 size 128x128, Windows2 size 128x128,
+ * gamma look-up disable,
+ * new edge enhancement enable */
+ {0x0058, 0x8619}, /* Window2 start X (*2) */
+ {0x0008, 0x861a}, /* Window2 start Y (*2) */
+ {0x00ff, 0x8615}, /* High lum thresh for white balance */
+ {0x0000, 0x8616}, /* Low lum thresh for white balance */
+ {0x0012, 0x8700}, /* Clock speed 48Mhz/(2+2)/2= 6 Mhz */
+ {0x0012, 0x8700}, /* Clock speed 48Mhz/(2+2)/2= 6 Mhz */
+ /* READ { 0x0000, 0x8656 } -> 0000: 61 */
+ {0x0028, 0x8802}, /* 375 Khz SSI clock, SSI r/w sync with VSYNC */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 28 */
+ {0x1f28, 0x8802}, /* 375 Khz SSI clock, SSI r/w sync with VSYNC */
+ {0x0010, 0x8801}, /* SSI reg addr */
+ {0x003e, 0x8800}, /* SSI data to write */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ {0x0028, 0x8802},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 28 */
+ {0x1f28, 0x8802},
+ {0x0000, 0x8801},
+ {0x001f, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ {0x0001, 0x8602}, /* optical black level for user settning = 1 */
+
+ /* Original: */
+ {0x0023, 0x8700}, /* Clock speed 48Mhz/(3+2)/4= 2.4 Mhz */
+ {0x000f, 0x8602}, /* optical black level for user settning = 15 */
+
+ {0x0028, 0x8802},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 28 */
+ {0x1f28, 0x8802},
+ {0x0010, 0x8801},
+ {0x007b, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ {0x002f, 0x8651}, /* R gain for white balance ... */
+ {0x0080, 0x8653},
+ /* READ { 0x0000, 0x8655 } -> 0000: 00 */
+ {0x0000, 0x8655},
+
+ {0x0030, 0x8112}, /* Video drop enable, ISO streaming enable */
+ {0x0020, 0x8112}, /* Video drop enable, ISO streaming disable */
+ /* UNKNOWN DIRECTION (URB_FUNCTION_SELECT_INTERFACE: (ALT=0) ) */
+ {}
+};
+
+/*
+ * Initialization data for Intel EasyPC Camera CS110
+ */
+static const u16 spca508cs110_init_data[][2] = {
+ {0x0000, 0x870b}, /* Reset CTL3 */
+ {0x0003, 0x8111}, /* Soft Reset compression, memory, TG & CDSP */
+ {0x0000, 0x8111}, /* Normal operation on reset */
+ {0x0090, 0x8110},
+ /* External Clock 2x & Synchronous Serial Interface Output */
+ {0x0020, 0x8112}, /* Video Drop packet enable */
+ {0x0000, 0x8114}, /* Software GPIO output data */
+ {0x0001, 0x8114},
+ {0x0001, 0x8114},
+ {0x0001, 0x8114},
+ {0x0003, 0x8114},
+
+ /* Initial sequence Synchronous Serial Interface */
+ {0x000f, 0x8402}, /* Memory bank Address */
+ {0x0000, 0x8403}, /* Memory bank Address */
+ {0x00ba, 0x8804}, /* SSI Slave address */
+ {0x0010, 0x8802}, /* 93.75kHz SSI Clock Two DataByte */
+ {0x0010, 0x8802}, /* 93.75kHz SSI Clock two DataByte */
+
+ {0x0001, 0x8801},
+ {0x000a, 0x8805}, /* a - NWG: Dunno what this is about */
+ {0x0000, 0x8800},
+ {0x0010, 0x8802},
+
+ {0x0002, 0x8801},
+ {0x0000, 0x8805},
+ {0x0000, 0x8800},
+ {0x0010, 0x8802},
+
+ {0x0003, 0x8801},
+ {0x0027, 0x8805},
+ {0x0001, 0x8800},
+ {0x0010, 0x8802},
+
+ {0x0004, 0x8801},
+ {0x0065, 0x8805},
+ {0x0001, 0x8800},
+ {0x0010, 0x8802},
+
+ {0x0005, 0x8801},
+ {0x0003, 0x8805},
+ {0x0000, 0x8800},
+ {0x0010, 0x8802},
+
+ {0x0006, 0x8801},
+ {0x001c, 0x8805},
+ {0x0000, 0x8800},
+ {0x0010, 0x8802},
+
+ {0x0007, 0x8801},
+ {0x002a, 0x8805},
+ {0x0000, 0x8800},
+ {0x0010, 0x8802},
+
+ {0x0002, 0x8704}, /* External input CKIx1 */
+ {0x0001, 0x8606}, /* 1 Line memory Read Counter (H) Result: (d)410 */
+ {0x009a, 0x8600}, /* Line memory Read Counter (L) */
+ {0x0001, 0x865b}, /* 1 Horizontal Offset for Valid Pixel(L) */
+ {0x0003, 0x865c}, /* 3 Vertical Offset for Valid Lines(L) */
+ {0x0058, 0x865d}, /* 58 Horizontal Valid Pixel Window(L) */
+
+ {0x0006, 0x8660}, /* Nibble data + input order */
+
+ {0x000a, 0x8602}, /* Optical black level set to 0x0a */
+ {0x0000, 0x8603}, /* Optical black level Offset */
+
+/* {0x0000, 0x8611}, * 0 R Offset for white Balance */
+/* {0x0000, 0x8612}, * 1 Gr Offset for white Balance */
+/* {0x0000, 0x8613}, * 1f B Offset for white Balance */
+/* {0x0000, 0x8614}, * f0 Gb Offset for white Balance */
+
+ {0x0040, 0x8651}, /* 2b BLUE gain for white balance good at all 60 */
+ {0x0030, 0x8652}, /* 41 Gr Gain for white Balance (L) */
+ {0x0035, 0x8653}, /* 26 RED gain for white balance */
+ {0x0035, 0x8654}, /* 40Gb Gain for white Balance (L) */
+ {0x0041, 0x863f},
+ /* Fixed Gamma correction enabled (makes colours look better) */
+
+ {0x0000, 0x8655},
+ /* High bits for white balance*****brightness control*** */
+ {}
+};
+
+static const u16 spca508_sightcam_init_data[][2] = {
+/* This line seems to setup the frame/canvas */
+ {0x000f, 0x8402},
+
+/* These 6 lines are needed to startup the webcam */
+ {0x0090, 0x8110},
+ {0x0001, 0x8114},
+ {0x0001, 0x8114},
+ {0x0001, 0x8114},
+ {0x0003, 0x8114},
+ {0x0080, 0x8804},
+
+/* This part seems to make the pictures darker? (autobrightness?) */
+ {0x0001, 0x8801},
+ {0x0004, 0x8800},
+ {0x0003, 0x8801},
+ {0x00e0, 0x8800},
+ {0x0004, 0x8801},
+ {0x00b4, 0x8800},
+ {0x0005, 0x8801},
+ {0x0000, 0x8800},
+
+ {0x0006, 0x8801},
+ {0x00e0, 0x8800},
+ {0x0007, 0x8801},
+ {0x000c, 0x8800},
+
+/* This section is just needed, it probably
+ * does something like the previous section,
+ * but the cam won't start if it's not included.
+ */
+ {0x0014, 0x8801},
+ {0x0008, 0x8800},
+ {0x0015, 0x8801},
+ {0x0067, 0x8800},
+ {0x0016, 0x8801},
+ {0x0000, 0x8800},
+ {0x0017, 0x8801},
+ {0x0020, 0x8800},
+ {0x0018, 0x8801},
+ {0x0044, 0x8800},
+
+/* Makes the picture darker - and the
+ * cam won't start if not included
+ */
+ {0x001e, 0x8801},
+ {0x00ea, 0x8800},
+ {0x001f, 0x8801},
+ {0x0001, 0x8800},
+ {0x0003, 0x8801},
+ {0x00e0, 0x8800},
+
+/* seems to place the colors ontop of each other #1 */
+ {0x0006, 0x8704},
+ {0x0001, 0x870c},
+ {0x0016, 0x8600},
+ {0x0002, 0x8606},
+
+/* if not included the pictures becomes _very_ dark */
+ {0x0064, 0x8607},
+ {0x003a, 0x8601},
+ {0x0000, 0x8602},
+
+/* seems to place the colors ontop of each other #2 */
+ {0x0016, 0x8600},
+ {0x0018, 0x8617},
+ {0x0008, 0x8618},
+ {0x00a1, 0x8656},
+
+/* webcam won't start if not included */
+ {0x0007, 0x865b},
+ {0x0001, 0x865c},
+ {0x0058, 0x865d},
+ {0x0048, 0x865e},
+
+/* adjusts the colors */
+ {0x0049, 0x8651},
+ {0x0040, 0x8652},
+ {0x004c, 0x8653},
+ {0x0040, 0x8654},
+ {}
+};
+
+static const u16 spca508_sightcam2_init_data[][2] = {
+ {0x0020, 0x8112},
+
+ {0x000f, 0x8402},
+ {0x0000, 0x8403},
+
+ {0x0008, 0x8201},
+ {0x0008, 0x8200},
+ {0x0001, 0x8200},
+ {0x0009, 0x8201},
+ {0x0008, 0x8200},
+ {0x0001, 0x8200},
+ {0x000a, 0x8201},
+ {0x0008, 0x8200},
+ {0x0001, 0x8200},
+ {0x000b, 0x8201},
+ {0x0008, 0x8200},
+ {0x0001, 0x8200},
+ {0x000c, 0x8201},
+ {0x0008, 0x8200},
+ {0x0001, 0x8200},
+ {0x000d, 0x8201},
+ {0x0008, 0x8200},
+ {0x0001, 0x8200},
+ {0x000e, 0x8201},
+ {0x0008, 0x8200},
+ {0x0001, 0x8200},
+ {0x0007, 0x8201},
+ {0x0008, 0x8200},
+ {0x0001, 0x8200},
+ {0x000f, 0x8201},
+ {0x0008, 0x8200},
+ {0x0001, 0x8200},
+
+ {0x0018, 0x8660},
+ {0x0010, 0x8201},
+
+ {0x0008, 0x8200},
+ {0x0001, 0x8200},
+ {0x0011, 0x8201},
+ {0x0008, 0x8200},
+ {0x0001, 0x8200},
+
+ {0x0000, 0x86b0},
+ {0x0034, 0x86b1},
+ {0x0000, 0x86b2},
+ {0x0049, 0x86b3},
+ {0x0000, 0x86b4},
+ {0x0000, 0x86b4},
+
+ {0x0012, 0x8201},
+ {0x0008, 0x8200},
+ {0x0001, 0x8200},
+ {0x0013, 0x8201},
+ {0x0008, 0x8200},
+ {0x0001, 0x8200},
+
+ {0x0001, 0x86b0},
+ {0x00aa, 0x86b1},
+ {0x0000, 0x86b2},
+ {0x00e4, 0x86b3},
+ {0x0000, 0x86b4},
+ {0x0000, 0x86b4},
+
+ {0x0018, 0x8660},
+
+ {0x0090, 0x8110},
+ {0x0001, 0x8114},
+ {0x0001, 0x8114},
+ {0x0001, 0x8114},
+ {0x0003, 0x8114},
+
+ {0x0080, 0x8804},
+ {0x0003, 0x8801},
+ {0x0012, 0x8800},
+ {0x0004, 0x8801},
+ {0x0005, 0x8800},
+ {0x0005, 0x8801},
+ {0x0000, 0x8800},
+ {0x0006, 0x8801},
+ {0x0000, 0x8800},
+ {0x0007, 0x8801},
+ {0x0000, 0x8800},
+ {0x0008, 0x8801},
+ {0x0005, 0x8800},
+ {0x000a, 0x8700},
+ {0x000e, 0x8801},
+ {0x0004, 0x8800},
+ {0x0005, 0x8801},
+ {0x0047, 0x8800},
+ {0x0006, 0x8801},
+ {0x0000, 0x8800},
+ {0x0007, 0x8801},
+ {0x00c0, 0x8800},
+ {0x0008, 0x8801},
+ {0x0003, 0x8800},
+ {0x0013, 0x8801},
+ {0x0001, 0x8800},
+ {0x0009, 0x8801},
+ {0x0000, 0x8800},
+ {0x000a, 0x8801},
+ {0x0000, 0x8800},
+ {0x000b, 0x8801},
+ {0x0000, 0x8800},
+ {0x000c, 0x8801},
+ {0x0000, 0x8800},
+ {0x000e, 0x8801},
+ {0x0004, 0x8800},
+ {0x000f, 0x8801},
+ {0x0000, 0x8800},
+ {0x0010, 0x8801},
+ {0x0006, 0x8800},
+ {0x0011, 0x8801},
+ {0x0006, 0x8800},
+ {0x0012, 0x8801},
+ {0x0000, 0x8800},
+ {0x0013, 0x8801},
+ {0x0001, 0x8800},
+
+ {0x000a, 0x8700},
+ {0x0000, 0x8702},
+ {0x0000, 0x8703},
+ {0x00c2, 0x8704},
+ {0x0001, 0x870c},
+
+ {0x0044, 0x8600},
+ {0x0002, 0x8606},
+ {0x0064, 0x8607},
+ {0x003a, 0x8601},
+ {0x0008, 0x8602},
+ {0x0044, 0x8600},
+ {0x0018, 0x8617},
+ {0x0008, 0x8618},
+ {0x00a1, 0x8656},
+ {0x0004, 0x865b},
+ {0x0002, 0x865c},
+ {0x0058, 0x865d},
+ {0x0048, 0x865e},
+ {0x0012, 0x8608},
+ {0x002c, 0x8609},
+ {0x0002, 0x860a},
+ {0x002c, 0x860b},
+ {0x00db, 0x860c},
+ {0x00f9, 0x860d},
+ {0x00f1, 0x860e},
+ {0x00e3, 0x860f},
+ {0x002c, 0x8610},
+ {0x006c, 0x8651},
+ {0x0041, 0x8652},
+ {0x0059, 0x8653},
+ {0x0040, 0x8654},
+ {0x00fa, 0x8611},
+ {0x00ff, 0x8612},
+ {0x00f8, 0x8613},
+ {0x0000, 0x8614},
+ {0x0001, 0x863f},
+ {0x0000, 0x8640},
+ {0x0026, 0x8641},
+ {0x0045, 0x8642},
+ {0x0060, 0x8643},
+ {0x0075, 0x8644},
+ {0x0088, 0x8645},
+ {0x009b, 0x8646},
+ {0x00b0, 0x8647},
+ {0x00c5, 0x8648},
+ {0x00d2, 0x8649},
+ {0x00dc, 0x864a},
+ {0x00e5, 0x864b},
+ {0x00eb, 0x864c},
+ {0x00f0, 0x864d},
+ {0x00f6, 0x864e},
+ {0x00fa, 0x864f},
+ {0x00ff, 0x8650},
+ {0x0060, 0x8657},
+ {0x0010, 0x8658},
+ {0x0018, 0x8659},
+ {0x0005, 0x865a},
+ {0x0018, 0x8660},
+ {0x0003, 0x8509},
+ {0x0011, 0x850a},
+ {0x0032, 0x850b},
+ {0x0010, 0x850c},
+ {0x0021, 0x850d},
+ {0x0001, 0x8500},
+ {0x0000, 0x8508},
+ {0x0012, 0x8608},
+ {0x002c, 0x8609},
+ {0x0002, 0x860a},
+ {0x0039, 0x860b},
+ {0x00d0, 0x860c},
+ {0x00f7, 0x860d},
+ {0x00ed, 0x860e},
+ {0x00db, 0x860f},
+ {0x0039, 0x8610},
+ {0x0012, 0x8657},
+ {0x000c, 0x8619},
+ {0x0004, 0x861a},
+ {0x00a1, 0x8656},
+ {0x00c8, 0x8615},
+ {0x0032, 0x8616},
+
+ {0x0030, 0x8112},
+ {0x0020, 0x8112},
+ {0x0020, 0x8112},
+ {0x000f, 0x8402},
+ {0x0000, 0x8403},
+
+ {0x0090, 0x8110},
+ {0x0001, 0x8114},
+ {0x0001, 0x8114},
+ {0x0001, 0x8114},
+ {0x0003, 0x8114},
+ {0x0080, 0x8804},
+
+ {0x0003, 0x8801},
+ {0x0012, 0x8800},
+ {0x0004, 0x8801},
+ {0x0005, 0x8800},
+ {0x0005, 0x8801},
+ {0x0047, 0x8800},
+ {0x0006, 0x8801},
+ {0x0000, 0x8800},
+ {0x0007, 0x8801},
+ {0x00c0, 0x8800},
+ {0x0008, 0x8801},
+ {0x0003, 0x8800},
+ {0x000a, 0x8700},
+ {0x000e, 0x8801},
+ {0x0004, 0x8800},
+ {0x0005, 0x8801},
+ {0x0047, 0x8800},
+ {0x0006, 0x8801},
+ {0x0000, 0x8800},
+ {0x0007, 0x8801},
+ {0x00c0, 0x8800},
+ {0x0008, 0x8801},
+ {0x0003, 0x8800},
+ {0x0013, 0x8801},
+ {0x0001, 0x8800},
+ {0x0009, 0x8801},
+ {0x0000, 0x8800},
+ {0x000a, 0x8801},
+ {0x0000, 0x8800},
+ {0x000b, 0x8801},
+ {0x0000, 0x8800},
+ {0x000c, 0x8801},
+ {0x0000, 0x8800},
+ {0x000e, 0x8801},
+ {0x0004, 0x8800},
+ {0x000f, 0x8801},
+ {0x0000, 0x8800},
+ {0x0010, 0x8801},
+ {0x0006, 0x8800},
+ {0x0011, 0x8801},
+ {0x0006, 0x8800},
+ {0x0012, 0x8801},
+ {0x0000, 0x8800},
+ {0x0013, 0x8801},
+ {0x0001, 0x8800},
+ {0x000a, 0x8700},
+ {0x0000, 0x8702},
+ {0x0000, 0x8703},
+ {0x00c2, 0x8704},
+ {0x0001, 0x870c},
+ {0x0044, 0x8600},
+ {0x0002, 0x8606},
+ {0x0064, 0x8607},
+ {0x003a, 0x8601},
+ {0x0008, 0x8602},
+ {0x0044, 0x8600},
+ {0x0018, 0x8617},
+ {0x0008, 0x8618},
+ {0x00a1, 0x8656},
+ {0x0004, 0x865b},
+ {0x0002, 0x865c},
+ {0x0058, 0x865d},
+ {0x0048, 0x865e},
+ {0x0012, 0x8608},
+ {0x002c, 0x8609},
+ {0x0002, 0x860a},
+ {0x002c, 0x860b},
+ {0x00db, 0x860c},
+ {0x00f9, 0x860d},
+ {0x00f1, 0x860e},
+ {0x00e3, 0x860f},
+ {0x002c, 0x8610},
+ {0x006c, 0x8651},
+ {0x0041, 0x8652},
+ {0x0059, 0x8653},
+ {0x0040, 0x8654},
+ {0x00fa, 0x8611},
+ {0x00ff, 0x8612},
+ {0x00f8, 0x8613},
+ {0x0000, 0x8614},
+ {0x0001, 0x863f},
+ {0x0000, 0x8640},
+ {0x0026, 0x8641},
+ {0x0045, 0x8642},
+ {0x0060, 0x8643},
+ {0x0075, 0x8644},
+ {0x0088, 0x8645},
+ {0x009b, 0x8646},
+ {0x00b0, 0x8647},
+ {0x00c5, 0x8648},
+ {0x00d2, 0x8649},
+ {0x00dc, 0x864a},
+ {0x00e5, 0x864b},
+ {0x00eb, 0x864c},
+ {0x00f0, 0x864d},
+ {0x00f6, 0x864e},
+ {0x00fa, 0x864f},
+ {0x00ff, 0x8650},
+ {0x0060, 0x8657},
+ {0x0010, 0x8658},
+ {0x0018, 0x8659},
+ {0x0005, 0x865a},
+ {0x0018, 0x8660},
+ {0x0003, 0x8509},
+ {0x0011, 0x850a},
+ {0x0032, 0x850b},
+ {0x0010, 0x850c},
+ {0x0021, 0x850d},
+ {0x0001, 0x8500},
+ {0x0000, 0x8508},
+
+ {0x0012, 0x8608},
+ {0x002c, 0x8609},
+ {0x0002, 0x860a},
+ {0x0039, 0x860b},
+ {0x00d0, 0x860c},
+ {0x00f7, 0x860d},
+ {0x00ed, 0x860e},
+ {0x00db, 0x860f},
+ {0x0039, 0x8610},
+ {0x0012, 0x8657},
+ {0x0064, 0x8619},
+
+/* This line starts it all, it is not needed here */
+/* since it has been build into the driver */
+/* jfm: don't start now */
+/* {0x0030, 0x8112}, */
+ {}
+};
+
+/*
+ * Initialization data for Creative Webcam Vista
+ */
+static const u16 spca508_vista_init_data[][2] = {
+ {0x0008, 0x8200}, /* Clear register */
+ {0x0000, 0x870b}, /* Reset CTL3 */
+ {0x0020, 0x8112}, /* Video Drop packet enable */
+ {0x0003, 0x8111}, /* Soft Reset compression, memory, TG & CDSP */
+ {0x0000, 0x8110}, /* Disable everything */
+ {0x0000, 0x8114}, /* Software GPIO output data */
+ {0x0000, 0x8114},
+
+ {0x0003, 0x8111},
+ {0x0000, 0x8111},
+ {0x0090, 0x8110}, /* Enable: SSI output, External 2X clock output */
+ {0x0020, 0x8112},
+ {0x0000, 0x8114},
+ {0x0001, 0x8114},
+ {0x0001, 0x8114},
+ {0x0001, 0x8114},
+ {0x0003, 0x8114},
+
+ {0x000f, 0x8402}, /* Memory bank Address */
+ {0x0000, 0x8403}, /* Memory bank Address */
+ {0x00ba, 0x8804}, /* SSI Slave address */
+ {0x0010, 0x8802}, /* 93.75kHz SSI Clock Two DataByte */
+
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 10 */
+ {0x0010, 0x8802}, /* Will write 2 bytes (DATA1+DATA2) */
+ {0x0020, 0x8801}, /* Register address for SSI read/write */
+ {0x0044, 0x8805}, /* DATA2 */
+ {0x0004, 0x8800}, /* DATA1 -> write triggered */
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 10 */
+ {0x0010, 0x8802},
+ {0x0009, 0x8801},
+ {0x0042, 0x8805},
+ {0x0001, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 10 */
+ {0x0010, 0x8802},
+ {0x003c, 0x8801},
+ {0x0001, 0x8805},
+ {0x0000, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 10 */
+ {0x0010, 0x8802},
+ {0x0001, 0x8801},
+ {0x000a, 0x8805},
+ {0x0000, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 10 */
+ {0x0010, 0x8802},
+ {0x0002, 0x8801},
+ {0x0000, 0x8805},
+ {0x0000, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 10 */
+ {0x0010, 0x8802},
+ {0x0003, 0x8801},
+ {0x0027, 0x8805},
+ {0x0001, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 10 */
+ {0x0010, 0x8802},
+ {0x0004, 0x8801},
+ {0x0065, 0x8805},
+ {0x0001, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 10 */
+ {0x0010, 0x8802},
+ {0x0005, 0x8801},
+ {0x0003, 0x8805},
+ {0x0000, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 10 */
+ {0x0010, 0x8802},
+ {0x0006, 0x8801},
+ {0x001c, 0x8805},
+ {0x0000, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 10 */
+ {0x0010, 0x8802},
+ {0x0007, 0x8801},
+ {0x002a, 0x8805},
+ {0x0000, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 10 */
+ {0x0010, 0x8802},
+ {0x000e, 0x8801},
+ {0x0000, 0x8805},
+ {0x0000, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 10 */
+ {0x0010, 0x8802},
+ {0x0028, 0x8801},
+ {0x002e, 0x8805},
+ {0x0000, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 10 */
+ {0x0010, 0x8802},
+ {0x0039, 0x8801},
+ {0x0013, 0x8805},
+ {0x0000, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 10 */
+ {0x0010, 0x8802},
+ {0x003b, 0x8801},
+ {0x000c, 0x8805},
+ {0x0000, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 10 */
+ {0x0010, 0x8802},
+ {0x0035, 0x8801},
+ {0x0028, 0x8805},
+ {0x0000, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+ /* READ { 0x0001, 0x8802 } -> 0000: 10 */
+ {0x0010, 0x8802},
+ {0x0009, 0x8801},
+ {0x0042, 0x8805},
+ {0x0001, 0x8800},
+ /* READ { 0x0001, 0x8803 } -> 0000: 00 */
+
+ {0x0050, 0x8703},
+ {0x0002, 0x8704}, /* External input CKIx1 */
+ {0x0001, 0x870c}, /* Select CKOx2 output */
+ {0x009a, 0x8600}, /* Line memory Read Counter (L) */
+ {0x0001, 0x8606}, /* 1 Line memory Read Counter (H) Result: (d)410 */
+ {0x0023, 0x8601},
+ {0x0010, 0x8602},
+ {0x000a, 0x8603},
+ {0x009a, 0x8600},
+ {0x0001, 0x865b}, /* 1 Horizontal Offset for Valid Pixel(L) */
+ {0x0003, 0x865c}, /* Vertical offset for valid lines (L) */
+ {0x0058, 0x865d}, /* Horizontal valid pixels window (L) */
+ {0x0048, 0x865e}, /* Vertical valid lines window (L) */
+ {0x0000, 0x865f},
+
+ {0x0006, 0x8660},
+ /* Enable nibble data input, select nibble input order */
+
+ {0x0013, 0x8608}, /* A11 Coeficients for color correction */
+ {0x0028, 0x8609},
+ /* Note: these values are confirmed at the end of array */
+ {0x0005, 0x860a}, /* ... */
+ {0x0025, 0x860b},
+ {0x00e1, 0x860c},
+ {0x00fa, 0x860d},
+ {0x00f4, 0x860e},
+ {0x00e8, 0x860f},
+ {0x0025, 0x8610}, /* A33 Coef. */
+ {0x00fc, 0x8611}, /* White balance offset: R */
+ {0x0001, 0x8612}, /* White balance offset: Gr */
+ {0x00fe, 0x8613}, /* White balance offset: B */
+ {0x0000, 0x8614}, /* White balance offset: Gb */
+
+ {0x0064, 0x8651}, /* R gain for white balance (L) */
+ {0x0040, 0x8652}, /* Gr gain for white balance (L) */
+ {0x0066, 0x8653}, /* B gain for white balance (L) */
+ {0x0040, 0x8654}, /* Gb gain for white balance (L) */
+ {0x0001, 0x863f}, /* Enable fixed gamma correction */
+
+ {0x00a1, 0x8656}, /* Size - Window1: 256x256, Window2: 128x128,
+ * UV division: UV no change,
+ * Enable New edge enhancement */
+ {0x0018, 0x8657}, /* Edge gain high threshold */
+ {0x0020, 0x8658}, /* Edge gain low threshold */
+ {0x000a, 0x8659}, /* Edge bandwidth high threshold */
+ {0x0005, 0x865a}, /* Edge bandwidth low threshold */
+ {0x0064, 0x8607}, /* UV filter enable */
+
+ {0x0016, 0x8660},
+ {0x0000, 0x86b0}, /* Bad pixels compensation address */
+ {0x00dc, 0x86b1}, /* X coord for bad pixels compensation (L) */
+ {0x0000, 0x86b2},
+ {0x0009, 0x86b3}, /* Y coord for bad pixels compensation (L) */
+ {0x0000, 0x86b4},
+
+ {0x0001, 0x86b0},
+ {0x00f5, 0x86b1},
+ {0x0000, 0x86b2},
+ {0x00c6, 0x86b3},
+ {0x0000, 0x86b4},
+
+ {0x0002, 0x86b0},
+ {0x001c, 0x86b1},
+ {0x0001, 0x86b2},
+ {0x00d7, 0x86b3},
+ {0x0000, 0x86b4},
+
+ {0x0003, 0x86b0},
+ {0x001c, 0x86b1},
+ {0x0001, 0x86b2},
+ {0x00d8, 0x86b3},
+ {0x0000, 0x86b4},
+
+ {0x0004, 0x86b0},
+ {0x001d, 0x86b1},
+ {0x0001, 0x86b2},
+ {0x00d8, 0x86b3},
+ {0x0000, 0x86b4},
+ {0x001e, 0x8660},
+
+ /* READ { 0x0000, 0x8608 } -> 0000: 13 */
+ /* READ { 0x0000, 0x8609 } -> 0000: 28 */
+ /* READ { 0x0000, 0x8610 } -> 0000: 05 */
+ /* READ { 0x0000, 0x8611 } -> 0000: 25 */
+ /* READ { 0x0000, 0x8612 } -> 0000: e1 */
+ /* READ { 0x0000, 0x8613 } -> 0000: fa */
+ /* READ { 0x0000, 0x8614 } -> 0000: f4 */
+ /* READ { 0x0000, 0x8615 } -> 0000: e8 */
+ /* READ { 0x0000, 0x8616 } -> 0000: 25 */
+ {}
+};
+
+static int reg_write(struct usb_device *dev,
+ u16 index, u16 value)
+{
+ int ret;
+
+ ret = usb_control_msg(dev,
+ usb_sndctrlpipe(dev, 0),
+ 0, /* request */
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0, 500);
+ PDEBUG(D_USBO, "reg write i:0x%04x = 0x%02x",
+ index, value);
+ if (ret < 0)
+ pr_err("reg write: error %d\n", ret);
+ return ret;
+}
+
+/* read 1 byte */
+/* returns: negative is error, pos or zero is data */
+static int reg_read(struct gspca_dev *gspca_dev,
+ u16 index) /* wIndex */
+{
+ int ret;
+
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ 0, /* register */
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, /* value */
+ index,
+ gspca_dev->usb_buf, 1,
+ 500); /* timeout */
+ PDEBUG(D_USBI, "reg read i:%04x --> %02x",
+ index, gspca_dev->usb_buf[0]);
+ if (ret < 0) {
+ pr_err("reg_read err %d\n", ret);
+ return ret;
+ }
+ return gspca_dev->usb_buf[0];
+}
+
+/* send 1 or 2 bytes to the sensor via the Synchronous Serial Interface */
+static int ssi_w(struct gspca_dev *gspca_dev,
+ u16 reg, u16 val)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int ret, retry;
+
+ ret = reg_write(dev, 0x8802, reg >> 8);
+ if (ret < 0)
+ goto out;
+ ret = reg_write(dev, 0x8801, reg & 0x00ff);
+ if (ret < 0)
+ goto out;
+ if ((reg & 0xff00) == 0x1000) { /* if 2 bytes */
+ ret = reg_write(dev, 0x8805, val & 0x00ff);
+ if (ret < 0)
+ goto out;
+ val >>= 8;
+ }
+ ret = reg_write(dev, 0x8800, val);
+ if (ret < 0)
+ goto out;
+
+ /* poll until not busy */
+ retry = 10;
+ for (;;) {
+ ret = reg_read(gspca_dev, 0x8803);
+ if (ret < 0)
+ break;
+ if (gspca_dev->usb_buf[0] == 0)
+ break;
+ if (--retry <= 0) {
+ PDEBUG(D_ERR, "ssi_w busy %02x",
+ gspca_dev->usb_buf[0]);
+ ret = -1;
+ break;
+ }
+ msleep(8);
+ }
+
+out:
+ return ret;
+}
+
+static int write_vector(struct gspca_dev *gspca_dev,
+ const u16 (*data)[2])
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int ret = 0;
+
+ while ((*data)[1] != 0) {
+ if ((*data)[1] & 0x8000) {
+ if ((*data)[1] == 0xdd00) /* delay */
+ msleep((*data)[0]);
+ else
+ ret = reg_write(dev, (*data)[1], (*data)[0]);
+ } else {
+ ret = ssi_w(gspca_dev, (*data)[1], (*data)[0]);
+ }
+ if (ret < 0)
+ break;
+ data++;
+ }
+ return ret;
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+ const u16 (*init_data)[2];
+ static const u16 (*(init_data_tb[]))[2] = {
+ spca508_vista_init_data, /* CreativeVista 0 */
+ spca508_sightcam_init_data, /* HamaUSBSightcam 1 */
+ spca508_sightcam2_init_data, /* HamaUSBSightcam2 2 */
+ spca508cs110_init_data, /* IntelEasyPCCamera 3 */
+ spca508cs110_init_data, /* MicroInnovationIC200 4 */
+ spca508_init_data, /* ViewQuestVQ110 5 */
+ };
+
+#ifdef GSPCA_DEBUG
+ int data1, data2;
+
+ /* Read from global register the USB product and vendor IDs, just to
+ * prove that we can communicate with the device. This works, which
+ * confirms at we are communicating properly and that the device
+ * is a 508. */
+ data1 = reg_read(gspca_dev, 0x8104);
+ data2 = reg_read(gspca_dev, 0x8105);
+ PDEBUG(D_PROBE, "Webcam Vendor ID: 0x%02x%02x", data2, data1);
+
+ data1 = reg_read(gspca_dev, 0x8106);
+ data2 = reg_read(gspca_dev, 0x8107);
+ PDEBUG(D_PROBE, "Webcam Product ID: 0x%02x%02x", data2, data1);
+
+ data1 = reg_read(gspca_dev, 0x8621);
+ PDEBUG(D_PROBE, "Window 1 average luminance: %d", data1);
+#endif
+
+ cam = &gspca_dev->cam;
+ cam->cam_mode = sif_mode;
+ cam->nmodes = ARRAY_SIZE(sif_mode);
+
+ sd->subtype = id->driver_info;
+ sd->brightness = BRIGHTNESS_DEF;
+
+ init_data = init_data_tb[sd->subtype];
+ return write_vector(gspca_dev, init_data);
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ return 0;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ int mode;
+
+ mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+ reg_write(gspca_dev->dev, 0x8500, mode);
+ switch (mode) {
+ case 0:
+ case 1:
+ reg_write(gspca_dev->dev, 0x8700, 0x28); /* clock */
+ break;
+ default:
+/* case 2: */
+/* case 3: */
+ reg_write(gspca_dev->dev, 0x8700, 0x23); /* clock */
+ break;
+ }
+ reg_write(gspca_dev->dev, 0x8112, 0x10 | 0x20);
+ return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ /* Video ISO disable, Video Drop Packet enable: */
+ reg_write(gspca_dev->dev, 0x8112, 0x20);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ switch (data[0]) {
+ case 0: /* start of frame */
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+ data += SPCA508_OFFSET_DATA;
+ len -= SPCA508_OFFSET_DATA;
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+ break;
+ case 0xff: /* drop */
+ break;
+ default:
+ data += 1;
+ len -= 1;
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ break;
+ }
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 brightness = sd->brightness;
+
+ /* MX seem contrast */
+ reg_write(gspca_dev->dev, 0x8651, brightness);
+ reg_write(gspca_dev->dev, 0x8652, brightness);
+ reg_write(gspca_dev->dev, 0x8653, brightness);
+ reg_write(gspca_dev->dev, 0x8654, brightness);
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->brightness = val;
+ if (gspca_dev->streaming)
+ setbrightness(gspca_dev);
+ return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->brightness;
+ return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x0130, 0x0130), .driver_info = HamaUSBSightcam},
+ {USB_DEVICE(0x041e, 0x4018), .driver_info = CreativeVista},
+ {USB_DEVICE(0x0733, 0x0110), .driver_info = ViewQuestVQ110},
+ {USB_DEVICE(0x0af9, 0x0010), .driver_info = HamaUSBSightcam},
+ {USB_DEVICE(0x0af9, 0x0011), .driver_info = HamaUSBSightcam2},
+ {USB_DEVICE(0x8086, 0x0110), .driver_info = IntelEasyPCCamera},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c
new file mode 100644
index 00000000..4a5f209c
--- /dev/null
+++ b/drivers/media/video/gspca/spca561.c
@@ -0,0 +1,1109 @@
+/*
+ * Sunplus spca561 subdriver
+ *
+ * Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "spca561"
+
+#include <linux/input.h>
+#include "gspca.h"
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("GSPCA/SPCA561 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ __u16 exposure; /* rev12a only */
+#define EXPOSURE_MIN 1
+#define EXPOSURE_DEF 700 /* == 10 fps */
+#define EXPOSURE_MAX (2047 + 325) /* see setexposure */
+
+ __u8 contrast; /* rev72a only */
+#define CONTRAST_MIN 0x00
+#define CONTRAST_DEF 0x20
+#define CONTRAST_MAX 0x3f
+
+ __u8 brightness; /* rev72a only */
+#define BRIGHTNESS_MIN 0
+#define BRIGHTNESS_DEF 0x20
+#define BRIGHTNESS_MAX 0x3f
+
+ __u8 white;
+#define HUE_MIN 1
+#define HUE_DEF 0x40
+#define HUE_MAX 0x7f
+
+ __u8 autogain;
+#define AUTOGAIN_MIN 0
+#define AUTOGAIN_DEF 1
+#define AUTOGAIN_MAX 1
+
+ __u8 gain; /* rev12a only */
+#define GAIN_MIN 0
+#define GAIN_DEF 63
+#define GAIN_MAX 255
+
+#define EXPO12A_DEF 3
+ __u8 expo12a; /* expo/gain? for rev 12a */
+
+ __u8 chip_revision;
+#define Rev012A 0
+#define Rev072A 1
+
+ signed char ag_cnt;
+#define AG_CNT_START 13
+};
+
+static const struct v4l2_pix_format sif_012a_mode[] = {
+ {160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 3},
+ {176, 144, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 2},
+ {320, 240, V4L2_PIX_FMT_SPCA561, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 4 / 8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {352, 288, V4L2_PIX_FMT_SPCA561, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288 * 4 / 8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+
+static const struct v4l2_pix_format sif_072a_mode[] = {
+ {160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 3},
+ {176, 144, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 2},
+ {320, 240, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {352, 288, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+
+/*
+ * Initialization data
+ * I'm not very sure how to split initialization from open data
+ * chunks. For now, we'll consider everything as initialization
+ */
+/* Frame packet header offsets for the spca561 */
+#define SPCA561_OFFSET_SNAP 1
+#define SPCA561_OFFSET_TYPE 2
+#define SPCA561_OFFSET_COMPRESS 3
+#define SPCA561_OFFSET_FRAMSEQ 4
+#define SPCA561_OFFSET_GPIO 5
+#define SPCA561_OFFSET_USBBUFF 6
+#define SPCA561_OFFSET_WIN2GRAVE 7
+#define SPCA561_OFFSET_WIN2RAVE 8
+#define SPCA561_OFFSET_WIN2BAVE 9
+#define SPCA561_OFFSET_WIN2GBAVE 10
+#define SPCA561_OFFSET_WIN1GRAVE 11
+#define SPCA561_OFFSET_WIN1RAVE 12
+#define SPCA561_OFFSET_WIN1BAVE 13
+#define SPCA561_OFFSET_WIN1GBAVE 14
+#define SPCA561_OFFSET_FREQ 15
+#define SPCA561_OFFSET_VSYNC 16
+#define SPCA561_INDEX_I2C_BASE 0x8800
+#define SPCA561_SNAPBIT 0x20
+#define SPCA561_SNAPCTRL 0x40
+
+static const u16 rev72a_reset[][2] = {
+ {0x0000, 0x8114}, /* Software GPIO output data */
+ {0x0001, 0x8114}, /* Software GPIO output data */
+ {0x0000, 0x8112}, /* Some kind of reset */
+ {}
+};
+static const __u16 rev72a_init_data1[][2] = {
+ {0x0003, 0x8701}, /* PCLK clock delay adjustment */
+ {0x0001, 0x8703}, /* HSYNC from cmos inverted */
+ {0x0011, 0x8118}, /* Enable and conf sensor */
+ {0x0001, 0x8118}, /* Conf sensor */
+ {0x0092, 0x8804}, /* I know nothing about these */
+ {0x0010, 0x8802}, /* 0x88xx registers, so I won't */
+ {}
+};
+static const u16 rev72a_init_sensor1[][2] = {
+ {0x0001, 0x000d},
+ {0x0002, 0x0018},
+ {0x0004, 0x0165},
+ {0x0005, 0x0021},
+ {0x0007, 0x00aa},
+ {0x0020, 0x1504},
+ {0x0039, 0x0002},
+ {0x0035, 0x0010},
+ {0x0009, 0x1049},
+ {0x0028, 0x000b},
+ {0x003b, 0x000f},
+ {0x003c, 0x0000},
+ {}
+};
+static const __u16 rev72a_init_data2[][2] = {
+ {0x0018, 0x8601}, /* Pixel/line selection for color separation */
+ {0x0000, 0x8602}, /* Optical black level for user setting */
+ {0x0060, 0x8604}, /* Optical black horizontal offset */
+ {0x0002, 0x8605}, /* Optical black vertical offset */
+ {0x0000, 0x8603}, /* Non-automatic optical black level */
+ {0x0002, 0x865b}, /* Horizontal offset for valid pixels */
+ {0x0000, 0x865f}, /* Vertical valid pixels window (x2) */
+ {0x00b0, 0x865d}, /* Horizontal valid pixels window (x2) */
+ {0x0090, 0x865e}, /* Vertical valid lines window (x2) */
+ {0x00e0, 0x8406}, /* Memory buffer threshold */
+ {0x0000, 0x8660}, /* Compensation memory stuff */
+ {0x0002, 0x8201}, /* Output address for r/w serial EEPROM */
+ {0x0008, 0x8200}, /* Clear valid bit for serial EEPROM */
+ {0x0001, 0x8200}, /* OprMode to be executed by hardware */
+/* from ms-win */
+ {0x0000, 0x8611}, /* R offset for white balance */
+ {0x00fd, 0x8612}, /* Gr offset for white balance */
+ {0x0003, 0x8613}, /* B offset for white balance */
+ {0x0000, 0x8614}, /* Gb offset for white balance */
+/* from ms-win */
+ {0x0035, 0x8651}, /* R gain for white balance */
+ {0x0040, 0x8652}, /* Gr gain for white balance */
+ {0x005f, 0x8653}, /* B gain for white balance */
+ {0x0040, 0x8654}, /* Gb gain for white balance */
+ {0x0002, 0x8502}, /* Maximum average bit rate stuff */
+ {0x0011, 0x8802},
+
+ {0x0087, 0x8700}, /* Set master clock (96Mhz????) */
+ {0x0081, 0x8702}, /* Master clock output enable */
+
+ {0x0000, 0x8500}, /* Set image type (352x288 no compression) */
+ /* Originally was 0x0010 (352x288 compression) */
+
+ {0x0002, 0x865b}, /* Horizontal offset for valid pixels */
+ {0x0003, 0x865c}, /* Vertical offset for valid lines */
+ {}
+};
+static const u16 rev72a_init_sensor2[][2] = {
+ {0x0003, 0x0121},
+ {0x0004, 0x0165},
+ {0x0005, 0x002f}, /* blanking control column */
+ {0x0006, 0x0000}, /* blanking mode row*/
+ {0x000a, 0x0002},
+ {0x0009, 0x1061}, /* setexposure times && pixel clock
+ * 0001 0 | 000 0110 0001 */
+ {0x0035, 0x0014},
+ {}
+};
+
+/******************** QC Express etch2 stuff ********************/
+static const __u16 Pb100_1map8300[][2] = {
+ /* reg, value */
+ {0x8320, 0x3304},
+
+ {0x8303, 0x0125}, /* image area */
+ {0x8304, 0x0169},
+ {0x8328, 0x000b},
+ {0x833c, 0x0001}, /*fixme: win:07*/
+
+ {0x832f, 0x1904}, /*fixme: was 0419*/
+ {0x8307, 0x00aa},
+ {0x8301, 0x0003},
+ {0x8302, 0x000e},
+ {}
+};
+static const __u16 Pb100_2map8300[][2] = {
+ /* reg, value */
+ {0x8339, 0x0000},
+ {0x8307, 0x00aa},
+ {}
+};
+
+static const __u16 spca561_161rev12A_data1[][2] = {
+ {0x29, 0x8118}, /* Control register (various enable bits) */
+ {0x08, 0x8114}, /* GPIO: Led off */
+ {0x0e, 0x8112}, /* 0x0e stream off 0x3e stream on */
+ {0x00, 0x8102}, /* white balance - new */
+ {0x92, 0x8804},
+ {0x04, 0x8802}, /* windows uses 08 */
+ {}
+};
+static const __u16 spca561_161rev12A_data2[][2] = {
+ {0x21, 0x8118},
+ {0x10, 0x8500},
+ {0x07, 0x8601},
+ {0x07, 0x8602},
+ {0x04, 0x8501},
+
+ {0x07, 0x8201}, /* windows uses 02 */
+ {0x08, 0x8200},
+ {0x01, 0x8200},
+
+ {0x90, 0x8604},
+ {0x00, 0x8605},
+ {0xb0, 0x8603},
+
+ /* sensor gains */
+ {0x07, 0x8601}, /* white balance - new */
+ {0x07, 0x8602}, /* white balance - new */
+ {0x00, 0x8610}, /* *red */
+ {0x00, 0x8611}, /* 3f *green */
+ {0x00, 0x8612}, /* green *blue */
+ {0x00, 0x8613}, /* blue *green */
+ {0x43, 0x8614}, /* green *red - white balance - was 0x35 */
+ {0x40, 0x8615}, /* 40 *green - white balance - was 0x35 */
+ {0x71, 0x8616}, /* 7a *blue - white balance - was 0x35 */
+ {0x40, 0x8617}, /* 40 *green - white balance - was 0x35 */
+
+ {0x0c, 0x8620}, /* 0c */
+ {0xc8, 0x8631}, /* c8 */
+ {0xc8, 0x8634}, /* c8 */
+ {0x23, 0x8635}, /* 23 */
+ {0x1f, 0x8636}, /* 1f */
+ {0xdd, 0x8637}, /* dd */
+ {0xe1, 0x8638}, /* e1 */
+ {0x1d, 0x8639}, /* 1d */
+ {0x21, 0x863a}, /* 21 */
+ {0xe3, 0x863b}, /* e3 */
+ {0xdf, 0x863c}, /* df */
+ {0xf0, 0x8505},
+ {0x32, 0x850a},
+/* {0x99, 0x8700}, * - white balance - new (removed) */
+ /* HDG we used to do this in stop0, making the init state and the state
+ after a start / stop different, so do this here instead. */
+ {0x29, 0x8118},
+ {}
+};
+
+static void reg_w_val(struct usb_device *dev, __u16 index, __u8 value)
+{
+ int ret;
+
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0, 500);
+ PDEBUG(D_USBO, "reg write: 0x%02x:0x%02x", index, value);
+ if (ret < 0)
+ pr_err("reg write: error %d\n", ret);
+}
+
+static void write_vector(struct gspca_dev *gspca_dev,
+ const __u16 data[][2])
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int i;
+
+ i = 0;
+ while (data[i][1] != 0) {
+ reg_w_val(dev, data[i][1], data[i][0]);
+ i++;
+ }
+}
+
+/* read 'len' bytes to gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev,
+ __u16 index, __u16 length)
+{
+ usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ 0, /* request */
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, /* value */
+ index, gspca_dev->usb_buf, length, 500);
+}
+
+/* write 'len' bytes from gspca_dev->usb_buf */
+static void reg_w_buf(struct gspca_dev *gspca_dev,
+ __u16 index, __u16 len)
+{
+ usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, /* value */
+ index, gspca_dev->usb_buf, len, 500);
+}
+
+static void i2c_write(struct gspca_dev *gspca_dev, __u16 value, __u16 reg)
+{
+ int retry = 60;
+
+ reg_w_val(gspca_dev->dev, 0x8801, reg);
+ reg_w_val(gspca_dev->dev, 0x8805, value);
+ reg_w_val(gspca_dev->dev, 0x8800, value >> 8);
+ do {
+ reg_r(gspca_dev, 0x8803, 1);
+ if (!gspca_dev->usb_buf[0])
+ return;
+ msleep(10);
+ } while (--retry);
+}
+
+static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode)
+{
+ int retry = 60;
+ __u8 value;
+
+ reg_w_val(gspca_dev->dev, 0x8804, 0x92);
+ reg_w_val(gspca_dev->dev, 0x8801, reg);
+ reg_w_val(gspca_dev->dev, 0x8802, mode | 0x01);
+ do {
+ reg_r(gspca_dev, 0x8803, 1);
+ if (!gspca_dev->usb_buf[0]) {
+ reg_r(gspca_dev, 0x8800, 1);
+ value = gspca_dev->usb_buf[0];
+ reg_r(gspca_dev, 0x8805, 1);
+ return ((int) value << 8) | gspca_dev->usb_buf[0];
+ }
+ msleep(10);
+ } while (--retry);
+ return -1;
+}
+
+static void sensor_mapwrite(struct gspca_dev *gspca_dev,
+ const __u16 (*sensormap)[2])
+{
+ while ((*sensormap)[0]) {
+ gspca_dev->usb_buf[0] = (*sensormap)[1];
+ gspca_dev->usb_buf[1] = (*sensormap)[1] >> 8;
+ reg_w_buf(gspca_dev, (*sensormap)[0], 2);
+ sensormap++;
+ }
+}
+
+static void write_sensor_72a(struct gspca_dev *gspca_dev,
+ const __u16 (*sensor)[2])
+{
+ while ((*sensor)[0]) {
+ i2c_write(gspca_dev, (*sensor)[1], (*sensor)[0]);
+ sensor++;
+ }
+}
+
+static void init_161rev12A(struct gspca_dev *gspca_dev)
+{
+ write_vector(gspca_dev, spca561_161rev12A_data1);
+ sensor_mapwrite(gspca_dev, Pb100_1map8300);
+/*fixme: should be in sd_start*/
+ write_vector(gspca_dev, spca561_161rev12A_data2);
+ sensor_mapwrite(gspca_dev, Pb100_2map8300);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+ __u16 vendor, product;
+ __u8 data1, data2;
+
+ /* Read frm global register the USB product and vendor IDs, just to
+ * prove that we can communicate with the device. This works, which
+ * confirms at we are communicating properly and that the device
+ * is a 561. */
+ reg_r(gspca_dev, 0x8104, 1);
+ data1 = gspca_dev->usb_buf[0];
+ reg_r(gspca_dev, 0x8105, 1);
+ data2 = gspca_dev->usb_buf[0];
+ vendor = (data2 << 8) | data1;
+ reg_r(gspca_dev, 0x8106, 1);
+ data1 = gspca_dev->usb_buf[0];
+ reg_r(gspca_dev, 0x8107, 1);
+ data2 = gspca_dev->usb_buf[0];
+ product = (data2 << 8) | data1;
+ if (vendor != id->idVendor || product != id->idProduct) {
+ PDEBUG(D_PROBE, "Bad vendor / product from device");
+ return -EINVAL;
+ }
+
+ cam = &gspca_dev->cam;
+ cam->needs_full_bandwidth = 1;
+
+ sd->chip_revision = id->driver_info;
+ if (sd->chip_revision == Rev012A) {
+ cam->cam_mode = sif_012a_mode;
+ cam->nmodes = ARRAY_SIZE(sif_012a_mode);
+ } else {
+ cam->cam_mode = sif_072a_mode;
+ cam->nmodes = ARRAY_SIZE(sif_072a_mode);
+ }
+ sd->brightness = BRIGHTNESS_DEF;
+ sd->contrast = CONTRAST_DEF;
+ sd->white = HUE_DEF;
+ sd->exposure = EXPOSURE_DEF;
+ sd->autogain = AUTOGAIN_DEF;
+ sd->gain = GAIN_DEF;
+ sd->expo12a = EXPO12A_DEF;
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init_12a(struct gspca_dev *gspca_dev)
+{
+ PDEBUG(D_STREAM, "Chip revision: 012a");
+ init_161rev12A(gspca_dev);
+ return 0;
+}
+static int sd_init_72a(struct gspca_dev *gspca_dev)
+{
+ PDEBUG(D_STREAM, "Chip revision: 072a");
+ write_vector(gspca_dev, rev72a_reset);
+ msleep(200);
+ write_vector(gspca_dev, rev72a_init_data1);
+ write_sensor_72a(gspca_dev, rev72a_init_sensor1);
+ write_vector(gspca_dev, rev72a_init_data2);
+ write_sensor_72a(gspca_dev, rev72a_init_sensor2);
+ reg_w_val(gspca_dev->dev, 0x8112, 0x30);
+ return 0;
+}
+
+/* rev 72a only */
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct usb_device *dev = gspca_dev->dev;
+ __u8 value;
+
+ value = sd->brightness;
+
+ /* offsets for white balance */
+ reg_w_val(dev, 0x8611, value); /* R */
+ reg_w_val(dev, 0x8612, value); /* Gr */
+ reg_w_val(dev, 0x8613, value); /* B */
+ reg_w_val(dev, 0x8614, value); /* Gb */
+}
+
+static void setwhite(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u16 white;
+ __u8 blue, red;
+ __u16 reg;
+
+ /* try to emulate MS-win as possible */
+ white = sd->white;
+ red = 0x20 + white * 3 / 8;
+ blue = 0x90 - white * 5 / 8;
+ if (sd->chip_revision == Rev012A) {
+ reg = 0x8614;
+ } else {
+ reg = 0x8651;
+ red += sd->contrast - 0x20;
+ blue += sd->contrast - 0x20;
+ }
+ reg_w_val(gspca_dev->dev, reg, red);
+ reg_w_val(gspca_dev->dev, reg + 2, blue);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct usb_device *dev = gspca_dev->dev;
+ __u8 value;
+
+ if (sd->chip_revision != Rev072A)
+ return;
+ value = sd->contrast + 0x20;
+
+ /* gains for white balance */
+ setwhite(gspca_dev);
+/* reg_w_val(dev, 0x8651, value); * R - done by setwhite */
+ reg_w_val(dev, 0x8652, value); /* Gr */
+/* reg_w_val(dev, 0x8653, value); * B - done by setwhite */
+ reg_w_val(dev, 0x8654, value); /* Gb */
+}
+
+/* rev 12a only */
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, expo = 0;
+
+ /* Register 0x8309 controls exposure for the spca561,
+ the basic exposure setting goes from 1-2047, where 1 is completely
+ dark and 2047 is very bright. It not only influences exposure but
+ also the framerate (to allow for longer exposure) from 1 - 300 it
+ only raises the exposure time then from 300 - 600 it halves the
+ framerate to be able to further raise the exposure time and for every
+ 300 more it halves the framerate again. This allows for a maximum
+ exposure time of circa 0.2 - 0.25 seconds (30 / (2000/3000) fps).
+ Sometimes this is not enough, the 1-2047 uses bits 0-10, bits 11-12
+ configure a divider for the base framerate which us used at the
+ exposure setting of 1-300. These bits configure the base framerate
+ according to the following formula: fps = 60 / (value + 2) */
+
+ /* We choose to use the high bits setting the fixed framerate divisor
+ asap, as setting high basic exposure setting without the fixed
+ divider in combination with high gains makes the cam stop */
+ int table[] = { 0, 450, 550, 625, EXPOSURE_MAX };
+
+ for (i = 0; i < ARRAY_SIZE(table) - 1; i++) {
+ if (sd->exposure <= table[i + 1]) {
+ expo = sd->exposure - table[i];
+ if (i)
+ expo += 300;
+ expo |= i << 11;
+ break;
+ }
+ }
+
+ gspca_dev->usb_buf[0] = expo;
+ gspca_dev->usb_buf[1] = expo >> 8;
+ reg_w_buf(gspca_dev, 0x8309, 2);
+}
+
+/* rev 12a only */
+static void setgain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* gain reg low 6 bits 0-63 gain, bit 6 and 7, both double the
+ sensitivity when set, so 31 + one of them set == 63, and 15
+ with both of them set == 63 */
+ if (sd->gain < 64)
+ gspca_dev->usb_buf[0] = sd->gain;
+ else if (sd->gain < 128)
+ gspca_dev->usb_buf[0] = (sd->gain / 2) | 0x40;
+ else
+ gspca_dev->usb_buf[0] = (sd->gain / 4) | 0xc0;
+
+ gspca_dev->usb_buf[1] = 0;
+ reg_w_buf(gspca_dev, 0x8335, 2);
+}
+
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->autogain)
+ sd->ag_cnt = AG_CNT_START;
+ else
+ sd->ag_cnt = -1;
+}
+
+static int sd_start_12a(struct gspca_dev *gspca_dev)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int mode;
+ static const __u8 Reg8391[8] =
+ {0x92, 0x30, 0x20, 0x00, 0x0c, 0x00, 0x00, 0x00};
+
+ mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+ if (mode <= 1) {
+ /* Use compression on 320x240 and above */
+ reg_w_val(dev, 0x8500, 0x10 | mode);
+ } else {
+ /* I couldn't get the compression to work below 320x240
+ * Fortunately at these resolutions the bandwidth
+ * is sufficient to push raw frames at ~20fps */
+ reg_w_val(dev, 0x8500, mode);
+ } /* -- qq@kuku.eu.org */
+
+ gspca_dev->usb_buf[0] = 0xaa;
+ gspca_dev->usb_buf[1] = 0x00;
+ reg_w_buf(gspca_dev, 0x8307, 2);
+ /* clock - lower 0x8X values lead to fps > 30 */
+ reg_w_val(gspca_dev->dev, 0x8700, 0x8a);
+ /* 0x8f 0x85 0x27 clock */
+ reg_w_val(gspca_dev->dev, 0x8112, 0x1e | 0x20);
+ reg_w_val(gspca_dev->dev, 0x850b, 0x03);
+ memcpy(gspca_dev->usb_buf, Reg8391, 8);
+ reg_w_buf(gspca_dev, 0x8391, 8);
+ reg_w_buf(gspca_dev, 0x8390, 8);
+ setwhite(gspca_dev);
+ setgain(gspca_dev);
+ setexposure(gspca_dev);
+
+ /* Led ON (bit 3 -> 0 */
+ reg_w_val(gspca_dev->dev, 0x8114, 0x00);
+ return 0;
+}
+static int sd_start_72a(struct gspca_dev *gspca_dev)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int Clck;
+ int mode;
+
+ write_vector(gspca_dev, rev72a_reset);
+ msleep(200);
+ write_vector(gspca_dev, rev72a_init_data1);
+ write_sensor_72a(gspca_dev, rev72a_init_sensor1);
+
+ mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+ switch (mode) {
+ default:
+ case 0:
+ Clck = 0x27; /* ms-win 0x87 */
+ break;
+ case 1:
+ Clck = 0x25;
+ break;
+ case 2:
+ Clck = 0x22;
+ break;
+ case 3:
+ Clck = 0x21;
+ break;
+ }
+ reg_w_val(dev, 0x8700, Clck); /* 0x27 clock */
+ reg_w_val(dev, 0x8702, 0x81);
+ reg_w_val(dev, 0x8500, mode); /* mode */
+ write_sensor_72a(gspca_dev, rev72a_init_sensor2);
+ setcontrast(gspca_dev);
+/* setbrightness(gspca_dev); * fixme: bad values */
+ setautogain(gspca_dev);
+ reg_w_val(dev, 0x8112, 0x10 | 0x20);
+ return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->chip_revision == Rev012A) {
+ reg_w_val(gspca_dev->dev, 0x8112, 0x0e);
+ /* Led Off (bit 3 -> 1 */
+ reg_w_val(gspca_dev->dev, 0x8114, 0x08);
+ } else {
+ reg_w_val(gspca_dev->dev, 0x8112, 0x20);
+/* reg_w_val(gspca_dev->dev, 0x8102, 0x00); ?? */
+ }
+}
+
+static void do_autogain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int expotimes;
+ int pixelclk;
+ int gainG;
+ __u8 R, Gr, Gb, B;
+ int y;
+ __u8 luma_mean = 110;
+ __u8 luma_delta = 20;
+ __u8 spring = 4;
+
+ if (sd->ag_cnt < 0)
+ return;
+ if (--sd->ag_cnt >= 0)
+ return;
+ sd->ag_cnt = AG_CNT_START;
+
+ switch (sd->chip_revision) {
+ case Rev072A:
+ reg_r(gspca_dev, 0x8621, 1);
+ Gr = gspca_dev->usb_buf[0];
+ reg_r(gspca_dev, 0x8622, 1);
+ R = gspca_dev->usb_buf[0];
+ reg_r(gspca_dev, 0x8623, 1);
+ B = gspca_dev->usb_buf[0];
+ reg_r(gspca_dev, 0x8624, 1);
+ Gb = gspca_dev->usb_buf[0];
+ y = (77 * R + 75 * (Gr + Gb) + 29 * B) >> 8;
+ /* u= (128*B-(43*(Gr+Gb+R))) >> 8; */
+ /* v= (128*R-(53*(Gr+Gb))-21*B) >> 8; */
+ /* PDEBUG(D_CONF,"reading Y %d U %d V %d ",y,u,v); */
+
+ if (y < luma_mean - luma_delta ||
+ y > luma_mean + luma_delta) {
+ expotimes = i2c_read(gspca_dev, 0x09, 0x10);
+ pixelclk = 0x0800;
+ expotimes = expotimes & 0x07ff;
+ /* PDEBUG(D_PACK,
+ "Exposition Times 0x%03X Clock 0x%04X ",
+ expotimes,pixelclk); */
+ gainG = i2c_read(gspca_dev, 0x35, 0x10);
+ /* PDEBUG(D_PACK,
+ "reading Gain register %d", gainG); */
+
+ expotimes += (luma_mean - y) >> spring;
+ gainG += (luma_mean - y) / 50;
+ /* PDEBUG(D_PACK,
+ "compute expotimes %d gain %d",
+ expotimes,gainG); */
+
+ if (gainG > 0x3f)
+ gainG = 0x3f;
+ else if (gainG < 3)
+ gainG = 3;
+ i2c_write(gspca_dev, gainG, 0x35);
+
+ if (expotimes > 0x0256)
+ expotimes = 0x0256;
+ else if (expotimes < 3)
+ expotimes = 3;
+ i2c_write(gspca_dev, expotimes | pixelclk, 0x09);
+ }
+ break;
+ }
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ len--;
+ switch (*data++) { /* sequence number */
+ case 0: /* start of frame */
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+
+ /* This should never happen */
+ if (len < 2) {
+ PDEBUG(D_ERR, "Short SOF packet, ignoring");
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
+ }
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ if (data[0] & 0x20) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+ input_sync(gspca_dev->input_dev);
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ }
+#endif
+
+ if (data[1] & 0x10) {
+ /* compressed bayer */
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+ } else {
+ /* raw bayer (with a header, which we skip) */
+ if (sd->chip_revision == Rev012A) {
+ data += 20;
+ len -= 20;
+ } else {
+ data += 16;
+ len -= 16;
+ }
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+ }
+ return;
+ case 0xff: /* drop (empty mpackets) */
+ return;
+ }
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+/* rev 72a only */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->brightness = val;
+ if (gspca_dev->streaming)
+ setbrightness(gspca_dev);
+ return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->brightness;
+ return 0;
+}
+
+/* rev 72a only */
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->contrast = val;
+ if (gspca_dev->streaming)
+ setcontrast(gspca_dev);
+ return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->contrast;
+ return 0;
+}
+
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->autogain = val;
+ if (gspca_dev->streaming)
+ setautogain(gspca_dev);
+ return 0;
+}
+
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->autogain;
+ return 0;
+}
+
+static int sd_setwhite(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->white = val;
+ if (gspca_dev->streaming)
+ setwhite(gspca_dev);
+ return 0;
+}
+
+static int sd_getwhite(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->white;
+ return 0;
+}
+
+/* rev12a only */
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->exposure = val;
+ if (gspca_dev->streaming)
+ setexposure(gspca_dev);
+ return 0;
+}
+
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->exposure;
+ return 0;
+}
+
+/* rev12a only */
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->gain = val;
+ if (gspca_dev->streaming)
+ setgain(gspca_dev);
+ return 0;
+}
+
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->gain;
+ return 0;
+}
+
+/* control tables */
+static const struct ctrl sd_ctrls_12a[] = {
+ {
+ {
+ .id = V4L2_CID_HUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Hue",
+ .minimum = HUE_MIN,
+ .maximum = HUE_MAX,
+ .step = 1,
+ .default_value = HUE_DEF,
+ },
+ .set = sd_setwhite,
+ .get = sd_getwhite,
+ },
+ {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = EXPOSURE_MIN,
+ .maximum = EXPOSURE_MAX,
+ .step = 1,
+ .default_value = EXPOSURE_DEF,
+ },
+ .set = sd_setexposure,
+ .get = sd_getexposure,
+ },
+ {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = GAIN_MIN,
+ .maximum = GAIN_MAX,
+ .step = 1,
+ .default_value = GAIN_DEF,
+ },
+ .set = sd_setgain,
+ .get = sd_getgain,
+ },
+};
+
+static const struct ctrl sd_ctrls_72a[] = {
+ {
+ {
+ .id = V4L2_CID_HUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Hue",
+ .minimum = HUE_MIN,
+ .maximum = HUE_MAX,
+ .step = 1,
+ .default_value = HUE_DEF,
+ },
+ .set = sd_setwhite,
+ .get = sd_getwhite,
+ },
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = BRIGHTNESS_MIN,
+ .maximum = BRIGHTNESS_MAX,
+ .step = 1,
+ .default_value = BRIGHTNESS_DEF,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+ {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = CONTRAST_MIN,
+ .maximum = CONTRAST_MAX,
+ .step = 1,
+ .default_value = CONTRAST_DEF,
+ },
+ .set = sd_setcontrast,
+ .get = sd_getcontrast,
+ },
+ {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto Gain",
+ .minimum = AUTOGAIN_MIN,
+ .maximum = AUTOGAIN_MAX,
+ .step = 1,
+ .default_value = AUTOGAIN_DEF,
+ },
+ .set = sd_setautogain,
+ .get = sd_getautogain,
+ },
+};
+
+/* sub-driver description */
+static const struct sd_desc sd_desc_12a = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls_12a,
+ .nctrls = ARRAY_SIZE(sd_ctrls_12a),
+ .config = sd_config,
+ .init = sd_init_12a,
+ .start = sd_start_12a,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ .other_input = 1,
+#endif
+};
+static const struct sd_desc sd_desc_72a = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls_72a,
+ .nctrls = ARRAY_SIZE(sd_ctrls_72a),
+ .config = sd_config,
+ .init = sd_init_72a,
+ .start = sd_start_72a,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+ .dq_callback = do_autogain,
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ .other_input = 1,
+#endif
+};
+static const struct sd_desc *sd_desc[2] = {
+ &sd_desc_12a,
+ &sd_desc_72a
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x041e, 0x401a), .driver_info = Rev072A},
+ {USB_DEVICE(0x041e, 0x403b), .driver_info = Rev012A},
+ {USB_DEVICE(0x0458, 0x7004), .driver_info = Rev072A},
+ {USB_DEVICE(0x0461, 0x0815), .driver_info = Rev072A},
+ {USB_DEVICE(0x046d, 0x0928), .driver_info = Rev012A},
+ {USB_DEVICE(0x046d, 0x0929), .driver_info = Rev012A},
+ {USB_DEVICE(0x046d, 0x092a), .driver_info = Rev012A},
+ {USB_DEVICE(0x046d, 0x092b), .driver_info = Rev012A},
+ {USB_DEVICE(0x046d, 0x092c), .driver_info = Rev012A},
+ {USB_DEVICE(0x046d, 0x092d), .driver_info = Rev012A},
+ {USB_DEVICE(0x046d, 0x092e), .driver_info = Rev012A},
+ {USB_DEVICE(0x046d, 0x092f), .driver_info = Rev012A},
+ {USB_DEVICE(0x04fc, 0x0561), .driver_info = Rev072A},
+ {USB_DEVICE(0x060b, 0xa001), .driver_info = Rev072A},
+ {USB_DEVICE(0x10fd, 0x7e50), .driver_info = Rev072A},
+ {USB_DEVICE(0xabcd, 0xcdee), .driver_info = Rev072A},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id,
+ sd_desc[id->driver_info],
+ sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/sq905.c b/drivers/media/video/gspca/sq905.c
new file mode 100644
index 00000000..2fe3c29b
--- /dev/null
+++ b/drivers/media/video/gspca/sq905.c
@@ -0,0 +1,435 @@
+/*
+ * SQ905 subdriver
+ *
+ * Copyright (C) 2008, 2009 Adam Baker and Theodore Kilgore
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * History and Acknowledgments
+ *
+ * The original Linux driver for SQ905 based cameras was written by
+ * Marcell Lengyel and furter developed by many other contributors
+ * and is available from http://sourceforge.net/projects/sqcam/
+ *
+ * This driver takes advantage of the reverse engineering work done for
+ * that driver and for libgphoto2 but shares no code with them.
+ *
+ * This driver has used as a base the finepix driver and other gspca
+ * based drivers and may still contain code fragments taken from those
+ * drivers.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "sq905"
+
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include "gspca.h"
+
+MODULE_AUTHOR("Adam Baker <linux@baker-net.org.uk>, "
+ "Theodore Kilgore <kilgota@auburn.edu>");
+MODULE_DESCRIPTION("GSPCA/SQ905 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* Default timeouts, in ms */
+#define SQ905_CMD_TIMEOUT 500
+#define SQ905_DATA_TIMEOUT 1000
+
+/* Maximum transfer size to use. */
+#define SQ905_MAX_TRANSFER 0x8000
+#define FRAME_HEADER_LEN 64
+
+/* The known modes, or registers. These go in the "value" slot. */
+
+/* 00 is "none" obviously */
+
+#define SQ905_BULK_READ 0x03 /* precedes any bulk read */
+#define SQ905_COMMAND 0x06 /* precedes the command codes below */
+#define SQ905_PING 0x07 /* when reading an "idling" command */
+#define SQ905_READ_DONE 0xc0 /* ack bulk read completed */
+
+/* Any non-zero value in the bottom 2 bits of the 2nd byte of
+ * the ID appears to indicate the camera can do 640*480. If the
+ * LSB of that byte is set the image is just upside down, otherwise
+ * it is rotated 180 degrees. */
+#define SQ905_HIRES_MASK 0x00000300
+#define SQ905_ORIENTATION_MASK 0x00000100
+
+/* Some command codes. These go in the "index" slot. */
+
+#define SQ905_ID 0xf0 /* asks for model string */
+#define SQ905_CONFIG 0x20 /* gets photo alloc. table, not used here */
+#define SQ905_DATA 0x30 /* accesses photo data, not used here */
+#define SQ905_CLEAR 0xa0 /* clear everything */
+#define SQ905_CAPTURE_LOW 0x60 /* Starts capture at 160x120 */
+#define SQ905_CAPTURE_MED 0x61 /* Starts capture at 320x240 */
+#define SQ905_CAPTURE_HIGH 0x62 /* Starts capture at 640x480 (some cams only) */
+/* note that the capture command also controls the output dimensions */
+
+/* Structure to hold all of our device specific stuff */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ /*
+ * Driver stuff
+ */
+ struct work_struct work_struct;
+ struct workqueue_struct *work_thread;
+};
+
+static struct v4l2_pix_format sq905_mode[] = {
+ { 160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+ { 320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+ { 640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0}
+};
+
+/*
+ * Send a command to the camera.
+ */
+static int sq905_command(struct gspca_dev *gspca_dev, u16 index)
+{
+ int ret;
+
+ gspca_dev->usb_buf[0] = '\0';
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ USB_REQ_SYNCH_FRAME, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ SQ905_COMMAND, index, gspca_dev->usb_buf, 1,
+ SQ905_CMD_TIMEOUT);
+ if (ret < 0) {
+ pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ USB_REQ_SYNCH_FRAME, /* request */
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ SQ905_PING, 0, gspca_dev->usb_buf, 1,
+ SQ905_CMD_TIMEOUT);
+ if (ret < 0) {
+ pr_err("%s: usb_control_msg failed 2 (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Acknowledge the end of a frame - see warning on sq905_command.
+ */
+static int sq905_ack_frame(struct gspca_dev *gspca_dev)
+{
+ int ret;
+
+ gspca_dev->usb_buf[0] = '\0';
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ USB_REQ_SYNCH_FRAME, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ SQ905_READ_DONE, 0, gspca_dev->usb_buf, 1,
+ SQ905_CMD_TIMEOUT);
+ if (ret < 0) {
+ pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * request and read a block of data - see warning on sq905_command.
+ */
+static int
+sq905_read_data(struct gspca_dev *gspca_dev, u8 *data, int size, int need_lock)
+{
+ int ret;
+ int act_len;
+
+ gspca_dev->usb_buf[0] = '\0';
+ if (need_lock)
+ mutex_lock(&gspca_dev->usb_lock);
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ USB_REQ_SYNCH_FRAME, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ SQ905_BULK_READ, size, gspca_dev->usb_buf,
+ 1, SQ905_CMD_TIMEOUT);
+ if (need_lock)
+ mutex_unlock(&gspca_dev->usb_lock);
+ if (ret < 0) {
+ pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
+ return ret;
+ }
+ ret = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x81),
+ data, size, &act_len, SQ905_DATA_TIMEOUT);
+
+ /* successful, it returns 0, otherwise negative */
+ if (ret < 0 || act_len != size) {
+ pr_err("bulk read fail (%d) len %d/%d\n", ret, act_len, size);
+ return -EIO;
+ }
+ return 0;
+}
+
+/* This function is called as a workqueue function and runs whenever the camera
+ * is streaming data. Because it is a workqueue function it is allowed to sleep
+ * so we can use synchronous USB calls. To avoid possible collisions with other
+ * threads attempting to use the camera's USB interface we take the gspca
+ * usb_lock when performing USB operations. In practice the only thing we need
+ * to protect against is the usb_set_interface call that gspca makes during
+ * stream_off as the camera doesn't provide any controls that the user could try
+ * to change.
+ */
+static void sq905_dostream(struct work_struct *work)
+{
+ struct sd *dev = container_of(work, struct sd, work_struct);
+ struct gspca_dev *gspca_dev = &dev->gspca_dev;
+ int bytes_left; /* bytes remaining in current frame. */
+ int data_len; /* size to use for the next read. */
+ int header_read; /* true if we have already read the frame header. */
+ int packet_type;
+ int frame_sz;
+ int ret;
+ u8 *data;
+ u8 *buffer;
+
+ buffer = kmalloc(SQ905_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
+ if (!buffer) {
+ pr_err("Couldn't allocate USB buffer\n");
+ goto quit_stream;
+ }
+
+ frame_sz = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].sizeimage
+ + FRAME_HEADER_LEN;
+
+ while (gspca_dev->present && gspca_dev->streaming) {
+ /* request some data and then read it until we have
+ * a complete frame. */
+ bytes_left = frame_sz;
+ header_read = 0;
+
+ /* Note we do not check for gspca_dev->streaming here, as
+ we must finish reading an entire frame, otherwise the
+ next time we stream we start reading in the middle of a
+ frame. */
+ while (bytes_left > 0 && gspca_dev->present) {
+ data_len = bytes_left > SQ905_MAX_TRANSFER ?
+ SQ905_MAX_TRANSFER : bytes_left;
+ ret = sq905_read_data(gspca_dev, buffer, data_len, 1);
+ if (ret < 0)
+ goto quit_stream;
+ PDEBUG(D_PACK,
+ "Got %d bytes out of %d for frame",
+ data_len, bytes_left);
+ bytes_left -= data_len;
+ data = buffer;
+ if (!header_read) {
+ packet_type = FIRST_PACKET;
+ /* The first 64 bytes of each frame are
+ * a header full of FF 00 bytes */
+ data += FRAME_HEADER_LEN;
+ data_len -= FRAME_HEADER_LEN;
+ header_read = 1;
+ } else if (bytes_left == 0) {
+ packet_type = LAST_PACKET;
+ } else {
+ packet_type = INTER_PACKET;
+ }
+ gspca_frame_add(gspca_dev, packet_type,
+ data, data_len);
+ /* If entire frame fits in one packet we still
+ need to add a LAST_PACKET */
+ if (packet_type == FIRST_PACKET &&
+ bytes_left == 0)
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ NULL, 0);
+ }
+ if (gspca_dev->present) {
+ /* acknowledge the frame */
+ mutex_lock(&gspca_dev->usb_lock);
+ ret = sq905_ack_frame(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
+ if (ret < 0)
+ goto quit_stream;
+ }
+ }
+quit_stream:
+ if (gspca_dev->present) {
+ mutex_lock(&gspca_dev->usb_lock);
+ sq905_command(gspca_dev, SQ905_CLEAR);
+ mutex_unlock(&gspca_dev->usb_lock);
+ }
+ kfree(buffer);
+}
+
+/* This function is called at probe time just before sd_init */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct cam *cam = &gspca_dev->cam;
+ struct sd *dev = (struct sd *) gspca_dev;
+
+ /* We don't use the buffer gspca allocates so make it small. */
+ cam->bulk = 1;
+ cam->bulk_size = 64;
+
+ INIT_WORK(&dev->work_struct, sq905_dostream);
+
+ return 0;
+}
+
+/* called on streamoff with alt==0 and on disconnect */
+/* the usb_lock is held at entry - restore on exit */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ struct sd *dev = (struct sd *) gspca_dev;
+
+ /* wait for the work queue to terminate */
+ mutex_unlock(&gspca_dev->usb_lock);
+ /* This waits for sq905_dostream to finish */
+ destroy_workqueue(dev->work_thread);
+ dev->work_thread = NULL;
+ mutex_lock(&gspca_dev->usb_lock);
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ u32 ident;
+ int ret;
+
+ /* connect to the camera and read
+ * the model ID and process that and put it away.
+ */
+ ret = sq905_command(gspca_dev, SQ905_CLEAR);
+ if (ret < 0)
+ return ret;
+ ret = sq905_command(gspca_dev, SQ905_ID);
+ if (ret < 0)
+ return ret;
+ ret = sq905_read_data(gspca_dev, gspca_dev->usb_buf, 4, 0);
+ if (ret < 0)
+ return ret;
+ /* usb_buf is allocated with kmalloc so is aligned.
+ * Camera model number is the right way round if we assume this
+ * reverse engineered ID is supposed to be big endian. */
+ ident = be32_to_cpup((__be32 *)gspca_dev->usb_buf);
+ ret = sq905_command(gspca_dev, SQ905_CLEAR);
+ if (ret < 0)
+ return ret;
+ PDEBUG(D_CONF, "SQ905 camera ID %08x detected", ident);
+ gspca_dev->cam.cam_mode = sq905_mode;
+ gspca_dev->cam.nmodes = ARRAY_SIZE(sq905_mode);
+ if (!(ident & SQ905_HIRES_MASK))
+ gspca_dev->cam.nmodes--;
+
+ if (ident & SQ905_ORIENTATION_MASK)
+ gspca_dev->cam.input_flags = V4L2_IN_ST_VFLIP;
+ else
+ gspca_dev->cam.input_flags = V4L2_IN_ST_VFLIP |
+ V4L2_IN_ST_HFLIP;
+ return 0;
+}
+
+/* Set up for getting frames. */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *dev = (struct sd *) gspca_dev;
+ int ret;
+
+ /* "Open the shutter" and set size, to start capture */
+ switch (gspca_dev->curr_mode) {
+ default:
+/* case 2: */
+ PDEBUG(D_STREAM, "Start streaming at high resolution");
+ ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_HIGH);
+ break;
+ case 1:
+ PDEBUG(D_STREAM, "Start streaming at medium resolution");
+ ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_MED);
+ break;
+ case 0:
+ PDEBUG(D_STREAM, "Start streaming at low resolution");
+ ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_LOW);
+ }
+
+ if (ret < 0) {
+ PDEBUG(D_ERR, "Start streaming command failed");
+ return ret;
+ }
+ /* Start the workqueue function to do the streaming */
+ dev->work_thread = create_singlethread_workqueue(MODULE_NAME);
+ queue_work(dev->work_thread, &dev->work_struct);
+
+ return 0;
+}
+
+/* Table of supported USB devices */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x2770, 0x9120)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stop0 = sd_stop0,
+};
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id,
+ &sd_desc,
+ sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/sq905c.c b/drivers/media/video/gspca/sq905c.c
new file mode 100644
index 00000000..ae783634
--- /dev/null
+++ b/drivers/media/video/gspca/sq905c.c
@@ -0,0 +1,342 @@
+/*
+ * SQ905C subdriver
+ *
+ * Copyright (C) 2009 Theodore Kilgore
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ *
+ * This driver uses work done in
+ * libgphoto2/camlibs/digigr8, Copyright (C) Theodore Kilgore.
+ *
+ * This driver has also used as a base the sq905c driver
+ * and may contain code fragments from it.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "sq905c"
+
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include "gspca.h"
+
+MODULE_AUTHOR("Theodore Kilgore <kilgota@auburn.edu>");
+MODULE_DESCRIPTION("GSPCA/SQ905C USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* Default timeouts, in ms */
+#define SQ905C_CMD_TIMEOUT 500
+#define SQ905C_DATA_TIMEOUT 1000
+
+/* Maximum transfer size to use. */
+#define SQ905C_MAX_TRANSFER 0x8000
+
+#define FRAME_HEADER_LEN 0x50
+
+/* Commands. These go in the "value" slot. */
+#define SQ905C_CLEAR 0xa0 /* clear everything */
+#define SQ905C_GET_ID 0x14f4 /* Read version number */
+#define SQ905C_CAPTURE_LOW 0xa040 /* Starts capture at 160x120 */
+#define SQ905C_CAPTURE_MED 0x1440 /* Starts capture at 320x240 */
+#define SQ905C_CAPTURE_HI 0x2840 /* Starts capture at 320x240 */
+
+/* For capture, this must go in the "index" slot. */
+#define SQ905C_CAPTURE_INDEX 0x110f
+
+/* Structure to hold all of our device specific stuff */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ const struct v4l2_pix_format *cap_mode;
+ /* Driver stuff */
+ struct work_struct work_struct;
+ struct workqueue_struct *work_thread;
+};
+
+/*
+ * Most of these cameras will do 640x480 and 320x240. 160x120 works
+ * in theory but gives very poor output. Therefore, not supported.
+ * The 0x2770:0x9050 cameras have max resolution of 320x240.
+ */
+static struct v4l2_pix_format sq905c_mode[] = {
+ { 320, 240, V4L2_PIX_FMT_SQ905C, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+ { 640, 480, V4L2_PIX_FMT_SQ905C, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0}
+};
+
+/* Send a command to the camera. */
+static int sq905c_command(struct gspca_dev *gspca_dev, u16 command, u16 index)
+{
+ int ret;
+
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ USB_REQ_SYNCH_FRAME, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ command, index, NULL, 0,
+ SQ905C_CMD_TIMEOUT);
+ if (ret < 0) {
+ pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sq905c_read(struct gspca_dev *gspca_dev, u16 command, u16 index,
+ int size)
+{
+ int ret;
+
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ USB_REQ_SYNCH_FRAME, /* request */
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ command, index, gspca_dev->usb_buf, size,
+ SQ905C_CMD_TIMEOUT);
+ if (ret < 0) {
+ pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* This function is called as a workqueue function and runs whenever the camera
+ * is streaming data. Because it is a workqueue function it is allowed to sleep
+ * so we can use synchronous USB calls. To avoid possible collisions with other
+ * threads attempting to use the camera's USB interface the gspca usb_lock is
+ * used when performing the one USB control operation inside the workqueue,
+ * which tells the camera to close the stream. In practice the only thing
+ * which needs to be protected against is the usb_set_interface call that
+ * gspca makes during stream_off. Otherwise the camera doesn't provide any
+ * controls that the user could try to change.
+ */
+static void sq905c_dostream(struct work_struct *work)
+{
+ struct sd *dev = container_of(work, struct sd, work_struct);
+ struct gspca_dev *gspca_dev = &dev->gspca_dev;
+ int bytes_left; /* bytes remaining in current frame. */
+ int data_len; /* size to use for the next read. */
+ int act_len;
+ int packet_type;
+ int ret;
+ u8 *buffer;
+
+ buffer = kmalloc(SQ905C_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
+ if (!buffer) {
+ pr_err("Couldn't allocate USB buffer\n");
+ goto quit_stream;
+ }
+
+ while (gspca_dev->present && gspca_dev->streaming) {
+ /* Request the header, which tells the size to download */
+ ret = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x81),
+ buffer, FRAME_HEADER_LEN, &act_len,
+ SQ905C_DATA_TIMEOUT);
+ PDEBUG(D_STREAM,
+ "Got %d bytes out of %d for header",
+ act_len, FRAME_HEADER_LEN);
+ if (ret < 0 || act_len < FRAME_HEADER_LEN)
+ goto quit_stream;
+ /* size is read from 4 bytes starting 0x40, little endian */
+ bytes_left = buffer[0x40]|(buffer[0x41]<<8)|(buffer[0x42]<<16)
+ |(buffer[0x43]<<24);
+ PDEBUG(D_STREAM, "bytes_left = 0x%x", bytes_left);
+ /* We keep the header. It has other information, too. */
+ packet_type = FIRST_PACKET;
+ gspca_frame_add(gspca_dev, packet_type,
+ buffer, FRAME_HEADER_LEN);
+ while (bytes_left > 0 && gspca_dev->present) {
+ data_len = bytes_left > SQ905C_MAX_TRANSFER ?
+ SQ905C_MAX_TRANSFER : bytes_left;
+ ret = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x81),
+ buffer, data_len, &act_len,
+ SQ905C_DATA_TIMEOUT);
+ if (ret < 0 || act_len < data_len)
+ goto quit_stream;
+ PDEBUG(D_STREAM,
+ "Got %d bytes out of %d for frame",
+ data_len, bytes_left);
+ bytes_left -= data_len;
+ if (bytes_left == 0)
+ packet_type = LAST_PACKET;
+ else
+ packet_type = INTER_PACKET;
+ gspca_frame_add(gspca_dev, packet_type,
+ buffer, data_len);
+ }
+ }
+quit_stream:
+ if (gspca_dev->present) {
+ mutex_lock(&gspca_dev->usb_lock);
+ sq905c_command(gspca_dev, SQ905C_CLEAR, 0);
+ mutex_unlock(&gspca_dev->usb_lock);
+ }
+ kfree(buffer);
+}
+
+/* This function is called at probe time just before sd_init */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct cam *cam = &gspca_dev->cam;
+ struct sd *dev = (struct sd *) gspca_dev;
+ int ret;
+
+ PDEBUG(D_PROBE,
+ "SQ9050 camera detected"
+ " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
+
+ ret = sq905c_command(gspca_dev, SQ905C_GET_ID, 0);
+ if (ret < 0) {
+ PDEBUG(D_ERR, "Get version command failed");
+ return ret;
+ }
+
+ ret = sq905c_read(gspca_dev, 0xf5, 0, 20);
+ if (ret < 0) {
+ PDEBUG(D_ERR, "Reading version command failed");
+ return ret;
+ }
+ /* Note we leave out the usb id and the manufacturing date */
+ PDEBUG(D_PROBE,
+ "SQ9050 ID string: %02x - %02x %02x %02x %02x %02x %02x",
+ gspca_dev->usb_buf[3],
+ gspca_dev->usb_buf[14], gspca_dev->usb_buf[15],
+ gspca_dev->usb_buf[16], gspca_dev->usb_buf[17],
+ gspca_dev->usb_buf[18], gspca_dev->usb_buf[19]);
+
+ cam->cam_mode = sq905c_mode;
+ cam->nmodes = 2;
+ if (gspca_dev->usb_buf[15] == 0)
+ cam->nmodes = 1;
+ /* We don't use the buffer gspca allocates so make it small. */
+ cam->bulk_size = 32;
+ cam->bulk = 1;
+ INIT_WORK(&dev->work_struct, sq905c_dostream);
+ return 0;
+}
+
+/* called on streamoff with alt==0 and on disconnect */
+/* the usb_lock is held at entry - restore on exit */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ struct sd *dev = (struct sd *) gspca_dev;
+
+ /* wait for the work queue to terminate */
+ mutex_unlock(&gspca_dev->usb_lock);
+ /* This waits for sq905c_dostream to finish */
+ destroy_workqueue(dev->work_thread);
+ dev->work_thread = NULL;
+ mutex_lock(&gspca_dev->usb_lock);
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ int ret;
+
+ /* connect to the camera and reset it. */
+ ret = sq905c_command(gspca_dev, SQ905C_CLEAR, 0);
+ return ret;
+}
+
+/* Set up for getting frames. */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *dev = (struct sd *) gspca_dev;
+ int ret;
+
+ dev->cap_mode = gspca_dev->cam.cam_mode;
+ /* "Open the shutter" and set size, to start capture */
+ switch (gspca_dev->width) {
+ case 640:
+ PDEBUG(D_STREAM, "Start streaming at high resolution");
+ dev->cap_mode++;
+ ret = sq905c_command(gspca_dev, SQ905C_CAPTURE_HI,
+ SQ905C_CAPTURE_INDEX);
+ break;
+ default: /* 320 */
+ PDEBUG(D_STREAM, "Start streaming at medium resolution");
+ ret = sq905c_command(gspca_dev, SQ905C_CAPTURE_MED,
+ SQ905C_CAPTURE_INDEX);
+ }
+
+ if (ret < 0) {
+ PDEBUG(D_ERR, "Start streaming command failed");
+ return ret;
+ }
+ /* Start the workqueue function to do the streaming */
+ dev->work_thread = create_singlethread_workqueue(MODULE_NAME);
+ queue_work(dev->work_thread, &dev->work_struct);
+
+ return 0;
+}
+
+/* Table of supported USB devices */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x2770, 0x905c)},
+ {USB_DEVICE(0x2770, 0x9050)},
+ {USB_DEVICE(0x2770, 0x9051)},
+ {USB_DEVICE(0x2770, 0x9052)},
+ {USB_DEVICE(0x2770, 0x913d)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stop0 = sd_stop0,
+};
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id,
+ &sd_desc,
+ sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/sq930x.c b/drivers/media/video/gspca/sq930x.c
new file mode 100644
index 00000000..1a8ba9b3
--- /dev/null
+++ b/drivers/media/video/gspca/sq930x.c
@@ -0,0 +1,1200 @@
+/*
+ * SQ930x subdriver
+ *
+ * Copyright (C) 2010 Jean-François Moine <http://moinejf.free.fr>
+ * Copyright (C) 2006 -2008 Gerard Klaver <gerard at gkall dot hobby dot nl>
+ * Copyright (C) 2007 Sam Revitch <samr7@cs.washington.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "sq930x"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>\n"
+ "Gerard Klaver <gerard at gkall dot hobby dot nl\n"
+ "Sam Revitch <samr7@cs.washington.edu>");
+MODULE_DESCRIPTION("GSPCA/SQ930x USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* Structure to hold all of our device specific stuff */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ u16 expo;
+ u8 gain;
+
+ u8 do_ctrl;
+ u8 gpio[2];
+ u8 sensor;
+ u8 type;
+#define Generic 0
+#define Creative_live_motion 1
+};
+enum sensors {
+ SENSOR_ICX098BQ,
+ SENSOR_LZ24BP,
+ SENSOR_MI0360,
+ SENSOR_MT9V111, /* = MI360SOC */
+ SENSOR_OV7660,
+ SENSOR_OV9630,
+};
+
+static int sd_setexpo(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getexpo(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
+
+static const struct ctrl sd_ctrls[] = {
+ {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 0x0001,
+ .maximum = 0x0fff,
+ .step = 1,
+#define EXPO_DEF 0x0356
+ .default_value = EXPO_DEF,
+ },
+ .set = sd_setexpo,
+ .get = sd_getexpo,
+ },
+ {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0x01,
+ .maximum = 0xff,
+ .step = 1,
+#define GAIN_DEF 0x8d
+ .default_value = GAIN_DEF,
+ },
+ .set = sd_setgain,
+ .get = sd_getgain,
+ },
+};
+
+static struct v4l2_pix_format vga_mode[] = {
+ {320, 240, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+ {640, 480, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+};
+
+/* sq930x registers */
+#define SQ930_CTRL_UCBUS_IO 0x0001
+#define SQ930_CTRL_I2C_IO 0x0002
+#define SQ930_CTRL_GPIO 0x0005
+#define SQ930_CTRL_CAP_START 0x0010
+#define SQ930_CTRL_CAP_STOP 0x0011
+#define SQ930_CTRL_SET_EXPOSURE 0x001d
+#define SQ930_CTRL_RESET 0x001e
+#define SQ930_CTRL_GET_DEV_INFO 0x001f
+
+/* gpio 1 (8..15) */
+#define SQ930_GPIO_DFL_I2C_SDA 0x0001
+#define SQ930_GPIO_DFL_I2C_SCL 0x0002
+#define SQ930_GPIO_RSTBAR 0x0004
+#define SQ930_GPIO_EXTRA1 0x0040
+#define SQ930_GPIO_EXTRA2 0x0080
+/* gpio 3 (24..31) */
+#define SQ930_GPIO_POWER 0x0200
+#define SQ930_GPIO_DFL_LED 0x1000
+
+struct ucbus_write_cmd {
+ u16 bw_addr;
+ u8 bw_data;
+};
+struct i2c_write_cmd {
+ u8 reg;
+ u16 val;
+};
+
+static const struct ucbus_write_cmd icx098bq_start_0[] = {
+ {0x0354, 0x00}, {0x03fa, 0x00}, {0xf800, 0x02}, {0xf801, 0xce},
+ {0xf802, 0xc1}, {0xf804, 0x00}, {0xf808, 0x00}, {0xf809, 0x0e},
+ {0xf80a, 0x01}, {0xf80b, 0xee}, {0xf807, 0x60}, {0xf80c, 0x02},
+ {0xf80d, 0xf0}, {0xf80e, 0x03}, {0xf80f, 0x0a}, {0xf81c, 0x02},
+ {0xf81d, 0xf0}, {0xf81e, 0x03}, {0xf81f, 0x0a}, {0xf83a, 0x00},
+ {0xf83b, 0x10}, {0xf83c, 0x00}, {0xf83d, 0x4e}, {0xf810, 0x04},
+ {0xf811, 0x00}, {0xf812, 0x02}, {0xf813, 0x10}, {0xf803, 0x00},
+ {0xf814, 0x01}, {0xf815, 0x18}, {0xf816, 0x00}, {0xf817, 0x48},
+ {0xf818, 0x00}, {0xf819, 0x25}, {0xf81a, 0x00}, {0xf81b, 0x3c},
+ {0xf82f, 0x03}, {0xf820, 0xff}, {0xf821, 0x0d}, {0xf822, 0xff},
+ {0xf823, 0x07}, {0xf824, 0xff}, {0xf825, 0x03}, {0xf826, 0xff},
+ {0xf827, 0x06}, {0xf828, 0xff}, {0xf829, 0x03}, {0xf82a, 0xff},
+ {0xf82b, 0x0c}, {0xf82c, 0xfd}, {0xf82d, 0x01}, {0xf82e, 0x00},
+ {0xf830, 0x00}, {0xf831, 0x47}, {0xf832, 0x00}, {0xf833, 0x00},
+ {0xf850, 0x00}, {0xf851, 0x00}, {0xf852, 0x00}, {0xf853, 0x24},
+ {0xf854, 0x00}, {0xf855, 0x18}, {0xf856, 0x00}, {0xf857, 0x3c},
+ {0xf858, 0x00}, {0xf859, 0x0c}, {0xf85a, 0x00}, {0xf85b, 0x30},
+ {0xf85c, 0x00}, {0xf85d, 0x0c}, {0xf85e, 0x00}, {0xf85f, 0x30},
+ {0xf860, 0x00}, {0xf861, 0x48}, {0xf862, 0x01}, {0xf863, 0xdc},
+ {0xf864, 0xff}, {0xf865, 0x98}, {0xf866, 0xff}, {0xf867, 0xc0},
+ {0xf868, 0xff}, {0xf869, 0x70}, {0xf86c, 0xff}, {0xf86d, 0x00},
+ {0xf86a, 0xff}, {0xf86b, 0x48}, {0xf86e, 0xff}, {0xf86f, 0x00},
+ {0xf870, 0x01}, {0xf871, 0xdb}, {0xf872, 0x01}, {0xf873, 0xfa},
+ {0xf874, 0x01}, {0xf875, 0xdb}, {0xf876, 0x01}, {0xf877, 0xfa},
+ {0xf878, 0x0f}, {0xf879, 0x0f}, {0xf87a, 0xff}, {0xf87b, 0xff},
+ {0xf800, 0x03}
+};
+static const struct ucbus_write_cmd icx098bq_start_1[] = {
+ {0xf5f0, 0x00}, {0xf5f1, 0xcd}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
+ {0xf5f4, 0xc0},
+ {0xf5f0, 0x49}, {0xf5f1, 0xcd}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
+ {0xf5f4, 0xc0},
+ {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00},
+ {0xf5f9, 0x00}
+};
+
+static const struct ucbus_write_cmd icx098bq_start_2[] = {
+ {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x82}, {0xf806, 0x00},
+ {0xf807, 0x7f}, {0xf800, 0x03},
+ {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x40}, {0xf806, 0x00},
+ {0xf807, 0x7f}, {0xf800, 0x03},
+ {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0xcf}, {0xf806, 0xd0},
+ {0xf807, 0x7f}, {0xf800, 0x03},
+ {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x00}, {0xf806, 0x00},
+ {0xf807, 0x7f}, {0xf800, 0x03}
+};
+
+static const struct ucbus_write_cmd lz24bp_start_0[] = {
+ {0x0354, 0x00}, {0x03fa, 0x00}, {0xf800, 0x02}, {0xf801, 0xbe},
+ {0xf802, 0xc6}, {0xf804, 0x00}, {0xf808, 0x00}, {0xf809, 0x06},
+ {0xf80a, 0x01}, {0xf80b, 0xfe}, {0xf807, 0x84}, {0xf80c, 0x02},
+ {0xf80d, 0xf7}, {0xf80e, 0x03}, {0xf80f, 0x0b}, {0xf81c, 0x00},
+ {0xf81d, 0x49}, {0xf81e, 0x03}, {0xf81f, 0x0b}, {0xf83a, 0x00},
+ {0xf83b, 0x01}, {0xf83c, 0x00}, {0xf83d, 0x6b}, {0xf810, 0x03},
+ {0xf811, 0x10}, {0xf812, 0x02}, {0xf813, 0x6f}, {0xf803, 0x00},
+ {0xf814, 0x00}, {0xf815, 0x44}, {0xf816, 0x00}, {0xf817, 0x48},
+ {0xf818, 0x00}, {0xf819, 0x25}, {0xf81a, 0x00}, {0xf81b, 0x3c},
+ {0xf82f, 0x03}, {0xf820, 0xff}, {0xf821, 0x0d}, {0xf822, 0xff},
+ {0xf823, 0x07}, {0xf824, 0xfd}, {0xf825, 0x07}, {0xf826, 0xf0},
+ {0xf827, 0x0c}, {0xf828, 0xff}, {0xf829, 0x03}, {0xf82a, 0xff},
+ {0xf82b, 0x0c}, {0xf82c, 0xfc}, {0xf82d, 0x01}, {0xf82e, 0x00},
+ {0xf830, 0x00}, {0xf831, 0x47}, {0xf832, 0x00}, {0xf833, 0x00},
+ {0xf850, 0x00}, {0xf851, 0x00}, {0xf852, 0x00}, {0xf853, 0x24},
+ {0xf854, 0x00}, {0xf855, 0x0c}, {0xf856, 0x00}, {0xf857, 0x30},
+ {0xf858, 0x00}, {0xf859, 0x18}, {0xf85a, 0x00}, {0xf85b, 0x3c},
+ {0xf85c, 0x00}, {0xf85d, 0x18}, {0xf85e, 0x00}, {0xf85f, 0x3c},
+ {0xf860, 0xff}, {0xf861, 0x37}, {0xf862, 0xff}, {0xf863, 0x1d},
+ {0xf864, 0xff}, {0xf865, 0x98}, {0xf866, 0xff}, {0xf867, 0xc0},
+ {0xf868, 0x00}, {0xf869, 0x37}, {0xf86c, 0x02}, {0xf86d, 0x1d},
+ {0xf86a, 0x00}, {0xf86b, 0x37}, {0xf86e, 0x02}, {0xf86f, 0x1d},
+ {0xf870, 0x01}, {0xf871, 0xc6}, {0xf872, 0x02}, {0xf873, 0x04},
+ {0xf874, 0x01}, {0xf875, 0xc6}, {0xf876, 0x02}, {0xf877, 0x04},
+ {0xf878, 0x0f}, {0xf879, 0x0f}, {0xf87a, 0xff}, {0xf87b, 0xff},
+ {0xf800, 0x03}
+};
+static const struct ucbus_write_cmd lz24bp_start_1_gen[] = {
+ {0xf5f0, 0x00}, {0xf5f1, 0xff}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
+ {0xf5f4, 0xb3},
+ {0xf5f0, 0x40}, {0xf5f1, 0xff}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
+ {0xf5f4, 0xb3},
+ {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00},
+ {0xf5f9, 0x00}
+};
+
+static const struct ucbus_write_cmd lz24bp_start_1_clm[] = {
+ {0xf5f0, 0x00}, {0xf5f1, 0xff}, {0xf5f2, 0x88}, {0xf5f3, 0x88},
+ {0xf5f4, 0xc0},
+ {0xf5f0, 0x40}, {0xf5f1, 0xff}, {0xf5f2, 0x88}, {0xf5f3, 0x88},
+ {0xf5f4, 0xc0},
+ {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00},
+ {0xf5f9, 0x00}
+};
+
+static const struct ucbus_write_cmd lz24bp_start_2[] = {
+ {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x80}, {0xf806, 0x00},
+ {0xf807, 0x7f}, {0xf800, 0x03},
+ {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x4e}, {0xf806, 0x00},
+ {0xf807, 0x7f}, {0xf800, 0x03},
+ {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0xc0}, {0xf806, 0x48},
+ {0xf807, 0x7f}, {0xf800, 0x03},
+ {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x00}, {0xf806, 0x00},
+ {0xf807, 0x7f}, {0xf800, 0x03}
+};
+
+static const struct ucbus_write_cmd mi0360_start_0[] = {
+ {0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0xcc}, {0xf333, 0xcc},
+ {0xf334, 0xcc}, {0xf335, 0xcc}, {0xf33f, 0x00}
+};
+static const struct i2c_write_cmd mi0360_init_23[] = {
+ {0x30, 0x0040}, /* reserved - def 0x0005 */
+ {0x31, 0x0000}, /* reserved - def 0x002a */
+ {0x34, 0x0100}, /* reserved - def 0x0100 */
+ {0x3d, 0x068f}, /* reserved - def 0x068f */
+};
+static const struct i2c_write_cmd mi0360_init_24[] = {
+ {0x03, 0x01e5}, /* window height */
+ {0x04, 0x0285}, /* window width */
+};
+static const struct i2c_write_cmd mi0360_init_25[] = {
+ {0x35, 0x0020}, /* global gain */
+ {0x2b, 0x0020}, /* green1 gain */
+ {0x2c, 0x002a}, /* blue gain */
+ {0x2d, 0x0028}, /* red gain */
+ {0x2e, 0x0020}, /* green2 gain */
+};
+static const struct ucbus_write_cmd mi0360_start_1[] = {
+ {0xf5f0, 0x11}, {0xf5f1, 0x99}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
+ {0xf5f4, 0xa6},
+ {0xf5f0, 0x51}, {0xf5f1, 0x99}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
+ {0xf5f4, 0xa6},
+ {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00},
+ {0xf5f9, 0x00}
+};
+static const struct i2c_write_cmd mi0360_start_2[] = {
+ {0x62, 0x041d}, /* reserved - def 0x0418 */
+};
+static const struct i2c_write_cmd mi0360_start_3[] = {
+ {0x05, 0x007b}, /* horiz blanking */
+};
+static const struct i2c_write_cmd mi0360_start_4[] = {
+ {0x05, 0x03f5}, /* horiz blanking */
+};
+
+static const struct i2c_write_cmd mt9v111_init_0[] = {
+ {0x01, 0x0001}, /* select IFP/SOC registers */
+ {0x06, 0x300c}, /* operating mode control */
+ {0x08, 0xcc00}, /* output format control (RGB) */
+ {0x01, 0x0004}, /* select sensor core registers */
+};
+static const struct i2c_write_cmd mt9v111_init_1[] = {
+ {0x03, 0x01e5}, /* window height */
+ {0x04, 0x0285}, /* window width */
+};
+static const struct i2c_write_cmd mt9v111_init_2[] = {
+ {0x30, 0x7800},
+ {0x31, 0x0000},
+ {0x07, 0x3002}, /* output control */
+ {0x35, 0x0020}, /* global gain */
+ {0x2b, 0x0020}, /* green1 gain */
+ {0x2c, 0x0020}, /* blue gain */
+ {0x2d, 0x0020}, /* red gain */
+ {0x2e, 0x0020}, /* green2 gain */
+};
+static const struct ucbus_write_cmd mt9v111_start_1[] = {
+ {0xf5f0, 0x11}, {0xf5f1, 0x96}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
+ {0xf5f4, 0xaa},
+ {0xf5f0, 0x51}, {0xf5f1, 0x96}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
+ {0xf5f4, 0xaa},
+ {0xf5fa, 0x00}, {0xf5f6, 0x0a}, {0xf5f7, 0x0a}, {0xf5f8, 0x0a},
+ {0xf5f9, 0x0a}
+};
+static const struct i2c_write_cmd mt9v111_init_3[] = {
+ {0x62, 0x0405},
+};
+static const struct i2c_write_cmd mt9v111_init_4[] = {
+/* {0x05, 0x00ce}, */
+ {0x05, 0x005d}, /* horizontal blanking */
+};
+
+static const struct ucbus_write_cmd ov7660_start_0[] = {
+ {0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0x00}, {0xf333, 0xc0},
+ {0xf334, 0x39}, {0xf335, 0xe7}, {0xf33f, 0x03}
+};
+
+static const struct ucbus_write_cmd ov9630_start_0[] = {
+ {0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0x00}, {0xf333, 0x00},
+ {0xf334, 0x3e}, {0xf335, 0xf8}, {0xf33f, 0x03}
+};
+
+/* start parameters indexed by [sensor][mode] */
+static const struct cap_s {
+ u8 cc_sizeid;
+ u8 cc_bytes[32];
+} capconfig[4][2] = {
+ [SENSOR_ICX098BQ] = {
+ {2, /* Bayer 320x240 */
+ {0x05, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
+ 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+ {4, /* Bayer 640x480 */
+ {0x01, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
+ 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+ },
+ [SENSOR_LZ24BP] = {
+ {2, /* Bayer 320x240 */
+ {0x05, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee,
+ 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+ {4, /* Bayer 640x480 */
+ {0x01, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee,
+ 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+ },
+ [SENSOR_MI0360] = {
+ {2, /* Bayer 320x240 */
+ {0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
+ 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+ {4, /* Bayer 640x480 */
+ {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
+ 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+ },
+ [SENSOR_MT9V111] = {
+ {2, /* Bayer 320x240 */
+ {0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
+ 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+ {4, /* Bayer 640x480 */
+ {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
+ 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+ },
+};
+
+struct sensor_s {
+ const char *name;
+ u8 i2c_addr;
+ u8 i2c_dum;
+ u8 gpio[5];
+ u8 cmd_len;
+ const struct ucbus_write_cmd *cmd;
+};
+
+static const struct sensor_s sensor_tb[] = {
+ [SENSOR_ICX098BQ] = {
+ "icx098bp",
+ 0x00, 0x00,
+ {0,
+ SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
+ SQ930_GPIO_DFL_I2C_SDA,
+ 0,
+ SQ930_GPIO_RSTBAR
+ },
+ 8, icx098bq_start_0
+ },
+ [SENSOR_LZ24BP] = {
+ "lz24bp",
+ 0x00, 0x00,
+ {0,
+ SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
+ SQ930_GPIO_DFL_I2C_SDA,
+ 0,
+ SQ930_GPIO_RSTBAR
+ },
+ 8, lz24bp_start_0
+ },
+ [SENSOR_MI0360] = {
+ "mi0360",
+ 0x5d, 0x80,
+ {SQ930_GPIO_RSTBAR,
+ SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
+ SQ930_GPIO_DFL_I2C_SDA,
+ 0,
+ 0
+ },
+ 7, mi0360_start_0
+ },
+ [SENSOR_MT9V111] = {
+ "mt9v111",
+ 0x5c, 0x7f,
+ {SQ930_GPIO_RSTBAR,
+ SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
+ SQ930_GPIO_DFL_I2C_SDA,
+ 0,
+ 0
+ },
+ 7, mi0360_start_0
+ },
+ [SENSOR_OV7660] = {
+ "ov7660",
+ 0x21, 0x00,
+ {0,
+ SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
+ SQ930_GPIO_DFL_I2C_SDA,
+ 0,
+ SQ930_GPIO_RSTBAR
+ },
+ 7, ov7660_start_0
+ },
+ [SENSOR_OV9630] = {
+ "ov9630",
+ 0x30, 0x00,
+ {0,
+ SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
+ SQ930_GPIO_DFL_I2C_SDA,
+ 0,
+ SQ930_GPIO_RSTBAR
+ },
+ 7, ov9630_start_0
+ },
+};
+
+static void reg_r(struct gspca_dev *gspca_dev,
+ u16 value, int len)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ 0x0c,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, 0, gspca_dev->usb_buf, len,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_r %04x failed %d\n", value, ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ PDEBUG(D_USBO, "reg_w v: %04x i: %04x", value, index);
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0x0c, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0,
+ 500);
+ msleep(30);
+ if (ret < 0) {
+ pr_err("reg_w %04x %04x failed %d\n", value, index, ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static void reg_wb(struct gspca_dev *gspca_dev, u16 value, u16 index,
+ const u8 *data, int len)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ PDEBUG(D_USBO, "reg_wb v: %04x i: %04x %02x...%02x",
+ value, index, *data, data[len - 1]);
+ memcpy(gspca_dev->usb_buf, data, len);
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0x0c, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, gspca_dev->usb_buf, len,
+ 1000);
+ msleep(30);
+ if (ret < 0) {
+ pr_err("reg_wb %04x %04x failed %d\n", value, index, ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static void i2c_write(struct sd *sd,
+ const struct i2c_write_cmd *cmd,
+ int ncmds)
+{
+ struct gspca_dev *gspca_dev = &sd->gspca_dev;
+ const struct sensor_s *sensor;
+ u16 val, idx;
+ u8 *buf;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+
+ sensor = &sensor_tb[sd->sensor];
+
+ val = (sensor->i2c_addr << 8) | SQ930_CTRL_I2C_IO;
+ idx = (cmd->val & 0xff00) | cmd->reg;
+
+ buf = gspca_dev->usb_buf;
+ *buf++ = sensor->i2c_dum;
+ *buf++ = cmd->val;
+
+ while (--ncmds > 0) {
+ cmd++;
+ *buf++ = cmd->reg;
+ *buf++ = cmd->val >> 8;
+ *buf++ = sensor->i2c_dum;
+ *buf++ = cmd->val;
+ }
+
+ PDEBUG(D_USBO, "i2c_w v: %04x i: %04x %02x...%02x",
+ val, idx, gspca_dev->usb_buf[0], buf[-1]);
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0x0c, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ val, idx,
+ gspca_dev->usb_buf, buf - gspca_dev->usb_buf,
+ 500);
+ if (ret < 0) {
+ pr_err("i2c_write failed %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static void ucbus_write(struct gspca_dev *gspca_dev,
+ const struct ucbus_write_cmd *cmd,
+ int ncmds,
+ int batchsize)
+{
+ u8 *buf;
+ u16 val, idx;
+ int len, ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+
+#ifdef GSPCA_DEBUG
+ if ((batchsize - 1) * 3 > USB_BUF_SZ) {
+ pr_err("Bug: usb_buf overflow\n");
+ gspca_dev->usb_err = -ENOMEM;
+ return;
+ }
+#endif
+
+ for (;;) {
+ len = ncmds;
+ if (len > batchsize)
+ len = batchsize;
+ ncmds -= len;
+
+ val = (cmd->bw_addr << 8) | SQ930_CTRL_UCBUS_IO;
+ idx = (cmd->bw_data << 8) | (cmd->bw_addr >> 8);
+
+ buf = gspca_dev->usb_buf;
+ while (--len > 0) {
+ cmd++;
+ *buf++ = cmd->bw_addr;
+ *buf++ = cmd->bw_addr >> 8;
+ *buf++ = cmd->bw_data;
+ }
+ if (buf != gspca_dev->usb_buf)
+ PDEBUG(D_USBO, "ucbus v: %04x i: %04x %02x...%02x",
+ val, idx,
+ gspca_dev->usb_buf[0], buf[-1]);
+ else
+ PDEBUG(D_USBO, "ucbus v: %04x i: %04x",
+ val, idx);
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0x0c, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ val, idx,
+ gspca_dev->usb_buf, buf - gspca_dev->usb_buf,
+ 500);
+ if (ret < 0) {
+ pr_err("ucbus_write failed %d\n", ret);
+ gspca_dev->usb_err = ret;
+ return;
+ }
+ msleep(30);
+ if (ncmds <= 0)
+ break;
+ cmd++;
+ }
+}
+
+static void gpio_set(struct sd *sd, u16 val, u16 mask)
+{
+ struct gspca_dev *gspca_dev = &sd->gspca_dev;
+
+ if (mask & 0x00ff) {
+ sd->gpio[0] &= ~mask;
+ sd->gpio[0] |= val;
+ reg_w(gspca_dev, 0x0100 | SQ930_CTRL_GPIO,
+ ~sd->gpio[0] << 8);
+ }
+ mask >>= 8;
+ val >>= 8;
+ if (mask) {
+ sd->gpio[1] &= ~mask;
+ sd->gpio[1] |= val;
+ reg_w(gspca_dev, 0x0300 | SQ930_CTRL_GPIO,
+ ~sd->gpio[1] << 8);
+ }
+}
+
+static void gpio_init(struct sd *sd,
+ const u8 *gpio)
+{
+ gpio_set(sd, *gpio++, 0x000f);
+ gpio_set(sd, *gpio++, 0x000f);
+ gpio_set(sd, *gpio++, 0x000f);
+ gpio_set(sd, *gpio++, 0x000f);
+ gpio_set(sd, *gpio, 0x000f);
+}
+
+static void bridge_init(struct sd *sd)
+{
+ static const struct ucbus_write_cmd clkfreq_cmd = {
+ 0xf031, 0 /* SQ930_CLKFREQ_60MHZ */
+ };
+
+ ucbus_write(&sd->gspca_dev, &clkfreq_cmd, 1, 1);
+
+ gpio_set(sd, SQ930_GPIO_POWER, 0xff00);
+}
+
+static void cmos_probe(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+ const struct sensor_s *sensor;
+ static const u8 probe_order[] = {
+/* SENSOR_LZ24BP, (tested as ccd) */
+ SENSOR_OV9630,
+ SENSOR_MI0360,
+ SENSOR_OV7660,
+ SENSOR_MT9V111,
+ };
+
+ for (i = 0; i < ARRAY_SIZE(probe_order); i++) {
+ sensor = &sensor_tb[probe_order[i]];
+ ucbus_write(&sd->gspca_dev, sensor->cmd, sensor->cmd_len, 8);
+ gpio_init(sd, sensor->gpio);
+ msleep(100);
+ reg_r(gspca_dev, (sensor->i2c_addr << 8) | 0x001c, 1);
+ msleep(100);
+ if (gspca_dev->usb_buf[0] != 0)
+ break;
+ }
+ if (i >= ARRAY_SIZE(probe_order)) {
+ pr_err("Unknown sensor\n");
+ gspca_dev->usb_err = -EINVAL;
+ return;
+ }
+ sd->sensor = probe_order[i];
+ switch (sd->sensor) {
+ case SENSOR_OV7660:
+ case SENSOR_OV9630:
+ pr_err("Sensor %s not yet treated\n",
+ sensor_tb[sd->sensor].name);
+ gspca_dev->usb_err = -EINVAL;
+ break;
+ }
+}
+
+static void mt9v111_init(struct gspca_dev *gspca_dev)
+{
+ int i, nwait;
+ static const u8 cmd_001b[] = {
+ 0x00, 0x3b, 0xf6, 0x01, 0x03, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00
+ };
+ static const u8 cmd_011b[][7] = {
+ {0x10, 0x01, 0x66, 0x08, 0x00, 0x00, 0x00},
+ {0x01, 0x00, 0x1a, 0x04, 0x00, 0x00, 0x00},
+ {0x20, 0x00, 0x10, 0x04, 0x00, 0x00, 0x00},
+ {0x02, 0x01, 0xae, 0x01, 0x00, 0x00, 0x00},
+ };
+
+ reg_wb(gspca_dev, 0x001b, 0x0000, cmd_001b, sizeof cmd_001b);
+ for (i = 0; i < ARRAY_SIZE(cmd_011b); i++) {
+ reg_wb(gspca_dev, 0x001b, 0x0000, cmd_011b[i],
+ ARRAY_SIZE(cmd_011b[0]));
+ msleep(400);
+ nwait = 20;
+ for (;;) {
+ reg_r(gspca_dev, 0x031b, 1);
+ if (gspca_dev->usb_buf[0] == 0
+ || gspca_dev->usb_err != 0)
+ break;
+ if (--nwait < 0) {
+ PDEBUG(D_PROBE, "mt9v111_init timeout");
+ gspca_dev->usb_err = -ETIME;
+ return;
+ }
+ msleep(50);
+ }
+ }
+}
+
+static void global_init(struct sd *sd, int first_time)
+{
+ switch (sd->sensor) {
+ case SENSOR_ICX098BQ:
+ if (first_time)
+ ucbus_write(&sd->gspca_dev,
+ icx098bq_start_0,
+ 8, 8);
+ gpio_init(sd, sensor_tb[sd->sensor].gpio);
+ break;
+ case SENSOR_LZ24BP:
+ if (sd->type != Creative_live_motion)
+ gpio_set(sd, SQ930_GPIO_EXTRA1, 0x00ff);
+ else
+ gpio_set(sd, 0, 0x00ff);
+ msleep(50);
+ if (first_time)
+ ucbus_write(&sd->gspca_dev,
+ lz24bp_start_0,
+ 8, 8);
+ gpio_init(sd, sensor_tb[sd->sensor].gpio);
+ break;
+ case SENSOR_MI0360:
+ if (first_time)
+ ucbus_write(&sd->gspca_dev,
+ mi0360_start_0,
+ ARRAY_SIZE(mi0360_start_0),
+ 8);
+ gpio_init(sd, sensor_tb[sd->sensor].gpio);
+ gpio_set(sd, SQ930_GPIO_EXTRA2, SQ930_GPIO_EXTRA2);
+ break;
+ default:
+/* case SENSOR_MT9V111: */
+ if (first_time)
+ mt9v111_init(&sd->gspca_dev);
+ else
+ gpio_init(sd, sensor_tb[sd->sensor].gpio);
+ break;
+ }
+}
+
+static void lz24bp_ppl(struct sd *sd, u16 ppl)
+{
+ struct ucbus_write_cmd cmds[2] = {
+ {0xf810, ppl >> 8},
+ {0xf811, ppl}
+ };
+
+ ucbus_write(&sd->gspca_dev, cmds, ARRAY_SIZE(cmds), 2);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, integclks, intstartclk, frameclks, min_frclk;
+ const struct sensor_s *sensor;
+ u16 cmd;
+ u8 buf[15];
+
+ integclks = sd->expo;
+ i = 0;
+ cmd = SQ930_CTRL_SET_EXPOSURE;
+
+ switch (sd->sensor) {
+ case SENSOR_ICX098BQ: /* ccd */
+ case SENSOR_LZ24BP:
+ min_frclk = sd->sensor == SENSOR_ICX098BQ ? 0x210 : 0x26f;
+ if (integclks >= min_frclk) {
+ intstartclk = 0;
+ frameclks = integclks;
+ } else {
+ intstartclk = min_frclk - integclks;
+ frameclks = min_frclk;
+ }
+ buf[i++] = intstartclk >> 8;
+ buf[i++] = intstartclk;
+ buf[i++] = frameclks >> 8;
+ buf[i++] = frameclks;
+ buf[i++] = sd->gain;
+ break;
+ default: /* cmos */
+/* case SENSOR_MI0360: */
+/* case SENSOR_MT9V111: */
+ cmd |= 0x0100;
+ sensor = &sensor_tb[sd->sensor];
+ buf[i++] = sensor->i2c_addr; /* i2c_slave_addr */
+ buf[i++] = 0x08; /* 2 * ni2c */
+ buf[i++] = 0x09; /* reg = shutter width */
+ buf[i++] = integclks >> 8; /* val H */
+ buf[i++] = sensor->i2c_dum;
+ buf[i++] = integclks; /* val L */
+ buf[i++] = 0x35; /* reg = global gain */
+ buf[i++] = 0x00; /* val H */
+ buf[i++] = sensor->i2c_dum;
+ buf[i++] = 0x80 + sd->gain / 2; /* val L */
+ buf[i++] = 0x00;
+ buf[i++] = 0x00;
+ buf[i++] = 0x00;
+ buf[i++] = 0x00;
+ buf[i++] = 0x83;
+ break;
+ }
+ reg_wb(gspca_dev, cmd, 0, buf, i);
+}
+
+/* This function is called at probe time just before sd_init */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam = &gspca_dev->cam;
+
+ sd->sensor = id->driver_info >> 8;
+ sd->type = id->driver_info;
+
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+
+ cam->bulk = 1;
+
+ sd->gain = GAIN_DEF;
+ sd->expo = EXPO_DEF;
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->gpio[0] = sd->gpio[1] = 0xff; /* force gpio rewrite */
+
+/*fixme: is this needed for icx098bp and mi0360?
+ if (sd->sensor != SENSOR_LZ24BP)
+ reg_w(gspca_dev, SQ930_CTRL_RESET, 0x0000);
+ */
+
+ reg_r(gspca_dev, SQ930_CTRL_GET_DEV_INFO, 8);
+ if (gspca_dev->usb_err < 0)
+ return gspca_dev->usb_err;
+
+/* it returns:
+ * 03 00 12 93 0b f6 c9 00 live! ultra
+ * 03 00 07 93 0b f6 ca 00 live! ultra for notebook
+ * 03 00 12 93 0b fe c8 00 Trust WB-3500T
+ * 02 00 06 93 0b fe c8 00 Joy-IT 318S
+ * 03 00 12 93 0b f6 cf 00 icam tracer - sensor icx098bq
+ * 02 00 12 93 0b fe cf 00 ProQ Motion Webcam
+ *
+ * byte
+ * 0: 02 = usb 1.0 (12Mbit) / 03 = usb2.0 (480Mbit)
+ * 1: 00
+ * 2: 06 / 07 / 12 = mode webcam? firmware??
+ * 3: 93 chip = 930b (930b or 930c)
+ * 4: 0b
+ * 5: f6 = cdd (icx098bq, lz24bp) / fe or de = cmos (i2c) (other sensors)
+ * 6: c8 / c9 / ca / cf = mode webcam?, sensor? webcam?
+ * 7: 00
+ */
+ PDEBUG(D_PROBE, "info: %02x %02x %02x %02x %02x %02x %02x %02x",
+ gspca_dev->usb_buf[0],
+ gspca_dev->usb_buf[1],
+ gspca_dev->usb_buf[2],
+ gspca_dev->usb_buf[3],
+ gspca_dev->usb_buf[4],
+ gspca_dev->usb_buf[5],
+ gspca_dev->usb_buf[6],
+ gspca_dev->usb_buf[7]);
+
+ bridge_init(sd);
+
+ if (sd->sensor == SENSOR_MI0360) {
+
+ /* no sensor probe for icam tracer */
+ if (gspca_dev->usb_buf[5] == 0xf6) /* if ccd */
+ sd->sensor = SENSOR_ICX098BQ;
+ else
+ cmos_probe(gspca_dev);
+ }
+ if (gspca_dev->usb_err >= 0) {
+ PDEBUG(D_PROBE, "Sensor %s", sensor_tb[sd->sensor].name);
+ global_init(sd, 1);
+ }
+ return gspca_dev->usb_err;
+}
+
+/* send the start/stop commands to the webcam */
+static void send_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ const struct cap_s *cap;
+ int mode;
+
+ mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+ cap = &capconfig[sd->sensor][mode];
+ reg_wb(gspca_dev, 0x0900 | SQ930_CTRL_CAP_START,
+ 0x0a00 | cap->cc_sizeid,
+ cap->cc_bytes, 32);
+}
+
+static void send_stop(struct gspca_dev *gspca_dev)
+{
+ reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0);
+}
+
+/* function called at start time before URB creation */
+static int sd_isoc_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ gspca_dev->cam.bulk_nurbs = 1; /* there must be one URB only */
+ sd->do_ctrl = 0;
+ gspca_dev->cam.bulk_size = gspca_dev->width * gspca_dev->height + 8;
+ return 0;
+}
+
+/* start the capture */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int mode;
+
+ bridge_init(sd);
+ global_init(sd, 0);
+ msleep(100);
+
+ switch (sd->sensor) {
+ case SENSOR_ICX098BQ:
+ ucbus_write(gspca_dev, icx098bq_start_0,
+ ARRAY_SIZE(icx098bq_start_0),
+ 8);
+ ucbus_write(gspca_dev, icx098bq_start_1,
+ ARRAY_SIZE(icx098bq_start_1),
+ 5);
+ ucbus_write(gspca_dev, icx098bq_start_2,
+ ARRAY_SIZE(icx098bq_start_2),
+ 6);
+ msleep(50);
+
+ /* 1st start */
+ send_start(gspca_dev);
+ gpio_set(sd, SQ930_GPIO_EXTRA2 | SQ930_GPIO_RSTBAR, 0x00ff);
+ msleep(70);
+ reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0x0000);
+ gpio_set(sd, 0x7f, 0x00ff);
+
+ /* 2nd start */
+ send_start(gspca_dev);
+ gpio_set(sd, SQ930_GPIO_EXTRA2 | SQ930_GPIO_RSTBAR, 0x00ff);
+ goto out;
+ case SENSOR_LZ24BP:
+ ucbus_write(gspca_dev, lz24bp_start_0,
+ ARRAY_SIZE(lz24bp_start_0),
+ 8);
+ if (sd->type != Creative_live_motion)
+ ucbus_write(gspca_dev, lz24bp_start_1_gen,
+ ARRAY_SIZE(lz24bp_start_1_gen),
+ 5);
+ else
+ ucbus_write(gspca_dev, lz24bp_start_1_clm,
+ ARRAY_SIZE(lz24bp_start_1_clm),
+ 5);
+ ucbus_write(gspca_dev, lz24bp_start_2,
+ ARRAY_SIZE(lz24bp_start_2),
+ 6);
+ mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+ lz24bp_ppl(sd, mode == 1 ? 0x0564 : 0x0310);
+ msleep(10);
+ break;
+ case SENSOR_MI0360:
+ ucbus_write(gspca_dev, mi0360_start_0,
+ ARRAY_SIZE(mi0360_start_0),
+ 8);
+ i2c_write(sd, mi0360_init_23,
+ ARRAY_SIZE(mi0360_init_23));
+ i2c_write(sd, mi0360_init_24,
+ ARRAY_SIZE(mi0360_init_24));
+ i2c_write(sd, mi0360_init_25,
+ ARRAY_SIZE(mi0360_init_25));
+ ucbus_write(gspca_dev, mi0360_start_1,
+ ARRAY_SIZE(mi0360_start_1),
+ 5);
+ i2c_write(sd, mi0360_start_2,
+ ARRAY_SIZE(mi0360_start_2));
+ i2c_write(sd, mi0360_start_3,
+ ARRAY_SIZE(mi0360_start_3));
+
+ /* 1st start */
+ send_start(gspca_dev);
+ msleep(60);
+ send_stop(gspca_dev);
+
+ i2c_write(sd,
+ mi0360_start_4, ARRAY_SIZE(mi0360_start_4));
+ break;
+ default:
+/* case SENSOR_MT9V111: */
+ ucbus_write(gspca_dev, mi0360_start_0,
+ ARRAY_SIZE(mi0360_start_0),
+ 8);
+ i2c_write(sd, mt9v111_init_0,
+ ARRAY_SIZE(mt9v111_init_0));
+ i2c_write(sd, mt9v111_init_1,
+ ARRAY_SIZE(mt9v111_init_1));
+ i2c_write(sd, mt9v111_init_2,
+ ARRAY_SIZE(mt9v111_init_2));
+ ucbus_write(gspca_dev, mt9v111_start_1,
+ ARRAY_SIZE(mt9v111_start_1),
+ 5);
+ i2c_write(sd, mt9v111_init_3,
+ ARRAY_SIZE(mt9v111_init_3));
+ i2c_write(sd, mt9v111_init_4,
+ ARRAY_SIZE(mt9v111_init_4));
+ break;
+ }
+
+ send_start(gspca_dev);
+out:
+ msleep(1000);
+
+ if (sd->sensor == SENSOR_MT9V111)
+ gpio_set(sd, SQ930_GPIO_DFL_LED, SQ930_GPIO_DFL_LED);
+
+ sd->do_ctrl = 1; /* set the exposure */
+
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor == SENSOR_MT9V111)
+ gpio_set(sd, 0, SQ930_GPIO_DFL_LED);
+ send_stop(gspca_dev);
+}
+
+/* function called when the application gets a new frame */
+/* It sets the exposure if required and restart the bulk transfer. */
+static void sd_dq_callback(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
+
+ if (!sd->do_ctrl || gspca_dev->cam.bulk_nurbs != 0)
+ return;
+ sd->do_ctrl = 0;
+
+ setexposure(gspca_dev);
+
+ gspca_dev->cam.bulk_nurbs = 1;
+ ret = usb_submit_urb(gspca_dev->urb[0], GFP_ATOMIC);
+ if (ret < 0)
+ pr_err("sd_dq_callback() err %d\n", ret);
+
+ /* wait a little time, otherwise the webcam crashes */
+ msleep(100);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->do_ctrl)
+ gspca_dev->cam.bulk_nurbs = 0;
+ gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len - 8);
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+}
+
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->gain = val;
+ if (gspca_dev->streaming)
+ sd->do_ctrl = 1;
+ return 0;
+}
+
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->gain;
+ return 0;
+}
+static int sd_setexpo(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->expo = val;
+ if (gspca_dev->streaming)
+ sd->do_ctrl = 1;
+ return 0;
+}
+
+static int sd_getexpo(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->expo;
+ return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .isoc_init = sd_isoc_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+ .dq_callback = sd_dq_callback,
+};
+
+/* Table of supported USB devices */
+#define ST(sensor, type) \
+ .driver_info = (SENSOR_ ## sensor << 8) \
+ | (type)
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x041e, 0x4038), ST(MI0360, 0)},
+ {USB_DEVICE(0x041e, 0x403c), ST(LZ24BP, 0)},
+ {USB_DEVICE(0x041e, 0x403d), ST(LZ24BP, 0)},
+ {USB_DEVICE(0x041e, 0x4041), ST(LZ24BP, Creative_live_motion)},
+ {USB_DEVICE(0x2770, 0x930b), ST(MI0360, 0)},
+ {USB_DEVICE(0x2770, 0x930c), ST(MI0360, 0)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c
new file mode 100644
index 00000000..4ae7cc8f
--- /dev/null
+++ b/drivers/media/video/gspca/stk014.c
@@ -0,0 +1,522 @@
+/*
+ * Syntek DV4000 (STK014) subdriver
+ *
+ * Copyright (C) 2008 Jean-Francois Moine (http://moinejf.free.fr)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "stk014"
+
+#include "gspca.h"
+#include "jpeg.h"
+
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
+MODULE_DESCRIPTION("Syntek DV4000 (STK014) USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* controls */
+enum e_ctrl {
+ BRIGHTNESS,
+ CONTRAST,
+ COLORS,
+ LIGHTFREQ,
+ NCTRLS /* number of controls */
+};
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ struct gspca_ctrl ctrls[NCTRLS];
+
+ u8 quality;
+#define QUALITY_MIN 70
+#define QUALITY_MAX 95
+#define QUALITY_DEF 88
+
+ u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+
+/* V4L2 controls supported by the driver */
+static void setbrightness(struct gspca_dev *gspca_dev);
+static void setcontrast(struct gspca_dev *gspca_dev);
+static void setcolors(struct gspca_dev *gspca_dev);
+static void setlightfreq(struct gspca_dev *gspca_dev);
+
+static const struct ctrl sd_ctrls[NCTRLS] = {
+[BRIGHTNESS] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 127,
+ },
+ .set_control = setbrightness
+ },
+[CONTRAST] = {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 127,
+ },
+ .set_control = setcontrast
+ },
+[COLORS] = {
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Color",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 127,
+ },
+ .set_control = setcolors
+ },
+[LIGHTFREQ] = {
+ {
+ .id = V4L2_CID_POWER_LINE_FREQUENCY,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Light frequency filter",
+ .minimum = 1,
+ .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */
+ .step = 1,
+ .default_value = 1,
+ },
+ .set_control = setlightfreq
+ },
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
+/* -- read a register -- */
+static u8 reg_r(struct gspca_dev *gspca_dev,
+ __u16 index)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return 0;
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ 0x00,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x00,
+ index,
+ gspca_dev->usb_buf, 1,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_r err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ return 0;
+ }
+ return gspca_dev->usb_buf[0];
+}
+
+/* -- write a register -- */
+static void reg_w(struct gspca_dev *gspca_dev,
+ __u16 index, __u16 value)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x01,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value,
+ index,
+ NULL,
+ 0,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_w err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+/* -- get a bulk value (4 bytes) -- */
+static void rcv_val(struct gspca_dev *gspca_dev,
+ int ads)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int alen, ret;
+
+ reg_w(gspca_dev, 0x634, (ads >> 16) & 0xff);
+ reg_w(gspca_dev, 0x635, (ads >> 8) & 0xff);
+ reg_w(gspca_dev, 0x636, ads & 0xff);
+ reg_w(gspca_dev, 0x637, 0);
+ reg_w(gspca_dev, 0x638, 4); /* len & 0xff */
+ reg_w(gspca_dev, 0x639, 0); /* len >> 8 */
+ reg_w(gspca_dev, 0x63a, 0);
+ reg_w(gspca_dev, 0x63b, 0);
+ reg_w(gspca_dev, 0x630, 5);
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_bulk_msg(dev,
+ usb_rcvbulkpipe(dev, 0x05),
+ gspca_dev->usb_buf,
+ 4, /* length */
+ &alen,
+ 500); /* timeout in milliseconds */
+ if (ret < 0) {
+ pr_err("rcv_val err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+/* -- send a bulk value -- */
+static void snd_val(struct gspca_dev *gspca_dev,
+ int ads,
+ unsigned int val)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int alen, ret;
+ __u8 seq = 0;
+
+ if (ads == 0x003f08) {
+ reg_r(gspca_dev, 0x0704);
+ seq = reg_r(gspca_dev, 0x0705);
+ reg_r(gspca_dev, 0x0650);
+ reg_w(gspca_dev, 0x654, seq);
+ } else {
+ reg_w(gspca_dev, 0x654, (ads >> 16) & 0xff);
+ }
+ reg_w(gspca_dev, 0x655, (ads >> 8) & 0xff);
+ reg_w(gspca_dev, 0x656, ads & 0xff);
+ reg_w(gspca_dev, 0x657, 0);
+ reg_w(gspca_dev, 0x658, 0x04); /* size */
+ reg_w(gspca_dev, 0x659, 0);
+ reg_w(gspca_dev, 0x65a, 0);
+ reg_w(gspca_dev, 0x65b, 0);
+ reg_w(gspca_dev, 0x650, 5);
+ if (gspca_dev->usb_err < 0)
+ return;
+ gspca_dev->usb_buf[0] = val >> 24;
+ gspca_dev->usb_buf[1] = val >> 16;
+ gspca_dev->usb_buf[2] = val >> 8;
+ gspca_dev->usb_buf[3] = val;
+ ret = usb_bulk_msg(dev,
+ usb_sndbulkpipe(dev, 6),
+ gspca_dev->usb_buf,
+ 4,
+ &alen,
+ 500); /* timeout in milliseconds */
+ if (ret < 0) {
+ pr_err("snd_val err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ } else {
+ if (ads == 0x003f08) {
+ seq += 4;
+ seq &= 0x3f;
+ reg_w(gspca_dev, 0x705, seq);
+ }
+ }
+}
+
+/* set a camera parameter */
+static void set_par(struct gspca_dev *gspca_dev,
+ int parval)
+{
+ snd_val(gspca_dev, 0x003f08, parval);
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int parval;
+
+ parval = 0x06000000 /* whiteness */
+ + (sd->ctrls[BRIGHTNESS].val << 16);
+ set_par(gspca_dev, parval);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int parval;
+
+ parval = 0x07000000 /* contrast */
+ + (sd->ctrls[CONTRAST].val << 16);
+ set_par(gspca_dev, parval);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int parval;
+
+ parval = 0x08000000 /* saturation */
+ + (sd->ctrls[COLORS].val << 16);
+ set_par(gspca_dev, parval);
+}
+
+static void setlightfreq(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ set_par(gspca_dev, sd->ctrls[LIGHTFREQ].val == 1
+ ? 0x33640000 /* 50 Hz */
+ : 0x33780000); /* 60 Hz */
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ gspca_dev->cam.cam_mode = vga_mode;
+ gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
+ gspca_dev->cam.ctrls = sd->ctrls;
+ sd->quality = QUALITY_DEF;
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ u8 ret;
+
+ /* check if the device responds */
+ usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1);
+ ret = reg_r(gspca_dev, 0x0740);
+ if (gspca_dev->usb_err >= 0) {
+ if (ret != 0xff) {
+ pr_err("init reg: 0x%02x\n", ret);
+ gspca_dev->usb_err = -EIO;
+ }
+ }
+ return gspca_dev->usb_err;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret, value;
+
+ /* create the JPEG header */
+ jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
+ 0x22); /* JPEG 411 */
+ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+
+ /* work on alternate 1 */
+ usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1);
+
+ set_par(gspca_dev, 0x10000000);
+ set_par(gspca_dev, 0x00000000);
+ set_par(gspca_dev, 0x8002e001);
+ set_par(gspca_dev, 0x14000000);
+ if (gspca_dev->width > 320)
+ value = 0x8002e001; /* 640x480 */
+ else
+ value = 0x4001f000; /* 320x240 */
+ set_par(gspca_dev, value);
+ ret = usb_set_interface(gspca_dev->dev,
+ gspca_dev->iface,
+ gspca_dev->alt);
+ if (ret < 0) {
+ pr_err("set intf %d %d failed\n",
+ gspca_dev->iface, gspca_dev->alt);
+ gspca_dev->usb_err = ret;
+ goto out;
+ }
+ reg_r(gspca_dev, 0x0630);
+ rcv_val(gspca_dev, 0x000020); /* << (value ff ff ff ff) */
+ reg_r(gspca_dev, 0x0650);
+ snd_val(gspca_dev, 0x000020, 0xffffffff);
+ reg_w(gspca_dev, 0x0620, 0);
+ reg_w(gspca_dev, 0x0630, 0);
+ reg_w(gspca_dev, 0x0640, 0);
+ reg_w(gspca_dev, 0x0650, 0);
+ reg_w(gspca_dev, 0x0660, 0);
+ setbrightness(gspca_dev); /* whiteness */
+ setcontrast(gspca_dev); /* contrast */
+ setcolors(gspca_dev); /* saturation */
+ set_par(gspca_dev, 0x09800000); /* Red ? */
+ set_par(gspca_dev, 0x0a800000); /* Green ? */
+ set_par(gspca_dev, 0x0b800000); /* Blue ? */
+ set_par(gspca_dev, 0x0d030000); /* Gamma ? */
+ setlightfreq(gspca_dev);
+
+ /* start the video flow */
+ set_par(gspca_dev, 0x01000000);
+ set_par(gspca_dev, 0x01000000);
+ if (gspca_dev->usb_err >= 0)
+ PDEBUG(D_STREAM, "camera started alt: 0x%02x",
+ gspca_dev->alt);
+out:
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct usb_device *dev = gspca_dev->dev;
+
+ set_par(gspca_dev, 0x02000000);
+ set_par(gspca_dev, 0x02000000);
+ usb_set_interface(dev, gspca_dev->iface, 1);
+ reg_r(gspca_dev, 0x0630);
+ rcv_val(gspca_dev, 0x000020); /* << (value ff ff ff ff) */
+ reg_r(gspca_dev, 0x0650);
+ snd_val(gspca_dev, 0x000020, 0xffffffff);
+ reg_w(gspca_dev, 0x0620, 0);
+ reg_w(gspca_dev, 0x0630, 0);
+ reg_w(gspca_dev, 0x0640, 0);
+ reg_w(gspca_dev, 0x0650, 0);
+ reg_w(gspca_dev, 0x0660, 0);
+ PDEBUG(D_STREAM, "camera stopped");
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ static unsigned char ffd9[] = {0xff, 0xd9};
+
+ /* a frame starts with:
+ * - 0xff 0xfe
+ * - 0x08 0x00 - length (little endian ?!)
+ * - 4 bytes = size of whole frame (BE - including header)
+ * - 0x00 0x0c
+ * - 0xff 0xd8
+ * - .. JPEG image with escape sequences (ff 00)
+ * (without ending - ff d9)
+ */
+ if (data[0] == 0xff && data[1] == 0xfe) {
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ ffd9, 2);
+
+ /* put the JPEG 411 header */
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+
+ /* beginning of the frame */
+#define STKHDRSZ 12
+ data += STKHDRSZ;
+ len -= STKHDRSZ;
+ }
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static int sd_querymenu(struct gspca_dev *gspca_dev,
+ struct v4l2_querymenu *menu)
+{
+ static const char *freq_nm[3] = {"NoFliker", "50 Hz", "60 Hz"};
+
+ switch (menu->id) {
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ if ((unsigned) menu->index >= ARRAY_SIZE(freq_nm))
+ break;
+ strcpy((char *) menu->name, freq_nm[menu->index]);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int sd_set_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (jcomp->quality < QUALITY_MIN)
+ sd->quality = QUALITY_MIN;
+ else if (jcomp->quality > QUALITY_MAX)
+ sd->quality = QUALITY_MAX;
+ else
+ sd->quality = jcomp->quality;
+ if (gspca_dev->streaming)
+ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+ return gspca_dev->usb_err;
+}
+
+static int sd_get_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ memset(jcomp, 0, sizeof *jcomp);
+ jcomp->quality = sd->quality;
+ jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
+ | V4L2_JPEG_MARKER_DQT;
+ return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = NCTRLS,
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+ .querymenu = sd_querymenu,
+ .get_jcomp = sd_get_jcomp,
+ .set_jcomp = sd_set_jcomp,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x05e1, 0x0893)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/stv0680.c b/drivers/media/video/gspca/stv0680.c
new file mode 100644
index 00000000..461ed645
--- /dev/null
+++ b/drivers/media/video/gspca/stv0680.c
@@ -0,0 +1,358 @@
+/*
+ * STV0680 USB Camera Driver
+ *
+ * Copyright (C) 2009 Hans de Goede <hdegoede@redhat.com>
+ *
+ * This module is adapted from the in kernel v4l1 stv680 driver:
+ *
+ * STV0680 USB Camera Driver, by Kevin Sisson (kjsisson@bellsouth.net)
+ *
+ * Thanks to STMicroelectronics for information on the usb commands, and
+ * to Steve Miller at STM for his help and encouragement while I was
+ * writing this driver.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "stv0680"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("STV0680 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ struct v4l2_pix_format mode;
+ u8 orig_mode;
+ u8 video_mode;
+ u8 current_mode;
+};
+
+/* V4L2 controls supported by the driver */
+static const struct ctrl sd_ctrls[] = {
+};
+
+static int stv_sndctrl(struct gspca_dev *gspca_dev, int set, u8 req, u16 val,
+ int size)
+{
+ int ret = -1;
+ u8 req_type = 0;
+ unsigned int pipe = 0;
+
+ switch (set) {
+ case 0: /* 0xc1 */
+ req_type = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT;
+ pipe = usb_rcvctrlpipe(gspca_dev->dev, 0);
+ break;
+ case 1: /* 0x41 */
+ req_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT;
+ pipe = usb_sndctrlpipe(gspca_dev->dev, 0);
+ break;
+ case 2: /* 0x80 */
+ req_type = USB_DIR_IN | USB_RECIP_DEVICE;
+ pipe = usb_rcvctrlpipe(gspca_dev->dev, 0);
+ break;
+ case 3: /* 0x40 */
+ req_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+ pipe = usb_sndctrlpipe(gspca_dev->dev, 0);
+ break;
+ }
+
+ ret = usb_control_msg(gspca_dev->dev, pipe,
+ req, req_type,
+ val, 0, gspca_dev->usb_buf, size, 500);
+
+ if ((ret < 0) && (req != 0x0a))
+ pr_err("usb_control_msg error %i, request = 0x%x, error = %i\n",
+ set, req, ret);
+
+ return ret;
+}
+
+static int stv0680_handle_error(struct gspca_dev *gspca_dev, int ret)
+{
+ stv_sndctrl(gspca_dev, 0, 0x80, 0, 0x02); /* Get Last Error */
+ PDEBUG(D_ERR, "last error: %i, command = 0x%x",
+ gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]);
+ return ret;
+}
+
+static int stv0680_get_video_mode(struct gspca_dev *gspca_dev)
+{
+ /* Note not sure if this init of usb_buf is really necessary */
+ memset(gspca_dev->usb_buf, 0, 8);
+ gspca_dev->usb_buf[0] = 0x0f;
+
+ if (stv_sndctrl(gspca_dev, 0, 0x87, 0, 0x08) != 0x08) {
+ PDEBUG(D_ERR, "Get_Camera_Mode failed");
+ return stv0680_handle_error(gspca_dev, -EIO);
+ }
+
+ return gspca_dev->usb_buf[0]; /* 01 = VGA, 03 = QVGA, 00 = CIF */
+}
+
+static int stv0680_set_video_mode(struct gspca_dev *gspca_dev, u8 mode)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->current_mode == mode)
+ return 0;
+
+ memset(gspca_dev->usb_buf, 0, 8);
+ gspca_dev->usb_buf[0] = mode;
+
+ if (stv_sndctrl(gspca_dev, 3, 0x07, 0x0100, 0x08) != 0x08) {
+ PDEBUG(D_ERR, "Set_Camera_Mode failed");
+ return stv0680_handle_error(gspca_dev, -EIO);
+ }
+
+ /* Verify we got what we've asked for */
+ if (stv0680_get_video_mode(gspca_dev) != mode) {
+ PDEBUG(D_ERR, "Error setting camera video mode!");
+ return -EIO;
+ }
+
+ sd->current_mode = mode;
+
+ return 0;
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ int ret;
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam = &gspca_dev->cam;
+
+ /* Give the camera some time to settle, otherwise initalization will
+ fail on hotplug, and yes it really needs a full second. */
+ msleep(1000);
+
+ /* ping camera to be sure STV0680 is present */
+ if (stv_sndctrl(gspca_dev, 0, 0x88, 0x5678, 0x02) != 0x02 ||
+ gspca_dev->usb_buf[0] != 0x56 || gspca_dev->usb_buf[1] != 0x78) {
+ PDEBUG(D_ERR, "STV(e): camera ping failed!!");
+ return stv0680_handle_error(gspca_dev, -ENODEV);
+ }
+
+ /* get camera descriptor */
+ if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0200, 0x09) != 0x09)
+ return stv0680_handle_error(gspca_dev, -ENODEV);
+
+ if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0200, 0x22) != 0x22 ||
+ gspca_dev->usb_buf[7] != 0xa0 || gspca_dev->usb_buf[8] != 0x23) {
+ PDEBUG(D_ERR, "Could not get descriptor 0200.");
+ return stv0680_handle_error(gspca_dev, -ENODEV);
+ }
+ if (stv_sndctrl(gspca_dev, 0, 0x8a, 0, 0x02) != 0x02)
+ return stv0680_handle_error(gspca_dev, -ENODEV);
+ if (stv_sndctrl(gspca_dev, 0, 0x8b, 0, 0x24) != 0x24)
+ return stv0680_handle_error(gspca_dev, -ENODEV);
+ if (stv_sndctrl(gspca_dev, 0, 0x85, 0, 0x10) != 0x10)
+ return stv0680_handle_error(gspca_dev, -ENODEV);
+
+ if (!(gspca_dev->usb_buf[7] & 0x09)) {
+ PDEBUG(D_ERR, "Camera supports neither CIF nor QVGA mode");
+ return -ENODEV;
+ }
+ if (gspca_dev->usb_buf[7] & 0x01)
+ PDEBUG(D_PROBE, "Camera supports CIF mode");
+ if (gspca_dev->usb_buf[7] & 0x02)
+ PDEBUG(D_PROBE, "Camera supports VGA mode");
+ if (gspca_dev->usb_buf[7] & 0x04)
+ PDEBUG(D_PROBE, "Camera supports QCIF mode");
+ if (gspca_dev->usb_buf[7] & 0x08)
+ PDEBUG(D_PROBE, "Camera supports QVGA mode");
+
+ if (gspca_dev->usb_buf[7] & 0x01)
+ sd->video_mode = 0x00; /* CIF */
+ else
+ sd->video_mode = 0x03; /* QVGA */
+
+ /* FW rev, ASIC rev, sensor ID */
+ PDEBUG(D_PROBE, "Firmware rev is %i.%i",
+ gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]);
+ PDEBUG(D_PROBE, "ASIC rev is %i.%i",
+ gspca_dev->usb_buf[2], gspca_dev->usb_buf[3]);
+ PDEBUG(D_PROBE, "Sensor ID is %i",
+ (gspca_dev->usb_buf[4]*16) + (gspca_dev->usb_buf[5]>>4));
+
+
+ ret = stv0680_get_video_mode(gspca_dev);
+ if (ret < 0)
+ return ret;
+ sd->current_mode = sd->orig_mode = ret;
+
+ ret = stv0680_set_video_mode(gspca_dev, sd->video_mode);
+ if (ret < 0)
+ return ret;
+
+ /* Get mode details */
+ if (stv_sndctrl(gspca_dev, 0, 0x8f, 0, 0x10) != 0x10)
+ return stv0680_handle_error(gspca_dev, -EIO);
+
+ cam->bulk = 1;
+ cam->bulk_nurbs = 1; /* The cam cannot handle more */
+ cam->bulk_size = (gspca_dev->usb_buf[0] << 24) |
+ (gspca_dev->usb_buf[1] << 16) |
+ (gspca_dev->usb_buf[2] << 8) |
+ (gspca_dev->usb_buf[3]);
+ sd->mode.width = (gspca_dev->usb_buf[4] << 8) |
+ (gspca_dev->usb_buf[5]); /* 322, 356, 644 */
+ sd->mode.height = (gspca_dev->usb_buf[6] << 8) |
+ (gspca_dev->usb_buf[7]); /* 242, 292, 484 */
+ sd->mode.pixelformat = V4L2_PIX_FMT_STV0680;
+ sd->mode.field = V4L2_FIELD_NONE;
+ sd->mode.bytesperline = sd->mode.width;
+ sd->mode.sizeimage = cam->bulk_size;
+ sd->mode.colorspace = V4L2_COLORSPACE_SRGB;
+
+ /* origGain = gspca_dev->usb_buf[12]; */
+
+ cam->cam_mode = &sd->mode;
+ cam->nmodes = 1;
+
+
+ ret = stv0680_set_video_mode(gspca_dev, sd->orig_mode);
+ if (ret < 0)
+ return ret;
+
+ if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0100, 0x12) != 0x12 ||
+ gspca_dev->usb_buf[8] != 0x53 || gspca_dev->usb_buf[9] != 0x05) {
+ pr_err("Could not get descriptor 0100\n");
+ return stv0680_handle_error(gspca_dev, -EIO);
+ }
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ return 0;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ int ret;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ ret = stv0680_set_video_mode(gspca_dev, sd->video_mode);
+ if (ret < 0)
+ return ret;
+
+ if (stv_sndctrl(gspca_dev, 0, 0x85, 0, 0x10) != 0x10)
+ return stv0680_handle_error(gspca_dev, -EIO);
+
+ /* Start stream at:
+ 0x0000 = CIF (352x288)
+ 0x0100 = VGA (640x480)
+ 0x0300 = QVGA (320x240) */
+ if (stv_sndctrl(gspca_dev, 1, 0x09, sd->video_mode << 8, 0x0) != 0x0)
+ return stv0680_handle_error(gspca_dev, -EIO);
+
+ return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ /* This is a high priority command; it stops all lower order cmds */
+ if (stv_sndctrl(gspca_dev, 1, 0x04, 0x0000, 0x0) != 0x0)
+ stv0680_handle_error(gspca_dev, -EIO);
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (!sd->gspca_dev.present)
+ return;
+
+ stv0680_set_video_mode(gspca_dev, sd->orig_mode);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data,
+ int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* Every now and then the camera sends a 16 byte packet, no idea
+ what it contains, but it is not image data, when this
+ happens the frame received before this packet is corrupt,
+ so discard it. */
+ if (len != sd->mode.sizeimage) {
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
+ }
+
+ /* Finish the previous frame, we do this upon reception of the next
+ packet, even though it is already complete so that the strange 16
+ byte packets send after a corrupt frame can discard it. */
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+
+ /* Store the just received frame */
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x0553, 0x0202)},
+ {USB_DEVICE(0x041e, 0x4007)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/stv06xx/Kconfig b/drivers/media/video/gspca/stv06xx/Kconfig
new file mode 100644
index 00000000..634ad38d
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/Kconfig
@@ -0,0 +1,9 @@
+config USB_STV06XX
+ tristate "STV06XX USB Camera Driver"
+ depends on USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on
+ the ST STV06XX chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_stv06xx.
diff --git a/drivers/media/video/gspca/stv06xx/Makefile b/drivers/media/video/gspca/stv06xx/Makefile
new file mode 100644
index 00000000..38bc4106
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/Makefile
@@ -0,0 +1,10 @@
+obj-$(CONFIG_USB_STV06XX) += gspca_stv06xx.o
+
+gspca_stv06xx-objs := stv06xx.o \
+ stv06xx_vv6410.o \
+ stv06xx_hdcs.o \
+ stv06xx_pb0100.o \
+ stv06xx_st6422.o
+
+ccflags-y += -I$(srctree)/drivers/media/video/gspca
+
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c
new file mode 100644
index 00000000..91d99b4c
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx.c
@@ -0,0 +1,621 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/input.h>
+#include "stv06xx_sensor.h"
+
+MODULE_AUTHOR("Erik Andrén");
+MODULE_DESCRIPTION("STV06XX USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+static bool dump_bridge;
+static bool dump_sensor;
+
+int stv06xx_write_bridge(struct sd *sd, u16 address, u16 i2c_data)
+{
+ int err;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+ u8 len = (i2c_data > 0xff) ? 2 : 1;
+
+ buf[0] = i2c_data & 0xff;
+ buf[1] = (i2c_data >> 8) & 0xff;
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, address, 0, buf, len,
+ STV06XX_URB_MSG_TIMEOUT);
+
+ PDEBUG(D_CONF, "Written 0x%x to address 0x%x, status: %d",
+ i2c_data, address, err);
+
+ return (err < 0) ? err : 0;
+}
+
+int stv06xx_read_bridge(struct sd *sd, u16 address, u8 *i2c_data)
+{
+ int err;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ 0x04, 0xc0, address, 0, buf, 1,
+ STV06XX_URB_MSG_TIMEOUT);
+
+ *i2c_data = buf[0];
+
+ PDEBUG(D_CONF, "Reading 0x%x from address 0x%x, status %d",
+ *i2c_data, address, err);
+
+ return (err < 0) ? err : 0;
+}
+
+/* Wraps the normal write sensor bytes / words functions for writing a
+ single value */
+int stv06xx_write_sensor(struct sd *sd, u8 address, u16 value)
+{
+ if (sd->sensor->i2c_len == 2) {
+ u16 data[2] = { address, value };
+ return stv06xx_write_sensor_words(sd, data, 1);
+ } else {
+ u8 data[2] = { address, value };
+ return stv06xx_write_sensor_bytes(sd, data, 1);
+ }
+}
+
+static int stv06xx_write_sensor_finish(struct sd *sd)
+{
+ int err = 0;
+
+ if (sd->bridge == BRIDGE_STV610) {
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ buf[0] = 0;
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x1704, 0, buf, 1,
+ STV06XX_URB_MSG_TIMEOUT);
+ }
+
+ return (err < 0) ? err : 0;
+}
+
+int stv06xx_write_sensor_bytes(struct sd *sd, const u8 *data, u8 len)
+{
+ int err, i, j;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ PDEBUG(D_CONF, "I2C: Command buffer contains %d entries", len);
+ for (i = 0; i < len;) {
+ /* Build the command buffer */
+ memset(buf, 0, I2C_BUFFER_LENGTH);
+ for (j = 0; j < I2C_MAX_BYTES && i < len; j++, i++) {
+ buf[j] = data[2*i];
+ buf[0x10 + j] = data[2*i+1];
+ PDEBUG(D_CONF, "I2C: Writing 0x%02x to reg 0x%02x",
+ data[2*i+1], data[2*i]);
+ }
+ buf[0x20] = sd->sensor->i2c_addr;
+ buf[0x21] = j - 1; /* Number of commands to send - 1 */
+ buf[0x22] = I2C_WRITE_CMD;
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x0400, 0, buf,
+ I2C_BUFFER_LENGTH,
+ STV06XX_URB_MSG_TIMEOUT);
+ if (err < 0)
+ return err;
+ }
+ return stv06xx_write_sensor_finish(sd);
+}
+
+int stv06xx_write_sensor_words(struct sd *sd, const u16 *data, u8 len)
+{
+ int err, i, j;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ PDEBUG(D_CONF, "I2C: Command buffer contains %d entries", len);
+
+ for (i = 0; i < len;) {
+ /* Build the command buffer */
+ memset(buf, 0, I2C_BUFFER_LENGTH);
+ for (j = 0; j < I2C_MAX_WORDS && i < len; j++, i++) {
+ buf[j] = data[2*i];
+ buf[0x10 + j * 2] = data[2*i+1];
+ buf[0x10 + j * 2 + 1] = data[2*i+1] >> 8;
+ PDEBUG(D_CONF, "I2C: Writing 0x%04x to reg 0x%02x",
+ data[2*i+1], data[2*i]);
+ }
+ buf[0x20] = sd->sensor->i2c_addr;
+ buf[0x21] = j - 1; /* Number of commands to send - 1 */
+ buf[0x22] = I2C_WRITE_CMD;
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x0400, 0, buf,
+ I2C_BUFFER_LENGTH,
+ STV06XX_URB_MSG_TIMEOUT);
+ if (err < 0)
+ return err;
+ }
+ return stv06xx_write_sensor_finish(sd);
+}
+
+int stv06xx_read_sensor(struct sd *sd, const u8 address, u16 *value)
+{
+ int err;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ err = stv06xx_write_bridge(sd, STV_I2C_FLUSH, sd->sensor->i2c_flush);
+ if (err < 0)
+ return err;
+
+ /* Clear mem */
+ memset(buf, 0, I2C_BUFFER_LENGTH);
+
+ buf[0] = address;
+ buf[0x20] = sd->sensor->i2c_addr;
+ buf[0x21] = 0;
+
+ /* Read I2C register */
+ buf[0x22] = I2C_READ_CMD;
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x1400, 0, buf, I2C_BUFFER_LENGTH,
+ STV06XX_URB_MSG_TIMEOUT);
+ if (err < 0) {
+ pr_err("I2C: Read error writing address: %d\n", err);
+ return err;
+ }
+
+ err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ 0x04, 0xc0, 0x1410, 0, buf, sd->sensor->i2c_len,
+ STV06XX_URB_MSG_TIMEOUT);
+ if (sd->sensor->i2c_len == 2)
+ *value = buf[0] | (buf[1] << 8);
+ else
+ *value = buf[0];
+
+ PDEBUG(D_CONF, "I2C: Read 0x%x from address 0x%x, status: %d",
+ *value, address, err);
+
+ return (err < 0) ? err : 0;
+}
+
+/* Dumps all bridge registers */
+static void stv06xx_dump_bridge(struct sd *sd)
+{
+ int i;
+ u8 data, buf;
+
+ pr_info("Dumping all stv06xx bridge registers\n");
+ for (i = 0x1400; i < 0x160f; i++) {
+ stv06xx_read_bridge(sd, i, &data);
+
+ pr_info("Read 0x%x from address 0x%x\n", data, i);
+ }
+
+ pr_info("Testing stv06xx bridge registers for writability\n");
+ for (i = 0x1400; i < 0x160f; i++) {
+ stv06xx_read_bridge(sd, i, &data);
+ buf = data;
+
+ stv06xx_write_bridge(sd, i, 0xff);
+ stv06xx_read_bridge(sd, i, &data);
+ if (data == 0xff)
+ pr_info("Register 0x%x is read/write\n", i);
+ else if (data != buf)
+ pr_info("Register 0x%x is read/write, but only partially\n",
+ i);
+ else
+ pr_info("Register 0x%x is read-only\n", i);
+
+ stv06xx_write_bridge(sd, i, buf);
+ }
+}
+
+/* this function is called at probe and resume time */
+static int stv06xx_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int err;
+
+ PDEBUG(D_PROBE, "Initializing camera");
+
+ /* Let the usb init settle for a bit
+ before performing the initialization */
+ msleep(250);
+
+ err = sd->sensor->init(sd);
+
+ if (dump_sensor && sd->sensor->dump)
+ sd->sensor->dump(sd);
+
+ return (err < 0) ? err : 0;
+}
+
+/* Start the camera */
+static int stv06xx_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct usb_host_interface *alt;
+ struct usb_interface *intf;
+ int err, packet_size;
+
+ intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
+ alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
+ if (!alt) {
+ PDEBUG(D_ERR, "Couldn't get altsetting");
+ return -EIO;
+ }
+
+ packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+ err = stv06xx_write_bridge(sd, STV_ISO_SIZE_L, packet_size);
+ if (err < 0)
+ return err;
+
+ /* Prepare the sensor for start */
+ err = sd->sensor->start(sd);
+ if (err < 0)
+ goto out;
+
+ /* Start isochronous streaming */
+ err = stv06xx_write_bridge(sd, STV_ISO_ENABLE, 1);
+
+out:
+ if (err < 0)
+ PDEBUG(D_STREAM, "Starting stream failed");
+ else
+ PDEBUG(D_STREAM, "Started streaming");
+
+ return (err < 0) ? err : 0;
+}
+
+static int stv06xx_isoc_init(struct gspca_dev *gspca_dev)
+{
+ struct usb_host_interface *alt;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* Start isoc bandwidth "negotiation" at max isoc bandwidth */
+ alt = &gspca_dev->dev->actconfig->intf_cache[0]->altsetting[1];
+ alt->endpoint[0].desc.wMaxPacketSize =
+ cpu_to_le16(sd->sensor->max_packet_size[gspca_dev->curr_mode]);
+
+ return 0;
+}
+
+static int stv06xx_isoc_nego(struct gspca_dev *gspca_dev)
+{
+ int ret, packet_size, min_packet_size;
+ struct usb_host_interface *alt;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ alt = &gspca_dev->dev->actconfig->intf_cache[0]->altsetting[1];
+ packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+ min_packet_size = sd->sensor->min_packet_size[gspca_dev->curr_mode];
+ if (packet_size <= min_packet_size)
+ return -EIO;
+
+ packet_size -= 100;
+ if (packet_size < min_packet_size)
+ packet_size = min_packet_size;
+ alt->endpoint[0].desc.wMaxPacketSize = cpu_to_le16(packet_size);
+
+ ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1);
+ if (ret < 0)
+ PDEBUG(D_ERR|D_STREAM, "set alt 1 err %d", ret);
+
+ return ret;
+}
+
+static void stv06xx_stopN(struct gspca_dev *gspca_dev)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* stop ISO-streaming */
+ err = stv06xx_write_bridge(sd, STV_ISO_ENABLE, 0);
+ if (err < 0)
+ goto out;
+
+ err = sd->sensor->stop(sd);
+
+out:
+ if (err < 0)
+ PDEBUG(D_STREAM, "Failed to stop stream");
+ else
+ PDEBUG(D_STREAM, "Stopped streaming");
+}
+
+/*
+ * Analyse an USB packet of the data stream and store it appropriately.
+ * Each packet contains an integral number of chunks. Each chunk has
+ * 2-bytes identification, followed by 2-bytes that describe the chunk
+ * length. Known/guessed chunk identifications are:
+ * 8001/8005/C001/C005 - Begin new frame
+ * 8002/8006/C002/C006 - End frame
+ * 0200/4200 - Contains actual image data, bayer or compressed
+ * 0005 - 11 bytes of unknown data
+ * 0100 - 2 bytes of unknown data
+ * The 0005 and 0100 chunks seem to appear only in compressed stream.
+ */
+static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(D_PACK, "Packet of length %d arrived", len);
+
+ /* A packet may contain several frames
+ loop until the whole packet is reached */
+ while (len) {
+ int id, chunk_len;
+
+ if (len < 4) {
+ PDEBUG(D_PACK, "Packet is smaller than 4 bytes");
+ return;
+ }
+
+ /* Capture the id */
+ id = (data[0] << 8) | data[1];
+
+ /* Capture the chunk length */
+ chunk_len = (data[2] << 8) | data[3];
+ PDEBUG(D_PACK, "Chunk id: %x, length: %d", id, chunk_len);
+
+ data += 4;
+ len -= 4;
+
+ if (len < chunk_len) {
+ PDEBUG(D_ERR, "URB packet length is smaller"
+ " than the specified chunk length");
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
+ }
+
+ /* First byte seem to be 02=data 2nd byte is unknown??? */
+ if (sd->bridge == BRIDGE_ST6422 && (id & 0xff00) == 0x0200)
+ goto frame_data;
+
+ switch (id) {
+ case 0x0200:
+ case 0x4200:
+frame_data:
+ PDEBUG(D_PACK, "Frame data packet detected");
+
+ if (sd->to_skip) {
+ int skip = (sd->to_skip < chunk_len) ?
+ sd->to_skip : chunk_len;
+ data += skip;
+ len -= skip;
+ chunk_len -= skip;
+ sd->to_skip -= skip;
+ }
+
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data, chunk_len);
+ break;
+
+ case 0x8001:
+ case 0x8005:
+ case 0xc001:
+ case 0xc005:
+ PDEBUG(D_PACK, "Starting new frame");
+
+ /* Create a new frame, chunk length should be zero */
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ NULL, 0);
+
+ if (sd->bridge == BRIDGE_ST6422)
+ sd->to_skip = gspca_dev->width * 4;
+
+ if (chunk_len)
+ PDEBUG(D_ERR, "Chunk length is "
+ "non-zero on a SOF");
+ break;
+
+ case 0x8002:
+ case 0x8006:
+ case 0xc002:
+ PDEBUG(D_PACK, "End of frame detected");
+
+ /* Complete the last frame (if any) */
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ NULL, 0);
+
+ if (chunk_len)
+ PDEBUG(D_ERR, "Chunk length is "
+ "non-zero on a EOF");
+ break;
+
+ case 0x0005:
+ PDEBUG(D_PACK, "Chunk 0x005 detected");
+ /* Unknown chunk with 11 bytes of data,
+ occurs just before end of each frame
+ in compressed mode */
+ break;
+
+ case 0x0100:
+ PDEBUG(D_PACK, "Chunk 0x0100 detected");
+ /* Unknown chunk with 2 bytes of data,
+ occurs 2-3 times per USB interrupt */
+ break;
+ case 0x42ff:
+ PDEBUG(D_PACK, "Chunk 0x42ff detected");
+ /* Special chunk seen sometimes on the ST6422 */
+ break;
+ default:
+ PDEBUG(D_PACK, "Unknown chunk 0x%04x detected", id);
+ /* Unknown chunk */
+ }
+ data += chunk_len;
+ len -= chunk_len;
+ }
+}
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* interrupt packet data */
+ int len) /* interrupt packet length */
+{
+ int ret = -EINVAL;
+
+ if (len == 1 && data[0] == 0x80) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+ input_sync(gspca_dev->input_dev);
+ ret = 0;
+ }
+
+ if (len == 1 && data[0] == 0x88) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ ret = 0;
+ }
+
+ return ret;
+}
+#endif
+
+static int stv06xx_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id);
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .config = stv06xx_config,
+ .init = stv06xx_init,
+ .start = stv06xx_start,
+ .stopN = stv06xx_stopN,
+ .pkt_scan = stv06xx_pkt_scan,
+ .isoc_init = stv06xx_isoc_init,
+ .isoc_nego = stv06xx_isoc_nego,
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ .int_pkt_scan = sd_int_pkt_scan,
+#endif
+};
+
+/* This function is called at probe time */
+static int stv06xx_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(D_PROBE, "Configuring camera");
+
+ sd->desc = sd_desc;
+ sd->bridge = id->driver_info;
+ gspca_dev->sd_desc = &sd->desc;
+
+ if (dump_bridge)
+ stv06xx_dump_bridge(sd);
+
+ sd->sensor = &stv06xx_sensor_st6422;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ sd->sensor = &stv06xx_sensor_vv6410;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ sd->sensor = &stv06xx_sensor_hdcs1x00;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ sd->sensor = &stv06xx_sensor_hdcs1020;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ sd->sensor = &stv06xx_sensor_pb0100;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ sd->sensor = NULL;
+ return -ENODEV;
+}
+
+
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ /* QuickCam Express */
+ {USB_DEVICE(0x046d, 0x0840), .driver_info = BRIDGE_STV600 },
+ /* LEGO cam / QuickCam Web */
+ {USB_DEVICE(0x046d, 0x0850), .driver_info = BRIDGE_STV610 },
+ /* Dexxa WebCam USB */
+ {USB_DEVICE(0x046d, 0x0870), .driver_info = BRIDGE_STV602 },
+ /* QuickCam Messenger */
+ {USB_DEVICE(0x046D, 0x08F0), .driver_info = BRIDGE_ST6422 },
+ /* QuickCam Communicate */
+ {USB_DEVICE(0x046D, 0x08F5), .driver_info = BRIDGE_ST6422 },
+ /* QuickCam Messenger (new) */
+ {USB_DEVICE(0x046D, 0x08F6), .driver_info = BRIDGE_ST6422 },
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ PDEBUG(D_PROBE, "Probing for a stv06xx device");
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static void sd_disconnect(struct usb_interface *intf)
+{
+ struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
+ struct sd *sd = (struct sd *) gspca_dev;
+ PDEBUG(D_PROBE, "Disconnecting the stv06xx device");
+
+ if (sd->sensor->disconnect)
+ sd->sensor->disconnect(sd);
+ gspca_disconnect(intf);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = sd_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
+
+module_param(dump_bridge, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dump_bridge, "Dumps all usb bridge registers at startup");
+
+module_param(dump_sensor, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dump_sensor, "Dumps all sensor registers at startup");
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.h b/drivers/media/video/gspca/stv06xx/stv06xx.h
new file mode 100644
index 00000000..d270a598
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#ifndef STV06XX_H_
+#define STV06XX_H_
+
+#include <linux/slab.h>
+#include "gspca.h"
+
+#define MODULE_NAME "STV06xx"
+
+#define STV_ISOC_ENDPOINT_ADDR 0x81
+
+#define STV_R 0x0509
+
+#define STV_REG23 0x0423
+
+/* Control registers of the STV0600 ASIC */
+#define STV_I2C_PARTNER 0x1420
+#define STV_I2C_VAL_REG_VAL_PAIRS_MIN1 0x1421
+#define STV_I2C_READ_WRITE_TOGGLE 0x1422
+#define STV_I2C_FLUSH 0x1423
+#define STV_I2C_SUCC_READ_REG_VALS 0x1424
+
+#define STV_ISO_ENABLE 0x1440
+#define STV_SCAN_RATE 0x1443
+#define STV_LED_CTRL 0x1445
+#define STV_STV0600_EMULATION 0x1446
+#define STV_REG00 0x1500
+#define STV_REG01 0x1501
+#define STV_REG02 0x1502
+#define STV_REG03 0x1503
+#define STV_REG04 0x1504
+
+#define STV_ISO_SIZE_L 0x15c1
+#define STV_ISO_SIZE_H 0x15c2
+
+/* Refers to the CIF 352x288 and QCIF 176x144 */
+/* 1: 288 lines, 2: 144 lines */
+#define STV_Y_CTRL 0x15c3
+
+#define STV_RESET 0x1620
+
+/* 0xa: 352 columns, 0x6: 176 columns */
+#define STV_X_CTRL 0x1680
+
+#define STV06XX_URB_MSG_TIMEOUT 5000
+
+#define I2C_MAX_BYTES 16
+#define I2C_MAX_WORDS 8
+
+#define I2C_BUFFER_LENGTH 0x23
+#define I2C_READ_CMD 3
+#define I2C_WRITE_CMD 1
+
+#define LED_ON 1
+#define LED_OFF 0
+
+/* STV06xx device descriptor */
+struct sd {
+ struct gspca_dev gspca_dev;
+
+ /* A pointer to the currently connected sensor */
+ const struct stv06xx_sensor *sensor;
+
+ /* A pointer to the sd_desc struct */
+ struct sd_desc desc;
+
+ /* Sensor private data */
+ void *sensor_priv;
+
+ /* The first 4 lines produced by the stv6422 are no good, this keeps
+ track of how many bytes we still need to skip during a frame */
+ int to_skip;
+
+ /* Bridge / Camera type */
+ u8 bridge;
+ #define BRIDGE_STV600 0
+ #define BRIDGE_STV602 1
+ #define BRIDGE_STV610 2
+ #define BRIDGE_ST6422 3 /* With integrated sensor */
+};
+
+int stv06xx_write_bridge(struct sd *sd, u16 address, u16 i2c_data);
+int stv06xx_read_bridge(struct sd *sd, u16 address, u8 *i2c_data);
+
+int stv06xx_write_sensor_bytes(struct sd *sd, const u8 *data, u8 len);
+int stv06xx_write_sensor_words(struct sd *sd, const u16 *data, u8 len);
+
+int stv06xx_read_sensor(struct sd *sd, const u8 address, u16 *value);
+int stv06xx_write_sensor(struct sd *sd, u8 address, u16 value);
+
+#endif
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c
new file mode 100644
index 00000000..a8698b7a
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c
@@ -0,0 +1,613 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ * Copyright (c) 2008 Chia-I Wu
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "stv06xx_hdcs.h"
+
+static const struct ctrl hdcs1x00_ctrl[] = {
+ {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x1,
+ .default_value = HDCS_DEFAULT_EXPOSURE,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = hdcs_set_exposure,
+ .get = hdcs_get_exposure
+ }, {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "gain",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x1,
+ .default_value = HDCS_DEFAULT_GAIN,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = hdcs_set_gain,
+ .get = hdcs_get_gain
+ }
+};
+
+static struct v4l2_pix_format hdcs1x00_mode[] = {
+ {
+ HDCS_1X00_DEF_WIDTH,
+ HDCS_1X00_DEF_HEIGHT,
+ V4L2_PIX_FMT_SGRBG8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ HDCS_1X00_DEF_WIDTH * HDCS_1X00_DEF_HEIGHT,
+ .bytesperline = HDCS_1X00_DEF_WIDTH,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1
+ }
+};
+
+static const struct ctrl hdcs1020_ctrl[] = {
+ {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x00,
+ .maximum = 0xffff,
+ .step = 0x1,
+ .default_value = HDCS_DEFAULT_EXPOSURE,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = hdcs_set_exposure,
+ .get = hdcs_get_exposure
+ }, {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "gain",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x1,
+ .default_value = HDCS_DEFAULT_GAIN,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = hdcs_set_gain,
+ .get = hdcs_get_gain
+ }
+};
+
+static struct v4l2_pix_format hdcs1020_mode[] = {
+ {
+ HDCS_1020_DEF_WIDTH,
+ HDCS_1020_DEF_HEIGHT,
+ V4L2_PIX_FMT_SGRBG8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ HDCS_1020_DEF_WIDTH * HDCS_1020_DEF_HEIGHT,
+ .bytesperline = HDCS_1020_DEF_WIDTH,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1
+ }
+};
+
+enum hdcs_power_state {
+ HDCS_STATE_SLEEP,
+ HDCS_STATE_IDLE,
+ HDCS_STATE_RUN
+};
+
+/* no lock? */
+struct hdcs {
+ enum hdcs_power_state state;
+ int w, h;
+
+ /* visible area of the sensor array */
+ struct {
+ int left, top;
+ int width, height;
+ int border;
+ } array;
+
+ struct {
+ /* Column timing overhead */
+ u8 cto;
+ /* Column processing overhead */
+ u8 cpo;
+ /* Row sample period constant */
+ u16 rs;
+ /* Exposure reset duration */
+ u16 er;
+ } exp;
+
+ int psmp;
+ u8 exp_cache, gain_cache;
+};
+
+static int hdcs_reg_write_seq(struct sd *sd, u8 reg, u8 *vals, u8 len)
+{
+ u8 regs[I2C_MAX_BYTES * 2];
+ int i;
+
+ if (unlikely((len <= 0) || (len >= I2C_MAX_BYTES) ||
+ (reg + len > 0xff)))
+ return -EINVAL;
+
+ for (i = 0; i < len; i++) {
+ regs[2 * i] = reg;
+ regs[2 * i + 1] = vals[i];
+ /* All addresses are shifted left one bit
+ * as bit 0 toggles r/w */
+ reg += 2;
+ }
+
+ return stv06xx_write_sensor_bytes(sd, regs, len);
+}
+
+static int hdcs_set_state(struct sd *sd, enum hdcs_power_state state)
+{
+ struct hdcs *hdcs = sd->sensor_priv;
+ u8 val;
+ int ret;
+
+ if (hdcs->state == state)
+ return 0;
+
+ /* we need to go idle before running or sleeping */
+ if (hdcs->state != HDCS_STATE_IDLE) {
+ ret = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 0);
+ if (ret)
+ return ret;
+ }
+
+ hdcs->state = HDCS_STATE_IDLE;
+
+ if (state == HDCS_STATE_IDLE)
+ return 0;
+
+ switch (state) {
+ case HDCS_STATE_SLEEP:
+ val = HDCS_SLEEP_MODE;
+ break;
+
+ case HDCS_STATE_RUN:
+ val = HDCS_RUN_ENABLE;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ ret = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), val);
+
+ /* Update the state if the write succeeded */
+ if (!ret)
+ hdcs->state = state;
+
+ return ret;
+}
+
+static int hdcs_reset(struct sd *sd)
+{
+ struct hdcs *hdcs = sd->sensor_priv;
+ int err;
+
+ err = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 1);
+ if (err < 0)
+ return err;
+
+ err = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 0);
+ if (err < 0)
+ hdcs->state = HDCS_STATE_IDLE;
+
+ return err;
+}
+
+static int hdcs_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct hdcs *hdcs = sd->sensor_priv;
+
+ *val = hdcs->exp_cache;
+
+ return 0;
+}
+
+static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct hdcs *hdcs = sd->sensor_priv;
+ int rowexp, srowexp;
+ int max_srowexp;
+ /* Column time period */
+ int ct;
+ /* Column processing period */
+ int cp;
+ /* Row processing period */
+ int rp;
+ /* Minimum number of column timing periods
+ within the column processing period */
+ int mnct;
+ int cycles, err;
+ u8 exp[14];
+
+ val &= 0xff;
+ hdcs->exp_cache = val;
+
+ cycles = val * HDCS_CLK_FREQ_MHZ * 257;
+
+ ct = hdcs->exp.cto + hdcs->psmp + (HDCS_ADC_START_SIG_DUR + 2);
+ cp = hdcs->exp.cto + (hdcs->w * ct / 2);
+
+ /* the cycles one row takes */
+ rp = hdcs->exp.rs + cp;
+
+ rowexp = cycles / rp;
+
+ /* the remaining cycles */
+ cycles -= rowexp * rp;
+
+ /* calculate sub-row exposure */
+ if (IS_1020(sd)) {
+ /* see HDCS-1020 datasheet 3.5.6.4, p. 63 */
+ srowexp = hdcs->w - (cycles + hdcs->exp.er + 13) / ct;
+
+ mnct = (hdcs->exp.er + 12 + ct - 1) / ct;
+ max_srowexp = hdcs->w - mnct;
+ } else {
+ /* see HDCS-1000 datasheet 3.4.5.5, p. 61 */
+ srowexp = cp - hdcs->exp.er - 6 - cycles;
+
+ mnct = (hdcs->exp.er + 5 + ct - 1) / ct;
+ max_srowexp = cp - mnct * ct - 1;
+ }
+
+ if (srowexp < 0)
+ srowexp = 0;
+ else if (srowexp > max_srowexp)
+ srowexp = max_srowexp;
+
+ if (IS_1020(sd)) {
+ exp[0] = HDCS20_CONTROL;
+ exp[1] = 0x00; /* Stop streaming */
+ exp[2] = HDCS_ROWEXPL;
+ exp[3] = rowexp & 0xff;
+ exp[4] = HDCS_ROWEXPH;
+ exp[5] = rowexp >> 8;
+ exp[6] = HDCS20_SROWEXP;
+ exp[7] = (srowexp >> 2) & 0xff;
+ exp[8] = HDCS20_ERROR;
+ exp[9] = 0x10; /* Clear exposure error flag*/
+ exp[10] = HDCS20_CONTROL;
+ exp[11] = 0x04; /* Restart streaming */
+ err = stv06xx_write_sensor_bytes(sd, exp, 6);
+ } else {
+ exp[0] = HDCS00_CONTROL;
+ exp[1] = 0x00; /* Stop streaming */
+ exp[2] = HDCS_ROWEXPL;
+ exp[3] = rowexp & 0xff;
+ exp[4] = HDCS_ROWEXPH;
+ exp[5] = rowexp >> 8;
+ exp[6] = HDCS00_SROWEXPL;
+ exp[7] = srowexp & 0xff;
+ exp[8] = HDCS00_SROWEXPH;
+ exp[9] = srowexp >> 8;
+ exp[10] = HDCS_STATUS;
+ exp[11] = 0x10; /* Clear exposure error flag*/
+ exp[12] = HDCS00_CONTROL;
+ exp[13] = 0x04; /* Restart streaming */
+ err = stv06xx_write_sensor_bytes(sd, exp, 7);
+ if (err < 0)
+ return err;
+ }
+ PDEBUG(D_V4L2, "Writing exposure %d, rowexp %d, srowexp %d",
+ val, rowexp, srowexp);
+ return err;
+}
+
+static int hdcs_set_gains(struct sd *sd, u8 g)
+{
+ struct hdcs *hdcs = sd->sensor_priv;
+ int err;
+ u8 gains[4];
+
+ hdcs->gain_cache = g;
+
+ /* the voltage gain Av = (1 + 19 * val / 127) * (1 + bit7) */
+ if (g > 127)
+ g = 0x80 | (g / 2);
+
+ gains[0] = g;
+ gains[1] = g;
+ gains[2] = g;
+ gains[3] = g;
+
+ err = hdcs_reg_write_seq(sd, HDCS_ERECPGA, gains, 4);
+ return err;
+}
+
+static int hdcs_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct hdcs *hdcs = sd->sensor_priv;
+
+ *val = hdcs->gain_cache;
+
+ return 0;
+}
+
+static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ PDEBUG(D_V4L2, "Writing gain %d", val);
+ return hdcs_set_gains((struct sd *) gspca_dev,
+ val & 0xff);
+}
+
+static int hdcs_set_size(struct sd *sd,
+ unsigned int width, unsigned int height)
+{
+ struct hdcs *hdcs = sd->sensor_priv;
+ u8 win[4];
+ unsigned int x, y;
+ int err;
+
+ /* must be multiple of 4 */
+ width = (width + 3) & ~0x3;
+ height = (height + 3) & ~0x3;
+
+ if (width > hdcs->array.width)
+ width = hdcs->array.width;
+
+ if (IS_1020(sd)) {
+ /* the borders are also invalid */
+ if (height + 2 * hdcs->array.border + HDCS_1020_BOTTOM_Y_SKIP
+ > hdcs->array.height)
+ height = hdcs->array.height - 2 * hdcs->array.border -
+ HDCS_1020_BOTTOM_Y_SKIP;
+
+ y = (hdcs->array.height - HDCS_1020_BOTTOM_Y_SKIP - height) / 2
+ + hdcs->array.top;
+ } else {
+ if (height > hdcs->array.height)
+ height = hdcs->array.height;
+
+ y = hdcs->array.top + (hdcs->array.height - height) / 2;
+ }
+
+ x = hdcs->array.left + (hdcs->array.width - width) / 2;
+
+ win[0] = y / 4;
+ win[1] = x / 4;
+ win[2] = (y + height) / 4 - 1;
+ win[3] = (x + width) / 4 - 1;
+
+ err = hdcs_reg_write_seq(sd, HDCS_FWROW, win, 4);
+ if (err < 0)
+ return err;
+
+ /* Update the current width and height */
+ hdcs->w = width;
+ hdcs->h = height;
+ return err;
+}
+
+static int hdcs_probe_1x00(struct sd *sd)
+{
+ struct hdcs *hdcs;
+ u16 sensor;
+ int ret;
+
+ ret = stv06xx_read_sensor(sd, HDCS_IDENT, &sensor);
+ if (ret < 0 || sensor != 0x08)
+ return -ENODEV;
+
+ pr_info("HDCS-1000/1100 sensor detected\n");
+
+ sd->gspca_dev.cam.cam_mode = hdcs1x00_mode;
+ sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1x00_mode);
+ sd->desc.ctrls = hdcs1x00_ctrl;
+ sd->desc.nctrls = ARRAY_SIZE(hdcs1x00_ctrl);
+
+ hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL);
+ if (!hdcs)
+ return -ENOMEM;
+
+ hdcs->array.left = 8;
+ hdcs->array.top = 8;
+ hdcs->array.width = HDCS_1X00_DEF_WIDTH;
+ hdcs->array.height = HDCS_1X00_DEF_HEIGHT;
+ hdcs->array.border = 4;
+
+ hdcs->exp.cto = 4;
+ hdcs->exp.cpo = 2;
+ hdcs->exp.rs = 186;
+ hdcs->exp.er = 100;
+
+ /*
+ * Frame rate on HDCS-1000 with STV600 depends on PSMP:
+ * 4 = doesn't work at all
+ * 5 = 7.8 fps,
+ * 6 = 6.9 fps,
+ * 8 = 6.3 fps,
+ * 10 = 5.5 fps,
+ * 15 = 4.4 fps,
+ * 31 = 2.8 fps
+ *
+ * Frame rate on HDCS-1000 with STV602 depends on PSMP:
+ * 15 = doesn't work at all
+ * 18 = doesn't work at all
+ * 19 = 7.3 fps
+ * 20 = 7.4 fps
+ * 21 = 7.4 fps
+ * 22 = 7.4 fps
+ * 24 = 6.3 fps
+ * 30 = 5.4 fps
+ */
+ hdcs->psmp = (sd->bridge == BRIDGE_STV602) ? 20 : 5;
+
+ sd->sensor_priv = hdcs;
+
+ return 0;
+}
+
+static int hdcs_probe_1020(struct sd *sd)
+{
+ struct hdcs *hdcs;
+ u16 sensor;
+ int ret;
+
+ ret = stv06xx_read_sensor(sd, HDCS_IDENT, &sensor);
+ if (ret < 0 || sensor != 0x10)
+ return -ENODEV;
+
+ pr_info("HDCS-1020 sensor detected\n");
+
+ sd->gspca_dev.cam.cam_mode = hdcs1020_mode;
+ sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1020_mode);
+ sd->desc.ctrls = hdcs1020_ctrl;
+ sd->desc.nctrls = ARRAY_SIZE(hdcs1020_ctrl);
+
+ hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL);
+ if (!hdcs)
+ return -ENOMEM;
+
+ /*
+ * From Andrey's test image: looks like HDCS-1020 upper-left
+ * visible pixel is at 24,8 (y maybe even smaller?) and lower-right
+ * visible pixel at 375,299 (x maybe even larger?)
+ */
+ hdcs->array.left = 24;
+ hdcs->array.top = 4;
+ hdcs->array.width = HDCS_1020_DEF_WIDTH;
+ hdcs->array.height = 304;
+ hdcs->array.border = 4;
+
+ hdcs->psmp = 6;
+
+ hdcs->exp.cto = 3;
+ hdcs->exp.cpo = 3;
+ hdcs->exp.rs = 155;
+ hdcs->exp.er = 96;
+
+ sd->sensor_priv = hdcs;
+
+ return 0;
+}
+
+static int hdcs_start(struct sd *sd)
+{
+ PDEBUG(D_STREAM, "Starting stream");
+
+ return hdcs_set_state(sd, HDCS_STATE_RUN);
+}
+
+static int hdcs_stop(struct sd *sd)
+{
+ PDEBUG(D_STREAM, "Halting stream");
+
+ return hdcs_set_state(sd, HDCS_STATE_SLEEP);
+}
+
+static void hdcs_disconnect(struct sd *sd)
+{
+ PDEBUG(D_PROBE, "Disconnecting the sensor");
+ kfree(sd->sensor_priv);
+}
+
+static int hdcs_init(struct sd *sd)
+{
+ struct hdcs *hdcs = sd->sensor_priv;
+ int i, err = 0;
+
+ /* Set the STV0602AA in STV0600 emulation mode */
+ if (sd->bridge == BRIDGE_STV602)
+ stv06xx_write_bridge(sd, STV_STV0600_EMULATION, 1);
+
+ /* Execute the bridge init */
+ for (i = 0; i < ARRAY_SIZE(stv_bridge_init) && !err; i++) {
+ err = stv06xx_write_bridge(sd, stv_bridge_init[i][0],
+ stv_bridge_init[i][1]);
+ }
+ if (err < 0)
+ return err;
+
+ /* sensor soft reset */
+ hdcs_reset(sd);
+
+ /* Execute the sensor init */
+ for (i = 0; i < ARRAY_SIZE(stv_sensor_init) && !err; i++) {
+ err = stv06xx_write_sensor(sd, stv_sensor_init[i][0],
+ stv_sensor_init[i][1]);
+ }
+ if (err < 0)
+ return err;
+
+ /* Enable continuous frame capture, bit 2: stop when frame complete */
+ err = stv06xx_write_sensor(sd, HDCS_REG_CONFIG(sd), BIT(3));
+ if (err < 0)
+ return err;
+
+ /* Set PGA sample duration
+ (was 0x7E for the STV602, but caused slow framerate with HDCS-1020) */
+ if (IS_1020(sd))
+ err = stv06xx_write_sensor(sd, HDCS_TCTRL,
+ (HDCS_ADC_START_SIG_DUR << 6) | hdcs->psmp);
+ else
+ err = stv06xx_write_sensor(sd, HDCS_TCTRL,
+ (HDCS_ADC_START_SIG_DUR << 5) | hdcs->psmp);
+ if (err < 0)
+ return err;
+
+ err = hdcs_set_gains(sd, HDCS_DEFAULT_GAIN);
+ if (err < 0)
+ return err;
+
+ err = hdcs_set_size(sd, hdcs->array.width, hdcs->array.height);
+ if (err < 0)
+ return err;
+
+ err = hdcs_set_exposure(&sd->gspca_dev, HDCS_DEFAULT_EXPOSURE);
+ return err;
+}
+
+static int hdcs_dump(struct sd *sd)
+{
+ u16 reg, val;
+
+ pr_info("Dumping sensor registers:\n");
+
+ for (reg = HDCS_IDENT; reg <= HDCS_ROWEXPH; reg++) {
+ stv06xx_read_sensor(sd, reg, &val);
+ pr_info("reg 0x%02x = 0x%02x\n", reg, val);
+ }
+ return 0;
+}
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h
new file mode 100644
index 00000000..a14a84a5
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ * Copyright (c) 2008 Chia-I Wu
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#ifndef STV06XX_HDCS_H_
+#define STV06XX_HDCS_H_
+
+#include "stv06xx_sensor.h"
+
+#define HDCS_REG_CONFIG(sd) (IS_1020(sd) ? HDCS20_CONFIG : HDCS00_CONFIG)
+#define HDCS_REG_CONTROL(sd) (IS_1020(sd) ? HDCS20_CONTROL : HDCS00_CONTROL)
+
+#define HDCS_1X00_DEF_WIDTH 360
+#define HDCS_1X00_DEF_HEIGHT 296
+
+#define HDCS_1020_DEF_WIDTH 352
+#define HDCS_1020_DEF_HEIGHT 292
+
+#define HDCS_1020_BOTTOM_Y_SKIP 4
+
+#define HDCS_CLK_FREQ_MHZ 25
+
+#define HDCS_ADC_START_SIG_DUR 3
+
+/* LSB bit of I2C or register address signifies write (0) or read (1) */
+/* I2C Registers common for both HDCS-1000/1100 and HDCS-1020 */
+/* Identifications Register */
+#define HDCS_IDENT (0x00 << 1)
+/* Status Register */
+#define HDCS_STATUS (0x01 << 1)
+/* Interrupt Mask Register */
+#define HDCS_IMASK (0x02 << 1)
+/* Pad Control Register */
+#define HDCS_PCTRL (0x03 << 1)
+/* Pad Drive Control Register */
+#define HDCS_PDRV (0x04 << 1)
+/* Interface Control Register */
+#define HDCS_ICTRL (0x05 << 1)
+/* Interface Timing Register */
+#define HDCS_ITMG (0x06 << 1)
+/* Baud Fraction Register */
+#define HDCS_BFRAC (0x07 << 1)
+/* Baud Rate Register */
+#define HDCS_BRATE (0x08 << 1)
+/* ADC Control Register */
+#define HDCS_ADCCTRL (0x09 << 1)
+/* First Window Row Register */
+#define HDCS_FWROW (0x0a << 1)
+/* First Window Column Register */
+#define HDCS_FWCOL (0x0b << 1)
+/* Last Window Row Register */
+#define HDCS_LWROW (0x0c << 1)
+/* Last Window Column Register */
+#define HDCS_LWCOL (0x0d << 1)
+/* Timing Control Register */
+#define HDCS_TCTRL (0x0e << 1)
+/* PGA Gain Register: Even Row, Even Column */
+#define HDCS_ERECPGA (0x0f << 1)
+/* PGA Gain Register: Even Row, Odd Column */
+#define HDCS_EROCPGA (0x10 << 1)
+/* PGA Gain Register: Odd Row, Even Column */
+#define HDCS_ORECPGA (0x11 << 1)
+/* PGA Gain Register: Odd Row, Odd Column */
+#define HDCS_OROCPGA (0x12 << 1)
+/* Row Exposure Low Register */
+#define HDCS_ROWEXPL (0x13 << 1)
+/* Row Exposure High Register */
+#define HDCS_ROWEXPH (0x14 << 1)
+
+/* I2C Registers only for HDCS-1000/1100 */
+/* Sub-Row Exposure Low Register */
+#define HDCS00_SROWEXPL (0x15 << 1)
+/* Sub-Row Exposure High Register */
+#define HDCS00_SROWEXPH (0x16 << 1)
+/* Configuration Register */
+#define HDCS00_CONFIG (0x17 << 1)
+/* Control Register */
+#define HDCS00_CONTROL (0x18 << 1)
+
+/* I2C Registers only for HDCS-1020 */
+/* Sub-Row Exposure Register */
+#define HDCS20_SROWEXP (0x15 << 1)
+/* Error Control Register */
+#define HDCS20_ERROR (0x16 << 1)
+/* Interface Timing 2 Register */
+#define HDCS20_ITMG2 (0x17 << 1)
+/* Interface Control 2 Register */
+#define HDCS20_ICTRL2 (0x18 << 1)
+/* Horizontal Blank Register */
+#define HDCS20_HBLANK (0x19 << 1)
+/* Vertical Blank Register */
+#define HDCS20_VBLANK (0x1a << 1)
+/* Configuration Register */
+#define HDCS20_CONFIG (0x1b << 1)
+/* Control Register */
+#define HDCS20_CONTROL (0x1c << 1)
+
+#define HDCS_RUN_ENABLE (1 << 2)
+#define HDCS_SLEEP_MODE (1 << 1)
+
+#define HDCS_DEFAULT_EXPOSURE 48
+#define HDCS_DEFAULT_GAIN 50
+
+static int hdcs_probe_1x00(struct sd *sd);
+static int hdcs_probe_1020(struct sd *sd);
+static int hdcs_start(struct sd *sd);
+static int hdcs_init(struct sd *sd);
+static int hdcs_stop(struct sd *sd);
+static int hdcs_dump(struct sd *sd);
+static void hdcs_disconnect(struct sd *sd);
+
+static int hdcs_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
+static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
+static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val);
+static int hdcs_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
+
+const struct stv06xx_sensor stv06xx_sensor_hdcs1x00 = {
+ .name = "HP HDCS-1000/1100",
+ .i2c_flush = 0,
+ .i2c_addr = (0x55 << 1),
+ .i2c_len = 1,
+
+ /* FIXME (see if we can lower min_packet_size, needs testing, and also
+ adjusting framerate when the bandwidth gets lower) */
+ .min_packet_size = { 847 },
+ .max_packet_size = { 847 },
+
+ .init = hdcs_init,
+ .probe = hdcs_probe_1x00,
+ .start = hdcs_start,
+ .stop = hdcs_stop,
+ .disconnect = hdcs_disconnect,
+ .dump = hdcs_dump,
+};
+
+const struct stv06xx_sensor stv06xx_sensor_hdcs1020 = {
+ .name = "HDCS-1020",
+ .i2c_flush = 0,
+ .i2c_addr = (0x55 << 1),
+ .i2c_len = 1,
+
+ /* FIXME (see if we can lower min_packet_size, needs testing, and also
+ adjusting framerate when the bandwidthm gets lower) */
+ .min_packet_size = { 847 },
+ .max_packet_size = { 847 },
+
+ .init = hdcs_init,
+ .probe = hdcs_probe_1020,
+ .start = hdcs_start,
+ .stop = hdcs_stop,
+ .dump = hdcs_dump,
+};
+
+static const u16 stv_bridge_init[][2] = {
+ {STV_ISO_ENABLE, 0},
+ {STV_REG23, 0},
+ {STV_REG00, 0x1d},
+ {STV_REG01, 0xb5},
+ {STV_REG02, 0xa8},
+ {STV_REG03, 0x95},
+ {STV_REG04, 0x07},
+
+ {STV_SCAN_RATE, 0x20},
+ {STV_Y_CTRL, 0x01},
+ {STV_X_CTRL, 0x0a}
+};
+
+static const u8 stv_sensor_init[][2] = {
+ /* Clear status (writing 1 will clear the corresponding status bit) */
+ {HDCS_STATUS, BIT(6) | BIT(5) | BIT(4) | BIT(3) | BIT(2) | BIT(1)},
+ /* Disable all interrupts */
+ {HDCS_IMASK, 0x00},
+ {HDCS_PCTRL, BIT(6) | BIT(5) | BIT(1) | BIT(0)},
+ {HDCS_PDRV, 0x00},
+ {HDCS_ICTRL, BIT(5)},
+ {HDCS_ITMG, BIT(4) | BIT(1)},
+ /* ADC output resolution to 10 bits */
+ {HDCS_ADCCTRL, 10}
+};
+
+#endif
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c
new file mode 100644
index 00000000..26f14fc4
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c
@@ -0,0 +1,575 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+/*
+ * The spec file for the PB-0100 suggests the following for best quality
+ * images after the sensor has been reset :
+ *
+ * PB_ADCGAINL = R60 = 0x03 (3 dec) : sets low reference of ADC
+ to produce good black level
+ * PB_PREADCTRL = R32 = 0x1400 (5120 dec) : Enables global gain changes
+ through R53
+ * PB_ADCMINGAIN = R52 = 0x10 (16 dec) : Sets the minimum gain for
+ auto-exposure
+ * PB_ADCGLOBALGAIN = R53 = 0x10 (16 dec) : Sets the global gain
+ * PB_EXPGAIN = R14 = 0x11 (17 dec) : Sets the auto-exposure value
+ * PB_UPDATEINT = R23 = 0x02 (2 dec) : Sets the speed on
+ auto-exposure routine
+ * PB_CFILLIN = R5 = 0x0E (14 dec) : Sets the frame rate
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "stv06xx_pb0100.h"
+
+static const struct ctrl pb0100_ctrl[] = {
+#define GAIN_IDX 0
+ {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 128
+ },
+ .set = pb0100_set_gain,
+ .get = pb0100_get_gain
+ },
+#define RED_BALANCE_IDX 1
+ {
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Balance",
+ .minimum = -255,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = pb0100_set_red_balance,
+ .get = pb0100_get_red_balance
+ },
+#define BLUE_BALANCE_IDX 2
+ {
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Balance",
+ .minimum = -255,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = pb0100_set_blue_balance,
+ .get = pb0100_get_blue_balance
+ },
+#define EXPOSURE_IDX 3
+ {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 0,
+ .maximum = 511,
+ .step = 1,
+ .default_value = 12
+ },
+ .set = pb0100_set_exposure,
+ .get = pb0100_get_exposure
+ },
+#define AUTOGAIN_IDX 4
+ {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Automatic Gain and Exposure",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1
+ },
+ .set = pb0100_set_autogain,
+ .get = pb0100_get_autogain
+ },
+#define AUTOGAIN_TARGET_IDX 5
+ {
+ {
+ .id = V4L2_CTRL_CLASS_USER + 0x1000,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Automatic Gain Target",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 128
+ },
+ .set = pb0100_set_autogain_target,
+ .get = pb0100_get_autogain_target
+ },
+#define NATURAL_IDX 6
+ {
+ {
+ .id = V4L2_CTRL_CLASS_USER + 0x1001,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Natural Light Source",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1
+ },
+ .set = pb0100_set_natural,
+ .get = pb0100_get_natural
+ }
+};
+
+static struct v4l2_pix_format pb0100_mode[] = {
+/* low res / subsample modes disabled as they are only half res horizontal,
+ halving the vertical resolution does not seem to work */
+ {
+ 320,
+ 240,
+ V4L2_PIX_FMT_SGRBG8,
+ V4L2_FIELD_NONE,
+ .sizeimage = 320 * 240,
+ .bytesperline = 320,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = PB0100_CROP_TO_VGA
+ },
+ {
+ 352,
+ 288,
+ V4L2_PIX_FMT_SGRBG8,
+ V4L2_FIELD_NONE,
+ .sizeimage = 352 * 288,
+ .bytesperline = 352,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0
+ }
+};
+
+static int pb0100_probe(struct sd *sd)
+{
+ u16 sensor;
+ int i, err;
+ s32 *sensor_settings;
+
+ err = stv06xx_read_sensor(sd, PB_IDENT, &sensor);
+
+ if (err < 0)
+ return -ENODEV;
+
+ if ((sensor >> 8) == 0x64) {
+ sensor_settings = kmalloc(
+ ARRAY_SIZE(pb0100_ctrl) * sizeof(s32),
+ GFP_KERNEL);
+ if (!sensor_settings)
+ return -ENOMEM;
+
+ pr_info("Photobit pb0100 sensor detected\n");
+
+ sd->gspca_dev.cam.cam_mode = pb0100_mode;
+ sd->gspca_dev.cam.nmodes = ARRAY_SIZE(pb0100_mode);
+ sd->desc.ctrls = pb0100_ctrl;
+ sd->desc.nctrls = ARRAY_SIZE(pb0100_ctrl);
+ for (i = 0; i < sd->desc.nctrls; i++)
+ sensor_settings[i] = pb0100_ctrl[i].qctrl.default_value;
+ sd->sensor_priv = sensor_settings;
+
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static int pb0100_start(struct sd *sd)
+{
+ int err, packet_size, max_packet_size;
+ struct usb_host_interface *alt;
+ struct usb_interface *intf;
+ struct cam *cam = &sd->gspca_dev.cam;
+ s32 *sensor_settings = sd->sensor_priv;
+ u32 mode = cam->cam_mode[sd->gspca_dev.curr_mode].priv;
+
+ intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
+ alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
+ if (!alt)
+ return -ENODEV;
+ packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+
+ /* If we don't have enough bandwidth use a lower framerate */
+ max_packet_size = sd->sensor->max_packet_size[sd->gspca_dev.curr_mode];
+ if (packet_size < max_packet_size)
+ stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(4)|BIT(3)|BIT(1));
+ else
+ stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(5)|BIT(3)|BIT(1));
+
+ /* Setup sensor window */
+ if (mode & PB0100_CROP_TO_VGA) {
+ stv06xx_write_sensor(sd, PB_RSTART, 30);
+ stv06xx_write_sensor(sd, PB_CSTART, 20);
+ stv06xx_write_sensor(sd, PB_RWSIZE, 240 - 1);
+ stv06xx_write_sensor(sd, PB_CWSIZE, 320 - 1);
+ } else {
+ stv06xx_write_sensor(sd, PB_RSTART, 8);
+ stv06xx_write_sensor(sd, PB_CSTART, 4);
+ stv06xx_write_sensor(sd, PB_RWSIZE, 288 - 1);
+ stv06xx_write_sensor(sd, PB_CWSIZE, 352 - 1);
+ }
+
+ if (mode & PB0100_SUBSAMPLE) {
+ stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02); /* Wrong, FIXME */
+ stv06xx_write_bridge(sd, STV_X_CTRL, 0x06);
+
+ stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x10);
+ } else {
+ stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01);
+ stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a);
+ /* larger -> slower */
+ stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x20);
+ }
+
+ /* set_gain also sets red and blue balance */
+ pb0100_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]);
+ pb0100_set_exposure(&sd->gspca_dev, sensor_settings[EXPOSURE_IDX]);
+ pb0100_set_autogain_target(&sd->gspca_dev,
+ sensor_settings[AUTOGAIN_TARGET_IDX]);
+ pb0100_set_autogain(&sd->gspca_dev, sensor_settings[AUTOGAIN_IDX]);
+
+ err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3)|BIT(1));
+ PDEBUG(D_STREAM, "Started stream, status: %d", err);
+
+ return (err < 0) ? err : 0;
+}
+
+static int pb0100_stop(struct sd *sd)
+{
+ int err;
+
+ err = stv06xx_write_sensor(sd, PB_ABORTFRAME, 1);
+
+ if (err < 0)
+ goto out;
+
+ /* Set bit 1 to zero */
+ err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3));
+
+ PDEBUG(D_STREAM, "Halting stream");
+out:
+ return (err < 0) ? err : 0;
+}
+
+static void pb0100_disconnect(struct sd *sd)
+{
+ sd->sensor = NULL;
+ kfree(sd->sensor_priv);
+}
+
+/* FIXME: Sort the init commands out and put them into tables,
+ this is only for getting the camera to work */
+/* FIXME: No error handling for now,
+ add this once the init has been converted to proper tables */
+static int pb0100_init(struct sd *sd)
+{
+ stv06xx_write_bridge(sd, STV_REG00, 1);
+ stv06xx_write_bridge(sd, STV_SCAN_RATE, 0);
+
+ /* Reset sensor */
+ stv06xx_write_sensor(sd, PB_RESET, 1);
+ stv06xx_write_sensor(sd, PB_RESET, 0);
+
+ /* Disable chip */
+ stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3));
+
+ /* Gain stuff...*/
+ stv06xx_write_sensor(sd, PB_PREADCTRL, BIT(12)|BIT(10)|BIT(6));
+ stv06xx_write_sensor(sd, PB_ADCGLOBALGAIN, 12);
+
+ /* Set up auto-exposure */
+ /* ADC VREF_HI new setting for a transition
+ from the Expose1 to the Expose2 setting */
+ stv06xx_write_sensor(sd, PB_R28, 12);
+ /* gain max for autoexposure */
+ stv06xx_write_sensor(sd, PB_ADCMAXGAIN, 180);
+ /* gain min for autoexposure */
+ stv06xx_write_sensor(sd, PB_ADCMINGAIN, 12);
+ /* Maximum frame integration time (programmed into R8)
+ allowed for auto-exposure routine */
+ stv06xx_write_sensor(sd, PB_R54, 3);
+ /* Minimum frame integration time (programmed into R8)
+ allowed for auto-exposure routine */
+ stv06xx_write_sensor(sd, PB_R55, 0);
+ stv06xx_write_sensor(sd, PB_UPDATEINT, 1);
+ /* R15 Expose0 (maximum that auto-exposure may use) */
+ stv06xx_write_sensor(sd, PB_R15, 800);
+ /* R17 Expose2 (minimum that auto-exposure may use) */
+ stv06xx_write_sensor(sd, PB_R17, 10);
+
+ stv06xx_write_sensor(sd, PB_EXPGAIN, 0);
+
+ /* 0x14 */
+ stv06xx_write_sensor(sd, PB_VOFFSET, 0);
+ /* 0x0D */
+ stv06xx_write_sensor(sd, PB_ADCGAINH, 11);
+ /* Set black level (important!) */
+ stv06xx_write_sensor(sd, PB_ADCGAINL, 0);
+
+ /* ??? */
+ stv06xx_write_bridge(sd, STV_REG00, 0x11);
+ stv06xx_write_bridge(sd, STV_REG03, 0x45);
+ stv06xx_write_bridge(sd, STV_REG04, 0x07);
+
+ /* Scan/timing for the sensor */
+ stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(4)|BIT(3)|BIT(1));
+ stv06xx_write_sensor(sd, PB_CFILLIN, 14);
+ stv06xx_write_sensor(sd, PB_VBL, 0);
+ stv06xx_write_sensor(sd, PB_FINTTIME, 0);
+ stv06xx_write_sensor(sd, PB_RINTTIME, 123);
+
+ stv06xx_write_bridge(sd, STV_REG01, 0xc2);
+ stv06xx_write_bridge(sd, STV_REG02, 0xb0);
+ return 0;
+}
+
+static int pb0100_dump(struct sd *sd)
+{
+ return 0;
+}
+
+static int pb0100_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[GAIN_IDX];
+
+ return 0;
+}
+
+static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ if (sensor_settings[AUTOGAIN_IDX])
+ return -EBUSY;
+
+ sensor_settings[GAIN_IDX] = val;
+ err = stv06xx_write_sensor(sd, PB_G1GAIN, val);
+ if (!err)
+ err = stv06xx_write_sensor(sd, PB_G2GAIN, val);
+ PDEBUG(D_V4L2, "Set green gain to %d, status: %d", val, err);
+
+ if (!err)
+ err = pb0100_set_red_balance(gspca_dev,
+ sensor_settings[RED_BALANCE_IDX]);
+ if (!err)
+ err = pb0100_set_blue_balance(gspca_dev,
+ sensor_settings[BLUE_BALANCE_IDX]);
+
+ return err;
+}
+
+static int pb0100_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[RED_BALANCE_IDX];
+
+ return 0;
+}
+
+static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ if (sensor_settings[AUTOGAIN_IDX])
+ return -EBUSY;
+
+ sensor_settings[RED_BALANCE_IDX] = val;
+ val += sensor_settings[GAIN_IDX];
+ if (val < 0)
+ val = 0;
+ else if (val > 255)
+ val = 255;
+
+ err = stv06xx_write_sensor(sd, PB_RGAIN, val);
+ PDEBUG(D_V4L2, "Set red gain to %d, status: %d", val, err);
+
+ return err;
+}
+
+static int pb0100_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[BLUE_BALANCE_IDX];
+
+ return 0;
+}
+
+static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ if (sensor_settings[AUTOGAIN_IDX])
+ return -EBUSY;
+
+ sensor_settings[BLUE_BALANCE_IDX] = val;
+ val += sensor_settings[GAIN_IDX];
+ if (val < 0)
+ val = 0;
+ else if (val > 255)
+ val = 255;
+
+ err = stv06xx_write_sensor(sd, PB_BGAIN, val);
+ PDEBUG(D_V4L2, "Set blue gain to %d, status: %d", val, err);
+
+ return err;
+}
+
+static int pb0100_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[EXPOSURE_IDX];
+
+ return 0;
+}
+
+static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ if (sensor_settings[AUTOGAIN_IDX])
+ return -EBUSY;
+
+ sensor_settings[EXPOSURE_IDX] = val;
+ err = stv06xx_write_sensor(sd, PB_RINTTIME, val);
+ PDEBUG(D_V4L2, "Set exposure to %d, status: %d", val, err);
+
+ return err;
+}
+
+static int pb0100_get_autogain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[AUTOGAIN_IDX];
+
+ return 0;
+}
+
+static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ sensor_settings[AUTOGAIN_IDX] = val;
+ if (sensor_settings[AUTOGAIN_IDX]) {
+ if (sensor_settings[NATURAL_IDX])
+ val = BIT(6)|BIT(4)|BIT(0);
+ else
+ val = BIT(4)|BIT(0);
+ } else
+ val = 0;
+
+ err = stv06xx_write_sensor(sd, PB_EXPGAIN, val);
+ PDEBUG(D_V4L2, "Set autogain to %d (natural: %d), status: %d",
+ sensor_settings[AUTOGAIN_IDX], sensor_settings[NATURAL_IDX],
+ err);
+
+ return err;
+}
+
+static int pb0100_get_autogain_target(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[AUTOGAIN_TARGET_IDX];
+
+ return 0;
+}
+
+static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err, totalpixels, brightpixels, darkpixels;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ sensor_settings[AUTOGAIN_TARGET_IDX] = val;
+
+ /* Number of pixels counted by the sensor when subsampling the pixels.
+ * Slightly larger than the real value to avoid oscillation */
+ totalpixels = gspca_dev->width * gspca_dev->height;
+ totalpixels = totalpixels/(8*8) + totalpixels/(64*64);
+
+ brightpixels = (totalpixels * val) >> 8;
+ darkpixels = totalpixels - brightpixels;
+ err = stv06xx_write_sensor(sd, PB_R21, brightpixels);
+ if (!err)
+ err = stv06xx_write_sensor(sd, PB_R22, darkpixels);
+
+ PDEBUG(D_V4L2, "Set autogain target to %d, status: %d", val, err);
+
+ return err;
+}
+
+static int pb0100_get_natural(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[NATURAL_IDX];
+
+ return 0;
+}
+
+static int pb0100_set_natural(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ sensor_settings[NATURAL_IDX] = val;
+
+ return pb0100_set_autogain(gspca_dev, sensor_settings[AUTOGAIN_IDX]);
+}
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h
new file mode 100644
index 00000000..757de246
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#ifndef STV06XX_PB0100_H_
+#define STV06XX_PB0100_H_
+
+#include "stv06xx_sensor.h"
+
+/* mode priv field flags */
+#define PB0100_CROP_TO_VGA 0x01
+#define PB0100_SUBSAMPLE 0x02
+
+/* I2C Registers */
+#define PB_IDENT 0x00 /* Chip Version */
+#define PB_RSTART 0x01 /* Row Window Start */
+#define PB_CSTART 0x02 /* Column Window Start */
+#define PB_RWSIZE 0x03 /* Row Window Size */
+#define PB_CWSIZE 0x04 /* Column Window Size */
+#define PB_CFILLIN 0x05 /* Column Fill-In */
+#define PB_VBL 0x06 /* Vertical Blank Count */
+#define PB_CONTROL 0x07 /* Control Mode */
+#define PB_FINTTIME 0x08 /* Integration Time/Frame Unit Count */
+#define PB_RINTTIME 0x09 /* Integration Time/Row Unit Count */
+#define PB_ROWSPEED 0x0a /* Row Speed Control */
+#define PB_ABORTFRAME 0x0b /* Abort Frame */
+#define PB_R12 0x0c /* Reserved */
+#define PB_RESET 0x0d /* Reset */
+#define PB_EXPGAIN 0x0e /* Exposure Gain Command */
+#define PB_R15 0x0f /* Expose0 */
+#define PB_R16 0x10 /* Expose1 */
+#define PB_R17 0x11 /* Expose2 */
+#define PB_R18 0x12 /* Low0_DAC */
+#define PB_R19 0x13 /* Low1_DAC */
+#define PB_R20 0x14 /* Low2_DAC */
+#define PB_R21 0x15 /* Threshold11 */
+#define PB_R22 0x16 /* Threshold0x */
+#define PB_UPDATEINT 0x17 /* Update Interval */
+#define PB_R24 0x18 /* High_DAC */
+#define PB_R25 0x19 /* Trans0H */
+#define PB_R26 0x1a /* Trans1L */
+#define PB_R27 0x1b /* Trans1H */
+#define PB_R28 0x1c /* Trans2L */
+#define PB_R29 0x1d /* Reserved */
+#define PB_R30 0x1e /* Reserved */
+#define PB_R31 0x1f /* Wait to Read */
+#define PB_PREADCTRL 0x20 /* Pixel Read Control Mode */
+#define PB_R33 0x21 /* IREF_VLN */
+#define PB_R34 0x22 /* IREF_VLP */
+#define PB_R35 0x23 /* IREF_VLN_INTEG */
+#define PB_R36 0x24 /* IREF_MASTER */
+#define PB_R37 0x25 /* IDACP */
+#define PB_R38 0x26 /* IDACN */
+#define PB_R39 0x27 /* DAC_Control_Reg */
+#define PB_R40 0x28 /* VCL */
+#define PB_R41 0x29 /* IREF_VLN_ADCIN */
+#define PB_R42 0x2a /* Reserved */
+#define PB_G1GAIN 0x2b /* Green 1 Gain */
+#define PB_BGAIN 0x2c /* Blue Gain */
+#define PB_RGAIN 0x2d /* Red Gain */
+#define PB_G2GAIN 0x2e /* Green 2 Gain */
+#define PB_R47 0x2f /* Dark Row Address */
+#define PB_R48 0x30 /* Dark Row Options */
+#define PB_R49 0x31 /* Reserved */
+#define PB_R50 0x32 /* Image Test Data */
+#define PB_ADCMAXGAIN 0x33 /* Maximum Gain */
+#define PB_ADCMINGAIN 0x34 /* Minimum Gain */
+#define PB_ADCGLOBALGAIN 0x35 /* Global Gain */
+#define PB_R54 0x36 /* Maximum Frame */
+#define PB_R55 0x37 /* Minimum Frame */
+#define PB_R56 0x38 /* Reserved */
+#define PB_VOFFSET 0x39 /* VOFFSET */
+#define PB_R58 0x3a /* Snap-Shot Sequence Trigger */
+#define PB_ADCGAINH 0x3b /* VREF_HI */
+#define PB_ADCGAINL 0x3c /* VREF_LO */
+#define PB_R61 0x3d /* Reserved */
+#define PB_R62 0x3e /* Reserved */
+#define PB_R63 0x3f /* Reserved */
+#define PB_R64 0x40 /* Red/Blue Gain */
+#define PB_R65 0x41 /* Green 2/Green 1 Gain */
+#define PB_R66 0x42 /* VREF_HI/LO */
+#define PB_R67 0x43 /* Integration Time/Row Unit Count */
+#define PB_R240 0xf0 /* ADC Test */
+#define PB_R241 0xf1 /* Chip Enable */
+#define PB_R242 0xf2 /* Reserved */
+
+static int pb0100_probe(struct sd *sd);
+static int pb0100_start(struct sd *sd);
+static int pb0100_init(struct sd *sd);
+static int pb0100_stop(struct sd *sd);
+static int pb0100_dump(struct sd *sd);
+static void pb0100_disconnect(struct sd *sd);
+
+/* V4L2 controls supported by the driver */
+static int pb0100_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
+static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val);
+static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val);
+static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
+static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_get_autogain(struct gspca_dev *gspca_dev, __s32 *val);
+static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_get_autogain_target(struct gspca_dev *gspca_dev, __s32 *val);
+static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_get_natural(struct gspca_dev *gspca_dev, __s32 *val);
+static int pb0100_set_natural(struct gspca_dev *gspca_dev, __s32 val);
+
+const struct stv06xx_sensor stv06xx_sensor_pb0100 = {
+ .name = "PB-0100",
+ .i2c_flush = 1,
+ .i2c_addr = 0xba,
+ .i2c_len = 2,
+
+ .min_packet_size = { 635, 847 },
+ .max_packet_size = { 847, 923 },
+
+ .init = pb0100_init,
+ .probe = pb0100_probe,
+ .start = pb0100_start,
+ .stop = pb0100_stop,
+ .dump = pb0100_dump,
+ .disconnect = pb0100_disconnect,
+};
+
+#endif
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h b/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h
new file mode 100644
index 00000000..fb229d8d
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#ifndef STV06XX_SENSOR_H_
+#define STV06XX_SENSOR_H_
+
+#include "stv06xx.h"
+
+#define IS_1020(sd) ((sd)->sensor == &stv06xx_sensor_hdcs1020)
+
+extern const struct stv06xx_sensor stv06xx_sensor_vv6410;
+extern const struct stv06xx_sensor stv06xx_sensor_hdcs1x00;
+extern const struct stv06xx_sensor stv06xx_sensor_hdcs1020;
+extern const struct stv06xx_sensor stv06xx_sensor_pb0100;
+extern const struct stv06xx_sensor stv06xx_sensor_st6422;
+
+struct stv06xx_sensor {
+ /* Defines the name of a sensor */
+ char name[32];
+
+ /* Sensor i2c address */
+ u8 i2c_addr;
+
+ /* Flush value*/
+ u8 i2c_flush;
+
+ /* length of an i2c word */
+ u8 i2c_len;
+
+ /* Isoc packet size (per mode) */
+ int min_packet_size[4];
+ int max_packet_size[4];
+
+ /* Probes if the sensor is connected */
+ int (*probe)(struct sd *sd);
+
+ /* Performs a initialization sequence */
+ int (*init)(struct sd *sd);
+
+ /* Executed at device disconnect */
+ void (*disconnect)(struct sd *sd);
+
+ /* Reads a sensor register */
+ int (*read_sensor)(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+ /* Writes to a sensor register */
+ int (*write_sensor)(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+ /* Instructs the sensor to start streaming */
+ int (*start)(struct sd *sd);
+
+ /* Instructs the sensor to stop streaming */
+ int (*stop)(struct sd *sd);
+
+ /* Instructs the sensor to dump all its contents */
+ int (*dump)(struct sd *sd);
+};
+
+#endif
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c
new file mode 100644
index 00000000..9940e035
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c
@@ -0,0 +1,403 @@
+/*
+ * Support for the sensor part which is integrated (I think) into the
+ * st6422 stv06xx alike bridge, as its integrated there are no i2c writes
+ * but instead direct bridge writes.
+ *
+ * Copyright (c) 2009 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Strongly based on qc-usb-messenger, which is:
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "stv06xx_st6422.h"
+
+/* controls */
+enum e_ctrl {
+ BRIGHTNESS,
+ CONTRAST,
+ GAIN,
+ EXPOSURE,
+ NCTRLS /* number of controls */
+};
+
+/* sensor settings */
+struct st6422_settings {
+ struct gspca_ctrl ctrls[NCTRLS];
+};
+
+static struct v4l2_pix_format st6422_mode[] = {
+ /* Note we actually get 124 lines of data, of which we skip the 4st
+ 4 as they are garbage */
+ {
+ 162,
+ 120,
+ V4L2_PIX_FMT_SGRBG8,
+ V4L2_FIELD_NONE,
+ .sizeimage = 162 * 120,
+ .bytesperline = 162,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1
+ },
+ /* Note we actually get 248 lines of data, of which we skip the 4st
+ 4 as they are garbage, and we tell the app it only gets the
+ first 240 of the 244 lines it actually gets, so that it ignores
+ the last 4. */
+ {
+ 324,
+ 240,
+ V4L2_PIX_FMT_SGRBG8,
+ V4L2_FIELD_NONE,
+ .sizeimage = 324 * 244,
+ .bytesperline = 324,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0
+ },
+};
+
+/* V4L2 controls supported by the driver */
+static void st6422_set_brightness(struct gspca_dev *gspca_dev);
+static void st6422_set_contrast(struct gspca_dev *gspca_dev);
+static void st6422_set_gain(struct gspca_dev *gspca_dev);
+static void st6422_set_exposure(struct gspca_dev *gspca_dev);
+
+static const struct ctrl st6422_ctrl[NCTRLS] = {
+[BRIGHTNESS] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 3
+ },
+ .set_control = st6422_set_brightness
+ },
+[CONTRAST] = {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 15,
+ .step = 1,
+ .default_value = 11
+ },
+ .set_control = st6422_set_contrast
+ },
+[GAIN] = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 64
+ },
+ .set_control = st6422_set_gain
+ },
+[EXPOSURE] = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 0,
+#define EXPOSURE_MAX 1023
+ .maximum = EXPOSURE_MAX,
+ .step = 1,
+ .default_value = 256
+ },
+ .set_control = st6422_set_exposure
+ },
+};
+
+static int st6422_probe(struct sd *sd)
+{
+ struct st6422_settings *sensor_settings;
+
+ if (sd->bridge != BRIDGE_ST6422)
+ return -ENODEV;
+
+ pr_info("st6422 sensor detected\n");
+
+ sensor_settings = kmalloc(sizeof *sensor_settings, GFP_KERNEL);
+ if (!sensor_settings)
+ return -ENOMEM;
+
+ sd->gspca_dev.cam.cam_mode = st6422_mode;
+ sd->gspca_dev.cam.nmodes = ARRAY_SIZE(st6422_mode);
+ sd->gspca_dev.cam.ctrls = sensor_settings->ctrls;
+ sd->desc.ctrls = st6422_ctrl;
+ sd->desc.nctrls = ARRAY_SIZE(st6422_ctrl);
+ sd->sensor_priv = sensor_settings;
+
+ return 0;
+}
+
+static int st6422_init(struct sd *sd)
+{
+ int err = 0, i;
+
+ const u16 st6422_bridge_init[][2] = {
+ { STV_ISO_ENABLE, 0x00 }, /* disable capture */
+ { 0x1436, 0x00 },
+ { 0x1432, 0x03 }, /* 0x00-0x1F brightness */
+ { 0x143a, 0xf9 }, /* 0x00-0x0F contrast */
+ { 0x0509, 0x38 }, /* R */
+ { 0x050a, 0x38 }, /* G */
+ { 0x050b, 0x38 }, /* B */
+ { 0x050c, 0x2a },
+ { 0x050d, 0x01 },
+
+
+ { 0x1431, 0x00 }, /* 0x00-0x07 ??? */
+ { 0x1433, 0x34 }, /* 160x120, 0x00-0x01 night filter */
+ { 0x1438, 0x18 }, /* 640x480 */
+/* 18 bayes */
+/* 10 compressed? */
+
+ { 0x1439, 0x00 },
+/* anti-noise? 0xa2 gives a perfect image */
+
+ { 0x143b, 0x05 },
+ { 0x143c, 0x00 }, /* 0x00-0x01 - ??? */
+
+
+/* shutter time 0x0000-0x03FF */
+/* low value give good picures on moving objects (but requires much light) */
+/* high value gives good picures in darkness (but tends to be overexposed) */
+ { 0x143e, 0x01 },
+ { 0x143d, 0x00 },
+
+ { 0x1442, 0xe2 },
+/* write: 1x1x xxxx */
+/* read: 1x1x xxxx */
+/* bit 5 == button pressed and hold if 0 */
+/* write 0xe2,0xea */
+
+/* 0x144a */
+/* 0x00 init */
+/* bit 7 == button has been pressed, but not handled */
+
+/* interrupt */
+/* if(urb->iso_frame_desc[i].status == 0x80) { */
+/* if(urb->iso_frame_desc[i].status == 0x88) { */
+
+ { 0x1500, 0xd0 },
+ { 0x1500, 0xd0 },
+ { 0x1500, 0x50 }, /* 0x00 - 0xFF 0x80 == compr ? */
+
+ { 0x1501, 0xaf },
+/* high val-> light area gets darker */
+/* low val -> light area gets lighter */
+ { 0x1502, 0xc2 },
+/* high val-> light area gets darker */
+/* low val -> light area gets lighter */
+ { 0x1503, 0x45 },
+/* high val-> light area gets darker */
+/* low val -> light area gets lighter */
+ { 0x1505, 0x02 },
+/* 2 : 324x248 80352 bytes */
+/* 7 : 248x162 40176 bytes */
+/* c+f: 162*124 20088 bytes */
+
+ { 0x150e, 0x8e },
+ { 0x150f, 0x37 },
+ { 0x15c0, 0x00 },
+ { 0x15c3, 0x08 }, /* 0x04/0x14 ... test pictures ??? */
+
+
+ { 0x143f, 0x01 }, /* commit settings */
+
+ };
+
+ for (i = 0; i < ARRAY_SIZE(st6422_bridge_init) && !err; i++) {
+ err = stv06xx_write_bridge(sd, st6422_bridge_init[i][0],
+ st6422_bridge_init[i][1]);
+ }
+
+ return err;
+}
+
+static void st6422_disconnect(struct sd *sd)
+{
+ sd->sensor = NULL;
+ kfree(sd->sensor_priv);
+}
+
+static int setbrightness(struct sd *sd)
+{
+ struct st6422_settings *sensor_settings = sd->sensor_priv;
+
+ /* val goes from 0 -> 31 */
+ return stv06xx_write_bridge(sd, 0x1432,
+ sensor_settings->ctrls[BRIGHTNESS].val);
+}
+
+static int setcontrast(struct sd *sd)
+{
+ struct st6422_settings *sensor_settings = sd->sensor_priv;
+
+ /* Val goes from 0 -> 15 */
+ return stv06xx_write_bridge(sd, 0x143a,
+ sensor_settings->ctrls[CONTRAST].val | 0xf0);
+}
+
+static int setgain(struct sd *sd)
+{
+ struct st6422_settings *sensor_settings = sd->sensor_priv;
+ u8 gain;
+ int err;
+
+ gain = sensor_settings->ctrls[GAIN].val;
+
+ /* Set red, green, blue, gain */
+ err = stv06xx_write_bridge(sd, 0x0509, gain);
+ if (err < 0)
+ return err;
+
+ err = stv06xx_write_bridge(sd, 0x050a, gain);
+ if (err < 0)
+ return err;
+
+ err = stv06xx_write_bridge(sd, 0x050b, gain);
+ if (err < 0)
+ return err;
+
+ /* 2 mystery writes */
+ err = stv06xx_write_bridge(sd, 0x050c, 0x2a);
+ if (err < 0)
+ return err;
+
+ return stv06xx_write_bridge(sd, 0x050d, 0x01);
+}
+
+static int setexposure(struct sd *sd)
+{
+ struct st6422_settings *sensor_settings = sd->sensor_priv;
+ u16 expo;
+ int err;
+
+ expo = sensor_settings->ctrls[EXPOSURE].val;
+ err = stv06xx_write_bridge(sd, 0x143d, expo & 0xff);
+ if (err < 0)
+ return err;
+
+ return stv06xx_write_bridge(sd, 0x143e, expo >> 8);
+}
+
+static int st6422_start(struct sd *sd)
+{
+ int err;
+ struct cam *cam = &sd->gspca_dev.cam;
+
+ if (cam->cam_mode[sd->gspca_dev.curr_mode].priv)
+ err = stv06xx_write_bridge(sd, 0x1505, 0x0f);
+ else
+ err = stv06xx_write_bridge(sd, 0x1505, 0x02);
+ if (err < 0)
+ return err;
+
+ err = setbrightness(sd);
+ if (err < 0)
+ return err;
+
+ err = setcontrast(sd);
+ if (err < 0)
+ return err;
+
+ err = setexposure(sd);
+ if (err < 0)
+ return err;
+
+ err = setgain(sd);
+ if (err < 0)
+ return err;
+
+ /* commit settings */
+ err = stv06xx_write_bridge(sd, 0x143f, 0x01);
+ return (err < 0) ? err : 0;
+}
+
+static int st6422_stop(struct sd *sd)
+{
+ PDEBUG(D_STREAM, "Halting stream");
+
+ return 0;
+}
+
+static void st6422_set_brightness(struct gspca_dev *gspca_dev)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = setbrightness(sd);
+
+ /* commit settings */
+ if (err >= 0)
+ err = stv06xx_write_bridge(sd, 0x143f, 0x01);
+
+ gspca_dev->usb_err = err;
+}
+
+static void st6422_set_contrast(struct gspca_dev *gspca_dev)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = setcontrast(sd);
+
+ /* commit settings */
+ if (err >= 0)
+ err = stv06xx_write_bridge(sd, 0x143f, 0x01);
+
+ gspca_dev->usb_err = err;
+}
+
+static void st6422_set_gain(struct gspca_dev *gspca_dev)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = setgain(sd);
+
+ /* commit settings */
+ if (err >= 0)
+ err = stv06xx_write_bridge(sd, 0x143f, 0x01);
+
+ gspca_dev->usb_err = err;
+}
+
+static void st6422_set_exposure(struct gspca_dev *gspca_dev)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = setexposure(sd);
+
+ /* commit settings */
+ if (err >= 0)
+ err = stv06xx_write_bridge(sd, 0x143f, 0x01);
+
+ gspca_dev->usb_err = err;
+}
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h
new file mode 100644
index 00000000..d7498e06
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h
@@ -0,0 +1,52 @@
+/*
+ * Support for the sensor part which is integrated (I think) into the
+ * st6422 stv06xx alike bridge, as its integrated there are no i2c writes
+ * but instead direct bridge writes.
+ *
+ * Copyright (c) 2009 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Strongly based on qc-usb-messenger, which is:
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef STV06XX_ST6422_H_
+#define STV06XX_ST6422_H_
+
+#include "stv06xx_sensor.h"
+
+static int st6422_probe(struct sd *sd);
+static int st6422_start(struct sd *sd);
+static int st6422_init(struct sd *sd);
+static int st6422_stop(struct sd *sd);
+static void st6422_disconnect(struct sd *sd);
+
+const struct stv06xx_sensor stv06xx_sensor_st6422 = {
+ .name = "ST6422",
+ /* No known way to lower framerate in case of less bandwidth */
+ .min_packet_size = { 300, 847 },
+ .max_packet_size = { 300, 847 },
+ .init = st6422_init,
+ .probe = st6422_probe,
+ .start = st6422_start,
+ .stop = st6422_stop,
+ .disconnect = st6422_disconnect,
+};
+
+#endif
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c
new file mode 100644
index 00000000..a5c69d9e
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "stv06xx_vv6410.h"
+
+static struct v4l2_pix_format vv6410_mode[] = {
+ {
+ 356,
+ 292,
+ V4L2_PIX_FMT_SGRBG8,
+ V4L2_FIELD_NONE,
+ .sizeimage = 356 * 292,
+ .bytesperline = 356,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0
+ }
+};
+
+static const struct ctrl vv6410_ctrl[] = {
+#define HFLIP_IDX 0
+ {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "horizontal flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = vv6410_set_hflip,
+ .get = vv6410_get_hflip
+ },
+#define VFLIP_IDX 1
+ {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "vertical flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = vv6410_set_vflip,
+ .get = vv6410_get_vflip
+ },
+#define GAIN_IDX 2
+ {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "analog gain",
+ .minimum = 0,
+ .maximum = 15,
+ .step = 1,
+ .default_value = 10
+ },
+ .set = vv6410_set_analog_gain,
+ .get = vv6410_get_analog_gain
+ },
+#define EXPOSURE_IDX 3
+ {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0,
+ .maximum = 32768,
+ .step = 1,
+ .default_value = 20000
+ },
+ .set = vv6410_set_exposure,
+ .get = vv6410_get_exposure
+ }
+ };
+
+static int vv6410_probe(struct sd *sd)
+{
+ u16 data;
+ int err, i;
+ s32 *sensor_settings;
+
+ err = stv06xx_read_sensor(sd, VV6410_DEVICEH, &data);
+ if (err < 0)
+ return -ENODEV;
+
+ if (data == 0x19) {
+ pr_info("vv6410 sensor detected\n");
+
+ sensor_settings = kmalloc(ARRAY_SIZE(vv6410_ctrl) * sizeof(s32),
+ GFP_KERNEL);
+ if (!sensor_settings)
+ return -ENOMEM;
+
+ sd->gspca_dev.cam.cam_mode = vv6410_mode;
+ sd->gspca_dev.cam.nmodes = ARRAY_SIZE(vv6410_mode);
+ sd->desc.ctrls = vv6410_ctrl;
+ sd->desc.nctrls = ARRAY_SIZE(vv6410_ctrl);
+
+ for (i = 0; i < sd->desc.nctrls; i++)
+ sensor_settings[i] = vv6410_ctrl[i].qctrl.default_value;
+ sd->sensor_priv = sensor_settings;
+ return 0;
+ }
+ return -ENODEV;
+}
+
+static int vv6410_init(struct sd *sd)
+{
+ int err = 0, i;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ for (i = 0; i < ARRAY_SIZE(stv_bridge_init); i++) {
+ stv06xx_write_bridge(sd, stv_bridge_init[i].addr, stv_bridge_init[i].data);
+ }
+
+ if (err < 0)
+ return err;
+
+ err = stv06xx_write_sensor_bytes(sd, (u8 *) vv6410_sensor_init,
+ ARRAY_SIZE(vv6410_sensor_init));
+ if (err < 0)
+ return err;
+
+ err = vv6410_set_exposure(&sd->gspca_dev,
+ sensor_settings[EXPOSURE_IDX]);
+ if (err < 0)
+ return err;
+
+ err = vv6410_set_analog_gain(&sd->gspca_dev,
+ sensor_settings[GAIN_IDX]);
+
+ return (err < 0) ? err : 0;
+}
+
+static void vv6410_disconnect(struct sd *sd)
+{
+ sd->sensor = NULL;
+ kfree(sd->sensor_priv);
+}
+
+static int vv6410_start(struct sd *sd)
+{
+ int err;
+ struct cam *cam = &sd->gspca_dev.cam;
+ u32 priv = cam->cam_mode[sd->gspca_dev.curr_mode].priv;
+
+ if (priv & VV6410_SUBSAMPLE) {
+ PDEBUG(D_CONF, "Enabling subsampling");
+ stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02);
+ stv06xx_write_bridge(sd, STV_X_CTRL, 0x06);
+
+ stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x10);
+ } else {
+ stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01);
+ stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a);
+ stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x00);
+
+ }
+
+ /* Turn on LED */
+ err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_ON);
+ if (err < 0)
+ return err;
+
+ err = stv06xx_write_sensor(sd, VV6410_SETUP0, 0);
+ if (err < 0)
+ return err;
+
+ PDEBUG(D_STREAM, "Starting stream");
+
+ return 0;
+}
+
+static int vv6410_stop(struct sd *sd)
+{
+ int err;
+
+ /* Turn off LED */
+ err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_OFF);
+ if (err < 0)
+ return err;
+
+ err = stv06xx_write_sensor(sd, VV6410_SETUP0, VV6410_LOW_POWER_MODE);
+ if (err < 0)
+ return err;
+
+ PDEBUG(D_STREAM, "Halting stream");
+
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_dump(struct sd *sd)
+{
+ u8 i;
+ int err = 0;
+
+ pr_info("Dumping all vv6410 sensor registers\n");
+ for (i = 0; i < 0xff && !err; i++) {
+ u16 data;
+ err = stv06xx_read_sensor(sd, i, &data);
+ pr_info("Register 0x%x contained 0x%x\n", i, data);
+ }
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[HFLIP_IDX];
+ PDEBUG(D_V4L2, "Read horizontal flip %d", *val);
+
+ return 0;
+}
+
+static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u16 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ sensor_settings[HFLIP_IDX] = val;
+ err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);
+ if (err < 0)
+ return err;
+
+ if (val)
+ i2c_data |= VV6410_HFLIP;
+ else
+ i2c_data &= ~VV6410_HFLIP;
+
+ PDEBUG(D_V4L2, "Set horizontal flip to %d", val);
+ err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data);
+
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[VFLIP_IDX];
+ PDEBUG(D_V4L2, "Read vertical flip %d", *val);
+
+ return 0;
+}
+
+static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u16 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ sensor_settings[VFLIP_IDX] = val;
+ err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);
+ if (err < 0)
+ return err;
+
+ if (val)
+ i2c_data |= VV6410_VFLIP;
+ else
+ i2c_data &= ~VV6410_VFLIP;
+
+ PDEBUG(D_V4L2, "Set vertical flip to %d", val);
+ err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data);
+
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_get_analog_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[GAIN_IDX];
+
+ PDEBUG(D_V4L2, "Read analog gain %d", *val);
+
+ return 0;
+}
+
+static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ sensor_settings[GAIN_IDX] = val;
+ PDEBUG(D_V4L2, "Set analog gain to %d", val);
+ err = stv06xx_write_sensor(sd, VV6410_ANALOGGAIN, 0xf0 | (val & 0xf));
+
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[EXPOSURE_IDX];
+
+ PDEBUG(D_V4L2, "Read exposure %d", *val);
+
+ return 0;
+}
+
+static int vv6410_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+ unsigned int fine, coarse;
+
+ sensor_settings[EXPOSURE_IDX] = val;
+
+ val = (val * val >> 14) + val / 4;
+
+ fine = val % VV6410_CIF_LINELENGTH;
+ coarse = min(512, val / VV6410_CIF_LINELENGTH);
+
+ PDEBUG(D_V4L2, "Set coarse exposure to %d, fine expsure to %d",
+ coarse, fine);
+
+ err = stv06xx_write_sensor(sd, VV6410_FINEH, fine >> 8);
+ if (err < 0)
+ goto out;
+
+ err = stv06xx_write_sensor(sd, VV6410_FINEL, fine & 0xff);
+ if (err < 0)
+ goto out;
+
+ err = stv06xx_write_sensor(sd, VV6410_COARSEH, coarse >> 8);
+ if (err < 0)
+ goto out;
+
+ err = stv06xx_write_sensor(sd, VV6410_COARSEL, coarse & 0xff);
+
+out:
+ return err;
+}
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h
new file mode 100644
index 00000000..a25b8873
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#ifndef STV06XX_VV6410_H_
+#define STV06XX_VV6410_H_
+
+#include "stv06xx_sensor.h"
+
+#define VV6410_COLS 416
+#define VV6410_ROWS 320
+
+/* Status registers */
+/* Chip identification number including revision indicator */
+#define VV6410_DEVICEH 0x00
+#define VV6410_DEVICEL 0x01
+
+/* User can determine whether timed I2C data
+ has been consumed by interrogating flag states */
+#define VV6410_STATUS0 0x02
+
+/* Current line counter value */
+#define VV6410_LINECOUNTH 0x03
+#define VV6410_LINECOUNTL 0x04
+
+/* End x coordinate of image size */
+#define VV6410_XENDH 0x05
+#define VV6410_XENDL 0x06
+
+/* End y coordinate of image size */
+#define VV6410_YENDH 0x07
+#define VV6410_YENDL 0x08
+
+/* This is the average pixel value returned from the
+ dark line offset cancellation algorithm */
+#define VV6410_DARKAVGH 0x09
+#define VV6410_DARKAVGL 0x0a
+
+/* This is the average pixel value returned from the
+ black line offset cancellation algorithm */
+#define VV6410_BLACKAVGH 0x0b
+#define VV6410_BLACKAVGL 0x0c
+
+/* Flags to indicate whether the x or y image coordinates have been clipped */
+#define VV6410_STATUS1 0x0d
+
+/* Setup registers */
+
+/* Low-power/sleep modes & video timing */
+#define VV6410_SETUP0 0x10
+
+/* Various parameters */
+#define VV6410_SETUP1 0x11
+
+/* Contains pixel counter reset value used by external sync */
+#define VV6410_SYNCVALUE 0x12
+
+/* Frame grabbing modes (FST, LST and QCK) */
+#define VV6410_FGMODES 0x14
+
+/* FST and QCK mapping modes. */
+#define VV6410_PINMAPPING 0x15
+
+/* Data resolution */
+#define VV6410_DATAFORMAT 0x16
+
+/* Output coding formats */
+#define VV6410_OPFORMAT 0x17
+
+/* Various mode select bits */
+#define VV6410_MODESELECT 0x18
+
+/* Exposure registers */
+/* Fine exposure. */
+#define VV6410_FINEH 0x20
+#define VV6410_FINEL 0x21
+
+/* Coarse exposure */
+#define VV6410_COARSEH 0x22
+#define VV6410_COARSEL 0x23
+
+/* Analog gain setting */
+#define VV6410_ANALOGGAIN 0x24
+
+/* Clock division */
+#define VV6410_CLKDIV 0x25
+
+/* Dark line offset cancellation value */
+#define VV6410_DARKOFFSETH 0x2c
+#define VV6410_DARKOFFSETL 0x2d
+
+/* Dark line offset cancellation enable */
+#define VV6410_DARKOFFSETSETUP 0x2e
+
+/* Video timing registers */
+/* Line Length (Pixel Clocks) */
+#define VV6410_LINELENGTHH 0x52
+#define VV6410_LINELENGTHL 0x53
+
+/* X-co-ordinate of top left corner of region of interest (x-offset) */
+#define VV6410_XOFFSETH 0x57
+#define VV6410_XOFFSETL 0x58
+
+/* Y-coordinate of top left corner of region of interest (y-offset) */
+#define VV6410_YOFFSETH 0x59
+#define VV6410_YOFFSETL 0x5a
+
+/* Field length (Lines) */
+#define VV6410_FIELDLENGTHH 0x61
+#define VV6410_FIELDLENGTHL 0x62
+
+/* System registers */
+/* Black offset cancellation default value */
+#define VV6410_BLACKOFFSETH 0x70
+#define VV6410_BLACKOFFSETL 0x71
+
+/* Black offset cancellation setup */
+#define VV6410_BLACKOFFSETSETUP 0x72
+
+/* Analog Control Register 0 */
+#define VV6410_CR0 0x75
+
+/* Analog Control Register 1 */
+#define VV6410_CR1 0x76
+
+/* ADC Setup Register */
+#define VV6410_AS0 0x77
+
+/* Analog Test Register */
+#define VV6410_AT0 0x78
+
+/* Audio Amplifier Setup Register */
+#define VV6410_AT1 0x79
+
+#define VV6410_HFLIP (1 << 3)
+#define VV6410_VFLIP (1 << 4)
+
+#define VV6410_LOW_POWER_MODE (1 << 0)
+#define VV6410_SOFT_RESET (1 << 2)
+#define VV6410_PAL_25_FPS (0 << 3)
+
+#define VV6410_CLK_DIV_2 (1 << 1)
+
+#define VV6410_FINE_EXPOSURE 320
+#define VV6410_COARSE_EXPOSURE 192
+#define VV6410_DEFAULT_GAIN 5
+
+#define VV6410_SUBSAMPLE 0x01
+#define VV6410_CROP_TO_QVGA 0x02
+
+#define VV6410_CIF_LINELENGTH 415
+
+static int vv6410_probe(struct sd *sd);
+static int vv6410_start(struct sd *sd);
+static int vv6410_init(struct sd *sd);
+static int vv6410_stop(struct sd *sd);
+static int vv6410_dump(struct sd *sd);
+static void vv6410_disconnect(struct sd *sd);
+
+/* V4L2 controls supported by the driver */
+static int vv6410_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
+static int vv6410_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
+static int vv6410_get_analog_gain(struct gspca_dev *gspca_dev, __s32 *val);
+static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val);
+static int vv6410_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
+static int vv6410_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
+
+const struct stv06xx_sensor stv06xx_sensor_vv6410 = {
+ .name = "ST VV6410",
+ .i2c_flush = 5,
+ .i2c_addr = 0x20,
+ .i2c_len = 1,
+ /* FIXME (see if we can lower packet_size-s, needs testing, and also
+ adjusting framerate when the bandwidth gets lower) */
+ .min_packet_size = { 1023 },
+ .max_packet_size = { 1023 },
+ .init = vv6410_init,
+ .probe = vv6410_probe,
+ .start = vv6410_start,
+ .stop = vv6410_stop,
+ .dump = vv6410_dump,
+ .disconnect = vv6410_disconnect,
+};
+
+/* If NULL, only single value to write, stored in len */
+struct stv_init {
+ u16 addr;
+ u8 data;
+};
+
+static const struct stv_init stv_bridge_init[] = {
+ /* This reg is written twice. Some kind of reset? */
+ {STV_RESET, 0x80},
+ {STV_RESET, 0x00},
+ {STV_SCAN_RATE, 0x00},
+ {STV_I2C_FLUSH, 0x04},
+ {STV_REG00, 0x0b},
+ {STV_REG01, 0xa7},
+ {STV_REG02, 0xb7},
+ {STV_REG03, 0x00},
+ {STV_REG04, 0x00},
+ {0x1536, 0x02},
+ {0x1537, 0x00},
+ {0x1538, 0x60},
+ {0x1539, 0x01},
+ {0x153a, 0x20},
+ {0x153b, 0x01},
+};
+
+static const u8 vv6410_sensor_init[][2] = {
+ /* Setup registers */
+ {VV6410_SETUP0, VV6410_SOFT_RESET},
+ {VV6410_SETUP0, VV6410_LOW_POWER_MODE},
+ /* Use shuffled read-out mode */
+ {VV6410_SETUP1, BIT(6)},
+ /* All modes to 1, FST, Fast QCK, Free running QCK, Free running LST, FST will qualify visible pixels */
+ {VV6410_FGMODES, BIT(6) | BIT(4) | BIT(2) | BIT(0)},
+ {VV6410_PINMAPPING, 0x00},
+ /* Pre-clock generator divide off */
+ {VV6410_DATAFORMAT, BIT(7) | BIT(0)},
+
+ {VV6410_CLKDIV, VV6410_CLK_DIV_2},
+
+ /* System registers */
+ /* Enable voltage doubler */
+ {VV6410_AS0, BIT(6) | BIT(4) | BIT(3) | BIT(2) | BIT(1)},
+ {VV6410_AT0, 0x00},
+ /* Power up audio, differential */
+ {VV6410_AT1, BIT(4) | BIT(0)},
+};
+
+#endif
diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c
new file mode 100644
index 00000000..c80f0c0c
--- /dev/null
+++ b/drivers/media/video/gspca/sunplus.c
@@ -0,0 +1,1214 @@
+/*
+ * Sunplus spca504(abc) spca533 spca536 library
+ * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "sunplus"
+
+#include "gspca.h"
+#include "jpeg.h"
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("GSPCA/SPCA5xx USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ s8 brightness;
+ u8 contrast;
+ u8 colors;
+ u8 autogain;
+ u8 quality;
+#define QUALITY_MIN 70
+#define QUALITY_MAX 95
+#define QUALITY_DEF 85
+
+ u8 bridge;
+#define BRIDGE_SPCA504 0
+#define BRIDGE_SPCA504B 1
+#define BRIDGE_SPCA504C 2
+#define BRIDGE_SPCA533 3
+#define BRIDGE_SPCA536 4
+ u8 subtype;
+#define AiptekMiniPenCam13 1
+#define LogitechClickSmart420 2
+#define LogitechClickSmart820 3
+#define MegapixV4 4
+#define MegaImageVI 5
+
+ u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+
+static const struct ctrl sd_ctrls[] = {
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = -128,
+ .maximum = 127,
+ .step = 1,
+#define BRIGHTNESS_DEF 0
+ .default_value = BRIGHTNESS_DEF,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+ {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+#define CONTRAST_DEF 0x20
+ .default_value = CONTRAST_DEF,
+ },
+ .set = sd_setcontrast,
+ .get = sd_getcontrast,
+ },
+ {
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Color",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+#define COLOR_DEF 0x1a
+ .default_value = COLOR_DEF,
+ },
+ .set = sd_setcolors,
+ .get = sd_getcolors,
+ },
+ {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto Gain",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define AUTOGAIN_DEF 1
+ .default_value = AUTOGAIN_DEF,
+ },
+ .set = sd_setautogain,
+ .get = sd_getautogain,
+ },
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 2},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+};
+
+static const struct v4l2_pix_format custom_mode[] = {
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 2},
+ {464, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 464,
+ .sizeimage = 464 * 480 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+};
+
+static const struct v4l2_pix_format vga_mode2[] = {
+ {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 4},
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 3},
+ {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 2},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+};
+
+#define SPCA50X_OFFSET_DATA 10
+#define SPCA504_PCCAM600_OFFSET_SNAPSHOT 3
+#define SPCA504_PCCAM600_OFFSET_COMPRESS 4
+#define SPCA504_PCCAM600_OFFSET_MODE 5
+#define SPCA504_PCCAM600_OFFSET_DATA 14
+ /* Frame packet header offsets for the spca533 */
+#define SPCA533_OFFSET_DATA 16
+#define SPCA533_OFFSET_FRAMSEQ 15
+/* Frame packet header offsets for the spca536 */
+#define SPCA536_OFFSET_DATA 4
+#define SPCA536_OFFSET_FRAMSEQ 1
+
+struct cmd {
+ u8 req;
+ u16 val;
+ u16 idx;
+};
+
+/* Initialisation data for the Creative PC-CAM 600 */
+static const struct cmd spca504_pccam600_init_data[] = {
+/* {0xa0, 0x0000, 0x0503}, * capture mode */
+ {0x00, 0x0000, 0x2000},
+ {0x00, 0x0013, 0x2301},
+ {0x00, 0x0003, 0x2000},
+ {0x00, 0x0001, 0x21ac},
+ {0x00, 0x0001, 0x21a6},
+ {0x00, 0x0000, 0x21a7}, /* brightness */
+ {0x00, 0x0020, 0x21a8}, /* contrast */
+ {0x00, 0x0001, 0x21ac}, /* sat/hue */
+ {0x00, 0x0000, 0x21ad}, /* hue */
+ {0x00, 0x001a, 0x21ae}, /* saturation */
+ {0x00, 0x0002, 0x21a3}, /* gamma */
+ {0x30, 0x0154, 0x0008},
+ {0x30, 0x0004, 0x0006},
+ {0x30, 0x0258, 0x0009},
+ {0x30, 0x0004, 0x0000},
+ {0x30, 0x0093, 0x0004},
+ {0x30, 0x0066, 0x0005},
+ {0x00, 0x0000, 0x2000},
+ {0x00, 0x0013, 0x2301},
+ {0x00, 0x0003, 0x2000},
+ {0x00, 0x0013, 0x2301},
+ {0x00, 0x0003, 0x2000},
+};
+
+/* Creative PC-CAM 600 specific open data, sent before using the
+ * generic initialisation data from spca504_open_data.
+ */
+static const struct cmd spca504_pccam600_open_data[] = {
+ {0x00, 0x0001, 0x2501},
+ {0x20, 0x0500, 0x0001}, /* snapshot mode */
+ {0x00, 0x0003, 0x2880},
+ {0x00, 0x0001, 0x2881},
+};
+
+/* Initialisation data for the logitech clicksmart 420 */
+static const struct cmd spca504A_clicksmart420_init_data[] = {
+/* {0xa0, 0x0000, 0x0503}, * capture mode */
+ {0x00, 0x0000, 0x2000},
+ {0x00, 0x0013, 0x2301},
+ {0x00, 0x0003, 0x2000},
+ {0x00, 0x0001, 0x21ac},
+ {0x00, 0x0001, 0x21a6},
+ {0x00, 0x0000, 0x21a7}, /* brightness */
+ {0x00, 0x0020, 0x21a8}, /* contrast */
+ {0x00, 0x0001, 0x21ac}, /* sat/hue */
+ {0x00, 0x0000, 0x21ad}, /* hue */
+ {0x00, 0x001a, 0x21ae}, /* saturation */
+ {0x00, 0x0002, 0x21a3}, /* gamma */
+ {0x30, 0x0004, 0x000a},
+ {0xb0, 0x0001, 0x0000},
+
+ {0xa1, 0x0080, 0x0001},
+ {0x30, 0x0049, 0x0000},
+ {0x30, 0x0060, 0x0005},
+ {0x0c, 0x0004, 0x0000},
+ {0x00, 0x0000, 0x0000},
+ {0x00, 0x0000, 0x2000},
+ {0x00, 0x0013, 0x2301},
+ {0x00, 0x0003, 0x2000},
+};
+
+/* clicksmart 420 open data ? */
+static const struct cmd spca504A_clicksmart420_open_data[] = {
+ {0x00, 0x0001, 0x2501},
+ {0x20, 0x0502, 0x0000},
+ {0x06, 0x0000, 0x0000},
+ {0x00, 0x0004, 0x2880},
+ {0x00, 0x0001, 0x2881},
+
+ {0xa0, 0x0000, 0x0503},
+};
+
+static const u8 qtable_creative_pccam[2][64] = {
+ { /* Q-table Y-components */
+ 0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12,
+ 0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11,
+ 0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11,
+ 0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13,
+ 0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17,
+ 0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c,
+ 0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e,
+ 0x16, 0x1c, 0x1d, 0x1d, 0x22, 0x1e, 0x1f, 0x1e},
+ { /* Q-table C-components */
+ 0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e}
+};
+
+/* FIXME: This Q-table is identical to the Creative PC-CAM one,
+ * except for one byte. Possibly a typo?
+ * NWG: 18/05/2003.
+ */
+static const u8 qtable_spca504_default[2][64] = {
+ { /* Q-table Y-components */
+ 0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12,
+ 0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11,
+ 0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11,
+ 0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13,
+ 0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17,
+ 0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c,
+ 0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e,
+ 0x16, 0x1c, 0x1d, 0x1d, 0x1d /* 0x22 */ , 0x1e, 0x1f, 0x1e,
+ },
+ { /* Q-table C-components */
+ 0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
+ 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e}
+};
+
+/* read <len> bytes to gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev,
+ u8 req,
+ u16 index,
+ u16 len)
+{
+ int ret;
+
+#ifdef GSPCA_DEBUG
+ if (len > USB_BUF_SZ) {
+ pr_err("reg_r: buffer overflow\n");
+ return;
+ }
+#endif
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ req,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, /* value */
+ index,
+ len ? gspca_dev->usb_buf : NULL, len,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_r err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+/* write one byte */
+static void reg_w_1(struct gspca_dev *gspca_dev,
+ u8 req,
+ u16 value,
+ u16 index,
+ u16 byte)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ gspca_dev->usb_buf[0] = byte;
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ req,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index,
+ gspca_dev->usb_buf, 1,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_w_1 err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+/* write req / index / value */
+static void reg_w_riv(struct gspca_dev *gspca_dev,
+ u8 req, u16 index, u16 value)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(dev,
+ usb_sndctrlpipe(dev, 0),
+ req,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0, 500);
+ if (ret < 0) {
+ pr_err("reg_w_riv err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ return;
+ }
+ PDEBUG(D_USBO, "reg_w_riv: 0x%02x,0x%04x:0x%04x",
+ req, index, value);
+}
+
+static void write_vector(struct gspca_dev *gspca_dev,
+ const struct cmd *data, int ncmds)
+{
+ while (--ncmds >= 0) {
+ reg_w_riv(gspca_dev, data->req, data->idx, data->val);
+ data++;
+ }
+}
+
+static void setup_qtable(struct gspca_dev *gspca_dev,
+ const u8 qtable[2][64])
+{
+ int i;
+
+ /* loop over y components */
+ for (i = 0; i < 64; i++)
+ reg_w_riv(gspca_dev, 0x00, 0x2800 + i, qtable[0][i]);
+
+ /* loop over c components */
+ for (i = 0; i < 64; i++)
+ reg_w_riv(gspca_dev, 0x00, 0x2840 + i, qtable[1][i]);
+}
+
+static void spca504_acknowledged_command(struct gspca_dev *gspca_dev,
+ u8 req, u16 idx, u16 val)
+{
+ reg_w_riv(gspca_dev, req, idx, val);
+ reg_r(gspca_dev, 0x01, 0x0001, 1);
+ PDEBUG(D_FRAM, "before wait 0x%04x", gspca_dev->usb_buf[0]);
+ reg_w_riv(gspca_dev, req, idx, val);
+
+ msleep(200);
+ reg_r(gspca_dev, 0x01, 0x0001, 1);
+ PDEBUG(D_FRAM, "after wait 0x%04x", gspca_dev->usb_buf[0]);
+}
+
+#ifdef GSPCA_DEBUG
+static void spca504_read_info(struct gspca_dev *gspca_dev)
+{
+ int i;
+ u8 info[6];
+
+ for (i = 0; i < 6; i++) {
+ reg_r(gspca_dev, 0, i, 1);
+ info[i] = gspca_dev->usb_buf[0];
+ }
+ PDEBUG(D_STREAM,
+ "Read info: %d %d %d %d %d %d."
+ " Should be 1,0,2,2,0,0",
+ info[0], info[1], info[2],
+ info[3], info[4], info[5]);
+}
+#endif
+
+static void spca504A_acknowledged_command(struct gspca_dev *gspca_dev,
+ u8 req,
+ u16 idx, u16 val, u8 endcode, u8 count)
+{
+ u16 status;
+
+ reg_w_riv(gspca_dev, req, idx, val);
+ reg_r(gspca_dev, 0x01, 0x0001, 1);
+ if (gspca_dev->usb_err < 0)
+ return;
+ PDEBUG(D_FRAM, "Status 0x%02x Need 0x%02x",
+ gspca_dev->usb_buf[0], endcode);
+ if (!count)
+ return;
+ count = 200;
+ while (--count > 0) {
+ msleep(10);
+ /* gsmart mini2 write a each wait setting 1 ms is enough */
+/* reg_w_riv(gspca_dev, req, idx, val); */
+ reg_r(gspca_dev, 0x01, 0x0001, 1);
+ status = gspca_dev->usb_buf[0];
+ if (status == endcode) {
+ PDEBUG(D_FRAM, "status 0x%04x after wait %d",
+ status, 200 - count);
+ break;
+ }
+ }
+}
+
+static void spca504B_PollingDataReady(struct gspca_dev *gspca_dev)
+{
+ int count = 10;
+
+ while (--count > 0) {
+ reg_r(gspca_dev, 0x21, 0, 1);
+ if ((gspca_dev->usb_buf[0] & 0x01) == 0)
+ break;
+ msleep(10);
+ }
+}
+
+static void spca504B_WaitCmdStatus(struct gspca_dev *gspca_dev)
+{
+ int count = 50;
+
+ while (--count > 0) {
+ reg_r(gspca_dev, 0x21, 1, 1);
+ if (gspca_dev->usb_buf[0] != 0) {
+ reg_w_1(gspca_dev, 0x21, 0, 1, 0);
+ reg_r(gspca_dev, 0x21, 1, 1);
+ spca504B_PollingDataReady(gspca_dev);
+ break;
+ }
+ msleep(10);
+ }
+}
+
+#ifdef GSPCA_DEBUG
+static void spca50x_GetFirmware(struct gspca_dev *gspca_dev)
+{
+ u8 *data;
+
+ data = gspca_dev->usb_buf;
+ reg_r(gspca_dev, 0x20, 0, 5);
+ PDEBUG(D_STREAM, "FirmWare: %d %d %d %d %d",
+ data[0], data[1], data[2], data[3], data[4]);
+ reg_r(gspca_dev, 0x23, 0, 64);
+ reg_r(gspca_dev, 0x23, 1, 64);
+}
+#endif
+
+static void spca504B_SetSizeType(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 Size;
+
+ Size = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+ switch (sd->bridge) {
+ case BRIDGE_SPCA533:
+ reg_w_riv(gspca_dev, 0x31, 0, 0);
+ spca504B_WaitCmdStatus(gspca_dev);
+ spca504B_PollingDataReady(gspca_dev);
+#ifdef GSPCA_DEBUG
+ spca50x_GetFirmware(gspca_dev);
+#endif
+ reg_w_1(gspca_dev, 0x24, 0, 8, 2); /* type */
+ reg_r(gspca_dev, 0x24, 8, 1);
+
+ reg_w_1(gspca_dev, 0x25, 0, 4, Size);
+ reg_r(gspca_dev, 0x25, 4, 1); /* size */
+ spca504B_PollingDataReady(gspca_dev);
+
+ /* Init the cam width height with some values get on init ? */
+ reg_w_riv(gspca_dev, 0x31, 0x0004, 0x00);
+ spca504B_WaitCmdStatus(gspca_dev);
+ spca504B_PollingDataReady(gspca_dev);
+ break;
+ default:
+/* case BRIDGE_SPCA504B: */
+/* case BRIDGE_SPCA536: */
+ reg_w_1(gspca_dev, 0x25, 0, 4, Size);
+ reg_r(gspca_dev, 0x25, 4, 1); /* size */
+ reg_w_1(gspca_dev, 0x27, 0, 0, 6);
+ reg_r(gspca_dev, 0x27, 0, 1); /* type */
+ spca504B_PollingDataReady(gspca_dev);
+ break;
+ case BRIDGE_SPCA504:
+ Size += 3;
+ if (sd->subtype == AiptekMiniPenCam13) {
+ /* spca504a aiptek */
+ spca504A_acknowledged_command(gspca_dev,
+ 0x08, Size, 0,
+ 0x80 | (Size & 0x0f), 1);
+ spca504A_acknowledged_command(gspca_dev,
+ 1, 3, 0, 0x9f, 0);
+ } else {
+ spca504_acknowledged_command(gspca_dev, 0x08, Size, 0);
+ }
+ break;
+ case BRIDGE_SPCA504C:
+ /* capture mode */
+ reg_w_riv(gspca_dev, 0xa0, (0x0500 | (Size & 0x0f)), 0x00);
+ reg_w_riv(gspca_dev, 0x20, 0x01, 0x0500 | (Size & 0x0f));
+ break;
+ }
+}
+
+static void spca504_wait_status(struct gspca_dev *gspca_dev)
+{
+ int cnt;
+
+ cnt = 256;
+ while (--cnt > 0) {
+ /* With this we get the status, when return 0 it's all ok */
+ reg_r(gspca_dev, 0x06, 0x00, 1);
+ if (gspca_dev->usb_buf[0] == 0)
+ return;
+ msleep(10);
+ }
+}
+
+static void spca504B_setQtable(struct gspca_dev *gspca_dev)
+{
+ reg_w_1(gspca_dev, 0x26, 0, 0, 3);
+ reg_r(gspca_dev, 0x26, 0, 1);
+ spca504B_PollingDataReady(gspca_dev);
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 reg;
+
+ reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f0 : 0x21a7;
+ reg_w_riv(gspca_dev, 0x00, reg, sd->brightness);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 reg;
+
+ reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f1 : 0x21a8;
+ reg_w_riv(gspca_dev, 0x00, reg, sd->contrast);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 reg;
+
+ reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f6 : 0x21ae;
+ reg_w_riv(gspca_dev, 0x00, reg, sd->colors);
+}
+
+static void init_ctl_reg(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int pollreg = 1;
+
+ setbrightness(gspca_dev);
+ setcontrast(gspca_dev);
+ setcolors(gspca_dev);
+
+ switch (sd->bridge) {
+ case BRIDGE_SPCA504:
+ case BRIDGE_SPCA504C:
+ pollreg = 0;
+ /* fall thru */
+ default:
+/* case BRIDGE_SPCA533: */
+/* case BRIDGE_SPCA504B: */
+ reg_w_riv(gspca_dev, 0, 0x21ad, 0x00); /* hue */
+ reg_w_riv(gspca_dev, 0, 0x21ac, 0x01); /* sat/hue */
+ reg_w_riv(gspca_dev, 0, 0x21a3, 0x00); /* gamma */
+ break;
+ case BRIDGE_SPCA536:
+ reg_w_riv(gspca_dev, 0, 0x20f5, 0x40);
+ reg_w_riv(gspca_dev, 0, 0x20f4, 0x01);
+ reg_w_riv(gspca_dev, 0, 0x2089, 0x00);
+ break;
+ }
+ if (pollreg)
+ spca504B_PollingDataReady(gspca_dev);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+
+ sd->bridge = id->driver_info >> 8;
+ sd->subtype = id->driver_info;
+
+ if (sd->subtype == AiptekMiniPenCam13) {
+
+ /* try to get the firmware as some cam answer 2.0.1.2.2
+ * and should be a spca504b then overwrite that setting */
+ reg_r(gspca_dev, 0x20, 0, 1);
+ switch (gspca_dev->usb_buf[0]) {
+ case 1:
+ break; /* (right bridge/subtype) */
+ case 2:
+ sd->bridge = BRIDGE_SPCA504B;
+ sd->subtype = 0;
+ break;
+ default:
+ return -ENODEV;
+ }
+ }
+
+ switch (sd->bridge) {
+ default:
+/* case BRIDGE_SPCA504B: */
+/* case BRIDGE_SPCA504: */
+/* case BRIDGE_SPCA536: */
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+ break;
+ case BRIDGE_SPCA533:
+ cam->cam_mode = custom_mode;
+ if (sd->subtype == MegaImageVI) /* 320x240 only */
+ cam->nmodes = ARRAY_SIZE(custom_mode) - 1;
+ else
+ cam->nmodes = ARRAY_SIZE(custom_mode);
+ break;
+ case BRIDGE_SPCA504C:
+ cam->cam_mode = vga_mode2;
+ cam->nmodes = ARRAY_SIZE(vga_mode2);
+ break;
+ }
+ sd->brightness = BRIGHTNESS_DEF;
+ sd->contrast = CONTRAST_DEF;
+ sd->colors = COLOR_DEF;
+ sd->autogain = AUTOGAIN_DEF;
+ sd->quality = QUALITY_DEF;
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->bridge) {
+ case BRIDGE_SPCA504B:
+ reg_w_riv(gspca_dev, 0x1d, 0x00, 0);
+ reg_w_riv(gspca_dev, 0x00, 0x2306, 0x01);
+ reg_w_riv(gspca_dev, 0x00, 0x0d04, 0x00);
+ reg_w_riv(gspca_dev, 0x00, 0x2000, 0x00);
+ reg_w_riv(gspca_dev, 0x00, 0x2301, 0x13);
+ reg_w_riv(gspca_dev, 0x00, 0x2306, 0x00);
+ /* fall thru */
+ case BRIDGE_SPCA533:
+ spca504B_PollingDataReady(gspca_dev);
+#ifdef GSPCA_DEBUG
+ spca50x_GetFirmware(gspca_dev);
+#endif
+ break;
+ case BRIDGE_SPCA536:
+#ifdef GSPCA_DEBUG
+ spca50x_GetFirmware(gspca_dev);
+#endif
+ reg_r(gspca_dev, 0x00, 0x5002, 1);
+ reg_w_1(gspca_dev, 0x24, 0, 0, 0);
+ reg_r(gspca_dev, 0x24, 0, 1);
+ spca504B_PollingDataReady(gspca_dev);
+ reg_w_riv(gspca_dev, 0x34, 0, 0);
+ spca504B_WaitCmdStatus(gspca_dev);
+ break;
+ case BRIDGE_SPCA504C: /* pccam600 */
+ PDEBUG(D_STREAM, "Opening SPCA504 (PC-CAM 600)");
+ reg_w_riv(gspca_dev, 0xe0, 0x0000, 0x0000);
+ reg_w_riv(gspca_dev, 0xe0, 0x0000, 0x0001); /* reset */
+ spca504_wait_status(gspca_dev);
+ if (sd->subtype == LogitechClickSmart420)
+ write_vector(gspca_dev,
+ spca504A_clicksmart420_open_data,
+ ARRAY_SIZE(spca504A_clicksmart420_open_data));
+ else
+ write_vector(gspca_dev, spca504_pccam600_open_data,
+ ARRAY_SIZE(spca504_pccam600_open_data));
+ setup_qtable(gspca_dev, qtable_creative_pccam);
+ break;
+ default:
+/* case BRIDGE_SPCA504: */
+ PDEBUG(D_STREAM, "Opening SPCA504");
+ if (sd->subtype == AiptekMiniPenCam13) {
+#ifdef GSPCA_DEBUG
+ spca504_read_info(gspca_dev);
+#endif
+
+ /* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */
+ spca504A_acknowledged_command(gspca_dev, 0x24,
+ 8, 3, 0x9e, 1);
+ /* Twice sequential need status 0xff->0x9e->0x9d */
+ spca504A_acknowledged_command(gspca_dev, 0x24,
+ 8, 3, 0x9e, 0);
+
+ spca504A_acknowledged_command(gspca_dev, 0x24,
+ 0, 0, 0x9d, 1);
+ /******************************/
+ /* spca504a aiptek */
+ spca504A_acknowledged_command(gspca_dev, 0x08,
+ 6, 0, 0x86, 1);
+/* reg_write (dev, 0, 0x2000, 0); */
+/* reg_write (dev, 0, 0x2883, 1); */
+/* spca504A_acknowledged_command (gspca_dev, 0x08,
+ 6, 0, 0x86, 1); */
+/* spca504A_acknowledged_command (gspca_dev, 0x24,
+ 0, 0, 0x9D, 1); */
+ reg_w_riv(gspca_dev, 0x00, 0x270c, 0x05);
+ /* L92 sno1t.txt */
+ reg_w_riv(gspca_dev, 0x00, 0x2310, 0x05);
+ spca504A_acknowledged_command(gspca_dev, 0x01,
+ 0x0f, 0, 0xff, 0);
+ }
+ /* setup qtable */
+ reg_w_riv(gspca_dev, 0, 0x2000, 0);
+ reg_w_riv(gspca_dev, 0, 0x2883, 1);
+ setup_qtable(gspca_dev, qtable_spca504_default);
+ break;
+ }
+ return gspca_dev->usb_err;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int enable;
+
+ /* create the JPEG header */
+ jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
+ 0x22); /* JPEG 411 */
+ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+
+ if (sd->bridge == BRIDGE_SPCA504B)
+ spca504B_setQtable(gspca_dev);
+ spca504B_SetSizeType(gspca_dev);
+ switch (sd->bridge) {
+ default:
+/* case BRIDGE_SPCA504B: */
+/* case BRIDGE_SPCA533: */
+/* case BRIDGE_SPCA536: */
+ switch (sd->subtype) {
+ case MegapixV4:
+ case LogitechClickSmart820:
+ case MegaImageVI:
+ reg_w_riv(gspca_dev, 0xf0, 0, 0);
+ spca504B_WaitCmdStatus(gspca_dev);
+ reg_r(gspca_dev, 0xf0, 4, 0);
+ spca504B_WaitCmdStatus(gspca_dev);
+ break;
+ default:
+ reg_w_riv(gspca_dev, 0x31, 0x0004, 0x00);
+ spca504B_WaitCmdStatus(gspca_dev);
+ spca504B_PollingDataReady(gspca_dev);
+ break;
+ }
+ break;
+ case BRIDGE_SPCA504:
+ if (sd->subtype == AiptekMiniPenCam13) {
+#ifdef GSPCA_DEBUG
+ spca504_read_info(gspca_dev);
+#endif
+
+ /* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */
+ spca504A_acknowledged_command(gspca_dev, 0x24,
+ 8, 3, 0x9e, 1);
+ /* Twice sequential need status 0xff->0x9e->0x9d */
+ spca504A_acknowledged_command(gspca_dev, 0x24,
+ 8, 3, 0x9e, 0);
+ spca504A_acknowledged_command(gspca_dev, 0x24,
+ 0, 0, 0x9d, 1);
+ } else {
+ spca504_acknowledged_command(gspca_dev, 0x24, 8, 3);
+#ifdef GSPCA_DEBUG
+ spca504_read_info(gspca_dev);
+#endif
+ spca504_acknowledged_command(gspca_dev, 0x24, 8, 3);
+ spca504_acknowledged_command(gspca_dev, 0x24, 0, 0);
+ }
+ spca504B_SetSizeType(gspca_dev);
+ reg_w_riv(gspca_dev, 0x00, 0x270c, 0x05);
+ /* L92 sno1t.txt */
+ reg_w_riv(gspca_dev, 0x00, 0x2310, 0x05);
+ break;
+ case BRIDGE_SPCA504C:
+ if (sd->subtype == LogitechClickSmart420) {
+ write_vector(gspca_dev,
+ spca504A_clicksmart420_init_data,
+ ARRAY_SIZE(spca504A_clicksmart420_init_data));
+ } else {
+ write_vector(gspca_dev, spca504_pccam600_init_data,
+ ARRAY_SIZE(spca504_pccam600_init_data));
+ }
+ enable = (sd->autogain ? 0x04 : 0x01);
+ reg_w_riv(gspca_dev, 0x0c, 0x0000, enable);
+ /* auto exposure */
+ reg_w_riv(gspca_dev, 0xb0, 0x0000, enable);
+ /* auto whiteness */
+
+ /* set default exposure compensation and whiteness balance */
+ reg_w_riv(gspca_dev, 0x30, 0x0001, 800); /* ~ 20 fps */
+ reg_w_riv(gspca_dev, 0x30, 0x0002, 1600);
+ spca504B_SetSizeType(gspca_dev);
+ break;
+ }
+ init_ctl_reg(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->bridge) {
+ default:
+/* case BRIDGE_SPCA533: */
+/* case BRIDGE_SPCA536: */
+/* case BRIDGE_SPCA504B: */
+ reg_w_riv(gspca_dev, 0x31, 0, 0);
+ spca504B_WaitCmdStatus(gspca_dev);
+ spca504B_PollingDataReady(gspca_dev);
+ break;
+ case BRIDGE_SPCA504:
+ case BRIDGE_SPCA504C:
+ reg_w_riv(gspca_dev, 0x00, 0x2000, 0x0000);
+
+ if (sd->subtype == AiptekMiniPenCam13) {
+ /* spca504a aiptek */
+/* spca504A_acknowledged_command(gspca_dev, 0x08,
+ 6, 0, 0x86, 1); */
+ spca504A_acknowledged_command(gspca_dev, 0x24,
+ 0x00, 0x00, 0x9d, 1);
+ spca504A_acknowledged_command(gspca_dev, 0x01,
+ 0x0f, 0x00, 0xff, 1);
+ } else {
+ spca504_acknowledged_command(gspca_dev, 0x24, 0, 0);
+ reg_w_riv(gspca_dev, 0x01, 0x000f, 0x0000);
+ }
+ break;
+ }
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, sof = 0;
+ static u8 ffd9[] = {0xff, 0xd9};
+
+/* frames are jpeg 4.1.1 without 0xff escape */
+ switch (sd->bridge) {
+ case BRIDGE_SPCA533:
+ if (data[0] == 0xff) {
+ if (data[1] != 0x01) { /* drop packet */
+/* gspca_dev->last_packet_type = DISCARD_PACKET; */
+ return;
+ }
+ sof = 1;
+ data += SPCA533_OFFSET_DATA;
+ len -= SPCA533_OFFSET_DATA;
+ } else {
+ data += 1;
+ len -= 1;
+ }
+ break;
+ case BRIDGE_SPCA536:
+ if (data[0] == 0xff) {
+ sof = 1;
+ data += SPCA536_OFFSET_DATA;
+ len -= SPCA536_OFFSET_DATA;
+ } else {
+ data += 2;
+ len -= 2;
+ }
+ break;
+ default:
+/* case BRIDGE_SPCA504: */
+/* case BRIDGE_SPCA504B: */
+ switch (data[0]) {
+ case 0xfe: /* start of frame */
+ sof = 1;
+ data += SPCA50X_OFFSET_DATA;
+ len -= SPCA50X_OFFSET_DATA;
+ break;
+ case 0xff: /* drop packet */
+/* gspca_dev->last_packet_type = DISCARD_PACKET; */
+ return;
+ default:
+ data += 1;
+ len -= 1;
+ break;
+ }
+ break;
+ case BRIDGE_SPCA504C:
+ switch (data[0]) {
+ case 0xfe: /* start of frame */
+ sof = 1;
+ data += SPCA504_PCCAM600_OFFSET_DATA;
+ len -= SPCA504_PCCAM600_OFFSET_DATA;
+ break;
+ case 0xff: /* drop packet */
+/* gspca_dev->last_packet_type = DISCARD_PACKET; */
+ return;
+ default:
+ data += 1;
+ len -= 1;
+ break;
+ }
+ break;
+ }
+ if (sof) { /* start of frame */
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ ffd9, 2);
+
+ /* put the JPEG header in the new frame */
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+ }
+
+ /* add 0x00 after 0xff */
+ i = 0;
+ do {
+ if (data[i] == 0xff) {
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data, i + 1);
+ len -= i;
+ data += i;
+ *data = 0x00;
+ i = 0;
+ }
+ i++;
+ } while (i < len);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->brightness = val;
+ if (gspca_dev->streaming)
+ setbrightness(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->brightness;
+ return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->contrast = val;
+ if (gspca_dev->streaming)
+ setcontrast(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->contrast;
+ return 0;
+}
+
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->colors = val;
+ if (gspca_dev->streaming)
+ setcolors(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->colors;
+ return 0;
+}
+
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->autogain = val;
+ return 0;
+}
+
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->autogain;
+ return 0;
+}
+
+static int sd_set_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (jcomp->quality < QUALITY_MIN)
+ sd->quality = QUALITY_MIN;
+ else if (jcomp->quality > QUALITY_MAX)
+ sd->quality = QUALITY_MAX;
+ else
+ sd->quality = jcomp->quality;
+ if (gspca_dev->streaming)
+ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+ return gspca_dev->usb_err;
+}
+
+static int sd_get_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ memset(jcomp, 0, sizeof *jcomp);
+ jcomp->quality = sd->quality;
+ jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
+ | V4L2_JPEG_MARKER_DQT;
+ return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+ .get_jcomp = sd_get_jcomp,
+ .set_jcomp = sd_set_jcomp,
+};
+
+/* -- module initialisation -- */
+#define BS(bridge, subtype) \
+ .driver_info = (BRIDGE_ ## bridge << 8) \
+ | (subtype)
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x041e, 0x400b), BS(SPCA504C, 0)},
+ {USB_DEVICE(0x041e, 0x4012), BS(SPCA504C, 0)},
+ {USB_DEVICE(0x041e, 0x4013), BS(SPCA504C, 0)},
+ {USB_DEVICE(0x0458, 0x7006), BS(SPCA504B, 0)},
+ {USB_DEVICE(0x0461, 0x0821), BS(SPCA533, 0)},
+ {USB_DEVICE(0x046d, 0x0905), BS(SPCA533, LogitechClickSmart820)},
+ {USB_DEVICE(0x046d, 0x0960), BS(SPCA504C, LogitechClickSmart420)},
+ {USB_DEVICE(0x0471, 0x0322), BS(SPCA504B, 0)},
+ {USB_DEVICE(0x04a5, 0x3003), BS(SPCA504B, 0)},
+ {USB_DEVICE(0x04a5, 0x3008), BS(SPCA533, 0)},
+ {USB_DEVICE(0x04a5, 0x300a), BS(SPCA533, 0)},
+ {USB_DEVICE(0x04f1, 0x1001), BS(SPCA504B, 0)},
+ {USB_DEVICE(0x04fc, 0x500c), BS(SPCA504B, 0)},
+ {USB_DEVICE(0x04fc, 0x504a), BS(SPCA504, AiptekMiniPenCam13)},
+ {USB_DEVICE(0x04fc, 0x504b), BS(SPCA504B, 0)},
+ {USB_DEVICE(0x04fc, 0x5330), BS(SPCA533, 0)},
+ {USB_DEVICE(0x04fc, 0x5360), BS(SPCA536, 0)},
+ {USB_DEVICE(0x04fc, 0xffff), BS(SPCA504B, 0)},
+ {USB_DEVICE(0x052b, 0x1507), BS(SPCA533, MegapixV4)},
+ {USB_DEVICE(0x052b, 0x1513), BS(SPCA533, MegapixV4)},
+ {USB_DEVICE(0x052b, 0x1803), BS(SPCA533, MegaImageVI)},
+ {USB_DEVICE(0x0546, 0x3155), BS(SPCA533, 0)},
+ {USB_DEVICE(0x0546, 0x3191), BS(SPCA504B, 0)},
+ {USB_DEVICE(0x0546, 0x3273), BS(SPCA504B, 0)},
+ {USB_DEVICE(0x055f, 0xc211), BS(SPCA536, 0)},
+ {USB_DEVICE(0x055f, 0xc230), BS(SPCA533, 0)},
+ {USB_DEVICE(0x055f, 0xc232), BS(SPCA533, 0)},
+ {USB_DEVICE(0x055f, 0xc360), BS(SPCA536, 0)},
+ {USB_DEVICE(0x055f, 0xc420), BS(SPCA504, 0)},
+ {USB_DEVICE(0x055f, 0xc430), BS(SPCA533, 0)},
+ {USB_DEVICE(0x055f, 0xc440), BS(SPCA533, 0)},
+ {USB_DEVICE(0x055f, 0xc520), BS(SPCA504, 0)},
+ {USB_DEVICE(0x055f, 0xc530), BS(SPCA533, 0)},
+ {USB_DEVICE(0x055f, 0xc540), BS(SPCA533, 0)},
+ {USB_DEVICE(0x055f, 0xc630), BS(SPCA533, 0)},
+ {USB_DEVICE(0x055f, 0xc650), BS(SPCA533, 0)},
+ {USB_DEVICE(0x05da, 0x1018), BS(SPCA504B, 0)},
+ {USB_DEVICE(0x06d6, 0x0031), BS(SPCA533, 0)},
+ {USB_DEVICE(0x0733, 0x1311), BS(SPCA533, 0)},
+ {USB_DEVICE(0x0733, 0x1314), BS(SPCA533, 0)},
+ {USB_DEVICE(0x0733, 0x2211), BS(SPCA533, 0)},
+ {USB_DEVICE(0x0733, 0x2221), BS(SPCA533, 0)},
+ {USB_DEVICE(0x0733, 0x3261), BS(SPCA536, 0)},
+ {USB_DEVICE(0x0733, 0x3281), BS(SPCA536, 0)},
+ {USB_DEVICE(0x08ca, 0x0104), BS(SPCA533, 0)},
+ {USB_DEVICE(0x08ca, 0x0106), BS(SPCA533, 0)},
+ {USB_DEVICE(0x08ca, 0x2008), BS(SPCA504B, 0)},
+ {USB_DEVICE(0x08ca, 0x2010), BS(SPCA533, 0)},
+ {USB_DEVICE(0x08ca, 0x2016), BS(SPCA504B, 0)},
+ {USB_DEVICE(0x08ca, 0x2018), BS(SPCA504B, 0)},
+ {USB_DEVICE(0x08ca, 0x2020), BS(SPCA533, 0)},
+ {USB_DEVICE(0x08ca, 0x2022), BS(SPCA533, 0)},
+ {USB_DEVICE(0x08ca, 0x2024), BS(SPCA536, 0)},
+ {USB_DEVICE(0x08ca, 0x2028), BS(SPCA533, 0)},
+ {USB_DEVICE(0x08ca, 0x2040), BS(SPCA536, 0)},
+ {USB_DEVICE(0x08ca, 0x2042), BS(SPCA536, 0)},
+ {USB_DEVICE(0x08ca, 0x2050), BS(SPCA536, 0)},
+ {USB_DEVICE(0x08ca, 0x2060), BS(SPCA536, 0)},
+ {USB_DEVICE(0x0d64, 0x0303), BS(SPCA536, 0)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c
new file mode 100644
index 00000000..9b9f85a8
--- /dev/null
+++ b/drivers/media/video/gspca/t613.c
@@ -0,0 +1,1466 @@
+/*
+ * T613 subdriver
+ *
+ * Copyright (C) 2010 Jean-Francois Moine (http://moinejf.free.fr)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *Notes: * t613 + tas5130A
+ * * Focus to light do not balance well as in win.
+ * Quality in win is not good, but its kinda better.
+ * * Fix some "extraneous bytes", most of apps will show the image anyway
+ * * Gamma table, is there, but its really doing something?
+ * * 7~8 Fps, its ok, max on win its 10.
+ * Costantino Leandro
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "t613"
+
+#include <linux/input.h>
+#include <linux/slab.h>
+#include "gspca.h"
+
+#define V4L2_CID_EFFECTS (V4L2_CID_PRIVATE_BASE + 0)
+
+MODULE_AUTHOR("Leandro Costantino <le_costantino@pixartargentina.com.ar>");
+MODULE_DESCRIPTION("GSPCA/T613 (JPEG Compliance) USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ u8 brightness;
+ u8 contrast;
+ u8 colors;
+ u8 autogain;
+ u8 gamma;
+ u8 sharpness;
+ u8 freq;
+ u8 red_gain;
+ u8 blue_gain;
+ u8 green_gain;
+ u8 awb; /* set default r/g/b and activate */
+ u8 mirror;
+ u8 effect;
+
+ u8 sensor;
+ u8 button_pressed;
+};
+enum sensors {
+ SENSOR_OM6802,
+ SENSOR_OTHER,
+ SENSOR_TAS5130A,
+ SENSOR_LT168G, /* must verify if this is the actual model */
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setlowlight(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getlowlight(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
+
+static int sd_setawb(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setblue_gain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getblue_gain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setred_gain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getred_gain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
+
+static int sd_setmirror(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getmirror(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_seteffect(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_geteffect(struct gspca_dev *gspca_dev, __s32 *val);
+
+static const struct ctrl sd_ctrls[] = {
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 14,
+ .step = 1,
+#define BRIGHTNESS_DEF 8
+ .default_value = BRIGHTNESS_DEF,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+ {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 0x0d,
+ .step = 1,
+#define CONTRAST_DEF 0x07
+ .default_value = CONTRAST_DEF,
+ },
+ .set = sd_setcontrast,
+ .get = sd_getcontrast,
+ },
+ {
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Color",
+ .minimum = 0,
+ .maximum = 0x0f,
+ .step = 1,
+#define COLORS_DEF 0x05
+ .default_value = COLORS_DEF,
+ },
+ .set = sd_setcolors,
+ .get = sd_getcolors,
+ },
+#define GAMMA_MAX 16
+#define GAMMA_DEF 10
+ {
+ {
+ .id = V4L2_CID_GAMMA, /* (gamma on win) */
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gamma",
+ .minimum = 0,
+ .maximum = GAMMA_MAX - 1,
+ .step = 1,
+ .default_value = GAMMA_DEF,
+ },
+ .set = sd_setgamma,
+ .get = sd_getgamma,
+ },
+ {
+ {
+ .id = V4L2_CID_BACKLIGHT_COMPENSATION, /* Activa lowlight,
+ * some apps dont bring up the
+ * backligth_compensation control) */
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Low Light",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define AUTOGAIN_DEF 0x01
+ .default_value = AUTOGAIN_DEF,
+ },
+ .set = sd_setlowlight,
+ .get = sd_getlowlight,
+ },
+ {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mirror Image",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define MIRROR_DEF 0
+ .default_value = MIRROR_DEF,
+ },
+ .set = sd_setmirror,
+ .get = sd_getmirror
+ },
+ {
+ {
+ .id = V4L2_CID_POWER_LINE_FREQUENCY,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Light Frequency Filter",
+ .minimum = 1, /* 1 -> 0x50, 2->0x60 */
+ .maximum = 2,
+ .step = 1,
+#define FREQ_DEF 1
+ .default_value = FREQ_DEF,
+ },
+ .set = sd_setfreq,
+ .get = sd_getfreq},
+
+ {
+ {
+ .id = V4L2_CID_AUTO_WHITE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Auto White Balance",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define AWB_DEF 0
+ .default_value = AWB_DEF,
+ },
+ .set = sd_setawb,
+ .get = sd_getawb
+ },
+ {
+ {
+ .id = V4L2_CID_SHARPNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Sharpness",
+ .minimum = 0,
+ .maximum = 15,
+ .step = 1,
+#define SHARPNESS_DEF 0x06
+ .default_value = SHARPNESS_DEF,
+ },
+ .set = sd_setsharpness,
+ .get = sd_getsharpness,
+ },
+ {
+ {
+ .id = V4L2_CID_EFFECTS,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Webcam Effects",
+ .minimum = 0,
+ .maximum = 4,
+ .step = 1,
+#define EFFECTS_DEF 0
+ .default_value = EFFECTS_DEF,
+ },
+ .set = sd_seteffect,
+ .get = sd_geteffect
+ },
+ {
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Balance",
+ .minimum = 0x10,
+ .maximum = 0x40,
+ .step = 1,
+#define BLUE_GAIN_DEF 0x20
+ .default_value = BLUE_GAIN_DEF,
+ },
+ .set = sd_setblue_gain,
+ .get = sd_getblue_gain,
+ },
+ {
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Balance",
+ .minimum = 0x10,
+ .maximum = 0x40,
+ .step = 1,
+#define RED_GAIN_DEF 0x20
+ .default_value = RED_GAIN_DEF,
+ },
+ .set = sd_setred_gain,
+ .get = sd_getred_gain,
+ },
+ {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0x10,
+ .maximum = 0x40,
+ .step = 1,
+#define GAIN_DEF 0x20
+ .default_value = GAIN_DEF,
+ },
+ .set = sd_setgain,
+ .get = sd_getgain,
+ },
+};
+
+static const struct v4l2_pix_format vga_mode_t16[] = {
+ {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120 * 4 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 4},
+ {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 3},
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 2},
+ {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
+/* sensor specific data */
+struct additional_sensor_data {
+ const u8 n3[6];
+ const u8 *n4, n4sz;
+ const u8 reg80, reg8e;
+ const u8 nset8[6];
+ const u8 data1[10];
+ const u8 data2[9];
+ const u8 data3[9];
+ const u8 data5[6];
+ const u8 stream[4];
+};
+
+static const u8 n4_om6802[] = {
+ 0x09, 0x01, 0x12, 0x04, 0x66, 0x8a, 0x80, 0x3c,
+ 0x81, 0x22, 0x84, 0x50, 0x8a, 0x78, 0x8b, 0x68,
+ 0x8c, 0x88, 0x8e, 0x33, 0x8f, 0x24, 0xaa, 0xb1,
+ 0xa2, 0x60, 0xa5, 0x30, 0xa6, 0x3a, 0xa8, 0xe8,
+ 0xae, 0x05, 0xb1, 0x00, 0xbb, 0x04, 0xbc, 0x48,
+ 0xbe, 0x36, 0xc6, 0x88, 0xe9, 0x00, 0xc5, 0xc0,
+ 0x65, 0x0a, 0xbb, 0x86, 0xaf, 0x58, 0xb0, 0x68,
+ 0x87, 0x40, 0x89, 0x2b, 0x8d, 0xff, 0x83, 0x40,
+ 0xac, 0x84, 0xad, 0x86, 0xaf, 0x46
+};
+static const u8 n4_other[] = {
+ 0x66, 0x00, 0x7f, 0x00, 0x80, 0xac, 0x81, 0x69,
+ 0x84, 0x40, 0x85, 0x70, 0x86, 0x20, 0x8a, 0x68,
+ 0x8b, 0x58, 0x8c, 0x88, 0x8d, 0xff, 0x8e, 0xb8,
+ 0x8f, 0x28, 0xa2, 0x60, 0xa5, 0x40, 0xa8, 0xa8,
+ 0xac, 0x84, 0xad, 0x84, 0xae, 0x24, 0xaf, 0x56,
+ 0xb0, 0x68, 0xb1, 0x00, 0xb2, 0x88, 0xbb, 0xc5,
+ 0xbc, 0x4a, 0xbe, 0x36, 0xc2, 0x88, 0xc5, 0xc0,
+ 0xc6, 0xda, 0xe9, 0x26, 0xeb, 0x00
+};
+static const u8 n4_tas5130a[] = {
+ 0x80, 0x3c, 0x81, 0x68, 0x83, 0xa0, 0x84, 0x20,
+ 0x8a, 0x68, 0x8b, 0x58, 0x8c, 0x88, 0x8e, 0xb4,
+ 0x8f, 0x24, 0xa1, 0xb1, 0xa2, 0x30, 0xa5, 0x10,
+ 0xa6, 0x4a, 0xae, 0x03, 0xb1, 0x44, 0xb2, 0x08,
+ 0xb7, 0x06, 0xb9, 0xe7, 0xbb, 0xc4, 0xbc, 0x4a,
+ 0xbe, 0x36, 0xbf, 0xff, 0xc2, 0x88, 0xc5, 0xc8,
+ 0xc6, 0xda
+};
+static const u8 n4_lt168g[] = {
+ 0x66, 0x01, 0x7f, 0x00, 0x80, 0x7c, 0x81, 0x28,
+ 0x83, 0x44, 0x84, 0x20, 0x86, 0x20, 0x8a, 0x70,
+ 0x8b, 0x58, 0x8c, 0x88, 0x8d, 0xa0, 0x8e, 0xb3,
+ 0x8f, 0x24, 0xa1, 0xb0, 0xa2, 0x38, 0xa5, 0x20,
+ 0xa6, 0x4a, 0xa8, 0xe8, 0xaf, 0x38, 0xb0, 0x68,
+ 0xb1, 0x44, 0xb2, 0x88, 0xbb, 0x86, 0xbd, 0x40,
+ 0xbe, 0x26, 0xc1, 0x05, 0xc2, 0x88, 0xc5, 0xc0,
+ 0xda, 0x8e, 0xdb, 0xca, 0xdc, 0xa8, 0xdd, 0x8c,
+ 0xde, 0x44, 0xdf, 0x0c, 0xe9, 0x80
+};
+
+static const struct additional_sensor_data sensor_data[] = {
+[SENSOR_OM6802] = {
+ .n3 =
+ {0x61, 0x68, 0x65, 0x0a, 0x60, 0x04},
+ .n4 = n4_om6802,
+ .n4sz = sizeof n4_om6802,
+ .reg80 = 0x3c,
+ .reg8e = 0x33,
+ .nset8 = {0xa8, 0xf0, 0xc6, 0x88, 0xc0, 0x00},
+ .data1 =
+ {0xc2, 0x28, 0x0f, 0x22, 0xcd, 0x27, 0x2c, 0x06,
+ 0xb3, 0xfc},
+ .data2 =
+ {0x80, 0xff, 0xff, 0x80, 0xff, 0xff, 0x80, 0xff,
+ 0xff},
+ .data3 =
+ {0x80, 0xff, 0xff, 0x80, 0xff, 0xff, 0x80, 0xff,
+ 0xff},
+ .data5 = /* this could be removed later */
+ {0x0c, 0x03, 0xab, 0x13, 0x81, 0x23},
+ .stream =
+ {0x0b, 0x04, 0x0a, 0x78},
+ },
+[SENSOR_OTHER] = {
+ .n3 =
+ {0x61, 0xc2, 0x65, 0x88, 0x60, 0x00},
+ .n4 = n4_other,
+ .n4sz = sizeof n4_other,
+ .reg80 = 0xac,
+ .reg8e = 0xb8,
+ .nset8 = {0xa8, 0xa8, 0xc6, 0xda, 0xc0, 0x00},
+ .data1 =
+ {0xc1, 0x48, 0x04, 0x1b, 0xca, 0x2e, 0x33, 0x3a,
+ 0xe8, 0xfc},
+ .data2 =
+ {0x4e, 0x9c, 0xec, 0x40, 0x80, 0xc0, 0x48, 0x96,
+ 0xd9},
+ .data3 =
+ {0x4e, 0x9c, 0xec, 0x40, 0x80, 0xc0, 0x48, 0x96,
+ 0xd9},
+ .data5 =
+ {0x0c, 0x03, 0xab, 0x29, 0x81, 0x69},
+ .stream =
+ {0x0b, 0x04, 0x0a, 0x00},
+ },
+[SENSOR_TAS5130A] = {
+ .n3 =
+ {0x61, 0xc2, 0x65, 0x0d, 0x60, 0x08},
+ .n4 = n4_tas5130a,
+ .n4sz = sizeof n4_tas5130a,
+ .reg80 = 0x3c,
+ .reg8e = 0xb4,
+ .nset8 = {0xa8, 0xf0, 0xc6, 0xda, 0xc0, 0x00},
+ .data1 =
+ {0xbb, 0x28, 0x10, 0x10, 0xbb, 0x28, 0x1e, 0x27,
+ 0xc8, 0xfc},
+ .data2 =
+ {0x60, 0xa8, 0xe0, 0x60, 0xa8, 0xe0, 0x60, 0xa8,
+ 0xe0},
+ .data3 =
+ {0x60, 0xa8, 0xe0, 0x60, 0xa8, 0xe0, 0x60, 0xa8,
+ 0xe0},
+ .data5 =
+ {0x0c, 0x03, 0xab, 0x10, 0x81, 0x20},
+ .stream =
+ {0x0b, 0x04, 0x0a, 0x40},
+ },
+[SENSOR_LT168G] = {
+ .n3 = {0x61, 0xc2, 0x65, 0x68, 0x60, 0x00},
+ .n4 = n4_lt168g,
+ .n4sz = sizeof n4_lt168g,
+ .reg80 = 0x7c,
+ .reg8e = 0xb3,
+ .nset8 = {0xa8, 0xf0, 0xc6, 0xba, 0xc0, 0x00},
+ .data1 = {0xc0, 0x38, 0x08, 0x10, 0xc0, 0x30, 0x10, 0x40,
+ 0xb0, 0xf4},
+ .data2 = {0x40, 0x80, 0xc0, 0x50, 0xa0, 0xf0, 0x53, 0xa6,
+ 0xff},
+ .data3 = {0x40, 0x80, 0xc0, 0x50, 0xa0, 0xf0, 0x53, 0xa6,
+ 0xff},
+ .data5 = {0x0c, 0x03, 0xab, 0x4b, 0x81, 0x2b},
+ .stream = {0x0b, 0x04, 0x0a, 0x28},
+ },
+};
+
+#define MAX_EFFECTS 7
+/* easily done by soft, this table could be removed,
+ * i keep it here just in case */
+static char *effects_control[MAX_EFFECTS] = {
+ "Normal",
+ "Emboss", /* disabled */
+ "Monochrome",
+ "Sepia",
+ "Sketch",
+ "Sun Effect", /* disabled */
+ "Negative",
+};
+static const u8 effects_table[MAX_EFFECTS][6] = {
+ {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x00}, /* Normal */
+ {0xa8, 0xc8, 0xc6, 0x52, 0xc0, 0x04}, /* Repujar */
+ {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x20}, /* Monochrome */
+ {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x80}, /* Sepia */
+ {0xa8, 0xc8, 0xc6, 0x52, 0xc0, 0x02}, /* Croquis */
+ {0xa8, 0xc8, 0xc6, 0xd2, 0xc0, 0x10}, /* Sun Effect */
+ {0xa8, 0xc8, 0xc6, 0xd2, 0xc0, 0x40}, /* Negative */
+};
+
+static const u8 gamma_table[GAMMA_MAX][17] = {
+/* gamma table from cam1690.ini */
+ {0x00, 0x00, 0x01, 0x04, 0x08, 0x0e, 0x16, 0x21, /* 0 */
+ 0x2e, 0x3d, 0x50, 0x65, 0x7d, 0x99, 0xb8, 0xdb,
+ 0xff},
+ {0x00, 0x01, 0x03, 0x08, 0x0e, 0x16, 0x21, 0x2d, /* 1 */
+ 0x3c, 0x4d, 0x60, 0x75, 0x8d, 0xa6, 0xc2, 0xe1,
+ 0xff},
+ {0x00, 0x01, 0x05, 0x0b, 0x12, 0x1c, 0x28, 0x35, /* 2 */
+ 0x45, 0x56, 0x69, 0x7e, 0x95, 0xad, 0xc7, 0xe3,
+ 0xff},
+ {0x00, 0x02, 0x07, 0x0f, 0x18, 0x24, 0x30, 0x3f, /* 3 */
+ 0x4f, 0x61, 0x73, 0x88, 0x9d, 0xb4, 0xcd, 0xe6,
+ 0xff},
+ {0x00, 0x04, 0x0b, 0x15, 0x20, 0x2d, 0x3b, 0x4a, /* 4 */
+ 0x5b, 0x6c, 0x7f, 0x92, 0xa7, 0xbc, 0xd2, 0xe9,
+ 0xff},
+ {0x00, 0x07, 0x11, 0x15, 0x20, 0x2d, 0x48, 0x58, /* 5 */
+ 0x68, 0x79, 0x8b, 0x9d, 0xb0, 0xc4, 0xd7, 0xec,
+ 0xff},
+ {0x00, 0x0c, 0x1a, 0x29, 0x38, 0x47, 0x57, 0x67, /* 6 */
+ 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee,
+ 0xff},
+ {0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, /* 7 */
+ 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0,
+ 0xff},
+ {0x00, 0x15, 0x27, 0x38, 0x49, 0x59, 0x69, 0x79, /* 8 */
+ 0x88, 0x97, 0xa7, 0xb6, 0xc4, 0xd3, 0xe2, 0xf0,
+ 0xff},
+ {0x00, 0x1c, 0x30, 0x43, 0x54, 0x65, 0x75, 0x84, /* 9 */
+ 0x93, 0xa1, 0xb0, 0xbd, 0xca, 0xd8, 0xe5, 0xf2,
+ 0xff},
+ {0x00, 0x24, 0x3b, 0x4f, 0x60, 0x70, 0x80, 0x8e, /* 10 */
+ 0x9c, 0xaa, 0xb7, 0xc4, 0xd0, 0xdc, 0xe8, 0xf3,
+ 0xff},
+ {0x00, 0x2a, 0x3c, 0x5d, 0x6e, 0x7e, 0x8d, 0x9b, /* 11 */
+ 0xa8, 0xb4, 0xc0, 0xcb, 0xd6, 0xe1, 0xeb, 0xf5,
+ 0xff},
+ {0x00, 0x3f, 0x5a, 0x6e, 0x7f, 0x8e, 0x9c, 0xa8, /* 12 */
+ 0xb4, 0xbf, 0xc9, 0xd3, 0xdc, 0xe5, 0xee, 0xf6,
+ 0xff},
+ {0x00, 0x54, 0x6f, 0x83, 0x93, 0xa0, 0xad, 0xb7, /* 13 */
+ 0xc2, 0xcb, 0xd4, 0xdc, 0xe4, 0xeb, 0xf2, 0xf9,
+ 0xff},
+ {0x00, 0x6e, 0x88, 0x9a, 0xa8, 0xb3, 0xbd, 0xc6, /* 14 */
+ 0xcf, 0xd6, 0xdd, 0xe3, 0xe9, 0xef, 0xf4, 0xfa,
+ 0xff},
+ {0x00, 0x93, 0xa8, 0xb7, 0xc1, 0xca, 0xd2, 0xd8, /* 15 */
+ 0xde, 0xe3, 0xe8, 0xed, 0xf1, 0xf5, 0xf8, 0xfc,
+ 0xff}
+};
+
+static const u8 tas5130a_sensor_init[][8] = {
+ {0x62, 0x08, 0x63, 0x70, 0x64, 0x1d, 0x60, 0x09},
+ {0x62, 0x20, 0x63, 0x01, 0x64, 0x02, 0x60, 0x09},
+ {0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09},
+};
+
+static u8 sensor_reset[] = {0x61, 0x68, 0x62, 0xff, 0x60, 0x07};
+
+/* read 1 byte */
+static u8 reg_r(struct gspca_dev *gspca_dev,
+ u16 index)
+{
+ usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ 0, /* request */
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, /* value */
+ index,
+ gspca_dev->usb_buf, 1, 500);
+ return gspca_dev->usb_buf[0];
+}
+
+static void reg_w(struct gspca_dev *gspca_dev,
+ u16 index)
+{
+ usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index,
+ NULL, 0, 500);
+}
+
+static void reg_w_buf(struct gspca_dev *gspca_dev,
+ const u8 *buffer, u16 len)
+{
+ if (len <= USB_BUF_SZ) {
+ memcpy(gspca_dev->usb_buf, buffer, len);
+ usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x01, 0,
+ gspca_dev->usb_buf, len, 500);
+ } else {
+ u8 *tmpbuf;
+
+ tmpbuf = kmemdup(buffer, len, GFP_KERNEL);
+ if (!tmpbuf) {
+ pr_err("Out of memory\n");
+ return;
+ }
+ usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x01, 0,
+ tmpbuf, len, 500);
+ kfree(tmpbuf);
+ }
+}
+
+/* write values to consecutive registers */
+static void reg_w_ixbuf(struct gspca_dev *gspca_dev,
+ u8 reg,
+ const u8 *buffer, u16 len)
+{
+ int i;
+ u8 *p, *tmpbuf;
+
+ if (len * 2 <= USB_BUF_SZ) {
+ p = tmpbuf = gspca_dev->usb_buf;
+ } else {
+ p = tmpbuf = kmalloc(len * 2, GFP_KERNEL);
+ if (!tmpbuf) {
+ pr_err("Out of memory\n");
+ return;
+ }
+ }
+ i = len;
+ while (--i >= 0) {
+ *p++ = reg++;
+ *p++ = *buffer++;
+ }
+ usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x01, 0,
+ tmpbuf, len * 2, 500);
+ if (len * 2 > USB_BUF_SZ)
+ kfree(tmpbuf);
+}
+
+static void om6802_sensor_init(struct gspca_dev *gspca_dev)
+{
+ int i;
+ const u8 *p;
+ u8 byte;
+ u8 val[6] = {0x62, 0, 0x64, 0, 0x60, 0x05};
+ static const u8 sensor_init[] = {
+ 0xdf, 0x6d,
+ 0xdd, 0x18,
+ 0x5a, 0xe0,
+ 0x5c, 0x07,
+ 0x5d, 0xb0,
+ 0x5e, 0x1e,
+ 0x60, 0x71,
+ 0xef, 0x00,
+ 0xe9, 0x00,
+ 0xea, 0x00,
+ 0x90, 0x24,
+ 0x91, 0xb2,
+ 0x82, 0x32,
+ 0xfd, 0x41,
+ 0x00 /* table end */
+ };
+
+ reg_w_buf(gspca_dev, sensor_reset, sizeof sensor_reset);
+ msleep(100);
+ i = 4;
+ while (--i > 0) {
+ byte = reg_r(gspca_dev, 0x0060);
+ if (!(byte & 0x01))
+ break;
+ msleep(100);
+ }
+ byte = reg_r(gspca_dev, 0x0063);
+ if (byte != 0x17) {
+ pr_err("Bad sensor reset %02x\n", byte);
+ /* continue? */
+ }
+
+ p = sensor_init;
+ while (*p != 0) {
+ val[1] = *p++;
+ val[3] = *p++;
+ if (*p == 0)
+ reg_w(gspca_dev, 0x3c80);
+ reg_w_buf(gspca_dev, val, sizeof val);
+ i = 4;
+ while (--i >= 0) {
+ msleep(15);
+ byte = reg_r(gspca_dev, 0x60);
+ if (!(byte & 0x01))
+ break;
+ }
+ }
+ msleep(15);
+ reg_w(gspca_dev, 0x3c80);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+
+ cam->cam_mode = vga_mode_t16;
+ cam->nmodes = ARRAY_SIZE(vga_mode_t16);
+
+ sd->brightness = BRIGHTNESS_DEF;
+ sd->contrast = CONTRAST_DEF;
+ sd->colors = COLORS_DEF;
+ sd->gamma = GAMMA_DEF;
+ sd->autogain = AUTOGAIN_DEF;
+ sd->mirror = MIRROR_DEF;
+ sd->freq = FREQ_DEF;
+ sd->awb = AWB_DEF;
+ sd->sharpness = SHARPNESS_DEF;
+ sd->effect = EFFECTS_DEF;
+ sd->red_gain = RED_GAIN_DEF;
+ sd->blue_gain = BLUE_GAIN_DEF;
+ sd->green_gain = GAIN_DEF * 3 - RED_GAIN_DEF - BLUE_GAIN_DEF;
+
+ return 0;
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ unsigned int brightness;
+ u8 set6[4] = { 0x8f, 0x24, 0xc3, 0x00 };
+
+ brightness = sd->brightness;
+ if (brightness < 7) {
+ set6[1] = 0x26;
+ set6[3] = 0x70 - brightness * 0x10;
+ } else {
+ set6[3] = 0x00 + ((brightness - 7) * 0x10);
+ }
+
+ reg_w_buf(gspca_dev, set6, sizeof set6);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ unsigned int contrast = sd->contrast;
+ u16 reg_to_write;
+
+ if (contrast < 7)
+ reg_to_write = 0x8ea9 - contrast * 0x200;
+ else
+ reg_to_write = 0x00a9 + (contrast - 7) * 0x200;
+
+ reg_w(gspca_dev, reg_to_write);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 reg_to_write;
+
+ reg_to_write = 0x80bb + sd->colors * 0x100; /* was 0xc0 */
+ reg_w(gspca_dev, reg_to_write);
+}
+
+static void setgamma(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(D_CONF, "Gamma: %d", sd->gamma);
+ reg_w_ixbuf(gspca_dev, 0x90,
+ gamma_table[sd->gamma], sizeof gamma_table[0]);
+}
+
+static void setRGB(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 all_gain_reg[6] =
+ {0x87, 0x00, 0x88, 0x00, 0x89, 0x00};
+
+ all_gain_reg[1] = sd->red_gain;
+ all_gain_reg[3] = sd->blue_gain;
+ all_gain_reg[5] = sd->green_gain;
+ reg_w_buf(gspca_dev, all_gain_reg, sizeof all_gain_reg);
+}
+
+/* Generic fnc for r/b balance, exposure and awb */
+static void setawb(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 reg80;
+
+ reg80 = (sensor_data[sd->sensor].reg80 << 8) | 0x80;
+
+ /* on awb leave defaults values */
+ if (!sd->awb) {
+ /* shoud we wait here.. */
+ /* update and reset RGB gains with webcam values */
+ sd->red_gain = reg_r(gspca_dev, 0x0087);
+ sd->blue_gain = reg_r(gspca_dev, 0x0088);
+ sd->green_gain = reg_r(gspca_dev, 0x0089);
+ reg80 &= ~0x0400; /* AWB off */
+ }
+ reg_w(gspca_dev, reg80);
+ reg_w(gspca_dev, reg80);
+}
+
+static void init_gains(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 reg80;
+ u8 all_gain_reg[8] =
+ {0x87, 0x00, 0x88, 0x00, 0x89, 0x00, 0x80, 0x00};
+
+ all_gain_reg[1] = sd->red_gain;
+ all_gain_reg[3] = sd->blue_gain;
+ all_gain_reg[5] = sd->green_gain;
+ reg80 = sensor_data[sd->sensor].reg80;
+ if (!sd->awb)
+ reg80 &= ~0x04;
+ all_gain_reg[7] = reg80;
+ reg_w_buf(gspca_dev, all_gain_reg, sizeof all_gain_reg);
+
+ reg_w(gspca_dev, (sd->red_gain << 8) + 0x87);
+ reg_w(gspca_dev, (sd->blue_gain << 8) + 0x88);
+ reg_w(gspca_dev, (sd->green_gain << 8) + 0x89);
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 reg_to_write;
+
+ reg_to_write = 0x0aa6 + 0x1000 * sd->sharpness;
+
+ reg_w(gspca_dev, reg_to_write);
+}
+
+static void setfreq(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 reg66;
+ u8 freq[4] = { 0x66, 0x00, 0xa8, 0xe8 };
+
+ switch (sd->sensor) {
+ case SENSOR_LT168G:
+ if (sd->freq != 0)
+ freq[3] = 0xa8;
+ reg66 = 0x41;
+ break;
+ case SENSOR_OM6802:
+ reg66 = 0xca;
+ break;
+ default:
+ reg66 = 0x40;
+ break;
+ }
+ switch (sd->freq) {
+ case 0: /* no flicker */
+ freq[3] = 0xf0;
+ break;
+ case 2: /* 60Hz */
+ reg66 &= ~0x40;
+ break;
+ }
+ freq[1] = reg66;
+
+ reg_w_buf(gspca_dev, freq, sizeof freq);
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ /* some of this registers are not really neded, because
+ * they are overriden by setbrigthness, setcontrast, etc,
+ * but wont hurt anyway, and can help someone with similar webcam
+ * to see the initial parameters.*/
+ struct sd *sd = (struct sd *) gspca_dev;
+ const struct additional_sensor_data *sensor;
+ int i;
+ u16 sensor_id;
+ u8 test_byte = 0;
+
+ static const u8 read_indexs[] =
+ { 0x0a, 0x0b, 0x66, 0x80, 0x81, 0x8e, 0x8f, 0xa5,
+ 0xa6, 0xa8, 0xbb, 0xbc, 0xc6, 0x00 };
+ static const u8 n1[] =
+ {0x08, 0x03, 0x09, 0x03, 0x12, 0x04};
+ static const u8 n2[] =
+ {0x08, 0x00};
+
+ sensor_id = (reg_r(gspca_dev, 0x06) << 8)
+ | reg_r(gspca_dev, 0x07);
+ switch (sensor_id & 0xff0f) {
+ case 0x0801:
+ PDEBUG(D_PROBE, "sensor tas5130a");
+ sd->sensor = SENSOR_TAS5130A;
+ break;
+ case 0x0802:
+ PDEBUG(D_PROBE, "sensor lt168g");
+ sd->sensor = SENSOR_LT168G;
+ break;
+ case 0x0803:
+ PDEBUG(D_PROBE, "sensor 'other'");
+ sd->sensor = SENSOR_OTHER;
+ break;
+ case 0x0807:
+ PDEBUG(D_PROBE, "sensor om6802");
+ sd->sensor = SENSOR_OM6802;
+ break;
+ default:
+ pr_err("unknown sensor %04x\n", sensor_id);
+ return -EINVAL;
+ }
+
+ if (sd->sensor == SENSOR_OM6802) {
+ reg_w_buf(gspca_dev, n1, sizeof n1);
+ i = 5;
+ while (--i >= 0) {
+ reg_w_buf(gspca_dev, sensor_reset, sizeof sensor_reset);
+ test_byte = reg_r(gspca_dev, 0x0063);
+ msleep(100);
+ if (test_byte == 0x17)
+ break; /* OK */
+ }
+ if (i < 0) {
+ pr_err("Bad sensor reset %02x\n", test_byte);
+ return -EIO;
+ }
+ reg_w_buf(gspca_dev, n2, sizeof n2);
+ }
+
+ i = 0;
+ while (read_indexs[i] != 0x00) {
+ test_byte = reg_r(gspca_dev, read_indexs[i]);
+ PDEBUG(D_STREAM, "Reg 0x%02x = 0x%02x", read_indexs[i],
+ test_byte);
+ i++;
+ }
+
+ sensor = &sensor_data[sd->sensor];
+ reg_w_buf(gspca_dev, sensor->n3, sizeof sensor->n3);
+ reg_w_buf(gspca_dev, sensor->n4, sensor->n4sz);
+
+ if (sd->sensor == SENSOR_LT168G) {
+ test_byte = reg_r(gspca_dev, 0x80);
+ PDEBUG(D_STREAM, "Reg 0x%02x = 0x%02x", 0x80,
+ test_byte);
+ reg_w(gspca_dev, 0x6c80);
+ }
+
+ reg_w_ixbuf(gspca_dev, 0xd0, sensor->data1, sizeof sensor->data1);
+ reg_w_ixbuf(gspca_dev, 0xc7, sensor->data2, sizeof sensor->data2);
+ reg_w_ixbuf(gspca_dev, 0xe0, sensor->data3, sizeof sensor->data3);
+
+ reg_w(gspca_dev, (sensor->reg80 << 8) + 0x80);
+ reg_w(gspca_dev, (sensor->reg80 << 8) + 0x80);
+ reg_w(gspca_dev, (sensor->reg8e << 8) + 0x8e);
+
+ setbrightness(gspca_dev);
+ setcontrast(gspca_dev);
+ setgamma(gspca_dev);
+ setcolors(gspca_dev);
+ setsharpness(gspca_dev);
+ init_gains(gspca_dev);
+ setfreq(gspca_dev);
+
+ reg_w_buf(gspca_dev, sensor->data5, sizeof sensor->data5);
+ reg_w_buf(gspca_dev, sensor->nset8, sizeof sensor->nset8);
+ reg_w_buf(gspca_dev, sensor->stream, sizeof sensor->stream);
+
+ if (sd->sensor == SENSOR_LT168G) {
+ test_byte = reg_r(gspca_dev, 0x80);
+ PDEBUG(D_STREAM, "Reg 0x%02x = 0x%02x", 0x80,
+ test_byte);
+ reg_w(gspca_dev, 0x6c80);
+ }
+
+ reg_w_ixbuf(gspca_dev, 0xd0, sensor->data1, sizeof sensor->data1);
+ reg_w_ixbuf(gspca_dev, 0xc7, sensor->data2, sizeof sensor->data2);
+ reg_w_ixbuf(gspca_dev, 0xe0, sensor->data3, sizeof sensor->data3);
+
+ return 0;
+}
+
+static void setmirror(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 hflipcmd[8] =
+ {0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09};
+
+ if (sd->mirror)
+ hflipcmd[3] = 0x01;
+
+ reg_w_buf(gspca_dev, hflipcmd, sizeof hflipcmd);
+}
+
+static void seteffect(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w_buf(gspca_dev, effects_table[sd->effect],
+ sizeof effects_table[0]);
+ if (sd->effect == 1 || sd->effect == 5) {
+ PDEBUG(D_CONF,
+ "This effect have been disabled for webcam \"safety\"");
+ return;
+ }
+
+ if (sd->effect == 1 || sd->effect == 4)
+ reg_w(gspca_dev, 0x4aa6);
+ else
+ reg_w(gspca_dev, 0xfaa6);
+}
+
+/* Is this really needed?
+ * i added some module parameters for test with some users */
+static void poll_sensor(struct gspca_dev *gspca_dev)
+{
+ static const u8 poll1[] =
+ {0x67, 0x05, 0x68, 0x81, 0x69, 0x80, 0x6a, 0x82,
+ 0x6b, 0x68, 0x6c, 0x69, 0x72, 0xd9, 0x73, 0x34,
+ 0x74, 0x32, 0x75, 0x92, 0x76, 0x00, 0x09, 0x01,
+ 0x60, 0x14};
+ static const u8 poll2[] =
+ {0x67, 0x02, 0x68, 0x71, 0x69, 0x72, 0x72, 0xa9,
+ 0x73, 0x02, 0x73, 0x02, 0x60, 0x14};
+ static const u8 noise03[] = /* (some differences / ms-drv) */
+ {0xa6, 0x0a, 0xea, 0xcf, 0xbe, 0x26, 0xb1, 0x5f,
+ 0xa1, 0xb1, 0xda, 0x6b, 0xdb, 0x98, 0xdf, 0x0c,
+ 0xc2, 0x80, 0xc3, 0x10};
+
+ PDEBUG(D_STREAM, "[Sensor requires polling]");
+ reg_w_buf(gspca_dev, poll1, sizeof poll1);
+ reg_w_buf(gspca_dev, poll2, sizeof poll2);
+ reg_w_buf(gspca_dev, noise03, sizeof noise03);
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ const struct additional_sensor_data *sensor;
+ int i, mode;
+ u8 t2[] = { 0x07, 0x00, 0x0d, 0x60, 0x0e, 0x80 };
+ static const u8 t3[] =
+ { 0x07, 0x00, 0x88, 0x02, 0x06, 0x00, 0xe7, 0x01 };
+
+ mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+ switch (mode) {
+ case 0: /* 640x480 (0x00) */
+ break;
+ case 1: /* 352x288 */
+ t2[1] = 0x40;
+ break;
+ case 2: /* 320x240 */
+ t2[1] = 0x10;
+ break;
+ case 3: /* 176x144 */
+ t2[1] = 0x50;
+ break;
+ default:
+/* case 4: * 160x120 */
+ t2[1] = 0x20;
+ break;
+ }
+
+ switch (sd->sensor) {
+ case SENSOR_OM6802:
+ om6802_sensor_init(gspca_dev);
+ break;
+ case SENSOR_TAS5130A:
+ i = 0;
+ for (;;) {
+ reg_w_buf(gspca_dev, tas5130a_sensor_init[i],
+ sizeof tas5130a_sensor_init[0]);
+ if (i >= ARRAY_SIZE(tas5130a_sensor_init) - 1)
+ break;
+ i++;
+ }
+ reg_w(gspca_dev, 0x3c80);
+ /* just in case and to keep sync with logs (for mine) */
+ reg_w_buf(gspca_dev, tas5130a_sensor_init[i],
+ sizeof tas5130a_sensor_init[0]);
+ reg_w(gspca_dev, 0x3c80);
+ break;
+ }
+ sensor = &sensor_data[sd->sensor];
+ setfreq(gspca_dev);
+ reg_r(gspca_dev, 0x0012);
+ reg_w_buf(gspca_dev, t2, sizeof t2);
+ reg_w_ixbuf(gspca_dev, 0xb3, t3, sizeof t3);
+ reg_w(gspca_dev, 0x0013);
+ msleep(15);
+ reg_w_buf(gspca_dev, sensor->stream, sizeof sensor->stream);
+ reg_w_buf(gspca_dev, sensor->stream, sizeof sensor->stream);
+
+ if (sd->sensor == SENSOR_OM6802)
+ poll_sensor(gspca_dev);
+
+ return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w_buf(gspca_dev, sensor_data[sd->sensor].stream,
+ sizeof sensor_data[sd->sensor].stream);
+ reg_w_buf(gspca_dev, sensor_data[sd->sensor].stream,
+ sizeof sensor_data[sd->sensor].stream);
+ if (sd->sensor == SENSOR_OM6802) {
+ msleep(20);
+ reg_w(gspca_dev, 0x0309);
+ }
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ /* If the last button state is pressed, release it now! */
+ if (sd->button_pressed) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ sd->button_pressed = 0;
+ }
+#endif
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int pkt_type;
+
+ if (data[0] == 0x5a) {
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ if (len > 20) {
+ u8 state = (data[20] & 0x80) ? 1 : 0;
+ if (sd->button_pressed != state) {
+ input_report_key(gspca_dev->input_dev,
+ KEY_CAMERA, state);
+ input_sync(gspca_dev->input_dev);
+ sd->button_pressed = state;
+ }
+ }
+#endif
+ /* Control Packet, after this came the header again,
+ * but extra bytes came in the packet before this,
+ * sometimes an EOF arrives, sometimes not... */
+ return;
+ }
+ data += 2;
+ len -= 2;
+ if (data[0] == 0xff && data[1] == 0xd8)
+ pkt_type = FIRST_PACKET;
+ else if (data[len - 2] == 0xff && data[len - 1] == 0xd9)
+ pkt_type = LAST_PACKET;
+ else
+ pkt_type = INTER_PACKET;
+ gspca_frame_add(gspca_dev, pkt_type, data, len);
+}
+
+static int sd_setblue_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->blue_gain = val;
+ if (gspca_dev->streaming)
+ reg_w(gspca_dev, (val << 8) + 0x88);
+ return 0;
+}
+
+static int sd_getblue_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->blue_gain;
+ return 0;
+}
+
+static int sd_setred_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->red_gain = val;
+ if (gspca_dev->streaming)
+ reg_w(gspca_dev, (val << 8) + 0x87);
+
+ return 0;
+}
+
+static int sd_getred_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->red_gain;
+ return 0;
+}
+
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 psg, nsg;
+
+ psg = sd->red_gain + sd->blue_gain + sd->green_gain;
+ nsg = val * 3;
+ sd->red_gain = sd->red_gain * nsg / psg;
+ if (sd->red_gain > 0x40)
+ sd->red_gain = 0x40;
+ else if (sd->red_gain < 0x10)
+ sd->red_gain = 0x10;
+ sd->blue_gain = sd->blue_gain * nsg / psg;
+ if (sd->blue_gain > 0x40)
+ sd->blue_gain = 0x40;
+ else if (sd->blue_gain < 0x10)
+ sd->blue_gain = 0x10;
+ sd->green_gain = sd->green_gain * nsg / psg;
+ if (sd->green_gain > 0x40)
+ sd->green_gain = 0x40;
+ else if (sd->green_gain < 0x10)
+ sd->green_gain = 0x10;
+
+ if (gspca_dev->streaming)
+ setRGB(gspca_dev);
+ return 0;
+}
+
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = (sd->red_gain + sd->blue_gain + sd->green_gain) / 3;
+ return 0;
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->brightness = val;
+ if (gspca_dev->streaming)
+ setbrightness(gspca_dev);
+ return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->brightness;
+ return *val;
+}
+
+static int sd_setawb(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->awb = val;
+ if (gspca_dev->streaming)
+ setawb(gspca_dev);
+ return 0;
+}
+
+static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->awb;
+ return *val;
+}
+
+static int sd_setmirror(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->mirror = val;
+ if (gspca_dev->streaming)
+ setmirror(gspca_dev);
+ return 0;
+}
+
+static int sd_getmirror(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->mirror;
+ return *val;
+}
+
+static int sd_seteffect(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->effect = val;
+ if (gspca_dev->streaming)
+ seteffect(gspca_dev);
+ return 0;
+}
+
+static int sd_geteffect(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->effect;
+ return *val;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->contrast = val;
+ if (gspca_dev->streaming)
+ setcontrast(gspca_dev);
+ return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->contrast;
+ return *val;
+}
+
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->colors = val;
+ if (gspca_dev->streaming)
+ setcolors(gspca_dev);
+ return 0;
+}
+
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->colors;
+ return 0;
+}
+
+static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->gamma = val;
+ if (gspca_dev->streaming)
+ setgamma(gspca_dev);
+ return 0;
+}
+
+static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->gamma;
+ return 0;
+}
+
+static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->freq = val;
+ if (gspca_dev->streaming)
+ setfreq(gspca_dev);
+ return 0;
+}
+
+static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->freq;
+ return 0;
+}
+
+static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->sharpness = val;
+ if (gspca_dev->streaming)
+ setsharpness(gspca_dev);
+ return 0;
+}
+
+static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->sharpness;
+ return 0;
+}
+
+/* Low Light set here......*/
+static int sd_setlowlight(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->autogain = val;
+ if (val != 0)
+ reg_w(gspca_dev, 0xf48e);
+ else
+ reg_w(gspca_dev, 0xb48e);
+ return 0;
+}
+
+static int sd_getlowlight(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->autogain;
+ return 0;
+}
+
+static int sd_querymenu(struct gspca_dev *gspca_dev,
+ struct v4l2_querymenu *menu)
+{
+ static const char *freq_nm[3] = {"NoFliker", "50 Hz", "60 Hz"};
+
+ switch (menu->id) {
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ if ((unsigned) menu->index >= ARRAY_SIZE(freq_nm))
+ break;
+ strcpy((char *) menu->name, freq_nm[menu->index]);
+ return 0;
+ case V4L2_CID_EFFECTS:
+ if ((unsigned) menu->index < ARRAY_SIZE(effects_control)) {
+ strlcpy((char *) menu->name,
+ effects_control[menu->index],
+ sizeof menu->name);
+ return 0;
+ }
+ break;
+ }
+ return -EINVAL;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+ .querymenu = sd_querymenu,
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ .other_input = 1,
+#endif
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x17a1, 0x0128)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/topro.c b/drivers/media/video/gspca/topro.c
new file mode 100644
index 00000000..444d3c5b
--- /dev/null
+++ b/drivers/media/video/gspca/topro.c
@@ -0,0 +1,4978 @@
+/*
+ * Topro TP6800/6810 webcam driver.
+ *
+ * Copyright (C) 2011 Jean-François Moine (http://moinejf.free.fr)
+ * Copyright (C) 2009 Anders Blomdell (anders.blomdell@control.lth.se)
+ * Copyright (C) 2008 Thomas Champagne (lafeuil@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "gspca.h"
+
+MODULE_DESCRIPTION("Topro TP6800/6810 gspca webcam driver");
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>, "
+ "Anders Blomdell <anders.blomdell@control.lth.se>");
+MODULE_LICENSE("GPL");
+
+static int force_sensor = -1;
+
+/* JPEG header */
+static const u8 jpeg_head[] = {
+ 0xff, 0xd8, /* jpeg */
+
+/* quantization table quality 50% */
+ 0xff, 0xdb, 0x00, 0x84, /* DQT */
+0,
+#define JPEG_QT0_OFFSET 7
+ 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e,
+ 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28,
+ 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25,
+ 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33,
+ 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44,
+ 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57,
+ 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71,
+ 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63,
+1,
+#define JPEG_QT1_OFFSET 72
+ 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a,
+ 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+
+ /* Define Huffman table (thanks to Thomas Kaiser) */
+ 0xff, 0xc4, 0x01, 0x5e,
+ 0x00, 0x00, 0x02, 0x03,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
+ 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,
+ 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04,
+ 0x07, 0x05, 0x04, 0x06, 0x01, 0x00, 0x00, 0x57,
+ 0x01, 0x02, 0x03, 0x00, 0x11, 0x04, 0x12, 0x21,
+ 0x31, 0x13, 0x41, 0x51, 0x61, 0x05, 0x22, 0x32,
+ 0x14, 0x71, 0x81, 0x91, 0x15, 0x23, 0x42, 0x52,
+ 0x62, 0xa1, 0xb1, 0x06, 0x33, 0x72, 0xc1, 0xd1,
+ 0x24, 0x43, 0x53, 0x82, 0x16, 0x34, 0x92, 0xa2,
+ 0xe1, 0xf1, 0xf0, 0x07, 0x08, 0x17, 0x18, 0x25,
+ 0x26, 0x27, 0x28, 0x35, 0x36, 0x37, 0x38, 0x44,
+ 0x45, 0x46, 0x47, 0x48, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x73,
+ 0x74, 0x75, 0x76, 0x77, 0x78, 0x83, 0x84, 0x85,
+ 0x86, 0x87, 0x88, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xb2,
+ 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xc2, 0xc3,
+ 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xd2, 0xd3, 0xd4,
+ 0xd5, 0xd6, 0xd7, 0xd8, 0xe2, 0xe3, 0xe4, 0xe5,
+ 0xe6, 0xe7, 0xe8, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
+ 0xf7, 0xf8, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x08, 0x09, 0x11, 0x00, 0x02,
+ 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05,
+ 0x04, 0x06, 0x01, 0x00, 0x00, 0x57, 0x00, 0x01,
+ 0x11, 0x02, 0x21, 0x03, 0x12, 0x31, 0x41, 0x13,
+ 0x22, 0x51, 0x61, 0x04, 0x32, 0x71, 0x05, 0x14,
+ 0x23, 0x42, 0x33, 0x52, 0x81, 0x91, 0xa1, 0xb1,
+ 0xf0, 0x06, 0x15, 0xc1, 0xd1, 0xe1, 0x24, 0x43,
+ 0x62, 0xf1, 0x16, 0x25, 0x34, 0x53, 0x72, 0x82,
+ 0x92, 0x07, 0x08, 0x17, 0x18, 0x26, 0x27, 0x28,
+ 0x35, 0x36, 0x37, 0x38, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x54, 0x55, 0x56, 0x57, 0x58, 0x63, 0x64,
+ 0x65, 0x66, 0x67, 0x68, 0x73, 0x74, 0x75, 0x76,
+ 0x77, 0x78, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+ 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0xa2, 0xa3,
+ 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xb2, 0xb3, 0xb4,
+ 0xb5, 0xb6, 0xb7, 0xb8, 0xc2, 0xc3, 0xc4, 0xc5,
+ 0xc6, 0xc7, 0xc8, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6,
+ 0xd7, 0xd8, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+ 0xe8, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
+ 0xff, 0xc0, 0x00, 0x11, /* SOF0 (start of frame 0 */
+ 0x08, /* data precision */
+#define JPEG_HEIGHT_OFFSET 493
+ 0x01, 0xe0, /* height */
+ 0x02, 0x80, /* width */
+ 0x03, /* component number */
+ 0x01,
+ 0x21, /* samples Y = jpeg 422 */
+ 0x00, /* quant Y */
+ 0x02, 0x11, 0x01, /* samples CbCr - quant CbCr */
+ 0x03, 0x11, 0x01,
+
+ 0xff, 0xda, 0x00, 0x0c, /* SOS (start of scan) */
+ 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00
+#define JPEG_HDR_SZ 521
+};
+
+enum e_ctrl {
+ EXPOSURE,
+ QUALITY,
+ SHARPNESS,
+ RGAIN,
+ GAIN,
+ BGAIN,
+ GAMMA,
+ AUTOGAIN,
+ NCTRLS /* number of controls */
+};
+
+#define AUTOGAIN_DEF 1
+
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ struct gspca_ctrl ctrls[NCTRLS];
+
+ u8 framerate;
+ u8 quality; /* webcam current JPEG quality (0..16) */
+ s8 ag_cnt; /* autogain / start counter for tp6810 */
+#define AG_CNT_START 13 /* check gain every N frames */
+
+ u8 bridge;
+ u8 sensor;
+
+ u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+
+enum bridges {
+ BRIDGE_TP6800,
+ BRIDGE_TP6810,
+};
+
+enum sensors {
+ SENSOR_CX0342,
+ SENSOR_SOI763A, /* ~= ov7630 / ov7648 */
+ NSENSORS
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 4 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG}
+};
+
+/*
+ * JPEG quality
+ * index: webcam compression
+ * value: JPEG quality in %
+ */
+static const u8 jpeg_q[17] = {
+ 88, 77, 67, 57, 55, 55, 45, 45, 36, 36, 30, 30, 26, 26, 22, 22, 94
+};
+
+#define BULK_OUT_SIZE 0x20
+#if BULK_OUT_SIZE > USB_BUF_SZ
+#error "USB buffer too small"
+#endif
+
+static const u8 rates[] = {30, 20, 15, 10, 7, 5};
+static const struct framerates framerates[] = {
+ {
+ .rates = rates,
+ .nrates = ARRAY_SIZE(rates)
+ },
+ {
+ .rates = rates,
+ .nrates = ARRAY_SIZE(rates)
+ }
+};
+static const u8 rates_6810[] = {30, 15, 10, 7, 5};
+static const struct framerates framerates_6810[] = {
+ {
+ .rates = rates_6810,
+ .nrates = ARRAY_SIZE(rates_6810)
+ },
+ {
+ .rates = rates_6810,
+ .nrates = ARRAY_SIZE(rates_6810)
+ }
+};
+
+/*
+ * webcam quality in %
+ * the last value is the ultra fine quality
+ */
+
+/* TP6800 register offsets */
+#define TP6800_R10_SIF_TYPE 0x10
+#define TP6800_R11_SIF_CONTROL 0x11
+#define TP6800_R12_SIF_ADDR_S 0x12
+#define TP6800_R13_SIF_TX_DATA 0x13
+#define TP6800_R14_SIF_RX_DATA 0x14
+#define TP6800_R15_GPIO_PU 0x15
+#define TP6800_R16_GPIO_PD 0x16
+#define TP6800_R17_GPIO_IO 0x17
+#define TP6800_R18_GPIO_DATA 0x18
+#define TP6800_R19_SIF_ADDR_S2 0x19
+#define TP6800_R1A_SIF_TX_DATA2 0x1a
+#define TP6800_R1B_SIF_RX_DATA2 0x1b
+#define TP6800_R21_ENDP_1_CTL 0x21
+#define TP6800_R2F_TIMING_CFG 0x2f
+#define TP6800_R30_SENSOR_CFG 0x30
+#define TP6800_R31_PIXEL_START 0x31
+#define TP6800_R32_PIXEL_END_L 0x32
+#define TP6800_R33_PIXEL_END_H 0x33
+#define TP6800_R34_LINE_START 0x34
+#define TP6800_R35_LINE_END_L 0x35
+#define TP6800_R36_LINE_END_H 0x36
+#define TP6800_R37_FRONT_DARK_ST 0x37
+#define TP6800_R38_FRONT_DARK_END 0x38
+#define TP6800_R39_REAR_DARK_ST_L 0x39
+#define TP6800_R3A_REAR_DARK_ST_H 0x3a
+#define TP6800_R3B_REAR_DARK_END_L 0x3b
+#define TP6800_R3C_REAR_DARK_END_H 0x3c
+#define TP6800_R3D_HORIZ_DARK_LINE_L 0x3d
+#define TP6800_R3E_HORIZ_DARK_LINE_H 0x3e
+#define TP6800_R3F_FRAME_RATE 0x3f
+#define TP6800_R50 0x50
+#define TP6800_R51 0x51
+#define TP6800_R52 0x52
+#define TP6800_R53 0x53
+#define TP6800_R54_DARK_CFG 0x54
+#define TP6800_R55_GAMMA_R 0x55
+#define TP6800_R56_GAMMA_G 0x56
+#define TP6800_R57_GAMMA_B 0x57
+#define TP6800_R5C_EDGE_THRLD 0x5c
+#define TP6800_R5D_DEMOSAIC_CFG 0x5d
+#define TP6800_R78_FORMAT 0x78
+#define TP6800_R79_QUALITY 0x79
+#define TP6800_R7A_BLK_THRLD 0x7a
+
+/* CX0342 register offsets */
+
+#define CX0342_SENSOR_ID 0x00
+#define CX0342_VERSION_NO 0x01
+#define CX0342_ORG_X_L 0x02
+#define CX0342_ORG_X_H 0x03
+#define CX0342_ORG_Y_L 0x04
+#define CX0342_ORG_Y_H 0x05
+#define CX0342_STOP_X_L 0x06
+#define CX0342_STOP_X_H 0x07
+#define CX0342_STOP_Y_L 0x08
+#define CX0342_STOP_Y_H 0x09
+#define CX0342_FRAME_WIDTH_L 0x0a
+#define CX0342_FRAME_WIDTH_H 0x0b
+#define CX0342_FRAME_HEIGH_L 0x0c
+#define CX0342_FRAME_HEIGH_H 0x0d
+#define CX0342_EXPO_LINE_L 0x10
+#define CX0342_EXPO_LINE_H 0x11
+#define CX0342_EXPO_CLK_L 0x12
+#define CX0342_EXPO_CLK_H 0x13
+#define CX0342_RAW_GRGAIN_L 0x14
+#define CX0342_RAW_GRGAIN_H 0x15
+#define CX0342_RAW_GBGAIN_L 0x16
+#define CX0342_RAW_GBGAIN_H 0x17
+#define CX0342_RAW_RGAIN_L 0x18
+#define CX0342_RAW_RGAIN_H 0x19
+#define CX0342_RAW_BGAIN_L 0x1a
+#define CX0342_RAW_BGAIN_H 0x1b
+#define CX0342_GLOBAL_GAIN 0x1c
+#define CX0342_SYS_CTRL_0 0x20
+#define CX0342_SYS_CTRL_1 0x21
+#define CX0342_SYS_CTRL_2 0x22
+#define CX0342_BYPASS_MODE 0x23
+#define CX0342_SYS_CTRL_3 0x24
+#define CX0342_TIMING_EN 0x25
+#define CX0342_OUTPUT_CTRL 0x26
+#define CX0342_AUTO_ADC_CALIB 0x27
+#define CX0342_SYS_CTRL_4 0x28
+#define CX0342_ADCGN 0x30
+#define CX0342_SLPCR 0x31
+#define CX0342_SLPFN_LO 0x32
+#define CX0342_ADC_CTL 0x33
+#define CX0342_LVRST_BLBIAS 0x34
+#define CX0342_VTHSEL 0x35
+#define CX0342_RAMP_RIV 0x36
+#define CX0342_LDOSEL 0x37
+#define CX0342_CLOCK_GEN 0x40
+#define CX0342_SOFT_RESET 0x41
+#define CX0342_PLL 0x42
+#define CX0342_DR_ENH_PULSE_OFFSET_L 0x43
+#define CX0342_DR_ENH_PULSE_OFFSET_H 0x44
+#define CX0342_DR_ENH_PULSE_POS_L 0x45
+#define CX0342_DR_ENH_PULSE_POS_H 0x46
+#define CX0342_DR_ENH_PULSE_WIDTH 0x47
+#define CX0342_AS_CURRENT_CNT_L 0x48
+#define CX0342_AS_CURRENT_CNT_H 0x49
+#define CX0342_AS_PREVIOUS_CNT_L 0x4a
+#define CX0342_AS_PREVIOUS_CNT_H 0x4b
+#define CX0342_SPV_VALUE_L 0x4c
+#define CX0342_SPV_VALUE_H 0x4d
+#define CX0342_GPXLTHD_L 0x50
+#define CX0342_GPXLTHD_H 0x51
+#define CX0342_RBPXLTHD_L 0x52
+#define CX0342_RBPXLTHD_H 0x53
+#define CX0342_PLANETHD_L 0x54
+#define CX0342_PLANETHD_H 0x55
+#define CX0342_ROWDARK_TH 0x56
+#define CX0342_ROWDARK_TOL 0x57
+#define CX0342_RB_GAP_L 0x58
+#define CX0342_RB_GAP_H 0x59
+#define CX0342_G_GAP_L 0x5a
+#define CX0342_G_GAP_H 0x5b
+#define CX0342_AUTO_ROW_DARK 0x60
+#define CX0342_MANUAL_DARK_VALUE 0x61
+#define CX0342_GB_DARK_OFFSET 0x62
+#define CX0342_GR_DARK_OFFSET 0x63
+#define CX0342_RED_DARK_OFFSET 0x64
+#define CX0342_BLUE_DARK_OFFSET 0x65
+#define CX0342_DATA_SCALING_MULTI 0x66
+#define CX0342_AUTOD_Q_FRAME 0x67
+#define CX0342_AUTOD_ALLOW_VARI 0x68
+#define CX0342_AUTO_DARK_VALUE_L 0x69
+#define CX0342_AUTO_DARK_VALUE_H 0x6a
+#define CX0342_IO_CTRL_0 0x70
+#define CX0342_IO_CTRL_1 0x71
+#define CX0342_IO_CTRL_2 0x72
+#define CX0342_IDLE_CTRL 0x73
+#define CX0342_TEST_MODE 0x74
+#define CX0342_FRAME_FIX_DATA_TEST 0x75
+#define CX0342_FRAME_CNT_TEST 0x76
+#define CX0342_RST_OVERFLOW_L 0x80
+#define CX0342_RST_OVERFLOW_H 0x81
+#define CX0342_RST_UNDERFLOW_L 0x82
+#define CX0342_RST_UNDERFLOW_H 0x83
+#define CX0342_DATA_OVERFLOW_L 0x84
+#define CX0342_DATA_OVERFLOW_H 0x85
+#define CX0342_DATA_UNDERFLOW_L 0x86
+#define CX0342_DATA_UNDERFLOW_H 0x87
+#define CX0342_CHANNEL_0_0_L_irst 0x90
+#define CX0342_CHANNEL_0_0_H_irst 0x91
+#define CX0342_CHANNEL_0_1_L_irst 0x92
+#define CX0342_CHANNEL_0_1_H_irst 0x93
+#define CX0342_CHANNEL_0_2_L_irst 0x94
+#define CX0342_CHANNEL_0_2_H_irst 0x95
+#define CX0342_CHANNEL_0_3_L_irst 0x96
+#define CX0342_CHANNEL_0_3_H_irst 0x97
+#define CX0342_CHANNEL_0_4_L_irst 0x98
+#define CX0342_CHANNEL_0_4_H_irst 0x99
+#define CX0342_CHANNEL_0_5_L_irst 0x9a
+#define CX0342_CHANNEL_0_5_H_irst 0x9b
+#define CX0342_CHANNEL_0_6_L_irst 0x9c
+#define CX0342_CHANNEL_0_6_H_irst 0x9d
+#define CX0342_CHANNEL_0_7_L_irst 0x9e
+#define CX0342_CHANNEL_0_7_H_irst 0x9f
+#define CX0342_CHANNEL_1_0_L_itx 0xa0
+#define CX0342_CHANNEL_1_0_H_itx 0xa1
+#define CX0342_CHANNEL_1_1_L_itx 0xa2
+#define CX0342_CHANNEL_1_1_H_itx 0xa3
+#define CX0342_CHANNEL_1_2_L_itx 0xa4
+#define CX0342_CHANNEL_1_2_H_itx 0xa5
+#define CX0342_CHANNEL_1_3_L_itx 0xa6
+#define CX0342_CHANNEL_1_3_H_itx 0xa7
+#define CX0342_CHANNEL_1_4_L_itx 0xa8
+#define CX0342_CHANNEL_1_4_H_itx 0xa9
+#define CX0342_CHANNEL_1_5_L_itx 0xaa
+#define CX0342_CHANNEL_1_5_H_itx 0xab
+#define CX0342_CHANNEL_1_6_L_itx 0xac
+#define CX0342_CHANNEL_1_6_H_itx 0xad
+#define CX0342_CHANNEL_1_7_L_itx 0xae
+#define CX0342_CHANNEL_1_7_H_itx 0xaf
+#define CX0342_CHANNEL_2_0_L_iwl 0xb0
+#define CX0342_CHANNEL_2_0_H_iwl 0xb1
+#define CX0342_CHANNEL_2_1_L_iwl 0xb2
+#define CX0342_CHANNEL_2_1_H_iwl 0xb3
+#define CX0342_CHANNEL_2_2_L_iwl 0xb4
+#define CX0342_CHANNEL_2_2_H_iwl 0xb5
+#define CX0342_CHANNEL_2_3_L_iwl 0xb6
+#define CX0342_CHANNEL_2_3_H_iwl 0xb7
+#define CX0342_CHANNEL_2_4_L_iwl 0xb8
+#define CX0342_CHANNEL_2_4_H_iwl 0xb9
+#define CX0342_CHANNEL_2_5_L_iwl 0xba
+#define CX0342_CHANNEL_2_5_H_iwl 0xbb
+#define CX0342_CHANNEL_2_6_L_iwl 0xbc
+#define CX0342_CHANNEL_2_6_H_iwl 0xbd
+#define CX0342_CHANNEL_2_7_L_iwl 0xbe
+#define CX0342_CHANNEL_2_7_H_iwl 0xbf
+#define CX0342_CHANNEL_3_0_L_ensp 0xc0
+#define CX0342_CHANNEL_3_0_H_ensp 0xc1
+#define CX0342_CHANNEL_3_1_L_ensp 0xc2
+#define CX0342_CHANNEL_3_1_H_ensp 0xc3
+#define CX0342_CHANNEL_3_2_L_ensp 0xc4
+#define CX0342_CHANNEL_3_2_H_ensp 0xc5
+#define CX0342_CHANNEL_3_3_L_ensp 0xc6
+#define CX0342_CHANNEL_3_3_H_ensp 0xc7
+#define CX0342_CHANNEL_3_4_L_ensp 0xc8
+#define CX0342_CHANNEL_3_4_H_ensp 0xc9
+#define CX0342_CHANNEL_3_5_L_ensp 0xca
+#define CX0342_CHANNEL_3_5_H_ensp 0xcb
+#define CX0342_CHANNEL_3_6_L_ensp 0xcc
+#define CX0342_CHANNEL_3_6_H_ensp 0xcd
+#define CX0342_CHANNEL_3_7_L_ensp 0xce
+#define CX0342_CHANNEL_3_7_H_ensp 0xcf
+#define CX0342_CHANNEL_4_0_L_sela 0xd0
+#define CX0342_CHANNEL_4_0_H_sela 0xd1
+#define CX0342_CHANNEL_4_1_L_sela 0xd2
+#define CX0342_CHANNEL_4_1_H_sela 0xd3
+#define CX0342_CHANNEL_5_0_L_intla 0xe0
+#define CX0342_CHANNEL_5_0_H_intla 0xe1
+#define CX0342_CHANNEL_5_1_L_intla 0xe2
+#define CX0342_CHANNEL_5_1_H_intla 0xe3
+#define CX0342_CHANNEL_5_2_L_intla 0xe4
+#define CX0342_CHANNEL_5_2_H_intla 0xe5
+#define CX0342_CHANNEL_5_3_L_intla 0xe6
+#define CX0342_CHANNEL_5_3_H_intla 0xe7
+#define CX0342_CHANNEL_6_0_L_xa_sel_pos 0xf0
+#define CX0342_CHANNEL_6_0_H_xa_sel_pos 0xf1
+#define CX0342_CHANNEL_7_1_L_cds_pos 0xf2
+#define CX0342_CHANNEL_7_1_H_cds_pos 0xf3
+#define CX0342_SENSOR_HEIGHT_L 0xfb
+#define CX0342_SENSOR_HEIGHT_H 0xfc
+#define CX0342_SENSOR_WIDTH_L 0xfd
+#define CX0342_SENSOR_WIDTH_H 0xfe
+#define CX0342_VSYNC_HSYNC_READ 0xff
+
+struct cmd {
+ u8 reg;
+ u8 val;
+};
+
+static const u8 DQT[17][130] = {
+ /* Define quantization table (thanks to Thomas Kaiser) */
+ { /* Quality 0 */
+ 0x00,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x01,
+ 0x04, 0x04, 0x04, 0x06, 0x05, 0x06, 0x0b, 0x06,
+ 0x06, 0x0b, 0x18, 0x10, 0x0e, 0x10, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ },
+ { /* Quality 1 */
+ 0x00,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x01,
+ 0x08, 0x09, 0x09, 0x0c, 0x0a, 0x0c, 0x17, 0x0d,
+ 0x0d, 0x17, 0x31, 0x21, 0x1c, 0x21, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ },
+ { /* Quality 2 */
+ 0x00,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x06, 0x06, 0x06, 0x04, 0x04, 0x04,
+ 0x04, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x01,
+ 0x0c, 0x0d, 0x0d, 0x12, 0x0f, 0x12, 0x23, 0x13,
+ 0x13, 0x23, 0x4a, 0x31, 0x2a, 0x31, 0x4a, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+ },
+ { /* Quality 3 */
+ 0x00,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04,
+ 0x04, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x01,
+ 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a,
+ 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ },
+ { /* Quality 4 */
+ 0x00,
+ 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x0a, 0x0a, 0x0a, 0x05, 0x05, 0x05,
+ 0x05, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x01,
+ 0x11, 0x16, 0x16, 0x1e, 0x1a, 0x1e, 0x3a, 0x20,
+ 0x20, 0x3a, 0x7b, 0x52, 0x46, 0x52, 0x7b, 0x7b,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+ },
+ { /* Quality 5 */
+ 0x00,
+ 0x04, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x0c, 0x0c, 0x0c, 0x06, 0x06, 0x06,
+ 0x06, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x01,
+ 0x11, 0x1b, 0x1b, 0x24, 0x1f, 0x24, 0x46, 0x27,
+ 0x27, 0x46, 0x94, 0x63, 0x54, 0x63, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ },
+ { /* Quality 6 */
+ 0x00,
+ 0x05, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x0e, 0x0e, 0x0e, 0x07, 0x07, 0x07,
+ 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x01,
+ 0x15, 0x1f, 0x1f, 0x2a, 0x24, 0x2a, 0x52, 0x2d,
+ 0x2d, 0x52, 0xad, 0x73, 0x62, 0x73, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ },
+ { /* Quality 7 */
+ 0x00,
+ 0x05, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x10, 0x10, 0x10, 0x08, 0x08, 0x08,
+ 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x01,
+ 0x15, 0x24, 0x24, 0x30, 0x2a, 0x30, 0x5e, 0x34,
+ 0x34, 0x5e, 0xc6, 0x84, 0x70, 0x84, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ },
+ { /* Quality 8 */
+ 0x00,
+ 0x06, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x14, 0x14, 0x14, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x01,
+ 0x19, 0x2d, 0x2d, 0x3c, 0x34, 0x3c, 0x75, 0x41,
+ 0x41, 0x75, 0xf7, 0xa5, 0x8c, 0xa5, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ },
+ { /* Quality 9 */
+ 0x00,
+ 0x06, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x18, 0x18, 0x18, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x01,
+ 0x19, 0x36, 0x36, 0x48, 0x3f, 0x48, 0x8d, 0x4e,
+ 0x4e, 0x8d, 0xff, 0xc6, 0xa8, 0xc6, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ { /* Quality 10 */
+ 0x00,
+ 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x1c, 0x1c, 0x1c, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x01,
+ 0x1d, 0x3f, 0x3f, 0x54, 0x49, 0x54, 0xa4, 0x5b,
+ 0x5b, 0xa4, 0xff, 0xe7, 0xc4, 0xe7, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ { /* Quality 11 */
+ 0x00,
+ 0x07, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x20, 0x20, 0x20, 0x10, 0x10, 0x10,
+ 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,
+ 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,
+ 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,
+ 0x01,
+ 0x1d, 0x48, 0x48, 0x60, 0x54, 0x60, 0xbc, 0x68,
+ 0x68, 0xbc, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ { /* Quality 12 */
+ 0x00,
+ 0x08, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x28, 0x28, 0x28, 0x14, 0x14, 0x14,
+ 0x14, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
+ 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
+ 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
+ 0x01,
+ 0x22, 0x5a, 0x5a, 0x78, 0x69, 0x78, 0xeb, 0x82,
+ 0x82, 0xeb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ { /* Quality 13 */
+ 0x00,
+ 0x08, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x30, 0x30, 0x30, 0x18, 0x18, 0x18,
+ 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x72, 0x72, 0x72, 0x72, 0x72,
+ 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72,
+ 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72,
+ 0x01,
+ 0x22, 0x6c, 0x6c, 0x90, 0x7e, 0x90, 0xff, 0x9c,
+ 0x9c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ { /* Quality 14 */
+ 0x00,
+ 0x0a, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x38, 0x38, 0x38, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x01,
+ 0x2a, 0x7e, 0x7e, 0xa8, 0x93, 0xa8, 0xff, 0xb6,
+ 0xb6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ { /* Quality 15 */
+ 0x00,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x40, 0x40, 0x40, 0x20, 0x20, 0x20,
+ 0x20, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x01,
+ 0x2a, 0x90, 0x90, 0xc0, 0xa8, 0xc0, 0xff, 0xd0,
+ 0xd0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ { /* Quality 16-31 */
+ 0x00,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x01,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ }
+};
+
+static const struct cmd tp6810_cx_init_common[] = {
+ {0x1c, 0x00},
+ {TP6800_R10_SIF_TYPE, 0x00},
+ {0x4e, 0x00},
+ {0x4f, 0x00},
+ {TP6800_R50, 0xff},
+ {TP6800_R51, 0x03},
+ {0x00, 0x07},
+ {TP6800_R79_QUALITY, 0x03},
+ {TP6800_R2F_TIMING_CFG, 0x37},
+ {TP6800_R30_SENSOR_CFG, 0x10},
+ {TP6800_R21_ENDP_1_CTL, 0x00},
+ {TP6800_R52, 0x40},
+ {TP6800_R53, 0x40},
+ {TP6800_R54_DARK_CFG, 0x40},
+ {TP6800_R30_SENSOR_CFG, 0x18},
+ {0x4b, 0x00},
+ {TP6800_R3F_FRAME_RATE, 0x83},
+ {TP6800_R79_QUALITY, 0x05},
+ {TP6800_R21_ENDP_1_CTL, 0x00},
+ {0x7c, 0x04},
+ {0x25, 0x14},
+ {0x26, 0x0f},
+ {0x7b, 0x10},
+};
+
+static const struct cmd tp6810_ov_init_common[] = {
+ {0x1c, 0x00},
+ {TP6800_R10_SIF_TYPE, 0x00},
+ {0x4e, 0x00},
+ {0x4f, 0x00},
+ {TP6800_R50, 0xff},
+ {TP6800_R51, 0x03},
+ {0x00, 0x07},
+ {TP6800_R52, 0x40},
+ {TP6800_R53, 0x40},
+ {TP6800_R54_DARK_CFG, 0x40},
+ {TP6800_R79_QUALITY, 0x03},
+ {TP6800_R2F_TIMING_CFG, 0x17},
+ {TP6800_R30_SENSOR_CFG, 0x18},
+ {TP6800_R21_ENDP_1_CTL, 0x00},
+ {TP6800_R3F_FRAME_RATE, 0x86},
+ {0x25, 0x18},
+ {0x26, 0x0f},
+ {0x7b, 0x90},
+};
+
+static const struct cmd tp6810_bridge_start[] = {
+ {0x59, 0x88},
+ {0x5a, 0x0f},
+ {0x5b, 0x4e},
+ {TP6800_R5C_EDGE_THRLD, 0x63},
+ {TP6800_R5D_DEMOSAIC_CFG, 0x00},
+ {0x03, 0x7f},
+ {0x04, 0x80},
+ {0x06, 0x00},
+ {0x00, 0x00},
+};
+
+static const struct cmd tp6810_late_start[] = {
+ {0x7d, 0x01},
+ {0xb0, 0x04},
+ {0xb1, 0x04},
+ {0xb2, 0x04},
+ {0xb3, 0x04},
+ {0xb4, 0x04},
+ {0xb5, 0x04},
+ {0xb6, 0x08},
+ {0xb7, 0x08},
+ {0xb8, 0x04},
+ {0xb9, 0x04},
+ {0xba, 0x04},
+ {0xbb, 0x04},
+ {0xbc, 0x04},
+ {0xbd, 0x08},
+ {0xbe, 0x08},
+ {0xbf, 0x08},
+ {0xc0, 0x04},
+ {0xc1, 0x04},
+ {0xc2, 0x08},
+ {0xc3, 0x08},
+ {0xc4, 0x08},
+ {0xc5, 0x08},
+ {0xc6, 0x08},
+ {0xc7, 0x13},
+ {0xc8, 0x04},
+ {0xc9, 0x08},
+ {0xca, 0x08},
+ {0xcb, 0x08},
+ {0xcc, 0x08},
+ {0xcd, 0x08},
+ {0xce, 0x13},
+ {0xcf, 0x13},
+ {0xd0, 0x08},
+ {0xd1, 0x08},
+ {0xd2, 0x08},
+ {0xd3, 0x08},
+ {0xd4, 0x08},
+ {0xd5, 0x13},
+ {0xd6, 0x13},
+ {0xd7, 0x13},
+ {0xd8, 0x08},
+ {0xd9, 0x08},
+ {0xda, 0x08},
+ {0xdb, 0x08},
+ {0xdc, 0x13},
+ {0xdd, 0x13},
+ {0xde, 0x13},
+ {0xdf, 0x13},
+ {0xe0, 0x08},
+ {0xe1, 0x08},
+ {0xe2, 0x08},
+ {0xe3, 0x13},
+ {0xe4, 0x13},
+ {0xe5, 0x13},
+ {0xe6, 0x13},
+ {0xe7, 0x13},
+ {0xe8, 0x08},
+ {0xe9, 0x08},
+ {0xea, 0x13},
+ {0xeb, 0x13},
+ {0xec, 0x13},
+ {0xed, 0x13},
+ {0xee, 0x13},
+ {0xef, 0x13},
+ {0x7d, 0x02},
+
+ /* later after isoc start */
+ {0x7d, 0x08},
+ {0x7d, 0x00},
+};
+
+static const struct cmd cx0342_timing_seq[] = {
+ {CX0342_CHANNEL_0_1_L_irst, 0x20},
+ {CX0342_CHANNEL_0_2_L_irst, 0x24},
+ {CX0342_CHANNEL_0_2_H_irst, 0x00},
+ {CX0342_CHANNEL_0_3_L_irst, 0x2f},
+ {CX0342_CHANNEL_0_3_H_irst, 0x00},
+ {CX0342_CHANNEL_1_0_L_itx, 0x02},
+ {CX0342_CHANNEL_1_0_H_itx, 0x00},
+ {CX0342_CHANNEL_1_1_L_itx, 0x20},
+ {CX0342_CHANNEL_1_1_H_itx, 0x00},
+ {CX0342_CHANNEL_1_2_L_itx, 0xe4},
+ {CX0342_CHANNEL_1_2_H_itx, 0x00},
+ {CX0342_CHANNEL_1_3_L_itx, 0xee},
+ {CX0342_CHANNEL_1_3_H_itx, 0x00},
+ {CX0342_CHANNEL_2_0_L_iwl, 0x30},
+ {CX0342_CHANNEL_2_0_H_iwl, 0x00},
+ {CX0342_CHANNEL_3_0_L_ensp, 0x34},
+ {CX0342_CHANNEL_3_1_L_ensp, 0xe2},
+ {CX0342_CHANNEL_3_1_H_ensp, 0x00},
+ {CX0342_CHANNEL_3_2_L_ensp, 0xf6},
+ {CX0342_CHANNEL_3_2_H_ensp, 0x00},
+ {CX0342_CHANNEL_3_3_L_ensp, 0xf4},
+ {CX0342_CHANNEL_3_3_H_ensp, 0x02},
+ {CX0342_CHANNEL_4_0_L_sela, 0x26},
+ {CX0342_CHANNEL_4_0_H_sela, 0x00},
+ {CX0342_CHANNEL_4_1_L_sela, 0xe2},
+ {CX0342_CHANNEL_4_1_H_sela, 0x00},
+ {CX0342_CHANNEL_5_0_L_intla, 0x26},
+ {CX0342_CHANNEL_5_1_L_intla, 0x29},
+ {CX0342_CHANNEL_5_2_L_intla, 0xf0},
+ {CX0342_CHANNEL_5_2_H_intla, 0x00},
+ {CX0342_CHANNEL_5_3_L_intla, 0xf3},
+ {CX0342_CHANNEL_5_3_H_intla, 0x00},
+ {CX0342_CHANNEL_6_0_L_xa_sel_pos, 0x24},
+ {CX0342_CHANNEL_7_1_L_cds_pos, 0x02},
+ {CX0342_TIMING_EN, 0x01},
+};
+
+/* define the JPEG header */
+static void jpeg_define(u8 *jpeg_hdr,
+ int height,
+ int width)
+{
+ memcpy(jpeg_hdr, jpeg_head, sizeof jpeg_head);
+ jpeg_hdr[JPEG_HEIGHT_OFFSET + 0] = height >> 8;
+ jpeg_hdr[JPEG_HEIGHT_OFFSET + 1] = height;
+ jpeg_hdr[JPEG_HEIGHT_OFFSET + 2] = width >> 8;
+ jpeg_hdr[JPEG_HEIGHT_OFFSET + 3] = width;
+}
+
+/* set the JPEG quality for sensor soi763a */
+static void jpeg_set_qual(u8 *jpeg_hdr,
+ int quality)
+{
+ int i, sc;
+
+ if (quality < 50)
+ sc = 5000 / quality;
+ else
+ sc = 200 - quality * 2;
+ for (i = 0; i < 64; i++) {
+ jpeg_hdr[JPEG_QT0_OFFSET + i] =
+ (jpeg_head[JPEG_QT0_OFFSET + i] * sc + 50) / 100;
+ jpeg_hdr[JPEG_QT1_OFFSET + i] =
+ (jpeg_head[JPEG_QT1_OFFSET + i] * sc + 50) / 100;
+ }
+}
+
+static void reg_w(struct gspca_dev *gspca_dev, u8 index, u8 value)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x0e,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0, 500);
+ if (ret < 0) {
+ pr_err("reg_w err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+/* the returned value is in gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev, u8 index)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ 0x0d,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index, gspca_dev->usb_buf, 1, 500);
+ if (ret < 0) {
+ pr_err("reg_r err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static void reg_w_buf(struct gspca_dev *gspca_dev,
+ const struct cmd *p, int l)
+{
+ do {
+ reg_w(gspca_dev, p->reg, p->val);
+ p++;
+ } while (--l > 0);
+}
+
+static int i2c_w(struct gspca_dev *gspca_dev, u8 index, u8 value)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x00);
+ reg_w(gspca_dev, TP6800_R19_SIF_ADDR_S2, index);
+ reg_w(gspca_dev, TP6800_R13_SIF_TX_DATA, value);
+ reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x01);
+ if (sd->bridge == BRIDGE_TP6800)
+ return 0;
+ msleep(5);
+ reg_r(gspca_dev, TP6800_R11_SIF_CONTROL);
+ if (gspca_dev->usb_buf[0] == 0)
+ return 0;
+ reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x00);
+ return -1; /* error */
+}
+
+static void i2c_w_buf(struct gspca_dev *gspca_dev,
+ const struct cmd *p, int l)
+{
+ do {
+ i2c_w(gspca_dev, p->reg, p->val);
+ p++;
+ } while (--l > 0);
+}
+
+static int i2c_r(struct gspca_dev *gspca_dev, u8 index, int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int v;
+
+ reg_w(gspca_dev, TP6800_R19_SIF_ADDR_S2, index);
+ reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x02);
+ msleep(5);
+ reg_r(gspca_dev, TP6800_R14_SIF_RX_DATA);
+ v = gspca_dev->usb_buf[0];
+ if (sd->bridge == BRIDGE_TP6800)
+ return v;
+ if (len > 1) {
+ reg_r(gspca_dev, TP6800_R1B_SIF_RX_DATA2);
+ v |= (gspca_dev->usb_buf[0] << 8);
+ }
+ reg_r(gspca_dev, TP6800_R11_SIF_CONTROL);
+ if (gspca_dev->usb_buf[0] == 0)
+ return v;
+ reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x00);
+ return -1;
+}
+
+static void bulk_w(struct gspca_dev *gspca_dev,
+ u8 tag,
+ const u8 *data,
+ int length)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int count, actual_count, ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ for (;;) {
+ count = length > BULK_OUT_SIZE - 1
+ ? BULK_OUT_SIZE - 1 : length;
+ gspca_dev->usb_buf[0] = tag;
+ memcpy(&gspca_dev->usb_buf[1], data, count);
+ ret = usb_bulk_msg(dev,
+ usb_sndbulkpipe(dev, 3),
+ gspca_dev->usb_buf, count + 1,
+ &actual_count, 500);
+ if (ret < 0) {
+ pr_err("bulk write error %d tag=%02x\n",
+ ret, tag);
+ gspca_dev->usb_err = ret;
+ return;
+ }
+ length -= count;
+ if (length <= 0)
+ break;
+ data += count;
+ }
+}
+
+static int probe_6810(struct gspca_dev *gspca_dev)
+{
+ u8 gpio;
+ int ret;
+
+ reg_r(gspca_dev, TP6800_R18_GPIO_DATA);
+ gpio = gspca_dev->usb_buf[0];
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x04); /* i2c 16 bits */
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x21); /* ov??? */
+ reg_w(gspca_dev, TP6800_R1A_SIF_TX_DATA2, 0x00);
+ if (i2c_w(gspca_dev, 0x00, 0x00) >= 0)
+ return SENSOR_SOI763A;
+
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x00); /* i2c 8 bits */
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x7f); /* (unknown i2c) */
+ if (i2c_w(gspca_dev, 0x00, 0x00) >= 0)
+ return -2;
+
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x00); /* i2c 8 bits */
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x11); /* tas??? / hv??? */
+ ret = i2c_r(gspca_dev, 0x00, 1);
+ if (ret > 0)
+ return -3;
+
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x6e); /* po??? */
+ ret = i2c_r(gspca_dev, 0x00, 1);
+ if (ret > 0)
+ return -4;
+
+ ret = i2c_r(gspca_dev, 0x01, 1);
+ if (ret > 0)
+ return -5;
+
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x04); /* i2c 16 bits */
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x5d); /* mi/mt??? */
+ ret = i2c_r(gspca_dev, 0x00, 2);
+ if (ret > 0)
+ return -6;
+
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x5c); /* mi/mt??? */
+ ret = i2c_r(gspca_dev, 0x36, 2);
+ if (ret > 0)
+ return -7;
+
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x61); /* (unknown i2c) */
+ reg_w(gspca_dev, TP6800_R1A_SIF_TX_DATA2, 0x10);
+ if (i2c_w(gspca_dev, 0xff, 0x00) >= 0)
+ return -8;
+
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x00); /* i2c 8 bits */
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x20); /* cx0342 */
+ ret = i2c_r(gspca_dev, 0x00, 1);
+ if (ret > 0)
+ return SENSOR_CX0342;
+ return -9;
+}
+
+static void cx0342_6810_init(struct gspca_dev *gspca_dev)
+{
+ static const struct cmd reg_init_1[] = {
+ {TP6800_R2F_TIMING_CFG, 0x2f},
+ {0x25, 0x02},
+ {TP6800_R21_ENDP_1_CTL, 0x00},
+ {TP6800_R3F_FRAME_RATE, 0x80},
+ {TP6800_R2F_TIMING_CFG, 0x2f},
+ {TP6800_R18_GPIO_DATA, 0xe1},
+ {TP6800_R18_GPIO_DATA, 0xc1},
+ {TP6800_R18_GPIO_DATA, 0xe1},
+ {TP6800_R11_SIF_CONTROL, 0x00},
+ };
+ static const struct cmd reg_init_2[] = {
+ {TP6800_R78_FORMAT, 0x48},
+ {TP6800_R11_SIF_CONTROL, 0x00},
+ };
+ static const struct cmd sensor_init[] = {
+ {CX0342_OUTPUT_CTRL, 0x07},
+ {CX0342_BYPASS_MODE, 0x58},
+ {CX0342_GPXLTHD_L, 0x28},
+ {CX0342_RBPXLTHD_L, 0x28},
+ {CX0342_PLANETHD_L, 0x50},
+ {CX0342_PLANETHD_H, 0x03},
+ {CX0342_RB_GAP_L, 0xff},
+ {CX0342_RB_GAP_H, 0x07},
+ {CX0342_G_GAP_L, 0xff},
+ {CX0342_G_GAP_H, 0x07},
+ {CX0342_RST_OVERFLOW_L, 0x5c},
+ {CX0342_RST_OVERFLOW_H, 0x01},
+ {CX0342_DATA_OVERFLOW_L, 0xfc},
+ {CX0342_DATA_OVERFLOW_H, 0x03},
+ {CX0342_DATA_UNDERFLOW_L, 0x00},
+ {CX0342_DATA_UNDERFLOW_H, 0x00},
+ {CX0342_SYS_CTRL_0, 0x40},
+ {CX0342_GLOBAL_GAIN, 0x01},
+ {CX0342_CLOCK_GEN, 0x00},
+ {CX0342_SYS_CTRL_0, 0x02},
+ {CX0342_IDLE_CTRL, 0x05},
+ {CX0342_ADCGN, 0x00},
+ {CX0342_ADC_CTL, 0x00},
+ {CX0342_LVRST_BLBIAS, 0x01},
+ {CX0342_VTHSEL, 0x0b},
+ {CX0342_RAMP_RIV, 0x0b},
+ {CX0342_LDOSEL, 0x07},
+ {CX0342_SPV_VALUE_L, 0x40},
+ {CX0342_SPV_VALUE_H, 0x02},
+
+ {CX0342_AUTO_ADC_CALIB, 0x81},
+ {CX0342_TIMING_EN, 0x01},
+ };
+
+ reg_w_buf(gspca_dev, reg_init_1, ARRAY_SIZE(reg_init_1));
+ reg_w_buf(gspca_dev, tp6810_cx_init_common,
+ ARRAY_SIZE(tp6810_cx_init_common));
+ reg_w_buf(gspca_dev, reg_init_2, ARRAY_SIZE(reg_init_2));
+
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x20); /* cx0342 I2C addr */
+ i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init));
+ i2c_w_buf(gspca_dev, cx0342_timing_seq, ARRAY_SIZE(cx0342_timing_seq));
+}
+
+static void soi763a_6810_init(struct gspca_dev *gspca_dev)
+{
+ static const struct cmd reg_init_1[] = {
+ {TP6800_R2F_TIMING_CFG, 0x2f},
+ {TP6800_R18_GPIO_DATA, 0xe1},
+ {0x25, 0x02},
+ {TP6800_R21_ENDP_1_CTL, 0x00},
+ {TP6800_R3F_FRAME_RATE, 0x80},
+ {TP6800_R2F_TIMING_CFG, 0x2f},
+ {TP6800_R18_GPIO_DATA, 0xc1},
+ };
+ static const struct cmd reg_init_2[] = {
+ {TP6800_R78_FORMAT, 0x54},
+ };
+ static const struct cmd sensor_init[] = {
+ {0x00, 0x00},
+ {0x01, 0x80},
+ {0x02, 0x80},
+ {0x03, 0x90},
+ {0x04, 0x20},
+ {0x05, 0x20},
+ {0x06, 0x80},
+ {0x07, 0x00},
+ {0x08, 0xff},
+ {0x09, 0xff},
+ {0x0a, 0x76}, /* 7630 = soi673a */
+ {0x0b, 0x30},
+ {0x0c, 0x20},
+ {0x0d, 0x20},
+ {0x0e, 0xff},
+ {0x0f, 0xff},
+ {0x10, 0x41},
+ {0x15, 0x14},
+ {0x11, 0x40},
+ {0x12, 0x48},
+ {0x13, 0x80},
+ {0x14, 0x80},
+ {0x16, 0x03},
+ {0x28, 0xb0},
+ {0x71, 0x20},
+ {0x75, 0x8e},
+ {0x17, 0x1b},
+ {0x18, 0xbd},
+ {0x19, 0x05},
+ {0x1a, 0xf6},
+ {0x1b, 0x04},
+ {0x1c, 0x7f}, /* omnivision */
+ {0x1d, 0xa2},
+ {0x1e, 0x00},
+ {0x1f, 0x00},
+ {0x20, 0x45},
+ {0x21, 0x80},
+ {0x22, 0x80},
+ {0x23, 0xee},
+ {0x24, 0x50},
+ {0x25, 0x7a},
+ {0x26, 0xa0},
+ {0x27, 0x9a},
+ {0x29, 0x30},
+ {0x2a, 0x80},
+ {0x2b, 0x00},
+ {0x2c, 0xac},
+ {0x2d, 0x05},
+ {0x2e, 0x80},
+ {0x2f, 0x3c},
+ {0x30, 0x22},
+ {0x31, 0x00},
+ {0x32, 0x86},
+ {0x33, 0x08},
+ {0x34, 0xff},
+ {0x35, 0xff},
+ {0x36, 0xff},
+ {0x37, 0xff},
+ {0x38, 0xff},
+ {0x39, 0xff},
+ {0x3a, 0xfe},
+ {0x3b, 0xfe},
+ {0x3c, 0xfe},
+ {0x3d, 0xfe},
+ {0x3e, 0xfe},
+ {0x3f, 0x71},
+ {0x40, 0xff},
+ {0x41, 0xff},
+ {0x42, 0xff},
+ {0x43, 0xff},
+ {0x44, 0xff},
+ {0x45, 0xff},
+ {0x46, 0xff},
+ {0x47, 0xff},
+ {0x48, 0xff},
+ {0x49, 0xff},
+ {0x4a, 0xfe},
+ {0x4b, 0xff},
+ {0x4c, 0x00},
+ {0x4d, 0x00},
+ {0x4e, 0xff},
+ {0x4f, 0xff},
+ {0x50, 0xff},
+ {0x51, 0xff},
+ {0x52, 0xff},
+ {0x53, 0xff},
+ {0x54, 0xff},
+ {0x55, 0xff},
+ {0x56, 0xff},
+ {0x57, 0xff},
+ {0x58, 0xff},
+ {0x59, 0xff},
+ {0x5a, 0xff},
+ {0x5b, 0xfe},
+ {0x5c, 0xff},
+ {0x5d, 0x8f},
+ {0x5e, 0xff},
+ {0x5f, 0x8f},
+ {0x60, 0xa2},
+ {0x61, 0x4a},
+ {0x62, 0xf3},
+ {0x63, 0x75},
+ {0x64, 0xf0},
+ {0x65, 0x00},
+ {0x66, 0x55},
+ {0x67, 0x92},
+ {0x68, 0xa0},
+ {0x69, 0x4a},
+ {0x6a, 0x22},
+ {0x6b, 0x00},
+ {0x6c, 0x33},
+ {0x6d, 0x44},
+ {0x6e, 0x22},
+ {0x6f, 0x84},
+ {0x70, 0x0b},
+ {0x72, 0x10},
+ {0x73, 0x50},
+ {0x74, 0x21},
+ {0x76, 0x00},
+ {0x77, 0xa5},
+ {0x78, 0x80},
+ {0x79, 0x80},
+ {0x7a, 0x80},
+ {0x7b, 0xe2},
+ {0x7c, 0x00},
+ {0x7d, 0xf7},
+ {0x7e, 0x00},
+ {0x7f, 0x00},
+ };
+
+ reg_w_buf(gspca_dev, reg_init_1, ARRAY_SIZE(reg_init_1));
+ reg_w_buf(gspca_dev, tp6810_ov_init_common,
+ ARRAY_SIZE(tp6810_ov_init_common));
+ reg_w_buf(gspca_dev, reg_init_2, ARRAY_SIZE(reg_init_2));
+
+ i2c_w(gspca_dev, 0x12, 0x80); /* sensor reset */
+ msleep(10);
+ i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init));
+}
+
+/* set the gain and exposure */
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor == SENSOR_CX0342) {
+ int expo;
+
+ expo = (sd->ctrls[EXPOSURE].val << 2) - 1;
+ i2c_w(gspca_dev, CX0342_EXPO_LINE_L, expo);
+ i2c_w(gspca_dev, CX0342_EXPO_LINE_H, expo >> 8);
+ if (sd->bridge == BRIDGE_TP6800)
+ i2c_w(gspca_dev, CX0342_RAW_GBGAIN_H,
+ sd->ctrls[GAIN].val >> 8);
+ i2c_w(gspca_dev, CX0342_RAW_GBGAIN_L, sd->ctrls[GAIN].val);
+ if (sd->bridge == BRIDGE_TP6800)
+ i2c_w(gspca_dev, CX0342_RAW_GRGAIN_H,
+ sd->ctrls[GAIN].val >> 8);
+ i2c_w(gspca_dev, CX0342_RAW_GRGAIN_L, sd->ctrls[GAIN].val);
+ if (sd->bridge == BRIDGE_TP6800)
+ i2c_w(gspca_dev, CX0342_RAW_BGAIN_H,
+ sd->ctrls[BGAIN].val >> 8);
+ i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, sd->ctrls[BGAIN].val);
+ if (sd->bridge == BRIDGE_TP6800)
+ i2c_w(gspca_dev, CX0342_RAW_RGAIN_H,
+ sd->ctrls[RGAIN].val >> 8);
+ i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, sd->ctrls[RGAIN].val);
+ i2c_w(gspca_dev, CX0342_SYS_CTRL_0,
+ sd->bridge == BRIDGE_TP6800 ? 0x80 : 0x81);
+ return;
+ }
+
+ /* soi763a */
+ i2c_w(gspca_dev, 0x10, /* AEC_H (exposure time) */
+ sd->ctrls[EXPOSURE].val);
+/* i2c_w(gspca_dev, 0x76, 0x02); * AEC_L ([1:0] */
+ i2c_w(gspca_dev, 0x00, /* gain */
+ sd->ctrls[GAIN].val);
+}
+
+/* set the JPEG quantization tables */
+static void set_dqt(struct gspca_dev *gspca_dev, u8 q)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* update the jpeg quantization tables */
+ PDEBUG(D_STREAM, "q %d -> %d", sd->quality, q);
+ sd->quality = q;
+ if (q > 16)
+ q = 16;
+ if (sd->sensor == SENSOR_SOI763A)
+ jpeg_set_qual(sd->jpeg_hdr, jpeg_q[q]);
+ else
+ memcpy(&sd->jpeg_hdr[JPEG_QT0_OFFSET - 1],
+ DQT[q], sizeof DQT[0]);
+}
+
+/* set the JPEG compression quality factor */
+static void setquality(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 q;
+
+ q = sd->ctrls[QUALITY].val;
+ if (q != 16)
+ q = 15 - q;
+
+ reg_w(gspca_dev, TP6800_R7A_BLK_THRLD, 0x00);
+ reg_w(gspca_dev, TP6800_R79_QUALITY, 0x04);
+ reg_w(gspca_dev, TP6800_R79_QUALITY, q);
+
+ /* auto quality */
+ if (q == 15 && sd->bridge == BRIDGE_TP6810) {
+ msleep(4);
+ reg_w(gspca_dev, TP6800_R7A_BLK_THRLD, 0x19);
+ }
+}
+
+static const u8 color_null[18] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+static const u8 color_gain[NSENSORS][18] = {
+[SENSOR_CX0342] =
+ {0x4c, 0x00, 0xa9, 0x00, 0x31, 0x00, /* Y R/G/B (LE values) */
+ 0xb6, 0x03, 0x6c, 0x03, 0xe0, 0x00, /* U R/G/B */
+ 0xdf, 0x00, 0x46, 0x03, 0xdc, 0x03}, /* V R/G/B */
+[SENSOR_SOI763A] =
+ {0x4c, 0x00, 0x95, 0x00, 0x1d, 0x00, /* Y R/G/B (LE values) */
+ 0xb6, 0x03, 0x6c, 0x03, 0xd7, 0x00, /* U R/G/B */
+ 0xd5, 0x00, 0x46, 0x03, 0xdc, 0x03}, /* V R/G/B */
+};
+
+static void setgamma(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int gamma;
+#define NGAMMA 6
+ static const u8 gamma_tb[NGAMMA][3][1024] = {
+ { /* gamma 0 - from tp6800 + soi763a */
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x03, 0x05, 0x07, 0x07, 0x08, 0x09, 0x09,
+ 0x0a, 0x0c, 0x0c, 0x0d, 0x0e, 0x0e, 0x10, 0x11,
+ 0x11, 0x12, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17,
+ 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e,
+ 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x23,
+ 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28,
+ 0x29, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f,
+ 0x2f, 0x30, 0x30, 0x31, 0x31, 0x33, 0x33, 0x34,
+ 0x34, 0x34, 0x35, 0x35, 0x37, 0x37, 0x38, 0x38,
+ 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c,
+ 0x3c, 0x3d, 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x40,
+ 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45,
+ 0x45, 0x47, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49,
+ 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c,
+ 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, 0x50,
+ 0x52, 0x52, 0x52, 0x53, 0x53, 0x54, 0x54, 0x54,
+ 0x55, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, 0x58,
+ 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b,
+ 0x5b, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5f,
+ 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61,
+ 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x65, 0x65,
+ 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68,
+ 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a,
+ 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e,
+ 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70,
+ 0x70, 0x71, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73,
+ 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77,
+ 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79,
+ 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b,
+ 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d,
+ 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80,
+ 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82,
+ 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86,
+ 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89,
+ 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
+ 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93,
+ 0x94, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98,
+ 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
+ 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9,
+ 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca,
+ 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
+ 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6,
+ 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3,
+ 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
+ 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed,
+ 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x03, 0x05, 0x07, 0x07, 0x08, 0x09, 0x09,
+ 0x0a, 0x0c, 0x0c, 0x0d, 0x0e, 0x0e, 0x10, 0x11,
+ 0x11, 0x12, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17,
+ 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e,
+ 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x23,
+ 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28,
+ 0x29, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f,
+ 0x2f, 0x30, 0x30, 0x31, 0x31, 0x33, 0x33, 0x34,
+ 0x34, 0x34, 0x35, 0x35, 0x37, 0x37, 0x38, 0x38,
+ 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c,
+ 0x3c, 0x3d, 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x40,
+ 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45,
+ 0x45, 0x47, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49,
+ 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c,
+ 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, 0x50,
+ 0x52, 0x52, 0x52, 0x53, 0x53, 0x54, 0x54, 0x54,
+ 0x55, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, 0x58,
+ 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b,
+ 0x5b, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5f,
+ 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61,
+ 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x65, 0x65,
+ 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68,
+ 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a,
+ 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e,
+ 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70,
+ 0x70, 0x71, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73,
+ 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77,
+ 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79,
+ 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b,
+ 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d,
+ 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80,
+ 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82,
+ 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86,
+ 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89,
+ 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
+ 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93,
+ 0x94, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98,
+ 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
+ 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9,
+ 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca,
+ 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
+ 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6,
+ 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3,
+ 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
+ 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed,
+ 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x03, 0x05, 0x07, 0x07, 0x08, 0x09, 0x09,
+ 0x0a, 0x0c, 0x0c, 0x0d, 0x0e, 0x0e, 0x10, 0x11,
+ 0x11, 0x12, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17,
+ 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e,
+ 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x23,
+ 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28,
+ 0x29, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f,
+ 0x2f, 0x30, 0x30, 0x31, 0x31, 0x33, 0x33, 0x34,
+ 0x34, 0x34, 0x35, 0x35, 0x37, 0x37, 0x38, 0x38,
+ 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c,
+ 0x3c, 0x3d, 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x40,
+ 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45,
+ 0x45, 0x47, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49,
+ 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c,
+ 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, 0x50,
+ 0x52, 0x52, 0x52, 0x53, 0x53, 0x54, 0x54, 0x54,
+ 0x55, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, 0x58,
+ 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b,
+ 0x5b, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5f,
+ 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61,
+ 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x65, 0x65,
+ 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68,
+ 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a,
+ 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e,
+ 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70,
+ 0x70, 0x71, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73,
+ 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x76,
+ 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79,
+ 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b,
+ 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d,
+ 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80,
+ 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82,
+ 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86,
+ 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89,
+ 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
+ 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93,
+ 0x94, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98,
+ 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
+ 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9,
+ 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca,
+ 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
+ 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6,
+ 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3,
+ 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
+ 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed,
+ 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}
+ },
+ { /* gamma 1 - from tp6810 + soi763a */
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x03, 0x05, 0x07, 0x08, 0x09, 0x0a,
+ 0x0c, 0x0d, 0x0e, 0x10, 0x11, 0x12, 0x14, 0x15,
+ 0x16, 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1c, 0x1e,
+ 0x1f, 0x20, 0x22, 0x22, 0x23, 0x25, 0x26, 0x27,
+ 0x27, 0x28, 0x29, 0x2b, 0x2b, 0x2c, 0x2d, 0x2f,
+ 0x2f, 0x30, 0x31, 0x33, 0x33, 0x34, 0x35, 0x35,
+ 0x37, 0x38, 0x38, 0x39, 0x3a, 0x3a, 0x3b, 0x3c,
+ 0x3c, 0x3d, 0x3f, 0x3f, 0x40, 0x42, 0x42, 0x43,
+ 0x43, 0x44, 0x45, 0x45, 0x47, 0x47, 0x48, 0x49,
+ 0x49, 0x4a, 0x4a, 0x4b, 0x4b, 0x4c, 0x4d, 0x4d,
+ 0x4f, 0x4f, 0x50, 0x50, 0x52, 0x52, 0x53, 0x53,
+ 0x54, 0x54, 0x55, 0x56, 0x56, 0x58, 0x58, 0x59,
+ 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5e,
+ 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61, 0x61,
+ 0x62, 0x62, 0x63, 0x63, 0x65, 0x65, 0x65, 0x66,
+ 0x66, 0x67, 0x67, 0x68, 0x68, 0x69, 0x69, 0x69,
+ 0x6a, 0x6a, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e,
+ 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71,
+ 0x73, 0x73, 0x73, 0x74, 0x74, 0x74, 0x75, 0x75,
+ 0x77, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x79,
+ 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c,
+ 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, 0x80, 0x80, 0x80,
+ 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x84, 0x84,
+ 0x84, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x88,
+ 0x88, 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a,
+ 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e,
+ 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x91,
+ 0x91, 0x91, 0x92, 0x92, 0x92, 0x92, 0x93, 0x93,
+ 0x93, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x97,
+ 0x97, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99,
+ 0x99, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3,
+ 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xab,
+ 0xac, 0xac, 0xac, 0xad, 0xad, 0xad, 0xad, 0xae,
+ 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2,
+ 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4,
+ 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7,
+ 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca,
+ 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1,
+ 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4,
+ 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8,
+ 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda,
+ 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
+ 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0,
+ 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1,
+ 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3,
+ 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
+ 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8,
+ 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9,
+ 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec,
+ 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef,
+ 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3,
+ 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5,
+ 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03,
+ 0x05, 0x07, 0x07, 0x08, 0x09, 0x0a, 0x0c, 0x0d,
+ 0x0e, 0x10, 0x10, 0x11, 0x12, 0x14, 0x15, 0x15,
+ 0x16, 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1c, 0x1e,
+ 0x1e, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x25, 0x25,
+ 0x26, 0x27, 0x27, 0x28, 0x29, 0x29, 0x2b, 0x2c,
+ 0x2c, 0x2d, 0x2d, 0x2f, 0x30, 0x30, 0x31, 0x31,
+ 0x33, 0x34, 0x34, 0x35, 0x35, 0x37, 0x38, 0x38,
+ 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d,
+ 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x42, 0x42, 0x43,
+ 0x43, 0x44, 0x44, 0x45, 0x45, 0x47, 0x47, 0x48,
+ 0x48, 0x49, 0x49, 0x4a, 0x4a, 0x4b, 0x4b, 0x4c,
+ 0x4c, 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50,
+ 0x52, 0x52, 0x53, 0x53, 0x53, 0x54, 0x54, 0x55,
+ 0x55, 0x56, 0x56, 0x56, 0x58, 0x58, 0x59, 0x59,
+ 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5c,
+ 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x60, 0x60, 0x60,
+ 0x61, 0x61, 0x62, 0x62, 0x62, 0x63, 0x63, 0x65,
+ 0x65, 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67,
+ 0x68, 0x68, 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a,
+ 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e,
+ 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x71,
+ 0x71, 0x71, 0x73, 0x73, 0x73, 0x74, 0x74, 0x74,
+ 0x75, 0x75, 0x75, 0x77, 0x77, 0x77, 0x78, 0x78,
+ 0x78, 0x79, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7a,
+ 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d,
+ 0x7d, 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80,
+ 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82,
+ 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86,
+ 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89, 0x89,
+ 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90,
+ 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92,
+ 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94,
+ 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x97,
+ 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x99,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9,
+ 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca,
+ 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xce, 0xcf,
+ 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0,
+ 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1,
+ 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4,
+ 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8,
+ 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3,
+ 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
+ 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9,
+ 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb,
+ 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed,
+ 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4,
+ 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5,
+ 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc,
+ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x02, 0x03, 0x05, 0x05, 0x07,
+ 0x08, 0x09, 0x0a, 0x0a, 0x0c, 0x0d, 0x0e, 0x0e,
+ 0x10, 0x11, 0x12, 0x12, 0x14, 0x15, 0x16, 0x16,
+ 0x17, 0x18, 0x18, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e,
+ 0x1e, 0x1f, 0x1f, 0x20, 0x22, 0x22, 0x23, 0x23,
+ 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x29, 0x29,
+ 0x2b, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f, 0x30,
+ 0x30, 0x31, 0x31, 0x33, 0x33, 0x34, 0x34, 0x35,
+ 0x35, 0x37, 0x37, 0x38, 0x38, 0x39, 0x39, 0x3a,
+ 0x3a, 0x3b, 0x3b, 0x3b, 0x3c, 0x3c, 0x3d, 0x3d,
+ 0x3f, 0x3f, 0x40, 0x40, 0x42, 0x42, 0x42, 0x43,
+ 0x43, 0x44, 0x44, 0x45, 0x45, 0x47, 0x47, 0x47,
+ 0x48, 0x48, 0x49, 0x49, 0x49, 0x4a, 0x4a, 0x4b,
+ 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4f,
+ 0x4f, 0x50, 0x50, 0x50, 0x52, 0x52, 0x52, 0x53,
+ 0x53, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x56,
+ 0x56, 0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x5a,
+ 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5c, 0x5c, 0x5c,
+ 0x5e, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x60, 0x60,
+ 0x60, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 0x63,
+ 0x63, 0x63, 0x65, 0x65, 0x65, 0x66, 0x66, 0x66,
+ 0x66, 0x67, 0x67, 0x67, 0x68, 0x68, 0x68, 0x69,
+ 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6a, 0x6c, 0x6c,
+ 0x6c, 0x6d, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e,
+ 0x6f, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x71,
+ 0x71, 0x71, 0x71, 0x73, 0x73, 0x73, 0x74, 0x74,
+ 0x74, 0x74, 0x75, 0x75, 0x75, 0x75, 0x77, 0x77,
+ 0x77, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79,
+ 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b,
+ 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d,
+ 0x7d, 0x7f, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80,
+ 0x80, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82,
+ 0x82, 0x84, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85,
+ 0x85, 0x86, 0x86, 0x86, 0x86, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x89, 0x89, 0x89, 0x89, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98,
+ 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2,
+ 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
+ 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
+ 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3,
+ 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4,
+ 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7,
+ 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8,
+ 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0,
+ 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1,
+ 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3,
+ 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4,
+ 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
+ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb,
+ 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
+ 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xee,
+ 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, 0xef,
+ 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1,
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3,
+ 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5,
+ 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc,
+ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd,
+ 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
+ },
+ { /* gamma 2 */
+ {0x00, 0x01, 0x02, 0x05, 0x07, 0x08, 0x0a, 0x0c,
+ 0x0d, 0x0e, 0x10, 0x12, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x1a, 0x1b, 0x1c, 0x1e, 0x1f, 0x20, 0x22,
+ 0x23, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2b, 0x2c,
+ 0x2d, 0x2d, 0x2f, 0x30, 0x31, 0x33, 0x34, 0x34,
+ 0x35, 0x37, 0x38, 0x38, 0x39, 0x3a, 0x3b, 0x3b,
+ 0x3c, 0x3d, 0x3f, 0x3f, 0x40, 0x42, 0x42, 0x43,
+ 0x44, 0x44, 0x45, 0x47, 0x47, 0x48, 0x49, 0x49,
+ 0x4a, 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4f, 0x4f,
+ 0x50, 0x50, 0x52, 0x53, 0x53, 0x54, 0x54, 0x55,
+ 0x55, 0x56, 0x56, 0x58, 0x58, 0x59, 0x5a, 0x5a,
+ 0x5b, 0x5b, 0x5c, 0x5c, 0x5e, 0x5e, 0x5f, 0x5f,
+ 0x60, 0x60, 0x61, 0x61, 0x62, 0x62, 0x63, 0x63,
+ 0x65, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68,
+ 0x68, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6c, 0x6c,
+ 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x70,
+ 0x70, 0x70, 0x71, 0x71, 0x73, 0x73, 0x73, 0x74,
+ 0x74, 0x75, 0x75, 0x75, 0x77, 0x77, 0x78, 0x78,
+ 0x78, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7b, 0x7b,
+ 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7f, 0x7f,
+ 0x7f, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x82,
+ 0x82, 0x82, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85,
+ 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x89, 0x89,
+ 0x89, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f,
+ 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
+ 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x94,
+ 0x94, 0x94, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97,
+ 0x97, 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3,
+ 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab,
+ 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3,
+ 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4,
+ 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7,
+ 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
+ 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xce,
+ 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0,
+ 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1,
+ 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4,
+ 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde,
+ 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0,
+ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1,
+ 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2,
+ 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4,
+ 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8,
+ 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9,
+ 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb,
+ 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed,
+ 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1,
+ 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3,
+ 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5,
+ 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9,
+ 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+ {0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x05,
+ 0x07, 0x08, 0x09, 0x0a, 0x0d, 0x0e, 0x10, 0x11,
+ 0x12, 0x14, 0x15, 0x16, 0x16, 0x17, 0x18, 0x1a,
+ 0x1b, 0x1c, 0x1e, 0x1f, 0x20, 0x20, 0x22, 0x23,
+ 0x25, 0x26, 0x26, 0x27, 0x28, 0x29, 0x29, 0x2b,
+ 0x2c, 0x2d, 0x2d, 0x2f, 0x30, 0x30, 0x31, 0x33,
+ 0x33, 0x34, 0x35, 0x35, 0x37, 0x38, 0x38, 0x39,
+ 0x3a, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3d, 0x3f,
+ 0x3f, 0x40, 0x42, 0x42, 0x43, 0x43, 0x44, 0x44,
+ 0x45, 0x45, 0x47, 0x47, 0x48, 0x48, 0x49, 0x4a,
+ 0x4a, 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d,
+ 0x4f, 0x4f, 0x50, 0x50, 0x52, 0x52, 0x53, 0x53,
+ 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x56, 0x58,
+ 0x58, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b,
+ 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f,
+ 0x60, 0x60, 0x61, 0x61, 0x61, 0x62, 0x62, 0x63,
+ 0x63, 0x63, 0x65, 0x65, 0x65, 0x66, 0x66, 0x67,
+ 0x67, 0x67, 0x68, 0x68, 0x68, 0x69, 0x69, 0x69,
+ 0x6a, 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d,
+ 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70,
+ 0x70, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73, 0x73,
+ 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77, 0x77,
+ 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79,
+ 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7b, 0x7c,
+ 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7d, 0x7f, 0x7f,
+ 0x7f, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81,
+ 0x82, 0x82, 0x82, 0x82, 0x84, 0x84, 0x84, 0x84,
+ 0x85, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x88,
+ 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x89, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92,
+ 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97,
+ 0x97, 0x98, 0x98, 0x98, 0x98, 0x98, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xad, 0xad, 0xad, 0xad, 0xad, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4,
+ 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7,
+ 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9,
+ 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca,
+ 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce,
+ 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3,
+ 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4,
+ 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda,
+ 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb,
+ 0xdb, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1,
+ 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2,
+ 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4,
+ 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6,
+ 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9,
+ 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
+ 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
+ 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xef,
+ 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4,
+ 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5,
+ 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+ {0x00, 0x00, 0x00, 0x01, 0x02, 0x05, 0x07, 0x08,
+ 0x09, 0x0a, 0x0c, 0x0e, 0x10, 0x11, 0x12, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x1a, 0x1b, 0x1c, 0x1e,
+ 0x1f, 0x20, 0x20, 0x22, 0x23, 0x25, 0x26, 0x27,
+ 0x28, 0x28, 0x29, 0x2b, 0x2c, 0x2d, 0x2d, 0x2f,
+ 0x30, 0x31, 0x31, 0x33, 0x34, 0x35, 0x35, 0x37,
+ 0x38, 0x38, 0x39, 0x3a, 0x3a, 0x3b, 0x3c, 0x3c,
+ 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x42, 0x43, 0x43,
+ 0x44, 0x44, 0x45, 0x47, 0x47, 0x48, 0x48, 0x49,
+ 0x4a, 0x4a, 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4d,
+ 0x4f, 0x4f, 0x50, 0x50, 0x52, 0x52, 0x53, 0x53,
+ 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58,
+ 0x59, 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c,
+ 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61,
+ 0x61, 0x61, 0x62, 0x62, 0x63, 0x63, 0x65, 0x65,
+ 0x65, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68, 0x68,
+ 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6c, 0x6c,
+ 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f,
+ 0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x73, 0x73,
+ 0x73, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77,
+ 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x7a,
+ 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c,
+ 0x7c, 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, 0x7f, 0x80,
+ 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82,
+ 0x82, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85,
+ 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89,
+ 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90,
+ 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92,
+ 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94,
+ 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x97,
+ 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x99,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb4, 0xb3, 0xb4, 0xb4, 0xb4,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
+ 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
+ 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3,
+ 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4,
+ 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda,
+ 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
+ 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
+ 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3,
+ 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4,
+ 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6,
+ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9,
+ 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec,
+ 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed,
+ 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1,
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3,
+ 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4,
+ 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}
+ },
+ { /* gamma 3 - from tp6810 + cx0342 */
+ {0x08, 0x09, 0x0c, 0x0d, 0x10, 0x11, 0x14, 0x15,
+ 0x17, 0x18, 0x1a, 0x1c, 0x1e, 0x1f, 0x20, 0x23,
+ 0x25, 0x26, 0x27, 0x28, 0x2b, 0x2c, 0x2d, 0x2f,
+ 0x30, 0x31, 0x33, 0x34, 0x35, 0x37, 0x38, 0x39,
+ 0x3a, 0x3b, 0x3c, 0x3d, 0x3f, 0x40, 0x42, 0x43,
+ 0x44, 0x45, 0x47, 0x48, 0x48, 0x49, 0x4a, 0x4b,
+ 0x4c, 0x4d, 0x4d, 0x4f, 0x50, 0x52, 0x53, 0x53,
+ 0x54, 0x55, 0x56, 0x56, 0x58, 0x59, 0x5a, 0x5a,
+ 0x5b, 0x5c, 0x5c, 0x5e, 0x5f, 0x5f, 0x60, 0x61,
+ 0x61, 0x62, 0x63, 0x63, 0x65, 0x66, 0x66, 0x67,
+ 0x68, 0x68, 0x69, 0x69, 0x6a, 0x6c, 0x6c, 0x6d,
+ 0x6d, 0x6e, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x73,
+ 0x73, 0x74, 0x74, 0x75, 0x75, 0x77, 0x77, 0x78,
+ 0x78, 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c,
+ 0x7d, 0x7d, 0x7f, 0x7f, 0x80, 0x80, 0x81, 0x81,
+ 0x82, 0x82, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86,
+ 0x86, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b,
+ 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, 0x8f,
+ 0x90, 0x90, 0x91, 0x91, 0x91, 0x92, 0x92, 0x93,
+ 0x93, 0x93, 0x94, 0x94, 0x96, 0x96, 0x97, 0x97,
+ 0x97, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d,
+ 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1,
+ 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+ 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa8, 0xa8,
+ 0xa8, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xac,
+ 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1,
+ 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
+ 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9,
+ 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd,
+ 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf,
+ 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2,
+ 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7,
+ 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
+ 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc,
+ 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce,
+ 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0,
+ 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3,
+ 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6,
+ 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8,
+ 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda,
+ 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1,
+ 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2,
+ 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
+ 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8,
+ 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb,
+ 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
+ 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee,
+ 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x03, 0x05, 0x07, 0x09, 0x0a, 0x0c, 0x0d, 0x10,
+ 0x11, 0x12, 0x14, 0x15, 0x17, 0x18, 0x1a, 0x1b,
+ 0x1c, 0x1e, 0x1f, 0x20, 0x22, 0x23, 0x25, 0x26,
+ 0x27, 0x28, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2f,
+ 0x30, 0x31, 0x33, 0x33, 0x34, 0x35, 0x37, 0x38,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3f,
+ 0x3f, 0x40, 0x42, 0x42, 0x43, 0x44, 0x45, 0x45,
+ 0x47, 0x47, 0x48, 0x49, 0x49, 0x4a, 0x4b, 0x4b,
+ 0x4c, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x52, 0x52,
+ 0x53, 0x53, 0x54, 0x54, 0x55, 0x55, 0x56, 0x58,
+ 0x58, 0x59, 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c,
+ 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61,
+ 0x61, 0x62, 0x62, 0x63, 0x63, 0x65, 0x65, 0x66,
+ 0x66, 0x67, 0x67, 0x67, 0x68, 0x68, 0x69, 0x69,
+ 0x6a, 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6e,
+ 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71,
+ 0x71, 0x73, 0x73, 0x74, 0x74, 0x74, 0x75, 0x75,
+ 0x77, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x79,
+ 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c,
+ 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80,
+ 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x84,
+ 0x84, 0x84, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86,
+ 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a,
+ 0x8a, 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90,
+ 0x90, 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92,
+ 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x96,
+ 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x98, 0x98,
+ 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab,
+ 0xab, 0xac, 0xac, 0xac, 0xac, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4, 0xb4, 0xb4,
+ 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3,
+ 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4,
+ 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
+ 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
+ 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce,
+ 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0,
+ 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1,
+ 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4,
+ 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6,
+ 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda,
+ 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
+ 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
+ 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1,
+ 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3,
+ 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
+ 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9,
+ 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
+ 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec,
+ 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xee,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef,
+ 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
+ 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x07, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14,
+ 0x16, 0x17, 0x18, 0x1b, 0x1c, 0x1e, 0x1f, 0x20,
+ 0x23, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2b, 0x2d,
+ 0x2f, 0x30, 0x31, 0x33, 0x34, 0x35, 0x37, 0x38,
+ 0x39, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3f, 0x40,
+ 0x42, 0x43, 0x44, 0x44, 0x45, 0x47, 0x48, 0x49,
+ 0x4a, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d, 0x4f, 0x50,
+ 0x52, 0x52, 0x53, 0x54, 0x55, 0x55, 0x56, 0x58,
+ 0x58, 0x59, 0x5a, 0x5b, 0x5b, 0x5c, 0x5e, 0x5e,
+ 0x5f, 0x5f, 0x60, 0x61, 0x61, 0x62, 0x63, 0x63,
+ 0x65, 0x65, 0x66, 0x67, 0x67, 0x68, 0x68, 0x69,
+ 0x6a, 0x6a, 0x6c, 0x6c, 0x6d, 0x6d, 0x6e, 0x6e,
+ 0x6f, 0x70, 0x70, 0x71, 0x71, 0x73, 0x73, 0x74,
+ 0x74, 0x75, 0x75, 0x77, 0x77, 0x78, 0x78, 0x79,
+ 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7d,
+ 0x7d, 0x7f, 0x7f, 0x80, 0x80, 0x81, 0x81, 0x81,
+ 0x82, 0x82, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86,
+ 0x88, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b,
+ 0x8b, 0x8b, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f,
+ 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x92, 0x92,
+ 0x92, 0x93, 0x93, 0x94, 0x94, 0x94, 0x96, 0x96,
+ 0x96, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99,
+ 0x99, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
+ 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0xa0,
+ 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa3,
+ 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5,
+ 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, 0xa9,
+ 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xac, 0xac, 0xac,
+ 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2,
+ 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xc4,
+ 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9,
+ 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb,
+ 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xcf,
+ 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1,
+ 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3,
+ 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8,
+ 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda,
+ 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4,
+ 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5,
+ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9,
+ 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, 0xeb,
+ 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed,
+ 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee,
+ 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd,
+ 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
+ },
+ { /* gamma 4 - from tp6800 + soi763a */
+ {0x11, 0x14, 0x15, 0x17, 0x1a, 0x1b, 0x1e, 0x1f,
+ 0x22, 0x23, 0x25, 0x27, 0x28, 0x2b, 0x2c, 0x2d,
+ 0x2f, 0x31, 0x33, 0x34, 0x35, 0x38, 0x39, 0x3a,
+ 0x3b, 0x3c, 0x3d, 0x40, 0x42, 0x43, 0x44, 0x45,
+ 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4f,
+ 0x50, 0x52, 0x53, 0x53, 0x54, 0x55, 0x56, 0x58,
+ 0x59, 0x5a, 0x5b, 0x5b, 0x5c, 0x5e, 0x5f, 0x60,
+ 0x61, 0x61, 0x62, 0x63, 0x65, 0x65, 0x66, 0x67,
+ 0x68, 0x68, 0x69, 0x6a, 0x6c, 0x6c, 0x6d, 0x6e,
+ 0x6f, 0x6f, 0x70, 0x71, 0x71, 0x73, 0x74, 0x74,
+ 0x75, 0x77, 0x77, 0x78, 0x79, 0x79, 0x7a, 0x7a,
+ 0x7b, 0x7c, 0x7c, 0x7d, 0x7f, 0x7f, 0x80, 0x80,
+ 0x81, 0x81, 0x82, 0x84, 0x84, 0x85, 0x85, 0x86,
+ 0x86, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b, 0x8b,
+ 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, 0x90, 0x90, 0x91,
+ 0x91, 0x92, 0x92, 0x93, 0x93, 0x94, 0x94, 0x96,
+ 0x96, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99,
+ 0x9a, 0x9a, 0x9b, 0x9b, 0x9c, 0x9c, 0x9d, 0x9d,
+ 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa2,
+ 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa5, 0xa5,
+ 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa9, 0xa9, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xad, 0xad, 0xad, 0xae,
+ 0xae, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb1, 0xb1,
+ 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb4, 0xb4,
+ 0xb4, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb8, 0xb8,
+ 0xb8, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xbc,
+ 0xbc, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf,
+ 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2,
+ 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc9,
+ 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xce,
+ 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0,
+ 0xd0, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
+ 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7,
+ 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb,
+ 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1,
+ 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3,
+ 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9,
+ 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec,
+ 0xec, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3,
+ 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5,
+ 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xfa, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+ {0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x11, 0x14, 0x15,
+ 0x16, 0x17, 0x1a, 0x1b, 0x1c, 0x1e, 0x1f, 0x20,
+ 0x23, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2b, 0x2c,
+ 0x2d, 0x2f, 0x30, 0x31, 0x33, 0x34, 0x34, 0x35,
+ 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3c, 0x3d,
+ 0x3f, 0x40, 0x42, 0x42, 0x43, 0x44, 0x45, 0x45,
+ 0x47, 0x48, 0x49, 0x49, 0x4a, 0x4b, 0x4b, 0x4c,
+ 0x4d, 0x4f, 0x4f, 0x50, 0x52, 0x52, 0x53, 0x54,
+ 0x54, 0x55, 0x55, 0x56, 0x58, 0x58, 0x59, 0x5a,
+ 0x5a, 0x5b, 0x5b, 0x5c, 0x5e, 0x5e, 0x5f, 0x5f,
+ 0x60, 0x60, 0x61, 0x61, 0x62, 0x63, 0x63, 0x65,
+ 0x65, 0x66, 0x66, 0x67, 0x67, 0x68, 0x68, 0x69,
+ 0x69, 0x6a, 0x6a, 0x6c, 0x6c, 0x6d, 0x6d, 0x6e,
+ 0x6e, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71, 0x73,
+ 0x73, 0x74, 0x74, 0x74, 0x75, 0x75, 0x77, 0x77,
+ 0x78, 0x78, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7b,
+ 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7f, 0x7f,
+ 0x7f, 0x80, 0x80, 0x81, 0x81, 0x81, 0x82, 0x82,
+ 0x84, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86, 0x86,
+ 0x88, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8a,
+ 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e,
+ 0x8e, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91,
+ 0x91, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x94,
+ 0x94, 0x94, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97,
+ 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a,
+ 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2,
+ 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa4,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab,
+ 0xaa, 0xab, 0xab, 0xac, 0xac, 0xac, 0xad, 0xad,
+ 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8,
+ 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7,
+ 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, 0xc9,
+ 0xca, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce,
+ 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0,
+ 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3,
+ 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4,
+ 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7,
+ 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8,
+ 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda,
+ 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
+ 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
+ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1,
+ 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2,
+ 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4,
+ 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb,
+ 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
+ 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef,
+ 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
+ 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+ {0x0d, 0x10, 0x11, 0x14, 0x15, 0x17, 0x18, 0x1b,
+ 0x1c, 0x1e, 0x20, 0x22, 0x23, 0x26, 0x27, 0x28,
+ 0x29, 0x2b, 0x2d, 0x2f, 0x30, 0x31, 0x33, 0x34,
+ 0x35, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d,
+ 0x3f, 0x40, 0x42, 0x43, 0x44, 0x45, 0x47, 0x48,
+ 0x49, 0x4a, 0x4b, 0x4b, 0x4c, 0x4d, 0x4f, 0x50,
+ 0x52, 0x52, 0x53, 0x54, 0x55, 0x56, 0x56, 0x58,
+ 0x59, 0x5a, 0x5a, 0x5b, 0x5c, 0x5e, 0x5e, 0x5f,
+ 0x60, 0x60, 0x61, 0x62, 0x62, 0x63, 0x65, 0x65,
+ 0x66, 0x67, 0x67, 0x68, 0x69, 0x69, 0x6a, 0x6c,
+ 0x6c, 0x6d, 0x6d, 0x6e, 0x6f, 0x6f, 0x70, 0x70,
+ 0x71, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, 0x77,
+ 0x78, 0x78, 0x79, 0x79, 0x7a, 0x7a, 0x7b, 0x7b,
+ 0x7c, 0x7c, 0x7d, 0x7d, 0x7f, 0x7f, 0x80, 0x80,
+ 0x81, 0x81, 0x82, 0x82, 0x84, 0x84, 0x85, 0x85,
+ 0x86, 0x86, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a,
+ 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8f,
+ 0x8f, 0x90, 0x90, 0x91, 0x91, 0x91, 0x92, 0x92,
+ 0x93, 0x93, 0x94, 0x94, 0x94, 0x96, 0x96, 0x97,
+ 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d,
+ 0x9d, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1,
+ 0xa1, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+ 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa8, 0xa8,
+ 0xa8, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xac,
+ 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1,
+ 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb4,
+ 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
+ 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xba,
+ 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd,
+ 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, 0xc3,
+ 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7,
+ 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca,
+ 0xca, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce,
+ 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
+ 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8,
+ 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf,
+ 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1,
+ 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3,
+ 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb,
+ 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed,
+ 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
+ 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+ 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4,
+ 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}
+ },
+ { /* gamma 5 */
+ {0x16, 0x18, 0x19, 0x1b, 0x1d, 0x1e, 0x20, 0x21,
+ 0x23, 0x24, 0x25, 0x27, 0x28, 0x2a, 0x2b, 0x2c,
+ 0x2d, 0x2f, 0x30, 0x31, 0x32, 0x34, 0x35, 0x36,
+ 0x37, 0x38, 0x39, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
+ 0x4f, 0x50, 0x51, 0x51, 0x52, 0x53, 0x54, 0x55,
+ 0x56, 0x56, 0x57, 0x58, 0x59, 0x59, 0x5a, 0x5b,
+ 0x5c, 0x5c, 0x5d, 0x5e, 0x5f, 0x5f, 0x60, 0x61,
+ 0x62, 0x62, 0x63, 0x64, 0x64, 0x65, 0x66, 0x66,
+ 0x67, 0x68, 0x68, 0x69, 0x6a, 0x6a, 0x6b, 0x6b,
+ 0x6c, 0x6d, 0x6d, 0x6e, 0x6f, 0x6f, 0x70, 0x70,
+ 0x71, 0x71, 0x72, 0x73, 0x73, 0x74, 0x74, 0x75,
+ 0x75, 0x76, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79,
+ 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7d, 0x7d, 0x7e,
+ 0x7e, 0x7f, 0x7f, 0x80, 0x80, 0x81, 0x81, 0x82,
+ 0x82, 0x83, 0x83, 0x84, 0x84, 0x84, 0x85, 0x85,
+ 0x86, 0x86, 0x87, 0x87, 0x88, 0x88, 0x89, 0x89,
+ 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8d,
+ 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x90, 0x90,
+ 0x91, 0x91, 0x91, 0x92, 0x92, 0x93, 0x93, 0x94,
+ 0x94, 0x94, 0x95, 0x95, 0x96, 0x96, 0x96, 0x97,
+ 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9d, 0x9d,
+ 0x9d, 0x9e, 0x9e, 0x9e, 0x9f, 0x9f, 0xa0, 0xa0,
+ 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa3,
+ 0xa3, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa6,
+ 0xa6, 0xa6, 0xa7, 0xa7, 0xa7, 0xa8, 0xa8, 0xa8,
+ 0xa9, 0xa9, 0xa9, 0xaa, 0xaa, 0xaa, 0xab, 0xab,
+ 0xab, 0xac, 0xac, 0xac, 0xad, 0xad, 0xad, 0xae,
+ 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb3,
+ 0xb3, 0xb3, 0xb4, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5,
+ 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xba, 0xba,
+ 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbc, 0xbc, 0xbc,
+ 0xbc, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1,
+ 0xc1, 0xc1, 0xc1, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7,
+ 0xc7, 0xc8, 0xc8, 0xc8, 0xc8, 0xc9, 0xc9, 0xc9,
+ 0xc9, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1,
+ 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3,
+ 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd5, 0xd5, 0xd5,
+ 0xd5, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7,
+ 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4,
+ 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
+ 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9,
+ 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xea, 0xeb,
+ 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
+ 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee,
+ 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3,
+ 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5,
+ 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd,
+ 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x0f, 0x11, 0x12, 0x14, 0x15, 0x16, 0x18, 0x19,
+ 0x1a, 0x1b, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22,
+ 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+ 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x31, 0x32,
+ 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x38, 0x39,
+ 0x3a, 0x3b, 0x3c, 0x3c, 0x3d, 0x3e, 0x3f, 0x3f,
+ 0x40, 0x41, 0x42, 0x42, 0x43, 0x44, 0x44, 0x45,
+ 0x46, 0x47, 0x47, 0x48, 0x49, 0x49, 0x4a, 0x4b,
+ 0x4b, 0x4c, 0x4c, 0x4d, 0x4e, 0x4e, 0x4f, 0x50,
+ 0x50, 0x51, 0x51, 0x52, 0x53, 0x53, 0x54, 0x54,
+ 0x55, 0x55, 0x56, 0x56, 0x57, 0x58, 0x58, 0x59,
+ 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5d,
+ 0x5d, 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61,
+ 0x61, 0x62, 0x62, 0x63, 0x63, 0x64, 0x64, 0x65,
+ 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x68, 0x68,
+ 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6c,
+ 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6f, 0x6f,
+ 0x6f, 0x70, 0x70, 0x71, 0x71, 0x71, 0x72, 0x72,
+ 0x73, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, 0x75,
+ 0x76, 0x76, 0x76, 0x77, 0x77, 0x78, 0x78, 0x78,
+ 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b,
+ 0x7b, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7e, 0x7e,
+ 0x7e, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x81,
+ 0x81, 0x81, 0x82, 0x82, 0x82, 0x83, 0x83, 0x83,
+ 0x84, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x86,
+ 0x86, 0x86, 0x87, 0x87, 0x87, 0x88, 0x88, 0x88,
+ 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8c, 0x8d, 0x8d,
+ 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f,
+ 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
+ 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x94,
+ 0x94, 0x94, 0x94, 0x95, 0x95, 0x95, 0x96, 0x96,
+ 0x96, 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98,
+ 0x98, 0x98, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9f, 0x9f, 0x9f, 0x9f, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xab, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5,
+ 0xb5, 0xb5, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbb, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4,
+ 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
+ 0xc7, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc9, 0xc9,
+ 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca,
+ 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce,
+ 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0,
+ 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1,
+ 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3,
+ 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4,
+ 0xd4, 0xd4, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5,
+ 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7,
+ 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8,
+ 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda,
+ 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde,
+ 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf,
+ 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3,
+ 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
+ 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9,
+ 0xe9, 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea,
+ 0xea, 0xea, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb,
+ 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed,
+ 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
+ 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd,
+ 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x13, 0x15, 0x16, 0x18, 0x19, 0x1b, 0x1c, 0x1e,
+ 0x1f, 0x20, 0x22, 0x23, 0x24, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31,
+ 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
+ 0x42, 0x43, 0x44, 0x44, 0x45, 0x46, 0x47, 0x48,
+ 0x49, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d, 0x4e,
+ 0x4f, 0x50, 0x50, 0x51, 0x52, 0x53, 0x53, 0x54,
+ 0x55, 0x55, 0x56, 0x57, 0x57, 0x58, 0x59, 0x59,
+ 0x5a, 0x5b, 0x5b, 0x5c, 0x5d, 0x5d, 0x5e, 0x5f,
+ 0x5f, 0x60, 0x60, 0x61, 0x62, 0x62, 0x63, 0x63,
+ 0x64, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68,
+ 0x69, 0x69, 0x6a, 0x6a, 0x6b, 0x6b, 0x6c, 0x6c,
+ 0x6d, 0x6d, 0x6e, 0x6e, 0x6f, 0x6f, 0x70, 0x70,
+ 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x74, 0x74,
+ 0x75, 0x75, 0x76, 0x76, 0x77, 0x77, 0x78, 0x78,
+ 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c,
+ 0x7c, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f,
+ 0x80, 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x83,
+ 0x83, 0x84, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86,
+ 0x86, 0x87, 0x87, 0x88, 0x88, 0x88, 0x89, 0x89,
+ 0x89, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8c, 0x8c,
+ 0x8c, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f,
+ 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x92, 0x92,
+ 0x92, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x95,
+ 0x95, 0x95, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97,
+ 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d,
+ 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9f, 0x9f,
+ 0x9f, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+ 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa8, 0xa8, 0xa8, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xaa, 0xaa, 0xaa, 0xab, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xad, 0xad,
+ 0xad, 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xba, 0xba, 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1, 0xc1, 0xc1,
+ 0xc1, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7,
+ 0xc7, 0xc7, 0xc7, 0xc7, 0xc8, 0xc8, 0xc8, 0xc8,
+ 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca,
+ 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1,
+ 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3,
+ 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4,
+ 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd6, 0xd6, 0xd6,
+ 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
+ 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0,
+ 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1,
+ 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3,
+ 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4,
+ 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6,
+ 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9,
+ 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xea,
+ 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec,
+ 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef,
+ 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2,
+ 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3,
+ 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5,
+ 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ },
+ };
+
+ reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00);
+ if (sd->bridge == BRIDGE_TP6810)
+ reg_w(gspca_dev, 0x02, 0x28);
+/* msleep(50); */
+ gamma = sd->ctrls[GAMMA].val;
+ bulk_w(gspca_dev, 0x00, gamma_tb[gamma][0], 1024);
+ bulk_w(gspca_dev, 0x01, gamma_tb[gamma][1], 1024);
+ bulk_w(gspca_dev, 0x02, gamma_tb[gamma][2], 1024);
+ if (sd->bridge == BRIDGE_TP6810) {
+ int i;
+
+ reg_w(gspca_dev, 0x02, 0x2b);
+ reg_w(gspca_dev, 0x02, 0x28);
+ for (i = 0; i < 6; i++)
+ reg_w(gspca_dev, TP6800_R55_GAMMA_R,
+ gamma_tb[gamma][0][i]);
+ reg_w(gspca_dev, 0x02, 0x2b);
+ reg_w(gspca_dev, 0x02, 0x28);
+ for (i = 0; i < 6; i++)
+ reg_w(gspca_dev, TP6800_R56_GAMMA_G,
+ gamma_tb[gamma][1][i]);
+ reg_w(gspca_dev, 0x02, 0x2b);
+ reg_w(gspca_dev, 0x02, 0x28);
+ for (i = 0; i < 6; i++)
+ reg_w(gspca_dev, TP6800_R57_GAMMA_B,
+ gamma_tb[gamma][2][i]);
+ reg_w(gspca_dev, 0x02, 0x28);
+ }
+ reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x03);
+/* msleep(50); */
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
+
+ if (sd->bridge == BRIDGE_TP6800) {
+ val = sd->ctrls[SHARPNESS].val
+ | 0x08; /* grid compensation enable */
+ if (gspca_dev->width == 640)
+ reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */
+ else
+ val |= 0x04; /* scaling down enable */
+ reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, val);
+ } else {
+ val = (sd->ctrls[SHARPNESS].val << 5) | 0x08;
+ reg_w(gspca_dev, 0x59, val);
+ }
+}
+
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (gspca_dev->ctrl_dis & (1 << AUTOGAIN))
+ return;
+ if (sd->ctrls[AUTOGAIN].val) {
+ sd->ag_cnt = AG_CNT_START;
+ gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN);
+ } else {
+ sd->ag_cnt = -1;
+ gspca_dev->ctrl_inac &= ~((1 << EXPOSURE) | (1 << GAIN));
+ }
+}
+
+/* set the resolution for sensor cx0342 */
+static void set_resolution(struct gspca_dev *gspca_dev)
+{
+ reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00);
+ if (gspca_dev->width == 320) {
+ reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x06);
+ msleep(100);
+ i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01);
+ msleep(100);
+ reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x03);
+ reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01); /* qvga */
+ reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, 0x0d);
+ i2c_w(gspca_dev, CX0342_EXPO_LINE_L, 0x37);
+ i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x01);
+ } else {
+ reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x05);
+ msleep(100);
+ i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01);
+ msleep(100);
+ reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x03);
+ reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */
+ reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, 0x09);
+ i2c_w(gspca_dev, CX0342_EXPO_LINE_L, 0xcf);
+ i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x00);
+ }
+ i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x01);
+ bulk_w(gspca_dev, 0x03, color_gain[SENSOR_CX0342],
+ ARRAY_SIZE(color_gain[0]));
+ setgamma(gspca_dev);
+ setquality(gspca_dev);
+}
+
+/* convert the frame rate to a tp68x0 value */
+static int get_fr_idx(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+
+ if (sd->bridge == BRIDGE_TP6800) {
+ for (i = 0; i < ARRAY_SIZE(rates) - 1; i++) {
+ if (sd->framerate >= rates[i])
+ break;
+ }
+ i = 6 - i; /* 1 = 5fps .. 6 = 30fps */
+
+ /* 640x480 * 30 fps does not work */
+ if (i == 6 /* if 30 fps */
+ && gspca_dev->width == 640)
+ i = 0x05; /* 15 fps */
+ } else {
+ for (i = 0; i < ARRAY_SIZE(rates_6810) - 1; i++) {
+ if (sd->framerate >= rates_6810[i])
+ break;
+ }
+ i = 7 - i; /* 3 = 5fps .. 7 = 30fps */
+
+ /* 640x480 * 30 fps does not work */
+ if (i == 7 /* if 30 fps */
+ && gspca_dev->width == 640)
+ i = 6; /* 15 fps */
+ i |= 0x80; /* clock * 1 */
+ }
+ return i;
+}
+
+static void setframerate(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 fr_idx;
+
+ fr_idx = get_fr_idx(gspca_dev);
+
+ if (sd->bridge == BRIDGE_TP6810) {
+ reg_r(gspca_dev, 0x7b);
+ reg_w(gspca_dev, 0x7b,
+ sd->sensor == SENSOR_CX0342 ? 0x10 : 0x90);
+ if (sd->ctrls[EXPOSURE].val >= 128)
+ fr_idx = 0xf0; /* lower frame rate */
+ }
+
+ reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, fr_idx);
+
+ if (sd->sensor == SENSOR_CX0342)
+ i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01);
+}
+
+static void setrgain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int rgain;
+
+ rgain = sd->ctrls[RGAIN].val;
+ i2c_w(gspca_dev, CX0342_RAW_RGAIN_H, rgain >> 8);
+ i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, rgain);
+ i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x80);
+}
+
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor == SENSOR_CX0342) {
+ sd->ctrls[BGAIN].val = sd->ctrls[BGAIN].val
+ * val / sd->ctrls[GAIN].val;
+ if (sd->ctrls[BGAIN].val > 4095)
+ sd->ctrls[BGAIN].val = 4095;
+ sd->ctrls[RGAIN].val = sd->ctrls[RGAIN].val
+ * val / sd->ctrls[GAIN].val;
+ if (sd->ctrls[RGAIN].val > 4095)
+ sd->ctrls[RGAIN].val = 4095;
+ }
+ sd->ctrls[GAIN].val = val;
+ if (gspca_dev->streaming)
+ setexposure(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static void setbgain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int bgain;
+
+ bgain = sd->ctrls[BGAIN].val;
+ i2c_w(gspca_dev, CX0342_RAW_BGAIN_H, bgain >> 8);
+ i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, bgain);
+ i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x80);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->bridge = id->driver_info;
+
+ gspca_dev->cam.cam_mode = vga_mode;
+ gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
+ gspca_dev->cam.mode_framerates = sd->bridge == BRIDGE_TP6800 ?
+ framerates : framerates_6810;
+
+ sd->framerate = 30; /* default: 30 fps */
+ gspca_dev->cam.ctrls = sd->ctrls;
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ static const struct cmd tp6800_preinit[] = {
+ {TP6800_R10_SIF_TYPE, 0x01}, /* sif */
+ {TP6800_R11_SIF_CONTROL, 0x01},
+ {TP6800_R15_GPIO_PU, 0x9f},
+ {TP6800_R16_GPIO_PD, 0x9f},
+ {TP6800_R17_GPIO_IO, 0x80},
+ {TP6800_R18_GPIO_DATA, 0x40}, /* LED off */
+ };
+ static const struct cmd tp6810_preinit[] = {
+ {TP6800_R2F_TIMING_CFG, 0x2f},
+ {TP6800_R15_GPIO_PU, 0x6f},
+ {TP6800_R16_GPIO_PD, 0x40},
+ {TP6800_R17_GPIO_IO, 0x9f},
+ {TP6800_R18_GPIO_DATA, 0xc1}, /* LED off */
+ };
+
+ if (sd->bridge == BRIDGE_TP6800)
+ reg_w_buf(gspca_dev, tp6800_preinit,
+ ARRAY_SIZE(tp6800_preinit));
+ else
+ reg_w_buf(gspca_dev, tp6810_preinit,
+ ARRAY_SIZE(tp6810_preinit));
+ msleep(15);
+ reg_r(gspca_dev, TP6800_R18_GPIO_DATA);
+ PDEBUG(D_PROBE, "gpio: %02x", gspca_dev->usb_buf[0]);
+/* values:
+ * 0x80: snapshot button
+ * 0x40: LED
+ * 0x20: (bridge / sensor) reset for tp6810 ?
+ * 0x07: sensor type ?
+ */
+
+ /* guess the sensor type */
+ if (force_sensor >= 0) {
+ sd->sensor = force_sensor;
+ } else {
+ if (sd->bridge == BRIDGE_TP6800) {
+/*fixme: not sure this is working*/
+ switch (gspca_dev->usb_buf[0] & 0x07) {
+ case 0:
+ sd->sensor = SENSOR_SOI763A;
+ break;
+ case 1:
+ sd->sensor = SENSOR_CX0342;
+ break;
+ }
+ } else {
+ int sensor;
+
+ sensor = probe_6810(gspca_dev);
+ if (sensor < 0) {
+ pr_warn("Unknown sensor %d - forced to soi763a\n",
+ -sensor);
+ sensor = SENSOR_SOI763A;
+ }
+ sd->sensor = sensor;
+ }
+ }
+ if (sd->sensor == SENSOR_SOI763A) {
+ pr_info("Sensor soi763a\n");
+ sd->ctrls[GAMMA].def = sd->bridge == BRIDGE_TP6800 ? 0 : 1;
+ sd->ctrls[GAIN].max = 15;
+ sd->ctrls[GAIN].def = 3;
+ gspca_dev->ctrl_dis = (1 << RGAIN) | (1 << BGAIN);
+ if (sd->bridge == BRIDGE_TP6810) {
+ soi763a_6810_init(gspca_dev);
+#if AUTOGAIN_DEF
+ gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN);
+#endif
+ } else {
+ gspca_dev->ctrl_dis |= (1 << AUTOGAIN);
+ }
+ } else {
+ pr_info("Sensor cx0342\n");
+ if (sd->bridge == BRIDGE_TP6810) {
+ cx0342_6810_init(gspca_dev);
+#if AUTOGAIN_DEF
+ gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN);
+#endif
+ } else {
+ gspca_dev->ctrl_dis |= (1 << AUTOGAIN);
+ }
+ }
+
+ if (sd->bridge == BRIDGE_TP6810)
+ sd->ctrls[QUALITY].def = 0; /* auto quality */
+ set_dqt(gspca_dev, 0);
+ return 0;
+}
+
+/* This function is called before choosing the alt setting */
+static int sd_isoc_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ static const struct cmd cx_sensor_init[] = {
+ {CX0342_AUTO_ADC_CALIB, 0x81},
+ {CX0342_EXPO_LINE_L, 0x37},
+ {CX0342_EXPO_LINE_H, 0x01},
+ {CX0342_RAW_GRGAIN_L, 0x00},
+ {CX0342_RAW_GBGAIN_L, 0x00},
+ {CX0342_RAW_RGAIN_L, 0x00},
+ {CX0342_RAW_BGAIN_L, 0x00},
+ {CX0342_SYS_CTRL_0, 0x81},
+ };
+ static const struct cmd cx_bridge_init[] = {
+ {0x4d, 0x00},
+ {0x4c, 0xff},
+ {0x4e, 0xff},
+ {0x4f, 0x00},
+ };
+ static const struct cmd ov_sensor_init[] = {
+ {0x10, 0x75}, /* exposure */
+ {0x76, 0x03},
+ {0x00, 0x00}, /* gain */
+ };
+ static const struct cmd ov_bridge_init[] = {
+ {0x7b, 0x90},
+ {TP6800_R3F_FRAME_RATE, 0x87},
+ };
+
+ if (sd->bridge == BRIDGE_TP6800)
+ return 0;
+ if (sd->sensor == SENSOR_CX0342) {
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x20);
+ reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x87);
+ i2c_w_buf(gspca_dev, cx_sensor_init,
+ ARRAY_SIZE(cx_sensor_init));
+ reg_w_buf(gspca_dev, cx_bridge_init,
+ ARRAY_SIZE(cx_bridge_init));
+ bulk_w(gspca_dev, 0x03, color_null, sizeof color_null);
+ reg_w(gspca_dev, 0x59, 0x40);
+ } else {
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x21);
+ i2c_w_buf(gspca_dev, ov_sensor_init,
+ ARRAY_SIZE(ov_sensor_init));
+ reg_r(gspca_dev, 0x7b);
+ reg_w_buf(gspca_dev, ov_bridge_init,
+ ARRAY_SIZE(ov_bridge_init));
+ }
+ reg_w(gspca_dev, TP6800_R78_FORMAT,
+ gspca_dev->curr_mode ? 0x00 : 0x01);
+ return gspca_dev->usb_err;
+}
+
+static void set_led(struct gspca_dev *gspca_dev, int on)
+{
+ u8 data;
+
+ reg_r(gspca_dev, TP6800_R18_GPIO_DATA);
+ data = gspca_dev->usb_buf[0];
+ if (on)
+ data &= ~0x40;
+ else
+ data |= 0x40;
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, data);
+}
+
+static void cx0342_6800_start(struct gspca_dev *gspca_dev)
+{
+ static const struct cmd reg_init[] = {
+/*fixme: is this usefull?*/
+ {TP6800_R17_GPIO_IO, 0x9f},
+ {TP6800_R16_GPIO_PD, 0x40},
+ {TP6800_R10_SIF_TYPE, 0x00}, /* i2c 8 bits */
+ {TP6800_R50, 0x00},
+ {TP6800_R51, 0x00},
+ {TP6800_R52, 0xff},
+ {TP6800_R53, 0x03},
+ {TP6800_R54_DARK_CFG, 0x07},
+ {TP6800_R5C_EDGE_THRLD, 0x40},
+ {TP6800_R7A_BLK_THRLD, 0x40},
+ {TP6800_R2F_TIMING_CFG, 0x17},
+ {TP6800_R30_SENSOR_CFG, 0x18}, /* G1B..RG0 */
+ {TP6800_R37_FRONT_DARK_ST, 0x00},
+ {TP6800_R38_FRONT_DARK_END, 0x00},
+ {TP6800_R39_REAR_DARK_ST_L, 0x00},
+ {TP6800_R3A_REAR_DARK_ST_H, 0x00},
+ {TP6800_R3B_REAR_DARK_END_L, 0x00},
+ {TP6800_R3C_REAR_DARK_END_H, 0x00},
+ {TP6800_R3D_HORIZ_DARK_LINE_L, 0x00},
+ {TP6800_R3E_HORIZ_DARK_LINE_H, 0x00},
+ {TP6800_R21_ENDP_1_CTL, 0x03},
+
+ {TP6800_R31_PIXEL_START, 0x0b},
+ {TP6800_R32_PIXEL_END_L, 0x8a},
+ {TP6800_R33_PIXEL_END_H, 0x02},
+ {TP6800_R34_LINE_START, 0x0e},
+ {TP6800_R35_LINE_END_L, 0xf4},
+ {TP6800_R36_LINE_END_H, 0x01},
+ {TP6800_R78_FORMAT, 0x00},
+ {TP6800_R12_SIF_ADDR_S, 0x20}, /* cx0342 i2c addr */
+ };
+ static const struct cmd sensor_init[] = {
+ {CX0342_OUTPUT_CTRL, 0x07},
+ {CX0342_BYPASS_MODE, 0x58},
+ {CX0342_GPXLTHD_L, 0x16},
+ {CX0342_RBPXLTHD_L, 0x16},
+ {CX0342_PLANETHD_L, 0xc0},
+ {CX0342_PLANETHD_H, 0x03},
+ {CX0342_RB_GAP_L, 0xff},
+ {CX0342_RB_GAP_H, 0x07},
+ {CX0342_G_GAP_L, 0xff},
+ {CX0342_G_GAP_H, 0x07},
+ {CX0342_RST_OVERFLOW_L, 0x5c},
+ {CX0342_RST_OVERFLOW_H, 0x01},
+ {CX0342_DATA_OVERFLOW_L, 0xfc},
+ {CX0342_DATA_OVERFLOW_H, 0x03},
+ {CX0342_DATA_UNDERFLOW_L, 0x00},
+ {CX0342_DATA_UNDERFLOW_H, 0x00},
+ {CX0342_SYS_CTRL_0, 0x40},
+ {CX0342_GLOBAL_GAIN, 0x01},
+ {CX0342_CLOCK_GEN, 0x00},
+ {CX0342_SYS_CTRL_0, 0x02},
+ {CX0342_IDLE_CTRL, 0x05},
+ {CX0342_ADCGN, 0x00},
+ {CX0342_ADC_CTL, 0x00},
+ {CX0342_LVRST_BLBIAS, 0x01},
+ {CX0342_VTHSEL, 0x0b},
+ {CX0342_RAMP_RIV, 0x0b},
+ {CX0342_LDOSEL, 0x07},
+ {CX0342_SPV_VALUE_L, 0x40},
+ {CX0342_SPV_VALUE_H, 0x02},
+ };
+
+ reg_w_buf(gspca_dev, reg_init, ARRAY_SIZE(reg_init));
+ i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init));
+ i2c_w_buf(gspca_dev, cx0342_timing_seq, ARRAY_SIZE(cx0342_timing_seq));
+ reg_w(gspca_dev, TP6800_R5C_EDGE_THRLD, 0x10);
+ reg_w(gspca_dev, TP6800_R54_DARK_CFG, 0x00);
+ i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x00);
+ i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x01);
+ setexposure(gspca_dev);
+ set_led(gspca_dev, 1);
+ set_resolution(gspca_dev);
+}
+
+static void cx0342_6810_start(struct gspca_dev *gspca_dev)
+{
+ static const struct cmd sensor_init_2[] = {
+ {CX0342_EXPO_LINE_L, 0x6f},
+ {CX0342_EXPO_LINE_H, 0x02},
+ {CX0342_RAW_GRGAIN_L, 0x00},
+ {CX0342_RAW_GBGAIN_L, 0x00},
+ {CX0342_RAW_RGAIN_L, 0x00},
+ {CX0342_RAW_BGAIN_L, 0x00},
+ {CX0342_SYS_CTRL_0, 0x81},
+ };
+ static const struct cmd bridge_init_2[] = {
+ {0x4d, 0x00},
+ {0x4c, 0xff},
+ {0x4e, 0xff},
+ {0x4f, 0x00},
+ {TP6800_R7A_BLK_THRLD, 0x00},
+ {TP6800_R79_QUALITY, 0x04},
+ {TP6800_R79_QUALITY, 0x01},
+ };
+ static const struct cmd bridge_init_3[] = {
+ {TP6800_R31_PIXEL_START, 0x08},
+ {TP6800_R32_PIXEL_END_L, 0x87},
+ {TP6800_R33_PIXEL_END_H, 0x02},
+ {TP6800_R34_LINE_START, 0x0e},
+ {TP6800_R35_LINE_END_L, 0xf4},
+ {TP6800_R36_LINE_END_H, 0x01},
+ };
+ static const struct cmd sensor_init_3[] = {
+ {CX0342_AUTO_ADC_CALIB, 0x81},
+ {CX0342_EXPO_LINE_L, 0x6f},
+ {CX0342_EXPO_LINE_H, 0x02},
+ {CX0342_RAW_GRGAIN_L, 0x00},
+ {CX0342_RAW_GBGAIN_L, 0x00},
+ {CX0342_RAW_RGAIN_L, 0x00},
+ {CX0342_RAW_BGAIN_L, 0x00},
+ {CX0342_SYS_CTRL_0, 0x81},
+ };
+ static const struct cmd bridge_init_5[] = {
+ {0x4d, 0x00},
+ {0x4c, 0xff},
+ {0x4e, 0xff},
+ {0x4f, 0x00},
+ };
+ static const struct cmd sensor_init_4[] = {
+ {CX0342_EXPO_LINE_L, 0xd3},
+ {CX0342_EXPO_LINE_H, 0x01},
+/*fixme: gains, but 00..80 only*/
+ {CX0342_RAW_GRGAIN_L, 0x40},
+ {CX0342_RAW_GBGAIN_L, 0x40},
+ {CX0342_RAW_RGAIN_L, 0x40},
+ {CX0342_RAW_BGAIN_L, 0x40},
+ {CX0342_SYS_CTRL_0, 0x81},
+ };
+ static const struct cmd sensor_init_5[] = {
+ {CX0342_IDLE_CTRL, 0x05},
+ {CX0342_ADCGN, 0x00},
+ {CX0342_ADC_CTL, 0x00},
+ {CX0342_LVRST_BLBIAS, 0x01},
+ {CX0342_VTHSEL, 0x0b},
+ {CX0342_RAMP_RIV, 0x0b},
+ {CX0342_LDOSEL, 0x07},
+ {CX0342_SPV_VALUE_L, 0x40},
+ {CX0342_SPV_VALUE_H, 0x02},
+ {CX0342_AUTO_ADC_CALIB, 0x81},
+ };
+
+ reg_w(gspca_dev, 0x22, gspca_dev->alt);
+ i2c_w_buf(gspca_dev, sensor_init_2, ARRAY_SIZE(sensor_init_2));
+ reg_w_buf(gspca_dev, bridge_init_2, ARRAY_SIZE(bridge_init_2));
+ reg_w_buf(gspca_dev, tp6810_cx_init_common,
+ ARRAY_SIZE(tp6810_cx_init_common));
+ reg_w_buf(gspca_dev, bridge_init_3, ARRAY_SIZE(bridge_init_3));
+ if (gspca_dev->curr_mode) {
+ reg_w(gspca_dev, 0x4a, 0x7f);
+ reg_w(gspca_dev, 0x07, 0x05);
+ reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */
+ } else {
+ reg_w(gspca_dev, 0x4a, 0xff);
+ reg_w(gspca_dev, 0x07, 0x85);
+ reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01); /* qvga */
+ }
+ setgamma(gspca_dev);
+ reg_w_buf(gspca_dev, tp6810_bridge_start,
+ ARRAY_SIZE(tp6810_bridge_start));
+ setsharpness(gspca_dev);
+ bulk_w(gspca_dev, 0x03, color_gain[SENSOR_CX0342],
+ ARRAY_SIZE(color_gain[0]));
+ reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x87);
+ i2c_w_buf(gspca_dev, sensor_init_3, ARRAY_SIZE(sensor_init_3));
+ reg_w_buf(gspca_dev, bridge_init_5, ARRAY_SIZE(bridge_init_5));
+ i2c_w_buf(gspca_dev, sensor_init_4, ARRAY_SIZE(sensor_init_4));
+ reg_w_buf(gspca_dev, bridge_init_5, ARRAY_SIZE(bridge_init_5));
+ i2c_w_buf(gspca_dev, sensor_init_5, ARRAY_SIZE(sensor_init_5));
+
+ set_led(gspca_dev, 1);
+/* setquality(gspca_dev); */
+}
+
+static void soi763a_6800_start(struct gspca_dev *gspca_dev)
+{
+ static const struct cmd reg_init[] = {
+ {TP6800_R79_QUALITY, 0x04},
+ {TP6800_R79_QUALITY, 0x01},
+ {TP6800_R10_SIF_TYPE, 0x00}, /* i2c 8 bits */
+
+ {TP6800_R50, 0x00},
+ {TP6800_R51, 0x00},
+ {TP6800_R52, 0xff},
+ {TP6800_R53, 0x03},
+ {TP6800_R54_DARK_CFG, 0x07},
+ {TP6800_R5C_EDGE_THRLD, 0x40},
+
+ {TP6800_R79_QUALITY, 0x03},
+ {TP6800_R7A_BLK_THRLD, 0x40},
+
+ {TP6800_R2F_TIMING_CFG, 0x46},
+ {TP6800_R30_SENSOR_CFG, 0x10}, /* BG1..G0R */
+ {TP6800_R37_FRONT_DARK_ST, 0x00},
+ {TP6800_R38_FRONT_DARK_END, 0x00},
+ {TP6800_R39_REAR_DARK_ST_L, 0x00},
+ {TP6800_R3A_REAR_DARK_ST_H, 0x00},
+ {TP6800_R3B_REAR_DARK_END_L, 0x00},
+ {TP6800_R3C_REAR_DARK_END_H, 0x00},
+ {TP6800_R3D_HORIZ_DARK_LINE_L, 0x00},
+ {TP6800_R3E_HORIZ_DARK_LINE_H, 0x00},
+ {TP6800_R21_ENDP_1_CTL, 0x03},
+
+ {TP6800_R3F_FRAME_RATE, 0x04}, /* 15 fps */
+ {TP6800_R5D_DEMOSAIC_CFG, 0x0e}, /* scale down - medium edge */
+
+ {TP6800_R31_PIXEL_START, 0x1b},
+ {TP6800_R32_PIXEL_END_L, 0x9a},
+ {TP6800_R33_PIXEL_END_H, 0x02},
+ {TP6800_R34_LINE_START, 0x0f},
+ {TP6800_R35_LINE_END_L, 0xf4},
+ {TP6800_R36_LINE_END_H, 0x01},
+ {TP6800_R78_FORMAT, 0x01}, /* qvga */
+ {TP6800_R12_SIF_ADDR_S, 0x21}, /* soi763a i2c addr */
+ {TP6800_R1A_SIF_TX_DATA2, 0x00},
+ };
+ static const struct cmd sensor_init[] = {
+ {0x12, 0x48}, /* mirror - RGB */
+ {0x13, 0xa0}, /* clock - no AGC nor AEC */
+ {0x03, 0xa4}, /* saturation */
+ {0x04, 0x30}, /* hue */
+ {0x05, 0x88}, /* contrast */
+ {0x06, 0x60}, /* brightness */
+ {0x10, 0x41}, /* AEC */
+ {0x11, 0x40}, /* clock rate */
+ {0x13, 0xa0},
+ {0x14, 0x00}, /* 640x480 */
+ {0x15, 0x14},
+ {0x1f, 0x41},
+ {0x20, 0x80},
+ {0x23, 0xee},
+ {0x24, 0x50},
+ {0x25, 0x7a},
+ {0x26, 0x00},
+ {0x27, 0xe2},
+ {0x28, 0xb0},
+ {0x2a, 0x00},
+ {0x2b, 0x00},
+ {0x2d, 0x81},
+ {0x2f, 0x9d},
+ {0x60, 0x80},
+ {0x61, 0x00},
+ {0x62, 0x88},
+ {0x63, 0x11},
+ {0x64, 0x89},
+ {0x65, 0x00},
+ {0x67, 0x94},
+ {0x68, 0x7a},
+ {0x69, 0x0f},
+ {0x6c, 0x80},
+ {0x6d, 0x80},
+ {0x6e, 0x80},
+ {0x6f, 0xff},
+ {0x71, 0x20},
+ {0x74, 0x20},
+ {0x75, 0x86},
+ {0x77, 0xb5},
+ {0x17, 0x18}, /* H href start */
+ {0x18, 0xbf}, /* H href end */
+ {0x19, 0x03}, /* V start */
+ {0x1a, 0xf8}, /* V end */
+ {0x01, 0x80}, /* blue gain */
+ {0x02, 0x80}, /* red gain */
+ };
+
+ reg_w_buf(gspca_dev, reg_init, ARRAY_SIZE(reg_init));
+
+ i2c_w(gspca_dev, 0x12, 0x80); /* sensor reset */
+ msleep(10);
+
+ i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init));
+
+ reg_w(gspca_dev, TP6800_R5C_EDGE_THRLD, 0x10);
+ reg_w(gspca_dev, TP6800_R54_DARK_CFG, 0x00);
+
+ setsharpness(gspca_dev);
+
+ bulk_w(gspca_dev, 0x03, color_gain[SENSOR_SOI763A],
+ ARRAY_SIZE(color_gain[0]));
+
+ set_led(gspca_dev, 1);
+ setexposure(gspca_dev);
+ setquality(gspca_dev);
+ setgamma(gspca_dev);
+}
+
+static void soi763a_6810_start(struct gspca_dev *gspca_dev)
+{
+ static const struct cmd bridge_init_2[] = {
+ {TP6800_R7A_BLK_THRLD, 0x00},
+ {TP6800_R79_QUALITY, 0x04},
+ {TP6800_R79_QUALITY, 0x01},
+ };
+ static const struct cmd bridge_init_3[] = {
+ {TP6800_R31_PIXEL_START, 0x20},
+ {TP6800_R32_PIXEL_END_L, 0x9f},
+ {TP6800_R33_PIXEL_END_H, 0x02},
+ {TP6800_R34_LINE_START, 0x13},
+ {TP6800_R35_LINE_END_L, 0xf8},
+ {TP6800_R36_LINE_END_H, 0x01},
+ };
+ static const struct cmd bridge_init_6[] = {
+ {0x08, 0xff},
+ {0x09, 0xff},
+ {0x0a, 0x5f},
+ {0x0b, 0x80},
+ };
+
+ reg_w(gspca_dev, 0x22, gspca_dev->alt);
+ bulk_w(gspca_dev, 0x03, color_null, sizeof color_null);
+ reg_w(gspca_dev, 0x59, 0x40);
+ setexposure(gspca_dev);
+ reg_w_buf(gspca_dev, bridge_init_2, ARRAY_SIZE(bridge_init_2));
+ reg_w_buf(gspca_dev, tp6810_ov_init_common,
+ ARRAY_SIZE(tp6810_ov_init_common));
+ reg_w_buf(gspca_dev, bridge_init_3, ARRAY_SIZE(bridge_init_3));
+ if (gspca_dev->curr_mode) {
+ reg_w(gspca_dev, 0x4a, 0x7f);
+ reg_w(gspca_dev, 0x07, 0x05);
+ reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */
+ } else {
+ reg_w(gspca_dev, 0x4a, 0xff);
+ reg_w(gspca_dev, 0x07, 0x85);
+ reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01); /* qvga */
+ }
+ setgamma(gspca_dev);
+ reg_w_buf(gspca_dev, tp6810_bridge_start,
+ ARRAY_SIZE(tp6810_bridge_start));
+
+ if (gspca_dev->curr_mode) {
+ reg_w(gspca_dev, 0x4f, 0x00);
+ reg_w(gspca_dev, 0x4e, 0x7c);
+ }
+
+ reg_w(gspca_dev, 0x00, 0x00);
+
+ setsharpness(gspca_dev);
+ bulk_w(gspca_dev, 0x03, color_gain[SENSOR_SOI763A],
+ ARRAY_SIZE(color_gain[0]));
+ set_led(gspca_dev, 1);
+ reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0xf0);
+ setexposure(gspca_dev);
+ reg_w_buf(gspca_dev, bridge_init_6, ARRAY_SIZE(bridge_init_6));
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width);
+ set_dqt(gspca_dev, sd->quality);
+ if (sd->bridge == BRIDGE_TP6800) {
+ if (sd->sensor == SENSOR_CX0342)
+ cx0342_6800_start(gspca_dev);
+ else
+ soi763a_6800_start(gspca_dev);
+ } else {
+ if (sd->sensor == SENSOR_CX0342)
+ cx0342_6810_start(gspca_dev);
+ else
+ soi763a_6810_start(gspca_dev);
+ reg_w_buf(gspca_dev, tp6810_late_start,
+ ARRAY_SIZE(tp6810_late_start));
+ reg_w(gspca_dev, 0x80, 0x03);
+ reg_w(gspca_dev, 0x82, gspca_dev->curr_mode ? 0x0a : 0x0e);
+
+ setexposure(gspca_dev);
+ setquality(gspca_dev);
+ setautogain(gspca_dev);
+ }
+
+ setframerate(gspca_dev);
+
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->bridge == BRIDGE_TP6800)
+ reg_w(gspca_dev, TP6800_R2F_TIMING_CFG, 0x03);
+ set_led(gspca_dev, 0);
+ reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data,
+ int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* the start of frame contains:
+ * ff d8
+ * ff fe
+ * width / 16
+ * height / 8
+ * quality
+ */
+ if (sd->bridge == BRIDGE_TP6810) {
+ if (*data != 0x5a) {
+/*fixme: don't discard the whole frame..*/
+ if (*data == 0xaa || *data == 0x00)
+ return;
+ if (*data > 0xc0) {
+ PDEBUG(D_FRAM, "bad frame");
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
+ }
+ }
+ data++;
+ len--;
+ if (*data == 0xff && data[1] == 0xd8) {
+/*fixme: there may be information in the 4 high bits*/
+ if ((data[6] & 0x0f) != sd->quality)
+ set_dqt(gspca_dev, data[6] & 0x0f);
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data + 7, len - 7);
+ } else if (data[len - 2] == 0xff && data[len - 1] == 0xd9) {
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ data, len);
+ } else {
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data, len);
+ }
+ return;
+ }
+
+ switch (*data) {
+ case 0x55:
+ gspca_frame_add(gspca_dev, LAST_PACKET, data, 0);
+
+ if (len < 8
+ || data[1] != 0xff || data[2] != 0xd8
+ || data[3] != 0xff || data[4] != 0xfe) {
+
+ /* Have only seen this with corrupt frames */
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
+ }
+ if (data[7] != sd->quality)
+ set_dqt(gspca_dev, data[7]);
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data + 8, len - 8);
+ break;
+ case 0xaa:
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ break;
+ case 0xcc:
+ if (data[1] != 0xff || data[2] != 0xd8)
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data + 1, len - 1);
+ else
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ break;
+ }
+}
+
+/* -- do autogain -- */
+/* gain setting is done in setexposure() for tp6810 */
+static void setgain(struct gspca_dev *gspca_dev) {}
+/* !! coarse_grained_expo_autogain is not used !! */
+#define exp_too_low_cnt bridge
+#define exp_too_high_cnt sensor
+
+#include "autogain_functions.h"
+static void sd_dq_callback(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret, alen;
+ int luma, expo;
+
+ if (sd->ag_cnt < 0)
+ return;
+ if (--sd->ag_cnt > 5)
+ return;
+ switch (sd->ag_cnt) {
+/* case 5: */
+ default:
+ reg_w(gspca_dev, 0x7d, 0x00);
+ break;
+ case 4:
+ reg_w(gspca_dev, 0x27, 0xb0);
+ break;
+ case 3:
+ reg_w(gspca_dev, 0x0c, 0x01);
+ break;
+ case 2:
+ ret = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x02),
+ gspca_dev->usb_buf,
+ 32,
+ &alen,
+ 500);
+ if (ret < 0) {
+ pr_err("bulk err %d\n", ret);
+ break;
+ }
+ /* values not used (unknown) */
+ break;
+ case 1:
+ reg_w(gspca_dev, 0x27, 0xd0);
+ break;
+ case 0:
+ ret = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x02),
+ gspca_dev->usb_buf,
+ 32,
+ &alen,
+ 500);
+ if (ret < 0) {
+ pr_err("bulk err %d\n", ret);
+ break;
+ }
+ luma = ((gspca_dev->usb_buf[8] << 8) + gspca_dev->usb_buf[7] +
+ (gspca_dev->usb_buf[11] << 8) + gspca_dev->usb_buf[10] +
+ (gspca_dev->usb_buf[14] << 8) + gspca_dev->usb_buf[13] +
+ (gspca_dev->usb_buf[17] << 8) + gspca_dev->usb_buf[16] +
+ (gspca_dev->usb_buf[20] << 8) + gspca_dev->usb_buf[19] +
+ (gspca_dev->usb_buf[23] << 8) + gspca_dev->usb_buf[22] +
+ (gspca_dev->usb_buf[26] << 8) + gspca_dev->usb_buf[25] +
+ (gspca_dev->usb_buf[29] << 8) + gspca_dev->usb_buf[28])
+ / 8;
+ if (gspca_dev->width == 640)
+ luma /= 4;
+ reg_w(gspca_dev, 0x7d, 0x00);
+
+ expo = sd->ctrls[EXPOSURE].val;
+ ret = auto_gain_n_exposure(gspca_dev, luma,
+ 60, /* desired luma */
+ 6, /* dead zone */
+ 2, /* gain knee */
+ 70); /* expo knee */
+ sd->ag_cnt = AG_CNT_START;
+ if (sd->bridge == BRIDGE_TP6810) {
+ if ((expo >= 128 && sd->ctrls[EXPOSURE].val < 128)
+ || (expo < 128 && sd->ctrls[EXPOSURE].val >= 128))
+ setframerate(gspca_dev);
+ }
+ break;
+ }
+}
+
+/* get stream parameters (framerate) */
+static void sd_get_streamparm(struct gspca_dev *gspca_dev,
+ struct v4l2_streamparm *parm)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct v4l2_captureparm *cp = &parm->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+ int fr, i;
+
+ cp->capability |= V4L2_CAP_TIMEPERFRAME;
+ tpf->numerator = 1;
+ i = get_fr_idx(gspca_dev);
+ if (i & 0x80) {
+ if (sd->bridge == BRIDGE_TP6800)
+ fr = rates[6 - (i & 0x07)];
+ else
+ fr = rates_6810[7 - (i & 0x07)];
+ } else {
+ fr = rates[6 - i];
+ }
+ tpf->denominator = fr;
+}
+
+/* set stream parameters (framerate) */
+static void sd_set_streamparm(struct gspca_dev *gspca_dev,
+ struct v4l2_streamparm *parm)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct v4l2_captureparm *cp = &parm->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+ int fr, i;
+
+ sd->framerate = tpf->denominator / tpf->numerator;
+ if (gspca_dev->streaming)
+ setframerate(gspca_dev);
+
+ /* Return the actual framerate */
+ i = get_fr_idx(gspca_dev);
+ if (i & 0x80)
+ fr = rates_6810[7 - (i & 0x07)];
+ else
+ fr = rates[6 - i];
+ tpf->numerator = 1;
+ tpf->denominator = fr;
+}
+
+static int sd_set_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor == SENSOR_SOI763A)
+ jpeg_set_qual(sd->jpeg_hdr, jcomp->quality);
+/* else
+ fixme: TODO
+*/
+ return gspca_dev->usb_err;
+}
+
+static int sd_get_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ memset(jcomp, 0, sizeof *jcomp);
+ jcomp->quality = jpeg_q[sd->quality];
+ jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
+ | V4L2_JPEG_MARKER_DQT;
+ return 0;
+}
+
+static struct ctrl sd_ctrls[NCTRLS] = {
+[EXPOSURE] = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 0x01,
+ .maximum = 0xdc,
+ .step = 1,
+ .default_value = 0x4e,
+ },
+ .set_control = setexposure
+ },
+[QUALITY] = {
+ {
+ .id = V4L2_CID_PRIVATE_BASE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Compression quality",
+ .minimum = 0,
+ .maximum = 15,
+ .step = 1,
+ .default_value = 13,
+ },
+ .set_control = setquality
+ },
+[RGAIN] = {
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red balance",
+ .minimum = 0,
+ .maximum = 4095,
+ .step = 1,
+ .default_value = 256,
+ },
+ .set_control = setrgain
+ },
+[GAIN] = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 4095,
+ .step = 1,
+ .default_value = 256,
+ },
+ .set = sd_setgain
+ },
+[BGAIN] = {
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue balance",
+ .minimum = 0,
+ .maximum = 4095,
+ .step = 1,
+ .default_value = 256,
+ },
+ .set_control = setbgain
+ },
+[SHARPNESS] = {
+ {
+ .id = V4L2_CID_SHARPNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Sharpness",
+ .minimum = 0,
+ .maximum = 3,
+ .step = 1,
+ .default_value = 2,
+ },
+ .set_control = setsharpness
+ },
+[GAMMA] = {
+ {
+ .id = V4L2_CID_GAMMA,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gamma",
+ .minimum = 0,
+ .maximum = NGAMMA - 1,
+ .step = 1,
+ .default_value = 1,
+ },
+ .set_control = setgamma
+ },
+[AUTOGAIN] = {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto Gain",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = AUTOGAIN_DEF
+ },
+ .set_control = setautogain
+ },
+};
+
+static const struct sd_desc sd_desc = {
+ .name = KBUILD_MODNAME,
+ .ctrls = sd_ctrls,
+ .nctrls = NCTRLS,
+ .config = sd_config,
+ .init = sd_init,
+ .isoc_init = sd_isoc_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+ .dq_callback = sd_dq_callback,
+ .get_streamparm = sd_get_streamparm,
+ .set_streamparm = sd_set_streamparm,
+ .get_jcomp = sd_get_jcomp,
+ .set_jcomp = sd_set_jcomp,
+};
+
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x06a2, 0x0003), .driver_info = BRIDGE_TP6800},
+ {USB_DEVICE(0x06a2, 0x6810), .driver_info = BRIDGE_TP6810},
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+static int sd_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(interface, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+ .reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
+
+module_param(force_sensor, int, 0644);
+MODULE_PARM_DESC(force_sensor,
+ "Force sensor. 0: cx0342, 1: soi763a");
diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/video/gspca/tv8532.c
new file mode 100644
index 00000000..c8922c5f
--- /dev/null
+++ b/drivers/media/video/gspca/tv8532.c
@@ -0,0 +1,421 @@
+/*
+ * Quickcam cameras initialization data
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#define MODULE_NAME "tv8532"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("TV8532 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ __u16 exposure;
+ __u16 gain;
+
+ __u8 packet;
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
+
+static const struct ctrl sd_ctrls[] = {
+ {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 1,
+ .maximum = 0x18f,
+ .step = 1,
+#define EXPOSURE_DEF 0x18f
+ .default_value = EXPOSURE_DEF,
+ },
+ .set = sd_setexposure,
+ .get = sd_getexposure,
+ },
+ {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 0x7ff,
+ .step = 1,
+#define GAIN_DEF 0x100
+ .default_value = GAIN_DEF,
+ },
+ .set = sd_setgain,
+ .get = sd_getgain,
+ },
+};
+
+static const struct v4l2_pix_format sif_mode[] = {
+ {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {352, 288, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+
+/* TV-8532A (ICM532A) registers (LE) */
+#define R00_PART_CONTROL 0x00
+#define LATENT_CHANGE 0x80
+#define EXPO_CHANGE 0x04
+#define R01_TIMING_CONTROL_LOW 0x01
+#define CMD_EEprom_Open 0x30
+#define CMD_EEprom_Close 0x29
+#define R03_TABLE_ADDR 0x03
+#define R04_WTRAM_DATA_L 0x04
+#define R05_WTRAM_DATA_M 0x05
+#define R06_WTRAM_DATA_H 0x06
+#define R07_TABLE_LEN 0x07
+#define R08_RAM_WRITE_ACTION 0x08
+#define R0C_AD_WIDTHL 0x0c
+#define R0D_AD_WIDTHH 0x0d
+#define R0E_AD_HEIGHTL 0x0e
+#define R0F_AD_HEIGHTH 0x0f
+#define R10_AD_COL_BEGINL 0x10
+#define R11_AD_COL_BEGINH 0x11
+#define MIRROR 0x04 /* [10] */
+#define R14_AD_ROW_BEGINL 0x14
+#define R15_AD_ROWBEGINH 0x15
+#define R1C_AD_EXPOSE_TIMEL 0x1c
+#define R20_GAIN_G1L 0x20
+#define R21_GAIN_G1H 0x21
+#define R22_GAIN_RL 0x22
+#define R23_GAIN_RH 0x23
+#define R24_GAIN_BL 0x24
+#define R25_GAIN_BH 0x25
+#define R26_GAIN_G2L 0x26
+#define R27_GAIN_G2H 0x27
+#define R28_QUANT 0x28
+#define R29_LINE 0x29
+#define R2C_POLARITY 0x2c
+#define R2D_POINT 0x2d
+#define R2E_POINTH 0x2e
+#define R2F_POINTB 0x2f
+#define R30_POINTBH 0x30
+#define R31_UPD 0x31
+#define R2A_HIGH_BUDGET 0x2a
+#define R2B_LOW_BUDGET 0x2b
+#define R34_VID 0x34
+#define R35_VIDH 0x35
+#define R36_PID 0x36
+#define R37_PIDH 0x37
+#define R39_Test1 0x39 /* GPIO */
+#define R3B_Test3 0x3b /* GPIO */
+#define R83_AD_IDH 0x83
+#define R91_AD_SLOPEREG 0x91
+#define R94_AD_BITCONTROL 0x94
+
+static const u8 eeprom_data[][3] = {
+/* dataH dataM dataL */
+ {0x01, 0x00, 0x01},
+ {0x01, 0x80, 0x11},
+ {0x05, 0x00, 0x14},
+ {0x05, 0x00, 0x1c},
+ {0x0d, 0x00, 0x1e},
+ {0x05, 0x00, 0x1f},
+ {0x05, 0x05, 0x19},
+ {0x05, 0x01, 0x1b},
+ {0x05, 0x09, 0x1e},
+ {0x0d, 0x89, 0x2e},
+ {0x05, 0x89, 0x2f},
+ {0x05, 0x0d, 0xd9},
+ {0x05, 0x09, 0xf1},
+};
+
+
+/* write 1 byte */
+static void reg_w1(struct gspca_dev *gspca_dev,
+ __u16 index, __u8 value)
+{
+ gspca_dev->usb_buf[0] = value;
+ usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0x02,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, /* value */
+ index, gspca_dev->usb_buf, 1, 500);
+}
+
+/* write 2 bytes */
+static void reg_w2(struct gspca_dev *gspca_dev,
+ u16 index, u16 value)
+{
+ gspca_dev->usb_buf[0] = value;
+ gspca_dev->usb_buf[1] = value >> 8;
+ usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0x02,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, /* value */
+ index, gspca_dev->usb_buf, 2, 500);
+}
+
+static void tv_8532WriteEEprom(struct gspca_dev *gspca_dev)
+{
+ int i;
+
+ reg_w1(gspca_dev, R01_TIMING_CONTROL_LOW, CMD_EEprom_Open);
+ for (i = 0; i < ARRAY_SIZE(eeprom_data); i++) {
+ reg_w1(gspca_dev, R03_TABLE_ADDR, i);
+ reg_w1(gspca_dev, R04_WTRAM_DATA_L, eeprom_data[i][2]);
+ reg_w1(gspca_dev, R05_WTRAM_DATA_M, eeprom_data[i][1]);
+ reg_w1(gspca_dev, R06_WTRAM_DATA_H, eeprom_data[i][0]);
+ reg_w1(gspca_dev, R08_RAM_WRITE_ACTION, 0);
+ }
+ reg_w1(gspca_dev, R07_TABLE_LEN, i);
+ reg_w1(gspca_dev, R01_TIMING_CONTROL_LOW, CMD_EEprom_Close);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+ cam->cam_mode = sif_mode;
+ cam->nmodes = ARRAY_SIZE(sif_mode);
+
+ sd->exposure = EXPOSURE_DEF;
+ sd->gain = GAIN_DEF;
+ return 0;
+}
+
+static void tv_8532_setReg(struct gspca_dev *gspca_dev)
+{
+ reg_w1(gspca_dev, R3B_Test3, 0x0a); /* Test0Sel = 10 */
+ /******************************************************/
+ reg_w1(gspca_dev, R0E_AD_HEIGHTL, 0x90);
+ reg_w1(gspca_dev, R0F_AD_HEIGHTH, 0x01);
+ reg_w2(gspca_dev, R1C_AD_EXPOSE_TIMEL, 0x018f);
+ reg_w1(gspca_dev, R10_AD_COL_BEGINL, 0x44);
+ /* begin active line */
+ reg_w1(gspca_dev, R11_AD_COL_BEGINH, 0x00);
+ /* mirror and digital gain */
+ reg_w1(gspca_dev, R14_AD_ROW_BEGINL, 0x0a);
+
+ reg_w1(gspca_dev, R94_AD_BITCONTROL, 0x02);
+ reg_w1(gspca_dev, R91_AD_SLOPEREG, 0x00);
+ reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE);
+ /* = 0x84 */
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ tv_8532WriteEEprom(gspca_dev);
+
+ return 0;
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w2(gspca_dev, R1C_AD_EXPOSE_TIMEL, sd->exposure);
+ reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE);
+ /* 0x84 */
+}
+
+static void setgain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w2(gspca_dev, R20_GAIN_G1L, sd->gain);
+ reg_w2(gspca_dev, R22_GAIN_RL, sd->gain);
+ reg_w2(gspca_dev, R24_GAIN_BL, sd->gain);
+ reg_w2(gspca_dev, R26_GAIN_G2L, sd->gain);
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w1(gspca_dev, R0C_AD_WIDTHL, 0xe8); /* 0x20; 0x0c */
+ reg_w1(gspca_dev, R0D_AD_WIDTHH, 0x03);
+
+ /************************************************/
+ reg_w1(gspca_dev, R28_QUANT, 0x90);
+ /* 0x72 compressed mode 0x28 */
+ if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
+ /* 176x144 */
+ reg_w1(gspca_dev, R29_LINE, 0x41);
+ /* CIF - 2 lines/packet */
+ } else {
+ /* 352x288 */
+ reg_w1(gspca_dev, R29_LINE, 0x81);
+ /* CIF - 2 lines/packet */
+ }
+ /************************************************/
+ reg_w1(gspca_dev, R2C_POLARITY, 0x10); /* slow clock */
+ reg_w1(gspca_dev, R2D_POINT, 0x14);
+ reg_w1(gspca_dev, R2E_POINTH, 0x01);
+ reg_w1(gspca_dev, R2F_POINTB, 0x12);
+ reg_w1(gspca_dev, R30_POINTBH, 0x01);
+
+ tv_8532_setReg(gspca_dev);
+
+ setexposure(gspca_dev);
+ setgain(gspca_dev);
+
+ /************************************************/
+ reg_w1(gspca_dev, R31_UPD, 0x01); /* update registers */
+ msleep(200);
+ reg_w1(gspca_dev, R31_UPD, 0x00); /* end update */
+
+ gspca_dev->empty_packet = 0; /* check the empty packets */
+ sd->packet = 0; /* ignore the first packets */
+
+ return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ reg_w1(gspca_dev, R3B_Test3, 0x0b); /* Test0Sel = 11 = GPIO */
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int packet_type0, packet_type1;
+
+ packet_type0 = packet_type1 = INTER_PACKET;
+ if (gspca_dev->empty_packet) {
+ gspca_dev->empty_packet = 0;
+ sd->packet = gspca_dev->height / 2;
+ packet_type0 = FIRST_PACKET;
+ } else if (sd->packet == 0)
+ return; /* 2 more lines in 352x288 ! */
+ sd->packet--;
+ if (sd->packet == 0)
+ packet_type1 = LAST_PACKET;
+
+ /* each packet contains:
+ * - header 2 bytes
+ * - RGRG line
+ * - 4 bytes
+ * - GBGB line
+ * - 4 bytes
+ */
+ gspca_frame_add(gspca_dev, packet_type0,
+ data + 2, gspca_dev->width);
+ gspca_frame_add(gspca_dev, packet_type1,
+ data + gspca_dev->width + 5, gspca_dev->width);
+}
+
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->exposure = val;
+ if (gspca_dev->streaming)
+ setexposure(gspca_dev);
+ return 0;
+}
+
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->exposure;
+ return 0;
+}
+
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->gain = val;
+ if (gspca_dev->streaming)
+ setgain(gspca_dev);
+ return 0;
+}
+
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->gain;
+ return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x046d, 0x0920)},
+ {USB_DEVICE(0x046d, 0x0921)},
+ {USB_DEVICE(0x0545, 0x808b)},
+ {USB_DEVICE(0x0545, 0x8333)},
+ {USB_DEVICE(0x0923, 0x010f)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c
new file mode 100644
index 00000000..208f6b2d
--- /dev/null
+++ b/drivers/media/video/gspca/vc032x.c
@@ -0,0 +1,4233 @@
+/*
+ * Z-star vc0321 library
+ *
+ * Copyright (C) 2009-2010 Jean-François Moine <http://moinejf.free.fr>
+ * Copyright (C) 2006 Koninski Artur takeshi87@o2.pl
+ * Copyright (C) 2006 Michel Xhaard
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "vc032x"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>");
+MODULE_DESCRIPTION("GSPCA/VC032X USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ u8 brightness;
+ u8 contrast;
+ u8 colors;
+ u8 hflip;
+ u8 vflip;
+ u8 lightfreq;
+ s8 sharpness;
+ u16 exposure;
+ u8 gain;
+ u8 autogain;
+ u8 backlight;
+
+ u8 image_offset;
+
+ u8 bridge;
+ u8 sensor;
+ u8 flags;
+#define FL_SAMSUNG 0x01 /* SamsungQ1 (2 sensors) */
+#define FL_HFLIP 0x02 /* mirrored by default */
+#define FL_VFLIP 0x04 /* vertical flipped by default */
+};
+enum bridges {
+ BRIDGE_VC0321,
+ BRIDGE_VC0323,
+};
+enum sensors {
+ SENSOR_HV7131R,
+ SENSOR_MI0360,
+ SENSOR_MI1310_SOC,
+ SENSOR_MI1320,
+ SENSOR_MI1320_SOC,
+ SENSOR_OV7660,
+ SENSOR_OV7670,
+ SENSOR_PO1200,
+ SENSOR_PO3130NC,
+ SENSOR_POxxxx,
+ NSENSORS
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setbacklight(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbacklight(struct gspca_dev *gspca_dev, __s32 *val);
+
+static const struct ctrl sd_ctrls[] = {
+#define BRIGHTNESS_IDX 0
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+#define BRIGHTNESS_DEF 128
+ .default_value = BRIGHTNESS_DEF,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+#define CONTRAST_IDX 1
+ {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+#define CONTRAST_DEF 127
+ .default_value = CONTRAST_DEF,
+ },
+ .set = sd_setcontrast,
+ .get = sd_getcontrast,
+ },
+#define COLORS_IDX 2
+ {
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 1,
+ .maximum = 127,
+ .step = 1,
+#define COLOR_DEF 63
+ .default_value = COLOR_DEF,
+ },
+ .set = sd_setcolors,
+ .get = sd_getcolors,
+ },
+/* next 2 controls work with some sensors only */
+#define HFLIP_IDX 3
+ {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mirror",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define HFLIP_DEF 0
+ .default_value = HFLIP_DEF,
+ },
+ .set = sd_sethflip,
+ .get = sd_gethflip,
+ },
+#define VFLIP_IDX 4
+ {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Vflip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define VFLIP_DEF 0
+ .default_value = VFLIP_DEF,
+ },
+ .set = sd_setvflip,
+ .get = sd_getvflip,
+ },
+#define LIGHTFREQ_IDX 5
+ {
+ {
+ .id = V4L2_CID_POWER_LINE_FREQUENCY,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Light frequency filter",
+ .minimum = 0,
+ .maximum = 2, /* 0: No, 1: 50Hz, 2:60Hz */
+ .step = 1,
+#define FREQ_DEF 1
+ .default_value = FREQ_DEF,
+ },
+ .set = sd_setfreq,
+ .get = sd_getfreq,
+ },
+#define SHARPNESS_IDX 6
+ {
+ {
+ .id = V4L2_CID_SHARPNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Sharpness",
+ .minimum = -1,
+ .maximum = 2,
+ .step = 1,
+#define SHARPNESS_DEF -1
+ .default_value = SHARPNESS_DEF,
+ },
+ .set = sd_setsharpness,
+ .get = sd_getsharpness,
+ },
+#define GAIN_IDX 7
+ {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 78,
+ .step = 1,
+#define GAIN_DEF 0
+ .default_value = GAIN_DEF,
+ },
+ .set = sd_setgain,
+ .get = sd_getgain,
+ },
+#define EXPOSURE_IDX 8
+ {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+#define EXPOSURE_DEF 450
+ .minimum = 0,
+ .maximum = 4095,
+ .step = 1,
+ .default_value = EXPOSURE_DEF,
+ },
+ .set = sd_setexposure,
+ .get = sd_getexposure,
+ },
+#define AUTOGAIN_IDX 9
+ {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Automatic Gain and Exposure",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define AUTOGAIN_DEF 1
+ .default_value = AUTOGAIN_DEF,
+ },
+ .set = sd_setautogain,
+ .get = sd_getautogain,
+ },
+#define BACKLIGHT_IDX 10
+ {
+ {
+ .id = V4L2_CID_BACKLIGHT_COMPENSATION,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Backlight Compensation",
+ .minimum = 0,
+ .maximum = 15,
+ .step = 1,
+#define BACKLIGHT_DEF 15
+ .default_value = BACKLIGHT_DEF,
+ },
+ .set = sd_setbacklight,
+ .get = sd_getbacklight,
+ },
+};
+
+/* table of the disabled controls */
+static u32 ctrl_dis[NSENSORS] = {
+ [SENSOR_HV7131R] =
+ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
+ | (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX)
+ | (1 << SHARPNESS_IDX)
+ | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+ | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
+ [SENSOR_MI0360] =
+ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
+ | (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX)
+ | (1 << SHARPNESS_IDX)
+ | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+ | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
+ [SENSOR_MI1310_SOC] =
+ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
+ | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX)
+ | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+ | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
+ [SENSOR_MI1320] =
+ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
+ | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX)
+ | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+ | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
+ [SENSOR_MI1320_SOC] =
+ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
+ | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX)
+ | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+ | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
+ [SENSOR_OV7660] =
+ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
+ | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX)
+ | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+ | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
+ [SENSOR_OV7670] =
+ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
+ | (1 << SHARPNESS_IDX)
+ | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+ | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
+ [SENSOR_PO1200] =
+ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
+ | (1 << LIGHTFREQ_IDX)
+ | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+ | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
+ [SENSOR_PO3130NC] =
+ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
+ | (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX)
+ | (1 << SHARPNESS_IDX)
+ | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+ | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
+ [SENSOR_POxxxx] =
+ (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX),
+};
+
+static const struct v4l2_pix_format vc0321_mode[] = {
+ {320, 240, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+static const struct v4l2_pix_format vc0323_mode[] = {
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+ {1280, 960, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, /* mi1310_soc only */
+ .bytesperline = 1280,
+ .sizeimage = 1280 * 960 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 2},
+};
+static const struct v4l2_pix_format bi_mode[] = {
+ {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 2},
+ {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {1280, 1024, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+ .bytesperline = 1280,
+ .sizeimage = 1280 * 1024 * 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+static const struct v4l2_pix_format svga_mode[] = {
+ {800, 600, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 800,
+ .sizeimage = 800 * 600 * 1 / 4 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
+/* OV7660/7670 registers */
+#define OV7660_REG_MVFP 0x1e
+#define OV7660_MVFP_MIRROR 0x20
+#define OV7660_MVFP_VFLIP 0x10
+
+static const u8 mi0360_matrix[9] = {
+ 0x50, 0xf8, 0xf8, 0xf5, 0x50, 0xfb, 0xff, 0xf1, 0x50
+};
+
+static const u8 mi0360_initVGA_JPG[][4] = {
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0xb3, 0x00, 0x24, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x03, 0xcc},
+ {0xb3, 0x03, 0x0a, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x01, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc},
+ {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x35, 0xdd, 0xcc}, /* i2c add: 5d */
+ {0xb3, 0x34, 0x02, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc},
+ {0xbc, 0x00, 0x71, 0xcc},
+ {0xb8, 0x00, 0x13, 0xcc},
+ {0xb8, 0x27, 0x20, 0xcc},
+ {0xb8, 0x2c, 0x50, 0xcc},
+ {0xb8, 0x2d, 0xf8, 0xcc},
+ {0xb8, 0x2e, 0xf8, 0xcc},
+ {0xb8, 0x2f, 0xf8, 0xcc},
+ {0xb8, 0x30, 0x50, 0xcc},
+ {0xb8, 0x31, 0xf8, 0xcc},
+ {0xb8, 0x32, 0xf8, 0xcc},
+ {0xb8, 0x33, 0xf8, 0xcc},
+ {0xb8, 0x34, 0x50, 0xcc},
+ {0xb8, 0x35, 0x00, 0xcc},
+ {0xb8, 0x36, 0x00, 0xcc},
+ {0xb8, 0x37, 0x00, 0xcc},
+ {0xb8, 0x01, 0x79, 0xcc},
+ {0xb8, 0x08, 0xe0, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0xb8, 0x01, 0x79, 0xcc},
+ {0xb8, 0x14, 0x18, 0xcc},
+ {0xb8, 0xb2, 0x0a, 0xcc},
+ {0xb8, 0xb4, 0x0a, 0xcc},
+ {0xb8, 0xb5, 0x0a, 0xcc},
+ {0xb8, 0xfe, 0x00, 0xcc},
+ {0xb8, 0xff, 0x28, 0xcc},
+ {0xb9, 0x00, 0x28, 0xcc},
+ {0xb9, 0x01, 0x28, 0xcc},
+ {0xb9, 0x02, 0x28, 0xcc},
+ {0xb9, 0x03, 0x00, 0xcc},
+ {0xb9, 0x04, 0x00, 0xcc},
+ {0xb9, 0x05, 0x3c, 0xcc},
+ {0xb9, 0x06, 0x3c, 0xcc},
+ {0xb9, 0x07, 0x3c, 0xcc},
+ {0xb9, 0x08, 0x3c, 0xcc},
+ {0xb8, 0x8e, 0x00, 0xcc},
+ {0xb8, 0x8f, 0xff, 0xcc},
+ {0xb8, 0x81, 0x09, 0xcc},
+ {0x31, 0x00, 0x00, 0xbb},
+ {0x09, 0x01, 0xc7, 0xbb},
+ {0x34, 0x01, 0x00, 0xbb},
+ {0x2b, 0x00, 0x28, 0xbb},
+ {0x2c, 0x00, 0x30, 0xbb},
+ {0x2d, 0x00, 0x30, 0xbb},
+ {0x2e, 0x00, 0x28, 0xbb},
+ {0x62, 0x04, 0x11, 0xbb},
+ {0x03, 0x01, 0xe0, 0xbb},
+ {0x2c, 0x00, 0x2c, 0xbb},
+ {0x20, 0xd0, 0x00, 0xbb},
+ {0x01, 0x00, 0x08, 0xbb},
+ {0x06, 0x00, 0x10, 0xbb},
+ {0x05, 0x00, 0x20, 0xbb},
+ {0x20, 0x00, 0x00, 0xbb},
+ {0xb6, 0x00, 0x00, 0xcc},
+ {0xb6, 0x03, 0x02, 0xcc},
+ {0xb6, 0x02, 0x80, 0xcc},
+ {0xb6, 0x05, 0x01, 0xcc},
+ {0xb6, 0x04, 0xe0, 0xcc},
+ {0xb6, 0x12, 0x78, 0xcc},
+ {0xb6, 0x18, 0x02, 0xcc},
+ {0xb6, 0x17, 0x58, 0xcc},
+ {0xb6, 0x16, 0x00, 0xcc},
+ {0xb6, 0x22, 0x12, 0xcc},
+ {0xb6, 0x23, 0x0b, 0xcc},
+ {0xb3, 0x02, 0x02, 0xcc},
+ {0xbf, 0xc0, 0x39, 0xcc},
+ {0xbf, 0xc1, 0x04, 0xcc},
+ {0xbf, 0xcc, 0x10, 0xcc},
+ {0xb9, 0x12, 0x00, 0xcc},
+ {0xb9, 0x13, 0x0a, 0xcc},
+ {0xb9, 0x14, 0x0a, 0xcc},
+ {0xb9, 0x15, 0x0a, 0xcc},
+ {0xb9, 0x16, 0x0a, 0xcc},
+ {0xb9, 0x18, 0x00, 0xcc},
+ {0xb9, 0x19, 0x0f, 0xcc},
+ {0xb9, 0x1a, 0x0f, 0xcc},
+ {0xb9, 0x1b, 0x0f, 0xcc},
+ {0xb9, 0x1c, 0x0f, 0xcc},
+ {0xb8, 0x8e, 0x00, 0xcc},
+ {0xb8, 0x8f, 0xff, 0xcc},
+ {0xb6, 0x12, 0xf8, 0xcc},
+ {0xb8, 0x0c, 0x20, 0xcc},
+ {0xb8, 0x0d, 0x70, 0xcc},
+ {0xb6, 0x13, 0x13, 0xcc},
+ {0x35, 0x00, 0x60, 0xbb},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {}
+};
+static const u8 mi0360_initQVGA_JPG[][4] = {
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0xb3, 0x00, 0x24, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x03, 0xcc},
+ {0xb3, 0x03, 0x0a, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x01, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc},
+ {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x35, 0xdd, 0xcc},
+ {0xb3, 0x34, 0x02, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc},
+ {0xbc, 0x00, 0xd1, 0xcc},
+ {0xb8, 0x00, 0x13, 0xcc},
+ {0xb8, 0x27, 0x20, 0xcc},
+ {0xb8, 0x2c, 0x50, 0xcc},
+ {0xb8, 0x2d, 0xf8, 0xcc},
+ {0xb8, 0x2e, 0xf8, 0xcc},
+ {0xb8, 0x2f, 0xf8, 0xcc},
+ {0xb8, 0x30, 0x50, 0xcc},
+ {0xb8, 0x31, 0xf8, 0xcc},
+ {0xb8, 0x32, 0xf8, 0xcc},
+ {0xb8, 0x33, 0xf8, 0xcc},
+ {0xb8, 0x34, 0x50, 0xcc},
+ {0xb8, 0x35, 0x00, 0xcc},
+ {0xb8, 0x36, 0x00, 0xcc},
+ {0xb8, 0x37, 0x00, 0xcc},
+ {0xb8, 0x01, 0x79, 0xcc},
+ {0xb8, 0x08, 0xe0, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0xb8, 0x01, 0x79, 0xcc},
+ {0xb8, 0x14, 0x18, 0xcc},
+ {0xb8, 0xb2, 0x0a, 0xcc},
+ {0xb8, 0xb4, 0x0a, 0xcc},
+ {0xb8, 0xb5, 0x0a, 0xcc},
+ {0xb8, 0xfe, 0x00, 0xcc},
+ {0xb8, 0xff, 0x28, 0xcc},
+ {0xb9, 0x00, 0x28, 0xcc},
+ {0xb9, 0x01, 0x28, 0xcc},
+ {0xb9, 0x02, 0x28, 0xcc},
+ {0xb9, 0x03, 0x00, 0xcc},
+ {0xb9, 0x04, 0x00, 0xcc},
+ {0xb9, 0x05, 0x3c, 0xcc},
+ {0xb9, 0x06, 0x3c, 0xcc},
+ {0xb9, 0x07, 0x3c, 0xcc},
+ {0xb9, 0x08, 0x3c, 0xcc},
+ {0xb8, 0x8e, 0x00, 0xcc},
+ {0xb8, 0x8f, 0xff, 0xcc},
+ {0xb8, 0x81, 0x09, 0xcc},
+ {0x31, 0x00, 0x00, 0xbb},
+ {0x09, 0x01, 0xc7, 0xbb},
+ {0x34, 0x01, 0x00, 0xbb},
+ {0x2b, 0x00, 0x28, 0xbb},
+ {0x2c, 0x00, 0x30, 0xbb},
+ {0x2d, 0x00, 0x30, 0xbb},
+ {0x2e, 0x00, 0x28, 0xbb},
+ {0x62, 0x04, 0x11, 0xbb},
+ {0x03, 0x01, 0xe0, 0xbb},
+ {0x2c, 0x00, 0x2c, 0xbb},
+ {0x20, 0xd0, 0x00, 0xbb},
+ {0x01, 0x00, 0x08, 0xbb},
+ {0x06, 0x00, 0x10, 0xbb},
+ {0x05, 0x00, 0x20, 0xbb},
+ {0x20, 0x00, 0x00, 0xbb},
+ {0xb6, 0x00, 0x00, 0xcc},
+ {0xb6, 0x03, 0x01, 0xcc},
+ {0xb6, 0x02, 0x40, 0xcc},
+ {0xb6, 0x05, 0x00, 0xcc},
+ {0xb6, 0x04, 0xf0, 0xcc},
+ {0xb6, 0x12, 0x78, 0xcc},
+ {0xb6, 0x18, 0x00, 0xcc},
+ {0xb6, 0x17, 0x96, 0xcc},
+ {0xb6, 0x16, 0x00, 0xcc},
+ {0xb6, 0x22, 0x12, 0xcc},
+ {0xb6, 0x23, 0x0b, 0xcc},
+ {0xb3, 0x02, 0x02, 0xcc},
+ {0xbf, 0xc0, 0x39, 0xcc},
+ {0xbf, 0xc1, 0x04, 0xcc},
+ {0xbf, 0xcc, 0x10, 0xcc},
+ {0xb9, 0x12, 0x00, 0xcc},
+ {0xb9, 0x13, 0x0a, 0xcc},
+ {0xb9, 0x14, 0x0a, 0xcc},
+ {0xb9, 0x15, 0x0a, 0xcc},
+ {0xb9, 0x16, 0x0a, 0xcc},
+ {0xb9, 0x18, 0x00, 0xcc},
+ {0xb9, 0x19, 0x0f, 0xcc},
+ {0xb9, 0x1a, 0x0f, 0xcc},
+ {0xb9, 0x1b, 0x0f, 0xcc},
+ {0xb9, 0x1c, 0x0f, 0xcc},
+ {0xb8, 0x8e, 0x00, 0xcc},
+ {0xb8, 0x8f, 0xff, 0xcc},
+ {0xb6, 0x12, 0xf8, 0xcc},
+ {0xb6, 0x13, 0x13, 0xcc},
+ {0xbc, 0x02, 0x18, 0xcc},
+ {0xbc, 0x03, 0x50, 0xcc},
+ {0xbc, 0x04, 0x18, 0xcc},
+ {0xbc, 0x05, 0x00, 0xcc},
+ {0xbc, 0x06, 0x00, 0xcc},
+ {0xbc, 0x08, 0x30, 0xcc},
+ {0xbc, 0x09, 0x40, 0xcc},
+ {0xbc, 0x0a, 0x10, 0xcc},
+ {0xb8, 0x0c, 0x20, 0xcc},
+ {0xb8, 0x0d, 0x70, 0xcc},
+ {0xbc, 0x0b, 0x00, 0xcc},
+ {0xbc, 0x0c, 0x00, 0xcc},
+ {0x35, 0x00, 0xef, 0xbb},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {}
+};
+
+static const u8 mi1310_socinitVGA_JPG[][4] = {
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0xb3, 0x00, 0x64, 0xcc},
+ {0xb3, 0x00, 0x65, 0xcc},
+ {0xb3, 0x05, 0x00, 0xcc},
+ {0xb3, 0x06, 0x00, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x34, 0x02, 0xcc},
+ {0xb3, 0x35, 0xdd, 0xcc}, /* i2c add: 5d */
+ {0xb3, 0x02, 0x00, 0xcc},
+ {0xb3, 0x03, 0x0a, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x03, 0xcc},
+ {0xb3, 0x23, 0xc0, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x04, 0xcc},
+ {0xb3, 0x17, 0xff, 0xcc},
+ {0xb3, 0x00, 0x65, 0xcc},
+ {0xb8, 0x00, 0x00, 0xcc},
+ {0xbc, 0x00, 0xd0, 0xcc},
+ {0xbc, 0x01, 0x01, 0xcc},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0xc8, 0x9f, 0x0b, 0xbb},
+ {0x5b, 0x00, 0x01, 0xbb},
+ {0x2f, 0xde, 0x20, 0xbb},
+ {0xf0, 0x00, 0x00, 0xbb},
+ {0x20, 0x03, 0x02, 0xbb}, /* h/v flip */
+ {0xf0, 0x00, 0x01, 0xbb},
+ {0x05, 0x00, 0x07, 0xbb},
+ {0x34, 0x00, 0x00, 0xbb},
+ {0x35, 0xff, 0x00, 0xbb},
+ {0xdc, 0x07, 0x02, 0xbb},
+ {0xdd, 0x3c, 0x18, 0xbb},
+ {0xde, 0x92, 0x6d, 0xbb},
+ {0xdf, 0xcd, 0xb1, 0xbb},
+ {0xe0, 0xff, 0xe7, 0xbb},
+ {0x06, 0xf0, 0x0d, 0xbb},
+ {0x06, 0x70, 0x0e, 0xbb},
+ {0x4c, 0x00, 0x01, 0xbb},
+ {0x4d, 0x00, 0x01, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x2e, 0x0c, 0x55, 0xbb},
+ {0x21, 0xb6, 0x6e, 0xbb},
+ {0x36, 0x30, 0x10, 0xbb},
+ {0x37, 0x00, 0xc1, 0xbb},
+ {0xf0, 0x00, 0x00, 0xbb},
+ {0x07, 0x00, 0x84, 0xbb},
+ {0x08, 0x02, 0x4a, 0xbb},
+ {0x05, 0x01, 0x10, 0xbb},
+ {0x06, 0x00, 0x39, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x58, 0x02, 0x67, 0xbb},
+ {0x57, 0x02, 0x00, 0xbb},
+ {0x5a, 0x02, 0x67, 0xbb},
+ {0x59, 0x02, 0x00, 0xbb},
+ {0x5c, 0x12, 0x0d, 0xbb},
+ {0x5d, 0x16, 0x11, 0xbb},
+ {0x39, 0x06, 0x18, 0xbb},
+ {0x3a, 0x06, 0x18, 0xbb},
+ {0x3b, 0x06, 0x18, 0xbb},
+ {0x3c, 0x06, 0x18, 0xbb},
+ {0x64, 0x7b, 0x5b, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x36, 0x30, 0x10, 0xbb},
+ {0x37, 0x00, 0xc0, 0xbb},
+ {0xbc, 0x0e, 0x00, 0xcc},
+ {0xbc, 0x0f, 0x05, 0xcc},
+ {0xbc, 0x10, 0xc0, 0xcc},
+ {0xbc, 0x11, 0x03, 0xcc},
+ {0xb6, 0x00, 0x00, 0xcc},
+ {0xb6, 0x03, 0x02, 0xcc},
+ {0xb6, 0x02, 0x80, 0xcc},
+ {0xb6, 0x05, 0x01, 0xcc},
+ {0xb6, 0x04, 0xe0, 0xcc},
+ {0xb6, 0x12, 0xf8, 0xcc},
+ {0xb6, 0x13, 0x25, 0xcc},
+ {0xb6, 0x18, 0x02, 0xcc},
+ {0xb6, 0x17, 0x58, 0xcc},
+ {0xb6, 0x16, 0x00, 0xcc},
+ {0xb6, 0x22, 0x12, 0xcc},
+ {0xb6, 0x23, 0x0b, 0xcc},
+ {0xbf, 0xc0, 0x39, 0xcc},
+ {0xbf, 0xc1, 0x04, 0xcc},
+ {0xbf, 0xcc, 0x00, 0xcc},
+ {0xbc, 0x02, 0x18, 0xcc},
+ {0xbc, 0x03, 0x50, 0xcc},
+ {0xbc, 0x04, 0x18, 0xcc},
+ {0xbc, 0x05, 0x00, 0xcc},
+ {0xbc, 0x06, 0x00, 0xcc},
+ {0xbc, 0x08, 0x30, 0xcc},
+ {0xbc, 0x09, 0x40, 0xcc},
+ {0xbc, 0x0a, 0x10, 0xcc},
+ {0xbc, 0x0b, 0x00, 0xcc},
+ {0xbc, 0x0c, 0x00, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {0xf0, 0x00, 0x01, 0xbb},
+ {0x80, 0x00, 0x03, 0xbb},
+ {0x81, 0xc7, 0x14, 0xbb},
+ {0x82, 0xeb, 0xe8, 0xbb},
+ {0x83, 0xfe, 0xf4, 0xbb},
+ {0x84, 0xcd, 0x10, 0xbb},
+ {0x85, 0xf3, 0xee, 0xbb},
+ {0x86, 0xff, 0xf1, 0xbb},
+ {0x87, 0xcd, 0x10, 0xbb},
+ {0x88, 0xf3, 0xee, 0xbb},
+ {0x89, 0x01, 0xf1, 0xbb},
+ {0x8a, 0xe5, 0x17, 0xbb},
+ {0x8b, 0xe8, 0xe2, 0xbb},
+ {0x8c, 0xf7, 0xed, 0xbb},
+ {0x8d, 0x00, 0xff, 0xbb},
+ {0x8e, 0xec, 0x10, 0xbb},
+ {0x8f, 0xf0, 0xed, 0xbb},
+ {0x90, 0xf9, 0xf2, 0xbb},
+ {0x91, 0x00, 0x00, 0xbb},
+ {0x92, 0xe9, 0x0d, 0xbb},
+ {0x93, 0xf4, 0xf2, 0xbb},
+ {0x94, 0xfb, 0xf5, 0xbb},
+ {0x95, 0x00, 0xff, 0xbb},
+ {0xb6, 0x0f, 0x08, 0xbb},
+ {0xb7, 0x3d, 0x16, 0xbb},
+ {0xb8, 0x0c, 0x04, 0xbb},
+ {0xb9, 0x1c, 0x07, 0xbb},
+ {0xba, 0x0a, 0x03, 0xbb},
+ {0xbb, 0x1b, 0x09, 0xbb},
+ {0xbc, 0x17, 0x0d, 0xbb},
+ {0xbd, 0x23, 0x1d, 0xbb},
+ {0xbe, 0x00, 0x28, 0xbb},
+ {0xbf, 0x11, 0x09, 0xbb},
+ {0xc0, 0x16, 0x15, 0xbb},
+ {0xc1, 0x00, 0x1b, 0xbb},
+ {0xc2, 0x0e, 0x07, 0xbb},
+ {0xc3, 0x14, 0x10, 0xbb},
+ {0xc4, 0x00, 0x17, 0xbb},
+ {0x06, 0x74, 0x8e, 0xbb},
+ {0xf0, 0x00, 0x01, 0xbb},
+ {0x06, 0xf4, 0x8e, 0xbb},
+ {0x00, 0x00, 0x50, 0xdd},
+ {0x06, 0x74, 0x8e, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x24, 0x50, 0x20, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x34, 0x0c, 0x50, 0xbb},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0xf0, 0x00, 0x00, 0xbb},
+ {0x03, 0x03, 0xc0, 0xbb},
+ {},
+};
+static const u8 mi1310_socinitQVGA_JPG[][4] = {
+ {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc},
+ {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc},
+ {0xb3, 0x05, 0x00, 0xcc}, {0xb3, 0x06, 0x00, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x34, 0x02, 0xcc}, {0xb3, 0x35, 0xdd, 0xcc},
+ {0xb3, 0x02, 0x00, 0xcc}, {0xb3, 0x03, 0x0a, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x03, 0xcc},
+ {0xb3, 0x23, 0xc0, 0xcc}, {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc}, {0xb3, 0x16, 0x04, 0xcc},
+ {0xb3, 0x17, 0xff, 0xcc}, {0xb3, 0x00, 0x65, 0xcc},
+ {0xb8, 0x00, 0x00, 0xcc}, {0xbc, 0x00, 0xf0, 0xcc},
+ {0xbc, 0x01, 0x01, 0xcc}, {0xf0, 0x00, 0x02, 0xbb},
+ {0xc8, 0x9f, 0x0b, 0xbb}, {0x5b, 0x00, 0x01, 0xbb},
+ {0x2f, 0xde, 0x20, 0xbb}, {0xf0, 0x00, 0x00, 0xbb},
+ {0x20, 0x03, 0x02, 0xbb}, /* h/v flip */
+ {0xf0, 0x00, 0x01, 0xbb},
+ {0x05, 0x00, 0x07, 0xbb}, {0x34, 0x00, 0x00, 0xbb},
+ {0x35, 0xff, 0x00, 0xbb}, {0xdc, 0x07, 0x02, 0xbb},
+ {0xdd, 0x3c, 0x18, 0xbb}, {0xde, 0x92, 0x6d, 0xbb},
+ {0xdf, 0xcd, 0xb1, 0xbb}, {0xe0, 0xff, 0xe7, 0xbb},
+ {0x06, 0xf0, 0x0d, 0xbb}, {0x06, 0x70, 0x0e, 0xbb},
+ {0x4c, 0x00, 0x01, 0xbb}, {0x4d, 0x00, 0x01, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb}, {0x2e, 0x0c, 0x55, 0xbb},
+ {0x21, 0xb6, 0x6e, 0xbb}, {0x36, 0x30, 0x10, 0xbb},
+ {0x37, 0x00, 0xc1, 0xbb}, {0xf0, 0x00, 0x00, 0xbb},
+ {0x07, 0x00, 0x84, 0xbb}, {0x08, 0x02, 0x4a, 0xbb},
+ {0x05, 0x01, 0x10, 0xbb}, {0x06, 0x00, 0x39, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb}, {0x58, 0x02, 0x67, 0xbb},
+ {0x57, 0x02, 0x00, 0xbb}, {0x5a, 0x02, 0x67, 0xbb},
+ {0x59, 0x02, 0x00, 0xbb}, {0x5c, 0x12, 0x0d, 0xbb},
+ {0x5d, 0x16, 0x11, 0xbb}, {0x39, 0x06, 0x18, 0xbb},
+ {0x3a, 0x06, 0x18, 0xbb}, {0x3b, 0x06, 0x18, 0xbb},
+ {0x3c, 0x06, 0x18, 0xbb}, {0x64, 0x7b, 0x5b, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb}, {0x36, 0x30, 0x10, 0xbb},
+ {0x37, 0x00, 0xc0, 0xbb}, {0xbc, 0x0e, 0x00, 0xcc},
+ {0xbc, 0x0f, 0x05, 0xcc}, {0xbc, 0x10, 0xc0, 0xcc},
+ {0xbc, 0x11, 0x03, 0xcc}, {0xb6, 0x00, 0x00, 0xcc},
+ {0xb6, 0x03, 0x01, 0xcc}, {0xb6, 0x02, 0x40, 0xcc},
+ {0xb6, 0x05, 0x00, 0xcc}, {0xb6, 0x04, 0xf0, 0xcc},
+ {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x25, 0xcc},
+ {0xb6, 0x18, 0x00, 0xcc}, {0xb6, 0x17, 0x96, 0xcc},
+ {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc},
+ {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x39, 0xcc},
+ {0xbf, 0xc1, 0x04, 0xcc}, {0xbf, 0xcc, 0x00, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc}, {0xf0, 0x00, 0x01, 0xbb},
+ {0x80, 0x00, 0x03, 0xbb}, {0x81, 0xc7, 0x14, 0xbb},
+ {0x82, 0xeb, 0xe8, 0xbb}, {0x83, 0xfe, 0xf4, 0xbb},
+ {0x84, 0xcd, 0x10, 0xbb}, {0x85, 0xf3, 0xee, 0xbb},
+ {0x86, 0xff, 0xf1, 0xbb}, {0x87, 0xcd, 0x10, 0xbb},
+ {0x88, 0xf3, 0xee, 0xbb}, {0x89, 0x01, 0xf1, 0xbb},
+ {0x8a, 0xe5, 0x17, 0xbb}, {0x8b, 0xe8, 0xe2, 0xbb},
+ {0x8c, 0xf7, 0xed, 0xbb}, {0x8d, 0x00, 0xff, 0xbb},
+ {0x8e, 0xec, 0x10, 0xbb}, {0x8f, 0xf0, 0xed, 0xbb},
+ {0x90, 0xf9, 0xf2, 0xbb}, {0x91, 0x00, 0x00, 0xbb},
+ {0x92, 0xe9, 0x0d, 0xbb}, {0x93, 0xf4, 0xf2, 0xbb},
+ {0x94, 0xfb, 0xf5, 0xbb}, {0x95, 0x00, 0xff, 0xbb},
+ {0xb6, 0x0f, 0x08, 0xbb}, {0xb7, 0x3d, 0x16, 0xbb},
+ {0xb8, 0x0c, 0x04, 0xbb}, {0xb9, 0x1c, 0x07, 0xbb},
+ {0xba, 0x0a, 0x03, 0xbb}, {0xbb, 0x1b, 0x09, 0xbb},
+ {0xbc, 0x17, 0x0d, 0xbb}, {0xbd, 0x23, 0x1d, 0xbb},
+ {0xbe, 0x00, 0x28, 0xbb}, {0xbf, 0x11, 0x09, 0xbb},
+ {0xc0, 0x16, 0x15, 0xbb}, {0xc1, 0x00, 0x1b, 0xbb},
+ {0xc2, 0x0e, 0x07, 0xbb}, {0xc3, 0x14, 0x10, 0xbb},
+ {0xc4, 0x00, 0x17, 0xbb}, {0x06, 0x74, 0x8e, 0xbb},
+ {0xf0, 0x00, 0x01, 0xbb}, {0x06, 0xf4, 0x8e, 0xbb},
+ {0x00, 0x00, 0x50, 0xdd}, {0x06, 0x74, 0x8e, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb}, {0x24, 0x50, 0x20, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb}, {0x34, 0x0c, 0x50, 0xbb},
+ {0xb3, 0x01, 0x41, 0xcc}, {0xf0, 0x00, 0x00, 0xbb},
+ {0x03, 0x03, 0xc0, 0xbb},
+ {},
+};
+static const u8 mi1310_soc_InitSXGA_JPG[][4] = {
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0xb3, 0x00, 0x64, 0xcc},
+ {0xb3, 0x00, 0x65, 0xcc},
+ {0xb3, 0x05, 0x00, 0xcc},
+ {0xb3, 0x06, 0x00, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x34, 0x02, 0xcc},
+ {0xb3, 0x35, 0xdd, 0xcc},
+ {0xb3, 0x02, 0x00, 0xcc},
+ {0xb3, 0x03, 0x0a, 0xcc},
+ {0xb3, 0x04, 0x0d, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x03, 0xcc},
+ {0xb3, 0x23, 0xc0, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x04, 0xcc},
+ {0xb3, 0x17, 0xff, 0xcc},
+ {0xb3, 0x00, 0x65, 0xcc},
+ {0xb8, 0x00, 0x00, 0xcc},
+ {0xbc, 0x00, 0x70, 0xcc},
+ {0xbc, 0x01, 0x01, 0xcc},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0xc8, 0x9f, 0x0b, 0xbb},
+ {0x5b, 0x00, 0x01, 0xbb},
+ {0xf0, 0x00, 0x00, 0xbb},
+ {0x20, 0x03, 0x02, 0xbb}, /* h/v flip */
+ {0xf0, 0x00, 0x01, 0xbb},
+ {0x05, 0x00, 0x07, 0xbb},
+ {0x34, 0x00, 0x00, 0xbb},
+ {0x35, 0xff, 0x00, 0xbb},
+ {0xdc, 0x07, 0x02, 0xbb},
+ {0xdd, 0x3c, 0x18, 0xbb},
+ {0xde, 0x92, 0x6d, 0xbb},
+ {0xdf, 0xcd, 0xb1, 0xbb},
+ {0xe0, 0xff, 0xe7, 0xbb},
+ {0x06, 0xf0, 0x0d, 0xbb},
+ {0x06, 0x70, 0x0e, 0xbb},
+ {0x4c, 0x00, 0x01, 0xbb},
+ {0x4d, 0x00, 0x01, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x2e, 0x0c, 0x60, 0xbb},
+ {0x21, 0xb6, 0x6e, 0xbb},
+ {0x37, 0x01, 0x40, 0xbb},
+ {0xf0, 0x00, 0x00, 0xbb},
+ {0x07, 0x00, 0x84, 0xbb},
+ {0x08, 0x02, 0x4a, 0xbb},
+ {0x05, 0x01, 0x10, 0xbb},
+ {0x06, 0x00, 0x39, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x58, 0x02, 0x67, 0xbb},
+ {0x57, 0x02, 0x00, 0xbb},
+ {0x5a, 0x02, 0x67, 0xbb},
+ {0x59, 0x02, 0x00, 0xbb},
+ {0x5c, 0x12, 0x0d, 0xbb},
+ {0x5d, 0x16, 0x11, 0xbb},
+ {0x39, 0x06, 0x18, 0xbb},
+ {0x3a, 0x06, 0x18, 0xbb},
+ {0x3b, 0x06, 0x18, 0xbb},
+ {0x3c, 0x06, 0x18, 0xbb},
+ {0x64, 0x7b, 0x5b, 0xbb},
+ {0xb6, 0x00, 0x00, 0xcc},
+ {0xb6, 0x03, 0x05, 0xcc},
+ {0xb6, 0x02, 0x00, 0xcc},
+ {0xb6, 0x05, 0x03, 0xcc},
+ {0xb6, 0x04, 0xc0, 0xcc},
+ {0xb6, 0x12, 0xf8, 0xcc},
+ {0xb6, 0x13, 0x29, 0xcc},
+ {0xb6, 0x18, 0x09, 0xcc},
+ {0xb6, 0x17, 0x60, 0xcc},
+ {0xb6, 0x16, 0x00, 0xcc},
+ {0xb6, 0x22, 0x12, 0xcc},
+ {0xb6, 0x23, 0x0b, 0xcc},
+ {0xbf, 0xc0, 0x39, 0xcc},
+ {0xbf, 0xc1, 0x04, 0xcc},
+ {0xbf, 0xcc, 0x00, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0x00, 0x00, 0x80, 0xdd},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x00, 0x00, 0x10, 0xdd},
+ {0x22, 0xa0, 0x78, 0xbb},
+ {0x23, 0xa0, 0x78, 0xbb},
+ {0x24, 0x7f, 0x00, 0xbb},
+ {0x28, 0xea, 0x02, 0xbb},
+ {0x29, 0x86, 0x7a, 0xbb},
+ {0x5e, 0x52, 0x4c, 0xbb},
+ {0x5f, 0x20, 0x24, 0xbb},
+ {0x60, 0x00, 0x02, 0xbb},
+ {0x02, 0x00, 0xee, 0xbb},
+ {0x03, 0x39, 0x23, 0xbb},
+ {0x04, 0x07, 0x24, 0xbb},
+ {0x09, 0x00, 0xc0, 0xbb},
+ {0x0a, 0x00, 0x79, 0xbb},
+ {0x0b, 0x00, 0x04, 0xbb},
+ {0x0c, 0x00, 0x5c, 0xbb},
+ {0x0d, 0x00, 0xd9, 0xbb},
+ {0x0e, 0x00, 0x53, 0xbb},
+ {0x0f, 0x00, 0x21, 0xbb},
+ {0x10, 0x00, 0xa4, 0xbb},
+ {0x11, 0x00, 0xe5, 0xbb},
+ {0x15, 0x00, 0x00, 0xbb},
+ {0x16, 0x00, 0x00, 0xbb},
+ {0x17, 0x00, 0x00, 0xbb},
+ {0x18, 0x00, 0x00, 0xbb},
+ {0x19, 0x00, 0x00, 0xbb},
+ {0x1a, 0x00, 0x00, 0xbb},
+ {0x1b, 0x00, 0x00, 0xbb},
+ {0x1c, 0x00, 0x00, 0xbb},
+ {0x1d, 0x00, 0x00, 0xbb},
+ {0x1e, 0x00, 0x00, 0xbb},
+ {0xf0, 0x00, 0x01, 0xbb},
+ {0x00, 0x00, 0x20, 0xdd},
+ {0x06, 0xf0, 0x8e, 0xbb},
+ {0x00, 0x00, 0x80, 0xdd},
+ {0x06, 0x70, 0x8e, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x00, 0x00, 0x20, 0xdd},
+ {0x5e, 0x6a, 0x53, 0xbb},
+ {0x5f, 0x40, 0x2c, 0xbb},
+ {0xf0, 0x00, 0x01, 0xbb},
+ {0x00, 0x00, 0x20, 0xdd},
+ {0x58, 0x00, 0x00, 0xbb},
+ {0x53, 0x09, 0x03, 0xbb},
+ {0x54, 0x31, 0x18, 0xbb},
+ {0x55, 0x8b, 0x5f, 0xbb},
+ {0x56, 0xc0, 0xa9, 0xbb},
+ {0x57, 0xe0, 0xd2, 0xbb},
+ {0xe1, 0x00, 0x00, 0xbb},
+ {0xdc, 0x09, 0x03, 0xbb},
+ {0xdd, 0x31, 0x18, 0xbb},
+ {0xde, 0x8b, 0x5f, 0xbb},
+ {0xdf, 0xc0, 0xa9, 0xbb},
+ {0xe0, 0xe0, 0xd2, 0xbb},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {0xf0, 0x00, 0x01, 0xbb},
+ {0x06, 0xf0, 0x8e, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x2f, 0xde, 0x20, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x24, 0x50, 0x20, 0xbb},
+ {0xbc, 0x0e, 0x00, 0xcc},
+ {0xbc, 0x0f, 0x05, 0xcc},
+ {0xbc, 0x10, 0xc0, 0xcc},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x34, 0x0c, 0x50, 0xbb},
+ {0xbc, 0x11, 0x03, 0xcc},
+ {0xf0, 0x00, 0x01, 0xbb},
+ {0x80, 0x00, 0x03, 0xbb},
+ {0x81, 0xc7, 0x14, 0xbb},
+ {0x82, 0xeb, 0xe8, 0xbb},
+ {0x83, 0xfe, 0xf4, 0xbb},
+ {0x84, 0xcd, 0x10, 0xbb},
+ {0x85, 0xf3, 0xee, 0xbb},
+ {0x86, 0xff, 0xf1, 0xbb},
+ {0x87, 0xcd, 0x10, 0xbb},
+ {0x88, 0xf3, 0xee, 0xbb},
+ {0x89, 0x01, 0xf1, 0xbb},
+ {0x8a, 0xe5, 0x17, 0xbb},
+ {0x8b, 0xe8, 0xe2, 0xbb},
+ {0x8c, 0xf7, 0xed, 0xbb},
+ {0x8d, 0x00, 0xff, 0xbb},
+ {0x8e, 0xec, 0x10, 0xbb},
+ {0x8f, 0xf0, 0xed, 0xbb},
+ {0x90, 0xf9, 0xf2, 0xbb},
+ {0x91, 0x00, 0x00, 0xbb},
+ {0x92, 0xe9, 0x0d, 0xbb},
+ {0x93, 0xf4, 0xf2, 0xbb},
+ {0x94, 0xfb, 0xf5, 0xbb},
+ {0x95, 0x00, 0xff, 0xbb},
+ {0xb6, 0x0f, 0x08, 0xbb},
+ {0xb7, 0x3d, 0x16, 0xbb},
+ {0xb8, 0x0c, 0x04, 0xbb},
+ {0xb9, 0x1c, 0x07, 0xbb},
+ {0xba, 0x0a, 0x03, 0xbb},
+ {0xbb, 0x1b, 0x09, 0xbb},
+ {0xbc, 0x17, 0x0d, 0xbb},
+ {0xbd, 0x23, 0x1d, 0xbb},
+ {0xbe, 0x00, 0x28, 0xbb},
+ {0xbf, 0x11, 0x09, 0xbb},
+ {0xc0, 0x16, 0x15, 0xbb},
+ {0xc1, 0x00, 0x1b, 0xbb},
+ {0xc2, 0x0e, 0x07, 0xbb},
+ {0xc3, 0x14, 0x10, 0xbb},
+ {0xc4, 0x00, 0x17, 0xbb},
+ {0x06, 0x74, 0x8e, 0xbb},
+ {0xf0, 0x00, 0x00, 0xbb},
+ {0x03, 0x03, 0xc0, 0xbb},
+ {}
+};
+
+static const u8 mi1320_gamma[17] = {
+ 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
+ 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff
+};
+static const u8 mi1320_matrix[9] = {
+ 0x54, 0xda, 0x06, 0xf1, 0x50, 0xf4, 0xf7, 0xea, 0x52
+};
+static const u8 mi1320_initVGA_data[][4] = {
+ {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x33, 0xdd},
+ {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x33, 0xdd},
+ {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x33, 0xdd},
+ {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc},
+ {0xb0, 0x16, 0x03, 0xcc}, {0xb3, 0x05, 0x00, 0xcc},
+ {0xb3, 0x06, 0x00, 0xcc}, {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x34, 0x02, 0xcc},
+ {0xb3, 0x35, 0xc8, 0xcc}, /* i2c add: 48 */
+ {0xb3, 0x02, 0x00, 0xcc},
+ {0xb3, 0x03, 0x0a, 0xcc}, {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x03, 0xcc}, {0xb3, 0x23, 0xc0, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x04, 0xcc}, {0xb3, 0x17, 0xff, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc}, {0xbc, 0x00, 0xd0, 0xcc},
+ {0xbc, 0x01, 0x01, 0xcc}, {0xf0, 0x00, 0x00, 0xbb},
+ {0x0d, 0x00, 0x09, 0xbb}, {0x00, 0x01, 0x00, 0xdd},
+ {0x0d, 0x00, 0x08, 0xbb}, {0xf0, 0x00, 0x01, 0xbb},
+ {0xa1, 0x05, 0x00, 0xbb}, {0xa4, 0x03, 0xc0, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb}, {0x00, 0x00, 0x10, 0xdd},
+ {0xc8, 0x9f, 0x0b, 0xbb}, {0x00, 0x00, 0x10, 0xdd},
+ {0xf0, 0x00, 0x00, 0xbb}, {0x00, 0x00, 0x10, 0xdd},
+ {0x20, 0x01, 0x00, 0xbb}, {0x00, 0x00, 0x10, 0xdd},
+ {0xf0, 0x00, 0x01, 0xbb}, {0x9d, 0x3c, 0xa0, 0xbb},
+ {0x47, 0x30, 0x30, 0xbb}, {0xf0, 0x00, 0x00, 0xbb},
+ {0x0a, 0x80, 0x11, 0xbb}, {0x35, 0x00, 0x22, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb}, {0x9d, 0xc5, 0x05, 0xbb},
+ {0xdc, 0x0f, 0xfc, 0xbb}, {0xf0, 0x00, 0x01, 0xbb},
+ {0x06, 0x74, 0x0e, 0xbb}, {0x80, 0x00, 0x06, 0xbb},
+ {0x81, 0x04, 0x00, 0xbb}, {0x82, 0x01, 0x02, 0xbb},
+ {0x83, 0x03, 0x02, 0xbb}, {0x84, 0x05, 0x00, 0xbb},
+ {0x85, 0x01, 0x00, 0xbb}, {0x86, 0x03, 0x02, 0xbb},
+ {0x87, 0x05, 0x00, 0xbb}, {0x88, 0x01, 0x00, 0xbb},
+ {0x89, 0x02, 0x02, 0xbb}, {0x8a, 0xfd, 0x04, 0xbb},
+ {0x8b, 0xfc, 0xfd, 0xbb}, {0x8c, 0xff, 0xfd, 0xbb},
+ {0x8d, 0x00, 0x00, 0xbb}, {0x8e, 0xfe, 0x05, 0xbb},
+ {0x8f, 0xfc, 0xfd, 0xbb}, {0x90, 0xfe, 0xfd, 0xbb},
+ {0x91, 0x00, 0x00, 0xbb}, {0x92, 0xfe, 0x03, 0xbb},
+ {0x93, 0xfd, 0xfe, 0xbb}, {0x94, 0xff, 0xfd, 0xbb},
+ {0x95, 0x00, 0x00, 0xbb}, {0xb6, 0x07, 0x05, 0xbb},
+ {0xb7, 0x13, 0x06, 0xbb}, {0xb8, 0x08, 0x06, 0xbb},
+ {0xb9, 0x14, 0x08, 0xbb}, {0xba, 0x06, 0x05, 0xbb},
+ {0xbb, 0x13, 0x06, 0xbb}, {0xbc, 0x03, 0x01, 0xbb},
+ {0xbd, 0x03, 0x04, 0xbb}, {0xbe, 0x00, 0x02, 0xbb},
+ {0xbf, 0x03, 0x01, 0xbb}, {0xc0, 0x02, 0x04, 0xbb},
+ {0xc1, 0x00, 0x04, 0xbb}, {0xc2, 0x02, 0x01, 0xbb},
+ {0xc3, 0x01, 0x03, 0xbb}, {0xc4, 0x00, 0x04, 0xbb},
+ {0xf0, 0x00, 0x00, 0xbb}, {0x05, 0x01, 0x13, 0xbb},
+ {0x06, 0x00, 0x11, 0xbb}, {0x07, 0x00, 0x85, 0xbb},
+ {0x08, 0x00, 0x27, 0xbb},
+ {0x20, 0x01, 0x00, 0xbb}, /* h/v flips - was 03 */
+ {0x21, 0x80, 0x00, 0xbb}, {0x22, 0x0d, 0x0f, 0xbb},
+ {0x24, 0x80, 0x00, 0xbb}, {0x59, 0x00, 0xff, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb}, {0x39, 0x03, 0x0d, 0xbb},
+ {0x3a, 0x06, 0x1b, 0xbb}, {0x3b, 0x00, 0x95, 0xbb},
+ {0x3c, 0x04, 0xdb, 0xbb}, {0x57, 0x02, 0x00, 0xbb},
+ {0x58, 0x02, 0x66, 0xbb}, {0x59, 0x00, 0xff, 0xbb},
+ {0x5a, 0x01, 0x33, 0xbb}, {0x5c, 0x12, 0x0d, 0xbb},
+ {0x5d, 0x16, 0x11, 0xbb}, {0x64, 0x5e, 0x1c, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb}, {0x2f, 0xd1, 0x00, 0xbb},
+ {0x5b, 0x00, 0x01, 0xbb}, {0xf0, 0x00, 0x02, 0xbb},
+ {0x36, 0x68, 0x10, 0xbb}, {0x00, 0x00, 0x30, 0xdd},
+ {0x37, 0x82, 0x00, 0xbb}, {0xbc, 0x0e, 0x00, 0xcc},
+ {0xbc, 0x0f, 0x05, 0xcc}, {0xbc, 0x10, 0xc0, 0xcc},
+ {0xbc, 0x11, 0x03, 0xcc}, {0xb6, 0x00, 0x00, 0xcc},
+ {0xb6, 0x03, 0x05, 0xcc}, {0xb6, 0x02, 0x00, 0xcc},
+ {0xb6, 0x05, 0x04, 0xcc}, {0xb6, 0x04, 0x00, 0xcc},
+ {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x29, 0xcc},
+ {0xb6, 0x18, 0x0a, 0xcc}, {0xb6, 0x17, 0x00, 0xcc},
+ {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc},
+ {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x26, 0xcc},
+ {0xbf, 0xc1, 0x02, 0xcc}, {0xbf, 0xcc, 0x04, 0xcc},
+ {0xbc, 0x02, 0x18, 0xcc}, {0xbc, 0x03, 0x50, 0xcc},
+ {0xbc, 0x04, 0x18, 0xcc}, {0xbc, 0x05, 0x00, 0xcc},
+ {0xbc, 0x06, 0x00, 0xcc}, {0xbc, 0x08, 0x30, 0xcc},
+ {0xbc, 0x09, 0x40, 0xcc}, {0xbc, 0x0a, 0x10, 0xcc},
+ {0xbc, 0x0b, 0x00, 0xcc}, {0xbc, 0x0c, 0x00, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc}, {0xb3, 0x01, 0x41, 0xcc},
+ {}
+};
+static const u8 mi1320_initQVGA_data[][4] = {
+ {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x33, 0xdd},
+ {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x33, 0xdd},
+ {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x33, 0xdd},
+ {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc},
+ {0xb0, 0x16, 0x03, 0xcc}, {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x01, 0xcc}, {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x34, 0x02, 0xcc},
+ {0xb3, 0x35, 0xc8, 0xcc}, {0xb3, 0x02, 0x00, 0xcc},
+ {0xb3, 0x03, 0x0a, 0xcc}, {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x01, 0xcc}, {0xb3, 0x23, 0xe0, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x00, 0x65, 0xcc}, {0xb8, 0x00, 0x00, 0xcc},
+ {0xbc, 0x00, 0xd0, 0xcc}, {0xbc, 0x01, 0x01, 0xcc},
+ {0xf0, 0x00, 0x00, 0xbb}, {0x0d, 0x00, 0x09, 0xbb},
+ {0x00, 0x01, 0x00, 0xdd}, {0x0d, 0x00, 0x08, 0xbb},
+ {0xf0, 0x00, 0x00, 0xbb}, {0x02, 0x00, 0x64, 0xbb},
+ {0x05, 0x01, 0x78, 0xbb}, {0x06, 0x00, 0x11, 0xbb},
+ {0x07, 0x01, 0x42, 0xbb}, {0x08, 0x00, 0x11, 0xbb},
+ {0x20, 0x01, 0x00, 0xbb}, {0x21, 0x80, 0x00, 0xbb},
+ {0x22, 0x0d, 0x0f, 0xbb}, {0x24, 0x80, 0x00, 0xbb},
+ {0x59, 0x00, 0xff, 0xbb}, {0xf0, 0x00, 0x01, 0xbb},
+ {0x9d, 0x3c, 0xa0, 0xbb}, {0x47, 0x30, 0x30, 0xbb},
+ {0xf0, 0x00, 0x00, 0xbb}, {0x0a, 0x80, 0x11, 0xbb},
+ {0x35, 0x00, 0x22, 0xbb}, {0xf0, 0x00, 0x02, 0xbb},
+ {0x9d, 0xc5, 0x05, 0xbb}, {0xdc, 0x0f, 0xfc, 0xbb},
+ {0xf0, 0x00, 0x01, 0xbb}, {0x06, 0x74, 0x0e, 0xbb},
+ {0x80, 0x00, 0x06, 0xbb}, {0x81, 0x04, 0x00, 0xbb},
+ {0x82, 0x01, 0x02, 0xbb}, {0x83, 0x03, 0x02, 0xbb},
+ {0x84, 0x05, 0x00, 0xbb}, {0x85, 0x01, 0x00, 0xbb},
+ {0x86, 0x03, 0x02, 0xbb}, {0x87, 0x05, 0x00, 0xbb},
+ {0x88, 0x01, 0x00, 0xbb}, {0x89, 0x02, 0x02, 0xbb},
+ {0x8a, 0xfd, 0x04, 0xbb}, {0x8b, 0xfc, 0xfd, 0xbb},
+ {0x8c, 0xff, 0xfd, 0xbb}, {0x8d, 0x00, 0x00, 0xbb},
+ {0x8e, 0xfe, 0x05, 0xbb}, {0x8f, 0xfc, 0xfd, 0xbb},
+ {0x90, 0xfe, 0xfd, 0xbb}, {0x91, 0x00, 0x00, 0xbb},
+ {0x92, 0xfe, 0x03, 0xbb}, {0x93, 0xfd, 0xfe, 0xbb},
+ {0x94, 0xff, 0xfd, 0xbb}, {0x95, 0x00, 0x00, 0xbb},
+ {0xb6, 0x07, 0x05, 0xbb}, {0xb7, 0x13, 0x06, 0xbb},
+ {0xb8, 0x08, 0x06, 0xbb}, {0xb9, 0x14, 0x08, 0xbb},
+ {0xba, 0x06, 0x05, 0xbb}, {0xbb, 0x13, 0x06, 0xbb},
+ {0xbc, 0x03, 0x01, 0xbb}, {0xbd, 0x03, 0x04, 0xbb},
+ {0xbe, 0x00, 0x02, 0xbb}, {0xbf, 0x03, 0x01, 0xbb},
+ {0xc0, 0x02, 0x04, 0xbb}, {0xc1, 0x00, 0x04, 0xbb},
+ {0xc2, 0x02, 0x01, 0xbb}, {0xc3, 0x01, 0x03, 0xbb},
+ {0xc4, 0x00, 0x04, 0xbb}, {0xf0, 0x00, 0x02, 0xbb},
+ {0xc8, 0x00, 0x00, 0xbb}, {0x2e, 0x00, 0x00, 0xbb},
+ {0x2e, 0x0c, 0x5b, 0xbb}, {0x2f, 0xd1, 0x00, 0xbb},
+ {0x39, 0x03, 0xca, 0xbb}, {0x3a, 0x06, 0x80, 0xbb},
+ {0x3b, 0x01, 0x52, 0xbb}, {0x3c, 0x05, 0x40, 0xbb},
+ {0x57, 0x01, 0x9c, 0xbb}, {0x58, 0x01, 0xee, 0xbb},
+ {0x59, 0x00, 0xf0, 0xbb}, {0x5a, 0x01, 0x20, 0xbb},
+ {0x5c, 0x1d, 0x17, 0xbb}, {0x5d, 0x22, 0x1c, 0xbb},
+ {0x64, 0x1e, 0x1c, 0xbb}, {0x5b, 0x00, 0x01, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb}, {0x36, 0x68, 0x10, 0xbb},
+ {0x00, 0x00, 0x30, 0xdd}, {0x37, 0x81, 0x00, 0xbb},
+ {0xbc, 0x02, 0x18, 0xcc}, {0xbc, 0x03, 0x50, 0xcc},
+ {0xbc, 0x04, 0x18, 0xcc}, {0xbc, 0x05, 0x00, 0xcc},
+ {0xbc, 0x06, 0x00, 0xcc}, {0xbc, 0x08, 0x30, 0xcc},
+ {0xbc, 0x09, 0x40, 0xcc}, {0xbc, 0x0a, 0x10, 0xcc},
+ {0xbc, 0x0b, 0x00, 0xcc}, {0xbc, 0x0c, 0x00, 0xcc},
+ {0xbf, 0xc0, 0x26, 0xcc}, {0xbf, 0xc1, 0x02, 0xcc},
+ {0xbf, 0xcc, 0x04, 0xcc}, {0xb3, 0x5c, 0x01, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {}
+};
+
+static const u8 mi1320_soc_InitVGA[][4] = {
+ {0xb3, 0x01, 0x01, 0xcc},
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0x00, 0x00, 0x30, 0xdd},
+ {0xb3, 0x00, 0x64, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x01, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x34, 0x02, 0xcc},
+ {0xb3, 0x35, 0xc8, 0xcc}, /* i2c add: 48 */
+ {0xb3, 0x02, 0x00, 0xcc},
+ {0xb3, 0x03, 0x0a, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x01, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc},
+ {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xb8, 0x00, 0x00, 0xcc},
+ {0xbc, 0x00, 0x71, 0xcc},
+ {0xbc, 0x01, 0x01, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x00, 0x00, 0x10, 0xdd},
+ {0xc8, 0x00, 0x00, 0xbb},
+ {0x00, 0x00, 0x30, 0xdd},
+ {0xf0, 0x00, 0x00, 0xbb},
+ {0x00, 0x00, 0x10, 0xdd},
+ {0x07, 0x00, 0xe0, 0xbb},
+ {0x08, 0x00, 0x0b, 0xbb},
+ {0x21, 0x00, 0x0c, 0xbb},
+ {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */
+ {0xbf, 0xc0, 0x26, 0xcc},
+ {0xbf, 0xc1, 0x02, 0xcc},
+ {0xbf, 0xcc, 0x04, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0xf0, 0x00, 0x00, 0xbb},
+ {0x05, 0x01, 0x78, 0xbb},
+ {0x06, 0x00, 0x11, 0xbb},
+ {0x07, 0x01, 0x42, 0xbb},
+ {0x08, 0x00, 0x11, 0xbb},
+ {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */
+ {0x21, 0x80, 0x00, 0xbb},
+ {0x22, 0x0d, 0x0f, 0xbb},
+ {0x24, 0x80, 0x00, 0xbb},
+ {0x59, 0x00, 0xff, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x39, 0x03, 0xca, 0xbb},
+ {0x3a, 0x06, 0x80, 0xbb},
+ {0x3b, 0x01, 0x52, 0xbb},
+ {0x3c, 0x05, 0x40, 0xbb},
+ {0x57, 0x01, 0x9c, 0xbb},
+ {0x58, 0x01, 0xee, 0xbb},
+ {0x59, 0x00, 0xf0, 0xbb},
+ {0x5a, 0x01, 0x20, 0xbb},
+ {0x5c, 0x1d, 0x17, 0xbb},
+ {0x5d, 0x22, 0x1c, 0xbb},
+ {0x64, 0x1e, 0x1c, 0xbb},
+ {0x5b, 0x00, 0x00, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x22, 0xa0, 0x78, 0xbb},
+ {0x23, 0xa0, 0x78, 0xbb},
+ {0x24, 0x7f, 0x00, 0xbb},
+ {0x28, 0xea, 0x02, 0xbb},
+ {0x29, 0x86, 0x7a, 0xbb},
+ {0x5e, 0x52, 0x4c, 0xbb},
+ {0x5f, 0x20, 0x24, 0xbb},
+ {0x60, 0x00, 0x02, 0xbb},
+ {0x02, 0x00, 0xee, 0xbb},
+ {0x03, 0x39, 0x23, 0xbb},
+ {0x04, 0x07, 0x24, 0xbb},
+ {0x09, 0x00, 0xc0, 0xbb},
+ {0x0a, 0x00, 0x79, 0xbb},
+ {0x0b, 0x00, 0x04, 0xbb},
+ {0x0c, 0x00, 0x5c, 0xbb},
+ {0x0d, 0x00, 0xd9, 0xbb},
+ {0x0e, 0x00, 0x53, 0xbb},
+ {0x0f, 0x00, 0x21, 0xbb},
+ {0x10, 0x00, 0xa4, 0xbb},
+ {0x11, 0x00, 0xe5, 0xbb},
+ {0x15, 0x00, 0x00, 0xbb},
+ {0x16, 0x00, 0x00, 0xbb},
+ {0x17, 0x00, 0x00, 0xbb},
+ {0x18, 0x00, 0x00, 0xbb},
+ {0x19, 0x00, 0x00, 0xbb},
+ {0x1a, 0x00, 0x00, 0xbb},
+ {0x1b, 0x00, 0x00, 0xbb},
+ {0x1c, 0x00, 0x00, 0xbb},
+ {0x1d, 0x00, 0x00, 0xbb},
+ {0x1e, 0x00, 0x00, 0xbb},
+ {0xf0, 0x00, 0x01, 0xbb},
+ {0x06, 0xe0, 0x0e, 0xbb},
+ {0x06, 0x60, 0x0e, 0xbb},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {}
+};
+static const u8 mi1320_soc_InitQVGA[][4] = {
+ {0xb3, 0x01, 0x01, 0xcc},
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0x00, 0x00, 0x30, 0xdd},
+ {0xb3, 0x00, 0x64, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x01, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x34, 0x02, 0xcc},
+ {0xb3, 0x35, 0xc8, 0xcc},
+ {0xb3, 0x02, 0x00, 0xcc},
+ {0xb3, 0x03, 0x0a, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x01, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc},
+ {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xb8, 0x00, 0x00, 0xcc},
+ {0xbc, 0x00, 0xd1, 0xcc},
+ {0xbc, 0x01, 0x01, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x00, 0x00, 0x10, 0xdd},
+ {0xc8, 0x00, 0x00, 0xbb},
+ {0x00, 0x00, 0x30, 0xdd},
+ {0xf0, 0x00, 0x00, 0xbb},
+ {0x00, 0x00, 0x10, 0xdd},
+ {0x07, 0x00, 0xe0, 0xbb},
+ {0x08, 0x00, 0x0b, 0xbb},
+ {0x21, 0x00, 0x0c, 0xbb},
+ {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */
+ {0xbf, 0xc0, 0x26, 0xcc},
+ {0xbf, 0xc1, 0x02, 0xcc},
+ {0xbf, 0xcc, 0x04, 0xcc},
+ {0xbc, 0x02, 0x18, 0xcc},
+ {0xbc, 0x03, 0x50, 0xcc},
+ {0xbc, 0x04, 0x18, 0xcc},
+ {0xbc, 0x05, 0x00, 0xcc},
+ {0xbc, 0x06, 0x00, 0xcc},
+ {0xbc, 0x08, 0x30, 0xcc},
+ {0xbc, 0x09, 0x40, 0xcc},
+ {0xbc, 0x0a, 0x10, 0xcc},
+ {0xbc, 0x0b, 0x00, 0xcc},
+ {0xbc, 0x0c, 0x00, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0xf0, 0x00, 0x00, 0xbb},
+ {0x05, 0x01, 0x78, 0xbb},
+ {0x06, 0x00, 0x11, 0xbb},
+ {0x07, 0x01, 0x42, 0xbb},
+ {0x08, 0x00, 0x11, 0xbb},
+ {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */
+ {0x21, 0x80, 0x00, 0xbb},
+ {0x22, 0x0d, 0x0f, 0xbb},
+ {0x24, 0x80, 0x00, 0xbb},
+ {0x59, 0x00, 0xff, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x39, 0x03, 0xca, 0xbb},
+ {0x3a, 0x06, 0x80, 0xbb},
+ {0x3b, 0x01, 0x52, 0xbb},
+ {0x3c, 0x05, 0x40, 0xbb},
+ {0x57, 0x01, 0x9c, 0xbb},
+ {0x58, 0x01, 0xee, 0xbb},
+ {0x59, 0x00, 0xf0, 0xbb},
+ {0x5a, 0x01, 0x20, 0xbb},
+ {0x5c, 0x1d, 0x17, 0xbb},
+ {0x5d, 0x22, 0x1c, 0xbb},
+ {0x64, 0x1e, 0x1c, 0xbb},
+ {0x5b, 0x00, 0x00, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x22, 0xa0, 0x78, 0xbb},
+ {0x23, 0xa0, 0x78, 0xbb},
+ {0x24, 0x7f, 0x00, 0xbb},
+ {0x28, 0xea, 0x02, 0xbb},
+ {0x29, 0x86, 0x7a, 0xbb},
+ {0x5e, 0x52, 0x4c, 0xbb},
+ {0x5f, 0x20, 0x24, 0xbb},
+ {0x60, 0x00, 0x02, 0xbb},
+ {0x02, 0x00, 0xee, 0xbb},
+ {0x03, 0x39, 0x23, 0xbb},
+ {0x04, 0x07, 0x24, 0xbb},
+ {0x09, 0x00, 0xc0, 0xbb},
+ {0x0a, 0x00, 0x79, 0xbb},
+ {0x0b, 0x00, 0x04, 0xbb},
+ {0x0c, 0x00, 0x5c, 0xbb},
+ {0x0d, 0x00, 0xd9, 0xbb},
+ {0x0e, 0x00, 0x53, 0xbb},
+ {0x0f, 0x00, 0x21, 0xbb},
+ {0x10, 0x00, 0xa4, 0xbb},
+ {0x11, 0x00, 0xe5, 0xbb},
+ {0x15, 0x00, 0x00, 0xbb},
+ {0x16, 0x00, 0x00, 0xbb},
+ {0x17, 0x00, 0x00, 0xbb},
+ {0x18, 0x00, 0x00, 0xbb},
+ {0x19, 0x00, 0x00, 0xbb},
+ {0x1a, 0x00, 0x00, 0xbb},
+ {0x1b, 0x00, 0x00, 0xbb},
+ {0x1c, 0x00, 0x00, 0xbb},
+ {0x1d, 0x00, 0x00, 0xbb},
+ {0x1e, 0x00, 0x00, 0xbb},
+ {0xf0, 0x00, 0x01, 0xbb},
+ {0x06, 0xe0, 0x0e, 0xbb},
+ {0x06, 0x60, 0x0e, 0xbb},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {}
+};
+static const u8 mi1320_soc_InitSXGA[][4] = {
+ {0xb3, 0x01, 0x01, 0xcc},
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0x00, 0x00, 0x30, 0xdd},
+ {0xb3, 0x00, 0x64, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x01, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x34, 0x02, 0xcc},
+ {0xb3, 0x35, 0xc8, 0xcc},
+ {0xb3, 0x02, 0x00, 0xcc},
+ {0xb3, 0x03, 0x0a, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x04, 0xcc},
+ {0xb3, 0x23, 0x00, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x04, 0xcc},
+ {0xb3, 0x17, 0xff, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xbc, 0x00, 0x71, 0xcc},
+ {0xbc, 0x01, 0x01, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x00, 0x00, 0x30, 0xdd},
+ {0xc8, 0x9f, 0x0b, 0xbb},
+ {0x00, 0x00, 0x20, 0xdd},
+ {0x5b, 0x00, 0x01, 0xbb},
+ {0x00, 0x00, 0x20, 0xdd},
+ {0xf0, 0x00, 0x00, 0xbb},
+ {0x00, 0x00, 0x30, 0xdd},
+ {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */
+ {0x00, 0x00, 0x20, 0xdd},
+ {0xbf, 0xc0, 0x26, 0xcc},
+ {0xbf, 0xc1, 0x02, 0xcc},
+ {0xbf, 0xcc, 0x04, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0xf0, 0x00, 0x00, 0xbb},
+ {0x05, 0x01, 0x78, 0xbb},
+ {0x06, 0x00, 0x11, 0xbb},
+ {0x07, 0x01, 0x42, 0xbb},
+ {0x08, 0x00, 0x11, 0xbb},
+ {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */
+ {0x21, 0x80, 0x00, 0xbb},
+ {0x22, 0x0d, 0x0f, 0xbb},
+ {0x24, 0x80, 0x00, 0xbb},
+ {0x59, 0x00, 0xff, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x39, 0x03, 0xca, 0xbb},
+ {0x3a, 0x06, 0x80, 0xbb},
+ {0x3b, 0x01, 0x52, 0xbb},
+ {0x3c, 0x05, 0x40, 0xbb},
+ {0x57, 0x01, 0x9c, 0xbb},
+ {0x58, 0x01, 0xee, 0xbb},
+ {0x59, 0x00, 0xf0, 0xbb},
+ {0x5a, 0x01, 0x20, 0xbb},
+ {0x5c, 0x1d, 0x17, 0xbb},
+ {0x5d, 0x22, 0x1c, 0xbb},
+ {0x64, 0x1e, 0x1c, 0xbb},
+ {0x5b, 0x00, 0x00, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x22, 0xa0, 0x78, 0xbb},
+ {0x23, 0xa0, 0x78, 0xbb},
+ {0x24, 0x7f, 0x00, 0xbb},
+ {0x28, 0xea, 0x02, 0xbb},
+ {0x29, 0x86, 0x7a, 0xbb},
+ {0x5e, 0x52, 0x4c, 0xbb},
+ {0x5f, 0x20, 0x24, 0xbb},
+ {0x60, 0x00, 0x02, 0xbb},
+ {0x02, 0x00, 0xee, 0xbb},
+ {0x03, 0x39, 0x23, 0xbb},
+ {0x04, 0x07, 0x24, 0xbb},
+ {0x09, 0x00, 0xc0, 0xbb},
+ {0x0a, 0x00, 0x79, 0xbb},
+ {0x0b, 0x00, 0x04, 0xbb},
+ {0x0c, 0x00, 0x5c, 0xbb},
+ {0x0d, 0x00, 0xd9, 0xbb},
+ {0x0e, 0x00, 0x53, 0xbb},
+ {0x0f, 0x00, 0x21, 0xbb},
+ {0x10, 0x00, 0xa4, 0xbb},
+ {0x11, 0x00, 0xe5, 0xbb},
+ {0x15, 0x00, 0x00, 0xbb},
+ {0x16, 0x00, 0x00, 0xbb},
+ {0x17, 0x00, 0x00, 0xbb},
+ {0x18, 0x00, 0x00, 0xbb},
+ {0x19, 0x00, 0x00, 0xbb},
+ {0x1a, 0x00, 0x00, 0xbb},
+ {0x1b, 0x00, 0x00, 0xbb},
+ {0x1c, 0x00, 0x00, 0xbb},
+ {0x1d, 0x00, 0x00, 0xbb},
+ {0x1e, 0x00, 0x00, 0xbb},
+ {0xf0, 0x00, 0x01, 0xbb},
+ {0x06, 0xe0, 0x0e, 0xbb},
+ {0x06, 0x60, 0x0e, 0xbb},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {0xf0, 0x00, 0x00, 0xbb},
+ {0x05, 0x01, 0x13, 0xbb},
+ {0x06, 0x00, 0x11, 0xbb},
+ {0x07, 0x00, 0x85, 0xbb},
+ {0x08, 0x00, 0x27, 0xbb},
+ {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */
+ {0x21, 0x80, 0x00, 0xbb},
+ {0x22, 0x0d, 0x0f, 0xbb},
+ {0x24, 0x80, 0x00, 0xbb},
+ {0x59, 0x00, 0xff, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x39, 0x03, 0x0d, 0xbb},
+ {0x3a, 0x06, 0x1b, 0xbb},
+ {0x3b, 0x00, 0x95, 0xbb},
+ {0x3c, 0x04, 0xdb, 0xbb},
+ {0x57, 0x02, 0x00, 0xbb},
+ {0x58, 0x02, 0x66, 0xbb},
+ {0x59, 0x00, 0xff, 0xbb},
+ {0x5a, 0x01, 0x33, 0xbb},
+ {0x5c, 0x12, 0x0d, 0xbb},
+ {0x5d, 0x16, 0x11, 0xbb},
+ {0x64, 0x5e, 0x1c, 0xbb},
+ {}
+};
+static const u8 po3130_gamma[17] = {
+ 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
+ 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff
+};
+static const u8 po3130_matrix[9] = {
+ 0x5f, 0xec, 0xf5, 0xf1, 0x5a, 0xf5, 0xf1, 0xec, 0x63
+};
+
+static const u8 po3130_initVGA_data[][4] = {
+ {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc},
+ {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc},
+ {0xb3, 0x00, 0x04, 0xcc}, {0xb3, 0x00, 0x24, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc}, {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x05, 0x00, 0xcc},
+ {0xb3, 0x06, 0x01, 0xcc}, {0xb3, 0x03, 0x1a, 0xcc},
+ {0xb3, 0x04, 0x15, 0xcc}, {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x01, 0xcc},
+ {0xb3, 0x23, 0xe8, 0xcc}, {0xb8, 0x08, 0xe8, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x34, 0x01, 0xcc},
+ {0xb3, 0x35, 0xf6, 0xcc}, /* i2c add: 76 */
+ {0xb3, 0x00, 0x27, 0xcc}, {0xbc, 0x00, 0x71, 0xcc},
+ {0xb8, 0x00, 0x21, 0xcc}, {0xb8, 0x27, 0x20, 0xcc},
+ {0xb8, 0x01, 0x79, 0xcc}, {0xb8, 0x81, 0x09, 0xcc},
+ {0xb8, 0x2c, 0x50, 0xcc}, {0xb8, 0x2d, 0xf8, 0xcc},
+ {0xb8, 0x2e, 0xf8, 0xcc}, {0xb8, 0x2f, 0xf8, 0xcc},
+ {0xb8, 0x30, 0x50, 0xcc}, {0xb8, 0x31, 0xf8, 0xcc},
+ {0xb8, 0x32, 0xf8, 0xcc}, {0xb8, 0x33, 0xf8, 0xcc},
+ {0xb8, 0x34, 0x50, 0xcc}, {0xb8, 0x35, 0x00, 0xcc},
+ {0xb8, 0x36, 0x00, 0xcc}, {0xb8, 0x37, 0x00, 0xcc},
+ {0x00, 0x1e, 0xc6, 0xaa}, {0x00, 0x20, 0x44, 0xaa},
+ {0x00, 0xad, 0x02, 0xaa}, {0x00, 0xae, 0x2c, 0xaa},
+ {0x00, 0x12, 0x08, 0xaa}, {0x00, 0x17, 0x41, 0xaa},
+ {0x00, 0x19, 0x41, 0xaa}, {0x00, 0x1e, 0x06, 0xaa},
+ {0x00, 0x21, 0x00, 0xaa}, {0x00, 0x36, 0xc0, 0xaa},
+ {0x00, 0x37, 0xc8, 0xaa}, {0x00, 0x3b, 0x36, 0xaa},
+ {0x00, 0x4b, 0xfe, 0xaa}, {0x00, 0x51, 0x1c, 0xaa},
+ {0x00, 0x52, 0x01, 0xaa}, {0x00, 0x55, 0x0a, 0xaa},
+ {0x00, 0x59, 0x02, 0xaa}, {0x00, 0x5a, 0x04, 0xaa},
+ {0x00, 0x5c, 0x10, 0xaa}, {0x00, 0x5d, 0x10, 0xaa},
+ {0x00, 0x5e, 0x10, 0xaa}, {0x00, 0x5f, 0x10, 0xaa},
+ {0x00, 0x61, 0x00, 0xaa}, {0x00, 0x62, 0x18, 0xaa},
+ {0x00, 0x63, 0x30, 0xaa}, {0x00, 0x70, 0x68, 0xaa},
+ {0x00, 0x80, 0x71, 0xaa}, {0x00, 0x81, 0x08, 0xaa},
+ {0x00, 0x82, 0x00, 0xaa}, {0x00, 0x83, 0x55, 0xaa},
+ {0x00, 0x84, 0x06, 0xaa}, {0x00, 0x85, 0x06, 0xaa},
+ {0x00, 0x86, 0x13, 0xaa}, {0x00, 0x87, 0x18, 0xaa},
+ {0x00, 0xaa, 0x3f, 0xaa}, {0x00, 0xab, 0x44, 0xaa},
+ {0x00, 0xb0, 0x68, 0xaa}, {0x00, 0xb5, 0x10, 0xaa},
+ {0x00, 0xb8, 0x20, 0xaa}, {0x00, 0xb9, 0xa0, 0xaa},
+ {0x00, 0xbc, 0x04, 0xaa}, {0x00, 0x8b, 0x40, 0xaa},
+ {0x00, 0x8c, 0x91, 0xaa}, {0x00, 0x8d, 0x8f, 0xaa},
+ {0x00, 0x8e, 0x91, 0xaa}, {0x00, 0x8f, 0x43, 0xaa},
+ {0x00, 0x90, 0x92, 0xaa}, {0x00, 0x91, 0x89, 0xaa},
+ {0x00, 0x92, 0x9d, 0xaa}, {0x00, 0x93, 0x46, 0xaa},
+ {0x00, 0xd6, 0x22, 0xaa}, {0x00, 0x73, 0x00, 0xaa},
+ {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa},
+ {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa},
+ {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa},
+ {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa},
+ {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa},
+ {0x00, 0x7e, 0xea, 0xaa}, {0x00, 0xd6, 0x62, 0xaa},
+ {0x00, 0x73, 0x00, 0xaa}, {0x00, 0x74, 0x10, 0xaa},
+ {0x00, 0x75, 0x20, 0xaa}, {0x00, 0x76, 0x2b, 0xaa},
+ {0x00, 0x77, 0x36, 0xaa}, {0x00, 0x78, 0x49, 0xaa},
+ {0x00, 0x79, 0x5a, 0xaa}, {0x00, 0x7a, 0x7f, 0xaa},
+ {0x00, 0x7b, 0x9b, 0xaa}, {0x00, 0x7c, 0xba, 0xaa},
+ {0x00, 0x7d, 0xd4, 0xaa}, {0x00, 0x7e, 0xea, 0xaa},
+ {0x00, 0xd6, 0xa2, 0xaa}, {0x00, 0x73, 0x00, 0xaa},
+ {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa},
+ {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa},
+ {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa},
+ {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa},
+ {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa},
+ {0x00, 0x7e, 0xea, 0xaa},
+ {0x00, 0x4c, 0x07, 0xaa},
+ {0x00, 0x4b, 0xe0, 0xaa}, {0x00, 0x4e, 0x77, 0xaa},
+ {0x00, 0x59, 0x02, 0xaa}, {0x00, 0x4d, 0x0a, 0xaa},
+/* {0x00, 0xd1, 0x00, 0xaa}, {0x00, 0x20, 0xc4, 0xaa},
+ {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc}, */
+ {0x00, 0xd1, 0x3c, 0xaa}, {0x00, 0x20, 0xc4, 0xaa},
+ {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc},
+ {0xb8, 0xfe, 0x00, 0xcc}, {0xb8, 0xff, 0x28, 0xcc},
+ {0xb9, 0x00, 0x28, 0xcc}, {0xb9, 0x01, 0x28, 0xcc},
+ {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc},
+ {0xb9, 0x04, 0x00, 0xcc}, {0xb9, 0x05, 0x3c, 0xcc},
+ {0xb9, 0x06, 0x3c, 0xcc}, {0xb9, 0x07, 0x3c, 0xcc},
+ {0xb9, 0x08, 0x3c, 0xcc}, {0x00, 0x05, 0x00, 0xaa},
+ {0xb3, 0x5c, 0x00, 0xcc}, {0xb3, 0x01, 0x41, 0xcc},
+ {}
+};
+static const u8 po3130_rundata[][4] = {
+ {0x00, 0x47, 0x45, 0xaa}, {0x00, 0x48, 0x9b, 0xaa},
+ {0x00, 0x49, 0x3a, 0xaa}, {0x00, 0x4a, 0x01, 0xaa},
+ {0x00, 0x44, 0x40, 0xaa},
+/* {0x00, 0xd5, 0x7c, 0xaa}, */
+ {0x00, 0xad, 0x04, 0xaa}, {0x00, 0xae, 0x00, 0xaa},
+ {0x00, 0xb0, 0x78, 0xaa}, {0x00, 0x98, 0x02, 0xaa},
+ {0x00, 0x94, 0x25, 0xaa}, {0x00, 0x95, 0x25, 0xaa},
+ {0x00, 0x59, 0x68, 0xaa}, {0x00, 0x44, 0x20, 0xaa},
+ {0x00, 0x17, 0x50, 0xaa}, {0x00, 0x19, 0x50, 0xaa},
+ {0x00, 0xd1, 0x3c, 0xaa}, {0x00, 0xd1, 0x3c, 0xaa},
+ {0x00, 0x1e, 0x06, 0xaa}, {0x00, 0x1e, 0x06, 0xaa},
+ {}
+};
+
+static const u8 po3130_initQVGA_data[][4] = {
+ {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc},
+ {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x09, 0xcc},
+ {0xb3, 0x00, 0x04, 0xcc}, {0xb3, 0x00, 0x24, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc}, {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x05, 0x00, 0xcc},
+ {0xb3, 0x06, 0x01, 0xcc}, {0xb3, 0x03, 0x1a, 0xcc},
+ {0xb3, 0x04, 0x15, 0xcc}, {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x01, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc}, {0xb8, 0x08, 0xe0, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x34, 0x01, 0xcc}, {0xb3, 0x35, 0xf6, 0xcc},
+ {0xb3, 0x00, 0x27, 0xcc}, {0xbc, 0x00, 0xd1, 0xcc},
+ {0xb8, 0x00, 0x21, 0xcc}, {0xb8, 0x27, 0x20, 0xcc},
+ {0xb8, 0x01, 0x79, 0xcc}, {0xb8, 0x81, 0x09, 0xcc},
+ {0xb8, 0x2c, 0x50, 0xcc}, {0xb8, 0x2d, 0xf8, 0xcc},
+ {0xb8, 0x2e, 0xf8, 0xcc}, {0xb8, 0x2f, 0xf8, 0xcc},
+ {0xb8, 0x30, 0x50, 0xcc}, {0xb8, 0x31, 0xf8, 0xcc},
+ {0xb8, 0x32, 0xf8, 0xcc}, {0xb8, 0x33, 0xf8, 0xcc},
+ {0xb8, 0x34, 0x50, 0xcc}, {0xb8, 0x35, 0x00, 0xcc},
+ {0xb8, 0x36, 0x00, 0xcc}, {0xb8, 0x37, 0x00, 0xcc},
+ {0x00, 0x1e, 0xc6, 0xaa}, {0x00, 0x20, 0x44, 0xaa},
+ {0x00, 0xad, 0x02, 0xaa}, {0x00, 0xae, 0x2c, 0xaa},
+ {0x00, 0x12, 0x08, 0xaa}, {0x00, 0x17, 0x41, 0xaa},
+ {0x00, 0x19, 0x41, 0xaa}, {0x00, 0x1e, 0x06, 0xaa},
+ {0x00, 0x21, 0x00, 0xaa}, {0x00, 0x36, 0xc0, 0xaa},
+ {0x00, 0x37, 0xc8, 0xaa}, {0x00, 0x3b, 0x36, 0xaa},
+ {0x00, 0x4b, 0xfe, 0xaa}, {0x00, 0x51, 0x1c, 0xaa},
+ {0x00, 0x52, 0x01, 0xaa}, {0x00, 0x55, 0x0a, 0xaa},
+ {0x00, 0x59, 0x6f, 0xaa}, {0x00, 0x5a, 0x04, 0xaa},
+ {0x00, 0x5c, 0x10, 0xaa}, {0x00, 0x5d, 0x10, 0xaa},
+ {0x00, 0x5e, 0x10, 0xaa}, {0x00, 0x5f, 0x10, 0xaa},
+ {0x00, 0x61, 0x00, 0xaa}, {0x00, 0x62, 0x18, 0xaa},
+ {0x00, 0x63, 0x30, 0xaa}, {0x00, 0x70, 0x68, 0xaa},
+ {0x00, 0x80, 0x71, 0xaa}, {0x00, 0x81, 0x08, 0xaa},
+ {0x00, 0x82, 0x00, 0xaa}, {0x00, 0x83, 0x55, 0xaa},
+ {0x00, 0x84, 0x06, 0xaa}, {0x00, 0x85, 0x06, 0xaa},
+ {0x00, 0x86, 0x13, 0xaa}, {0x00, 0x87, 0x18, 0xaa},
+ {0x00, 0xaa, 0x3f, 0xaa}, {0x00, 0xab, 0x44, 0xaa},
+ {0x00, 0xb0, 0x68, 0xaa}, {0x00, 0xb5, 0x10, 0xaa},
+ {0x00, 0xb8, 0x20, 0xaa}, {0x00, 0xb9, 0xa0, 0xaa},
+ {0x00, 0xbc, 0x04, 0xaa}, {0x00, 0x8b, 0x40, 0xaa},
+ {0x00, 0x8c, 0x91, 0xaa}, {0x00, 0x8d, 0x8f, 0xaa},
+ {0x00, 0x8e, 0x91, 0xaa}, {0x00, 0x8f, 0x43, 0xaa},
+ {0x00, 0x90, 0x92, 0xaa}, {0x00, 0x91, 0x89, 0xaa},
+ {0x00, 0x92, 0x9d, 0xaa}, {0x00, 0x93, 0x46, 0xaa},
+ {0x00, 0xd6, 0x22, 0xaa}, {0x00, 0x73, 0x00, 0xaa},
+ {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa},
+ {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa},
+ {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa},
+ {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa},
+ {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa},
+ {0x00, 0x7e, 0xea, 0xaa}, {0x00, 0xd6, 0x62, 0xaa},
+ {0x00, 0x73, 0x00, 0xaa}, {0x00, 0x74, 0x10, 0xaa},
+ {0x00, 0x75, 0x20, 0xaa}, {0x00, 0x76, 0x2b, 0xaa},
+ {0x00, 0x77, 0x36, 0xaa}, {0x00, 0x78, 0x49, 0xaa},
+ {0x00, 0x79, 0x5a, 0xaa}, {0x00, 0x7a, 0x7f, 0xaa},
+ {0x00, 0x7b, 0x9b, 0xaa}, {0x00, 0x7c, 0xba, 0xaa},
+ {0x00, 0x7d, 0xd4, 0xaa}, {0x00, 0x7e, 0xea, 0xaa},
+ {0x00, 0xd6, 0xa2, 0xaa}, {0x00, 0x73, 0x00, 0xaa},
+ {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa},
+ {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa},
+ {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa},
+ {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa},
+ {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa},
+ {0x00, 0x7e, 0xea, 0xaa}, {0x00, 0x4c, 0x07, 0xaa},
+ {0x00, 0x4b, 0xe0, 0xaa}, {0x00, 0x4e, 0x77, 0xaa},
+ {0x00, 0x59, 0x66, 0xaa}, {0x00, 0x4d, 0x0a, 0xaa},
+ {0x00, 0xd1, 0x00, 0xaa}, {0x00, 0x20, 0xc4, 0xaa},
+ {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc},
+ {0xb8, 0xfe, 0x00, 0xcc}, {0xb8, 0xff, 0x28, 0xcc},
+ {0xb9, 0x00, 0x28, 0xcc}, {0xb9, 0x01, 0x28, 0xcc},
+ {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc},
+ {0xb9, 0x04, 0x00, 0xcc}, {0xb9, 0x05, 0x3c, 0xcc},
+ {0xb9, 0x06, 0x3c, 0xcc}, {0xb9, 0x07, 0x3c, 0xcc},
+ {0xb9, 0x08, 0x3c, 0xcc}, {0xbc, 0x02, 0x18, 0xcc},
+ {0xbc, 0x03, 0x50, 0xcc}, {0xbc, 0x04, 0x18, 0xcc},
+ {0xbc, 0x05, 0x00, 0xcc}, {0xbc, 0x06, 0x00, 0xcc},
+ {0xbc, 0x08, 0x30, 0xcc}, {0xbc, 0x09, 0x40, 0xcc},
+ {0xbc, 0x0a, 0x10, 0xcc}, {0xbc, 0x0b, 0x00, 0xcc},
+ {0xbc, 0x0c, 0x00, 0xcc}, {0x00, 0x05, 0x00, 0xaa},
+ {0xb3, 0x5c, 0x00, 0xcc}, {0xb3, 0x01, 0x41, 0xcc},
+ {}
+};
+
+static const u8 hv7131r_gamma[17] = {
+ 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
+ 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff
+};
+static const u8 hv7131r_matrix[9] = {
+ 0x5f, 0xec, 0xf5, 0xf1, 0x5a, 0xf5, 0xf1, 0xec, 0x63
+};
+static const u8 hv7131r_initVGA_data[][4] = {
+ {0xb3, 0x01, 0x01, 0xcc},
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0x00, 0x00, 0x20, 0xdd},
+ {0xb3, 0x00, 0x24, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x03, 0xcc},
+ {0xb3, 0x01, 0x45, 0xcc},
+ {0xb3, 0x03, 0x0b, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x01, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x02, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc},
+ {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x34, 0x01, 0xcc},
+ {0xb3, 0x35, 0x91, 0xcc}, /* i2c add: 11 */
+ {0xb3, 0x00, 0x27, 0xcc},
+ {0xbc, 0x00, 0x73, 0xcc},
+ {0xb8, 0x00, 0x23, 0xcc},
+ {0xb8, 0x2c, 0x50, 0xcc},
+ {0xb8, 0x2d, 0xf8, 0xcc},
+ {0xb8, 0x2e, 0xf8, 0xcc},
+ {0xb8, 0x2f, 0xf8, 0xcc},
+ {0xb8, 0x30, 0x50, 0xcc},
+ {0xb8, 0x31, 0xf8, 0xcc},
+ {0xb8, 0x32, 0xf8, 0xcc},
+ {0xb8, 0x33, 0xf8, 0xcc},
+ {0xb8, 0x34, 0x58, 0xcc},
+ {0xb8, 0x35, 0x00, 0xcc},
+ {0xb8, 0x36, 0x00, 0xcc},
+ {0xb8, 0x37, 0x00, 0xcc},
+ {0xb8, 0x27, 0x20, 0xcc},
+ {0xb8, 0x01, 0x7d, 0xcc},
+ {0xb8, 0x81, 0x09, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0xb8, 0x8e, 0x00, 0xcc},
+ {0xb8, 0x8f, 0xff, 0xcc},
+ {0x00, 0x01, 0x0c, 0xaa},
+ {0x00, 0x14, 0x01, 0xaa},
+ {0x00, 0x15, 0xe6, 0xaa},
+ {0x00, 0x16, 0x02, 0xaa},
+ {0x00, 0x17, 0x86, 0xaa},
+ {0x00, 0x23, 0x00, 0xaa},
+ {0x00, 0x25, 0x03, 0xaa},
+ {0x00, 0x26, 0xa9, 0xaa},
+ {0x00, 0x27, 0x80, 0xaa},
+ {0x00, 0x30, 0x18, 0xaa},
+ {0xb6, 0x00, 0x00, 0xcc},
+ {0xb6, 0x03, 0x02, 0xcc},
+ {0xb6, 0x02, 0x80, 0xcc},
+ {0xb6, 0x05, 0x01, 0xcc},
+ {0xb6, 0x04, 0xe0, 0xcc},
+ {0xb6, 0x12, 0x78, 0xcc},
+ {0xb6, 0x18, 0x02, 0xcc},
+ {0xb6, 0x17, 0x58, 0xcc},
+ {0xb6, 0x16, 0x00, 0xcc},
+ {0xb6, 0x22, 0x12, 0xcc},
+ {0xb6, 0x23, 0x0b, 0xcc},
+ {0xb3, 0x02, 0x02, 0xcc},
+ {0xbf, 0xc0, 0x39, 0xcc},
+ {0xbf, 0xc1, 0x04, 0xcc},
+ {0xbf, 0xcc, 0x10, 0xcc},
+ {0xb6, 0x12, 0xf8, 0xcc},
+ {0xb6, 0x13, 0x13, 0xcc},
+ {0xb9, 0x12, 0x00, 0xcc},
+ {0xb9, 0x13, 0x0a, 0xcc},
+ {0xb9, 0x14, 0x0a, 0xcc},
+ {0xb9, 0x15, 0x0a, 0xcc},
+ {0xb9, 0x16, 0x0a, 0xcc},
+ {0xb8, 0x0c, 0x20, 0xcc},
+ {0xb8, 0x0d, 0x70, 0xcc},
+ {0xb9, 0x18, 0x00, 0xcc},
+ {0xb9, 0x19, 0x0f, 0xcc},
+ {0xb9, 0x1a, 0x0f, 0xcc},
+ {0xb9, 0x1b, 0x0f, 0xcc},
+ {0xb9, 0x1c, 0x0f, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {}
+};
+
+static const u8 hv7131r_initQVGA_data[][4] = {
+ {0xb3, 0x01, 0x01, 0xcc},
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0x00, 0x00, 0x20, 0xdd},
+ {0xb3, 0x00, 0x24, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x03, 0xcc},
+ {0xb3, 0x01, 0x45, 0xcc},
+ {0xb3, 0x03, 0x0b, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x01, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x02, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc},
+ {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x34, 0x01, 0xcc},
+ {0xb3, 0x35, 0x91, 0xcc},
+ {0xb3, 0x00, 0x27, 0xcc},
+ {0xbc, 0x00, 0xd3, 0xcc},
+ {0xb8, 0x00, 0x23, 0xcc},
+ {0xb8, 0x2c, 0x50, 0xcc},
+ {0xb8, 0x2d, 0xf8, 0xcc},
+ {0xb8, 0x2e, 0xf8, 0xcc},
+ {0xb8, 0x2f, 0xf8, 0xcc},
+ {0xb8, 0x30, 0x50, 0xcc},
+ {0xb8, 0x31, 0xf8, 0xcc},
+ {0xb8, 0x32, 0xf8, 0xcc},
+ {0xb8, 0x33, 0xf8, 0xcc},
+ {0xb8, 0x34, 0x58, 0xcc},
+ {0xb8, 0x35, 0x00, 0xcc},
+ {0xb8, 0x36, 0x00, 0xcc},
+ {0xb8, 0x37, 0x00, 0xcc},
+ {0xb8, 0x27, 0x20, 0xcc},
+ {0xb8, 0x01, 0x7d, 0xcc},
+ {0xb8, 0x81, 0x09, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0xb8, 0x8e, 0x00, 0xcc},
+ {0xb8, 0x8f, 0xff, 0xcc},
+ {0x00, 0x01, 0x0c, 0xaa},
+ {0x00, 0x14, 0x01, 0xaa},
+ {0x00, 0x15, 0xe6, 0xaa},
+ {0x00, 0x16, 0x02, 0xaa},
+ {0x00, 0x17, 0x86, 0xaa},
+ {0x00, 0x23, 0x00, 0xaa},
+ {0x00, 0x25, 0x03, 0xaa},
+ {0x00, 0x26, 0xa9, 0xaa},
+ {0x00, 0x27, 0x80, 0xaa},
+ {0x00, 0x30, 0x18, 0xaa},
+ {0xb6, 0x00, 0x00, 0xcc},
+ {0xb6, 0x03, 0x01, 0xcc},
+ {0xb6, 0x02, 0x40, 0xcc},
+ {0xb6, 0x05, 0x00, 0xcc},
+ {0xb6, 0x04, 0xf0, 0xcc},
+ {0xb6, 0x12, 0x78, 0xcc},
+ {0xb6, 0x18, 0x00, 0xcc},
+ {0xb6, 0x17, 0x96, 0xcc},
+ {0xb6, 0x16, 0x00, 0xcc},
+ {0xb6, 0x22, 0x12, 0xcc},
+ {0xb6, 0x23, 0x0b, 0xcc},
+ {0xb3, 0x02, 0x02, 0xcc},
+ {0xbf, 0xc0, 0x39, 0xcc},
+ {0xbf, 0xc1, 0x04, 0xcc},
+ {0xbf, 0xcc, 0x10, 0xcc},
+ {0xbc, 0x02, 0x18, 0xcc},
+ {0xbc, 0x03, 0x50, 0xcc},
+ {0xbc, 0x04, 0x18, 0xcc},
+ {0xbc, 0x05, 0x00, 0xcc},
+ {0xbc, 0x06, 0x00, 0xcc},
+ {0xbc, 0x08, 0x30, 0xcc},
+ {0xbc, 0x09, 0x40, 0xcc},
+ {0xbc, 0x0a, 0x10, 0xcc},
+ {0xbc, 0x0b, 0x00, 0xcc},
+ {0xbc, 0x0c, 0x00, 0xcc},
+ {0xb9, 0x12, 0x00, 0xcc},
+ {0xb9, 0x13, 0x0a, 0xcc},
+ {0xb9, 0x14, 0x0a, 0xcc},
+ {0xb9, 0x15, 0x0a, 0xcc},
+ {0xb9, 0x16, 0x0a, 0xcc},
+ {0xb9, 0x18, 0x00, 0xcc},
+ {0xb9, 0x19, 0x0f, 0xcc},
+ {0xb8, 0x0c, 0x20, 0xcc},
+ {0xb8, 0x0d, 0x70, 0xcc},
+ {0xb9, 0x1a, 0x0f, 0xcc},
+ {0xb9, 0x1b, 0x0f, 0xcc},
+ {0xb9, 0x1c, 0x0f, 0xcc},
+ {0xb6, 0x12, 0xf8, 0xcc},
+ {0xb6, 0x13, 0x13, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {}
+};
+
+static const u8 ov7660_gamma[17] = {
+ 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
+ 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff
+};
+static const u8 ov7660_matrix[9] = {
+ 0x5a, 0xf0, 0xf6, 0xf3, 0x57, 0xf6, 0xf3, 0xef, 0x62
+};
+static const u8 ov7660_initVGA_data[][4] = {
+ {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc},
+ {0x00, 0x00, 0x50, 0xdd},
+ {0xb0, 0x03, 0x01, 0xcc},
+ {0xb3, 0x00, 0x21, 0xcc}, {0xb3, 0x00, 0x26, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x03, 0xcc},
+ {0xb3, 0x03, 0x1f, 0xcc}, {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x05, 0x00, 0xcc},
+ {0xb3, 0x06, 0x01, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},/* 0xb315 <-0 href startl */
+ {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc}, {0xb3, 0x1d, 0x01, 0xcc},
+ {0xb3, 0x1f, 0x02, 0xcc},
+ {0xb3, 0x34, 0x01, 0xcc},
+ {0xb3, 0x35, 0xa1, 0xcc}, /* i2c add: 21 */
+ {0xb3, 0x00, 0x26, 0xcc},
+ {0xb8, 0x00, 0x33, 0xcc}, /* 13 */
+ {0xb8, 0x01, 0x7d, 0xcc},
+ {0xbc, 0x00, 0x73, 0xcc}, {0xb8, 0x81, 0x09, 0xcc},
+ {0xb8, 0x27, 0x20, 0xcc},
+ {0xb8, 0x8f, 0x50, 0xcc},
+ {0x00, 0x01, 0x80, 0xaa}, {0x00, 0x02, 0x80, 0xaa},
+ {0x00, 0x12, 0x80, 0xaa},
+ {0x00, 0x12, 0x05, 0xaa},
+ {0x00, 0x1e, 0x01, 0xaa}, /* MVFP */
+ {0x00, 0x3d, 0x40, 0xaa}, /* 0x3d <-40 gamma 01 */
+ {0x00, 0x41, 0x00, 0xaa}, /* edge 00 */
+ {0x00, 0x0d, 0x48, 0xaa}, {0x00, 0x0e, 0x04, 0xaa},
+ {0x00, 0x13, 0xa7, 0xaa},
+ {0x00, 0x40, 0xc1, 0xaa}, {0x00, 0x35, 0x00, 0xaa},
+ {0x00, 0x36, 0x00, 0xaa},
+ {0x00, 0x3c, 0x68, 0xaa}, {0x00, 0x1b, 0x05, 0xaa},
+ {0x00, 0x39, 0x43, 0xaa},
+ {0x00, 0x8d, 0xcf, 0xaa},
+ {0x00, 0x8b, 0xcc, 0xaa}, {0x00, 0x8c, 0xcc, 0xaa},
+ {0x00, 0x0f, 0x62, 0xaa},
+ {0x00, 0x35, 0x84, 0xaa},
+ {0x00, 0x3b, 0x08, 0xaa}, /* 0 * Nightframe 1/4 + 50Hz -> 0xC8 */
+ {0x00, 0x3a, 0x00, 0xaa}, /* mx change yuyv format 00, 04, 01; 08, 0c*/
+ {0x00, 0x14, 0x2a, 0xaa}, /* agc ampli */
+ {0x00, 0x9e, 0x40, 0xaa}, {0xb8, 0x8f, 0x50, 0xcc},
+ {0x00, 0x01, 0x80, 0xaa},
+ {0x00, 0x02, 0x80, 0xaa},
+ {0xb8, 0xfe, 0x00, 0xcc}, {0xb8, 0xff, 0x28, 0xcc},
+ {0xb9, 0x00, 0x28, 0xcc},
+ {0xb9, 0x01, 0x28, 0xcc}, {0xb9, 0x02, 0x28, 0xcc},
+ {0xb9, 0x03, 0x00, 0xcc},
+ {0xb9, 0x04, 0x00, 0xcc},
+ {0xb9, 0x05, 0x3c, 0xcc}, {0xb9, 0x06, 0x3c, 0xcc},
+ {0xb9, 0x07, 0x3c, 0xcc},
+ {0xb9, 0x08, 0x3c, 0xcc},
+
+ {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc},
+
+ {0x00, 0x29, 0x3c, 0xaa}, {0xb3, 0x01, 0x45, 0xcc},
+ {}
+};
+static const u8 ov7660_initQVGA_data[][4] = {
+ {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc},
+ {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc},
+ {0xb3, 0x00, 0x21, 0xcc}, {0xb3, 0x00, 0x26, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc}, {0xb3, 0x06, 0x03, 0xcc},
+ {0xb3, 0x03, 0x1f, 0xcc}, {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x05, 0x00, 0xcc}, {0xb3, 0x06, 0x01, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},/* 0xb315 <-0 href startl */
+ {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc}, {0xb3, 0x1d, 0x01, 0xcc},
+ {0xb3, 0x1f, 0x02, 0xcc}, {0xb3, 0x34, 0x01, 0xcc},
+ {0xb3, 0x35, 0xa1, 0xcc}, {0xb3, 0x00, 0x26, 0xcc},
+ {0xb8, 0x00, 0x33, 0xcc}, /* 13 */
+ {0xb8, 0x01, 0x7d, 0xcc},
+/* sizer */
+ {0xbc, 0x00, 0xd3, 0xcc},
+ {0xb8, 0x81, 0x09, 0xcc}, {0xb8, 0x81, 0x09, 0xcc},
+ {0xb8, 0x27, 0x20, 0xcc}, {0xb8, 0x8f, 0x50, 0xcc},
+ {0x00, 0x01, 0x80, 0xaa}, {0x00, 0x02, 0x80, 0xaa},
+ {0x00, 0x12, 0x80, 0xaa}, {0x00, 0x12, 0x05, 0xaa},
+ {0x00, 0x1e, 0x01, 0xaa}, /* MVFP */
+ {0x00, 0x3d, 0x40, 0xaa}, /* 0x3d <-40 gamma 01 */
+ {0x00, 0x41, 0x00, 0xaa}, /* edge 00 */
+ {0x00, 0x0d, 0x48, 0xaa}, {0x00, 0x0e, 0x04, 0xaa},
+ {0x00, 0x13, 0xa7, 0xaa},
+ {0x00, 0x40, 0xc1, 0xaa}, {0x00, 0x35, 0x00, 0xaa},
+ {0x00, 0x36, 0x00, 0xaa},
+ {0x00, 0x3c, 0x68, 0xaa}, {0x00, 0x1b, 0x05, 0xaa},
+ {0x00, 0x39, 0x43, 0xaa}, {0x00, 0x8d, 0xcf, 0xaa},
+ {0x00, 0x8b, 0xcc, 0xaa}, {0x00, 0x8c, 0xcc, 0xaa},
+ {0x00, 0x0f, 0x62, 0xaa}, {0x00, 0x35, 0x84, 0xaa},
+ {0x00, 0x3b, 0x08, 0xaa}, /* 0 * Nightframe 1/4 + 50Hz -> 0xC8 */
+ {0x00, 0x3a, 0x00, 0xaa}, /* mx change yuyv format 00, 04, 01; 08, 0c*/
+ {0x00, 0x14, 0x2a, 0xaa}, /* agc ampli */
+ {0x00, 0x9e, 0x40, 0xaa}, {0xb8, 0x8f, 0x50, 0xcc},
+ {0x00, 0x01, 0x80, 0xaa},
+ {0x00, 0x02, 0x80, 0xaa},
+/* sizer filters */
+ {0xbc, 0x02, 0x08, 0xcc},
+ {0xbc, 0x03, 0x70, 0xcc},
+ {0xb8, 0x35, 0x00, 0xcc},
+ {0xb8, 0x36, 0x00, 0xcc},
+ {0xb8, 0x37, 0x00, 0xcc},
+ {0xbc, 0x04, 0x08, 0xcc},
+ {0xbc, 0x05, 0x00, 0xcc},
+ {0xbc, 0x06, 0x00, 0xcc},
+ {0xbc, 0x08, 0x3c, 0xcc},
+ {0xbc, 0x09, 0x40, 0xcc},
+ {0xbc, 0x0a, 0x04, 0xcc},
+ {0xbc, 0x0b, 0x00, 0xcc},
+ {0xbc, 0x0c, 0x00, 0xcc},
+/* */
+ {0xb8, 0xfe, 0x00, 0xcc},
+ {0xb8, 0xff, 0x28, 0xcc},
+/* */
+ {0xb9, 0x00, 0x28, 0xcc}, {0xb9, 0x01, 0x28, 0xcc},
+ {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc},
+ {0xb9, 0x04, 0x00, 0xcc}, {0xb9, 0x05, 0x3c, 0xcc},
+ {0xb9, 0x06, 0x3c, 0xcc}, {0xb9, 0x07, 0x3c, 0xcc},
+ {0xb9, 0x08, 0x3c, 0xcc},
+/* */
+ {0xb8, 0x8e, 0x00, 0xcc},
+ {0xb8, 0x8f, 0xff, 0xcc}, /* ff */
+ {0x00, 0x29, 0x3c, 0xaa},
+ {0xb3, 0x01, 0x45, 0xcc}, /* 45 */
+ {}
+};
+
+static const u8 ov7660_50HZ[][4] = {
+ {0x00, 0x3b, 0x08, 0xaa},
+ {0x00, 0x9d, 0x40, 0xaa},
+ {0x00, 0x13, 0xa7, 0xaa},
+ {}
+};
+
+static const u8 ov7660_60HZ[][4] = {
+ {0x00, 0x3b, 0x00, 0xaa},
+ {0x00, 0x9e, 0x40, 0xaa},
+ {0x00, 0x13, 0xa7, 0xaa},
+ {}
+};
+
+static const u8 ov7660_NoFliker[][4] = {
+ {0x00, 0x13, 0x87, 0xaa},
+ {}
+};
+
+static const u8 ov7670_InitVGA[][4] = {
+ {0xb3, 0x01, 0x05, 0xcc},
+ {0x00, 0x00, 0x30, 0xdd},
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0x00, 0x00, 0x10, 0xdd},
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0x00, 0x00, 0x10, 0xdd},
+ {0xb3, 0x00, 0x66, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xb0, 0x16, 0x01, 0xcc},
+ {0xb3, 0x35, 0xa1, 0xcc}, /* i2c add: 21 */
+ {0xb3, 0x34, 0x01, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x01, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x02, 0x02, 0xcc},
+ {0xb3, 0x03, 0x1f, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc},
+ {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x01, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc},
+ {0xbc, 0x00, 0x41, 0xcc},
+ {0xbc, 0x01, 0x01, 0xcc},
+ {0x00, 0x12, 0x80, 0xaa},
+ {0x00, 0x00, 0x20, 0xdd},
+ {0x00, 0x12, 0x00, 0xaa},
+ {0x00, 0x11, 0x40, 0xaa},
+ {0x00, 0x6b, 0x0a, 0xaa},
+ {0x00, 0x3a, 0x04, 0xaa},
+ {0x00, 0x40, 0xc0, 0xaa},
+ {0x00, 0x8c, 0x00, 0xaa},
+ {0x00, 0x7a, 0x29, 0xaa},
+ {0x00, 0x7b, 0x0e, 0xaa},
+ {0x00, 0x7c, 0x1a, 0xaa},
+ {0x00, 0x7d, 0x31, 0xaa},
+ {0x00, 0x7e, 0x53, 0xaa},
+ {0x00, 0x7f, 0x60, 0xaa},
+ {0x00, 0x80, 0x6b, 0xaa},
+ {0x00, 0x81, 0x73, 0xaa},
+ {0x00, 0x82, 0x7b, 0xaa},
+ {0x00, 0x83, 0x82, 0xaa},
+ {0x00, 0x84, 0x89, 0xaa},
+ {0x00, 0x85, 0x96, 0xaa},
+ {0x00, 0x86, 0xa1, 0xaa},
+ {0x00, 0x87, 0xb7, 0xaa},
+ {0x00, 0x88, 0xcc, 0xaa},
+ {0x00, 0x89, 0xe1, 0xaa},
+ {0x00, 0x13, 0xe0, 0xaa},
+ {0x00, 0x00, 0x00, 0xaa},
+ {0x00, 0x10, 0x00, 0xaa},
+ {0x00, 0x0d, 0x40, 0xaa},
+ {0x00, 0x14, 0x28, 0xaa},
+ {0x00, 0xa5, 0x05, 0xaa},
+ {0x00, 0xab, 0x07, 0xaa},
+ {0x00, 0x24, 0x95, 0xaa},
+ {0x00, 0x25, 0x33, 0xaa},
+ {0x00, 0x26, 0xe3, 0xaa},
+ {0x00, 0x9f, 0x88, 0xaa},
+ {0x00, 0xa0, 0x78, 0xaa},
+ {0x00, 0x55, 0x90, 0xaa},
+ {0x00, 0xa1, 0x03, 0xaa},
+ {0x00, 0xa6, 0xe0, 0xaa},
+ {0x00, 0xa7, 0xd8, 0xaa},
+ {0x00, 0xa8, 0xf0, 0xaa},
+ {0x00, 0xa9, 0x90, 0xaa},
+ {0x00, 0xaa, 0x14, 0xaa},
+ {0x00, 0x13, 0xe5, 0xaa},
+ {0x00, 0x0e, 0x61, 0xaa},
+ {0x00, 0x0f, 0x4b, 0xaa},
+ {0x00, 0x16, 0x02, 0xaa},
+ {0x00, 0x1e, 0x07, 0xaa}, /* MVFP */
+ {0x00, 0x21, 0x02, 0xaa},
+ {0x00, 0x22, 0x91, 0xaa},
+ {0x00, 0x29, 0x07, 0xaa},
+ {0x00, 0x33, 0x0b, 0xaa},
+ {0x00, 0x35, 0x0b, 0xaa},
+ {0x00, 0x37, 0x1d, 0xaa},
+ {0x00, 0x38, 0x71, 0xaa},
+ {0x00, 0x39, 0x2a, 0xaa},
+ {0x00, 0x3c, 0x78, 0xaa},
+ {0x00, 0x4d, 0x40, 0xaa},
+ {0x00, 0x4e, 0x20, 0xaa},
+ {0x00, 0x74, 0x19, 0xaa},
+ {0x00, 0x8d, 0x4f, 0xaa},
+ {0x00, 0x8e, 0x00, 0xaa},
+ {0x00, 0x8f, 0x00, 0xaa},
+ {0x00, 0x90, 0x00, 0xaa},
+ {0x00, 0x91, 0x00, 0xaa},
+ {0x00, 0x96, 0x00, 0xaa},
+ {0x00, 0x9a, 0x80, 0xaa},
+ {0x00, 0xb0, 0x84, 0xaa},
+ {0x00, 0xb1, 0x0c, 0xaa},
+ {0x00, 0xb2, 0x0e, 0xaa},
+ {0x00, 0xb3, 0x82, 0xaa},
+ {0x00, 0xb8, 0x0a, 0xaa},
+ {0x00, 0x43, 0x14, 0xaa},
+ {0x00, 0x44, 0xf0, 0xaa},
+ {0x00, 0x45, 0x45, 0xaa},
+ {0x00, 0x46, 0x63, 0xaa},
+ {0x00, 0x47, 0x2d, 0xaa},
+ {0x00, 0x48, 0x46, 0xaa},
+ {0x00, 0x59, 0x88, 0xaa},
+ {0x00, 0x5a, 0xa0, 0xaa},
+ {0x00, 0x5b, 0xc6, 0xaa},
+ {0x00, 0x5c, 0x7d, 0xaa},
+ {0x00, 0x5d, 0x5f, 0xaa},
+ {0x00, 0x5e, 0x19, 0xaa},
+ {0x00, 0x6c, 0x0a, 0xaa},
+ {0x00, 0x6d, 0x55, 0xaa},
+ {0x00, 0x6e, 0x11, 0xaa},
+ {0x00, 0x6f, 0x9e, 0xaa},
+ {0x00, 0x69, 0x00, 0xaa},
+ {0x00, 0x6a, 0x40, 0xaa},
+ {0x00, 0x01, 0x40, 0xaa},
+ {0x00, 0x02, 0x40, 0xaa},
+ {0x00, 0x13, 0xe7, 0xaa},
+ {0x00, 0x5f, 0xf0, 0xaa},
+ {0x00, 0x60, 0xf0, 0xaa},
+ {0x00, 0x61, 0xf0, 0xaa},
+ {0x00, 0x27, 0xa0, 0xaa},
+ {0x00, 0x28, 0x80, 0xaa},
+ {0x00, 0x2c, 0x90, 0xaa},
+ {0x00, 0x4f, 0x66, 0xaa},
+ {0x00, 0x50, 0x66, 0xaa},
+ {0x00, 0x51, 0x00, 0xaa},
+ {0x00, 0x52, 0x22, 0xaa},
+ {0x00, 0x53, 0x5e, 0xaa},
+ {0x00, 0x54, 0x80, 0xaa},
+ {0x00, 0x58, 0x9e, 0xaa},
+ {0x00, 0x41, 0x08, 0xaa},
+ {0x00, 0x3f, 0x00, 0xaa},
+ {0x00, 0x75, 0x85, 0xaa},
+ {0x00, 0x76, 0xe1, 0xaa},
+ {0x00, 0x4c, 0x00, 0xaa},
+ {0x00, 0x77, 0x0a, 0xaa},
+ {0x00, 0x3d, 0x88, 0xaa},
+ {0x00, 0x4b, 0x09, 0xaa},
+ {0x00, 0xc9, 0x60, 0xaa},
+ {0x00, 0x41, 0x38, 0xaa},
+ {0x00, 0x62, 0x30, 0xaa},
+ {0x00, 0x63, 0x30, 0xaa},
+ {0x00, 0x64, 0x08, 0xaa},
+ {0x00, 0x94, 0x07, 0xaa},
+ {0x00, 0x95, 0x0b, 0xaa},
+ {0x00, 0x65, 0x00, 0xaa},
+ {0x00, 0x66, 0x05, 0xaa},
+ {0x00, 0x56, 0x50, 0xaa},
+ {0x00, 0x34, 0x11, 0xaa},
+ {0x00, 0xa4, 0x88, 0xaa},
+ {0x00, 0x96, 0x00, 0xaa},
+ {0x00, 0x97, 0x30, 0xaa},
+ {0x00, 0x98, 0x20, 0xaa},
+ {0x00, 0x99, 0x30, 0xaa},
+ {0x00, 0x9a, 0x84, 0xaa},
+ {0x00, 0x9b, 0x29, 0xaa},
+ {0x00, 0x9c, 0x03, 0xaa},
+ {0x00, 0x78, 0x04, 0xaa},
+ {0x00, 0x79, 0x01, 0xaa},
+ {0x00, 0xc8, 0xf0, 0xaa},
+ {0x00, 0x79, 0x0f, 0xaa},
+ {0x00, 0xc8, 0x00, 0xaa},
+ {0x00, 0x79, 0x10, 0xaa},
+ {0x00, 0xc8, 0x7e, 0xaa},
+ {0x00, 0x79, 0x0a, 0xaa},
+ {0x00, 0xc8, 0x80, 0xaa},
+ {0x00, 0x79, 0x0b, 0xaa},
+ {0x00, 0xc8, 0x01, 0xaa},
+ {0x00, 0x79, 0x0c, 0xaa},
+ {0x00, 0xc8, 0x0f, 0xaa},
+ {0x00, 0x79, 0x0d, 0xaa},
+ {0x00, 0xc8, 0x20, 0xaa},
+ {0x00, 0x79, 0x09, 0xaa},
+ {0x00, 0xc8, 0x80, 0xaa},
+ {0x00, 0x79, 0x02, 0xaa},
+ {0x00, 0xc8, 0xc0, 0xaa},
+ {0x00, 0x79, 0x03, 0xaa},
+ {0x00, 0xc8, 0x40, 0xaa},
+ {0x00, 0x79, 0x05, 0xaa},
+ {0x00, 0xc8, 0x30, 0xaa},
+ {0x00, 0x79, 0x26, 0xaa},
+ {0x00, 0x11, 0x40, 0xaa},
+ {0x00, 0x3a, 0x04, 0xaa},
+ {0x00, 0x12, 0x00, 0xaa},
+ {0x00, 0x40, 0xc0, 0xaa},
+ {0x00, 0x8c, 0x00, 0xaa},
+ {0x00, 0x17, 0x14, 0xaa},
+ {0x00, 0x18, 0x02, 0xaa},
+ {0x00, 0x32, 0x92, 0xaa},
+ {0x00, 0x19, 0x02, 0xaa},
+ {0x00, 0x1a, 0x7a, 0xaa},
+ {0x00, 0x03, 0x0a, 0xaa},
+ {0x00, 0x0c, 0x00, 0xaa},
+ {0x00, 0x3e, 0x00, 0xaa},
+ {0x00, 0x70, 0x3a, 0xaa},
+ {0x00, 0x71, 0x35, 0xaa},
+ {0x00, 0x72, 0x11, 0xaa},
+ {0x00, 0x73, 0xf0, 0xaa},
+ {0x00, 0xa2, 0x02, 0xaa},
+ {0x00, 0xb1, 0x00, 0xaa},
+ {0x00, 0xb1, 0x0c, 0xaa},
+ {0x00, 0x1e, 0x37, 0xaa}, /* MVFP */
+ {0x00, 0xaa, 0x14, 0xaa},
+ {0x00, 0x24, 0x80, 0xaa},
+ {0x00, 0x25, 0x74, 0xaa},
+ {0x00, 0x26, 0xd3, 0xaa},
+ {0x00, 0x0d, 0x00, 0xaa},
+ {0x00, 0x14, 0x18, 0xaa},
+ {0x00, 0x9d, 0x99, 0xaa},
+ {0x00, 0x9e, 0x7f, 0xaa},
+ {0x00, 0x64, 0x08, 0xaa},
+ {0x00, 0x94, 0x07, 0xaa},
+ {0x00, 0x95, 0x06, 0xaa},
+ {0x00, 0x66, 0x05, 0xaa},
+ {0x00, 0x41, 0x08, 0xaa},
+ {0x00, 0x3f, 0x00, 0xaa},
+ {0x00, 0x75, 0x07, 0xaa},
+ {0x00, 0x76, 0xe1, 0xaa},
+ {0x00, 0x4c, 0x00, 0xaa},
+ {0x00, 0x77, 0x00, 0xaa},
+ {0x00, 0x3d, 0xc2, 0xaa},
+ {0x00, 0x4b, 0x09, 0xaa},
+ {0x00, 0xc9, 0x60, 0xaa},
+ {0x00, 0x41, 0x38, 0xaa},
+ {0xbf, 0xc0, 0x26, 0xcc},
+ {0xbf, 0xc1, 0x02, 0xcc},
+ {0xbf, 0xcc, 0x04, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {0xb3, 0x01, 0x45, 0xcc},
+ {0x00, 0x77, 0x05, 0xaa},
+ {},
+};
+
+static const u8 ov7670_InitQVGA[][4] = {
+ {0xb3, 0x01, 0x05, 0xcc},
+ {0x00, 0x00, 0x30, 0xdd},
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0x00, 0x00, 0x10, 0xdd},
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0x00, 0x00, 0x10, 0xdd},
+ {0xb3, 0x00, 0x66, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xb0, 0x16, 0x01, 0xcc},
+ {0xb3, 0x35, 0xa1, 0xcc}, /* i2c add: 21 */
+ {0xb3, 0x34, 0x01, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x01, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x02, 0x02, 0xcc},
+ {0xb3, 0x03, 0x1f, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc},
+ {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x01, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc},
+ {0xbc, 0x00, 0xd1, 0xcc},
+ {0xbc, 0x01, 0x01, 0xcc},
+ {0x00, 0x12, 0x80, 0xaa},
+ {0x00, 0x00, 0x20, 0xdd},
+ {0x00, 0x12, 0x00, 0xaa},
+ {0x00, 0x11, 0x40, 0xaa},
+ {0x00, 0x6b, 0x0a, 0xaa},
+ {0x00, 0x3a, 0x04, 0xaa},
+ {0x00, 0x40, 0xc0, 0xaa},
+ {0x00, 0x8c, 0x00, 0xaa},
+ {0x00, 0x7a, 0x29, 0xaa},
+ {0x00, 0x7b, 0x0e, 0xaa},
+ {0x00, 0x7c, 0x1a, 0xaa},
+ {0x00, 0x7d, 0x31, 0xaa},
+ {0x00, 0x7e, 0x53, 0xaa},
+ {0x00, 0x7f, 0x60, 0xaa},
+ {0x00, 0x80, 0x6b, 0xaa},
+ {0x00, 0x81, 0x73, 0xaa},
+ {0x00, 0x82, 0x7b, 0xaa},
+ {0x00, 0x83, 0x82, 0xaa},
+ {0x00, 0x84, 0x89, 0xaa},
+ {0x00, 0x85, 0x96, 0xaa},
+ {0x00, 0x86, 0xa1, 0xaa},
+ {0x00, 0x87, 0xb7, 0xaa},
+ {0x00, 0x88, 0xcc, 0xaa},
+ {0x00, 0x89, 0xe1, 0xaa},
+ {0x00, 0x13, 0xe0, 0xaa},
+ {0x00, 0x00, 0x00, 0xaa},
+ {0x00, 0x10, 0x00, 0xaa},
+ {0x00, 0x0d, 0x40, 0xaa},
+ {0x00, 0x14, 0x28, 0xaa},
+ {0x00, 0xa5, 0x05, 0xaa},
+ {0x00, 0xab, 0x07, 0xaa},
+ {0x00, 0x24, 0x95, 0xaa},
+ {0x00, 0x25, 0x33, 0xaa},
+ {0x00, 0x26, 0xe3, 0xaa},
+ {0x00, 0x9f, 0x88, 0xaa},
+ {0x00, 0xa0, 0x78, 0xaa},
+ {0x00, 0x55, 0x90, 0xaa},
+ {0x00, 0xa1, 0x03, 0xaa},
+ {0x00, 0xa6, 0xe0, 0xaa},
+ {0x00, 0xa7, 0xd8, 0xaa},
+ {0x00, 0xa8, 0xf0, 0xaa},
+ {0x00, 0xa9, 0x90, 0xaa},
+ {0x00, 0xaa, 0x14, 0xaa},
+ {0x00, 0x13, 0xe5, 0xaa},
+ {0x00, 0x0e, 0x61, 0xaa},
+ {0x00, 0x0f, 0x4b, 0xaa},
+ {0x00, 0x16, 0x02, 0xaa},
+ {0x00, 0x1e, 0x07, 0xaa}, /* MVFP */
+ {0x00, 0x21, 0x02, 0xaa},
+ {0x00, 0x22, 0x91, 0xaa},
+ {0x00, 0x29, 0x07, 0xaa},
+ {0x00, 0x33, 0x0b, 0xaa},
+ {0x00, 0x35, 0x0b, 0xaa},
+ {0x00, 0x37, 0x1d, 0xaa},
+ {0x00, 0x38, 0x71, 0xaa},
+ {0x00, 0x39, 0x2a, 0xaa},
+ {0x00, 0x3c, 0x78, 0xaa},
+ {0x00, 0x4d, 0x40, 0xaa},
+ {0x00, 0x4e, 0x20, 0xaa},
+ {0x00, 0x74, 0x19, 0xaa},
+ {0x00, 0x8d, 0x4f, 0xaa},
+ {0x00, 0x8e, 0x00, 0xaa},
+ {0x00, 0x8f, 0x00, 0xaa},
+ {0x00, 0x90, 0x00, 0xaa},
+ {0x00, 0x91, 0x00, 0xaa},
+ {0x00, 0x96, 0x00, 0xaa},
+ {0x00, 0x9a, 0x80, 0xaa},
+ {0x00, 0xb0, 0x84, 0xaa},
+ {0x00, 0xb1, 0x0c, 0xaa},
+ {0x00, 0xb2, 0x0e, 0xaa},
+ {0x00, 0xb3, 0x82, 0xaa},
+ {0x00, 0xb8, 0x0a, 0xaa},
+ {0x00, 0x43, 0x14, 0xaa},
+ {0x00, 0x44, 0xf0, 0xaa},
+ {0x00, 0x45, 0x45, 0xaa},
+ {0x00, 0x46, 0x63, 0xaa},
+ {0x00, 0x47, 0x2d, 0xaa},
+ {0x00, 0x48, 0x46, 0xaa},
+ {0x00, 0x59, 0x88, 0xaa},
+ {0x00, 0x5a, 0xa0, 0xaa},
+ {0x00, 0x5b, 0xc6, 0xaa},
+ {0x00, 0x5c, 0x7d, 0xaa},
+ {0x00, 0x5d, 0x5f, 0xaa},
+ {0x00, 0x5e, 0x19, 0xaa},
+ {0x00, 0x6c, 0x0a, 0xaa},
+ {0x00, 0x6d, 0x55, 0xaa},
+ {0x00, 0x6e, 0x11, 0xaa},
+ {0x00, 0x6f, 0x9e, 0xaa},
+ {0x00, 0x69, 0x00, 0xaa},
+ {0x00, 0x6a, 0x40, 0xaa},
+ {0x00, 0x01, 0x40, 0xaa},
+ {0x00, 0x02, 0x40, 0xaa},
+ {0x00, 0x13, 0xe7, 0xaa},
+ {0x00, 0x5f, 0xf0, 0xaa},
+ {0x00, 0x60, 0xf0, 0xaa},
+ {0x00, 0x61, 0xf0, 0xaa},
+ {0x00, 0x27, 0xa0, 0xaa},
+ {0x00, 0x28, 0x80, 0xaa},
+ {0x00, 0x2c, 0x90, 0xaa},
+ {0x00, 0x4f, 0x66, 0xaa},
+ {0x00, 0x50, 0x66, 0xaa},
+ {0x00, 0x51, 0x00, 0xaa},
+ {0x00, 0x52, 0x22, 0xaa},
+ {0x00, 0x53, 0x5e, 0xaa},
+ {0x00, 0x54, 0x80, 0xaa},
+ {0x00, 0x58, 0x9e, 0xaa},
+ {0x00, 0x41, 0x08, 0xaa},
+ {0x00, 0x3f, 0x00, 0xaa},
+ {0x00, 0x75, 0x85, 0xaa},
+ {0x00, 0x76, 0xe1, 0xaa},
+ {0x00, 0x4c, 0x00, 0xaa},
+ {0x00, 0x77, 0x0a, 0xaa},
+ {0x00, 0x3d, 0x88, 0xaa},
+ {0x00, 0x4b, 0x09, 0xaa},
+ {0x00, 0xc9, 0x60, 0xaa},
+ {0x00, 0x41, 0x38, 0xaa},
+ {0x00, 0x62, 0x30, 0xaa},
+ {0x00, 0x63, 0x30, 0xaa},
+ {0x00, 0x64, 0x08, 0xaa},
+ {0x00, 0x94, 0x07, 0xaa},
+ {0x00, 0x95, 0x0b, 0xaa},
+ {0x00, 0x65, 0x00, 0xaa},
+ {0x00, 0x66, 0x05, 0xaa},
+ {0x00, 0x56, 0x50, 0xaa},
+ {0x00, 0x34, 0x11, 0xaa},
+ {0x00, 0xa4, 0x88, 0xaa},
+ {0x00, 0x96, 0x00, 0xaa},
+ {0x00, 0x97, 0x30, 0xaa},
+ {0x00, 0x98, 0x20, 0xaa},
+ {0x00, 0x99, 0x30, 0xaa},
+ {0x00, 0x9a, 0x84, 0xaa},
+ {0x00, 0x9b, 0x29, 0xaa},
+ {0x00, 0x9c, 0x03, 0xaa},
+ {0x00, 0x78, 0x04, 0xaa},
+ {0x00, 0x79, 0x01, 0xaa},
+ {0x00, 0xc8, 0xf0, 0xaa},
+ {0x00, 0x79, 0x0f, 0xaa},
+ {0x00, 0xc8, 0x00, 0xaa},
+ {0x00, 0x79, 0x10, 0xaa},
+ {0x00, 0xc8, 0x7e, 0xaa},
+ {0x00, 0x79, 0x0a, 0xaa},
+ {0x00, 0xc8, 0x80, 0xaa},
+ {0x00, 0x79, 0x0b, 0xaa},
+ {0x00, 0xc8, 0x01, 0xaa},
+ {0x00, 0x79, 0x0c, 0xaa},
+ {0x00, 0xc8, 0x0f, 0xaa},
+ {0x00, 0x79, 0x0d, 0xaa},
+ {0x00, 0xc8, 0x20, 0xaa},
+ {0x00, 0x79, 0x09, 0xaa},
+ {0x00, 0xc8, 0x80, 0xaa},
+ {0x00, 0x79, 0x02, 0xaa},
+ {0x00, 0xc8, 0xc0, 0xaa},
+ {0x00, 0x79, 0x03, 0xaa},
+ {0x00, 0xc8, 0x40, 0xaa},
+ {0x00, 0x79, 0x05, 0xaa},
+ {0x00, 0xc8, 0x30, 0xaa},
+ {0x00, 0x79, 0x26, 0xaa},
+ {0x00, 0x11, 0x40, 0xaa},
+ {0x00, 0x3a, 0x04, 0xaa},
+ {0x00, 0x12, 0x00, 0xaa},
+ {0x00, 0x40, 0xc0, 0xaa},
+ {0x00, 0x8c, 0x00, 0xaa},
+ {0x00, 0x17, 0x14, 0xaa},
+ {0x00, 0x18, 0x02, 0xaa},
+ {0x00, 0x32, 0x92, 0xaa},
+ {0x00, 0x19, 0x02, 0xaa},
+ {0x00, 0x1a, 0x7a, 0xaa},
+ {0x00, 0x03, 0x0a, 0xaa},
+ {0x00, 0x0c, 0x00, 0xaa},
+ {0x00, 0x3e, 0x00, 0xaa},
+ {0x00, 0x70, 0x3a, 0xaa},
+ {0x00, 0x71, 0x35, 0xaa},
+ {0x00, 0x72, 0x11, 0xaa},
+ {0x00, 0x73, 0xf0, 0xaa},
+ {0x00, 0xa2, 0x02, 0xaa},
+ {0x00, 0xb1, 0x00, 0xaa},
+ {0x00, 0xb1, 0x0c, 0xaa},
+ {0x00, 0x1e, 0x37, 0xaa}, /* MVFP */
+ {0x00, 0xaa, 0x14, 0xaa},
+ {0x00, 0x24, 0x80, 0xaa},
+ {0x00, 0x25, 0x74, 0xaa},
+ {0x00, 0x26, 0xd3, 0xaa},
+ {0x00, 0x0d, 0x00, 0xaa},
+ {0x00, 0x14, 0x18, 0xaa},
+ {0x00, 0x9d, 0x99, 0xaa},
+ {0x00, 0x9e, 0x7f, 0xaa},
+ {0x00, 0x64, 0x08, 0xaa},
+ {0x00, 0x94, 0x07, 0xaa},
+ {0x00, 0x95, 0x06, 0xaa},
+ {0x00, 0x66, 0x05, 0xaa},
+ {0x00, 0x41, 0x08, 0xaa},
+ {0x00, 0x3f, 0x00, 0xaa},
+ {0x00, 0x75, 0x07, 0xaa},
+ {0x00, 0x76, 0xe1, 0xaa},
+ {0x00, 0x4c, 0x00, 0xaa},
+ {0x00, 0x77, 0x00, 0xaa},
+ {0x00, 0x3d, 0xc2, 0xaa},
+ {0x00, 0x4b, 0x09, 0xaa},
+ {0x00, 0xc9, 0x60, 0xaa},
+ {0x00, 0x41, 0x38, 0xaa},
+ {0xbc, 0x02, 0x18, 0xcc},
+ {0xbc, 0x03, 0x50, 0xcc},
+ {0xbc, 0x04, 0x18, 0xcc},
+ {0xbc, 0x05, 0x00, 0xcc},
+ {0xbc, 0x06, 0x00, 0xcc},
+ {0xbc, 0x08, 0x30, 0xcc},
+ {0xbc, 0x09, 0x40, 0xcc},
+ {0xbc, 0x0a, 0x10, 0xcc},
+ {0xbc, 0x0b, 0x00, 0xcc},
+ {0xbc, 0x0c, 0x00, 0xcc},
+ {0xbf, 0xc0, 0x26, 0xcc},
+ {0xbf, 0xc1, 0x02, 0xcc},
+ {0xbf, 0xcc, 0x04, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {0xb3, 0x01, 0x45, 0xcc},
+ {0x00, 0x77, 0x05, 0xaa},
+ {},
+};
+
+/* PO1200 - values from usbvm326.inf and ms-win trace */
+static const u8 po1200_gamma[17] = {
+ 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
+ 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff
+};
+static const u8 po1200_matrix[9] = {
+ 0x60, 0xf9, 0xe5, 0xe7, 0x50, 0x05, 0xf3, 0xe6, 0x5e
+};
+static const u8 po1200_initVGA_data[][4] = {
+ {0xb0, 0x03, 0x19, 0xcc}, /* reset? */
+ {0xb0, 0x03, 0x19, 0xcc},
+/* {0x00, 0x00, 0x33, 0xdd}, */
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0xb0, 0x02, 0x02, 0xcc},
+ {0xb3, 0x5d, 0x00, 0xcc},
+ {0xb3, 0x01, 0x01, 0xcc},
+ {0xb3, 0x00, 0x64, 0xcc},
+ {0xb3, 0x00, 0x65, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x01, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xb3, 0x02, 0xb2, 0xcc},
+ {0xb3, 0x03, 0x18, 0xcc},
+ {0xb3, 0x04, 0x15, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x02, 0xcc},
+ {0xb3, 0x23, 0x58, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x03, 0xcc},
+ {0xb3, 0x17, 0x1f, 0xcc},
+ {0xbc, 0x00, 0x71, 0xcc},
+ {0xbc, 0x01, 0x01, 0xcc},
+ {0xb0, 0x54, 0x13, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xb3, 0x34, 0x01, 0xcc},
+ {0xb3, 0x35, 0xdc, 0xcc}, /* i2c add: 5c */
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x12, 0x05, 0xaa},
+ {0x00, 0x13, 0x02, 0xaa},
+ {0x00, 0x1e, 0xc6, 0xaa}, /* h/v flip */
+ {0x00, 0x21, 0x00, 0xaa},
+ {0x00, 0x25, 0x02, 0xaa},
+ {0x00, 0x3c, 0x4f, 0xaa},
+ {0x00, 0x3f, 0xe0, 0xaa},
+ {0x00, 0x42, 0xff, 0xaa},
+ {0x00, 0x45, 0x34, 0xaa},
+ {0x00, 0x55, 0xfe, 0xaa},
+ {0x00, 0x59, 0xd3, 0xaa},
+ {0x00, 0x5e, 0x04, 0xaa},
+ {0x00, 0x61, 0xb8, 0xaa}, /* sharpness */
+ {0x00, 0x62, 0x02, 0xaa},
+ {0x00, 0xa7, 0x31, 0xaa},
+ {0x00, 0xa9, 0x66, 0xaa},
+ {0x00, 0xb0, 0x00, 0xaa},
+ {0x00, 0xb1, 0x00, 0xaa},
+ {0x00, 0xb3, 0x11, 0xaa},
+ {0x00, 0xb6, 0x26, 0xaa},
+ {0x00, 0xb7, 0x20, 0xaa},
+ {0x00, 0xba, 0x04, 0xaa},
+ {0x00, 0x88, 0x42, 0xaa},
+ {0x00, 0x89, 0x9a, 0xaa},
+ {0x00, 0x8a, 0x88, 0xaa},
+ {0x00, 0x8b, 0x8e, 0xaa},
+ {0x00, 0x8c, 0x3e, 0xaa},
+ {0x00, 0x8d, 0x90, 0xaa},
+ {0x00, 0x8e, 0x87, 0xaa},
+ {0x00, 0x8f, 0x96, 0xaa},
+ {0x00, 0x90, 0x3d, 0xaa},
+ {0x00, 0x64, 0x00, 0xaa},
+ {0x00, 0x65, 0x10, 0xaa},
+ {0x00, 0x66, 0x20, 0xaa},
+ {0x00, 0x67, 0x2b, 0xaa},
+ {0x00, 0x68, 0x36, 0xaa},
+ {0x00, 0x69, 0x49, 0xaa},
+ {0x00, 0x6a, 0x5a, 0xaa},
+ {0x00, 0x6b, 0x7f, 0xaa},
+ {0x00, 0x6c, 0x9b, 0xaa},
+ {0x00, 0x6d, 0xba, 0xaa},
+ {0x00, 0x6e, 0xd4, 0xaa},
+ {0x00, 0x6f, 0xea, 0xaa},
+ {0x00, 0x70, 0x00, 0xaa},
+ {0x00, 0x71, 0x10, 0xaa},
+ {0x00, 0x72, 0x20, 0xaa},
+ {0x00, 0x73, 0x2b, 0xaa},
+ {0x00, 0x74, 0x36, 0xaa},
+ {0x00, 0x75, 0x49, 0xaa},
+ {0x00, 0x76, 0x5a, 0xaa},
+ {0x00, 0x77, 0x7f, 0xaa},
+ {0x00, 0x78, 0x9b, 0xaa},
+ {0x00, 0x79, 0xba, 0xaa},
+ {0x00, 0x7a, 0xd4, 0xaa},
+ {0x00, 0x7b, 0xea, 0xaa},
+ {0x00, 0x7c, 0x00, 0xaa},
+ {0x00, 0x7d, 0x10, 0xaa},
+ {0x00, 0x7e, 0x20, 0xaa},
+ {0x00, 0x7f, 0x2b, 0xaa},
+ {0x00, 0x80, 0x36, 0xaa},
+ {0x00, 0x81, 0x49, 0xaa},
+ {0x00, 0x82, 0x5a, 0xaa},
+ {0x00, 0x83, 0x7f, 0xaa},
+ {0x00, 0x84, 0x9b, 0xaa},
+ {0x00, 0x85, 0xba, 0xaa},
+ {0x00, 0x86, 0xd4, 0xaa},
+ {0x00, 0x87, 0xea, 0xaa},
+ {0x00, 0x57, 0x2a, 0xaa},
+ {0x00, 0x03, 0x01, 0xaa},
+ {0x00, 0x04, 0x10, 0xaa},
+ {0x00, 0x05, 0x10, 0xaa},
+ {0x00, 0x06, 0x10, 0xaa},
+ {0x00, 0x07, 0x10, 0xaa},
+ {0x00, 0x08, 0x13, 0xaa},
+ {0x00, 0x0a, 0x00, 0xaa},
+ {0x00, 0x0b, 0x10, 0xaa},
+ {0x00, 0x0c, 0x20, 0xaa},
+ {0x00, 0x0d, 0x18, 0xaa},
+ {0x00, 0x22, 0x01, 0xaa},
+ {0x00, 0x23, 0x60, 0xaa},
+ {0x00, 0x25, 0x08, 0xaa},
+ {0x00, 0x26, 0x82, 0xaa},
+ {0x00, 0x2e, 0x0f, 0xaa},
+ {0x00, 0x2f, 0x1e, 0xaa},
+ {0x00, 0x30, 0x2d, 0xaa},
+ {0x00, 0x31, 0x3c, 0xaa},
+ {0x00, 0x32, 0x4b, 0xaa},
+ {0x00, 0x33, 0x5a, 0xaa},
+ {0x00, 0x34, 0x69, 0xaa},
+ {0x00, 0x35, 0x78, 0xaa},
+ {0x00, 0x36, 0x87, 0xaa},
+ {0x00, 0x37, 0x96, 0xaa},
+ {0x00, 0x38, 0xa5, 0xaa},
+ {0x00, 0x39, 0xb4, 0xaa},
+ {0x00, 0x3a, 0xc3, 0xaa},
+ {0x00, 0x3b, 0xd2, 0xaa},
+ {0x00, 0x3c, 0xe1, 0xaa},
+ {0x00, 0x3e, 0xff, 0xaa},
+ {0x00, 0x3f, 0xff, 0xaa},
+ {0x00, 0x40, 0xff, 0xaa},
+ {0x00, 0x41, 0xff, 0xaa},
+ {0x00, 0x42, 0xff, 0xaa},
+ {0x00, 0x43, 0xff, 0xaa},
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x20, 0xc4, 0xaa},
+ {0x00, 0x13, 0x03, 0xaa},
+ {0x00, 0x3c, 0x50, 0xaa},
+ {0x00, 0x61, 0x6a, 0xaa}, /* sharpness? */
+ {0x00, 0x51, 0x5b, 0xaa},
+ {0x00, 0x52, 0x91, 0xaa},
+ {0x00, 0x53, 0x4c, 0xaa},
+ {0x00, 0x54, 0x50, 0xaa},
+ {0x00, 0x56, 0x02, 0xaa},
+ {0xb6, 0x00, 0x00, 0xcc},
+ {0xb6, 0x03, 0x03, 0xcc},
+ {0xb6, 0x02, 0x20, 0xcc},
+ {0xb6, 0x05, 0x02, 0xcc},
+ {0xb6, 0x04, 0x58, 0xcc},
+ {0xb6, 0x12, 0xf8, 0xcc},
+ {0xb6, 0x13, 0x21, 0xcc},
+ {0xb6, 0x18, 0x03, 0xcc},
+ {0xb6, 0x17, 0xa9, 0xcc},
+ {0xb6, 0x16, 0x80, 0xcc},
+ {0xb6, 0x22, 0x12, 0xcc},
+ {0xb6, 0x23, 0x0b, 0xcc},
+ {0xbf, 0xc0, 0x39, 0xcc},
+ {0xbf, 0xc1, 0x04, 0xcc},
+ {0xbf, 0xcc, 0x00, 0xcc},
+ {0xb8, 0x06, 0x20, 0xcc},
+ {0xb8, 0x07, 0x03, 0xcc},
+ {0xb8, 0x08, 0x58, 0xcc},
+ {0xb8, 0x09, 0x02, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0xd9, 0x0f, 0xaa},
+ {0x00, 0xda, 0xaa, 0xaa},
+ {0x00, 0xd9, 0x10, 0xaa},
+ {0x00, 0xda, 0xaa, 0xaa},
+ {0x00, 0xd9, 0x11, 0xaa},
+ {0x00, 0xda, 0x00, 0xaa},
+ {0x00, 0xd9, 0x12, 0xaa},
+ {0x00, 0xda, 0xff, 0xaa},
+ {0x00, 0xd9, 0x13, 0xaa},
+ {0x00, 0xda, 0xff, 0xaa},
+ {0x00, 0xe8, 0x11, 0xaa},
+ {0x00, 0xe9, 0x12, 0xaa},
+ {0x00, 0xea, 0x5c, 0xaa},
+ {0x00, 0xeb, 0xff, 0xaa},
+ {0x00, 0xd8, 0x80, 0xaa},
+ {0x00, 0xe6, 0x02, 0xaa},
+ {0x00, 0xd6, 0x40, 0xaa},
+ {0x00, 0xe3, 0x05, 0xaa},
+ {0x00, 0xe0, 0x40, 0xaa},
+ {0x00, 0xde, 0x03, 0xaa},
+ {0x00, 0xdf, 0x03, 0xaa},
+ {0x00, 0xdb, 0x02, 0xaa},
+ {0x00, 0xdc, 0x00, 0xaa},
+ {0x00, 0xdd, 0x03, 0xaa},
+ {0x00, 0xe1, 0x08, 0xaa},
+ {0x00, 0xe2, 0x01, 0xaa},
+ {0x00, 0xd6, 0x40, 0xaa},
+ {0x00, 0xe4, 0x40, 0xaa},
+ {0x00, 0xa8, 0x8f, 0xaa},
+ {0x00, 0xb4, 0x16, 0xaa},
+ {0xb0, 0x02, 0x06, 0xcc},
+ {0xb0, 0x18, 0x06, 0xcc},
+ {0xb0, 0x19, 0x06, 0xcc},
+ {0xb3, 0x5d, 0x18, 0xcc},
+ {0xb3, 0x05, 0x00, 0xcc},
+ {0xb3, 0x06, 0x00, 0xcc},
+ {0x00, 0xb4, 0x0e, 0xaa},
+ {0x00, 0xb5, 0x49, 0xaa},
+ {0x00, 0xb6, 0x1c, 0xaa},
+ {0x00, 0xb7, 0x96, 0xaa},
+/* end of usbvm326.inf - start of ms-win trace */
+ {0xb6, 0x12, 0xf8, 0xcc},
+ {0xb6, 0x13, 0x3d, 0xcc},
+/*read b306*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x1a, 0x09, 0xaa},
+ {0x00, 0x1b, 0x8a, 0xaa},
+/*read b827*/
+ {0xb8, 0x27, 0x00, 0xcc},
+ {0xb8, 0x26, 0x60, 0xcc},
+ {0xb8, 0x26, 0x60, 0xcc},
+/*gamma - to do?*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0xae, 0x84, 0xaa},
+/*gamma again*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x96, 0xa0, 0xaa},
+/*matrix*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x91, 0x35, 0xaa},
+ {0x00, 0x92, 0x22, 0xaa},
+/*gamma*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x95, 0x85, 0xaa},
+/*matrix*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x4d, 0x20, 0xaa},
+ {0xb8, 0x22, 0x40, 0xcc},
+ {0xb8, 0x23, 0x40, 0xcc},
+ {0xb8, 0x24, 0x40, 0xcc},
+ {0xb8, 0x81, 0x09, 0xcc},
+ {0x00, 0x00, 0x64, 0xdd},
+ {0x00, 0x03, 0x01, 0xaa},
+/*read 46*/
+ {0x00, 0x46, 0x3c, 0xaa},
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x16, 0x40, 0xaa},
+ {0x00, 0x17, 0x40, 0xaa},
+ {0x00, 0x18, 0x40, 0xaa},
+ {0x00, 0x19, 0x41, 0xaa},
+ {0x00, 0x03, 0x01, 0xaa},
+ {0x00, 0x46, 0x3c, 0xaa},
+ {0x00, 0x00, 0x18, 0xdd},
+/*read bfff*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0xb4, 0x1c, 0xaa},
+ {0x00, 0xb5, 0x92, 0xaa},
+ {0x00, 0xb6, 0x39, 0xaa},
+ {0x00, 0xb7, 0x24, 0xaa},
+/*write 89 0400 1415*/
+ {}
+};
+
+static const u8 poxxxx_init_common[][4] = {
+ {0xb3, 0x00, 0x04, 0xcc},
+ {0x00, 0x00, 0x10, 0xdd},
+ {0xb3, 0x00, 0x64, 0xcc},
+ {0x00, 0x00, 0x10, 0xdd},
+ {0xb3, 0x00, 0x65, 0xcc},
+ {0x00, 0x00, 0x10, 0xdd},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xb0, 0x03, 0x09, 0xcc},
+ {0xb3, 0x05, 0x00, 0xcc},
+ {0xb3, 0x06, 0x00, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x34, 0x01, 0xcc},
+ {0xb3, 0x35, 0xf6, 0xcc}, /* i2c add: 76 */
+ {0xb3, 0x02, 0xb0, 0xcc},
+ {0xb3, 0x03, 0x18, 0xcc},
+ {0xb3, 0x04, 0x15, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x04, 0xcc}, /* sensor height = 1024 */
+ {0xb3, 0x23, 0x00, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x04, 0xcc}, /* sensor width = 1280 */
+ {0xb3, 0x17, 0xff, 0xcc},
+ {0xb3, 0x2c, 0x03, 0xcc},
+ {0xb3, 0x2d, 0x56, 0xcc},
+ {0xb3, 0x2e, 0x02, 0xcc},
+ {0xb3, 0x2f, 0x0a, 0xcc},
+ {0xb3, 0x40, 0x00, 0xcc},
+ {0xb3, 0x41, 0x34, 0xcc},
+ {0xb3, 0x42, 0x01, 0xcc},
+ {0xb3, 0x43, 0xe0, 0xcc},
+ {0xbc, 0x00, 0x71, 0xcc},
+ {0xbc, 0x01, 0x01, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0xb3, 0x4d, 0x00, 0xcc},
+ {0x00, 0x0b, 0x2a, 0xaa},
+ {0x00, 0x0e, 0x03, 0xaa},
+ {0x00, 0x0f, 0xea, 0xaa},
+ {0x00, 0x12, 0x08, 0xaa},
+ {0x00, 0x1e, 0x06, 0xaa},
+ {0x00, 0x21, 0x00, 0xaa},
+ {0x00, 0x31, 0x1f, 0xaa},
+ {0x00, 0x33, 0x38, 0xaa},
+ {0x00, 0x36, 0xc0, 0xaa},
+ {0x00, 0x37, 0xc8, 0xaa},
+ {0x00, 0x3b, 0x36, 0xaa},
+ {0x00, 0x4b, 0xfe, 0xaa},
+ {0x00, 0x4d, 0x2e, 0xaa},
+ {0x00, 0x51, 0x1c, 0xaa},
+ {0x00, 0x52, 0x01, 0xaa},
+ {0x00, 0x55, 0x0a, 0xaa},
+ {0x00, 0x56, 0x0a, 0xaa},
+ {0x00, 0x57, 0x07, 0xaa},
+ {0x00, 0x58, 0x07, 0xaa},
+ {0x00, 0x59, 0x04, 0xaa},
+ {0x00, 0x70, 0x68, 0xaa},
+ {0x00, 0x71, 0x04, 0xaa},
+ {0x00, 0x72, 0x10, 0xaa},
+ {0x00, 0x80, 0x71, 0xaa},
+ {0x00, 0x81, 0x08, 0xaa},
+ {0x00, 0x82, 0x00, 0xaa},
+ {0x00, 0x83, 0x55, 0xaa},
+ {0x00, 0x84, 0x06, 0xaa},
+ {0x00, 0x85, 0x06, 0xaa},
+ {0x00, 0x8b, 0x25, 0xaa},
+ {0x00, 0x8c, 0x00, 0xaa},
+ {0x00, 0x8d, 0x86, 0xaa},
+ {0x00, 0x8e, 0x82, 0xaa},
+ {0x00, 0x8f, 0x2d, 0xaa},
+ {0x00, 0x90, 0x8b, 0xaa},
+ {0x00, 0x91, 0x81, 0xaa},
+ {0x00, 0x92, 0x81, 0xaa},
+ {0x00, 0x93, 0x23, 0xaa},
+ {0x00, 0xa3, 0x2a, 0xaa},
+ {0x00, 0xa4, 0x03, 0xaa},
+ {0x00, 0xa5, 0xea, 0xaa},
+ {0x00, 0xb0, 0x68, 0xaa},
+ {0x00, 0xbc, 0x04, 0xaa},
+ {0x00, 0xbe, 0x3b, 0xaa},
+ {0x00, 0x4e, 0x40, 0xaa},
+ {0x00, 0x06, 0x04, 0xaa},
+ {0x00, 0x07, 0x03, 0xaa},
+ {0x00, 0xcd, 0x18, 0xaa},
+ {0x00, 0x28, 0x03, 0xaa},
+ {0x00, 0x29, 0xef, 0xaa},
+/* reinit on alt 2 (qvga) or alt7 (vga) */
+ {0xb3, 0x05, 0x00, 0xcc},
+ {0xb3, 0x06, 0x00, 0xcc},
+ {0xb8, 0x00, 0x01, 0xcc},
+
+ {0x00, 0x1d, 0x85, 0xaa},
+ {0x00, 0x1e, 0xc6, 0xaa},
+ {0x00, 0x00, 0x40, 0xdd},
+ {0x00, 0x1d, 0x05, 0xaa},
+ {}
+};
+static const u8 poxxxx_gamma[][4] = {
+ {0x00, 0xd6, 0x22, 0xaa}, /* gamma 0 */
+ {0x00, 0x73, 0x00, 0xaa},
+ {0x00, 0x74, 0x0a, 0xaa},
+ {0x00, 0x75, 0x16, 0xaa},
+ {0x00, 0x76, 0x25, 0xaa},
+ {0x00, 0x77, 0x34, 0xaa},
+ {0x00, 0x78, 0x49, 0xaa},
+ {0x00, 0x79, 0x5a, 0xaa},
+ {0x00, 0x7a, 0x7f, 0xaa},
+ {0x00, 0x7b, 0x9b, 0xaa},
+ {0x00, 0x7c, 0xba, 0xaa},
+ {0x00, 0x7d, 0xd4, 0xaa},
+ {0x00, 0x7e, 0xea, 0xaa},
+
+ {0x00, 0xd6, 0x62, 0xaa}, /* gamma 1 */
+ {0x00, 0x73, 0x00, 0xaa},
+ {0x00, 0x74, 0x0a, 0xaa},
+ {0x00, 0x75, 0x16, 0xaa},
+ {0x00, 0x76, 0x25, 0xaa},
+ {0x00, 0x77, 0x34, 0xaa},
+ {0x00, 0x78, 0x49, 0xaa},
+ {0x00, 0x79, 0x5a, 0xaa},
+ {0x00, 0x7a, 0x7f, 0xaa},
+ {0x00, 0x7b, 0x9b, 0xaa},
+ {0x00, 0x7c, 0xba, 0xaa},
+ {0x00, 0x7d, 0xd4, 0xaa},
+ {0x00, 0x7e, 0xea, 0xaa},
+
+ {0x00, 0xd6, 0xa2, 0xaa}, /* gamma 2 */
+ {0x00, 0x73, 0x00, 0xaa},
+ {0x00, 0x74, 0x0a, 0xaa},
+ {0x00, 0x75, 0x16, 0xaa},
+ {0x00, 0x76, 0x25, 0xaa},
+ {0x00, 0x77, 0x34, 0xaa},
+ {0x00, 0x78, 0x49, 0xaa},
+ {0x00, 0x79, 0x5a, 0xaa},
+ {0x00, 0x7a, 0x7f, 0xaa},
+ {0x00, 0x7b, 0x9b, 0xaa},
+ {0x00, 0x7c, 0xba, 0xaa},
+ {0x00, 0x7d, 0xd4, 0xaa},
+ {0x00, 0x7e, 0xea, 0xaa},
+ {}
+};
+static const u8 poxxxx_init_start_3[][4] = {
+ {0x00, 0xb8, 0x28, 0xaa},
+ {0x00, 0xb9, 0x1e, 0xaa},
+ {0x00, 0xb6, 0x14, 0xaa},
+ {0x00, 0xb7, 0x0f, 0xaa},
+ {0x00, 0x5c, 0x10, 0xaa},
+ {0x00, 0x5d, 0x18, 0xaa},
+ {0x00, 0x5e, 0x24, 0xaa},
+ {0x00, 0x5f, 0x24, 0xaa},
+ {0x00, 0x86, 0x1a, 0xaa},
+ {0x00, 0x60, 0x00, 0xaa},
+ {0x00, 0x61, 0x1b, 0xaa},
+ {0x00, 0x62, 0x30, 0xaa},
+ {0x00, 0x63, 0x40, 0xaa},
+ {0x00, 0x87, 0x1a, 0xaa},
+ {0x00, 0x64, 0x00, 0xaa},
+ {0x00, 0x65, 0x08, 0xaa},
+ {0x00, 0x66, 0x10, 0xaa},
+ {0x00, 0x67, 0x20, 0xaa},
+ {0x00, 0x88, 0x10, 0xaa},
+ {0x00, 0x68, 0x00, 0xaa},
+ {0x00, 0x69, 0x08, 0xaa},
+ {0x00, 0x6a, 0x0f, 0xaa},
+ {0x00, 0x6b, 0x0f, 0xaa},
+ {0x00, 0x89, 0x07, 0xaa},
+ {0x00, 0xd5, 0x4c, 0xaa},
+ {0x00, 0x0a, 0x00, 0xaa},
+ {0x00, 0x0b, 0x2a, 0xaa},
+ {0x00, 0x0e, 0x03, 0xaa},
+ {0x00, 0x0f, 0xea, 0xaa},
+ {0x00, 0xa2, 0x00, 0xaa},
+ {0x00, 0xa3, 0x2a, 0xaa},
+ {0x00, 0xa4, 0x03, 0xaa},
+ {0x00, 0xa5, 0xea, 0xaa},
+ {}
+};
+static const u8 poxxxx_initVGA[][4] = {
+ {0x00, 0x20, 0x11, 0xaa},
+ {0x00, 0x33, 0x38, 0xaa},
+ {0x00, 0xbb, 0x0d, 0xaa},
+ {0xb3, 0x22, 0x01, 0xcc}, /* change to 640x480 */
+ {0xb3, 0x23, 0xe0, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc},
+ {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x02, 0xb0, 0xcc},
+ {0xb3, 0x06, 0x00, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {0x00, 0x04, 0x06, 0xaa},
+ {0x00, 0x05, 0x3f, 0xaa},
+ {0x00, 0x04, 0x00, 0xdd}, /* delay 1s */
+ {}
+};
+static const u8 poxxxx_initQVGA[][4] = {
+ {0x00, 0x20, 0x33, 0xaa},
+ {0x00, 0x33, 0x38, 0xaa},
+ {0x00, 0xbb, 0x0d, 0xaa},
+ {0xb3, 0x22, 0x00, 0xcc}, /* change to 320x240 */
+ {0xb3, 0x23, 0xf0, 0xcc},
+ {0xb3, 0x16, 0x01, 0xcc},
+ {0xb3, 0x17, 0x3f, 0xcc},
+ {0xb3, 0x02, 0xb0, 0xcc},
+ {0xb3, 0x06, 0x01, 0xcc},
+ {0xb3, 0x5c, 0x00, 0xcc},
+ {0x00, 0x04, 0x06, 0xaa},
+ {0x00, 0x05, 0x3f, 0xaa},
+ {0x00, 0x04, 0x00, 0xdd}, /* delay 1s */
+ {}
+};
+static const u8 poxxxx_init_end_1[][4] = {
+ {0x00, 0x47, 0x25, 0xaa},
+ {0x00, 0x48, 0x80, 0xaa},
+ {0x00, 0x49, 0x1f, 0xaa},
+ {0x00, 0x4a, 0x40, 0xaa},
+ {0x00, 0x44, 0x40, 0xaa},
+ {0x00, 0xab, 0x4a, 0xaa},
+ {0x00, 0xb1, 0x00, 0xaa},
+ {0x00, 0xb2, 0x04, 0xaa},
+ {0x00, 0xb3, 0x08, 0xaa},
+ {0x00, 0xb4, 0x0b, 0xaa},
+ {0x00, 0xb5, 0x0d, 0xaa},
+ {}
+};
+static const u8 poxxxx_init_end_2[][4] = {
+ {0x00, 0x1d, 0x85, 0xaa},
+ {0x00, 0x1e, 0x06, 0xaa},
+ {0x00, 0x1d, 0x05, 0xaa},
+ {}
+};
+
+struct sensor_info {
+ s8 sensorId;
+ u8 I2cAdd;
+ u8 IdAdd;
+ u16 VpId;
+ u8 m1;
+ u8 m2;
+ u8 op;
+};
+
+/* probe values */
+static const struct sensor_info vc0321_probe_data[] = {
+/* sensorId, I2cAdd, IdAdd, VpId, m1, m2, op */
+/* 0 OV9640 */
+ {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05},
+/* 1 ICM108T (may respond on IdAdd == 0x83 - tested in vc032x_probe_sensor) */
+ {-1, 0x80 | 0x20, 0x82, 0x0000, 0x24, 0x25, 0x01},
+/* 2 PO2130 (may detect PO3130NC - tested in vc032x_probe_sensor)*/
+ {-1, 0x80 | 0x76, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* 3 MI1310 */
+ {-1, 0x80 | 0x5d, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* 4 MI360 - tested in vc032x_probe_sensor */
+/* {SENSOR_MI0360, 0x80 | 0x5d, 0x00, 0x8243, 0x24, 0x25, 0x01}, */
+/* 5 7131R */
+ {SENSOR_HV7131R, 0x80 | 0x11, 0x00, 0x0209, 0x24, 0x25, 0x01},
+/* 6 OV7649 */
+ {-1, 0x80 | 0x21, 0x0a, 0x0000, 0x21, 0x20, 0x05},
+/* 7 PAS302BCW */
+ {-1, 0x80 | 0x40, 0x00, 0x0000, 0x20, 0x22, 0x05},
+/* 8 OV7660 */
+ {SENSOR_OV7660, 0x80 | 0x21, 0x0a, 0x7660, 0x26, 0x26, 0x05},
+/* 9 PO3130NC - (tested in vc032x_probe_sensor) */
+/* {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01}, */
+/* 10 PO1030KC */
+ {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* 11 MI1310_SOC */
+ {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01},
+/* 12 OV9650 */
+ {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05},
+/* 13 S5K532 */
+ {-1, 0x80 | 0x11, 0x39, 0x0000, 0x24, 0x25, 0x01},
+/* 14 MI360_SOC - ??? */
+/* 15 PO1200N */
+ {SENSOR_PO1200, 0x80 | 0x5c, 0x00, 0x1200, 0x67, 0x67, 0x01},
+/* 16 PO3030K */
+ {-1, 0x80 | 0x18, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* 17 PO2030 */
+ {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* ?? */
+ {-1, 0x80 | 0x56, 0x01, 0x0000, 0x64, 0x67, 0x01},
+ {SENSOR_MI1320, 0x80 | 0x48, 0x00, 0x148c, 0x64, 0x65, 0x01},
+};
+static const struct sensor_info vc0323_probe_data[] = {
+/* sensorId, I2cAdd, IdAdd, VpId, m1, m2, op */
+/* 0 OV9640 */
+ {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05},
+/* 1 ICM108T (may respond on IdAdd == 0x83 - tested in vc032x_probe_sensor) */
+ {-1, 0x80 | 0x20, 0x82, 0x0000, 0x24, 0x25, 0x01},
+/* 2 PO2130 (may detect PO3130NC - tested in vc032x_probe_sensor)*/
+ {-1, 0x80 | 0x76, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* 3 MI1310 */
+ {-1, 0x80 | 0x5d, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* 4 MI360 - tested in vc032x_probe_sensor */
+/* {SENSOR_MI0360, 0x80 | 0x5d, 0x00, 0x8243, 0x24, 0x25, 0x01}, */
+/* 5 7131R */
+ {SENSOR_HV7131R, 0x80 | 0x11, 0x00, 0x0209, 0x24, 0x25, 0x01},
+/* 6 OV7649 */
+ {-1, 0x80 | 0x21, 0x0a, 0x0000, 0x21, 0x20, 0x05},
+/* 7 PAS302BCW */
+ {-1, 0x80 | 0x40, 0x00, 0x0000, 0x20, 0x22, 0x05},
+/* 8 OV7660 */
+ {SENSOR_OV7660, 0x80 | 0x21, 0x0a, 0x7660, 0x26, 0x26, 0x05},
+/* 9 PO3130NC - (tested in vc032x_probe_sensor) */
+/* {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01}, */
+/* 10 PO1030KC */
+ {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* 11 MI1310_SOC */
+ {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01},
+/* 12 OV9650 */
+ {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05},
+/* 13 S5K532 */
+ {-1, 0x80 | 0x11, 0x39, 0x0000, 0x24, 0x25, 0x01},
+/* 14 MI360_SOC - ??? */
+/* 15 PO1200N */
+ {SENSOR_PO1200, 0x80 | 0x5c, 0x00, 0x1200, 0x67, 0x67, 0x01},
+/* 16 ?? */
+ {-1, 0x80 | 0x2d, 0x00, 0x0000, 0x65, 0x67, 0x01},
+/* 17 PO2030 */
+ {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* ?? */
+ {-1, 0x80 | 0x56, 0x01, 0x0000, 0x64, 0x67, 0x01},
+ {SENSOR_MI1320_SOC, 0x80 | 0x48, 0x00, 0x148c, 0x64, 0x67, 0x01},
+/*fixme: not in the ms-win probe - may be found before? */
+ {SENSOR_OV7670, 0x80 | 0x21, 0x0a, 0x7673, 0x66, 0x67, 0x05},
+};
+
+/* read 'len' bytes in gspca_dev->usb_buf */
+static void reg_r_i(struct gspca_dev *gspca_dev,
+ u16 req,
+ u16 index,
+ u16 len)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ req,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 1, /* value */
+ index, gspca_dev->usb_buf, len,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_r err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+static void reg_r(struct gspca_dev *gspca_dev,
+ u16 req,
+ u16 index,
+ u16 len)
+{
+ reg_r_i(gspca_dev, req, index, len);
+#ifdef GSPCA_DEBUG
+ if (gspca_dev->usb_err < 0)
+ return;
+ if (len == 1)
+ PDEBUG(D_USBI, "GET %02x 0001 %04x %02x", req, index,
+ gspca_dev->usb_buf[0]);
+ else
+ PDEBUG(D_USBI, "GET %02x 0001 %04x %02x %02x %02x",
+ req, index,
+ gspca_dev->usb_buf[0],
+ gspca_dev->usb_buf[1],
+ gspca_dev->usb_buf[2]);
+#endif
+}
+
+static void reg_w_i(struct gspca_dev *gspca_dev,
+ u16 req,
+ u16 value,
+ u16 index)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ req,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_w err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+static void reg_w(struct gspca_dev *gspca_dev,
+ u16 req,
+ u16 value,
+ u16 index)
+{
+#ifdef GSPCA_DEBUG
+ if (gspca_dev->usb_err < 0)
+ return;
+ PDEBUG(D_USBO, "SET %02x %04x %04x", req, value, index);
+#endif
+ reg_w_i(gspca_dev, req, value, index);
+}
+
+static u16 read_sensor_register(struct gspca_dev *gspca_dev,
+ u16 address)
+{
+ u8 ldata, mdata, hdata;
+ int retry = 50;
+
+ reg_r(gspca_dev, 0xa1, 0xb33f, 1);
+ if (!(gspca_dev->usb_buf[0] & 0x02)) {
+ pr_err("I2c Bus Busy Wait %02x\n", gspca_dev->usb_buf[0]);
+ return 0;
+ }
+ reg_w(gspca_dev, 0xa0, address, 0xb33a);
+ reg_w(gspca_dev, 0xa0, 0x02, 0xb339);
+
+ do {
+ reg_r(gspca_dev, 0xa1, 0xb33b, 1);
+ if (gspca_dev->usb_buf[0] == 0x00)
+ break;
+ msleep(40);
+ } while (--retry >= 0);
+
+ reg_r(gspca_dev, 0xa1, 0xb33e, 1);
+ ldata = gspca_dev->usb_buf[0];
+ reg_r(gspca_dev, 0xa1, 0xb33d, 1);
+ mdata = gspca_dev->usb_buf[0];
+ reg_r(gspca_dev, 0xa1, 0xb33c, 1);
+ hdata = gspca_dev->usb_buf[0];
+ if (hdata != 0 && mdata != 0 && ldata != 0)
+ PDEBUG(D_PROBE, "Read Sensor %02x%02x %02x",
+ hdata, mdata, ldata);
+ reg_r(gspca_dev, 0xa1, 0xb334, 1);
+ if (gspca_dev->usb_buf[0] == 0x02)
+ return (hdata << 8) + mdata;
+ return hdata;
+}
+
+static int vc032x_probe_sensor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, n;
+ u16 value;
+ const struct sensor_info *ptsensor_info;
+
+/*fixme: should also check the other sensor (back mi1320_soc, front mc501cb)*/
+ if (sd->flags & FL_SAMSUNG) {
+ reg_w(gspca_dev, 0xa0, 0x01, 0xb301);
+ reg_w(gspca_dev, 0x89, 0xf0ff, 0xffff);
+ /* select the back sensor */
+ }
+
+ reg_r(gspca_dev, 0xa1, 0xbfcf, 1);
+ PDEBUG(D_PROBE, "vc032%d check sensor header %02x",
+ sd->bridge == BRIDGE_VC0321 ? 1 : 3, gspca_dev->usb_buf[0]);
+ if (sd->bridge == BRIDGE_VC0321) {
+ ptsensor_info = vc0321_probe_data;
+ n = ARRAY_SIZE(vc0321_probe_data);
+ } else {
+ ptsensor_info = vc0323_probe_data;
+ n = ARRAY_SIZE(vc0323_probe_data);
+ }
+ for (i = 0; i < n; i++) {
+ reg_w(gspca_dev, 0xa0, 0x02, 0xb334);
+ reg_w(gspca_dev, 0xa0, ptsensor_info->m1, 0xb300);
+ reg_w(gspca_dev, 0xa0, ptsensor_info->m2, 0xb300);
+ reg_w(gspca_dev, 0xa0, 0x01, 0xb308);
+ reg_w(gspca_dev, 0xa0, 0x0c, 0xb309);
+ reg_w(gspca_dev, 0xa0, ptsensor_info->I2cAdd, 0xb335);
+ reg_w(gspca_dev, 0xa0, ptsensor_info->op, 0xb301);
+ value = read_sensor_register(gspca_dev, ptsensor_info->IdAdd);
+ if (value == 0 && ptsensor_info->IdAdd == 0x82)
+ value = read_sensor_register(gspca_dev, 0x83);
+ if (value != 0) {
+ PDEBUG(D_ERR|D_PROBE, "Sensor ID %04x (%d)",
+ value, i);
+ if (value == ptsensor_info->VpId)
+ return ptsensor_info->sensorId;
+
+ switch (value) {
+ case 0x3130:
+ return SENSOR_PO3130NC;
+ case 0x7673:
+ return SENSOR_OV7670;
+ case 0x8243:
+ return SENSOR_MI0360;
+ }
+ }
+ ptsensor_info++;
+ }
+ return -1;
+}
+
+static void i2c_write(struct gspca_dev *gspca_dev,
+ u8 reg, const u8 *val,
+ u8 size) /* 1 or 2 */
+{
+ int retry;
+
+#ifdef GSPCA_DEBUG
+ if (gspca_dev->usb_err < 0)
+ return;
+ if (size == 1)
+ PDEBUG(D_USBO, "i2c_w %02x %02x", reg, *val);
+ else
+ PDEBUG(D_USBO, "i2c_w %02x %02x%02x", reg, *val, val[1]);
+#endif
+ reg_r_i(gspca_dev, 0xa1, 0xb33f, 1);
+/*fixme:should check if (!(gspca_dev->usb_buf[0] & 0x02)) error*/
+ reg_w_i(gspca_dev, 0xa0, size, 0xb334);
+ reg_w_i(gspca_dev, 0xa0, reg, 0xb33a);
+ reg_w_i(gspca_dev, 0xa0, val[0], 0xb336);
+ if (size > 1)
+ reg_w_i(gspca_dev, 0xa0, val[1], 0xb337);
+ reg_w_i(gspca_dev, 0xa0, 0x01, 0xb339);
+ retry = 4;
+ do {
+ reg_r_i(gspca_dev, 0xa1, 0xb33b, 1);
+ if (gspca_dev->usb_buf[0] == 0)
+ break;
+ msleep(20);
+ } while (--retry > 0);
+ if (retry <= 0)
+ pr_err("i2c_write timeout\n");
+}
+
+static void put_tab_to_reg(struct gspca_dev *gspca_dev,
+ const u8 *tab, u8 tabsize, u16 addr)
+{
+ int j;
+ u16 ad = addr;
+
+ for (j = 0; j < tabsize; j++)
+ reg_w(gspca_dev, 0xa0, tab[j], ad++);
+}
+
+static void usb_exchange(struct gspca_dev *gspca_dev,
+ const u8 data[][4])
+{
+ int i = 0;
+
+ for (;;) {
+ switch (data[i][3]) {
+ default:
+ return;
+ case 0xcc: /* normal write */
+ reg_w(gspca_dev, 0xa0, data[i][2],
+ (data[i][0]) << 8 | data[i][1]);
+ break;
+ case 0xaa: /* i2c op */
+ i2c_write(gspca_dev, data[i][1], &data[i][2], 1);
+ break;
+ case 0xbb: /* i2c op */
+ i2c_write(gspca_dev, data[i][0], &data[i][1], 2);
+ break;
+ case 0xdd:
+ msleep(data[i][1] * 256 + data[i][2] + 10);
+ break;
+ }
+ i++;
+ }
+ /*not reached*/
+}
+
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->bridge = id->driver_info >> 8;
+ sd->flags = id->driver_info & 0xff;
+
+ if (id->idVendor == 0x046d &&
+ (id->idProduct == 0x0892 || id->idProduct == 0x0896))
+ sd->sensor = SENSOR_POxxxx; /* no probe */
+
+ sd->brightness = BRIGHTNESS_DEF;
+ sd->contrast = CONTRAST_DEF;
+ sd->colors = COLOR_DEF;
+ sd->hflip = HFLIP_DEF;
+ sd->vflip = VFLIP_DEF;
+ sd->lightfreq = FREQ_DEF;
+ sd->sharpness = SHARPNESS_DEF;
+ sd->gain = GAIN_DEF;
+ sd->exposure = EXPOSURE_DEF;
+ sd->autogain = AUTOGAIN_DEF;
+ sd->backlight = BACKLIGHT_DEF;
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+ int sensor;
+ /* number of packets per ISOC message */
+ static u8 npkt[NSENSORS] = {
+ [SENSOR_HV7131R] = 64,
+ [SENSOR_MI0360] = 32,
+ [SENSOR_MI1310_SOC] = 32,
+ [SENSOR_MI1320] = 64,
+ [SENSOR_MI1320_SOC] = 128,
+ [SENSOR_OV7660] = 32,
+ [SENSOR_OV7670] = 64,
+ [SENSOR_PO1200] = 128,
+ [SENSOR_PO3130NC] = 128,
+ [SENSOR_POxxxx] = 128,
+ };
+
+ if (sd->sensor != SENSOR_POxxxx)
+ sensor = vc032x_probe_sensor(gspca_dev);
+ else
+ sensor = sd->sensor;
+
+ switch (sensor) {
+ case -1:
+ pr_err("Unknown sensor...\n");
+ return -EINVAL;
+ case SENSOR_HV7131R:
+ PDEBUG(D_PROBE, "Find Sensor HV7131R");
+ break;
+ case SENSOR_MI0360:
+ PDEBUG(D_PROBE, "Find Sensor MI0360");
+ sd->bridge = BRIDGE_VC0323;
+ break;
+ case SENSOR_MI1310_SOC:
+ PDEBUG(D_PROBE, "Find Sensor MI1310_SOC");
+ break;
+ case SENSOR_MI1320:
+ PDEBUG(D_PROBE, "Find Sensor MI1320");
+ break;
+ case SENSOR_MI1320_SOC:
+ PDEBUG(D_PROBE, "Find Sensor MI1320_SOC");
+ break;
+ case SENSOR_OV7660:
+ PDEBUG(D_PROBE, "Find Sensor OV7660");
+ break;
+ case SENSOR_OV7670:
+ PDEBUG(D_PROBE, "Find Sensor OV7670");
+ break;
+ case SENSOR_PO1200:
+ PDEBUG(D_PROBE, "Find Sensor PO1200");
+ break;
+ case SENSOR_PO3130NC:
+ PDEBUG(D_PROBE, "Find Sensor PO3130NC");
+ break;
+ case SENSOR_POxxxx:
+ PDEBUG(D_PROBE, "Sensor POxxxx");
+ break;
+ }
+ sd->sensor = sensor;
+
+ cam = &gspca_dev->cam;
+ if (sd->bridge == BRIDGE_VC0321) {
+ cam->cam_mode = vc0321_mode;
+ cam->nmodes = ARRAY_SIZE(vc0321_mode);
+ } else {
+ switch (sensor) {
+ case SENSOR_PO1200:
+ cam->cam_mode = svga_mode;
+ cam->nmodes = ARRAY_SIZE(svga_mode);
+ break;
+ case SENSOR_MI1310_SOC:
+ cam->cam_mode = vc0323_mode;
+ cam->nmodes = ARRAY_SIZE(vc0323_mode);
+ break;
+ case SENSOR_MI1320_SOC:
+ cam->cam_mode = bi_mode;
+ cam->nmodes = ARRAY_SIZE(bi_mode);
+ break;
+ case SENSOR_OV7670:
+ cam->cam_mode = bi_mode;
+ cam->nmodes = ARRAY_SIZE(bi_mode) - 1;
+ break;
+ default:
+ cam->cam_mode = vc0323_mode;
+ cam->nmodes = ARRAY_SIZE(vc0323_mode) - 1;
+ break;
+ }
+ }
+ cam->npkt = npkt[sd->sensor];
+ gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
+
+ if (sd->sensor == SENSOR_OV7670)
+ sd->flags |= FL_HFLIP | FL_VFLIP;
+
+ if (sd->bridge == BRIDGE_VC0321) {
+ reg_r(gspca_dev, 0x8a, 0, 3);
+ reg_w(gspca_dev, 0x87, 0x00, 0x0f0f);
+ reg_r(gspca_dev, 0x8b, 0, 3);
+ reg_w(gspca_dev, 0x88, 0x00, 0x0202);
+ if (sd->sensor == SENSOR_POxxxx) {
+ reg_r(gspca_dev, 0xa1, 0xb300, 1);
+ if (gspca_dev->usb_buf[0] != 0) {
+ reg_w(gspca_dev, 0xa0, 0x26, 0xb300);
+ reg_w(gspca_dev, 0xa0, 0x04, 0xb300);
+ }
+ reg_w(gspca_dev, 0xa0, 0x00, 0xb300);
+ }
+ }
+ return gspca_dev->usb_err;
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data;
+
+ if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS_IDX))
+ return;
+ data = sd->brightness;
+ if (data >= 0x80)
+ data &= 0x7f;
+ else
+ data = 0xff ^ data;
+ i2c_write(gspca_dev, 0x98, &data, 1);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (gspca_dev->ctrl_dis & (1 << CONTRAST_IDX))
+ return;
+ i2c_write(gspca_dev, 0x99, &sd->contrast, 1);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data;
+
+ if (gspca_dev->ctrl_dis & (1 << COLORS_IDX))
+ return;
+ data = sd->colors - (sd->colors >> 3) - 1;
+ i2c_write(gspca_dev, 0x94, &data, 1);
+ i2c_write(gspca_dev, 0x95, &sd->colors, 1);
+}
+
+static void sethvflip(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data[2], hflip, vflip;
+
+ hflip = sd->hflip;
+ if (sd->flags & FL_HFLIP)
+ hflip = !hflip;
+ vflip = sd->vflip;
+ if (sd->flags & FL_VFLIP)
+ vflip = !vflip;
+ switch (sd->sensor) {
+ case SENSOR_MI1310_SOC:
+ case SENSOR_MI1320:
+ case SENSOR_MI1320_SOC:
+ data[0] = data[1] = 0; /* select page 0 */
+ i2c_write(gspca_dev, 0xf0, data, 2);
+ data[0] = sd->sensor == SENSOR_MI1310_SOC ? 0x03 : 0x01;
+ data[1] = 0x02 * hflip
+ | 0x01 * vflip;
+ i2c_write(gspca_dev, 0x20, data, 2);
+ break;
+ case SENSOR_OV7660:
+ case SENSOR_OV7670:
+ data[0] = sd->sensor == SENSOR_OV7660 ? 0x01 : 0x07;
+ data[0] |= OV7660_MVFP_MIRROR * hflip
+ | OV7660_MVFP_VFLIP * vflip;
+ i2c_write(gspca_dev, OV7660_REG_MVFP, data, 1);
+ break;
+ case SENSOR_PO1200:
+ data[0] = 0;
+ i2c_write(gspca_dev, 0x03, data, 1);
+ data[0] = 0x80 * hflip
+ | 0x40 * vflip
+ | 0x06;
+ i2c_write(gspca_dev, 0x1e, data, 1);
+ break;
+ }
+}
+
+static void setlightfreq(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ static const u8 (*ov7660_freq_tb[3])[4] =
+ {ov7660_NoFliker, ov7660_50HZ, ov7660_60HZ};
+
+ if (sd->sensor != SENSOR_OV7660)
+ return;
+ usb_exchange(gspca_dev, ov7660_freq_tb[sd->lightfreq]);
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data;
+
+ switch (sd->sensor) {
+ case SENSOR_PO1200:
+ data = 0;
+ i2c_write(gspca_dev, 0x03, &data, 1);
+ if (sd->sharpness < 0)
+ data = 0x6a;
+ else
+ data = 0xb5 + sd->sharpness * 3;
+ i2c_write(gspca_dev, 0x61, &data, 1);
+ break;
+ case SENSOR_POxxxx:
+ if (sd->sharpness < 0)
+ data = 0x7e; /* def = max */
+ else
+ data = 0x60 + sd->sharpness * 0x0f;
+ i2c_write(gspca_dev, 0x59, &data, 1);
+ break;
+ }
+}
+static void setgain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (gspca_dev->ctrl_dis & (1 << GAIN_IDX))
+ return;
+ i2c_write(gspca_dev, 0x15, &sd->gain, 1);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data;
+
+ if (gspca_dev->ctrl_dis & (1 << EXPOSURE_IDX))
+ return;
+ data = sd->exposure >> 8;
+ i2c_write(gspca_dev, 0x1a, &data, 1);
+ data = sd->exposure;
+ i2c_write(gspca_dev, 0x1b, &data, 1);
+}
+
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ static const u8 data[2] = {0x28, 0x3c};
+
+ if (gspca_dev->ctrl_dis & (1 << AUTOGAIN_IDX))
+ return;
+ i2c_write(gspca_dev, 0xd1, &data[sd->autogain], 1);
+}
+
+static void setgamma(struct gspca_dev *gspca_dev)
+{
+/*fixme:to do */
+ usb_exchange(gspca_dev, poxxxx_gamma);
+}
+
+static void setbacklight(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 v;
+ u8 data;
+
+ data = (sd->backlight << 4) | 0x0f;
+ i2c_write(gspca_dev, 0xaa, &data, 1);
+ v = 613 + 12 * sd->backlight;
+ data = v >> 8;
+ i2c_write(gspca_dev, 0xc4, &data, 1);
+ data = v;
+ i2c_write(gspca_dev, 0xc5, &data, 1);
+ v = 1093 - 12 * sd->backlight;
+ data = v >> 8;
+ i2c_write(gspca_dev, 0xc6, &data, 1);
+ data = v;
+ i2c_write(gspca_dev, 0xc7, &data, 1);
+ v = 342 + 9 * sd->backlight;
+ data = v >> 8;
+ i2c_write(gspca_dev, 0xc8, &data, 1);
+ data = v;
+ i2c_write(gspca_dev, 0xc9, &data, 1);
+ v = 702 - 9 * sd->backlight;
+ data = v >> 8;
+ i2c_write(gspca_dev, 0xca, &data, 1);
+ data = v;
+ i2c_write(gspca_dev, 0xcb, &data, 1);
+}
+
+static void setwb(struct gspca_dev *gspca_dev)
+{
+/*fixme:to do - valid when reg d1 = 0x1c - (reg16 + reg15 = 0xa3)*/
+ static const u8 data[2] = {0x00, 0x00};
+
+ i2c_write(gspca_dev, 0x16, &data[0], 1);
+ i2c_write(gspca_dev, 0x18, &data[1], 1);
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ const u8 (*init)[4];
+ const u8 *GammaT = NULL;
+ const u8 *MatrixT = NULL;
+ int mode;
+ static const u8 (*mi1320_soc_init[])[4] = {
+ mi1320_soc_InitSXGA,
+ mi1320_soc_InitVGA,
+ mi1320_soc_InitQVGA,
+ };
+
+/*fixme: back sensor only*/
+ if (sd->flags & FL_SAMSUNG) {
+ reg_w(gspca_dev, 0x89, 0xf0ff, 0xffff);
+ reg_w(gspca_dev, 0xa9, 0x8348, 0x000e);
+ reg_w(gspca_dev, 0xa9, 0x0000, 0x001a);
+ }
+
+ /* Assume start use the good resolution from gspca_dev->mode */
+ if (sd->bridge == BRIDGE_VC0321) {
+ reg_w(gspca_dev, 0xa0, 0xff, 0xbfec);
+ reg_w(gspca_dev, 0xa0, 0xff, 0xbfed);
+ reg_w(gspca_dev, 0xa0, 0xff, 0xbfee);
+ reg_w(gspca_dev, 0xa0, 0xff, 0xbfef);
+ sd->image_offset = 46;
+ } else {
+ if (gspca_dev->cam.cam_mode[gspca_dev->curr_mode].pixelformat
+ == V4L2_PIX_FMT_JPEG)
+ sd->image_offset = 0;
+ else
+ sd->image_offset = 32;
+ }
+
+ mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+ switch (sd->sensor) {
+ case SENSOR_HV7131R:
+ GammaT = hv7131r_gamma;
+ MatrixT = hv7131r_matrix;
+ if (mode)
+ init = hv7131r_initQVGA_data; /* 320x240 */
+ else
+ init = hv7131r_initVGA_data; /* 640x480 */
+ break;
+ case SENSOR_OV7660:
+ GammaT = ov7660_gamma;
+ MatrixT = ov7660_matrix;
+ if (mode)
+ init = ov7660_initQVGA_data; /* 320x240 */
+ else
+ init = ov7660_initVGA_data; /* 640x480 */
+ break;
+ case SENSOR_MI0360:
+ GammaT = mi1320_gamma;
+ MatrixT = mi0360_matrix;
+ if (mode)
+ init = mi0360_initQVGA_JPG; /* 320x240 */
+ else
+ init = mi0360_initVGA_JPG; /* 640x480 */
+ break;
+ case SENSOR_MI1310_SOC:
+ GammaT = mi1320_gamma;
+ MatrixT = mi1320_matrix;
+ switch (mode) {
+ case 1:
+ init = mi1310_socinitQVGA_JPG; /* 320x240 */
+ break;
+ case 0:
+ init = mi1310_socinitVGA_JPG; /* 640x480 */
+ break;
+ default:
+ init = mi1310_soc_InitSXGA_JPG; /* 1280x1024 */
+ break;
+ }
+ break;
+ case SENSOR_MI1320:
+ GammaT = mi1320_gamma;
+ MatrixT = mi1320_matrix;
+ if (mode)
+ init = mi1320_initQVGA_data; /* 320x240 */
+ else
+ init = mi1320_initVGA_data; /* 640x480 */
+ break;
+ case SENSOR_MI1320_SOC:
+ GammaT = mi1320_gamma;
+ MatrixT = mi1320_matrix;
+ init = mi1320_soc_init[mode];
+ break;
+ case SENSOR_OV7670:
+ init = mode == 1 ? ov7670_InitVGA : ov7670_InitQVGA;
+ break;
+ case SENSOR_PO3130NC:
+ GammaT = po3130_gamma;
+ MatrixT = po3130_matrix;
+ if (mode)
+ init = po3130_initQVGA_data; /* 320x240 */
+ else
+ init = po3130_initVGA_data; /* 640x480 */
+ usb_exchange(gspca_dev, init);
+ init = po3130_rundata;
+ break;
+ case SENSOR_PO1200:
+ GammaT = po1200_gamma;
+ MatrixT = po1200_matrix;
+ init = po1200_initVGA_data;
+ break;
+ default:
+/* case SENSOR_POxxxx: */
+ usb_exchange(gspca_dev, poxxxx_init_common);
+ setgamma(gspca_dev);
+ setbacklight(gspca_dev);
+ setbrightness(gspca_dev);
+ setcontrast(gspca_dev);
+ setcolors(gspca_dev);
+ setsharpness(gspca_dev);
+ setautogain(gspca_dev);
+ setexposure(gspca_dev);
+ setgain(gspca_dev);
+ usb_exchange(gspca_dev, poxxxx_init_start_3);
+ if (mode)
+ init = poxxxx_initQVGA;
+ else
+ init = poxxxx_initVGA;
+ usb_exchange(gspca_dev, init);
+ reg_r(gspca_dev, 0x8c, 0x0000, 3);
+ reg_w(gspca_dev, 0xa0,
+ gspca_dev->usb_buf[2] & 1 ? 0 : 1,
+ 0xb35c);
+ msleep(300);
+/*fixme: i2c read 04 and 05*/
+ init = poxxxx_init_end_1;
+ break;
+ }
+ usb_exchange(gspca_dev, init);
+ if (GammaT && MatrixT) {
+ put_tab_to_reg(gspca_dev, GammaT, 17, 0xb84a);
+ put_tab_to_reg(gspca_dev, GammaT, 17, 0xb85b);
+ put_tab_to_reg(gspca_dev, GammaT, 17, 0xb86c);
+ put_tab_to_reg(gspca_dev, MatrixT, 9, 0xb82c);
+
+ switch (sd->sensor) {
+ case SENSOR_PO1200:
+ case SENSOR_HV7131R:
+ reg_w(gspca_dev, 0x89, 0x0400, 0x1415);
+ break;
+ case SENSOR_MI1310_SOC:
+ reg_w(gspca_dev, 0x89, 0x058c, 0x0000);
+ break;
+ }
+ msleep(100);
+ sethvflip(gspca_dev);
+ setlightfreq(gspca_dev);
+ }
+ switch (sd->sensor) {
+ case SENSOR_OV7670:
+ reg_w(gspca_dev, 0x87, 0xffff, 0xffff);
+ reg_w(gspca_dev, 0x88, 0xff00, 0xf0f1);
+ reg_w(gspca_dev, 0xa0, 0x0000, 0xbfff);
+ break;
+ case SENSOR_POxxxx:
+ usb_exchange(gspca_dev, poxxxx_init_end_2);
+ setwb(gspca_dev);
+ msleep(80); /* led on */
+ reg_w(gspca_dev, 0x89, 0xffff, 0xfdff);
+ break;
+ }
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->sensor) {
+ case SENSOR_MI1310_SOC:
+ reg_w(gspca_dev, 0x89, 0x058c, 0x00ff);
+ break;
+ case SENSOR_POxxxx:
+ return;
+ default:
+ if (!(sd->flags & FL_SAMSUNG))
+ reg_w(gspca_dev, 0x89, 0xffff, 0xffff);
+ break;
+ }
+ reg_w(gspca_dev, 0xa0, 0x01, 0xb301);
+ reg_w(gspca_dev, 0xa0, 0x09, 0xb003);
+}
+
+/* called on streamoff with alt 0 and on disconnect */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (!gspca_dev->present)
+ return;
+/*fixme: is this useful?*/
+ if (sd->sensor == SENSOR_MI1310_SOC)
+ reg_w(gspca_dev, 0x89, 0x058c, 0x00ff);
+ else if (!(sd->flags & FL_SAMSUNG))
+ reg_w(gspca_dev, 0x89, 0xffff, 0xffff);
+
+ if (sd->sensor == SENSOR_POxxxx) {
+ reg_w(gspca_dev, 0xa0, 0x26, 0xb300);
+ reg_w(gspca_dev, 0xa0, 0x04, 0xb300);
+ reg_w(gspca_dev, 0xa0, 0x00, 0xb300);
+ }
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso pkt length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (data[0] == 0xff && data[1] == 0xd8) {
+ PDEBUG(D_PACK,
+ "vc032x header packet found len %d", len);
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+ data += sd->image_offset;
+ len -= sd->image_offset;
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+ return;
+ }
+
+ /* The vc0321 sends some additional data after sending the complete
+ * frame, we ignore this. */
+ if (sd->bridge == BRIDGE_VC0321) {
+ int size, l;
+
+ l = gspca_dev->image_len;
+ size = gspca_dev->frsz;
+ if (len > size - l)
+ len = size - l;
+ }
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->brightness = val;
+ if (gspca_dev->streaming)
+ setbrightness(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->brightness;
+ return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->contrast = val;
+ if (gspca_dev->streaming)
+ setcontrast(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->contrast;
+ return 0;
+}
+
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->colors = val;
+ if (gspca_dev->streaming)
+ setcolors(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->colors;
+ return 0;
+}
+
+static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->hflip = val;
+ if (gspca_dev->streaming)
+ sethvflip(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->hflip;
+ return 0;
+}
+
+static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->vflip = val;
+ if (gspca_dev->streaming)
+ sethvflip(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->vflip;
+ return 0;
+}
+
+static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->lightfreq = val;
+ if (gspca_dev->streaming)
+ setlightfreq(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->lightfreq;
+ return 0;
+}
+
+static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->sharpness = val;
+ if (gspca_dev->streaming)
+ setsharpness(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->sharpness;
+ return 0;
+}
+
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->gain = val;
+ if (gspca_dev->streaming)
+ setgain(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->gain;
+ return 0;
+}
+
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->exposure = val;
+ if (gspca_dev->streaming)
+ setexposure(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->exposure;
+ return 0;
+}
+
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->autogain = val;
+ if (gspca_dev->streaming)
+ setautogain(gspca_dev);
+
+ return gspca_dev->usb_err;
+}
+
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->autogain;
+ return 0;
+}
+
+static int sd_setbacklight(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->backlight = val;
+ if (gspca_dev->streaming)
+ setbacklight(gspca_dev);
+
+ return gspca_dev->usb_err;
+}
+
+static int sd_getbacklight(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->backlight;
+ return 0;
+}
+
+static int sd_querymenu(struct gspca_dev *gspca_dev,
+ struct v4l2_querymenu *menu)
+{
+ static const char *freq_nm[3] = {"NoFliker", "50 Hz", "60 Hz"};
+
+ switch (menu->id) {
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ if (menu->index >= ARRAY_SIZE(freq_nm))
+ break;
+ strcpy((char *) menu->name, freq_nm[menu->index]);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+ .querymenu = sd_querymenu,
+};
+
+/* -- module initialisation -- */
+#define BF(bridge, flags) \
+ .driver_info = (BRIDGE_ ## bridge << 8) \
+ | (flags)
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x041e, 0x405b), BF(VC0323, FL_VFLIP)},
+ {USB_DEVICE(0x046d, 0x0892), BF(VC0321, 0)},
+ {USB_DEVICE(0x046d, 0x0896), BF(VC0321, 0)},
+ {USB_DEVICE(0x046d, 0x0897), BF(VC0321, 0)},
+ {USB_DEVICE(0x0ac8, 0x0321), BF(VC0321, 0)},
+ {USB_DEVICE(0x0ac8, 0x0323), BF(VC0323, 0)},
+ {USB_DEVICE(0x0ac8, 0x0328), BF(VC0321, 0)},
+ {USB_DEVICE(0x0ac8, 0xc001), BF(VC0321, 0)},
+ {USB_DEVICE(0x0ac8, 0xc002), BF(VC0321, 0)},
+ {USB_DEVICE(0x0ac8, 0xc301), BF(VC0323, FL_SAMSUNG)},
+ {USB_DEVICE(0x15b8, 0x6001), BF(VC0323, 0)},
+ {USB_DEVICE(0x15b8, 0x6002), BF(VC0323, 0)},
+ {USB_DEVICE(0x17ef, 0x4802), BF(VC0323, 0)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/vicam.c b/drivers/media/video/gspca/vicam.c
new file mode 100644
index 00000000..911152e1
--- /dev/null
+++ b/drivers/media/video/gspca/vicam.c
@@ -0,0 +1,372 @@
+/*
+ * gspca ViCam subdriver
+ *
+ * Copyright (C) 2011 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Based on the usbvideo vicam driver, which is:
+ *
+ * Copyright (c) 2002 Joe Burks (jburks@wavicle.org),
+ * Christopher L Cheney (ccheney@cheney.cx),
+ * Pavel Machek (pavel@ucw.cz),
+ * John Tyner (jtyner@cs.ucr.edu),
+ * Monroe Williams (monroe@pobox.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "vicam"
+#define HEADER_SIZE 64
+
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/ihex.h>
+#include "gspca.h"
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("GSPCA ViCam USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+enum e_ctrl {
+ GAIN,
+ EXPOSURE,
+ NCTRL /* number of controls */
+};
+
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ struct work_struct work_struct;
+ struct workqueue_struct *work_thread;
+ struct gspca_ctrl ctrls[NCTRL];
+};
+
+/* The vicam sensor has a resolution of 512 x 244, with I believe square
+ pixels, but this is forced to a 4:3 ratio by optics. So it has
+ non square pixels :( */
+static struct v4l2_pix_format vicam_mode[] = {
+ { 256, 122, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+ .bytesperline = 256,
+ .sizeimage = 256 * 122,
+ .colorspace = V4L2_COLORSPACE_SRGB,},
+ /* 2 modes with somewhat more square pixels */
+ { 256, 200, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+ .bytesperline = 256,
+ .sizeimage = 256 * 200,
+ .colorspace = V4L2_COLORSPACE_SRGB,},
+ { 256, 240, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+ .bytesperline = 256,
+ .sizeimage = 256 * 240,
+ .colorspace = V4L2_COLORSPACE_SRGB,},
+#if 0 /* This mode has extremely non square pixels, testing use only */
+ { 512, 122, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+ .bytesperline = 512,
+ .sizeimage = 512 * 122,
+ .colorspace = V4L2_COLORSPACE_SRGB,},
+#endif
+ { 512, 244, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+ .bytesperline = 512,
+ .sizeimage = 512 * 244,
+ .colorspace = V4L2_COLORSPACE_SRGB,},
+};
+
+static const struct ctrl sd_ctrls[] = {
+[GAIN] = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 200,
+ },
+ },
+[EXPOSURE] = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 0,
+ .maximum = 2047,
+ .step = 1,
+ .default_value = 256,
+ },
+ },
+};
+
+static int vicam_control_msg(struct gspca_dev *gspca_dev, u8 request,
+ u16 value, u16 index, u8 *data, u16 len)
+{
+ int ret;
+
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ request,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, data, len, 1000);
+ if (ret < 0)
+ pr_err("control msg req %02X error %d\n", request, ret);
+
+ return ret;
+}
+
+static int vicam_set_camera_power(struct gspca_dev *gspca_dev, int state)
+{
+ int ret;
+
+ ret = vicam_control_msg(gspca_dev, 0x50, state, 0, NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ if (state)
+ ret = vicam_control_msg(gspca_dev, 0x55, 1, 0, NULL, 0);
+
+ return ret;
+}
+
+/*
+ * request and read a block of data - see warning on vicam_command.
+ */
+static int vicam_read_frame(struct gspca_dev *gspca_dev, u8 *data, int size)
+{
+ struct sd *sd = (struct sd *)gspca_dev;
+ int ret, unscaled_height, act_len = 0;
+ u8 *req_data = gspca_dev->usb_buf;
+
+ memset(req_data, 0, 16);
+ req_data[0] = sd->ctrls[GAIN].val;
+ if (gspca_dev->width == 256)
+ req_data[1] |= 0x01; /* low nibble x-scale */
+ if (gspca_dev->height <= 122) {
+ req_data[1] |= 0x10; /* high nibble y-scale */
+ unscaled_height = gspca_dev->height * 2;
+ } else
+ unscaled_height = gspca_dev->height;
+ req_data[2] = 0x90; /* unknown, does not seem to do anything */
+ if (unscaled_height <= 200)
+ req_data[3] = 0x06; /* vend? */
+ else if (unscaled_height <= 242) /* Yes 242 not 240 */
+ req_data[3] = 0x07; /* vend? */
+ else /* Up to 244 lines with req_data[3] == 0x08 */
+ req_data[3] = 0x08; /* vend? */
+
+ if (sd->ctrls[EXPOSURE].val < 256) {
+ /* Frame rate maxed out, use partial frame expo time */
+ req_data[4] = 255 - sd->ctrls[EXPOSURE].val;
+ req_data[5] = 0x00;
+ req_data[6] = 0x00;
+ req_data[7] = 0x01;
+ } else {
+ /* Modify frame rate */
+ req_data[4] = 0x00;
+ req_data[5] = 0x00;
+ req_data[6] = sd->ctrls[EXPOSURE].val & 0xFF;
+ req_data[7] = sd->ctrls[EXPOSURE].val >> 8;
+ }
+ req_data[8] = ((244 - unscaled_height) / 2) & ~0x01; /* vstart */
+ /* bytes 9-15 do not seem to affect exposure or image quality */
+
+ mutex_lock(&gspca_dev->usb_lock);
+ ret = vicam_control_msg(gspca_dev, 0x51, 0x80, 0, req_data, 16);
+ mutex_unlock(&gspca_dev->usb_lock);
+ if (ret < 0)
+ return ret;
+
+ ret = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x81),
+ data, size, &act_len, 10000);
+ /* successful, it returns 0, otherwise negative */
+ if (ret < 0 || act_len != size) {
+ pr_err("bulk read fail (%d) len %d/%d\n",
+ ret, act_len, size);
+ return -EIO;
+ }
+ return 0;
+}
+
+/* This function is called as a workqueue function and runs whenever the camera
+ * is streaming data. Because it is a workqueue function it is allowed to sleep
+ * so we can use synchronous USB calls. To avoid possible collisions with other
+ * threads attempting to use the camera's USB interface we take the gspca
+ * usb_lock when performing USB operations. In practice the only thing we need
+ * to protect against is the usb_set_interface call that gspca makes during
+ * stream_off as the camera doesn't provide any controls that the user could try
+ * to change.
+ */
+static void vicam_dostream(struct work_struct *work)
+{
+ struct sd *sd = container_of(work, struct sd, work_struct);
+ struct gspca_dev *gspca_dev = &sd->gspca_dev;
+ int ret, frame_sz;
+ u8 *buffer;
+
+ frame_sz = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].sizeimage +
+ HEADER_SIZE;
+ buffer = kmalloc(frame_sz, GFP_KERNEL | GFP_DMA);
+ if (!buffer) {
+ pr_err("Couldn't allocate USB buffer\n");
+ goto exit;
+ }
+
+ while (gspca_dev->present && gspca_dev->streaming) {
+ ret = vicam_read_frame(gspca_dev, buffer, frame_sz);
+ if (ret < 0)
+ break;
+
+ /* Note the frame header contents seem to be completely
+ constant, they do not change with either image, or
+ settings. So we simply discard it. The frames have
+ a very similar 64 byte footer, which we don't even
+ bother reading from the cam */
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ buffer + HEADER_SIZE,
+ frame_sz - HEADER_SIZE);
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+ }
+exit:
+ kfree(buffer);
+}
+
+/* This function is called at probe time just before sd_init */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct cam *cam = &gspca_dev->cam;
+ struct sd *sd = (struct sd *)gspca_dev;
+
+ /* We don't use the buffer gspca allocates so make it small. */
+ cam->bulk = 1;
+ cam->bulk_size = 64;
+ cam->cam_mode = vicam_mode;
+ cam->nmodes = ARRAY_SIZE(vicam_mode);
+ cam->ctrls = sd->ctrls;
+
+ INIT_WORK(&sd->work_struct, vicam_dostream);
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ int ret;
+ const struct ihex_binrec *rec;
+ const struct firmware *uninitialized_var(fw);
+ u8 *firmware_buf;
+
+ ret = request_ihex_firmware(&fw, "vicam/firmware.fw",
+ &gspca_dev->dev->dev);
+ if (ret) {
+ pr_err("Failed to load \"vicam/firmware.fw\": %d\n", ret);
+ return ret;
+ }
+
+ firmware_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!firmware_buf) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+ for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) {
+ memcpy(firmware_buf, rec->data, be16_to_cpu(rec->len));
+ ret = vicam_control_msg(gspca_dev, 0xff, 0, 0, firmware_buf,
+ be16_to_cpu(rec->len));
+ if (ret < 0)
+ break;
+ }
+
+ kfree(firmware_buf);
+exit:
+ release_firmware(fw);
+ return ret;
+}
+
+/* Set up for getting frames. */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *)gspca_dev;
+ int ret;
+
+ ret = vicam_set_camera_power(gspca_dev, 1);
+ if (ret < 0)
+ return ret;
+
+ /* Start the workqueue function to do the streaming */
+ sd->work_thread = create_singlethread_workqueue(MODULE_NAME);
+ queue_work(sd->work_thread, &sd->work_struct);
+
+ return 0;
+}
+
+/* called on streamoff with alt==0 and on disconnect */
+/* the usb_lock is held at entry - restore on exit */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ struct sd *dev = (struct sd *)gspca_dev;
+
+ /* wait for the work queue to terminate */
+ mutex_unlock(&gspca_dev->usb_lock);
+ /* This waits for vicam_dostream to finish */
+ destroy_workqueue(dev->work_thread);
+ dev->work_thread = NULL;
+ mutex_lock(&gspca_dev->usb_lock);
+
+ if (gspca_dev->present)
+ vicam_set_camera_power(gspca_dev, 0);
+}
+
+/* Table of supported USB devices */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x04c1, 0x009d)},
+ {USB_DEVICE(0x0602, 0x1001)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stop0 = sd_stop0,
+};
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id,
+ &sd_desc,
+ sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/w996Xcf.c b/drivers/media/video/gspca/w996Xcf.c
new file mode 100644
index 00000000..27d2cef0
--- /dev/null
+++ b/drivers/media/video/gspca/w996Xcf.c
@@ -0,0 +1,560 @@
+/**
+ *
+ * GSPCA sub driver for W996[78]CF JPEG USB Dual Mode Camera Chip.
+ *
+ * Copyright (C) 2009 Hans de Goede <hdegoede@redhat.com>
+ *
+ * This module is adapted from the in kernel v4l1 w9968cf driver:
+ *
+ * Copyright (C) 2002-2004 by Luca Risolia <luca.risolia@studio.unibo.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* Note this is not a stand alone driver, it gets included in ov519.c, this
+ is a bit of a hack, but it needs the driver code for a lot of different
+ ov sensors which is already present in ov519.c (the old v4l1 driver used
+ the ovchipcam framework). When we have the time we really should move
+ the sensor drivers to v4l2 sub drivers, and properly split of this
+ driver from ov519.c */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define W9968CF_I2C_BUS_DELAY 4 /* delay in us for I2C bit r/w operations */
+
+#define Y_QUANTABLE (&sd->jpeg_hdr[JPEG_QT0_OFFSET])
+#define UV_QUANTABLE (&sd->jpeg_hdr[JPEG_QT1_OFFSET])
+
+static const struct v4l2_pix_format w9968cf_vga_mode[] = {
+ {160, 120, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE,
+ .bytesperline = 160 * 2,
+ .sizeimage = 160 * 120 * 2,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+ {176, 144, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE,
+ .bytesperline = 176 * 2,
+ .sizeimage = 176 * 144 * 2,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320 * 2,
+ .sizeimage = 320 * 240 * 2,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+ {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 352 * 2,
+ .sizeimage = 352 * 288 * 2,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640 * 2,
+ .sizeimage = 640 * 480 * 2,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+};
+
+static void reg_w(struct sd *sd, u16 index, u16 value);
+
+/*--------------------------------------------------------------------------
+ Write 64-bit data to the fast serial bus registers.
+ Return 0 on success, -1 otherwise.
+ --------------------------------------------------------------------------*/
+static void w9968cf_write_fsb(struct sd *sd, u16* data)
+{
+ struct usb_device *udev = sd->gspca_dev.dev;
+ u16 value;
+ int ret;
+
+ if (sd->gspca_dev.usb_err < 0)
+ return;
+
+ value = *data++;
+ memcpy(sd->gspca_dev.usb_buf, data, 6);
+
+ ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0,
+ USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+ value, 0x06, sd->gspca_dev.usb_buf, 6, 500);
+ if (ret < 0) {
+ pr_err("Write FSB registers failed (%d)\n", ret);
+ sd->gspca_dev.usb_err = ret;
+ }
+}
+
+/*--------------------------------------------------------------------------
+ Write data to the serial bus control register.
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static void w9968cf_write_sb(struct sd *sd, u16 value)
+{
+ int ret;
+
+ if (sd->gspca_dev.usb_err < 0)
+ return;
+
+ /* We don't use reg_w here, as that would cause all writes when
+ bitbanging i2c to be logged, making the logs impossible to read */
+ ret = usb_control_msg(sd->gspca_dev.dev,
+ usb_sndctrlpipe(sd->gspca_dev.dev, 0),
+ 0,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, 0x01, NULL, 0, 500);
+
+ udelay(W9968CF_I2C_BUS_DELAY);
+
+ if (ret < 0) {
+ pr_err("Write SB reg [01] %04x failed\n", value);
+ sd->gspca_dev.usb_err = ret;
+ }
+}
+
+/*--------------------------------------------------------------------------
+ Read data from the serial bus control register.
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_read_sb(struct sd *sd)
+{
+ int ret;
+
+ if (sd->gspca_dev.usb_err < 0)
+ return -1;
+
+ /* We don't use reg_r here, as the w9968cf is special and has 16
+ bit registers instead of 8 bit */
+ ret = usb_control_msg(sd->gspca_dev.dev,
+ usb_rcvctrlpipe(sd->gspca_dev.dev, 0),
+ 1,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, 0x01, sd->gspca_dev.usb_buf, 2, 500);
+ if (ret >= 0) {
+ ret = sd->gspca_dev.usb_buf[0] |
+ (sd->gspca_dev.usb_buf[1] << 8);
+ } else {
+ pr_err("Read SB reg [01] failed\n");
+ sd->gspca_dev.usb_err = ret;
+ }
+
+ udelay(W9968CF_I2C_BUS_DELAY);
+
+ return ret;
+}
+
+/*--------------------------------------------------------------------------
+ Upload quantization tables for the JPEG compression.
+ This function is called by w9968cf_start_transfer().
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static void w9968cf_upload_quantizationtables(struct sd *sd)
+{
+ u16 a, b;
+ int i, j;
+
+ reg_w(sd, 0x39, 0x0010); /* JPEG clock enable */
+
+ for (i = 0, j = 0; i < 32; i++, j += 2) {
+ a = Y_QUANTABLE[j] | ((unsigned)(Y_QUANTABLE[j + 1]) << 8);
+ b = UV_QUANTABLE[j] | ((unsigned)(UV_QUANTABLE[j + 1]) << 8);
+ reg_w(sd, 0x40 + i, a);
+ reg_w(sd, 0x60 + i, b);
+ }
+ reg_w(sd, 0x39, 0x0012); /* JPEG encoder enable */
+}
+
+/****************************************************************************
+ * Low-level I2C I/O functions. *
+ * The adapter supports the following I2C transfer functions: *
+ * i2c_adap_fastwrite_byte_data() (at 400 kHz bit frequency only) *
+ * i2c_adap_read_byte_data() *
+ * i2c_adap_read_byte() *
+ ****************************************************************************/
+
+static void w9968cf_smbus_start(struct sd *sd)
+{
+ w9968cf_write_sb(sd, 0x0011); /* SDE=1, SDA=0, SCL=1 */
+ w9968cf_write_sb(sd, 0x0010); /* SDE=1, SDA=0, SCL=0 */
+}
+
+static void w9968cf_smbus_stop(struct sd *sd)
+{
+ w9968cf_write_sb(sd, 0x0010); /* SDE=1, SDA=0, SCL=0 */
+ w9968cf_write_sb(sd, 0x0011); /* SDE=1, SDA=0, SCL=1 */
+ w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */
+}
+
+static void w9968cf_smbus_write_byte(struct sd *sd, u8 v)
+{
+ u8 bit;
+ int sda;
+
+ for (bit = 0 ; bit < 8 ; bit++) {
+ sda = (v & 0x80) ? 2 : 0;
+ v <<= 1;
+ /* SDE=1, SDA=sda, SCL=0 */
+ w9968cf_write_sb(sd, 0x10 | sda);
+ /* SDE=1, SDA=sda, SCL=1 */
+ w9968cf_write_sb(sd, 0x11 | sda);
+ /* SDE=1, SDA=sda, SCL=0 */
+ w9968cf_write_sb(sd, 0x10 | sda);
+ }
+}
+
+static void w9968cf_smbus_read_byte(struct sd *sd, u8 *v)
+{
+ u8 bit;
+
+ /* No need to ensure SDA is high as we are always called after
+ read_ack which ends with SDA high */
+ *v = 0;
+ for (bit = 0 ; bit < 8 ; bit++) {
+ *v <<= 1;
+ /* SDE=1, SDA=1, SCL=1 */
+ w9968cf_write_sb(sd, 0x0013);
+ *v |= (w9968cf_read_sb(sd) & 0x0008) ? 1 : 0;
+ /* SDE=1, SDA=1, SCL=0 */
+ w9968cf_write_sb(sd, 0x0012);
+ }
+}
+
+static void w9968cf_smbus_write_nack(struct sd *sd)
+{
+ /* No need to ensure SDA is high as we are always called after
+ read_byte which ends with SDA high */
+ w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */
+ w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */
+}
+
+static void w9968cf_smbus_read_ack(struct sd *sd)
+{
+ int sda;
+
+ /* Ensure SDA is high before raising clock to avoid a spurious stop */
+ w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */
+ w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */
+ sda = w9968cf_read_sb(sd);
+ w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */
+ if (sda >= 0 && (sda & 0x08)) {
+ PDEBUG(D_USBI, "Did not receive i2c ACK");
+ sd->gspca_dev.usb_err = -EIO;
+ }
+}
+
+/* SMBus protocol: S Addr Wr [A] Subaddr [A] Value [A] P */
+static void w9968cf_i2c_w(struct sd *sd, u8 reg, u8 value)
+{
+ u16* data = (u16 *)sd->gspca_dev.usb_buf;
+
+ data[0] = 0x082f | ((sd->sensor_addr & 0x80) ? 0x1500 : 0x0);
+ data[0] |= (sd->sensor_addr & 0x40) ? 0x4000 : 0x0;
+ data[1] = 0x2082 | ((sd->sensor_addr & 0x40) ? 0x0005 : 0x0);
+ data[1] |= (sd->sensor_addr & 0x20) ? 0x0150 : 0x0;
+ data[1] |= (sd->sensor_addr & 0x10) ? 0x5400 : 0x0;
+ data[2] = 0x8208 | ((sd->sensor_addr & 0x08) ? 0x0015 : 0x0);
+ data[2] |= (sd->sensor_addr & 0x04) ? 0x0540 : 0x0;
+ data[2] |= (sd->sensor_addr & 0x02) ? 0x5000 : 0x0;
+ data[3] = 0x1d20 | ((sd->sensor_addr & 0x02) ? 0x0001 : 0x0);
+ data[3] |= (sd->sensor_addr & 0x01) ? 0x0054 : 0x0;
+
+ w9968cf_write_fsb(sd, data);
+
+ data[0] = 0x8208 | ((reg & 0x80) ? 0x0015 : 0x0);
+ data[0] |= (reg & 0x40) ? 0x0540 : 0x0;
+ data[0] |= (reg & 0x20) ? 0x5000 : 0x0;
+ data[1] = 0x0820 | ((reg & 0x20) ? 0x0001 : 0x0);
+ data[1] |= (reg & 0x10) ? 0x0054 : 0x0;
+ data[1] |= (reg & 0x08) ? 0x1500 : 0x0;
+ data[1] |= (reg & 0x04) ? 0x4000 : 0x0;
+ data[2] = 0x2082 | ((reg & 0x04) ? 0x0005 : 0x0);
+ data[2] |= (reg & 0x02) ? 0x0150 : 0x0;
+ data[2] |= (reg & 0x01) ? 0x5400 : 0x0;
+ data[3] = 0x001d;
+
+ w9968cf_write_fsb(sd, data);
+
+ data[0] = 0x8208 | ((value & 0x80) ? 0x0015 : 0x0);
+ data[0] |= (value & 0x40) ? 0x0540 : 0x0;
+ data[0] |= (value & 0x20) ? 0x5000 : 0x0;
+ data[1] = 0x0820 | ((value & 0x20) ? 0x0001 : 0x0);
+ data[1] |= (value & 0x10) ? 0x0054 : 0x0;
+ data[1] |= (value & 0x08) ? 0x1500 : 0x0;
+ data[1] |= (value & 0x04) ? 0x4000 : 0x0;
+ data[2] = 0x2082 | ((value & 0x04) ? 0x0005 : 0x0);
+ data[2] |= (value & 0x02) ? 0x0150 : 0x0;
+ data[2] |= (value & 0x01) ? 0x5400 : 0x0;
+ data[3] = 0xfe1d;
+
+ w9968cf_write_fsb(sd, data);
+
+ PDEBUG(D_USBO, "i2c 0x%02x -> [0x%02x]", value, reg);
+}
+
+/* SMBus protocol: S Addr Wr [A] Subaddr [A] P S Addr+1 Rd [A] [Value] NA P */
+static int w9968cf_i2c_r(struct sd *sd, u8 reg)
+{
+ int ret = 0;
+ u8 value;
+
+ /* Fast serial bus data control disable */
+ w9968cf_write_sb(sd, 0x0013); /* don't change ! */
+
+ w9968cf_smbus_start(sd);
+ w9968cf_smbus_write_byte(sd, sd->sensor_addr);
+ w9968cf_smbus_read_ack(sd);
+ w9968cf_smbus_write_byte(sd, reg);
+ w9968cf_smbus_read_ack(sd);
+ w9968cf_smbus_stop(sd);
+ w9968cf_smbus_start(sd);
+ w9968cf_smbus_write_byte(sd, sd->sensor_addr + 1);
+ w9968cf_smbus_read_ack(sd);
+ w9968cf_smbus_read_byte(sd, &value);
+ /* signal we don't want to read anymore, the v4l1 driver used to
+ send an ack here which is very wrong! (and then fixed
+ the issues this gave by retrying reads) */
+ w9968cf_smbus_write_nack(sd);
+ w9968cf_smbus_stop(sd);
+
+ /* Fast serial bus data control re-enable */
+ w9968cf_write_sb(sd, 0x0030);
+
+ if (sd->gspca_dev.usb_err >= 0) {
+ ret = value;
+ PDEBUG(D_USBI, "i2c [0x%02X] -> 0x%02X", reg, value);
+ } else
+ PDEBUG(D_ERR, "i2c read [0x%02x] failed", reg);
+
+ return ret;
+}
+
+/*--------------------------------------------------------------------------
+ Turn on the LED on some webcams. A beep should be heard too.
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static void w9968cf_configure(struct sd *sd)
+{
+ reg_w(sd, 0x00, 0xff00); /* power-down */
+ reg_w(sd, 0x00, 0xbf17); /* reset everything */
+ reg_w(sd, 0x00, 0xbf10); /* normal operation */
+ reg_w(sd, 0x01, 0x0010); /* serial bus, SDS high */
+ reg_w(sd, 0x01, 0x0000); /* serial bus, SDS low */
+ reg_w(sd, 0x01, 0x0010); /* ..high 'beep-beep' */
+ reg_w(sd, 0x01, 0x0030); /* Set sda scl to FSB mode */
+
+ sd->stopped = 1;
+}
+
+static void w9968cf_init(struct sd *sd)
+{
+ unsigned long hw_bufsize = sd->sif ? (352 * 288 * 2) : (640 * 480 * 2),
+ y0 = 0x0000,
+ u0 = y0 + hw_bufsize / 2,
+ v0 = u0 + hw_bufsize / 4,
+ y1 = v0 + hw_bufsize / 4,
+ u1 = y1 + hw_bufsize / 2,
+ v1 = u1 + hw_bufsize / 4;
+
+ reg_w(sd, 0x00, 0xff00); /* power off */
+ reg_w(sd, 0x00, 0xbf10); /* power on */
+
+ reg_w(sd, 0x03, 0x405d); /* DRAM timings */
+ reg_w(sd, 0x04, 0x0030); /* SDRAM timings */
+
+ reg_w(sd, 0x20, y0 & 0xffff); /* Y buf.0, low */
+ reg_w(sd, 0x21, y0 >> 16); /* Y buf.0, high */
+ reg_w(sd, 0x24, u0 & 0xffff); /* U buf.0, low */
+ reg_w(sd, 0x25, u0 >> 16); /* U buf.0, high */
+ reg_w(sd, 0x28, v0 & 0xffff); /* V buf.0, low */
+ reg_w(sd, 0x29, v0 >> 16); /* V buf.0, high */
+
+ reg_w(sd, 0x22, y1 & 0xffff); /* Y buf.1, low */
+ reg_w(sd, 0x23, y1 >> 16); /* Y buf.1, high */
+ reg_w(sd, 0x26, u1 & 0xffff); /* U buf.1, low */
+ reg_w(sd, 0x27, u1 >> 16); /* U buf.1, high */
+ reg_w(sd, 0x2a, v1 & 0xffff); /* V buf.1, low */
+ reg_w(sd, 0x2b, v1 >> 16); /* V buf.1, high */
+
+ reg_w(sd, 0x32, y1 & 0xffff); /* JPEG buf 0 low */
+ reg_w(sd, 0x33, y1 >> 16); /* JPEG buf 0 high */
+
+ reg_w(sd, 0x34, y1 & 0xffff); /* JPEG buf 1 low */
+ reg_w(sd, 0x35, y1 >> 16); /* JPEG bug 1 high */
+
+ reg_w(sd, 0x36, 0x0000);/* JPEG restart interval */
+ reg_w(sd, 0x37, 0x0804);/*JPEG VLE FIFO threshold*/
+ reg_w(sd, 0x38, 0x0000);/* disable hw up-scaling */
+ reg_w(sd, 0x3f, 0x0000); /* JPEG/MCTL test data */
+}
+
+static void w9968cf_set_crop_window(struct sd *sd)
+{
+ int start_cropx, start_cropy, x, y, fw, fh, cw, ch,
+ max_width, max_height;
+
+ if (sd->sif) {
+ max_width = 352;
+ max_height = 288;
+ } else {
+ max_width = 640;
+ max_height = 480;
+ }
+
+ if (sd->sensor == SEN_OV7620) {
+ /* Sigh, this is dependend on the clock / framerate changes
+ made by the frequency control, sick. */
+ if (sd->ctrls[FREQ].val == 1) {
+ start_cropx = 277;
+ start_cropy = 37;
+ } else {
+ start_cropx = 105;
+ start_cropy = 37;
+ }
+ } else {
+ start_cropx = 320;
+ start_cropy = 35;
+ }
+
+ /* Work around to avoid FP arithmetics */
+ #define SC(x) ((x) << 10)
+
+ /* Scaling factors */
+ fw = SC(sd->gspca_dev.width) / max_width;
+ fh = SC(sd->gspca_dev.height) / max_height;
+
+ cw = (fw >= fh) ? max_width : SC(sd->gspca_dev.width) / fh;
+ ch = (fw >= fh) ? SC(sd->gspca_dev.height) / fw : max_height;
+
+ sd->sensor_width = max_width;
+ sd->sensor_height = max_height;
+
+ x = (max_width - cw) / 2;
+ y = (max_height - ch) / 2;
+
+ reg_w(sd, 0x10, start_cropx + x);
+ reg_w(sd, 0x11, start_cropy + y);
+ reg_w(sd, 0x12, start_cropx + x + cw);
+ reg_w(sd, 0x13, start_cropy + y + ch);
+}
+
+static void w9968cf_mode_init_regs(struct sd *sd)
+{
+ int val, vs_polarity, hs_polarity;
+
+ w9968cf_set_crop_window(sd);
+
+ reg_w(sd, 0x14, sd->gspca_dev.width);
+ reg_w(sd, 0x15, sd->gspca_dev.height);
+
+ /* JPEG width & height */
+ reg_w(sd, 0x30, sd->gspca_dev.width);
+ reg_w(sd, 0x31, sd->gspca_dev.height);
+
+ /* Y & UV frame buffer strides (in WORD) */
+ if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat ==
+ V4L2_PIX_FMT_JPEG) {
+ reg_w(sd, 0x2c, sd->gspca_dev.width / 2);
+ reg_w(sd, 0x2d, sd->gspca_dev.width / 4);
+ } else
+ reg_w(sd, 0x2c, sd->gspca_dev.width);
+
+ reg_w(sd, 0x00, 0xbf17); /* reset everything */
+ reg_w(sd, 0x00, 0xbf10); /* normal operation */
+
+ /* Transfer size in WORDS (for UYVY format only) */
+ val = sd->gspca_dev.width * sd->gspca_dev.height;
+ reg_w(sd, 0x3d, val & 0xffff); /* low bits */
+ reg_w(sd, 0x3e, val >> 16); /* high bits */
+
+ if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat ==
+ V4L2_PIX_FMT_JPEG) {
+ /* We may get called multiple times (usb isoc bw negotiat.) */
+ jpeg_define(sd->jpeg_hdr, sd->gspca_dev.height,
+ sd->gspca_dev.width, 0x22); /* JPEG 420 */
+ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+ w9968cf_upload_quantizationtables(sd);
+ }
+
+ /* Video Capture Control Register */
+ if (sd->sensor == SEN_OV7620) {
+ /* Seems to work around a bug in the image sensor */
+ vs_polarity = 1;
+ hs_polarity = 1;
+ } else {
+ vs_polarity = 1;
+ hs_polarity = 0;
+ }
+
+ val = (vs_polarity << 12) | (hs_polarity << 11);
+
+ /* NOTE: We may not have enough memory to do double buffering while
+ doing compression (amount of memory differs per model cam).
+ So we use the second image buffer also as jpeg stream buffer
+ (see w9968cf_init), and disable double buffering. */
+ if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat ==
+ V4L2_PIX_FMT_JPEG) {
+ /* val |= 0x0002; YUV422P */
+ val |= 0x0003; /* YUV420P */
+ } else
+ val |= 0x0080; /* Enable HW double buffering */
+
+ /* val |= 0x0020; enable clamping */
+ /* val |= 0x0008; enable (1-2-1) filter */
+ /* val |= 0x000c; enable (2-3-6-3-2) filter */
+
+ val |= 0x8000; /* capt. enable */
+
+ reg_w(sd, 0x16, val);
+
+ sd->gspca_dev.empty_packet = 0;
+}
+
+static void w9968cf_stop0(struct sd *sd)
+{
+ reg_w(sd, 0x39, 0x0000); /* disable JPEG encoder */
+ reg_w(sd, 0x16, 0x0000); /* stop video capture */
+}
+
+/* The w9968cf docs say that a 0 sized packet means EOF (and also SOF
+ for the next frame). This seems to simply not be true when operating
+ in JPEG mode, in this case there may be empty packets within the
+ frame. So in JPEG mode use the JPEG SOI marker to detect SOF.
+
+ Note to make things even more interesting the w9968cf sends *PLANAR* jpeg,
+ to be precise it sends: SOI, SOF, DRI, SOS, Y-data, SOS, U-data, SOS,
+ V-data, EOI. */
+static void w9968cf_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (w9968cf_vga_mode[gspca_dev->curr_mode].pixelformat ==
+ V4L2_PIX_FMT_JPEG) {
+ if (len >= 2 &&
+ data[0] == 0xff &&
+ data[1] == 0xd8) {
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ NULL, 0);
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+ /* Strip the ff d8, our own header (which adds
+ huffman and quantization tables) already has this */
+ len -= 2;
+ data += 2;
+ }
+ } else {
+ /* In UYVY mode an empty packet signals EOF */
+ if (gspca_dev->empty_packet) {
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ NULL, 0);
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ NULL, 0);
+ gspca_dev->empty_packet = 0;
+ }
+ }
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
diff --git a/drivers/media/video/gspca/xirlink_cit.c b/drivers/media/video/gspca/xirlink_cit.c
new file mode 100644
index 00000000..ecada178
--- /dev/null
+++ b/drivers/media/video/gspca/xirlink_cit.c
@@ -0,0 +1,3326 @@
+/*
+ * USB IBM C-It Video Camera driver
+ *
+ * Supports Xirlink C-It Video Camera, IBM PC Camera,
+ * IBM NetCamera and Veo Stingray.
+ *
+ * Copyright (C) 2010 Hans de Goede <hdegoede@redhat.com>
+ *
+ * This driver is based on earlier work of:
+ *
+ * (C) Copyright 1999 Johannes Erdfelt
+ * (C) Copyright 1999 Randy Dunlap
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "xirlink-cit"
+
+#include <linux/input.h>
+#include "gspca.h"
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Xirlink C-IT");
+MODULE_LICENSE("GPL");
+
+/* FIXME we should autodetect this */
+static int ibm_netcam_pro;
+module_param(ibm_netcam_pro, int, 0);
+MODULE_PARM_DESC(ibm_netcam_pro,
+ "Use IBM Netcamera Pro init sequences for Model 3 cams");
+
+/* FIXME this should be handled through the V4L2 input selection API */
+static int rca_input;
+module_param(rca_input, int, 0644);
+MODULE_PARM_DESC(rca_input,
+ "Use rca input instead of ccd sensor on Model 3 cams");
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ u8 model;
+#define CIT_MODEL0 0 /* bcd version 0.01 cams ie the xvp-500 */
+#define CIT_MODEL1 1 /* The model 1 - 4 nomenclature comes from the old */
+#define CIT_MODEL2 2 /* ibmcam driver */
+#define CIT_MODEL3 3
+#define CIT_MODEL4 4
+#define CIT_IBM_NETCAM_PRO 5
+ u8 input_index;
+ u8 button_state;
+ u8 stop_on_control_change;
+ u8 sof_read;
+ u8 sof_len;
+ u8 contrast;
+ u8 brightness;
+ u8 hue;
+ u8 sharpness;
+ u8 lighting;
+ u8 hflip;
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setlighting(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getlighting(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val);
+static void sd_stop0(struct gspca_dev *gspca_dev);
+
+static const struct ctrl sd_ctrls[] = {
+#define SD_BRIGHTNESS 0
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 63,
+ .step = 1,
+#define BRIGHTNESS_DEFAULT 32
+ .default_value = BRIGHTNESS_DEFAULT,
+ .flags = 0,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+#define SD_CONTRAST 1
+ {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "contrast",
+ .minimum = 0,
+ .maximum = 20,
+ .step = 1,
+#define CONTRAST_DEFAULT 10
+ .default_value = CONTRAST_DEFAULT,
+ .flags = 0,
+ },
+ .set = sd_setcontrast,
+ .get = sd_getcontrast,
+ },
+#define SD_HUE 2
+ {
+ {
+ .id = V4L2_CID_HUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Hue",
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+#define HUE_DEFAULT 63
+ .default_value = HUE_DEFAULT,
+ .flags = 0,
+ },
+ .set = sd_sethue,
+ .get = sd_gethue,
+ },
+#define SD_SHARPNESS 3
+ {
+ {
+ .id = V4L2_CID_SHARPNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Sharpness",
+ .minimum = 0,
+ .maximum = 6,
+ .step = 1,
+#define SHARPNESS_DEFAULT 3
+ .default_value = SHARPNESS_DEFAULT,
+ .flags = 0,
+ },
+ .set = sd_setsharpness,
+ .get = sd_getsharpness,
+ },
+#define SD_LIGHTING 4
+ {
+ {
+ .id = V4L2_CID_BACKLIGHT_COMPENSATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Lighting",
+ .minimum = 0,
+ .maximum = 2,
+ .step = 1,
+#define LIGHTING_DEFAULT 1
+ .default_value = LIGHTING_DEFAULT,
+ .flags = 0,
+ },
+ .set = sd_setlighting,
+ .get = sd_getlighting,
+ },
+#define SD_HFLIP 5
+ {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mirror",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define HFLIP_DEFAULT 0
+ .default_value = HFLIP_DEFAULT,
+ },
+ .set = sd_sethflip,
+ .get = sd_gethflip,
+ },
+};
+
+static const struct v4l2_pix_format cif_yuv_mode[] = {
+ {176, 144, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144 * 3 / 2 + 4,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+ {352, 288, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288 * 3 / 2 + 4,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+};
+
+static const struct v4l2_pix_format vga_yuv_mode[] = {
+ {160, 120, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120 * 3 / 2 + 4,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+ {320, 240, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 2 + 4,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+ {640, 480, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 2 + 4,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+};
+
+static const struct v4l2_pix_format model0_mode[] = {
+ {160, 120, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120 * 3 / 2 + 4,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+ {176, 144, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144 * 3 / 2 + 4,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+ {320, 240, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 2 + 4,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+};
+
+static const struct v4l2_pix_format model2_mode[] = {
+ {160, 120, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120 * 3 / 2 + 4,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+ {176, 144, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144 * 3 / 2 + 4,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+ {320, 240, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 + 4,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+ {352, 288, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288 + 4,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+};
+
+/*
+ * 01.01.08 - Added for RCA video in support -LO
+ * This struct is used to init the Model3 cam to use the RCA video in port
+ * instead of the CCD sensor.
+ */
+static const u16 rca_initdata[][3] = {
+ {0, 0x0000, 0x010c},
+ {0, 0x0006, 0x012c},
+ {0, 0x0078, 0x012d},
+ {0, 0x0046, 0x012f},
+ {0, 0xd141, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfea8, 0x0124},
+ {1, 0x0000, 0x0116},
+ {0, 0x0064, 0x0116},
+ {1, 0x0000, 0x0115},
+ {0, 0x0003, 0x0115},
+ {0, 0x0008, 0x0123},
+ {0, 0x0000, 0x0117},
+ {0, 0x0000, 0x0112},
+ {0, 0x0080, 0x0100},
+ {0, 0x0000, 0x0100},
+ {1, 0x0000, 0x0116},
+ {0, 0x0060, 0x0116},
+ {0, 0x0002, 0x0112},
+ {0, 0x0000, 0x0123},
+ {0, 0x0001, 0x0117},
+ {0, 0x0040, 0x0108},
+ {0, 0x0019, 0x012c},
+ {0, 0x0040, 0x0116},
+ {0, 0x000a, 0x0115},
+ {0, 0x000b, 0x0115},
+ {0, 0x0078, 0x012d},
+ {0, 0x0046, 0x012f},
+ {0, 0xd141, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfea8, 0x0124},
+ {0, 0x0064, 0x0116},
+ {0, 0x0000, 0x0115},
+ {0, 0x0001, 0x0115},
+ {0, 0xffff, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x00aa, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xffff, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x00f2, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x000f, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xffff, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x00f8, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x00fc, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xffff, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x00f9, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x003c, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xffff, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0027, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0019, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0021, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0006, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0045, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x002a, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x000e, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x002b, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x00f4, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x002c, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0004, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x002d, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0014, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x002e, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0003, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x002f, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0003, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0014, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0040, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0040, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0053, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0x0000, 0x0101},
+ {0, 0x00a0, 0x0103},
+ {0, 0x0078, 0x0105},
+ {0, 0x0000, 0x010a},
+ {0, 0x0024, 0x010b},
+ {0, 0x0028, 0x0119},
+ {0, 0x0088, 0x011b},
+ {0, 0x0002, 0x011d},
+ {0, 0x0003, 0x011e},
+ {0, 0x0000, 0x0129},
+ {0, 0x00fc, 0x012b},
+ {0, 0x0008, 0x0102},
+ {0, 0x0000, 0x0104},
+ {0, 0x0008, 0x011a},
+ {0, 0x0028, 0x011c},
+ {0, 0x0021, 0x012a},
+ {0, 0x0000, 0x0118},
+ {0, 0x0000, 0x0132},
+ {0, 0x0000, 0x0109},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0031, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0040, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0040, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x00dc, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0032, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0020, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0040, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0040, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0030, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0008, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0x0003, 0x0111},
+};
+
+/* TESTME the old ibmcam driver repeats certain commands to Model1 cameras, we
+ do the same for now (testing needed to see if this is really necessary) */
+static const int cit_model1_ntries = 5;
+static const int cit_model1_ntries2 = 2;
+
+static int cit_write_reg(struct gspca_dev *gspca_dev, u16 value, u16 index)
+{
+ struct usb_device *udev = gspca_dev->dev;
+ int err;
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
+ value, index, NULL, 0, 1000);
+ if (err < 0)
+ pr_err("Failed to write a register (index 0x%04X, value 0x%02X, error %d)\n",
+ index, value, err);
+
+ return 0;
+}
+
+static int cit_read_reg(struct gspca_dev *gspca_dev, u16 index, int verbose)
+{
+ struct usb_device *udev = gspca_dev->dev;
+ __u8 *buf = gspca_dev->usb_buf;
+ int res;
+
+ res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x01,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
+ 0x00, index, buf, 8, 1000);
+ if (res < 0) {
+ pr_err("Failed to read a register (index 0x%04X, error %d)\n",
+ index, res);
+ return res;
+ }
+
+ if (verbose)
+ PDEBUG(D_PROBE, "Register %04x value: %02x", index, buf[0]);
+
+ return 0;
+}
+
+/*
+ * cit_send_FF_04_02()
+ *
+ * This procedure sends magic 3-command prefix to the camera.
+ * The purpose of this prefix is not known.
+ *
+ * History:
+ * 1/2/00 Created.
+ */
+static void cit_send_FF_04_02(struct gspca_dev *gspca_dev)
+{
+ cit_write_reg(gspca_dev, 0x00FF, 0x0127);
+ cit_write_reg(gspca_dev, 0x0004, 0x0124);
+ cit_write_reg(gspca_dev, 0x0002, 0x0124);
+}
+
+static void cit_send_00_04_06(struct gspca_dev *gspca_dev)
+{
+ cit_write_reg(gspca_dev, 0x0000, 0x0127);
+ cit_write_reg(gspca_dev, 0x0004, 0x0124);
+ cit_write_reg(gspca_dev, 0x0006, 0x0124);
+}
+
+static void cit_send_x_00(struct gspca_dev *gspca_dev, unsigned short x)
+{
+ cit_write_reg(gspca_dev, x, 0x0127);
+ cit_write_reg(gspca_dev, 0x0000, 0x0124);
+}
+
+static void cit_send_x_00_05(struct gspca_dev *gspca_dev, unsigned short x)
+{
+ cit_send_x_00(gspca_dev, x);
+ cit_write_reg(gspca_dev, 0x0005, 0x0124);
+}
+
+static void cit_send_x_00_05_02(struct gspca_dev *gspca_dev, unsigned short x)
+{
+ cit_write_reg(gspca_dev, x, 0x0127);
+ cit_write_reg(gspca_dev, 0x0000, 0x0124);
+ cit_write_reg(gspca_dev, 0x0005, 0x0124);
+ cit_write_reg(gspca_dev, 0x0002, 0x0124);
+}
+
+static void cit_send_x_01_00_05(struct gspca_dev *gspca_dev, u16 x)
+{
+ cit_write_reg(gspca_dev, x, 0x0127);
+ cit_write_reg(gspca_dev, 0x0001, 0x0124);
+ cit_write_reg(gspca_dev, 0x0000, 0x0124);
+ cit_write_reg(gspca_dev, 0x0005, 0x0124);
+}
+
+static void cit_send_x_00_05_02_01(struct gspca_dev *gspca_dev, u16 x)
+{
+ cit_write_reg(gspca_dev, x, 0x0127);
+ cit_write_reg(gspca_dev, 0x0000, 0x0124);
+ cit_write_reg(gspca_dev, 0x0005, 0x0124);
+ cit_write_reg(gspca_dev, 0x0002, 0x0124);
+ cit_write_reg(gspca_dev, 0x0001, 0x0124);
+}
+
+static void cit_send_x_00_05_02_08_01(struct gspca_dev *gspca_dev, u16 x)
+{
+ cit_write_reg(gspca_dev, x, 0x0127);
+ cit_write_reg(gspca_dev, 0x0000, 0x0124);
+ cit_write_reg(gspca_dev, 0x0005, 0x0124);
+ cit_write_reg(gspca_dev, 0x0002, 0x0124);
+ cit_write_reg(gspca_dev, 0x0008, 0x0124);
+ cit_write_reg(gspca_dev, 0x0001, 0x0124);
+}
+
+static void cit_Packet_Format1(struct gspca_dev *gspca_dev, u16 fkey, u16 val)
+{
+ cit_send_x_01_00_05(gspca_dev, 0x0088);
+ cit_send_x_00_05(gspca_dev, fkey);
+ cit_send_x_00_05_02_08_01(gspca_dev, val);
+ cit_send_x_00_05(gspca_dev, 0x0088);
+ cit_send_x_00_05_02_01(gspca_dev, fkey);
+ cit_send_x_00_05(gspca_dev, 0x0089);
+ cit_send_x_00(gspca_dev, fkey);
+ cit_send_00_04_06(gspca_dev);
+ cit_read_reg(gspca_dev, 0x0126, 0);
+ cit_send_FF_04_02(gspca_dev);
+}
+
+static void cit_PacketFormat2(struct gspca_dev *gspca_dev, u16 fkey, u16 val)
+{
+ cit_send_x_01_00_05(gspca_dev, 0x0088);
+ cit_send_x_00_05(gspca_dev, fkey);
+ cit_send_x_00_05_02(gspca_dev, val);
+}
+
+static void cit_model2_Packet2(struct gspca_dev *gspca_dev)
+{
+ cit_write_reg(gspca_dev, 0x00ff, 0x012d);
+ cit_write_reg(gspca_dev, 0xfea3, 0x0124);
+}
+
+static void cit_model2_Packet1(struct gspca_dev *gspca_dev, u16 v1, u16 v2)
+{
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x00ff, 0x012e);
+ cit_write_reg(gspca_dev, v1, 0x012f);
+ cit_write_reg(gspca_dev, 0x00ff, 0x0130);
+ cit_write_reg(gspca_dev, 0xc719, 0x0124);
+ cit_write_reg(gspca_dev, v2, 0x0127);
+
+ cit_model2_Packet2(gspca_dev);
+}
+
+/*
+ * cit_model3_Packet1()
+ *
+ * 00_0078_012d
+ * 00_0097_012f
+ * 00_d141_0124
+ * 00_0096_0127
+ * 00_fea8_0124
+*/
+static void cit_model3_Packet1(struct gspca_dev *gspca_dev, u16 v1, u16 v2)
+{
+ cit_write_reg(gspca_dev, 0x0078, 0x012d);
+ cit_write_reg(gspca_dev, v1, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, v2, 0x0127);
+ cit_write_reg(gspca_dev, 0xfea8, 0x0124);
+}
+
+static void cit_model4_Packet1(struct gspca_dev *gspca_dev, u16 v1, u16 v2)
+{
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, v1, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, v2, 0x0127);
+ cit_write_reg(gspca_dev, 0xfea8, 0x0124);
+}
+
+static void cit_model4_BrightnessPacket(struct gspca_dev *gspca_dev, u16 val)
+{
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x0026, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, val, 0x0127);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+ cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+ cit_write_reg(gspca_dev, 0x0038, 0x012d);
+ cit_write_reg(gspca_dev, 0x0004, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+ sd->model = id->driver_info;
+ if (sd->model == CIT_MODEL3 && ibm_netcam_pro)
+ sd->model = CIT_IBM_NETCAM_PRO;
+
+ cam = &gspca_dev->cam;
+ switch (sd->model) {
+ case CIT_MODEL0:
+ cam->cam_mode = model0_mode;
+ cam->nmodes = ARRAY_SIZE(model0_mode);
+ gspca_dev->ctrl_dis = ~((1 << SD_CONTRAST) | (1 << SD_HFLIP));
+ sd->sof_len = 4;
+ break;
+ case CIT_MODEL1:
+ cam->cam_mode = cif_yuv_mode;
+ cam->nmodes = ARRAY_SIZE(cif_yuv_mode);
+ gspca_dev->ctrl_dis = (1 << SD_HUE) | (1 << SD_HFLIP);
+ sd->sof_len = 4;
+ break;
+ case CIT_MODEL2:
+ cam->cam_mode = model2_mode + 1; /* no 160x120 */
+ cam->nmodes = 3;
+ gspca_dev->ctrl_dis = (1 << SD_CONTRAST) |
+ (1 << SD_SHARPNESS) |
+ (1 << SD_HFLIP);
+ break;
+ case CIT_MODEL3:
+ cam->cam_mode = vga_yuv_mode;
+ cam->nmodes = ARRAY_SIZE(vga_yuv_mode);
+ gspca_dev->ctrl_dis = (1 << SD_HUE) |
+ (1 << SD_LIGHTING) |
+ (1 << SD_HFLIP);
+ sd->stop_on_control_change = 1;
+ sd->sof_len = 4;
+ break;
+ case CIT_MODEL4:
+ cam->cam_mode = model2_mode;
+ cam->nmodes = ARRAY_SIZE(model2_mode);
+ gspca_dev->ctrl_dis = (1 << SD_CONTRAST) |
+ (1 << SD_SHARPNESS) |
+ (1 << SD_LIGHTING) |
+ (1 << SD_HFLIP);
+ break;
+ case CIT_IBM_NETCAM_PRO:
+ cam->cam_mode = vga_yuv_mode;
+ cam->nmodes = 2; /* no 640 x 480 */
+ cam->input_flags = V4L2_IN_ST_VFLIP;
+ gspca_dev->ctrl_dis = ~(1 << SD_CONTRAST);
+ sd->stop_on_control_change = 1;
+ sd->sof_len = 4;
+ break;
+ }
+
+ sd->brightness = BRIGHTNESS_DEFAULT;
+ sd->contrast = CONTRAST_DEFAULT;
+ sd->hue = HUE_DEFAULT;
+ sd->sharpness = SHARPNESS_DEFAULT;
+ sd->lighting = LIGHTING_DEFAULT;
+ sd->hflip = HFLIP_DEFAULT;
+
+ return 0;
+}
+
+static int cit_init_model0(struct gspca_dev *gspca_dev)
+{
+ cit_write_reg(gspca_dev, 0x0000, 0x0100); /* turn on led */
+ cit_write_reg(gspca_dev, 0x0001, 0x0112); /* turn on autogain ? */
+ cit_write_reg(gspca_dev, 0x0000, 0x0400);
+ cit_write_reg(gspca_dev, 0x0001, 0x0400);
+ cit_write_reg(gspca_dev, 0x0000, 0x0420);
+ cit_write_reg(gspca_dev, 0x0001, 0x0420);
+ cit_write_reg(gspca_dev, 0x000d, 0x0409);
+ cit_write_reg(gspca_dev, 0x0002, 0x040a);
+ cit_write_reg(gspca_dev, 0x0018, 0x0405);
+ cit_write_reg(gspca_dev, 0x0008, 0x0435);
+ cit_write_reg(gspca_dev, 0x0026, 0x040b);
+ cit_write_reg(gspca_dev, 0x0007, 0x0437);
+ cit_write_reg(gspca_dev, 0x0015, 0x042f);
+ cit_write_reg(gspca_dev, 0x002b, 0x0439);
+ cit_write_reg(gspca_dev, 0x0026, 0x043a);
+ cit_write_reg(gspca_dev, 0x0008, 0x0438);
+ cit_write_reg(gspca_dev, 0x001e, 0x042b);
+ cit_write_reg(gspca_dev, 0x0041, 0x042c);
+
+ return 0;
+}
+
+static int cit_init_ibm_netcam_pro(struct gspca_dev *gspca_dev)
+{
+ cit_read_reg(gspca_dev, 0x128, 1);
+ cit_write_reg(gspca_dev, 0x0003, 0x0133);
+ cit_write_reg(gspca_dev, 0x0000, 0x0117);
+ cit_write_reg(gspca_dev, 0x0008, 0x0123);
+ cit_write_reg(gspca_dev, 0x0000, 0x0100);
+ cit_read_reg(gspca_dev, 0x0116, 0);
+ cit_write_reg(gspca_dev, 0x0060, 0x0116);
+ cit_write_reg(gspca_dev, 0x0002, 0x0112);
+ cit_write_reg(gspca_dev, 0x0000, 0x0133);
+ cit_write_reg(gspca_dev, 0x0000, 0x0123);
+ cit_write_reg(gspca_dev, 0x0001, 0x0117);
+ cit_write_reg(gspca_dev, 0x0040, 0x0108);
+ cit_write_reg(gspca_dev, 0x0019, 0x012c);
+ cit_write_reg(gspca_dev, 0x0060, 0x0116);
+ cit_write_reg(gspca_dev, 0x0002, 0x0115);
+ cit_write_reg(gspca_dev, 0x000b, 0x0115);
+
+ cit_write_reg(gspca_dev, 0x0078, 0x012d);
+ cit_write_reg(gspca_dev, 0x0001, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0079, 0x012d);
+ cit_write_reg(gspca_dev, 0x00ff, 0x0130);
+ cit_write_reg(gspca_dev, 0xcd41, 0x0124);
+ cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+ cit_read_reg(gspca_dev, 0x0126, 1);
+
+ cit_model3_Packet1(gspca_dev, 0x0000, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0000, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x000b, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x000c, 0x0008);
+ cit_model3_Packet1(gspca_dev, 0x000d, 0x003a);
+ cit_model3_Packet1(gspca_dev, 0x000e, 0x0060);
+ cit_model3_Packet1(gspca_dev, 0x000f, 0x0060);
+ cit_model3_Packet1(gspca_dev, 0x0010, 0x0008);
+ cit_model3_Packet1(gspca_dev, 0x0011, 0x0004);
+ cit_model3_Packet1(gspca_dev, 0x0012, 0x0028);
+ cit_model3_Packet1(gspca_dev, 0x0013, 0x0002);
+ cit_model3_Packet1(gspca_dev, 0x0014, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0015, 0x00fb);
+ cit_model3_Packet1(gspca_dev, 0x0016, 0x0002);
+ cit_model3_Packet1(gspca_dev, 0x0017, 0x0037);
+ cit_model3_Packet1(gspca_dev, 0x0018, 0x0036);
+ cit_model3_Packet1(gspca_dev, 0x001e, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x001f, 0x0008);
+ cit_model3_Packet1(gspca_dev, 0x0020, 0x00c1);
+ cit_model3_Packet1(gspca_dev, 0x0021, 0x0034);
+ cit_model3_Packet1(gspca_dev, 0x0022, 0x0034);
+ cit_model3_Packet1(gspca_dev, 0x0025, 0x0002);
+ cit_model3_Packet1(gspca_dev, 0x0028, 0x0022);
+ cit_model3_Packet1(gspca_dev, 0x0029, 0x000a);
+ cit_model3_Packet1(gspca_dev, 0x002b, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x002c, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x002d, 0x00ff);
+ cit_model3_Packet1(gspca_dev, 0x002e, 0x00ff);
+ cit_model3_Packet1(gspca_dev, 0x002f, 0x00ff);
+ cit_model3_Packet1(gspca_dev, 0x0030, 0x00ff);
+ cit_model3_Packet1(gspca_dev, 0x0031, 0x00ff);
+ cit_model3_Packet1(gspca_dev, 0x0032, 0x0007);
+ cit_model3_Packet1(gspca_dev, 0x0033, 0x0005);
+ cit_model3_Packet1(gspca_dev, 0x0037, 0x0040);
+ cit_model3_Packet1(gspca_dev, 0x0039, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x003a, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x003b, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x003c, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0040, 0x000c);
+ cit_model3_Packet1(gspca_dev, 0x0041, 0x00fb);
+ cit_model3_Packet1(gspca_dev, 0x0042, 0x0002);
+ cit_model3_Packet1(gspca_dev, 0x0043, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0045, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0046, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0047, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0048, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0049, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x004a, 0x00ff);
+ cit_model3_Packet1(gspca_dev, 0x004b, 0x00ff);
+ cit_model3_Packet1(gspca_dev, 0x004c, 0x00ff);
+ cit_model3_Packet1(gspca_dev, 0x004f, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0050, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0051, 0x0002);
+ cit_model3_Packet1(gspca_dev, 0x0055, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0056, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0057, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0058, 0x0002);
+ cit_model3_Packet1(gspca_dev, 0x0059, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x005c, 0x0016);
+ cit_model3_Packet1(gspca_dev, 0x005d, 0x0022);
+ cit_model3_Packet1(gspca_dev, 0x005e, 0x003c);
+ cit_model3_Packet1(gspca_dev, 0x005f, 0x0050);
+ cit_model3_Packet1(gspca_dev, 0x0060, 0x0044);
+ cit_model3_Packet1(gspca_dev, 0x0061, 0x0005);
+ cit_model3_Packet1(gspca_dev, 0x006a, 0x007e);
+ cit_model3_Packet1(gspca_dev, 0x006f, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0072, 0x001b);
+ cit_model3_Packet1(gspca_dev, 0x0073, 0x0005);
+ cit_model3_Packet1(gspca_dev, 0x0074, 0x000a);
+ cit_model3_Packet1(gspca_dev, 0x0075, 0x001b);
+ cit_model3_Packet1(gspca_dev, 0x0076, 0x002a);
+ cit_model3_Packet1(gspca_dev, 0x0077, 0x003c);
+ cit_model3_Packet1(gspca_dev, 0x0078, 0x0050);
+ cit_model3_Packet1(gspca_dev, 0x007b, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x007c, 0x0011);
+ cit_model3_Packet1(gspca_dev, 0x007d, 0x0024);
+ cit_model3_Packet1(gspca_dev, 0x007e, 0x0043);
+ cit_model3_Packet1(gspca_dev, 0x007f, 0x005a);
+ cit_model3_Packet1(gspca_dev, 0x0084, 0x0020);
+ cit_model3_Packet1(gspca_dev, 0x0085, 0x0033);
+ cit_model3_Packet1(gspca_dev, 0x0086, 0x000a);
+ cit_model3_Packet1(gspca_dev, 0x0087, 0x0030);
+ cit_model3_Packet1(gspca_dev, 0x0088, 0x0070);
+ cit_model3_Packet1(gspca_dev, 0x008b, 0x0008);
+ cit_model3_Packet1(gspca_dev, 0x008f, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0090, 0x0006);
+ cit_model3_Packet1(gspca_dev, 0x0091, 0x0028);
+ cit_model3_Packet1(gspca_dev, 0x0092, 0x005a);
+ cit_model3_Packet1(gspca_dev, 0x0093, 0x0082);
+ cit_model3_Packet1(gspca_dev, 0x0096, 0x0014);
+ cit_model3_Packet1(gspca_dev, 0x0097, 0x0020);
+ cit_model3_Packet1(gspca_dev, 0x0098, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00b0, 0x0046);
+ cit_model3_Packet1(gspca_dev, 0x00b1, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00b2, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00b3, 0x0004);
+ cit_model3_Packet1(gspca_dev, 0x00b4, 0x0007);
+ cit_model3_Packet1(gspca_dev, 0x00b6, 0x0002);
+ cit_model3_Packet1(gspca_dev, 0x00b7, 0x0004);
+ cit_model3_Packet1(gspca_dev, 0x00bb, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00bc, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x00bd, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00bf, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00c0, 0x00c8);
+ cit_model3_Packet1(gspca_dev, 0x00c1, 0x0014);
+ cit_model3_Packet1(gspca_dev, 0x00c2, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x00c3, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00c4, 0x0004);
+ cit_model3_Packet1(gspca_dev, 0x00cb, 0x00bf);
+ cit_model3_Packet1(gspca_dev, 0x00cc, 0x00bf);
+ cit_model3_Packet1(gspca_dev, 0x00cd, 0x00bf);
+ cit_model3_Packet1(gspca_dev, 0x00ce, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00cf, 0x0020);
+ cit_model3_Packet1(gspca_dev, 0x00d0, 0x0040);
+ cit_model3_Packet1(gspca_dev, 0x00d1, 0x00bf);
+ cit_model3_Packet1(gspca_dev, 0x00d1, 0x00bf);
+ cit_model3_Packet1(gspca_dev, 0x00d2, 0x00bf);
+ cit_model3_Packet1(gspca_dev, 0x00d3, 0x00bf);
+ cit_model3_Packet1(gspca_dev, 0x00ea, 0x0008);
+ cit_model3_Packet1(gspca_dev, 0x00eb, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00ec, 0x00e8);
+ cit_model3_Packet1(gspca_dev, 0x00ed, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x00ef, 0x0022);
+ cit_model3_Packet1(gspca_dev, 0x00f0, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00f2, 0x0028);
+ cit_model3_Packet1(gspca_dev, 0x00f4, 0x0002);
+ cit_model3_Packet1(gspca_dev, 0x00f5, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00fa, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00fb, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x00fc, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00fd, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00fe, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00ff, 0x0000);
+
+ cit_model3_Packet1(gspca_dev, 0x00be, 0x0003);
+ cit_model3_Packet1(gspca_dev, 0x00c8, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00c9, 0x0020);
+ cit_model3_Packet1(gspca_dev, 0x00ca, 0x0040);
+ cit_model3_Packet1(gspca_dev, 0x0053, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x0082, 0x000e);
+ cit_model3_Packet1(gspca_dev, 0x0083, 0x0020);
+ cit_model3_Packet1(gspca_dev, 0x0034, 0x003c);
+ cit_model3_Packet1(gspca_dev, 0x006e, 0x0055);
+ cit_model3_Packet1(gspca_dev, 0x0062, 0x0005);
+ cit_model3_Packet1(gspca_dev, 0x0063, 0x0008);
+ cit_model3_Packet1(gspca_dev, 0x0066, 0x000a);
+ cit_model3_Packet1(gspca_dev, 0x0067, 0x0006);
+ cit_model3_Packet1(gspca_dev, 0x006b, 0x0010);
+ cit_model3_Packet1(gspca_dev, 0x005a, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x005b, 0x000a);
+ cit_model3_Packet1(gspca_dev, 0x0023, 0x0006);
+ cit_model3_Packet1(gspca_dev, 0x0026, 0x0004);
+ cit_model3_Packet1(gspca_dev, 0x0036, 0x0069);
+ cit_model3_Packet1(gspca_dev, 0x0038, 0x0064);
+ cit_model3_Packet1(gspca_dev, 0x003d, 0x0003);
+ cit_model3_Packet1(gspca_dev, 0x003e, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x00b8, 0x0014);
+ cit_model3_Packet1(gspca_dev, 0x00b9, 0x0014);
+ cit_model3_Packet1(gspca_dev, 0x00e6, 0x0004);
+ cit_model3_Packet1(gspca_dev, 0x00e8, 0x0001);
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->model) {
+ case CIT_MODEL0:
+ cit_init_model0(gspca_dev);
+ sd_stop0(gspca_dev);
+ break;
+ case CIT_MODEL1:
+ case CIT_MODEL2:
+ case CIT_MODEL3:
+ case CIT_MODEL4:
+ break; /* All is done in sd_start */
+ case CIT_IBM_NETCAM_PRO:
+ cit_init_ibm_netcam_pro(gspca_dev);
+ sd_stop0(gspca_dev);
+ break;
+ }
+ return 0;
+}
+
+static int cit_set_brightness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+
+ switch (sd->model) {
+ case CIT_MODEL0:
+ case CIT_IBM_NETCAM_PRO:
+ /* No (known) brightness control for these */
+ break;
+ case CIT_MODEL1:
+ /* Model 1: Brightness range 0 - 63 */
+ cit_Packet_Format1(gspca_dev, 0x0031, sd->brightness);
+ cit_Packet_Format1(gspca_dev, 0x0032, sd->brightness);
+ cit_Packet_Format1(gspca_dev, 0x0033, sd->brightness);
+ break;
+ case CIT_MODEL2:
+ /* Model 2: Brightness range 0x60 - 0xee */
+ /* Scale 0 - 63 to 0x60 - 0xee */
+ i = 0x60 + sd->brightness * 2254 / 1000;
+ cit_model2_Packet1(gspca_dev, 0x001a, i);
+ break;
+ case CIT_MODEL3:
+ /* Model 3: Brightness range 'i' in [0x0C..0x3F] */
+ i = sd->brightness;
+ if (i < 0x0c)
+ i = 0x0c;
+ cit_model3_Packet1(gspca_dev, 0x0036, i);
+ break;
+ case CIT_MODEL4:
+ /* Model 4: Brightness range 'i' in [0x04..0xb4] */
+ /* Scale 0 - 63 to 0x04 - 0xb4 */
+ i = 0x04 + sd->brightness * 2794 / 1000;
+ cit_model4_BrightnessPacket(gspca_dev, i);
+ break;
+ }
+
+ return 0;
+}
+
+static int cit_set_contrast(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->model) {
+ case CIT_MODEL0: {
+ int i;
+ /* gain 0-15, 0-20 -> 0-15 */
+ i = sd->contrast * 1000 / 1333;
+ cit_write_reg(gspca_dev, i, 0x0422);
+ /* gain 0-31, may not be lower then 0x0422, 0-20 -> 0-31 */
+ i = sd->contrast * 2000 / 1333;
+ cit_write_reg(gspca_dev, i, 0x0423);
+ /* gain 0-127, may not be lower then 0x0423, 0-20 -> 0-63 */
+ i = sd->contrast * 4000 / 1333;
+ cit_write_reg(gspca_dev, i, 0x0424);
+ /* gain 0-127, may not be lower then 0x0424, , 0-20 -> 0-127 */
+ i = sd->contrast * 8000 / 1333;
+ cit_write_reg(gspca_dev, i, 0x0425);
+ break;
+ }
+ case CIT_MODEL2:
+ case CIT_MODEL4:
+ /* These models do not have this control. */
+ break;
+ case CIT_MODEL1:
+ {
+ /* Scale 0 - 20 to 15 - 0 */
+ int i, new_contrast = (20 - sd->contrast) * 1000 / 1333;
+ for (i = 0; i < cit_model1_ntries; i++) {
+ cit_Packet_Format1(gspca_dev, 0x0014, new_contrast);
+ cit_send_FF_04_02(gspca_dev);
+ }
+ break;
+ }
+ case CIT_MODEL3:
+ { /* Preset hardware values */
+ static const struct {
+ unsigned short cv1;
+ unsigned short cv2;
+ unsigned short cv3;
+ } cv[7] = {
+ { 0x05, 0x05, 0x0f }, /* Minimum */
+ { 0x04, 0x04, 0x16 },
+ { 0x02, 0x03, 0x16 },
+ { 0x02, 0x08, 0x16 },
+ { 0x01, 0x0c, 0x16 },
+ { 0x01, 0x0e, 0x16 },
+ { 0x01, 0x10, 0x16 } /* Maximum */
+ };
+ int i = sd->contrast / 3;
+ cit_model3_Packet1(gspca_dev, 0x0067, cv[i].cv1);
+ cit_model3_Packet1(gspca_dev, 0x005b, cv[i].cv2);
+ cit_model3_Packet1(gspca_dev, 0x005c, cv[i].cv3);
+ break;
+ }
+ case CIT_IBM_NETCAM_PRO:
+ cit_model3_Packet1(gspca_dev, 0x005b, sd->contrast + 1);
+ break;
+ }
+ return 0;
+}
+
+static int cit_set_hue(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->model) {
+ case CIT_MODEL0:
+ case CIT_MODEL1:
+ case CIT_IBM_NETCAM_PRO:
+ /* No hue control for these models */
+ break;
+ case CIT_MODEL2:
+ cit_model2_Packet1(gspca_dev, 0x0024, sd->hue);
+ /* cit_model2_Packet1(gspca_dev, 0x0020, sat); */
+ break;
+ case CIT_MODEL3: {
+ /* Model 3: Brightness range 'i' in [0x05..0x37] */
+ /* TESTME according to the ibmcam driver this does not work */
+ if (0) {
+ /* Scale 0 - 127 to 0x05 - 0x37 */
+ int i = 0x05 + sd->hue * 1000 / 2540;
+ cit_model3_Packet1(gspca_dev, 0x007e, i);
+ }
+ break;
+ }
+ case CIT_MODEL4:
+ /* HDG: taken from ibmcam, setting the color gains does not
+ * really belong here.
+ *
+ * I am not sure r/g/b_gain variables exactly control gain
+ * of those channels. Most likely they subtly change some
+ * very internal image processing settings in the camera.
+ * In any case, here is what they do, and feel free to tweak:
+ *
+ * r_gain: seriously affects red gain
+ * g_gain: seriously affects green gain
+ * b_gain: seriously affects blue gain
+ * hue: changes average color from violet (0) to red (0xFF)
+ */
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x001e, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 160, 0x0127); /* Green gain */
+ cit_write_reg(gspca_dev, 160, 0x012e); /* Red gain */
+ cit_write_reg(gspca_dev, 160, 0x0130); /* Blue gain */
+ cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+ cit_write_reg(gspca_dev, sd->hue, 0x012d); /* Hue */
+ cit_write_reg(gspca_dev, 0xf545, 0x0124);
+ break;
+ }
+ return 0;
+}
+
+static int cit_set_sharpness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->model) {
+ case CIT_MODEL0:
+ case CIT_MODEL2:
+ case CIT_MODEL4:
+ case CIT_IBM_NETCAM_PRO:
+ /* These models do not have this control */
+ break;
+ case CIT_MODEL1: {
+ int i;
+ const unsigned short sa[] = {
+ 0x11, 0x13, 0x16, 0x18, 0x1a, 0x8, 0x0a };
+
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_PacketFormat2(gspca_dev, 0x0013, sa[sd->sharpness]);
+ break;
+ }
+ case CIT_MODEL3:
+ { /*
+ * "Use a table of magic numbers.
+ * This setting doesn't really change much.
+ * But that's how Windows does it."
+ */
+ static const struct {
+ unsigned short sv1;
+ unsigned short sv2;
+ unsigned short sv3;
+ unsigned short sv4;
+ } sv[7] = {
+ { 0x00, 0x00, 0x05, 0x14 }, /* Smoothest */
+ { 0x01, 0x04, 0x05, 0x14 },
+ { 0x02, 0x04, 0x05, 0x14 },
+ { 0x03, 0x04, 0x05, 0x14 },
+ { 0x03, 0x05, 0x05, 0x14 },
+ { 0x03, 0x06, 0x05, 0x14 },
+ { 0x03, 0x07, 0x05, 0x14 } /* Sharpest */
+ };
+ cit_model3_Packet1(gspca_dev, 0x0060, sv[sd->sharpness].sv1);
+ cit_model3_Packet1(gspca_dev, 0x0061, sv[sd->sharpness].sv2);
+ cit_model3_Packet1(gspca_dev, 0x0062, sv[sd->sharpness].sv3);
+ cit_model3_Packet1(gspca_dev, 0x0063, sv[sd->sharpness].sv4);
+ break;
+ }
+ }
+ return 0;
+}
+
+/*
+ * cit_set_lighting()
+ *
+ * Camera model 1:
+ * We have 3 levels of lighting conditions: 0=Bright, 1=Medium, 2=Low.
+ *
+ * Camera model 2:
+ * We have 16 levels of lighting, 0 for bright light and up to 15 for
+ * low light. But values above 5 or so are useless because camera is
+ * not really capable to produce anything worth viewing at such light.
+ * This setting may be altered only in certain camera state.
+ *
+ * Low lighting forces slower FPS.
+ *
+ * History:
+ * 1/5/00 Created.
+ * 2/20/00 Added support for Model 2 cameras.
+ */
+static void cit_set_lighting(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->model) {
+ case CIT_MODEL0:
+ case CIT_MODEL2:
+ case CIT_MODEL3:
+ case CIT_MODEL4:
+ case CIT_IBM_NETCAM_PRO:
+ break;
+ case CIT_MODEL1: {
+ int i;
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x0027, sd->lighting);
+ break;
+ }
+ }
+}
+
+static void cit_set_hflip(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->model) {
+ case CIT_MODEL0:
+ if (sd->hflip)
+ cit_write_reg(gspca_dev, 0x0020, 0x0115);
+ else
+ cit_write_reg(gspca_dev, 0x0040, 0x0115);
+ break;
+ case CIT_MODEL1:
+ case CIT_MODEL2:
+ case CIT_MODEL3:
+ case CIT_MODEL4:
+ case CIT_IBM_NETCAM_PRO:
+ break;
+ }
+}
+
+static int cit_restart_stream(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->model) {
+ case CIT_MODEL0:
+ case CIT_MODEL1:
+ cit_write_reg(gspca_dev, 0x0001, 0x0114);
+ /* Fall through */
+ case CIT_MODEL2:
+ case CIT_MODEL4:
+ cit_write_reg(gspca_dev, 0x00c0, 0x010c); /* Go! */
+ usb_clear_halt(gspca_dev->dev, gspca_dev->urb[0]->pipe);
+ break;
+ case CIT_MODEL3:
+ case CIT_IBM_NETCAM_PRO:
+ cit_write_reg(gspca_dev, 0x0001, 0x0114);
+ cit_write_reg(gspca_dev, 0x00c0, 0x010c); /* Go! */
+ usb_clear_halt(gspca_dev->dev, gspca_dev->urb[0]->pipe);
+ /* Clear button events from while we were not streaming */
+ cit_write_reg(gspca_dev, 0x0001, 0x0113);
+ break;
+ }
+
+ sd->sof_read = 0;
+
+ return 0;
+}
+
+static int cit_get_packet_size(struct gspca_dev *gspca_dev)
+{
+ struct usb_host_interface *alt;
+ struct usb_interface *intf;
+
+ intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
+ alt = usb_altnum_to_altsetting(intf, gspca_dev->alt);
+ if (!alt) {
+ pr_err("Couldn't get altsetting\n");
+ return -EIO;
+ }
+
+ return le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+}
+
+/* Calculate the clockdiv giving us max fps given the available bandwidth */
+static int cit_get_clock_div(struct gspca_dev *gspca_dev)
+{
+ int clock_div = 7; /* 0=30 1=25 2=20 3=15 4=12 5=7.5 6=6 7=3fps ?? */
+ int fps[8] = { 30, 25, 20, 15, 12, 8, 6, 3 };
+ int packet_size;
+
+ packet_size = cit_get_packet_size(gspca_dev);
+ if (packet_size < 0)
+ return packet_size;
+
+ while (clock_div > 3 &&
+ 1000 * packet_size >
+ gspca_dev->width * gspca_dev->height *
+ fps[clock_div - 1] * 3 / 2)
+ clock_div--;
+
+ PDEBUG(D_PROBE,
+ "PacketSize: %d, res: %dx%d -> using clockdiv: %d (%d fps)",
+ packet_size, gspca_dev->width, gspca_dev->height, clock_div,
+ fps[clock_div]);
+
+ return clock_div;
+}
+
+static int cit_start_model0(struct gspca_dev *gspca_dev)
+{
+ const unsigned short compression = 0; /* 0=none, 7=best frame rate */
+ int clock_div;
+
+ clock_div = cit_get_clock_div(gspca_dev);
+ if (clock_div < 0)
+ return clock_div;
+
+ cit_write_reg(gspca_dev, 0x0000, 0x0100); /* turn on led */
+ cit_write_reg(gspca_dev, 0x0003, 0x0438);
+ cit_write_reg(gspca_dev, 0x001e, 0x042b);
+ cit_write_reg(gspca_dev, 0x0041, 0x042c);
+ cit_write_reg(gspca_dev, 0x0008, 0x0436);
+ cit_write_reg(gspca_dev, 0x0024, 0x0403);
+ cit_write_reg(gspca_dev, 0x002c, 0x0404);
+ cit_write_reg(gspca_dev, 0x0002, 0x0426);
+ cit_write_reg(gspca_dev, 0x0014, 0x0427);
+
+ switch (gspca_dev->width) {
+ case 160: /* 160x120 */
+ cit_write_reg(gspca_dev, 0x0004, 0x010b);
+ cit_write_reg(gspca_dev, 0x0001, 0x010a);
+ cit_write_reg(gspca_dev, 0x0010, 0x0102);
+ cit_write_reg(gspca_dev, 0x00a0, 0x0103);
+ cit_write_reg(gspca_dev, 0x0000, 0x0104);
+ cit_write_reg(gspca_dev, 0x0078, 0x0105);
+ break;
+
+ case 176: /* 176x144 */
+ cit_write_reg(gspca_dev, 0x0006, 0x010b);
+ cit_write_reg(gspca_dev, 0x0000, 0x010a);
+ cit_write_reg(gspca_dev, 0x0005, 0x0102);
+ cit_write_reg(gspca_dev, 0x00b0, 0x0103);
+ cit_write_reg(gspca_dev, 0x0000, 0x0104);
+ cit_write_reg(gspca_dev, 0x0090, 0x0105);
+ break;
+
+ case 320: /* 320x240 */
+ cit_write_reg(gspca_dev, 0x0008, 0x010b);
+ cit_write_reg(gspca_dev, 0x0004, 0x010a);
+ cit_write_reg(gspca_dev, 0x0005, 0x0102);
+ cit_write_reg(gspca_dev, 0x00a0, 0x0103);
+ cit_write_reg(gspca_dev, 0x0010, 0x0104);
+ cit_write_reg(gspca_dev, 0x0078, 0x0105);
+ break;
+ }
+
+ cit_write_reg(gspca_dev, compression, 0x0109);
+ cit_write_reg(gspca_dev, clock_div, 0x0111);
+
+ return 0;
+}
+
+static int cit_start_model1(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, clock_div;
+
+ clock_div = cit_get_clock_div(gspca_dev);
+ if (clock_div < 0)
+ return clock_div;
+
+ cit_read_reg(gspca_dev, 0x0128, 1);
+ cit_read_reg(gspca_dev, 0x0100, 0);
+ cit_write_reg(gspca_dev, 0x01, 0x0100); /* LED On */
+ cit_read_reg(gspca_dev, 0x0100, 0);
+ cit_write_reg(gspca_dev, 0x81, 0x0100); /* LED Off */
+ cit_read_reg(gspca_dev, 0x0100, 0);
+ cit_write_reg(gspca_dev, 0x01, 0x0100); /* LED On */
+ cit_write_reg(gspca_dev, 0x01, 0x0108);
+
+ cit_write_reg(gspca_dev, 0x03, 0x0112);
+ cit_read_reg(gspca_dev, 0x0115, 0);
+ cit_write_reg(gspca_dev, 0x06, 0x0115);
+ cit_read_reg(gspca_dev, 0x0116, 0);
+ cit_write_reg(gspca_dev, 0x44, 0x0116);
+ cit_read_reg(gspca_dev, 0x0116, 0);
+ cit_write_reg(gspca_dev, 0x40, 0x0116);
+ cit_read_reg(gspca_dev, 0x0115, 0);
+ cit_write_reg(gspca_dev, 0x0e, 0x0115);
+ cit_write_reg(gspca_dev, 0x19, 0x012c);
+
+ cit_Packet_Format1(gspca_dev, 0x00, 0x1e);
+ cit_Packet_Format1(gspca_dev, 0x39, 0x0d);
+ cit_Packet_Format1(gspca_dev, 0x39, 0x09);
+ cit_Packet_Format1(gspca_dev, 0x3b, 0x00);
+ cit_Packet_Format1(gspca_dev, 0x28, 0x22);
+ cit_Packet_Format1(gspca_dev, 0x27, 0x00);
+ cit_Packet_Format1(gspca_dev, 0x2b, 0x1f);
+ cit_Packet_Format1(gspca_dev, 0x39, 0x08);
+
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x2c, 0x00);
+
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x30, 0x14);
+
+ cit_PacketFormat2(gspca_dev, 0x39, 0x02);
+ cit_PacketFormat2(gspca_dev, 0x01, 0xe1);
+ cit_PacketFormat2(gspca_dev, 0x02, 0xcd);
+ cit_PacketFormat2(gspca_dev, 0x03, 0xcd);
+ cit_PacketFormat2(gspca_dev, 0x04, 0xfa);
+ cit_PacketFormat2(gspca_dev, 0x3f, 0xff);
+ cit_PacketFormat2(gspca_dev, 0x39, 0x00);
+
+ cit_PacketFormat2(gspca_dev, 0x39, 0x02);
+ cit_PacketFormat2(gspca_dev, 0x0a, 0x37);
+ cit_PacketFormat2(gspca_dev, 0x0b, 0xb8);
+ cit_PacketFormat2(gspca_dev, 0x0c, 0xf3);
+ cit_PacketFormat2(gspca_dev, 0x0d, 0xe3);
+ cit_PacketFormat2(gspca_dev, 0x0e, 0x0d);
+ cit_PacketFormat2(gspca_dev, 0x0f, 0xf2);
+ cit_PacketFormat2(gspca_dev, 0x10, 0xd5);
+ cit_PacketFormat2(gspca_dev, 0x11, 0xba);
+ cit_PacketFormat2(gspca_dev, 0x12, 0x53);
+ cit_PacketFormat2(gspca_dev, 0x3f, 0xff);
+ cit_PacketFormat2(gspca_dev, 0x39, 0x00);
+
+ cit_PacketFormat2(gspca_dev, 0x39, 0x02);
+ cit_PacketFormat2(gspca_dev, 0x16, 0x00);
+ cit_PacketFormat2(gspca_dev, 0x17, 0x28);
+ cit_PacketFormat2(gspca_dev, 0x18, 0x7d);
+ cit_PacketFormat2(gspca_dev, 0x19, 0xbe);
+ cit_PacketFormat2(gspca_dev, 0x3f, 0xff);
+ cit_PacketFormat2(gspca_dev, 0x39, 0x00);
+
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x00, 0x18);
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x13, 0x18);
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x14, 0x06);
+
+ /* TESTME These are handled through controls
+ KEEP until someone can test leaving this out is ok */
+ if (0) {
+ /* This is default brightness */
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x31, 0x37);
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x32, 0x46);
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x33, 0x55);
+ }
+
+ cit_Packet_Format1(gspca_dev, 0x2e, 0x04);
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x2d, 0x04);
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x29, 0x80);
+ cit_Packet_Format1(gspca_dev, 0x2c, 0x01);
+ cit_Packet_Format1(gspca_dev, 0x30, 0x17);
+ cit_Packet_Format1(gspca_dev, 0x39, 0x08);
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x34, 0x00);
+
+ cit_write_reg(gspca_dev, 0x00, 0x0101);
+ cit_write_reg(gspca_dev, 0x00, 0x010a);
+
+ switch (gspca_dev->width) {
+ case 128: /* 128x96 */
+ cit_write_reg(gspca_dev, 0x80, 0x0103);
+ cit_write_reg(gspca_dev, 0x60, 0x0105);
+ cit_write_reg(gspca_dev, 0x0c, 0x010b);
+ cit_write_reg(gspca_dev, 0x04, 0x011b); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x0b, 0x011d);
+ cit_write_reg(gspca_dev, 0x00, 0x011e); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x00, 0x0129);
+ break;
+ case 176: /* 176x144 */
+ cit_write_reg(gspca_dev, 0xb0, 0x0103);
+ cit_write_reg(gspca_dev, 0x8f, 0x0105);
+ cit_write_reg(gspca_dev, 0x06, 0x010b);
+ cit_write_reg(gspca_dev, 0x04, 0x011b); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x0d, 0x011d);
+ cit_write_reg(gspca_dev, 0x00, 0x011e); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x03, 0x0129);
+ break;
+ case 352: /* 352x288 */
+ cit_write_reg(gspca_dev, 0xb0, 0x0103);
+ cit_write_reg(gspca_dev, 0x90, 0x0105);
+ cit_write_reg(gspca_dev, 0x02, 0x010b);
+ cit_write_reg(gspca_dev, 0x04, 0x011b); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x05, 0x011d);
+ cit_write_reg(gspca_dev, 0x00, 0x011e); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x00, 0x0129);
+ break;
+ }
+
+ cit_write_reg(gspca_dev, 0xff, 0x012b);
+
+ /* TESTME These are handled through controls
+ KEEP until someone can test leaving this out is ok */
+ if (0) {
+ /* This is another brightness - don't know why */
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x31, 0xc3);
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x32, 0xd2);
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x33, 0xe1);
+
+ /* Default contrast */
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x14, 0x0a);
+
+ /* Default sharpness */
+ for (i = 0; i < cit_model1_ntries2; i++)
+ cit_PacketFormat2(gspca_dev, 0x13, 0x1a);
+
+ /* Default lighting conditions */
+ cit_Packet_Format1(gspca_dev, 0x0027, sd->lighting);
+ }
+
+ /* Assorted init */
+ switch (gspca_dev->width) {
+ case 128: /* 128x96 */
+ cit_Packet_Format1(gspca_dev, 0x2b, 0x1e);
+ cit_write_reg(gspca_dev, 0xc9, 0x0119); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x80, 0x0109); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x36, 0x0102);
+ cit_write_reg(gspca_dev, 0x1a, 0x0104);
+ cit_write_reg(gspca_dev, 0x04, 0x011a); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x2b, 0x011c);
+ cit_write_reg(gspca_dev, 0x23, 0x012a); /* Same everywhere */
+ break;
+ case 176: /* 176x144 */
+ cit_Packet_Format1(gspca_dev, 0x2b, 0x1e);
+ cit_write_reg(gspca_dev, 0xc9, 0x0119); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x80, 0x0109); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x04, 0x0102);
+ cit_write_reg(gspca_dev, 0x02, 0x0104);
+ cit_write_reg(gspca_dev, 0x04, 0x011a); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x2b, 0x011c);
+ cit_write_reg(gspca_dev, 0x23, 0x012a); /* Same everywhere */
+ break;
+ case 352: /* 352x288 */
+ cit_Packet_Format1(gspca_dev, 0x2b, 0x1f);
+ cit_write_reg(gspca_dev, 0xc9, 0x0119); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x80, 0x0109); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x08, 0x0102);
+ cit_write_reg(gspca_dev, 0x01, 0x0104);
+ cit_write_reg(gspca_dev, 0x04, 0x011a); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x2f, 0x011c);
+ cit_write_reg(gspca_dev, 0x23, 0x012a); /* Same everywhere */
+ break;
+ }
+
+ cit_write_reg(gspca_dev, 0x01, 0x0100); /* LED On */
+ cit_write_reg(gspca_dev, clock_div, 0x0111);
+
+ return 0;
+}
+
+static int cit_start_model2(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int clock_div = 0;
+
+ cit_write_reg(gspca_dev, 0x0000, 0x0100); /* LED on */
+ cit_read_reg(gspca_dev, 0x0116, 0);
+ cit_write_reg(gspca_dev, 0x0060, 0x0116);
+ cit_write_reg(gspca_dev, 0x0002, 0x0112);
+ cit_write_reg(gspca_dev, 0x00bc, 0x012c);
+ cit_write_reg(gspca_dev, 0x0008, 0x012b);
+ cit_write_reg(gspca_dev, 0x0000, 0x0108);
+ cit_write_reg(gspca_dev, 0x0001, 0x0133);
+ cit_write_reg(gspca_dev, 0x0001, 0x0102);
+ switch (gspca_dev->width) {
+ case 176: /* 176x144 */
+ cit_write_reg(gspca_dev, 0x002c, 0x0103); /* All except 320x240 */
+ cit_write_reg(gspca_dev, 0x0000, 0x0104); /* Same */
+ cit_write_reg(gspca_dev, 0x0024, 0x0105); /* 176x144, 352x288 */
+ cit_write_reg(gspca_dev, 0x00b9, 0x010a); /* Unique to this mode */
+ cit_write_reg(gspca_dev, 0x0038, 0x0119); /* Unique to this mode */
+ /* TESTME HDG: this does not seem right
+ (it is 2 for all other resolutions) */
+ sd->sof_len = 10;
+ break;
+ case 320: /* 320x240 */
+ cit_write_reg(gspca_dev, 0x0028, 0x0103); /* Unique to this mode */
+ cit_write_reg(gspca_dev, 0x0000, 0x0104); /* Same */
+ cit_write_reg(gspca_dev, 0x001e, 0x0105); /* 320x240, 352x240 */
+ cit_write_reg(gspca_dev, 0x0039, 0x010a); /* All except 176x144 */
+ cit_write_reg(gspca_dev, 0x0070, 0x0119); /* All except 176x144 */
+ sd->sof_len = 2;
+ break;
+ /* case VIDEOSIZE_352x240: */
+ cit_write_reg(gspca_dev, 0x002c, 0x0103); /* All except 320x240 */
+ cit_write_reg(gspca_dev, 0x0000, 0x0104); /* Same */
+ cit_write_reg(gspca_dev, 0x001e, 0x0105); /* 320x240, 352x240 */
+ cit_write_reg(gspca_dev, 0x0039, 0x010a); /* All except 176x144 */
+ cit_write_reg(gspca_dev, 0x0070, 0x0119); /* All except 176x144 */
+ sd->sof_len = 2;
+ break;
+ case 352: /* 352x288 */
+ cit_write_reg(gspca_dev, 0x002c, 0x0103); /* All except 320x240 */
+ cit_write_reg(gspca_dev, 0x0000, 0x0104); /* Same */
+ cit_write_reg(gspca_dev, 0x0024, 0x0105); /* 176x144, 352x288 */
+ cit_write_reg(gspca_dev, 0x0039, 0x010a); /* All except 176x144 */
+ cit_write_reg(gspca_dev, 0x0070, 0x0119); /* All except 176x144 */
+ sd->sof_len = 2;
+ break;
+ }
+
+ cit_write_reg(gspca_dev, 0x0000, 0x0100); /* LED on */
+
+ switch (gspca_dev->width) {
+ case 176: /* 176x144 */
+ cit_write_reg(gspca_dev, 0x0050, 0x0111);
+ cit_write_reg(gspca_dev, 0x00d0, 0x0111);
+ break;
+ case 320: /* 320x240 */
+ case 352: /* 352x288 */
+ cit_write_reg(gspca_dev, 0x0040, 0x0111);
+ cit_write_reg(gspca_dev, 0x00c0, 0x0111);
+ break;
+ }
+ cit_write_reg(gspca_dev, 0x009b, 0x010f);
+ cit_write_reg(gspca_dev, 0x00bb, 0x010f);
+
+ /*
+ * Hardware settings, may affect CMOS sensor; not user controls!
+ * -------------------------------------------------------------
+ * 0x0004: no effect
+ * 0x0006: hardware effect
+ * 0x0008: no effect
+ * 0x000a: stops video stream, probably important h/w setting
+ * 0x000c: changes color in hardware manner (not user setting)
+ * 0x0012: changes number of colors (does not affect speed)
+ * 0x002a: no effect
+ * 0x002c: hardware setting (related to scan lines)
+ * 0x002e: stops video stream, probably important h/w setting
+ */
+ cit_model2_Packet1(gspca_dev, 0x000a, 0x005c);
+ cit_model2_Packet1(gspca_dev, 0x0004, 0x0000);
+ cit_model2_Packet1(gspca_dev, 0x0006, 0x00fb);
+ cit_model2_Packet1(gspca_dev, 0x0008, 0x0000);
+ cit_model2_Packet1(gspca_dev, 0x000c, 0x0009);
+ cit_model2_Packet1(gspca_dev, 0x0012, 0x000a);
+ cit_model2_Packet1(gspca_dev, 0x002a, 0x0000);
+ cit_model2_Packet1(gspca_dev, 0x002c, 0x0000);
+ cit_model2_Packet1(gspca_dev, 0x002e, 0x0008);
+
+ /*
+ * Function 0x0030 pops up all over the place. Apparently
+ * it is a hardware control register, with every bit assigned to
+ * do something.
+ */
+ cit_model2_Packet1(gspca_dev, 0x0030, 0x0000);
+
+ /*
+ * Magic control of CMOS sensor. Only lower values like
+ * 0-3 work, and picture shifts left or right. Don't change.
+ */
+ switch (gspca_dev->width) {
+ case 176: /* 176x144 */
+ cit_model2_Packet1(gspca_dev, 0x0014, 0x0002);
+ cit_model2_Packet1(gspca_dev, 0x0016, 0x0002); /* Horizontal shift */
+ cit_model2_Packet1(gspca_dev, 0x0018, 0x004a); /* Another hardware setting */
+ clock_div = 6;
+ break;
+ case 320: /* 320x240 */
+ cit_model2_Packet1(gspca_dev, 0x0014, 0x0009);
+ cit_model2_Packet1(gspca_dev, 0x0016, 0x0005); /* Horizontal shift */
+ cit_model2_Packet1(gspca_dev, 0x0018, 0x0044); /* Another hardware setting */
+ clock_div = 8;
+ break;
+ /* case VIDEOSIZE_352x240: */
+ /* This mode doesn't work as Windows programs it; changed to work */
+ cit_model2_Packet1(gspca_dev, 0x0014, 0x0009); /* Windows sets this to 8 */
+ cit_model2_Packet1(gspca_dev, 0x0016, 0x0003); /* Horizontal shift */
+ cit_model2_Packet1(gspca_dev, 0x0018, 0x0044); /* Windows sets this to 0x0045 */
+ clock_div = 10;
+ break;
+ case 352: /* 352x288 */
+ cit_model2_Packet1(gspca_dev, 0x0014, 0x0003);
+ cit_model2_Packet1(gspca_dev, 0x0016, 0x0002); /* Horizontal shift */
+ cit_model2_Packet1(gspca_dev, 0x0018, 0x004a); /* Another hardware setting */
+ clock_div = 16;
+ break;
+ }
+
+ /* TESTME These are handled through controls
+ KEEP until someone can test leaving this out is ok */
+ if (0)
+ cit_model2_Packet1(gspca_dev, 0x001a, 0x005a);
+
+ /*
+ * We have our own frame rate setting varying from 0 (slowest) to 6
+ * (fastest). The camera model 2 allows frame rate in range [0..0x1F]
+ # where 0 is also the slowest setting. However for all practical
+ # reasons high settings make no sense because USB is not fast enough
+ # to support high FPS. Be aware that the picture datastream will be
+ # severely disrupted if you ask for frame rate faster than allowed
+ # for the video size - see below:
+ *
+ * Allowable ranges (obtained experimentally on OHCI, K6-3, 450 MHz):
+ * -----------------------------------------------------------------
+ * 176x144: [6..31]
+ * 320x240: [8..31]
+ * 352x240: [10..31]
+ * 352x288: [16..31] I have to raise lower threshold for stability...
+ *
+ * As usual, slower FPS provides better sensitivity.
+ */
+ cit_model2_Packet1(gspca_dev, 0x001c, clock_div);
+
+ /*
+ * This setting does not visibly affect pictures; left it here
+ * because it was present in Windows USB data stream. This function
+ * does not allow arbitrary values and apparently is a bit mask, to
+ * be activated only at appropriate time. Don't change it randomly!
+ */
+ switch (gspca_dev->width) {
+ case 176: /* 176x144 */
+ cit_model2_Packet1(gspca_dev, 0x0026, 0x00c2);
+ break;
+ case 320: /* 320x240 */
+ cit_model2_Packet1(gspca_dev, 0x0026, 0x0044);
+ break;
+ /* case VIDEOSIZE_352x240: */
+ cit_model2_Packet1(gspca_dev, 0x0026, 0x0046);
+ break;
+ case 352: /* 352x288 */
+ cit_model2_Packet1(gspca_dev, 0x0026, 0x0048);
+ break;
+ }
+
+ /* FIXME this cannot be changed while streaming, so we
+ should report a grabbed flag for this control. */
+ cit_model2_Packet1(gspca_dev, 0x0028, sd->lighting);
+ /* color balance rg2 */
+ cit_model2_Packet1(gspca_dev, 0x001e, 0x002f);
+ /* saturation */
+ cit_model2_Packet1(gspca_dev, 0x0020, 0x0034);
+ /* color balance yb */
+ cit_model2_Packet1(gspca_dev, 0x0022, 0x00a0);
+
+ /* Hardware control command */
+ cit_model2_Packet1(gspca_dev, 0x0030, 0x0004);
+
+ return 0;
+}
+
+static int cit_start_model3(struct gspca_dev *gspca_dev)
+{
+ const unsigned short compression = 0; /* 0=none, 7=best frame rate */
+ int i, clock_div = 0;
+
+ /* HDG not in ibmcam driver, added to see if it helps with
+ auto-detecting between model3 and ibm netcamera pro */
+ cit_read_reg(gspca_dev, 0x128, 1);
+
+ cit_write_reg(gspca_dev, 0x0000, 0x0100);
+ cit_read_reg(gspca_dev, 0x0116, 0);
+ cit_write_reg(gspca_dev, 0x0060, 0x0116);
+ cit_write_reg(gspca_dev, 0x0002, 0x0112);
+ cit_write_reg(gspca_dev, 0x0000, 0x0123);
+ cit_write_reg(gspca_dev, 0x0001, 0x0117);
+ cit_write_reg(gspca_dev, 0x0040, 0x0108);
+ cit_write_reg(gspca_dev, 0x0019, 0x012c);
+ cit_write_reg(gspca_dev, 0x0060, 0x0116);
+ cit_write_reg(gspca_dev, 0x0002, 0x0115);
+ cit_write_reg(gspca_dev, 0x0003, 0x0115);
+ cit_read_reg(gspca_dev, 0x0115, 0);
+ cit_write_reg(gspca_dev, 0x000b, 0x0115);
+
+ /* TESTME HDG not in ibmcam driver, added to see if it helps with
+ auto-detecting between model3 and ibm netcamera pro */
+ if (0) {
+ cit_write_reg(gspca_dev, 0x0078, 0x012d);
+ cit_write_reg(gspca_dev, 0x0001, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0079, 0x012d);
+ cit_write_reg(gspca_dev, 0x00ff, 0x0130);
+ cit_write_reg(gspca_dev, 0xcd41, 0x0124);
+ cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+ cit_read_reg(gspca_dev, 0x0126, 1);
+ }
+
+ cit_model3_Packet1(gspca_dev, 0x000a, 0x0040);
+ cit_model3_Packet1(gspca_dev, 0x000b, 0x00f6);
+ cit_model3_Packet1(gspca_dev, 0x000c, 0x0002);
+ cit_model3_Packet1(gspca_dev, 0x000d, 0x0020);
+ cit_model3_Packet1(gspca_dev, 0x000e, 0x0033);
+ cit_model3_Packet1(gspca_dev, 0x000f, 0x0007);
+ cit_model3_Packet1(gspca_dev, 0x0010, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0011, 0x0070);
+ cit_model3_Packet1(gspca_dev, 0x0012, 0x0030);
+ cit_model3_Packet1(gspca_dev, 0x0013, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0014, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x0015, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x0016, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x0017, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x0018, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x001e, 0x00c3);
+ cit_model3_Packet1(gspca_dev, 0x0020, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0028, 0x0010);
+ cit_model3_Packet1(gspca_dev, 0x0029, 0x0054);
+ cit_model3_Packet1(gspca_dev, 0x002a, 0x0013);
+ cit_model3_Packet1(gspca_dev, 0x002b, 0x0007);
+ cit_model3_Packet1(gspca_dev, 0x002d, 0x0028);
+ cit_model3_Packet1(gspca_dev, 0x002e, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0031, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0032, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0033, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0034, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0035, 0x0038);
+ cit_model3_Packet1(gspca_dev, 0x003a, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x003c, 0x001e);
+ cit_model3_Packet1(gspca_dev, 0x003f, 0x000a);
+ cit_model3_Packet1(gspca_dev, 0x0041, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0046, 0x003f);
+ cit_model3_Packet1(gspca_dev, 0x0047, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0050, 0x0005);
+ cit_model3_Packet1(gspca_dev, 0x0052, 0x001a);
+ cit_model3_Packet1(gspca_dev, 0x0053, 0x0003);
+ cit_model3_Packet1(gspca_dev, 0x005a, 0x006b);
+ cit_model3_Packet1(gspca_dev, 0x005d, 0x001e);
+ cit_model3_Packet1(gspca_dev, 0x005e, 0x0030);
+ cit_model3_Packet1(gspca_dev, 0x005f, 0x0041);
+ cit_model3_Packet1(gspca_dev, 0x0064, 0x0008);
+ cit_model3_Packet1(gspca_dev, 0x0065, 0x0015);
+ cit_model3_Packet1(gspca_dev, 0x0068, 0x000f);
+ cit_model3_Packet1(gspca_dev, 0x0079, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x007a, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x007c, 0x003f);
+ cit_model3_Packet1(gspca_dev, 0x0082, 0x000f);
+ cit_model3_Packet1(gspca_dev, 0x0085, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0099, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x009b, 0x0023);
+ cit_model3_Packet1(gspca_dev, 0x009c, 0x0022);
+ cit_model3_Packet1(gspca_dev, 0x009d, 0x0096);
+ cit_model3_Packet1(gspca_dev, 0x009e, 0x0096);
+ cit_model3_Packet1(gspca_dev, 0x009f, 0x000a);
+
+ switch (gspca_dev->width) {
+ case 160:
+ cit_write_reg(gspca_dev, 0x0000, 0x0101); /* Same on 160x120, 320x240 */
+ cit_write_reg(gspca_dev, 0x00a0, 0x0103); /* Same on 160x120, 320x240 */
+ cit_write_reg(gspca_dev, 0x0078, 0x0105); /* Same on 160x120, 320x240 */
+ cit_write_reg(gspca_dev, 0x0000, 0x010a); /* Same */
+ cit_write_reg(gspca_dev, 0x0024, 0x010b); /* Differs everywhere */
+ cit_write_reg(gspca_dev, 0x00a9, 0x0119);
+ cit_write_reg(gspca_dev, 0x0016, 0x011b);
+ cit_write_reg(gspca_dev, 0x0002, 0x011d); /* Same on 160x120, 320x240 */
+ cit_write_reg(gspca_dev, 0x0003, 0x011e); /* Same on 160x120, 640x480 */
+ cit_write_reg(gspca_dev, 0x0000, 0x0129); /* Same */
+ cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */
+ cit_write_reg(gspca_dev, 0x0018, 0x0102);
+ cit_write_reg(gspca_dev, 0x0004, 0x0104);
+ cit_write_reg(gspca_dev, 0x0004, 0x011a);
+ cit_write_reg(gspca_dev, 0x0028, 0x011c);
+ cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */
+ cit_write_reg(gspca_dev, 0x0000, 0x0118);
+ cit_write_reg(gspca_dev, 0x0000, 0x0132);
+ cit_model3_Packet1(gspca_dev, 0x0021, 0x0001); /* Same */
+ cit_write_reg(gspca_dev, compression, 0x0109);
+ clock_div = 3;
+ break;
+ case 320:
+ cit_write_reg(gspca_dev, 0x0000, 0x0101); /* Same on 160x120, 320x240 */
+ cit_write_reg(gspca_dev, 0x00a0, 0x0103); /* Same on 160x120, 320x240 */
+ cit_write_reg(gspca_dev, 0x0078, 0x0105); /* Same on 160x120, 320x240 */
+ cit_write_reg(gspca_dev, 0x0000, 0x010a); /* Same */
+ cit_write_reg(gspca_dev, 0x0028, 0x010b); /* Differs everywhere */
+ cit_write_reg(gspca_dev, 0x0002, 0x011d); /* Same */
+ cit_write_reg(gspca_dev, 0x0000, 0x011e);
+ cit_write_reg(gspca_dev, 0x0000, 0x0129); /* Same */
+ cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */
+ /* 4 commands from 160x120 skipped */
+ cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */
+ cit_model3_Packet1(gspca_dev, 0x0021, 0x0001); /* Same */
+ cit_write_reg(gspca_dev, compression, 0x0109);
+ cit_write_reg(gspca_dev, 0x00d9, 0x0119);
+ cit_write_reg(gspca_dev, 0x0006, 0x011b);
+ cit_write_reg(gspca_dev, 0x0021, 0x0102); /* Same on 320x240, 640x480 */
+ cit_write_reg(gspca_dev, 0x0010, 0x0104);
+ cit_write_reg(gspca_dev, 0x0004, 0x011a);
+ cit_write_reg(gspca_dev, 0x003f, 0x011c);
+ cit_write_reg(gspca_dev, 0x001c, 0x0118);
+ cit_write_reg(gspca_dev, 0x0000, 0x0132);
+ clock_div = 5;
+ break;
+ case 640:
+ cit_write_reg(gspca_dev, 0x00f0, 0x0105);
+ cit_write_reg(gspca_dev, 0x0000, 0x010a); /* Same */
+ cit_write_reg(gspca_dev, 0x0038, 0x010b); /* Differs everywhere */
+ cit_write_reg(gspca_dev, 0x00d9, 0x0119); /* Same on 320x240, 640x480 */
+ cit_write_reg(gspca_dev, 0x0006, 0x011b); /* Same on 320x240, 640x480 */
+ cit_write_reg(gspca_dev, 0x0004, 0x011d); /* NC */
+ cit_write_reg(gspca_dev, 0x0003, 0x011e); /* Same on 160x120, 640x480 */
+ cit_write_reg(gspca_dev, 0x0000, 0x0129); /* Same */
+ cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */
+ cit_write_reg(gspca_dev, 0x0021, 0x0102); /* Same on 320x240, 640x480 */
+ cit_write_reg(gspca_dev, 0x0016, 0x0104); /* NC */
+ cit_write_reg(gspca_dev, 0x0004, 0x011a); /* Same on 320x240, 640x480 */
+ cit_write_reg(gspca_dev, 0x003f, 0x011c); /* Same on 320x240, 640x480 */
+ cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */
+ cit_write_reg(gspca_dev, 0x001c, 0x0118); /* Same on 320x240, 640x480 */
+ cit_model3_Packet1(gspca_dev, 0x0021, 0x0001); /* Same */
+ cit_write_reg(gspca_dev, compression, 0x0109);
+ cit_write_reg(gspca_dev, 0x0040, 0x0101);
+ cit_write_reg(gspca_dev, 0x0040, 0x0103);
+ cit_write_reg(gspca_dev, 0x0000, 0x0132); /* Same on 320x240, 640x480 */
+ clock_div = 7;
+ break;
+ }
+
+ cit_model3_Packet1(gspca_dev, 0x007e, 0x000e); /* Hue */
+ cit_model3_Packet1(gspca_dev, 0x0036, 0x0011); /* Brightness */
+ cit_model3_Packet1(gspca_dev, 0x0060, 0x0002); /* Sharpness */
+ cit_model3_Packet1(gspca_dev, 0x0061, 0x0004); /* Sharpness */
+ cit_model3_Packet1(gspca_dev, 0x0062, 0x0005); /* Sharpness */
+ cit_model3_Packet1(gspca_dev, 0x0063, 0x0014); /* Sharpness */
+ cit_model3_Packet1(gspca_dev, 0x0096, 0x00a0); /* Red sharpness */
+ cit_model3_Packet1(gspca_dev, 0x0097, 0x0096); /* Blue sharpness */
+ cit_model3_Packet1(gspca_dev, 0x0067, 0x0001); /* Contrast */
+ cit_model3_Packet1(gspca_dev, 0x005b, 0x000c); /* Contrast */
+ cit_model3_Packet1(gspca_dev, 0x005c, 0x0016); /* Contrast */
+ cit_model3_Packet1(gspca_dev, 0x0098, 0x000b);
+ cit_model3_Packet1(gspca_dev, 0x002c, 0x0003); /* Was 1, broke 640x480 */
+ cit_model3_Packet1(gspca_dev, 0x002f, 0x002a);
+ cit_model3_Packet1(gspca_dev, 0x0030, 0x0029);
+ cit_model3_Packet1(gspca_dev, 0x0037, 0x0002);
+ cit_model3_Packet1(gspca_dev, 0x0038, 0x0059);
+ cit_model3_Packet1(gspca_dev, 0x003d, 0x002e);
+ cit_model3_Packet1(gspca_dev, 0x003e, 0x0028);
+ cit_model3_Packet1(gspca_dev, 0x0078, 0x0005);
+ cit_model3_Packet1(gspca_dev, 0x007b, 0x0011);
+ cit_model3_Packet1(gspca_dev, 0x007d, 0x004b);
+ cit_model3_Packet1(gspca_dev, 0x007f, 0x0022);
+ cit_model3_Packet1(gspca_dev, 0x0080, 0x000c);
+ cit_model3_Packet1(gspca_dev, 0x0081, 0x000b);
+ cit_model3_Packet1(gspca_dev, 0x0083, 0x00fd);
+ cit_model3_Packet1(gspca_dev, 0x0086, 0x000b);
+ cit_model3_Packet1(gspca_dev, 0x0087, 0x000b);
+ cit_model3_Packet1(gspca_dev, 0x007e, 0x000e);
+ cit_model3_Packet1(gspca_dev, 0x0096, 0x00a0); /* Red sharpness */
+ cit_model3_Packet1(gspca_dev, 0x0097, 0x0096); /* Blue sharpness */
+ cit_model3_Packet1(gspca_dev, 0x0098, 0x000b);
+
+ /* FIXME we should probably use cit_get_clock_div() here (in
+ combination with isoc negotiation using the programmable isoc size)
+ like with the IBM netcam pro). */
+ cit_write_reg(gspca_dev, clock_div, 0x0111); /* Clock Divider */
+
+ switch (gspca_dev->width) {
+ case 160:
+ cit_model3_Packet1(gspca_dev, 0x001f, 0x0000); /* Same */
+ cit_model3_Packet1(gspca_dev, 0x0039, 0x001f); /* Same */
+ cit_model3_Packet1(gspca_dev, 0x003b, 0x003c); /* Same */
+ cit_model3_Packet1(gspca_dev, 0x0040, 0x000a);
+ cit_model3_Packet1(gspca_dev, 0x0051, 0x000a);
+ break;
+ case 320:
+ cit_model3_Packet1(gspca_dev, 0x001f, 0x0000); /* Same */
+ cit_model3_Packet1(gspca_dev, 0x0039, 0x001f); /* Same */
+ cit_model3_Packet1(gspca_dev, 0x003b, 0x003c); /* Same */
+ cit_model3_Packet1(gspca_dev, 0x0040, 0x0008);
+ cit_model3_Packet1(gspca_dev, 0x0051, 0x000b);
+ break;
+ case 640:
+ cit_model3_Packet1(gspca_dev, 0x001f, 0x0002); /* !Same */
+ cit_model3_Packet1(gspca_dev, 0x0039, 0x003e); /* !Same */
+ cit_model3_Packet1(gspca_dev, 0x0040, 0x0008);
+ cit_model3_Packet1(gspca_dev, 0x0051, 0x000a);
+ break;
+ }
+
+/* if (sd->input_index) { */
+ if (rca_input) {
+ for (i = 0; i < ARRAY_SIZE(rca_initdata); i++) {
+ if (rca_initdata[i][0])
+ cit_read_reg(gspca_dev, rca_initdata[i][2], 0);
+ else
+ cit_write_reg(gspca_dev, rca_initdata[i][1],
+ rca_initdata[i][2]);
+ }
+ }
+
+ return 0;
+}
+
+static int cit_start_model4(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ cit_write_reg(gspca_dev, 0x0000, 0x0100);
+ cit_write_reg(gspca_dev, 0x00c0, 0x0111);
+ cit_write_reg(gspca_dev, 0x00bc, 0x012c);
+ cit_write_reg(gspca_dev, 0x0080, 0x012b);
+ cit_write_reg(gspca_dev, 0x0000, 0x0108);
+ cit_write_reg(gspca_dev, 0x0001, 0x0133);
+ cit_write_reg(gspca_dev, 0x009b, 0x010f);
+ cit_write_reg(gspca_dev, 0x00bb, 0x010f);
+ cit_model4_Packet1(gspca_dev, 0x0038, 0x0000);
+ cit_model4_Packet1(gspca_dev, 0x000a, 0x005c);
+
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x0004, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0000, 0x0127);
+ cit_write_reg(gspca_dev, 0x00fb, 0x012e);
+ cit_write_reg(gspca_dev, 0x0000, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012f);
+ cit_write_reg(gspca_dev, 0xd055, 0x0124);
+ cit_write_reg(gspca_dev, 0x000c, 0x0127);
+ cit_write_reg(gspca_dev, 0x0009, 0x012e);
+ cit_write_reg(gspca_dev, 0xaa28, 0x0124);
+
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x0012, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0008, 0x0127);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+ cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+ cit_write_reg(gspca_dev, 0x002a, 0x012d);
+ cit_write_reg(gspca_dev, 0x0000, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+ cit_model4_Packet1(gspca_dev, 0x0034, 0x0000);
+
+ switch (gspca_dev->width) {
+ case 128: /* 128x96 */
+ cit_write_reg(gspca_dev, 0x0070, 0x0119);
+ cit_write_reg(gspca_dev, 0x00d0, 0x0111);
+ cit_write_reg(gspca_dev, 0x0039, 0x010a);
+ cit_write_reg(gspca_dev, 0x0001, 0x0102);
+ cit_write_reg(gspca_dev, 0x0028, 0x0103);
+ cit_write_reg(gspca_dev, 0x0000, 0x0104);
+ cit_write_reg(gspca_dev, 0x001e, 0x0105);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x0016, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x000a, 0x0127);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+ cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+ cit_write_reg(gspca_dev, 0x0014, 0x012d);
+ cit_write_reg(gspca_dev, 0x0008, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012e);
+ cit_write_reg(gspca_dev, 0x001a, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
+ cit_write_reg(gspca_dev, 0x005a, 0x012d);
+ cit_write_reg(gspca_dev, 0x9545, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0127);
+ cit_write_reg(gspca_dev, 0x0018, 0x012e);
+ cit_write_reg(gspca_dev, 0x0043, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012f);
+ cit_write_reg(gspca_dev, 0xd055, 0x0124);
+ cit_write_reg(gspca_dev, 0x001c, 0x0127);
+ cit_write_reg(gspca_dev, 0x00eb, 0x012e);
+ cit_write_reg(gspca_dev, 0xaa28, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x0032, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0000, 0x0127);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+ cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+ cit_write_reg(gspca_dev, 0x0036, 0x012d);
+ cit_write_reg(gspca_dev, 0x0008, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x001e, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0017, 0x0127);
+ cit_write_reg(gspca_dev, 0x0013, 0x012e);
+ cit_write_reg(gspca_dev, 0x0031, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+ cit_write_reg(gspca_dev, 0x0017, 0x012d);
+ cit_write_reg(gspca_dev, 0x0078, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0x0000, 0x0127);
+ cit_write_reg(gspca_dev, 0xfea8, 0x0124);
+ sd->sof_len = 2;
+ break;
+ case 160: /* 160x120 */
+ cit_write_reg(gspca_dev, 0x0038, 0x0119);
+ cit_write_reg(gspca_dev, 0x00d0, 0x0111);
+ cit_write_reg(gspca_dev, 0x00b9, 0x010a);
+ cit_write_reg(gspca_dev, 0x0001, 0x0102);
+ cit_write_reg(gspca_dev, 0x0028, 0x0103);
+ cit_write_reg(gspca_dev, 0x0000, 0x0104);
+ cit_write_reg(gspca_dev, 0x001e, 0x0105);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x0016, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x000b, 0x0127);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+ cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+ cit_write_reg(gspca_dev, 0x0014, 0x012d);
+ cit_write_reg(gspca_dev, 0x0008, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012e);
+ cit_write_reg(gspca_dev, 0x001a, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
+ cit_write_reg(gspca_dev, 0x005a, 0x012d);
+ cit_write_reg(gspca_dev, 0x9545, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0127);
+ cit_write_reg(gspca_dev, 0x0018, 0x012e);
+ cit_write_reg(gspca_dev, 0x0043, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012f);
+ cit_write_reg(gspca_dev, 0xd055, 0x0124);
+ cit_write_reg(gspca_dev, 0x001c, 0x0127);
+ cit_write_reg(gspca_dev, 0x00c7, 0x012e);
+ cit_write_reg(gspca_dev, 0xaa28, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x0032, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0025, 0x0127);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+ cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+ cit_write_reg(gspca_dev, 0x0036, 0x012d);
+ cit_write_reg(gspca_dev, 0x0008, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x001e, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0048, 0x0127);
+ cit_write_reg(gspca_dev, 0x0035, 0x012e);
+ cit_write_reg(gspca_dev, 0x00d0, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+ cit_write_reg(gspca_dev, 0x0048, 0x012d);
+ cit_write_reg(gspca_dev, 0x0090, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0x0001, 0x0127);
+ cit_write_reg(gspca_dev, 0xfea8, 0x0124);
+ sd->sof_len = 2;
+ break;
+ case 176: /* 176x144 */
+ cit_write_reg(gspca_dev, 0x0038, 0x0119);
+ cit_write_reg(gspca_dev, 0x00d0, 0x0111);
+ cit_write_reg(gspca_dev, 0x00b9, 0x010a);
+ cit_write_reg(gspca_dev, 0x0001, 0x0102);
+ cit_write_reg(gspca_dev, 0x002c, 0x0103);
+ cit_write_reg(gspca_dev, 0x0000, 0x0104);
+ cit_write_reg(gspca_dev, 0x0024, 0x0105);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x0016, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0007, 0x0127);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+ cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+ cit_write_reg(gspca_dev, 0x0014, 0x012d);
+ cit_write_reg(gspca_dev, 0x0001, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012e);
+ cit_write_reg(gspca_dev, 0x001a, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
+ cit_write_reg(gspca_dev, 0x005e, 0x012d);
+ cit_write_reg(gspca_dev, 0x9545, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0127);
+ cit_write_reg(gspca_dev, 0x0018, 0x012e);
+ cit_write_reg(gspca_dev, 0x0049, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012f);
+ cit_write_reg(gspca_dev, 0xd055, 0x0124);
+ cit_write_reg(gspca_dev, 0x001c, 0x0127);
+ cit_write_reg(gspca_dev, 0x00c7, 0x012e);
+ cit_write_reg(gspca_dev, 0xaa28, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x0032, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0028, 0x0127);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+ cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+ cit_write_reg(gspca_dev, 0x0036, 0x012d);
+ cit_write_reg(gspca_dev, 0x0008, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x001e, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0010, 0x0127);
+ cit_write_reg(gspca_dev, 0x0013, 0x012e);
+ cit_write_reg(gspca_dev, 0x002a, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+ cit_write_reg(gspca_dev, 0x0010, 0x012d);
+ cit_write_reg(gspca_dev, 0x006d, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0x0001, 0x0127);
+ cit_write_reg(gspca_dev, 0xfea8, 0x0124);
+ /* TESTME HDG: this does not seem right
+ (it is 2 for all other resolutions) */
+ sd->sof_len = 10;
+ break;
+ case 320: /* 320x240 */
+ cit_write_reg(gspca_dev, 0x0070, 0x0119);
+ cit_write_reg(gspca_dev, 0x00d0, 0x0111);
+ cit_write_reg(gspca_dev, 0x0039, 0x010a);
+ cit_write_reg(gspca_dev, 0x0001, 0x0102);
+ cit_write_reg(gspca_dev, 0x0028, 0x0103);
+ cit_write_reg(gspca_dev, 0x0000, 0x0104);
+ cit_write_reg(gspca_dev, 0x001e, 0x0105);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x0016, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x000a, 0x0127);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+ cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+ cit_write_reg(gspca_dev, 0x0014, 0x012d);
+ cit_write_reg(gspca_dev, 0x0008, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012e);
+ cit_write_reg(gspca_dev, 0x001a, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
+ cit_write_reg(gspca_dev, 0x005a, 0x012d);
+ cit_write_reg(gspca_dev, 0x9545, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0127);
+ cit_write_reg(gspca_dev, 0x0018, 0x012e);
+ cit_write_reg(gspca_dev, 0x0043, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012f);
+ cit_write_reg(gspca_dev, 0xd055, 0x0124);
+ cit_write_reg(gspca_dev, 0x001c, 0x0127);
+ cit_write_reg(gspca_dev, 0x00eb, 0x012e);
+ cit_write_reg(gspca_dev, 0xaa28, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x0032, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0000, 0x0127);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+ cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+ cit_write_reg(gspca_dev, 0x0036, 0x012d);
+ cit_write_reg(gspca_dev, 0x0008, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x001e, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0017, 0x0127);
+ cit_write_reg(gspca_dev, 0x0013, 0x012e);
+ cit_write_reg(gspca_dev, 0x0031, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+ cit_write_reg(gspca_dev, 0x0017, 0x012d);
+ cit_write_reg(gspca_dev, 0x0078, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0x0000, 0x0127);
+ cit_write_reg(gspca_dev, 0xfea8, 0x0124);
+ sd->sof_len = 2;
+ break;
+ case 352: /* 352x288 */
+ cit_write_reg(gspca_dev, 0x0070, 0x0119);
+ cit_write_reg(gspca_dev, 0x00c0, 0x0111);
+ cit_write_reg(gspca_dev, 0x0039, 0x010a);
+ cit_write_reg(gspca_dev, 0x0001, 0x0102);
+ cit_write_reg(gspca_dev, 0x002c, 0x0103);
+ cit_write_reg(gspca_dev, 0x0000, 0x0104);
+ cit_write_reg(gspca_dev, 0x0024, 0x0105);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x0016, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0006, 0x0127);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+ cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+ cit_write_reg(gspca_dev, 0x0014, 0x012d);
+ cit_write_reg(gspca_dev, 0x0002, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012e);
+ cit_write_reg(gspca_dev, 0x001a, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
+ cit_write_reg(gspca_dev, 0x005e, 0x012d);
+ cit_write_reg(gspca_dev, 0x9545, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0127);
+ cit_write_reg(gspca_dev, 0x0018, 0x012e);
+ cit_write_reg(gspca_dev, 0x0049, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012f);
+ cit_write_reg(gspca_dev, 0xd055, 0x0124);
+ cit_write_reg(gspca_dev, 0x001c, 0x0127);
+ cit_write_reg(gspca_dev, 0x00cf, 0x012e);
+ cit_write_reg(gspca_dev, 0xaa28, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x0032, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0000, 0x0127);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+ cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+ cit_write_reg(gspca_dev, 0x0036, 0x012d);
+ cit_write_reg(gspca_dev, 0x0008, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x001e, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0010, 0x0127);
+ cit_write_reg(gspca_dev, 0x0013, 0x012e);
+ cit_write_reg(gspca_dev, 0x0025, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+ cit_write_reg(gspca_dev, 0x0010, 0x012d);
+ cit_write_reg(gspca_dev, 0x0048, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0x0000, 0x0127);
+ cit_write_reg(gspca_dev, 0xfea8, 0x0124);
+ sd->sof_len = 2;
+ break;
+ }
+
+ cit_model4_Packet1(gspca_dev, 0x0038, 0x0004);
+
+ return 0;
+}
+
+static int cit_start_ibm_netcam_pro(struct gspca_dev *gspca_dev)
+{
+ const unsigned short compression = 0; /* 0=none, 7=best frame rate */
+ int i, clock_div;
+
+ clock_div = cit_get_clock_div(gspca_dev);
+ if (clock_div < 0)
+ return clock_div;
+
+ cit_write_reg(gspca_dev, 0x0003, 0x0133);
+ cit_write_reg(gspca_dev, 0x0000, 0x0117);
+ cit_write_reg(gspca_dev, 0x0008, 0x0123);
+ cit_write_reg(gspca_dev, 0x0000, 0x0100);
+ cit_write_reg(gspca_dev, 0x0060, 0x0116);
+ /* cit_write_reg(gspca_dev, 0x0002, 0x0112); see sd_stop0 */
+ cit_write_reg(gspca_dev, 0x0000, 0x0133);
+ cit_write_reg(gspca_dev, 0x0000, 0x0123);
+ cit_write_reg(gspca_dev, 0x0001, 0x0117);
+ cit_write_reg(gspca_dev, 0x0040, 0x0108);
+ cit_write_reg(gspca_dev, 0x0019, 0x012c);
+ cit_write_reg(gspca_dev, 0x0060, 0x0116);
+ /* cit_write_reg(gspca_dev, 0x000b, 0x0115); see sd_stop0 */
+
+ cit_model3_Packet1(gspca_dev, 0x0049, 0x0000);
+
+ cit_write_reg(gspca_dev, 0x0000, 0x0101); /* Same on 160x120, 320x240 */
+ cit_write_reg(gspca_dev, 0x003a, 0x0102); /* Hstart */
+ cit_write_reg(gspca_dev, 0x00a0, 0x0103); /* Same on 160x120, 320x240 */
+ cit_write_reg(gspca_dev, 0x0078, 0x0105); /* Same on 160x120, 320x240 */
+ cit_write_reg(gspca_dev, 0x0000, 0x010a); /* Same */
+ cit_write_reg(gspca_dev, 0x0002, 0x011d); /* Same on 160x120, 320x240 */
+ cit_write_reg(gspca_dev, 0x0000, 0x0129); /* Same */
+ cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */
+ cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */
+
+ switch (gspca_dev->width) {
+ case 160: /* 160x120 */
+ cit_write_reg(gspca_dev, 0x0024, 0x010b);
+ cit_write_reg(gspca_dev, 0x0089, 0x0119);
+ cit_write_reg(gspca_dev, 0x000a, 0x011b);
+ cit_write_reg(gspca_dev, 0x0003, 0x011e);
+ cit_write_reg(gspca_dev, 0x0007, 0x0104);
+ cit_write_reg(gspca_dev, 0x0009, 0x011a);
+ cit_write_reg(gspca_dev, 0x008b, 0x011c);
+ cit_write_reg(gspca_dev, 0x0008, 0x0118);
+ cit_write_reg(gspca_dev, 0x0000, 0x0132);
+ break;
+ case 320: /* 320x240 */
+ cit_write_reg(gspca_dev, 0x0028, 0x010b);
+ cit_write_reg(gspca_dev, 0x00d9, 0x0119);
+ cit_write_reg(gspca_dev, 0x0006, 0x011b);
+ cit_write_reg(gspca_dev, 0x0000, 0x011e);
+ cit_write_reg(gspca_dev, 0x000e, 0x0104);
+ cit_write_reg(gspca_dev, 0x0004, 0x011a);
+ cit_write_reg(gspca_dev, 0x003f, 0x011c);
+ cit_write_reg(gspca_dev, 0x000c, 0x0118);
+ cit_write_reg(gspca_dev, 0x0000, 0x0132);
+ break;
+ }
+
+ cit_model3_Packet1(gspca_dev, 0x0019, 0x0031);
+ cit_model3_Packet1(gspca_dev, 0x001a, 0x0003);
+ cit_model3_Packet1(gspca_dev, 0x001b, 0x0038);
+ cit_model3_Packet1(gspca_dev, 0x001c, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0024, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x0027, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x002a, 0x0004);
+ cit_model3_Packet1(gspca_dev, 0x0035, 0x000b);
+ cit_model3_Packet1(gspca_dev, 0x003f, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x0044, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0054, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00c4, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00e7, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x00e9, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x00ee, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00f3, 0x00c0);
+
+ cit_write_reg(gspca_dev, compression, 0x0109);
+ cit_write_reg(gspca_dev, clock_div, 0x0111);
+
+/* if (sd->input_index) { */
+ if (rca_input) {
+ for (i = 0; i < ARRAY_SIZE(rca_initdata); i++) {
+ if (rca_initdata[i][0])
+ cit_read_reg(gspca_dev, rca_initdata[i][2], 0);
+ else
+ cit_write_reg(gspca_dev, rca_initdata[i][1],
+ rca_initdata[i][2]);
+ }
+ }
+
+ return 0;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int packet_size;
+
+ packet_size = cit_get_packet_size(gspca_dev);
+ if (packet_size < 0)
+ return packet_size;
+
+ switch (sd->model) {
+ case CIT_MODEL0:
+ cit_start_model0(gspca_dev);
+ break;
+ case CIT_MODEL1:
+ cit_start_model1(gspca_dev);
+ break;
+ case CIT_MODEL2:
+ cit_start_model2(gspca_dev);
+ break;
+ case CIT_MODEL3:
+ cit_start_model3(gspca_dev);
+ break;
+ case CIT_MODEL4:
+ cit_start_model4(gspca_dev);
+ break;
+ case CIT_IBM_NETCAM_PRO:
+ cit_start_ibm_netcam_pro(gspca_dev);
+ break;
+ }
+
+ cit_set_brightness(gspca_dev);
+ cit_set_contrast(gspca_dev);
+ cit_set_hue(gspca_dev);
+ cit_set_sharpness(gspca_dev);
+ cit_set_lighting(gspca_dev);
+ cit_set_hflip(gspca_dev);
+
+ /* Program max isoc packet size */
+ cit_write_reg(gspca_dev, packet_size >> 8, 0x0106);
+ cit_write_reg(gspca_dev, packet_size & 0xff, 0x0107);
+
+ cit_restart_stream(gspca_dev);
+
+ return 0;
+}
+
+static int sd_isoc_init(struct gspca_dev *gspca_dev)
+{
+ struct usb_host_interface *alt;
+ int max_packet_size;
+
+ switch (gspca_dev->width) {
+ case 160:
+ max_packet_size = 450;
+ break;
+ case 176:
+ max_packet_size = 600;
+ break;
+ default:
+ max_packet_size = 1022;
+ break;
+ }
+
+ /* Start isoc bandwidth "negotiation" at max isoc bandwidth */
+ alt = &gspca_dev->dev->actconfig->intf_cache[0]->altsetting[1];
+ alt->endpoint[0].desc.wMaxPacketSize = cpu_to_le16(max_packet_size);
+
+ return 0;
+}
+
+static int sd_isoc_nego(struct gspca_dev *gspca_dev)
+{
+ int ret, packet_size, min_packet_size;
+ struct usb_host_interface *alt;
+
+ switch (gspca_dev->width) {
+ case 160:
+ min_packet_size = 200;
+ break;
+ case 176:
+ min_packet_size = 266;
+ break;
+ default:
+ min_packet_size = 400;
+ break;
+ }
+
+ alt = &gspca_dev->dev->actconfig->intf_cache[0]->altsetting[1];
+ packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+ if (packet_size <= min_packet_size)
+ return -EIO;
+
+ packet_size -= 100;
+ if (packet_size < min_packet_size)
+ packet_size = min_packet_size;
+ alt->endpoint[0].desc.wMaxPacketSize = cpu_to_le16(packet_size);
+
+ ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1);
+ if (ret < 0)
+ pr_err("set alt 1 err %d\n", ret);
+
+ return ret;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ cit_write_reg(gspca_dev, 0x0000, 0x010c);
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* We cannot use gspca_dev->present here as that is not set when
+ sd_init gets called and we get called from sd_init */
+ if (!gspca_dev->dev)
+ return;
+
+ switch (sd->model) {
+ case CIT_MODEL0:
+ /* HDG windows does this, but it causes the cams autogain to
+ restart from a gain of 0, which does not look good when
+ changing resolutions. */
+ /* cit_write_reg(gspca_dev, 0x0000, 0x0112); */
+ cit_write_reg(gspca_dev, 0x00c0, 0x0100); /* LED Off */
+ break;
+ case CIT_MODEL1:
+ cit_send_FF_04_02(gspca_dev);
+ cit_read_reg(gspca_dev, 0x0100, 0);
+ cit_write_reg(gspca_dev, 0x81, 0x0100); /* LED Off */
+ break;
+ case CIT_MODEL2:
+ case CIT_MODEL4:
+ cit_model2_Packet1(gspca_dev, 0x0030, 0x0004);
+
+ cit_write_reg(gspca_dev, 0x0080, 0x0100); /* LED Off */
+ cit_write_reg(gspca_dev, 0x0020, 0x0111);
+ cit_write_reg(gspca_dev, 0x00a0, 0x0111);
+
+ cit_model2_Packet1(gspca_dev, 0x0030, 0x0002);
+
+ cit_write_reg(gspca_dev, 0x0020, 0x0111);
+ cit_write_reg(gspca_dev, 0x0000, 0x0112);
+ break;
+ case CIT_MODEL3:
+ cit_write_reg(gspca_dev, 0x0006, 0x012c);
+ cit_model3_Packet1(gspca_dev, 0x0046, 0x0000);
+ cit_read_reg(gspca_dev, 0x0116, 0);
+ cit_write_reg(gspca_dev, 0x0064, 0x0116);
+ cit_read_reg(gspca_dev, 0x0115, 0);
+ cit_write_reg(gspca_dev, 0x0003, 0x0115);
+ cit_write_reg(gspca_dev, 0x0008, 0x0123);
+ cit_write_reg(gspca_dev, 0x0000, 0x0117);
+ cit_write_reg(gspca_dev, 0x0000, 0x0112);
+ cit_write_reg(gspca_dev, 0x0080, 0x0100);
+ break;
+ case CIT_IBM_NETCAM_PRO:
+ cit_model3_Packet1(gspca_dev, 0x0049, 0x00ff);
+ cit_write_reg(gspca_dev, 0x0006, 0x012c);
+ cit_write_reg(gspca_dev, 0x0000, 0x0116);
+ /* HDG windows does this, but I cannot get the camera
+ to restart with this without redoing the entire init
+ sequence which makes switching modes really slow */
+ /* cit_write_reg(gspca_dev, 0x0006, 0x0115); */
+ cit_write_reg(gspca_dev, 0x0008, 0x0123);
+ cit_write_reg(gspca_dev, 0x0000, 0x0117);
+ cit_write_reg(gspca_dev, 0x0003, 0x0133);
+ cit_write_reg(gspca_dev, 0x0000, 0x0111);
+ /* HDG windows does this, but I get a green picture when
+ restarting the stream after this */
+ /* cit_write_reg(gspca_dev, 0x0000, 0x0112); */
+ cit_write_reg(gspca_dev, 0x00c0, 0x0100);
+ break;
+ }
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ /* If the last button state is pressed, release it now! */
+ if (sd->button_state) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ sd->button_state = 0;
+ }
+#endif
+}
+
+static u8 *cit_find_sof(struct gspca_dev *gspca_dev, u8 *data, int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 byte3 = 0, byte4 = 0;
+ int i;
+
+ switch (sd->model) {
+ case CIT_MODEL0:
+ case CIT_MODEL1:
+ case CIT_MODEL3:
+ case CIT_IBM_NETCAM_PRO:
+ switch (gspca_dev->width) {
+ case 160: /* 160x120 */
+ byte3 = 0x02;
+ byte4 = 0x0a;
+ break;
+ case 176: /* 176x144 */
+ byte3 = 0x02;
+ byte4 = 0x0e;
+ break;
+ case 320: /* 320x240 */
+ byte3 = 0x02;
+ byte4 = 0x08;
+ break;
+ case 352: /* 352x288 */
+ byte3 = 0x02;
+ byte4 = 0x00;
+ break;
+ case 640:
+ byte3 = 0x03;
+ byte4 = 0x08;
+ break;
+ }
+
+ /* These have a different byte3 */
+ if (sd->model <= CIT_MODEL1)
+ byte3 = 0x00;
+
+ for (i = 0; i < len; i++) {
+ /* For this model the SOF always starts at offset 0
+ so no need to search the entire frame */
+ if (sd->model == CIT_MODEL0 && sd->sof_read != i)
+ break;
+
+ switch (sd->sof_read) {
+ case 0:
+ if (data[i] == 0x00)
+ sd->sof_read++;
+ break;
+ case 1:
+ if (data[i] == 0xff)
+ sd->sof_read++;
+ else if (data[i] == 0x00)
+ sd->sof_read = 1;
+ else
+ sd->sof_read = 0;
+ break;
+ case 2:
+ if (data[i] == byte3)
+ sd->sof_read++;
+ else if (data[i] == 0x00)
+ sd->sof_read = 1;
+ else
+ sd->sof_read = 0;
+ break;
+ case 3:
+ if (data[i] == byte4) {
+ sd->sof_read = 0;
+ return data + i + (sd->sof_len - 3);
+ }
+ if (byte3 == 0x00 && data[i] == 0xff)
+ sd->sof_read = 2;
+ else if (data[i] == 0x00)
+ sd->sof_read = 1;
+ else
+ sd->sof_read = 0;
+ break;
+ }
+ }
+ break;
+ case CIT_MODEL2:
+ case CIT_MODEL4:
+ /* TESTME we need to find a longer sof signature to avoid
+ false positives */
+ for (i = 0; i < len; i++) {
+ switch (sd->sof_read) {
+ case 0:
+ if (data[i] == 0x00)
+ sd->sof_read++;
+ break;
+ case 1:
+ sd->sof_read = 0;
+ if (data[i] == 0xff) {
+ if (i >= 4)
+ PDEBUG(D_FRAM,
+ "header found at offset: %d: %02x %02x 00 %02x %02x %02x\n",
+ i - 1,
+ data[i - 4],
+ data[i - 3],
+ data[i],
+ data[i + 1],
+ data[i + 2]);
+ else
+ PDEBUG(D_FRAM,
+ "header found at offset: %d: 00 %02x %02x %02x\n",
+ i - 1,
+ data[i],
+ data[i + 1],
+ data[i + 2]);
+ return data + i + (sd->sof_len - 1);
+ }
+ break;
+ }
+ }
+ break;
+ }
+ return NULL;
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ unsigned char *sof;
+
+ sof = cit_find_sof(gspca_dev, data, len);
+ if (sof) {
+ int n;
+
+ /* finish decoding current frame */
+ n = sof - data;
+ if (n > sd->sof_len)
+ n -= sd->sof_len;
+ else
+ n = 0;
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ data, n);
+ gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+ len -= sof - data;
+ data = sof;
+ }
+
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->brightness = val;
+ if (gspca_dev->streaming) {
+ if (sd->stop_on_control_change)
+ sd_stopN(gspca_dev);
+ cit_set_brightness(gspca_dev);
+ if (sd->stop_on_control_change)
+ cit_restart_stream(gspca_dev);
+ }
+
+ return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->brightness;
+
+ return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->contrast = val;
+ if (gspca_dev->streaming) {
+ if (sd->stop_on_control_change)
+ sd_stopN(gspca_dev);
+ cit_set_contrast(gspca_dev);
+ if (sd->stop_on_control_change)
+ cit_restart_stream(gspca_dev);
+ }
+
+ return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->contrast;
+
+ return 0;
+}
+
+static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->hue = val;
+ if (gspca_dev->streaming) {
+ if (sd->stop_on_control_change)
+ sd_stopN(gspca_dev);
+ cit_set_hue(gspca_dev);
+ if (sd->stop_on_control_change)
+ cit_restart_stream(gspca_dev);
+ }
+ return 0;
+}
+
+static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->hue;
+
+ return 0;
+}
+
+static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->sharpness = val;
+ if (gspca_dev->streaming) {
+ if (sd->stop_on_control_change)
+ sd_stopN(gspca_dev);
+ cit_set_sharpness(gspca_dev);
+ if (sd->stop_on_control_change)
+ cit_restart_stream(gspca_dev);
+ }
+ return 0;
+}
+
+static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->sharpness;
+
+ return 0;
+}
+
+static int sd_setlighting(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->lighting = val;
+ if (gspca_dev->streaming) {
+ if (sd->stop_on_control_change)
+ sd_stopN(gspca_dev);
+ cit_set_lighting(gspca_dev);
+ if (sd->stop_on_control_change)
+ cit_restart_stream(gspca_dev);
+ }
+ return 0;
+}
+
+static int sd_getlighting(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->lighting;
+
+ return 0;
+}
+
+static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->hflip = val;
+ if (gspca_dev->streaming) {
+ if (sd->stop_on_control_change)
+ sd_stopN(gspca_dev);
+ cit_set_hflip(gspca_dev);
+ if (sd->stop_on_control_change)
+ cit_restart_stream(gspca_dev);
+ }
+ return 0;
+}
+
+static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->hflip;
+
+ return 0;
+}
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+static void cit_check_button(struct gspca_dev *gspca_dev)
+{
+ int new_button_state;
+ struct sd *sd = (struct sd *)gspca_dev;
+
+ switch (sd->model) {
+ case CIT_MODEL3:
+ case CIT_IBM_NETCAM_PRO:
+ break;
+ default: /* TEST ME unknown if this works on other models too */
+ return;
+ }
+
+ /* Read the button state */
+ cit_read_reg(gspca_dev, 0x0113, 0);
+ new_button_state = !gspca_dev->usb_buf[0];
+
+ /* Tell the cam we've seen the button press, notice that this
+ is a nop (iow the cam keeps reporting pressed) until the
+ button is actually released. */
+ if (new_button_state)
+ cit_write_reg(gspca_dev, 0x01, 0x0113);
+
+ if (sd->button_state != new_button_state) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA,
+ new_button_state);
+ input_sync(gspca_dev->input_dev);
+ sd->button_state = new_button_state;
+ }
+}
+#endif
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ .dq_callback = cit_check_button,
+ .other_input = 1,
+#endif
+};
+
+static const struct sd_desc sd_desc_isoc_nego = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .isoc_init = sd_isoc_init,
+ .isoc_nego = sd_isoc_nego,
+ .stopN = sd_stopN,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ .dq_callback = cit_check_button,
+ .other_input = 1,
+#endif
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ { USB_DEVICE_VER(0x0545, 0x8080, 0x0001, 0x0001), .driver_info = CIT_MODEL0 },
+ { USB_DEVICE_VER(0x0545, 0x8080, 0x0002, 0x0002), .driver_info = CIT_MODEL1 },
+ { USB_DEVICE_VER(0x0545, 0x8080, 0x030a, 0x030a), .driver_info = CIT_MODEL2 },
+ { USB_DEVICE_VER(0x0545, 0x8080, 0x0301, 0x0301), .driver_info = CIT_MODEL3 },
+ { USB_DEVICE_VER(0x0545, 0x8002, 0x030a, 0x030a), .driver_info = CIT_MODEL4 },
+ { USB_DEVICE_VER(0x0545, 0x800c, 0x030a, 0x030a), .driver_info = CIT_MODEL2 },
+ { USB_DEVICE_VER(0x0545, 0x800d, 0x030a, 0x030a), .driver_info = CIT_MODEL4 },
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ const struct sd_desc *desc = &sd_desc;
+
+ switch (id->driver_info) {
+ case CIT_MODEL0:
+ case CIT_MODEL1:
+ if (intf->cur_altsetting->desc.bInterfaceNumber != 2)
+ return -ENODEV;
+ break;
+ case CIT_MODEL2:
+ case CIT_MODEL4:
+ if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+ return -ENODEV;
+ break;
+ case CIT_MODEL3:
+ if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+ return -ENODEV;
+ /* FIXME this likely applies to all model3 cams and probably
+ to other models too. */
+ if (ibm_netcam_pro)
+ desc = &sd_desc_isoc_nego;
+ break;
+ }
+
+ return gspca_dev_probe2(intf, id, desc, sizeof(struct sd), THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/zc3xx-reg.h b/drivers/media/video/gspca/zc3xx-reg.h
new file mode 100644
index 00000000..a1bd94e8
--- /dev/null
+++ b/drivers/media/video/gspca/zc3xx-reg.h
@@ -0,0 +1,251 @@
+/*
+ * zc030x registers
+ *
+ * Copyright (c) 2008 Mauro Carvalho Chehab <mchehab@infradead.org>
+ *
+ * The register aliases used here came from this driver:
+ * http://zc0302.sourceforge.net/zc0302.php
+ *
+ * This code is placed under the terms of the GNU General Public License v2
+ */
+
+/* Define the register map */
+#define ZC3XX_R000_SYSTEMCONTROL 0x0000
+#define ZC3XX_R001_SYSTEMOPERATING 0x0001
+
+/* Picture size */
+#define ZC3XX_R002_CLOCKSELECT 0x0002
+#define ZC3XX_R003_FRAMEWIDTHHIGH 0x0003
+#define ZC3XX_R004_FRAMEWIDTHLOW 0x0004
+#define ZC3XX_R005_FRAMEHEIGHTHIGH 0x0005
+#define ZC3XX_R006_FRAMEHEIGHTLOW 0x0006
+
+/* JPEG control */
+#define ZC3XX_R008_CLOCKSETTING 0x0008
+
+/* Test mode */
+#define ZC3XX_R00B_TESTMODECONTROL 0x000b
+
+/* Frame retreiving */
+#define ZC3XX_R00C_LASTACQTIME 0x000c
+#define ZC3XX_R00D_MONITORRES 0x000d
+#define ZC3XX_R00E_TIMESTAMPHIGH 0x000e
+#define ZC3XX_R00F_TIMESTAMPLOW 0x000f
+#define ZC3XX_R018_FRAMELOST 0x0018
+#define ZC3XX_R019_AUTOADJUSTFPS 0x0019
+#define ZC3XX_R01A_LASTFRAMESTATE 0x001a
+#define ZC3XX_R025_DATACOUNTER 0x0025
+
+/* Stream and sensor specific */
+#define ZC3XX_R010_CMOSSENSORSELECT 0x0010
+#define ZC3XX_R011_VIDEOSTATUS 0x0011
+#define ZC3XX_R012_VIDEOCONTROLFUNC 0x0012
+
+/* Horizontal and vertical synchros */
+#define ZC3XX_R01D_HSYNC_0 0x001d
+#define ZC3XX_R01E_HSYNC_1 0x001e
+#define ZC3XX_R01F_HSYNC_2 0x001f
+#define ZC3XX_R020_HSYNC_3 0x0020
+
+/* Target picture size in byte */
+#define ZC3XX_R022_TARGETPICTSIZE_0 0x0022
+#define ZC3XX_R023_TARGETPICTSIZE_1 0x0023
+#define ZC3XX_R024_TARGETPICTSIZE_2 0x0024
+
+/* Audio registers */
+#define ZC3XX_R030_AUDIOADC 0x0030
+#define ZC3XX_R031_AUDIOSTREAMSTATUS 0x0031
+#define ZC3XX_R032_AUDIOSTATUS 0x0032
+
+/* Sensor interface */
+#define ZC3XX_R080_HBLANKHIGH 0x0080
+#define ZC3XX_R081_HBLANKLOW 0x0081
+#define ZC3XX_R082_RESETLEVELADDR 0x0082
+#define ZC3XX_R083_RGAINADDR 0x0083
+#define ZC3XX_R084_GGAINADDR 0x0084
+#define ZC3XX_R085_BGAINADDR 0x0085
+#define ZC3XX_R086_EXPTIMEHIGH 0x0086
+#define ZC3XX_R087_EXPTIMEMID 0x0087
+#define ZC3XX_R088_EXPTIMELOW 0x0088
+#define ZC3XX_R089_RESETBLACKHIGH 0x0089
+#define ZC3XX_R08A_RESETWHITEHIGH 0x008a
+#define ZC3XX_R08B_I2CDEVICEADDR 0x008b
+#define ZC3XX_R08C_I2CIDLEANDNACK 0x008c
+#define ZC3XX_R08D_COMPABILITYMODE 0x008d
+#define ZC3XX_R08E_COMPABILITYMODE2 0x008e
+
+/* I2C control */
+#define ZC3XX_R090_I2CCOMMAND 0x0090
+#define ZC3XX_R091_I2CSTATUS 0x0091
+#define ZC3XX_R092_I2CADDRESSSELECT 0x0092
+#define ZC3XX_R093_I2CSETVALUE 0x0093
+#define ZC3XX_R094_I2CWRITEACK 0x0094
+#define ZC3XX_R095_I2CREAD 0x0095
+#define ZC3XX_R096_I2CREADACK 0x0096
+
+/* Window inside the sensor array */
+#define ZC3XX_R097_WINYSTARTHIGH 0x0097
+#define ZC3XX_R098_WINYSTARTLOW 0x0098
+#define ZC3XX_R099_WINXSTARTHIGH 0x0099
+#define ZC3XX_R09A_WINXSTARTLOW 0x009a
+#define ZC3XX_R09B_WINHEIGHTHIGH 0x009b
+#define ZC3XX_R09C_WINHEIGHTLOW 0x009c
+#define ZC3XX_R09D_WINWIDTHHIGH 0x009d
+#define ZC3XX_R09E_WINWIDTHLOW 0x009e
+#define ZC3XX_R119_FIRSTYHIGH 0x0119
+#define ZC3XX_R11A_FIRSTYLOW 0x011a
+#define ZC3XX_R11B_FIRSTXHIGH 0x011b
+#define ZC3XX_R11C_FIRSTXLOW 0x011c
+
+/* Max sensor array size */
+#define ZC3XX_R09F_MAXXHIGH 0x009f
+#define ZC3XX_R0A0_MAXXLOW 0x00a0
+#define ZC3XX_R0A1_MAXYHIGH 0x00a1
+#define ZC3XX_R0A2_MAXYLOW 0x00a2
+#define ZC3XX_R0A3_EXPOSURETIMEHIGH 0x00a3
+#define ZC3XX_R0A4_EXPOSURETIMELOW 0x00a4
+#define ZC3XX_R0A5_EXPOSUREGAIN 0x00a5
+#define ZC3XX_R0A6_EXPOSUREBLACKLVL 0x00a6
+
+/* Other registers */
+#define ZC3XX_R100_OPERATIONMODE 0x0100
+#define ZC3XX_R101_SENSORCORRECTION 0x0101
+
+/* Gains */
+#define ZC3XX_R116_RGAIN 0x0116
+#define ZC3XX_R117_GGAIN 0x0117
+#define ZC3XX_R118_BGAIN 0x0118
+#define ZC3XX_R11D_GLOBALGAIN 0x011d
+#define ZC3XX_R1A8_DIGITALGAIN 0x01a8
+#define ZC3XX_R1A9_DIGITALLIMITDIFF 0x01a9
+#define ZC3XX_R1AA_DIGITALGAINSTEP 0x01aa
+
+/* Auto correction */
+#define ZC3XX_R180_AUTOCORRECTENABLE 0x0180
+#define ZC3XX_R181_WINXSTART 0x0181
+#define ZC3XX_R182_WINXWIDTH 0x0182
+#define ZC3XX_R183_WINXCENTER 0x0183
+#define ZC3XX_R184_WINYSTART 0x0184
+#define ZC3XX_R185_WINYWIDTH 0x0185
+#define ZC3XX_R186_WINYCENTER 0x0186
+
+/* Gain range */
+#define ZC3XX_R187_MAXGAIN 0x0187
+#define ZC3XX_R188_MINGAIN 0x0188
+
+/* Auto exposure and white balance */
+#define ZC3XX_R189_AWBSTATUS 0x0189
+#define ZC3XX_R18A_AWBFREEZE 0x018a
+#define ZC3XX_R18B_AESTATUS 0x018b
+#define ZC3XX_R18C_AEFREEZE 0x018c
+#define ZC3XX_R18F_AEUNFREEZE 0x018f
+#define ZC3XX_R190_EXPOSURELIMITHIGH 0x0190
+#define ZC3XX_R191_EXPOSURELIMITMID 0x0191
+#define ZC3XX_R192_EXPOSURELIMITLOW 0x0192
+#define ZC3XX_R195_ANTIFLICKERHIGH 0x0195
+#define ZC3XX_R196_ANTIFLICKERMID 0x0196
+#define ZC3XX_R197_ANTIFLICKERLOW 0x0197
+
+/* What is this ? */
+#define ZC3XX_R18D_YTARGET 0x018d
+#define ZC3XX_R18E_RESETLVL 0x018e
+
+/* Color */
+#define ZC3XX_R1A0_REDMEANAFTERAGC 0x01a0
+#define ZC3XX_R1A1_GREENMEANAFTERAGC 0x01a1
+#define ZC3XX_R1A2_BLUEMEANAFTERAGC 0x01a2
+#define ZC3XX_R1A3_REDMEANAFTERAWB 0x01a3
+#define ZC3XX_R1A4_GREENMEANAFTERAWB 0x01a4
+#define ZC3XX_R1A5_BLUEMEANAFTERAWB 0x01a5
+#define ZC3XX_R1A6_YMEANAFTERAE 0x01a6
+#define ZC3XX_R1A7_CALCGLOBALMEAN 0x01a7
+
+/* Matrixes */
+
+/* Color matrix is like :
+ R' = R * RGB00 + G * RGB01 + B * RGB02 + RGB03
+ G' = R * RGB10 + G * RGB11 + B * RGB22 + RGB13
+ B' = R * RGB20 + G * RGB21 + B * RGB12 + RGB23
+ */
+#define ZC3XX_R10A_RGB00 0x010a
+#define ZC3XX_R10B_RGB01 0x010b
+#define ZC3XX_R10C_RGB02 0x010c
+#define ZC3XX_R113_RGB03 0x0113
+#define ZC3XX_R10D_RGB10 0x010d
+#define ZC3XX_R10E_RGB11 0x010e
+#define ZC3XX_R10F_RGB12 0x010f
+#define ZC3XX_R114_RGB13 0x0114
+#define ZC3XX_R110_RGB20 0x0110
+#define ZC3XX_R111_RGB21 0x0111
+#define ZC3XX_R112_RGB22 0x0112
+#define ZC3XX_R115_RGB23 0x0115
+
+/* Gamma matrix */
+#define ZC3XX_R120_GAMMA00 0x0120
+#define ZC3XX_R121_GAMMA01 0x0121
+#define ZC3XX_R122_GAMMA02 0x0122
+#define ZC3XX_R123_GAMMA03 0x0123
+#define ZC3XX_R124_GAMMA04 0x0124
+#define ZC3XX_R125_GAMMA05 0x0125
+#define ZC3XX_R126_GAMMA06 0x0126
+#define ZC3XX_R127_GAMMA07 0x0127
+#define ZC3XX_R128_GAMMA08 0x0128
+#define ZC3XX_R129_GAMMA09 0x0129
+#define ZC3XX_R12A_GAMMA0A 0x012a
+#define ZC3XX_R12B_GAMMA0B 0x012b
+#define ZC3XX_R12C_GAMMA0C 0x012c
+#define ZC3XX_R12D_GAMMA0D 0x012d
+#define ZC3XX_R12E_GAMMA0E 0x012e
+#define ZC3XX_R12F_GAMMA0F 0x012f
+#define ZC3XX_R130_GAMMA10 0x0130
+#define ZC3XX_R131_GAMMA11 0x0131
+#define ZC3XX_R132_GAMMA12 0x0132
+#define ZC3XX_R133_GAMMA13 0x0133
+#define ZC3XX_R134_GAMMA14 0x0134
+#define ZC3XX_R135_GAMMA15 0x0135
+#define ZC3XX_R136_GAMMA16 0x0136
+#define ZC3XX_R137_GAMMA17 0x0137
+#define ZC3XX_R138_GAMMA18 0x0138
+#define ZC3XX_R139_GAMMA19 0x0139
+#define ZC3XX_R13A_GAMMA1A 0x013a
+#define ZC3XX_R13B_GAMMA1B 0x013b
+#define ZC3XX_R13C_GAMMA1C 0x013c
+#define ZC3XX_R13D_GAMMA1D 0x013d
+#define ZC3XX_R13E_GAMMA1E 0x013e
+#define ZC3XX_R13F_GAMMA1F 0x013f
+
+/* Luminance gamma */
+#define ZC3XX_R140_YGAMMA00 0x0140
+#define ZC3XX_R141_YGAMMA01 0x0141
+#define ZC3XX_R142_YGAMMA02 0x0142
+#define ZC3XX_R143_YGAMMA03 0x0143
+#define ZC3XX_R144_YGAMMA04 0x0144
+#define ZC3XX_R145_YGAMMA05 0x0145
+#define ZC3XX_R146_YGAMMA06 0x0146
+#define ZC3XX_R147_YGAMMA07 0x0147
+#define ZC3XX_R148_YGAMMA08 0x0148
+#define ZC3XX_R149_YGAMMA09 0x0149
+#define ZC3XX_R14A_YGAMMA0A 0x014a
+#define ZC3XX_R14B_YGAMMA0B 0x014b
+#define ZC3XX_R14C_YGAMMA0C 0x014c
+#define ZC3XX_R14D_YGAMMA0D 0x014d
+#define ZC3XX_R14E_YGAMMA0E 0x014e
+#define ZC3XX_R14F_YGAMMA0F 0x014f
+#define ZC3XX_R150_YGAMMA10 0x0150
+#define ZC3XX_R151_YGAMMA11 0x0151
+
+#define ZC3XX_R1C5_SHARPNESSMODE 0x01c5
+#define ZC3XX_R1C6_SHARPNESS00 0x01c6
+#define ZC3XX_R1C7_SHARPNESS01 0x01c7
+#define ZC3XX_R1C8_SHARPNESS02 0x01c8
+#define ZC3XX_R1C9_SHARPNESS03 0x01c9
+#define ZC3XX_R1CA_SHARPNESS04 0x01ca
+#define ZC3XX_R1CB_SHARPNESS05 0x01cb
+
+/* Dead pixels */
+#define ZC3XX_R250_DEADPIXELSMODE 0x0250
+
+/* EEPROM */
+#define ZC3XX_R300_EEPROMCONFIG 0x0300
+#define ZC3XX_R301_EEPROMACCESS 0x0301
+#define ZC3XX_R302_EEPROMSTATUS 0x0302
diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c
new file mode 100644
index 00000000..7d9a4f1b
--- /dev/null
+++ b/drivers/media/video/gspca/zc3xx.c
@@ -0,0 +1,7186 @@
+/*
+ * Z-Star/Vimicro zc301/zc302p/vc30x driver
+ *
+ * Copyright (C) 2009-2012 Jean-Francois Moine <http://moinejf.free.fr>
+ * Copyright (C) 2004 2005 2006 Michel Xhaard mxhaard@magic.fr
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/input.h>
+#include "gspca.h"
+#include "jpeg.h"
+
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>, "
+ "Serge A. Suchkov <Serge.A.S@tochka.ru>");
+MODULE_DESCRIPTION("GSPCA ZC03xx/VC3xx USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+static int force_sensor = -1;
+
+#define REG08_DEF 3 /* default JPEG compression (70%) */
+#include "zc3xx-reg.h"
+
+/* controls */
+enum e_ctrl {
+ BRIGHTNESS,
+ CONTRAST,
+ EXPOSURE,
+ GAMMA,
+ AUTOGAIN,
+ LIGHTFREQ,
+ SHARPNESS,
+ QUALITY,
+ NCTRLS /* number of controls */
+};
+
+#define AUTOGAIN_DEF 1
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ struct gspca_ctrl ctrls[NCTRLS];
+
+ struct work_struct work;
+ struct workqueue_struct *work_thread;
+
+ u8 reg08; /* webcam compression quality */
+
+ u8 bridge;
+ u8 sensor; /* Type of image sensor chip */
+ u16 chip_revision;
+
+ u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+enum bridges {
+ BRIDGE_ZC301,
+ BRIDGE_ZC303,
+};
+enum sensors {
+ SENSOR_ADCM2700,
+ SENSOR_CS2102,
+ SENSOR_CS2102K,
+ SENSOR_GC0303,
+ SENSOR_GC0305,
+ SENSOR_HDCS2020,
+ SENSOR_HV7131B,
+ SENSOR_HV7131R,
+ SENSOR_ICM105A,
+ SENSOR_MC501CB,
+ SENSOR_MT9V111_1, /* (mi360soc) zc301 */
+ SENSOR_MT9V111_3, /* (mi360soc) zc303 */
+ SENSOR_OV7620, /* OV7648 - same values */
+ SENSOR_OV7630C,
+ SENSOR_PAS106,
+ SENSOR_PAS202B,
+ SENSOR_PB0330,
+ SENSOR_PO2030,
+ SENSOR_TAS5130C,
+ SENSOR_MAX
+};
+
+/* V4L2 controls supported by the driver */
+static void setcontrast(struct gspca_dev *gspca_dev);
+static void setexposure(struct gspca_dev *gspca_dev);
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static void setlightfreq(struct gspca_dev *gspca_dev);
+static void setsharpness(struct gspca_dev *gspca_dev);
+static int sd_setquality(struct gspca_dev *gspca_dev, __s32 val);
+
+static const struct ctrl sd_ctrls[NCTRLS] = {
+[BRIGHTNESS] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 128,
+ },
+ .set_control = setcontrast
+ },
+[CONTRAST] = {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 128,
+ },
+ .set_control = setcontrast
+ },
+[EXPOSURE] = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 0x30d,
+ .maximum = 0x493e,
+ .step = 1,
+ .default_value = 0x927
+ },
+ .set_control = setexposure
+ },
+[GAMMA] = {
+ {
+ .id = V4L2_CID_GAMMA,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gamma",
+ .minimum = 1,
+ .maximum = 6,
+ .step = 1,
+ .default_value = 4,
+ },
+ .set_control = setcontrast
+ },
+[AUTOGAIN] = {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto Gain",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = AUTOGAIN_DEF,
+ .flags = V4L2_CTRL_FLAG_UPDATE
+ },
+ .set = sd_setautogain
+ },
+[LIGHTFREQ] = {
+ {
+ .id = V4L2_CID_POWER_LINE_FREQUENCY,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Light frequency filter",
+ .minimum = 0,
+ .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */
+ .step = 1,
+ .default_value = 0,
+ },
+ .set_control = setlightfreq
+ },
+[SHARPNESS] = {
+ {
+ .id = V4L2_CID_SHARPNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Sharpness",
+ .minimum = 0,
+ .maximum = 3,
+ .step = 1,
+ .default_value = 2,
+ },
+ .set_control = setsharpness
+ },
+[QUALITY] = {
+ {
+ .id = V4L2_CID_JPEG_COMPRESSION_QUALITY,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Compression Quality",
+ .minimum = 40,
+ .maximum = 70,
+ .step = 1,
+ .default_value = 70 /* updated in sd_init() */
+ },
+ .set = sd_setquality
+ },
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
+static const struct v4l2_pix_format broken_vga_mode[] = {
+ {320, 232, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 232 * 4 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {640, 472, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 472 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
+static const struct v4l2_pix_format sif_mode[] = {
+ {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
+/* bridge reg08 -> JPEG quality conversion table */
+static u8 jpeg_qual[] = {40, 50, 60, 70, /*80*/};
+
+/* usb exchanges */
+struct usb_action {
+ u8 req;
+ u8 val;
+ u16 idx;
+};
+
+static const struct usb_action adcm2700_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x04, ZC3XX_R002_CLOCKSELECT}, /* 00,02,04,cc */
+ {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+ {0xa0, 0xd8, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d8,cc */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
+ {0xa0, 0xde, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,de,cc */
+ {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */
+ {0xbb, 0x00, 0x0400}, /* 04,00,00,bb */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xbb, 0x0f, 0x140f}, /* 14,0f,0f,bb */
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x58, ZC3XX_R116_RGAIN}, /* 01,16,58,cc */
+ {0xa0, 0x5a, ZC3XX_R118_BGAIN}, /* 01,18,5a,cc */
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
+ {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */
+ {0xbb, 0x00, 0x0408}, /* 04,00,08,bb */
+ {0xdd, 0x00, 0x0200}, /* 00,02,00,dd */
+ {0xbb, 0x00, 0x0400}, /* 04,00,00,bb */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xbb, 0x0f, 0x140f}, /* 14,0f,0f,bb */
+ {0xbb, 0xe0, 0x0c2e}, /* 0c,e0,2e,bb */
+ {0xbb, 0x01, 0x2000}, /* 20,01,00,bb */
+ {0xbb, 0x96, 0x2400}, /* 24,96,00,bb */
+ {0xbb, 0x06, 0x1006}, /* 10,06,06,bb */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xbb, 0x5f, 0x2090}, /* 20,5f,90,bb */
+ {0xbb, 0x01, 0x8000}, /* 80,01,00,bb */
+ {0xbb, 0x09, 0x8400}, /* 84,09,00,bb */
+ {0xbb, 0x86, 0x0002}, /* 00,86,02,bb */
+ {0xbb, 0xe6, 0x0401}, /* 04,e6,01,bb */
+ {0xbb, 0x86, 0x0802}, /* 08,86,02,bb */
+ {0xbb, 0xe6, 0x0c01}, /* 0c,e6,01,bb */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xaa, 0xfe, 0x0000}, /* 00,fe,00,aa */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xaa, 0xfe, 0x0020}, /* 00,fe,20,aa */
+/*mswin+*/
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xaa, 0xfe, 0x0002},
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xaa, 0xb4, 0xcd37},
+ {0xaa, 0xa4, 0x0004},
+ {0xaa, 0xa8, 0x0007},
+ {0xaa, 0xac, 0x0004},
+/*mswin-*/
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xaa, 0xfe, 0x0000}, /* 00,fe,00,aa */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xbb, 0x04, 0x0400}, /* 04,04,00,bb */
+ {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */
+ {0xbb, 0x01, 0x0400}, /* 04,01,00,bb */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xbb, 0x41, 0x2803}, /* 28,41,03,bb */
+ {0xbb, 0x40, 0x2c03}, /* 2c,40,03,bb */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */
+ {}
+};
+static const struct usb_action adcm2700_InitialScale[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */
+ {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+ {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d0,cc */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
+ {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,d8,cc */
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */
+ {0xbb, 0x00, 0x0400}, /* 04,00,00,bb */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xbb, 0x0f, 0x140f}, /* 14,0f,0f,bb */
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x58, ZC3XX_R116_RGAIN}, /* 01,16,58,cc */
+ {0xa0, 0x5a, ZC3XX_R118_BGAIN}, /* 01,18,5a,cc */
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
+ {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */
+ {0xbb, 0x00, 0x0408}, /* 04,00,08,bb */
+ {0xdd, 0x00, 0x0200}, /* 00,02,00,dd */
+ {0xbb, 0x00, 0x0400}, /* 04,00,00,bb */
+ {0xdd, 0x00, 0x0050}, /* 00,00,50,dd */
+ {0xbb, 0x0f, 0x140f}, /* 14,0f,0f,bb */
+ {0xbb, 0xe0, 0x0c2e}, /* 0c,e0,2e,bb */
+ {0xbb, 0x01, 0x2000}, /* 20,01,00,bb */
+ {0xbb, 0x96, 0x2400}, /* 24,96,00,bb */
+ {0xbb, 0x06, 0x1006}, /* 10,06,06,bb */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xbb, 0x5f, 0x2090}, /* 20,5f,90,bb */
+ {0xbb, 0x01, 0x8000}, /* 80,01,00,bb */
+ {0xbb, 0x09, 0x8400}, /* 84,09,00,bb */
+ {0xbb, 0x86, 0x0002}, /* 00,88,02,bb */
+ {0xbb, 0xe6, 0x0401}, /* 04,e6,01,bb */
+ {0xbb, 0x86, 0x0802}, /* 08,88,02,bb */
+ {0xbb, 0xe6, 0x0c01}, /* 0c,e6,01,bb */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xaa, 0xfe, 0x0000}, /* 00,fe,00,aa */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xaa, 0xfe, 0x0020}, /* 00,fe,20,aa */
+ /*******/
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xaa, 0xfe, 0x0000}, /* 00,fe,00,aa */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xbb, 0x04, 0x0400}, /* 04,04,00,bb */
+ {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */
+ {0xbb, 0x01, 0x0400}, /* 04,01,00,bb */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xbb, 0x41, 0x2803}, /* 28,41,03,bb */
+ {0xbb, 0x40, 0x2c03}, /* 2c,40,03,bb */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */
+ {}
+};
+static const struct usb_action adcm2700_50HZ[] = {
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xbb, 0x05, 0x8400}, /* 84,05,00,bb */
+ {0xbb, 0xd0, 0xb007}, /* b0,d0,07,bb */
+ {0xbb, 0xa0, 0xb80f}, /* b8,a0,0f,bb */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */
+ {0xaa, 0x26, 0x00d0}, /* 00,26,d0,aa */
+ {0xaa, 0x28, 0x0002}, /* 00,28,02,aa */
+ {}
+};
+static const struct usb_action adcm2700_60HZ[] = {
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xbb, 0x07, 0x8400}, /* 84,07,00,bb */
+ {0xbb, 0x82, 0xb006}, /* b0,82,06,bb */
+ {0xbb, 0x04, 0xb80d}, /* b8,04,0d,bb */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */
+ {0xaa, 0x26, 0x0057}, /* 00,26,57,aa */
+ {0xaa, 0x28, 0x0002}, /* 00,28,02,aa */
+ {}
+};
+static const struct usb_action adcm2700_NoFliker[] = {
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xbb, 0x07, 0x8400}, /* 84,07,00,bb */
+ {0xbb, 0x05, 0xb000}, /* b0,05,00,bb */
+ {0xbb, 0xa0, 0xb801}, /* b8,a0,01,bb */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */
+ {}
+};
+static const struct usb_action cs2102_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x20, ZC3XX_R080_HBLANKHIGH},
+ {0xa0, 0x21, ZC3XX_R081_HBLANKLOW},
+ {0xa0, 0x30, ZC3XX_R083_RGAINADDR},
+ {0xa0, 0x31, ZC3XX_R084_GGAINADDR},
+ {0xa0, 0x32, ZC3XX_R085_BGAINADDR},
+ {0xa0, 0x23, ZC3XX_R086_EXPTIMEHIGH},
+ {0xa0, 0x24, ZC3XX_R087_EXPTIMEMID},
+ {0xa0, 0x25, ZC3XX_R088_EXPTIMELOW},
+ {0xa0, 0xb3, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xaa, 0x02, 0x0008},
+ {0xaa, 0x03, 0x0000},
+ {0xaa, 0x11, 0x0000},
+ {0xaa, 0x12, 0x0089},
+ {0xaa, 0x13, 0x0000},
+ {0xaa, 0x14, 0x00e9},
+ {0xaa, 0x20, 0x0000},
+ {0xaa, 0x22, 0x0000},
+ {0xaa, 0x0b, 0x0004},
+ {0xaa, 0x30, 0x0030},
+ {0xaa, 0x31, 0x0030},
+ {0xaa, 0x32, 0x0030},
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x10, 0x01ae},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x68, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x00, 0x01ad},
+ {}
+};
+
+static const struct usb_action cs2102_Initial[] = { /* 640x480 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x20, ZC3XX_R080_HBLANKHIGH},
+ {0xa0, 0x21, ZC3XX_R081_HBLANKLOW},
+ {0xa0, 0x30, ZC3XX_R083_RGAINADDR},
+ {0xa0, 0x31, ZC3XX_R084_GGAINADDR},
+ {0xa0, 0x32, ZC3XX_R085_BGAINADDR},
+ {0xa0, 0x23, ZC3XX_R086_EXPTIMEHIGH},
+ {0xa0, 0x24, ZC3XX_R087_EXPTIMEMID},
+ {0xa0, 0x25, ZC3XX_R088_EXPTIMELOW},
+ {0xa0, 0xb3, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xaa, 0x02, 0x0008},
+ {0xaa, 0x03, 0x0000},
+ {0xaa, 0x11, 0x0001},
+ {0xaa, 0x12, 0x0087},
+ {0xaa, 0x13, 0x0001},
+ {0xaa, 0x14, 0x00e7},
+ {0xaa, 0x20, 0x0000},
+ {0xaa, 0x22, 0x0000},
+ {0xaa, 0x0b, 0x0004},
+ {0xaa, 0x30, 0x0030},
+ {0xaa, 0x31, 0x0030},
+ {0xaa, 0x32, 0x0030},
+ {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x15, 0x01ae},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x68, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x00, 0x01ad},
+ {}
+};
+static const struct usb_action cs2102_50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x23, 0x0001},
+ {0xaa, 0x24, 0x005f},
+ {0xaa, 0x25, 0x0090},
+ {0xaa, 0x21, 0x00dd},
+ {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0xbf, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x3a, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x98, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xdd, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xe4, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action cs2102_50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x23, 0x0000},
+ {0xaa, 0x24, 0x00af},
+ {0xaa, 0x25, 0x00c8},
+ {0xaa, 0x21, 0x0068},
+ {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x5f, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x90, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x1d, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x4c, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x68, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xe3, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action cs2102_60HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x23, 0x0001},
+ {0xaa, 0x24, 0x0055},
+ {0xaa, 0x25, 0x00cc},
+ {0xaa, 0x21, 0x003f},
+ {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0xab, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x98, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x30, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0xd4, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x39, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x70, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xb0, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action cs2102_60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x23, 0x0000},
+ {0xaa, 0x24, 0x00aa},
+ {0xaa, 0x25, 0x00e6},
+ {0xaa, 0x21, 0x003f},
+ {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x55, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xcc, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x18, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x6a, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x3f, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xa5, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action cs2102_NoFlikerScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x23, 0x0001},
+ {0xaa, 0x24, 0x005f},
+ {0xaa, 0x25, 0x0000},
+ {0xaa, 0x21, 0x0001},
+ {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0xbf, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x80, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x01, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xa0, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action cs2102_NoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x23, 0x0000},
+ {0xaa, 0x24, 0x00af},
+ {0xaa, 0x25, 0x0080},
+ {0xaa, 0x21, 0x0001},
+ {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x5f, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x80, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x01, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xa0, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {}
+};
+
+/* CS2102_KOCOM */
+static const struct usb_action cs2102K_InitialScale[] = {
+ {0xa0, 0x11, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+ {0xa0, 0x55, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x0a, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x0b, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x0c, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x7c, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x0d, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0xa3, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0xfb, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x03, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x08, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x0e, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x0f, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x10, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x11, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x12, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x15, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x16, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x17, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x78, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID},
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x00, 0x01ad},
+ {0xa0, 0x01, 0x01b1},
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x60, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x4c, ZC3XX_R118_BGAIN},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
+ {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
+ {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
+ {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */
+ {0xa0, 0x38, ZC3XX_R121_GAMMA01},
+ {0xa0, 0x59, ZC3XX_R122_GAMMA02},
+ {0xa0, 0x79, ZC3XX_R123_GAMMA03},
+ {0xa0, 0x92, ZC3XX_R124_GAMMA04},
+ {0xa0, 0xa7, ZC3XX_R125_GAMMA05},
+ {0xa0, 0xb9, ZC3XX_R126_GAMMA06},
+ {0xa0, 0xc8, ZC3XX_R127_GAMMA07},
+ {0xa0, 0xd4, ZC3XX_R128_GAMMA08},
+ {0xa0, 0xdf, ZC3XX_R129_GAMMA09},
+ {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A},
+ {0xa0, 0xee, ZC3XX_R12B_GAMMA0B},
+ {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C},
+ {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D},
+ {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E},
+ {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+ {0xa0, 0x26, ZC3XX_R130_GAMMA10},
+ {0xa0, 0x22, ZC3XX_R131_GAMMA11},
+ {0xa0, 0x20, ZC3XX_R132_GAMMA12},
+ {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
+ {0xa0, 0x16, ZC3XX_R134_GAMMA14},
+ {0xa0, 0x13, ZC3XX_R135_GAMMA15},
+ {0xa0, 0x10, ZC3XX_R136_GAMMA16},
+ {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
+ {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+ {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+ {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+ {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
+ {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
+ {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
+ {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
+ {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
+ {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */
+ {0xa0, 0xf4, ZC3XX_R10B_RGB01},
+ {0xa0, 0xf4, ZC3XX_R10C_RGB02},
+ {0xa0, 0xf4, ZC3XX_R10D_RGB10},
+ {0xa0, 0x58, ZC3XX_R10E_RGB11},
+ {0xa0, 0xf4, ZC3XX_R10F_RGB12},
+ {0xa0, 0xf4, ZC3XX_R110_RGB20},
+ {0xa0, 0xf4, ZC3XX_R111_RGB21},
+ {0xa0, 0x58, ZC3XX_R112_RGB22},
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH},
+ {0xa0, 0x22, ZC3XX_R0A4_EXPOSURETIMELOW},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xee, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x3a, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x04, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x0f, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x19, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x1f, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x60, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x4c, ZC3XX_R118_BGAIN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x5c, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x5c, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x96, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x96, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {}
+};
+
+static const struct usb_action cs2102K_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+/*fixme: next sequence = i2c exchanges*/
+ {0xa0, 0x55, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x0a, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x0b, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x0c, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x7b, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x0d, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0xa3, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0xfb, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x03, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x08, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x0e, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x0f, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x10, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x11, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x12, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x15, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x16, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x17, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x78, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID},
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x00, 0x01ad},
+ {0xa0, 0x01, 0x01b1},
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x60, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x4c, ZC3XX_R118_BGAIN},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
+ {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
+ {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
+ {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */
+ {0xa0, 0x38, ZC3XX_R121_GAMMA01},
+ {0xa0, 0x59, ZC3XX_R122_GAMMA02},
+ {0xa0, 0x79, ZC3XX_R123_GAMMA03},
+ {0xa0, 0x92, ZC3XX_R124_GAMMA04},
+ {0xa0, 0xa7, ZC3XX_R125_GAMMA05},
+ {0xa0, 0xb9, ZC3XX_R126_GAMMA06},
+ {0xa0, 0xc8, ZC3XX_R127_GAMMA07},
+ {0xa0, 0xd4, ZC3XX_R128_GAMMA08},
+ {0xa0, 0xdf, ZC3XX_R129_GAMMA09},
+ {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A},
+ {0xa0, 0xee, ZC3XX_R12B_GAMMA0B},
+ {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C},
+ {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D},
+ {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E},
+ {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+ {0xa0, 0x26, ZC3XX_R130_GAMMA10},
+ {0xa0, 0x22, ZC3XX_R131_GAMMA11},
+ {0xa0, 0x20, ZC3XX_R132_GAMMA12},
+ {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
+ {0xa0, 0x16, ZC3XX_R134_GAMMA14},
+ {0xa0, 0x13, ZC3XX_R135_GAMMA15},
+ {0xa0, 0x10, ZC3XX_R136_GAMMA16},
+ {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
+ {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+ {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+ {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+ {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
+ {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
+ {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
+ {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
+ {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
+ {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */
+ {0xa0, 0xf4, ZC3XX_R10B_RGB01},
+ {0xa0, 0xf4, ZC3XX_R10C_RGB02},
+ {0xa0, 0xf4, ZC3XX_R10D_RGB10},
+ {0xa0, 0x58, ZC3XX_R10E_RGB11},
+ {0xa0, 0xf4, ZC3XX_R10F_RGB12},
+ {0xa0, 0xf4, ZC3XX_R110_RGB20},
+ {0xa0, 0xf4, ZC3XX_R111_RGB21},
+ {0xa0, 0x58, ZC3XX_R112_RGB22},
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH},
+ {0xa0, 0x22, ZC3XX_R0A4_EXPOSURETIMELOW},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xee, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x3a, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x04, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x0f, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x19, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x1f, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x60, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x4c, ZC3XX_R118_BGAIN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x5c, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x5c, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x96, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x96, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+/*fixme:what does the next sequence?*/
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0xd0, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0xd0, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x0a, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x0a, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x44, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x44, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x7e, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x7e, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {}
+};
+
+static const struct usb_action gc0305_Initial[] = { /* 640x480 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xa0, 0x04, ZC3XX_R002_CLOCKSELECT}, /* 00,02,04,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
+ {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e6,cc */
+ {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */
+ {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc */
+ {0xaa, 0x13, 0x0002}, /* 00,13,02,aa */
+ {0xaa, 0x15, 0x0003}, /* 00,15,03,aa */
+ {0xaa, 0x01, 0x0000}, /* 00,01,00,aa */
+ {0xaa, 0x02, 0x0000}, /* 00,02,00,aa */
+ {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */
+ {0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa */
+ {0xaa, 0x1d, 0x0080}, /* 00,1d,80,aa */
+ {0xaa, 0x1f, 0x0008}, /* 00,1f,08,aa */
+ {0xaa, 0x21, 0x0012}, /* 00,21,12,aa */
+ {0xa0, 0x82, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,82,cc */
+ {0xa0, 0x83, ZC3XX_R087_EXPTIMEMID}, /* 00,87,83,cc */
+ {0xa0, 0x84, ZC3XX_R088_EXPTIMELOW}, /* 00,88,84,cc */
+ {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */
+ {0xaa, 0x0a, 0x0000}, /* 00,0a,00,aa */
+ {0xaa, 0x0b, 0x00b0}, /* 00,0b,b0,aa */
+ {0xaa, 0x0c, 0x0000}, /* 00,0c,00,aa */
+ {0xaa, 0x0d, 0x00b0}, /* 00,0d,b0,aa */
+ {0xaa, 0x0e, 0x0000}, /* 00,0e,00,aa */
+ {0xaa, 0x0f, 0x00b0}, /* 00,0f,b0,aa */
+ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
+ {0xaa, 0x11, 0x00b0}, /* 00,11,b0,aa */
+ {0xaa, 0x16, 0x0001}, /* 00,16,01,aa */
+ {0xaa, 0x17, 0x00e6}, /* 00,17,e6,aa */
+ {0xaa, 0x18, 0x0002}, /* 00,18,02,aa */
+ {0xaa, 0x19, 0x0086}, /* 00,19,86,aa */
+ {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
+ {0xaa, 0x1b, 0x0020}, /* 00,1b,20,aa */
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x76, ZC3XX_R189_AWBSTATUS}, /* 01,89,76,cc */
+ {0xa0, 0x09, 0x01ad}, /* 01,ad,09,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */
+ {0xa0, 0x85, ZC3XX_R18D_YTARGET}, /* 01,8d,85,cc */
+ {0xa0, 0x00, 0x011e}, /* 01,1e,00,cc */
+ {0xa0, 0x52, ZC3XX_R116_RGAIN}, /* 01,16,52,cc */
+ {0xa0, 0x40, ZC3XX_R117_GGAIN}, /* 01,17,40,cc */
+ {0xa0, 0x52, ZC3XX_R118_BGAIN}, /* 01,18,52,cc */
+ {0xa0, 0x03, ZC3XX_R113_RGB03}, /* 01,13,03,cc */
+ {}
+};
+static const struct usb_action gc0305_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
+ {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e8,cc */
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */
+ {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc */
+ {0xaa, 0x13, 0x0000}, /* 00,13,00,aa */
+ {0xaa, 0x15, 0x0001}, /* 00,15,01,aa */
+ {0xaa, 0x01, 0x0000}, /* 00,01,00,aa */
+ {0xaa, 0x02, 0x0000}, /* 00,02,00,aa */
+ {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */
+ {0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa */
+ {0xaa, 0x1d, 0x0080}, /* 00,1d,80,aa */
+ {0xaa, 0x1f, 0x0008}, /* 00,1f,08,aa */
+ {0xaa, 0x21, 0x0012}, /* 00,21,12,aa */
+ {0xa0, 0x82, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,82,cc */
+ {0xa0, 0x83, ZC3XX_R087_EXPTIMEMID}, /* 00,87,83,cc */
+ {0xa0, 0x84, ZC3XX_R088_EXPTIMELOW}, /* 00,88,84,cc */
+ {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */
+ {0xaa, 0x0a, 0x0000}, /* 00,0a,00,aa */
+ {0xaa, 0x0b, 0x00b0}, /* 00,0b,b0,aa */
+ {0xaa, 0x0c, 0x0000}, /* 00,0c,00,aa */
+ {0xaa, 0x0d, 0x00b0}, /* 00,0d,b0,aa */
+ {0xaa, 0x0e, 0x0000}, /* 00,0e,00,aa */
+ {0xaa, 0x0f, 0x00b0}, /* 00,0f,b0,aa */
+ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
+ {0xaa, 0x11, 0x00b0}, /* 00,11,b0,aa */
+ {0xaa, 0x16, 0x0001}, /* 00,16,01,aa */
+ {0xaa, 0x17, 0x00e8}, /* 00,17,e8,aa */
+ {0xaa, 0x18, 0x0002}, /* 00,18,02,aa */
+ {0xaa, 0x19, 0x0088}, /* 00,19,88,aa */
+ {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
+ {0xaa, 0x1b, 0x0020}, /* 00,1b,20,aa */
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x76, ZC3XX_R189_AWBSTATUS}, /* 01,89,76,cc */
+ {0xa0, 0x09, 0x01ad}, /* 01,ad,09,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */
+ {0xa0, 0x00, 0x011e}, /* 01,1e,00,cc */
+ {0xa0, 0x52, ZC3XX_R116_RGAIN}, /* 01,16,52,cc */
+ {0xa0, 0x40, ZC3XX_R117_GGAIN}, /* 01,17,40,cc */
+ {0xa0, 0x52, ZC3XX_R118_BGAIN}, /* 01,18,52,cc */
+ {0xa0, 0x03, ZC3XX_R113_RGB03}, /* 01,13,03,cc */
+ {}
+};
+static const struct usb_action gc0305_50HZ[] = {
+ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
+ {0xaa, 0x83, 0x0002}, /* 00,83,02,aa */
+ {0xaa, 0x84, 0x0038}, /* 00,84,38,aa */ /* win: 00,84,ec */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x0b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0b,cc */
+ {0xa0, 0x18, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,18,cc */
+ /* win: 01,92,10 */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x8e, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,8e,cc */
+ /* win: 01,97,ec */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc */
+ {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc */
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc */
+ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */
+ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,60,cc */
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */
+/* {0xa0, 0x85, ZC3XX_R18D_YTARGET}, * 01,8d,85,cc *
+ * if 640x480 */
+ {}
+};
+static const struct usb_action gc0305_60HZ[] = {
+ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
+ {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */
+ {0xaa, 0x84, 0x00ec}, /* 00,84,ec,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x0b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0b,cc */
+ {0xa0, 0x10, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,10,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0xec, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,ec,cc */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc */
+ {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc */
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc */
+ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */
+ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,60,cc */
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */
+ {0xa0, 0x80, ZC3XX_R18D_YTARGET}, /* 01,8d,80,cc */
+ {}
+};
+
+static const struct usb_action gc0305_NoFliker[] = {
+ {0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc */
+ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
+ {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */
+ {0xaa, 0x84, 0x0020}, /* 00,84,20,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x00, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,00,cc */
+ {0xa0, 0x48, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,48,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc */
+ {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc */
+ {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc */
+ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */
+ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,60,cc */
+ {0xa0, 0x03, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,03,cc */
+ {0xa0, 0x80, ZC3XX_R18D_YTARGET}, /* 01,8d,80,cc */
+ {}
+};
+
+static const struct usb_action hdcs2020_InitialScale[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x11, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* qtable 0x05 */
+ {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+ {0xaa, 0x1c, 0x0000},
+ {0xaa, 0x0a, 0x0001},
+ {0xaa, 0x0b, 0x0006},
+ {0xaa, 0x0c, 0x007b},
+ {0xaa, 0x0d, 0x00a7},
+ {0xaa, 0x03, 0x00fb},
+ {0xaa, 0x05, 0x0000},
+ {0xaa, 0x06, 0x0003},
+ {0xaa, 0x09, 0x0008},
+
+ {0xaa, 0x0f, 0x0018}, /* set sensor gain */
+ {0xaa, 0x10, 0x0018},
+ {0xaa, 0x11, 0x0018},
+ {0xaa, 0x12, 0x0018},
+
+ {0xaa, 0x15, 0x004e},
+ {0xaa, 0x1c, 0x0004},
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x70, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa1, 0x01, 0x0002},
+ {0xa1, 0x01, 0x0008},
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x40, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x40, ZC3XX_R118_BGAIN},
+ {0xa1, 0x01, 0x0008},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
+ {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
+ {0xa1, 0x01, 0x01c8},
+ {0xa1, 0x01, 0x01c9},
+ {0xa1, 0x01, 0x01ca},
+ {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
+ {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */
+ {0xa0, 0x38, ZC3XX_R121_GAMMA01},
+ {0xa0, 0x59, ZC3XX_R122_GAMMA02},
+ {0xa0, 0x79, ZC3XX_R123_GAMMA03},
+ {0xa0, 0x92, ZC3XX_R124_GAMMA04},
+ {0xa0, 0xa7, ZC3XX_R125_GAMMA05},
+ {0xa0, 0xb9, ZC3XX_R126_GAMMA06},
+ {0xa0, 0xc8, ZC3XX_R127_GAMMA07},
+ {0xa0, 0xd4, ZC3XX_R128_GAMMA08},
+ {0xa0, 0xdf, ZC3XX_R129_GAMMA09},
+ {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A},
+ {0xa0, 0xee, ZC3XX_R12B_GAMMA0B},
+ {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C},
+ {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D},
+ {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E},
+ {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+ {0xa0, 0x26, ZC3XX_R130_GAMMA10},
+ {0xa0, 0x22, ZC3XX_R131_GAMMA11},
+ {0xa0, 0x20, ZC3XX_R132_GAMMA12},
+ {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
+ {0xa0, 0x16, ZC3XX_R134_GAMMA14},
+ {0xa0, 0x13, ZC3XX_R135_GAMMA15},
+ {0xa0, 0x10, ZC3XX_R136_GAMMA16},
+ {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
+ {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+ {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+ {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+ {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
+ {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
+ {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
+ {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
+ {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
+
+ {0xa0, 0x66, ZC3XX_R10A_RGB00}, /* matrix */
+ {0xa0, 0xed, ZC3XX_R10B_RGB01},
+ {0xa0, 0xed, ZC3XX_R10C_RGB02},
+ {0xa0, 0xed, ZC3XX_R10D_RGB10},
+ {0xa0, 0x66, ZC3XX_R10E_RGB11},
+ {0xa0, 0xed, ZC3XX_R10F_RGB12},
+ {0xa0, 0xed, ZC3XX_R110_RGB20},
+ {0xa0, 0xed, ZC3XX_R111_RGB21},
+ {0xa0, 0x66, ZC3XX_R112_RGB22},
+
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x13, 0x0031},
+ {0xaa, 0x14, 0x0001},
+ {0xaa, 0x0e, 0x0004},
+ {0xaa, 0x19, 0x00cd},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x62, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+
+ {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 0x14 */
+ {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x04, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x18, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x2c, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x41, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x40, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x40, ZC3XX_R118_BGAIN},
+ {}
+};
+static const struct usb_action hdcs2020_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+ {0xaa, 0x1c, 0x0000},
+ {0xaa, 0x0a, 0x0001},
+ {0xaa, 0x0b, 0x0006},
+ {0xaa, 0x0c, 0x007a},
+ {0xaa, 0x0d, 0x00a7},
+ {0xaa, 0x03, 0x00fb},
+ {0xaa, 0x05, 0x0000},
+ {0xaa, 0x06, 0x0003},
+ {0xaa, 0x09, 0x0008},
+ {0xaa, 0x0f, 0x0018}, /* original setting */
+ {0xaa, 0x10, 0x0018},
+ {0xaa, 0x11, 0x0018},
+ {0xaa, 0x12, 0x0018},
+ {0xaa, 0x15, 0x004e},
+ {0xaa, 0x1c, 0x0004},
+ {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x70, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa1, 0x01, 0x0002},
+ {0xa1, 0x01, 0x0008},
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x40, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x40, ZC3XX_R118_BGAIN},
+ {0xa1, 0x01, 0x0008},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
+ {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
+ {0xa1, 0x01, 0x01c8},
+ {0xa1, 0x01, 0x01c9},
+ {0xa1, 0x01, 0x01ca},
+ {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
+ {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */
+ {0xa0, 0x38, ZC3XX_R121_GAMMA01},
+ {0xa0, 0x59, ZC3XX_R122_GAMMA02},
+ {0xa0, 0x79, ZC3XX_R123_GAMMA03},
+ {0xa0, 0x92, ZC3XX_R124_GAMMA04},
+ {0xa0, 0xa7, ZC3XX_R125_GAMMA05},
+ {0xa0, 0xb9, ZC3XX_R126_GAMMA06},
+ {0xa0, 0xc8, ZC3XX_R127_GAMMA07},
+ {0xa0, 0xd4, ZC3XX_R128_GAMMA08},
+ {0xa0, 0xdf, ZC3XX_R129_GAMMA09},
+ {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A},
+ {0xa0, 0xee, ZC3XX_R12B_GAMMA0B},
+ {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C},
+ {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D},
+ {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E},
+ {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+ {0xa0, 0x26, ZC3XX_R130_GAMMA10},
+ {0xa0, 0x22, ZC3XX_R131_GAMMA11},
+ {0xa0, 0x20, ZC3XX_R132_GAMMA12},
+ {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
+ {0xa0, 0x16, ZC3XX_R134_GAMMA14},
+ {0xa0, 0x13, ZC3XX_R135_GAMMA15},
+ {0xa0, 0x10, ZC3XX_R136_GAMMA16},
+ {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
+ {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+ {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+ {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+ {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
+ {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
+ {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
+ {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
+ {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
+ {0xa0, 0x66, ZC3XX_R10A_RGB00}, /* matrix */
+ {0xa0, 0xed, ZC3XX_R10B_RGB01},
+ {0xa0, 0xed, ZC3XX_R10C_RGB02},
+ {0xa0, 0xed, ZC3XX_R10D_RGB10},
+ {0xa0, 0x66, ZC3XX_R10E_RGB11},
+ {0xa0, 0xed, ZC3XX_R10F_RGB12},
+ {0xa0, 0xed, ZC3XX_R110_RGB20},
+ {0xa0, 0xed, ZC3XX_R111_RGB21},
+ {0xa0, 0x66, ZC3XX_R112_RGB22},
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ /**** set exposure ***/
+ {0xaa, 0x13, 0x0031},
+ {0xaa, 0x14, 0x0001},
+ {0xaa, 0x0e, 0x0004},
+ {0xaa, 0x19, 0x00cd},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x62, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x04, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x18, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x2c, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x41, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x40, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x40, ZC3XX_R118_BGAIN},
+ {}
+};
+static const struct usb_action hdcs2020_50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x13, 0x0018}, /* 00,13,18,aa */
+ {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */
+ {0xaa, 0x0e, 0x0005}, /* 00,0e,05,aa */
+ {0xaa, 0x19, 0x001f}, /* 00,19,1f,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */
+ {0xa0, 0x76, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,76,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x46, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,46,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
+ {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,28,cc */
+ {0xa0, 0x05, ZC3XX_R01D_HSYNC_0}, /* 00,1d,05,cc */
+ {0xa0, 0x1a, ZC3XX_R01E_HSYNC_1}, /* 00,1e,1a,cc */
+ {0xa0, 0x2f, ZC3XX_R01F_HSYNC_2}, /* 00,1f,2f,cc */
+ {}
+};
+static const struct usb_action hdcs2020_60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x13, 0x0031}, /* 00,13,31,aa */
+ {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */
+ {0xaa, 0x0e, 0x0004}, /* 00,0e,04,aa */
+ {0xaa, 0x19, 0x00cd}, /* 00,19,cd,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */
+ {0xa0, 0x62, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,62,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3d,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
+ {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,28,cc */
+ {0xa0, 0x04, ZC3XX_R01D_HSYNC_0}, /* 00,1d,04,cc */
+ {0xa0, 0x18, ZC3XX_R01E_HSYNC_1}, /* 00,1e,18,cc */
+ {0xa0, 0x2c, ZC3XX_R01F_HSYNC_2}, /* 00,1f,2c,cc */
+ {}
+};
+static const struct usb_action hdcs2020_NoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x13, 0x0010}, /* 00,13,10,aa */
+ {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */
+ {0xaa, 0x0e, 0x0004}, /* 00,0e,04,aa */
+ {0xaa, 0x19, 0x0000}, /* 00,19,00,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */
+ {0xa0, 0x70, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,70,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+ {0xa0, 0x04, ZC3XX_R01D_HSYNC_0}, /* 00,1d,04,cc */
+ {0xa0, 0x17, ZC3XX_R01E_HSYNC_1}, /* 00,1e,17,cc */
+ {0xa0, 0x2a, ZC3XX_R01F_HSYNC_2}, /* 00,1f,2a,cc */
+ {}
+};
+
+static const struct usb_action hv7131b_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xaa, 0x30, 0x002d},
+ {0xaa, 0x01, 0x0005},
+ {0xaa, 0x11, 0x0000},
+ {0xaa, 0x13, 0x0001}, /* {0xaa, 0x13, 0x0000}, */
+ {0xaa, 0x14, 0x0001},
+ {0xaa, 0x15, 0x00e8},
+ {0xaa, 0x16, 0x0002},
+ {0xaa, 0x17, 0x0086}, /* 00,17,88,aa */
+ {0xaa, 0x31, 0x0038},
+ {0xaa, 0x32, 0x0038},
+ {0xaa, 0x33, 0x0038},
+ {0xaa, 0x5b, 0x0001},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x68, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x00, 0x01ad},
+ {0xa0, 0xc0, 0x019b},
+ {0xa0, 0xa0, 0x019c},
+ {0xa0, 0x02, ZC3XX_R188_MINGAIN},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xaa, 0x02, 0x0090}, /* 00,02,80,aa */
+ {}
+};
+
+static const struct usb_action hv7131b_Initial[] = { /* 640x480*/
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xaa, 0x30, 0x002d},
+ {0xaa, 0x01, 0x0005},
+ {0xaa, 0x11, 0x0001},
+ {0xaa, 0x13, 0x0000}, /* {0xaa, 0x13, 0x0001}; */
+ {0xaa, 0x14, 0x0001},
+ {0xaa, 0x15, 0x00e6},
+ {0xaa, 0x16, 0x0002},
+ {0xaa, 0x17, 0x0086},
+ {0xaa, 0x31, 0x0038},
+ {0xaa, 0x32, 0x0038},
+ {0xaa, 0x33, 0x0038},
+ {0xaa, 0x5b, 0x0001},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x70, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x00, 0x01ad},
+ {0xa0, 0xc0, 0x019b},
+ {0xa0, 0xa0, 0x019c},
+ {0xa0, 0x02, ZC3XX_R188_MINGAIN},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xaa, 0x02, 0x0090}, /* {0xaa, 0x02, 0x0080}, */
+ {}
+};
+static const struct usb_action hv7131b_50HZ[] = { /* 640x480*/
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */
+ {0xaa, 0x26, 0x0053}, /* 00,26,53,aa */
+ {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */
+ {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
+ {0xaa, 0x21, 0x0050}, /* 00,21,50,aa */
+ {0xaa, 0x22, 0x001b}, /* 00,22,1b,aa */
+ {0xaa, 0x23, 0x00fc}, /* 00,23,fc,aa */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0x9b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,9b,cc */
+ {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0xea, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,ea,cc */
+ {0xa0, 0x60, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,60,cc */
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */
+ {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
+ {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */
+ {0xa0, 0x1b, ZC3XX_R01F_HSYNC_2}, /* 00,1f,1b,cc */
+ {0xa0, 0xfc, ZC3XX_R020_HSYNC_3}, /* 00,20,fc,cc */
+ {}
+};
+static const struct usb_action hv7131b_50HZScale[] = { /* 320x240 */
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */
+ {0xaa, 0x26, 0x0053}, /* 00,26,53,aa */
+ {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */
+ {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
+ {0xaa, 0x21, 0x0050}, /* 00,21,50,aa */
+ {0xaa, 0x22, 0x0012}, /* 00,22,12,aa */
+ {0xaa, 0x23, 0x0080}, /* 00,23,80,aa */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0x9b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,9b,cc */
+ {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */
+ {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,01,cc */
+ {0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,d4,cc */
+ {0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,c0,cc */
+ {0xa0, 0x07, ZC3XX_R18C_AEFREEZE}, /* 01,8c,07,cc */
+ {0xa0, 0x0f, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,0f,cc */
+ {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
+ {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */
+ {0xa0, 0x12, ZC3XX_R01F_HSYNC_2}, /* 00,1f,12,cc */
+ {0xa0, 0x80, ZC3XX_R020_HSYNC_3}, /* 00,20,80,cc */
+ {}
+};
+static const struct usb_action hv7131b_60HZ[] = { /* 640x480*/
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */
+ {0xaa, 0x26, 0x00a1}, /* 00,26,a1,aa */
+ {0xaa, 0x27, 0x0020}, /* 00,27,20,aa */
+ {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
+ {0xaa, 0x21, 0x0040}, /* 00,21,40,aa */
+ {0xaa, 0x22, 0x0013}, /* 00,22,13,aa */
+ {0xaa, 0x23, 0x004c}, /* 00,23,4c,aa */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0x4d, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,4d,cc */
+ {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,60,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0xc3, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,c3,cc */
+ {0xa0, 0x50, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,50,cc */
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */
+ {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
+ {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, /* 00,1e,40,cc */
+ {0xa0, 0x13, ZC3XX_R01F_HSYNC_2}, /* 00,1f,13,cc */
+ {0xa0, 0x4c, ZC3XX_R020_HSYNC_3}, /* 00,20,4c,cc */
+ {}
+};
+static const struct usb_action hv7131b_60HZScale[] = { /* 320x240 */
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */
+ {0xaa, 0x26, 0x00a1}, /* 00,26,a1,aa */
+ {0xaa, 0x27, 0x0020}, /* 00,27,20,aa */
+ {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
+ {0xaa, 0x21, 0x00a0}, /* 00,21,a0,aa */
+ {0xaa, 0x22, 0x0016}, /* 00,22,16,aa */
+ {0xaa, 0x23, 0x0040}, /* 00,23,40,aa */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0x4d, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,4d,cc */
+ {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,60,cc */
+ {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,01,cc */
+ {0xa0, 0x86, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,86,cc */
+ {0xa0, 0xa0, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,a0,cc */
+ {0xa0, 0x07, ZC3XX_R18C_AEFREEZE}, /* 01,8c,07,cc */
+ {0xa0, 0x0f, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,0f,cc */
+ {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
+ {0xa0, 0xa0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,a0,cc */
+ {0xa0, 0x16, ZC3XX_R01F_HSYNC_2}, /* 00,1f,16,cc */
+ {0xa0, 0x40, ZC3XX_R020_HSYNC_3}, /* 00,20,40,cc */
+ {}
+};
+static const struct usb_action hv7131b_NoFliker[] = { /* 640x480*/
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x25, 0x0003}, /* 00,25,03,aa */
+ {0xaa, 0x26, 0x0000}, /* 00,26,00,aa */
+ {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */
+ {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
+ {0xaa, 0x21, 0x0010}, /* 00,21,10,aa */
+ {0xaa, 0x22, 0x0000}, /* 00,22,00,aa */
+ {0xaa, 0x23, 0x0003}, /* 00,23,03,aa */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,f8,cc */
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,00,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,02,cc */
+ {0xa0, 0x00, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,00,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
+ {0xa0, 0x10, ZC3XX_R01E_HSYNC_1}, /* 00,1e,10,cc */
+ {0xa0, 0x00, ZC3XX_R01F_HSYNC_2}, /* 00,1f,00,cc */
+ {0xa0, 0x03, ZC3XX_R020_HSYNC_3}, /* 00,20,03,cc */
+ {}
+};
+static const struct usb_action hv7131b_NoFlikerScale[] = { /* 320x240 */
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x25, 0x0003}, /* 00,25,03,aa */
+ {0xaa, 0x26, 0x0000}, /* 00,26,00,aa */
+ {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */
+ {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
+ {0xaa, 0x21, 0x00a0}, /* 00,21,a0,aa */
+ {0xaa, 0x22, 0x0016}, /* 00,22,16,aa */
+ {0xaa, 0x23, 0x0040}, /* 00,23,40,aa */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,f8,cc */
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,00,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,02,cc */
+ {0xa0, 0x00, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,00,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
+ {0xa0, 0xa0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,a0,cc */
+ {0xa0, 0x16, ZC3XX_R01F_HSYNC_2}, /* 00,1f,16,cc */
+ {0xa0, 0x40, ZC3XX_R020_HSYNC_3}, /* 00,20,40,cc */
+ {}
+};
+
+/* from lPEPI264v.inf (hv7131b!) */
+static const struct usb_action hv7131r_InitialScale[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},
+ {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xdd, 0x00, 0x0200},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xaa, 0x01, 0x000c},
+ {0xaa, 0x11, 0x0000},
+ {0xaa, 0x13, 0x0000},
+ {0xaa, 0x14, 0x0001},
+ {0xaa, 0x15, 0x00e8},
+ {0xaa, 0x16, 0x0002},
+ {0xaa, 0x17, 0x0088},
+ {0xaa, 0x30, 0x000b},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x78, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x00, 0x01ad},
+ {0xa0, 0xc0, 0x019b},
+ {0xa0, 0xa0, 0x019c},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {}
+};
+static const struct usb_action hv7131r_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},
+ {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
+ {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xdd, 0x00, 0x0200},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xaa, 0x01, 0x000c},
+ {0xaa, 0x11, 0x0000},
+ {0xaa, 0x13, 0x0000},
+ {0xaa, 0x14, 0x0001},
+ {0xaa, 0x15, 0x00e6},
+ {0xaa, 0x16, 0x0002},
+ {0xaa, 0x17, 0x0086},
+ {0xaa, 0x30, 0x000b},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x78, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x00, 0x01ad},
+ {0xa0, 0xc0, 0x019b},
+ {0xa0, 0xa0, 0x019c},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {}
+};
+static const struct usb_action hv7131r_50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x06, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x68, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0xea, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x60, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x18, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action hv7131r_50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x0c, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0xd1, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x40, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x18, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action hv7131r_60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x06, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x1a, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0xc3, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x50, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x18, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action hv7131r_60HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x0c, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x35, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x86, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0xa0, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x18, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action hv7131r_NoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x58, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action hv7131r_NoFlikerScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x04, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0xb0, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+ {}
+};
+
+static const struct usb_action icm105a_InitialScale[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x0c, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x00, ZC3XX_R097_WINYSTARTHIGH},
+ {0xa0, 0x01, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R099_WINXSTARTHIGH},
+ {0xa0, 0x01, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x01, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x01, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},
+ {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xaa, 0x01, 0x0010},
+ {0xaa, 0x03, 0x0000},
+ {0xaa, 0x04, 0x0001},
+ {0xaa, 0x05, 0x0020},
+ {0xaa, 0x06, 0x0001},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0001},
+ {0xaa, 0x04, 0x0011},
+ {0xaa, 0x05, 0x00a0},
+ {0xaa, 0x06, 0x0001},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0002},
+ {0xaa, 0x04, 0x0013},
+ {0xaa, 0x05, 0x0020},
+ {0xaa, 0x06, 0x0001},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0003},
+ {0xaa, 0x04, 0x0015},
+ {0xaa, 0x05, 0x0020},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0004},
+ {0xaa, 0x04, 0x0017},
+ {0xaa, 0x05, 0x0020},
+ {0xaa, 0x06, 0x000d},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0005},
+ {0xaa, 0x04, 0x0019},
+ {0xaa, 0x05, 0x0020},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0006},
+ {0xaa, 0x04, 0x0017},
+ {0xaa, 0x05, 0x0026},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0007},
+ {0xaa, 0x04, 0x0019},
+ {0xaa, 0x05, 0x0022},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0008},
+ {0xaa, 0x04, 0x0021},
+ {0xaa, 0x05, 0x00aa},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0009},
+ {0xaa, 0x04, 0x0023},
+ {0xaa, 0x05, 0x00aa},
+ {0xaa, 0x06, 0x000d},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x000a},
+ {0xaa, 0x04, 0x0025},
+ {0xaa, 0x05, 0x00aa},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x000b},
+ {0xaa, 0x04, 0x00ec},
+ {0xaa, 0x05, 0x002e},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x000c},
+ {0xaa, 0x04, 0x00fa},
+ {0xaa, 0x05, 0x002a},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x07, 0x000d},
+ {0xaa, 0x01, 0x0005},
+ {0xaa, 0x94, 0x0002},
+ {0xaa, 0x90, 0x0000},
+ {0xaa, 0x91, 0x001f},
+ {0xaa, 0x10, 0x0064},
+ {0xaa, 0x9b, 0x00f0},
+ {0xaa, 0x9c, 0x0002},
+ {0xaa, 0x14, 0x001a},
+ {0xaa, 0x20, 0x0080},
+ {0xaa, 0x22, 0x0080},
+ {0xaa, 0x24, 0x0080},
+ {0xaa, 0x26, 0x0080},
+ {0xaa, 0x00, 0x0084},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xaa, 0xa8, 0x00c0},
+ {0xa1, 0x01, 0x0002},
+ {0xa1, 0x01, 0x0008},
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x40, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x40, ZC3XX_R118_BGAIN},
+ {0xa1, 0x01, 0x0008},
+
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
+ {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
+ {0xa1, 0x01, 0x01c8},
+ {0xa1, 0x01, 0x01c9},
+ {0xa1, 0x01, 0x01ca},
+ {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
+ {0xa0, 0x52, ZC3XX_R10A_RGB00}, /* matrix */
+ {0xa0, 0xf7, ZC3XX_R10B_RGB01},
+ {0xa0, 0xf7, ZC3XX_R10C_RGB02},
+ {0xa0, 0xf7, ZC3XX_R10D_RGB10},
+ {0xa0, 0x52, ZC3XX_R10E_RGB11},
+ {0xa0, 0xf7, ZC3XX_R10F_RGB12},
+ {0xa0, 0xf7, ZC3XX_R110_RGB20},
+ {0xa0, 0xf7, ZC3XX_R111_RGB21},
+ {0xa0, 0x52, ZC3XX_R112_RGB22},
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x0d, 0x0003},
+ {0xaa, 0x0c, 0x008c},
+ {0xaa, 0x0e, 0x0095},
+ {0xaa, 0x0f, 0x0002},
+ {0xaa, 0x1c, 0x0094},
+ {0xaa, 0x1d, 0x0002},
+ {0xaa, 0x20, 0x0080},
+ {0xaa, 0x22, 0x0080},
+ {0xaa, 0x24, 0x0080},
+ {0xaa, 0x26, 0x0080},
+ {0xaa, 0x00, 0x0084},
+ {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH},
+ {0xa0, 0x94, ZC3XX_R0A4_EXPOSURETIMELOW},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x84, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xe3, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xec, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf5, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0xc0, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x40, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x40, ZC3XX_R118_BGAIN},
+ {}
+};
+
+static const struct usb_action icm105a_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x0c, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x00, ZC3XX_R097_WINYSTARTHIGH},
+ {0xa0, 0x02, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R099_WINXSTARTHIGH},
+ {0xa0, 0x02, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x02, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x02, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},
+ {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
+ {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW},
+ {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xaa, 0x01, 0x0010},
+ {0xaa, 0x03, 0x0000},
+ {0xaa, 0x04, 0x0001},
+ {0xaa, 0x05, 0x0020},
+ {0xaa, 0x06, 0x0001},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0001},
+ {0xaa, 0x04, 0x0011},
+ {0xaa, 0x05, 0x00a0},
+ {0xaa, 0x06, 0x0001},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0002},
+ {0xaa, 0x04, 0x0013},
+ {0xaa, 0x05, 0x0020},
+ {0xaa, 0x06, 0x0001},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0003},
+ {0xaa, 0x04, 0x0015},
+ {0xaa, 0x05, 0x0020},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0004},
+ {0xaa, 0x04, 0x0017},
+ {0xaa, 0x05, 0x0020},
+ {0xaa, 0x06, 0x000d},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0005},
+ {0xa0, 0x04, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x19, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa1, 0x01, 0x0091},
+ {0xaa, 0x05, 0x0020},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0006},
+ {0xaa, 0x04, 0x0017},
+ {0xaa, 0x05, 0x0026},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0007},
+ {0xaa, 0x04, 0x0019},
+ {0xaa, 0x05, 0x0022},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0008},
+ {0xaa, 0x04, 0x0021},
+ {0xaa, 0x05, 0x00aa},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0009},
+ {0xaa, 0x04, 0x0023},
+ {0xaa, 0x05, 0x00aa},
+ {0xaa, 0x06, 0x000d},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x000a},
+ {0xaa, 0x04, 0x0025},
+ {0xaa, 0x05, 0x00aa},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x000b},
+ {0xaa, 0x04, 0x00ec},
+ {0xaa, 0x05, 0x002e},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x000c},
+ {0xaa, 0x04, 0x00fa},
+ {0xaa, 0x05, 0x002a},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x07, 0x000d},
+ {0xaa, 0x01, 0x0005},
+ {0xaa, 0x94, 0x0002},
+ {0xaa, 0x90, 0x0000},
+ {0xaa, 0x91, 0x0010},
+ {0xaa, 0x10, 0x0064},
+ {0xaa, 0x9b, 0x00f0},
+ {0xaa, 0x9c, 0x0002},
+ {0xaa, 0x14, 0x001a},
+ {0xaa, 0x20, 0x0080},
+ {0xaa, 0x22, 0x0080},
+ {0xaa, 0x24, 0x0080},
+ {0xaa, 0x26, 0x0080},
+ {0xaa, 0x00, 0x0084},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xaa, 0xa8, 0x0080},
+ {0xa0, 0x78, ZC3XX_R18D_YTARGET},
+ {0xa1, 0x01, 0x0002},
+ {0xa1, 0x01, 0x0008},
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x40, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x40, ZC3XX_R118_BGAIN},
+ {0xa1, 0x01, 0x0008},
+
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
+ {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
+ {0xa1, 0x01, 0x01c8},
+ {0xa1, 0x01, 0x01c9},
+ {0xa1, 0x01, 0x01ca},
+ {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
+
+ {0xa0, 0x52, ZC3XX_R10A_RGB00}, /* matrix */
+ {0xa0, 0xf7, ZC3XX_R10B_RGB01},
+ {0xa0, 0xf7, ZC3XX_R10C_RGB02},
+ {0xa0, 0xf7, ZC3XX_R10D_RGB10},
+ {0xa0, 0x52, ZC3XX_R10E_RGB11},
+ {0xa0, 0xf7, ZC3XX_R10F_RGB12},
+ {0xa0, 0xf7, ZC3XX_R110_RGB20},
+ {0xa0, 0xf7, ZC3XX_R111_RGB21},
+ {0xa0, 0x52, ZC3XX_R112_RGB22},
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x0d, 0x0003},
+ {0xaa, 0x0c, 0x0020},
+ {0xaa, 0x0e, 0x000e},
+ {0xaa, 0x0f, 0x0002},
+ {0xaa, 0x1c, 0x000d},
+ {0xaa, 0x1d, 0x0002},
+ {0xaa, 0x20, 0x0080},
+ {0xaa, 0x22, 0x0080},
+ {0xaa, 0x24, 0x0080},
+ {0xaa, 0x26, 0x0080},
+ {0xaa, 0x00, 0x0084},
+ {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH},
+ {0xa0, 0x0d, ZC3XX_R0A4_EXPOSURETIMELOW},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x1a, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x4b, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xc8, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xd8, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xea, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x40, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x40, ZC3XX_R118_BGAIN},
+ {}
+};
+static const struct usb_action icm105a_50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
+ {0xaa, 0x0c, 0x0020}, /* 00,0c,20,aa */
+ {0xaa, 0x0e, 0x000e}, /* 00,0e,0e,aa */
+ {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */
+ {0xaa, 0x1c, 0x000d}, /* 00,1c,0d,aa */
+ {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
+ {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */
+ {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */
+ {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */
+ {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */
+ {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */
+ {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */
+ {0xa0, 0x0d, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,0d,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
+ {0xa0, 0x1a, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,1a,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x4b, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,4b,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+ {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,12,cc */
+ {0xa0, 0xc8, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c8,cc */
+ {0xa0, 0xd8, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d8,cc */
+ {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ea,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {}
+};
+static const struct usb_action icm105a_50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
+ {0xaa, 0x0c, 0x008c}, /* 00,0c,8c,aa */
+ {0xaa, 0x0e, 0x0095}, /* 00,0e,95,aa */
+ {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */
+ {0xaa, 0x1c, 0x0094}, /* 00,1c,94,aa */
+ {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
+ {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */
+ {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */
+ {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */
+ {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */
+ {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */
+ {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */
+ {0xa0, 0x94, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,94,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
+ {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,20,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x84, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,84,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+ {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,12,cc */
+ {0xa0, 0xe3, ZC3XX_R01D_HSYNC_0}, /* 00,1d,e3,cc */
+ {0xa0, 0xec, ZC3XX_R01E_HSYNC_1}, /* 00,1e,ec,cc */
+ {0xa0, 0xf5, ZC3XX_R01F_HSYNC_2}, /* 00,1f,f5,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, /* 01,a7,00,cc */
+ {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */
+ {}
+};
+static const struct usb_action icm105a_60HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
+ {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */
+ {0xaa, 0x0e, 0x000d}, /* 00,0e,0d,aa */
+ {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */
+ {0xaa, 0x1c, 0x0008}, /* 00,1c,08,aa */
+ {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
+ {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */
+ {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */
+ {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */
+ {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */
+ {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */
+ {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */
+ {0xa0, 0x08, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,08,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
+ {0xa0, 0x10, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,10,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x41, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,41,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+ {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,12,cc */
+ {0xa0, 0xc1, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c1,cc */
+ {0xa0, 0xd4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d4,cc */
+ {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {}
+};
+static const struct usb_action icm105a_60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
+ {0xaa, 0x0c, 0x0008}, /* 00,0c,08,aa */
+ {0xaa, 0x0e, 0x0086}, /* 00,0e,86,aa */
+ {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */
+ {0xaa, 0x1c, 0x0085}, /* 00,1c,85,aa */
+ {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
+ {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */
+ {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */
+ {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */
+ {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */
+ {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */
+ {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */
+ {0xa0, 0x85, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,85,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
+ {0xa0, 0x08, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,08,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,81,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+ {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,12,cc */
+ {0xa0, 0xc2, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c2,cc */
+ {0xa0, 0xd6, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d6,cc */
+ {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ea,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, /* 01,a7,00,cc */
+ {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */
+ {}
+};
+static const struct usb_action icm105a_NoFlikerScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
+ {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */
+ {0xaa, 0x0e, 0x000d}, /* 00,0e,0d,aa */
+ {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */
+ {0xaa, 0x1c, 0x0000}, /* 00,1c,00,aa */
+ {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
+ {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */
+ {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */
+ {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */
+ {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */
+ {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */
+ {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */
+ {0xa0, 0x00, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,00,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
+ {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,20,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+ {0xa0, 0xc1, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c1,cc */
+ {0xa0, 0xd4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d4,cc */
+ {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {}
+};
+static const struct usb_action icm105a_NoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
+ {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */
+ {0xaa, 0x0e, 0x0081}, /* 00,0e,81,aa */
+ {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */
+ {0xaa, 0x1c, 0x0080}, /* 00,1c,80,aa */
+ {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
+ {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */
+ {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */
+ {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */
+ {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */
+ {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */
+ {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */
+ {0xa0, 0x80, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,80,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
+ {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,20,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+ {0xa0, 0xc1, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c1,cc */
+ {0xa0, 0xd4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d4,cc */
+ {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, /* 01,a7,00,cc */
+ {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */
+ {}
+};
+
+static const struct usb_action mc501cb_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 00,02,00,cc */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+ {0xa0, 0xd8, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d8,cc */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
+ {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */
+ {0xa0, 0xde, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,de,cc */
+ {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */
+ {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */
+ {0xa0, 0x33, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,33,cc */
+ {0xa0, 0x34, ZC3XX_R087_EXPTIMEMID}, /* 00,87,34,cc */
+ {0xa0, 0x35, ZC3XX_R088_EXPTIMELOW}, /* 00,88,35,cc */
+ {0xa0, 0xb0, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,b0,cc */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+ {0xaa, 0x01, 0x0001}, /* 00,01,01,aa */
+ {0xaa, 0x01, 0x0003}, /* 00,01,03,aa */
+ {0xaa, 0x01, 0x0001}, /* 00,01,01,aa */
+ {0xaa, 0x03, 0x0000}, /* 00,03,00,aa */
+ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
+ {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */
+ {0xaa, 0x12, 0x0000}, /* 00,12,00,aa */
+ {0xaa, 0x13, 0x0000}, /* 00,13,00,aa */
+ {0xaa, 0x14, 0x0000}, /* 00,14,00,aa */
+ {0xaa, 0x15, 0x0000}, /* 00,15,00,aa */
+ {0xaa, 0x16, 0x0000}, /* 00,16,00,aa */
+ {0xaa, 0x17, 0x0001}, /* 00,17,01,aa */
+ {0xaa, 0x18, 0x00de}, /* 00,18,de,aa */
+ {0xaa, 0x19, 0x0002}, /* 00,19,02,aa */
+ {0xaa, 0x1a, 0x0086}, /* 00,1a,86,aa */
+ {0xaa, 0x20, 0x00a8}, /* 00,20,a8,aa */
+ {0xaa, 0x22, 0x0000}, /* 00,22,00,aa */
+ {0xaa, 0x23, 0x0000}, /* 00,23,00,aa */
+ {0xaa, 0x24, 0x0000}, /* 00,24,00,aa */
+ {0xaa, 0x40, 0x0033}, /* 00,40,33,aa */
+ {0xaa, 0x41, 0x0077}, /* 00,41,77,aa */
+ {0xaa, 0x42, 0x0053}, /* 00,42,53,aa */
+ {0xaa, 0x43, 0x00b0}, /* 00,43,b0,aa */
+ {0xaa, 0x4b, 0x0001}, /* 00,4b,01,aa */
+ {0xaa, 0x72, 0x0020}, /* 00,72,20,aa */
+ {0xaa, 0x73, 0x0000}, /* 00,73,00,aa */
+ {0xaa, 0x80, 0x0000}, /* 00,80,00,aa */
+ {0xaa, 0x85, 0x0050}, /* 00,85,50,aa */
+ {0xaa, 0x91, 0x0070}, /* 00,91,70,aa */
+ {0xaa, 0x92, 0x0072}, /* 00,92,72,aa */
+ {0xaa, 0x03, 0x0001}, /* 00,03,01,aa */
+ {0xaa, 0x10, 0x00a0}, /* 00,10,a0,aa */
+ {0xaa, 0x11, 0x0001}, /* 00,11,01,aa */
+ {0xaa, 0x30, 0x0000}, /* 00,30,00,aa */
+ {0xaa, 0x60, 0x0000}, /* 00,60,00,aa */
+ {0xaa, 0xa0, 0x001a}, /* 00,a0,1a,aa */
+ {0xaa, 0xa1, 0x0000}, /* 00,a1,00,aa */
+ {0xaa, 0xa2, 0x003f}, /* 00,a2,3f,aa */
+ {0xaa, 0xa3, 0x0028}, /* 00,a3,28,aa */
+ {0xaa, 0xa4, 0x0010}, /* 00,a4,10,aa */
+ {0xaa, 0xa5, 0x0020}, /* 00,a5,20,aa */
+ {0xaa, 0xb1, 0x0044}, /* 00,b1,44,aa */
+ {0xaa, 0xd0, 0x0001}, /* 00,d0,01,aa */
+ {0xaa, 0xd1, 0x0085}, /* 00,d1,85,aa */
+ {0xaa, 0xd2, 0x0080}, /* 00,d2,80,aa */
+ {0xaa, 0xd3, 0x0080}, /* 00,d3,80,aa */
+ {0xaa, 0xd4, 0x0080}, /* 00,d4,80,aa */
+ {0xaa, 0xd5, 0x0080}, /* 00,d5,80,aa */
+ {0xaa, 0xc0, 0x00c3}, /* 00,c0,c3,aa */
+ {0xaa, 0xc2, 0x0044}, /* 00,c2,44,aa */
+ {0xaa, 0xc4, 0x0040}, /* 00,c4,40,aa */
+ {0xaa, 0xc5, 0x0020}, /* 00,c5,20,aa */
+ {0xaa, 0xc6, 0x0008}, /* 00,c6,08,aa */
+ {0xaa, 0x03, 0x0004}, /* 00,03,04,aa */
+ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
+ {0xaa, 0x40, 0x0030}, /* 00,40,30,aa */
+ {0xaa, 0x41, 0x0020}, /* 00,41,20,aa */
+ {0xaa, 0x42, 0x002d}, /* 00,42,2d,aa */
+ {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+ {0xaa, 0x1c, 0x0050}, /* 00,1C,50,aa */
+ {0xaa, 0x11, 0x0081}, /* 00,11,81,aa */
+ {0xaa, 0x3b, 0x001d}, /* 00,3b,1D,aa */
+ {0xaa, 0x3c, 0x004c}, /* 00,3c,4C,aa */
+ {0xaa, 0x3d, 0x0018}, /* 00,3d,18,aa */
+ {0xaa, 0x3e, 0x006a}, /* 00,3e,6A,aa */
+ {0xaa, 0x01, 0x0000}, /* 00,01,00,aa */
+ {0xaa, 0x52, 0x00ff}, /* 00,52,FF,aa */
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
+ {0xaa, 0x03, 0x0002}, /* 00,03,02,aa */
+ {0xaa, 0x51, 0x0027}, /* 00,51,27,aa */
+ {0xaa, 0x52, 0x0020}, /* 00,52,20,aa */
+ {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+ {0xaa, 0x50, 0x0010}, /* 00,50,10,aa */
+ {0xaa, 0x51, 0x0010}, /* 00,51,10,aa */
+ {0xaa, 0x54, 0x0010}, /* 00,54,10,aa */
+ {0xaa, 0x55, 0x0010}, /* 00,55,10,aa */
+ {0xa0, 0xf0, 0x0199}, /* 01,99,F0,cc */
+ {0xa0, 0x80, 0x019a}, /* 01,9A,80,cc */
+
+ {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+ {0xaa, 0x36, 0x001d}, /* 00,36,1D,aa */
+ {0xaa, 0x37, 0x004c}, /* 00,37,4C,aa */
+ {0xaa, 0x3b, 0x001d}, /* 00,3B,1D,aa */
+ {}
+};
+
+static const struct usb_action mc501cb_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+ {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d0,cc */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
+ {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */
+ {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,d8,cc */
+ {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */
+ {0xa0, 0x33, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,33,cc */
+ {0xa0, 0x34, ZC3XX_R087_EXPTIMEMID}, /* 00,87,34,cc */
+ {0xa0, 0x35, ZC3XX_R088_EXPTIMELOW}, /* 00,88,35,cc */
+ {0xa0, 0xb0, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,b0,cc */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+ {0xaa, 0x01, 0x0001}, /* 00,01,01,aa */
+ {0xaa, 0x01, 0x0003}, /* 00,01,03,aa */
+ {0xaa, 0x01, 0x0001}, /* 00,01,01,aa */
+ {0xaa, 0x03, 0x0000}, /* 00,03,00,aa */
+ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
+ {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */
+ {0xaa, 0x12, 0x0000}, /* 00,12,00,aa */
+ {0xaa, 0x13, 0x0000}, /* 00,13,00,aa */
+ {0xaa, 0x14, 0x0000}, /* 00,14,00,aa */
+ {0xaa, 0x15, 0x0000}, /* 00,15,00,aa */
+ {0xaa, 0x16, 0x0000}, /* 00,16,00,aa */
+ {0xaa, 0x17, 0x0001}, /* 00,17,01,aa */
+ {0xaa, 0x18, 0x00d8}, /* 00,18,d8,aa */
+ {0xaa, 0x19, 0x0002}, /* 00,19,02,aa */
+ {0xaa, 0x1a, 0x0088}, /* 00,1a,88,aa */
+ {0xaa, 0x20, 0x00a8}, /* 00,20,a8,aa */
+ {0xaa, 0x22, 0x0000}, /* 00,22,00,aa */
+ {0xaa, 0x23, 0x0000}, /* 00,23,00,aa */
+ {0xaa, 0x24, 0x0000}, /* 00,24,00,aa */
+ {0xaa, 0x40, 0x0033}, /* 00,40,33,aa */
+ {0xaa, 0x41, 0x0077}, /* 00,41,77,aa */
+ {0xaa, 0x42, 0x0053}, /* 00,42,53,aa */
+ {0xaa, 0x43, 0x00b0}, /* 00,43,b0,aa */
+ {0xaa, 0x4b, 0x0001}, /* 00,4b,01,aa */
+ {0xaa, 0x72, 0x0020}, /* 00,72,20,aa */
+ {0xaa, 0x73, 0x0000}, /* 00,73,00,aa */
+ {0xaa, 0x80, 0x0000}, /* 00,80,00,aa */
+ {0xaa, 0x85, 0x0050}, /* 00,85,50,aa */
+ {0xaa, 0x91, 0x0070}, /* 00,91,70,aa */
+ {0xaa, 0x92, 0x0072}, /* 00,92,72,aa */
+ {0xaa, 0x03, 0x0001}, /* 00,03,01,aa */
+ {0xaa, 0x10, 0x00a0}, /* 00,10,a0,aa */
+ {0xaa, 0x11, 0x0001}, /* 00,11,01,aa */
+ {0xaa, 0x30, 0x0000}, /* 00,30,00,aa */
+ {0xaa, 0x60, 0x0000}, /* 00,60,00,aa */
+ {0xaa, 0xa0, 0x001a}, /* 00,a0,1a,aa */
+ {0xaa, 0xa1, 0x0000}, /* 00,a1,00,aa */
+ {0xaa, 0xa2, 0x003f}, /* 00,a2,3f,aa */
+ {0xaa, 0xa3, 0x0028}, /* 00,a3,28,aa */
+ {0xaa, 0xa4, 0x0010}, /* 00,a4,10,aa */
+ {0xaa, 0xa5, 0x0020}, /* 00,a5,20,aa */
+ {0xaa, 0xb1, 0x0044}, /* 00,b1,44,aa */
+ {0xaa, 0xd0, 0x0001}, /* 00,d0,01,aa */
+ {0xaa, 0xd1, 0x0085}, /* 00,d1,85,aa */
+ {0xaa, 0xd2, 0x0080}, /* 00,d2,80,aa */
+ {0xaa, 0xd3, 0x0080}, /* 00,d3,80,aa */
+ {0xaa, 0xd4, 0x0080}, /* 00,d4,80,aa */
+ {0xaa, 0xd5, 0x0080}, /* 00,d5,80,aa */
+ {0xaa, 0xc0, 0x00c3}, /* 00,c0,c3,aa */
+ {0xaa, 0xc2, 0x0044}, /* 00,c2,44,aa */
+ {0xaa, 0xc4, 0x0040}, /* 00,c4,40,aa */
+ {0xaa, 0xc5, 0x0020}, /* 00,c5,20,aa */
+ {0xaa, 0xc6, 0x0008}, /* 00,c6,08,aa */
+ {0xaa, 0x03, 0x0004}, /* 00,03,04,aa */
+ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
+ {0xaa, 0x40, 0x0030}, /* 00,40,30,aa */
+ {0xaa, 0x41, 0x0020}, /* 00,41,20,aa */
+ {0xaa, 0x42, 0x002d}, /* 00,42,2d,aa */
+ {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+ {0xaa, 0x1c, 0x0050}, /* 00,1c,50,aa */
+ {0xaa, 0x11, 0x0081}, /* 00,11,81,aa */
+ {0xaa, 0x3b, 0x003a}, /* 00,3b,3A,aa */
+ {0xaa, 0x3c, 0x0098}, /* 00,3c,98,aa */
+ {0xaa, 0x3d, 0x0030}, /* 00,3d,30,aa */
+ {0xaa, 0x3e, 0x00d4}, /* 00,3E,D4,aa */
+ {0xaa, 0x01, 0x0000}, /* 00,01,00,aa */
+ {0xaa, 0x52, 0x00ff}, /* 00,52,FF,aa */
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
+ {0xaa, 0x03, 0x0002}, /* 00,03,02,aa */
+ {0xaa, 0x51, 0x004e}, /* 00,51,4E,aa */
+ {0xaa, 0x52, 0x0041}, /* 00,52,41,aa */
+ {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+ {0xaa, 0x50, 0x0010}, /* 00,50,10,aa */
+ {0xaa, 0x51, 0x0010}, /* 00,51,10,aa */
+ {0xaa, 0x54, 0x0010}, /* 00,54,10,aa */
+ {0xaa, 0x55, 0x0010}, /* 00,55,10,aa */
+ {0xa0, 0xf0, 0x0199}, /* 01,99,F0,cc */
+ {0xa0, 0x80, 0x019a}, /* 01,9A,80,cc */
+ {}
+};
+
+static const struct usb_action mc501cb_50HZ[] = {
+ {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+ {0xaa, 0x36, 0x001d}, /* 00,36,1D,aa */
+ {0xaa, 0x37, 0x004c}, /* 00,37,4C,aa */
+ {0xaa, 0x3b, 0x001d}, /* 00,3B,1D,aa */
+ {0xaa, 0x3c, 0x004c}, /* 00,3C,4C,aa */
+ {0xaa, 0x3d, 0x001d}, /* 00,3D,1D,aa */
+ {0xaa, 0x3e, 0x004c}, /* 00,3E,4C,aa */
+ {}
+};
+
+static const struct usb_action mc501cb_50HZScale[] = {
+ {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+ {0xaa, 0x36, 0x003a}, /* 00,36,3A,aa */
+ {0xaa, 0x37, 0x0098}, /* 00,37,98,aa */
+ {0xaa, 0x3b, 0x003a}, /* 00,3B,3A,aa */
+ {0xaa, 0x3c, 0x0098}, /* 00,3C,98,aa */
+ {0xaa, 0x3d, 0x003a}, /* 00,3D,3A,aa */
+ {0xaa, 0x3e, 0x0098}, /* 00,3E,98,aa */
+ {}
+};
+
+static const struct usb_action mc501cb_60HZ[] = {
+ {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+ {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */
+ {0xaa, 0x37, 0x006a}, /* 00,37,6A,aa */
+ {0xaa, 0x3d, 0x0018}, /* 00,3D,18,aa */
+ {0xaa, 0x3e, 0x006a}, /* 00,3E,6A,aa */
+ {0xaa, 0x3b, 0x0018}, /* 00,3B,18,aa */
+ {0xaa, 0x3c, 0x006a}, /* 00,3C,6A,aa */
+ {}
+};
+
+static const struct usb_action mc501cb_60HZScale[] = {
+ {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+ {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */
+ {0xaa, 0x37, 0x00d4}, /* 00,37,D4,aa */
+ {0xaa, 0x3d, 0x0030}, /* 00,3D,30,aa */
+ {0xaa, 0x3e, 0x00d4}, /* 00,3E,D4,aa */
+ {0xaa, 0x3b, 0x0030}, /* 00,3B,30,aa */
+ {0xaa, 0x3c, 0x00d4}, /* 00,3C,D4,aa */
+ {}
+};
+
+static const struct usb_action mc501cb_NoFliker[] = {
+ {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+ {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */
+ {0xaa, 0x37, 0x006a}, /* 00,37,6A,aa */
+ {0xaa, 0x3d, 0x0018}, /* 00,3D,18,aa */
+ {0xaa, 0x3e, 0x006a}, /* 00,3E,6A,aa */
+ {0xaa, 0x3b, 0x0018}, /* 00,3B,18,aa */
+ {0xaa, 0x3c, 0x006a}, /* 00,3C,6A,aa */
+ {}
+};
+
+static const struct usb_action mc501cb_NoFlikerScale[] = {
+ {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+ {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */
+ {0xaa, 0x37, 0x00d4}, /* 00,37,D4,aa */
+ {0xaa, 0x3d, 0x0030}, /* 00,3D,30,aa */
+ {0xaa, 0x3e, 0x00d4}, /* 00,3E,D4,aa */
+ {0xaa, 0x3b, 0x0030}, /* 00,3B,30,aa */
+ {0xaa, 0x3c, 0x00d4}, /* 00,3C,D4,aa */
+ {}
+};
+
+/* from zs211.inf */
+static const struct usb_action ov7620_Initial[] = { /* 640x480 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, /* 00,02,40,cc */
+ {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,06,cc */
+ {0xa0, 0x02, ZC3XX_R083_RGAINADDR}, /* 00,83,02,cc */
+ {0xa0, 0x01, ZC3XX_R085_BGAINADDR}, /* 00,85,01,cc */
+ {0xa0, 0x80, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,80,cc */
+ {0xa0, 0x81, ZC3XX_R087_EXPTIMEMID}, /* 00,87,81,cc */
+ {0xa0, 0x10, ZC3XX_R088_EXPTIMELOW}, /* 00,88,10,cc */
+ {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,a1,cc */
+ {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+ {0xa0, 0xd8, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d8,cc */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
+ {0xa0, 0xde, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,de,cc */
+ {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */
+ {0xaa, 0x12, 0x0088}, /* 00,12,88,aa */
+ {0xaa, 0x12, 0x0048}, /* 00,12,48,aa */
+ {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */
+ {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */
+ {0xaa, 0x04, 0x0000}, /* 00,04,00,aa */
+ {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */
+ {0xaa, 0x14, 0x0000}, /* 00,14,00,aa */
+ {0xaa, 0x15, 0x0004}, /* 00,15,04,aa */
+ {0xaa, 0x17, 0x0018}, /* 00,17,18,aa */
+ {0xaa, 0x18, 0x00ba}, /* 00,18,ba,aa */
+ {0xaa, 0x19, 0x0002}, /* 00,19,02,aa */
+ {0xaa, 0x1a, 0x00f1}, /* 00,1a,f1,aa */
+ {0xaa, 0x20, 0x0040}, /* 00,20,40,aa */
+ {0xaa, 0x24, 0x0088}, /* 00,24,88,aa */
+ {0xaa, 0x25, 0x0078}, /* 00,25,78,aa */
+ {0xaa, 0x27, 0x00f6}, /* 00,27,f6,aa */
+ {0xaa, 0x28, 0x00a0}, /* 00,28,a0,aa */
+ {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */
+ {0xaa, 0x2a, 0x0083}, /* 00,2a,83,aa */
+ {0xaa, 0x2b, 0x0096}, /* 00,2b,96,aa */
+ {0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */
+ {0xaa, 0x74, 0x0020}, /* 00,74,20,aa */
+ {0xaa, 0x61, 0x0068}, /* 00,61,68,aa */
+ {0xaa, 0x64, 0x0088}, /* 00,64,88,aa */
+ {0xaa, 0x00, 0x0000}, /* 00,00,00,aa */
+ {0xaa, 0x06, 0x0080}, /* 00,06,80,aa */
+ {0xaa, 0x01, 0x0090}, /* 00,01,90,aa */
+ {0xaa, 0x02, 0x0030}, /* 00,02,30,aa */
+ {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,77,cc */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+ {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x68, ZC3XX_R116_RGAIN}, /* 01,16,68,cc */
+ {0xa0, 0x52, ZC3XX_R118_BGAIN}, /* 01,18,52,cc */
+ {0xa0, 0x40, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,40,cc */
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
+ {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,50,cc */
+ {}
+};
+static const struct usb_action ov7620_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x50, ZC3XX_R002_CLOCKSELECT}, /* 00,02,50,cc */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */
+ /* mx change? */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,06,cc */
+ {0xa0, 0x02, ZC3XX_R083_RGAINADDR}, /* 00,83,02,cc */
+ {0xa0, 0x01, ZC3XX_R085_BGAINADDR}, /* 00,85,01,cc */
+ {0xa0, 0x80, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,80,cc */
+ {0xa0, 0x81, ZC3XX_R087_EXPTIMEMID}, /* 00,87,81,cc */
+ {0xa0, 0x10, ZC3XX_R088_EXPTIMELOW}, /* 00,88,10,cc */
+ {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,a1,cc */
+ {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+ {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d0,cc */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
+ {0xa0, 0xd6, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,d6,cc */
+ /* OV7648 00,9c,d8,cc */
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */
+ {0xaa, 0x12, 0x0088}, /* 00,12,88,aa */
+ {0xaa, 0x12, 0x0048}, /* 00,12,48,aa */
+ {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */
+ {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */
+ {0xaa, 0x04, 0x0000}, /* 00,04,00,aa */
+ {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */
+ {0xaa, 0x14, 0x0000}, /* 00,14,00,aa */
+ {0xaa, 0x15, 0x0004}, /* 00,15,04,aa */
+ {0xaa, 0x24, 0x0088}, /* 00,24,88,aa */
+ {0xaa, 0x25, 0x0078}, /* 00,25,78,aa */
+ {0xaa, 0x17, 0x0018}, /* 00,17,18,aa */
+ {0xaa, 0x18, 0x00ba}, /* 00,18,ba,aa */
+ {0xaa, 0x19, 0x0002}, /* 00,19,02,aa */
+ {0xaa, 0x1a, 0x00f2}, /* 00,1a,f2,aa */
+ {0xaa, 0x20, 0x0040}, /* 00,20,40,aa */
+ {0xaa, 0x27, 0x00f6}, /* 00,27,f6,aa */
+ {0xaa, 0x28, 0x00a0}, /* 00,28,a0,aa */
+ {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */
+ {0xaa, 0x2a, 0x0083}, /* 00,2a,83,aa */
+ {0xaa, 0x2b, 0x0096}, /* 00,2b,96,aa */
+ {0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */
+ {0xaa, 0x74, 0x0020}, /* 00,74,20,aa */
+ {0xaa, 0x61, 0x0068}, /* 00,61,68,aa */
+ {0xaa, 0x64, 0x0088}, /* 00,64,88,aa */
+ {0xaa, 0x00, 0x0000}, /* 00,00,00,aa */
+ {0xaa, 0x06, 0x0080}, /* 00,06,80,aa */
+ {0xaa, 0x01, 0x0090}, /* 00,01,90,aa */
+ {0xaa, 0x02, 0x0030}, /* 00,02,30,aa */
+ {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,77,cc */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+ {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x68, ZC3XX_R116_RGAIN}, /* 01,16,68,cc */
+ {0xa0, 0x52, ZC3XX_R118_BGAIN}, /* 01,18,52,cc */
+ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,50,cc */
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
+ {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,50,cc */
+ {}
+};
+static const struct usb_action ov7620_50HZ[] = {
+ {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */
+ {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */
+ {0xaa, 0x2b, 0x0096}, /* 00,2b,96,aa */
+ {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */
+ {0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
+ {0xa0, 0x18, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,18,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x83, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,83,cc */
+ {0xaa, 0x10, 0x0082}, /* 00,10,82,aa */
+ {0xaa, 0x76, 0x0003}, /* 00,76,03,aa */
+/* {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, * 00,02,40,cc
+ * if mode0 (640x480) */
+ {}
+};
+static const struct usb_action ov7620_60HZ[] = {
+ {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */
+ /* (bug in zs211.inf) */
+ {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */
+ {0xaa, 0x2b, 0x0000}, /* 00,2b,00,aa */
+ {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */
+ {0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
+ {0xa0, 0x18, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,18,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x83, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,83,cc */
+ {0xaa, 0x10, 0x0020}, /* 00,10,20,aa */
+ {0xaa, 0x76, 0x0003}, /* 00,76,03,aa */
+/* {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, * 00,02,40,cc
+ * if mode0 (640x480) */
+/* ?? in gspca v1, it was
+ {0xa0, 0x00, 0x0039}, * 00,00,00,dd *
+ {0xa1, 0x01, 0x0037}, */
+ {}
+};
+static const struct usb_action ov7620_NoFliker[] = {
+ {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */
+ /* (bug in zs211.inf) */
+ {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */
+ {0xaa, 0x2b, 0x0000}, /* 00,2b,00,aa */
+ {0xaa, 0x75, 0x008e}, /* 00,75,8e,aa */
+ {0xaa, 0x2d, 0x0001}, /* 00,2d,01,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
+ {0xa0, 0x18, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,18,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,01,cc */
+/* {0xa0, 0x44, ZC3XX_R002_CLOCKSELECT}, * 00,02,44,cc
+ * if mode1 (320x240) */
+/* ?? was
+ {0xa0, 0x00, 0x0039}, * 00,00,00,dd *
+ {0xa1, 0x01, 0x0037}, */
+ {}
+};
+
+static const struct usb_action ov7630c_InitialScale[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xaa, 0x12, 0x0080},
+ {0xa0, 0x02, ZC3XX_R083_RGAINADDR},
+ {0xa0, 0x01, ZC3XX_R085_BGAINADDR},
+ {0xa0, 0x90, ZC3XX_R086_EXPTIMEHIGH},
+ {0xa0, 0x91, ZC3XX_R087_EXPTIMEMID},
+ {0xa0, 0x10, ZC3XX_R088_EXPTIMELOW},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+ {0xaa, 0x12, 0x0069},
+ {0xaa, 0x04, 0x0020},
+ {0xaa, 0x06, 0x0050},
+ {0xaa, 0x13, 0x0083},
+ {0xaa, 0x14, 0x0000},
+ {0xaa, 0x15, 0x0024},
+ {0xaa, 0x17, 0x0018},
+ {0xaa, 0x18, 0x00ba},
+ {0xaa, 0x19, 0x0002},
+ {0xaa, 0x1a, 0x00f6},
+ {0xaa, 0x1b, 0x0002},
+ {0xaa, 0x20, 0x00c2},
+ {0xaa, 0x24, 0x0060},
+ {0xaa, 0x25, 0x0040},
+ {0xaa, 0x26, 0x0030},
+ {0xaa, 0x27, 0x00ea},
+ {0xaa, 0x28, 0x00a0},
+ {0xaa, 0x21, 0x0000},
+ {0xaa, 0x2a, 0x0081},
+ {0xaa, 0x2b, 0x0096},
+ {0xaa, 0x2d, 0x0094},
+ {0xaa, 0x2f, 0x003d},
+ {0xaa, 0x30, 0x0024},
+ {0xaa, 0x60, 0x0000},
+ {0xaa, 0x61, 0x0040},
+ {0xaa, 0x68, 0x007c},
+ {0xaa, 0x6f, 0x0015},
+ {0xaa, 0x75, 0x0088},
+ {0xaa, 0x77, 0x00b5},
+ {0xaa, 0x01, 0x0060},
+ {0xaa, 0x02, 0x0060},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x00, 0x01ad},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x60, ZC3XX_R116_RGAIN},
+ {0xa0, 0x46, ZC3XX_R118_BGAIN},
+ {0xa0, 0x04, ZC3XX_R113_RGB03},
+/* 0x10, */
+ {0xa1, 0x01, 0x0002},
+ {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
+ {0xa0, 0xf8, ZC3XX_R10B_RGB01},
+ {0xa0, 0xf8, ZC3XX_R10C_RGB02},
+ {0xa0, 0xf8, ZC3XX_R10D_RGB10},
+ {0xa0, 0x50, ZC3XX_R10E_RGB11},
+ {0xa0, 0xf8, ZC3XX_R10F_RGB12},
+ {0xa0, 0xf8, ZC3XX_R110_RGB20},
+ {0xa0, 0xf8, ZC3XX_R111_RGB21},
+ {0xa0, 0x50, ZC3XX_R112_RGB22},
+ {0xa1, 0x01, 0x0008},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
+ {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
+ {0xa1, 0x01, 0x01c8},
+ {0xa1, 0x01, 0x01c9},
+ {0xa1, 0x01, 0x01ca},
+ {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
+ {0xa0, 0x01, ZC3XX_R120_GAMMA00}, /* gamma 2 ?*/
+ {0xa0, 0x0c, ZC3XX_R121_GAMMA01},
+ {0xa0, 0x1f, ZC3XX_R122_GAMMA02},
+ {0xa0, 0x3a, ZC3XX_R123_GAMMA03},
+ {0xa0, 0x53, ZC3XX_R124_GAMMA04},
+ {0xa0, 0x6d, ZC3XX_R125_GAMMA05},
+ {0xa0, 0x85, ZC3XX_R126_GAMMA06},
+ {0xa0, 0x9c, ZC3XX_R127_GAMMA07},
+ {0xa0, 0xb0, ZC3XX_R128_GAMMA08},
+ {0xa0, 0xc2, ZC3XX_R129_GAMMA09},
+ {0xa0, 0xd1, ZC3XX_R12A_GAMMA0A},
+ {0xa0, 0xde, ZC3XX_R12B_GAMMA0B},
+ {0xa0, 0xe9, ZC3XX_R12C_GAMMA0C},
+ {0xa0, 0xf2, ZC3XX_R12D_GAMMA0D},
+ {0xa0, 0xf9, ZC3XX_R12E_GAMMA0E},
+ {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+ {0xa0, 0x05, ZC3XX_R130_GAMMA10},
+ {0xa0, 0x0f, ZC3XX_R131_GAMMA11},
+ {0xa0, 0x16, ZC3XX_R132_GAMMA12},
+ {0xa0, 0x1a, ZC3XX_R133_GAMMA13},
+ {0xa0, 0x19, ZC3XX_R134_GAMMA14},
+ {0xa0, 0x19, ZC3XX_R135_GAMMA15},
+ {0xa0, 0x17, ZC3XX_R136_GAMMA16},
+ {0xa0, 0x15, ZC3XX_R137_GAMMA17},
+ {0xa0, 0x12, ZC3XX_R138_GAMMA18},
+ {0xa0, 0x10, ZC3XX_R139_GAMMA19},
+ {0xa0, 0x0e, ZC3XX_R13A_GAMMA1A},
+ {0xa0, 0x0b, ZC3XX_R13B_GAMMA1B},
+ {0xa0, 0x09, ZC3XX_R13C_GAMMA1C},
+ {0xa0, 0x08, ZC3XX_R13D_GAMMA1D},
+ {0xa0, 0x06, ZC3XX_R13E_GAMMA1E},
+ {0xa0, 0x03, ZC3XX_R13F_GAMMA1F},
+ {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
+ {0xa0, 0xf8, ZC3XX_R10B_RGB01},
+ {0xa0, 0xf8, ZC3XX_R10C_RGB02},
+ {0xa0, 0xf8, ZC3XX_R10D_RGB10},
+ {0xa0, 0x50, ZC3XX_R10E_RGB11},
+ {0xa0, 0xf8, ZC3XX_R10F_RGB12},
+ {0xa0, 0xf8, ZC3XX_R110_RGB20},
+ {0xa0, 0xf8, ZC3XX_R111_RGB21},
+ {0xa0, 0x50, ZC3XX_R112_RGB22},
+
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xaa, 0x10, 0x001b},
+ {0xaa, 0x76, 0x0002},
+ {0xaa, 0x2a, 0x0081},
+ {0xaa, 0x2b, 0x0000},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x01, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xb8, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x37, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xaa, 0x13, 0x0083}, /* 40 */
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+
+static const struct usb_action ov7630c_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+
+ {0xaa, 0x12, 0x0080},
+ {0xa0, 0x02, ZC3XX_R083_RGAINADDR},
+ {0xa0, 0x01, ZC3XX_R085_BGAINADDR},
+ {0xa0, 0x90, ZC3XX_R086_EXPTIMEHIGH},
+ {0xa0, 0x91, ZC3XX_R087_EXPTIMEMID},
+ {0xa0, 0x10, ZC3XX_R088_EXPTIMELOW},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW},
+ {0xaa, 0x12, 0x0069}, /* i2c */
+ {0xaa, 0x04, 0x0020},
+ {0xaa, 0x06, 0x0050},
+ {0xaa, 0x13, 0x00c3},
+ {0xaa, 0x14, 0x0000},
+ {0xaa, 0x15, 0x0024},
+ {0xaa, 0x19, 0x0003},
+ {0xaa, 0x1a, 0x00f6},
+ {0xaa, 0x1b, 0x0002},
+ {0xaa, 0x20, 0x00c2},
+ {0xaa, 0x24, 0x0060},
+ {0xaa, 0x25, 0x0040},
+ {0xaa, 0x26, 0x0030},
+ {0xaa, 0x27, 0x00ea},
+ {0xaa, 0x28, 0x00a0},
+ {0xaa, 0x21, 0x0000},
+ {0xaa, 0x2a, 0x0081},
+ {0xaa, 0x2b, 0x0096},
+ {0xaa, 0x2d, 0x0084},
+ {0xaa, 0x2f, 0x003d},
+ {0xaa, 0x30, 0x0024},
+ {0xaa, 0x60, 0x0000},
+ {0xaa, 0x61, 0x0040},
+ {0xaa, 0x68, 0x007c},
+ {0xaa, 0x6f, 0x0015},
+ {0xaa, 0x75, 0x0088},
+ {0xaa, 0x77, 0x00b5},
+ {0xaa, 0x01, 0x0060},
+ {0xaa, 0x02, 0x0060},
+ {0xaa, 0x17, 0x0018},
+ {0xaa, 0x18, 0x00ba},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x00, 0x01ad},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x60, ZC3XX_R116_RGAIN},
+ {0xa0, 0x46, ZC3XX_R118_BGAIN},
+ {0xa0, 0x04, ZC3XX_R113_RGB03},
+
+ {0xa1, 0x01, 0x0002},
+ {0xa0, 0x4e, ZC3XX_R10A_RGB00}, /* matrix */
+ {0xa0, 0xfe, ZC3XX_R10B_RGB01},
+ {0xa0, 0xf4, ZC3XX_R10C_RGB02},
+ {0xa0, 0xf7, ZC3XX_R10D_RGB10},
+ {0xa0, 0x4d, ZC3XX_R10E_RGB11},
+ {0xa0, 0xfc, ZC3XX_R10F_RGB12},
+ {0xa0, 0x00, ZC3XX_R110_RGB20},
+ {0xa0, 0xf6, ZC3XX_R111_RGB21},
+ {0xa0, 0x4a, ZC3XX_R112_RGB22},
+
+ {0xa1, 0x01, 0x0008},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
+ {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
+ {0xa1, 0x01, 0x01c8},
+ {0xa1, 0x01, 0x01c9},
+ {0xa1, 0x01, 0x01ca},
+ {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
+ {0xa0, 0x16, ZC3XX_R120_GAMMA00}, /* gamma ~4 */
+ {0xa0, 0x3a, ZC3XX_R121_GAMMA01},
+ {0xa0, 0x5b, ZC3XX_R122_GAMMA02},
+ {0xa0, 0x7c, ZC3XX_R123_GAMMA03},
+ {0xa0, 0x94, ZC3XX_R124_GAMMA04},
+ {0xa0, 0xa9, ZC3XX_R125_GAMMA05},
+ {0xa0, 0xbb, ZC3XX_R126_GAMMA06},
+ {0xa0, 0xca, ZC3XX_R127_GAMMA07},
+ {0xa0, 0xd7, ZC3XX_R128_GAMMA08},
+ {0xa0, 0xe1, ZC3XX_R129_GAMMA09},
+ {0xa0, 0xea, ZC3XX_R12A_GAMMA0A},
+ {0xa0, 0xf1, ZC3XX_R12B_GAMMA0B},
+ {0xa0, 0xf7, ZC3XX_R12C_GAMMA0C},
+ {0xa0, 0xfc, ZC3XX_R12D_GAMMA0D},
+ {0xa0, 0xff, ZC3XX_R12E_GAMMA0E},
+ {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+ {0xa0, 0x20, ZC3XX_R130_GAMMA10},
+ {0xa0, 0x22, ZC3XX_R131_GAMMA11},
+ {0xa0, 0x20, ZC3XX_R132_GAMMA12},
+ {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
+ {0xa0, 0x16, ZC3XX_R134_GAMMA14},
+ {0xa0, 0x13, ZC3XX_R135_GAMMA15},
+ {0xa0, 0x10, ZC3XX_R136_GAMMA16},
+ {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
+ {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+ {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+ {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+ {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
+ {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
+ {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
+ {0xa0, 0x00, ZC3XX_R13E_GAMMA1E},
+ {0xa0, 0x01, ZC3XX_R13F_GAMMA1F},
+ {0xa0, 0x4e, ZC3XX_R10A_RGB00}, /* matrix */
+ {0xa0, 0xfe, ZC3XX_R10B_RGB01},
+ {0xa0, 0xf4, ZC3XX_R10C_RGB02},
+ {0xa0, 0xf7, ZC3XX_R10D_RGB10},
+ {0xa0, 0x4d, ZC3XX_R10E_RGB11},
+ {0xa0, 0xfc, ZC3XX_R10F_RGB12},
+ {0xa0, 0x00, ZC3XX_R110_RGB20},
+ {0xa0, 0xf6, ZC3XX_R111_RGB21},
+ {0xa0, 0x4a, ZC3XX_R112_RGB22},
+
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xaa, 0x10, 0x000d},
+ {0xaa, 0x76, 0x0002},
+ {0xaa, 0x2a, 0x0081},
+ {0xaa, 0x2b, 0x0000},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x00, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xd8, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x1b, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xaa, 0x13, 0x00c3},
+
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+
+static const struct usb_action pas106b_Initial_com[] = {
+/* Sream and Sensor specific */
+ {0xa1, 0x01, 0x0010}, /* CMOSSensorSelect */
+/* System */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* SystemControl */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* SystemControl */
+/* Picture size */
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* ClockSelect */
+ {0xa0, 0x03, 0x003a},
+ {0xa0, 0x0c, 0x003b},
+ {0xa0, 0x04, 0x0038},
+ {}
+};
+
+static const struct usb_action pas106b_InitialScale[] = { /* 176x144 */
+/* JPEG control */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+/* Sream and Sensor specific */
+ {0xa0, 0x0f, ZC3XX_R010_CMOSSENSORSELECT},
+/* Picture size */
+ {0xa0, 0x00, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0xb0, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x00, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0x90, ZC3XX_R006_FRAMEHEIGHTLOW},
+/* System */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+/* Sream and Sensor specific */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+/* Sensor Interface */
+ {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE},
+/* Window inside sensor array */
+ {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0x28, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x68, ZC3XX_R09E_WINWIDTHLOW},
+/* Init the sensor */
+ {0xaa, 0x02, 0x0004},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x09, 0x0005},
+ {0xaa, 0x0a, 0x0002},
+ {0xaa, 0x0b, 0x0002},
+ {0xaa, 0x0c, 0x0005},
+ {0xaa, 0x0d, 0x0000},
+ {0xaa, 0x0e, 0x0002},
+ {0xaa, 0x14, 0x0081},
+/* Other registers */
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+/* Frame retreiving */
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+/* Gains */
+ {0xa0, 0xa0, ZC3XX_R1A8_DIGITALGAIN},
+/* Unknown */
+ {0xa0, 0x00, 0x01ad},
+/* Sharpness */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+/* Other registers */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+/* Auto exposure and white balance */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+/*Dead pixels */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+/* EEPROM */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+/* JPEG control */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},
+ {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},
+/* Other registers */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+/* Auto exposure and white balance */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+/*Dead pixels */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+/* EEPROM */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+/* JPEG control */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},
+ {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},
+
+ {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */
+ {0xa0, 0xf4, ZC3XX_R10B_RGB01},
+ {0xa0, 0xf4, ZC3XX_R10C_RGB02},
+ {0xa0, 0xf4, ZC3XX_R10D_RGB10},
+ {0xa0, 0x58, ZC3XX_R10E_RGB11},
+ {0xa0, 0xf4, ZC3XX_R10F_RGB12},
+ {0xa0, 0xf4, ZC3XX_R110_RGB20},
+ {0xa0, 0xf4, ZC3XX_R111_RGB21},
+ {0xa0, 0x58, ZC3XX_R112_RGB22},
+/* Auto correction */
+ {0xa0, 0x03, ZC3XX_R181_WINXSTART},
+ {0xa0, 0x08, ZC3XX_R182_WINXWIDTH},
+ {0xa0, 0x16, ZC3XX_R183_WINXCENTER},
+ {0xa0, 0x03, ZC3XX_R184_WINYSTART},
+ {0xa0, 0x05, ZC3XX_R185_WINYWIDTH},
+ {0xa0, 0x14, ZC3XX_R186_WINYCENTER},
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+/* Auto exposure and white balance */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xb1, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x87, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+/* sensor on */
+ {0xaa, 0x07, 0x00b1},
+ {0xaa, 0x05, 0x0003},
+ {0xaa, 0x04, 0x0001},
+ {0xaa, 0x03, 0x003b},
+/* Gains */
+ {0xa0, 0x20, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xa0, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+/* Auto correction */
+ {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa1, 0x01, 0x0180}, /* AutoCorrectEnable */
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+/* Gains */
+ {0xa0, 0x40, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x40, ZC3XX_R118_BGAIN},
+ {}
+};
+
+static const struct usb_action pas106b_Initial[] = { /* 352x288 */
+/* JPEG control */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+/* Sream and Sensor specific */
+ {0xa0, 0x0f, ZC3XX_R010_CMOSSENSORSELECT},
+/* Picture size */
+ {0xa0, 0x01, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x60, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0x20, ZC3XX_R006_FRAMEHEIGHTLOW},
+/* System */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+/* Sream and Sensor specific */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+/* Sensor Interface */
+ {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE},
+/* Window inside sensor array */
+ {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0x28, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x68, ZC3XX_R09E_WINWIDTHLOW},
+/* Init the sensor */
+ {0xaa, 0x02, 0x0004},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x09, 0x0005},
+ {0xaa, 0x0a, 0x0002},
+ {0xaa, 0x0b, 0x0002},
+ {0xaa, 0x0c, 0x0005},
+ {0xaa, 0x0d, 0x0000},
+ {0xaa, 0x0e, 0x0002},
+ {0xaa, 0x14, 0x0081},
+/* Other registers */
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+/* Frame retreiving */
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+/* Gains */
+ {0xa0, 0xa0, ZC3XX_R1A8_DIGITALGAIN},
+/* Unknown */
+ {0xa0, 0x00, 0x01ad},
+/* Sharpness */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+/* Other registers */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+/* Auto exposure and white balance */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x80, ZC3XX_R18D_YTARGET},
+/*Dead pixels */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+/* EEPROM */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+/* JPEG control */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},
+ {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},
+/* Other registers */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+/* Auto exposure and white balance */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+/*Dead pixels */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+/* EEPROM */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+/* JPEG control */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},
+ {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},
+
+ {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */
+ {0xa0, 0xf4, ZC3XX_R10B_RGB01},
+ {0xa0, 0xf4, ZC3XX_R10C_RGB02},
+ {0xa0, 0xf4, ZC3XX_R10D_RGB10},
+ {0xa0, 0x58, ZC3XX_R10E_RGB11},
+ {0xa0, 0xf4, ZC3XX_R10F_RGB12},
+ {0xa0, 0xf4, ZC3XX_R110_RGB20},
+ {0xa0, 0xf4, ZC3XX_R111_RGB21},
+ {0xa0, 0x58, ZC3XX_R112_RGB22},
+/* Auto correction */
+ {0xa0, 0x03, ZC3XX_R181_WINXSTART},
+ {0xa0, 0x08, ZC3XX_R182_WINXWIDTH},
+ {0xa0, 0x16, ZC3XX_R183_WINXCENTER},
+ {0xa0, 0x03, ZC3XX_R184_WINYSTART},
+ {0xa0, 0x05, ZC3XX_R185_WINYWIDTH},
+ {0xa0, 0x14, ZC3XX_R186_WINYCENTER},
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+
+/* Auto exposure and white balance */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xb1, ZC3XX_R192_EXPOSURELIMITLOW},
+
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x87, ZC3XX_R197_ANTIFLICKERLOW},
+
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+/* sensor on */
+ {0xaa, 0x07, 0x00b1},
+ {0xaa, 0x05, 0x0003},
+ {0xaa, 0x04, 0x0001},
+ {0xaa, 0x03, 0x003b},
+/* Gains */
+ {0xa0, 0x20, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+/* Auto correction */
+ {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa1, 0x01, 0x0180}, /* AutoCorrectEnable */
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+/* Gains */
+ {0xa0, 0x40, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x40, ZC3XX_R118_BGAIN},
+
+ {0xa0, 0x00, 0x0007}, /* AutoCorrectEnable */
+ {0xa0, 0xff, ZC3XX_R018_FRAMELOST}, /* Frame adjust */
+ {}
+};
+static const struct usb_action pas106b_50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,06,cc */
+ {0xa0, 0x54, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,54,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x87, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,87,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x30, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,30,cc */
+ {0xaa, 0x03, 0x0021}, /* 00,03,21,aa */
+ {0xaa, 0x04, 0x000c}, /* 00,04,0c,aa */
+ {0xaa, 0x05, 0x0002}, /* 00,05,02,aa */
+ {0xaa, 0x07, 0x001c}, /* 00,07,1c,aa */
+ {0xa0, 0x04, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,04,cc */
+ {}
+};
+static const struct usb_action pas106b_60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,06,cc */
+ {0xa0, 0x2e, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,2e,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x71, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,71,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x30, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,30,cc */
+ {0xaa, 0x03, 0x001c}, /* 00,03,1c,aa */
+ {0xaa, 0x04, 0x0004}, /* 00,04,04,aa */
+ {0xaa, 0x05, 0x0001}, /* 00,05,01,aa */
+ {0xaa, 0x07, 0x00c4}, /* 00,07,c4,aa */
+ {0xa0, 0x04, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,04,cc */
+ {}
+};
+static const struct usb_action pas106b_NoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,06,cc */
+ {0xa0, 0x50, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,50,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xaa, 0x03, 0x0013}, /* 00,03,13,aa */
+ {0xaa, 0x04, 0x0000}, /* 00,04,00,aa */
+ {0xaa, 0x05, 0x0001}, /* 00,05,01,aa */
+ {0xaa, 0x07, 0x0030}, /* 00,07,30,aa */
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {}
+};
+
+/* from lvWIMv.inf 046d:08a2/:08aa 2007/06/03 */
+static const struct usb_action pas202b_Initial[] = { /* 640x480 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0e,cc */
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 00,02,00,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+ {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+ {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,03,cc */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+ {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,03,cc */
+ {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */
+ {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e6,cc */
+ {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */
+ {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */
+ {0xaa, 0x02, 0x0002}, /* 00,02,04,aa --> 02 */
+ {0xaa, 0x07, 0x0006}, /* 00,07,06,aa */
+ {0xaa, 0x08, 0x0002}, /* 00,08,02,aa */
+ {0xaa, 0x09, 0x0006}, /* 00,09,06,aa */
+ {0xaa, 0x0a, 0x0001}, /* 00,0a,01,aa */
+ {0xaa, 0x0b, 0x0001}, /* 00,0b,01,aa */
+ {0xaa, 0x0c, 0x0006},
+ {0xaa, 0x0d, 0x0000}, /* 00,0d,00,aa */
+ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
+ {0xaa, 0x12, 0x0005}, /* 00,12,05,aa */
+ {0xaa, 0x13, 0x0063}, /* 00,13,63,aa */
+ {0xaa, 0x15, 0x0070}, /* 00,15,70,aa */
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+ {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x70, ZC3XX_R18D_YTARGET}, /* 01,8d,70,cc */
+ {}
+};
+static const struct usb_action pas202b_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0e,cc */
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+ {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */
+ {0xa0, 0x08, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,08,cc */
+ {0xa0, 0x02, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,02,cc */
+ {0xa0, 0x08, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,08,cc */
+ {0xa0, 0x02, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,02,cc */
+ {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */
+ {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */
+ {0xaa, 0x02, 0x0002}, /* 00,02,02,aa */
+ {0xaa, 0x07, 0x0006}, /* 00,07,06,aa */
+ {0xaa, 0x08, 0x0002}, /* 00,08,02,aa */
+ {0xaa, 0x09, 0x0006}, /* 00,09,06,aa */
+ {0xaa, 0x0a, 0x0001}, /* 00,0a,01,aa */
+ {0xaa, 0x0b, 0x0001}, /* 00,0b,01,aa */
+ {0xaa, 0x0c, 0x0006},
+ {0xaa, 0x0d, 0x0000}, /* 00,0d,00,aa */
+ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
+ {0xaa, 0x12, 0x0005}, /* 00,12,05,aa */
+ {0xaa, 0x13, 0x0063}, /* 00,13,63,aa */
+ {0xaa, 0x15, 0x0070}, /* 00,15,70,aa */
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+ {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x70, ZC3XX_R18D_YTARGET}, /* 01,8d,70,cc */
+ {0xa0, 0xff, ZC3XX_R097_WINYSTARTHIGH},
+ {0xa0, 0xfe, ZC3XX_R098_WINYSTARTLOW},
+ {}
+};
+static const struct usb_action pas202b_50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+ {0xaa, 0x21, 0x001b},
+ {0xaa, 0x03, 0x0044}, /* 00,03,44,aa */
+ {0xaa, 0x04, 0x0008},
+ {0xaa, 0x05, 0x001b},
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x1b, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x4d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,4d,cc */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x44, ZC3XX_R01D_HSYNC_0}, /* 00,1d,44,cc */
+ {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */
+ {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ad,cc */
+ {0xa0, 0xeb, ZC3XX_R020_HSYNC_3}, /* 00,20,eb,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+static const struct usb_action pas202b_50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0004},
+ {0xaa, 0x21, 0x003d},
+ {0xaa, 0x03, 0x0041}, /* 00,03,41,aa */
+ {0xaa, 0x04, 0x0010},
+ {0xaa, 0x05, 0x003d},
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x3d, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x9b, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,9b,cc */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x41, ZC3XX_R01D_HSYNC_0}, /* 00,1d,41,cc */
+ {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */
+ {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ad,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+static const struct usb_action pas202b_60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+ {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */
+ {0xaa, 0x03, 0x0045}, /* 00,03,45,aa */
+ {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */
+ {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x40, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,40,cc */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x45, ZC3XX_R01D_HSYNC_0}, /* 00,1d,45,cc */
+ {0xa0, 0x8e, ZC3XX_R01E_HSYNC_1}, /* 00,1e,8e,cc */
+ {0xa0, 0xc1, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c1,cc */
+ {0xa0, 0xf5, ZC3XX_R020_HSYNC_3}, /* 00,20,f5,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+static const struct usb_action pas202b_60HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0004},
+ {0xaa, 0x21, 0x0008},
+ {0xaa, 0x03, 0x0042}, /* 00,03,42,aa */
+ {0xaa, 0x04, 0x0010},
+ {0xaa, 0x05, 0x0008},
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x08, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,81,cc */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x42, ZC3XX_R01D_HSYNC_0}, /* 00,1d,42,cc */
+ {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */
+ {0xa0, 0xaf, ZC3XX_R01F_HSYNC_2}, /* 00,1f,af,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+static const struct usb_action pas202b_NoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+ {0xaa, 0x21, 0x0006},
+ {0xaa, 0x03, 0x0040}, /* 00,03,40,aa */
+ {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */
+ {0xaa, 0x05, 0x0006},
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x06, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x40, ZC3XX_R01D_HSYNC_0}, /* 00,1d,40,cc */
+ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, /* 00,1e,60,cc */
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+static const struct usb_action pas202b_NoFlikerScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0004},
+ {0xaa, 0x21, 0x000c},
+ {0xaa, 0x03, 0x0040}, /* 00,03,40,aa */
+ {0xaa, 0x04, 0x0010},
+ {0xaa, 0x05, 0x000c},
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x0c, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x02, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,02,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x40, ZC3XX_R01D_HSYNC_0}, /* 00,1d,40,cc */
+ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, /* 00,1e,60,cc */
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+
+/* mt9v111 (mi0360soc) and pb0330 from vm30x.inf 0ac8:301b 07/02/13 */
+static const struct usb_action mt9v111_1_Initial[] = { /* 640x480 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xdd, 0x00, 0x0200},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xaa, 0x01, 0x0001},
+ {0xaa, 0x06, 0x0000},
+ {0xaa, 0x08, 0x0483},
+ {0xaa, 0x01, 0x0004},
+ {0xaa, 0x08, 0x0006},
+ {0xaa, 0x02, 0x0011},
+ {0xaa, 0x03, 0x01e5}, /*jfm: was 01e7*/
+ {0xaa, 0x04, 0x0285}, /*jfm: was 0287*/
+ {0xaa, 0x07, 0x3002},
+ {0xaa, 0x20, 0x5100},
+ {0xaa, 0x35, 0x507f},
+ {0xaa, 0x30, 0x0005},
+ {0xaa, 0x31, 0x0000},
+ {0xaa, 0x58, 0x0078},
+ {0xaa, 0x62, 0x0411},
+ {0xaa, 0x2b, 0x007f},
+ {0xaa, 0x2c, 0x007f}, /*jfm: was 0030*/
+ {0xaa, 0x2d, 0x007f}, /*jfm: was 0030*/
+ {0xaa, 0x2e, 0x007f}, /*jfm: was 0030*/
+ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x09, 0x01ad}, /*jfm: was 00*/
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x6c, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x61, ZC3XX_R116_RGAIN},
+ {0xa0, 0x65, ZC3XX_R118_BGAIN},
+ {}
+};
+static const struct usb_action mt9v111_1_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xdd, 0x00, 0x0200},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xaa, 0x01, 0x0001},
+ {0xaa, 0x06, 0x0000},
+ {0xaa, 0x08, 0x0483},
+ {0xaa, 0x01, 0x0004},
+ {0xaa, 0x08, 0x0006},
+ {0xaa, 0x02, 0x0011},
+ {0xaa, 0x03, 0x01e7},
+ {0xaa, 0x04, 0x0287},
+ {0xaa, 0x07, 0x3002},
+ {0xaa, 0x20, 0x5100},
+ {0xaa, 0x35, 0x007f}, /*jfm: was 0050*/
+ {0xaa, 0x30, 0x0005},
+ {0xaa, 0x31, 0x0000},
+ {0xaa, 0x58, 0x0078},
+ {0xaa, 0x62, 0x0411},
+ {0xaa, 0x2b, 0x007f}, /*jfm: was 28*/
+ {0xaa, 0x2c, 0x007f}, /*jfm: was 30*/
+ {0xaa, 0x2d, 0x007f}, /*jfm: was 30*/
+ {0xaa, 0x2e, 0x007f}, /*jfm: was 28*/
+ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x09, 0x01ad}, /*jfm: was 00*/
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x6c, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x61, ZC3XX_R116_RGAIN},
+ {0xa0, 0x65, ZC3XX_R118_BGAIN},
+ {}
+};
+static const struct usb_action mt9v111_1_AE50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0562},
+ {0xbb, 0x01, 0x09aa},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x9b, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x62, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mt9v111_1_AE50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0509},
+ {0xbb, 0x01, 0x0934},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mt9v111_1_AE60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x05, 0x003d},
+ {0xaa, 0x09, 0x016e},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xdd, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x62, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mt9v111_1_AE60HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0509},
+ {0xbb, 0x01, 0x0983},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mt9v111_1_AENoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0509},
+ {0xbb, 0x01, 0x0960},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x09, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mt9v111_1_AENoFlikerScale[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0534},
+ {0xbb, 0x02, 0x0960},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x34, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+/* from usbvm303.inf 0ac8:303b 07/03/25 (3 - tas5130c) */
+static const struct usb_action mt9v111_3_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xdd, 0x00, 0x0200},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xaa, 0x01, 0x0001}, /* select IFP/SOC registers */
+ {0xaa, 0x06, 0x0000}, /* operating mode control */
+ {0xaa, 0x08, 0x0483}, /* output format control */
+ /* H red first, V red or blue first,
+ * raw Bayer, auto flicker */
+ {0xaa, 0x01, 0x0004}, /* select sensor core registers */
+ {0xaa, 0x08, 0x0006}, /* row start */
+ {0xaa, 0x02, 0x0011}, /* column start */
+ {0xaa, 0x03, 0x01e5}, /* window height - 1 */
+ {0xaa, 0x04, 0x0285}, /* window width - 1 */
+ {0xaa, 0x07, 0x3002}, /* output control */
+ {0xaa, 0x20, 0x1100}, /* read mode: bits 8 & 12 (?) */
+ {0xaa, 0x35, 0x007f}, /* global gain */
+ {0xaa, 0x30, 0x0005},
+ {0xaa, 0x31, 0x0000},
+ {0xaa, 0x58, 0x0078},
+ {0xaa, 0x62, 0x0411},
+ {0xaa, 0x2b, 0x007f}, /* green1 gain */
+ {0xaa, 0x2c, 0x007f}, /* blue gain */
+ {0xaa, 0x2d, 0x007f}, /* red gain */
+ {0xaa, 0x2e, 0x007f}, /* green2 gain */
+ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x00, 0x01ad},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x80, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x61, ZC3XX_R116_RGAIN},
+ {0xa0, 0x65, ZC3XX_R118_BGAIN},
+ {}
+};
+static const struct usb_action mt9v111_3_InitialScale[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xdd, 0x00, 0x0200},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xaa, 0x01, 0x0001},
+ {0xaa, 0x06, 0x0000},
+ {0xaa, 0x08, 0x0483},
+ {0xaa, 0x01, 0x0004},
+ {0xaa, 0x08, 0x0006},
+ {0xaa, 0x02, 0x0011},
+ {0xaa, 0x03, 0x01e7},
+ {0xaa, 0x04, 0x0287},
+ {0xaa, 0x07, 0x3002},
+ {0xaa, 0x20, 0x1100},
+ {0xaa, 0x35, 0x007f},
+ {0xaa, 0x30, 0x0005},
+ {0xaa, 0x31, 0x0000},
+ {0xaa, 0x58, 0x0078},
+ {0xaa, 0x62, 0x0411},
+ {0xaa, 0x2b, 0x007f},
+ {0xaa, 0x2c, 0x007f},
+ {0xaa, 0x2d, 0x007f},
+ {0xaa, 0x2e, 0x007f},
+ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x00, 0x01ad},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x80, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x61, ZC3XX_R116_RGAIN},
+ {0xa0, 0x65, ZC3XX_R118_BGAIN},
+ {}
+};
+static const struct usb_action mt9v111_3_AE50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x05, 0x0009}, /* horizontal blanking */
+ {0xaa, 0x09, 0x01ce}, /* shutter width */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mt9v111_3_AE50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x05, 0x0009},
+ {0xaa, 0x09, 0x01ce},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mt9v111_3_AE60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x05, 0x0009},
+ {0xaa, 0x09, 0x0083},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mt9v111_3_AE60HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x05, 0x0009},
+ {0xaa, 0x09, 0x0083},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mt9v111_3_AENoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x05, 0x0034},
+ {0xaa, 0x09, 0x0260},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x34, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mt9v111_3_AENoFlikerScale[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x05, 0x0034},
+ {0xaa, 0x09, 0x0260},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x34, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+
+static const struct usb_action pb0330_Initial[] = { /* 640x480 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xdd, 0x00, 0x0200},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xaa, 0x01, 0x0006},
+ {0xaa, 0x02, 0x0011},
+ {0xaa, 0x03, 0x01e5}, /*jfm: was 1e7*/
+ {0xaa, 0x04, 0x0285}, /*jfm: was 0287*/
+ {0xaa, 0x06, 0x0003},
+ {0xaa, 0x07, 0x3002},
+ {0xaa, 0x20, 0x1100},
+ {0xaa, 0x2f, 0xf7b0},
+ {0xaa, 0x30, 0x0005},
+ {0xaa, 0x31, 0x0000},
+ {0xaa, 0x34, 0x0100},
+ {0xaa, 0x35, 0x0060},
+ {0xaa, 0x3d, 0x068f},
+ {0xaa, 0x40, 0x01e0},
+ {0xaa, 0x58, 0x0078},
+ {0xaa, 0x62, 0x0411},
+ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x09, 0x01ad}, /*jfm: was 00 */
+ {0xa0, 0x15, 0x01ae},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x78, ZC3XX_R18D_YTARGET}, /*jfm: was 6c*/
+ {}
+};
+static const struct usb_action pb0330_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xdd, 0x00, 0x0200},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xaa, 0x01, 0x0006},
+ {0xaa, 0x02, 0x0011},
+ {0xaa, 0x03, 0x01e7},
+ {0xaa, 0x04, 0x0287},
+ {0xaa, 0x06, 0x0003},
+ {0xaa, 0x07, 0x3002},
+ {0xaa, 0x20, 0x1100},
+ {0xaa, 0x2f, 0xf7b0},
+ {0xaa, 0x30, 0x0005},
+ {0xaa, 0x31, 0x0000},
+ {0xaa, 0x34, 0x0100},
+ {0xaa, 0x35, 0x0060},
+ {0xaa, 0x3d, 0x068f},
+ {0xaa, 0x40, 0x01e0},
+ {0xaa, 0x58, 0x0078},
+ {0xaa, 0x62, 0x0411},
+ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x09, 0x01ad},
+ {0xa0, 0x15, 0x01ae},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x78, ZC3XX_R18D_YTARGET}, /*jfm: was 6c*/
+ {}
+};
+static const struct usb_action pb0330_50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x055c},
+ {0xbb, 0x01, 0x09aa},
+ {0xbb, 0x00, 0x1001},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xc4, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x5c, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action pb0330_50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0566},
+ {0xbb, 0x02, 0x09b2},
+ {0xbb, 0x00, 0x1002},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x8c, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x8a, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action pb0330_60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0535},
+ {0xbb, 0x01, 0x0974},
+ {0xbb, 0x00, 0x1001},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xfe, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x35, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x50, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xd0, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action pb0330_60HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0535},
+ {0xbb, 0x02, 0x096c},
+ {0xbb, 0x00, 0x1002},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xc0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x7c, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x35, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x50, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xd0, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action pb0330_NoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0509},
+ {0xbb, 0x02, 0x0940},
+ {0xbb, 0x00, 0x1002},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x09, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action pb0330_NoFlikerScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0535},
+ {0xbb, 0x01, 0x0980},
+ {0xbb, 0x00, 0x1001},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x35, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+ {}
+};
+
+/* from oem9.inf */
+static const struct usb_action po2030_Initial[] = { /* 640x480 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x04, ZC3XX_R002_CLOCKSELECT}, /* 00,02,04,cc */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x04, ZC3XX_R080_HBLANKHIGH}, /* 00,80,04,cc */
+ {0xa0, 0x05, ZC3XX_R081_HBLANKLOW}, /* 00,81,05,cc */
+ {0xa0, 0x16, ZC3XX_R083_RGAINADDR}, /* 00,83,16,cc */
+ {0xa0, 0x18, ZC3XX_R085_BGAINADDR}, /* 00,85,18,cc */
+ {0xa0, 0x1a, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,1a,cc */
+ {0xa0, 0x1b, ZC3XX_R087_EXPTIMEMID}, /* 00,87,1b,cc */
+ {0xa0, 0x1c, ZC3XX_R088_EXPTIMELOW}, /* 00,88,1c,cc */
+ {0xa0, 0xee, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,ee,cc */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */
+ {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
+ {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e6,cc */
+ {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */
+ {0xaa, 0x09, 0x00ce}, /* 00,09,ce,aa */
+ {0xaa, 0x0b, 0x0005}, /* 00,0b,05,aa */
+ {0xaa, 0x0d, 0x0054}, /* 00,0d,54,aa */
+ {0xaa, 0x0f, 0x00eb}, /* 00,0f,eb,aa */
+ {0xaa, 0x87, 0x0000}, /* 00,87,00,aa */
+ {0xaa, 0x88, 0x0004}, /* 00,88,04,aa */
+ {0xaa, 0x89, 0x0000}, /* 00,89,00,aa */
+ {0xaa, 0x8a, 0x0005}, /* 00,8a,05,aa */
+ {0xaa, 0x13, 0x0003}, /* 00,13,03,aa */
+ {0xaa, 0x16, 0x0040}, /* 00,16,40,aa */
+ {0xaa, 0x18, 0x0040}, /* 00,18,40,aa */
+ {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
+ {0xaa, 0x29, 0x00e8}, /* 00,29,e8,aa */
+ {0xaa, 0x45, 0x0045}, /* 00,45,45,aa */
+ {0xaa, 0x50, 0x00ed}, /* 00,50,ed,aa */
+ {0xaa, 0x51, 0x0025}, /* 00,51,25,aa */
+ {0xaa, 0x52, 0x0042}, /* 00,52,42,aa */
+ {0xaa, 0x53, 0x002f}, /* 00,53,2f,aa */
+ {0xaa, 0x79, 0x0025}, /* 00,79,25,aa */
+ {0xaa, 0x7b, 0x0000}, /* 00,7b,00,aa */
+ {0xaa, 0x7e, 0x0025}, /* 00,7e,25,aa */
+ {0xaa, 0x7f, 0x0025}, /* 00,7f,25,aa */
+ {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */
+ {0xaa, 0x33, 0x0036}, /* 00,33,36,aa */
+ {0xaa, 0x36, 0x0060}, /* 00,36,60,aa */
+ {0xaa, 0x37, 0x0008}, /* 00,37,08,aa */
+ {0xaa, 0x3b, 0x0031}, /* 00,3b,31,aa */
+ {0xaa, 0x44, 0x000f}, /* 00,44,0f,aa */
+ {0xaa, 0x58, 0x0002}, /* 00,58,02,aa */
+ {0xaa, 0x66, 0x00c0}, /* 00,66,c0,aa */
+ {0xaa, 0x67, 0x0044}, /* 00,67,44,aa */
+ {0xaa, 0x6b, 0x00a0}, /* 00,6b,a0,aa */
+ {0xaa, 0x6c, 0x0054}, /* 00,6c,54,aa */
+ {0xaa, 0xd6, 0x0007}, /* 00,d6,07,aa */
+ {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,f7,cc */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+ {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x7a, ZC3XX_R116_RGAIN}, /* 01,16,7a,cc */
+ {0xa0, 0x4a, ZC3XX_R118_BGAIN}, /* 01,18,4a,cc */
+ {}
+};
+
+/* from oem9.inf */
+static const struct usb_action po2030_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x04, ZC3XX_R080_HBLANKHIGH}, /* 00,80,04,cc */
+ {0xa0, 0x05, ZC3XX_R081_HBLANKLOW}, /* 00,81,05,cc */
+ {0xa0, 0x16, ZC3XX_R083_RGAINADDR}, /* 00,83,16,cc */
+ {0xa0, 0x18, ZC3XX_R085_BGAINADDR}, /* 00,85,18,cc */
+ {0xa0, 0x1a, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,1a,cc */
+ {0xa0, 0x1b, ZC3XX_R087_EXPTIMEMID}, /* 00,87,1b,cc */
+ {0xa0, 0x1c, ZC3XX_R088_EXPTIMELOW}, /* 00,88,1c,cc */
+ {0xa0, 0xee, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,ee,cc */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */
+ {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
+ {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e8,cc */
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */
+ {0xaa, 0x09, 0x00cc}, /* 00,09,cc,aa */
+ {0xaa, 0x0b, 0x0005}, /* 00,0b,05,aa */
+ {0xaa, 0x0d, 0x0058}, /* 00,0d,58,aa */
+ {0xaa, 0x0f, 0x00ed}, /* 00,0f,ed,aa */
+ {0xaa, 0x87, 0x0000}, /* 00,87,00,aa */
+ {0xaa, 0x88, 0x0004}, /* 00,88,04,aa */
+ {0xaa, 0x89, 0x0000}, /* 00,89,00,aa */
+ {0xaa, 0x8a, 0x0005}, /* 00,8a,05,aa */
+ {0xaa, 0x13, 0x0003}, /* 00,13,03,aa */
+ {0xaa, 0x16, 0x0040}, /* 00,16,40,aa */
+ {0xaa, 0x18, 0x0040}, /* 00,18,40,aa */
+ {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
+ {0xaa, 0x29, 0x00e8}, /* 00,29,e8,aa */
+ {0xaa, 0x45, 0x0045}, /* 00,45,45,aa */
+ {0xaa, 0x50, 0x00ed}, /* 00,50,ed,aa */
+ {0xaa, 0x51, 0x0025}, /* 00,51,25,aa */
+ {0xaa, 0x52, 0x0042}, /* 00,52,42,aa */
+ {0xaa, 0x53, 0x002f}, /* 00,53,2f,aa */
+ {0xaa, 0x79, 0x0025}, /* 00,79,25,aa */
+ {0xaa, 0x7b, 0x0000}, /* 00,7b,00,aa */
+ {0xaa, 0x7e, 0x0025}, /* 00,7e,25,aa */
+ {0xaa, 0x7f, 0x0025}, /* 00,7f,25,aa */
+ {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */
+ {0xaa, 0x33, 0x0036}, /* 00,33,36,aa */
+ {0xaa, 0x36, 0x0060}, /* 00,36,60,aa */
+ {0xaa, 0x37, 0x0008}, /* 00,37,08,aa */
+ {0xaa, 0x3b, 0x0031}, /* 00,3b,31,aa */
+ {0xaa, 0x44, 0x000f}, /* 00,44,0f,aa */
+ {0xaa, 0x58, 0x0002}, /* 00,58,02,aa */
+ {0xaa, 0x66, 0x00c0}, /* 00,66,c0,aa */
+ {0xaa, 0x67, 0x0044}, /* 00,67,44,aa */
+ {0xaa, 0x6b, 0x00a0}, /* 00,6b,a0,aa */
+ {0xaa, 0x6c, 0x0054}, /* 00,6c,54,aa */
+ {0xaa, 0xd6, 0x0007}, /* 00,d6,07,aa */
+ {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,f7,cc */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+ {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x7a, ZC3XX_R116_RGAIN}, /* 01,16,7a,cc */
+ {0xa0, 0x4a, ZC3XX_R118_BGAIN}, /* 01,18,4a,cc */
+ {}
+};
+
+static const struct usb_action po2030_50HZ[] = {
+ {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */
+ {0xaa, 0x1a, 0x0001}, /* 00,1a,01,aa */
+ {0xaa, 0x1b, 0x000a}, /* 00,1b,0a,aa */
+ {0xaa, 0x1c, 0x00b0}, /* 00,1c,b0,aa */
+ {0xa0, 0x05, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,05,cc */
+ {0xa0, 0x35, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,35,cc */
+ {0xa0, 0x70, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,70,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x85, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,85,cc */
+ {0xa0, 0x58, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,58,cc */
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+ {0xa0, 0x22, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,22,cc */
+ {0xa0, 0x88, ZC3XX_R18D_YTARGET}, /* 01,8d,88,cc */
+ {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc */
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */
+ {}
+};
+
+static const struct usb_action po2030_60HZ[] = {
+ {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */
+ {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */
+ {0xaa, 0x1b, 0x00de}, /* 00,1b,de,aa */
+ {0xaa, 0x1c, 0x0040}, /* 00,1c,40,aa */
+ {0xa0, 0x08, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,08,cc */
+ {0xa0, 0xae, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,ae,cc */
+ {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x6f, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,6f,cc */
+ {0xa0, 0x20, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,20,cc */
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+ {0xa0, 0x22, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,22,cc */
+ {0xa0, 0x88, ZC3XX_R18D_YTARGET}, /* 01,8d,88,cc */
+ /* win: 01,8d,80 */
+ {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc */
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */
+ {}
+};
+
+static const struct usb_action po2030_NoFliker[] = {
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
+ {0xaa, 0x8d, 0x000d}, /* 00,8d,0d,aa */
+ {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */
+ {0xaa, 0x1b, 0x0002}, /* 00,1b,02,aa */
+ {0xaa, 0x1c, 0x0078}, /* 00,1c,78,aa */
+ {0xaa, 0x46, 0x0000}, /* 00,46,00,aa */
+ {0xaa, 0x15, 0x0000}, /* 00,15,00,aa */
+ {}
+};
+
+static const struct usb_action tas5130c_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x50, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x02, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x00, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+
+ {0xa0, 0x04, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x0f, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x04, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x0f, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+ {0xa0, 0x06, ZC3XX_R08D_COMPABILITYMODE},
+ {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x70, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x00, 0x01ad},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x07, ZC3XX_R0A5_EXPOSUREGAIN},
+ {0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL},
+ {}
+};
+static const struct usb_action tas5130c_Initial[] = { /* 640x480 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x02, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x00, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x05, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x0f, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x05, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x0f, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
+ {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW},
+ {0xa0, 0x06, ZC3XX_R08D_COMPABILITYMODE},
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x70, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x00, 0x01ad},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x07, ZC3XX_R0A5_EXPOSUREGAIN},
+ {0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL},
+ {}
+};
+static const struct usb_action tas5130c_50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
+ {0xaa, 0xa4, 0x0063}, /* 00,a4,63,aa */
+ {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
+ {0xa0, 0x63, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,63,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xfe, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,47,cc */
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xd3, ZC3XX_R01D_HSYNC_0}, /* 00,1d,d3,cc */
+ {0xa0, 0xda, ZC3XX_R01E_HSYNC_1}, /* 00,1e,da,cc */
+ {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ea,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */
+ {0xa0, 0x4c, ZC3XX_R0A0_MAXXLOW},
+ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+ {}
+};
+static const struct usb_action tas5130c_50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
+ {0xaa, 0xa4, 0x0077}, /* 00,a4,77,aa */
+ {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
+ {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,77,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xd0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7d,cc */
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xf0, ZC3XX_R01D_HSYNC_0}, /* 00,1d,f0,cc */
+ {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,f4,cc */
+ {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,f8,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */
+ {0xa0, 0xc0, ZC3XX_R0A0_MAXXLOW},
+ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+ {}
+};
+static const struct usb_action tas5130c_60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
+ {0xaa, 0xa4, 0x0036}, /* 00,a4,36,aa */
+ {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
+ {0xa0, 0x36, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,36,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x54, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3e,cc */
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xca, ZC3XX_R01D_HSYNC_0}, /* 00,1d,ca,cc */
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */
+ {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */
+ {0xa0, 0x28, ZC3XX_R0A0_MAXXLOW},
+ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+ {}
+};
+static const struct usb_action tas5130c_60HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
+ {0xaa, 0xa4, 0x0077}, /* 00,a4,77,aa */
+ {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
+ {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,77,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x09, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x47, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7d,cc */
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xc8, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c8,cc */
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */
+ {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */
+ {0xa0, 0x20, ZC3XX_R0A0_MAXXLOW},
+ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+ {}
+};
+static const struct usb_action tas5130c_NoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
+ {0xaa, 0xa4, 0x0040}, /* 00,a4,40,aa */
+ {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
+ {0xa0, 0x40, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,40,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+ {0xa0, 0xbc, ZC3XX_R01D_HSYNC_0}, /* 00,1d,bc,cc */
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */
+ {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x02, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,02,cc */
+ {0xa0, 0xf0, ZC3XX_R0A0_MAXXLOW},
+ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+ {}
+};
+
+static const struct usb_action tas5130c_NoFlikerScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
+ {0xaa, 0xa4, 0x0090}, /* 00,a4,90,aa */
+ {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
+ {0xa0, 0x90, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,90,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x0a, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+ {0xa0, 0xbc, ZC3XX_R01D_HSYNC_0}, /* 00,1d,bc,cc */
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */
+ {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x02, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,02,cc */
+ {0xa0, 0xf0, ZC3XX_R0A0_MAXXLOW},
+ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+ {}
+};
+
+/* from usbvm305.inf 0ac8:305b 07/06/15 (3 - tas5130c) */
+static const struct usb_action gc0303_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc, */
+ {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, /* 00,08,02,cc, */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc, */
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc, */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc, */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc, */
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc, */
+ {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc, */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc, */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc, */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc, */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc, */
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc, */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc, */
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc, */
+ {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e6,cc,
+ * 6<->8 */
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc,
+ * 6<->8 */
+ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, /* 00,87,10,cc, */
+ {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc, */
+ {0xaa, 0x01, 0x0000},
+ {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa, */
+ {0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa, */
+ {0xaa, 0x1b, 0x0000},
+ {0xa0, 0x82, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,82,cc, */
+ {0xa0, 0x83, ZC3XX_R087_EXPTIMEMID}, /* 00,87,83,cc, */
+ {0xa0, 0x84, ZC3XX_R088_EXPTIMELOW}, /* 00,88,84,cc, */
+ {0xaa, 0x05, 0x0010}, /* 00,05,10,aa, */
+ {0xaa, 0x0a, 0x0002},
+ {0xaa, 0x0b, 0x0000},
+ {0xaa, 0x0c, 0x0002},
+ {0xaa, 0x0d, 0x0000},
+ {0xaa, 0x0e, 0x0002},
+ {0xaa, 0x0f, 0x0000},
+ {0xaa, 0x10, 0x0002},
+ {0xaa, 0x11, 0x0000},
+ {0xaa, 0x16, 0x0001}, /* 00,16,01,aa, */
+ {0xaa, 0x17, 0x00e8}, /* 00,17,e6,aa, (e6 -> e8) */
+ {0xaa, 0x18, 0x0002}, /* 00,18,02,aa, */
+ {0xaa, 0x19, 0x0088}, /* 00,19,86,aa, */
+ {0xaa, 0x20, 0x0020}, /* 00,20,20,aa, */
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc, */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc, */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc, */
+ {0xa0, 0x76, ZC3XX_R189_AWBSTATUS}, /* 01,89,76,cc, */
+ {0xa0, 0x09, 0x01ad}, /* 01,ad,09,cc, */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc, */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc, */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc, */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc, */
+ {0xa0, 0x58, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x61, ZC3XX_R116_RGAIN}, /* 01,16,61,cc, */
+ {0xa0, 0x65, ZC3XX_R118_BGAIN}, /* 01,18,65,cc */
+ {0xaa, 0x1b, 0x0000},
+ {}
+};
+
+static const struct usb_action gc0303_InitialScale[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc, */
+ {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, /* 00,08,02,cc, */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc, */
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc, */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc, */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc, */
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc, */
+ {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc, */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc, */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc, */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc, */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc, */
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc, */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc, */
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc, */
+ {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e8,cc,
+ * 8<->6 */
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc,
+ * 8<->6 */
+ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, /* 00,87,10,cc, */
+ {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc, */
+ {0xaa, 0x01, 0x0000},
+ {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa, */
+ {0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa, */
+ {0xaa, 0x1b, 0x0000},
+ {0xa0, 0x82, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,82,cc, */
+ {0xa0, 0x83, ZC3XX_R087_EXPTIMEMID}, /* 00,87,83,cc, */
+ {0xa0, 0x84, ZC3XX_R088_EXPTIMELOW}, /* 00,88,84,cc, */
+ {0xaa, 0x05, 0x0010}, /* 00,05,10,aa, */
+ {0xaa, 0x0a, 0x0001},
+ {0xaa, 0x0b, 0x0000},
+ {0xaa, 0x0c, 0x0001},
+ {0xaa, 0x0d, 0x0000},
+ {0xaa, 0x0e, 0x0001},
+ {0xaa, 0x0f, 0x0000},
+ {0xaa, 0x10, 0x0001},
+ {0xaa, 0x11, 0x0000},
+ {0xaa, 0x16, 0x0001}, /* 00,16,01,aa, */
+ {0xaa, 0x17, 0x00e8}, /* 00,17,e6,aa (e6 -> e8) */
+ {0xaa, 0x18, 0x0002}, /* 00,18,02,aa, */
+ {0xaa, 0x19, 0x0088}, /* 00,19,88,aa, */
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc, */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc, */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc, */
+ {0xa0, 0x76, ZC3XX_R189_AWBSTATUS}, /* 01,89,76,cc, */
+ {0xa0, 0x09, 0x01ad}, /* 01,ad,09,cc, */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc, */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc, */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc, */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc, */
+ {0xa0, 0x58, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x61, ZC3XX_R116_RGAIN}, /* 01,16,61,cc, */
+ {0xa0, 0x65, ZC3XX_R118_BGAIN}, /* 01,18,65,cc */
+ {0xaa, 0x1b, 0x0000},
+ {}
+};
+static const struct usb_action gc0303_50HZ[] = {
+ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
+ {0xaa, 0x83, 0x0001}, /* 00,83,01,aa */
+ {0xaa, 0x84, 0x0063},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */
+ {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0d,cc, */
+ {0xa0, 0xa8, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,50,cc, */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */
+ {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,47,cc, */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */
+ {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc, */
+ {0xa0, 0x48, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */
+ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */
+ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */
+ {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc, */
+ {0xa0, 0x7f, ZC3XX_R18D_YTARGET},
+ {}
+};
+
+static const struct usb_action gc0303_50HZScale[] = {
+ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
+ {0xaa, 0x83, 0x0003}, /* 00,83,03,aa */
+ {0xaa, 0x84, 0x0054}, /* 00,84,54,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */
+ {0xa0, 0x0d, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0d,cc, */
+ {0xa0, 0x50, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,50,cc, */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */
+ {0xa0, 0x8e, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,8e,cc, */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */
+ {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc, */
+ {0xa0, 0x48, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc, */
+ {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */
+ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */
+ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */
+ {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc, */
+ {0xa0, 0x7f, ZC3XX_R18D_YTARGET},
+ {}
+};
+
+static const struct usb_action gc0303_60HZ[] = {
+ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
+ {0xaa, 0x83, 0x0000},
+ {0xaa, 0x84, 0x003b},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */
+ {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,05,cc, */
+ {0xa0, 0x88, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,88,cc, */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */
+ {0xa0, 0x3b, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3b,cc, */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */
+ {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc, */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc, */
+ {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */
+ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */
+ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */
+ {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc, */
+ {0xa0, 0x80, ZC3XX_R18D_YTARGET},
+ {}
+};
+
+static const struct usb_action gc0303_60HZScale[] = {
+ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
+ {0xaa, 0x83, 0x0000},
+ {0xaa, 0x84, 0x0076},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */
+ {0xa0, 0x0b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,1,0b,cc, */
+ {0xa0, 0x10, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,2,10,cc, */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,5,00,cc, */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,6,00,cc, */
+ {0xa0, 0x76, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,7,76,cc, */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,c,0e,cc, */
+ {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,f,15,cc, */
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,9,10,cc, */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,a,24,cc, */
+ {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,d,62,cc, */
+ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,e,90,cc, */
+ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,f,c8,cc, */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,0,ff,cc, */
+ {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,d,58,cc, */
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc, */
+ {0xa0, 0x80, ZC3XX_R18D_YTARGET},
+ {}
+};
+
+static const struct usb_action gc0303_NoFliker[] = {
+ {0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc, */
+ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
+ {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */
+ {0xaa, 0x84, 0x0020}, /* 00,84,20,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,0,00,cc, */
+ {0xa0, 0x00, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x48, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */
+ {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc, */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */
+ {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */
+ {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */
+ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */
+ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */
+ {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */
+ {0xa0, 0x03, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,03,cc */
+ {}
+};
+
+static const struct usb_action gc0303_NoFlikerScale[] = {
+ {0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc, */
+ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
+ {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */
+ {0xaa, 0x84, 0x0020}, /* 00,84,20,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */
+ {0xa0, 0x00, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x48, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */
+ {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc, */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */
+ {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */
+ {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */
+ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */
+ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */
+ {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */
+ {0xa0, 0x03, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,03,cc */
+ {}
+};
+
+static u8 reg_r(struct gspca_dev *gspca_dev,
+ u16 index)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return 0;
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ 0xa1,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x01, /* value */
+ index, gspca_dev->usb_buf, 1,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_r err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ return 0;
+ }
+ return gspca_dev->usb_buf[0];
+}
+
+static void reg_w(struct gspca_dev *gspca_dev,
+ u8 value,
+ u16 index)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0xa0,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_w_i err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static u16 i2c_read(struct gspca_dev *gspca_dev,
+ u8 reg)
+{
+ u8 retbyte;
+ u16 retval;
+
+ if (gspca_dev->usb_err < 0)
+ return 0;
+ reg_w(gspca_dev, reg, 0x0092);
+ reg_w(gspca_dev, 0x02, 0x0090); /* <- read command */
+ msleep(20);
+ retbyte = reg_r(gspca_dev, 0x0091); /* read status */
+ if (retbyte != 0x00)
+ pr_err("i2c_r status error %02x\n", retbyte);
+ retval = reg_r(gspca_dev, 0x0095); /* read Lowbyte */
+ retval |= reg_r(gspca_dev, 0x0096) << 8; /* read Hightbyte */
+ return retval;
+}
+
+static u8 i2c_write(struct gspca_dev *gspca_dev,
+ u8 reg,
+ u8 valL,
+ u8 valH)
+{
+ u8 retbyte;
+
+ if (gspca_dev->usb_err < 0)
+ return 0;
+ reg_w(gspca_dev, reg, 0x92);
+ reg_w(gspca_dev, valL, 0x93);
+ reg_w(gspca_dev, valH, 0x94);
+ reg_w(gspca_dev, 0x01, 0x90); /* <- write command */
+ msleep(1);
+ retbyte = reg_r(gspca_dev, 0x0091); /* read status */
+ if (retbyte != 0x00)
+ pr_err("i2c_w status error %02x\n", retbyte);
+ return retbyte;
+}
+
+static void usb_exchange(struct gspca_dev *gspca_dev,
+ const struct usb_action *action)
+{
+ while (action->req) {
+ switch (action->req) {
+ case 0xa0: /* write register */
+ reg_w(gspca_dev, action->val, action->idx);
+ break;
+ case 0xa1: /* read status */
+ reg_r(gspca_dev, action->idx);
+ break;
+ case 0xaa:
+ i2c_write(gspca_dev,
+ action->val, /* reg */
+ action->idx & 0xff, /* valL */
+ action->idx >> 8); /* valH */
+ break;
+ case 0xbb:
+ i2c_write(gspca_dev,
+ action->idx >> 8, /* reg */
+ action->idx & 0xff, /* valL */
+ action->val); /* valH */
+ break;
+ default:
+/* case 0xdd: * delay */
+ msleep(action->idx);
+ break;
+ }
+ action++;
+ msleep(1);
+ }
+}
+
+static void setmatrix(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+ const u8 *matrix;
+ static const u8 adcm2700_matrix[9] =
+/* {0x66, 0xed, 0xed, 0xed, 0x66, 0xed, 0xed, 0xed, 0x66}; */
+/*ms-win*/
+ {0x74, 0xed, 0xed, 0xed, 0x74, 0xed, 0xed, 0xed, 0x74};
+ static const u8 gc0305_matrix[9] =
+ {0x50, 0xf8, 0xf8, 0xf8, 0x50, 0xf8, 0xf8, 0xf8, 0x50};
+ static const u8 ov7620_matrix[9] =
+ {0x58, 0xf4, 0xf4, 0xf4, 0x58, 0xf4, 0xf4, 0xf4, 0x58};
+ static const u8 pas202b_matrix[9] =
+ {0x4c, 0xf5, 0xff, 0xf9, 0x51, 0xf5, 0xfb, 0xed, 0x5f};
+ static const u8 po2030_matrix[9] =
+ {0x60, 0xf0, 0xf0, 0xf0, 0x60, 0xf0, 0xf0, 0xf0, 0x60};
+ static const u8 tas5130c_matrix[9] =
+ {0x68, 0xec, 0xec, 0xec, 0x68, 0xec, 0xec, 0xec, 0x68};
+ static const u8 gc0303_matrix[9] =
+ {0x6c, 0xea, 0xea, 0xea, 0x6c, 0xea, 0xea, 0xea, 0x6c};
+ static const u8 *matrix_tb[SENSOR_MAX] = {
+ [SENSOR_ADCM2700] = adcm2700_matrix,
+ [SENSOR_CS2102] = ov7620_matrix,
+ [SENSOR_CS2102K] = NULL,
+ [SENSOR_GC0303] = gc0303_matrix,
+ [SENSOR_GC0305] = gc0305_matrix,
+ [SENSOR_HDCS2020] = NULL,
+ [SENSOR_HV7131B] = NULL,
+ [SENSOR_HV7131R] = po2030_matrix,
+ [SENSOR_ICM105A] = po2030_matrix,
+ [SENSOR_MC501CB] = NULL,
+ [SENSOR_MT9V111_1] = gc0305_matrix,
+ [SENSOR_MT9V111_3] = gc0305_matrix,
+ [SENSOR_OV7620] = ov7620_matrix,
+ [SENSOR_OV7630C] = NULL,
+ [SENSOR_PAS106] = NULL,
+ [SENSOR_PAS202B] = pas202b_matrix,
+ [SENSOR_PB0330] = gc0305_matrix,
+ [SENSOR_PO2030] = po2030_matrix,
+ [SENSOR_TAS5130C] = tas5130c_matrix,
+ };
+
+ matrix = matrix_tb[sd->sensor];
+ if (matrix == NULL)
+ return; /* matrix already loaded */
+ for (i = 0; i < ARRAY_SIZE(ov7620_matrix); i++)
+ reg_w(gspca_dev, matrix[i], 0x010a + i);
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int sharpness;
+ static const u8 sharpness_tb[][2] = {
+ {0x02, 0x03},
+ {0x04, 0x07},
+ {0x08, 0x0f},
+ {0x10, 0x1e}
+ };
+
+ sharpness = sd->ctrls[SHARPNESS].val;
+ reg_w(gspca_dev, sharpness_tb[sharpness][0], 0x01c6);
+ reg_r(gspca_dev, 0x01c8);
+ reg_r(gspca_dev, 0x01c9);
+ reg_r(gspca_dev, 0x01ca);
+ reg_w(gspca_dev, sharpness_tb[sharpness][1], 0x01cb);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ const u8 *Tgamma;
+ int g, i, brightness, contrast, adj, gp1, gp2;
+ u8 gr[16];
+ static const u8 delta_b[16] = /* delta for brightness */
+ {0x50, 0x38, 0x2d, 0x28, 0x24, 0x21, 0x1e, 0x1d,
+ 0x1d, 0x1b, 0x1b, 0x1b, 0x19, 0x18, 0x18, 0x18};
+ static const u8 delta_c[16] = /* delta for contrast */
+ {0x2c, 0x1a, 0x12, 0x0c, 0x0a, 0x06, 0x06, 0x06,
+ 0x04, 0x06, 0x04, 0x04, 0x03, 0x03, 0x02, 0x02};
+ static const u8 gamma_tb[6][16] = {
+ {0x00, 0x00, 0x03, 0x0d, 0x1b, 0x2e, 0x45, 0x5f,
+ 0x79, 0x93, 0xab, 0xc1, 0xd4, 0xe5, 0xf3, 0xff},
+ {0x01, 0x0c, 0x1f, 0x3a, 0x53, 0x6d, 0x85, 0x9c,
+ 0xb0, 0xc2, 0xd1, 0xde, 0xe9, 0xf2, 0xf9, 0xff},
+ {0x04, 0x16, 0x30, 0x4e, 0x68, 0x81, 0x98, 0xac,
+ 0xbe, 0xcd, 0xda, 0xe4, 0xed, 0xf5, 0xfb, 0xff},
+ {0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
+ 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff},
+ {0x20, 0x4b, 0x6e, 0x8d, 0xa3, 0xb5, 0xc5, 0xd2,
+ 0xdc, 0xe5, 0xec, 0xf2, 0xf6, 0xfa, 0xfd, 0xff},
+ {0x24, 0x44, 0x64, 0x84, 0x9d, 0xb2, 0xc4, 0xd3,
+ 0xe0, 0xeb, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff},
+ };
+
+ Tgamma = gamma_tb[sd->ctrls[GAMMA].val - 1];
+
+ contrast = ((int) sd->ctrls[CONTRAST].val - 128); /* -128 / 127 */
+ brightness = ((int) sd->ctrls[BRIGHTNESS].val - 128); /* -128 / 92 */
+ adj = 0;
+ gp1 = gp2 = 0;
+ for (i = 0; i < 16; i++) {
+ g = Tgamma[i] + delta_b[i] * brightness / 256
+ - delta_c[i] * contrast / 256 - adj / 2;
+ if (g > 0xff)
+ g = 0xff;
+ else if (g < 0)
+ g = 0;
+ reg_w(gspca_dev, g, 0x0120 + i); /* gamma */
+ if (contrast > 0)
+ adj--;
+ else if (contrast < 0)
+ adj++;
+ if (i > 1)
+ gr[i - 1] = (g - gp2) / 2;
+ else if (i != 0)
+ gr[0] = gp1 == 0 ? 0 : (g - gp1);
+ gp2 = gp1;
+ gp1 = g;
+ }
+ gr[15] = (0xff - gp2) / 2;
+ for (i = 0; i < 16; i++)
+ reg_w(gspca_dev, gr[i], 0x0130 + i); /* gradient */
+}
+
+static void getexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor != SENSOR_HV7131R)
+ return;
+ sd->ctrls[EXPOSURE].val = (i2c_read(gspca_dev, 0x25) << 9)
+ | (i2c_read(gspca_dev, 0x26) << 1)
+ | (i2c_read(gspca_dev, 0x27) >> 7);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int val;
+
+ if (sd->sensor != SENSOR_HV7131R)
+ return;
+ val = sd->ctrls[EXPOSURE].val;
+ i2c_write(gspca_dev, 0x25, val >> 9, 0x00);
+ i2c_write(gspca_dev, 0x26, val >> 1, 0x00);
+ i2c_write(gspca_dev, 0x27, val << 7, 0x00);
+}
+
+static void setquality(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s8 reg07;
+
+ reg07 = 0;
+ switch (sd->sensor) {
+ case SENSOR_OV7620:
+ reg07 = 0x30;
+ break;
+ case SENSOR_HV7131R:
+ case SENSOR_PAS202B:
+ return; /* done by work queue */
+ }
+ reg_w(gspca_dev, sd->reg08, ZC3XX_R008_CLOCKSETTING);
+ if (reg07 != 0)
+ reg_w(gspca_dev, reg07, 0x0007);
+}
+
+/* Matches the sensor's internal frame rate to the lighting frequency.
+ * Valid frequencies are:
+ * 50Hz, for European and Asian lighting (default)
+ * 60Hz, for American lighting
+ * 0 = No Fliker (for outdoore usage)
+ */
+static void setlightfreq(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, mode;
+ const struct usb_action *zc3_freq;
+ static const struct usb_action *freq_tb[SENSOR_MAX][6] = {
+ [SENSOR_ADCM2700] =
+ {adcm2700_NoFliker, adcm2700_NoFliker,
+ adcm2700_50HZ, adcm2700_50HZ,
+ adcm2700_60HZ, adcm2700_60HZ},
+ [SENSOR_CS2102] =
+ {cs2102_NoFliker, cs2102_NoFlikerScale,
+ cs2102_50HZ, cs2102_50HZScale,
+ cs2102_60HZ, cs2102_60HZScale},
+ [SENSOR_CS2102K] =
+ {cs2102_NoFliker, cs2102_NoFlikerScale,
+ NULL, NULL, /* currently disabled */
+ NULL, NULL},
+ [SENSOR_GC0303] =
+ {gc0303_NoFliker, gc0303_NoFlikerScale,
+ gc0303_50HZ, gc0303_50HZScale,
+ gc0303_60HZ, gc0303_60HZScale},
+ [SENSOR_GC0305] =
+ {gc0305_NoFliker, gc0305_NoFliker,
+ gc0305_50HZ, gc0305_50HZ,
+ gc0305_60HZ, gc0305_60HZ},
+ [SENSOR_HDCS2020] =
+ {hdcs2020_NoFliker, hdcs2020_NoFliker,
+ hdcs2020_50HZ, hdcs2020_50HZ,
+ hdcs2020_60HZ, hdcs2020_60HZ},
+ [SENSOR_HV7131B] =
+ {hv7131b_NoFliker, hv7131b_NoFlikerScale,
+ hv7131b_50HZ, hv7131b_50HZScale,
+ hv7131b_60HZ, hv7131b_60HZScale},
+ [SENSOR_HV7131R] =
+ {hv7131r_NoFliker, hv7131r_NoFlikerScale,
+ hv7131r_50HZ, hv7131r_50HZScale,
+ hv7131r_60HZ, hv7131r_60HZScale},
+ [SENSOR_ICM105A] =
+ {icm105a_NoFliker, icm105a_NoFlikerScale,
+ icm105a_50HZ, icm105a_50HZScale,
+ icm105a_60HZ, icm105a_60HZScale},
+ [SENSOR_MC501CB] =
+ {mc501cb_NoFliker, mc501cb_NoFlikerScale,
+ mc501cb_50HZ, mc501cb_50HZScale,
+ mc501cb_60HZ, mc501cb_60HZScale},
+ [SENSOR_MT9V111_1] =
+ {mt9v111_1_AENoFliker, mt9v111_1_AENoFlikerScale,
+ mt9v111_1_AE50HZ, mt9v111_1_AE50HZScale,
+ mt9v111_1_AE60HZ, mt9v111_1_AE60HZScale},
+ [SENSOR_MT9V111_3] =
+ {mt9v111_3_AENoFliker, mt9v111_3_AENoFlikerScale,
+ mt9v111_3_AE50HZ, mt9v111_3_AE50HZScale,
+ mt9v111_3_AE60HZ, mt9v111_3_AE60HZScale},
+ [SENSOR_OV7620] =
+ {ov7620_NoFliker, ov7620_NoFliker,
+ ov7620_50HZ, ov7620_50HZ,
+ ov7620_60HZ, ov7620_60HZ},
+ [SENSOR_OV7630C] =
+ {NULL, NULL,
+ NULL, NULL,
+ NULL, NULL},
+ [SENSOR_PAS106] =
+ {pas106b_NoFliker, pas106b_NoFliker,
+ pas106b_50HZ, pas106b_50HZ,
+ pas106b_60HZ, pas106b_60HZ},
+ [SENSOR_PAS202B] =
+ {pas202b_NoFliker, pas202b_NoFlikerScale,
+ pas202b_50HZ, pas202b_50HZScale,
+ pas202b_60HZ, pas202b_60HZScale},
+ [SENSOR_PB0330] =
+ {pb0330_NoFliker, pb0330_NoFlikerScale,
+ pb0330_50HZ, pb0330_50HZScale,
+ pb0330_60HZ, pb0330_60HZScale},
+ [SENSOR_PO2030] =
+ {po2030_NoFliker, po2030_NoFliker,
+ po2030_50HZ, po2030_50HZ,
+ po2030_60HZ, po2030_60HZ},
+ [SENSOR_TAS5130C] =
+ {tas5130c_NoFliker, tas5130c_NoFlikerScale,
+ tas5130c_50HZ, tas5130c_50HZScale,
+ tas5130c_60HZ, tas5130c_60HZScale},
+ };
+
+ i = sd->ctrls[LIGHTFREQ].val * 2;
+ mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+ if (mode)
+ i++; /* 320x240 */
+ zc3_freq = freq_tb[sd->sensor][i];
+ if (zc3_freq == NULL)
+ return;
+ usb_exchange(gspca_dev, zc3_freq);
+ switch (sd->sensor) {
+ case SENSOR_GC0305:
+ if (mode /* if 320x240 */
+ && sd->ctrls[LIGHTFREQ].val == 1) /* and 50Hz */
+ reg_w(gspca_dev, 0x85, 0x018d);
+ /* win: 0x80, 0x018d */
+ break;
+ case SENSOR_OV7620:
+ if (!mode) { /* if 640x480 */
+ if (sd->ctrls[LIGHTFREQ].val != 0) /* and filter */
+ reg_w(gspca_dev, 0x40, 0x0002);
+ else
+ reg_w(gspca_dev, 0x44, 0x0002);
+ }
+ break;
+ case SENSOR_PAS202B:
+ reg_w(gspca_dev, 0x00, 0x01a7);
+ break;
+ }
+}
+
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 autoval;
+
+ if (sd->ctrls[AUTOGAIN].val)
+ autoval = 0x42;
+ else
+ autoval = 0x02;
+ reg_w(gspca_dev, autoval, 0x0180);
+}
+
+/* update the transfer parameters */
+/* This function is executed from a work queue. */
+/* The exact use of the bridge registers 07 and 08 is not known.
+ * The following algorithm has been adapted from ms-win traces */
+static void transfer_update(struct work_struct *work)
+{
+ struct sd *sd = container_of(work, struct sd, work);
+ struct gspca_dev *gspca_dev = &sd->gspca_dev;
+ int change, good;
+ u8 reg07, reg11;
+
+ /* synchronize with the main driver and initialize the registers */
+ mutex_lock(&gspca_dev->usb_lock);
+ reg07 = 0; /* max */
+ reg_w(gspca_dev, reg07, 0x0007);
+ reg_w(gspca_dev, sd->reg08, ZC3XX_R008_CLOCKSETTING);
+ mutex_unlock(&gspca_dev->usb_lock);
+
+ good = 0;
+ for (;;) {
+ msleep(100);
+
+ /* get the transfer status */
+ /* the bit 0 of the bridge register 11 indicates overflow */
+ mutex_lock(&gspca_dev->usb_lock);
+ if (!gspca_dev->present || !gspca_dev->streaming)
+ goto err;
+ reg11 = reg_r(gspca_dev, 0x0011);
+ if (gspca_dev->usb_err < 0
+ || !gspca_dev->present || !gspca_dev->streaming)
+ goto err;
+
+ change = reg11 & 0x01;
+ if (change) { /* overflow */
+ switch (reg07) {
+ case 0: /* max */
+ reg07 = sd->sensor == SENSOR_HV7131R
+ ? 0x30 : 0x32;
+ if (sd->reg08 != 0) {
+ change = 3;
+ sd->reg08--;
+ }
+ break;
+ case 0x32:
+ reg07 -= 4;
+ break;
+ default:
+ reg07 -= 2;
+ break;
+ case 2:
+ change = 0; /* already min */
+ break;
+ }
+ good = 0;
+ } else { /* no overflow */
+ if (reg07 != 0) { /* if not max */
+ good++;
+ if (good >= 10) {
+ good = 0;
+ change = 1;
+ reg07 += 2;
+ switch (reg07) {
+ case 0x30:
+ if (sd->sensor == SENSOR_PAS202B)
+ reg07 += 2;
+ break;
+ case 0x32:
+ case 0x34:
+ reg07 = 0;
+ break;
+ }
+ }
+ } else { /* reg07 max */
+ if (sd->reg08 < sizeof jpeg_qual - 1) {
+ good++;
+ if (good > 10) {
+ sd->reg08++;
+ change = 2;
+ }
+ }
+ }
+ }
+ if (change) {
+ if (change & 1) {
+ reg_w(gspca_dev, reg07, 0x0007);
+ if (gspca_dev->usb_err < 0
+ || !gspca_dev->present
+ || !gspca_dev->streaming)
+ goto err;
+ }
+ if (change & 2) {
+ reg_w(gspca_dev, sd->reg08,
+ ZC3XX_R008_CLOCKSETTING);
+ if (gspca_dev->usb_err < 0
+ || !gspca_dev->present
+ || !gspca_dev->streaming)
+ goto err;
+ sd->ctrls[QUALITY].val = jpeg_qual[sd->reg08];
+ jpeg_set_qual(sd->jpeg_hdr,
+ jpeg_qual[sd->reg08]);
+ }
+ }
+ mutex_unlock(&gspca_dev->usb_lock);
+ }
+ return;
+err:
+ mutex_unlock(&gspca_dev->usb_lock);
+}
+
+static void send_unknown(struct gspca_dev *gspca_dev, int sensor)
+{
+ reg_w(gspca_dev, 0x01, 0x0000); /* bridge reset */
+ switch (sensor) {
+ case SENSOR_PAS106:
+ reg_w(gspca_dev, 0x03, 0x003a);
+ reg_w(gspca_dev, 0x0c, 0x003b);
+ reg_w(gspca_dev, 0x08, 0x0038);
+ break;
+ case SENSOR_ADCM2700:
+ case SENSOR_GC0305:
+ case SENSOR_OV7620:
+ case SENSOR_MT9V111_1:
+ case SENSOR_MT9V111_3:
+ case SENSOR_PB0330:
+ case SENSOR_PO2030:
+ reg_w(gspca_dev, 0x0d, 0x003a);
+ reg_w(gspca_dev, 0x02, 0x003b);
+ reg_w(gspca_dev, 0x00, 0x0038);
+ break;
+ case SENSOR_HV7131R:
+ case SENSOR_PAS202B:
+ reg_w(gspca_dev, 0x03, 0x003b);
+ reg_w(gspca_dev, 0x0c, 0x003a);
+ reg_w(gspca_dev, 0x0b, 0x0039);
+ if (sensor == SENSOR_PAS202B)
+ reg_w(gspca_dev, 0x0b, 0x0038);
+ break;
+ }
+}
+
+/* start probe 2 wires */
+static void start_2wr_probe(struct gspca_dev *gspca_dev, int sensor)
+{
+ reg_w(gspca_dev, 0x01, 0x0000);
+ reg_w(gspca_dev, sensor, 0x0010);
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0x03, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0012);
+/* msleep(2); */
+}
+
+static int sif_probe(struct gspca_dev *gspca_dev)
+{
+ u16 checkword;
+
+ start_2wr_probe(gspca_dev, 0x0f); /* PAS106 */
+ reg_w(gspca_dev, 0x08, 0x008d);
+ msleep(150);
+ checkword = ((i2c_read(gspca_dev, 0x00) & 0x0f) << 4)
+ | ((i2c_read(gspca_dev, 0x01) & 0xf0) >> 4);
+ PDEBUG(D_PROBE, "probe sif 0x%04x", checkword);
+ if (checkword == 0x0007) {
+ send_unknown(gspca_dev, SENSOR_PAS106);
+ return 0x0f; /* PAS106 */
+ }
+ return -1;
+}
+
+static int vga_2wr_probe(struct gspca_dev *gspca_dev)
+{
+ u16 retword;
+
+ start_2wr_probe(gspca_dev, 0x00); /* HV7131B */
+ i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
+ retword = i2c_read(gspca_dev, 0x01);
+ if (retword != 0)
+ return 0x00; /* HV7131B */
+
+ start_2wr_probe(gspca_dev, 0x04); /* CS2102 */
+ i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
+ retword = i2c_read(gspca_dev, 0x01);
+ if (retword != 0)
+ return 0x04; /* CS2102 */
+
+ start_2wr_probe(gspca_dev, 0x06); /* OmniVision */
+ reg_w(gspca_dev, 0x08, 0x008d);
+ i2c_write(gspca_dev, 0x11, 0xaa, 0x00);
+ retword = i2c_read(gspca_dev, 0x11);
+ if (retword != 0) {
+ /* (should have returned 0xaa) --> Omnivision? */
+ /* reg_r 0x10 -> 0x06 --> */
+ goto ov_check;
+ }
+
+ start_2wr_probe(gspca_dev, 0x08); /* HDCS2020 */
+ i2c_write(gspca_dev, 0x1c, 0x00, 0x00);
+ i2c_write(gspca_dev, 0x15, 0xaa, 0x00);
+ retword = i2c_read(gspca_dev, 0x15);
+ if (retword != 0)
+ return 0x08; /* HDCS2020 */
+
+ start_2wr_probe(gspca_dev, 0x0a); /* PB0330 */
+ i2c_write(gspca_dev, 0x07, 0xaa, 0xaa);
+ retword = i2c_read(gspca_dev, 0x07);
+ if (retword != 0)
+ return 0x0a; /* PB0330 */
+ retword = i2c_read(gspca_dev, 0x03);
+ if (retword != 0)
+ return 0x0a; /* PB0330 ?? */
+ retword = i2c_read(gspca_dev, 0x04);
+ if (retword != 0)
+ return 0x0a; /* PB0330 ?? */
+
+ start_2wr_probe(gspca_dev, 0x0c); /* ICM105A */
+ i2c_write(gspca_dev, 0x01, 0x11, 0x00);
+ retword = i2c_read(gspca_dev, 0x01);
+ if (retword != 0)
+ return 0x0c; /* ICM105A */
+
+ start_2wr_probe(gspca_dev, 0x0e); /* PAS202BCB */
+ reg_w(gspca_dev, 0x08, 0x008d);
+ i2c_write(gspca_dev, 0x03, 0xaa, 0x00);
+ msleep(50);
+ retword = i2c_read(gspca_dev, 0x03);
+ if (retword != 0) {
+ send_unknown(gspca_dev, SENSOR_PAS202B);
+ return 0x0e; /* PAS202BCB */
+ }
+
+ start_2wr_probe(gspca_dev, 0x02); /* TAS5130C */
+ i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
+ retword = i2c_read(gspca_dev, 0x01);
+ if (retword != 0)
+ return 0x02; /* TAS5130C */
+ov_check:
+ reg_r(gspca_dev, 0x0010); /* ?? */
+ reg_r(gspca_dev, 0x0010);
+
+ reg_w(gspca_dev, 0x01, 0x0000);
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0x06, 0x0010); /* OmniVision */
+ reg_w(gspca_dev, 0xa1, 0x008b);
+ reg_w(gspca_dev, 0x08, 0x008d);
+ msleep(500);
+ reg_w(gspca_dev, 0x01, 0x0012);
+ i2c_write(gspca_dev, 0x12, 0x80, 0x00); /* sensor reset */
+ retword = i2c_read(gspca_dev, 0x0a) << 8;
+ retword |= i2c_read(gspca_dev, 0x0b);
+ PDEBUG(D_PROBE, "probe 2wr ov vga 0x%04x", retword);
+ switch (retword) {
+ case 0x7631: /* OV7630C */
+ reg_w(gspca_dev, 0x06, 0x0010);
+ break;
+ case 0x7620: /* OV7620 */
+ case 0x7648: /* OV7648 */
+ break;
+ default:
+ return -1; /* not OmniVision */
+ }
+ return retword;
+}
+
+struct sensor_by_chipset_revision {
+ u16 revision;
+ u8 internal_sensor_id;
+};
+static const struct sensor_by_chipset_revision chipset_revision_sensor[] = {
+ {0xc000, 0x12}, /* TAS5130C */
+ {0xc001, 0x13}, /* MT9V111 */
+ {0xe001, 0x13},
+ {0x8001, 0x13},
+ {0x8000, 0x14}, /* CS2102K */
+ {0x8400, 0x15}, /* MT9V111 */
+ {0xe400, 0x15},
+};
+
+static int vga_3wr_probe(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+ u16 retword;
+
+/*fixme: lack of 8b=b3 (11,12)-> 10, 8b=e0 (14,15,16)-> 12 found in gspcav1*/
+ reg_w(gspca_dev, 0x02, 0x0010);
+ reg_r(gspca_dev, 0x0010);
+ reg_w(gspca_dev, 0x01, 0x0000);
+ reg_w(gspca_dev, 0x00, 0x0010);
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0x91, 0x008b);
+ reg_w(gspca_dev, 0x03, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0012);
+ reg_w(gspca_dev, 0x05, 0x0012);
+ retword = i2c_read(gspca_dev, 0x14);
+ if (retword != 0)
+ return 0x11; /* HV7131R */
+ retword = i2c_read(gspca_dev, 0x15);
+ if (retword != 0)
+ return 0x11; /* HV7131R */
+ retword = i2c_read(gspca_dev, 0x16);
+ if (retword != 0)
+ return 0x11; /* HV7131R */
+
+ reg_w(gspca_dev, 0x02, 0x0010);
+ retword = reg_r(gspca_dev, 0x000b) << 8;
+ retword |= reg_r(gspca_dev, 0x000a);
+ PDEBUG(D_PROBE, "probe 3wr vga 1 0x%04x", retword);
+ reg_r(gspca_dev, 0x0010);
+ if ((retword & 0xff00) == 0x6400)
+ return 0x02; /* TAS5130C */
+ for (i = 0; i < ARRAY_SIZE(chipset_revision_sensor); i++) {
+ if (chipset_revision_sensor[i].revision == retword) {
+ sd->chip_revision = retword;
+ send_unknown(gspca_dev, SENSOR_PB0330);
+ return chipset_revision_sensor[i].internal_sensor_id;
+ }
+ }
+
+ reg_w(gspca_dev, 0x01, 0x0000); /* check PB0330 */
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0xdd, 0x008b);
+ reg_w(gspca_dev, 0x0a, 0x0010);
+ reg_w(gspca_dev, 0x03, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0012);
+ retword = i2c_read(gspca_dev, 0x00);
+ if (retword != 0) {
+ PDEBUG(D_PROBE, "probe 3wr vga type 0a");
+ return 0x0a; /* PB0330 */
+ }
+
+ /* probe gc0303 / gc0305 */
+ reg_w(gspca_dev, 0x01, 0x0000);
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0x98, 0x008b);
+ reg_w(gspca_dev, 0x01, 0x0010);
+ reg_w(gspca_dev, 0x03, 0x0012);
+ msleep(2);
+ reg_w(gspca_dev, 0x01, 0x0012);
+ retword = i2c_read(gspca_dev, 0x00);
+ if (retword != 0) {
+ PDEBUG(D_PROBE, "probe 3wr vga type %02x", retword);
+ if (retword == 0x0011) /* gc0303 */
+ return 0x0303;
+ if (retword == 0x0029) /* gc0305 */
+ send_unknown(gspca_dev, SENSOR_GC0305);
+ return retword;
+ }
+
+ reg_w(gspca_dev, 0x01, 0x0000); /* check OmniVision */
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0xa1, 0x008b);
+ reg_w(gspca_dev, 0x08, 0x008d);
+ reg_w(gspca_dev, 0x06, 0x0010);
+ reg_w(gspca_dev, 0x01, 0x0012);
+ reg_w(gspca_dev, 0x05, 0x0012);
+ if (i2c_read(gspca_dev, 0x1c) == 0x007f /* OV7610 - manufacturer ID */
+ && i2c_read(gspca_dev, 0x1d) == 0x00a2) {
+ send_unknown(gspca_dev, SENSOR_OV7620);
+ return 0x06; /* OmniVision confirm ? */
+ }
+
+ reg_w(gspca_dev, 0x01, 0x0000);
+ reg_w(gspca_dev, 0x00, 0x0002);
+ reg_w(gspca_dev, 0x01, 0x0010);
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0xee, 0x008b);
+ reg_w(gspca_dev, 0x03, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0012);
+ reg_w(gspca_dev, 0x05, 0x0012);
+ retword = i2c_read(gspca_dev, 0x00) << 8; /* ID 0 */
+ retword |= i2c_read(gspca_dev, 0x01); /* ID 1 */
+ PDEBUG(D_PROBE, "probe 3wr vga 2 0x%04x", retword);
+ if (retword == 0x2030) {
+#ifdef GSPCA_DEBUG
+ u8 retbyte;
+
+ retbyte = i2c_read(gspca_dev, 0x02); /* revision number */
+ PDEBUG(D_PROBE, "sensor PO2030 rev 0x%02x", retbyte);
+#endif
+ send_unknown(gspca_dev, SENSOR_PO2030);
+ return retword;
+ }
+
+ reg_w(gspca_dev, 0x01, 0x0000);
+ reg_w(gspca_dev, 0x0a, 0x0010);
+ reg_w(gspca_dev, 0xd3, 0x008b);
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0x03, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0012);
+ reg_w(gspca_dev, 0x05, 0x0012);
+ reg_w(gspca_dev, 0xd3, 0x008b);
+ retword = i2c_read(gspca_dev, 0x01);
+ if (retword != 0) {
+ PDEBUG(D_PROBE, "probe 3wr vga type 0a ? ret: %04x", retword);
+ return 0x16; /* adcm2700 (6100/6200) */
+ }
+ return -1;
+}
+
+static int zcxx_probeSensor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int sensor;
+
+ switch (sd->sensor) {
+ case SENSOR_MC501CB:
+ return -1; /* don't probe */
+ case SENSOR_GC0303:
+ /* may probe but with no write in reg 0x0010 */
+ return -1; /* don't probe */
+ case SENSOR_PAS106:
+ sensor = sif_probe(gspca_dev);
+ if (sensor >= 0)
+ return sensor;
+ break;
+ }
+ sensor = vga_2wr_probe(gspca_dev);
+ if (sensor >= 0)
+ return sensor;
+ return vga_3wr_probe(gspca_dev);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (id->idProduct == 0x301b)
+ sd->bridge = BRIDGE_ZC301;
+ else
+ sd->bridge = BRIDGE_ZC303;
+
+ /* define some sensors from the vendor/product */
+ sd->sensor = id->driver_info;
+
+ gspca_dev->cam.ctrls = sd->ctrls;
+ sd->reg08 = REG08_DEF;
+
+ INIT_WORK(&sd->work, transfer_update);
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+ int sensor;
+ static const u8 gamma[SENSOR_MAX] = {
+ [SENSOR_ADCM2700] = 4,
+ [SENSOR_CS2102] = 4,
+ [SENSOR_CS2102K] = 5,
+ [SENSOR_GC0303] = 3,
+ [SENSOR_GC0305] = 4,
+ [SENSOR_HDCS2020] = 4,
+ [SENSOR_HV7131B] = 4,
+ [SENSOR_HV7131R] = 4,
+ [SENSOR_ICM105A] = 4,
+ [SENSOR_MC501CB] = 4,
+ [SENSOR_MT9V111_1] = 4,
+ [SENSOR_MT9V111_3] = 4,
+ [SENSOR_OV7620] = 3,
+ [SENSOR_OV7630C] = 4,
+ [SENSOR_PAS106] = 4,
+ [SENSOR_PAS202B] = 4,
+ [SENSOR_PB0330] = 4,
+ [SENSOR_PO2030] = 4,
+ [SENSOR_TAS5130C] = 3,
+ };
+ static const u8 mode_tb[SENSOR_MAX] = {
+ [SENSOR_ADCM2700] = 2,
+ [SENSOR_CS2102] = 1,
+ [SENSOR_CS2102K] = 1,
+ [SENSOR_GC0303] = 1,
+ [SENSOR_GC0305] = 1,
+ [SENSOR_HDCS2020] = 1,
+ [SENSOR_HV7131B] = 1,
+ [SENSOR_HV7131R] = 1,
+ [SENSOR_ICM105A] = 1,
+ [SENSOR_MC501CB] = 2,
+ [SENSOR_MT9V111_1] = 1,
+ [SENSOR_MT9V111_3] = 1,
+ [SENSOR_OV7620] = 2,
+ [SENSOR_OV7630C] = 1,
+ [SENSOR_PAS106] = 0,
+ [SENSOR_PAS202B] = 1,
+ [SENSOR_PB0330] = 1,
+ [SENSOR_PO2030] = 1,
+ [SENSOR_TAS5130C] = 1,
+ };
+ static const u8 reg08_tb[SENSOR_MAX] = {
+ [SENSOR_ADCM2700] = 1,
+ [SENSOR_CS2102] = 3,
+ [SENSOR_CS2102K] = 3,
+ [SENSOR_GC0303] = 2,
+ [SENSOR_GC0305] = 3,
+ [SENSOR_HDCS2020] = 1,
+ [SENSOR_HV7131B] = 3,
+ [SENSOR_HV7131R] = 3,
+ [SENSOR_ICM105A] = 3,
+ [SENSOR_MC501CB] = 3,
+ [SENSOR_MT9V111_1] = 3,
+ [SENSOR_MT9V111_3] = 3,
+ [SENSOR_OV7620] = 1,
+ [SENSOR_OV7630C] = 3,
+ [SENSOR_PAS106] = 3,
+ [SENSOR_PAS202B] = 3,
+ [SENSOR_PB0330] = 3,
+ [SENSOR_PO2030] = 2,
+ [SENSOR_TAS5130C] = 3,
+ };
+
+ sensor = zcxx_probeSensor(gspca_dev);
+ if (sensor >= 0)
+ PDEBUG(D_PROBE, "probe sensor -> %04x", sensor);
+ if ((unsigned) force_sensor < SENSOR_MAX) {
+ sd->sensor = force_sensor;
+ PDEBUG(D_PROBE, "sensor forced to %d", force_sensor);
+ } else {
+ switch (sensor) {
+ case -1:
+ switch (sd->sensor) {
+ case SENSOR_MC501CB:
+ PDEBUG(D_PROBE, "Sensor MC501CB");
+ break;
+ case SENSOR_GC0303:
+ PDEBUG(D_PROBE, "Sensor GC0303");
+ break;
+ default:
+ pr_warn("Unknown sensor - set to TAS5130C\n");
+ sd->sensor = SENSOR_TAS5130C;
+ }
+ break;
+ case 0:
+ /* check the sensor type */
+ sensor = i2c_read(gspca_dev, 0x00);
+ PDEBUG(D_PROBE, "Sensor hv7131 type %d", sensor);
+ switch (sensor) {
+ case 0: /* hv7131b */
+ case 1: /* hv7131e */
+ PDEBUG(D_PROBE, "Find Sensor HV7131B");
+ sd->sensor = SENSOR_HV7131B;
+ break;
+ default:
+/* case 2: * hv7131r */
+ PDEBUG(D_PROBE, "Find Sensor HV7131R");
+ sd->sensor = SENSOR_HV7131R;
+ break;
+ }
+ break;
+ case 0x02:
+ PDEBUG(D_PROBE, "Sensor TAS5130C");
+ sd->sensor = SENSOR_TAS5130C;
+ break;
+ case 0x04:
+ PDEBUG(D_PROBE, "Find Sensor CS2102");
+ sd->sensor = SENSOR_CS2102;
+ break;
+ case 0x08:
+ PDEBUG(D_PROBE, "Find Sensor HDCS2020");
+ sd->sensor = SENSOR_HDCS2020;
+ break;
+ case 0x0a:
+ PDEBUG(D_PROBE,
+ "Find Sensor PB0330. Chip revision %x",
+ sd->chip_revision);
+ sd->sensor = SENSOR_PB0330;
+ break;
+ case 0x0c:
+ PDEBUG(D_PROBE, "Find Sensor ICM105A");
+ sd->sensor = SENSOR_ICM105A;
+ break;
+ case 0x0e:
+ PDEBUG(D_PROBE, "Find Sensor PAS202B");
+ sd->sensor = SENSOR_PAS202B;
+ break;
+ case 0x0f:
+ PDEBUG(D_PROBE, "Find Sensor PAS106");
+ sd->sensor = SENSOR_PAS106;
+ break;
+ case 0x10:
+ case 0x12:
+ PDEBUG(D_PROBE, "Find Sensor TAS5130C");
+ sd->sensor = SENSOR_TAS5130C;
+ break;
+ case 0x11:
+ PDEBUG(D_PROBE, "Find Sensor HV7131R");
+ sd->sensor = SENSOR_HV7131R;
+ break;
+ case 0x13:
+ case 0x15:
+ PDEBUG(D_PROBE,
+ "Sensor MT9V111. Chip revision %04x",
+ sd->chip_revision);
+ sd->sensor = sd->bridge == BRIDGE_ZC301
+ ? SENSOR_MT9V111_1
+ : SENSOR_MT9V111_3;
+ break;
+ case 0x14:
+ PDEBUG(D_PROBE,
+ "Find Sensor CS2102K?. Chip revision %x",
+ sd->chip_revision);
+ sd->sensor = SENSOR_CS2102K;
+ break;
+ case 0x16:
+ PDEBUG(D_PROBE, "Find Sensor ADCM2700");
+ sd->sensor = SENSOR_ADCM2700;
+ break;
+ case 0x29:
+ PDEBUG(D_PROBE, "Find Sensor GC0305");
+ sd->sensor = SENSOR_GC0305;
+ break;
+ case 0x0303:
+ PDEBUG(D_PROBE, "Sensor GC0303");
+ sd->sensor = SENSOR_GC0303;
+ break;
+ case 0x2030:
+ PDEBUG(D_PROBE, "Find Sensor PO2030");
+ sd->sensor = SENSOR_PO2030;
+ sd->ctrls[SHARPNESS].def = 0; /* from win traces */
+ break;
+ case 0x7620:
+ PDEBUG(D_PROBE, "Find Sensor OV7620");
+ sd->sensor = SENSOR_OV7620;
+ break;
+ case 0x7631:
+ PDEBUG(D_PROBE, "Find Sensor OV7630C");
+ sd->sensor = SENSOR_OV7630C;
+ break;
+ case 0x7648:
+ PDEBUG(D_PROBE, "Find Sensor OV7648");
+ sd->sensor = SENSOR_OV7620; /* same sensor (?) */
+ break;
+ default:
+ pr_err("Unknown sensor %04x\n", sensor);
+ return -EINVAL;
+ }
+ }
+ if (sensor < 0x20) {
+ if (sensor == -1 || sensor == 0x10 || sensor == 0x12)
+ reg_w(gspca_dev, 0x02, 0x0010);
+ reg_r(gspca_dev, 0x0010);
+ }
+
+ cam = &gspca_dev->cam;
+ switch (mode_tb[sd->sensor]) {
+ case 0:
+ cam->cam_mode = sif_mode;
+ cam->nmodes = ARRAY_SIZE(sif_mode);
+ break;
+ case 1:
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+ break;
+ default:
+/* case 2: */
+ cam->cam_mode = broken_vga_mode;
+ cam->nmodes = ARRAY_SIZE(broken_vga_mode);
+ break;
+ }
+
+ sd->ctrls[GAMMA].def = gamma[sd->sensor];
+ sd->reg08 = reg08_tb[sd->sensor];
+ sd->ctrls[QUALITY].def = jpeg_qual[sd->reg08];
+ sd->ctrls[QUALITY].min = jpeg_qual[0];
+ sd->ctrls[QUALITY].max = jpeg_qual[ARRAY_SIZE(jpeg_qual) - 1];
+
+ switch (sd->sensor) {
+ case SENSOR_HV7131R:
+ gspca_dev->ctrl_dis = (1 << QUALITY);
+ break;
+ case SENSOR_OV7630C:
+ gspca_dev->ctrl_dis = (1 << LIGHTFREQ) | (1 << EXPOSURE);
+ break;
+ case SENSOR_PAS202B:
+ gspca_dev->ctrl_dis = (1 << QUALITY) | (1 << EXPOSURE);
+ break;
+ default:
+ gspca_dev->ctrl_dis = (1 << EXPOSURE);
+ break;
+ }
+#if AUTOGAIN_DEF
+ if (sd->ctrls[AUTOGAIN].val)
+ gspca_dev->ctrl_inac = (1 << EXPOSURE);
+#endif
+
+ /* switch off the led */
+ reg_w(gspca_dev, 0x01, 0x0000);
+ return gspca_dev->usb_err;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int mode;
+ static const struct usb_action *init_tb[SENSOR_MAX][2] = {
+ [SENSOR_ADCM2700] =
+ {adcm2700_Initial, adcm2700_InitialScale},
+ [SENSOR_CS2102] =
+ {cs2102_Initial, cs2102_InitialScale},
+ [SENSOR_CS2102K] =
+ {cs2102K_Initial, cs2102K_InitialScale},
+ [SENSOR_GC0303] =
+ {gc0303_Initial, gc0303_InitialScale},
+ [SENSOR_GC0305] =
+ {gc0305_Initial, gc0305_InitialScale},
+ [SENSOR_HDCS2020] =
+ {hdcs2020_Initial, hdcs2020_InitialScale},
+ [SENSOR_HV7131B] =
+ {hv7131b_Initial, hv7131b_InitialScale},
+ [SENSOR_HV7131R] =
+ {hv7131r_Initial, hv7131r_InitialScale},
+ [SENSOR_ICM105A] =
+ {icm105a_Initial, icm105a_InitialScale},
+ [SENSOR_MC501CB] =
+ {mc501cb_Initial, mc501cb_InitialScale},
+ [SENSOR_MT9V111_1] =
+ {mt9v111_1_Initial, mt9v111_1_InitialScale},
+ [SENSOR_MT9V111_3] =
+ {mt9v111_3_Initial, mt9v111_3_InitialScale},
+ [SENSOR_OV7620] =
+ {ov7620_Initial, ov7620_InitialScale},
+ [SENSOR_OV7630C] =
+ {ov7630c_Initial, ov7630c_InitialScale},
+ [SENSOR_PAS106] =
+ {pas106b_Initial, pas106b_InitialScale},
+ [SENSOR_PAS202B] =
+ {pas202b_Initial, pas202b_InitialScale},
+ [SENSOR_PB0330] =
+ {pb0330_Initial, pb0330_InitialScale},
+ [SENSOR_PO2030] =
+ {po2030_Initial, po2030_InitialScale},
+ [SENSOR_TAS5130C] =
+ {tas5130c_Initial, tas5130c_InitialScale},
+ };
+
+ /* create the JPEG header */
+ jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
+ 0x21); /* JPEG 422 */
+
+ mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+ switch (sd->sensor) {
+ case SENSOR_HV7131R:
+ zcxx_probeSensor(gspca_dev);
+ break;
+ case SENSOR_PAS106:
+ usb_exchange(gspca_dev, pas106b_Initial_com);
+ break;
+ }
+ usb_exchange(gspca_dev, init_tb[sd->sensor][mode]);
+
+ switch (sd->sensor) {
+ case SENSOR_ADCM2700:
+ case SENSOR_GC0305:
+ case SENSOR_OV7620:
+ case SENSOR_PO2030:
+ case SENSOR_TAS5130C:
+ case SENSOR_GC0303:
+/* msleep(100); * ?? */
+ reg_r(gspca_dev, 0x0002); /* --> 0x40 */
+ reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */
+ reg_w(gspca_dev, 0x15, 0x01ae);
+ if (sd->sensor == SENSOR_TAS5130C)
+ break;
+ reg_w(gspca_dev, 0x0d, 0x003a);
+ reg_w(gspca_dev, 0x02, 0x003b);
+ reg_w(gspca_dev, 0x00, 0x0038);
+ break;
+ case SENSOR_HV7131R:
+ case SENSOR_PAS202B:
+ reg_w(gspca_dev, 0x03, 0x003b);
+ reg_w(gspca_dev, 0x0c, 0x003a);
+ reg_w(gspca_dev, 0x0b, 0x0039);
+ if (sd->sensor == SENSOR_HV7131R)
+ reg_w(gspca_dev, 0x50, ZC3XX_R11D_GLOBALGAIN);
+ break;
+ }
+
+ setmatrix(gspca_dev);
+ switch (sd->sensor) {
+ case SENSOR_ADCM2700:
+ case SENSOR_OV7620:
+ reg_r(gspca_dev, 0x0008);
+ reg_w(gspca_dev, 0x00, 0x0008);
+ break;
+ case SENSOR_PAS202B:
+ case SENSOR_GC0305:
+ case SENSOR_HV7131R:
+ case SENSOR_TAS5130C:
+ reg_r(gspca_dev, 0x0008);
+ /* fall thru */
+ case SENSOR_PO2030:
+ reg_w(gspca_dev, 0x03, 0x0008);
+ break;
+ }
+ setsharpness(gspca_dev);
+
+ /* set the gamma tables when not set */
+ switch (sd->sensor) {
+ case SENSOR_CS2102K: /* gamma set in xxx_Initial */
+ case SENSOR_HDCS2020:
+ case SENSOR_OV7630C:
+ break;
+ default:
+ setcontrast(gspca_dev);
+ break;
+ }
+ setmatrix(gspca_dev); /* one more time? */
+ switch (sd->sensor) {
+ case SENSOR_OV7620:
+ case SENSOR_PAS202B:
+ reg_r(gspca_dev, 0x0180); /* from win */
+ reg_w(gspca_dev, 0x00, 0x0180);
+ break;
+ }
+ setquality(gspca_dev);
+ jpeg_set_qual(sd->jpeg_hdr, jpeg_qual[sd->reg08]);
+ setlightfreq(gspca_dev);
+
+ switch (sd->sensor) {
+ case SENSOR_ADCM2700:
+ reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */
+ reg_w(gspca_dev, 0x15, 0x01ae);
+ reg_w(gspca_dev, 0x02, 0x0180);
+ /* ms-win + */
+ reg_w(gspca_dev, 0x40, 0x0117);
+ break;
+ case SENSOR_HV7131R:
+ setexposure(gspca_dev);
+ reg_w(gspca_dev, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN);
+ break;
+ case SENSOR_GC0305:
+ case SENSOR_TAS5130C:
+ reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */
+ reg_w(gspca_dev, 0x15, 0x01ae);
+ /* fall thru */
+ case SENSOR_PAS202B:
+ case SENSOR_PO2030:
+/* reg_w(gspca_dev, 0x40, ZC3XX_R117_GGAIN); in win traces */
+ reg_r(gspca_dev, 0x0180);
+ break;
+ case SENSOR_OV7620:
+ reg_w(gspca_dev, 0x09, 0x01ad);
+ reg_w(gspca_dev, 0x15, 0x01ae);
+ i2c_read(gspca_dev, 0x13); /*fixme: returns 0xa3 */
+ i2c_write(gspca_dev, 0x13, 0xa3, 0x00);
+ /*fixme: returned value to send? */
+ reg_w(gspca_dev, 0x40, 0x0117);
+ reg_r(gspca_dev, 0x0180);
+ break;
+ }
+
+ setautogain(gspca_dev);
+
+ /* start the transfer update thread if needed */
+ if (gspca_dev->usb_err >= 0) {
+ switch (sd->sensor) {
+ case SENSOR_HV7131R:
+ case SENSOR_PAS202B:
+ sd->work_thread =
+ create_singlethread_workqueue(KBUILD_MODNAME);
+ queue_work(sd->work_thread, &sd->work);
+ break;
+ }
+ }
+
+ return gspca_dev->usb_err;
+}
+
+/* called on streamoff with alt 0 and on disconnect */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->work_thread != NULL) {
+ mutex_unlock(&gspca_dev->usb_lock);
+ destroy_workqueue(sd->work_thread);
+ mutex_lock(&gspca_dev->usb_lock);
+ sd->work_thread = NULL;
+ }
+ if (!gspca_dev->present)
+ return;
+ send_unknown(gspca_dev, sd->sensor);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data,
+ int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* check the JPEG end of frame */
+ if (len >= 3
+ && data[len - 3] == 0xff && data[len - 2] == 0xd9) {
+/*fixme: what does the last byte mean?*/
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ data, len - 1);
+ return;
+ }
+
+ /* check the JPEG start of a frame */
+ if (data[0] == 0xff && data[1] == 0xd8) {
+ /* put the JPEG header in the new frame */
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+
+ /* remove the webcam's header:
+ * ff d8 ff fe 00 0e 00 00 ss ss 00 01 ww ww hh hh pp pp
+ * - 'ss ss' is the frame sequence number (BE)
+ * - 'ww ww' and 'hh hh' are the window dimensions (BE)
+ * - 'pp pp' is the packet sequence number (BE)
+ */
+ data += 18;
+ len -= 18;
+ }
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->ctrls[AUTOGAIN].val = val;
+ if (val) {
+ gspca_dev->ctrl_inac |= (1 << EXPOSURE);
+ } else {
+ gspca_dev->ctrl_inac &= ~(1 << EXPOSURE);
+ if (gspca_dev->streaming)
+ getexposure(gspca_dev);
+ }
+ if (gspca_dev->streaming)
+ setautogain(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_querymenu(struct gspca_dev *gspca_dev,
+ struct v4l2_querymenu *menu)
+{
+ switch (menu->id) {
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ switch (menu->index) {
+ case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
+ strcpy((char *) menu->name, "NoFliker");
+ return 0;
+ case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
+ strcpy((char *) menu->name, "50 Hz");
+ return 0;
+ case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
+ strcpy((char *) menu->name, "60 Hz");
+ return 0;
+ }
+ break;
+ }
+ return -EINVAL;
+}
+
+static int sd_setquality(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(jpeg_qual) - 1; i++) {
+ if (val <= jpeg_qual[i])
+ break;
+ }
+ if (i > 0
+ && i == sd->reg08
+ && val < jpeg_qual[sd->reg08])
+ i--;
+ sd->reg08 = i;
+ sd->ctrls[QUALITY].val = jpeg_qual[i];
+ if (gspca_dev->streaming)
+ jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val);
+ return gspca_dev->usb_err;
+}
+
+static int sd_set_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd_setquality(gspca_dev, jcomp->quality);
+ jcomp->quality = sd->ctrls[QUALITY].val;
+ return gspca_dev->usb_err;
+}
+
+static int sd_get_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ memset(jcomp, 0, sizeof *jcomp);
+ jcomp->quality = sd->ctrls[QUALITY].val;
+ jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
+ | V4L2_JPEG_MARKER_DQT;
+ return 0;
+}
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* interrupt packet data */
+ int len) /* interrput packet length */
+{
+ if (len == 8 && data[4] == 1) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+ input_sync(gspca_dev->input_dev);
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ }
+
+ return 0;
+}
+#endif
+
+static const struct sd_desc sd_desc = {
+ .name = KBUILD_MODNAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+ .querymenu = sd_querymenu,
+ .get_jcomp = sd_get_jcomp,
+ .set_jcomp = sd_set_jcomp,
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ .int_pkt_scan = sd_int_pkt_scan,
+#endif
+};
+
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x03f0, 0x1b07)},
+ {USB_DEVICE(0x041e, 0x041e)},
+ {USB_DEVICE(0x041e, 0x4017)},
+ {USB_DEVICE(0x041e, 0x401c), .driver_info = SENSOR_PAS106},
+ {USB_DEVICE(0x041e, 0x401e)},
+ {USB_DEVICE(0x041e, 0x401f)},
+ {USB_DEVICE(0x041e, 0x4022)},
+ {USB_DEVICE(0x041e, 0x4029)},
+ {USB_DEVICE(0x041e, 0x4034), .driver_info = SENSOR_PAS106},
+ {USB_DEVICE(0x041e, 0x4035), .driver_info = SENSOR_PAS106},
+ {USB_DEVICE(0x041e, 0x4036)},
+ {USB_DEVICE(0x041e, 0x403a)},
+ {USB_DEVICE(0x041e, 0x4051), .driver_info = SENSOR_GC0303},
+ {USB_DEVICE(0x041e, 0x4053), .driver_info = SENSOR_GC0303},
+ {USB_DEVICE(0x0458, 0x7007)},
+ {USB_DEVICE(0x0458, 0x700c)},
+ {USB_DEVICE(0x0458, 0x700f)},
+ {USB_DEVICE(0x0461, 0x0a00)},
+ {USB_DEVICE(0x046d, 0x089d), .driver_info = SENSOR_MC501CB},
+ {USB_DEVICE(0x046d, 0x08a0)},
+ {USB_DEVICE(0x046d, 0x08a1)},
+ {USB_DEVICE(0x046d, 0x08a2)},
+ {USB_DEVICE(0x046d, 0x08a3)},
+ {USB_DEVICE(0x046d, 0x08a6)},
+ {USB_DEVICE(0x046d, 0x08a7)},
+ {USB_DEVICE(0x046d, 0x08a9)},
+ {USB_DEVICE(0x046d, 0x08aa)},
+ {USB_DEVICE(0x046d, 0x08ac)},
+ {USB_DEVICE(0x046d, 0x08ad)},
+ {USB_DEVICE(0x046d, 0x08ae)},
+ {USB_DEVICE(0x046d, 0x08af)},
+ {USB_DEVICE(0x046d, 0x08b9)},
+ {USB_DEVICE(0x046d, 0x08d7)},
+ {USB_DEVICE(0x046d, 0x08d8)},
+ {USB_DEVICE(0x046d, 0x08d9)},
+ {USB_DEVICE(0x046d, 0x08da)},
+ {USB_DEVICE(0x046d, 0x08dd), .driver_info = SENSOR_MC501CB},
+ {USB_DEVICE(0x0471, 0x0325), .driver_info = SENSOR_PAS106},
+ {USB_DEVICE(0x0471, 0x0326), .driver_info = SENSOR_PAS106},
+ {USB_DEVICE(0x0471, 0x032d), .driver_info = SENSOR_PAS106},
+ {USB_DEVICE(0x0471, 0x032e), .driver_info = SENSOR_PAS106},
+ {USB_DEVICE(0x055f, 0xc005)},
+ {USB_DEVICE(0x055f, 0xd003)},
+ {USB_DEVICE(0x055f, 0xd004)},
+ {USB_DEVICE(0x0698, 0x2003)},
+ {USB_DEVICE(0x0ac8, 0x0301), .driver_info = SENSOR_PAS106},
+ {USB_DEVICE(0x0ac8, 0x0302), .driver_info = SENSOR_PAS106},
+ {USB_DEVICE(0x0ac8, 0x301b)},
+ {USB_DEVICE(0x0ac8, 0x303b)},
+ {USB_DEVICE(0x0ac8, 0x305b)},
+ {USB_DEVICE(0x0ac8, 0x307b)},
+ {USB_DEVICE(0x10fd, 0x0128)},
+ {USB_DEVICE(0x10fd, 0x804d)},
+ {USB_DEVICE(0x10fd, 0x8050)},
+ {} /* end of entry */
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+/* USB driver */
+static struct usb_driver sd_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
+
+module_param(force_sensor, int, 0644);
+MODULE_PARM_DESC(force_sensor,
+ "Force sensor. Only for experts!!!");
diff --git a/drivers/media/video/hdpvr/Kconfig b/drivers/media/video/hdpvr/Kconfig
new file mode 100644
index 00000000..de247f3c
--- /dev/null
+++ b/drivers/media/video/hdpvr/Kconfig
@@ -0,0 +1,10 @@
+
+config VIDEO_HDPVR
+ tristate "Hauppauge HD PVR support"
+ depends on VIDEO_DEV
+ ---help---
+ This is a video4linux driver for Hauppauge's HD PVR USB device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called hdpvr
+
diff --git a/drivers/media/video/hdpvr/Makefile b/drivers/media/video/hdpvr/Makefile
new file mode 100644
index 00000000..52f057f2
--- /dev/null
+++ b/drivers/media/video/hdpvr/Makefile
@@ -0,0 +1,7 @@
+hdpvr-objs := hdpvr-control.o hdpvr-core.o hdpvr-video.o hdpvr-i2c.o
+
+obj-$(CONFIG_VIDEO_HDPVR) += hdpvr.o
+
+ccflags-y += -Idrivers/media/video
+
+ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/video/hdpvr/hdpvr-control.c b/drivers/media/video/hdpvr/hdpvr-control.c
new file mode 100644
index 00000000..068df4ba
--- /dev/null
+++ b/drivers/media/video/hdpvr/hdpvr-control.c
@@ -0,0 +1,198 @@
+/*
+ * Hauppauge HD PVR USB driver - video 4 linux 2 interface
+ *
+ * Copyright (C) 2008 Janne Grunau (j@jannau.net)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/mutex.h>
+
+#include <linux/videodev2.h>
+
+#include <media/v4l2-common.h>
+
+#include "hdpvr.h"
+
+
+int hdpvr_config_call(struct hdpvr_device *dev, uint value, u8 valbuf)
+{
+ int ret;
+ char request_type = 0x38, snd_request = 0x01;
+
+ mutex_lock(&dev->usbc_mutex);
+ dev->usbc_buf[0] = valbuf;
+ ret = usb_control_msg(dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ snd_request, 0x00 | request_type,
+ value, CTRL_DEFAULT_INDEX,
+ dev->usbc_buf, 1, 10000);
+
+ mutex_unlock(&dev->usbc_mutex);
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "config call request for value 0x%x returned %d\n", value,
+ ret);
+
+ return ret < 0 ? ret : 0;
+}
+
+struct hdpvr_video_info *get_video_info(struct hdpvr_device *dev)
+{
+ struct hdpvr_video_info *vidinf = NULL;
+#ifdef HDPVR_DEBUG
+ char print_buf[15];
+#endif
+ int ret;
+
+ vidinf = kzalloc(sizeof(struct hdpvr_video_info), GFP_KERNEL);
+ if (!vidinf) {
+ v4l2_err(&dev->v4l2_dev, "out of memory\n");
+ goto err;
+ }
+
+ mutex_lock(&dev->usbc_mutex);
+ ret = usb_control_msg(dev->udev,
+ usb_rcvctrlpipe(dev->udev, 0),
+ 0x81, 0x80 | 0x38,
+ 0x1400, 0x0003,
+ dev->usbc_buf, 5,
+ 1000);
+ if (ret == 5) {
+ vidinf->width = dev->usbc_buf[1] << 8 | dev->usbc_buf[0];
+ vidinf->height = dev->usbc_buf[3] << 8 | dev->usbc_buf[2];
+ vidinf->fps = dev->usbc_buf[4];
+ }
+
+#ifdef HDPVR_DEBUG
+ if (hdpvr_debug & MSG_INFO) {
+ hex_dump_to_buffer(dev->usbc_buf, 5, 16, 1, print_buf,
+ sizeof(print_buf), 0);
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "get video info returned: %d, %s\n", ret, print_buf);
+ }
+#endif
+ mutex_unlock(&dev->usbc_mutex);
+
+ if (!vidinf->width || !vidinf->height || !vidinf->fps) {
+ kfree(vidinf);
+ vidinf = NULL;
+ }
+err:
+ return vidinf;
+}
+
+int get_input_lines_info(struct hdpvr_device *dev)
+{
+#ifdef HDPVR_DEBUG
+ char print_buf[9];
+#endif
+ int ret, lines;
+
+ mutex_lock(&dev->usbc_mutex);
+ ret = usb_control_msg(dev->udev,
+ usb_rcvctrlpipe(dev->udev, 0),
+ 0x81, 0x80 | 0x38,
+ 0x1800, 0x0003,
+ dev->usbc_buf, 3,
+ 1000);
+
+#ifdef HDPVR_DEBUG
+ if (hdpvr_debug & MSG_INFO) {
+ hex_dump_to_buffer(dev->usbc_buf, 3, 16, 1, print_buf,
+ sizeof(print_buf), 0);
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "get input lines info returned: %d, %s\n", ret,
+ print_buf);
+ }
+#endif
+ lines = dev->usbc_buf[1] << 8 | dev->usbc_buf[0];
+ mutex_unlock(&dev->usbc_mutex);
+ return lines;
+}
+
+
+int hdpvr_set_bitrate(struct hdpvr_device *dev)
+{
+ int ret;
+
+ mutex_lock(&dev->usbc_mutex);
+ memset(dev->usbc_buf, 0, 4);
+ dev->usbc_buf[0] = dev->options.bitrate;
+ dev->usbc_buf[2] = dev->options.peak_bitrate;
+
+ ret = usb_control_msg(dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ 0x01, 0x38, CTRL_BITRATE_VALUE,
+ CTRL_DEFAULT_INDEX, dev->usbc_buf, 4, 1000);
+ mutex_unlock(&dev->usbc_mutex);
+
+ return ret;
+}
+
+int hdpvr_set_audio(struct hdpvr_device *dev, u8 input,
+ enum v4l2_mpeg_audio_encoding codec)
+{
+ int ret = 0;
+
+ if (dev->flags & HDPVR_FLAG_AC3_CAP) {
+ mutex_lock(&dev->usbc_mutex);
+ memset(dev->usbc_buf, 0, 2);
+ dev->usbc_buf[0] = input;
+ if (codec == V4L2_MPEG_AUDIO_ENCODING_AAC)
+ dev->usbc_buf[1] = 0;
+ else if (codec == V4L2_MPEG_AUDIO_ENCODING_AC3)
+ dev->usbc_buf[1] = 1;
+ else {
+ mutex_unlock(&dev->usbc_mutex);
+ v4l2_err(&dev->v4l2_dev, "invalid audio codec %d\n",
+ codec);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ ret = usb_control_msg(dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ 0x01, 0x38, CTRL_AUDIO_INPUT_VALUE,
+ CTRL_DEFAULT_INDEX, dev->usbc_buf, 2,
+ 1000);
+ mutex_unlock(&dev->usbc_mutex);
+ if (ret == 2)
+ ret = 0;
+ } else
+ ret = hdpvr_config_call(dev, CTRL_AUDIO_INPUT_VALUE, input);
+error:
+ return ret;
+}
+
+int hdpvr_set_options(struct hdpvr_device *dev)
+{
+ hdpvr_config_call(dev, CTRL_VIDEO_STD_TYPE, dev->options.video_std);
+
+ hdpvr_config_call(dev, CTRL_VIDEO_INPUT_VALUE,
+ dev->options.video_input+1);
+
+ hdpvr_set_audio(dev, dev->options.audio_input+1,
+ dev->options.audio_codec);
+
+ hdpvr_set_bitrate(dev);
+ hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE,
+ dev->options.bitrate_mode);
+ hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, dev->options.gop_mode);
+
+ hdpvr_config_call(dev, CTRL_BRIGHTNESS, dev->options.brightness);
+ hdpvr_config_call(dev, CTRL_CONTRAST, dev->options.contrast);
+ hdpvr_config_call(dev, CTRL_HUE, dev->options.hue);
+ hdpvr_config_call(dev, CTRL_SATURATION, dev->options.saturation);
+ hdpvr_config_call(dev, CTRL_SHARPNESS, dev->options.sharpness);
+
+ return 0;
+}
diff --git a/drivers/media/video/hdpvr/hdpvr-core.c b/drivers/media/video/hdpvr/hdpvr-core.c
new file mode 100644
index 00000000..6510110f
--- /dev/null
+++ b/drivers/media/video/hdpvr/hdpvr-core.c
@@ -0,0 +1,472 @@
+/*
+ * Hauppauge HD PVR USB driver
+ *
+ * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2008 Janne Grunau (j@jannau.net)
+ * Copyright (C) 2008 John Poet
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/atomic.h>
+#include <linux/usb.h>
+#include <linux/mutex.h>
+#include <linux/i2c.h>
+
+#include <linux/videodev2.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-common.h>
+
+#include "hdpvr.h"
+
+static int video_nr[HDPVR_MAX] = {[0 ... (HDPVR_MAX - 1)] = UNSET};
+module_param_array(video_nr, int, NULL, 0);
+MODULE_PARM_DESC(video_nr, "video device number (-1=Auto)");
+
+/* holds the number of currently registered devices */
+static atomic_t dev_nr = ATOMIC_INIT(-1);
+
+int hdpvr_debug;
+module_param(hdpvr_debug, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(hdpvr_debug, "enable debugging output");
+
+static uint default_video_input = HDPVR_VIDEO_INPUTS;
+module_param(default_video_input, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(default_video_input, "default video input: 0=Component / "
+ "1=S-Video / 2=Composite");
+
+static uint default_audio_input = HDPVR_AUDIO_INPUTS;
+module_param(default_audio_input, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(default_audio_input, "default audio input: 0=RCA back / "
+ "1=RCA front / 2=S/PDIF");
+
+static bool boost_audio;
+module_param(boost_audio, bool, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(boost_audio, "boost the audio signal");
+
+
+/* table of devices that work with this driver */
+static struct usb_device_id hdpvr_table[] = {
+ { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID) },
+ { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID1) },
+ { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID2) },
+ { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID3) },
+ { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID4) },
+ { } /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, hdpvr_table);
+
+
+void hdpvr_delete(struct hdpvr_device *dev)
+{
+ hdpvr_free_buffers(dev);
+
+ if (dev->video_dev)
+ video_device_release(dev->video_dev);
+
+ usb_put_dev(dev->udev);
+}
+
+static void challenge(u8 *bytes)
+{
+ u64 *i64P, tmp64;
+ uint i, idx;
+
+ for (idx = 0; idx < 32; ++idx) {
+
+ if (idx & 0x3)
+ bytes[(idx >> 3) + 3] = bytes[(idx >> 2) & 0x3];
+
+ switch (idx & 0x3) {
+ case 0x3:
+ bytes[2] += bytes[3] * 4 + bytes[4] + bytes[5];
+ bytes[4] += bytes[(idx & 0x1) * 2] * 9 + 9;
+ break;
+ case 0x1:
+ bytes[0] *= 8;
+ bytes[0] += 7*idx + 4;
+ bytes[6] += bytes[3] * 3;
+ break;
+ case 0x0:
+ bytes[3 - (idx >> 3)] = bytes[idx >> 2];
+ bytes[5] += bytes[6] * 3;
+ for (i = 0; i < 3; i++)
+ bytes[3] *= bytes[3] + 1;
+ break;
+ case 0x2:
+ for (i = 0; i < 3; i++)
+ bytes[1] *= bytes[6] + 1;
+ for (i = 0; i < 3; i++) {
+ i64P = (u64 *)bytes;
+ tmp64 = le64_to_cpup(i64P);
+ tmp64 <<= bytes[7] & 0x0f;
+ *i64P += cpu_to_le64(tmp64);
+ }
+ break;
+ }
+ }
+}
+
+/* try to init the device like the windows driver */
+static int device_authorization(struct hdpvr_device *dev)
+{
+
+ int ret, retval = -ENOMEM;
+ char request_type = 0x38, rcv_request = 0x81;
+ char *response;
+#ifdef HDPVR_DEBUG
+ size_t buf_size = 46;
+ char *print_buf = kzalloc(5*buf_size+1, GFP_KERNEL);
+ if (!print_buf) {
+ v4l2_err(&dev->v4l2_dev, "Out of memory\n");
+ return retval;
+ }
+#endif
+
+ mutex_lock(&dev->usbc_mutex);
+ ret = usb_control_msg(dev->udev,
+ usb_rcvctrlpipe(dev->udev, 0),
+ rcv_request, 0x80 | request_type,
+ 0x0400, 0x0003,
+ dev->usbc_buf, 46,
+ 10000);
+ if (ret != 46) {
+ v4l2_err(&dev->v4l2_dev,
+ "unexpected answer of status request, len %d\n", ret);
+ goto unlock;
+ }
+#ifdef HDPVR_DEBUG
+ else {
+ hex_dump_to_buffer(dev->usbc_buf, 46, 16, 1, print_buf,
+ 5*buf_size+1, 0);
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "Status request returned, len %d: %s\n",
+ ret, print_buf);
+ }
+#endif
+
+ dev->fw_ver = dev->usbc_buf[1];
+
+ v4l2_info(&dev->v4l2_dev, "firmware version 0x%x dated %s\n",
+ dev->fw_ver, &dev->usbc_buf[2]);
+
+ if (dev->fw_ver > 0x15) {
+ dev->options.brightness = 0x80;
+ dev->options.contrast = 0x40;
+ dev->options.hue = 0xf;
+ dev->options.saturation = 0x40;
+ dev->options.sharpness = 0x80;
+ }
+
+ switch (dev->fw_ver) {
+ case HDPVR_FIRMWARE_VERSION:
+ dev->flags &= ~HDPVR_FLAG_AC3_CAP;
+ break;
+ case HDPVR_FIRMWARE_VERSION_AC3:
+ case HDPVR_FIRMWARE_VERSION_0X12:
+ case HDPVR_FIRMWARE_VERSION_0X15:
+ dev->flags |= HDPVR_FLAG_AC3_CAP;
+ break;
+ default:
+ v4l2_info(&dev->v4l2_dev, "untested firmware, the driver might"
+ " not work.\n");
+ if (dev->fw_ver >= HDPVR_FIRMWARE_VERSION_AC3)
+ dev->flags |= HDPVR_FLAG_AC3_CAP;
+ else
+ dev->flags &= ~HDPVR_FLAG_AC3_CAP;
+ }
+
+ response = dev->usbc_buf+38;
+#ifdef HDPVR_DEBUG
+ hex_dump_to_buffer(response, 8, 16, 1, print_buf, 5*buf_size+1, 0);
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, "challenge: %s\n",
+ print_buf);
+#endif
+ challenge(response);
+#ifdef HDPVR_DEBUG
+ hex_dump_to_buffer(response, 8, 16, 1, print_buf, 5*buf_size+1, 0);
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, " response: %s\n",
+ print_buf);
+#endif
+
+ msleep(100);
+ ret = usb_control_msg(dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ 0xd1, 0x00 | request_type,
+ 0x0000, 0x0000,
+ response, 8,
+ 10000);
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "magic request returned %d\n", ret);
+
+ retval = ret != 8;
+unlock:
+ mutex_unlock(&dev->usbc_mutex);
+ return retval;
+}
+
+static int hdpvr_device_init(struct hdpvr_device *dev)
+{
+ int ret;
+ u8 *buf;
+ struct hdpvr_video_info *vidinf;
+
+ if (device_authorization(dev))
+ return -EACCES;
+
+ /* default options for init */
+ hdpvr_set_options(dev);
+
+ /* set filter options */
+ mutex_lock(&dev->usbc_mutex);
+ buf = dev->usbc_buf;
+ buf[0] = 0x03; buf[1] = 0x03; buf[2] = 0x00; buf[3] = 0x00;
+ ret = usb_control_msg(dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ 0x01, 0x38,
+ CTRL_LOW_PASS_FILTER_VALUE, CTRL_DEFAULT_INDEX,
+ buf, 4,
+ 1000);
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "control request returned %d\n", ret);
+ mutex_unlock(&dev->usbc_mutex);
+
+ vidinf = get_video_info(dev);
+ if (!vidinf)
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "no valid video signal or device init failed\n");
+ else
+ kfree(vidinf);
+
+ /* enable fan and bling leds */
+ mutex_lock(&dev->usbc_mutex);
+ buf[0] = 0x1;
+ ret = usb_control_msg(dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ 0xd4, 0x38, 0, 0, buf, 1,
+ 1000);
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "control request returned %d\n", ret);
+
+ /* boost analog audio */
+ buf[0] = boost_audio;
+ ret = usb_control_msg(dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ 0xd5, 0x38, 0, 0, buf, 1,
+ 1000);
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "control request returned %d\n", ret);
+ mutex_unlock(&dev->usbc_mutex);
+
+ dev->status = STATUS_IDLE;
+ return 0;
+}
+
+static const struct hdpvr_options hdpvr_default_options = {
+ .video_std = HDPVR_60HZ,
+ .video_input = HDPVR_COMPONENT,
+ .audio_input = HDPVR_RCA_BACK,
+ .bitrate = 65, /* 6 mbps */
+ .peak_bitrate = 90, /* 9 mbps */
+ .bitrate_mode = HDPVR_CONSTANT,
+ .gop_mode = HDPVR_SIMPLE_IDR_GOP,
+ .audio_codec = V4L2_MPEG_AUDIO_ENCODING_AAC,
+ /* original picture controls for firmware version <= 0x15 */
+ /* updated in device_authorization() for newer firmware */
+ .brightness = 0x86,
+ .contrast = 0x80,
+ .hue = 0x80,
+ .saturation = 0x80,
+ .sharpness = 0x80,
+};
+
+static int hdpvr_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct hdpvr_device *dev;
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ struct i2c_client *client;
+ size_t buffer_size;
+ int i;
+ int retval = -ENOMEM;
+
+ /* allocate memory for our device state and initialize it */
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ err("Out of memory");
+ goto error;
+ }
+
+ dev->workqueue = 0;
+
+ /* register v4l2_device early so it can be used for printks */
+ if (v4l2_device_register(&interface->dev, &dev->v4l2_dev)) {
+ err("v4l2_device_register failed");
+ goto error;
+ }
+
+ mutex_init(&dev->io_mutex);
+ mutex_init(&dev->i2c_mutex);
+ mutex_init(&dev->usbc_mutex);
+ dev->usbc_buf = kmalloc(64, GFP_KERNEL);
+ if (!dev->usbc_buf) {
+ v4l2_err(&dev->v4l2_dev, "Out of memory\n");
+ goto error;
+ }
+
+ init_waitqueue_head(&dev->wait_buffer);
+ init_waitqueue_head(&dev->wait_data);
+
+ dev->workqueue = create_singlethread_workqueue("hdpvr_buffer");
+ if (!dev->workqueue)
+ goto error;
+
+ /* init video transfer queues */
+ INIT_LIST_HEAD(&dev->free_buff_list);
+ INIT_LIST_HEAD(&dev->rec_buff_list);
+
+ dev->options = hdpvr_default_options;
+
+ if (default_video_input < HDPVR_VIDEO_INPUTS)
+ dev->options.video_input = default_video_input;
+
+ if (default_audio_input < HDPVR_AUDIO_INPUTS) {
+ dev->options.audio_input = default_audio_input;
+ if (default_audio_input == HDPVR_SPDIF)
+ dev->options.audio_codec =
+ V4L2_MPEG_AUDIO_ENCODING_AC3;
+ }
+
+ dev->udev = usb_get_dev(interface_to_usbdev(interface));
+
+ /* set up the endpoint information */
+ /* use only the first bulk-in and bulk-out endpoints */
+ iface_desc = interface->cur_altsetting;
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+
+ if (!dev->bulk_in_endpointAddr &&
+ usb_endpoint_is_bulk_in(endpoint)) {
+ /* USB interface description is buggy, reported max
+ * packet size is 512 bytes, windows driver uses 8192 */
+ buffer_size = 8192;
+ dev->bulk_in_size = buffer_size;
+ dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
+ }
+
+ }
+ if (!dev->bulk_in_endpointAddr) {
+ v4l2_err(&dev->v4l2_dev, "Could not find bulk-in endpoint\n");
+ goto error;
+ }
+
+ /* init the device */
+ if (hdpvr_device_init(dev)) {
+ v4l2_err(&dev->v4l2_dev, "device init failed\n");
+ goto error;
+ }
+
+ mutex_lock(&dev->io_mutex);
+ if (hdpvr_alloc_buffers(dev, NUM_BUFFERS)) {
+ mutex_unlock(&dev->io_mutex);
+ v4l2_err(&dev->v4l2_dev,
+ "allocating transfer buffers failed\n");
+ goto error;
+ }
+ mutex_unlock(&dev->io_mutex);
+
+ if (hdpvr_register_videodev(dev, &interface->dev,
+ video_nr[atomic_inc_return(&dev_nr)])) {
+ v4l2_err(&dev->v4l2_dev, "registering videodev failed\n");
+ goto error;
+ }
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ retval = hdpvr_register_i2c_adapter(dev);
+ if (retval < 0) {
+ v4l2_err(&dev->v4l2_dev, "i2c adapter register failed\n");
+ goto error;
+ }
+
+ client = hdpvr_register_ir_rx_i2c(dev);
+ if (!client) {
+ v4l2_err(&dev->v4l2_dev, "i2c IR RX device register failed\n");
+ goto reg_fail;
+ }
+
+ client = hdpvr_register_ir_tx_i2c(dev);
+ if (!client) {
+ v4l2_err(&dev->v4l2_dev, "i2c IR TX device register failed\n");
+ goto reg_fail;
+ }
+#endif
+
+ /* let the user know what node this device is now attached to */
+ v4l2_info(&dev->v4l2_dev, "device now attached to %s\n",
+ video_device_node_name(dev->video_dev));
+ return 0;
+
+reg_fail:
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_del_adapter(&dev->i2c_adapter);
+#endif
+error:
+ if (dev) {
+ /* Destroy single thread */
+ if (dev->workqueue)
+ destroy_workqueue(dev->workqueue);
+ /* this frees allocated memory */
+ hdpvr_delete(dev);
+ }
+ return retval;
+}
+
+static void hdpvr_disconnect(struct usb_interface *interface)
+{
+ struct hdpvr_device *dev = to_hdpvr_dev(usb_get_intfdata(interface));
+
+ v4l2_info(&dev->v4l2_dev, "device %s disconnected\n",
+ video_device_node_name(dev->video_dev));
+ /* prevent more I/O from starting and stop any ongoing */
+ mutex_lock(&dev->io_mutex);
+ dev->status = STATUS_DISCONNECTED;
+ wake_up_interruptible(&dev->wait_data);
+ wake_up_interruptible(&dev->wait_buffer);
+ mutex_unlock(&dev->io_mutex);
+ v4l2_device_disconnect(&dev->v4l2_dev);
+ msleep(100);
+ flush_workqueue(dev->workqueue);
+ mutex_lock(&dev->io_mutex);
+ hdpvr_cancel_queue(dev);
+ mutex_unlock(&dev->io_mutex);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_del_adapter(&dev->i2c_adapter);
+#endif
+ video_unregister_device(dev->video_dev);
+ atomic_dec(&dev_nr);
+}
+
+
+static struct usb_driver hdpvr_usb_driver = {
+ .name = "hdpvr",
+ .probe = hdpvr_probe,
+ .disconnect = hdpvr_disconnect,
+ .id_table = hdpvr_table,
+};
+
+module_usb_driver(hdpvr_usb_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.2.1");
+MODULE_AUTHOR("Janne Grunau");
+MODULE_DESCRIPTION("Hauppauge HD PVR driver");
diff --git a/drivers/media/video/hdpvr/hdpvr-i2c.c b/drivers/media/video/hdpvr/hdpvr-i2c.c
new file mode 100644
index 00000000..82e819fa
--- /dev/null
+++ b/drivers/media/video/hdpvr/hdpvr-i2c.c
@@ -0,0 +1,231 @@
+
+/*
+ * Hauppauge HD PVR USB driver
+ *
+ * Copyright (C) 2008 Janne Grunau (j@jannau.net)
+ *
+ * IR device registration code is
+ * Copyright (C) 2010 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+
+#include "hdpvr.h"
+
+#define CTRL_READ_REQUEST 0xb8
+#define CTRL_WRITE_REQUEST 0x38
+
+#define REQTYPE_I2C_READ 0xb1
+#define REQTYPE_I2C_WRITE 0xb0
+#define REQTYPE_I2C_WRITE_STATT 0xd0
+
+#define Z8F0811_IR_TX_I2C_ADDR 0x70
+#define Z8F0811_IR_RX_I2C_ADDR 0x71
+
+
+struct i2c_client *hdpvr_register_ir_tx_i2c(struct hdpvr_device *dev)
+{
+ struct IR_i2c_init_data *init_data = &dev->ir_i2c_init_data;
+ struct i2c_board_info hdpvr_ir_tx_i2c_board_info = {
+ I2C_BOARD_INFO("ir_tx_z8f0811_hdpvr", Z8F0811_IR_TX_I2C_ADDR),
+ };
+
+ init_data->name = "HD-PVR";
+ hdpvr_ir_tx_i2c_board_info.platform_data = init_data;
+
+ return i2c_new_device(&dev->i2c_adapter, &hdpvr_ir_tx_i2c_board_info);
+}
+
+struct i2c_client *hdpvr_register_ir_rx_i2c(struct hdpvr_device *dev)
+{
+ struct IR_i2c_init_data *init_data = &dev->ir_i2c_init_data;
+ struct i2c_board_info hdpvr_ir_rx_i2c_board_info = {
+ I2C_BOARD_INFO("ir_rx_z8f0811_hdpvr", Z8F0811_IR_RX_I2C_ADDR),
+ };
+
+ /* Our default information for ir-kbd-i2c.c to use */
+ init_data->ir_codes = RC_MAP_HAUPPAUGE;
+ init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR;
+ init_data->type = RC_TYPE_RC5;
+ init_data->name = "HD-PVR";
+ init_data->polling_interval = 405; /* ms, duplicated from Windows */
+ hdpvr_ir_rx_i2c_board_info.platform_data = init_data;
+
+ return i2c_new_device(&dev->i2c_adapter, &hdpvr_ir_rx_i2c_board_info);
+}
+
+static int hdpvr_i2c_read(struct hdpvr_device *dev, int bus,
+ unsigned char addr, char *wdata, int wlen,
+ char *data, int len)
+{
+ int ret;
+
+ if ((len > sizeof(dev->i2c_buf)) || (wlen > sizeof(dev->i2c_buf)))
+ return -EINVAL;
+
+ if (wlen) {
+ memcpy(&dev->i2c_buf, wdata, wlen);
+ ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
+ REQTYPE_I2C_WRITE, CTRL_WRITE_REQUEST,
+ (bus << 8) | addr, 0, &dev->i2c_buf,
+ wlen, 1000);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
+ REQTYPE_I2C_READ, CTRL_READ_REQUEST,
+ (bus << 8) | addr, 0, &dev->i2c_buf, len, 1000);
+
+ if (ret == len) {
+ memcpy(data, &dev->i2c_buf, len);
+ ret = 0;
+ } else if (ret >= 0)
+ ret = -EIO;
+
+ return ret;
+}
+
+static int hdpvr_i2c_write(struct hdpvr_device *dev, int bus,
+ unsigned char addr, char *data, int len)
+{
+ int ret;
+
+ if (len > sizeof(dev->i2c_buf))
+ return -EINVAL;
+
+ memcpy(&dev->i2c_buf, data, len);
+ ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
+ REQTYPE_I2C_WRITE, CTRL_WRITE_REQUEST,
+ (bus << 8) | addr, 0, &dev->i2c_buf, len, 1000);
+
+ if (ret < 0)
+ return ret;
+
+ ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
+ REQTYPE_I2C_WRITE_STATT, CTRL_READ_REQUEST,
+ 0, 0, &dev->i2c_buf, 2, 1000);
+
+ if ((ret == 2) && (dev->i2c_buf[1] == (len - 1)))
+ ret = 0;
+ else if (ret >= 0)
+ ret = -EIO;
+
+ return ret;
+}
+
+static int hdpvr_transfer(struct i2c_adapter *i2c_adapter, struct i2c_msg *msgs,
+ int num)
+{
+ struct hdpvr_device *dev = i2c_get_adapdata(i2c_adapter);
+ int retval = 0, addr;
+
+ if (num <= 0)
+ return 0;
+
+ mutex_lock(&dev->i2c_mutex);
+
+ addr = msgs[0].addr << 1;
+
+ if (num == 1) {
+ if (msgs[0].flags & I2C_M_RD)
+ retval = hdpvr_i2c_read(dev, 1, addr, NULL, 0,
+ msgs[0].buf, msgs[0].len);
+ else
+ retval = hdpvr_i2c_write(dev, 1, addr, msgs[0].buf,
+ msgs[0].len);
+ } else if (num == 2) {
+ if (msgs[0].addr != msgs[1].addr) {
+ v4l2_warn(&dev->v4l2_dev, "refusing 2-phase i2c xfer "
+ "with conflicting target addresses\n");
+ retval = -EINVAL;
+ goto out;
+ }
+
+ if ((msgs[0].flags & I2C_M_RD) || !(msgs[1].flags & I2C_M_RD)) {
+ v4l2_warn(&dev->v4l2_dev, "refusing complex xfer with "
+ "r0=%d, r1=%d\n", msgs[0].flags & I2C_M_RD,
+ msgs[1].flags & I2C_M_RD);
+ retval = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Write followed by atomic read is the only complex xfer that
+ * we actually support here.
+ */
+ retval = hdpvr_i2c_read(dev, 1, addr, msgs[0].buf, msgs[0].len,
+ msgs[1].buf, msgs[1].len);
+ } else {
+ v4l2_warn(&dev->v4l2_dev, "refusing %d-phase i2c xfer\n", num);
+ }
+
+out:
+ mutex_unlock(&dev->i2c_mutex);
+
+ return retval ? retval : num;
+}
+
+static u32 hdpvr_functionality(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static struct i2c_algorithm hdpvr_algo = {
+ .master_xfer = hdpvr_transfer,
+ .functionality = hdpvr_functionality,
+};
+
+static struct i2c_adapter hdpvr_i2c_adapter_template = {
+ .name = "Hauppage HD PVR I2C",
+ .owner = THIS_MODULE,
+ .algo = &hdpvr_algo,
+};
+
+static int hdpvr_activate_ir(struct hdpvr_device *dev)
+{
+ char buffer[2];
+
+ mutex_lock(&dev->i2c_mutex);
+
+ hdpvr_i2c_read(dev, 0, 0x54, NULL, 0, buffer, 1);
+
+ buffer[0] = 0;
+ buffer[1] = 0x8;
+ hdpvr_i2c_write(dev, 1, 0x54, buffer, 2);
+
+ buffer[1] = 0x18;
+ hdpvr_i2c_write(dev, 1, 0x54, buffer, 2);
+
+ mutex_unlock(&dev->i2c_mutex);
+
+ return 0;
+}
+
+int hdpvr_register_i2c_adapter(struct hdpvr_device *dev)
+{
+ int retval = -ENOMEM;
+
+ hdpvr_activate_ir(dev);
+
+ memcpy(&dev->i2c_adapter, &hdpvr_i2c_adapter_template,
+ sizeof(struct i2c_adapter));
+ dev->i2c_adapter.dev.parent = &dev->udev->dev;
+
+ i2c_set_adapdata(&dev->i2c_adapter, dev);
+
+ retval = i2c_add_adapter(&dev->i2c_adapter);
+
+ return retval;
+}
+
+#endif
diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c
new file mode 100644
index 00000000..11ffe9cc
--- /dev/null
+++ b/drivers/media/video/hdpvr/hdpvr-video.c
@@ -0,0 +1,1289 @@
+/*
+ * Hauppauge HD PVR USB driver - video 4 linux 2 interface
+ *
+ * Copyright (C) 2008 Janne Grunau (j@jannau.net)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+
+#include <linux/videodev2.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include "hdpvr.h"
+
+#define BULK_URB_TIMEOUT 90 /* 0.09 seconds */
+
+#define print_buffer_status() { \
+ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, \
+ "%s:%d buffer stat: %d free, %d proc\n", \
+ __func__, __LINE__, \
+ list_size(&dev->free_buff_list), \
+ list_size(&dev->rec_buff_list)); }
+
+struct hdpvr_fh {
+ struct hdpvr_device *dev;
+};
+
+static uint list_size(struct list_head *list)
+{
+ struct list_head *tmp;
+ uint count = 0;
+
+ list_for_each(tmp, list) {
+ count++;
+ }
+
+ return count;
+}
+
+/*=========================================================================*/
+/* urb callback */
+static void hdpvr_read_bulk_callback(struct urb *urb)
+{
+ struct hdpvr_buffer *buf = (struct hdpvr_buffer *)urb->context;
+ struct hdpvr_device *dev = buf->dev;
+
+ /* marking buffer as received and wake waiting */
+ buf->status = BUFSTAT_READY;
+ wake_up_interruptible(&dev->wait_data);
+}
+
+/*=========================================================================*/
+/* bufffer bits */
+
+/* function expects dev->io_mutex to be hold by caller */
+int hdpvr_cancel_queue(struct hdpvr_device *dev)
+{
+ struct hdpvr_buffer *buf;
+
+ list_for_each_entry(buf, &dev->rec_buff_list, buff_list) {
+ usb_kill_urb(buf->urb);
+ buf->status = BUFSTAT_AVAILABLE;
+ }
+
+ list_splice_init(&dev->rec_buff_list, dev->free_buff_list.prev);
+
+ return 0;
+}
+
+static int hdpvr_free_queue(struct list_head *q)
+{
+ struct list_head *tmp;
+ struct list_head *p;
+ struct hdpvr_buffer *buf;
+ struct urb *urb;
+
+ for (p = q->next; p != q;) {
+ buf = list_entry(p, struct hdpvr_buffer, buff_list);
+
+ urb = buf->urb;
+ usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+ urb->transfer_buffer, urb->transfer_dma);
+ usb_free_urb(urb);
+ tmp = p->next;
+ list_del(p);
+ kfree(buf);
+ p = tmp;
+ }
+
+ return 0;
+}
+
+/* function expects dev->io_mutex to be hold by caller */
+int hdpvr_free_buffers(struct hdpvr_device *dev)
+{
+ hdpvr_cancel_queue(dev);
+
+ hdpvr_free_queue(&dev->free_buff_list);
+ hdpvr_free_queue(&dev->rec_buff_list);
+
+ return 0;
+}
+
+/* function expects dev->io_mutex to be hold by caller */
+int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count)
+{
+ uint i;
+ int retval = -ENOMEM;
+ u8 *mem;
+ struct hdpvr_buffer *buf;
+ struct urb *urb;
+
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "allocating %u buffers\n", count);
+
+ for (i = 0; i < count; i++) {
+
+ buf = kzalloc(sizeof(struct hdpvr_buffer), GFP_KERNEL);
+ if (!buf) {
+ v4l2_err(&dev->v4l2_dev, "cannot allocate buffer\n");
+ goto exit;
+ }
+ buf->dev = dev;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ v4l2_err(&dev->v4l2_dev, "cannot allocate urb\n");
+ goto exit_urb;
+ }
+ buf->urb = urb;
+
+ mem = usb_alloc_coherent(dev->udev, dev->bulk_in_size, GFP_KERNEL,
+ &urb->transfer_dma);
+ if (!mem) {
+ v4l2_err(&dev->v4l2_dev,
+ "cannot allocate usb transfer buffer\n");
+ goto exit_urb_buffer;
+ }
+
+ usb_fill_bulk_urb(buf->urb, dev->udev,
+ usb_rcvbulkpipe(dev->udev,
+ dev->bulk_in_endpointAddr),
+ mem, dev->bulk_in_size,
+ hdpvr_read_bulk_callback, buf);
+
+ buf->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ buf->status = BUFSTAT_AVAILABLE;
+ list_add_tail(&buf->buff_list, &dev->free_buff_list);
+ }
+ return 0;
+exit_urb_buffer:
+ usb_free_urb(urb);
+exit_urb:
+ kfree(buf);
+exit:
+ hdpvr_free_buffers(dev);
+ return retval;
+}
+
+static int hdpvr_submit_buffers(struct hdpvr_device *dev)
+{
+ struct hdpvr_buffer *buf;
+ struct urb *urb;
+ int ret = 0, err_count = 0;
+
+ mutex_lock(&dev->io_mutex);
+
+ while (dev->status == STATUS_STREAMING &&
+ !list_empty(&dev->free_buff_list)) {
+
+ buf = list_entry(dev->free_buff_list.next, struct hdpvr_buffer,
+ buff_list);
+ if (buf->status != BUFSTAT_AVAILABLE) {
+ v4l2_err(&dev->v4l2_dev,
+ "buffer not marked as available\n");
+ ret = -EFAULT;
+ goto err;
+ }
+
+ urb = buf->urb;
+ urb->status = 0;
+ urb->actual_length = 0;
+ ret = usb_submit_urb(urb, GFP_KERNEL);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev,
+ "usb_submit_urb in %s returned %d\n",
+ __func__, ret);
+ if (++err_count > 2)
+ break;
+ continue;
+ }
+ buf->status = BUFSTAT_INPROGRESS;
+ list_move_tail(&buf->buff_list, &dev->rec_buff_list);
+ }
+err:
+ print_buffer_status();
+ mutex_unlock(&dev->io_mutex);
+ return ret;
+}
+
+static struct hdpvr_buffer *hdpvr_get_next_buffer(struct hdpvr_device *dev)
+{
+ struct hdpvr_buffer *buf;
+
+ mutex_lock(&dev->io_mutex);
+
+ if (list_empty(&dev->rec_buff_list)) {
+ mutex_unlock(&dev->io_mutex);
+ return NULL;
+ }
+
+ buf = list_entry(dev->rec_buff_list.next, struct hdpvr_buffer,
+ buff_list);
+ mutex_unlock(&dev->io_mutex);
+
+ return buf;
+}
+
+static void hdpvr_transmit_buffers(struct work_struct *work)
+{
+ struct hdpvr_device *dev = container_of(work, struct hdpvr_device,
+ worker);
+
+ while (dev->status == STATUS_STREAMING) {
+
+ if (hdpvr_submit_buffers(dev)) {
+ v4l2_err(&dev->v4l2_dev, "couldn't submit buffers\n");
+ goto error;
+ }
+ if (wait_event_interruptible(dev->wait_buffer,
+ !list_empty(&dev->free_buff_list) ||
+ dev->status != STATUS_STREAMING))
+ goto error;
+ }
+
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "transmit worker exited\n");
+ return;
+error:
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "transmit buffers errored\n");
+ dev->status = STATUS_ERROR;
+}
+
+/* function expects dev->io_mutex to be hold by caller */
+static int hdpvr_start_streaming(struct hdpvr_device *dev)
+{
+ int ret;
+ struct hdpvr_video_info *vidinf;
+
+ if (dev->status == STATUS_STREAMING)
+ return 0;
+ else if (dev->status != STATUS_IDLE)
+ return -EAGAIN;
+
+ vidinf = get_video_info(dev);
+
+ if (vidinf) {
+ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
+ "video signal: %dx%d@%dhz\n", vidinf->width,
+ vidinf->height, vidinf->fps);
+ kfree(vidinf);
+
+ /* start streaming 2 request */
+ ret = usb_control_msg(dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ 0xb8, 0x38, 0x1, 0, NULL, 0, 8000);
+ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
+ "encoder start control request returned %d\n", ret);
+
+ hdpvr_config_call(dev, CTRL_START_STREAMING_VALUE, 0x00);
+
+ dev->status = STATUS_STREAMING;
+
+ INIT_WORK(&dev->worker, hdpvr_transmit_buffers);
+ queue_work(dev->workqueue, &dev->worker);
+
+ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
+ "streaming started\n");
+
+ return 0;
+ }
+ msleep(250);
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "no video signal at input %d\n", dev->options.video_input);
+ return -EAGAIN;
+}
+
+
+/* function expects dev->io_mutex to be hold by caller */
+static int hdpvr_stop_streaming(struct hdpvr_device *dev)
+{
+ int actual_length;
+ uint c = 0;
+ u8 *buf;
+
+ if (dev->status == STATUS_IDLE)
+ return 0;
+ else if (dev->status != STATUS_STREAMING)
+ return -EAGAIN;
+
+ buf = kmalloc(dev->bulk_in_size, GFP_KERNEL);
+ if (!buf)
+ v4l2_err(&dev->v4l2_dev, "failed to allocate temporary buffer "
+ "for emptying the internal device buffer. "
+ "Next capture start will be slow\n");
+
+ dev->status = STATUS_SHUTTING_DOWN;
+ hdpvr_config_call(dev, CTRL_STOP_STREAMING_VALUE, 0x00);
+ mutex_unlock(&dev->io_mutex);
+
+ wake_up_interruptible(&dev->wait_buffer);
+ msleep(50);
+
+ flush_workqueue(dev->workqueue);
+
+ mutex_lock(&dev->io_mutex);
+ /* kill the still outstanding urbs */
+ hdpvr_cancel_queue(dev);
+
+ /* emptying the device buffer beforeshutting it down */
+ while (buf && ++c < 500 &&
+ !usb_bulk_msg(dev->udev,
+ usb_rcvbulkpipe(dev->udev,
+ dev->bulk_in_endpointAddr),
+ buf, dev->bulk_in_size, &actual_length,
+ BULK_URB_TIMEOUT)) {
+ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
+ "%2d: got %d bytes\n", c, actual_length);
+ }
+ kfree(buf);
+ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
+ "used %d urbs to empty device buffers\n", c-1);
+ msleep(10);
+
+ dev->status = STATUS_IDLE;
+
+ return 0;
+}
+
+
+/*=======================================================================*/
+/*
+ * video 4 linux 2 file operations
+ */
+
+static int hdpvr_open(struct file *file)
+{
+ struct hdpvr_device *dev;
+ struct hdpvr_fh *fh;
+ int retval = -ENOMEM;
+
+ dev = (struct hdpvr_device *)video_get_drvdata(video_devdata(file));
+ if (!dev) {
+ pr_err("open failing with with ENODEV\n");
+ retval = -ENODEV;
+ goto err;
+ }
+
+ fh = kzalloc(sizeof(struct hdpvr_fh), GFP_KERNEL);
+ if (!fh) {
+ v4l2_err(&dev->v4l2_dev, "Out of memory\n");
+ goto err;
+ }
+ /* lock the device to allow correctly handling errors
+ * in resumption */
+ mutex_lock(&dev->io_mutex);
+ dev->open_count++;
+ mutex_unlock(&dev->io_mutex);
+
+ fh->dev = dev;
+
+ /* save our object in the file's private structure */
+ file->private_data = fh;
+
+ retval = 0;
+err:
+ return retval;
+}
+
+static int hdpvr_release(struct file *file)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&dev->io_mutex);
+ if (!(--dev->open_count) && dev->status == STATUS_STREAMING)
+ hdpvr_stop_streaming(dev);
+
+ mutex_unlock(&dev->io_mutex);
+
+ return 0;
+}
+
+/*
+ * hdpvr_v4l2_read()
+ * will allocate buffers when called for the first time
+ */
+static ssize_t hdpvr_read(struct file *file, char __user *buffer, size_t count,
+ loff_t *pos)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+ struct hdpvr_buffer *buf = NULL;
+ struct urb *urb;
+ unsigned int ret = 0;
+ int rem, cnt;
+
+ if (*pos)
+ return -ESPIPE;
+
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&dev->io_mutex);
+ if (dev->status == STATUS_IDLE) {
+ if (hdpvr_start_streaming(dev)) {
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "start_streaming failed\n");
+ ret = -EIO;
+ msleep(200);
+ dev->status = STATUS_IDLE;
+ mutex_unlock(&dev->io_mutex);
+ goto err;
+ }
+ print_buffer_status();
+ }
+ mutex_unlock(&dev->io_mutex);
+
+ /* wait for the first buffer */
+ if (!(file->f_flags & O_NONBLOCK)) {
+ if (wait_event_interruptible(dev->wait_data,
+ hdpvr_get_next_buffer(dev)))
+ return -ERESTARTSYS;
+ }
+
+ buf = hdpvr_get_next_buffer(dev);
+
+ while (count > 0 && buf) {
+
+ if (buf->status != BUFSTAT_READY &&
+ dev->status != STATUS_DISCONNECTED) {
+ /* return nonblocking */
+ if (file->f_flags & O_NONBLOCK) {
+ if (!ret)
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ if (wait_event_interruptible(dev->wait_data,
+ buf->status == BUFSTAT_READY)) {
+ ret = -ERESTARTSYS;
+ goto err;
+ }
+ }
+
+ if (buf->status != BUFSTAT_READY)
+ break;
+
+ /* set remaining bytes to copy */
+ urb = buf->urb;
+ rem = urb->actual_length - buf->pos;
+ cnt = rem > count ? count : rem;
+
+ if (copy_to_user(buffer, urb->transfer_buffer + buf->pos,
+ cnt)) {
+ v4l2_err(&dev->v4l2_dev, "read: copy_to_user failed\n");
+ if (!ret)
+ ret = -EFAULT;
+ goto err;
+ }
+
+ buf->pos += cnt;
+ count -= cnt;
+ buffer += cnt;
+ ret += cnt;
+
+ /* finished, take next buffer */
+ if (buf->pos == urb->actual_length) {
+ mutex_lock(&dev->io_mutex);
+ buf->pos = 0;
+ buf->status = BUFSTAT_AVAILABLE;
+
+ list_move_tail(&buf->buff_list, &dev->free_buff_list);
+
+ print_buffer_status();
+
+ mutex_unlock(&dev->io_mutex);
+
+ wake_up_interruptible(&dev->wait_buffer);
+
+ buf = hdpvr_get_next_buffer(dev);
+ }
+ }
+err:
+ if (!ret && !buf)
+ ret = -EAGAIN;
+ return ret;
+}
+
+static unsigned int hdpvr_poll(struct file *filp, poll_table *wait)
+{
+ struct hdpvr_buffer *buf = NULL;
+ struct hdpvr_fh *fh = filp->private_data;
+ struct hdpvr_device *dev = fh->dev;
+ unsigned int mask = 0;
+
+ mutex_lock(&dev->io_mutex);
+
+ if (!video_is_registered(dev->video_dev)) {
+ mutex_unlock(&dev->io_mutex);
+ return -EIO;
+ }
+
+ if (dev->status == STATUS_IDLE) {
+ if (hdpvr_start_streaming(dev)) {
+ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
+ "start_streaming failed\n");
+ dev->status = STATUS_IDLE;
+ }
+
+ print_buffer_status();
+ }
+ mutex_unlock(&dev->io_mutex);
+
+ buf = hdpvr_get_next_buffer(dev);
+ /* only wait if no data is available */
+ if (!buf || buf->status != BUFSTAT_READY) {
+ poll_wait(filp, &dev->wait_data, wait);
+ buf = hdpvr_get_next_buffer(dev);
+ }
+ if (buf && buf->status == BUFSTAT_READY)
+ mask |= POLLIN | POLLRDNORM;
+
+ return mask;
+}
+
+
+static const struct v4l2_file_operations hdpvr_fops = {
+ .owner = THIS_MODULE,
+ .open = hdpvr_open,
+ .release = hdpvr_release,
+ .read = hdpvr_read,
+ .poll = hdpvr_poll,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+/*=======================================================================*/
+/*
+ * V4L2 ioctl handling
+ */
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct hdpvr_device *dev = video_drvdata(file);
+
+ strcpy(cap->driver, "hdpvr");
+ strcpy(cap->card, "Hauppauge HD PVR");
+ usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_AUDIO |
+ V4L2_CAP_READWRITE;
+ return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *private_data,
+ v4l2_std_id *std)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+ u8 std_type = 1;
+
+ if (*std & (V4L2_STD_NTSC | V4L2_STD_PAL_60))
+ std_type = 0;
+
+ return hdpvr_config_call(dev, CTRL_VIDEO_STD_TYPE, std_type);
+}
+
+static const char *iname[] = {
+ [HDPVR_COMPONENT] = "Component",
+ [HDPVR_SVIDEO] = "S-Video",
+ [HDPVR_COMPOSITE] = "Composite",
+};
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+ unsigned int n;
+
+ n = i->index;
+ if (n >= HDPVR_VIDEO_INPUTS)
+ return -EINVAL;
+
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+
+ strncpy(i->name, iname[n], sizeof(i->name) - 1);
+ i->name[sizeof(i->name) - 1] = '\0';
+
+ i->audioset = 1<<HDPVR_RCA_FRONT | 1<<HDPVR_RCA_BACK | 1<<HDPVR_SPDIF;
+
+ i->std = dev->video_dev->tvnorms;
+
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *private_data,
+ unsigned int index)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+ int retval;
+
+ if (index >= HDPVR_VIDEO_INPUTS)
+ return -EINVAL;
+
+ if (dev->status != STATUS_IDLE)
+ return -EAGAIN;
+
+ retval = hdpvr_config_call(dev, CTRL_VIDEO_INPUT_VALUE, index+1);
+ if (!retval)
+ dev->options.video_input = index;
+
+ return retval;
+}
+
+static int vidioc_g_input(struct file *file, void *private_data,
+ unsigned int *index)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+
+ *index = dev->options.video_input;
+ return 0;
+}
+
+
+static const char *audio_iname[] = {
+ [HDPVR_RCA_FRONT] = "RCA front",
+ [HDPVR_RCA_BACK] = "RCA back",
+ [HDPVR_SPDIF] = "SPDIF",
+};
+
+static int vidioc_enumaudio(struct file *file, void *priv,
+ struct v4l2_audio *audio)
+{
+ unsigned int n;
+
+ n = audio->index;
+ if (n >= HDPVR_AUDIO_INPUTS)
+ return -EINVAL;
+
+ audio->capability = V4L2_AUDCAP_STEREO;
+
+ strncpy(audio->name, audio_iname[n], sizeof(audio->name) - 1);
+ audio->name[sizeof(audio->name) - 1] = '\0';
+
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *private_data,
+ struct v4l2_audio *audio)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+ int retval;
+
+ if (audio->index >= HDPVR_AUDIO_INPUTS)
+ return -EINVAL;
+
+ if (dev->status != STATUS_IDLE)
+ return -EAGAIN;
+
+ retval = hdpvr_set_audio(dev, audio->index+1, dev->options.audio_codec);
+ if (!retval)
+ dev->options.audio_input = audio->index;
+
+ return retval;
+}
+
+static int vidioc_g_audio(struct file *file, void *private_data,
+ struct v4l2_audio *audio)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+
+ audio->index = dev->options.audio_input;
+ audio->capability = V4L2_AUDCAP_STEREO;
+ strncpy(audio->name, audio_iname[audio->index], sizeof(audio->name));
+ audio->name[sizeof(audio->name) - 1] = '\0';
+ return 0;
+}
+
+static const s32 supported_v4l2_ctrls[] = {
+ V4L2_CID_BRIGHTNESS,
+ V4L2_CID_CONTRAST,
+ V4L2_CID_SATURATION,
+ V4L2_CID_HUE,
+ V4L2_CID_SHARPNESS,
+ V4L2_CID_MPEG_AUDIO_ENCODING,
+ V4L2_CID_MPEG_VIDEO_ENCODING,
+ V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+ V4L2_CID_MPEG_VIDEO_BITRATE,
+ V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
+};
+
+static int fill_queryctrl(struct hdpvr_options *opt, struct v4l2_queryctrl *qc,
+ int ac3, int fw_ver)
+{
+ int err;
+
+ if (fw_ver > 0x15) {
+ switch (qc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
+ case V4L2_CID_CONTRAST:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x40);
+ case V4L2_CID_SATURATION:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x40);
+ case V4L2_CID_HUE:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0x1e, 1, 0xf);
+ case V4L2_CID_SHARPNESS:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
+ }
+ } else {
+ switch (qc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x86);
+ case V4L2_CID_CONTRAST:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
+ case V4L2_CID_SATURATION:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
+ case V4L2_CID_HUE:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
+ case V4L2_CID_SHARPNESS:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
+ }
+ }
+
+ switch (qc->id) {
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ return v4l2_ctrl_query_fill(
+ qc, V4L2_MPEG_AUDIO_ENCODING_AAC,
+ ac3 ? V4L2_MPEG_AUDIO_ENCODING_AC3
+ : V4L2_MPEG_AUDIO_ENCODING_AAC,
+ 1, V4L2_MPEG_AUDIO_ENCODING_AAC);
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ return v4l2_ctrl_query_fill(
+ qc, V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 1,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC);
+
+/* case V4L2_CID_MPEG_VIDEO_? maybe keyframe interval: */
+/* return v4l2_ctrl_query_fill(qc, 0, 128, 128, 0); */
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ return v4l2_ctrl_query_fill(
+ qc, V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
+
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ return v4l2_ctrl_query_fill(qc, 1000000, 13500000, 100000,
+ 6500000);
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+ err = v4l2_ctrl_query_fill(qc, 1100000, 20200000, 100000,
+ 9000000);
+ if (!err && opt->bitrate_mode == HDPVR_CONSTANT)
+ qc->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return err;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int vidioc_queryctrl(struct file *file, void *private_data,
+ struct v4l2_queryctrl *qc)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+ int i, next;
+ u32 id = qc->id;
+
+ memset(qc, 0, sizeof(*qc));
+
+ next = !!(id & V4L2_CTRL_FLAG_NEXT_CTRL);
+ qc->id = id & ~V4L2_CTRL_FLAG_NEXT_CTRL;
+
+ for (i = 0; i < ARRAY_SIZE(supported_v4l2_ctrls); i++) {
+ if (next) {
+ if (qc->id < supported_v4l2_ctrls[i])
+ qc->id = supported_v4l2_ctrls[i];
+ else
+ continue;
+ }
+
+ if (qc->id == supported_v4l2_ctrls[i])
+ return fill_queryctrl(&dev->options, qc,
+ dev->flags & HDPVR_FLAG_AC3_CAP,
+ dev->fw_ver);
+
+ if (qc->id < supported_v4l2_ctrls[i])
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *private_data,
+ struct v4l2_control *ctrl)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = dev->options.brightness;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->value = dev->options.contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ ctrl->value = dev->options.saturation;
+ break;
+ case V4L2_CID_HUE:
+ ctrl->value = dev->options.hue;
+ break;
+ case V4L2_CID_SHARPNESS:
+ ctrl->value = dev->options.sharpness;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *private_data,
+ struct v4l2_control *ctrl)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+ int retval;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ retval = hdpvr_config_call(dev, CTRL_BRIGHTNESS, ctrl->value);
+ if (!retval)
+ dev->options.brightness = ctrl->value;
+ break;
+ case V4L2_CID_CONTRAST:
+ retval = hdpvr_config_call(dev, CTRL_CONTRAST, ctrl->value);
+ if (!retval)
+ dev->options.contrast = ctrl->value;
+ break;
+ case V4L2_CID_SATURATION:
+ retval = hdpvr_config_call(dev, CTRL_SATURATION, ctrl->value);
+ if (!retval)
+ dev->options.saturation = ctrl->value;
+ break;
+ case V4L2_CID_HUE:
+ retval = hdpvr_config_call(dev, CTRL_HUE, ctrl->value);
+ if (!retval)
+ dev->options.hue = ctrl->value;
+ break;
+ case V4L2_CID_SHARPNESS:
+ retval = hdpvr_config_call(dev, CTRL_SHARPNESS, ctrl->value);
+ if (!retval)
+ dev->options.sharpness = ctrl->value;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return retval;
+}
+
+
+static int hdpvr_get_ctrl(struct hdpvr_options *opt,
+ struct v4l2_ext_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ ctrl->value = opt->audio_codec;
+ break;
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC;
+ break;
+/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */
+/* ctrl->value = (opt->gop_mode & 0x2) ? 0 : 128; */
+/* break; */
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ ctrl->value = opt->bitrate_mode == HDPVR_CONSTANT
+ ? V4L2_MPEG_VIDEO_BITRATE_MODE_CBR
+ : V4L2_MPEG_VIDEO_BITRATE_MODE_VBR;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ ctrl->value = opt->bitrate * 100000;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+ ctrl->value = opt->peak_bitrate * 100000;
+ break;
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG2_TS;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int vidioc_g_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *ctrls)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+ int i, err = 0;
+
+ if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+ for (i = 0; i < ctrls->count; i++) {
+ struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+ err = hdpvr_get_ctrl(&dev->options, ctrl);
+ if (err) {
+ ctrls->error_idx = i;
+ break;
+ }
+ }
+ return err;
+
+ }
+
+ return -EINVAL;
+}
+
+
+static int hdpvr_try_ctrl(struct v4l2_ext_control *ctrl, int ac3)
+{
+ int ret = -EINVAL;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ if (ctrl->value == V4L2_MPEG_AUDIO_ENCODING_AAC ||
+ (ac3 && ctrl->value == V4L2_MPEG_AUDIO_ENCODING_AC3))
+ ret = 0;
+ break;
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ if (ctrl->value == V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC)
+ ret = 0;
+ break;
+/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */
+/* if (ctrl->value == 0 || ctrl->value == 128) */
+/* ret = 0; */
+/* break; */
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR ||
+ ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR)
+ ret = 0;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ {
+ uint bitrate = ctrl->value / 100000;
+ if (bitrate >= 10 && bitrate <= 135)
+ ret = 0;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+ {
+ uint peak_bitrate = ctrl->value / 100000;
+ if (peak_bitrate >= 10 && peak_bitrate <= 202)
+ ret = 0;
+ break;
+ }
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ if (ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_TS)
+ ret = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int vidioc_try_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *ctrls)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+ int i, err = 0;
+
+ if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+ for (i = 0; i < ctrls->count; i++) {
+ struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+ err = hdpvr_try_ctrl(ctrl,
+ dev->flags & HDPVR_FLAG_AC3_CAP);
+ if (err) {
+ ctrls->error_idx = i;
+ break;
+ }
+ }
+ return err;
+ }
+
+ return -EINVAL;
+}
+
+
+static int hdpvr_set_ctrl(struct hdpvr_device *dev,
+ struct v4l2_ext_control *ctrl)
+{
+ struct hdpvr_options *opt = &dev->options;
+ int ret = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ if (dev->flags & HDPVR_FLAG_AC3_CAP) {
+ opt->audio_codec = ctrl->value;
+ ret = hdpvr_set_audio(dev, opt->audio_input,
+ opt->audio_codec);
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ break;
+/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */
+/* if (ctrl->value == 0 && !(opt->gop_mode & 0x2)) { */
+/* opt->gop_mode |= 0x2; */
+/* hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, */
+/* opt->gop_mode); */
+/* } */
+/* if (ctrl->value == 128 && opt->gop_mode & 0x2) { */
+/* opt->gop_mode &= ~0x2; */
+/* hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, */
+/* opt->gop_mode); */
+/* } */
+/* break; */
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR &&
+ opt->bitrate_mode != HDPVR_CONSTANT) {
+ opt->bitrate_mode = HDPVR_CONSTANT;
+ hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE,
+ opt->bitrate_mode);
+ }
+ if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR &&
+ opt->bitrate_mode == HDPVR_CONSTANT) {
+ opt->bitrate_mode = HDPVR_VARIABLE_AVERAGE;
+ hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE,
+ opt->bitrate_mode);
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE: {
+ uint bitrate = ctrl->value / 100000;
+
+ opt->bitrate = bitrate;
+ if (bitrate >= opt->peak_bitrate)
+ opt->peak_bitrate = bitrate+1;
+
+ hdpvr_set_bitrate(dev);
+ break;
+ }
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: {
+ uint peak_bitrate = ctrl->value / 100000;
+
+ if (opt->bitrate_mode == HDPVR_CONSTANT)
+ break;
+
+ if (opt->bitrate < peak_bitrate) {
+ opt->peak_bitrate = peak_bitrate;
+ hdpvr_set_bitrate(dev);
+ } else
+ ret = -EINVAL;
+ break;
+ }
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ break;
+ default:
+ return -EINVAL;
+ }
+ return ret;
+}
+
+static int vidioc_s_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *ctrls)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+ int i, err = 0;
+
+ if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+ for (i = 0; i < ctrls->count; i++) {
+ struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+ err = hdpvr_try_ctrl(ctrl,
+ dev->flags & HDPVR_FLAG_AC3_CAP);
+ if (err) {
+ ctrls->error_idx = i;
+ break;
+ }
+ err = hdpvr_set_ctrl(dev, ctrl);
+ if (err) {
+ ctrls->error_idx = i;
+ break;
+ }
+ }
+ return err;
+
+ }
+
+ return -EINVAL;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *private_data,
+ struct v4l2_fmtdesc *f)
+{
+
+ if (f->index != 0 || f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ f->flags = V4L2_FMT_FLAG_COMPRESSED;
+ strncpy(f->description, "MPEG2-TS with AVC/AAC streams", 32);
+ f->pixelformat = V4L2_PIX_FMT_MPEG;
+
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *private_data,
+ struct v4l2_format *f)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+ struct hdpvr_video_info *vid_info;
+
+ if (!dev)
+ return -ENODEV;
+
+ vid_info = get_video_info(dev);
+ if (!vid_info)
+ return -EFAULT;
+
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.width = vid_info->width;
+ f->fmt.pix.height = vid_info->height;
+ f->fmt.pix.sizeimage = dev->bulk_in_size;
+ f->fmt.pix.colorspace = 0;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.field = V4L2_FIELD_ANY;
+
+ kfree(vid_info);
+ return 0;
+}
+
+static int vidioc_encoder_cmd(struct file *filp, void *priv,
+ struct v4l2_encoder_cmd *a)
+{
+ struct hdpvr_fh *fh = filp->private_data;
+ struct hdpvr_device *dev = fh->dev;
+ int res;
+
+ mutex_lock(&dev->io_mutex);
+
+ memset(&a->raw, 0, sizeof(a->raw));
+ switch (a->cmd) {
+ case V4L2_ENC_CMD_START:
+ a->flags = 0;
+ res = hdpvr_start_streaming(dev);
+ break;
+ case V4L2_ENC_CMD_STOP:
+ res = hdpvr_stop_streaming(dev);
+ break;
+ default:
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "Unsupported encoder cmd %d\n", a->cmd);
+ res = -EINVAL;
+ }
+ mutex_unlock(&dev->io_mutex);
+ return res;
+}
+
+static int vidioc_try_encoder_cmd(struct file *filp, void *priv,
+ struct v4l2_encoder_cmd *a)
+{
+ switch (a->cmd) {
+ case V4L2_ENC_CMD_START:
+ case V4L2_ENC_CMD_STOP:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct v4l2_ioctl_ops hdpvr_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_enumaudio = vidioc_enumaudio,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls,
+ .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls,
+ .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_encoder_cmd = vidioc_encoder_cmd,
+ .vidioc_try_encoder_cmd = vidioc_try_encoder_cmd,
+};
+
+static void hdpvr_device_release(struct video_device *vdev)
+{
+ struct hdpvr_device *dev = video_get_drvdata(vdev);
+
+ hdpvr_delete(dev);
+ mutex_lock(&dev->io_mutex);
+ destroy_workqueue(dev->workqueue);
+ mutex_unlock(&dev->io_mutex);
+
+ v4l2_device_unregister(&dev->v4l2_dev);
+
+ /* deregister I2C adapter */
+#if defined(CONFIG_I2C) || (CONFIG_I2C_MODULE)
+ mutex_lock(&dev->i2c_mutex);
+ i2c_del_adapter(&dev->i2c_adapter);
+ mutex_unlock(&dev->i2c_mutex);
+#endif /* CONFIG_I2C */
+
+ kfree(dev->usbc_buf);
+ kfree(dev);
+}
+
+static const struct video_device hdpvr_video_template = {
+/* .type = VFL_TYPE_GRABBER, */
+/* .type2 = VID_TYPE_CAPTURE | VID_TYPE_MPEG_ENCODER, */
+ .fops = &hdpvr_fops,
+ .release = hdpvr_device_release,
+ .ioctl_ops = &hdpvr_ioctl_ops,
+ .tvnorms =
+ V4L2_STD_NTSC | V4L2_STD_SECAM | V4L2_STD_PAL_B |
+ V4L2_STD_PAL_G | V4L2_STD_PAL_H | V4L2_STD_PAL_I |
+ V4L2_STD_PAL_D | V4L2_STD_PAL_M | V4L2_STD_PAL_N |
+ V4L2_STD_PAL_60,
+ .current_norm = V4L2_STD_NTSC | V4L2_STD_PAL_M |
+ V4L2_STD_PAL_60,
+};
+
+int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent,
+ int devnum)
+{
+ /* setup and register video device */
+ dev->video_dev = video_device_alloc();
+ if (!dev->video_dev) {
+ v4l2_err(&dev->v4l2_dev, "video_device_alloc() failed\n");
+ goto error;
+ }
+
+ *(dev->video_dev) = hdpvr_video_template;
+ strcpy(dev->video_dev->name, "Hauppauge HD PVR");
+ dev->video_dev->parent = parent;
+ video_set_drvdata(dev->video_dev, dev);
+
+ if (video_register_device(dev->video_dev, VFL_TYPE_GRABBER, devnum)) {
+ v4l2_err(&dev->v4l2_dev, "video_device registration failed\n");
+ goto error;
+ }
+
+ return 0;
+error:
+ return -ENOMEM;
+}
diff --git a/drivers/media/video/hdpvr/hdpvr.h b/drivers/media/video/hdpvr/hdpvr.h
new file mode 100644
index 00000000..fea3c692
--- /dev/null
+++ b/drivers/media/video/hdpvr/hdpvr.h
@@ -0,0 +1,317 @@
+/*
+ * Hauppauge HD PVR USB driver
+ *
+ * Copyright (C) 2008 Janne Grunau (j@jannau.net)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-device.h>
+#include <media/ir-kbd-i2c.h>
+
+#define HDPVR_MAX 8
+#define HDPVR_I2C_MAX_SIZE 128
+
+/* Define these values to match your devices */
+#define HD_PVR_VENDOR_ID 0x2040
+#define HD_PVR_PRODUCT_ID 0x4900
+#define HD_PVR_PRODUCT_ID1 0x4901
+#define HD_PVR_PRODUCT_ID2 0x4902
+#define HD_PVR_PRODUCT_ID4 0x4903
+#define HD_PVR_PRODUCT_ID3 0x4982
+
+#define UNSET (-1U)
+
+#define NUM_BUFFERS 64
+
+#define HDPVR_FIRMWARE_VERSION 0x08
+#define HDPVR_FIRMWARE_VERSION_AC3 0x0d
+#define HDPVR_FIRMWARE_VERSION_0X12 0x12
+#define HDPVR_FIRMWARE_VERSION_0X15 0x15
+
+/* #define HDPVR_DEBUG */
+
+extern int hdpvr_debug;
+
+#define MSG_INFO 1
+#define MSG_BUFFER 2
+
+struct hdpvr_options {
+ u8 video_std;
+ u8 video_input;
+ u8 audio_input;
+ u8 bitrate; /* in 100kbps */
+ u8 peak_bitrate; /* in 100kbps */
+ u8 bitrate_mode;
+ u8 gop_mode;
+ enum v4l2_mpeg_audio_encoding audio_codec;
+ u8 brightness;
+ u8 contrast;
+ u8 hue;
+ u8 saturation;
+ u8 sharpness;
+};
+
+/* Structure to hold all of our device specific stuff */
+struct hdpvr_device {
+ /* the v4l device for this device */
+ struct video_device *video_dev;
+ /* the usb device for this device */
+ struct usb_device *udev;
+ /* v4l2-device unused */
+ struct v4l2_device v4l2_dev;
+
+ /* the max packet size of the bulk endpoint */
+ size_t bulk_in_size;
+ /* the address of the bulk in endpoint */
+ __u8 bulk_in_endpointAddr;
+
+ /* holds the current device status */
+ __u8 status;
+ /* count the number of openers */
+ uint open_count;
+
+ /* holds the cureent set options */
+ struct hdpvr_options options;
+
+ uint flags;
+
+ /* synchronize I/O */
+ struct mutex io_mutex;
+ /* available buffers */
+ struct list_head free_buff_list;
+ /* in progress buffers */
+ struct list_head rec_buff_list;
+ /* waitqueue for buffers */
+ wait_queue_head_t wait_buffer;
+ /* waitqueue for data */
+ wait_queue_head_t wait_data;
+ /**/
+ struct workqueue_struct *workqueue;
+ /**/
+ struct work_struct worker;
+
+ /* I2C adapter */
+ struct i2c_adapter i2c_adapter;
+ /* I2C lock */
+ struct mutex i2c_mutex;
+ /* I2C message buffer space */
+ char i2c_buf[HDPVR_I2C_MAX_SIZE];
+
+ /* For passing data to ir-kbd-i2c */
+ struct IR_i2c_init_data ir_i2c_init_data;
+
+ /* usb control transfer buffer and lock */
+ struct mutex usbc_mutex;
+ u8 *usbc_buf;
+ u8 fw_ver;
+};
+
+static inline struct hdpvr_device *to_hdpvr_dev(struct v4l2_device *v4l2_dev)
+{
+ return container_of(v4l2_dev, struct hdpvr_device, v4l2_dev);
+}
+
+
+/* buffer one bulk urb of data */
+struct hdpvr_buffer {
+ struct list_head buff_list;
+
+ struct urb *urb;
+
+ struct hdpvr_device *dev;
+
+ uint pos;
+
+ __u8 status;
+};
+
+/* */
+
+struct hdpvr_video_info {
+ u16 width;
+ u16 height;
+ u8 fps;
+};
+
+enum {
+ STATUS_UNINITIALIZED = 0,
+ STATUS_IDLE,
+ STATUS_STARTING,
+ STATUS_SHUTTING_DOWN,
+ STATUS_STREAMING,
+ STATUS_ERROR,
+ STATUS_DISCONNECTED,
+};
+
+enum {
+ HDPVR_FLAG_AC3_CAP = 1,
+};
+
+enum {
+ BUFSTAT_UNINITIALIZED = 0,
+ BUFSTAT_AVAILABLE,
+ BUFSTAT_INPROGRESS,
+ BUFSTAT_READY,
+};
+
+#define CTRL_START_STREAMING_VALUE 0x0700
+#define CTRL_STOP_STREAMING_VALUE 0x0800
+#define CTRL_BITRATE_VALUE 0x1000
+#define CTRL_BITRATE_MODE_VALUE 0x1200
+#define CTRL_GOP_MODE_VALUE 0x1300
+#define CTRL_VIDEO_INPUT_VALUE 0x1500
+#define CTRL_VIDEO_STD_TYPE 0x1700
+#define CTRL_AUDIO_INPUT_VALUE 0x2500
+#define CTRL_BRIGHTNESS 0x2900
+#define CTRL_CONTRAST 0x2a00
+#define CTRL_HUE 0x2b00
+#define CTRL_SATURATION 0x2c00
+#define CTRL_SHARPNESS 0x2d00
+#define CTRL_LOW_PASS_FILTER_VALUE 0x3100
+
+#define CTRL_DEFAULT_INDEX 0x0003
+
+
+ /* :0 s 38 01 1000 0003 0004 4 = 0a00ca00
+ * BITRATE SETTING
+ * 1st and 2nd byte (little endian): average bitrate in 100 000 bit/s
+ * min: 1 mbit/s, max: 13.5 mbit/s
+ * 3rd and 4th byte (little endian): peak bitrate in 100 000 bit/s
+ * min: average + 100kbit/s,
+ * max: 20.2 mbit/s
+ */
+
+ /* :0 s 38 01 1200 0003 0001 1 = 02
+ * BIT RATE MODE
+ * constant = 1, variable (peak) = 2, variable (average) = 3
+ */
+
+ /* :0 s 38 01 1300 0003 0001 1 = 03
+ * GOP MODE (2 bit)
+ * low bit 0/1: advanced/simple GOP
+ * high bit 0/1: IDR(4/32/128) / no IDR (4/32/0)
+ */
+
+ /* :0 s 38 01 1700 0003 0001 1 = 00
+ * VIDEO STANDARD or FREQUNCY 0 = 60hz, 1 = 50hz
+ */
+
+ /* :0 s 38 01 3100 0003 0004 4 = 03030000
+ * FILTER CONTROL
+ * 1st byte luma low pass filter strength,
+ * 2nd byte chroma low pass filter strength,
+ * 3rd byte MF enable chroma, min=0, max=1
+ * 4th byte n
+ */
+
+
+ /* :0 s 38 b9 0001 0000 0000 0 */
+
+
+
+/* :0 s 38 d3 0000 0000 0001 1 = 00 */
+/* ret = usb_control_msg(dev->udev, */
+/* usb_sndctrlpipe(dev->udev, 0), */
+/* 0xd3, 0x38, */
+/* 0, 0, */
+/* "\0", 1, */
+/* 1000); */
+
+/* info("control request returned %d", ret); */
+/* msleep(5000); */
+
+
+ /* :0 s b8 81 1400 0003 0005 5 <
+ * :0 0 5 = d0024002 19
+ * QUERY FRAME SIZE AND RATE
+ * 1st and 2nd byte (little endian): horizontal resolution
+ * 3rd and 4th byte (little endian): vertical resolution
+ * 5th byte: frame rate
+ */
+
+ /* :0 s b8 81 1800 0003 0003 3 <
+ * :0 0 3 = 030104
+ * QUERY SIGNAL AND DETECTED LINES, maybe INPUT
+ */
+
+enum hdpvr_video_std {
+ HDPVR_60HZ = 0,
+ HDPVR_50HZ,
+};
+
+enum hdpvr_video_input {
+ HDPVR_COMPONENT = 0,
+ HDPVR_SVIDEO,
+ HDPVR_COMPOSITE,
+ HDPVR_VIDEO_INPUTS
+};
+
+enum hdpvr_audio_inputs {
+ HDPVR_RCA_BACK = 0,
+ HDPVR_RCA_FRONT,
+ HDPVR_SPDIF,
+ HDPVR_AUDIO_INPUTS
+};
+
+enum hdpvr_bitrate_mode {
+ HDPVR_CONSTANT = 1,
+ HDPVR_VARIABLE_PEAK,
+ HDPVR_VARIABLE_AVERAGE,
+};
+
+enum hdpvr_gop_mode {
+ HDPVR_ADVANCED_IDR_GOP = 0,
+ HDPVR_SIMPLE_IDR_GOP,
+ HDPVR_ADVANCED_NOIDR_GOP,
+ HDPVR_SIMPLE_NOIDR_GOP,
+};
+
+void hdpvr_delete(struct hdpvr_device *dev);
+
+/*========================================================================*/
+/* hardware control functions */
+int hdpvr_set_options(struct hdpvr_device *dev);
+
+int hdpvr_set_bitrate(struct hdpvr_device *dev);
+
+int hdpvr_set_audio(struct hdpvr_device *dev, u8 input,
+ enum v4l2_mpeg_audio_encoding codec);
+
+int hdpvr_config_call(struct hdpvr_device *dev, uint value,
+ unsigned char valbuf);
+
+struct hdpvr_video_info *get_video_info(struct hdpvr_device *dev);
+
+/* :0 s b8 81 1800 0003 0003 3 < */
+/* :0 0 3 = 0301ff */
+int get_input_lines_info(struct hdpvr_device *dev);
+
+
+/*========================================================================*/
+/* v4l2 registration */
+int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent,
+ int devnumber);
+
+int hdpvr_cancel_queue(struct hdpvr_device *dev);
+
+/*========================================================================*/
+/* i2c adapter registration */
+int hdpvr_register_i2c_adapter(struct hdpvr_device *dev);
+
+struct i2c_client *hdpvr_register_ir_rx_i2c(struct hdpvr_device *dev);
+struct i2c_client *hdpvr_register_ir_tx_i2c(struct hdpvr_device *dev);
+
+/*========================================================================*/
+/* buffer management */
+int hdpvr_free_buffers(struct hdpvr_device *dev);
+int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count);
diff --git a/drivers/media/video/hexium_gemini.c b/drivers/media/video/hexium_gemini.c
new file mode 100644
index 00000000..a62322d5
--- /dev/null
+++ b/drivers/media/video/hexium_gemini.c
@@ -0,0 +1,533 @@
+/*
+ hexium_gemini.c - v4l2 driver for Hexium Gemini frame grabber cards
+
+ Visit http://www.mihu.de/linux/saa7146/ and follow the link
+ to "hexium" for further details about this card.
+
+ Copyright (C) 2003 Michael Hunold <michael@mihu.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define DEBUG_VARIABLE debug
+
+#include <media/saa7146_vv.h>
+#include <linux/module.h>
+
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "debug verbosity");
+
+/* global variables */
+static int hexium_num;
+
+#define HEXIUM_GEMINI 4
+#define HEXIUM_GEMINI_DUAL 5
+
+#define HEXIUM_INPUTS 9
+static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = {
+ { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+ { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+ { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+ { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+ { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+ { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+ { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+ { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+ { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+};
+
+#define HEXIUM_AUDIOS 0
+
+struct hexium_data
+{
+ s8 adr;
+ u8 byte;
+};
+
+#define HEXIUM_CONTROLS 1
+static struct v4l2_queryctrl hexium_controls[] = {
+ { V4L2_CID_PRIVATE_BASE, V4L2_CTRL_TYPE_BOOLEAN, "B/W", 0, 1, 1, 0, 0 },
+};
+
+#define HEXIUM_GEMINI_V_1_0 1
+#define HEXIUM_GEMINI_DUAL_V_1_0 2
+
+struct hexium
+{
+ int type;
+
+ struct video_device *video_dev;
+ struct i2c_adapter i2c_adapter;
+
+ int cur_input; /* current input */
+ v4l2_std_id cur_std; /* current standard */
+ int cur_bw; /* current black/white status */
+};
+
+/* Samsung KS0127B decoder default registers */
+static u8 hexium_ks0127b[0x100]={
+/*00*/ 0x00,0x52,0x30,0x40,0x01,0x0C,0x2A,0x10,
+/*08*/ 0x00,0x00,0x00,0x60,0x00,0x00,0x0F,0x06,
+/*10*/ 0x00,0x00,0xE4,0xC0,0x00,0x00,0x00,0x00,
+/*18*/ 0x14,0x9B,0xFE,0xFF,0xFC,0xFF,0x03,0x22,
+/*20*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*28*/ 0x00,0x00,0x00,0x00,0x00,0x2C,0x9B,0x00,
+/*30*/ 0x00,0x00,0x10,0x80,0x80,0x10,0x80,0x80,
+/*38*/ 0x01,0x04,0x00,0x00,0x00,0x29,0xC0,0x00,
+/*40*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*48*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*50*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*58*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*60*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*68*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*70*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*78*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*80*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*88*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*90*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*98*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*A0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*A8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*B0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*B8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*C0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*C8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*D0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*D8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*E0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*E8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*F0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*F8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+};
+
+static struct hexium_data hexium_pal[] = {
+ { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
+};
+
+static struct hexium_data hexium_pal_bw[] = {
+ { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
+};
+
+static struct hexium_data hexium_ntsc[] = {
+ { 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF }
+};
+
+static struct hexium_data hexium_ntsc_bw[] = {
+ { 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF }
+};
+
+static struct hexium_data hexium_secam[] = {
+ { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
+};
+
+static struct hexium_data hexium_input_select[] = {
+ { 0x02, 0x60 },
+ { 0x02, 0x64 },
+ { 0x02, 0x61 },
+ { 0x02, 0x65 },
+ { 0x02, 0x62 },
+ { 0x02, 0x66 },
+ { 0x02, 0x68 },
+ { 0x02, 0x69 },
+ { 0x02, 0x6A },
+};
+
+/* fixme: h_offset = 0 for Hexium Gemini *Dual*, which
+ are currently *not* supported*/
+static struct saa7146_standard hexium_standards[] = {
+ {
+ .name = "PAL", .id = V4L2_STD_PAL,
+ .v_offset = 28, .v_field = 288,
+ .h_offset = 1, .h_pixels = 680,
+ .v_max_out = 576, .h_max_out = 768,
+ }, {
+ .name = "NTSC", .id = V4L2_STD_NTSC,
+ .v_offset = 28, .v_field = 240,
+ .h_offset = 1, .h_pixels = 640,
+ .v_max_out = 480, .h_max_out = 640,
+ }, {
+ .name = "SECAM", .id = V4L2_STD_SECAM,
+ .v_offset = 28, .v_field = 288,
+ .h_offset = 1, .h_pixels = 720,
+ .v_max_out = 576, .h_max_out = 768,
+ }
+};
+
+/* bring hardware to a sane state. this has to be done, just in case someone
+ wants to capture from this device before it has been properly initialized.
+ the capture engine would badly fail, because no valid signal arrives on the
+ saa7146, thus leading to timeouts and stuff. */
+static int hexium_init_done(struct saa7146_dev *dev)
+{
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+ union i2c_smbus_data data;
+ int i = 0;
+
+ DEB_D("hexium_init_done called\n");
+
+ /* initialize the helper ics to useful values */
+ for (i = 0; i < sizeof(hexium_ks0127b); i++) {
+ data.byte = hexium_ks0127b[i];
+ if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) {
+ pr_err("hexium_init_done() failed for address 0x%02x\n",
+ i);
+ }
+ }
+
+ return 0;
+}
+
+static int hexium_set_input(struct hexium *hexium, int input)
+{
+ union i2c_smbus_data data;
+
+ DEB_D("\n");
+
+ data.byte = hexium_input_select[input].byte;
+ if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, hexium_input_select[input].adr, I2C_SMBUS_BYTE_DATA, &data)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int hexium_set_standard(struct hexium *hexium, struct hexium_data *vdec)
+{
+ union i2c_smbus_data data;
+ int i = 0;
+
+ DEB_D("\n");
+
+ while (vdec[i].adr != -1) {
+ data.byte = vdec[i].byte;
+ if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, vdec[i].adr, I2C_SMBUS_BYTE_DATA, &data)) {
+ pr_err("hexium_init_done: hexium_set_standard() failed for address 0x%02x\n",
+ i);
+ return -1;
+ }
+ i++;
+ }
+ return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
+{
+ DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index);
+
+ if (i->index >= HEXIUM_INPUTS)
+ return -EINVAL;
+
+ memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input));
+
+ DEB_D("v4l2_ioctl: VIDIOC_ENUMINPUT %d\n", i->index);
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *input)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+ *input = hexium->cur_input;
+
+ DEB_D("VIDIOC_G_INPUT: %d\n", *input);
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+ DEB_EE("VIDIOC_S_INPUT %d\n", input);
+
+ if (input >= HEXIUM_INPUTS)
+ return -EINVAL;
+
+ hexium->cur_input = input;
+ hexium_set_input(hexium, input);
+ return 0;
+}
+
+/* the saa7146 provides some controls (brightness, contrast, saturation)
+ which gets registered *after* this function. because of this we have
+ to return with a value != 0 even if the function succeeded.. */
+static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qc)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ int i;
+
+ for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
+ if (hexium_controls[i].id == qc->id) {
+ *qc = hexium_controls[i];
+ DEB_D("VIDIOC_QUERYCTRL %d\n", qc->id);
+ return 0;
+ }
+ }
+ return dev->ext_vv_data->core_ops->vidioc_queryctrl(file, fh, qc);
+}
+
+static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+ int i;
+
+ for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
+ if (hexium_controls[i].id == vc->id)
+ break;
+ }
+
+ if (i < 0)
+ return dev->ext_vv_data->core_ops->vidioc_g_ctrl(file, fh, vc);
+
+ if (vc->id == V4L2_CID_PRIVATE_BASE) {
+ vc->value = hexium->cur_bw;
+ DEB_D("VIDIOC_G_CTRL BW:%d\n", vc->value);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+ int i = 0;
+
+ for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
+ if (hexium_controls[i].id == vc->id)
+ break;
+ }
+
+ if (i < 0)
+ return dev->ext_vv_data->core_ops->vidioc_s_ctrl(file, fh, vc);
+
+ if (vc->id == V4L2_CID_PRIVATE_BASE)
+ hexium->cur_bw = vc->value;
+
+ DEB_D("VIDIOC_S_CTRL BW:%d\n", hexium->cur_bw);
+
+ if (0 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) {
+ hexium_set_standard(hexium, hexium_pal);
+ return 0;
+ }
+ if (0 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) {
+ hexium_set_standard(hexium, hexium_ntsc);
+ return 0;
+ }
+ if (0 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std) {
+ hexium_set_standard(hexium, hexium_secam);
+ return 0;
+ }
+ if (1 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) {
+ hexium_set_standard(hexium, hexium_pal_bw);
+ return 0;
+ }
+ if (1 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) {
+ hexium_set_standard(hexium, hexium_ntsc_bw);
+ return 0;
+ }
+ if (1 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std)
+ /* fixme: is there no bw secam mode? */
+ return -EINVAL;
+
+ return -EINVAL;
+}
+
+
+static struct saa7146_ext_vv vv_data;
+
+/* this function only gets called when the probing was successful */
+static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
+{
+ struct hexium *hexium;
+ int ret;
+
+ DEB_EE("\n");
+
+ hexium = kzalloc(sizeof(struct hexium), GFP_KERNEL);
+ if (NULL == hexium) {
+ pr_err("not enough kernel memory in hexium_attach()\n");
+ return -ENOMEM;
+ }
+ dev->ext_priv = hexium;
+
+ /* enable i2c-port pins */
+ saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26));
+
+ hexium->i2c_adapter = (struct i2c_adapter) {
+ .name = "hexium gemini",
+ };
+ saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
+ if (i2c_add_adapter(&hexium->i2c_adapter) < 0) {
+ DEB_S("cannot register i2c-device. skipping.\n");
+ kfree(hexium);
+ return -EFAULT;
+ }
+
+ /* set HWControl GPIO number 2 */
+ saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
+
+ saa7146_write(dev, DD1_INIT, 0x07000700);
+ saa7146_write(dev, DD1_STREAM_B, 0x00000000);
+ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+
+ /* the rest */
+ hexium->cur_input = 0;
+ hexium_init_done(dev);
+
+ hexium_set_standard(hexium, hexium_pal);
+ hexium->cur_std = V4L2_STD_PAL;
+
+ hexium_set_input(hexium, 0);
+ hexium->cur_input = 0;
+
+ saa7146_vv_init(dev, &vv_data);
+ vv_data.ops.vidioc_queryctrl = vidioc_queryctrl;
+ vv_data.ops.vidioc_g_ctrl = vidioc_g_ctrl;
+ vv_data.ops.vidioc_s_ctrl = vidioc_s_ctrl;
+ vv_data.ops.vidioc_enum_input = vidioc_enum_input;
+ vv_data.ops.vidioc_g_input = vidioc_g_input;
+ vv_data.ops.vidioc_s_input = vidioc_s_input;
+ ret = saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_GRABBER);
+ if (ret < 0) {
+ pr_err("cannot register capture v4l2 device. skipping.\n");
+ return ret;
+ }
+
+ pr_info("found 'hexium gemini' frame grabber-%d\n", hexium_num);
+ hexium_num++;
+
+ return 0;
+}
+
+static int hexium_detach(struct saa7146_dev *dev)
+{
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+ DEB_EE("dev:%p\n", dev);
+
+ saa7146_unregister_device(&hexium->video_dev, dev);
+ saa7146_vv_release(dev);
+
+ hexium_num--;
+
+ i2c_del_adapter(&hexium->i2c_adapter);
+ kfree(hexium);
+ return 0;
+}
+
+static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std)
+{
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+ if (V4L2_STD_PAL == std->id) {
+ hexium_set_standard(hexium, hexium_pal);
+ hexium->cur_std = V4L2_STD_PAL;
+ return 0;
+ } else if (V4L2_STD_NTSC == std->id) {
+ hexium_set_standard(hexium, hexium_ntsc);
+ hexium->cur_std = V4L2_STD_NTSC;
+ return 0;
+ } else if (V4L2_STD_SECAM == std->id) {
+ hexium_set_standard(hexium, hexium_secam);
+ hexium->cur_std = V4L2_STD_SECAM;
+ return 0;
+ }
+
+ return -1;
+}
+
+static struct saa7146_extension hexium_extension;
+
+static struct saa7146_pci_extension_data hexium_gemini_4bnc = {
+ .ext_priv = "Hexium Gemini (4 BNC)",
+ .ext = &hexium_extension,
+};
+
+static struct saa7146_pci_extension_data hexium_gemini_dual_4bnc = {
+ .ext_priv = "Hexium Gemini Dual (4 BNC)",
+ .ext = &hexium_extension,
+};
+
+static struct pci_device_id pci_tbl[] = {
+ {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
+ .subvendor = 0x17c8,
+ .subdevice = 0x2401,
+ .driver_data = (unsigned long) &hexium_gemini_4bnc,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
+ .subvendor = 0x17c8,
+ .subdevice = 0x2402,
+ .driver_data = (unsigned long) &hexium_gemini_dual_4bnc,
+ },
+ {
+ .vendor = 0,
+ }
+};
+
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+static struct saa7146_ext_vv vv_data = {
+ .inputs = HEXIUM_INPUTS,
+ .capabilities = 0,
+ .stds = &hexium_standards[0],
+ .num_stds = sizeof(hexium_standards) / sizeof(struct saa7146_standard),
+ .std_callback = &std_callback,
+};
+
+static struct saa7146_extension hexium_extension = {
+ .name = "hexium gemini",
+ .flags = SAA7146_USE_I2C_IRQ,
+
+ .pci_tbl = &pci_tbl[0],
+ .module = THIS_MODULE,
+
+ .attach = hexium_attach,
+ .detach = hexium_detach,
+
+ .irq_mask = 0,
+ .irq_func = NULL,
+};
+
+static int __init hexium_init_module(void)
+{
+ if (0 != saa7146_register_extension(&hexium_extension)) {
+ DEB_S("failed to register extension\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void __exit hexium_cleanup_module(void)
+{
+ saa7146_unregister_extension(&hexium_extension);
+}
+
+module_init(hexium_init_module);
+module_exit(hexium_cleanup_module);
+
+MODULE_DESCRIPTION("video4linux-2 driver for Hexium Gemini frame grabber cards");
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/hexium_orion.c b/drivers/media/video/hexium_orion.c
new file mode 100644
index 00000000..23debc96
--- /dev/null
+++ b/drivers/media/video/hexium_orion.c
@@ -0,0 +1,502 @@
+/*
+ hexium_orion.c - v4l2 driver for the Hexium Orion frame grabber cards
+
+ Visit http://www.mihu.de/linux/saa7146/ and follow the link
+ to "hexium" for further details about this card.
+
+ Copyright (C) 2003 Michael Hunold <michael@mihu.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define DEBUG_VARIABLE debug
+
+#include <media/saa7146_vv.h>
+#include <linux/module.h>
+
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "debug verbosity");
+
+/* global variables */
+static int hexium_num;
+
+#define HEXIUM_HV_PCI6_ORION 1
+#define HEXIUM_ORION_1SVHS_3BNC 2
+#define HEXIUM_ORION_4BNC 3
+
+#define HEXIUM_INPUTS 9
+static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = {
+ { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+ { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+ { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+ { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+ { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+ { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+ { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+ { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+ { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+};
+
+#define HEXIUM_AUDIOS 0
+
+struct hexium_data
+{
+ s8 adr;
+ u8 byte;
+};
+
+struct hexium
+{
+ int type;
+ struct video_device *video_dev;
+ struct i2c_adapter i2c_adapter;
+
+ int cur_input; /* current input */
+};
+
+/* Philips SAA7110 decoder default registers */
+static u8 hexium_saa7110[53]={
+/*00*/ 0x4C,0x3C,0x0D,0xEF,0xBD,0xF0,0x00,0x00,
+/*08*/ 0xF8,0xF8,0x60,0x60,0x40,0x86,0x18,0x90,
+/*10*/ 0x00,0x2C,0x40,0x46,0x42,0x1A,0xFF,0xDA,
+/*18*/ 0xF0,0x8B,0x00,0x00,0x00,0x00,0x00,0x00,
+/*20*/ 0xD9,0x17,0x40,0x41,0x80,0x41,0x80,0x4F,
+/*28*/ 0xFE,0x01,0x0F,0x0F,0x03,0x01,0x81,0x03,
+/*30*/ 0x44,0x75,0x01,0x8C,0x03
+};
+
+static struct {
+ struct hexium_data data[8];
+} hexium_input_select[] = {
+{
+ { /* cvbs 1 */
+ { 0x06, 0x00 },
+ { 0x20, 0xD9 },
+ { 0x21, 0x17 }, // 0x16,
+ { 0x22, 0x40 },
+ { 0x2C, 0x03 },
+ { 0x30, 0x44 },
+ { 0x31, 0x75 }, // ??
+ { 0x21, 0x16 }, // 0x03,
+ }
+}, {
+ { /* cvbs 2 */
+ { 0x06, 0x00 },
+ { 0x20, 0x78 },
+ { 0x21, 0x07 }, // 0x03,
+ { 0x22, 0xD2 },
+ { 0x2C, 0x83 },
+ { 0x30, 0x60 },
+ { 0x31, 0xB5 }, // ?
+ { 0x21, 0x03 },
+ }
+}, {
+ { /* cvbs 3 */
+ { 0x06, 0x00 },
+ { 0x20, 0xBA },
+ { 0x21, 0x07 }, // 0x05,
+ { 0x22, 0x91 },
+ { 0x2C, 0x03 },
+ { 0x30, 0x60 },
+ { 0x31, 0xB5 }, // ??
+ { 0x21, 0x05 }, // 0x03,
+ }
+}, {
+ { /* cvbs 4 */
+ { 0x06, 0x00 },
+ { 0x20, 0xD8 },
+ { 0x21, 0x17 }, // 0x16,
+ { 0x22, 0x40 },
+ { 0x2C, 0x03 },
+ { 0x30, 0x44 },
+ { 0x31, 0x75 }, // ??
+ { 0x21, 0x16 }, // 0x03,
+ }
+}, {
+ { /* cvbs 5 */
+ { 0x06, 0x00 },
+ { 0x20, 0xB8 },
+ { 0x21, 0x07 }, // 0x05,
+ { 0x22, 0x91 },
+ { 0x2C, 0x03 },
+ { 0x30, 0x60 },
+ { 0x31, 0xB5 }, // ??
+ { 0x21, 0x05 }, // 0x03,
+ }
+}, {
+ { /* cvbs 6 */
+ { 0x06, 0x00 },
+ { 0x20, 0x7C },
+ { 0x21, 0x07 }, // 0x03
+ { 0x22, 0xD2 },
+ { 0x2C, 0x83 },
+ { 0x30, 0x60 },
+ { 0x31, 0xB5 }, // ??
+ { 0x21, 0x03 },
+ }
+}, {
+ { /* y/c 1 */
+ { 0x06, 0x80 },
+ { 0x20, 0x59 },
+ { 0x21, 0x17 },
+ { 0x22, 0x42 },
+ { 0x2C, 0xA3 },
+ { 0x30, 0x44 },
+ { 0x31, 0x75 },
+ { 0x21, 0x12 },
+ }
+}, {
+ { /* y/c 2 */
+ { 0x06, 0x80 },
+ { 0x20, 0x9A },
+ { 0x21, 0x17 },
+ { 0x22, 0xB1 },
+ { 0x2C, 0x13 },
+ { 0x30, 0x60 },
+ { 0x31, 0xB5 },
+ { 0x21, 0x14 },
+ }
+}, {
+ { /* y/c 3 */
+ { 0x06, 0x80 },
+ { 0x20, 0x3C },
+ { 0x21, 0x27 },
+ { 0x22, 0xC1 },
+ { 0x2C, 0x23 },
+ { 0x30, 0x44 },
+ { 0x31, 0x75 },
+ { 0x21, 0x21 },
+ }
+}
+};
+
+static struct saa7146_standard hexium_standards[] = {
+ {
+ .name = "PAL", .id = V4L2_STD_PAL,
+ .v_offset = 16, .v_field = 288,
+ .h_offset = 1, .h_pixels = 680,
+ .v_max_out = 576, .h_max_out = 768,
+ }, {
+ .name = "NTSC", .id = V4L2_STD_NTSC,
+ .v_offset = 16, .v_field = 240,
+ .h_offset = 1, .h_pixels = 640,
+ .v_max_out = 480, .h_max_out = 640,
+ }, {
+ .name = "SECAM", .id = V4L2_STD_SECAM,
+ .v_offset = 16, .v_field = 288,
+ .h_offset = 1, .h_pixels = 720,
+ .v_max_out = 576, .h_max_out = 768,
+ }
+};
+
+/* this is only called for old HV-PCI6/Orion cards
+ without eeprom */
+static int hexium_probe(struct saa7146_dev *dev)
+{
+ struct hexium *hexium = NULL;
+ union i2c_smbus_data data;
+ int err = 0;
+
+ DEB_EE("\n");
+
+ /* there are no hexium orion cards with revision 0 saa7146s */
+ if (0 == dev->revision) {
+ return -EFAULT;
+ }
+
+ hexium = kzalloc(sizeof(struct hexium), GFP_KERNEL);
+ if (NULL == hexium) {
+ pr_err("hexium_probe: not enough kernel memory\n");
+ return -ENOMEM;
+ }
+
+ /* enable i2c-port pins */
+ saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26));
+
+ saa7146_write(dev, DD1_INIT, 0x01000100);
+ saa7146_write(dev, DD1_STREAM_B, 0x00000000);
+ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+
+ hexium->i2c_adapter = (struct i2c_adapter) {
+ .name = "hexium orion",
+ };
+ saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
+ if (i2c_add_adapter(&hexium->i2c_adapter) < 0) {
+ DEB_S("cannot register i2c-device. skipping.\n");
+ kfree(hexium);
+ return -EFAULT;
+ }
+
+ /* set SAA7110 control GPIO 0 */
+ saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTHI);
+ /* set HWControl GPIO number 2 */
+ saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
+
+ mdelay(10);
+
+ /* detect newer Hexium Orion cards by subsystem ids */
+ if (0x17c8 == dev->pci->subsystem_vendor && 0x0101 == dev->pci->subsystem_device) {
+ pr_info("device is a Hexium Orion w/ 1 SVHS + 3 BNC inputs\n");
+ /* we store the pointer in our private data field */
+ dev->ext_priv = hexium;
+ hexium->type = HEXIUM_ORION_1SVHS_3BNC;
+ return 0;
+ }
+
+ if (0x17c8 == dev->pci->subsystem_vendor && 0x2101 == dev->pci->subsystem_device) {
+ pr_info("device is a Hexium Orion w/ 4 BNC inputs\n");
+ /* we store the pointer in our private data field */
+ dev->ext_priv = hexium;
+ hexium->type = HEXIUM_ORION_4BNC;
+ return 0;
+ }
+
+ /* check if this is an old hexium Orion card by looking at
+ a saa7110 at address 0x4e */
+ if (0 == (err = i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_READ, 0x00, I2C_SMBUS_BYTE_DATA, &data))) {
+ pr_info("device is a Hexium HV-PCI6/Orion (old)\n");
+ /* we store the pointer in our private data field */
+ dev->ext_priv = hexium;
+ hexium->type = HEXIUM_HV_PCI6_ORION;
+ return 0;
+ }
+
+ i2c_del_adapter(&hexium->i2c_adapter);
+ kfree(hexium);
+ return -EFAULT;
+}
+
+/* bring hardware to a sane state. this has to be done, just in case someone
+ wants to capture from this device before it has been properly initialized.
+ the capture engine would badly fail, because no valid signal arrives on the
+ saa7146, thus leading to timeouts and stuff. */
+static int hexium_init_done(struct saa7146_dev *dev)
+{
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+ union i2c_smbus_data data;
+ int i = 0;
+
+ DEB_D("hexium_init_done called\n");
+
+ /* initialize the helper ics to useful values */
+ for (i = 0; i < sizeof(hexium_saa7110); i++) {
+ data.byte = hexium_saa7110[i];
+ if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) {
+ pr_err("failed for address 0x%02x\n", i);
+ }
+ }
+
+ return 0;
+}
+
+static int hexium_set_input(struct hexium *hexium, int input)
+{
+ union i2c_smbus_data data;
+ int i = 0;
+
+ DEB_D("\n");
+
+ for (i = 0; i < 8; i++) {
+ int adr = hexium_input_select[input].data[i].adr;
+ data.byte = hexium_input_select[input].data[i].byte;
+ if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, adr, I2C_SMBUS_BYTE_DATA, &data)) {
+ return -1;
+ }
+ pr_debug("%d: 0x%02x => 0x%02x\n", input, adr, data.byte);
+ }
+
+ return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
+{
+ DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index);
+
+ if (i->index >= HEXIUM_INPUTS)
+ return -EINVAL;
+
+ memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input));
+
+ DEB_D("v4l2_ioctl: VIDIOC_ENUMINPUT %d\n", i->index);
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *input)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+ *input = hexium->cur_input;
+
+ DEB_D("VIDIOC_G_INPUT: %d\n", *input);
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+ if (input >= HEXIUM_INPUTS)
+ return -EINVAL;
+
+ hexium->cur_input = input;
+ hexium_set_input(hexium, input);
+
+ return 0;
+}
+
+static struct saa7146_ext_vv vv_data;
+
+/* this function only gets called when the probing was successful */
+static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
+{
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+ DEB_EE("\n");
+
+ saa7146_vv_init(dev, &vv_data);
+ vv_data.ops.vidioc_enum_input = vidioc_enum_input;
+ vv_data.ops.vidioc_g_input = vidioc_g_input;
+ vv_data.ops.vidioc_s_input = vidioc_s_input;
+ if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium orion", VFL_TYPE_GRABBER)) {
+ pr_err("cannot register capture v4l2 device. skipping.\n");
+ return -1;
+ }
+
+ pr_err("found 'hexium orion' frame grabber-%d\n", hexium_num);
+ hexium_num++;
+
+ /* the rest */
+ hexium->cur_input = 0;
+ hexium_init_done(dev);
+
+ return 0;
+}
+
+static int hexium_detach(struct saa7146_dev *dev)
+{
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+ DEB_EE("dev:%p\n", dev);
+
+ saa7146_unregister_device(&hexium->video_dev, dev);
+ saa7146_vv_release(dev);
+
+ hexium_num--;
+
+ i2c_del_adapter(&hexium->i2c_adapter);
+ kfree(hexium);
+ return 0;
+}
+
+static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std)
+{
+ return 0;
+}
+
+static struct saa7146_extension extension;
+
+static struct saa7146_pci_extension_data hexium_hv_pci6 = {
+ .ext_priv = "Hexium HV-PCI6 / Orion",
+ .ext = &extension,
+};
+
+static struct saa7146_pci_extension_data hexium_orion_1svhs_3bnc = {
+ .ext_priv = "Hexium HV-PCI6 / Orion (1 SVHS/3 BNC)",
+ .ext = &extension,
+};
+
+static struct saa7146_pci_extension_data hexium_orion_4bnc = {
+ .ext_priv = "Hexium HV-PCI6 / Orion (4 BNC)",
+ .ext = &extension,
+};
+
+static struct pci_device_id pci_tbl[] = {
+ {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
+ .subvendor = 0x0000,
+ .subdevice = 0x0000,
+ .driver_data = (unsigned long) &hexium_hv_pci6,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
+ .subvendor = 0x17c8,
+ .subdevice = 0x0101,
+ .driver_data = (unsigned long) &hexium_orion_1svhs_3bnc,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
+ .subvendor = 0x17c8,
+ .subdevice = 0x2101,
+ .driver_data = (unsigned long) &hexium_orion_4bnc,
+ },
+ {
+ .vendor = 0,
+ }
+};
+
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+static struct saa7146_ext_vv vv_data = {
+ .inputs = HEXIUM_INPUTS,
+ .capabilities = 0,
+ .stds = &hexium_standards[0],
+ .num_stds = sizeof(hexium_standards) / sizeof(struct saa7146_standard),
+ .std_callback = &std_callback,
+};
+
+static struct saa7146_extension extension = {
+ .name = "hexium HV-PCI6 Orion",
+ .flags = 0, // SAA7146_USE_I2C_IRQ,
+
+ .pci_tbl = &pci_tbl[0],
+ .module = THIS_MODULE,
+
+ .probe = hexium_probe,
+ .attach = hexium_attach,
+ .detach = hexium_detach,
+
+ .irq_mask = 0,
+ .irq_func = NULL,
+};
+
+static int __init hexium_init_module(void)
+{
+ if (0 != saa7146_register_extension(&extension)) {
+ DEB_S("failed to register extension\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void __exit hexium_cleanup_module(void)
+{
+ saa7146_unregister_extension(&extension);
+}
+
+module_init(hexium_init_module);
+module_exit(hexium_cleanup_module);
+
+MODULE_DESCRIPTION("video4linux-2 driver for Hexium Orion frame grabber cards");
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/ibmmpeg2.h b/drivers/media/video/ibmmpeg2.h
new file mode 100644
index 00000000..68e10387
--- /dev/null
+++ b/drivers/media/video/ibmmpeg2.h
@@ -0,0 +1,94 @@
+/* ibmmpeg2.h - IBM MPEGCD21 definitions */
+
+#ifndef __IBM_MPEG2__
+#define __IBM_MPEG2__
+
+/* Define all MPEG Decoder registers */
+/* Chip Control and Status */
+#define IBM_MP2_CHIP_CONTROL 0x200*2
+#define IBM_MP2_CHIP_MODE 0x201*2
+/* Timer Control and Status */
+#define IBM_MP2_SYNC_STC2 0x202*2
+#define IBM_MP2_SYNC_STC1 0x203*2
+#define IBM_MP2_SYNC_STC0 0x204*2
+#define IBM_MP2_SYNC_PTS2 0x205*2
+#define IBM_MP2_SYNC_PTS1 0x206*2
+#define IBM_MP2_SYNC_PTS0 0x207*2
+/* Video FIFO Control */
+#define IBM_MP2_FIFO 0x208*2
+#define IBM_MP2_FIFOW 0x100*2
+#define IBM_MP2_FIFO_STAT 0x209*2
+#define IBM_MP2_RB_THRESHOLD 0x22b*2
+/* Command buffer */
+#define IBM_MP2_COMMAND 0x20a*2
+#define IBM_MP2_CMD_DATA 0x20b*2
+#define IBM_MP2_CMD_STAT 0x20c*2
+#define IBM_MP2_CMD_ADDR 0x20d*2
+/* Internal Processor Control and Status */
+#define IBM_MP2_PROC_IADDR 0x20e*2
+#define IBM_MP2_PROC_IDATA 0x20f*2
+#define IBM_MP2_WR_PROT 0x235*2
+/* DRAM Access */
+#define IBM_MP2_DRAM_ADDR 0x210*2
+#define IBM_MP2_DRAM_DATA 0x212*2
+#define IBM_MP2_DRAM_CMD_STAT 0x213*2
+#define IBM_MP2_BLOCK_SIZE 0x23b*2
+#define IBM_MP2_SRC_ADDR 0x23c*2
+/* Onscreen Display */
+#define IBM_MP2_OSD_ADDR 0x214*2
+#define IBM_MP2_OSD_DATA 0x215*2
+#define IBM_MP2_OSD_MODE 0x217*2
+#define IBM_MP2_OSD_LINK_ADDR 0x229*2
+#define IBM_MP2_OSD_SIZE 0x22a*2
+/* Interrupt Control */
+#define IBM_MP2_HOST_INT 0x218*2
+#define IBM_MP2_MASK0 0x219*2
+#define IBM_MP2_HOST_INT1 0x23e*2
+#define IBM_MP2_MASK1 0x23f*2
+/* Audio Control */
+#define IBM_MP2_AUD_IADDR 0x21a*2
+#define IBM_MP2_AUD_IDATA 0x21b*2
+#define IBM_MP2_AUD_FIFO 0x21c*2
+#define IBM_MP2_AUD_FIFOW 0x101*2
+#define IBM_MP2_AUD_CTL 0x21d*2
+#define IBM_MP2_BEEP_CTL 0x21e*2
+#define IBM_MP2_FRNT_ATTEN 0x22d*2
+/* Display Control */
+#define IBM_MP2_DISP_MODE 0x220*2
+#define IBM_MP2_DISP_DLY 0x221*2
+#define IBM_MP2_VBI_CTL 0x222*2
+#define IBM_MP2_DISP_LBOR 0x223*2
+#define IBM_MP2_DISP_TBOR 0x224*2
+/* Polarity Control */
+#define IBM_MP2_INFC_CTL 0x22c*2
+
+/* control commands */
+#define IBM_MP2_PLAY 0
+#define IBM_MP2_PAUSE 1
+#define IBM_MP2_SINGLE_FRAME 2
+#define IBM_MP2_FAST_FORWARD 3
+#define IBM_MP2_SLOW_MOTION 4
+#define IBM_MP2_IMED_NORM_PLAY 5
+#define IBM_MP2_RESET_WINDOW 6
+#define IBM_MP2_FREEZE_FRAME 7
+#define IBM_MP2_RESET_VID_RATE 8
+#define IBM_MP2_CONFIG_DECODER 9
+#define IBM_MP2_CHANNEL_SWITCH 10
+#define IBM_MP2_RESET_AUD_RATE 11
+#define IBM_MP2_PRE_OP_CHN_SW 12
+#define IBM_MP2_SET_STILL_MODE 14
+
+/* Define Xilinx FPGA Internal Registers */
+
+/* general control register 0 */
+#define XILINX_CTL0 0x600
+/* genlock delay resister 1 */
+#define XILINX_GLDELAY 0x602
+/* send 16 bits to CS3310 port */
+#define XILINX_CS3310 0x604
+/* send 16 bits to CS3310 and complete */
+#define XILINX_CS3310_CMPLT 0x60c
+/* pulse width modulator control */
+#define XILINX_PWM 0x606
+
+#endif
diff --git a/drivers/media/video/imx074.c b/drivers/media/video/imx074.c
new file mode 100644
index 00000000..351e9baf
--- /dev/null
+++ b/drivers/media/video/imx074.c
@@ -0,0 +1,475 @@
+/*
+ * Driver for IMX074 CMOS Image Sensor from Sony
+ *
+ * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * Partially inspired by the IMX074 driver from the Android / MSM tree
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/module.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-chip-ident.h>
+
+/* IMX074 registers */
+
+#define MODE_SELECT 0x0100
+#define IMAGE_ORIENTATION 0x0101
+#define GROUPED_PARAMETER_HOLD 0x0104
+
+/* Integration Time */
+#define COARSE_INTEGRATION_TIME_HI 0x0202
+#define COARSE_INTEGRATION_TIME_LO 0x0203
+/* Gain */
+#define ANALOGUE_GAIN_CODE_GLOBAL_HI 0x0204
+#define ANALOGUE_GAIN_CODE_GLOBAL_LO 0x0205
+
+/* PLL registers */
+#define PRE_PLL_CLK_DIV 0x0305
+#define PLL_MULTIPLIER 0x0307
+#define PLSTATIM 0x302b
+#define VNDMY_ABLMGSHLMT 0x300a
+#define Y_OPBADDR_START_DI 0x3014
+/* mode setting */
+#define FRAME_LENGTH_LINES_HI 0x0340
+#define FRAME_LENGTH_LINES_LO 0x0341
+#define LINE_LENGTH_PCK_HI 0x0342
+#define LINE_LENGTH_PCK_LO 0x0343
+#define YADDR_START 0x0347
+#define YADDR_END 0x034b
+#define X_OUTPUT_SIZE_MSB 0x034c
+#define X_OUTPUT_SIZE_LSB 0x034d
+#define Y_OUTPUT_SIZE_MSB 0x034e
+#define Y_OUTPUT_SIZE_LSB 0x034f
+#define X_EVEN_INC 0x0381
+#define X_ODD_INC 0x0383
+#define Y_EVEN_INC 0x0385
+#define Y_ODD_INC 0x0387
+
+#define HMODEADD 0x3001
+#define VMODEADD 0x3016
+#define VAPPLINE_START 0x3069
+#define VAPPLINE_END 0x306b
+#define SHUTTER 0x3086
+#define HADDAVE 0x30e8
+#define LANESEL 0x3301
+
+/* IMX074 supported geometry */
+#define IMX074_WIDTH 1052
+#define IMX074_HEIGHT 780
+
+/* IMX074 has only one fixed colorspace per pixelcode */
+struct imx074_datafmt {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+};
+
+struct imx074 {
+ struct v4l2_subdev subdev;
+ const struct imx074_datafmt *fmt;
+};
+
+static const struct imx074_datafmt imx074_colour_fmts[] = {
+ {V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
+};
+
+static struct imx074 *to_imx074(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct imx074, subdev);
+}
+
+/* Find a data format by a pixel code in an array */
+static const struct imx074_datafmt *imx074_find_datafmt(enum v4l2_mbus_pixelcode code)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(imx074_colour_fmts); i++)
+ if (imx074_colour_fmts[i].code == code)
+ return imx074_colour_fmts + i;
+
+ return NULL;
+}
+
+static int reg_write(struct i2c_client *client, const u16 addr, const u8 data)
+{
+ struct i2c_adapter *adap = client->adapter;
+ struct i2c_msg msg;
+ unsigned char tx[3];
+ int ret;
+
+ msg.addr = client->addr;
+ msg.buf = tx;
+ msg.len = 3;
+ msg.flags = 0;
+
+ tx[0] = addr >> 8;
+ tx[1] = addr & 0xff;
+ tx[2] = data;
+
+ ret = i2c_transfer(adap, &msg, 1);
+
+ mdelay(2);
+
+ return ret == 1 ? 0 : -EIO;
+}
+
+static int reg_read(struct i2c_client *client, const u16 addr)
+{
+ u8 buf[2] = {addr >> 8, addr & 0xff};
+ int ret;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = buf,
+ }, {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 2,
+ .buf = buf,
+ },
+ };
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret < 0) {
+ dev_warn(&client->dev, "Reading register %x from %x failed\n",
+ addr, client->addr);
+ return ret;
+ }
+
+ return buf[0] & 0xff; /* no sign-extension */
+}
+
+static int imx074_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct imx074_datafmt *fmt = imx074_find_datafmt(mf->code);
+
+ dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code);
+
+ if (!fmt) {
+ mf->code = imx074_colour_fmts[0].code;
+ mf->colorspace = imx074_colour_fmts[0].colorspace;
+ }
+
+ mf->width = IMX074_WIDTH;
+ mf->height = IMX074_HEIGHT;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int imx074_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct imx074 *priv = to_imx074(client);
+
+ dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code);
+
+ /* MIPI CSI could have changed the format, double-check */
+ if (!imx074_find_datafmt(mf->code))
+ return -EINVAL;
+
+ imx074_try_fmt(sd, mf);
+
+ priv->fmt = imx074_find_datafmt(mf->code);
+
+ return 0;
+}
+
+static int imx074_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct imx074 *priv = to_imx074(client);
+
+ const struct imx074_datafmt *fmt = priv->fmt;
+
+ mf->code = fmt->code;
+ mf->colorspace = fmt->colorspace;
+ mf->width = IMX074_WIDTH;
+ mf->height = IMX074_HEIGHT;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int imx074_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct v4l2_rect *rect = &a->c;
+
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ rect->top = 0;
+ rect->left = 0;
+ rect->width = IMX074_WIDTH;
+ rect->height = IMX074_HEIGHT;
+
+ return 0;
+}
+
+static int imx074_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ a->bounds.width = IMX074_WIDTH;
+ a->bounds.height = IMX074_HEIGHT;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int imx074_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if ((unsigned int)index >= ARRAY_SIZE(imx074_colour_fmts))
+ return -EINVAL;
+
+ *code = imx074_colour_fmts[index].code;
+ return 0;
+}
+
+static int imx074_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ /* MODE_SELECT: stream or standby */
+ return reg_write(client, MODE_SELECT, !!enable);
+}
+
+static int imx074_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+
+ if (id->match.addr != client->addr)
+ return -ENODEV;
+
+ id->ident = V4L2_IDENT_IMX074;
+ id->revision = 0;
+
+ return 0;
+}
+
+static int imx074_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ cfg->type = V4L2_MBUS_CSI2;
+ cfg->flags = V4L2_MBUS_CSI2_2_LANE |
+ V4L2_MBUS_CSI2_CHANNEL_0 |
+ V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops imx074_subdev_video_ops = {
+ .s_stream = imx074_s_stream,
+ .s_mbus_fmt = imx074_s_fmt,
+ .g_mbus_fmt = imx074_g_fmt,
+ .try_mbus_fmt = imx074_try_fmt,
+ .enum_mbus_fmt = imx074_enum_fmt,
+ .g_crop = imx074_g_crop,
+ .cropcap = imx074_cropcap,
+ .g_mbus_config = imx074_g_mbus_config,
+};
+
+static struct v4l2_subdev_core_ops imx074_subdev_core_ops = {
+ .g_chip_ident = imx074_g_chip_ident,
+};
+
+static struct v4l2_subdev_ops imx074_subdev_ops = {
+ .core = &imx074_subdev_core_ops,
+ .video = &imx074_subdev_video_ops,
+};
+
+static int imx074_video_probe(struct i2c_client *client)
+{
+ int ret;
+ u16 id;
+
+ /* Read sensor Model ID */
+ ret = reg_read(client, 0);
+ if (ret < 0)
+ return ret;
+
+ id = ret << 8;
+
+ ret = reg_read(client, 1);
+ if (ret < 0)
+ return ret;
+
+ id |= ret;
+
+ dev_info(&client->dev, "Chip ID 0x%04x detected\n", id);
+
+ if (id != 0x74)
+ return -ENODEV;
+
+ /* PLL Setting EXTCLK=24MHz, 22.5times */
+ reg_write(client, PLL_MULTIPLIER, 0x2D);
+ reg_write(client, PRE_PLL_CLK_DIV, 0x02);
+ reg_write(client, PLSTATIM, 0x4B);
+
+ /* 2-lane mode */
+ reg_write(client, 0x3024, 0x00);
+
+ reg_write(client, IMAGE_ORIENTATION, 0x00);
+
+ /* select RAW mode:
+ * 0x08+0x08 = top 8 bits
+ * 0x0a+0x08 = compressed 8-bits
+ * 0x0a+0x0a = 10 bits
+ */
+ reg_write(client, 0x0112, 0x08);
+ reg_write(client, 0x0113, 0x08);
+
+ /* Base setting for High frame mode */
+ reg_write(client, VNDMY_ABLMGSHLMT, 0x80);
+ reg_write(client, Y_OPBADDR_START_DI, 0x08);
+ reg_write(client, 0x3015, 0x37);
+ reg_write(client, 0x301C, 0x01);
+ reg_write(client, 0x302C, 0x05);
+ reg_write(client, 0x3031, 0x26);
+ reg_write(client, 0x3041, 0x60);
+ reg_write(client, 0x3051, 0x24);
+ reg_write(client, 0x3053, 0x34);
+ reg_write(client, 0x3057, 0xC0);
+ reg_write(client, 0x305C, 0x09);
+ reg_write(client, 0x305D, 0x07);
+ reg_write(client, 0x3060, 0x30);
+ reg_write(client, 0x3065, 0x00);
+ reg_write(client, 0x30AA, 0x08);
+ reg_write(client, 0x30AB, 0x1C);
+ reg_write(client, 0x30B0, 0x32);
+ reg_write(client, 0x30B2, 0x83);
+ reg_write(client, 0x30D3, 0x04);
+ reg_write(client, 0x3106, 0x78);
+ reg_write(client, 0x310C, 0x82);
+ reg_write(client, 0x3304, 0x05);
+ reg_write(client, 0x3305, 0x04);
+ reg_write(client, 0x3306, 0x11);
+ reg_write(client, 0x3307, 0x02);
+ reg_write(client, 0x3308, 0x0C);
+ reg_write(client, 0x3309, 0x06);
+ reg_write(client, 0x330A, 0x08);
+ reg_write(client, 0x330B, 0x04);
+ reg_write(client, 0x330C, 0x08);
+ reg_write(client, 0x330D, 0x06);
+ reg_write(client, 0x330E, 0x01);
+ reg_write(client, 0x3381, 0x00);
+
+ /* V : 1/2V-addition (1,3), H : 1/2H-averaging (1,3) -> Full HD */
+ /* 1608 = 1560 + 48 (black lines) */
+ reg_write(client, FRAME_LENGTH_LINES_HI, 0x06);
+ reg_write(client, FRAME_LENGTH_LINES_LO, 0x48);
+ reg_write(client, YADDR_START, 0x00);
+ reg_write(client, YADDR_END, 0x2F);
+ /* 0x838 == 2104 */
+ reg_write(client, X_OUTPUT_SIZE_MSB, 0x08);
+ reg_write(client, X_OUTPUT_SIZE_LSB, 0x38);
+ /* 0x618 == 1560 */
+ reg_write(client, Y_OUTPUT_SIZE_MSB, 0x06);
+ reg_write(client, Y_OUTPUT_SIZE_LSB, 0x18);
+ reg_write(client, X_EVEN_INC, 0x01);
+ reg_write(client, X_ODD_INC, 0x03);
+ reg_write(client, Y_EVEN_INC, 0x01);
+ reg_write(client, Y_ODD_INC, 0x03);
+ reg_write(client, HMODEADD, 0x00);
+ reg_write(client, VMODEADD, 0x16);
+ reg_write(client, VAPPLINE_START, 0x24);
+ reg_write(client, VAPPLINE_END, 0x53);
+ reg_write(client, SHUTTER, 0x00);
+ reg_write(client, HADDAVE, 0x80);
+
+ reg_write(client, LANESEL, 0x00);
+
+ reg_write(client, GROUPED_PARAMETER_HOLD, 0x00); /* off */
+
+ return 0;
+}
+
+static int imx074_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct imx074 *priv;
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ int ret;
+
+ if (!icl) {
+ dev_err(&client->dev, "IMX074: missing platform data!\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_warn(&adapter->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
+ return -EIO;
+ }
+
+ priv = kzalloc(sizeof(struct imx074), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ v4l2_i2c_subdev_init(&priv->subdev, client, &imx074_subdev_ops);
+
+ priv->fmt = &imx074_colour_fmts[0];
+
+ ret = imx074_video_probe(client);
+ if (ret < 0) {
+ kfree(priv);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int imx074_remove(struct i2c_client *client)
+{
+ struct imx074 *priv = to_imx074(client);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ if (icl->free_bus)
+ icl->free_bus(icl);
+ kfree(priv);
+
+ return 0;
+}
+
+static const struct i2c_device_id imx074_id[] = {
+ { "imx074", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, imx074_id);
+
+static struct i2c_driver imx074_i2c_driver = {
+ .driver = {
+ .name = "imx074",
+ },
+ .probe = imx074_probe,
+ .remove = imx074_remove,
+ .id_table = imx074_id,
+};
+
+module_i2c_driver(imx074_i2c_driver);
+
+MODULE_DESCRIPTION("Sony IMX074 Camera driver");
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/indycam.c b/drivers/media/video/indycam.c
new file mode 100644
index 00000000..54823633
--- /dev/null
+++ b/drivers/media/video/indycam.c
@@ -0,0 +1,390 @@
+/*
+ * indycam.c - Silicon Graphics IndyCam digital camera driver
+ *
+ * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
+ * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+
+/* IndyCam decodes stream of photons into digital image representation ;-) */
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+
+#include "indycam.h"
+
+#define INDYCAM_MODULE_VERSION "0.0.5"
+
+MODULE_DESCRIPTION("SGI IndyCam driver");
+MODULE_VERSION(INDYCAM_MODULE_VERSION);
+MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>");
+MODULE_LICENSE("GPL");
+
+
+// #define INDYCAM_DEBUG
+
+#ifdef INDYCAM_DEBUG
+#define dprintk(x...) printk("IndyCam: " x);
+#define indycam_regdump(client) indycam_regdump_debug(client)
+#else
+#define dprintk(x...)
+#define indycam_regdump(client)
+#endif
+
+struct indycam {
+ struct v4l2_subdev sd;
+ u8 version;
+};
+
+static inline struct indycam *to_indycam(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct indycam, sd);
+}
+
+static const u8 initseq[] = {
+ INDYCAM_CONTROL_AGCENA, /* INDYCAM_CONTROL */
+ INDYCAM_SHUTTER_60, /* INDYCAM_SHUTTER */
+ INDYCAM_GAIN_DEFAULT, /* INDYCAM_GAIN */
+ 0x00, /* INDYCAM_BRIGHTNESS (read-only) */
+ INDYCAM_RED_BALANCE_DEFAULT, /* INDYCAM_RED_BALANCE */
+ INDYCAM_BLUE_BALANCE_DEFAULT, /* INDYCAM_BLUE_BALANCE */
+ INDYCAM_RED_SATURATION_DEFAULT, /* INDYCAM_RED_SATURATION */
+ INDYCAM_BLUE_SATURATION_DEFAULT,/* INDYCAM_BLUE_SATURATION */
+};
+
+/* IndyCam register handling */
+
+static int indycam_read_reg(struct v4l2_subdev *sd, u8 reg, u8 *value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ if (reg == INDYCAM_REG_RESET) {
+ dprintk("indycam_read_reg(): "
+ "skipping write-only register %d\n", reg);
+ *value = 0;
+ return 0;
+ }
+
+ ret = i2c_smbus_read_byte_data(client, reg);
+
+ if (ret < 0) {
+ printk(KERN_ERR "IndyCam: indycam_read_reg(): read failed, "
+ "register = 0x%02x\n", reg);
+ return ret;
+ }
+
+ *value = (u8)ret;
+
+ return 0;
+}
+
+static int indycam_write_reg(struct v4l2_subdev *sd, u8 reg, u8 value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int err;
+
+ if (reg == INDYCAM_REG_BRIGHTNESS || reg == INDYCAM_REG_VERSION) {
+ dprintk("indycam_write_reg(): "
+ "skipping read-only register %d\n", reg);
+ return 0;
+ }
+
+ dprintk("Writing Reg %d = 0x%02x\n", reg, value);
+ err = i2c_smbus_write_byte_data(client, reg, value);
+
+ if (err) {
+ printk(KERN_ERR "IndyCam: indycam_write_reg(): write failed, "
+ "register = 0x%02x, value = 0x%02x\n", reg, value);
+ }
+ return err;
+}
+
+static int indycam_write_block(struct v4l2_subdev *sd, u8 reg,
+ u8 length, u8 *data)
+{
+ int i, err;
+
+ for (i = 0; i < length; i++) {
+ err = indycam_write_reg(sd, reg + i, data[i]);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+/* Helper functions */
+
+#ifdef INDYCAM_DEBUG
+static void indycam_regdump_debug(struct v4l2_subdev *sd)
+{
+ int i;
+ u8 val;
+
+ for (i = 0; i < 9; i++) {
+ indycam_read_reg(sd, i, &val);
+ dprintk("Reg %d = 0x%02x\n", i, val);
+ }
+}
+#endif
+
+static int indycam_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct indycam *camera = to_indycam(sd);
+ u8 reg;
+ int ret = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTOGAIN:
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ ret = indycam_read_reg(sd, INDYCAM_REG_CONTROL, &reg);
+ if (ret)
+ return -EIO;
+ if (ctrl->id == V4L2_CID_AUTOGAIN)
+ ctrl->value = (reg & INDYCAM_CONTROL_AGCENA)
+ ? 1 : 0;
+ else
+ ctrl->value = (reg & INDYCAM_CONTROL_AWBCTL)
+ ? 1 : 0;
+ break;
+ case V4L2_CID_EXPOSURE:
+ ret = indycam_read_reg(sd, INDYCAM_REG_SHUTTER, &reg);
+ if (ret)
+ return -EIO;
+ ctrl->value = ((s32)reg == 0x00) ? 0xff : ((s32)reg - 1);
+ break;
+ case V4L2_CID_GAIN:
+ ret = indycam_read_reg(sd, INDYCAM_REG_GAIN, &reg);
+ if (ret)
+ return -EIO;
+ ctrl->value = (s32)reg;
+ break;
+ case V4L2_CID_RED_BALANCE:
+ ret = indycam_read_reg(sd, INDYCAM_REG_RED_BALANCE, &reg);
+ if (ret)
+ return -EIO;
+ ctrl->value = (s32)reg;
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ ret = indycam_read_reg(sd, INDYCAM_REG_BLUE_BALANCE, &reg);
+ if (ret)
+ return -EIO;
+ ctrl->value = (s32)reg;
+ break;
+ case INDYCAM_CONTROL_RED_SATURATION:
+ ret = indycam_read_reg(sd,
+ INDYCAM_REG_RED_SATURATION, &reg);
+ if (ret)
+ return -EIO;
+ ctrl->value = (s32)reg;
+ break;
+ case INDYCAM_CONTROL_BLUE_SATURATION:
+ ret = indycam_read_reg(sd,
+ INDYCAM_REG_BLUE_SATURATION, &reg);
+ if (ret)
+ return -EIO;
+ ctrl->value = (s32)reg;
+ break;
+ case V4L2_CID_GAMMA:
+ if (camera->version == CAMERA_VERSION_MOOSE) {
+ ret = indycam_read_reg(sd,
+ INDYCAM_REG_GAMMA, &reg);
+ if (ret)
+ return -EIO;
+ ctrl->value = (s32)reg;
+ } else {
+ ctrl->value = INDYCAM_GAMMA_DEFAULT;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int indycam_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct indycam *camera = to_indycam(sd);
+ u8 reg;
+ int ret = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTOGAIN:
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ ret = indycam_read_reg(sd, INDYCAM_REG_CONTROL, &reg);
+ if (ret)
+ break;
+
+ if (ctrl->id == V4L2_CID_AUTOGAIN) {
+ if (ctrl->value)
+ reg |= INDYCAM_CONTROL_AGCENA;
+ else
+ reg &= ~INDYCAM_CONTROL_AGCENA;
+ } else {
+ if (ctrl->value)
+ reg |= INDYCAM_CONTROL_AWBCTL;
+ else
+ reg &= ~INDYCAM_CONTROL_AWBCTL;
+ }
+
+ ret = indycam_write_reg(sd, INDYCAM_REG_CONTROL, reg);
+ break;
+ case V4L2_CID_EXPOSURE:
+ reg = (ctrl->value == 0xff) ? 0x00 : (ctrl->value + 1);
+ ret = indycam_write_reg(sd, INDYCAM_REG_SHUTTER, reg);
+ break;
+ case V4L2_CID_GAIN:
+ ret = indycam_write_reg(sd, INDYCAM_REG_GAIN, ctrl->value);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ ret = indycam_write_reg(sd, INDYCAM_REG_RED_BALANCE,
+ ctrl->value);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ ret = indycam_write_reg(sd, INDYCAM_REG_BLUE_BALANCE,
+ ctrl->value);
+ break;
+ case INDYCAM_CONTROL_RED_SATURATION:
+ ret = indycam_write_reg(sd, INDYCAM_REG_RED_SATURATION,
+ ctrl->value);
+ break;
+ case INDYCAM_CONTROL_BLUE_SATURATION:
+ ret = indycam_write_reg(sd, INDYCAM_REG_BLUE_SATURATION,
+ ctrl->value);
+ break;
+ case V4L2_CID_GAMMA:
+ if (camera->version == CAMERA_VERSION_MOOSE) {
+ ret = indycam_write_reg(sd, INDYCAM_REG_GAMMA,
+ ctrl->value);
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/* I2C-interface */
+
+static int indycam_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct indycam *camera = to_indycam(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_INDYCAM,
+ camera->version);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops indycam_core_ops = {
+ .g_chip_ident = indycam_g_chip_ident,
+ .g_ctrl = indycam_g_ctrl,
+ .s_ctrl = indycam_s_ctrl,
+};
+
+static const struct v4l2_subdev_ops indycam_ops = {
+ .core = &indycam_core_ops,
+};
+
+static int indycam_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int err = 0;
+ struct indycam *camera;
+ struct v4l2_subdev *sd;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ camera = kzalloc(sizeof(struct indycam), GFP_KERNEL);
+ if (!camera)
+ return -ENOMEM;
+
+ sd = &camera->sd;
+ v4l2_i2c_subdev_init(sd, client, &indycam_ops);
+
+ camera->version = i2c_smbus_read_byte_data(client,
+ INDYCAM_REG_VERSION);
+ if (camera->version != CAMERA_VERSION_INDY &&
+ camera->version != CAMERA_VERSION_MOOSE) {
+ kfree(camera);
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO "IndyCam v%d.%d detected\n",
+ INDYCAM_VERSION_MAJOR(camera->version),
+ INDYCAM_VERSION_MINOR(camera->version));
+
+ indycam_regdump(sd);
+
+ // initialize
+ err = indycam_write_block(sd, 0, sizeof(initseq), (u8 *)&initseq);
+ if (err) {
+ printk(KERN_ERR "IndyCam initialization failed\n");
+ kfree(camera);
+ return -EIO;
+ }
+
+ indycam_regdump(sd);
+
+ // white balance
+ err = indycam_write_reg(sd, INDYCAM_REG_CONTROL,
+ INDYCAM_CONTROL_AGCENA | INDYCAM_CONTROL_AWBCTL);
+ if (err) {
+ printk(KERN_ERR "IndyCam: White balancing camera failed\n");
+ kfree(camera);
+ return -EIO;
+ }
+
+ indycam_regdump(sd);
+
+ printk(KERN_INFO "IndyCam initialized\n");
+
+ return 0;
+}
+
+static int indycam_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_indycam(sd));
+ return 0;
+}
+
+static const struct i2c_device_id indycam_id[] = {
+ { "indycam", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, indycam_id);
+
+static struct i2c_driver indycam_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "indycam",
+ },
+ .probe = indycam_probe,
+ .remove = indycam_remove,
+ .id_table = indycam_id,
+};
+
+module_i2c_driver(indycam_driver);
diff --git a/drivers/media/video/indycam.h b/drivers/media/video/indycam.h
new file mode 100644
index 00000000..881f21c4
--- /dev/null
+++ b/drivers/media/video/indycam.h
@@ -0,0 +1,93 @@
+/*
+ * indycam.h - Silicon Graphics IndyCam digital camera driver
+ *
+ * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
+ * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _INDYCAM_H_
+#define _INDYCAM_H_
+
+/* I2C address for the Guinness Camera */
+#define INDYCAM_ADDR 0x56
+
+/* Camera version */
+#define CAMERA_VERSION_INDY 0x10 /* v1.0 */
+#define CAMERA_VERSION_MOOSE 0x12 /* v1.2 */
+#define INDYCAM_VERSION_MAJOR(x) (((x) & 0xf0) >> 4)
+#define INDYCAM_VERSION_MINOR(x) ((x) & 0x0f)
+
+/* Register bus addresses */
+#define INDYCAM_REG_CONTROL 0x00
+#define INDYCAM_REG_SHUTTER 0x01
+#define INDYCAM_REG_GAIN 0x02
+#define INDYCAM_REG_BRIGHTNESS 0x03 /* read-only */
+#define INDYCAM_REG_RED_BALANCE 0x04
+#define INDYCAM_REG_BLUE_BALANCE 0x05
+#define INDYCAM_REG_RED_SATURATION 0x06
+#define INDYCAM_REG_BLUE_SATURATION 0x07
+#define INDYCAM_REG_GAMMA 0x08
+#define INDYCAM_REG_VERSION 0x0e /* read-only */
+#define INDYCAM_REG_RESET 0x0f /* write-only */
+
+#define INDYCAM_REG_LED 0x46
+#define INDYCAM_REG_ORIENTATION 0x47
+#define INDYCAM_REG_BUTTON 0x48
+
+/* Field definitions of registers */
+#define INDYCAM_CONTROL_AGCENA (1<<0) /* automatic gain control */
+#define INDYCAM_CONTROL_AWBCTL (1<<1) /* automatic white balance */
+ /* 2-3 are reserved */
+#define INDYCAM_CONTROL_EVNFLD (1<<4) /* read-only */
+
+#define INDYCAM_SHUTTER_10000 0x02 /* 1/10000 second */
+#define INDYCAM_SHUTTER_4000 0x04 /* 1/4000 second */
+#define INDYCAM_SHUTTER_2000 0x08 /* 1/2000 second */
+#define INDYCAM_SHUTTER_1000 0x10 /* 1/1000 second */
+#define INDYCAM_SHUTTER_500 0x20 /* 1/500 second */
+#define INDYCAM_SHUTTER_250 0x3f /* 1/250 second */
+#define INDYCAM_SHUTTER_125 0x7e /* 1/125 second */
+#define INDYCAM_SHUTTER_100 0x9e /* 1/100 second */
+#define INDYCAM_SHUTTER_60 0x00 /* 1/60 second */
+
+#define INDYCAM_LED_ACTIVE 0x10
+#define INDYCAM_LED_INACTIVE 0x30
+#define INDYCAM_ORIENTATION_BOTTOM_TO_TOP 0x40
+#define INDYCAM_BUTTON_RELEASED 0x10
+
+/* Values for controls */
+#define INDYCAM_SHUTTER_MIN 0x00
+#define INDYCAM_SHUTTER_MAX 0xff
+#define INDYCAM_GAIN_MIN 0x00
+#define INDYCAM_GAIN_MAX 0xff
+#define INDYCAM_RED_BALANCE_MIN 0x00
+#define INDYCAM_RED_BALANCE_MAX 0xff
+#define INDYCAM_BLUE_BALANCE_MIN 0x00
+#define INDYCAM_BLUE_BALANCE_MAX 0xff
+#define INDYCAM_RED_SATURATION_MIN 0x00
+#define INDYCAM_RED_SATURATION_MAX 0xff
+#define INDYCAM_BLUE_SATURATION_MIN 0x00
+#define INDYCAM_BLUE_SATURATION_MAX 0xff
+#define INDYCAM_GAMMA_MIN 0x00
+#define INDYCAM_GAMMA_MAX 0xff
+
+#define INDYCAM_AGC_DEFAULT 1
+#define INDYCAM_AWB_DEFAULT 0
+#define INDYCAM_SHUTTER_DEFAULT 0xff
+#define INDYCAM_GAIN_DEFAULT 0x80
+#define INDYCAM_RED_BALANCE_DEFAULT 0x18
+#define INDYCAM_BLUE_BALANCE_DEFAULT 0xa4
+#define INDYCAM_RED_SATURATION_DEFAULT 0x80
+#define INDYCAM_BLUE_SATURATION_DEFAULT 0xc0
+#define INDYCAM_GAMMA_DEFAULT 0x80
+
+/* Driver interface definitions */
+
+#define INDYCAM_CONTROL_RED_SATURATION (V4L2_CID_PRIVATE_BASE + 0)
+#define INDYCAM_CONTROL_BLUE_SATURATION (V4L2_CID_PRIVATE_BASE + 1)
+
+#endif
diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c
new file mode 100644
index 00000000..04f192a0
--- /dev/null
+++ b/drivers/media/video/ir-kbd-i2c.c
@@ -0,0 +1,489 @@
+/*
+ *
+ * keyboard input driver for i2c IR remote controls
+ *
+ * Copyright (c) 2000-2003 Gerd Knorr <kraxel@bytesex.org>
+ * modified for PixelView (BT878P+W/FM) by
+ * Michal Kochanowicz <mkochano@pld.org.pl>
+ * Christoph Bartelmus <lirc@bartelmus.de>
+ * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by
+ * Ulrich Mueller <ulrich.mueller42@web.de>
+ * modified for em2820 based USB TV tuners by
+ * Markus Rechberger <mrechberger@gmail.com>
+ * modified for DViCO Fusion HDTV 5 RT GOLD by
+ * Chaogui Zhang <czhang1974@gmail.com>
+ * modified for MSI TV@nywhere Plus by
+ * Henry Wong <henry@stuffedcow.net>
+ * Mark Schultz <n9xmj@yahoo.com>
+ * Brian Rogers <brian_rogers@comcast.net>
+ * modified for AVerMedia Cardbus by
+ * Oldrich Jedlicka <oldium.pro@seznam.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+
+#include <media/rc-core.h>
+#include <media/ir-kbd-i2c.h>
+
+/* ----------------------------------------------------------------------- */
+/* insmod parameters */
+
+static int debug;
+module_param(debug, int, 0644); /* debug level (0,1,2) */
+
+
+#define MODULE_NAME "ir-kbd-i2c"
+#define dprintk(level, fmt, arg...) if (debug >= level) \
+ printk(KERN_DEBUG MODULE_NAME ": " fmt , ## arg)
+
+/* ----------------------------------------------------------------------- */
+
+static int get_key_haup_common(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw,
+ int size, int offset)
+{
+ unsigned char buf[6];
+ int start, range, toggle, dev, code, ircode;
+
+ /* poll IR chip */
+ if (size != i2c_master_recv(ir->c, buf, size))
+ return -EIO;
+
+ /* split rc5 data block ... */
+ start = (buf[offset] >> 7) & 1;
+ range = (buf[offset] >> 6) & 1;
+ toggle = (buf[offset] >> 5) & 1;
+ dev = buf[offset] & 0x1f;
+ code = (buf[offset+1] >> 2) & 0x3f;
+
+ /* rc5 has two start bits
+ * the first bit must be one
+ * the second bit defines the command range (1 = 0-63, 0 = 64 - 127)
+ */
+ if (!start)
+ /* no key pressed */
+ return 0;
+ /*
+ * Hauppauge remotes (black/silver) always use
+ * specific device ids. If we do not filter the
+ * device ids then messages destined for devices
+ * such as TVs (id=0) will get through causing
+ * mis-fired events.
+ *
+ * We also filter out invalid key presses which
+ * produce annoying debug log entries.
+ */
+ ircode= (start << 12) | (toggle << 11) | (dev << 6) | code;
+ if ((ircode & 0x1fff)==0x1fff)
+ /* invalid key press */
+ return 0;
+
+ if (!range)
+ code += 64;
+
+ dprintk(1,"ir hauppauge (rc5): s%d r%d t%d dev=%d code=%d\n",
+ start, range, toggle, dev, code);
+
+ /* return key */
+ *ir_key = (dev << 8) | code;
+ *ir_raw = ircode;
+ return 1;
+}
+
+static int get_key_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+ return get_key_haup_common (ir, ir_key, ir_raw, 3, 0);
+}
+
+static int get_key_haup_xvr(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+ int ret;
+ unsigned char buf[1] = { 0 };
+
+ /*
+ * This is the same apparent "are you ready?" poll command observed
+ * watching Windows driver traffic and implemented in lirc_zilog. With
+ * this added, we get far saner remote behavior with z8 chips on usb
+ * connected devices, even with the default polling interval of 100ms.
+ */
+ ret = i2c_master_send(ir->c, buf, 1);
+ if (ret != 1)
+ return (ret < 0) ? ret : -EINVAL;
+
+ return get_key_haup_common (ir, ir_key, ir_raw, 6, 3);
+}
+
+static int get_key_pixelview(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+ unsigned char b;
+
+ /* poll IR chip */
+ if (1 != i2c_master_recv(ir->c, &b, 1)) {
+ dprintk(1,"read error\n");
+ return -EIO;
+ }
+ *ir_key = b;
+ *ir_raw = b;
+ return 1;
+}
+
+static int get_key_fusionhdtv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+ unsigned char buf[4];
+
+ /* poll IR chip */
+ if (4 != i2c_master_recv(ir->c, buf, 4)) {
+ dprintk(1,"read error\n");
+ return -EIO;
+ }
+
+ if(buf[0] !=0 || buf[1] !=0 || buf[2] !=0 || buf[3] != 0)
+ dprintk(2, "%s: 0x%2x 0x%2x 0x%2x 0x%2x\n", __func__,
+ buf[0], buf[1], buf[2], buf[3]);
+
+ /* no key pressed or signal from other ir remote */
+ if(buf[0] != 0x1 || buf[1] != 0xfe)
+ return 0;
+
+ *ir_key = buf[2];
+ *ir_raw = (buf[2] << 8) | buf[3];
+
+ return 1;
+}
+
+static int get_key_knc1(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+ unsigned char b;
+
+ /* poll IR chip */
+ if (1 != i2c_master_recv(ir->c, &b, 1)) {
+ dprintk(1,"read error\n");
+ return -EIO;
+ }
+
+ /* it seems that 0xFE indicates that a button is still hold
+ down, while 0xff indicates that no button is hold
+ down. 0xfe sequences are sometimes interrupted by 0xFF */
+
+ dprintk(2,"key %02x\n", b);
+
+ if (b == 0xff)
+ return 0;
+
+ if (b == 0xfe)
+ /* keep old data */
+ return 1;
+
+ *ir_key = b;
+ *ir_raw = b;
+ return 1;
+}
+
+static int get_key_avermedia_cardbus(struct IR_i2c *ir,
+ u32 *ir_key, u32 *ir_raw)
+{
+ unsigned char subaddr, key, keygroup;
+ struct i2c_msg msg[] = { { .addr = ir->c->addr, .flags = 0,
+ .buf = &subaddr, .len = 1},
+ { .addr = ir->c->addr, .flags = I2C_M_RD,
+ .buf = &key, .len = 1} };
+ subaddr = 0x0d;
+ if (2 != i2c_transfer(ir->c->adapter, msg, 2)) {
+ dprintk(1, "read error\n");
+ return -EIO;
+ }
+
+ if (key == 0xff)
+ return 0;
+
+ subaddr = 0x0b;
+ msg[1].buf = &keygroup;
+ if (2 != i2c_transfer(ir->c->adapter, msg, 2)) {
+ dprintk(1, "read error\n");
+ return -EIO;
+ }
+
+ if (keygroup == 0xff)
+ return 0;
+
+ dprintk(1, "read key 0x%02x/0x%02x\n", key, keygroup);
+ if (keygroup < 2 || keygroup > 3) {
+ /* Only a warning */
+ dprintk(1, "warning: invalid key group 0x%02x for key 0x%02x\n",
+ keygroup, key);
+ }
+ key |= (keygroup & 1) << 6;
+
+ *ir_key = key;
+ *ir_raw = key;
+ return 1;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int ir_key_poll(struct IR_i2c *ir)
+{
+ static u32 ir_key, ir_raw;
+ int rc;
+
+ dprintk(3, "%s\n", __func__);
+ rc = ir->get_key(ir, &ir_key, &ir_raw);
+ if (rc < 0) {
+ dprintk(2,"error\n");
+ return rc;
+ }
+
+ if (rc) {
+ dprintk(1, "%s: keycode = 0x%04x\n", __func__, ir_key);
+ rc_keydown(ir->rc, ir_key, 0);
+ }
+ return 0;
+}
+
+static void ir_work(struct work_struct *work)
+{
+ int rc;
+ struct IR_i2c *ir = container_of(work, struct IR_i2c, work.work);
+
+ rc = ir_key_poll(ir);
+ if (rc == -ENODEV) {
+ rc_unregister_device(ir->rc);
+ ir->rc = NULL;
+ return;
+ }
+
+ schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling_interval));
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ char *ir_codes = NULL;
+ const char *name = NULL;
+ u64 rc_type = RC_TYPE_UNKNOWN;
+ struct IR_i2c *ir;
+ struct rc_dev *rc = NULL;
+ struct i2c_adapter *adap = client->adapter;
+ unsigned short addr = client->addr;
+ int err;
+
+ ir = kzalloc(sizeof(struct IR_i2c), GFP_KERNEL);
+ if (!ir)
+ return -ENOMEM;
+
+ ir->c = client;
+ ir->polling_interval = DEFAULT_POLLING_INTERVAL;
+ i2c_set_clientdata(client, ir);
+
+ switch(addr) {
+ case 0x64:
+ name = "Pixelview";
+ ir->get_key = get_key_pixelview;
+ rc_type = RC_TYPE_OTHER;
+ ir_codes = RC_MAP_EMPTY;
+ break;
+ case 0x18:
+ case 0x1f:
+ case 0x1a:
+ name = "Hauppauge";
+ ir->get_key = get_key_haup;
+ rc_type = RC_TYPE_RC5;
+ ir_codes = RC_MAP_HAUPPAUGE;
+ break;
+ case 0x30:
+ name = "KNC One";
+ ir->get_key = get_key_knc1;
+ rc_type = RC_TYPE_OTHER;
+ ir_codes = RC_MAP_EMPTY;
+ break;
+ case 0x6b:
+ name = "FusionHDTV";
+ ir->get_key = get_key_fusionhdtv;
+ rc_type = RC_TYPE_RC5;
+ ir_codes = RC_MAP_FUSIONHDTV_MCE;
+ break;
+ case 0x40:
+ name = "AVerMedia Cardbus remote";
+ ir->get_key = get_key_avermedia_cardbus;
+ rc_type = RC_TYPE_OTHER;
+ ir_codes = RC_MAP_AVERMEDIA_CARDBUS;
+ break;
+ case 0x71:
+ name = "Hauppauge/Zilog Z8";
+ ir->get_key = get_key_haup_xvr;
+ rc_type = RC_TYPE_RC5;
+ ir_codes = RC_MAP_HAUPPAUGE;
+ break;
+ }
+
+ /* Let the caller override settings */
+ if (client->dev.platform_data) {
+ const struct IR_i2c_init_data *init_data =
+ client->dev.platform_data;
+
+ ir_codes = init_data->ir_codes;
+ rc = init_data->rc_dev;
+
+ name = init_data->name;
+ if (init_data->type)
+ rc_type = init_data->type;
+
+ if (init_data->polling_interval)
+ ir->polling_interval = init_data->polling_interval;
+
+ switch (init_data->internal_get_key_func) {
+ case IR_KBD_GET_KEY_CUSTOM:
+ /* The bridge driver provided us its own function */
+ ir->get_key = init_data->get_key;
+ break;
+ case IR_KBD_GET_KEY_PIXELVIEW:
+ ir->get_key = get_key_pixelview;
+ break;
+ case IR_KBD_GET_KEY_HAUP:
+ ir->get_key = get_key_haup;
+ break;
+ case IR_KBD_GET_KEY_KNC1:
+ ir->get_key = get_key_knc1;
+ break;
+ case IR_KBD_GET_KEY_FUSIONHDTV:
+ ir->get_key = get_key_fusionhdtv;
+ break;
+ case IR_KBD_GET_KEY_HAUP_XVR:
+ ir->get_key = get_key_haup_xvr;
+ break;
+ case IR_KBD_GET_KEY_AVERMEDIA_CARDBUS:
+ ir->get_key = get_key_avermedia_cardbus;
+ break;
+ }
+ }
+
+ if (!rc) {
+ /*
+ * If platform_data doesn't specify rc_dev, initilize it
+ * internally
+ */
+ rc = rc_allocate_device();
+ if (!rc) {
+ err = -ENOMEM;
+ goto err_out_free;
+ }
+ }
+ ir->rc = rc;
+
+ /* Make sure we are all setup before going on */
+ if (!name || !ir->get_key || !rc_type || !ir_codes) {
+ dprintk(1, ": Unsupported device at address 0x%02x\n",
+ addr);
+ err = -ENODEV;
+ goto err_out_free;
+ }
+
+ /* Sets name */
+ snprintf(ir->name, sizeof(ir->name), "i2c IR (%s)", name);
+ ir->ir_codes = ir_codes;
+
+ snprintf(ir->phys, sizeof(ir->phys), "%s/%s/ir0",
+ dev_name(&adap->dev),
+ dev_name(&client->dev));
+
+ /*
+ * Initialize input_dev fields
+ * It doesn't make sense to allow overriding them via platform_data
+ */
+ rc->input_id.bustype = BUS_I2C;
+ rc->input_phys = ir->phys;
+ rc->input_name = ir->name;
+
+ /*
+ * Initialize the other fields of rc_dev
+ */
+ rc->map_name = ir->ir_codes;
+ rc->allowed_protos = rc_type;
+ if (!rc->driver_name)
+ rc->driver_name = MODULE_NAME;
+
+ err = rc_register_device(rc);
+ if (err)
+ goto err_out_free;
+
+ printk(MODULE_NAME ": %s detected at %s [%s]\n",
+ ir->name, ir->phys, adap->name);
+
+ /* start polling via eventd */
+ INIT_DELAYED_WORK(&ir->work, ir_work);
+ schedule_delayed_work(&ir->work, 0);
+
+ return 0;
+
+ err_out_free:
+ /* Only frees rc if it were allocated internally */
+ rc_free_device(rc);
+ kfree(ir);
+ return err;
+}
+
+static int ir_remove(struct i2c_client *client)
+{
+ struct IR_i2c *ir = i2c_get_clientdata(client);
+
+ /* kill outstanding polls */
+ cancel_delayed_work_sync(&ir->work);
+
+ /* unregister device */
+ if (ir->rc)
+ rc_unregister_device(ir->rc);
+
+ /* free memory */
+ kfree(ir);
+ return 0;
+}
+
+static const struct i2c_device_id ir_kbd_id[] = {
+ /* Generic entry for any IR receiver */
+ { "ir_video", 0 },
+ /* IR device specific entries should be added here */
+ { "ir_rx_z8f0811_haup", 0 },
+ { "ir_rx_z8f0811_hdpvr", 0 },
+ { }
+};
+
+static struct i2c_driver ir_kbd_driver = {
+ .driver = {
+ .name = "ir-kbd-i2c",
+ },
+ .probe = ir_probe,
+ .remove = ir_remove,
+ .id_table = ir_kbd_id,
+};
+
+module_i2c_driver(ir_kbd_driver);
+
+/* ----------------------------------------------------------------------- */
+
+MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, Ulrich Mueller");
+MODULE_DESCRIPTION("input driver for i2c IR remote controls");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/ivtv/Kconfig b/drivers/media/video/ivtv/Kconfig
new file mode 100644
index 00000000..89f65914
--- /dev/null
+++ b/drivers/media/video/ivtv/Kconfig
@@ -0,0 +1,45 @@
+config VIDEO_IVTV
+ tristate "Conexant cx23416/cx23415 MPEG encoder/decoder support"
+ depends on VIDEO_V4L2 && PCI && I2C
+ select I2C_ALGOBIT
+ depends on RC_CORE
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ select VIDEO_CX2341X
+ select VIDEO_CX25840
+ select VIDEO_MSP3400
+ select VIDEO_SAA711X
+ select VIDEO_SAA717X
+ select VIDEO_SAA7127
+ select VIDEO_CS53L32A
+ select VIDEO_M52790
+ select VIDEO_WM8775
+ select VIDEO_WM8739
+ select VIDEO_VP27SMPX
+ select VIDEO_UPD64031A
+ select VIDEO_UPD64083
+ ---help---
+ This is a video4linux driver for Conexant cx23416 or cx23415 based
+ PCI personal video recorder devices.
+
+ This is used in devices such as the Hauppauge PVR-150/250/350/500
+ cards. There is a driver homepage at <http://www.ivtvdriver.org>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ivtv.
+
+config VIDEO_FB_IVTV
+ tristate "Conexant cx23415 framebuffer support"
+ depends on VIDEO_IVTV && FB
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ ---help---
+ This is a framebuffer driver for the Conexant cx23415 MPEG
+ encoder/decoder.
+
+ This is used in the Hauppauge PVR-350 card. There is a driver
+ homepage at <http://www.ivtvdriver.org>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ivtvfb.
diff --git a/drivers/media/video/ivtv/Makefile b/drivers/media/video/ivtv/Makefile
new file mode 100644
index 00000000..77de8a45
--- /dev/null
+++ b/drivers/media/video/ivtv/Makefile
@@ -0,0 +1,14 @@
+ivtv-objs := ivtv-routing.o ivtv-cards.o ivtv-controls.o \
+ ivtv-driver.o ivtv-fileops.o ivtv-firmware.o \
+ ivtv-gpio.o ivtv-i2c.o ivtv-ioctl.o ivtv-irq.o \
+ ivtv-mailbox.o ivtv-queue.o ivtv-streams.o ivtv-udma.o \
+ ivtv-vbi.o ivtv-yuv.o
+
+obj-$(CONFIG_VIDEO_IVTV) += ivtv.o
+obj-$(CONFIG_VIDEO_FB_IVTV) += ivtvfb.o
+
+ccflags-y += -I$(srctree)/drivers/media/video
+ccflags-y += -I$(srctree)/drivers/media/common/tuners
+ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
+ccflags-y += -I$(srctree)/drivers/media/dvb/frontends
+
diff --git a/drivers/media/video/ivtv/ivtv-cards.c b/drivers/media/video/ivtv/ivtv-cards.c
new file mode 100644
index 00000000..145e4749
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-cards.c
@@ -0,0 +1,1370 @@
+/*
+ Functions to query card hardware
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-cards.h"
+#include "ivtv-i2c.h"
+
+#include <media/msp3400.h>
+#include <media/m52790.h>
+#include <media/wm8775.h>
+#include <media/cs53l32a.h>
+#include <media/cx25840.h>
+#include <media/upd64031a.h>
+
+#define MSP_TUNER MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, \
+ MSP_DSP_IN_TUNER, MSP_DSP_IN_TUNER)
+#define MSP_SCART1 MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, \
+ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART)
+#define MSP_SCART2 MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1, \
+ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART)
+#define MSP_SCART3 MSP_INPUT(MSP_IN_SCART3, MSP_IN_TUNER1, \
+ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART)
+#define MSP_MONO MSP_INPUT(MSP_IN_MONO, MSP_IN_TUNER1, \
+ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART)
+
+#define V4L2_STD_PAL_SECAM (V4L2_STD_PAL|V4L2_STD_SECAM)
+
+/* usual i2c tuner addresses to probe */
+static struct ivtv_card_tuner_i2c ivtv_i2c_std = {
+ .radio = { I2C_CLIENT_END },
+ .demod = { 0x43, I2C_CLIENT_END },
+ .tv = { 0x61, 0x60, I2C_CLIENT_END },
+};
+
+/* as above, but with possible radio tuner */
+static struct ivtv_card_tuner_i2c ivtv_i2c_radio = {
+ .radio = { 0x60, I2C_CLIENT_END },
+ .demod = { 0x43, I2C_CLIENT_END },
+ .tv = { 0x61, I2C_CLIENT_END },
+};
+
+/* using the tda8290+75a combo */
+static struct ivtv_card_tuner_i2c ivtv_i2c_tda8290 = {
+ .radio = { I2C_CLIENT_END },
+ .demod = { I2C_CLIENT_END },
+ .tv = { 0x4b, I2C_CLIENT_END },
+};
+
+/********************** card configuration *******************************/
+
+/* Please add new PCI IDs to: http://pci-ids.ucw.cz/
+ This keeps the PCI ID database up to date. Note that the entries
+ must be added under vendor 0x4444 (Conexant) as subsystem IDs.
+ New vendor IDs should still be added to the vendor ID list. */
+
+/* Hauppauge PVR-250 cards */
+
+/* Note: for Hauppauge cards the tveeprom information is used instead of PCI IDs */
+static const struct ivtv_card ivtv_card_pvr250 = {
+ .type = IVTV_CARD_PVR_250,
+ .name = "Hauppauge WinTV PVR-250",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA7115,
+ .hw_audio = IVTV_HW_MSP34XX,
+ .hw_audio_ctrl = IVTV_HW_MSP34XX,
+ .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 |
+ IVTV_HW_TVEEPROM | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
+ { IVTV_CARD_INPUT_SVIDEO2, 2, IVTV_SAA71XX_SVIDEO1 },
+ { IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 },
+ { IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER },
+ { IVTV_CARD_INPUT_LINE_IN1, MSP_SCART1 },
+ { IVTV_CARD_INPUT_LINE_IN2, MSP_SCART3 },
+ },
+ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 },
+ .i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Hauppauge PVR-350 cards */
+
+/* Outputs for Hauppauge PVR350 cards */
+static struct ivtv_card_output ivtv_pvr350_outputs[] = {
+ {
+ .name = "S-Video + Composite",
+ .video_output = 0,
+ }, {
+ .name = "Composite",
+ .video_output = 1,
+ }, {
+ .name = "S-Video",
+ .video_output = 2,
+ }, {
+ .name = "RGB",
+ .video_output = 3,
+ }, {
+ .name = "YUV C",
+ .video_output = 4,
+ }, {
+ .name = "YUV V",
+ .video_output = 5,
+ }
+};
+
+static const struct ivtv_card ivtv_card_pvr350 = {
+ .type = IVTV_CARD_PVR_350,
+ .name = "Hauppauge WinTV PVR-350",
+ .v4l2_capabilities = IVTV_CAP_ENCODER | IVTV_CAP_DECODER,
+ .video_outputs = ivtv_pvr350_outputs,
+ .nof_outputs = ARRAY_SIZE(ivtv_pvr350_outputs),
+ .hw_video = IVTV_HW_SAA7115,
+ .hw_audio = IVTV_HW_MSP34XX,
+ .hw_audio_ctrl = IVTV_HW_MSP34XX,
+ .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 |
+ IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER |
+ IVTV_HW_I2C_IR_RX_HAUP_EXT | IVTV_HW_I2C_IR_RX_HAUP_INT,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
+ { IVTV_CARD_INPUT_SVIDEO2, 2, IVTV_SAA71XX_SVIDEO1 },
+ { IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 },
+ { IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER },
+ { IVTV_CARD_INPUT_LINE_IN1, MSP_SCART1 },
+ { IVTV_CARD_INPUT_LINE_IN2, MSP_SCART3 },
+ },
+ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 },
+ .i2c = &ivtv_i2c_std,
+};
+
+/* PVR-350 V1 boards have a different audio tuner input and use a
+ saa7114 instead of a saa7115.
+ Note that the info below comes from a pre-production model so it may
+ not be correct. Especially the audio behaves strangely (mono only it seems) */
+static const struct ivtv_card ivtv_card_pvr350_v1 = {
+ .type = IVTV_CARD_PVR_350_V1,
+ .name = "Hauppauge WinTV PVR-350 (V1)",
+ .v4l2_capabilities = IVTV_CAP_ENCODER | IVTV_CAP_DECODER,
+ .video_outputs = ivtv_pvr350_outputs,
+ .nof_outputs = ARRAY_SIZE(ivtv_pvr350_outputs),
+ .hw_video = IVTV_HW_SAA7114,
+ .hw_audio = IVTV_HW_MSP34XX,
+ .hw_audio_ctrl = IVTV_HW_MSP34XX,
+ .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7114 |
+ IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
+ { IVTV_CARD_INPUT_SVIDEO2, 2, IVTV_SAA71XX_SVIDEO1 },
+ { IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 },
+ { IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, MSP_MONO },
+ { IVTV_CARD_INPUT_LINE_IN1, MSP_SCART1 },
+ { IVTV_CARD_INPUT_LINE_IN2, MSP_SCART3 },
+ },
+ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 },
+ .i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Hauppauge PVR-150/PVR-500 cards */
+
+static const struct ivtv_card ivtv_card_pvr150 = {
+ .type = IVTV_CARD_PVR_150,
+ .name = "Hauppauge WinTV PVR-150",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_CX25840,
+ .hw_audio = IVTV_HW_CX25840,
+ .hw_audio_ctrl = IVTV_HW_CX25840,
+ .hw_muxer = IVTV_HW_WM8775,
+ .hw_all = IVTV_HW_WM8775 | IVTV_HW_CX25840 |
+ IVTV_HW_TVEEPROM | IVTV_HW_TUNER |
+ IVTV_HW_I2C_IR_RX_HAUP_EXT | IVTV_HW_I2C_IR_RX_HAUP_INT |
+ IVTV_HW_Z8F0811_IR_HAUP,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE7 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO1 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE3 },
+ { IVTV_CARD_INPUT_SVIDEO2, 2, CX25840_SVIDEO2 },
+ { IVTV_CARD_INPUT_COMPOSITE2, 2, CX25840_COMPOSITE4 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER,
+ CX25840_AUDIO8, WM8775_AIN2 },
+ { IVTV_CARD_INPUT_LINE_IN1,
+ CX25840_AUDIO_SERIAL, WM8775_AIN2 },
+ { IVTV_CARD_INPUT_LINE_IN2,
+ CX25840_AUDIO_SERIAL, WM8775_AIN3 },
+ },
+ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER,
+ CX25840_AUDIO_SERIAL, WM8775_AIN4 },
+ /* apparently needed for the IR blaster */
+ .gpio_init = { .direction = 0x1f01, .initial_value = 0x26f3 },
+ .i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* AVerMedia M179 cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_m179[] = {
+ { PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_AVERMEDIA, 0xa3cf },
+ { PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_AVERMEDIA, 0xa3ce },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_m179 = {
+ .type = IVTV_CARD_M179,
+ .name = "AVerMedia M179",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA7114,
+ .hw_audio = IVTV_HW_GPIO,
+ .hw_audio_ctrl = IVTV_HW_GPIO,
+ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER },
+ { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN },
+ },
+ .gpio_init = { .direction = 0xe380, .initial_value = 0x8290 },
+ .gpio_audio_input = { .mask = 0x8040, .tuner = 0x8000, .linein = 0x0000 },
+ .gpio_audio_mute = { .mask = 0x2000, .mute = 0x2000 },
+ .gpio_audio_mode = { .mask = 0x4300, .mono = 0x4000, .stereo = 0x0200,
+ .lang1 = 0x0200, .lang2 = 0x0100, .both = 0x0000 },
+ .gpio_audio_freq = { .mask = 0x0018, .f32000 = 0x0000,
+ .f44100 = 0x0008, .f48000 = 0x0010 },
+ .gpio_audio_detect = { .mask = 0x4000, .stereo = 0x0000 },
+ .tuners = {
+ /* As far as we know all M179 cards use this tuner */
+ { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_NTSC },
+ },
+ .pci_list = ivtv_pci_m179,
+ .i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan MPG600/Kuroutoshikou ITVC16-STVLP cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_mpg600[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0xfff3 },
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0xffff },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_mpg600 = {
+ .type = IVTV_CARD_MPG600,
+ .name = "Yuan MPG600, Kuroutoshikou ITVC16-STVLP",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA7115,
+ .hw_audio = IVTV_HW_GPIO,
+ .hw_audio_ctrl = IVTV_HW_GPIO,
+ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER },
+ { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN },
+ },
+ .gpio_init = { .direction = 0x3080, .initial_value = 0x0004 },
+ .gpio_audio_input = { .mask = 0x3000, .tuner = 0x0000, .linein = 0x2000 },
+ .gpio_audio_mute = { .mask = 0x0001, .mute = 0x0001 },
+ .gpio_audio_mode = { .mask = 0x000e, .mono = 0x0006, .stereo = 0x0004,
+ .lang1 = 0x0004, .lang2 = 0x0000, .both = 0x0008 },
+ .gpio_audio_detect = { .mask = 0x0900, .stereo = 0x0100 },
+ .tuners = {
+ /* The PAL tuner is confirmed */
+ { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FQ1216ME },
+ { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
+ },
+ .pci_list = ivtv_pci_mpg600,
+ .i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan MPG160/Kuroutoshikou ITVC15-STVLP cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_mpg160[] = {
+ { PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_YUAN1, 0 },
+ { PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_IODATA, 0x40a0 },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_mpg160 = {
+ .type = IVTV_CARD_MPG160,
+ .name = "YUAN MPG160, Kuroutoshikou ITVC15-STVLP, I/O Data GV-M2TV/PCI",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA7114,
+ .hw_audio = IVTV_HW_GPIO,
+ .hw_audio_ctrl = IVTV_HW_GPIO,
+ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER },
+ { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN },
+ },
+ .gpio_init = { .direction = 0x7080, .initial_value = 0x400c },
+ .gpio_audio_input = { .mask = 0x3000, .tuner = 0x0000, .linein = 0x2000 },
+ .gpio_audio_mute = { .mask = 0x0001, .mute = 0x0001 },
+ .gpio_audio_mode = { .mask = 0x000e, .mono = 0x0006, .stereo = 0x0004,
+ .lang1 = 0x0004, .lang2 = 0x0000, .both = 0x0008 },
+ .gpio_audio_detect = { .mask = 0x0900, .stereo = 0x0100 },
+ .tuners = {
+ { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FQ1216ME },
+ { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
+ },
+ .pci_list = ivtv_pci_mpg160,
+ .i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan PG600/Diamond PVR-550 cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_pg600[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_DIAMONDMM, 0x0070 },
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN3, 0x0600 },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_pg600 = {
+ .type = IVTV_CARD_PG600,
+ .name = "Yuan PG600, Diamond PVR-550",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_CX25840,
+ .hw_audio = IVTV_HW_CX25840,
+ .hw_audio_ctrl = IVTV_HW_CX25840,
+ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1,
+ CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
+ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL },
+ },
+ .tuners = {
+ { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FQ1216ME },
+ { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
+ },
+ .pci_list = ivtv_pci_pg600,
+ .i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Adaptec VideOh! AVC-2410 card */
+
+static const struct ivtv_card_pci_info ivtv_pci_avc2410[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ADAPTEC, 0x0093 },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_avc2410 = {
+ .type = IVTV_CARD_AVC2410,
+ .name = "Adaptec VideOh! AVC-2410",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA7115,
+ .hw_audio = IVTV_HW_MSP34XX,
+ .hw_audio_ctrl = IVTV_HW_MSP34XX,
+ .hw_muxer = IVTV_HW_CS53L32A,
+ .hw_all = IVTV_HW_MSP34XX | IVTV_HW_CS53L32A |
+ IVTV_HW_SAA7115 | IVTV_HW_TUNER |
+ IVTV_HW_I2C_IR_RX_ADAPTEC,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER,
+ MSP_TUNER, CS53L32A_IN0 },
+ { IVTV_CARD_INPUT_LINE_IN1,
+ MSP_SCART1, CS53L32A_IN2 },
+ },
+ /* This card has no eeprom and in fact the Windows driver relies
+ on the country/region setting of the user to decide which tuner
+ is available. */
+ .tuners = {
+ { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+ { .std = V4L2_STD_ALL - V4L2_STD_NTSC_M_JP,
+ .tuner = TUNER_PHILIPS_FM1236_MK3 },
+ { .std = V4L2_STD_NTSC_M_JP, .tuner = TUNER_PHILIPS_FQ1286 },
+ },
+ .pci_list = ivtv_pci_avc2410,
+ .i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Adaptec VideOh! AVC-2010 card */
+
+static const struct ivtv_card_pci_info ivtv_pci_avc2010[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ADAPTEC, 0x0092 },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_avc2010 = {
+ .type = IVTV_CARD_AVC2010,
+ .name = "Adaptec VideOh! AVC-2010",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA7115,
+ .hw_audio = IVTV_HW_CS53L32A,
+ .hw_audio_ctrl = IVTV_HW_CS53L32A,
+ .hw_all = IVTV_HW_CS53L32A | IVTV_HW_SAA7115,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_SVIDEO1, 0, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 0, IVTV_SAA71XX_COMPOSITE3 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_LINE_IN1, CS53L32A_IN2 },
+ },
+ /* Does not have a tuner */
+ .pci_list = ivtv_pci_avc2010,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Nagase Transgear 5000TV card */
+
+static const struct ivtv_card_pci_info ivtv_pci_tg5000tv[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xbfff },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_tg5000tv = {
+ .type = IVTV_CARD_TG5000TV,
+ .name = "Nagase Transgear 5000TV",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA7114 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X |
+ IVTV_HW_GPIO,
+ .hw_audio = IVTV_HW_GPIO,
+ .hw_audio_ctrl = IVTV_HW_GPIO,
+ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER |
+ IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO2 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER },
+ { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN },
+ },
+ .gr_config = UPD64031A_VERTICAL_EXTERNAL,
+ .gpio_init = { .direction = 0xe080, .initial_value = 0x8000 },
+ .gpio_audio_input = { .mask = 0x8080, .tuner = 0x8000, .linein = 0x0080 },
+ .gpio_audio_mute = { .mask = 0x6000, .mute = 0x6000 },
+ .gpio_audio_mode = { .mask = 0x4300, .mono = 0x4000, .stereo = 0x0200,
+ .lang1 = 0x0300, .lang2 = 0x0000, .both = 0x0200 },
+ .gpio_video_input = { .mask = 0x0030, .tuner = 0x0000,
+ .composite = 0x0010, .svideo = 0x0020 },
+ .tuners = {
+ { .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 },
+ },
+ .pci_list = ivtv_pci_tg5000tv,
+ .i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* AOpen VA2000MAX-SNT6 card */
+
+static const struct ivtv_card_pci_info ivtv_pci_va2000[] = {
+ { PCI_DEVICE_ID_IVTV16, 0, 0xff5f },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_va2000 = {
+ .type = IVTV_CARD_VA2000MAX_SNT6,
+ .name = "AOpen VA2000MAX-SNT6",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD6408X,
+ .hw_audio = IVTV_HW_MSP34XX,
+ .hw_audio_ctrl = IVTV_HW_MSP34XX,
+ .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 |
+ IVTV_HW_UPD6408X | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER },
+ },
+ .tuners = {
+ { .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 },
+ },
+ .pci_list = ivtv_pci_va2000,
+ .i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan MPG600GR/Kuroutoshikou CX23416GYC-STVLP cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_cx23416gyc[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0x0600 },
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN4, 0x0600 },
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_MELCO, 0x0523 },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_cx23416gyc = {
+ .type = IVTV_CARD_CX23416GYC,
+ .name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO |
+ IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+ .hw_audio = IVTV_HW_SAA717X,
+ .hw_audio_ctrl = IVTV_HW_SAA717X,
+ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER |
+ IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO3 |
+ IVTV_SAA717X_TUNER_FLAG },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO3 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_SAA717X_IN2 },
+ { IVTV_CARD_INPUT_LINE_IN1, IVTV_SAA717X_IN0 },
+ },
+ .gr_config = UPD64031A_VERTICAL_EXTERNAL,
+ .gpio_init = { .direction = 0xf880, .initial_value = 0x8800 },
+ .gpio_video_input = { .mask = 0x0020, .tuner = 0x0000,
+ .composite = 0x0020, .svideo = 0x0020 },
+ .gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000,
+ .f44100 = 0x4000, .f48000 = 0x8000 },
+ .tuners = {
+ { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+ { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
+ },
+ .pci_list = ivtv_pci_cx23416gyc,
+ .i2c = &ivtv_i2c_std,
+};
+
+static const struct ivtv_card ivtv_card_cx23416gyc_nogr = {
+ .type = IVTV_CARD_CX23416GYC_NOGR,
+ .name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP (no GR)",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO | IVTV_HW_UPD6408X,
+ .hw_audio = IVTV_HW_SAA717X,
+ .hw_audio_ctrl = IVTV_HW_SAA717X,
+ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER |
+ IVTV_HW_UPD6408X,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 |
+ IVTV_SAA717X_TUNER_FLAG },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_SAA717X_IN2 },
+ { IVTV_CARD_INPUT_LINE_IN1, IVTV_SAA717X_IN0 },
+ },
+ .gpio_init = { .direction = 0xf880, .initial_value = 0x8800 },
+ .gpio_video_input = { .mask = 0x0020, .tuner = 0x0000,
+ .composite = 0x0020, .svideo = 0x0020 },
+ .gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000,
+ .f44100 = 0x4000, .f48000 = 0x8000 },
+ .tuners = {
+ { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+ { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
+ },
+ .i2c = &ivtv_i2c_std,
+};
+
+static const struct ivtv_card ivtv_card_cx23416gyc_nogrycs = {
+ .type = IVTV_CARD_CX23416GYC_NOGRYCS,
+ .name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP (no GR/YCS)",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO,
+ .hw_audio = IVTV_HW_SAA717X,
+ .hw_audio_ctrl = IVTV_HW_SAA717X,
+ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 |
+ IVTV_SAA717X_TUNER_FLAG },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_SAA717X_IN2 },
+ { IVTV_CARD_INPUT_LINE_IN1, IVTV_SAA717X_IN0 },
+ },
+ .gpio_init = { .direction = 0xf880, .initial_value = 0x8800 },
+ .gpio_video_input = { .mask = 0x0020, .tuner = 0x0000,
+ .composite = 0x0020, .svideo = 0x0020 },
+ .gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000,
+ .f44100 = 0x4000, .f48000 = 0x8000 },
+ .tuners = {
+ { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+ { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
+ },
+ .i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* I/O Data GV-MVP/RX & GV-MVP/RX2W (dual tuner) cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_gv_mvprx[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd01e },
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd038 }, /* 2W unit #1 */
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd039 }, /* 2W unit #2 */
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_gv_mvprx = {
+ .type = IVTV_CARD_GV_MVPRX,
+ .name = "I/O Data GV-MVP/RX, GV-MVP/RX2W (dual tuner)",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+ .hw_audio = IVTV_HW_GPIO,
+ .hw_audio_ctrl = IVTV_HW_WM8739,
+ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_VP27SMPX |
+ IVTV_HW_TUNER | IVTV_HW_WM8739 |
+ IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO1 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER },
+ { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN },
+ },
+ .gpio_init = { .direction = 0xc301, .initial_value = 0x0200 },
+ .gpio_audio_input = { .mask = 0xffff, .tuner = 0x0200, .linein = 0x0300 },
+ .tuners = {
+ /* This card has the Panasonic VP27 tuner */
+ { .std = V4L2_STD_MN, .tuner = TUNER_PANASONIC_VP27 },
+ },
+ .pci_list = ivtv_pci_gv_mvprx,
+ .i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* I/O Data GV-MVP/RX2E card */
+
+static const struct ivtv_card_pci_info ivtv_pci_gv_mvprx2e[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd025 },
+ {0, 0, 0}
+};
+
+static const struct ivtv_card ivtv_card_gv_mvprx2e = {
+ .type = IVTV_CARD_GV_MVPRX2E,
+ .name = "I/O Data GV-MVP/RX2E",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA7115,
+ .hw_audio = IVTV_HW_GPIO,
+ .hw_audio_ctrl = IVTV_HW_WM8739,
+ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER |
+ IVTV_HW_VP27SMPX | IVTV_HW_WM8739,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER },
+ { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN },
+ },
+ .gpio_init = { .direction = 0xc301, .initial_value = 0x0200 },
+ .gpio_audio_input = { .mask = 0xffff, .tuner = 0x0200, .linein = 0x0300 },
+ .tuners = {
+ /* This card has the Panasonic VP27 tuner */
+ { .std = V4L2_STD_MN, .tuner = TUNER_PANASONIC_VP27 },
+ },
+ .pci_list = ivtv_pci_gv_mvprx2e,
+ .i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* GotVIEW PCI DVD card */
+
+static const struct ivtv_card_pci_info ivtv_pci_gotview_pci_dvd[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0x0600 },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_gotview_pci_dvd = {
+ .type = IVTV_CARD_GOTVIEW_PCI_DVD,
+ .name = "GotView PCI DVD",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA717X,
+ .hw_audio = IVTV_HW_SAA717X,
+ .hw_audio_ctrl = IVTV_HW_SAA717X,
+ .hw_all = IVTV_HW_SAA717X | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE1 }, /* pin 116 */
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, /* pin 114/109 */
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 }, /* pin 118 */
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_SAA717X_IN0 },
+ { IVTV_CARD_INPUT_LINE_IN1, IVTV_SAA717X_IN2 },
+ },
+ .gpio_init = { .direction = 0xf000, .initial_value = 0xA000 },
+ .tuners = {
+ /* This card has a Philips FQ1216ME MK3 tuner */
+ { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+ },
+ .pci_list = ivtv_pci_gotview_pci_dvd,
+ .i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* GotVIEW PCI DVD2 Deluxe card */
+
+static const struct ivtv_card_pci_info ivtv_pci_gotview_pci_dvd2[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_GOTVIEW1, 0x0600 },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_gotview_pci_dvd2 = {
+ .type = IVTV_CARD_GOTVIEW_PCI_DVD2,
+ .name = "GotView PCI DVD2 Deluxe",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_CX25840,
+ .hw_audio = IVTV_HW_CX25840,
+ .hw_audio_ctrl = IVTV_HW_CX25840,
+ .hw_muxer = IVTV_HW_GPIO,
+ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1,
+ CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5, 0 },
+ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 },
+ },
+ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 },
+ .gpio_init = { .direction = 0x0800, .initial_value = 0 },
+ .gpio_audio_input = { .mask = 0x0800, .tuner = 0, .linein = 0, .radio = 0x0800 },
+ .tuners = {
+ /* This card has a Philips FQ1216ME MK5 tuner */
+ { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+ },
+ .pci_list = ivtv_pci_gotview_pci_dvd2,
+ .i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan MPC622 miniPCI card */
+
+static const struct ivtv_card_pci_info ivtv_pci_yuan_mpc622[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN2, 0xd998 },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_yuan_mpc622 = {
+ .type = IVTV_CARD_YUAN_MPC622,
+ .name = "Yuan MPC622",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_CX25840,
+ .hw_audio = IVTV_HW_CX25840,
+ .hw_audio_ctrl = IVTV_HW_CX25840,
+ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1,
+ CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
+ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL },
+ },
+ .gpio_init = { .direction = 0x00ff, .initial_value = 0x0002 },
+ .tuners = {
+ /* This card has the TDA8290/TDA8275 tuner chips */
+ { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_TDA8290 },
+ },
+ .pci_list = ivtv_pci_yuan_mpc622,
+ .i2c = &ivtv_i2c_tda8290,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* DIGITAL COWBOY DCT-MTVP1 card */
+
+static const struct ivtv_card_pci_info ivtv_pci_dctmvtvp1[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xbfff },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_dctmvtvp1 = {
+ .type = IVTV_CARD_DCTMTVP1,
+ .name = "Digital Cowboy DCT-MTVP1",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X |
+ IVTV_HW_GPIO,
+ .hw_audio = IVTV_HW_GPIO,
+ .hw_audio_ctrl = IVTV_HW_GPIO,
+ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER |
+ IVTV_HW_UPD64031A | IVTV_HW_UPD6408X,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO2 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER },
+ { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN },
+ },
+ .gpio_init = { .direction = 0xe080, .initial_value = 0x8000 },
+ .gpio_audio_input = { .mask = 0x8080, .tuner = 0x8000, .linein = 0x0080 },
+ .gpio_audio_mute = { .mask = 0x6000, .mute = 0x6000 },
+ .gpio_audio_mode = { .mask = 0x4300, .mono = 0x4000, .stereo = 0x0200,
+ .lang1 = 0x0300, .lang2 = 0x0000, .both = 0x0200 },
+ .gpio_video_input = { .mask = 0x0030, .tuner = 0x0000,
+ .composite = 0x0010, .svideo = 0x0020},
+ .tuners = {
+ { .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 },
+ },
+ .pci_list = ivtv_pci_dctmvtvp1,
+ .i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan PG600-2/GotView PCI DVD Lite cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_pg600v2[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN3, 0x0600 },
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_GOTVIEW2, 0x0600 },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_pg600v2 = {
+ .type = IVTV_CARD_PG600V2,
+ .name = "Yuan PG600-2, GotView PCI DVD Lite",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_CX25840,
+ .hw_audio = IVTV_HW_CX25840,
+ .hw_audio_ctrl = IVTV_HW_CX25840,
+ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+ /* XC2028 support apparently works for the Yuan, it's still
+ uncertain whether it also works with the GotView. */
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1,
+ CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
+ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL },
+ },
+ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
+ .xceive_pin = 12,
+ .tuners = {
+ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+ },
+ .pci_list = ivtv_pci_pg600v2,
+ .i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Club3D ZAP-TV1x01 cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_club3d[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN3, 0x0600 },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_club3d = {
+ .type = IVTV_CARD_CLUB3D,
+ .name = "Club3D ZAP-TV1x01",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_CX25840,
+ .hw_audio = IVTV_HW_CX25840,
+ .hw_audio_ctrl = IVTV_HW_CX25840,
+ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1,
+ CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE3 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
+ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL },
+ },
+ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
+ .xceive_pin = 12,
+ .tuners = {
+ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+ },
+ .pci_list = ivtv_pci_club3d,
+ .i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* AVerTV MCE 116 Plus (M116) card */
+
+static const struct ivtv_card_pci_info ivtv_pci_avertv_mce116[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc439 },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_avertv_mce116 = {
+ .type = IVTV_CARD_AVERTV_MCE116,
+ .name = "AVerTV MCE 116 Plus",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_CX25840,
+ .hw_audio = IVTV_HW_CX25840,
+ .hw_audio_ctrl = IVTV_HW_CX25840,
+ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | IVTV_HW_WM8739 |
+ IVTV_HW_I2C_IR_RX_AVER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO3 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
+ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 },
+ },
+ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
+ /* enable line-in */
+ .gpio_init = { .direction = 0xe000, .initial_value = 0x4000 },
+ .xceive_pin = 10,
+ .tuners = {
+ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+ },
+ .pci_list = ivtv_pci_avertv_mce116,
+ .i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* AVerMedia PVR-150 Plus / AVerTV M113 cards with a Daewoo/Partsnic Tuner */
+
+static const struct ivtv_card_pci_info ivtv_pci_aver_pvr150[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc034 }, /* NTSC */
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc035 }, /* NTSC FM */
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_aver_pvr150 = {
+ .type = IVTV_CARD_AVER_PVR150PLUS,
+ .name = "AVerMedia PVR-150 Plus / AVerTV M113 Partsnic (Daewoo) Tuner",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_CX25840,
+ .hw_audio = IVTV_HW_CX25840,
+ .hw_audio_ctrl = IVTV_HW_CX25840,
+ .hw_muxer = IVTV_HW_GPIO,
+ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER |
+ IVTV_HW_WM8739 | IVTV_HW_GPIO,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO3 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5, 0 },
+ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 },
+ },
+ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 },
+ /* The 74HC4052 Dual 4:1 multiplexer is controlled by 2 GPIO lines */
+ .gpio_init = { .direction = 0xc000, .initial_value = 0 },
+ .gpio_audio_input = { .mask = 0xc000,
+ .tuner = 0x0000,
+ .linein = 0x4000,
+ .radio = 0x8000 },
+ .tuners = {
+ /* Subsystem ID's 0xc03[45] have a Partsnic PTI-5NF05 tuner */
+ { .std = V4L2_STD_MN, .tuner = TUNER_PARTSNIC_PTI_5NF05 },
+ },
+ .pci_list = ivtv_pci_aver_pvr150,
+ /* Subsystem ID 0xc035 has a TEA5767(?) FM tuner, 0xc034 does not */
+ .i2c = &ivtv_i2c_radio,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* AVerMedia UltraTV 1500 MCE (newer non-cx88 version, M113 variant) card */
+
+static const struct ivtv_card_pci_info ivtv_pci_aver_ultra1500mce[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc019 }, /* NTSC */
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc01b }, /* PAL/SECAM */
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_aver_ultra1500mce = {
+ .type = IVTV_CARD_AVER_ULTRA1500MCE,
+ .name = "AVerMedia UltraTV 1500 MCE / AVerTV M113 Philips Tuner",
+ .comment = "For non-NTSC tuners, use the pal= or secam= module options",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_CX25840,
+ .hw_audio = IVTV_HW_CX25840,
+ .hw_audio_ctrl = IVTV_HW_CX25840,
+ .hw_muxer = IVTV_HW_GPIO,
+ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER |
+ IVTV_HW_WM8739 | IVTV_HW_GPIO,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO3 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5, 0 },
+ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 },
+ },
+ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 },
+ /* The 74HC4052 Dual 4:1 multiplexer is controlled by 2 GPIO lines */
+ .gpio_init = { .direction = 0xc000, .initial_value = 0 },
+ .gpio_audio_input = { .mask = 0xc000,
+ .tuner = 0x0000,
+ .linein = 0x4000,
+ .radio = 0x8000 },
+ .tuners = {
+ /* The UltraTV 1500 MCE has a Philips FM1236 MK5 TV/FM tuner */
+ { .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FM1236_MK3 },
+ { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216MK5 },
+ },
+ .pci_list = ivtv_pci_aver_ultra1500mce,
+ .i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* AVerMedia EZMaker PCI Deluxe card */
+
+static const struct ivtv_card_pci_info ivtv_pci_aver_ezmaker[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc03f },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_aver_ezmaker = {
+ .type = IVTV_CARD_AVER_EZMAKER,
+ .name = "AVerMedia EZMaker PCI Deluxe",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_CX25840,
+ .hw_audio = IVTV_HW_CX25840,
+ .hw_audio_ctrl = IVTV_HW_CX25840,
+ .hw_all = IVTV_HW_CX25840 | IVTV_HW_WM8739,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_SVIDEO1, 0, CX25840_SVIDEO3 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 0, CX25840_COMPOSITE1 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 0 },
+ },
+ .gpio_init = { .direction = 0x4000, .initial_value = 0x4000 },
+ /* Does not have a tuner */
+ .pci_list = ivtv_pci_aver_ezmaker,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* ASUS Falcon2 */
+
+static const struct ivtv_card_pci_info ivtv_pci_asus_falcon2[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ASUSTEK, 0x4b66 },
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ASUSTEK, 0x462e },
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ASUSTEK, 0x4b2e },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_asus_falcon2 = {
+ .type = IVTV_CARD_ASUS_FALCON2,
+ .name = "ASUS Falcon2",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_CX25840,
+ .hw_audio = IVTV_HW_CX25840,
+ .hw_audio_ctrl = IVTV_HW_CX25840,
+ .hw_muxer = IVTV_HW_M52790,
+ .hw_all = IVTV_HW_CX25840 | IVTV_HW_M52790 | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO3 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 2, CX25840_COMPOSITE2 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5, M52790_IN_TUNER },
+ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL,
+ M52790_IN_V2 | M52790_SW1_YCMIX | M52790_SW2_YCMIX },
+ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, M52790_IN_V2 },
+ },
+ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, M52790_IN_TUNER },
+ .tuners = {
+ { .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FM1236_MK3 },
+ },
+ .pci_list = ivtv_pci_asus_falcon2,
+ .i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* AVerMedia M104 miniPCI card */
+
+static const struct ivtv_card_pci_info ivtv_pci_aver_m104[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc136 },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_aver_m104 = {
+ .type = IVTV_CARD_AVER_M104,
+ .name = "AVerMedia M104",
+ .comment = "Not yet supported!\n",
+ .v4l2_capabilities = 0, /*IVTV_CAP_ENCODER,*/
+ .hw_video = IVTV_HW_CX25840,
+ .hw_audio = IVTV_HW_CX25840,
+ .hw_audio_ctrl = IVTV_HW_CX25840,
+ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | IVTV_HW_WM8739,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_SVIDEO1, 0, CX25840_SVIDEO3 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 0, CX25840_COMPOSITE1 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 },
+ },
+ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 },
+ /* enable line-in + reset tuner */
+ .gpio_init = { .direction = 0xe000, .initial_value = 0x4000 },
+ .xceive_pin = 10,
+ .tuners = {
+ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+ },
+ .pci_list = ivtv_pci_aver_m104,
+ .i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Buffalo PC-MV5L/PCI cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_buffalo[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_MELCO, 0x052b },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_buffalo = {
+ .type = IVTV_CARD_BUFFALO_MV5L,
+ .name = "Buffalo PC-MV5L/PCI",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_CX25840,
+ .hw_audio = IVTV_HW_CX25840,
+ .hw_audio_ctrl = IVTV_HW_CX25840,
+ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1,
+ CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
+ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL },
+ },
+ .xceive_pin = 12,
+ .tuners = {
+ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+ },
+ .pci_list = ivtv_pci_buffalo,
+ .i2c = &ivtv_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+/* Sony Kikyou */
+
+static const struct ivtv_card_pci_info ivtv_pci_kikyou[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_SONY, 0x813d },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_kikyou = {
+ .type = IVTV_CARD_KIKYOU,
+ .name = "Sony VAIO Giga Pocket (ENX Kikyou)",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_SAA7115,
+ .hw_audio = IVTV_HW_GPIO,
+ .hw_audio_ctrl = IVTV_HW_GPIO,
+ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE1 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE1 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO1 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER },
+ { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN },
+ { IVTV_CARD_INPUT_LINE_IN2, IVTV_GPIO_LINE_IN },
+ },
+ .gpio_init = { .direction = 0x03e1, .initial_value = 0x0320 },
+ .gpio_audio_input = { .mask = 0x0060,
+ .tuner = 0x0020,
+ .linein = 0x0000,
+ .radio = 0x0060 },
+ .gpio_audio_mute = { .mask = 0x0000,
+ .mute = 0x0000 }, /* 0x200? Disable for now. */
+ .gpio_audio_mode = { .mask = 0x0080,
+ .mono = 0x0000,
+ .stereo = 0x0000, /* SAP */
+ .lang1 = 0x0080,
+ .lang2 = 0x0000,
+ .both = 0x0080 },
+ .tuners = {
+ { .std = V4L2_STD_ALL, .tuner = TUNER_SONY_BTF_PXN01Z },
+ },
+ .pci_list = ivtv_pci_kikyou,
+ .i2c = &ivtv_i2c_std,
+};
+
+
+static const struct ivtv_card *ivtv_card_list[] = {
+ &ivtv_card_pvr250,
+ &ivtv_card_pvr350,
+ &ivtv_card_pvr150,
+ &ivtv_card_m179,
+ &ivtv_card_mpg600,
+ &ivtv_card_mpg160,
+ &ivtv_card_pg600,
+ &ivtv_card_avc2410,
+ &ivtv_card_avc2010,
+ &ivtv_card_tg5000tv,
+ &ivtv_card_va2000,
+ &ivtv_card_cx23416gyc,
+ &ivtv_card_gv_mvprx,
+ &ivtv_card_gv_mvprx2e,
+ &ivtv_card_gotview_pci_dvd,
+ &ivtv_card_gotview_pci_dvd2,
+ &ivtv_card_yuan_mpc622,
+ &ivtv_card_dctmvtvp1,
+ &ivtv_card_pg600v2,
+ &ivtv_card_club3d,
+ &ivtv_card_avertv_mce116,
+ &ivtv_card_asus_falcon2,
+ &ivtv_card_aver_pvr150,
+ &ivtv_card_aver_ezmaker,
+ &ivtv_card_aver_m104,
+ &ivtv_card_buffalo,
+ &ivtv_card_aver_ultra1500mce,
+ &ivtv_card_kikyou,
+
+ /* Variations of standard cards but with the same PCI IDs.
+ These cards must come last in this list. */
+ &ivtv_card_pvr350_v1,
+ &ivtv_card_cx23416gyc_nogr,
+ &ivtv_card_cx23416gyc_nogrycs,
+};
+
+const struct ivtv_card *ivtv_get_card(u16 index)
+{
+ if (index >= ARRAY_SIZE(ivtv_card_list))
+ return NULL;
+ return ivtv_card_list[index];
+}
+
+int ivtv_get_input(struct ivtv *itv, u16 index, struct v4l2_input *input)
+{
+ const struct ivtv_card_video_input *card_input = itv->card->video_inputs + index;
+ static const char * const input_strs[] = {
+ "Tuner 1",
+ "S-Video 1",
+ "S-Video 2",
+ "Composite 1",
+ "Composite 2",
+ "Composite 3"
+ };
+
+ if (index >= itv->nof_inputs)
+ return -EINVAL;
+ input->index = index;
+ strlcpy(input->name, input_strs[card_input->video_type - 1],
+ sizeof(input->name));
+ input->type = (card_input->video_type == IVTV_CARD_INPUT_VID_TUNER ?
+ V4L2_INPUT_TYPE_TUNER : V4L2_INPUT_TYPE_CAMERA);
+ input->audioset = (1 << itv->nof_audio_inputs) - 1;
+ input->std = (input->type == V4L2_INPUT_TYPE_TUNER) ?
+ itv->tuner_std : V4L2_STD_ALL;
+ return 0;
+}
+
+int ivtv_get_output(struct ivtv *itv, u16 index, struct v4l2_output *output)
+{
+ const struct ivtv_card_output *card_output = itv->card->video_outputs + index;
+
+ if (index >= itv->card->nof_outputs)
+ return -EINVAL;
+ output->index = index;
+ strlcpy(output->name, card_output->name, sizeof(output->name));
+ output->type = V4L2_OUTPUT_TYPE_ANALOG;
+ output->audioset = 1;
+ output->std = V4L2_STD_ALL;
+ return 0;
+}
+
+int ivtv_get_audio_input(struct ivtv *itv, u16 index, struct v4l2_audio *audio)
+{
+ const struct ivtv_card_audio_input *aud_input = itv->card->audio_inputs + index;
+ static const char * const input_strs[] = {
+ "Tuner 1",
+ "Line In 1",
+ "Line In 2"
+ };
+
+ memset(audio, 0, sizeof(*audio));
+ if (index >= itv->nof_audio_inputs)
+ return -EINVAL;
+ strlcpy(audio->name, input_strs[aud_input->audio_type - 1],
+ sizeof(audio->name));
+ audio->index = index;
+ audio->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+}
+
+int ivtv_get_audio_output(struct ivtv *itv, u16 index, struct v4l2_audioout *aud_output)
+{
+ memset(aud_output, 0, sizeof(*aud_output));
+ if (itv->card->video_outputs == NULL || index != 0)
+ return -EINVAL;
+ strlcpy(aud_output->name, "A/V Audio Out", sizeof(aud_output->name));
+ return 0;
+}
diff --git a/drivers/media/video/ivtv/ivtv-cards.h b/drivers/media/video/ivtv/ivtv-cards.h
new file mode 100644
index 00000000..e6f5c029
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-cards.h
@@ -0,0 +1,309 @@
+/*
+ Functions to query card hardware
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef IVTV_CARDS_H
+#define IVTV_CARDS_H
+
+/* Supported cards */
+#define IVTV_CARD_PVR_250 0 /* WinTV PVR 250 */
+#define IVTV_CARD_PVR_350 1 /* encoder, decoder, tv-out */
+#define IVTV_CARD_PVR_150 2 /* WinTV PVR 150 and PVR 500 (really just two
+ PVR150s on one PCI board) */
+#define IVTV_CARD_M179 3 /* AVerMedia M179 (encoder only) */
+#define IVTV_CARD_MPG600 4 /* Kuroutoshikou ITVC16-STVLP/YUAN MPG600, encoder only */
+#define IVTV_CARD_MPG160 5 /* Kuroutoshikou ITVC15-STVLP/YUAN MPG160
+ cx23415 based, but does not have tv-out */
+#define IVTV_CARD_PG600 6 /* YUAN PG600/DIAMONDMM PVR-550 based on the CX Falcon 2 */
+#define IVTV_CARD_AVC2410 7 /* Adaptec AVC-2410 */
+#define IVTV_CARD_AVC2010 8 /* Adaptec AVD-2010 (No Tuner) */
+#define IVTV_CARD_TG5000TV 9 /* NAGASE TRANSGEAR 5000TV, encoder only */
+#define IVTV_CARD_VA2000MAX_SNT6 10 /* VA2000MAX-STN6 */
+#define IVTV_CARD_CX23416GYC 11 /* Kuroutoshikou CX23416GYC-STVLP (Yuan MPG600GR OEM) */
+#define IVTV_CARD_GV_MVPRX 12 /* I/O Data GV-MVP/RX, RX2, RX2W */
+#define IVTV_CARD_GV_MVPRX2E 13 /* I/O Data GV-MVP/RX2E */
+#define IVTV_CARD_GOTVIEW_PCI_DVD 14 /* GotView PCI DVD */
+#define IVTV_CARD_GOTVIEW_PCI_DVD2 15 /* GotView PCI DVD2 */
+#define IVTV_CARD_YUAN_MPC622 16 /* Yuan MPC622 miniPCI */
+#define IVTV_CARD_DCTMTVP1 17 /* DIGITAL COWBOY DCT-MTVP1 */
+#define IVTV_CARD_PG600V2 18 /* Yuan PG600V2/GotView PCI DVD Lite */
+#define IVTV_CARD_CLUB3D 19 /* Club3D ZAP-TV1x01 */
+#define IVTV_CARD_AVERTV_MCE116 20 /* AVerTV MCE 116 Plus */
+#define IVTV_CARD_ASUS_FALCON2 21 /* ASUS Falcon2 */
+#define IVTV_CARD_AVER_PVR150PLUS 22 /* AVerMedia PVR-150 Plus */
+#define IVTV_CARD_AVER_EZMAKER 23 /* AVerMedia EZMaker PCI Deluxe */
+#define IVTV_CARD_AVER_M104 24 /* AverMedia M104 miniPCI card */
+#define IVTV_CARD_BUFFALO_MV5L 25 /* Buffalo PC-MV5L/PCI card */
+#define IVTV_CARD_AVER_ULTRA1500MCE 26 /* AVerMedia UltraTV 1500 MCE */
+#define IVTV_CARD_KIKYOU 27 /* Sony VAIO Giga Pocket (ENX Kikyou) */
+#define IVTV_CARD_LAST 27
+
+/* Variants of existing cards but with the same PCI IDs. The driver
+ detects these based on other device information.
+ These cards must always come last.
+ New cards must be inserted above, and the indices of the cards below
+ must be adjusted accordingly. */
+
+/* PVR-350 V1 (uses saa7114) */
+#define IVTV_CARD_PVR_350_V1 (IVTV_CARD_LAST+1)
+/* 2 variants of Kuroutoshikou CX23416GYC-STVLP (Yuan MPG600GR OEM) */
+#define IVTV_CARD_CX23416GYC_NOGR (IVTV_CARD_LAST+2)
+#define IVTV_CARD_CX23416GYC_NOGRYCS (IVTV_CARD_LAST+3)
+
+/* system vendor and device IDs */
+#define PCI_VENDOR_ID_ICOMP 0x4444
+#define PCI_DEVICE_ID_IVTV15 0x0803
+#define PCI_DEVICE_ID_IVTV16 0x0016
+
+/* subsystem vendor ID */
+#define IVTV_PCI_ID_HAUPPAUGE 0x0070
+#define IVTV_PCI_ID_HAUPPAUGE_ALT1 0x0270
+#define IVTV_PCI_ID_HAUPPAUGE_ALT2 0x4070
+#define IVTV_PCI_ID_ADAPTEC 0x9005
+#define IVTV_PCI_ID_ASUSTEK 0x1043
+#define IVTV_PCI_ID_AVERMEDIA 0x1461
+#define IVTV_PCI_ID_YUAN1 0x12ab
+#define IVTV_PCI_ID_YUAN2 0xff01
+#define IVTV_PCI_ID_YUAN3 0xffab
+#define IVTV_PCI_ID_YUAN4 0xfbab
+#define IVTV_PCI_ID_DIAMONDMM 0xff92
+#define IVTV_PCI_ID_IODATA 0x10fc
+#define IVTV_PCI_ID_MELCO 0x1154
+#define IVTV_PCI_ID_GOTVIEW1 0xffac
+#define IVTV_PCI_ID_GOTVIEW2 0xffad
+#define IVTV_PCI_ID_SONY 0x104d
+
+/* hardware flags, no gaps allowed */
+#define IVTV_HW_CX25840 (1 << 0)
+#define IVTV_HW_SAA7115 (1 << 1)
+#define IVTV_HW_SAA7127 (1 << 2)
+#define IVTV_HW_MSP34XX (1 << 3)
+#define IVTV_HW_TUNER (1 << 4)
+#define IVTV_HW_WM8775 (1 << 5)
+#define IVTV_HW_CS53L32A (1 << 6)
+#define IVTV_HW_TVEEPROM (1 << 7)
+#define IVTV_HW_SAA7114 (1 << 8)
+#define IVTV_HW_UPD64031A (1 << 9)
+#define IVTV_HW_UPD6408X (1 << 10)
+#define IVTV_HW_SAA717X (1 << 11)
+#define IVTV_HW_WM8739 (1 << 12)
+#define IVTV_HW_VP27SMPX (1 << 13)
+#define IVTV_HW_M52790 (1 << 14)
+#define IVTV_HW_GPIO (1 << 15)
+#define IVTV_HW_I2C_IR_RX_AVER (1 << 16)
+#define IVTV_HW_I2C_IR_RX_HAUP_EXT (1 << 17) /* External before internal */
+#define IVTV_HW_I2C_IR_RX_HAUP_INT (1 << 18)
+#define IVTV_HW_Z8F0811_IR_TX_HAUP (1 << 19)
+#define IVTV_HW_Z8F0811_IR_RX_HAUP (1 << 20)
+#define IVTV_HW_I2C_IR_RX_ADAPTEC (1 << 21)
+
+#define IVTV_HW_Z8F0811_IR_HAUP (IVTV_HW_Z8F0811_IR_RX_HAUP | \
+ IVTV_HW_Z8F0811_IR_TX_HAUP)
+
+#define IVTV_HW_SAA711X (IVTV_HW_SAA7115 | IVTV_HW_SAA7114)
+
+#define IVTV_HW_IR_RX_ANY (IVTV_HW_I2C_IR_RX_AVER | \
+ IVTV_HW_I2C_IR_RX_HAUP_EXT | \
+ IVTV_HW_I2C_IR_RX_HAUP_INT | \
+ IVTV_HW_Z8F0811_IR_RX_HAUP | \
+ IVTV_HW_I2C_IR_RX_ADAPTEC)
+
+#define IVTV_HW_IR_TX_ANY (IVTV_HW_Z8F0811_IR_TX_HAUP)
+
+#define IVTV_HW_IR_ANY (IVTV_HW_IR_RX_ANY | IVTV_HW_IR_TX_ANY)
+
+/* video inputs */
+#define IVTV_CARD_INPUT_VID_TUNER 1
+#define IVTV_CARD_INPUT_SVIDEO1 2
+#define IVTV_CARD_INPUT_SVIDEO2 3
+#define IVTV_CARD_INPUT_COMPOSITE1 4
+#define IVTV_CARD_INPUT_COMPOSITE2 5
+#define IVTV_CARD_INPUT_COMPOSITE3 6
+
+/* audio inputs */
+#define IVTV_CARD_INPUT_AUD_TUNER 1
+#define IVTV_CARD_INPUT_LINE_IN1 2
+#define IVTV_CARD_INPUT_LINE_IN2 3
+
+#define IVTV_CARD_MAX_VIDEO_INPUTS 6
+#define IVTV_CARD_MAX_AUDIO_INPUTS 3
+#define IVTV_CARD_MAX_TUNERS 3
+
+/* SAA71XX HW inputs */
+#define IVTV_SAA71XX_COMPOSITE0 0
+#define IVTV_SAA71XX_COMPOSITE1 1
+#define IVTV_SAA71XX_COMPOSITE2 2
+#define IVTV_SAA71XX_COMPOSITE3 3
+#define IVTV_SAA71XX_COMPOSITE4 4
+#define IVTV_SAA71XX_COMPOSITE5 5
+#define IVTV_SAA71XX_SVIDEO0 6
+#define IVTV_SAA71XX_SVIDEO1 7
+#define IVTV_SAA71XX_SVIDEO2 8
+#define IVTV_SAA71XX_SVIDEO3 9
+
+/* SAA717X needs to mark the tuner input by ORing with this flag */
+#define IVTV_SAA717X_TUNER_FLAG 0x80
+
+/* Dummy HW input */
+#define IVTV_DUMMY_AUDIO 0
+
+/* GPIO HW inputs */
+#define IVTV_GPIO_TUNER 0
+#define IVTV_GPIO_LINE_IN 1
+
+/* SAA717X HW inputs */
+#define IVTV_SAA717X_IN0 0
+#define IVTV_SAA717X_IN1 1
+#define IVTV_SAA717X_IN2 2
+
+/* V4L2 capability aliases */
+#define IVTV_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \
+ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE | \
+ V4L2_CAP_SLICED_VBI_CAPTURE)
+#define IVTV_CAP_DECODER (V4L2_CAP_VIDEO_OUTPUT | \
+ V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_OVERLAY)
+
+struct ivtv_card_video_input {
+ u8 video_type; /* video input type */
+ u8 audio_index; /* index in ivtv_card_audio_input array */
+ u16 video_input; /* hardware video input */
+};
+
+struct ivtv_card_audio_input {
+ u8 audio_type; /* audio input type */
+ u32 audio_input; /* hardware audio input */
+ u16 muxer_input; /* hardware muxer input for boards with a
+ multiplexer chip */
+};
+
+struct ivtv_card_output {
+ u8 name[32];
+ u16 video_output; /* hardware video output */
+};
+
+struct ivtv_card_pci_info {
+ u16 device;
+ u16 subsystem_vendor;
+ u16 subsystem_device;
+};
+
+/* GPIO definitions */
+
+/* The mask is the set of bits used by the operation */
+
+struct ivtv_gpio_init { /* set initial GPIO DIR and OUT values */
+ u16 direction; /* DIR setting. Leave to 0 if no init is needed */
+ u16 initial_value;
+};
+
+struct ivtv_gpio_video_input { /* select tuner/line in input */
+ u16 mask; /* leave to 0 if not supported */
+ u16 tuner;
+ u16 composite;
+ u16 svideo;
+};
+
+struct ivtv_gpio_audio_input { /* select tuner/line in input */
+ u16 mask; /* leave to 0 if not supported */
+ u16 tuner;
+ u16 linein;
+ u16 radio;
+};
+
+struct ivtv_gpio_audio_mute {
+ u16 mask; /* leave to 0 if not supported */
+ u16 mute; /* set this value to mute, 0 to unmute */
+};
+
+struct ivtv_gpio_audio_mode {
+ u16 mask; /* leave to 0 if not supported */
+ u16 mono; /* set audio to mono */
+ u16 stereo; /* set audio to stereo */
+ u16 lang1; /* set audio to the first language */
+ u16 lang2; /* set audio to the second language */
+ u16 both; /* both languages are output */
+};
+
+struct ivtv_gpio_audio_freq {
+ u16 mask; /* leave to 0 if not supported */
+ u16 f32000;
+ u16 f44100;
+ u16 f48000;
+};
+
+struct ivtv_gpio_audio_detect {
+ u16 mask; /* leave to 0 if not supported */
+ u16 stereo; /* if the input matches this value then
+ stereo is detected */
+};
+
+struct ivtv_card_tuner {
+ v4l2_std_id std; /* standard for which the tuner is suitable */
+ int tuner; /* tuner ID (from tuner.h) */
+};
+
+struct ivtv_card_tuner_i2c {
+ unsigned short radio[2];/* radio tuner i2c address to probe */
+ unsigned short demod[2];/* demodulator i2c address to probe */
+ unsigned short tv[4]; /* tv tuner i2c addresses to probe */
+};
+
+/* for card information/parameters */
+struct ivtv_card {
+ int type;
+ char *name;
+ char *comment;
+ u32 v4l2_capabilities;
+ u32 hw_video; /* hardware used to process video */
+ u32 hw_audio; /* hardware used to process audio */
+ u32 hw_audio_ctrl; /* hardware used for the V4L2 controls (only 1 dev allowed) */
+ u32 hw_muxer; /* hardware used to multiplex audio input */
+ u32 hw_all; /* all hardware used by the board */
+ struct ivtv_card_video_input video_inputs[IVTV_CARD_MAX_VIDEO_INPUTS];
+ struct ivtv_card_audio_input audio_inputs[IVTV_CARD_MAX_AUDIO_INPUTS];
+ struct ivtv_card_audio_input radio_input;
+ int nof_outputs;
+ const struct ivtv_card_output *video_outputs;
+ u8 gr_config; /* config byte for the ghost reduction device */
+ u8 xceive_pin; /* XCeive tuner GPIO reset pin */
+
+ /* GPIO card-specific settings */
+ struct ivtv_gpio_init gpio_init;
+ struct ivtv_gpio_video_input gpio_video_input;
+ struct ivtv_gpio_audio_input gpio_audio_input;
+ struct ivtv_gpio_audio_mute gpio_audio_mute;
+ struct ivtv_gpio_audio_mode gpio_audio_mode;
+ struct ivtv_gpio_audio_freq gpio_audio_freq;
+ struct ivtv_gpio_audio_detect gpio_audio_detect;
+
+ struct ivtv_card_tuner tuners[IVTV_CARD_MAX_TUNERS];
+ struct ivtv_card_tuner_i2c *i2c;
+
+ /* list of device and subsystem vendor/devices that
+ correspond to this card type. */
+ const struct ivtv_card_pci_info *pci_list;
+};
+
+int ivtv_get_input(struct ivtv *itv, u16 index, struct v4l2_input *input);
+int ivtv_get_output(struct ivtv *itv, u16 index, struct v4l2_output *output);
+int ivtv_get_audio_input(struct ivtv *itv, u16 index, struct v4l2_audio *input);
+int ivtv_get_audio_output(struct ivtv *itv, u16 index, struct v4l2_audioout *output);
+const struct ivtv_card *ivtv_get_card(u16 index);
+
+#endif
diff --git a/drivers/media/video/ivtv/ivtv-controls.c b/drivers/media/video/ivtv/ivtv-controls.c
new file mode 100644
index 00000000..c6042460
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-controls.c
@@ -0,0 +1,163 @@
+/*
+ ioctl control functions
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-controls.h"
+#include "ivtv-mailbox.h"
+
+static int ivtv_s_stream_vbi_fmt(struct cx2341x_handler *cxhdl, u32 fmt)
+{
+ struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);
+
+ /* First try to allocate sliced VBI buffers if needed. */
+ if (fmt && itv->vbi.sliced_mpeg_data[0] == NULL) {
+ int i;
+
+ for (i = 0; i < IVTV_VBI_FRAMES; i++) {
+ /* Yuck, hardcoded. Needs to be a define */
+ itv->vbi.sliced_mpeg_data[i] = kmalloc(2049, GFP_KERNEL);
+ if (itv->vbi.sliced_mpeg_data[i] == NULL) {
+ while (--i >= 0) {
+ kfree(itv->vbi.sliced_mpeg_data[i]);
+ itv->vbi.sliced_mpeg_data[i] = NULL;
+ }
+ return -ENOMEM;
+ }
+ }
+ }
+
+ itv->vbi.insert_mpeg = fmt;
+
+ if (itv->vbi.insert_mpeg == 0) {
+ return 0;
+ }
+ /* Need sliced data for mpeg insertion */
+ if (ivtv_get_service_set(itv->vbi.sliced_in) == 0) {
+ if (itv->is_60hz)
+ itv->vbi.sliced_in->service_set = V4L2_SLICED_CAPTION_525;
+ else
+ itv->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625;
+ ivtv_expand_service_set(itv->vbi.sliced_in, itv->is_50hz);
+ }
+ return 0;
+}
+
+static int ivtv_s_video_encoding(struct cx2341x_handler *cxhdl, u32 val)
+{
+ struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);
+ int is_mpeg1 = val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
+ struct v4l2_mbus_framefmt fmt;
+
+ /* fix videodecoder resolution */
+ fmt.width = cxhdl->width / (is_mpeg1 ? 2 : 1);
+ fmt.height = cxhdl->height;
+ fmt.code = V4L2_MBUS_FMT_FIXED;
+ v4l2_subdev_call(itv->sd_video, video, s_mbus_fmt, &fmt);
+ return 0;
+}
+
+static int ivtv_s_audio_sampling_freq(struct cx2341x_handler *cxhdl, u32 idx)
+{
+ static const u32 freqs[3] = { 44100, 48000, 32000 };
+ struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);
+
+ /* The audio clock of the digitizer must match the codec sample
+ rate otherwise you get some very strange effects. */
+ if (idx < ARRAY_SIZE(freqs))
+ ivtv_call_all(itv, audio, s_clock_freq, freqs[idx]);
+ return 0;
+}
+
+static int ivtv_s_audio_mode(struct cx2341x_handler *cxhdl, u32 val)
+{
+ struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);
+
+ itv->dualwatch_stereo_mode = val;
+ return 0;
+}
+
+struct cx2341x_handler_ops ivtv_cxhdl_ops = {
+ .s_audio_mode = ivtv_s_audio_mode,
+ .s_audio_sampling_freq = ivtv_s_audio_sampling_freq,
+ .s_video_encoding = ivtv_s_video_encoding,
+ .s_stream_vbi_fmt = ivtv_s_stream_vbi_fmt,
+};
+
+int ivtv_g_pts_frame(struct ivtv *itv, s64 *pts, s64 *frame)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+
+ if (test_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags)) {
+ *pts = (s64)((u64)itv->last_dec_timing[2] << 32) |
+ (u64)itv->last_dec_timing[1];
+ *frame = itv->last_dec_timing[0];
+ return 0;
+ }
+ *pts = 0;
+ *frame = 0;
+ if (atomic_read(&itv->decoding)) {
+ if (ivtv_api(itv, CX2341X_DEC_GET_TIMING_INFO, 5, data)) {
+ IVTV_DEBUG_WARN("GET_TIMING: couldn't read clock\n");
+ return -EIO;
+ }
+ memcpy(itv->last_dec_timing, data, sizeof(itv->last_dec_timing));
+ set_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags);
+ *pts = (s64)((u64) data[2] << 32) | (u64) data[1];
+ *frame = data[0];
+ /*timing->scr = (u64) (((u64) data[4] << 32) | (u64) (data[3]));*/
+ }
+ return 0;
+}
+
+static int ivtv_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ivtv *itv = container_of(ctrl->handler, struct ivtv, cxhdl.hdl);
+
+ switch (ctrl->id) {
+ /* V4L2_CID_MPEG_VIDEO_DEC_PTS and V4L2_CID_MPEG_VIDEO_DEC_FRAME
+ control cluster */
+ case V4L2_CID_MPEG_VIDEO_DEC_PTS:
+ return ivtv_g_pts_frame(itv, &itv->ctrl_pts->val64,
+ &itv->ctrl_frame->val64);
+ }
+ return 0;
+}
+
+static int ivtv_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ivtv *itv = container_of(ctrl->handler, struct ivtv, cxhdl.hdl);
+
+ switch (ctrl->id) {
+ /* V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK and MULTILINGUAL_PLAYBACK
+ control cluster */
+ case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK:
+ itv->audio_stereo_mode = itv->ctrl_audio_playback->val - 1;
+ itv->audio_bilingual_mode = itv->ctrl_audio_multilingual_playback->val - 1;
+ ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode);
+ break;
+ }
+ return 0;
+}
+
+const struct v4l2_ctrl_ops ivtv_hdl_out_ops = {
+ .s_ctrl = ivtv_s_ctrl,
+ .g_volatile_ctrl = ivtv_g_volatile_ctrl,
+};
diff --git a/drivers/media/video/ivtv/ivtv-controls.h b/drivers/media/video/ivtv/ivtv-controls.h
new file mode 100644
index 00000000..3999e635
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-controls.h
@@ -0,0 +1,28 @@
+/*
+ ioctl control functions
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef IVTV_CONTROLS_H
+#define IVTV_CONTROLS_H
+
+extern struct cx2341x_handler_ops ivtv_cxhdl_ops;
+extern const struct v4l2_ctrl_ops ivtv_hdl_out_ops;
+int ivtv_g_pts_frame(struct ivtv *itv, s64 *pts, s64 *frame);
+
+#endif
diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c
new file mode 100644
index 00000000..679262ed
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-driver.c
@@ -0,0 +1,1498 @@
+/*
+ ivtv driver initialization and card probing
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* Main Driver file for the ivtv project:
+ * Driver for the Conexant CX23415/CX23416 chip.
+ * Author: Kevin Thayer (nufan_wfk at yahoo.com)
+ * License: GPL
+ * http://www.ivtvdriver.org
+ *
+ * -----
+ * MPG600/MPG160 support by T.Adachi <tadachi@tadachi-net.com>
+ * and Takeru KOMORIYA<komoriya@paken.org>
+ *
+ * AVerMedia M179 GPIO info by Chris Pinkham <cpinkham@bc2va.org>
+ * using information provided by Jiun-Kuei Jung @ AVerMedia.
+ *
+ * Kurouto Sikou CX23416GYC-STVLP tested by K.Ohta <alpha292@bremen.or.jp>
+ * using information from T.Adachi,Takeru KOMORIYA and others :-)
+ *
+ * Nagase TRANSGEAR 5000TV, Aopen VA2000MAX-STN6 and I/O data GV-MVP/RX
+ * version by T.Adachi. Special thanks Mr.Suzuki
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-version.h"
+#include "ivtv-fileops.h"
+#include "ivtv-i2c.h"
+#include "ivtv-firmware.h"
+#include "ivtv-queue.h"
+#include "ivtv-udma.h"
+#include "ivtv-irq.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-streams.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-cards.h"
+#include "ivtv-vbi.h"
+#include "ivtv-routing.h"
+#include "ivtv-controls.h"
+#include "ivtv-gpio.h"
+#include <linux/dma-mapping.h>
+#include <media/tveeprom.h>
+#include <media/saa7115.h>
+#include <media/v4l2-chip-ident.h>
+#include "tuner-xc2028.h"
+
+/* If you have already X v4l cards, then set this to X. This way
+ the device numbers stay matched. Example: you have a WinTV card
+ without radio and a PVR-350 with. Normally this would give a
+ video1 device together with a radio0 device for the PVR. By
+ setting this to 1 you ensure that radio0 is now also radio1. */
+int ivtv_first_minor;
+
+/* add your revision and whatnot here */
+static struct pci_device_id ivtv_pci_tbl[] __devinitdata = {
+ {PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV15,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV16,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci,ivtv_pci_tbl);
+
+/* ivtv instance counter */
+static atomic_t ivtv_instance = ATOMIC_INIT(0);
+
+/* Parameter declarations */
+static int cardtype[IVTV_MAX_CARDS];
+static int tuner[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 };
+static int radio[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 };
+static int i2c_clock_period[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 };
+
+static unsigned int cardtype_c = 1;
+static unsigned int tuner_c = 1;
+static int radio_c = 1;
+static unsigned int i2c_clock_period_c = 1;
+static char pal[] = "---";
+static char secam[] = "--";
+static char ntsc[] = "-";
+
+/* Buffers */
+
+/* DMA Buffers, Default size in MB allocated */
+#define IVTV_DEFAULT_ENC_MPG_BUFFERS 4
+#define IVTV_DEFAULT_ENC_YUV_BUFFERS 2
+#define IVTV_DEFAULT_ENC_VBI_BUFFERS 1
+/* Exception: size in kB for this stream (MB is overkill) */
+#define IVTV_DEFAULT_ENC_PCM_BUFFERS 320
+#define IVTV_DEFAULT_DEC_MPG_BUFFERS 1
+#define IVTV_DEFAULT_DEC_YUV_BUFFERS 1
+/* Exception: size in kB for this stream (MB is way overkill) */
+#define IVTV_DEFAULT_DEC_VBI_BUFFERS 64
+
+static int enc_mpg_buffers = IVTV_DEFAULT_ENC_MPG_BUFFERS;
+static int enc_yuv_buffers = IVTV_DEFAULT_ENC_YUV_BUFFERS;
+static int enc_vbi_buffers = IVTV_DEFAULT_ENC_VBI_BUFFERS;
+static int enc_pcm_buffers = IVTV_DEFAULT_ENC_PCM_BUFFERS;
+static int dec_mpg_buffers = IVTV_DEFAULT_DEC_MPG_BUFFERS;
+static int dec_yuv_buffers = IVTV_DEFAULT_DEC_YUV_BUFFERS;
+static int dec_vbi_buffers = IVTV_DEFAULT_DEC_VBI_BUFFERS;
+
+static int ivtv_yuv_mode;
+static int ivtv_yuv_threshold = -1;
+static int ivtv_pci_latency = 1;
+
+int ivtv_debug;
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+int ivtv_fw_debug;
+#endif
+
+static int tunertype = -1;
+static int newi2c = -1;
+
+module_param_array(tuner, int, &tuner_c, 0644);
+module_param_array(radio, int, &radio_c, 0644);
+module_param_array(cardtype, int, &cardtype_c, 0644);
+module_param_string(pal, pal, sizeof(pal), 0644);
+module_param_string(secam, secam, sizeof(secam), 0644);
+module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
+module_param_named(debug,ivtv_debug, int, 0644);
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+module_param_named(fw_debug, ivtv_fw_debug, int, 0644);
+#endif
+module_param(ivtv_pci_latency, int, 0644);
+module_param(ivtv_yuv_mode, int, 0644);
+module_param(ivtv_yuv_threshold, int, 0644);
+module_param(ivtv_first_minor, int, 0644);
+
+module_param(enc_mpg_buffers, int, 0644);
+module_param(enc_yuv_buffers, int, 0644);
+module_param(enc_vbi_buffers, int, 0644);
+module_param(enc_pcm_buffers, int, 0644);
+module_param(dec_mpg_buffers, int, 0644);
+module_param(dec_yuv_buffers, int, 0644);
+module_param(dec_vbi_buffers, int, 0644);
+
+module_param(tunertype, int, 0644);
+module_param(newi2c, int, 0644);
+module_param_array(i2c_clock_period, int, &i2c_clock_period_c, 0644);
+
+MODULE_PARM_DESC(tuner, "Tuner type selection,\n"
+ "\t\t\tsee tuner.h for values");
+MODULE_PARM_DESC(radio,
+ "Enable or disable the radio. Use only if autodetection\n"
+ "\t\t\tfails. 0 = disable, 1 = enable");
+MODULE_PARM_DESC(cardtype,
+ "Only use this option if your card is not detected properly.\n"
+ "\t\tSpecify card type:\n"
+ "\t\t\t 1 = WinTV PVR 250\n"
+ "\t\t\t 2 = WinTV PVR 350\n"
+ "\t\t\t 3 = WinTV PVR-150 or PVR-500\n"
+ "\t\t\t 4 = AVerMedia M179\n"
+ "\t\t\t 5 = YUAN MPG600/Kuroutoshikou iTVC16-STVLP\n"
+ "\t\t\t 6 = YUAN MPG160/Kuroutoshikou iTVC15-STVLP\n"
+ "\t\t\t 7 = YUAN PG600/DIAMONDMM PVR-550 (CX Falcon 2)\n"
+ "\t\t\t 8 = Adaptec AVC-2410\n"
+ "\t\t\t 9 = Adaptec AVC-2010\n"
+ "\t\t\t10 = NAGASE TRANSGEAR 5000TV\n"
+ "\t\t\t11 = AOpen VA2000MAX-STN6\n"
+ "\t\t\t12 = YUAN MPG600GR/Kuroutoshikou CX23416GYC-STVLP\n"
+ "\t\t\t13 = I/O Data GV-MVP/RX\n"
+ "\t\t\t14 = I/O Data GV-MVP/RX2E\n"
+ "\t\t\t15 = GOTVIEW PCI DVD\n"
+ "\t\t\t16 = GOTVIEW PCI DVD2 Deluxe\n"
+ "\t\t\t17 = Yuan MPC622\n"
+ "\t\t\t18 = Digital Cowboy DCT-MTVP1\n"
+ "\t\t\t19 = Yuan PG600V2/GotView PCI DVD Lite\n"
+ "\t\t\t20 = Club3D ZAP-TV1x01\n"
+ "\t\t\t21 = AverTV MCE 116 Plus\n"
+ "\t\t\t22 = ASUS Falcon2\n"
+ "\t\t\t23 = AverMedia PVR-150 Plus\n"
+ "\t\t\t24 = AverMedia EZMaker PCI Deluxe\n"
+ "\t\t\t25 = AverMedia M104 (not yet working)\n"
+ "\t\t\t26 = Buffalo PC-MV5L/PCI\n"
+ "\t\t\t27 = AVerMedia UltraTV 1500 MCE\n"
+ "\t\t\t28 = Sony VAIO Giga Pocket (ENX Kikyou)\n"
+ "\t\t\t 0 = Autodetect (default)\n"
+ "\t\t\t-1 = Ignore this card\n\t\t");
+MODULE_PARM_DESC(pal, "Set PAL standard: BGH, DK, I, M, N, Nc, 60");
+MODULE_PARM_DESC(secam, "Set SECAM standard: BGH, DK, L, LC");
+MODULE_PARM_DESC(ntsc, "Set NTSC standard: M, J (Japan), K (South Korea)");
+MODULE_PARM_DESC(tunertype,
+ "Specify tuner type:\n"
+ "\t\t\t 0 = tuner for PAL-B/G/H/D/K/I, SECAM-B/G/H/D/K/L/Lc\n"
+ "\t\t\t 1 = tuner for NTSC-M/J/K, PAL-M/N/Nc\n"
+ "\t\t\t-1 = Autodetect (default)\n");
+MODULE_PARM_DESC(debug,
+ "Debug level (bitmask). Default: 0\n"
+ "\t\t\t 1/0x0001: warning\n"
+ "\t\t\t 2/0x0002: info\n"
+ "\t\t\t 4/0x0004: mailbox\n"
+ "\t\t\t 8/0x0008: ioctl\n"
+ "\t\t\t 16/0x0010: file\n"
+ "\t\t\t 32/0x0020: dma\n"
+ "\t\t\t 64/0x0040: irq\n"
+ "\t\t\t 128/0x0080: decoder\n"
+ "\t\t\t 256/0x0100: yuv\n"
+ "\t\t\t 512/0x0200: i2c\n"
+ "\t\t\t1024/0x0400: high volume\n");
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+MODULE_PARM_DESC(fw_debug,
+ "Enable code for debugging firmware problems. Default: 0\n");
+#endif
+MODULE_PARM_DESC(ivtv_pci_latency,
+ "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n"
+ "\t\t\tDefault: Yes");
+MODULE_PARM_DESC(ivtv_yuv_mode,
+ "Specify the yuv playback mode:\n"
+ "\t\t\t0 = interlaced\n\t\t\t1 = progressive\n\t\t\t2 = auto\n"
+ "\t\t\tDefault: 0 (interlaced)");
+MODULE_PARM_DESC(ivtv_yuv_threshold,
+ "If ivtv_yuv_mode is 2 (auto) then playback content as\n\t\tprogressive if src height <= ivtv_yuvthreshold\n"
+ "\t\t\tDefault: 480");
+MODULE_PARM_DESC(enc_mpg_buffers,
+ "Encoder MPG Buffers (in MB)\n"
+ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_MPG_BUFFERS));
+MODULE_PARM_DESC(enc_yuv_buffers,
+ "Encoder YUV Buffers (in MB)\n"
+ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_YUV_BUFFERS));
+MODULE_PARM_DESC(enc_vbi_buffers,
+ "Encoder VBI Buffers (in MB)\n"
+ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_VBI_BUFFERS));
+MODULE_PARM_DESC(enc_pcm_buffers,
+ "Encoder PCM buffers (in kB)\n"
+ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_PCM_BUFFERS));
+MODULE_PARM_DESC(dec_mpg_buffers,
+ "Decoder MPG buffers (in MB)\n"
+ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_MPG_BUFFERS));
+MODULE_PARM_DESC(dec_yuv_buffers,
+ "Decoder YUV buffers (in MB)\n"
+ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_YUV_BUFFERS));
+MODULE_PARM_DESC(dec_vbi_buffers,
+ "Decoder VBI buffers (in kB)\n"
+ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_VBI_BUFFERS));
+MODULE_PARM_DESC(newi2c,
+ "Use new I2C implementation\n"
+ "\t\t\t-1 is autodetect, 0 is off, 1 is on\n"
+ "\t\t\tDefault is autodetect");
+MODULE_PARM_DESC(i2c_clock_period,
+ "Period of SCL for the I2C bus controlled by the CX23415/6\n"
+ "\t\t\tMin: 10 usec (100 kHz), Max: 4500 usec (222 Hz)\n"
+ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_I2C_CLOCK_PERIOD));
+
+MODULE_PARM_DESC(ivtv_first_minor, "Set device node number assigned to first card");
+
+MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil");
+MODULE_DESCRIPTION("CX23415/CX23416 driver");
+MODULE_SUPPORTED_DEVICE
+ ("CX23415/CX23416 MPEG2 encoder (WinTV PVR-150/250/350/500,\n"
+ "\t\t\tYuan MPG series and similar)");
+MODULE_LICENSE("GPL");
+
+MODULE_VERSION(IVTV_VERSION);
+
+void ivtv_clear_irq_mask(struct ivtv *itv, u32 mask)
+{
+ itv->irqmask &= ~mask;
+ write_reg_sync(itv->irqmask, IVTV_REG_IRQMASK);
+}
+
+void ivtv_set_irq_mask(struct ivtv *itv, u32 mask)
+{
+ itv->irqmask |= mask;
+ write_reg_sync(itv->irqmask, IVTV_REG_IRQMASK);
+}
+
+int ivtv_set_output_mode(struct ivtv *itv, int mode)
+{
+ int old_mode;
+
+ spin_lock(&itv->lock);
+ old_mode = itv->output_mode;
+ if (old_mode == 0)
+ itv->output_mode = old_mode = mode;
+ spin_unlock(&itv->lock);
+ return old_mode;
+}
+
+struct ivtv_stream *ivtv_get_output_stream(struct ivtv *itv)
+{
+ switch (itv->output_mode) {
+ case OUT_MPG:
+ return &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
+ case OUT_YUV:
+ return &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
+ default:
+ return NULL;
+ }
+}
+
+int ivtv_waitq(wait_queue_head_t *waitq)
+{
+ DEFINE_WAIT(wait);
+
+ prepare_to_wait(waitq, &wait, TASK_INTERRUPTIBLE);
+ schedule();
+ finish_wait(waitq, &wait);
+ return signal_pending(current) ? -EINTR : 0;
+}
+
+/* Generic utility functions */
+int ivtv_msleep_timeout(unsigned int msecs, int intr)
+{
+ int timeout = msecs_to_jiffies(msecs);
+
+ do {
+ set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
+ timeout = schedule_timeout(timeout);
+ if (intr) {
+ int ret = signal_pending(current);
+
+ if (ret)
+ return ret;
+ }
+ } while (timeout);
+ return 0;
+}
+
+/* Release ioremapped memory */
+static void ivtv_iounmap(struct ivtv *itv)
+{
+ if (itv == NULL)
+ return;
+
+ /* Release registers memory */
+ if (itv->reg_mem != NULL) {
+ IVTV_DEBUG_INFO("releasing reg_mem\n");
+ iounmap(itv->reg_mem);
+ itv->reg_mem = NULL;
+ }
+ /* Release io memory */
+ if (itv->has_cx23415 && itv->dec_mem != NULL) {
+ IVTV_DEBUG_INFO("releasing dec_mem\n");
+ iounmap(itv->dec_mem);
+ }
+ itv->dec_mem = NULL;
+
+ /* Release io memory */
+ if (itv->enc_mem != NULL) {
+ IVTV_DEBUG_INFO("releasing enc_mem\n");
+ iounmap(itv->enc_mem);
+ itv->enc_mem = NULL;
+ }
+}
+
+/* Hauppauge card? get values from tveeprom */
+void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv)
+{
+ u8 eedata[256];
+
+ itv->i2c_client.addr = 0xA0 >> 1;
+ tveeprom_read(&itv->i2c_client, eedata, sizeof(eedata));
+ tveeprom_hauppauge_analog(&itv->i2c_client, tv, eedata);
+}
+
+static void ivtv_process_eeprom(struct ivtv *itv)
+{
+ struct tveeprom tv;
+ int pci_slot = PCI_SLOT(itv->pdev->devfn);
+
+ ivtv_read_eeprom(itv, &tv);
+
+ /* Many thanks to Steven Toth from Hauppauge for providing the
+ model numbers */
+ switch (tv.model) {
+ /* In a few cases the PCI subsystem IDs do not correctly
+ identify the card. A better method is to check the
+ model number from the eeprom instead. */
+ case 30012 ... 30039: /* Low profile PVR250 */
+ case 32000 ... 32999:
+ case 48000 ... 48099: /* 48??? range are PVR250s with a cx23415 */
+ case 48400 ... 48599:
+ itv->card = ivtv_get_card(IVTV_CARD_PVR_250);
+ break;
+ case 48100 ... 48399:
+ case 48600 ... 48999:
+ itv->card = ivtv_get_card(IVTV_CARD_PVR_350);
+ break;
+ case 23000 ... 23999: /* PVR500 */
+ case 25000 ... 25999: /* Low profile PVR150 */
+ case 26000 ... 26999: /* Regular PVR150 */
+ itv->card = ivtv_get_card(IVTV_CARD_PVR_150);
+ break;
+ case 0:
+ IVTV_ERR("Invalid EEPROM\n");
+ return;
+ default:
+ IVTV_ERR("Unknown model %d, defaulting to PVR-150\n", tv.model);
+ itv->card = ivtv_get_card(IVTV_CARD_PVR_150);
+ break;
+ }
+
+ switch (tv.model) {
+ /* Old style PVR350 (with an saa7114) uses this input for
+ the tuner. */
+ case 48254:
+ itv->card = ivtv_get_card(IVTV_CARD_PVR_350_V1);
+ break;
+ default:
+ break;
+ }
+
+ itv->v4l2_cap = itv->card->v4l2_capabilities;
+ itv->card_name = itv->card->name;
+ itv->card_i2c = itv->card->i2c;
+
+ /* If this is a PVR500 then it should be possible to detect whether it is the
+ first or second unit by looking at the subsystem device ID: is bit 4 is
+ set, then it is the second unit (according to info from Hauppauge).
+
+ However, while this works for most cards, I have seen a few PVR500 cards
+ where both units have the same subsystem ID.
+
+ So instead I look at the reported 'PCI slot' (which is the slot on the PVR500
+ PCI bridge) and if it is 8, then it is assumed to be the first unit, otherwise
+ it is the second unit. It is possible that it is a different slot when ivtv is
+ used in Xen, in that case I ignore this card here. The worst that can happen
+ is that the card presents itself with a non-working radio device.
+
+ This detection is needed since the eeprom reports incorrectly that a radio is
+ present on the second unit. */
+ if (tv.model / 1000 == 23) {
+ static const struct ivtv_card_tuner_i2c ivtv_i2c_radio = {
+ .radio = { 0x60, I2C_CLIENT_END },
+ .demod = { 0x43, I2C_CLIENT_END },
+ .tv = { 0x61, I2C_CLIENT_END },
+ };
+
+ itv->card_name = "WinTV PVR 500";
+ itv->card_i2c = &ivtv_i2c_radio;
+ if (pci_slot == 8 || pci_slot == 9) {
+ int is_first = (pci_slot & 1) == 0;
+
+ itv->card_name = is_first ? "WinTV PVR 500 (unit #1)" :
+ "WinTV PVR 500 (unit #2)";
+ if (!is_first) {
+ IVTV_INFO("Correcting tveeprom data: no radio present on second unit\n");
+ tv.has_radio = 0;
+ }
+ }
+ }
+ IVTV_INFO("Autodetected %s\n", itv->card_name);
+
+ switch (tv.tuner_hauppauge_model) {
+ case 85:
+ case 99:
+ case 112:
+ itv->pvr150_workaround = 1;
+ break;
+ default:
+ break;
+ }
+ if (tv.tuner_type == TUNER_ABSENT)
+ IVTV_ERR("tveeprom cannot autodetect tuner!\n");
+
+ if (itv->options.tuner == -1)
+ itv->options.tuner = tv.tuner_type;
+ if (itv->options.radio == -1)
+ itv->options.radio = (tv.has_radio != 0);
+ /* only enable newi2c if an IR blaster is present */
+ if (itv->options.newi2c == -1 && tv.has_ir) {
+ itv->options.newi2c = (tv.has_ir & 4) ? 1 : 0;
+ if (itv->options.newi2c) {
+ IVTV_INFO("Reopen i2c bus for IR-blaster support\n");
+ exit_ivtv_i2c(itv);
+ init_ivtv_i2c(itv);
+ }
+ }
+
+ if (itv->std != 0)
+ /* user specified tuner standard */
+ return;
+
+ /* autodetect tuner standard */
+ if (tv.tuner_formats & V4L2_STD_PAL) {
+ IVTV_DEBUG_INFO("PAL tuner detected\n");
+ itv->std |= V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
+ } else if (tv.tuner_formats & V4L2_STD_NTSC) {
+ IVTV_DEBUG_INFO("NTSC tuner detected\n");
+ itv->std |= V4L2_STD_NTSC_M;
+ } else if (tv.tuner_formats & V4L2_STD_SECAM) {
+ IVTV_DEBUG_INFO("SECAM tuner detected\n");
+ itv->std |= V4L2_STD_SECAM_L;
+ } else {
+ IVTV_INFO("No tuner detected, default to NTSC-M\n");
+ itv->std |= V4L2_STD_NTSC_M;
+ }
+}
+
+static v4l2_std_id ivtv_parse_std(struct ivtv *itv)
+{
+ switch (pal[0]) {
+ case '6':
+ tunertype = 0;
+ return V4L2_STD_PAL_60;
+ case 'b':
+ case 'B':
+ case 'g':
+ case 'G':
+ case 'h':
+ case 'H':
+ tunertype = 0;
+ return V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
+ case 'n':
+ case 'N':
+ tunertype = 1;
+ if (pal[1] == 'c' || pal[1] == 'C')
+ return V4L2_STD_PAL_Nc;
+ return V4L2_STD_PAL_N;
+ case 'i':
+ case 'I':
+ tunertype = 0;
+ return V4L2_STD_PAL_I;
+ case 'd':
+ case 'D':
+ case 'k':
+ case 'K':
+ tunertype = 0;
+ return V4L2_STD_PAL_DK;
+ case 'M':
+ case 'm':
+ tunertype = 1;
+ return V4L2_STD_PAL_M;
+ case '-':
+ break;
+ default:
+ IVTV_WARN("pal= argument not recognised\n");
+ return 0;
+ }
+
+ switch (secam[0]) {
+ case 'b':
+ case 'B':
+ case 'g':
+ case 'G':
+ case 'h':
+ case 'H':
+ tunertype = 0;
+ return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
+ case 'd':
+ case 'D':
+ case 'k':
+ case 'K':
+ tunertype = 0;
+ return V4L2_STD_SECAM_DK;
+ case 'l':
+ case 'L':
+ tunertype = 0;
+ if (secam[1] == 'C' || secam[1] == 'c')
+ return V4L2_STD_SECAM_LC;
+ return V4L2_STD_SECAM_L;
+ case '-':
+ break;
+ default:
+ IVTV_WARN("secam= argument not recognised\n");
+ return 0;
+ }
+
+ switch (ntsc[0]) {
+ case 'm':
+ case 'M':
+ tunertype = 1;
+ return V4L2_STD_NTSC_M;
+ case 'j':
+ case 'J':
+ tunertype = 1;
+ return V4L2_STD_NTSC_M_JP;
+ case 'k':
+ case 'K':
+ tunertype = 1;
+ return V4L2_STD_NTSC_M_KR;
+ case '-':
+ break;
+ default:
+ IVTV_WARN("ntsc= argument not recognised\n");
+ return 0;
+ }
+
+ /* no match found */
+ return 0;
+}
+
+static void ivtv_process_options(struct ivtv *itv)
+{
+ const char *chipname;
+ int i, j;
+
+ itv->options.kilobytes[IVTV_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers * 1024;
+ itv->options.kilobytes[IVTV_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers * 1024;
+ itv->options.kilobytes[IVTV_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers * 1024;
+ itv->options.kilobytes[IVTV_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers;
+ itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_MPG] = dec_mpg_buffers * 1024;
+ itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_YUV] = dec_yuv_buffers * 1024;
+ itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_VBI] = dec_vbi_buffers;
+ itv->options.cardtype = cardtype[itv->instance];
+ itv->options.tuner = tuner[itv->instance];
+ itv->options.radio = radio[itv->instance];
+
+ itv->options.i2c_clock_period = i2c_clock_period[itv->instance];
+ if (itv->options.i2c_clock_period == -1)
+ itv->options.i2c_clock_period = IVTV_DEFAULT_I2C_CLOCK_PERIOD;
+ else if (itv->options.i2c_clock_period < 10)
+ itv->options.i2c_clock_period = 10;
+ else if (itv->options.i2c_clock_period > 4500)
+ itv->options.i2c_clock_period = 4500;
+
+ itv->options.newi2c = newi2c;
+ if (tunertype < -1 || tunertype > 1) {
+ IVTV_WARN("Invalid tunertype argument, will autodetect instead\n");
+ tunertype = -1;
+ }
+ itv->std = ivtv_parse_std(itv);
+ if (itv->std == 0 && tunertype >= 0)
+ itv->std = tunertype ? V4L2_STD_MN : (V4L2_STD_ALL & ~V4L2_STD_MN);
+ itv->has_cx23415 = (itv->pdev->device == PCI_DEVICE_ID_IVTV15);
+ chipname = itv->has_cx23415 ? "cx23415" : "cx23416";
+ if (itv->options.cardtype == -1) {
+ IVTV_INFO("Ignore card (detected %s based chip)\n", chipname);
+ return;
+ }
+ if ((itv->card = ivtv_get_card(itv->options.cardtype - 1))) {
+ IVTV_INFO("User specified %s card (detected %s based chip)\n",
+ itv->card->name, chipname);
+ } else if (itv->options.cardtype != 0) {
+ IVTV_ERR("Unknown user specified type, trying to autodetect card\n");
+ }
+ if (itv->card == NULL) {
+ if (itv->pdev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE ||
+ itv->pdev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT1 ||
+ itv->pdev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT2) {
+ itv->card = ivtv_get_card(itv->has_cx23415 ? IVTV_CARD_PVR_350 : IVTV_CARD_PVR_150);
+ IVTV_INFO("Autodetected Hauppauge card (%s based)\n",
+ chipname);
+ }
+ }
+ if (itv->card == NULL) {
+ for (i = 0; (itv->card = ivtv_get_card(i)); i++) {
+ if (itv->card->pci_list == NULL)
+ continue;
+ for (j = 0; itv->card->pci_list[j].device; j++) {
+ if (itv->pdev->device !=
+ itv->card->pci_list[j].device)
+ continue;
+ if (itv->pdev->subsystem_vendor !=
+ itv->card->pci_list[j].subsystem_vendor)
+ continue;
+ if (itv->pdev->subsystem_device !=
+ itv->card->pci_list[j].subsystem_device)
+ continue;
+ IVTV_INFO("Autodetected %s card (%s based)\n",
+ itv->card->name, chipname);
+ goto done;
+ }
+ }
+ }
+done:
+
+ if (itv->card == NULL) {
+ itv->card = ivtv_get_card(IVTV_CARD_PVR_150);
+ IVTV_ERR("Unknown card: vendor/device: [%04x:%04x]\n",
+ itv->pdev->vendor, itv->pdev->device);
+ IVTV_ERR(" subsystem vendor/device: [%04x:%04x]\n",
+ itv->pdev->subsystem_vendor, itv->pdev->subsystem_device);
+ IVTV_ERR(" %s based\n", chipname);
+ IVTV_ERR("Defaulting to %s card\n", itv->card->name);
+ IVTV_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n");
+ IVTV_ERR("card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)\n");
+ IVTV_ERR("Prefix your subject line with [UNKNOWN IVTV CARD].\n");
+ }
+ itv->v4l2_cap = itv->card->v4l2_capabilities;
+ itv->card_name = itv->card->name;
+ itv->card_i2c = itv->card->i2c;
+}
+
+/* Precondition: the ivtv structure has been memset to 0. Only
+ the dev and num fields have been filled in.
+ No assumptions on the card type may be made here (see ivtv_init_struct2
+ for that).
+ */
+static int __devinit ivtv_init_struct1(struct ivtv *itv)
+{
+ struct sched_param param = { .sched_priority = 99 };
+
+ itv->base_addr = pci_resource_start(itv->pdev, 0);
+ itv->enc_mbox.max_mbox = 2; /* the encoder has 3 mailboxes (0-2) */
+ itv->dec_mbox.max_mbox = 1; /* the decoder has 2 mailboxes (0-1) */
+
+ mutex_init(&itv->serialize_lock);
+ mutex_init(&itv->i2c_bus_lock);
+ mutex_init(&itv->udma.lock);
+
+ spin_lock_init(&itv->lock);
+ spin_lock_init(&itv->dma_reg_lock);
+
+ init_kthread_worker(&itv->irq_worker);
+ itv->irq_worker_task = kthread_run(kthread_worker_fn, &itv->irq_worker,
+ itv->v4l2_dev.name);
+ if (IS_ERR(itv->irq_worker_task)) {
+ IVTV_ERR("Could not create ivtv task\n");
+ return -1;
+ }
+ /* must use the FIFO scheduler as it is realtime sensitive */
+ sched_setscheduler(itv->irq_worker_task, SCHED_FIFO, &param);
+
+ init_kthread_work(&itv->irq_work, ivtv_irq_work_handler);
+
+ /* Initial settings */
+ itv->cxhdl.port = CX2341X_PORT_MEMORY;
+ itv->cxhdl.capabilities = CX2341X_CAP_HAS_SLICED_VBI;
+ init_waitqueue_head(&itv->eos_waitq);
+ init_waitqueue_head(&itv->event_waitq);
+ init_waitqueue_head(&itv->vsync_waitq);
+ init_waitqueue_head(&itv->dma_waitq);
+ init_timer(&itv->dma_timer);
+ itv->dma_timer.function = ivtv_unfinished_dma;
+ itv->dma_timer.data = (unsigned long)itv;
+
+ itv->cur_dma_stream = -1;
+ itv->cur_pio_stream = -1;
+
+ /* Ctrls */
+ itv->speed = 1000;
+
+ /* VBI */
+ itv->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ itv->vbi.sliced_in = &itv->vbi.in.fmt.sliced;
+
+ /* Init the sg table for osd/yuv output */
+ sg_init_table(itv->udma.SGlist, IVTV_DMA_SG_OSD_ENT);
+
+ /* OSD */
+ itv->osd_global_alpha_state = 1;
+ itv->osd_global_alpha = 255;
+
+ /* YUV */
+ atomic_set(&itv->yuv_info.next_dma_frame, -1);
+ itv->yuv_info.lace_mode = ivtv_yuv_mode;
+ itv->yuv_info.lace_threshold = ivtv_yuv_threshold;
+ itv->yuv_info.max_frames_buffered = 3;
+ itv->yuv_info.track_osd = 1;
+ return 0;
+}
+
+/* Second initialization part. Here the card type has been
+ autodetected. */
+static void __devinit ivtv_init_struct2(struct ivtv *itv)
+{
+ int i;
+
+ for (i = 0; i < IVTV_CARD_MAX_VIDEO_INPUTS; i++)
+ if (itv->card->video_inputs[i].video_type == 0)
+ break;
+ itv->nof_inputs = i;
+ for (i = 0; i < IVTV_CARD_MAX_AUDIO_INPUTS; i++)
+ if (itv->card->audio_inputs[i].audio_type == 0)
+ break;
+ itv->nof_audio_inputs = i;
+
+ if (itv->card->hw_all & IVTV_HW_CX25840) {
+ itv->vbi.sliced_size = 288; /* multiple of 16, real size = 284 */
+ } else {
+ itv->vbi.sliced_size = 64; /* multiple of 16, real size = 52 */
+ }
+
+ /* Find tuner input */
+ for (i = 0; i < itv->nof_inputs; i++) {
+ if (itv->card->video_inputs[i].video_type ==
+ IVTV_CARD_INPUT_VID_TUNER)
+ break;
+ }
+ if (i == itv->nof_inputs)
+ i = 0;
+ itv->active_input = i;
+ itv->audio_input = itv->card->video_inputs[i].audio_index;
+}
+
+static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *pdev,
+ const struct pci_device_id *pci_id)
+{
+ u16 cmd;
+ unsigned char pci_latency;
+
+ IVTV_DEBUG_INFO("Enabling pci device\n");
+
+ if (pci_enable_device(pdev)) {
+ IVTV_ERR("Can't enable device!\n");
+ return -EIO;
+ }
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
+ IVTV_ERR("No suitable DMA available.\n");
+ return -EIO;
+ }
+ if (!request_mem_region(itv->base_addr, IVTV_ENCODER_SIZE, "ivtv encoder")) {
+ IVTV_ERR("Cannot request encoder memory region.\n");
+ return -EIO;
+ }
+
+ if (!request_mem_region(itv->base_addr + IVTV_REG_OFFSET,
+ IVTV_REG_SIZE, "ivtv registers")) {
+ IVTV_ERR("Cannot request register memory region.\n");
+ release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
+ return -EIO;
+ }
+
+ if (itv->has_cx23415 &&
+ !request_mem_region(itv->base_addr + IVTV_DECODER_OFFSET,
+ IVTV_DECODER_SIZE, "ivtv decoder")) {
+ IVTV_ERR("Cannot request decoder memory region.\n");
+ release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
+ release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+ return -EIO;
+ }
+
+ /* Check for bus mastering */
+ pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+ if (!(cmd & PCI_COMMAND_MASTER)) {
+ IVTV_DEBUG_INFO("Attempting to enable Bus Mastering\n");
+ pci_set_master(pdev);
+ pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+ if (!(cmd & PCI_COMMAND_MASTER)) {
+ IVTV_ERR("Bus Mastering is not enabled\n");
+ return -ENXIO;
+ }
+ }
+ IVTV_DEBUG_INFO("Bus Mastering Enabled.\n");
+
+ pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency);
+
+ if (pci_latency < 64 && ivtv_pci_latency) {
+ IVTV_INFO("Unreasonably low latency timer, "
+ "setting to 64 (was %d)\n", pci_latency);
+ pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64);
+ pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency);
+ }
+ /* This config space value relates to DMA latencies. The
+ default value 0x8080 is too low however and will lead
+ to DMA errors. 0xffff is the max value which solves
+ these problems. */
+ pci_write_config_dword(pdev, 0x40, 0xffff);
+
+ IVTV_DEBUG_INFO("%d (rev %d) at %02x:%02x.%x, "
+ "irq: %d, latency: %d, memory: 0x%lx\n",
+ pdev->device, pdev->revision, pdev->bus->number,
+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
+ pdev->irq, pci_latency, (unsigned long)itv->base_addr);
+
+ return 0;
+}
+
+static void ivtv_load_and_init_modules(struct ivtv *itv)
+{
+ u32 hw = itv->card->hw_all;
+ unsigned i;
+
+ /* check which i2c devices are actually found */
+ for (i = 0; i < 32; i++) {
+ u32 device = 1 << i;
+
+ if (!(device & hw))
+ continue;
+ if (device == IVTV_HW_GPIO || device == IVTV_HW_TVEEPROM) {
+ /* GPIO and TVEEPROM do not use i2c probing */
+ itv->hw_flags |= device;
+ continue;
+ }
+ if (ivtv_i2c_register(itv, i) == 0)
+ itv->hw_flags |= device;
+ }
+
+ /* probe for legacy IR controllers that aren't in card definitions */
+ if ((itv->hw_flags & IVTV_HW_IR_ANY) == 0)
+ ivtv_i2c_new_ir_legacy(itv);
+
+ if (itv->card->hw_all & IVTV_HW_CX25840)
+ itv->sd_video = ivtv_find_hw(itv, IVTV_HW_CX25840);
+ else if (itv->card->hw_all & IVTV_HW_SAA717X)
+ itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA717X);
+ else if (itv->card->hw_all & IVTV_HW_SAA7114)
+ itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA7114);
+ else
+ itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA7115);
+ itv->sd_audio = ivtv_find_hw(itv, itv->card->hw_audio_ctrl);
+ itv->sd_muxer = ivtv_find_hw(itv, itv->card->hw_muxer);
+
+ hw = itv->hw_flags;
+
+ if (itv->card->type == IVTV_CARD_CX23416GYC) {
+ /* Several variations of this card exist, detect which card
+ type should be used. */
+ if ((hw & (IVTV_HW_UPD64031A | IVTV_HW_UPD6408X)) == 0)
+ itv->card = ivtv_get_card(IVTV_CARD_CX23416GYC_NOGRYCS);
+ else if ((hw & IVTV_HW_UPD64031A) == 0)
+ itv->card = ivtv_get_card(IVTV_CARD_CX23416GYC_NOGR);
+ }
+ else if (itv->card->type == IVTV_CARD_GV_MVPRX ||
+ itv->card->type == IVTV_CARD_GV_MVPRX2E) {
+ /* The crystal frequency of GVMVPRX is 24.576MHz */
+ v4l2_subdev_call(itv->sd_video, video, s_crystal_freq,
+ SAA7115_FREQ_24_576_MHZ, SAA7115_FREQ_FL_UCGC);
+ }
+
+ if (hw & IVTV_HW_CX25840) {
+ itv->vbi.raw_decoder_line_size = 1444;
+ itv->vbi.raw_decoder_sav_odd_field = 0x20;
+ itv->vbi.raw_decoder_sav_even_field = 0x60;
+ itv->vbi.sliced_decoder_line_size = 272;
+ itv->vbi.sliced_decoder_sav_odd_field = 0xB0;
+ itv->vbi.sliced_decoder_sav_even_field = 0xF0;
+ }
+
+ if (hw & IVTV_HW_SAA711X) {
+ struct v4l2_dbg_chip_ident v;
+
+ /* determine the exact saa711x model */
+ itv->hw_flags &= ~IVTV_HW_SAA711X;
+
+ v.match.type = V4L2_CHIP_MATCH_I2C_DRIVER;
+ strlcpy(v.match.name, "saa7115", sizeof(v.match.name));
+ ivtv_call_hw(itv, IVTV_HW_SAA711X, core, g_chip_ident, &v);
+ if (v.ident == V4L2_IDENT_SAA7114) {
+ itv->hw_flags |= IVTV_HW_SAA7114;
+ /* VBI is not yet supported by the saa7114 driver. */
+ itv->v4l2_cap &= ~(V4L2_CAP_SLICED_VBI_CAPTURE|V4L2_CAP_VBI_CAPTURE);
+ } else {
+ itv->hw_flags |= IVTV_HW_SAA7115;
+ }
+ itv->vbi.raw_decoder_line_size = 1443;
+ itv->vbi.raw_decoder_sav_odd_field = 0x25;
+ itv->vbi.raw_decoder_sav_even_field = 0x62;
+ itv->vbi.sliced_decoder_line_size = 51;
+ itv->vbi.sliced_decoder_sav_odd_field = 0xAB;
+ itv->vbi.sliced_decoder_sav_even_field = 0xEC;
+ }
+
+ if (hw & IVTV_HW_SAA717X) {
+ itv->vbi.raw_decoder_line_size = 1443;
+ itv->vbi.raw_decoder_sav_odd_field = 0x25;
+ itv->vbi.raw_decoder_sav_even_field = 0x62;
+ itv->vbi.sliced_decoder_line_size = 51;
+ itv->vbi.sliced_decoder_sav_odd_field = 0xAB;
+ itv->vbi.sliced_decoder_sav_even_field = 0xEC;
+ }
+}
+
+static int __devinit ivtv_probe(struct pci_dev *pdev,
+ const struct pci_device_id *pci_id)
+{
+ int retval = 0;
+ int vbi_buf_size;
+ struct ivtv *itv;
+
+ itv = kzalloc(sizeof(struct ivtv), GFP_ATOMIC);
+ if (itv == NULL)
+ return -ENOMEM;
+ itv->pdev = pdev;
+ itv->instance = v4l2_device_set_name(&itv->v4l2_dev, "ivtv",
+ &ivtv_instance);
+
+ retval = v4l2_device_register(&pdev->dev, &itv->v4l2_dev);
+ if (retval) {
+ kfree(itv);
+ return retval;
+ }
+ IVTV_INFO("Initializing card %d\n", itv->instance);
+
+ ivtv_process_options(itv);
+ if (itv->options.cardtype == -1) {
+ retval = -ENODEV;
+ goto err;
+ }
+ if (ivtv_init_struct1(itv)) {
+ retval = -ENOMEM;
+ goto err;
+ }
+ retval = cx2341x_handler_init(&itv->cxhdl, 50);
+ if (retval)
+ goto err;
+ itv->v4l2_dev.ctrl_handler = &itv->cxhdl.hdl;
+ itv->cxhdl.ops = &ivtv_cxhdl_ops;
+ itv->cxhdl.priv = itv;
+ itv->cxhdl.func = ivtv_api_func;
+
+ IVTV_DEBUG_INFO("base addr: 0x%08x\n", itv->base_addr);
+
+ /* PCI Device Setup */
+ retval = ivtv_setup_pci(itv, pdev, pci_id);
+ if (retval == -EIO)
+ goto free_worker;
+ if (retval == -ENXIO)
+ goto free_mem;
+
+ /* map io memory */
+ IVTV_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n",
+ itv->base_addr + IVTV_ENCODER_OFFSET, IVTV_ENCODER_SIZE);
+ itv->enc_mem = ioremap_nocache(itv->base_addr + IVTV_ENCODER_OFFSET,
+ IVTV_ENCODER_SIZE);
+ if (!itv->enc_mem) {
+ IVTV_ERR("ioremap failed. Can't get a window into CX23415/6 "
+ "encoder memory\n");
+ IVTV_ERR("Each capture card with a CX23415/6 needs 8 MB of "
+ "vmalloc address space for this window\n");
+ IVTV_ERR("Check the output of 'grep Vmalloc /proc/meminfo'\n");
+ IVTV_ERR("Use the vmalloc= kernel command line option to set "
+ "VmallocTotal to a larger value\n");
+ retval = -ENOMEM;
+ goto free_mem;
+ }
+
+ if (itv->has_cx23415) {
+ IVTV_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n",
+ itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE);
+ itv->dec_mem = ioremap_nocache(itv->base_addr + IVTV_DECODER_OFFSET,
+ IVTV_DECODER_SIZE);
+ if (!itv->dec_mem) {
+ IVTV_ERR("ioremap failed. Can't get a window into "
+ "CX23415 decoder memory\n");
+ IVTV_ERR("Each capture card with a CX23415 needs 8 MB "
+ "of vmalloc address space for this window\n");
+ IVTV_ERR("Check the output of 'grep Vmalloc "
+ "/proc/meminfo'\n");
+ IVTV_ERR("Use the vmalloc= kernel command line option "
+ "to set VmallocTotal to a larger value\n");
+ retval = -ENOMEM;
+ goto free_mem;
+ }
+ }
+ else {
+ itv->dec_mem = itv->enc_mem;
+ }
+
+ /* map registers memory */
+ IVTV_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n",
+ itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+ itv->reg_mem =
+ ioremap_nocache(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+ if (!itv->reg_mem) {
+ IVTV_ERR("ioremap failed. Can't get a window into CX23415/6 "
+ "register space\n");
+ IVTV_ERR("Each capture card with a CX23415/6 needs 64 kB of "
+ "vmalloc address space for this window\n");
+ IVTV_ERR("Check the output of 'grep Vmalloc /proc/meminfo'\n");
+ IVTV_ERR("Use the vmalloc= kernel command line option to set "
+ "VmallocTotal to a larger value\n");
+ retval = -ENOMEM;
+ goto free_io;
+ }
+
+ retval = ivtv_gpio_init(itv);
+ if (retval)
+ goto free_io;
+
+ /* active i2c */
+ IVTV_DEBUG_INFO("activating i2c...\n");
+ if (init_ivtv_i2c(itv)) {
+ IVTV_ERR("Could not initialize i2c\n");
+ goto free_io;
+ }
+
+ if (itv->card->hw_all & IVTV_HW_TVEEPROM) {
+ /* Based on the model number the cardtype may be changed.
+ The PCI IDs are not always reliable. */
+ ivtv_process_eeprom(itv);
+ }
+ if (itv->card->comment)
+ IVTV_INFO("%s", itv->card->comment);
+ if (itv->card->v4l2_capabilities == 0) {
+ /* card was detected but is not supported */
+ retval = -ENODEV;
+ goto free_i2c;
+ }
+
+ if (itv->std == 0) {
+ itv->std = V4L2_STD_NTSC_M;
+ }
+
+ if (itv->options.tuner == -1) {
+ int i;
+
+ for (i = 0; i < IVTV_CARD_MAX_TUNERS; i++) {
+ if ((itv->std & itv->card->tuners[i].std) == 0)
+ continue;
+ itv->options.tuner = itv->card->tuners[i].tuner;
+ break;
+ }
+ }
+ /* if no tuner was found, then pick the first tuner in the card list */
+ if (itv->options.tuner == -1 && itv->card->tuners[0].std) {
+ itv->std = itv->card->tuners[0].std;
+ if (itv->std & V4L2_STD_PAL)
+ itv->std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
+ else if (itv->std & V4L2_STD_NTSC)
+ itv->std = V4L2_STD_NTSC_M;
+ else if (itv->std & V4L2_STD_SECAM)
+ itv->std = V4L2_STD_SECAM_L;
+ itv->options.tuner = itv->card->tuners[0].tuner;
+ }
+ if (itv->options.radio == -1)
+ itv->options.radio = (itv->card->radio_input.audio_type != 0);
+
+ /* The card is now fully identified, continue with card-specific
+ initialization. */
+ ivtv_init_struct2(itv);
+
+ ivtv_load_and_init_modules(itv);
+
+ if (itv->std & V4L2_STD_525_60) {
+ itv->is_60hz = 1;
+ itv->is_out_60hz = 1;
+ } else {
+ itv->is_50hz = 1;
+ itv->is_out_50hz = 1;
+ }
+
+ itv->yuv_info.osd_full_w = 720;
+ itv->yuv_info.osd_full_h = itv->is_out_50hz ? 576 : 480;
+ itv->yuv_info.v4l2_src_w = itv->yuv_info.osd_full_w;
+ itv->yuv_info.v4l2_src_h = itv->yuv_info.osd_full_h;
+
+ cx2341x_handler_set_50hz(&itv->cxhdl, itv->is_50hz);
+
+ itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_MPG] = 0x08000;
+ itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_PCM] = 0x01200;
+ itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_MPG] = 0x10000;
+ itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_YUV] = 0x10000;
+ itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_YUV] = 0x08000;
+
+ /* Setup VBI Raw Size. Should be big enough to hold PAL.
+ It is possible to switch between PAL and NTSC, so we need to
+ take the largest size here. */
+ /* 1456 is multiple of 16, real size = 1444 */
+ itv->vbi.raw_size = 1456;
+ /* We use a buffer size of 1/2 of the total size needed for a
+ frame. This is actually very useful, since we now receive
+ a field at a time and that makes 'compressing' the raw data
+ down to size by stripping off the SAV codes a lot easier.
+ Note: having two different buffer sizes prevents standard
+ switching on the fly. We need to find a better solution... */
+ vbi_buf_size = itv->vbi.raw_size * (itv->is_60hz ? 24 : 36) / 2;
+ itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_VBI] = vbi_buf_size;
+ itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_VBI] = sizeof(struct v4l2_sliced_vbi_data) * 36;
+
+ if (itv->options.radio > 0)
+ itv->v4l2_cap |= V4L2_CAP_RADIO;
+
+ if (itv->options.tuner > -1) {
+ struct tuner_setup setup;
+
+ setup.addr = ADDR_UNSET;
+ setup.type = itv->options.tuner;
+ setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */
+ if (itv->options.radio > 0)
+ setup.mode_mask |= T_RADIO;
+ setup.tuner_callback = (setup.type == TUNER_XC2028) ?
+ ivtv_reset_tuner_gpio : NULL;
+ ivtv_call_all(itv, tuner, s_type_addr, &setup);
+ if (setup.type == TUNER_XC2028) {
+ static struct xc2028_ctrl ctrl = {
+ .fname = XC2028_DEFAULT_FIRMWARE,
+ .max_len = 64,
+ };
+ struct v4l2_priv_tun_config cfg = {
+ .tuner = itv->options.tuner,
+ .priv = &ctrl,
+ };
+ ivtv_call_all(itv, tuner, s_config, &cfg);
+ }
+ }
+
+ /* The tuner is fixed to the standard. The other inputs (e.g. S-Video)
+ are not. */
+ itv->tuner_std = itv->std;
+
+ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
+ struct v4l2_ctrl_handler *hdl = itv->v4l2_dev.ctrl_handler;
+
+ itv->ctrl_pts = v4l2_ctrl_new_std(hdl, &ivtv_hdl_out_ops,
+ V4L2_CID_MPEG_VIDEO_DEC_PTS, 0, 0, 1, 0);
+ itv->ctrl_frame = v4l2_ctrl_new_std(hdl, &ivtv_hdl_out_ops,
+ V4L2_CID_MPEG_VIDEO_DEC_FRAME, 0, 0x7fffffff, 1, 0);
+ /* Note: V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO is not supported,
+ mask that menu item. */
+ itv->ctrl_audio_playback =
+ v4l2_ctrl_new_std_menu(hdl, &ivtv_hdl_out_ops,
+ V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK,
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO,
+ 1 << V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO,
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_STEREO);
+ itv->ctrl_audio_multilingual_playback =
+ v4l2_ctrl_new_std_menu(hdl, &ivtv_hdl_out_ops,
+ V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK,
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO,
+ 1 << V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO,
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_LEFT);
+ if (hdl->error) {
+ retval = hdl->error;
+ goto free_i2c;
+ }
+ v4l2_ctrl_cluster(2, &itv->ctrl_pts);
+ v4l2_ctrl_cluster(2, &itv->ctrl_audio_playback);
+ ivtv_call_all(itv, video, s_std_output, itv->std);
+ /* Turn off the output signal. The mpeg decoder is not yet
+ active so without this you would get a green image until the
+ mpeg decoder becomes active. */
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0);
+ }
+
+ /* clear interrupt mask, effectively disabling interrupts */
+ ivtv_set_irq_mask(itv, 0xffffffff);
+
+ /* Register IRQ */
+ retval = request_irq(itv->pdev->irq, ivtv_irq_handler,
+ IRQF_SHARED | IRQF_DISABLED, itv->v4l2_dev.name, (void *)itv);
+ if (retval) {
+ IVTV_ERR("Failed to register irq %d\n", retval);
+ goto free_i2c;
+ }
+
+ retval = ivtv_streams_setup(itv);
+ if (retval) {
+ IVTV_ERR("Error %d setting up streams\n", retval);
+ goto free_irq;
+ }
+ retval = ivtv_streams_register(itv);
+ if (retval) {
+ IVTV_ERR("Error %d registering devices\n", retval);
+ goto free_streams;
+ }
+ IVTV_INFO("Initialized card: %s\n", itv->card_name);
+ return 0;
+
+free_streams:
+ ivtv_streams_cleanup(itv, 1);
+free_irq:
+ free_irq(itv->pdev->irq, (void *)itv);
+free_i2c:
+ v4l2_ctrl_handler_free(&itv->cxhdl.hdl);
+ exit_ivtv_i2c(itv);
+free_io:
+ ivtv_iounmap(itv);
+free_mem:
+ release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
+ release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+ if (itv->has_cx23415)
+ release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE);
+free_worker:
+ kthread_stop(itv->irq_worker_task);
+err:
+ if (retval == 0)
+ retval = -ENODEV;
+ IVTV_ERR("Error %d on initialization\n", retval);
+
+ v4l2_device_unregister(&itv->v4l2_dev);
+ kfree(itv);
+ return retval;
+}
+
+int ivtv_init_on_first_open(struct ivtv *itv)
+{
+ struct v4l2_frequency vf;
+ /* Needed to call ioctls later */
+ struct ivtv_open_id fh;
+ int fw_retry_count = 3;
+ int video_input;
+
+ fh.itv = itv;
+
+ if (test_bit(IVTV_F_I_FAILED, &itv->i_flags))
+ return -ENXIO;
+
+ if (test_and_set_bit(IVTV_F_I_INITED, &itv->i_flags))
+ return 0;
+
+ while (--fw_retry_count > 0) {
+ /* load firmware */
+ if (ivtv_firmware_init(itv) == 0)
+ break;
+ if (fw_retry_count > 1)
+ IVTV_WARN("Retry loading firmware\n");
+ }
+
+ if (fw_retry_count == 0) {
+ set_bit(IVTV_F_I_FAILED, &itv->i_flags);
+ return -ENXIO;
+ }
+
+ /* Try and get firmware versions */
+ IVTV_DEBUG_INFO("Getting firmware version..\n");
+ ivtv_firmware_versions(itv);
+
+ if (itv->card->hw_all & IVTV_HW_CX25840)
+ v4l2_subdev_call(itv->sd_video, core, load_fw);
+
+ vf.tuner = 0;
+ vf.type = V4L2_TUNER_ANALOG_TV;
+ vf.frequency = 6400; /* the tuner 'baseline' frequency */
+
+ /* Set initial frequency. For PAL/SECAM broadcasts no
+ 'default' channel exists AFAIK. */
+ if (itv->std == V4L2_STD_NTSC_M_JP) {
+ vf.frequency = 1460; /* ch. 1 91250*16/1000 */
+ }
+ else if (itv->std & V4L2_STD_NTSC_M) {
+ vf.frequency = 1076; /* ch. 4 67250*16/1000 */
+ }
+
+ video_input = itv->active_input;
+ itv->active_input++; /* Force update of input */
+ ivtv_s_input(NULL, &fh, video_input);
+
+ /* Let the VIDIOC_S_STD ioctl do all the work, keeps the code
+ in one place. */
+ itv->std++; /* Force full standard initialization */
+ itv->std_out = itv->std;
+ ivtv_s_frequency(NULL, &fh, &vf);
+
+ if (itv->card->v4l2_capabilities & V4L2_CAP_VIDEO_OUTPUT) {
+ /* Turn on the TV-out: ivtv_init_mpeg_decoder() initializes
+ the mpeg decoder so now the saa7127 receives a proper
+ signal. */
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1);
+ ivtv_init_mpeg_decoder(itv);
+ }
+
+ /* On a cx23416 this seems to be able to enable DMA to the chip? */
+ if (!itv->has_cx23415)
+ write_reg_sync(0x03, IVTV_REG_DMACONTROL);
+
+ ivtv_s_std_enc(itv, &itv->tuner_std);
+
+ /* Default interrupts enabled. For the PVR350 this includes the
+ decoder VSYNC interrupt, which is always on. It is not only used
+ during decoding but also by the OSD.
+ Some old PVR250 cards had a cx23415, so testing for that is too
+ general. Instead test if the card has video output capability. */
+ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
+ ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT | IVTV_IRQ_DEC_VSYNC);
+ ivtv_set_osd_alpha(itv);
+ ivtv_s_std_dec(itv, &itv->tuner_std);
+ } else {
+ ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT);
+ }
+
+ /* Setup initial controls */
+ cx2341x_handler_setup(&itv->cxhdl);
+ return 0;
+}
+
+static void ivtv_remove(struct pci_dev *pdev)
+{
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
+ struct ivtv *itv = to_ivtv(v4l2_dev);
+ int i;
+
+ IVTV_DEBUG_INFO("Removing card\n");
+
+ if (test_bit(IVTV_F_I_INITED, &itv->i_flags)) {
+ /* Stop all captures */
+ IVTV_DEBUG_INFO("Stopping all streams\n");
+ if (atomic_read(&itv->capturing) > 0)
+ ivtv_stop_all_captures(itv);
+
+ /* Stop all decoding */
+ IVTV_DEBUG_INFO("Stopping decoding\n");
+
+ /* Turn off the TV-out */
+ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0);
+ if (atomic_read(&itv->decoding) > 0) {
+ int type;
+
+ if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags))
+ type = IVTV_DEC_STREAM_TYPE_YUV;
+ else
+ type = IVTV_DEC_STREAM_TYPE_MPG;
+ ivtv_stop_v4l2_decode_stream(&itv->streams[type],
+ V4L2_DEC_CMD_STOP_TO_BLACK | V4L2_DEC_CMD_STOP_IMMEDIATELY, 0);
+ }
+ ivtv_halt_firmware(itv);
+ }
+
+ /* Interrupts */
+ ivtv_set_irq_mask(itv, 0xffffffff);
+ del_timer_sync(&itv->dma_timer);
+
+ /* Kill irq worker */
+ flush_kthread_worker(&itv->irq_worker);
+ kthread_stop(itv->irq_worker_task);
+
+ ivtv_streams_cleanup(itv, 1);
+ ivtv_udma_free(itv);
+
+ v4l2_ctrl_handler_free(&itv->cxhdl.hdl);
+
+ exit_ivtv_i2c(itv);
+
+ free_irq(itv->pdev->irq, (void *)itv);
+ ivtv_iounmap(itv);
+
+ release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
+ release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+ if (itv->has_cx23415)
+ release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE);
+
+ pci_disable_device(itv->pdev);
+ for (i = 0; i < IVTV_VBI_FRAMES; i++)
+ kfree(itv->vbi.sliced_mpeg_data[i]);
+
+ printk(KERN_INFO "ivtv: Removed %s\n", itv->card_name);
+
+ v4l2_device_unregister(&itv->v4l2_dev);
+ kfree(itv);
+}
+
+/* define a pci_driver for card detection */
+static struct pci_driver ivtv_pci_driver = {
+ .name = "ivtv",
+ .id_table = ivtv_pci_tbl,
+ .probe = ivtv_probe,
+ .remove = ivtv_remove,
+};
+
+static int __init module_start(void)
+{
+ printk(KERN_INFO "ivtv: Start initialization, version %s\n", IVTV_VERSION);
+
+ /* Validate parameters */
+ if (ivtv_first_minor < 0 || ivtv_first_minor >= IVTV_MAX_CARDS) {
+ printk(KERN_ERR "ivtv: Exiting, ivtv_first_minor must be between 0 and %d\n",
+ IVTV_MAX_CARDS - 1);
+ return -1;
+ }
+
+ if (ivtv_debug < 0 || ivtv_debug > 2047) {
+ ivtv_debug = 0;
+ printk(KERN_INFO "ivtv: Debug value must be >= 0 and <= 2047\n");
+ }
+
+ if (pci_register_driver(&ivtv_pci_driver)) {
+ printk(KERN_ERR "ivtv: Error detecting PCI card\n");
+ return -ENODEV;
+ }
+ printk(KERN_INFO "ivtv: End initialization\n");
+ return 0;
+}
+
+static void __exit module_cleanup(void)
+{
+ pci_unregister_driver(&ivtv_pci_driver);
+}
+
+/* Note: These symbols are exported because they are used by the ivtvfb
+ framebuffer module and an infrared module for the IR-blaster. */
+EXPORT_SYMBOL(ivtv_set_irq_mask);
+EXPORT_SYMBOL(ivtv_api);
+EXPORT_SYMBOL(ivtv_vapi);
+EXPORT_SYMBOL(ivtv_vapi_result);
+EXPORT_SYMBOL(ivtv_clear_irq_mask);
+EXPORT_SYMBOL(ivtv_debug);
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+EXPORT_SYMBOL(ivtv_fw_debug);
+#endif
+EXPORT_SYMBOL(ivtv_reset_ir_gpio);
+EXPORT_SYMBOL(ivtv_udma_setup);
+EXPORT_SYMBOL(ivtv_udma_unmap);
+EXPORT_SYMBOL(ivtv_udma_alloc);
+EXPORT_SYMBOL(ivtv_udma_prepare);
+EXPORT_SYMBOL(ivtv_init_on_first_open);
+EXPORT_SYMBOL(ivtv_firmware_check);
+
+module_init(module_start);
+module_exit(module_cleanup);
diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h
new file mode 100644
index 00000000..2e220028
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-driver.h
@@ -0,0 +1,839 @@
+/*
+ ivtv driver internal defines and structures
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef IVTV_DRIVER_H
+#define IVTV_DRIVER_H
+
+/* Internal header for ivtv project:
+ * Driver for the cx23415/6 chip.
+ * Author: Kevin Thayer (nufan_wfk at yahoo.com)
+ * License: GPL
+ * http://www.ivtvdriver.org
+ *
+ * -----
+ * MPG600/MPG160 support by T.Adachi <tadachi@tadachi-net.com>
+ * and Takeru KOMORIYA<komoriya@paken.org>
+ *
+ * AVerMedia M179 GPIO info by Chris Pinkham <cpinkham@bc2va.org>
+ * using information provided by Jiun-Kuei Jung @ AVerMedia.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/list.h>
+#include <linux/unistd.h>
+#include <linux/pagemap.h>
+#include <linux/scatterlist.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+
+#include <linux/dvb/video.h>
+#include <linux/dvb/audio.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+#include <media/tuner.h>
+#include <media/cx2341x.h>
+#include <media/ir-kbd-i2c.h>
+
+#include <linux/ivtv.h>
+
+/* Memory layout */
+#define IVTV_ENCODER_OFFSET 0x00000000
+#define IVTV_ENCODER_SIZE 0x00800000 /* Total size is 0x01000000, but only first half is used */
+#define IVTV_DECODER_OFFSET 0x01000000
+#define IVTV_DECODER_SIZE 0x00800000 /* Total size is 0x01000000, but only first half is used */
+#define IVTV_REG_OFFSET 0x02000000
+#define IVTV_REG_SIZE 0x00010000
+
+/* Maximum ivtv driver instances. Some people have a huge number of
+ capture cards, so set this to a high value. */
+#define IVTV_MAX_CARDS 32
+
+#define IVTV_ENC_STREAM_TYPE_MPG 0
+#define IVTV_ENC_STREAM_TYPE_YUV 1
+#define IVTV_ENC_STREAM_TYPE_VBI 2
+#define IVTV_ENC_STREAM_TYPE_PCM 3
+#define IVTV_ENC_STREAM_TYPE_RAD 4
+#define IVTV_DEC_STREAM_TYPE_MPG 5
+#define IVTV_DEC_STREAM_TYPE_VBI 6
+#define IVTV_DEC_STREAM_TYPE_VOUT 7
+#define IVTV_DEC_STREAM_TYPE_YUV 8
+#define IVTV_MAX_STREAMS 9
+
+#define IVTV_DMA_SG_OSD_ENT (2883584/PAGE_SIZE) /* sg entities */
+
+/* DMA Registers */
+#define IVTV_REG_DMAXFER (0x0000)
+#define IVTV_REG_DMASTATUS (0x0004)
+#define IVTV_REG_DECDMAADDR (0x0008)
+#define IVTV_REG_ENCDMAADDR (0x000c)
+#define IVTV_REG_DMACONTROL (0x0010)
+#define IVTV_REG_IRQSTATUS (0x0040)
+#define IVTV_REG_IRQMASK (0x0048)
+
+/* Setup Registers */
+#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8)
+#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC)
+#define IVTV_REG_DEC_SDRAM_REFRESH (0x08F8)
+#define IVTV_REG_DEC_SDRAM_PRECHARGE (0x08FC)
+#define IVTV_REG_VDM (0x2800)
+#define IVTV_REG_AO (0x2D00)
+#define IVTV_REG_BYTEFLUSH (0x2D24)
+#define IVTV_REG_SPU (0x9050)
+#define IVTV_REG_HW_BLOCKS (0x9054)
+#define IVTV_REG_VPU (0x9058)
+#define IVTV_REG_APU (0xA064)
+
+/* Other registers */
+#define IVTV_REG_DEC_LINE_FIELD (0x28C0)
+
+/* debugging */
+extern int ivtv_debug;
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+extern int ivtv_fw_debug;
+#endif
+
+#define IVTV_DBGFLG_WARN (1 << 0)
+#define IVTV_DBGFLG_INFO (1 << 1)
+#define IVTV_DBGFLG_MB (1 << 2)
+#define IVTV_DBGFLG_IOCTL (1 << 3)
+#define IVTV_DBGFLG_FILE (1 << 4)
+#define IVTV_DBGFLG_DMA (1 << 5)
+#define IVTV_DBGFLG_IRQ (1 << 6)
+#define IVTV_DBGFLG_DEC (1 << 7)
+#define IVTV_DBGFLG_YUV (1 << 8)
+#define IVTV_DBGFLG_I2C (1 << 9)
+/* Flag to turn on high volume debugging */
+#define IVTV_DBGFLG_HIGHVOL (1 << 10)
+
+#define IVTV_DEBUG(x, type, fmt, args...) \
+ do { \
+ if ((x) & ivtv_debug) \
+ v4l2_info(&itv->v4l2_dev, " " type ": " fmt , ##args); \
+ } while (0)
+#define IVTV_DEBUG_WARN(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_WARN, "warn", fmt , ## args)
+#define IVTV_DEBUG_INFO(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_INFO, "info", fmt , ## args)
+#define IVTV_DEBUG_MB(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_MB, "mb", fmt , ## args)
+#define IVTV_DEBUG_DMA(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_DMA, "dma", fmt , ## args)
+#define IVTV_DEBUG_IOCTL(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_IOCTL, "ioctl", fmt , ## args)
+#define IVTV_DEBUG_FILE(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_FILE, "file", fmt , ## args)
+#define IVTV_DEBUG_I2C(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_I2C, "i2c", fmt , ## args)
+#define IVTV_DEBUG_IRQ(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_IRQ, "irq", fmt , ## args)
+#define IVTV_DEBUG_DEC(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_DEC, "dec", fmt , ## args)
+#define IVTV_DEBUG_YUV(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_YUV, "yuv", fmt , ## args)
+
+#define IVTV_DEBUG_HIGH_VOL(x, type, fmt, args...) \
+ do { \
+ if (((x) & ivtv_debug) && (ivtv_debug & IVTV_DBGFLG_HIGHVOL)) \
+ v4l2_info(&itv->v4l2_dev, " " type ": " fmt , ##args); \
+ } while (0)
+#define IVTV_DEBUG_HI_WARN(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_WARN, "warn", fmt , ## args)
+#define IVTV_DEBUG_HI_INFO(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_INFO, "info", fmt , ## args)
+#define IVTV_DEBUG_HI_MB(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_MB, "mb", fmt , ## args)
+#define IVTV_DEBUG_HI_DMA(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_DMA, "dma", fmt , ## args)
+#define IVTV_DEBUG_HI_IOCTL(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_IOCTL, "ioctl", fmt , ## args)
+#define IVTV_DEBUG_HI_FILE(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_FILE, "file", fmt , ## args)
+#define IVTV_DEBUG_HI_I2C(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_I2C, "i2c", fmt , ## args)
+#define IVTV_DEBUG_HI_IRQ(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_IRQ, "irq", fmt , ## args)
+#define IVTV_DEBUG_HI_DEC(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_DEC, "dec", fmt , ## args)
+#define IVTV_DEBUG_HI_YUV(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_YUV, "yuv", fmt , ## args)
+
+/* Standard kernel messages */
+#define IVTV_ERR(fmt, args...) v4l2_err(&itv->v4l2_dev, fmt , ## args)
+#define IVTV_WARN(fmt, args...) v4l2_warn(&itv->v4l2_dev, fmt , ## args)
+#define IVTV_INFO(fmt, args...) v4l2_info(&itv->v4l2_dev, fmt , ## args)
+
+/* output modes (cx23415 only) */
+#define OUT_NONE 0
+#define OUT_MPG 1
+#define OUT_YUV 2
+#define OUT_UDMA_YUV 3
+#define OUT_PASSTHROUGH 4
+
+#define IVTV_MAX_PGM_INDEX (400)
+
+/* Default I2C SCL period in microseconds */
+#define IVTV_DEFAULT_I2C_CLOCK_PERIOD 20
+
+struct ivtv_options {
+ int kilobytes[IVTV_MAX_STREAMS]; /* size in kilobytes of each stream */
+ int cardtype; /* force card type on load */
+ int tuner; /* set tuner on load */
+ int radio; /* enable/disable radio */
+ int newi2c; /* new I2C algorithm */
+ int i2c_clock_period; /* period of SCL for I2C bus */
+};
+
+/* ivtv-specific mailbox template */
+struct ivtv_mailbox {
+ u32 flags;
+ u32 cmd;
+ u32 retval;
+ u32 timeout;
+ u32 data[CX2341X_MBOX_MAX_DATA];
+};
+
+struct ivtv_api_cache {
+ unsigned long last_jiffies; /* when last command was issued */
+ u32 data[CX2341X_MBOX_MAX_DATA]; /* last sent api data */
+};
+
+struct ivtv_mailbox_data {
+ volatile struct ivtv_mailbox __iomem *mbox;
+ /* Bits 0-2 are for the encoder mailboxes, 0-1 are for the decoder mailboxes.
+ If the bit is set, then the corresponding mailbox is in use by the driver. */
+ unsigned long busy;
+ u8 max_mbox;
+};
+
+/* per-buffer bit flags */
+#define IVTV_F_B_NEED_BUF_SWAP (1 << 0) /* this buffer should be byte swapped */
+
+/* per-stream, s_flags */
+#define IVTV_F_S_DMA_PENDING 0 /* this stream has pending DMA */
+#define IVTV_F_S_DMA_HAS_VBI 1 /* the current DMA request also requests VBI data */
+#define IVTV_F_S_NEEDS_DATA 2 /* this decoding stream needs more data */
+
+#define IVTV_F_S_CLAIMED 3 /* this stream is claimed */
+#define IVTV_F_S_STREAMING 4 /* the fw is decoding/encoding this stream */
+#define IVTV_F_S_INTERNAL_USE 5 /* this stream is used internally (sliced VBI processing) */
+#define IVTV_F_S_PASSTHROUGH 6 /* this stream is in passthrough mode */
+#define IVTV_F_S_STREAMOFF 7 /* signal end of stream EOS */
+#define IVTV_F_S_APPL_IO 8 /* this stream is used read/written by an application */
+
+#define IVTV_F_S_PIO_PENDING 9 /* this stream has pending PIO */
+#define IVTV_F_S_PIO_HAS_VBI 1 /* the current PIO request also requests VBI data */
+
+/* per-ivtv, i_flags */
+#define IVTV_F_I_DMA 0 /* DMA in progress */
+#define IVTV_F_I_UDMA 1 /* UDMA in progress */
+#define IVTV_F_I_UDMA_PENDING 2 /* UDMA pending */
+#define IVTV_F_I_SPEED_CHANGE 3 /* a speed change is in progress */
+#define IVTV_F_I_EOS 4 /* end of encoder stream reached */
+#define IVTV_F_I_RADIO_USER 5 /* the radio tuner is selected */
+#define IVTV_F_I_DIG_RST 6 /* reset digitizer */
+#define IVTV_F_I_DEC_YUV 7 /* YUV instead of MPG is being decoded */
+#define IVTV_F_I_UPDATE_CC 9 /* CC should be updated */
+#define IVTV_F_I_UPDATE_WSS 10 /* WSS should be updated */
+#define IVTV_F_I_UPDATE_VPS 11 /* VPS should be updated */
+#define IVTV_F_I_DECODING_YUV 12 /* this stream is YUV frame decoding */
+#define IVTV_F_I_ENC_PAUSED 13 /* the encoder is paused */
+#define IVTV_F_I_VALID_DEC_TIMINGS 14 /* last_dec_timing is valid */
+#define IVTV_F_I_HAVE_WORK 15 /* used in the interrupt handler: there is work to be done */
+#define IVTV_F_I_WORK_HANDLER_VBI 16 /* there is work to be done for VBI */
+#define IVTV_F_I_WORK_HANDLER_YUV 17 /* there is work to be done for YUV */
+#define IVTV_F_I_WORK_HANDLER_PIO 18 /* there is work to be done for PIO */
+#define IVTV_F_I_PIO 19 /* PIO in progress */
+#define IVTV_F_I_DEC_PAUSED 20 /* the decoder is paused */
+#define IVTV_F_I_INITED 21 /* set after first open */
+#define IVTV_F_I_FAILED 22 /* set if first open failed */
+
+/* Event notifications */
+#define IVTV_F_I_EV_DEC_STOPPED 28 /* decoder stopped event */
+#define IVTV_F_I_EV_VSYNC 29 /* VSYNC event */
+#define IVTV_F_I_EV_VSYNC_FIELD 30 /* VSYNC event field (0 = first, 1 = second field) */
+#define IVTV_F_I_EV_VSYNC_ENABLED 31 /* VSYNC event enabled */
+
+/* Scatter-Gather array element, used in DMA transfers */
+struct ivtv_sg_element {
+ __le32 src;
+ __le32 dst;
+ __le32 size;
+};
+
+struct ivtv_sg_host_element {
+ u32 src;
+ u32 dst;
+ u32 size;
+};
+
+struct ivtv_user_dma {
+ struct mutex lock;
+ int page_count;
+ struct page *map[IVTV_DMA_SG_OSD_ENT];
+ /* Needed when dealing with highmem userspace buffers */
+ struct page *bouncemap[IVTV_DMA_SG_OSD_ENT];
+
+ /* Base Dev SG Array for cx23415/6 */
+ struct ivtv_sg_element SGarray[IVTV_DMA_SG_OSD_ENT];
+ dma_addr_t SG_handle;
+ int SG_length;
+
+ /* SG List of Buffers */
+ struct scatterlist SGlist[IVTV_DMA_SG_OSD_ENT];
+};
+
+struct ivtv_dma_page_info {
+ unsigned long uaddr;
+ unsigned long first;
+ unsigned long last;
+ unsigned int offset;
+ unsigned int tail;
+ int page_count;
+};
+
+struct ivtv_buffer {
+ struct list_head list;
+ dma_addr_t dma_handle;
+ unsigned short b_flags;
+ unsigned short dma_xfer_cnt;
+ char *buf;
+ u32 bytesused;
+ u32 readpos;
+};
+
+struct ivtv_queue {
+ struct list_head list; /* the list of buffers in this queue */
+ u32 buffers; /* number of buffers in this queue */
+ u32 length; /* total number of bytes of available buffer space */
+ u32 bytesused; /* total number of bytes used in this queue */
+};
+
+struct ivtv; /* forward reference */
+
+struct ivtv_stream {
+ /* These first four fields are always set, even if the stream
+ is not actually created. */
+ struct video_device *vdev; /* NULL when stream not created */
+ struct ivtv *itv; /* for ease of use */
+ const char *name; /* name of the stream */
+ int type; /* stream type */
+ u32 caps; /* V4L2 capabilities */
+
+ struct v4l2_fh *fh; /* pointer to the streaming filehandle */
+ spinlock_t qlock; /* locks access to the queues */
+ unsigned long s_flags; /* status flags, see above */
+ int dma; /* can be PCI_DMA_TODEVICE, PCI_DMA_FROMDEVICE or PCI_DMA_NONE */
+ u32 pending_offset;
+ u32 pending_backup;
+ u64 pending_pts;
+
+ u32 dma_offset;
+ u32 dma_backup;
+ u64 dma_pts;
+
+ int subtype;
+ wait_queue_head_t waitq;
+ u32 dma_last_offset;
+
+ /* Buffer Stats */
+ u32 buffers;
+ u32 buf_size;
+ u32 buffers_stolen;
+
+ /* Buffer Queues */
+ struct ivtv_queue q_free; /* free buffers */
+ struct ivtv_queue q_full; /* full buffers */
+ struct ivtv_queue q_io; /* waiting for I/O */
+ struct ivtv_queue q_dma; /* waiting for DMA */
+ struct ivtv_queue q_predma; /* waiting for DMA */
+
+ /* DMA xfer counter, buffers belonging to the same DMA
+ xfer will have the same dma_xfer_cnt. */
+ u16 dma_xfer_cnt;
+
+ /* Base Dev SG Array for cx23415/6 */
+ struct ivtv_sg_host_element *sg_pending;
+ struct ivtv_sg_host_element *sg_processing;
+ struct ivtv_sg_element *sg_dma;
+ dma_addr_t sg_handle;
+ int sg_pending_size;
+ int sg_processing_size;
+ int sg_processed;
+
+ /* SG List of Buffers */
+ struct scatterlist *SGlist;
+};
+
+struct ivtv_open_id {
+ struct v4l2_fh fh;
+ int type; /* stream type */
+ int yuv_frames; /* 1: started OUT_UDMA_YUV output mode */
+ struct ivtv *itv;
+};
+
+static inline struct ivtv_open_id *fh2id(struct v4l2_fh *fh)
+{
+ return container_of(fh, struct ivtv_open_id, fh);
+}
+
+struct yuv_frame_info
+{
+ u32 update;
+ s32 src_x;
+ s32 src_y;
+ u32 src_w;
+ u32 src_h;
+ s32 dst_x;
+ s32 dst_y;
+ u32 dst_w;
+ u32 dst_h;
+ s32 pan_x;
+ s32 pan_y;
+ u32 vis_w;
+ u32 vis_h;
+ u32 interlaced_y;
+ u32 interlaced_uv;
+ s32 tru_x;
+ u32 tru_w;
+ u32 tru_h;
+ u32 offset_y;
+ s32 lace_mode;
+ u32 sync_field;
+ u32 delay;
+ u32 interlaced;
+};
+
+#define IVTV_YUV_MODE_INTERLACED 0x00
+#define IVTV_YUV_MODE_PROGRESSIVE 0x01
+#define IVTV_YUV_MODE_AUTO 0x02
+#define IVTV_YUV_MODE_MASK 0x03
+
+#define IVTV_YUV_SYNC_EVEN 0x00
+#define IVTV_YUV_SYNC_ODD 0x04
+#define IVTV_YUV_SYNC_MASK 0x04
+
+#define IVTV_YUV_BUFFERS 8
+
+struct yuv_playback_info
+{
+ u32 reg_2834;
+ u32 reg_2838;
+ u32 reg_283c;
+ u32 reg_2840;
+ u32 reg_2844;
+ u32 reg_2848;
+ u32 reg_2854;
+ u32 reg_285c;
+ u32 reg_2864;
+
+ u32 reg_2870;
+ u32 reg_2874;
+ u32 reg_2890;
+ u32 reg_2898;
+ u32 reg_289c;
+
+ u32 reg_2918;
+ u32 reg_291c;
+ u32 reg_2920;
+ u32 reg_2924;
+ u32 reg_2928;
+ u32 reg_292c;
+ u32 reg_2930;
+
+ u32 reg_2934;
+
+ u32 reg_2938;
+ u32 reg_293c;
+ u32 reg_2940;
+ u32 reg_2944;
+ u32 reg_2948;
+ u32 reg_294c;
+ u32 reg_2950;
+ u32 reg_2954;
+ u32 reg_2958;
+ u32 reg_295c;
+ u32 reg_2960;
+ u32 reg_2964;
+ u32 reg_2968;
+ u32 reg_296c;
+
+ u32 reg_2970;
+
+ int v_filter_1;
+ int v_filter_2;
+ int h_filter;
+
+ u8 track_osd; /* Should yuv output track the OSD size & position */
+
+ u32 osd_x_offset;
+ u32 osd_y_offset;
+
+ u32 osd_x_pan;
+ u32 osd_y_pan;
+
+ u32 osd_vis_w;
+ u32 osd_vis_h;
+
+ u32 osd_full_w;
+ u32 osd_full_h;
+
+ int decode_height;
+
+ int lace_mode;
+ int lace_threshold;
+ int lace_sync_field;
+
+ atomic_t next_dma_frame;
+ atomic_t next_fill_frame;
+
+ u32 yuv_forced_update;
+ int update_frame;
+
+ u8 fields_lapsed; /* Counter used when delaying a frame */
+
+ struct yuv_frame_info new_frame_info[IVTV_YUV_BUFFERS];
+ struct yuv_frame_info old_frame_info;
+ struct yuv_frame_info old_frame_info_args;
+
+ void *blanking_ptr;
+ dma_addr_t blanking_dmaptr;
+
+ int stream_size;
+
+ u8 draw_frame; /* PVR350 buffer to draw into */
+ u8 max_frames_buffered; /* Maximum number of frames to buffer */
+
+ struct v4l2_rect main_rect;
+ u32 v4l2_src_w;
+ u32 v4l2_src_h;
+
+ u8 running; /* Have any frames been displayed */
+};
+
+#define IVTV_VBI_FRAMES 32
+
+/* VBI data */
+struct vbi_cc {
+ u8 odd[2]; /* two-byte payload of odd field */
+ u8 even[2]; /* two-byte payload of even field */;
+};
+
+struct vbi_vps {
+ u8 data[5]; /* five-byte VPS payload */
+};
+
+struct vbi_info {
+ /* VBI general data, does not change during streaming */
+
+ u32 raw_decoder_line_size; /* raw VBI line size from digitizer */
+ u8 raw_decoder_sav_odd_field; /* raw VBI Start Active Video digitizer code of odd field */
+ u8 raw_decoder_sav_even_field; /* raw VBI Start Active Video digitizer code of even field */
+ u32 sliced_decoder_line_size; /* sliced VBI line size from digitizer */
+ u8 sliced_decoder_sav_odd_field; /* sliced VBI Start Active Video digitizer code of odd field */
+ u8 sliced_decoder_sav_even_field; /* sliced VBI Start Active Video digitizer code of even field */
+
+ u32 start[2]; /* start of first VBI line in the odd/even fields */
+ u32 count; /* number of VBI lines per field */
+ u32 raw_size; /* size of raw VBI line from the digitizer */
+ u32 sliced_size; /* size of sliced VBI line from the digitizer */
+
+ u32 dec_start; /* start in decoder memory of VBI re-insertion buffers */
+ u32 enc_start; /* start in encoder memory of VBI capture buffers */
+ u32 enc_size; /* size of VBI capture area */
+ int fpi; /* number of VBI frames per interrupt */
+
+ struct v4l2_format in; /* current VBI capture format */
+ struct v4l2_sliced_vbi_format *sliced_in; /* convenience pointer to sliced struct in vbi.in union */
+ int insert_mpeg; /* if non-zero, then embed VBI data in MPEG stream */
+
+ /* Raw VBI compatibility hack */
+
+ u32 frame; /* frame counter hack needed for backwards compatibility
+ of old VBI software */
+
+ /* Sliced VBI output data */
+
+ struct vbi_cc cc_payload[256]; /* sliced VBI CC payload array: it is an array to
+ prevent dropping CC data if they couldn't be
+ processed fast enough */
+ int cc_payload_idx; /* index in cc_payload */
+ u8 cc_missing_cnt; /* counts number of frames without CC for passthrough mode */
+ int wss_payload; /* sliced VBI WSS payload */
+ u8 wss_missing_cnt; /* counts number of frames without WSS for passthrough mode */
+ struct vbi_vps vps_payload; /* sliced VBI VPS payload */
+
+ /* Sliced VBI capture data */
+
+ struct v4l2_sliced_vbi_data sliced_data[36]; /* sliced VBI storage for VBI encoder stream */
+ struct v4l2_sliced_vbi_data sliced_dec_data[36];/* sliced VBI storage for VBI decoder stream */
+
+ /* VBI Embedding data */
+
+ /* Buffer for VBI data inserted into MPEG stream.
+ The first byte is a dummy byte that's never used.
+ The next 16 bytes contain the MPEG header for the VBI data,
+ the remainder is the actual VBI data.
+ The max size accepted by the MPEG VBI reinsertion turns out
+ to be 1552 bytes, which happens to be 4 + (1 + 42) * (2 * 18) bytes,
+ where 4 is a four byte header, 42 is the max sliced VBI payload, 1 is
+ a single line header byte and 2 * 18 is the number of VBI lines per frame.
+
+ However, it seems that the data must be 1K aligned, so we have to
+ pad the data until the 1 or 2 K boundary.
+
+ This pointer array will allocate 2049 bytes to store each VBI frame. */
+ u8 *sliced_mpeg_data[IVTV_VBI_FRAMES];
+ u32 sliced_mpeg_size[IVTV_VBI_FRAMES];
+ struct ivtv_buffer sliced_mpeg_buf; /* temporary buffer holding data from sliced_mpeg_data */
+ u32 inserted_frame; /* index in sliced_mpeg_size of next sliced data
+ to be inserted in the MPEG stream */
+};
+
+/* forward declaration of struct defined in ivtv-cards.h */
+struct ivtv_card;
+
+/* Struct to hold info about ivtv cards */
+struct ivtv {
+ /* General fixed card data */
+ struct pci_dev *pdev; /* PCI device */
+ const struct ivtv_card *card; /* card information */
+ const char *card_name; /* full name of the card */
+ const struct ivtv_card_tuner_i2c *card_i2c; /* i2c addresses to probe for tuner */
+ u8 has_cx23415; /* 1 if it is a cx23415 based card, 0 for cx23416 */
+ u8 pvr150_workaround; /* 1 if the cx25840 needs to workaround a PVR150 bug */
+ u8 nof_inputs; /* number of video inputs */
+ u8 nof_audio_inputs; /* number of audio inputs */
+ u32 v4l2_cap; /* V4L2 capabilities of card */
+ u32 hw_flags; /* hardware description of the board */
+ v4l2_std_id tuner_std; /* the norm of the card's tuner (fixed) */
+ struct v4l2_subdev *sd_video; /* controlling video decoder subdev */
+ struct v4l2_subdev *sd_audio; /* controlling audio subdev */
+ struct v4l2_subdev *sd_muxer; /* controlling audio muxer subdev */
+ u32 base_addr; /* PCI resource base address */
+ volatile void __iomem *enc_mem; /* pointer to mapped encoder memory */
+ volatile void __iomem *dec_mem; /* pointer to mapped decoder memory */
+ volatile void __iomem *reg_mem; /* pointer to mapped registers */
+ struct ivtv_options options; /* user options */
+
+ struct v4l2_device v4l2_dev;
+ struct cx2341x_handler cxhdl;
+ struct {
+ /* PTS/Frame count control cluster */
+ struct v4l2_ctrl *ctrl_pts;
+ struct v4l2_ctrl *ctrl_frame;
+ };
+ struct {
+ /* Audio Playback control cluster */
+ struct v4l2_ctrl *ctrl_audio_playback;
+ struct v4l2_ctrl *ctrl_audio_multilingual_playback;
+ };
+ struct v4l2_ctrl_handler hdl_gpio;
+ struct v4l2_subdev sd_gpio; /* GPIO sub-device */
+ u16 instance;
+
+ /* High-level state info */
+ unsigned long i_flags; /* global ivtv flags */
+ u8 is_50hz; /* 1 if the current capture standard is 50 Hz */
+ u8 is_60hz /* 1 if the current capture standard is 60 Hz */;
+ u8 is_out_50hz /* 1 if the current TV output standard is 50 Hz */;
+ u8 is_out_60hz /* 1 if the current TV output standard is 60 Hz */;
+ int output_mode; /* decoder output mode: NONE, MPG, YUV, UDMA YUV, passthrough */
+ u32 audio_input; /* current audio input */
+ u32 active_input; /* current video input */
+ u32 active_output; /* current video output */
+ v4l2_std_id std; /* current capture TV standard */
+ v4l2_std_id std_out; /* current TV output standard */
+ u8 audio_stereo_mode; /* decoder setting how to handle stereo MPEG audio */
+ u8 audio_bilingual_mode; /* decoder setting how to handle bilingual MPEG audio */
+
+ /* Locking */
+ spinlock_t lock; /* lock access to this struct */
+ struct mutex serialize_lock; /* mutex used to serialize open/close/start/stop/ioctl operations */
+
+ /* Streams */
+ int stream_buf_size[IVTV_MAX_STREAMS]; /* stream buffer size */
+ struct ivtv_stream streams[IVTV_MAX_STREAMS]; /* stream data */
+ atomic_t capturing; /* count number of active capture streams */
+ atomic_t decoding; /* count number of active decoding streams */
+
+
+ /* Interrupts & DMA */
+ u32 irqmask; /* active interrupts */
+ u32 irq_rr_idx; /* round-robin stream index */
+ struct kthread_worker irq_worker; /* kthread worker for PIO/YUV/VBI actions */
+ struct task_struct *irq_worker_task; /* task for irq_worker */
+ struct kthread_work irq_work; /* kthread work entry */
+ spinlock_t dma_reg_lock; /* lock access to DMA engine registers */
+ int cur_dma_stream; /* index of current stream doing DMA (-1 if none) */
+ int cur_pio_stream; /* index of current stream doing PIO (-1 if none) */
+ u32 dma_data_req_offset; /* store offset in decoder memory of current DMA request */
+ u32 dma_data_req_size; /* store size of current DMA request */
+ int dma_retries; /* current DMA retry attempt */
+ struct ivtv_user_dma udma; /* user based DMA for OSD */
+ struct timer_list dma_timer; /* timer used to catch unfinished DMAs */
+ u32 last_vsync_field; /* last seen vsync field */
+ wait_queue_head_t dma_waitq; /* wake up when the current DMA is finished */
+ wait_queue_head_t eos_waitq; /* wake up when EOS arrives */
+ wait_queue_head_t event_waitq; /* wake up when the next decoder event arrives */
+ wait_queue_head_t vsync_waitq; /* wake up when the next decoder vsync arrives */
+
+
+ /* Mailbox */
+ struct ivtv_mailbox_data enc_mbox; /* encoder mailboxes */
+ struct ivtv_mailbox_data dec_mbox; /* decoder mailboxes */
+ struct ivtv_api_cache api_cache[256]; /* cached API commands */
+
+
+ /* I2C */
+ struct i2c_adapter i2c_adap;
+ struct i2c_algo_bit_data i2c_algo;
+ struct i2c_client i2c_client;
+ int i2c_state; /* i2c bit state */
+ struct mutex i2c_bus_lock; /* lock i2c bus */
+
+ struct IR_i2c_init_data ir_i2c_init_data;
+
+ /* Program Index information */
+ u32 pgm_info_offset; /* start of pgm info in encoder memory */
+ u32 pgm_info_num; /* number of elements in the pgm cyclic buffer in encoder memory */
+ u32 pgm_info_write_idx; /* last index written by the card that was transferred to pgm_info[] */
+ u32 pgm_info_read_idx; /* last index in pgm_info read by the application */
+ struct v4l2_enc_idx_entry pgm_info[IVTV_MAX_PGM_INDEX]; /* filled from the pgm cyclic buffer on the card */
+
+
+ /* Miscellaneous */
+ u32 open_id; /* incremented each time an open occurs, is >= 1 */
+ int search_pack_header; /* 1 if ivtv_copy_buf_to_user() is scanning for a pack header (0xba) */
+ int speed; /* current playback speed setting */
+ u8 speed_mute_audio; /* 1 if audio should be muted when fast forward */
+ u64 mpg_data_received; /* number of bytes received from the MPEG stream */
+ u64 vbi_data_inserted; /* number of VBI bytes inserted into the MPEG stream */
+ u32 last_dec_timing[3]; /* cache last retrieved pts/scr/frame values */
+ unsigned long dualwatch_jiffies;/* jiffies value of the previous dualwatch check */
+ u32 dualwatch_stereo_mode; /* current detected dualwatch stereo mode */
+
+
+ /* VBI state info */
+ struct vbi_info vbi; /* VBI-specific data */
+
+
+ /* YUV playback */
+ struct yuv_playback_info yuv_info; /* YUV playback data */
+
+
+ /* OSD support */
+ unsigned long osd_video_pbase;
+ int osd_global_alpha_state; /* 1 = global alpha is on */
+ int osd_local_alpha_state; /* 1 = local alpha is on */
+ int osd_chroma_key_state; /* 1 = chroma-keying is on */
+ u8 osd_global_alpha; /* current global alpha */
+ u32 osd_chroma_key; /* current chroma key */
+ struct v4l2_rect osd_rect; /* current OSD position and size */
+ struct v4l2_rect main_rect; /* current Main window position and size */
+ struct osd_info *osd_info; /* ivtvfb private OSD info */
+ void (*ivtvfb_restore)(struct ivtv *itv); /* Used for a warm start */
+};
+
+static inline struct ivtv *to_ivtv(struct v4l2_device *v4l2_dev)
+{
+ return container_of(v4l2_dev, struct ivtv, v4l2_dev);
+}
+
+/* Globals */
+extern int ivtv_first_minor;
+
+/*==============Prototypes==================*/
+
+/* Hardware/IRQ */
+void ivtv_set_irq_mask(struct ivtv *itv, u32 mask);
+void ivtv_clear_irq_mask(struct ivtv *itv, u32 mask);
+
+/* try to set output mode, return current mode. */
+int ivtv_set_output_mode(struct ivtv *itv, int mode);
+
+/* return current output stream based on current mode */
+struct ivtv_stream *ivtv_get_output_stream(struct ivtv *itv);
+
+/* Return non-zero if a signal is pending */
+int ivtv_msleep_timeout(unsigned int msecs, int intr);
+
+/* Wait on queue, returns -EINTR if interrupted */
+int ivtv_waitq(wait_queue_head_t *waitq);
+
+/* Read Hauppauge eeprom */
+struct tveeprom; /* forward reference */
+void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv);
+
+/* First-open initialization: load firmware, init cx25840, etc. */
+int ivtv_init_on_first_open(struct ivtv *itv);
+
+/* Test if the current VBI mode is raw (1) or sliced (0) */
+static inline int ivtv_raw_vbi(const struct ivtv *itv)
+{
+ return itv->vbi.in.type == V4L2_BUF_TYPE_VBI_CAPTURE;
+}
+
+/* This is a PCI post thing, where if the pci register is not read, then
+ the write doesn't always take effect right away. By reading back the
+ register any pending PCI writes will be performed (in order), and so
+ you can be sure that the writes are guaranteed to be done.
+
+ Rarely needed, only in some timing sensitive cases.
+ Apparently if this is not done some motherboards seem
+ to kill the firmware and get into the broken state until computer is
+ rebooted. */
+#define write_sync(val, reg) \
+ do { writel(val, reg); readl(reg); } while (0)
+
+#define read_reg(reg) readl(itv->reg_mem + (reg))
+#define write_reg(val, reg) writel(val, itv->reg_mem + (reg))
+#define write_reg_sync(val, reg) \
+ do { write_reg(val, reg); read_reg(reg); } while (0)
+
+#define read_enc(addr) readl(itv->enc_mem + (u32)(addr))
+#define write_enc(val, addr) writel(val, itv->enc_mem + (u32)(addr))
+#define write_enc_sync(val, addr) \
+ do { write_enc(val, addr); read_enc(addr); } while (0)
+
+#define read_dec(addr) readl(itv->dec_mem + (u32)(addr))
+#define write_dec(val, addr) writel(val, itv->dec_mem + (u32)(addr))
+#define write_dec_sync(val, addr) \
+ do { write_dec(val, addr); read_dec(addr); } while (0)
+
+/* Call the specified callback for all subdevs matching hw (if 0, then
+ match them all). Ignore any errors. */
+#define ivtv_call_hw(itv, hw, o, f, args...) \
+ do { \
+ struct v4l2_subdev *__sd; \
+ __v4l2_device_call_subdevs_p(&(itv)->v4l2_dev, __sd, \
+ !(hw) || (__sd->grp_id & (hw)), o, f , ##args); \
+ } while (0)
+
+#define ivtv_call_all(itv, o, f, args...) ivtv_call_hw(itv, 0, o, f , ##args)
+
+/* Call the specified callback for all subdevs matching hw (if 0, then
+ match them all). If the callback returns an error other than 0 or
+ -ENOIOCTLCMD, then return with that error code. */
+#define ivtv_call_hw_err(itv, hw, o, f, args...) \
+({ \
+ struct v4l2_subdev *__sd; \
+ __v4l2_device_call_subdevs_until_err_p(&(itv)->v4l2_dev, __sd, \
+ !(hw) || (__sd->grp_id & (hw)), o, f , ##args); \
+})
+
+#define ivtv_call_all_err(itv, o, f, args...) ivtv_call_hw_err(itv, 0, o, f , ##args)
+
+#endif
diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c
new file mode 100644
index 00000000..c9663e88
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-fileops.c
@@ -0,0 +1,1036 @@
+/*
+ file operation functions
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-fileops.h"
+#include "ivtv-i2c.h"
+#include "ivtv-queue.h"
+#include "ivtv-udma.h"
+#include "ivtv-irq.h"
+#include "ivtv-vbi.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-routing.h"
+#include "ivtv-streams.h"
+#include "ivtv-yuv.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-cards.h"
+#include "ivtv-firmware.h"
+#include <media/v4l2-event.h>
+#include <media/saa7115.h>
+
+/* This function tries to claim the stream for a specific file descriptor.
+ If no one else is using this stream then the stream is claimed and
+ associated VBI streams are also automatically claimed.
+ Possible error returns: -EBUSY if someone else has claimed
+ the stream or 0 on success. */
+static int ivtv_claim_stream(struct ivtv_open_id *id, int type)
+{
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[type];
+ struct ivtv_stream *s_vbi;
+ int vbi_type;
+
+ if (test_and_set_bit(IVTV_F_S_CLAIMED, &s->s_flags)) {
+ /* someone already claimed this stream */
+ if (s->fh == &id->fh) {
+ /* yes, this file descriptor did. So that's OK. */
+ return 0;
+ }
+ if (s->fh == NULL && (type == IVTV_DEC_STREAM_TYPE_VBI ||
+ type == IVTV_ENC_STREAM_TYPE_VBI)) {
+ /* VBI is handled already internally, now also assign
+ the file descriptor to this stream for external
+ reading of the stream. */
+ s->fh = &id->fh;
+ IVTV_DEBUG_INFO("Start Read VBI\n");
+ return 0;
+ }
+ /* someone else is using this stream already */
+ IVTV_DEBUG_INFO("Stream %d is busy\n", type);
+ return -EBUSY;
+ }
+ s->fh = &id->fh;
+ if (type == IVTV_DEC_STREAM_TYPE_VBI) {
+ /* Enable reinsertion interrupt */
+ ivtv_clear_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
+ }
+
+ /* IVTV_DEC_STREAM_TYPE_MPG needs to claim IVTV_DEC_STREAM_TYPE_VBI,
+ IVTV_ENC_STREAM_TYPE_MPG needs to claim IVTV_ENC_STREAM_TYPE_VBI
+ (provided VBI insertion is on and sliced VBI is selected), for all
+ other streams we're done */
+ if (type == IVTV_DEC_STREAM_TYPE_MPG) {
+ vbi_type = IVTV_DEC_STREAM_TYPE_VBI;
+ } else if (type == IVTV_ENC_STREAM_TYPE_MPG &&
+ itv->vbi.insert_mpeg && !ivtv_raw_vbi(itv)) {
+ vbi_type = IVTV_ENC_STREAM_TYPE_VBI;
+ } else {
+ return 0;
+ }
+ s_vbi = &itv->streams[vbi_type];
+
+ if (!test_and_set_bit(IVTV_F_S_CLAIMED, &s_vbi->s_flags)) {
+ /* Enable reinsertion interrupt */
+ if (vbi_type == IVTV_DEC_STREAM_TYPE_VBI)
+ ivtv_clear_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
+ }
+ /* mark that it is used internally */
+ set_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags);
+ return 0;
+}
+
+/* This function releases a previously claimed stream. It will take into
+ account associated VBI streams. */
+void ivtv_release_stream(struct ivtv_stream *s)
+{
+ struct ivtv *itv = s->itv;
+ struct ivtv_stream *s_vbi;
+
+ s->fh = NULL;
+ if ((s->type == IVTV_DEC_STREAM_TYPE_VBI || s->type == IVTV_ENC_STREAM_TYPE_VBI) &&
+ test_bit(IVTV_F_S_INTERNAL_USE, &s->s_flags)) {
+ /* this stream is still in use internally */
+ return;
+ }
+ if (!test_and_clear_bit(IVTV_F_S_CLAIMED, &s->s_flags)) {
+ IVTV_DEBUG_WARN("Release stream %s not in use!\n", s->name);
+ return;
+ }
+
+ ivtv_flush_queues(s);
+
+ /* disable reinsertion interrupt */
+ if (s->type == IVTV_DEC_STREAM_TYPE_VBI)
+ ivtv_set_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
+
+ /* IVTV_DEC_STREAM_TYPE_MPG needs to release IVTV_DEC_STREAM_TYPE_VBI,
+ IVTV_ENC_STREAM_TYPE_MPG needs to release IVTV_ENC_STREAM_TYPE_VBI,
+ for all other streams we're done */
+ if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
+ s_vbi = &itv->streams[IVTV_DEC_STREAM_TYPE_VBI];
+ else if (s->type == IVTV_ENC_STREAM_TYPE_MPG)
+ s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+ else
+ return;
+
+ /* clear internal use flag */
+ if (!test_and_clear_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags)) {
+ /* was already cleared */
+ return;
+ }
+ if (s_vbi->fh) {
+ /* VBI stream still claimed by a file descriptor */
+ return;
+ }
+ /* disable reinsertion interrupt */
+ if (s_vbi->type == IVTV_DEC_STREAM_TYPE_VBI)
+ ivtv_set_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
+ clear_bit(IVTV_F_S_CLAIMED, &s_vbi->s_flags);
+ ivtv_flush_queues(s_vbi);
+}
+
+static void ivtv_dualwatch(struct ivtv *itv)
+{
+ struct v4l2_tuner vt;
+ u32 new_stereo_mode;
+ const u32 dual = 0x02;
+
+ new_stereo_mode = v4l2_ctrl_g_ctrl(itv->cxhdl.audio_mode);
+ memset(&vt, 0, sizeof(vt));
+ ivtv_call_all(itv, tuner, g_tuner, &vt);
+ if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans & V4L2_TUNER_SUB_LANG2))
+ new_stereo_mode = dual;
+
+ if (new_stereo_mode == itv->dualwatch_stereo_mode)
+ return;
+
+ IVTV_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x.\n",
+ itv->dualwatch_stereo_mode, new_stereo_mode);
+ if (v4l2_ctrl_s_ctrl(itv->cxhdl.audio_mode, new_stereo_mode))
+ IVTV_DEBUG_INFO("dualwatch: changing stereo flag failed\n");
+}
+
+static void ivtv_update_pgm_info(struct ivtv *itv)
+{
+ u32 wr_idx = (read_enc(itv->pgm_info_offset) - itv->pgm_info_offset - 4) / 24;
+ int cnt;
+ int i = 0;
+
+ if (wr_idx >= itv->pgm_info_num) {
+ IVTV_DEBUG_WARN("Invalid PGM index %d (>= %d)\n", wr_idx, itv->pgm_info_num);
+ return;
+ }
+ cnt = (wr_idx + itv->pgm_info_num - itv->pgm_info_write_idx) % itv->pgm_info_num;
+ while (i < cnt) {
+ int idx = (itv->pgm_info_write_idx + i) % itv->pgm_info_num;
+ struct v4l2_enc_idx_entry *e = itv->pgm_info + idx;
+ u32 addr = itv->pgm_info_offset + 4 + idx * 24;
+ const int mapping[8] = { -1, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_P, -1,
+ V4L2_ENC_IDX_FRAME_B, -1, -1, -1 };
+ // 1=I, 2=P, 4=B
+
+ e->offset = read_enc(addr + 4) + ((u64)read_enc(addr + 8) << 32);
+ if (e->offset > itv->mpg_data_received) {
+ break;
+ }
+ e->offset += itv->vbi_data_inserted;
+ e->length = read_enc(addr);
+ e->pts = read_enc(addr + 16) + ((u64)(read_enc(addr + 20) & 1) << 32);
+ e->flags = mapping[read_enc(addr + 12) & 7];
+ i++;
+ }
+ itv->pgm_info_write_idx = (itv->pgm_info_write_idx + i) % itv->pgm_info_num;
+}
+
+static struct ivtv_buffer *ivtv_get_buffer(struct ivtv_stream *s, int non_block, int *err)
+{
+ struct ivtv *itv = s->itv;
+ struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+ struct ivtv_buffer *buf;
+ DEFINE_WAIT(wait);
+
+ *err = 0;
+ while (1) {
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG) {
+ /* Process pending program info updates and pending VBI data */
+ ivtv_update_pgm_info(itv);
+
+ if (time_after(jiffies,
+ itv->dualwatch_jiffies +
+ msecs_to_jiffies(1000))) {
+ itv->dualwatch_jiffies = jiffies;
+ ivtv_dualwatch(itv);
+ }
+
+ if (test_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
+ !test_bit(IVTV_F_S_APPL_IO, &s_vbi->s_flags)) {
+ while ((buf = ivtv_dequeue(s_vbi, &s_vbi->q_full))) {
+ /* byteswap and process VBI data */
+ ivtv_process_vbi_data(itv, buf, s_vbi->dma_pts, s_vbi->type);
+ ivtv_enqueue(s_vbi, buf, &s_vbi->q_free);
+ }
+ }
+ buf = &itv->vbi.sliced_mpeg_buf;
+ if (buf->readpos != buf->bytesused) {
+ return buf;
+ }
+ }
+
+ /* do we have leftover data? */
+ buf = ivtv_dequeue(s, &s->q_io);
+ if (buf)
+ return buf;
+
+ /* do we have new data? */
+ buf = ivtv_dequeue(s, &s->q_full);
+ if (buf) {
+ if ((buf->b_flags & IVTV_F_B_NEED_BUF_SWAP) == 0)
+ return buf;
+ buf->b_flags &= ~IVTV_F_B_NEED_BUF_SWAP;
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG)
+ /* byteswap MPG data */
+ ivtv_buf_swap(buf);
+ else if (s->type != IVTV_DEC_STREAM_TYPE_VBI) {
+ /* byteswap and process VBI data */
+ ivtv_process_vbi_data(itv, buf, s->dma_pts, s->type);
+ }
+ return buf;
+ }
+
+ /* return if end of stream */
+ if (s->type != IVTV_DEC_STREAM_TYPE_VBI && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+ IVTV_DEBUG_INFO("EOS %s\n", s->name);
+ return NULL;
+ }
+
+ /* return if file was opened with O_NONBLOCK */
+ if (non_block) {
+ *err = -EAGAIN;
+ return NULL;
+ }
+
+ /* wait for more data to arrive */
+ mutex_unlock(&itv->serialize_lock);
+ prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE);
+ /* New buffers might have become available before we were added to the waitqueue */
+ if (!s->q_full.buffers)
+ schedule();
+ finish_wait(&s->waitq, &wait);
+ mutex_lock(&itv->serialize_lock);
+ if (signal_pending(current)) {
+ /* return if a signal was received */
+ IVTV_DEBUG_INFO("User stopped %s\n", s->name);
+ *err = -EINTR;
+ return NULL;
+ }
+ }
+}
+
+static void ivtv_setup_sliced_vbi_buf(struct ivtv *itv)
+{
+ int idx = itv->vbi.inserted_frame % IVTV_VBI_FRAMES;
+
+ itv->vbi.sliced_mpeg_buf.buf = itv->vbi.sliced_mpeg_data[idx];
+ itv->vbi.sliced_mpeg_buf.bytesused = itv->vbi.sliced_mpeg_size[idx];
+ itv->vbi.sliced_mpeg_buf.readpos = 0;
+}
+
+static size_t ivtv_copy_buf_to_user(struct ivtv_stream *s, struct ivtv_buffer *buf,
+ char __user *ubuf, size_t ucount)
+{
+ struct ivtv *itv = s->itv;
+ size_t len = buf->bytesused - buf->readpos;
+
+ if (len > ucount) len = ucount;
+ if (itv->vbi.insert_mpeg && s->type == IVTV_ENC_STREAM_TYPE_MPG &&
+ !ivtv_raw_vbi(itv) && buf != &itv->vbi.sliced_mpeg_buf) {
+ const char *start = buf->buf + buf->readpos;
+ const char *p = start + 1;
+ const u8 *q;
+ u8 ch = itv->search_pack_header ? 0xba : 0xe0;
+ int stuffing, i;
+
+ while (start + len > p && (q = memchr(p, 0, start + len - p))) {
+ p = q + 1;
+ if ((char *)q + 15 >= buf->buf + buf->bytesused ||
+ q[1] != 0 || q[2] != 1 || q[3] != ch) {
+ continue;
+ }
+ if (!itv->search_pack_header) {
+ if ((q[6] & 0xc0) != 0x80)
+ continue;
+ if (((q[7] & 0xc0) == 0x80 && (q[9] & 0xf0) == 0x20) ||
+ ((q[7] & 0xc0) == 0xc0 && (q[9] & 0xf0) == 0x30)) {
+ ch = 0xba;
+ itv->search_pack_header = 1;
+ p = q + 9;
+ }
+ continue;
+ }
+ stuffing = q[13] & 7;
+ /* all stuffing bytes must be 0xff */
+ for (i = 0; i < stuffing; i++)
+ if (q[14 + i] != 0xff)
+ break;
+ if (i == stuffing && (q[4] & 0xc4) == 0x44 && (q[12] & 3) == 3 &&
+ q[14 + stuffing] == 0 && q[15 + stuffing] == 0 &&
+ q[16 + stuffing] == 1) {
+ itv->search_pack_header = 0;
+ len = (char *)q - start;
+ ivtv_setup_sliced_vbi_buf(itv);
+ break;
+ }
+ }
+ }
+ if (copy_to_user(ubuf, (u8 *)buf->buf + buf->readpos, len)) {
+ IVTV_DEBUG_WARN("copy %zd bytes to user failed for %s\n", len, s->name);
+ return -EFAULT;
+ }
+ /*IVTV_INFO("copied %lld %d %d %d %d %d vbi %d\n", itv->mpg_data_received, len, ucount,
+ buf->readpos, buf->bytesused, buf->bytesused - buf->readpos - len,
+ buf == &itv->vbi.sliced_mpeg_buf); */
+ buf->readpos += len;
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG && buf != &itv->vbi.sliced_mpeg_buf)
+ itv->mpg_data_received += len;
+ return len;
+}
+
+static ssize_t ivtv_read(struct ivtv_stream *s, char __user *ubuf, size_t tot_count, int non_block)
+{
+ struct ivtv *itv = s->itv;
+ size_t tot_written = 0;
+ int single_frame = 0;
+
+ if (atomic_read(&itv->capturing) == 0 && s->fh == NULL) {
+ /* shouldn't happen */
+ IVTV_DEBUG_WARN("Stream %s not initialized before read\n", s->name);
+ return -EIO;
+ }
+
+ /* Each VBI buffer is one frame, the v4l2 API says that for VBI the frames should
+ arrive one-by-one, so make sure we never output more than one VBI frame at a time */
+ if (s->type == IVTV_DEC_STREAM_TYPE_VBI ||
+ (s->type == IVTV_ENC_STREAM_TYPE_VBI && !ivtv_raw_vbi(itv)))
+ single_frame = 1;
+
+ for (;;) {
+ struct ivtv_buffer *buf;
+ int rc;
+
+ buf = ivtv_get_buffer(s, non_block, &rc);
+ /* if there is no data available... */
+ if (buf == NULL) {
+ /* if we got data, then return that regardless */
+ if (tot_written)
+ break;
+ /* EOS condition */
+ if (rc == 0) {
+ clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+ clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+ ivtv_release_stream(s);
+ }
+ /* set errno */
+ return rc;
+ }
+ rc = ivtv_copy_buf_to_user(s, buf, ubuf + tot_written, tot_count - tot_written);
+ if (buf != &itv->vbi.sliced_mpeg_buf) {
+ ivtv_enqueue(s, buf, (buf->readpos == buf->bytesused) ? &s->q_free : &s->q_io);
+ }
+ else if (buf->readpos == buf->bytesused) {
+ int idx = itv->vbi.inserted_frame % IVTV_VBI_FRAMES;
+ itv->vbi.sliced_mpeg_size[idx] = 0;
+ itv->vbi.inserted_frame++;
+ itv->vbi_data_inserted += buf->bytesused;
+ }
+ if (rc < 0)
+ return rc;
+ tot_written += rc;
+
+ if (tot_written == tot_count || single_frame)
+ break;
+ }
+ return tot_written;
+}
+
+static ssize_t ivtv_read_pos(struct ivtv_stream *s, char __user *ubuf, size_t count,
+ loff_t *pos, int non_block)
+{
+ ssize_t rc = count ? ivtv_read(s, ubuf, count, non_block) : 0;
+ struct ivtv *itv = s->itv;
+
+ IVTV_DEBUG_HI_FILE("read %zd from %s, got %zd\n", count, s->name, rc);
+ if (rc > 0)
+ pos += rc;
+ return rc;
+}
+
+int ivtv_start_capture(struct ivtv_open_id *id)
+{
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+ struct ivtv_stream *s_vbi;
+
+ if (s->type == IVTV_ENC_STREAM_TYPE_RAD ||
+ s->type == IVTV_DEC_STREAM_TYPE_MPG ||
+ s->type == IVTV_DEC_STREAM_TYPE_YUV ||
+ s->type == IVTV_DEC_STREAM_TYPE_VOUT) {
+ /* you cannot read from these stream types. */
+ return -EPERM;
+ }
+
+ /* Try to claim this stream. */
+ if (ivtv_claim_stream(id, s->type))
+ return -EBUSY;
+
+ /* This stream does not need to start capturing */
+ if (s->type == IVTV_DEC_STREAM_TYPE_VBI) {
+ set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+ return 0;
+ }
+
+ /* If capture is already in progress, then we also have to
+ do nothing extra. */
+ if (test_bit(IVTV_F_S_STREAMOFF, &s->s_flags) || test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+ set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+ return 0;
+ }
+
+ /* Start VBI capture if required */
+ s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG &&
+ test_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
+ !test_and_set_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags)) {
+ /* Note: the IVTV_ENC_STREAM_TYPE_VBI is claimed
+ automatically when the MPG stream is claimed.
+ We only need to start the VBI capturing. */
+ if (ivtv_start_v4l2_encode_stream(s_vbi)) {
+ IVTV_DEBUG_WARN("VBI capture start failed\n");
+
+ /* Failure, clean up and return an error */
+ clear_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags);
+ clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+ /* also releases the associated VBI stream */
+ ivtv_release_stream(s);
+ return -EIO;
+ }
+ IVTV_DEBUG_INFO("VBI insertion started\n");
+ }
+
+ /* Tell the card to start capturing */
+ if (!ivtv_start_v4l2_encode_stream(s)) {
+ /* We're done */
+ set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+ /* Resume a possibly paused encoder */
+ if (test_and_clear_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags))
+ ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 1);
+ return 0;
+ }
+
+ /* failure, clean up */
+ IVTV_DEBUG_WARN("Failed to start capturing for stream %s\n", s->name);
+
+ /* Note: the IVTV_ENC_STREAM_TYPE_VBI is released
+ automatically when the MPG stream is released.
+ We only need to stop the VBI capturing. */
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG &&
+ test_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags)) {
+ ivtv_stop_v4l2_encode_stream(s_vbi, 0);
+ clear_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags);
+ }
+ clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+ ivtv_release_stream(s);
+ return -EIO;
+}
+
+ssize_t ivtv_v4l2_read(struct file * filp, char __user *buf, size_t count, loff_t * pos)
+{
+ struct ivtv_open_id *id = fh2id(filp->private_data);
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+ int rc;
+
+ IVTV_DEBUG_HI_FILE("read %zd bytes from %s\n", count, s->name);
+
+ rc = ivtv_start_capture(id);
+ if (rc)
+ return rc;
+ return ivtv_read_pos(s, buf, count, pos, filp->f_flags & O_NONBLOCK);
+}
+
+int ivtv_start_decoding(struct ivtv_open_id *id, int speed)
+{
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+ int rc;
+
+ if (atomic_read(&itv->decoding) == 0) {
+ if (ivtv_claim_stream(id, s->type)) {
+ /* someone else is using this stream already */
+ IVTV_DEBUG_WARN("start decode, stream already claimed\n");
+ return -EBUSY;
+ }
+ rc = ivtv_start_v4l2_decode_stream(s, 0);
+ if (rc < 0) {
+ if (rc == -EAGAIN)
+ rc = ivtv_start_v4l2_decode_stream(s, 0);
+ if (rc < 0)
+ return rc;
+ }
+ }
+ if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
+ return ivtv_set_speed(itv, speed);
+ return 0;
+}
+
+ssize_t ivtv_v4l2_write(struct file *filp, const char __user *user_buf, size_t count, loff_t *pos)
+{
+ struct ivtv_open_id *id = fh2id(filp->private_data);
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ struct ivtv_buffer *buf;
+ struct ivtv_queue q;
+ int bytes_written = 0;
+ int mode;
+ int rc;
+ DEFINE_WAIT(wait);
+
+ IVTV_DEBUG_HI_FILE("write %zd bytes to %s\n", count, s->name);
+
+ if (s->type != IVTV_DEC_STREAM_TYPE_MPG &&
+ s->type != IVTV_DEC_STREAM_TYPE_YUV &&
+ s->type != IVTV_DEC_STREAM_TYPE_VOUT)
+ /* not decoder streams */
+ return -EPERM;
+
+ /* Try to claim this stream */
+ if (ivtv_claim_stream(id, s->type))
+ return -EBUSY;
+
+ /* This stream does not need to start any decoding */
+ if (s->type == IVTV_DEC_STREAM_TYPE_VOUT) {
+ int elems = count / sizeof(struct v4l2_sliced_vbi_data);
+
+ set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+ return ivtv_write_vbi_from_user(itv,
+ (const struct v4l2_sliced_vbi_data __user *)user_buf, elems);
+ }
+
+ mode = s->type == IVTV_DEC_STREAM_TYPE_MPG ? OUT_MPG : OUT_YUV;
+
+ if (ivtv_set_output_mode(itv, mode) != mode) {
+ ivtv_release_stream(s);
+ return -EBUSY;
+ }
+ ivtv_queue_init(&q);
+ set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+
+ /* Start decoder (returns 0 if already started) */
+ rc = ivtv_start_decoding(id, itv->speed);
+ if (rc) {
+ IVTV_DEBUG_WARN("Failed start decode stream %s\n", s->name);
+
+ /* failure, clean up */
+ clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+ clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+ return rc;
+ }
+
+retry:
+ /* If possible, just DMA the entire frame - Check the data transfer size
+ since we may get here before the stream has been fully set-up */
+ if (mode == OUT_YUV && s->q_full.length == 0 && itv->dma_data_req_size) {
+ while (count >= itv->dma_data_req_size) {
+ rc = ivtv_yuv_udma_stream_frame(itv, (void __user *)user_buf);
+
+ if (rc < 0)
+ return rc;
+
+ bytes_written += itv->dma_data_req_size;
+ user_buf += itv->dma_data_req_size;
+ count -= itv->dma_data_req_size;
+ }
+ if (count == 0) {
+ IVTV_DEBUG_HI_FILE("Wrote %d bytes to %s (%d)\n", bytes_written, s->name, s->q_full.bytesused);
+ return bytes_written;
+ }
+ }
+
+ for (;;) {
+ /* Gather buffers */
+ while (q.length - q.bytesused < count && (buf = ivtv_dequeue(s, &s->q_io)))
+ ivtv_enqueue(s, buf, &q);
+ while (q.length - q.bytesused < count && (buf = ivtv_dequeue(s, &s->q_free))) {
+ ivtv_enqueue(s, buf, &q);
+ }
+ if (q.buffers)
+ break;
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ mutex_unlock(&itv->serialize_lock);
+ prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE);
+ /* New buffers might have become free before we were added to the waitqueue */
+ if (!s->q_free.buffers)
+ schedule();
+ finish_wait(&s->waitq, &wait);
+ mutex_lock(&itv->serialize_lock);
+ if (signal_pending(current)) {
+ IVTV_DEBUG_INFO("User stopped %s\n", s->name);
+ return -EINTR;
+ }
+ }
+
+ /* copy user data into buffers */
+ while ((buf = ivtv_dequeue(s, &q))) {
+ /* yuv is a pain. Don't copy more data than needed for a single
+ frame, otherwise we lose sync with the incoming stream */
+ if (s->type == IVTV_DEC_STREAM_TYPE_YUV &&
+ yi->stream_size + count > itv->dma_data_req_size)
+ rc = ivtv_buf_copy_from_user(s, buf, user_buf,
+ itv->dma_data_req_size - yi->stream_size);
+ else
+ rc = ivtv_buf_copy_from_user(s, buf, user_buf, count);
+
+ /* Make sure we really got all the user data */
+ if (rc < 0) {
+ ivtv_queue_move(s, &q, NULL, &s->q_free, 0);
+ return rc;
+ }
+ user_buf += rc;
+ count -= rc;
+ bytes_written += rc;
+
+ if (s->type == IVTV_DEC_STREAM_TYPE_YUV) {
+ yi->stream_size += rc;
+ /* If we have a complete yuv frame, break loop now */
+ if (yi->stream_size == itv->dma_data_req_size) {
+ ivtv_enqueue(s, buf, &s->q_full);
+ yi->stream_size = 0;
+ break;
+ }
+ }
+
+ if (buf->bytesused != s->buf_size) {
+ /* incomplete, leave in q_io for next time */
+ ivtv_enqueue(s, buf, &s->q_io);
+ break;
+ }
+ /* Byteswap MPEG buffer */
+ if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
+ ivtv_buf_swap(buf);
+ ivtv_enqueue(s, buf, &s->q_full);
+ }
+
+ if (test_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags)) {
+ if (s->q_full.length >= itv->dma_data_req_size) {
+ int got_sig;
+
+ if (mode == OUT_YUV)
+ ivtv_yuv_setup_stream_frame(itv);
+
+ mutex_unlock(&itv->serialize_lock);
+ prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
+ while (!(got_sig = signal_pending(current)) &&
+ test_bit(IVTV_F_S_DMA_PENDING, &s->s_flags)) {
+ schedule();
+ }
+ finish_wait(&itv->dma_waitq, &wait);
+ mutex_lock(&itv->serialize_lock);
+ if (got_sig) {
+ IVTV_DEBUG_INFO("User interrupted %s\n", s->name);
+ return -EINTR;
+ }
+
+ clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
+ ivtv_queue_move(s, &s->q_full, NULL, &s->q_predma, itv->dma_data_req_size);
+ ivtv_dma_stream_dec_prepare(s, itv->dma_data_req_offset + IVTV_DECODER_OFFSET, 1);
+ }
+ }
+ /* more user data is available, wait until buffers become free
+ to transfer the rest. */
+ if (count && !(filp->f_flags & O_NONBLOCK))
+ goto retry;
+ IVTV_DEBUG_HI_FILE("Wrote %d bytes to %s (%d)\n", bytes_written, s->name, s->q_full.bytesused);
+ return bytes_written;
+}
+
+unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table *wait)
+{
+ struct ivtv_open_id *id = fh2id(filp->private_data);
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+ int res = 0;
+
+ /* add stream's waitq to the poll list */
+ IVTV_DEBUG_HI_FILE("Decoder poll\n");
+
+ /* If there are subscribed events, then only use the new event
+ API instead of the old video.h based API. */
+ if (!list_empty(&id->fh.subscribed)) {
+ poll_wait(filp, &id->fh.wait, wait);
+ /* Turn off the old-style vsync events */
+ clear_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags);
+ if (v4l2_event_pending(&id->fh))
+ res = POLLPRI;
+ } else {
+ /* This is the old-style API which is here only for backwards
+ compatibility. */
+ poll_wait(filp, &s->waitq, wait);
+ set_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags);
+ if (test_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags) ||
+ test_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags))
+ res = POLLPRI;
+ }
+
+ /* Allow write if buffers are available for writing */
+ if (s->q_free.buffers)
+ res |= POLLOUT | POLLWRNORM;
+ return res;
+}
+
+unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait)
+{
+ struct ivtv_open_id *id = fh2id(filp->private_data);
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+ int eof = test_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+ unsigned res = 0;
+
+ /* Start a capture if there is none */
+ if (!eof && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+ int rc;
+
+ rc = ivtv_start_capture(id);
+ if (rc) {
+ IVTV_DEBUG_INFO("Could not start capture for %s (%d)\n",
+ s->name, rc);
+ return POLLERR;
+ }
+ IVTV_DEBUG_FILE("Encoder poll started capture\n");
+ }
+
+ /* add stream's waitq to the poll list */
+ IVTV_DEBUG_HI_FILE("Encoder poll\n");
+ poll_wait(filp, &s->waitq, wait);
+ if (v4l2_event_pending(&id->fh))
+ res |= POLLPRI;
+ else
+ poll_wait(filp, &id->fh.wait, wait);
+
+ if (s->q_full.length || s->q_io.length)
+ return res | POLLIN | POLLRDNORM;
+ if (eof)
+ return res | POLLHUP;
+ return res;
+}
+
+void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end)
+{
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+
+ IVTV_DEBUG_FILE("close() of %s\n", s->name);
+
+ /* 'Unclaim' this stream */
+
+ /* Stop capturing */
+ if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+ struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+
+ IVTV_DEBUG_INFO("close stopping capture\n");
+ /* Special case: a running VBI capture for VBI insertion
+ in the mpeg stream. Need to stop that too. */
+ if (id->type == IVTV_ENC_STREAM_TYPE_MPG &&
+ test_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags) &&
+ !test_bit(IVTV_F_S_APPL_IO, &s_vbi->s_flags)) {
+ IVTV_DEBUG_INFO("close stopping embedded VBI capture\n");
+ ivtv_stop_v4l2_encode_stream(s_vbi, 0);
+ }
+ if ((id->type == IVTV_DEC_STREAM_TYPE_VBI ||
+ id->type == IVTV_ENC_STREAM_TYPE_VBI) &&
+ test_bit(IVTV_F_S_INTERNAL_USE, &s->s_flags)) {
+ /* Also used internally, don't stop capturing */
+ s->fh = NULL;
+ }
+ else {
+ ivtv_stop_v4l2_encode_stream(s, gop_end);
+ }
+ }
+ if (!gop_end) {
+ clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+ clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+ ivtv_release_stream(s);
+ }
+}
+
+static void ivtv_stop_decoding(struct ivtv_open_id *id, int flags, u64 pts)
+{
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+
+ IVTV_DEBUG_FILE("close() of %s\n", s->name);
+
+ if (id->type == IVTV_DEC_STREAM_TYPE_YUV &&
+ test_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags)) {
+ /* Restore registers we've changed & clean up any mess */
+ ivtv_yuv_close(itv);
+ }
+
+ /* Stop decoding */
+ if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+ IVTV_DEBUG_INFO("close stopping decode\n");
+
+ ivtv_stop_v4l2_decode_stream(s, flags, pts);
+ itv->output_mode = OUT_NONE;
+ }
+ clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+ clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+
+ if (itv->output_mode == OUT_UDMA_YUV && id->yuv_frames)
+ itv->output_mode = OUT_NONE;
+
+ itv->speed = 0;
+ clear_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags);
+ ivtv_release_stream(s);
+}
+
+int ivtv_v4l2_close(struct file *filp)
+{
+ struct v4l2_fh *fh = filp->private_data;
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+
+ IVTV_DEBUG_FILE("close %s\n", s->name);
+
+ /* Stop radio */
+ if (id->type == IVTV_ENC_STREAM_TYPE_RAD &&
+ v4l2_fh_is_singular_file(filp)) {
+ /* Closing radio device, return to TV mode */
+ ivtv_mute(itv);
+ /* Mark that the radio is no longer in use */
+ clear_bit(IVTV_F_I_RADIO_USER, &itv->i_flags);
+ /* Switch tuner to TV */
+ ivtv_call_all(itv, core, s_std, itv->std);
+ /* Select correct audio input (i.e. TV tuner or Line in) */
+ ivtv_audio_set_io(itv);
+ if (itv->hw_flags & IVTV_HW_SAA711X) {
+ ivtv_call_hw(itv, IVTV_HW_SAA711X, video, s_crystal_freq,
+ SAA7115_FREQ_32_11_MHZ, 0);
+ }
+ if (atomic_read(&itv->capturing) > 0) {
+ /* Undo video mute */
+ ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1,
+ v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute) |
+ (v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute_yuv) << 8));
+ }
+ /* Done! Unmute and continue. */
+ ivtv_unmute(itv);
+ }
+
+ v4l2_fh_del(fh);
+ v4l2_fh_exit(fh);
+
+ /* Easy case first: this stream was never claimed by us */
+ if (s->fh != &id->fh) {
+ kfree(id);
+ return 0;
+ }
+
+ /* 'Unclaim' this stream */
+
+ if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) {
+ struct ivtv_stream *s_vout = &itv->streams[IVTV_DEC_STREAM_TYPE_VOUT];
+
+ ivtv_stop_decoding(id, V4L2_DEC_CMD_STOP_TO_BLACK | V4L2_DEC_CMD_STOP_IMMEDIATELY, 0);
+
+ /* If all output streams are closed, and if the user doesn't have
+ IVTV_DEC_STREAM_TYPE_VOUT open, then disable CC on TV-out. */
+ if (itv->output_mode == OUT_NONE && !test_bit(IVTV_F_S_APPL_IO, &s_vout->s_flags)) {
+ /* disable CC on TV-out */
+ ivtv_disable_cc(itv);
+ }
+ } else {
+ ivtv_stop_capture(id, 0);
+ }
+ kfree(id);
+ return 0;
+}
+
+int ivtv_v4l2_open(struct file *filp)
+{
+ struct video_device *vdev = video_devdata(filp);
+ struct ivtv_stream *s = video_get_drvdata(vdev);
+ struct ivtv *itv = s->itv;
+ struct ivtv_open_id *item;
+ int res = 0;
+
+ IVTV_DEBUG_FILE("open %s\n", s->name);
+
+ if (ivtv_init_on_first_open(itv)) {
+ IVTV_ERR("Failed to initialize on device %s\n",
+ video_device_node_name(vdev));
+ return -ENXIO;
+ }
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ /* Unless ivtv_fw_debug is set, error out if firmware dead. */
+ if (ivtv_fw_debug) {
+ IVTV_WARN("Opening %s with dead firmware lockout disabled\n",
+ video_device_node_name(vdev));
+ IVTV_WARN("Selected firmware errors will be ignored\n");
+ } else {
+#else
+ if (1) {
+#endif
+ res = ivtv_firmware_check(itv, "ivtv_serialized_open");
+ if (res == -EAGAIN)
+ res = ivtv_firmware_check(itv, "ivtv_serialized_open");
+ if (res < 0)
+ return -EIO;
+ }
+
+ if (s->type == IVTV_DEC_STREAM_TYPE_MPG &&
+ test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_YUV].s_flags))
+ return -EBUSY;
+
+ if (s->type == IVTV_DEC_STREAM_TYPE_YUV &&
+ test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_MPG].s_flags))
+ return -EBUSY;
+
+ if (s->type == IVTV_DEC_STREAM_TYPE_YUV) {
+ if (read_reg(0x82c) == 0) {
+ IVTV_ERR("Tried to open YUV output device but need to send data to mpeg decoder before it can be used\n");
+ /* return -ENODEV; */
+ }
+ ivtv_udma_alloc(itv);
+ }
+
+ /* Allocate memory */
+ item = kzalloc(sizeof(struct ivtv_open_id), GFP_KERNEL);
+ if (NULL == item) {
+ IVTV_DEBUG_WARN("nomem on v4l2 open\n");
+ return -ENOMEM;
+ }
+ v4l2_fh_init(&item->fh, s->vdev);
+ item->itv = itv;
+ item->type = s->type;
+
+ filp->private_data = &item->fh;
+ v4l2_fh_add(&item->fh);
+
+ if (item->type == IVTV_ENC_STREAM_TYPE_RAD &&
+ v4l2_fh_is_singular_file(filp)) {
+ if (!test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) {
+ if (atomic_read(&itv->capturing) > 0) {
+ /* switching to radio while capture is
+ in progress is not polite */
+ v4l2_fh_del(&item->fh);
+ v4l2_fh_exit(&item->fh);
+ kfree(item);
+ return -EBUSY;
+ }
+ }
+ /* Mark that the radio is being used. */
+ set_bit(IVTV_F_I_RADIO_USER, &itv->i_flags);
+ /* We have the radio */
+ ivtv_mute(itv);
+ /* Switch tuner to radio */
+ ivtv_call_all(itv, tuner, s_radio);
+ /* Select the correct audio input (i.e. radio tuner) */
+ ivtv_audio_set_io(itv);
+ if (itv->hw_flags & IVTV_HW_SAA711X) {
+ ivtv_call_hw(itv, IVTV_HW_SAA711X, video, s_crystal_freq,
+ SAA7115_FREQ_32_11_MHZ, SAA7115_FREQ_FL_APLL);
+ }
+ /* Done! Unmute and continue. */
+ ivtv_unmute(itv);
+ }
+
+ /* YUV or MPG Decoding Mode? */
+ if (s->type == IVTV_DEC_STREAM_TYPE_MPG) {
+ clear_bit(IVTV_F_I_DEC_YUV, &itv->i_flags);
+ } else if (s->type == IVTV_DEC_STREAM_TYPE_YUV) {
+ set_bit(IVTV_F_I_DEC_YUV, &itv->i_flags);
+ /* For yuv, we need to know the dma size before we start */
+ itv->dma_data_req_size =
+ 1080 * ((itv->yuv_info.v4l2_src_h + 31) & ~31);
+ itv->yuv_info.stream_size = 0;
+ }
+ return 0;
+}
+
+void ivtv_mute(struct ivtv *itv)
+{
+ if (atomic_read(&itv->capturing))
+ ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 1);
+ IVTV_DEBUG_INFO("Mute\n");
+}
+
+void ivtv_unmute(struct ivtv *itv)
+{
+ if (atomic_read(&itv->capturing)) {
+ ivtv_msleep_timeout(100, 0);
+ ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12);
+ ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 0);
+ }
+ IVTV_DEBUG_INFO("Unmute\n");
+}
diff --git a/drivers/media/video/ivtv/ivtv-fileops.h b/drivers/media/video/ivtv/ivtv-fileops.h
new file mode 100644
index 00000000..049a2923
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-fileops.h
@@ -0,0 +1,44 @@
+/*
+ file operation functions
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef IVTV_FILEOPS_H
+#define IVTV_FILEOPS_H
+
+/* Testing/Debugging */
+int ivtv_v4l2_open(struct file *filp);
+ssize_t ivtv_v4l2_read(struct file *filp, char __user *buf, size_t count,
+ loff_t * pos);
+ssize_t ivtv_v4l2_write(struct file *filp, const char __user *buf, size_t count,
+ loff_t * pos);
+int ivtv_v4l2_close(struct file *filp);
+unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait);
+unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table * wait);
+int ivtv_start_capture(struct ivtv_open_id *id);
+void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end);
+int ivtv_start_decoding(struct ivtv_open_id *id, int speed);
+void ivtv_mute(struct ivtv *itv);
+void ivtv_unmute(struct ivtv *itv);
+
+/* Utilities */
+
+/* Release a previously claimed stream. */
+void ivtv_release_stream(struct ivtv_stream *s);
+
+#endif
diff --git a/drivers/media/video/ivtv/ivtv-firmware.c b/drivers/media/video/ivtv/ivtv-firmware.c
new file mode 100644
index 00000000..02c5adeb
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-firmware.c
@@ -0,0 +1,398 @@
+/*
+ ivtv firmware functions.
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-firmware.h"
+#include "ivtv-yuv.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-cards.h"
+#include <linux/firmware.h>
+#include <media/saa7127.h>
+
+#define IVTV_MASK_SPU_ENABLE 0xFFFFFFFE
+#define IVTV_MASK_VPU_ENABLE15 0xFFFFFFF6
+#define IVTV_MASK_VPU_ENABLE16 0xFFFFFFFB
+#define IVTV_CMD_VDM_STOP 0x00000000
+#define IVTV_CMD_AO_STOP 0x00000005
+#define IVTV_CMD_APU_PING 0x00000000
+#define IVTV_CMD_VPU_STOP15 0xFFFFFFFE
+#define IVTV_CMD_VPU_STOP16 0xFFFFFFEE
+#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF
+#define IVTV_CMD_SPU_STOP 0x00000001
+#define IVTV_CMD_SDRAM_PRECHARGE_INIT 0x0000001A
+#define IVTV_CMD_SDRAM_REFRESH_INIT 0x80000640
+#define IVTV_SDRAM_SLEEPTIME 600
+
+#define IVTV_DECODE_INIT_MPEG_FILENAME "v4l-cx2341x-init.mpg"
+#define IVTV_DECODE_INIT_MPEG_SIZE (152*1024)
+
+/* Encoder/decoder firmware sizes */
+#define IVTV_FW_ENC_SIZE (376836)
+#define IVTV_FW_DEC_SIZE (256*1024)
+
+static int load_fw_direct(const char *fn, volatile u8 __iomem *mem, struct ivtv *itv, long size)
+{
+ const struct firmware *fw = NULL;
+ int retries = 3;
+
+retry:
+ if (retries && request_firmware(&fw, fn, &itv->pdev->dev) == 0) {
+ int i;
+ volatile u32 __iomem *dst = (volatile u32 __iomem *)mem;
+ const u32 *src = (const u32 *)fw->data;
+
+ if (fw->size != size) {
+ /* Due to race conditions in firmware loading (esp. with udev <0.95)
+ the wrong file was sometimes loaded. So we check filesizes to
+ see if at least the right-sized file was loaded. If not, then we
+ retry. */
+ IVTV_INFO("Retry: file loaded was not %s (expected size %ld, got %zd)\n", fn, size, fw->size);
+ release_firmware(fw);
+ retries--;
+ goto retry;
+ }
+ for (i = 0; i < fw->size; i += 4) {
+ /* no need for endianness conversion on the ppc */
+ __raw_writel(*src, dst);
+ dst++;
+ src++;
+ }
+ IVTV_INFO("Loaded %s firmware (%zd bytes)\n", fn, fw->size);
+ release_firmware(fw);
+ return size;
+ }
+ IVTV_ERR("Unable to open firmware %s (must be %ld bytes)\n", fn, size);
+ IVTV_ERR("Did you put the firmware in the hotplug firmware directory?\n");
+ return -ENOMEM;
+}
+
+void ivtv_halt_firmware(struct ivtv *itv)
+{
+ IVTV_DEBUG_INFO("Preparing for firmware halt.\n");
+ if (itv->has_cx23415 && itv->dec_mbox.mbox)
+ ivtv_vapi(itv, CX2341X_DEC_HALT_FW, 0);
+ if (itv->enc_mbox.mbox)
+ ivtv_vapi(itv, CX2341X_ENC_HALT_FW, 0);
+
+ ivtv_msleep_timeout(10, 0);
+ itv->enc_mbox.mbox = itv->dec_mbox.mbox = NULL;
+
+ IVTV_DEBUG_INFO("Stopping VDM\n");
+ write_reg(IVTV_CMD_VDM_STOP, IVTV_REG_VDM);
+
+ IVTV_DEBUG_INFO("Stopping AO\n");
+ write_reg(IVTV_CMD_AO_STOP, IVTV_REG_AO);
+
+ IVTV_DEBUG_INFO("pinging (?) APU\n");
+ write_reg(IVTV_CMD_APU_PING, IVTV_REG_APU);
+
+ IVTV_DEBUG_INFO("Stopping VPU\n");
+ if (!itv->has_cx23415)
+ write_reg(IVTV_CMD_VPU_STOP16, IVTV_REG_VPU);
+ else
+ write_reg(IVTV_CMD_VPU_STOP15, IVTV_REG_VPU);
+
+ IVTV_DEBUG_INFO("Resetting Hw Blocks\n");
+ write_reg(IVTV_CMD_HW_BLOCKS_RST, IVTV_REG_HW_BLOCKS);
+
+ IVTV_DEBUG_INFO("Stopping SPU\n");
+ write_reg(IVTV_CMD_SPU_STOP, IVTV_REG_SPU);
+
+ ivtv_msleep_timeout(10, 0);
+
+ IVTV_DEBUG_INFO("init Encoder SDRAM pre-charge\n");
+ write_reg(IVTV_CMD_SDRAM_PRECHARGE_INIT, IVTV_REG_ENC_SDRAM_PRECHARGE);
+
+ IVTV_DEBUG_INFO("init Encoder SDRAM refresh to 1us\n");
+ write_reg(IVTV_CMD_SDRAM_REFRESH_INIT, IVTV_REG_ENC_SDRAM_REFRESH);
+
+ if (itv->has_cx23415) {
+ IVTV_DEBUG_INFO("init Decoder SDRAM pre-charge\n");
+ write_reg(IVTV_CMD_SDRAM_PRECHARGE_INIT, IVTV_REG_DEC_SDRAM_PRECHARGE);
+
+ IVTV_DEBUG_INFO("init Decoder SDRAM refresh to 1us\n");
+ write_reg(IVTV_CMD_SDRAM_REFRESH_INIT, IVTV_REG_DEC_SDRAM_REFRESH);
+ }
+
+ IVTV_DEBUG_INFO("Sleeping for %dms\n", IVTV_SDRAM_SLEEPTIME);
+ ivtv_msleep_timeout(IVTV_SDRAM_SLEEPTIME, 0);
+}
+
+void ivtv_firmware_versions(struct ivtv *itv)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+
+ /* Encoder */
+ ivtv_vapi_result(itv, data, CX2341X_ENC_GET_VERSION, 0);
+ IVTV_INFO("Encoder revision: 0x%08x\n", data[0]);
+
+ if (data[0] != 0x02060039)
+ IVTV_WARN("Recommended firmware version is 0x02060039.\n");
+
+ if (itv->has_cx23415) {
+ /* Decoder */
+ ivtv_vapi_result(itv, data, CX2341X_DEC_GET_VERSION, 0);
+ IVTV_INFO("Decoder revision: 0x%08x\n", data[0]);
+ }
+}
+
+static int ivtv_firmware_copy(struct ivtv *itv)
+{
+ IVTV_DEBUG_INFO("Loading encoder image\n");
+ if (load_fw_direct(CX2341X_FIRM_ENC_FILENAME,
+ itv->enc_mem, itv, IVTV_FW_ENC_SIZE) != IVTV_FW_ENC_SIZE) {
+ IVTV_DEBUG_WARN("failed loading encoder firmware\n");
+ return -3;
+ }
+ if (!itv->has_cx23415)
+ return 0;
+
+ IVTV_DEBUG_INFO("Loading decoder image\n");
+ if (load_fw_direct(CX2341X_FIRM_DEC_FILENAME,
+ itv->dec_mem, itv, IVTV_FW_DEC_SIZE) != IVTV_FW_DEC_SIZE) {
+ IVTV_DEBUG_WARN("failed loading decoder firmware\n");
+ return -1;
+ }
+ return 0;
+}
+
+static volatile struct ivtv_mailbox __iomem *ivtv_search_mailbox(const volatile u8 __iomem *mem, u32 size)
+{
+ int i;
+
+ /* mailbox is preceded by a 16 byte 'magic cookie' starting at a 256-byte
+ address boundary */
+ for (i = 0; i < size; i += 0x100) {
+ if (readl(mem + i) == 0x12345678 &&
+ readl(mem + i + 4) == 0x34567812 &&
+ readl(mem + i + 8) == 0x56781234 &&
+ readl(mem + i + 12) == 0x78123456) {
+ return (volatile struct ivtv_mailbox __iomem *)(mem + i + 16);
+ }
+ }
+ return NULL;
+}
+
+int ivtv_firmware_init(struct ivtv *itv)
+{
+ int err;
+
+ ivtv_halt_firmware(itv);
+
+ /* load firmware */
+ err = ivtv_firmware_copy(itv);
+ if (err) {
+ IVTV_DEBUG_WARN("Error %d loading firmware\n", err);
+ return err;
+ }
+
+ /* start firmware */
+ write_reg(read_reg(IVTV_REG_SPU) & IVTV_MASK_SPU_ENABLE, IVTV_REG_SPU);
+ ivtv_msleep_timeout(100, 0);
+ if (itv->has_cx23415)
+ write_reg(read_reg(IVTV_REG_VPU) & IVTV_MASK_VPU_ENABLE15, IVTV_REG_VPU);
+ else
+ write_reg(read_reg(IVTV_REG_VPU) & IVTV_MASK_VPU_ENABLE16, IVTV_REG_VPU);
+ ivtv_msleep_timeout(100, 0);
+
+ /* find mailboxes and ping firmware */
+ itv->enc_mbox.mbox = ivtv_search_mailbox(itv->enc_mem, IVTV_ENCODER_SIZE);
+ if (itv->enc_mbox.mbox == NULL)
+ IVTV_ERR("Encoder mailbox not found\n");
+ else if (ivtv_vapi(itv, CX2341X_ENC_PING_FW, 0)) {
+ IVTV_ERR("Encoder firmware dead!\n");
+ itv->enc_mbox.mbox = NULL;
+ }
+ if (itv->enc_mbox.mbox == NULL)
+ return -ENODEV;
+
+ if (!itv->has_cx23415)
+ return 0;
+
+ itv->dec_mbox.mbox = ivtv_search_mailbox(itv->dec_mem, IVTV_DECODER_SIZE);
+ if (itv->dec_mbox.mbox == NULL) {
+ IVTV_ERR("Decoder mailbox not found\n");
+ } else if (itv->has_cx23415 && ivtv_vapi(itv, CX2341X_DEC_PING_FW, 0)) {
+ IVTV_ERR("Decoder firmware dead!\n");
+ itv->dec_mbox.mbox = NULL;
+ } else {
+ /* Firmware okay, so check yuv output filter table */
+ ivtv_yuv_filter_check(itv);
+ }
+ return itv->dec_mbox.mbox ? 0 : -ENODEV;
+}
+
+void ivtv_init_mpeg_decoder(struct ivtv *itv)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ long readbytes;
+ volatile u8 __iomem *mem_offset;
+
+ data[0] = 0;
+ data[1] = itv->cxhdl.width; /* YUV source width */
+ data[2] = itv->cxhdl.height;
+ data[3] = itv->cxhdl.audio_properties; /* Audio settings to use,
+ bitmap. see docs. */
+ if (ivtv_api(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, data)) {
+ IVTV_ERR("ivtv_init_mpeg_decoder failed to set decoder source\n");
+ return;
+ }
+
+ if (ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 1) != 0) {
+ IVTV_ERR("ivtv_init_mpeg_decoder failed to start playback\n");
+ return;
+ }
+ ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, 2, data);
+ mem_offset = itv->dec_mem + data[1];
+
+ if ((readbytes = load_fw_direct(IVTV_DECODE_INIT_MPEG_FILENAME,
+ mem_offset, itv, IVTV_DECODE_INIT_MPEG_SIZE)) <= 0) {
+ IVTV_DEBUG_WARN("failed to read mpeg decoder initialisation file %s\n",
+ IVTV_DECODE_INIT_MPEG_FILENAME);
+ } else {
+ ivtv_vapi(itv, CX2341X_DEC_SCHED_DMA_FROM_HOST, 3, 0, readbytes, 0);
+ ivtv_msleep_timeout(100, 0);
+ }
+ ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 4, 0, 0, 0, 1);
+}
+
+/* Try to restart the card & restore previous settings */
+int ivtv_firmware_restart(struct ivtv *itv)
+{
+ int rc = 0;
+ v4l2_std_id std;
+
+ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)
+ /* Display test image during restart */
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing,
+ SAA7127_INPUT_TYPE_TEST_IMAGE,
+ itv->card->video_outputs[itv->active_output].video_output,
+ 0);
+
+ mutex_lock(&itv->udma.lock);
+
+ rc = ivtv_firmware_init(itv);
+ if (rc) {
+ mutex_unlock(&itv->udma.lock);
+ return rc;
+ }
+
+ /* Allow settings to reload */
+ ivtv_mailbox_cache_invalidate(itv);
+
+ /* Restore encoder video standard */
+ std = itv->std;
+ itv->std = 0;
+ ivtv_s_std_enc(itv, &std);
+
+ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
+ ivtv_init_mpeg_decoder(itv);
+
+ /* Restore decoder video standard */
+ std = itv->std_out;
+ itv->std_out = 0;
+ ivtv_s_std_dec(itv, &std);
+
+ /* Restore framebuffer if active */
+ if (itv->ivtvfb_restore)
+ itv->ivtvfb_restore(itv);
+
+ /* Restore alpha settings */
+ ivtv_set_osd_alpha(itv);
+
+ /* Restore normal output */
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing,
+ SAA7127_INPUT_TYPE_NORMAL,
+ itv->card->video_outputs[itv->active_output].video_output,
+ 0);
+ }
+
+ mutex_unlock(&itv->udma.lock);
+ return rc;
+}
+
+/* Check firmware running state. The checks fall through
+ allowing multiple failures to be logged. */
+int ivtv_firmware_check(struct ivtv *itv, char *where)
+{
+ int res = 0;
+
+ /* Check encoder is still running */
+ if (ivtv_vapi(itv, CX2341X_ENC_PING_FW, 0) < 0) {
+ IVTV_WARN("Encoder has died : %s\n", where);
+ res = -1;
+ }
+
+ /* Also check audio. Only check if not in use & encoder is okay */
+ if (!res && !atomic_read(&itv->capturing) &&
+ (!atomic_read(&itv->decoding) ||
+ (atomic_read(&itv->decoding) < 2 && test_bit(IVTV_F_I_DEC_YUV,
+ &itv->i_flags)))) {
+
+ if (ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12) < 0) {
+ IVTV_WARN("Audio has died (Encoder OK) : %s\n", where);
+ res = -2;
+ }
+ }
+
+ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
+ /* Second audio check. Skip if audio already failed */
+ if (res != -2 && read_dec(0x100) != read_dec(0x104)) {
+ /* Wait & try again to be certain. */
+ ivtv_msleep_timeout(14, 0);
+ if (read_dec(0x100) != read_dec(0x104)) {
+ IVTV_WARN("Audio has died (Decoder) : %s\n",
+ where);
+ res = -1;
+ }
+ }
+
+ /* Check decoder is still running */
+ if (ivtv_vapi(itv, CX2341X_DEC_PING_FW, 0) < 0) {
+ IVTV_WARN("Decoder has died : %s\n", where);
+ res = -1;
+ }
+ }
+
+ /* If something failed & currently idle, try to reload */
+ if (res && !atomic_read(&itv->capturing) &&
+ !atomic_read(&itv->decoding)) {
+ IVTV_INFO("Detected in %s that firmware had failed - "
+ "Reloading\n", where);
+ res = ivtv_firmware_restart(itv);
+ /*
+ * Even if restarted ok, still signal a problem had occurred.
+ * The caller can come through this function again to check
+ * if things are really ok after the restart.
+ */
+ if (!res) {
+ IVTV_INFO("Firmware restart okay\n");
+ res = -EAGAIN;
+ } else {
+ IVTV_INFO("Firmware restart failed\n");
+ }
+ } else if (res) {
+ res = -EIO;
+ }
+
+ return res;
+}
diff --git a/drivers/media/video/ivtv/ivtv-firmware.h b/drivers/media/video/ivtv/ivtv-firmware.h
new file mode 100644
index 00000000..52bb4e55
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-firmware.h
@@ -0,0 +1,31 @@
+/*
+ ivtv firmware functions.
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef IVTV_FIRMWARE_H
+#define IVTV_FIRMWARE_H
+
+int ivtv_firmware_init(struct ivtv *itv);
+void ivtv_firmware_versions(struct ivtv *itv);
+void ivtv_halt_firmware(struct ivtv *itv);
+void ivtv_init_mpeg_decoder(struct ivtv *itv);
+int ivtv_firmware_check(struct ivtv *itv, char *where);
+
+#endif
diff --git a/drivers/media/video/ivtv/ivtv-gpio.c b/drivers/media/video/ivtv/ivtv-gpio.c
new file mode 100644
index 00000000..8f0d0778
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-gpio.c
@@ -0,0 +1,374 @@
+/*
+ gpio functions.
+ Merging GPIO support into driver:
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-cards.h"
+#include "ivtv-gpio.h"
+#include "tuner-xc2028.h"
+#include <media/tuner.h>
+#include <media/v4l2-ctrls.h>
+
+/*
+ * GPIO assignment of Yuan MPG600/MPG160
+ *
+ * bit 15 14 13 12 | 11 10 9 8 | 7 6 5 4 | 3 2 1 0
+ * OUTPUT IN1 IN0 AM3 AM2 AM1 AM0
+ * INPUT DM1 DM0
+ *
+ * IN* : Input selection
+ * IN1 IN0
+ * 1 1 N/A
+ * 1 0 Line
+ * 0 1 N/A
+ * 0 0 Tuner
+ *
+ * AM* : Audio Mode
+ * AM3 0: Normal 1: Mixed(Sub+Main channel)
+ * AM2 0: Subchannel 1: Main channel
+ * AM1 0: Stereo 1: Mono
+ * AM0 0: Normal 1: Mute
+ *
+ * DM* : Detected tuner audio Mode
+ * DM1 0: Stereo 1: Mono
+ * DM0 0: Multiplex 1: Normal
+ *
+ * GPIO Initial Settings
+ * MPG600 MPG160
+ * DIR 0x3080 0x7080
+ * OUTPUT 0x000C 0x400C
+ *
+ * Special thanks to Makoto Iguchi <iguchi@tahoo.org> and Mr. Anonymous
+ * for analyzing GPIO of MPG160.
+ *
+ *****************************************************************************
+ *
+ * GPIO assignment of Avermedia M179 (per information direct from AVerMedia)
+ *
+ * bit 15 14 13 12 | 11 10 9 8 | 7 6 5 4 | 3 2 1 0
+ * OUTPUT IN0 AM0 IN1 AM1 AM2 IN2 BR0 BR1
+ * INPUT
+ *
+ * IN* : Input selection
+ * IN0 IN1 IN2
+ * * 1 * Mute
+ * 0 0 0 Line-In
+ * 1 0 0 TV Tuner Audio
+ * 0 0 1 FM Audio
+ * 1 0 1 Mute
+ *
+ * AM* : Audio Mode
+ * AM0 AM1 AM2
+ * 0 0 0 TV Tuner Audio: L_OUT=(L+R)/2, R_OUT=SAP
+ * 0 0 1 TV Tuner Audio: L_OUT=R_OUT=SAP (SAP)
+ * 0 1 0 TV Tuner Audio: L_OUT=L, R_OUT=R (stereo)
+ * 0 1 1 TV Tuner Audio: mute
+ * 1 * * TV Tuner Audio: L_OUT=R_OUT=(L+R)/2 (mono)
+ *
+ * BR* : Audio Sample Rate (BR stands for bitrate for some reason)
+ * BR0 BR1
+ * 0 0 32 kHz
+ * 0 1 44.1 kHz
+ * 1 0 48 kHz
+ *
+ * DM* : Detected tuner audio Mode
+ * Unknown currently
+ *
+ * Special thanks to AVerMedia Technologies, Inc. and Jiun-Kuei Jung at
+ * AVerMedia for providing the GPIO information used to add support
+ * for the M179 cards.
+ */
+
+/********************* GPIO stuffs *********************/
+
+/* GPIO registers */
+#define IVTV_REG_GPIO_IN 0x9008
+#define IVTV_REG_GPIO_OUT 0x900c
+#define IVTV_REG_GPIO_DIR 0x9020
+
+void ivtv_reset_ir_gpio(struct ivtv *itv)
+{
+ int curdir, curout;
+
+ if (itv->card->type != IVTV_CARD_PVR_150)
+ return;
+ IVTV_DEBUG_INFO("Resetting PVR150 IR\n");
+ curout = read_reg(IVTV_REG_GPIO_OUT);
+ curdir = read_reg(IVTV_REG_GPIO_DIR);
+ curdir |= 0x80;
+ write_reg(curdir, IVTV_REG_GPIO_DIR);
+ curout = (curout & ~0xF) | 1;
+ write_reg(curout, IVTV_REG_GPIO_OUT);
+ /* We could use something else for smaller time */
+ schedule_timeout_interruptible(msecs_to_jiffies(1));
+ curout |= 2;
+ write_reg(curout, IVTV_REG_GPIO_OUT);
+ curdir &= ~0x80;
+ write_reg(curdir, IVTV_REG_GPIO_DIR);
+}
+
+/* Xceive tuner reset function */
+int ivtv_reset_tuner_gpio(void *dev, int component, int cmd, int value)
+{
+ struct i2c_algo_bit_data *algo = dev;
+ struct ivtv *itv = algo->data;
+ u32 curout;
+
+ if (cmd != XC2028_TUNER_RESET)
+ return 0;
+ IVTV_DEBUG_INFO("Resetting tuner\n");
+ curout = read_reg(IVTV_REG_GPIO_OUT);
+ curout &= ~(1 << itv->card->xceive_pin);
+ write_reg(curout, IVTV_REG_GPIO_OUT);
+ schedule_timeout_interruptible(msecs_to_jiffies(1));
+
+ curout |= 1 << itv->card->xceive_pin;
+ write_reg(curout, IVTV_REG_GPIO_OUT);
+ schedule_timeout_interruptible(msecs_to_jiffies(1));
+ return 0;
+}
+
+static inline struct ivtv *sd_to_ivtv(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct ivtv, sd_gpio);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct ivtv, hdl_gpio)->sd_gpio;
+}
+
+static int subdev_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask, data;
+
+ mask = itv->card->gpio_audio_freq.mask;
+ switch (freq) {
+ case 32000:
+ data = itv->card->gpio_audio_freq.f32000;
+ break;
+ case 44100:
+ data = itv->card->gpio_audio_freq.f44100;
+ break;
+ case 48000:
+ default:
+ data = itv->card->gpio_audio_freq.f48000;
+ break;
+ }
+ if (mask)
+ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+ return 0;
+}
+
+static int subdev_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask;
+
+ mask = itv->card->gpio_audio_detect.mask;
+ if (mask == 0 || (read_reg(IVTV_REG_GPIO_IN) & mask))
+ vt->rxsubchans = V4L2_TUNER_SUB_STEREO |
+ V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ else
+ vt->rxsubchans = V4L2_TUNER_SUB_MONO;
+ return 0;
+}
+
+static int subdev_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask, data;
+
+ mask = itv->card->gpio_audio_mode.mask;
+ switch (vt->audmode) {
+ case V4L2_TUNER_MODE_LANG1:
+ data = itv->card->gpio_audio_mode.lang1;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ data = itv->card->gpio_audio_mode.lang2;
+ break;
+ case V4L2_TUNER_MODE_MONO:
+ data = itv->card->gpio_audio_mode.mono;
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ default:
+ data = itv->card->gpio_audio_mode.stereo;
+ break;
+ }
+ if (mask)
+ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+ return 0;
+}
+
+static int subdev_s_radio(struct v4l2_subdev *sd)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask, data;
+
+ mask = itv->card->gpio_audio_input.mask;
+ data = itv->card->gpio_audio_input.radio;
+ if (mask)
+ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+ return 0;
+}
+
+static int subdev_s_audio_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask, data;
+
+ if (input > 2)
+ return -EINVAL;
+ mask = itv->card->gpio_audio_input.mask;
+ switch (input) {
+ case 0:
+ data = itv->card->gpio_audio_input.tuner;
+ break;
+ case 1:
+ data = itv->card->gpio_audio_input.linein;
+ break;
+ case 2:
+ default:
+ data = itv->card->gpio_audio_input.radio;
+ break;
+ }
+ if (mask)
+ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+ return 0;
+}
+
+static int subdev_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask, data;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ mask = itv->card->gpio_audio_mute.mask;
+ data = ctrl->val ? itv->card->gpio_audio_mute.mute : 0;
+ if (mask)
+ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) |
+ (data & mask), IVTV_REG_GPIO_OUT);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+
+static int subdev_log_status(struct v4l2_subdev *sd)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+
+ IVTV_INFO("GPIO status: DIR=0x%04x OUT=0x%04x IN=0x%04x\n",
+ read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT),
+ read_reg(IVTV_REG_GPIO_IN));
+ v4l2_ctrl_handler_log_status(&itv->hdl_gpio, sd->name);
+ return 0;
+}
+
+static int subdev_s_video_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask, data;
+
+ if (input > 2) /* 0:Tuner 1:Composite 2:S-Video */
+ return -EINVAL;
+ mask = itv->card->gpio_video_input.mask;
+ if (input == 0)
+ data = itv->card->gpio_video_input.tuner;
+ else if (input == 1)
+ data = itv->card->gpio_video_input.composite;
+ else
+ data = itv->card->gpio_video_input.svideo;
+ if (mask)
+ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops gpio_ctrl_ops = {
+ .s_ctrl = subdev_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops subdev_core_ops = {
+ .log_status = subdev_log_status,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+};
+
+static const struct v4l2_subdev_tuner_ops subdev_tuner_ops = {
+ .s_radio = subdev_s_radio,
+ .g_tuner = subdev_g_tuner,
+ .s_tuner = subdev_s_tuner,
+};
+
+static const struct v4l2_subdev_audio_ops subdev_audio_ops = {
+ .s_clock_freq = subdev_s_clock_freq,
+ .s_routing = subdev_s_audio_routing,
+};
+
+static const struct v4l2_subdev_video_ops subdev_video_ops = {
+ .s_routing = subdev_s_video_routing,
+};
+
+static const struct v4l2_subdev_ops subdev_ops = {
+ .core = &subdev_core_ops,
+ .tuner = &subdev_tuner_ops,
+ .audio = &subdev_audio_ops,
+ .video = &subdev_video_ops,
+};
+
+int ivtv_gpio_init(struct ivtv *itv)
+{
+ u16 pin = 0;
+
+ if (itv->card->xceive_pin)
+ pin = 1 << itv->card->xceive_pin;
+
+ if ((itv->card->gpio_init.direction | pin) == 0)
+ return 0;
+
+ IVTV_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n",
+ read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT));
+
+ /* init output data then direction */
+ write_reg(itv->card->gpio_init.initial_value | pin, IVTV_REG_GPIO_OUT);
+ write_reg(itv->card->gpio_init.direction | pin, IVTV_REG_GPIO_DIR);
+ v4l2_subdev_init(&itv->sd_gpio, &subdev_ops);
+ snprintf(itv->sd_gpio.name, sizeof(itv->sd_gpio.name), "%s-gpio", itv->v4l2_dev.name);
+ itv->sd_gpio.grp_id = IVTV_HW_GPIO;
+ v4l2_ctrl_handler_init(&itv->hdl_gpio, 1);
+ v4l2_ctrl_new_std(&itv->hdl_gpio, &gpio_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+ if (itv->hdl_gpio.error)
+ return itv->hdl_gpio.error;
+ itv->sd_gpio.ctrl_handler = &itv->hdl_gpio;
+ v4l2_ctrl_handler_setup(&itv->hdl_gpio);
+ return v4l2_device_register_subdev(&itv->v4l2_dev, &itv->sd_gpio);
+}
diff --git a/drivers/media/video/ivtv/ivtv-gpio.h b/drivers/media/video/ivtv/ivtv-gpio.h
new file mode 100644
index 00000000..0b5d19c8
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-gpio.h
@@ -0,0 +1,29 @@
+/*
+ gpio functions.
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef IVTV_GPIO_H
+#define IVTV_GPIO_H
+
+/* GPIO stuff */
+int ivtv_gpio_init(struct ivtv *itv);
+void ivtv_reset_ir_gpio(struct ivtv *itv);
+int ivtv_reset_tuner_gpio(void *dev, int component, int cmd, int value);
+
+#endif
diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c
new file mode 100644
index 00000000..d47f41a0
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-i2c.c
@@ -0,0 +1,760 @@
+/*
+ I2C functions
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ This file includes an i2c implementation that was reverse engineered
+ from the Hauppauge windows driver. Older ivtv versions used i2c-algo-bit,
+ which whilst fine under most circumstances, had trouble with the Zilog
+ CPU on the PVR-150 which handles IR functions (occasional inability to
+ communicate with the chip until it was reset) and also with the i2c
+ bus being completely unreachable when multiple PVR cards were present.
+
+ The implementation is very similar to i2c-algo-bit, but there are enough
+ subtle differences that the two are hard to merge. The general strategy
+ employed by i2c-algo-bit is to use udelay() to implement the timing
+ when putting out bits on the scl/sda lines. The general strategy taken
+ here is to poll the lines for state changes (see ivtv_waitscl and
+ ivtv_waitsda). In addition there are small delays at various locations
+ which poll the SCL line 5 times (ivtv_scldelay). I would guess that
+ since this is memory mapped I/O that the length of those delays is tied
+ to the PCI bus clock. There is some extra code to do with recovery
+ and retries. Since it is not known what causes the actual i2c problems
+ in the first place, the only goal if one was to attempt to use
+ i2c-algo-bit would be to try to make it follow the same code path.
+ This would be a lot of work, and I'm also not convinced that it would
+ provide a generic benefit to i2c-algo-bit. Therefore consider this
+ an engineering solution -- not pretty, but it works.
+
+ Some more general comments about what we are doing:
+
+ The i2c bus is a 2 wire serial bus, with clock (SCL) and data (SDA)
+ lines. To communicate on the bus (as a master, we don't act as a slave),
+ we first initiate a start condition (ivtv_start). We then write the
+ address of the device that we want to communicate with, along with a flag
+ that indicates whether this is a read or a write. The slave then issues
+ an ACK signal (ivtv_ack), which tells us that it is ready for reading /
+ writing. We then proceed with reading or writing (ivtv_read/ivtv_write),
+ and finally issue a stop condition (ivtv_stop) to make the bus available
+ to other masters.
+
+ There is an additional form of transaction where a write may be
+ immediately followed by a read. In this case, there is no intervening
+ stop condition. (Only the msp3400 chip uses this method of data transfer).
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-cards.h"
+#include "ivtv-gpio.h"
+#include "ivtv-i2c.h"
+#include <media/cx25840.h>
+
+/* i2c implementation for cx23415/6 chip, ivtv project.
+ * Author: Kevin Thayer (nufan_wfk at yahoo.com)
+ */
+/* i2c stuff */
+#define IVTV_REG_I2C_SETSCL_OFFSET 0x7000
+#define IVTV_REG_I2C_SETSDA_OFFSET 0x7004
+#define IVTV_REG_I2C_GETSCL_OFFSET 0x7008
+#define IVTV_REG_I2C_GETSDA_OFFSET 0x700c
+
+#define IVTV_CS53L32A_I2C_ADDR 0x11
+#define IVTV_M52790_I2C_ADDR 0x48
+#define IVTV_CX25840_I2C_ADDR 0x44
+#define IVTV_SAA7115_I2C_ADDR 0x21
+#define IVTV_SAA7127_I2C_ADDR 0x44
+#define IVTV_SAA717x_I2C_ADDR 0x21
+#define IVTV_MSP3400_I2C_ADDR 0x40
+#define IVTV_HAUPPAUGE_I2C_ADDR 0x50
+#define IVTV_WM8739_I2C_ADDR 0x1a
+#define IVTV_WM8775_I2C_ADDR 0x1b
+#define IVTV_TEA5767_I2C_ADDR 0x60
+#define IVTV_UPD64031A_I2C_ADDR 0x12
+#define IVTV_UPD64083_I2C_ADDR 0x5c
+#define IVTV_VP27SMPX_I2C_ADDR 0x5b
+#define IVTV_M52790_I2C_ADDR 0x48
+#define IVTV_AVERMEDIA_IR_RX_I2C_ADDR 0x40
+#define IVTV_HAUP_EXT_IR_RX_I2C_ADDR 0x1a
+#define IVTV_HAUP_INT_IR_RX_I2C_ADDR 0x18
+#define IVTV_Z8F0811_IR_TX_I2C_ADDR 0x70
+#define IVTV_Z8F0811_IR_RX_I2C_ADDR 0x71
+#define IVTV_ADAPTEC_IR_ADDR 0x6b
+
+/* This array should match the IVTV_HW_ defines */
+static const u8 hw_addrs[] = {
+ IVTV_CX25840_I2C_ADDR,
+ IVTV_SAA7115_I2C_ADDR,
+ IVTV_SAA7127_I2C_ADDR,
+ IVTV_MSP3400_I2C_ADDR,
+ 0,
+ IVTV_WM8775_I2C_ADDR,
+ IVTV_CS53L32A_I2C_ADDR,
+ 0,
+ IVTV_SAA7115_I2C_ADDR,
+ IVTV_UPD64031A_I2C_ADDR,
+ IVTV_UPD64083_I2C_ADDR,
+ IVTV_SAA717x_I2C_ADDR,
+ IVTV_WM8739_I2C_ADDR,
+ IVTV_VP27SMPX_I2C_ADDR,
+ IVTV_M52790_I2C_ADDR,
+ 0, /* IVTV_HW_GPIO dummy driver ID */
+ IVTV_AVERMEDIA_IR_RX_I2C_ADDR, /* IVTV_HW_I2C_IR_RX_AVER */
+ IVTV_HAUP_EXT_IR_RX_I2C_ADDR, /* IVTV_HW_I2C_IR_RX_HAUP_EXT */
+ IVTV_HAUP_INT_IR_RX_I2C_ADDR, /* IVTV_HW_I2C_IR_RX_HAUP_INT */
+ IVTV_Z8F0811_IR_TX_I2C_ADDR, /* IVTV_HW_Z8F0811_IR_TX_HAUP */
+ IVTV_Z8F0811_IR_RX_I2C_ADDR, /* IVTV_HW_Z8F0811_IR_RX_HAUP */
+ IVTV_ADAPTEC_IR_ADDR, /* IVTV_HW_I2C_IR_RX_ADAPTEC */
+};
+
+/* This array should match the IVTV_HW_ defines */
+static const char * const hw_devicenames[] = {
+ "cx25840",
+ "saa7115",
+ "saa7127_auto", /* saa7127 or saa7129 */
+ "msp3400",
+ "tuner",
+ "wm8775",
+ "cs53l32a",
+ "tveeprom",
+ "saa7114",
+ "upd64031a",
+ "upd64083",
+ "saa717x",
+ "wm8739",
+ "vp27smpx",
+ "m52790",
+ "gpio",
+ "ir_video", /* IVTV_HW_I2C_IR_RX_AVER */
+ "ir_video", /* IVTV_HW_I2C_IR_RX_HAUP_EXT */
+ "ir_video", /* IVTV_HW_I2C_IR_RX_HAUP_INT */
+ "ir_tx_z8f0811_haup", /* IVTV_HW_Z8F0811_IR_TX_HAUP */
+ "ir_rx_z8f0811_haup", /* IVTV_HW_Z8F0811_IR_RX_HAUP */
+ "ir_video", /* IVTV_HW_I2C_IR_RX_ADAPTEC */
+};
+
+static int get_key_adaptec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+ unsigned char keybuf[4];
+
+ keybuf[0] = 0x00;
+ i2c_master_send(ir->c, keybuf, 1);
+ /* poll IR chip */
+ if (i2c_master_recv(ir->c, keybuf, sizeof(keybuf)) != sizeof(keybuf)) {
+ return 0;
+ }
+
+ /* key pressed ? */
+ if (keybuf[2] == 0xff)
+ return 0;
+
+ /* remove repeat bit */
+ keybuf[2] &= 0x7f;
+ keybuf[3] |= 0x80;
+
+ *ir_key = keybuf[3] | keybuf[2] << 8 | keybuf[1] << 16 |keybuf[0] << 24;
+ *ir_raw = *ir_key;
+
+ return 1;
+}
+
+static int ivtv_i2c_new_ir(struct ivtv *itv, u32 hw, const char *type, u8 addr)
+{
+ struct i2c_board_info info;
+ struct i2c_adapter *adap = &itv->i2c_adap;
+ struct IR_i2c_init_data *init_data = &itv->ir_i2c_init_data;
+ unsigned short addr_list[2] = { addr, I2C_CLIENT_END };
+
+ /* Only allow one IR transmitter to be registered per board */
+ if (hw & IVTV_HW_IR_TX_ANY) {
+ if (itv->hw_flags & IVTV_HW_IR_TX_ANY)
+ return -1;
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, type, I2C_NAME_SIZE);
+ return i2c_new_probed_device(adap, &info, addr_list, NULL)
+ == NULL ? -1 : 0;
+ }
+
+ /* Only allow one IR receiver to be registered per board */
+ if (itv->hw_flags & IVTV_HW_IR_RX_ANY)
+ return -1;
+
+ /* Our default information for ir-kbd-i2c.c to use */
+ switch (hw) {
+ case IVTV_HW_I2C_IR_RX_AVER:
+ init_data->ir_codes = RC_MAP_AVERMEDIA_CARDBUS;
+ init_data->internal_get_key_func =
+ IR_KBD_GET_KEY_AVERMEDIA_CARDBUS;
+ init_data->type = RC_TYPE_OTHER;
+ init_data->name = "AVerMedia AVerTV card";
+ break;
+ case IVTV_HW_I2C_IR_RX_HAUP_EXT:
+ case IVTV_HW_I2C_IR_RX_HAUP_INT:
+ init_data->ir_codes = RC_MAP_HAUPPAUGE;
+ init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP;
+ init_data->type = RC_TYPE_RC5;
+ init_data->name = itv->card_name;
+ break;
+ case IVTV_HW_Z8F0811_IR_RX_HAUP:
+ /* Default to grey remote */
+ init_data->ir_codes = RC_MAP_HAUPPAUGE;
+ init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR;
+ init_data->type = RC_TYPE_RC5;
+ init_data->name = itv->card_name;
+ break;
+ case IVTV_HW_I2C_IR_RX_ADAPTEC:
+ init_data->get_key = get_key_adaptec;
+ init_data->name = itv->card_name;
+ /* FIXME: The protocol and RC_MAP needs to be corrected */
+ init_data->ir_codes = RC_MAP_EMPTY;
+ init_data->type = RC_TYPE_UNKNOWN;
+ break;
+ }
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ info.platform_data = init_data;
+ strlcpy(info.type, type, I2C_NAME_SIZE);
+
+ return i2c_new_probed_device(adap, &info, addr_list, NULL) == NULL ?
+ -1 : 0;
+}
+
+/* Instantiate the IR receiver device using probing -- undesirable */
+struct i2c_client *ivtv_i2c_new_ir_legacy(struct ivtv *itv)
+{
+ struct i2c_board_info info;
+ /*
+ * The external IR receiver is at i2c address 0x34.
+ * The internal IR receiver is at i2c address 0x30.
+ *
+ * In theory, both can be fitted, and Hauppauge suggests an external
+ * overrides an internal. That's why we probe 0x1a (~0x34) first. CB
+ *
+ * Some of these addresses we probe may collide with other i2c address
+ * allocations, so this function must be called after all other i2c
+ * devices we care about are registered.
+ */
+ const unsigned short addr_list[] = {
+ 0x1a, /* Hauppauge IR external - collides with WM8739 */
+ 0x18, /* Hauppauge IR internal */
+ I2C_CLIENT_END
+ };
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
+ return i2c_new_probed_device(&itv->i2c_adap, &info, addr_list, NULL);
+}
+
+int ivtv_i2c_register(struct ivtv *itv, unsigned idx)
+{
+ struct v4l2_subdev *sd;
+ struct i2c_adapter *adap = &itv->i2c_adap;
+ const char *type = hw_devicenames[idx];
+ u32 hw = 1 << idx;
+
+ if (idx >= ARRAY_SIZE(hw_addrs))
+ return -1;
+ if (hw == IVTV_HW_TUNER) {
+ /* special tuner handling */
+ sd = v4l2_i2c_new_subdev(&itv->v4l2_dev, adap, type, 0,
+ itv->card_i2c->radio);
+ if (sd)
+ sd->grp_id = 1 << idx;
+ sd = v4l2_i2c_new_subdev(&itv->v4l2_dev, adap, type, 0,
+ itv->card_i2c->demod);
+ if (sd)
+ sd->grp_id = 1 << idx;
+ sd = v4l2_i2c_new_subdev(&itv->v4l2_dev, adap, type, 0,
+ itv->card_i2c->tv);
+ if (sd)
+ sd->grp_id = 1 << idx;
+ return sd ? 0 : -1;
+ }
+
+ if (hw & IVTV_HW_IR_ANY)
+ return ivtv_i2c_new_ir(itv, hw, type, hw_addrs[idx]);
+
+ /* Is it not an I2C device or one we do not wish to register? */
+ if (!hw_addrs[idx])
+ return -1;
+
+ /* It's an I2C device other than an analog tuner or IR chip */
+ if (hw == IVTV_HW_UPD64031A || hw == IVTV_HW_UPD6408X) {
+ sd = v4l2_i2c_new_subdev(&itv->v4l2_dev,
+ adap, type, 0, I2C_ADDRS(hw_addrs[idx]));
+ } else if (hw == IVTV_HW_CX25840) {
+ struct cx25840_platform_data pdata;
+ struct i2c_board_info cx25840_info = {
+ .type = "cx25840",
+ .addr = hw_addrs[idx],
+ .platform_data = &pdata,
+ };
+
+ pdata.pvr150_workaround = itv->pvr150_workaround;
+ sd = v4l2_i2c_new_subdev_board(&itv->v4l2_dev, adap,
+ &cx25840_info, NULL);
+ } else {
+ sd = v4l2_i2c_new_subdev(&itv->v4l2_dev,
+ adap, type, hw_addrs[idx], NULL);
+ }
+ if (sd)
+ sd->grp_id = 1 << idx;
+ return sd ? 0 : -1;
+}
+
+struct v4l2_subdev *ivtv_find_hw(struct ivtv *itv, u32 hw)
+{
+ struct v4l2_subdev *result = NULL;
+ struct v4l2_subdev *sd;
+
+ spin_lock(&itv->v4l2_dev.lock);
+ v4l2_device_for_each_subdev(sd, &itv->v4l2_dev) {
+ if (sd->grp_id == hw) {
+ result = sd;
+ break;
+ }
+ }
+ spin_unlock(&itv->v4l2_dev.lock);
+ return result;
+}
+
+/* Set the serial clock line to the desired state */
+static void ivtv_setscl(struct ivtv *itv, int state)
+{
+ /* write them out */
+ /* write bits are inverted */
+ write_reg(~state, IVTV_REG_I2C_SETSCL_OFFSET);
+}
+
+/* Set the serial data line to the desired state */
+static void ivtv_setsda(struct ivtv *itv, int state)
+{
+ /* write them out */
+ /* write bits are inverted */
+ write_reg(~state & 1, IVTV_REG_I2C_SETSDA_OFFSET);
+}
+
+/* Read the serial clock line */
+static int ivtv_getscl(struct ivtv *itv)
+{
+ return read_reg(IVTV_REG_I2C_GETSCL_OFFSET) & 1;
+}
+
+/* Read the serial data line */
+static int ivtv_getsda(struct ivtv *itv)
+{
+ return read_reg(IVTV_REG_I2C_GETSDA_OFFSET) & 1;
+}
+
+/* Implement a short delay by polling the serial clock line */
+static void ivtv_scldelay(struct ivtv *itv)
+{
+ int i;
+
+ for (i = 0; i < 5; ++i)
+ ivtv_getscl(itv);
+}
+
+/* Wait for the serial clock line to become set to a specific value */
+static int ivtv_waitscl(struct ivtv *itv, int val)
+{
+ int i;
+
+ ivtv_scldelay(itv);
+ for (i = 0; i < 1000; ++i) {
+ if (ivtv_getscl(itv) == val)
+ return 1;
+ }
+ return 0;
+}
+
+/* Wait for the serial data line to become set to a specific value */
+static int ivtv_waitsda(struct ivtv *itv, int val)
+{
+ int i;
+
+ ivtv_scldelay(itv);
+ for (i = 0; i < 1000; ++i) {
+ if (ivtv_getsda(itv) == val)
+ return 1;
+ }
+ return 0;
+}
+
+/* Wait for the slave to issue an ACK */
+static int ivtv_ack(struct ivtv *itv)
+{
+ int ret = 0;
+
+ if (ivtv_getscl(itv) == 1) {
+ IVTV_DEBUG_HI_I2C("SCL was high starting an ack\n");
+ ivtv_setscl(itv, 0);
+ if (!ivtv_waitscl(itv, 0)) {
+ IVTV_DEBUG_I2C("Could not set SCL low starting an ack\n");
+ return -EREMOTEIO;
+ }
+ }
+ ivtv_setsda(itv, 1);
+ ivtv_scldelay(itv);
+ ivtv_setscl(itv, 1);
+ if (!ivtv_waitsda(itv, 0)) {
+ IVTV_DEBUG_I2C("Slave did not ack\n");
+ ret = -EREMOTEIO;
+ }
+ ivtv_setscl(itv, 0);
+ if (!ivtv_waitscl(itv, 0)) {
+ IVTV_DEBUG_I2C("Failed to set SCL low after ACK\n");
+ ret = -EREMOTEIO;
+ }
+ return ret;
+}
+
+/* Write a single byte to the i2c bus and wait for the slave to ACK */
+static int ivtv_sendbyte(struct ivtv *itv, unsigned char byte)
+{
+ int i, bit;
+
+ IVTV_DEBUG_HI_I2C("write %x\n",byte);
+ for (i = 0; i < 8; ++i, byte<<=1) {
+ ivtv_setscl(itv, 0);
+ if (!ivtv_waitscl(itv, 0)) {
+ IVTV_DEBUG_I2C("Error setting SCL low\n");
+ return -EREMOTEIO;
+ }
+ bit = (byte>>7)&1;
+ ivtv_setsda(itv, bit);
+ if (!ivtv_waitsda(itv, bit)) {
+ IVTV_DEBUG_I2C("Error setting SDA\n");
+ return -EREMOTEIO;
+ }
+ ivtv_setscl(itv, 1);
+ if (!ivtv_waitscl(itv, 1)) {
+ IVTV_DEBUG_I2C("Slave not ready for bit\n");
+ return -EREMOTEIO;
+ }
+ }
+ ivtv_setscl(itv, 0);
+ if (!ivtv_waitscl(itv, 0)) {
+ IVTV_DEBUG_I2C("Error setting SCL low\n");
+ return -EREMOTEIO;
+ }
+ return ivtv_ack(itv);
+}
+
+/* Read a byte from the i2c bus and send a NACK if applicable (i.e. for the
+ final byte) */
+static int ivtv_readbyte(struct ivtv *itv, unsigned char *byte, int nack)
+{
+ int i;
+
+ *byte = 0;
+
+ ivtv_setsda(itv, 1);
+ ivtv_scldelay(itv);
+ for (i = 0; i < 8; ++i) {
+ ivtv_setscl(itv, 0);
+ ivtv_scldelay(itv);
+ ivtv_setscl(itv, 1);
+ if (!ivtv_waitscl(itv, 1)) {
+ IVTV_DEBUG_I2C("Error setting SCL high\n");
+ return -EREMOTEIO;
+ }
+ *byte = ((*byte)<<1)|ivtv_getsda(itv);
+ }
+ ivtv_setscl(itv, 0);
+ ivtv_scldelay(itv);
+ ivtv_setsda(itv, nack);
+ ivtv_scldelay(itv);
+ ivtv_setscl(itv, 1);
+ ivtv_scldelay(itv);
+ ivtv_setscl(itv, 0);
+ ivtv_scldelay(itv);
+ IVTV_DEBUG_HI_I2C("read %x\n",*byte);
+ return 0;
+}
+
+/* Issue a start condition on the i2c bus to alert slaves to prepare for
+ an address write */
+static int ivtv_start(struct ivtv *itv)
+{
+ int sda;
+
+ sda = ivtv_getsda(itv);
+ if (sda != 1) {
+ IVTV_DEBUG_HI_I2C("SDA was low at start\n");
+ ivtv_setsda(itv, 1);
+ if (!ivtv_waitsda(itv, 1)) {
+ IVTV_DEBUG_I2C("SDA stuck low\n");
+ return -EREMOTEIO;
+ }
+ }
+ if (ivtv_getscl(itv) != 1) {
+ ivtv_setscl(itv, 1);
+ if (!ivtv_waitscl(itv, 1)) {
+ IVTV_DEBUG_I2C("SCL stuck low at start\n");
+ return -EREMOTEIO;
+ }
+ }
+ ivtv_setsda(itv, 0);
+ ivtv_scldelay(itv);
+ return 0;
+}
+
+/* Issue a stop condition on the i2c bus to release it */
+static int ivtv_stop(struct ivtv *itv)
+{
+ int i;
+
+ if (ivtv_getscl(itv) != 0) {
+ IVTV_DEBUG_HI_I2C("SCL not low when stopping\n");
+ ivtv_setscl(itv, 0);
+ if (!ivtv_waitscl(itv, 0)) {
+ IVTV_DEBUG_I2C("SCL could not be set low\n");
+ }
+ }
+ ivtv_setsda(itv, 0);
+ ivtv_scldelay(itv);
+ ivtv_setscl(itv, 1);
+ if (!ivtv_waitscl(itv, 1)) {
+ IVTV_DEBUG_I2C("SCL could not be set high\n");
+ return -EREMOTEIO;
+ }
+ ivtv_scldelay(itv);
+ ivtv_setsda(itv, 1);
+ if (!ivtv_waitsda(itv, 1)) {
+ IVTV_DEBUG_I2C("resetting I2C\n");
+ for (i = 0; i < 16; ++i) {
+ ivtv_setscl(itv, 0);
+ ivtv_scldelay(itv);
+ ivtv_setscl(itv, 1);
+ ivtv_scldelay(itv);
+ ivtv_setsda(itv, 1);
+ }
+ ivtv_waitsda(itv, 1);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+/* Write a message to the given i2c slave. do_stop may be 0 to prevent
+ issuing the i2c stop condition (when following with a read) */
+static int ivtv_write(struct ivtv *itv, unsigned char addr, unsigned char *data, u32 len, int do_stop)
+{
+ int retry, ret = -EREMOTEIO;
+ u32 i;
+
+ for (retry = 0; ret != 0 && retry < 8; ++retry) {
+ ret = ivtv_start(itv);
+
+ if (ret == 0) {
+ ret = ivtv_sendbyte(itv, addr<<1);
+ for (i = 0; ret == 0 && i < len; ++i)
+ ret = ivtv_sendbyte(itv, data[i]);
+ }
+ if (ret != 0 || do_stop) {
+ ivtv_stop(itv);
+ }
+ }
+ if (ret)
+ IVTV_DEBUG_I2C("i2c write to %x failed\n", addr);
+ return ret;
+}
+
+/* Read data from the given i2c slave. A stop condition is always issued. */
+static int ivtv_read(struct ivtv *itv, unsigned char addr, unsigned char *data, u32 len)
+{
+ int retry, ret = -EREMOTEIO;
+ u32 i;
+
+ for (retry = 0; ret != 0 && retry < 8; ++retry) {
+ ret = ivtv_start(itv);
+ if (ret == 0)
+ ret = ivtv_sendbyte(itv, (addr << 1) | 1);
+ for (i = 0; ret == 0 && i < len; ++i) {
+ ret = ivtv_readbyte(itv, &data[i], i == len - 1);
+ }
+ ivtv_stop(itv);
+ }
+ if (ret)
+ IVTV_DEBUG_I2C("i2c read from %x failed\n", addr);
+ return ret;
+}
+
+/* Kernel i2c transfer implementation. Takes a number of messages to be read
+ or written. If a read follows a write, this will occur without an
+ intervening stop condition */
+static int ivtv_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
+{
+ struct v4l2_device *v4l2_dev = i2c_get_adapdata(i2c_adap);
+ struct ivtv *itv = to_ivtv(v4l2_dev);
+ int retval;
+ int i;
+
+ mutex_lock(&itv->i2c_bus_lock);
+ for (i = retval = 0; retval == 0 && i < num; i++) {
+ if (msgs[i].flags & I2C_M_RD)
+ retval = ivtv_read(itv, msgs[i].addr, msgs[i].buf, msgs[i].len);
+ else {
+ /* if followed by a read, don't stop */
+ int stop = !(i + 1 < num && msgs[i + 1].flags == I2C_M_RD);
+
+ retval = ivtv_write(itv, msgs[i].addr, msgs[i].buf, msgs[i].len, stop);
+ }
+ }
+ mutex_unlock(&itv->i2c_bus_lock);
+ return retval ? retval : num;
+}
+
+/* Kernel i2c capabilities */
+static u32 ivtv_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static struct i2c_algorithm ivtv_algo = {
+ .master_xfer = ivtv_xfer,
+ .functionality = ivtv_functionality,
+};
+
+/* template for our-bit banger */
+static struct i2c_adapter ivtv_i2c_adap_hw_template = {
+ .name = "ivtv i2c driver",
+ .algo = &ivtv_algo,
+ .algo_data = NULL, /* filled from template */
+ .owner = THIS_MODULE,
+};
+
+static void ivtv_setscl_old(void *data, int state)
+{
+ struct ivtv *itv = (struct ivtv *)data;
+
+ if (state)
+ itv->i2c_state |= 0x01;
+ else
+ itv->i2c_state &= ~0x01;
+
+ /* write them out */
+ /* write bits are inverted */
+ write_reg(~itv->i2c_state, IVTV_REG_I2C_SETSCL_OFFSET);
+}
+
+static void ivtv_setsda_old(void *data, int state)
+{
+ struct ivtv *itv = (struct ivtv *)data;
+
+ if (state)
+ itv->i2c_state |= 0x01;
+ else
+ itv->i2c_state &= ~0x01;
+
+ /* write them out */
+ /* write bits are inverted */
+ write_reg(~itv->i2c_state, IVTV_REG_I2C_SETSDA_OFFSET);
+}
+
+static int ivtv_getscl_old(void *data)
+{
+ struct ivtv *itv = (struct ivtv *)data;
+
+ return read_reg(IVTV_REG_I2C_GETSCL_OFFSET) & 1;
+}
+
+static int ivtv_getsda_old(void *data)
+{
+ struct ivtv *itv = (struct ivtv *)data;
+
+ return read_reg(IVTV_REG_I2C_GETSDA_OFFSET) & 1;
+}
+
+/* template for i2c-bit-algo */
+static struct i2c_adapter ivtv_i2c_adap_template = {
+ .name = "ivtv i2c driver",
+ .algo = NULL, /* set by i2c-algo-bit */
+ .algo_data = NULL, /* filled from template */
+ .owner = THIS_MODULE,
+};
+
+#define IVTV_ALGO_BIT_TIMEOUT (2) /* seconds */
+
+static const struct i2c_algo_bit_data ivtv_i2c_algo_template = {
+ .setsda = ivtv_setsda_old,
+ .setscl = ivtv_setscl_old,
+ .getsda = ivtv_getsda_old,
+ .getscl = ivtv_getscl_old,
+ .udelay = IVTV_DEFAULT_I2C_CLOCK_PERIOD / 2, /* microseconds */
+ .timeout = IVTV_ALGO_BIT_TIMEOUT * HZ, /* jiffies */
+};
+
+static struct i2c_client ivtv_i2c_client_template = {
+ .name = "ivtv internal",
+};
+
+/* init + register i2c adapter */
+int init_ivtv_i2c(struct ivtv *itv)
+{
+ int retval;
+
+ IVTV_DEBUG_I2C("i2c init\n");
+
+ /* Sanity checks for the I2C hardware arrays. They must be the
+ * same size.
+ */
+ if (ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs)) {
+ IVTV_ERR("Mismatched I2C hardware arrays\n");
+ return -ENODEV;
+ }
+ if (itv->options.newi2c > 0) {
+ memcpy(&itv->i2c_adap, &ivtv_i2c_adap_hw_template,
+ sizeof(struct i2c_adapter));
+ } else {
+ memcpy(&itv->i2c_adap, &ivtv_i2c_adap_template,
+ sizeof(struct i2c_adapter));
+ memcpy(&itv->i2c_algo, &ivtv_i2c_algo_template,
+ sizeof(struct i2c_algo_bit_data));
+ }
+ itv->i2c_algo.udelay = itv->options.i2c_clock_period / 2;
+ itv->i2c_algo.data = itv;
+ itv->i2c_adap.algo_data = &itv->i2c_algo;
+
+ sprintf(itv->i2c_adap.name + strlen(itv->i2c_adap.name), " #%d",
+ itv->instance);
+ i2c_set_adapdata(&itv->i2c_adap, &itv->v4l2_dev);
+
+ memcpy(&itv->i2c_client, &ivtv_i2c_client_template,
+ sizeof(struct i2c_client));
+ itv->i2c_client.adapter = &itv->i2c_adap;
+ itv->i2c_adap.dev.parent = &itv->pdev->dev;
+
+ IVTV_DEBUG_I2C("setting scl and sda to 1\n");
+ ivtv_setscl(itv, 1);
+ ivtv_setsda(itv, 1);
+
+ if (itv->options.newi2c > 0)
+ retval = i2c_add_adapter(&itv->i2c_adap);
+ else
+ retval = i2c_bit_add_bus(&itv->i2c_adap);
+
+ return retval;
+}
+
+void exit_ivtv_i2c(struct ivtv *itv)
+{
+ IVTV_DEBUG_I2C("i2c exit\n");
+
+ i2c_del_adapter(&itv->i2c_adap);
+}
diff --git a/drivers/media/video/ivtv/ivtv-i2c.h b/drivers/media/video/ivtv/ivtv-i2c.h
new file mode 100644
index 00000000..7b9ec1cf
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-i2c.h
@@ -0,0 +1,32 @@
+/*
+ I2C functions
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef IVTV_I2C_H
+#define IVTV_I2C_H
+
+struct i2c_client *ivtv_i2c_new_ir_legacy(struct ivtv *itv);
+int ivtv_i2c_register(struct ivtv *itv, unsigned idx);
+struct v4l2_subdev *ivtv_find_hw(struct ivtv *itv, u32 hw);
+
+/* init + register i2c adapter */
+int init_ivtv_i2c(struct ivtv *itv);
+void exit_ivtv_i2c(struct ivtv *itv);
+
+#endif
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c
new file mode 100644
index 00000000..989e5569
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-ioctl.c
@@ -0,0 +1,1913 @@
+/*
+ ioctl system call
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-version.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-i2c.h"
+#include "ivtv-queue.h"
+#include "ivtv-fileops.h"
+#include "ivtv-vbi.h"
+#include "ivtv-routing.h"
+#include "ivtv-streams.h"
+#include "ivtv-yuv.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-gpio.h"
+#include "ivtv-controls.h"
+#include "ivtv-cards.h"
+#include <media/saa7127.h>
+#include <media/tveeprom.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-event.h>
+#include <linux/dvb/audio.h>
+
+u16 ivtv_service2vbi(int type)
+{
+ switch (type) {
+ case V4L2_SLICED_TELETEXT_B:
+ return IVTV_SLICED_TYPE_TELETEXT_B;
+ case V4L2_SLICED_CAPTION_525:
+ return IVTV_SLICED_TYPE_CAPTION_525;
+ case V4L2_SLICED_WSS_625:
+ return IVTV_SLICED_TYPE_WSS_625;
+ case V4L2_SLICED_VPS:
+ return IVTV_SLICED_TYPE_VPS;
+ default:
+ return 0;
+ }
+}
+
+static int valid_service_line(int field, int line, int is_pal)
+{
+ return (is_pal && line >= 6 && (line != 23 || field == 0)) ||
+ (!is_pal && line >= 10 && line < 22);
+}
+
+static u16 select_service_from_set(int field, int line, u16 set, int is_pal)
+{
+ u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525);
+ int i;
+
+ set = set & valid_set;
+ if (set == 0 || !valid_service_line(field, line, is_pal)) {
+ return 0;
+ }
+ if (!is_pal) {
+ if (line == 21 && (set & V4L2_SLICED_CAPTION_525))
+ return V4L2_SLICED_CAPTION_525;
+ }
+ else {
+ if (line == 16 && field == 0 && (set & V4L2_SLICED_VPS))
+ return V4L2_SLICED_VPS;
+ if (line == 23 && field == 0 && (set & V4L2_SLICED_WSS_625))
+ return V4L2_SLICED_WSS_625;
+ if (line == 23)
+ return 0;
+ }
+ for (i = 0; i < 32; i++) {
+ if ((1 << i) & set)
+ return 1 << i;
+ }
+ return 0;
+}
+
+void ivtv_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
+{
+ u16 set = fmt->service_set;
+ int f, l;
+
+ fmt->service_set = 0;
+ for (f = 0; f < 2; f++) {
+ for (l = 0; l < 24; l++) {
+ fmt->service_lines[f][l] = select_service_from_set(f, l, set, is_pal);
+ }
+ }
+}
+
+static void check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
+{
+ int f, l;
+
+ for (f = 0; f < 2; f++) {
+ for (l = 0; l < 24; l++) {
+ fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal);
+ }
+ }
+}
+
+u16 ivtv_get_service_set(struct v4l2_sliced_vbi_format *fmt)
+{
+ int f, l;
+ u16 set = 0;
+
+ for (f = 0; f < 2; f++) {
+ for (l = 0; l < 24; l++) {
+ set |= fmt->service_lines[f][l];
+ }
+ }
+ return set;
+}
+
+void ivtv_set_osd_alpha(struct ivtv *itv)
+{
+ ivtv_vapi(itv, CX2341X_OSD_SET_GLOBAL_ALPHA, 3,
+ itv->osd_global_alpha_state, itv->osd_global_alpha, !itv->osd_local_alpha_state);
+ ivtv_vapi(itv, CX2341X_OSD_SET_CHROMA_KEY, 2, itv->osd_chroma_key_state, itv->osd_chroma_key);
+}
+
+int ivtv_set_speed(struct ivtv *itv, int speed)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ struct ivtv_stream *s;
+ int single_step = (speed == 1 || speed == -1);
+ DEFINE_WAIT(wait);
+
+ if (speed == 0) speed = 1000;
+
+ /* No change? */
+ if (speed == itv->speed && !single_step)
+ return 0;
+
+ s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
+
+ if (single_step && (speed < 0) == (itv->speed < 0)) {
+ /* Single step video and no need to change direction */
+ ivtv_vapi(itv, CX2341X_DEC_STEP_VIDEO, 1, 0);
+ itv->speed = speed;
+ return 0;
+ }
+ if (single_step)
+ /* Need to change direction */
+ speed = speed < 0 ? -1000 : 1000;
+
+ data[0] = (speed > 1000 || speed < -1000) ? 0x80000000 : 0;
+ data[0] |= (speed > 1000 || speed < -1500) ? 0x40000000 : 0;
+ data[1] = (speed < 0);
+ data[2] = speed < 0 ? 3 : 7;
+ data[3] = v4l2_ctrl_g_ctrl(itv->cxhdl.video_b_frames);
+ data[4] = (speed == 1500 || speed == 500) ? itv->speed_mute_audio : 0;
+ data[5] = 0;
+ data[6] = 0;
+
+ if (speed == 1500 || speed == -1500) data[0] |= 1;
+ else if (speed == 2000 || speed == -2000) data[0] |= 2;
+ else if (speed > -1000 && speed < 0) data[0] |= (-1000 / speed);
+ else if (speed < 1000 && speed > 0) data[0] |= (1000 / speed);
+
+ /* If not decoding, just change speed setting */
+ if (atomic_read(&itv->decoding) > 0) {
+ int got_sig = 0;
+
+ /* Stop all DMA and decoding activity */
+ ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1, 0);
+
+ /* Wait for any DMA to finish */
+ mutex_unlock(&itv->serialize_lock);
+ prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
+ while (test_bit(IVTV_F_I_DMA, &itv->i_flags)) {
+ got_sig = signal_pending(current);
+ if (got_sig)
+ break;
+ got_sig = 0;
+ schedule();
+ }
+ finish_wait(&itv->dma_waitq, &wait);
+ mutex_lock(&itv->serialize_lock);
+ if (got_sig)
+ return -EINTR;
+
+ /* Change Speed safely */
+ ivtv_api(itv, CX2341X_DEC_SET_PLAYBACK_SPEED, 7, data);
+ IVTV_DEBUG_INFO("Setting Speed to 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ data[0], data[1], data[2], data[3], data[4], data[5], data[6]);
+ }
+ if (single_step) {
+ speed = (speed < 0) ? -1 : 1;
+ ivtv_vapi(itv, CX2341X_DEC_STEP_VIDEO, 1, 0);
+ }
+ itv->speed = speed;
+ return 0;
+}
+
+static int ivtv_validate_speed(int cur_speed, int new_speed)
+{
+ int fact = new_speed < 0 ? -1 : 1;
+ int s;
+
+ if (cur_speed == 0)
+ cur_speed = 1000;
+ if (new_speed < 0)
+ new_speed = -new_speed;
+ if (cur_speed < 0)
+ cur_speed = -cur_speed;
+
+ if (cur_speed <= new_speed) {
+ if (new_speed > 1500)
+ return fact * 2000;
+ if (new_speed > 1000)
+ return fact * 1500;
+ }
+ else {
+ if (new_speed >= 2000)
+ return fact * 2000;
+ if (new_speed >= 1500)
+ return fact * 1500;
+ if (new_speed >= 1000)
+ return fact * 1000;
+ }
+ if (new_speed == 0)
+ return 1000;
+ if (new_speed == 1 || new_speed == 1000)
+ return fact * new_speed;
+
+ s = new_speed;
+ new_speed = 1000 / new_speed;
+ if (1000 / cur_speed == new_speed)
+ new_speed += (cur_speed < s) ? -1 : 1;
+ if (new_speed > 60) return 1000 / (fact * 60);
+ return 1000 / (fact * new_speed);
+}
+
+static int ivtv_video_command(struct ivtv *itv, struct ivtv_open_id *id,
+ struct v4l2_decoder_cmd *dc, int try)
+{
+ struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
+
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+
+ switch (dc->cmd) {
+ case V4L2_DEC_CMD_START: {
+ dc->flags &= V4L2_DEC_CMD_START_MUTE_AUDIO;
+ dc->start.speed = ivtv_validate_speed(itv->speed, dc->start.speed);
+ if (dc->start.speed < 0)
+ dc->start.format = V4L2_DEC_START_FMT_GOP;
+ else
+ dc->start.format = V4L2_DEC_START_FMT_NONE;
+ if (dc->start.speed != 500 && dc->start.speed != 1500)
+ dc->flags = dc->start.speed == 1000 ? 0 :
+ V4L2_DEC_CMD_START_MUTE_AUDIO;
+ if (try) break;
+
+ itv->speed_mute_audio = dc->flags & V4L2_DEC_CMD_START_MUTE_AUDIO;
+ if (ivtv_set_output_mode(itv, OUT_MPG) != OUT_MPG)
+ return -EBUSY;
+ if (test_and_clear_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags)) {
+ /* forces ivtv_set_speed to be called */
+ itv->speed = 0;
+ }
+ return ivtv_start_decoding(id, dc->start.speed);
+ }
+
+ case V4L2_DEC_CMD_STOP:
+ dc->flags &= V4L2_DEC_CMD_STOP_IMMEDIATELY | V4L2_DEC_CMD_STOP_TO_BLACK;
+ if (dc->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY)
+ dc->stop.pts = 0;
+ if (try) break;
+ if (atomic_read(&itv->decoding) == 0)
+ return 0;
+ if (itv->output_mode != OUT_MPG)
+ return -EBUSY;
+
+ itv->output_mode = OUT_NONE;
+ return ivtv_stop_v4l2_decode_stream(s, dc->flags, dc->stop.pts);
+
+ case V4L2_DEC_CMD_PAUSE:
+ dc->flags &= V4L2_DEC_CMD_PAUSE_TO_BLACK;
+ if (try) break;
+ if (itv->output_mode != OUT_MPG)
+ return -EBUSY;
+ if (atomic_read(&itv->decoding) > 0) {
+ ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1,
+ (dc->flags & V4L2_DEC_CMD_PAUSE_TO_BLACK) ? 1 : 0);
+ set_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags);
+ }
+ break;
+
+ case V4L2_DEC_CMD_RESUME:
+ dc->flags = 0;
+ if (try) break;
+ if (itv->output_mode != OUT_MPG)
+ return -EBUSY;
+ if (test_and_clear_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags)) {
+ int speed = itv->speed;
+ itv->speed = 0;
+ return ivtv_start_decoding(id, speed);
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int ivtv_g_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+
+ vbifmt->reserved[0] = 0;
+ vbifmt->reserved[1] = 0;
+ if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT))
+ return -EINVAL;
+ vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+ if (itv->is_60hz) {
+ vbifmt->service_lines[0][21] = V4L2_SLICED_CAPTION_525;
+ vbifmt->service_lines[1][21] = V4L2_SLICED_CAPTION_525;
+ } else {
+ vbifmt->service_lines[0][23] = V4L2_SLICED_WSS_625;
+ vbifmt->service_lines[0][16] = V4L2_SLICED_VPS;
+ }
+ vbifmt->service_set = ivtv_get_service_set(vbifmt);
+ return 0;
+}
+
+static int ivtv_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+
+ pixfmt->width = itv->cxhdl.width;
+ pixfmt->height = itv->cxhdl.height;
+ pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ pixfmt->field = V4L2_FIELD_INTERLACED;
+ pixfmt->priv = 0;
+ if (id->type == IVTV_ENC_STREAM_TYPE_YUV) {
+ pixfmt->pixelformat = V4L2_PIX_FMT_HM12;
+ /* YUV size is (Y=(h*720) + UV=(h*(720/2))) */
+ pixfmt->sizeimage = pixfmt->height * 720 * 3 / 2;
+ pixfmt->bytesperline = 720;
+ } else {
+ pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;
+ pixfmt->sizeimage = 128 * 1024;
+ pixfmt->bytesperline = 0;
+ }
+ return 0;
+}
+
+static int ivtv_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+ struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi;
+
+ vbifmt->sampling_rate = 27000000;
+ vbifmt->offset = 248;
+ vbifmt->samples_per_line = itv->vbi.raw_decoder_line_size - 4;
+ vbifmt->sample_format = V4L2_PIX_FMT_GREY;
+ vbifmt->start[0] = itv->vbi.start[0];
+ vbifmt->start[1] = itv->vbi.start[1];
+ vbifmt->count[0] = vbifmt->count[1] = itv->vbi.count;
+ vbifmt->flags = 0;
+ vbifmt->reserved[0] = 0;
+ vbifmt->reserved[1] = 0;
+ return 0;
+}
+
+static int ivtv_g_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+
+ vbifmt->reserved[0] = 0;
+ vbifmt->reserved[1] = 0;
+ vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+
+ if (id->type == IVTV_DEC_STREAM_TYPE_VBI) {
+ vbifmt->service_set = itv->is_50hz ? V4L2_SLICED_VBI_625 :
+ V4L2_SLICED_VBI_525;
+ ivtv_expand_service_set(vbifmt, itv->is_50hz);
+ return 0;
+ }
+
+ v4l2_subdev_call(itv->sd_video, vbi, g_sliced_fmt, vbifmt);
+ vbifmt->service_set = ivtv_get_service_set(vbifmt);
+ return 0;
+}
+
+static int ivtv_g_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+ pixfmt->width = itv->main_rect.width;
+ pixfmt->height = itv->main_rect.height;
+ pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ pixfmt->field = V4L2_FIELD_INTERLACED;
+ pixfmt->priv = 0;
+ if (id->type == IVTV_DEC_STREAM_TYPE_YUV) {
+ switch (itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) {
+ case IVTV_YUV_MODE_INTERLACED:
+ pixfmt->field = (itv->yuv_info.lace_mode & IVTV_YUV_SYNC_MASK) ?
+ V4L2_FIELD_INTERLACED_BT : V4L2_FIELD_INTERLACED_TB;
+ break;
+ case IVTV_YUV_MODE_PROGRESSIVE:
+ pixfmt->field = V4L2_FIELD_NONE;
+ break;
+ default:
+ pixfmt->field = V4L2_FIELD_ANY;
+ break;
+ }
+ pixfmt->pixelformat = V4L2_PIX_FMT_HM12;
+ pixfmt->bytesperline = 720;
+ pixfmt->width = itv->yuv_info.v4l2_src_w;
+ pixfmt->height = itv->yuv_info.v4l2_src_h;
+ /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
+ pixfmt->sizeimage =
+ 1080 * ((pixfmt->height + 31) & ~31);
+ } else {
+ pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;
+ pixfmt->sizeimage = 128 * 1024;
+ pixfmt->bytesperline = 0;
+ }
+ return 0;
+}
+
+static int ivtv_g_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+ struct v4l2_window *winfmt = &fmt->fmt.win;
+
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+ winfmt->chromakey = itv->osd_chroma_key;
+ winfmt->global_alpha = itv->osd_global_alpha;
+ winfmt->field = V4L2_FIELD_INTERLACED;
+ winfmt->clips = NULL;
+ winfmt->clipcount = 0;
+ winfmt->bitmap = NULL;
+ winfmt->w.top = winfmt->w.left = 0;
+ winfmt->w.width = itv->osd_rect.width;
+ winfmt->w.height = itv->osd_rect.height;
+ return 0;
+}
+
+static int ivtv_try_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ return ivtv_g_fmt_sliced_vbi_out(file, fh, fmt);
+}
+
+static int ivtv_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+ int w = fmt->fmt.pix.width;
+ int h = fmt->fmt.pix.height;
+ int min_h = 2;
+
+ w = min(w, 720);
+ w = max(w, 2);
+ if (id->type == IVTV_ENC_STREAM_TYPE_YUV) {
+ /* YUV height must be a multiple of 32 */
+ h &= ~0x1f;
+ min_h = 32;
+ }
+ h = min(h, itv->is_50hz ? 576 : 480);
+ h = max(h, min_h);
+ ivtv_g_fmt_vid_cap(file, fh, fmt);
+ fmt->fmt.pix.width = w;
+ fmt->fmt.pix.height = h;
+ return 0;
+}
+
+static int ivtv_try_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ return ivtv_g_fmt_vbi_cap(file, fh, fmt);
+}
+
+static int ivtv_try_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+
+ if (id->type == IVTV_DEC_STREAM_TYPE_VBI)
+ return ivtv_g_fmt_sliced_vbi_cap(file, fh, fmt);
+
+ /* set sliced VBI capture format */
+ vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+ vbifmt->reserved[0] = 0;
+ vbifmt->reserved[1] = 0;
+
+ if (vbifmt->service_set)
+ ivtv_expand_service_set(vbifmt, itv->is_50hz);
+ check_service_set(vbifmt, itv->is_50hz);
+ vbifmt->service_set = ivtv_get_service_set(vbifmt);
+ return 0;
+}
+
+static int ivtv_try_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct ivtv_open_id *id = fh2id(fh);
+ s32 w = fmt->fmt.pix.width;
+ s32 h = fmt->fmt.pix.height;
+ int field = fmt->fmt.pix.field;
+ int ret = ivtv_g_fmt_vid_out(file, fh, fmt);
+
+ w = min(w, 720);
+ w = max(w, 2);
+ /* Why can the height be 576 even when the output is NTSC?
+
+ Internally the buffers of the PVR350 are always set to 720x576. The
+ decoded video frame will always be placed in the top left corner of
+ this buffer. For any video which is not 720x576, the buffer will
+ then be cropped to remove the unused right and lower areas, with
+ the remaining image being scaled by the hardware to fit the display
+ area. The video can be scaled both up and down, so a 720x480 video
+ can be displayed full-screen on PAL and a 720x576 video can be
+ displayed without cropping on NTSC.
+
+ Note that the scaling only occurs on the video stream, the osd
+ resolution is locked to the broadcast standard and not scaled.
+
+ Thanks to Ian Armstrong for this explanation. */
+ h = min(h, 576);
+ h = max(h, 2);
+ if (id->type == IVTV_DEC_STREAM_TYPE_YUV)
+ fmt->fmt.pix.field = field;
+ fmt->fmt.pix.width = w;
+ fmt->fmt.pix.height = h;
+ return ret;
+}
+
+static int ivtv_try_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+ u32 chromakey = fmt->fmt.win.chromakey;
+ u8 global_alpha = fmt->fmt.win.global_alpha;
+
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+ ivtv_g_fmt_vid_out_overlay(file, fh, fmt);
+ fmt->fmt.win.chromakey = chromakey;
+ fmt->fmt.win.global_alpha = global_alpha;
+ return 0;
+}
+
+static int ivtv_s_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ return ivtv_g_fmt_sliced_vbi_out(file, fh, fmt);
+}
+
+static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+ struct v4l2_mbus_framefmt mbus_fmt;
+ int ret = ivtv_try_fmt_vid_cap(file, fh, fmt);
+ int w = fmt->fmt.pix.width;
+ int h = fmt->fmt.pix.height;
+
+ if (ret)
+ return ret;
+
+ if (itv->cxhdl.width == w && itv->cxhdl.height == h)
+ return 0;
+
+ if (atomic_read(&itv->capturing) > 0)
+ return -EBUSY;
+
+ itv->cxhdl.width = w;
+ itv->cxhdl.height = h;
+ if (v4l2_ctrl_g_ctrl(itv->cxhdl.video_encoding) == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
+ fmt->fmt.pix.width /= 2;
+ mbus_fmt.width = fmt->fmt.pix.width;
+ mbus_fmt.height = h;
+ mbus_fmt.code = V4L2_MBUS_FMT_FIXED;
+ v4l2_subdev_call(itv->sd_video, video, s_mbus_fmt, &mbus_fmt);
+ return ivtv_g_fmt_vid_cap(file, fh, fmt);
+}
+
+static int ivtv_s_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ if (!ivtv_raw_vbi(itv) && atomic_read(&itv->capturing) > 0)
+ return -EBUSY;
+ itv->vbi.sliced_in->service_set = 0;
+ itv->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ v4l2_subdev_call(itv->sd_video, vbi, s_raw_fmt, &fmt->fmt.vbi);
+ return ivtv_g_fmt_vbi_cap(file, fh, fmt);
+}
+
+static int ivtv_s_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+ int ret = ivtv_try_fmt_sliced_vbi_cap(file, fh, fmt);
+
+ if (ret || id->type == IVTV_DEC_STREAM_TYPE_VBI)
+ return ret;
+
+ check_service_set(vbifmt, itv->is_50hz);
+ if (ivtv_raw_vbi(itv) && atomic_read(&itv->capturing) > 0)
+ return -EBUSY;
+ itv->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
+ v4l2_subdev_call(itv->sd_video, vbi, s_sliced_fmt, vbifmt);
+ memcpy(itv->vbi.sliced_in, vbifmt, sizeof(*itv->vbi.sliced_in));
+ return 0;
+}
+
+static int ivtv_s_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ int ret = ivtv_try_fmt_vid_out(file, fh, fmt);
+
+ if (ret)
+ return ret;
+
+ if (id->type != IVTV_DEC_STREAM_TYPE_YUV)
+ return 0;
+
+ /* Return now if we already have some frame data */
+ if (yi->stream_size)
+ return -EBUSY;
+
+ yi->v4l2_src_w = fmt->fmt.pix.width;
+ yi->v4l2_src_h = fmt->fmt.pix.height;
+
+ switch (fmt->fmt.pix.field) {
+ case V4L2_FIELD_NONE:
+ yi->lace_mode = IVTV_YUV_MODE_PROGRESSIVE;
+ break;
+ case V4L2_FIELD_ANY:
+ yi->lace_mode = IVTV_YUV_MODE_AUTO;
+ break;
+ case V4L2_FIELD_INTERLACED_BT:
+ yi->lace_mode =
+ IVTV_YUV_MODE_INTERLACED|IVTV_YUV_SYNC_ODD;
+ break;
+ case V4L2_FIELD_INTERLACED_TB:
+ default:
+ yi->lace_mode = IVTV_YUV_MODE_INTERLACED;
+ break;
+ }
+ yi->lace_sync_field = (yi->lace_mode & IVTV_YUV_SYNC_MASK) == IVTV_YUV_SYNC_EVEN ? 0 : 1;
+
+ if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags))
+ itv->dma_data_req_size =
+ 1080 * ((yi->v4l2_src_h + 31) & ~31);
+
+ return 0;
+}
+
+static int ivtv_s_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+ int ret = ivtv_try_fmt_vid_out_overlay(file, fh, fmt);
+
+ if (ret == 0) {
+ itv->osd_chroma_key = fmt->fmt.win.chromakey;
+ itv->osd_global_alpha = fmt->fmt.win.global_alpha;
+ ivtv_set_osd_alpha(itv);
+ }
+ return ret;
+}
+
+static int ivtv_g_chip_ident(struct file *file, void *fh, struct v4l2_dbg_chip_ident *chip)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+ if (chip->match.type == V4L2_CHIP_MATCH_HOST) {
+ if (v4l2_chip_match_host(&chip->match))
+ chip->ident = itv->has_cx23415 ? V4L2_IDENT_CX23415 : V4L2_IDENT_CX23416;
+ return 0;
+ }
+ if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
+ chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+ /* TODO: is this correct? */
+ return ivtv_call_all_err(itv, core, g_chip_ident, chip);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+ struct v4l2_dbg_register *regs = arg;
+ volatile u8 __iomem *reg_start;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (regs->reg >= IVTV_REG_OFFSET && regs->reg < IVTV_REG_OFFSET + IVTV_REG_SIZE)
+ reg_start = itv->reg_mem - IVTV_REG_OFFSET;
+ else if (itv->has_cx23415 && regs->reg >= IVTV_DECODER_OFFSET &&
+ regs->reg < IVTV_DECODER_OFFSET + IVTV_DECODER_SIZE)
+ reg_start = itv->dec_mem - IVTV_DECODER_OFFSET;
+ else if (regs->reg < IVTV_ENCODER_SIZE)
+ reg_start = itv->enc_mem;
+ else
+ return -EINVAL;
+
+ regs->size = 4;
+ if (cmd == VIDIOC_DBG_G_REGISTER)
+ regs->val = readl(regs->reg + reg_start);
+ else
+ writel(regs->val, regs->reg + reg_start);
+ return 0;
+}
+
+static int ivtv_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ if (v4l2_chip_match_host(&reg->match))
+ return ivtv_itvc(itv, VIDIOC_DBG_G_REGISTER, reg);
+ /* TODO: subdev errors should not be ignored, this should become a
+ subdev helper function. */
+ ivtv_call_all(itv, core, g_register, reg);
+ return 0;
+}
+
+static int ivtv_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ if (v4l2_chip_match_host(&reg->match))
+ return ivtv_itvc(itv, VIDIOC_DBG_S_REGISTER, reg);
+ /* TODO: subdev errors should not be ignored, this should become a
+ subdev helper function. */
+ ivtv_call_all(itv, core, s_register, reg);
+ return 0;
+}
+#endif
+
+static int ivtv_querycap(struct file *file, void *fh, struct v4l2_capability *vcap)
+{
+ struct ivtv_open_id *id = fh2id(file->private_data);
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+
+ strlcpy(vcap->driver, IVTV_DRIVER_NAME, sizeof(vcap->driver));
+ strlcpy(vcap->card, itv->card_name, sizeof(vcap->card));
+ snprintf(vcap->bus_info, sizeof(vcap->bus_info), "PCI:%s", pci_name(itv->pdev));
+ vcap->capabilities = itv->v4l2_cap | V4L2_CAP_DEVICE_CAPS;
+ vcap->device_caps = s->caps;
+ return 0;
+}
+
+static int ivtv_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ return ivtv_get_audio_input(itv, vin->index, vin);
+}
+
+static int ivtv_g_audio(struct file *file, void *fh, struct v4l2_audio *vin)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ vin->index = itv->audio_input;
+ return ivtv_get_audio_input(itv, vin->index, vin);
+}
+
+static int ivtv_s_audio(struct file *file, void *fh, struct v4l2_audio *vout)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ if (vout->index >= itv->nof_audio_inputs)
+ return -EINVAL;
+
+ itv->audio_input = vout->index;
+ ivtv_audio_set_io(itv);
+
+ return 0;
+}
+
+static int ivtv_enumaudout(struct file *file, void *fh, struct v4l2_audioout *vin)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ /* set it to defaults from our table */
+ return ivtv_get_audio_output(itv, vin->index, vin);
+}
+
+static int ivtv_g_audout(struct file *file, void *fh, struct v4l2_audioout *vin)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ vin->index = 0;
+ return ivtv_get_audio_output(itv, vin->index, vin);
+}
+
+static int ivtv_s_audout(struct file *file, void *fh, struct v4l2_audioout *vout)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ return ivtv_get_audio_output(itv, vout->index, vout);
+}
+
+static int ivtv_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ /* set it to defaults from our table */
+ return ivtv_get_input(itv, vin->index, vin);
+}
+
+static int ivtv_enum_output(struct file *file, void *fh, struct v4l2_output *vout)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ return ivtv_get_output(itv, vout->index, vout);
+}
+
+static int ivtv_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap)
+{
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ int streamtype;
+
+ streamtype = id->type;
+
+ if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+ cropcap->bounds.top = cropcap->bounds.left = 0;
+ cropcap->bounds.width = 720;
+ if (cropcap->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ cropcap->bounds.height = itv->is_50hz ? 576 : 480;
+ cropcap->pixelaspect.numerator = itv->is_50hz ? 59 : 10;
+ cropcap->pixelaspect.denominator = itv->is_50hz ? 54 : 11;
+ } else if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
+ if (yi->track_osd) {
+ cropcap->bounds.width = yi->osd_full_w;
+ cropcap->bounds.height = yi->osd_full_h;
+ } else {
+ cropcap->bounds.width = 720;
+ cropcap->bounds.height =
+ itv->is_out_50hz ? 576 : 480;
+ }
+ cropcap->pixelaspect.numerator = itv->is_out_50hz ? 59 : 10;
+ cropcap->pixelaspect.denominator = itv->is_out_50hz ? 54 : 11;
+ } else {
+ cropcap->bounds.height = itv->is_out_50hz ? 576 : 480;
+ cropcap->pixelaspect.numerator = itv->is_out_50hz ? 59 : 10;
+ cropcap->pixelaspect.denominator = itv->is_out_50hz ? 54 : 11;
+ }
+ cropcap->defrect = cropcap->bounds;
+ return 0;
+}
+
+static int ivtv_s_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+{
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ int streamtype;
+
+ streamtype = id->type;
+
+ if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
+ if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
+ yi->main_rect = crop->c;
+ return 0;
+ } else {
+ if (!ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
+ crop->c.width, crop->c.height, crop->c.left, crop->c.top)) {
+ itv->main_rect = crop->c;
+ return 0;
+ }
+ }
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int ivtv_g_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+{
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ int streamtype;
+
+ streamtype = id->type;
+
+ if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
+ if (streamtype == IVTV_DEC_STREAM_TYPE_YUV)
+ crop->c = yi->main_rect;
+ else
+ crop->c = itv->main_rect;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int ivtv_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
+{
+ static struct v4l2_fmtdesc formats[] = {
+ { 0, 0, 0,
+ "HM12 (YUV 4:2:0)", V4L2_PIX_FMT_HM12,
+ { 0, 0, 0, 0 }
+ },
+ { 1, 0, V4L2_FMT_FLAG_COMPRESSED,
+ "MPEG", V4L2_PIX_FMT_MPEG,
+ { 0, 0, 0, 0 }
+ }
+ };
+ enum v4l2_buf_type type = fmt->type;
+
+ if (fmt->index > 1)
+ return -EINVAL;
+
+ *fmt = formats[fmt->index];
+ fmt->type = type;
+ return 0;
+}
+
+static int ivtv_enum_fmt_vid_out(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ static struct v4l2_fmtdesc formats[] = {
+ { 0, 0, 0,
+ "HM12 (YUV 4:2:0)", V4L2_PIX_FMT_HM12,
+ { 0, 0, 0, 0 }
+ },
+ { 1, 0, V4L2_FMT_FLAG_COMPRESSED,
+ "MPEG", V4L2_PIX_FMT_MPEG,
+ { 0, 0, 0, 0 }
+ }
+ };
+ enum v4l2_buf_type type = fmt->type;
+
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+
+ if (fmt->index > 1)
+ return -EINVAL;
+
+ *fmt = formats[fmt->index];
+ fmt->type = type;
+
+ return 0;
+}
+
+static int ivtv_g_input(struct file *file, void *fh, unsigned int *i)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ *i = itv->active_input;
+
+ return 0;
+}
+
+int ivtv_s_input(struct file *file, void *fh, unsigned int inp)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ if (inp < 0 || inp >= itv->nof_inputs)
+ return -EINVAL;
+
+ if (inp == itv->active_input) {
+ IVTV_DEBUG_INFO("Input unchanged\n");
+ return 0;
+ }
+
+ if (atomic_read(&itv->capturing) > 0) {
+ return -EBUSY;
+ }
+
+ IVTV_DEBUG_INFO("Changing input from %d to %d\n",
+ itv->active_input, inp);
+
+ itv->active_input = inp;
+ /* Set the audio input to whatever is appropriate for the
+ input type. */
+ itv->audio_input = itv->card->video_inputs[inp].audio_index;
+
+ /* prevent others from messing with the streams until
+ we're finished changing inputs. */
+ ivtv_mute(itv);
+ ivtv_video_set_io(itv);
+ ivtv_audio_set_io(itv);
+ ivtv_unmute(itv);
+
+ return 0;
+}
+
+static int ivtv_g_output(struct file *file, void *fh, unsigned int *i)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+
+ *i = itv->active_output;
+
+ return 0;
+}
+
+static int ivtv_s_output(struct file *file, void *fh, unsigned int outp)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ if (outp >= itv->card->nof_outputs)
+ return -EINVAL;
+
+ if (outp == itv->active_output) {
+ IVTV_DEBUG_INFO("Output unchanged\n");
+ return 0;
+ }
+ IVTV_DEBUG_INFO("Changing output from %d to %d\n",
+ itv->active_output, outp);
+
+ itv->active_output = outp;
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing,
+ SAA7127_INPUT_TYPE_NORMAL,
+ itv->card->video_outputs[outp].video_output, 0);
+
+ return 0;
+}
+
+static int ivtv_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ if (vf->tuner != 0)
+ return -EINVAL;
+
+ ivtv_call_all(itv, tuner, g_frequency, vf);
+ return 0;
+}
+
+int ivtv_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ if (vf->tuner != 0)
+ return -EINVAL;
+
+ ivtv_mute(itv);
+ IVTV_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency);
+ ivtv_call_all(itv, tuner, s_frequency, vf);
+ ivtv_unmute(itv);
+ return 0;
+}
+
+static int ivtv_g_std(struct file *file, void *fh, v4l2_std_id *std)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ *std = itv->std;
+ return 0;
+}
+
+void ivtv_s_std_enc(struct ivtv *itv, v4l2_std_id *std)
+{
+ itv->std = *std;
+ itv->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0;
+ itv->is_50hz = !itv->is_60hz;
+ cx2341x_handler_set_50hz(&itv->cxhdl, itv->is_50hz);
+ itv->cxhdl.width = 720;
+ itv->cxhdl.height = itv->is_50hz ? 576 : 480;
+ itv->vbi.count = itv->is_50hz ? 18 : 12;
+ itv->vbi.start[0] = itv->is_50hz ? 6 : 10;
+ itv->vbi.start[1] = itv->is_50hz ? 318 : 273;
+
+ if (itv->hw_flags & IVTV_HW_CX25840)
+ itv->vbi.sliced_decoder_line_size = itv->is_60hz ? 272 : 284;
+
+ /* Tuner */
+ ivtv_call_all(itv, core, s_std, itv->std);
+}
+
+void ivtv_s_std_dec(struct ivtv *itv, v4l2_std_id *std)
+{
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ DEFINE_WAIT(wait);
+ int f;
+
+ /* set display standard */
+ itv->std_out = *std;
+ itv->is_out_60hz = (*std & V4L2_STD_525_60) ? 1 : 0;
+ itv->is_out_50hz = !itv->is_out_60hz;
+ ivtv_call_all(itv, video, s_std_output, itv->std_out);
+
+ /*
+ * The next firmware call is time sensitive. Time it to
+ * avoid risk of a hard lock, by trying to ensure the call
+ * happens within the first 100 lines of the top field.
+ * Make 4 attempts to sync to the decoder before giving up.
+ */
+ mutex_unlock(&itv->serialize_lock);
+ for (f = 0; f < 4; f++) {
+ prepare_to_wait(&itv->vsync_waitq, &wait,
+ TASK_UNINTERRUPTIBLE);
+ if ((read_reg(IVTV_REG_DEC_LINE_FIELD) >> 16) < 100)
+ break;
+ schedule_timeout(msecs_to_jiffies(25));
+ }
+ finish_wait(&itv->vsync_waitq, &wait);
+ mutex_lock(&itv->serialize_lock);
+
+ if (f == 4)
+ IVTV_WARN("Mode change failed to sync to decoder\n");
+
+ ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz);
+ itv->main_rect.left = 0;
+ itv->main_rect.top = 0;
+ itv->main_rect.width = 720;
+ itv->main_rect.height = itv->is_out_50hz ? 576 : 480;
+ ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
+ 720, itv->main_rect.height, 0, 0);
+ yi->main_rect = itv->main_rect;
+ if (!itv->osd_info) {
+ yi->osd_full_w = 720;
+ yi->osd_full_h = itv->is_out_50hz ? 576 : 480;
+ }
+}
+
+int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ if ((*std & V4L2_STD_ALL) == 0)
+ return -EINVAL;
+
+ if (*std == itv->std)
+ return 0;
+
+ if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ||
+ atomic_read(&itv->capturing) > 0 ||
+ atomic_read(&itv->decoding) > 0) {
+ /* Switching standard would mess with already running
+ streams, prevent that by returning EBUSY. */
+ return -EBUSY;
+ }
+
+ IVTV_DEBUG_INFO("Switching standard to %llx.\n",
+ (unsigned long long)itv->std);
+
+ ivtv_s_std_enc(itv, std);
+ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)
+ ivtv_s_std_dec(itv, std);
+
+ return 0;
+}
+
+static int ivtv_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
+{
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+
+ if (vt->index != 0)
+ return -EINVAL;
+
+ ivtv_call_all(itv, tuner, s_tuner, vt);
+
+ return 0;
+}
+
+static int ivtv_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ if (vt->index != 0)
+ return -EINVAL;
+
+ ivtv_call_all(itv, tuner, g_tuner, vt);
+
+ if (vt->type == V4L2_TUNER_RADIO)
+ strlcpy(vt->name, "ivtv Radio Tuner", sizeof(vt->name));
+ else
+ strlcpy(vt->name, "ivtv TV Tuner", sizeof(vt->name));
+ return 0;
+}
+
+static int ivtv_g_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_sliced_vbi_cap *cap)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+ int set = itv->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525;
+ int f, l;
+
+ if (cap->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
+ for (f = 0; f < 2; f++) {
+ for (l = 0; l < 24; l++) {
+ if (valid_service_line(f, l, itv->is_50hz))
+ cap->service_lines[f][l] = set;
+ }
+ }
+ } else if (cap->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) {
+ if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT))
+ return -EINVAL;
+ if (itv->is_60hz) {
+ cap->service_lines[0][21] = V4L2_SLICED_CAPTION_525;
+ cap->service_lines[1][21] = V4L2_SLICED_CAPTION_525;
+ } else {
+ cap->service_lines[0][23] = V4L2_SLICED_WSS_625;
+ cap->service_lines[0][16] = V4L2_SLICED_VPS;
+ }
+ } else {
+ return -EINVAL;
+ }
+
+ set = 0;
+ for (f = 0; f < 2; f++)
+ for (l = 0; l < 24; l++)
+ set |= cap->service_lines[f][l];
+ cap->service_set = set;
+ return 0;
+}
+
+static int ivtv_g_enc_index(struct file *file, void *fh, struct v4l2_enc_idx *idx)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+ struct v4l2_enc_idx_entry *e = idx->entry;
+ int entries;
+ int i;
+
+ entries = (itv->pgm_info_write_idx + IVTV_MAX_PGM_INDEX - itv->pgm_info_read_idx) %
+ IVTV_MAX_PGM_INDEX;
+ if (entries > V4L2_ENC_IDX_ENTRIES)
+ entries = V4L2_ENC_IDX_ENTRIES;
+ idx->entries = 0;
+ for (i = 0; i < entries; i++) {
+ *e = itv->pgm_info[(itv->pgm_info_read_idx + i) % IVTV_MAX_PGM_INDEX];
+ if ((e->flags & V4L2_ENC_IDX_FRAME_MASK) <= V4L2_ENC_IDX_FRAME_B) {
+ idx->entries++;
+ e++;
+ }
+ }
+ itv->pgm_info_read_idx = (itv->pgm_info_read_idx + idx->entries) % IVTV_MAX_PGM_INDEX;
+ return 0;
+}
+
+static int ivtv_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *enc)
+{
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+
+
+ switch (enc->cmd) {
+ case V4L2_ENC_CMD_START:
+ IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");
+ enc->flags = 0;
+ return ivtv_start_capture(id);
+
+ case V4L2_ENC_CMD_STOP:
+ IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");
+ enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
+ ivtv_stop_capture(id, enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END);
+ return 0;
+
+ case V4L2_ENC_CMD_PAUSE:
+ IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");
+ enc->flags = 0;
+
+ if (!atomic_read(&itv->capturing))
+ return -EPERM;
+ if (test_and_set_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags))
+ return 0;
+
+ ivtv_mute(itv);
+ ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 0);
+ break;
+
+ case V4L2_ENC_CMD_RESUME:
+ IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");
+ enc->flags = 0;
+
+ if (!atomic_read(&itv->capturing))
+ return -EPERM;
+
+ if (!test_and_clear_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags))
+ return 0;
+
+ ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 1);
+ ivtv_unmute(itv);
+ break;
+ default:
+ IVTV_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ivtv_try_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *enc)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ switch (enc->cmd) {
+ case V4L2_ENC_CMD_START:
+ IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");
+ enc->flags = 0;
+ return 0;
+
+ case V4L2_ENC_CMD_STOP:
+ IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");
+ enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
+ return 0;
+
+ case V4L2_ENC_CMD_PAUSE:
+ IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");
+ enc->flags = 0;
+ return 0;
+
+ case V4L2_ENC_CMD_RESUME:
+ IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");
+ enc->flags = 0;
+ return 0;
+ default:
+ IVTV_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);
+ return -EINVAL;
+ }
+}
+
+static int ivtv_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ struct yuv_playback_info *yi = &itv->yuv_info;
+
+ int pixfmt;
+ static u32 pixel_format[16] = {
+ V4L2_PIX_FMT_PAL8, /* Uses a 256-entry RGB colormap */
+ V4L2_PIX_FMT_RGB565,
+ V4L2_PIX_FMT_RGB555,
+ V4L2_PIX_FMT_RGB444,
+ V4L2_PIX_FMT_RGB32,
+ 0,
+ 0,
+ 0,
+ V4L2_PIX_FMT_PAL8, /* Uses a 256-entry YUV colormap */
+ V4L2_PIX_FMT_YUV565,
+ V4L2_PIX_FMT_YUV555,
+ V4L2_PIX_FMT_YUV444,
+ V4L2_PIX_FMT_YUV32,
+ 0,
+ 0,
+ 0,
+ };
+
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
+ return -EINVAL;
+ if (!itv->osd_video_pbase)
+ return -EINVAL;
+
+ fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY | V4L2_FBUF_CAP_CHROMAKEY |
+ V4L2_FBUF_CAP_GLOBAL_ALPHA;
+
+ ivtv_vapi_result(itv, data, CX2341X_OSD_GET_STATE, 0);
+ data[0] |= (read_reg(0x2a00) >> 7) & 0x40;
+ pixfmt = (data[0] >> 3) & 0xf;
+
+ fb->fmt.pixelformat = pixel_format[pixfmt];
+ fb->fmt.width = itv->osd_rect.width;
+ fb->fmt.height = itv->osd_rect.height;
+ fb->fmt.field = V4L2_FIELD_INTERLACED;
+ fb->fmt.bytesperline = fb->fmt.width;
+ fb->fmt.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ fb->fmt.field = V4L2_FIELD_INTERLACED;
+ fb->fmt.priv = 0;
+ if (fb->fmt.pixelformat != V4L2_PIX_FMT_PAL8)
+ fb->fmt.bytesperline *= 2;
+ if (fb->fmt.pixelformat == V4L2_PIX_FMT_RGB32 ||
+ fb->fmt.pixelformat == V4L2_PIX_FMT_YUV32)
+ fb->fmt.bytesperline *= 2;
+ fb->fmt.sizeimage = fb->fmt.bytesperline * fb->fmt.height;
+ fb->base = (void *)itv->osd_video_pbase;
+ fb->flags = 0;
+
+ if (itv->osd_chroma_key_state)
+ fb->flags |= V4L2_FBUF_FLAG_CHROMAKEY;
+
+ if (itv->osd_global_alpha_state)
+ fb->flags |= V4L2_FBUF_FLAG_GLOBAL_ALPHA;
+
+ if (yi->track_osd)
+ fb->flags |= V4L2_FBUF_FLAG_OVERLAY;
+
+ pixfmt &= 7;
+
+ /* no local alpha for RGB565 or unknown formats */
+ if (pixfmt == 1 || pixfmt > 4)
+ return 0;
+
+ /* 16-bit formats have inverted local alpha */
+ if (pixfmt == 2 || pixfmt == 3)
+ fb->capability |= V4L2_FBUF_CAP_LOCAL_INV_ALPHA;
+ else
+ fb->capability |= V4L2_FBUF_CAP_LOCAL_ALPHA;
+
+ if (itv->osd_local_alpha_state) {
+ /* 16-bit formats have inverted local alpha */
+ if (pixfmt == 2 || pixfmt == 3)
+ fb->flags |= V4L2_FBUF_FLAG_LOCAL_INV_ALPHA;
+ else
+ fb->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA;
+ }
+
+ return 0;
+}
+
+static int ivtv_s_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb)
+{
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+ struct yuv_playback_info *yi = &itv->yuv_info;
+
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
+ return -EINVAL;
+ if (!itv->osd_video_pbase)
+ return -EINVAL;
+
+ itv->osd_global_alpha_state = (fb->flags & V4L2_FBUF_FLAG_GLOBAL_ALPHA) != 0;
+ itv->osd_local_alpha_state =
+ (fb->flags & (V4L2_FBUF_FLAG_LOCAL_ALPHA|V4L2_FBUF_FLAG_LOCAL_INV_ALPHA)) != 0;
+ itv->osd_chroma_key_state = (fb->flags & V4L2_FBUF_FLAG_CHROMAKEY) != 0;
+ ivtv_set_osd_alpha(itv);
+ yi->track_osd = (fb->flags & V4L2_FBUF_FLAG_OVERLAY) != 0;
+ return ivtv_g_fbuf(file, fh, fb);
+}
+
+static int ivtv_overlay(struct file *file, void *fh, unsigned int on)
+{
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
+ return -EINVAL;
+
+ ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, on != 0);
+
+ return 0;
+}
+
+static int ivtv_subscribe_event(struct v4l2_fh *fh, struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_VSYNC:
+ case V4L2_EVENT_EOS:
+ case V4L2_EVENT_CTRL:
+ return v4l2_event_subscribe(fh, sub, 0);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ivtv_log_status(struct file *file, void *fh)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+ u32 data[CX2341X_MBOX_MAX_DATA];
+
+ int has_output = itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT;
+ struct v4l2_input vidin;
+ struct v4l2_audio audin;
+ int i;
+
+ IVTV_INFO("Version: %s Card: %s\n", IVTV_VERSION, itv->card_name);
+ if (itv->hw_flags & IVTV_HW_TVEEPROM) {
+ struct tveeprom tv;
+
+ ivtv_read_eeprom(itv, &tv);
+ }
+ ivtv_call_all(itv, core, log_status);
+ ivtv_get_input(itv, itv->active_input, &vidin);
+ ivtv_get_audio_input(itv, itv->audio_input, &audin);
+ IVTV_INFO("Video Input: %s\n", vidin.name);
+ IVTV_INFO("Audio Input: %s%s\n", audin.name,
+ (itv->dualwatch_stereo_mode & ~0x300) == 0x200 ? " (Bilingual)" : "");
+ if (has_output) {
+ struct v4l2_output vidout;
+ struct v4l2_audioout audout;
+ int mode = itv->output_mode;
+ static const char * const output_modes[5] = {
+ "None",
+ "MPEG Streaming",
+ "YUV Streaming",
+ "YUV Frames",
+ "Passthrough",
+ };
+ static const char * const alpha_mode[4] = {
+ "None",
+ "Global",
+ "Local",
+ "Global and Local"
+ };
+ static const char * const pixel_format[16] = {
+ "ARGB Indexed",
+ "RGB 5:6:5",
+ "ARGB 1:5:5:5",
+ "ARGB 1:4:4:4",
+ "ARGB 8:8:8:8",
+ "5",
+ "6",
+ "7",
+ "AYUV Indexed",
+ "YUV 5:6:5",
+ "AYUV 1:5:5:5",
+ "AYUV 1:4:4:4",
+ "AYUV 8:8:8:8",
+ "13",
+ "14",
+ "15",
+ };
+
+ ivtv_get_output(itv, itv->active_output, &vidout);
+ ivtv_get_audio_output(itv, 0, &audout);
+ IVTV_INFO("Video Output: %s\n", vidout.name);
+ if (mode < 0 || mode > OUT_PASSTHROUGH)
+ mode = OUT_NONE;
+ IVTV_INFO("Output Mode: %s\n", output_modes[mode]);
+ ivtv_vapi_result(itv, data, CX2341X_OSD_GET_STATE, 0);
+ data[0] |= (read_reg(0x2a00) >> 7) & 0x40;
+ IVTV_INFO("Overlay: %s, Alpha: %s, Pixel Format: %s\n",
+ data[0] & 1 ? "On" : "Off",
+ alpha_mode[(data[0] >> 1) & 0x3],
+ pixel_format[(data[0] >> 3) & 0xf]);
+ }
+ IVTV_INFO("Tuner: %s\n",
+ test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV");
+ v4l2_ctrl_handler_log_status(&itv->cxhdl.hdl, itv->v4l2_dev.name);
+ IVTV_INFO("Status flags: 0x%08lx\n", itv->i_flags);
+ for (i = 0; i < IVTV_MAX_STREAMS; i++) {
+ struct ivtv_stream *s = &itv->streams[i];
+
+ if (s->vdev == NULL || s->buffers == 0)
+ continue;
+ IVTV_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", s->name, s->s_flags,
+ (s->buffers - s->q_free.buffers) * 100 / s->buffers,
+ (s->buffers * s->buf_size) / 1024, s->buffers);
+ }
+
+ IVTV_INFO("Read MPG/VBI: %lld/%lld bytes\n",
+ (long long)itv->mpg_data_received,
+ (long long)itv->vbi_data_inserted);
+ return 0;
+}
+
+static int ivtv_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dec)
+{
+ struct ivtv_open_id *id = fh2id(file->private_data);
+ struct ivtv *itv = id->itv;
+
+ IVTV_DEBUG_IOCTL("VIDIOC_DECODER_CMD %d\n", dec->cmd);
+ return ivtv_video_command(itv, id, dec, false);
+}
+
+static int ivtv_try_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dec)
+{
+ struct ivtv_open_id *id = fh2id(file->private_data);
+ struct ivtv *itv = id->itv;
+
+ IVTV_DEBUG_IOCTL("VIDIOC_TRY_DECODER_CMD %d\n", dec->cmd);
+ return ivtv_video_command(itv, id, dec, true);
+}
+
+static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg)
+{
+ struct ivtv_open_id *id = fh2id(filp->private_data);
+ struct ivtv *itv = id->itv;
+ int nonblocking = filp->f_flags & O_NONBLOCK;
+ struct ivtv_stream *s = &itv->streams[id->type];
+ unsigned long iarg = (unsigned long)arg;
+
+ switch (cmd) {
+ case IVTV_IOC_DMA_FRAME: {
+ struct ivtv_dma_frame *args = arg;
+
+ IVTV_DEBUG_IOCTL("IVTV_IOC_DMA_FRAME\n");
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+ if (args->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+ if (itv->output_mode == OUT_UDMA_YUV && args->y_source == NULL)
+ return 0;
+ if (ivtv_start_decoding(id, id->type)) {
+ return -EBUSY;
+ }
+ if (ivtv_set_output_mode(itv, OUT_UDMA_YUV) != OUT_UDMA_YUV) {
+ ivtv_release_stream(s);
+ return -EBUSY;
+ }
+ /* Mark that this file handle started the UDMA_YUV mode */
+ id->yuv_frames = 1;
+ if (args->y_source == NULL)
+ return 0;
+ return ivtv_yuv_prep_frame(itv, args);
+ }
+
+ case IVTV_IOC_PASSTHROUGH_MODE:
+ IVTV_DEBUG_IOCTL("IVTV_IOC_PASSTHROUGH_MODE\n");
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+ return ivtv_passthrough_mode(itv, *(int *)arg != 0);
+
+ case VIDEO_GET_PTS: {
+ s64 *pts = arg;
+ s64 frame;
+
+ IVTV_DEBUG_IOCTL("VIDEO_GET_PTS\n");
+ if (s->type < IVTV_DEC_STREAM_TYPE_MPG) {
+ *pts = s->dma_pts;
+ break;
+ }
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+ return ivtv_g_pts_frame(itv, pts, &frame);
+ }
+
+ case VIDEO_GET_FRAME_COUNT: {
+ s64 *frame = arg;
+ s64 pts;
+
+ IVTV_DEBUG_IOCTL("VIDEO_GET_FRAME_COUNT\n");
+ if (s->type < IVTV_DEC_STREAM_TYPE_MPG) {
+ *frame = 0;
+ break;
+ }
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+ return ivtv_g_pts_frame(itv, &pts, frame);
+ }
+
+ case VIDEO_PLAY: {
+ struct v4l2_decoder_cmd dc;
+
+ IVTV_DEBUG_IOCTL("VIDEO_PLAY\n");
+ memset(&dc, 0, sizeof(dc));
+ dc.cmd = V4L2_DEC_CMD_START;
+ return ivtv_video_command(itv, id, &dc, 0);
+ }
+
+ case VIDEO_STOP: {
+ struct v4l2_decoder_cmd dc;
+
+ IVTV_DEBUG_IOCTL("VIDEO_STOP\n");
+ memset(&dc, 0, sizeof(dc));
+ dc.cmd = V4L2_DEC_CMD_STOP;
+ dc.flags = V4L2_DEC_CMD_STOP_TO_BLACK | V4L2_DEC_CMD_STOP_IMMEDIATELY;
+ return ivtv_video_command(itv, id, &dc, 0);
+ }
+
+ case VIDEO_FREEZE: {
+ struct v4l2_decoder_cmd dc;
+
+ IVTV_DEBUG_IOCTL("VIDEO_FREEZE\n");
+ memset(&dc, 0, sizeof(dc));
+ dc.cmd = V4L2_DEC_CMD_PAUSE;
+ return ivtv_video_command(itv, id, &dc, 0);
+ }
+
+ case VIDEO_CONTINUE: {
+ struct v4l2_decoder_cmd dc;
+
+ IVTV_DEBUG_IOCTL("VIDEO_CONTINUE\n");
+ memset(&dc, 0, sizeof(dc));
+ dc.cmd = V4L2_DEC_CMD_RESUME;
+ return ivtv_video_command(itv, id, &dc, 0);
+ }
+
+ case VIDEO_COMMAND:
+ case VIDEO_TRY_COMMAND: {
+ /* Note: struct v4l2_decoder_cmd has the same layout as
+ struct video_command */
+ struct v4l2_decoder_cmd *dc = arg;
+ int try = (cmd == VIDEO_TRY_COMMAND);
+
+ if (try)
+ IVTV_DEBUG_IOCTL("VIDEO_TRY_COMMAND %d\n", dc->cmd);
+ else
+ IVTV_DEBUG_IOCTL("VIDEO_COMMAND %d\n", dc->cmd);
+ return ivtv_video_command(itv, id, dc, try);
+ }
+
+ case VIDEO_GET_EVENT: {
+ struct video_event *ev = arg;
+ DEFINE_WAIT(wait);
+
+ IVTV_DEBUG_IOCTL("VIDEO_GET_EVENT\n");
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+ memset(ev, 0, sizeof(*ev));
+ set_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags);
+
+ while (1) {
+ if (test_and_clear_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags))
+ ev->type = VIDEO_EVENT_DECODER_STOPPED;
+ else if (test_and_clear_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags)) {
+ ev->type = VIDEO_EVENT_VSYNC;
+ ev->u.vsync_field = test_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags) ?
+ VIDEO_VSYNC_FIELD_ODD : VIDEO_VSYNC_FIELD_EVEN;
+ if (itv->output_mode == OUT_UDMA_YUV &&
+ (itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) ==
+ IVTV_YUV_MODE_PROGRESSIVE) {
+ ev->u.vsync_field = VIDEO_VSYNC_FIELD_PROGRESSIVE;
+ }
+ }
+ if (ev->type)
+ return 0;
+ if (nonblocking)
+ return -EAGAIN;
+ /* Wait for event. Note that serialize_lock is locked,
+ so to allow other processes to access the driver while
+ we are waiting unlock first and later lock again. */
+ mutex_unlock(&itv->serialize_lock);
+ prepare_to_wait(&itv->event_waitq, &wait, TASK_INTERRUPTIBLE);
+ if (!test_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags) &&
+ !test_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags))
+ schedule();
+ finish_wait(&itv->event_waitq, &wait);
+ mutex_lock(&itv->serialize_lock);
+ if (signal_pending(current)) {
+ /* return if a signal was received */
+ IVTV_DEBUG_INFO("User stopped wait for event\n");
+ return -EINTR;
+ }
+ }
+ break;
+ }
+
+ case VIDEO_SELECT_SOURCE:
+ IVTV_DEBUG_IOCTL("VIDEO_SELECT_SOURCE\n");
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+ return ivtv_passthrough_mode(itv, iarg == VIDEO_SOURCE_DEMUX);
+
+ case AUDIO_SET_MUTE:
+ IVTV_DEBUG_IOCTL("AUDIO_SET_MUTE\n");
+ itv->speed_mute_audio = iarg;
+ return 0;
+
+ case AUDIO_CHANNEL_SELECT:
+ IVTV_DEBUG_IOCTL("AUDIO_CHANNEL_SELECT\n");
+ if (iarg > AUDIO_STEREO_SWAPPED)
+ return -EINVAL;
+ return v4l2_ctrl_s_ctrl(itv->ctrl_audio_playback, iarg + 1);
+
+ case AUDIO_BILINGUAL_CHANNEL_SELECT:
+ IVTV_DEBUG_IOCTL("AUDIO_BILINGUAL_CHANNEL_SELECT\n");
+ if (iarg > AUDIO_STEREO_SWAPPED)
+ return -EINVAL;
+ return v4l2_ctrl_s_ctrl(itv->ctrl_audio_multilingual_playback, iarg + 1);
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static long ivtv_default(struct file *file, void *fh, bool valid_prio,
+ int cmd, void *arg)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ if (!valid_prio) {
+ switch (cmd) {
+ case IVTV_IOC_PASSTHROUGH_MODE:
+ case VIDEO_PLAY:
+ case VIDEO_STOP:
+ case VIDEO_FREEZE:
+ case VIDEO_CONTINUE:
+ case VIDEO_COMMAND:
+ case VIDEO_SELECT_SOURCE:
+ case AUDIO_SET_MUTE:
+ case AUDIO_CHANNEL_SELECT:
+ case AUDIO_BILINGUAL_CHANNEL_SELECT:
+ return -EBUSY;
+ }
+ }
+
+ switch (cmd) {
+ case VIDIOC_INT_RESET: {
+ u32 val = *(u32 *)arg;
+
+ if ((val == 0 && itv->options.newi2c) || (val & 0x01))
+ ivtv_reset_ir_gpio(itv);
+ if (val & 0x02)
+ v4l2_subdev_call(itv->sd_video, core, reset, 0);
+ break;
+ }
+
+ case IVTV_IOC_DMA_FRAME:
+ case IVTV_IOC_PASSTHROUGH_MODE:
+ case VIDEO_GET_PTS:
+ case VIDEO_GET_FRAME_COUNT:
+ case VIDEO_GET_EVENT:
+ case VIDEO_PLAY:
+ case VIDEO_STOP:
+ case VIDEO_FREEZE:
+ case VIDEO_CONTINUE:
+ case VIDEO_COMMAND:
+ case VIDEO_TRY_COMMAND:
+ case VIDEO_SELECT_SOURCE:
+ case AUDIO_SET_MUTE:
+ case AUDIO_CHANNEL_SELECT:
+ case AUDIO_BILINGUAL_CHANNEL_SELECT:
+ return ivtv_decoder_ioctls(file, cmd, (void *)arg);
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+long ivtv_v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct video_device *vfd = video_devdata(filp);
+ long ret;
+
+ if (ivtv_debug & IVTV_DBGFLG_IOCTL)
+ vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
+ ret = video_ioctl2(filp, cmd, arg);
+ vfd->debug = 0;
+ return ret;
+}
+
+static const struct v4l2_ioctl_ops ivtv_ioctl_ops = {
+ .vidioc_querycap = ivtv_querycap,
+ .vidioc_s_audio = ivtv_s_audio,
+ .vidioc_g_audio = ivtv_g_audio,
+ .vidioc_enumaudio = ivtv_enumaudio,
+ .vidioc_s_audout = ivtv_s_audout,
+ .vidioc_g_audout = ivtv_g_audout,
+ .vidioc_enum_input = ivtv_enum_input,
+ .vidioc_enum_output = ivtv_enum_output,
+ .vidioc_enumaudout = ivtv_enumaudout,
+ .vidioc_cropcap = ivtv_cropcap,
+ .vidioc_s_crop = ivtv_s_crop,
+ .vidioc_g_crop = ivtv_g_crop,
+ .vidioc_g_input = ivtv_g_input,
+ .vidioc_s_input = ivtv_s_input,
+ .vidioc_g_output = ivtv_g_output,
+ .vidioc_s_output = ivtv_s_output,
+ .vidioc_g_frequency = ivtv_g_frequency,
+ .vidioc_s_frequency = ivtv_s_frequency,
+ .vidioc_s_tuner = ivtv_s_tuner,
+ .vidioc_g_tuner = ivtv_g_tuner,
+ .vidioc_g_enc_index = ivtv_g_enc_index,
+ .vidioc_g_fbuf = ivtv_g_fbuf,
+ .vidioc_s_fbuf = ivtv_s_fbuf,
+ .vidioc_g_std = ivtv_g_std,
+ .vidioc_s_std = ivtv_s_std,
+ .vidioc_overlay = ivtv_overlay,
+ .vidioc_log_status = ivtv_log_status,
+ .vidioc_enum_fmt_vid_cap = ivtv_enum_fmt_vid_cap,
+ .vidioc_encoder_cmd = ivtv_encoder_cmd,
+ .vidioc_try_encoder_cmd = ivtv_try_encoder_cmd,
+ .vidioc_decoder_cmd = ivtv_decoder_cmd,
+ .vidioc_try_decoder_cmd = ivtv_try_decoder_cmd,
+ .vidioc_enum_fmt_vid_out = ivtv_enum_fmt_vid_out,
+ .vidioc_g_fmt_vid_cap = ivtv_g_fmt_vid_cap,
+ .vidioc_g_fmt_vbi_cap = ivtv_g_fmt_vbi_cap,
+ .vidioc_g_fmt_sliced_vbi_cap = ivtv_g_fmt_sliced_vbi_cap,
+ .vidioc_g_fmt_vid_out = ivtv_g_fmt_vid_out,
+ .vidioc_g_fmt_vid_out_overlay = ivtv_g_fmt_vid_out_overlay,
+ .vidioc_g_fmt_sliced_vbi_out = ivtv_g_fmt_sliced_vbi_out,
+ .vidioc_s_fmt_vid_cap = ivtv_s_fmt_vid_cap,
+ .vidioc_s_fmt_vbi_cap = ivtv_s_fmt_vbi_cap,
+ .vidioc_s_fmt_sliced_vbi_cap = ivtv_s_fmt_sliced_vbi_cap,
+ .vidioc_s_fmt_vid_out = ivtv_s_fmt_vid_out,
+ .vidioc_s_fmt_vid_out_overlay = ivtv_s_fmt_vid_out_overlay,
+ .vidioc_s_fmt_sliced_vbi_out = ivtv_s_fmt_sliced_vbi_out,
+ .vidioc_try_fmt_vid_cap = ivtv_try_fmt_vid_cap,
+ .vidioc_try_fmt_vbi_cap = ivtv_try_fmt_vbi_cap,
+ .vidioc_try_fmt_sliced_vbi_cap = ivtv_try_fmt_sliced_vbi_cap,
+ .vidioc_try_fmt_vid_out = ivtv_try_fmt_vid_out,
+ .vidioc_try_fmt_vid_out_overlay = ivtv_try_fmt_vid_out_overlay,
+ .vidioc_try_fmt_sliced_vbi_out = ivtv_try_fmt_sliced_vbi_out,
+ .vidioc_g_sliced_vbi_cap = ivtv_g_sliced_vbi_cap,
+ .vidioc_g_chip_ident = ivtv_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = ivtv_g_register,
+ .vidioc_s_register = ivtv_s_register,
+#endif
+ .vidioc_default = ivtv_default,
+ .vidioc_subscribe_event = ivtv_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+void ivtv_set_funcs(struct video_device *vdev)
+{
+ vdev->ioctl_ops = &ivtv_ioctl_ops;
+}
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.h b/drivers/media/video/ivtv/ivtv-ioctl.h
new file mode 100644
index 00000000..89185cae
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-ioctl.h
@@ -0,0 +1,36 @@
+/*
+ ioctl system call
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef IVTV_IOCTL_H
+#define IVTV_IOCTL_H
+
+u16 ivtv_service2vbi(int type);
+void ivtv_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal);
+u16 ivtv_get_service_set(struct v4l2_sliced_vbi_format *fmt);
+void ivtv_set_osd_alpha(struct ivtv *itv);
+int ivtv_set_speed(struct ivtv *itv, int speed);
+void ivtv_set_funcs(struct video_device *vdev);
+void ivtv_s_std_enc(struct ivtv *itv, v4l2_std_id *std);
+void ivtv_s_std_dec(struct ivtv *itv, v4l2_std_id *std);
+int ivtv_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf);
+int ivtv_s_input(struct file *file, void *fh, unsigned int inp);
+long ivtv_v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
+
+#endif
diff --git a/drivers/media/video/ivtv/ivtv-irq.c b/drivers/media/video/ivtv/ivtv-irq.c
new file mode 100644
index 00000000..1b3b9578
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-irq.c
@@ -0,0 +1,1038 @@
+/* interrupt handling
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-queue.h"
+#include "ivtv-udma.h"
+#include "ivtv-irq.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-vbi.h"
+#include "ivtv-yuv.h"
+#include <media/v4l2-event.h>
+
+#define DMA_MAGIC_COOKIE 0x000001fe
+
+static void ivtv_dma_dec_start(struct ivtv_stream *s);
+
+static const int ivtv_stream_map[] = {
+ IVTV_ENC_STREAM_TYPE_MPG,
+ IVTV_ENC_STREAM_TYPE_YUV,
+ IVTV_ENC_STREAM_TYPE_PCM,
+ IVTV_ENC_STREAM_TYPE_VBI,
+};
+
+
+static void ivtv_pio_work_handler(struct ivtv *itv)
+{
+ struct ivtv_stream *s = &itv->streams[itv->cur_pio_stream];
+ struct ivtv_buffer *buf;
+ int i = 0;
+
+ IVTV_DEBUG_HI_DMA("ivtv_pio_work_handler\n");
+ if (itv->cur_pio_stream < 0 || itv->cur_pio_stream >= IVTV_MAX_STREAMS ||
+ s->vdev == NULL || !ivtv_use_pio(s)) {
+ itv->cur_pio_stream = -1;
+ /* trigger PIO complete user interrupt */
+ write_reg(IVTV_IRQ_ENC_PIO_COMPLETE, 0x44);
+ return;
+ }
+ IVTV_DEBUG_HI_DMA("Process PIO %s\n", s->name);
+ list_for_each_entry(buf, &s->q_dma.list, list) {
+ u32 size = s->sg_processing[i].size & 0x3ffff;
+
+ /* Copy the data from the card to the buffer */
+ if (s->type == IVTV_DEC_STREAM_TYPE_VBI) {
+ memcpy_fromio(buf->buf, itv->dec_mem + s->sg_processing[i].src - IVTV_DECODER_OFFSET, size);
+ }
+ else {
+ memcpy_fromio(buf->buf, itv->enc_mem + s->sg_processing[i].src, size);
+ }
+ i++;
+ if (i == s->sg_processing_size)
+ break;
+ }
+ write_reg(IVTV_IRQ_ENC_PIO_COMPLETE, 0x44);
+}
+
+void ivtv_irq_work_handler(struct kthread_work *work)
+{
+ struct ivtv *itv = container_of(work, struct ivtv, irq_work);
+
+ if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_PIO, &itv->i_flags))
+ ivtv_pio_work_handler(itv);
+
+ if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_VBI, &itv->i_flags))
+ ivtv_vbi_work_handler(itv);
+
+ if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags))
+ ivtv_yuv_work_handler(itv);
+}
+
+/* Determine the required DMA size, setup enough buffers in the predma queue and
+ actually copy the data from the card to the buffers in case a PIO transfer is
+ required for this stream.
+ */
+static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MAX_DATA])
+{
+ struct ivtv *itv = s->itv;
+ struct ivtv_buffer *buf;
+ u32 bytes_needed = 0;
+ u32 offset, size;
+ u32 UVoffset = 0, UVsize = 0;
+ int skip_bufs = s->q_predma.buffers;
+ int idx = s->sg_pending_size;
+ int rc;
+
+ /* sanity checks */
+ if (s->vdev == NULL) {
+ IVTV_DEBUG_WARN("Stream %s not started\n", s->name);
+ return -1;
+ }
+ if (!test_bit(IVTV_F_S_CLAIMED, &s->s_flags)) {
+ IVTV_DEBUG_WARN("Stream %s not open\n", s->name);
+ return -1;
+ }
+
+ /* determine offset, size and PTS for the various streams */
+ switch (s->type) {
+ case IVTV_ENC_STREAM_TYPE_MPG:
+ offset = data[1];
+ size = data[2];
+ s->pending_pts = 0;
+ break;
+
+ case IVTV_ENC_STREAM_TYPE_YUV:
+ offset = data[1];
+ size = data[2];
+ UVoffset = data[3];
+ UVsize = data[4];
+ s->pending_pts = ((u64) data[5] << 32) | data[6];
+ break;
+
+ case IVTV_ENC_STREAM_TYPE_PCM:
+ offset = data[1] + 12;
+ size = data[2] - 12;
+ s->pending_pts = read_dec(offset - 8) |
+ ((u64)(read_dec(offset - 12)) << 32);
+ if (itv->has_cx23415)
+ offset += IVTV_DECODER_OFFSET;
+ break;
+
+ case IVTV_ENC_STREAM_TYPE_VBI:
+ size = itv->vbi.enc_size * itv->vbi.fpi;
+ offset = read_enc(itv->vbi.enc_start - 4) + 12;
+ if (offset == 12) {
+ IVTV_DEBUG_INFO("VBI offset == 0\n");
+ return -1;
+ }
+ s->pending_pts = read_enc(offset - 4) | ((u64)read_enc(offset - 8) << 32);
+ break;
+
+ case IVTV_DEC_STREAM_TYPE_VBI:
+ size = read_dec(itv->vbi.dec_start + 4) + 8;
+ offset = read_dec(itv->vbi.dec_start) + itv->vbi.dec_start;
+ s->pending_pts = 0;
+ offset += IVTV_DECODER_OFFSET;
+ break;
+ default:
+ /* shouldn't happen */
+ return -1;
+ }
+
+ /* if this is the start of the DMA then fill in the magic cookie */
+ if (s->sg_pending_size == 0 && ivtv_use_dma(s)) {
+ if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM ||
+ s->type == IVTV_DEC_STREAM_TYPE_VBI)) {
+ s->pending_backup = read_dec(offset - IVTV_DECODER_OFFSET);
+ write_dec_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset - IVTV_DECODER_OFFSET);
+ }
+ else {
+ s->pending_backup = read_enc(offset);
+ write_enc_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset);
+ }
+ s->pending_offset = offset;
+ }
+
+ bytes_needed = size;
+ if (s->type == IVTV_ENC_STREAM_TYPE_YUV) {
+ /* The size for the Y samples needs to be rounded upwards to a
+ multiple of the buf_size. The UV samples then start in the
+ next buffer. */
+ bytes_needed = s->buf_size * ((bytes_needed + s->buf_size - 1) / s->buf_size);
+ bytes_needed += UVsize;
+ }
+
+ IVTV_DEBUG_HI_DMA("%s %s: 0x%08x bytes at 0x%08x\n",
+ ivtv_use_pio(s) ? "PIO" : "DMA", s->name, bytes_needed, offset);
+
+ rc = ivtv_queue_move(s, &s->q_free, &s->q_full, &s->q_predma, bytes_needed);
+ if (rc < 0) { /* Insufficient buffers */
+ IVTV_DEBUG_WARN("Cannot obtain %d bytes for %s data transfer\n",
+ bytes_needed, s->name);
+ return -1;
+ }
+ if (rc && !s->buffers_stolen && test_bit(IVTV_F_S_APPL_IO, &s->s_flags)) {
+ IVTV_WARN("All %s stream buffers are full. Dropping data.\n", s->name);
+ IVTV_WARN("Cause: the application is not reading fast enough.\n");
+ }
+ s->buffers_stolen = rc;
+
+ /* got the buffers, now fill in sg_pending */
+ buf = list_entry(s->q_predma.list.next, struct ivtv_buffer, list);
+ memset(buf->buf, 0, 128);
+ list_for_each_entry(buf, &s->q_predma.list, list) {
+ if (skip_bufs-- > 0)
+ continue;
+ s->sg_pending[idx].dst = buf->dma_handle;
+ s->sg_pending[idx].src = offset;
+ s->sg_pending[idx].size = s->buf_size;
+ buf->bytesused = min(size, s->buf_size);
+ buf->dma_xfer_cnt = s->dma_xfer_cnt;
+
+ s->q_predma.bytesused += buf->bytesused;
+ size -= buf->bytesused;
+ offset += s->buf_size;
+
+ /* Sync SG buffers */
+ ivtv_buf_sync_for_device(s, buf);
+
+ if (size == 0) { /* YUV */
+ /* process the UV section */
+ offset = UVoffset;
+ size = UVsize;
+ }
+ idx++;
+ }
+ s->sg_pending_size = idx;
+ return 0;
+}
+
+static void dma_post(struct ivtv_stream *s)
+{
+ struct ivtv *itv = s->itv;
+ struct ivtv_buffer *buf = NULL;
+ struct list_head *p;
+ u32 offset;
+ __le32 *u32buf;
+ int x = 0;
+
+ IVTV_DEBUG_HI_DMA("%s %s completed (%x)\n", ivtv_use_pio(s) ? "PIO" : "DMA",
+ s->name, s->dma_offset);
+ list_for_each(p, &s->q_dma.list) {
+ buf = list_entry(p, struct ivtv_buffer, list);
+ u32buf = (__le32 *)buf->buf;
+
+ /* Sync Buffer */
+ ivtv_buf_sync_for_cpu(s, buf);
+
+ if (x == 0 && ivtv_use_dma(s)) {
+ offset = s->dma_last_offset;
+ if (u32buf[offset / 4] != DMA_MAGIC_COOKIE)
+ {
+ for (offset = 0; offset < 64; offset++) {
+ if (u32buf[offset] == DMA_MAGIC_COOKIE) {
+ break;
+ }
+ }
+ offset *= 4;
+ if (offset == 256) {
+ IVTV_DEBUG_WARN("%s: Couldn't find start of buffer within the first 256 bytes\n", s->name);
+ offset = s->dma_last_offset;
+ }
+ if (s->dma_last_offset != offset)
+ IVTV_DEBUG_WARN("%s: offset %d -> %d\n", s->name, s->dma_last_offset, offset);
+ s->dma_last_offset = offset;
+ }
+ if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM ||
+ s->type == IVTV_DEC_STREAM_TYPE_VBI)) {
+ write_dec_sync(0, s->dma_offset - IVTV_DECODER_OFFSET);
+ }
+ else {
+ write_enc_sync(0, s->dma_offset);
+ }
+ if (offset) {
+ buf->bytesused -= offset;
+ memcpy(buf->buf, buf->buf + offset, buf->bytesused + offset);
+ }
+ *u32buf = cpu_to_le32(s->dma_backup);
+ }
+ x++;
+ /* flag byteswap ABCD -> DCBA for MPG & VBI data outside irq */
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG ||
+ s->type == IVTV_ENC_STREAM_TYPE_VBI)
+ buf->b_flags |= IVTV_F_B_NEED_BUF_SWAP;
+ }
+ if (buf)
+ buf->bytesused += s->dma_last_offset;
+ if (buf && s->type == IVTV_DEC_STREAM_TYPE_VBI) {
+ list_for_each_entry(buf, &s->q_dma.list, list) {
+ /* Parse and Groom VBI Data */
+ s->q_dma.bytesused -= buf->bytesused;
+ ivtv_process_vbi_data(itv, buf, 0, s->type);
+ s->q_dma.bytesused += buf->bytesused;
+ }
+ if (s->fh == NULL) {
+ ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0);
+ return;
+ }
+ }
+ ivtv_queue_move(s, &s->q_dma, NULL, &s->q_full, s->q_dma.bytesused);
+ if (s->fh)
+ wake_up(&s->waitq);
+}
+
+void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock)
+{
+ struct ivtv *itv = s->itv;
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ u8 frame = yi->draw_frame;
+ struct yuv_frame_info *f = &yi->new_frame_info[frame];
+ struct ivtv_buffer *buf;
+ u32 y_size = 720 * ((f->src_h + 31) & ~31);
+ u32 uv_offset = offset + IVTV_YUV_BUFFER_UV_OFFSET;
+ int y_done = 0;
+ int bytes_written = 0;
+ unsigned long flags = 0;
+ int idx = 0;
+
+ IVTV_DEBUG_HI_DMA("DEC PREPARE DMA %s: %08x %08x\n", s->name, s->q_predma.bytesused, offset);
+
+ /* Insert buffer block for YUV if needed */
+ if (s->type == IVTV_DEC_STREAM_TYPE_YUV && f->offset_y) {
+ if (yi->blanking_dmaptr) {
+ s->sg_pending[idx].src = yi->blanking_dmaptr;
+ s->sg_pending[idx].dst = offset;
+ s->sg_pending[idx].size = 720 * 16;
+ }
+ offset += 720 * 16;
+ idx++;
+ }
+
+ list_for_each_entry(buf, &s->q_predma.list, list) {
+ /* YUV UV Offset from Y Buffer */
+ if (s->type == IVTV_DEC_STREAM_TYPE_YUV && !y_done &&
+ (bytes_written + buf->bytesused) >= y_size) {
+ s->sg_pending[idx].src = buf->dma_handle;
+ s->sg_pending[idx].dst = offset;
+ s->sg_pending[idx].size = y_size - bytes_written;
+ offset = uv_offset;
+ if (s->sg_pending[idx].size != buf->bytesused) {
+ idx++;
+ s->sg_pending[idx].src =
+ buf->dma_handle + s->sg_pending[idx - 1].size;
+ s->sg_pending[idx].dst = offset;
+ s->sg_pending[idx].size =
+ buf->bytesused - s->sg_pending[idx - 1].size;
+ offset += s->sg_pending[idx].size;
+ }
+ y_done = 1;
+ } else {
+ s->sg_pending[idx].src = buf->dma_handle;
+ s->sg_pending[idx].dst = offset;
+ s->sg_pending[idx].size = buf->bytesused;
+ offset += buf->bytesused;
+ }
+ bytes_written += buf->bytesused;
+
+ /* Sync SG buffers */
+ ivtv_buf_sync_for_device(s, buf);
+ idx++;
+ }
+ s->sg_pending_size = idx;
+
+ /* Sync Hardware SG List of buffers */
+ ivtv_stream_sync_for_device(s);
+ if (lock)
+ spin_lock_irqsave(&itv->dma_reg_lock, flags);
+ if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) {
+ ivtv_dma_dec_start(s);
+ }
+ else {
+ set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags);
+ }
+ if (lock)
+ spin_unlock_irqrestore(&itv->dma_reg_lock, flags);
+}
+
+static void ivtv_dma_enc_start_xfer(struct ivtv_stream *s)
+{
+ struct ivtv *itv = s->itv;
+
+ s->sg_dma->src = cpu_to_le32(s->sg_processing[s->sg_processed].src);
+ s->sg_dma->dst = cpu_to_le32(s->sg_processing[s->sg_processed].dst);
+ s->sg_dma->size = cpu_to_le32(s->sg_processing[s->sg_processed].size | 0x80000000);
+ s->sg_processed++;
+ /* Sync Hardware SG List of buffers */
+ ivtv_stream_sync_for_device(s);
+ write_reg(s->sg_handle, IVTV_REG_ENCDMAADDR);
+ write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x02, IVTV_REG_DMAXFER);
+ itv->dma_timer.expires = jiffies + msecs_to_jiffies(300);
+ add_timer(&itv->dma_timer);
+}
+
+static void ivtv_dma_dec_start_xfer(struct ivtv_stream *s)
+{
+ struct ivtv *itv = s->itv;
+
+ s->sg_dma->src = cpu_to_le32(s->sg_processing[s->sg_processed].src);
+ s->sg_dma->dst = cpu_to_le32(s->sg_processing[s->sg_processed].dst);
+ s->sg_dma->size = cpu_to_le32(s->sg_processing[s->sg_processed].size | 0x80000000);
+ s->sg_processed++;
+ /* Sync Hardware SG List of buffers */
+ ivtv_stream_sync_for_device(s);
+ write_reg(s->sg_handle, IVTV_REG_DECDMAADDR);
+ write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER);
+ itv->dma_timer.expires = jiffies + msecs_to_jiffies(300);
+ add_timer(&itv->dma_timer);
+}
+
+/* start the encoder DMA */
+static void ivtv_dma_enc_start(struct ivtv_stream *s)
+{
+ struct ivtv *itv = s->itv;
+ struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+ int i;
+
+ IVTV_DEBUG_HI_DMA("start %s for %s\n", ivtv_use_dma(s) ? "DMA" : "PIO", s->name);
+
+ if (s->q_predma.bytesused)
+ ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
+
+ if (ivtv_use_dma(s))
+ s->sg_pending[s->sg_pending_size - 1].size += 256;
+
+ /* If this is an MPEG stream, and VBI data is also pending, then append the
+ VBI DMA to the MPEG DMA and transfer both sets of data at once.
+
+ VBI DMA is a second class citizen compared to MPEG and mixing them together
+ will confuse the firmware (the end of a VBI DMA is seen as the end of a
+ MPEG DMA, thus effectively dropping an MPEG frame). So instead we make
+ sure we only use the MPEG DMA to transfer the VBI DMA if both are in
+ use. This way no conflicts occur. */
+ clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags);
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG && s_vbi->sg_pending_size &&
+ s->sg_pending_size + s_vbi->sg_pending_size <= s->buffers) {
+ ivtv_queue_move(s_vbi, &s_vbi->q_predma, NULL, &s_vbi->q_dma, s_vbi->q_predma.bytesused);
+ if (ivtv_use_dma(s_vbi))
+ s_vbi->sg_pending[s_vbi->sg_pending_size - 1].size += 256;
+ for (i = 0; i < s_vbi->sg_pending_size; i++) {
+ s->sg_pending[s->sg_pending_size++] = s_vbi->sg_pending[i];
+ }
+ s_vbi->dma_offset = s_vbi->pending_offset;
+ s_vbi->sg_pending_size = 0;
+ s_vbi->dma_xfer_cnt++;
+ set_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags);
+ IVTV_DEBUG_HI_DMA("include DMA for %s\n", s_vbi->name);
+ }
+
+ s->dma_xfer_cnt++;
+ memcpy(s->sg_processing, s->sg_pending, sizeof(struct ivtv_sg_host_element) * s->sg_pending_size);
+ s->sg_processing_size = s->sg_pending_size;
+ s->sg_pending_size = 0;
+ s->sg_processed = 0;
+ s->dma_offset = s->pending_offset;
+ s->dma_backup = s->pending_backup;
+ s->dma_pts = s->pending_pts;
+
+ if (ivtv_use_pio(s)) {
+ set_bit(IVTV_F_I_WORK_HANDLER_PIO, &itv->i_flags);
+ set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags);
+ set_bit(IVTV_F_I_PIO, &itv->i_flags);
+ itv->cur_pio_stream = s->type;
+ }
+ else {
+ itv->dma_retries = 0;
+ ivtv_dma_enc_start_xfer(s);
+ set_bit(IVTV_F_I_DMA, &itv->i_flags);
+ itv->cur_dma_stream = s->type;
+ }
+}
+
+static void ivtv_dma_dec_start(struct ivtv_stream *s)
+{
+ struct ivtv *itv = s->itv;
+
+ if (s->q_predma.bytesused)
+ ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
+ s->dma_xfer_cnt++;
+ memcpy(s->sg_processing, s->sg_pending, sizeof(struct ivtv_sg_host_element) * s->sg_pending_size);
+ s->sg_processing_size = s->sg_pending_size;
+ s->sg_pending_size = 0;
+ s->sg_processed = 0;
+
+ IVTV_DEBUG_HI_DMA("start DMA for %s\n", s->name);
+ itv->dma_retries = 0;
+ ivtv_dma_dec_start_xfer(s);
+ set_bit(IVTV_F_I_DMA, &itv->i_flags);
+ itv->cur_dma_stream = s->type;
+}
+
+static void ivtv_irq_dma_read(struct ivtv *itv)
+{
+ struct ivtv_stream *s = NULL;
+ struct ivtv_buffer *buf;
+ int hw_stream_type = 0;
+
+ IVTV_DEBUG_HI_IRQ("DEC DMA READ\n");
+
+ del_timer(&itv->dma_timer);
+
+ if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags) && itv->cur_dma_stream < 0)
+ return;
+
+ if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags)) {
+ s = &itv->streams[itv->cur_dma_stream];
+ ivtv_stream_sync_for_cpu(s);
+
+ if (read_reg(IVTV_REG_DMASTATUS) & 0x14) {
+ IVTV_DEBUG_WARN("DEC DMA ERROR %x (xfer %d of %d, retry %d)\n",
+ read_reg(IVTV_REG_DMASTATUS),
+ s->sg_processed, s->sg_processing_size, itv->dma_retries);
+ write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
+ if (itv->dma_retries == 3) {
+ /* Too many retries, give up on this frame */
+ itv->dma_retries = 0;
+ s->sg_processed = s->sg_processing_size;
+ }
+ else {
+ /* Retry, starting with the first xfer segment.
+ Just retrying the current segment is not sufficient. */
+ s->sg_processed = 0;
+ itv->dma_retries++;
+ }
+ }
+ if (s->sg_processed < s->sg_processing_size) {
+ /* DMA next buffer */
+ ivtv_dma_dec_start_xfer(s);
+ return;
+ }
+ if (s->type == IVTV_DEC_STREAM_TYPE_YUV)
+ hw_stream_type = 2;
+ IVTV_DEBUG_HI_DMA("DEC DATA READ %s: %d\n", s->name, s->q_dma.bytesused);
+
+ /* For some reason must kick the firmware, like PIO mode,
+ I think this tells the firmware we are done and the size
+ of the xfer so it can calculate what we need next.
+ I think we can do this part ourselves but would have to
+ fully calculate xfer info ourselves and not use interrupts
+ */
+ ivtv_vapi(itv, CX2341X_DEC_SCHED_DMA_FROM_HOST, 3, 0, s->q_dma.bytesused,
+ hw_stream_type);
+
+ /* Free last DMA call */
+ while ((buf = ivtv_dequeue(s, &s->q_dma)) != NULL) {
+ ivtv_buf_sync_for_cpu(s, buf);
+ ivtv_enqueue(s, buf, &s->q_free);
+ }
+ wake_up(&s->waitq);
+ }
+ clear_bit(IVTV_F_I_UDMA, &itv->i_flags);
+ clear_bit(IVTV_F_I_DMA, &itv->i_flags);
+ itv->cur_dma_stream = -1;
+ wake_up(&itv->dma_waitq);
+}
+
+static void ivtv_irq_enc_dma_complete(struct ivtv *itv)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ struct ivtv_stream *s;
+
+ ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, 2, data);
+ IVTV_DEBUG_HI_IRQ("ENC DMA COMPLETE %x %d (%d)\n", data[0], data[1], itv->cur_dma_stream);
+
+ del_timer(&itv->dma_timer);
+
+ if (itv->cur_dma_stream < 0)
+ return;
+
+ s = &itv->streams[itv->cur_dma_stream];
+ ivtv_stream_sync_for_cpu(s);
+
+ if (data[0] & 0x18) {
+ IVTV_DEBUG_WARN("ENC DMA ERROR %x (offset %08x, xfer %d of %d, retry %d)\n", data[0],
+ s->dma_offset, s->sg_processed, s->sg_processing_size, itv->dma_retries);
+ write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
+ if (itv->dma_retries == 3) {
+ /* Too many retries, give up on this frame */
+ itv->dma_retries = 0;
+ s->sg_processed = s->sg_processing_size;
+ }
+ else {
+ /* Retry, starting with the first xfer segment.
+ Just retrying the current segment is not sufficient. */
+ s->sg_processed = 0;
+ itv->dma_retries++;
+ }
+ }
+ if (s->sg_processed < s->sg_processing_size) {
+ /* DMA next buffer */
+ ivtv_dma_enc_start_xfer(s);
+ return;
+ }
+ clear_bit(IVTV_F_I_DMA, &itv->i_flags);
+ itv->cur_dma_stream = -1;
+ dma_post(s);
+ if (test_and_clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags)) {
+ s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+ dma_post(s);
+ }
+ s->sg_processing_size = 0;
+ s->sg_processed = 0;
+ wake_up(&itv->dma_waitq);
+}
+
+static void ivtv_irq_enc_pio_complete(struct ivtv *itv)
+{
+ struct ivtv_stream *s;
+
+ if (itv->cur_pio_stream < 0 || itv->cur_pio_stream >= IVTV_MAX_STREAMS) {
+ itv->cur_pio_stream = -1;
+ return;
+ }
+ s = &itv->streams[itv->cur_pio_stream];
+ IVTV_DEBUG_HI_IRQ("ENC PIO COMPLETE %s\n", s->name);
+ clear_bit(IVTV_F_I_PIO, &itv->i_flags);
+ itv->cur_pio_stream = -1;
+ dma_post(s);
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG)
+ ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, 0);
+ else if (s->type == IVTV_ENC_STREAM_TYPE_YUV)
+ ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, 1);
+ else if (s->type == IVTV_ENC_STREAM_TYPE_PCM)
+ ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, 2);
+ clear_bit(IVTV_F_I_PIO, &itv->i_flags);
+ if (test_and_clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags)) {
+ s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+ dma_post(s);
+ }
+ wake_up(&itv->dma_waitq);
+}
+
+static void ivtv_irq_dma_err(struct ivtv *itv)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ u32 status;
+
+ del_timer(&itv->dma_timer);
+
+ ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, 2, data);
+ status = read_reg(IVTV_REG_DMASTATUS);
+ IVTV_DEBUG_WARN("DMA ERROR %08x %08x %08x %d\n", data[0], data[1],
+ status, itv->cur_dma_stream);
+ /*
+ * We do *not* write back to the IVTV_REG_DMASTATUS register to
+ * clear the error status, if either the encoder write (0x02) or
+ * decoder read (0x01) bus master DMA operation do not indicate
+ * completed. We can race with the DMA engine, which may have
+ * transitioned to completed status *after* we read the register.
+ * Setting a IVTV_REG_DMASTATUS flag back to "busy" status, after the
+ * DMA engine has completed, will cause the DMA engine to stop working.
+ */
+ status &= 0x3;
+ if (status == 0x3)
+ write_reg(status, IVTV_REG_DMASTATUS);
+
+ if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags) &&
+ itv->cur_dma_stream >= 0 && itv->cur_dma_stream < IVTV_MAX_STREAMS) {
+ struct ivtv_stream *s = &itv->streams[itv->cur_dma_stream];
+
+ if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) {
+ /* retry */
+ /*
+ * FIXME - handle cases of DMA error similar to
+ * encoder below, except conditioned on status & 0x1
+ */
+ ivtv_dma_dec_start(s);
+ return;
+ } else {
+ if ((status & 0x2) == 0) {
+ /*
+ * CX2341x Bus Master DMA write is ongoing.
+ * Reset the timer and let it complete.
+ */
+ itv->dma_timer.expires =
+ jiffies + msecs_to_jiffies(600);
+ add_timer(&itv->dma_timer);
+ return;
+ }
+
+ if (itv->dma_retries < 3) {
+ /*
+ * CX2341x Bus Master DMA write has ended.
+ * Retry the write, starting with the first
+ * xfer segment. Just retrying the current
+ * segment is not sufficient.
+ */
+ s->sg_processed = 0;
+ itv->dma_retries++;
+ ivtv_dma_enc_start_xfer(s);
+ return;
+ }
+ /* Too many retries, give up on this one */
+ }
+
+ }
+ if (test_bit(IVTV_F_I_UDMA, &itv->i_flags)) {
+ ivtv_udma_start(itv);
+ return;
+ }
+ clear_bit(IVTV_F_I_UDMA, &itv->i_flags);
+ clear_bit(IVTV_F_I_DMA, &itv->i_flags);
+ itv->cur_dma_stream = -1;
+ wake_up(&itv->dma_waitq);
+}
+
+static void ivtv_irq_enc_start_cap(struct ivtv *itv)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ struct ivtv_stream *s;
+
+ /* Get DMA destination and size arguments from card */
+ ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA, 7, data);
+ IVTV_DEBUG_HI_IRQ("ENC START CAP %d: %08x %08x\n", data[0], data[1], data[2]);
+
+ if (data[0] > 2 || data[1] == 0 || data[2] == 0) {
+ IVTV_DEBUG_WARN("Unknown input: %08x %08x %08x\n",
+ data[0], data[1], data[2]);
+ return;
+ }
+ s = &itv->streams[ivtv_stream_map[data[0]]];
+ if (!stream_enc_dma_append(s, data)) {
+ set_bit(ivtv_use_pio(s) ? IVTV_F_S_PIO_PENDING : IVTV_F_S_DMA_PENDING, &s->s_flags);
+ }
+}
+
+static void ivtv_irq_enc_vbi_cap(struct ivtv *itv)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ struct ivtv_stream *s;
+
+ IVTV_DEBUG_HI_IRQ("ENC START VBI CAP\n");
+ s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+
+ if (!stream_enc_dma_append(s, data))
+ set_bit(ivtv_use_pio(s) ? IVTV_F_S_PIO_PENDING : IVTV_F_S_DMA_PENDING, &s->s_flags);
+}
+
+static void ivtv_irq_dec_vbi_reinsert(struct ivtv *itv)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_VBI];
+
+ IVTV_DEBUG_HI_IRQ("DEC VBI REINSERT\n");
+ if (test_bit(IVTV_F_S_CLAIMED, &s->s_flags) &&
+ !stream_enc_dma_append(s, data)) {
+ set_bit(IVTV_F_S_PIO_PENDING, &s->s_flags);
+ }
+}
+
+static void ivtv_irq_dec_data_req(struct ivtv *itv)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ struct ivtv_stream *s;
+
+ /* YUV or MPG */
+
+ if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) {
+ ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, 2, data);
+ itv->dma_data_req_size =
+ 1080 * ((itv->yuv_info.v4l2_src_h + 31) & ~31);
+ itv->dma_data_req_offset = data[1];
+ if (atomic_read(&itv->yuv_info.next_dma_frame) >= 0)
+ ivtv_yuv_frame_complete(itv);
+ s = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
+ }
+ else {
+ ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, 3, data);
+ itv->dma_data_req_size = min_t(u32, data[2], 0x10000);
+ itv->dma_data_req_offset = data[1];
+ s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
+ }
+ IVTV_DEBUG_HI_IRQ("DEC DATA REQ %s: %d %08x %u\n", s->name, s->q_full.bytesused,
+ itv->dma_data_req_offset, itv->dma_data_req_size);
+ if (itv->dma_data_req_size == 0 || s->q_full.bytesused < itv->dma_data_req_size) {
+ set_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
+ }
+ else {
+ if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags))
+ ivtv_yuv_setup_stream_frame(itv);
+ clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
+ ivtv_queue_move(s, &s->q_full, NULL, &s->q_predma, itv->dma_data_req_size);
+ ivtv_dma_stream_dec_prepare(s, itv->dma_data_req_offset + IVTV_DECODER_OFFSET, 0);
+ }
+}
+
+static void ivtv_irq_vsync(struct ivtv *itv)
+{
+ /* The vsync interrupt is unusual in that it won't clear until
+ * the end of the first line for the current field, at which
+ * point it clears itself. This can result in repeated vsync
+ * interrupts, or a missed vsync. Read some of the registers
+ * to determine the line being displayed and ensure we handle
+ * one vsync per frame.
+ */
+ unsigned int frame = read_reg(IVTV_REG_DEC_LINE_FIELD) & 1;
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ int last_dma_frame = atomic_read(&yi->next_dma_frame);
+ struct yuv_frame_info *f = &yi->new_frame_info[last_dma_frame];
+
+ if (0) IVTV_DEBUG_IRQ("DEC VSYNC\n");
+
+ if (((frame ^ f->sync_field) == 0 &&
+ ((itv->last_vsync_field & 1) ^ f->sync_field)) ||
+ (frame != (itv->last_vsync_field & 1) && !f->interlaced)) {
+ int next_dma_frame = last_dma_frame;
+
+ if (!(f->interlaced && f->delay && yi->fields_lapsed < 1)) {
+ if (next_dma_frame >= 0 && next_dma_frame != atomic_read(&yi->next_fill_frame)) {
+ write_reg(yuv_offset[next_dma_frame] >> 4, 0x82c);
+ write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830);
+ write_reg(yuv_offset[next_dma_frame] >> 4, 0x834);
+ write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x838);
+ next_dma_frame = (next_dma_frame + 1) % IVTV_YUV_BUFFERS;
+ atomic_set(&yi->next_dma_frame, next_dma_frame);
+ yi->fields_lapsed = -1;
+ yi->running = 1;
+ }
+ }
+ }
+ if (frame != (itv->last_vsync_field & 1)) {
+ static const struct v4l2_event evtop = {
+ .type = V4L2_EVENT_VSYNC,
+ .u.vsync.field = V4L2_FIELD_TOP,
+ };
+ static const struct v4l2_event evbottom = {
+ .type = V4L2_EVENT_VSYNC,
+ .u.vsync.field = V4L2_FIELD_BOTTOM,
+ };
+ struct ivtv_stream *s = ivtv_get_output_stream(itv);
+
+ itv->last_vsync_field += 1;
+ if (frame == 0) {
+ clear_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags);
+ clear_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags);
+ }
+ else {
+ set_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags);
+ }
+ if (test_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags)) {
+ set_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags);
+ wake_up(&itv->event_waitq);
+ if (s)
+ wake_up(&s->waitq);
+ }
+ if (s && s->vdev)
+ v4l2_event_queue(s->vdev, frame ? &evtop : &evbottom);
+ wake_up(&itv->vsync_waitq);
+
+ /* Send VBI to saa7127 */
+ if (frame && (itv->output_mode == OUT_PASSTHROUGH ||
+ test_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags) ||
+ test_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags) ||
+ test_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags))) {
+ set_bit(IVTV_F_I_WORK_HANDLER_VBI, &itv->i_flags);
+ set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags);
+ }
+
+ /* Check if we need to update the yuv registers */
+ if (yi->running && (yi->yuv_forced_update || f->update)) {
+ if (!f->update) {
+ last_dma_frame =
+ (u8)(atomic_read(&yi->next_dma_frame) -
+ 1) % IVTV_YUV_BUFFERS;
+ f = &yi->new_frame_info[last_dma_frame];
+ }
+
+ if (f->src_w) {
+ yi->update_frame = last_dma_frame;
+ f->update = 0;
+ yi->yuv_forced_update = 0;
+ set_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags);
+ set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags);
+ }
+ }
+
+ yi->fields_lapsed++;
+ }
+}
+
+#define IVTV_IRQ_DMA (IVTV_IRQ_DMA_READ | IVTV_IRQ_ENC_DMA_COMPLETE | IVTV_IRQ_DMA_ERR | IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_VBI_CAP | IVTV_IRQ_DEC_DATA_REQ | IVTV_IRQ_DEC_VBI_RE_INSERT)
+
+irqreturn_t ivtv_irq_handler(int irq, void *dev_id)
+{
+ struct ivtv *itv = (struct ivtv *)dev_id;
+ u32 combo;
+ u32 stat;
+ int i;
+ u8 vsync_force = 0;
+
+ spin_lock(&itv->dma_reg_lock);
+ /* get contents of irq status register */
+ stat = read_reg(IVTV_REG_IRQSTATUS);
+
+ combo = ~itv->irqmask & stat;
+
+ /* Clear out IRQ */
+ if (combo) write_reg(combo, IVTV_REG_IRQSTATUS);
+
+ if (0 == combo) {
+ /* The vsync interrupt is unusual and clears itself. If we
+ * took too long, we may have missed it. Do some checks
+ */
+ if (~itv->irqmask & IVTV_IRQ_DEC_VSYNC) {
+ /* vsync is enabled, see if we're in a new field */
+ if ((itv->last_vsync_field & 1) !=
+ (read_reg(IVTV_REG_DEC_LINE_FIELD) & 1)) {
+ /* New field, looks like we missed it */
+ IVTV_DEBUG_YUV("VSync interrupt missed %d\n",
+ read_reg(IVTV_REG_DEC_LINE_FIELD) >> 16);
+ vsync_force = 1;
+ }
+ }
+
+ if (!vsync_force) {
+ /* No Vsync expected, wasn't for us */
+ spin_unlock(&itv->dma_reg_lock);
+ return IRQ_NONE;
+ }
+ }
+
+ /* Exclude interrupts noted below from the output, otherwise the log is flooded with
+ these messages */
+ if (combo & ~0xff6d0400)
+ IVTV_DEBUG_HI_IRQ("======= valid IRQ bits: 0x%08x ======\n", combo);
+
+ if (combo & IVTV_IRQ_DEC_DMA_COMPLETE) {
+ IVTV_DEBUG_HI_IRQ("DEC DMA COMPLETE\n");
+ }
+
+ if (combo & IVTV_IRQ_DMA_READ) {
+ ivtv_irq_dma_read(itv);
+ }
+
+ if (combo & IVTV_IRQ_ENC_DMA_COMPLETE) {
+ ivtv_irq_enc_dma_complete(itv);
+ }
+
+ if (combo & IVTV_IRQ_ENC_PIO_COMPLETE) {
+ ivtv_irq_enc_pio_complete(itv);
+ }
+
+ if (combo & IVTV_IRQ_DMA_ERR) {
+ ivtv_irq_dma_err(itv);
+ }
+
+ if (combo & IVTV_IRQ_ENC_START_CAP) {
+ ivtv_irq_enc_start_cap(itv);
+ }
+
+ if (combo & IVTV_IRQ_ENC_VBI_CAP) {
+ ivtv_irq_enc_vbi_cap(itv);
+ }
+
+ if (combo & IVTV_IRQ_DEC_VBI_RE_INSERT) {
+ ivtv_irq_dec_vbi_reinsert(itv);
+ }
+
+ if (combo & IVTV_IRQ_ENC_EOS) {
+ IVTV_DEBUG_IRQ("ENC EOS\n");
+ set_bit(IVTV_F_I_EOS, &itv->i_flags);
+ wake_up(&itv->eos_waitq);
+ }
+
+ if (combo & IVTV_IRQ_DEC_DATA_REQ) {
+ ivtv_irq_dec_data_req(itv);
+ }
+
+ /* Decoder Vertical Sync - We can't rely on 'combo', so check if vsync enabled */
+ if (~itv->irqmask & IVTV_IRQ_DEC_VSYNC) {
+ ivtv_irq_vsync(itv);
+ }
+
+ if (combo & IVTV_IRQ_ENC_VIM_RST) {
+ IVTV_DEBUG_IRQ("VIM RST\n");
+ /*ivtv_vapi(itv, CX2341X_ENC_REFRESH_INPUT, 0); */
+ }
+
+ if (combo & IVTV_IRQ_DEC_AUD_MODE_CHG) {
+ IVTV_DEBUG_INFO("Stereo mode changed\n");
+ }
+
+ if ((combo & IVTV_IRQ_DMA) && !test_bit(IVTV_F_I_DMA, &itv->i_flags)) {
+ itv->irq_rr_idx++;
+ for (i = 0; i < IVTV_MAX_STREAMS; i++) {
+ int idx = (i + itv->irq_rr_idx) % IVTV_MAX_STREAMS;
+ struct ivtv_stream *s = &itv->streams[idx];
+
+ if (!test_and_clear_bit(IVTV_F_S_DMA_PENDING, &s->s_flags))
+ continue;
+ if (s->type >= IVTV_DEC_STREAM_TYPE_MPG)
+ ivtv_dma_dec_start(s);
+ else
+ ivtv_dma_enc_start(s);
+ break;
+ }
+
+ if (i == IVTV_MAX_STREAMS &&
+ test_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags))
+ ivtv_udma_start(itv);
+ }
+
+ if ((combo & IVTV_IRQ_DMA) && !test_bit(IVTV_F_I_PIO, &itv->i_flags)) {
+ itv->irq_rr_idx++;
+ for (i = 0; i < IVTV_MAX_STREAMS; i++) {
+ int idx = (i + itv->irq_rr_idx) % IVTV_MAX_STREAMS;
+ struct ivtv_stream *s = &itv->streams[idx];
+
+ if (!test_and_clear_bit(IVTV_F_S_PIO_PENDING, &s->s_flags))
+ continue;
+ if (s->type == IVTV_DEC_STREAM_TYPE_VBI || s->type < IVTV_DEC_STREAM_TYPE_MPG)
+ ivtv_dma_enc_start(s);
+ break;
+ }
+ }
+
+ if (test_and_clear_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags)) {
+ queue_kthread_work(&itv->irq_worker, &itv->irq_work);
+ }
+
+ spin_unlock(&itv->dma_reg_lock);
+
+ /* If we've just handled a 'forced' vsync, it's safest to say it
+ * wasn't ours. Another device may have triggered it at just
+ * the right time.
+ */
+ return vsync_force ? IRQ_NONE : IRQ_HANDLED;
+}
+
+void ivtv_unfinished_dma(unsigned long arg)
+{
+ struct ivtv *itv = (struct ivtv *)arg;
+
+ if (!test_bit(IVTV_F_I_DMA, &itv->i_flags))
+ return;
+ IVTV_ERR("DMA TIMEOUT %08x %d\n", read_reg(IVTV_REG_DMASTATUS), itv->cur_dma_stream);
+
+ write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
+ clear_bit(IVTV_F_I_UDMA, &itv->i_flags);
+ clear_bit(IVTV_F_I_DMA, &itv->i_flags);
+ itv->cur_dma_stream = -1;
+ wake_up(&itv->dma_waitq);
+}
diff --git a/drivers/media/video/ivtv/ivtv-irq.h b/drivers/media/video/ivtv/ivtv-irq.h
new file mode 100644
index 00000000..1e844337
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-irq.h
@@ -0,0 +1,53 @@
+/*
+ interrupt handling
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef IVTV_IRQ_H
+#define IVTV_IRQ_H
+
+#define IVTV_IRQ_ENC_START_CAP (0x1 << 31)
+#define IVTV_IRQ_ENC_EOS (0x1 << 30)
+#define IVTV_IRQ_ENC_VBI_CAP (0x1 << 29)
+#define IVTV_IRQ_ENC_VIM_RST (0x1 << 28)
+#define IVTV_IRQ_ENC_DMA_COMPLETE (0x1 << 27)
+#define IVTV_IRQ_ENC_PIO_COMPLETE (0x1 << 25)
+#define IVTV_IRQ_DEC_AUD_MODE_CHG (0x1 << 24)
+#define IVTV_IRQ_DEC_DATA_REQ (0x1 << 22)
+#define IVTV_IRQ_DEC_DMA_COMPLETE (0x1 << 20)
+#define IVTV_IRQ_DEC_VBI_RE_INSERT (0x1 << 19)
+#define IVTV_IRQ_DMA_ERR (0x1 << 18)
+#define IVTV_IRQ_DMA_WRITE (0x1 << 17)
+#define IVTV_IRQ_DMA_READ (0x1 << 16)
+#define IVTV_IRQ_DEC_VSYNC (0x1 << 10)
+
+/* IRQ Masks */
+#define IVTV_IRQ_MASK_INIT (IVTV_IRQ_DMA_ERR|IVTV_IRQ_ENC_DMA_COMPLETE|\
+ IVTV_IRQ_DMA_READ|IVTV_IRQ_ENC_PIO_COMPLETE)
+
+#define IVTV_IRQ_MASK_CAPTURE (IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_EOS)
+#define IVTV_IRQ_MASK_DECODE (IVTV_IRQ_DEC_DATA_REQ|IVTV_IRQ_DEC_AUD_MODE_CHG)
+
+irqreturn_t ivtv_irq_handler(int irq, void *dev_id);
+
+void ivtv_irq_work_handler(struct kthread_work *work);
+void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock);
+void ivtv_unfinished_dma(unsigned long arg);
+
+#endif
diff --git a/drivers/media/video/ivtv/ivtv-mailbox.c b/drivers/media/video/ivtv/ivtv-mailbox.c
new file mode 100644
index 00000000..e3ce9676
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-mailbox.c
@@ -0,0 +1,387 @@
+/*
+ mailbox functions
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+
+#include "ivtv-driver.h"
+#include "ivtv-mailbox.h"
+
+/* Firmware mailbox flags*/
+#define IVTV_MBOX_FIRMWARE_DONE 0x00000004
+#define IVTV_MBOX_DRIVER_DONE 0x00000002
+#define IVTV_MBOX_DRIVER_BUSY 0x00000001
+#define IVTV_MBOX_FREE 0x00000000
+
+/* Firmware mailbox standard timeout */
+#define IVTV_API_STD_TIMEOUT 0x02000000
+
+#define API_CACHE (1 << 0) /* Allow the command to be stored in the cache */
+#define API_RESULT (1 << 1) /* Allow 1 second for this cmd to end */
+#define API_FAST_RESULT (3 << 1) /* Allow 0.1 second for this cmd to end */
+#define API_DMA (1 << 3) /* DMA mailbox, has special handling */
+#define API_HIGH_VOL (1 << 5) /* High volume command (i.e. called during encoding or decoding) */
+#define API_NO_WAIT_MB (1 << 4) /* Command may not wait for a free mailbox */
+#define API_NO_WAIT_RES (1 << 5) /* Command may not wait for the result */
+#define API_NO_POLL (1 << 6) /* Avoid pointless polling */
+
+struct ivtv_api_info {
+ int flags; /* Flags, see above */
+ const char *name; /* The name of the command */
+};
+
+#define API_ENTRY(x, f) [x] = { (f), #x }
+
+static const struct ivtv_api_info api_info[256] = {
+ /* MPEG encoder API */
+ API_ENTRY(CX2341X_ENC_PING_FW, API_FAST_RESULT),
+ API_ENTRY(CX2341X_ENC_START_CAPTURE, API_RESULT | API_NO_POLL),
+ API_ENTRY(CX2341X_ENC_STOP_CAPTURE, API_RESULT),
+ API_ENTRY(CX2341X_ENC_SET_AUDIO_ID, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_VIDEO_ID, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_PCR_ID, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_FRAME_RATE, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_FRAME_SIZE, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_BIT_RATE, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_GOP_PROPERTIES, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_ASPECT_RATIO, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_DNR_FILTER_MODE, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_DNR_FILTER_PROPS, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_CORING_LEVELS, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_VBI_LINE, API_RESULT),
+ API_ENTRY(CX2341X_ENC_SET_STREAM_TYPE, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_OUTPUT_PORT, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_AUDIO_PROPERTIES, API_CACHE),
+ API_ENTRY(CX2341X_ENC_HALT_FW, API_FAST_RESULT),
+ API_ENTRY(CX2341X_ENC_GET_VERSION, API_FAST_RESULT),
+ API_ENTRY(CX2341X_ENC_SET_GOP_CLOSURE, API_CACHE),
+ API_ENTRY(CX2341X_ENC_GET_SEQ_END, API_RESULT),
+ API_ENTRY(CX2341X_ENC_SET_PGM_INDEX_INFO, API_FAST_RESULT),
+ API_ENTRY(CX2341X_ENC_SET_VBI_CONFIG, API_RESULT),
+ API_ENTRY(CX2341X_ENC_SET_DMA_BLOCK_SIZE, API_CACHE),
+ API_ENTRY(CX2341X_ENC_GET_PREV_DMA_INFO_MB_10, API_FAST_RESULT),
+ API_ENTRY(CX2341X_ENC_GET_PREV_DMA_INFO_MB_9, API_FAST_RESULT),
+ API_ENTRY(CX2341X_ENC_SCHED_DMA_TO_HOST, API_DMA | API_HIGH_VOL),
+ API_ENTRY(CX2341X_ENC_INITIALIZE_INPUT, API_RESULT),
+ API_ENTRY(CX2341X_ENC_SET_FRAME_DROP_RATE, API_CACHE),
+ API_ENTRY(CX2341X_ENC_PAUSE_ENCODER, API_RESULT),
+ API_ENTRY(CX2341X_ENC_REFRESH_INPUT, API_NO_WAIT_MB | API_HIGH_VOL),
+ API_ENTRY(CX2341X_ENC_SET_COPYRIGHT, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_EVENT_NOTIFICATION, API_RESULT),
+ API_ENTRY(CX2341X_ENC_SET_NUM_VSYNC_LINES, API_CACHE),
+ API_ENTRY(CX2341X_ENC_SET_PLACEHOLDER, API_CACHE),
+ API_ENTRY(CX2341X_ENC_MUTE_VIDEO, API_RESULT),
+ API_ENTRY(CX2341X_ENC_MUTE_AUDIO, API_RESULT),
+ API_ENTRY(CX2341X_ENC_SET_VERT_CROP_LINE, API_FAST_RESULT),
+ API_ENTRY(CX2341X_ENC_MISC, API_FAST_RESULT),
+ /* Obsolete PULLDOWN API command */
+ API_ENTRY(0xb1, API_CACHE),
+
+ /* MPEG decoder API */
+ API_ENTRY(CX2341X_DEC_PING_FW, API_FAST_RESULT),
+ API_ENTRY(CX2341X_DEC_START_PLAYBACK, API_RESULT | API_NO_POLL),
+ API_ENTRY(CX2341X_DEC_STOP_PLAYBACK, API_RESULT),
+ API_ENTRY(CX2341X_DEC_SET_PLAYBACK_SPEED, API_RESULT),
+ API_ENTRY(CX2341X_DEC_STEP_VIDEO, API_RESULT),
+ API_ENTRY(CX2341X_DEC_SET_DMA_BLOCK_SIZE, API_CACHE),
+ API_ENTRY(CX2341X_DEC_GET_XFER_INFO, API_FAST_RESULT),
+ API_ENTRY(CX2341X_DEC_GET_DMA_STATUS, API_FAST_RESULT),
+ API_ENTRY(CX2341X_DEC_SCHED_DMA_FROM_HOST, API_DMA | API_HIGH_VOL),
+ API_ENTRY(CX2341X_DEC_PAUSE_PLAYBACK, API_RESULT),
+ API_ENTRY(CX2341X_DEC_HALT_FW, API_FAST_RESULT),
+ API_ENTRY(CX2341X_DEC_SET_STANDARD, API_CACHE),
+ API_ENTRY(CX2341X_DEC_GET_VERSION, API_FAST_RESULT),
+ API_ENTRY(CX2341X_DEC_SET_STREAM_INPUT, API_CACHE),
+ API_ENTRY(CX2341X_DEC_GET_TIMING_INFO, API_RESULT /*| API_NO_WAIT_RES*/),
+ API_ENTRY(CX2341X_DEC_SET_AUDIO_MODE, API_CACHE),
+ API_ENTRY(CX2341X_DEC_SET_EVENT_NOTIFICATION, API_RESULT),
+ API_ENTRY(CX2341X_DEC_SET_DISPLAY_BUFFERS, API_CACHE),
+ API_ENTRY(CX2341X_DEC_EXTRACT_VBI, API_RESULT),
+ API_ENTRY(CX2341X_DEC_SET_DECODER_SOURCE, API_FAST_RESULT),
+ API_ENTRY(CX2341X_DEC_SET_PREBUFFERING, API_CACHE),
+
+ /* OSD API */
+ API_ENTRY(CX2341X_OSD_GET_FRAMEBUFFER, API_FAST_RESULT),
+ API_ENTRY(CX2341X_OSD_GET_PIXEL_FORMAT, API_FAST_RESULT),
+ API_ENTRY(CX2341X_OSD_SET_PIXEL_FORMAT, API_CACHE),
+ API_ENTRY(CX2341X_OSD_GET_STATE, API_FAST_RESULT),
+ API_ENTRY(CX2341X_OSD_SET_STATE, API_CACHE),
+ API_ENTRY(CX2341X_OSD_GET_OSD_COORDS, API_FAST_RESULT),
+ API_ENTRY(CX2341X_OSD_SET_OSD_COORDS, API_CACHE),
+ API_ENTRY(CX2341X_OSD_GET_SCREEN_COORDS, API_FAST_RESULT),
+ API_ENTRY(CX2341X_OSD_SET_SCREEN_COORDS, API_CACHE),
+ API_ENTRY(CX2341X_OSD_GET_GLOBAL_ALPHA, API_FAST_RESULT),
+ API_ENTRY(CX2341X_OSD_SET_GLOBAL_ALPHA, API_CACHE),
+ API_ENTRY(CX2341X_OSD_SET_BLEND_COORDS, API_CACHE),
+ API_ENTRY(CX2341X_OSD_GET_FLICKER_STATE, API_FAST_RESULT),
+ API_ENTRY(CX2341X_OSD_SET_FLICKER_STATE, API_CACHE),
+ API_ENTRY(CX2341X_OSD_BLT_COPY, API_RESULT),
+ API_ENTRY(CX2341X_OSD_BLT_FILL, API_RESULT),
+ API_ENTRY(CX2341X_OSD_BLT_TEXT, API_RESULT),
+ API_ENTRY(CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, API_CACHE),
+ API_ENTRY(CX2341X_OSD_SET_CHROMA_KEY, API_CACHE),
+ API_ENTRY(CX2341X_OSD_GET_ALPHA_CONTENT_INDEX, API_FAST_RESULT),
+ API_ENTRY(CX2341X_OSD_SET_ALPHA_CONTENT_INDEX, API_CACHE)
+};
+
+static int try_mailbox(struct ivtv *itv, struct ivtv_mailbox_data *mbdata, int mb)
+{
+ u32 flags = readl(&mbdata->mbox[mb].flags);
+ int is_free = flags == IVTV_MBOX_FREE || (flags & IVTV_MBOX_FIRMWARE_DONE);
+
+ /* if the mailbox is free, then try to claim it */
+ if (is_free && !test_and_set_bit(mb, &mbdata->busy)) {
+ write_sync(IVTV_MBOX_DRIVER_BUSY, &mbdata->mbox[mb].flags);
+ return 1;
+ }
+ return 0;
+}
+
+/* Try to find a free mailbox. Note mailbox 0 is reserved for DMA and so is not
+ attempted here. */
+static int get_mailbox(struct ivtv *itv, struct ivtv_mailbox_data *mbdata, int flags)
+{
+ unsigned long then = jiffies;
+ int i, mb;
+ int max_mbox = mbdata->max_mbox;
+ int retries = 100;
+
+ /* All slow commands use the same mailbox, serializing them and also
+ leaving the other mailbox free for simple fast commands. */
+ if ((flags & API_FAST_RESULT) == API_RESULT)
+ max_mbox = 1;
+
+ /* find free non-DMA mailbox */
+ for (i = 0; i < retries; i++) {
+ for (mb = 1; mb <= max_mbox; mb++)
+ if (try_mailbox(itv, mbdata, mb))
+ return mb;
+
+ /* Sleep before a retry, if not atomic */
+ if (!(flags & API_NO_WAIT_MB)) {
+ if (time_after(jiffies,
+ then + msecs_to_jiffies(10*retries)))
+ break;
+ ivtv_msleep_timeout(10, 0);
+ }
+ }
+ return -ENODEV;
+}
+
+static void write_mailbox(volatile struct ivtv_mailbox __iomem *mbox, int cmd, int args, u32 data[])
+{
+ int i;
+
+ write_sync(cmd, &mbox->cmd);
+ write_sync(IVTV_API_STD_TIMEOUT, &mbox->timeout);
+
+ for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++)
+ write_sync(data[i], &mbox->data[i]);
+
+ write_sync(IVTV_MBOX_DRIVER_DONE | IVTV_MBOX_DRIVER_BUSY, &mbox->flags);
+}
+
+static void clear_all_mailboxes(struct ivtv *itv, struct ivtv_mailbox_data *mbdata)
+{
+ int i;
+
+ for (i = 0; i <= mbdata->max_mbox; i++) {
+ IVTV_DEBUG_WARN("Clearing mailbox %d: cmd 0x%08x flags 0x%08x\n",
+ i, readl(&mbdata->mbox[i].cmd), readl(&mbdata->mbox[i].flags));
+ write_sync(0, &mbdata->mbox[i].flags);
+ clear_bit(i, &mbdata->busy);
+ }
+}
+
+static int ivtv_api_call(struct ivtv *itv, int cmd, int args, u32 data[])
+{
+ struct ivtv_mailbox_data *mbdata = (cmd >= 128) ? &itv->enc_mbox : &itv->dec_mbox;
+ volatile struct ivtv_mailbox __iomem *mbox;
+ int api_timeout = msecs_to_jiffies(1000);
+ int flags, mb, i;
+ unsigned long then;
+
+ /* sanity checks */
+ if (NULL == mbdata) {
+ IVTV_ERR("No mailbox allocated\n");
+ return -ENODEV;
+ }
+ if (args < 0 || args > CX2341X_MBOX_MAX_DATA ||
+ cmd < 0 || cmd > 255 || api_info[cmd].name == NULL) {
+ IVTV_ERR("Invalid MB call: cmd = 0x%02x, args = %d\n", cmd, args);
+ return -EINVAL;
+ }
+
+ if (api_info[cmd].flags & API_HIGH_VOL) {
+ IVTV_DEBUG_HI_MB("MB Call: %s\n", api_info[cmd].name);
+ }
+ else {
+ IVTV_DEBUG_MB("MB Call: %s\n", api_info[cmd].name);
+ }
+
+ /* clear possibly uninitialized part of data array */
+ for (i = args; i < CX2341X_MBOX_MAX_DATA; i++)
+ data[i] = 0;
+
+ /* If this command was issued within the last 30 minutes and with identical
+ data, then just return 0 as there is no need to issue this command again.
+ Just an optimization to prevent unnecessary use of mailboxes. */
+ if (itv->api_cache[cmd].last_jiffies &&
+ time_before(jiffies,
+ itv->api_cache[cmd].last_jiffies +
+ msecs_to_jiffies(1800000)) &&
+ !memcmp(data, itv->api_cache[cmd].data, sizeof(itv->api_cache[cmd].data))) {
+ itv->api_cache[cmd].last_jiffies = jiffies;
+ return 0;
+ }
+
+ flags = api_info[cmd].flags;
+
+ if (flags & API_DMA) {
+ for (i = 0; i < 100; i++) {
+ mb = i % (mbdata->max_mbox + 1);
+ if (try_mailbox(itv, mbdata, mb)) {
+ write_mailbox(&mbdata->mbox[mb], cmd, args, data);
+ clear_bit(mb, &mbdata->busy);
+ return 0;
+ }
+ IVTV_DEBUG_WARN("%s: mailbox %d not free %08x\n",
+ api_info[cmd].name, mb, readl(&mbdata->mbox[mb].flags));
+ }
+ IVTV_WARN("Could not find free DMA mailbox for %s\n", api_info[cmd].name);
+ clear_all_mailboxes(itv, mbdata);
+ return -EBUSY;
+ }
+
+ if ((flags & API_FAST_RESULT) == API_FAST_RESULT)
+ api_timeout = msecs_to_jiffies(100);
+
+ mb = get_mailbox(itv, mbdata, flags);
+ if (mb < 0) {
+ IVTV_DEBUG_WARN("No free mailbox found (%s)\n", api_info[cmd].name);
+ clear_all_mailboxes(itv, mbdata);
+ return -EBUSY;
+ }
+ mbox = &mbdata->mbox[mb];
+ write_mailbox(mbox, cmd, args, data);
+ if (flags & API_CACHE) {
+ memcpy(itv->api_cache[cmd].data, data, sizeof(itv->api_cache[cmd].data));
+ itv->api_cache[cmd].last_jiffies = jiffies;
+ }
+ if ((flags & API_RESULT) == 0) {
+ clear_bit(mb, &mbdata->busy);
+ return 0;
+ }
+
+ /* Get results */
+ then = jiffies;
+
+ if (!(flags & API_NO_POLL)) {
+ /* First try to poll, then switch to delays */
+ for (i = 0; i < 100; i++) {
+ if (readl(&mbox->flags) & IVTV_MBOX_FIRMWARE_DONE)
+ break;
+ }
+ }
+ while (!(readl(&mbox->flags) & IVTV_MBOX_FIRMWARE_DONE)) {
+ if (time_after(jiffies, then + api_timeout)) {
+ IVTV_DEBUG_WARN("Could not get result (%s)\n", api_info[cmd].name);
+ /* reset the mailbox, but it is likely too late already */
+ write_sync(0, &mbox->flags);
+ clear_bit(mb, &mbdata->busy);
+ return -EIO;
+ }
+ if (flags & API_NO_WAIT_RES)
+ mdelay(1);
+ else
+ ivtv_msleep_timeout(1, 0);
+ }
+ if (time_after(jiffies, then + msecs_to_jiffies(100)))
+ IVTV_DEBUG_WARN("%s took %u jiffies\n",
+ api_info[cmd].name,
+ jiffies_to_msecs(jiffies - then));
+
+ for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++)
+ data[i] = readl(&mbox->data[i]);
+ write_sync(0, &mbox->flags);
+ clear_bit(mb, &mbdata->busy);
+ return 0;
+}
+
+int ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[])
+{
+ int res = ivtv_api_call(itv, cmd, args, data);
+
+ /* Allow a single retry, probably already too late though.
+ If there is no free mailbox then that is usually an indication
+ of a more serious problem. */
+ return (res == -EBUSY) ? ivtv_api_call(itv, cmd, args, data) : res;
+}
+
+int ivtv_api_func(void *priv, u32 cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA])
+{
+ return ivtv_api(priv, cmd, in, data);
+}
+
+int ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...)
+{
+ va_list ap;
+ int i;
+
+ va_start(ap, args);
+ for (i = 0; i < args; i++) {
+ data[i] = va_arg(ap, u32);
+ }
+ va_end(ap);
+ return ivtv_api(itv, cmd, args, data);
+}
+
+int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ va_list ap;
+ int i;
+
+ va_start(ap, args);
+ for (i = 0; i < args; i++) {
+ data[i] = va_arg(ap, u32);
+ }
+ va_end(ap);
+ return ivtv_api(itv, cmd, args, data);
+}
+
+/* This one is for stuff that can't sleep.. irq handlers, etc.. */
+void ivtv_api_get_data(struct ivtv_mailbox_data *mbdata, int mb,
+ int argc, u32 data[])
+{
+ volatile u32 __iomem *p = mbdata->mbox[mb].data;
+ int i;
+ for (i = 0; i < argc; i++, p++)
+ data[i] = readl(p);
+}
+
+/* Wipe api cache */
+void ivtv_mailbox_cache_invalidate(struct ivtv *itv)
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ itv->api_cache[i].last_jiffies = 0;
+}
diff --git a/drivers/media/video/ivtv/ivtv-mailbox.h b/drivers/media/video/ivtv/ivtv-mailbox.h
new file mode 100644
index 00000000..2c834d2c
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-mailbox.h
@@ -0,0 +1,35 @@
+/*
+ mailbox functions
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef IVTV_MAILBOX_H
+#define IVTV_MAILBOX_H
+
+#define IVTV_MBOX_DMA_END 8
+#define IVTV_MBOX_DMA 9
+
+void ivtv_api_get_data(struct ivtv_mailbox_data *mbdata, int mb,
+ int argc, u32 data[]);
+int ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[]);
+int ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...);
+int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...);
+int ivtv_api_func(void *priv, u32 cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]);
+void ivtv_mailbox_cache_invalidate(struct ivtv *itv);
+
+#endif
diff --git a/drivers/media/video/ivtv/ivtv-queue.c b/drivers/media/video/ivtv/ivtv-queue.c
new file mode 100644
index 00000000..7fde36e6
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-queue.c
@@ -0,0 +1,297 @@
+/*
+ buffer queues.
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-queue.h"
+
+int ivtv_buf_copy_from_user(struct ivtv_stream *s, struct ivtv_buffer *buf, const char __user *src, int copybytes)
+{
+ if (s->buf_size - buf->bytesused < copybytes)
+ copybytes = s->buf_size - buf->bytesused;
+ if (copy_from_user(buf->buf + buf->bytesused, src, copybytes)) {
+ return -EFAULT;
+ }
+ buf->bytesused += copybytes;
+ return copybytes;
+}
+
+void ivtv_buf_swap(struct ivtv_buffer *buf)
+{
+ int i;
+
+ for (i = 0; i < buf->bytesused; i += 4)
+ swab32s((u32 *)(buf->buf + i));
+}
+
+void ivtv_queue_init(struct ivtv_queue *q)
+{
+ INIT_LIST_HEAD(&q->list);
+ q->buffers = 0;
+ q->length = 0;
+ q->bytesused = 0;
+}
+
+void ivtv_enqueue(struct ivtv_stream *s, struct ivtv_buffer *buf, struct ivtv_queue *q)
+{
+ unsigned long flags;
+
+ /* clear the buffer if it is going to be enqueued to the free queue */
+ if (q == &s->q_free) {
+ buf->bytesused = 0;
+ buf->readpos = 0;
+ buf->b_flags = 0;
+ buf->dma_xfer_cnt = 0;
+ }
+ spin_lock_irqsave(&s->qlock, flags);
+ list_add_tail(&buf->list, &q->list);
+ q->buffers++;
+ q->length += s->buf_size;
+ q->bytesused += buf->bytesused - buf->readpos;
+ spin_unlock_irqrestore(&s->qlock, flags);
+}
+
+struct ivtv_buffer *ivtv_dequeue(struct ivtv_stream *s, struct ivtv_queue *q)
+{
+ struct ivtv_buffer *buf = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&s->qlock, flags);
+ if (!list_empty(&q->list)) {
+ buf = list_entry(q->list.next, struct ivtv_buffer, list);
+ list_del_init(q->list.next);
+ q->buffers--;
+ q->length -= s->buf_size;
+ q->bytesused -= buf->bytesused - buf->readpos;
+ }
+ spin_unlock_irqrestore(&s->qlock, flags);
+ return buf;
+}
+
+static void ivtv_queue_move_buf(struct ivtv_stream *s, struct ivtv_queue *from,
+ struct ivtv_queue *to, int clear)
+{
+ struct ivtv_buffer *buf = list_entry(from->list.next, struct ivtv_buffer, list);
+
+ list_move_tail(from->list.next, &to->list);
+ from->buffers--;
+ from->length -= s->buf_size;
+ from->bytesused -= buf->bytesused - buf->readpos;
+ /* special handling for q_free */
+ if (clear)
+ buf->bytesused = buf->readpos = buf->b_flags = buf->dma_xfer_cnt = 0;
+ to->buffers++;
+ to->length += s->buf_size;
+ to->bytesused += buf->bytesused - buf->readpos;
+}
+
+/* Move 'needed_bytes' worth of buffers from queue 'from' into queue 'to'.
+ If 'needed_bytes' == 0, then move all buffers from 'from' into 'to'.
+ If 'steal' != NULL, then buffers may also taken from that queue if
+ needed, but only if 'from' is the free queue.
+
+ The buffer is automatically cleared if it goes to the free queue. It is
+ also cleared if buffers need to be taken from the 'steal' queue and
+ the 'from' queue is the free queue.
+
+ When 'from' is q_free, then needed_bytes is compared to the total
+ available buffer length, otherwise needed_bytes is compared to the
+ bytesused value. For the 'steal' queue the total available buffer
+ length is always used.
+
+ -ENOMEM is returned if the buffers could not be obtained, 0 if all
+ buffers where obtained from the 'from' list and if non-zero then
+ the number of stolen buffers is returned. */
+int ivtv_queue_move(struct ivtv_stream *s, struct ivtv_queue *from, struct ivtv_queue *steal,
+ struct ivtv_queue *to, int needed_bytes)
+{
+ unsigned long flags;
+ int rc = 0;
+ int from_free = from == &s->q_free;
+ int to_free = to == &s->q_free;
+ int bytes_available, bytes_steal;
+
+ spin_lock_irqsave(&s->qlock, flags);
+ if (needed_bytes == 0) {
+ from_free = 1;
+ needed_bytes = from->length;
+ }
+
+ bytes_available = from_free ? from->length : from->bytesused;
+ bytes_steal = (from_free && steal) ? steal->length : 0;
+
+ if (bytes_available + bytes_steal < needed_bytes) {
+ spin_unlock_irqrestore(&s->qlock, flags);
+ return -ENOMEM;
+ }
+ while (bytes_available < needed_bytes) {
+ struct ivtv_buffer *buf = list_entry(steal->list.prev, struct ivtv_buffer, list);
+ u16 dma_xfer_cnt = buf->dma_xfer_cnt;
+
+ /* move buffers from the tail of the 'steal' queue to the tail of the
+ 'from' queue. Always copy all the buffers with the same dma_xfer_cnt
+ value, this ensures that you do not end up with partial frame data
+ if one frame is stored in multiple buffers. */
+ while (dma_xfer_cnt == buf->dma_xfer_cnt) {
+ list_move_tail(steal->list.prev, &from->list);
+ rc++;
+ steal->buffers--;
+ steal->length -= s->buf_size;
+ steal->bytesused -= buf->bytesused - buf->readpos;
+ buf->bytesused = buf->readpos = buf->b_flags = buf->dma_xfer_cnt = 0;
+ from->buffers++;
+ from->length += s->buf_size;
+ bytes_available += s->buf_size;
+ if (list_empty(&steal->list))
+ break;
+ buf = list_entry(steal->list.prev, struct ivtv_buffer, list);
+ }
+ }
+ if (from_free) {
+ u32 old_length = to->length;
+
+ while (to->length - old_length < needed_bytes) {
+ ivtv_queue_move_buf(s, from, to, 1);
+ }
+ }
+ else {
+ u32 old_bytesused = to->bytesused;
+
+ while (to->bytesused - old_bytesused < needed_bytes) {
+ ivtv_queue_move_buf(s, from, to, to_free);
+ }
+ }
+ spin_unlock_irqrestore(&s->qlock, flags);
+ return rc;
+}
+
+void ivtv_flush_queues(struct ivtv_stream *s)
+{
+ ivtv_queue_move(s, &s->q_io, NULL, &s->q_free, 0);
+ ivtv_queue_move(s, &s->q_full, NULL, &s->q_free, 0);
+ ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0);
+ ivtv_queue_move(s, &s->q_predma, NULL, &s->q_free, 0);
+}
+
+int ivtv_stream_alloc(struct ivtv_stream *s)
+{
+ struct ivtv *itv = s->itv;
+ int SGsize = sizeof(struct ivtv_sg_host_element) * s->buffers;
+ int i;
+
+ if (s->buffers == 0)
+ return 0;
+
+ IVTV_DEBUG_INFO("Allocate %s%s stream: %d x %d buffers (%dkB total)\n",
+ s->dma != PCI_DMA_NONE ? "DMA " : "",
+ s->name, s->buffers, s->buf_size, s->buffers * s->buf_size / 1024);
+
+ s->sg_pending = kzalloc(SGsize, GFP_KERNEL|__GFP_NOWARN);
+ if (s->sg_pending == NULL) {
+ IVTV_ERR("Could not allocate sg_pending for %s stream\n", s->name);
+ return -ENOMEM;
+ }
+ s->sg_pending_size = 0;
+
+ s->sg_processing = kzalloc(SGsize, GFP_KERNEL|__GFP_NOWARN);
+ if (s->sg_processing == NULL) {
+ IVTV_ERR("Could not allocate sg_processing for %s stream\n", s->name);
+ kfree(s->sg_pending);
+ s->sg_pending = NULL;
+ return -ENOMEM;
+ }
+ s->sg_processing_size = 0;
+
+ s->sg_dma = kzalloc(sizeof(struct ivtv_sg_element),
+ GFP_KERNEL|__GFP_NOWARN);
+ if (s->sg_dma == NULL) {
+ IVTV_ERR("Could not allocate sg_dma for %s stream\n", s->name);
+ kfree(s->sg_pending);
+ s->sg_pending = NULL;
+ kfree(s->sg_processing);
+ s->sg_processing = NULL;
+ return -ENOMEM;
+ }
+ if (ivtv_might_use_dma(s)) {
+ s->sg_handle = pci_map_single(itv->pdev, s->sg_dma,
+ sizeof(struct ivtv_sg_element), PCI_DMA_TODEVICE);
+ ivtv_stream_sync_for_cpu(s);
+ }
+
+ /* allocate stream buffers. Initially all buffers are in q_free. */
+ for (i = 0; i < s->buffers; i++) {
+ struct ivtv_buffer *buf = kzalloc(sizeof(struct ivtv_buffer),
+ GFP_KERNEL|__GFP_NOWARN);
+
+ if (buf == NULL)
+ break;
+ buf->buf = kmalloc(s->buf_size + 256, GFP_KERNEL|__GFP_NOWARN);
+ if (buf->buf == NULL) {
+ kfree(buf);
+ break;
+ }
+ INIT_LIST_HEAD(&buf->list);
+ if (ivtv_might_use_dma(s)) {
+ buf->dma_handle = pci_map_single(s->itv->pdev,
+ buf->buf, s->buf_size + 256, s->dma);
+ ivtv_buf_sync_for_cpu(s, buf);
+ }
+ ivtv_enqueue(s, buf, &s->q_free);
+ }
+ if (i == s->buffers)
+ return 0;
+ IVTV_ERR("Couldn't allocate buffers for %s stream\n", s->name);
+ ivtv_stream_free(s);
+ return -ENOMEM;
+}
+
+void ivtv_stream_free(struct ivtv_stream *s)
+{
+ struct ivtv_buffer *buf;
+
+ /* move all buffers to q_free */
+ ivtv_flush_queues(s);
+
+ /* empty q_free */
+ while ((buf = ivtv_dequeue(s, &s->q_free))) {
+ if (ivtv_might_use_dma(s))
+ pci_unmap_single(s->itv->pdev, buf->dma_handle,
+ s->buf_size + 256, s->dma);
+ kfree(buf->buf);
+ kfree(buf);
+ }
+
+ /* Free SG Array/Lists */
+ if (s->sg_dma != NULL) {
+ if (s->sg_handle != IVTV_DMA_UNMAPPED) {
+ pci_unmap_single(s->itv->pdev, s->sg_handle,
+ sizeof(struct ivtv_sg_element), PCI_DMA_TODEVICE);
+ s->sg_handle = IVTV_DMA_UNMAPPED;
+ }
+ kfree(s->sg_pending);
+ kfree(s->sg_processing);
+ kfree(s->sg_dma);
+ s->sg_pending = NULL;
+ s->sg_processing = NULL;
+ s->sg_dma = NULL;
+ s->sg_pending_size = 0;
+ s->sg_processing_size = 0;
+ }
+}
diff --git a/drivers/media/video/ivtv/ivtv-queue.h b/drivers/media/video/ivtv/ivtv-queue.h
new file mode 100644
index 00000000..91233839
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-queue.h
@@ -0,0 +1,96 @@
+/*
+ buffer queues.
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef IVTV_QUEUE_H
+#define IVTV_QUEUE_H
+
+#define IVTV_DMA_UNMAPPED ((u32) -1)
+#define SLICED_VBI_PIO 0
+
+/* ivtv_buffer utility functions */
+
+static inline int ivtv_might_use_pio(struct ivtv_stream *s)
+{
+ return s->dma == PCI_DMA_NONE || (SLICED_VBI_PIO && s->type == IVTV_ENC_STREAM_TYPE_VBI);
+}
+
+static inline int ivtv_use_pio(struct ivtv_stream *s)
+{
+ struct ivtv *itv = s->itv;
+
+ return s->dma == PCI_DMA_NONE ||
+ (SLICED_VBI_PIO && s->type == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set);
+}
+
+static inline int ivtv_might_use_dma(struct ivtv_stream *s)
+{
+ return s->dma != PCI_DMA_NONE;
+}
+
+static inline int ivtv_use_dma(struct ivtv_stream *s)
+{
+ return !ivtv_use_pio(s);
+}
+
+static inline void ivtv_buf_sync_for_cpu(struct ivtv_stream *s, struct ivtv_buffer *buf)
+{
+ if (ivtv_use_dma(s))
+ pci_dma_sync_single_for_cpu(s->itv->pdev, buf->dma_handle,
+ s->buf_size + 256, s->dma);
+}
+
+static inline void ivtv_buf_sync_for_device(struct ivtv_stream *s, struct ivtv_buffer *buf)
+{
+ if (ivtv_use_dma(s))
+ pci_dma_sync_single_for_device(s->itv->pdev, buf->dma_handle,
+ s->buf_size + 256, s->dma);
+}
+
+int ivtv_buf_copy_from_user(struct ivtv_stream *s, struct ivtv_buffer *buf, const char __user *src, int copybytes);
+void ivtv_buf_swap(struct ivtv_buffer *buf);
+
+/* ivtv_queue utility functions */
+void ivtv_queue_init(struct ivtv_queue *q);
+void ivtv_enqueue(struct ivtv_stream *s, struct ivtv_buffer *buf, struct ivtv_queue *q);
+struct ivtv_buffer *ivtv_dequeue(struct ivtv_stream *s, struct ivtv_queue *q);
+int ivtv_queue_move(struct ivtv_stream *s, struct ivtv_queue *from, struct ivtv_queue *steal,
+ struct ivtv_queue *to, int needed_bytes);
+void ivtv_flush_queues(struct ivtv_stream *s);
+
+/* ivtv_stream utility functions */
+int ivtv_stream_alloc(struct ivtv_stream *s);
+void ivtv_stream_free(struct ivtv_stream *s);
+
+static inline void ivtv_stream_sync_for_cpu(struct ivtv_stream *s)
+{
+ if (ivtv_use_dma(s))
+ pci_dma_sync_single_for_cpu(s->itv->pdev, s->sg_handle,
+ sizeof(struct ivtv_sg_element), PCI_DMA_TODEVICE);
+}
+
+static inline void ivtv_stream_sync_for_device(struct ivtv_stream *s)
+{
+ if (ivtv_use_dma(s))
+ pci_dma_sync_single_for_device(s->itv->pdev, s->sg_handle,
+ sizeof(struct ivtv_sg_element), PCI_DMA_TODEVICE);
+}
+
+#endif
diff --git a/drivers/media/video/ivtv/ivtv-routing.c b/drivers/media/video/ivtv/ivtv-routing.c
new file mode 100644
index 00000000..8898c569
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-routing.c
@@ -0,0 +1,119 @@
+/*
+ Audio/video-routing-related ivtv functions.
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-i2c.h"
+#include "ivtv-cards.h"
+#include "ivtv-gpio.h"
+#include "ivtv-routing.h"
+
+#include <media/msp3400.h>
+#include <media/m52790.h>
+#include <media/upd64031a.h>
+#include <media/upd64083.h>
+
+/* Selects the audio input and output according to the current
+ settings. */
+void ivtv_audio_set_io(struct ivtv *itv)
+{
+ const struct ivtv_card_audio_input *in;
+ u32 input, output = 0;
+
+ /* Determine which input to use */
+ if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags))
+ in = &itv->card->radio_input;
+ else
+ in = &itv->card->audio_inputs[itv->audio_input];
+
+ /* handle muxer chips */
+ input = in->muxer_input;
+ if (itv->card->hw_muxer & IVTV_HW_M52790)
+ output = M52790_OUT_STEREO;
+ v4l2_subdev_call(itv->sd_muxer, audio, s_routing,
+ input, output, 0);
+
+ input = in->audio_input;
+ output = 0;
+ if (itv->card->hw_audio & IVTV_HW_MSP34XX)
+ output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1);
+ ivtv_call_hw(itv, itv->card->hw_audio, audio, s_routing,
+ input, output, 0);
+}
+
+/* Selects the video input and output according to the current
+ settings. */
+void ivtv_video_set_io(struct ivtv *itv)
+{
+ int inp = itv->active_input;
+ u32 input;
+ u32 type;
+
+ v4l2_subdev_call(itv->sd_video, video, s_routing,
+ itv->card->video_inputs[inp].video_input, 0, 0);
+
+ type = itv->card->video_inputs[inp].video_type;
+
+ if (type == IVTV_CARD_INPUT_VID_TUNER) {
+ input = 0; /* Tuner */
+ } else if (type < IVTV_CARD_INPUT_COMPOSITE1) {
+ input = 2; /* S-Video */
+ } else {
+ input = 1; /* Composite */
+ }
+
+ if (itv->card->hw_video & IVTV_HW_GPIO)
+ ivtv_call_hw(itv, IVTV_HW_GPIO, video, s_routing,
+ input, 0, 0);
+
+ if (itv->card->hw_video & IVTV_HW_UPD64031A) {
+ if (type == IVTV_CARD_INPUT_VID_TUNER ||
+ type >= IVTV_CARD_INPUT_COMPOSITE1) {
+ /* Composite: GR on, connect to 3DYCS */
+ input = UPD64031A_GR_ON | UPD64031A_3DYCS_COMPOSITE;
+ } else {
+ /* S-Video: GR bypassed, turn it off */
+ input = UPD64031A_GR_OFF | UPD64031A_3DYCS_DISABLE;
+ }
+ input |= itv->card->gr_config;
+
+ ivtv_call_hw(itv, IVTV_HW_UPD64031A, video, s_routing,
+ input, 0, 0);
+ }
+
+ if (itv->card->hw_video & IVTV_HW_UPD6408X) {
+ input = UPD64083_YCS_MODE;
+ if (type > IVTV_CARD_INPUT_VID_TUNER &&
+ type < IVTV_CARD_INPUT_COMPOSITE1) {
+ /* S-Video uses YCNR mode and internal Y-ADC, the
+ upd64031a is not used. */
+ input |= UPD64083_YCNR_MODE;
+ }
+ else if (itv->card->hw_video & IVTV_HW_UPD64031A) {
+ /* Use upd64031a output for tuner and
+ composite(CX23416GYC only) inputs */
+ if (type == IVTV_CARD_INPUT_VID_TUNER ||
+ itv->card->type == IVTV_CARD_CX23416GYC) {
+ input |= UPD64083_EXT_Y_ADC;
+ }
+ }
+ ivtv_call_hw(itv, IVTV_HW_UPD6408X, video, s_routing,
+ input, 0, 0);
+ }
+}
diff --git a/drivers/media/video/ivtv/ivtv-routing.h b/drivers/media/video/ivtv/ivtv-routing.h
new file mode 100644
index 00000000..c72a9731
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-routing.h
@@ -0,0 +1,27 @@
+/*
+ Audio/video-routing-related ivtv functions.
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef IVTV_ROUTING_H
+#define IVTV_ROUTING_H
+
+void ivtv_audio_set_io(struct ivtv *itv);
+void ivtv_video_set_io(struct ivtv *itv);
+
+#endif
diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c
new file mode 100644
index 00000000..7ea5ca7f
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-streams.c
@@ -0,0 +1,1014 @@
+/*
+ init/start/stop/exit stream functions
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* License: GPL
+ * Author: Kevin Thayer <nufan_wfk at yahoo dot com>
+ *
+ * This file will hold API related functions, both internal (firmware api)
+ * and external (v4l2, etc)
+ *
+ * -----
+ * MPG600/MPG160 support by T.Adachi <tadachi@tadachi-net.com>
+ * and Takeru KOMORIYA<komoriya@paken.org>
+ *
+ * AVerMedia M179 GPIO info by Chris Pinkham <cpinkham@bc2va.org>
+ * using information provided by Jiun-Kuei Jung @ AVerMedia.
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-fileops.h"
+#include "ivtv-queue.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-irq.h"
+#include "ivtv-yuv.h"
+#include "ivtv-cards.h"
+#include "ivtv-streams.h"
+#include "ivtv-firmware.h"
+#include <media/v4l2-event.h>
+
+static const struct v4l2_file_operations ivtv_v4l2_enc_fops = {
+ .owner = THIS_MODULE,
+ .read = ivtv_v4l2_read,
+ .write = ivtv_v4l2_write,
+ .open = ivtv_v4l2_open,
+ .unlocked_ioctl = ivtv_v4l2_ioctl,
+ .release = ivtv_v4l2_close,
+ .poll = ivtv_v4l2_enc_poll,
+};
+
+static const struct v4l2_file_operations ivtv_v4l2_dec_fops = {
+ .owner = THIS_MODULE,
+ .read = ivtv_v4l2_read,
+ .write = ivtv_v4l2_write,
+ .open = ivtv_v4l2_open,
+ .unlocked_ioctl = ivtv_v4l2_ioctl,
+ .release = ivtv_v4l2_close,
+ .poll = ivtv_v4l2_dec_poll,
+};
+
+#define IVTV_V4L2_DEC_MPG_OFFSET 16 /* offset from 0 to register decoder mpg v4l2 minors on */
+#define IVTV_V4L2_ENC_PCM_OFFSET 24 /* offset from 0 to register pcm v4l2 minors on */
+#define IVTV_V4L2_ENC_YUV_OFFSET 32 /* offset from 0 to register yuv v4l2 minors on */
+#define IVTV_V4L2_DEC_YUV_OFFSET 48 /* offset from 0 to register decoder yuv v4l2 minors on */
+#define IVTV_V4L2_DEC_VBI_OFFSET 8 /* offset from 0 to register decoder vbi input v4l2 minors on */
+#define IVTV_V4L2_DEC_VOUT_OFFSET 16 /* offset from 0 to register vbi output v4l2 minors on */
+
+static struct {
+ const char *name;
+ int vfl_type;
+ int num_offset;
+ int dma, pio;
+ enum v4l2_buf_type buf_type;
+ u32 v4l2_caps;
+ const struct v4l2_file_operations *fops;
+} ivtv_stream_info[] = {
+ { /* IVTV_ENC_STREAM_TYPE_MPG */
+ "encoder MPG",
+ VFL_TYPE_GRABBER, 0,
+ PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER |
+ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
+ &ivtv_v4l2_enc_fops
+ },
+ { /* IVTV_ENC_STREAM_TYPE_YUV */
+ "encoder YUV",
+ VFL_TYPE_GRABBER, IVTV_V4L2_ENC_YUV_OFFSET,
+ PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER |
+ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
+ &ivtv_v4l2_enc_fops
+ },
+ { /* IVTV_ENC_STREAM_TYPE_VBI */
+ "encoder VBI",
+ VFL_TYPE_VBI, 0,
+ PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_TUNER |
+ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
+ &ivtv_v4l2_enc_fops
+ },
+ { /* IVTV_ENC_STREAM_TYPE_PCM */
+ "encoder PCM",
+ VFL_TYPE_GRABBER, IVTV_V4L2_ENC_PCM_OFFSET,
+ PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_PRIVATE,
+ V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
+ &ivtv_v4l2_enc_fops
+ },
+ { /* IVTV_ENC_STREAM_TYPE_RAD */
+ "encoder radio",
+ VFL_TYPE_RADIO, 0,
+ PCI_DMA_NONE, 1, V4L2_BUF_TYPE_PRIVATE,
+ V4L2_CAP_RADIO | V4L2_CAP_TUNER,
+ &ivtv_v4l2_enc_fops
+ },
+ { /* IVTV_DEC_STREAM_TYPE_MPG */
+ "decoder MPG",
+ VFL_TYPE_GRABBER, IVTV_V4L2_DEC_MPG_OFFSET,
+ PCI_DMA_TODEVICE, 0, V4L2_BUF_TYPE_VIDEO_OUTPUT,
+ V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
+ &ivtv_v4l2_dec_fops
+ },
+ { /* IVTV_DEC_STREAM_TYPE_VBI */
+ "decoder VBI",
+ VFL_TYPE_VBI, IVTV_V4L2_DEC_VBI_OFFSET,
+ PCI_DMA_NONE, 1, V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_READWRITE,
+ &ivtv_v4l2_enc_fops
+ },
+ { /* IVTV_DEC_STREAM_TYPE_VOUT */
+ "decoder VOUT",
+ VFL_TYPE_VBI, IVTV_V4L2_DEC_VOUT_OFFSET,
+ PCI_DMA_NONE, 1, V4L2_BUF_TYPE_VBI_OUTPUT,
+ V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
+ &ivtv_v4l2_dec_fops
+ },
+ { /* IVTV_DEC_STREAM_TYPE_YUV */
+ "decoder YUV",
+ VFL_TYPE_GRABBER, IVTV_V4L2_DEC_YUV_OFFSET,
+ PCI_DMA_TODEVICE, 0, V4L2_BUF_TYPE_VIDEO_OUTPUT,
+ V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
+ &ivtv_v4l2_dec_fops
+ }
+};
+
+static void ivtv_stream_init(struct ivtv *itv, int type)
+{
+ struct ivtv_stream *s = &itv->streams[type];
+ struct video_device *vdev = s->vdev;
+
+ /* we need to keep vdev, so restore it afterwards */
+ memset(s, 0, sizeof(*s));
+ s->vdev = vdev;
+
+ /* initialize ivtv_stream fields */
+ s->itv = itv;
+ s->type = type;
+ s->name = ivtv_stream_info[type].name;
+ s->caps = ivtv_stream_info[type].v4l2_caps;
+
+ if (ivtv_stream_info[type].pio)
+ s->dma = PCI_DMA_NONE;
+ else
+ s->dma = ivtv_stream_info[type].dma;
+ s->buf_size = itv->stream_buf_size[type];
+ if (s->buf_size)
+ s->buffers = (itv->options.kilobytes[type] * 1024 + s->buf_size - 1) / s->buf_size;
+ spin_lock_init(&s->qlock);
+ init_waitqueue_head(&s->waitq);
+ s->sg_handle = IVTV_DMA_UNMAPPED;
+ ivtv_queue_init(&s->q_free);
+ ivtv_queue_init(&s->q_full);
+ ivtv_queue_init(&s->q_dma);
+ ivtv_queue_init(&s->q_predma);
+ ivtv_queue_init(&s->q_io);
+}
+
+static int ivtv_prep_dev(struct ivtv *itv, int type)
+{
+ struct ivtv_stream *s = &itv->streams[type];
+ int num_offset = ivtv_stream_info[type].num_offset;
+ int num = itv->instance + ivtv_first_minor + num_offset;
+
+ /* These four fields are always initialized. If vdev == NULL, then
+ this stream is not in use. In that case no other fields but these
+ four can be used. */
+ s->vdev = NULL;
+ s->itv = itv;
+ s->type = type;
+ s->name = ivtv_stream_info[type].name;
+
+ /* Check whether the radio is supported */
+ if (type == IVTV_ENC_STREAM_TYPE_RAD && !(itv->v4l2_cap & V4L2_CAP_RADIO))
+ return 0;
+ if (type >= IVTV_DEC_STREAM_TYPE_MPG && !(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return 0;
+
+ /* User explicitly selected 0 buffers for these streams, so don't
+ create them. */
+ if (ivtv_stream_info[type].dma != PCI_DMA_NONE &&
+ itv->options.kilobytes[type] == 0) {
+ IVTV_INFO("Disabled %s device\n", ivtv_stream_info[type].name);
+ return 0;
+ }
+
+ ivtv_stream_init(itv, type);
+
+ /* allocate and initialize the v4l2 video device structure */
+ s->vdev = video_device_alloc();
+ if (s->vdev == NULL) {
+ IVTV_ERR("Couldn't allocate v4l2 video_device for %s\n", s->name);
+ return -ENOMEM;
+ }
+
+ snprintf(s->vdev->name, sizeof(s->vdev->name), "%s %s",
+ itv->v4l2_dev.name, s->name);
+
+ s->vdev->num = num;
+ s->vdev->v4l2_dev = &itv->v4l2_dev;
+ s->vdev->fops = ivtv_stream_info[type].fops;
+ s->vdev->ctrl_handler = itv->v4l2_dev.ctrl_handler;
+ s->vdev->release = video_device_release;
+ s->vdev->tvnorms = V4L2_STD_ALL;
+ s->vdev->lock = &itv->serialize_lock;
+ set_bit(V4L2_FL_USE_FH_PRIO, &s->vdev->flags);
+ ivtv_set_funcs(s->vdev);
+ return 0;
+}
+
+/* Initialize v4l2 variables and prepare v4l2 devices */
+int ivtv_streams_setup(struct ivtv *itv)
+{
+ int type;
+
+ /* Setup V4L2 Devices */
+ for (type = 0; type < IVTV_MAX_STREAMS; type++) {
+ /* Prepare device */
+ if (ivtv_prep_dev(itv, type))
+ break;
+
+ if (itv->streams[type].vdev == NULL)
+ continue;
+
+ /* Allocate Stream */
+ if (ivtv_stream_alloc(&itv->streams[type]))
+ break;
+ }
+ if (type == IVTV_MAX_STREAMS)
+ return 0;
+
+ /* One or more streams could not be initialized. Clean 'em all up. */
+ ivtv_streams_cleanup(itv, 0);
+ return -ENOMEM;
+}
+
+static int ivtv_reg_dev(struct ivtv *itv, int type)
+{
+ struct ivtv_stream *s = &itv->streams[type];
+ int vfl_type = ivtv_stream_info[type].vfl_type;
+ const char *name;
+ int num;
+
+ if (s->vdev == NULL)
+ return 0;
+
+ num = s->vdev->num;
+ /* card number + user defined offset + device offset */
+ if (type != IVTV_ENC_STREAM_TYPE_MPG) {
+ struct ivtv_stream *s_mpg = &itv->streams[IVTV_ENC_STREAM_TYPE_MPG];
+
+ if (s_mpg->vdev)
+ num = s_mpg->vdev->num + ivtv_stream_info[type].num_offset;
+ }
+ video_set_drvdata(s->vdev, s);
+
+ /* Register device. First try the desired minor, then any free one. */
+ if (video_register_device_no_warn(s->vdev, vfl_type, num)) {
+ IVTV_ERR("Couldn't register v4l2 device for %s (device node number %d)\n",
+ s->name, num);
+ video_device_release(s->vdev);
+ s->vdev = NULL;
+ return -ENOMEM;
+ }
+ name = video_device_node_name(s->vdev);
+
+ switch (vfl_type) {
+ case VFL_TYPE_GRABBER:
+ IVTV_INFO("Registered device %s for %s (%d kB)\n",
+ name, s->name, itv->options.kilobytes[type]);
+ break;
+ case VFL_TYPE_RADIO:
+ IVTV_INFO("Registered device %s for %s\n",
+ name, s->name);
+ break;
+ case VFL_TYPE_VBI:
+ if (itv->options.kilobytes[type])
+ IVTV_INFO("Registered device %s for %s (%d kB)\n",
+ name, s->name, itv->options.kilobytes[type]);
+ else
+ IVTV_INFO("Registered device %s for %s\n",
+ name, s->name);
+ break;
+ }
+ return 0;
+}
+
+/* Register v4l2 devices */
+int ivtv_streams_register(struct ivtv *itv)
+{
+ int type;
+ int err = 0;
+
+ /* Register V4L2 devices */
+ for (type = 0; type < IVTV_MAX_STREAMS; type++)
+ err |= ivtv_reg_dev(itv, type);
+
+ if (err == 0)
+ return 0;
+
+ /* One or more streams could not be initialized. Clean 'em all up. */
+ ivtv_streams_cleanup(itv, 1);
+ return -ENOMEM;
+}
+
+/* Unregister v4l2 devices */
+void ivtv_streams_cleanup(struct ivtv *itv, int unregister)
+{
+ int type;
+
+ /* Teardown all streams */
+ for (type = 0; type < IVTV_MAX_STREAMS; type++) {
+ struct video_device *vdev = itv->streams[type].vdev;
+
+ itv->streams[type].vdev = NULL;
+ if (vdev == NULL)
+ continue;
+
+ ivtv_stream_free(&itv->streams[type]);
+ /* Unregister or release device */
+ if (unregister)
+ video_unregister_device(vdev);
+ else
+ video_device_release(vdev);
+ }
+}
+
+static void ivtv_vbi_setup(struct ivtv *itv)
+{
+ int raw = ivtv_raw_vbi(itv);
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ int lines;
+ int i;
+
+ /* Reset VBI */
+ ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, 0xffff , 0, 0, 0, 0);
+
+ /* setup VBI registers */
+ if (raw)
+ v4l2_subdev_call(itv->sd_video, vbi, s_raw_fmt, &itv->vbi.in.fmt.vbi);
+ else
+ v4l2_subdev_call(itv->sd_video, vbi, s_sliced_fmt, &itv->vbi.in.fmt.sliced);
+
+ /* determine number of lines and total number of VBI bytes.
+ A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1
+ The '- 1' byte is probably an unused U or V byte. Or something...
+ A sliced line takes 51 bytes: 4 byte frame header, 4 byte internal
+ header, 42 data bytes + checksum (to be confirmed) */
+ if (raw) {
+ lines = itv->vbi.count * 2;
+ } else {
+ lines = itv->is_60hz ? 24 : 38;
+ if (itv->is_60hz && (itv->hw_flags & IVTV_HW_CX25840))
+ lines += 2;
+ }
+
+ itv->vbi.enc_size = lines * (raw ? itv->vbi.raw_size : itv->vbi.sliced_size);
+
+ /* Note: sliced vs raw flag doesn't seem to have any effect
+ TODO: check mode (0x02) value with older ivtv versions. */
+ data[0] = raw | 0x02 | (0xbd << 8);
+
+ /* Every X number of frames a VBI interrupt arrives (frames as in 25 or 30 fps) */
+ data[1] = 1;
+ /* The VBI frames are stored in a ringbuffer with this size (with a VBI frame as unit) */
+ data[2] = raw ? 4 : 4 * (itv->vbi.raw_size / itv->vbi.enc_size);
+ /* The start/stop codes determine which VBI lines end up in the raw VBI data area.
+ The codes are from table 24 in the saa7115 datasheet. Each raw/sliced/video line
+ is framed with codes FF0000XX where XX is the SAV/EAV (Start/End of Active Video)
+ code. These values for raw VBI are obtained from a driver disassembly. The sliced
+ start/stop codes was deduced from this, but they do not appear in the driver.
+ Other code pairs that I found are: 0x250E6249/0x13545454 and 0x25256262/0x38137F54.
+ However, I have no idea what these values are for. */
+ if (itv->hw_flags & IVTV_HW_CX25840) {
+ /* Setup VBI for the cx25840 digitizer */
+ if (raw) {
+ data[3] = 0x20602060;
+ data[4] = 0x30703070;
+ } else {
+ data[3] = 0xB0F0B0F0;
+ data[4] = 0xA0E0A0E0;
+ }
+ /* Lines per frame */
+ data[5] = lines;
+ /* bytes per line */
+ data[6] = (raw ? itv->vbi.raw_size : itv->vbi.sliced_size);
+ } else {
+ /* Setup VBI for the saa7115 digitizer */
+ if (raw) {
+ data[3] = 0x25256262;
+ data[4] = 0x387F7F7F;
+ } else {
+ data[3] = 0xABABECEC;
+ data[4] = 0xB6F1F1F1;
+ }
+ /* Lines per frame */
+ data[5] = lines;
+ /* bytes per line */
+ data[6] = itv->vbi.enc_size / lines;
+ }
+
+ IVTV_DEBUG_INFO(
+ "Setup VBI API header 0x%08x pkts %d buffs %d ln %d sz %d\n",
+ data[0], data[1], data[2], data[5], data[6]);
+
+ ivtv_api(itv, CX2341X_ENC_SET_VBI_CONFIG, 7, data);
+
+ /* returns the VBI encoder memory area. */
+ itv->vbi.enc_start = data[2];
+ itv->vbi.fpi = data[0];
+ if (!itv->vbi.fpi)
+ itv->vbi.fpi = 1;
+
+ IVTV_DEBUG_INFO("Setup VBI start 0x%08x frames %d fpi %d\n",
+ itv->vbi.enc_start, data[1], itv->vbi.fpi);
+
+ /* select VBI lines.
+ Note that the sliced argument seems to have no effect. */
+ for (i = 2; i <= 24; i++) {
+ int valid;
+
+ if (itv->is_60hz) {
+ valid = i >= 10 && i < 22;
+ } else {
+ valid = i >= 6 && i < 24;
+ }
+ ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, i - 1,
+ valid, 0 , 0, 0);
+ ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, (i - 1) | 0x80000000,
+ valid, 0, 0, 0);
+ }
+
+ /* Remaining VBI questions:
+ - Is it possible to select particular VBI lines only for inclusion in the MPEG
+ stream? Currently you can only get the first X lines.
+ - Is mixed raw and sliced VBI possible?
+ - What's the meaning of the raw/sliced flag?
+ - What's the meaning of params 2, 3 & 4 of the Select VBI command? */
+}
+
+int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ struct ivtv *itv = s->itv;
+ int captype = 0, subtype = 0;
+ int enable_passthrough = 0;
+
+ if (s->vdev == NULL)
+ return -EINVAL;
+
+ IVTV_DEBUG_INFO("Start encoder stream %s\n", s->name);
+
+ switch (s->type) {
+ case IVTV_ENC_STREAM_TYPE_MPG:
+ captype = 0;
+ subtype = 3;
+
+ /* Stop Passthrough */
+ if (itv->output_mode == OUT_PASSTHROUGH) {
+ ivtv_passthrough_mode(itv, 0);
+ enable_passthrough = 1;
+ }
+ itv->mpg_data_received = itv->vbi_data_inserted = 0;
+ itv->dualwatch_jiffies = jiffies;
+ itv->dualwatch_stereo_mode = v4l2_ctrl_g_ctrl(itv->cxhdl.audio_mode);
+ itv->search_pack_header = 0;
+ break;
+
+ case IVTV_ENC_STREAM_TYPE_YUV:
+ if (itv->output_mode == OUT_PASSTHROUGH) {
+ captype = 2;
+ subtype = 11; /* video+audio+decoder */
+ break;
+ }
+ captype = 1;
+ subtype = 1;
+ break;
+ case IVTV_ENC_STREAM_TYPE_PCM:
+ captype = 1;
+ subtype = 2;
+ break;
+ case IVTV_ENC_STREAM_TYPE_VBI:
+ captype = 1;
+ subtype = 4;
+
+ itv->vbi.frame = 0;
+ itv->vbi.inserted_frame = 0;
+ memset(itv->vbi.sliced_mpeg_size,
+ 0, sizeof(itv->vbi.sliced_mpeg_size));
+ break;
+ default:
+ return -EINVAL;
+ }
+ s->subtype = subtype;
+ s->buffers_stolen = 0;
+
+ /* Clear Streamoff flags in case left from last capture */
+ clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+
+ if (atomic_read(&itv->capturing) == 0) {
+ int digitizer;
+
+ /* Always use frame based mode. Experiments have demonstrated that byte
+ stream based mode results in dropped frames and corruption. Not often,
+ but occasionally. Many thanks go to Leonard Orb who spent a lot of
+ effort and time trying to trace the cause of the drop outs. */
+ /* 1 frame per DMA */
+ /*ivtv_vapi(itv, CX2341X_ENC_SET_DMA_BLOCK_SIZE, 2, 128, 0); */
+ ivtv_vapi(itv, CX2341X_ENC_SET_DMA_BLOCK_SIZE, 2, 1, 1);
+
+ /* Stuff from Windows, we don't know what it is */
+ ivtv_vapi(itv, CX2341X_ENC_SET_VERT_CROP_LINE, 1, 0);
+ /* According to the docs, this should be correct. However, this is
+ untested. I don't dare enable this without having tested it.
+ Only very few old cards actually have this hardware combination.
+ ivtv_vapi(itv, CX2341X_ENC_SET_VERT_CROP_LINE, 1,
+ ((itv->hw_flags & IVTV_HW_SAA7114) && itv->is_60hz) ? 10001 : 0);
+ */
+ ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 3, !itv->has_cx23415);
+ ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 8, 0);
+ ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 4, 1);
+ ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12);
+
+ /* assign placeholder */
+ ivtv_vapi(itv, CX2341X_ENC_SET_PLACEHOLDER, 12,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+ if (itv->card->hw_all & (IVTV_HW_SAA7115 | IVTV_HW_SAA717X))
+ digitizer = 0xF1;
+ else if (itv->card->hw_all & IVTV_HW_SAA7114)
+ digitizer = 0xEF;
+ else /* cx25840 */
+ digitizer = 0x140;
+
+ ivtv_vapi(itv, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, digitizer, digitizer);
+
+ /* Setup VBI */
+ if (itv->v4l2_cap & V4L2_CAP_VBI_CAPTURE) {
+ ivtv_vbi_setup(itv);
+ }
+
+ /* assign program index info. Mask 7: select I/P/B, Num_req: 400 max */
+ ivtv_vapi_result(itv, data, CX2341X_ENC_SET_PGM_INDEX_INFO, 2, 7, 400);
+ itv->pgm_info_offset = data[0];
+ itv->pgm_info_num = data[1];
+ itv->pgm_info_write_idx = 0;
+ itv->pgm_info_read_idx = 0;
+
+ IVTV_DEBUG_INFO("PGM Index at 0x%08x with %d elements\n",
+ itv->pgm_info_offset, itv->pgm_info_num);
+
+ /* Setup API for Stream */
+ cx2341x_handler_setup(&itv->cxhdl);
+
+ /* mute if capturing radio */
+ if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags))
+ ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1,
+ 1 | (v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute_yuv) << 8));
+ }
+
+ /* Vsync Setup */
+ if (itv->has_cx23415 && !test_and_set_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) {
+ /* event notification (on) */
+ ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 1, IVTV_IRQ_ENC_VIM_RST, -1);
+ ivtv_clear_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST);
+ }
+
+ if (atomic_read(&itv->capturing) == 0) {
+ /* Clear all Pending Interrupts */
+ ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
+
+ clear_bit(IVTV_F_I_EOS, &itv->i_flags);
+
+ cx2341x_handler_set_busy(&itv->cxhdl, 1);
+
+ /* Initialize Digitizer for Capture */
+ /* Avoid tinny audio problem - ensure audio clocks are going */
+ v4l2_subdev_call(itv->sd_audio, audio, s_stream, 1);
+ /* Avoid unpredictable PCI bus hang - disable video clocks */
+ v4l2_subdev_call(itv->sd_video, video, s_stream, 0);
+ ivtv_msleep_timeout(300, 0);
+ ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0);
+ v4l2_subdev_call(itv->sd_video, video, s_stream, 1);
+ }
+
+ /* begin_capture */
+ if (ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, captype, subtype))
+ {
+ IVTV_DEBUG_WARN( "Error starting capture!\n");
+ return -EINVAL;
+ }
+
+ /* Start Passthrough */
+ if (enable_passthrough) {
+ ivtv_passthrough_mode(itv, 1);
+ }
+
+ if (s->type == IVTV_ENC_STREAM_TYPE_VBI)
+ ivtv_clear_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP);
+ else
+ ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
+
+ /* you're live! sit back and await interrupts :) */
+ atomic_inc(&itv->capturing);
+ return 0;
+}
+
+static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ struct ivtv *itv = s->itv;
+ int datatype;
+ u16 width;
+ u16 height;
+
+ if (s->vdev == NULL)
+ return -EINVAL;
+
+ IVTV_DEBUG_INFO("Setting some initial decoder settings\n");
+
+ width = itv->cxhdl.width;
+ height = itv->cxhdl.height;
+
+ /* set audio mode to left/stereo for dual/stereo mode. */
+ ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode);
+
+ /* set number of internal decoder buffers */
+ ivtv_vapi(itv, CX2341X_DEC_SET_DISPLAY_BUFFERS, 1, 0);
+
+ /* prebuffering */
+ ivtv_vapi(itv, CX2341X_DEC_SET_PREBUFFERING, 1, 1);
+
+ /* extract from user packets */
+ ivtv_vapi_result(itv, data, CX2341X_DEC_EXTRACT_VBI, 1, 1);
+ itv->vbi.dec_start = data[0];
+
+ IVTV_DEBUG_INFO("Decoder VBI RE-Insert start 0x%08x size 0x%08x\n",
+ itv->vbi.dec_start, data[1]);
+
+ /* set decoder source settings */
+ /* Data type: 0 = mpeg from host,
+ 1 = yuv from encoder,
+ 2 = yuv_from_host */
+ switch (s->type) {
+ case IVTV_DEC_STREAM_TYPE_YUV:
+ if (itv->output_mode == OUT_PASSTHROUGH) {
+ datatype = 1;
+ } else {
+ /* Fake size to avoid switching video standard */
+ datatype = 2;
+ width = 720;
+ height = itv->is_out_50hz ? 576 : 480;
+ }
+ IVTV_DEBUG_INFO("Setup DEC YUV Stream data[0] = %d\n", datatype);
+ break;
+ case IVTV_DEC_STREAM_TYPE_MPG:
+ default:
+ datatype = 0;
+ break;
+ }
+ if (ivtv_vapi(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, datatype,
+ width, height, itv->cxhdl.audio_properties)) {
+ IVTV_DEBUG_WARN("Couldn't initialize decoder source\n");
+ }
+
+ /* Decoder sometimes dies here, so wait a moment */
+ ivtv_msleep_timeout(10, 0);
+
+ /* Known failure point for firmware, so check */
+ return ivtv_firmware_check(itv, "ivtv_setup_v4l2_decode_stream");
+}
+
+int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset)
+{
+ struct ivtv *itv = s->itv;
+ int rc;
+
+ if (s->vdev == NULL)
+ return -EINVAL;
+
+ if (test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags))
+ return 0; /* already started */
+
+ IVTV_DEBUG_INFO("Starting decode stream %s (gop_offset %d)\n", s->name, gop_offset);
+
+ rc = ivtv_setup_v4l2_decode_stream(s);
+ if (rc < 0) {
+ clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+ return rc;
+ }
+
+ /* set dma size to 65536 bytes */
+ ivtv_vapi(itv, CX2341X_DEC_SET_DMA_BLOCK_SIZE, 1, 65536);
+
+ /* Clear Streamoff */
+ clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+
+ /* Zero out decoder counters */
+ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[0]);
+ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[1]);
+ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[2]);
+ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[3]);
+ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[0]);
+ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[1]);
+ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[2]);
+ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[3]);
+
+ /* turn on notification of dual/stereo mode change */
+ ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 1, IVTV_IRQ_DEC_AUD_MODE_CHG, -1);
+
+ /* start playback */
+ ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, gop_offset, 0);
+
+ /* Let things settle before we actually start */
+ ivtv_msleep_timeout(10, 0);
+
+ /* Clear the following Interrupt mask bits for decoding */
+ ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_DECODE);
+ IVTV_DEBUG_IRQ("IRQ Mask is now: 0x%08x\n", itv->irqmask);
+
+ /* you're live! sit back and await interrupts :) */
+ atomic_inc(&itv->decoding);
+ return 0;
+}
+
+void ivtv_stop_all_captures(struct ivtv *itv)
+{
+ int i;
+
+ for (i = IVTV_MAX_STREAMS - 1; i >= 0; i--) {
+ struct ivtv_stream *s = &itv->streams[i];
+
+ if (s->vdev == NULL)
+ continue;
+ if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+ ivtv_stop_v4l2_encode_stream(s, 0);
+ }
+ }
+}
+
+int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end)
+{
+ struct ivtv *itv = s->itv;
+ DECLARE_WAITQUEUE(wait, current);
+ int cap_type;
+ int stopmode;
+
+ if (s->vdev == NULL)
+ return -EINVAL;
+
+ /* This function assumes that you are allowed to stop the capture
+ and that we are actually capturing */
+
+ IVTV_DEBUG_INFO("Stop Capture\n");
+
+ if (s->type == IVTV_DEC_STREAM_TYPE_VOUT)
+ return 0;
+ if (atomic_read(&itv->capturing) == 0)
+ return 0;
+
+ switch (s->type) {
+ case IVTV_ENC_STREAM_TYPE_YUV:
+ cap_type = 1;
+ break;
+ case IVTV_ENC_STREAM_TYPE_PCM:
+ cap_type = 1;
+ break;
+ case IVTV_ENC_STREAM_TYPE_VBI:
+ cap_type = 1;
+ break;
+ case IVTV_ENC_STREAM_TYPE_MPG:
+ default:
+ cap_type = 0;
+ break;
+ }
+
+ /* Stop Capture Mode */
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) {
+ stopmode = 0;
+ } else {
+ stopmode = 1;
+ }
+
+ /* end_capture */
+ /* when: 0 = end of GOP 1 = NOW!, type: 0 = mpeg, subtype: 3 = video+audio */
+ ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, stopmode, cap_type, s->subtype);
+
+ if (!test_bit(IVTV_F_S_PASSTHROUGH, &s->s_flags)) {
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) {
+ /* only run these if we're shutting down the last cap */
+ unsigned long duration;
+ unsigned long then = jiffies;
+
+ add_wait_queue(&itv->eos_waitq, &wait);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ /* wait 2s for EOS interrupt */
+ while (!test_bit(IVTV_F_I_EOS, &itv->i_flags) &&
+ time_before(jiffies,
+ then + msecs_to_jiffies(2000))) {
+ schedule_timeout(msecs_to_jiffies(10));
+ }
+
+ /* To convert jiffies to ms, we must multiply by 1000
+ * and divide by HZ. To avoid runtime division, we
+ * convert this to multiplication by 1000/HZ.
+ * Since integer division truncates, we get the best
+ * accuracy if we do a rounding calculation of the constant.
+ * Think of the case where HZ is 1024.
+ */
+ duration = ((1000 + HZ / 2) / HZ) * (jiffies - then);
+
+ if (!test_bit(IVTV_F_I_EOS, &itv->i_flags)) {
+ IVTV_DEBUG_WARN("%s: EOS interrupt not received! stopping anyway.\n", s->name);
+ IVTV_DEBUG_WARN("%s: waited %lu ms.\n", s->name, duration);
+ } else {
+ IVTV_DEBUG_INFO("%s: EOS took %lu ms to occur.\n", s->name, duration);
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&itv->eos_waitq, &wait);
+ set_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+ }
+
+ /* Handle any pending interrupts */
+ ivtv_msleep_timeout(100, 0);
+ }
+
+ atomic_dec(&itv->capturing);
+
+ /* Clear capture and no-read bits */
+ clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+
+ if (s->type == IVTV_ENC_STREAM_TYPE_VBI)
+ ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP);
+
+ if (atomic_read(&itv->capturing) > 0) {
+ return 0;
+ }
+
+ cx2341x_handler_set_busy(&itv->cxhdl, 0);
+
+ /* Set the following Interrupt mask bits for capture */
+ ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
+ del_timer(&itv->dma_timer);
+
+ /* event notification (off) */
+ if (test_and_clear_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) {
+ /* type: 0 = refresh */
+ /* on/off: 0 = off, intr: 0x10000000, mbox_id: -1: none */
+ ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_ENC_VIM_RST, -1);
+ ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST);
+ }
+
+ /* Raw-passthrough is implied on start. Make sure it's stopped so
+ the encoder will re-initialize when next started */
+ ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, 1, 2, 7);
+
+ wake_up(&s->waitq);
+
+ return 0;
+}
+
+int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts)
+{
+ static const struct v4l2_event ev = {
+ .type = V4L2_EVENT_EOS,
+ };
+ struct ivtv *itv = s->itv;
+
+ if (s->vdev == NULL)
+ return -EINVAL;
+
+ if (s->type != IVTV_DEC_STREAM_TYPE_YUV && s->type != IVTV_DEC_STREAM_TYPE_MPG)
+ return -EINVAL;
+
+ if (!test_bit(IVTV_F_S_STREAMING, &s->s_flags))
+ return 0;
+
+ IVTV_DEBUG_INFO("Stop Decode at %llu, flags: %x\n", (unsigned long long)pts, flags);
+
+ /* Stop Decoder */
+ if (!(flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) || pts) {
+ u32 tmp = 0;
+
+ /* Wait until the decoder is no longer running */
+ if (pts) {
+ ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3,
+ 0, (u32)(pts & 0xffffffff), (u32)(pts >> 32));
+ }
+ while (1) {
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ ivtv_vapi_result(itv, data, CX2341X_DEC_GET_XFER_INFO, 0);
+ if (s->q_full.buffers + s->q_dma.buffers == 0) {
+ if (tmp == data[3])
+ break;
+ tmp = data[3];
+ }
+ if (ivtv_msleep_timeout(100, 1))
+ break;
+ }
+ }
+ ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, flags & V4L2_DEC_CMD_STOP_TO_BLACK, 0, 0);
+
+ /* turn off notification of dual/stereo mode change */
+ ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_DEC_AUD_MODE_CHG, -1);
+
+ ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_DECODE);
+ del_timer(&itv->dma_timer);
+
+ clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
+ clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+ ivtv_flush_queues(s);
+
+ /* decoder needs time to settle */
+ ivtv_msleep_timeout(40, 0);
+
+ /* decrement decoding */
+ atomic_dec(&itv->decoding);
+
+ set_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags);
+ wake_up(&itv->event_waitq);
+ v4l2_event_queue(s->vdev, &ev);
+
+ /* wake up wait queues */
+ wake_up(&s->waitq);
+
+ return 0;
+}
+
+int ivtv_passthrough_mode(struct ivtv *itv, int enable)
+{
+ struct ivtv_stream *yuv_stream = &itv->streams[IVTV_ENC_STREAM_TYPE_YUV];
+ struct ivtv_stream *dec_stream = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
+
+ if (yuv_stream->vdev == NULL || dec_stream->vdev == NULL)
+ return -EINVAL;
+
+ IVTV_DEBUG_INFO("ivtv ioctl: Select passthrough mode\n");
+
+ /* Prevent others from starting/stopping streams while we
+ initiate/terminate passthrough mode */
+ if (enable) {
+ if (itv->output_mode == OUT_PASSTHROUGH) {
+ return 0;
+ }
+ if (ivtv_set_output_mode(itv, OUT_PASSTHROUGH) != OUT_PASSTHROUGH)
+ return -EBUSY;
+
+ /* Fully initialize stream, and then unflag init */
+ set_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags);
+ set_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags);
+
+ /* Setup YUV Decoder */
+ ivtv_setup_v4l2_decode_stream(dec_stream);
+
+ /* Start Decoder */
+ ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 1);
+ atomic_inc(&itv->decoding);
+
+ /* Setup capture if not already done */
+ if (atomic_read(&itv->capturing) == 0) {
+ cx2341x_handler_setup(&itv->cxhdl);
+ cx2341x_handler_set_busy(&itv->cxhdl, 1);
+ }
+
+ /* Start Passthrough Mode */
+ ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, 2, 11);
+ atomic_inc(&itv->capturing);
+ return 0;
+ }
+
+ if (itv->output_mode != OUT_PASSTHROUGH)
+ return 0;
+
+ /* Stop Passthrough Mode */
+ ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, 1, 2, 11);
+ ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, 1, 0, 0);
+
+ atomic_dec(&itv->capturing);
+ atomic_dec(&itv->decoding);
+ clear_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags);
+ clear_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags);
+ itv->output_mode = OUT_NONE;
+ if (atomic_read(&itv->capturing) == 0)
+ cx2341x_handler_set_busy(&itv->cxhdl, 0);
+
+ return 0;
+}
diff --git a/drivers/media/video/ivtv/ivtv-streams.h b/drivers/media/video/ivtv/ivtv-streams.h
new file mode 100644
index 00000000..a653a513
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-streams.h
@@ -0,0 +1,37 @@
+/*
+ init/start/stop/exit stream functions
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef IVTV_STREAMS_H
+#define IVTV_STREAMS_H
+
+int ivtv_streams_setup(struct ivtv *itv);
+int ivtv_streams_register(struct ivtv *itv);
+void ivtv_streams_cleanup(struct ivtv *itv, int unregister);
+
+/* Capture related */
+int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s);
+int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end);
+int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset);
+int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts);
+
+void ivtv_stop_all_captures(struct ivtv *itv);
+int ivtv_passthrough_mode(struct ivtv *itv, int enable);
+
+#endif
diff --git a/drivers/media/video/ivtv/ivtv-udma.c b/drivers/media/video/ivtv/ivtv-udma.c
new file mode 100644
index 00000000..7338cb2d
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-udma.c
@@ -0,0 +1,234 @@
+/*
+ User DMA
+
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-udma.h"
+
+void ivtv_udma_get_page_info(struct ivtv_dma_page_info *dma_page, unsigned long first, unsigned long size)
+{
+ dma_page->uaddr = first & PAGE_MASK;
+ dma_page->offset = first & ~PAGE_MASK;
+ dma_page->tail = 1 + ((first+size-1) & ~PAGE_MASK);
+ dma_page->first = (first & PAGE_MASK) >> PAGE_SHIFT;
+ dma_page->last = ((first+size-1) & PAGE_MASK) >> PAGE_SHIFT;
+ dma_page->page_count = dma_page->last - dma_page->first + 1;
+ if (dma_page->page_count == 1) dma_page->tail -= dma_page->offset;
+}
+
+int ivtv_udma_fill_sg_list (struct ivtv_user_dma *dma, struct ivtv_dma_page_info *dma_page, int map_offset)
+{
+ int i, offset;
+ unsigned long flags;
+
+ if (map_offset < 0)
+ return map_offset;
+
+ offset = dma_page->offset;
+
+ /* Fill SG Array with new values */
+ for (i = 0; i < dma_page->page_count; i++) {
+ unsigned int len = (i == dma_page->page_count - 1) ?
+ dma_page->tail : PAGE_SIZE - offset;
+
+ if (PageHighMem(dma->map[map_offset])) {
+ void *src;
+
+ if (dma->bouncemap[map_offset] == NULL)
+ dma->bouncemap[map_offset] = alloc_page(GFP_KERNEL);
+ if (dma->bouncemap[map_offset] == NULL)
+ return -1;
+ local_irq_save(flags);
+ src = kmap_atomic(dma->map[map_offset]) + offset;
+ memcpy(page_address(dma->bouncemap[map_offset]) + offset, src, len);
+ kunmap_atomic(src);
+ local_irq_restore(flags);
+ sg_set_page(&dma->SGlist[map_offset], dma->bouncemap[map_offset], len, offset);
+ }
+ else {
+ sg_set_page(&dma->SGlist[map_offset], dma->map[map_offset], len, offset);
+ }
+ offset = 0;
+ map_offset++;
+ }
+ return map_offset;
+}
+
+void ivtv_udma_fill_sg_array (struct ivtv_user_dma *dma, u32 buffer_offset, u32 buffer_offset_2, u32 split) {
+ int i;
+ struct scatterlist *sg;
+
+ for (i = 0, sg = dma->SGlist; i < dma->SG_length; i++, sg++) {
+ dma->SGarray[i].size = cpu_to_le32(sg_dma_len(sg));
+ dma->SGarray[i].src = cpu_to_le32(sg_dma_address(sg));
+ dma->SGarray[i].dst = cpu_to_le32(buffer_offset);
+ buffer_offset += sg_dma_len(sg);
+
+ split -= sg_dma_len(sg);
+ if (split == 0)
+ buffer_offset = buffer_offset_2;
+ }
+}
+
+/* User DMA Buffers */
+void ivtv_udma_alloc(struct ivtv *itv)
+{
+ if (itv->udma.SG_handle == 0) {
+ /* Map DMA Page Array Buffer */
+ itv->udma.SG_handle = pci_map_single(itv->pdev, itv->udma.SGarray,
+ sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
+ ivtv_udma_sync_for_cpu(itv);
+ }
+}
+
+int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr,
+ void __user *userbuf, int size_in_bytes)
+{
+ struct ivtv_dma_page_info user_dma;
+ struct ivtv_user_dma *dma = &itv->udma;
+ int i, err;
+
+ IVTV_DEBUG_DMA("ivtv_udma_setup, dst: 0x%08x\n", (unsigned int)ivtv_dest_addr);
+
+ /* Still in USE */
+ if (dma->SG_length || dma->page_count) {
+ IVTV_DEBUG_WARN("ivtv_udma_setup: SG_length %d page_count %d still full?\n",
+ dma->SG_length, dma->page_count);
+ return -EBUSY;
+ }
+
+ ivtv_udma_get_page_info(&user_dma, (unsigned long)userbuf, size_in_bytes);
+
+ if (user_dma.page_count <= 0) {
+ IVTV_DEBUG_WARN("ivtv_udma_setup: Error %d page_count from %d bytes %d offset\n",
+ user_dma.page_count, size_in_bytes, user_dma.offset);
+ return -EINVAL;
+ }
+
+ /* Get user pages for DMA Xfer */
+ down_read(&current->mm->mmap_sem);
+ err = get_user_pages(current, current->mm,
+ user_dma.uaddr, user_dma.page_count, 0, 1, dma->map, NULL);
+ up_read(&current->mm->mmap_sem);
+
+ if (user_dma.page_count != err) {
+ IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n",
+ err, user_dma.page_count);
+ if (err >= 0) {
+ for (i = 0; i < err; i++)
+ put_page(dma->map[i]);
+ return -EINVAL;
+ }
+ return err;
+ }
+
+ dma->page_count = user_dma.page_count;
+
+ /* Fill SG List with new values */
+ if (ivtv_udma_fill_sg_list(dma, &user_dma, 0) < 0) {
+ for (i = 0; i < dma->page_count; i++) {
+ put_page(dma->map[i]);
+ }
+ dma->page_count = 0;
+ return -ENOMEM;
+ }
+
+ /* Map SG List */
+ dma->SG_length = pci_map_sg(itv->pdev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
+
+ /* Fill SG Array with new values */
+ ivtv_udma_fill_sg_array (dma, ivtv_dest_addr, 0, -1);
+
+ /* Tag SG Array with Interrupt Bit */
+ dma->SGarray[dma->SG_length - 1].size |= cpu_to_le32(0x80000000);
+
+ ivtv_udma_sync_for_device(itv);
+ return dma->page_count;
+}
+
+void ivtv_udma_unmap(struct ivtv *itv)
+{
+ struct ivtv_user_dma *dma = &itv->udma;
+ int i;
+
+ IVTV_DEBUG_INFO("ivtv_unmap_user_dma\n");
+
+ /* Nothing to free */
+ if (dma->page_count == 0)
+ return;
+
+ /* Unmap Scatterlist */
+ if (dma->SG_length) {
+ pci_unmap_sg(itv->pdev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
+ dma->SG_length = 0;
+ }
+ /* sync DMA */
+ ivtv_udma_sync_for_cpu(itv);
+
+ /* Release User Pages */
+ for (i = 0; i < dma->page_count; i++) {
+ put_page(dma->map[i]);
+ }
+ dma->page_count = 0;
+}
+
+void ivtv_udma_free(struct ivtv *itv)
+{
+ int i;
+
+ /* Unmap SG Array */
+ if (itv->udma.SG_handle) {
+ pci_unmap_single(itv->pdev, itv->udma.SG_handle,
+ sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
+ }
+
+ /* Unmap Scatterlist */
+ if (itv->udma.SG_length) {
+ pci_unmap_sg(itv->pdev, itv->udma.SGlist, itv->udma.page_count, PCI_DMA_TODEVICE);
+ }
+
+ for (i = 0; i < IVTV_DMA_SG_OSD_ENT; i++) {
+ if (itv->udma.bouncemap[i])
+ __free_page(itv->udma.bouncemap[i]);
+ }
+}
+
+void ivtv_udma_start(struct ivtv *itv)
+{
+ IVTV_DEBUG_DMA("start UDMA\n");
+ write_reg(itv->udma.SG_handle, IVTV_REG_DECDMAADDR);
+ write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER);
+ set_bit(IVTV_F_I_DMA, &itv->i_flags);
+ set_bit(IVTV_F_I_UDMA, &itv->i_flags);
+ clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags);
+}
+
+void ivtv_udma_prepare(struct ivtv *itv)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&itv->dma_reg_lock, flags);
+ if (!test_bit(IVTV_F_I_DMA, &itv->i_flags))
+ ivtv_udma_start(itv);
+ else
+ set_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags);
+ spin_unlock_irqrestore(&itv->dma_reg_lock, flags);
+}
diff --git a/drivers/media/video/ivtv/ivtv-udma.h b/drivers/media/video/ivtv/ivtv-udma.h
new file mode 100644
index 00000000..ee3c9efb
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-udma.h
@@ -0,0 +1,48 @@
+/*
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2006-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef IVTV_UDMA_H
+#define IVTV_UDMA_H
+
+/* User DMA functions */
+void ivtv_udma_get_page_info(struct ivtv_dma_page_info *dma_page, unsigned long first, unsigned long size);
+int ivtv_udma_fill_sg_list(struct ivtv_user_dma *dma, struct ivtv_dma_page_info *dma_page, int map_offset);
+void ivtv_udma_fill_sg_array(struct ivtv_user_dma *dma, u32 buffer_offset, u32 buffer_offset_2, u32 split);
+int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr,
+ void __user *userbuf, int size_in_bytes);
+void ivtv_udma_unmap(struct ivtv *itv);
+void ivtv_udma_free(struct ivtv *itv);
+void ivtv_udma_alloc(struct ivtv *itv);
+void ivtv_udma_prepare(struct ivtv *itv);
+void ivtv_udma_start(struct ivtv *itv);
+
+static inline void ivtv_udma_sync_for_device(struct ivtv *itv)
+{
+ pci_dma_sync_single_for_device(itv->pdev, itv->udma.SG_handle,
+ sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
+}
+
+static inline void ivtv_udma_sync_for_cpu(struct ivtv *itv)
+{
+ pci_dma_sync_single_for_cpu(itv->pdev, itv->udma.SG_handle,
+ sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
+}
+
+#endif
diff --git a/drivers/media/video/ivtv/ivtv-vbi.c b/drivers/media/video/ivtv/ivtv-vbi.c
new file mode 100644
index 00000000..293db806
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-vbi.c
@@ -0,0 +1,549 @@
+/*
+ Vertical Blank Interval support functions
+ Copyright (C) 2004-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-i2c.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-queue.h"
+#include "ivtv-cards.h"
+#include "ivtv-vbi.h"
+
+static void ivtv_set_vps(struct ivtv *itv, int enabled)
+{
+ struct v4l2_sliced_vbi_data data;
+
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return;
+ data.id = V4L2_SLICED_VPS;
+ data.field = 0;
+ data.line = enabled ? 16 : 0;
+ data.data[2] = itv->vbi.vps_payload.data[0];
+ data.data[8] = itv->vbi.vps_payload.data[1];
+ data.data[9] = itv->vbi.vps_payload.data[2];
+ data.data[10] = itv->vbi.vps_payload.data[3];
+ data.data[11] = itv->vbi.vps_payload.data[4];
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, vbi, s_vbi_data, &data);
+}
+
+static void ivtv_set_cc(struct ivtv *itv, int mode, const struct vbi_cc *cc)
+{
+ struct v4l2_sliced_vbi_data data;
+
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return;
+ data.id = V4L2_SLICED_CAPTION_525;
+ data.field = 0;
+ data.line = (mode & 1) ? 21 : 0;
+ data.data[0] = cc->odd[0];
+ data.data[1] = cc->odd[1];
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, vbi, s_vbi_data, &data);
+ data.field = 1;
+ data.line = (mode & 2) ? 21 : 0;
+ data.data[0] = cc->even[0];
+ data.data[1] = cc->even[1];
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, vbi, s_vbi_data, &data);
+}
+
+static void ivtv_set_wss(struct ivtv *itv, int enabled, int mode)
+{
+ struct v4l2_sliced_vbi_data data;
+
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return;
+ /* When using a 50 Hz system, always turn on the
+ wide screen signal with 4x3 ratio as the default.
+ Turning this signal on and off can confuse certain
+ TVs. As far as I can tell there is no reason not to
+ transmit this signal. */
+ if ((itv->std_out & V4L2_STD_625_50) && !enabled) {
+ enabled = 1;
+ mode = 0x08; /* 4x3 full format */
+ }
+ data.id = V4L2_SLICED_WSS_625;
+ data.field = 0;
+ data.line = enabled ? 23 : 0;
+ data.data[0] = mode & 0xff;
+ data.data[1] = (mode >> 8) & 0xff;
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, vbi, s_vbi_data, &data);
+}
+
+static int odd_parity(u8 c)
+{
+ c ^= (c >> 4);
+ c ^= (c >> 2);
+ c ^= (c >> 1);
+
+ return c & 1;
+}
+
+static void ivtv_write_vbi_line(struct ivtv *itv,
+ const struct v4l2_sliced_vbi_data *d,
+ struct vbi_cc *cc, int *found_cc)
+{
+ struct vbi_info *vi = &itv->vbi;
+
+ if (d->id == V4L2_SLICED_CAPTION_525 && d->line == 21) {
+ if (d->field) {
+ cc->even[0] = d->data[0];
+ cc->even[1] = d->data[1];
+ } else {
+ cc->odd[0] = d->data[0];
+ cc->odd[1] = d->data[1];
+ }
+ *found_cc = 1;
+ } else if (d->id == V4L2_SLICED_VPS && d->line == 16 && d->field == 0) {
+ struct vbi_vps vps;
+
+ vps.data[0] = d->data[2];
+ vps.data[1] = d->data[8];
+ vps.data[2] = d->data[9];
+ vps.data[3] = d->data[10];
+ vps.data[4] = d->data[11];
+ if (memcmp(&vps, &vi->vps_payload, sizeof(vps))) {
+ vi->vps_payload = vps;
+ set_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags);
+ }
+ } else if (d->id == V4L2_SLICED_WSS_625 &&
+ d->line == 23 && d->field == 0) {
+ int wss = d->data[0] | d->data[1] << 8;
+
+ if (vi->wss_payload != wss) {
+ vi->wss_payload = wss;
+ set_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags);
+ }
+ }
+}
+
+static void ivtv_write_vbi_cc_lines(struct ivtv *itv, const struct vbi_cc *cc)
+{
+ struct vbi_info *vi = &itv->vbi;
+
+ if (vi->cc_payload_idx < ARRAY_SIZE(vi->cc_payload)) {
+ memcpy(&vi->cc_payload[vi->cc_payload_idx], cc,
+ sizeof(struct vbi_cc));
+ vi->cc_payload_idx++;
+ set_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags);
+ }
+}
+
+static void ivtv_write_vbi(struct ivtv *itv,
+ const struct v4l2_sliced_vbi_data *sliced,
+ size_t cnt)
+{
+ struct vbi_cc cc = { .odd = { 0x80, 0x80 }, .even = { 0x80, 0x80 } };
+ int found_cc = 0;
+ size_t i;
+
+ for (i = 0; i < cnt; i++)
+ ivtv_write_vbi_line(itv, sliced + i, &cc, &found_cc);
+
+ if (found_cc)
+ ivtv_write_vbi_cc_lines(itv, &cc);
+}
+
+ssize_t
+ivtv_write_vbi_from_user(struct ivtv *itv,
+ const struct v4l2_sliced_vbi_data __user *sliced,
+ size_t cnt)
+{
+ struct vbi_cc cc = { .odd = { 0x80, 0x80 }, .even = { 0x80, 0x80 } };
+ int found_cc = 0;
+ size_t i;
+ struct v4l2_sliced_vbi_data d;
+ ssize_t ret = cnt * sizeof(struct v4l2_sliced_vbi_data);
+
+ for (i = 0; i < cnt; i++) {
+ if (copy_from_user(&d, sliced + i,
+ sizeof(struct v4l2_sliced_vbi_data))) {
+ ret = -EFAULT;
+ break;
+ }
+ ivtv_write_vbi_line(itv, &d, &cc, &found_cc);
+ }
+
+ if (found_cc)
+ ivtv_write_vbi_cc_lines(itv, &cc);
+
+ return ret;
+}
+
+static void copy_vbi_data(struct ivtv *itv, int lines, u32 pts_stamp)
+{
+ int line = 0;
+ int i;
+ u32 linemask[2] = { 0, 0 };
+ unsigned short size;
+ static const u8 mpeg_hdr_data[] = {
+ 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x0c, 0x66,
+ 0x24, 0x01, 0x01, 0xd1, 0xd3, 0xfa, 0xff, 0xff,
+ 0x00, 0x00, 0x01, 0xbd, 0x00, 0x1a, 0x84, 0x80,
+ 0x07, 0x21, 0x00, 0x5d, 0x63, 0xa7, 0xff, 0xff
+ };
+ const int sd = sizeof(mpeg_hdr_data); /* start of vbi data */
+ int idx = itv->vbi.frame % IVTV_VBI_FRAMES;
+ u8 *dst = &itv->vbi.sliced_mpeg_data[idx][0];
+
+ for (i = 0; i < lines; i++) {
+ int f, l;
+
+ if (itv->vbi.sliced_data[i].id == 0)
+ continue;
+
+ l = itv->vbi.sliced_data[i].line - 6;
+ f = itv->vbi.sliced_data[i].field;
+ if (f)
+ l += 18;
+ if (l < 32)
+ linemask[0] |= (1 << l);
+ else
+ linemask[1] |= (1 << (l - 32));
+ dst[sd + 12 + line * 43] =
+ ivtv_service2vbi(itv->vbi.sliced_data[i].id);
+ memcpy(dst + sd + 12 + line * 43 + 1, itv->vbi.sliced_data[i].data, 42);
+ line++;
+ }
+ memcpy(dst, mpeg_hdr_data, sizeof(mpeg_hdr_data));
+ if (line == 36) {
+ /* All lines are used, so there is no space for the linemask
+ (the max size of the VBI data is 36 * 43 + 4 bytes).
+ So in this case we use the magic number 'ITV0'. */
+ memcpy(dst + sd, "ITV0", 4);
+ memcpy(dst + sd + 4, dst + sd + 12, line * 43);
+ size = 4 + ((43 * line + 3) & ~3);
+ } else {
+ memcpy(dst + sd, "itv0", 4);
+ cpu_to_le32s(&linemask[0]);
+ cpu_to_le32s(&linemask[1]);
+ memcpy(dst + sd + 4, &linemask[0], 8);
+ size = 12 + ((43 * line + 3) & ~3);
+ }
+ dst[4+16] = (size + 10) >> 8;
+ dst[5+16] = (size + 10) & 0xff;
+ dst[9+16] = 0x21 | ((pts_stamp >> 29) & 0x6);
+ dst[10+16] = (pts_stamp >> 22) & 0xff;
+ dst[11+16] = 1 | ((pts_stamp >> 14) & 0xff);
+ dst[12+16] = (pts_stamp >> 7) & 0xff;
+ dst[13+16] = 1 | ((pts_stamp & 0x7f) << 1);
+ itv->vbi.sliced_mpeg_size[idx] = sd + size;
+}
+
+static int ivtv_convert_ivtv_vbi(struct ivtv *itv, u8 *p)
+{
+ u32 linemask[2];
+ int i, l, id2;
+ int line = 0;
+
+ if (!memcmp(p, "itv0", 4)) {
+ memcpy(linemask, p + 4, 8);
+ p += 12;
+ } else if (!memcmp(p, "ITV0", 4)) {
+ linemask[0] = 0xffffffff;
+ linemask[1] = 0xf;
+ p += 4;
+ } else {
+ /* unknown VBI data, convert to empty VBI frame */
+ linemask[0] = linemask[1] = 0;
+ }
+ for (i = 0; i < 36; i++) {
+ int err = 0;
+
+ if (i < 32 && !(linemask[0] & (1 << i)))
+ continue;
+ if (i >= 32 && !(linemask[1] & (1 << (i - 32))))
+ continue;
+ id2 = *p & 0xf;
+ switch (id2) {
+ case IVTV_SLICED_TYPE_TELETEXT_B:
+ id2 = V4L2_SLICED_TELETEXT_B;
+ break;
+ case IVTV_SLICED_TYPE_CAPTION_525:
+ id2 = V4L2_SLICED_CAPTION_525;
+ err = !odd_parity(p[1]) || !odd_parity(p[2]);
+ break;
+ case IVTV_SLICED_TYPE_VPS:
+ id2 = V4L2_SLICED_VPS;
+ break;
+ case IVTV_SLICED_TYPE_WSS_625:
+ id2 = V4L2_SLICED_WSS_625;
+ break;
+ default:
+ id2 = 0;
+ break;
+ }
+ if (err == 0) {
+ l = (i < 18) ? i + 6 : i - 18 + 6;
+ itv->vbi.sliced_dec_data[line].line = l;
+ itv->vbi.sliced_dec_data[line].field = i >= 18;
+ itv->vbi.sliced_dec_data[line].id = id2;
+ memcpy(itv->vbi.sliced_dec_data[line].data, p + 1, 42);
+ line++;
+ }
+ p += 43;
+ }
+ while (line < 36) {
+ itv->vbi.sliced_dec_data[line].id = 0;
+ itv->vbi.sliced_dec_data[line].line = 0;
+ itv->vbi.sliced_dec_data[line].field = 0;
+ line++;
+ }
+ return line * sizeof(itv->vbi.sliced_dec_data[0]);
+}
+
+/* Compress raw VBI format, removes leading SAV codes and surplus space after the
+ field.
+ Returns new compressed size. */
+static u32 compress_raw_buf(struct ivtv *itv, u8 *buf, u32 size)
+{
+ u32 line_size = itv->vbi.raw_decoder_line_size;
+ u32 lines = itv->vbi.count;
+ u8 sav1 = itv->vbi.raw_decoder_sav_odd_field;
+ u8 sav2 = itv->vbi.raw_decoder_sav_even_field;
+ u8 *q = buf;
+ u8 *p;
+ int i;
+
+ for (i = 0; i < lines; i++) {
+ p = buf + i * line_size;
+
+ /* Look for SAV code */
+ if (p[0] != 0xff || p[1] || p[2] || (p[3] != sav1 && p[3] != sav2)) {
+ break;
+ }
+ memcpy(q, p + 4, line_size - 4);
+ q += line_size - 4;
+ }
+ return lines * (line_size - 4);
+}
+
+
+/* Compressed VBI format, all found sliced blocks put next to one another
+ Returns new compressed size */
+static u32 compress_sliced_buf(struct ivtv *itv, u32 line, u8 *buf, u32 size, u8 sav)
+{
+ u32 line_size = itv->vbi.sliced_decoder_line_size;
+ struct v4l2_decode_vbi_line vbi;
+ int i;
+ unsigned lines = 0;
+
+ /* find the first valid line */
+ for (i = 0; i < size; i++, buf++) {
+ if (buf[0] == 0xff && !buf[1] && !buf[2] && buf[3] == sav)
+ break;
+ }
+
+ size -= i;
+ if (size < line_size) {
+ return line;
+ }
+ for (i = 0; i < size / line_size; i++) {
+ u8 *p = buf + i * line_size;
+
+ /* Look for SAV code */
+ if (p[0] != 0xff || p[1] || p[2] || p[3] != sav) {
+ continue;
+ }
+ vbi.p = p + 4;
+ v4l2_subdev_call(itv->sd_video, vbi, decode_vbi_line, &vbi);
+ if (vbi.type && !(lines & (1 << vbi.line))) {
+ lines |= 1 << vbi.line;
+ itv->vbi.sliced_data[line].id = vbi.type;
+ itv->vbi.sliced_data[line].field = vbi.is_second_field;
+ itv->vbi.sliced_data[line].line = vbi.line;
+ memcpy(itv->vbi.sliced_data[line].data, vbi.p, 42);
+ line++;
+ }
+ }
+ return line;
+}
+
+void ivtv_process_vbi_data(struct ivtv *itv, struct ivtv_buffer *buf,
+ u64 pts_stamp, int streamtype)
+{
+ u8 *p = (u8 *) buf->buf;
+ u32 size = buf->bytesused;
+ int y;
+
+ /* Raw VBI data */
+ if (streamtype == IVTV_ENC_STREAM_TYPE_VBI && ivtv_raw_vbi(itv)) {
+ u8 type;
+
+ ivtv_buf_swap(buf);
+
+ type = p[3];
+
+ size = buf->bytesused = compress_raw_buf(itv, p, size);
+
+ /* second field of the frame? */
+ if (type == itv->vbi.raw_decoder_sav_even_field) {
+ /* Dirty hack needed for backwards
+ compatibility of old VBI software. */
+ p += size - 4;
+ memcpy(p, &itv->vbi.frame, 4);
+ itv->vbi.frame++;
+ }
+ return;
+ }
+
+ /* Sliced VBI data with data insertion */
+ if (streamtype == IVTV_ENC_STREAM_TYPE_VBI) {
+ int lines;
+
+ ivtv_buf_swap(buf);
+
+ /* first field */
+ lines = compress_sliced_buf(itv, 0, p, size / 2,
+ itv->vbi.sliced_decoder_sav_odd_field);
+ /* second field */
+ /* experimentation shows that the second half does not always begin
+ at the exact address. So start a bit earlier (hence 32). */
+ lines = compress_sliced_buf(itv, lines, p + size / 2 - 32, size / 2 + 32,
+ itv->vbi.sliced_decoder_sav_even_field);
+ /* always return at least one empty line */
+ if (lines == 0) {
+ itv->vbi.sliced_data[0].id = 0;
+ itv->vbi.sliced_data[0].line = 0;
+ itv->vbi.sliced_data[0].field = 0;
+ lines = 1;
+ }
+ buf->bytesused = size = lines * sizeof(itv->vbi.sliced_data[0]);
+ memcpy(p, &itv->vbi.sliced_data[0], size);
+
+ if (itv->vbi.insert_mpeg) {
+ copy_vbi_data(itv, lines, pts_stamp);
+ }
+ itv->vbi.frame++;
+ return;
+ }
+
+ /* Sliced VBI re-inserted from an MPEG stream */
+ if (streamtype == IVTV_DEC_STREAM_TYPE_VBI) {
+ /* If the size is not 4-byte aligned, then the starting address
+ for the swapping is also shifted. After swapping the data the
+ real start address of the VBI data is exactly 4 bytes after the
+ original start. It's a bit fiddly but it works like a charm.
+ Non-4-byte alignment happens when an lseek is done on the input
+ mpeg file to a non-4-byte aligned position. So on arrival here
+ the VBI data is also non-4-byte aligned. */
+ int offset = size & 3;
+ int cnt;
+
+ if (offset) {
+ p += 4 - offset;
+ }
+ /* Swap Buffer */
+ for (y = 0; y < size; y += 4) {
+ swab32s((u32 *)(p + y));
+ }
+
+ cnt = ivtv_convert_ivtv_vbi(itv, p + offset);
+ memcpy(buf->buf, itv->vbi.sliced_dec_data, cnt);
+ buf->bytesused = cnt;
+
+ ivtv_write_vbi(itv, itv->vbi.sliced_dec_data,
+ cnt / sizeof(itv->vbi.sliced_dec_data[0]));
+ return;
+ }
+}
+
+void ivtv_disable_cc(struct ivtv *itv)
+{
+ struct vbi_cc cc = { .odd = { 0x80, 0x80 }, .even = { 0x80, 0x80 } };
+
+ clear_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags);
+ ivtv_set_cc(itv, 0, &cc);
+ itv->vbi.cc_payload_idx = 0;
+}
+
+
+void ivtv_vbi_work_handler(struct ivtv *itv)
+{
+ struct vbi_info *vi = &itv->vbi;
+ struct v4l2_sliced_vbi_data data;
+ struct vbi_cc cc = { .odd = { 0x80, 0x80 }, .even = { 0x80, 0x80 } };
+
+ /* Lock */
+ if (itv->output_mode == OUT_PASSTHROUGH) {
+ if (itv->is_50hz) {
+ data.id = V4L2_SLICED_WSS_625;
+ data.field = 0;
+
+ if (v4l2_subdev_call(itv->sd_video, vbi, g_vbi_data, &data) == 0) {
+ ivtv_set_wss(itv, 1, data.data[0] & 0xf);
+ vi->wss_missing_cnt = 0;
+ } else if (vi->wss_missing_cnt == 4) {
+ ivtv_set_wss(itv, 1, 0x8); /* 4x3 full format */
+ } else {
+ vi->wss_missing_cnt++;
+ }
+ }
+ else {
+ int mode = 0;
+
+ data.id = V4L2_SLICED_CAPTION_525;
+ data.field = 0;
+ if (v4l2_subdev_call(itv->sd_video, vbi, g_vbi_data, &data) == 0) {
+ mode |= 1;
+ cc.odd[0] = data.data[0];
+ cc.odd[1] = data.data[1];
+ }
+ data.field = 1;
+ if (v4l2_subdev_call(itv->sd_video, vbi, g_vbi_data, &data) == 0) {
+ mode |= 2;
+ cc.even[0] = data.data[0];
+ cc.even[1] = data.data[1];
+ }
+ if (mode) {
+ vi->cc_missing_cnt = 0;
+ ivtv_set_cc(itv, mode, &cc);
+ } else if (vi->cc_missing_cnt == 4) {
+ ivtv_set_cc(itv, 0, &cc);
+ } else {
+ vi->cc_missing_cnt++;
+ }
+ }
+ return;
+ }
+
+ if (test_and_clear_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags)) {
+ ivtv_set_wss(itv, 1, vi->wss_payload & 0xf);
+ }
+
+ if (test_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags)) {
+ if (vi->cc_payload_idx == 0) {
+ clear_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags);
+ ivtv_set_cc(itv, 3, &cc);
+ }
+ while (vi->cc_payload_idx) {
+ cc = vi->cc_payload[0];
+
+ memcpy(vi->cc_payload, vi->cc_payload + 1,
+ sizeof(vi->cc_payload) - sizeof(vi->cc_payload[0]));
+ vi->cc_payload_idx--;
+ if (vi->cc_payload_idx && cc.odd[0] == 0x80 && cc.odd[1] == 0x80)
+ continue;
+
+ ivtv_set_cc(itv, 3, &cc);
+ break;
+ }
+ }
+
+ if (test_and_clear_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags)) {
+ ivtv_set_vps(itv, 1);
+ }
+}
diff --git a/drivers/media/video/ivtv/ivtv-vbi.h b/drivers/media/video/ivtv/ivtv-vbi.h
new file mode 100644
index 00000000..166dd0b7
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-vbi.h
@@ -0,0 +1,34 @@
+/*
+ Vertical Blank Interval support functions
+ Copyright (C) 2004-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef IVTV_VBI_H
+#define IVTV_VBI_H
+
+ssize_t
+ivtv_write_vbi_from_user(struct ivtv *itv,
+ const struct v4l2_sliced_vbi_data __user *sliced,
+ size_t count);
+void ivtv_process_vbi_data(struct ivtv *itv, struct ivtv_buffer *buf,
+ u64 pts_stamp, int streamtype);
+int ivtv_used_line(struct ivtv *itv, int line, int field);
+void ivtv_disable_cc(struct ivtv *itv);
+void ivtv_set_vbi(unsigned long arg);
+void ivtv_vbi_work_handler(struct ivtv *itv);
+
+#endif
diff --git a/drivers/media/video/ivtv/ivtv-version.h b/drivers/media/video/ivtv/ivtv-version.h
new file mode 100644
index 00000000..a20f346f
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-version.h
@@ -0,0 +1,26 @@
+/*
+ ivtv driver version information
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef IVTV_VERSION_H
+#define IVTV_VERSION_H
+
+#define IVTV_DRIVER_NAME "ivtv"
+#define IVTV_VERSION "1.4.3"
+
+#endif
diff --git a/drivers/media/video/ivtv/ivtv-yuv.c b/drivers/media/video/ivtv/ivtv-yuv.c
new file mode 100644
index 00000000..2ad65eb2
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-yuv.c
@@ -0,0 +1,1296 @@
+/*
+ yuv support
+
+ Copyright (C) 2007 Ian Armstrong <ian@iarmst.demon.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-udma.h"
+#include "ivtv-yuv.h"
+
+/* YUV buffer offsets */
+const u32 yuv_offset[IVTV_YUV_BUFFERS] = {
+ 0x001a8600,
+ 0x00240400,
+ 0x002d8200,
+ 0x00370000,
+ 0x00029000,
+ 0x000C0E00,
+ 0x006B0400,
+ 0x00748200
+};
+
+static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma,
+ struct ivtv_dma_frame *args)
+{
+ struct ivtv_dma_page_info y_dma;
+ struct ivtv_dma_page_info uv_dma;
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ u8 frame = yi->draw_frame;
+ struct yuv_frame_info *f = &yi->new_frame_info[frame];
+ int i;
+ int y_pages, uv_pages;
+ unsigned long y_buffer_offset, uv_buffer_offset;
+ int y_decode_height, uv_decode_height, y_size;
+
+ y_buffer_offset = IVTV_DECODER_OFFSET + yuv_offset[frame];
+ uv_buffer_offset = y_buffer_offset + IVTV_YUV_BUFFER_UV_OFFSET;
+
+ y_decode_height = uv_decode_height = f->src_h + f->src_y;
+
+ if (f->offset_y)
+ y_buffer_offset += 720 * 16;
+
+ if (y_decode_height & 15)
+ y_decode_height = (y_decode_height + 16) & ~15;
+
+ if (uv_decode_height & 31)
+ uv_decode_height = (uv_decode_height + 32) & ~31;
+
+ y_size = 720 * y_decode_height;
+
+ /* Still in USE */
+ if (dma->SG_length || dma->page_count) {
+ IVTV_DEBUG_WARN
+ ("prep_user_dma: SG_length %d page_count %d still full?\n",
+ dma->SG_length, dma->page_count);
+ return -EBUSY;
+ }
+
+ ivtv_udma_get_page_info (&y_dma, (unsigned long)args->y_source, 720 * y_decode_height);
+ ivtv_udma_get_page_info (&uv_dma, (unsigned long)args->uv_source, 360 * uv_decode_height);
+
+ /* Get user pages for DMA Xfer */
+ down_read(&current->mm->mmap_sem);
+ y_pages = get_user_pages(current, current->mm, y_dma.uaddr, y_dma.page_count, 0, 1, &dma->map[0], NULL);
+ uv_pages = 0; /* silence gcc. value is set and consumed only if: */
+ if (y_pages == y_dma.page_count) {
+ uv_pages = get_user_pages(current, current->mm,
+ uv_dma.uaddr, uv_dma.page_count, 0, 1,
+ &dma->map[y_pages], NULL);
+ }
+ up_read(&current->mm->mmap_sem);
+
+ if (y_pages != y_dma.page_count || uv_pages != uv_dma.page_count) {
+ int rc = -EFAULT;
+
+ if (y_pages == y_dma.page_count) {
+ IVTV_DEBUG_WARN
+ ("failed to map uv user pages, returned %d "
+ "expecting %d\n", uv_pages, uv_dma.page_count);
+
+ if (uv_pages >= 0) {
+ for (i = 0; i < uv_pages; i++)
+ put_page(dma->map[y_pages + i]);
+ rc = -EFAULT;
+ } else {
+ rc = uv_pages;
+ }
+ } else {
+ IVTV_DEBUG_WARN
+ ("failed to map y user pages, returned %d "
+ "expecting %d\n", y_pages, y_dma.page_count);
+ }
+ if (y_pages >= 0) {
+ for (i = 0; i < y_pages; i++)
+ put_page(dma->map[i]);
+ /*
+ * Inherit the -EFAULT from rc's
+ * initialization, but allow it to be
+ * overriden by uv_pages above if it was an
+ * actual errno.
+ */
+ } else {
+ rc = y_pages;
+ }
+ return rc;
+ }
+
+ dma->page_count = y_pages + uv_pages;
+
+ /* Fill & map SG List */
+ if (ivtv_udma_fill_sg_list (dma, &uv_dma, ivtv_udma_fill_sg_list (dma, &y_dma, 0)) < 0) {
+ IVTV_DEBUG_WARN("could not allocate bounce buffers for highmem userspace buffers\n");
+ for (i = 0; i < dma->page_count; i++) {
+ put_page(dma->map[i]);
+ }
+ dma->page_count = 0;
+ return -ENOMEM;
+ }
+ dma->SG_length = pci_map_sg(itv->pdev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
+
+ /* Fill SG Array with new values */
+ ivtv_udma_fill_sg_array(dma, y_buffer_offset, uv_buffer_offset, y_size);
+
+ /* If we've offset the y plane, ensure top area is blanked */
+ if (f->offset_y && yi->blanking_dmaptr) {
+ dma->SGarray[dma->SG_length].size = cpu_to_le32(720*16);
+ dma->SGarray[dma->SG_length].src = cpu_to_le32(yi->blanking_dmaptr);
+ dma->SGarray[dma->SG_length].dst = cpu_to_le32(IVTV_DECODER_OFFSET + yuv_offset[frame]);
+ dma->SG_length++;
+ }
+
+ /* Tag SG Array with Interrupt Bit */
+ dma->SGarray[dma->SG_length - 1].size |= cpu_to_le32(0x80000000);
+
+ ivtv_udma_sync_for_device(itv);
+ return 0;
+}
+
+/* We rely on a table held in the firmware - Quick check. */
+int ivtv_yuv_filter_check(struct ivtv *itv)
+{
+ int i, y, uv;
+
+ for (i = 0, y = 16, uv = 4; i < 16; i++, y += 24, uv += 12) {
+ if ((read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + y) != i << 16) ||
+ (read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + uv) != i << 16)) {
+ IVTV_WARN ("YUV filter table not found in firmware.\n");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static void ivtv_yuv_filter(struct ivtv *itv, int h_filter, int v_filter_1, int v_filter_2)
+{
+ u32 i, line;
+
+ /* If any filter is -1, then don't update it */
+ if (h_filter > -1) {
+ if (h_filter > 4)
+ h_filter = 4;
+ i = IVTV_YUV_HORIZONTAL_FILTER_OFFSET + (h_filter * 384);
+ for (line = 0; line < 16; line++) {
+ write_reg(read_dec(i), 0x02804);
+ write_reg(read_dec(i), 0x0281c);
+ i += 4;
+ write_reg(read_dec(i), 0x02808);
+ write_reg(read_dec(i), 0x02820);
+ i += 4;
+ write_reg(read_dec(i), 0x0280c);
+ write_reg(read_dec(i), 0x02824);
+ i += 4;
+ write_reg(read_dec(i), 0x02810);
+ write_reg(read_dec(i), 0x02828);
+ i += 4;
+ write_reg(read_dec(i), 0x02814);
+ write_reg(read_dec(i), 0x0282c);
+ i += 8;
+ write_reg(0, 0x02818);
+ write_reg(0, 0x02830);
+ }
+ IVTV_DEBUG_YUV("h_filter -> %d\n", h_filter);
+ }
+
+ if (v_filter_1 > -1) {
+ if (v_filter_1 > 4)
+ v_filter_1 = 4;
+ i = IVTV_YUV_VERTICAL_FILTER_OFFSET + (v_filter_1 * 192);
+ for (line = 0; line < 16; line++) {
+ write_reg(read_dec(i), 0x02900);
+ i += 4;
+ write_reg(read_dec(i), 0x02904);
+ i += 8;
+ write_reg(0, 0x02908);
+ }
+ IVTV_DEBUG_YUV("v_filter_1 -> %d\n", v_filter_1);
+ }
+
+ if (v_filter_2 > -1) {
+ if (v_filter_2 > 4)
+ v_filter_2 = 4;
+ i = IVTV_YUV_VERTICAL_FILTER_OFFSET + (v_filter_2 * 192);
+ for (line = 0; line < 16; line++) {
+ write_reg(read_dec(i), 0x0290c);
+ i += 4;
+ write_reg(read_dec(i), 0x02910);
+ i += 8;
+ write_reg(0, 0x02914);
+ }
+ IVTV_DEBUG_YUV("v_filter_2 -> %d\n", v_filter_2);
+ }
+}
+
+static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info *f)
+{
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ u32 reg_2834, reg_2838, reg_283c;
+ u32 reg_2844, reg_2854, reg_285c;
+ u32 reg_2864, reg_2874, reg_2890;
+ u32 reg_2870, reg_2870_base, reg_2870_offset;
+ int x_cutoff;
+ int h_filter;
+ u32 master_width;
+
+ IVTV_DEBUG_WARN
+ ("Adjust to width %d src_w %d dst_w %d src_x %d dst_x %d\n",
+ f->tru_w, f->src_w, f->dst_w, f->src_x, f->dst_x);
+
+ /* How wide is the src image */
+ x_cutoff = f->src_w + f->src_x;
+
+ /* Set the display width */
+ reg_2834 = f->dst_w;
+ reg_2838 = reg_2834;
+
+ /* Set the display position */
+ reg_2890 = f->dst_x;
+
+ /* Index into the image horizontally */
+ reg_2870 = 0;
+
+ /* 2870 is normally fudged to align video coords with osd coords.
+ If running full screen, it causes an unwanted left shift
+ Remove the fudge if we almost fill the screen.
+ Gradually adjust the offset to avoid the video 'snapping'
+ left/right if it gets dragged through this region.
+ Only do this if osd is full width. */
+ if (f->vis_w == 720) {
+ if ((f->tru_x - f->pan_x > -1) && (f->tru_x - f->pan_x <= 40) && (f->dst_w >= 680))
+ reg_2870 = 10 - (f->tru_x - f->pan_x) / 4;
+ else if ((f->tru_x - f->pan_x < 0) && (f->tru_x - f->pan_x >= -20) && (f->dst_w >= 660))
+ reg_2870 = (10 + (f->tru_x - f->pan_x) / 2);
+
+ if (f->dst_w >= f->src_w)
+ reg_2870 = reg_2870 << 16 | reg_2870;
+ else
+ reg_2870 = ((reg_2870 & ~1) << 15) | (reg_2870 & ~1);
+ }
+
+ if (f->dst_w < f->src_w)
+ reg_2870 = 0x000d000e - reg_2870;
+ else
+ reg_2870 = 0x0012000e - reg_2870;
+
+ /* We're also using 2870 to shift the image left (src_x & negative dst_x) */
+ reg_2870_offset = (f->src_x * ((f->dst_w << 21) / f->src_w)) >> 19;
+
+ if (f->dst_w >= f->src_w) {
+ x_cutoff &= ~1;
+ master_width = (f->src_w * 0x00200000) / (f->dst_w);
+ if (master_width * f->dst_w != f->src_w * 0x00200000)
+ master_width++;
+ reg_2834 = (reg_2834 << 16) | x_cutoff;
+ reg_2838 = (reg_2838 << 16) | x_cutoff;
+ reg_283c = master_width >> 2;
+ reg_2844 = master_width >> 2;
+ reg_2854 = master_width;
+ reg_285c = master_width >> 1;
+ reg_2864 = master_width >> 1;
+
+ /* We also need to factor in the scaling
+ (src_w - dst_w) / (src_w / 4) */
+ if (f->dst_w > f->src_w)
+ reg_2870_base = ((f->dst_w - f->src_w)<<16) / (f->src_w <<14);
+ else
+ reg_2870_base = 0;
+
+ reg_2870 += (((reg_2870_offset << 14) & 0xFFFF0000) | reg_2870_offset >> 2) + (reg_2870_base << 17 | reg_2870_base);
+ reg_2874 = 0;
+ } else if (f->dst_w < f->src_w / 2) {
+ master_width = (f->src_w * 0x00080000) / f->dst_w;
+ if (master_width * f->dst_w != f->src_w * 0x00080000)
+ master_width++;
+ reg_2834 = (reg_2834 << 16) | x_cutoff;
+ reg_2838 = (reg_2838 << 16) | x_cutoff;
+ reg_283c = master_width >> 2;
+ reg_2844 = master_width >> 1;
+ reg_2854 = master_width;
+ reg_285c = master_width >> 1;
+ reg_2864 = master_width >> 1;
+ reg_2870 += ((reg_2870_offset << 15) & 0xFFFF0000) | reg_2870_offset;
+ reg_2870 += (5 - (((f->src_w + f->src_w / 2) - 1) / f->dst_w)) << 16;
+ reg_2874 = 0x00000012;
+ } else {
+ master_width = (f->src_w * 0x00100000) / f->dst_w;
+ if (master_width * f->dst_w != f->src_w * 0x00100000)
+ master_width++;
+ reg_2834 = (reg_2834 << 16) | x_cutoff;
+ reg_2838 = (reg_2838 << 16) | x_cutoff;
+ reg_283c = master_width >> 2;
+ reg_2844 = master_width >> 1;
+ reg_2854 = master_width;
+ reg_285c = master_width >> 1;
+ reg_2864 = master_width >> 1;
+ reg_2870 += ((reg_2870_offset << 14) & 0xFFFF0000) | reg_2870_offset >> 1;
+ reg_2870 += (5 - (((f->src_w * 3) - 1) / f->dst_w)) << 16;
+ reg_2874 = 0x00000001;
+ }
+
+ /* Select the horizontal filter */
+ if (f->src_w == f->dst_w) {
+ /* An exact size match uses filter 0 */
+ h_filter = 0;
+ } else {
+ /* Figure out which filter to use */
+ h_filter = ((f->src_w << 16) / f->dst_w) >> 15;
+ h_filter = (h_filter >> 1) + (h_filter & 1);
+ /* Only an exact size match can use filter 0 */
+ h_filter += !h_filter;
+ }
+
+ write_reg(reg_2834, 0x02834);
+ write_reg(reg_2838, 0x02838);
+ IVTV_DEBUG_YUV("Update reg 0x2834 %08x->%08x 0x2838 %08x->%08x\n",
+ yi->reg_2834, reg_2834, yi->reg_2838, reg_2838);
+
+ write_reg(reg_283c, 0x0283c);
+ write_reg(reg_2844, 0x02844);
+
+ IVTV_DEBUG_YUV("Update reg 0x283c %08x->%08x 0x2844 %08x->%08x\n",
+ yi->reg_283c, reg_283c, yi->reg_2844, reg_2844);
+
+ write_reg(0x00080514, 0x02840);
+ write_reg(0x00100514, 0x02848);
+ IVTV_DEBUG_YUV("Update reg 0x2840 %08x->%08x 0x2848 %08x->%08x\n",
+ yi->reg_2840, 0x00080514, yi->reg_2848, 0x00100514);
+
+ write_reg(reg_2854, 0x02854);
+ IVTV_DEBUG_YUV("Update reg 0x2854 %08x->%08x \n",
+ yi->reg_2854, reg_2854);
+
+ write_reg(reg_285c, 0x0285c);
+ write_reg(reg_2864, 0x02864);
+ IVTV_DEBUG_YUV("Update reg 0x285c %08x->%08x 0x2864 %08x->%08x\n",
+ yi->reg_285c, reg_285c, yi->reg_2864, reg_2864);
+
+ write_reg(reg_2874, 0x02874);
+ IVTV_DEBUG_YUV("Update reg 0x2874 %08x->%08x\n",
+ yi->reg_2874, reg_2874);
+
+ write_reg(reg_2870, 0x02870);
+ IVTV_DEBUG_YUV("Update reg 0x2870 %08x->%08x\n",
+ yi->reg_2870, reg_2870);
+
+ write_reg(reg_2890, 0x02890);
+ IVTV_DEBUG_YUV("Update reg 0x2890 %08x->%08x\n",
+ yi->reg_2890, reg_2890);
+
+ /* Only update the filter if we really need to */
+ if (h_filter != yi->h_filter) {
+ ivtv_yuv_filter(itv, h_filter, -1, -1);
+ yi->h_filter = h_filter;
+ }
+}
+
+static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *f)
+{
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ u32 master_height;
+ u32 reg_2918, reg_291c, reg_2920, reg_2928;
+ u32 reg_2930, reg_2934, reg_293c;
+ u32 reg_2940, reg_2944, reg_294c;
+ u32 reg_2950, reg_2954, reg_2958, reg_295c;
+ u32 reg_2960, reg_2964, reg_2968, reg_296c;
+ u32 reg_289c;
+ u32 src_major_y, src_minor_y;
+ u32 src_major_uv, src_minor_uv;
+ u32 reg_2964_base, reg_2968_base;
+ int v_filter_1, v_filter_2;
+
+ IVTV_DEBUG_WARN
+ ("Adjust to height %d src_h %d dst_h %d src_y %d dst_y %d\n",
+ f->tru_h, f->src_h, f->dst_h, f->src_y, f->dst_y);
+
+ /* What scaling mode is being used... */
+ IVTV_DEBUG_YUV("Scaling mode Y: %s\n",
+ f->interlaced_y ? "Interlaced" : "Progressive");
+
+ IVTV_DEBUG_YUV("Scaling mode UV: %s\n",
+ f->interlaced_uv ? "Interlaced" : "Progressive");
+
+ /* What is the source video being treated as... */
+ IVTV_DEBUG_WARN("Source video: %s\n",
+ f->interlaced ? "Interlaced" : "Progressive");
+
+ /* We offset into the image using two different index methods, so split
+ the y source coord into two parts. */
+ if (f->src_y < 8) {
+ src_minor_uv = f->src_y;
+ src_major_uv = 0;
+ } else {
+ src_minor_uv = 8;
+ src_major_uv = f->src_y - 8;
+ }
+
+ src_minor_y = src_minor_uv;
+ src_major_y = src_major_uv;
+
+ if (f->offset_y)
+ src_minor_y += 16;
+
+ if (f->interlaced_y)
+ reg_2918 = (f->dst_h << 16) | (f->src_h + src_minor_y);
+ else
+ reg_2918 = (f->dst_h << 16) | ((f->src_h + src_minor_y) << 1);
+
+ if (f->interlaced_uv)
+ reg_291c = (f->dst_h << 16) | ((f->src_h + src_minor_uv) >> 1);
+ else
+ reg_291c = (f->dst_h << 16) | (f->src_h + src_minor_uv);
+
+ reg_2964_base = (src_minor_y * ((f->dst_h << 16) / f->src_h)) >> 14;
+ reg_2968_base = (src_minor_uv * ((f->dst_h << 16) / f->src_h)) >> 14;
+
+ if (f->dst_h / 2 >= f->src_h && !f->interlaced_y) {
+ master_height = (f->src_h * 0x00400000) / f->dst_h;
+ if ((f->src_h * 0x00400000) - (master_height * f->dst_h) >= f->dst_h / 2)
+ master_height++;
+ reg_2920 = master_height >> 2;
+ reg_2928 = master_height >> 3;
+ reg_2930 = master_height;
+ reg_2940 = master_height >> 1;
+ reg_2964_base >>= 3;
+ reg_2968_base >>= 3;
+ reg_296c = 0x00000000;
+ } else if (f->dst_h >= f->src_h) {
+ master_height = (f->src_h * 0x00400000) / f->dst_h;
+ master_height = (master_height >> 1) + (master_height & 1);
+ reg_2920 = master_height >> 2;
+ reg_2928 = master_height >> 2;
+ reg_2930 = master_height;
+ reg_2940 = master_height >> 1;
+ reg_296c = 0x00000000;
+ if (f->interlaced_y) {
+ reg_2964_base >>= 3;
+ } else {
+ reg_296c++;
+ reg_2964_base >>= 2;
+ }
+ if (f->interlaced_uv)
+ reg_2928 >>= 1;
+ reg_2968_base >>= 3;
+ } else if (f->dst_h >= f->src_h / 2) {
+ master_height = (f->src_h * 0x00200000) / f->dst_h;
+ master_height = (master_height >> 1) + (master_height & 1);
+ reg_2920 = master_height >> 2;
+ reg_2928 = master_height >> 2;
+ reg_2930 = master_height;
+ reg_2940 = master_height;
+ reg_296c = 0x00000101;
+ if (f->interlaced_y) {
+ reg_2964_base >>= 2;
+ } else {
+ reg_296c++;
+ reg_2964_base >>= 1;
+ }
+ if (f->interlaced_uv)
+ reg_2928 >>= 1;
+ reg_2968_base >>= 2;
+ } else {
+ master_height = (f->src_h * 0x00100000) / f->dst_h;
+ master_height = (master_height >> 1) + (master_height & 1);
+ reg_2920 = master_height >> 2;
+ reg_2928 = master_height >> 2;
+ reg_2930 = master_height;
+ reg_2940 = master_height;
+ reg_2964_base >>= 1;
+ reg_2968_base >>= 2;
+ reg_296c = 0x00000102;
+ }
+
+ /* FIXME These registers change depending on scaled / unscaled output
+ We really need to work out what they should be */
+ if (f->src_h == f->dst_h) {
+ reg_2934 = 0x00020000;
+ reg_293c = 0x00100000;
+ reg_2944 = 0x00040000;
+ reg_294c = 0x000b0000;
+ } else {
+ reg_2934 = 0x00000FF0;
+ reg_293c = 0x00000FF0;
+ reg_2944 = 0x00000FF0;
+ reg_294c = 0x00000FF0;
+ }
+
+ /* The first line to be displayed */
+ reg_2950 = 0x00010000 + src_major_y;
+ if (f->interlaced_y)
+ reg_2950 += 0x00010000;
+ reg_2954 = reg_2950 + 1;
+
+ reg_2958 = 0x00010000 + (src_major_y >> 1);
+ if (f->interlaced_uv)
+ reg_2958 += 0x00010000;
+ reg_295c = reg_2958 + 1;
+
+ if (yi->decode_height == 480)
+ reg_289c = 0x011e0017;
+ else
+ reg_289c = 0x01500017;
+
+ if (f->dst_y < 0)
+ reg_289c = (reg_289c - ((f->dst_y & ~1)<<15))-(f->dst_y >>1);
+ else
+ reg_289c = (reg_289c + ((f->dst_y & ~1)<<15))+(f->dst_y >>1);
+
+ /* How much of the source to decode.
+ Take into account the source offset */
+ reg_2960 = ((src_minor_y + f->src_h + src_major_y) - 1) |
+ (((src_minor_uv + f->src_h + src_major_uv - 1) & ~1) << 15);
+
+ /* Calculate correct value for register 2964 */
+ if (f->src_h == f->dst_h) {
+ reg_2964 = 1;
+ } else {
+ reg_2964 = 2 + ((f->dst_h << 1) / f->src_h);
+ reg_2964 = (reg_2964 >> 1) + (reg_2964 & 1);
+ }
+ reg_2968 = (reg_2964 << 16) + reg_2964 + (reg_2964 >> 1);
+ reg_2964 = (reg_2964 << 16) + reg_2964 + (reg_2964 * 46 / 94);
+
+ /* Okay, we've wasted time working out the correct value,
+ but if we use it, it fouls the the window alignment.
+ Fudge it to what we want... */
+ reg_2964 = 0x00010001 + ((reg_2964 & 0x0000FFFF) - (reg_2964 >> 16));
+ reg_2968 = 0x00010001 + ((reg_2968 & 0x0000FFFF) - (reg_2968 >> 16));
+
+ /* Deviate further from what it should be. I find the flicker headache
+ inducing so try to reduce it slightly. Leave 2968 as-is otherwise
+ colours foul. */
+ if ((reg_2964 != 0x00010001) && (f->dst_h / 2 <= f->src_h))
+ reg_2964 = (reg_2964 & 0xFFFF0000) + ((reg_2964 & 0x0000FFFF) / 2);
+
+ if (!f->interlaced_y)
+ reg_2964 -= 0x00010001;
+ if (!f->interlaced_uv)
+ reg_2968 -= 0x00010001;
+
+ reg_2964 += ((reg_2964_base << 16) | reg_2964_base);
+ reg_2968 += ((reg_2968_base << 16) | reg_2968_base);
+
+ /* Select the vertical filter */
+ if (f->src_h == f->dst_h) {
+ /* An exact size match uses filter 0/1 */
+ v_filter_1 = 0;
+ v_filter_2 = 1;
+ } else {
+ /* Figure out which filter to use */
+ v_filter_1 = ((f->src_h << 16) / f->dst_h) >> 15;
+ v_filter_1 = (v_filter_1 >> 1) + (v_filter_1 & 1);
+ /* Only an exact size match can use filter 0 */
+ v_filter_1 += !v_filter_1;
+ v_filter_2 = v_filter_1;
+ }
+
+ write_reg(reg_2934, 0x02934);
+ write_reg(reg_293c, 0x0293c);
+ IVTV_DEBUG_YUV("Update reg 0x2934 %08x->%08x 0x293c %08x->%08x\n",
+ yi->reg_2934, reg_2934, yi->reg_293c, reg_293c);
+ write_reg(reg_2944, 0x02944);
+ write_reg(reg_294c, 0x0294c);
+ IVTV_DEBUG_YUV("Update reg 0x2944 %08x->%08x 0x294c %08x->%08x\n",
+ yi->reg_2944, reg_2944, yi->reg_294c, reg_294c);
+
+ /* Ensure 2970 is 0 (does it ever change ?) */
+/* write_reg(0,0x02970); */
+/* IVTV_DEBUG_YUV("Update reg 0x2970 %08x->%08x\n", yi->reg_2970, 0); */
+
+ write_reg(reg_2930, 0x02938);
+ write_reg(reg_2930, 0x02930);
+ IVTV_DEBUG_YUV("Update reg 0x2930 %08x->%08x 0x2938 %08x->%08x\n",
+ yi->reg_2930, reg_2930, yi->reg_2938, reg_2930);
+
+ write_reg(reg_2928, 0x02928);
+ write_reg(reg_2928 + 0x514, 0x0292C);
+ IVTV_DEBUG_YUV("Update reg 0x2928 %08x->%08x 0x292c %08x->%08x\n",
+ yi->reg_2928, reg_2928, yi->reg_292c, reg_2928 + 0x514);
+
+ write_reg(reg_2920, 0x02920);
+ write_reg(reg_2920 + 0x514, 0x02924);
+ IVTV_DEBUG_YUV("Update reg 0x2920 %08x->%08x 0x2924 %08x->%08x\n",
+ yi->reg_2920, reg_2920, yi->reg_2924, reg_2920 + 0x514);
+
+ write_reg(reg_2918, 0x02918);
+ write_reg(reg_291c, 0x0291C);
+ IVTV_DEBUG_YUV("Update reg 0x2918 %08x->%08x 0x291C %08x->%08x\n",
+ yi->reg_2918, reg_2918, yi->reg_291c, reg_291c);
+
+ write_reg(reg_296c, 0x0296c);
+ IVTV_DEBUG_YUV("Update reg 0x296c %08x->%08x\n",
+ yi->reg_296c, reg_296c);
+
+ write_reg(reg_2940, 0x02948);
+ write_reg(reg_2940, 0x02940);
+ IVTV_DEBUG_YUV("Update reg 0x2940 %08x->%08x 0x2948 %08x->%08x\n",
+ yi->reg_2940, reg_2940, yi->reg_2948, reg_2940);
+
+ write_reg(reg_2950, 0x02950);
+ write_reg(reg_2954, 0x02954);
+ IVTV_DEBUG_YUV("Update reg 0x2950 %08x->%08x 0x2954 %08x->%08x\n",
+ yi->reg_2950, reg_2950, yi->reg_2954, reg_2954);
+
+ write_reg(reg_2958, 0x02958);
+ write_reg(reg_295c, 0x0295C);
+ IVTV_DEBUG_YUV("Update reg 0x2958 %08x->%08x 0x295C %08x->%08x\n",
+ yi->reg_2958, reg_2958, yi->reg_295c, reg_295c);
+
+ write_reg(reg_2960, 0x02960);
+ IVTV_DEBUG_YUV("Update reg 0x2960 %08x->%08x \n",
+ yi->reg_2960, reg_2960);
+
+ write_reg(reg_2964, 0x02964);
+ write_reg(reg_2968, 0x02968);
+ IVTV_DEBUG_YUV("Update reg 0x2964 %08x->%08x 0x2968 %08x->%08x\n",
+ yi->reg_2964, reg_2964, yi->reg_2968, reg_2968);
+
+ write_reg(reg_289c, 0x0289c);
+ IVTV_DEBUG_YUV("Update reg 0x289c %08x->%08x\n",
+ yi->reg_289c, reg_289c);
+
+ /* Only update filter 1 if we really need to */
+ if (v_filter_1 != yi->v_filter_1) {
+ ivtv_yuv_filter(itv, -1, v_filter_1, -1);
+ yi->v_filter_1 = v_filter_1;
+ }
+
+ /* Only update filter 2 if we really need to */
+ if (v_filter_2 != yi->v_filter_2) {
+ ivtv_yuv_filter(itv, -1, -1, v_filter_2);
+ yi->v_filter_2 = v_filter_2;
+ }
+}
+
+/* Modify the supplied coordinate information to fit the visible osd area */
+static u32 ivtv_yuv_window_setup(struct ivtv *itv, struct yuv_frame_info *f)
+{
+ struct yuv_frame_info *of = &itv->yuv_info.old_frame_info;
+ int osd_crop;
+ u32 osd_scale;
+ u32 yuv_update = 0;
+
+ /* Sorry, but no negative coords for src */
+ if (f->src_x < 0)
+ f->src_x = 0;
+ if (f->src_y < 0)
+ f->src_y = 0;
+
+ /* Can only reduce width down to 1/4 original size */
+ if ((osd_crop = f->src_w - 4 * f->dst_w) > 0) {
+ f->src_x += osd_crop / 2;
+ f->src_w = (f->src_w - osd_crop) & ~3;
+ f->dst_w = f->src_w / 4;
+ f->dst_w += f->dst_w & 1;
+ }
+
+ /* Can only reduce height down to 1/4 original size */
+ if (f->src_h / f->dst_h >= 2) {
+ /* Overflow may be because we're running progressive,
+ so force mode switch */
+ f->interlaced_y = 1;
+ /* Make sure we're still within limits for interlace */
+ if ((osd_crop = f->src_h - 4 * f->dst_h) > 0) {
+ /* If we reach here we'll have to force the height. */
+ f->src_y += osd_crop / 2;
+ f->src_h = (f->src_h - osd_crop) & ~3;
+ f->dst_h = f->src_h / 4;
+ f->dst_h += f->dst_h & 1;
+ }
+ }
+
+ /* If there's nothing to safe to display, we may as well stop now */
+ if ((int)f->dst_w <= 2 || (int)f->dst_h <= 2 ||
+ (int)f->src_w <= 2 || (int)f->src_h <= 2) {
+ return IVTV_YUV_UPDATE_INVALID;
+ }
+
+ /* Ensure video remains inside OSD area */
+ osd_scale = (f->src_h << 16) / f->dst_h;
+
+ if ((osd_crop = f->pan_y - f->dst_y) > 0) {
+ /* Falls off the upper edge - crop */
+ f->src_y += (osd_scale * osd_crop) >> 16;
+ f->src_h -= (osd_scale * osd_crop) >> 16;
+ f->dst_h -= osd_crop;
+ f->dst_y = 0;
+ } else {
+ f->dst_y -= f->pan_y;
+ }
+
+ if ((osd_crop = f->dst_h + f->dst_y - f->vis_h) > 0) {
+ /* Falls off the lower edge - crop */
+ f->dst_h -= osd_crop;
+ f->src_h -= (osd_scale * osd_crop) >> 16;
+ }
+
+ osd_scale = (f->src_w << 16) / f->dst_w;
+
+ if ((osd_crop = f->pan_x - f->dst_x) > 0) {
+ /* Fall off the left edge - crop */
+ f->src_x += (osd_scale * osd_crop) >> 16;
+ f->src_w -= (osd_scale * osd_crop) >> 16;
+ f->dst_w -= osd_crop;
+ f->dst_x = 0;
+ } else {
+ f->dst_x -= f->pan_x;
+ }
+
+ if ((osd_crop = f->dst_w + f->dst_x - f->vis_w) > 0) {
+ /* Falls off the right edge - crop */
+ f->dst_w -= osd_crop;
+ f->src_w -= (osd_scale * osd_crop) >> 16;
+ }
+
+ if (itv->yuv_info.track_osd) {
+ /* The OSD can be moved. Track to it */
+ f->dst_x += itv->yuv_info.osd_x_offset;
+ f->dst_y += itv->yuv_info.osd_y_offset;
+ }
+
+ /* Width & height for both src & dst must be even.
+ Same for coordinates. */
+ f->dst_w &= ~1;
+ f->dst_x &= ~1;
+
+ f->src_w += f->src_x & 1;
+ f->src_x &= ~1;
+
+ f->src_w &= ~1;
+ f->dst_w &= ~1;
+
+ f->dst_h &= ~1;
+ f->dst_y &= ~1;
+
+ f->src_h += f->src_y & 1;
+ f->src_y &= ~1;
+
+ f->src_h &= ~1;
+ f->dst_h &= ~1;
+
+ /* Due to rounding, we may have reduced the output size to <1/4 of
+ the source. Check again, but this time just resize. Don't change
+ source coordinates */
+ if (f->dst_w < f->src_w / 4) {
+ f->src_w &= ~3;
+ f->dst_w = f->src_w / 4;
+ f->dst_w += f->dst_w & 1;
+ }
+ if (f->dst_h < f->src_h / 4) {
+ f->src_h &= ~3;
+ f->dst_h = f->src_h / 4;
+ f->dst_h += f->dst_h & 1;
+ }
+
+ /* Check again. If there's nothing to safe to display, stop now */
+ if ((int)f->dst_w <= 2 || (int)f->dst_h <= 2 ||
+ (int)f->src_w <= 2 || (int)f->src_h <= 2) {
+ return IVTV_YUV_UPDATE_INVALID;
+ }
+
+ /* Both x offset & width are linked, so they have to be done together */
+ if ((of->dst_w != f->dst_w) || (of->src_w != f->src_w) ||
+ (of->dst_x != f->dst_x) || (of->src_x != f->src_x) ||
+ (of->pan_x != f->pan_x) || (of->vis_w != f->vis_w)) {
+ yuv_update |= IVTV_YUV_UPDATE_HORIZONTAL;
+ }
+
+ if ((of->src_h != f->src_h) || (of->dst_h != f->dst_h) ||
+ (of->dst_y != f->dst_y) || (of->src_y != f->src_y) ||
+ (of->pan_y != f->pan_y) || (of->vis_h != f->vis_h) ||
+ (of->lace_mode != f->lace_mode) ||
+ (of->interlaced_y != f->interlaced_y) ||
+ (of->interlaced_uv != f->interlaced_uv)) {
+ yuv_update |= IVTV_YUV_UPDATE_VERTICAL;
+ }
+
+ return yuv_update;
+}
+
+/* Update the scaling register to the requested value */
+void ivtv_yuv_work_handler(struct ivtv *itv)
+{
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ struct yuv_frame_info f;
+ int frame = yi->update_frame;
+ u32 yuv_update;
+
+ IVTV_DEBUG_YUV("Update yuv registers for frame %d\n", frame);
+ f = yi->new_frame_info[frame];
+
+ if (yi->track_osd) {
+ /* Snapshot the osd pan info */
+ f.pan_x = yi->osd_x_pan;
+ f.pan_y = yi->osd_y_pan;
+ f.vis_w = yi->osd_vis_w;
+ f.vis_h = yi->osd_vis_h;
+ } else {
+ /* Not tracking the osd, so assume full screen */
+ f.pan_x = 0;
+ f.pan_y = 0;
+ f.vis_w = 720;
+ f.vis_h = yi->decode_height;
+ }
+
+ /* Calculate the display window coordinates. Exit if nothing left */
+ if (!(yuv_update = ivtv_yuv_window_setup(itv, &f)))
+ return;
+
+ if (yuv_update & IVTV_YUV_UPDATE_INVALID) {
+ write_reg(0x01008080, 0x2898);
+ } else if (yuv_update) {
+ write_reg(0x00108080, 0x2898);
+
+ if (yuv_update & IVTV_YUV_UPDATE_HORIZONTAL)
+ ivtv_yuv_handle_horizontal(itv, &f);
+
+ if (yuv_update & IVTV_YUV_UPDATE_VERTICAL)
+ ivtv_yuv_handle_vertical(itv, &f);
+ }
+ yi->old_frame_info = f;
+}
+
+static void ivtv_yuv_init(struct ivtv *itv)
+{
+ struct yuv_playback_info *yi = &itv->yuv_info;
+
+ IVTV_DEBUG_YUV("ivtv_yuv_init\n");
+
+ /* Take a snapshot of the current register settings */
+ yi->reg_2834 = read_reg(0x02834);
+ yi->reg_2838 = read_reg(0x02838);
+ yi->reg_283c = read_reg(0x0283c);
+ yi->reg_2840 = read_reg(0x02840);
+ yi->reg_2844 = read_reg(0x02844);
+ yi->reg_2848 = read_reg(0x02848);
+ yi->reg_2854 = read_reg(0x02854);
+ yi->reg_285c = read_reg(0x0285c);
+ yi->reg_2864 = read_reg(0x02864);
+ yi->reg_2870 = read_reg(0x02870);
+ yi->reg_2874 = read_reg(0x02874);
+ yi->reg_2898 = read_reg(0x02898);
+ yi->reg_2890 = read_reg(0x02890);
+
+ yi->reg_289c = read_reg(0x0289c);
+ yi->reg_2918 = read_reg(0x02918);
+ yi->reg_291c = read_reg(0x0291c);
+ yi->reg_2920 = read_reg(0x02920);
+ yi->reg_2924 = read_reg(0x02924);
+ yi->reg_2928 = read_reg(0x02928);
+ yi->reg_292c = read_reg(0x0292c);
+ yi->reg_2930 = read_reg(0x02930);
+ yi->reg_2934 = read_reg(0x02934);
+ yi->reg_2938 = read_reg(0x02938);
+ yi->reg_293c = read_reg(0x0293c);
+ yi->reg_2940 = read_reg(0x02940);
+ yi->reg_2944 = read_reg(0x02944);
+ yi->reg_2948 = read_reg(0x02948);
+ yi->reg_294c = read_reg(0x0294c);
+ yi->reg_2950 = read_reg(0x02950);
+ yi->reg_2954 = read_reg(0x02954);
+ yi->reg_2958 = read_reg(0x02958);
+ yi->reg_295c = read_reg(0x0295c);
+ yi->reg_2960 = read_reg(0x02960);
+ yi->reg_2964 = read_reg(0x02964);
+ yi->reg_2968 = read_reg(0x02968);
+ yi->reg_296c = read_reg(0x0296c);
+ yi->reg_2970 = read_reg(0x02970);
+
+ yi->v_filter_1 = -1;
+ yi->v_filter_2 = -1;
+ yi->h_filter = -1;
+
+ /* Set some valid size info */
+ yi->osd_x_offset = read_reg(0x02a04) & 0x00000FFF;
+ yi->osd_y_offset = (read_reg(0x02a04) >> 16) & 0x00000FFF;
+
+ /* Bit 2 of reg 2878 indicates current decoder output format
+ 0 : NTSC 1 : PAL */
+ if (read_reg(0x2878) & 4)
+ yi->decode_height = 576;
+ else
+ yi->decode_height = 480;
+
+ if (!itv->osd_info) {
+ yi->osd_vis_w = 720 - yi->osd_x_offset;
+ yi->osd_vis_h = yi->decode_height - yi->osd_y_offset;
+ } else {
+ /* If no visible size set, assume full size */
+ if (!yi->osd_vis_w)
+ yi->osd_vis_w = 720 - yi->osd_x_offset;
+
+ if (!yi->osd_vis_h) {
+ yi->osd_vis_h = yi->decode_height - yi->osd_y_offset;
+ } else if (yi->osd_vis_h + yi->osd_y_offset > yi->decode_height) {
+ /* If output video standard has changed, requested height may
+ not be legal */
+ IVTV_DEBUG_WARN("Clipping yuv output - fb size (%d) exceeds video standard limit (%d)\n",
+ yi->osd_vis_h + yi->osd_y_offset,
+ yi->decode_height);
+ yi->osd_vis_h = yi->decode_height - yi->osd_y_offset;
+ }
+ }
+
+ /* We need a buffer for blanking when Y plane is offset - non-fatal if we can't get one */
+ yi->blanking_ptr = kzalloc(720 * 16, GFP_KERNEL|__GFP_NOWARN);
+ if (yi->blanking_ptr) {
+ yi->blanking_dmaptr = pci_map_single(itv->pdev, yi->blanking_ptr, 720*16, PCI_DMA_TODEVICE);
+ } else {
+ yi->blanking_dmaptr = 0;
+ IVTV_DEBUG_WARN("Failed to allocate yuv blanking buffer\n");
+ }
+
+ /* Enable YUV decoder output */
+ write_reg_sync(0x01, IVTV_REG_VDM);
+
+ set_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags);
+ atomic_set(&yi->next_dma_frame, 0);
+}
+
+/* Get next available yuv buffer on PVR350 */
+static void ivtv_yuv_next_free(struct ivtv *itv)
+{
+ int draw, display;
+ struct yuv_playback_info *yi = &itv->yuv_info;
+
+ if (atomic_read(&yi->next_dma_frame) == -1)
+ ivtv_yuv_init(itv);
+
+ draw = atomic_read(&yi->next_fill_frame);
+ display = atomic_read(&yi->next_dma_frame);
+
+ if (display > draw)
+ display -= IVTV_YUV_BUFFERS;
+
+ if (draw - display >= yi->max_frames_buffered)
+ draw = (u8)(draw - 1) % IVTV_YUV_BUFFERS;
+ else
+ yi->new_frame_info[draw].update = 0;
+
+ yi->draw_frame = draw;
+}
+
+/* Set up frame according to ivtv_dma_frame parameters */
+static void ivtv_yuv_setup_frame(struct ivtv *itv, struct ivtv_dma_frame *args)
+{
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ u8 frame = yi->draw_frame;
+ u8 last_frame = (u8)(frame - 1) % IVTV_YUV_BUFFERS;
+ struct yuv_frame_info *nf = &yi->new_frame_info[frame];
+ struct yuv_frame_info *of = &yi->new_frame_info[last_frame];
+ int lace_threshold = yi->lace_threshold;
+
+ /* Preserve old update flag in case we're overwriting a queued frame */
+ int update = nf->update;
+
+ /* Take a snapshot of the yuv coordinate information */
+ nf->src_x = args->src.left;
+ nf->src_y = args->src.top;
+ nf->src_w = args->src.width;
+ nf->src_h = args->src.height;
+ nf->dst_x = args->dst.left;
+ nf->dst_y = args->dst.top;
+ nf->dst_w = args->dst.width;
+ nf->dst_h = args->dst.height;
+ nf->tru_x = args->dst.left;
+ nf->tru_w = args->src_width;
+ nf->tru_h = args->src_height;
+
+ /* Are we going to offset the Y plane */
+ nf->offset_y = (nf->tru_h + nf->src_x < 512 - 16) ? 1 : 0;
+
+ nf->update = 0;
+ nf->interlaced_y = 0;
+ nf->interlaced_uv = 0;
+ nf->delay = 0;
+ nf->sync_field = 0;
+ nf->lace_mode = yi->lace_mode & IVTV_YUV_MODE_MASK;
+
+ if (lace_threshold < 0)
+ lace_threshold = yi->decode_height - 1;
+
+ /* Work out the lace settings */
+ switch (nf->lace_mode) {
+ case IVTV_YUV_MODE_PROGRESSIVE: /* Progressive mode */
+ nf->interlaced = 0;
+ if (nf->tru_h < 512 || (nf->tru_h > 576 && nf->tru_h < 1021))
+ nf->interlaced_y = 0;
+ else
+ nf->interlaced_y = 1;
+
+ if (nf->tru_h < 1021 && (nf->dst_h >= nf->src_h / 2))
+ nf->interlaced_uv = 0;
+ else
+ nf->interlaced_uv = 1;
+ break;
+
+ case IVTV_YUV_MODE_AUTO:
+ if (nf->tru_h <= lace_threshold || nf->tru_h > 576 || nf->tru_w > 720) {
+ nf->interlaced = 0;
+ if ((nf->tru_h < 512) ||
+ (nf->tru_h > 576 && nf->tru_h < 1021) ||
+ (nf->tru_w > 720 && nf->tru_h < 1021))
+ nf->interlaced_y = 0;
+ else
+ nf->interlaced_y = 1;
+ if (nf->tru_h < 1021 && (nf->dst_h >= nf->src_h / 2))
+ nf->interlaced_uv = 0;
+ else
+ nf->interlaced_uv = 1;
+ } else {
+ nf->interlaced = 1;
+ nf->interlaced_y = 1;
+ nf->interlaced_uv = 1;
+ }
+ break;
+
+ case IVTV_YUV_MODE_INTERLACED: /* Interlace mode */
+ default:
+ nf->interlaced = 1;
+ nf->interlaced_y = 1;
+ nf->interlaced_uv = 1;
+ break;
+ }
+
+ if (memcmp(&yi->old_frame_info_args, nf, sizeof(*nf))) {
+ yi->old_frame_info_args = *nf;
+ nf->update = 1;
+ IVTV_DEBUG_YUV("Requesting reg update for frame %d\n", frame);
+ }
+
+ nf->update |= update;
+ nf->sync_field = yi->lace_sync_field;
+ nf->delay = nf->sync_field != of->sync_field;
+}
+
+/* Frame is complete & ready for display */
+void ivtv_yuv_frame_complete(struct ivtv *itv)
+{
+ atomic_set(&itv->yuv_info.next_fill_frame,
+ (itv->yuv_info.draw_frame + 1) % IVTV_YUV_BUFFERS);
+}
+
+static int ivtv_yuv_udma_frame(struct ivtv *itv, struct ivtv_dma_frame *args)
+{
+ DEFINE_WAIT(wait);
+ int rc = 0;
+ int got_sig = 0;
+ /* DMA the frame */
+ mutex_lock(&itv->udma.lock);
+
+ if ((rc = ivtv_yuv_prep_user_dma(itv, &itv->udma, args)) != 0) {
+ mutex_unlock(&itv->udma.lock);
+ return rc;
+ }
+
+ ivtv_udma_prepare(itv);
+ prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
+ /* if no UDMA is pending and no UDMA is in progress, then the DMA
+ is finished */
+ while (test_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags) ||
+ test_bit(IVTV_F_I_UDMA, &itv->i_flags)) {
+ /* don't interrupt if the DMA is in progress but break off
+ a still pending DMA. */
+ got_sig = signal_pending(current);
+ if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags))
+ break;
+ got_sig = 0;
+ schedule();
+ }
+ finish_wait(&itv->dma_waitq, &wait);
+
+ /* Unmap Last DMA Xfer */
+ ivtv_udma_unmap(itv);
+
+ if (got_sig) {
+ IVTV_DEBUG_INFO("User stopped YUV UDMA\n");
+ mutex_unlock(&itv->udma.lock);
+ return -EINTR;
+ }
+
+ ivtv_yuv_frame_complete(itv);
+
+ mutex_unlock(&itv->udma.lock);
+ return rc;
+}
+
+/* Setup frame according to V4L2 parameters */
+void ivtv_yuv_setup_stream_frame(struct ivtv *itv)
+{
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ struct ivtv_dma_frame dma_args;
+
+ ivtv_yuv_next_free(itv);
+
+ /* Copy V4L2 parameters to an ivtv_dma_frame struct... */
+ dma_args.y_source = NULL;
+ dma_args.uv_source = NULL;
+ dma_args.src.left = 0;
+ dma_args.src.top = 0;
+ dma_args.src.width = yi->v4l2_src_w;
+ dma_args.src.height = yi->v4l2_src_h;
+ dma_args.dst = yi->main_rect;
+ dma_args.src_width = yi->v4l2_src_w;
+ dma_args.src_height = yi->v4l2_src_h;
+
+ /* ... and use the same setup routine as ivtv_yuv_prep_frame */
+ ivtv_yuv_setup_frame(itv, &dma_args);
+
+ if (!itv->dma_data_req_offset)
+ itv->dma_data_req_offset = yuv_offset[yi->draw_frame];
+}
+
+/* Attempt to dma a frame from a user buffer */
+int ivtv_yuv_udma_stream_frame(struct ivtv *itv, void __user *src)
+{
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ struct ivtv_dma_frame dma_args;
+ int res;
+
+ ivtv_yuv_setup_stream_frame(itv);
+
+ /* We only need to supply source addresses for this */
+ dma_args.y_source = src;
+ dma_args.uv_source = src + 720 * ((yi->v4l2_src_h + 31) & ~31);
+ /* Wait for frame DMA. Note that serialize_lock is locked,
+ so to allow other processes to access the driver while
+ we are waiting unlock first and later lock again. */
+ mutex_unlock(&itv->serialize_lock);
+ res = ivtv_yuv_udma_frame(itv, &dma_args);
+ mutex_lock(&itv->serialize_lock);
+ return res;
+}
+
+/* IVTV_IOC_DMA_FRAME ioctl handler */
+int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args)
+{
+ int res;
+
+/* IVTV_DEBUG_INFO("yuv_prep_frame\n"); */
+ ivtv_yuv_next_free(itv);
+ ivtv_yuv_setup_frame(itv, args);
+ /* Wait for frame DMA. Note that serialize_lock is locked,
+ so to allow other processes to access the driver while
+ we are waiting unlock first and later lock again. */
+ mutex_unlock(&itv->serialize_lock);
+ res = ivtv_yuv_udma_frame(itv, args);
+ mutex_lock(&itv->serialize_lock);
+ return res;
+}
+
+void ivtv_yuv_close(struct ivtv *itv)
+{
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ int h_filter, v_filter_1, v_filter_2;
+
+ IVTV_DEBUG_YUV("ivtv_yuv_close\n");
+ mutex_unlock(&itv->serialize_lock);
+ ivtv_waitq(&itv->vsync_waitq);
+ mutex_lock(&itv->serialize_lock);
+
+ yi->running = 0;
+ atomic_set(&yi->next_dma_frame, -1);
+ atomic_set(&yi->next_fill_frame, 0);
+
+ /* Reset registers we have changed so mpeg playback works */
+
+ /* If we fully restore this register, the display may remain active.
+ Restore, but set one bit to blank the video. Firmware will always
+ clear this bit when needed, so not a problem. */
+ write_reg(yi->reg_2898 | 0x01000000, 0x2898);
+
+ write_reg(yi->reg_2834, 0x02834);
+ write_reg(yi->reg_2838, 0x02838);
+ write_reg(yi->reg_283c, 0x0283c);
+ write_reg(yi->reg_2840, 0x02840);
+ write_reg(yi->reg_2844, 0x02844);
+ write_reg(yi->reg_2848, 0x02848);
+ write_reg(yi->reg_2854, 0x02854);
+ write_reg(yi->reg_285c, 0x0285c);
+ write_reg(yi->reg_2864, 0x02864);
+ write_reg(yi->reg_2870, 0x02870);
+ write_reg(yi->reg_2874, 0x02874);
+ write_reg(yi->reg_2890, 0x02890);
+ write_reg(yi->reg_289c, 0x0289c);
+
+ write_reg(yi->reg_2918, 0x02918);
+ write_reg(yi->reg_291c, 0x0291c);
+ write_reg(yi->reg_2920, 0x02920);
+ write_reg(yi->reg_2924, 0x02924);
+ write_reg(yi->reg_2928, 0x02928);
+ write_reg(yi->reg_292c, 0x0292c);
+ write_reg(yi->reg_2930, 0x02930);
+ write_reg(yi->reg_2934, 0x02934);
+ write_reg(yi->reg_2938, 0x02938);
+ write_reg(yi->reg_293c, 0x0293c);
+ write_reg(yi->reg_2940, 0x02940);
+ write_reg(yi->reg_2944, 0x02944);
+ write_reg(yi->reg_2948, 0x02948);
+ write_reg(yi->reg_294c, 0x0294c);
+ write_reg(yi->reg_2950, 0x02950);
+ write_reg(yi->reg_2954, 0x02954);
+ write_reg(yi->reg_2958, 0x02958);
+ write_reg(yi->reg_295c, 0x0295c);
+ write_reg(yi->reg_2960, 0x02960);
+ write_reg(yi->reg_2964, 0x02964);
+ write_reg(yi->reg_2968, 0x02968);
+ write_reg(yi->reg_296c, 0x0296c);
+ write_reg(yi->reg_2970, 0x02970);
+
+ /* Prepare to restore filters */
+
+ /* First the horizontal filter */
+ if ((yi->reg_2834 & 0x0000FFFF) == (yi->reg_2834 >> 16)) {
+ /* An exact size match uses filter 0 */
+ h_filter = 0;
+ } else {
+ /* Figure out which filter to use */
+ h_filter = ((yi->reg_2834 << 16) / (yi->reg_2834 >> 16)) >> 15;
+ h_filter = (h_filter >> 1) + (h_filter & 1);
+ /* Only an exact size match can use filter 0. */
+ h_filter += !h_filter;
+ }
+
+ /* Now the vertical filter */
+ if ((yi->reg_2918 & 0x0000FFFF) == (yi->reg_2918 >> 16)) {
+ /* An exact size match uses filter 0/1 */
+ v_filter_1 = 0;
+ v_filter_2 = 1;
+ } else {
+ /* Figure out which filter to use */
+ v_filter_1 = ((yi->reg_2918 << 16) / (yi->reg_2918 >> 16)) >> 15;
+ v_filter_1 = (v_filter_1 >> 1) + (v_filter_1 & 1);
+ /* Only an exact size match can use filter 0 */
+ v_filter_1 += !v_filter_1;
+ v_filter_2 = v_filter_1;
+ }
+
+ /* Now restore the filters */
+ ivtv_yuv_filter(itv, h_filter, v_filter_1, v_filter_2);
+
+ /* and clear a few registers */
+ write_reg(0, 0x02814);
+ write_reg(0, 0x0282c);
+ write_reg(0, 0x02904);
+ write_reg(0, 0x02910);
+
+ /* Release the blanking buffer */
+ if (yi->blanking_ptr) {
+ kfree(yi->blanking_ptr);
+ yi->blanking_ptr = NULL;
+ pci_unmap_single(itv->pdev, yi->blanking_dmaptr, 720*16, PCI_DMA_TODEVICE);
+ }
+
+ /* Invalidate the old dimension information */
+ yi->old_frame_info.src_w = 0;
+ yi->old_frame_info.src_h = 0;
+ yi->old_frame_info_args.src_w = 0;
+ yi->old_frame_info_args.src_h = 0;
+
+ /* All done. */
+ clear_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags);
+}
diff --git a/drivers/media/video/ivtv/ivtv-yuv.h b/drivers/media/video/ivtv/ivtv-yuv.h
new file mode 100644
index 00000000..ca5173fb
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-yuv.h
@@ -0,0 +1,44 @@
+/*
+ yuv support
+
+ Copyright (C) 2007 Ian Armstrong <ian@iarmst.demon.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef IVTV_YUV_H
+#define IVTV_YUV_H
+
+#define IVTV_YUV_BUFFER_UV_OFFSET 0x65400 /* Offset to UV Buffer */
+
+/* Offset to filter table in firmware */
+#define IVTV_YUV_HORIZONTAL_FILTER_OFFSET 0x025d8
+#define IVTV_YUV_VERTICAL_FILTER_OFFSET 0x03358
+
+#define IVTV_YUV_UPDATE_HORIZONTAL 0x01
+#define IVTV_YUV_UPDATE_VERTICAL 0x02
+#define IVTV_YUV_UPDATE_INVALID 0x04
+
+extern const u32 yuv_offset[IVTV_YUV_BUFFERS];
+
+int ivtv_yuv_filter_check(struct ivtv *itv);
+void ivtv_yuv_setup_stream_frame(struct ivtv *itv);
+int ivtv_yuv_udma_stream_frame(struct ivtv *itv, void __user *src);
+void ivtv_yuv_frame_complete(struct ivtv *itv);
+int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args);
+void ivtv_yuv_close(struct ivtv *itv);
+void ivtv_yuv_work_handler(struct ivtv *itv);
+
+#endif
diff --git a/drivers/media/video/ivtv/ivtvfb.c b/drivers/media/video/ivtv/ivtvfb.c
new file mode 100644
index 00000000..e5e7fa9e
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtvfb.c
@@ -0,0 +1,1315 @@
+/*
+ On Screen Display cx23415 Framebuffer driver
+
+ This module presents the cx23415 OSD (onscreen display) framebuffer memory
+ as a standard Linux /dev/fb style framebuffer device. The framebuffer has
+ support for 8, 16 & 32 bpp packed pixel formats with alpha channel. In 16bpp
+ mode, there is a choice of a three color depths (12, 15 or 16 bits), but no
+ local alpha. The colorspace is selectable between rgb & yuv.
+ Depending on the TV standard configured in the ivtv module at load time,
+ the initial resolution is either 640x400 (NTSC) or 640x480 (PAL) at 8bpp.
+ Video timings are locked to ensure a vertical refresh rate of 50Hz (PAL)
+ or 59.94 (NTSC)
+
+ Copyright (c) 2003 Matt T. Yourst <yourst@yourst.com>
+
+ Derived from drivers/video/vesafb.c
+ Portions (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+
+ 2.6 kernel port:
+ Copyright (C) 2004 Matthias Badaire
+
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+
+ Copyright (C) 2006 Ian Armstrong <ian@iarmst.demon.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fb.h>
+#include <linux/ivtvfb.h>
+#include <linux/slab.h>
+
+#ifdef CONFIG_MTRR
+#include <asm/mtrr.h>
+#endif
+
+#include "ivtv-driver.h"
+#include "ivtv-cards.h"
+#include "ivtv-i2c.h"
+#include "ivtv-udma.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-firmware.h"
+
+/* card parameters */
+static int ivtvfb_card_id = -1;
+static int ivtvfb_debug = 0;
+static bool osd_laced;
+static int osd_depth;
+static int osd_upper;
+static int osd_left;
+static int osd_yres;
+static int osd_xres;
+
+module_param(ivtvfb_card_id, int, 0444);
+module_param_named(debug,ivtvfb_debug, int, 0644);
+module_param(osd_laced, bool, 0444);
+module_param(osd_depth, int, 0444);
+module_param(osd_upper, int, 0444);
+module_param(osd_left, int, 0444);
+module_param(osd_yres, int, 0444);
+module_param(osd_xres, int, 0444);
+
+MODULE_PARM_DESC(ivtvfb_card_id,
+ "Only use framebuffer of the specified ivtv card (0-31)\n"
+ "\t\t\tdefault -1: initialize all available framebuffers");
+
+MODULE_PARM_DESC(debug,
+ "Debug level (bitmask). Default: errors only\n"
+ "\t\t\t(debug = 3 gives full debugging)");
+
+/* Why upper, left, xres, yres, depth, laced ? To match terminology used
+ by fbset.
+ Why start at 1 for left & upper coordinate ? Because X doesn't allow 0 */
+
+MODULE_PARM_DESC(osd_laced,
+ "Interlaced mode\n"
+ "\t\t\t0=off\n"
+ "\t\t\t1=on\n"
+ "\t\t\tdefault off");
+
+MODULE_PARM_DESC(osd_depth,
+ "Bits per pixel - 8, 16, 32\n"
+ "\t\t\tdefault 8");
+
+MODULE_PARM_DESC(osd_upper,
+ "Vertical start position\n"
+ "\t\t\tdefault 0 (Centered)");
+
+MODULE_PARM_DESC(osd_left,
+ "Horizontal start position\n"
+ "\t\t\tdefault 0 (Centered)");
+
+MODULE_PARM_DESC(osd_yres,
+ "Display height\n"
+ "\t\t\tdefault 480 (PAL)\n"
+ "\t\t\t 400 (NTSC)");
+
+MODULE_PARM_DESC(osd_xres,
+ "Display width\n"
+ "\t\t\tdefault 640");
+
+MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil, John Harvey, Ian Armstrong");
+MODULE_LICENSE("GPL");
+
+/* --------------------------------------------------------------------- */
+
+#define IVTVFB_DBGFLG_WARN (1 << 0)
+#define IVTVFB_DBGFLG_INFO (1 << 1)
+
+#define IVTVFB_DEBUG(x, type, fmt, args...) \
+ do { \
+ if ((x) & ivtvfb_debug) \
+ printk(KERN_INFO "ivtvfb%d " type ": " fmt, itv->instance , ## args); \
+ } while (0)
+#define IVTVFB_DEBUG_WARN(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_WARN, "warning", fmt , ## args)
+#define IVTVFB_DEBUG_INFO(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_INFO, "info", fmt , ## args)
+
+/* Standard kernel messages */
+#define IVTVFB_ERR(fmt, args...) printk(KERN_ERR "ivtvfb%d: " fmt, itv->instance , ## args)
+#define IVTVFB_WARN(fmt, args...) printk(KERN_WARNING "ivtvfb%d: " fmt, itv->instance , ## args)
+#define IVTVFB_INFO(fmt, args...) printk(KERN_INFO "ivtvfb%d: " fmt, itv->instance , ## args)
+
+/* --------------------------------------------------------------------- */
+
+#define IVTV_OSD_MAX_WIDTH 720
+#define IVTV_OSD_MAX_HEIGHT 576
+
+#define IVTV_OSD_BPP_8 0x00
+#define IVTV_OSD_BPP_16_444 0x03
+#define IVTV_OSD_BPP_16_555 0x02
+#define IVTV_OSD_BPP_16_565 0x01
+#define IVTV_OSD_BPP_32 0x04
+
+struct osd_info {
+ /* Physical base address */
+ unsigned long video_pbase;
+ /* Relative base address (relative to start of decoder memory) */
+ u32 video_rbase;
+ /* Mapped base address */
+ volatile char __iomem *video_vbase;
+ /* Buffer size */
+ u32 video_buffer_size;
+
+#ifdef CONFIG_MTRR
+ /* video_base rounded down as required by hardware MTRRs */
+ unsigned long fb_start_aligned_physaddr;
+ /* video_base rounded up as required by hardware MTRRs */
+ unsigned long fb_end_aligned_physaddr;
+#endif
+
+ /* Store the buffer offset */
+ int set_osd_coords_x;
+ int set_osd_coords_y;
+
+ /* Current dimensions (NOT VISIBLE SIZE!) */
+ int display_width;
+ int display_height;
+ int display_byte_stride;
+
+ /* Current bits per pixel */
+ int bits_per_pixel;
+ int bytes_per_pixel;
+
+ /* Frame buffer stuff */
+ struct fb_info ivtvfb_info;
+ struct fb_var_screeninfo ivtvfb_defined;
+ struct fb_fix_screeninfo ivtvfb_fix;
+
+ /* Used for a warm start */
+ struct fb_var_screeninfo fbvar_cur;
+ int blank_cur;
+ u32 palette_cur[256];
+ u32 pan_cur;
+};
+
+struct ivtv_osd_coords {
+ unsigned long offset;
+ unsigned long max_offset;
+ int pixel_stride;
+ int lines;
+ int x;
+ int y;
+};
+
+/* --------------------------------------------------------------------- */
+
+/* ivtv API calls for framebuffer related support */
+
+static int ivtvfb_get_framebuffer(struct ivtv *itv, u32 *fbbase,
+ u32 *fblength)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ int rc;
+
+ ivtv_firmware_check(itv, "ivtvfb_get_framebuffer");
+ rc = ivtv_vapi_result(itv, data, CX2341X_OSD_GET_FRAMEBUFFER, 0);
+ *fbbase = data[0];
+ *fblength = data[1];
+ return rc;
+}
+
+static int ivtvfb_get_osd_coords(struct ivtv *itv,
+ struct ivtv_osd_coords *osd)
+{
+ struct osd_info *oi = itv->osd_info;
+ u32 data[CX2341X_MBOX_MAX_DATA];
+
+ ivtv_vapi_result(itv, data, CX2341X_OSD_GET_OSD_COORDS, 0);
+
+ osd->offset = data[0] - oi->video_rbase;
+ osd->max_offset = oi->display_width * oi->display_height * 4;
+ osd->pixel_stride = data[1];
+ osd->lines = data[2];
+ osd->x = data[3];
+ osd->y = data[4];
+ return 0;
+}
+
+static int ivtvfb_set_osd_coords(struct ivtv *itv, const struct ivtv_osd_coords *osd)
+{
+ struct osd_info *oi = itv->osd_info;
+
+ oi->display_width = osd->pixel_stride;
+ oi->display_byte_stride = osd->pixel_stride * oi->bytes_per_pixel;
+ oi->set_osd_coords_x += osd->x;
+ oi->set_osd_coords_y = osd->y;
+
+ return ivtv_vapi(itv, CX2341X_OSD_SET_OSD_COORDS, 5,
+ osd->offset + oi->video_rbase,
+ osd->pixel_stride,
+ osd->lines, osd->x, osd->y);
+}
+
+static int ivtvfb_set_display_window(struct ivtv *itv, struct v4l2_rect *ivtv_window)
+{
+ int osd_height_limit = itv->is_out_50hz ? 576 : 480;
+
+ /* Only fail if resolution too high, otherwise fudge the start coords. */
+ if ((ivtv_window->height > osd_height_limit) || (ivtv_window->width > IVTV_OSD_MAX_WIDTH))
+ return -EINVAL;
+
+ /* Ensure we don't exceed display limits */
+ if (ivtv_window->top + ivtv_window->height > osd_height_limit) {
+ IVTVFB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid height setting (%d, %d)\n",
+ ivtv_window->top, ivtv_window->height);
+ ivtv_window->top = osd_height_limit - ivtv_window->height;
+ }
+
+ if (ivtv_window->left + ivtv_window->width > IVTV_OSD_MAX_WIDTH) {
+ IVTVFB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid width setting (%d, %d)\n",
+ ivtv_window->left, ivtv_window->width);
+ ivtv_window->left = IVTV_OSD_MAX_WIDTH - ivtv_window->width;
+ }
+
+ /* Set the OSD origin */
+ write_reg((ivtv_window->top << 16) | ivtv_window->left, 0x02a04);
+
+ /* How much to display */
+ write_reg(((ivtv_window->top+ivtv_window->height) << 16) | (ivtv_window->left+ivtv_window->width), 0x02a08);
+
+ /* Pass this info back the yuv handler */
+ itv->yuv_info.osd_vis_w = ivtv_window->width;
+ itv->yuv_info.osd_vis_h = ivtv_window->height;
+ itv->yuv_info.osd_x_offset = ivtv_window->left;
+ itv->yuv_info.osd_y_offset = ivtv_window->top;
+
+ return 0;
+}
+
+static int ivtvfb_prep_dec_dma_to_device(struct ivtv *itv,
+ unsigned long ivtv_dest_addr, void __user *userbuf,
+ int size_in_bytes)
+{
+ DEFINE_WAIT(wait);
+ int got_sig = 0;
+
+ mutex_lock(&itv->udma.lock);
+ /* Map User DMA */
+ if (ivtv_udma_setup(itv, ivtv_dest_addr, userbuf, size_in_bytes) <= 0) {
+ mutex_unlock(&itv->udma.lock);
+ IVTVFB_WARN("ivtvfb_prep_dec_dma_to_device, "
+ "Error with get_user_pages: %d bytes, %d pages returned\n",
+ size_in_bytes, itv->udma.page_count);
+
+ /* get_user_pages must have failed completely */
+ return -EIO;
+ }
+
+ IVTVFB_DEBUG_INFO("ivtvfb_prep_dec_dma_to_device, %d bytes, %d pages\n",
+ size_in_bytes, itv->udma.page_count);
+
+ ivtv_udma_prepare(itv);
+ prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
+ /* if no UDMA is pending and no UDMA is in progress, then the DMA
+ is finished */
+ while (test_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags) ||
+ test_bit(IVTV_F_I_UDMA, &itv->i_flags)) {
+ /* don't interrupt if the DMA is in progress but break off
+ a still pending DMA. */
+ got_sig = signal_pending(current);
+ if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags))
+ break;
+ got_sig = 0;
+ schedule();
+ }
+ finish_wait(&itv->dma_waitq, &wait);
+
+ /* Unmap Last DMA Xfer */
+ ivtv_udma_unmap(itv);
+ mutex_unlock(&itv->udma.lock);
+ if (got_sig) {
+ IVTV_DEBUG_INFO("User stopped OSD\n");
+ return -EINTR;
+ }
+
+ return 0;
+}
+
+static int ivtvfb_prep_frame(struct ivtv *itv, int cmd, void __user *source,
+ unsigned long dest_offset, int count)
+{
+ DEFINE_WAIT(wait);
+ struct osd_info *oi = itv->osd_info;
+
+ /* Nothing to do */
+ if (count == 0) {
+ IVTVFB_DEBUG_WARN("ivtvfb_prep_frame: Nothing to do. count = 0\n");
+ return -EINVAL;
+ }
+
+ /* Check Total FB Size */
+ if ((dest_offset + count) > oi->video_buffer_size) {
+ IVTVFB_WARN("ivtvfb_prep_frame: Overflowing the framebuffer %ld, only %d available\n",
+ dest_offset + count, oi->video_buffer_size);
+ return -E2BIG;
+ }
+
+ /* Not fatal, but will have undesirable results */
+ if ((unsigned long)source & 3)
+ IVTVFB_WARN("ivtvfb_prep_frame: Source address not 32 bit aligned (0x%08lx)\n",
+ (unsigned long)source);
+
+ if (dest_offset & 3)
+ IVTVFB_WARN("ivtvfb_prep_frame: Dest offset not 32 bit aligned (%ld)\n", dest_offset);
+
+ if (count & 3)
+ IVTVFB_WARN("ivtvfb_prep_frame: Count not a multiple of 4 (%d)\n", count);
+
+ /* Check Source */
+ if (!access_ok(VERIFY_READ, source + dest_offset, count)) {
+ IVTVFB_WARN("Invalid userspace pointer 0x%08lx\n",
+ (unsigned long)source);
+
+ IVTVFB_DEBUG_WARN("access_ok() failed for offset 0x%08lx source 0x%08lx count %d\n",
+ dest_offset, (unsigned long)source,
+ count);
+ return -EINVAL;
+ }
+
+ /* OSD Address to send DMA to */
+ dest_offset += IVTV_DECODER_OFFSET + oi->video_rbase;
+
+ /* Fill Buffers */
+ return ivtvfb_prep_dec_dma_to_device(itv, dest_offset, source, count);
+}
+
+static ssize_t ivtvfb_write(struct fb_info *info, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ unsigned long p = *ppos;
+ void *dst;
+ int err = 0;
+ int dma_err;
+ unsigned long total_size;
+ struct ivtv *itv = (struct ivtv *) info->par;
+ unsigned long dma_offset =
+ IVTV_DECODER_OFFSET + itv->osd_info->video_rbase;
+ unsigned long dma_size;
+ u16 lead = 0, tail = 0;
+
+ if (info->state != FBINFO_STATE_RUNNING)
+ return -EPERM;
+
+ total_size = info->screen_size;
+
+ if (total_size == 0)
+ total_size = info->fix.smem_len;
+
+ if (p > total_size)
+ return -EFBIG;
+
+ if (count > total_size) {
+ err = -EFBIG;
+ count = total_size;
+ }
+
+ if (count + p > total_size) {
+ if (!err)
+ err = -ENOSPC;
+ count = total_size - p;
+ }
+
+ dst = (void __force *) (info->screen_base + p);
+
+ if (info->fbops->fb_sync)
+ info->fbops->fb_sync(info);
+
+ /* If transfer size > threshold and both src/dst
+ addresses are aligned, use DMA */
+ if (count >= 4096 &&
+ ((unsigned long)buf & 3) == ((unsigned long)dst & 3)) {
+ /* Odd address = can't DMA. Align */
+ if ((unsigned long)dst & 3) {
+ lead = 4 - ((unsigned long)dst & 3);
+ if (copy_from_user(dst, buf, lead))
+ return -EFAULT;
+ buf += lead;
+ dst += lead;
+ }
+ /* DMA resolution is 32 bits */
+ if ((count - lead) & 3)
+ tail = (count - lead) & 3;
+ /* DMA the data */
+ dma_size = count - lead - tail;
+ dma_err = ivtvfb_prep_dec_dma_to_device(itv,
+ p + lead + dma_offset, (void __user *)buf, dma_size);
+ if (dma_err)
+ return dma_err;
+ dst += dma_size;
+ buf += dma_size;
+ /* Copy any leftover data */
+ if (tail && copy_from_user(dst, buf, tail))
+ return -EFAULT;
+ } else if (copy_from_user(dst, buf, count)) {
+ return -EFAULT;
+ }
+
+ if (!err)
+ *ppos += count;
+
+ return (err) ? err : count;
+}
+
+static int ivtvfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
+{
+ DEFINE_WAIT(wait);
+ struct ivtv *itv = (struct ivtv *)info->par;
+ int rc = 0;
+
+ switch (cmd) {
+ case FBIOGET_VBLANK: {
+ struct fb_vblank vblank;
+ u32 trace;
+
+ memset(&vblank, 0, sizeof(struct fb_vblank));
+
+ vblank.flags = FB_VBLANK_HAVE_COUNT |FB_VBLANK_HAVE_VCOUNT |
+ FB_VBLANK_HAVE_VSYNC;
+ trace = read_reg(IVTV_REG_DEC_LINE_FIELD) >> 16;
+ if (itv->is_out_50hz && trace > 312)
+ trace -= 312;
+ else if (itv->is_out_60hz && trace > 262)
+ trace -= 262;
+ if (trace == 1)
+ vblank.flags |= FB_VBLANK_VSYNCING;
+ vblank.count = itv->last_vsync_field;
+ vblank.vcount = trace;
+ vblank.hcount = 0;
+ if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank)))
+ return -EFAULT;
+ return 0;
+ }
+
+ case FBIO_WAITFORVSYNC:
+ prepare_to_wait(&itv->vsync_waitq, &wait, TASK_INTERRUPTIBLE);
+ if (!schedule_timeout(msecs_to_jiffies(50)))
+ rc = -ETIMEDOUT;
+ finish_wait(&itv->vsync_waitq, &wait);
+ return rc;
+
+ case IVTVFB_IOC_DMA_FRAME: {
+ struct ivtvfb_dma_frame args;
+
+ IVTVFB_DEBUG_INFO("IVTVFB_IOC_DMA_FRAME\n");
+ if (copy_from_user(&args, (void __user *)arg, sizeof(args)))
+ return -EFAULT;
+
+ return ivtvfb_prep_frame(itv, cmd, args.source, args.dest_offset, args.count);
+ }
+
+ default:
+ IVTVFB_DEBUG_INFO("Unknown ioctl %08x\n", cmd);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* Framebuffer device handling */
+
+static int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var)
+{
+ struct osd_info *oi = itv->osd_info;
+ struct ivtv_osd_coords ivtv_osd;
+ struct v4l2_rect ivtv_window;
+ int osd_mode = -1;
+
+ IVTVFB_DEBUG_INFO("ivtvfb_set_var\n");
+
+ /* Select color space */
+ if (var->nonstd) /* YUV */
+ write_reg(read_reg(0x02a00) | 0x0002000, 0x02a00);
+ else /* RGB */
+ write_reg(read_reg(0x02a00) & ~0x0002000, 0x02a00);
+
+ /* Set the color mode */
+ switch (var->bits_per_pixel) {
+ case 8:
+ osd_mode = IVTV_OSD_BPP_8;
+ break;
+ case 32:
+ osd_mode = IVTV_OSD_BPP_32;
+ break;
+ case 16:
+ switch (var->green.length) {
+ case 4:
+ osd_mode = IVTV_OSD_BPP_16_444;
+ break;
+ case 5:
+ osd_mode = IVTV_OSD_BPP_16_555;
+ break;
+ case 6:
+ osd_mode = IVTV_OSD_BPP_16_565;
+ break;
+ default:
+ IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n");
+ }
+ break;
+ default:
+ IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n");
+ }
+
+ /* Set video mode. Although rare, the display can become scrambled even
+ if we don't change mode. Always 'bounce' to osd_mode via mode 0 */
+ if (osd_mode != -1) {
+ ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, 0);
+ ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, osd_mode);
+ }
+
+ oi->bits_per_pixel = var->bits_per_pixel;
+ oi->bytes_per_pixel = var->bits_per_pixel / 8;
+
+ /* Set the flicker filter */
+ switch (var->vmode & FB_VMODE_MASK) {
+ case FB_VMODE_NONINTERLACED: /* Filter on */
+ ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 1);
+ break;
+ case FB_VMODE_INTERLACED: /* Filter off */
+ ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 0);
+ break;
+ default:
+ IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid video mode\n");
+ }
+
+ /* Read the current osd info */
+ ivtvfb_get_osd_coords(itv, &ivtv_osd);
+
+ /* Now set the OSD to the size we want */
+ ivtv_osd.pixel_stride = var->xres_virtual;
+ ivtv_osd.lines = var->yres_virtual;
+ ivtv_osd.x = 0;
+ ivtv_osd.y = 0;
+ ivtvfb_set_osd_coords(itv, &ivtv_osd);
+
+ /* Can't seem to find the right API combo for this.
+ Use another function which does what we need through direct register access. */
+ ivtv_window.width = var->xres;
+ ivtv_window.height = var->yres;
+
+ /* Minimum margin cannot be 0, as X won't allow such a mode */
+ if (!var->upper_margin)
+ var->upper_margin++;
+ if (!var->left_margin)
+ var->left_margin++;
+ ivtv_window.top = var->upper_margin - 1;
+ ivtv_window.left = var->left_margin - 1;
+
+ ivtvfb_set_display_window(itv, &ivtv_window);
+
+ /* Pass screen size back to yuv handler */
+ itv->yuv_info.osd_full_w = ivtv_osd.pixel_stride;
+ itv->yuv_info.osd_full_h = ivtv_osd.lines;
+
+ /* Force update of yuv registers */
+ itv->yuv_info.yuv_forced_update = 1;
+
+ /* Keep a copy of these settings */
+ memcpy(&oi->fbvar_cur, var, sizeof(oi->fbvar_cur));
+
+ IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n",
+ var->xres, var->yres,
+ var->xres_virtual, var->yres_virtual,
+ var->bits_per_pixel);
+
+ IVTVFB_DEBUG_INFO("Display position: %d, %d\n",
+ var->left_margin, var->upper_margin);
+
+ IVTVFB_DEBUG_INFO("Display filter: %s\n",
+ (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off");
+ IVTVFB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB");
+
+ return 0;
+}
+
+static int ivtvfb_get_fix(struct ivtv *itv, struct fb_fix_screeninfo *fix)
+{
+ struct osd_info *oi = itv->osd_info;
+
+ IVTVFB_DEBUG_INFO("ivtvfb_get_fix\n");
+ memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+ strlcpy(fix->id, "cx23415 TV out", sizeof(fix->id));
+ fix->smem_start = oi->video_pbase;
+ fix->smem_len = oi->video_buffer_size;
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->visual = (oi->bits_per_pixel == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
+ fix->xpanstep = 1;
+ fix->ypanstep = 1;
+ fix->ywrapstep = 0;
+ fix->line_length = oi->display_byte_stride;
+ fix->accel = FB_ACCEL_NONE;
+ return 0;
+}
+
+/* Check the requested display mode, returning -EINVAL if we can't
+ handle it. */
+
+static int _ivtvfb_check_var(struct fb_var_screeninfo *var, struct ivtv *itv)
+{
+ struct osd_info *oi = itv->osd_info;
+ int osd_height_limit;
+ u32 pixclock, hlimit, vlimit;
+
+ IVTVFB_DEBUG_INFO("ivtvfb_check_var\n");
+
+ /* Set base references for mode calcs. */
+ if (itv->is_out_50hz) {
+ pixclock = 84316;
+ hlimit = 776;
+ vlimit = 591;
+ osd_height_limit = 576;
+ }
+ else {
+ pixclock = 83926;
+ hlimit = 776;
+ vlimit = 495;
+ osd_height_limit = 480;
+ }
+
+ if (var->bits_per_pixel == 8 || var->bits_per_pixel == 32) {
+ var->transp.offset = 24;
+ var->transp.length = 8;
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ }
+ else if (var->bits_per_pixel == 16) {
+ /* To find out the true mode, check green length */
+ switch (var->green.length) {
+ case 4:
+ var->red.offset = 8;
+ var->red.length = 4;
+ var->green.offset = 4;
+ var->green.length = 4;
+ var->blue.offset = 0;
+ var->blue.length = 4;
+ var->transp.offset = 12;
+ var->transp.length = 1;
+ break;
+ case 5:
+ var->red.offset = 10;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 5;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ var->transp.offset = 15;
+ var->transp.length = 1;
+ break;
+ default:
+ var->red.offset = 11;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 6;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ break;
+ }
+ }
+ else {
+ IVTVFB_DEBUG_WARN("Invalid colour mode: %d\n", var->bits_per_pixel);
+ return -EINVAL;
+ }
+
+ /* Check the resolution */
+ if (var->xres > IVTV_OSD_MAX_WIDTH || var->yres > osd_height_limit) {
+ IVTVFB_DEBUG_WARN("Invalid resolution: %dx%d\n",
+ var->xres, var->yres);
+ return -EINVAL;
+ }
+
+ /* Max horizontal size is 1023 @ 32bpp, 2046 & 16bpp, 4092 @ 8bpp */
+ if (var->xres_virtual > 4095 / (var->bits_per_pixel / 8) ||
+ var->xres_virtual * var->yres_virtual * (var->bits_per_pixel / 8) > oi->video_buffer_size ||
+ var->xres_virtual < var->xres ||
+ var->yres_virtual < var->yres) {
+ IVTVFB_DEBUG_WARN("Invalid virtual resolution: %dx%d\n",
+ var->xres_virtual, var->yres_virtual);
+ return -EINVAL;
+ }
+
+ /* Some extra checks if in 8 bit mode */
+ if (var->bits_per_pixel == 8) {
+ /* Width must be a multiple of 4 */
+ if (var->xres & 3) {
+ IVTVFB_DEBUG_WARN("Invalid resolution for 8bpp: %d\n", var->xres);
+ return -EINVAL;
+ }
+ if (var->xres_virtual & 3) {
+ IVTVFB_DEBUG_WARN("Invalid virtual resolution for 8bpp: %d)\n", var->xres_virtual);
+ return -EINVAL;
+ }
+ }
+ else if (var->bits_per_pixel == 16) {
+ /* Width must be a multiple of 2 */
+ if (var->xres & 1) {
+ IVTVFB_DEBUG_WARN("Invalid resolution for 16bpp: %d\n", var->xres);
+ return -EINVAL;
+ }
+ if (var->xres_virtual & 1) {
+ IVTVFB_DEBUG_WARN("Invalid virtual resolution for 16bpp: %d)\n", var->xres_virtual);
+ return -EINVAL;
+ }
+ }
+
+ /* Now check the offsets */
+ if (var->xoffset >= var->xres_virtual || var->yoffset >= var->yres_virtual) {
+ IVTVFB_DEBUG_WARN("Invalid offset: %d (%d) %d (%d)\n",
+ var->xoffset, var->xres_virtual, var->yoffset, var->yres_virtual);
+ return -EINVAL;
+ }
+
+ /* Check pixel format */
+ if (var->nonstd > 1) {
+ IVTVFB_DEBUG_WARN("Invalid nonstd % d\n", var->nonstd);
+ return -EINVAL;
+ }
+
+ /* Check video mode */
+ if (((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) &&
+ ((var->vmode & FB_VMODE_MASK) != FB_VMODE_INTERLACED)) {
+ IVTVFB_DEBUG_WARN("Invalid video mode: %d\n", var->vmode & FB_VMODE_MASK);
+ return -EINVAL;
+ }
+
+ /* Check the left & upper margins
+ If the margins are too large, just center the screen
+ (enforcing margins causes too many problems) */
+
+ if (var->left_margin + var->xres > IVTV_OSD_MAX_WIDTH + 1)
+ var->left_margin = 1 + ((IVTV_OSD_MAX_WIDTH - var->xres) / 2);
+
+ if (var->upper_margin + var->yres > (itv->is_out_50hz ? 577 : 481))
+ var->upper_margin = 1 + (((itv->is_out_50hz ? 576 : 480) -
+ var->yres) / 2);
+
+ /* Maintain overall 'size' for a constant refresh rate */
+ var->right_margin = hlimit - var->left_margin - var->xres;
+ var->lower_margin = vlimit - var->upper_margin - var->yres;
+
+ /* Fixed sync times */
+ var->hsync_len = 24;
+ var->vsync_len = 2;
+
+ /* Non-interlaced / interlaced mode is used to switch the OSD filter
+ on or off. Adjust the clock timings to maintain a constant
+ vertical refresh rate. */
+ if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED)
+ var->pixclock = pixclock / 2;
+ else
+ var->pixclock = pixclock;
+
+ itv->osd_rect.width = var->xres;
+ itv->osd_rect.height = var->yres;
+
+ IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n",
+ var->xres, var->yres,
+ var->xres_virtual, var->yres_virtual,
+ var->bits_per_pixel);
+
+ IVTVFB_DEBUG_INFO("Display position: %d, %d\n",
+ var->left_margin, var->upper_margin);
+
+ IVTVFB_DEBUG_INFO("Display filter: %s\n",
+ (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off");
+ IVTVFB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB");
+ return 0;
+}
+
+static int ivtvfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ struct ivtv *itv = (struct ivtv *) info->par;
+ IVTVFB_DEBUG_INFO("ivtvfb_check_var\n");
+ return _ivtvfb_check_var(var, itv);
+}
+
+static int ivtvfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ u32 osd_pan_index;
+ struct ivtv *itv = (struct ivtv *) info->par;
+
+ if (var->yoffset + info->var.yres > info->var.yres_virtual ||
+ var->xoffset + info->var.xres > info->var.xres_virtual)
+ return -EINVAL;
+
+ osd_pan_index = var->yoffset * info->fix.line_length
+ + var->xoffset * info->var.bits_per_pixel / 8;
+ write_reg(osd_pan_index, 0x02A0C);
+
+ /* Pass this info back the yuv handler */
+ itv->yuv_info.osd_x_pan = var->xoffset;
+ itv->yuv_info.osd_y_pan = var->yoffset;
+ /* Force update of yuv registers */
+ itv->yuv_info.yuv_forced_update = 1;
+ /* Remember this value */
+ itv->osd_info->pan_cur = osd_pan_index;
+ return 0;
+}
+
+static int ivtvfb_set_par(struct fb_info *info)
+{
+ int rc = 0;
+ struct ivtv *itv = (struct ivtv *) info->par;
+
+ IVTVFB_DEBUG_INFO("ivtvfb_set_par\n");
+
+ rc = ivtvfb_set_var(itv, &info->var);
+ ivtvfb_pan_display(&info->var, info);
+ ivtvfb_get_fix(itv, &info->fix);
+ ivtv_firmware_check(itv, "ivtvfb_set_par");
+ return rc;
+}
+
+static int ivtvfb_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp,
+ struct fb_info *info)
+{
+ u32 color, *palette;
+ struct ivtv *itv = (struct ivtv *)info->par;
+
+ if (regno >= info->cmap.len)
+ return -EINVAL;
+
+ color = ((transp & 0xFF00) << 16) |((red & 0xFF00) << 8) | (green & 0xFF00) | ((blue & 0xFF00) >> 8);
+ if (info->var.bits_per_pixel <= 8) {
+ write_reg(regno, 0x02a30);
+ write_reg(color, 0x02a34);
+ itv->osd_info->palette_cur[regno] = color;
+ return 0;
+ }
+ if (regno >= 16)
+ return -EINVAL;
+
+ palette = info->pseudo_palette;
+ if (info->var.bits_per_pixel == 16) {
+ switch (info->var.green.length) {
+ case 4:
+ color = ((red & 0xf000) >> 4) |
+ ((green & 0xf000) >> 8) |
+ ((blue & 0xf000) >> 12);
+ break;
+ case 5:
+ color = ((red & 0xf800) >> 1) |
+ ((green & 0xf800) >> 6) |
+ ((blue & 0xf800) >> 11);
+ break;
+ case 6:
+ color = (red & 0xf800 ) |
+ ((green & 0xfc00) >> 5) |
+ ((blue & 0xf800) >> 11);
+ break;
+ }
+ }
+ palette[regno] = color;
+ return 0;
+}
+
+/* We don't really support blanking. All this does is enable or
+ disable the OSD. */
+static int ivtvfb_blank(int blank_mode, struct fb_info *info)
+{
+ struct ivtv *itv = (struct ivtv *)info->par;
+
+ IVTVFB_DEBUG_INFO("Set blanking mode : %d\n", blank_mode);
+ switch (blank_mode) {
+ case FB_BLANK_UNBLANK:
+ ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 1);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1);
+ break;
+ case FB_BLANK_NORMAL:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_VSYNC_SUSPEND:
+ ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1);
+ break;
+ case FB_BLANK_POWERDOWN:
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0);
+ ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0);
+ break;
+ }
+ itv->osd_info->blank_cur = blank_mode;
+ return 0;
+}
+
+static struct fb_ops ivtvfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_write = ivtvfb_write,
+ .fb_check_var = ivtvfb_check_var,
+ .fb_set_par = ivtvfb_set_par,
+ .fb_setcolreg = ivtvfb_setcolreg,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_cursor = NULL,
+ .fb_ioctl = ivtvfb_ioctl,
+ .fb_pan_display = ivtvfb_pan_display,
+ .fb_blank = ivtvfb_blank,
+};
+
+/* Restore hardware after firmware restart */
+static void ivtvfb_restore(struct ivtv *itv)
+{
+ struct osd_info *oi = itv->osd_info;
+ int i;
+
+ ivtvfb_set_var(itv, &oi->fbvar_cur);
+ ivtvfb_blank(oi->blank_cur, &oi->ivtvfb_info);
+ for (i = 0; i < 256; i++) {
+ write_reg(i, 0x02a30);
+ write_reg(oi->palette_cur[i], 0x02a34);
+ }
+ write_reg(oi->pan_cur, 0x02a0c);
+}
+
+/* Initialization */
+
+
+/* Setup our initial video mode */
+static int ivtvfb_init_vidmode(struct ivtv *itv)
+{
+ struct osd_info *oi = itv->osd_info;
+ struct v4l2_rect start_window;
+ int max_height;
+
+ /* Color mode */
+
+ if (osd_depth != 8 && osd_depth != 16 && osd_depth != 32)
+ osd_depth = 8;
+ oi->bits_per_pixel = osd_depth;
+ oi->bytes_per_pixel = oi->bits_per_pixel / 8;
+
+ /* Horizontal size & position */
+
+ if (osd_xres > 720)
+ osd_xres = 720;
+
+ /* Must be a multiple of 4 for 8bpp & 2 for 16bpp */
+ if (osd_depth == 8)
+ osd_xres &= ~3;
+ else if (osd_depth == 16)
+ osd_xres &= ~1;
+
+ start_window.width = osd_xres ? osd_xres : 640;
+
+ /* Check horizontal start (osd_left). */
+ if (osd_left && osd_left + start_window.width > 721) {
+ IVTVFB_ERR("Invalid osd_left - assuming default\n");
+ osd_left = 0;
+ }
+
+ /* Hardware coords start at 0, user coords start at 1. */
+ osd_left--;
+
+ start_window.left = osd_left >= 0 ?
+ osd_left : ((IVTV_OSD_MAX_WIDTH - start_window.width) / 2);
+
+ oi->display_byte_stride =
+ start_window.width * oi->bytes_per_pixel;
+
+ /* Vertical size & position */
+
+ max_height = itv->is_out_50hz ? 576 : 480;
+
+ if (osd_yres > max_height)
+ osd_yres = max_height;
+
+ start_window.height = osd_yres ?
+ osd_yres : itv->is_out_50hz ? 480 : 400;
+
+ /* Check vertical start (osd_upper). */
+ if (osd_upper + start_window.height > max_height + 1) {
+ IVTVFB_ERR("Invalid osd_upper - assuming default\n");
+ osd_upper = 0;
+ }
+
+ /* Hardware coords start at 0, user coords start at 1. */
+ osd_upper--;
+
+ start_window.top = osd_upper >= 0 ? osd_upper : ((max_height - start_window.height) / 2);
+
+ oi->display_width = start_window.width;
+ oi->display_height = start_window.height;
+
+ /* Generate a valid fb_var_screeninfo */
+
+ oi->ivtvfb_defined.xres = oi->display_width;
+ oi->ivtvfb_defined.yres = oi->display_height;
+ oi->ivtvfb_defined.xres_virtual = oi->display_width;
+ oi->ivtvfb_defined.yres_virtual = oi->display_height;
+ oi->ivtvfb_defined.bits_per_pixel = oi->bits_per_pixel;
+ oi->ivtvfb_defined.vmode = (osd_laced ? FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED);
+ oi->ivtvfb_defined.left_margin = start_window.left + 1;
+ oi->ivtvfb_defined.upper_margin = start_window.top + 1;
+ oi->ivtvfb_defined.accel_flags = FB_ACCEL_NONE;
+ oi->ivtvfb_defined.nonstd = 0;
+
+ /* We've filled in the most data, let the usual mode check
+ routine fill in the rest. */
+ _ivtvfb_check_var(&oi->ivtvfb_defined, itv);
+
+ /* Generate valid fb_fix_screeninfo */
+
+ ivtvfb_get_fix(itv, &oi->ivtvfb_fix);
+
+ /* Generate valid fb_info */
+
+ oi->ivtvfb_info.node = -1;
+ oi->ivtvfb_info.flags = FBINFO_FLAG_DEFAULT;
+ oi->ivtvfb_info.fbops = &ivtvfb_ops;
+ oi->ivtvfb_info.par = itv;
+ oi->ivtvfb_info.var = oi->ivtvfb_defined;
+ oi->ivtvfb_info.fix = oi->ivtvfb_fix;
+ oi->ivtvfb_info.screen_base = (u8 __iomem *)oi->video_vbase;
+ oi->ivtvfb_info.fbops = &ivtvfb_ops;
+
+ /* Supply some monitor specs. Bogus values will do for now */
+ oi->ivtvfb_info.monspecs.hfmin = 8000;
+ oi->ivtvfb_info.monspecs.hfmax = 70000;
+ oi->ivtvfb_info.monspecs.vfmin = 10;
+ oi->ivtvfb_info.monspecs.vfmax = 100;
+
+ /* Allocate color map */
+ if (fb_alloc_cmap(&oi->ivtvfb_info.cmap, 256, 1)) {
+ IVTVFB_ERR("abort, unable to alloc cmap\n");
+ return -ENOMEM;
+ }
+
+ /* Allocate the pseudo palette */
+ oi->ivtvfb_info.pseudo_palette =
+ kmalloc(sizeof(u32) * 16, GFP_KERNEL|__GFP_NOWARN);
+
+ if (!oi->ivtvfb_info.pseudo_palette) {
+ IVTVFB_ERR("abort, unable to alloc pseudo palette\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/* Find OSD buffer base & size. Add to mtrr. Zero osd buffer. */
+
+static int ivtvfb_init_io(struct ivtv *itv)
+{
+ struct osd_info *oi = itv->osd_info;
+
+ mutex_lock(&itv->serialize_lock);
+ if (ivtv_init_on_first_open(itv)) {
+ mutex_unlock(&itv->serialize_lock);
+ IVTVFB_ERR("Failed to initialize ivtv\n");
+ return -ENXIO;
+ }
+ mutex_unlock(&itv->serialize_lock);
+
+ if (ivtvfb_get_framebuffer(itv, &oi->video_rbase,
+ &oi->video_buffer_size) < 0) {
+ IVTVFB_ERR("Firmware failed to respond\n");
+ return -EIO;
+ }
+
+ /* The osd buffer size depends on the number of video buffers allocated
+ on the PVR350 itself. For now we'll hardcode the smallest osd buffer
+ size to prevent any overlap. */
+ oi->video_buffer_size = 1704960;
+
+ oi->video_pbase = itv->base_addr + IVTV_DECODER_OFFSET + oi->video_rbase;
+ oi->video_vbase = itv->dec_mem + oi->video_rbase;
+
+ if (!oi->video_vbase) {
+ IVTVFB_ERR("abort, video memory 0x%x @ 0x%lx isn't mapped!\n",
+ oi->video_buffer_size, oi->video_pbase);
+ return -EIO;
+ }
+
+ IVTVFB_INFO("Framebuffer at 0x%lx, mapped to 0x%p, size %dk\n",
+ oi->video_pbase, oi->video_vbase,
+ oi->video_buffer_size / 1024);
+
+#ifdef CONFIG_MTRR
+ {
+ /* Find the largest power of two that maps the whole buffer */
+ int size_shift = 31;
+
+ while (!(oi->video_buffer_size & (1 << size_shift))) {
+ size_shift--;
+ }
+ size_shift++;
+ oi->fb_start_aligned_physaddr = oi->video_pbase & ~((1 << size_shift) - 1);
+ oi->fb_end_aligned_physaddr = oi->video_pbase + oi->video_buffer_size;
+ oi->fb_end_aligned_physaddr += (1 << size_shift) - 1;
+ oi->fb_end_aligned_physaddr &= ~((1 << size_shift) - 1);
+ if (mtrr_add(oi->fb_start_aligned_physaddr,
+ oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr,
+ MTRR_TYPE_WRCOMB, 1) < 0) {
+ IVTVFB_INFO("disabled mttr\n");
+ oi->fb_start_aligned_physaddr = 0;
+ oi->fb_end_aligned_physaddr = 0;
+ }
+ }
+#endif
+
+ /* Blank the entire osd. */
+ memset_io(oi->video_vbase, 0, oi->video_buffer_size);
+
+ return 0;
+}
+
+/* Release any memory we've grabbed & remove mtrr entry */
+static void ivtvfb_release_buffers (struct ivtv *itv)
+{
+ struct osd_info *oi = itv->osd_info;
+
+ /* Release cmap */
+ if (oi->ivtvfb_info.cmap.len)
+ fb_dealloc_cmap(&oi->ivtvfb_info.cmap);
+
+ /* Release pseudo palette */
+ if (oi->ivtvfb_info.pseudo_palette)
+ kfree(oi->ivtvfb_info.pseudo_palette);
+
+#ifdef CONFIG_MTRR
+ if (oi->fb_end_aligned_physaddr) {
+ mtrr_del(-1, oi->fb_start_aligned_physaddr,
+ oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr);
+ }
+#endif
+
+ kfree(oi);
+ itv->osd_info = NULL;
+}
+
+/* Initialize the specified card */
+
+static int ivtvfb_init_card(struct ivtv *itv)
+{
+ int rc;
+
+ if (itv->osd_info) {
+ IVTVFB_ERR("Card %d already initialised\n", ivtvfb_card_id);
+ return -EBUSY;
+ }
+
+ itv->osd_info = kzalloc(sizeof(struct osd_info),
+ GFP_ATOMIC|__GFP_NOWARN);
+ if (itv->osd_info == NULL) {
+ IVTVFB_ERR("Failed to allocate memory for osd_info\n");
+ return -ENOMEM;
+ }
+
+ /* Find & setup the OSD buffer */
+ rc = ivtvfb_init_io(itv);
+ if (rc) {
+ ivtvfb_release_buffers(itv);
+ return rc;
+ }
+
+ /* Set the startup video mode information */
+ if ((rc = ivtvfb_init_vidmode(itv))) {
+ ivtvfb_release_buffers(itv);
+ return rc;
+ }
+
+ /* Register the framebuffer */
+ if (register_framebuffer(&itv->osd_info->ivtvfb_info) < 0) {
+ ivtvfb_release_buffers(itv);
+ return -EINVAL;
+ }
+
+ itv->osd_video_pbase = itv->osd_info->video_pbase;
+
+ /* Set the card to the requested mode */
+ ivtvfb_set_par(&itv->osd_info->ivtvfb_info);
+
+ /* Set color 0 to black */
+ write_reg(0, 0x02a30);
+ write_reg(0, 0x02a34);
+
+ /* Enable the osd */
+ ivtvfb_blank(FB_BLANK_UNBLANK, &itv->osd_info->ivtvfb_info);
+
+ /* Enable restart */
+ itv->ivtvfb_restore = ivtvfb_restore;
+
+ /* Allocate DMA */
+ ivtv_udma_alloc(itv);
+ return 0;
+
+}
+
+static int __init ivtvfb_callback_init(struct device *dev, void *p)
+{
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
+ struct ivtv *itv = container_of(v4l2_dev, struct ivtv, v4l2_dev);
+
+ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
+ if (ivtvfb_init_card(itv) == 0) {
+ IVTVFB_INFO("Framebuffer registered on %s\n",
+ itv->v4l2_dev.name);
+ (*(int *)p)++;
+ }
+ }
+ return 0;
+}
+
+static int ivtvfb_callback_cleanup(struct device *dev, void *p)
+{
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
+ struct ivtv *itv = container_of(v4l2_dev, struct ivtv, v4l2_dev);
+ struct osd_info *oi = itv->osd_info;
+
+ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
+ if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) {
+ IVTVFB_WARN("Framebuffer %d is in use, cannot unload\n",
+ itv->instance);
+ return 0;
+ }
+ IVTVFB_INFO("Unregister framebuffer %d\n", itv->instance);
+ itv->ivtvfb_restore = NULL;
+ ivtvfb_blank(FB_BLANK_VSYNC_SUSPEND, &oi->ivtvfb_info);
+ ivtvfb_release_buffers(itv);
+ itv->osd_video_pbase = 0;
+ }
+ return 0;
+}
+
+static int __init ivtvfb_init(void)
+{
+ struct device_driver *drv;
+ int registered = 0;
+ int err;
+
+ if (ivtvfb_card_id < -1 || ivtvfb_card_id >= IVTV_MAX_CARDS) {
+ printk(KERN_ERR "ivtvfb: ivtvfb_card_id parameter is out of range (valid range: -1 - %d)\n",
+ IVTV_MAX_CARDS - 1);
+ return -EINVAL;
+ }
+
+ drv = driver_find("ivtv", &pci_bus_type);
+ err = driver_for_each_device(drv, NULL, &registered, ivtvfb_callback_init);
+ if (!registered) {
+ printk(KERN_ERR "ivtvfb: no cards found\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static void ivtvfb_cleanup(void)
+{
+ struct device_driver *drv;
+ int err;
+
+ printk(KERN_INFO "ivtvfb: Unloading framebuffer module\n");
+
+ drv = driver_find("ivtv", &pci_bus_type);
+ err = driver_for_each_device(drv, NULL, NULL, ivtvfb_callback_cleanup);
+}
+
+module_init(ivtvfb_init);
+module_exit(ivtvfb_cleanup);
diff --git a/drivers/media/video/ks0127.c b/drivers/media/video/ks0127.c
new file mode 100644
index 00000000..ee7ca2dc
--- /dev/null
+++ b/drivers/media/video/ks0127.c
@@ -0,0 +1,724 @@
+/*
+ * Video Capture Driver (Video for Linux 1/2)
+ * for the Matrox Marvel G200,G400 and Rainbow Runner-G series
+ *
+ * This module is an interface to the KS0127 video decoder chip.
+ *
+ * Copyright (C) 1999 Ryan Drake <stiletto@mediaone.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *****************************************************************************
+ *
+ * Modified and extended by
+ * Mike Bernson <mike@mlb.org>
+ * Gerard v.d. Horst
+ * Leon van Stuivenberg <l.vanstuivenberg@chello.nl>
+ * Gernot Ziegler <gz@lysator.liu.se>
+ *
+ * Version History:
+ * V1.0 Ryan Drake Initial version by Ryan Drake
+ * V1.1 Gerard v.d. Horst Added some debugoutput, reset the video-standard
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include "ks0127.h"
+
+MODULE_DESCRIPTION("KS0127 video decoder driver");
+MODULE_AUTHOR("Ryan Drake");
+MODULE_LICENSE("GPL");
+
+/* Addresses */
+#define I2C_KS0127_ADDON 0xD8
+#define I2C_KS0127_ONBOARD 0xDA
+
+
+/* ks0127 control registers */
+#define KS_STAT 0x00
+#define KS_CMDA 0x01
+#define KS_CMDB 0x02
+#define KS_CMDC 0x03
+#define KS_CMDD 0x04
+#define KS_HAVB 0x05
+#define KS_HAVE 0x06
+#define KS_HS1B 0x07
+#define KS_HS1E 0x08
+#define KS_HS2B 0x09
+#define KS_HS2E 0x0a
+#define KS_AGC 0x0b
+#define KS_HXTRA 0x0c
+#define KS_CDEM 0x0d
+#define KS_PORTAB 0x0e
+#define KS_LUMA 0x0f
+#define KS_CON 0x10
+#define KS_BRT 0x11
+#define KS_CHROMA 0x12
+#define KS_CHROMB 0x13
+#define KS_DEMOD 0x14
+#define KS_SAT 0x15
+#define KS_HUE 0x16
+#define KS_VERTIA 0x17
+#define KS_VERTIB 0x18
+#define KS_VERTIC 0x19
+#define KS_HSCLL 0x1a
+#define KS_HSCLH 0x1b
+#define KS_VSCLL 0x1c
+#define KS_VSCLH 0x1d
+#define KS_OFMTA 0x1e
+#define KS_OFMTB 0x1f
+#define KS_VBICTL 0x20
+#define KS_CCDAT2 0x21
+#define KS_CCDAT1 0x22
+#define KS_VBIL30 0x23
+#define KS_VBIL74 0x24
+#define KS_VBIL118 0x25
+#define KS_VBIL1512 0x26
+#define KS_TTFRAM 0x27
+#define KS_TESTA 0x28
+#define KS_UVOFFH 0x29
+#define KS_UVOFFL 0x2a
+#define KS_UGAIN 0x2b
+#define KS_VGAIN 0x2c
+#define KS_VAVB 0x2d
+#define KS_VAVE 0x2e
+#define KS_CTRACK 0x2f
+#define KS_POLCTL 0x30
+#define KS_REFCOD 0x31
+#define KS_INVALY 0x32
+#define KS_INVALU 0x33
+#define KS_INVALV 0x34
+#define KS_UNUSEY 0x35
+#define KS_UNUSEU 0x36
+#define KS_UNUSEV 0x37
+#define KS_USRSAV 0x38
+#define KS_USREAV 0x39
+#define KS_SHS1A 0x3a
+#define KS_SHS1B 0x3b
+#define KS_SHS1C 0x3c
+#define KS_CMDE 0x3d
+#define KS_VSDEL 0x3e
+#define KS_CMDF 0x3f
+#define KS_GAMMA0 0x40
+#define KS_GAMMA1 0x41
+#define KS_GAMMA2 0x42
+#define KS_GAMMA3 0x43
+#define KS_GAMMA4 0x44
+#define KS_GAMMA5 0x45
+#define KS_GAMMA6 0x46
+#define KS_GAMMA7 0x47
+#define KS_GAMMA8 0x48
+#define KS_GAMMA9 0x49
+#define KS_GAMMA10 0x4a
+#define KS_GAMMA11 0x4b
+#define KS_GAMMA12 0x4c
+#define KS_GAMMA13 0x4d
+#define KS_GAMMA14 0x4e
+#define KS_GAMMA15 0x4f
+#define KS_GAMMA16 0x50
+#define KS_GAMMA17 0x51
+#define KS_GAMMA18 0x52
+#define KS_GAMMA19 0x53
+#define KS_GAMMA20 0x54
+#define KS_GAMMA21 0x55
+#define KS_GAMMA22 0x56
+#define KS_GAMMA23 0x57
+#define KS_GAMMA24 0x58
+#define KS_GAMMA25 0x59
+#define KS_GAMMA26 0x5a
+#define KS_GAMMA27 0x5b
+#define KS_GAMMA28 0x5c
+#define KS_GAMMA29 0x5d
+#define KS_GAMMA30 0x5e
+#define KS_GAMMA31 0x5f
+#define KS_GAMMAD0 0x60
+#define KS_GAMMAD1 0x61
+#define KS_GAMMAD2 0x62
+#define KS_GAMMAD3 0x63
+#define KS_GAMMAD4 0x64
+#define KS_GAMMAD5 0x65
+#define KS_GAMMAD6 0x66
+#define KS_GAMMAD7 0x67
+#define KS_GAMMAD8 0x68
+#define KS_GAMMAD9 0x69
+#define KS_GAMMAD10 0x6a
+#define KS_GAMMAD11 0x6b
+#define KS_GAMMAD12 0x6c
+#define KS_GAMMAD13 0x6d
+#define KS_GAMMAD14 0x6e
+#define KS_GAMMAD15 0x6f
+#define KS_GAMMAD16 0x70
+#define KS_GAMMAD17 0x71
+#define KS_GAMMAD18 0x72
+#define KS_GAMMAD19 0x73
+#define KS_GAMMAD20 0x74
+#define KS_GAMMAD21 0x75
+#define KS_GAMMAD22 0x76
+#define KS_GAMMAD23 0x77
+#define KS_GAMMAD24 0x78
+#define KS_GAMMAD25 0x79
+#define KS_GAMMAD26 0x7a
+#define KS_GAMMAD27 0x7b
+#define KS_GAMMAD28 0x7c
+#define KS_GAMMAD29 0x7d
+#define KS_GAMMAD30 0x7e
+#define KS_GAMMAD31 0x7f
+
+
+/****************************************************************************
+* mga_dev : represents one ks0127 chip.
+****************************************************************************/
+
+struct adjust {
+ int contrast;
+ int bright;
+ int hue;
+ int ugain;
+ int vgain;
+};
+
+struct ks0127 {
+ struct v4l2_subdev sd;
+ v4l2_std_id norm;
+ int ident;
+ u8 regs[256];
+};
+
+static inline struct ks0127 *to_ks0127(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct ks0127, sd);
+}
+
+
+static int debug; /* insmod parameter */
+
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug output");
+
+static u8 reg_defaults[64];
+
+static void init_reg_defaults(void)
+{
+ static int initialized;
+ u8 *table = reg_defaults;
+
+ if (initialized)
+ return;
+ initialized = 1;
+
+ table[KS_CMDA] = 0x2c; /* VSE=0, CCIR 601, autodetect standard */
+ table[KS_CMDB] = 0x12; /* VALIGN=0, AGC control and input */
+ table[KS_CMDC] = 0x00; /* Test options */
+ /* clock & input select, write 1 to PORTA */
+ table[KS_CMDD] = 0x01;
+ table[KS_HAVB] = 0x00; /* HAV Start Control */
+ table[KS_HAVE] = 0x00; /* HAV End Control */
+ table[KS_HS1B] = 0x10; /* HS1 Start Control */
+ table[KS_HS1E] = 0x00; /* HS1 End Control */
+ table[KS_HS2B] = 0x00; /* HS2 Start Control */
+ table[KS_HS2E] = 0x00; /* HS2 End Control */
+ table[KS_AGC] = 0x53; /* Manual setting for AGC */
+ table[KS_HXTRA] = 0x00; /* Extra Bits for HAV and HS1/2 */
+ table[KS_CDEM] = 0x00; /* Chroma Demodulation Control */
+ table[KS_PORTAB] = 0x0f; /* port B is input, port A output GPPORT */
+ table[KS_LUMA] = 0x01; /* Luma control */
+ table[KS_CON] = 0x00; /* Contrast Control */
+ table[KS_BRT] = 0x00; /* Brightness Control */
+ table[KS_CHROMA] = 0x2a; /* Chroma control A */
+ table[KS_CHROMB] = 0x90; /* Chroma control B */
+ table[KS_DEMOD] = 0x00; /* Chroma Demodulation Control & Status */
+ table[KS_SAT] = 0x00; /* Color Saturation Control*/
+ table[KS_HUE] = 0x00; /* Hue Control */
+ table[KS_VERTIA] = 0x00; /* Vertical Processing Control A */
+ /* Vertical Processing Control B, luma 1 line delayed */
+ table[KS_VERTIB] = 0x12;
+ table[KS_VERTIC] = 0x0b; /* Vertical Processing Control C */
+ table[KS_HSCLL] = 0x00; /* Horizontal Scaling Ratio Low */
+ table[KS_HSCLH] = 0x00; /* Horizontal Scaling Ratio High */
+ table[KS_VSCLL] = 0x00; /* Vertical Scaling Ratio Low */
+ table[KS_VSCLH] = 0x00; /* Vertical Scaling Ratio High */
+ /* 16 bit YCbCr 4:2:2 output; I can't make the bt866 like 8 bit /Sam */
+ table[KS_OFMTA] = 0x30;
+ table[KS_OFMTB] = 0x00; /* Output Control B */
+ /* VBI Decoder Control; 4bit fmt: avoid Y overflow */
+ table[KS_VBICTL] = 0x5d;
+ table[KS_CCDAT2] = 0x00; /* Read Only register */
+ table[KS_CCDAT1] = 0x00; /* Read Only register */
+ table[KS_VBIL30] = 0xa8; /* VBI data decoding options */
+ table[KS_VBIL74] = 0xaa; /* VBI data decoding options */
+ table[KS_VBIL118] = 0x2a; /* VBI data decoding options */
+ table[KS_VBIL1512] = 0x00; /* VBI data decoding options */
+ table[KS_TTFRAM] = 0x00; /* Teletext frame alignment pattern */
+ table[KS_TESTA] = 0x00; /* test register, shouldn't be written */
+ table[KS_UVOFFH] = 0x00; /* UV Offset Adjustment High */
+ table[KS_UVOFFL] = 0x00; /* UV Offset Adjustment Low */
+ table[KS_UGAIN] = 0x00; /* U Component Gain Adjustment */
+ table[KS_VGAIN] = 0x00; /* V Component Gain Adjustment */
+ table[KS_VAVB] = 0x07; /* VAV Begin */
+ table[KS_VAVE] = 0x00; /* VAV End */
+ table[KS_CTRACK] = 0x00; /* Chroma Tracking Control */
+ table[KS_POLCTL] = 0x41; /* Timing Signal Polarity Control */
+ table[KS_REFCOD] = 0x80; /* Reference Code Insertion Control */
+ table[KS_INVALY] = 0x10; /* Invalid Y Code */
+ table[KS_INVALU] = 0x80; /* Invalid U Code */
+ table[KS_INVALV] = 0x80; /* Invalid V Code */
+ table[KS_UNUSEY] = 0x10; /* Unused Y Code */
+ table[KS_UNUSEU] = 0x80; /* Unused U Code */
+ table[KS_UNUSEV] = 0x80; /* Unused V Code */
+ table[KS_USRSAV] = 0x00; /* reserved */
+ table[KS_USREAV] = 0x00; /* reserved */
+ table[KS_SHS1A] = 0x00; /* User Defined SHS1 A */
+ /* User Defined SHS1 B, ALT656=1 on 0127B */
+ table[KS_SHS1B] = 0x80;
+ table[KS_SHS1C] = 0x00; /* User Defined SHS1 C */
+ table[KS_CMDE] = 0x00; /* Command Register E */
+ table[KS_VSDEL] = 0x00; /* VS Delay Control */
+ /* Command Register F, update -immediately- */
+ /* (there might come no vsync)*/
+ table[KS_CMDF] = 0x02;
+}
+
+
+/* We need to manually read because of a bug in the KS0127 chip.
+ *
+ * An explanation from kayork@mail.utexas.edu:
+ *
+ * During I2C reads, the KS0127 only samples for a stop condition
+ * during the place where the acknowledge bit should be. Any standard
+ * I2C implementation (correctly) throws in another clock transition
+ * at the 9th bit, and the KS0127 will not recognize the stop condition
+ * and will continue to clock out data.
+ *
+ * So we have to do the read ourself. Big deal.
+ * workaround in i2c-algo-bit
+ */
+
+
+static u8 ks0127_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ char val = 0;
+ struct i2c_msg msgs[] = {
+ { client->addr, 0, sizeof(reg), &reg },
+ { client->addr, I2C_M_RD | I2C_M_NO_RD_ACK, sizeof(val), &val }
+ };
+ int ret;
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs))
+ v4l2_dbg(1, debug, sd, "read error\n");
+
+ return val;
+}
+
+
+static void ks0127_write(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ks0127 *ks = to_ks0127(sd);
+ char msg[] = { reg, val };
+
+ if (i2c_master_send(client, msg, sizeof(msg)) != sizeof(msg))
+ v4l2_dbg(1, debug, sd, "write error\n");
+
+ ks->regs[reg] = val;
+}
+
+
+/* generic bit-twiddling */
+static void ks0127_and_or(struct v4l2_subdev *sd, u8 reg, u8 and_v, u8 or_v)
+{
+ struct ks0127 *ks = to_ks0127(sd);
+
+ u8 val = ks->regs[reg];
+ val = (val & and_v) | or_v;
+ ks0127_write(sd, reg, val);
+}
+
+
+
+/****************************************************************************
+* ks0127 private api
+****************************************************************************/
+static void ks0127_init(struct v4l2_subdev *sd)
+{
+ struct ks0127 *ks = to_ks0127(sd);
+ u8 *table = reg_defaults;
+ int i;
+
+ ks->ident = V4L2_IDENT_KS0127;
+
+ v4l2_dbg(1, debug, sd, "reset\n");
+ msleep(1);
+
+ /* initialize all registers to known values */
+ /* (except STAT, 0x21, 0x22, TEST and 0x38,0x39) */
+
+ for (i = 1; i < 33; i++)
+ ks0127_write(sd, i, table[i]);
+
+ for (i = 35; i < 40; i++)
+ ks0127_write(sd, i, table[i]);
+
+ for (i = 41; i < 56; i++)
+ ks0127_write(sd, i, table[i]);
+
+ for (i = 58; i < 64; i++)
+ ks0127_write(sd, i, table[i]);
+
+
+ if ((ks0127_read(sd, KS_STAT) & 0x80) == 0) {
+ ks->ident = V4L2_IDENT_KS0122S;
+ v4l2_dbg(1, debug, sd, "ks0122s found\n");
+ return;
+ }
+
+ switch (ks0127_read(sd, KS_CMDE) & 0x0f) {
+ case 0:
+ v4l2_dbg(1, debug, sd, "ks0127 found\n");
+ break;
+
+ case 9:
+ ks->ident = V4L2_IDENT_KS0127B;
+ v4l2_dbg(1, debug, sd, "ks0127B Revision A found\n");
+ break;
+
+ default:
+ v4l2_dbg(1, debug, sd, "unknown revision\n");
+ break;
+ }
+}
+
+static int ks0127_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct ks0127 *ks = to_ks0127(sd);
+
+ switch (input) {
+ case KS_INPUT_COMPOSITE_1:
+ case KS_INPUT_COMPOSITE_2:
+ case KS_INPUT_COMPOSITE_3:
+ case KS_INPUT_COMPOSITE_4:
+ case KS_INPUT_COMPOSITE_5:
+ case KS_INPUT_COMPOSITE_6:
+ v4l2_dbg(1, debug, sd,
+ "s_routing %d: Composite\n", input);
+ /* autodetect 50/60 Hz */
+ ks0127_and_or(sd, KS_CMDA, 0xfc, 0x00);
+ /* VSE=0 */
+ ks0127_and_or(sd, KS_CMDA, ~0x40, 0x00);
+ /* set input line */
+ ks0127_and_or(sd, KS_CMDB, 0xb0, input);
+ /* non-freerunning mode */
+ ks0127_and_or(sd, KS_CMDC, 0x70, 0x0a);
+ /* analog input */
+ ks0127_and_or(sd, KS_CMDD, 0x03, 0x00);
+ /* enable chroma demodulation */
+ ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x00);
+ /* chroma trap, HYBWR=1 */
+ ks0127_and_or(sd, KS_LUMA, 0x00,
+ (reg_defaults[KS_LUMA])|0x0c);
+ /* scaler fullbw, luma comb off */
+ ks0127_and_or(sd, KS_VERTIA, 0x08, 0x81);
+ /* manual chroma comb .25 .5 .25 */
+ ks0127_and_or(sd, KS_VERTIC, 0x0f, 0x90);
+
+ /* chroma path delay */
+ ks0127_and_or(sd, KS_CHROMB, 0x0f, 0x90);
+
+ ks0127_write(sd, KS_UGAIN, reg_defaults[KS_UGAIN]);
+ ks0127_write(sd, KS_VGAIN, reg_defaults[KS_VGAIN]);
+ ks0127_write(sd, KS_UVOFFH, reg_defaults[KS_UVOFFH]);
+ ks0127_write(sd, KS_UVOFFL, reg_defaults[KS_UVOFFL]);
+ break;
+
+ case KS_INPUT_SVIDEO_1:
+ case KS_INPUT_SVIDEO_2:
+ case KS_INPUT_SVIDEO_3:
+ v4l2_dbg(1, debug, sd,
+ "s_routing %d: S-Video\n", input);
+ /* autodetect 50/60 Hz */
+ ks0127_and_or(sd, KS_CMDA, 0xfc, 0x00);
+ /* VSE=0 */
+ ks0127_and_or(sd, KS_CMDA, ~0x40, 0x00);
+ /* set input line */
+ ks0127_and_or(sd, KS_CMDB, 0xb0, input);
+ /* non-freerunning mode */
+ ks0127_and_or(sd, KS_CMDC, 0x70, 0x0a);
+ /* analog input */
+ ks0127_and_or(sd, KS_CMDD, 0x03, 0x00);
+ /* enable chroma demodulation */
+ ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x00);
+ ks0127_and_or(sd, KS_LUMA, 0x00,
+ reg_defaults[KS_LUMA]);
+ /* disable luma comb */
+ ks0127_and_or(sd, KS_VERTIA, 0x08,
+ (reg_defaults[KS_VERTIA]&0xf0)|0x01);
+ ks0127_and_or(sd, KS_VERTIC, 0x0f,
+ reg_defaults[KS_VERTIC]&0xf0);
+
+ ks0127_and_or(sd, KS_CHROMB, 0x0f,
+ reg_defaults[KS_CHROMB]&0xf0);
+
+ ks0127_write(sd, KS_UGAIN, reg_defaults[KS_UGAIN]);
+ ks0127_write(sd, KS_VGAIN, reg_defaults[KS_VGAIN]);
+ ks0127_write(sd, KS_UVOFFH, reg_defaults[KS_UVOFFH]);
+ ks0127_write(sd, KS_UVOFFL, reg_defaults[KS_UVOFFL]);
+ break;
+
+ case KS_INPUT_YUV656:
+ v4l2_dbg(1, debug, sd, "s_routing 15: YUV656\n");
+ if (ks->norm & V4L2_STD_525_60)
+ /* force 60 Hz */
+ ks0127_and_or(sd, KS_CMDA, 0xfc, 0x03);
+ else
+ /* force 50 Hz */
+ ks0127_and_or(sd, KS_CMDA, 0xfc, 0x02);
+
+ ks0127_and_or(sd, KS_CMDA, 0xff, 0x40); /* VSE=1 */
+ /* set input line and VALIGN */
+ ks0127_and_or(sd, KS_CMDB, 0xb0, (input | 0x40));
+ /* freerunning mode, */
+ /* TSTGEN = 1 TSTGFR=11 TSTGPH=0 TSTGPK=0 VMEM=1*/
+ ks0127_and_or(sd, KS_CMDC, 0x70, 0x87);
+ /* digital input, SYNDIR = 0 INPSL=01 CLKDIR=0 EAV=0 */
+ ks0127_and_or(sd, KS_CMDD, 0x03, 0x08);
+ /* disable chroma demodulation */
+ ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x30);
+ /* HYPK =01 CTRAP = 0 HYBWR=0 PED=1 RGBH=1 UNIT=1 */
+ ks0127_and_or(sd, KS_LUMA, 0x00, 0x71);
+ ks0127_and_or(sd, KS_VERTIC, 0x0f,
+ reg_defaults[KS_VERTIC]&0xf0);
+
+ /* scaler fullbw, luma comb off */
+ ks0127_and_or(sd, KS_VERTIA, 0x08, 0x81);
+
+ ks0127_and_or(sd, KS_CHROMB, 0x0f,
+ reg_defaults[KS_CHROMB]&0xf0);
+
+ ks0127_and_or(sd, KS_CON, 0x00, 0x00);
+ ks0127_and_or(sd, KS_BRT, 0x00, 32); /* spec: 34 */
+ /* spec: 229 (e5) */
+ ks0127_and_or(sd, KS_SAT, 0x00, 0xe8);
+ ks0127_and_or(sd, KS_HUE, 0x00, 0);
+
+ ks0127_and_or(sd, KS_UGAIN, 0x00, 238);
+ ks0127_and_or(sd, KS_VGAIN, 0x00, 0x00);
+
+ /*UOFF:0x30, VOFF:0x30, TSTCGN=1 */
+ ks0127_and_or(sd, KS_UVOFFH, 0x00, 0x4f);
+ ks0127_and_or(sd, KS_UVOFFL, 0x00, 0x00);
+ break;
+
+ default:
+ v4l2_dbg(1, debug, sd,
+ "s_routing: Unknown input %d\n", input);
+ break;
+ }
+
+ /* hack: CDMLPF sometimes spontaneously switches on; */
+ /* force back off */
+ ks0127_write(sd, KS_DEMOD, reg_defaults[KS_DEMOD]);
+ return 0;
+}
+
+static int ks0127_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct ks0127 *ks = to_ks0127(sd);
+
+ /* Set to automatic SECAM/Fsc mode */
+ ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x00);
+
+ ks->norm = std;
+ if (std & V4L2_STD_NTSC) {
+ v4l2_dbg(1, debug, sd,
+ "s_std: NTSC_M\n");
+ ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x20);
+ } else if (std & V4L2_STD_PAL_N) {
+ v4l2_dbg(1, debug, sd,
+ "s_std: NTSC_N (fixme)\n");
+ ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x40);
+ } else if (std & V4L2_STD_PAL) {
+ v4l2_dbg(1, debug, sd,
+ "s_std: PAL_N\n");
+ ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x20);
+ } else if (std & V4L2_STD_PAL_M) {
+ v4l2_dbg(1, debug, sd,
+ "s_std: PAL_M (fixme)\n");
+ ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x40);
+ } else if (std & V4L2_STD_SECAM) {
+ v4l2_dbg(1, debug, sd,
+ "s_std: SECAM\n");
+
+ /* set to secam autodetection */
+ ks0127_and_or(sd, KS_CHROMA, 0xdf, 0x20);
+ ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x00);
+ schedule_timeout_interruptible(HZ/10+1);
+
+ /* did it autodetect? */
+ if (!(ks0127_read(sd, KS_DEMOD) & 0x40))
+ /* force to secam mode */
+ ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x0f);
+ } else {
+ v4l2_dbg(1, debug, sd, "s_std: Unknown norm %llx\n",
+ (unsigned long long)std);
+ }
+ return 0;
+}
+
+static int ks0127_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ v4l2_dbg(1, debug, sd, "s_stream(%d)\n", enable);
+ if (enable) {
+ /* All output pins on */
+ ks0127_and_or(sd, KS_OFMTA, 0xcf, 0x30);
+ /* Obey the OEN pin */
+ ks0127_and_or(sd, KS_CDEM, 0x7f, 0x00);
+ } else {
+ /* Video output pins off */
+ ks0127_and_or(sd, KS_OFMTA, 0xcf, 0x00);
+ /* Ignore the OEN pin */
+ ks0127_and_or(sd, KS_CDEM, 0x7f, 0x80);
+ }
+ return 0;
+}
+
+static int ks0127_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd)
+{
+ int stat = V4L2_IN_ST_NO_SIGNAL;
+ u8 status;
+ v4l2_std_id std = V4L2_STD_ALL;
+
+ status = ks0127_read(sd, KS_STAT);
+ if (!(status & 0x20)) /* NOVID not set */
+ stat = 0;
+ if (!(status & 0x01)) /* CLOCK set */
+ stat |= V4L2_IN_ST_NO_COLOR;
+ if ((status & 0x08)) /* PALDET set */
+ std = V4L2_STD_PAL;
+ else
+ std = V4L2_STD_NTSC;
+ if (pstd)
+ *pstd = std;
+ if (pstatus)
+ *pstatus = stat;
+ return 0;
+}
+
+static int ks0127_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ v4l2_dbg(1, debug, sd, "querystd\n");
+ return ks0127_status(sd, NULL, std);
+}
+
+static int ks0127_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+ v4l2_dbg(1, debug, sd, "g_input_status\n");
+ return ks0127_status(sd, status, NULL);
+}
+
+static int ks0127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ks0127 *ks = to_ks0127(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, ks->ident, 0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops ks0127_core_ops = {
+ .g_chip_ident = ks0127_g_chip_ident,
+ .s_std = ks0127_s_std,
+};
+
+static const struct v4l2_subdev_video_ops ks0127_video_ops = {
+ .s_routing = ks0127_s_routing,
+ .s_stream = ks0127_s_stream,
+ .querystd = ks0127_querystd,
+ .g_input_status = ks0127_g_input_status,
+};
+
+static const struct v4l2_subdev_ops ks0127_ops = {
+ .core = &ks0127_core_ops,
+ .video = &ks0127_video_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+
+static int ks0127_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct ks0127 *ks;
+ struct v4l2_subdev *sd;
+
+ v4l_info(client, "%s chip found @ 0x%x (%s)\n",
+ client->addr == (I2C_KS0127_ADDON >> 1) ? "addon" : "on-board",
+ client->addr << 1, client->adapter->name);
+
+ ks = kzalloc(sizeof(*ks), GFP_KERNEL);
+ if (ks == NULL)
+ return -ENOMEM;
+ sd = &ks->sd;
+ v4l2_i2c_subdev_init(sd, client, &ks0127_ops);
+
+ /* power up */
+ init_reg_defaults();
+ ks0127_write(sd, KS_CMDA, 0x2c);
+ mdelay(10);
+
+ /* reset the device */
+ ks0127_init(sd);
+ return 0;
+}
+
+static int ks0127_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ ks0127_write(sd, KS_OFMTA, 0x20); /* tristate */
+ ks0127_write(sd, KS_CMDA, 0x2c | 0x80); /* power down */
+ kfree(to_ks0127(sd));
+ return 0;
+}
+
+static const struct i2c_device_id ks0127_id[] = {
+ { "ks0127", 0 },
+ { "ks0127b", 0 },
+ { "ks0122s", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ks0127_id);
+
+static struct i2c_driver ks0127_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ks0127",
+ },
+ .probe = ks0127_probe,
+ .remove = ks0127_remove,
+ .id_table = ks0127_id,
+};
+
+module_i2c_driver(ks0127_driver);
diff --git a/drivers/media/video/ks0127.h b/drivers/media/video/ks0127.h
new file mode 100644
index 00000000..cb8abd54
--- /dev/null
+++ b/drivers/media/video/ks0127.h
@@ -0,0 +1,51 @@
+/*
+ * Video Capture Driver ( Video for Linux 1/2 )
+ * for the Matrox Marvel G200,G400 and Rainbow Runner-G series
+ *
+ * This module is an interface to the KS0127 video decoder chip.
+ *
+ * Copyright (C) 1999 Ryan Drake <stiletto@mediaone.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef KS0127_H
+#define KS0127_H
+
+/* input channels */
+#define KS_INPUT_COMPOSITE_1 0
+#define KS_INPUT_COMPOSITE_2 1
+#define KS_INPUT_COMPOSITE_3 2
+#define KS_INPUT_COMPOSITE_4 4
+#define KS_INPUT_COMPOSITE_5 5
+#define KS_INPUT_COMPOSITE_6 6
+
+#define KS_INPUT_SVIDEO_1 8
+#define KS_INPUT_SVIDEO_2 9
+#define KS_INPUT_SVIDEO_3 10
+
+#define KS_INPUT_YUV656 15
+#define KS_INPUT_COUNT 10
+
+/* output channels */
+#define KS_OUTPUT_YUV656E 0
+#define KS_OUTPUT_EXV 1
+
+/* video standards */
+#define KS_STD_NTSC_N 112 /* 50 Hz NTSC */
+#define KS_STD_PAL_M 113 /* 60 Hz PAL */
+
+#endif /* KS0127_H */
+
diff --git a/drivers/media/video/m52790.c b/drivers/media/video/m52790.c
new file mode 100644
index 00000000..0991576f
--- /dev/null
+++ b/drivers/media/video/m52790.c
@@ -0,0 +1,216 @@
+/*
+ * m52790 i2c ivtv driver.
+ * Copyright (C) 2007 Hans Verkuil
+ *
+ * A/V source switching Mitsubishi M52790SP/FP
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/ioctl.h>
+#include <asm/uaccess.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/m52790.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+
+MODULE_DESCRIPTION("i2c device driver for m52790 A/V switch");
+MODULE_AUTHOR("Hans Verkuil");
+MODULE_LICENSE("GPL");
+
+
+struct m52790_state {
+ struct v4l2_subdev sd;
+ u16 input;
+ u16 output;
+};
+
+static inline struct m52790_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct m52790_state, sd);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int m52790_write(struct v4l2_subdev *sd)
+{
+ struct m52790_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ u8 sw1 = (state->input | state->output) & 0xff;
+ u8 sw2 = (state->input | state->output) >> 8;
+
+ return i2c_smbus_write_byte_data(client, sw1, sw2);
+}
+
+/* Note: audio and video are linked and cannot be switched separately.
+ So audio and video routing commands are identical for this chip.
+ In theory the video amplifier and audio modes could be handled
+ separately for the output, but that seems to be overkill right now.
+ The same holds for implementing an audio mute control, this is now
+ part of the audio output routing. The normal case is that another
+ chip takes care of the actual muting so making it part of the
+ output routing seems to be the right thing to do for now. */
+static int m52790_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct m52790_state *state = to_state(sd);
+
+ state->input = input;
+ state->output = output;
+ m52790_write(sd);
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int m52790_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct m52790_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (reg->reg != 0)
+ return -EINVAL;
+ reg->size = 1;
+ reg->val = state->input | state->output;
+ return 0;
+}
+
+static int m52790_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct m52790_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (reg->reg != 0)
+ return -EINVAL;
+ state->input = reg->val & 0x0303;
+ state->output = reg->val & ~0x0303;
+ m52790_write(sd);
+ return 0;
+}
+#endif
+
+static int m52790_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_M52790, 0);
+}
+
+static int m52790_log_status(struct v4l2_subdev *sd)
+{
+ struct m52790_state *state = to_state(sd);
+
+ v4l2_info(sd, "Switch 1: %02x\n",
+ (state->input | state->output) & 0xff);
+ v4l2_info(sd, "Switch 2: %02x\n",
+ (state->input | state->output) >> 8);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops m52790_core_ops = {
+ .log_status = m52790_log_status,
+ .g_chip_ident = m52790_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = m52790_g_register,
+ .s_register = m52790_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_audio_ops m52790_audio_ops = {
+ .s_routing = m52790_s_routing,
+};
+
+static const struct v4l2_subdev_video_ops m52790_video_ops = {
+ .s_routing = m52790_s_routing,
+};
+
+static const struct v4l2_subdev_ops m52790_ops = {
+ .core = &m52790_core_ops,
+ .audio = &m52790_audio_ops,
+ .video = &m52790_video_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+/* i2c implementation */
+
+static int m52790_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct m52790_state *state;
+ struct v4l2_subdev *sd;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ state = kzalloc(sizeof(struct m52790_state), GFP_KERNEL);
+ if (state == NULL)
+ return -ENOMEM;
+
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &m52790_ops);
+ state->input = M52790_IN_TUNER;
+ state->output = M52790_OUT_STEREO;
+ m52790_write(sd);
+ return 0;
+}
+
+static int m52790_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct i2c_device_id m52790_id[] = {
+ { "m52790", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, m52790_id);
+
+static struct i2c_driver m52790_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "m52790",
+ },
+ .probe = m52790_probe,
+ .remove = m52790_remove,
+ .id_table = m52790_id,
+};
+
+module_i2c_driver(m52790_driver);
diff --git a/drivers/media/video/m5mols/Kconfig b/drivers/media/video/m5mols/Kconfig
new file mode 100644
index 00000000..302dc3d7
--- /dev/null
+++ b/drivers/media/video/m5mols/Kconfig
@@ -0,0 +1,5 @@
+config VIDEO_M5MOLS
+ tristate "Fujitsu M-5MOLS 8MP sensor support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ ---help---
+ 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 00000000..0a44e028
--- /dev/null
+++ b/drivers/media/video/m5mols/Makefile
@@ -0,0 +1,3 @@
+m5mols-objs := m5mols_core.o m5mols_controls.o m5mols_capture.o
+
+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 00000000..4b021e1e
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols.h
@@ -0,0 +1,299 @@
+/*
+ * Header for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim <riverful.kim@samsung.com>
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef M5MOLS_H
+#define M5MOLS_H
+
+#include <media/v4l2-subdev.h>
+#include "m5mols_reg.h"
+
+extern int m5mols_debug;
+
+#define to_m5mols(__sd) container_of(__sd, struct m5mols_info, sd)
+
+#define to_sd(__ctrl) \
+ (&container_of(__ctrl->handler, struct m5mols_info, handle)->sd)
+
+enum m5mols_restype {
+ M5MOLS_RESTYPE_MONITOR,
+ M5MOLS_RESTYPE_CAPTURE,
+ M5MOLS_RESTYPE_MAX,
+};
+
+/**
+ * struct m5mols_resolution - structure for the resolution
+ * @type: resolution type according to the pixel code
+ * @width: width of the resolution
+ * @height: height of the resolution
+ * @reg: resolution preset register value
+ */
+struct m5mols_resolution {
+ u8 reg;
+ enum m5mols_restype type;
+ u16 width;
+ u16 height;
+};
+
+/**
+ * struct m5mols_exif - structure for the EXIF information of M-5MOLS
+ * @exposure_time: exposure time register value
+ * @shutter_speed: speed of the shutter register value
+ * @aperture: aperture register value
+ * @exposure_bias: it calls also EV bias
+ * @iso_speed: ISO register value
+ * @flash: status register value of the flash
+ * @sdr: status register value of the Subject Distance Range
+ * @qval: not written exact meaning in document
+ */
+struct m5mols_exif {
+ u32 exposure_time;
+ u32 shutter_speed;
+ u32 aperture;
+ u32 brightness;
+ u32 exposure_bias;
+ u16 iso_speed;
+ u16 flash;
+ u16 sdr;
+ u16 qval;
+};
+
+/**
+ * struct m5mols_capture - Structure for the capture capability
+ * @exif: EXIF information
+ * @main: size in bytes of the main image
+ * @thumb: size in bytes of the thumb image, if it was accompanied
+ * @total: total size in bytes of the produced image
+ */
+struct m5mols_capture {
+ struct m5mols_exif exif;
+ u32 main;
+ u32 thumb;
+ u32 total;
+};
+
+/**
+ * struct m5mols_scenemode - structure for the scenemode capability
+ * @metering: metering light register value
+ * @ev_bias: EV bias register value
+ * @wb_mode: mode which means the WhiteBalance is Auto or Manual
+ * @wb_preset: whitebalance preset register value in the Manual mode
+ * @chroma_en: register value whether the Chroma capability is enabled or not
+ * @chroma_lvl: chroma's level register value
+ * @edge_en: register value Whether the Edge capability is enabled or not
+ * @edge_lvl: edge's level register value
+ * @af_range: Auto Focus's range
+ * @fd_mode: Face Detection mode
+ * @mcc: Multi-axis Color Conversion which means emotion color
+ * @light: status of the Light
+ * @flash: status of the Flash
+ * @tone: Tone color which means Contrast
+ * @iso: ISO register value
+ * @capt_mode: Mode of the Image Stabilization while the camera capturing
+ * @wdr: Wide Dynamic Range register value
+ *
+ * The each value according to each scenemode is recommended in the documents.
+ */
+struct m5mols_scenemode {
+ u8 metering;
+ u8 ev_bias;
+ u8 wb_mode;
+ u8 wb_preset;
+ u8 chroma_en;
+ u8 chroma_lvl;
+ u8 edge_en;
+ u8 edge_lvl;
+ u8 af_range;
+ u8 fd_mode;
+ u8 mcc;
+ u8 light;
+ u8 flash;
+ u8 tone;
+ u8 iso;
+ u8 capt_mode;
+ u8 wdr;
+};
+
+/**
+ * struct m5mols_version - firmware version information
+ * @customer: customer information
+ * @project: version of project information according to customer
+ * @fw: firmware revision
+ * @hw: hardware revision
+ * @param: version of the parameter
+ * @awb: Auto WhiteBalance algorithm version
+ * @str: information about manufacturer and packaging vendor
+ * @af: Auto Focus version
+ *
+ * The register offset starts the customer version at 0x0, and it ends
+ * the awb version at 0x09. The customer, project information occupies 1 bytes
+ * each. And also the fw, hw, param, awb each requires 2 bytes. The str is
+ * unique string associated with firmware's version. It includes information
+ * about manufacturer and the vendor of the sensor's packaging. The least
+ * significant 2 bytes of the string indicate packaging manufacturer.
+ */
+#define VERSION_STRING_SIZE 22
+struct m5mols_version {
+ u8 customer;
+ u8 project;
+ u16 fw;
+ u16 hw;
+ u16 param;
+ u16 awb;
+ u8 str[VERSION_STRING_SIZE];
+ u8 af;
+};
+
+/**
+ * struct m5mols_info - M-5MOLS driver data structure
+ * @pdata: platform data
+ * @sd: v4l-subdev instance
+ * @pad: media pad
+ * @ffmt: current fmt according to resolution type
+ * @res_type: current resolution type
+ * @irq_waitq: waitqueue for the capture
+ * @flags: state variable for the interrupt handler
+ * @handle: control handler
+ * @autoexposure: Auto Exposure control
+ * @exposure: Exposure control
+ * @autowb: Auto White Balance control
+ * @colorfx: Color effect control
+ * @saturation: Saturation control
+ * @zoom: Zoom control
+ * @ver: information of the version
+ * @cap: the capture mode attributes
+ * @power: current sensor's power status
+ * @isp_ready: 1 when the ISP controller has completed booting
+ * @ctrl_sync: 1 when the control handler state is restored in H/W
+ * @lock_ae: true means the Auto Exposure is locked
+ * @lock_awb: true means the Aut WhiteBalance is locked
+ * @resolution: register value for current resolution
+ * @mode: register value for current operation mode
+ * @set_power: optional power callback to the board code
+ */
+struct m5mols_info {
+ const struct m5mols_platform_data *pdata;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct v4l2_mbus_framefmt ffmt[M5MOLS_RESTYPE_MAX];
+ int res_type;
+
+ wait_queue_head_t irq_waitq;
+ atomic_t irq_done;
+
+ struct v4l2_ctrl_handler handle;
+
+ /* Autoexposure/exposure control cluster */
+ struct v4l2_ctrl *autoexposure;
+ struct v4l2_ctrl *exposure;
+
+ struct v4l2_ctrl *autowb;
+ struct v4l2_ctrl *colorfx;
+ struct v4l2_ctrl *saturation;
+ struct v4l2_ctrl *zoom;
+
+ struct m5mols_version ver;
+ struct m5mols_capture cap;
+
+ unsigned int isp_ready:1;
+ unsigned int power:1;
+ unsigned int ctrl_sync:1;
+
+ bool lock_ae;
+ bool lock_awb;
+ u8 resolution;
+ u8 mode;
+
+ int (*set_power)(struct device *dev, int on);
+};
+
+#define is_available_af(__info) (__info->ver.af)
+#define is_code(__code, __type) (__code == m5mols_default_ffmt[__type].code)
+#define is_manufacturer(__info, __manufacturer) \
+ (__info->ver.str[0] == __manufacturer[0] && \
+ __info->ver.str[1] == __manufacturer[1])
+/*
+ * I2C operation of the M-5MOLS
+ *
+ * The I2C read operation of the M-5MOLS requires 2 messages. The first
+ * message sends the information about the command, command category, and total
+ * message size. The second message is used to retrieve the data specifed in
+ * the first message
+ *
+ * 1st message 2nd message
+ * +-------+---+----------+-----+-------+ +------+------+------+------+
+ * | size1 | R | category | cmd | size2 | | d[0] | d[1] | d[2] | d[3] |
+ * +-------+---+----------+-----+-------+ +------+------+------+------+
+ * - size1: message data size(5 in this case)
+ * - size2: desired buffer size of the 2nd message
+ * - d[0..3]: according to size2
+ *
+ * The I2C write operation needs just one message. The message includes
+ * category, command, total size, and desired data.
+ *
+ * 1st message
+ * +-------+---+----------+-----+------+------+------+------+
+ * | size1 | W | category | cmd | d[0] | d[1] | d[2] | d[3] |
+ * +-------+---+----------+-----+------+------+------+------+
+ * - d[0..3]: according to size1
+ */
+int m5mols_read_u8(struct v4l2_subdev *sd, u32 reg_comb, u8 *val);
+int m5mols_read_u16(struct v4l2_subdev *sd, u32 reg_comb, u16 *val);
+int m5mols_read_u32(struct v4l2_subdev *sd, u32 reg_comb, u32 *val);
+int m5mols_write(struct v4l2_subdev *sd, u32 reg_comb, u32 val);
+
+int m5mols_busy_wait(struct v4l2_subdev *sd, u32 reg, u32 value, u32 mask,
+ int timeout);
+
+/* Mask value for busy waiting until M-5MOLS I2C interface is initialized */
+#define M5MOLS_I2C_RDY_WAIT_FL (1 << 16)
+/* ISP state transition timeout, in ms */
+#define M5MOLS_MODE_CHANGE_TIMEOUT 200
+#define M5MOLS_BUSY_WAIT_DEF_TIMEOUT 250
+
+/*
+ * Mode operation of the M-5MOLS
+ *
+ * Changing the mode of the M-5MOLS is needed right executing order.
+ * There are three modes(PARAMETER, MONITOR, CAPTURE) which can be changed
+ * by user. There are various categories associated with each mode.
+ *
+ * +============================================================+
+ * | mode | category |
+ * +============================================================+
+ * | FLASH | FLASH(only after Stand-by or Power-on) |
+ * | SYSTEM | SYSTEM(only after sensor arm-booting) |
+ * | PARAMETER | PARAMETER |
+ * | MONITOR | MONITOR(preview), Auto Focus, Face Detection |
+ * | CAPTURE | Single CAPTURE, Preview(recording) |
+ * +============================================================+
+ *
+ * The available executing order between each modes are as follows:
+ * PARAMETER <---> MONITOR <---> CAPTURE
+ */
+int m5mols_mode(struct m5mols_info *info, u8 mode);
+
+int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg);
+int m5mols_wait_interrupt(struct v4l2_subdev *sd, u8 condition, u32 timeout);
+int m5mols_restore_controls(struct m5mols_info *info);
+int m5mols_start_capture(struct m5mols_info *info);
+int m5mols_do_scenemode(struct m5mols_info *info, u8 mode);
+int m5mols_lock_3a(struct m5mols_info *info, bool lock);
+int m5mols_set_ctrl(struct v4l2_ctrl *ctrl);
+
+/* The firmware function */
+int m5mols_update_fw(struct v4l2_subdev *sd,
+ int (*set_power)(struct m5mols_info *, bool));
+
+#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 00000000..ba25e8e2
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols_capture.c
@@ -0,0 +1,160 @@
+
+/*
+ * The Capture code for Fujitsu M-5MOLS ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim <riverful.kim@samsung.com>
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/m5mols.h>
+#include <media/s5p_fimc.h>
+
+#include "m5mols.h"
+#include "m5mols_reg.h"
+
+/**
+ * m5mols_read_rational - I2C read of a rational number
+ *
+ * Read numerator and denominator from registers @addr_num and @addr_den
+ * respectively and return the division result in @val.
+ */
+static int m5mols_read_rational(struct v4l2_subdev *sd, u32 addr_num,
+ u32 addr_den, u32 *val)
+{
+ u32 num, den;
+
+ int ret = m5mols_read_u32(sd, addr_num, &num);
+ if (!ret)
+ ret = m5mols_read_u32(sd, addr_den, &den);
+ if (ret)
+ return ret;
+ *val = den == 0 ? 0 : num / den;
+ return ret;
+}
+
+/**
+ * m5mols_capture_info - Gather captured image information
+ *
+ * For now it gathers only EXIF information and file size.
+ */
+static int m5mols_capture_info(struct m5mols_info *info)
+{
+ struct m5mols_exif *exif = &info->cap.exif;
+ struct v4l2_subdev *sd = &info->sd;
+ int ret;
+
+ ret = m5mols_read_rational(sd, EXIF_INFO_EXPTIME_NU,
+ EXIF_INFO_EXPTIME_DE, &exif->exposure_time);
+ if (ret)
+ return ret;
+ ret = m5mols_read_rational(sd, EXIF_INFO_TV_NU, EXIF_INFO_TV_DE,
+ &exif->shutter_speed);
+ if (ret)
+ return ret;
+ ret = m5mols_read_rational(sd, EXIF_INFO_AV_NU, EXIF_INFO_AV_DE,
+ &exif->aperture);
+ if (ret)
+ return ret;
+ ret = m5mols_read_rational(sd, EXIF_INFO_BV_NU, EXIF_INFO_BV_DE,
+ &exif->brightness);
+ if (ret)
+ return ret;
+ ret = m5mols_read_rational(sd, EXIF_INFO_EBV_NU, EXIF_INFO_EBV_DE,
+ &exif->exposure_bias);
+ if (ret)
+ return ret;
+
+ ret = m5mols_read_u16(sd, EXIF_INFO_ISO, &exif->iso_speed);
+ if (!ret)
+ ret = m5mols_read_u16(sd, EXIF_INFO_FLASH, &exif->flash);
+ if (!ret)
+ ret = m5mols_read_u16(sd, EXIF_INFO_SDR, &exif->sdr);
+ if (!ret)
+ ret = m5mols_read_u16(sd, EXIF_INFO_QVAL, &exif->qval);
+ if (ret)
+ return ret;
+
+ if (!ret)
+ ret = m5mols_read_u32(sd, CAPC_IMAGE_SIZE, &info->cap.main);
+ if (!ret)
+ ret = m5mols_read_u32(sd, CAPC_THUMB_SIZE, &info->cap.thumb);
+ if (!ret)
+ info->cap.total = info->cap.main + info->cap.thumb;
+
+ return ret;
+}
+
+int m5mols_start_capture(struct m5mols_info *info)
+{
+ struct v4l2_subdev *sd = &info->sd;
+ u8 resolution = info->resolution;
+ int ret;
+
+ /*
+ * Synchronize the controls, set the capture frame resolution and color
+ * format. The frame capture is initiated during switching from Monitor
+ * to Capture mode.
+ */
+ ret = m5mols_mode(info, REG_MONITOR);
+ if (!ret)
+ ret = m5mols_restore_controls(info);
+ if (!ret)
+ ret = m5mols_write(sd, CAPP_YUVOUT_MAIN, REG_JPEG);
+ if (!ret)
+ ret = m5mols_write(sd, CAPP_MAIN_IMAGE_SIZE, resolution);
+ if (!ret)
+ ret = m5mols_lock_3a(info, true);
+ if (!ret)
+ ret = m5mols_mode(info, REG_CAPTURE);
+ if (!ret)
+ /* Wait until a frame is captured to ISP internal memory */
+ ret = m5mols_wait_interrupt(sd, REG_INT_CAPTURE, 2000);
+ if (!ret)
+ ret = m5mols_lock_3a(info, false);
+ if (ret)
+ return ret;
+
+ /*
+ * Initiate the captured data transfer to a MIPI-CSI receiver.
+ */
+ ret = m5mols_write(sd, CAPC_SEL_FRAME, 1);
+ if (!ret)
+ ret = m5mols_write(sd, CAPC_START, REG_CAP_START_MAIN);
+ if (!ret) {
+ bool captured = false;
+ unsigned int size;
+
+ /* Wait for the capture completion interrupt */
+ ret = m5mols_wait_interrupt(sd, REG_INT_CAPTURE, 2000);
+ if (!ret) {
+ captured = true;
+ ret = m5mols_capture_info(info);
+ }
+ size = captured ? info->cap.main : 0;
+ v4l2_dbg(1, m5mols_debug, sd, "%s: size: %d, thumb.: %d B\n",
+ __func__, size, info->cap.thumb);
+
+ v4l2_subdev_notify(sd, S5P_FIMC_TX_END_NOTIFY, &size);
+ }
+
+ return ret;
+}
diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c
new file mode 100644
index 00000000..d135d20d
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols_controls.c
@@ -0,0 +1,299 @@
+/*
+ * Controls for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim <riverful.kim@samsung.com>
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+
+#include "m5mols.h"
+#include "m5mols_reg.h"
+
+static struct m5mols_scenemode m5mols_default_scenemode[] = {
+ [REG_SCENE_NORMAL] = {
+ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+ REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+ REG_AF_NORMAL, REG_FD_OFF,
+ REG_MCC_NORMAL, REG_LIGHT_OFF, REG_FLASH_OFF,
+ 5, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+ },
+ [REG_SCENE_PORTRAIT] = {
+ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+ REG_CHROMA_ON, 3, REG_EDGE_ON, 4,
+ REG_AF_NORMAL, BIT_FD_EN | BIT_FD_DRAW_FACE_FRAME,
+ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+ 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+ },
+ [REG_SCENE_LANDSCAPE] = {
+ REG_AE_ALL, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+ REG_CHROMA_ON, 4, REG_EDGE_ON, 6,
+ REG_AF_NORMAL, REG_FD_OFF,
+ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+ 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+ },
+ [REG_SCENE_SPORTS] = {
+ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+ REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+ REG_AF_NORMAL, REG_FD_OFF,
+ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+ 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+ },
+ [REG_SCENE_PARTY_INDOOR] = {
+ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+ REG_CHROMA_ON, 4, REG_EDGE_ON, 5,
+ REG_AF_NORMAL, REG_FD_OFF,
+ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+ 6, REG_ISO_200, REG_CAP_NONE, REG_WDR_OFF,
+ },
+ [REG_SCENE_BEACH_SNOW] = {
+ REG_AE_CENTER, REG_AE_INDEX_10_POS, REG_AWB_AUTO, 0,
+ REG_CHROMA_ON, 4, REG_EDGE_ON, 5,
+ REG_AF_NORMAL, REG_FD_OFF,
+ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+ 6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF,
+ },
+ [REG_SCENE_SUNSET] = {
+ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET,
+ REG_AWB_DAYLIGHT,
+ REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+ REG_AF_NORMAL, REG_FD_OFF,
+ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+ 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+ },
+ [REG_SCENE_DAWN_DUSK] = {
+ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET,
+ REG_AWB_FLUORESCENT_1,
+ REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+ REG_AF_NORMAL, REG_FD_OFF,
+ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+ 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+ },
+ [REG_SCENE_FALL] = {
+ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+ REG_CHROMA_ON, 5, REG_EDGE_ON, 5,
+ REG_AF_NORMAL, REG_FD_OFF,
+ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+ 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+ },
+ [REG_SCENE_NIGHT] = {
+ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+ REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+ REG_AF_NORMAL, REG_FD_OFF,
+ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+ 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+ },
+ [REG_SCENE_AGAINST_LIGHT] = {
+ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+ REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+ REG_AF_NORMAL, REG_FD_OFF,
+ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+ 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+ },
+ [REG_SCENE_FIRE] = {
+ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+ REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+ REG_AF_NORMAL, REG_FD_OFF,
+ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+ 6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF,
+ },
+ [REG_SCENE_TEXT] = {
+ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+ REG_CHROMA_ON, 3, REG_EDGE_ON, 7,
+ REG_AF_MACRO, REG_FD_OFF,
+ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+ 6, REG_ISO_AUTO, REG_CAP_ANTI_SHAKE, REG_WDR_ON,
+ },
+ [REG_SCENE_CANDLE] = {
+ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+ REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+ REG_AF_NORMAL, REG_FD_OFF,
+ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+ 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+ },
+};
+
+/**
+ * m5mols_do_scenemode() - Change current scenemode
+ * @mode: Desired mode of the scenemode
+ *
+ * WARNING: The execution order is important. Do not change the order.
+ */
+int m5mols_do_scenemode(struct m5mols_info *info, u8 mode)
+{
+ struct v4l2_subdev *sd = &info->sd;
+ struct m5mols_scenemode scenemode = m5mols_default_scenemode[mode];
+ int ret;
+
+ if (mode > REG_SCENE_CANDLE)
+ return -EINVAL;
+
+ ret = m5mols_lock_3a(info, false);
+ if (!ret)
+ ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, mode);
+ if (!ret)
+ ret = m5mols_write(sd, AE_EV_PRESET_CAPTURE, mode);
+ if (!ret)
+ ret = m5mols_write(sd, AE_MODE, scenemode.metering);
+ if (!ret)
+ ret = m5mols_write(sd, AE_INDEX, scenemode.ev_bias);
+ if (!ret)
+ ret = m5mols_write(sd, AWB_MODE, scenemode.wb_mode);
+ if (!ret)
+ ret = m5mols_write(sd, AWB_MANUAL, scenemode.wb_preset);
+ if (!ret)
+ ret = m5mols_write(sd, MON_CHROMA_EN, scenemode.chroma_en);
+ if (!ret)
+ ret = m5mols_write(sd, MON_CHROMA_LVL, scenemode.chroma_lvl);
+ if (!ret)
+ ret = m5mols_write(sd, MON_EDGE_EN, scenemode.edge_en);
+ if (!ret)
+ ret = m5mols_write(sd, MON_EDGE_LVL, scenemode.edge_lvl);
+ if (!ret && is_available_af(info))
+ ret = m5mols_write(sd, AF_MODE, scenemode.af_range);
+ if (!ret && is_available_af(info))
+ ret = m5mols_write(sd, FD_CTL, scenemode.fd_mode);
+ if (!ret)
+ ret = m5mols_write(sd, MON_TONE_CTL, scenemode.tone);
+ if (!ret)
+ ret = m5mols_write(sd, AE_ISO, scenemode.iso);
+ if (!ret)
+ ret = m5mols_mode(info, REG_CAPTURE);
+ if (!ret)
+ ret = m5mols_write(sd, CAPP_WDR_EN, scenemode.wdr);
+ if (!ret)
+ ret = m5mols_write(sd, CAPP_MCC_MODE, scenemode.mcc);
+ if (!ret)
+ ret = m5mols_write(sd, CAPP_LIGHT_CTRL, scenemode.light);
+ if (!ret)
+ ret = m5mols_write(sd, CAPP_FLASH_CTRL, scenemode.flash);
+ if (!ret)
+ ret = m5mols_write(sd, CAPC_MODE, scenemode.capt_mode);
+ if (!ret)
+ ret = m5mols_mode(info, REG_MONITOR);
+
+ return ret;
+}
+
+static int m5mols_lock_ae(struct m5mols_info *info, bool lock)
+{
+ int ret = 0;
+
+ if (info->lock_ae != lock)
+ ret = m5mols_write(&info->sd, AE_LOCK,
+ lock ? REG_AE_LOCK : REG_AE_UNLOCK);
+ if (!ret)
+ info->lock_ae = lock;
+
+ return ret;
+}
+
+static int m5mols_lock_awb(struct m5mols_info *info, bool lock)
+{
+ int ret = 0;
+
+ if (info->lock_awb != lock)
+ ret = m5mols_write(&info->sd, AWB_LOCK,
+ lock ? REG_AWB_LOCK : REG_AWB_UNLOCK);
+ if (!ret)
+ info->lock_awb = lock;
+
+ return ret;
+}
+
+/* m5mols_lock_3a() - Lock 3A(Auto Exposure, Auto Whitebalance, Auto Focus) */
+int m5mols_lock_3a(struct m5mols_info *info, bool lock)
+{
+ int ret;
+
+ ret = m5mols_lock_ae(info, lock);
+ if (!ret)
+ ret = m5mols_lock_awb(info, lock);
+ /* Don't need to handle unlocking AF */
+ if (!ret && is_available_af(info) && lock)
+ ret = m5mols_write(&info->sd, AF_EXECUTE, REG_AF_STOP);
+
+ return ret;
+}
+
+/* m5mols_set_ctrl() - The main s_ctrl function called by m5mols_set_ctrl() */
+int m5mols_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ struct m5mols_info *info = to_m5mols(sd);
+ int ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_ZOOM_ABSOLUTE:
+ return m5mols_write(sd, MON_ZOOM, ctrl->val);
+
+ case V4L2_CID_EXPOSURE_AUTO:
+ ret = m5mols_lock_ae(info,
+ ctrl->val == V4L2_EXPOSURE_AUTO ? false : true);
+ if (!ret && ctrl->val == V4L2_EXPOSURE_AUTO)
+ ret = m5mols_write(sd, AE_MODE, REG_AE_ALL);
+ if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL) {
+ int val = info->exposure->val;
+ ret = m5mols_write(sd, AE_MODE, REG_AE_OFF);
+ if (!ret)
+ ret = m5mols_write(sd, AE_MAN_GAIN_MON, val);
+ if (!ret)
+ ret = m5mols_write(sd, AE_MAN_GAIN_CAP, val);
+ }
+ return ret;
+
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ ret = m5mols_lock_awb(info, ctrl->val ? false : true);
+ if (!ret)
+ ret = m5mols_write(sd, AWB_MODE, ctrl->val ?
+ REG_AWB_AUTO : REG_AWB_PRESET);
+ return ret;
+
+ case V4L2_CID_SATURATION:
+ ret = m5mols_write(sd, MON_CHROMA_LVL, ctrl->val);
+ if (!ret)
+ ret = m5mols_write(sd, MON_CHROMA_EN, REG_CHROMA_ON);
+ return ret;
+
+ case V4L2_CID_COLORFX:
+ /*
+ * This control uses two kinds of registers: normal & color.
+ * The normal effect belongs to category 1, while the color
+ * one belongs to category 2.
+ *
+ * The normal effect uses one register: CAT1_EFFECT.
+ * The color effect uses three registers:
+ * CAT2_COLOR_EFFECT, CAT2_CFIXR, CAT2_CFIXB.
+ */
+ ret = m5mols_write(sd, PARM_EFFECT,
+ ctrl->val == V4L2_COLORFX_NEGATIVE ? REG_EFFECT_NEGA :
+ ctrl->val == V4L2_COLORFX_EMBOSS ? REG_EFFECT_EMBOSS :
+ REG_EFFECT_OFF);
+ if (!ret)
+ ret = m5mols_write(sd, MON_EFFECT,
+ ctrl->val == V4L2_COLORFX_SEPIA ?
+ REG_COLOR_EFFECT_ON : REG_COLOR_EFFECT_OFF);
+ if (!ret)
+ ret = m5mols_write(sd, MON_CFIXR,
+ ctrl->val == V4L2_COLORFX_SEPIA ?
+ REG_CFIXR_SEPIA : 0);
+ if (!ret)
+ ret = m5mols_write(sd, MON_CFIXB,
+ ctrl->val == V4L2_COLORFX_SEPIA ?
+ REG_CFIXB_SEPIA : 0);
+ return ret;
+ }
+
+ return -EINVAL;
+}
diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c
new file mode 100644
index 00000000..d718aee0
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols_core.c
@@ -0,0 +1,1065 @@
+/*
+ * Driver for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim <riverful.kim@samsung.com>
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/videodev2.h>
+#include <linux/module.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/m5mols.h>
+
+#include "m5mols.h"
+#include "m5mols_reg.h"
+
+int m5mols_debug;
+module_param(m5mols_debug, int, 0644);
+
+#define MODULE_NAME "M5MOLS"
+#define M5MOLS_I2C_CHECK_RETRY 500
+
+/* The regulator consumer names for external voltage regulators */
+static struct regulator_bulk_data supplies[] = {
+ {
+ .supply = "core", /* ARM core power, 1.2V */
+ }, {
+ .supply = "dig_18", /* digital power 1, 1.8V */
+ }, {
+ .supply = "d_sensor", /* sensor power 1, 1.8V */
+ }, {
+ .supply = "dig_28", /* digital power 2, 2.8V */
+ }, {
+ .supply = "a_sensor", /* analog power */
+ }, {
+ .supply = "dig_12", /* digital power 3, 1.2V */
+ },
+};
+
+static struct v4l2_mbus_framefmt m5mols_default_ffmt[M5MOLS_RESTYPE_MAX] = {
+ [M5MOLS_RESTYPE_MONITOR] = {
+ .width = 1920,
+ .height = 1080,
+ .code = V4L2_MBUS_FMT_VYUY8_2X8,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+ [M5MOLS_RESTYPE_CAPTURE] = {
+ .width = 1920,
+ .height = 1080,
+ .code = V4L2_MBUS_FMT_JPEG_1X8,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+};
+#define SIZE_DEFAULT_FFMT ARRAY_SIZE(m5mols_default_ffmt)
+
+static const struct m5mols_resolution m5mols_reg_res[] = {
+ { 0x01, M5MOLS_RESTYPE_MONITOR, 128, 96 }, /* SUB-QCIF */
+ { 0x03, M5MOLS_RESTYPE_MONITOR, 160, 120 }, /* QQVGA */
+ { 0x05, M5MOLS_RESTYPE_MONITOR, 176, 144 }, /* QCIF */
+ { 0x06, M5MOLS_RESTYPE_MONITOR, 176, 176 },
+ { 0x08, M5MOLS_RESTYPE_MONITOR, 240, 320 }, /* QVGA */
+ { 0x09, M5MOLS_RESTYPE_MONITOR, 320, 240 }, /* QVGA */
+ { 0x0c, M5MOLS_RESTYPE_MONITOR, 240, 400 }, /* WQVGA */
+ { 0x0d, M5MOLS_RESTYPE_MONITOR, 400, 240 }, /* WQVGA */
+ { 0x0e, M5MOLS_RESTYPE_MONITOR, 352, 288 }, /* CIF */
+ { 0x13, M5MOLS_RESTYPE_MONITOR, 480, 360 },
+ { 0x15, M5MOLS_RESTYPE_MONITOR, 640, 360 }, /* qHD */
+ { 0x17, M5MOLS_RESTYPE_MONITOR, 640, 480 }, /* VGA */
+ { 0x18, M5MOLS_RESTYPE_MONITOR, 720, 480 },
+ { 0x1a, M5MOLS_RESTYPE_MONITOR, 800, 480 }, /* WVGA */
+ { 0x1f, M5MOLS_RESTYPE_MONITOR, 800, 600 }, /* SVGA */
+ { 0x21, M5MOLS_RESTYPE_MONITOR, 1280, 720 }, /* HD */
+ { 0x25, M5MOLS_RESTYPE_MONITOR, 1920, 1080 }, /* 1080p */
+ { 0x29, M5MOLS_RESTYPE_MONITOR, 3264, 2448 }, /* 2.63fps 8M */
+ { 0x39, M5MOLS_RESTYPE_MONITOR, 800, 602 }, /* AHS_MON debug */
+
+ { 0x02, M5MOLS_RESTYPE_CAPTURE, 320, 240 }, /* QVGA */
+ { 0x04, M5MOLS_RESTYPE_CAPTURE, 400, 240 }, /* WQVGA */
+ { 0x07, M5MOLS_RESTYPE_CAPTURE, 480, 360 },
+ { 0x08, M5MOLS_RESTYPE_CAPTURE, 640, 360 }, /* qHD */
+ { 0x09, M5MOLS_RESTYPE_CAPTURE, 640, 480 }, /* VGA */
+ { 0x0a, M5MOLS_RESTYPE_CAPTURE, 800, 480 }, /* WVGA */
+ { 0x10, M5MOLS_RESTYPE_CAPTURE, 1280, 720 }, /* HD */
+ { 0x14, M5MOLS_RESTYPE_CAPTURE, 1280, 960 }, /* 1M */
+ { 0x17, M5MOLS_RESTYPE_CAPTURE, 1600, 1200 }, /* 2M */
+ { 0x19, M5MOLS_RESTYPE_CAPTURE, 1920, 1080 }, /* Full-HD */
+ { 0x1a, M5MOLS_RESTYPE_CAPTURE, 2048, 1152 }, /* 3Mega */
+ { 0x1b, M5MOLS_RESTYPE_CAPTURE, 2048, 1536 },
+ { 0x1c, M5MOLS_RESTYPE_CAPTURE, 2560, 1440 }, /* 4Mega */
+ { 0x1d, M5MOLS_RESTYPE_CAPTURE, 2560, 1536 },
+ { 0x1f, M5MOLS_RESTYPE_CAPTURE, 2560, 1920 }, /* 5Mega */
+ { 0x21, M5MOLS_RESTYPE_CAPTURE, 3264, 1836 }, /* 6Mega */
+ { 0x22, M5MOLS_RESTYPE_CAPTURE, 3264, 1960 },
+ { 0x25, M5MOLS_RESTYPE_CAPTURE, 3264, 2448 }, /* 8Mega */
+};
+
+/**
+ * m5mols_swap_byte - an byte array to integer conversion function
+ * @size: size in bytes of I2C packet defined in the M-5MOLS datasheet
+ *
+ * Convert I2C data byte array with performing any required byte
+ * reordering to assure proper values for each data type, regardless
+ * of the architecture endianness.
+ */
+static u32 m5mols_swap_byte(u8 *data, u8 length)
+{
+ if (length == 1)
+ return *data;
+ else if (length == 2)
+ return be16_to_cpu(*((u16 *)data));
+ else
+ return be32_to_cpu(*((u32 *)data));
+}
+
+/**
+ * m5mols_read - I2C read function
+ * @reg: combination of size, category and command for the I2C packet
+ * @size: desired size of I2C packet
+ * @val: read value
+ *
+ * Returns 0 on success, or else negative errno.
+ */
+static int m5mols_read(struct v4l2_subdev *sd, u32 size, u32 reg, u32 *val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct m5mols_info *info = to_m5mols(sd);
+ u8 rbuf[M5MOLS_I2C_MAX_SIZE + 1];
+ u8 category = I2C_CATEGORY(reg);
+ u8 cmd = I2C_COMMAND(reg);
+ struct i2c_msg msg[2];
+ u8 wbuf[5];
+ int ret;
+
+ if (!client->adapter)
+ return -ENODEV;
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 5;
+ msg[0].buf = wbuf;
+ wbuf[0] = 5;
+ wbuf[1] = M5MOLS_BYTE_READ;
+ wbuf[2] = category;
+ wbuf[3] = cmd;
+ wbuf[4] = size;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = size + 1;
+ msg[1].buf = rbuf;
+
+ /* minimum stabilization time */
+ usleep_range(200, 200);
+
+ ret = i2c_transfer(client->adapter, msg, 2);
+
+ if (ret == 2) {
+ *val = m5mols_swap_byte(&rbuf[1], size);
+ return 0;
+ }
+
+ if (info->isp_ready)
+ v4l2_err(sd, "read failed: size:%d cat:%02x cmd:%02x. %d\n",
+ size, category, cmd, ret);
+
+ return ret < 0 ? ret : -EIO;
+}
+
+int m5mols_read_u8(struct v4l2_subdev *sd, u32 reg, u8 *val)
+{
+ u32 val_32;
+ int ret;
+
+ if (I2C_SIZE(reg) != 1) {
+ v4l2_err(sd, "Wrong data size\n");
+ return -EINVAL;
+ }
+
+ ret = m5mols_read(sd, I2C_SIZE(reg), reg, &val_32);
+ if (ret)
+ return ret;
+
+ *val = (u8)val_32;
+ return ret;
+}
+
+int m5mols_read_u16(struct v4l2_subdev *sd, u32 reg, u16 *val)
+{
+ u32 val_32;
+ int ret;
+
+ if (I2C_SIZE(reg) != 2) {
+ v4l2_err(sd, "Wrong data size\n");
+ return -EINVAL;
+ }
+
+ ret = m5mols_read(sd, I2C_SIZE(reg), reg, &val_32);
+ if (ret)
+ return ret;
+
+ *val = (u16)val_32;
+ return ret;
+}
+
+int m5mols_read_u32(struct v4l2_subdev *sd, u32 reg, u32 *val)
+{
+ if (I2C_SIZE(reg) != 4) {
+ v4l2_err(sd, "Wrong data size\n");
+ return -EINVAL;
+ }
+
+ return m5mols_read(sd, I2C_SIZE(reg), reg, val);
+}
+
+/**
+ * m5mols_write - I2C command write function
+ * @reg: combination of size, category and command for the I2C packet
+ * @val: value to write
+ *
+ * Returns 0 on success, or else negative errno.
+ */
+int m5mols_write(struct v4l2_subdev *sd, u32 reg, u32 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct m5mols_info *info = to_m5mols(sd);
+ u8 wbuf[M5MOLS_I2C_MAX_SIZE + 4];
+ u8 category = I2C_CATEGORY(reg);
+ u8 cmd = I2C_COMMAND(reg);
+ u8 size = I2C_SIZE(reg);
+ u32 *buf = (u32 *)&wbuf[4];
+ struct i2c_msg msg[1];
+ int ret;
+
+ if (!client->adapter)
+ return -ENODEV;
+
+ if (size != 1 && size != 2 && size != 4) {
+ v4l2_err(sd, "Wrong data size\n");
+ return -EINVAL;
+ }
+
+ msg->addr = client->addr;
+ msg->flags = 0;
+ msg->len = (u16)size + 4;
+ msg->buf = wbuf;
+ wbuf[0] = size + 4;
+ wbuf[1] = M5MOLS_BYTE_WRITE;
+ wbuf[2] = category;
+ wbuf[3] = cmd;
+
+ *buf = m5mols_swap_byte((u8 *)&val, size);
+
+ usleep_range(200, 200);
+
+ ret = i2c_transfer(client->adapter, msg, 1);
+ if (ret == 1)
+ return 0;
+
+ if (info->isp_ready)
+ v4l2_err(sd, "write failed: cat:%02x cmd:%02x ret:%d\n",
+ category, cmd, ret);
+
+ return ret < 0 ? ret : -EIO;
+}
+
+/**
+ * m5mols_busy_wait - Busy waiting with I2C register polling
+ * @reg: the I2C_REG() address of an 8-bit status register to check
+ * @value: expected status register value
+ * @mask: bit mask for the read status register value
+ * @timeout: timeout in miliseconds, or -1 for default timeout
+ *
+ * The @reg register value is ORed with @mask before comparing with @value.
+ *
+ * Return: 0 if the requested condition became true within less than
+ * @timeout ms, or else negative errno.
+ */
+int m5mols_busy_wait(struct v4l2_subdev *sd, u32 reg, u32 value, u32 mask,
+ int timeout)
+{
+ int ms = timeout < 0 ? M5MOLS_BUSY_WAIT_DEF_TIMEOUT : timeout;
+ unsigned long end = jiffies + msecs_to_jiffies(ms);
+ u8 status;
+
+ do {
+ int ret = m5mols_read_u8(sd, reg, &status);
+
+ if (ret < 0 && !(mask & M5MOLS_I2C_RDY_WAIT_FL))
+ return ret;
+ if (!ret && (status & mask & 0xff) == (value & 0xff))
+ return 0;
+ usleep_range(100, 250);
+ } while (ms > 0 && time_is_after_jiffies(end));
+
+ return -EBUSY;
+}
+
+/**
+ * m5mols_enable_interrupt - Clear interrupt pending bits and unmask interrupts
+ *
+ * Before writing desired interrupt value the INT_FACTOR register should
+ * be read to clear pending interrupts.
+ */
+int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg)
+{
+ struct m5mols_info *info = to_m5mols(sd);
+ u8 mask = is_available_af(info) ? REG_INT_AF : 0;
+ u8 dummy;
+ int ret;
+
+ ret = m5mols_read_u8(sd, SYSTEM_INT_FACTOR, &dummy);
+ if (!ret)
+ ret = m5mols_write(sd, SYSTEM_INT_ENABLE, reg & ~mask);
+ return ret;
+}
+
+int m5mols_wait_interrupt(struct v4l2_subdev *sd, u8 irq_mask, u32 timeout)
+{
+ struct m5mols_info *info = to_m5mols(sd);
+
+ int ret = wait_event_interruptible_timeout(info->irq_waitq,
+ atomic_add_unless(&info->irq_done, -1, 0),
+ msecs_to_jiffies(timeout));
+ if (ret <= 0)
+ return ret ? ret : -ETIMEDOUT;
+
+ return m5mols_busy_wait(sd, SYSTEM_INT_FACTOR, irq_mask,
+ M5MOLS_I2C_RDY_WAIT_FL | irq_mask, -1);
+}
+
+/**
+ * m5mols_reg_mode - Write the mode and check busy status
+ *
+ * It always accompanies a little delay changing the M-5MOLS mode, so it is
+ * needed checking current busy status to guarantee right mode.
+ */
+static int m5mols_reg_mode(struct v4l2_subdev *sd, u8 mode)
+{
+ int ret = m5mols_write(sd, SYSTEM_SYSMODE, mode);
+ if (ret < 0)
+ return ret;
+ return m5mols_busy_wait(sd, SYSTEM_SYSMODE, mode, 0xff,
+ M5MOLS_MODE_CHANGE_TIMEOUT);
+}
+
+/**
+ * m5mols_mode - manage the M-5MOLS's mode
+ * @mode: the required operation mode
+ *
+ * The commands of M-5MOLS are grouped into specific modes. Each functionality
+ * can be guaranteed only when the sensor is operating in mode which which
+ * a command belongs to.
+ */
+int m5mols_mode(struct m5mols_info *info, u8 mode)
+{
+ struct v4l2_subdev *sd = &info->sd;
+ int ret = -EINVAL;
+ u8 reg;
+
+ if (mode < REG_PARAMETER || mode > REG_CAPTURE)
+ return ret;
+
+ ret = m5mols_read_u8(sd, SYSTEM_SYSMODE, &reg);
+ if (ret || reg == mode)
+ return ret;
+
+ switch (reg) {
+ case REG_PARAMETER:
+ ret = m5mols_reg_mode(sd, REG_MONITOR);
+ if (mode == REG_MONITOR)
+ break;
+ if (!ret)
+ ret = m5mols_reg_mode(sd, REG_CAPTURE);
+ break;
+
+ case REG_MONITOR:
+ if (mode == REG_PARAMETER) {
+ ret = m5mols_reg_mode(sd, REG_PARAMETER);
+ break;
+ }
+
+ ret = m5mols_reg_mode(sd, REG_CAPTURE);
+ break;
+
+ case REG_CAPTURE:
+ ret = m5mols_reg_mode(sd, REG_MONITOR);
+ if (mode == REG_MONITOR)
+ break;
+ if (!ret)
+ ret = m5mols_reg_mode(sd, REG_PARAMETER);
+ break;
+
+ default:
+ v4l2_warn(sd, "Wrong mode: %d\n", mode);
+ }
+
+ if (!ret)
+ info->mode = mode;
+
+ return ret;
+}
+
+/**
+ * m5mols_get_version - retrieve full revisions information of M-5MOLS
+ *
+ * The version information includes revisions of hardware and firmware,
+ * AutoFocus alghorithm version and the version string.
+ */
+static int m5mols_get_version(struct v4l2_subdev *sd)
+{
+ struct m5mols_info *info = to_m5mols(sd);
+ struct m5mols_version *ver = &info->ver;
+ u8 *str = ver->str;
+ int i;
+ int ret;
+
+ ret = m5mols_read_u8(sd, SYSTEM_VER_CUSTOMER, &ver->customer);
+ if (!ret)
+ ret = m5mols_read_u8(sd, SYSTEM_VER_PROJECT, &ver->project);
+ if (!ret)
+ ret = m5mols_read_u16(sd, SYSTEM_VER_FIRMWARE, &ver->fw);
+ if (!ret)
+ ret = m5mols_read_u16(sd, SYSTEM_VER_HARDWARE, &ver->hw);
+ if (!ret)
+ ret = m5mols_read_u16(sd, SYSTEM_VER_PARAMETER, &ver->param);
+ if (!ret)
+ ret = m5mols_read_u16(sd, SYSTEM_VER_AWB, &ver->awb);
+ if (!ret)
+ ret = m5mols_read_u8(sd, AF_VERSION, &ver->af);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < VERSION_STRING_SIZE; i++) {
+ ret = m5mols_read_u8(sd, SYSTEM_VER_STRING, &str[i]);
+ if (ret)
+ return ret;
+ }
+
+ ver->fw = be16_to_cpu(ver->fw);
+ ver->hw = be16_to_cpu(ver->hw);
+ ver->param = be16_to_cpu(ver->param);
+ ver->awb = be16_to_cpu(ver->awb);
+
+ v4l2_info(sd, "Manufacturer\t[%s]\n",
+ is_manufacturer(info, REG_SAMSUNG_ELECTRO) ?
+ "Samsung Electro-Machanics" :
+ is_manufacturer(info, REG_SAMSUNG_OPTICS) ?
+ "Samsung Fiber-Optics" :
+ is_manufacturer(info, REG_SAMSUNG_TECHWIN) ?
+ "Samsung Techwin" : "None");
+ v4l2_info(sd, "Customer/Project\t[0x%02x/0x%02x]\n",
+ info->ver.customer, info->ver.project);
+
+ if (!is_available_af(info))
+ v4l2_info(sd, "No support Auto Focus on this firmware\n");
+
+ return ret;
+}
+
+/**
+ * __find_restype - Lookup M-5MOLS resolution type according to pixel code
+ * @code: pixel code
+ */
+static enum m5mols_restype __find_restype(enum v4l2_mbus_pixelcode code)
+{
+ enum m5mols_restype type = M5MOLS_RESTYPE_MONITOR;
+
+ do {
+ if (code == m5mols_default_ffmt[type].code)
+ return type;
+ } while (type++ != SIZE_DEFAULT_FFMT);
+
+ return 0;
+}
+
+/**
+ * __find_resolution - Lookup preset and type of M-5MOLS's resolution
+ * @mf: pixel format to find/negotiate the resolution preset for
+ * @type: M-5MOLS resolution type
+ * @resolution: M-5MOLS resolution preset register value
+ *
+ * Find nearest resolution matching resolution preset and adjust mf
+ * to supported values.
+ */
+static int __find_resolution(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf,
+ enum m5mols_restype *type,
+ u32 *resolution)
+{
+ const struct m5mols_resolution *fsize = &m5mols_reg_res[0];
+ const struct m5mols_resolution *match = NULL;
+ enum m5mols_restype stype = __find_restype(mf->code);
+ int i = ARRAY_SIZE(m5mols_reg_res);
+ unsigned int min_err = ~0;
+
+ while (i--) {
+ int err;
+ if (stype == fsize->type) {
+ err = abs(fsize->width - mf->width)
+ + abs(fsize->height - mf->height);
+
+ if (err < min_err) {
+ min_err = err;
+ match = fsize;
+ }
+ }
+ fsize++;
+ }
+ if (match) {
+ mf->width = match->width;
+ mf->height = match->height;
+ *resolution = match->reg;
+ *type = stype;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static struct v4l2_mbus_framefmt *__find_format(struct m5mols_info *info,
+ struct v4l2_subdev_fh *fh,
+ enum v4l2_subdev_format_whence which,
+ enum m5mols_restype type)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL;
+
+ return &info->ffmt[type];
+}
+
+static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct m5mols_info *info = to_m5mols(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __find_format(info, fh, fmt->which, info->res_type);
+ if (!format)
+ return -EINVAL;
+
+ fmt->format = *format;
+ return 0;
+}
+
+static int m5mols_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct m5mols_info *info = to_m5mols(sd);
+ struct v4l2_mbus_framefmt *format = &fmt->format;
+ struct v4l2_mbus_framefmt *sfmt;
+ enum m5mols_restype type;
+ u32 resolution = 0;
+ int ret;
+
+ ret = __find_resolution(sd, format, &type, &resolution);
+ if (ret < 0)
+ return ret;
+
+ sfmt = __find_format(info, fh, fmt->which, type);
+ if (!sfmt)
+ return 0;
+
+
+ format->code = m5mols_default_ffmt[type].code;
+ format->colorspace = V4L2_COLORSPACE_JPEG;
+ format->field = V4L2_FIELD_NONE;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ *sfmt = *format;
+ info->resolution = resolution;
+ info->res_type = type;
+ }
+
+ return 0;
+}
+
+static int m5mols_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (!code || code->index >= SIZE_DEFAULT_FFMT)
+ return -EINVAL;
+
+ code->code = m5mols_default_ffmt[code->index].code;
+
+ return 0;
+}
+
+static struct v4l2_subdev_pad_ops m5mols_pad_ops = {
+ .enum_mbus_code = m5mols_enum_mbus_code,
+ .get_fmt = m5mols_get_fmt,
+ .set_fmt = m5mols_set_fmt,
+};
+
+/**
+ * m5mols_restore_controls - Apply current control values to the registers
+ *
+ * m5mols_do_scenemode() handles all parameters for which there is yet no
+ * individual control. It should be replaced at some point by setting each
+ * control individually, in required register set up order.
+ */
+int m5mols_restore_controls(struct m5mols_info *info)
+{
+ int ret;
+
+ if (info->ctrl_sync)
+ return 0;
+
+ ret = m5mols_do_scenemode(info, REG_SCENE_NORMAL);
+ if (ret)
+ return ret;
+
+ ret = v4l2_ctrl_handler_setup(&info->handle);
+ info->ctrl_sync = !ret;
+
+ return ret;
+}
+
+/**
+ * m5mols_start_monitor - Start the monitor mode
+ *
+ * Before applying the controls setup the resolution and frame rate
+ * in PARAMETER mode, and then switch over to MONITOR mode.
+ */
+static int m5mols_start_monitor(struct m5mols_info *info)
+{
+ struct v4l2_subdev *sd = &info->sd;
+ int ret;
+
+ ret = m5mols_mode(info, REG_PARAMETER);
+ if (!ret)
+ ret = m5mols_write(sd, PARM_MON_SIZE, info->resolution);
+ if (!ret)
+ ret = m5mols_write(sd, PARM_MON_FPS, REG_FPS_30);
+ if (!ret)
+ ret = m5mols_mode(info, REG_MONITOR);
+ if (!ret)
+ ret = m5mols_restore_controls(info);
+
+ return ret;
+}
+
+static int m5mols_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct m5mols_info *info = to_m5mols(sd);
+ u32 code = info->ffmt[info->res_type].code;
+
+ if (enable) {
+ int ret = -EINVAL;
+
+ if (is_code(code, M5MOLS_RESTYPE_MONITOR))
+ ret = m5mols_start_monitor(info);
+ if (is_code(code, M5MOLS_RESTYPE_CAPTURE))
+ ret = m5mols_start_capture(info);
+
+ return ret;
+ }
+
+ return m5mols_mode(info, REG_PARAMETER);
+}
+
+static const struct v4l2_subdev_video_ops m5mols_video_ops = {
+ .s_stream = m5mols_s_stream,
+};
+
+static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ struct m5mols_info *info = to_m5mols(sd);
+ int ispstate = info->mode;
+ int ret;
+
+ /*
+ * If needed, defer restoring the controls until
+ * the device is fully initialized.
+ */
+ if (!info->isp_ready) {
+ info->ctrl_sync = 0;
+ return 0;
+ }
+
+ ret = m5mols_mode(info, REG_PARAMETER);
+ if (ret < 0)
+ return ret;
+ ret = m5mols_set_ctrl(ctrl);
+ if (ret < 0)
+ return ret;
+ return m5mols_mode(info, ispstate);
+}
+
+static const struct v4l2_ctrl_ops m5mols_ctrl_ops = {
+ .s_ctrl = m5mols_s_ctrl,
+};
+
+static int m5mols_sensor_power(struct m5mols_info *info, bool enable)
+{
+ struct v4l2_subdev *sd = &info->sd;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ const struct m5mols_platform_data *pdata = info->pdata;
+ int ret;
+
+ if (info->power == enable)
+ return 0;
+
+ if (enable) {
+ if (info->set_power) {
+ ret = info->set_power(&client->dev, 1);
+ if (ret)
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies);
+ if (ret) {
+ info->set_power(&client->dev, 0);
+ return ret;
+ }
+
+ gpio_set_value(pdata->gpio_reset, !pdata->reset_polarity);
+ info->power = 1;
+
+ return ret;
+ }
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies);
+ if (ret)
+ return ret;
+
+ if (info->set_power)
+ info->set_power(&client->dev, 0);
+
+ gpio_set_value(pdata->gpio_reset, pdata->reset_polarity);
+
+ info->isp_ready = 0;
+ info->power = 0;
+
+ return ret;
+}
+
+/* m5mols_update_fw - optional firmware update routine */
+int __attribute__ ((weak)) m5mols_update_fw(struct v4l2_subdev *sd,
+ int (*set_power)(struct m5mols_info *, bool))
+{
+ return 0;
+}
+
+/**
+ * m5mols_fw_start - M-5MOLS internal ARM controller initialization
+ *
+ * Execute the M-5MOLS internal ARM controller initialization sequence.
+ * This function should be called after the supply voltage has been
+ * applied and before any requests to the device are made.
+ */
+static int m5mols_fw_start(struct v4l2_subdev *sd)
+{
+ struct m5mols_info *info = to_m5mols(sd);
+ int ret;
+
+ atomic_set(&info->irq_done, 0);
+ /* Wait until I2C slave is initialized in Flash Writer mode */
+ ret = m5mols_busy_wait(sd, FLASH_CAM_START, REG_IN_FLASH_MODE,
+ M5MOLS_I2C_RDY_WAIT_FL | 0xff, -1);
+ if (!ret)
+ ret = m5mols_write(sd, FLASH_CAM_START, REG_START_ARM_BOOT);
+ if (!ret)
+ ret = m5mols_wait_interrupt(sd, REG_INT_MODE, 2000);
+ if (ret < 0)
+ return ret;
+
+ info->isp_ready = 1;
+
+ ret = m5mols_get_version(sd);
+ if (!ret)
+ ret = m5mols_update_fw(sd, m5mols_sensor_power);
+ if (ret)
+ return ret;
+
+ v4l2_dbg(1, m5mols_debug, sd, "Success ARM Booting\n");
+
+ ret = m5mols_write(sd, PARM_INTERFACE, REG_INTERFACE_MIPI);
+ if (!ret)
+ ret = m5mols_enable_interrupt(sd,
+ REG_INT_AF | REG_INT_CAPTURE);
+
+ return ret;
+}
+
+static int m5mols_init_controls(struct m5mols_info *info)
+{
+ struct v4l2_subdev *sd = &info->sd;
+ u16 max_exposure;
+ u16 step_zoom;
+ int ret;
+
+ /* Determine value's range & step of controls for various FW version */
+ ret = m5mols_read_u16(sd, AE_MAX_GAIN_MON, &max_exposure);
+ if (!ret)
+ step_zoom = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1;
+ if (ret)
+ return ret;
+
+ v4l2_ctrl_handler_init(&info->handle, 6);
+ info->autowb = v4l2_ctrl_new_std(&info->handle,
+ &m5mols_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE,
+ 0, 1, 1, 0);
+ info->saturation = v4l2_ctrl_new_std(&info->handle,
+ &m5mols_ctrl_ops, V4L2_CID_SATURATION,
+ 1, 5, 1, 3);
+ info->zoom = v4l2_ctrl_new_std(&info->handle,
+ &m5mols_ctrl_ops, V4L2_CID_ZOOM_ABSOLUTE,
+ 1, 70, step_zoom, 1);
+ info->exposure = v4l2_ctrl_new_std(&info->handle,
+ &m5mols_ctrl_ops, V4L2_CID_EXPOSURE,
+ 0, max_exposure, 1, (int)max_exposure/2);
+ info->colorfx = v4l2_ctrl_new_std_menu(&info->handle,
+ &m5mols_ctrl_ops, V4L2_CID_COLORFX,
+ 4, (1 << V4L2_COLORFX_BW), V4L2_COLORFX_NONE);
+ info->autoexposure = v4l2_ctrl_new_std_menu(&info->handle,
+ &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO,
+ 1, 0, V4L2_EXPOSURE_AUTO);
+
+ sd->ctrl_handler = &info->handle;
+ if (info->handle.error) {
+ v4l2_err(sd, "Failed to initialize controls: %d\n", ret);
+ v4l2_ctrl_handler_free(&info->handle);
+ return info->handle.error;
+ }
+
+ v4l2_ctrl_cluster(2, &info->autoexposure);
+
+ return 0;
+}
+
+/**
+ * m5mols_s_power - Main sensor power control function
+ *
+ * To prevent breaking the lens when the sensor is powered off the Soft-Landing
+ * algorithm is called where available. The Soft-Landing algorithm availability
+ * dependends on the firmware provider.
+ */
+static int m5mols_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct m5mols_info *info = to_m5mols(sd);
+ int ret;
+
+ if (on) {
+ ret = m5mols_sensor_power(info, true);
+ if (!ret)
+ ret = m5mols_fw_start(sd);
+ return ret;
+ }
+
+ if (is_manufacturer(info, REG_SAMSUNG_TECHWIN)) {
+ ret = m5mols_mode(info, REG_MONITOR);
+ if (!ret)
+ ret = m5mols_write(sd, AF_EXECUTE, REG_AF_STOP);
+ if (!ret)
+ ret = m5mols_write(sd, AF_MODE, REG_AF_POWEROFF);
+ if (!ret)
+ ret = m5mols_busy_wait(sd, SYSTEM_STATUS, REG_AF_IDLE,
+ 0xff, -1);
+ if (ret < 0)
+ v4l2_warn(sd, "Soft landing lens failed\n");
+ }
+
+ ret = m5mols_sensor_power(info, false);
+ info->ctrl_sync = 0;
+
+ return ret;
+}
+
+static int m5mols_log_status(struct v4l2_subdev *sd)
+{
+ struct m5mols_info *info = to_m5mols(sd);
+
+ v4l2_ctrl_handler_log_status(&info->handle, sd->name);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_core_ops m5mols_core_ops = {
+ .s_power = m5mols_s_power,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .log_status = m5mols_log_status,
+};
+
+/*
+ * V4L2 subdev internal operations
+ */
+static int m5mols_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
+
+ *format = m5mols_default_ffmt[0];
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops m5mols_subdev_internal_ops = {
+ .open = m5mols_open,
+};
+
+static const struct v4l2_subdev_ops m5mols_ops = {
+ .core = &m5mols_core_ops,
+ .pad = &m5mols_pad_ops,
+ .video = &m5mols_video_ops,
+};
+
+static irqreturn_t m5mols_irq_handler(int irq, void *data)
+{
+ struct m5mols_info *info = to_m5mols(data);
+
+ atomic_set(&info->irq_done, 1);
+ wake_up_interruptible(&info->irq_waitq);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit m5mols_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct m5mols_platform_data *pdata = client->dev.platform_data;
+ struct m5mols_info *info;
+ struct v4l2_subdev *sd;
+ int ret;
+
+ if (pdata == NULL) {
+ dev_err(&client->dev, "No platform data\n");
+ return -EINVAL;
+ }
+
+ if (!gpio_is_valid(pdata->gpio_reset)) {
+ dev_err(&client->dev, "No valid RESET GPIO specified\n");
+ return -EINVAL;
+ }
+
+ if (!client->irq) {
+ dev_err(&client->dev, "Interrupt not assigned\n");
+ return -EINVAL;
+ }
+
+ info = kzalloc(sizeof(struct m5mols_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->pdata = pdata;
+ info->set_power = pdata->set_power;
+
+ ret = gpio_request(pdata->gpio_reset, "M5MOLS_NRST");
+ if (ret) {
+ dev_err(&client->dev, "Failed to request gpio: %d\n", ret);
+ goto out_free;
+ }
+ gpio_direction_output(pdata->gpio_reset, pdata->reset_polarity);
+
+ ret = regulator_bulk_get(&client->dev, ARRAY_SIZE(supplies), supplies);
+ if (ret) {
+ dev_err(&client->dev, "Failed to get regulators: %d\n", ret);
+ goto out_gpio;
+ }
+
+ sd = &info->sd;
+ v4l2_i2c_subdev_init(sd, client, &m5mols_ops);
+ strlcpy(sd->name, MODULE_NAME, sizeof(sd->name));
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ sd->internal_ops = &m5mols_subdev_internal_ops;
+ info->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&sd->entity, 1, &info->pad, 0);
+ if (ret < 0)
+ goto out_reg;
+ sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
+
+ init_waitqueue_head(&info->irq_waitq);
+ ret = request_irq(client->irq, m5mols_irq_handler,
+ IRQF_TRIGGER_RISING, MODULE_NAME, sd);
+ if (ret) {
+ dev_err(&client->dev, "Interrupt request failed: %d\n", ret);
+ goto out_me;
+ }
+ info->res_type = M5MOLS_RESTYPE_MONITOR;
+ info->ffmt[0] = m5mols_default_ffmt[0];
+ info->ffmt[1] = m5mols_default_ffmt[1];
+
+ ret = m5mols_sensor_power(info, true);
+ if (ret)
+ goto out_me;
+
+ ret = m5mols_fw_start(sd);
+ if (!ret)
+ ret = m5mols_init_controls(info);
+
+ m5mols_sensor_power(info, false);
+ if (!ret)
+ return 0;
+out_me:
+ media_entity_cleanup(&sd->entity);
+out_reg:
+ regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
+out_gpio:
+ gpio_free(pdata->gpio_reset);
+out_free:
+ kfree(info);
+ return ret;
+}
+
+static int __devexit m5mols_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct m5mols_info *info = to_m5mols(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+ free_irq(client->irq, sd);
+
+ regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
+ gpio_free(info->pdata->gpio_reset);
+ media_entity_cleanup(&sd->entity);
+ kfree(info);
+ return 0;
+}
+
+static const struct i2c_device_id m5mols_id[] = {
+ { MODULE_NAME, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, m5mols_id);
+
+static struct i2c_driver m5mols_i2c_driver = {
+ .driver = {
+ .name = MODULE_NAME,
+ },
+ .probe = m5mols_probe,
+ .remove = __devexit_p(m5mols_remove),
+ .id_table = m5mols_id,
+};
+
+module_i2c_driver(m5mols_i2c_driver);
+
+MODULE_AUTHOR("HeungJun Kim <riverful.kim@samsung.com>");
+MODULE_AUTHOR("Dongsoo Kim <dongsoo45.kim@samsung.com>");
+MODULE_DESCRIPTION("Fujitsu M-5MOLS 8M Pixel camera driver");
+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 00000000..ae4aced0
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols_reg.h
@@ -0,0 +1,361 @@
+/*
+ * Register map for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim <riverful.kim@samsung.com>
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef M5MOLS_REG_H
+#define M5MOLS_REG_H
+
+#define M5MOLS_I2C_MAX_SIZE 4
+#define M5MOLS_BYTE_READ 0x01
+#define M5MOLS_BYTE_WRITE 0x02
+
+#define I2C_CATEGORY(__cat) ((__cat >> 16) & 0xff)
+#define I2C_COMMAND(__comm) ((__comm >> 8) & 0xff)
+#define I2C_SIZE(__reg_s) ((__reg_s) & 0xff)
+#define I2C_REG(__cat, __cmd, __reg_s) ((__cat << 16) | (__cmd << 8) | __reg_s)
+
+/*
+ * Category section register
+ *
+ * The category means set including relevant command of M-5MOLS.
+ */
+#define CAT_SYSTEM 0x00
+#define CAT_PARAM 0x01
+#define CAT_MONITOR 0x02
+#define CAT_AE 0x03
+#define CAT_WB 0x06
+#define CAT_EXIF 0x07
+#define CAT_FD 0x09
+#define CAT_LENS 0x0a
+#define CAT_CAPT_PARM 0x0b
+#define CAT_CAPT_CTRL 0x0c
+#define CAT_FLASH 0x0f /* related to FW, revisions, booting */
+
+/*
+ * Category 0 - SYSTEM mode
+ *
+ * The SYSTEM mode in the M-5MOLS means area available to handle with the whole
+ * & all-round system of sensor. It deals with version/interrupt/setting mode &
+ * even sensor's status. Especially, the M-5MOLS sensor with ISP varies by
+ * packaging & manufacturer, even the customer and project code. And the
+ * function details may vary among them. The version information helps to
+ * determine what methods shall be used in the driver.
+ *
+ * There is many registers between customer version address and awb one. For
+ * more specific contents, see definition if file m5mols.h.
+ */
+#define SYSTEM_VER_CUSTOMER I2C_REG(CAT_SYSTEM, 0x00, 1)
+#define SYSTEM_VER_PROJECT I2C_REG(CAT_SYSTEM, 0x01, 1)
+#define SYSTEM_VER_FIRMWARE I2C_REG(CAT_SYSTEM, 0x02, 2)
+#define SYSTEM_VER_HARDWARE I2C_REG(CAT_SYSTEM, 0x04, 2)
+#define SYSTEM_VER_PARAMETER I2C_REG(CAT_SYSTEM, 0x06, 2)
+#define SYSTEM_VER_AWB I2C_REG(CAT_SYSTEM, 0x08, 2)
+
+#define SYSTEM_SYSMODE I2C_REG(CAT_SYSTEM, 0x0b, 1)
+#define REG_SYSINIT 0x00 /* SYSTEM mode */
+#define REG_PARAMETER 0x01 /* PARAMETER mode */
+#define REG_MONITOR 0x02 /* MONITOR mode */
+#define REG_CAPTURE 0x03 /* CAPTURE mode */
+
+#define SYSTEM_CMD(__cmd) I2C_REG(CAT_SYSTEM, cmd, 1)
+#define SYSTEM_VER_STRING I2C_REG(CAT_SYSTEM, 0x0a, 1)
+#define REG_SAMSUNG_ELECTRO "SE" /* Samsung Electro-Mechanics */
+#define REG_SAMSUNG_OPTICS "OP" /* Samsung Fiber-Optics */
+#define REG_SAMSUNG_TECHWIN "TB" /* Samsung Techwin */
+/* SYSTEM mode status */
+#define SYSTEM_STATUS I2C_REG(CAT_SYSTEM, 0x0c, 1)
+
+/* Interrupt pending register */
+#define SYSTEM_INT_FACTOR I2C_REG(CAT_SYSTEM, 0x10, 1)
+/* interrupt enable register */
+#define SYSTEM_INT_ENABLE I2C_REG(CAT_SYSTEM, 0x11, 1)
+#define REG_INT_MODE (1 << 0)
+#define REG_INT_AF (1 << 1)
+#define REG_INT_ZOOM (1 << 2)
+#define REG_INT_CAPTURE (1 << 3)
+#define REG_INT_FRAMESYNC (1 << 4)
+#define REG_INT_FD (1 << 5)
+#define REG_INT_LENS_INIT (1 << 6)
+#define REG_INT_SOUND (1 << 7)
+#define REG_INT_MASK 0x0f
+
+/*
+ * category 1 - PARAMETER mode
+ *
+ * This category supports function of camera features of M-5MOLS. It means we
+ * can handle with preview(MONITOR) resolution size/frame per second/interface
+ * between the sensor and the Application Processor/even the image effect.
+ */
+
+/* Resolution in the MONITOR mode */
+#define PARM_MON_SIZE I2C_REG(CAT_PARAM, 0x01, 1)
+
+/* Frame rate */
+#define PARM_MON_FPS I2C_REG(CAT_PARAM, 0x02, 1)
+#define REG_FPS_30 0x02
+
+/* Video bus between the sensor and a host processor */
+#define PARM_INTERFACE I2C_REG(CAT_PARAM, 0x00, 1)
+#define REG_INTERFACE_MIPI 0x02
+
+/* Image effects */
+#define PARM_EFFECT I2C_REG(CAT_PARAM, 0x0b, 1)
+#define REG_EFFECT_OFF 0x00
+#define REG_EFFECT_NEGA 0x01
+#define REG_EFFECT_EMBOSS 0x06
+#define REG_EFFECT_OUTLINE 0x07
+#define REG_EFFECT_WATERCOLOR 0x08
+
+/*
+ * Category 2 - MONITOR mode
+ *
+ * The MONITOR mode is same as preview mode as we said. The M-5MOLS has another
+ * mode named "Preview", but this preview mode is used at the case specific
+ * vider-recording mode. This mmode supports only YUYV format. On the other
+ * hand, the JPEG & RAW formats is supports by CAPTURE mode. And, there are
+ * another options like zoom/color effect(different with effect in PARAMETER
+ * mode)/anti hand shaking algorithm.
+ */
+
+/* Target digital zoom position */
+#define MON_ZOOM I2C_REG(CAT_MONITOR, 0x01, 1)
+
+/* CR value for color effect */
+#define MON_CFIXR I2C_REG(CAT_MONITOR, 0x0a, 1)
+/* CB value for color effect */
+#define MON_CFIXB I2C_REG(CAT_MONITOR, 0x09, 1)
+#define REG_CFIXB_SEPIA 0xd8
+#define REG_CFIXR_SEPIA 0x18
+
+#define MON_EFFECT I2C_REG(CAT_MONITOR, 0x0b, 1)
+#define REG_COLOR_EFFECT_OFF 0x00
+#define REG_COLOR_EFFECT_ON 0x01
+
+/* Chroma enable */
+#define MON_CHROMA_EN I2C_REG(CAT_MONITOR, 0x10, 1)
+/* Chroma level */
+#define MON_CHROMA_LVL I2C_REG(CAT_MONITOR, 0x0f, 1)
+#define REG_CHROMA_OFF 0x00
+#define REG_CHROMA_ON 0x01
+
+/* Sharpness on/off */
+#define MON_EDGE_EN I2C_REG(CAT_MONITOR, 0x12, 1)
+/* Sharpness level */
+#define MON_EDGE_LVL I2C_REG(CAT_MONITOR, 0x11, 1)
+#define REG_EDGE_OFF 0x00
+#define REG_EDGE_ON 0x01
+
+/* Set color tone (contrast) */
+#define MON_TONE_CTL I2C_REG(CAT_MONITOR, 0x25, 1)
+
+/*
+ * Category 3 - Auto Exposure
+ *
+ * The M-5MOLS exposure capbility is detailed as which is similar to digital
+ * camera. This category supports AE locking/various AE mode(range of exposure)
+ * /ISO/flickering/EV bias/shutter/meteoring, and anything else. And the
+ * maximum/minimum exposure gain value depending on M-5MOLS firmware, may be
+ * different. So, this category also provide getting the max/min values. And,
+ * each MONITOR and CAPTURE mode has each gain/shutter/max exposure values.
+ */
+
+/* Auto Exposure locking */
+#define AE_LOCK I2C_REG(CAT_AE, 0x00, 1)
+#define REG_AE_UNLOCK 0x00
+#define REG_AE_LOCK 0x01
+
+/* Auto Exposure algorithm mode */
+#define AE_MODE I2C_REG(CAT_AE, 0x01, 1)
+#define REG_AE_OFF 0x00 /* AE off */
+#define REG_AE_ALL 0x01 /* calc AE in all block integral */
+#define REG_AE_CENTER 0x03 /* calc AE in center weighted */
+#define REG_AE_SPOT 0x06 /* calc AE in specific spot */
+
+#define AE_ISO I2C_REG(CAT_AE, 0x05, 1)
+#define REG_ISO_AUTO 0x00
+#define REG_ISO_50 0x01
+#define REG_ISO_100 0x02
+#define REG_ISO_200 0x03
+#define REG_ISO_400 0x04
+#define REG_ISO_800 0x05
+
+/* EV (scenemode) preset for MONITOR */
+#define AE_EV_PRESET_MONITOR I2C_REG(CAT_AE, 0x0a, 1)
+/* EV (scenemode) preset for CAPTURE */
+#define AE_EV_PRESET_CAPTURE I2C_REG(CAT_AE, 0x0b, 1)
+#define REG_SCENE_NORMAL 0x00
+#define REG_SCENE_PORTRAIT 0x01
+#define REG_SCENE_LANDSCAPE 0x02
+#define REG_SCENE_SPORTS 0x03
+#define REG_SCENE_PARTY_INDOOR 0x04
+#define REG_SCENE_BEACH_SNOW 0x05
+#define REG_SCENE_SUNSET 0x06
+#define REG_SCENE_DAWN_DUSK 0x07
+#define REG_SCENE_FALL 0x08
+#define REG_SCENE_NIGHT 0x09
+#define REG_SCENE_AGAINST_LIGHT 0x0a
+#define REG_SCENE_FIRE 0x0b
+#define REG_SCENE_TEXT 0x0c
+#define REG_SCENE_CANDLE 0x0d
+
+/* Manual gain in MONITOR mode */
+#define AE_MAN_GAIN_MON I2C_REG(CAT_AE, 0x12, 2)
+/* Maximum gain in MONITOR mode */
+#define AE_MAX_GAIN_MON I2C_REG(CAT_AE, 0x1a, 2)
+/* Manual gain in CAPTURE mode */
+#define AE_MAN_GAIN_CAP I2C_REG(CAT_AE, 0x26, 2)
+
+#define AE_INDEX I2C_REG(CAT_AE, 0x38, 1)
+#define REG_AE_INDEX_20_NEG 0x00
+#define REG_AE_INDEX_15_NEG 0x01
+#define REG_AE_INDEX_10_NEG 0x02
+#define REG_AE_INDEX_05_NEG 0x03
+#define REG_AE_INDEX_00 0x04
+#define REG_AE_INDEX_05_POS 0x05
+#define REG_AE_INDEX_10_POS 0x06
+#define REG_AE_INDEX_15_POS 0x07
+#define REG_AE_INDEX_20_POS 0x08
+
+/*
+ * Category 6 - White Balance
+ */
+
+/* Auto Whitebalance locking */
+#define AWB_LOCK I2C_REG(CAT_WB, 0x00, 1)
+#define REG_AWB_UNLOCK 0x00
+#define REG_AWB_LOCK 0x01
+
+#define AWB_MODE I2C_REG(CAT_WB, 0x02, 1)
+#define REG_AWB_AUTO 0x01 /* AWB off */
+#define REG_AWB_PRESET 0x02 /* AWB preset */
+
+/* Manual WB (preset) */
+#define AWB_MANUAL I2C_REG(CAT_WB, 0x03, 1)
+#define REG_AWB_INCANDESCENT 0x01
+#define REG_AWB_FLUORESCENT_1 0x02
+#define REG_AWB_FLUORESCENT_2 0x03
+#define REG_AWB_DAYLIGHT 0x04
+#define REG_AWB_CLOUDY 0x05
+#define REG_AWB_SHADE 0x06
+#define REG_AWB_HORIZON 0x07
+#define REG_AWB_LEDLIGHT 0x09
+
+/*
+ * Category 7 - EXIF information
+ */
+#define EXIF_INFO_EXPTIME_NU I2C_REG(CAT_EXIF, 0x00, 4)
+#define EXIF_INFO_EXPTIME_DE I2C_REG(CAT_EXIF, 0x04, 4)
+#define EXIF_INFO_TV_NU I2C_REG(CAT_EXIF, 0x08, 4)
+#define EXIF_INFO_TV_DE I2C_REG(CAT_EXIF, 0x0c, 4)
+#define EXIF_INFO_AV_NU I2C_REG(CAT_EXIF, 0x10, 4)
+#define EXIF_INFO_AV_DE I2C_REG(CAT_EXIF, 0x14, 4)
+#define EXIF_INFO_BV_NU I2C_REG(CAT_EXIF, 0x18, 4)
+#define EXIF_INFO_BV_DE I2C_REG(CAT_EXIF, 0x1c, 4)
+#define EXIF_INFO_EBV_NU I2C_REG(CAT_EXIF, 0x20, 4)
+#define EXIF_INFO_EBV_DE I2C_REG(CAT_EXIF, 0x24, 4)
+#define EXIF_INFO_ISO I2C_REG(CAT_EXIF, 0x28, 2)
+#define EXIF_INFO_FLASH I2C_REG(CAT_EXIF, 0x2a, 2)
+#define EXIF_INFO_SDR I2C_REG(CAT_EXIF, 0x2c, 2)
+#define EXIF_INFO_QVAL I2C_REG(CAT_EXIF, 0x2e, 2)
+
+/*
+ * Category 9 - Face Detection
+ */
+#define FD_CTL I2C_REG(CAT_FD, 0x00, 1)
+#define BIT_FD_EN 0
+#define BIT_FD_DRAW_FACE_FRAME 4
+#define BIT_FD_DRAW_SMILE_LVL 6
+#define REG_FD(shift) (1 << shift)
+#define REG_FD_OFF 0x0
+
+/*
+ * Category A - Lens Parameter
+ */
+#define AF_MODE I2C_REG(CAT_LENS, 0x01, 1)
+#define REG_AF_NORMAL 0x00 /* Normal AF, one time */
+#define REG_AF_MACRO 0x01 /* Macro AF, one time */
+#define REG_AF_POWEROFF 0x07
+
+#define AF_EXECUTE I2C_REG(CAT_LENS, 0x02, 1)
+#define REG_AF_STOP 0x00
+#define REG_AF_EXE_AUTO 0x01
+#define REG_AF_EXE_CAF 0x02
+
+#define AF_STATUS I2C_REG(CAT_LENS, 0x03, 1)
+#define REG_AF_FAIL 0x00
+#define REG_AF_SUCCESS 0x02
+#define REG_AF_IDLE 0x04
+#define REG_AF_BUSY 0x05
+
+#define AF_VERSION I2C_REG(CAT_LENS, 0x0a, 1)
+
+/*
+ * Category B - CAPTURE Parameter
+ */
+#define CAPP_YUVOUT_MAIN I2C_REG(CAT_CAPT_PARM, 0x00, 1)
+#define REG_YUV422 0x00
+#define REG_BAYER10 0x05
+#define REG_BAYER8 0x06
+#define REG_JPEG 0x10
+
+#define CAPP_MAIN_IMAGE_SIZE I2C_REG(CAT_CAPT_PARM, 0x01, 1)
+
+#define CAPP_MCC_MODE I2C_REG(CAT_CAPT_PARM, 0x1d, 1)
+#define REG_MCC_OFF 0x00
+#define REG_MCC_NORMAL 0x01
+
+#define CAPP_WDR_EN I2C_REG(CAT_CAPT_PARM, 0x2c, 1)
+#define REG_WDR_OFF 0x00
+#define REG_WDR_ON 0x01
+#define REG_WDR_AUTO 0x02
+
+#define CAPP_LIGHT_CTRL I2C_REG(CAT_CAPT_PARM, 0x40, 1)
+#define REG_LIGHT_OFF 0x00
+#define REG_LIGHT_ON 0x01
+#define REG_LIGHT_AUTO 0x02
+
+#define CAPP_FLASH_CTRL I2C_REG(CAT_CAPT_PARM, 0x41, 1)
+#define REG_FLASH_OFF 0x00
+#define REG_FLASH_ON 0x01
+#define REG_FLASH_AUTO 0x02
+
+/*
+ * Category C - CAPTURE Control
+ */
+#define CAPC_MODE I2C_REG(CAT_CAPT_CTRL, 0x00, 1)
+#define REG_CAP_NONE 0x00
+#define REG_CAP_ANTI_SHAKE 0x02
+
+/* Select single- or multi-shot capture */
+#define CAPC_SEL_FRAME I2C_REG(CAT_CAPT_CTRL, 0x06, 1)
+
+#define CAPC_START I2C_REG(CAT_CAPT_CTRL, 0x09, 1)
+#define REG_CAP_START_MAIN 0x01
+#define REG_CAP_START_THUMB 0x03
+
+#define CAPC_IMAGE_SIZE I2C_REG(CAT_CAPT_CTRL, 0x0d, 4)
+#define CAPC_THUMB_SIZE I2C_REG(CAT_CAPT_CTRL, 0x11, 4)
+
+/*
+ * Category F - Flash
+ *
+ * This mode provides functions about internal flash stuff and system startup.
+ */
+
+/* Starts internal ARM core booting after power-up */
+#define FLASH_CAM_START I2C_REG(CAT_FLASH, 0x12, 1)
+#define REG_START_ARM_BOOT 0x01 /* write value */
+#define REG_IN_FLASH_MODE 0x00 /* read value */
+
+#endif /* M5MOLS_REG_H */
diff --git a/drivers/media/video/marvell-ccic/Kconfig b/drivers/media/video/marvell-ccic/Kconfig
new file mode 100644
index 00000000..bf739e3b
--- /dev/null
+++ b/drivers/media/video/marvell-ccic/Kconfig
@@ -0,0 +1,23 @@
+config VIDEO_CAFE_CCIC
+ tristate "Marvell 88ALP01 (Cafe) CMOS Camera Controller support"
+ depends on PCI && I2C && VIDEO_V4L2
+ select VIDEO_OV7670
+ select VIDEOBUF2_VMALLOC
+ select VIDEOBUF2_DMA_CONTIG
+ ---help---
+ This is a video4linux2 driver for the Marvell 88ALP01 integrated
+ CMOS camera controller. This is the controller found on first-
+ generation OLPC systems.
+
+config VIDEO_MMP_CAMERA
+ tristate "Marvell Armada 610 integrated camera controller support"
+ depends on ARCH_MMP && I2C && VIDEO_V4L2
+ select VIDEO_OV7670
+ select I2C_GPIO
+ select VIDEOBUF2_DMA_SG
+ ---help---
+ This is a Video4Linux2 driver for the integrated camera
+ controller found on Marvell Armada 610 application
+ processors (and likely beyond). This is the controller found
+ in OLPC XO 1.75 systems.
+
diff --git a/drivers/media/video/marvell-ccic/Makefile b/drivers/media/video/marvell-ccic/Makefile
new file mode 100644
index 00000000..05a792c5
--- /dev/null
+++ b/drivers/media/video/marvell-ccic/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o
+cafe_ccic-y := cafe-driver.o mcam-core.o
+
+obj-$(CONFIG_VIDEO_MMP_CAMERA) += mmp_camera.o
+mmp_camera-y := mmp-driver.o mcam-core.o
+
diff --git a/drivers/media/video/marvell-ccic/cafe-driver.c b/drivers/media/video/marvell-ccic/cafe-driver.c
new file mode 100644
index 00000000..d030f9be
--- /dev/null
+++ b/drivers/media/video/marvell-ccic/cafe-driver.c
@@ -0,0 +1,654 @@
+/*
+ * A driver for the CMOS camera controller in the Marvell 88ALP01 "cafe"
+ * multifunction chip. Currently works with the Omnivision OV7670
+ * sensor.
+ *
+ * The data sheet for this device can be found at:
+ * http://www.marvell.com/products/pc_connectivity/88alp01/
+ *
+ * Copyright 2006-11 One Laptop Per Child Association, Inc.
+ * Copyright 2006-11 Jonathan Corbet <corbet@lwn.net>
+ *
+ * Written by Jonathan Corbet, corbet@lwn.net.
+ *
+ * v4l2_device/v4l2_subdev conversion by:
+ * Copyright (C) 2009 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * This file may be distributed under the terms of the GNU General
+ * Public License, version 2.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include "mcam-core.h"
+
+#define CAFE_VERSION 0x000002
+
+
+/*
+ * Parameters.
+ */
+MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
+MODULE_DESCRIPTION("Marvell 88ALP01 CMOS Camera Controller driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("Video");
+
+
+
+
+struct cafe_camera {
+ int registered; /* Fully initialized? */
+ struct mcam_camera mcam;
+ struct pci_dev *pdev;
+ wait_queue_head_t smbus_wait; /* Waiting on i2c events */
+};
+
+/*
+ * Most of the camera controller registers are defined in mcam-core.h,
+ * but the Cafe platform has some additional registers of its own;
+ * they are described here.
+ */
+
+/*
+ * "General purpose register" has a couple of GPIOs used for sensor
+ * power and reset on OLPC XO 1.0 systems.
+ */
+#define REG_GPR 0xb4
+#define GPR_C1EN 0x00000020 /* Pad 1 (power down) enable */
+#define GPR_C0EN 0x00000010 /* Pad 0 (reset) enable */
+#define GPR_C1 0x00000002 /* Control 1 value */
+/*
+ * Control 0 is wired to reset on OLPC machines. For ov7x sensors,
+ * it is active low.
+ */
+#define GPR_C0 0x00000001 /* Control 0 value */
+
+/*
+ * These registers control the SMBUS module for communicating
+ * with the sensor.
+ */
+#define REG_TWSIC0 0xb8 /* TWSI (smbus) control 0 */
+#define TWSIC0_EN 0x00000001 /* TWSI enable */
+#define TWSIC0_MODE 0x00000002 /* 1 = 16-bit, 0 = 8-bit */
+#define TWSIC0_SID 0x000003fc /* Slave ID */
+/*
+ * Subtle trickery: the slave ID field starts with bit 2. But the
+ * Linux i2c stack wants to treat the bottommost bit as a separate
+ * read/write bit, which is why slave ID's are usually presented
+ * >>1. For consistency with that behavior, we shift over three
+ * bits instead of two.
+ */
+#define TWSIC0_SID_SHIFT 3
+#define TWSIC0_CLKDIV 0x0007fc00 /* Clock divider */
+#define TWSIC0_MASKACK 0x00400000 /* Mask ack from sensor */
+#define TWSIC0_OVMAGIC 0x00800000 /* Make it work on OV sensors */
+
+#define REG_TWSIC1 0xbc /* TWSI control 1 */
+#define TWSIC1_DATA 0x0000ffff /* Data to/from camchip */
+#define TWSIC1_ADDR 0x00ff0000 /* Address (register) */
+#define TWSIC1_ADDR_SHIFT 16
+#define TWSIC1_READ 0x01000000 /* Set for read op */
+#define TWSIC1_WSTAT 0x02000000 /* Write status */
+#define TWSIC1_RVALID 0x04000000 /* Read data valid */
+#define TWSIC1_ERROR 0x08000000 /* Something screwed up */
+
+/*
+ * Here's the weird global control registers
+ */
+#define REG_GL_CSR 0x3004 /* Control/status register */
+#define GCSR_SRS 0x00000001 /* SW Reset set */
+#define GCSR_SRC 0x00000002 /* SW Reset clear */
+#define GCSR_MRS 0x00000004 /* Master reset set */
+#define GCSR_MRC 0x00000008 /* HW Reset clear */
+#define GCSR_CCIC_EN 0x00004000 /* CCIC Clock enable */
+#define REG_GL_IMASK 0x300c /* Interrupt mask register */
+#define GIMSK_CCIC_EN 0x00000004 /* CCIC Interrupt enable */
+
+#define REG_GL_FCR 0x3038 /* GPIO functional control register */
+#define GFCR_GPIO_ON 0x08 /* Camera GPIO enabled */
+#define REG_GL_GPIOR 0x315c /* GPIO register */
+#define GGPIO_OUT 0x80000 /* GPIO output */
+#define GGPIO_VAL 0x00008 /* Output pin value */
+
+#define REG_LEN (REG_GL_IMASK + 4)
+
+
+/*
+ * Debugging and related.
+ */
+#define cam_err(cam, fmt, arg...) \
+ dev_err(&(cam)->pdev->dev, fmt, ##arg);
+#define cam_warn(cam, fmt, arg...) \
+ dev_warn(&(cam)->pdev->dev, fmt, ##arg);
+
+/* -------------------------------------------------------------------- */
+/*
+ * The I2C/SMBUS interface to the camera itself starts here. The
+ * controller handles SMBUS itself, presenting a relatively simple register
+ * interface; all we have to do is to tell it where to route the data.
+ */
+#define CAFE_SMBUS_TIMEOUT (HZ) /* generous */
+
+static inline struct cafe_camera *to_cam(struct v4l2_device *dev)
+{
+ struct mcam_camera *m = container_of(dev, struct mcam_camera, v4l2_dev);
+ return container_of(m, struct cafe_camera, mcam);
+}
+
+
+static int cafe_smbus_write_done(struct mcam_camera *mcam)
+{
+ unsigned long flags;
+ int c1;
+
+ /*
+ * We must delay after the interrupt, or the controller gets confused
+ * and never does give us good status. Fortunately, we don't do this
+ * often.
+ */
+ udelay(20);
+ spin_lock_irqsave(&mcam->dev_lock, flags);
+ c1 = mcam_reg_read(mcam, REG_TWSIC1);
+ spin_unlock_irqrestore(&mcam->dev_lock, flags);
+ return (c1 & (TWSIC1_WSTAT|TWSIC1_ERROR)) != TWSIC1_WSTAT;
+}
+
+static int cafe_smbus_write_data(struct cafe_camera *cam,
+ u16 addr, u8 command, u8 value)
+{
+ unsigned int rval;
+ unsigned long flags;
+ struct mcam_camera *mcam = &cam->mcam;
+
+ spin_lock_irqsave(&mcam->dev_lock, flags);
+ rval = TWSIC0_EN | ((addr << TWSIC0_SID_SHIFT) & TWSIC0_SID);
+ rval |= TWSIC0_OVMAGIC; /* Make OV sensors work */
+ /*
+ * Marvell sez set clkdiv to all 1's for now.
+ */
+ rval |= TWSIC0_CLKDIV;
+ mcam_reg_write(mcam, REG_TWSIC0, rval);
+ (void) mcam_reg_read(mcam, REG_TWSIC1); /* force write */
+ rval = value | ((command << TWSIC1_ADDR_SHIFT) & TWSIC1_ADDR);
+ mcam_reg_write(mcam, REG_TWSIC1, rval);
+ spin_unlock_irqrestore(&mcam->dev_lock, flags);
+
+ /* Unfortunately, reading TWSIC1 too soon after sending a command
+ * causes the device to die.
+ * Use a busy-wait because we often send a large quantity of small
+ * commands at-once; using msleep() would cause a lot of context
+ * switches which take longer than 2ms, resulting in a noticeable
+ * boot-time and capture-start delays.
+ */
+ mdelay(2);
+
+ /*
+ * Another sad fact is that sometimes, commands silently complete but
+ * cafe_smbus_write_done() never becomes aware of this.
+ * This happens at random and appears to possible occur with any
+ * command.
+ * We don't understand why this is. We work around this issue
+ * with the timeout in the wait below, assuming that all commands
+ * complete within the timeout.
+ */
+ wait_event_timeout(cam->smbus_wait, cafe_smbus_write_done(mcam),
+ CAFE_SMBUS_TIMEOUT);
+
+ spin_lock_irqsave(&mcam->dev_lock, flags);
+ rval = mcam_reg_read(mcam, REG_TWSIC1);
+ spin_unlock_irqrestore(&mcam->dev_lock, flags);
+
+ if (rval & TWSIC1_WSTAT) {
+ cam_err(cam, "SMBUS write (%02x/%02x/%02x) timed out\n", addr,
+ command, value);
+ return -EIO;
+ }
+ if (rval & TWSIC1_ERROR) {
+ cam_err(cam, "SMBUS write (%02x/%02x/%02x) error\n", addr,
+ command, value);
+ return -EIO;
+ }
+ return 0;
+}
+
+
+
+static int cafe_smbus_read_done(struct mcam_camera *mcam)
+{
+ unsigned long flags;
+ int c1;
+
+ /*
+ * We must delay after the interrupt, or the controller gets confused
+ * and never does give us good status. Fortunately, we don't do this
+ * often.
+ */
+ udelay(20);
+ spin_lock_irqsave(&mcam->dev_lock, flags);
+ c1 = mcam_reg_read(mcam, REG_TWSIC1);
+ spin_unlock_irqrestore(&mcam->dev_lock, flags);
+ return c1 & (TWSIC1_RVALID|TWSIC1_ERROR);
+}
+
+
+
+static int cafe_smbus_read_data(struct cafe_camera *cam,
+ u16 addr, u8 command, u8 *value)
+{
+ unsigned int rval;
+ unsigned long flags;
+ struct mcam_camera *mcam = &cam->mcam;
+
+ spin_lock_irqsave(&mcam->dev_lock, flags);
+ rval = TWSIC0_EN | ((addr << TWSIC0_SID_SHIFT) & TWSIC0_SID);
+ rval |= TWSIC0_OVMAGIC; /* Make OV sensors work */
+ /*
+ * Marvel sez set clkdiv to all 1's for now.
+ */
+ rval |= TWSIC0_CLKDIV;
+ mcam_reg_write(mcam, REG_TWSIC0, rval);
+ (void) mcam_reg_read(mcam, REG_TWSIC1); /* force write */
+ rval = TWSIC1_READ | ((command << TWSIC1_ADDR_SHIFT) & TWSIC1_ADDR);
+ mcam_reg_write(mcam, REG_TWSIC1, rval);
+ spin_unlock_irqrestore(&mcam->dev_lock, flags);
+
+ wait_event_timeout(cam->smbus_wait,
+ cafe_smbus_read_done(mcam), CAFE_SMBUS_TIMEOUT);
+ spin_lock_irqsave(&mcam->dev_lock, flags);
+ rval = mcam_reg_read(mcam, REG_TWSIC1);
+ spin_unlock_irqrestore(&mcam->dev_lock, flags);
+
+ if (rval & TWSIC1_ERROR) {
+ cam_err(cam, "SMBUS read (%02x/%02x) error\n", addr, command);
+ return -EIO;
+ }
+ if (!(rval & TWSIC1_RVALID)) {
+ cam_err(cam, "SMBUS read (%02x/%02x) timed out\n", addr,
+ command);
+ return -EIO;
+ }
+ *value = rval & 0xff;
+ return 0;
+}
+
+/*
+ * Perform a transfer over SMBUS. This thing is called under
+ * the i2c bus lock, so we shouldn't race with ourselves...
+ */
+static int cafe_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
+ unsigned short flags, char rw, u8 command,
+ int size, union i2c_smbus_data *data)
+{
+ struct cafe_camera *cam = i2c_get_adapdata(adapter);
+ int ret = -EINVAL;
+
+ /*
+ * This interface would appear to only do byte data ops. OK
+ * it can do word too, but the cam chip has no use for that.
+ */
+ if (size != I2C_SMBUS_BYTE_DATA) {
+ cam_err(cam, "funky xfer size %d\n", size);
+ return -EINVAL;
+ }
+
+ if (rw == I2C_SMBUS_WRITE)
+ ret = cafe_smbus_write_data(cam, addr, command, data->byte);
+ else if (rw == I2C_SMBUS_READ)
+ ret = cafe_smbus_read_data(cam, addr, command, &data->byte);
+ return ret;
+}
+
+
+static void cafe_smbus_enable_irq(struct cafe_camera *cam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->mcam.dev_lock, flags);
+ mcam_reg_set_bit(&cam->mcam, REG_IRQMASK, TWSIIRQS);
+ spin_unlock_irqrestore(&cam->mcam.dev_lock, flags);
+}
+
+static u32 cafe_smbus_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_SMBUS_READ_BYTE_DATA |
+ I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
+}
+
+static struct i2c_algorithm cafe_smbus_algo = {
+ .smbus_xfer = cafe_smbus_xfer,
+ .functionality = cafe_smbus_func
+};
+
+static int cafe_smbus_setup(struct cafe_camera *cam)
+{
+ struct i2c_adapter *adap;
+ int ret;
+
+ adap = kzalloc(sizeof(*adap), GFP_KERNEL);
+ if (adap == NULL)
+ return -ENOMEM;
+ cam->mcam.i2c_adapter = adap;
+ cafe_smbus_enable_irq(cam);
+ adap->owner = THIS_MODULE;
+ adap->algo = &cafe_smbus_algo;
+ strcpy(adap->name, "cafe_ccic");
+ adap->dev.parent = &cam->pdev->dev;
+ i2c_set_adapdata(adap, cam);
+ ret = i2c_add_adapter(adap);
+ if (ret)
+ printk(KERN_ERR "Unable to register cafe i2c adapter\n");
+ return ret;
+}
+
+static void cafe_smbus_shutdown(struct cafe_camera *cam)
+{
+ i2c_del_adapter(cam->mcam.i2c_adapter);
+ kfree(cam->mcam.i2c_adapter);
+}
+
+
+/*
+ * Controller-level stuff
+ */
+
+static void cafe_ctlr_init(struct mcam_camera *mcam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mcam->dev_lock, flags);
+ /*
+ * Added magic to bring up the hardware on the B-Test board
+ */
+ mcam_reg_write(mcam, 0x3038, 0x8);
+ mcam_reg_write(mcam, 0x315c, 0x80008);
+ /*
+ * Go through the dance needed to wake the device up.
+ * Note that these registers are global and shared
+ * with the NAND and SD devices. Interaction between the
+ * three still needs to be examined.
+ */
+ mcam_reg_write(mcam, REG_GL_CSR, GCSR_SRS|GCSR_MRS); /* Needed? */
+ mcam_reg_write(mcam, REG_GL_CSR, GCSR_SRC|GCSR_MRC);
+ mcam_reg_write(mcam, REG_GL_CSR, GCSR_SRC|GCSR_MRS);
+ /*
+ * Here we must wait a bit for the controller to come around.
+ */
+ spin_unlock_irqrestore(&mcam->dev_lock, flags);
+ msleep(5);
+ spin_lock_irqsave(&mcam->dev_lock, flags);
+
+ mcam_reg_write(mcam, REG_GL_CSR, GCSR_CCIC_EN|GCSR_SRC|GCSR_MRC);
+ mcam_reg_set_bit(mcam, REG_GL_IMASK, GIMSK_CCIC_EN);
+ /*
+ * Mask all interrupts.
+ */
+ mcam_reg_write(mcam, REG_IRQMASK, 0);
+ spin_unlock_irqrestore(&mcam->dev_lock, flags);
+}
+
+
+static void cafe_ctlr_power_up(struct mcam_camera *mcam)
+{
+ /*
+ * Part one of the sensor dance: turn the global
+ * GPIO signal on.
+ */
+ mcam_reg_write(mcam, REG_GL_FCR, GFCR_GPIO_ON);
+ mcam_reg_write(mcam, REG_GL_GPIOR, GGPIO_OUT|GGPIO_VAL);
+ /*
+ * Put the sensor into operational mode (assumes OLPC-style
+ * wiring). Control 0 is reset - set to 1 to operate.
+ * Control 1 is power down, set to 0 to operate.
+ */
+ mcam_reg_write(mcam, REG_GPR, GPR_C1EN|GPR_C0EN); /* pwr up, reset */
+ mcam_reg_write(mcam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C0);
+}
+
+static void cafe_ctlr_power_down(struct mcam_camera *mcam)
+{
+ mcam_reg_write(mcam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C1);
+ mcam_reg_write(mcam, REG_GL_FCR, GFCR_GPIO_ON);
+ mcam_reg_write(mcam, REG_GL_GPIOR, GGPIO_OUT);
+}
+
+
+
+/*
+ * The platform interrupt handler.
+ */
+static irqreturn_t cafe_irq(int irq, void *data)
+{
+ struct cafe_camera *cam = data;
+ struct mcam_camera *mcam = &cam->mcam;
+ unsigned int irqs, handled;
+
+ spin_lock(&mcam->dev_lock);
+ irqs = mcam_reg_read(mcam, REG_IRQSTAT);
+ handled = cam->registered && mccic_irq(mcam, irqs);
+ if (irqs & TWSIIRQS) {
+ mcam_reg_write(mcam, REG_IRQSTAT, TWSIIRQS);
+ wake_up(&cam->smbus_wait);
+ handled = 1;
+ }
+ spin_unlock(&mcam->dev_lock);
+ return IRQ_RETVAL(handled);
+}
+
+
+/* -------------------------------------------------------------------------- */
+/*
+ * PCI interface stuff.
+ */
+
+static int cafe_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ int ret;
+ struct cafe_camera *cam;
+ struct mcam_camera *mcam;
+
+ /*
+ * Start putting together one of our big camera structures.
+ */
+ ret = -ENOMEM;
+ cam = kzalloc(sizeof(struct cafe_camera), GFP_KERNEL);
+ if (cam == NULL)
+ goto out;
+ cam->pdev = pdev;
+ mcam = &cam->mcam;
+ mcam->chip_id = V4L2_IDENT_CAFE;
+ spin_lock_init(&mcam->dev_lock);
+ init_waitqueue_head(&cam->smbus_wait);
+ mcam->plat_power_up = cafe_ctlr_power_up;
+ mcam->plat_power_down = cafe_ctlr_power_down;
+ mcam->dev = &pdev->dev;
+ /*
+ * Set the clock speed for the XO 1; I don't believe this
+ * driver has ever run anywhere else.
+ */
+ mcam->clock_speed = 45;
+ mcam->use_smbus = 1;
+ /*
+ * Vmalloc mode for buffers is traditional with this driver.
+ * We *might* be able to run DMA_contig, especially on a system
+ * with CMA in it.
+ */
+ mcam->buffer_mode = B_vmalloc;
+ /*
+ * Get set up on the PCI bus.
+ */
+ ret = pci_enable_device(pdev);
+ if (ret)
+ goto out_free;
+ pci_set_master(pdev);
+
+ ret = -EIO;
+ mcam->regs = pci_iomap(pdev, 0, 0);
+ if (!mcam->regs) {
+ printk(KERN_ERR "Unable to ioremap cafe-ccic regs\n");
+ goto out_disable;
+ }
+ ret = request_irq(pdev->irq, cafe_irq, IRQF_SHARED, "cafe-ccic", cam);
+ if (ret)
+ goto out_iounmap;
+
+ /*
+ * Initialize the controller and leave it powered up. It will
+ * stay that way until the sensor driver shows up.
+ */
+ cafe_ctlr_init(mcam);
+ cafe_ctlr_power_up(mcam);
+ /*
+ * Set up I2C/SMBUS communications. We have to drop the mutex here
+ * because the sensor could attach in this call chain, leading to
+ * unsightly deadlocks.
+ */
+ ret = cafe_smbus_setup(cam);
+ if (ret)
+ goto out_pdown;
+
+ ret = mccic_register(mcam);
+ if (ret == 0) {
+ cam->registered = 1;
+ return 0;
+ }
+
+ cafe_smbus_shutdown(cam);
+out_pdown:
+ cafe_ctlr_power_down(mcam);
+ free_irq(pdev->irq, cam);
+out_iounmap:
+ pci_iounmap(pdev, mcam->regs);
+out_disable:
+ pci_disable_device(pdev);
+out_free:
+ kfree(cam);
+out:
+ return ret;
+}
+
+
+/*
+ * Shut down an initialized device
+ */
+static void cafe_shutdown(struct cafe_camera *cam)
+{
+ mccic_shutdown(&cam->mcam);
+ cafe_smbus_shutdown(cam);
+ free_irq(cam->pdev->irq, cam);
+ pci_iounmap(cam->pdev, cam->mcam.regs);
+}
+
+
+static void cafe_pci_remove(struct pci_dev *pdev)
+{
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
+ struct cafe_camera *cam = to_cam(v4l2_dev);
+
+ if (cam == NULL) {
+ printk(KERN_WARNING "pci_remove on unknown pdev %p\n", pdev);
+ return;
+ }
+ cafe_shutdown(cam);
+ kfree(cam);
+}
+
+
+#ifdef CONFIG_PM
+/*
+ * Basic power management.
+ */
+static int cafe_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
+ struct cafe_camera *cam = to_cam(v4l2_dev);
+ int ret;
+
+ ret = pci_save_state(pdev);
+ if (ret)
+ return ret;
+ mccic_suspend(&cam->mcam);
+ pci_disable_device(pdev);
+ return 0;
+}
+
+
+static int cafe_pci_resume(struct pci_dev *pdev)
+{
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
+ struct cafe_camera *cam = to_cam(v4l2_dev);
+ int ret = 0;
+
+ pci_restore_state(pdev);
+ ret = pci_enable_device(pdev);
+
+ if (ret) {
+ cam_warn(cam, "Unable to re-enable device on resume!\n");
+ return ret;
+ }
+ cafe_ctlr_init(&cam->mcam);
+ return mccic_resume(&cam->mcam);
+}
+
+#endif /* CONFIG_PM */
+
+static struct pci_device_id cafe_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL,
+ PCI_DEVICE_ID_MARVELL_88ALP01_CCIC) },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, cafe_ids);
+
+static struct pci_driver cafe_pci_driver = {
+ .name = "cafe1000-ccic",
+ .id_table = cafe_ids,
+ .probe = cafe_pci_probe,
+ .remove = cafe_pci_remove,
+#ifdef CONFIG_PM
+ .suspend = cafe_pci_suspend,
+ .resume = cafe_pci_resume,
+#endif
+};
+
+
+
+
+static int __init cafe_init(void)
+{
+ int ret;
+
+ printk(KERN_NOTICE "Marvell M88ALP01 'CAFE' Camera Controller version %d\n",
+ CAFE_VERSION);
+ ret = pci_register_driver(&cafe_pci_driver);
+ if (ret) {
+ printk(KERN_ERR "Unable to register cafe_ccic driver\n");
+ goto out;
+ }
+ ret = 0;
+
+out:
+ return ret;
+}
+
+
+static void __exit cafe_exit(void)
+{
+ pci_unregister_driver(&cafe_pci_driver);
+}
+
+module_init(cafe_init);
+module_exit(cafe_exit);
diff --git a/drivers/media/video/marvell-ccic/mcam-core.c b/drivers/media/video/marvell-ccic/mcam-core.c
new file mode 100644
index 00000000..996ac34d
--- /dev/null
+++ b/drivers/media/video/marvell-ccic/mcam-core.c
@@ -0,0 +1,1879 @@
+/*
+ * The Marvell camera core. This device appears in a number of settings,
+ * so it needs platform-specific support outside of the core.
+ *
+ * Copyright 2011 Jonathan Corbet corbet@lwn.net
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/vmalloc.h>
+#include <linux/io.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/ov7670.h>
+#include <media/videobuf2-vmalloc.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-dma-sg.h>
+
+#include "mcam-core.h"
+
+/*
+ * Basic frame stats - to be deleted shortly
+ */
+static int frames;
+static int singles;
+static int delivered;
+
+#ifdef MCAM_MODE_VMALLOC
+/*
+ * Internal DMA buffer management. Since the controller cannot do S/G I/O,
+ * we must have physically contiguous buffers to bring frames into.
+ * These parameters control how many buffers we use, whether we
+ * allocate them at load time (better chance of success, but nails down
+ * memory) or when somebody tries to use the camera (riskier), and,
+ * for load-time allocation, how big they should be.
+ *
+ * The controller can cycle through three buffers. We could use
+ * more by flipping pointers around, but it probably makes little
+ * sense.
+ */
+
+static bool alloc_bufs_at_read;
+module_param(alloc_bufs_at_read, bool, 0444);
+MODULE_PARM_DESC(alloc_bufs_at_read,
+ "Non-zero value causes DMA buffers to be allocated when the "
+ "video capture device is read, rather than at module load "
+ "time. This saves memory, but decreases the chances of "
+ "successfully getting those buffers. This parameter is "
+ "only used in the vmalloc buffer mode");
+
+static int n_dma_bufs = 3;
+module_param(n_dma_bufs, uint, 0644);
+MODULE_PARM_DESC(n_dma_bufs,
+ "The number of DMA buffers to allocate. Can be either two "
+ "(saves memory, makes timing tighter) or three.");
+
+static int dma_buf_size = VGA_WIDTH * VGA_HEIGHT * 2; /* Worst case */
+module_param(dma_buf_size, uint, 0444);
+MODULE_PARM_DESC(dma_buf_size,
+ "The size of the allocated DMA buffers. If actual operating "
+ "parameters require larger buffers, an attempt to reallocate "
+ "will be made.");
+#else /* MCAM_MODE_VMALLOC */
+static const bool alloc_bufs_at_read = 0;
+static const int n_dma_bufs = 3; /* Used by S/G_PARM */
+#endif /* MCAM_MODE_VMALLOC */
+
+static bool flip;
+module_param(flip, bool, 0444);
+MODULE_PARM_DESC(flip,
+ "If set, the sensor will be instructed to flip the image "
+ "vertically.");
+
+static int buffer_mode = -1;
+module_param(buffer_mode, int, 0444);
+MODULE_PARM_DESC(buffer_mode,
+ "Set the buffer mode to be used; default is to go with what "
+ "the platform driver asks for. Set to 0 for vmalloc, 1 for "
+ "DMA contiguous.");
+
+/*
+ * Status flags. Always manipulated with bit operations.
+ */
+#define CF_BUF0_VALID 0 /* Buffers valid - first three */
+#define CF_BUF1_VALID 1
+#define CF_BUF2_VALID 2
+#define CF_DMA_ACTIVE 3 /* A frame is incoming */
+#define CF_CONFIG_NEEDED 4 /* Must configure hardware */
+#define CF_SINGLE_BUFFER 5 /* Running with a single buffer */
+#define CF_SG_RESTART 6 /* SG restart needed */
+
+#define sensor_call(cam, o, f, args...) \
+ v4l2_subdev_call(cam->sensor, o, f, ##args)
+
+static struct mcam_format_struct {
+ __u8 *desc;
+ __u32 pixelformat;
+ int bpp; /* Bytes per pixel */
+ enum v4l2_mbus_pixelcode mbus_code;
+} mcam_formats[] = {
+ {
+ .desc = "YUYV 4:2:2",
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .bpp = 2,
+ },
+ {
+ .desc = "RGB 444",
+ .pixelformat = V4L2_PIX_FMT_RGB444,
+ .mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE,
+ .bpp = 2,
+ },
+ {
+ .desc = "RGB 565",
+ .pixelformat = V4L2_PIX_FMT_RGB565,
+ .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE,
+ .bpp = 2,
+ },
+ {
+ .desc = "Raw RGB Bayer",
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .mbus_code = V4L2_MBUS_FMT_SBGGR8_1X8,
+ .bpp = 1
+ },
+};
+#define N_MCAM_FMTS ARRAY_SIZE(mcam_formats)
+
+static struct mcam_format_struct *mcam_find_format(u32 pixelformat)
+{
+ unsigned i;
+
+ for (i = 0; i < N_MCAM_FMTS; i++)
+ if (mcam_formats[i].pixelformat == pixelformat)
+ return mcam_formats + i;
+ /* Not found? Then return the first format. */
+ return mcam_formats;
+}
+
+/*
+ * The default format we use until somebody says otherwise.
+ */
+static const struct v4l2_pix_format mcam_def_pix_format = {
+ .width = VGA_WIDTH,
+ .height = VGA_HEIGHT,
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ .field = V4L2_FIELD_NONE,
+ .bytesperline = VGA_WIDTH*2,
+ .sizeimage = VGA_WIDTH*VGA_HEIGHT*2,
+};
+
+static const enum v4l2_mbus_pixelcode mcam_def_mbus_code =
+ V4L2_MBUS_FMT_YUYV8_2X8;
+
+
+/*
+ * The two-word DMA descriptor format used by the Armada 610 and like. There
+ * Is a three-word format as well (set C1_DESC_3WORD) where the third
+ * word is a pointer to the next descriptor, but we don't use it. Two-word
+ * descriptors have to be contiguous in memory.
+ */
+struct mcam_dma_desc {
+ u32 dma_addr;
+ u32 segment_len;
+};
+
+/*
+ * Our buffer type for working with videobuf2. Note that the vb2
+ * developers have decreed that struct vb2_buffer must be at the
+ * beginning of this structure.
+ */
+struct mcam_vb_buffer {
+ struct vb2_buffer vb_buf;
+ struct list_head queue;
+ struct mcam_dma_desc *dma_desc; /* Descriptor virtual address */
+ dma_addr_t dma_desc_pa; /* Descriptor physical address */
+ int dma_desc_nent; /* Number of mapped descriptors */
+};
+
+static inline struct mcam_vb_buffer *vb_to_mvb(struct vb2_buffer *vb)
+{
+ return container_of(vb, struct mcam_vb_buffer, vb_buf);
+}
+
+/*
+ * Hand a completed buffer back to user space.
+ */
+static void mcam_buffer_done(struct mcam_camera *cam, int frame,
+ struct vb2_buffer *vbuf)
+{
+ vbuf->v4l2_buf.bytesused = cam->pix_format.sizeimage;
+ vbuf->v4l2_buf.sequence = cam->buf_seq[frame];
+ vb2_set_plane_payload(vbuf, 0, cam->pix_format.sizeimage);
+ vb2_buffer_done(vbuf, VB2_BUF_STATE_DONE);
+}
+
+
+
+/*
+ * Debugging and related.
+ */
+#define cam_err(cam, fmt, arg...) \
+ dev_err((cam)->dev, fmt, ##arg);
+#define cam_warn(cam, fmt, arg...) \
+ dev_warn((cam)->dev, fmt, ##arg);
+#define cam_dbg(cam, fmt, arg...) \
+ dev_dbg((cam)->dev, fmt, ##arg);
+
+
+/*
+ * Flag manipulation helpers
+ */
+static void mcam_reset_buffers(struct mcam_camera *cam)
+{
+ int i;
+
+ cam->next_buf = -1;
+ for (i = 0; i < cam->nbufs; i++)
+ clear_bit(i, &cam->flags);
+}
+
+static inline int mcam_needs_config(struct mcam_camera *cam)
+{
+ return test_bit(CF_CONFIG_NEEDED, &cam->flags);
+}
+
+static void mcam_set_config_needed(struct mcam_camera *cam, int needed)
+{
+ if (needed)
+ set_bit(CF_CONFIG_NEEDED, &cam->flags);
+ else
+ clear_bit(CF_CONFIG_NEEDED, &cam->flags);
+}
+
+/* ------------------------------------------------------------------- */
+/*
+ * Make the controller start grabbing images. Everything must
+ * be set up before doing this.
+ */
+static void mcam_ctlr_start(struct mcam_camera *cam)
+{
+ /* set_bit performs a read, so no other barrier should be
+ needed here */
+ mcam_reg_set_bit(cam, REG_CTRL0, C0_ENABLE);
+}
+
+static void mcam_ctlr_stop(struct mcam_camera *cam)
+{
+ mcam_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE);
+}
+
+/* ------------------------------------------------------------------- */
+
+#ifdef MCAM_MODE_VMALLOC
+/*
+ * Code specific to the vmalloc buffer mode.
+ */
+
+/*
+ * Allocate in-kernel DMA buffers for vmalloc mode.
+ */
+static int mcam_alloc_dma_bufs(struct mcam_camera *cam, int loadtime)
+{
+ int i;
+
+ mcam_set_config_needed(cam, 1);
+ if (loadtime)
+ cam->dma_buf_size = dma_buf_size;
+ else
+ cam->dma_buf_size = cam->pix_format.sizeimage;
+ if (n_dma_bufs > 3)
+ n_dma_bufs = 3;
+
+ cam->nbufs = 0;
+ for (i = 0; i < n_dma_bufs; i++) {
+ cam->dma_bufs[i] = dma_alloc_coherent(cam->dev,
+ cam->dma_buf_size, cam->dma_handles + i,
+ GFP_KERNEL);
+ if (cam->dma_bufs[i] == NULL) {
+ cam_warn(cam, "Failed to allocate DMA buffer\n");
+ break;
+ }
+ (cam->nbufs)++;
+ }
+
+ switch (cam->nbufs) {
+ case 1:
+ dma_free_coherent(cam->dev, cam->dma_buf_size,
+ cam->dma_bufs[0], cam->dma_handles[0]);
+ cam->nbufs = 0;
+ case 0:
+ cam_err(cam, "Insufficient DMA buffers, cannot operate\n");
+ return -ENOMEM;
+
+ case 2:
+ if (n_dma_bufs > 2)
+ cam_warn(cam, "Will limp along with only 2 buffers\n");
+ break;
+ }
+ return 0;
+}
+
+static void mcam_free_dma_bufs(struct mcam_camera *cam)
+{
+ int i;
+
+ for (i = 0; i < cam->nbufs; i++) {
+ dma_free_coherent(cam->dev, cam->dma_buf_size,
+ cam->dma_bufs[i], cam->dma_handles[i]);
+ cam->dma_bufs[i] = NULL;
+ }
+ cam->nbufs = 0;
+}
+
+
+/*
+ * Set up DMA buffers when operating in vmalloc mode
+ */
+static void mcam_ctlr_dma_vmalloc(struct mcam_camera *cam)
+{
+ /*
+ * Store the first two Y buffers (we aren't supporting
+ * planar formats for now, so no UV bufs). Then either
+ * set the third if it exists, or tell the controller
+ * to just use two.
+ */
+ mcam_reg_write(cam, REG_Y0BAR, cam->dma_handles[0]);
+ mcam_reg_write(cam, REG_Y1BAR, cam->dma_handles[1]);
+ if (cam->nbufs > 2) {
+ mcam_reg_write(cam, REG_Y2BAR, cam->dma_handles[2]);
+ mcam_reg_clear_bit(cam, REG_CTRL1, C1_TWOBUFS);
+ } else
+ mcam_reg_set_bit(cam, REG_CTRL1, C1_TWOBUFS);
+ if (cam->chip_id == V4L2_IDENT_CAFE)
+ mcam_reg_write(cam, REG_UBAR, 0); /* 32 bits only */
+}
+
+/*
+ * Copy data out to user space in the vmalloc case
+ */
+static void mcam_frame_tasklet(unsigned long data)
+{
+ struct mcam_camera *cam = (struct mcam_camera *) data;
+ int i;
+ unsigned long flags;
+ struct mcam_vb_buffer *buf;
+
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ for (i = 0; i < cam->nbufs; i++) {
+ int bufno = cam->next_buf;
+
+ if (cam->state != S_STREAMING || bufno < 0)
+ break; /* I/O got stopped */
+ if (++(cam->next_buf) >= cam->nbufs)
+ cam->next_buf = 0;
+ if (!test_bit(bufno, &cam->flags))
+ continue;
+ if (list_empty(&cam->buffers)) {
+ singles++;
+ break; /* Leave it valid, hope for better later */
+ }
+ delivered++;
+ clear_bit(bufno, &cam->flags);
+ buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer,
+ queue);
+ list_del_init(&buf->queue);
+ /*
+ * Drop the lock during the big copy. This *should* be safe...
+ */
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+ memcpy(vb2_plane_vaddr(&buf->vb_buf, 0), cam->dma_bufs[bufno],
+ cam->pix_format.sizeimage);
+ mcam_buffer_done(cam, bufno, &buf->vb_buf);
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ }
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+}
+
+
+/*
+ * Make sure our allocated buffers are up to the task.
+ */
+static int mcam_check_dma_buffers(struct mcam_camera *cam)
+{
+ if (cam->nbufs > 0 && cam->dma_buf_size < cam->pix_format.sizeimage)
+ mcam_free_dma_bufs(cam);
+ if (cam->nbufs == 0)
+ return mcam_alloc_dma_bufs(cam, 0);
+ return 0;
+}
+
+static void mcam_vmalloc_done(struct mcam_camera *cam, int frame)
+{
+ tasklet_schedule(&cam->s_tasklet);
+}
+
+#else /* MCAM_MODE_VMALLOC */
+
+static inline int mcam_alloc_dma_bufs(struct mcam_camera *cam, int loadtime)
+{
+ return 0;
+}
+
+static inline void mcam_free_dma_bufs(struct mcam_camera *cam)
+{
+ return;
+}
+
+static inline int mcam_check_dma_buffers(struct mcam_camera *cam)
+{
+ return 0;
+}
+
+
+
+#endif /* MCAM_MODE_VMALLOC */
+
+
+#ifdef MCAM_MODE_DMA_CONTIG
+/* ---------------------------------------------------------------------- */
+/*
+ * DMA-contiguous code.
+ */
+/*
+ * Set up a contiguous buffer for the given frame. Here also is where
+ * the underrun strategy is set: if there is no buffer available, reuse
+ * the buffer from the other BAR and set the CF_SINGLE_BUFFER flag to
+ * keep the interrupt handler from giving that buffer back to user
+ * space. In this way, we always have a buffer to DMA to and don't
+ * have to try to play games stopping and restarting the controller.
+ */
+static void mcam_set_contig_buffer(struct mcam_camera *cam, int frame)
+{
+ struct mcam_vb_buffer *buf;
+ /*
+ * If there are no available buffers, go into single mode
+ */
+ if (list_empty(&cam->buffers)) {
+ buf = cam->vb_bufs[frame ^ 0x1];
+ cam->vb_bufs[frame] = buf;
+ mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR,
+ vb2_dma_contig_plane_dma_addr(&buf->vb_buf, 0));
+ set_bit(CF_SINGLE_BUFFER, &cam->flags);
+ singles++;
+ return;
+ }
+ /*
+ * OK, we have a buffer we can use.
+ */
+ buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer, queue);
+ list_del_init(&buf->queue);
+ mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR,
+ vb2_dma_contig_plane_dma_addr(&buf->vb_buf, 0));
+ cam->vb_bufs[frame] = buf;
+ clear_bit(CF_SINGLE_BUFFER, &cam->flags);
+}
+
+/*
+ * Initial B_DMA_contig setup.
+ */
+static void mcam_ctlr_dma_contig(struct mcam_camera *cam)
+{
+ mcam_reg_set_bit(cam, REG_CTRL1, C1_TWOBUFS);
+ cam->nbufs = 2;
+ mcam_set_contig_buffer(cam, 0);
+ mcam_set_contig_buffer(cam, 1);
+}
+
+/*
+ * Frame completion handling.
+ */
+static void mcam_dma_contig_done(struct mcam_camera *cam, int frame)
+{
+ struct mcam_vb_buffer *buf = cam->vb_bufs[frame];
+
+ if (!test_bit(CF_SINGLE_BUFFER, &cam->flags)) {
+ delivered++;
+ mcam_buffer_done(cam, frame, &buf->vb_buf);
+ }
+ mcam_set_contig_buffer(cam, frame);
+}
+
+#endif /* MCAM_MODE_DMA_CONTIG */
+
+#ifdef MCAM_MODE_DMA_SG
+/* ---------------------------------------------------------------------- */
+/*
+ * Scatter/gather-specific code.
+ */
+
+/*
+ * Set up the next buffer for S/G I/O; caller should be sure that
+ * the controller is stopped and a buffer is available.
+ */
+static void mcam_sg_next_buffer(struct mcam_camera *cam)
+{
+ struct mcam_vb_buffer *buf;
+
+ buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer, queue);
+ list_del_init(&buf->queue);
+ /*
+ * Very Bad Not Good Things happen if you don't clear
+ * C1_DESC_ENA before making any descriptor changes.
+ */
+ mcam_reg_clear_bit(cam, REG_CTRL1, C1_DESC_ENA);
+ mcam_reg_write(cam, REG_DMA_DESC_Y, buf->dma_desc_pa);
+ mcam_reg_write(cam, REG_DESC_LEN_Y,
+ buf->dma_desc_nent*sizeof(struct mcam_dma_desc));
+ mcam_reg_write(cam, REG_DESC_LEN_U, 0);
+ mcam_reg_write(cam, REG_DESC_LEN_V, 0);
+ mcam_reg_set_bit(cam, REG_CTRL1, C1_DESC_ENA);
+ cam->vb_bufs[0] = buf;
+}
+
+/*
+ * Initial B_DMA_sg setup
+ */
+static void mcam_ctlr_dma_sg(struct mcam_camera *cam)
+{
+ /*
+ * The list-empty condition can hit us at resume time
+ * if the buffer list was empty when the system was suspended.
+ */
+ if (list_empty(&cam->buffers)) {
+ set_bit(CF_SG_RESTART, &cam->flags);
+ return;
+ }
+
+ mcam_reg_clear_bit(cam, REG_CTRL1, C1_DESC_3WORD);
+ mcam_sg_next_buffer(cam);
+ cam->nbufs = 3;
+}
+
+
+/*
+ * Frame completion with S/G is trickier. We can't muck with
+ * a descriptor chain on the fly, since the controller buffers it
+ * internally. So we have to actually stop and restart; Marvell
+ * says this is the way to do it.
+ *
+ * Of course, stopping is easier said than done; experience shows
+ * that the controller can start a frame *after* C0_ENABLE has been
+ * cleared. So when running in S/G mode, the controller is "stopped"
+ * on receipt of the start-of-frame interrupt. That means we can
+ * safely change the DMA descriptor array here and restart things
+ * (assuming there's another buffer waiting to go).
+ */
+static void mcam_dma_sg_done(struct mcam_camera *cam, int frame)
+{
+ struct mcam_vb_buffer *buf = cam->vb_bufs[0];
+
+ /*
+ * If we're no longer supposed to be streaming, don't do anything.
+ */
+ if (cam->state != S_STREAMING)
+ return;
+ /*
+ * If we have another buffer available, put it in and
+ * restart the engine.
+ */
+ if (!list_empty(&cam->buffers)) {
+ mcam_sg_next_buffer(cam);
+ mcam_ctlr_start(cam);
+ /*
+ * Otherwise set CF_SG_RESTART and the controller will
+ * be restarted once another buffer shows up.
+ */
+ } else {
+ set_bit(CF_SG_RESTART, &cam->flags);
+ singles++;
+ cam->vb_bufs[0] = NULL;
+ }
+ /*
+ * Now we can give the completed frame back to user space.
+ */
+ delivered++;
+ mcam_buffer_done(cam, frame, &buf->vb_buf);
+}
+
+
+/*
+ * Scatter/gather mode requires stopping the controller between
+ * frames so we can put in a new DMA descriptor array. If no new
+ * buffer exists at frame completion, the controller is left stopped;
+ * this function is charged with gettig things going again.
+ */
+static void mcam_sg_restart(struct mcam_camera *cam)
+{
+ mcam_ctlr_dma_sg(cam);
+ mcam_ctlr_start(cam);
+ clear_bit(CF_SG_RESTART, &cam->flags);
+}
+
+#else /* MCAM_MODE_DMA_SG */
+
+static inline void mcam_sg_restart(struct mcam_camera *cam)
+{
+ return;
+}
+
+#endif /* MCAM_MODE_DMA_SG */
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Buffer-mode-independent controller code.
+ */
+
+/*
+ * Image format setup
+ */
+static void mcam_ctlr_image(struct mcam_camera *cam)
+{
+ int imgsz;
+ struct v4l2_pix_format *fmt = &cam->pix_format;
+
+ imgsz = ((fmt->height << IMGSZ_V_SHIFT) & IMGSZ_V_MASK) |
+ (fmt->bytesperline & IMGSZ_H_MASK);
+ mcam_reg_write(cam, REG_IMGSIZE, imgsz);
+ mcam_reg_write(cam, REG_IMGOFFSET, 0);
+ /* YPITCH just drops the last two bits */
+ mcam_reg_write_mask(cam, REG_IMGPITCH, fmt->bytesperline,
+ IMGP_YP_MASK);
+ /*
+ * Tell the controller about the image format we are using.
+ */
+ switch (cam->pix_format.pixelformat) {
+ case V4L2_PIX_FMT_YUYV:
+ mcam_reg_write_mask(cam, REG_CTRL0,
+ C0_DF_YUV|C0_YUV_PACKED|C0_YUVE_YUYV,
+ C0_DF_MASK);
+ break;
+
+ case V4L2_PIX_FMT_RGB444:
+ mcam_reg_write_mask(cam, REG_CTRL0,
+ C0_DF_RGB|C0_RGBF_444|C0_RGB4_XRGB,
+ C0_DF_MASK);
+ /* Alpha value? */
+ break;
+
+ case V4L2_PIX_FMT_RGB565:
+ mcam_reg_write_mask(cam, REG_CTRL0,
+ C0_DF_RGB|C0_RGBF_565|C0_RGB5_BGGR,
+ C0_DF_MASK);
+ break;
+
+ default:
+ cam_err(cam, "Unknown format %x\n", cam->pix_format.pixelformat);
+ break;
+ }
+ /*
+ * Make sure it knows we want to use hsync/vsync.
+ */
+ mcam_reg_write_mask(cam, REG_CTRL0, C0_SIF_HVSYNC,
+ C0_SIFM_MASK);
+}
+
+
+/*
+ * Configure the controller for operation; caller holds the
+ * device mutex.
+ */
+static int mcam_ctlr_configure(struct mcam_camera *cam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ clear_bit(CF_SG_RESTART, &cam->flags);
+ cam->dma_setup(cam);
+ mcam_ctlr_image(cam);
+ mcam_set_config_needed(cam, 0);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+ return 0;
+}
+
+static void mcam_ctlr_irq_enable(struct mcam_camera *cam)
+{
+ /*
+ * Clear any pending interrupts, since we do not
+ * expect to have I/O active prior to enabling.
+ */
+ mcam_reg_write(cam, REG_IRQSTAT, FRAMEIRQS);
+ mcam_reg_set_bit(cam, REG_IRQMASK, FRAMEIRQS);
+}
+
+static void mcam_ctlr_irq_disable(struct mcam_camera *cam)
+{
+ mcam_reg_clear_bit(cam, REG_IRQMASK, FRAMEIRQS);
+}
+
+
+
+static void mcam_ctlr_init(struct mcam_camera *cam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ /*
+ * Make sure it's not powered down.
+ */
+ mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN);
+ /*
+ * Turn off the enable bit. It sure should be off anyway,
+ * but it's good to be sure.
+ */
+ mcam_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE);
+ /*
+ * Clock the sensor appropriately. Controller clock should
+ * be 48MHz, sensor "typical" value is half that.
+ */
+ mcam_reg_write_mask(cam, REG_CLKCTRL, 2, CLK_DIV_MASK);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+}
+
+
+/*
+ * Stop the controller, and don't return until we're really sure that no
+ * further DMA is going on.
+ */
+static void mcam_ctlr_stop_dma(struct mcam_camera *cam)
+{
+ unsigned long flags;
+
+ /*
+ * Theory: stop the camera controller (whether it is operating
+ * or not). Delay briefly just in case we race with the SOF
+ * interrupt, then wait until no DMA is active.
+ */
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ clear_bit(CF_SG_RESTART, &cam->flags);
+ mcam_ctlr_stop(cam);
+ cam->state = S_IDLE;
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+ /*
+ * This is a brutally long sleep, but experience shows that
+ * it can take the controller a while to get the message that
+ * it needs to stop grabbing frames. In particular, we can
+ * sometimes (on mmp) get a frame at the end WITHOUT the
+ * start-of-frame indication.
+ */
+ msleep(150);
+ if (test_bit(CF_DMA_ACTIVE, &cam->flags))
+ cam_err(cam, "Timeout waiting for DMA to end\n");
+ /* This would be bad news - what now? */
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ mcam_ctlr_irq_disable(cam);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+}
+
+/*
+ * Power up and down.
+ */
+static void mcam_ctlr_power_up(struct mcam_camera *cam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ cam->plat_power_up(cam);
+ mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+ msleep(5); /* Just to be sure */
+}
+
+static void mcam_ctlr_power_down(struct mcam_camera *cam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ /*
+ * School of hard knocks department: be sure we do any register
+ * twiddling on the controller *before* calling the platform
+ * power down routine.
+ */
+ mcam_reg_set_bit(cam, REG_CTRL1, C1_PWRDWN);
+ cam->plat_power_down(cam);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+}
+
+/* -------------------------------------------------------------------- */
+/*
+ * Communications with the sensor.
+ */
+
+static int __mcam_cam_reset(struct mcam_camera *cam)
+{
+ return sensor_call(cam, core, reset, 0);
+}
+
+/*
+ * We have found the sensor on the i2c. Let's try to have a
+ * conversation.
+ */
+static int mcam_cam_init(struct mcam_camera *cam)
+{
+ struct v4l2_dbg_chip_ident chip;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ if (cam->state != S_NOTREADY)
+ cam_warn(cam, "Cam init with device in funky state %d",
+ cam->state);
+ ret = __mcam_cam_reset(cam);
+ if (ret)
+ goto out;
+ chip.ident = V4L2_IDENT_NONE;
+ chip.match.type = V4L2_CHIP_MATCH_I2C_ADDR;
+ chip.match.addr = cam->sensor_addr;
+ ret = sensor_call(cam, core, g_chip_ident, &chip);
+ if (ret)
+ goto out;
+ cam->sensor_type = chip.ident;
+ if (cam->sensor_type != V4L2_IDENT_OV7670) {
+ cam_err(cam, "Unsupported sensor type 0x%x", cam->sensor_type);
+ ret = -EINVAL;
+ goto out;
+ }
+/* Get/set parameters? */
+ ret = 0;
+ cam->state = S_IDLE;
+out:
+ mcam_ctlr_power_down(cam);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+/*
+ * Configure the sensor to match the parameters we have. Caller should
+ * hold s_mutex
+ */
+static int mcam_cam_set_flip(struct mcam_camera *cam)
+{
+ struct v4l2_control ctrl;
+
+ memset(&ctrl, 0, sizeof(ctrl));
+ ctrl.id = V4L2_CID_VFLIP;
+ ctrl.value = flip;
+ return sensor_call(cam, core, s_ctrl, &ctrl);
+}
+
+
+static int mcam_cam_configure(struct mcam_camera *cam)
+{
+ struct v4l2_mbus_framefmt mbus_fmt;
+ int ret;
+
+ v4l2_fill_mbus_format(&mbus_fmt, &cam->pix_format, cam->mbus_code);
+ ret = sensor_call(cam, core, init, 0);
+ if (ret == 0)
+ ret = sensor_call(cam, video, s_mbus_fmt, &mbus_fmt);
+ /*
+ * OV7670 does weird things if flip is set *before* format...
+ */
+ ret += mcam_cam_set_flip(cam);
+ return ret;
+}
+
+/*
+ * Get everything ready, and start grabbing frames.
+ */
+static int mcam_read_setup(struct mcam_camera *cam)
+{
+ int ret;
+ unsigned long flags;
+
+ /*
+ * Configuration. If we still don't have DMA buffers,
+ * make one last, desperate attempt.
+ */
+ if (cam->buffer_mode == B_vmalloc && cam->nbufs == 0 &&
+ mcam_alloc_dma_bufs(cam, 0))
+ return -ENOMEM;
+
+ if (mcam_needs_config(cam)) {
+ mcam_cam_configure(cam);
+ ret = mcam_ctlr_configure(cam);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * Turn it loose.
+ */
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ clear_bit(CF_DMA_ACTIVE, &cam->flags);
+ mcam_reset_buffers(cam);
+ mcam_ctlr_irq_enable(cam);
+ cam->state = S_STREAMING;
+ if (!test_bit(CF_SG_RESTART, &cam->flags))
+ mcam_ctlr_start(cam);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+/*
+ * Videobuf2 interface code.
+ */
+
+static int mcam_vb_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt, unsigned int *nbufs,
+ unsigned int *num_planes, unsigned int sizes[],
+ void *alloc_ctxs[])
+{
+ struct mcam_camera *cam = vb2_get_drv_priv(vq);
+ int minbufs = (cam->buffer_mode == B_DMA_contig) ? 3 : 2;
+
+ sizes[0] = cam->pix_format.sizeimage;
+ *num_planes = 1; /* Someday we have to support planar formats... */
+ if (*nbufs < minbufs)
+ *nbufs = minbufs;
+ if (cam->buffer_mode == B_DMA_contig)
+ alloc_ctxs[0] = cam->vb_alloc_ctx;
+ return 0;
+}
+
+
+static void mcam_vb_buf_queue(struct vb2_buffer *vb)
+{
+ struct mcam_vb_buffer *mvb = vb_to_mvb(vb);
+ struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue);
+ unsigned long flags;
+ int start;
+
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ start = (cam->state == S_BUFWAIT) && !list_empty(&cam->buffers);
+ list_add(&mvb->queue, &cam->buffers);
+ if (cam->state == S_STREAMING && test_bit(CF_SG_RESTART, &cam->flags))
+ mcam_sg_restart(cam);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+ if (start)
+ mcam_read_setup(cam);
+}
+
+
+/*
+ * vb2 uses these to release the mutex when waiting in dqbuf. I'm
+ * not actually sure we need to do this (I'm not sure that vb2_dqbuf() needs
+ * to be called with the mutex held), but better safe than sorry.
+ */
+static void mcam_vb_wait_prepare(struct vb2_queue *vq)
+{
+ struct mcam_camera *cam = vb2_get_drv_priv(vq);
+
+ mutex_unlock(&cam->s_mutex);
+}
+
+static void mcam_vb_wait_finish(struct vb2_queue *vq)
+{
+ struct mcam_camera *cam = vb2_get_drv_priv(vq);
+
+ mutex_lock(&cam->s_mutex);
+}
+
+/*
+ * These need to be called with the mutex held from vb2
+ */
+static int mcam_vb_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct mcam_camera *cam = vb2_get_drv_priv(vq);
+
+ if (cam->state != S_IDLE) {
+ INIT_LIST_HEAD(&cam->buffers);
+ return -EINVAL;
+ }
+ cam->sequence = 0;
+ /*
+ * Videobuf2 sneakily hoards all the buffers and won't
+ * give them to us until *after* streaming starts. But
+ * we can't actually start streaming until we have a
+ * destination. So go into a wait state and hope they
+ * give us buffers soon.
+ */
+ if (cam->buffer_mode != B_vmalloc && list_empty(&cam->buffers)) {
+ cam->state = S_BUFWAIT;
+ return 0;
+ }
+ return mcam_read_setup(cam);
+}
+
+static int mcam_vb_stop_streaming(struct vb2_queue *vq)
+{
+ struct mcam_camera *cam = vb2_get_drv_priv(vq);
+ unsigned long flags;
+
+ if (cam->state == S_BUFWAIT) {
+ /* They never gave us buffers */
+ cam->state = S_IDLE;
+ return 0;
+ }
+ if (cam->state != S_STREAMING)
+ return -EINVAL;
+ mcam_ctlr_stop_dma(cam);
+ /*
+ * VB2 reclaims the buffers, so we need to forget
+ * about them.
+ */
+ spin_lock_irqsave(&cam->dev_lock, flags);
+ INIT_LIST_HEAD(&cam->buffers);
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+ return 0;
+}
+
+
+static const struct vb2_ops mcam_vb2_ops = {
+ .queue_setup = mcam_vb_queue_setup,
+ .buf_queue = mcam_vb_buf_queue,
+ .start_streaming = mcam_vb_start_streaming,
+ .stop_streaming = mcam_vb_stop_streaming,
+ .wait_prepare = mcam_vb_wait_prepare,
+ .wait_finish = mcam_vb_wait_finish,
+};
+
+
+#ifdef MCAM_MODE_DMA_SG
+/*
+ * Scatter/gather mode uses all of the above functions plus a
+ * few extras to deal with DMA mapping.
+ */
+static int mcam_vb_sg_buf_init(struct vb2_buffer *vb)
+{
+ struct mcam_vb_buffer *mvb = vb_to_mvb(vb);
+ struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue);
+ int ndesc = cam->pix_format.sizeimage/PAGE_SIZE + 1;
+
+ mvb->dma_desc = dma_alloc_coherent(cam->dev,
+ ndesc * sizeof(struct mcam_dma_desc),
+ &mvb->dma_desc_pa, GFP_KERNEL);
+ if (mvb->dma_desc == NULL) {
+ cam_err(cam, "Unable to get DMA descriptor array\n");
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static int mcam_vb_sg_buf_prepare(struct vb2_buffer *vb)
+{
+ struct mcam_vb_buffer *mvb = vb_to_mvb(vb);
+ struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_dma_sg_desc *sgd = vb2_dma_sg_plane_desc(vb, 0);
+ struct mcam_dma_desc *desc = mvb->dma_desc;
+ struct scatterlist *sg;
+ int i;
+
+ mvb->dma_desc_nent = dma_map_sg(cam->dev, sgd->sglist, sgd->num_pages,
+ DMA_FROM_DEVICE);
+ if (mvb->dma_desc_nent <= 0)
+ return -EIO; /* Not sure what's right here */
+ for_each_sg(sgd->sglist, sg, mvb->dma_desc_nent, i) {
+ desc->dma_addr = sg_dma_address(sg);
+ desc->segment_len = sg_dma_len(sg);
+ desc++;
+ }
+ return 0;
+}
+
+static int mcam_vb_sg_buf_finish(struct vb2_buffer *vb)
+{
+ struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_dma_sg_desc *sgd = vb2_dma_sg_plane_desc(vb, 0);
+
+ dma_unmap_sg(cam->dev, sgd->sglist, sgd->num_pages, DMA_FROM_DEVICE);
+ return 0;
+}
+
+static void mcam_vb_sg_buf_cleanup(struct vb2_buffer *vb)
+{
+ struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue);
+ struct mcam_vb_buffer *mvb = vb_to_mvb(vb);
+ int ndesc = cam->pix_format.sizeimage/PAGE_SIZE + 1;
+
+ dma_free_coherent(cam->dev, ndesc * sizeof(struct mcam_dma_desc),
+ mvb->dma_desc, mvb->dma_desc_pa);
+}
+
+
+static const struct vb2_ops mcam_vb2_sg_ops = {
+ .queue_setup = mcam_vb_queue_setup,
+ .buf_init = mcam_vb_sg_buf_init,
+ .buf_prepare = mcam_vb_sg_buf_prepare,
+ .buf_queue = mcam_vb_buf_queue,
+ .buf_finish = mcam_vb_sg_buf_finish,
+ .buf_cleanup = mcam_vb_sg_buf_cleanup,
+ .start_streaming = mcam_vb_start_streaming,
+ .stop_streaming = mcam_vb_stop_streaming,
+ .wait_prepare = mcam_vb_wait_prepare,
+ .wait_finish = mcam_vb_wait_finish,
+};
+
+#endif /* MCAM_MODE_DMA_SG */
+
+static int mcam_setup_vb2(struct mcam_camera *cam)
+{
+ struct vb2_queue *vq = &cam->vb_queue;
+
+ memset(vq, 0, sizeof(*vq));
+ vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ vq->drv_priv = cam;
+ INIT_LIST_HEAD(&cam->buffers);
+ switch (cam->buffer_mode) {
+ case B_DMA_contig:
+#ifdef MCAM_MODE_DMA_CONTIG
+ vq->ops = &mcam_vb2_ops;
+ vq->mem_ops = &vb2_dma_contig_memops;
+ cam->vb_alloc_ctx = vb2_dma_contig_init_ctx(cam->dev);
+ vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ cam->dma_setup = mcam_ctlr_dma_contig;
+ cam->frame_complete = mcam_dma_contig_done;
+#endif
+ break;
+ case B_DMA_sg:
+#ifdef MCAM_MODE_DMA_SG
+ vq->ops = &mcam_vb2_sg_ops;
+ vq->mem_ops = &vb2_dma_sg_memops;
+ vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ cam->dma_setup = mcam_ctlr_dma_sg;
+ cam->frame_complete = mcam_dma_sg_done;
+#endif
+ break;
+ case B_vmalloc:
+#ifdef MCAM_MODE_VMALLOC
+ tasklet_init(&cam->s_tasklet, mcam_frame_tasklet,
+ (unsigned long) cam);
+ vq->ops = &mcam_vb2_ops;
+ vq->mem_ops = &vb2_vmalloc_memops;
+ vq->buf_struct_size = sizeof(struct mcam_vb_buffer);
+ vq->io_modes = VB2_MMAP;
+ cam->dma_setup = mcam_ctlr_dma_vmalloc;
+ cam->frame_complete = mcam_vmalloc_done;
+#endif
+ break;
+ }
+ return vb2_queue_init(vq);
+}
+
+static void mcam_cleanup_vb2(struct mcam_camera *cam)
+{
+ vb2_queue_release(&cam->vb_queue);
+#ifdef MCAM_MODE_DMA_CONTIG
+ if (cam->buffer_mode == B_DMA_contig)
+ vb2_dma_contig_cleanup_ctx(cam->vb_alloc_ctx);
+#endif
+}
+
+
+/* ---------------------------------------------------------------------- */
+/*
+ * The long list of V4L2 ioctl() operations.
+ */
+
+static int mcam_vidioc_streamon(struct file *filp, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct mcam_camera *cam = filp->private_data;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = vb2_streamon(&cam->vb_queue, type);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+static int mcam_vidioc_streamoff(struct file *filp, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct mcam_camera *cam = filp->private_data;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = vb2_streamoff(&cam->vb_queue, type);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+static int mcam_vidioc_reqbufs(struct file *filp, void *priv,
+ struct v4l2_requestbuffers *req)
+{
+ struct mcam_camera *cam = filp->private_data;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = vb2_reqbufs(&cam->vb_queue, req);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+static int mcam_vidioc_querybuf(struct file *filp, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct mcam_camera *cam = filp->private_data;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = vb2_querybuf(&cam->vb_queue, buf);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+static int mcam_vidioc_qbuf(struct file *filp, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct mcam_camera *cam = filp->private_data;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = vb2_qbuf(&cam->vb_queue, buf);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+static int mcam_vidioc_dqbuf(struct file *filp, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct mcam_camera *cam = filp->private_data;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = vb2_dqbuf(&cam->vb_queue, buf, filp->f_flags & O_NONBLOCK);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+
+static int mcam_vidioc_queryctrl(struct file *filp, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ struct mcam_camera *cam = priv;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = sensor_call(cam, core, queryctrl, qc);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+static int mcam_vidioc_g_ctrl(struct file *filp, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct mcam_camera *cam = priv;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = sensor_call(cam, core, g_ctrl, ctrl);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+static int mcam_vidioc_s_ctrl(struct file *filp, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct mcam_camera *cam = priv;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = sensor_call(cam, core, s_ctrl, ctrl);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+static int mcam_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strcpy(cap->driver, "marvell_ccic");
+ strcpy(cap->card, "marvell_ccic");
+ cap->version = 1;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+ return 0;
+}
+
+
+static int mcam_vidioc_enum_fmt_vid_cap(struct file *filp,
+ void *priv, struct v4l2_fmtdesc *fmt)
+{
+ if (fmt->index >= N_MCAM_FMTS)
+ return -EINVAL;
+ strlcpy(fmt->description, mcam_formats[fmt->index].desc,
+ sizeof(fmt->description));
+ fmt->pixelformat = mcam_formats[fmt->index].pixelformat;
+ return 0;
+}
+
+static int mcam_vidioc_try_fmt_vid_cap(struct file *filp, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct mcam_camera *cam = priv;
+ struct mcam_format_struct *f;
+ struct v4l2_pix_format *pix = &fmt->fmt.pix;
+ struct v4l2_mbus_framefmt mbus_fmt;
+ int ret;
+
+ f = mcam_find_format(pix->pixelformat);
+ pix->pixelformat = f->pixelformat;
+ v4l2_fill_mbus_format(&mbus_fmt, pix, f->mbus_code);
+ mutex_lock(&cam->s_mutex);
+ ret = sensor_call(cam, video, try_mbus_fmt, &mbus_fmt);
+ mutex_unlock(&cam->s_mutex);
+ v4l2_fill_pix_format(pix, &mbus_fmt);
+ pix->bytesperline = pix->width * f->bpp;
+ pix->sizeimage = pix->height * pix->bytesperline;
+ return ret;
+}
+
+static int mcam_vidioc_s_fmt_vid_cap(struct file *filp, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct mcam_camera *cam = priv;
+ struct mcam_format_struct *f;
+ int ret;
+
+ /*
+ * Can't do anything if the device is not idle
+ * Also can't if there are streaming buffers in place.
+ */
+ if (cam->state != S_IDLE || cam->vb_queue.num_buffers > 0)
+ return -EBUSY;
+
+ f = mcam_find_format(fmt->fmt.pix.pixelformat);
+
+ /*
+ * See if the formatting works in principle.
+ */
+ ret = mcam_vidioc_try_fmt_vid_cap(filp, priv, fmt);
+ if (ret)
+ return ret;
+ /*
+ * Now we start to change things for real, so let's do it
+ * under lock.
+ */
+ mutex_lock(&cam->s_mutex);
+ cam->pix_format = fmt->fmt.pix;
+ cam->mbus_code = f->mbus_code;
+
+ /*
+ * Make sure we have appropriate DMA buffers.
+ */
+ if (cam->buffer_mode == B_vmalloc) {
+ ret = mcam_check_dma_buffers(cam);
+ if (ret)
+ goto out;
+ }
+ mcam_set_config_needed(cam, 1);
+ ret = 0;
+out:
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+/*
+ * Return our stored notion of how the camera is/should be configured.
+ * The V4l2 spec wants us to be smarter, and actually get this from
+ * the camera (and not mess with it at open time). Someday.
+ */
+static int mcam_vidioc_g_fmt_vid_cap(struct file *filp, void *priv,
+ struct v4l2_format *f)
+{
+ struct mcam_camera *cam = priv;
+
+ f->fmt.pix = cam->pix_format;
+ return 0;
+}
+
+/*
+ * We only have one input - the sensor - so minimize the nonsense here.
+ */
+static int mcam_vidioc_enum_input(struct file *filp, void *priv,
+ struct v4l2_input *input)
+{
+ if (input->index != 0)
+ return -EINVAL;
+
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+ input->std = V4L2_STD_ALL; /* Not sure what should go here */
+ strcpy(input->name, "Camera");
+ return 0;
+}
+
+static int mcam_vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int mcam_vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ if (i != 0)
+ return -EINVAL;
+ return 0;
+}
+
+/* from vivi.c */
+static int mcam_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id *a)
+{
+ return 0;
+}
+
+/*
+ * G/S_PARM. Most of this is done by the sensor, but we are
+ * the level which controls the number of read buffers.
+ */
+static int mcam_vidioc_g_parm(struct file *filp, void *priv,
+ struct v4l2_streamparm *parms)
+{
+ struct mcam_camera *cam = priv;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = sensor_call(cam, video, g_parm, parms);
+ mutex_unlock(&cam->s_mutex);
+ parms->parm.capture.readbuffers = n_dma_bufs;
+ return ret;
+}
+
+static int mcam_vidioc_s_parm(struct file *filp, void *priv,
+ struct v4l2_streamparm *parms)
+{
+ struct mcam_camera *cam = priv;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = sensor_call(cam, video, s_parm, parms);
+ mutex_unlock(&cam->s_mutex);
+ parms->parm.capture.readbuffers = n_dma_bufs;
+ return ret;
+}
+
+static int mcam_vidioc_g_chip_ident(struct file *file, void *priv,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct mcam_camera *cam = priv;
+
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+ if (v4l2_chip_match_host(&chip->match)) {
+ chip->ident = cam->chip_id;
+ return 0;
+ }
+ return sensor_call(cam, core, g_chip_ident, chip);
+}
+
+static int mcam_vidioc_enum_framesizes(struct file *filp, void *priv,
+ struct v4l2_frmsizeenum *sizes)
+{
+ struct mcam_camera *cam = priv;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = sensor_call(cam, video, enum_framesizes, sizes);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+static int mcam_vidioc_enum_frameintervals(struct file *filp, void *priv,
+ struct v4l2_frmivalenum *interval)
+{
+ struct mcam_camera *cam = priv;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = sensor_call(cam, video, enum_frameintervals, interval);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int mcam_vidioc_g_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct mcam_camera *cam = priv;
+
+ if (v4l2_chip_match_host(&reg->match)) {
+ reg->val = mcam_reg_read(cam, reg->reg);
+ reg->size = 4;
+ return 0;
+ }
+ return sensor_call(cam, core, g_register, reg);
+}
+
+static int mcam_vidioc_s_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct mcam_camera *cam = priv;
+
+ if (v4l2_chip_match_host(&reg->match)) {
+ mcam_reg_write(cam, reg->reg, reg->val);
+ return 0;
+ }
+ return sensor_call(cam, core, s_register, reg);
+}
+#endif
+
+static const struct v4l2_ioctl_ops mcam_v4l_ioctl_ops = {
+ .vidioc_querycap = mcam_vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = mcam_vidioc_enum_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = mcam_vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = mcam_vidioc_s_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = mcam_vidioc_g_fmt_vid_cap,
+ .vidioc_enum_input = mcam_vidioc_enum_input,
+ .vidioc_g_input = mcam_vidioc_g_input,
+ .vidioc_s_input = mcam_vidioc_s_input,
+ .vidioc_s_std = mcam_vidioc_s_std,
+ .vidioc_reqbufs = mcam_vidioc_reqbufs,
+ .vidioc_querybuf = mcam_vidioc_querybuf,
+ .vidioc_qbuf = mcam_vidioc_qbuf,
+ .vidioc_dqbuf = mcam_vidioc_dqbuf,
+ .vidioc_streamon = mcam_vidioc_streamon,
+ .vidioc_streamoff = mcam_vidioc_streamoff,
+ .vidioc_queryctrl = mcam_vidioc_queryctrl,
+ .vidioc_g_ctrl = mcam_vidioc_g_ctrl,
+ .vidioc_s_ctrl = mcam_vidioc_s_ctrl,
+ .vidioc_g_parm = mcam_vidioc_g_parm,
+ .vidioc_s_parm = mcam_vidioc_s_parm,
+ .vidioc_enum_framesizes = mcam_vidioc_enum_framesizes,
+ .vidioc_enum_frameintervals = mcam_vidioc_enum_frameintervals,
+ .vidioc_g_chip_ident = mcam_vidioc_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = mcam_vidioc_g_register,
+ .vidioc_s_register = mcam_vidioc_s_register,
+#endif
+};
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Our various file operations.
+ */
+static int mcam_v4l_open(struct file *filp)
+{
+ struct mcam_camera *cam = video_drvdata(filp);
+ int ret = 0;
+
+ filp->private_data = cam;
+
+ frames = singles = delivered = 0;
+ mutex_lock(&cam->s_mutex);
+ if (cam->users == 0) {
+ ret = mcam_setup_vb2(cam);
+ if (ret)
+ goto out;
+ mcam_ctlr_power_up(cam);
+ __mcam_cam_reset(cam);
+ mcam_set_config_needed(cam, 1);
+ }
+ (cam->users)++;
+out:
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+static int mcam_v4l_release(struct file *filp)
+{
+ struct mcam_camera *cam = filp->private_data;
+
+ cam_dbg(cam, "Release, %d frames, %d singles, %d delivered\n", frames,
+ singles, delivered);
+ mutex_lock(&cam->s_mutex);
+ (cam->users)--;
+ if (cam->users == 0) {
+ mcam_ctlr_stop_dma(cam);
+ mcam_cleanup_vb2(cam);
+ mcam_ctlr_power_down(cam);
+ if (cam->buffer_mode == B_vmalloc && alloc_bufs_at_read)
+ mcam_free_dma_bufs(cam);
+ }
+ mutex_unlock(&cam->s_mutex);
+ return 0;
+}
+
+static ssize_t mcam_v4l_read(struct file *filp,
+ char __user *buffer, size_t len, loff_t *pos)
+{
+ struct mcam_camera *cam = filp->private_data;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = vb2_read(&cam->vb_queue, buffer, len, pos,
+ filp->f_flags & O_NONBLOCK);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+
+static unsigned int mcam_v4l_poll(struct file *filp,
+ struct poll_table_struct *pt)
+{
+ struct mcam_camera *cam = filp->private_data;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = vb2_poll(&cam->vb_queue, filp, pt);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+static int mcam_v4l_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct mcam_camera *cam = filp->private_data;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+ ret = vb2_mmap(&cam->vb_queue, vma);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+}
+
+
+
+static const struct v4l2_file_operations mcam_v4l_fops = {
+ .owner = THIS_MODULE,
+ .open = mcam_v4l_open,
+ .release = mcam_v4l_release,
+ .read = mcam_v4l_read,
+ .poll = mcam_v4l_poll,
+ .mmap = mcam_v4l_mmap,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+
+/*
+ * This template device holds all of those v4l2 methods; we
+ * clone it for specific real devices.
+ */
+static struct video_device mcam_v4l_template = {
+ .name = "mcam",
+ .tvnorms = V4L2_STD_NTSC_M,
+ .current_norm = V4L2_STD_NTSC_M, /* make mplayer happy */
+
+ .fops = &mcam_v4l_fops,
+ .ioctl_ops = &mcam_v4l_ioctl_ops,
+ .release = video_device_release_empty,
+};
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Interrupt handler stuff
+ */
+static void mcam_frame_complete(struct mcam_camera *cam, int frame)
+{
+ /*
+ * Basic frame housekeeping.
+ */
+ set_bit(frame, &cam->flags);
+ clear_bit(CF_DMA_ACTIVE, &cam->flags);
+ cam->next_buf = frame;
+ cam->buf_seq[frame] = ++(cam->sequence);
+ frames++;
+ /*
+ * "This should never happen"
+ */
+ if (cam->state != S_STREAMING)
+ return;
+ /*
+ * Process the frame and set up the next one.
+ */
+ cam->frame_complete(cam, frame);
+}
+
+
+/*
+ * The interrupt handler; this needs to be called from the
+ * platform irq handler with the lock held.
+ */
+int mccic_irq(struct mcam_camera *cam, unsigned int irqs)
+{
+ unsigned int frame, handled = 0;
+
+ mcam_reg_write(cam, REG_IRQSTAT, FRAMEIRQS); /* Clear'em all */
+ /*
+ * Handle any frame completions. There really should
+ * not be more than one of these, or we have fallen
+ * far behind.
+ *
+ * When running in S/G mode, the frame number lacks any
+ * real meaning - there's only one descriptor array - but
+ * the controller still picks a different one to signal
+ * each time.
+ */
+ for (frame = 0; frame < cam->nbufs; frame++)
+ if (irqs & (IRQ_EOF0 << frame)) {
+ mcam_frame_complete(cam, frame);
+ handled = 1;
+ if (cam->buffer_mode == B_DMA_sg)
+ break;
+ }
+ /*
+ * If a frame starts, note that we have DMA active. This
+ * code assumes that we won't get multiple frame interrupts
+ * at once; may want to rethink that.
+ */
+ if (irqs & (IRQ_SOF0 | IRQ_SOF1 | IRQ_SOF2)) {
+ set_bit(CF_DMA_ACTIVE, &cam->flags);
+ handled = 1;
+ if (cam->buffer_mode == B_DMA_sg)
+ mcam_ctlr_stop(cam);
+ }
+ return handled;
+}
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Registration and such.
+ */
+static struct ov7670_config sensor_cfg = {
+ /*
+ * Exclude QCIF mode, because it only captures a tiny portion
+ * of the sensor FOV
+ */
+ .min_width = 320,
+ .min_height = 240,
+};
+
+
+int mccic_register(struct mcam_camera *cam)
+{
+ struct i2c_board_info ov7670_info = {
+ .type = "ov7670",
+ .addr = 0x42 >> 1,
+ .platform_data = &sensor_cfg,
+ };
+ int ret;
+
+ /*
+ * Validate the requested buffer mode.
+ */
+ if (buffer_mode >= 0)
+ cam->buffer_mode = buffer_mode;
+ if (cam->buffer_mode == B_DMA_sg &&
+ cam->chip_id == V4L2_IDENT_CAFE) {
+ printk(KERN_ERR "marvell-cam: Cafe can't do S/G I/O, "
+ "attempting vmalloc mode instead\n");
+ cam->buffer_mode = B_vmalloc;
+ }
+ if (!mcam_buffer_mode_supported(cam->buffer_mode)) {
+ printk(KERN_ERR "marvell-cam: buffer mode %d unsupported\n",
+ cam->buffer_mode);
+ return -EINVAL;
+ }
+ /*
+ * Register with V4L
+ */
+ ret = v4l2_device_register(cam->dev, &cam->v4l2_dev);
+ if (ret)
+ return ret;
+
+ mutex_init(&cam->s_mutex);
+ cam->state = S_NOTREADY;
+ mcam_set_config_needed(cam, 1);
+ cam->pix_format = mcam_def_pix_format;
+ cam->mbus_code = mcam_def_mbus_code;
+ INIT_LIST_HEAD(&cam->buffers);
+ mcam_ctlr_init(cam);
+
+ /*
+ * Try to find the sensor.
+ */
+ sensor_cfg.clock_speed = cam->clock_speed;
+ sensor_cfg.use_smbus = cam->use_smbus;
+ cam->sensor_addr = ov7670_info.addr;
+ cam->sensor = v4l2_i2c_new_subdev_board(&cam->v4l2_dev,
+ cam->i2c_adapter, &ov7670_info, NULL);
+ if (cam->sensor == NULL) {
+ ret = -ENODEV;
+ goto out_unregister;
+ }
+
+ ret = mcam_cam_init(cam);
+ if (ret)
+ goto out_unregister;
+ /*
+ * Get the v4l2 setup done.
+ */
+ mutex_lock(&cam->s_mutex);
+ cam->vdev = mcam_v4l_template;
+ cam->vdev.debug = 0;
+ cam->vdev.v4l2_dev = &cam->v4l2_dev;
+ ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1);
+ if (ret)
+ goto out;
+ video_set_drvdata(&cam->vdev, cam);
+
+ /*
+ * If so requested, try to get our DMA buffers now.
+ */
+ if (cam->buffer_mode == B_vmalloc && !alloc_bufs_at_read) {
+ if (mcam_alloc_dma_bufs(cam, 1))
+ cam_warn(cam, "Unable to alloc DMA buffers at load"
+ " will try again later.");
+ }
+
+out:
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+out_unregister:
+ v4l2_device_unregister(&cam->v4l2_dev);
+ return ret;
+}
+
+
+void mccic_shutdown(struct mcam_camera *cam)
+{
+ /*
+ * If we have no users (and we really, really should have no
+ * users) the device will already be powered down. Trying to
+ * take it down again will wedge the machine, which is frowned
+ * upon.
+ */
+ if (cam->users > 0) {
+ cam_warn(cam, "Removing a device with users!\n");
+ mcam_ctlr_power_down(cam);
+ }
+ vb2_queue_release(&cam->vb_queue);
+ if (cam->buffer_mode == B_vmalloc)
+ mcam_free_dma_bufs(cam);
+ video_unregister_device(&cam->vdev);
+ v4l2_device_unregister(&cam->v4l2_dev);
+}
+
+/*
+ * Power management
+ */
+#ifdef CONFIG_PM
+
+void mccic_suspend(struct mcam_camera *cam)
+{
+ mutex_lock(&cam->s_mutex);
+ if (cam->users > 0) {
+ enum mcam_state cstate = cam->state;
+
+ mcam_ctlr_stop_dma(cam);
+ mcam_ctlr_power_down(cam);
+ cam->state = cstate;
+ }
+ mutex_unlock(&cam->s_mutex);
+}
+
+int mccic_resume(struct mcam_camera *cam)
+{
+ int ret = 0;
+
+ mutex_lock(&cam->s_mutex);
+ if (cam->users > 0) {
+ mcam_ctlr_power_up(cam);
+ __mcam_cam_reset(cam);
+ } else {
+ mcam_ctlr_power_down(cam);
+ }
+ mutex_unlock(&cam->s_mutex);
+
+ set_bit(CF_CONFIG_NEEDED, &cam->flags);
+ if (cam->state == S_STREAMING) {
+ /*
+ * If there was a buffer in the DMA engine at suspend
+ * time, put it back on the queue or we'll forget about it.
+ */
+ if (cam->buffer_mode == B_DMA_sg && cam->vb_bufs[0])
+ list_add(&cam->vb_bufs[0]->queue, &cam->buffers);
+ ret = mcam_read_setup(cam);
+ }
+ return ret;
+}
+#endif /* CONFIG_PM */
diff --git a/drivers/media/video/marvell-ccic/mcam-core.h b/drivers/media/video/marvell-ccic/mcam-core.h
new file mode 100644
index 00000000..bd6acba9
--- /dev/null
+++ b/drivers/media/video/marvell-ccic/mcam-core.h
@@ -0,0 +1,322 @@
+/*
+ * Marvell camera core structures.
+ *
+ * Copyright 2011 Jonathan Corbet corbet@lwn.net
+ */
+#ifndef _MCAM_CORE_H
+#define _MCAM_CORE_H
+
+#include <linux/list.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/videobuf2-core.h>
+
+/*
+ * Create our own symbols for the supported buffer modes, but, for now,
+ * base them entirely on which videobuf2 options have been selected.
+ */
+#if defined(CONFIG_VIDEOBUF2_VMALLOC) || defined(CONFIG_VIDEOBUF2_VMALLOC_MODULE)
+#define MCAM_MODE_VMALLOC 1
+#endif
+
+#if defined(CONFIG_VIDEOBUF2_DMA_CONTIG) || defined(CONFIG_VIDEOBUF2_DMA_CONTIG_MODULE)
+#define MCAM_MODE_DMA_CONTIG 1
+#endif
+
+#if defined(CONFIG_VIDEOBUF2_DMA_SG) || defined(CONFIG_VIDEOBUF2_DMA_SG_MODULE)
+#define MCAM_MODE_DMA_SG 1
+#endif
+
+#if !defined(MCAM_MODE_VMALLOC) && !defined(MCAM_MODE_DMA_CONTIG) && \
+ !defined(MCAM_MODE_DMA_SG)
+#error One of the videobuf buffer modes must be selected in the config
+#endif
+
+
+enum mcam_state {
+ S_NOTREADY, /* Not yet initialized */
+ S_IDLE, /* Just hanging around */
+ S_FLAKED, /* Some sort of problem */
+ S_STREAMING, /* Streaming data */
+ S_BUFWAIT /* streaming requested but no buffers yet */
+};
+#define MAX_DMA_BUFS 3
+
+/*
+ * Different platforms work best with different buffer modes, so we
+ * let the platform pick.
+ */
+enum mcam_buffer_mode {
+ B_vmalloc = 0,
+ B_DMA_contig = 1,
+ B_DMA_sg = 2
+};
+
+/*
+ * Is a given buffer mode supported by the current kernel configuration?
+ */
+static inline int mcam_buffer_mode_supported(enum mcam_buffer_mode mode)
+{
+ switch (mode) {
+#ifdef MCAM_MODE_VMALLOC
+ case B_vmalloc:
+#endif
+#ifdef MCAM_MODE_DMA_CONTIG
+ case B_DMA_contig:
+#endif
+#ifdef MCAM_MODE_DMA_SG
+ case B_DMA_sg:
+#endif
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+
+/*
+ * A description of one of our devices.
+ * Locking: controlled by s_mutex. Certain fields, however, require
+ * the dev_lock spinlock; they are marked as such by comments.
+ * dev_lock is also required for access to device registers.
+ */
+struct mcam_camera {
+ /*
+ * These fields should be set by the platform code prior to
+ * calling mcam_register().
+ */
+ struct i2c_adapter *i2c_adapter;
+ unsigned char __iomem *regs;
+ spinlock_t dev_lock;
+ struct device *dev; /* For messages, dma alloc */
+ unsigned int chip_id;
+ short int clock_speed; /* Sensor clock speed, default 30 */
+ short int use_smbus; /* SMBUS or straight I2c? */
+ enum mcam_buffer_mode buffer_mode;
+ /*
+ * Callbacks from the core to the platform code.
+ */
+ void (*plat_power_up) (struct mcam_camera *cam);
+ void (*plat_power_down) (struct mcam_camera *cam);
+
+ /*
+ * Everything below here is private to the mcam core and
+ * should not be touched by the platform code.
+ */
+ struct v4l2_device v4l2_dev;
+ enum mcam_state state;
+ unsigned long flags; /* Buffer status, mainly (dev_lock) */
+ int users; /* How many open FDs */
+
+ /*
+ * Subsystem structures.
+ */
+ struct video_device vdev;
+ struct v4l2_subdev *sensor;
+ unsigned short sensor_addr;
+
+ /* Videobuf2 stuff */
+ struct vb2_queue vb_queue;
+ struct list_head buffers; /* Available frames */
+
+ unsigned int nbufs; /* How many are alloc'd */
+ int next_buf; /* Next to consume (dev_lock) */
+
+ /* DMA buffers - vmalloc mode */
+#ifdef MCAM_MODE_VMALLOC
+ unsigned int dma_buf_size; /* allocated size */
+ void *dma_bufs[MAX_DMA_BUFS]; /* Internal buffer addresses */
+ dma_addr_t dma_handles[MAX_DMA_BUFS]; /* Buffer bus addresses */
+ struct tasklet_struct s_tasklet;
+#endif
+ unsigned int sequence; /* Frame sequence number */
+ unsigned int buf_seq[MAX_DMA_BUFS]; /* Sequence for individual bufs */
+
+ /* DMA buffers - DMA modes */
+ struct mcam_vb_buffer *vb_bufs[MAX_DMA_BUFS];
+ struct vb2_alloc_ctx *vb_alloc_ctx;
+
+ /* Mode-specific ops, set at open time */
+ void (*dma_setup)(struct mcam_camera *cam);
+ void (*frame_complete)(struct mcam_camera *cam, int frame);
+
+ /* Current operating parameters */
+ u32 sensor_type; /* Currently ov7670 only */
+ struct v4l2_pix_format pix_format;
+ enum v4l2_mbus_pixelcode mbus_code;
+
+ /* Locks */
+ struct mutex s_mutex; /* Access to this structure */
+};
+
+
+/*
+ * Register I/O functions. These are here because the platform code
+ * may legitimately need to mess with the register space.
+ */
+/*
+ * Device register I/O
+ */
+static inline void mcam_reg_write(struct mcam_camera *cam, unsigned int reg,
+ unsigned int val)
+{
+ iowrite32(val, cam->regs + reg);
+}
+
+static inline unsigned int mcam_reg_read(struct mcam_camera *cam,
+ unsigned int reg)
+{
+ return ioread32(cam->regs + reg);
+}
+
+
+static inline void mcam_reg_write_mask(struct mcam_camera *cam, unsigned int reg,
+ unsigned int val, unsigned int mask)
+{
+ unsigned int v = mcam_reg_read(cam, reg);
+
+ v = (v & ~mask) | (val & mask);
+ mcam_reg_write(cam, reg, v);
+}
+
+static inline void mcam_reg_clear_bit(struct mcam_camera *cam,
+ unsigned int reg, unsigned int val)
+{
+ mcam_reg_write_mask(cam, reg, 0, val);
+}
+
+static inline void mcam_reg_set_bit(struct mcam_camera *cam,
+ unsigned int reg, unsigned int val)
+{
+ mcam_reg_write_mask(cam, reg, val, val);
+}
+
+/*
+ * Functions for use by platform code.
+ */
+int mccic_register(struct mcam_camera *cam);
+int mccic_irq(struct mcam_camera *cam, unsigned int irqs);
+void mccic_shutdown(struct mcam_camera *cam);
+#ifdef CONFIG_PM
+void mccic_suspend(struct mcam_camera *cam);
+int mccic_resume(struct mcam_camera *cam);
+#endif
+
+/*
+ * Register definitions for the m88alp01 camera interface. Offsets in bytes
+ * as given in the spec.
+ */
+#define REG_Y0BAR 0x00
+#define REG_Y1BAR 0x04
+#define REG_Y2BAR 0x08
+/* ... */
+
+#define REG_IMGPITCH 0x24 /* Image pitch register */
+#define IMGP_YP_SHFT 2 /* Y pitch params */
+#define IMGP_YP_MASK 0x00003ffc /* Y pitch field */
+#define IMGP_UVP_SHFT 18 /* UV pitch (planar) */
+#define IMGP_UVP_MASK 0x3ffc0000
+#define REG_IRQSTATRAW 0x28 /* RAW IRQ Status */
+#define IRQ_EOF0 0x00000001 /* End of frame 0 */
+#define IRQ_EOF1 0x00000002 /* End of frame 1 */
+#define IRQ_EOF2 0x00000004 /* End of frame 2 */
+#define IRQ_SOF0 0x00000008 /* Start of frame 0 */
+#define IRQ_SOF1 0x00000010 /* Start of frame 1 */
+#define IRQ_SOF2 0x00000020 /* Start of frame 2 */
+#define IRQ_OVERFLOW 0x00000040 /* FIFO overflow */
+#define IRQ_TWSIW 0x00010000 /* TWSI (smbus) write */
+#define IRQ_TWSIR 0x00020000 /* TWSI read */
+#define IRQ_TWSIE 0x00040000 /* TWSI error */
+#define TWSIIRQS (IRQ_TWSIW|IRQ_TWSIR|IRQ_TWSIE)
+#define FRAMEIRQS (IRQ_EOF0|IRQ_EOF1|IRQ_EOF2|IRQ_SOF0|IRQ_SOF1|IRQ_SOF2)
+#define ALLIRQS (TWSIIRQS|FRAMEIRQS|IRQ_OVERFLOW)
+#define REG_IRQMASK 0x2c /* IRQ mask - same bits as IRQSTAT */
+#define REG_IRQSTAT 0x30 /* IRQ status / clear */
+
+#define REG_IMGSIZE 0x34 /* Image size */
+#define IMGSZ_V_MASK 0x1fff0000
+#define IMGSZ_V_SHIFT 16
+#define IMGSZ_H_MASK 0x00003fff
+#define REG_IMGOFFSET 0x38 /* IMage offset */
+
+#define REG_CTRL0 0x3c /* Control 0 */
+#define C0_ENABLE 0x00000001 /* Makes the whole thing go */
+
+/* Mask for all the format bits */
+#define C0_DF_MASK 0x00fffffc /* Bits 2-23 */
+
+/* RGB ordering */
+#define C0_RGB4_RGBX 0x00000000
+#define C0_RGB4_XRGB 0x00000004
+#define C0_RGB4_BGRX 0x00000008
+#define C0_RGB4_XBGR 0x0000000c
+#define C0_RGB5_RGGB 0x00000000
+#define C0_RGB5_GRBG 0x00000004
+#define C0_RGB5_GBRG 0x00000008
+#define C0_RGB5_BGGR 0x0000000c
+
+/* Spec has two fields for DIN and DOUT, but they must match, so
+ combine them here. */
+#define C0_DF_YUV 0x00000000 /* Data is YUV */
+#define C0_DF_RGB 0x000000a0 /* ... RGB */
+#define C0_DF_BAYER 0x00000140 /* ... Bayer */
+/* 8-8-8 must be missing from the below - ask */
+#define C0_RGBF_565 0x00000000
+#define C0_RGBF_444 0x00000800
+#define C0_RGB_BGR 0x00001000 /* Blue comes first */
+#define C0_YUV_PLANAR 0x00000000 /* YUV 422 planar format */
+#define C0_YUV_PACKED 0x00008000 /* YUV 422 packed */
+#define C0_YUV_420PL 0x0000a000 /* YUV 420 planar */
+/* Think that 420 packed must be 111 - ask */
+#define C0_YUVE_YUYV 0x00000000 /* Y1CbY0Cr */
+#define C0_YUVE_YVYU 0x00010000 /* Y1CrY0Cb */
+#define C0_YUVE_VYUY 0x00020000 /* CrY1CbY0 */
+#define C0_YUVE_UYVY 0x00030000 /* CbY1CrY0 */
+#define C0_YUVE_XYUV 0x00000000 /* 420: .YUV */
+#define C0_YUVE_XYVU 0x00010000 /* 420: .YVU */
+#define C0_YUVE_XUVY 0x00020000 /* 420: .UVY */
+#define C0_YUVE_XVUY 0x00030000 /* 420: .VUY */
+/* Bayer bits 18,19 if needed */
+#define C0_HPOL_LOW 0x01000000 /* HSYNC polarity active low */
+#define C0_VPOL_LOW 0x02000000 /* VSYNC polarity active low */
+#define C0_VCLK_LOW 0x04000000 /* VCLK on falling edge */
+#define C0_DOWNSCALE 0x08000000 /* Enable downscaler */
+#define C0_SIFM_MASK 0xc0000000 /* SIF mode bits */
+#define C0_SIF_HVSYNC 0x00000000 /* Use H/VSYNC */
+#define CO_SOF_NOSYNC 0x40000000 /* Use inband active signaling */
+
+/* Bits below C1_444ALPHA are not present in Cafe */
+#define REG_CTRL1 0x40 /* Control 1 */
+#define C1_CLKGATE 0x00000001 /* Sensor clock gate */
+#define C1_DESC_ENA 0x00000100 /* DMA descriptor enable */
+#define C1_DESC_3WORD 0x00000200 /* Three-word descriptors used */
+#define C1_444ALPHA 0x00f00000 /* Alpha field in RGB444 */
+#define C1_ALPHA_SHFT 20
+#define C1_DMAB32 0x00000000 /* 32-byte DMA burst */
+#define C1_DMAB16 0x02000000 /* 16-byte DMA burst */
+#define C1_DMAB64 0x04000000 /* 64-byte DMA burst */
+#define C1_DMAB_MASK 0x06000000
+#define C1_TWOBUFS 0x08000000 /* Use only two DMA buffers */
+#define C1_PWRDWN 0x10000000 /* Power down */
+
+#define REG_CLKCTRL 0x88 /* Clock control */
+#define CLK_DIV_MASK 0x0000ffff /* Upper bits RW "reserved" */
+
+/* This appears to be a Cafe-only register */
+#define REG_UBAR 0xc4 /* Upper base address register */
+
+/* Armada 610 DMA descriptor registers */
+#define REG_DMA_DESC_Y 0x200
+#define REG_DMA_DESC_U 0x204
+#define REG_DMA_DESC_V 0x208
+#define REG_DESC_LEN_Y 0x20c /* Lengths are in bytes */
+#define REG_DESC_LEN_U 0x210
+#define REG_DESC_LEN_V 0x214
+
+/*
+ * Useful stuff that probably belongs somewhere global.
+ */
+#define VGA_WIDTH 640
+#define VGA_HEIGHT 480
+
+#endif /* _MCAM_CORE_H */
diff --git a/drivers/media/video/marvell-ccic/mmp-driver.c b/drivers/media/video/marvell-ccic/mmp-driver.c
new file mode 100644
index 00000000..c4c17fe7
--- /dev/null
+++ b/drivers/media/video/marvell-ccic/mmp-driver.c
@@ -0,0 +1,380 @@
+/*
+ * Support for the camera device found on Marvell MMP processors; known
+ * to work with the Armada 610 as used in the OLPC 1.75 system.
+ *
+ * Copyright 2011 Jonathan Corbet <corbet@lwn.net>
+ *
+ * This file may be distributed under the terms of the GNU General
+ * Public License, version 2.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/i2c-gpio.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/mmp-camera.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/pm.h>
+
+#include "mcam-core.h"
+
+MODULE_ALIAS("platform:mmp-camera");
+MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
+MODULE_LICENSE("GPL");
+
+struct mmp_camera {
+ void *power_regs;
+ struct platform_device *pdev;
+ struct mcam_camera mcam;
+ struct list_head devlist;
+ int irq;
+};
+
+static inline struct mmp_camera *mcam_to_cam(struct mcam_camera *mcam)
+{
+ return container_of(mcam, struct mmp_camera, mcam);
+}
+
+/*
+ * A silly little infrastructure so we can keep track of our devices.
+ * Chances are that we will never have more than one of them, but
+ * the Armada 610 *does* have two controllers...
+ */
+
+static LIST_HEAD(mmpcam_devices);
+static struct mutex mmpcam_devices_lock;
+
+static void mmpcam_add_device(struct mmp_camera *cam)
+{
+ mutex_lock(&mmpcam_devices_lock);
+ list_add(&cam->devlist, &mmpcam_devices);
+ mutex_unlock(&mmpcam_devices_lock);
+}
+
+static void mmpcam_remove_device(struct mmp_camera *cam)
+{
+ mutex_lock(&mmpcam_devices_lock);
+ list_del(&cam->devlist);
+ mutex_unlock(&mmpcam_devices_lock);
+}
+
+/*
+ * Platform dev remove passes us a platform_device, and there's
+ * no handy unused drvdata to stash a backpointer in. So just
+ * dig it out of our list.
+ */
+static struct mmp_camera *mmpcam_find_device(struct platform_device *pdev)
+{
+ struct mmp_camera *cam;
+
+ mutex_lock(&mmpcam_devices_lock);
+ list_for_each_entry(cam, &mmpcam_devices, devlist) {
+ if (cam->pdev == pdev) {
+ mutex_unlock(&mmpcam_devices_lock);
+ return cam;
+ }
+ }
+ mutex_unlock(&mmpcam_devices_lock);
+ return NULL;
+}
+
+
+
+
+/*
+ * Power-related registers; this almost certainly belongs
+ * somewhere else.
+ *
+ * ARMADA 610 register manual, sec 7.2.1, p1842.
+ */
+#define CPU_SUBSYS_PMU_BASE 0xd4282800
+#define REG_CCIC_DCGCR 0x28 /* CCIC dyn clock gate ctrl reg */
+#define REG_CCIC_CRCR 0x50 /* CCIC clk reset ctrl reg */
+
+/*
+ * Power control.
+ */
+static void mmpcam_power_up_ctlr(struct mmp_camera *cam)
+{
+ iowrite32(0x3f, cam->power_regs + REG_CCIC_DCGCR);
+ iowrite32(0x3805b, cam->power_regs + REG_CCIC_CRCR);
+ mdelay(1);
+}
+
+static void mmpcam_power_up(struct mcam_camera *mcam)
+{
+ struct mmp_camera *cam = mcam_to_cam(mcam);
+ struct mmp_camera_platform_data *pdata;
+/*
+ * Turn on power and clocks to the controller.
+ */
+ mmpcam_power_up_ctlr(cam);
+/*
+ * Provide power to the sensor.
+ */
+ mcam_reg_write(mcam, REG_CLKCTRL, 0x60000002);
+ pdata = cam->pdev->dev.platform_data;
+ gpio_set_value(pdata->sensor_power_gpio, 1);
+ mdelay(5);
+ mcam_reg_clear_bit(mcam, REG_CTRL1, 0x10000000);
+ gpio_set_value(pdata->sensor_reset_gpio, 0); /* reset is active low */
+ mdelay(5);
+ gpio_set_value(pdata->sensor_reset_gpio, 1); /* reset is active low */
+ mdelay(5);
+}
+
+static void mmpcam_power_down(struct mcam_camera *mcam)
+{
+ struct mmp_camera *cam = mcam_to_cam(mcam);
+ struct mmp_camera_platform_data *pdata;
+/*
+ * Turn off clocks and set reset lines
+ */
+ iowrite32(0, cam->power_regs + REG_CCIC_DCGCR);
+ iowrite32(0, cam->power_regs + REG_CCIC_CRCR);
+/*
+ * Shut down the sensor.
+ */
+ pdata = cam->pdev->dev.platform_data;
+ gpio_set_value(pdata->sensor_power_gpio, 0);
+ gpio_set_value(pdata->sensor_reset_gpio, 0);
+}
+
+
+static irqreturn_t mmpcam_irq(int irq, void *data)
+{
+ struct mcam_camera *mcam = data;
+ unsigned int irqs, handled;
+
+ spin_lock(&mcam->dev_lock);
+ irqs = mcam_reg_read(mcam, REG_IRQSTAT);
+ handled = mccic_irq(mcam, irqs);
+ spin_unlock(&mcam->dev_lock);
+ return IRQ_RETVAL(handled);
+}
+
+
+static int mmpcam_probe(struct platform_device *pdev)
+{
+ struct mmp_camera *cam;
+ struct mcam_camera *mcam;
+ struct resource *res;
+ struct mmp_camera_platform_data *pdata;
+ int ret;
+
+ cam = kzalloc(sizeof(*cam), GFP_KERNEL);
+ if (cam == NULL)
+ return -ENOMEM;
+ cam->pdev = pdev;
+ INIT_LIST_HEAD(&cam->devlist);
+
+ mcam = &cam->mcam;
+ mcam->plat_power_up = mmpcam_power_up;
+ mcam->plat_power_down = mmpcam_power_down;
+ mcam->dev = &pdev->dev;
+ mcam->use_smbus = 0;
+ mcam->chip_id = V4L2_IDENT_ARMADA610;
+ mcam->buffer_mode = B_DMA_sg;
+ spin_lock_init(&mcam->dev_lock);
+ /*
+ * Get our I/O memory.
+ */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "no iomem resource!\n");
+ ret = -ENODEV;
+ goto out_free;
+ }
+ mcam->regs = ioremap(res->start, resource_size(res));
+ if (mcam->regs == NULL) {
+ dev_err(&pdev->dev, "MMIO ioremap fail\n");
+ ret = -ENODEV;
+ goto out_free;
+ }
+ /*
+ * Power/clock memory is elsewhere; get it too. Perhaps this
+ * should really be managed outside of this driver?
+ */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "no power resource!\n");
+ ret = -ENODEV;
+ goto out_unmap1;
+ }
+ cam->power_regs = ioremap(res->start, resource_size(res));
+ if (cam->power_regs == NULL) {
+ dev_err(&pdev->dev, "power MMIO ioremap fail\n");
+ ret = -ENODEV;
+ goto out_unmap1;
+ }
+ /*
+ * Find the i2c adapter. This assumes, of course, that the
+ * i2c bus is already up and functioning.
+ */
+ pdata = pdev->dev.platform_data;
+ mcam->i2c_adapter = platform_get_drvdata(pdata->i2c_device);
+ if (mcam->i2c_adapter == NULL) {
+ ret = -ENODEV;
+ dev_err(&pdev->dev, "No i2c adapter\n");
+ goto out_unmap2;
+ }
+ /*
+ * Sensor GPIO pins.
+ */
+ ret = gpio_request(pdata->sensor_power_gpio, "cam-power");
+ if (ret) {
+ dev_err(&pdev->dev, "Can't get sensor power gpio %d",
+ pdata->sensor_power_gpio);
+ goto out_unmap2;
+ }
+ gpio_direction_output(pdata->sensor_power_gpio, 0);
+ ret = gpio_request(pdata->sensor_reset_gpio, "cam-reset");
+ if (ret) {
+ dev_err(&pdev->dev, "Can't get sensor reset gpio %d",
+ pdata->sensor_reset_gpio);
+ goto out_gpio;
+ }
+ gpio_direction_output(pdata->sensor_reset_gpio, 0);
+ /*
+ * Power the device up and hand it off to the core.
+ */
+ mmpcam_power_up(mcam);
+ ret = mccic_register(mcam);
+ if (ret)
+ goto out_gpio2;
+ /*
+ * Finally, set up our IRQ now that the core is ready to
+ * deal with it.
+ */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ ret = -ENODEV;
+ goto out_unregister;
+ }
+ cam->irq = res->start;
+ ret = request_irq(cam->irq, mmpcam_irq, IRQF_SHARED,
+ "mmp-camera", mcam);
+ if (ret == 0) {
+ mmpcam_add_device(cam);
+ return 0;
+ }
+
+out_unregister:
+ mccic_shutdown(mcam);
+out_gpio2:
+ mmpcam_power_down(mcam);
+ gpio_free(pdata->sensor_reset_gpio);
+out_gpio:
+ gpio_free(pdata->sensor_power_gpio);
+out_unmap2:
+ iounmap(cam->power_regs);
+out_unmap1:
+ iounmap(mcam->regs);
+out_free:
+ kfree(cam);
+ return ret;
+}
+
+
+static int mmpcam_remove(struct mmp_camera *cam)
+{
+ struct mcam_camera *mcam = &cam->mcam;
+ struct mmp_camera_platform_data *pdata;
+
+ mmpcam_remove_device(cam);
+ free_irq(cam->irq, mcam);
+ mccic_shutdown(mcam);
+ mmpcam_power_down(mcam);
+ pdata = cam->pdev->dev.platform_data;
+ gpio_free(pdata->sensor_reset_gpio);
+ gpio_free(pdata->sensor_power_gpio);
+ iounmap(cam->power_regs);
+ iounmap(mcam->regs);
+ kfree(cam);
+ return 0;
+}
+
+static int mmpcam_platform_remove(struct platform_device *pdev)
+{
+ struct mmp_camera *cam = mmpcam_find_device(pdev);
+
+ if (cam == NULL)
+ return -ENODEV;
+ return mmpcam_remove(cam);
+}
+
+/*
+ * Suspend/resume support.
+ */
+#ifdef CONFIG_PM
+
+static int mmpcam_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct mmp_camera *cam = mmpcam_find_device(pdev);
+
+ if (state.event != PM_EVENT_SUSPEND)
+ return 0;
+ mccic_suspend(&cam->mcam);
+ return 0;
+}
+
+static int mmpcam_resume(struct platform_device *pdev)
+{
+ struct mmp_camera *cam = mmpcam_find_device(pdev);
+
+ /*
+ * Power up unconditionally just in case the core tries to
+ * touch a register even if nothing was active before; trust
+ * me, it's better this way.
+ */
+ mmpcam_power_up_ctlr(cam);
+ return mccic_resume(&cam->mcam);
+}
+
+#endif
+
+
+static struct platform_driver mmpcam_driver = {
+ .probe = mmpcam_probe,
+ .remove = mmpcam_platform_remove,
+#ifdef CONFIG_PM
+ .suspend = mmpcam_suspend,
+ .resume = mmpcam_resume,
+#endif
+ .driver = {
+ .name = "mmp-camera",
+ .owner = THIS_MODULE
+ }
+};
+
+
+static int __init mmpcam_init_module(void)
+{
+ mutex_init(&mmpcam_devices_lock);
+ return platform_driver_register(&mmpcam_driver);
+}
+
+static void __exit mmpcam_exit_module(void)
+{
+ platform_driver_unregister(&mmpcam_driver);
+ /*
+ * platform_driver_unregister() should have emptied the list
+ */
+ if (!list_empty(&mmpcam_devices))
+ printk(KERN_ERR "mmp_camera leaving devices behind\n");
+}
+
+module_init(mmpcam_init_module);
+module_exit(mmpcam_exit_module);
diff --git a/drivers/media/video/mem2mem_testdev.c b/drivers/media/video/mem2mem_testdev.c
new file mode 100644
index 00000000..12897e8a
--- /dev/null
+++ b/drivers/media/video/mem2mem_testdev.c
@@ -0,0 +1,1050 @@
+/*
+ * A virtual v4l2-mem2mem example device.
+ *
+ * This is a virtual device driver for testing mem-to-mem videobuf framework.
+ * It simulates a device that uses memory buffers for both source and
+ * destination, processes the data and issues an "irq" (simulated by a timer).
+ * The device is capable of multi-instance, multi-buffer-per-transaction
+ * operation (via the mem2mem framework).
+ *
+ * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
+ * Pawel Osciak, <pawel@osciak.com>
+ * Marek Szyprowski, <m.szyprowski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <linux/platform_device.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-vmalloc.h>
+
+#define MEM2MEM_TEST_MODULE_NAME "mem2mem-testdev"
+
+MODULE_DESCRIPTION("Virtual device for mem2mem framework testing");
+MODULE_AUTHOR("Pawel Osciak, <pawel@osciak.com>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.1.1");
+
+#define MIN_W 32
+#define MIN_H 32
+#define MAX_W 640
+#define MAX_H 480
+#define DIM_ALIGN_MASK 0x08 /* 8-alignment for dimensions */
+
+/* Flags that indicate a format can be used for capture/output */
+#define MEM2MEM_CAPTURE (1 << 0)
+#define MEM2MEM_OUTPUT (1 << 1)
+
+#define MEM2MEM_NAME "m2m-testdev"
+
+/* Per queue */
+#define MEM2MEM_DEF_NUM_BUFS VIDEO_MAX_FRAME
+/* In bytes, per queue */
+#define MEM2MEM_VID_MEM_LIMIT (16 * 1024 * 1024)
+
+/* Default transaction time in msec */
+#define MEM2MEM_DEF_TRANSTIME 1000
+/* Default number of buffers per transaction */
+#define MEM2MEM_DEF_TRANSLEN 1
+#define MEM2MEM_COLOR_STEP (0xff >> 4)
+#define MEM2MEM_NUM_TILES 8
+
+#define dprintk(dev, fmt, arg...) \
+ v4l2_dbg(1, 1, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)
+
+
+void m2mtest_dev_release(struct device *dev)
+{}
+
+static struct platform_device m2mtest_pdev = {
+ .name = MEM2MEM_NAME,
+ .dev.release = m2mtest_dev_release,
+};
+
+struct m2mtest_fmt {
+ char *name;
+ u32 fourcc;
+ int depth;
+ /* Types the format can be used for */
+ u32 types;
+};
+
+static struct m2mtest_fmt formats[] = {
+ {
+ .name = "RGB565 (BE)",
+ .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
+ .depth = 16,
+ /* Both capture and output format */
+ .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
+ },
+ {
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ /* Output-only format */
+ .types = MEM2MEM_OUTPUT,
+ },
+};
+
+/* Per-queue, driver-specific private data */
+struct m2mtest_q_data {
+ unsigned int width;
+ unsigned int height;
+ unsigned int sizeimage;
+ struct m2mtest_fmt *fmt;
+};
+
+enum {
+ V4L2_M2M_SRC = 0,
+ V4L2_M2M_DST = 1,
+};
+
+/* Source and destination queue data */
+static struct m2mtest_q_data q_data[2];
+
+static struct m2mtest_q_data *get_q_data(enum v4l2_buf_type type)
+{
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ return &q_data[V4L2_M2M_SRC];
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return &q_data[V4L2_M2M_DST];
+ default:
+ BUG();
+ }
+ return NULL;
+}
+
+#define V4L2_CID_TRANS_TIME_MSEC V4L2_CID_PRIVATE_BASE
+#define V4L2_CID_TRANS_NUM_BUFS (V4L2_CID_PRIVATE_BASE + 1)
+
+static struct v4l2_queryctrl m2mtest_ctrls[] = {
+ {
+ .id = V4L2_CID_TRANS_TIME_MSEC,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Transaction time (msec)",
+ .minimum = 1,
+ .maximum = 10000,
+ .step = 100,
+ .default_value = 1000,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_TRANS_NUM_BUFS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Buffers per transaction",
+ .minimum = 1,
+ .maximum = MEM2MEM_DEF_NUM_BUFS,
+ .step = 1,
+ .default_value = 1,
+ .flags = 0,
+ },
+};
+
+#define NUM_FORMATS ARRAY_SIZE(formats)
+
+static struct m2mtest_fmt *find_format(struct v4l2_format *f)
+{
+ struct m2mtest_fmt *fmt;
+ unsigned int k;
+
+ for (k = 0; k < NUM_FORMATS; k++) {
+ fmt = &formats[k];
+ if (fmt->fourcc == f->fmt.pix.pixelformat)
+ break;
+ }
+
+ if (k == NUM_FORMATS)
+ return NULL;
+
+ return &formats[k];
+}
+
+struct m2mtest_dev {
+ struct v4l2_device v4l2_dev;
+ struct video_device *vfd;
+
+ atomic_t num_inst;
+ struct mutex dev_mutex;
+ spinlock_t irqlock;
+
+ struct timer_list timer;
+
+ struct v4l2_m2m_dev *m2m_dev;
+};
+
+struct m2mtest_ctx {
+ struct m2mtest_dev *dev;
+
+ /* Processed buffers in this transaction */
+ u8 num_processed;
+
+ /* Transaction length (i.e. how many buffers per transaction) */
+ u32 translen;
+ /* Transaction time (i.e. simulated processing time) in milliseconds */
+ u32 transtime;
+
+ /* Abort requested by m2m */
+ int aborting;
+
+ struct v4l2_m2m_ctx *m2m_ctx;
+};
+
+static struct v4l2_queryctrl *get_ctrl(int id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(m2mtest_ctrls); ++i) {
+ if (id == m2mtest_ctrls[i].id)
+ return &m2mtest_ctrls[i];
+ }
+
+ return NULL;
+}
+
+static int device_process(struct m2mtest_ctx *ctx,
+ struct vb2_buffer *in_vb,
+ struct vb2_buffer *out_vb)
+{
+ struct m2mtest_dev *dev = ctx->dev;
+ struct m2mtest_q_data *q_data;
+ u8 *p_in, *p_out;
+ int x, y, t, w;
+ int tile_w, bytes_left;
+ int width, height, bytesperline;
+
+ q_data = get_q_data(V4L2_BUF_TYPE_VIDEO_OUTPUT);
+
+ width = q_data->width;
+ height = q_data->height;
+ bytesperline = (q_data->width * q_data->fmt->depth) >> 3;
+
+ p_in = vb2_plane_vaddr(in_vb, 0);
+ p_out = vb2_plane_vaddr(out_vb, 0);
+ if (!p_in || !p_out) {
+ v4l2_err(&dev->v4l2_dev,
+ "Acquiring kernel pointers to buffers failed\n");
+ return -EFAULT;
+ }
+
+ if (vb2_plane_size(in_vb, 0) > vb2_plane_size(out_vb, 0)) {
+ v4l2_err(&dev->v4l2_dev, "Output buffer is too small\n");
+ return -EINVAL;
+ }
+
+ tile_w = (width * (q_data[V4L2_M2M_DST].fmt->depth >> 3))
+ / MEM2MEM_NUM_TILES;
+ bytes_left = bytesperline - tile_w * MEM2MEM_NUM_TILES;
+ w = 0;
+
+ for (y = 0; y < height; ++y) {
+ for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
+ if (w & 0x1) {
+ for (x = 0; x < tile_w; ++x)
+ *p_out++ = *p_in++ + MEM2MEM_COLOR_STEP;
+ } else {
+ for (x = 0; x < tile_w; ++x)
+ *p_out++ = *p_in++ - MEM2MEM_COLOR_STEP;
+ }
+ ++w;
+ }
+ p_in += bytes_left;
+ p_out += bytes_left;
+ }
+
+ return 0;
+}
+
+static void schedule_irq(struct m2mtest_dev *dev, int msec_timeout)
+{
+ dprintk(dev, "Scheduling a simulated irq\n");
+ mod_timer(&dev->timer, jiffies + msecs_to_jiffies(msec_timeout));
+}
+
+/*
+ * mem2mem callbacks
+ */
+
+/**
+ * job_ready() - check whether an instance is ready to be scheduled to run
+ */
+static int job_ready(void *priv)
+{
+ struct m2mtest_ctx *ctx = priv;
+
+ if (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) < ctx->translen
+ || v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) < ctx->translen) {
+ dprintk(ctx->dev, "Not enough buffers available\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static void job_abort(void *priv)
+{
+ struct m2mtest_ctx *ctx = priv;
+
+ /* Will cancel the transaction in the next interrupt handler */
+ ctx->aborting = 1;
+}
+
+static void m2mtest_lock(void *priv)
+{
+ struct m2mtest_ctx *ctx = priv;
+ struct m2mtest_dev *dev = ctx->dev;
+ mutex_lock(&dev->dev_mutex);
+}
+
+static void m2mtest_unlock(void *priv)
+{
+ struct m2mtest_ctx *ctx = priv;
+ struct m2mtest_dev *dev = ctx->dev;
+ mutex_unlock(&dev->dev_mutex);
+}
+
+
+/* device_run() - prepares and starts the device
+ *
+ * This simulates all the immediate preparations required before starting
+ * a device. This will be called by the framework when it decides to schedule
+ * a particular instance.
+ */
+static void device_run(void *priv)
+{
+ struct m2mtest_ctx *ctx = priv;
+ struct m2mtest_dev *dev = ctx->dev;
+ struct vb2_buffer *src_buf, *dst_buf;
+
+ src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+
+ device_process(ctx, src_buf, dst_buf);
+
+ /* Run a timer, which simulates a hardware irq */
+ schedule_irq(dev, ctx->transtime);
+}
+
+static void device_isr(unsigned long priv)
+{
+ struct m2mtest_dev *m2mtest_dev = (struct m2mtest_dev *)priv;
+ struct m2mtest_ctx *curr_ctx;
+ struct vb2_buffer *src_vb, *dst_vb;
+ unsigned long flags;
+
+ curr_ctx = v4l2_m2m_get_curr_priv(m2mtest_dev->m2m_dev);
+
+ if (NULL == curr_ctx) {
+ printk(KERN_ERR
+ "Instance released before the end of transaction\n");
+ return;
+ }
+
+ src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);
+ dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx);
+
+ curr_ctx->num_processed++;
+
+ spin_lock_irqsave(&m2mtest_dev->irqlock, flags);
+ v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);
+ v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE);
+ spin_unlock_irqrestore(&m2mtest_dev->irqlock, flags);
+
+ if (curr_ctx->num_processed == curr_ctx->translen
+ || curr_ctx->aborting) {
+ dprintk(curr_ctx->dev, "Finishing transaction\n");
+ curr_ctx->num_processed = 0;
+ v4l2_m2m_job_finish(m2mtest_dev->m2m_dev, curr_ctx->m2m_ctx);
+ } else {
+ device_run(curr_ctx);
+ }
+}
+
+/*
+ * video ioctls
+ */
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1);
+ strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1);
+ cap->bus_info[0] = 0;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT
+ | V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+static int enum_fmt(struct v4l2_fmtdesc *f, u32 type)
+{
+ int i, num;
+ struct m2mtest_fmt *fmt;
+
+ num = 0;
+
+ for (i = 0; i < NUM_FORMATS; ++i) {
+ if (formats[i].types & type) {
+ /* index-th format of type type found ? */
+ if (num == f->index)
+ break;
+ /* Correct type but haven't reached our index yet,
+ * just increment per-type index */
+ ++num;
+ }
+ }
+
+ if (i < NUM_FORMATS) {
+ /* Format found */
+ fmt = &formats[i];
+ strncpy(f->description, fmt->name, sizeof(f->description) - 1);
+ f->pixelformat = fmt->fourcc;
+ return 0;
+ }
+
+ /* Format not found */
+ return -EINVAL;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ return enum_fmt(f, MEM2MEM_CAPTURE);
+}
+
+static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ return enum_fmt(f, MEM2MEM_OUTPUT);
+}
+
+static int vidioc_g_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f)
+{
+ struct vb2_queue *vq;
+ struct m2mtest_q_data *q_data;
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ q_data = get_q_data(f->type);
+
+ f->fmt.pix.width = q_data->width;
+ f->fmt.pix.height = q_data->height;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.pixelformat = q_data->fmt->fourcc;
+ f->fmt.pix.bytesperline = (q_data->width * q_data->fmt->depth) >> 3;
+ f->fmt.pix.sizeimage = q_data->sizeimage;
+
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ return vidioc_g_fmt(priv, f);
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ return vidioc_g_fmt(priv, f);
+}
+
+static int vidioc_try_fmt(struct v4l2_format *f, struct m2mtest_fmt *fmt)
+{
+ enum v4l2_field field;
+
+ field = f->fmt.pix.field;
+
+ if (field == V4L2_FIELD_ANY)
+ field = V4L2_FIELD_NONE;
+ else if (V4L2_FIELD_NONE != field)
+ return -EINVAL;
+
+ /* V4L2 specification suggests the driver corrects the format struct
+ * if any of the dimensions is unsupported */
+ f->fmt.pix.field = field;
+
+ if (f->fmt.pix.height < MIN_H)
+ f->fmt.pix.height = MIN_H;
+ else if (f->fmt.pix.height > MAX_H)
+ f->fmt.pix.height = MAX_H;
+
+ if (f->fmt.pix.width < MIN_W)
+ f->fmt.pix.width = MIN_W;
+ else if (f->fmt.pix.width > MAX_W)
+ f->fmt.pix.width = MAX_W;
+
+ f->fmt.pix.width &= ~DIM_ALIGN_MASK;
+ f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct m2mtest_fmt *fmt;
+ struct m2mtest_ctx *ctx = priv;
+
+ fmt = find_format(f);
+ if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) {
+ v4l2_err(&ctx->dev->v4l2_dev,
+ "Fourcc format (0x%08x) invalid.\n",
+ f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ return vidioc_try_fmt(f, fmt);
+}
+
+static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct m2mtest_fmt *fmt;
+ struct m2mtest_ctx *ctx = priv;
+
+ fmt = find_format(f);
+ if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) {
+ v4l2_err(&ctx->dev->v4l2_dev,
+ "Fourcc format (0x%08x) invalid.\n",
+ f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ return vidioc_try_fmt(f, fmt);
+}
+
+static int vidioc_s_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f)
+{
+ struct m2mtest_q_data *q_data;
+ struct vb2_queue *vq;
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ q_data = get_q_data(f->type);
+ if (!q_data)
+ return -EINVAL;
+
+ if (vb2_is_busy(vq)) {
+ v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__);
+ return -EBUSY;
+ }
+
+ q_data->fmt = find_format(f);
+ q_data->width = f->fmt.pix.width;
+ q_data->height = f->fmt.pix.height;
+ q_data->sizeimage = q_data->width * q_data->height
+ * q_data->fmt->depth >> 3;
+
+ dprintk(ctx->dev,
+ "Setting format for type %d, wxh: %dx%d, fmt: %d\n",
+ f->type, q_data->width, q_data->height, q_data->fmt->fourcc);
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ int ret;
+
+ ret = vidioc_try_fmt_vid_cap(file, priv, f);
+ if (ret)
+ return ret;
+
+ return vidioc_s_fmt(priv, f);
+}
+
+static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ int ret;
+
+ ret = vidioc_try_fmt_vid_out(file, priv, f);
+ if (ret)
+ return ret;
+
+ return vidioc_s_fmt(priv, f);
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct m2mtest_ctx *ctx = priv;
+
+ return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct m2mtest_ctx *ctx = priv;
+
+ return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct m2mtest_ctx *ctx = priv;
+
+ return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct m2mtest_ctx *ctx = priv;
+
+ return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct m2mtest_ctx *ctx = priv;
+
+ return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
+}
+
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct m2mtest_ctx *ctx = priv;
+
+ return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ struct v4l2_queryctrl *c;
+
+ c = get_ctrl(qc->id);
+ if (!c)
+ return -EINVAL;
+
+ *qc = *c;
+ return 0;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct m2mtest_ctx *ctx = priv;
+
+ switch (ctrl->id) {
+ case V4L2_CID_TRANS_TIME_MSEC:
+ ctrl->value = ctx->transtime;
+ break;
+
+ case V4L2_CID_TRANS_NUM_BUFS:
+ ctrl->value = ctx->translen;
+ break;
+
+ default:
+ v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int check_ctrl_val(struct m2mtest_ctx *ctx, struct v4l2_control *ctrl)
+{
+ struct v4l2_queryctrl *c;
+
+ c = get_ctrl(ctrl->id);
+ if (!c)
+ return -EINVAL;
+
+ if (ctrl->value < c->minimum || ctrl->value > c->maximum) {
+ v4l2_err(&ctx->dev->v4l2_dev, "Value out of range\n");
+ return -ERANGE;
+ }
+
+ return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct m2mtest_ctx *ctx = priv;
+ int ret = 0;
+
+ ret = check_ctrl_val(ctx, ctrl);
+ if (ret != 0)
+ return ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_TRANS_TIME_MSEC:
+ ctx->transtime = ctrl->value;
+ break;
+
+ case V4L2_CID_TRANS_NUM_BUFS:
+ ctx->translen = ctrl->value;
+ break;
+
+ default:
+ v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+static const struct v4l2_ioctl_ops m2mtest_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+
+ .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
+ .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out,
+ .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out,
+ .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out,
+
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+};
+
+
+/*
+ * Queue operations
+ */
+
+static int m2mtest_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct m2mtest_ctx *ctx = vb2_get_drv_priv(vq);
+ struct m2mtest_q_data *q_data;
+ unsigned int size, count = *nbuffers;
+
+ q_data = get_q_data(vq->type);
+
+ size = q_data->width * q_data->height * q_data->fmt->depth >> 3;
+
+ while (size * count > MEM2MEM_VID_MEM_LIMIT)
+ (count)--;
+
+ *nplanes = 1;
+ *nbuffers = count;
+ sizes[0] = size;
+
+ /*
+ * videobuf2-vmalloc allocator is context-less so no need to set
+ * alloc_ctxs array.
+ */
+
+ dprintk(ctx->dev, "get %d buffer(s) of size %d each.\n", count, size);
+
+ return 0;
+}
+
+static int m2mtest_buf_prepare(struct vb2_buffer *vb)
+{
+ struct m2mtest_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct m2mtest_q_data *q_data;
+
+ dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type);
+
+ q_data = get_q_data(vb->vb2_queue->type);
+
+ if (vb2_plane_size(vb, 0) < q_data->sizeimage) {
+ dprintk(ctx->dev, "%s data will not fit into plane (%lu < %lu)\n",
+ __func__, vb2_plane_size(vb, 0), (long)q_data->sizeimage);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, 0, q_data->sizeimage);
+
+ return 0;
+}
+
+static void m2mtest_buf_queue(struct vb2_buffer *vb)
+{
+ struct m2mtest_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+}
+
+static void m2mtest_wait_prepare(struct vb2_queue *q)
+{
+ struct m2mtest_ctx *ctx = vb2_get_drv_priv(q);
+ m2mtest_unlock(ctx);
+}
+
+static void m2mtest_wait_finish(struct vb2_queue *q)
+{
+ struct m2mtest_ctx *ctx = vb2_get_drv_priv(q);
+ m2mtest_lock(ctx);
+}
+
+static struct vb2_ops m2mtest_qops = {
+ .queue_setup = m2mtest_queue_setup,
+ .buf_prepare = m2mtest_buf_prepare,
+ .buf_queue = m2mtest_buf_queue,
+ .wait_prepare = m2mtest_wait_prepare,
+ .wait_finish = m2mtest_wait_finish,
+};
+
+static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
+{
+ struct m2mtest_ctx *ctx = priv;
+ int ret;
+
+ memset(src_vq, 0, sizeof(*src_vq));
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ src_vq->io_modes = VB2_MMAP;
+ src_vq->drv_priv = ctx;
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ src_vq->ops = &m2mtest_qops;
+ src_vq->mem_ops = &vb2_vmalloc_memops;
+
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ memset(dst_vq, 0, sizeof(*dst_vq));
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ dst_vq->io_modes = VB2_MMAP;
+ dst_vq->drv_priv = ctx;
+ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ dst_vq->ops = &m2mtest_qops;
+ dst_vq->mem_ops = &vb2_vmalloc_memops;
+
+ return vb2_queue_init(dst_vq);
+}
+
+/*
+ * File operations
+ */
+static int m2mtest_open(struct file *file)
+{
+ struct m2mtest_dev *dev = video_drvdata(file);
+ struct m2mtest_ctx *ctx = NULL;
+
+ ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ file->private_data = ctx;
+ ctx->dev = dev;
+ ctx->translen = MEM2MEM_DEF_TRANSLEN;
+ ctx->transtime = MEM2MEM_DEF_TRANSTIME;
+ ctx->num_processed = 0;
+
+ ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
+
+ if (IS_ERR(ctx->m2m_ctx)) {
+ int ret = PTR_ERR(ctx->m2m_ctx);
+
+ kfree(ctx);
+ return ret;
+ }
+
+ atomic_inc(&dev->num_inst);
+
+ dprintk(dev, "Created instance %p, m2m_ctx: %p\n", ctx, ctx->m2m_ctx);
+
+ return 0;
+}
+
+static int m2mtest_release(struct file *file)
+{
+ struct m2mtest_dev *dev = video_drvdata(file);
+ struct m2mtest_ctx *ctx = file->private_data;
+
+ dprintk(dev, "Releasing instance %p\n", ctx);
+
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
+ kfree(ctx);
+
+ atomic_dec(&dev->num_inst);
+
+ return 0;
+}
+
+static unsigned int m2mtest_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct m2mtest_ctx *ctx = file->private_data;
+
+ return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+}
+
+static int m2mtest_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct m2mtest_ctx *ctx = file->private_data;
+
+ return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
+}
+
+static const struct v4l2_file_operations m2mtest_fops = {
+ .owner = THIS_MODULE,
+ .open = m2mtest_open,
+ .release = m2mtest_release,
+ .poll = m2mtest_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = m2mtest_mmap,
+};
+
+static struct video_device m2mtest_videodev = {
+ .name = MEM2MEM_NAME,
+ .fops = &m2mtest_fops,
+ .ioctl_ops = &m2mtest_ioctl_ops,
+ .minor = -1,
+ .release = video_device_release,
+};
+
+static struct v4l2_m2m_ops m2m_ops = {
+ .device_run = device_run,
+ .job_ready = job_ready,
+ .job_abort = job_abort,
+ .lock = m2mtest_lock,
+ .unlock = m2mtest_unlock,
+};
+
+static int m2mtest_probe(struct platform_device *pdev)
+{
+ struct m2mtest_dev *dev;
+ struct video_device *vfd;
+ int ret;
+
+ dev = kzalloc(sizeof *dev, GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ spin_lock_init(&dev->irqlock);
+
+ ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+ if (ret)
+ goto free_dev;
+
+ atomic_set(&dev->num_inst, 0);
+ mutex_init(&dev->dev_mutex);
+
+ vfd = video_device_alloc();
+ if (!vfd) {
+ v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
+ ret = -ENOMEM;
+ goto unreg_dev;
+ }
+
+ *vfd = m2mtest_videodev;
+ vfd->lock = &dev->dev_mutex;
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+ goto rel_vdev;
+ }
+
+ video_set_drvdata(vfd, dev);
+ snprintf(vfd->name, sizeof(vfd->name), "%s", m2mtest_videodev.name);
+ dev->vfd = vfd;
+ v4l2_info(&dev->v4l2_dev, MEM2MEM_TEST_MODULE_NAME
+ "Device registered as /dev/video%d\n", vfd->num);
+
+ setup_timer(&dev->timer, device_isr, (long)dev);
+ platform_set_drvdata(pdev, dev);
+
+ dev->m2m_dev = v4l2_m2m_init(&m2m_ops);
+ if (IS_ERR(dev->m2m_dev)) {
+ v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
+ ret = PTR_ERR(dev->m2m_dev);
+ goto err_m2m;
+ }
+
+ q_data[V4L2_M2M_SRC].fmt = &formats[0];
+ q_data[V4L2_M2M_DST].fmt = &formats[0];
+
+ return 0;
+
+ v4l2_m2m_release(dev->m2m_dev);
+err_m2m:
+ video_unregister_device(dev->vfd);
+rel_vdev:
+ video_device_release(vfd);
+unreg_dev:
+ v4l2_device_unregister(&dev->v4l2_dev);
+free_dev:
+ kfree(dev);
+
+ return ret;
+}
+
+static int m2mtest_remove(struct platform_device *pdev)
+{
+ struct m2mtest_dev *dev =
+ (struct m2mtest_dev *)platform_get_drvdata(pdev);
+
+ v4l2_info(&dev->v4l2_dev, "Removing " MEM2MEM_TEST_MODULE_NAME);
+ v4l2_m2m_release(dev->m2m_dev);
+ del_timer_sync(&dev->timer);
+ video_unregister_device(dev->vfd);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ kfree(dev);
+
+ return 0;
+}
+
+static struct platform_driver m2mtest_pdrv = {
+ .probe = m2mtest_probe,
+ .remove = m2mtest_remove,
+ .driver = {
+ .name = MEM2MEM_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static void __exit m2mtest_exit(void)
+{
+ platform_driver_unregister(&m2mtest_pdrv);
+ platform_device_unregister(&m2mtest_pdev);
+}
+
+static int __init m2mtest_init(void)
+{
+ int ret;
+
+ ret = platform_device_register(&m2mtest_pdev);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&m2mtest_pdrv);
+ if (ret)
+ platform_device_unregister(&m2mtest_pdev);
+
+ return 0;
+}
+
+module_init(m2mtest_init);
+module_exit(m2mtest_exit);
+
diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c
new file mode 100644
index 00000000..b09a3c80
--- /dev/null
+++ b/drivers/media/video/meye.c
@@ -0,0 +1,1964 @@
+/*
+ * Motion Eye video4linux driver for Sony Vaio PictureBook
+ *
+ * Copyright (C) 2001-2004 Stelian Pop <stelian@popies.net>
+ *
+ * Copyright (C) 2001-2002 Alcôve <www.alcove.com>
+ *
+ * Copyright (C) 2000 Andrew Tridgell <tridge@valinux.com>
+ *
+ * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras.
+ *
+ * Some parts borrowed from various video4linux drivers, especially
+ * bttv-driver.c and zoran.c, see original files for credits.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/gfp.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/vmalloc.h>
+#include <linux/dma-mapping.h>
+
+#include "meye.h"
+#include <linux/meye.h>
+
+MODULE_AUTHOR("Stelian Pop <stelian@popies.net>");
+MODULE_DESCRIPTION("v4l2 driver for the MotionEye camera");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(MEYE_DRIVER_VERSION);
+
+/* number of grab buffers */
+static unsigned int gbuffers = 2;
+module_param(gbuffers, int, 0444);
+MODULE_PARM_DESC(gbuffers, "number of capture buffers, default is 2 (32 max)");
+
+/* size of a grab buffer */
+static unsigned int gbufsize = MEYE_MAX_BUFSIZE;
+module_param(gbufsize, int, 0444);
+MODULE_PARM_DESC(gbufsize, "size of the capture buffers, default is 614400"
+ " (will be rounded up to a page multiple)");
+
+/* /dev/videoX registration number */
+static int video_nr = -1;
+module_param(video_nr, int, 0444);
+MODULE_PARM_DESC(video_nr, "video device to register (0=/dev/video0, etc)");
+
+/* driver structure - only one possible */
+static struct meye meye;
+
+/****************************************************************************/
+/* Memory allocation routines (stolen from bttv-driver.c) */
+/****************************************************************************/
+static void *rvmalloc(unsigned long size)
+{
+ void *mem;
+ unsigned long adr;
+
+ size = PAGE_ALIGN(size);
+ mem = vmalloc_32(size);
+ if (mem) {
+ memset(mem, 0, size);
+ adr = (unsigned long) mem;
+ while (size > 0) {
+ SetPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ }
+ return mem;
+}
+
+static void rvfree(void * mem, unsigned long size)
+{
+ unsigned long adr;
+
+ if (mem) {
+ adr = (unsigned long) mem;
+ while ((long) size > 0) {
+ ClearPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ vfree(mem);
+ }
+}
+
+/*
+ * return a page table pointing to N pages of locked memory
+ *
+ * NOTE: The meye device expects DMA addresses on 32 bits, we build
+ * a table of 1024 entries = 4 bytes * 1024 = 4096 bytes.
+ */
+static int ptable_alloc(void)
+{
+ u32 *pt;
+ int i;
+
+ memset(meye.mchip_ptable, 0, sizeof(meye.mchip_ptable));
+
+ /* give only 32 bit DMA addresses */
+ if (dma_set_mask(&meye.mchip_dev->dev, DMA_BIT_MASK(32)))
+ return -1;
+
+ meye.mchip_ptable_toc = dma_alloc_coherent(&meye.mchip_dev->dev,
+ PAGE_SIZE,
+ &meye.mchip_dmahandle,
+ GFP_KERNEL);
+ if (!meye.mchip_ptable_toc) {
+ meye.mchip_dmahandle = 0;
+ return -1;
+ }
+
+ pt = meye.mchip_ptable_toc;
+ for (i = 0; i < MCHIP_NB_PAGES; i++) {
+ dma_addr_t dma;
+ meye.mchip_ptable[i] = dma_alloc_coherent(&meye.mchip_dev->dev,
+ PAGE_SIZE,
+ &dma,
+ GFP_KERNEL);
+ if (!meye.mchip_ptable[i]) {
+ int j;
+ pt = meye.mchip_ptable_toc;
+ for (j = 0; j < i; ++j) {
+ dma = (dma_addr_t) *pt;
+ dma_free_coherent(&meye.mchip_dev->dev,
+ PAGE_SIZE,
+ meye.mchip_ptable[j], dma);
+ pt++;
+ }
+ dma_free_coherent(&meye.mchip_dev->dev,
+ PAGE_SIZE,
+ meye.mchip_ptable_toc,
+ meye.mchip_dmahandle);
+ meye.mchip_ptable_toc = NULL;
+ meye.mchip_dmahandle = 0;
+ return -1;
+ }
+ *pt = (u32) dma;
+ pt++;
+ }
+ return 0;
+}
+
+static void ptable_free(void)
+{
+ u32 *pt;
+ int i;
+
+ pt = meye.mchip_ptable_toc;
+ for (i = 0; i < MCHIP_NB_PAGES; i++) {
+ dma_addr_t dma = (dma_addr_t) *pt;
+ if (meye.mchip_ptable[i])
+ dma_free_coherent(&meye.mchip_dev->dev,
+ PAGE_SIZE,
+ meye.mchip_ptable[i], dma);
+ pt++;
+ }
+
+ if (meye.mchip_ptable_toc)
+ dma_free_coherent(&meye.mchip_dev->dev,
+ PAGE_SIZE,
+ meye.mchip_ptable_toc,
+ meye.mchip_dmahandle);
+
+ memset(meye.mchip_ptable, 0, sizeof(meye.mchip_ptable));
+ meye.mchip_ptable_toc = NULL;
+ meye.mchip_dmahandle = 0;
+}
+
+/* copy data from ptable into buf */
+static void ptable_copy(u8 *buf, int start, int size, int pt_pages)
+{
+ int i;
+
+ for (i = 0; i < (size / PAGE_SIZE) * PAGE_SIZE; i += PAGE_SIZE) {
+ memcpy(buf + i, meye.mchip_ptable[start++], PAGE_SIZE);
+ if (start >= pt_pages)
+ start = 0;
+ }
+ memcpy(buf + i, meye.mchip_ptable[start], size % PAGE_SIZE);
+}
+
+/****************************************************************************/
+/* JPEG tables at different qualities to load into the VRJ chip */
+/****************************************************************************/
+
+/* return a set of quantisation tables based on a quality from 1 to 10 */
+static u16 *jpeg_quantisation_tables(int *length, int quality)
+{
+ static u16 jpeg_tables[][70] = { {
+ 0xdbff, 0x4300, 0xff00, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff,
+ 0xdbff, 0x4300, 0xff01, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff,
+ },
+ {
+ 0xdbff, 0x4300, 0x5000, 0x3c37, 0x3c46, 0x5032, 0x4146, 0x5a46,
+ 0x5055, 0x785f, 0x82c8, 0x6e78, 0x786e, 0xaff5, 0x91b9, 0xffc8,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff,
+ 0xdbff, 0x4300, 0x5501, 0x5a5a, 0x6978, 0xeb78, 0x8282, 0xffeb,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff,
+ },
+ {
+ 0xdbff, 0x4300, 0x2800, 0x1e1c, 0x1e23, 0x2819, 0x2123, 0x2d23,
+ 0x282b, 0x3c30, 0x4164, 0x373c, 0x3c37, 0x587b, 0x495d, 0x9164,
+ 0x9980, 0x8f96, 0x8c80, 0xa08a, 0xe6b4, 0xa0c3, 0xdaaa, 0x8aad,
+ 0xc88c, 0xcbff, 0xeeda, 0xfff5, 0xffff, 0xc19b, 0xffff, 0xfaff,
+ 0xe6ff, 0xfffd, 0xfff8,
+ 0xdbff, 0x4300, 0x2b01, 0x2d2d, 0x353c, 0x763c, 0x4141, 0xf876,
+ 0x8ca5, 0xf8a5, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8,
+ 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8,
+ 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8,
+ 0xf8f8, 0xf8f8, 0xfff8,
+ },
+ {
+ 0xdbff, 0x4300, 0x1b00, 0x1412, 0x1417, 0x1b11, 0x1617, 0x1e17,
+ 0x1b1c, 0x2820, 0x2b42, 0x2528, 0x2825, 0x3a51, 0x303d, 0x6042,
+ 0x6555, 0x5f64, 0x5d55, 0x6a5b, 0x9978, 0x6a81, 0x9071, 0x5b73,
+ 0x855d, 0x86b5, 0x9e90, 0xaba3, 0xabad, 0x8067, 0xc9bc, 0xa6ba,
+ 0x99c7, 0xaba8, 0xffa4,
+ 0xdbff, 0x4300, 0x1c01, 0x1e1e, 0x2328, 0x4e28, 0x2b2b, 0xa44e,
+ 0x5d6e, 0xa46e, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4,
+ 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4,
+ 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4,
+ 0xa4a4, 0xa4a4, 0xffa4,
+ },
+ {
+ 0xdbff, 0x4300, 0x1400, 0x0f0e, 0x0f12, 0x140d, 0x1012, 0x1712,
+ 0x1415, 0x1e18, 0x2132, 0x1c1e, 0x1e1c, 0x2c3d, 0x242e, 0x4932,
+ 0x4c40, 0x474b, 0x4640, 0x5045, 0x735a, 0x5062, 0x6d55, 0x4556,
+ 0x6446, 0x6588, 0x776d, 0x817b, 0x8182, 0x604e, 0x978d, 0x7d8c,
+ 0x7396, 0x817e, 0xff7c,
+ 0xdbff, 0x4300, 0x1501, 0x1717, 0x1a1e, 0x3b1e, 0x2121, 0x7c3b,
+ 0x4653, 0x7c53, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c,
+ 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c,
+ 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c,
+ 0x7c7c, 0x7c7c, 0xff7c,
+ },
+ {
+ 0xdbff, 0x4300, 0x1000, 0x0c0b, 0x0c0e, 0x100a, 0x0d0e, 0x120e,
+ 0x1011, 0x1813, 0x1a28, 0x1618, 0x1816, 0x2331, 0x1d25, 0x3a28,
+ 0x3d33, 0x393c, 0x3833, 0x4037, 0x5c48, 0x404e, 0x5744, 0x3745,
+ 0x5038, 0x516d, 0x5f57, 0x6762, 0x6768, 0x4d3e, 0x7971, 0x6470,
+ 0x5c78, 0x6765, 0xff63,
+ 0xdbff, 0x4300, 0x1101, 0x1212, 0x1518, 0x2f18, 0x1a1a, 0x632f,
+ 0x3842, 0x6342, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363,
+ 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363,
+ 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363,
+ 0x6363, 0x6363, 0xff63,
+ },
+ {
+ 0xdbff, 0x4300, 0x0d00, 0x0a09, 0x0a0b, 0x0d08, 0x0a0b, 0x0e0b,
+ 0x0d0e, 0x130f, 0x1520, 0x1213, 0x1312, 0x1c27, 0x171e, 0x2e20,
+ 0x3129, 0x2e30, 0x2d29, 0x332c, 0x4a3a, 0x333e, 0x4636, 0x2c37,
+ 0x402d, 0x4157, 0x4c46, 0x524e, 0x5253, 0x3e32, 0x615a, 0x505a,
+ 0x4a60, 0x5251, 0xff4f,
+ 0xdbff, 0x4300, 0x0e01, 0x0e0e, 0x1113, 0x2613, 0x1515, 0x4f26,
+ 0x2d35, 0x4f35, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f,
+ 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f,
+ 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f,
+ 0x4f4f, 0x4f4f, 0xff4f,
+ },
+ {
+ 0xdbff, 0x4300, 0x0a00, 0x0707, 0x0708, 0x0a06, 0x0808, 0x0b08,
+ 0x0a0a, 0x0e0b, 0x1018, 0x0d0e, 0x0e0d, 0x151d, 0x1116, 0x2318,
+ 0x251f, 0x2224, 0x221f, 0x2621, 0x372b, 0x262f, 0x3429, 0x2129,
+ 0x3022, 0x3141, 0x3934, 0x3e3b, 0x3e3e, 0x2e25, 0x4944, 0x3c43,
+ 0x3748, 0x3e3d, 0xff3b,
+ 0xdbff, 0x4300, 0x0a01, 0x0b0b, 0x0d0e, 0x1c0e, 0x1010, 0x3b1c,
+ 0x2228, 0x3b28, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b,
+ 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b,
+ 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b,
+ 0x3b3b, 0x3b3b, 0xff3b,
+ },
+ {
+ 0xdbff, 0x4300, 0x0600, 0x0504, 0x0506, 0x0604, 0x0506, 0x0706,
+ 0x0607, 0x0a08, 0x0a10, 0x090a, 0x0a09, 0x0e14, 0x0c0f, 0x1710,
+ 0x1814, 0x1718, 0x1614, 0x1a16, 0x251d, 0x1a1f, 0x231b, 0x161c,
+ 0x2016, 0x202c, 0x2623, 0x2927, 0x292a, 0x1f19, 0x302d, 0x282d,
+ 0x2530, 0x2928, 0xff28,
+ 0xdbff, 0x4300, 0x0701, 0x0707, 0x080a, 0x130a, 0x0a0a, 0x2813,
+ 0x161a, 0x281a, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828,
+ 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828,
+ 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828,
+ 0x2828, 0x2828, 0xff28,
+ },
+ {
+ 0xdbff, 0x4300, 0x0300, 0x0202, 0x0203, 0x0302, 0x0303, 0x0403,
+ 0x0303, 0x0504, 0x0508, 0x0405, 0x0504, 0x070a, 0x0607, 0x0c08,
+ 0x0c0a, 0x0b0c, 0x0b0a, 0x0d0b, 0x120e, 0x0d10, 0x110e, 0x0b0e,
+ 0x100b, 0x1016, 0x1311, 0x1514, 0x1515, 0x0f0c, 0x1817, 0x1416,
+ 0x1218, 0x1514, 0xff14,
+ 0xdbff, 0x4300, 0x0301, 0x0404, 0x0405, 0x0905, 0x0505, 0x1409,
+ 0x0b0d, 0x140d, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414,
+ 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414,
+ 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414,
+ 0x1414, 0x1414, 0xff14,
+ },
+ {
+ 0xdbff, 0x4300, 0x0100, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101,
+ 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101,
+ 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101,
+ 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101,
+ 0x0101, 0x0101, 0xff01,
+ 0xdbff, 0x4300, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101,
+ 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101,
+ 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101,
+ 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101,
+ 0x0101, 0x0101, 0xff01,
+ } };
+
+ if (quality < 0 || quality > 10) {
+ printk(KERN_WARNING
+ "meye: invalid quality level %d - using 8\n", quality);
+ quality = 8;
+ }
+
+ *length = ARRAY_SIZE(jpeg_tables[quality]);
+ return jpeg_tables[quality];
+}
+
+/* return a generic set of huffman tables */
+static u16 *jpeg_huffman_tables(int *length)
+{
+ static u16 tables[] = {
+ 0xC4FF, 0xB500, 0x0010, 0x0102, 0x0303, 0x0402, 0x0503, 0x0405,
+ 0x0004, 0x0100, 0x017D, 0x0302, 0x0400, 0x0511, 0x2112, 0x4131,
+ 0x1306, 0x6151, 0x2207, 0x1471, 0x8132, 0xA191, 0x2308, 0xB142,
+ 0x15C1, 0xD152, 0x24F0, 0x6233, 0x8272, 0x0A09, 0x1716, 0x1918,
+ 0x251A, 0x2726, 0x2928, 0x342A, 0x3635, 0x3837, 0x3A39, 0x4443,
+ 0x4645, 0x4847, 0x4A49, 0x5453, 0x5655, 0x5857, 0x5A59, 0x6463,
+ 0x6665, 0x6867, 0x6A69, 0x7473, 0x7675, 0x7877, 0x7A79, 0x8483,
+ 0x8685, 0x8887, 0x8A89, 0x9392, 0x9594, 0x9796, 0x9998, 0xA29A,
+ 0xA4A3, 0xA6A5, 0xA8A7, 0xAAA9, 0xB3B2, 0xB5B4, 0xB7B6, 0xB9B8,
+ 0xC2BA, 0xC4C3, 0xC6C5, 0xC8C7, 0xCAC9, 0xD3D2, 0xD5D4, 0xD7D6,
+ 0xD9D8, 0xE1DA, 0xE3E2, 0xE5E4, 0xE7E6, 0xE9E8, 0xF1EA, 0xF3F2,
+ 0xF5F4, 0xF7F6, 0xF9F8, 0xFFFA,
+ 0xC4FF, 0xB500, 0x0011, 0x0102, 0x0402, 0x0304, 0x0704, 0x0405,
+ 0x0004, 0x0201, 0x0077, 0x0201, 0x1103, 0x0504, 0x3121, 0x1206,
+ 0x5141, 0x6107, 0x1371, 0x3222, 0x0881, 0x4214, 0xA191, 0xC1B1,
+ 0x2309, 0x5233, 0x15F0, 0x7262, 0x0AD1, 0x2416, 0xE134, 0xF125,
+ 0x1817, 0x1A19, 0x2726, 0x2928, 0x352A, 0x3736, 0x3938, 0x433A,
+ 0x4544, 0x4746, 0x4948, 0x534A, 0x5554, 0x5756, 0x5958, 0x635A,
+ 0x6564, 0x6766, 0x6968, 0x736A, 0x7574, 0x7776, 0x7978, 0x827A,
+ 0x8483, 0x8685, 0x8887, 0x8A89, 0x9392, 0x9594, 0x9796, 0x9998,
+ 0xA29A, 0xA4A3, 0xA6A5, 0xA8A7, 0xAAA9, 0xB3B2, 0xB5B4, 0xB7B6,
+ 0xB9B8, 0xC2BA, 0xC4C3, 0xC6C5, 0xC8C7, 0xCAC9, 0xD3D2, 0xD5D4,
+ 0xD7D6, 0xD9D8, 0xE2DA, 0xE4E3, 0xE6E5, 0xE8E7, 0xEAE9, 0xF3F2,
+ 0xF5F4, 0xF7F6, 0xF9F8, 0xFFFA,
+ 0xC4FF, 0x1F00, 0x0000, 0x0501, 0x0101, 0x0101, 0x0101, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0201, 0x0403, 0x0605, 0x0807, 0x0A09,
+ 0xFF0B,
+ 0xC4FF, 0x1F00, 0x0001, 0x0103, 0x0101, 0x0101, 0x0101, 0x0101,
+ 0x0000, 0x0000, 0x0000, 0x0201, 0x0403, 0x0605, 0x0807, 0x0A09,
+ 0xFF0B
+ };
+
+ *length = ARRAY_SIZE(tables);
+ return tables;
+}
+
+/****************************************************************************/
+/* MCHIP low-level functions */
+/****************************************************************************/
+
+/* returns the horizontal capture size */
+static inline int mchip_hsize(void)
+{
+ return meye.params.subsample ? 320 : 640;
+}
+
+/* returns the vertical capture size */
+static inline int mchip_vsize(void)
+{
+ return meye.params.subsample ? 240 : 480;
+}
+
+/* waits for a register to be available */
+static void mchip_sync(int reg)
+{
+ u32 status;
+ int i;
+
+ if (reg == MCHIP_MM_FIFO_DATA) {
+ for (i = 0; i < MCHIP_REG_TIMEOUT; i++) {
+ status = readl(meye.mchip_mmregs +
+ MCHIP_MM_FIFO_STATUS);
+ if (!(status & MCHIP_MM_FIFO_WAIT)) {
+ printk(KERN_WARNING "meye: fifo not ready\n");
+ return;
+ }
+ if (status & MCHIP_MM_FIFO_READY)
+ return;
+ udelay(1);
+ }
+ } else if (reg > 0x80) {
+ u32 mask = (reg < 0x100) ? MCHIP_HIC_STATUS_MCC_RDY
+ : MCHIP_HIC_STATUS_VRJ_RDY;
+ for (i = 0; i < MCHIP_REG_TIMEOUT; i++) {
+ status = readl(meye.mchip_mmregs + MCHIP_HIC_STATUS);
+ if (status & mask)
+ return;
+ udelay(1);
+ }
+ } else
+ return;
+ printk(KERN_WARNING
+ "meye: mchip_sync() timeout on reg 0x%x status=0x%x\n",
+ reg, status);
+}
+
+/* sets a value into the register */
+static inline void mchip_set(int reg, u32 v)
+{
+ mchip_sync(reg);
+ writel(v, meye.mchip_mmregs + reg);
+}
+
+/* get the register value */
+static inline u32 mchip_read(int reg)
+{
+ mchip_sync(reg);
+ return readl(meye.mchip_mmregs + reg);
+}
+
+/* wait for a register to become a particular value */
+static inline int mchip_delay(u32 reg, u32 v)
+{
+ int n = 10;
+ while (--n && mchip_read(reg) != v)
+ udelay(1);
+ return n;
+}
+
+/* setup subsampling */
+static void mchip_subsample(void)
+{
+ mchip_set(MCHIP_MCC_R_SAMPLING, meye.params.subsample);
+ mchip_set(MCHIP_MCC_R_XRANGE, mchip_hsize());
+ mchip_set(MCHIP_MCC_R_YRANGE, mchip_vsize());
+ mchip_set(MCHIP_MCC_B_XRANGE, mchip_hsize());
+ mchip_set(MCHIP_MCC_B_YRANGE, mchip_vsize());
+ mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE);
+}
+
+/* set the framerate into the mchip */
+static void mchip_set_framerate(void)
+{
+ mchip_set(MCHIP_HIC_S_RATE, meye.params.framerate);
+}
+
+/* load some huffman and quantisation tables into the VRJ chip ready
+ for JPEG compression */
+static void mchip_load_tables(void)
+{
+ int i;
+ int length;
+ u16 *tables;
+
+ tables = jpeg_huffman_tables(&length);
+ for (i = 0; i < length; i++)
+ writel(tables[i], meye.mchip_mmregs + MCHIP_VRJ_TABLE_DATA);
+
+ tables = jpeg_quantisation_tables(&length, meye.params.quality);
+ for (i = 0; i < length; i++)
+ writel(tables[i], meye.mchip_mmregs + MCHIP_VRJ_TABLE_DATA);
+}
+
+/* setup the VRJ parameters in the chip */
+static void mchip_vrj_setup(u8 mode)
+{
+ mchip_set(MCHIP_VRJ_BUS_MODE, 5);
+ mchip_set(MCHIP_VRJ_SIGNAL_ACTIVE_LEVEL, 0x1f);
+ mchip_set(MCHIP_VRJ_PDAT_USE, 1);
+ mchip_set(MCHIP_VRJ_IRQ_FLAG, 0xa0);
+ mchip_set(MCHIP_VRJ_MODE_SPECIFY, mode);
+ mchip_set(MCHIP_VRJ_NUM_LINES, mchip_vsize());
+ mchip_set(MCHIP_VRJ_NUM_PIXELS, mchip_hsize());
+ mchip_set(MCHIP_VRJ_NUM_COMPONENTS, 0x1b);
+ mchip_set(MCHIP_VRJ_LIMIT_COMPRESSED_LO, 0xFFFF);
+ mchip_set(MCHIP_VRJ_LIMIT_COMPRESSED_HI, 0xFFFF);
+ mchip_set(MCHIP_VRJ_COMP_DATA_FORMAT, 0xC);
+ mchip_set(MCHIP_VRJ_RESTART_INTERVAL, 0);
+ mchip_set(MCHIP_VRJ_SOF1, 0x601);
+ mchip_set(MCHIP_VRJ_SOF2, 0x1502);
+ mchip_set(MCHIP_VRJ_SOF3, 0x1503);
+ mchip_set(MCHIP_VRJ_SOF4, 0x1596);
+ mchip_set(MCHIP_VRJ_SOS, 0x0ed0);
+
+ mchip_load_tables();
+}
+
+/* sets the DMA parameters into the chip */
+static void mchip_dma_setup(dma_addr_t dma_addr)
+{
+ int i;
+
+ mchip_set(MCHIP_MM_PT_ADDR, (u32)dma_addr);
+ for (i = 0; i < 4; i++)
+ mchip_set(MCHIP_MM_FIR(i), 0);
+ meye.mchip_fnum = 0;
+}
+
+/* setup for DMA transfers - also zeros the framebuffer */
+static int mchip_dma_alloc(void)
+{
+ if (!meye.mchip_dmahandle)
+ if (ptable_alloc())
+ return -1;
+ return 0;
+}
+
+/* frees the DMA buffer */
+static void mchip_dma_free(void)
+{
+ if (meye.mchip_dmahandle) {
+ mchip_dma_setup(0);
+ ptable_free();
+ }
+}
+
+/* stop any existing HIC action and wait for any dma to complete then
+ reset the dma engine */
+static void mchip_hic_stop(void)
+{
+ int i, j;
+
+ meye.mchip_mode = MCHIP_HIC_MODE_NOOP;
+ if (!(mchip_read(MCHIP_HIC_STATUS) & MCHIP_HIC_STATUS_BUSY))
+ return;
+ for (i = 0; i < 20; ++i) {
+ mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_STOP);
+ mchip_delay(MCHIP_HIC_CMD, 0);
+ for (j = 0; j < 100; ++j) {
+ if (mchip_delay(MCHIP_HIC_STATUS,
+ MCHIP_HIC_STATUS_IDLE))
+ return;
+ msleep(1);
+ }
+ printk(KERN_ERR "meye: need to reset HIC!\n");
+
+ mchip_set(MCHIP_HIC_CTL, MCHIP_HIC_CTL_SOFT_RESET);
+ msleep(250);
+ }
+ printk(KERN_ERR "meye: resetting HIC hanged!\n");
+}
+
+/****************************************************************************/
+/* MCHIP frame processing functions */
+/****************************************************************************/
+
+/* get the next ready frame from the dma engine */
+static u32 mchip_get_frame(void)
+{
+ u32 v;
+
+ v = mchip_read(MCHIP_MM_FIR(meye.mchip_fnum));
+ return v;
+}
+
+/* frees the current frame from the dma engine */
+static void mchip_free_frame(void)
+{
+ mchip_set(MCHIP_MM_FIR(meye.mchip_fnum), 0);
+ meye.mchip_fnum++;
+ meye.mchip_fnum %= 4;
+}
+
+/* read one frame from the framebuffer assuming it was captured using
+ a uncompressed transfer */
+static void mchip_cont_read_frame(u32 v, u8 *buf, int size)
+{
+ int pt_id;
+
+ pt_id = (v >> 17) & 0x3FF;
+
+ ptable_copy(buf, pt_id, size, MCHIP_NB_PAGES);
+}
+
+/* read a compressed frame from the framebuffer */
+static int mchip_comp_read_frame(u32 v, u8 *buf, int size)
+{
+ int pt_start, pt_end, trailer;
+ int fsize;
+ int i;
+
+ pt_start = (v >> 19) & 0xFF;
+ pt_end = (v >> 11) & 0xFF;
+ trailer = (v >> 1) & 0x3FF;
+
+ if (pt_end < pt_start)
+ fsize = (MCHIP_NB_PAGES_MJPEG - pt_start) * PAGE_SIZE +
+ pt_end * PAGE_SIZE + trailer * 4;
+ else
+ fsize = (pt_end - pt_start) * PAGE_SIZE + trailer * 4;
+
+ if (fsize > size) {
+ printk(KERN_WARNING "meye: oversized compressed frame %d\n",
+ fsize);
+ return -1;
+ }
+
+ ptable_copy(buf, pt_start, fsize, MCHIP_NB_PAGES_MJPEG);
+
+#ifdef MEYE_JPEG_CORRECTION
+
+ /* Some mchip generated jpeg frames are incorrect. In most
+ * (all ?) of those cases, the final EOI (0xff 0xd9) marker
+ * is not present at the end of the frame.
+ *
+ * Since adding the final marker is not enough to restore
+ * the jpeg integrity, we drop the frame.
+ */
+
+ for (i = fsize - 1; i > 0 && buf[i] == 0xff; i--) ;
+
+ if (i < 2 || buf[i - 1] != 0xff || buf[i] != 0xd9)
+ return -1;
+
+#endif
+
+ return fsize;
+}
+
+/* take a picture into SDRAM */
+static void mchip_take_picture(void)
+{
+ int i;
+
+ mchip_hic_stop();
+ mchip_subsample();
+ mchip_dma_setup(meye.mchip_dmahandle);
+
+ mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_CAP);
+ mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START);
+
+ mchip_delay(MCHIP_HIC_CMD, 0);
+
+ for (i = 0; i < 100; ++i) {
+ if (mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE))
+ break;
+ msleep(1);
+ }
+}
+
+/* dma a previously taken picture into a buffer */
+static void mchip_get_picture(u8 *buf, int bufsize)
+{
+ u32 v;
+ int i;
+
+ mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_OUT);
+ mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START);
+
+ mchip_delay(MCHIP_HIC_CMD, 0);
+ for (i = 0; i < 100; ++i) {
+ if (mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE))
+ break;
+ msleep(1);
+ }
+ for (i = 0; i < 4; ++i) {
+ v = mchip_get_frame();
+ if (v & MCHIP_MM_FIR_RDY) {
+ mchip_cont_read_frame(v, buf, bufsize);
+ break;
+ }
+ mchip_free_frame();
+ }
+}
+
+/* start continuous dma capture */
+static void mchip_continuous_start(void)
+{
+ mchip_hic_stop();
+ mchip_subsample();
+ mchip_set_framerate();
+ mchip_dma_setup(meye.mchip_dmahandle);
+
+ meye.mchip_mode = MCHIP_HIC_MODE_CONT_OUT;
+
+ mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_CONT_OUT);
+ mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START);
+
+ mchip_delay(MCHIP_HIC_CMD, 0);
+}
+
+/* compress one frame into a buffer */
+static int mchip_compress_frame(u8 *buf, int bufsize)
+{
+ u32 v;
+ int len = -1, i;
+
+ mchip_vrj_setup(0x3f);
+ udelay(50);
+
+ mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_COMP);
+ mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START);
+
+ mchip_delay(MCHIP_HIC_CMD, 0);
+ for (i = 0; i < 100; ++i) {
+ if (mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE))
+ break;
+ msleep(1);
+ }
+
+ for (i = 0; i < 4; ++i) {
+ v = mchip_get_frame();
+ if (v & MCHIP_MM_FIR_RDY) {
+ len = mchip_comp_read_frame(v, buf, bufsize);
+ break;
+ }
+ mchip_free_frame();
+ }
+ return len;
+}
+
+#if 0
+/* uncompress one image into a buffer */
+static int mchip_uncompress_frame(u8 *img, int imgsize, u8 *buf, int bufsize)
+{
+ mchip_vrj_setup(0x3f);
+ udelay(50);
+
+ mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_DECOMP);
+ mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START);
+
+ mchip_delay(MCHIP_HIC_CMD, 0);
+
+ return mchip_comp_read_frame(buf, bufsize);
+}
+#endif
+
+/* start continuous compressed capture */
+static void mchip_cont_compression_start(void)
+{
+ mchip_hic_stop();
+ mchip_vrj_setup(0x3f);
+ mchip_subsample();
+ mchip_set_framerate();
+ mchip_dma_setup(meye.mchip_dmahandle);
+
+ meye.mchip_mode = MCHIP_HIC_MODE_CONT_COMP;
+
+ mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_CONT_COMP);
+ mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START);
+
+ mchip_delay(MCHIP_HIC_CMD, 0);
+}
+
+/****************************************************************************/
+/* Interrupt handling */
+/****************************************************************************/
+
+static irqreturn_t meye_irq(int irq, void *dev_id)
+{
+ u32 v;
+ int reqnr;
+ static int sequence;
+
+ v = mchip_read(MCHIP_MM_INTA);
+
+ if (meye.mchip_mode != MCHIP_HIC_MODE_CONT_OUT &&
+ meye.mchip_mode != MCHIP_HIC_MODE_CONT_COMP)
+ return IRQ_NONE;
+
+again:
+ v = mchip_get_frame();
+ if (!(v & MCHIP_MM_FIR_RDY))
+ return IRQ_HANDLED;
+
+ if (meye.mchip_mode == MCHIP_HIC_MODE_CONT_OUT) {
+ if (kfifo_out_locked(&meye.grabq, (unsigned char *)&reqnr,
+ sizeof(int), &meye.grabq_lock) != sizeof(int)) {
+ mchip_free_frame();
+ return IRQ_HANDLED;
+ }
+ mchip_cont_read_frame(v, meye.grab_fbuffer + gbufsize * reqnr,
+ mchip_hsize() * mchip_vsize() * 2);
+ meye.grab_buffer[reqnr].size = mchip_hsize() * mchip_vsize() * 2;
+ meye.grab_buffer[reqnr].state = MEYE_BUF_DONE;
+ do_gettimeofday(&meye.grab_buffer[reqnr].timestamp);
+ meye.grab_buffer[reqnr].sequence = sequence++;
+ kfifo_in_locked(&meye.doneq, (unsigned char *)&reqnr,
+ sizeof(int), &meye.doneq_lock);
+ wake_up_interruptible(&meye.proc_list);
+ } else {
+ int size;
+ size = mchip_comp_read_frame(v, meye.grab_temp, gbufsize);
+ if (size == -1) {
+ mchip_free_frame();
+ goto again;
+ }
+ if (kfifo_out_locked(&meye.grabq, (unsigned char *)&reqnr,
+ sizeof(int), &meye.grabq_lock) != sizeof(int)) {
+ mchip_free_frame();
+ goto again;
+ }
+ memcpy(meye.grab_fbuffer + gbufsize * reqnr, meye.grab_temp,
+ size);
+ meye.grab_buffer[reqnr].size = size;
+ meye.grab_buffer[reqnr].state = MEYE_BUF_DONE;
+ do_gettimeofday(&meye.grab_buffer[reqnr].timestamp);
+ meye.grab_buffer[reqnr].sequence = sequence++;
+ kfifo_in_locked(&meye.doneq, (unsigned char *)&reqnr,
+ sizeof(int), &meye.doneq_lock);
+ wake_up_interruptible(&meye.proc_list);
+ }
+ mchip_free_frame();
+ goto again;
+}
+
+/****************************************************************************/
+/* video4linux integration */
+/****************************************************************************/
+
+static int meye_open(struct file *file)
+{
+ int i;
+
+ if (test_and_set_bit(0, &meye.in_use))
+ return -EBUSY;
+
+ mchip_hic_stop();
+
+ if (mchip_dma_alloc()) {
+ printk(KERN_ERR "meye: mchip framebuffer allocation failed\n");
+ clear_bit(0, &meye.in_use);
+ return -ENOBUFS;
+ }
+
+ for (i = 0; i < MEYE_MAX_BUFNBRS; i++)
+ meye.grab_buffer[i].state = MEYE_BUF_UNUSED;
+ kfifo_reset(&meye.grabq);
+ kfifo_reset(&meye.doneq);
+ return 0;
+}
+
+static int meye_release(struct file *file)
+{
+ mchip_hic_stop();
+ mchip_dma_free();
+ clear_bit(0, &meye.in_use);
+ return 0;
+}
+
+static int meyeioc_g_params(struct meye_params *p)
+{
+ *p = meye.params;
+ return 0;
+}
+
+static int meyeioc_s_params(struct meye_params *jp)
+{
+ if (jp->subsample > 1)
+ return -EINVAL;
+
+ if (jp->quality > 10)
+ return -EINVAL;
+
+ if (jp->sharpness > 63 || jp->agc > 63 || jp->picture > 63)
+ return -EINVAL;
+
+ if (jp->framerate > 31)
+ return -EINVAL;
+
+ mutex_lock(&meye.lock);
+
+ if (meye.params.subsample != jp->subsample ||
+ meye.params.quality != jp->quality)
+ mchip_hic_stop(); /* need restart */
+
+ meye.params = *jp;
+ sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERASHARPNESS,
+ meye.params.sharpness);
+ sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC,
+ meye.params.agc);
+ sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE,
+ meye.params.picture);
+ mutex_unlock(&meye.lock);
+
+ return 0;
+}
+
+static int meyeioc_qbuf_capt(int *nb)
+{
+ if (!meye.grab_fbuffer)
+ return -EINVAL;
+
+ if (*nb >= gbuffers)
+ return -EINVAL;
+
+ if (*nb < 0) {
+ /* stop capture */
+ mchip_hic_stop();
+ return 0;
+ }
+
+ if (meye.grab_buffer[*nb].state != MEYE_BUF_UNUSED)
+ return -EBUSY;
+
+ mutex_lock(&meye.lock);
+
+ if (meye.mchip_mode != MCHIP_HIC_MODE_CONT_COMP)
+ mchip_cont_compression_start();
+
+ meye.grab_buffer[*nb].state = MEYE_BUF_USING;
+ kfifo_in_locked(&meye.grabq, (unsigned char *)nb, sizeof(int),
+ &meye.grabq_lock);
+ mutex_unlock(&meye.lock);
+
+ return 0;
+}
+
+static int meyeioc_sync(struct file *file, void *fh, int *i)
+{
+ int unused;
+
+ if (*i < 0 || *i >= gbuffers)
+ return -EINVAL;
+
+ mutex_lock(&meye.lock);
+ switch (meye.grab_buffer[*i].state) {
+
+ case MEYE_BUF_UNUSED:
+ mutex_unlock(&meye.lock);
+ return -EINVAL;
+ case MEYE_BUF_USING:
+ if (file->f_flags & O_NONBLOCK) {
+ mutex_unlock(&meye.lock);
+ return -EAGAIN;
+ }
+ if (wait_event_interruptible(meye.proc_list,
+ (meye.grab_buffer[*i].state != MEYE_BUF_USING))) {
+ mutex_unlock(&meye.lock);
+ return -EINTR;
+ }
+ /* fall through */
+ case MEYE_BUF_DONE:
+ meye.grab_buffer[*i].state = MEYE_BUF_UNUSED;
+ if (kfifo_out_locked(&meye.doneq, (unsigned char *)&unused,
+ sizeof(int), &meye.doneq_lock) != sizeof(int))
+ break;
+ }
+ *i = meye.grab_buffer[*i].size;
+ mutex_unlock(&meye.lock);
+ return 0;
+}
+
+static int meyeioc_stillcapt(void)
+{
+ if (!meye.grab_fbuffer)
+ return -EINVAL;
+
+ if (meye.grab_buffer[0].state != MEYE_BUF_UNUSED)
+ return -EBUSY;
+
+ mutex_lock(&meye.lock);
+ meye.grab_buffer[0].state = MEYE_BUF_USING;
+ mchip_take_picture();
+
+ mchip_get_picture(meye.grab_fbuffer,
+ mchip_hsize() * mchip_vsize() * 2);
+
+ meye.grab_buffer[0].state = MEYE_BUF_DONE;
+ mutex_unlock(&meye.lock);
+
+ return 0;
+}
+
+static int meyeioc_stilljcapt(int *len)
+{
+ if (!meye.grab_fbuffer)
+ return -EINVAL;
+
+ if (meye.grab_buffer[0].state != MEYE_BUF_UNUSED)
+ return -EBUSY;
+
+ mutex_lock(&meye.lock);
+ meye.grab_buffer[0].state = MEYE_BUF_USING;
+ *len = -1;
+
+ while (*len == -1) {
+ mchip_take_picture();
+ *len = mchip_compress_frame(meye.grab_fbuffer, gbufsize);
+ }
+
+ meye.grab_buffer[0].state = MEYE_BUF_DONE;
+ mutex_unlock(&meye.lock);
+ return 0;
+}
+
+static int vidioc_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ strcpy(cap->driver, "meye");
+ strcpy(cap->card, "meye");
+ sprintf(cap->bus_info, "PCI:%s", pci_name(meye.mchip_dev));
+
+ cap->version = (MEYE_DRIVER_MAJORVERSION << 8) +
+ MEYE_DRIVER_MINORVERSION;
+
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
+
+ strcpy(i->name, "Camera");
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int i)
+{
+ if (i != 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *fh,
+ struct v4l2_queryctrl *c)
+{
+ switch (c->id) {
+
+ case V4L2_CID_BRIGHTNESS:
+ c->type = V4L2_CTRL_TYPE_INTEGER;
+ strcpy(c->name, "Brightness");
+ c->minimum = 0;
+ c->maximum = 63;
+ c->step = 1;
+ c->default_value = 32;
+ c->flags = 0;
+ break;
+ case V4L2_CID_HUE:
+ c->type = V4L2_CTRL_TYPE_INTEGER;
+ strcpy(c->name, "Hue");
+ c->minimum = 0;
+ c->maximum = 63;
+ c->step = 1;
+ c->default_value = 32;
+ c->flags = 0;
+ break;
+ case V4L2_CID_CONTRAST:
+ c->type = V4L2_CTRL_TYPE_INTEGER;
+ strcpy(c->name, "Contrast");
+ c->minimum = 0;
+ c->maximum = 63;
+ c->step = 1;
+ c->default_value = 32;
+ c->flags = 0;
+ break;
+ case V4L2_CID_SATURATION:
+ c->type = V4L2_CTRL_TYPE_INTEGER;
+ strcpy(c->name, "Saturation");
+ c->minimum = 0;
+ c->maximum = 63;
+ c->step = 1;
+ c->default_value = 32;
+ c->flags = 0;
+ break;
+ case V4L2_CID_AGC:
+ c->type = V4L2_CTRL_TYPE_INTEGER;
+ strcpy(c->name, "Agc");
+ c->minimum = 0;
+ c->maximum = 63;
+ c->step = 1;
+ c->default_value = 48;
+ c->flags = 0;
+ break;
+ case V4L2_CID_MEYE_SHARPNESS:
+ case V4L2_CID_SHARPNESS:
+ c->type = V4L2_CTRL_TYPE_INTEGER;
+ strcpy(c->name, "Sharpness");
+ c->minimum = 0;
+ c->maximum = 63;
+ c->step = 1;
+ c->default_value = 32;
+
+ /* Continue to report legacy private SHARPNESS ctrl but
+ * say it is disabled in preference to ctrl in the spec
+ */
+ c->flags = (c->id == V4L2_CID_SHARPNESS) ? 0 :
+ V4L2_CTRL_FLAG_DISABLED;
+ break;
+ case V4L2_CID_PICTURE:
+ c->type = V4L2_CTRL_TYPE_INTEGER;
+ strcpy(c->name, "Picture");
+ c->minimum = 0;
+ c->maximum = 63;
+ c->step = 1;
+ c->default_value = 0;
+ c->flags = 0;
+ break;
+ case V4L2_CID_JPEGQUAL:
+ c->type = V4L2_CTRL_TYPE_INTEGER;
+ strcpy(c->name, "JPEG quality");
+ c->minimum = 0;
+ c->maximum = 10;
+ c->step = 1;
+ c->default_value = 8;
+ c->flags = 0;
+ break;
+ case V4L2_CID_FRAMERATE:
+ c->type = V4L2_CTRL_TYPE_INTEGER;
+ strcpy(c->name, "Framerate");
+ c->minimum = 0;
+ c->maximum = 31;
+ c->step = 1;
+ c->default_value = 0;
+ c->flags = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c)
+{
+ mutex_lock(&meye.lock);
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ sony_pic_camera_command(
+ SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, c->value);
+ meye.brightness = c->value << 10;
+ break;
+ case V4L2_CID_HUE:
+ sony_pic_camera_command(
+ SONY_PIC_COMMAND_SETCAMERAHUE, c->value);
+ meye.hue = c->value << 10;
+ break;
+ case V4L2_CID_CONTRAST:
+ sony_pic_camera_command(
+ SONY_PIC_COMMAND_SETCAMERACONTRAST, c->value);
+ meye.contrast = c->value << 10;
+ break;
+ case V4L2_CID_SATURATION:
+ sony_pic_camera_command(
+ SONY_PIC_COMMAND_SETCAMERACOLOR, c->value);
+ meye.colour = c->value << 10;
+ break;
+ case V4L2_CID_AGC:
+ sony_pic_camera_command(
+ SONY_PIC_COMMAND_SETCAMERAAGC, c->value);
+ meye.params.agc = c->value;
+ break;
+ case V4L2_CID_SHARPNESS:
+ case V4L2_CID_MEYE_SHARPNESS:
+ sony_pic_camera_command(
+ SONY_PIC_COMMAND_SETCAMERASHARPNESS, c->value);
+ meye.params.sharpness = c->value;
+ break;
+ case V4L2_CID_PICTURE:
+ sony_pic_camera_command(
+ SONY_PIC_COMMAND_SETCAMERAPICTURE, c->value);
+ meye.params.picture = c->value;
+ break;
+ case V4L2_CID_JPEGQUAL:
+ meye.params.quality = c->value;
+ break;
+ case V4L2_CID_FRAMERATE:
+ meye.params.framerate = c->value;
+ break;
+ default:
+ mutex_unlock(&meye.lock);
+ return -EINVAL;
+ }
+ mutex_unlock(&meye.lock);
+
+ return 0;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c)
+{
+ mutex_lock(&meye.lock);
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ c->value = meye.brightness >> 10;
+ break;
+ case V4L2_CID_HUE:
+ c->value = meye.hue >> 10;
+ break;
+ case V4L2_CID_CONTRAST:
+ c->value = meye.contrast >> 10;
+ break;
+ case V4L2_CID_SATURATION:
+ c->value = meye.colour >> 10;
+ break;
+ case V4L2_CID_AGC:
+ c->value = meye.params.agc;
+ break;
+ case V4L2_CID_SHARPNESS:
+ case V4L2_CID_MEYE_SHARPNESS:
+ c->value = meye.params.sharpness;
+ break;
+ case V4L2_CID_PICTURE:
+ c->value = meye.params.picture;
+ break;
+ case V4L2_CID_JPEGQUAL:
+ c->value = meye.params.quality;
+ break;
+ case V4L2_CID_FRAMERATE:
+ c->value = meye.params.framerate;
+ break;
+ default:
+ mutex_unlock(&meye.lock);
+ return -EINVAL;
+ }
+ mutex_unlock(&meye.lock);
+
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index > 1)
+ return -EINVAL;
+
+ if (f->index == 0) {
+ /* standard YUV 422 capture */
+ f->flags = 0;
+ strcpy(f->description, "YUV422");
+ f->pixelformat = V4L2_PIX_FMT_YUYV;
+ } else {
+ /* compressed MJPEG capture */
+ f->flags = V4L2_FMT_FLAG_COMPRESSED;
+ strcpy(f->description, "MJPEG");
+ f->pixelformat = V4L2_PIX_FMT_MJPEG;
+ }
+
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV &&
+ f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG)
+ return -EINVAL;
+
+ if (f->fmt.pix.field != V4L2_FIELD_ANY &&
+ f->fmt.pix.field != V4L2_FIELD_NONE)
+ return -EINVAL;
+
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+
+ if (f->fmt.pix.width <= 320) {
+ f->fmt.pix.width = 320;
+ f->fmt.pix.height = 240;
+ } else {
+ f->fmt.pix.width = 640;
+ f->fmt.pix.height = 480;
+ }
+
+ f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+ f->fmt.pix.sizeimage = f->fmt.pix.height *
+ f->fmt.pix.bytesperline;
+ f->fmt.pix.colorspace = 0;
+ f->fmt.pix.priv = 0;
+
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ switch (meye.mchip_mode) {
+ case MCHIP_HIC_MODE_CONT_OUT:
+ default:
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
+ break;
+ case MCHIP_HIC_MODE_CONT_COMP:
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
+ break;
+ }
+
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.width = mchip_hsize();
+ f->fmt.pix.height = mchip_vsize();
+ f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+ f->fmt.pix.sizeimage = f->fmt.pix.height *
+ f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV &&
+ f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG)
+ return -EINVAL;
+
+ if (f->fmt.pix.field != V4L2_FIELD_ANY &&
+ f->fmt.pix.field != V4L2_FIELD_NONE)
+ return -EINVAL;
+
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ mutex_lock(&meye.lock);
+
+ if (f->fmt.pix.width <= 320) {
+ f->fmt.pix.width = 320;
+ f->fmt.pix.height = 240;
+ meye.params.subsample = 1;
+ } else {
+ f->fmt.pix.width = 640;
+ f->fmt.pix.height = 480;
+ meye.params.subsample = 0;
+ }
+
+ switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YUYV:
+ meye.mchip_mode = MCHIP_HIC_MODE_CONT_OUT;
+ break;
+ case V4L2_PIX_FMT_MJPEG:
+ meye.mchip_mode = MCHIP_HIC_MODE_CONT_COMP;
+ break;
+ }
+
+ mutex_unlock(&meye.lock);
+ f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+ f->fmt.pix.sizeimage = f->fmt.pix.height *
+ f->fmt.pix.bytesperline;
+ f->fmt.pix.colorspace = 0;
+ f->fmt.pix.priv = 0;
+
+ return 0;
+}
+
+static int vidioc_reqbufs(struct file *file, void *fh,
+ struct v4l2_requestbuffers *req)
+{
+ int i;
+
+ if (req->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+ if (meye.grab_fbuffer && req->count == gbuffers) {
+ /* already allocated, no modifications */
+ return 0;
+ }
+
+ mutex_lock(&meye.lock);
+ if (meye.grab_fbuffer) {
+ for (i = 0; i < gbuffers; i++)
+ if (meye.vma_use_count[i]) {
+ mutex_unlock(&meye.lock);
+ return -EINVAL;
+ }
+ rvfree(meye.grab_fbuffer, gbuffers * gbufsize);
+ meye.grab_fbuffer = NULL;
+ }
+
+ gbuffers = max(2, min((int)req->count, MEYE_MAX_BUFNBRS));
+ req->count = gbuffers;
+ meye.grab_fbuffer = rvmalloc(gbuffers * gbufsize);
+
+ if (!meye.grab_fbuffer) {
+ printk(KERN_ERR "meye: v4l framebuffer allocation"
+ " failed\n");
+ mutex_unlock(&meye.lock);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < gbuffers; i++)
+ meye.vma_use_count[i] = 0;
+
+ mutex_unlock(&meye.lock);
+
+ return 0;
+}
+
+static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+ unsigned int index = buf->index;
+
+ if (index >= gbuffers)
+ return -EINVAL;
+
+ buf->bytesused = meye.grab_buffer[index].size;
+ buf->flags = V4L2_BUF_FLAG_MAPPED;
+
+ if (meye.grab_buffer[index].state == MEYE_BUF_USING)
+ buf->flags |= V4L2_BUF_FLAG_QUEUED;
+
+ if (meye.grab_buffer[index].state == MEYE_BUF_DONE)
+ buf->flags |= V4L2_BUF_FLAG_DONE;
+
+ buf->field = V4L2_FIELD_NONE;
+ buf->timestamp = meye.grab_buffer[index].timestamp;
+ buf->sequence = meye.grab_buffer[index].sequence;
+ buf->memory = V4L2_MEMORY_MMAP;
+ buf->m.offset = index * gbufsize;
+ buf->length = gbufsize;
+
+ return 0;
+}
+
+static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+ if (buf->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+ if (buf->index >= gbuffers)
+ return -EINVAL;
+
+ if (meye.grab_buffer[buf->index].state != MEYE_BUF_UNUSED)
+ return -EINVAL;
+
+ mutex_lock(&meye.lock);
+ buf->flags |= V4L2_BUF_FLAG_QUEUED;
+ buf->flags &= ~V4L2_BUF_FLAG_DONE;
+ meye.grab_buffer[buf->index].state = MEYE_BUF_USING;
+ kfifo_in_locked(&meye.grabq, (unsigned char *)&buf->index,
+ sizeof(int), &meye.grabq_lock);
+ mutex_unlock(&meye.lock);
+
+ return 0;
+}
+
+static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+ int reqnr;
+
+ if (buf->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+ mutex_lock(&meye.lock);
+
+ if (kfifo_len(&meye.doneq) == 0 && file->f_flags & O_NONBLOCK) {
+ mutex_unlock(&meye.lock);
+ return -EAGAIN;
+ }
+
+ if (wait_event_interruptible(meye.proc_list,
+ kfifo_len(&meye.doneq) != 0) < 0) {
+ mutex_unlock(&meye.lock);
+ return -EINTR;
+ }
+
+ if (!kfifo_out_locked(&meye.doneq, (unsigned char *)&reqnr,
+ sizeof(int), &meye.doneq_lock)) {
+ mutex_unlock(&meye.lock);
+ return -EBUSY;
+ }
+
+ if (meye.grab_buffer[reqnr].state != MEYE_BUF_DONE) {
+ mutex_unlock(&meye.lock);
+ return -EINVAL;
+ }
+
+ buf->index = reqnr;
+ buf->bytesused = meye.grab_buffer[reqnr].size;
+ buf->flags = V4L2_BUF_FLAG_MAPPED;
+ buf->field = V4L2_FIELD_NONE;
+ buf->timestamp = meye.grab_buffer[reqnr].timestamp;
+ buf->sequence = meye.grab_buffer[reqnr].sequence;
+ buf->memory = V4L2_MEMORY_MMAP;
+ buf->m.offset = reqnr * gbufsize;
+ buf->length = gbufsize;
+ meye.grab_buffer[reqnr].state = MEYE_BUF_UNUSED;
+ mutex_unlock(&meye.lock);
+
+ return 0;
+}
+
+static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+ mutex_lock(&meye.lock);
+
+ switch (meye.mchip_mode) {
+ case MCHIP_HIC_MODE_CONT_OUT:
+ mchip_continuous_start();
+ break;
+ case MCHIP_HIC_MODE_CONT_COMP:
+ mchip_cont_compression_start();
+ break;
+ default:
+ mutex_unlock(&meye.lock);
+ return -EINVAL;
+ }
+
+ mutex_unlock(&meye.lock);
+
+ return 0;
+}
+
+static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+ mutex_lock(&meye.lock);
+ mchip_hic_stop();
+ kfifo_reset(&meye.grabq);
+ kfifo_reset(&meye.doneq);
+
+ for (i = 0; i < MEYE_MAX_BUFNBRS; i++)
+ meye.grab_buffer[i].state = MEYE_BUF_UNUSED;
+
+ mutex_unlock(&meye.lock);
+ return 0;
+}
+
+static long vidioc_default(struct file *file, void *fh, bool valid_prio,
+ int cmd, void *arg)
+{
+ switch (cmd) {
+ case MEYEIOC_G_PARAMS:
+ return meyeioc_g_params((struct meye_params *) arg);
+
+ case MEYEIOC_S_PARAMS:
+ return meyeioc_s_params((struct meye_params *) arg);
+
+ case MEYEIOC_QBUF_CAPT:
+ return meyeioc_qbuf_capt((int *) arg);
+
+ case MEYEIOC_SYNC:
+ return meyeioc_sync(file, fh, (int *) arg);
+
+ case MEYEIOC_STILLCAPT:
+ return meyeioc_stillcapt();
+
+ case MEYEIOC_STILLJCAPT:
+ return meyeioc_stilljcapt((int *) arg);
+
+ default:
+ return -EINVAL;
+ }
+
+}
+
+static unsigned int meye_poll(struct file *file, poll_table *wait)
+{
+ unsigned int res = 0;
+
+ mutex_lock(&meye.lock);
+ poll_wait(file, &meye.proc_list, wait);
+ if (kfifo_len(&meye.doneq))
+ res = POLLIN | POLLRDNORM;
+ mutex_unlock(&meye.lock);
+ return res;
+}
+
+static void meye_vm_open(struct vm_area_struct *vma)
+{
+ long idx = (long)vma->vm_private_data;
+ meye.vma_use_count[idx]++;
+}
+
+static void meye_vm_close(struct vm_area_struct *vma)
+{
+ long idx = (long)vma->vm_private_data;
+ meye.vma_use_count[idx]--;
+}
+
+static const struct vm_operations_struct meye_vm_ops = {
+ .open = meye_vm_open,
+ .close = meye_vm_close,
+};
+
+static int meye_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ unsigned long start = vma->vm_start;
+ unsigned long size = vma->vm_end - vma->vm_start;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ unsigned long page, pos;
+
+ mutex_lock(&meye.lock);
+ if (size > gbuffers * gbufsize) {
+ mutex_unlock(&meye.lock);
+ return -EINVAL;
+ }
+ if (!meye.grab_fbuffer) {
+ int i;
+
+ /* lazy allocation */
+ meye.grab_fbuffer = rvmalloc(gbuffers*gbufsize);
+ if (!meye.grab_fbuffer) {
+ printk(KERN_ERR "meye: v4l framebuffer allocation failed\n");
+ mutex_unlock(&meye.lock);
+ return -ENOMEM;
+ }
+ for (i = 0; i < gbuffers; i++)
+ meye.vma_use_count[i] = 0;
+ }
+ pos = (unsigned long)meye.grab_fbuffer + offset;
+
+ while (size > 0) {
+ page = vmalloc_to_pfn((void *)pos);
+ if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
+ mutex_unlock(&meye.lock);
+ return -EAGAIN;
+ }
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size -= PAGE_SIZE;
+ else
+ size = 0;
+ }
+
+ vma->vm_ops = &meye_vm_ops;
+ vma->vm_flags &= ~VM_IO; /* not I/O memory */
+ vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
+ vma->vm_private_data = (void *) (offset / gbufsize);
+ meye_vm_open(vma);
+
+ mutex_unlock(&meye.lock);
+ return 0;
+}
+
+static const struct v4l2_file_operations meye_fops = {
+ .owner = THIS_MODULE,
+ .open = meye_open,
+ .release = meye_release,
+ .mmap = meye_mmap,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = meye_poll,
+};
+
+static const struct v4l2_ioctl_ops meye_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_default = vidioc_default,
+};
+
+static struct video_device meye_template = {
+ .name = "meye",
+ .fops = &meye_fops,
+ .ioctl_ops = &meye_ioctl_ops,
+ .release = video_device_release,
+};
+
+#ifdef CONFIG_PM
+static int meye_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ pci_save_state(pdev);
+ meye.pm_mchip_mode = meye.mchip_mode;
+ mchip_hic_stop();
+ mchip_set(MCHIP_MM_INTA, 0x0);
+ return 0;
+}
+
+static int meye_resume(struct pci_dev *pdev)
+{
+ pci_restore_state(pdev);
+ pci_write_config_word(meye.mchip_dev, MCHIP_PCI_SOFTRESET_SET, 1);
+
+ mchip_delay(MCHIP_HIC_CMD, 0);
+ mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE);
+ msleep(1);
+ mchip_set(MCHIP_VRJ_SOFT_RESET, 1);
+ msleep(1);
+ mchip_set(MCHIP_MM_PCI_MODE, 5);
+ msleep(1);
+ mchip_set(MCHIP_MM_INTA, MCHIP_MM_INTA_HIC_1_MASK);
+
+ switch (meye.pm_mchip_mode) {
+ case MCHIP_HIC_MODE_CONT_OUT:
+ mchip_continuous_start();
+ break;
+ case MCHIP_HIC_MODE_CONT_COMP:
+ mchip_cont_compression_start();
+ break;
+ }
+ return 0;
+}
+#endif
+
+static int __devinit meye_probe(struct pci_dev *pcidev,
+ const struct pci_device_id *ent)
+{
+ struct v4l2_device *v4l2_dev = &meye.v4l2_dev;
+ int ret = -EBUSY;
+ unsigned long mchip_adr;
+
+ if (meye.mchip_dev != NULL) {
+ printk(KERN_ERR "meye: only one device allowed!\n");
+ goto outnotdev;
+ }
+
+ ret = v4l2_device_register(&pcidev->dev, v4l2_dev);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
+ return ret;
+ }
+ ret = -ENOMEM;
+ meye.mchip_dev = pcidev;
+ meye.vdev = video_device_alloc();
+ if (!meye.vdev) {
+ v4l2_err(v4l2_dev, "video_device_alloc() failed!\n");
+ goto outnotdev;
+ }
+
+ meye.grab_temp = vmalloc(MCHIP_NB_PAGES_MJPEG * PAGE_SIZE);
+ if (!meye.grab_temp) {
+ v4l2_err(v4l2_dev, "grab buffer allocation failed\n");
+ goto outvmalloc;
+ }
+
+ spin_lock_init(&meye.grabq_lock);
+ if (kfifo_alloc(&meye.grabq, sizeof(int) * MEYE_MAX_BUFNBRS,
+ GFP_KERNEL)) {
+ v4l2_err(v4l2_dev, "fifo allocation failed\n");
+ goto outkfifoalloc1;
+ }
+ spin_lock_init(&meye.doneq_lock);
+ if (kfifo_alloc(&meye.doneq, sizeof(int) * MEYE_MAX_BUFNBRS,
+ GFP_KERNEL)) {
+ v4l2_err(v4l2_dev, "fifo allocation failed\n");
+ goto outkfifoalloc2;
+ }
+
+ memcpy(meye.vdev, &meye_template, sizeof(meye_template));
+ meye.vdev->v4l2_dev = &meye.v4l2_dev;
+
+ ret = -EIO;
+ if ((ret = sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 1))) {
+ v4l2_err(v4l2_dev, "meye: unable to power on the camera\n");
+ v4l2_err(v4l2_dev, "meye: did you enable the camera in "
+ "sonypi using the module options ?\n");
+ goto outsonypienable;
+ }
+
+ if ((ret = pci_enable_device(meye.mchip_dev))) {
+ v4l2_err(v4l2_dev, "meye: pci_enable_device failed\n");
+ goto outenabledev;
+ }
+
+ mchip_adr = pci_resource_start(meye.mchip_dev,0);
+ if (!mchip_adr) {
+ v4l2_err(v4l2_dev, "meye: mchip has no device base address\n");
+ goto outregions;
+ }
+ if (!request_mem_region(pci_resource_start(meye.mchip_dev, 0),
+ pci_resource_len(meye.mchip_dev, 0),
+ "meye")) {
+ v4l2_err(v4l2_dev, "meye: request_mem_region failed\n");
+ goto outregions;
+ }
+ meye.mchip_mmregs = ioremap(mchip_adr, MCHIP_MM_REGS);
+ if (!meye.mchip_mmregs) {
+ v4l2_err(v4l2_dev, "meye: ioremap failed\n");
+ goto outremap;
+ }
+
+ meye.mchip_irq = pcidev->irq;
+ if (request_irq(meye.mchip_irq, meye_irq,
+ IRQF_DISABLED | IRQF_SHARED, "meye", meye_irq)) {
+ v4l2_err(v4l2_dev, "request_irq failed\n");
+ goto outreqirq;
+ }
+
+ pci_write_config_byte(meye.mchip_dev, PCI_CACHE_LINE_SIZE, 8);
+ pci_write_config_byte(meye.mchip_dev, PCI_LATENCY_TIMER, 64);
+
+ pci_set_master(meye.mchip_dev);
+
+ /* Ask the camera to perform a soft reset. */
+ pci_write_config_word(meye.mchip_dev, MCHIP_PCI_SOFTRESET_SET, 1);
+
+ mchip_delay(MCHIP_HIC_CMD, 0);
+ mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE);
+
+ msleep(1);
+ mchip_set(MCHIP_VRJ_SOFT_RESET, 1);
+
+ msleep(1);
+ mchip_set(MCHIP_MM_PCI_MODE, 5);
+
+ msleep(1);
+ mchip_set(MCHIP_MM_INTA, MCHIP_MM_INTA_HIC_1_MASK);
+
+ mutex_init(&meye.lock);
+ init_waitqueue_head(&meye.proc_list);
+ meye.brightness = 32 << 10;
+ meye.hue = 32 << 10;
+ meye.colour = 32 << 10;
+ meye.contrast = 32 << 10;
+ meye.params.subsample = 0;
+ meye.params.quality = 8;
+ meye.params.sharpness = 32;
+ meye.params.agc = 48;
+ meye.params.picture = 0;
+ meye.params.framerate = 0;
+
+ sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, 32);
+ sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAHUE, 32);
+ sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACOLOR, 32);
+ sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACONTRAST, 32);
+ sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERASHARPNESS, 32);
+ sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE, 0);
+ sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC, 48);
+
+ if (video_register_device(meye.vdev, VFL_TYPE_GRABBER,
+ video_nr) < 0) {
+ v4l2_err(v4l2_dev, "video_register_device failed\n");
+ goto outvideoreg;
+ }
+
+ v4l2_info(v4l2_dev, "Motion Eye Camera Driver v%s.\n",
+ MEYE_DRIVER_VERSION);
+ v4l2_info(v4l2_dev, "mchip KL5A72002 rev. %d, base %lx, irq %d\n",
+ meye.mchip_dev->revision, mchip_adr, meye.mchip_irq);
+
+ return 0;
+
+outvideoreg:
+ free_irq(meye.mchip_irq, meye_irq);
+outreqirq:
+ iounmap(meye.mchip_mmregs);
+outremap:
+ release_mem_region(pci_resource_start(meye.mchip_dev, 0),
+ pci_resource_len(meye.mchip_dev, 0));
+outregions:
+ pci_disable_device(meye.mchip_dev);
+outenabledev:
+ sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 0);
+outsonypienable:
+ kfifo_free(&meye.doneq);
+outkfifoalloc2:
+ kfifo_free(&meye.grabq);
+outkfifoalloc1:
+ vfree(meye.grab_temp);
+outvmalloc:
+ video_device_release(meye.vdev);
+outnotdev:
+ return ret;
+}
+
+static void __devexit meye_remove(struct pci_dev *pcidev)
+{
+ video_unregister_device(meye.vdev);
+
+ mchip_hic_stop();
+
+ mchip_dma_free();
+
+ /* disable interrupts */
+ mchip_set(MCHIP_MM_INTA, 0x0);
+
+ free_irq(meye.mchip_irq, meye_irq);
+
+ iounmap(meye.mchip_mmregs);
+
+ release_mem_region(pci_resource_start(meye.mchip_dev, 0),
+ pci_resource_len(meye.mchip_dev, 0));
+
+ pci_disable_device(meye.mchip_dev);
+
+ sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 0);
+
+ kfifo_free(&meye.doneq);
+ kfifo_free(&meye.grabq);
+
+ vfree(meye.grab_temp);
+
+ if (meye.grab_fbuffer) {
+ rvfree(meye.grab_fbuffer, gbuffers*gbufsize);
+ meye.grab_fbuffer = NULL;
+ }
+
+ printk(KERN_INFO "meye: removed\n");
+}
+
+static struct pci_device_id meye_pci_tbl[] = {
+ { PCI_VDEVICE(KAWASAKI, PCI_DEVICE_ID_MCHIP_KL5A72002), 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(pci, meye_pci_tbl);
+
+static struct pci_driver meye_driver = {
+ .name = "meye",
+ .id_table = meye_pci_tbl,
+ .probe = meye_probe,
+ .remove = __devexit_p(meye_remove),
+#ifdef CONFIG_PM
+ .suspend = meye_suspend,
+ .resume = meye_resume,
+#endif
+};
+
+static int __init meye_init(void)
+{
+ gbuffers = max(2, min((int)gbuffers, MEYE_MAX_BUFNBRS));
+ if (gbufsize < 0 || gbufsize > MEYE_MAX_BUFSIZE)
+ gbufsize = MEYE_MAX_BUFSIZE;
+ gbufsize = PAGE_ALIGN(gbufsize);
+ printk(KERN_INFO "meye: using %d buffers with %dk (%dk total) "
+ "for capture\n",
+ gbuffers,
+ gbufsize / 1024, gbuffers * gbufsize / 1024);
+ return pci_register_driver(&meye_driver);
+}
+
+static void __exit meye_exit(void)
+{
+ pci_unregister_driver(&meye_driver);
+}
+
+module_init(meye_init);
+module_exit(meye_exit);
diff --git a/drivers/media/video/meye.h b/drivers/media/video/meye.h
new file mode 100644
index 00000000..4bdeb03f
--- /dev/null
+++ b/drivers/media/video/meye.h
@@ -0,0 +1,324 @@
+/*
+ * Motion Eye video4linux driver for Sony Vaio PictureBook
+ *
+ * Copyright (C) 2001-2004 Stelian Pop <stelian@popies.net>
+ *
+ * Copyright (C) 2001-2002 Alcôve <www.alcove.com>
+ *
+ * Copyright (C) 2000 Andrew Tridgell <tridge@valinux.com>
+ *
+ * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras.
+ *
+ * Some parts borrowed from various video4linux drivers, especially
+ * bttv-driver.c and zoran.c, see original files for credits.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _MEYE_PRIV_H_
+#define _MEYE_PRIV_H_
+
+#define MEYE_DRIVER_MAJORVERSION 1
+#define MEYE_DRIVER_MINORVERSION 14
+
+#define MEYE_DRIVER_VERSION __stringify(MEYE_DRIVER_MAJORVERSION) "." \
+ __stringify(MEYE_DRIVER_MINORVERSION)
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/kfifo.h>
+
+/****************************************************************************/
+/* Motion JPEG chip registers */
+/****************************************************************************/
+
+/* Motion JPEG chip PCI configuration registers */
+#define MCHIP_PCI_POWER_CSR 0x54
+#define MCHIP_PCI_MCORE_STATUS 0x60 /* see HIC_STATUS */
+#define MCHIP_PCI_HOSTUSEREQ_SET 0x64
+#define MCHIP_PCI_HOSTUSEREQ_CLR 0x68
+#define MCHIP_PCI_LOWPOWER_SET 0x6c
+#define MCHIP_PCI_LOWPOWER_CLR 0x70
+#define MCHIP_PCI_SOFTRESET_SET 0x74
+
+/* Motion JPEG chip memory mapped registers */
+#define MCHIP_MM_REGS 0x200 /* 512 bytes */
+#define MCHIP_REG_TIMEOUT 1000 /* reg access, ~us */
+#define MCHIP_MCC_VRJ_TIMEOUT 1000 /* MCC & VRJ access */
+
+#define MCHIP_MM_PCI_MODE 0x00 /* PCI access mode */
+#define MCHIP_MM_PCI_MODE_RETRY 0x00000001 /* retry mode */
+#define MCHIP_MM_PCI_MODE_MASTER 0x00000002 /* master access */
+#define MCHIP_MM_PCI_MODE_READ_LINE 0x00000004 /* read line */
+
+#define MCHIP_MM_INTA 0x04 /* Int status/mask */
+#define MCHIP_MM_INTA_MCC 0x00000001 /* MCC interrupt */
+#define MCHIP_MM_INTA_VRJ 0x00000002 /* VRJ interrupt */
+#define MCHIP_MM_INTA_HIC_1 0x00000004 /* one frame done */
+#define MCHIP_MM_INTA_HIC_1_MASK 0x00000400 /* 1: enable */
+#define MCHIP_MM_INTA_HIC_END 0x00000008 /* all frames done */
+#define MCHIP_MM_INTA_HIC_END_MASK 0x00000800
+#define MCHIP_MM_INTA_JPEG 0x00000010 /* decompress. error */
+#define MCHIP_MM_INTA_JPEG_MASK 0x00001000
+#define MCHIP_MM_INTA_CAPTURE 0x00000020 /* capture end */
+#define MCHIP_MM_INTA_PCI_ERR 0x00000040 /* PCI error */
+#define MCHIP_MM_INTA_PCI_ERR_MASK 0x00004000
+
+#define MCHIP_MM_PT_ADDR 0x08 /* page table address*/
+ /* n*4kB */
+#define MCHIP_NB_PAGES 1024 /* pages for display */
+#define MCHIP_NB_PAGES_MJPEG 256 /* pages for mjpeg */
+
+#define MCHIP_MM_FIR(n) (0x0c+(n)*4) /* Frame info 0-3 */
+#define MCHIP_MM_FIR_RDY 0x00000001 /* frame ready */
+#define MCHIP_MM_FIR_FAILFR_MASK 0xf8000000 /* # of failed frames */
+#define MCHIP_MM_FIR_FAILFR_SHIFT 27
+
+ /* continuous comp/decomp mode */
+#define MCHIP_MM_FIR_C_ENDL_MASK 0x000007fe /* end DW [10] */
+#define MCHIP_MM_FIR_C_ENDL_SHIFT 1
+#define MCHIP_MM_FIR_C_ENDP_MASK 0x0007f800 /* end page [8] */
+#define MCHIP_MM_FIR_C_ENDP_SHIFT 11
+#define MCHIP_MM_FIR_C_STARTP_MASK 0x07f80000 /* start page [8] */
+#define MCHIP_MM_FIR_C_STARTP_SHIFT 19
+
+ /* continuous picture output mode */
+#define MCHIP_MM_FIR_O_STARTP_MASK 0x7ffe0000 /* start page [10] */
+#define MCHIP_MM_FIR_O_STARTP_SHIFT 17
+
+#define MCHIP_MM_FIFO_DATA 0x1c /* PCI TGT FIFO data */
+#define MCHIP_MM_FIFO_STATUS 0x20 /* PCI TGT FIFO stat */
+#define MCHIP_MM_FIFO_MASK 0x00000003
+#define MCHIP_MM_FIFO_WAIT_OR_READY 0x00000002 /* Bits common to WAIT & READY*/
+#define MCHIP_MM_FIFO_IDLE 0x0 /* HIC idle */
+#define MCHIP_MM_FIFO_IDLE1 0x1 /* idem ??? */
+#define MCHIP_MM_FIFO_WAIT 0x2 /* wait request */
+#define MCHIP_MM_FIFO_READY 0x3 /* data ready */
+
+#define MCHIP_HIC_HOST_USEREQ 0x40 /* host uses MCORE */
+
+#define MCHIP_HIC_TP_BUSY 0x44 /* taking picture */
+
+#define MCHIP_HIC_PIC_SAVED 0x48 /* pic in SDRAM */
+
+#define MCHIP_HIC_LOWPOWER 0x4c /* clock stopped */
+
+#define MCHIP_HIC_CTL 0x50 /* HIC control */
+#define MCHIP_HIC_CTL_SOFT_RESET 0x00000001 /* MCORE reset */
+#define MCHIP_HIC_CTL_MCORE_RDY 0x00000002 /* MCORE ready */
+
+#define MCHIP_HIC_CMD 0x54 /* HIC command */
+#define MCHIP_HIC_CMD_BITS 0x00000003 /* cmd width=[1:0]*/
+#define MCHIP_HIC_CMD_NOOP 0x0
+#define MCHIP_HIC_CMD_START 0x1
+#define MCHIP_HIC_CMD_STOP 0x2
+
+#define MCHIP_HIC_MODE 0x58
+#define MCHIP_HIC_MODE_NOOP 0x0
+#define MCHIP_HIC_MODE_STILL_CAP 0x1 /* still pic capt */
+#define MCHIP_HIC_MODE_DISPLAY 0x2 /* display */
+#define MCHIP_HIC_MODE_STILL_COMP 0x3 /* still pic comp. */
+#define MCHIP_HIC_MODE_STILL_DECOMP 0x4 /* still pic decomp. */
+#define MCHIP_HIC_MODE_CONT_COMP 0x5 /* cont capt+comp */
+#define MCHIP_HIC_MODE_CONT_DECOMP 0x6 /* cont decomp+disp */
+#define MCHIP_HIC_MODE_STILL_OUT 0x7 /* still pic output */
+#define MCHIP_HIC_MODE_CONT_OUT 0x8 /* cont output */
+
+#define MCHIP_HIC_STATUS 0x5c
+#define MCHIP_HIC_STATUS_MCC_RDY 0x00000001 /* MCC reg acc ok */
+#define MCHIP_HIC_STATUS_VRJ_RDY 0x00000002 /* VRJ reg acc ok */
+#define MCHIP_HIC_STATUS_IDLE 0x00000003
+#define MCHIP_HIC_STATUS_CAPDIS 0x00000004 /* cap/disp in prog */
+#define MCHIP_HIC_STATUS_COMPDEC 0x00000008 /* (de)comp in prog */
+#define MCHIP_HIC_STATUS_BUSY 0x00000010 /* HIC busy */
+
+#define MCHIP_HIC_S_RATE 0x60 /* MJPEG # frames */
+
+#define MCHIP_HIC_PCI_VFMT 0x64 /* video format */
+#define MCHIP_HIC_PCI_VFMT_YVYU 0x00000001 /* 0: V Y' U Y */
+ /* 1: Y' V Y U */
+
+#define MCHIP_MCC_CMD 0x80 /* MCC commands */
+#define MCHIP_MCC_CMD_INITIAL 0x0 /* idle ? */
+#define MCHIP_MCC_CMD_IIC_START_SET 0x1
+#define MCHIP_MCC_CMD_IIC_END_SET 0x2
+#define MCHIP_MCC_CMD_FM_WRITE 0x3 /* frame memory */
+#define MCHIP_MCC_CMD_FM_READ 0x4
+#define MCHIP_MCC_CMD_FM_STOP 0x5
+#define MCHIP_MCC_CMD_CAPTURE 0x6
+#define MCHIP_MCC_CMD_DISPLAY 0x7
+#define MCHIP_MCC_CMD_END_DISP 0x8
+#define MCHIP_MCC_CMD_STILL_COMP 0x9
+#define MCHIP_MCC_CMD_STILL_DECOMP 0xa
+#define MCHIP_MCC_CMD_STILL_OUTPUT 0xb
+#define MCHIP_MCC_CMD_CONT_OUTPUT 0xc
+#define MCHIP_MCC_CMD_CONT_COMP 0xd
+#define MCHIP_MCC_CMD_CONT_DECOMP 0xe
+#define MCHIP_MCC_CMD_RESET 0xf /* MCC reset */
+
+#define MCHIP_MCC_IIC_WR 0x84
+
+#define MCHIP_MCC_MCC_WR 0x88
+
+#define MCHIP_MCC_MCC_RD 0x8c
+
+#define MCHIP_MCC_STATUS 0x90
+#define MCHIP_MCC_STATUS_CAPT 0x00000001 /* capturing */
+#define MCHIP_MCC_STATUS_DISP 0x00000002 /* displaying */
+#define MCHIP_MCC_STATUS_COMP 0x00000004 /* compressing */
+#define MCHIP_MCC_STATUS_DECOMP 0x00000008 /* decompressing */
+#define MCHIP_MCC_STATUS_MCC_WR 0x00000010 /* register ready */
+#define MCHIP_MCC_STATUS_MCC_RD 0x00000020 /* register ready */
+#define MCHIP_MCC_STATUS_IIC_WR 0x00000040 /* register ready */
+#define MCHIP_MCC_STATUS_OUTPUT 0x00000080 /* output in prog */
+
+#define MCHIP_MCC_SIG_POLARITY 0x94
+#define MCHIP_MCC_SIG_POL_VS_H 0x00000001 /* VS active-high */
+#define MCHIP_MCC_SIG_POL_HS_H 0x00000002 /* HS active-high */
+#define MCHIP_MCC_SIG_POL_DOE_H 0x00000004 /* DOE active-high */
+
+#define MCHIP_MCC_IRQ 0x98
+#define MCHIP_MCC_IRQ_CAPDIS_STRT 0x00000001 /* cap/disp started */
+#define MCHIP_MCC_IRQ_CAPDIS_STRT_MASK 0x00000010
+#define MCHIP_MCC_IRQ_CAPDIS_END 0x00000002 /* cap/disp ended */
+#define MCHIP_MCC_IRQ_CAPDIS_END_MASK 0x00000020
+#define MCHIP_MCC_IRQ_COMPDEC_STRT 0x00000004 /* (de)comp started */
+#define MCHIP_MCC_IRQ_COMPDEC_STRT_MASK 0x00000040
+#define MCHIP_MCC_IRQ_COMPDEC_END 0x00000008 /* (de)comp ended */
+#define MCHIP_MCC_IRQ_COMPDEC_END_MASK 0x00000080
+
+#define MCHIP_MCC_HSTART 0x9c /* video in */
+#define MCHIP_MCC_VSTART 0xa0
+#define MCHIP_MCC_HCOUNT 0xa4
+#define MCHIP_MCC_VCOUNT 0xa8
+#define MCHIP_MCC_R_XBASE 0xac /* capt/disp */
+#define MCHIP_MCC_R_YBASE 0xb0
+#define MCHIP_MCC_R_XRANGE 0xb4
+#define MCHIP_MCC_R_YRANGE 0xb8
+#define MCHIP_MCC_B_XBASE 0xbc /* comp/decomp */
+#define MCHIP_MCC_B_YBASE 0xc0
+#define MCHIP_MCC_B_XRANGE 0xc4
+#define MCHIP_MCC_B_YRANGE 0xc8
+
+#define MCHIP_MCC_R_SAMPLING 0xcc /* 1: 1:4 */
+
+#define MCHIP_VRJ_CMD 0x100 /* VRJ commands */
+
+/* VRJ registers (see table 12.2.4) */
+#define MCHIP_VRJ_COMPRESSED_DATA 0x1b0
+#define MCHIP_VRJ_PIXEL_DATA 0x1b8
+
+#define MCHIP_VRJ_BUS_MODE 0x100
+#define MCHIP_VRJ_SIGNAL_ACTIVE_LEVEL 0x108
+#define MCHIP_VRJ_PDAT_USE 0x110
+#define MCHIP_VRJ_MODE_SPECIFY 0x118
+#define MCHIP_VRJ_LIMIT_COMPRESSED_LO 0x120
+#define MCHIP_VRJ_LIMIT_COMPRESSED_HI 0x124
+#define MCHIP_VRJ_COMP_DATA_FORMAT 0x128
+#define MCHIP_VRJ_TABLE_DATA 0x140
+#define MCHIP_VRJ_RESTART_INTERVAL 0x148
+#define MCHIP_VRJ_NUM_LINES 0x150
+#define MCHIP_VRJ_NUM_PIXELS 0x158
+#define MCHIP_VRJ_NUM_COMPONENTS 0x160
+#define MCHIP_VRJ_SOF1 0x168
+#define MCHIP_VRJ_SOF2 0x170
+#define MCHIP_VRJ_SOF3 0x178
+#define MCHIP_VRJ_SOF4 0x180
+#define MCHIP_VRJ_SOS 0x188
+#define MCHIP_VRJ_SOFT_RESET 0x190
+
+#define MCHIP_VRJ_STATUS 0x1c0
+#define MCHIP_VRJ_STATUS_BUSY 0x00001
+#define MCHIP_VRJ_STATUS_COMP_ACCESS 0x00002
+#define MCHIP_VRJ_STATUS_PIXEL_ACCESS 0x00004
+#define MCHIP_VRJ_STATUS_ERROR 0x00008
+
+#define MCHIP_VRJ_IRQ_FLAG 0x1c8
+#define MCHIP_VRJ_ERROR_REPORT 0x1d8
+
+#define MCHIP_VRJ_START_COMMAND 0x1a0
+
+/****************************************************************************/
+/* Driver definitions. */
+/****************************************************************************/
+
+/* Sony Programmable I/O Controller for accessing the camera commands */
+#include <linux/sony-laptop.h>
+
+/* private API definitions */
+#include <linux/meye.h>
+#include <linux/mutex.h>
+
+
+/* Enable jpg software correction */
+#define MEYE_JPEG_CORRECTION 1
+
+/* Maximum size of a buffer */
+#define MEYE_MAX_BUFSIZE 614400 /* 640 * 480 * 2 */
+
+/* Maximum number of buffers */
+#define MEYE_MAX_BUFNBRS 32
+
+/* State of a buffer */
+#define MEYE_BUF_UNUSED 0 /* not used */
+#define MEYE_BUF_USING 1 /* currently grabbing / playing */
+#define MEYE_BUF_DONE 2 /* done */
+
+/* grab buffer */
+struct meye_grab_buffer {
+ int state; /* state of buffer */
+ unsigned long size; /* size of jpg frame */
+ struct timeval timestamp; /* timestamp */
+ unsigned long sequence; /* sequence number */
+};
+
+/* size of kfifos containings buffer indices */
+#define MEYE_QUEUE_SIZE MEYE_MAX_BUFNBRS
+
+/* Motion Eye device structure */
+struct meye {
+ struct v4l2_device v4l2_dev; /* Main v4l2_device struct */
+ struct pci_dev *mchip_dev; /* pci device */
+ u8 mchip_irq; /* irq */
+ u8 mchip_mode; /* actual mchip mode: HIC_MODE... */
+ u8 mchip_fnum; /* current mchip frame number */
+ unsigned char __iomem *mchip_mmregs;/* mchip: memory mapped registers */
+ u8 *mchip_ptable[MCHIP_NB_PAGES];/* mchip: ptable */
+ void *mchip_ptable_toc; /* mchip: ptable toc */
+ dma_addr_t mchip_dmahandle; /* mchip: dma handle to ptable toc */
+ unsigned char *grab_fbuffer; /* capture framebuffer */
+ unsigned char *grab_temp; /* temporary buffer */
+ /* list of buffers */
+ struct meye_grab_buffer grab_buffer[MEYE_MAX_BUFNBRS];
+ int vma_use_count[MEYE_MAX_BUFNBRS]; /* mmap count */
+ struct mutex lock; /* mutex for open/mmap... */
+ struct kfifo grabq; /* queue for buffers to be grabbed */
+ spinlock_t grabq_lock; /* lock protecting the queue */
+ struct kfifo doneq; /* queue for grabbed buffers */
+ spinlock_t doneq_lock; /* lock protecting the queue */
+ wait_queue_head_t proc_list; /* wait queue */
+ struct video_device *vdev; /* video device parameters */
+ u16 brightness;
+ u16 hue;
+ u16 contrast;
+ u16 colour;
+ struct meye_params params; /* additional parameters */
+ unsigned long in_use; /* set to 1 if the device is in use */
+#ifdef CONFIG_PM
+ u8 pm_mchip_mode; /* old mchip mode */
+#endif
+};
+
+#endif
diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c
new file mode 100644
index 00000000..82ce5072
--- /dev/null
+++ b/drivers/media/video/msp3400-driver.c
@@ -0,0 +1,892 @@
+/*
+ * Programming the mspx4xx sound processor family
+ *
+ * (c) 1997-2001 Gerd Knorr <kraxel@bytesex.org>
+ *
+ * what works and what doesn't:
+ *
+ * AM-Mono
+ * Support for Hauppauge cards added (decoding handled by tuner) added by
+ * Frederic Crozat <fcrozat@mail.dotcom.fr>
+ *
+ * FM-Mono
+ * should work. The stereo modes are backward compatible to FM-mono,
+ * therefore FM-Mono should be allways available.
+ *
+ * FM-Stereo (B/G, used in germany)
+ * should work, with autodetect
+ *
+ * FM-Stereo (satellite)
+ * should work, no autodetect (i.e. default is mono, but you can
+ * switch to stereo -- untested)
+ *
+ * NICAM (B/G, L , used in UK, Scandinavia, Spain and France)
+ * should work, with autodetect. Support for NICAM was added by
+ * Pekka Pietikainen <pp@netppl.fi>
+ *
+ * TODO:
+ * - better SAT support
+ *
+ * 980623 Thomas Sailer (sailer@ife.ee.ethz.ch)
+ * using soundcore instead of OSS
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/msp3400.h>
+#include <media/tvaudio.h>
+#include "msp3400-driver.h"
+
+/* ---------------------------------------------------------------------- */
+
+MODULE_DESCRIPTION("device driver for msp34xx TV sound processor");
+MODULE_AUTHOR("Gerd Knorr");
+MODULE_LICENSE("GPL");
+
+/* module parameters */
+static int opmode = OPMODE_AUTO;
+int msp_debug; /* msp_debug output */
+bool msp_once; /* no continuous stereo monitoring */
+bool msp_amsound; /* hard-wire AM sound at 6.5 Hz (france),
+ the autoscan seems work well only with FM... */
+int msp_standard = 1; /* Override auto detect of audio msp_standard,
+ if needed. */
+bool msp_dolby;
+
+int msp_stereo_thresh = 0x190; /* a2 threshold for stereo/bilingual
+ (msp34xxg only) 0x00a0-0x03c0 */
+
+/* read-only */
+module_param(opmode, int, 0444);
+
+/* read-write */
+module_param_named(once, msp_once, bool, 0644);
+module_param_named(debug, msp_debug, int, 0644);
+module_param_named(stereo_threshold, msp_stereo_thresh, int, 0644);
+module_param_named(standard, msp_standard, int, 0644);
+module_param_named(amsound, msp_amsound, bool, 0644);
+module_param_named(dolby, msp_dolby, bool, 0644);
+
+MODULE_PARM_DESC(opmode, "Forces a MSP3400 opmode. 0=Manual, 1=Autodetect, 2=Autodetect and autoselect");
+MODULE_PARM_DESC(once, "No continuous stereo monitoring");
+MODULE_PARM_DESC(debug, "Enable debug messages [0-3]");
+MODULE_PARM_DESC(stereo_threshold, "Sets signal threshold to activate stereo");
+MODULE_PARM_DESC(standard, "Specify audio standard: 32 = NTSC, 64 = radio, Default: Autodetect");
+MODULE_PARM_DESC(amsound, "Hardwire AM sound at 6.5Hz (France), FM can autoscan");
+MODULE_PARM_DESC(dolby, "Activates Dolby processing");
+
+/* ---------------------------------------------------------------------- */
+
+/* control subaddress */
+#define I2C_MSP_CONTROL 0x00
+/* demodulator unit subaddress */
+#define I2C_MSP_DEM 0x10
+/* DSP unit subaddress */
+#define I2C_MSP_DSP 0x12
+
+
+/* ----------------------------------------------------------------------- */
+/* functions for talking to the MSP3400C Sound processor */
+
+int msp_reset(struct i2c_client *client)
+{
+ /* reset and read revision code */
+ static u8 reset_off[3] = { I2C_MSP_CONTROL, 0x80, 0x00 };
+ static u8 reset_on[3] = { I2C_MSP_CONTROL, 0x00, 0x00 };
+ static u8 write[3] = { I2C_MSP_DSP + 1, 0x00, 0x1e };
+ u8 read[2];
+ struct i2c_msg reset[2] = {
+ { client->addr, I2C_M_IGNORE_NAK, 3, reset_off },
+ { client->addr, I2C_M_IGNORE_NAK, 3, reset_on },
+ };
+ struct i2c_msg test[2] = {
+ { client->addr, 0, 3, write },
+ { client->addr, I2C_M_RD, 2, read },
+ };
+
+ v4l_dbg(3, msp_debug, client, "msp_reset\n");
+ if (i2c_transfer(client->adapter, &reset[0], 1) != 1 ||
+ i2c_transfer(client->adapter, &reset[1], 1) != 1 ||
+ i2c_transfer(client->adapter, test, 2) != 2) {
+ v4l_err(client, "chip reset failed\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int msp_read(struct i2c_client *client, int dev, int addr)
+{
+ int err, retval;
+ u8 write[3];
+ u8 read[2];
+ struct i2c_msg msgs[2] = {
+ { client->addr, 0, 3, write },
+ { client->addr, I2C_M_RD, 2, read }
+ };
+
+ write[0] = dev + 1;
+ write[1] = addr >> 8;
+ write[2] = addr & 0xff;
+
+ for (err = 0; err < 3; err++) {
+ if (i2c_transfer(client->adapter, msgs, 2) == 2)
+ break;
+ v4l_warn(client, "I/O error #%d (read 0x%02x/0x%02x)\n", err,
+ dev, addr);
+ schedule_timeout_interruptible(msecs_to_jiffies(10));
+ }
+ if (err == 3) {
+ v4l_warn(client, "resetting chip, sound will go off.\n");
+ msp_reset(client);
+ return -1;
+ }
+ retval = read[0] << 8 | read[1];
+ v4l_dbg(3, msp_debug, client, "msp_read(0x%x, 0x%x): 0x%x\n",
+ dev, addr, retval);
+ return retval;
+}
+
+int msp_read_dem(struct i2c_client *client, int addr)
+{
+ return msp_read(client, I2C_MSP_DEM, addr);
+}
+
+int msp_read_dsp(struct i2c_client *client, int addr)
+{
+ return msp_read(client, I2C_MSP_DSP, addr);
+}
+
+static int msp_write(struct i2c_client *client, int dev, int addr, int val)
+{
+ int err;
+ u8 buffer[5];
+
+ buffer[0] = dev;
+ buffer[1] = addr >> 8;
+ buffer[2] = addr & 0xff;
+ buffer[3] = val >> 8;
+ buffer[4] = val & 0xff;
+
+ v4l_dbg(3, msp_debug, client, "msp_write(0x%x, 0x%x, 0x%x)\n",
+ dev, addr, val);
+ for (err = 0; err < 3; err++) {
+ if (i2c_master_send(client, buffer, 5) == 5)
+ break;
+ v4l_warn(client, "I/O error #%d (write 0x%02x/0x%02x)\n", err,
+ dev, addr);
+ schedule_timeout_interruptible(msecs_to_jiffies(10));
+ }
+ if (err == 3) {
+ v4l_warn(client, "resetting chip, sound will go off.\n");
+ msp_reset(client);
+ return -1;
+ }
+ return 0;
+}
+
+int msp_write_dem(struct i2c_client *client, int addr, int val)
+{
+ return msp_write(client, I2C_MSP_DEM, addr, val);
+}
+
+int msp_write_dsp(struct i2c_client *client, int addr, int val)
+{
+ return msp_write(client, I2C_MSP_DSP, addr, val);
+}
+
+/* ----------------------------------------------------------------------- *
+ * bits 9 8 5 - SCART DSP input Select:
+ * 0 0 0 - SCART 1 to DSP input (reset position)
+ * 0 1 0 - MONO to DSP input
+ * 1 0 0 - SCART 2 to DSP input
+ * 1 1 1 - Mute DSP input
+ *
+ * bits 11 10 6 - SCART 1 Output Select:
+ * 0 0 0 - undefined (reset position)
+ * 0 1 0 - SCART 2 Input to SCART 1 Output (for devices with 2 SCARTS)
+ * 1 0 0 - MONO input to SCART 1 Output
+ * 1 1 0 - SCART 1 DA to SCART 1 Output
+ * 0 0 1 - SCART 2 DA to SCART 1 Output
+ * 0 1 1 - SCART 1 Input to SCART 1 Output
+ * 1 1 1 - Mute SCART 1 Output
+ *
+ * bits 13 12 7 - SCART 2 Output Select (for devices with 2 Output SCART):
+ * 0 0 0 - SCART 1 DA to SCART 2 Output (reset position)
+ * 0 1 0 - SCART 1 Input to SCART 2 Output
+ * 1 0 0 - MONO input to SCART 2 Output
+ * 0 0 1 - SCART 2 DA to SCART 2 Output
+ * 0 1 1 - SCART 2 Input to SCART 2 Output
+ * 1 1 0 - Mute SCART 2 Output
+ *
+ * Bits 4 to 0 should be zero.
+ * ----------------------------------------------------------------------- */
+
+static int scarts[3][9] = {
+ /* MASK IN1 IN2 IN3 IN4 IN1_DA IN2_DA MONO MUTE */
+ /* SCART DSP Input select */
+ { 0x0320, 0x0000, 0x0200, 0x0300, 0x0020, -1, -1, 0x0100, 0x0320 },
+ /* SCART1 Output select */
+ { 0x0c40, 0x0440, 0x0400, 0x0000, 0x0840, 0x0c00, 0x0040, 0x0800, 0x0c40 },
+ /* SCART2 Output select */
+ { 0x3080, 0x1000, 0x1080, 0x2080, 0x3080, 0x0000, 0x0080, 0x2000, 0x3000 },
+};
+
+static char *scart_names[] = {
+ "in1", "in2", "in3", "in4", "in1 da", "in2 da", "mono", "mute"
+};
+
+void msp_set_scart(struct i2c_client *client, int in, int out)
+{
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
+
+ state->in_scart = in;
+
+ if (in >= 0 && in <= 7 && out >= 0 && out <= 2) {
+ if (-1 == scarts[out][in + 1])
+ return;
+
+ state->acb &= ~scarts[out][0];
+ state->acb |= scarts[out][in + 1];
+ } else
+ state->acb = 0xf60; /* Mute Input and SCART 1 Output */
+
+ v4l_dbg(1, msp_debug, client, "scart switch: %s => %d (ACB=0x%04x)\n",
+ scart_names[in], out, state->acb);
+ msp_write_dsp(client, 0x13, state->acb);
+
+ /* Sets I2S speed 0 = 1.024 Mbps, 1 = 2.048 Mbps */
+ if (state->has_i2s_conf)
+ msp_write_dem(client, 0x40, state->i2s_mode);
+}
+
+/* ------------------------------------------------------------------------ */
+
+static void msp_wake_thread(struct i2c_client *client)
+{
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
+
+ if (NULL == state->kthread)
+ return;
+ state->watch_stereo = 0;
+ state->restart = 1;
+ wake_up_interruptible(&state->wq);
+}
+
+int msp_sleep(struct msp_state *state, int timeout)
+{
+ DECLARE_WAITQUEUE(wait, current);
+
+ add_wait_queue(&state->wq, &wait);
+ if (!kthread_should_stop()) {
+ if (timeout < 0) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule();
+ } else {
+ schedule_timeout_interruptible
+ (msecs_to_jiffies(timeout));
+ }
+ }
+
+ remove_wait_queue(&state->wq, &wait);
+ try_to_freeze();
+ return state->restart;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int msp_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct msp_state *state = ctrl_to_state(ctrl);
+ struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
+ int val = ctrl->val;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_VOLUME: {
+ /* audio volume cluster */
+ int reallymuted = state->muted->val | state->scan_in_progress;
+
+ if (!reallymuted)
+ val = (val * 0x7f / 65535) << 8;
+
+ v4l_dbg(1, msp_debug, client, "mute=%s scanning=%s volume=%d\n",
+ state->muted->val ? "on" : "off",
+ state->scan_in_progress ? "yes" : "no",
+ state->volume->val);
+
+ msp_write_dsp(client, 0x0000, val);
+ msp_write_dsp(client, 0x0007, reallymuted ? 0x1 : (val | 0x1));
+ if (state->has_scart2_out_volume)
+ msp_write_dsp(client, 0x0040, reallymuted ? 0x1 : (val | 0x1));
+ if (state->has_headphones)
+ msp_write_dsp(client, 0x0006, val);
+ break;
+ }
+
+ case V4L2_CID_AUDIO_BASS:
+ val = ((val - 32768) * 0x60 / 65535) << 8;
+ msp_write_dsp(client, 0x0002, val);
+ if (state->has_headphones)
+ msp_write_dsp(client, 0x0031, val);
+ break;
+
+ case V4L2_CID_AUDIO_TREBLE:
+ val = ((val - 32768) * 0x60 / 65535) << 8;
+ msp_write_dsp(client, 0x0003, val);
+ if (state->has_headphones)
+ msp_write_dsp(client, 0x0032, val);
+ break;
+
+ case V4L2_CID_AUDIO_LOUDNESS:
+ val = val ? ((5 * 4) << 8) : 0;
+ msp_write_dsp(client, 0x0004, val);
+ if (state->has_headphones)
+ msp_write_dsp(client, 0x0033, val);
+ break;
+
+ case V4L2_CID_AUDIO_BALANCE:
+ val = (u8)((val / 256) - 128);
+ msp_write_dsp(client, 0x0001, val << 8);
+ if (state->has_headphones)
+ msp_write_dsp(client, 0x0030, val << 8);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+void msp_update_volume(struct msp_state *state)
+{
+ /* Force an update of the volume/mute cluster */
+ v4l2_ctrl_lock(state->volume);
+ state->volume->val = state->volume->cur.val;
+ state->muted->val = state->muted->cur.val;
+ msp_s_ctrl(state->volume);
+ v4l2_ctrl_unlock(state->volume);
+}
+
+/* --- v4l2 ioctls --- */
+static int msp_s_radio(struct v4l2_subdev *sd)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (state->radio)
+ return 0;
+ state->radio = 1;
+ v4l_dbg(1, msp_debug, client, "switching to radio mode\n");
+ state->watch_stereo = 0;
+ switch (state->opmode) {
+ case OPMODE_MANUAL:
+ /* set msp3400 to FM radio mode */
+ msp3400c_set_mode(client, MSP_MODE_FM_RADIO);
+ msp3400c_set_carrier(client, MSP_CARRIER(10.7),
+ MSP_CARRIER(10.7));
+ msp_update_volume(state);
+ break;
+ case OPMODE_AUTODETECT:
+ case OPMODE_AUTOSELECT:
+ /* the thread will do for us */
+ msp_wake_thread(client);
+ break;
+ }
+ return 0;
+}
+
+static int msp_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ /* new channel -- kick audio carrier scan */
+ msp_wake_thread(client);
+ return 0;
+}
+
+static int msp_querystd(struct v4l2_subdev *sd, v4l2_std_id *id)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ *id &= state->detected_std;
+
+ v4l_dbg(2, msp_debug, client,
+ "detected standard: %s(0x%08Lx)\n",
+ msp_standard_std_name(state->std), state->detected_std);
+
+ return 0;
+}
+
+static int msp_s_std(struct v4l2_subdev *sd, v4l2_std_id id)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int update = state->radio || state->v4l2_std != id;
+
+ state->v4l2_std = id;
+ state->radio = 0;
+ if (update)
+ msp_wake_thread(client);
+ return 0;
+}
+
+static int msp_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int tuner = (input >> 3) & 1;
+ int sc_in = input & 0x7;
+ int sc1_out = output & 0xf;
+ int sc2_out = (output >> 4) & 0xf;
+ u16 val, reg;
+ int i;
+ int extern_input = 1;
+
+ if (state->route_in == input && state->route_out == output)
+ return 0;
+ state->route_in = input;
+ state->route_out = output;
+ /* check if the tuner input is used */
+ for (i = 0; i < 5; i++) {
+ if (((input >> (4 + i * 4)) & 0xf) == 0)
+ extern_input = 0;
+ }
+ state->mode = extern_input ? MSP_MODE_EXTERN : MSP_MODE_AM_DETECT;
+ state->rxsubchans = V4L2_TUNER_SUB_STEREO;
+ msp_set_scart(client, sc_in, 0);
+ msp_set_scart(client, sc1_out, 1);
+ msp_set_scart(client, sc2_out, 2);
+ msp_set_audmode(client);
+ reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb;
+ val = msp_read_dem(client, reg);
+ msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8));
+ /* wake thread when a new input is chosen */
+ msp_wake_thread(client);
+ return 0;
+}
+
+static int msp_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (vt->type != V4L2_TUNER_ANALOG_TV)
+ return 0;
+ if (!state->radio) {
+ if (state->opmode == OPMODE_AUTOSELECT)
+ msp_detect_stereo(client);
+ vt->rxsubchans = state->rxsubchans;
+ }
+ vt->audmode = state->audmode;
+ vt->capability |= V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
+ return 0;
+}
+
+static int msp_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (state->radio) /* TODO: add mono/stereo support for radio */
+ return 0;
+ if (state->audmode == vt->audmode)
+ return 0;
+ state->audmode = vt->audmode;
+ /* only set audmode */
+ msp_set_audmode(client);
+ return 0;
+}
+
+static int msp_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ v4l_dbg(1, msp_debug, client, "Setting I2S speed to %d\n", freq);
+
+ switch (freq) {
+ case 1024000:
+ state->i2s_mode = 0;
+ break;
+ case 2048000:
+ state->i2s_mode = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int msp_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, state->ident,
+ (state->rev1 << 16) | state->rev2);
+}
+
+static int msp_log_status(struct v4l2_subdev *sd)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ const char *p;
+ char prefix[V4L2_SUBDEV_NAME_SIZE + 20];
+
+ if (state->opmode == OPMODE_AUTOSELECT)
+ msp_detect_stereo(client);
+ v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n",
+ client->name, state->rev1, state->rev2);
+ snprintf(prefix, sizeof(prefix), "%s: Audio: ", sd->name);
+ v4l2_ctrl_handler_log_status(&state->hdl, prefix);
+ switch (state->mode) {
+ case MSP_MODE_AM_DETECT: p = "AM (for carrier detect)"; break;
+ case MSP_MODE_FM_RADIO: p = "FM Radio"; break;
+ case MSP_MODE_FM_TERRA: p = "Terrestrial FM-mono/stereo"; break;
+ case MSP_MODE_FM_SAT: p = "Satellite FM-mono"; break;
+ case MSP_MODE_FM_NICAM1: p = "NICAM/FM (B/G, D/K)"; break;
+ case MSP_MODE_FM_NICAM2: p = "NICAM/FM (I)"; break;
+ case MSP_MODE_AM_NICAM: p = "NICAM/AM (L)"; break;
+ case MSP_MODE_BTSC: p = "BTSC"; break;
+ case MSP_MODE_EXTERN: p = "External input"; break;
+ default: p = "unknown"; break;
+ }
+ if (state->mode == MSP_MODE_EXTERN) {
+ v4l_info(client, "Mode: %s\n", p);
+ } else if (state->opmode == OPMODE_MANUAL) {
+ v4l_info(client, "Mode: %s (%s%s)\n", p,
+ (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono",
+ (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : "");
+ } else {
+ if (state->opmode == OPMODE_AUTODETECT)
+ v4l_info(client, "Mode: %s\n", p);
+ v4l_info(client, "Standard: %s (%s%s)\n",
+ msp_standard_std_name(state->std),
+ (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono",
+ (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : "");
+ }
+ v4l_info(client, "Audmode: 0x%04x\n", state->audmode);
+ v4l_info(client, "Routing: 0x%08x (input) 0x%08x (output)\n",
+ state->route_in, state->route_out);
+ v4l_info(client, "ACB: 0x%04x\n", state->acb);
+ return 0;
+}
+
+static int msp_suspend(struct i2c_client *client, pm_message_t state)
+{
+ v4l_dbg(1, msp_debug, client, "suspend\n");
+ msp_reset(client);
+ return 0;
+}
+
+static int msp_resume(struct i2c_client *client)
+{
+ v4l_dbg(1, msp_debug, client, "resume\n");
+ msp_wake_thread(client);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_ctrl_ops msp_ctrl_ops = {
+ .s_ctrl = msp_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops msp_core_ops = {
+ .log_status = msp_log_status,
+ .g_chip_ident = msp_g_chip_ident,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+ .s_std = msp_s_std,
+};
+
+static const struct v4l2_subdev_video_ops msp_video_ops = {
+ .querystd = msp_querystd,
+};
+
+static const struct v4l2_subdev_tuner_ops msp_tuner_ops = {
+ .s_frequency = msp_s_frequency,
+ .g_tuner = msp_g_tuner,
+ .s_tuner = msp_s_tuner,
+ .s_radio = msp_s_radio,
+};
+
+static const struct v4l2_subdev_audio_ops msp_audio_ops = {
+ .s_routing = msp_s_routing,
+ .s_i2s_clock_freq = msp_s_i2s_clock_freq,
+};
+
+static const struct v4l2_subdev_ops msp_ops = {
+ .core = &msp_core_ops,
+ .video = &msp_video_ops,
+ .tuner = &msp_tuner_ops,
+ .audio = &msp_audio_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct msp_state *state;
+ struct v4l2_subdev *sd;
+ struct v4l2_ctrl_handler *hdl;
+ int (*thread_func)(void *data) = NULL;
+ int msp_hard;
+ int msp_family;
+ int msp_revision;
+ int msp_product, msp_prod_hi, msp_prod_lo;
+ int msp_rom;
+
+ if (!id)
+ strlcpy(client->name, "msp3400", sizeof(client->name));
+
+ if (msp_reset(client) == -1) {
+ v4l_dbg(1, msp_debug, client, "msp3400 not found\n");
+ return -ENODEV;
+ }
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &msp_ops);
+
+ state->v4l2_std = V4L2_STD_NTSC;
+ state->detected_std = V4L2_STD_ALL;
+ state->audmode = V4L2_TUNER_MODE_STEREO;
+ state->input = -1;
+ state->i2s_mode = 0;
+ init_waitqueue_head(&state->wq);
+ /* These are the reset input/output positions */
+ state->route_in = MSP_INPUT_DEFAULT;
+ state->route_out = MSP_OUTPUT_DEFAULT;
+
+ state->rev1 = msp_read_dsp(client, 0x1e);
+ if (state->rev1 != -1)
+ state->rev2 = msp_read_dsp(client, 0x1f);
+ v4l_dbg(1, msp_debug, client, "rev1=0x%04x, rev2=0x%04x\n",
+ state->rev1, state->rev2);
+ if (state->rev1 == -1 || (state->rev1 == 0 && state->rev2 == 0)) {
+ v4l_dbg(1, msp_debug, client,
+ "not an msp3400 (cannot read chip version)\n");
+ kfree(state);
+ return -ENODEV;
+ }
+
+ msp_family = ((state->rev1 >> 4) & 0x0f) + 3;
+ msp_product = (state->rev2 >> 8) & 0xff;
+ msp_prod_hi = msp_product / 10;
+ msp_prod_lo = msp_product % 10;
+ msp_revision = (state->rev1 & 0x0f) + '@';
+ msp_hard = ((state->rev1 >> 8) & 0xff) + '@';
+ msp_rom = state->rev2 & 0x1f;
+ /* Rev B=2, C=3, D=4, G=7 */
+ state->ident = msp_family * 10000 + 4000 + msp_product * 10 +
+ msp_revision - '@';
+
+ /* Has NICAM support: all mspx41x and mspx45x products have NICAM */
+ state->has_nicam =
+ msp_prod_hi == 1 || msp_prod_hi == 5;
+ /* Has radio support: was added with revision G */
+ state->has_radio =
+ msp_revision >= 'G';
+ /* Has headphones output: not for stripped down products */
+ state->has_headphones =
+ msp_prod_lo < 5;
+ /* Has scart2 input: not in stripped down products of the '3' family */
+ state->has_scart2 =
+ msp_family >= 4 || msp_prod_lo < 7;
+ /* Has scart3 input: not in stripped down products of the '3' family */
+ state->has_scart3 =
+ msp_family >= 4 || msp_prod_lo < 5;
+ /* Has scart4 input: not in pre D revisions, not in stripped D revs */
+ state->has_scart4 =
+ msp_family >= 4 || (msp_revision >= 'D' && msp_prod_lo < 5);
+ /* Has scart2 output: not in stripped down products of
+ * the '3' family */
+ state->has_scart2_out =
+ msp_family >= 4 || msp_prod_lo < 5;
+ /* Has scart2 a volume control? Not in pre-D revisions. */
+ state->has_scart2_out_volume =
+ msp_revision > 'C' && state->has_scart2_out;
+ /* Has a configurable i2s out? */
+ state->has_i2s_conf =
+ msp_revision >= 'G' && msp_prod_lo < 7;
+ /* Has subwoofer output: not in pre-D revs and not in stripped down
+ * products */
+ state->has_subwoofer =
+ msp_revision >= 'D' && msp_prod_lo < 5;
+ /* Has soundprocessing (bass/treble/balance/loudness/equalizer):
+ * not in stripped down products */
+ state->has_sound_processing =
+ msp_prod_lo < 7;
+ /* Has Virtual Dolby Surround: only in msp34x1 */
+ state->has_virtual_dolby_surround =
+ msp_revision == 'G' && msp_prod_lo == 1;
+ /* Has Virtual Dolby Surround & Dolby Pro Logic: only in msp34x2 */
+ state->has_dolby_pro_logic =
+ msp_revision == 'G' && msp_prod_lo == 2;
+ /* The msp343xG supports BTSC only and cannot do Automatic Standard
+ * Detection. */
+ state->force_btsc =
+ msp_family == 3 && msp_revision == 'G' && msp_prod_hi == 3;
+
+ state->opmode = opmode;
+ if (state->opmode == OPMODE_AUTO) {
+ /* MSP revision G and up have both autodetect and autoselect */
+ if (msp_revision >= 'G')
+ state->opmode = OPMODE_AUTOSELECT;
+ /* MSP revision D and up have autodetect */
+ else if (msp_revision >= 'D')
+ state->opmode = OPMODE_AUTODETECT;
+ else
+ state->opmode = OPMODE_MANUAL;
+ }
+
+ hdl = &state->hdl;
+ v4l2_ctrl_handler_init(hdl, 6);
+ if (state->has_sound_processing) {
+ v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+ V4L2_CID_AUDIO_BASS, 0, 65535, 65535 / 100, 32768);
+ v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+ V4L2_CID_AUDIO_TREBLE, 0, 65535, 65535 / 100, 32768);
+ v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+ V4L2_CID_AUDIO_LOUDNESS, 0, 1, 1, 0);
+ }
+ state->volume = v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+ V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 58880);
+ v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+ V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768);
+ state->muted = v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+ sd->ctrl_handler = hdl;
+ if (hdl->error) {
+ int err = hdl->error;
+
+ v4l2_ctrl_handler_free(hdl);
+ kfree(state);
+ return err;
+ }
+
+ v4l2_ctrl_cluster(2, &state->volume);
+ v4l2_ctrl_handler_setup(hdl);
+
+ /* hello world :-) */
+ v4l_info(client, "MSP%d4%02d%c-%c%d found @ 0x%x (%s)\n",
+ msp_family, msp_product,
+ msp_revision, msp_hard, msp_rom,
+ client->addr << 1, client->adapter->name);
+ v4l_info(client, "%s ", client->name);
+ if (state->has_nicam && state->has_radio)
+ printk(KERN_CONT "supports nicam and radio, ");
+ else if (state->has_nicam)
+ printk(KERN_CONT "supports nicam, ");
+ else if (state->has_radio)
+ printk(KERN_CONT "supports radio, ");
+ printk(KERN_CONT "mode is ");
+
+ /* version-specific initialization */
+ switch (state->opmode) {
+ case OPMODE_MANUAL:
+ printk(KERN_CONT "manual");
+ thread_func = msp3400c_thread;
+ break;
+ case OPMODE_AUTODETECT:
+ printk(KERN_CONT "autodetect");
+ thread_func = msp3410d_thread;
+ break;
+ case OPMODE_AUTOSELECT:
+ printk(KERN_CONT "autodetect and autoselect");
+ thread_func = msp34xxg_thread;
+ break;
+ }
+ printk(KERN_CONT "\n");
+
+ /* startup control thread if needed */
+ if (thread_func) {
+ state->kthread = kthread_run(thread_func, client, "msp34xx");
+
+ if (IS_ERR(state->kthread))
+ v4l_warn(client, "kernel_thread() failed\n");
+ msp_wake_thread(client);
+ }
+ return 0;
+}
+
+static int msp_remove(struct i2c_client *client)
+{
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
+
+ v4l2_device_unregister_subdev(&state->sd);
+ /* shutdown control thread */
+ if (state->kthread) {
+ state->restart = 1;
+ kthread_stop(state->kthread);
+ }
+ msp_reset(client);
+
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct i2c_device_id msp_id[] = {
+ { "msp3400", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, msp_id);
+
+static struct i2c_driver msp_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "msp3400",
+ },
+ .probe = msp_probe,
+ .remove = msp_remove,
+ .suspend = msp_suspend,
+ .resume = msp_resume,
+ .id_table = msp_id,
+};
+
+module_i2c_driver(msp_driver);
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/msp3400-driver.h b/drivers/media/video/msp3400-driver.h
new file mode 100644
index 00000000..fbe5e071
--- /dev/null
+++ b/drivers/media/video/msp3400-driver.h
@@ -0,0 +1,137 @@
+/*
+ */
+
+#ifndef MSP3400_DRIVER_H
+#define MSP3400_DRIVER_H
+
+#include <media/msp3400.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+
+/* ---------------------------------------------------------------------- */
+
+/* This macro is allowed for *constants* only, gcc must calculate it
+ at compile time. Remember -- no floats in kernel mode */
+#define MSP_CARRIER(freq) ((int)((float)(freq / 18.432) * (1 << 24)))
+
+#define MSP_MODE_AM_DETECT 0
+#define MSP_MODE_FM_RADIO 2
+#define MSP_MODE_FM_TERRA 3
+#define MSP_MODE_FM_SAT 4
+#define MSP_MODE_FM_NICAM1 5
+#define MSP_MODE_FM_NICAM2 6
+#define MSP_MODE_AM_NICAM 7
+#define MSP_MODE_BTSC 8
+#define MSP_MODE_EXTERN 9
+
+#define SCART_IN1 0
+#define SCART_IN2 1
+#define SCART_IN3 2
+#define SCART_IN4 3
+#define SCART_IN1_DA 4
+#define SCART_IN2_DA 5
+#define SCART_MONO 6
+#define SCART_MUTE 7
+
+#define SCART_DSP_IN 0
+#define SCART1_OUT 1
+#define SCART2_OUT 2
+
+#define OPMODE_AUTO -1
+#define OPMODE_MANUAL 0
+#define OPMODE_AUTODETECT 1 /* use autodetect (>= msp3410 only) */
+#define OPMODE_AUTOSELECT 2 /* use autodetect & autoselect (>= msp34xxG) */
+
+/* module parameters */
+extern int msp_debug;
+extern bool msp_once;
+extern bool msp_amsound;
+extern int msp_standard;
+extern bool msp_dolby;
+extern int msp_stereo_thresh;
+
+struct msp_state {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+ int rev1, rev2;
+ int ident;
+ u8 has_nicam;
+ u8 has_radio;
+ u8 has_headphones;
+ u8 has_ntsc_jp_d_k3;
+ u8 has_scart2;
+ u8 has_scart3;
+ u8 has_scart4;
+ u8 has_scart2_out;
+ u8 has_scart2_out_volume;
+ u8 has_i2s_conf;
+ u8 has_subwoofer;
+ u8 has_sound_processing;
+ u8 has_virtual_dolby_surround;
+ u8 has_dolby_pro_logic;
+ u8 force_btsc;
+
+ int radio;
+ int opmode;
+ int std;
+ int mode;
+ v4l2_std_id v4l2_std, detected_std;
+ int nicam_on;
+ int acb;
+ int in_scart;
+ int i2s_mode;
+ int main, second; /* sound carrier */
+ int input;
+ u32 route_in;
+ u32 route_out;
+
+ /* v4l2 */
+ int audmode;
+ int rxsubchans;
+
+ struct {
+ /* volume cluster */
+ struct v4l2_ctrl *volume;
+ struct v4l2_ctrl *muted;
+ };
+
+ int scan_in_progress;
+
+ /* thread */
+ struct task_struct *kthread;
+ wait_queue_head_t wq;
+ unsigned int restart:1;
+ unsigned int watch_stereo:1;
+};
+
+static inline struct msp_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct msp_state, sd);
+}
+
+static inline struct msp_state *ctrl_to_state(struct v4l2_ctrl *ctrl)
+{
+ return container_of(ctrl->handler, struct msp_state, hdl);
+}
+
+/* msp3400-driver.c */
+int msp_write_dem(struct i2c_client *client, int addr, int val);
+int msp_write_dsp(struct i2c_client *client, int addr, int val);
+int msp_read_dem(struct i2c_client *client, int addr);
+int msp_read_dsp(struct i2c_client *client, int addr);
+int msp_reset(struct i2c_client *client);
+void msp_set_scart(struct i2c_client *client, int in, int out);
+void msp_update_volume(struct msp_state *state);
+int msp_sleep(struct msp_state *state, int timeout);
+
+/* msp3400-kthreads.c */
+const char *msp_standard_std_name(int std);
+void msp_set_audmode(struct i2c_client *client);
+int msp_detect_stereo(struct i2c_client *client);
+int msp3400c_thread(void *data);
+int msp3410d_thread(void *data);
+int msp34xxg_thread(void *data);
+void msp3400c_set_mode(struct i2c_client *client, int mode);
+void msp3400c_set_carrier(struct i2c_client *client, int cdo1, int cdo2);
+
+#endif /* MSP3400_DRIVER_H */
diff --git a/drivers/media/video/msp3400-kthreads.c b/drivers/media/video/msp3400-kthreads.c
new file mode 100644
index 00000000..f8b51714
--- /dev/null
+++ b/drivers/media/video/msp3400-kthreads.c
@@ -0,0 +1,1165 @@
+/*
+ * Programming the mspx4xx sound processor family
+ *
+ * (c) 1997-2001 Gerd Knorr <kraxel@bytesex.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/freezer.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/msp3400.h>
+#include <linux/kthread.h>
+#include <linux/suspend.h>
+#include "msp3400-driver.h"
+
+/* this one uses the automatic sound standard detection of newer msp34xx
+ chip versions */
+static struct {
+ int retval;
+ int main, second;
+ char *name;
+ v4l2_std_id std;
+} msp_stdlist[] = {
+ { 0x0000, 0, 0, "could not detect sound standard", V4L2_STD_ALL },
+ { 0x0001, 0, 0, "autodetect start", V4L2_STD_ALL },
+ { 0x0002, MSP_CARRIER(4.5), MSP_CARRIER(4.72),
+ "4.5/4.72 M Dual FM-Stereo", V4L2_STD_MN },
+ { 0x0003, MSP_CARRIER(5.5), MSP_CARRIER(5.7421875),
+ "5.5/5.74 B/G Dual FM-Stereo", V4L2_STD_BG },
+ { 0x0004, MSP_CARRIER(6.5), MSP_CARRIER(6.2578125),
+ "6.5/6.25 D/K1 Dual FM-Stereo", V4L2_STD_DK },
+ { 0x0005, MSP_CARRIER(6.5), MSP_CARRIER(6.7421875),
+ "6.5/6.74 D/K2 Dual FM-Stereo", V4L2_STD_DK },
+ { 0x0006, MSP_CARRIER(6.5), MSP_CARRIER(6.5),
+ "6.5 D/K FM-Mono (HDEV3)", V4L2_STD_DK },
+ { 0x0007, MSP_CARRIER(6.5), MSP_CARRIER(5.7421875),
+ "6.5/5.74 D/K3 Dual FM-Stereo", V4L2_STD_DK },
+ { 0x0008, MSP_CARRIER(5.5), MSP_CARRIER(5.85),
+ "5.5/5.85 B/G NICAM FM", V4L2_STD_BG },
+ { 0x0009, MSP_CARRIER(6.5), MSP_CARRIER(5.85),
+ "6.5/5.85 L NICAM AM", V4L2_STD_L },
+ { 0x000a, MSP_CARRIER(6.0), MSP_CARRIER(6.55),
+ "6.0/6.55 I NICAM FM", V4L2_STD_PAL_I },
+ { 0x000b, MSP_CARRIER(6.5), MSP_CARRIER(5.85),
+ "6.5/5.85 D/K NICAM FM", V4L2_STD_DK },
+ { 0x000c, MSP_CARRIER(6.5), MSP_CARRIER(5.85),
+ "6.5/5.85 D/K NICAM FM (HDEV2)", V4L2_STD_DK },
+ { 0x000d, MSP_CARRIER(6.5), MSP_CARRIER(5.85),
+ "6.5/5.85 D/K NICAM FM (HDEV3)", V4L2_STD_DK },
+ { 0x0020, MSP_CARRIER(4.5), MSP_CARRIER(4.5),
+ "4.5 M BTSC-Stereo", V4L2_STD_MTS },
+ { 0x0021, MSP_CARRIER(4.5), MSP_CARRIER(4.5),
+ "4.5 M BTSC-Mono + SAP", V4L2_STD_MTS },
+ { 0x0030, MSP_CARRIER(4.5), MSP_CARRIER(4.5),
+ "4.5 M EIA-J Japan Stereo", V4L2_STD_NTSC_M_JP },
+ { 0x0040, MSP_CARRIER(10.7), MSP_CARRIER(10.7),
+ "10.7 FM-Stereo Radio", V4L2_STD_ALL },
+ { 0x0050, MSP_CARRIER(6.5), MSP_CARRIER(6.5),
+ "6.5 SAT-Mono", V4L2_STD_ALL },
+ { 0x0051, MSP_CARRIER(7.02), MSP_CARRIER(7.20),
+ "7.02/7.20 SAT-Stereo", V4L2_STD_ALL },
+ { 0x0060, MSP_CARRIER(7.2), MSP_CARRIER(7.2),
+ "7.2 SAT ADR", V4L2_STD_ALL },
+ { -1, 0, 0, NULL, 0 }, /* EOF */
+};
+
+static struct msp3400c_init_data_dem {
+ int fir1[6];
+ int fir2[6];
+ int cdo1;
+ int cdo2;
+ int ad_cv;
+ int mode_reg;
+ int dsp_src;
+ int dsp_matrix;
+} msp3400c_init_data[] = {
+ { /* AM (for carrier detect / msp3400) */
+ {75, 19, 36, 35, 39, 40},
+ {75, 19, 36, 35, 39, 40},
+ MSP_CARRIER(5.5), MSP_CARRIER(5.5),
+ 0x00d0, 0x0500, 0x0020, 0x3000
+ }, { /* AM (for carrier detect / msp3410) */
+ {-1, -1, -8, 2, 59, 126},
+ {-1, -1, -8, 2, 59, 126},
+ MSP_CARRIER(5.5), MSP_CARRIER(5.5),
+ 0x00d0, 0x0100, 0x0020, 0x3000
+ }, { /* FM Radio */
+ {-8, -8, 4, 6, 78, 107},
+ {-8, -8, 4, 6, 78, 107},
+ MSP_CARRIER(10.7), MSP_CARRIER(10.7),
+ 0x00d0, 0x0480, 0x0020, 0x3000
+ }, { /* Terrestrial FM-mono + FM-stereo */
+ {3, 18, 27, 48, 66, 72},
+ {3, 18, 27, 48, 66, 72},
+ MSP_CARRIER(5.5), MSP_CARRIER(5.5),
+ 0x00d0, 0x0480, 0x0030, 0x3000
+ }, { /* Sat FM-mono */
+ { 1, 9, 14, 24, 33, 37},
+ { 3, 18, 27, 48, 66, 72},
+ MSP_CARRIER(6.5), MSP_CARRIER(6.5),
+ 0x00c6, 0x0480, 0x0000, 0x3000
+ }, { /* NICAM/FM -- B/G (5.5/5.85), D/K (6.5/5.85) */
+ {-2, -8, -10, 10, 50, 86},
+ {3, 18, 27, 48, 66, 72},
+ MSP_CARRIER(5.5), MSP_CARRIER(5.5),
+ 0x00d0, 0x0040, 0x0120, 0x3000
+ }, { /* NICAM/FM -- I (6.0/6.552) */
+ {2, 4, -6, -4, 40, 94},
+ {3, 18, 27, 48, 66, 72},
+ MSP_CARRIER(6.0), MSP_CARRIER(6.0),
+ 0x00d0, 0x0040, 0x0120, 0x3000
+ }, { /* NICAM/AM -- L (6.5/5.85) */
+ {-2, -8, -10, 10, 50, 86},
+ {-4, -12, -9, 23, 79, 126},
+ MSP_CARRIER(6.5), MSP_CARRIER(6.5),
+ 0x00c6, 0x0140, 0x0120, 0x7c00
+ },
+};
+
+struct msp3400c_carrier_detect {
+ int cdo;
+ char *name;
+};
+
+static struct msp3400c_carrier_detect msp3400c_carrier_detect_main[] = {
+ /* main carrier */
+ { MSP_CARRIER(4.5), "4.5 NTSC" },
+ { MSP_CARRIER(5.5), "5.5 PAL B/G" },
+ { MSP_CARRIER(6.0), "6.0 PAL I" },
+ { MSP_CARRIER(6.5), "6.5 PAL D/K + SAT + SECAM" }
+};
+
+static struct msp3400c_carrier_detect msp3400c_carrier_detect_55[] = {
+ /* PAL B/G */
+ { MSP_CARRIER(5.7421875), "5.742 PAL B/G FM-stereo" },
+ { MSP_CARRIER(5.85), "5.85 PAL B/G NICAM" }
+};
+
+static struct msp3400c_carrier_detect msp3400c_carrier_detect_65[] = {
+ /* PAL SAT / SECAM */
+ { MSP_CARRIER(5.85), "5.85 PAL D/K + SECAM NICAM" },
+ { MSP_CARRIER(6.2578125), "6.25 PAL D/K1 FM-stereo" },
+ { MSP_CARRIER(6.7421875), "6.74 PAL D/K2 FM-stereo" },
+ { MSP_CARRIER(7.02), "7.02 PAL SAT FM-stereo s/b" },
+ { MSP_CARRIER(7.20), "7.20 PAL SAT FM-stereo s" },
+ { MSP_CARRIER(7.38), "7.38 PAL SAT FM-stereo b" },
+};
+
+/* ------------------------------------------------------------------------ */
+
+const char *msp_standard_std_name(int std)
+{
+ int i;
+
+ for (i = 0; msp_stdlist[i].name != NULL; i++)
+ if (msp_stdlist[i].retval == std)
+ return msp_stdlist[i].name;
+ return "unknown";
+}
+
+static v4l2_std_id msp_standard_std(int std)
+{
+ int i;
+
+ for (i = 0; msp_stdlist[i].name != NULL; i++)
+ if (msp_stdlist[i].retval == std)
+ return msp_stdlist[i].std;
+ return V4L2_STD_ALL;
+}
+
+static void msp_set_source(struct i2c_client *client, u16 src)
+{
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
+
+ if (msp_dolby) {
+ msp_write_dsp(client, 0x0008, 0x0520); /* I2S1 */
+ msp_write_dsp(client, 0x0009, 0x0620); /* I2S2 */
+ } else {
+ msp_write_dsp(client, 0x0008, src);
+ msp_write_dsp(client, 0x0009, src);
+ }
+ msp_write_dsp(client, 0x000a, src);
+ msp_write_dsp(client, 0x000b, src);
+ msp_write_dsp(client, 0x000c, src);
+ if (state->has_scart2_out)
+ msp_write_dsp(client, 0x0041, src);
+}
+
+void msp3400c_set_carrier(struct i2c_client *client, int cdo1, int cdo2)
+{
+ msp_write_dem(client, 0x0093, cdo1 & 0xfff);
+ msp_write_dem(client, 0x009b, cdo1 >> 12);
+ msp_write_dem(client, 0x00a3, cdo2 & 0xfff);
+ msp_write_dem(client, 0x00ab, cdo2 >> 12);
+ msp_write_dem(client, 0x0056, 0); /* LOAD_REG_1/2 */
+}
+
+void msp3400c_set_mode(struct i2c_client *client, int mode)
+{
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
+ struct msp3400c_init_data_dem *data = &msp3400c_init_data[mode];
+ int tuner = (state->route_in >> 3) & 1;
+ int i;
+
+ v4l_dbg(1, msp_debug, client, "set_mode: %d\n", mode);
+ state->mode = mode;
+ state->rxsubchans = V4L2_TUNER_SUB_MONO;
+
+ msp_write_dem(client, 0x00bb, data->ad_cv | (tuner ? 0x100 : 0));
+
+ for (i = 5; i >= 0; i--) /* fir 1 */
+ msp_write_dem(client, 0x0001, data->fir1[i]);
+
+ msp_write_dem(client, 0x0005, 0x0004); /* fir 2 */
+ msp_write_dem(client, 0x0005, 0x0040);
+ msp_write_dem(client, 0x0005, 0x0000);
+ for (i = 5; i >= 0; i--)
+ msp_write_dem(client, 0x0005, data->fir2[i]);
+
+ msp_write_dem(client, 0x0083, data->mode_reg);
+
+ msp3400c_set_carrier(client, data->cdo1, data->cdo2);
+
+ msp_set_source(client, data->dsp_src);
+ /* set prescales */
+
+ /* volume prescale for SCART (AM mono input) */
+ msp_write_dsp(client, 0x000d, 0x1900);
+ msp_write_dsp(client, 0x000e, data->dsp_matrix);
+ if (state->has_nicam) /* nicam prescale */
+ msp_write_dsp(client, 0x0010, 0x5a00);
+}
+
+/* Set audio mode. Note that the pre-'G' models do not support BTSC+SAP,
+ nor do they support stereo BTSC. */
+static void msp3400c_set_audmode(struct i2c_client *client)
+{
+ static char *strmode[] = {
+ "mono", "stereo", "lang2", "lang1", "lang1+lang2"
+ };
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
+ char *modestr = (state->audmode >= 0 && state->audmode < 5) ?
+ strmode[state->audmode] : "unknown";
+ int src = 0; /* channel source: FM/AM, nicam or SCART */
+ int audmode = state->audmode;
+
+ if (state->opmode == OPMODE_AUTOSELECT) {
+ /* this method would break everything, let's make sure
+ * it's never called
+ */
+ v4l_dbg(1, msp_debug, client,
+ "set_audmode called with mode=%d instead of set_source (ignored)\n",
+ state->audmode);
+ return;
+ }
+
+ /* Note: for the C and D revs no NTSC stereo + SAP is possible as
+ the hardware does not support SAP. So the rxsubchans combination
+ of STEREO | LANG2 does not occur. */
+
+ if (state->mode != MSP_MODE_EXTERN) {
+ /* switch to mono if only mono is available */
+ if (state->rxsubchans == V4L2_TUNER_SUB_MONO)
+ audmode = V4L2_TUNER_MODE_MONO;
+ /* if bilingual */
+ else if (state->rxsubchans & V4L2_TUNER_SUB_LANG2) {
+ /* and mono or stereo, then fallback to lang1 */
+ if (audmode == V4L2_TUNER_MODE_MONO ||
+ audmode == V4L2_TUNER_MODE_STEREO)
+ audmode = V4L2_TUNER_MODE_LANG1;
+ }
+ /* if stereo, and audmode is not mono, then switch to stereo */
+ else if (audmode != V4L2_TUNER_MODE_MONO)
+ audmode = V4L2_TUNER_MODE_STEREO;
+ }
+
+ /* switch demodulator */
+ switch (state->mode) {
+ case MSP_MODE_FM_TERRA:
+ v4l_dbg(1, msp_debug, client, "FM set_audmode: %s\n", modestr);
+ switch (audmode) {
+ case V4L2_TUNER_MODE_STEREO:
+ msp_write_dsp(client, 0x000e, 0x3001);
+ break;
+ case V4L2_TUNER_MODE_MONO:
+ case V4L2_TUNER_MODE_LANG1:
+ case V4L2_TUNER_MODE_LANG2:
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ msp_write_dsp(client, 0x000e, 0x3000);
+ break;
+ }
+ break;
+ case MSP_MODE_FM_SAT:
+ v4l_dbg(1, msp_debug, client, "SAT set_audmode: %s\n", modestr);
+ switch (audmode) {
+ case V4L2_TUNER_MODE_MONO:
+ msp3400c_set_carrier(client, MSP_CARRIER(6.5), MSP_CARRIER(6.5));
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ msp3400c_set_carrier(client, MSP_CARRIER(7.2), MSP_CARRIER(7.02));
+ break;
+ case V4L2_TUNER_MODE_LANG1:
+ msp3400c_set_carrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02));
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ msp3400c_set_carrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02));
+ break;
+ }
+ break;
+ case MSP_MODE_FM_NICAM1:
+ case MSP_MODE_FM_NICAM2:
+ case MSP_MODE_AM_NICAM:
+ v4l_dbg(1, msp_debug, client,
+ "NICAM set_audmode: %s\n", modestr);
+ if (state->nicam_on)
+ src = 0x0100; /* NICAM */
+ break;
+ case MSP_MODE_BTSC:
+ v4l_dbg(1, msp_debug, client,
+ "BTSC set_audmode: %s\n", modestr);
+ break;
+ case MSP_MODE_EXTERN:
+ v4l_dbg(1, msp_debug, client,
+ "extern set_audmode: %s\n", modestr);
+ src = 0x0200; /* SCART */
+ break;
+ case MSP_MODE_FM_RADIO:
+ v4l_dbg(1, msp_debug, client,
+ "FM-Radio set_audmode: %s\n", modestr);
+ break;
+ default:
+ v4l_dbg(1, msp_debug, client, "mono set_audmode\n");
+ return;
+ }
+
+ /* switch audio */
+ v4l_dbg(1, msp_debug, client, "set audmode %d\n", audmode);
+ switch (audmode) {
+ case V4L2_TUNER_MODE_STEREO:
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ src |= 0x0020;
+ break;
+ case V4L2_TUNER_MODE_MONO:
+ if (state->mode == MSP_MODE_AM_NICAM) {
+ v4l_dbg(1, msp_debug, client, "switching to AM mono\n");
+ /* AM mono decoding is handled by tuner, not MSP chip */
+ /* SCART switching control register */
+ msp_set_scart(client, SCART_MONO, 0);
+ src = 0x0200;
+ break;
+ }
+ if (state->rxsubchans & V4L2_TUNER_SUB_STEREO)
+ src = 0x0030;
+ break;
+ case V4L2_TUNER_MODE_LANG1:
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ src |= 0x0010;
+ break;
+ }
+ v4l_dbg(1, msp_debug, client,
+ "set_audmode final source/matrix = 0x%x\n", src);
+
+ msp_set_source(client, src);
+}
+
+static void msp3400c_print_mode(struct i2c_client *client)
+{
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
+
+ if (state->main == state->second)
+ v4l_dbg(1, msp_debug, client,
+ "mono sound carrier: %d.%03d MHz\n",
+ state->main / 910000, (state->main / 910) % 1000);
+ else
+ v4l_dbg(1, msp_debug, client,
+ "main sound carrier: %d.%03d MHz\n",
+ state->main / 910000, (state->main / 910) % 1000);
+ if (state->mode == MSP_MODE_FM_NICAM1 || state->mode == MSP_MODE_FM_NICAM2)
+ v4l_dbg(1, msp_debug, client,
+ "NICAM/FM carrier : %d.%03d MHz\n",
+ state->second / 910000, (state->second/910) % 1000);
+ if (state->mode == MSP_MODE_AM_NICAM)
+ v4l_dbg(1, msp_debug, client,
+ "NICAM/AM carrier : %d.%03d MHz\n",
+ state->second / 910000, (state->second / 910) % 1000);
+ if (state->mode == MSP_MODE_FM_TERRA && state->main != state->second) {
+ v4l_dbg(1, msp_debug, client,
+ "FM-stereo carrier : %d.%03d MHz\n",
+ state->second / 910000, (state->second / 910) % 1000);
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int msp3400c_detect_stereo(struct i2c_client *client)
+{
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
+ int val;
+ int rxsubchans = state->rxsubchans;
+ int newnicam = state->nicam_on;
+ int update = 0;
+
+ switch (state->mode) {
+ case MSP_MODE_FM_TERRA:
+ val = msp_read_dsp(client, 0x18);
+ if (val > 32767)
+ val -= 65536;
+ v4l_dbg(2, msp_debug, client,
+ "stereo detect register: %d\n", val);
+ if (val > 8192) {
+ rxsubchans = V4L2_TUNER_SUB_STEREO;
+ } else if (val < -4096) {
+ rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ } else {
+ rxsubchans = V4L2_TUNER_SUB_MONO;
+ }
+ newnicam = 0;
+ break;
+ case MSP_MODE_FM_NICAM1:
+ case MSP_MODE_FM_NICAM2:
+ case MSP_MODE_AM_NICAM:
+ val = msp_read_dem(client, 0x23);
+ v4l_dbg(2, msp_debug, client, "nicam sync=%d, mode=%d\n",
+ val & 1, (val & 0x1e) >> 1);
+
+ if (val & 1) {
+ /* nicam synced */
+ switch ((val & 0x1e) >> 1) {
+ case 0:
+ case 8:
+ rxsubchans = V4L2_TUNER_SUB_STEREO;
+ break;
+ case 1:
+ case 9:
+ rxsubchans = V4L2_TUNER_SUB_MONO;
+ break;
+ case 2:
+ case 10:
+ rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ break;
+ default:
+ rxsubchans = V4L2_TUNER_SUB_MONO;
+ break;
+ }
+ newnicam = 1;
+ } else {
+ newnicam = 0;
+ rxsubchans = V4L2_TUNER_SUB_MONO;
+ }
+ break;
+ }
+ if (rxsubchans != state->rxsubchans) {
+ update = 1;
+ v4l_dbg(1, msp_debug, client,
+ "watch: rxsubchans %02x => %02x\n",
+ state->rxsubchans, rxsubchans);
+ state->rxsubchans = rxsubchans;
+ }
+ if (newnicam != state->nicam_on) {
+ update = 1;
+ v4l_dbg(1, msp_debug, client, "watch: nicam %d => %d\n",
+ state->nicam_on, newnicam);
+ state->nicam_on = newnicam;
+ }
+ return update;
+}
+
+/*
+ * A kernel thread for msp3400 control -- we don't want to block the
+ * in the ioctl while doing the sound carrier & stereo detect
+ */
+/* stereo/multilang monitoring */
+static void watch_stereo(struct i2c_client *client)
+{
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
+
+ if (msp_detect_stereo(client))
+ msp_set_audmode(client);
+
+ if (msp_once)
+ state->watch_stereo = 0;
+}
+
+int msp3400c_thread(void *data)
+{
+ struct i2c_client *client = data;
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
+ struct msp3400c_carrier_detect *cd;
+ int count, max1, max2, val1, val2, val, i;
+
+ v4l_dbg(1, msp_debug, client, "msp3400 daemon started\n");
+ state->detected_std = V4L2_STD_ALL;
+ set_freezable();
+ for (;;) {
+ v4l_dbg(2, msp_debug, client, "msp3400 thread: sleep\n");
+ msp_sleep(state, -1);
+ v4l_dbg(2, msp_debug, client, "msp3400 thread: wakeup\n");
+
+restart:
+ v4l_dbg(2, msp_debug, client, "thread: restart scan\n");
+ state->restart = 0;
+ if (kthread_should_stop())
+ break;
+
+ if (state->radio || MSP_MODE_EXTERN == state->mode) {
+ /* no carrier scan, just unmute */
+ v4l_dbg(1, msp_debug, client,
+ "thread: no carrier scan\n");
+ state->scan_in_progress = 0;
+ msp_update_volume(state);
+ continue;
+ }
+
+ /* mute audio */
+ state->scan_in_progress = 1;
+ msp_update_volume(state);
+
+ msp3400c_set_mode(client, MSP_MODE_AM_DETECT);
+ val1 = val2 = 0;
+ max1 = max2 = -1;
+ state->watch_stereo = 0;
+ state->nicam_on = 0;
+
+ /* wait for tuner to settle down after a channel change */
+ if (msp_sleep(state, 200))
+ goto restart;
+
+ /* carrier detect pass #1 -- main carrier */
+ cd = msp3400c_carrier_detect_main;
+ count = ARRAY_SIZE(msp3400c_carrier_detect_main);
+
+ if (msp_amsound && (state->v4l2_std & V4L2_STD_SECAM)) {
+ /* autodetect doesn't work well with AM ... */
+ max1 = 3;
+ count = 0;
+ v4l_dbg(1, msp_debug, client, "AM sound override\n");
+ }
+
+ for (i = 0; i < count; i++) {
+ msp3400c_set_carrier(client, cd[i].cdo, cd[i].cdo);
+ if (msp_sleep(state, 100))
+ goto restart;
+ val = msp_read_dsp(client, 0x1b);
+ if (val > 32767)
+ val -= 65536;
+ if (val1 < val)
+ val1 = val, max1 = i;
+ v4l_dbg(1, msp_debug, client,
+ "carrier1 val: %5d / %s\n", val, cd[i].name);
+ }
+
+ /* carrier detect pass #2 -- second (stereo) carrier */
+ switch (max1) {
+ case 1: /* 5.5 */
+ cd = msp3400c_carrier_detect_55;
+ count = ARRAY_SIZE(msp3400c_carrier_detect_55);
+ break;
+ case 3: /* 6.5 */
+ cd = msp3400c_carrier_detect_65;
+ count = ARRAY_SIZE(msp3400c_carrier_detect_65);
+ break;
+ case 0: /* 4.5 */
+ case 2: /* 6.0 */
+ default:
+ cd = NULL;
+ count = 0;
+ break;
+ }
+
+ if (msp_amsound && (state->v4l2_std & V4L2_STD_SECAM)) {
+ /* autodetect doesn't work well with AM ... */
+ cd = NULL;
+ count = 0;
+ max2 = 0;
+ }
+ for (i = 0; i < count; i++) {
+ msp3400c_set_carrier(client, cd[i].cdo, cd[i].cdo);
+ if (msp_sleep(state, 100))
+ goto restart;
+ val = msp_read_dsp(client, 0x1b);
+ if (val > 32767)
+ val -= 65536;
+ if (val2 < val)
+ val2 = val, max2 = i;
+ v4l_dbg(1, msp_debug, client,
+ "carrier2 val: %5d / %s\n", val, cd[i].name);
+ }
+
+ /* program the msp3400 according to the results */
+ state->main = msp3400c_carrier_detect_main[max1].cdo;
+ switch (max1) {
+ case 1: /* 5.5 */
+ state->detected_std = V4L2_STD_BG | V4L2_STD_PAL_H;
+ if (max2 == 0) {
+ /* B/G FM-stereo */
+ state->second = msp3400c_carrier_detect_55[max2].cdo;
+ msp3400c_set_mode(client, MSP_MODE_FM_TERRA);
+ state->watch_stereo = 1;
+ } else if (max2 == 1 && state->has_nicam) {
+ /* B/G NICAM */
+ state->second = msp3400c_carrier_detect_55[max2].cdo;
+ msp3400c_set_mode(client, MSP_MODE_FM_NICAM1);
+ state->nicam_on = 1;
+ state->watch_stereo = 1;
+ } else {
+ goto no_second;
+ }
+ break;
+ case 2: /* 6.0 */
+ /* PAL I NICAM */
+ state->detected_std = V4L2_STD_PAL_I;
+ state->second = MSP_CARRIER(6.552);
+ msp3400c_set_mode(client, MSP_MODE_FM_NICAM2);
+ state->nicam_on = 1;
+ state->watch_stereo = 1;
+ break;
+ case 3: /* 6.5 */
+ if (max2 == 1 || max2 == 2) {
+ /* D/K FM-stereo */
+ state->second = msp3400c_carrier_detect_65[max2].cdo;
+ msp3400c_set_mode(client, MSP_MODE_FM_TERRA);
+ state->watch_stereo = 1;
+ state->detected_std = V4L2_STD_DK;
+ } else if (max2 == 0 && (state->v4l2_std & V4L2_STD_SECAM)) {
+ /* L NICAM or AM-mono */
+ state->second = msp3400c_carrier_detect_65[max2].cdo;
+ msp3400c_set_mode(client, MSP_MODE_AM_NICAM);
+ state->watch_stereo = 1;
+ state->detected_std = V4L2_STD_L;
+ } else if (max2 == 0 && state->has_nicam) {
+ /* D/K NICAM */
+ state->second = msp3400c_carrier_detect_65[max2].cdo;
+ msp3400c_set_mode(client, MSP_MODE_FM_NICAM1);
+ state->nicam_on = 1;
+ state->watch_stereo = 1;
+ state->detected_std = V4L2_STD_DK;
+ } else {
+ goto no_second;
+ }
+ break;
+ case 0: /* 4.5 */
+ state->detected_std = V4L2_STD_MN;
+ default:
+no_second:
+ state->second = msp3400c_carrier_detect_main[max1].cdo;
+ msp3400c_set_mode(client, MSP_MODE_FM_TERRA);
+ break;
+ }
+ msp3400c_set_carrier(client, state->second, state->main);
+
+ /* unmute */
+ state->scan_in_progress = 0;
+ msp3400c_set_audmode(client);
+ msp_update_volume(state);
+
+ if (msp_debug)
+ msp3400c_print_mode(client);
+
+ /* monitor tv audio mode, the first time don't wait
+ so long to get a quick stereo/bilingual result */
+ count = 3;
+ while (state->watch_stereo) {
+ if (msp_sleep(state, count ? 1000 : 5000))
+ goto restart;
+ if (count)
+ count--;
+ watch_stereo(client);
+ }
+ }
+ v4l_dbg(1, msp_debug, client, "thread: exit\n");
+ return 0;
+}
+
+
+int msp3410d_thread(void *data)
+{
+ struct i2c_client *client = data;
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
+ int val, i, std, count;
+
+ v4l_dbg(1, msp_debug, client, "msp3410 daemon started\n");
+ state->detected_std = V4L2_STD_ALL;
+ set_freezable();
+ for (;;) {
+ v4l_dbg(2, msp_debug, client, "msp3410 thread: sleep\n");
+ msp_sleep(state, -1);
+ v4l_dbg(2, msp_debug, client, "msp3410 thread: wakeup\n");
+
+restart:
+ v4l_dbg(2, msp_debug, client, "thread: restart scan\n");
+ state->restart = 0;
+ if (kthread_should_stop())
+ break;
+
+ if (state->mode == MSP_MODE_EXTERN) {
+ /* no carrier scan needed, just unmute */
+ v4l_dbg(1, msp_debug, client,
+ "thread: no carrier scan\n");
+ state->scan_in_progress = 0;
+ msp_update_volume(state);
+ continue;
+ }
+
+ /* mute audio */
+ state->scan_in_progress = 1;
+ msp_update_volume(state);
+
+ /* start autodetect. Note: autodetect is not supported for
+ NTSC-M and radio, hence we force the standard in those
+ cases. */
+ if (state->radio)
+ std = 0x40;
+ else
+ std = (state->v4l2_std & V4L2_STD_NTSC) ? 0x20 : 1;
+ state->watch_stereo = 0;
+ state->nicam_on = 0;
+
+ /* wait for tuner to settle down after a channel change */
+ if (msp_sleep(state, 200))
+ goto restart;
+
+ if (msp_debug)
+ v4l_dbg(2, msp_debug, client,
+ "setting standard: %s (0x%04x)\n",
+ msp_standard_std_name(std), std);
+
+ if (std != 1) {
+ /* programmed some specific mode */
+ val = std;
+ } else {
+ /* triggered autodetect */
+ msp_write_dem(client, 0x20, std);
+ for (;;) {
+ if (msp_sleep(state, 100))
+ goto restart;
+
+ /* check results */
+ val = msp_read_dem(client, 0x7e);
+ if (val < 0x07ff)
+ break;
+ v4l_dbg(2, msp_debug, client,
+ "detection still in progress\n");
+ }
+ }
+ for (i = 0; msp_stdlist[i].name != NULL; i++)
+ if (msp_stdlist[i].retval == val)
+ break;
+ v4l_dbg(1, msp_debug, client, "current standard: %s (0x%04x)\n",
+ msp_standard_std_name(val), val);
+ state->main = msp_stdlist[i].main;
+ state->second = msp_stdlist[i].second;
+ state->std = val;
+ state->rxsubchans = V4L2_TUNER_SUB_MONO;
+
+ if (msp_amsound && !state->radio &&
+ (state->v4l2_std & V4L2_STD_SECAM) && (val != 0x0009)) {
+ /* autodetection has failed, let backup */
+ v4l_dbg(1, msp_debug, client, "autodetection failed,"
+ " switching to backup standard: %s (0x%04x)\n",
+ msp_stdlist[8].name ?
+ msp_stdlist[8].name : "unknown", val);
+ state->std = val = 0x0009;
+ msp_write_dem(client, 0x20, val);
+ } else {
+ state->detected_std = msp_standard_std(state->std);
+ }
+
+ /* set stereo */
+ switch (val) {
+ case 0x0008: /* B/G NICAM */
+ case 0x000a: /* I NICAM */
+ case 0x000b: /* D/K NICAM */
+ if (val == 0x000a)
+ state->mode = MSP_MODE_FM_NICAM2;
+ else
+ state->mode = MSP_MODE_FM_NICAM1;
+ /* just turn on stereo */
+ state->nicam_on = 1;
+ state->watch_stereo = 1;
+ break;
+ case 0x0009:
+ state->mode = MSP_MODE_AM_NICAM;
+ state->nicam_on = 1;
+ state->watch_stereo = 1;
+ break;
+ case 0x0020: /* BTSC */
+ /* The pre-'G' models only have BTSC-mono */
+ state->mode = MSP_MODE_BTSC;
+ break;
+ case 0x0040: /* FM radio */
+ state->mode = MSP_MODE_FM_RADIO;
+ state->rxsubchans = V4L2_TUNER_SUB_STEREO;
+ /* not needed in theory if we have radio, but
+ short programming enables carrier mute */
+ msp3400c_set_mode(client, MSP_MODE_FM_RADIO);
+ msp3400c_set_carrier(client, MSP_CARRIER(10.7),
+ MSP_CARRIER(10.7));
+ break;
+ case 0x0002:
+ case 0x0003:
+ case 0x0004:
+ case 0x0005:
+ state->mode = MSP_MODE_FM_TERRA;
+ state->watch_stereo = 1;
+ break;
+ }
+
+ /* set various prescales */
+ msp_write_dsp(client, 0x0d, 0x1900); /* scart */
+ msp_write_dsp(client, 0x0e, 0x3000); /* FM */
+ if (state->has_nicam)
+ msp_write_dsp(client, 0x10, 0x5a00); /* nicam */
+
+ if (state->has_i2s_conf)
+ msp_write_dem(client, 0x40, state->i2s_mode);
+
+ /* unmute */
+ msp3400c_set_audmode(client);
+ state->scan_in_progress = 0;
+ msp_update_volume(state);
+
+ /* monitor tv audio mode, the first time don't wait
+ so long to get a quick stereo/bilingual result */
+ count = 3;
+ while (state->watch_stereo) {
+ if (msp_sleep(state, count ? 1000 : 5000))
+ goto restart;
+ if (count)
+ count--;
+ watch_stereo(client);
+ }
+ }
+ v4l_dbg(1, msp_debug, client, "thread: exit\n");
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+/* msp34xxG + (autoselect no-thread)
+ * this one uses both automatic standard detection and automatic sound
+ * select which are available in the newer G versions
+ * struct msp: only norm, acb and source are really used in this mode
+ */
+
+static int msp34xxg_modus(struct i2c_client *client)
+{
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
+
+ if (state->radio) {
+ v4l_dbg(1, msp_debug, client, "selected radio modus\n");
+ return 0x0001;
+ }
+ if (state->v4l2_std == V4L2_STD_NTSC_M_JP) {
+ v4l_dbg(1, msp_debug, client, "selected M (EIA-J) modus\n");
+ return 0x4001;
+ }
+ if (state->v4l2_std == V4L2_STD_NTSC_M_KR) {
+ v4l_dbg(1, msp_debug, client, "selected M (A2) modus\n");
+ return 0x0001;
+ }
+ if (state->v4l2_std == V4L2_STD_SECAM_L) {
+ v4l_dbg(1, msp_debug, client, "selected SECAM-L modus\n");
+ return 0x6001;
+ }
+ if (state->v4l2_std & V4L2_STD_MN) {
+ v4l_dbg(1, msp_debug, client, "selected M (BTSC) modus\n");
+ return 0x2001;
+ }
+ return 0x7001;
+}
+
+static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in)
+ {
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
+ int source, matrix;
+
+ switch (state->audmode) {
+ case V4L2_TUNER_MODE_MONO:
+ source = 0; /* mono only */
+ matrix = 0x30;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ source = 4; /* stereo or B */
+ matrix = 0x10;
+ break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ source = 1; /* stereo or A|B */
+ matrix = 0x20;
+ break;
+ case V4L2_TUNER_MODE_LANG1:
+ source = 3; /* stereo or A */
+ matrix = 0x00;
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ default:
+ source = 3; /* stereo or A */
+ matrix = 0x20;
+ break;
+ }
+
+ if (in == MSP_DSP_IN_TUNER)
+ source = (source << 8) | 0x20;
+ /* the msp34x2g puts the MAIN_AVC, MAIN and AUX sources in 12, 13, 14
+ instead of 11, 12, 13. So we add one for that msp version. */
+ else if (in >= MSP_DSP_IN_MAIN_AVC && state->has_dolby_pro_logic)
+ source = ((in + 1) << 8) | matrix;
+ else
+ source = (in << 8) | matrix;
+
+ v4l_dbg(1, msp_debug, client,
+ "set source to %d (0x%x) for output %02x\n", in, source, reg);
+ msp_write_dsp(client, reg, source);
+}
+
+static void msp34xxg_set_sources(struct i2c_client *client)
+{
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
+ u32 in = state->route_in;
+
+ msp34xxg_set_source(client, 0x0008, (in >> 4) & 0xf);
+ /* quasi-peak detector is set to same input as the loudspeaker (MAIN) */
+ msp34xxg_set_source(client, 0x000c, (in >> 4) & 0xf);
+ msp34xxg_set_source(client, 0x0009, (in >> 8) & 0xf);
+ msp34xxg_set_source(client, 0x000a, (in >> 12) & 0xf);
+ if (state->has_scart2_out)
+ msp34xxg_set_source(client, 0x0041, (in >> 16) & 0xf);
+ msp34xxg_set_source(client, 0x000b, (in >> 20) & 0xf);
+}
+
+/* (re-)initialize the msp34xxg */
+static void msp34xxg_reset(struct i2c_client *client)
+{
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
+ int tuner = (state->route_in >> 3) & 1;
+ int modus;
+
+ /* initialize std to 1 (autodetect) to signal that no standard is
+ selected yet. */
+ state->std = 1;
+
+ msp_reset(client);
+
+ if (state->has_i2s_conf)
+ msp_write_dem(client, 0x40, state->i2s_mode);
+
+ /* step-by-step initialisation, as described in the manual */
+ modus = msp34xxg_modus(client);
+ modus |= tuner ? 0x100 : 0;
+ msp_write_dem(client, 0x30, modus);
+
+ /* write the dsps that may have an influence on
+ standard/audio autodetection right now */
+ msp34xxg_set_sources(client);
+
+ msp_write_dsp(client, 0x0d, 0x1900); /* scart */
+ msp_write_dsp(client, 0x0e, 0x3000); /* FM */
+ if (state->has_nicam)
+ msp_write_dsp(client, 0x10, 0x5a00); /* nicam */
+
+ /* set identification threshold. Personally, I
+ * I set it to a higher value than the default
+ * of 0x190 to ignore noisy stereo signals.
+ * this needs tuning. (recommended range 0x00a0-0x03c0)
+ * 0x7f0 = forced mono mode
+ *
+ * a2 threshold for stereo/bilingual.
+ * Note: this register is part of the Manual/Compatibility mode.
+ * It is supported by all 'G'-family chips.
+ */
+ msp_write_dem(client, 0x22, msp_stereo_thresh);
+}
+
+int msp34xxg_thread(void *data)
+{
+ struct i2c_client *client = data;
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
+ int val, i;
+
+ v4l_dbg(1, msp_debug, client, "msp34xxg daemon started\n");
+ state->detected_std = V4L2_STD_ALL;
+ set_freezable();
+ for (;;) {
+ v4l_dbg(2, msp_debug, client, "msp34xxg thread: sleep\n");
+ msp_sleep(state, -1);
+ v4l_dbg(2, msp_debug, client, "msp34xxg thread: wakeup\n");
+
+restart:
+ v4l_dbg(1, msp_debug, client, "thread: restart scan\n");
+ state->restart = 0;
+ if (kthread_should_stop())
+ break;
+
+ if (state->mode == MSP_MODE_EXTERN) {
+ /* no carrier scan needed, just unmute */
+ v4l_dbg(1, msp_debug, client,
+ "thread: no carrier scan\n");
+ state->scan_in_progress = 0;
+ msp_update_volume(state);
+ continue;
+ }
+
+ /* setup the chip*/
+ msp34xxg_reset(client);
+ state->std = state->radio ? 0x40 :
+ (state->force_btsc && msp_standard == 1) ? 32 : msp_standard;
+ msp_write_dem(client, 0x20, state->std);
+ /* start autodetect */
+ if (state->std != 1)
+ goto unmute;
+
+ /* watch autodetect */
+ v4l_dbg(1, msp_debug, client,
+ "started autodetect, waiting for result\n");
+ for (i = 0; i < 10; i++) {
+ if (msp_sleep(state, 100))
+ goto restart;
+
+ /* check results */
+ val = msp_read_dem(client, 0x7e);
+ if (val < 0x07ff) {
+ state->std = val;
+ break;
+ }
+ v4l_dbg(2, msp_debug, client,
+ "detection still in progress\n");
+ }
+ if (state->std == 1) {
+ v4l_dbg(1, msp_debug, client,
+ "detection still in progress after 10 tries. giving up.\n");
+ continue;
+ }
+
+unmute:
+ v4l_dbg(1, msp_debug, client,
+ "detected standard: %s (0x%04x)\n",
+ msp_standard_std_name(state->std), state->std);
+ state->detected_std = msp_standard_std(state->std);
+
+ if (state->std == 9) {
+ /* AM NICAM mode */
+ msp_write_dsp(client, 0x0e, 0x7c00);
+ }
+
+ /* unmute: dispatch sound to scart output, set scart volume */
+ msp_update_volume(state);
+
+ /* restore ACB */
+ if (msp_write_dsp(client, 0x13, state->acb))
+ return -1;
+
+ /* the periodic stereo/SAP check is only relevant for
+ the 0x20 standard (BTSC) */
+ if (state->std != 0x20)
+ continue;
+
+ state->watch_stereo = 1;
+
+ /* monitor tv audio mode, the first time don't wait
+ in order to get a quick stereo/SAP update */
+ watch_stereo(client);
+ while (state->watch_stereo) {
+ watch_stereo(client);
+ if (msp_sleep(state, 5000))
+ goto restart;
+ }
+ }
+ v4l_dbg(1, msp_debug, client, "thread: exit\n");
+ return 0;
+}
+
+static int msp34xxg_detect_stereo(struct i2c_client *client)
+{
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
+ int status = msp_read_dem(client, 0x0200);
+ int is_bilingual = status & 0x100;
+ int is_stereo = status & 0x40;
+ int oldrx = state->rxsubchans;
+
+ if (state->mode == MSP_MODE_EXTERN)
+ return 0;
+
+ state->rxsubchans = 0;
+ if (is_stereo)
+ state->rxsubchans = V4L2_TUNER_SUB_STEREO;
+ else
+ state->rxsubchans = V4L2_TUNER_SUB_MONO;
+ if (is_bilingual) {
+ if (state->std == 0x20)
+ state->rxsubchans |= V4L2_TUNER_SUB_SAP;
+ else
+ state->rxsubchans =
+ V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ }
+ v4l_dbg(1, msp_debug, client,
+ "status=0x%x, stereo=%d, bilingual=%d -> rxsubchans=%d\n",
+ status, is_stereo, is_bilingual, state->rxsubchans);
+ return (oldrx != state->rxsubchans);
+}
+
+static void msp34xxg_set_audmode(struct i2c_client *client)
+{
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
+
+ if (state->std == 0x20) {
+ if ((state->rxsubchans & V4L2_TUNER_SUB_SAP) &&
+ (state->audmode == V4L2_TUNER_MODE_LANG1_LANG2 ||
+ state->audmode == V4L2_TUNER_MODE_LANG2)) {
+ msp_write_dem(client, 0x20, 0x21);
+ } else {
+ msp_write_dem(client, 0x20, 0x20);
+ }
+ }
+
+ msp34xxg_set_sources(client);
+}
+
+void msp_set_audmode(struct i2c_client *client)
+{
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
+
+ switch (state->opmode) {
+ case OPMODE_MANUAL:
+ case OPMODE_AUTODETECT:
+ msp3400c_set_audmode(client);
+ break;
+ case OPMODE_AUTOSELECT:
+ msp34xxg_set_audmode(client);
+ break;
+ }
+}
+
+int msp_detect_stereo(struct i2c_client *client)
+{
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
+
+ switch (state->opmode) {
+ case OPMODE_MANUAL:
+ case OPMODE_AUTODETECT:
+ return msp3400c_detect_stereo(client);
+ case OPMODE_AUTOSELECT:
+ return msp34xxg_detect_stereo(client);
+ }
+ return 0;
+}
+
diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c
new file mode 100644
index 00000000..7e648183
--- /dev/null
+++ b/drivers/media/video/mt9m001.c
@@ -0,0 +1,737 @@
+/*
+ * Driver for MT9M001 CMOS Image Sensor from Micron
+ *
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+
+/*
+ * mt9m001 i2c address 0x5d
+ * The platform has to define ctruct i2c_board_info objects and link to them
+ * from struct soc_camera_link
+ */
+
+/* mt9m001 selected register addresses */
+#define MT9M001_CHIP_VERSION 0x00
+#define MT9M001_ROW_START 0x01
+#define MT9M001_COLUMN_START 0x02
+#define MT9M001_WINDOW_HEIGHT 0x03
+#define MT9M001_WINDOW_WIDTH 0x04
+#define MT9M001_HORIZONTAL_BLANKING 0x05
+#define MT9M001_VERTICAL_BLANKING 0x06
+#define MT9M001_OUTPUT_CONTROL 0x07
+#define MT9M001_SHUTTER_WIDTH 0x09
+#define MT9M001_FRAME_RESTART 0x0b
+#define MT9M001_SHUTTER_DELAY 0x0c
+#define MT9M001_RESET 0x0d
+#define MT9M001_READ_OPTIONS1 0x1e
+#define MT9M001_READ_OPTIONS2 0x20
+#define MT9M001_GLOBAL_GAIN 0x35
+#define MT9M001_CHIP_ENABLE 0xF1
+
+#define MT9M001_MAX_WIDTH 1280
+#define MT9M001_MAX_HEIGHT 1024
+#define MT9M001_MIN_WIDTH 48
+#define MT9M001_MIN_HEIGHT 32
+#define MT9M001_COLUMN_SKIP 20
+#define MT9M001_ROW_SKIP 12
+
+/* MT9M001 has only one fixed colorspace per pixelcode */
+struct mt9m001_datafmt {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+};
+
+/* Find a data format by a pixel code in an array */
+static const struct mt9m001_datafmt *mt9m001_find_datafmt(
+ enum v4l2_mbus_pixelcode code, const struct mt9m001_datafmt *fmt,
+ int n)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ if (fmt[i].code == code)
+ return fmt + i;
+
+ return NULL;
+}
+
+static const struct mt9m001_datafmt mt9m001_colour_fmts[] = {
+ /*
+ * Order important: first natively supported,
+ * second supported with a GPIO extender
+ */
+ {V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
+};
+
+static const struct mt9m001_datafmt mt9m001_monochrome_fmts[] = {
+ /* Order important - see above */
+ {V4L2_MBUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG},
+ {V4L2_MBUS_FMT_Y8_1X8, V4L2_COLORSPACE_JPEG},
+};
+
+struct mt9m001 {
+ struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
+ struct {
+ /* exposure/auto-exposure cluster */
+ struct v4l2_ctrl *autoexposure;
+ struct v4l2_ctrl *exposure;
+ };
+ struct v4l2_rect rect; /* Sensor window */
+ const struct mt9m001_datafmt *fmt;
+ const struct mt9m001_datafmt *fmts;
+ int num_fmts;
+ int model; /* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */
+ unsigned int total_h;
+ unsigned short y_skip_top; /* Lines to skip at the top */
+};
+
+static struct mt9m001 *to_mt9m001(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct mt9m001, subdev);
+}
+
+static int reg_read(struct i2c_client *client, const u8 reg)
+{
+ return i2c_smbus_read_word_swapped(client, reg);
+}
+
+static int reg_write(struct i2c_client *client, const u8 reg,
+ const u16 data)
+{
+ return i2c_smbus_write_word_swapped(client, reg, data);
+}
+
+static int reg_set(struct i2c_client *client, const u8 reg,
+ const u16 data)
+{
+ int ret;
+
+ ret = reg_read(client, reg);
+ if (ret < 0)
+ return ret;
+ return reg_write(client, reg, ret | data);
+}
+
+static int reg_clear(struct i2c_client *client, const u8 reg,
+ const u16 data)
+{
+ int ret;
+
+ ret = reg_read(client, reg);
+ if (ret < 0)
+ return ret;
+ return reg_write(client, reg, ret & ~data);
+}
+
+static int mt9m001_init(struct i2c_client *client)
+{
+ int ret;
+
+ dev_dbg(&client->dev, "%s\n", __func__);
+
+ /*
+ * We don't know, whether platform provides reset, issue a soft reset
+ * too. This returns all registers to their default values.
+ */
+ ret = reg_write(client, MT9M001_RESET, 1);
+ if (!ret)
+ ret = reg_write(client, MT9M001_RESET, 0);
+
+ /* Disable chip, synchronous option update */
+ if (!ret)
+ ret = reg_write(client, MT9M001_OUTPUT_CONTROL, 0);
+
+ return ret;
+}
+
+static int mt9m001_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ /* Switch to master "normal" mode or stop sensor readout */
+ if (reg_write(client, MT9M001_OUTPUT_CONTROL, enable ? 2 : 0) < 0)
+ return -EIO;
+ return 0;
+}
+
+static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+ struct v4l2_rect rect = a->c;
+ int ret;
+ const u16 hblank = 9, vblank = 25;
+
+ if (mt9m001->fmts == mt9m001_colour_fmts)
+ /*
+ * Bayer format - even number of rows for simplicity,
+ * but let the user play with the top row.
+ */
+ rect.height = ALIGN(rect.height, 2);
+
+ /* Datasheet requirement: see register description */
+ rect.width = ALIGN(rect.width, 2);
+ rect.left = ALIGN(rect.left, 2);
+
+ soc_camera_limit_side(&rect.left, &rect.width,
+ MT9M001_COLUMN_SKIP, MT9M001_MIN_WIDTH, MT9M001_MAX_WIDTH);
+
+ soc_camera_limit_side(&rect.top, &rect.height,
+ MT9M001_ROW_SKIP, MT9M001_MIN_HEIGHT, MT9M001_MAX_HEIGHT);
+
+ mt9m001->total_h = rect.height + mt9m001->y_skip_top + vblank;
+
+ /* Blanking and start values - default... */
+ ret = reg_write(client, MT9M001_HORIZONTAL_BLANKING, hblank);
+ if (!ret)
+ ret = reg_write(client, MT9M001_VERTICAL_BLANKING, vblank);
+
+ /*
+ * The caller provides a supported format, as verified per
+ * call to .try_mbus_fmt()
+ */
+ if (!ret)
+ ret = reg_write(client, MT9M001_COLUMN_START, rect.left);
+ if (!ret)
+ ret = reg_write(client, MT9M001_ROW_START, rect.top);
+ if (!ret)
+ ret = reg_write(client, MT9M001_WINDOW_WIDTH, rect.width - 1);
+ if (!ret)
+ ret = reg_write(client, MT9M001_WINDOW_HEIGHT,
+ rect.height + mt9m001->y_skip_top - 1);
+ if (!ret && v4l2_ctrl_g_ctrl(mt9m001->autoexposure) == V4L2_EXPOSURE_AUTO)
+ ret = reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h);
+
+ if (!ret)
+ mt9m001->rect = rect;
+
+ return ret;
+}
+
+static int mt9m001_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+
+ a->c = mt9m001->rect;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int mt9m001_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = MT9M001_COLUMN_SKIP;
+ a->bounds.top = MT9M001_ROW_SKIP;
+ a->bounds.width = MT9M001_MAX_WIDTH;
+ a->bounds.height = MT9M001_MAX_HEIGHT;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int mt9m001_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+
+ mf->width = mt9m001->rect.width;
+ mf->height = mt9m001->rect.height;
+ mf->code = mt9m001->fmt->code;
+ mf->colorspace = mt9m001->fmt->colorspace;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int mt9m001_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+ struct v4l2_crop a = {
+ .c = {
+ .left = mt9m001->rect.left,
+ .top = mt9m001->rect.top,
+ .width = mf->width,
+ .height = mf->height,
+ },
+ };
+ int ret;
+
+ /* No support for scaling so far, just crop. TODO: use skipping */
+ ret = mt9m001_s_crop(sd, &a);
+ if (!ret) {
+ mf->width = mt9m001->rect.width;
+ mf->height = mt9m001->rect.height;
+ mt9m001->fmt = mt9m001_find_datafmt(mf->code,
+ mt9m001->fmts, mt9m001->num_fmts);
+ mf->colorspace = mt9m001->fmt->colorspace;
+ }
+
+ return ret;
+}
+
+static int mt9m001_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+ const struct mt9m001_datafmt *fmt;
+
+ v4l_bound_align_image(&mf->width, MT9M001_MIN_WIDTH,
+ MT9M001_MAX_WIDTH, 1,
+ &mf->height, MT9M001_MIN_HEIGHT + mt9m001->y_skip_top,
+ MT9M001_MAX_HEIGHT + mt9m001->y_skip_top, 0, 0);
+
+ if (mt9m001->fmts == mt9m001_colour_fmts)
+ mf->height = ALIGN(mf->height - 1, 2);
+
+ fmt = mt9m001_find_datafmt(mf->code, mt9m001->fmts,
+ mt9m001->num_fmts);
+ if (!fmt) {
+ fmt = mt9m001->fmt;
+ mf->code = fmt->code;
+ }
+
+ mf->colorspace = fmt->colorspace;
+
+ return 0;
+}
+
+static int mt9m001_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+
+ if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+
+ if (id->match.addr != client->addr)
+ return -ENODEV;
+
+ id->ident = mt9m001->model;
+ id->revision = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int mt9m001_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ return -EINVAL;
+
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ reg->size = 2;
+ reg->val = reg_read(client, reg->reg);
+
+ if (reg->val > 0xffff)
+ return -EIO;
+
+ return 0;
+}
+
+static int mt9m001_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ return -EINVAL;
+
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ if (reg_write(client, reg->reg, reg->val) < 0)
+ return -EIO;
+
+ return 0;
+}
+#endif
+
+static int mt9m001_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct mt9m001 *mt9m001 = container_of(ctrl->handler,
+ struct mt9m001, hdl);
+ s32 min, max;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE_AUTO:
+ min = mt9m001->exposure->minimum;
+ max = mt9m001->exposure->maximum;
+ mt9m001->exposure->val =
+ (524 + (mt9m001->total_h - 1) * (max - min)) / 1048 + min;
+ break;
+ }
+ return 0;
+}
+
+static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct mt9m001 *mt9m001 = container_of(ctrl->handler,
+ struct mt9m001, hdl);
+ struct v4l2_subdev *sd = &mt9m001->subdev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct v4l2_ctrl *exp = mt9m001->exposure;
+ int data;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ if (ctrl->val)
+ data = reg_set(client, MT9M001_READ_OPTIONS2, 0x8000);
+ else
+ data = reg_clear(client, MT9M001_READ_OPTIONS2, 0x8000);
+ if (data < 0)
+ return -EIO;
+ return 0;
+
+ case V4L2_CID_GAIN:
+ /* See Datasheet Table 7, Gain settings. */
+ if (ctrl->val <= ctrl->default_value) {
+ /* Pack it into 0..1 step 0.125, register values 0..8 */
+ unsigned long range = ctrl->default_value - ctrl->minimum;
+ data = ((ctrl->val - ctrl->minimum) * 8 + range / 2) / range;
+
+ dev_dbg(&client->dev, "Setting gain %d\n", data);
+ data = reg_write(client, MT9M001_GLOBAL_GAIN, data);
+ if (data < 0)
+ return -EIO;
+ } else {
+ /* Pack it into 1.125..15 variable step, register values 9..67 */
+ /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */
+ unsigned long range = ctrl->maximum - ctrl->default_value - 1;
+ unsigned long gain = ((ctrl->val - ctrl->default_value - 1) *
+ 111 + range / 2) / range + 9;
+
+ if (gain <= 32)
+ data = gain;
+ else if (gain <= 64)
+ data = ((gain - 32) * 16 + 16) / 32 + 80;
+ else
+ data = ((gain - 64) * 7 + 28) / 56 + 96;
+
+ dev_dbg(&client->dev, "Setting gain from %d to %d\n",
+ reg_read(client, MT9M001_GLOBAL_GAIN), data);
+ data = reg_write(client, MT9M001_GLOBAL_GAIN, data);
+ if (data < 0)
+ return -EIO;
+ }
+ return 0;
+
+ case V4L2_CID_EXPOSURE_AUTO:
+ if (ctrl->val == V4L2_EXPOSURE_MANUAL) {
+ unsigned long range = exp->maximum - exp->minimum;
+ unsigned long shutter = ((exp->val - exp->minimum) * 1048 +
+ range / 2) / range + 1;
+
+ dev_dbg(&client->dev,
+ "Setting shutter width from %d to %lu\n",
+ reg_read(client, MT9M001_SHUTTER_WIDTH), shutter);
+ if (reg_write(client, MT9M001_SHUTTER_WIDTH, shutter) < 0)
+ return -EIO;
+ } else {
+ const u16 vblank = 25;
+
+ mt9m001->total_h = mt9m001->rect.height +
+ mt9m001->y_skip_top + vblank;
+ if (reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h) < 0)
+ return -EIO;
+ }
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/*
+ * Interface active, can use i2c. If it fails, it can indeed mean, that
+ * this wasn't our capture interface, so, we wait for the right one
+ */
+static int mt9m001_video_probe(struct soc_camera_link *icl,
+ struct i2c_client *client)
+{
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+ s32 data;
+ unsigned long flags;
+ int ret;
+
+ /* Enable the chip */
+ data = reg_write(client, MT9M001_CHIP_ENABLE, 1);
+ dev_dbg(&client->dev, "write: %d\n", data);
+
+ /* Read out the chip version register */
+ data = reg_read(client, MT9M001_CHIP_VERSION);
+
+ /* must be 0x8411 or 0x8421 for colour sensor and 8431 for bw */
+ switch (data) {
+ case 0x8411:
+ case 0x8421:
+ mt9m001->model = V4L2_IDENT_MT9M001C12ST;
+ mt9m001->fmts = mt9m001_colour_fmts;
+ break;
+ case 0x8431:
+ mt9m001->model = V4L2_IDENT_MT9M001C12STM;
+ mt9m001->fmts = mt9m001_monochrome_fmts;
+ break;
+ default:
+ dev_err(&client->dev,
+ "No MT9M001 chip detected, register read %x\n", data);
+ return -ENODEV;
+ }
+
+ mt9m001->num_fmts = 0;
+
+ /*
+ * This is a 10bit sensor, so by default we only allow 10bit.
+ * The platform may support different bus widths due to
+ * different routing of the data lines.
+ */
+ if (icl->query_bus_param)
+ flags = icl->query_bus_param(icl);
+ else
+ flags = SOCAM_DATAWIDTH_10;
+
+ if (flags & SOCAM_DATAWIDTH_10)
+ mt9m001->num_fmts++;
+ else
+ mt9m001->fmts++;
+
+ if (flags & SOCAM_DATAWIDTH_8)
+ mt9m001->num_fmts++;
+
+ mt9m001->fmt = &mt9m001->fmts[0];
+
+ dev_info(&client->dev, "Detected a MT9M001 chip ID %x (%s)\n", data,
+ data == 0x8431 ? "C12STM" : "C12ST");
+
+ ret = mt9m001_init(client);
+ if (ret < 0)
+ dev_err(&client->dev, "Failed to initialise the camera\n");
+
+ /* mt9m001_init() has reset the chip, returning registers to defaults */
+ return v4l2_ctrl_handler_setup(&mt9m001->hdl);
+}
+
+static void mt9m001_video_remove(struct soc_camera_link *icl)
+{
+ if (icl->free_bus)
+ icl->free_bus(icl);
+}
+
+static int mt9m001_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+
+ *lines = mt9m001->y_skip_top;
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops mt9m001_ctrl_ops = {
+ .g_volatile_ctrl = mt9m001_g_volatile_ctrl,
+ .s_ctrl = mt9m001_s_ctrl,
+};
+
+static struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = {
+ .g_chip_ident = mt9m001_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = mt9m001_g_register,
+ .s_register = mt9m001_s_register,
+#endif
+};
+
+static int mt9m001_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+
+ if (index >= mt9m001->num_fmts)
+ return -EINVAL;
+
+ *code = mt9m001->fmts[index].code;
+ return 0;
+}
+
+static int mt9m001_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ /* MT9M001 has all capture_format parameters fixed */
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_FALLING |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_MASTER;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static int mt9m001_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ const struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+ unsigned int bps = soc_mbus_get_fmtdesc(mt9m001->fmt->code)->bits_per_sample;
+
+ if (icl->set_bus_param)
+ return icl->set_bus_param(icl, 1 << (bps - 1));
+
+ /*
+ * Without board specific bus width settings we only support the
+ * sensors native bus width
+ */
+ return bps == 10 ? 0 : -EINVAL;
+}
+
+static struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = {
+ .s_stream = mt9m001_s_stream,
+ .s_mbus_fmt = mt9m001_s_fmt,
+ .g_mbus_fmt = mt9m001_g_fmt,
+ .try_mbus_fmt = mt9m001_try_fmt,
+ .s_crop = mt9m001_s_crop,
+ .g_crop = mt9m001_g_crop,
+ .cropcap = mt9m001_cropcap,
+ .enum_mbus_fmt = mt9m001_enum_fmt,
+ .g_mbus_config = mt9m001_g_mbus_config,
+ .s_mbus_config = mt9m001_s_mbus_config,
+};
+
+static struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = {
+ .g_skip_top_lines = mt9m001_g_skip_top_lines,
+};
+
+static struct v4l2_subdev_ops mt9m001_subdev_ops = {
+ .core = &mt9m001_subdev_core_ops,
+ .video = &mt9m001_subdev_video_ops,
+ .sensor = &mt9m001_subdev_sensor_ops,
+};
+
+static int mt9m001_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct mt9m001 *mt9m001;
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ int ret;
+
+ if (!icl) {
+ dev_err(&client->dev, "MT9M001 driver needs platform data\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_warn(&adapter->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
+ return -EIO;
+ }
+
+ mt9m001 = kzalloc(sizeof(struct mt9m001), GFP_KERNEL);
+ if (!mt9m001)
+ return -ENOMEM;
+
+ v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops);
+ v4l2_ctrl_handler_init(&mt9m001->hdl, 4);
+ v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops,
+ V4L2_CID_GAIN, 0, 127, 1, 64);
+ mt9m001->exposure = v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops,
+ V4L2_CID_EXPOSURE, 1, 255, 1, 255);
+ /*
+ * Simulated autoexposure. If enabled, we calculate shutter width
+ * ourselves in the driver based on vertical blanking and frame width
+ */
+ mt9m001->autoexposure = v4l2_ctrl_new_std_menu(&mt9m001->hdl,
+ &mt9m001_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
+ V4L2_EXPOSURE_AUTO);
+ mt9m001->subdev.ctrl_handler = &mt9m001->hdl;
+ if (mt9m001->hdl.error) {
+ int err = mt9m001->hdl.error;
+
+ kfree(mt9m001);
+ return err;
+ }
+ v4l2_ctrl_auto_cluster(2, &mt9m001->autoexposure,
+ V4L2_EXPOSURE_MANUAL, true);
+
+ /* Second stage probe - when a capture adapter is there */
+ mt9m001->y_skip_top = 0;
+ mt9m001->rect.left = MT9M001_COLUMN_SKIP;
+ mt9m001->rect.top = MT9M001_ROW_SKIP;
+ mt9m001->rect.width = MT9M001_MAX_WIDTH;
+ mt9m001->rect.height = MT9M001_MAX_HEIGHT;
+
+ ret = mt9m001_video_probe(icl, client);
+ if (ret) {
+ v4l2_ctrl_handler_free(&mt9m001->hdl);
+ kfree(mt9m001);
+ }
+
+ return ret;
+}
+
+static int mt9m001_remove(struct i2c_client *client)
+{
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ v4l2_device_unregister_subdev(&mt9m001->subdev);
+ v4l2_ctrl_handler_free(&mt9m001->hdl);
+ mt9m001_video_remove(icl);
+ kfree(mt9m001);
+
+ return 0;
+}
+
+static const struct i2c_device_id mt9m001_id[] = {
+ { "mt9m001", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mt9m001_id);
+
+static struct i2c_driver mt9m001_i2c_driver = {
+ .driver = {
+ .name = "mt9m001",
+ },
+ .probe = mt9m001_probe,
+ .remove = mt9m001_remove,
+ .id_table = mt9m001_id,
+};
+
+module_i2c_driver(mt9m001_i2c_driver);
+
+MODULE_DESCRIPTION("Micron MT9M001 Camera driver");
+MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mt9m032.c b/drivers/media/video/mt9m032.c
new file mode 100644
index 00000000..645973c5
--- /dev/null
+++ b/drivers/media/video/mt9m032.c
@@ -0,0 +1,869 @@
+/*
+ * Driver for MT9M032 CMOS Image Sensor from Micron
+ *
+ * Copyright (C) 2010-2011 Lund Engineering
+ * Contact: Gil Lund <gwlund@lundeng.com>
+ * Author: Martin Hostettler <martin@neutronstar.dyndns.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
+
+#include <media/media-entity.h>
+#include <media/mt9m032.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include "aptina-pll.h"
+
+/*
+ * width and height include active boundary and black parts
+ *
+ * column 0- 15 active boundary
+ * column 16-1455 image
+ * column 1456-1471 active boundary
+ * column 1472-1599 black
+ *
+ * row 0- 51 black
+ * row 53- 59 active boundary
+ * row 60-1139 image
+ * row 1140-1147 active boundary
+ * row 1148-1151 black
+ */
+
+#define MT9M032_PIXEL_ARRAY_WIDTH 1600
+#define MT9M032_PIXEL_ARRAY_HEIGHT 1152
+
+#define MT9M032_CHIP_VERSION 0x00
+#define MT9M032_CHIP_VERSION_VALUE 0x1402
+#define MT9M032_ROW_START 0x01
+#define MT9M032_ROW_START_MIN 0
+#define MT9M032_ROW_START_MAX 1152
+#define MT9M032_ROW_START_DEF 60
+#define MT9M032_COLUMN_START 0x02
+#define MT9M032_COLUMN_START_MIN 0
+#define MT9M032_COLUMN_START_MAX 1600
+#define MT9M032_COLUMN_START_DEF 16
+#define MT9M032_ROW_SIZE 0x03
+#define MT9M032_ROW_SIZE_MIN 32
+#define MT9M032_ROW_SIZE_MAX 1152
+#define MT9M032_ROW_SIZE_DEF 1080
+#define MT9M032_COLUMN_SIZE 0x04
+#define MT9M032_COLUMN_SIZE_MIN 32
+#define MT9M032_COLUMN_SIZE_MAX 1600
+#define MT9M032_COLUMN_SIZE_DEF 1440
+#define MT9M032_HBLANK 0x05
+#define MT9M032_VBLANK 0x06
+#define MT9M032_VBLANK_MAX 0x7ff
+#define MT9M032_SHUTTER_WIDTH_HIGH 0x08
+#define MT9M032_SHUTTER_WIDTH_LOW 0x09
+#define MT9M032_SHUTTER_WIDTH_MIN 1
+#define MT9M032_SHUTTER_WIDTH_MAX 1048575
+#define MT9M032_SHUTTER_WIDTH_DEF 1943
+#define MT9M032_PIX_CLK_CTRL 0x0a
+#define MT9M032_PIX_CLK_CTRL_INV_PIXCLK 0x8000
+#define MT9M032_RESTART 0x0b
+#define MT9M032_RESET 0x0d
+#define MT9M032_PLL_CONFIG1 0x11
+#define MT9M032_PLL_CONFIG1_OUTDIV_MASK 0x3f
+#define MT9M032_PLL_CONFIG1_MUL_SHIFT 8
+#define MT9M032_READ_MODE1 0x1e
+#define MT9M032_READ_MODE2 0x20
+#define MT9M032_READ_MODE2_VFLIP_SHIFT 15
+#define MT9M032_READ_MODE2_HFLIP_SHIFT 14
+#define MT9M032_READ_MODE2_ROW_BLC 0x40
+#define MT9M032_GAIN_GREEN1 0x2b
+#define MT9M032_GAIN_BLUE 0x2c
+#define MT9M032_GAIN_RED 0x2d
+#define MT9M032_GAIN_GREEN2 0x2e
+
+/* write only */
+#define MT9M032_GAIN_ALL 0x35
+#define MT9M032_GAIN_DIGITAL_MASK 0x7f
+#define MT9M032_GAIN_DIGITAL_SHIFT 8
+#define MT9M032_GAIN_AMUL_SHIFT 6
+#define MT9M032_GAIN_ANALOG_MASK 0x3f
+#define MT9M032_FORMATTER1 0x9e
+#define MT9M032_FORMATTER2 0x9f
+#define MT9M032_FORMATTER2_DOUT_EN 0x1000
+#define MT9M032_FORMATTER2_PIXCLK_EN 0x2000
+
+/*
+ * The available MT9M032 datasheet is missing documentation for register 0x10
+ * MT9P031 seems to be close enough, so use constants from that datasheet for
+ * now.
+ * But keep the name MT9P031 to remind us, that this isn't really confirmed
+ * for this sensor.
+ */
+#define MT9P031_PLL_CONTROL 0x10
+#define MT9P031_PLL_CONTROL_PWROFF 0x0050
+#define MT9P031_PLL_CONTROL_PWRON 0x0051
+#define MT9P031_PLL_CONTROL_USEPLL 0x0052
+#define MT9P031_PLL_CONFIG2 0x11
+#define MT9P031_PLL_CONFIG2_P1_DIV_MASK 0x1f
+
+struct mt9m032 {
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+ struct mt9m032_platform_data *pdata;
+
+ unsigned int pix_clock;
+
+ struct v4l2_ctrl_handler ctrls;
+ struct {
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+ };
+
+ struct mutex lock; /* Protects streaming, format, interval and crop */
+
+ bool streaming;
+
+ struct v4l2_mbus_framefmt format;
+ struct v4l2_rect crop;
+ struct v4l2_fract frame_interval;
+};
+
+#define to_mt9m032(sd) container_of(sd, struct mt9m032, subdev)
+#define to_dev(sensor) \
+ (&((struct i2c_client *)v4l2_get_subdevdata(&(sensor)->subdev))->dev)
+
+static int mt9m032_read(struct i2c_client *client, u8 reg)
+{
+ return i2c_smbus_read_word_swapped(client, reg);
+}
+
+static int mt9m032_write(struct i2c_client *client, u8 reg, const u16 data)
+{
+ return i2c_smbus_write_word_swapped(client, reg, data);
+}
+
+static u32 mt9m032_row_time(struct mt9m032 *sensor, unsigned int width)
+{
+ unsigned int effective_width;
+ u32 ns;
+
+ effective_width = width + 716; /* empirical value */
+ ns = div_u64(1000000000ULL * effective_width, sensor->pix_clock);
+ dev_dbg(to_dev(sensor), "MT9M032 line time: %u ns\n", ns);
+ return ns;
+}
+
+static int mt9m032_update_timing(struct mt9m032 *sensor,
+ struct v4l2_fract *interval)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+ struct v4l2_rect *crop = &sensor->crop;
+ unsigned int min_vblank;
+ unsigned int vblank;
+ u32 row_time;
+
+ if (!interval)
+ interval = &sensor->frame_interval;
+
+ row_time = mt9m032_row_time(sensor, crop->width);
+
+ vblank = div_u64(1000000000ULL * interval->numerator,
+ (u64)row_time * interval->denominator)
+ - crop->height;
+
+ if (vblank > MT9M032_VBLANK_MAX) {
+ /* hardware limits to 11 bit values */
+ interval->denominator = 1000;
+ interval->numerator =
+ div_u64((crop->height + MT9M032_VBLANK_MAX) *
+ (u64)row_time * interval->denominator,
+ 1000000000ULL);
+ vblank = div_u64(1000000000ULL * interval->numerator,
+ (u64)row_time * interval->denominator)
+ - crop->height;
+ }
+ /* enforce minimal 1.6ms blanking time. */
+ min_vblank = 1600000 / row_time;
+ vblank = clamp_t(unsigned int, vblank, min_vblank, MT9M032_VBLANK_MAX);
+
+ return mt9m032_write(client, MT9M032_VBLANK, vblank);
+}
+
+static int mt9m032_update_geom_timing(struct mt9m032 *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+ int ret;
+
+ ret = mt9m032_write(client, MT9M032_COLUMN_SIZE,
+ sensor->crop.width - 1);
+ if (!ret)
+ ret = mt9m032_write(client, MT9M032_ROW_SIZE,
+ sensor->crop.height - 1);
+ if (!ret)
+ ret = mt9m032_write(client, MT9M032_COLUMN_START,
+ sensor->crop.left);
+ if (!ret)
+ ret = mt9m032_write(client, MT9M032_ROW_START,
+ sensor->crop.top);
+ if (!ret)
+ ret = mt9m032_update_timing(sensor, NULL);
+ return ret;
+}
+
+static int update_formatter2(struct mt9m032 *sensor, bool streaming)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+ u16 reg_val = MT9M032_FORMATTER2_DOUT_EN
+ | 0x0070; /* parts reserved! */
+ /* possibly for changing to 14-bit mode */
+
+ if (streaming)
+ reg_val |= MT9M032_FORMATTER2_PIXCLK_EN; /* pixclock enable */
+
+ return mt9m032_write(client, MT9M032_FORMATTER2, reg_val);
+}
+
+static int mt9m032_setup_pll(struct mt9m032 *sensor)
+{
+ static const struct aptina_pll_limits limits = {
+ .ext_clock_min = 8000000,
+ .ext_clock_max = 16500000,
+ .int_clock_min = 2000000,
+ .int_clock_max = 24000000,
+ .out_clock_min = 322000000,
+ .out_clock_max = 693000000,
+ .pix_clock_max = 99000000,
+ .n_min = 1,
+ .n_max = 64,
+ .m_min = 16,
+ .m_max = 255,
+ .p1_min = 1,
+ .p1_max = 128,
+ };
+
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+ struct mt9m032_platform_data *pdata = sensor->pdata;
+ struct aptina_pll pll;
+ int ret;
+
+ pll.ext_clock = pdata->ext_clock;
+ pll.pix_clock = pdata->pix_clock;
+
+ ret = aptina_pll_calculate(&client->dev, &limits, &pll);
+ if (ret < 0)
+ return ret;
+
+ sensor->pix_clock = pdata->pix_clock;
+
+ ret = mt9m032_write(client, MT9M032_PLL_CONFIG1,
+ (pll.m << MT9M032_PLL_CONFIG1_MUL_SHIFT)
+ | (pll.p1 - 1));
+ if (!ret)
+ ret = mt9m032_write(client, MT9P031_PLL_CONFIG2, pll.n - 1);
+ if (!ret)
+ ret = mt9m032_write(client, MT9P031_PLL_CONTROL,
+ MT9P031_PLL_CONTROL_PWRON |
+ MT9P031_PLL_CONTROL_USEPLL);
+ if (!ret) /* more reserved, Continuous, Master Mode */
+ ret = mt9m032_write(client, MT9M032_READ_MODE1, 0x8006);
+ if (!ret) /* Set 14-bit mode, select 7 divider */
+ ret = mt9m032_write(client, MT9M032_FORMATTER1, 0x111e);
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Subdev pad operations
+ */
+
+static int mt9m032_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index != 0)
+ return -EINVAL;
+
+ code->code = V4L2_MBUS_FMT_Y8_1X8;
+ return 0;
+}
+
+static int mt9m032_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->index != 0 || fse->code != V4L2_MBUS_FMT_Y8_1X8)
+ return -EINVAL;
+
+ fse->min_width = MT9M032_COLUMN_SIZE_DEF;
+ fse->max_width = MT9M032_COLUMN_SIZE_DEF;
+ fse->min_height = MT9M032_ROW_SIZE_DEF;
+ fse->max_height = MT9M032_ROW_SIZE_DEF;
+
+ return 0;
+}
+
+/**
+ * __mt9m032_get_pad_crop() - get crop rect
+ * @sensor: pointer to the sensor struct
+ * @fh: file handle for getting the try crop rect from
+ * @which: select try or active crop rect
+ *
+ * Returns a pointer the current active or fh relative try crop rect
+ */
+static struct v4l2_rect *
+__mt9m032_get_pad_crop(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh,
+ enum v4l2_subdev_format_whence which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_crop(fh, 0);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &sensor->crop;
+ default:
+ return NULL;
+ }
+}
+
+/**
+ * __mt9m032_get_pad_format() - get format
+ * @sensor: pointer to the sensor struct
+ * @fh: file handle for getting the try format from
+ * @which: select try or active format
+ *
+ * Returns a pointer the current active or fh relative try format
+ */
+static struct v4l2_mbus_framefmt *
+__mt9m032_get_pad_format(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh,
+ enum v4l2_subdev_format_whence which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_format(fh, 0);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &sensor->format;
+ default:
+ return NULL;
+ }
+}
+
+static int mt9m032_get_pad_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct mt9m032 *sensor = to_mt9m032(subdev);
+
+ mutex_lock(&sensor->lock);
+ fmt->format = *__mt9m032_get_pad_format(sensor, fh, fmt->which);
+ mutex_unlock(&sensor->lock);
+
+ return 0;
+}
+
+static int mt9m032_set_pad_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct mt9m032 *sensor = to_mt9m032(subdev);
+ int ret;
+
+ mutex_lock(&sensor->lock);
+
+ if (sensor->streaming && fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ ret = -EBUSY;
+ goto done;
+ }
+
+ /* Scaling is not supported, the format is thus fixed. */
+ fmt->format = *__mt9m032_get_pad_format(sensor, fh, fmt->which);
+ ret = 0;
+
+done:
+ mutex_unlock(&sensor->lock);
+ return ret;
+}
+
+static int mt9m032_get_pad_crop(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct mt9m032 *sensor = to_mt9m032(subdev);
+
+ mutex_lock(&sensor->lock);
+ crop->rect = *__mt9m032_get_pad_crop(sensor, fh, crop->which);
+ mutex_unlock(&sensor->lock);
+
+ return 0;
+}
+
+static int mt9m032_set_pad_crop(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct mt9m032 *sensor = to_mt9m032(subdev);
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *__crop;
+ struct v4l2_rect rect;
+ int ret = 0;
+
+ mutex_lock(&sensor->lock);
+
+ if (sensor->streaming && crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ ret = -EBUSY;
+ goto done;
+ }
+
+ /* Clamp the crop rectangle boundaries and align them to a multiple of 2
+ * pixels to ensure a GRBG Bayer pattern.
+ */
+ rect.left = clamp(ALIGN(crop->rect.left, 2), MT9M032_COLUMN_START_MIN,
+ MT9M032_COLUMN_START_MAX);
+ rect.top = clamp(ALIGN(crop->rect.top, 2), MT9M032_ROW_START_MIN,
+ MT9M032_ROW_START_MAX);
+ rect.width = clamp(ALIGN(crop->rect.width, 2), MT9M032_COLUMN_SIZE_MIN,
+ MT9M032_COLUMN_SIZE_MAX);
+ rect.height = clamp(ALIGN(crop->rect.height, 2), MT9M032_ROW_SIZE_MIN,
+ MT9M032_ROW_SIZE_MAX);
+
+ rect.width = min(rect.width, MT9M032_PIXEL_ARRAY_WIDTH - rect.left);
+ rect.height = min(rect.height, MT9M032_PIXEL_ARRAY_HEIGHT - rect.top);
+
+ __crop = __mt9m032_get_pad_crop(sensor, fh, crop->which);
+
+ if (rect.width != __crop->width || rect.height != __crop->height) {
+ /* Reset the output image size if the crop rectangle size has
+ * been modified.
+ */
+ format = __mt9m032_get_pad_format(sensor, fh, crop->which);
+ format->width = rect.width;
+ format->height = rect.height;
+ }
+
+ *__crop = rect;
+ crop->rect = rect;
+
+ if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ ret = mt9m032_update_geom_timing(sensor);
+
+done:
+ mutex_unlock(&sensor->lock);
+ return ret;
+}
+
+static int mt9m032_get_frame_interval(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct mt9m032 *sensor = to_mt9m032(subdev);
+
+ mutex_lock(&sensor->lock);
+ memset(fi, 0, sizeof(*fi));
+ fi->interval = sensor->frame_interval;
+ mutex_unlock(&sensor->lock);
+
+ return 0;
+}
+
+static int mt9m032_set_frame_interval(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct mt9m032 *sensor = to_mt9m032(subdev);
+ int ret;
+
+ mutex_lock(&sensor->lock);
+
+ if (sensor->streaming) {
+ ret = -EBUSY;
+ goto done;
+ }
+
+ /* Avoid divisions by 0. */
+ if (fi->interval.denominator == 0)
+ fi->interval.denominator = 1;
+
+ ret = mt9m032_update_timing(sensor, &fi->interval);
+ if (!ret)
+ sensor->frame_interval = fi->interval;
+
+done:
+ mutex_unlock(&sensor->lock);
+ return ret;
+}
+
+static int mt9m032_s_stream(struct v4l2_subdev *subdev, int streaming)
+{
+ struct mt9m032 *sensor = to_mt9m032(subdev);
+ int ret;
+
+ mutex_lock(&sensor->lock);
+ ret = update_formatter2(sensor, streaming);
+ if (!ret)
+ sensor->streaming = streaming;
+ mutex_unlock(&sensor->lock);
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev core operations
+ */
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int mt9m032_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct mt9m032 *sensor = to_mt9m032(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+ int val;
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ return -EINVAL;
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ val = mt9m032_read(client, reg->reg);
+ if (val < 0)
+ return -EIO;
+
+ reg->size = 2;
+ reg->val = val;
+
+ return 0;
+}
+
+static int mt9m032_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct mt9m032 *sensor = to_mt9m032(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ return -EINVAL;
+
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ return mt9m032_write(client, reg->reg, reg->val);
+}
+#endif
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev control operations
+ */
+
+static int update_read_mode2(struct mt9m032 *sensor, bool vflip, bool hflip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+ int reg_val = (vflip << MT9M032_READ_MODE2_VFLIP_SHIFT)
+ | (hflip << MT9M032_READ_MODE2_HFLIP_SHIFT)
+ | MT9M032_READ_MODE2_ROW_BLC
+ | 0x0007;
+
+ return mt9m032_write(client, MT9M032_READ_MODE2, reg_val);
+}
+
+static int mt9m032_set_gain(struct mt9m032 *sensor, s32 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+ int digital_gain_val; /* in 1/8th (0..127) */
+ int analog_mul; /* 0 or 1 */
+ int analog_gain_val; /* in 1/16th. (0..63) */
+ u16 reg_val;
+
+ digital_gain_val = 51; /* from setup example */
+
+ if (val < 63) {
+ analog_mul = 0;
+ analog_gain_val = val;
+ } else {
+ analog_mul = 1;
+ analog_gain_val = val / 2;
+ }
+
+ /* a_gain = (1 + analog_mul) + (analog_gain_val + 1) / 16 */
+ /* overall_gain = a_gain * (1 + digital_gain_val / 8) */
+
+ reg_val = ((digital_gain_val & MT9M032_GAIN_DIGITAL_MASK)
+ << MT9M032_GAIN_DIGITAL_SHIFT)
+ | ((analog_mul & 1) << MT9M032_GAIN_AMUL_SHIFT)
+ | (analog_gain_val & MT9M032_GAIN_ANALOG_MASK);
+
+ return mt9m032_write(client, MT9M032_GAIN_ALL, reg_val);
+}
+
+static int mt9m032_try_ctrl(struct v4l2_ctrl *ctrl)
+{
+ if (ctrl->id == V4L2_CID_GAIN && ctrl->val >= 63) {
+ /* round because of multiplier used for values >= 63 */
+ ctrl->val &= ~1;
+ }
+
+ return 0;
+}
+
+static int mt9m032_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct mt9m032 *sensor =
+ container_of(ctrl->handler, struct mt9m032, ctrls);
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+ int ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ return mt9m032_set_gain(sensor, ctrl->val);
+
+ case V4L2_CID_HFLIP:
+ /* case V4L2_CID_VFLIP: -- In the same cluster */
+ return update_read_mode2(sensor, sensor->vflip->val,
+ sensor->hflip->val);
+
+ case V4L2_CID_EXPOSURE:
+ ret = mt9m032_write(client, MT9M032_SHUTTER_WIDTH_HIGH,
+ (ctrl->val >> 16) & 0xffff);
+ if (ret < 0)
+ return ret;
+
+ return mt9m032_write(client, MT9M032_SHUTTER_WIDTH_LOW,
+ ctrl->val & 0xffff);
+ }
+
+ return 0;
+}
+
+static struct v4l2_ctrl_ops mt9m032_ctrl_ops = {
+ .s_ctrl = mt9m032_set_ctrl,
+ .try_ctrl = mt9m032_try_ctrl,
+};
+
+/* -------------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops mt9m032_core_ops = {
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = mt9m032_g_register,
+ .s_register = mt9m032_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops mt9m032_video_ops = {
+ .s_stream = mt9m032_s_stream,
+ .g_frame_interval = mt9m032_get_frame_interval,
+ .s_frame_interval = mt9m032_set_frame_interval,
+};
+
+static const struct v4l2_subdev_pad_ops mt9m032_pad_ops = {
+ .enum_mbus_code = mt9m032_enum_mbus_code,
+ .enum_frame_size = mt9m032_enum_frame_size,
+ .get_fmt = mt9m032_get_pad_format,
+ .set_fmt = mt9m032_set_pad_format,
+ .set_crop = mt9m032_set_pad_crop,
+ .get_crop = mt9m032_get_pad_crop,
+};
+
+static const struct v4l2_subdev_ops mt9m032_ops = {
+ .core = &mt9m032_core_ops,
+ .video = &mt9m032_video_ops,
+ .pad = &mt9m032_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * Driver initialization and probing
+ */
+
+static int mt9m032_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ struct mt9m032 *sensor;
+ int chip_version;
+ int ret;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_warn(&client->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
+ return -EIO;
+ }
+
+ if (!client->dev.platform_data)
+ return -ENODEV;
+
+ sensor = kzalloc(sizeof(*sensor), GFP_KERNEL);
+ if (sensor == NULL)
+ return -ENOMEM;
+
+ mutex_init(&sensor->lock);
+
+ sensor->pdata = client->dev.platform_data;
+
+ v4l2_i2c_subdev_init(&sensor->subdev, client, &mt9m032_ops);
+ sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ chip_version = mt9m032_read(client, MT9M032_CHIP_VERSION);
+ if (chip_version != MT9M032_CHIP_VERSION_VALUE) {
+ dev_err(&client->dev, "MT9M032 not detected, wrong version "
+ "0x%04x\n", chip_version);
+ ret = -ENODEV;
+ goto error_sensor;
+ }
+
+ dev_info(&client->dev, "MT9M032 detected at address 0x%02x\n",
+ client->addr);
+
+ sensor->frame_interval.numerator = 1;
+ sensor->frame_interval.denominator = 30;
+
+ sensor->crop.left = MT9M032_COLUMN_START_DEF;
+ sensor->crop.top = MT9M032_ROW_START_DEF;
+ sensor->crop.width = MT9M032_COLUMN_SIZE_DEF;
+ sensor->crop.height = MT9M032_ROW_SIZE_DEF;
+
+ sensor->format.width = sensor->crop.width;
+ sensor->format.height = sensor->crop.height;
+ sensor->format.code = V4L2_MBUS_FMT_Y8_1X8;
+ sensor->format.field = V4L2_FIELD_NONE;
+ sensor->format.colorspace = V4L2_COLORSPACE_SRGB;
+
+ v4l2_ctrl_handler_init(&sensor->ctrls, 4);
+
+ v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops,
+ V4L2_CID_GAIN, 0, 127, 1, 64);
+
+ sensor->hflip = v4l2_ctrl_new_std(&sensor->ctrls,
+ &mt9m032_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ sensor->vflip = v4l2_ctrl_new_std(&sensor->ctrls,
+ &mt9m032_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+ v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops,
+ V4L2_CID_EXPOSURE, MT9M032_SHUTTER_WIDTH_MIN,
+ MT9M032_SHUTTER_WIDTH_MAX, 1,
+ MT9M032_SHUTTER_WIDTH_DEF);
+
+ if (sensor->ctrls.error) {
+ ret = sensor->ctrls.error;
+ dev_err(&client->dev, "control initialization error %d\n", ret);
+ goto error_ctrl;
+ }
+
+ v4l2_ctrl_cluster(2, &sensor->hflip);
+
+ sensor->subdev.ctrl_handler = &sensor->ctrls;
+ sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&sensor->subdev.entity, 1, &sensor->pad, 0);
+ if (ret < 0)
+ goto error_ctrl;
+
+ ret = mt9m032_write(client, MT9M032_RESET, 1); /* reset on */
+ if (ret < 0)
+ goto error_entity;
+ mt9m032_write(client, MT9M032_RESET, 0); /* reset off */
+ if (ret < 0)
+ goto error_entity;
+
+ ret = mt9m032_setup_pll(sensor);
+ if (ret < 0)
+ goto error_entity;
+ usleep_range(10000, 11000);
+
+ ret = v4l2_ctrl_handler_setup(&sensor->ctrls);
+ if (ret < 0)
+ goto error_entity;
+
+ /* SIZE */
+ ret = mt9m032_update_geom_timing(sensor);
+ if (ret < 0)
+ goto error_entity;
+
+ ret = mt9m032_write(client, 0x41, 0x0000); /* reserved !!! */
+ if (ret < 0)
+ goto error_entity;
+ ret = mt9m032_write(client, 0x42, 0x0003); /* reserved !!! */
+ if (ret < 0)
+ goto error_entity;
+ ret = mt9m032_write(client, 0x43, 0x0003); /* reserved !!! */
+ if (ret < 0)
+ goto error_entity;
+ ret = mt9m032_write(client, 0x7f, 0x0000); /* reserved !!! */
+ if (ret < 0)
+ goto error_entity;
+ if (sensor->pdata->invert_pixclock) {
+ ret = mt9m032_write(client, MT9M032_PIX_CLK_CTRL,
+ MT9M032_PIX_CLK_CTRL_INV_PIXCLK);
+ if (ret < 0)
+ goto error_entity;
+ }
+
+ ret = mt9m032_write(client, MT9M032_RESTART, 1); /* Restart on */
+ if (ret < 0)
+ goto error_entity;
+ msleep(100);
+ ret = mt9m032_write(client, MT9M032_RESTART, 0); /* Restart off */
+ if (ret < 0)
+ goto error_entity;
+ msleep(100);
+ ret = update_formatter2(sensor, false);
+ if (ret < 0)
+ goto error_entity;
+
+ return ret;
+
+error_entity:
+ media_entity_cleanup(&sensor->subdev.entity);
+error_ctrl:
+ v4l2_ctrl_handler_free(&sensor->ctrls);
+error_sensor:
+ mutex_destroy(&sensor->lock);
+ kfree(sensor);
+ return ret;
+}
+
+static int mt9m032_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct mt9m032 *sensor = to_mt9m032(subdev);
+
+ v4l2_device_unregister_subdev(&sensor->subdev);
+ v4l2_ctrl_handler_free(&sensor->ctrls);
+ media_entity_cleanup(&sensor->subdev.entity);
+ mutex_destroy(&sensor->lock);
+ kfree(sensor);
+ return 0;
+}
+
+static const struct i2c_device_id mt9m032_id_table[] = {
+ { MT9M032_NAME, 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, mt9m032_id_table);
+
+static struct i2c_driver mt9m032_i2c_driver = {
+ .driver = {
+ .name = MT9M032_NAME,
+ },
+ .probe = mt9m032_probe,
+ .remove = mt9m032_remove,
+ .id_table = mt9m032_id_table,
+};
+
+module_i2c_driver(mt9m032_i2c_driver);
+
+MODULE_AUTHOR("Martin Hostettler <martin@neutronstar.dyndns.org>");
+MODULE_DESCRIPTION("MT9M032 camera sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c
new file mode 100644
index 00000000..b0c52996
--- /dev/null
+++ b/drivers/media/video/mt9m111.c
@@ -0,0 +1,1015 @@
+/*
+ * Driver for MT9M111/MT9M112/MT9M131 CMOS Image Sensor from Micron/Aptina
+ *
+ * Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/log2.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/module.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-chip-ident.h>
+
+/*
+ * MT9M111, MT9M112 and MT9M131:
+ * i2c address is 0x48 or 0x5d (depending on SADDR pin)
+ * The platform has to define i2c_board_info and call i2c_register_board_info()
+ */
+
+/*
+ * Sensor core register addresses (0x000..0x0ff)
+ */
+#define MT9M111_CHIP_VERSION 0x000
+#define MT9M111_ROW_START 0x001
+#define MT9M111_COLUMN_START 0x002
+#define MT9M111_WINDOW_HEIGHT 0x003
+#define MT9M111_WINDOW_WIDTH 0x004
+#define MT9M111_HORIZONTAL_BLANKING_B 0x005
+#define MT9M111_VERTICAL_BLANKING_B 0x006
+#define MT9M111_HORIZONTAL_BLANKING_A 0x007
+#define MT9M111_VERTICAL_BLANKING_A 0x008
+#define MT9M111_SHUTTER_WIDTH 0x009
+#define MT9M111_ROW_SPEED 0x00a
+#define MT9M111_EXTRA_DELAY 0x00b
+#define MT9M111_SHUTTER_DELAY 0x00c
+#define MT9M111_RESET 0x00d
+#define MT9M111_READ_MODE_B 0x020
+#define MT9M111_READ_MODE_A 0x021
+#define MT9M111_FLASH_CONTROL 0x023
+#define MT9M111_GREEN1_GAIN 0x02b
+#define MT9M111_BLUE_GAIN 0x02c
+#define MT9M111_RED_GAIN 0x02d
+#define MT9M111_GREEN2_GAIN 0x02e
+#define MT9M111_GLOBAL_GAIN 0x02f
+#define MT9M111_CONTEXT_CONTROL 0x0c8
+#define MT9M111_PAGE_MAP 0x0f0
+#define MT9M111_BYTE_WISE_ADDR 0x0f1
+
+#define MT9M111_RESET_SYNC_CHANGES (1 << 15)
+#define MT9M111_RESET_RESTART_BAD_FRAME (1 << 9)
+#define MT9M111_RESET_SHOW_BAD_FRAMES (1 << 8)
+#define MT9M111_RESET_RESET_SOC (1 << 5)
+#define MT9M111_RESET_OUTPUT_DISABLE (1 << 4)
+#define MT9M111_RESET_CHIP_ENABLE (1 << 3)
+#define MT9M111_RESET_ANALOG_STANDBY (1 << 2)
+#define MT9M111_RESET_RESTART_FRAME (1 << 1)
+#define MT9M111_RESET_RESET_MODE (1 << 0)
+
+#define MT9M111_RM_FULL_POWER_RD (0 << 10)
+#define MT9M111_RM_LOW_POWER_RD (1 << 10)
+#define MT9M111_RM_COL_SKIP_4X (1 << 5)
+#define MT9M111_RM_ROW_SKIP_4X (1 << 4)
+#define MT9M111_RM_COL_SKIP_2X (1 << 3)
+#define MT9M111_RM_ROW_SKIP_2X (1 << 2)
+#define MT9M111_RMB_MIRROR_COLS (1 << 1)
+#define MT9M111_RMB_MIRROR_ROWS (1 << 0)
+#define MT9M111_CTXT_CTRL_RESTART (1 << 15)
+#define MT9M111_CTXT_CTRL_DEFECTCOR_B (1 << 12)
+#define MT9M111_CTXT_CTRL_RESIZE_B (1 << 10)
+#define MT9M111_CTXT_CTRL_CTRL2_B (1 << 9)
+#define MT9M111_CTXT_CTRL_GAMMA_B (1 << 8)
+#define MT9M111_CTXT_CTRL_XENON_EN (1 << 7)
+#define MT9M111_CTXT_CTRL_READ_MODE_B (1 << 3)
+#define MT9M111_CTXT_CTRL_LED_FLASH_EN (1 << 2)
+#define MT9M111_CTXT_CTRL_VBLANK_SEL_B (1 << 1)
+#define MT9M111_CTXT_CTRL_HBLANK_SEL_B (1 << 0)
+
+/*
+ * Colorpipe register addresses (0x100..0x1ff)
+ */
+#define MT9M111_OPER_MODE_CTRL 0x106
+#define MT9M111_OUTPUT_FORMAT_CTRL 0x108
+#define MT9M111_REDUCER_XZOOM_B 0x1a0
+#define MT9M111_REDUCER_XSIZE_B 0x1a1
+#define MT9M111_REDUCER_YZOOM_B 0x1a3
+#define MT9M111_REDUCER_YSIZE_B 0x1a4
+#define MT9M111_REDUCER_XZOOM_A 0x1a6
+#define MT9M111_REDUCER_XSIZE_A 0x1a7
+#define MT9M111_REDUCER_YZOOM_A 0x1a9
+#define MT9M111_REDUCER_YSIZE_A 0x1aa
+
+#define MT9M111_OUTPUT_FORMAT_CTRL2_A 0x13a
+#define MT9M111_OUTPUT_FORMAT_CTRL2_B 0x19b
+
+#define MT9M111_OPMODE_AUTOEXPO_EN (1 << 14)
+#define MT9M111_OPMODE_AUTOWHITEBAL_EN (1 << 1)
+#define MT9M111_OUTFMT_FLIP_BAYER_COL (1 << 9)
+#define MT9M111_OUTFMT_FLIP_BAYER_ROW (1 << 8)
+#define MT9M111_OUTFMT_PROCESSED_BAYER (1 << 14)
+#define MT9M111_OUTFMT_BYPASS_IFP (1 << 10)
+#define MT9M111_OUTFMT_INV_PIX_CLOCK (1 << 9)
+#define MT9M111_OUTFMT_RGB (1 << 8)
+#define MT9M111_OUTFMT_RGB565 (0 << 6)
+#define MT9M111_OUTFMT_RGB555 (1 << 6)
+#define MT9M111_OUTFMT_RGB444x (2 << 6)
+#define MT9M111_OUTFMT_RGBx444 (3 << 6)
+#define MT9M111_OUTFMT_TST_RAMP_OFF (0 << 4)
+#define MT9M111_OUTFMT_TST_RAMP_COL (1 << 4)
+#define MT9M111_OUTFMT_TST_RAMP_ROW (2 << 4)
+#define MT9M111_OUTFMT_TST_RAMP_FRAME (3 << 4)
+#define MT9M111_OUTFMT_SHIFT_3_UP (1 << 3)
+#define MT9M111_OUTFMT_AVG_CHROMA (1 << 2)
+#define MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN (1 << 1)
+#define MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B (1 << 0)
+
+/*
+ * Camera control register addresses (0x200..0x2ff not implemented)
+ */
+
+#define reg_read(reg) mt9m111_reg_read(client, MT9M111_##reg)
+#define reg_write(reg, val) mt9m111_reg_write(client, MT9M111_##reg, (val))
+#define reg_set(reg, val) mt9m111_reg_set(client, MT9M111_##reg, (val))
+#define reg_clear(reg, val) mt9m111_reg_clear(client, MT9M111_##reg, (val))
+#define reg_mask(reg, val, mask) mt9m111_reg_mask(client, MT9M111_##reg, \
+ (val), (mask))
+
+#define MT9M111_MIN_DARK_ROWS 8
+#define MT9M111_MIN_DARK_COLS 26
+#define MT9M111_MAX_HEIGHT 1024
+#define MT9M111_MAX_WIDTH 1280
+
+struct mt9m111_context {
+ u16 read_mode;
+ u16 blanking_h;
+ u16 blanking_v;
+ u16 reducer_xzoom;
+ u16 reducer_yzoom;
+ u16 reducer_xsize;
+ u16 reducer_ysize;
+ u16 output_fmt_ctrl2;
+ u16 control;
+};
+
+static struct mt9m111_context context_a = {
+ .read_mode = MT9M111_READ_MODE_A,
+ .blanking_h = MT9M111_HORIZONTAL_BLANKING_A,
+ .blanking_v = MT9M111_VERTICAL_BLANKING_A,
+ .reducer_xzoom = MT9M111_REDUCER_XZOOM_A,
+ .reducer_yzoom = MT9M111_REDUCER_YZOOM_A,
+ .reducer_xsize = MT9M111_REDUCER_XSIZE_A,
+ .reducer_ysize = MT9M111_REDUCER_YSIZE_A,
+ .output_fmt_ctrl2 = MT9M111_OUTPUT_FORMAT_CTRL2_A,
+ .control = MT9M111_CTXT_CTRL_RESTART,
+};
+
+static struct mt9m111_context context_b = {
+ .read_mode = MT9M111_READ_MODE_B,
+ .blanking_h = MT9M111_HORIZONTAL_BLANKING_B,
+ .blanking_v = MT9M111_VERTICAL_BLANKING_B,
+ .reducer_xzoom = MT9M111_REDUCER_XZOOM_B,
+ .reducer_yzoom = MT9M111_REDUCER_YZOOM_B,
+ .reducer_xsize = MT9M111_REDUCER_XSIZE_B,
+ .reducer_ysize = MT9M111_REDUCER_YSIZE_B,
+ .output_fmt_ctrl2 = MT9M111_OUTPUT_FORMAT_CTRL2_B,
+ .control = MT9M111_CTXT_CTRL_RESTART |
+ MT9M111_CTXT_CTRL_DEFECTCOR_B | MT9M111_CTXT_CTRL_RESIZE_B |
+ MT9M111_CTXT_CTRL_CTRL2_B | MT9M111_CTXT_CTRL_GAMMA_B |
+ MT9M111_CTXT_CTRL_READ_MODE_B | MT9M111_CTXT_CTRL_VBLANK_SEL_B |
+ MT9M111_CTXT_CTRL_HBLANK_SEL_B,
+};
+
+/* MT9M111 has only one fixed colorspace per pixelcode */
+struct mt9m111_datafmt {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+};
+
+static const struct mt9m111_datafmt mt9m111_colour_fmts[] = {
+ {V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG},
+ {V4L2_MBUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG},
+ {V4L2_MBUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG},
+ {V4L2_MBUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_JPEG},
+ {V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_BGR565_2X8_LE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_BGR565_2X8_BE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB},
+};
+
+struct mt9m111 {
+ struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
+ struct v4l2_ctrl *gain;
+ int model; /* V4L2_IDENT_MT9M111 or V4L2_IDENT_MT9M112 code
+ * from v4l2-chip-ident.h */
+ struct mt9m111_context *ctx;
+ struct v4l2_rect rect; /* cropping rectangle */
+ int width; /* output */
+ int height; /* sizes */
+ struct mutex power_lock; /* lock to protect power_count */
+ int power_count;
+ const struct mt9m111_datafmt *fmt;
+ int lastpage; /* PageMap cache value */
+ unsigned char datawidth;
+};
+
+/* Find a data format by a pixel code */
+static const struct mt9m111_datafmt *mt9m111_find_datafmt(struct mt9m111 *mt9m111,
+ enum v4l2_mbus_pixelcode code)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(mt9m111_colour_fmts); i++)
+ if (mt9m111_colour_fmts[i].code == code)
+ return mt9m111_colour_fmts + i;
+
+ return mt9m111->fmt;
+}
+
+static struct mt9m111 *to_mt9m111(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct mt9m111, subdev);
+}
+
+static int reg_page_map_set(struct i2c_client *client, const u16 reg)
+{
+ int ret;
+ u16 page;
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
+
+ page = (reg >> 8);
+ if (page == mt9m111->lastpage)
+ return 0;
+ if (page > 2)
+ return -EINVAL;
+
+ ret = i2c_smbus_write_word_swapped(client, MT9M111_PAGE_MAP, page);
+ if (!ret)
+ mt9m111->lastpage = page;
+ return ret;
+}
+
+static int mt9m111_reg_read(struct i2c_client *client, const u16 reg)
+{
+ int ret;
+
+ ret = reg_page_map_set(client, reg);
+ if (!ret)
+ ret = i2c_smbus_read_word_swapped(client, reg & 0xff);
+
+ dev_dbg(&client->dev, "read reg.%03x -> %04x\n", reg, ret);
+ return ret;
+}
+
+static int mt9m111_reg_write(struct i2c_client *client, const u16 reg,
+ const u16 data)
+{
+ int ret;
+
+ ret = reg_page_map_set(client, reg);
+ if (!ret)
+ ret = i2c_smbus_write_word_swapped(client, reg & 0xff, data);
+ dev_dbg(&client->dev, "write reg.%03x = %04x -> %d\n", reg, data, ret);
+ return ret;
+}
+
+static int mt9m111_reg_set(struct i2c_client *client, const u16 reg,
+ const u16 data)
+{
+ int ret;
+
+ ret = mt9m111_reg_read(client, reg);
+ if (ret >= 0)
+ ret = mt9m111_reg_write(client, reg, ret | data);
+ return ret;
+}
+
+static int mt9m111_reg_clear(struct i2c_client *client, const u16 reg,
+ const u16 data)
+{
+ int ret;
+
+ ret = mt9m111_reg_read(client, reg);
+ if (ret >= 0)
+ ret = mt9m111_reg_write(client, reg, ret & ~data);
+ return ret;
+}
+
+static int mt9m111_reg_mask(struct i2c_client *client, const u16 reg,
+ const u16 data, const u16 mask)
+{
+ int ret;
+
+ ret = mt9m111_reg_read(client, reg);
+ if (ret >= 0)
+ ret = mt9m111_reg_write(client, reg, (ret & ~mask) | data);
+ return ret;
+}
+
+static int mt9m111_set_context(struct mt9m111 *mt9m111,
+ struct mt9m111_context *ctx)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+ return reg_write(CONTEXT_CONTROL, ctx->control);
+}
+
+static int mt9m111_setup_rect_ctx(struct mt9m111 *mt9m111,
+ struct mt9m111_context *ctx, struct v4l2_rect *rect,
+ unsigned int width, unsigned int height)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+ int ret = mt9m111_reg_write(client, ctx->reducer_xzoom, rect->width);
+ if (!ret)
+ ret = mt9m111_reg_write(client, ctx->reducer_yzoom, rect->height);
+ if (!ret)
+ ret = mt9m111_reg_write(client, ctx->reducer_xsize, width);
+ if (!ret)
+ ret = mt9m111_reg_write(client, ctx->reducer_ysize, height);
+ return ret;
+}
+
+static int mt9m111_setup_geometry(struct mt9m111 *mt9m111, struct v4l2_rect *rect,
+ int width, int height, enum v4l2_mbus_pixelcode code)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+ int ret;
+
+ ret = reg_write(COLUMN_START, rect->left);
+ if (!ret)
+ ret = reg_write(ROW_START, rect->top);
+
+ if (!ret)
+ ret = reg_write(WINDOW_WIDTH, rect->width);
+ if (!ret)
+ ret = reg_write(WINDOW_HEIGHT, rect->height);
+
+ if (code != V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE) {
+ /* IFP in use, down-scaling possible */
+ if (!ret)
+ ret = mt9m111_setup_rect_ctx(mt9m111, &context_b,
+ rect, width, height);
+ if (!ret)
+ ret = mt9m111_setup_rect_ctx(mt9m111, &context_a,
+ rect, width, height);
+ }
+
+ dev_dbg(&client->dev, "%s(%x): %ux%u@%u:%u -> %ux%u = %d\n",
+ __func__, code, rect->width, rect->height, rect->left, rect->top,
+ width, height, ret);
+
+ return ret;
+}
+
+static int mt9m111_enable(struct mt9m111 *mt9m111)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+ return reg_write(RESET, MT9M111_RESET_CHIP_ENABLE);
+}
+
+static int mt9m111_reset(struct mt9m111 *mt9m111)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+ int ret;
+
+ ret = reg_set(RESET, MT9M111_RESET_RESET_MODE);
+ if (!ret)
+ ret = reg_set(RESET, MT9M111_RESET_RESET_SOC);
+ if (!ret)
+ ret = reg_clear(RESET, MT9M111_RESET_RESET_MODE
+ | MT9M111_RESET_RESET_SOC);
+
+ return ret;
+}
+
+static int mt9m111_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct v4l2_rect rect = a->c;
+ struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
+ int width, height;
+ int ret;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (mt9m111->fmt->code == V4L2_MBUS_FMT_SBGGR8_1X8 ||
+ mt9m111->fmt->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE) {
+ /* Bayer format - even size lengths */
+ rect.width = ALIGN(rect.width, 2);
+ rect.height = ALIGN(rect.height, 2);
+ /* Let the user play with the starting pixel */
+ }
+
+ /* FIXME: the datasheet doesn't specify minimum sizes */
+ soc_camera_limit_side(&rect.left, &rect.width,
+ MT9M111_MIN_DARK_COLS, 2, MT9M111_MAX_WIDTH);
+
+ soc_camera_limit_side(&rect.top, &rect.height,
+ MT9M111_MIN_DARK_ROWS, 2, MT9M111_MAX_HEIGHT);
+
+ width = min(mt9m111->width, rect.width);
+ height = min(mt9m111->height, rect.height);
+
+ ret = mt9m111_setup_geometry(mt9m111, &rect, width, height, mt9m111->fmt->code);
+ if (!ret) {
+ mt9m111->rect = rect;
+ mt9m111->width = width;
+ mt9m111->height = height;
+ }
+
+ return ret;
+}
+
+static int mt9m111_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
+
+ a->c = mt9m111->rect;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int mt9m111_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ a->bounds.left = MT9M111_MIN_DARK_COLS;
+ a->bounds.top = MT9M111_MIN_DARK_ROWS;
+ a->bounds.width = MT9M111_MAX_WIDTH;
+ a->bounds.height = MT9M111_MAX_HEIGHT;
+ a->defrect = a->bounds;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int mt9m111_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
+
+ mf->width = mt9m111->width;
+ mf->height = mt9m111->height;
+ mf->code = mt9m111->fmt->code;
+ mf->colorspace = mt9m111->fmt->colorspace;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int mt9m111_set_pixfmt(struct mt9m111 *mt9m111,
+ enum v4l2_mbus_pixelcode code)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+ u16 data_outfmt2, mask_outfmt2 = MT9M111_OUTFMT_PROCESSED_BAYER |
+ MT9M111_OUTFMT_BYPASS_IFP | MT9M111_OUTFMT_RGB |
+ MT9M111_OUTFMT_RGB565 | MT9M111_OUTFMT_RGB555 |
+ MT9M111_OUTFMT_RGB444x | MT9M111_OUTFMT_RGBx444 |
+ MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN |
+ MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
+ int ret;
+
+ switch (code) {
+ case V4L2_MBUS_FMT_SBGGR8_1X8:
+ data_outfmt2 = MT9M111_OUTFMT_PROCESSED_BAYER |
+ MT9M111_OUTFMT_RGB;
+ break;
+ case V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE:
+ data_outfmt2 = MT9M111_OUTFMT_BYPASS_IFP | MT9M111_OUTFMT_RGB;
+ break;
+ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
+ data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555 |
+ MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN;
+ break;
+ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE:
+ data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555;
+ break;
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 |
+ MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN;
+ break;
+ case V4L2_MBUS_FMT_RGB565_2X8_BE:
+ data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565;
+ break;
+ case V4L2_MBUS_FMT_BGR565_2X8_BE:
+ data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 |
+ MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
+ break;
+ case V4L2_MBUS_FMT_BGR565_2X8_LE:
+ data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 |
+ MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN |
+ MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
+ break;
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ data_outfmt2 = 0;
+ break;
+ case V4L2_MBUS_FMT_VYUY8_2X8:
+ data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
+ break;
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN;
+ break;
+ case V4L2_MBUS_FMT_YVYU8_2X8:
+ data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN |
+ MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
+ break;
+ default:
+ dev_err(&client->dev, "Pixel format not handled: %x\n", code);
+ return -EINVAL;
+ }
+
+ ret = mt9m111_reg_mask(client, context_a.output_fmt_ctrl2,
+ data_outfmt2, mask_outfmt2);
+ if (!ret)
+ ret = mt9m111_reg_mask(client, context_b.output_fmt_ctrl2,
+ data_outfmt2, mask_outfmt2);
+
+ return ret;
+}
+
+static int mt9m111_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
+ const struct mt9m111_datafmt *fmt;
+ struct v4l2_rect *rect = &mt9m111->rect;
+ bool bayer;
+
+ fmt = mt9m111_find_datafmt(mt9m111, mf->code);
+
+ bayer = fmt->code == V4L2_MBUS_FMT_SBGGR8_1X8 ||
+ fmt->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE;
+
+ /*
+ * With Bayer format enforce even side lengths, but let the user play
+ * with the starting pixel
+ */
+ if (bayer) {
+ rect->width = ALIGN(rect->width, 2);
+ rect->height = ALIGN(rect->height, 2);
+ }
+
+ if (fmt->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE) {
+ /* IFP bypass mode, no scaling */
+ mf->width = rect->width;
+ mf->height = rect->height;
+ } else {
+ /* No upscaling */
+ if (mf->width > rect->width)
+ mf->width = rect->width;
+ if (mf->height > rect->height)
+ mf->height = rect->height;
+ }
+
+ dev_dbg(&client->dev, "%s(): %ux%u, code=%x\n", __func__,
+ mf->width, mf->height, fmt->code);
+
+ mf->code = fmt->code;
+ mf->colorspace = fmt->colorspace;
+
+ return 0;
+}
+
+static int mt9m111_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct mt9m111_datafmt *fmt;
+ struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
+ struct v4l2_rect *rect = &mt9m111->rect;
+ int ret;
+
+ mt9m111_try_fmt(sd, mf);
+ fmt = mt9m111_find_datafmt(mt9m111, mf->code);
+ /* try_fmt() guarantees fmt != NULL && fmt->code == mf->code */
+
+ ret = mt9m111_setup_geometry(mt9m111, rect, mf->width, mf->height, mf->code);
+ if (!ret)
+ ret = mt9m111_set_pixfmt(mt9m111, mf->code);
+ if (!ret) {
+ mt9m111->width = mf->width;
+ mt9m111->height = mf->height;
+ mt9m111->fmt = fmt;
+ }
+
+ return ret;
+}
+
+static int mt9m111_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
+
+ if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+
+ if (id->match.addr != client->addr)
+ return -ENODEV;
+
+ id->ident = mt9m111->model;
+ id->revision = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int mt9m111_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int val;
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff)
+ return -EINVAL;
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ val = mt9m111_reg_read(client, reg->reg);
+ reg->size = 2;
+ reg->val = (u64)val;
+
+ if (reg->val > 0xffff)
+ return -EIO;
+
+ return 0;
+}
+
+static int mt9m111_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff)
+ return -EINVAL;
+
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ if (mt9m111_reg_write(client, reg->reg, reg->val) < 0)
+ return -EIO;
+
+ return 0;
+}
+#endif
+
+static int mt9m111_set_flip(struct mt9m111 *mt9m111, int flip, int mask)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+ int ret;
+
+ if (flip)
+ ret = mt9m111_reg_set(client, mt9m111->ctx->read_mode, mask);
+ else
+ ret = mt9m111_reg_clear(client, mt9m111->ctx->read_mode, mask);
+
+ return ret;
+}
+
+static int mt9m111_get_global_gain(struct mt9m111 *mt9m111)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+ int data;
+
+ data = reg_read(GLOBAL_GAIN);
+ if (data >= 0)
+ return (data & 0x2f) * (1 << ((data >> 10) & 1)) *
+ (1 << ((data >> 9) & 1));
+ return data;
+}
+
+static int mt9m111_set_global_gain(struct mt9m111 *mt9m111, int gain)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+ u16 val;
+
+ if (gain > 63 * 2 * 2)
+ return -EINVAL;
+
+ if ((gain >= 64 * 2) && (gain < 63 * 2 * 2))
+ val = (1 << 10) | (1 << 9) | (gain / 4);
+ else if ((gain >= 64) && (gain < 64 * 2))
+ val = (1 << 9) | (gain / 2);
+ else
+ val = gain;
+
+ return reg_write(GLOBAL_GAIN, val);
+}
+
+static int mt9m111_set_autoexposure(struct mt9m111 *mt9m111, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+
+ if (on)
+ return reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
+ return reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
+}
+
+static int mt9m111_set_autowhitebalance(struct mt9m111 *mt9m111, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+
+ if (on)
+ return reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
+ return reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
+}
+
+static int mt9m111_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct mt9m111 *mt9m111 = container_of(ctrl->handler,
+ struct mt9m111, hdl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ return mt9m111_set_flip(mt9m111, ctrl->val,
+ MT9M111_RMB_MIRROR_ROWS);
+ case V4L2_CID_HFLIP:
+ return mt9m111_set_flip(mt9m111, ctrl->val,
+ MT9M111_RMB_MIRROR_COLS);
+ case V4L2_CID_GAIN:
+ return mt9m111_set_global_gain(mt9m111, ctrl->val);
+ case V4L2_CID_EXPOSURE_AUTO:
+ return mt9m111_set_autoexposure(mt9m111, ctrl->val);
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ return mt9m111_set_autowhitebalance(mt9m111, ctrl->val);
+ }
+
+ return -EINVAL;
+}
+
+static int mt9m111_suspend(struct mt9m111 *mt9m111)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+ int ret;
+
+ v4l2_ctrl_s_ctrl(mt9m111->gain, mt9m111_get_global_gain(mt9m111));
+
+ ret = reg_set(RESET, MT9M111_RESET_RESET_MODE);
+ if (!ret)
+ ret = reg_set(RESET, MT9M111_RESET_RESET_SOC |
+ MT9M111_RESET_OUTPUT_DISABLE |
+ MT9M111_RESET_ANALOG_STANDBY);
+ if (!ret)
+ ret = reg_clear(RESET, MT9M111_RESET_CHIP_ENABLE);
+
+ return ret;
+}
+
+static void mt9m111_restore_state(struct mt9m111 *mt9m111)
+{
+ mt9m111_set_context(mt9m111, mt9m111->ctx);
+ mt9m111_set_pixfmt(mt9m111, mt9m111->fmt->code);
+ mt9m111_setup_geometry(mt9m111, &mt9m111->rect,
+ mt9m111->width, mt9m111->height, mt9m111->fmt->code);
+ v4l2_ctrl_handler_setup(&mt9m111->hdl);
+}
+
+static int mt9m111_resume(struct mt9m111 *mt9m111)
+{
+ int ret = mt9m111_enable(mt9m111);
+ if (!ret)
+ ret = mt9m111_reset(mt9m111);
+ if (!ret)
+ mt9m111_restore_state(mt9m111);
+
+ return ret;
+}
+
+static int mt9m111_init(struct mt9m111 *mt9m111)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+ int ret;
+
+ /* Default HIGHPOWER context */
+ mt9m111->ctx = &context_b;
+ ret = mt9m111_enable(mt9m111);
+ if (!ret)
+ ret = mt9m111_reset(mt9m111);
+ if (!ret)
+ ret = mt9m111_set_context(mt9m111, mt9m111->ctx);
+ if (ret)
+ dev_err(&client->dev, "mt9m111 init failed: %d\n", ret);
+ return ret;
+}
+
+/*
+ * Interface active, can use i2c. If it fails, it can indeed mean, that
+ * this wasn't our capture interface, so, we wait for the right one
+ */
+static int mt9m111_video_probe(struct i2c_client *client)
+{
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
+ s32 data;
+ int ret;
+
+ data = reg_read(CHIP_VERSION);
+
+ switch (data) {
+ case 0x143a: /* MT9M111 or MT9M131 */
+ mt9m111->model = V4L2_IDENT_MT9M111;
+ dev_info(&client->dev,
+ "Detected a MT9M111/MT9M131 chip ID %x\n", data);
+ break;
+ case 0x148c: /* MT9M112 */
+ mt9m111->model = V4L2_IDENT_MT9M112;
+ dev_info(&client->dev, "Detected a MT9M112 chip ID %x\n", data);
+ break;
+ default:
+ dev_err(&client->dev,
+ "No MT9M111/MT9M112/MT9M131 chip detected register read %x\n",
+ data);
+ return -ENODEV;
+ }
+
+ ret = mt9m111_init(mt9m111);
+ if (ret)
+ return ret;
+ return v4l2_ctrl_handler_setup(&mt9m111->hdl);
+}
+
+static int mt9m111_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret = 0;
+
+ mutex_lock(&mt9m111->power_lock);
+
+ /*
+ * If the power count is modified from 0 to != 0 or from != 0 to 0,
+ * update the power state.
+ */
+ if (mt9m111->power_count == !on) {
+ if (on) {
+ ret = mt9m111_resume(mt9m111);
+ if (ret) {
+ dev_err(&client->dev,
+ "Failed to resume the sensor: %d\n", ret);
+ goto out;
+ }
+ } else {
+ mt9m111_suspend(mt9m111);
+ }
+ }
+
+ /* Update the power count. */
+ mt9m111->power_count += on ? 1 : -1;
+ WARN_ON(mt9m111->power_count < 0);
+
+out:
+ mutex_unlock(&mt9m111->power_lock);
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops mt9m111_ctrl_ops = {
+ .s_ctrl = mt9m111_s_ctrl,
+};
+
+static struct v4l2_subdev_core_ops mt9m111_subdev_core_ops = {
+ .g_chip_ident = mt9m111_g_chip_ident,
+ .s_power = mt9m111_s_power,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = mt9m111_g_register,
+ .s_register = mt9m111_s_register,
+#endif
+};
+
+static int mt9m111_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= ARRAY_SIZE(mt9m111_colour_fmts))
+ return -EINVAL;
+
+ *code = mt9m111_colour_fmts[index].code;
+ return 0;
+}
+
+static int mt9m111_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = {
+ .s_mbus_fmt = mt9m111_s_fmt,
+ .g_mbus_fmt = mt9m111_g_fmt,
+ .try_mbus_fmt = mt9m111_try_fmt,
+ .s_crop = mt9m111_s_crop,
+ .g_crop = mt9m111_g_crop,
+ .cropcap = mt9m111_cropcap,
+ .enum_mbus_fmt = mt9m111_enum_fmt,
+ .g_mbus_config = mt9m111_g_mbus_config,
+};
+
+static struct v4l2_subdev_ops mt9m111_subdev_ops = {
+ .core = &mt9m111_subdev_core_ops,
+ .video = &mt9m111_subdev_video_ops,
+};
+
+static int mt9m111_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct mt9m111 *mt9m111;
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ int ret;
+
+ if (!icl) {
+ dev_err(&client->dev, "mt9m111: driver needs platform data\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_warn(&adapter->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
+ return -EIO;
+ }
+
+ mt9m111 = kzalloc(sizeof(struct mt9m111), GFP_KERNEL);
+ if (!mt9m111)
+ return -ENOMEM;
+
+ v4l2_i2c_subdev_init(&mt9m111->subdev, client, &mt9m111_subdev_ops);
+ v4l2_ctrl_handler_init(&mt9m111->hdl, 5);
+ v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
+ V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+ mt9m111->gain = v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
+ V4L2_CID_GAIN, 0, 63 * 2 * 2, 1, 32);
+ v4l2_ctrl_new_std_menu(&mt9m111->hdl,
+ &mt9m111_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
+ V4L2_EXPOSURE_AUTO);
+ mt9m111->subdev.ctrl_handler = &mt9m111->hdl;
+ if (mt9m111->hdl.error) {
+ int err = mt9m111->hdl.error;
+
+ kfree(mt9m111);
+ return err;
+ }
+
+ /* Second stage probe - when a capture adapter is there */
+ mt9m111->rect.left = MT9M111_MIN_DARK_COLS;
+ mt9m111->rect.top = MT9M111_MIN_DARK_ROWS;
+ mt9m111->rect.width = MT9M111_MAX_WIDTH;
+ mt9m111->rect.height = MT9M111_MAX_HEIGHT;
+ mt9m111->fmt = &mt9m111_colour_fmts[0];
+ mt9m111->lastpage = -1;
+ mutex_init(&mt9m111->power_lock);
+
+ ret = mt9m111_video_probe(client);
+ if (ret) {
+ v4l2_ctrl_handler_free(&mt9m111->hdl);
+ kfree(mt9m111);
+ }
+
+ return ret;
+}
+
+static int mt9m111_remove(struct i2c_client *client)
+{
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
+
+ v4l2_device_unregister_subdev(&mt9m111->subdev);
+ v4l2_ctrl_handler_free(&mt9m111->hdl);
+ kfree(mt9m111);
+
+ return 0;
+}
+
+static const struct i2c_device_id mt9m111_id[] = {
+ { "mt9m111", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mt9m111_id);
+
+static struct i2c_driver mt9m111_i2c_driver = {
+ .driver = {
+ .name = "mt9m111",
+ },
+ .probe = mt9m111_probe,
+ .remove = mt9m111_remove,
+ .id_table = mt9m111_id,
+};
+
+module_i2c_driver(mt9m111_i2c_driver);
+
+MODULE_DESCRIPTION("Micron/Aptina MT9M111/MT9M112/MT9M131 Camera driver");
+MODULE_AUTHOR("Robert Jarzmik");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mt9p031.c b/drivers/media/video/mt9p031.c
new file mode 100644
index 00000000..c81eaf4f
--- /dev/null
+++ b/drivers/media/video/mt9p031.c
@@ -0,0 +1,939 @@
+/*
+ * Driver for MT9P031 CMOS Image Sensor from Aptina
+ *
+ * Copyright (C) 2011, Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Copyright (C) 2011, Javier Martin <javier.martin@vista-silicon.com>
+ * Copyright (C) 2011, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * Based on the MT9V032 driver and Bastian Hecht's code.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/log2.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/mt9p031.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include "aptina-pll.h"
+
+#define MT9P031_PIXEL_ARRAY_WIDTH 2752
+#define MT9P031_PIXEL_ARRAY_HEIGHT 2004
+
+#define MT9P031_CHIP_VERSION 0x00
+#define MT9P031_CHIP_VERSION_VALUE 0x1801
+#define MT9P031_ROW_START 0x01
+#define MT9P031_ROW_START_MIN 0
+#define MT9P031_ROW_START_MAX 2004
+#define MT9P031_ROW_START_DEF 54
+#define MT9P031_COLUMN_START 0x02
+#define MT9P031_COLUMN_START_MIN 0
+#define MT9P031_COLUMN_START_MAX 2750
+#define MT9P031_COLUMN_START_DEF 16
+#define MT9P031_WINDOW_HEIGHT 0x03
+#define MT9P031_WINDOW_HEIGHT_MIN 2
+#define MT9P031_WINDOW_HEIGHT_MAX 2006
+#define MT9P031_WINDOW_HEIGHT_DEF 1944
+#define MT9P031_WINDOW_WIDTH 0x04
+#define MT9P031_WINDOW_WIDTH_MIN 2
+#define MT9P031_WINDOW_WIDTH_MAX 2752
+#define MT9P031_WINDOW_WIDTH_DEF 2592
+#define MT9P031_HORIZONTAL_BLANK 0x05
+#define MT9P031_HORIZONTAL_BLANK_MIN 0
+#define MT9P031_HORIZONTAL_BLANK_MAX 4095
+#define MT9P031_VERTICAL_BLANK 0x06
+#define MT9P031_VERTICAL_BLANK_MIN 0
+#define MT9P031_VERTICAL_BLANK_MAX 4095
+#define MT9P031_VERTICAL_BLANK_DEF 25
+#define MT9P031_OUTPUT_CONTROL 0x07
+#define MT9P031_OUTPUT_CONTROL_CEN 2
+#define MT9P031_OUTPUT_CONTROL_SYN 1
+#define MT9P031_OUTPUT_CONTROL_DEF 0x1f82
+#define MT9P031_SHUTTER_WIDTH_UPPER 0x08
+#define MT9P031_SHUTTER_WIDTH_LOWER 0x09
+#define MT9P031_SHUTTER_WIDTH_MIN 1
+#define MT9P031_SHUTTER_WIDTH_MAX 1048575
+#define MT9P031_SHUTTER_WIDTH_DEF 1943
+#define MT9P031_PLL_CONTROL 0x10
+#define MT9P031_PLL_CONTROL_PWROFF 0x0050
+#define MT9P031_PLL_CONTROL_PWRON 0x0051
+#define MT9P031_PLL_CONTROL_USEPLL 0x0052
+#define MT9P031_PLL_CONFIG_1 0x11
+#define MT9P031_PLL_CONFIG_2 0x12
+#define MT9P031_PIXEL_CLOCK_CONTROL 0x0a
+#define MT9P031_FRAME_RESTART 0x0b
+#define MT9P031_SHUTTER_DELAY 0x0c
+#define MT9P031_RST 0x0d
+#define MT9P031_RST_ENABLE 1
+#define MT9P031_RST_DISABLE 0
+#define MT9P031_READ_MODE_1 0x1e
+#define MT9P031_READ_MODE_2 0x20
+#define MT9P031_READ_MODE_2_ROW_MIR (1 << 15)
+#define MT9P031_READ_MODE_2_COL_MIR (1 << 14)
+#define MT9P031_READ_MODE_2_ROW_BLC (1 << 6)
+#define MT9P031_ROW_ADDRESS_MODE 0x22
+#define MT9P031_COLUMN_ADDRESS_MODE 0x23
+#define MT9P031_GLOBAL_GAIN 0x35
+#define MT9P031_GLOBAL_GAIN_MIN 8
+#define MT9P031_GLOBAL_GAIN_MAX 1024
+#define MT9P031_GLOBAL_GAIN_DEF 8
+#define MT9P031_GLOBAL_GAIN_MULT (1 << 6)
+#define MT9P031_ROW_BLACK_DEF_OFFSET 0x4b
+#define MT9P031_TEST_PATTERN 0xa0
+#define MT9P031_TEST_PATTERN_SHIFT 3
+#define MT9P031_TEST_PATTERN_ENABLE (1 << 0)
+#define MT9P031_TEST_PATTERN_DISABLE (0 << 0)
+#define MT9P031_TEST_PATTERN_GREEN 0xa1
+#define MT9P031_TEST_PATTERN_RED 0xa2
+#define MT9P031_TEST_PATTERN_BLUE 0xa3
+
+struct mt9p031 {
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+ struct v4l2_rect crop; /* Sensor window */
+ struct v4l2_mbus_framefmt format;
+ struct v4l2_ctrl_handler ctrls;
+ struct mt9p031_platform_data *pdata;
+ struct mutex power_lock; /* lock to protect power_count */
+ int power_count;
+
+ struct aptina_pll pll;
+
+ /* Registers cache */
+ u16 output_control;
+ u16 mode2;
+};
+
+static struct mt9p031 *to_mt9p031(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct mt9p031, subdev);
+}
+
+static int mt9p031_read(struct i2c_client *client, u8 reg)
+{
+ return i2c_smbus_read_word_swapped(client, reg);
+}
+
+static int mt9p031_write(struct i2c_client *client, u8 reg, u16 data)
+{
+ return i2c_smbus_write_word_swapped(client, reg, data);
+}
+
+static int mt9p031_set_output_control(struct mt9p031 *mt9p031, u16 clear,
+ u16 set)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ u16 value = (mt9p031->output_control & ~clear) | set;
+ int ret;
+
+ ret = mt9p031_write(client, MT9P031_OUTPUT_CONTROL, value);
+ if (ret < 0)
+ return ret;
+
+ mt9p031->output_control = value;
+ return 0;
+}
+
+static int mt9p031_set_mode2(struct mt9p031 *mt9p031, u16 clear, u16 set)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ u16 value = (mt9p031->mode2 & ~clear) | set;
+ int ret;
+
+ ret = mt9p031_write(client, MT9P031_READ_MODE_2, value);
+ if (ret < 0)
+ return ret;
+
+ mt9p031->mode2 = value;
+ return 0;
+}
+
+static int mt9p031_reset(struct mt9p031 *mt9p031)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ int ret;
+
+ /* Disable chip output, synchronous option update */
+ ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_ENABLE);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_DISABLE);
+ if (ret < 0)
+ return ret;
+
+ return mt9p031_set_output_control(mt9p031, MT9P031_OUTPUT_CONTROL_CEN,
+ 0);
+}
+
+static int mt9p031_pll_setup(struct mt9p031 *mt9p031)
+{
+ static const struct aptina_pll_limits limits = {
+ .ext_clock_min = 6000000,
+ .ext_clock_max = 27000000,
+ .int_clock_min = 2000000,
+ .int_clock_max = 13500000,
+ .out_clock_min = 180000000,
+ .out_clock_max = 360000000,
+ .pix_clock_max = 96000000,
+ .n_min = 1,
+ .n_max = 64,
+ .m_min = 16,
+ .m_max = 255,
+ .p1_min = 1,
+ .p1_max = 128,
+ };
+
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ struct mt9p031_platform_data *pdata = mt9p031->pdata;
+
+ mt9p031->pll.ext_clock = pdata->ext_freq;
+ mt9p031->pll.pix_clock = pdata->target_freq;
+
+ return aptina_pll_calculate(&client->dev, &limits, &mt9p031->pll);
+}
+
+static int mt9p031_pll_enable(struct mt9p031 *mt9p031)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ int ret;
+
+ ret = mt9p031_write(client, MT9P031_PLL_CONTROL,
+ MT9P031_PLL_CONTROL_PWRON);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9p031_write(client, MT9P031_PLL_CONFIG_1,
+ (mt9p031->pll.m << 8) | (mt9p031->pll.n - 1));
+ if (ret < 0)
+ return ret;
+
+ ret = mt9p031_write(client, MT9P031_PLL_CONFIG_2, mt9p031->pll.p1 - 1);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(1000, 2000);
+ ret = mt9p031_write(client, MT9P031_PLL_CONTROL,
+ MT9P031_PLL_CONTROL_PWRON |
+ MT9P031_PLL_CONTROL_USEPLL);
+ return ret;
+}
+
+static inline int mt9p031_pll_disable(struct mt9p031 *mt9p031)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+
+ return mt9p031_write(client, MT9P031_PLL_CONTROL,
+ MT9P031_PLL_CONTROL_PWROFF);
+}
+
+static int mt9p031_power_on(struct mt9p031 *mt9p031)
+{
+ /* Ensure RESET_BAR is low */
+ if (mt9p031->pdata->reset) {
+ mt9p031->pdata->reset(&mt9p031->subdev, 1);
+ usleep_range(1000, 2000);
+ }
+
+ /* Emable clock */
+ if (mt9p031->pdata->set_xclk)
+ mt9p031->pdata->set_xclk(&mt9p031->subdev,
+ mt9p031->pdata->ext_freq);
+
+ /* Now RESET_BAR must be high */
+ if (mt9p031->pdata->reset) {
+ mt9p031->pdata->reset(&mt9p031->subdev, 0);
+ usleep_range(1000, 2000);
+ }
+
+ return 0;
+}
+
+static void mt9p031_power_off(struct mt9p031 *mt9p031)
+{
+ if (mt9p031->pdata->reset) {
+ mt9p031->pdata->reset(&mt9p031->subdev, 1);
+ usleep_range(1000, 2000);
+ }
+
+ if (mt9p031->pdata->set_xclk)
+ mt9p031->pdata->set_xclk(&mt9p031->subdev, 0);
+}
+
+static int __mt9p031_set_power(struct mt9p031 *mt9p031, bool on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ int ret;
+
+ if (!on) {
+ mt9p031_power_off(mt9p031);
+ return 0;
+ }
+
+ ret = mt9p031_power_on(mt9p031);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9p031_reset(mt9p031);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to reset the camera\n");
+ return ret;
+ }
+
+ return v4l2_ctrl_handler_setup(&mt9p031->ctrls);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev video operations
+ */
+
+static int mt9p031_set_params(struct mt9p031 *mt9p031)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ struct v4l2_mbus_framefmt *format = &mt9p031->format;
+ const struct v4l2_rect *crop = &mt9p031->crop;
+ unsigned int hblank;
+ unsigned int vblank;
+ unsigned int xskip;
+ unsigned int yskip;
+ unsigned int xbin;
+ unsigned int ybin;
+ int ret;
+
+ /* Windows position and size.
+ *
+ * TODO: Make sure the start coordinates and window size match the
+ * skipping, binning and mirroring (see description of registers 2 and 4
+ * in table 13, and Binning section on page 41).
+ */
+ ret = mt9p031_write(client, MT9P031_COLUMN_START, crop->left);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_ROW_START, crop->top);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_WINDOW_WIDTH, crop->width - 1);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_WINDOW_HEIGHT, crop->height - 1);
+ if (ret < 0)
+ return ret;
+
+ /* Row and column binning and skipping. Use the maximum binning value
+ * compatible with the skipping settings.
+ */
+ xskip = DIV_ROUND_CLOSEST(crop->width, format->width);
+ yskip = DIV_ROUND_CLOSEST(crop->height, format->height);
+ xbin = 1 << (ffs(xskip) - 1);
+ ybin = 1 << (ffs(yskip) - 1);
+
+ ret = mt9p031_write(client, MT9P031_COLUMN_ADDRESS_MODE,
+ ((xbin - 1) << 4) | (xskip - 1));
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_ROW_ADDRESS_MODE,
+ ((ybin - 1) << 4) | (yskip - 1));
+ if (ret < 0)
+ return ret;
+
+ /* Blanking - use minimum value for horizontal blanking and default
+ * value for vertical blanking.
+ */
+ hblank = 346 * ybin + 64 + (80 >> max_t(unsigned int, xbin, 3));
+ vblank = MT9P031_VERTICAL_BLANK_DEF;
+
+ ret = mt9p031_write(client, MT9P031_HORIZONTAL_BLANK, hblank);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_VERTICAL_BLANK, vblank);
+ if (ret < 0)
+ return ret;
+
+ return ret;
+}
+
+static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+ int ret;
+
+ if (!enable) {
+ /* Stop sensor readout */
+ ret = mt9p031_set_output_control(mt9p031,
+ MT9P031_OUTPUT_CONTROL_CEN, 0);
+ if (ret < 0)
+ return ret;
+
+ return mt9p031_pll_disable(mt9p031);
+ }
+
+ ret = mt9p031_set_params(mt9p031);
+ if (ret < 0)
+ return ret;
+
+ /* Switch to master "normal" mode */
+ ret = mt9p031_set_output_control(mt9p031, 0,
+ MT9P031_OUTPUT_CONTROL_CEN);
+ if (ret < 0)
+ return ret;
+
+ return mt9p031_pll_enable(mt9p031);
+}
+
+static int mt9p031_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+
+ if (code->pad || code->index)
+ return -EINVAL;
+
+ code->code = mt9p031->format.code;
+ return 0;
+}
+
+static int mt9p031_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+
+ if (fse->index >= 8 || fse->code != mt9p031->format.code)
+ return -EINVAL;
+
+ fse->min_width = MT9P031_WINDOW_WIDTH_DEF
+ / min_t(unsigned int, 7, fse->index + 1);
+ fse->max_width = fse->min_width;
+ fse->min_height = MT9P031_WINDOW_HEIGHT_DEF / (fse->index + 1);
+ fse->max_height = fse->min_height;
+
+ return 0;
+}
+
+static struct v4l2_mbus_framefmt *
+__mt9p031_get_pad_format(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh,
+ unsigned int pad, u32 which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_format(fh, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &mt9p031->format;
+ default:
+ return NULL;
+ }
+}
+
+static struct v4l2_rect *
+__mt9p031_get_pad_crop(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh,
+ unsigned int pad, u32 which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_crop(fh, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &mt9p031->crop;
+ default:
+ return NULL;
+ }
+}
+
+static int mt9p031_get_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+
+ fmt->format = *__mt9p031_get_pad_format(mt9p031, fh, fmt->pad,
+ fmt->which);
+ return 0;
+}
+
+static int mt9p031_set_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *format)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+ struct v4l2_mbus_framefmt *__format;
+ struct v4l2_rect *__crop;
+ unsigned int width;
+ unsigned int height;
+ unsigned int hratio;
+ unsigned int vratio;
+
+ __crop = __mt9p031_get_pad_crop(mt9p031, fh, format->pad,
+ format->which);
+
+ /* Clamp the width and height to avoid dividing by zero. */
+ width = clamp_t(unsigned int, ALIGN(format->format.width, 2),
+ max(__crop->width / 7, MT9P031_WINDOW_WIDTH_MIN),
+ __crop->width);
+ height = clamp_t(unsigned int, ALIGN(format->format.height, 2),
+ max(__crop->height / 8, MT9P031_WINDOW_HEIGHT_MIN),
+ __crop->height);
+
+ hratio = DIV_ROUND_CLOSEST(__crop->width, width);
+ vratio = DIV_ROUND_CLOSEST(__crop->height, height);
+
+ __format = __mt9p031_get_pad_format(mt9p031, fh, format->pad,
+ format->which);
+ __format->width = __crop->width / hratio;
+ __format->height = __crop->height / vratio;
+
+ format->format = *__format;
+
+ return 0;
+}
+
+static int mt9p031_get_crop(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+
+ crop->rect = *__mt9p031_get_pad_crop(mt9p031, fh, crop->pad,
+ crop->which);
+ return 0;
+}
+
+static int mt9p031_set_crop(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+ struct v4l2_mbus_framefmt *__format;
+ struct v4l2_rect *__crop;
+ struct v4l2_rect rect;
+
+ /* Clamp the crop rectangle boundaries and align them to a multiple of 2
+ * pixels to ensure a GRBG Bayer pattern.
+ */
+ rect.left = clamp(ALIGN(crop->rect.left, 2), MT9P031_COLUMN_START_MIN,
+ MT9P031_COLUMN_START_MAX);
+ rect.top = clamp(ALIGN(crop->rect.top, 2), MT9P031_ROW_START_MIN,
+ MT9P031_ROW_START_MAX);
+ rect.width = clamp(ALIGN(crop->rect.width, 2),
+ MT9P031_WINDOW_WIDTH_MIN,
+ MT9P031_WINDOW_WIDTH_MAX);
+ rect.height = clamp(ALIGN(crop->rect.height, 2),
+ MT9P031_WINDOW_HEIGHT_MIN,
+ MT9P031_WINDOW_HEIGHT_MAX);
+
+ rect.width = min(rect.width, MT9P031_PIXEL_ARRAY_WIDTH - rect.left);
+ rect.height = min(rect.height, MT9P031_PIXEL_ARRAY_HEIGHT - rect.top);
+
+ __crop = __mt9p031_get_pad_crop(mt9p031, fh, crop->pad, crop->which);
+
+ if (rect.width != __crop->width || rect.height != __crop->height) {
+ /* Reset the output image size if the crop rectangle size has
+ * been modified.
+ */
+ __format = __mt9p031_get_pad_format(mt9p031, fh, crop->pad,
+ crop->which);
+ __format->width = rect.width;
+ __format->height = rect.height;
+ }
+
+ *__crop = rect;
+ crop->rect = rect;
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev control operations
+ */
+
+#define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001)
+
+static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct mt9p031 *mt9p031 =
+ container_of(ctrl->handler, struct mt9p031, ctrls);
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ u16 data;
+ int ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ ret = mt9p031_write(client, MT9P031_SHUTTER_WIDTH_UPPER,
+ (ctrl->val >> 16) & 0xffff);
+ if (ret < 0)
+ return ret;
+
+ return mt9p031_write(client, MT9P031_SHUTTER_WIDTH_LOWER,
+ ctrl->val & 0xffff);
+
+ case V4L2_CID_GAIN:
+ /* Gain is controlled by 2 analog stages and a digital stage.
+ * Valid values for the 3 stages are
+ *
+ * Stage Min Max Step
+ * ------------------------------------------
+ * First analog stage x1 x2 1
+ * Second analog stage x1 x4 0.125
+ * Digital stage x1 x16 0.125
+ *
+ * To minimize noise, the gain stages should be used in the
+ * second analog stage, first analog stage, digital stage order.
+ * Gain from a previous stage should be pushed to its maximum
+ * value before the next stage is used.
+ */
+ if (ctrl->val <= 32) {
+ data = ctrl->val;
+ } else if (ctrl->val <= 64) {
+ ctrl->val &= ~1;
+ data = (1 << 6) | (ctrl->val >> 1);
+ } else {
+ ctrl->val &= ~7;
+ data = ((ctrl->val - 64) << 5) | (1 << 6) | 32;
+ }
+
+ return mt9p031_write(client, MT9P031_GLOBAL_GAIN, data);
+
+ case V4L2_CID_HFLIP:
+ if (ctrl->val)
+ return mt9p031_set_mode2(mt9p031,
+ 0, MT9P031_READ_MODE_2_COL_MIR);
+ else
+ return mt9p031_set_mode2(mt9p031,
+ MT9P031_READ_MODE_2_COL_MIR, 0);
+
+ case V4L2_CID_VFLIP:
+ if (ctrl->val)
+ return mt9p031_set_mode2(mt9p031,
+ 0, MT9P031_READ_MODE_2_ROW_MIR);
+ else
+ return mt9p031_set_mode2(mt9p031,
+ MT9P031_READ_MODE_2_ROW_MIR, 0);
+
+ case V4L2_CID_TEST_PATTERN:
+ if (!ctrl->val) {
+ ret = mt9p031_set_mode2(mt9p031,
+ 0, MT9P031_READ_MODE_2_ROW_BLC);
+ if (ret < 0)
+ return ret;
+
+ return mt9p031_write(client, MT9P031_TEST_PATTERN,
+ MT9P031_TEST_PATTERN_DISABLE);
+ }
+
+ ret = mt9p031_write(client, MT9P031_TEST_PATTERN_GREEN, 0x05a0);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_TEST_PATTERN_RED, 0x0a50);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_TEST_PATTERN_BLUE, 0x0aa0);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC,
+ 0);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, 0);
+ if (ret < 0)
+ return ret;
+
+ return mt9p031_write(client, MT9P031_TEST_PATTERN,
+ ((ctrl->val - 1) << MT9P031_TEST_PATTERN_SHIFT)
+ | MT9P031_TEST_PATTERN_ENABLE);
+ }
+ return 0;
+}
+
+static struct v4l2_ctrl_ops mt9p031_ctrl_ops = {
+ .s_ctrl = mt9p031_s_ctrl,
+};
+
+static const char * const mt9p031_test_pattern_menu[] = {
+ "Disabled",
+ "Color Field",
+ "Horizontal Gradient",
+ "Vertical Gradient",
+ "Diagonal Gradient",
+ "Classic Test Pattern",
+ "Walking 1s",
+ "Monochrome Horizontal Bars",
+ "Monochrome Vertical Bars",
+ "Vertical Color Bars",
+};
+
+static const struct v4l2_ctrl_config mt9p031_ctrls[] = {
+ {
+ .ops = &mt9p031_ctrl_ops,
+ .id = V4L2_CID_TEST_PATTERN,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Test Pattern",
+ .min = 0,
+ .max = ARRAY_SIZE(mt9p031_test_pattern_menu) - 1,
+ .step = 0,
+ .def = 0,
+ .flags = 0,
+ .menu_skip_mask = 0,
+ .qmenu = mt9p031_test_pattern_menu,
+ }
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev core operations
+ */
+
+static int mt9p031_set_power(struct v4l2_subdev *subdev, int on)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+ int ret = 0;
+
+ mutex_lock(&mt9p031->power_lock);
+
+ /* If the power count is modified from 0 to != 0 or from != 0 to 0,
+ * update the power state.
+ */
+ if (mt9p031->power_count == !on) {
+ ret = __mt9p031_set_power(mt9p031, !!on);
+ if (ret < 0)
+ goto out;
+ }
+
+ /* Update the power count. */
+ mt9p031->power_count += on ? 1 : -1;
+ WARN_ON(mt9p031->power_count < 0);
+
+out:
+ mutex_unlock(&mt9p031->power_lock);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev internal operations
+ */
+
+static int mt9p031_registered(struct v4l2_subdev *subdev)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+ s32 data;
+ int ret;
+
+ ret = mt9p031_power_on(mt9p031);
+ if (ret < 0) {
+ dev_err(&client->dev, "MT9P031 power up failed\n");
+ return ret;
+ }
+
+ /* Read out the chip version register */
+ data = mt9p031_read(client, MT9P031_CHIP_VERSION);
+ if (data != MT9P031_CHIP_VERSION_VALUE) {
+ dev_err(&client->dev, "MT9P031 not detected, wrong version "
+ "0x%04x\n", data);
+ return -ENODEV;
+ }
+
+ mt9p031_power_off(mt9p031);
+
+ dev_info(&client->dev, "MT9P031 detected at address 0x%02x\n",
+ client->addr);
+
+ return ret;
+}
+
+static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *crop;
+
+ crop = v4l2_subdev_get_try_crop(fh, 0);
+ crop->left = MT9P031_COLUMN_START_DEF;
+ crop->top = MT9P031_ROW_START_DEF;
+ crop->width = MT9P031_WINDOW_WIDTH_DEF;
+ crop->height = MT9P031_WINDOW_HEIGHT_DEF;
+
+ format = v4l2_subdev_get_try_format(fh, 0);
+
+ if (mt9p031->pdata->version == MT9P031_MONOCHROME_VERSION)
+ format->code = V4L2_MBUS_FMT_Y12_1X12;
+ else
+ format->code = V4L2_MBUS_FMT_SGRBG12_1X12;
+
+ format->width = MT9P031_WINDOW_WIDTH_DEF;
+ format->height = MT9P031_WINDOW_HEIGHT_DEF;
+ format->field = V4L2_FIELD_NONE;
+ format->colorspace = V4L2_COLORSPACE_SRGB;
+
+ return mt9p031_set_power(subdev, 1);
+}
+
+static int mt9p031_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+ return mt9p031_set_power(subdev, 0);
+}
+
+static struct v4l2_subdev_core_ops mt9p031_subdev_core_ops = {
+ .s_power = mt9p031_set_power,
+};
+
+static struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = {
+ .s_stream = mt9p031_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops mt9p031_subdev_pad_ops = {
+ .enum_mbus_code = mt9p031_enum_mbus_code,
+ .enum_frame_size = mt9p031_enum_frame_size,
+ .get_fmt = mt9p031_get_format,
+ .set_fmt = mt9p031_set_format,
+ .get_crop = mt9p031_get_crop,
+ .set_crop = mt9p031_set_crop,
+};
+
+static struct v4l2_subdev_ops mt9p031_subdev_ops = {
+ .core = &mt9p031_subdev_core_ops,
+ .video = &mt9p031_subdev_video_ops,
+ .pad = &mt9p031_subdev_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops mt9p031_subdev_internal_ops = {
+ .registered = mt9p031_registered,
+ .open = mt9p031_open,
+ .close = mt9p031_close,
+};
+
+/* -----------------------------------------------------------------------------
+ * Driver initialization and probing
+ */
+
+static int mt9p031_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct mt9p031_platform_data *pdata = client->dev.platform_data;
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct mt9p031 *mt9p031;
+ unsigned int i;
+ int ret;
+
+ if (pdata == NULL) {
+ dev_err(&client->dev, "No platform data\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_warn(&client->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
+ return -EIO;
+ }
+
+ mt9p031 = kzalloc(sizeof(*mt9p031), GFP_KERNEL);
+ if (mt9p031 == NULL)
+ return -ENOMEM;
+
+ mt9p031->pdata = pdata;
+ mt9p031->output_control = MT9P031_OUTPUT_CONTROL_DEF;
+ mt9p031->mode2 = MT9P031_READ_MODE_2_ROW_BLC;
+
+ v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 4);
+
+ v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
+ V4L2_CID_EXPOSURE, MT9P031_SHUTTER_WIDTH_MIN,
+ MT9P031_SHUTTER_WIDTH_MAX, 1,
+ MT9P031_SHUTTER_WIDTH_DEF);
+ v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
+ V4L2_CID_GAIN, MT9P031_GLOBAL_GAIN_MIN,
+ MT9P031_GLOBAL_GAIN_MAX, 1, MT9P031_GLOBAL_GAIN_DEF);
+ v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+ for (i = 0; i < ARRAY_SIZE(mt9p031_ctrls); ++i)
+ v4l2_ctrl_new_custom(&mt9p031->ctrls, &mt9p031_ctrls[i], NULL);
+
+ mt9p031->subdev.ctrl_handler = &mt9p031->ctrls;
+
+ if (mt9p031->ctrls.error)
+ printk(KERN_INFO "%s: control initialization error %d\n",
+ __func__, mt9p031->ctrls.error);
+
+ mutex_init(&mt9p031->power_lock);
+ v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops);
+ mt9p031->subdev.internal_ops = &mt9p031_subdev_internal_ops;
+
+ mt9p031->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&mt9p031->subdev.entity, 1, &mt9p031->pad, 0);
+ if (ret < 0)
+ goto done;
+
+ mt9p031->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ mt9p031->crop.width = MT9P031_WINDOW_WIDTH_DEF;
+ mt9p031->crop.height = MT9P031_WINDOW_HEIGHT_DEF;
+ mt9p031->crop.left = MT9P031_COLUMN_START_DEF;
+ mt9p031->crop.top = MT9P031_ROW_START_DEF;
+
+ if (mt9p031->pdata->version == MT9P031_MONOCHROME_VERSION)
+ mt9p031->format.code = V4L2_MBUS_FMT_Y12_1X12;
+ else
+ mt9p031->format.code = V4L2_MBUS_FMT_SGRBG12_1X12;
+
+ mt9p031->format.width = MT9P031_WINDOW_WIDTH_DEF;
+ mt9p031->format.height = MT9P031_WINDOW_HEIGHT_DEF;
+ mt9p031->format.field = V4L2_FIELD_NONE;
+ mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB;
+
+ ret = mt9p031_pll_setup(mt9p031);
+
+done:
+ if (ret < 0) {
+ v4l2_ctrl_handler_free(&mt9p031->ctrls);
+ media_entity_cleanup(&mt9p031->subdev.entity);
+ kfree(mt9p031);
+ }
+
+ return ret;
+}
+
+static int mt9p031_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+
+ v4l2_ctrl_handler_free(&mt9p031->ctrls);
+ v4l2_device_unregister_subdev(subdev);
+ media_entity_cleanup(&subdev->entity);
+ kfree(mt9p031);
+
+ return 0;
+}
+
+static const struct i2c_device_id mt9p031_id[] = {
+ { "mt9p031", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mt9p031_id);
+
+static struct i2c_driver mt9p031_i2c_driver = {
+ .driver = {
+ .name = "mt9p031",
+ },
+ .probe = mt9p031_probe,
+ .remove = mt9p031_remove,
+ .id_table = mt9p031_id,
+};
+
+module_i2c_driver(mt9p031_i2c_driver);
+
+MODULE_DESCRIPTION("Aptina MT9P031 Camera driver");
+MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/mt9t001.c b/drivers/media/video/mt9t001.c
new file mode 100644
index 00000000..49ca3cbf
--- /dev/null
+++ b/drivers/media/video/mt9t001.c
@@ -0,0 +1,824 @@
+/*
+ * Driver for MT9T001 CMOS Image Sensor from Aptina (Micron)
+ *
+ * Copyright (C) 2010-2011, Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * Based on the MT9M001 driver,
+ *
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/log2.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-mediabus.h>
+
+#include <media/mt9t001.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#define MT9T001_PIXEL_ARRAY_HEIGHT 1568
+#define MT9T001_PIXEL_ARRAY_WIDTH 2112
+
+#define MT9T001_CHIP_VERSION 0x00
+#define MT9T001_CHIP_ID 0x1621
+#define MT9T001_ROW_START 0x01
+#define MT9T001_ROW_START_MIN 0
+#define MT9T001_ROW_START_DEF 20
+#define MT9T001_ROW_START_MAX 1534
+#define MT9T001_COLUMN_START 0x02
+#define MT9T001_COLUMN_START_MIN 0
+#define MT9T001_COLUMN_START_DEF 32
+#define MT9T001_COLUMN_START_MAX 2046
+#define MT9T001_WINDOW_HEIGHT 0x03
+#define MT9T001_WINDOW_HEIGHT_MIN 1
+#define MT9T001_WINDOW_HEIGHT_DEF 1535
+#define MT9T001_WINDOW_HEIGHT_MAX 1567
+#define MT9T001_WINDOW_WIDTH 0x04
+#define MT9T001_WINDOW_WIDTH_MIN 1
+#define MT9T001_WINDOW_WIDTH_DEF 2047
+#define MT9T001_WINDOW_WIDTH_MAX 2111
+#define MT9T001_HORIZONTAL_BLANKING 0x05
+#define MT9T001_HORIZONTAL_BLANKING_MIN 21
+#define MT9T001_HORIZONTAL_BLANKING_MAX 1023
+#define MT9T001_VERTICAL_BLANKING 0x06
+#define MT9T001_VERTICAL_BLANKING_MIN 3
+#define MT9T001_VERTICAL_BLANKING_MAX 1023
+#define MT9T001_OUTPUT_CONTROL 0x07
+#define MT9T001_OUTPUT_CONTROL_SYNC (1 << 0)
+#define MT9T001_OUTPUT_CONTROL_CHIP_ENABLE (1 << 1)
+#define MT9T001_OUTPUT_CONTROL_TEST_DATA (1 << 6)
+#define MT9T001_SHUTTER_WIDTH_HIGH 0x08
+#define MT9T001_SHUTTER_WIDTH_LOW 0x09
+#define MT9T001_SHUTTER_WIDTH_MIN 1
+#define MT9T001_SHUTTER_WIDTH_DEF 1561
+#define MT9T001_SHUTTER_WIDTH_MAX (1024 * 1024)
+#define MT9T001_PIXEL_CLOCK 0x0a
+#define MT9T001_PIXEL_CLOCK_INVERT (1 << 15)
+#define MT9T001_PIXEL_CLOCK_SHIFT_MASK (7 << 8)
+#define MT9T001_PIXEL_CLOCK_SHIFT_SHIFT 8
+#define MT9T001_PIXEL_CLOCK_DIVIDE_MASK (0x7f << 0)
+#define MT9T001_FRAME_RESTART 0x0b
+#define MT9T001_SHUTTER_DELAY 0x0c
+#define MT9T001_SHUTTER_DELAY_MAX 2047
+#define MT9T001_RESET 0x0d
+#define MT9T001_READ_MODE1 0x1e
+#define MT9T001_READ_MODE_SNAPSHOT (1 << 8)
+#define MT9T001_READ_MODE_STROBE_ENABLE (1 << 9)
+#define MT9T001_READ_MODE_STROBE_WIDTH (1 << 10)
+#define MT9T001_READ_MODE_STROBE_OVERRIDE (1 << 11)
+#define MT9T001_READ_MODE2 0x20
+#define MT9T001_READ_MODE_BAD_FRAMES (1 << 0)
+#define MT9T001_READ_MODE_LINE_VALID_CONTINUOUS (1 << 9)
+#define MT9T001_READ_MODE_LINE_VALID_FRAME (1 << 10)
+#define MT9T001_READ_MODE3 0x21
+#define MT9T001_READ_MODE_GLOBAL_RESET (1 << 0)
+#define MT9T001_READ_MODE_GHST_CTL (1 << 1)
+#define MT9T001_ROW_ADDRESS_MODE 0x22
+#define MT9T001_ROW_SKIP_MASK (7 << 0)
+#define MT9T001_ROW_BIN_MASK (3 << 3)
+#define MT9T001_ROW_BIN_SHIFT 3
+#define MT9T001_COLUMN_ADDRESS_MODE 0x23
+#define MT9T001_COLUMN_SKIP_MASK (7 << 0)
+#define MT9T001_COLUMN_BIN_MASK (3 << 3)
+#define MT9T001_COLUMN_BIN_SHIFT 3
+#define MT9T001_GREEN1_GAIN 0x2b
+#define MT9T001_BLUE_GAIN 0x2c
+#define MT9T001_RED_GAIN 0x2d
+#define MT9T001_GREEN2_GAIN 0x2e
+#define MT9T001_TEST_DATA 0x32
+#define MT9T001_GLOBAL_GAIN 0x35
+#define MT9T001_GLOBAL_GAIN_MIN 8
+#define MT9T001_GLOBAL_GAIN_MAX 1024
+#define MT9T001_BLACK_LEVEL 0x49
+#define MT9T001_ROW_BLACK_DEFAULT_OFFSET 0x4b
+#define MT9T001_BLC_DELTA_THRESHOLDS 0x5d
+#define MT9T001_CAL_THRESHOLDS 0x5f
+#define MT9T001_GREEN1_OFFSET 0x60
+#define MT9T001_GREEN2_OFFSET 0x61
+#define MT9T001_BLACK_LEVEL_CALIBRATION 0x62
+#define MT9T001_BLACK_LEVEL_OVERRIDE (1 << 0)
+#define MT9T001_BLACK_LEVEL_DISABLE_OFFSET (1 << 1)
+#define MT9T001_BLACK_LEVEL_RECALCULATE (1 << 12)
+#define MT9T001_BLACK_LEVEL_LOCK_RED_BLUE (1 << 13)
+#define MT9T001_BLACK_LEVEL_LOCK_GREEN (1 << 14)
+#define MT9T001_RED_OFFSET 0x63
+#define MT9T001_BLUE_OFFSET 0x64
+
+struct mt9t001 {
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+
+ struct v4l2_mbus_framefmt format;
+ struct v4l2_rect crop;
+
+ struct v4l2_ctrl_handler ctrls;
+ struct v4l2_ctrl *gains[4];
+
+ u16 output_control;
+ u16 black_level;
+};
+
+static inline struct mt9t001 *to_mt9t001(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct mt9t001, subdev);
+}
+
+static int mt9t001_read(struct i2c_client *client, u8 reg)
+{
+ return i2c_smbus_read_word_swapped(client, reg);
+}
+
+static int mt9t001_write(struct i2c_client *client, u8 reg, u16 data)
+{
+ return i2c_smbus_write_word_swapped(client, reg, data);
+}
+
+static int mt9t001_set_output_control(struct mt9t001 *mt9t001, u16 clear,
+ u16 set)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev);
+ u16 value = (mt9t001->output_control & ~clear) | set;
+ int ret;
+
+ if (value == mt9t001->output_control)
+ return 0;
+
+ ret = mt9t001_write(client, MT9T001_OUTPUT_CONTROL, value);
+ if (ret < 0)
+ return ret;
+
+ mt9t001->output_control = value;
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev video operations
+ */
+
+static struct v4l2_mbus_framefmt *
+__mt9t001_get_pad_format(struct mt9t001 *mt9t001, struct v4l2_subdev_fh *fh,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_format(fh, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &mt9t001->format;
+ default:
+ return NULL;
+ }
+}
+
+static struct v4l2_rect *
+__mt9t001_get_pad_crop(struct mt9t001 *mt9t001, struct v4l2_subdev_fh *fh,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_crop(fh, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &mt9t001->crop;
+ default:
+ return NULL;
+ }
+}
+
+static int mt9t001_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ const u16 mode = MT9T001_OUTPUT_CONTROL_CHIP_ENABLE;
+ struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ struct mt9t001 *mt9t001 = to_mt9t001(subdev);
+ struct v4l2_mbus_framefmt *format = &mt9t001->format;
+ struct v4l2_rect *crop = &mt9t001->crop;
+ unsigned int hratio;
+ unsigned int vratio;
+ int ret;
+
+ if (!enable)
+ return mt9t001_set_output_control(mt9t001, mode, 0);
+
+ /* Configure the window size and row/column bin */
+ hratio = DIV_ROUND_CLOSEST(crop->width, format->width);
+ vratio = DIV_ROUND_CLOSEST(crop->height, format->height);
+
+ ret = mt9t001_write(client, MT9T001_ROW_ADDRESS_MODE, hratio - 1);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_COLUMN_ADDRESS_MODE, vratio - 1);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_COLUMN_START, crop->left);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_ROW_START, crop->top);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_WINDOW_WIDTH, crop->width - 1);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_WINDOW_HEIGHT, crop->height - 1);
+ if (ret < 0)
+ return ret;
+
+ /* Switch to master "normal" mode */
+ return mt9t001_set_output_control(mt9t001, 0, mode);
+}
+
+static int mt9t001_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index > 0)
+ return -EINVAL;
+
+ code->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ return 0;
+}
+
+static int mt9t001_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->index >= 8 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10)
+ return -EINVAL;
+
+ fse->min_width = (MT9T001_WINDOW_WIDTH_DEF + 1) / fse->index;
+ fse->max_width = fse->min_width;
+ fse->min_height = (MT9T001_WINDOW_HEIGHT_DEF + 1) / fse->index;
+ fse->max_height = fse->min_height;
+
+ return 0;
+}
+
+static int mt9t001_get_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *format)
+{
+ struct mt9t001 *mt9t001 = to_mt9t001(subdev);
+
+ format->format = *__mt9t001_get_pad_format(mt9t001, fh, format->pad,
+ format->which);
+ return 0;
+}
+
+static int mt9t001_set_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *format)
+{
+ struct mt9t001 *mt9t001 = to_mt9t001(subdev);
+ struct v4l2_mbus_framefmt *__format;
+ struct v4l2_rect *__crop;
+ unsigned int width;
+ unsigned int height;
+ unsigned int hratio;
+ unsigned int vratio;
+
+ __crop = __mt9t001_get_pad_crop(mt9t001, fh, format->pad,
+ format->which);
+
+ /* Clamp the width and height to avoid dividing by zero. */
+ width = clamp_t(unsigned int, ALIGN(format->format.width, 2),
+ max(__crop->width / 8, MT9T001_WINDOW_HEIGHT_MIN + 1),
+ __crop->width);
+ height = clamp_t(unsigned int, ALIGN(format->format.height, 2),
+ max(__crop->height / 8, MT9T001_WINDOW_HEIGHT_MIN + 1),
+ __crop->height);
+
+ hratio = DIV_ROUND_CLOSEST(__crop->width, width);
+ vratio = DIV_ROUND_CLOSEST(__crop->height, height);
+
+ __format = __mt9t001_get_pad_format(mt9t001, fh, format->pad,
+ format->which);
+ __format->width = __crop->width / hratio;
+ __format->height = __crop->height / vratio;
+
+ format->format = *__format;
+
+ return 0;
+}
+
+static int mt9t001_get_crop(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct mt9t001 *mt9t001 = to_mt9t001(subdev);
+
+ crop->rect = *__mt9t001_get_pad_crop(mt9t001, fh, crop->pad,
+ crop->which);
+ return 0;
+}
+
+static int mt9t001_set_crop(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct mt9t001 *mt9t001 = to_mt9t001(subdev);
+ struct v4l2_mbus_framefmt *__format;
+ struct v4l2_rect *__crop;
+ struct v4l2_rect rect;
+
+ /* Clamp the crop rectangle boundaries and align them to a multiple of 2
+ * pixels.
+ */
+ rect.left = clamp(ALIGN(crop->rect.left, 2),
+ MT9T001_COLUMN_START_MIN,
+ MT9T001_COLUMN_START_MAX);
+ rect.top = clamp(ALIGN(crop->rect.top, 2),
+ MT9T001_ROW_START_MIN,
+ MT9T001_ROW_START_MAX);
+ rect.width = clamp(ALIGN(crop->rect.width, 2),
+ MT9T001_WINDOW_WIDTH_MIN + 1,
+ MT9T001_WINDOW_WIDTH_MAX + 1);
+ rect.height = clamp(ALIGN(crop->rect.height, 2),
+ MT9T001_WINDOW_HEIGHT_MIN + 1,
+ MT9T001_WINDOW_HEIGHT_MAX + 1);
+
+ rect.width = min(rect.width, MT9T001_PIXEL_ARRAY_WIDTH - rect.left);
+ rect.height = min(rect.height, MT9T001_PIXEL_ARRAY_HEIGHT - rect.top);
+
+ __crop = __mt9t001_get_pad_crop(mt9t001, fh, crop->pad, crop->which);
+
+ if (rect.width != __crop->width || rect.height != __crop->height) {
+ /* Reset the output image size if the crop rectangle size has
+ * been modified.
+ */
+ __format = __mt9t001_get_pad_format(mt9t001, fh, crop->pad,
+ crop->which);
+ __format->width = rect.width;
+ __format->height = rect.height;
+ }
+
+ *__crop = rect;
+ crop->rect = rect;
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev control operations
+ */
+
+#define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001)
+#define V4L2_CID_BLACK_LEVEL_AUTO (V4L2_CID_USER_BASE | 0x1002)
+#define V4L2_CID_BLACK_LEVEL_OFFSET (V4L2_CID_USER_BASE | 0x1003)
+#define V4L2_CID_BLACK_LEVEL_CALIBRATE (V4L2_CID_USER_BASE | 0x1004)
+
+#define V4L2_CID_GAIN_RED (V4L2_CTRL_CLASS_CAMERA | 0x1001)
+#define V4L2_CID_GAIN_GREEN_RED (V4L2_CTRL_CLASS_CAMERA | 0x1002)
+#define V4L2_CID_GAIN_GREEN_BLUE (V4L2_CTRL_CLASS_CAMERA | 0x1003)
+#define V4L2_CID_GAIN_BLUE (V4L2_CTRL_CLASS_CAMERA | 0x1004)
+
+static u16 mt9t001_gain_value(s32 *gain)
+{
+ /* Gain is controlled by 2 analog stages and a digital stage. Valid
+ * values for the 3 stages are
+ *
+ * Stage Min Max Step
+ * ------------------------------------------
+ * First analog stage x1 x2 1
+ * Second analog stage x1 x4 0.125
+ * Digital stage x1 x16 0.125
+ *
+ * To minimize noise, the gain stages should be used in the second
+ * analog stage, first analog stage, digital stage order. Gain from a
+ * previous stage should be pushed to its maximum value before the next
+ * stage is used.
+ */
+ if (*gain <= 32)
+ return *gain;
+
+ if (*gain <= 64) {
+ *gain &= ~1;
+ return (1 << 6) | (*gain >> 1);
+ }
+
+ *gain &= ~7;
+ return ((*gain - 64) << 5) | (1 << 6) | 32;
+}
+
+static int mt9t001_ctrl_freeze(struct mt9t001 *mt9t001, bool freeze)
+{
+ return mt9t001_set_output_control(mt9t001,
+ freeze ? 0 : MT9T001_OUTPUT_CONTROL_SYNC,
+ freeze ? MT9T001_OUTPUT_CONTROL_SYNC : 0);
+}
+
+static int mt9t001_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ static const u8 gains[4] = {
+ MT9T001_RED_GAIN, MT9T001_GREEN1_GAIN,
+ MT9T001_GREEN2_GAIN, MT9T001_BLUE_GAIN
+ };
+
+ struct mt9t001 *mt9t001 =
+ container_of(ctrl->handler, struct mt9t001, ctrls);
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev);
+ unsigned int count;
+ unsigned int i;
+ u16 value;
+ int ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN_RED:
+ case V4L2_CID_GAIN_GREEN_RED:
+ case V4L2_CID_GAIN_GREEN_BLUE:
+ case V4L2_CID_GAIN_BLUE:
+
+ /* Disable control updates if more than one control has changed
+ * in the cluster.
+ */
+ for (i = 0, count = 0; i < 4; ++i) {
+ struct v4l2_ctrl *gain = mt9t001->gains[i];
+
+ if (gain->val != gain->cur.val)
+ count++;
+ }
+
+ if (count > 1) {
+ ret = mt9t001_ctrl_freeze(mt9t001, true);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Update the gain controls. */
+ for (i = 0; i < 4; ++i) {
+ struct v4l2_ctrl *gain = mt9t001->gains[i];
+
+ if (gain->val == gain->cur.val)
+ continue;
+
+ value = mt9t001_gain_value(&gain->val);
+ ret = mt9t001_write(client, gains[i], value);
+ if (ret < 0) {
+ mt9t001_ctrl_freeze(mt9t001, false);
+ return ret;
+ }
+ }
+
+ /* Enable control updates. */
+ if (count > 1) {
+ ret = mt9t001_ctrl_freeze(mt9t001, false);
+ if (ret < 0)
+ return ret;
+ }
+
+ break;
+
+ case V4L2_CID_EXPOSURE:
+ ret = mt9t001_write(client, MT9T001_SHUTTER_WIDTH_LOW,
+ ctrl->val & 0xffff);
+ if (ret < 0)
+ return ret;
+
+ return mt9t001_write(client, MT9T001_SHUTTER_WIDTH_HIGH,
+ ctrl->val >> 16);
+
+ case V4L2_CID_TEST_PATTERN:
+ ret = mt9t001_set_output_control(mt9t001,
+ ctrl->val ? 0 : MT9T001_OUTPUT_CONTROL_TEST_DATA,
+ ctrl->val ? MT9T001_OUTPUT_CONTROL_TEST_DATA : 0);
+ if (ret < 0)
+ return ret;
+
+ return mt9t001_write(client, MT9T001_TEST_DATA, ctrl->val << 2);
+
+ case V4L2_CID_BLACK_LEVEL_AUTO:
+ value = ctrl->val ? 0 : MT9T001_BLACK_LEVEL_OVERRIDE;
+ ret = mt9t001_write(client, MT9T001_BLACK_LEVEL_CALIBRATION,
+ value);
+ if (ret < 0)
+ return ret;
+
+ mt9t001->black_level = value;
+ break;
+
+ case V4L2_CID_BLACK_LEVEL_OFFSET:
+ ret = mt9t001_write(client, MT9T001_GREEN1_OFFSET, ctrl->val);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_GREEN2_OFFSET, ctrl->val);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_RED_OFFSET, ctrl->val);
+ if (ret < 0)
+ return ret;
+
+ return mt9t001_write(client, MT9T001_BLUE_OFFSET, ctrl->val);
+
+ case V4L2_CID_BLACK_LEVEL_CALIBRATE:
+ return mt9t001_write(client, MT9T001_BLACK_LEVEL_CALIBRATION,
+ MT9T001_BLACK_LEVEL_RECALCULATE |
+ mt9t001->black_level);
+ }
+
+ return 0;
+}
+
+static struct v4l2_ctrl_ops mt9t001_ctrl_ops = {
+ .s_ctrl = mt9t001_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config mt9t001_ctrls[] = {
+ {
+ .ops = &mt9t001_ctrl_ops,
+ .id = V4L2_CID_TEST_PATTERN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Test pattern",
+ .min = 0,
+ .max = 1023,
+ .step = 1,
+ .def = 0,
+ .flags = 0,
+ }, {
+ .ops = &mt9t001_ctrl_ops,
+ .id = V4L2_CID_BLACK_LEVEL_AUTO,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Black Level, Auto",
+ .min = 0,
+ .max = 1,
+ .step = 1,
+ .def = 1,
+ .flags = 0,
+ }, {
+ .ops = &mt9t001_ctrl_ops,
+ .id = V4L2_CID_BLACK_LEVEL_OFFSET,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Black Level, Offset",
+ .min = -256,
+ .max = 255,
+ .step = 1,
+ .def = 32,
+ .flags = 0,
+ }, {
+ .ops = &mt9t001_ctrl_ops,
+ .id = V4L2_CID_BLACK_LEVEL_CALIBRATE,
+ .type = V4L2_CTRL_TYPE_BUTTON,
+ .name = "Black Level, Calibrate",
+ .min = 0,
+ .max = 0,
+ .step = 0,
+ .def = 0,
+ .flags = V4L2_CTRL_FLAG_WRITE_ONLY,
+ },
+};
+
+static const struct v4l2_ctrl_config mt9t001_gains[] = {
+ {
+ .ops = &mt9t001_ctrl_ops,
+ .id = V4L2_CID_GAIN_RED,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain, Red",
+ .min = MT9T001_GLOBAL_GAIN_MIN,
+ .max = MT9T001_GLOBAL_GAIN_MAX,
+ .step = 1,
+ .def = MT9T001_GLOBAL_GAIN_MIN,
+ .flags = 0,
+ }, {
+ .ops = &mt9t001_ctrl_ops,
+ .id = V4L2_CID_GAIN_GREEN_RED,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain, Green (R)",
+ .min = MT9T001_GLOBAL_GAIN_MIN,
+ .max = MT9T001_GLOBAL_GAIN_MAX,
+ .step = 1,
+ .def = MT9T001_GLOBAL_GAIN_MIN,
+ .flags = 0,
+ }, {
+ .ops = &mt9t001_ctrl_ops,
+ .id = V4L2_CID_GAIN_GREEN_BLUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain, Green (B)",
+ .min = MT9T001_GLOBAL_GAIN_MIN,
+ .max = MT9T001_GLOBAL_GAIN_MAX,
+ .step = 1,
+ .def = MT9T001_GLOBAL_GAIN_MIN,
+ .flags = 0,
+ }, {
+ .ops = &mt9t001_ctrl_ops,
+ .id = V4L2_CID_GAIN_BLUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain, Blue",
+ .min = MT9T001_GLOBAL_GAIN_MIN,
+ .max = MT9T001_GLOBAL_GAIN_MAX,
+ .step = 1,
+ .def = MT9T001_GLOBAL_GAIN_MIN,
+ .flags = 0,
+ },
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev internal operations
+ */
+
+static int mt9t001_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *crop;
+
+ crop = v4l2_subdev_get_try_crop(fh, 0);
+ crop->left = MT9T001_COLUMN_START_DEF;
+ crop->top = MT9T001_ROW_START_DEF;
+ crop->width = MT9T001_WINDOW_WIDTH_DEF + 1;
+ crop->height = MT9T001_WINDOW_HEIGHT_DEF + 1;
+
+ format = v4l2_subdev_get_try_format(fh, 0);
+ format->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ format->width = MT9T001_WINDOW_WIDTH_DEF + 1;
+ format->height = MT9T001_WINDOW_HEIGHT_DEF + 1;
+ format->field = V4L2_FIELD_NONE;
+ format->colorspace = V4L2_COLORSPACE_SRGB;
+
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops mt9t001_subdev_video_ops = {
+ .s_stream = mt9t001_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops mt9t001_subdev_pad_ops = {
+ .enum_mbus_code = mt9t001_enum_mbus_code,
+ .enum_frame_size = mt9t001_enum_frame_size,
+ .get_fmt = mt9t001_get_format,
+ .set_fmt = mt9t001_set_format,
+ .get_crop = mt9t001_get_crop,
+ .set_crop = mt9t001_set_crop,
+};
+
+static struct v4l2_subdev_ops mt9t001_subdev_ops = {
+ .video = &mt9t001_subdev_video_ops,
+ .pad = &mt9t001_subdev_pad_ops,
+};
+
+static struct v4l2_subdev_internal_ops mt9t001_subdev_internal_ops = {
+ .open = mt9t001_open,
+};
+
+static int mt9t001_video_probe(struct i2c_client *client)
+{
+ struct mt9t001_platform_data *pdata = client->dev.platform_data;
+ s32 data;
+ int ret;
+
+ dev_info(&client->dev, "Probing MT9T001 at address 0x%02x\n",
+ client->addr);
+
+ /* Reset the chip and stop data read out */
+ ret = mt9t001_write(client, MT9T001_RESET, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_RESET, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t001_write(client, MT9T001_OUTPUT_CONTROL, 0);
+ if (ret < 0)
+ return ret;
+
+ /* Configure the pixel clock polarity */
+ if (pdata && pdata->clk_pol) {
+ ret = mt9t001_write(client, MT9T001_PIXEL_CLOCK,
+ MT9T001_PIXEL_CLOCK_INVERT);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Read and check the sensor version */
+ data = mt9t001_read(client, MT9T001_CHIP_VERSION);
+ if (data != MT9T001_CHIP_ID) {
+ dev_err(&client->dev, "MT9T001 not detected, wrong version "
+ "0x%04x\n", data);
+ return -ENODEV;
+ }
+
+ dev_info(&client->dev, "MT9T001 detected at address 0x%02x\n",
+ client->addr);
+
+ return ret;
+}
+
+static int mt9t001_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct mt9t001 *mt9t001;
+ unsigned int i;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_warn(&client->adapter->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
+ return -EIO;
+ }
+
+ ret = mt9t001_video_probe(client);
+ if (ret < 0)
+ return ret;
+
+ mt9t001 = kzalloc(sizeof(*mt9t001), GFP_KERNEL);
+ if (!mt9t001)
+ return -ENOMEM;
+
+ v4l2_ctrl_handler_init(&mt9t001->ctrls, ARRAY_SIZE(mt9t001_ctrls) +
+ ARRAY_SIZE(mt9t001_gains) + 2);
+
+ v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops,
+ V4L2_CID_EXPOSURE, MT9T001_SHUTTER_WIDTH_MIN,
+ MT9T001_SHUTTER_WIDTH_MAX, 1,
+ MT9T001_SHUTTER_WIDTH_DEF);
+ v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops,
+ V4L2_CID_BLACK_LEVEL, 1, 1, 1, 1);
+
+ for (i = 0; i < ARRAY_SIZE(mt9t001_ctrls); ++i)
+ v4l2_ctrl_new_custom(&mt9t001->ctrls, &mt9t001_ctrls[i], NULL);
+
+ for (i = 0; i < ARRAY_SIZE(mt9t001_gains); ++i)
+ mt9t001->gains[i] = v4l2_ctrl_new_custom(&mt9t001->ctrls,
+ &mt9t001_gains[i], NULL);
+
+ v4l2_ctrl_cluster(ARRAY_SIZE(mt9t001_gains), mt9t001->gains);
+
+ mt9t001->subdev.ctrl_handler = &mt9t001->ctrls;
+
+ if (mt9t001->ctrls.error) {
+ printk(KERN_INFO "%s: control initialization error %d\n",
+ __func__, mt9t001->ctrls.error);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ mt9t001->crop.left = MT9T001_COLUMN_START_DEF;
+ mt9t001->crop.top = MT9T001_ROW_START_DEF;
+ mt9t001->crop.width = MT9T001_WINDOW_WIDTH_DEF + 1;
+ mt9t001->crop.height = MT9T001_WINDOW_HEIGHT_DEF + 1;
+
+ mt9t001->format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ mt9t001->format.width = MT9T001_WINDOW_WIDTH_DEF + 1;
+ mt9t001->format.height = MT9T001_WINDOW_HEIGHT_DEF + 1;
+ mt9t001->format.field = V4L2_FIELD_NONE;
+ mt9t001->format.colorspace = V4L2_COLORSPACE_SRGB;
+
+ v4l2_i2c_subdev_init(&mt9t001->subdev, client, &mt9t001_subdev_ops);
+ mt9t001->subdev.internal_ops = &mt9t001_subdev_internal_ops;
+ mt9t001->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ mt9t001->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&mt9t001->subdev.entity, 1, &mt9t001->pad, 0);
+
+done:
+ if (ret < 0) {
+ v4l2_ctrl_handler_free(&mt9t001->ctrls);
+ media_entity_cleanup(&mt9t001->subdev.entity);
+ kfree(mt9t001);
+ }
+
+ return ret;
+}
+
+static int mt9t001_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct mt9t001 *mt9t001 = to_mt9t001(subdev);
+
+ v4l2_ctrl_handler_free(&mt9t001->ctrls);
+ v4l2_device_unregister_subdev(subdev);
+ media_entity_cleanup(&subdev->entity);
+ kfree(mt9t001);
+ return 0;
+}
+
+static const struct i2c_device_id mt9t001_id[] = {
+ { "mt9t001", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mt9t001_id);
+
+static struct i2c_driver mt9t001_driver = {
+ .driver = {
+ .name = "mt9t001",
+ },
+ .probe = mt9t001_probe,
+ .remove = mt9t001_remove,
+ .id_table = mt9t001_id,
+};
+
+module_i2c_driver(mt9t001_driver);
+
+MODULE_DESCRIPTION("Aptina (Micron) MT9T001 Camera driver");
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c
new file mode 100644
index 00000000..14150741
--- /dev/null
+++ b/drivers/media/video/mt9t031.c
@@ -0,0 +1,857 @@
+/*
+ * Driver for MT9T031 CMOS Image Sensor from Micron
+ *
+ * Copyright (C) 2008, Guennadi Liakhovetski, DENX Software Engineering <lg@denx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/log2.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/videodev2.h>
+#include <linux/module.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-ctrls.h>
+
+/*
+ * ATTENTION: this driver still cannot be used outside of the soc-camera
+ * framework because of its PM implementation, using the video_device node.
+ * If hardware becomes available for testing, alternative PM approaches shall
+ * be considered and tested.
+ */
+
+/*
+ * mt9t031 i2c address 0x5d
+ * The platform has to define i2c_board_info and link to it from
+ * struct soc_camera_link
+ */
+
+/* mt9t031 selected register addresses */
+#define MT9T031_CHIP_VERSION 0x00
+#define MT9T031_ROW_START 0x01
+#define MT9T031_COLUMN_START 0x02
+#define MT9T031_WINDOW_HEIGHT 0x03
+#define MT9T031_WINDOW_WIDTH 0x04
+#define MT9T031_HORIZONTAL_BLANKING 0x05
+#define MT9T031_VERTICAL_BLANKING 0x06
+#define MT9T031_OUTPUT_CONTROL 0x07
+#define MT9T031_SHUTTER_WIDTH_UPPER 0x08
+#define MT9T031_SHUTTER_WIDTH 0x09
+#define MT9T031_PIXEL_CLOCK_CONTROL 0x0a
+#define MT9T031_FRAME_RESTART 0x0b
+#define MT9T031_SHUTTER_DELAY 0x0c
+#define MT9T031_RESET 0x0d
+#define MT9T031_READ_MODE_1 0x1e
+#define MT9T031_READ_MODE_2 0x20
+#define MT9T031_READ_MODE_3 0x21
+#define MT9T031_ROW_ADDRESS_MODE 0x22
+#define MT9T031_COLUMN_ADDRESS_MODE 0x23
+#define MT9T031_GLOBAL_GAIN 0x35
+#define MT9T031_CHIP_ENABLE 0xF8
+
+#define MT9T031_MAX_HEIGHT 1536
+#define MT9T031_MAX_WIDTH 2048
+#define MT9T031_MIN_HEIGHT 2
+#define MT9T031_MIN_WIDTH 18
+#define MT9T031_HORIZONTAL_BLANK 142
+#define MT9T031_VERTICAL_BLANK 25
+#define MT9T031_COLUMN_SKIP 32
+#define MT9T031_ROW_SKIP 20
+
+struct mt9t031 {
+ struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
+ struct {
+ /* exposure/auto-exposure cluster */
+ struct v4l2_ctrl *autoexposure;
+ struct v4l2_ctrl *exposure;
+ };
+ struct v4l2_rect rect; /* Sensor window */
+ int model; /* V4L2_IDENT_MT9T031* codes from v4l2-chip-ident.h */
+ u16 xskip;
+ u16 yskip;
+ unsigned int total_h;
+ unsigned short y_skip_top; /* Lines to skip at the top */
+};
+
+static struct mt9t031 *to_mt9t031(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct mt9t031, subdev);
+}
+
+static int reg_read(struct i2c_client *client, const u8 reg)
+{
+ return i2c_smbus_read_word_swapped(client, reg);
+}
+
+static int reg_write(struct i2c_client *client, const u8 reg,
+ const u16 data)
+{
+ return i2c_smbus_write_word_swapped(client, reg, data);
+}
+
+static int reg_set(struct i2c_client *client, const u8 reg,
+ const u16 data)
+{
+ int ret;
+
+ ret = reg_read(client, reg);
+ if (ret < 0)
+ return ret;
+ return reg_write(client, reg, ret | data);
+}
+
+static int reg_clear(struct i2c_client *client, const u8 reg,
+ const u16 data)
+{
+ int ret;
+
+ ret = reg_read(client, reg);
+ if (ret < 0)
+ return ret;
+ return reg_write(client, reg, ret & ~data);
+}
+
+static int set_shutter(struct i2c_client *client, const u32 data)
+{
+ int ret;
+
+ ret = reg_write(client, MT9T031_SHUTTER_WIDTH_UPPER, data >> 16);
+
+ if (ret >= 0)
+ ret = reg_write(client, MT9T031_SHUTTER_WIDTH, data & 0xffff);
+
+ return ret;
+}
+
+static int get_shutter(struct i2c_client *client, u32 *data)
+{
+ int ret;
+
+ ret = reg_read(client, MT9T031_SHUTTER_WIDTH_UPPER);
+ *data = ret << 16;
+
+ if (ret >= 0)
+ ret = reg_read(client, MT9T031_SHUTTER_WIDTH);
+ *data |= ret & 0xffff;
+
+ return ret < 0 ? ret : 0;
+}
+
+static int mt9t031_idle(struct i2c_client *client)
+{
+ int ret;
+
+ /* Disable chip output, synchronous option update */
+ ret = reg_write(client, MT9T031_RESET, 1);
+ if (ret >= 0)
+ ret = reg_write(client, MT9T031_RESET, 0);
+ if (ret >= 0)
+ ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2);
+
+ return ret >= 0 ? 0 : -EIO;
+}
+
+static int mt9t031_disable(struct i2c_client *client)
+{
+ /* Disable the chip */
+ reg_clear(client, MT9T031_OUTPUT_CONTROL, 2);
+
+ return 0;
+}
+
+static int mt9t031_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ if (enable)
+ /* Switch to master "normal" mode */
+ ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 2);
+ else
+ /* Stop sensor readout */
+ ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2);
+
+ if (ret < 0)
+ return -EIO;
+
+ return 0;
+}
+
+/* target must be _even_ */
+static u16 mt9t031_skip(s32 *source, s32 target, s32 max)
+{
+ unsigned int skip;
+
+ if (*source < target + target / 2) {
+ *source = target;
+ return 1;
+ }
+
+ skip = min(max, *source + target / 2) / target;
+ if (skip > 8)
+ skip = 8;
+ *source = target * skip;
+
+ return skip;
+}
+
+/* rect is the sensor rectangle, the caller guarantees parameter validity */
+static int mt9t031_set_params(struct i2c_client *client,
+ struct v4l2_rect *rect, u16 xskip, u16 yskip)
+{
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
+ int ret;
+ u16 xbin, ybin;
+ const u16 hblank = MT9T031_HORIZONTAL_BLANK,
+ vblank = MT9T031_VERTICAL_BLANK;
+
+ xbin = min(xskip, (u16)3);
+ ybin = min(yskip, (u16)3);
+
+ /*
+ * Could just do roundup(rect->left, [xy]bin * 2); but this is cheaper.
+ * There is always a valid suitably aligned value. The worst case is
+ * xbin = 3, width = 2048. Then we will start at 36, the last read out
+ * pixel will be 2083, which is < 2085 - first black pixel.
+ *
+ * MT9T031 datasheet imposes window left border alignment, depending on
+ * the selected xskip. Failing to conform to this requirement produces
+ * dark horizontal stripes in the image. However, even obeying to this
+ * requirement doesn't eliminate the stripes in all configurations. They
+ * appear "locally reproducibly," but can differ between tests under
+ * different lighting conditions.
+ */
+ switch (xbin) {
+ case 1:
+ rect->left &= ~1;
+ break;
+ case 2:
+ rect->left &= ~3;
+ break;
+ case 3:
+ rect->left = rect->left > roundup(MT9T031_COLUMN_SKIP, 6) ?
+ (rect->left / 6) * 6 : roundup(MT9T031_COLUMN_SKIP, 6);
+ }
+
+ rect->top &= ~1;
+
+ dev_dbg(&client->dev, "skip %u:%u, rect %ux%u@%u:%u\n",
+ xskip, yskip, rect->width, rect->height, rect->left, rect->top);
+
+ /* Disable register update, reconfigure atomically */
+ ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 1);
+ if (ret < 0)
+ return ret;
+
+ /* Blanking and start values - default... */
+ ret = reg_write(client, MT9T031_HORIZONTAL_BLANKING, hblank);
+ if (ret >= 0)
+ ret = reg_write(client, MT9T031_VERTICAL_BLANKING, vblank);
+
+ if (yskip != mt9t031->yskip || xskip != mt9t031->xskip) {
+ /* Binning, skipping */
+ if (ret >= 0)
+ ret = reg_write(client, MT9T031_COLUMN_ADDRESS_MODE,
+ ((xbin - 1) << 4) | (xskip - 1));
+ if (ret >= 0)
+ ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE,
+ ((ybin - 1) << 4) | (yskip - 1));
+ }
+ dev_dbg(&client->dev, "new physical left %u, top %u\n",
+ rect->left, rect->top);
+
+ /*
+ * The caller provides a supported format, as guaranteed by
+ * .try_mbus_fmt(), soc_camera_s_crop() and soc_camera_cropcap()
+ */
+ if (ret >= 0)
+ ret = reg_write(client, MT9T031_COLUMN_START, rect->left);
+ if (ret >= 0)
+ ret = reg_write(client, MT9T031_ROW_START, rect->top);
+ if (ret >= 0)
+ ret = reg_write(client, MT9T031_WINDOW_WIDTH, rect->width - 1);
+ if (ret >= 0)
+ ret = reg_write(client, MT9T031_WINDOW_HEIGHT,
+ rect->height + mt9t031->y_skip_top - 1);
+ if (ret >= 0 && v4l2_ctrl_g_ctrl(mt9t031->autoexposure) == V4L2_EXPOSURE_AUTO) {
+ mt9t031->total_h = rect->height + mt9t031->y_skip_top + vblank;
+
+ ret = set_shutter(client, mt9t031->total_h);
+ }
+
+ /* Re-enable register update, commit all changes */
+ if (ret >= 0)
+ ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 1);
+
+ if (ret >= 0) {
+ mt9t031->rect = *rect;
+ mt9t031->xskip = xskip;
+ mt9t031->yskip = yskip;
+ }
+
+ return ret < 0 ? ret : 0;
+}
+
+static int mt9t031_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct v4l2_rect rect = a->c;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
+
+ rect.width = ALIGN(rect.width, 2);
+ rect.height = ALIGN(rect.height, 2);
+
+ soc_camera_limit_side(&rect.left, &rect.width,
+ MT9T031_COLUMN_SKIP, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH);
+
+ soc_camera_limit_side(&rect.top, &rect.height,
+ MT9T031_ROW_SKIP, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT);
+
+ return mt9t031_set_params(client, &rect, mt9t031->xskip, mt9t031->yskip);
+}
+
+static int mt9t031_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
+
+ a->c = mt9t031->rect;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int mt9t031_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = MT9T031_COLUMN_SKIP;
+ a->bounds.top = MT9T031_ROW_SKIP;
+ a->bounds.width = MT9T031_MAX_WIDTH;
+ a->bounds.height = MT9T031_MAX_HEIGHT;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int mt9t031_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
+
+ mf->width = mt9t031->rect.width / mt9t031->xskip;
+ mf->height = mt9t031->rect.height / mt9t031->yskip;
+ mf->code = V4L2_MBUS_FMT_SBGGR10_1X10;
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int mt9t031_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
+ u16 xskip, yskip;
+ struct v4l2_rect rect = mt9t031->rect;
+
+ /*
+ * try_fmt has put width and height within limits.
+ * S_FMT: use binning and skipping for scaling
+ */
+ xskip = mt9t031_skip(&rect.width, mf->width, MT9T031_MAX_WIDTH);
+ yskip = mt9t031_skip(&rect.height, mf->height, MT9T031_MAX_HEIGHT);
+
+ mf->code = V4L2_MBUS_FMT_SBGGR10_1X10;
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
+
+ /* mt9t031_set_params() doesn't change width and height */
+ return mt9t031_set_params(client, &rect, xskip, yskip);
+}
+
+/*
+ * If a user window larger than sensor window is requested, we'll increase the
+ * sensor window.
+ */
+static int mt9t031_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ v4l_bound_align_image(
+ &mf->width, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH, 1,
+ &mf->height, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT, 1, 0);
+
+ mf->code = V4L2_MBUS_FMT_SBGGR10_1X10;
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
+
+ return 0;
+}
+
+static int mt9t031_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
+
+ if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+
+ if (id->match.addr != client->addr)
+ return -ENODEV;
+
+ id->ident = mt9t031->model;
+ id->revision = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int mt9t031_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ return -EINVAL;
+
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ reg->val = reg_read(client, reg->reg);
+
+ if (reg->val > 0xffff)
+ return -EIO;
+
+ return 0;
+}
+
+static int mt9t031_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ return -EINVAL;
+
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ if (reg_write(client, reg->reg, reg->val) < 0)
+ return -EIO;
+
+ return 0;
+}
+#endif
+
+static int mt9t031_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct mt9t031 *mt9t031 = container_of(ctrl->handler,
+ struct mt9t031, hdl);
+ const u32 shutter_max = MT9T031_MAX_HEIGHT + MT9T031_VERTICAL_BLANK;
+ s32 min, max;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE_AUTO:
+ min = mt9t031->exposure->minimum;
+ max = mt9t031->exposure->maximum;
+ mt9t031->exposure->val =
+ (shutter_max / 2 + (mt9t031->total_h - 1) * (max - min))
+ / shutter_max + min;
+ break;
+ }
+ return 0;
+}
+
+static int mt9t031_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct mt9t031 *mt9t031 = container_of(ctrl->handler,
+ struct mt9t031, hdl);
+ struct v4l2_subdev *sd = &mt9t031->subdev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct v4l2_ctrl *exp = mt9t031->exposure;
+ int data;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ if (ctrl->val)
+ data = reg_set(client, MT9T031_READ_MODE_2, 0x8000);
+ else
+ data = reg_clear(client, MT9T031_READ_MODE_2, 0x8000);
+ if (data < 0)
+ return -EIO;
+ return 0;
+ case V4L2_CID_HFLIP:
+ if (ctrl->val)
+ data = reg_set(client, MT9T031_READ_MODE_2, 0x4000);
+ else
+ data = reg_clear(client, MT9T031_READ_MODE_2, 0x4000);
+ if (data < 0)
+ return -EIO;
+ return 0;
+ case V4L2_CID_GAIN:
+ /* See Datasheet Table 7, Gain settings. */
+ if (ctrl->val <= ctrl->default_value) {
+ /* Pack it into 0..1 step 0.125, register values 0..8 */
+ unsigned long range = ctrl->default_value - ctrl->minimum;
+ data = ((ctrl->val - ctrl->minimum) * 8 + range / 2) / range;
+
+ dev_dbg(&client->dev, "Setting gain %d\n", data);
+ data = reg_write(client, MT9T031_GLOBAL_GAIN, data);
+ if (data < 0)
+ return -EIO;
+ } else {
+ /* Pack it into 1.125..128 variable step, register values 9..0x7860 */
+ /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */
+ unsigned long range = ctrl->maximum - ctrl->default_value - 1;
+ /* calculated gain: map 65..127 to 9..1024 step 0.125 */
+ unsigned long gain = ((ctrl->val - ctrl->default_value - 1) *
+ 1015 + range / 2) / range + 9;
+
+ if (gain <= 32) /* calculated gain 9..32 -> 9..32 */
+ data = gain;
+ else if (gain <= 64) /* calculated gain 33..64 -> 0x51..0x60 */
+ data = ((gain - 32) * 16 + 16) / 32 + 80;
+ else
+ /* calculated gain 65..1024 -> (1..120) << 8 + 0x60 */
+ data = (((gain - 64 + 7) * 32) & 0xff00) | 0x60;
+
+ dev_dbg(&client->dev, "Set gain from 0x%x to 0x%x\n",
+ reg_read(client, MT9T031_GLOBAL_GAIN), data);
+ data = reg_write(client, MT9T031_GLOBAL_GAIN, data);
+ if (data < 0)
+ return -EIO;
+ }
+ return 0;
+
+ case V4L2_CID_EXPOSURE_AUTO:
+ if (ctrl->val == V4L2_EXPOSURE_MANUAL) {
+ unsigned int range = exp->maximum - exp->minimum;
+ unsigned int shutter = ((exp->val - exp->minimum) * 1048 +
+ range / 2) / range + 1;
+ u32 old;
+
+ get_shutter(client, &old);
+ dev_dbg(&client->dev, "Set shutter from %u to %u\n",
+ old, shutter);
+ if (set_shutter(client, shutter) < 0)
+ return -EIO;
+ } else {
+ const u16 vblank = MT9T031_VERTICAL_BLANK;
+ mt9t031->total_h = mt9t031->rect.height +
+ mt9t031->y_skip_top + vblank;
+
+ if (set_shutter(client, mt9t031->total_h) < 0)
+ return -EIO;
+ }
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * Power Management:
+ * This function does nothing for now but must be present for pm to work
+ */
+static int mt9t031_runtime_suspend(struct device *dev)
+{
+ return 0;
+}
+
+/*
+ * Power Management:
+ * COLUMN_ADDRESS_MODE and ROW_ADDRESS_MODE are not rewritten if unchanged
+ * they are however changed at reset if the platform hook is present
+ * thus we rewrite them with the values stored by the driver
+ */
+static int mt9t031_runtime_resume(struct device *dev)
+{
+ struct video_device *vdev = to_video_device(dev);
+ struct v4l2_subdev *sd = soc_camera_vdev_to_subdev(vdev);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
+
+ int ret;
+ u16 xbin, ybin;
+
+ xbin = min(mt9t031->xskip, (u16)3);
+ ybin = min(mt9t031->yskip, (u16)3);
+
+ ret = reg_write(client, MT9T031_COLUMN_ADDRESS_MODE,
+ ((xbin - 1) << 4) | (mt9t031->xskip - 1));
+ if (ret < 0)
+ return ret;
+
+ ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE,
+ ((ybin - 1) << 4) | (mt9t031->yskip - 1));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct dev_pm_ops mt9t031_dev_pm_ops = {
+ .runtime_suspend = mt9t031_runtime_suspend,
+ .runtime_resume = mt9t031_runtime_resume,
+};
+
+static struct device_type mt9t031_dev_type = {
+ .name = "MT9T031",
+ .pm = &mt9t031_dev_pm_ops,
+};
+
+static int mt9t031_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct video_device *vdev = soc_camera_i2c_to_vdev(client);
+
+ if (on)
+ vdev->dev.type = &mt9t031_dev_type;
+ else
+ vdev->dev.type = NULL;
+
+ return 0;
+}
+
+/*
+ * Interface active, can use i2c. If it fails, it can indeed mean, that
+ * this wasn't our capture interface, so, we wait for the right one
+ */
+static int mt9t031_video_probe(struct i2c_client *client)
+{
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
+ s32 data;
+ int ret;
+
+ /* Enable the chip */
+ data = reg_write(client, MT9T031_CHIP_ENABLE, 1);
+ dev_dbg(&client->dev, "write: %d\n", data);
+
+ /* Read out the chip version register */
+ data = reg_read(client, MT9T031_CHIP_VERSION);
+
+ switch (data) {
+ case 0x1621:
+ mt9t031->model = V4L2_IDENT_MT9T031;
+ break;
+ default:
+ dev_err(&client->dev,
+ "No MT9T031 chip detected, register read %x\n", data);
+ return -ENODEV;
+ }
+
+ dev_info(&client->dev, "Detected a MT9T031 chip ID %x\n", data);
+
+ ret = mt9t031_idle(client);
+ if (ret < 0)
+ dev_err(&client->dev, "Failed to initialise the camera\n");
+ else
+ v4l2_ctrl_handler_setup(&mt9t031->hdl);
+
+ return ret;
+}
+
+static int mt9t031_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
+
+ *lines = mt9t031->y_skip_top;
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops mt9t031_ctrl_ops = {
+ .g_volatile_ctrl = mt9t031_g_volatile_ctrl,
+ .s_ctrl = mt9t031_s_ctrl,
+};
+
+static struct v4l2_subdev_core_ops mt9t031_subdev_core_ops = {
+ .g_chip_ident = mt9t031_g_chip_ident,
+ .s_power = mt9t031_s_power,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = mt9t031_g_register,
+ .s_register = mt9t031_s_register,
+#endif
+};
+
+static int mt9t031_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index)
+ return -EINVAL;
+
+ *code = V4L2_MBUS_FMT_SBGGR10_1X10;
+ return 0;
+}
+
+static int mt9t031_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
+ V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static int mt9t031_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ if (soc_camera_apply_board_flags(icl, cfg) &
+ V4L2_MBUS_PCLK_SAMPLE_FALLING)
+ return reg_clear(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
+ else
+ return reg_set(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
+}
+
+static struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = {
+ .s_stream = mt9t031_s_stream,
+ .s_mbus_fmt = mt9t031_s_fmt,
+ .g_mbus_fmt = mt9t031_g_fmt,
+ .try_mbus_fmt = mt9t031_try_fmt,
+ .s_crop = mt9t031_s_crop,
+ .g_crop = mt9t031_g_crop,
+ .cropcap = mt9t031_cropcap,
+ .enum_mbus_fmt = mt9t031_enum_fmt,
+ .g_mbus_config = mt9t031_g_mbus_config,
+ .s_mbus_config = mt9t031_s_mbus_config,
+};
+
+static struct v4l2_subdev_sensor_ops mt9t031_subdev_sensor_ops = {
+ .g_skip_top_lines = mt9t031_g_skip_top_lines,
+};
+
+static struct v4l2_subdev_ops mt9t031_subdev_ops = {
+ .core = &mt9t031_subdev_core_ops,
+ .video = &mt9t031_subdev_video_ops,
+ .sensor = &mt9t031_subdev_sensor_ops,
+};
+
+static int mt9t031_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct mt9t031 *mt9t031;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ int ret;
+
+ if (!icl) {
+ dev_err(&client->dev, "MT9T031 driver needs platform data\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_warn(&adapter->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
+ return -EIO;
+ }
+
+ mt9t031 = kzalloc(sizeof(struct mt9t031), GFP_KERNEL);
+ if (!mt9t031)
+ return -ENOMEM;
+
+ v4l2_i2c_subdev_init(&mt9t031->subdev, client, &mt9t031_subdev_ops);
+ v4l2_ctrl_handler_init(&mt9t031->hdl, 5);
+ v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
+ V4L2_CID_GAIN, 0, 127, 1, 64);
+
+ /*
+ * Simulated autoexposure. If enabled, we calculate shutter width
+ * ourselves in the driver based on vertical blanking and frame width
+ */
+ mt9t031->autoexposure = v4l2_ctrl_new_std_menu(&mt9t031->hdl,
+ &mt9t031_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
+ V4L2_EXPOSURE_AUTO);
+ mt9t031->exposure = v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
+ V4L2_CID_EXPOSURE, 1, 255, 1, 255);
+
+ mt9t031->subdev.ctrl_handler = &mt9t031->hdl;
+ if (mt9t031->hdl.error) {
+ int err = mt9t031->hdl.error;
+
+ kfree(mt9t031);
+ return err;
+ }
+ v4l2_ctrl_auto_cluster(2, &mt9t031->autoexposure,
+ V4L2_EXPOSURE_MANUAL, true);
+
+ mt9t031->y_skip_top = 0;
+ mt9t031->rect.left = MT9T031_COLUMN_SKIP;
+ mt9t031->rect.top = MT9T031_ROW_SKIP;
+ mt9t031->rect.width = MT9T031_MAX_WIDTH;
+ mt9t031->rect.height = MT9T031_MAX_HEIGHT;
+
+ mt9t031->xskip = 1;
+ mt9t031->yskip = 1;
+
+ mt9t031_idle(client);
+
+ ret = mt9t031_video_probe(client);
+
+ mt9t031_disable(client);
+
+ if (ret) {
+ v4l2_ctrl_handler_free(&mt9t031->hdl);
+ kfree(mt9t031);
+ }
+
+ return ret;
+}
+
+static int mt9t031_remove(struct i2c_client *client)
+{
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
+
+ v4l2_device_unregister_subdev(&mt9t031->subdev);
+ v4l2_ctrl_handler_free(&mt9t031->hdl);
+ kfree(mt9t031);
+
+ return 0;
+}
+
+static const struct i2c_device_id mt9t031_id[] = {
+ { "mt9t031", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mt9t031_id);
+
+static struct i2c_driver mt9t031_i2c_driver = {
+ .driver = {
+ .name = "mt9t031",
+ },
+ .probe = mt9t031_probe,
+ .remove = mt9t031_remove,
+ .id_table = mt9t031_id,
+};
+
+module_i2c_driver(mt9t031_i2c_driver);
+
+MODULE_DESCRIPTION("Micron MT9T031 Camera driver");
+MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/mt9t112.c b/drivers/media/video/mt9t112.c
new file mode 100644
index 00000000..8d1445f1
--- /dev/null
+++ b/drivers/media/video/mt9t112.c
@@ -0,0 +1,1124 @@
+/*
+ * mt9t112 Camera Driver
+ *
+ * Copyright (C) 2009 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on ov772x driver, mt9m111 driver,
+ *
+ * Copyright (C) 2008 Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ * Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr>
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
+ * Copyright (C) 2008 Magnus Damm
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/videodev2.h>
+
+#include <media/mt9t112.h>
+#include <media/soc_camera.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-common.h>
+
+/* you can check PLL/clock info */
+/* #define EXT_CLOCK 24000000 */
+
+/************************************************************************
+ macro
+************************************************************************/
+/*
+ * frame size
+ */
+#define MAX_WIDTH 2048
+#define MAX_HEIGHT 1536
+
+#define VGA_WIDTH 640
+#define VGA_HEIGHT 480
+
+/*
+ * macro of read/write
+ */
+#define ECHECKER(ret, x) \
+ do { \
+ (ret) = (x); \
+ if ((ret) < 0) \
+ return (ret); \
+ } while (0)
+
+#define mt9t112_reg_write(ret, client, a, b) \
+ ECHECKER(ret, __mt9t112_reg_write(client, a, b))
+#define mt9t112_mcu_write(ret, client, a, b) \
+ ECHECKER(ret, __mt9t112_mcu_write(client, a, b))
+
+#define mt9t112_reg_mask_set(ret, client, a, b, c) \
+ ECHECKER(ret, __mt9t112_reg_mask_set(client, a, b, c))
+#define mt9t112_mcu_mask_set(ret, client, a, b, c) \
+ ECHECKER(ret, __mt9t112_mcu_mask_set(client, a, b, c))
+
+#define mt9t112_reg_read(ret, client, a) \
+ ECHECKER(ret, __mt9t112_reg_read(client, a))
+
+/*
+ * Logical address
+ */
+#define _VAR(id, offset, base) (base | (id & 0x1f) << 10 | (offset & 0x3ff))
+#define VAR(id, offset) _VAR(id, offset, 0x0000)
+#define VAR8(id, offset) _VAR(id, offset, 0x8000)
+
+/************************************************************************
+ struct
+************************************************************************/
+struct mt9t112_format {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+ u16 fmt;
+ u16 order;
+};
+
+struct mt9t112_priv {
+ struct v4l2_subdev subdev;
+ struct mt9t112_camera_info *info;
+ struct i2c_client *client;
+ struct v4l2_rect frame;
+ const struct mt9t112_format *format;
+ int model;
+ u32 flags;
+/* for flags */
+#define INIT_DONE (1 << 0)
+#define PCLK_RISING (1 << 1)
+};
+
+/************************************************************************
+ supported format
+************************************************************************/
+
+static const struct mt9t112_format mt9t112_cfmts[] = {
+ {
+ .code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 0,
+ }, {
+ .code = V4L2_MBUS_FMT_VYUY8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 1,
+ }, {
+ .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 2,
+ }, {
+ .code = V4L2_MBUS_FMT_YVYU8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 3,
+ }, {
+ .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .fmt = 8,
+ .order = 2,
+ }, {
+ .code = V4L2_MBUS_FMT_RGB565_2X8_LE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .fmt = 4,
+ .order = 2,
+ },
+};
+
+/************************************************************************
+ general function
+************************************************************************/
+static struct mt9t112_priv *to_mt9t112(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client),
+ struct mt9t112_priv,
+ subdev);
+}
+
+static int __mt9t112_reg_read(const struct i2c_client *client, u16 command)
+{
+ struct i2c_msg msg[2];
+ u8 buf[2];
+ int ret;
+
+ command = swab16(command);
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 2;
+ msg[0].buf = (u8 *)&command;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 2;
+ msg[1].buf = buf;
+
+ /*
+ * if return value of this function is < 0,
+ * it mean error.
+ * else, under 16bit is valid data.
+ */
+ ret = i2c_transfer(client->adapter, msg, 2);
+ if (ret < 0)
+ return ret;
+
+ memcpy(&ret, buf, 2);
+ return swab16(ret);
+}
+
+static int __mt9t112_reg_write(const struct i2c_client *client,
+ u16 command, u16 data)
+{
+ struct i2c_msg msg;
+ u8 buf[4];
+ int ret;
+
+ command = swab16(command);
+ data = swab16(data);
+
+ memcpy(buf + 0, &command, 2);
+ memcpy(buf + 2, &data, 2);
+
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = 4;
+ msg.buf = buf;
+
+ /*
+ * i2c_transfer return message length,
+ * but this function should return 0 if correct case
+ */
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret >= 0)
+ ret = 0;
+
+ return ret;
+}
+
+static int __mt9t112_reg_mask_set(const struct i2c_client *client,
+ u16 command,
+ u16 mask,
+ u16 set)
+{
+ int val = __mt9t112_reg_read(client, command);
+ if (val < 0)
+ return val;
+
+ val &= ~mask;
+ val |= set & mask;
+
+ return __mt9t112_reg_write(client, command, val);
+}
+
+/* mcu access */
+static int __mt9t112_mcu_read(const struct i2c_client *client, u16 command)
+{
+ int ret;
+
+ ret = __mt9t112_reg_write(client, 0x098E, command);
+ if (ret < 0)
+ return ret;
+
+ return __mt9t112_reg_read(client, 0x0990);
+}
+
+static int __mt9t112_mcu_write(const struct i2c_client *client,
+ u16 command, u16 data)
+{
+ int ret;
+
+ ret = __mt9t112_reg_write(client, 0x098E, command);
+ if (ret < 0)
+ return ret;
+
+ return __mt9t112_reg_write(client, 0x0990, data);
+}
+
+static int __mt9t112_mcu_mask_set(const struct i2c_client *client,
+ u16 command,
+ u16 mask,
+ u16 set)
+{
+ int val = __mt9t112_mcu_read(client, command);
+ if (val < 0)
+ return val;
+
+ val &= ~mask;
+ val |= set & mask;
+
+ return __mt9t112_mcu_write(client, command, val);
+}
+
+static int mt9t112_reset(const struct i2c_client *client)
+{
+ int ret;
+
+ mt9t112_reg_mask_set(ret, client, 0x001a, 0x0001, 0x0001);
+ msleep(1);
+ mt9t112_reg_mask_set(ret, client, 0x001a, 0x0001, 0x0000);
+
+ return ret;
+}
+
+#ifndef EXT_CLOCK
+#define CLOCK_INFO(a, b)
+#else
+#define CLOCK_INFO(a, b) mt9t112_clock_info(a, b)
+static int mt9t112_clock_info(const struct i2c_client *client, u32 ext)
+{
+ int m, n, p1, p2, p3, p4, p5, p6, p7;
+ u32 vco, clk;
+ char *enable;
+
+ ext /= 1000; /* kbyte order */
+
+ mt9t112_reg_read(n, client, 0x0012);
+ p1 = n & 0x000f;
+ n = n >> 4;
+ p2 = n & 0x000f;
+ n = n >> 4;
+ p3 = n & 0x000f;
+
+ mt9t112_reg_read(n, client, 0x002a);
+ p4 = n & 0x000f;
+ n = n >> 4;
+ p5 = n & 0x000f;
+ n = n >> 4;
+ p6 = n & 0x000f;
+
+ mt9t112_reg_read(n, client, 0x002c);
+ p7 = n & 0x000f;
+
+ mt9t112_reg_read(n, client, 0x0010);
+ m = n & 0x00ff;
+ n = (n >> 8) & 0x003f;
+
+ enable = ((6000 > ext) || (54000 < ext)) ? "X" : "";
+ dev_dbg(&client->dev, "EXTCLK : %10u K %s\n", ext, enable);
+
+ vco = 2 * m * ext / (n+1);
+ enable = ((384000 > vco) || (768000 < vco)) ? "X" : "";
+ dev_dbg(&client->dev, "VCO : %10u K %s\n", vco, enable);
+
+ clk = vco / (p1+1) / (p2+1);
+ enable = (96000 < clk) ? "X" : "";
+ dev_dbg(&client->dev, "PIXCLK : %10u K %s\n", clk, enable);
+
+ clk = vco / (p3+1);
+ enable = (768000 < clk) ? "X" : "";
+ dev_dbg(&client->dev, "MIPICLK : %10u K %s\n", clk, enable);
+
+ clk = vco / (p6+1);
+ enable = (96000 < clk) ? "X" : "";
+ dev_dbg(&client->dev, "MCU CLK : %10u K %s\n", clk, enable);
+
+ clk = vco / (p5+1);
+ enable = (54000 < clk) ? "X" : "";
+ dev_dbg(&client->dev, "SOC CLK : %10u K %s\n", clk, enable);
+
+ clk = vco / (p4+1);
+ enable = (70000 < clk) ? "X" : "";
+ dev_dbg(&client->dev, "Sensor CLK : %10u K %s\n", clk, enable);
+
+ clk = vco / (p7+1);
+ dev_dbg(&client->dev, "External sensor : %10u K\n", clk);
+
+ clk = ext / (n+1);
+ enable = ((2000 > clk) || (24000 < clk)) ? "X" : "";
+ dev_dbg(&client->dev, "PFD : %10u K %s\n", clk, enable);
+
+ return 0;
+}
+#endif
+
+static void mt9t112_frame_check(u32 *width, u32 *height, u32 *left, u32 *top)
+{
+ soc_camera_limit_side(left, width, 0, 0, MAX_WIDTH);
+ soc_camera_limit_side(top, height, 0, 0, MAX_HEIGHT);
+}
+
+static int mt9t112_set_a_frame_size(const struct i2c_client *client,
+ u16 width,
+ u16 height)
+{
+ int ret;
+ u16 wstart = (MAX_WIDTH - width) / 2;
+ u16 hstart = (MAX_HEIGHT - height) / 2;
+
+ /* (Context A) Image Width/Height */
+ mt9t112_mcu_write(ret, client, VAR(26, 0), width);
+ mt9t112_mcu_write(ret, client, VAR(26, 2), height);
+
+ /* (Context A) Output Width/Height */
+ mt9t112_mcu_write(ret, client, VAR(18, 43), 8 + width);
+ mt9t112_mcu_write(ret, client, VAR(18, 45), 8 + height);
+
+ /* (Context A) Start Row/Column */
+ mt9t112_mcu_write(ret, client, VAR(18, 2), 4 + hstart);
+ mt9t112_mcu_write(ret, client, VAR(18, 4), 4 + wstart);
+
+ /* (Context A) End Row/Column */
+ mt9t112_mcu_write(ret, client, VAR(18, 6), 11 + height + hstart);
+ mt9t112_mcu_write(ret, client, VAR(18, 8), 11 + width + wstart);
+
+ mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06);
+
+ return ret;
+}
+
+static int mt9t112_set_pll_dividers(const struct i2c_client *client,
+ u8 m, u8 n,
+ u8 p1, u8 p2, u8 p3,
+ u8 p4, u8 p5, u8 p6,
+ u8 p7)
+{
+ int ret;
+ u16 val;
+
+ /* N/M */
+ val = (n << 8) |
+ (m << 0);
+ mt9t112_reg_mask_set(ret, client, 0x0010, 0x3fff, val);
+
+ /* P1/P2/P3 */
+ val = ((p3 & 0x0F) << 8) |
+ ((p2 & 0x0F) << 4) |
+ ((p1 & 0x0F) << 0);
+ mt9t112_reg_mask_set(ret, client, 0x0012, 0x0fff, val);
+
+ /* P4/P5/P6 */
+ val = (0x7 << 12) |
+ ((p6 & 0x0F) << 8) |
+ ((p5 & 0x0F) << 4) |
+ ((p4 & 0x0F) << 0);
+ mt9t112_reg_mask_set(ret, client, 0x002A, 0x7fff, val);
+
+ /* P7 */
+ val = (0x1 << 12) |
+ ((p7 & 0x0F) << 0);
+ mt9t112_reg_mask_set(ret, client, 0x002C, 0x100f, val);
+
+ return ret;
+}
+
+static int mt9t112_init_pll(const struct i2c_client *client)
+{
+ struct mt9t112_priv *priv = to_mt9t112(client);
+ int data, i, ret;
+
+ mt9t112_reg_mask_set(ret, client, 0x0014, 0x003, 0x0001);
+
+ /* PLL control: BYPASS PLL = 8517 */
+ mt9t112_reg_write(ret, client, 0x0014, 0x2145);
+
+ /* Replace these registers when new timing parameters are generated */
+ mt9t112_set_pll_dividers(client,
+ priv->info->divider.m,
+ priv->info->divider.n,
+ priv->info->divider.p1,
+ priv->info->divider.p2,
+ priv->info->divider.p3,
+ priv->info->divider.p4,
+ priv->info->divider.p5,
+ priv->info->divider.p6,
+ priv->info->divider.p7);
+
+ /*
+ * TEST_BYPASS on
+ * PLL_ENABLE on
+ * SEL_LOCK_DET on
+ * TEST_BYPASS off
+ */
+ mt9t112_reg_write(ret, client, 0x0014, 0x2525);
+ mt9t112_reg_write(ret, client, 0x0014, 0x2527);
+ mt9t112_reg_write(ret, client, 0x0014, 0x3427);
+ mt9t112_reg_write(ret, client, 0x0014, 0x3027);
+
+ mdelay(10);
+
+ /*
+ * PLL_BYPASS off
+ * Reference clock count
+ * I2C Master Clock Divider
+ */
+ mt9t112_reg_write(ret, client, 0x0014, 0x3046);
+ mt9t112_reg_write(ret, client, 0x0022, 0x0190);
+ mt9t112_reg_write(ret, client, 0x3B84, 0x0212);
+
+ /* External sensor clock is PLL bypass */
+ mt9t112_reg_write(ret, client, 0x002E, 0x0500);
+
+ mt9t112_reg_mask_set(ret, client, 0x0018, 0x0002, 0x0002);
+ mt9t112_reg_mask_set(ret, client, 0x3B82, 0x0004, 0x0004);
+
+ /* MCU disabled */
+ mt9t112_reg_mask_set(ret, client, 0x0018, 0x0004, 0x0004);
+
+ /* out of standby */
+ mt9t112_reg_mask_set(ret, client, 0x0018, 0x0001, 0);
+
+ mdelay(50);
+
+ /*
+ * Standby Workaround
+ * Disable Secondary I2C Pads
+ */
+ mt9t112_reg_write(ret, client, 0x0614, 0x0001);
+ mdelay(1);
+ mt9t112_reg_write(ret, client, 0x0614, 0x0001);
+ mdelay(1);
+ mt9t112_reg_write(ret, client, 0x0614, 0x0001);
+ mdelay(1);
+ mt9t112_reg_write(ret, client, 0x0614, 0x0001);
+ mdelay(1);
+ mt9t112_reg_write(ret, client, 0x0614, 0x0001);
+ mdelay(1);
+ mt9t112_reg_write(ret, client, 0x0614, 0x0001);
+ mdelay(1);
+
+ /* poll to verify out of standby. Must Poll this bit */
+ for (i = 0; i < 100; i++) {
+ mt9t112_reg_read(data, client, 0x0018);
+ if (!(0x4000 & data))
+ break;
+
+ mdelay(10);
+ }
+
+ return ret;
+}
+
+static int mt9t112_init_setting(const struct i2c_client *client)
+{
+
+ int ret;
+
+ /* Adaptive Output Clock (A) */
+ mt9t112_mcu_mask_set(ret, client, VAR(26, 160), 0x0040, 0x0000);
+
+ /* Read Mode (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 12), 0x0024);
+
+ /* Fine Correction (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 15), 0x00CC);
+
+ /* Fine IT Min (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 17), 0x01f1);
+
+ /* Fine IT Max Margin (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 19), 0x00fF);
+
+ /* Base Frame Lines (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 29), 0x032D);
+
+ /* Min Line Length (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 31), 0x073a);
+
+ /* Line Length (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 37), 0x07d0);
+
+ /* Adaptive Output Clock (B) */
+ mt9t112_mcu_mask_set(ret, client, VAR(27, 160), 0x0040, 0x0000);
+
+ /* Row Start (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 74), 0x004);
+
+ /* Column Start (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 76), 0x004);
+
+ /* Row End (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 78), 0x60B);
+
+ /* Column End (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 80), 0x80B);
+
+ /* Fine Correction (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 87), 0x008C);
+
+ /* Fine IT Min (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 89), 0x01F1);
+
+ /* Fine IT Max Margin (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 91), 0x00FF);
+
+ /* Base Frame Lines (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 101), 0x0668);
+
+ /* Min Line Length (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 103), 0x0AF0);
+
+ /* Line Length (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 109), 0x0AF0);
+
+ /*
+ * Flicker Dectection registers
+ * This section should be replaced whenever new Timing file is generated
+ * All the following registers need to be replaced
+ * Following registers are generated from Register Wizard but user can
+ * modify them. For detail see auto flicker detection tuning
+ */
+
+ /* FD_FDPERIOD_SELECT */
+ mt9t112_mcu_write(ret, client, VAR8(8, 5), 0x01);
+
+ /* PRI_B_CONFIG_FD_ALGO_RUN */
+ mt9t112_mcu_write(ret, client, VAR(27, 17), 0x0003);
+
+ /* PRI_A_CONFIG_FD_ALGO_RUN */
+ mt9t112_mcu_write(ret, client, VAR(26, 17), 0x0003);
+
+ /*
+ * AFD range detection tuning registers
+ */
+
+ /* search_f1_50 */
+ mt9t112_mcu_write(ret, client, VAR8(18, 165), 0x25);
+
+ /* search_f2_50 */
+ mt9t112_mcu_write(ret, client, VAR8(18, 166), 0x28);
+
+ /* search_f1_60 */
+ mt9t112_mcu_write(ret, client, VAR8(18, 167), 0x2C);
+
+ /* search_f2_60 */
+ mt9t112_mcu_write(ret, client, VAR8(18, 168), 0x2F);
+
+ /* period_50Hz (A) */
+ mt9t112_mcu_write(ret, client, VAR8(18, 68), 0xBA);
+
+ /* secret register by aptina */
+ /* period_50Hz (A MSB) */
+ mt9t112_mcu_write(ret, client, VAR8(18, 303), 0x00);
+
+ /* period_60Hz (A) */
+ mt9t112_mcu_write(ret, client, VAR8(18, 69), 0x9B);
+
+ /* secret register by aptina */
+ /* period_60Hz (A MSB) */
+ mt9t112_mcu_write(ret, client, VAR8(18, 301), 0x00);
+
+ /* period_50Hz (B) */
+ mt9t112_mcu_write(ret, client, VAR8(18, 140), 0x82);
+
+ /* secret register by aptina */
+ /* period_50Hz (B) MSB */
+ mt9t112_mcu_write(ret, client, VAR8(18, 304), 0x00);
+
+ /* period_60Hz (B) */
+ mt9t112_mcu_write(ret, client, VAR8(18, 141), 0x6D);
+
+ /* secret register by aptina */
+ /* period_60Hz (B) MSB */
+ mt9t112_mcu_write(ret, client, VAR8(18, 302), 0x00);
+
+ /* FD Mode */
+ mt9t112_mcu_write(ret, client, VAR8(8, 2), 0x10);
+
+ /* Stat_min */
+ mt9t112_mcu_write(ret, client, VAR8(8, 9), 0x02);
+
+ /* Stat_max */
+ mt9t112_mcu_write(ret, client, VAR8(8, 10), 0x03);
+
+ /* Min_amplitude */
+ mt9t112_mcu_write(ret, client, VAR8(8, 12), 0x0A);
+
+ /* RX FIFO Watermark (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 70), 0x0014);
+
+ /* RX FIFO Watermark (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 142), 0x0014);
+
+ /* MCLK: 16MHz
+ * PCLK: 73MHz
+ * CorePixCLK: 36.5 MHz
+ */
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x0044), 133);
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x0045), 110);
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x008c), 130);
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x008d), 108);
+
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x00A5), 27);
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x00a6), 30);
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x00a7), 32);
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x00a8), 35);
+
+ return ret;
+}
+
+static int mt9t112_auto_focus_setting(const struct i2c_client *client)
+{
+ int ret;
+
+ mt9t112_mcu_write(ret, client, VAR(12, 13), 0x000F);
+ mt9t112_mcu_write(ret, client, VAR(12, 23), 0x0F0F);
+ mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06);
+
+ mt9t112_reg_write(ret, client, 0x0614, 0x0000);
+
+ mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x05);
+ mt9t112_mcu_write(ret, client, VAR8(12, 2), 0x02);
+ mt9t112_mcu_write(ret, client, VAR(12, 3), 0x0002);
+ mt9t112_mcu_write(ret, client, VAR(17, 3), 0x8001);
+ mt9t112_mcu_write(ret, client, VAR(17, 11), 0x0025);
+ mt9t112_mcu_write(ret, client, VAR(17, 13), 0x0193);
+ mt9t112_mcu_write(ret, client, VAR8(17, 33), 0x18);
+ mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x05);
+
+ return ret;
+}
+
+static int mt9t112_auto_focus_trigger(const struct i2c_client *client)
+{
+ int ret;
+
+ mt9t112_mcu_write(ret, client, VAR8(12, 25), 0x01);
+
+ return ret;
+}
+
+static int mt9t112_init_camera(const struct i2c_client *client)
+{
+ int ret;
+
+ ECHECKER(ret, mt9t112_reset(client));
+
+ ECHECKER(ret, mt9t112_init_pll(client));
+
+ ECHECKER(ret, mt9t112_init_setting(client));
+
+ ECHECKER(ret, mt9t112_auto_focus_setting(client));
+
+ mt9t112_reg_mask_set(ret, client, 0x0018, 0x0004, 0);
+
+ /* Analog setting B */
+ mt9t112_reg_write(ret, client, 0x3084, 0x2409);
+ mt9t112_reg_write(ret, client, 0x3092, 0x0A49);
+ mt9t112_reg_write(ret, client, 0x3094, 0x4949);
+ mt9t112_reg_write(ret, client, 0x3096, 0x4950);
+
+ /*
+ * Disable adaptive clock
+ * PRI_A_CONFIG_JPEG_OB_TX_CONTROL_VAR
+ * PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR
+ */
+ mt9t112_mcu_write(ret, client, VAR(26, 160), 0x0A2E);
+ mt9t112_mcu_write(ret, client, VAR(27, 160), 0x0A2E);
+
+ /* Configure STatus in Status_before_length Format and enable header */
+ /* PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR */
+ mt9t112_mcu_write(ret, client, VAR(27, 144), 0x0CB4);
+
+ /* Enable JPEG in context B */
+ /* PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR */
+ mt9t112_mcu_write(ret, client, VAR8(27, 142), 0x01);
+
+ /* Disable Dac_TXLO */
+ mt9t112_reg_write(ret, client, 0x316C, 0x350F);
+
+ /* Set max slew rates */
+ mt9t112_reg_write(ret, client, 0x1E, 0x777);
+
+ return ret;
+}
+
+/************************************************************************
+ v4l2_subdev_core_ops
+************************************************************************/
+static int mt9t112_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t112_priv *priv = to_mt9t112(client);
+
+ id->ident = priv->model;
+ id->revision = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int mt9t112_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ reg->size = 2;
+ mt9t112_reg_read(ret, client, reg->reg);
+
+ reg->val = (__u64)ret;
+
+ return 0;
+}
+
+static int mt9t112_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ mt9t112_reg_write(ret, client, reg->reg, reg->val);
+
+ return ret;
+}
+#endif
+
+static struct v4l2_subdev_core_ops mt9t112_subdev_core_ops = {
+ .g_chip_ident = mt9t112_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = mt9t112_g_register,
+ .s_register = mt9t112_s_register,
+#endif
+};
+
+
+/************************************************************************
+ v4l2_subdev_video_ops
+************************************************************************/
+static int mt9t112_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t112_priv *priv = to_mt9t112(client);
+ int ret = 0;
+
+ if (!enable) {
+ /* FIXME
+ *
+ * If user selected large output size,
+ * and used it long time,
+ * mt9t112 camera will be very warm.
+ *
+ * But current driver can not stop mt9t112 camera.
+ * So, set small size here to solve this problem.
+ */
+ mt9t112_set_a_frame_size(client, VGA_WIDTH, VGA_HEIGHT);
+ return ret;
+ }
+
+ if (!(priv->flags & INIT_DONE)) {
+ u16 param = PCLK_RISING & priv->flags ? 0x0001 : 0x0000;
+
+ ECHECKER(ret, mt9t112_init_camera(client));
+
+ /* Invert PCLK (Data sampled on falling edge of pixclk) */
+ mt9t112_reg_write(ret, client, 0x3C20, param);
+
+ mdelay(5);
+
+ priv->flags |= INIT_DONE;
+ }
+
+ mt9t112_mcu_write(ret, client, VAR(26, 7), priv->format->fmt);
+ mt9t112_mcu_write(ret, client, VAR(26, 9), priv->format->order);
+ mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06);
+
+ mt9t112_set_a_frame_size(client,
+ priv->frame.width,
+ priv->frame.height);
+
+ ECHECKER(ret, mt9t112_auto_focus_trigger(client));
+
+ dev_dbg(&client->dev, "format : %d\n", priv->format->code);
+ dev_dbg(&client->dev, "size : %d x %d\n",
+ priv->frame.width,
+ priv->frame.height);
+
+ CLOCK_INFO(client, EXT_CLOCK);
+
+ return ret;
+}
+
+static int mt9t112_set_params(struct mt9t112_priv *priv,
+ const struct v4l2_rect *rect,
+ enum v4l2_mbus_pixelcode code)
+{
+ int i;
+
+ /*
+ * get color format
+ */
+ for (i = 0; i < ARRAY_SIZE(mt9t112_cfmts); i++)
+ if (mt9t112_cfmts[i].code == code)
+ break;
+
+ if (i == ARRAY_SIZE(mt9t112_cfmts))
+ return -EINVAL;
+
+ priv->frame = *rect;
+
+ /*
+ * frame size check
+ */
+ mt9t112_frame_check(&priv->frame.width, &priv->frame.height,
+ &priv->frame.left, &priv->frame.top);
+
+ priv->format = mt9t112_cfmts + i;
+
+ return 0;
+}
+
+static int mt9t112_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ a->bounds.width = MAX_WIDTH;
+ a->bounds.height = MAX_HEIGHT;
+ a->defrect.left = 0;
+ a->defrect.top = 0;
+ a->defrect.width = VGA_WIDTH;
+ a->defrect.height = VGA_HEIGHT;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int mt9t112_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t112_priv *priv = to_mt9t112(client);
+
+ a->c = priv->frame;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int mt9t112_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t112_priv *priv = to_mt9t112(client);
+ struct v4l2_rect *rect = &a->c;
+
+ return mt9t112_set_params(priv, rect, priv->format->code);
+}
+
+static int mt9t112_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t112_priv *priv = to_mt9t112(client);
+
+ mf->width = priv->frame.width;
+ mf->height = priv->frame.height;
+ mf->colorspace = priv->format->colorspace;
+ mf->code = priv->format->code;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int mt9t112_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t112_priv *priv = to_mt9t112(client);
+ struct v4l2_rect rect = {
+ .width = mf->width,
+ .height = mf->height,
+ .left = priv->frame.left,
+ .top = priv->frame.top,
+ };
+ int ret;
+
+ ret = mt9t112_set_params(priv, &rect, mf->code);
+
+ if (!ret)
+ mf->colorspace = priv->format->colorspace;
+
+ return ret;
+}
+
+static int mt9t112_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ unsigned int top, left;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mt9t112_cfmts); i++)
+ if (mt9t112_cfmts[i].code == mf->code)
+ break;
+
+ if (i == ARRAY_SIZE(mt9t112_cfmts)) {
+ mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ } else {
+ mf->colorspace = mt9t112_cfmts[i].colorspace;
+ }
+
+ mt9t112_frame_check(&mf->width, &mf->height, &left, &top);
+
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int mt9t112_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= ARRAY_SIZE(mt9t112_cfmts))
+ return -EINVAL;
+
+ *code = mt9t112_cfmts[index].code;
+
+ return 0;
+}
+
+static int mt9t112_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH |
+ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static int mt9t112_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct mt9t112_priv *priv = to_mt9t112(client);
+
+ if (soc_camera_apply_board_flags(icl, cfg) & V4L2_MBUS_PCLK_SAMPLE_RISING)
+ priv->flags |= PCLK_RISING;
+
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops mt9t112_subdev_video_ops = {
+ .s_stream = mt9t112_s_stream,
+ .g_mbus_fmt = mt9t112_g_fmt,
+ .s_mbus_fmt = mt9t112_s_fmt,
+ .try_mbus_fmt = mt9t112_try_fmt,
+ .cropcap = mt9t112_cropcap,
+ .g_crop = mt9t112_g_crop,
+ .s_crop = mt9t112_s_crop,
+ .enum_mbus_fmt = mt9t112_enum_fmt,
+ .g_mbus_config = mt9t112_g_mbus_config,
+ .s_mbus_config = mt9t112_s_mbus_config,
+};
+
+/************************************************************************
+ i2c driver
+************************************************************************/
+static struct v4l2_subdev_ops mt9t112_subdev_ops = {
+ .core = &mt9t112_subdev_core_ops,
+ .video = &mt9t112_subdev_video_ops,
+};
+
+static int mt9t112_camera_probe(struct i2c_client *client)
+{
+ struct mt9t112_priv *priv = to_mt9t112(client);
+ const char *devname;
+ int chipid;
+
+ /*
+ * check and show chip ID
+ */
+ mt9t112_reg_read(chipid, client, 0x0000);
+
+ switch (chipid) {
+ case 0x2680:
+ devname = "mt9t111";
+ priv->model = V4L2_IDENT_MT9T111;
+ break;
+ case 0x2682:
+ devname = "mt9t112";
+ priv->model = V4L2_IDENT_MT9T112;
+ break;
+ default:
+ dev_err(&client->dev, "Product ID error %04x\n", chipid);
+ return -ENODEV;
+ }
+
+ dev_info(&client->dev, "%s chip ID %04x\n", devname, chipid);
+
+ return 0;
+}
+
+static int mt9t112_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct mt9t112_priv *priv;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct v4l2_rect rect = {
+ .width = VGA_WIDTH,
+ .height = VGA_HEIGHT,
+ .left = (MAX_WIDTH - VGA_WIDTH) / 2,
+ .top = (MAX_HEIGHT - VGA_HEIGHT) / 2,
+ };
+ int ret;
+
+ if (!icl || !icl->priv) {
+ dev_err(&client->dev, "mt9t112: missing platform data!\n");
+ return -EINVAL;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->info = icl->priv;
+
+ v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops);
+
+ ret = mt9t112_camera_probe(client);
+ if (ret) {
+ kfree(priv);
+ return ret;
+ }
+
+ /* Cannot fail: using the default supported pixel code */
+ mt9t112_set_params(priv, &rect, V4L2_MBUS_FMT_UYVY8_2X8);
+
+ return ret;
+}
+
+static int mt9t112_remove(struct i2c_client *client)
+{
+ struct mt9t112_priv *priv = to_mt9t112(client);
+
+ kfree(priv);
+ return 0;
+}
+
+static const struct i2c_device_id mt9t112_id[] = {
+ { "mt9t112", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mt9t112_id);
+
+static struct i2c_driver mt9t112_i2c_driver = {
+ .driver = {
+ .name = "mt9t112",
+ },
+ .probe = mt9t112_probe,
+ .remove = mt9t112_remove,
+ .id_table = mt9t112_id,
+};
+
+module_i2c_driver(mt9t112_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for mt9t112");
+MODULE_AUTHOR("Kuninori Morimoto");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/mt9v011.c b/drivers/media/video/mt9v011.c
new file mode 100644
index 00000000..6bf01ad6
--- /dev/null
+++ b/drivers/media/video/mt9v011.c
@@ -0,0 +1,712 @@
+/*
+ * mt9v011 -Micron 1/4-Inch VGA Digital Image Sensor
+ *
+ * Copyright (c) 2009 Mauro Carvalho Chehab (mchehab@redhat.com)
+ * This code is placed under the terms of the GNU General Public License v2
+ */
+
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <asm/div64.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/mt9v011.h>
+
+MODULE_DESCRIPTION("Micron mt9v011 sensor driver");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_LICENSE("GPL");
+
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-2)");
+
+#define R00_MT9V011_CHIP_VERSION 0x00
+#define R01_MT9V011_ROWSTART 0x01
+#define R02_MT9V011_COLSTART 0x02
+#define R03_MT9V011_HEIGHT 0x03
+#define R04_MT9V011_WIDTH 0x04
+#define R05_MT9V011_HBLANK 0x05
+#define R06_MT9V011_VBLANK 0x06
+#define R07_MT9V011_OUT_CTRL 0x07
+#define R09_MT9V011_SHUTTER_WIDTH 0x09
+#define R0A_MT9V011_CLK_SPEED 0x0a
+#define R0B_MT9V011_RESTART 0x0b
+#define R0C_MT9V011_SHUTTER_DELAY 0x0c
+#define R0D_MT9V011_RESET 0x0d
+#define R1E_MT9V011_DIGITAL_ZOOM 0x1e
+#define R20_MT9V011_READ_MODE 0x20
+#define R2B_MT9V011_GREEN_1_GAIN 0x2b
+#define R2C_MT9V011_BLUE_GAIN 0x2c
+#define R2D_MT9V011_RED_GAIN 0x2d
+#define R2E_MT9V011_GREEN_2_GAIN 0x2e
+#define R35_MT9V011_GLOBAL_GAIN 0x35
+#define RF1_MT9V011_CHIP_ENABLE 0xf1
+
+#define MT9V011_VERSION 0x8232
+#define MT9V011_REV_B_VERSION 0x8243
+
+/* supported controls */
+static struct v4l2_queryctrl mt9v011_qctrl[] = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = (1 << 12) - 1 - 0x0020,
+ .step = 1,
+ .default_value = 0x0020,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 0,
+ .maximum = 2047,
+ .step = 1,
+ .default_value = 0x01fc,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Balance",
+ .minimum = -1 << 9,
+ .maximum = (1 << 9) - 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Balance",
+ .minimum = -1 << 9,
+ .maximum = (1 << 9) - 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mirror",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Vflip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ }, {
+ }
+};
+
+struct mt9v011 {
+ struct v4l2_subdev sd;
+ unsigned width, height;
+ unsigned xtal;
+ unsigned hflip:1;
+ unsigned vflip:1;
+
+ u16 global_gain, exposure;
+ s16 red_bal, blue_bal;
+};
+
+static inline struct mt9v011 *to_mt9v011(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct mt9v011, sd);
+}
+
+static int mt9v011_read(struct v4l2_subdev *sd, unsigned char addr)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
+ __be16 buffer;
+ int rc, val;
+
+ rc = i2c_master_send(c, &addr, 1);
+ if (rc != 1)
+ v4l2_dbg(0, debug, sd,
+ "i2c i/o error: rc == %d (should be 1)\n", rc);
+
+ msleep(10);
+
+ rc = i2c_master_recv(c, (char *)&buffer, 2);
+ if (rc != 2)
+ v4l2_dbg(0, debug, sd,
+ "i2c i/o error: rc == %d (should be 2)\n", rc);
+
+ val = be16_to_cpu(buffer);
+
+ v4l2_dbg(2, debug, sd, "mt9v011: read 0x%02x = 0x%04x\n", addr, val);
+
+ return val;
+}
+
+static void mt9v011_write(struct v4l2_subdev *sd, unsigned char addr,
+ u16 value)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
+ unsigned char buffer[3];
+ int rc;
+
+ buffer[0] = addr;
+ buffer[1] = value >> 8;
+ buffer[2] = value & 0xff;
+
+ v4l2_dbg(2, debug, sd,
+ "mt9v011: writing 0x%02x 0x%04x\n", buffer[0], value);
+ rc = i2c_master_send(c, buffer, 3);
+ if (rc != 3)
+ v4l2_dbg(0, debug, sd,
+ "i2c i/o error: rc == %d (should be 3)\n", rc);
+}
+
+
+struct i2c_reg_value {
+ unsigned char reg;
+ u16 value;
+};
+
+/*
+ * Values used at the original driver
+ * Some values are marked as Reserved at the datasheet
+ */
+static const struct i2c_reg_value mt9v011_init_default[] = {
+ { R0D_MT9V011_RESET, 0x0001 },
+ { R0D_MT9V011_RESET, 0x0000 },
+
+ { R0C_MT9V011_SHUTTER_DELAY, 0x0000 },
+ { R09_MT9V011_SHUTTER_WIDTH, 0x1fc },
+
+ { R0A_MT9V011_CLK_SPEED, 0x0000 },
+ { R1E_MT9V011_DIGITAL_ZOOM, 0x0000 },
+
+ { R07_MT9V011_OUT_CTRL, 0x0002 }, /* chip enable */
+};
+
+
+static u16 calc_mt9v011_gain(s16 lineargain)
+{
+
+ u16 digitalgain = 0;
+ u16 analogmult = 0;
+ u16 analoginit = 0;
+
+ if (lineargain < 0)
+ lineargain = 0;
+
+ /* recommended minimum */
+ lineargain += 0x0020;
+
+ if (lineargain > 2047)
+ lineargain = 2047;
+
+ if (lineargain > 1023) {
+ digitalgain = 3;
+ analogmult = 3;
+ analoginit = lineargain / 16;
+ } else if (lineargain > 511) {
+ digitalgain = 1;
+ analogmult = 3;
+ analoginit = lineargain / 8;
+ } else if (lineargain > 255) {
+ analogmult = 3;
+ analoginit = lineargain / 4;
+ } else if (lineargain > 127) {
+ analogmult = 1;
+ analoginit = lineargain / 2;
+ } else
+ analoginit = lineargain;
+
+ return analoginit + (analogmult << 7) + (digitalgain << 9);
+
+}
+
+static void set_balance(struct v4l2_subdev *sd)
+{
+ struct mt9v011 *core = to_mt9v011(sd);
+ u16 green_gain, blue_gain, red_gain;
+ u16 exposure;
+ s16 bal;
+
+ exposure = core->exposure;
+
+ green_gain = calc_mt9v011_gain(core->global_gain);
+
+ bal = core->global_gain;
+ bal += (core->blue_bal * core->global_gain / (1 << 7));
+ blue_gain = calc_mt9v011_gain(bal);
+
+ bal = core->global_gain;
+ bal += (core->red_bal * core->global_gain / (1 << 7));
+ red_gain = calc_mt9v011_gain(bal);
+
+ mt9v011_write(sd, R2B_MT9V011_GREEN_1_GAIN, green_gain);
+ mt9v011_write(sd, R2E_MT9V011_GREEN_2_GAIN, green_gain);
+ mt9v011_write(sd, R2C_MT9V011_BLUE_GAIN, blue_gain);
+ mt9v011_write(sd, R2D_MT9V011_RED_GAIN, red_gain);
+ mt9v011_write(sd, R09_MT9V011_SHUTTER_WIDTH, exposure);
+}
+
+static void calc_fps(struct v4l2_subdev *sd, u32 *numerator, u32 *denominator)
+{
+ struct mt9v011 *core = to_mt9v011(sd);
+ unsigned height, width, hblank, vblank, speed;
+ unsigned row_time, t_time;
+ u64 frames_per_ms;
+ unsigned tmp;
+
+ height = mt9v011_read(sd, R03_MT9V011_HEIGHT);
+ width = mt9v011_read(sd, R04_MT9V011_WIDTH);
+ hblank = mt9v011_read(sd, R05_MT9V011_HBLANK);
+ vblank = mt9v011_read(sd, R06_MT9V011_VBLANK);
+ speed = mt9v011_read(sd, R0A_MT9V011_CLK_SPEED);
+
+ row_time = (width + 113 + hblank) * (speed + 2);
+ t_time = row_time * (height + vblank + 1);
+
+ frames_per_ms = core->xtal * 1000l;
+ do_div(frames_per_ms, t_time);
+ tmp = frames_per_ms;
+
+ v4l2_dbg(1, debug, sd, "Programmed to %u.%03u fps (%d pixel clcks)\n",
+ tmp / 1000, tmp % 1000, t_time);
+
+ if (numerator && denominator) {
+ *numerator = 1000;
+ *denominator = (u32)frames_per_ms;
+ }
+}
+
+static u16 calc_speed(struct v4l2_subdev *sd, u32 numerator, u32 denominator)
+{
+ struct mt9v011 *core = to_mt9v011(sd);
+ unsigned height, width, hblank, vblank;
+ unsigned row_time, line_time;
+ u64 t_time, speed;
+
+ /* Avoid bogus calculus */
+ if (!numerator || !denominator)
+ return 0;
+
+ height = mt9v011_read(sd, R03_MT9V011_HEIGHT);
+ width = mt9v011_read(sd, R04_MT9V011_WIDTH);
+ hblank = mt9v011_read(sd, R05_MT9V011_HBLANK);
+ vblank = mt9v011_read(sd, R06_MT9V011_VBLANK);
+
+ row_time = width + 113 + hblank;
+ line_time = height + vblank + 1;
+
+ t_time = core->xtal * ((u64)numerator);
+ /* round to the closest value */
+ t_time += denominator / 2;
+ do_div(t_time, denominator);
+
+ speed = t_time;
+ do_div(speed, row_time * line_time);
+
+ /* Avoid having a negative value for speed */
+ if (speed < 2)
+ speed = 0;
+ else
+ speed -= 2;
+
+ /* Avoid speed overflow */
+ if (speed > 15)
+ return 15;
+
+ return (u16)speed;
+}
+
+static void set_res(struct v4l2_subdev *sd)
+{
+ struct mt9v011 *core = to_mt9v011(sd);
+ unsigned vstart, hstart;
+
+ /*
+ * The mt9v011 doesn't have scaling. So, in order to select the desired
+ * resolution, we're cropping at the middle of the sensor.
+ * hblank and vblank should be adjusted, in order to warrant that
+ * we'll preserve the line timings for 30 fps, no matter what resolution
+ * is selected.
+ * NOTE: datasheet says that width (and height) should be filled with
+ * width-1. However, this doesn't work, since one pixel per line will
+ * be missing.
+ */
+
+ hstart = 20 + (640 - core->width) / 2;
+ mt9v011_write(sd, R02_MT9V011_COLSTART, hstart);
+ mt9v011_write(sd, R04_MT9V011_WIDTH, core->width);
+ mt9v011_write(sd, R05_MT9V011_HBLANK, 771 - core->width);
+
+ vstart = 8 + (480 - core->height) / 2;
+ mt9v011_write(sd, R01_MT9V011_ROWSTART, vstart);
+ mt9v011_write(sd, R03_MT9V011_HEIGHT, core->height);
+ mt9v011_write(sd, R06_MT9V011_VBLANK, 508 - core->height);
+
+ calc_fps(sd, NULL, NULL);
+};
+
+static void set_read_mode(struct v4l2_subdev *sd)
+{
+ struct mt9v011 *core = to_mt9v011(sd);
+ unsigned mode = 0x1000;
+
+ if (core->hflip)
+ mode |= 0x4000;
+
+ if (core->vflip)
+ mode |= 0x8000;
+
+ mt9v011_write(sd, R20_MT9V011_READ_MODE, mode);
+}
+
+static int mt9v011_reset(struct v4l2_subdev *sd, u32 val)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mt9v011_init_default); i++)
+ mt9v011_write(sd, mt9v011_init_default[i].reg,
+ mt9v011_init_default[i].value);
+
+ set_balance(sd);
+ set_res(sd);
+ set_read_mode(sd);
+
+ return 0;
+};
+
+static int mt9v011_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct mt9v011 *core = to_mt9v011(sd);
+
+ v4l2_dbg(1, debug, sd, "g_ctrl called\n");
+
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ ctrl->value = core->global_gain;
+ return 0;
+ case V4L2_CID_EXPOSURE:
+ ctrl->value = core->exposure;
+ return 0;
+ case V4L2_CID_RED_BALANCE:
+ ctrl->value = core->red_bal;
+ return 0;
+ case V4L2_CID_BLUE_BALANCE:
+ ctrl->value = core->blue_bal;
+ return 0;
+ case V4L2_CID_HFLIP:
+ ctrl->value = core->hflip ? 1 : 0;
+ return 0;
+ case V4L2_CID_VFLIP:
+ ctrl->value = core->vflip ? 1 : 0;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int mt9v011_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ int i;
+
+ v4l2_dbg(1, debug, sd, "queryctrl called\n");
+
+ for (i = 0; i < ARRAY_SIZE(mt9v011_qctrl); i++)
+ if (qc->id && qc->id == mt9v011_qctrl[i].id) {
+ memcpy(qc, &(mt9v011_qctrl[i]),
+ sizeof(*qc));
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+
+static int mt9v011_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct mt9v011 *core = to_mt9v011(sd);
+ u8 i, n;
+ n = ARRAY_SIZE(mt9v011_qctrl);
+
+ for (i = 0; i < n; i++) {
+ if (ctrl->id != mt9v011_qctrl[i].id)
+ continue;
+ if (ctrl->value < mt9v011_qctrl[i].minimum ||
+ ctrl->value > mt9v011_qctrl[i].maximum)
+ return -ERANGE;
+ v4l2_dbg(1, debug, sd, "s_ctrl: id=%d, value=%d\n",
+ ctrl->id, ctrl->value);
+ break;
+ }
+
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ core->global_gain = ctrl->value;
+ break;
+ case V4L2_CID_EXPOSURE:
+ core->exposure = ctrl->value;
+ break;
+ case V4L2_CID_RED_BALANCE:
+ core->red_bal = ctrl->value;
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ core->blue_bal = ctrl->value;
+ break;
+ case V4L2_CID_HFLIP:
+ core->hflip = ctrl->value;
+ set_read_mode(sd);
+ return 0;
+ case V4L2_CID_VFLIP:
+ core->vflip = ctrl->value;
+ set_read_mode(sd);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ set_balance(sd);
+
+ return 0;
+}
+
+static int mt9v011_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index > 0)
+ return -EINVAL;
+
+ *code = V4L2_MBUS_FMT_SGRBG8_1X8;
+ return 0;
+}
+
+static int mt9v011_try_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt)
+{
+ if (fmt->code != V4L2_MBUS_FMT_SGRBG8_1X8)
+ return -EINVAL;
+
+ v4l_bound_align_image(&fmt->width, 48, 639, 1,
+ &fmt->height, 32, 480, 1, 0);
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+
+ return 0;
+}
+
+static int mt9v011_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+{
+ struct v4l2_captureparm *cp = &parms->parm.capture;
+
+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ memset(cp, 0, sizeof(struct v4l2_captureparm));
+ cp->capability = V4L2_CAP_TIMEPERFRAME;
+ calc_fps(sd,
+ &cp->timeperframe.numerator,
+ &cp->timeperframe.denominator);
+
+ return 0;
+}
+
+static int mt9v011_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+{
+ struct v4l2_captureparm *cp = &parms->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+ u16 speed;
+
+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (cp->extendedmode != 0)
+ return -EINVAL;
+
+ speed = calc_speed(sd, tpf->numerator, tpf->denominator);
+
+ mt9v011_write(sd, R0A_MT9V011_CLK_SPEED, speed);
+ v4l2_dbg(1, debug, sd, "Setting speed to %d\n", speed);
+
+ /* Recalculate and update fps info */
+ calc_fps(sd, &tpf->numerator, &tpf->denominator);
+
+ return 0;
+}
+
+static int mt9v011_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt)
+{
+ struct mt9v011 *core = to_mt9v011(sd);
+ int rc;
+
+ rc = mt9v011_try_mbus_fmt(sd, fmt);
+ if (rc < 0)
+ return -EINVAL;
+
+ core->width = fmt->width;
+ core->height = fmt->height;
+
+ set_res(sd);
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int mt9v011_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ reg->val = mt9v011_read(sd, reg->reg & 0xff);
+ reg->size = 2;
+
+ return 0;
+}
+
+static int mt9v011_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ mt9v011_write(sd, reg->reg & 0xff, reg->val & 0xffff);
+
+ return 0;
+}
+#endif
+
+static int mt9v011_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ u16 version;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_MT9V011,
+ version);
+}
+
+static const struct v4l2_subdev_core_ops mt9v011_core_ops = {
+ .queryctrl = mt9v011_queryctrl,
+ .g_ctrl = mt9v011_g_ctrl,
+ .s_ctrl = mt9v011_s_ctrl,
+ .reset = mt9v011_reset,
+ .g_chip_ident = mt9v011_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = mt9v011_g_register,
+ .s_register = mt9v011_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops mt9v011_video_ops = {
+ .enum_mbus_fmt = mt9v011_enum_mbus_fmt,
+ .try_mbus_fmt = mt9v011_try_mbus_fmt,
+ .s_mbus_fmt = mt9v011_s_mbus_fmt,
+ .g_parm = mt9v011_g_parm,
+ .s_parm = mt9v011_s_parm,
+};
+
+static const struct v4l2_subdev_ops mt9v011_ops = {
+ .core = &mt9v011_core_ops,
+ .video = &mt9v011_video_ops,
+};
+
+
+/****************************************************************************
+ I2C Client & Driver
+ ****************************************************************************/
+
+static int mt9v011_probe(struct i2c_client *c,
+ const struct i2c_device_id *id)
+{
+ u16 version;
+ struct mt9v011 *core;
+ struct v4l2_subdev *sd;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(c->adapter,
+ I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
+ return -EIO;
+
+ core = kzalloc(sizeof(struct mt9v011), GFP_KERNEL);
+ if (!core)
+ return -ENOMEM;
+
+ sd = &core->sd;
+ v4l2_i2c_subdev_init(sd, c, &mt9v011_ops);
+
+ /* Check if the sensor is really a MT9V011 */
+ version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION);
+ if ((version != MT9V011_VERSION) &&
+ (version != MT9V011_REV_B_VERSION)) {
+ v4l2_info(sd, "*** unknown micron chip detected (0x%04x).\n",
+ version);
+ kfree(core);
+ return -EINVAL;
+ }
+
+ core->global_gain = 0x0024;
+ core->exposure = 0x01fc;
+ core->width = 640;
+ core->height = 480;
+ core->xtal = 27000000; /* Hz */
+
+ if (c->dev.platform_data) {
+ struct mt9v011_platform_data *pdata = c->dev.platform_data;
+
+ core->xtal = pdata->xtal;
+ v4l2_dbg(1, debug, sd, "xtal set to %d.%03d MHz\n",
+ core->xtal / 1000000, (core->xtal / 1000) % 1000);
+ }
+
+ v4l_info(c, "chip found @ 0x%02x (%s - chip version 0x%04x)\n",
+ c->addr << 1, c->adapter->name, version);
+
+ return 0;
+}
+
+static int mt9v011_remove(struct i2c_client *c)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(c);
+
+ v4l2_dbg(1, debug, sd,
+ "mt9v011.c: removing mt9v011 adapter on address 0x%x\n",
+ c->addr << 1);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_mt9v011(sd));
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct i2c_device_id mt9v011_id[] = {
+ { "mt9v011", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mt9v011_id);
+
+static struct i2c_driver mt9v011_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "mt9v011",
+ },
+ .probe = mt9v011_probe,
+ .remove = mt9v011_remove,
+ .id_table = mt9v011_id,
+};
+
+module_i2c_driver(mt9v011_driver);
diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c
new file mode 100644
index 00000000..bf63417a
--- /dev/null
+++ b/drivers/media/video/mt9v022.c
@@ -0,0 +1,879 @@
+/*
+ * Driver for MT9V022 CMOS Image Sensor from Micron
+ *
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+
+/*
+ * mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c
+ * The platform has to define ctruct i2c_board_info objects and link to them
+ * from struct soc_camera_link
+ */
+
+static char *sensor_type;
+module_param(sensor_type, charp, S_IRUGO);
+MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\"");
+
+/* mt9v022 selected register addresses */
+#define MT9V022_CHIP_VERSION 0x00
+#define MT9V022_COLUMN_START 0x01
+#define MT9V022_ROW_START 0x02
+#define MT9V022_WINDOW_HEIGHT 0x03
+#define MT9V022_WINDOW_WIDTH 0x04
+#define MT9V022_HORIZONTAL_BLANKING 0x05
+#define MT9V022_VERTICAL_BLANKING 0x06
+#define MT9V022_CHIP_CONTROL 0x07
+#define MT9V022_SHUTTER_WIDTH1 0x08
+#define MT9V022_SHUTTER_WIDTH2 0x09
+#define MT9V022_SHUTTER_WIDTH_CTRL 0x0a
+#define MT9V022_TOTAL_SHUTTER_WIDTH 0x0b
+#define MT9V022_RESET 0x0c
+#define MT9V022_READ_MODE 0x0d
+#define MT9V022_MONITOR_MODE 0x0e
+#define MT9V022_PIXEL_OPERATION_MODE 0x0f
+#define MT9V022_LED_OUT_CONTROL 0x1b
+#define MT9V022_ADC_MODE_CONTROL 0x1c
+#define MT9V022_ANALOG_GAIN 0x35
+#define MT9V022_BLACK_LEVEL_CALIB_CTRL 0x47
+#define MT9V022_PIXCLK_FV_LV 0x74
+#define MT9V022_DIGITAL_TEST_PATTERN 0x7f
+#define MT9V022_AEC_AGC_ENABLE 0xAF
+#define MT9V022_MAX_TOTAL_SHUTTER_WIDTH 0xBD
+
+/* Progressive scan, master, defaults */
+#define MT9V022_CHIP_CONTROL_DEFAULT 0x188
+
+#define MT9V022_MAX_WIDTH 752
+#define MT9V022_MAX_HEIGHT 480
+#define MT9V022_MIN_WIDTH 48
+#define MT9V022_MIN_HEIGHT 32
+#define MT9V022_COLUMN_SKIP 1
+#define MT9V022_ROW_SKIP 4
+
+/* MT9V022 has only one fixed colorspace per pixelcode */
+struct mt9v022_datafmt {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+};
+
+/* Find a data format by a pixel code in an array */
+static const struct mt9v022_datafmt *mt9v022_find_datafmt(
+ enum v4l2_mbus_pixelcode code, const struct mt9v022_datafmt *fmt,
+ int n)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ if (fmt[i].code == code)
+ return fmt + i;
+
+ return NULL;
+}
+
+static const struct mt9v022_datafmt mt9v022_colour_fmts[] = {
+ /*
+ * Order important: first natively supported,
+ * second supported with a GPIO extender
+ */
+ {V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
+};
+
+static const struct mt9v022_datafmt mt9v022_monochrome_fmts[] = {
+ /* Order important - see above */
+ {V4L2_MBUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG},
+ {V4L2_MBUS_FMT_Y8_1X8, V4L2_COLORSPACE_JPEG},
+};
+
+struct mt9v022 {
+ struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
+ struct {
+ /* exposure/auto-exposure cluster */
+ struct v4l2_ctrl *autoexposure;
+ struct v4l2_ctrl *exposure;
+ };
+ struct {
+ /* gain/auto-gain cluster */
+ struct v4l2_ctrl *autogain;
+ struct v4l2_ctrl *gain;
+ };
+ struct v4l2_rect rect; /* Sensor window */
+ const struct mt9v022_datafmt *fmt;
+ const struct mt9v022_datafmt *fmts;
+ int num_fmts;
+ int model; /* V4L2_IDENT_MT9V022* codes from v4l2-chip-ident.h */
+ u16 chip_control;
+ unsigned short y_skip_top; /* Lines to skip at the top */
+};
+
+static struct mt9v022 *to_mt9v022(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct mt9v022, subdev);
+}
+
+static int reg_read(struct i2c_client *client, const u8 reg)
+{
+ return i2c_smbus_read_word_swapped(client, reg);
+}
+
+static int reg_write(struct i2c_client *client, const u8 reg,
+ const u16 data)
+{
+ return i2c_smbus_write_word_swapped(client, reg, data);
+}
+
+static int reg_set(struct i2c_client *client, const u8 reg,
+ const u16 data)
+{
+ int ret;
+
+ ret = reg_read(client, reg);
+ if (ret < 0)
+ return ret;
+ return reg_write(client, reg, ret | data);
+}
+
+static int reg_clear(struct i2c_client *client, const u8 reg,
+ const u16 data)
+{
+ int ret;
+
+ ret = reg_read(client, reg);
+ if (ret < 0)
+ return ret;
+ return reg_write(client, reg, ret & ~data);
+}
+
+static int mt9v022_init(struct i2c_client *client)
+{
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+ int ret;
+
+ /*
+ * Almost the default mode: master, parallel, simultaneous, and an
+ * undocumented bit 0x200, which is present in table 7, but not in 8,
+ * plus snapshot mode to disable scan for now
+ */
+ mt9v022->chip_control |= 0x10;
+ ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control);
+ if (!ret)
+ ret = reg_write(client, MT9V022_READ_MODE, 0x300);
+
+ /* All defaults */
+ if (!ret)
+ /* AEC, AGC on */
+ ret = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x3);
+ if (!ret)
+ ret = reg_write(client, MT9V022_ANALOG_GAIN, 16);
+ if (!ret)
+ ret = reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, 480);
+ if (!ret)
+ ret = reg_write(client, MT9V022_MAX_TOTAL_SHUTTER_WIDTH, 480);
+ if (!ret)
+ /* default - auto */
+ ret = reg_clear(client, MT9V022_BLACK_LEVEL_CALIB_CTRL, 1);
+ if (!ret)
+ ret = reg_write(client, MT9V022_DIGITAL_TEST_PATTERN, 0);
+ if (!ret)
+ return v4l2_ctrl_handler_setup(&mt9v022->hdl);
+
+ return ret;
+}
+
+static int mt9v022_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+
+ if (enable)
+ /* Switch to master "normal" mode */
+ mt9v022->chip_control &= ~0x10;
+ else
+ /* Switch to snapshot mode */
+ mt9v022->chip_control |= 0x10;
+
+ if (reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control) < 0)
+ return -EIO;
+ return 0;
+}
+
+static int mt9v022_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+ struct v4l2_rect rect = a->c;
+ int ret;
+
+ /* Bayer format - even size lengths */
+ if (mt9v022->fmts == mt9v022_colour_fmts) {
+ rect.width = ALIGN(rect.width, 2);
+ rect.height = ALIGN(rect.height, 2);
+ /* Let the user play with the starting pixel */
+ }
+
+ soc_camera_limit_side(&rect.left, &rect.width,
+ MT9V022_COLUMN_SKIP, MT9V022_MIN_WIDTH, MT9V022_MAX_WIDTH);
+
+ soc_camera_limit_side(&rect.top, &rect.height,
+ MT9V022_ROW_SKIP, MT9V022_MIN_HEIGHT, MT9V022_MAX_HEIGHT);
+
+ /* Like in example app. Contradicts the datasheet though */
+ ret = reg_read(client, MT9V022_AEC_AGC_ENABLE);
+ if (ret >= 0) {
+ if (ret & 1) /* Autoexposure */
+ ret = reg_write(client, MT9V022_MAX_TOTAL_SHUTTER_WIDTH,
+ rect.height + mt9v022->y_skip_top + 43);
+ else
+ ret = reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH,
+ rect.height + mt9v022->y_skip_top + 43);
+ }
+ /* Setup frame format: defaults apart from width and height */
+ if (!ret)
+ ret = reg_write(client, MT9V022_COLUMN_START, rect.left);
+ if (!ret)
+ ret = reg_write(client, MT9V022_ROW_START, rect.top);
+ if (!ret)
+ /*
+ * Default 94, Phytec driver says:
+ * "width + horizontal blank >= 660"
+ */
+ ret = reg_write(client, MT9V022_HORIZONTAL_BLANKING,
+ rect.width > 660 - 43 ? 43 :
+ 660 - rect.width);
+ if (!ret)
+ ret = reg_write(client, MT9V022_VERTICAL_BLANKING, 45);
+ if (!ret)
+ ret = reg_write(client, MT9V022_WINDOW_WIDTH, rect.width);
+ if (!ret)
+ ret = reg_write(client, MT9V022_WINDOW_HEIGHT,
+ rect.height + mt9v022->y_skip_top);
+
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(&client->dev, "Frame %dx%d pixel\n", rect.width, rect.height);
+
+ mt9v022->rect = rect;
+
+ return 0;
+}
+
+static int mt9v022_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+
+ a->c = mt9v022->rect;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int mt9v022_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = MT9V022_COLUMN_SKIP;
+ a->bounds.top = MT9V022_ROW_SKIP;
+ a->bounds.width = MT9V022_MAX_WIDTH;
+ a->bounds.height = MT9V022_MAX_HEIGHT;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int mt9v022_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+
+ mf->width = mt9v022->rect.width;
+ mf->height = mt9v022->rect.height;
+ mf->code = mt9v022->fmt->code;
+ mf->colorspace = mt9v022->fmt->colorspace;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int mt9v022_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+ struct v4l2_crop a = {
+ .c = {
+ .left = mt9v022->rect.left,
+ .top = mt9v022->rect.top,
+ .width = mf->width,
+ .height = mf->height,
+ },
+ };
+ int ret;
+
+ /*
+ * The caller provides a supported format, as verified per call to
+ * .try_mbus_fmt(), datawidth is from our supported format list
+ */
+ switch (mf->code) {
+ case V4L2_MBUS_FMT_Y8_1X8:
+ case V4L2_MBUS_FMT_Y10_1X10:
+ if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATM)
+ return -EINVAL;
+ break;
+ case V4L2_MBUS_FMT_SBGGR8_1X8:
+ case V4L2_MBUS_FMT_SBGGR10_1X10:
+ if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATC)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* No support for scaling on this camera, just crop. */
+ ret = mt9v022_s_crop(sd, &a);
+ if (!ret) {
+ mf->width = mt9v022->rect.width;
+ mf->height = mt9v022->rect.height;
+ mt9v022->fmt = mt9v022_find_datafmt(mf->code,
+ mt9v022->fmts, mt9v022->num_fmts);
+ mf->colorspace = mt9v022->fmt->colorspace;
+ }
+
+ return ret;
+}
+
+static int mt9v022_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+ const struct mt9v022_datafmt *fmt;
+ int align = mf->code == V4L2_MBUS_FMT_SBGGR8_1X8 ||
+ mf->code == V4L2_MBUS_FMT_SBGGR10_1X10;
+
+ v4l_bound_align_image(&mf->width, MT9V022_MIN_WIDTH,
+ MT9V022_MAX_WIDTH, align,
+ &mf->height, MT9V022_MIN_HEIGHT + mt9v022->y_skip_top,
+ MT9V022_MAX_HEIGHT + mt9v022->y_skip_top, align, 0);
+
+ fmt = mt9v022_find_datafmt(mf->code, mt9v022->fmts,
+ mt9v022->num_fmts);
+ if (!fmt) {
+ fmt = mt9v022->fmt;
+ mf->code = fmt->code;
+ }
+
+ mf->colorspace = fmt->colorspace;
+
+ return 0;
+}
+
+static int mt9v022_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+
+ if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+
+ if (id->match.addr != client->addr)
+ return -ENODEV;
+
+ id->ident = mt9v022->model;
+ id->revision = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int mt9v022_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ return -EINVAL;
+
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ reg->size = 2;
+ reg->val = reg_read(client, reg->reg);
+
+ if (reg->val > 0xffff)
+ return -EIO;
+
+ return 0;
+}
+
+static int mt9v022_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ return -EINVAL;
+
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ if (reg_write(client, reg->reg, reg->val) < 0)
+ return -EIO;
+
+ return 0;
+}
+#endif
+
+static int mt9v022_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct mt9v022 *mt9v022 = container_of(ctrl->handler,
+ struct mt9v022, hdl);
+ struct v4l2_subdev *sd = &mt9v022->subdev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct v4l2_ctrl *gain = mt9v022->gain;
+ struct v4l2_ctrl *exp = mt9v022->exposure;
+ unsigned long range;
+ int data;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTOGAIN:
+ data = reg_read(client, MT9V022_ANALOG_GAIN);
+ if (data < 0)
+ return -EIO;
+
+ range = gain->maximum - gain->minimum;
+ gain->val = ((data - 16) * range + 24) / 48 + gain->minimum;
+ return 0;
+ case V4L2_CID_EXPOSURE_AUTO:
+ data = reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH);
+ if (data < 0)
+ return -EIO;
+
+ range = exp->maximum - exp->minimum;
+ exp->val = ((data - 1) * range + 239) / 479 + exp->minimum;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int mt9v022_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct mt9v022 *mt9v022 = container_of(ctrl->handler,
+ struct mt9v022, hdl);
+ struct v4l2_subdev *sd = &mt9v022->subdev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int data;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ if (ctrl->val)
+ data = reg_set(client, MT9V022_READ_MODE, 0x10);
+ else
+ data = reg_clear(client, MT9V022_READ_MODE, 0x10);
+ if (data < 0)
+ return -EIO;
+ return 0;
+ case V4L2_CID_HFLIP:
+ if (ctrl->val)
+ data = reg_set(client, MT9V022_READ_MODE, 0x20);
+ else
+ data = reg_clear(client, MT9V022_READ_MODE, 0x20);
+ if (data < 0)
+ return -EIO;
+ return 0;
+ case V4L2_CID_AUTOGAIN:
+ if (ctrl->val) {
+ if (reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0)
+ return -EIO;
+ } else {
+ struct v4l2_ctrl *gain = mt9v022->gain;
+ /* mt9v022 has minimum == default */
+ unsigned long range = gain->maximum - gain->minimum;
+ /* Valid values 16 to 64, 32 to 64 must be even. */
+ unsigned long gain_val = ((gain->val - gain->minimum) *
+ 48 + range / 2) / range + 16;
+
+ if (gain_val >= 32)
+ gain_val &= ~1;
+
+ /*
+ * The user wants to set gain manually, hope, she
+ * knows, what she's doing... Switch AGC off.
+ */
+ if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0)
+ return -EIO;
+
+ dev_dbg(&client->dev, "Setting gain from %d to %lu\n",
+ reg_read(client, MT9V022_ANALOG_GAIN), gain_val);
+ if (reg_write(client, MT9V022_ANALOG_GAIN, gain_val) < 0)
+ return -EIO;
+ }
+ return 0;
+ case V4L2_CID_EXPOSURE_AUTO:
+ if (ctrl->val == V4L2_EXPOSURE_AUTO) {
+ data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x1);
+ } else {
+ struct v4l2_ctrl *exp = mt9v022->exposure;
+ unsigned long range = exp->maximum - exp->minimum;
+ unsigned long shutter = ((exp->val - exp->minimum) *
+ 479 + range / 2) / range + 1;
+
+ /*
+ * The user wants to set shutter width manually, hope,
+ * she knows, what she's doing... Switch AEC off.
+ */
+ data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1);
+ if (data < 0)
+ return -EIO;
+ dev_dbg(&client->dev, "Shutter width from %d to %lu\n",
+ reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH),
+ shutter);
+ if (reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH,
+ shutter) < 0)
+ return -EIO;
+ }
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/*
+ * Interface active, can use i2c. If it fails, it can indeed mean, that
+ * this wasn't our capture interface, so, we wait for the right one
+ */
+static int mt9v022_video_probe(struct i2c_client *client)
+{
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ s32 data;
+ int ret;
+ unsigned long flags;
+
+ /* Read out the chip version register */
+ data = reg_read(client, MT9V022_CHIP_VERSION);
+
+ /* must be 0x1311 or 0x1313 */
+ if (data != 0x1311 && data != 0x1313) {
+ ret = -ENODEV;
+ dev_info(&client->dev, "No MT9V022 found, ID register 0x%x\n",
+ data);
+ goto ei2c;
+ }
+
+ /* Soft reset */
+ ret = reg_write(client, MT9V022_RESET, 1);
+ if (ret < 0)
+ goto ei2c;
+ /* 15 clock cycles */
+ udelay(200);
+ if (reg_read(client, MT9V022_RESET)) {
+ dev_err(&client->dev, "Resetting MT9V022 failed!\n");
+ if (ret > 0)
+ ret = -EIO;
+ goto ei2c;
+ }
+
+ /* Set monochrome or colour sensor type */
+ if (sensor_type && (!strcmp("colour", sensor_type) ||
+ !strcmp("color", sensor_type))) {
+ ret = reg_write(client, MT9V022_PIXEL_OPERATION_MODE, 4 | 0x11);
+ mt9v022->model = V4L2_IDENT_MT9V022IX7ATC;
+ mt9v022->fmts = mt9v022_colour_fmts;
+ } else {
+ ret = reg_write(client, MT9V022_PIXEL_OPERATION_MODE, 0x11);
+ mt9v022->model = V4L2_IDENT_MT9V022IX7ATM;
+ mt9v022->fmts = mt9v022_monochrome_fmts;
+ }
+
+ if (ret < 0)
+ goto ei2c;
+
+ mt9v022->num_fmts = 0;
+
+ /*
+ * This is a 10bit sensor, so by default we only allow 10bit.
+ * The platform may support different bus widths due to
+ * different routing of the data lines.
+ */
+ if (icl->query_bus_param)
+ flags = icl->query_bus_param(icl);
+ else
+ flags = SOCAM_DATAWIDTH_10;
+
+ if (flags & SOCAM_DATAWIDTH_10)
+ mt9v022->num_fmts++;
+ else
+ mt9v022->fmts++;
+
+ if (flags & SOCAM_DATAWIDTH_8)
+ mt9v022->num_fmts++;
+
+ mt9v022->fmt = &mt9v022->fmts[0];
+
+ dev_info(&client->dev, "Detected a MT9V022 chip ID %x, %s sensor\n",
+ data, mt9v022->model == V4L2_IDENT_MT9V022IX7ATM ?
+ "monochrome" : "colour");
+
+ ret = mt9v022_init(client);
+ if (ret < 0)
+ dev_err(&client->dev, "Failed to initialise the camera\n");
+
+ei2c:
+ return ret;
+}
+
+static int mt9v022_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+
+ *lines = mt9v022->y_skip_top;
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops mt9v022_ctrl_ops = {
+ .g_volatile_ctrl = mt9v022_g_volatile_ctrl,
+ .s_ctrl = mt9v022_s_ctrl,
+};
+
+static struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = {
+ .g_chip_ident = mt9v022_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = mt9v022_g_register,
+ .s_register = mt9v022_s_register,
+#endif
+};
+
+static int mt9v022_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+
+ if (index >= mt9v022->num_fmts)
+ return -EINVAL;
+
+ *code = mt9v022->fmts[index].code;
+ return 0;
+}
+
+static int mt9v022_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE |
+ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static int mt9v022_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+ unsigned long flags = soc_camera_apply_board_flags(icl, cfg);
+ unsigned int bps = soc_mbus_get_fmtdesc(mt9v022->fmt->code)->bits_per_sample;
+ int ret;
+ u16 pixclk = 0;
+
+ if (icl->set_bus_param) {
+ ret = icl->set_bus_param(icl, 1 << (bps - 1));
+ if (ret)
+ return ret;
+ } else if (bps != 10) {
+ /*
+ * Without board specific bus width settings we only support the
+ * sensors native bus width
+ */
+ return -EINVAL;
+ }
+
+ if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
+ pixclk |= 0x10;
+
+ if (!(flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH))
+ pixclk |= 0x1;
+
+ if (!(flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH))
+ pixclk |= 0x2;
+
+ ret = reg_write(client, MT9V022_PIXCLK_FV_LV, pixclk);
+ if (ret < 0)
+ return ret;
+
+ if (!(flags & V4L2_MBUS_MASTER))
+ mt9v022->chip_control &= ~0x8;
+
+ ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(&client->dev, "Calculated pixclk 0x%x, chip control 0x%x\n",
+ pixclk, mt9v022->chip_control);
+
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = {
+ .s_stream = mt9v022_s_stream,
+ .s_mbus_fmt = mt9v022_s_fmt,
+ .g_mbus_fmt = mt9v022_g_fmt,
+ .try_mbus_fmt = mt9v022_try_fmt,
+ .s_crop = mt9v022_s_crop,
+ .g_crop = mt9v022_g_crop,
+ .cropcap = mt9v022_cropcap,
+ .enum_mbus_fmt = mt9v022_enum_fmt,
+ .g_mbus_config = mt9v022_g_mbus_config,
+ .s_mbus_config = mt9v022_s_mbus_config,
+};
+
+static struct v4l2_subdev_sensor_ops mt9v022_subdev_sensor_ops = {
+ .g_skip_top_lines = mt9v022_g_skip_top_lines,
+};
+
+static struct v4l2_subdev_ops mt9v022_subdev_ops = {
+ .core = &mt9v022_subdev_core_ops,
+ .video = &mt9v022_subdev_video_ops,
+ .sensor = &mt9v022_subdev_sensor_ops,
+};
+
+static int mt9v022_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct mt9v022 *mt9v022;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ int ret;
+
+ if (!icl) {
+ dev_err(&client->dev, "MT9V022 driver needs platform data\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_warn(&adapter->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
+ return -EIO;
+ }
+
+ mt9v022 = kzalloc(sizeof(struct mt9v022), GFP_KERNEL);
+ if (!mt9v022)
+ return -ENOMEM;
+
+ v4l2_i2c_subdev_init(&mt9v022->subdev, client, &mt9v022_subdev_ops);
+ v4l2_ctrl_handler_init(&mt9v022->hdl, 6);
+ v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ mt9v022->autogain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ mt9v022->gain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
+ V4L2_CID_GAIN, 0, 127, 1, 64);
+
+ /*
+ * Simulated autoexposure. If enabled, we calculate shutter width
+ * ourselves in the driver based on vertical blanking and frame width
+ */
+ mt9v022->autoexposure = v4l2_ctrl_new_std_menu(&mt9v022->hdl,
+ &mt9v022_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
+ V4L2_EXPOSURE_AUTO);
+ mt9v022->exposure = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
+ V4L2_CID_EXPOSURE, 1, 255, 1, 255);
+
+ mt9v022->subdev.ctrl_handler = &mt9v022->hdl;
+ if (mt9v022->hdl.error) {
+ int err = mt9v022->hdl.error;
+
+ kfree(mt9v022);
+ return err;
+ }
+ v4l2_ctrl_auto_cluster(2, &mt9v022->autoexposure,
+ V4L2_EXPOSURE_MANUAL, true);
+ v4l2_ctrl_auto_cluster(2, &mt9v022->autogain, 0, true);
+
+ mt9v022->chip_control = MT9V022_CHIP_CONTROL_DEFAULT;
+
+ /*
+ * MT9V022 _really_ corrupts the first read out line.
+ * TODO: verify on i.MX31
+ */
+ mt9v022->y_skip_top = 1;
+ mt9v022->rect.left = MT9V022_COLUMN_SKIP;
+ mt9v022->rect.top = MT9V022_ROW_SKIP;
+ mt9v022->rect.width = MT9V022_MAX_WIDTH;
+ mt9v022->rect.height = MT9V022_MAX_HEIGHT;
+
+ ret = mt9v022_video_probe(client);
+ if (ret) {
+ v4l2_ctrl_handler_free(&mt9v022->hdl);
+ kfree(mt9v022);
+ }
+
+ return ret;
+}
+
+static int mt9v022_remove(struct i2c_client *client)
+{
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ v4l2_device_unregister_subdev(&mt9v022->subdev);
+ if (icl->free_bus)
+ icl->free_bus(icl);
+ v4l2_ctrl_handler_free(&mt9v022->hdl);
+ kfree(mt9v022);
+
+ return 0;
+}
+static const struct i2c_device_id mt9v022_id[] = {
+ { "mt9v022", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mt9v022_id);
+
+static struct i2c_driver mt9v022_i2c_driver = {
+ .driver = {
+ .name = "mt9v022",
+ },
+ .probe = mt9v022_probe,
+ .remove = mt9v022_remove,
+ .id_table = mt9v022_id,
+};
+
+module_i2c_driver(mt9v022_i2c_driver);
+
+MODULE_DESCRIPTION("Micron MT9V022 Camera driver");
+MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mt9v032.c b/drivers/media/video/mt9v032.c
new file mode 100644
index 00000000..75e253a3
--- /dev/null
+++ b/drivers/media/video/mt9v032.c
@@ -0,0 +1,763 @@
+/*
+ * Driver for MT9V032 CMOS Image Sensor from Micron
+ *
+ * Copyright (C) 2010, Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * Based on the MT9M001 driver,
+ *
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/log2.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/module.h>
+
+#include <media/mt9v032.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#define MT9V032_PIXEL_ARRAY_HEIGHT 492
+#define MT9V032_PIXEL_ARRAY_WIDTH 782
+
+#define MT9V032_CHIP_VERSION 0x00
+#define MT9V032_CHIP_ID_REV1 0x1311
+#define MT9V032_CHIP_ID_REV3 0x1313
+#define MT9V032_COLUMN_START 0x01
+#define MT9V032_COLUMN_START_MIN 1
+#define MT9V032_COLUMN_START_DEF 1
+#define MT9V032_COLUMN_START_MAX 752
+#define MT9V032_ROW_START 0x02
+#define MT9V032_ROW_START_MIN 4
+#define MT9V032_ROW_START_DEF 5
+#define MT9V032_ROW_START_MAX 482
+#define MT9V032_WINDOW_HEIGHT 0x03
+#define MT9V032_WINDOW_HEIGHT_MIN 1
+#define MT9V032_WINDOW_HEIGHT_DEF 480
+#define MT9V032_WINDOW_HEIGHT_MAX 480
+#define MT9V032_WINDOW_WIDTH 0x04
+#define MT9V032_WINDOW_WIDTH_MIN 1
+#define MT9V032_WINDOW_WIDTH_DEF 752
+#define MT9V032_WINDOW_WIDTH_MAX 752
+#define MT9V032_HORIZONTAL_BLANKING 0x05
+#define MT9V032_HORIZONTAL_BLANKING_MIN 43
+#define MT9V032_HORIZONTAL_BLANKING_MAX 1023
+#define MT9V032_VERTICAL_BLANKING 0x06
+#define MT9V032_VERTICAL_BLANKING_MIN 4
+#define MT9V032_VERTICAL_BLANKING_MAX 3000
+#define MT9V032_CHIP_CONTROL 0x07
+#define MT9V032_CHIP_CONTROL_MASTER_MODE (1 << 3)
+#define MT9V032_CHIP_CONTROL_DOUT_ENABLE (1 << 7)
+#define MT9V032_CHIP_CONTROL_SEQUENTIAL (1 << 8)
+#define MT9V032_SHUTTER_WIDTH1 0x08
+#define MT9V032_SHUTTER_WIDTH2 0x09
+#define MT9V032_SHUTTER_WIDTH_CONTROL 0x0a
+#define MT9V032_TOTAL_SHUTTER_WIDTH 0x0b
+#define MT9V032_TOTAL_SHUTTER_WIDTH_MIN 1
+#define MT9V032_TOTAL_SHUTTER_WIDTH_DEF 480
+#define MT9V032_TOTAL_SHUTTER_WIDTH_MAX 32767
+#define MT9V032_RESET 0x0c
+#define MT9V032_READ_MODE 0x0d
+#define MT9V032_READ_MODE_ROW_BIN_MASK (3 << 0)
+#define MT9V032_READ_MODE_ROW_BIN_SHIFT 0
+#define MT9V032_READ_MODE_COLUMN_BIN_MASK (3 << 2)
+#define MT9V032_READ_MODE_COLUMN_BIN_SHIFT 2
+#define MT9V032_READ_MODE_ROW_FLIP (1 << 4)
+#define MT9V032_READ_MODE_COLUMN_FLIP (1 << 5)
+#define MT9V032_READ_MODE_DARK_COLUMNS (1 << 6)
+#define MT9V032_READ_MODE_DARK_ROWS (1 << 7)
+#define MT9V032_PIXEL_OPERATION_MODE 0x0f
+#define MT9V032_PIXEL_OPERATION_MODE_COLOR (1 << 2)
+#define MT9V032_PIXEL_OPERATION_MODE_HDR (1 << 6)
+#define MT9V032_ANALOG_GAIN 0x35
+#define MT9V032_ANALOG_GAIN_MIN 16
+#define MT9V032_ANALOG_GAIN_DEF 16
+#define MT9V032_ANALOG_GAIN_MAX 64
+#define MT9V032_MAX_ANALOG_GAIN 0x36
+#define MT9V032_MAX_ANALOG_GAIN_MAX 127
+#define MT9V032_FRAME_DARK_AVERAGE 0x42
+#define MT9V032_DARK_AVG_THRESH 0x46
+#define MT9V032_DARK_AVG_LOW_THRESH_MASK (255 << 0)
+#define MT9V032_DARK_AVG_LOW_THRESH_SHIFT 0
+#define MT9V032_DARK_AVG_HIGH_THRESH_MASK (255 << 8)
+#define MT9V032_DARK_AVG_HIGH_THRESH_SHIFT 8
+#define MT9V032_ROW_NOISE_CORR_CONTROL 0x70
+#define MT9V032_ROW_NOISE_CORR_ENABLE (1 << 5)
+#define MT9V032_ROW_NOISE_CORR_USE_BLK_AVG (1 << 7)
+#define MT9V032_PIXEL_CLOCK 0x74
+#define MT9V032_PIXEL_CLOCK_INV_LINE (1 << 0)
+#define MT9V032_PIXEL_CLOCK_INV_FRAME (1 << 1)
+#define MT9V032_PIXEL_CLOCK_XOR_LINE (1 << 2)
+#define MT9V032_PIXEL_CLOCK_CONT_LINE (1 << 3)
+#define MT9V032_PIXEL_CLOCK_INV_PXL_CLK (1 << 4)
+#define MT9V032_TEST_PATTERN 0x7f
+#define MT9V032_TEST_PATTERN_DATA_MASK (1023 << 0)
+#define MT9V032_TEST_PATTERN_DATA_SHIFT 0
+#define MT9V032_TEST_PATTERN_USE_DATA (1 << 10)
+#define MT9V032_TEST_PATTERN_GRAY_MASK (3 << 11)
+#define MT9V032_TEST_PATTERN_GRAY_NONE (0 << 11)
+#define MT9V032_TEST_PATTERN_GRAY_VERTICAL (1 << 11)
+#define MT9V032_TEST_PATTERN_GRAY_HORIZONTAL (2 << 11)
+#define MT9V032_TEST_PATTERN_GRAY_DIAGONAL (3 << 11)
+#define MT9V032_TEST_PATTERN_ENABLE (1 << 13)
+#define MT9V032_TEST_PATTERN_FLIP (1 << 14)
+#define MT9V032_AEC_AGC_ENABLE 0xaf
+#define MT9V032_AEC_ENABLE (1 << 0)
+#define MT9V032_AGC_ENABLE (1 << 1)
+#define MT9V032_THERMAL_INFO 0xc1
+
+struct mt9v032 {
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+
+ struct v4l2_mbus_framefmt format;
+ struct v4l2_rect crop;
+
+ struct v4l2_ctrl_handler ctrls;
+
+ struct mutex power_lock;
+ int power_count;
+
+ struct mt9v032_platform_data *pdata;
+ u16 chip_control;
+ u16 aec_agc;
+};
+
+static struct mt9v032 *to_mt9v032(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct mt9v032, subdev);
+}
+
+static int mt9v032_read(struct i2c_client *client, const u8 reg)
+{
+ s32 data = i2c_smbus_read_word_swapped(client, reg);
+ dev_dbg(&client->dev, "%s: read 0x%04x from 0x%02x\n", __func__,
+ data, reg);
+ return data;
+}
+
+static int mt9v032_write(struct i2c_client *client, const u8 reg,
+ const u16 data)
+{
+ dev_dbg(&client->dev, "%s: writing 0x%04x to 0x%02x\n", __func__,
+ data, reg);
+ return i2c_smbus_write_word_swapped(client, reg, data);
+}
+
+static int mt9v032_set_chip_control(struct mt9v032 *mt9v032, u16 clear, u16 set)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev);
+ u16 value = (mt9v032->chip_control & ~clear) | set;
+ int ret;
+
+ ret = mt9v032_write(client, MT9V032_CHIP_CONTROL, value);
+ if (ret < 0)
+ return ret;
+
+ mt9v032->chip_control = value;
+ return 0;
+}
+
+static int
+mt9v032_update_aec_agc(struct mt9v032 *mt9v032, u16 which, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev);
+ u16 value = mt9v032->aec_agc;
+ int ret;
+
+ if (enable)
+ value |= which;
+ else
+ value &= ~which;
+
+ ret = mt9v032_write(client, MT9V032_AEC_AGC_ENABLE, value);
+ if (ret < 0)
+ return ret;
+
+ mt9v032->aec_agc = value;
+ return 0;
+}
+
+static int mt9v032_power_on(struct mt9v032 *mt9v032)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev);
+ int ret;
+
+ if (mt9v032->pdata->set_clock) {
+ mt9v032->pdata->set_clock(&mt9v032->subdev, 25000000);
+ udelay(1);
+ }
+
+ /* Reset the chip and stop data read out */
+ ret = mt9v032_write(client, MT9V032_RESET, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9v032_write(client, MT9V032_RESET, 0);
+ if (ret < 0)
+ return ret;
+
+ return mt9v032_write(client, MT9V032_CHIP_CONTROL, 0);
+}
+
+static void mt9v032_power_off(struct mt9v032 *mt9v032)
+{
+ if (mt9v032->pdata->set_clock)
+ mt9v032->pdata->set_clock(&mt9v032->subdev, 0);
+}
+
+static int __mt9v032_set_power(struct mt9v032 *mt9v032, bool on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev);
+ int ret;
+
+ if (!on) {
+ mt9v032_power_off(mt9v032);
+ return 0;
+ }
+
+ ret = mt9v032_power_on(mt9v032);
+ if (ret < 0)
+ return ret;
+
+ /* Configure the pixel clock polarity */
+ if (mt9v032->pdata && mt9v032->pdata->clk_pol) {
+ ret = mt9v032_write(client, MT9V032_PIXEL_CLOCK,
+ MT9V032_PIXEL_CLOCK_INV_PXL_CLK);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Disable the noise correction algorithm and restore the controls. */
+ ret = mt9v032_write(client, MT9V032_ROW_NOISE_CORR_CONTROL, 0);
+ if (ret < 0)
+ return ret;
+
+ return v4l2_ctrl_handler_setup(&mt9v032->ctrls);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev video operations
+ */
+
+static struct v4l2_mbus_framefmt *
+__mt9v032_get_pad_format(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_format(fh, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &mt9v032->format;
+ default:
+ return NULL;
+ }
+}
+
+static struct v4l2_rect *
+__mt9v032_get_pad_crop(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_crop(fh, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &mt9v032->crop;
+ default:
+ return NULL;
+ }
+}
+
+static int mt9v032_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ const u16 mode = MT9V032_CHIP_CONTROL_MASTER_MODE
+ | MT9V032_CHIP_CONTROL_DOUT_ENABLE
+ | MT9V032_CHIP_CONTROL_SEQUENTIAL;
+ struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ struct mt9v032 *mt9v032 = to_mt9v032(subdev);
+ struct v4l2_mbus_framefmt *format = &mt9v032->format;
+ struct v4l2_rect *crop = &mt9v032->crop;
+ unsigned int hratio;
+ unsigned int vratio;
+ int ret;
+
+ if (!enable)
+ return mt9v032_set_chip_control(mt9v032, mode, 0);
+
+ /* Configure the window size and row/column bin */
+ hratio = DIV_ROUND_CLOSEST(crop->width, format->width);
+ vratio = DIV_ROUND_CLOSEST(crop->height, format->height);
+
+ ret = mt9v032_write(client, MT9V032_READ_MODE,
+ (hratio - 1) << MT9V032_READ_MODE_ROW_BIN_SHIFT |
+ (vratio - 1) << MT9V032_READ_MODE_COLUMN_BIN_SHIFT);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9v032_write(client, MT9V032_COLUMN_START, crop->left);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9v032_write(client, MT9V032_ROW_START, crop->top);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9v032_write(client, MT9V032_WINDOW_WIDTH, crop->width);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9v032_write(client, MT9V032_WINDOW_HEIGHT, crop->height);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9v032_write(client, MT9V032_HORIZONTAL_BLANKING,
+ max(43, 660 - crop->width));
+ if (ret < 0)
+ return ret;
+
+ /* Switch to master "normal" mode */
+ return mt9v032_set_chip_control(mt9v032, 0, mode);
+}
+
+static int mt9v032_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index > 0)
+ return -EINVAL;
+
+ code->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ return 0;
+}
+
+static int mt9v032_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->index >= 8 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10)
+ return -EINVAL;
+
+ fse->min_width = MT9V032_WINDOW_WIDTH_DEF / fse->index;
+ fse->max_width = fse->min_width;
+ fse->min_height = MT9V032_WINDOW_HEIGHT_DEF / fse->index;
+ fse->max_height = fse->min_height;
+
+ return 0;
+}
+
+static int mt9v032_get_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *format)
+{
+ struct mt9v032 *mt9v032 = to_mt9v032(subdev);
+
+ format->format = *__mt9v032_get_pad_format(mt9v032, fh, format->pad,
+ format->which);
+ return 0;
+}
+
+static int mt9v032_set_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *format)
+{
+ struct mt9v032 *mt9v032 = to_mt9v032(subdev);
+ struct v4l2_mbus_framefmt *__format;
+ struct v4l2_rect *__crop;
+ unsigned int width;
+ unsigned int height;
+ unsigned int hratio;
+ unsigned int vratio;
+
+ __crop = __mt9v032_get_pad_crop(mt9v032, fh, format->pad,
+ format->which);
+
+ /* Clamp the width and height to avoid dividing by zero. */
+ width = clamp_t(unsigned int, ALIGN(format->format.width, 2),
+ max(__crop->width / 8, MT9V032_WINDOW_WIDTH_MIN),
+ __crop->width);
+ height = clamp_t(unsigned int, ALIGN(format->format.height, 2),
+ max(__crop->height / 8, MT9V032_WINDOW_HEIGHT_MIN),
+ __crop->height);
+
+ hratio = DIV_ROUND_CLOSEST(__crop->width, width);
+ vratio = DIV_ROUND_CLOSEST(__crop->height, height);
+
+ __format = __mt9v032_get_pad_format(mt9v032, fh, format->pad,
+ format->which);
+ __format->width = __crop->width / hratio;
+ __format->height = __crop->height / vratio;
+
+ format->format = *__format;
+
+ return 0;
+}
+
+static int mt9v032_get_crop(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct mt9v032 *mt9v032 = to_mt9v032(subdev);
+
+ crop->rect = *__mt9v032_get_pad_crop(mt9v032, fh, crop->pad,
+ crop->which);
+ return 0;
+}
+
+static int mt9v032_set_crop(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct mt9v032 *mt9v032 = to_mt9v032(subdev);
+ struct v4l2_mbus_framefmt *__format;
+ struct v4l2_rect *__crop;
+ struct v4l2_rect rect;
+
+ /* Clamp the crop rectangle boundaries and align them to a non multiple
+ * of 2 pixels to ensure a GRBG Bayer pattern.
+ */
+ rect.left = clamp(ALIGN(crop->rect.left + 1, 2) - 1,
+ MT9V032_COLUMN_START_MIN,
+ MT9V032_COLUMN_START_MAX);
+ rect.top = clamp(ALIGN(crop->rect.top + 1, 2) - 1,
+ MT9V032_ROW_START_MIN,
+ MT9V032_ROW_START_MAX);
+ rect.width = clamp(ALIGN(crop->rect.width, 2),
+ MT9V032_WINDOW_WIDTH_MIN,
+ MT9V032_WINDOW_WIDTH_MAX);
+ rect.height = clamp(ALIGN(crop->rect.height, 2),
+ MT9V032_WINDOW_HEIGHT_MIN,
+ MT9V032_WINDOW_HEIGHT_MAX);
+
+ rect.width = min(rect.width, MT9V032_PIXEL_ARRAY_WIDTH - rect.left);
+ rect.height = min(rect.height, MT9V032_PIXEL_ARRAY_HEIGHT - rect.top);
+
+ __crop = __mt9v032_get_pad_crop(mt9v032, fh, crop->pad, crop->which);
+
+ if (rect.width != __crop->width || rect.height != __crop->height) {
+ /* Reset the output image size if the crop rectangle size has
+ * been modified.
+ */
+ __format = __mt9v032_get_pad_format(mt9v032, fh, crop->pad,
+ crop->which);
+ __format->width = rect.width;
+ __format->height = rect.height;
+ }
+
+ *__crop = rect;
+ crop->rect = rect;
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev control operations
+ */
+
+#define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001)
+
+static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct mt9v032 *mt9v032 =
+ container_of(ctrl->handler, struct mt9v032, ctrls);
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev);
+ u16 data;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTOGAIN:
+ return mt9v032_update_aec_agc(mt9v032, MT9V032_AGC_ENABLE,
+ ctrl->val);
+
+ case V4L2_CID_GAIN:
+ return mt9v032_write(client, MT9V032_ANALOG_GAIN, ctrl->val);
+
+ case V4L2_CID_EXPOSURE_AUTO:
+ return mt9v032_update_aec_agc(mt9v032, MT9V032_AEC_ENABLE,
+ ctrl->val);
+
+ case V4L2_CID_EXPOSURE:
+ return mt9v032_write(client, MT9V032_TOTAL_SHUTTER_WIDTH,
+ ctrl->val);
+
+ case V4L2_CID_TEST_PATTERN:
+ switch (ctrl->val) {
+ case 0:
+ data = 0;
+ break;
+ case 1:
+ data = MT9V032_TEST_PATTERN_GRAY_VERTICAL
+ | MT9V032_TEST_PATTERN_ENABLE;
+ break;
+ case 2:
+ data = MT9V032_TEST_PATTERN_GRAY_HORIZONTAL
+ | MT9V032_TEST_PATTERN_ENABLE;
+ break;
+ case 3:
+ data = MT9V032_TEST_PATTERN_GRAY_DIAGONAL
+ | MT9V032_TEST_PATTERN_ENABLE;
+ break;
+ default:
+ data = (ctrl->val << MT9V032_TEST_PATTERN_DATA_SHIFT)
+ | MT9V032_TEST_PATTERN_USE_DATA
+ | MT9V032_TEST_PATTERN_ENABLE
+ | MT9V032_TEST_PATTERN_FLIP;
+ break;
+ }
+
+ return mt9v032_write(client, MT9V032_TEST_PATTERN, data);
+ }
+
+ return 0;
+}
+
+static struct v4l2_ctrl_ops mt9v032_ctrl_ops = {
+ .s_ctrl = mt9v032_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config mt9v032_ctrls[] = {
+ {
+ .ops = &mt9v032_ctrl_ops,
+ .id = V4L2_CID_TEST_PATTERN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Test pattern",
+ .min = 0,
+ .max = 1023,
+ .step = 1,
+ .def = 0,
+ .flags = 0,
+ }
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev core operations
+ */
+
+static int mt9v032_set_power(struct v4l2_subdev *subdev, int on)
+{
+ struct mt9v032 *mt9v032 = to_mt9v032(subdev);
+ int ret = 0;
+
+ mutex_lock(&mt9v032->power_lock);
+
+ /* If the power count is modified from 0 to != 0 or from != 0 to 0,
+ * update the power state.
+ */
+ if (mt9v032->power_count == !on) {
+ ret = __mt9v032_set_power(mt9v032, !!on);
+ if (ret < 0)
+ goto done;
+ }
+
+ /* Update the power count. */
+ mt9v032->power_count += on ? 1 : -1;
+ WARN_ON(mt9v032->power_count < 0);
+
+done:
+ mutex_unlock(&mt9v032->power_lock);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev internal operations
+ */
+
+static int mt9v032_registered(struct v4l2_subdev *subdev)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ struct mt9v032 *mt9v032 = to_mt9v032(subdev);
+ s32 data;
+ int ret;
+
+ dev_info(&client->dev, "Probing MT9V032 at address 0x%02x\n",
+ client->addr);
+
+ ret = mt9v032_power_on(mt9v032);
+ if (ret < 0) {
+ dev_err(&client->dev, "MT9V032 power up failed\n");
+ return ret;
+ }
+
+ /* Read and check the sensor version */
+ data = mt9v032_read(client, MT9V032_CHIP_VERSION);
+ if (data != MT9V032_CHIP_ID_REV1 && data != MT9V032_CHIP_ID_REV3) {
+ dev_err(&client->dev, "MT9V032 not detected, wrong version "
+ "0x%04x\n", data);
+ return -ENODEV;
+ }
+
+ mt9v032_power_off(mt9v032);
+
+ dev_info(&client->dev, "MT9V032 detected at address 0x%02x\n",
+ client->addr);
+
+ return ret;
+}
+
+static int mt9v032_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *crop;
+
+ crop = v4l2_subdev_get_try_crop(fh, 0);
+ crop->left = MT9V032_COLUMN_START_DEF;
+ crop->top = MT9V032_ROW_START_DEF;
+ crop->width = MT9V032_WINDOW_WIDTH_DEF;
+ crop->height = MT9V032_WINDOW_HEIGHT_DEF;
+
+ format = v4l2_subdev_get_try_format(fh, 0);
+ format->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ format->width = MT9V032_WINDOW_WIDTH_DEF;
+ format->height = MT9V032_WINDOW_HEIGHT_DEF;
+ format->field = V4L2_FIELD_NONE;
+ format->colorspace = V4L2_COLORSPACE_SRGB;
+
+ return mt9v032_set_power(subdev, 1);
+}
+
+static int mt9v032_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+ return mt9v032_set_power(subdev, 0);
+}
+
+static struct v4l2_subdev_core_ops mt9v032_subdev_core_ops = {
+ .s_power = mt9v032_set_power,
+};
+
+static struct v4l2_subdev_video_ops mt9v032_subdev_video_ops = {
+ .s_stream = mt9v032_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops mt9v032_subdev_pad_ops = {
+ .enum_mbus_code = mt9v032_enum_mbus_code,
+ .enum_frame_size = mt9v032_enum_frame_size,
+ .get_fmt = mt9v032_get_format,
+ .set_fmt = mt9v032_set_format,
+ .get_crop = mt9v032_get_crop,
+ .set_crop = mt9v032_set_crop,
+};
+
+static struct v4l2_subdev_ops mt9v032_subdev_ops = {
+ .core = &mt9v032_subdev_core_ops,
+ .video = &mt9v032_subdev_video_ops,
+ .pad = &mt9v032_subdev_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops mt9v032_subdev_internal_ops = {
+ .registered = mt9v032_registered,
+ .open = mt9v032_open,
+ .close = mt9v032_close,
+};
+
+/* -----------------------------------------------------------------------------
+ * Driver initialization and probing
+ */
+
+static int mt9v032_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct mt9v032 *mt9v032;
+ unsigned int i;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_warn(&client->adapter->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
+ return -EIO;
+ }
+
+ mt9v032 = kzalloc(sizeof(*mt9v032), GFP_KERNEL);
+ if (!mt9v032)
+ return -ENOMEM;
+
+ mutex_init(&mt9v032->power_lock);
+ mt9v032->pdata = client->dev.platform_data;
+
+ v4l2_ctrl_handler_init(&mt9v032->ctrls, ARRAY_SIZE(mt9v032_ctrls) + 4);
+
+ v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops,
+ V4L2_CID_GAIN, MT9V032_ANALOG_GAIN_MIN,
+ MT9V032_ANALOG_GAIN_MAX, 1, MT9V032_ANALOG_GAIN_DEF);
+ v4l2_ctrl_new_std_menu(&mt9v032->ctrls, &mt9v032_ctrl_ops,
+ V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0,
+ V4L2_EXPOSURE_AUTO);
+ v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops,
+ V4L2_CID_EXPOSURE, MT9V032_TOTAL_SHUTTER_WIDTH_MIN,
+ MT9V032_TOTAL_SHUTTER_WIDTH_MAX, 1,
+ MT9V032_TOTAL_SHUTTER_WIDTH_DEF);
+
+ for (i = 0; i < ARRAY_SIZE(mt9v032_ctrls); ++i)
+ v4l2_ctrl_new_custom(&mt9v032->ctrls, &mt9v032_ctrls[i], NULL);
+
+ mt9v032->subdev.ctrl_handler = &mt9v032->ctrls;
+
+ if (mt9v032->ctrls.error)
+ printk(KERN_INFO "%s: control initialization error %d\n",
+ __func__, mt9v032->ctrls.error);
+
+ mt9v032->crop.left = MT9V032_COLUMN_START_DEF;
+ mt9v032->crop.top = MT9V032_ROW_START_DEF;
+ mt9v032->crop.width = MT9V032_WINDOW_WIDTH_DEF;
+ mt9v032->crop.height = MT9V032_WINDOW_HEIGHT_DEF;
+
+ mt9v032->format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ mt9v032->format.width = MT9V032_WINDOW_WIDTH_DEF;
+ mt9v032->format.height = MT9V032_WINDOW_HEIGHT_DEF;
+ mt9v032->format.field = V4L2_FIELD_NONE;
+ mt9v032->format.colorspace = V4L2_COLORSPACE_SRGB;
+
+ mt9v032->aec_agc = MT9V032_AEC_ENABLE | MT9V032_AGC_ENABLE;
+
+ v4l2_i2c_subdev_init(&mt9v032->subdev, client, &mt9v032_subdev_ops);
+ mt9v032->subdev.internal_ops = &mt9v032_subdev_internal_ops;
+ mt9v032->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ mt9v032->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&mt9v032->subdev.entity, 1, &mt9v032->pad, 0);
+ if (ret < 0)
+ kfree(mt9v032);
+
+ return ret;
+}
+
+static int mt9v032_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct mt9v032 *mt9v032 = to_mt9v032(subdev);
+
+ v4l2_device_unregister_subdev(subdev);
+ media_entity_cleanup(&subdev->entity);
+ kfree(mt9v032);
+ return 0;
+}
+
+static const struct i2c_device_id mt9v032_id[] = {
+ { "mt9v032", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mt9v032_id);
+
+static struct i2c_driver mt9v032_driver = {
+ .driver = {
+ .name = "mt9v032",
+ },
+ .probe = mt9v032_probe,
+ .remove = mt9v032_remove,
+ .id_table = mt9v032_id,
+};
+
+module_i2c_driver(mt9v032_driver);
+
+MODULE_DESCRIPTION("Aptina MT9V032 Camera driver");
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mx1_camera.c b/drivers/media/video/mx1_camera.c
new file mode 100644
index 00000000..055d11dd
--- /dev/null
+++ b/drivers/media/video/mx1_camera.c
@@ -0,0 +1,898 @@
+/*
+ * V4L2 Driver for i.MXL/i.MXL camera (CSI) host
+ *
+ * Copyright (C) 2008, Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
+ * Copyright (C) 2009, Darius Augulis <augulis.darius@gmail.com>
+ *
+ * Based on PXA SoC camera driver
+ * Copyright (C) 2006, Sascha Hauer, Pengutronix
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/videodev2.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/videobuf-dma-contig.h>
+#include <media/soc_mediabus.h>
+
+#include <asm/dma.h>
+#include <asm/fiq.h>
+#include <mach/dma-mx1-mx2.h>
+#include <mach/hardware.h>
+#include <mach/mx1_camera.h>
+
+/*
+ * CSI registers
+ */
+#define CSICR1 0x00 /* CSI Control Register 1 */
+#define CSISR 0x08 /* CSI Status Register */
+#define CSIRXR 0x10 /* CSI RxFIFO Register */
+
+#define CSICR1_RXFF_LEVEL(x) (((x) & 0x3) << 19)
+#define CSICR1_SOF_POL (1 << 17)
+#define CSICR1_SOF_INTEN (1 << 16)
+#define CSICR1_MCLKDIV(x) (((x) & 0xf) << 12)
+#define CSICR1_MCLKEN (1 << 9)
+#define CSICR1_FCC (1 << 8)
+#define CSICR1_BIG_ENDIAN (1 << 7)
+#define CSICR1_CLR_RXFIFO (1 << 5)
+#define CSICR1_GCLK_MODE (1 << 4)
+#define CSICR1_DATA_POL (1 << 2)
+#define CSICR1_REDGE (1 << 1)
+#define CSICR1_EN (1 << 0)
+
+#define CSISR_SFF_OR_INT (1 << 25)
+#define CSISR_RFF_OR_INT (1 << 24)
+#define CSISR_STATFF_INT (1 << 21)
+#define CSISR_RXFF_INT (1 << 18)
+#define CSISR_SOF_INT (1 << 16)
+#define CSISR_DRDY (1 << 0)
+
+#define DRIVER_VERSION "0.0.2"
+#define DRIVER_NAME "mx1-camera"
+
+#define CSI_IRQ_MASK (CSISR_SFF_OR_INT | CSISR_RFF_OR_INT | \
+ CSISR_STATFF_INT | CSISR_RXFF_INT | CSISR_SOF_INT)
+
+#define CSI_BUS_FLAGS (V4L2_MBUS_MASTER | V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | \
+ V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_LOW)
+
+#define MAX_VIDEO_MEM 16 /* Video memory limit in megabytes */
+
+/*
+ * Structures
+ */
+
+/* buffer for one video frame */
+struct mx1_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+ enum v4l2_mbus_pixelcode code;
+ int inwork;
+};
+
+/*
+ * i.MX1/i.MXL is only supposed to handle one camera on its Camera Sensor
+ * Interface. If anyone ever builds hardware to enable more than
+ * one camera, they will have to modify this driver too
+ */
+struct mx1_camera_dev {
+ struct soc_camera_host soc_host;
+ struct soc_camera_device *icd;
+ struct mx1_camera_pdata *pdata;
+ struct mx1_buffer *active;
+ struct resource *res;
+ struct clk *clk;
+ struct list_head capture;
+
+ void __iomem *base;
+ int dma_chan;
+ unsigned int irq;
+ unsigned long mclk;
+
+ spinlock_t lock;
+};
+
+/*
+ * Videobuf operations
+ */
+static int mx1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
+ unsigned int *size)
+{
+ struct soc_camera_device *icd = vq->priv_data;
+ int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+
+ if (bytes_per_line < 0)
+ return bytes_per_line;
+
+ *size = bytes_per_line * icd->user_height;
+
+ if (!*count)
+ *count = 32;
+
+ if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024)
+ *count = (MAX_VIDEO_MEM * 1024 * 1024) / *size;
+
+ dev_dbg(icd->parent, "count=%d, size=%d\n", *count, *size);
+
+ return 0;
+}
+
+static void free_buffer(struct videobuf_queue *vq, struct mx1_buffer *buf)
+{
+ struct soc_camera_device *icd = vq->priv_data;
+ struct videobuf_buffer *vb = &buf->vb;
+
+ BUG_ON(in_interrupt());
+
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+ vb, vb->baddr, vb->bsize);
+
+ /*
+ * This waits until this buffer is out of danger, i.e., until it is no
+ * longer in STATE_QUEUED or STATE_ACTIVE
+ */
+ videobuf_waiton(vq, vb, 0, 0);
+ videobuf_dma_contig_free(vq, vb);
+
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int mx1_videobuf_prepare(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb, enum v4l2_field field)
+{
+ struct soc_camera_device *icd = vq->priv_data;
+ struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb);
+ int ret;
+ int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+
+ if (bytes_per_line < 0)
+ return bytes_per_line;
+
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+ vb, vb->baddr, vb->bsize);
+
+ /* Added list head initialization on alloc */
+ WARN_ON(!list_empty(&vb->queue));
+
+ BUG_ON(NULL == icd->current_fmt);
+
+ /*
+ * I think, in buf_prepare you only have to protect global data,
+ * the actual buffer is yours
+ */
+ buf->inwork = 1;
+
+ if (buf->code != icd->current_fmt->code ||
+ vb->width != icd->user_width ||
+ vb->height != icd->user_height ||
+ vb->field != field) {
+ buf->code = icd->current_fmt->code;
+ vb->width = icd->user_width;
+ vb->height = icd->user_height;
+ vb->field = field;
+ vb->state = VIDEOBUF_NEEDS_INIT;
+ }
+
+ vb->size = bytes_per_line * vb->height;
+ if (0 != vb->baddr && vb->bsize < vb->size) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (vb->state == VIDEOBUF_NEEDS_INIT) {
+ ret = videobuf_iolock(vq, vb, NULL);
+ if (ret)
+ goto fail;
+
+ vb->state = VIDEOBUF_PREPARED;
+ }
+
+ buf->inwork = 0;
+
+ return 0;
+
+fail:
+ free_buffer(vq, buf);
+out:
+ buf->inwork = 0;
+ return ret;
+}
+
+static int mx1_camera_setup_dma(struct mx1_camera_dev *pcdev)
+{
+ struct videobuf_buffer *vbuf = &pcdev->active->vb;
+ struct device *dev = pcdev->icd->parent;
+ int ret;
+
+ if (unlikely(!pcdev->active)) {
+ dev_err(dev, "DMA End IRQ with no active buffer\n");
+ return -EFAULT;
+ }
+
+ /* setup sg list for future DMA */
+ ret = imx_dma_setup_single(pcdev->dma_chan,
+ videobuf_to_dma_contig(vbuf),
+ vbuf->size, pcdev->res->start +
+ CSIRXR, DMA_MODE_READ);
+ if (unlikely(ret))
+ dev_err(dev, "Failed to setup DMA sg list\n");
+
+ return ret;
+}
+
+/* Called under spinlock_irqsave(&pcdev->lock, ...) */
+static void mx1_videobuf_queue(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct soc_camera_device *icd = vq->priv_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx1_camera_dev *pcdev = ici->priv;
+ struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb);
+
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+ vb, vb->baddr, vb->bsize);
+
+ list_add_tail(&vb->queue, &pcdev->capture);
+
+ vb->state = VIDEOBUF_ACTIVE;
+
+ if (!pcdev->active) {
+ pcdev->active = buf;
+
+ /* setup sg list for future DMA */
+ if (!mx1_camera_setup_dma(pcdev)) {
+ unsigned int temp;
+ /* enable SOF irq */
+ temp = __raw_readl(pcdev->base + CSICR1) |
+ CSICR1_SOF_INTEN;
+ __raw_writel(temp, pcdev->base + CSICR1);
+ }
+ }
+}
+
+static void mx1_videobuf_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb);
+#ifdef DEBUG
+ struct soc_camera_device *icd = vq->priv_data;
+ struct device *dev = icd->parent;
+
+ dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+ vb, vb->baddr, vb->bsize);
+
+ switch (vb->state) {
+ case VIDEOBUF_ACTIVE:
+ dev_dbg(dev, "%s (active)\n", __func__);
+ break;
+ case VIDEOBUF_QUEUED:
+ dev_dbg(dev, "%s (queued)\n", __func__);
+ break;
+ case VIDEOBUF_PREPARED:
+ dev_dbg(dev, "%s (prepared)\n", __func__);
+ break;
+ default:
+ dev_dbg(dev, "%s (unknown)\n", __func__);
+ break;
+ }
+#endif
+
+ free_buffer(vq, buf);
+}
+
+static void mx1_camera_wakeup(struct mx1_camera_dev *pcdev,
+ struct videobuf_buffer *vb,
+ struct mx1_buffer *buf)
+{
+ /* _init is used to debug races, see comment in mx1_camera_reqbufs() */
+ list_del_init(&vb->queue);
+ vb->state = VIDEOBUF_DONE;
+ do_gettimeofday(&vb->ts);
+ vb->field_count++;
+ wake_up(&vb->done);
+
+ if (list_empty(&pcdev->capture)) {
+ pcdev->active = NULL;
+ return;
+ }
+
+ pcdev->active = list_entry(pcdev->capture.next,
+ struct mx1_buffer, vb.queue);
+
+ /* setup sg list for future DMA */
+ if (likely(!mx1_camera_setup_dma(pcdev))) {
+ unsigned int temp;
+
+ /* enable SOF irq */
+ temp = __raw_readl(pcdev->base + CSICR1) | CSICR1_SOF_INTEN;
+ __raw_writel(temp, pcdev->base + CSICR1);
+ }
+}
+
+static void mx1_camera_dma_irq(int channel, void *data)
+{
+ struct mx1_camera_dev *pcdev = data;
+ struct device *dev = pcdev->icd->parent;
+ struct mx1_buffer *buf;
+ struct videobuf_buffer *vb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pcdev->lock, flags);
+
+ imx_dma_disable(channel);
+
+ if (unlikely(!pcdev->active)) {
+ dev_err(dev, "DMA End IRQ with no active buffer\n");
+ goto out;
+ }
+
+ vb = &pcdev->active->vb;
+ buf = container_of(vb, struct mx1_buffer, vb);
+ WARN_ON(buf->inwork || list_empty(&vb->queue));
+ dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+ vb, vb->baddr, vb->bsize);
+
+ mx1_camera_wakeup(pcdev, vb, buf);
+out:
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+}
+
+static struct videobuf_queue_ops mx1_videobuf_ops = {
+ .buf_setup = mx1_videobuf_setup,
+ .buf_prepare = mx1_videobuf_prepare,
+ .buf_queue = mx1_videobuf_queue,
+ .buf_release = mx1_videobuf_release,
+};
+
+static void mx1_camera_init_videobuf(struct videobuf_queue *q,
+ struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx1_camera_dev *pcdev = ici->priv;
+
+ videobuf_queue_dma_contig_init(q, &mx1_videobuf_ops, icd->parent,
+ &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_NONE,
+ sizeof(struct mx1_buffer), icd, &icd->video_lock);
+}
+
+static int mclk_get_divisor(struct mx1_camera_dev *pcdev)
+{
+ unsigned int mclk = pcdev->mclk;
+ unsigned long div;
+ unsigned long lcdclk;
+
+ lcdclk = clk_get_rate(pcdev->clk);
+
+ /*
+ * We verify platform_mclk_10khz != 0, so if anyone breaks it, here
+ * they get a nice Oops
+ */
+ div = (lcdclk + 2 * mclk - 1) / (2 * mclk) - 1;
+
+ dev_dbg(pcdev->icd->parent,
+ "System clock %lukHz, target freq %dkHz, divisor %lu\n",
+ lcdclk / 1000, mclk / 1000, div);
+
+ return div;
+}
+
+static void mx1_camera_activate(struct mx1_camera_dev *pcdev)
+{
+ unsigned int csicr1 = CSICR1_EN;
+
+ dev_dbg(pcdev->icd->parent, "Activate device\n");
+
+ clk_enable(pcdev->clk);
+
+ /* enable CSI before doing anything else */
+ __raw_writel(csicr1, pcdev->base + CSICR1);
+
+ csicr1 |= CSICR1_MCLKEN | CSICR1_FCC | CSICR1_GCLK_MODE;
+ csicr1 |= CSICR1_MCLKDIV(mclk_get_divisor(pcdev));
+ csicr1 |= CSICR1_RXFF_LEVEL(2); /* 16 words */
+
+ __raw_writel(csicr1, pcdev->base + CSICR1);
+}
+
+static void mx1_camera_deactivate(struct mx1_camera_dev *pcdev)
+{
+ dev_dbg(pcdev->icd->parent, "Deactivate device\n");
+
+ /* Disable all CSI interface */
+ __raw_writel(0x00, pcdev->base + CSICR1);
+
+ clk_disable(pcdev->clk);
+}
+
+/*
+ * The following two functions absolutely depend on the fact, that
+ * there can be only one camera on i.MX1/i.MXL camera sensor interface
+ */
+static int mx1_camera_add_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx1_camera_dev *pcdev = ici->priv;
+
+ if (pcdev->icd)
+ return -EBUSY;
+
+ dev_info(icd->parent, "MX1 Camera driver attached to camera %d\n",
+ icd->devnum);
+
+ mx1_camera_activate(pcdev);
+
+ pcdev->icd = icd;
+
+ return 0;
+}
+
+static void mx1_camera_remove_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx1_camera_dev *pcdev = ici->priv;
+ unsigned int csicr1;
+
+ BUG_ON(icd != pcdev->icd);
+
+ /* disable interrupts */
+ csicr1 = __raw_readl(pcdev->base + CSICR1) & ~CSI_IRQ_MASK;
+ __raw_writel(csicr1, pcdev->base + CSICR1);
+
+ /* Stop DMA engine */
+ imx_dma_disable(pcdev->dma_chan);
+
+ dev_info(icd->parent, "MX1 Camera driver detached from camera %d\n",
+ icd->devnum);
+
+ mx1_camera_deactivate(pcdev);
+
+ pcdev->icd = NULL;
+}
+
+static int mx1_camera_set_crop(struct soc_camera_device *icd,
+ struct v4l2_crop *a)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+
+ return v4l2_subdev_call(sd, video, s_crop, a);
+}
+
+static int mx1_camera_set_bus_param(struct soc_camera_device *icd)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx1_camera_dev *pcdev = ici->priv;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long common_flags;
+ unsigned int csicr1;
+ int ret;
+
+ /* MX1 supports only 8bit buswidth */
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg, CSI_BUS_FLAGS);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%x\n",
+ cfg.flags, CSI_BUS_FLAGS);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = CSI_BUS_FLAGS;
+ }
+
+ /* Make choises, based on platform choice */
+ if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
+ if (!pcdev->pdata ||
+ pcdev->pdata->flags & MX1_CAMERA_VSYNC_HIGH)
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
+ else
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
+ }
+
+ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+ (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
+ if (!pcdev->pdata ||
+ pcdev->pdata->flags & MX1_CAMERA_PCLK_RISING)
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
+ else
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
+ }
+
+ if ((common_flags & V4L2_MBUS_DATA_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)) {
+ if (!pcdev->pdata ||
+ pcdev->pdata->flags & MX1_CAMERA_DATA_HIGH)
+ common_flags &= ~V4L2_MBUS_DATA_ACTIVE_LOW;
+ else
+ common_flags &= ~V4L2_MBUS_DATA_ACTIVE_HIGH;
+ }
+
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n",
+ common_flags, ret);
+ return ret;
+ }
+
+ csicr1 = __raw_readl(pcdev->base + CSICR1);
+
+ if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
+ csicr1 |= CSICR1_REDGE;
+ if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
+ csicr1 |= CSICR1_SOF_POL;
+ if (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)
+ csicr1 |= CSICR1_DATA_POL;
+
+ __raw_writel(csicr1, pcdev->base + CSICR1);
+
+ return 0;
+}
+
+static int mx1_camera_set_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_mbus_framefmt mf;
+ int ret, buswidth;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+ if (!xlate) {
+ dev_warn(icd->parent, "Format %x not found\n",
+ pix->pixelformat);
+ return -EINVAL;
+ }
+
+ buswidth = xlate->host_fmt->bits_per_sample;
+ if (buswidth > 8) {
+ dev_warn(icd->parent,
+ "bits-per-sample %d for format %x unsupported\n",
+ buswidth, pix->pixelformat);
+ return -EINVAL;
+ }
+
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
+
+ ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ if (mf.code != xlate->code)
+ return -EINVAL;
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->field = mf.field;
+ pix->colorspace = mf.colorspace;
+ icd->current_fmt = xlate;
+
+ return ret;
+}
+
+static int mx1_camera_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_mbus_framefmt mf;
+ int ret;
+ /* TODO: limit to mx1 hardware capabilities */
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+ if (!xlate) {
+ dev_warn(icd->parent, "Format %x not found\n",
+ pix->pixelformat);
+ return -EINVAL;
+ }
+
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
+
+ /* limit to sensor capabilities */
+ ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->field = mf.field;
+ pix->colorspace = mf.colorspace;
+
+ return 0;
+}
+
+static int mx1_camera_reqbufs(struct soc_camera_device *icd,
+ struct v4l2_requestbuffers *p)
+{
+ int i;
+
+ /*
+ * This is for locking debugging only. I removed spinlocks and now I
+ * check whether .prepare is ever called on a linked buffer, or whether
+ * a dma IRQ can occur for an in-work or unlinked buffer. Until now
+ * it hadn't triggered
+ */
+ for (i = 0; i < p->count; i++) {
+ struct mx1_buffer *buf = container_of(icd->vb_vidq.bufs[i],
+ struct mx1_buffer, vb);
+ buf->inwork = 0;
+ INIT_LIST_HEAD(&buf->vb.queue);
+ }
+
+ return 0;
+}
+
+static unsigned int mx1_camera_poll(struct file *file, poll_table *pt)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct mx1_buffer *buf;
+
+ buf = list_entry(icd->vb_vidq.stream.next, struct mx1_buffer,
+ vb.stream);
+
+ poll_wait(file, &buf->vb.done, pt);
+
+ if (buf->vb.state == VIDEOBUF_DONE ||
+ buf->vb.state == VIDEOBUF_ERROR)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+static int mx1_camera_querycap(struct soc_camera_host *ici,
+ struct v4l2_capability *cap)
+{
+ /* cap->name is set by the friendly caller:-> */
+ strlcpy(cap->card, "i.MX1/i.MXL Camera", sizeof(cap->card));
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+static struct soc_camera_host_ops mx1_soc_camera_host_ops = {
+ .owner = THIS_MODULE,
+ .add = mx1_camera_add_device,
+ .remove = mx1_camera_remove_device,
+ .set_bus_param = mx1_camera_set_bus_param,
+ .set_crop = mx1_camera_set_crop,
+ .set_fmt = mx1_camera_set_fmt,
+ .try_fmt = mx1_camera_try_fmt,
+ .init_videobuf = mx1_camera_init_videobuf,
+ .reqbufs = mx1_camera_reqbufs,
+ .poll = mx1_camera_poll,
+ .querycap = mx1_camera_querycap,
+};
+
+static struct fiq_handler fh = {
+ .name = "csi_sof"
+};
+
+static int __init mx1_camera_probe(struct platform_device *pdev)
+{
+ struct mx1_camera_dev *pcdev;
+ struct resource *res;
+ struct pt_regs regs;
+ struct clk *clk;
+ void __iomem *base;
+ unsigned int irq;
+ int err = 0;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (!res || (int)irq <= 0) {
+ err = -ENODEV;
+ goto exit;
+ }
+
+ clk = clk_get(&pdev->dev, "csi_clk");
+ if (IS_ERR(clk)) {
+ err = PTR_ERR(clk);
+ goto exit;
+ }
+
+ pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL);
+ if (!pcdev) {
+ dev_err(&pdev->dev, "Could not allocate pcdev\n");
+ err = -ENOMEM;
+ goto exit_put_clk;
+ }
+
+ pcdev->res = res;
+ pcdev->clk = clk;
+
+ pcdev->pdata = pdev->dev.platform_data;
+
+ if (pcdev->pdata)
+ pcdev->mclk = pcdev->pdata->mclk_10khz * 10000;
+
+ if (!pcdev->mclk) {
+ dev_warn(&pdev->dev,
+ "mclk_10khz == 0! Please, fix your platform data. "
+ "Using default 20MHz\n");
+ pcdev->mclk = 20000000;
+ }
+
+ INIT_LIST_HEAD(&pcdev->capture);
+ spin_lock_init(&pcdev->lock);
+
+ /*
+ * Request the regions.
+ */
+ if (!request_mem_region(res->start, resource_size(res), DRIVER_NAME)) {
+ err = -EBUSY;
+ goto exit_kfree;
+ }
+
+ base = ioremap(res->start, resource_size(res));
+ if (!base) {
+ err = -ENOMEM;
+ goto exit_release;
+ }
+ pcdev->irq = irq;
+ pcdev->base = base;
+
+ /* request dma */
+ pcdev->dma_chan = imx_dma_request_by_prio(DRIVER_NAME, DMA_PRIO_HIGH);
+ if (pcdev->dma_chan < 0) {
+ dev_err(&pdev->dev, "Can't request DMA for MX1 CSI\n");
+ err = -EBUSY;
+ goto exit_iounmap;
+ }
+ dev_dbg(&pdev->dev, "got DMA channel %d\n", pcdev->dma_chan);
+
+ imx_dma_setup_handlers(pcdev->dma_chan, mx1_camera_dma_irq, NULL,
+ pcdev);
+
+ imx_dma_config_channel(pcdev->dma_chan, IMX_DMA_TYPE_FIFO,
+ IMX_DMA_MEMSIZE_32, MX1_DMA_REQ_CSI_R, 0);
+ /* burst length : 16 words = 64 bytes */
+ imx_dma_config_burstlen(pcdev->dma_chan, 0);
+
+ /* request irq */
+ err = claim_fiq(&fh);
+ if (err) {
+ dev_err(&pdev->dev, "Camera interrupt register failed \n");
+ goto exit_free_dma;
+ }
+
+ set_fiq_handler(&mx1_camera_sof_fiq_start, &mx1_camera_sof_fiq_end -
+ &mx1_camera_sof_fiq_start);
+
+ regs.ARM_r8 = (long)MX1_DMA_DIMR;
+ regs.ARM_r9 = (long)MX1_DMA_CCR(pcdev->dma_chan);
+ regs.ARM_r10 = (long)pcdev->base + CSICR1;
+ regs.ARM_fp = (long)pcdev->base + CSISR;
+ regs.ARM_sp = 1 << pcdev->dma_chan;
+ set_fiq_regs(&regs);
+
+ mxc_set_irq_fiq(irq, 1);
+ enable_fiq(irq);
+
+ pcdev->soc_host.drv_name = DRIVER_NAME;
+ pcdev->soc_host.ops = &mx1_soc_camera_host_ops;
+ pcdev->soc_host.priv = pcdev;
+ pcdev->soc_host.v4l2_dev.dev = &pdev->dev;
+ pcdev->soc_host.nr = pdev->id;
+ err = soc_camera_host_register(&pcdev->soc_host);
+ if (err)
+ goto exit_free_irq;
+
+ dev_info(&pdev->dev, "MX1 Camera driver loaded\n");
+
+ return 0;
+
+exit_free_irq:
+ disable_fiq(irq);
+ mxc_set_irq_fiq(irq, 0);
+ release_fiq(&fh);
+exit_free_dma:
+ imx_dma_free(pcdev->dma_chan);
+exit_iounmap:
+ iounmap(base);
+exit_release:
+ release_mem_region(res->start, resource_size(res));
+exit_kfree:
+ kfree(pcdev);
+exit_put_clk:
+ clk_put(clk);
+exit:
+ return err;
+}
+
+static int __exit mx1_camera_remove(struct platform_device *pdev)
+{
+ struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+ struct mx1_camera_dev *pcdev = container_of(soc_host,
+ struct mx1_camera_dev, soc_host);
+ struct resource *res;
+
+ imx_dma_free(pcdev->dma_chan);
+ disable_fiq(pcdev->irq);
+ mxc_set_irq_fiq(pcdev->irq, 0);
+ release_fiq(&fh);
+
+ clk_put(pcdev->clk);
+
+ soc_camera_host_unregister(soc_host);
+
+ iounmap(pcdev->base);
+
+ res = pcdev->res;
+ release_mem_region(res->start, resource_size(res));
+
+ kfree(pcdev);
+
+ dev_info(&pdev->dev, "MX1 Camera driver unloaded\n");
+
+ return 0;
+}
+
+static struct platform_driver mx1_camera_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .remove = __exit_p(mx1_camera_remove),
+};
+
+static int __init mx1_camera_init(void)
+{
+ return platform_driver_probe(&mx1_camera_driver, mx1_camera_probe);
+}
+
+static void __exit mx1_camera_exit(void)
+{
+ return platform_driver_unregister(&mx1_camera_driver);
+}
+
+module_init(mx1_camera_init);
+module_exit(mx1_camera_exit);
+
+MODULE_DESCRIPTION("i.MX1/i.MXL SoC Camera Host driver");
+MODULE_AUTHOR("Paulius Zaleckas <paulius.zaleckas@teltonika.lt>");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c
new file mode 100644
index 00000000..18afaeea
--- /dev/null
+++ b/drivers/media/video/mx2_camera.c
@@ -0,0 +1,1856 @@
+/*
+ * V4L2 Driver for i.MX27/i.MX25 camera host
+ *
+ * Copyright (C) 2008, Sascha Hauer, Pengutronix
+ * Copyright (C) 2010, Baruch Siach, Orex Computed Radiography
+ * Copyright (C) 2012, Javier Martin, Vista Silicon S.L.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/gcd.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/clk.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
+
+#include <linux/videodev2.h>
+
+#include <mach/mx2_cam.h>
+#include <mach/hardware.h>
+
+#include <asm/dma.h>
+
+#define MX2_CAM_DRV_NAME "mx2-camera"
+#define MX2_CAM_VERSION "0.0.6"
+#define MX2_CAM_DRIVER_DESCRIPTION "i.MX2x_Camera"
+
+/* reset values */
+#define CSICR1_RESET_VAL 0x40000800
+#define CSICR2_RESET_VAL 0x0
+#define CSICR3_RESET_VAL 0x0
+
+/* csi control reg 1 */
+#define CSICR1_SWAP16_EN (1 << 31)
+#define CSICR1_EXT_VSYNC (1 << 30)
+#define CSICR1_EOF_INTEN (1 << 29)
+#define CSICR1_PRP_IF_EN (1 << 28)
+#define CSICR1_CCIR_MODE (1 << 27)
+#define CSICR1_COF_INTEN (1 << 26)
+#define CSICR1_SF_OR_INTEN (1 << 25)
+#define CSICR1_RF_OR_INTEN (1 << 24)
+#define CSICR1_STATFF_LEVEL (3 << 22)
+#define CSICR1_STATFF_INTEN (1 << 21)
+#define CSICR1_RXFF_LEVEL(l) (((l) & 3) << 19) /* MX27 */
+#define CSICR1_FB2_DMA_INTEN (1 << 20) /* MX25 */
+#define CSICR1_FB1_DMA_INTEN (1 << 19) /* MX25 */
+#define CSICR1_RXFF_INTEN (1 << 18)
+#define CSICR1_SOF_POL (1 << 17)
+#define CSICR1_SOF_INTEN (1 << 16)
+#define CSICR1_MCLKDIV(d) (((d) & 0xF) << 12)
+#define CSICR1_HSYNC_POL (1 << 11)
+#define CSICR1_CCIR_EN (1 << 10)
+#define CSICR1_MCLKEN (1 << 9)
+#define CSICR1_FCC (1 << 8)
+#define CSICR1_PACK_DIR (1 << 7)
+#define CSICR1_CLR_STATFIFO (1 << 6)
+#define CSICR1_CLR_RXFIFO (1 << 5)
+#define CSICR1_GCLK_MODE (1 << 4)
+#define CSICR1_INV_DATA (1 << 3)
+#define CSICR1_INV_PCLK (1 << 2)
+#define CSICR1_REDGE (1 << 1)
+
+#define SHIFT_STATFF_LEVEL 22
+#define SHIFT_RXFF_LEVEL 19
+#define SHIFT_MCLKDIV 12
+
+/* control reg 3 */
+#define CSICR3_FRMCNT (0xFFFF << 16)
+#define CSICR3_FRMCNT_RST (1 << 15)
+#define CSICR3_DMA_REFLASH_RFF (1 << 14)
+#define CSICR3_DMA_REFLASH_SFF (1 << 13)
+#define CSICR3_DMA_REQ_EN_RFF (1 << 12)
+#define CSICR3_DMA_REQ_EN_SFF (1 << 11)
+#define CSICR3_RXFF_LEVEL(l) (((l) & 7) << 4) /* MX25 */
+#define CSICR3_CSI_SUP (1 << 3)
+#define CSICR3_ZERO_PACK_EN (1 << 2)
+#define CSICR3_ECC_INT_EN (1 << 1)
+#define CSICR3_ECC_AUTO_EN (1 << 0)
+
+#define SHIFT_FRMCNT 16
+
+/* csi status reg */
+#define CSISR_SFF_OR_INT (1 << 25)
+#define CSISR_RFF_OR_INT (1 << 24)
+#define CSISR_STATFF_INT (1 << 21)
+#define CSISR_DMA_TSF_FB2_INT (1 << 20) /* MX25 */
+#define CSISR_DMA_TSF_FB1_INT (1 << 19) /* MX25 */
+#define CSISR_RXFF_INT (1 << 18)
+#define CSISR_EOF_INT (1 << 17)
+#define CSISR_SOF_INT (1 << 16)
+#define CSISR_F2_INT (1 << 15)
+#define CSISR_F1_INT (1 << 14)
+#define CSISR_COF_INT (1 << 13)
+#define CSISR_ECC_INT (1 << 1)
+#define CSISR_DRDY (1 << 0)
+
+#define CSICR1 0x00
+#define CSICR2 0x04
+#define CSISR (cpu_is_mx27() ? 0x08 : 0x18)
+#define CSISTATFIFO 0x0c
+#define CSIRFIFO 0x10
+#define CSIRXCNT 0x14
+#define CSICR3 (cpu_is_mx27() ? 0x1C : 0x08)
+#define CSIDMASA_STATFIFO 0x20
+#define CSIDMATA_STATFIFO 0x24
+#define CSIDMASA_FB1 0x28
+#define CSIDMASA_FB2 0x2c
+#define CSIFBUF_PARA 0x30
+#define CSIIMAG_PARA 0x34
+
+/* EMMA PrP */
+#define PRP_CNTL 0x00
+#define PRP_INTR_CNTL 0x04
+#define PRP_INTRSTATUS 0x08
+#define PRP_SOURCE_Y_PTR 0x0c
+#define PRP_SOURCE_CB_PTR 0x10
+#define PRP_SOURCE_CR_PTR 0x14
+#define PRP_DEST_RGB1_PTR 0x18
+#define PRP_DEST_RGB2_PTR 0x1c
+#define PRP_DEST_Y_PTR 0x20
+#define PRP_DEST_CB_PTR 0x24
+#define PRP_DEST_CR_PTR 0x28
+#define PRP_SRC_FRAME_SIZE 0x2c
+#define PRP_DEST_CH1_LINE_STRIDE 0x30
+#define PRP_SRC_PIXEL_FORMAT_CNTL 0x34
+#define PRP_CH1_PIXEL_FORMAT_CNTL 0x38
+#define PRP_CH1_OUT_IMAGE_SIZE 0x3c
+#define PRP_CH2_OUT_IMAGE_SIZE 0x40
+#define PRP_SRC_LINE_STRIDE 0x44
+#define PRP_CSC_COEF_012 0x48
+#define PRP_CSC_COEF_345 0x4c
+#define PRP_CSC_COEF_678 0x50
+#define PRP_CH1_RZ_HORI_COEF1 0x54
+#define PRP_CH1_RZ_HORI_COEF2 0x58
+#define PRP_CH1_RZ_HORI_VALID 0x5c
+#define PRP_CH1_RZ_VERT_COEF1 0x60
+#define PRP_CH1_RZ_VERT_COEF2 0x64
+#define PRP_CH1_RZ_VERT_VALID 0x68
+#define PRP_CH2_RZ_HORI_COEF1 0x6c
+#define PRP_CH2_RZ_HORI_COEF2 0x70
+#define PRP_CH2_RZ_HORI_VALID 0x74
+#define PRP_CH2_RZ_VERT_COEF1 0x78
+#define PRP_CH2_RZ_VERT_COEF2 0x7c
+#define PRP_CH2_RZ_VERT_VALID 0x80
+
+#define PRP_CNTL_CH1EN (1 << 0)
+#define PRP_CNTL_CH2EN (1 << 1)
+#define PRP_CNTL_CSIEN (1 << 2)
+#define PRP_CNTL_DATA_IN_YUV420 (0 << 3)
+#define PRP_CNTL_DATA_IN_YUV422 (1 << 3)
+#define PRP_CNTL_DATA_IN_RGB16 (2 << 3)
+#define PRP_CNTL_DATA_IN_RGB32 (3 << 3)
+#define PRP_CNTL_CH1_OUT_RGB8 (0 << 5)
+#define PRP_CNTL_CH1_OUT_RGB16 (1 << 5)
+#define PRP_CNTL_CH1_OUT_RGB32 (2 << 5)
+#define PRP_CNTL_CH1_OUT_YUV422 (3 << 5)
+#define PRP_CNTL_CH2_OUT_YUV420 (0 << 7)
+#define PRP_CNTL_CH2_OUT_YUV422 (1 << 7)
+#define PRP_CNTL_CH2_OUT_YUV444 (2 << 7)
+#define PRP_CNTL_CH1_LEN (1 << 9)
+#define PRP_CNTL_CH2_LEN (1 << 10)
+#define PRP_CNTL_SKIP_FRAME (1 << 11)
+#define PRP_CNTL_SWRST (1 << 12)
+#define PRP_CNTL_CLKEN (1 << 13)
+#define PRP_CNTL_WEN (1 << 14)
+#define PRP_CNTL_CH1BYP (1 << 15)
+#define PRP_CNTL_IN_TSKIP(x) ((x) << 16)
+#define PRP_CNTL_CH1_TSKIP(x) ((x) << 19)
+#define PRP_CNTL_CH2_TSKIP(x) ((x) << 22)
+#define PRP_CNTL_INPUT_FIFO_LEVEL(x) ((x) << 25)
+#define PRP_CNTL_RZ_FIFO_LEVEL(x) ((x) << 27)
+#define PRP_CNTL_CH2B1EN (1 << 29)
+#define PRP_CNTL_CH2B2EN (1 << 30)
+#define PRP_CNTL_CH2FEN (1 << 31)
+
+/* IRQ Enable and status register */
+#define PRP_INTR_RDERR (1 << 0)
+#define PRP_INTR_CH1WERR (1 << 1)
+#define PRP_INTR_CH2WERR (1 << 2)
+#define PRP_INTR_CH1FC (1 << 3)
+#define PRP_INTR_CH2FC (1 << 5)
+#define PRP_INTR_LBOVF (1 << 7)
+#define PRP_INTR_CH2OVF (1 << 8)
+
+/* Resizing registers */
+#define PRP_RZ_VALID_TBL_LEN(x) ((x) << 24)
+#define PRP_RZ_VALID_BILINEAR (1 << 31)
+
+#define MAX_VIDEO_MEM 16
+
+#define RESIZE_NUM_MIN 1
+#define RESIZE_NUM_MAX 20
+#define BC_COEF 3
+#define SZ_COEF (1 << BC_COEF)
+
+#define RESIZE_DIR_H 0
+#define RESIZE_DIR_V 1
+
+#define RESIZE_ALGO_BILINEAR 0
+#define RESIZE_ALGO_AVERAGING 1
+
+struct mx2_prp_cfg {
+ int channel;
+ u32 in_fmt;
+ u32 out_fmt;
+ u32 src_pixel;
+ u32 ch1_pixel;
+ u32 irq_flags;
+};
+
+/* prp resizing parameters */
+struct emma_prp_resize {
+ int algo; /* type of algorithm used */
+ int len; /* number of coefficients */
+ unsigned char s[RESIZE_NUM_MAX]; /* table of coefficients */
+};
+
+/* prp configuration for a client-host fmt pair */
+struct mx2_fmt_cfg {
+ enum v4l2_mbus_pixelcode in_fmt;
+ u32 out_fmt;
+ struct mx2_prp_cfg cfg;
+};
+
+enum mx2_buffer_state {
+ MX2_STATE_QUEUED,
+ MX2_STATE_ACTIVE,
+ MX2_STATE_DONE,
+};
+
+struct mx2_buf_internal {
+ struct list_head queue;
+ int bufnum;
+ bool discard;
+};
+
+/* buffer for one video frame */
+struct mx2_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct vb2_buffer vb;
+ enum mx2_buffer_state state;
+ struct mx2_buf_internal internal;
+};
+
+struct mx2_camera_dev {
+ struct device *dev;
+ struct soc_camera_host soc_host;
+ struct soc_camera_device *icd;
+ struct clk *clk_csi, *clk_emma;
+
+ unsigned int irq_csi, irq_emma;
+ void __iomem *base_csi, *base_emma;
+ unsigned long base_dma;
+
+ struct mx2_camera_platform_data *pdata;
+ struct resource *res_csi, *res_emma;
+ unsigned long platform_flags;
+
+ struct list_head capture;
+ struct list_head active_bufs;
+ struct list_head discard;
+
+ spinlock_t lock;
+
+ int dma;
+ struct mx2_buffer *active;
+ struct mx2_buffer *fb1_active;
+ struct mx2_buffer *fb2_active;
+
+ u32 csicr1;
+
+ struct mx2_buf_internal buf_discard[2];
+ void *discard_buffer;
+ dma_addr_t discard_buffer_dma;
+ size_t discard_size;
+ struct mx2_fmt_cfg *emma_prp;
+ struct emma_prp_resize resizing[2];
+ unsigned int s_width, s_height;
+ u32 frame_count;
+ struct vb2_alloc_ctx *alloc_ctx;
+};
+
+static struct mx2_buffer *mx2_ibuf_to_buf(struct mx2_buf_internal *int_buf)
+{
+ return container_of(int_buf, struct mx2_buffer, internal);
+}
+
+static struct mx2_fmt_cfg mx27_emma_prp_table[] = {
+ /*
+ * This is a generic configuration which is valid for most
+ * prp input-output format combinations.
+ * We set the incomming and outgoing pixelformat to a
+ * 16 Bit wide format and adjust the bytesperline
+ * accordingly. With this configuration the inputdata
+ * will not be changed by the emma and could be any type
+ * of 16 Bit Pixelformat.
+ */
+ {
+ .in_fmt = 0,
+ .out_fmt = 0,
+ .cfg = {
+ .channel = 1,
+ .in_fmt = PRP_CNTL_DATA_IN_RGB16,
+ .out_fmt = PRP_CNTL_CH1_OUT_RGB16,
+ .src_pixel = 0x2ca00565, /* RGB565 */
+ .ch1_pixel = 0x2ca00565, /* RGB565 */
+ .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH1WERR |
+ PRP_INTR_CH1FC | PRP_INTR_LBOVF,
+ }
+ },
+ {
+ .in_fmt = V4L2_MBUS_FMT_YUYV8_2X8,
+ .out_fmt = V4L2_PIX_FMT_YUV420,
+ .cfg = {
+ .channel = 2,
+ .in_fmt = PRP_CNTL_DATA_IN_YUV422,
+ .out_fmt = PRP_CNTL_CH2_OUT_YUV420,
+ .src_pixel = 0x22000888, /* YUV422 (YUYV) */
+ .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH2WERR |
+ PRP_INTR_CH2FC | PRP_INTR_LBOVF |
+ PRP_INTR_CH2OVF,
+ }
+ },
+};
+
+static struct mx2_fmt_cfg *mx27_emma_prp_get_format(
+ enum v4l2_mbus_pixelcode in_fmt,
+ u32 out_fmt)
+{
+ int i;
+
+ for (i = 1; i < ARRAY_SIZE(mx27_emma_prp_table); i++)
+ if ((mx27_emma_prp_table[i].in_fmt == in_fmt) &&
+ (mx27_emma_prp_table[i].out_fmt == out_fmt)) {
+ return &mx27_emma_prp_table[i];
+ }
+ /* If no match return the most generic configuration */
+ return &mx27_emma_prp_table[0];
+};
+
+static void mx27_update_emma_buf(struct mx2_camera_dev *pcdev,
+ unsigned long phys, int bufnum)
+{
+ struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+
+ if (prp->cfg.channel == 1) {
+ writel(phys, pcdev->base_emma +
+ PRP_DEST_RGB1_PTR + 4 * bufnum);
+ } else {
+ writel(phys, pcdev->base_emma +
+ PRP_DEST_Y_PTR - 0x14 * bufnum);
+ if (prp->out_fmt == V4L2_PIX_FMT_YUV420) {
+ u32 imgsize = pcdev->icd->user_height *
+ pcdev->icd->user_width;
+
+ writel(phys + imgsize, pcdev->base_emma +
+ PRP_DEST_CB_PTR - 0x14 * bufnum);
+ writel(phys + ((5 * imgsize) / 4), pcdev->base_emma +
+ PRP_DEST_CR_PTR - 0x14 * bufnum);
+ }
+ }
+}
+
+static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev)
+{
+ unsigned long flags;
+
+ clk_disable(pcdev->clk_csi);
+ writel(0, pcdev->base_csi + CSICR1);
+ if (cpu_is_mx27()) {
+ writel(0, pcdev->base_emma + PRP_CNTL);
+ } else if (cpu_is_mx25()) {
+ spin_lock_irqsave(&pcdev->lock, flags);
+ pcdev->fb1_active = NULL;
+ pcdev->fb2_active = NULL;
+ writel(0, pcdev->base_csi + CSIDMASA_FB1);
+ writel(0, pcdev->base_csi + CSIDMASA_FB2);
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+ }
+}
+
+/*
+ * The following two functions absolutely depend on the fact, that
+ * there can be only one camera on mx2 camera sensor interface
+ */
+static int mx2_camera_add_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ int ret;
+ u32 csicr1;
+
+ if (pcdev->icd)
+ return -EBUSY;
+
+ ret = clk_enable(pcdev->clk_csi);
+ if (ret < 0)
+ return ret;
+
+ csicr1 = CSICR1_MCLKEN;
+
+ if (cpu_is_mx27()) {
+ csicr1 |= CSICR1_PRP_IF_EN | CSICR1_FCC |
+ CSICR1_RXFF_LEVEL(0);
+ } else if (cpu_is_mx27())
+ csicr1 |= CSICR1_SOF_INTEN | CSICR1_RXFF_LEVEL(2);
+
+ pcdev->csicr1 = csicr1;
+ writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
+
+ pcdev->icd = icd;
+ pcdev->frame_count = 0;
+
+ dev_info(icd->parent, "Camera driver attached to camera %d\n",
+ icd->devnum);
+
+ return 0;
+}
+
+static void mx2_camera_remove_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+
+ BUG_ON(icd != pcdev->icd);
+
+ dev_info(icd->parent, "Camera driver detached from camera %d\n",
+ icd->devnum);
+
+ mx2_camera_deactivate(pcdev);
+
+ pcdev->icd = NULL;
+}
+
+static void mx25_camera_frame_done(struct mx2_camera_dev *pcdev, int fb,
+ int state)
+{
+ struct vb2_buffer *vb;
+ struct mx2_buffer *buf;
+ struct mx2_buffer **fb_active = fb == 1 ? &pcdev->fb1_active :
+ &pcdev->fb2_active;
+ u32 fb_reg = fb == 1 ? CSIDMASA_FB1 : CSIDMASA_FB2;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pcdev->lock, flags);
+
+ if (*fb_active == NULL)
+ goto out;
+
+ vb = &(*fb_active)->vb;
+ dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+ vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
+
+ do_gettimeofday(&vb->v4l2_buf.timestamp);
+ vb->v4l2_buf.sequence++;
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+
+ if (list_empty(&pcdev->capture)) {
+ buf = NULL;
+ writel(0, pcdev->base_csi + fb_reg);
+ } else {
+ buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
+ internal.queue);
+ vb = &buf->vb;
+ list_del(&buf->internal.queue);
+ buf->state = MX2_STATE_ACTIVE;
+ writel(vb2_dma_contig_plane_dma_addr(vb, 0),
+ pcdev->base_csi + fb_reg);
+ }
+
+ *fb_active = buf;
+
+out:
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+}
+
+static irqreturn_t mx25_camera_irq(int irq_csi, void *data)
+{
+ struct mx2_camera_dev *pcdev = data;
+ u32 status = readl(pcdev->base_csi + CSISR);
+
+ if (status & CSISR_DMA_TSF_FB1_INT)
+ mx25_camera_frame_done(pcdev, 1, MX2_STATE_DONE);
+ else if (status & CSISR_DMA_TSF_FB2_INT)
+ mx25_camera_frame_done(pcdev, 2, MX2_STATE_DONE);
+
+ /* FIXME: handle CSISR_RFF_OR_INT */
+
+ writel(status, pcdev->base_csi + CSISR);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Videobuf operations
+ */
+static int mx2_videobuf_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *count, unsigned int *num_planes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+
+ dev_dbg(icd->parent, "count=%d, size=%d\n", *count, sizes[0]);
+
+ /* TODO: support for VIDIOC_CREATE_BUFS not ready */
+ if (fmt != NULL)
+ return -ENOTTY;
+
+ if (bytes_per_line < 0)
+ return bytes_per_line;
+
+ alloc_ctxs[0] = pcdev->alloc_ctx;
+
+ sizes[0] = bytes_per_line * icd->user_height;
+
+ if (0 == *count)
+ *count = 32;
+ if (!*num_planes &&
+ sizes[0] * *count > MAX_VIDEO_MEM * 1024 * 1024)
+ *count = (MAX_VIDEO_MEM * 1024 * 1024) / sizes[0];
+
+ *num_planes = 1;
+
+ return 0;
+}
+
+static int mx2_videobuf_prepare(struct vb2_buffer *vb)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+ int ret = 0;
+
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+ vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
+
+ if (bytes_per_line < 0)
+ return bytes_per_line;
+
+#ifdef DEBUG
+ /*
+ * This can be useful if you want to see if we actually fill
+ * the buffer with something
+ */
+ memset((void *)vb2_plane_vaddr(vb, 0),
+ 0xaa, vb2_get_plane_payload(vb, 0));
+#endif
+
+ vb2_set_plane_payload(vb, 0, bytes_per_line * icd->user_height);
+ if (vb2_plane_vaddr(vb, 0) &&
+ vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ return 0;
+
+out:
+ return ret;
+}
+
+static void mx2_videobuf_queue(struct vb2_buffer *vb)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct soc_camera_host *ici =
+ to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb);
+ unsigned long flags;
+
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+ vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
+
+ spin_lock_irqsave(&pcdev->lock, flags);
+
+ buf->state = MX2_STATE_QUEUED;
+ list_add_tail(&buf->internal.queue, &pcdev->capture);
+
+ if (cpu_is_mx25()) {
+ u32 csicr3, dma_inten = 0;
+
+ if (pcdev->fb1_active == NULL) {
+ writel(vb2_dma_contig_plane_dma_addr(vb, 0),
+ pcdev->base_csi + CSIDMASA_FB1);
+ pcdev->fb1_active = buf;
+ dma_inten = CSICR1_FB1_DMA_INTEN;
+ } else if (pcdev->fb2_active == NULL) {
+ writel(vb2_dma_contig_plane_dma_addr(vb, 0),
+ pcdev->base_csi + CSIDMASA_FB2);
+ pcdev->fb2_active = buf;
+ dma_inten = CSICR1_FB2_DMA_INTEN;
+ }
+
+ if (dma_inten) {
+ list_del(&buf->internal.queue);
+ buf->state = MX2_STATE_ACTIVE;
+
+ csicr3 = readl(pcdev->base_csi + CSICR3);
+
+ /* Reflash DMA */
+ writel(csicr3 | CSICR3_DMA_REFLASH_RFF,
+ pcdev->base_csi + CSICR3);
+
+ /* clear & enable interrupts */
+ writel(dma_inten, pcdev->base_csi + CSISR);
+ pcdev->csicr1 |= dma_inten;
+ writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
+
+ /* enable DMA */
+ csicr3 |= CSICR3_DMA_REQ_EN_RFF | CSICR3_RXFF_LEVEL(1);
+ writel(csicr3, pcdev->base_csi + CSICR3);
+ }
+ }
+
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+}
+
+static void mx2_videobuf_release(struct vb2_buffer *vb)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb);
+ unsigned long flags;
+
+#ifdef DEBUG
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+ vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
+
+ switch (buf->state) {
+ case MX2_STATE_ACTIVE:
+ dev_info(icd->parent, "%s (active)\n", __func__);
+ break;
+ case MX2_STATE_QUEUED:
+ dev_info(icd->parent, "%s (queued)\n", __func__);
+ break;
+ default:
+ dev_info(icd->parent, "%s (unknown) %d\n", __func__,
+ buf->state);
+ break;
+ }
+#endif
+
+ /*
+ * Terminate only queued but inactive buffers. Active buffers are
+ * released when they become inactive after videobuf_waiton().
+ *
+ * FIXME: implement forced termination of active buffers for mx27 and
+ * mx27 eMMA, so that the user won't get stuck in an uninterruptible
+ * state. This requires a specific handling for each of the these DMA
+ * types.
+ */
+
+ spin_lock_irqsave(&pcdev->lock, flags);
+ if (cpu_is_mx25() && buf->state == MX2_STATE_ACTIVE) {
+ if (pcdev->fb1_active == buf) {
+ pcdev->csicr1 &= ~CSICR1_FB1_DMA_INTEN;
+ writel(0, pcdev->base_csi + CSIDMASA_FB1);
+ pcdev->fb1_active = NULL;
+ } else if (pcdev->fb2_active == buf) {
+ pcdev->csicr1 &= ~CSICR1_FB2_DMA_INTEN;
+ writel(0, pcdev->base_csi + CSIDMASA_FB2);
+ pcdev->fb2_active = NULL;
+ }
+ writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
+ }
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+}
+
+static void mx27_camera_emma_buf_init(struct soc_camera_device *icd,
+ int bytesperline)
+{
+ struct soc_camera_host *ici =
+ to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+
+ writel((pcdev->s_width << 16) | pcdev->s_height,
+ pcdev->base_emma + PRP_SRC_FRAME_SIZE);
+ writel(prp->cfg.src_pixel,
+ pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL);
+ if (prp->cfg.channel == 1) {
+ writel((icd->user_width << 16) | icd->user_height,
+ pcdev->base_emma + PRP_CH1_OUT_IMAGE_SIZE);
+ writel(bytesperline,
+ pcdev->base_emma + PRP_DEST_CH1_LINE_STRIDE);
+ writel(prp->cfg.ch1_pixel,
+ pcdev->base_emma + PRP_CH1_PIXEL_FORMAT_CNTL);
+ } else { /* channel 2 */
+ writel((icd->user_width << 16) | icd->user_height,
+ pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE);
+ }
+
+ /* Enable interrupts */
+ writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL);
+}
+
+static void mx2_prp_resize_commit(struct mx2_camera_dev *pcdev)
+{
+ int dir;
+
+ for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) {
+ unsigned char *s = pcdev->resizing[dir].s;
+ int len = pcdev->resizing[dir].len;
+ unsigned int coeff[2] = {0, 0};
+ unsigned int valid = 0;
+ int i;
+
+ if (len == 0)
+ continue;
+
+ for (i = RESIZE_NUM_MAX - 1; i >= 0; i--) {
+ int j;
+
+ j = i > 9 ? 1 : 0;
+ coeff[j] = (coeff[j] << BC_COEF) |
+ (s[i] & (SZ_COEF - 1));
+
+ if (i == 5 || i == 15)
+ coeff[j] <<= 1;
+
+ valid = (valid << 1) | (s[i] >> BC_COEF);
+ }
+
+ valid |= PRP_RZ_VALID_TBL_LEN(len);
+
+ if (pcdev->resizing[dir].algo == RESIZE_ALGO_BILINEAR)
+ valid |= PRP_RZ_VALID_BILINEAR;
+
+ if (pcdev->emma_prp->cfg.channel == 1) {
+ if (dir == RESIZE_DIR_H) {
+ writel(coeff[0], pcdev->base_emma +
+ PRP_CH1_RZ_HORI_COEF1);
+ writel(coeff[1], pcdev->base_emma +
+ PRP_CH1_RZ_HORI_COEF2);
+ writel(valid, pcdev->base_emma +
+ PRP_CH1_RZ_HORI_VALID);
+ } else {
+ writel(coeff[0], pcdev->base_emma +
+ PRP_CH1_RZ_VERT_COEF1);
+ writel(coeff[1], pcdev->base_emma +
+ PRP_CH1_RZ_VERT_COEF2);
+ writel(valid, pcdev->base_emma +
+ PRP_CH1_RZ_VERT_VALID);
+ }
+ } else {
+ if (dir == RESIZE_DIR_H) {
+ writel(coeff[0], pcdev->base_emma +
+ PRP_CH2_RZ_HORI_COEF1);
+ writel(coeff[1], pcdev->base_emma +
+ PRP_CH2_RZ_HORI_COEF2);
+ writel(valid, pcdev->base_emma +
+ PRP_CH2_RZ_HORI_VALID);
+ } else {
+ writel(coeff[0], pcdev->base_emma +
+ PRP_CH2_RZ_VERT_COEF1);
+ writel(coeff[1], pcdev->base_emma +
+ PRP_CH2_RZ_VERT_COEF2);
+ writel(valid, pcdev->base_emma +
+ PRP_CH2_RZ_VERT_VALID);
+ }
+ }
+ }
+}
+
+static int mx2_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(q);
+ struct soc_camera_host *ici =
+ to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+ struct vb2_buffer *vb;
+ struct mx2_buffer *buf;
+ unsigned long phys;
+ int bytesperline;
+
+ if (cpu_is_mx27()) {
+ unsigned long flags;
+ if (count < 2)
+ return -EINVAL;
+
+ spin_lock_irqsave(&pcdev->lock, flags);
+
+ buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
+ internal.queue);
+ buf->internal.bufnum = 0;
+ vb = &buf->vb;
+ buf->state = MX2_STATE_ACTIVE;
+
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+ mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum);
+ list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
+
+ buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
+ internal.queue);
+ buf->internal.bufnum = 1;
+ vb = &buf->vb;
+ buf->state = MX2_STATE_ACTIVE;
+
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+ mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum);
+ list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
+
+ bytesperline = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+ if (bytesperline < 0)
+ return bytesperline;
+
+ /*
+ * I didn't manage to properly enable/disable the prp
+ * on a per frame basis during running transfers,
+ * thus we allocate a buffer here and use it to
+ * discard frames when no buffer is available.
+ * Feel free to work on this ;)
+ */
+ pcdev->discard_size = icd->user_height * bytesperline;
+ pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev,
+ pcdev->discard_size, &pcdev->discard_buffer_dma,
+ GFP_KERNEL);
+ if (!pcdev->discard_buffer)
+ return -ENOMEM;
+
+ pcdev->buf_discard[0].discard = true;
+ list_add_tail(&pcdev->buf_discard[0].queue,
+ &pcdev->discard);
+
+ pcdev->buf_discard[1].discard = true;
+ list_add_tail(&pcdev->buf_discard[1].queue,
+ &pcdev->discard);
+
+ mx2_prp_resize_commit(pcdev);
+
+ mx27_camera_emma_buf_init(icd, bytesperline);
+
+ if (prp->cfg.channel == 1) {
+ writel(PRP_CNTL_CH1EN |
+ PRP_CNTL_CSIEN |
+ prp->cfg.in_fmt |
+ prp->cfg.out_fmt |
+ PRP_CNTL_CH1_LEN |
+ PRP_CNTL_CH1BYP |
+ PRP_CNTL_CH1_TSKIP(0) |
+ PRP_CNTL_IN_TSKIP(0),
+ pcdev->base_emma + PRP_CNTL);
+ } else {
+ writel(PRP_CNTL_CH2EN |
+ PRP_CNTL_CSIEN |
+ prp->cfg.in_fmt |
+ prp->cfg.out_fmt |
+ PRP_CNTL_CH2_LEN |
+ PRP_CNTL_CH2_TSKIP(0) |
+ PRP_CNTL_IN_TSKIP(0),
+ pcdev->base_emma + PRP_CNTL);
+ }
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+ }
+
+ return 0;
+}
+
+static int mx2_stop_streaming(struct vb2_queue *q)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(q);
+ struct soc_camera_host *ici =
+ to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+ unsigned long flags;
+ void *b;
+ u32 cntl;
+
+ if (cpu_is_mx27()) {
+ spin_lock_irqsave(&pcdev->lock, flags);
+
+ cntl = readl(pcdev->base_emma + PRP_CNTL);
+ if (prp->cfg.channel == 1) {
+ writel(cntl & ~PRP_CNTL_CH1EN,
+ pcdev->base_emma + PRP_CNTL);
+ } else {
+ writel(cntl & ~PRP_CNTL_CH2EN,
+ pcdev->base_emma + PRP_CNTL);
+ }
+ INIT_LIST_HEAD(&pcdev->capture);
+ INIT_LIST_HEAD(&pcdev->active_bufs);
+ INIT_LIST_HEAD(&pcdev->discard);
+
+ b = pcdev->discard_buffer;
+ pcdev->discard_buffer = NULL;
+
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+
+ dma_free_coherent(ici->v4l2_dev.dev,
+ pcdev->discard_size, b, pcdev->discard_buffer_dma);
+ }
+
+ return 0;
+}
+
+static struct vb2_ops mx2_videobuf_ops = {
+ .queue_setup = mx2_videobuf_setup,
+ .buf_prepare = mx2_videobuf_prepare,
+ .buf_queue = mx2_videobuf_queue,
+ .buf_cleanup = mx2_videobuf_release,
+ .start_streaming = mx2_start_streaming,
+ .stop_streaming = mx2_stop_streaming,
+};
+
+static int mx2_camera_init_videobuf(struct vb2_queue *q,
+ struct soc_camera_device *icd)
+{
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->drv_priv = icd;
+ q->ops = &mx2_videobuf_ops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct mx2_buffer);
+
+ return vb2_queue_init(q);
+}
+
+#define MX2_BUS_FLAGS (V4L2_MBUS_MASTER | \
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_VSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_HSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_PCLK_SAMPLE_RISING | \
+ V4L2_MBUS_PCLK_SAMPLE_FALLING | \
+ V4L2_MBUS_DATA_ACTIVE_HIGH | \
+ V4L2_MBUS_DATA_ACTIVE_LOW)
+
+static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev)
+{
+ u32 cntl;
+ int count = 0;
+
+ cntl = readl(pcdev->base_emma + PRP_CNTL);
+ writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL);
+ while (count++ < 100) {
+ if (!(readl(pcdev->base_emma + PRP_CNTL) & PRP_CNTL_SWRST))
+ return 0;
+ barrier();
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int mx2_camera_set_bus_param(struct soc_camera_device *icd)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long common_flags;
+ int ret;
+ int bytesperline;
+ u32 csicr1 = pcdev->csicr1;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg, MX2_BUS_FLAGS);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%x\n",
+ cfg.flags, MX2_BUS_FLAGS);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = MX2_BUS_FLAGS;
+ }
+
+ if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
+ if (pcdev->platform_flags & MX2_CAMERA_HSYNC_HIGH)
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
+ else
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
+ }
+
+ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+ (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
+ if (pcdev->platform_flags & MX2_CAMERA_PCLK_SAMPLE_RISING)
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
+ else
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
+ }
+
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n",
+ common_flags, ret);
+ return ret;
+ }
+
+ if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
+ csicr1 |= CSICR1_REDGE;
+ if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
+ csicr1 |= CSICR1_SOF_POL;
+ if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
+ csicr1 |= CSICR1_HSYNC_POL;
+ if (pcdev->platform_flags & MX2_CAMERA_SWAP16)
+ csicr1 |= CSICR1_SWAP16_EN;
+ if (pcdev->platform_flags & MX2_CAMERA_EXT_VSYNC)
+ csicr1 |= CSICR1_EXT_VSYNC;
+ if (pcdev->platform_flags & MX2_CAMERA_CCIR)
+ csicr1 |= CSICR1_CCIR_EN;
+ if (pcdev->platform_flags & MX2_CAMERA_CCIR_INTERLACE)
+ csicr1 |= CSICR1_CCIR_MODE;
+ if (pcdev->platform_flags & MX2_CAMERA_GATED_CLOCK)
+ csicr1 |= CSICR1_GCLK_MODE;
+ if (pcdev->platform_flags & MX2_CAMERA_INV_DATA)
+ csicr1 |= CSICR1_INV_DATA;
+ if (pcdev->platform_flags & MX2_CAMERA_PACK_DIR_MSB)
+ csicr1 |= CSICR1_PACK_DIR;
+
+ pcdev->csicr1 = csicr1;
+
+ bytesperline = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+ if (bytesperline < 0)
+ return bytesperline;
+
+ if (cpu_is_mx27()) {
+ ret = mx27_camera_emma_prp_reset(pcdev);
+ if (ret)
+ return ret;
+ } else if (cpu_is_mx25()) {
+ writel((bytesperline * icd->user_height) >> 2,
+ pcdev->base_csi + CSIRXCNT);
+ writel((bytesperline << 16) | icd->user_height,
+ pcdev->base_csi + CSIIMAG_PARA);
+ }
+
+ writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
+
+ return 0;
+}
+
+static int mx2_camera_set_crop(struct soc_camera_device *icd,
+ struct v4l2_crop *a)
+{
+ struct v4l2_rect *rect = &a->c;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_mbus_framefmt mf;
+ int ret;
+
+ soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096);
+ soc_camera_limit_side(&rect->top, &rect->height, 0, 2, 4096);
+
+ ret = v4l2_subdev_call(sd, video, s_crop, a);
+ if (ret < 0)
+ return ret;
+
+ /* The capture device might have changed its output */
+ ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(icd->parent, "Sensor cropped %dx%d\n",
+ mf.width, mf.height);
+
+ icd->user_width = mf.width;
+ icd->user_height = mf.height;
+
+ return ret;
+}
+
+static int mx2_camera_get_formats(struct soc_camera_device *icd,
+ unsigned int idx,
+ struct soc_camera_format_xlate *xlate)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_mbus_pixelfmt *fmt;
+ struct device *dev = icd->parent;
+ enum v4l2_mbus_pixelcode code;
+ int ret, formats = 0;
+
+ ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
+ if (ret < 0)
+ /* no more formats */
+ return 0;
+
+ fmt = soc_mbus_get_fmtdesc(code);
+ if (!fmt) {
+ dev_err(dev, "Invalid format code #%u: %d\n", idx, code);
+ return 0;
+ }
+
+ if (code == V4L2_MBUS_FMT_YUYV8_2X8) {
+ formats++;
+ if (xlate) {
+ /*
+ * CH2 can output YUV420 which is a standard format in
+ * soc_mediabus.c
+ */
+ xlate->host_fmt =
+ soc_mbus_get_fmtdesc(V4L2_MBUS_FMT_YUYV8_1_5X8);
+ xlate->code = code;
+ dev_dbg(dev, "Providing host format %s for sensor code %d\n",
+ xlate->host_fmt->name, code);
+ xlate++;
+ }
+ }
+
+ /* Generic pass-trough */
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = fmt;
+ xlate->code = code;
+ xlate++;
+ }
+ return formats;
+}
+
+static int mx2_emmaprp_resize(struct mx2_camera_dev *pcdev,
+ struct v4l2_mbus_framefmt *mf_in,
+ struct v4l2_pix_format *pix_out, bool apply)
+{
+ int num, den;
+ unsigned long m;
+ int i, dir;
+
+ for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) {
+ struct emma_prp_resize tmprsz;
+ unsigned char *s = tmprsz.s;
+ int len = 0;
+ int in, out;
+
+ if (dir == RESIZE_DIR_H) {
+ in = mf_in->width;
+ out = pix_out->width;
+ } else {
+ in = mf_in->height;
+ out = pix_out->height;
+ }
+
+ if (in < out)
+ return -EINVAL;
+ else if (in == out)
+ continue;
+
+ /* Calculate ratio */
+ m = gcd(in, out);
+ num = in / m;
+ den = out / m;
+ if (num > RESIZE_NUM_MAX)
+ return -EINVAL;
+
+ if ((num >= 2 * den) && (den == 1) &&
+ (num < 9) && (!(num & 0x01))) {
+ int sum = 0;
+ int j;
+
+ /* Average scaling for >= 2:1 ratios */
+ /* Support can be added for num >=9 and odd values */
+
+ tmprsz.algo = RESIZE_ALGO_AVERAGING;
+ len = num;
+
+ for (i = 0; i < (len / 2); i++)
+ s[i] = 8;
+
+ do {
+ for (i = 0; i < (len / 2); i++) {
+ s[i] = s[i] >> 1;
+ sum = 0;
+ for (j = 0; j < (len / 2); j++)
+ sum += s[j];
+ if (sum == 4)
+ break;
+ }
+ } while (sum != 4);
+
+ for (i = (len / 2); i < len; i++)
+ s[i] = s[len - i - 1];
+
+ s[len - 1] |= SZ_COEF;
+ } else {
+ /* bilinear scaling for < 2:1 ratios */
+ int v; /* overflow counter */
+ int coeff, nxt; /* table output */
+ int in_pos_inc = 2 * den;
+ int out_pos = num;
+ int out_pos_inc = 2 * num;
+ int init_carry = num - den;
+ int carry = init_carry;
+
+ tmprsz.algo = RESIZE_ALGO_BILINEAR;
+ v = den + in_pos_inc;
+ do {
+ coeff = v - out_pos;
+ out_pos += out_pos_inc;
+ carry += out_pos_inc;
+ for (nxt = 0; v < out_pos; nxt++) {
+ v += in_pos_inc;
+ carry -= in_pos_inc;
+ }
+
+ if (len > RESIZE_NUM_MAX)
+ return -EINVAL;
+
+ coeff = ((coeff << BC_COEF) +
+ (in_pos_inc >> 1)) / in_pos_inc;
+
+ if (coeff >= (SZ_COEF - 1))
+ coeff--;
+
+ coeff |= SZ_COEF;
+ s[len] = (unsigned char)coeff;
+ len++;
+
+ for (i = 1; i < nxt; i++) {
+ if (len >= RESIZE_NUM_MAX)
+ return -EINVAL;
+ s[len] = 0;
+ len++;
+ }
+ } while (carry != init_carry);
+ }
+ tmprsz.len = len;
+ if (dir == RESIZE_DIR_H)
+ mf_in->width = pix_out->width;
+ else
+ mf_in->height = pix_out->height;
+
+ if (apply)
+ memcpy(&pcdev->resizing[dir], &tmprsz, sizeof(tmprsz));
+ }
+ return 0;
+}
+
+static int mx2_camera_set_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_mbus_framefmt mf;
+ int ret;
+
+ dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n",
+ __func__, pix->width, pix->height);
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+ if (!xlate) {
+ dev_warn(icd->parent, "Format %x not found\n",
+ pix->pixelformat);
+ return -EINVAL;
+ }
+
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
+
+ ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+
+ /* Store width and height returned by the sensor for resizing */
+ pcdev->s_width = mf.width;
+ pcdev->s_height = mf.height;
+ dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n",
+ __func__, pcdev->s_width, pcdev->s_height);
+
+ pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code,
+ xlate->host_fmt->fourcc);
+
+ memset(pcdev->resizing, 0, sizeof(pcdev->resizing));
+ if ((mf.width != pix->width || mf.height != pix->height) &&
+ pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) {
+ if (mx2_emmaprp_resize(pcdev, &mf, pix, true) < 0)
+ dev_dbg(icd->parent, "%s: can't resize\n", __func__);
+ }
+
+ if (mf.code != xlate->code)
+ return -EINVAL;
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->field = mf.field;
+ pix->colorspace = mf.colorspace;
+ icd->current_fmt = xlate;
+
+ dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n",
+ __func__, pix->width, pix->height);
+
+ return 0;
+}
+
+static int mx2_camera_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_mbus_framefmt mf;
+ __u32 pixfmt = pix->pixelformat;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ unsigned int width_limit;
+ int ret;
+
+ dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n",
+ __func__, pix->width, pix->height);
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (pixfmt && !xlate) {
+ dev_warn(icd->parent, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+
+ /* FIXME: implement MX27 limits */
+
+ /* limit to MX25 hardware capabilities */
+ if (cpu_is_mx25()) {
+ if (xlate->host_fmt->bits_per_sample <= 8)
+ width_limit = 0xffff * 4;
+ else
+ width_limit = 0xffff * 2;
+ /* CSIIMAG_PARA limit */
+ if (pix->width > width_limit)
+ pix->width = width_limit;
+ if (pix->height > 0xffff)
+ pix->height = 0xffff;
+
+ pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
+ xlate->host_fmt);
+ if (pix->bytesperline < 0)
+ return pix->bytesperline;
+ pix->sizeimage = pix->height * pix->bytesperline;
+ /* Check against the CSIRXCNT limit */
+ if (pix->sizeimage > 4 * 0x3ffff) {
+ /* Adjust geometry, preserve aspect ratio */
+ unsigned int new_height = int_sqrt(4 * 0x3ffff *
+ pix->height / pix->bytesperline);
+ pix->width = new_height * pix->width / pix->height;
+ pix->height = new_height;
+ pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
+ xlate->host_fmt);
+ BUG_ON(pix->bytesperline < 0);
+ }
+ }
+
+ /* limit to sensor capabilities */
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
+
+ ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n",
+ __func__, pcdev->s_width, pcdev->s_height);
+
+ /* If the sensor does not support image size try PrP resizing */
+ pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code,
+ xlate->host_fmt->fourcc);
+
+ memset(pcdev->resizing, 0, sizeof(pcdev->resizing));
+ if ((mf.width != pix->width || mf.height != pix->height) &&
+ pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) {
+ if (mx2_emmaprp_resize(pcdev, &mf, pix, false) < 0)
+ dev_dbg(icd->parent, "%s: can't resize\n", __func__);
+ }
+
+ if (mf.field == V4L2_FIELD_ANY)
+ mf.field = V4L2_FIELD_NONE;
+ /*
+ * Driver supports interlaced images provided they have
+ * both fields so that they can be processed as if they
+ * were progressive.
+ */
+ if (mf.field != V4L2_FIELD_NONE && !V4L2_FIELD_HAS_BOTH(mf.field)) {
+ dev_err(icd->parent, "Field type %d unsupported.\n",
+ mf.field);
+ return -EINVAL;
+ }
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->field = mf.field;
+ pix->colorspace = mf.colorspace;
+
+ dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n",
+ __func__, pix->width, pix->height);
+
+ return 0;
+}
+
+static int mx2_camera_querycap(struct soc_camera_host *ici,
+ struct v4l2_capability *cap)
+{
+ /* cap->name is set by the friendly caller:-> */
+ strlcpy(cap->card, MX2_CAM_DRIVER_DESCRIPTION, sizeof(cap->card));
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+static unsigned int mx2_camera_poll(struct file *file, poll_table *pt)
+{
+ struct soc_camera_device *icd = file->private_data;
+
+ return vb2_poll(&icd->vb2_vidq, file, pt);
+}
+
+static struct soc_camera_host_ops mx2_soc_camera_host_ops = {
+ .owner = THIS_MODULE,
+ .add = mx2_camera_add_device,
+ .remove = mx2_camera_remove_device,
+ .set_fmt = mx2_camera_set_fmt,
+ .set_crop = mx2_camera_set_crop,
+ .get_formats = mx2_camera_get_formats,
+ .try_fmt = mx2_camera_try_fmt,
+ .init_videobuf2 = mx2_camera_init_videobuf,
+ .poll = mx2_camera_poll,
+ .querycap = mx2_camera_querycap,
+ .set_bus_param = mx2_camera_set_bus_param,
+};
+
+static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev,
+ int bufnum, bool err)
+{
+#ifdef DEBUG
+ struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+#endif
+ struct mx2_buf_internal *ibuf;
+ struct mx2_buffer *buf;
+ struct vb2_buffer *vb;
+ unsigned long phys;
+
+ ibuf = list_first_entry(&pcdev->active_bufs, struct mx2_buf_internal,
+ queue);
+
+ BUG_ON(ibuf->bufnum != bufnum);
+
+ if (ibuf->discard) {
+ /*
+ * Discard buffer must not be returned to user space.
+ * Just return it to the discard queue.
+ */
+ list_move_tail(pcdev->active_bufs.next, &pcdev->discard);
+ } else {
+ buf = mx2_ibuf_to_buf(ibuf);
+
+ vb = &buf->vb;
+#ifdef DEBUG
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+ if (prp->cfg.channel == 1) {
+ if (readl(pcdev->base_emma + PRP_DEST_RGB1_PTR +
+ 4 * bufnum) != phys) {
+ dev_err(pcdev->dev, "%lx != %x\n", phys,
+ readl(pcdev->base_emma +
+ PRP_DEST_RGB1_PTR + 4 * bufnum));
+ }
+ } else {
+ if (readl(pcdev->base_emma + PRP_DEST_Y_PTR -
+ 0x14 * bufnum) != phys) {
+ dev_err(pcdev->dev, "%lx != %x\n", phys,
+ readl(pcdev->base_emma +
+ PRP_DEST_Y_PTR - 0x14 * bufnum));
+ }
+ }
+#endif
+ dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb,
+ vb2_plane_vaddr(vb, 0),
+ vb2_get_plane_payload(vb, 0));
+
+ list_del_init(&buf->internal.queue);
+ do_gettimeofday(&vb->v4l2_buf.timestamp);
+ vb->v4l2_buf.sequence = pcdev->frame_count;
+ if (err)
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+ else
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ }
+
+ pcdev->frame_count++;
+
+ if (list_empty(&pcdev->capture)) {
+ if (list_empty(&pcdev->discard)) {
+ dev_warn(pcdev->dev, "%s: trying to access empty discard list\n",
+ __func__);
+ return;
+ }
+
+ ibuf = list_first_entry(&pcdev->discard,
+ struct mx2_buf_internal, queue);
+ ibuf->bufnum = bufnum;
+
+ list_move_tail(pcdev->discard.next, &pcdev->active_bufs);
+ mx27_update_emma_buf(pcdev, pcdev->discard_buffer_dma, bufnum);
+ return;
+ }
+
+ buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
+ internal.queue);
+
+ buf->internal.bufnum = bufnum;
+
+ list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
+
+ vb = &buf->vb;
+ buf->state = MX2_STATE_ACTIVE;
+
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+ mx27_update_emma_buf(pcdev, phys, bufnum);
+}
+
+static irqreturn_t mx27_camera_emma_irq(int irq_emma, void *data)
+{
+ struct mx2_camera_dev *pcdev = data;
+ unsigned int status = readl(pcdev->base_emma + PRP_INTRSTATUS);
+ struct mx2_buf_internal *ibuf;
+
+ spin_lock(&pcdev->lock);
+
+ if (list_empty(&pcdev->active_bufs)) {
+ dev_warn(pcdev->dev, "%s: called while active list is empty\n",
+ __func__);
+
+ if (!status) {
+ spin_unlock(&pcdev->lock);
+ return IRQ_NONE;
+ }
+ }
+
+ if (status & (1 << 7)) { /* overflow */
+ u32 cntl = readl(pcdev->base_emma + PRP_CNTL);
+ writel(cntl & ~(PRP_CNTL_CH1EN | PRP_CNTL_CH2EN),
+ pcdev->base_emma + PRP_CNTL);
+ writel(cntl, pcdev->base_emma + PRP_CNTL);
+
+ ibuf = list_first_entry(&pcdev->active_bufs,
+ struct mx2_buf_internal, queue);
+ mx27_camera_frame_done_emma(pcdev,
+ ibuf->bufnum, true);
+
+ status &= ~(1 << 7);
+ } else if (((status & (3 << 5)) == (3 << 5)) ||
+ ((status & (3 << 3)) == (3 << 3))) {
+ /*
+ * Both buffers have triggered, process the one we're expecting
+ * to first
+ */
+ ibuf = list_first_entry(&pcdev->active_bufs,
+ struct mx2_buf_internal, queue);
+ mx27_camera_frame_done_emma(pcdev, ibuf->bufnum, false);
+ status &= ~(1 << (6 - ibuf->bufnum)); /* mark processed */
+ } else if ((status & (1 << 6)) || (status & (1 << 4))) {
+ mx27_camera_frame_done_emma(pcdev, 0, false);
+ } else if ((status & (1 << 5)) || (status & (1 << 3))) {
+ mx27_camera_frame_done_emma(pcdev, 1, false);
+ }
+
+ spin_unlock(&pcdev->lock);
+ writel(status, pcdev->base_emma + PRP_INTRSTATUS);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit mx27_camera_emma_init(struct mx2_camera_dev *pcdev)
+{
+ struct resource *res_emma = pcdev->res_emma;
+ int err = 0;
+
+ if (!request_mem_region(res_emma->start, resource_size(res_emma),
+ MX2_CAM_DRV_NAME)) {
+ err = -EBUSY;
+ goto out;
+ }
+
+ pcdev->base_emma = ioremap(res_emma->start, resource_size(res_emma));
+ if (!pcdev->base_emma) {
+ err = -ENOMEM;
+ goto exit_release;
+ }
+
+ err = request_irq(pcdev->irq_emma, mx27_camera_emma_irq, 0,
+ MX2_CAM_DRV_NAME, pcdev);
+ if (err) {
+ dev_err(pcdev->dev, "Camera EMMA interrupt register failed \n");
+ goto exit_iounmap;
+ }
+
+ pcdev->clk_emma = clk_get(NULL, "emma");
+ if (IS_ERR(pcdev->clk_emma)) {
+ err = PTR_ERR(pcdev->clk_emma);
+ goto exit_free_irq;
+ }
+
+ clk_enable(pcdev->clk_emma);
+
+ err = mx27_camera_emma_prp_reset(pcdev);
+ if (err)
+ goto exit_clk_emma_put;
+
+ return err;
+
+exit_clk_emma_put:
+ clk_disable(pcdev->clk_emma);
+ clk_put(pcdev->clk_emma);
+exit_free_irq:
+ free_irq(pcdev->irq_emma, pcdev);
+exit_iounmap:
+ iounmap(pcdev->base_emma);
+exit_release:
+ release_mem_region(res_emma->start, resource_size(res_emma));
+out:
+ return err;
+}
+
+static int __devinit mx2_camera_probe(struct platform_device *pdev)
+{
+ struct mx2_camera_dev *pcdev;
+ struct resource *res_csi, *res_emma;
+ void __iomem *base_csi;
+ int irq_csi, irq_emma;
+ int err = 0;
+
+ dev_dbg(&pdev->dev, "initialising\n");
+
+ res_csi = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq_csi = platform_get_irq(pdev, 0);
+ if (res_csi == NULL || irq_csi < 0) {
+ dev_err(&pdev->dev, "Missing platform resources data\n");
+ err = -ENODEV;
+ goto exit;
+ }
+
+ pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL);
+ if (!pcdev) {
+ dev_err(&pdev->dev, "Could not allocate pcdev\n");
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ pcdev->clk_csi = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(pcdev->clk_csi)) {
+ dev_err(&pdev->dev, "Could not get csi clock\n");
+ err = PTR_ERR(pcdev->clk_csi);
+ goto exit_kfree;
+ }
+
+ pcdev->res_csi = res_csi;
+ pcdev->pdata = pdev->dev.platform_data;
+ if (pcdev->pdata) {
+ long rate;
+
+ pcdev->platform_flags = pcdev->pdata->flags;
+
+ rate = clk_round_rate(pcdev->clk_csi, pcdev->pdata->clk * 2);
+ if (rate <= 0) {
+ err = -ENODEV;
+ goto exit_dma_free;
+ }
+ err = clk_set_rate(pcdev->clk_csi, rate);
+ if (err < 0)
+ goto exit_dma_free;
+ }
+
+ INIT_LIST_HEAD(&pcdev->capture);
+ INIT_LIST_HEAD(&pcdev->active_bufs);
+ INIT_LIST_HEAD(&pcdev->discard);
+ spin_lock_init(&pcdev->lock);
+
+ /*
+ * Request the regions.
+ */
+ if (!request_mem_region(res_csi->start, resource_size(res_csi),
+ MX2_CAM_DRV_NAME)) {
+ err = -EBUSY;
+ goto exit_dma_free;
+ }
+
+ base_csi = ioremap(res_csi->start, resource_size(res_csi));
+ if (!base_csi) {
+ err = -ENOMEM;
+ goto exit_release;
+ }
+ pcdev->irq_csi = irq_csi;
+ pcdev->base_csi = base_csi;
+ pcdev->base_dma = res_csi->start;
+ pcdev->dev = &pdev->dev;
+
+ if (cpu_is_mx25()) {
+ err = request_irq(pcdev->irq_csi, mx25_camera_irq, 0,
+ MX2_CAM_DRV_NAME, pcdev);
+ if (err) {
+ dev_err(pcdev->dev, "Camera interrupt register failed \n");
+ goto exit_iounmap;
+ }
+ }
+
+ if (cpu_is_mx27()) {
+ /* EMMA support */
+ res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ irq_emma = platform_get_irq(pdev, 1);
+
+ if (!res_emma || !irq_emma) {
+ dev_err(&pdev->dev, "no EMMA resources\n");
+ goto exit_free_irq;
+ }
+
+ pcdev->res_emma = res_emma;
+ pcdev->irq_emma = irq_emma;
+ if (mx27_camera_emma_init(pcdev))
+ goto exit_free_irq;
+ }
+
+ pcdev->soc_host.drv_name = MX2_CAM_DRV_NAME,
+ pcdev->soc_host.ops = &mx2_soc_camera_host_ops,
+ pcdev->soc_host.priv = pcdev;
+ pcdev->soc_host.v4l2_dev.dev = &pdev->dev;
+ pcdev->soc_host.nr = pdev->id;
+
+ pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(pcdev->alloc_ctx)) {
+ err = PTR_ERR(pcdev->alloc_ctx);
+ goto eallocctx;
+ }
+ err = soc_camera_host_register(&pcdev->soc_host);
+ if (err)
+ goto exit_free_emma;
+
+ dev_info(&pdev->dev, "MX2 Camera (CSI) driver probed, clock frequency: %ld\n",
+ clk_get_rate(pcdev->clk_csi));
+
+ return 0;
+
+exit_free_emma:
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
+eallocctx:
+ if (cpu_is_mx27()) {
+ free_irq(pcdev->irq_emma, pcdev);
+ clk_disable(pcdev->clk_emma);
+ clk_put(pcdev->clk_emma);
+ iounmap(pcdev->base_emma);
+ release_mem_region(pcdev->res_emma->start, resource_size(pcdev->res_emma));
+ }
+exit_free_irq:
+ if (cpu_is_mx25())
+ free_irq(pcdev->irq_csi, pcdev);
+exit_iounmap:
+ iounmap(base_csi);
+exit_release:
+ release_mem_region(res_csi->start, resource_size(res_csi));
+exit_dma_free:
+ clk_put(pcdev->clk_csi);
+exit_kfree:
+ kfree(pcdev);
+exit:
+ return err;
+}
+
+static int __devexit mx2_camera_remove(struct platform_device *pdev)
+{
+ struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+ struct mx2_camera_dev *pcdev = container_of(soc_host,
+ struct mx2_camera_dev, soc_host);
+ struct resource *res;
+
+ clk_put(pcdev->clk_csi);
+ if (cpu_is_mx25())
+ free_irq(pcdev->irq_csi, pcdev);
+ if (cpu_is_mx27())
+ free_irq(pcdev->irq_emma, pcdev);
+
+ soc_camera_host_unregister(&pcdev->soc_host);
+
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
+
+ iounmap(pcdev->base_csi);
+
+ if (cpu_is_mx27()) {
+ clk_disable(pcdev->clk_emma);
+ clk_put(pcdev->clk_emma);
+ iounmap(pcdev->base_emma);
+ res = pcdev->res_emma;
+ release_mem_region(res->start, resource_size(res));
+ }
+
+ res = pcdev->res_csi;
+ release_mem_region(res->start, resource_size(res));
+
+ kfree(pcdev);
+
+ dev_info(&pdev->dev, "MX2 Camera driver unloaded\n");
+
+ return 0;
+}
+
+static struct platform_driver mx2_camera_driver = {
+ .driver = {
+ .name = MX2_CAM_DRV_NAME,
+ },
+ .remove = __devexit_p(mx2_camera_remove),
+};
+
+
+static int __init mx2_camera_init(void)
+{
+ return platform_driver_probe(&mx2_camera_driver, &mx2_camera_probe);
+}
+
+static void __exit mx2_camera_exit(void)
+{
+ return platform_driver_unregister(&mx2_camera_driver);
+}
+
+module_init(mx2_camera_init);
+module_exit(mx2_camera_exit);
+
+MODULE_DESCRIPTION("i.MX27/i.MX25 SoC Camera Host driver");
+MODULE_AUTHOR("Sascha Hauer <sha@pengutronix.de>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(MX2_CAM_VERSION);
diff --git a/drivers/media/video/mx2_emmaprp.c b/drivers/media/video/mx2_emmaprp.c
new file mode 100644
index 00000000..ba89a740
--- /dev/null
+++ b/drivers/media/video/mx2_emmaprp.c
@@ -0,0 +1,1008 @@
+/*
+ * Support eMMa-PrP through mem2mem framework.
+ *
+ * eMMa-PrP is a piece of HW that allows fetching buffers
+ * from one memory location and do several operations on
+ * them such as scaling or format conversion giving, as a result
+ * a new processed buffer in another memory location.
+ *
+ * Based on mem2mem_testdev.c by Pawel Osciak.
+ *
+ * Copyright (c) 2011 Vista Silicon S.L.
+ * Javier Martin <javier.martin@vista-silicon.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version
+ */
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+#include <linux/platform_device.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+#include <asm/sizes.h>
+
+#define EMMAPRP_MODULE_NAME "mem2mem-emmaprp"
+
+MODULE_DESCRIPTION("Mem-to-mem device which supports eMMa-PrP present in mx2 SoCs");
+MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.1");
+
+static bool debug;
+module_param(debug, bool, 0644);
+
+#define MIN_W 32
+#define MIN_H 32
+#define MAX_W 2040
+#define MAX_H 2046
+
+#define S_ALIGN 1 /* multiple of 2 */
+#define W_ALIGN_YUV420 3 /* multiple of 8 */
+#define W_ALIGN_OTHERS 2 /* multiple of 4 */
+#define H_ALIGN 1 /* multiple of 2 */
+
+/* Flags that indicate a format can be used for capture/output */
+#define MEM2MEM_CAPTURE (1 << 0)
+#define MEM2MEM_OUTPUT (1 << 1)
+
+#define MEM2MEM_NAME "m2m-emmaprp"
+
+/* In bytes, per queue */
+#define MEM2MEM_VID_MEM_LIMIT SZ_16M
+
+#define dprintk(dev, fmt, arg...) \
+ v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)
+
+/* EMMA PrP */
+#define PRP_CNTL 0x00
+#define PRP_INTR_CNTL 0x04
+#define PRP_INTRSTATUS 0x08
+#define PRP_SOURCE_Y_PTR 0x0c
+#define PRP_SOURCE_CB_PTR 0x10
+#define PRP_SOURCE_CR_PTR 0x14
+#define PRP_DEST_RGB1_PTR 0x18
+#define PRP_DEST_RGB2_PTR 0x1c
+#define PRP_DEST_Y_PTR 0x20
+#define PRP_DEST_CB_PTR 0x24
+#define PRP_DEST_CR_PTR 0x28
+#define PRP_SRC_FRAME_SIZE 0x2c
+#define PRP_DEST_CH1_LINE_STRIDE 0x30
+#define PRP_SRC_PIXEL_FORMAT_CNTL 0x34
+#define PRP_CH1_PIXEL_FORMAT_CNTL 0x38
+#define PRP_CH1_OUT_IMAGE_SIZE 0x3c
+#define PRP_CH2_OUT_IMAGE_SIZE 0x40
+#define PRP_SRC_LINE_STRIDE 0x44
+#define PRP_CSC_COEF_012 0x48
+#define PRP_CSC_COEF_345 0x4c
+#define PRP_CSC_COEF_678 0x50
+#define PRP_CH1_RZ_HORI_COEF1 0x54
+#define PRP_CH1_RZ_HORI_COEF2 0x58
+#define PRP_CH1_RZ_HORI_VALID 0x5c
+#define PRP_CH1_RZ_VERT_COEF1 0x60
+#define PRP_CH1_RZ_VERT_COEF2 0x64
+#define PRP_CH1_RZ_VERT_VALID 0x68
+#define PRP_CH2_RZ_HORI_COEF1 0x6c
+#define PRP_CH2_RZ_HORI_COEF2 0x70
+#define PRP_CH2_RZ_HORI_VALID 0x74
+#define PRP_CH2_RZ_VERT_COEF1 0x78
+#define PRP_CH2_RZ_VERT_COEF2 0x7c
+#define PRP_CH2_RZ_VERT_VALID 0x80
+
+#define PRP_CNTL_CH1EN (1 << 0)
+#define PRP_CNTL_CH2EN (1 << 1)
+#define PRP_CNTL_CSIEN (1 << 2)
+#define PRP_CNTL_DATA_IN_YUV420 (0 << 3)
+#define PRP_CNTL_DATA_IN_YUV422 (1 << 3)
+#define PRP_CNTL_DATA_IN_RGB16 (2 << 3)
+#define PRP_CNTL_DATA_IN_RGB32 (3 << 3)
+#define PRP_CNTL_CH1_OUT_RGB8 (0 << 5)
+#define PRP_CNTL_CH1_OUT_RGB16 (1 << 5)
+#define PRP_CNTL_CH1_OUT_RGB32 (2 << 5)
+#define PRP_CNTL_CH1_OUT_YUV422 (3 << 5)
+#define PRP_CNTL_CH2_OUT_YUV420 (0 << 7)
+#define PRP_CNTL_CH2_OUT_YUV422 (1 << 7)
+#define PRP_CNTL_CH2_OUT_YUV444 (2 << 7)
+#define PRP_CNTL_CH1_LEN (1 << 9)
+#define PRP_CNTL_CH2_LEN (1 << 10)
+#define PRP_CNTL_SKIP_FRAME (1 << 11)
+#define PRP_CNTL_SWRST (1 << 12)
+#define PRP_CNTL_CLKEN (1 << 13)
+#define PRP_CNTL_WEN (1 << 14)
+#define PRP_CNTL_CH1BYP (1 << 15)
+#define PRP_CNTL_IN_TSKIP(x) ((x) << 16)
+#define PRP_CNTL_CH1_TSKIP(x) ((x) << 19)
+#define PRP_CNTL_CH2_TSKIP(x) ((x) << 22)
+#define PRP_CNTL_INPUT_FIFO_LEVEL(x) ((x) << 25)
+#define PRP_CNTL_RZ_FIFO_LEVEL(x) ((x) << 27)
+#define PRP_CNTL_CH2B1EN (1 << 29)
+#define PRP_CNTL_CH2B2EN (1 << 30)
+#define PRP_CNTL_CH2FEN (1 << 31)
+
+#define PRP_SIZE_HEIGHT(x) (x)
+#define PRP_SIZE_WIDTH(x) ((x) << 16)
+
+/* IRQ Enable and status register */
+#define PRP_INTR_RDERR (1 << 0)
+#define PRP_INTR_CH1WERR (1 << 1)
+#define PRP_INTR_CH2WERR (1 << 2)
+#define PRP_INTR_CH1FC (1 << 3)
+#define PRP_INTR_CH2FC (1 << 5)
+#define PRP_INTR_LBOVF (1 << 7)
+#define PRP_INTR_CH2OVF (1 << 8)
+
+#define PRP_INTR_ST_RDERR (1 << 0)
+#define PRP_INTR_ST_CH1WERR (1 << 1)
+#define PRP_INTR_ST_CH2WERR (1 << 2)
+#define PRP_INTR_ST_CH2B2CI (1 << 3)
+#define PRP_INTR_ST_CH2B1CI (1 << 4)
+#define PRP_INTR_ST_CH1B2CI (1 << 5)
+#define PRP_INTR_ST_CH1B1CI (1 << 6)
+#define PRP_INTR_ST_LBOVF (1 << 7)
+#define PRP_INTR_ST_CH2OVF (1 << 8)
+
+struct emmaprp_fmt {
+ char *name;
+ u32 fourcc;
+ /* Types the format can be used for */
+ u32 types;
+};
+
+static struct emmaprp_fmt formats[] = {
+ {
+ .name = "YUV 4:2:0 Planar",
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .types = MEM2MEM_CAPTURE,
+ },
+ {
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .types = MEM2MEM_OUTPUT,
+ },
+};
+
+/* Per-queue, driver-specific private data */
+struct emmaprp_q_data {
+ unsigned int width;
+ unsigned int height;
+ unsigned int sizeimage;
+ struct emmaprp_fmt *fmt;
+};
+
+enum {
+ V4L2_M2M_SRC = 0,
+ V4L2_M2M_DST = 1,
+};
+
+#define NUM_FORMATS ARRAY_SIZE(formats)
+
+static struct emmaprp_fmt *find_format(struct v4l2_format *f)
+{
+ struct emmaprp_fmt *fmt;
+ unsigned int k;
+
+ for (k = 0; k < NUM_FORMATS; k++) {
+ fmt = &formats[k];
+ if (fmt->fourcc == f->fmt.pix.pixelformat)
+ break;
+ }
+
+ if (k == NUM_FORMATS)
+ return NULL;
+
+ return &formats[k];
+}
+
+struct emmaprp_dev {
+ struct v4l2_device v4l2_dev;
+ struct video_device *vfd;
+
+ struct mutex dev_mutex;
+ spinlock_t irqlock;
+
+ int irq_emma;
+ void __iomem *base_emma;
+ struct clk *clk_emma;
+ struct resource *res_emma;
+
+ struct v4l2_m2m_dev *m2m_dev;
+ struct vb2_alloc_ctx *alloc_ctx;
+};
+
+struct emmaprp_ctx {
+ struct emmaprp_dev *dev;
+ /* Abort requested by m2m */
+ int aborting;
+ struct emmaprp_q_data q_data[2];
+ struct v4l2_m2m_ctx *m2m_ctx;
+};
+
+static struct emmaprp_q_data *get_q_data(struct emmaprp_ctx *ctx,
+ enum v4l2_buf_type type)
+{
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ return &(ctx->q_data[V4L2_M2M_SRC]);
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return &(ctx->q_data[V4L2_M2M_DST]);
+ default:
+ BUG();
+ }
+ return NULL;
+}
+
+/*
+ * mem2mem callbacks
+ */
+static void emmaprp_job_abort(void *priv)
+{
+ struct emmaprp_ctx *ctx = priv;
+ struct emmaprp_dev *pcdev = ctx->dev;
+
+ ctx->aborting = 1;
+
+ dprintk(pcdev, "Aborting task\n");
+
+ v4l2_m2m_job_finish(pcdev->m2m_dev, ctx->m2m_ctx);
+}
+
+static void emmaprp_lock(void *priv)
+{
+ struct emmaprp_ctx *ctx = priv;
+ struct emmaprp_dev *pcdev = ctx->dev;
+ mutex_lock(&pcdev->dev_mutex);
+}
+
+static void emmaprp_unlock(void *priv)
+{
+ struct emmaprp_ctx *ctx = priv;
+ struct emmaprp_dev *pcdev = ctx->dev;
+ mutex_unlock(&pcdev->dev_mutex);
+}
+
+static inline void emmaprp_dump_regs(struct emmaprp_dev *pcdev)
+{
+ dprintk(pcdev,
+ "eMMa-PrP Registers:\n"
+ " SOURCE_Y_PTR = 0x%08X\n"
+ " SRC_FRAME_SIZE = 0x%08X\n"
+ " DEST_Y_PTR = 0x%08X\n"
+ " DEST_CR_PTR = 0x%08X\n"
+ " DEST_CB_PTR = 0x%08X\n"
+ " CH2_OUT_IMAGE_SIZE = 0x%08X\n"
+ " CNTL = 0x%08X\n",
+ readl(pcdev->base_emma + PRP_SOURCE_Y_PTR),
+ readl(pcdev->base_emma + PRP_SRC_FRAME_SIZE),
+ readl(pcdev->base_emma + PRP_DEST_Y_PTR),
+ readl(pcdev->base_emma + PRP_DEST_CR_PTR),
+ readl(pcdev->base_emma + PRP_DEST_CB_PTR),
+ readl(pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE),
+ readl(pcdev->base_emma + PRP_CNTL));
+}
+
+static void emmaprp_device_run(void *priv)
+{
+ struct emmaprp_ctx *ctx = priv;
+ struct emmaprp_q_data *s_q_data, *d_q_data;
+ struct vb2_buffer *src_buf, *dst_buf;
+ struct emmaprp_dev *pcdev = ctx->dev;
+ unsigned int s_width, s_height;
+ unsigned int d_width, d_height;
+ unsigned int d_size;
+ dma_addr_t p_in, p_out;
+ u32 tmp;
+
+ src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+
+ s_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ s_width = s_q_data->width;
+ s_height = s_q_data->height;
+
+ d_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ d_width = d_q_data->width;
+ d_height = d_q_data->height;
+ d_size = d_width * d_height;
+
+ p_in = vb2_dma_contig_plane_dma_addr(src_buf, 0);
+ p_out = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+ if (!p_in || !p_out) {
+ v4l2_err(&pcdev->v4l2_dev,
+ "Acquiring kernel pointers to buffers failed\n");
+ return;
+ }
+
+ /* Input frame parameters */
+ writel(p_in, pcdev->base_emma + PRP_SOURCE_Y_PTR);
+ writel(PRP_SIZE_WIDTH(s_width) | PRP_SIZE_HEIGHT(s_height),
+ pcdev->base_emma + PRP_SRC_FRAME_SIZE);
+
+ /* Output frame parameters */
+ writel(p_out, pcdev->base_emma + PRP_DEST_Y_PTR);
+ writel(p_out + d_size, pcdev->base_emma + PRP_DEST_CB_PTR);
+ writel(p_out + d_size + (d_size >> 2),
+ pcdev->base_emma + PRP_DEST_CR_PTR);
+ writel(PRP_SIZE_WIDTH(d_width) | PRP_SIZE_HEIGHT(d_height),
+ pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE);
+
+ /* IRQ configuration */
+ tmp = readl(pcdev->base_emma + PRP_INTR_CNTL);
+ writel(tmp | PRP_INTR_RDERR |
+ PRP_INTR_CH2WERR |
+ PRP_INTR_CH2FC,
+ pcdev->base_emma + PRP_INTR_CNTL);
+
+ emmaprp_dump_regs(pcdev);
+
+ /* Enable transfer */
+ tmp = readl(pcdev->base_emma + PRP_CNTL);
+ writel(tmp | PRP_CNTL_CH2_OUT_YUV420 |
+ PRP_CNTL_DATA_IN_YUV422 |
+ PRP_CNTL_CH2EN,
+ pcdev->base_emma + PRP_CNTL);
+}
+
+static irqreturn_t emmaprp_irq(int irq_emma, void *data)
+{
+ struct emmaprp_dev *pcdev = data;
+ struct emmaprp_ctx *curr_ctx;
+ struct vb2_buffer *src_vb, *dst_vb;
+ unsigned long flags;
+ u32 irqst;
+
+ /* Check irq flags and clear irq */
+ irqst = readl(pcdev->base_emma + PRP_INTRSTATUS);
+ writel(irqst, pcdev->base_emma + PRP_INTRSTATUS);
+ dprintk(pcdev, "irqst = 0x%08x\n", irqst);
+
+ curr_ctx = v4l2_m2m_get_curr_priv(pcdev->m2m_dev);
+ if (curr_ctx == NULL) {
+ pr_err("Instance released before the end of transaction\n");
+ return IRQ_HANDLED;
+ }
+
+ if (!curr_ctx->aborting) {
+ if ((irqst & PRP_INTR_ST_RDERR) ||
+ (irqst & PRP_INTR_ST_CH2WERR)) {
+ pr_err("PrP bus error ocurred, this transfer is probably corrupted\n");
+ writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL);
+ } else if (irqst & PRP_INTR_ST_CH2B1CI) { /* buffer ready */
+ src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);
+ dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx);
+
+ spin_lock_irqsave(&pcdev->irqlock, flags);
+ v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);
+ v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE);
+ spin_unlock_irqrestore(&pcdev->irqlock, flags);
+ }
+ }
+
+ v4l2_m2m_job_finish(pcdev->m2m_dev, curr_ctx->m2m_ctx);
+ return IRQ_HANDLED;
+}
+
+/*
+ * video ioctls
+ */
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1);
+ strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1);
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT
+ | V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+static int enum_fmt(struct v4l2_fmtdesc *f, u32 type)
+{
+ int i, num;
+ struct emmaprp_fmt *fmt;
+
+ num = 0;
+
+ for (i = 0; i < NUM_FORMATS; ++i) {
+ if (formats[i].types & type) {
+ /* index-th format of type type found ? */
+ if (num == f->index)
+ break;
+ /* Correct type but haven't reached our index yet,
+ * just increment per-type index */
+ ++num;
+ }
+ }
+
+ if (i < NUM_FORMATS) {
+ /* Format found */
+ fmt = &formats[i];
+ strlcpy(f->description, fmt->name, sizeof(f->description) - 1);
+ f->pixelformat = fmt->fourcc;
+ return 0;
+ }
+
+ /* Format not found */
+ return -EINVAL;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ return enum_fmt(f, MEM2MEM_CAPTURE);
+}
+
+static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ return enum_fmt(f, MEM2MEM_OUTPUT);
+}
+
+static int vidioc_g_fmt(struct emmaprp_ctx *ctx, struct v4l2_format *f)
+{
+ struct vb2_queue *vq;
+ struct emmaprp_q_data *q_data;
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ q_data = get_q_data(ctx, f->type);
+
+ f->fmt.pix.width = q_data->width;
+ f->fmt.pix.height = q_data->height;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.pixelformat = q_data->fmt->fourcc;
+ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420)
+ f->fmt.pix.bytesperline = q_data->width * 3 / 2;
+ else /* YUYV */
+ f->fmt.pix.bytesperline = q_data->width * 2;
+ f->fmt.pix.sizeimage = q_data->sizeimage;
+
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ return vidioc_g_fmt(priv, f);
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ return vidioc_g_fmt(priv, f);
+}
+
+static int vidioc_try_fmt(struct v4l2_format *f)
+{
+ enum v4l2_field field;
+
+
+ if (!find_format(f))
+ return -EINVAL;
+
+ field = f->fmt.pix.field;
+ if (field == V4L2_FIELD_ANY)
+ field = V4L2_FIELD_NONE;
+ else if (V4L2_FIELD_NONE != field)
+ return -EINVAL;
+
+ /* V4L2 specification suggests the driver corrects the format struct
+ * if any of the dimensions is unsupported */
+ f->fmt.pix.field = field;
+
+ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) {
+ v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W,
+ W_ALIGN_YUV420, &f->fmt.pix.height,
+ MIN_H, MAX_H, H_ALIGN, S_ALIGN);
+ f->fmt.pix.bytesperline = f->fmt.pix.width * 3 / 2;
+ } else {
+ v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W,
+ W_ALIGN_OTHERS, &f->fmt.pix.height,
+ MIN_H, MAX_H, H_ALIGN, S_ALIGN);
+ f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+ }
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct emmaprp_fmt *fmt;
+ struct emmaprp_ctx *ctx = priv;
+
+ fmt = find_format(f);
+ if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) {
+ v4l2_err(&ctx->dev->v4l2_dev,
+ "Fourcc format (0x%08x) invalid.\n",
+ f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ return vidioc_try_fmt(f);
+}
+
+static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct emmaprp_fmt *fmt;
+ struct emmaprp_ctx *ctx = priv;
+
+ fmt = find_format(f);
+ if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) {
+ v4l2_err(&ctx->dev->v4l2_dev,
+ "Fourcc format (0x%08x) invalid.\n",
+ f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ return vidioc_try_fmt(f);
+}
+
+static int vidioc_s_fmt(struct emmaprp_ctx *ctx, struct v4l2_format *f)
+{
+ struct emmaprp_q_data *q_data;
+ struct vb2_queue *vq;
+ int ret;
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ q_data = get_q_data(ctx, f->type);
+ if (!q_data)
+ return -EINVAL;
+
+ if (vb2_is_busy(vq)) {
+ v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__);
+ return -EBUSY;
+ }
+
+ ret = vidioc_try_fmt(f);
+ if (ret)
+ return ret;
+
+ q_data->fmt = find_format(f);
+ q_data->width = f->fmt.pix.width;
+ q_data->height = f->fmt.pix.height;
+ if (q_data->fmt->fourcc == V4L2_PIX_FMT_YUV420)
+ q_data->sizeimage = q_data->width * q_data->height * 3 / 2;
+ else /* YUYV */
+ q_data->sizeimage = q_data->width * q_data->height * 2;
+
+ dprintk(ctx->dev,
+ "Setting format for type %d, wxh: %dx%d, fmt: %d\n",
+ f->type, q_data->width, q_data->height, q_data->fmt->fourcc);
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ int ret;
+
+ ret = vidioc_try_fmt_vid_cap(file, priv, f);
+ if (ret)
+ return ret;
+
+ return vidioc_s_fmt(priv, f);
+}
+
+static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ int ret;
+
+ ret = vidioc_try_fmt_vid_out(file, priv, f);
+ if (ret)
+ return ret;
+
+ return vidioc_s_fmt(priv, f);
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct emmaprp_ctx *ctx = priv;
+
+ return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct emmaprp_ctx *ctx = priv;
+
+ return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct emmaprp_ctx *ctx = priv;
+
+ return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct emmaprp_ctx *ctx = priv;
+
+ return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct emmaprp_ctx *ctx = priv;
+
+ return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
+}
+
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct emmaprp_ctx *ctx = priv;
+
+ return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
+}
+
+static const struct v4l2_ioctl_ops emmaprp_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+
+ .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
+ .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out,
+ .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out,
+ .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out,
+
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+};
+
+
+/*
+ * Queue operations
+ */
+static int emmaprp_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct emmaprp_ctx *ctx = vb2_get_drv_priv(vq);
+ struct emmaprp_q_data *q_data;
+ unsigned int size, count = *nbuffers;
+
+ q_data = get_q_data(ctx, vq->type);
+
+ if (q_data->fmt->fourcc == V4L2_PIX_FMT_YUV420)
+ size = q_data->width * q_data->height * 3 / 2;
+ else
+ size = q_data->width * q_data->height * 2;
+
+ while (size * count > MEM2MEM_VID_MEM_LIMIT)
+ (count)--;
+
+ *nplanes = 1;
+ *nbuffers = count;
+ sizes[0] = size;
+
+ alloc_ctxs[0] = ctx->dev->alloc_ctx;
+
+ dprintk(ctx->dev, "get %d buffer(s) of size %d each.\n", count, size);
+
+ return 0;
+}
+
+static int emmaprp_buf_prepare(struct vb2_buffer *vb)
+{
+ struct emmaprp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct emmaprp_q_data *q_data;
+
+ dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type);
+
+ q_data = get_q_data(ctx, vb->vb2_queue->type);
+
+ if (vb2_plane_size(vb, 0) < q_data->sizeimage) {
+ dprintk(ctx->dev, "%s data will not fit into plane"
+ "(%lu < %lu)\n", __func__,
+ vb2_plane_size(vb, 0),
+ (long)q_data->sizeimage);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, 0, q_data->sizeimage);
+
+ return 0;
+}
+
+static void emmaprp_buf_queue(struct vb2_buffer *vb)
+{
+ struct emmaprp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+}
+
+static struct vb2_ops emmaprp_qops = {
+ .queue_setup = emmaprp_queue_setup,
+ .buf_prepare = emmaprp_buf_prepare,
+ .buf_queue = emmaprp_buf_queue,
+};
+
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct emmaprp_ctx *ctx = priv;
+ int ret;
+
+ memset(src_vq, 0, sizeof(*src_vq));
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ src_vq->io_modes = VB2_MMAP;
+ src_vq->drv_priv = ctx;
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ src_vq->ops = &emmaprp_qops;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ memset(dst_vq, 0, sizeof(*dst_vq));
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ dst_vq->io_modes = VB2_MMAP;
+ dst_vq->drv_priv = ctx;
+ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ dst_vq->ops = &emmaprp_qops;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+
+ return vb2_queue_init(dst_vq);
+}
+
+/*
+ * File operations
+ */
+static int emmaprp_open(struct file *file)
+{
+ struct emmaprp_dev *pcdev = video_drvdata(file);
+ struct emmaprp_ctx *ctx;
+
+ ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ file->private_data = ctx;
+ ctx->dev = pcdev;
+
+ ctx->m2m_ctx = v4l2_m2m_ctx_init(pcdev->m2m_dev, ctx, &queue_init);
+
+ if (IS_ERR(ctx->m2m_ctx)) {
+ int ret = PTR_ERR(ctx->m2m_ctx);
+
+ kfree(ctx);
+ return ret;
+ }
+
+ clk_enable(pcdev->clk_emma);
+ ctx->q_data[V4L2_M2M_SRC].fmt = &formats[1];
+ ctx->q_data[V4L2_M2M_DST].fmt = &formats[0];
+
+ dprintk(pcdev, "Created instance %p, m2m_ctx: %p\n", ctx, ctx->m2m_ctx);
+
+ return 0;
+}
+
+static int emmaprp_release(struct file *file)
+{
+ struct emmaprp_dev *pcdev = video_drvdata(file);
+ struct emmaprp_ctx *ctx = file->private_data;
+
+ dprintk(pcdev, "Releasing instance %p\n", ctx);
+
+ clk_disable(pcdev->clk_emma);
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
+ kfree(ctx);
+
+ return 0;
+}
+
+static unsigned int emmaprp_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct emmaprp_ctx *ctx = file->private_data;
+
+ return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+}
+
+static int emmaprp_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct emmaprp_ctx *ctx = file->private_data;
+
+ return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
+}
+
+static const struct v4l2_file_operations emmaprp_fops = {
+ .owner = THIS_MODULE,
+ .open = emmaprp_open,
+ .release = emmaprp_release,
+ .poll = emmaprp_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = emmaprp_mmap,
+};
+
+static struct video_device emmaprp_videodev = {
+ .name = MEM2MEM_NAME,
+ .fops = &emmaprp_fops,
+ .ioctl_ops = &emmaprp_ioctl_ops,
+ .minor = -1,
+ .release = video_device_release,
+};
+
+static struct v4l2_m2m_ops m2m_ops = {
+ .device_run = emmaprp_device_run,
+ .job_abort = emmaprp_job_abort,
+ .lock = emmaprp_lock,
+ .unlock = emmaprp_unlock,
+};
+
+static int emmaprp_probe(struct platform_device *pdev)
+{
+ struct emmaprp_dev *pcdev;
+ struct video_device *vfd;
+ struct resource *res_emma;
+ int irq_emma;
+ int ret;
+
+ pcdev = kzalloc(sizeof *pcdev, GFP_KERNEL);
+ if (!pcdev)
+ return -ENOMEM;
+
+ spin_lock_init(&pcdev->irqlock);
+
+ pcdev->clk_emma = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(pcdev->clk_emma)) {
+ ret = PTR_ERR(pcdev->clk_emma);
+ goto free_dev;
+ }
+
+ irq_emma = platform_get_irq(pdev, 0);
+ res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (irq_emma < 0 || res_emma == NULL) {
+ dev_err(&pdev->dev, "Missing platform resources data\n");
+ ret = -ENODEV;
+ goto free_clk;
+ }
+
+ ret = v4l2_device_register(&pdev->dev, &pcdev->v4l2_dev);
+ if (ret)
+ goto free_clk;
+
+ mutex_init(&pcdev->dev_mutex);
+
+ vfd = video_device_alloc();
+ if (!vfd) {
+ v4l2_err(&pcdev->v4l2_dev, "Failed to allocate video device\n");
+ ret = -ENOMEM;
+ goto unreg_dev;
+ }
+
+ *vfd = emmaprp_videodev;
+ vfd->lock = &pcdev->dev_mutex;
+
+ video_set_drvdata(vfd, pcdev);
+ snprintf(vfd->name, sizeof(vfd->name), "%s", emmaprp_videodev.name);
+ pcdev->vfd = vfd;
+ v4l2_info(&pcdev->v4l2_dev, EMMAPRP_MODULE_NAME
+ " Device registered as /dev/video%d\n", vfd->num);
+
+ platform_set_drvdata(pdev, pcdev);
+
+ if (devm_request_mem_region(&pdev->dev, res_emma->start,
+ resource_size(res_emma), MEM2MEM_NAME) == NULL)
+ goto rel_vdev;
+
+ pcdev->base_emma = devm_ioremap(&pdev->dev, res_emma->start,
+ resource_size(res_emma));
+ if (!pcdev->base_emma)
+ goto rel_vdev;
+
+ pcdev->irq_emma = irq_emma;
+ pcdev->res_emma = res_emma;
+
+ if (devm_request_irq(&pdev->dev, pcdev->irq_emma, emmaprp_irq,
+ 0, MEM2MEM_NAME, pcdev) < 0)
+ goto rel_vdev;
+
+ pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(pcdev->alloc_ctx)) {
+ v4l2_err(&pcdev->v4l2_dev, "Failed to alloc vb2 context\n");
+ ret = PTR_ERR(pcdev->alloc_ctx);
+ goto rel_vdev;
+ }
+
+ pcdev->m2m_dev = v4l2_m2m_init(&m2m_ops);
+ if (IS_ERR(pcdev->m2m_dev)) {
+ v4l2_err(&pcdev->v4l2_dev, "Failed to init mem2mem device\n");
+ ret = PTR_ERR(pcdev->m2m_dev);
+ goto rel_ctx;
+ }
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+ if (ret) {
+ v4l2_err(&pcdev->v4l2_dev, "Failed to register video device\n");
+ goto rel_m2m;
+ }
+
+ return 0;
+
+
+rel_m2m:
+ v4l2_m2m_release(pcdev->m2m_dev);
+rel_ctx:
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
+rel_vdev:
+ video_device_release(vfd);
+unreg_dev:
+ v4l2_device_unregister(&pcdev->v4l2_dev);
+free_clk:
+ clk_put(pcdev->clk_emma);
+free_dev:
+ kfree(pcdev);
+
+ return ret;
+}
+
+static int emmaprp_remove(struct platform_device *pdev)
+{
+ struct emmaprp_dev *pcdev = platform_get_drvdata(pdev);
+
+ v4l2_info(&pcdev->v4l2_dev, "Removing " EMMAPRP_MODULE_NAME);
+
+ video_unregister_device(pcdev->vfd);
+ v4l2_m2m_release(pcdev->m2m_dev);
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
+ v4l2_device_unregister(&pcdev->v4l2_dev);
+ clk_put(pcdev->clk_emma);
+ kfree(pcdev);
+
+ return 0;
+}
+
+static struct platform_driver emmaprp_pdrv = {
+ .probe = emmaprp_probe,
+ .remove = emmaprp_remove,
+ .driver = {
+ .name = MEM2MEM_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static void __exit emmaprp_exit(void)
+{
+ platform_driver_unregister(&emmaprp_pdrv);
+}
+
+static int __init emmaprp_init(void)
+{
+ return platform_driver_register(&emmaprp_pdrv);
+}
+
+module_init(emmaprp_init);
+module_exit(emmaprp_exit);
diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c
new file mode 100644
index 00000000..93c35ef5
--- /dev/null
+++ b/drivers/media/video/mx3_camera.c
@@ -0,0 +1,1295 @@
+/*
+ * V4L2 Driver for i.MX3x camera host
+ *
+ * Copyright (C) 2008
+ * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/videodev2.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
+
+#include <mach/ipu.h>
+#include <mach/mx3_camera.h>
+#include <mach/dma.h>
+
+#define MX3_CAM_DRV_NAME "mx3-camera"
+
+/* CMOS Sensor Interface Registers */
+#define CSI_REG_START 0x60
+
+#define CSI_SENS_CONF (0x60 - CSI_REG_START)
+#define CSI_SENS_FRM_SIZE (0x64 - CSI_REG_START)
+#define CSI_ACT_FRM_SIZE (0x68 - CSI_REG_START)
+#define CSI_OUT_FRM_CTRL (0x6C - CSI_REG_START)
+#define CSI_TST_CTRL (0x70 - CSI_REG_START)
+#define CSI_CCIR_CODE_1 (0x74 - CSI_REG_START)
+#define CSI_CCIR_CODE_2 (0x78 - CSI_REG_START)
+#define CSI_CCIR_CODE_3 (0x7C - CSI_REG_START)
+#define CSI_FLASH_STROBE_1 (0x80 - CSI_REG_START)
+#define CSI_FLASH_STROBE_2 (0x84 - CSI_REG_START)
+
+#define CSI_SENS_CONF_VSYNC_POL_SHIFT 0
+#define CSI_SENS_CONF_HSYNC_POL_SHIFT 1
+#define CSI_SENS_CONF_DATA_POL_SHIFT 2
+#define CSI_SENS_CONF_PIX_CLK_POL_SHIFT 3
+#define CSI_SENS_CONF_SENS_PRTCL_SHIFT 4
+#define CSI_SENS_CONF_SENS_CLKSRC_SHIFT 7
+#define CSI_SENS_CONF_DATA_FMT_SHIFT 8
+#define CSI_SENS_CONF_DATA_WIDTH_SHIFT 10
+#define CSI_SENS_CONF_EXT_VSYNC_SHIFT 15
+#define CSI_SENS_CONF_DIVRATIO_SHIFT 16
+
+#define CSI_SENS_CONF_DATA_FMT_RGB_YUV444 (0UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
+#define CSI_SENS_CONF_DATA_FMT_YUV422 (2UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
+#define CSI_SENS_CONF_DATA_FMT_BAYER (3UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
+
+#define MAX_VIDEO_MEM 16
+
+enum csi_buffer_state {
+ CSI_BUF_NEEDS_INIT,
+ CSI_BUF_PREPARED,
+};
+
+struct mx3_camera_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct vb2_buffer vb;
+ enum csi_buffer_state state;
+ struct list_head queue;
+
+ /* One descriptot per scatterlist (per frame) */
+ struct dma_async_tx_descriptor *txd;
+
+ /* We have to "build" a scatterlist ourselves - one element per frame */
+ struct scatterlist sg;
+};
+
+/**
+ * struct mx3_camera_dev - i.MX3x camera (CSI) object
+ * @dev: camera device, to which the coherent buffer is attached
+ * @icd: currently attached camera sensor
+ * @clk: pointer to clock
+ * @base: remapped register base address
+ * @pdata: platform data
+ * @platform_flags: platform flags
+ * @mclk: master clock frequency in Hz
+ * @capture: list of capture videobuffers
+ * @lock: protects video buffer lists
+ * @active: active video buffer
+ * @idmac_channel: array of pointers to IPU DMAC DMA channels
+ * @soc_host: embedded soc_host object
+ */
+struct mx3_camera_dev {
+ /*
+ * i.MX3x is only supposed to handle one camera on its Camera Sensor
+ * Interface. If anyone ever builds hardware to enable more than one
+ * camera _simultaneously_, they will have to modify this driver too
+ */
+ struct soc_camera_device *icd;
+ struct clk *clk;
+
+ void __iomem *base;
+
+ struct mx3_camera_pdata *pdata;
+
+ unsigned long platform_flags;
+ unsigned long mclk;
+ u16 width_flags; /* max 15 bits */
+
+ struct list_head capture;
+ spinlock_t lock; /* Protects video buffer lists */
+ struct mx3_camera_buffer *active;
+ size_t buf_total;
+ struct vb2_alloc_ctx *alloc_ctx;
+ enum v4l2_field field;
+ int sequence;
+
+ /* IDMAC / dmaengine interface */
+ struct idmac_channel *idmac_channel[1]; /* We need one channel */
+
+ struct soc_camera_host soc_host;
+};
+
+struct dma_chan_request {
+ struct mx3_camera_dev *mx3_cam;
+ enum ipu_channel id;
+};
+
+static u32 csi_reg_read(struct mx3_camera_dev *mx3, off_t reg)
+{
+ return __raw_readl(mx3->base + reg);
+}
+
+static void csi_reg_write(struct mx3_camera_dev *mx3, u32 value, off_t reg)
+{
+ __raw_writel(value, mx3->base + reg);
+}
+
+static struct mx3_camera_buffer *to_mx3_vb(struct vb2_buffer *vb)
+{
+ return container_of(vb, struct mx3_camera_buffer, vb);
+}
+
+/* Called from the IPU IDMAC ISR */
+static void mx3_cam_dma_done(void *arg)
+{
+ struct idmac_tx_desc *desc = to_tx_desc(arg);
+ struct dma_chan *chan = desc->txd.chan;
+ struct idmac_channel *ichannel = to_idmac_chan(chan);
+ struct mx3_camera_dev *mx3_cam = ichannel->client;
+
+ dev_dbg(chan->device->dev, "callback cookie %d, active DMA 0x%08x\n",
+ desc->txd.cookie, mx3_cam->active ? sg_dma_address(&mx3_cam->active->sg) : 0);
+
+ spin_lock(&mx3_cam->lock);
+ if (mx3_cam->active) {
+ struct vb2_buffer *vb = &mx3_cam->active->vb;
+ struct mx3_camera_buffer *buf = to_mx3_vb(vb);
+
+ list_del_init(&buf->queue);
+ do_gettimeofday(&vb->v4l2_buf.timestamp);
+ vb->v4l2_buf.field = mx3_cam->field;
+ vb->v4l2_buf.sequence = mx3_cam->sequence++;
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ }
+
+ if (list_empty(&mx3_cam->capture)) {
+ mx3_cam->active = NULL;
+ spin_unlock(&mx3_cam->lock);
+
+ /*
+ * stop capture - without further buffers IPU_CHA_BUF0_RDY will
+ * not get updated
+ */
+ return;
+ }
+
+ mx3_cam->active = list_entry(mx3_cam->capture.next,
+ struct mx3_camera_buffer, queue);
+ spin_unlock(&mx3_cam->lock);
+}
+
+/*
+ * Videobuf operations
+ */
+
+/*
+ * Calculate the __buffer__ (not data) size and number of buffers.
+ */
+static int mx3_videobuf_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *count, unsigned int *num_planes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ int bytes_per_line;
+ unsigned int height;
+
+ if (!mx3_cam->idmac_channel[0])
+ return -EINVAL;
+
+ if (fmt) {
+ const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd,
+ fmt->fmt.pix.pixelformat);
+ if (!xlate)
+ return -EINVAL;
+ bytes_per_line = soc_mbus_bytes_per_line(fmt->fmt.pix.width,
+ xlate->host_fmt);
+ height = fmt->fmt.pix.height;
+ } else {
+ /* Called from VIDIOC_REQBUFS or in compatibility mode */
+ bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+ height = icd->user_height;
+ }
+ if (bytes_per_line < 0)
+ return bytes_per_line;
+
+ sizes[0] = bytes_per_line * height;
+
+ alloc_ctxs[0] = mx3_cam->alloc_ctx;
+
+ if (!vq->num_buffers)
+ mx3_cam->sequence = 0;
+
+ if (!*count)
+ *count = 2;
+
+ /* If *num_planes != 0, we have already verified *count. */
+ if (!*num_planes &&
+ sizes[0] * *count + mx3_cam->buf_total > MAX_VIDEO_MEM * 1024 * 1024)
+ *count = (MAX_VIDEO_MEM * 1024 * 1024 - mx3_cam->buf_total) /
+ sizes[0];
+
+ *num_planes = 1;
+
+ return 0;
+}
+
+static enum pixel_fmt fourcc_to_ipu_pix(__u32 fourcc)
+{
+ /* Add more formats as need arises and test possibilities appear... */
+ switch (fourcc) {
+ case V4L2_PIX_FMT_RGB24:
+ return IPU_PIX_FMT_RGB24;
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_RGB565:
+ default:
+ return IPU_PIX_FMT_GENERIC;
+ }
+}
+
+static void mx3_videobuf_queue(struct vb2_buffer *vb)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct mx3_camera_buffer *buf = to_mx3_vb(vb);
+ struct scatterlist *sg = &buf->sg;
+ struct dma_async_tx_descriptor *txd;
+ struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
+ struct idmac_video_param *video = &ichan->params.video;
+ const struct soc_mbus_pixelfmt *host_fmt = icd->current_fmt->host_fmt;
+ int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, host_fmt);
+ unsigned long flags;
+ dma_cookie_t cookie;
+ size_t new_size;
+
+ BUG_ON(bytes_per_line <= 0);
+
+ new_size = bytes_per_line * icd->user_height;
+
+ if (vb2_plane_size(vb, 0) < new_size) {
+ dev_err(icd->parent, "Buffer #%d too small (%lu < %zu)\n",
+ vb->v4l2_buf.index, vb2_plane_size(vb, 0), new_size);
+ goto error;
+ }
+
+ if (buf->state == CSI_BUF_NEEDS_INIT) {
+ sg_dma_address(sg) = vb2_dma_contig_plane_dma_addr(vb, 0);
+ sg_dma_len(sg) = new_size;
+
+ txd = dmaengine_prep_slave_sg(
+ &ichan->dma_chan, sg, 1, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!txd)
+ goto error;
+
+ txd->callback_param = txd;
+ txd->callback = mx3_cam_dma_done;
+
+ buf->state = CSI_BUF_PREPARED;
+ buf->txd = txd;
+ } else {
+ txd = buf->txd;
+ }
+
+ vb2_set_plane_payload(vb, 0, new_size);
+
+ /* This is the configuration of one sg-element */
+ video->out_pixel_fmt = fourcc_to_ipu_pix(host_fmt->fourcc);
+
+ if (video->out_pixel_fmt == IPU_PIX_FMT_GENERIC) {
+ /*
+ * If the IPU DMA channel is configured to transfer generic
+ * 8-bit data, we have to set up the geometry parameters
+ * correctly, according to the current pixel format. The DMA
+ * horizontal parameters in this case are expressed in bytes,
+ * not in pixels.
+ */
+ video->out_width = bytes_per_line;
+ video->out_height = icd->user_height;
+ video->out_stride = bytes_per_line;
+ } else {
+ /*
+ * For IPU known formats the pixel unit will be managed
+ * successfully by the IPU code
+ */
+ video->out_width = icd->user_width;
+ video->out_height = icd->user_height;
+ video->out_stride = icd->user_width;
+ }
+
+#ifdef DEBUG
+ /* helps to see what DMA actually has written */
+ if (vb2_plane_vaddr(vb, 0))
+ memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0));
+#endif
+
+ spin_lock_irqsave(&mx3_cam->lock, flags);
+ list_add_tail(&buf->queue, &mx3_cam->capture);
+
+ if (!mx3_cam->active)
+ mx3_cam->active = buf;
+
+ spin_unlock_irq(&mx3_cam->lock);
+
+ cookie = txd->tx_submit(txd);
+ dev_dbg(icd->parent, "Submitted cookie %d DMA 0x%08x\n",
+ cookie, sg_dma_address(&buf->sg));
+
+ if (cookie >= 0)
+ return;
+
+ spin_lock_irq(&mx3_cam->lock);
+
+ /* Submit error */
+ list_del_init(&buf->queue);
+
+ if (mx3_cam->active == buf)
+ mx3_cam->active = NULL;
+
+ spin_unlock_irqrestore(&mx3_cam->lock, flags);
+error:
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+}
+
+static void mx3_videobuf_release(struct vb2_buffer *vb)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct mx3_camera_buffer *buf = to_mx3_vb(vb);
+ struct dma_async_tx_descriptor *txd = buf->txd;
+ unsigned long flags;
+
+ dev_dbg(icd->parent,
+ "Release%s DMA 0x%08x, queue %sempty\n",
+ mx3_cam->active == buf ? " active" : "", sg_dma_address(&buf->sg),
+ list_empty(&buf->queue) ? "" : "not ");
+
+ spin_lock_irqsave(&mx3_cam->lock, flags);
+
+ if (mx3_cam->active == buf)
+ mx3_cam->active = NULL;
+
+ /* Doesn't hurt also if the list is empty */
+ list_del_init(&buf->queue);
+ buf->state = CSI_BUF_NEEDS_INIT;
+
+ if (txd) {
+ buf->txd = NULL;
+ if (mx3_cam->idmac_channel[0])
+ async_tx_ack(txd);
+ }
+
+ spin_unlock_irqrestore(&mx3_cam->lock, flags);
+
+ mx3_cam->buf_total -= vb2_plane_size(vb, 0);
+}
+
+static int mx3_videobuf_init(struct vb2_buffer *vb)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct mx3_camera_buffer *buf = to_mx3_vb(vb);
+
+ /* This is for locking debugging only */
+ INIT_LIST_HEAD(&buf->queue);
+ sg_init_table(&buf->sg, 1);
+
+ buf->state = CSI_BUF_NEEDS_INIT;
+
+ mx3_cam->buf_total += vb2_plane_size(vb, 0);
+
+ return 0;
+}
+
+static int mx3_stop_streaming(struct vb2_queue *q)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(q);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
+ struct mx3_camera_buffer *buf, *tmp;
+ unsigned long flags;
+
+ if (ichan) {
+ struct dma_chan *chan = &ichan->dma_chan;
+ chan->device->device_control(chan, DMA_PAUSE, 0);
+ }
+
+ spin_lock_irqsave(&mx3_cam->lock, flags);
+
+ mx3_cam->active = NULL;
+
+ list_for_each_entry_safe(buf, tmp, &mx3_cam->capture, queue) {
+ list_del_init(&buf->queue);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+
+ spin_unlock_irqrestore(&mx3_cam->lock, flags);
+
+ return 0;
+}
+
+static struct vb2_ops mx3_videobuf_ops = {
+ .queue_setup = mx3_videobuf_setup,
+ .buf_queue = mx3_videobuf_queue,
+ .buf_cleanup = mx3_videobuf_release,
+ .buf_init = mx3_videobuf_init,
+ .wait_prepare = soc_camera_unlock,
+ .wait_finish = soc_camera_lock,
+ .stop_streaming = mx3_stop_streaming,
+};
+
+static int mx3_camera_init_videobuf(struct vb2_queue *q,
+ struct soc_camera_device *icd)
+{
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->drv_priv = icd;
+ q->ops = &mx3_videobuf_ops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct mx3_camera_buffer);
+
+ return vb2_queue_init(q);
+}
+
+/* First part of ipu_csi_init_interface() */
+static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam,
+ struct soc_camera_device *icd)
+{
+ u32 conf;
+ long rate;
+
+ /* Set default size: ipu_csi_set_window_size() */
+ csi_reg_write(mx3_cam, (640 - 1) | ((480 - 1) << 16), CSI_ACT_FRM_SIZE);
+ /* ...and position to 0:0: ipu_csi_set_window_pos() */
+ conf = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000;
+ csi_reg_write(mx3_cam, conf, CSI_OUT_FRM_CTRL);
+
+ /* We use only gated clock synchronisation mode so far */
+ conf = 0 << CSI_SENS_CONF_SENS_PRTCL_SHIFT;
+
+ /* Set generic data, platform-biggest bus-width */
+ conf |= CSI_SENS_CONF_DATA_FMT_BAYER;
+
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
+ conf |= 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)
+ conf |= 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)
+ conf |= 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ else/* if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)*/
+ conf |= 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+
+ if (mx3_cam->platform_flags & MX3_CAMERA_CLK_SRC)
+ conf |= 1 << CSI_SENS_CONF_SENS_CLKSRC_SHIFT;
+ if (mx3_cam->platform_flags & MX3_CAMERA_EXT_VSYNC)
+ conf |= 1 << CSI_SENS_CONF_EXT_VSYNC_SHIFT;
+ if (mx3_cam->platform_flags & MX3_CAMERA_DP)
+ conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT;
+ if (mx3_cam->platform_flags & MX3_CAMERA_PCP)
+ conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT;
+ if (mx3_cam->platform_flags & MX3_CAMERA_HSP)
+ conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT;
+ if (mx3_cam->platform_flags & MX3_CAMERA_VSP)
+ conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT;
+
+ /* ipu_csi_init_interface() */
+ csi_reg_write(mx3_cam, conf, CSI_SENS_CONF);
+
+ clk_enable(mx3_cam->clk);
+ rate = clk_round_rate(mx3_cam->clk, mx3_cam->mclk);
+ dev_dbg(icd->parent, "Set SENS_CONF to %x, rate %ld\n", conf, rate);
+ if (rate)
+ clk_set_rate(mx3_cam->clk, rate);
+}
+
+/* Called with .video_lock held */
+static int mx3_camera_add_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+
+ if (mx3_cam->icd)
+ return -EBUSY;
+
+ mx3_camera_activate(mx3_cam, icd);
+
+ mx3_cam->buf_total = 0;
+ mx3_cam->icd = icd;
+
+ dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n",
+ icd->devnum);
+
+ return 0;
+}
+
+/* Called with .video_lock held */
+static void mx3_camera_remove_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct idmac_channel **ichan = &mx3_cam->idmac_channel[0];
+
+ BUG_ON(icd != mx3_cam->icd);
+
+ if (*ichan) {
+ dma_release_channel(&(*ichan)->dma_chan);
+ *ichan = NULL;
+ }
+
+ clk_disable(mx3_cam->clk);
+
+ mx3_cam->icd = NULL;
+
+ dev_info(icd->parent, "MX3 Camera driver detached from camera %d\n",
+ icd->devnum);
+}
+
+static int test_platform_param(struct mx3_camera_dev *mx3_cam,
+ unsigned char buswidth, unsigned long *flags)
+{
+ /*
+ * If requested data width is supported by the platform, use it or any
+ * possible lower value - i.MX31 is smart enough to shift bits
+ */
+ if (buswidth > fls(mx3_cam->width_flags))
+ return -EINVAL;
+
+ /*
+ * Platform specified synchronization and pixel clock polarities are
+ * only a recommendation and are only used during probing. MX3x
+ * camera interface only works in master mode, i.e., uses HSYNC and
+ * VSYNC signals from the sensor
+ */
+ *flags = V4L2_MBUS_MASTER |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_HSYNC_ACTIVE_LOW |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_VSYNC_ACTIVE_LOW |
+ V4L2_MBUS_PCLK_SAMPLE_RISING |
+ V4L2_MBUS_PCLK_SAMPLE_FALLING |
+ V4L2_MBUS_DATA_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_LOW;
+
+ return 0;
+}
+
+static int mx3_camera_try_bus_param(struct soc_camera_device *icd,
+ const unsigned int depth)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long bus_flags, common_flags;
+ int ret = test_platform_param(mx3_cam, depth, &bus_flags);
+
+ dev_dbg(icd->parent, "request bus width %d bit: %d\n", depth, ret);
+
+ if (ret < 0)
+ return ret;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg,
+ bus_flags);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%lx\n",
+ cfg.flags, bus_flags);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ }
+
+ return 0;
+}
+
+static bool chan_filter(struct dma_chan *chan, void *arg)
+{
+ struct dma_chan_request *rq = arg;
+ struct mx3_camera_pdata *pdata;
+
+ if (!imx_dma_is_ipu(chan))
+ return false;
+
+ if (!rq)
+ return false;
+
+ pdata = rq->mx3_cam->soc_host.v4l2_dev.dev->platform_data;
+
+ return rq->id == chan->chan_id &&
+ pdata->dma_dev == chan->device->dev;
+}
+
+static const struct soc_mbus_pixelfmt mx3_camera_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .name = "Bayer BGGR (sRGB) 8 bit",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ }, {
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .name = "Monochrome 8 bit",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+};
+
+/* This will be corrected as we get more formats */
+static bool mx3_camera_packing_supported(const struct soc_mbus_pixelfmt *fmt)
+{
+ return fmt->packing == SOC_MBUS_PACKING_NONE ||
+ (fmt->bits_per_sample == 8 &&
+ fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) ||
+ (fmt->bits_per_sample > 8 &&
+ fmt->packing == SOC_MBUS_PACKING_EXTEND16);
+}
+
+static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int idx,
+ struct soc_camera_format_xlate *xlate)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ int formats = 0, ret;
+ enum v4l2_mbus_pixelcode code;
+ const struct soc_mbus_pixelfmt *fmt;
+
+ ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
+ if (ret < 0)
+ /* No more formats */
+ return 0;
+
+ fmt = soc_mbus_get_fmtdesc(code);
+ if (!fmt) {
+ dev_warn(icd->parent,
+ "Unsupported format code #%u: %d\n", idx, code);
+ return 0;
+ }
+
+ /* This also checks support for the requested bits-per-sample */
+ ret = mx3_camera_try_bus_param(icd, fmt->bits_per_sample);
+ if (ret < 0)
+ return 0;
+
+ switch (code) {
+ case V4L2_MBUS_FMT_SBGGR10_1X10:
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = &mx3_camera_formats[0];
+ xlate->code = code;
+ xlate++;
+ dev_dbg(dev, "Providing format %s using code %d\n",
+ mx3_camera_formats[0].name, code);
+ }
+ break;
+ case V4L2_MBUS_FMT_Y10_1X10:
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = &mx3_camera_formats[1];
+ xlate->code = code;
+ xlate++;
+ dev_dbg(dev, "Providing format %s using code %d\n",
+ mx3_camera_formats[1].name, code);
+ }
+ break;
+ default:
+ if (!mx3_camera_packing_supported(fmt))
+ return 0;
+ }
+
+ /* Generic pass-through */
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = fmt;
+ xlate->code = code;
+ dev_dbg(dev, "Providing format %c%c%c%c in pass-through mode\n",
+ (fmt->fourcc >> (0*8)) & 0xFF,
+ (fmt->fourcc >> (1*8)) & 0xFF,
+ (fmt->fourcc >> (2*8)) & 0xFF,
+ (fmt->fourcc >> (3*8)) & 0xFF);
+ xlate++;
+ }
+
+ return formats;
+}
+
+static void configure_geometry(struct mx3_camera_dev *mx3_cam,
+ unsigned int width, unsigned int height,
+ const struct soc_mbus_pixelfmt *fmt)
+{
+ u32 ctrl, width_field, height_field;
+
+ if (fourcc_to_ipu_pix(fmt->fourcc) == IPU_PIX_FMT_GENERIC) {
+ /*
+ * As the CSI will be configured to output BAYER, here
+ * the width parameter count the number of samples to
+ * capture to complete the whole image width.
+ */
+ unsigned int num, den;
+ int ret = soc_mbus_samples_per_pixel(fmt, &num, &den);
+ BUG_ON(ret < 0);
+ width = width * num / den;
+ }
+
+ /* Setup frame size - this cannot be changed on-the-fly... */
+ width_field = width - 1;
+ height_field = height - 1;
+ csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_SENS_FRM_SIZE);
+
+ csi_reg_write(mx3_cam, width_field << 16, CSI_FLASH_STROBE_1);
+ csi_reg_write(mx3_cam, (height_field << 16) | 0x22, CSI_FLASH_STROBE_2);
+
+ csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_ACT_FRM_SIZE);
+
+ /* ...and position */
+ ctrl = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000;
+ /* Sensor does the cropping */
+ csi_reg_write(mx3_cam, ctrl | 0 | (0 << 8), CSI_OUT_FRM_CTRL);
+}
+
+static int acquire_dma_channel(struct mx3_camera_dev *mx3_cam)
+{
+ dma_cap_mask_t mask;
+ struct dma_chan *chan;
+ struct idmac_channel **ichan = &mx3_cam->idmac_channel[0];
+ /* We have to use IDMAC_IC_7 for Bayer / generic data */
+ struct dma_chan_request rq = {.mx3_cam = mx3_cam,
+ .id = IDMAC_IC_7};
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ dma_cap_set(DMA_PRIVATE, mask);
+ chan = dma_request_channel(mask, chan_filter, &rq);
+ if (!chan)
+ return -EBUSY;
+
+ *ichan = to_idmac_chan(chan);
+ (*ichan)->client = mx3_cam;
+
+ return 0;
+}
+
+/*
+ * FIXME: learn to use stride != width, then we can keep stride properly aligned
+ * and support arbitrary (even) widths.
+ */
+static inline void stride_align(__u32 *width)
+{
+ if (ALIGN(*width, 8) < 4096)
+ *width = ALIGN(*width, 8);
+ else
+ *width = *width & ~7;
+}
+
+/*
+ * As long as we don't implement host-side cropping and scaling, we can use
+ * default g_crop and cropcap from soc_camera.c
+ */
+static int mx3_camera_set_crop(struct soc_camera_device *icd,
+ struct v4l2_crop *a)
+{
+ struct v4l2_rect *rect = &a->c;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_mbus_framefmt mf;
+ int ret;
+
+ soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096);
+ soc_camera_limit_side(&rect->top, &rect->height, 0, 2, 4096);
+
+ ret = v4l2_subdev_call(sd, video, s_crop, a);
+ if (ret < 0)
+ return ret;
+
+ /* The capture device might have changed its output sizes */
+ ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ if (mf.code != icd->current_fmt->code)
+ return -EINVAL;
+
+ if (mf.width & 7) {
+ /* Ouch! We can only handle 8-byte aligned width... */
+ stride_align(&mf.width);
+ ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (mf.width != icd->user_width || mf.height != icd->user_height)
+ configure_geometry(mx3_cam, mf.width, mf.height,
+ icd->current_fmt->host_fmt);
+
+ dev_dbg(icd->parent, "Sensor cropped %dx%d\n",
+ mf.width, mf.height);
+
+ icd->user_width = mf.width;
+ icd->user_height = mf.height;
+
+ return ret;
+}
+
+static int mx3_camera_set_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_mbus_framefmt mf;
+ int ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+ if (!xlate) {
+ dev_warn(icd->parent, "Format %x not found\n",
+ pix->pixelformat);
+ return -EINVAL;
+ }
+
+ stride_align(&pix->width);
+ dev_dbg(icd->parent, "Set format %dx%d\n", pix->width, pix->height);
+
+ /*
+ * Might have to perform a complete interface initialisation like in
+ * ipu_csi_init_interface() in mxc_v4l2_s_param(). Also consider
+ * mxc_v4l2_s_fmt()
+ */
+
+ configure_geometry(mx3_cam, pix->width, pix->height, xlate->host_fmt);
+
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
+
+ ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ if (mf.code != xlate->code)
+ return -EINVAL;
+
+ if (!mx3_cam->idmac_channel[0]) {
+ ret = acquire_dma_channel(mx3_cam);
+ if (ret < 0)
+ return ret;
+ }
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->field = mf.field;
+ mx3_cam->field = mf.field;
+ pix->colorspace = mf.colorspace;
+ icd->current_fmt = xlate;
+
+ dev_dbg(icd->parent, "Sensor set %dx%d\n", pix->width, pix->height);
+
+ return ret;
+}
+
+static int mx3_camera_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_mbus_framefmt mf;
+ __u32 pixfmt = pix->pixelformat;
+ int ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (pixfmt && !xlate) {
+ dev_warn(icd->parent, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+
+ /* limit to MX3 hardware capabilities */
+ if (pix->height > 4096)
+ pix->height = 4096;
+ if (pix->width > 4096)
+ pix->width = 4096;
+
+ /* limit to sensor capabilities */
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
+
+ ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->colorspace = mf.colorspace;
+
+ switch (mf.field) {
+ case V4L2_FIELD_ANY:
+ pix->field = V4L2_FIELD_NONE;
+ break;
+ case V4L2_FIELD_NONE:
+ break;
+ default:
+ dev_err(icd->parent, "Field type %d unsupported.\n",
+ mf.field);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int mx3_camera_reqbufs(struct soc_camera_device *icd,
+ struct v4l2_requestbuffers *p)
+{
+ return 0;
+}
+
+static unsigned int mx3_camera_poll(struct file *file, poll_table *pt)
+{
+ struct soc_camera_device *icd = file->private_data;
+
+ return vb2_poll(&icd->vb2_vidq, file, pt);
+}
+
+static int mx3_camera_querycap(struct soc_camera_host *ici,
+ struct v4l2_capability *cap)
+{
+ /* cap->name is set by the firendly caller:-> */
+ strlcpy(cap->card, "i.MX3x Camera", sizeof(cap->card));
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+static int mx3_camera_set_bus_param(struct soc_camera_device *icd)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ u32 pixfmt = icd->current_fmt->host_fmt->fourcc;
+ unsigned long bus_flags, common_flags;
+ u32 dw, sens_conf;
+ const struct soc_mbus_pixelfmt *fmt;
+ int buswidth;
+ int ret;
+ const struct soc_camera_format_xlate *xlate;
+ struct device *dev = icd->parent;
+
+ fmt = soc_mbus_get_fmtdesc(icd->current_fmt->code);
+ if (!fmt)
+ return -EINVAL;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (!xlate) {
+ dev_warn(dev, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+
+ buswidth = fmt->bits_per_sample;
+ ret = test_platform_param(mx3_cam, buswidth, &bus_flags);
+
+ dev_dbg(dev, "requested bus width %d bit: %d\n", buswidth, ret);
+
+ if (ret < 0)
+ return ret;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg,
+ bus_flags);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%lx\n",
+ cfg.flags, bus_flags);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = bus_flags;
+ }
+
+ dev_dbg(dev, "Flags cam: 0x%x host: 0x%lx common: 0x%lx\n",
+ cfg.flags, bus_flags, common_flags);
+
+ /* Make choices, based on platform preferences */
+ if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
+ if (mx3_cam->platform_flags & MX3_CAMERA_HSP)
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
+ else
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
+ }
+
+ if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
+ if (mx3_cam->platform_flags & MX3_CAMERA_VSP)
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
+ else
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
+ }
+
+ if ((common_flags & V4L2_MBUS_DATA_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)) {
+ if (mx3_cam->platform_flags & MX3_CAMERA_DP)
+ common_flags &= ~V4L2_MBUS_DATA_ACTIVE_HIGH;
+ else
+ common_flags &= ~V4L2_MBUS_DATA_ACTIVE_LOW;
+ }
+
+ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+ (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
+ if (mx3_cam->platform_flags & MX3_CAMERA_PCP)
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
+ else
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
+ }
+
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n",
+ common_flags, ret);
+ return ret;
+ }
+
+ /*
+ * So far only gated clock mode is supported. Add a line
+ * (3 << CSI_SENS_CONF_SENS_PRTCL_SHIFT) |
+ * below and select the required mode when supporting other
+ * synchronisation protocols.
+ */
+ sens_conf = csi_reg_read(mx3_cam, CSI_SENS_CONF) &
+ ~((1 << CSI_SENS_CONF_VSYNC_POL_SHIFT) |
+ (1 << CSI_SENS_CONF_HSYNC_POL_SHIFT) |
+ (1 << CSI_SENS_CONF_DATA_POL_SHIFT) |
+ (1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT) |
+ (3 << CSI_SENS_CONF_DATA_FMT_SHIFT) |
+ (3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT));
+
+ /* TODO: Support RGB and YUV formats */
+
+ /* This has been set in mx3_camera_activate(), but we clear it above */
+ sens_conf |= CSI_SENS_CONF_DATA_FMT_BAYER;
+
+ if (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
+ sens_conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT;
+ if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ sens_conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT;
+ if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ sens_conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT;
+ if (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)
+ sens_conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT;
+
+ /* Just do what we're asked to do */
+ switch (xlate->host_fmt->bits_per_sample) {
+ case 4:
+ dw = 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ break;
+ case 8:
+ dw = 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ break;
+ case 10:
+ dw = 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ break;
+ default:
+ /*
+ * Actually it can only be 15 now, default is just to silence
+ * compiler warnings
+ */
+ case 15:
+ dw = 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ }
+
+ csi_reg_write(mx3_cam, sens_conf | dw, CSI_SENS_CONF);
+
+ dev_dbg(dev, "Set SENS_CONF to %x\n", sens_conf | dw);
+
+ return 0;
+}
+
+static struct soc_camera_host_ops mx3_soc_camera_host_ops = {
+ .owner = THIS_MODULE,
+ .add = mx3_camera_add_device,
+ .remove = mx3_camera_remove_device,
+ .set_crop = mx3_camera_set_crop,
+ .set_fmt = mx3_camera_set_fmt,
+ .try_fmt = mx3_camera_try_fmt,
+ .get_formats = mx3_camera_get_formats,
+ .init_videobuf2 = mx3_camera_init_videobuf,
+ .reqbufs = mx3_camera_reqbufs,
+ .poll = mx3_camera_poll,
+ .querycap = mx3_camera_querycap,
+ .set_bus_param = mx3_camera_set_bus_param,
+};
+
+static int __devinit mx3_camera_probe(struct platform_device *pdev)
+{
+ struct mx3_camera_dev *mx3_cam;
+ struct resource *res;
+ void __iomem *base;
+ int err = 0;
+ struct soc_camera_host *soc_host;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ err = -ENODEV;
+ goto egetres;
+ }
+
+ mx3_cam = vzalloc(sizeof(*mx3_cam));
+ if (!mx3_cam) {
+ dev_err(&pdev->dev, "Could not allocate mx3 camera object\n");
+ err = -ENOMEM;
+ goto ealloc;
+ }
+
+ mx3_cam->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(mx3_cam->clk)) {
+ err = PTR_ERR(mx3_cam->clk);
+ goto eclkget;
+ }
+
+ mx3_cam->pdata = pdev->dev.platform_data;
+ mx3_cam->platform_flags = mx3_cam->pdata->flags;
+ if (!(mx3_cam->platform_flags & (MX3_CAMERA_DATAWIDTH_4 |
+ MX3_CAMERA_DATAWIDTH_8 | MX3_CAMERA_DATAWIDTH_10 |
+ MX3_CAMERA_DATAWIDTH_15))) {
+ /*
+ * Platform hasn't set available data widths. This is bad.
+ * Warn and use a default.
+ */
+ dev_warn(&pdev->dev, "WARNING! Platform hasn't set available "
+ "data widths, using default 8 bit\n");
+ mx3_cam->platform_flags |= MX3_CAMERA_DATAWIDTH_8;
+ }
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)
+ mx3_cam->width_flags = 1 << 3;
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)
+ mx3_cam->width_flags |= 1 << 7;
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)
+ mx3_cam->width_flags |= 1 << 9;
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
+ mx3_cam->width_flags |= 1 << 14;
+
+ mx3_cam->mclk = mx3_cam->pdata->mclk_10khz * 10000;
+ if (!mx3_cam->mclk) {
+ dev_warn(&pdev->dev,
+ "mclk_10khz == 0! Please, fix your platform data. "
+ "Using default 20MHz\n");
+ mx3_cam->mclk = 20000000;
+ }
+
+ /* list of video-buffers */
+ INIT_LIST_HEAD(&mx3_cam->capture);
+ spin_lock_init(&mx3_cam->lock);
+
+ base = ioremap(res->start, resource_size(res));
+ if (!base) {
+ pr_err("Couldn't map %x@%x\n", resource_size(res), res->start);
+ err = -ENOMEM;
+ goto eioremap;
+ }
+
+ mx3_cam->base = base;
+
+ soc_host = &mx3_cam->soc_host;
+ soc_host->drv_name = MX3_CAM_DRV_NAME;
+ soc_host->ops = &mx3_soc_camera_host_ops;
+ soc_host->priv = mx3_cam;
+ soc_host->v4l2_dev.dev = &pdev->dev;
+ soc_host->nr = pdev->id;
+
+ mx3_cam->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(mx3_cam->alloc_ctx)) {
+ err = PTR_ERR(mx3_cam->alloc_ctx);
+ goto eallocctx;
+ }
+
+ err = soc_camera_host_register(soc_host);
+ if (err)
+ goto ecamhostreg;
+
+ /* IDMAC interface */
+ dmaengine_get();
+
+ return 0;
+
+ecamhostreg:
+ vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx);
+eallocctx:
+ iounmap(base);
+eioremap:
+ clk_put(mx3_cam->clk);
+eclkget:
+ vfree(mx3_cam);
+ealloc:
+egetres:
+ return err;
+}
+
+static int __devexit mx3_camera_remove(struct platform_device *pdev)
+{
+ struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+ struct mx3_camera_dev *mx3_cam = container_of(soc_host,
+ struct mx3_camera_dev, soc_host);
+
+ clk_put(mx3_cam->clk);
+
+ soc_camera_host_unregister(soc_host);
+
+ iounmap(mx3_cam->base);
+
+ /*
+ * The channel has either not been allocated,
+ * or should have been released
+ */
+ if (WARN_ON(mx3_cam->idmac_channel[0]))
+ dma_release_channel(&mx3_cam->idmac_channel[0]->dma_chan);
+
+ vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx);
+
+ vfree(mx3_cam);
+
+ dmaengine_put();
+
+ return 0;
+}
+
+static struct platform_driver mx3_camera_driver = {
+ .driver = {
+ .name = MX3_CAM_DRV_NAME,
+ },
+ .probe = mx3_camera_probe,
+ .remove = __devexit_p(mx3_camera_remove),
+};
+
+module_platform_driver(mx3_camera_driver);
+
+MODULE_DESCRIPTION("i.MX3x SoC Camera Host driver");
+MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.2.3");
+MODULE_ALIAS("platform:" MX3_CAM_DRV_NAME);
diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c
new file mode 100644
index 00000000..2e413174
--- /dev/null
+++ b/drivers/media/video/mxb.c
@@ -0,0 +1,879 @@
+/*
+ mxb - v4l2 driver for the Multimedia eXtension Board
+
+ Copyright (C) 1998-2006 Michael Hunold <michael@mihu.de>
+
+ Visit http://www.themm.net/~mihu/linux/saa7146/mxb.html
+ for further details about this card.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define DEBUG_VARIABLE debug
+
+#include <media/saa7146_vv.h>
+#include <media/tuner.h>
+#include <media/v4l2-common.h>
+#include <media/saa7115.h>
+#include <linux/module.h>
+
+#include "mxb.h"
+#include "tea6415c.h"
+#include "tea6420.h"
+
+#define I2C_SAA7111A 0x24
+#define I2C_TDA9840 0x42
+#define I2C_TEA6415C 0x43
+#define I2C_TEA6420_1 0x4c
+#define I2C_TEA6420_2 0x4d
+#define I2C_TUNER 0x60
+
+#define MXB_BOARD_CAN_DO_VBI(dev) (dev->revision != 0)
+
+/* global variable */
+static int mxb_num;
+
+/* initial frequence the tuner will be tuned to.
+ in verden (lower saxony, germany) 4148 is a
+ channel called "phoenix" */
+static int freq = 4148;
+module_param(freq, int, 0644);
+MODULE_PARM_DESC(freq, "initial frequency the tuner will be tuned to while setup");
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off).");
+
+#define MXB_INPUTS 4
+enum { TUNER, AUX1, AUX3, AUX3_YC };
+
+static struct v4l2_input mxb_inputs[MXB_INPUTS] = {
+ { TUNER, "Tuner", V4L2_INPUT_TYPE_TUNER, 1, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+ { AUX1, "AUX1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+ { AUX3, "AUX3 Composite", V4L2_INPUT_TYPE_CAMERA, 4, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+ { AUX3_YC, "AUX3 S-Video", V4L2_INPUT_TYPE_CAMERA, 4, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+};
+
+/* this array holds the information, which port of the saa7146 each
+ input actually uses. the mxb uses port 0 for every input */
+static struct {
+ int hps_source;
+ int hps_sync;
+} input_port_selection[MXB_INPUTS] = {
+ { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A },
+ { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A },
+ { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A },
+ { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A },
+};
+
+/* this array holds the information of the audio source (mxb_audios),
+ which has to be switched corresponding to the video source (mxb_channels) */
+static int video_audio_connect[MXB_INPUTS] =
+ { 0, 1, 3, 3 };
+
+struct mxb_routing {
+ u32 input;
+ u32 output;
+};
+
+/* These are the necessary input-output-pins for bringing one audio source
+ (see above) to the CD-output. Note that gain is set to 0 in this table. */
+static struct mxb_routing TEA6420_cd[MXB_AUDIOS + 1][2] = {
+ { { 1, 1 }, { 1, 1 } }, /* Tuner */
+ { { 5, 1 }, { 6, 1 } }, /* AUX 1 */
+ { { 4, 1 }, { 6, 1 } }, /* AUX 2 */
+ { { 3, 1 }, { 6, 1 } }, /* AUX 3 */
+ { { 1, 1 }, { 3, 1 } }, /* Radio */
+ { { 1, 1 }, { 2, 1 } }, /* CD-Rom */
+ { { 6, 1 }, { 6, 1 } } /* Mute */
+};
+
+/* These are the necessary input-output-pins for bringing one audio source
+ (see above) to the line-output. Note that gain is set to 0 in this table. */
+static struct mxb_routing TEA6420_line[MXB_AUDIOS + 1][2] = {
+ { { 2, 3 }, { 1, 2 } },
+ { { 5, 3 }, { 6, 2 } },
+ { { 4, 3 }, { 6, 2 } },
+ { { 3, 3 }, { 6, 2 } },
+ { { 2, 3 }, { 3, 2 } },
+ { { 2, 3 }, { 2, 2 } },
+ { { 6, 3 }, { 6, 2 } } /* Mute */
+};
+
+#define MAXCONTROLS 1
+static struct v4l2_queryctrl mxb_controls[] = {
+ { V4L2_CID_AUDIO_MUTE, V4L2_CTRL_TYPE_BOOLEAN, "Mute", 0, 1, 1, 0, 0 },
+};
+
+struct mxb
+{
+ struct video_device *video_dev;
+ struct video_device *vbi_dev;
+
+ struct i2c_adapter i2c_adapter;
+
+ struct v4l2_subdev *saa7111a;
+ struct v4l2_subdev *tda9840;
+ struct v4l2_subdev *tea6415c;
+ struct v4l2_subdev *tuner;
+ struct v4l2_subdev *tea6420_1;
+ struct v4l2_subdev *tea6420_2;
+
+ int cur_mode; /* current audio mode (mono, stereo, ...) */
+ int cur_input; /* current input */
+ int cur_mute; /* current mute status */
+ struct v4l2_frequency cur_freq; /* current frequency the tuner is tuned to */
+};
+
+#define saa7111a_call(mxb, o, f, args...) \
+ v4l2_subdev_call(mxb->saa7111a, o, f, ##args)
+#define tda9840_call(mxb, o, f, args...) \
+ v4l2_subdev_call(mxb->tda9840, o, f, ##args)
+#define tea6415c_call(mxb, o, f, args...) \
+ v4l2_subdev_call(mxb->tea6415c, o, f, ##args)
+#define tuner_call(mxb, o, f, args...) \
+ v4l2_subdev_call(mxb->tuner, o, f, ##args)
+#define call_all(dev, o, f, args...) \
+ v4l2_device_call_until_err(&dev->v4l2_dev, 0, o, f, ##args)
+
+static inline void tea6420_route_cd(struct mxb *mxb, int idx)
+{
+ v4l2_subdev_call(mxb->tea6420_1, audio, s_routing,
+ TEA6420_cd[idx][0].input, TEA6420_cd[idx][0].output, 0);
+ v4l2_subdev_call(mxb->tea6420_2, audio, s_routing,
+ TEA6420_cd[idx][1].input, TEA6420_cd[idx][1].output, 0);
+}
+
+static inline void tea6420_route_line(struct mxb *mxb, int idx)
+{
+ v4l2_subdev_call(mxb->tea6420_1, audio, s_routing,
+ TEA6420_line[idx][0].input, TEA6420_line[idx][0].output, 0);
+ v4l2_subdev_call(mxb->tea6420_2, audio, s_routing,
+ TEA6420_line[idx][1].input, TEA6420_line[idx][1].output, 0);
+}
+
+static struct saa7146_extension extension;
+
+static int mxb_probe(struct saa7146_dev *dev)
+{
+ struct mxb *mxb = NULL;
+
+ mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL);
+ if (mxb == NULL) {
+ DEB_D("not enough kernel memory\n");
+ return -ENOMEM;
+ }
+
+ snprintf(mxb->i2c_adapter.name, sizeof(mxb->i2c_adapter.name), "mxb%d", mxb_num);
+
+ saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
+ if (i2c_add_adapter(&mxb->i2c_adapter) < 0) {
+ DEB_S("cannot register i2c-device. skipping.\n");
+ kfree(mxb);
+ return -EFAULT;
+ }
+
+ mxb->saa7111a = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
+ "saa7111", I2C_SAA7111A, NULL);
+ mxb->tea6420_1 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
+ "tea6420", I2C_TEA6420_1, NULL);
+ mxb->tea6420_2 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
+ "tea6420", I2C_TEA6420_2, NULL);
+ mxb->tea6415c = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
+ "tea6415c", I2C_TEA6415C, NULL);
+ mxb->tda9840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
+ "tda9840", I2C_TDA9840, NULL);
+ mxb->tuner = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
+ "tuner", I2C_TUNER, NULL);
+
+ /* check if all devices are present */
+ if (!mxb->tea6420_1 || !mxb->tea6420_2 || !mxb->tea6415c ||
+ !mxb->tda9840 || !mxb->saa7111a || !mxb->tuner) {
+ pr_err("did not find all i2c devices. aborting\n");
+ i2c_del_adapter(&mxb->i2c_adapter);
+ kfree(mxb);
+ return -ENODEV;
+ }
+
+ /* all devices are present, probe was successful */
+
+ /* we store the pointer in our private data field */
+ dev->ext_priv = mxb;
+
+ return 0;
+}
+
+/* some init data for the saa7740, the so-called 'sound arena module'.
+ there are no specs available, so we simply use some init values */
+static struct {
+ int length;
+ char data[9];
+} mxb_saa7740_init[] = {
+ { 3, { 0x80, 0x00, 0x00 } },{ 3, { 0x80, 0x89, 0x00 } },
+ { 3, { 0x80, 0xb0, 0x0a } },{ 3, { 0x00, 0x00, 0x00 } },
+ { 3, { 0x49, 0x00, 0x00 } },{ 3, { 0x4a, 0x00, 0x00 } },
+ { 3, { 0x4b, 0x00, 0x00 } },{ 3, { 0x4c, 0x00, 0x00 } },
+ { 3, { 0x4d, 0x00, 0x00 } },{ 3, { 0x4e, 0x00, 0x00 } },
+ { 3, { 0x4f, 0x00, 0x00 } },{ 3, { 0x50, 0x00, 0x00 } },
+ { 3, { 0x51, 0x00, 0x00 } },{ 3, { 0x52, 0x00, 0x00 } },
+ { 3, { 0x53, 0x00, 0x00 } },{ 3, { 0x54, 0x00, 0x00 } },
+ { 3, { 0x55, 0x00, 0x00 } },{ 3, { 0x56, 0x00, 0x00 } },
+ { 3, { 0x57, 0x00, 0x00 } },{ 3, { 0x58, 0x00, 0x00 } },
+ { 3, { 0x59, 0x00, 0x00 } },{ 3, { 0x5a, 0x00, 0x00 } },
+ { 3, { 0x5b, 0x00, 0x00 } },{ 3, { 0x5c, 0x00, 0x00 } },
+ { 3, { 0x5d, 0x00, 0x00 } },{ 3, { 0x5e, 0x00, 0x00 } },
+ { 3, { 0x5f, 0x00, 0x00 } },{ 3, { 0x60, 0x00, 0x00 } },
+ { 3, { 0x61, 0x00, 0x00 } },{ 3, { 0x62, 0x00, 0x00 } },
+ { 3, { 0x63, 0x00, 0x00 } },{ 3, { 0x64, 0x00, 0x00 } },
+ { 3, { 0x65, 0x00, 0x00 } },{ 3, { 0x66, 0x00, 0x00 } },
+ { 3, { 0x67, 0x00, 0x00 } },{ 3, { 0x68, 0x00, 0x00 } },
+ { 3, { 0x69, 0x00, 0x00 } },{ 3, { 0x6a, 0x00, 0x00 } },
+ { 3, { 0x6b, 0x00, 0x00 } },{ 3, { 0x6c, 0x00, 0x00 } },
+ { 3, { 0x6d, 0x00, 0x00 } },{ 3, { 0x6e, 0x00, 0x00 } },
+ { 3, { 0x6f, 0x00, 0x00 } },{ 3, { 0x70, 0x00, 0x00 } },
+ { 3, { 0x71, 0x00, 0x00 } },{ 3, { 0x72, 0x00, 0x00 } },
+ { 3, { 0x73, 0x00, 0x00 } },{ 3, { 0x74, 0x00, 0x00 } },
+ { 3, { 0x75, 0x00, 0x00 } },{ 3, { 0x76, 0x00, 0x00 } },
+ { 3, { 0x77, 0x00, 0x00 } },{ 3, { 0x41, 0x00, 0x42 } },
+ { 3, { 0x42, 0x10, 0x42 } },{ 3, { 0x43, 0x20, 0x42 } },
+ { 3, { 0x44, 0x30, 0x42 } },{ 3, { 0x45, 0x00, 0x01 } },
+ { 3, { 0x46, 0x00, 0x01 } },{ 3, { 0x47, 0x00, 0x01 } },
+ { 3, { 0x48, 0x00, 0x01 } },
+ { 9, { 0x01, 0x03, 0xc5, 0x5c, 0x7a, 0x85, 0x01, 0x00, 0x54 } },
+ { 9, { 0x21, 0x03, 0xc5, 0x5c, 0x7a, 0x85, 0x01, 0x00, 0x54 } },
+ { 9, { 0x09, 0x0b, 0xb4, 0x6b, 0x74, 0x85, 0x95, 0x00, 0x34 } },
+ { 9, { 0x29, 0x0b, 0xb4, 0x6b, 0x74, 0x85, 0x95, 0x00, 0x34 } },
+ { 9, { 0x11, 0x17, 0x43, 0x62, 0x68, 0x89, 0xd1, 0xff, 0xb0 } },
+ { 9, { 0x31, 0x17, 0x43, 0x62, 0x68, 0x89, 0xd1, 0xff, 0xb0 } },
+ { 9, { 0x19, 0x20, 0x62, 0x51, 0x5a, 0x95, 0x19, 0x01, 0x50 } },
+ { 9, { 0x39, 0x20, 0x62, 0x51, 0x5a, 0x95, 0x19, 0x01, 0x50 } },
+ { 9, { 0x05, 0x3e, 0xd2, 0x69, 0x4e, 0x9a, 0x51, 0x00, 0xf0 } },
+ { 9, { 0x25, 0x3e, 0xd2, 0x69, 0x4e, 0x9a, 0x51, 0x00, 0xf0 } },
+ { 9, { 0x0d, 0x3d, 0xa1, 0x40, 0x7d, 0x9f, 0x29, 0xfe, 0x14 } },
+ { 9, { 0x2d, 0x3d, 0xa1, 0x40, 0x7d, 0x9f, 0x29, 0xfe, 0x14 } },
+ { 9, { 0x15, 0x73, 0xa1, 0x50, 0x5d, 0xa6, 0xf5, 0xfe, 0x38 } },
+ { 9, { 0x35, 0x73, 0xa1, 0x50, 0x5d, 0xa6, 0xf5, 0xfe, 0x38 } },
+ { 9, { 0x1d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } },
+ { 9, { 0x3d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } },
+ { 3, { 0x80, 0xb3, 0x0a } },
+ {-1, { 0 } }
+};
+
+/* bring hardware to a sane state. this has to be done, just in case someone
+ wants to capture from this device before it has been properly initialized.
+ the capture engine would badly fail, because no valid signal arrives on the
+ saa7146, thus leading to timeouts and stuff. */
+static int mxb_init_done(struct saa7146_dev* dev)
+{
+ struct mxb* mxb = (struct mxb*)dev->ext_priv;
+ struct i2c_msg msg;
+ struct tuner_setup tun_setup;
+ v4l2_std_id std = V4L2_STD_PAL_BG;
+
+ int i = 0, err = 0;
+
+ /* select video mode in saa7111a */
+ saa7111a_call(mxb, core, s_std, std);
+
+ /* select tuner-output on saa7111a */
+ i = 0;
+ saa7111a_call(mxb, video, s_routing, SAA7115_COMPOSITE0,
+ SAA7111_FMT_CCIR, 0);
+
+ /* select a tuner type */
+ tun_setup.mode_mask = T_ANALOG_TV;
+ tun_setup.addr = ADDR_UNSET;
+ tun_setup.type = TUNER_PHILIPS_PAL;
+ tuner_call(mxb, tuner, s_type_addr, &tun_setup);
+ /* tune in some frequency on tuner */
+ mxb->cur_freq.tuner = 0;
+ mxb->cur_freq.type = V4L2_TUNER_ANALOG_TV;
+ mxb->cur_freq.frequency = freq;
+ tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq);
+
+ /* set a default video standard */
+ tuner_call(mxb, core, s_std, std);
+
+ /* mute audio on tea6420s */
+ tea6420_route_line(mxb, 6);
+ tea6420_route_cd(mxb, 6);
+
+ /* switch to tuner-channel on tea6415c */
+ tea6415c_call(mxb, video, s_routing, 3, 17, 0);
+
+ /* select tuner-output on multicable on tea6415c */
+ tea6415c_call(mxb, video, s_routing, 3, 13, 0);
+
+ /* the rest for mxb */
+ mxb->cur_input = 0;
+ mxb->cur_mute = 1;
+
+ mxb->cur_mode = V4L2_TUNER_MODE_STEREO;
+
+ /* check if the saa7740 (aka 'sound arena module') is present
+ on the mxb. if so, we must initialize it. due to lack of
+ informations about the saa7740, the values were reverse
+ engineered. */
+ msg.addr = 0x1b;
+ msg.flags = 0;
+ msg.len = mxb_saa7740_init[0].length;
+ msg.buf = &mxb_saa7740_init[0].data[0];
+
+ err = i2c_transfer(&mxb->i2c_adapter, &msg, 1);
+ if (err == 1) {
+ /* the sound arena module is a pos, that's probably the reason
+ philips refuses to hand out a datasheet for the saa7740...
+ it seems to screw up the i2c bus, so we disable fast irq
+ based i2c transactions here and rely on the slow and safe
+ polling method ... */
+ extension.flags &= ~SAA7146_USE_I2C_IRQ;
+ for (i = 1; ; i++) {
+ if (-1 == mxb_saa7740_init[i].length)
+ break;
+
+ msg.len = mxb_saa7740_init[i].length;
+ msg.buf = &mxb_saa7740_init[i].data[0];
+ err = i2c_transfer(&mxb->i2c_adapter, &msg, 1);
+ if (err != 1) {
+ DEB_D("failed to initialize 'sound arena module'\n");
+ goto err;
+ }
+ }
+ pr_info("'sound arena module' detected\n");
+ }
+err:
+ /* the rest for saa7146: you should definitely set some basic values
+ for the input-port handling of the saa7146. */
+
+ /* ext->saa has been filled by the core driver */
+
+ /* some stuff is done via variables */
+ saa7146_set_hps_source_and_sync(dev, input_port_selection[mxb->cur_input].hps_source,
+ input_port_selection[mxb->cur_input].hps_sync);
+
+ /* some stuff is done via direct write to the registers */
+
+ /* this is ugly, but because of the fact that this is completely
+ hardware dependend, it should be done directly... */
+ saa7146_write(dev, DD1_STREAM_B, 0x00000000);
+ saa7146_write(dev, DD1_INIT, 0x02000200);
+ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+
+ return 0;
+}
+
+/* interrupt-handler. this gets called when irq_mask is != 0.
+ it must clear the interrupt-bits in irq_mask it has handled */
+/*
+void mxb_irq_bh(struct saa7146_dev* dev, u32* irq_mask)
+{
+ struct mxb* mxb = (struct mxb*)dev->ext_priv;
+}
+*/
+
+static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qc)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ int i;
+
+ for (i = MAXCONTROLS - 1; i >= 0; i--) {
+ if (mxb_controls[i].id == qc->id) {
+ *qc = mxb_controls[i];
+ DEB_D("VIDIOC_QUERYCTRL %d\n", qc->id);
+ return 0;
+ }
+ }
+ return dev->ext_vv_data->core_ops->vidioc_queryctrl(file, fh, qc);
+}
+
+static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+ int i;
+
+ for (i = MAXCONTROLS - 1; i >= 0; i--) {
+ if (mxb_controls[i].id == vc->id)
+ break;
+ }
+
+ if (i < 0)
+ return dev->ext_vv_data->core_ops->vidioc_g_ctrl(file, fh, vc);
+
+ if (vc->id == V4L2_CID_AUDIO_MUTE) {
+ vc->value = mxb->cur_mute;
+ DEB_D("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d\n", vc->value);
+ return 0;
+ }
+
+ DEB_EE("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d\n", vc->value);
+ return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+ int i = 0;
+
+ for (i = MAXCONTROLS - 1; i >= 0; i--) {
+ if (mxb_controls[i].id == vc->id)
+ break;
+ }
+
+ if (i < 0)
+ return dev->ext_vv_data->core_ops->vidioc_s_ctrl(file, fh, vc);
+
+ if (vc->id == V4L2_CID_AUDIO_MUTE) {
+ mxb->cur_mute = vc->value;
+ /* switch the audio-source */
+ tea6420_route_line(mxb, vc->value ? 6 :
+ video_audio_connect[mxb->cur_input]);
+ DEB_EE("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d\n", vc->value);
+ }
+ return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
+{
+ DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index);
+ if (i->index >= MXB_INPUTS)
+ return -EINVAL;
+ memcpy(i, &mxb_inputs[i->index], sizeof(struct v4l2_input));
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+ *i = mxb->cur_input;
+
+ DEB_EE("VIDIOC_G_INPUT %d\n", *i);
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+ int err = 0;
+ int i = 0;
+
+ DEB_EE("VIDIOC_S_INPUT %d\n", input);
+
+ if (input >= MXB_INPUTS)
+ return -EINVAL;
+
+ mxb->cur_input = input;
+
+ saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source,
+ input_port_selection[input].hps_sync);
+
+ /* prepare switching of tea6415c and saa7111a;
+ have a look at the 'background'-file for further informations */
+ switch (input) {
+ case TUNER:
+ i = SAA7115_COMPOSITE0;
+
+ err = tea6415c_call(mxb, video, s_routing, 3, 17, 0);
+
+ /* connect tuner-output always to multicable */
+ if (!err)
+ err = tea6415c_call(mxb, video, s_routing, 3, 13, 0);
+ break;
+ case AUX3_YC:
+ /* nothing to be done here. aux3_yc is
+ directly connected to the saa711a */
+ i = SAA7115_SVIDEO1;
+ break;
+ case AUX3:
+ /* nothing to be done here. aux3 is
+ directly connected to the saa711a */
+ i = SAA7115_COMPOSITE1;
+ break;
+ case AUX1:
+ i = SAA7115_COMPOSITE0;
+ err = tea6415c_call(mxb, video, s_routing, 1, 17, 0);
+ break;
+ }
+
+ if (err)
+ return err;
+
+ /* switch video in saa7111a */
+ if (saa7111a_call(mxb, video, s_routing, i, SAA7111_FMT_CCIR, 0))
+ pr_err("VIDIOC_S_INPUT: could not address saa7111a\n");
+
+ /* switch the audio-source only if necessary */
+ if (0 == mxb->cur_mute)
+ tea6420_route_line(mxb, video_audio_connect[input]);
+
+ return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+ if (t->index) {
+ DEB_D("VIDIOC_G_TUNER: channel %d does not have a tuner attached\n",
+ t->index);
+ return -EINVAL;
+ }
+
+ DEB_EE("VIDIOC_G_TUNER: %d\n", t->index);
+
+ memset(t, 0, sizeof(*t));
+ strlcpy(t->name, "TV Tuner", sizeof(t->name));
+ t->type = V4L2_TUNER_ANALOG_TV;
+ t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
+ t->audmode = mxb->cur_mode;
+ return call_all(dev, tuner, g_tuner, t);
+}
+
+static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+ if (t->index) {
+ DEB_D("VIDIOC_S_TUNER: channel %d does not have a tuner attached\n",
+ t->index);
+ return -EINVAL;
+ }
+
+ mxb->cur_mode = t->audmode;
+ return call_all(dev, tuner, s_tuner, t);
+}
+
+static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+ if (mxb->cur_input) {
+ DEB_D("VIDIOC_G_FREQ: channel %d does not have a tuner!\n",
+ mxb->cur_input);
+ return -EINVAL;
+ }
+
+ *f = mxb->cur_freq;
+
+ DEB_EE("VIDIOC_G_FREQ: freq:0x%08x\n", mxb->cur_freq.frequency);
+ return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency *f)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ if (f->tuner)
+ return -EINVAL;
+
+ if (V4L2_TUNER_ANALOG_TV != f->type)
+ return -EINVAL;
+
+ if (mxb->cur_input) {
+ DEB_D("VIDIOC_S_FREQ: channel %d does not have a tuner!\n",
+ mxb->cur_input);
+ return -EINVAL;
+ }
+
+ mxb->cur_freq = *f;
+ DEB_EE("VIDIOC_S_FREQUENCY: freq:0x%08x\n", mxb->cur_freq.frequency);
+
+ /* tune in desired frequency */
+ tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq);
+
+ /* hack: changing the frequency should invalidate the vbi-counter (=> alevt) */
+ spin_lock(&dev->slock);
+ vv->vbi_fieldcount = 0;
+ spin_unlock(&dev->slock);
+
+ return 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+ if (a->index > MXB_INPUTS) {
+ DEB_D("VIDIOC_G_AUDIO %d out of range\n", a->index);
+ return -EINVAL;
+ }
+
+ DEB_EE("VIDIOC_G_AUDIO %d\n", a->index);
+ memcpy(a, &mxb_audios[video_audio_connect[mxb->cur_input]], sizeof(struct v4l2_audio));
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+ DEB_D("VIDIOC_S_AUDIO %d\n", a->index);
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vidioc_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+
+ return call_all(dev, core, g_register, reg);
+}
+
+static int vidioc_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+
+ return call_all(dev, core, s_register, reg);
+}
+#endif
+
+static long vidioc_default(struct file *file, void *fh, bool valid_prio,
+ int cmd, void *arg)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+ switch (cmd) {
+ case MXB_S_AUDIO_CD:
+ {
+ int i = *(int *)arg;
+
+ if (i < 0 || i >= MXB_AUDIOS) {
+ DEB_D("invalid argument to MXB_S_AUDIO_CD: i:%d\n", i);
+ return -EINVAL;
+ }
+
+ DEB_EE("MXB_S_AUDIO_CD: i:%d\n", i);
+
+ tea6420_route_cd(mxb, i);
+ return 0;
+ }
+ case MXB_S_AUDIO_LINE:
+ {
+ int i = *(int *)arg;
+
+ if (i < 0 || i >= MXB_AUDIOS) {
+ DEB_D("invalid argument to MXB_S_AUDIO_LINE: i:%d\n",
+ i);
+ return -EINVAL;
+ }
+
+ DEB_EE("MXB_S_AUDIO_LINE: i:%d\n", i);
+ tea6420_route_line(mxb, i);
+ return 0;
+ }
+ default:
+/*
+ DEB2(pr_err("does not handle this ioctl\n"));
+*/
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static struct saa7146_ext_vv vv_data;
+
+/* this function only gets called when the probing was successful */
+static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
+{
+ struct mxb *mxb;
+
+ DEB_EE("dev:%p\n", dev);
+
+ saa7146_vv_init(dev, &vv_data);
+ if (mxb_probe(dev)) {
+ saa7146_vv_release(dev);
+ return -1;
+ }
+ mxb = (struct mxb *)dev->ext_priv;
+
+ vv_data.ops.vidioc_queryctrl = vidioc_queryctrl;
+ vv_data.ops.vidioc_g_ctrl = vidioc_g_ctrl;
+ vv_data.ops.vidioc_s_ctrl = vidioc_s_ctrl;
+ vv_data.ops.vidioc_enum_input = vidioc_enum_input;
+ vv_data.ops.vidioc_g_input = vidioc_g_input;
+ vv_data.ops.vidioc_s_input = vidioc_s_input;
+ vv_data.ops.vidioc_g_tuner = vidioc_g_tuner;
+ vv_data.ops.vidioc_s_tuner = vidioc_s_tuner;
+ vv_data.ops.vidioc_g_frequency = vidioc_g_frequency;
+ vv_data.ops.vidioc_s_frequency = vidioc_s_frequency;
+ vv_data.ops.vidioc_g_audio = vidioc_g_audio;
+ vv_data.ops.vidioc_s_audio = vidioc_s_audio;
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ vv_data.ops.vidioc_g_register = vidioc_g_register;
+ vv_data.ops.vidioc_s_register = vidioc_s_register;
+#endif
+ vv_data.ops.vidioc_default = vidioc_default;
+ if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) {
+ ERR("cannot register capture v4l2 device. skipping.\n");
+ saa7146_vv_release(dev);
+ return -1;
+ }
+
+ /* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/
+ if (MXB_BOARD_CAN_DO_VBI(dev)) {
+ if (saa7146_register_device(&mxb->vbi_dev, dev, "mxb", VFL_TYPE_VBI)) {
+ ERR("cannot register vbi v4l2 device. skipping.\n");
+ }
+ }
+
+ pr_info("found Multimedia eXtension Board #%d\n", mxb_num);
+
+ mxb_num++;
+ mxb_init_done(dev);
+ return 0;
+}
+
+static int mxb_detach(struct saa7146_dev *dev)
+{
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+ DEB_EE("dev:%p\n", dev);
+
+ saa7146_unregister_device(&mxb->video_dev,dev);
+ if (MXB_BOARD_CAN_DO_VBI(dev))
+ saa7146_unregister_device(&mxb->vbi_dev, dev);
+ saa7146_vv_release(dev);
+
+ mxb_num--;
+
+ i2c_del_adapter(&mxb->i2c_adapter);
+ kfree(mxb);
+
+ return 0;
+}
+
+static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standard)
+{
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+ if (V4L2_STD_PAL_I == standard->id) {
+ v4l2_std_id std = V4L2_STD_PAL_I;
+
+ DEB_D("VIDIOC_S_STD: setting mxb for PAL_I\n");
+ /* set the 7146 gpio register -- I don't know what this does exactly */
+ saa7146_write(dev, GPIO_CTRL, 0x00404050);
+ /* unset the 7111 gpio register -- I don't know what this does exactly */
+ saa7111a_call(mxb, core, s_gpio, 0);
+ tuner_call(mxb, core, s_std, std);
+ } else {
+ v4l2_std_id std = V4L2_STD_PAL_BG;
+
+ DEB_D("VIDIOC_S_STD: setting mxb for PAL/NTSC/SECAM\n");
+ /* set the 7146 gpio register -- I don't know what this does exactly */
+ saa7146_write(dev, GPIO_CTRL, 0x00404050);
+ /* set the 7111 gpio register -- I don't know what this does exactly */
+ saa7111a_call(mxb, core, s_gpio, 1);
+ tuner_call(mxb, core, s_std, std);
+ }
+ return 0;
+}
+
+static struct saa7146_standard standard[] = {
+ {
+ .name = "PAL-BG", .id = V4L2_STD_PAL_BG,
+ .v_offset = 0x17, .v_field = 288,
+ .h_offset = 0x14, .h_pixels = 680,
+ .v_max_out = 576, .h_max_out = 768,
+ }, {
+ .name = "PAL-I", .id = V4L2_STD_PAL_I,
+ .v_offset = 0x17, .v_field = 288,
+ .h_offset = 0x14, .h_pixels = 680,
+ .v_max_out = 576, .h_max_out = 768,
+ }, {
+ .name = "NTSC", .id = V4L2_STD_NTSC,
+ .v_offset = 0x16, .v_field = 240,
+ .h_offset = 0x06, .h_pixels = 708,
+ .v_max_out = 480, .h_max_out = 640,
+ }, {
+ .name = "SECAM", .id = V4L2_STD_SECAM,
+ .v_offset = 0x14, .v_field = 288,
+ .h_offset = 0x14, .h_pixels = 720,
+ .v_max_out = 576, .h_max_out = 768,
+ }
+};
+
+static struct saa7146_pci_extension_data mxb = {
+ .ext_priv = "Multimedia eXtension Board",
+ .ext = &extension,
+};
+
+static struct pci_device_id pci_tbl[] = {
+ {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
+ .subvendor = 0x0000,
+ .subdevice = 0x0000,
+ .driver_data = (unsigned long)&mxb,
+ }, {
+ .vendor = 0,
+ }
+};
+
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+static struct saa7146_ext_vv vv_data = {
+ .inputs = MXB_INPUTS,
+ .capabilities = V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE,
+ .stds = &standard[0],
+ .num_stds = sizeof(standard)/sizeof(struct saa7146_standard),
+ .std_callback = &std_callback,
+};
+
+static struct saa7146_extension extension = {
+ .name = MXB_IDENTIFIER,
+ .flags = SAA7146_USE_I2C_IRQ,
+
+ .pci_tbl = &pci_tbl[0],
+ .module = THIS_MODULE,
+
+ .attach = mxb_attach,
+ .detach = mxb_detach,
+
+ .irq_mask = 0,
+ .irq_func = NULL,
+};
+
+static int __init mxb_init_module(void)
+{
+ if (saa7146_register_extension(&extension)) {
+ DEB_S("failed to register extension\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void __exit mxb_cleanup_module(void)
+{
+ saa7146_unregister_extension(&extension);
+}
+
+module_init(mxb_init_module);
+module_exit(mxb_cleanup_module);
+
+MODULE_DESCRIPTION("video4linux-2 driver for the Siemens-Nixdorf 'Multimedia eXtension board'");
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxb.h b/drivers/media/video/mxb.h
new file mode 100644
index 00000000..400a57ba
--- /dev/null
+++ b/drivers/media/video/mxb.h
@@ -0,0 +1,42 @@
+#ifndef __MXB__
+#define __MXB__
+
+#define BASE_VIDIOC_MXB 10
+
+#define MXB_S_AUDIO_CD _IOW ('V', BASE_VIDIOC_PRIVATE+BASE_VIDIOC_MXB+0, int)
+#define MXB_S_AUDIO_LINE _IOW ('V', BASE_VIDIOC_PRIVATE+BASE_VIDIOC_MXB+1, int)
+
+#define MXB_IDENTIFIER "Multimedia eXtension Board"
+
+#define MXB_AUDIOS 6
+
+/* these are the available audio sources, which can switched
+ to the line- and cd-output individually */
+static struct v4l2_audio mxb_audios[MXB_AUDIOS] = {
+ {
+ .index = 0,
+ .name = "Tuner",
+ .capability = V4L2_AUDCAP_STEREO,
+ } , {
+ .index = 1,
+ .name = "AUX1",
+ .capability = V4L2_AUDCAP_STEREO,
+ } , {
+ .index = 2,
+ .name = "AUX2",
+ .capability = V4L2_AUDCAP_STEREO,
+ } , {
+ .index = 3,
+ .name = "AUX3",
+ .capability = V4L2_AUDCAP_STEREO,
+ } , {
+ .index = 4,
+ .name = "Radio (X9)",
+ .capability = V4L2_AUDCAP_STEREO,
+ } , {
+ .index = 5,
+ .name = "CD-ROM (X10)",
+ .capability = V4L2_AUDCAP_STEREO,
+ }
+};
+#endif
diff --git a/drivers/media/video/noon010pc30.c b/drivers/media/video/noon010pc30.c
new file mode 100644
index 00000000..440c1296
--- /dev/null
+++ b/drivers/media/video/noon010pc30.c
@@ -0,0 +1,851 @@
+/*
+ * Driver for SiliconFile NOON010PC30 CIF (1/11") Image Sensor with ISP
+ *
+ * Copyright (C) 2010 - 2011 Samsung Electronics Co., Ltd.
+ * Contact: Sylwester Nawrocki, <s.nawrocki@samsung.com>
+ *
+ * Initial register configuration based on a driver authored by
+ * HeungJun Kim <riverful.kim@samsung.com>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <media/noon010pc30.h>
+#include <media/v4l2-chip-ident.h>
+#include <linux/videodev2.h>
+#include <linux/module.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mediabus.h>
+#include <media/v4l2-subdev.h>
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Enable module debug trace. Set to 1 to enable.");
+
+#define MODULE_NAME "NOON010PC30"
+
+/*
+ * Register offsets within a page
+ * b15..b8 - page id, b7..b0 - register address
+ */
+#define POWER_CTRL_REG 0x0001
+#define PAGEMODE_REG 0x03
+#define DEVICE_ID_REG 0x0004
+#define NOON010PC30_ID 0x86
+#define VDO_CTL_REG(n) (0x0010 + (n))
+#define SYNC_CTL_REG 0x0012
+/* Window size and position */
+#define WIN_ROWH_REG 0x0013
+#define WIN_ROWL_REG 0x0014
+#define WIN_COLH_REG 0x0015
+#define WIN_COLL_REG 0x0016
+#define WIN_HEIGHTH_REG 0x0017
+#define WIN_HEIGHTL_REG 0x0018
+#define WIN_WIDTHH_REG 0x0019
+#define WIN_WIDTHL_REG 0x001A
+#define HBLANKH_REG 0x001B
+#define HBLANKL_REG 0x001C
+#define VSYNCH_REG 0x001D
+#define VSYNCL_REG 0x001E
+/* VSYNC control */
+#define VS_CTL_REG(n) (0x00A1 + (n))
+/* page 1 */
+#define ISP_CTL_REG(n) (0x0110 + (n))
+#define YOFS_REG 0x0119
+#define DARK_YOFS_REG 0x011A
+#define SAT_CTL_REG 0x0120
+#define BSAT_REG 0x0121
+#define RSAT_REG 0x0122
+/* Color correction */
+#define CMC_CTL_REG 0x0130
+#define CMC_OFSGH_REG 0x0133
+#define CMC_OFSGL_REG 0x0135
+#define CMC_SIGN_REG 0x0136
+#define CMC_GOFS_REG 0x0137
+#define CMC_COEF_REG(n) (0x0138 + (n))
+#define CMC_OFS_REG(n) (0x0141 + (n))
+/* Gamma correction */
+#define GMA_CTL_REG 0x0160
+#define GMA_COEF_REG(n) (0x0161 + (n))
+/* Lens Shading */
+#define LENS_CTRL_REG 0x01D0
+#define LENS_XCEN_REG 0x01D1
+#define LENS_YCEN_REG 0x01D2
+#define LENS_RC_REG 0x01D3
+#define LENS_GC_REG 0x01D4
+#define LENS_BC_REG 0x01D5
+#define L_AGON_REG 0x01D6
+#define L_AGOFF_REG 0x01D7
+/* Page 3 - Auto Exposure */
+#define AE_CTL_REG(n) (0x0310 + (n))
+#define AE_CTL9_REG 0x032C
+#define AE_CTL10_REG 0x032D
+#define AE_YLVL_REG 0x031C
+#define AE_YTH_REG(n) (0x031D + (n))
+#define AE_WGT_REG 0x0326
+#define EXP_TIMEH_REG 0x0333
+#define EXP_TIMEM_REG 0x0334
+#define EXP_TIMEL_REG 0x0335
+#define EXP_MMINH_REG 0x0336
+#define EXP_MMINL_REG 0x0337
+#define EXP_MMAXH_REG 0x0338
+#define EXP_MMAXM_REG 0x0339
+#define EXP_MMAXL_REG 0x033A
+/* Page 4 - Auto White Balance */
+#define AWB_CTL_REG(n) (0x0410 + (n))
+#define AWB_ENABE 0x80
+#define AWB_WGHT_REG 0x0419
+#define BGAIN_PAR_REG(n) (0x044F + (n))
+/* Manual white balance, when AWB_CTL2[0]=1 */
+#define MWB_RGAIN_REG 0x0466
+#define MWB_BGAIN_REG 0x0467
+
+/* The token to mark an array end */
+#define REG_TERM 0xFFFF
+
+struct noon010_format {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+ u16 ispctl1_reg;
+};
+
+struct noon010_frmsize {
+ u16 width;
+ u16 height;
+ int vid_ctl1;
+};
+
+static const char * const noon010_supply_name[] = {
+ "vdd_core", "vddio", "vdda"
+};
+
+#define NOON010_NUM_SUPPLIES ARRAY_SIZE(noon010_supply_name)
+
+struct noon010_info {
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct v4l2_ctrl_handler hdl;
+ struct regulator_bulk_data supply[NOON010_NUM_SUPPLIES];
+ u32 gpio_nreset;
+ u32 gpio_nstby;
+
+ /* Protects the struct members below */
+ struct mutex lock;
+
+ const struct noon010_format *curr_fmt;
+ const struct noon010_frmsize *curr_win;
+ unsigned int apply_new_cfg:1;
+ unsigned int streaming:1;
+ unsigned int hflip:1;
+ unsigned int vflip:1;
+ unsigned int power:1;
+ u8 i2c_reg_page;
+};
+
+struct i2c_regval {
+ u16 addr;
+ u16 val;
+};
+
+/* Supported resolutions. */
+static const struct noon010_frmsize noon010_sizes[] = {
+ {
+ .width = 352,
+ .height = 288,
+ .vid_ctl1 = 0,
+ }, {
+ .width = 176,
+ .height = 144,
+ .vid_ctl1 = 0x10,
+ }, {
+ .width = 88,
+ .height = 72,
+ .vid_ctl1 = 0x20,
+ },
+};
+
+/* Supported pixel formats. */
+static const struct noon010_format noon010_formats[] = {
+ {
+ .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .ispctl1_reg = 0x03,
+ }, {
+ .code = V4L2_MBUS_FMT_YVYU8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .ispctl1_reg = 0x02,
+ }, {
+ .code = V4L2_MBUS_FMT_VYUY8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .ispctl1_reg = 0,
+ }, {
+ .code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .ispctl1_reg = 0x01,
+ }, {
+ .code = V4L2_MBUS_FMT_RGB565_2X8_BE,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .ispctl1_reg = 0x40,
+ },
+};
+
+static const struct i2c_regval noon010_base_regs[] = {
+ { WIN_COLL_REG, 0x06 }, { HBLANKL_REG, 0x7C },
+ /* Color corection and saturation */
+ { ISP_CTL_REG(0), 0x30 }, { ISP_CTL_REG(2), 0x30 },
+ { YOFS_REG, 0x80 }, { DARK_YOFS_REG, 0x04 },
+ { SAT_CTL_REG, 0x1F }, { BSAT_REG, 0x90 },
+ { CMC_CTL_REG, 0x0F }, { CMC_OFSGH_REG, 0x3C },
+ { CMC_OFSGL_REG, 0x2C }, { CMC_SIGN_REG, 0x3F },
+ { CMC_COEF_REG(0), 0x79 }, { CMC_OFS_REG(0), 0x00 },
+ { CMC_COEF_REG(1), 0x39 }, { CMC_OFS_REG(1), 0x00 },
+ { CMC_COEF_REG(2), 0x00 }, { CMC_OFS_REG(2), 0x00 },
+ { CMC_COEF_REG(3), 0x11 }, { CMC_OFS_REG(3), 0x8B },
+ { CMC_COEF_REG(4), 0x65 }, { CMC_OFS_REG(4), 0x07 },
+ { CMC_COEF_REG(5), 0x14 }, { CMC_OFS_REG(5), 0x04 },
+ { CMC_COEF_REG(6), 0x01 }, { CMC_OFS_REG(6), 0x9C },
+ { CMC_COEF_REG(7), 0x33 }, { CMC_OFS_REG(7), 0x89 },
+ { CMC_COEF_REG(8), 0x74 }, { CMC_OFS_REG(8), 0x25 },
+ /* Automatic white balance */
+ { AWB_CTL_REG(0), 0x78 }, { AWB_CTL_REG(1), 0x2E },
+ { AWB_CTL_REG(2), 0x20 }, { AWB_CTL_REG(3), 0x85 },
+ /* Auto exposure */
+ { AE_CTL_REG(0), 0xDC }, { AE_CTL_REG(1), 0x81 },
+ { AE_CTL_REG(2), 0x30 }, { AE_CTL_REG(3), 0xA5 },
+ { AE_CTL_REG(4), 0x40 }, { AE_CTL_REG(5), 0x51 },
+ { AE_CTL_REG(6), 0x33 }, { AE_CTL_REG(7), 0x7E },
+ { AE_CTL9_REG, 0x00 }, { AE_CTL10_REG, 0x02 },
+ { AE_YLVL_REG, 0x44 }, { AE_YTH_REG(0), 0x34 },
+ { AE_YTH_REG(1), 0x30 }, { AE_WGT_REG, 0xD5 },
+ /* Lens shading compensation */
+ { LENS_CTRL_REG, 0x01 }, { LENS_XCEN_REG, 0x80 },
+ { LENS_YCEN_REG, 0x70 }, { LENS_RC_REG, 0x53 },
+ { LENS_GC_REG, 0x40 }, { LENS_BC_REG, 0x3E },
+ { REG_TERM, 0 },
+};
+
+static inline struct noon010_info *to_noon010(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct noon010_info, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct noon010_info, hdl)->sd;
+}
+
+static inline int set_i2c_page(struct noon010_info *info,
+ struct i2c_client *client, unsigned int reg)
+{
+ u32 page = reg >> 8 & 0xFF;
+ int ret = 0;
+
+ if (info->i2c_reg_page != page && (reg & 0xFF) != 0x03) {
+ ret = i2c_smbus_write_byte_data(client, PAGEMODE_REG, page);
+ if (!ret)
+ info->i2c_reg_page = page;
+ }
+ return ret;
+}
+
+static int cam_i2c_read(struct v4l2_subdev *sd, u32 reg_addr)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct noon010_info *info = to_noon010(sd);
+ int ret = set_i2c_page(info, client, reg_addr);
+
+ if (ret)
+ return ret;
+ return i2c_smbus_read_byte_data(client, reg_addr & 0xFF);
+}
+
+static int cam_i2c_write(struct v4l2_subdev *sd, u32 reg_addr, u32 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct noon010_info *info = to_noon010(sd);
+ int ret = set_i2c_page(info, client, reg_addr);
+
+ if (ret)
+ return ret;
+ return i2c_smbus_write_byte_data(client, reg_addr & 0xFF, val);
+}
+
+static inline int noon010_bulk_write_reg(struct v4l2_subdev *sd,
+ const struct i2c_regval *msg)
+{
+ while (msg->addr != REG_TERM) {
+ int ret = cam_i2c_write(sd, msg->addr, msg->val);
+
+ if (ret)
+ return ret;
+ msg++;
+ }
+ return 0;
+}
+
+/* Device reset and sleep mode control */
+static int noon010_power_ctrl(struct v4l2_subdev *sd, bool reset, bool sleep)
+{
+ struct noon010_info *info = to_noon010(sd);
+ u8 reg = sleep ? 0xF1 : 0xF0;
+ int ret = 0;
+
+ if (reset) {
+ ret = cam_i2c_write(sd, POWER_CTRL_REG, reg | 0x02);
+ udelay(20);
+ }
+ if (!ret) {
+ ret = cam_i2c_write(sd, POWER_CTRL_REG, reg);
+ if (reset && !ret)
+ info->i2c_reg_page = -1;
+ }
+ return ret;
+}
+
+/* Automatic white balance control */
+static int noon010_enable_autowhitebalance(struct v4l2_subdev *sd, int on)
+{
+ int ret;
+
+ ret = cam_i2c_write(sd, AWB_CTL_REG(1), on ? 0x2E : 0x2F);
+ if (!ret)
+ ret = cam_i2c_write(sd, AWB_CTL_REG(0), on ? 0xFB : 0x7B);
+ return ret;
+}
+
+/* Called with struct noon010_info.lock mutex held */
+static int noon010_set_flip(struct v4l2_subdev *sd, int hflip, int vflip)
+{
+ struct noon010_info *info = to_noon010(sd);
+ int reg, ret;
+
+ reg = cam_i2c_read(sd, VDO_CTL_REG(1));
+ if (reg < 0)
+ return reg;
+
+ reg &= 0x7C;
+ if (hflip)
+ reg |= 0x01;
+ if (vflip)
+ reg |= 0x02;
+
+ ret = cam_i2c_write(sd, VDO_CTL_REG(1), reg | 0x80);
+ if (!ret) {
+ info->hflip = hflip;
+ info->vflip = vflip;
+ }
+ return ret;
+}
+
+/* Configure resolution and color format */
+static int noon010_set_params(struct v4l2_subdev *sd)
+{
+ struct noon010_info *info = to_noon010(sd);
+
+ int ret = cam_i2c_write(sd, VDO_CTL_REG(0),
+ info->curr_win->vid_ctl1);
+ if (ret)
+ return ret;
+ return cam_i2c_write(sd, ISP_CTL_REG(0),
+ info->curr_fmt->ispctl1_reg);
+}
+
+/* Find nearest matching image pixel size. */
+static int noon010_try_frame_size(struct v4l2_mbus_framefmt *mf,
+ const struct noon010_frmsize **size)
+{
+ unsigned int min_err = ~0;
+ int i = ARRAY_SIZE(noon010_sizes);
+ const struct noon010_frmsize *fsize = &noon010_sizes[0],
+ *match = NULL;
+
+ while (i--) {
+ int err = abs(fsize->width - mf->width)
+ + abs(fsize->height - mf->height);
+
+ if (err < min_err) {
+ min_err = err;
+ match = fsize;
+ }
+ fsize++;
+ }
+ if (match) {
+ mf->width = match->width;
+ mf->height = match->height;
+ if (size)
+ *size = match;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/* Called with info.lock mutex held */
+static int power_enable(struct noon010_info *info)
+{
+ int ret;
+
+ if (info->power) {
+ v4l2_info(&info->sd, "%s: sensor is already on\n", __func__);
+ return 0;
+ }
+
+ if (gpio_is_valid(info->gpio_nstby))
+ gpio_set_value(info->gpio_nstby, 0);
+
+ if (gpio_is_valid(info->gpio_nreset))
+ gpio_set_value(info->gpio_nreset, 0);
+
+ ret = regulator_bulk_enable(NOON010_NUM_SUPPLIES, info->supply);
+ if (ret)
+ return ret;
+
+ if (gpio_is_valid(info->gpio_nreset)) {
+ msleep(50);
+ gpio_set_value(info->gpio_nreset, 1);
+ }
+ if (gpio_is_valid(info->gpio_nstby)) {
+ udelay(1000);
+ gpio_set_value(info->gpio_nstby, 1);
+ }
+ if (gpio_is_valid(info->gpio_nreset)) {
+ udelay(1000);
+ gpio_set_value(info->gpio_nreset, 0);
+ msleep(100);
+ gpio_set_value(info->gpio_nreset, 1);
+ msleep(20);
+ }
+ info->power = 1;
+
+ v4l2_dbg(1, debug, &info->sd, "%s: sensor is on\n", __func__);
+ return 0;
+}
+
+/* Called with info.lock mutex held */
+static int power_disable(struct noon010_info *info)
+{
+ int ret;
+
+ if (!info->power) {
+ v4l2_info(&info->sd, "%s: sensor is already off\n", __func__);
+ return 0;
+ }
+
+ ret = regulator_bulk_disable(NOON010_NUM_SUPPLIES, info->supply);
+ if (ret)
+ return ret;
+
+ if (gpio_is_valid(info->gpio_nstby))
+ gpio_set_value(info->gpio_nstby, 0);
+
+ if (gpio_is_valid(info->gpio_nreset))
+ gpio_set_value(info->gpio_nreset, 0);
+
+ info->power = 0;
+
+ v4l2_dbg(1, debug, &info->sd, "%s: sensor is off\n", __func__);
+
+ return 0;
+}
+
+static int noon010_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ struct noon010_info *info = to_noon010(sd);
+ int ret = 0;
+
+ v4l2_dbg(1, debug, sd, "%s: ctrl_id: %d, value: %d\n",
+ __func__, ctrl->id, ctrl->val);
+
+ mutex_lock(&info->lock);
+ /*
+ * If the device is not powered up by the host driver do
+ * not apply any controls to H/W at this time. Instead
+ * the controls will be restored right after power-up.
+ */
+ if (!info->power)
+ goto unlock;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ ret = noon010_enable_autowhitebalance(sd, ctrl->val);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ ret = cam_i2c_write(sd, MWB_BGAIN_REG, ctrl->val);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ ret = cam_i2c_write(sd, MWB_RGAIN_REG, ctrl->val);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+unlock:
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static int noon010_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index >= ARRAY_SIZE(noon010_formats))
+ return -EINVAL;
+
+ code->code = noon010_formats[code->index].code;
+ return 0;
+}
+
+static int noon010_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct noon010_info *info = to_noon010(sd);
+ struct v4l2_mbus_framefmt *mf;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ if (fh) {
+ mf = v4l2_subdev_get_try_format(fh, 0);
+ fmt->format = *mf;
+ }
+ return 0;
+ }
+ mf = &fmt->format;
+
+ mutex_lock(&info->lock);
+ mf->width = info->curr_win->width;
+ mf->height = info->curr_win->height;
+ mf->code = info->curr_fmt->code;
+ mf->colorspace = info->curr_fmt->colorspace;
+ mf->field = V4L2_FIELD_NONE;
+
+ mutex_unlock(&info->lock);
+ return 0;
+}
+
+/* Return nearest media bus frame format. */
+static const struct noon010_format *noon010_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ int i = ARRAY_SIZE(noon010_formats);
+
+ while (--i)
+ if (mf->code == noon010_formats[i].code)
+ break;
+ mf->code = noon010_formats[i].code;
+
+ return &noon010_formats[i];
+}
+
+static int noon010_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct noon010_info *info = to_noon010(sd);
+ const struct noon010_frmsize *size = NULL;
+ const struct noon010_format *nf;
+ struct v4l2_mbus_framefmt *mf;
+ int ret = 0;
+
+ nf = noon010_try_fmt(sd, &fmt->format);
+ noon010_try_frame_size(&fmt->format, &size);
+ fmt->format.colorspace = V4L2_COLORSPACE_JPEG;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ if (fh) {
+ mf = v4l2_subdev_get_try_format(fh, 0);
+ *mf = fmt->format;
+ }
+ return 0;
+ }
+ mutex_lock(&info->lock);
+ if (!info->streaming) {
+ info->apply_new_cfg = 1;
+ info->curr_fmt = nf;
+ info->curr_win = size;
+ } else {
+ ret = -EBUSY;
+ }
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+/* Called with struct noon010_info.lock mutex held */
+static int noon010_base_config(struct v4l2_subdev *sd)
+{
+ int ret = noon010_bulk_write_reg(sd, noon010_base_regs);
+ if (!ret)
+ ret = noon010_set_params(sd);
+ if (!ret)
+ ret = noon010_set_flip(sd, 1, 0);
+
+ return ret;
+}
+
+static int noon010_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct noon010_info *info = to_noon010(sd);
+ int ret;
+
+ mutex_lock(&info->lock);
+ if (on) {
+ ret = power_enable(info);
+ if (!ret)
+ ret = noon010_base_config(sd);
+ } else {
+ noon010_power_ctrl(sd, false, true);
+ ret = power_disable(info);
+ }
+ mutex_unlock(&info->lock);
+
+ /* Restore the controls state */
+ if (!ret && on)
+ ret = v4l2_ctrl_handler_setup(&info->hdl);
+
+ return ret;
+}
+
+static int noon010_s_stream(struct v4l2_subdev *sd, int on)
+{
+ struct noon010_info *info = to_noon010(sd);
+ int ret = 0;
+
+ mutex_lock(&info->lock);
+ if (!info->streaming != !on) {
+ ret = noon010_power_ctrl(sd, false, !on);
+ if (!ret)
+ info->streaming = on;
+ }
+ if (!ret && on && info->apply_new_cfg) {
+ ret = noon010_set_params(sd);
+ if (!ret)
+ info->apply_new_cfg = 0;
+ }
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static int noon010_log_status(struct v4l2_subdev *sd)
+{
+ struct noon010_info *info = to_noon010(sd);
+
+ v4l2_ctrl_handler_log_status(&info->hdl, sd->name);
+ return 0;
+}
+
+static int noon010_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(fh, 0);
+
+ mf->width = noon010_sizes[0].width;
+ mf->height = noon010_sizes[0].height;
+ mf->code = noon010_formats[0].code;
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ mf->field = V4L2_FIELD_NONE;
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops noon010_subdev_internal_ops = {
+ .open = noon010_open,
+};
+
+static const struct v4l2_ctrl_ops noon010_ctrl_ops = {
+ .s_ctrl = noon010_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops noon010_core_ops = {
+ .s_power = noon010_s_power,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .log_status = noon010_log_status,
+};
+
+static struct v4l2_subdev_pad_ops noon010_pad_ops = {
+ .enum_mbus_code = noon010_enum_mbus_code,
+ .get_fmt = noon010_get_fmt,
+ .set_fmt = noon010_set_fmt,
+};
+
+static struct v4l2_subdev_video_ops noon010_video_ops = {
+ .s_stream = noon010_s_stream,
+};
+
+static const struct v4l2_subdev_ops noon010_ops = {
+ .core = &noon010_core_ops,
+ .pad = &noon010_pad_ops,
+ .video = &noon010_video_ops,
+};
+
+/* Return 0 if NOON010PC30L sensor type was detected or -ENODEV otherwise. */
+static int noon010_detect(struct i2c_client *client, struct noon010_info *info)
+{
+ int ret;
+
+ ret = power_enable(info);
+ if (ret)
+ return ret;
+
+ ret = i2c_smbus_read_byte_data(client, DEVICE_ID_REG);
+ if (ret < 0)
+ dev_err(&client->dev, "I2C read failed: 0x%X\n", ret);
+
+ power_disable(info);
+
+ return ret == NOON010PC30_ID ? 0 : -ENODEV;
+}
+
+static int noon010_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct noon010_info *info;
+ struct v4l2_subdev *sd;
+ const struct noon010pc30_platform_data *pdata
+ = client->dev.platform_data;
+ int ret;
+ int i;
+
+ if (!pdata) {
+ dev_err(&client->dev, "No platform data!\n");
+ return -EIO;
+ }
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ mutex_init(&info->lock);
+ sd = &info->sd;
+ v4l2_i2c_subdev_init(sd, client, &noon010_ops);
+ strlcpy(sd->name, MODULE_NAME, sizeof(sd->name));
+
+ sd->internal_ops = &noon010_subdev_internal_ops;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ v4l2_ctrl_handler_init(&info->hdl, 3);
+
+ v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops,
+ V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+ v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops,
+ V4L2_CID_RED_BALANCE, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops,
+ V4L2_CID_BLUE_BALANCE, 0, 127, 1, 64);
+
+ sd->ctrl_handler = &info->hdl;
+
+ ret = info->hdl.error;
+ if (ret)
+ goto np_err;
+
+ info->i2c_reg_page = -1;
+ info->gpio_nreset = -EINVAL;
+ info->gpio_nstby = -EINVAL;
+ info->curr_fmt = &noon010_formats[0];
+ info->curr_win = &noon010_sizes[0];
+
+ if (gpio_is_valid(pdata->gpio_nreset)) {
+ ret = gpio_request(pdata->gpio_nreset, "NOON010PC30 NRST");
+ if (ret) {
+ dev_err(&client->dev, "GPIO request error: %d\n", ret);
+ goto np_err;
+ }
+ info->gpio_nreset = pdata->gpio_nreset;
+ gpio_direction_output(info->gpio_nreset, 0);
+ gpio_export(info->gpio_nreset, 0);
+ }
+
+ if (gpio_is_valid(pdata->gpio_nstby)) {
+ ret = gpio_request(pdata->gpio_nstby, "NOON010PC30 NSTBY");
+ if (ret) {
+ dev_err(&client->dev, "GPIO request error: %d\n", ret);
+ goto np_gpio_err;
+ }
+ info->gpio_nstby = pdata->gpio_nstby;
+ gpio_direction_output(info->gpio_nstby, 0);
+ gpio_export(info->gpio_nstby, 0);
+ }
+
+ for (i = 0; i < NOON010_NUM_SUPPLIES; i++)
+ info->supply[i].supply = noon010_supply_name[i];
+
+ ret = regulator_bulk_get(&client->dev, NOON010_NUM_SUPPLIES,
+ info->supply);
+ if (ret)
+ goto np_reg_err;
+
+ info->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
+ ret = media_entity_init(&sd->entity, 1, &info->pad, 0);
+ if (ret < 0)
+ goto np_me_err;
+
+ ret = noon010_detect(client, info);
+ if (!ret)
+ return 0;
+
+np_me_err:
+ regulator_bulk_free(NOON010_NUM_SUPPLIES, info->supply);
+np_reg_err:
+ if (gpio_is_valid(info->gpio_nstby))
+ gpio_free(info->gpio_nstby);
+np_gpio_err:
+ if (gpio_is_valid(info->gpio_nreset))
+ gpio_free(info->gpio_nreset);
+np_err:
+ v4l2_ctrl_handler_free(&info->hdl);
+ v4l2_device_unregister_subdev(sd);
+ kfree(info);
+ return ret;
+}
+
+static int noon010_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct noon010_info *info = to_noon010(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&info->hdl);
+
+ regulator_bulk_free(NOON010_NUM_SUPPLIES, info->supply);
+
+ if (gpio_is_valid(info->gpio_nreset))
+ gpio_free(info->gpio_nreset);
+
+ if (gpio_is_valid(info->gpio_nstby))
+ gpio_free(info->gpio_nstby);
+
+ media_entity_cleanup(&sd->entity);
+ kfree(info);
+ return 0;
+}
+
+static const struct i2c_device_id noon010_id[] = {
+ { MODULE_NAME, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, noon010_id);
+
+
+static struct i2c_driver noon010_i2c_driver = {
+ .driver = {
+ .name = MODULE_NAME
+ },
+ .probe = noon010_probe,
+ .remove = noon010_remove,
+ .id_table = noon010_id,
+};
+
+module_i2c_driver(noon010_i2c_driver);
+
+MODULE_DESCRIPTION("Siliconfile NOON010PC30 camera driver");
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/omap/Kconfig b/drivers/media/video/omap/Kconfig
new file mode 100644
index 00000000..390ab094
--- /dev/null
+++ b/drivers/media/video/omap/Kconfig
@@ -0,0 +1,14 @@
+config VIDEO_OMAP2_VOUT_VRFB
+ bool
+
+config VIDEO_OMAP2_VOUT
+ tristate "OMAP2/OMAP3 V4L2-Display driver"
+ depends on ARCH_OMAP2 || ARCH_OMAP3
+ select VIDEOBUF_GEN
+ select VIDEOBUF_DMA_CONTIG
+ select OMAP2_DSS
+ select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3
+ select VIDEO_OMAP2_VOUT_VRFB if VIDEO_OMAP2_VOUT && OMAP2_VRFB
+ default n
+ ---help---
+ V4L2 Display driver support for OMAP2/3 based boards.
diff --git a/drivers/media/video/omap/Makefile b/drivers/media/video/omap/Makefile
new file mode 100644
index 00000000..fc410b43
--- /dev/null
+++ b/drivers/media/video/omap/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the omap video device drivers.
+#
+
+# OMAP2/3 Display driver
+omap-vout-y := omap_vout.o omap_voutlib.o
+omap-vout-$(CONFIG_VIDEO_OMAP2_VOUT_VRFB) += omap_vout_vrfb.o
+obj-$(CONFIG_VIDEO_OMAP2_VOUT) += omap-vout.o
diff --git a/drivers/media/video/omap/omap_vout.c b/drivers/media/video/omap/omap_vout.c
new file mode 100644
index 00000000..88cf9d95
--- /dev/null
+++ b/drivers/media/video/omap/omap_vout.c
@@ -0,0 +1,2289 @@
+/*
+ * omap_vout.c
+ *
+ * Copyright (C) 2005-2010 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ * Leveraged code from the OMAP2 camera driver
+ * Video-for-Linux (Version 2) camera capture driver for
+ * the OMAP24xx camera controller.
+ *
+ * Author: Andy Lowe (source@mvista.com)
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2010 Texas Instruments.
+ *
+ * History:
+ * 20-APR-2006 Khasim Modified VRFB based Rotation,
+ * The image data is always read from 0 degree
+ * view and written
+ * to the virtual space of desired rotation angle
+ * 4-DEC-2006 Jian Changed to support better memory management
+ *
+ * 17-Nov-2008 Hardik Changed driver to use video_ioctl2
+ *
+ * 23-Feb-2010 Vaibhav H Modified to use new DSS2 interface
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/videodev2.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+
+#include <media/videobuf-dma-contig.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+
+#include <plat/dma.h>
+#include <plat/vrfb.h>
+#include <video/omapdss.h>
+
+#include "omap_voutlib.h"
+#include "omap_voutdef.h"
+#include "omap_vout_vrfb.h"
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("OMAP Video for Linux Video out driver");
+MODULE_LICENSE("GPL");
+
+/* Driver Configuration macros */
+#define VOUT_NAME "omap_vout"
+
+enum omap_vout_channels {
+ OMAP_VIDEO1,
+ OMAP_VIDEO2,
+};
+
+static struct videobuf_queue_ops video_vbq_ops;
+/* Variables configurable through module params*/
+static u32 video1_numbuffers = 3;
+static u32 video2_numbuffers = 3;
+static u32 video1_bufsize = OMAP_VOUT_MAX_BUF_SIZE;
+static u32 video2_bufsize = OMAP_VOUT_MAX_BUF_SIZE;
+static bool vid1_static_vrfb_alloc;
+static bool vid2_static_vrfb_alloc;
+static bool debug;
+
+/* Module parameters */
+module_param(video1_numbuffers, uint, S_IRUGO);
+MODULE_PARM_DESC(video1_numbuffers,
+ "Number of buffers to be allocated at init time for Video1 device.");
+
+module_param(video2_numbuffers, uint, S_IRUGO);
+MODULE_PARM_DESC(video2_numbuffers,
+ "Number of buffers to be allocated at init time for Video2 device.");
+
+module_param(video1_bufsize, uint, S_IRUGO);
+MODULE_PARM_DESC(video1_bufsize,
+ "Size of the buffer to be allocated for video1 device");
+
+module_param(video2_bufsize, uint, S_IRUGO);
+MODULE_PARM_DESC(video2_bufsize,
+ "Size of the buffer to be allocated for video2 device");
+
+module_param(vid1_static_vrfb_alloc, bool, S_IRUGO);
+MODULE_PARM_DESC(vid1_static_vrfb_alloc,
+ "Static allocation of the VRFB buffer for video1 device");
+
+module_param(vid2_static_vrfb_alloc, bool, S_IRUGO);
+MODULE_PARM_DESC(vid2_static_vrfb_alloc,
+ "Static allocation of the VRFB buffer for video2 device");
+
+module_param(debug, bool, S_IRUGO);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+/* list of image formats supported by OMAP2 video pipelines */
+static const struct v4l2_fmtdesc omap_formats[] = {
+ {
+ /* Note: V4L2 defines RGB565 as:
+ *
+ * Byte 0 Byte 1
+ * g2 g1 g0 r4 r3 r2 r1 r0 b4 b3 b2 b1 b0 g5 g4 g3
+ *
+ * We interpret RGB565 as:
+ *
+ * Byte 0 Byte 1
+ * g2 g1 g0 b4 b3 b2 b1 b0 r4 r3 r2 r1 r0 g5 g4 g3
+ */
+ .description = "RGB565, le",
+ .pixelformat = V4L2_PIX_FMT_RGB565,
+ },
+ {
+ /* Note: V4L2 defines RGB32 as: RGB-8-8-8-8 we use
+ * this for RGB24 unpack mode, the last 8 bits are ignored
+ * */
+ .description = "RGB32, le",
+ .pixelformat = V4L2_PIX_FMT_RGB32,
+ },
+ {
+ /* Note: V4L2 defines RGB24 as: RGB-8-8-8 we use
+ * this for RGB24 packed mode
+ *
+ */
+ .description = "RGB24, le",
+ .pixelformat = V4L2_PIX_FMT_RGB24,
+ },
+ {
+ .description = "YUYV (YUV 4:2:2), packed",
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ },
+ {
+ .description = "UYVY, packed",
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ },
+};
+
+#define NUM_OUTPUT_FORMATS (ARRAY_SIZE(omap_formats))
+
+/*
+ * Try format
+ */
+static int omap_vout_try_format(struct v4l2_pix_format *pix)
+{
+ int ifmt, bpp = 0;
+
+ pix->height = clamp(pix->height, (u32)VID_MIN_HEIGHT,
+ (u32)VID_MAX_HEIGHT);
+ pix->width = clamp(pix->width, (u32)VID_MIN_WIDTH, (u32)VID_MAX_WIDTH);
+
+ for (ifmt = 0; ifmt < NUM_OUTPUT_FORMATS; ifmt++) {
+ if (pix->pixelformat == omap_formats[ifmt].pixelformat)
+ break;
+ }
+
+ if (ifmt == NUM_OUTPUT_FORMATS)
+ ifmt = 0;
+
+ pix->pixelformat = omap_formats[ifmt].pixelformat;
+ pix->field = V4L2_FIELD_ANY;
+ pix->priv = 0;
+
+ switch (pix->pixelformat) {
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_UYVY:
+ default:
+ pix->colorspace = V4L2_COLORSPACE_JPEG;
+ bpp = YUYV_BPP;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB565X:
+ pix->colorspace = V4L2_COLORSPACE_SRGB;
+ bpp = RGB565_BPP;
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ pix->colorspace = V4L2_COLORSPACE_SRGB;
+ bpp = RGB24_BPP;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_BGR32:
+ pix->colorspace = V4L2_COLORSPACE_SRGB;
+ bpp = RGB32_BPP;
+ break;
+ }
+ pix->bytesperline = pix->width * bpp;
+ pix->sizeimage = pix->bytesperline * pix->height;
+
+ return bpp;
+}
+
+/*
+ * omap_vout_uservirt_to_phys: This inline function is used to convert user
+ * space virtual address to physical address.
+ */
+static u32 omap_vout_uservirt_to_phys(u32 virtp)
+{
+ unsigned long physp = 0;
+ struct vm_area_struct *vma;
+ struct mm_struct *mm = current->mm;
+
+ vma = find_vma(mm, virtp);
+ /* For kernel direct-mapped memory, take the easy way */
+ if (virtp >= PAGE_OFFSET) {
+ physp = virt_to_phys((void *) virtp);
+ } else if (vma && (vma->vm_flags & VM_IO) && vma->vm_pgoff) {
+ /* this will catch, kernel-allocated, mmaped-to-usermode
+ addresses */
+ physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start);
+ } else {
+ /* otherwise, use get_user_pages() for general userland pages */
+ int res, nr_pages = 1;
+ struct page *pages;
+ down_read(&current->mm->mmap_sem);
+
+ res = get_user_pages(current, current->mm, virtp, nr_pages, 1,
+ 0, &pages, NULL);
+ up_read(&current->mm->mmap_sem);
+
+ if (res == nr_pages) {
+ physp = __pa(page_address(&pages[0]) +
+ (virtp & ~PAGE_MASK));
+ } else {
+ printk(KERN_WARNING VOUT_NAME
+ "get_user_pages failed\n");
+ return 0;
+ }
+ }
+
+ return physp;
+}
+
+/*
+ * Free the V4L2 buffers
+ */
+void omap_vout_free_buffers(struct omap_vout_device *vout)
+{
+ int i, numbuffers;
+
+ /* Allocate memory for the buffers */
+ numbuffers = (vout->vid) ? video2_numbuffers : video1_numbuffers;
+ vout->buffer_size = (vout->vid) ? video2_bufsize : video1_bufsize;
+
+ for (i = 0; i < numbuffers; i++) {
+ omap_vout_free_buffer(vout->buf_virt_addr[i],
+ vout->buffer_size);
+ vout->buf_phy_addr[i] = 0;
+ vout->buf_virt_addr[i] = 0;
+ }
+}
+
+/*
+ * Convert V4L2 rotation to DSS rotation
+ * V4L2 understand 0, 90, 180, 270.
+ * Convert to 0, 1, 2 and 3 respectively for DSS
+ */
+static int v4l2_rot_to_dss_rot(int v4l2_rotation,
+ enum dss_rotation *rotation, bool mirror)
+{
+ int ret = 0;
+
+ switch (v4l2_rotation) {
+ case 90:
+ *rotation = dss_rotation_90_degree;
+ break;
+ case 180:
+ *rotation = dss_rotation_180_degree;
+ break;
+ case 270:
+ *rotation = dss_rotation_270_degree;
+ break;
+ case 0:
+ *rotation = dss_rotation_0_degree;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int omap_vout_calculate_offset(struct omap_vout_device *vout)
+{
+ struct omapvideo_info *ovid;
+ struct v4l2_rect *crop = &vout->crop;
+ struct v4l2_pix_format *pix = &vout->pix;
+ int *cropped_offset = &vout->cropped_offset;
+ int ps = 2, line_length = 0;
+
+ ovid = &vout->vid_info;
+
+ if (ovid->rotation_type == VOUT_ROT_VRFB) {
+ omap_vout_calculate_vrfb_offset(vout);
+ } else {
+ vout->line_length = line_length = pix->width;
+
+ if (V4L2_PIX_FMT_YUYV == pix->pixelformat ||
+ V4L2_PIX_FMT_UYVY == pix->pixelformat)
+ ps = 2;
+ else if (V4L2_PIX_FMT_RGB32 == pix->pixelformat)
+ ps = 4;
+ else if (V4L2_PIX_FMT_RGB24 == pix->pixelformat)
+ ps = 3;
+
+ vout->ps = ps;
+
+ *cropped_offset = (line_length * ps) *
+ crop->top + crop->left * ps;
+ }
+
+ v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "%s Offset:%x\n",
+ __func__, vout->cropped_offset);
+
+ return 0;
+}
+
+/*
+ * Convert V4L2 pixel format to DSS pixel format
+ */
+static int video_mode_to_dss_mode(struct omap_vout_device *vout)
+{
+ struct omap_overlay *ovl;
+ struct omapvideo_info *ovid;
+ struct v4l2_pix_format *pix = &vout->pix;
+ enum omap_color_mode mode;
+
+ ovid = &vout->vid_info;
+ ovl = ovid->overlays[0];
+
+ switch (pix->pixelformat) {
+ case 0:
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ mode = OMAP_DSS_COLOR_YUV2;
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ mode = OMAP_DSS_COLOR_UYVY;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ mode = OMAP_DSS_COLOR_RGB16;
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ mode = OMAP_DSS_COLOR_RGB24P;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ mode = (ovl->id == OMAP_DSS_VIDEO1) ?
+ OMAP_DSS_COLOR_RGB24U : OMAP_DSS_COLOR_ARGB32;
+ break;
+ case V4L2_PIX_FMT_BGR32:
+ mode = OMAP_DSS_COLOR_RGBX32;
+ break;
+ default:
+ mode = -EINVAL;
+ }
+ return mode;
+}
+
+/*
+ * Setup the overlay
+ */
+static int omapvid_setup_overlay(struct omap_vout_device *vout,
+ struct omap_overlay *ovl, int posx, int posy, int outw,
+ int outh, u32 addr)
+{
+ int ret = 0;
+ struct omap_overlay_info info;
+ int cropheight, cropwidth, pixheight, pixwidth;
+
+ if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0 &&
+ (outw != vout->pix.width || outh != vout->pix.height)) {
+ ret = -EINVAL;
+ goto setup_ovl_err;
+ }
+
+ vout->dss_mode = video_mode_to_dss_mode(vout);
+ if (vout->dss_mode == -EINVAL) {
+ ret = -EINVAL;
+ goto setup_ovl_err;
+ }
+
+ /* Setup the input plane parameters according to
+ * rotation value selected.
+ */
+ if (is_rotation_90_or_270(vout)) {
+ cropheight = vout->crop.width;
+ cropwidth = vout->crop.height;
+ pixheight = vout->pix.width;
+ pixwidth = vout->pix.height;
+ } else {
+ cropheight = vout->crop.height;
+ cropwidth = vout->crop.width;
+ pixheight = vout->pix.height;
+ pixwidth = vout->pix.width;
+ }
+
+ ovl->get_overlay_info(ovl, &info);
+ info.paddr = addr;
+ info.width = cropwidth;
+ info.height = cropheight;
+ info.color_mode = vout->dss_mode;
+ info.mirror = vout->mirror;
+ info.pos_x = posx;
+ info.pos_y = posy;
+ info.out_width = outw;
+ info.out_height = outh;
+ info.global_alpha = vout->win.global_alpha;
+ if (!is_rotation_enabled(vout)) {
+ info.rotation = 0;
+ info.rotation_type = OMAP_DSS_ROT_DMA;
+ info.screen_width = pixwidth;
+ } else {
+ info.rotation = vout->rotation;
+ info.rotation_type = OMAP_DSS_ROT_VRFB;
+ info.screen_width = 2048;
+ }
+
+ v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev,
+ "%s enable=%d addr=%x width=%d\n height=%d color_mode=%d\n"
+ "rotation=%d mirror=%d posx=%d posy=%d out_width = %d \n"
+ "out_height=%d rotation_type=%d screen_width=%d\n",
+ __func__, ovl->is_enabled(ovl), info.paddr, info.width, info.height,
+ info.color_mode, info.rotation, info.mirror, info.pos_x,
+ info.pos_y, info.out_width, info.out_height, info.rotation_type,
+ info.screen_width);
+
+ ret = ovl->set_overlay_info(ovl, &info);
+ if (ret)
+ goto setup_ovl_err;
+
+ return 0;
+
+setup_ovl_err:
+ v4l2_warn(&vout->vid_dev->v4l2_dev, "setup_overlay failed\n");
+ return ret;
+}
+
+/*
+ * Initialize the overlay structure
+ */
+static int omapvid_init(struct omap_vout_device *vout, u32 addr)
+{
+ int ret = 0, i;
+ struct v4l2_window *win;
+ struct omap_overlay *ovl;
+ int posx, posy, outw, outh, temp;
+ struct omap_video_timings *timing;
+ struct omapvideo_info *ovid = &vout->vid_info;
+
+ win = &vout->win;
+ for (i = 0; i < ovid->num_overlays; i++) {
+ ovl = ovid->overlays[i];
+ if (!ovl->manager || !ovl->manager->device)
+ return -EINVAL;
+
+ timing = &ovl->manager->device->panel.timings;
+
+ outw = win->w.width;
+ outh = win->w.height;
+ switch (vout->rotation) {
+ case dss_rotation_90_degree:
+ /* Invert the height and width for 90
+ * and 270 degree rotation
+ */
+ temp = outw;
+ outw = outh;
+ outh = temp;
+ posy = (timing->y_res - win->w.width) - win->w.left;
+ posx = win->w.top;
+ break;
+
+ case dss_rotation_180_degree:
+ posx = (timing->x_res - win->w.width) - win->w.left;
+ posy = (timing->y_res - win->w.height) - win->w.top;
+ break;
+
+ case dss_rotation_270_degree:
+ temp = outw;
+ outw = outh;
+ outh = temp;
+ posy = win->w.left;
+ posx = (timing->x_res - win->w.height) - win->w.top;
+ break;
+
+ default:
+ posx = win->w.left;
+ posy = win->w.top;
+ break;
+ }
+
+ ret = omapvid_setup_overlay(vout, ovl, posx, posy,
+ outw, outh, addr);
+ if (ret)
+ goto omapvid_init_err;
+ }
+ return 0;
+
+omapvid_init_err:
+ v4l2_warn(&vout->vid_dev->v4l2_dev, "apply_changes failed\n");
+ return ret;
+}
+
+/*
+ * Apply the changes set the go bit of DSS
+ */
+static int omapvid_apply_changes(struct omap_vout_device *vout)
+{
+ int i;
+ struct omap_overlay *ovl;
+ struct omapvideo_info *ovid = &vout->vid_info;
+
+ for (i = 0; i < ovid->num_overlays; i++) {
+ ovl = ovid->overlays[i];
+ if (!ovl->manager || !ovl->manager->device)
+ return -EINVAL;
+ ovl->manager->apply(ovl->manager);
+ }
+
+ return 0;
+}
+
+static int omapvid_handle_interlace_display(struct omap_vout_device *vout,
+ unsigned int irqstatus, struct timeval timevalue)
+{
+ u32 fid;
+
+ if (vout->first_int) {
+ vout->first_int = 0;
+ goto err;
+ }
+
+ if (irqstatus & DISPC_IRQ_EVSYNC_ODD)
+ fid = 1;
+ else if (irqstatus & DISPC_IRQ_EVSYNC_EVEN)
+ fid = 0;
+ else
+ goto err;
+
+ vout->field_id ^= 1;
+ if (fid != vout->field_id) {
+ if (fid == 0)
+ vout->field_id = fid;
+ } else if (0 == fid) {
+ if (vout->cur_frm == vout->next_frm)
+ goto err;
+
+ vout->cur_frm->ts = timevalue;
+ vout->cur_frm->state = VIDEOBUF_DONE;
+ wake_up_interruptible(&vout->cur_frm->done);
+ vout->cur_frm = vout->next_frm;
+ } else {
+ if (list_empty(&vout->dma_queue) ||
+ (vout->cur_frm != vout->next_frm))
+ goto err;
+ }
+
+ return vout->field_id;
+err:
+ return 0;
+}
+
+static void omap_vout_isr(void *arg, unsigned int irqstatus)
+{
+ int ret, fid, mgr_id;
+ u32 addr, irq;
+ struct omap_overlay *ovl;
+ struct timeval timevalue;
+ struct omapvideo_info *ovid;
+ struct omap_dss_device *cur_display;
+ struct omap_vout_device *vout = (struct omap_vout_device *)arg;
+
+ if (!vout->streaming)
+ return;
+
+ ovid = &vout->vid_info;
+ ovl = ovid->overlays[0];
+ /* get the display device attached to the overlay */
+ if (!ovl->manager || !ovl->manager->device)
+ return;
+
+ mgr_id = ovl->manager->id;
+ cur_display = ovl->manager->device;
+
+ spin_lock(&vout->vbq_lock);
+ do_gettimeofday(&timevalue);
+
+ switch (cur_display->type) {
+ case OMAP_DISPLAY_TYPE_DSI:
+ case OMAP_DISPLAY_TYPE_DPI:
+ if (mgr_id == OMAP_DSS_CHANNEL_LCD)
+ irq = DISPC_IRQ_VSYNC;
+ else if (mgr_id == OMAP_DSS_CHANNEL_LCD2)
+ irq = DISPC_IRQ_VSYNC2;
+ else
+ goto vout_isr_err;
+
+ if (!(irqstatus & irq))
+ goto vout_isr_err;
+ break;
+ case OMAP_DISPLAY_TYPE_VENC:
+ fid = omapvid_handle_interlace_display(vout, irqstatus,
+ timevalue);
+ if (!fid)
+ goto vout_isr_err;
+ break;
+ case OMAP_DISPLAY_TYPE_HDMI:
+ if (!(irqstatus & DISPC_IRQ_EVSYNC_EVEN))
+ goto vout_isr_err;
+ break;
+ default:
+ goto vout_isr_err;
+ }
+
+ if (!vout->first_int && (vout->cur_frm != vout->next_frm)) {
+ vout->cur_frm->ts = timevalue;
+ vout->cur_frm->state = VIDEOBUF_DONE;
+ wake_up_interruptible(&vout->cur_frm->done);
+ vout->cur_frm = vout->next_frm;
+ }
+
+ vout->first_int = 0;
+ if (list_empty(&vout->dma_queue))
+ goto vout_isr_err;
+
+ vout->next_frm = list_entry(vout->dma_queue.next,
+ struct videobuf_buffer, queue);
+ list_del(&vout->next_frm->queue);
+
+ vout->next_frm->state = VIDEOBUF_ACTIVE;
+
+ addr = (unsigned long) vout->queued_buf_addr[vout->next_frm->i]
+ + vout->cropped_offset;
+
+ /* First save the configuration in ovelray structure */
+ ret = omapvid_init(vout, addr);
+ if (ret)
+ printk(KERN_ERR VOUT_NAME
+ "failed to set overlay info\n");
+ /* Enable the pipeline and set the Go bit */
+ ret = omapvid_apply_changes(vout);
+ if (ret)
+ printk(KERN_ERR VOUT_NAME "failed to change mode\n");
+
+vout_isr_err:
+ spin_unlock(&vout->vbq_lock);
+}
+
+/* Video buffer call backs */
+
+/*
+ * Buffer setup function is called by videobuf layer when REQBUF ioctl is
+ * called. This is used to setup buffers and return size and count of
+ * buffers allocated. After the call to this buffer, videobuf layer will
+ * setup buffer queue depending on the size and count of buffers
+ */
+static int omap_vout_buffer_setup(struct videobuf_queue *q, unsigned int *count,
+ unsigned int *size)
+{
+ int startindex = 0, i, j;
+ u32 phy_addr = 0, virt_addr = 0;
+ struct omap_vout_device *vout = q->priv_data;
+ struct omapvideo_info *ovid = &vout->vid_info;
+ int vid_max_buf_size;
+
+ if (!vout)
+ return -EINVAL;
+
+ vid_max_buf_size = vout->vid == OMAP_VIDEO1 ? video1_bufsize :
+ video2_bufsize;
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != q->type)
+ return -EINVAL;
+
+ startindex = (vout->vid == OMAP_VIDEO1) ?
+ video1_numbuffers : video2_numbuffers;
+ if (V4L2_MEMORY_MMAP == vout->memory && *count < startindex)
+ *count = startindex;
+
+ if (ovid->rotation_type == VOUT_ROT_VRFB) {
+ if (omap_vout_vrfb_buffer_setup(vout, count, startindex))
+ return -ENOMEM;
+ }
+
+ if (V4L2_MEMORY_MMAP != vout->memory)
+ return 0;
+
+ /* Now allocated the V4L2 buffers */
+ *size = PAGE_ALIGN(vout->pix.width * vout->pix.height * vout->bpp);
+ startindex = (vout->vid == OMAP_VIDEO1) ?
+ video1_numbuffers : video2_numbuffers;
+
+ /* Check the size of the buffer */
+ if (*size > vid_max_buf_size) {
+ v4l2_err(&vout->vid_dev->v4l2_dev,
+ "buffer allocation mismatch [%u] [%u]\n",
+ *size, vout->buffer_size);
+ return -ENOMEM;
+ }
+
+ for (i = startindex; i < *count; i++) {
+ vout->buffer_size = *size;
+
+ virt_addr = omap_vout_alloc_buffer(vout->buffer_size,
+ &phy_addr);
+ if (!virt_addr) {
+ if (ovid->rotation_type == VOUT_ROT_NONE) {
+ break;
+ } else {
+ if (!is_rotation_enabled(vout))
+ break;
+ /* Free the VRFB buffers if no space for V4L2 buffers */
+ for (j = i; j < *count; j++) {
+ omap_vout_free_buffer(
+ vout->smsshado_virt_addr[j],
+ vout->smsshado_size);
+ vout->smsshado_virt_addr[j] = 0;
+ vout->smsshado_phy_addr[j] = 0;
+ }
+ }
+ }
+ vout->buf_virt_addr[i] = virt_addr;
+ vout->buf_phy_addr[i] = phy_addr;
+ }
+ *count = vout->buffer_allocated = i;
+
+ return 0;
+}
+
+/*
+ * Free the V4L2 buffers additionally allocated than default
+ * number of buffers
+ */
+static void omap_vout_free_extra_buffers(struct omap_vout_device *vout)
+{
+ int num_buffers = 0, i;
+
+ num_buffers = (vout->vid == OMAP_VIDEO1) ?
+ video1_numbuffers : video2_numbuffers;
+
+ for (i = num_buffers; i < vout->buffer_allocated; i++) {
+ if (vout->buf_virt_addr[i])
+ omap_vout_free_buffer(vout->buf_virt_addr[i],
+ vout->buffer_size);
+
+ vout->buf_virt_addr[i] = 0;
+ vout->buf_phy_addr[i] = 0;
+ }
+ vout->buffer_allocated = num_buffers;
+}
+
+/*
+ * This function will be called when VIDIOC_QBUF ioctl is called.
+ * It prepare buffers before give out for the display. This function
+ * converts user space virtual address into physical address if userptr memory
+ * exchange mechanism is used. If rotation is enabled, it copies entire
+ * buffer into VRFB memory space before giving it to the DSS.
+ */
+static int omap_vout_buffer_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct omap_vout_device *vout = q->priv_data;
+ struct omapvideo_info *ovid = &vout->vid_info;
+
+ if (VIDEOBUF_NEEDS_INIT == vb->state) {
+ vb->width = vout->pix.width;
+ vb->height = vout->pix.height;
+ vb->size = vb->width * vb->height * vout->bpp;
+ vb->field = field;
+ }
+ vb->state = VIDEOBUF_PREPARED;
+ /* if user pointer memory mechanism is used, get the physical
+ * address of the buffer
+ */
+ if (V4L2_MEMORY_USERPTR == vb->memory) {
+ if (0 == vb->baddr)
+ return -EINVAL;
+ /* Physical address */
+ vout->queued_buf_addr[vb->i] = (u8 *)
+ omap_vout_uservirt_to_phys(vb->baddr);
+ } else {
+ u32 addr, dma_addr;
+ unsigned long size;
+
+ addr = (unsigned long) vout->buf_virt_addr[vb->i];
+ size = (unsigned long) vb->size;
+
+ dma_addr = dma_map_single(vout->vid_dev->v4l2_dev.dev, (void *) addr,
+ size, DMA_TO_DEVICE);
+ if (dma_mapping_error(vout->vid_dev->v4l2_dev.dev, dma_addr))
+ v4l2_err(&vout->vid_dev->v4l2_dev, "dma_map_single failed\n");
+
+ vout->queued_buf_addr[vb->i] = (u8 *)vout->buf_phy_addr[vb->i];
+ }
+
+ if (ovid->rotation_type == VOUT_ROT_VRFB)
+ return omap_vout_prepare_vrfb(vout, vb);
+ else
+ return 0;
+}
+
+/*
+ * Buffer queue function will be called from the videobuf layer when _QBUF
+ * ioctl is called. It is used to enqueue buffer, which is ready to be
+ * displayed.
+ */
+static void omap_vout_buffer_queue(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ struct omap_vout_device *vout = q->priv_data;
+
+ /* Driver is also maintainig a queue. So enqueue buffer in the driver
+ * queue */
+ list_add_tail(&vb->queue, &vout->dma_queue);
+
+ vb->state = VIDEOBUF_QUEUED;
+}
+
+/*
+ * Buffer release function is called from videobuf layer to release buffer
+ * which are already allocated
+ */
+static void omap_vout_buffer_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ struct omap_vout_device *vout = q->priv_data;
+
+ vb->state = VIDEOBUF_NEEDS_INIT;
+
+ if (V4L2_MEMORY_MMAP != vout->memory)
+ return;
+}
+
+/*
+ * File operations
+ */
+static unsigned int omap_vout_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct omap_vout_device *vout = file->private_data;
+ struct videobuf_queue *q = &vout->vbq;
+
+ return videobuf_poll_stream(file, q, wait);
+}
+
+static void omap_vout_vm_open(struct vm_area_struct *vma)
+{
+ struct omap_vout_device *vout = vma->vm_private_data;
+
+ v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev,
+ "vm_open [vma=%08lx-%08lx]\n", vma->vm_start, vma->vm_end);
+ vout->mmap_count++;
+}
+
+static void omap_vout_vm_close(struct vm_area_struct *vma)
+{
+ struct omap_vout_device *vout = vma->vm_private_data;
+
+ v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev,
+ "vm_close [vma=%08lx-%08lx]\n", vma->vm_start, vma->vm_end);
+ vout->mmap_count--;
+}
+
+static struct vm_operations_struct omap_vout_vm_ops = {
+ .open = omap_vout_vm_open,
+ .close = omap_vout_vm_close,
+};
+
+static int omap_vout_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ int i;
+ void *pos;
+ unsigned long start = vma->vm_start;
+ unsigned long size = (vma->vm_end - vma->vm_start);
+ struct omap_vout_device *vout = file->private_data;
+ struct videobuf_queue *q = &vout->vbq;
+
+ v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev,
+ " %s pgoff=0x%lx, start=0x%lx, end=0x%lx\n", __func__,
+ vma->vm_pgoff, vma->vm_start, vma->vm_end);
+
+ /* look for the buffer to map */
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ if (NULL == q->bufs[i])
+ continue;
+ if (V4L2_MEMORY_MMAP != q->bufs[i]->memory)
+ continue;
+ if (q->bufs[i]->boff == (vma->vm_pgoff << PAGE_SHIFT))
+ break;
+ }
+
+ if (VIDEO_MAX_FRAME == i) {
+ v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev,
+ "offset invalid [offset=0x%lx]\n",
+ (vma->vm_pgoff << PAGE_SHIFT));
+ return -EINVAL;
+ }
+ /* Check the size of the buffer */
+ if (size > vout->buffer_size) {
+ v4l2_err(&vout->vid_dev->v4l2_dev,
+ "insufficient memory [%lu] [%u]\n",
+ size, vout->buffer_size);
+ return -ENOMEM;
+ }
+
+ q->bufs[i]->baddr = vma->vm_start;
+
+ vma->vm_flags |= VM_RESERVED;
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+ vma->vm_ops = &omap_vout_vm_ops;
+ vma->vm_private_data = (void *) vout;
+ pos = (void *)vout->buf_virt_addr[i];
+ vma->vm_pgoff = virt_to_phys((void *)pos) >> PAGE_SHIFT;
+ while (size > 0) {
+ unsigned long pfn;
+ pfn = virt_to_phys((void *) pos) >> PAGE_SHIFT;
+ if (remap_pfn_range(vma, start, pfn, PAGE_SIZE, PAGE_SHARED))
+ return -EAGAIN;
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ vout->mmap_count++;
+ v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Exiting %s\n", __func__);
+
+ return 0;
+}
+
+static int omap_vout_release(struct file *file)
+{
+ unsigned int ret, i;
+ struct videobuf_queue *q;
+ struct omapvideo_info *ovid;
+ struct omap_vout_device *vout = file->private_data;
+
+ v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Entering %s\n", __func__);
+ ovid = &vout->vid_info;
+
+ if (!vout)
+ return 0;
+
+ q = &vout->vbq;
+ /* Disable all the overlay managers connected with this interface */
+ for (i = 0; i < ovid->num_overlays; i++) {
+ struct omap_overlay *ovl = ovid->overlays[i];
+ if (ovl->manager && ovl->manager->device)
+ ovl->disable(ovl);
+ }
+ /* Turn off the pipeline */
+ ret = omapvid_apply_changes(vout);
+ if (ret)
+ v4l2_warn(&vout->vid_dev->v4l2_dev,
+ "Unable to apply changes\n");
+
+ /* Free all buffers */
+ omap_vout_free_extra_buffers(vout);
+
+ /* Free the VRFB buffers only if they are allocated
+ * during reqbufs. Don't free if init time allocated
+ */
+ if (ovid->rotation_type == VOUT_ROT_VRFB) {
+ if (!vout->vrfb_static_allocation)
+ omap_vout_free_vrfb_buffers(vout);
+ }
+ videobuf_mmap_free(q);
+
+ /* Even if apply changes fails we should continue
+ freeing allocated memory */
+ if (vout->streaming) {
+ u32 mask = 0;
+
+ mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN |
+ DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_VSYNC2;
+ omap_dispc_unregister_isr(omap_vout_isr, vout, mask);
+ vout->streaming = 0;
+
+ videobuf_streamoff(q);
+ videobuf_queue_cancel(q);
+ }
+
+ if (vout->mmap_count != 0)
+ vout->mmap_count = 0;
+
+ vout->opened -= 1;
+ file->private_data = NULL;
+
+ if (vout->buffer_allocated)
+ videobuf_mmap_free(q);
+
+ v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Exiting %s\n", __func__);
+ return ret;
+}
+
+static int omap_vout_open(struct file *file)
+{
+ struct videobuf_queue *q;
+ struct omap_vout_device *vout = NULL;
+
+ vout = video_drvdata(file);
+ v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Entering %s\n", __func__);
+
+ if (vout == NULL)
+ return -ENODEV;
+
+ /* for now, we only support single open */
+ if (vout->opened)
+ return -EBUSY;
+
+ vout->opened += 1;
+
+ file->private_data = vout;
+ vout->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+
+ q = &vout->vbq;
+ video_vbq_ops.buf_setup = omap_vout_buffer_setup;
+ video_vbq_ops.buf_prepare = omap_vout_buffer_prepare;
+ video_vbq_ops.buf_release = omap_vout_buffer_release;
+ video_vbq_ops.buf_queue = omap_vout_buffer_queue;
+ spin_lock_init(&vout->vbq_lock);
+
+ videobuf_queue_dma_contig_init(q, &video_vbq_ops, q->dev,
+ &vout->vbq_lock, vout->type, V4L2_FIELD_NONE,
+ sizeof(struct videobuf_buffer), vout, NULL);
+
+ v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Exiting %s\n", __func__);
+ return 0;
+}
+
+/*
+ * V4L2 ioctls
+ */
+static int vidioc_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct omap_vout_device *vout = fh;
+
+ strlcpy(cap->driver, VOUT_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, vout->vfd->name, sizeof(cap->card));
+ cap->bus_info[0] = '\0';
+ cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT |
+ V4L2_CAP_VIDEO_OUTPUT_OVERLAY;
+
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_out(struct file *file, void *fh,
+ struct v4l2_fmtdesc *fmt)
+{
+ int index = fmt->index;
+
+ if (index >= NUM_OUTPUT_FORMATS)
+ return -EINVAL;
+
+ fmt->flags = omap_formats[index].flags;
+ strlcpy(fmt->description, omap_formats[index].description,
+ sizeof(fmt->description));
+ fmt->pixelformat = omap_formats[index].pixelformat;
+
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_out(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct omap_vout_device *vout = fh;
+
+ f->fmt.pix = vout->pix;
+ return 0;
+
+}
+
+static int vidioc_try_fmt_vid_out(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct omap_overlay *ovl;
+ struct omapvideo_info *ovid;
+ struct omap_video_timings *timing;
+ struct omap_vout_device *vout = fh;
+
+ ovid = &vout->vid_info;
+ ovl = ovid->overlays[0];
+
+ if (!ovl->manager || !ovl->manager->device)
+ return -EINVAL;
+ /* get the display device attached to the overlay */
+ timing = &ovl->manager->device->panel.timings;
+
+ vout->fbuf.fmt.height = timing->y_res;
+ vout->fbuf.fmt.width = timing->x_res;
+
+ omap_vout_try_format(&f->fmt.pix);
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_out(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ int ret, bpp;
+ struct omap_overlay *ovl;
+ struct omapvideo_info *ovid;
+ struct omap_video_timings *timing;
+ struct omap_vout_device *vout = fh;
+
+ if (vout->streaming)
+ return -EBUSY;
+
+ mutex_lock(&vout->lock);
+
+ ovid = &vout->vid_info;
+ ovl = ovid->overlays[0];
+
+ /* get the display device attached to the overlay */
+ if (!ovl->manager || !ovl->manager->device) {
+ ret = -EINVAL;
+ goto s_fmt_vid_out_exit;
+ }
+ timing = &ovl->manager->device->panel.timings;
+
+ /* We dont support RGB24-packed mode if vrfb rotation
+ * is enabled*/
+ if ((is_rotation_enabled(vout)) &&
+ f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) {
+ ret = -EINVAL;
+ goto s_fmt_vid_out_exit;
+ }
+
+ /* get the framebuffer parameters */
+
+ if (is_rotation_90_or_270(vout)) {
+ vout->fbuf.fmt.height = timing->x_res;
+ vout->fbuf.fmt.width = timing->y_res;
+ } else {
+ vout->fbuf.fmt.height = timing->y_res;
+ vout->fbuf.fmt.width = timing->x_res;
+ }
+
+ /* change to samller size is OK */
+
+ bpp = omap_vout_try_format(&f->fmt.pix);
+ f->fmt.pix.sizeimage = f->fmt.pix.width * f->fmt.pix.height * bpp;
+
+ /* try & set the new output format */
+ vout->bpp = bpp;
+ vout->pix = f->fmt.pix;
+ vout->vrfb_bpp = 1;
+
+ /* If YUYV then vrfb bpp is 2, for others its 1 */
+ if (V4L2_PIX_FMT_YUYV == vout->pix.pixelformat ||
+ V4L2_PIX_FMT_UYVY == vout->pix.pixelformat)
+ vout->vrfb_bpp = 2;
+
+ /* set default crop and win */
+ omap_vout_new_format(&vout->pix, &vout->fbuf, &vout->crop, &vout->win);
+
+ /* Save the changes in the overlay strcuture */
+ ret = omapvid_init(vout, 0);
+ if (ret) {
+ v4l2_err(&vout->vid_dev->v4l2_dev, "failed to change mode\n");
+ goto s_fmt_vid_out_exit;
+ }
+
+ ret = 0;
+
+s_fmt_vid_out_exit:
+ mutex_unlock(&vout->lock);
+ return ret;
+}
+
+static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ int ret = 0;
+ struct omap_vout_device *vout = fh;
+ struct omap_overlay *ovl;
+ struct omapvideo_info *ovid;
+ struct v4l2_window *win = &f->fmt.win;
+
+ ovid = &vout->vid_info;
+ ovl = ovid->overlays[0];
+
+ ret = omap_vout_try_window(&vout->fbuf, win);
+
+ if (!ret) {
+ if ((ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0)
+ win->global_alpha = 255;
+ else
+ win->global_alpha = f->fmt.win.global_alpha;
+ }
+
+ return ret;
+}
+
+static int vidioc_s_fmt_vid_overlay(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ int ret = 0;
+ struct omap_overlay *ovl;
+ struct omapvideo_info *ovid;
+ struct omap_vout_device *vout = fh;
+ struct v4l2_window *win = &f->fmt.win;
+
+ mutex_lock(&vout->lock);
+ ovid = &vout->vid_info;
+ ovl = ovid->overlays[0];
+
+ ret = omap_vout_new_window(&vout->crop, &vout->win, &vout->fbuf, win);
+ if (!ret) {
+ /* Video1 plane does not support global alpha on OMAP3 */
+ if ((ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0)
+ vout->win.global_alpha = 255;
+ else
+ vout->win.global_alpha = f->fmt.win.global_alpha;
+
+ vout->win.chromakey = f->fmt.win.chromakey;
+ }
+ mutex_unlock(&vout->lock);
+ return ret;
+}
+
+static int vidioc_enum_fmt_vid_overlay(struct file *file, void *fh,
+ struct v4l2_fmtdesc *fmt)
+{
+ int index = fmt->index;
+
+ if (index >= NUM_OUTPUT_FORMATS)
+ return -EINVAL;
+
+ fmt->flags = omap_formats[index].flags;
+ strlcpy(fmt->description, omap_formats[index].description,
+ sizeof(fmt->description));
+ fmt->pixelformat = omap_formats[index].pixelformat;
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ u32 key_value = 0;
+ struct omap_overlay *ovl;
+ struct omapvideo_info *ovid;
+ struct omap_vout_device *vout = fh;
+ struct omap_overlay_manager_info info;
+ struct v4l2_window *win = &f->fmt.win;
+
+ ovid = &vout->vid_info;
+ ovl = ovid->overlays[0];
+
+ win->w = vout->win.w;
+ win->field = vout->win.field;
+ win->global_alpha = vout->win.global_alpha;
+
+ if (ovl->manager && ovl->manager->get_manager_info) {
+ ovl->manager->get_manager_info(ovl->manager, &info);
+ key_value = info.trans_key;
+ }
+ win->chromakey = key_value;
+ return 0;
+}
+
+static int vidioc_cropcap(struct file *file, void *fh,
+ struct v4l2_cropcap *cropcap)
+{
+ struct omap_vout_device *vout = fh;
+ struct v4l2_pix_format *pix = &vout->pix;
+
+ if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+
+ /* Width and height are always even */
+ cropcap->bounds.width = pix->width & ~1;
+ cropcap->bounds.height = pix->height & ~1;
+
+ omap_vout_default_crop(&vout->pix, &vout->fbuf, &cropcap->defrect);
+ cropcap->pixelaspect.numerator = 1;
+ cropcap->pixelaspect.denominator = 1;
+ return 0;
+}
+
+static int vidioc_g_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+{
+ struct omap_vout_device *vout = fh;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+ crop->c = vout->crop;
+ return 0;
+}
+
+static int vidioc_s_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+{
+ int ret = -EINVAL;
+ struct omap_vout_device *vout = fh;
+ struct omapvideo_info *ovid;
+ struct omap_overlay *ovl;
+ struct omap_video_timings *timing;
+
+ if (vout->streaming)
+ return -EBUSY;
+
+ mutex_lock(&vout->lock);
+ ovid = &vout->vid_info;
+ ovl = ovid->overlays[0];
+
+ if (!ovl->manager || !ovl->manager->device) {
+ ret = -EINVAL;
+ goto s_crop_err;
+ }
+ /* get the display device attached to the overlay */
+ timing = &ovl->manager->device->panel.timings;
+
+ if (is_rotation_90_or_270(vout)) {
+ vout->fbuf.fmt.height = timing->x_res;
+ vout->fbuf.fmt.width = timing->y_res;
+ } else {
+ vout->fbuf.fmt.height = timing->y_res;
+ vout->fbuf.fmt.width = timing->x_res;
+ }
+
+ if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ ret = omap_vout_new_crop(&vout->pix, &vout->crop, &vout->win,
+ &vout->fbuf, &crop->c);
+
+s_crop_err:
+ mutex_unlock(&vout->lock);
+ return ret;
+}
+
+static int vidioc_queryctrl(struct file *file, void *fh,
+ struct v4l2_queryctrl *ctrl)
+{
+ int ret = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_ROTATE:
+ ret = v4l2_ctrl_query_fill(ctrl, 0, 270, 90, 0);
+ break;
+ case V4L2_CID_BG_COLOR:
+ ret = v4l2_ctrl_query_fill(ctrl, 0, 0xFFFFFF, 1, 0);
+ break;
+ case V4L2_CID_VFLIP:
+ ret = v4l2_ctrl_query_fill(ctrl, 0, 1, 1, 0);
+ break;
+ default:
+ ctrl->name[0] = '\0';
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *ctrl)
+{
+ int ret = 0;
+ struct omap_vout_device *vout = fh;
+
+ switch (ctrl->id) {
+ case V4L2_CID_ROTATE:
+ ctrl->value = vout->control[0].value;
+ break;
+ case V4L2_CID_BG_COLOR:
+ {
+ struct omap_overlay_manager_info info;
+ struct omap_overlay *ovl;
+
+ ovl = vout->vid_info.overlays[0];
+ if (!ovl->manager || !ovl->manager->get_manager_info) {
+ ret = -EINVAL;
+ break;
+ }
+
+ ovl->manager->get_manager_info(ovl->manager, &info);
+ ctrl->value = info.default_color;
+ break;
+ }
+ case V4L2_CID_VFLIP:
+ ctrl->value = vout->control[2].value;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a)
+{
+ int ret = 0;
+ struct omap_vout_device *vout = fh;
+
+ switch (a->id) {
+ case V4L2_CID_ROTATE:
+ {
+ struct omapvideo_info *ovid;
+ int rotation = a->value;
+
+ ovid = &vout->vid_info;
+
+ mutex_lock(&vout->lock);
+ if (rotation && ovid->rotation_type == VOUT_ROT_NONE) {
+ mutex_unlock(&vout->lock);
+ ret = -ERANGE;
+ break;
+ }
+
+ if (rotation && vout->pix.pixelformat == V4L2_PIX_FMT_RGB24) {
+ mutex_unlock(&vout->lock);
+ ret = -EINVAL;
+ break;
+ }
+
+ if (v4l2_rot_to_dss_rot(rotation, &vout->rotation,
+ vout->mirror)) {
+ mutex_unlock(&vout->lock);
+ ret = -EINVAL;
+ break;
+ }
+
+ vout->control[0].value = rotation;
+ mutex_unlock(&vout->lock);
+ break;
+ }
+ case V4L2_CID_BG_COLOR:
+ {
+ struct omap_overlay *ovl;
+ unsigned int color = a->value;
+ struct omap_overlay_manager_info info;
+
+ ovl = vout->vid_info.overlays[0];
+
+ mutex_lock(&vout->lock);
+ if (!ovl->manager || !ovl->manager->get_manager_info) {
+ mutex_unlock(&vout->lock);
+ ret = -EINVAL;
+ break;
+ }
+
+ ovl->manager->get_manager_info(ovl->manager, &info);
+ info.default_color = color;
+ if (ovl->manager->set_manager_info(ovl->manager, &info)) {
+ mutex_unlock(&vout->lock);
+ ret = -EINVAL;
+ break;
+ }
+
+ vout->control[1].value = color;
+ mutex_unlock(&vout->lock);
+ break;
+ }
+ case V4L2_CID_VFLIP:
+ {
+ struct omap_overlay *ovl;
+ struct omapvideo_info *ovid;
+ unsigned int mirror = a->value;
+
+ ovid = &vout->vid_info;
+ ovl = ovid->overlays[0];
+
+ mutex_lock(&vout->lock);
+ if (mirror && ovid->rotation_type == VOUT_ROT_NONE) {
+ mutex_unlock(&vout->lock);
+ ret = -ERANGE;
+ break;
+ }
+
+ if (mirror && vout->pix.pixelformat == V4L2_PIX_FMT_RGB24) {
+ mutex_unlock(&vout->lock);
+ ret = -EINVAL;
+ break;
+ }
+ vout->mirror = mirror;
+ vout->control[2].value = mirror;
+ mutex_unlock(&vout->lock);
+ break;
+ }
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int vidioc_reqbufs(struct file *file, void *fh,
+ struct v4l2_requestbuffers *req)
+{
+ int ret = 0;
+ unsigned int i, num_buffers = 0;
+ struct omap_vout_device *vout = fh;
+ struct videobuf_queue *q = &vout->vbq;
+
+ if ((req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) || (req->count < 0))
+ return -EINVAL;
+ /* if memory is not mmp or userptr
+ return error */
+ if ((V4L2_MEMORY_MMAP != req->memory) &&
+ (V4L2_MEMORY_USERPTR != req->memory))
+ return -EINVAL;
+
+ mutex_lock(&vout->lock);
+ /* Cannot be requested when streaming is on */
+ if (vout->streaming) {
+ ret = -EBUSY;
+ goto reqbuf_err;
+ }
+
+ /* If buffers are already allocated free them */
+ if (q->bufs[0] && (V4L2_MEMORY_MMAP == q->bufs[0]->memory)) {
+ if (vout->mmap_count) {
+ ret = -EBUSY;
+ goto reqbuf_err;
+ }
+ num_buffers = (vout->vid == OMAP_VIDEO1) ?
+ video1_numbuffers : video2_numbuffers;
+ for (i = num_buffers; i < vout->buffer_allocated; i++) {
+ omap_vout_free_buffer(vout->buf_virt_addr[i],
+ vout->buffer_size);
+ vout->buf_virt_addr[i] = 0;
+ vout->buf_phy_addr[i] = 0;
+ }
+ vout->buffer_allocated = num_buffers;
+ videobuf_mmap_free(q);
+ } else if (q->bufs[0] && (V4L2_MEMORY_USERPTR == q->bufs[0]->memory)) {
+ if (vout->buffer_allocated) {
+ videobuf_mmap_free(q);
+ for (i = 0; i < vout->buffer_allocated; i++) {
+ kfree(q->bufs[i]);
+ q->bufs[i] = NULL;
+ }
+ vout->buffer_allocated = 0;
+ }
+ }
+
+ /*store the memory type in data structure */
+ vout->memory = req->memory;
+
+ INIT_LIST_HEAD(&vout->dma_queue);
+
+ /* call videobuf_reqbufs api */
+ ret = videobuf_reqbufs(q, req);
+ if (ret < 0)
+ goto reqbuf_err;
+
+ vout->buffer_allocated = req->count;
+
+reqbuf_err:
+ mutex_unlock(&vout->lock);
+ return ret;
+}
+
+static int vidioc_querybuf(struct file *file, void *fh,
+ struct v4l2_buffer *b)
+{
+ struct omap_vout_device *vout = fh;
+
+ return videobuf_querybuf(&vout->vbq, b);
+}
+
+static int vidioc_qbuf(struct file *file, void *fh,
+ struct v4l2_buffer *buffer)
+{
+ struct omap_vout_device *vout = fh;
+ struct videobuf_queue *q = &vout->vbq;
+
+ if ((V4L2_BUF_TYPE_VIDEO_OUTPUT != buffer->type) ||
+ (buffer->index >= vout->buffer_allocated) ||
+ (q->bufs[buffer->index]->memory != buffer->memory)) {
+ return -EINVAL;
+ }
+ if (V4L2_MEMORY_USERPTR == buffer->memory) {
+ if ((buffer->length < vout->pix.sizeimage) ||
+ (0 == buffer->m.userptr)) {
+ return -EINVAL;
+ }
+ }
+
+ if ((is_rotation_enabled(vout)) &&
+ vout->vrfb_dma_tx.req_status == DMA_CHAN_NOT_ALLOTED) {
+ v4l2_warn(&vout->vid_dev->v4l2_dev,
+ "DMA Channel not allocated for Rotation\n");
+ return -EINVAL;
+ }
+
+ return videobuf_qbuf(q, buffer);
+}
+
+static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct omap_vout_device *vout = fh;
+ struct videobuf_queue *q = &vout->vbq;
+
+ int ret;
+ u32 addr;
+ unsigned long size;
+ struct videobuf_buffer *vb;
+
+ vb = q->bufs[b->index];
+
+ if (!vout->streaming)
+ return -EINVAL;
+
+ if (file->f_flags & O_NONBLOCK)
+ /* Call videobuf_dqbuf for non blocking mode */
+ ret = videobuf_dqbuf(q, (struct v4l2_buffer *)b, 1);
+ else
+ /* Call videobuf_dqbuf for blocking mode */
+ ret = videobuf_dqbuf(q, (struct v4l2_buffer *)b, 0);
+
+ addr = (unsigned long) vout->buf_phy_addr[vb->i];
+ size = (unsigned long) vb->size;
+ dma_unmap_single(vout->vid_dev->v4l2_dev.dev, addr,
+ size, DMA_TO_DEVICE);
+ return ret;
+}
+
+static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+ int ret = 0, j;
+ u32 addr = 0, mask = 0;
+ struct omap_vout_device *vout = fh;
+ struct videobuf_queue *q = &vout->vbq;
+ struct omapvideo_info *ovid = &vout->vid_info;
+
+ mutex_lock(&vout->lock);
+
+ if (vout->streaming) {
+ ret = -EBUSY;
+ goto streamon_err;
+ }
+
+ ret = videobuf_streamon(q);
+ if (ret)
+ goto streamon_err;
+
+ if (list_empty(&vout->dma_queue)) {
+ ret = -EIO;
+ goto streamon_err1;
+ }
+
+ /* Get the next frame from the buffer queue */
+ vout->next_frm = vout->cur_frm = list_entry(vout->dma_queue.next,
+ struct videobuf_buffer, queue);
+ /* Remove buffer from the buffer queue */
+ list_del(&vout->cur_frm->queue);
+ /* Mark state of the current frame to active */
+ vout->cur_frm->state = VIDEOBUF_ACTIVE;
+ /* Initialize field_id and started member */
+ vout->field_id = 0;
+
+ /* set flag here. Next QBUF will start DMA */
+ vout->streaming = 1;
+
+ vout->first_int = 1;
+
+ if (omap_vout_calculate_offset(vout)) {
+ ret = -EINVAL;
+ goto streamon_err1;
+ }
+ addr = (unsigned long) vout->queued_buf_addr[vout->cur_frm->i]
+ + vout->cropped_offset;
+
+ mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD
+ | DISPC_IRQ_VSYNC2;
+
+ omap_dispc_register_isr(omap_vout_isr, vout, mask);
+
+ for (j = 0; j < ovid->num_overlays; j++) {
+ struct omap_overlay *ovl = ovid->overlays[j];
+
+ if (ovl->manager && ovl->manager->device) {
+ struct omap_overlay_info info;
+ ovl->get_overlay_info(ovl, &info);
+ info.paddr = addr;
+ if (ovl->set_overlay_info(ovl, &info)) {
+ ret = -EINVAL;
+ goto streamon_err1;
+ }
+ }
+ }
+
+ /* First save the configuration in ovelray structure */
+ ret = omapvid_init(vout, addr);
+ if (ret)
+ v4l2_err(&vout->vid_dev->v4l2_dev,
+ "failed to set overlay info\n");
+ /* Enable the pipeline and set the Go bit */
+ ret = omapvid_apply_changes(vout);
+ if (ret)
+ v4l2_err(&vout->vid_dev->v4l2_dev, "failed to change mode\n");
+
+ for (j = 0; j < ovid->num_overlays; j++) {
+ struct omap_overlay *ovl = ovid->overlays[j];
+
+ if (ovl->manager && ovl->manager->device) {
+ ret = ovl->enable(ovl);
+ if (ret)
+ goto streamon_err1;
+ }
+ }
+
+ ret = 0;
+
+streamon_err1:
+ if (ret)
+ ret = videobuf_streamoff(q);
+streamon_err:
+ mutex_unlock(&vout->lock);
+ return ret;
+}
+
+static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+ u32 mask = 0;
+ int ret = 0, j;
+ struct omap_vout_device *vout = fh;
+ struct omapvideo_info *ovid = &vout->vid_info;
+
+ if (!vout->streaming)
+ return -EINVAL;
+
+ vout->streaming = 0;
+ mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD
+ | DISPC_IRQ_VSYNC2;
+
+ omap_dispc_unregister_isr(omap_vout_isr, vout, mask);
+
+ for (j = 0; j < ovid->num_overlays; j++) {
+ struct omap_overlay *ovl = ovid->overlays[j];
+
+ if (ovl->manager && ovl->manager->device)
+ ovl->disable(ovl);
+ }
+
+ /* Turn of the pipeline */
+ ret = omapvid_apply_changes(vout);
+ if (ret)
+ v4l2_err(&vout->vid_dev->v4l2_dev, "failed to change mode in"
+ " streamoff\n");
+
+ INIT_LIST_HEAD(&vout->dma_queue);
+ ret = videobuf_streamoff(&vout->vbq);
+
+ return ret;
+}
+
+static int vidioc_s_fbuf(struct file *file, void *fh,
+ struct v4l2_framebuffer *a)
+{
+ int enable = 0;
+ struct omap_overlay *ovl;
+ struct omapvideo_info *ovid;
+ struct omap_vout_device *vout = fh;
+ struct omap_overlay_manager_info info;
+ enum omap_dss_trans_key_type key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
+
+ ovid = &vout->vid_info;
+ ovl = ovid->overlays[0];
+
+ /* OMAP DSS doesn't support Source and Destination color
+ key together */
+ if ((a->flags & V4L2_FBUF_FLAG_SRC_CHROMAKEY) &&
+ (a->flags & V4L2_FBUF_FLAG_CHROMAKEY))
+ return -EINVAL;
+ /* OMAP DSS Doesn't support the Destination color key
+ and alpha blending together */
+ if ((a->flags & V4L2_FBUF_FLAG_CHROMAKEY) &&
+ (a->flags & V4L2_FBUF_FLAG_LOCAL_ALPHA))
+ return -EINVAL;
+
+ if ((a->flags & V4L2_FBUF_FLAG_SRC_CHROMAKEY)) {
+ vout->fbuf.flags |= V4L2_FBUF_FLAG_SRC_CHROMAKEY;
+ key_type = OMAP_DSS_COLOR_KEY_VID_SRC;
+ } else
+ vout->fbuf.flags &= ~V4L2_FBUF_FLAG_SRC_CHROMAKEY;
+
+ if ((a->flags & V4L2_FBUF_FLAG_CHROMAKEY)) {
+ vout->fbuf.flags |= V4L2_FBUF_FLAG_CHROMAKEY;
+ key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
+ } else
+ vout->fbuf.flags &= ~V4L2_FBUF_FLAG_CHROMAKEY;
+
+ if (a->flags & (V4L2_FBUF_FLAG_CHROMAKEY |
+ V4L2_FBUF_FLAG_SRC_CHROMAKEY))
+ enable = 1;
+ else
+ enable = 0;
+ if (ovl->manager && ovl->manager->get_manager_info &&
+ ovl->manager->set_manager_info) {
+
+ ovl->manager->get_manager_info(ovl->manager, &info);
+ info.trans_enabled = enable;
+ info.trans_key_type = key_type;
+ info.trans_key = vout->win.chromakey;
+
+ if (ovl->manager->set_manager_info(ovl->manager, &info))
+ return -EINVAL;
+ }
+ if (a->flags & V4L2_FBUF_FLAG_LOCAL_ALPHA) {
+ vout->fbuf.flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA;
+ enable = 1;
+ } else {
+ vout->fbuf.flags &= ~V4L2_FBUF_FLAG_LOCAL_ALPHA;
+ enable = 0;
+ }
+ if (ovl->manager && ovl->manager->get_manager_info &&
+ ovl->manager->set_manager_info) {
+ ovl->manager->get_manager_info(ovl->manager, &info);
+ /* enable this only if there is no zorder cap */
+ if ((ovl->caps & OMAP_DSS_OVL_CAP_ZORDER) == 0)
+ info.partial_alpha_enabled = enable;
+ if (ovl->manager->set_manager_info(ovl->manager, &info))
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vidioc_g_fbuf(struct file *file, void *fh,
+ struct v4l2_framebuffer *a)
+{
+ struct omap_overlay *ovl;
+ struct omapvideo_info *ovid;
+ struct omap_vout_device *vout = fh;
+ struct omap_overlay_manager_info info;
+
+ ovid = &vout->vid_info;
+ ovl = ovid->overlays[0];
+
+ /* The video overlay must stay within the framebuffer and can't be
+ positioned independently. */
+ a->flags = V4L2_FBUF_FLAG_OVERLAY;
+ a->capability = V4L2_FBUF_CAP_LOCAL_ALPHA | V4L2_FBUF_CAP_CHROMAKEY
+ | V4L2_FBUF_CAP_SRC_CHROMAKEY;
+
+ if (ovl->manager && ovl->manager->get_manager_info) {
+ ovl->manager->get_manager_info(ovl->manager, &info);
+ if (info.trans_key_type == OMAP_DSS_COLOR_KEY_VID_SRC)
+ a->flags |= V4L2_FBUF_FLAG_SRC_CHROMAKEY;
+ if (info.trans_key_type == OMAP_DSS_COLOR_KEY_GFX_DST)
+ a->flags |= V4L2_FBUF_FLAG_CHROMAKEY;
+ }
+ if (ovl->manager && ovl->manager->get_manager_info) {
+ ovl->manager->get_manager_info(ovl->manager, &info);
+ if (info.partial_alpha_enabled)
+ a->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops vout_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
+ .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out,
+ .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out,
+ .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_fbuf = vidioc_s_fbuf,
+ .vidioc_g_fbuf = vidioc_g_fbuf,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay,
+ .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay,
+ .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay,
+ .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay,
+ .vidioc_cropcap = vidioc_cropcap,
+ .vidioc_g_crop = vidioc_g_crop,
+ .vidioc_s_crop = vidioc_s_crop,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+};
+
+static const struct v4l2_file_operations omap_vout_fops = {
+ .owner = THIS_MODULE,
+ .poll = omap_vout_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = omap_vout_mmap,
+ .open = omap_vout_open,
+ .release = omap_vout_release,
+};
+
+/* Init functions used during driver initialization */
+/* Initial setup of video_data */
+static int __init omap_vout_setup_video_data(struct omap_vout_device *vout)
+{
+ struct video_device *vfd;
+ struct v4l2_pix_format *pix;
+ struct v4l2_control *control;
+ struct omap_dss_device *display =
+ vout->vid_info.overlays[0]->manager->device;
+
+ /* set the default pix */
+ pix = &vout->pix;
+
+ /* Set the default picture of QVGA */
+ pix->width = QQVGA_WIDTH;
+ pix->height = QQVGA_HEIGHT;
+
+ /* Default pixel format is RGB 5-6-5 */
+ pix->pixelformat = V4L2_PIX_FMT_RGB565;
+ pix->field = V4L2_FIELD_ANY;
+ pix->bytesperline = pix->width * 2;
+ pix->sizeimage = pix->bytesperline * pix->height;
+ pix->priv = 0;
+ pix->colorspace = V4L2_COLORSPACE_JPEG;
+
+ vout->bpp = RGB565_BPP;
+ vout->fbuf.fmt.width = display->panel.timings.x_res;
+ vout->fbuf.fmt.height = display->panel.timings.y_res;
+
+ /* Set the data structures for the overlay parameters*/
+ vout->win.global_alpha = 255;
+ vout->fbuf.flags = 0;
+ vout->fbuf.capability = V4L2_FBUF_CAP_LOCAL_ALPHA |
+ V4L2_FBUF_CAP_SRC_CHROMAKEY | V4L2_FBUF_CAP_CHROMAKEY;
+ vout->win.chromakey = 0;
+
+ omap_vout_new_format(pix, &vout->fbuf, &vout->crop, &vout->win);
+
+ /*Initialize the control variables for
+ rotation, flipping and background color. */
+ control = vout->control;
+ control[0].id = V4L2_CID_ROTATE;
+ control[0].value = 0;
+ vout->rotation = 0;
+ vout->mirror = 0;
+ vout->control[2].id = V4L2_CID_HFLIP;
+ vout->control[2].value = 0;
+ if (vout->vid_info.rotation_type == VOUT_ROT_VRFB)
+ vout->vrfb_bpp = 2;
+
+ control[1].id = V4L2_CID_BG_COLOR;
+ control[1].value = 0;
+
+ /* initialize the video_device struct */
+ vfd = vout->vfd = video_device_alloc();
+
+ if (!vfd) {
+ printk(KERN_ERR VOUT_NAME ": could not allocate"
+ " video device struct\n");
+ return -ENOMEM;
+ }
+ vfd->release = video_device_release;
+ vfd->ioctl_ops = &vout_ioctl_ops;
+
+ strlcpy(vfd->name, VOUT_NAME, sizeof(vfd->name));
+
+ vfd->fops = &omap_vout_fops;
+ vfd->v4l2_dev = &vout->vid_dev->v4l2_dev;
+ mutex_init(&vout->lock);
+
+ vfd->minor = -1;
+ return 0;
+
+}
+
+/* Setup video buffers */
+static int __init omap_vout_setup_video_bufs(struct platform_device *pdev,
+ int vid_num)
+{
+ u32 numbuffers;
+ int ret = 0, i;
+ struct omapvideo_info *ovid;
+ struct omap_vout_device *vout;
+ struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
+ struct omap2video_device *vid_dev =
+ container_of(v4l2_dev, struct omap2video_device, v4l2_dev);
+
+ vout = vid_dev->vouts[vid_num];
+ ovid = &vout->vid_info;
+
+ numbuffers = (vid_num == 0) ? video1_numbuffers : video2_numbuffers;
+ vout->buffer_size = (vid_num == 0) ? video1_bufsize : video2_bufsize;
+ dev_info(&pdev->dev, "Buffer Size = %d\n", vout->buffer_size);
+
+ for (i = 0; i < numbuffers; i++) {
+ vout->buf_virt_addr[i] =
+ omap_vout_alloc_buffer(vout->buffer_size,
+ (u32 *) &vout->buf_phy_addr[i]);
+ if (!vout->buf_virt_addr[i]) {
+ numbuffers = i;
+ ret = -ENOMEM;
+ goto free_buffers;
+ }
+ }
+
+ vout->cropped_offset = 0;
+
+ if (ovid->rotation_type == VOUT_ROT_VRFB) {
+ int static_vrfb_allocation = (vid_num == 0) ?
+ vid1_static_vrfb_alloc : vid2_static_vrfb_alloc;
+ ret = omap_vout_setup_vrfb_bufs(pdev, vid_num,
+ static_vrfb_allocation);
+ }
+
+ return ret;
+
+free_buffers:
+ for (i = 0; i < numbuffers; i++) {
+ omap_vout_free_buffer(vout->buf_virt_addr[i],
+ vout->buffer_size);
+ vout->buf_virt_addr[i] = 0;
+ vout->buf_phy_addr[i] = 0;
+ }
+ return ret;
+
+}
+
+/* Create video out devices */
+static int __init omap_vout_create_video_devices(struct platform_device *pdev)
+{
+ int ret = 0, k;
+ struct omap_vout_device *vout;
+ struct video_device *vfd = NULL;
+ struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
+ struct omap2video_device *vid_dev = container_of(v4l2_dev,
+ struct omap2video_device, v4l2_dev);
+
+ for (k = 0; k < pdev->num_resources; k++) {
+
+ vout = kzalloc(sizeof(struct omap_vout_device), GFP_KERNEL);
+ if (!vout) {
+ dev_err(&pdev->dev, ": could not allocate memory\n");
+ return -ENOMEM;
+ }
+
+ vout->vid = k;
+ vid_dev->vouts[k] = vout;
+ vout->vid_dev = vid_dev;
+ /* Select video2 if only 1 overlay is controlled by V4L2 */
+ if (pdev->num_resources == 1)
+ vout->vid_info.overlays[0] = vid_dev->overlays[k + 2];
+ else
+ /* Else select video1 and video2 one by one. */
+ vout->vid_info.overlays[0] = vid_dev->overlays[k + 1];
+ vout->vid_info.num_overlays = 1;
+ vout->vid_info.id = k + 1;
+
+ /* Set VRFB as rotation_type for omap2 and omap3 */
+ if (cpu_is_omap24xx() || cpu_is_omap34xx())
+ vout->vid_info.rotation_type = VOUT_ROT_VRFB;
+
+ /* Setup the default configuration for the video devices
+ */
+ if (omap_vout_setup_video_data(vout) != 0) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ /* Allocate default number of buffers for the video streaming
+ * and reserve the VRFB space for rotation
+ */
+ if (omap_vout_setup_video_bufs(pdev, k) != 0) {
+ ret = -ENOMEM;
+ goto error1;
+ }
+
+ /* Register the Video device with V4L2
+ */
+ vfd = vout->vfd;
+ if (video_register_device(vfd, VFL_TYPE_GRABBER, -1) < 0) {
+ dev_err(&pdev->dev, ": Could not register "
+ "Video for Linux device\n");
+ vfd->minor = -1;
+ ret = -ENODEV;
+ goto error2;
+ }
+ video_set_drvdata(vfd, vout);
+
+ /* Configure the overlay structure */
+ ret = omapvid_init(vid_dev->vouts[k], 0);
+ if (!ret)
+ goto success;
+
+error2:
+ if (vout->vid_info.rotation_type == VOUT_ROT_VRFB)
+ omap_vout_release_vrfb(vout);
+ omap_vout_free_buffers(vout);
+error1:
+ video_device_release(vfd);
+error:
+ kfree(vout);
+ return ret;
+
+success:
+ dev_info(&pdev->dev, ": registered and initialized"
+ " video device %d\n", vfd->minor);
+ if (k == (pdev->num_resources - 1))
+ return 0;
+ }
+
+ return -ENODEV;
+}
+/* Driver functions */
+static void omap_vout_cleanup_device(struct omap_vout_device *vout)
+{
+ struct video_device *vfd;
+ struct omapvideo_info *ovid;
+
+ if (!vout)
+ return;
+
+ vfd = vout->vfd;
+ ovid = &vout->vid_info;
+ if (vfd) {
+ if (!video_is_registered(vfd)) {
+ /*
+ * The device was never registered, so release the
+ * video_device struct directly.
+ */
+ video_device_release(vfd);
+ } else {
+ /*
+ * The unregister function will release the video_device
+ * struct as well as unregistering it.
+ */
+ video_unregister_device(vfd);
+ }
+ }
+ if (ovid->rotation_type == VOUT_ROT_VRFB) {
+ omap_vout_release_vrfb(vout);
+ /* Free the VRFB buffer if allocated
+ * init time
+ */
+ if (vout->vrfb_static_allocation)
+ omap_vout_free_vrfb_buffers(vout);
+ }
+ omap_vout_free_buffers(vout);
+
+ kfree(vout);
+}
+
+static int omap_vout_remove(struct platform_device *pdev)
+{
+ int k;
+ struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
+ struct omap2video_device *vid_dev = container_of(v4l2_dev, struct
+ omap2video_device, v4l2_dev);
+
+ v4l2_device_unregister(v4l2_dev);
+ for (k = 0; k < pdev->num_resources; k++)
+ omap_vout_cleanup_device(vid_dev->vouts[k]);
+
+ for (k = 0; k < vid_dev->num_displays; k++) {
+ if (vid_dev->displays[k]->state != OMAP_DSS_DISPLAY_DISABLED)
+ vid_dev->displays[k]->driver->disable(vid_dev->displays[k]);
+
+ omap_dss_put_device(vid_dev->displays[k]);
+ }
+ kfree(vid_dev);
+ return 0;
+}
+
+static int __init omap_vout_probe(struct platform_device *pdev)
+{
+ int ret = 0, i;
+ struct omap_overlay *ovl;
+ struct omap_dss_device *dssdev = NULL;
+ struct omap_dss_device *def_display;
+ struct omap2video_device *vid_dev = NULL;
+
+ if (pdev->num_resources == 0) {
+ dev_err(&pdev->dev, "probed for an unknown device\n");
+ return -ENODEV;
+ }
+
+ vid_dev = kzalloc(sizeof(struct omap2video_device), GFP_KERNEL);
+ if (vid_dev == NULL)
+ return -ENOMEM;
+
+ vid_dev->num_displays = 0;
+ for_each_dss_dev(dssdev) {
+ omap_dss_get_device(dssdev);
+
+ if (!dssdev->driver) {
+ dev_warn(&pdev->dev, "no driver for display: %s\n",
+ dssdev->name);
+ omap_dss_put_device(dssdev);
+ continue;
+ }
+
+ vid_dev->displays[vid_dev->num_displays++] = dssdev;
+ }
+
+ if (vid_dev->num_displays == 0) {
+ dev_err(&pdev->dev, "no displays\n");
+ ret = -EINVAL;
+ goto probe_err0;
+ }
+
+ vid_dev->num_overlays = omap_dss_get_num_overlays();
+ for (i = 0; i < vid_dev->num_overlays; i++)
+ vid_dev->overlays[i] = omap_dss_get_overlay(i);
+
+ vid_dev->num_managers = omap_dss_get_num_overlay_managers();
+ for (i = 0; i < vid_dev->num_managers; i++)
+ vid_dev->managers[i] = omap_dss_get_overlay_manager(i);
+
+ /* Get the Video1 overlay and video2 overlay.
+ * Setup the Display attached to that overlays
+ */
+ for (i = 1; i < vid_dev->num_overlays; i++) {
+ ovl = omap_dss_get_overlay(i);
+ if (ovl->manager && ovl->manager->device) {
+ def_display = ovl->manager->device;
+ } else {
+ dev_warn(&pdev->dev, "cannot find display\n");
+ def_display = NULL;
+ }
+ if (def_display) {
+ struct omap_dss_driver *dssdrv = def_display->driver;
+
+ ret = dssdrv->enable(def_display);
+ if (ret) {
+ /* Here we are not considering a error
+ * as display may be enabled by frame
+ * buffer driver
+ */
+ dev_warn(&pdev->dev,
+ "'%s' Display already enabled\n",
+ def_display->name);
+ }
+ }
+ }
+
+ if (v4l2_device_register(&pdev->dev, &vid_dev->v4l2_dev) < 0) {
+ dev_err(&pdev->dev, "v4l2_device_register failed\n");
+ ret = -ENODEV;
+ goto probe_err1;
+ }
+
+ ret = omap_vout_create_video_devices(pdev);
+ if (ret)
+ goto probe_err2;
+
+ for (i = 0; i < vid_dev->num_displays; i++) {
+ struct omap_dss_device *display = vid_dev->displays[i];
+
+ if (display->driver->update)
+ display->driver->update(display, 0, 0,
+ display->panel.timings.x_res,
+ display->panel.timings.y_res);
+ }
+ return 0;
+
+probe_err2:
+ v4l2_device_unregister(&vid_dev->v4l2_dev);
+probe_err1:
+ for (i = 1; i < vid_dev->num_overlays; i++) {
+ def_display = NULL;
+ ovl = omap_dss_get_overlay(i);
+ if (ovl->manager && ovl->manager->device)
+ def_display = ovl->manager->device;
+
+ if (def_display && def_display->driver)
+ def_display->driver->disable(def_display);
+ }
+probe_err0:
+ kfree(vid_dev);
+ return ret;
+}
+
+static struct platform_driver omap_vout_driver = {
+ .driver = {
+ .name = VOUT_NAME,
+ },
+ .remove = omap_vout_remove,
+};
+
+static int __init omap_vout_init(void)
+{
+ if (platform_driver_probe(&omap_vout_driver, omap_vout_probe) != 0) {
+ printk(KERN_ERR VOUT_NAME ":Could not register Video driver\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void omap_vout_cleanup(void)
+{
+ platform_driver_unregister(&omap_vout_driver);
+}
+
+late_initcall(omap_vout_init);
+module_exit(omap_vout_cleanup);
diff --git a/drivers/media/video/omap/omap_vout_vrfb.c b/drivers/media/video/omap/omap_vout_vrfb.c
new file mode 100644
index 00000000..4be26abf
--- /dev/null
+++ b/drivers/media/video/omap/omap_vout_vrfb.c
@@ -0,0 +1,390 @@
+/*
+ * omap_vout_vrfb.c
+ *
+ * Copyright (C) 2010 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+
+#include <media/videobuf-dma-contig.h>
+#include <media/v4l2-device.h>
+
+#include <plat/dma.h>
+#include <plat/vrfb.h>
+
+#include "omap_voutdef.h"
+#include "omap_voutlib.h"
+
+/*
+ * Function for allocating video buffers
+ */
+static int omap_vout_allocate_vrfb_buffers(struct omap_vout_device *vout,
+ unsigned int *count, int startindex)
+{
+ int i, j;
+
+ for (i = 0; i < *count; i++) {
+ if (!vout->smsshado_virt_addr[i]) {
+ vout->smsshado_virt_addr[i] =
+ omap_vout_alloc_buffer(vout->smsshado_size,
+ &vout->smsshado_phy_addr[i]);
+ }
+ if (!vout->smsshado_virt_addr[i] && startindex != -1) {
+ if (V4L2_MEMORY_MMAP == vout->memory && i >= startindex)
+ break;
+ }
+ if (!vout->smsshado_virt_addr[i]) {
+ for (j = 0; j < i; j++) {
+ omap_vout_free_buffer(
+ vout->smsshado_virt_addr[j],
+ vout->smsshado_size);
+ vout->smsshado_virt_addr[j] = 0;
+ vout->smsshado_phy_addr[j] = 0;
+ }
+ *count = 0;
+ return -ENOMEM;
+ }
+ memset((void *) vout->smsshado_virt_addr[i], 0,
+ vout->smsshado_size);
+ }
+ return 0;
+}
+
+/*
+ * Wakes up the application once the DMA transfer to VRFB space is completed.
+ */
+static void omap_vout_vrfb_dma_tx_callback(int lch, u16 ch_status, void *data)
+{
+ struct vid_vrfb_dma *t = (struct vid_vrfb_dma *) data;
+
+ t->tx_status = 1;
+ wake_up_interruptible(&t->wait);
+}
+
+/*
+ * Free VRFB buffers
+ */
+void omap_vout_free_vrfb_buffers(struct omap_vout_device *vout)
+{
+ int j;
+
+ for (j = 0; j < VRFB_NUM_BUFS; j++) {
+ omap_vout_free_buffer(vout->smsshado_virt_addr[j],
+ vout->smsshado_size);
+ vout->smsshado_virt_addr[j] = 0;
+ vout->smsshado_phy_addr[j] = 0;
+ }
+}
+
+int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num,
+ bool static_vrfb_allocation)
+{
+ int ret = 0, i, j;
+ struct omap_vout_device *vout;
+ struct video_device *vfd;
+ int image_width, image_height;
+ int vrfb_num_bufs = VRFB_NUM_BUFS;
+ struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
+ struct omap2video_device *vid_dev =
+ container_of(v4l2_dev, struct omap2video_device, v4l2_dev);
+
+ vout = vid_dev->vouts[vid_num];
+ vfd = vout->vfd;
+
+ for (i = 0; i < VRFB_NUM_BUFS; i++) {
+ if (omap_vrfb_request_ctx(&vout->vrfb_context[i])) {
+ dev_info(&pdev->dev, ": VRFB allocation failed\n");
+ for (j = 0; j < i; j++)
+ omap_vrfb_release_ctx(&vout->vrfb_context[j]);
+ ret = -ENOMEM;
+ goto free_buffers;
+ }
+ }
+
+ /* Calculate VRFB memory size */
+ /* allocate for worst case size */
+ image_width = VID_MAX_WIDTH / TILE_SIZE;
+ if (VID_MAX_WIDTH % TILE_SIZE)
+ image_width++;
+
+ image_width = image_width * TILE_SIZE;
+ image_height = VID_MAX_HEIGHT / TILE_SIZE;
+
+ if (VID_MAX_HEIGHT % TILE_SIZE)
+ image_height++;
+
+ image_height = image_height * TILE_SIZE;
+ vout->smsshado_size = PAGE_ALIGN(image_width * image_height * 2 * 2);
+
+ /*
+ * Request and Initialize DMA, for DMA based VRFB transfer
+ */
+ vout->vrfb_dma_tx.dev_id = OMAP_DMA_NO_DEVICE;
+ vout->vrfb_dma_tx.dma_ch = -1;
+ vout->vrfb_dma_tx.req_status = DMA_CHAN_ALLOTED;
+ ret = omap_request_dma(vout->vrfb_dma_tx.dev_id, "VRFB DMA TX",
+ omap_vout_vrfb_dma_tx_callback,
+ (void *) &vout->vrfb_dma_tx, &vout->vrfb_dma_tx.dma_ch);
+ if (ret < 0) {
+ vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED;
+ dev_info(&pdev->dev, ": failed to allocate DMA Channel for"
+ " video%d\n", vfd->minor);
+ }
+ init_waitqueue_head(&vout->vrfb_dma_tx.wait);
+
+ /* statically allocated the VRFB buffer is done through
+ commands line aruments */
+ if (static_vrfb_allocation) {
+ if (omap_vout_allocate_vrfb_buffers(vout, &vrfb_num_bufs, -1)) {
+ ret = -ENOMEM;
+ goto release_vrfb_ctx;
+ }
+ vout->vrfb_static_allocation = 1;
+ }
+ return 0;
+
+release_vrfb_ctx:
+ for (j = 0; j < VRFB_NUM_BUFS; j++)
+ omap_vrfb_release_ctx(&vout->vrfb_context[j]);
+free_buffers:
+ omap_vout_free_buffers(vout);
+
+ return ret;
+}
+
+/*
+ * Release the VRFB context once the module exits
+ */
+void omap_vout_release_vrfb(struct omap_vout_device *vout)
+{
+ int i;
+
+ for (i = 0; i < VRFB_NUM_BUFS; i++)
+ omap_vrfb_release_ctx(&vout->vrfb_context[i]);
+
+ if (vout->vrfb_dma_tx.req_status == DMA_CHAN_ALLOTED) {
+ vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED;
+ omap_free_dma(vout->vrfb_dma_tx.dma_ch);
+ }
+}
+
+/*
+ * Allocate the buffers for the VRFB space. Data is copied from V4L2
+ * buffers to the VRFB buffers using the DMA engine.
+ */
+int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout,
+ unsigned int *count, unsigned int startindex)
+{
+ int i;
+ bool yuv_mode;
+
+ if (!is_rotation_enabled(vout))
+ return 0;
+
+ /* If rotation is enabled, allocate memory for VRFB space also */
+ *count = *count > VRFB_NUM_BUFS ? VRFB_NUM_BUFS : *count;
+
+ /* Allocate the VRFB buffers only if the buffers are not
+ * allocated during init time.
+ */
+ if (!vout->vrfb_static_allocation)
+ if (omap_vout_allocate_vrfb_buffers(vout, count, startindex))
+ return -ENOMEM;
+
+ if (vout->dss_mode == OMAP_DSS_COLOR_YUV2 ||
+ vout->dss_mode == OMAP_DSS_COLOR_UYVY)
+ yuv_mode = true;
+ else
+ yuv_mode = false;
+
+ for (i = 0; i < *count; i++)
+ omap_vrfb_setup(&vout->vrfb_context[i],
+ vout->smsshado_phy_addr[i], vout->pix.width,
+ vout->pix.height, vout->bpp, yuv_mode);
+
+ return 0;
+}
+
+int omap_vout_prepare_vrfb(struct omap_vout_device *vout,
+ struct videobuf_buffer *vb)
+{
+ dma_addr_t dmabuf;
+ struct vid_vrfb_dma *tx;
+ enum dss_rotation rotation;
+ u32 dest_frame_index = 0, src_element_index = 0;
+ u32 dest_element_index = 0, src_frame_index = 0;
+ u32 elem_count = 0, frame_count = 0, pixsize = 2;
+
+ if (!is_rotation_enabled(vout))
+ return 0;
+
+ dmabuf = vout->buf_phy_addr[vb->i];
+ /* If rotation is enabled, copy input buffer into VRFB
+ * memory space using DMA. We are copying input buffer
+ * into VRFB memory space of desired angle and DSS will
+ * read image VRFB memory for 0 degree angle
+ */
+ pixsize = vout->bpp * vout->vrfb_bpp;
+ /*
+ * DMA transfer in double index mode
+ */
+
+ /* Frame index */
+ dest_frame_index = ((MAX_PIXELS_PER_LINE * pixsize) -
+ (vout->pix.width * vout->bpp)) + 1;
+
+ /* Source and destination parameters */
+ src_element_index = 0;
+ src_frame_index = 0;
+ dest_element_index = 1;
+ /* Number of elements per frame */
+ elem_count = vout->pix.width * vout->bpp;
+ frame_count = vout->pix.height;
+ tx = &vout->vrfb_dma_tx;
+ tx->tx_status = 0;
+ omap_set_dma_transfer_params(tx->dma_ch, OMAP_DMA_DATA_TYPE_S32,
+ (elem_count / 4), frame_count, OMAP_DMA_SYNC_ELEMENT,
+ tx->dev_id, 0x0);
+ /* src_port required only for OMAP1 */
+ omap_set_dma_src_params(tx->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
+ dmabuf, src_element_index, src_frame_index);
+ /*set dma source burst mode for VRFB */
+ omap_set_dma_src_burst_mode(tx->dma_ch, OMAP_DMA_DATA_BURST_16);
+ rotation = calc_rotation(vout);
+
+ /* dest_port required only for OMAP1 */
+ omap_set_dma_dest_params(tx->dma_ch, 0, OMAP_DMA_AMODE_DOUBLE_IDX,
+ vout->vrfb_context[vb->i].paddr[0], dest_element_index,
+ dest_frame_index);
+ /*set dma dest burst mode for VRFB */
+ omap_set_dma_dest_burst_mode(tx->dma_ch, OMAP_DMA_DATA_BURST_16);
+ omap_dma_set_global_params(DMA_DEFAULT_ARB_RATE, 0x20, 0);
+
+ omap_start_dma(tx->dma_ch);
+ interruptible_sleep_on_timeout(&tx->wait, VRFB_TX_TIMEOUT);
+
+ if (tx->tx_status == 0) {
+ omap_stop_dma(tx->dma_ch);
+ return -EINVAL;
+ }
+ /* Store buffers physical address into an array. Addresses
+ * from this array will be used to configure DSS */
+ vout->queued_buf_addr[vb->i] = (u8 *)
+ vout->vrfb_context[vb->i].paddr[rotation];
+ return 0;
+}
+
+/*
+ * Calculate the buffer offsets from which the streaming should
+ * start. This offset calculation is mainly required because of
+ * the VRFB 32 pixels alignment with rotation.
+ */
+void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout)
+{
+ enum dss_rotation rotation;
+ bool mirroring = vout->mirror;
+ struct v4l2_rect *crop = &vout->crop;
+ struct v4l2_pix_format *pix = &vout->pix;
+ int *cropped_offset = &vout->cropped_offset;
+ int vr_ps = 1, ps = 2, temp_ps = 2;
+ int offset = 0, ctop = 0, cleft = 0, line_length = 0;
+
+ rotation = calc_rotation(vout);
+
+ if (V4L2_PIX_FMT_YUYV == pix->pixelformat ||
+ V4L2_PIX_FMT_UYVY == pix->pixelformat) {
+ if (is_rotation_enabled(vout)) {
+ /*
+ * ps - Actual pixel size for YUYV/UYVY for
+ * VRFB/Mirroring is 4 bytes
+ * vr_ps - Virtually pixel size for YUYV/UYVY is
+ * 2 bytes
+ */
+ ps = 4;
+ vr_ps = 2;
+ } else {
+ ps = 2; /* otherwise the pixel size is 2 byte */
+ }
+ } else if (V4L2_PIX_FMT_RGB32 == pix->pixelformat) {
+ ps = 4;
+ } else if (V4L2_PIX_FMT_RGB24 == pix->pixelformat) {
+ ps = 3;
+ }
+ vout->ps = ps;
+ vout->vr_ps = vr_ps;
+
+ if (is_rotation_enabled(vout)) {
+ line_length = MAX_PIXELS_PER_LINE;
+ ctop = (pix->height - crop->height) - crop->top;
+ cleft = (pix->width - crop->width) - crop->left;
+ } else {
+ line_length = pix->width;
+ }
+ vout->line_length = line_length;
+ switch (rotation) {
+ case dss_rotation_90_degree:
+ offset = vout->vrfb_context[0].yoffset *
+ vout->vrfb_context[0].bytespp;
+ temp_ps = ps / vr_ps;
+ if (mirroring == 0) {
+ *cropped_offset = offset + line_length *
+ temp_ps * cleft + crop->top * temp_ps;
+ } else {
+ *cropped_offset = offset + line_length * temp_ps *
+ cleft + crop->top * temp_ps + (line_length *
+ ((crop->width / (vr_ps)) - 1) * ps);
+ }
+ break;
+ case dss_rotation_180_degree:
+ offset = ((MAX_PIXELS_PER_LINE * vout->vrfb_context[0].yoffset *
+ vout->vrfb_context[0].bytespp) +
+ (vout->vrfb_context[0].xoffset *
+ vout->vrfb_context[0].bytespp));
+ if (mirroring == 0) {
+ *cropped_offset = offset + (line_length * ps * ctop) +
+ (cleft / vr_ps) * ps;
+
+ } else {
+ *cropped_offset = offset + (line_length * ps * ctop) +
+ (cleft / vr_ps) * ps + (line_length *
+ (crop->height - 1) * ps);
+ }
+ break;
+ case dss_rotation_270_degree:
+ offset = MAX_PIXELS_PER_LINE * vout->vrfb_context[0].xoffset *
+ vout->vrfb_context[0].bytespp;
+ temp_ps = ps / vr_ps;
+ if (mirroring == 0) {
+ *cropped_offset = offset + line_length *
+ temp_ps * crop->left + ctop * ps;
+ } else {
+ *cropped_offset = offset + line_length *
+ temp_ps * crop->left + ctop * ps +
+ (line_length * ((crop->width / vr_ps) - 1) *
+ ps);
+ }
+ break;
+ case dss_rotation_0_degree:
+ if (mirroring == 0) {
+ *cropped_offset = (line_length * ps) *
+ crop->top + (crop->left / vr_ps) * ps;
+ } else {
+ *cropped_offset = (line_length * ps) *
+ crop->top + (crop->left / vr_ps) * ps +
+ (line_length * (crop->height - 1) * ps);
+ }
+ break;
+ default:
+ *cropped_offset = (line_length * ps * crop->top) /
+ vr_ps + (crop->left * ps) / vr_ps +
+ ((crop->width / vr_ps) - 1) * ps;
+ break;
+ }
+}
diff --git a/drivers/media/video/omap/omap_vout_vrfb.h b/drivers/media/video/omap/omap_vout_vrfb.h
new file mode 100644
index 00000000..ffde741e
--- /dev/null
+++ b/drivers/media/video/omap/omap_vout_vrfb.h
@@ -0,0 +1,40 @@
+/*
+ * omap_vout_vrfb.h
+ *
+ * Copyright (C) 2010 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ */
+
+#ifndef OMAP_VOUT_VRFB_H
+#define OMAP_VOUT_VRFB_H
+
+#ifdef CONFIG_VIDEO_OMAP2_VOUT_VRFB
+void omap_vout_free_vrfb_buffers(struct omap_vout_device *vout);
+int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num,
+ u32 static_vrfb_allocation);
+void omap_vout_release_vrfb(struct omap_vout_device *vout);
+int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout,
+ unsigned int *count, unsigned int startindex);
+int omap_vout_prepare_vrfb(struct omap_vout_device *vout,
+ struct videobuf_buffer *vb);
+void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout);
+#else
+void omap_vout_free_vrfb_buffers(struct omap_vout_device *vout) { }
+int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num,
+ u32 static_vrfb_allocation)
+ { return 0; }
+void omap_vout_release_vrfb(struct omap_vout_device *vout) { }
+int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout,
+ unsigned int *count, unsigned int startindex)
+ { return 0; }
+int omap_vout_prepare_vrfb(struct omap_vout_device *vout,
+ struct videobuf_buffer *vb)
+ { return 0; }
+void omap_vout_calculate_vrfb_offset(struct omap_vout_device *vout) { }
+#endif
+
+#endif
diff --git a/drivers/media/video/omap/omap_voutdef.h b/drivers/media/video/omap/omap_voutdef.h
new file mode 100644
index 00000000..27a95d23
--- /dev/null
+++ b/drivers/media/video/omap/omap_voutdef.h
@@ -0,0 +1,225 @@
+/*
+ * omap_voutdef.h
+ *
+ * Copyright (C) 2010 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#ifndef OMAP_VOUTDEF_H
+#define OMAP_VOUTDEF_H
+
+#include <video/omapdss.h>
+#include <plat/vrfb.h>
+
+#define YUYV_BPP 2
+#define RGB565_BPP 2
+#define RGB24_BPP 3
+#define RGB32_BPP 4
+#define TILE_SIZE 32
+#define YUYV_VRFB_BPP 2
+#define RGB_VRFB_BPP 1
+#define MAX_CID 3
+#define MAC_VRFB_CTXS 4
+#define MAX_VOUT_DEV 2
+#define MAX_OVLS 3
+#define MAX_DISPLAYS 10
+#define MAX_MANAGERS 3
+
+#define QQVGA_WIDTH 160
+#define QQVGA_HEIGHT 120
+
+/* Max Resolution supported by the driver */
+#define VID_MAX_WIDTH 1280 /* Largest width */
+#define VID_MAX_HEIGHT 720 /* Largest height */
+
+/* Mimimum requirement is 2x2 for DSS */
+#define VID_MIN_WIDTH 2
+#define VID_MIN_HEIGHT 2
+
+/* 2048 x 2048 is max res supported by OMAP display controller */
+#define MAX_PIXELS_PER_LINE 2048
+
+#define VRFB_TX_TIMEOUT 1000
+#define VRFB_NUM_BUFS 4
+
+/* Max buffer size tobe allocated during init */
+#define OMAP_VOUT_MAX_BUF_SIZE (VID_MAX_WIDTH*VID_MAX_HEIGHT*4)
+
+enum dma_channel_state {
+ DMA_CHAN_NOT_ALLOTED,
+ DMA_CHAN_ALLOTED,
+};
+
+/* Enum for Rotation
+ * DSS understands rotation in 0, 1, 2, 3 context
+ * while V4L2 driver understands it as 0, 90, 180, 270
+ */
+enum dss_rotation {
+ dss_rotation_0_degree = 0,
+ dss_rotation_90_degree = 1,
+ dss_rotation_180_degree = 2,
+ dss_rotation_270_degree = 3,
+};
+
+/* Enum for choosing rotation type for vout
+ * DSS2 doesn't understand no rotation as an
+ * option while V4L2 driver doesn't support
+ * rotation in the case where VRFB is not built in
+ * the kernel
+ */
+enum vout_rotaion_type {
+ VOUT_ROT_NONE = 0,
+ VOUT_ROT_VRFB = 1,
+};
+
+/*
+ * This structure is used to store the DMA transfer parameters
+ * for VRFB hidden buffer
+ */
+struct vid_vrfb_dma {
+ int dev_id;
+ int dma_ch;
+ int req_status;
+ int tx_status;
+ wait_queue_head_t wait;
+};
+
+struct omapvideo_info {
+ int id;
+ int num_overlays;
+ struct omap_overlay *overlays[MAX_OVLS];
+ enum vout_rotaion_type rotation_type;
+};
+
+struct omap2video_device {
+ struct mutex mtx;
+
+ int state;
+
+ struct v4l2_device v4l2_dev;
+ struct omap_vout_device *vouts[MAX_VOUT_DEV];
+
+ int num_displays;
+ struct omap_dss_device *displays[MAX_DISPLAYS];
+ int num_overlays;
+ struct omap_overlay *overlays[MAX_OVLS];
+ int num_managers;
+ struct omap_overlay_manager *managers[MAX_MANAGERS];
+};
+
+/* per-device data structure */
+struct omap_vout_device {
+
+ struct omapvideo_info vid_info;
+ struct video_device *vfd;
+ struct omap2video_device *vid_dev;
+ int vid;
+ int opened;
+
+ /* we don't allow to change image fmt/size once buffer has
+ * been allocated
+ */
+ int buffer_allocated;
+ /* allow to reuse previously allocated buffer which is big enough */
+ int buffer_size;
+ /* keep buffer info across opens */
+ unsigned long buf_virt_addr[VIDEO_MAX_FRAME];
+ unsigned long buf_phy_addr[VIDEO_MAX_FRAME];
+ enum omap_color_mode dss_mode;
+
+ /* we don't allow to request new buffer when old buffers are
+ * still mmaped
+ */
+ int mmap_count;
+
+ spinlock_t vbq_lock; /* spinlock for videobuf queues */
+ unsigned long field_count; /* field counter for videobuf_buffer */
+
+ /* non-NULL means streaming is in progress. */
+ bool streaming;
+
+ struct v4l2_pix_format pix;
+ struct v4l2_rect crop;
+ struct v4l2_window win;
+ struct v4l2_framebuffer fbuf;
+
+ /* Lock to protect the shared data structures in ioctl */
+ struct mutex lock;
+
+ /* V4L2 control structure for different control id */
+ struct v4l2_control control[MAX_CID];
+ enum dss_rotation rotation;
+ bool mirror;
+ int flicker_filter;
+ /* V4L2 control structure for different control id */
+
+ int bpp; /* bytes per pixel */
+ int vrfb_bpp; /* bytes per pixel with respect to VRFB */
+
+ struct vid_vrfb_dma vrfb_dma_tx;
+ unsigned int smsshado_phy_addr[MAC_VRFB_CTXS];
+ unsigned int smsshado_virt_addr[MAC_VRFB_CTXS];
+ struct vrfb vrfb_context[MAC_VRFB_CTXS];
+ bool vrfb_static_allocation;
+ unsigned int smsshado_size;
+ unsigned char pos;
+
+ int ps, vr_ps, line_length, first_int, field_id;
+ enum v4l2_memory memory;
+ struct videobuf_buffer *cur_frm, *next_frm;
+ struct list_head dma_queue;
+ u8 *queued_buf_addr[VIDEO_MAX_FRAME];
+ u32 cropped_offset;
+ s32 tv_field1_offset;
+ void *isr_handle;
+
+ /* Buffer queue variables */
+ struct omap_vout_device *vout;
+ enum v4l2_buf_type type;
+ struct videobuf_queue vbq;
+ int io_allowed;
+
+};
+
+/*
+ * Return true if rotation is 90 or 270
+ */
+static inline int is_rotation_90_or_270(const struct omap_vout_device *vout)
+{
+ return (vout->rotation == dss_rotation_90_degree ||
+ vout->rotation == dss_rotation_270_degree);
+}
+
+/*
+ * Return true if rotation is enabled
+ */
+static inline int is_rotation_enabled(const struct omap_vout_device *vout)
+{
+ return vout->rotation || vout->mirror;
+}
+
+/*
+ * Reverse the rotation degree if mirroring is enabled
+ */
+static inline int calc_rotation(const struct omap_vout_device *vout)
+{
+ if (!vout->mirror)
+ return vout->rotation;
+
+ switch (vout->rotation) {
+ case dss_rotation_90_degree:
+ return dss_rotation_270_degree;
+ case dss_rotation_270_degree:
+ return dss_rotation_90_degree;
+ case dss_rotation_180_degree:
+ return dss_rotation_0_degree;
+ default:
+ return dss_rotation_180_degree;
+ }
+}
+
+void omap_vout_free_buffers(struct omap_vout_device *vout);
+#endif /* ifndef OMAP_VOUTDEF_H */
diff --git a/drivers/media/video/omap/omap_voutlib.c b/drivers/media/video/omap/omap_voutlib.c
new file mode 100644
index 00000000..115408b9
--- /dev/null
+++ b/drivers/media/video/omap/omap_voutlib.c
@@ -0,0 +1,339 @@
+/*
+ * omap_voutlib.c
+ *
+ * Copyright (C) 2005-2010 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ * Based on the OMAP2 camera driver
+ * Video-for-Linux (Version 2) camera capture driver for
+ * the OMAP24xx camera controller.
+ *
+ * Author: Andy Lowe (source@mvista.com)
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2010 Texas Instruments.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+#include <linux/dma-mapping.h>
+
+#include <plat/cpu.h>
+
+#include "omap_voutlib.h"
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("OMAP Video library");
+MODULE_LICENSE("GPL");
+
+/* Return the default overlay cropping rectangle in crop given the image
+ * size in pix and the video display size in fbuf. The default
+ * cropping rectangle is the largest rectangle no larger than the capture size
+ * that will fit on the display. The default cropping rectangle is centered in
+ * the image. All dimensions and offsets are rounded down to even numbers.
+ */
+void omap_vout_default_crop(struct v4l2_pix_format *pix,
+ struct v4l2_framebuffer *fbuf, struct v4l2_rect *crop)
+{
+ crop->width = (pix->width < fbuf->fmt.width) ?
+ pix->width : fbuf->fmt.width;
+ crop->height = (pix->height < fbuf->fmt.height) ?
+ pix->height : fbuf->fmt.height;
+ crop->width &= ~1;
+ crop->height &= ~1;
+ crop->left = ((pix->width - crop->width) >> 1) & ~1;
+ crop->top = ((pix->height - crop->height) >> 1) & ~1;
+}
+EXPORT_SYMBOL_GPL(omap_vout_default_crop);
+
+/* Given a new render window in new_win, adjust the window to the
+ * nearest supported configuration. The adjusted window parameters are
+ * returned in new_win.
+ * Returns zero if successful, or -EINVAL if the requested window is
+ * impossible and cannot reasonably be adjusted.
+ */
+int omap_vout_try_window(struct v4l2_framebuffer *fbuf,
+ struct v4l2_window *new_win)
+{
+ struct v4l2_rect try_win;
+
+ /* make a working copy of the new_win rectangle */
+ try_win = new_win->w;
+
+ /* adjust the preview window so it fits on the display by clipping any
+ * offscreen areas
+ */
+ if (try_win.left < 0) {
+ try_win.width += try_win.left;
+ try_win.left = 0;
+ }
+ if (try_win.top < 0) {
+ try_win.height += try_win.top;
+ try_win.top = 0;
+ }
+ try_win.width = (try_win.width < fbuf->fmt.width) ?
+ try_win.width : fbuf->fmt.width;
+ try_win.height = (try_win.height < fbuf->fmt.height) ?
+ try_win.height : fbuf->fmt.height;
+ if (try_win.left + try_win.width > fbuf->fmt.width)
+ try_win.width = fbuf->fmt.width - try_win.left;
+ if (try_win.top + try_win.height > fbuf->fmt.height)
+ try_win.height = fbuf->fmt.height - try_win.top;
+ try_win.width &= ~1;
+ try_win.height &= ~1;
+
+ if (try_win.width <= 0 || try_win.height <= 0)
+ return -EINVAL;
+
+ /* We now have a valid preview window, so go with it */
+ new_win->w = try_win;
+ new_win->field = V4L2_FIELD_ANY;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(omap_vout_try_window);
+
+/* Given a new render window in new_win, adjust the window to the
+ * nearest supported configuration. The image cropping window in crop
+ * will also be adjusted if necessary. Preference is given to keeping the
+ * the window as close to the requested configuration as possible. If
+ * successful, new_win, vout->win, and crop are updated.
+ * Returns zero if successful, or -EINVAL if the requested preview window is
+ * impossible and cannot reasonably be adjusted.
+ */
+int omap_vout_new_window(struct v4l2_rect *crop,
+ struct v4l2_window *win, struct v4l2_framebuffer *fbuf,
+ struct v4l2_window *new_win)
+{
+ int err;
+
+ err = omap_vout_try_window(fbuf, new_win);
+ if (err)
+ return err;
+
+ /* update our preview window */
+ win->w = new_win->w;
+ win->field = new_win->field;
+ win->chromakey = new_win->chromakey;
+
+ /* Adjust the cropping window to allow for resizing limitation */
+ if (cpu_is_omap24xx()) {
+ /* For 24xx limit is 8x to 1/2x scaling. */
+ if ((crop->height/win->w.height) >= 2)
+ crop->height = win->w.height * 2;
+
+ if ((crop->width/win->w.width) >= 2)
+ crop->width = win->w.width * 2;
+
+ if (crop->width > 768) {
+ /* The OMAP2420 vertical resizing line buffer is 768
+ * pixels wide. If the cropped image is wider than
+ * 768 pixels then it cannot be vertically resized.
+ */
+ if (crop->height != win->w.height)
+ crop->width = 768;
+ }
+ } else if (cpu_is_omap34xx()) {
+ /* For 34xx limit is 8x to 1/4x scaling. */
+ if ((crop->height/win->w.height) >= 4)
+ crop->height = win->w.height * 4;
+
+ if ((crop->width/win->w.width) >= 4)
+ crop->width = win->w.width * 4;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(omap_vout_new_window);
+
+/* Given a new cropping rectangle in new_crop, adjust the cropping rectangle to
+ * the nearest supported configuration. The image render window in win will
+ * also be adjusted if necessary. The preview window is adjusted such that the
+ * horizontal and vertical rescaling ratios stay constant. If the render
+ * window would fall outside the display boundaries, the cropping rectangle
+ * will also be adjusted to maintain the rescaling ratios. If successful, crop
+ * and win are updated.
+ * Returns zero if successful, or -EINVAL if the requested cropping rectangle is
+ * impossible and cannot reasonably be adjusted.
+ */
+int omap_vout_new_crop(struct v4l2_pix_format *pix,
+ struct v4l2_rect *crop, struct v4l2_window *win,
+ struct v4l2_framebuffer *fbuf, const struct v4l2_rect *new_crop)
+{
+ struct v4l2_rect try_crop;
+ unsigned long vresize, hresize;
+
+ /* make a working copy of the new_crop rectangle */
+ try_crop = *new_crop;
+
+ /* adjust the cropping rectangle so it fits in the image */
+ if (try_crop.left < 0) {
+ try_crop.width += try_crop.left;
+ try_crop.left = 0;
+ }
+ if (try_crop.top < 0) {
+ try_crop.height += try_crop.top;
+ try_crop.top = 0;
+ }
+ try_crop.width = (try_crop.width < pix->width) ?
+ try_crop.width : pix->width;
+ try_crop.height = (try_crop.height < pix->height) ?
+ try_crop.height : pix->height;
+ if (try_crop.left + try_crop.width > pix->width)
+ try_crop.width = pix->width - try_crop.left;
+ if (try_crop.top + try_crop.height > pix->height)
+ try_crop.height = pix->height - try_crop.top;
+
+ try_crop.width &= ~1;
+ try_crop.height &= ~1;
+
+ if (try_crop.width <= 0 || try_crop.height <= 0)
+ return -EINVAL;
+
+ if (cpu_is_omap24xx()) {
+ if (try_crop.height != win->w.height) {
+ /* If we're resizing vertically, we can't support a
+ * crop width wider than 768 pixels.
+ */
+ if (try_crop.width > 768)
+ try_crop.width = 768;
+ }
+ }
+ /* vertical resizing */
+ vresize = (1024 * try_crop.height) / win->w.height;
+ if (cpu_is_omap24xx() && (vresize > 2048))
+ vresize = 2048;
+ else if (cpu_is_omap34xx() && (vresize > 4096))
+ vresize = 4096;
+
+ win->w.height = ((1024 * try_crop.height) / vresize) & ~1;
+ if (win->w.height == 0)
+ win->w.height = 2;
+ if (win->w.height + win->w.top > fbuf->fmt.height) {
+ /* We made the preview window extend below the bottom of the
+ * display, so clip it to the display boundary and resize the
+ * cropping height to maintain the vertical resizing ratio.
+ */
+ win->w.height = (fbuf->fmt.height - win->w.top) & ~1;
+ if (try_crop.height == 0)
+ try_crop.height = 2;
+ }
+ /* horizontal resizing */
+ hresize = (1024 * try_crop.width) / win->w.width;
+ if (cpu_is_omap24xx() && (hresize > 2048))
+ hresize = 2048;
+ else if (cpu_is_omap34xx() && (hresize > 4096))
+ hresize = 4096;
+
+ win->w.width = ((1024 * try_crop.width) / hresize) & ~1;
+ if (win->w.width == 0)
+ win->w.width = 2;
+ if (win->w.width + win->w.left > fbuf->fmt.width) {
+ /* We made the preview window extend past the right side of the
+ * display, so clip it to the display boundary and resize the
+ * cropping width to maintain the horizontal resizing ratio.
+ */
+ win->w.width = (fbuf->fmt.width - win->w.left) & ~1;
+ if (try_crop.width == 0)
+ try_crop.width = 2;
+ }
+ if (cpu_is_omap24xx()) {
+ if ((try_crop.height/win->w.height) >= 2)
+ try_crop.height = win->w.height * 2;
+
+ if ((try_crop.width/win->w.width) >= 2)
+ try_crop.width = win->w.width * 2;
+
+ if (try_crop.width > 768) {
+ /* The OMAP2420 vertical resizing line buffer is
+ * 768 pixels wide. If the cropped image is wider
+ * than 768 pixels then it cannot be vertically resized.
+ */
+ if (try_crop.height != win->w.height)
+ try_crop.width = 768;
+ }
+ } else if (cpu_is_omap34xx()) {
+ if ((try_crop.height/win->w.height) >= 4)
+ try_crop.height = win->w.height * 4;
+
+ if ((try_crop.width/win->w.width) >= 4)
+ try_crop.width = win->w.width * 4;
+ }
+ /* update our cropping rectangle and we're done */
+ *crop = try_crop;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(omap_vout_new_crop);
+
+/* Given a new format in pix and fbuf, crop and win
+ * structures are initialized to default values. crop
+ * is initialized to the largest window size that will fit on the display. The
+ * crop window is centered in the image. win is initialized to
+ * the same size as crop and is centered on the display.
+ * All sizes and offsets are constrained to be even numbers.
+ */
+void omap_vout_new_format(struct v4l2_pix_format *pix,
+ struct v4l2_framebuffer *fbuf, struct v4l2_rect *crop,
+ struct v4l2_window *win)
+{
+ /* crop defines the preview source window in the image capture
+ * buffer
+ */
+ omap_vout_default_crop(pix, fbuf, crop);
+
+ /* win defines the preview target window on the display */
+ win->w.width = crop->width;
+ win->w.height = crop->height;
+ win->w.left = ((fbuf->fmt.width - win->w.width) >> 1) & ~1;
+ win->w.top = ((fbuf->fmt.height - win->w.height) >> 1) & ~1;
+}
+EXPORT_SYMBOL_GPL(omap_vout_new_format);
+
+/*
+ * Allocate buffers
+ */
+unsigned long omap_vout_alloc_buffer(u32 buf_size, u32 *phys_addr)
+{
+ u32 order, size;
+ unsigned long virt_addr, addr;
+
+ size = PAGE_ALIGN(buf_size);
+ order = get_order(size);
+ virt_addr = __get_free_pages(GFP_KERNEL, order);
+ addr = virt_addr;
+
+ if (virt_addr) {
+ while (size > 0) {
+ SetPageReserved(virt_to_page(addr));
+ addr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ }
+ *phys_addr = (u32) virt_to_phys((void *) virt_addr);
+ return virt_addr;
+}
+
+/*
+ * Free buffers
+ */
+void omap_vout_free_buffer(unsigned long virtaddr, u32 buf_size)
+{
+ u32 order, size;
+ unsigned long addr = virtaddr;
+
+ size = PAGE_ALIGN(buf_size);
+ order = get_order(size);
+
+ while (size > 0) {
+ ClearPageReserved(virt_to_page(addr));
+ addr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ free_pages((unsigned long) virtaddr, order);
+}
diff --git a/drivers/media/video/omap/omap_voutlib.h b/drivers/media/video/omap/omap_voutlib.h
new file mode 100644
index 00000000..e51750a5
--- /dev/null
+++ b/drivers/media/video/omap/omap_voutlib.h
@@ -0,0 +1,36 @@
+/*
+ * omap_voutlib.h
+ *
+ * Copyright (C) 2010 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ */
+
+#ifndef OMAP_VOUTLIB_H
+#define OMAP_VOUTLIB_H
+
+void omap_vout_default_crop(struct v4l2_pix_format *pix,
+ struct v4l2_framebuffer *fbuf, struct v4l2_rect *crop);
+
+int omap_vout_new_crop(struct v4l2_pix_format *pix,
+ struct v4l2_rect *crop, struct v4l2_window *win,
+ struct v4l2_framebuffer *fbuf,
+ const struct v4l2_rect *new_crop);
+
+int omap_vout_try_window(struct v4l2_framebuffer *fbuf,
+ struct v4l2_window *new_win);
+
+int omap_vout_new_window(struct v4l2_rect *crop,
+ struct v4l2_window *win, struct v4l2_framebuffer *fbuf,
+ struct v4l2_window *new_win);
+
+void omap_vout_new_format(struct v4l2_pix_format *pix,
+ struct v4l2_framebuffer *fbuf, struct v4l2_rect *crop,
+ struct v4l2_window *win);
+unsigned long omap_vout_alloc_buffer(u32 buf_size, u32 *phys_addr);
+void omap_vout_free_buffer(unsigned long virtaddr, u32 buf_size);
+#endif /* #ifndef OMAP_VOUTLIB_H */
+
diff --git a/drivers/media/video/omap1_camera.c b/drivers/media/video/omap1_camera.c
new file mode 100644
index 00000000..c20f5ecd
--- /dev/null
+++ b/drivers/media/video/omap1_camera.c
@@ -0,0 +1,1725 @@
+/*
+ * V4L2 SoC Camera driver for OMAP1 Camera Interface
+ *
+ * Copyright (C) 2010, Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
+ *
+ * Based on V4L2 Driver for i.MXL/i.MXL camera (CSI) host
+ * Copyright (C) 2008, Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
+ * Copyright (C) 2009, Darius Augulis <augulis.darius@gmail.com>
+ *
+ * Based on PXA SoC camera driver
+ * Copyright (C) 2006, Sascha Hauer, Pengutronix
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * Hardware specific bits initialy based on former work by Matt Callow
+ * drivers/media/video/omap/omap1510cam.c
+ * Copyright (C) 2006 Matt Callow
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <media/omap1_camera.h>
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
+#include <media/videobuf-dma-contig.h>
+#include <media/videobuf-dma-sg.h>
+
+#include <plat/dma.h>
+
+
+#define DRIVER_NAME "omap1-camera"
+#define DRIVER_VERSION "0.0.2"
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * OMAP1 Camera Interface registers
+ * ---------------------------------------------------------------------------
+ */
+
+#define REG_CTRLCLOCK 0x00
+#define REG_IT_STATUS 0x04
+#define REG_MODE 0x08
+#define REG_STATUS 0x0C
+#define REG_CAMDATA 0x10
+#define REG_GPIO 0x14
+#define REG_PEAK_COUNTER 0x18
+
+/* CTRLCLOCK bit shifts */
+#define LCLK_EN BIT(7)
+#define DPLL_EN BIT(6)
+#define MCLK_EN BIT(5)
+#define CAMEXCLK_EN BIT(4)
+#define POLCLK BIT(3)
+#define FOSCMOD_SHIFT 0
+#define FOSCMOD_MASK (0x7 << FOSCMOD_SHIFT)
+#define FOSCMOD_12MHz 0x0
+#define FOSCMOD_6MHz 0x2
+#define FOSCMOD_9_6MHz 0x4
+#define FOSCMOD_24MHz 0x5
+#define FOSCMOD_8MHz 0x6
+
+/* IT_STATUS bit shifts */
+#define DATA_TRANSFER BIT(5)
+#define FIFO_FULL BIT(4)
+#define H_DOWN BIT(3)
+#define H_UP BIT(2)
+#define V_DOWN BIT(1)
+#define V_UP BIT(0)
+
+/* MODE bit shifts */
+#define RAZ_FIFO BIT(18)
+#define EN_FIFO_FULL BIT(17)
+#define EN_NIRQ BIT(16)
+#define THRESHOLD_SHIFT 9
+#define THRESHOLD_MASK (0x7f << THRESHOLD_SHIFT)
+#define DMA BIT(8)
+#define EN_H_DOWN BIT(7)
+#define EN_H_UP BIT(6)
+#define EN_V_DOWN BIT(5)
+#define EN_V_UP BIT(4)
+#define ORDERCAMD BIT(3)
+
+#define IRQ_MASK (EN_V_UP | EN_V_DOWN | EN_H_UP | EN_H_DOWN | \
+ EN_NIRQ | EN_FIFO_FULL)
+
+/* STATUS bit shifts */
+#define HSTATUS BIT(1)
+#define VSTATUS BIT(0)
+
+/* GPIO bit shifts */
+#define CAM_RST BIT(0)
+
+/* end of OMAP1 Camera Interface registers */
+
+
+#define SOCAM_BUS_FLAGS (V4L2_MBUS_MASTER | \
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | \
+ V4L2_MBUS_DATA_ACTIVE_HIGH)
+
+
+#define FIFO_SIZE ((THRESHOLD_MASK >> THRESHOLD_SHIFT) + 1)
+#define FIFO_SHIFT __fls(FIFO_SIZE)
+
+#define DMA_BURST_SHIFT (1 + OMAP_DMA_DATA_BURST_4)
+#define DMA_BURST_SIZE (1 << DMA_BURST_SHIFT)
+
+#define DMA_ELEMENT_SHIFT OMAP_DMA_DATA_TYPE_S32
+#define DMA_ELEMENT_SIZE (1 << DMA_ELEMENT_SHIFT)
+
+#define DMA_FRAME_SHIFT_CONTIG (FIFO_SHIFT - 1)
+#define DMA_FRAME_SHIFT_SG DMA_BURST_SHIFT
+
+#define DMA_FRAME_SHIFT(x) ((x) == OMAP1_CAM_DMA_CONTIG ? \
+ DMA_FRAME_SHIFT_CONTIG : \
+ DMA_FRAME_SHIFT_SG)
+#define DMA_FRAME_SIZE(x) (1 << DMA_FRAME_SHIFT(x))
+#define DMA_SYNC OMAP_DMA_SYNC_FRAME
+#define THRESHOLD_LEVEL DMA_FRAME_SIZE
+
+
+#define MAX_VIDEO_MEM 4 /* arbitrary video memory limit in MB */
+
+
+/*
+ * Structures
+ */
+
+/* buffer for one video frame */
+struct omap1_cam_buf {
+ struct videobuf_buffer vb;
+ enum v4l2_mbus_pixelcode code;
+ int inwork;
+ struct scatterlist *sgbuf;
+ int sgcount;
+ int bytes_left;
+ enum videobuf_state result;
+};
+
+struct omap1_cam_dev {
+ struct soc_camera_host soc_host;
+ struct soc_camera_device *icd;
+ struct clk *clk;
+
+ unsigned int irq;
+ void __iomem *base;
+
+ int dma_ch;
+
+ struct omap1_cam_platform_data *pdata;
+ struct resource *res;
+ unsigned long pflags;
+ unsigned long camexclk;
+
+ struct list_head capture;
+
+ /* lock used to protect videobuf */
+ spinlock_t lock;
+
+ /* Pointers to DMA buffers */
+ struct omap1_cam_buf *active;
+ struct omap1_cam_buf *ready;
+
+ enum omap1_cam_vb_mode vb_mode;
+ int (*mmap_mapper)(struct videobuf_queue *q,
+ struct videobuf_buffer *buf,
+ struct vm_area_struct *vma);
+
+ u32 reg_cache[0];
+};
+
+
+static void cam_write(struct omap1_cam_dev *pcdev, u16 reg, u32 val)
+{
+ pcdev->reg_cache[reg / sizeof(u32)] = val;
+ __raw_writel(val, pcdev->base + reg);
+}
+
+static u32 cam_read(struct omap1_cam_dev *pcdev, u16 reg, bool from_cache)
+{
+ return !from_cache ? __raw_readl(pcdev->base + reg) :
+ pcdev->reg_cache[reg / sizeof(u32)];
+}
+
+#define CAM_READ(pcdev, reg) \
+ cam_read(pcdev, REG_##reg, false)
+#define CAM_WRITE(pcdev, reg, val) \
+ cam_write(pcdev, REG_##reg, val)
+#define CAM_READ_CACHE(pcdev, reg) \
+ cam_read(pcdev, REG_##reg, true)
+
+/*
+ * Videobuf operations
+ */
+static int omap1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
+ unsigned int *size)
+{
+ struct soc_camera_device *icd = vq->priv_data;
+ int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct omap1_cam_dev *pcdev = ici->priv;
+
+ if (bytes_per_line < 0)
+ return bytes_per_line;
+
+ *size = bytes_per_line * icd->user_height;
+
+ if (!*count || *count < OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode))
+ *count = OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode);
+
+ if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024)
+ *count = (MAX_VIDEO_MEM * 1024 * 1024) / *size;
+
+ dev_dbg(icd->parent,
+ "%s: count=%d, size=%d\n", __func__, *count, *size);
+
+ return 0;
+}
+
+static void free_buffer(struct videobuf_queue *vq, struct omap1_cam_buf *buf,
+ enum omap1_cam_vb_mode vb_mode)
+{
+ struct videobuf_buffer *vb = &buf->vb;
+
+ BUG_ON(in_interrupt());
+
+ videobuf_waiton(vq, vb, 0, 0);
+
+ if (vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ videobuf_dma_contig_free(vq, vb);
+ } else {
+ struct soc_camera_device *icd = vq->priv_data;
+ struct device *dev = icd->parent;
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+
+ videobuf_dma_unmap(dev, dma);
+ videobuf_dma_free(dma);
+ }
+
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int omap1_videobuf_prepare(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb, enum v4l2_field field)
+{
+ struct soc_camera_device *icd = vq->priv_data;
+ struct omap1_cam_buf *buf = container_of(vb, struct omap1_cam_buf, vb);
+ int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ int ret;
+
+ if (bytes_per_line < 0)
+ return bytes_per_line;
+
+ WARN_ON(!list_empty(&vb->queue));
+
+ BUG_ON(NULL == icd->current_fmt);
+
+ buf->inwork = 1;
+
+ if (buf->code != icd->current_fmt->code || vb->field != field ||
+ vb->width != icd->user_width ||
+ vb->height != icd->user_height) {
+ buf->code = icd->current_fmt->code;
+ vb->width = icd->user_width;
+ vb->height = icd->user_height;
+ vb->field = field;
+ vb->state = VIDEOBUF_NEEDS_INIT;
+ }
+
+ vb->size = bytes_per_line * vb->height;
+
+ if (vb->baddr && vb->bsize < vb->size) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (vb->state == VIDEOBUF_NEEDS_INIT) {
+ ret = videobuf_iolock(vq, vb, NULL);
+ if (ret)
+ goto fail;
+
+ vb->state = VIDEOBUF_PREPARED;
+ }
+ buf->inwork = 0;
+
+ return 0;
+fail:
+ free_buffer(vq, buf, pcdev->vb_mode);
+out:
+ buf->inwork = 0;
+ return ret;
+}
+
+static void set_dma_dest_params(int dma_ch, struct omap1_cam_buf *buf,
+ enum omap1_cam_vb_mode vb_mode)
+{
+ dma_addr_t dma_addr;
+ unsigned int block_size;
+
+ if (vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ dma_addr = videobuf_to_dma_contig(&buf->vb);
+ block_size = buf->vb.size;
+ } else {
+ if (WARN_ON(!buf->sgbuf)) {
+ buf->result = VIDEOBUF_ERROR;
+ return;
+ }
+ dma_addr = sg_dma_address(buf->sgbuf);
+ if (WARN_ON(!dma_addr)) {
+ buf->sgbuf = NULL;
+ buf->result = VIDEOBUF_ERROR;
+ return;
+ }
+ block_size = sg_dma_len(buf->sgbuf);
+ if (WARN_ON(!block_size)) {
+ buf->sgbuf = NULL;
+ buf->result = VIDEOBUF_ERROR;
+ return;
+ }
+ if (unlikely(buf->bytes_left < block_size))
+ block_size = buf->bytes_left;
+ if (WARN_ON(dma_addr & (DMA_FRAME_SIZE(vb_mode) *
+ DMA_ELEMENT_SIZE - 1))) {
+ dma_addr = ALIGN(dma_addr, DMA_FRAME_SIZE(vb_mode) *
+ DMA_ELEMENT_SIZE);
+ block_size &= ~(DMA_FRAME_SIZE(vb_mode) *
+ DMA_ELEMENT_SIZE - 1);
+ }
+ buf->bytes_left -= block_size;
+ buf->sgcount++;
+ }
+
+ omap_set_dma_dest_params(dma_ch,
+ OMAP_DMA_PORT_EMIFF, OMAP_DMA_AMODE_POST_INC, dma_addr, 0, 0);
+ omap_set_dma_transfer_params(dma_ch,
+ OMAP_DMA_DATA_TYPE_S32, DMA_FRAME_SIZE(vb_mode),
+ block_size >> (DMA_FRAME_SHIFT(vb_mode) + DMA_ELEMENT_SHIFT),
+ DMA_SYNC, 0, 0);
+}
+
+static struct omap1_cam_buf *prepare_next_vb(struct omap1_cam_dev *pcdev)
+{
+ struct omap1_cam_buf *buf;
+
+ /*
+ * If there is already a buffer pointed out by the pcdev->ready,
+ * (re)use it, otherwise try to fetch and configure a new one.
+ */
+ buf = pcdev->ready;
+ if (!buf) {
+ if (list_empty(&pcdev->capture))
+ return buf;
+ buf = list_entry(pcdev->capture.next,
+ struct omap1_cam_buf, vb.queue);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ pcdev->ready = buf;
+ list_del_init(&buf->vb.queue);
+ }
+
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ /*
+ * In CONTIG mode, we can safely enter next buffer parameters
+ * into the DMA programming register set after the DMA
+ * has already been activated on the previous buffer
+ */
+ set_dma_dest_params(pcdev->dma_ch, buf, pcdev->vb_mode);
+ } else {
+ /*
+ * In SG mode, the above is not safe since there are probably
+ * a bunch of sgbufs from previous sglist still pending.
+ * Instead, mark the sglist fresh for the upcoming
+ * try_next_sgbuf().
+ */
+ buf->sgbuf = NULL;
+ }
+
+ return buf;
+}
+
+static struct scatterlist *try_next_sgbuf(int dma_ch, struct omap1_cam_buf *buf)
+{
+ struct scatterlist *sgbuf;
+
+ if (likely(buf->sgbuf)) {
+ /* current sglist is active */
+ if (unlikely(!buf->bytes_left)) {
+ /* indicate sglist complete */
+ sgbuf = NULL;
+ } else {
+ /* process next sgbuf */
+ sgbuf = sg_next(buf->sgbuf);
+ if (WARN_ON(!sgbuf)) {
+ buf->result = VIDEOBUF_ERROR;
+ } else if (WARN_ON(!sg_dma_len(sgbuf))) {
+ sgbuf = NULL;
+ buf->result = VIDEOBUF_ERROR;
+ }
+ }
+ buf->sgbuf = sgbuf;
+ } else {
+ /* sglist is fresh, initialize it before using */
+ struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
+
+ sgbuf = dma->sglist;
+ if (!(WARN_ON(!sgbuf))) {
+ buf->sgbuf = sgbuf;
+ buf->sgcount = 0;
+ buf->bytes_left = buf->vb.size;
+ buf->result = VIDEOBUF_DONE;
+ }
+ }
+ if (sgbuf)
+ /*
+ * Put our next sgbuf parameters (address, size)
+ * into the DMA programming register set.
+ */
+ set_dma_dest_params(dma_ch, buf, OMAP1_CAM_DMA_SG);
+
+ return sgbuf;
+}
+
+static void start_capture(struct omap1_cam_dev *pcdev)
+{
+ struct omap1_cam_buf *buf = pcdev->active;
+ u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
+ u32 mode = CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN;
+
+ if (WARN_ON(!buf))
+ return;
+
+ /*
+ * Enable start of frame interrupt, which we will use for activating
+ * our end of frame watchdog when capture actually starts.
+ */
+ mode |= EN_V_UP;
+
+ if (unlikely(ctrlclock & LCLK_EN))
+ /* stop pixel clock before FIFO reset */
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
+ /* reset FIFO */
+ CAM_WRITE(pcdev, MODE, mode | RAZ_FIFO);
+
+ omap_start_dma(pcdev->dma_ch);
+
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) {
+ /*
+ * In SG mode, it's a good moment for fetching next sgbuf
+ * from the current sglist and, if available, already putting
+ * its parameters into the DMA programming register set.
+ */
+ try_next_sgbuf(pcdev->dma_ch, buf);
+ }
+
+ /* (re)enable pixel clock */
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | LCLK_EN);
+ /* release FIFO reset */
+ CAM_WRITE(pcdev, MODE, mode);
+}
+
+static void suspend_capture(struct omap1_cam_dev *pcdev)
+{
+ u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
+
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
+ omap_stop_dma(pcdev->dma_ch);
+}
+
+static void disable_capture(struct omap1_cam_dev *pcdev)
+{
+ u32 mode = CAM_READ_CACHE(pcdev, MODE);
+
+ CAM_WRITE(pcdev, MODE, mode & ~(IRQ_MASK | DMA));
+}
+
+static void omap1_videobuf_queue(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct soc_camera_device *icd = vq->priv_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ struct omap1_cam_buf *buf;
+ u32 mode;
+
+ list_add_tail(&vb->queue, &pcdev->capture);
+ vb->state = VIDEOBUF_QUEUED;
+
+ if (pcdev->active) {
+ /*
+ * Capture in progress, so don't touch pcdev->ready even if
+ * empty. Since the transfer of the DMA programming register set
+ * content to the DMA working register set is done automatically
+ * by the DMA hardware, this can pretty well happen while we
+ * are keeping the lock here. Leave fetching it from the queue
+ * to be done when a next DMA interrupt occures instead.
+ */
+ return;
+ }
+
+ WARN_ON(pcdev->ready);
+
+ buf = prepare_next_vb(pcdev);
+ if (WARN_ON(!buf))
+ return;
+
+ pcdev->active = buf;
+ pcdev->ready = NULL;
+
+ dev_dbg(icd->parent,
+ "%s: capture not active, setup FIFO, start DMA\n", __func__);
+ mode = CAM_READ_CACHE(pcdev, MODE) & ~THRESHOLD_MASK;
+ mode |= THRESHOLD_LEVEL(pcdev->vb_mode) << THRESHOLD_SHIFT;
+ CAM_WRITE(pcdev, MODE, mode | EN_FIFO_FULL | DMA);
+
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) {
+ /*
+ * In SG mode, the above prepare_next_vb() didn't actually
+ * put anything into the DMA programming register set,
+ * so we have to do it now, before activating DMA.
+ */
+ try_next_sgbuf(pcdev->dma_ch, buf);
+ }
+
+ start_capture(pcdev);
+}
+
+static void omap1_videobuf_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct omap1_cam_buf *buf =
+ container_of(vb, struct omap1_cam_buf, vb);
+ struct soc_camera_device *icd = vq->priv_data;
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
+ struct omap1_cam_dev *pcdev = ici->priv;
+
+ switch (vb->state) {
+ case VIDEOBUF_DONE:
+ dev_dbg(dev, "%s (done)\n", __func__);
+ break;
+ case VIDEOBUF_ACTIVE:
+ dev_dbg(dev, "%s (active)\n", __func__);
+ break;
+ case VIDEOBUF_QUEUED:
+ dev_dbg(dev, "%s (queued)\n", __func__);
+ break;
+ case VIDEOBUF_PREPARED:
+ dev_dbg(dev, "%s (prepared)\n", __func__);
+ break;
+ default:
+ dev_dbg(dev, "%s (unknown %d)\n", __func__, vb->state);
+ break;
+ }
+
+ free_buffer(vq, buf, pcdev->vb_mode);
+}
+
+static void videobuf_done(struct omap1_cam_dev *pcdev,
+ enum videobuf_state result)
+{
+ struct omap1_cam_buf *buf = pcdev->active;
+ struct videobuf_buffer *vb;
+ struct device *dev = pcdev->icd->parent;
+
+ if (WARN_ON(!buf)) {
+ suspend_capture(pcdev);
+ disable_capture(pcdev);
+ return;
+ }
+
+ if (result == VIDEOBUF_ERROR)
+ suspend_capture(pcdev);
+
+ vb = &buf->vb;
+ if (waitqueue_active(&vb->done)) {
+ if (!pcdev->ready && result != VIDEOBUF_ERROR) {
+ /*
+ * No next buffer has been entered into the DMA
+ * programming register set on time (could be done only
+ * while the previous DMA interurpt was processed, not
+ * later), so the last DMA block, be it a whole buffer
+ * if in CONTIG or its last sgbuf if in SG mode, is
+ * about to be reused by the just autoreinitialized DMA
+ * engine, and overwritten with next frame data. Best we
+ * can do is stopping the capture as soon as possible,
+ * hopefully before the next frame start.
+ */
+ suspend_capture(pcdev);
+ }
+ vb->state = result;
+ do_gettimeofday(&vb->ts);
+ if (result != VIDEOBUF_ERROR)
+ vb->field_count++;
+ wake_up(&vb->done);
+
+ /* shift in next buffer */
+ buf = pcdev->ready;
+ pcdev->active = buf;
+ pcdev->ready = NULL;
+
+ if (!buf) {
+ /*
+ * No next buffer was ready on time (see above), so
+ * indicate error condition to force capture restart or
+ * stop, depending on next buffer already queued or not.
+ */
+ result = VIDEOBUF_ERROR;
+ prepare_next_vb(pcdev);
+
+ buf = pcdev->ready;
+ pcdev->active = buf;
+ pcdev->ready = NULL;
+ }
+ } else if (pcdev->ready) {
+ /*
+ * In both CONTIG and SG mode, the DMA engine has possibly
+ * been already autoreinitialized with the preprogrammed
+ * pcdev->ready buffer. We can either accept this fact
+ * and just swap the buffers, or provoke an error condition
+ * and restart capture. The former seems less intrusive.
+ */
+ dev_dbg(dev, "%s: nobody waiting on videobuf, swap with next\n",
+ __func__);
+ pcdev->active = pcdev->ready;
+
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) {
+ /*
+ * In SG mode, we have to make sure that the buffer we
+ * are putting back into the pcdev->ready is marked
+ * fresh.
+ */
+ buf->sgbuf = NULL;
+ }
+ pcdev->ready = buf;
+
+ buf = pcdev->active;
+ } else {
+ /*
+ * No next buffer has been entered into
+ * the DMA programming register set on time.
+ */
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ /*
+ * In CONTIG mode, the DMA engine has already been
+ * reinitialized with the current buffer. Best we can do
+ * is not touching it.
+ */
+ dev_dbg(dev,
+ "%s: nobody waiting on videobuf, reuse it\n",
+ __func__);
+ } else {
+ /*
+ * In SG mode, the DMA engine has just been
+ * autoreinitialized with the last sgbuf from the
+ * current list. Restart capture in order to transfer
+ * next frame start into the first sgbuf, not the last
+ * one.
+ */
+ if (result != VIDEOBUF_ERROR) {
+ suspend_capture(pcdev);
+ result = VIDEOBUF_ERROR;
+ }
+ }
+ }
+
+ if (!buf) {
+ dev_dbg(dev, "%s: no more videobufs, stop capture\n", __func__);
+ disable_capture(pcdev);
+ return;
+ }
+
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ /*
+ * In CONTIG mode, the current buffer parameters had already
+ * been entered into the DMA programming register set while the
+ * buffer was fetched with prepare_next_vb(), they may have also
+ * been transferred into the runtime set and already active if
+ * the DMA still running.
+ */
+ } else {
+ /* In SG mode, extra steps are required */
+ if (result == VIDEOBUF_ERROR)
+ /* make sure we (re)use sglist from start on error */
+ buf->sgbuf = NULL;
+
+ /*
+ * In any case, enter the next sgbuf parameters into the DMA
+ * programming register set. They will be used either during
+ * nearest DMA autoreinitialization or, in case of an error,
+ * on DMA startup below.
+ */
+ try_next_sgbuf(pcdev->dma_ch, buf);
+ }
+
+ if (result == VIDEOBUF_ERROR) {
+ dev_dbg(dev, "%s: videobuf error; reset FIFO, restart DMA\n",
+ __func__);
+ start_capture(pcdev);
+ /*
+ * In SG mode, the above also resulted in the next sgbuf
+ * parameters being entered into the DMA programming register
+ * set, making them ready for next DMA autoreinitialization.
+ */
+ }
+
+ /*
+ * Finally, try fetching next buffer.
+ * In CONTIG mode, it will also enter it into the DMA programming
+ * register set, making it ready for next DMA autoreinitialization.
+ */
+ prepare_next_vb(pcdev);
+}
+
+static void dma_isr(int channel, unsigned short status, void *data)
+{
+ struct omap1_cam_dev *pcdev = data;
+ struct omap1_cam_buf *buf = pcdev->active;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pcdev->lock, flags);
+
+ if (WARN_ON(!buf)) {
+ suspend_capture(pcdev);
+ disable_capture(pcdev);
+ goto out;
+ }
+
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ /*
+ * In CONTIG mode, assume we have just managed to collect the
+ * whole frame, hopefully before our end of frame watchdog is
+ * triggered. Then, all we have to do is disabling the watchdog
+ * for this frame, and calling videobuf_done() with success
+ * indicated.
+ */
+ CAM_WRITE(pcdev, MODE,
+ CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN);
+ videobuf_done(pcdev, VIDEOBUF_DONE);
+ } else {
+ /*
+ * In SG mode, we have to process every sgbuf from the current
+ * sglist, one after another.
+ */
+ if (buf->sgbuf) {
+ /*
+ * Current sglist not completed yet, try fetching next
+ * sgbuf, hopefully putting it into the DMA programming
+ * register set, making it ready for next DMA
+ * autoreinitialization.
+ */
+ try_next_sgbuf(pcdev->dma_ch, buf);
+ if (buf->sgbuf)
+ goto out;
+
+ /*
+ * No more sgbufs left in the current sglist. This
+ * doesn't mean that the whole videobuffer is already
+ * complete, but only that the last sgbuf from the
+ * current sglist is about to be filled. It will be
+ * ready on next DMA interrupt, signalled with the
+ * buf->sgbuf set back to NULL.
+ */
+ if (buf->result != VIDEOBUF_ERROR) {
+ /*
+ * Video frame collected without errors so far,
+ * we can prepare for collecting a next one
+ * as soon as DMA gets autoreinitialized
+ * after the current (last) sgbuf is completed.
+ */
+ buf = prepare_next_vb(pcdev);
+ if (!buf)
+ goto out;
+
+ try_next_sgbuf(pcdev->dma_ch, buf);
+ goto out;
+ }
+ }
+ /* end of videobuf */
+ videobuf_done(pcdev, buf->result);
+ }
+
+out:
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+}
+
+static irqreturn_t cam_isr(int irq, void *data)
+{
+ struct omap1_cam_dev *pcdev = data;
+ struct device *dev = pcdev->icd->parent;
+ struct omap1_cam_buf *buf = pcdev->active;
+ u32 it_status;
+ unsigned long flags;
+
+ it_status = CAM_READ(pcdev, IT_STATUS);
+ if (!it_status)
+ return IRQ_NONE;
+
+ spin_lock_irqsave(&pcdev->lock, flags);
+
+ if (WARN_ON(!buf)) {
+ dev_warn(dev, "%s: unhandled camera interrupt, status == %#x\n",
+ __func__, it_status);
+ suspend_capture(pcdev);
+ disable_capture(pcdev);
+ goto out;
+ }
+
+ if (unlikely(it_status & FIFO_FULL)) {
+ dev_warn(dev, "%s: FIFO overflow\n", __func__);
+
+ } else if (it_status & V_DOWN) {
+ /* end of video frame watchdog */
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ /*
+ * In CONTIG mode, the watchdog is disabled with
+ * successful DMA end of block interrupt, and reenabled
+ * on next frame start. If we get here, there is nothing
+ * to check, we must be out of sync.
+ */
+ } else {
+ if (buf->sgcount == 2) {
+ /*
+ * If exactly 2 sgbufs from the next sglist have
+ * been programmed into the DMA engine (the
+ * first one already transferred into the DMA
+ * runtime register set, the second one still
+ * in the programming set), then we are in sync.
+ */
+ goto out;
+ }
+ }
+ dev_notice(dev, "%s: unexpected end of video frame\n",
+ __func__);
+
+ } else if (it_status & V_UP) {
+ u32 mode;
+
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ /*
+ * In CONTIG mode, we need this interrupt every frame
+ * in oredr to reenable our end of frame watchdog.
+ */
+ mode = CAM_READ_CACHE(pcdev, MODE);
+ } else {
+ /*
+ * In SG mode, the below enabled end of frame watchdog
+ * is kept on permanently, so we can turn this one shot
+ * setup off.
+ */
+ mode = CAM_READ_CACHE(pcdev, MODE) & ~EN_V_UP;
+ }
+
+ if (!(mode & EN_V_DOWN)) {
+ /* (re)enable end of frame watchdog interrupt */
+ mode |= EN_V_DOWN;
+ }
+ CAM_WRITE(pcdev, MODE, mode);
+ goto out;
+
+ } else {
+ dev_warn(dev, "%s: unhandled camera interrupt, status == %#x\n",
+ __func__, it_status);
+ goto out;
+ }
+
+ videobuf_done(pcdev, VIDEOBUF_ERROR);
+out:
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static struct videobuf_queue_ops omap1_videobuf_ops = {
+ .buf_setup = omap1_videobuf_setup,
+ .buf_prepare = omap1_videobuf_prepare,
+ .buf_queue = omap1_videobuf_queue,
+ .buf_release = omap1_videobuf_release,
+};
+
+
+/*
+ * SOC Camera host operations
+ */
+
+static void sensor_reset(struct omap1_cam_dev *pcdev, bool reset)
+{
+ /* apply/release camera sensor reset if requested by platform data */
+ if (pcdev->pflags & OMAP1_CAMERA_RST_HIGH)
+ CAM_WRITE(pcdev, GPIO, reset);
+ else if (pcdev->pflags & OMAP1_CAMERA_RST_LOW)
+ CAM_WRITE(pcdev, GPIO, !reset);
+}
+
+/*
+ * The following two functions absolutely depend on the fact, that
+ * there can be only one camera on OMAP1 camera sensor interface
+ */
+static int omap1_cam_add_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ u32 ctrlclock;
+
+ if (pcdev->icd)
+ return -EBUSY;
+
+ clk_enable(pcdev->clk);
+
+ /* setup sensor clock */
+ ctrlclock = CAM_READ(pcdev, CTRLCLOCK);
+ ctrlclock &= ~(CAMEXCLK_EN | MCLK_EN | DPLL_EN);
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+
+ ctrlclock &= ~FOSCMOD_MASK;
+ switch (pcdev->camexclk) {
+ case 6000000:
+ ctrlclock |= CAMEXCLK_EN | FOSCMOD_6MHz;
+ break;
+ case 8000000:
+ ctrlclock |= CAMEXCLK_EN | FOSCMOD_8MHz | DPLL_EN;
+ break;
+ case 9600000:
+ ctrlclock |= CAMEXCLK_EN | FOSCMOD_9_6MHz | DPLL_EN;
+ break;
+ case 12000000:
+ ctrlclock |= CAMEXCLK_EN | FOSCMOD_12MHz;
+ break;
+ case 24000000:
+ ctrlclock |= CAMEXCLK_EN | FOSCMOD_24MHz | DPLL_EN;
+ default:
+ break;
+ }
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~DPLL_EN);
+
+ /* enable internal clock */
+ ctrlclock |= MCLK_EN;
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+
+ sensor_reset(pcdev, false);
+
+ pcdev->icd = icd;
+
+ dev_dbg(icd->parent, "OMAP1 Camera driver attached to camera %d\n",
+ icd->devnum);
+ return 0;
+}
+
+static void omap1_cam_remove_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ u32 ctrlclock;
+
+ BUG_ON(icd != pcdev->icd);
+
+ suspend_capture(pcdev);
+ disable_capture(pcdev);
+
+ sensor_reset(pcdev, true);
+
+ /* disable and release system clocks */
+ ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
+ ctrlclock &= ~(MCLK_EN | DPLL_EN | CAMEXCLK_EN);
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+
+ ctrlclock = (ctrlclock & ~FOSCMOD_MASK) | FOSCMOD_12MHz;
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | MCLK_EN);
+
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~MCLK_EN);
+
+ clk_disable(pcdev->clk);
+
+ pcdev->icd = NULL;
+
+ dev_dbg(icd->parent,
+ "OMAP1 Camera driver detached from camera %d\n", icd->devnum);
+}
+
+/* Duplicate standard formats based on host capability of byte swapping */
+static const struct soc_mbus_lookup omap1_cam_formats[] = {
+{
+ .code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .name = "YUYV",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_VYUY8_2X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .name = "YVYU",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .name = "UYVY",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_YVYU8_2X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .name = "VYUY",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .name = "RGB555",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_RGB555X,
+ .name = "RGB555X",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_RGB565_2X8_BE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .name = "RGB565",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_RGB565_2X8_LE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+ .name = "RGB565X",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ },
+},
+};
+
+static int omap1_cam_get_formats(struct soc_camera_device *icd,
+ unsigned int idx, struct soc_camera_format_xlate *xlate)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ int formats = 0, ret;
+ enum v4l2_mbus_pixelcode code;
+ const struct soc_mbus_pixelfmt *fmt;
+
+ ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
+ if (ret < 0)
+ /* No more formats */
+ return 0;
+
+ fmt = soc_mbus_get_fmtdesc(code);
+ if (!fmt) {
+ dev_warn(dev, "%s: unsupported format code #%d: %d\n", __func__,
+ idx, code);
+ return 0;
+ }
+
+ /* Check support for the requested bits-per-sample */
+ if (fmt->bits_per_sample != 8)
+ return 0;
+
+ switch (code) {
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ case V4L2_MBUS_FMT_YVYU8_2X8:
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ case V4L2_MBUS_FMT_VYUY8_2X8:
+ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE:
+ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
+ case V4L2_MBUS_FMT_RGB565_2X8_BE:
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = soc_mbus_find_fmtdesc(code,
+ omap1_cam_formats,
+ ARRAY_SIZE(omap1_cam_formats));
+ xlate->code = code;
+ xlate++;
+ dev_dbg(dev,
+ "%s: providing format %s as byte swapped code #%d\n",
+ __func__, xlate->host_fmt->name, code);
+ }
+ default:
+ if (xlate)
+ dev_dbg(dev,
+ "%s: providing format %s in pass-through mode\n",
+ __func__, fmt->name);
+ }
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = fmt;
+ xlate->code = code;
+ xlate++;
+ }
+
+ return formats;
+}
+
+static bool is_dma_aligned(s32 bytes_per_line, unsigned int height,
+ enum omap1_cam_vb_mode vb_mode)
+{
+ int size = bytes_per_line * height;
+
+ return IS_ALIGNED(bytes_per_line, DMA_ELEMENT_SIZE) &&
+ IS_ALIGNED(size, DMA_FRAME_SIZE(vb_mode) * DMA_ELEMENT_SIZE);
+}
+
+static int dma_align(int *width, int *height,
+ const struct soc_mbus_pixelfmt *fmt,
+ enum omap1_cam_vb_mode vb_mode, bool enlarge)
+{
+ s32 bytes_per_line = soc_mbus_bytes_per_line(*width, fmt);
+
+ if (bytes_per_line < 0)
+ return bytes_per_line;
+
+ if (!is_dma_aligned(bytes_per_line, *height, vb_mode)) {
+ unsigned int pxalign = __fls(bytes_per_line / *width);
+ unsigned int salign = DMA_FRAME_SHIFT(vb_mode) +
+ DMA_ELEMENT_SHIFT - pxalign;
+ unsigned int incr = enlarge << salign;
+
+ v4l_bound_align_image(width, 1, *width + incr, 0,
+ height, 1, *height + incr, 0, salign);
+ return 0;
+ }
+ return 1;
+}
+
+#define subdev_call_with_sense(pcdev, dev, icd, sd, function, args...) \
+({ \
+ struct soc_camera_sense sense = { \
+ .master_clock = pcdev->camexclk, \
+ .pixel_clock_max = 0, \
+ }; \
+ int __ret; \
+ \
+ if (pcdev->pdata) \
+ sense.pixel_clock_max = pcdev->pdata->lclk_khz_max * 1000; \
+ icd->sense = &sense; \
+ __ret = v4l2_subdev_call(sd, video, function, ##args); \
+ icd->sense = NULL; \
+ \
+ if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) { \
+ if (sense.pixel_clock > sense.pixel_clock_max) { \
+ dev_err(dev, \
+ "%s: pixel clock %lu set by the camera too high!\n", \
+ __func__, sense.pixel_clock); \
+ __ret = -EINVAL; \
+ } \
+ } \
+ __ret; \
+})
+
+static int set_mbus_format(struct omap1_cam_dev *pcdev, struct device *dev,
+ struct soc_camera_device *icd, struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf,
+ const struct soc_camera_format_xlate *xlate)
+{
+ s32 bytes_per_line;
+ int ret = subdev_call_with_sense(pcdev, dev, icd, sd, s_mbus_fmt, mf);
+
+ if (ret < 0) {
+ dev_err(dev, "%s: s_mbus_fmt failed\n", __func__);
+ return ret;
+ }
+
+ if (mf->code != xlate->code) {
+ dev_err(dev, "%s: unexpected pixel code change\n", __func__);
+ return -EINVAL;
+ }
+
+ bytes_per_line = soc_mbus_bytes_per_line(mf->width, xlate->host_fmt);
+ if (bytes_per_line < 0) {
+ dev_err(dev, "%s: soc_mbus_bytes_per_line() failed\n",
+ __func__);
+ return bytes_per_line;
+ }
+
+ if (!is_dma_aligned(bytes_per_line, mf->height, pcdev->vb_mode)) {
+ dev_err(dev, "%s: resulting geometry %ux%u not DMA aligned\n",
+ __func__, mf->width, mf->height);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int omap1_cam_set_crop(struct soc_camera_device *icd,
+ struct v4l2_crop *crop)
+{
+ struct v4l2_rect *rect = &crop->c;
+ const struct soc_camera_format_xlate *xlate = icd->current_fmt;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ struct v4l2_mbus_framefmt mf;
+ int ret;
+
+ ret = subdev_call_with_sense(pcdev, dev, icd, sd, s_crop, crop);
+ if (ret < 0) {
+ dev_warn(dev, "%s: failed to crop to %ux%u@%u:%u\n", __func__,
+ rect->width, rect->height, rect->left, rect->top);
+ return ret;
+ }
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
+ if (ret < 0) {
+ dev_warn(dev, "%s: failed to fetch current format\n", __func__);
+ return ret;
+ }
+
+ ret = dma_align(&mf.width, &mf.height, xlate->host_fmt, pcdev->vb_mode,
+ false);
+ if (ret < 0) {
+ dev_err(dev, "%s: failed to align %ux%u %s with DMA\n",
+ __func__, mf.width, mf.height,
+ xlate->host_fmt->name);
+ return ret;
+ }
+
+ if (!ret) {
+ /* sensor returned geometry not DMA aligned, trying to fix */
+ ret = set_mbus_format(pcdev, dev, icd, sd, &mf, xlate);
+ if (ret < 0) {
+ dev_err(dev, "%s: failed to set format\n", __func__);
+ return ret;
+ }
+ }
+
+ icd->user_width = mf.width;
+ icd->user_height = mf.height;
+
+ return 0;
+}
+
+static int omap1_cam_set_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_mbus_framefmt mf;
+ int ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+ if (!xlate) {
+ dev_warn(dev, "%s: format %#x not found\n", __func__,
+ pix->pixelformat);
+ return -EINVAL;
+ }
+
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
+
+ ret = dma_align(&mf.width, &mf.height, xlate->host_fmt, pcdev->vb_mode,
+ true);
+ if (ret < 0) {
+ dev_err(dev, "%s: failed to align %ux%u %s with DMA\n",
+ __func__, pix->width, pix->height,
+ xlate->host_fmt->name);
+ return ret;
+ }
+
+ ret = set_mbus_format(pcdev, dev, icd, sd, &mf, xlate);
+ if (ret < 0) {
+ dev_err(dev, "%s: failed to set format\n", __func__);
+ return ret;
+ }
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->field = mf.field;
+ pix->colorspace = mf.colorspace;
+ icd->current_fmt = xlate;
+
+ return 0;
+}
+
+static int omap1_cam_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_mbus_framefmt mf;
+ int ret;
+ /* TODO: limit to mx1 hardware capabilities */
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+ if (!xlate) {
+ dev_warn(icd->parent, "Format %#x not found\n",
+ pix->pixelformat);
+ return -EINVAL;
+ }
+
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
+
+ /* limit to sensor capabilities */
+ ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->field = mf.field;
+ pix->colorspace = mf.colorspace;
+
+ return 0;
+}
+
+static bool sg_mode;
+
+/*
+ * Local mmap_mapper wrapper,
+ * used for detecting videobuf-dma-contig buffer allocation failures
+ * and switching to videobuf-dma-sg automatically for future attempts.
+ */
+static int omap1_cam_mmap_mapper(struct videobuf_queue *q,
+ struct videobuf_buffer *buf,
+ struct vm_area_struct *vma)
+{
+ struct soc_camera_device *icd = q->priv_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ int ret;
+
+ ret = pcdev->mmap_mapper(q, buf, vma);
+
+ if (ret == -ENOMEM)
+ sg_mode = true;
+
+ return ret;
+}
+
+static void omap1_cam_init_videobuf(struct videobuf_queue *q,
+ struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct omap1_cam_dev *pcdev = ici->priv;
+
+ if (!sg_mode)
+ videobuf_queue_dma_contig_init(q, &omap1_videobuf_ops,
+ icd->parent, &pcdev->lock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
+ sizeof(struct omap1_cam_buf), icd, &icd->video_lock);
+ else
+ videobuf_queue_sg_init(q, &omap1_videobuf_ops,
+ icd->parent, &pcdev->lock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
+ sizeof(struct omap1_cam_buf), icd, &icd->video_lock);
+
+ /* use videobuf mode (auto)selected with the module parameter */
+ pcdev->vb_mode = sg_mode ? OMAP1_CAM_DMA_SG : OMAP1_CAM_DMA_CONTIG;
+
+ /*
+ * Ensure we substitute the videobuf-dma-contig version of the
+ * mmap_mapper() callback with our own wrapper, used for switching
+ * automatically to videobuf-dma-sg on buffer allocation failure.
+ */
+ if (!sg_mode && q->int_ops->mmap_mapper != omap1_cam_mmap_mapper) {
+ pcdev->mmap_mapper = q->int_ops->mmap_mapper;
+ q->int_ops->mmap_mapper = omap1_cam_mmap_mapper;
+ }
+}
+
+static int omap1_cam_reqbufs(struct soc_camera_device *icd,
+ struct v4l2_requestbuffers *p)
+{
+ int i;
+
+ /*
+ * This is for locking debugging only. I removed spinlocks and now I
+ * check whether .prepare is ever called on a linked buffer, or whether
+ * a dma IRQ can occur for an in-work or unlinked buffer. Until now
+ * it hadn't triggered
+ */
+ for (i = 0; i < p->count; i++) {
+ struct omap1_cam_buf *buf = container_of(icd->vb_vidq.bufs[i],
+ struct omap1_cam_buf, vb);
+ buf->inwork = 0;
+ INIT_LIST_HEAD(&buf->vb.queue);
+ }
+
+ return 0;
+}
+
+static int omap1_cam_querycap(struct soc_camera_host *ici,
+ struct v4l2_capability *cap)
+{
+ /* cap->name is set by the friendly caller:-> */
+ strlcpy(cap->card, "OMAP1 Camera", sizeof(cap->card));
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+static int omap1_cam_set_bus_param(struct soc_camera_device *icd)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ u32 pixfmt = icd->current_fmt->host_fmt->fourcc;
+ const struct soc_camera_format_xlate *xlate;
+ const struct soc_mbus_pixelfmt *fmt;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long common_flags;
+ u32 ctrlclock, mode;
+ int ret;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg, SOCAM_BUS_FLAGS);
+ if (!common_flags) {
+ dev_warn(dev,
+ "Flags incompatible: camera 0x%x, host 0x%x\n",
+ cfg.flags, SOCAM_BUS_FLAGS);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = SOCAM_BUS_FLAGS;
+ }
+
+ /* Make choices, possibly based on platform configuration */
+ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+ (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
+ if (!pcdev->pdata ||
+ pcdev->pdata->flags & OMAP1_CAMERA_LCLK_RISING)
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
+ else
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
+ }
+
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n",
+ common_flags, ret);
+ return ret;
+ }
+
+ ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
+ if (ctrlclock & LCLK_EN)
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
+
+ if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) {
+ dev_dbg(dev, "CTRLCLOCK_REG |= POLCLK\n");
+ ctrlclock |= POLCLK;
+ } else {
+ dev_dbg(dev, "CTRLCLOCK_REG &= ~POLCLK\n");
+ ctrlclock &= ~POLCLK;
+ }
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
+
+ if (ctrlclock & LCLK_EN)
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+
+ /* select bus endianess */
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ fmt = xlate->host_fmt;
+
+ mode = CAM_READ(pcdev, MODE) & ~(RAZ_FIFO | IRQ_MASK | DMA);
+ if (fmt->order == SOC_MBUS_ORDER_LE) {
+ dev_dbg(dev, "MODE_REG &= ~ORDERCAMD\n");
+ CAM_WRITE(pcdev, MODE, mode & ~ORDERCAMD);
+ } else {
+ dev_dbg(dev, "MODE_REG |= ORDERCAMD\n");
+ CAM_WRITE(pcdev, MODE, mode | ORDERCAMD);
+ }
+
+ return 0;
+}
+
+static unsigned int omap1_cam_poll(struct file *file, poll_table *pt)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct omap1_cam_buf *buf;
+
+ buf = list_entry(icd->vb_vidq.stream.next, struct omap1_cam_buf,
+ vb.stream);
+
+ poll_wait(file, &buf->vb.done, pt);
+
+ if (buf->vb.state == VIDEOBUF_DONE ||
+ buf->vb.state == VIDEOBUF_ERROR)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+static struct soc_camera_host_ops omap1_host_ops = {
+ .owner = THIS_MODULE,
+ .add = omap1_cam_add_device,
+ .remove = omap1_cam_remove_device,
+ .get_formats = omap1_cam_get_formats,
+ .set_crop = omap1_cam_set_crop,
+ .set_fmt = omap1_cam_set_fmt,
+ .try_fmt = omap1_cam_try_fmt,
+ .init_videobuf = omap1_cam_init_videobuf,
+ .reqbufs = omap1_cam_reqbufs,
+ .querycap = omap1_cam_querycap,
+ .set_bus_param = omap1_cam_set_bus_param,
+ .poll = omap1_cam_poll,
+};
+
+static int __init omap1_cam_probe(struct platform_device *pdev)
+{
+ struct omap1_cam_dev *pcdev;
+ struct resource *res;
+ struct clk *clk;
+ void __iomem *base;
+ unsigned int irq;
+ int err = 0;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (!res || (int)irq <= 0) {
+ err = -ENODEV;
+ goto exit;
+ }
+
+ clk = clk_get(&pdev->dev, "armper_ck");
+ if (IS_ERR(clk)) {
+ err = PTR_ERR(clk);
+ goto exit;
+ }
+
+ pcdev = kzalloc(sizeof(*pcdev) + resource_size(res), GFP_KERNEL);
+ if (!pcdev) {
+ dev_err(&pdev->dev, "Could not allocate pcdev\n");
+ err = -ENOMEM;
+ goto exit_put_clk;
+ }
+
+ pcdev->res = res;
+ pcdev->clk = clk;
+
+ pcdev->pdata = pdev->dev.platform_data;
+ if (pcdev->pdata) {
+ pcdev->pflags = pcdev->pdata->flags;
+ pcdev->camexclk = pcdev->pdata->camexclk_khz * 1000;
+ }
+
+ switch (pcdev->camexclk) {
+ case 6000000:
+ case 8000000:
+ case 9600000:
+ case 12000000:
+ case 24000000:
+ break;
+ default:
+ /* pcdev->camexclk != 0 => pcdev->pdata != NULL */
+ dev_warn(&pdev->dev,
+ "Incorrect sensor clock frequency %ld kHz, "
+ "should be one of 0, 6, 8, 9.6, 12 or 24 MHz, "
+ "please correct your platform data\n",
+ pcdev->pdata->camexclk_khz);
+ pcdev->camexclk = 0;
+ case 0:
+ dev_info(&pdev->dev, "Not providing sensor clock\n");
+ }
+
+ INIT_LIST_HEAD(&pcdev->capture);
+ spin_lock_init(&pcdev->lock);
+
+ /*
+ * Request the region.
+ */
+ if (!request_mem_region(res->start, resource_size(res), DRIVER_NAME)) {
+ err = -EBUSY;
+ goto exit_kfree;
+ }
+
+ base = ioremap(res->start, resource_size(res));
+ if (!base) {
+ err = -ENOMEM;
+ goto exit_release;
+ }
+ pcdev->irq = irq;
+ pcdev->base = base;
+
+ sensor_reset(pcdev, true);
+
+ err = omap_request_dma(OMAP_DMA_CAMERA_IF_RX, DRIVER_NAME,
+ dma_isr, (void *)pcdev, &pcdev->dma_ch);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Can't request DMA for OMAP1 Camera\n");
+ err = -EBUSY;
+ goto exit_iounmap;
+ }
+ dev_dbg(&pdev->dev, "got DMA channel %d\n", pcdev->dma_ch);
+
+ /* preconfigure DMA */
+ omap_set_dma_src_params(pcdev->dma_ch, OMAP_DMA_PORT_TIPB,
+ OMAP_DMA_AMODE_CONSTANT, res->start + REG_CAMDATA,
+ 0, 0);
+ omap_set_dma_dest_burst_mode(pcdev->dma_ch, OMAP_DMA_DATA_BURST_4);
+ /* setup DMA autoinitialization */
+ omap_dma_link_lch(pcdev->dma_ch, pcdev->dma_ch);
+
+ err = request_irq(pcdev->irq, cam_isr, 0, DRIVER_NAME, pcdev);
+ if (err) {
+ dev_err(&pdev->dev, "Camera interrupt register failed\n");
+ goto exit_free_dma;
+ }
+
+ pcdev->soc_host.drv_name = DRIVER_NAME;
+ pcdev->soc_host.ops = &omap1_host_ops;
+ pcdev->soc_host.priv = pcdev;
+ pcdev->soc_host.v4l2_dev.dev = &pdev->dev;
+ pcdev->soc_host.nr = pdev->id;
+
+ err = soc_camera_host_register(&pcdev->soc_host);
+ if (err)
+ goto exit_free_irq;
+
+ dev_info(&pdev->dev, "OMAP1 Camera Interface driver loaded\n");
+
+ return 0;
+
+exit_free_irq:
+ free_irq(pcdev->irq, pcdev);
+exit_free_dma:
+ omap_free_dma(pcdev->dma_ch);
+exit_iounmap:
+ iounmap(base);
+exit_release:
+ release_mem_region(res->start, resource_size(res));
+exit_kfree:
+ kfree(pcdev);
+exit_put_clk:
+ clk_put(clk);
+exit:
+ return err;
+}
+
+static int __exit omap1_cam_remove(struct platform_device *pdev)
+{
+ struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+ struct omap1_cam_dev *pcdev = container_of(soc_host,
+ struct omap1_cam_dev, soc_host);
+ struct resource *res;
+
+ free_irq(pcdev->irq, pcdev);
+
+ omap_free_dma(pcdev->dma_ch);
+
+ soc_camera_host_unregister(soc_host);
+
+ iounmap(pcdev->base);
+
+ res = pcdev->res;
+ release_mem_region(res->start, resource_size(res));
+
+ clk_put(pcdev->clk);
+
+ kfree(pcdev);
+
+ dev_info(&pdev->dev, "OMAP1 Camera Interface driver unloaded\n");
+
+ return 0;
+}
+
+static struct platform_driver omap1_cam_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .probe = omap1_cam_probe,
+ .remove = __exit_p(omap1_cam_remove),
+};
+
+module_platform_driver(omap1_cam_driver);
+
+module_param(sg_mode, bool, 0644);
+MODULE_PARM_DESC(sg_mode, "videobuf mode, 0: dma-contig (default), 1: dma-sg");
+
+MODULE_DESCRIPTION("OMAP1 Camera Interface driver");
+MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/media/video/omap24xxcam-dma.c b/drivers/media/video/omap24xxcam-dma.c
new file mode 100644
index 00000000..3ea38a8d
--- /dev/null
+++ b/drivers/media/video/omap24xxcam-dma.c
@@ -0,0 +1,601 @@
+/*
+ * drivers/media/video/omap24xxcam-dma.c
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+ *
+ * Based on code from Andy Lowe <source@mvista.com> and
+ * David Cohen <david.cohen@indt.org.br>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/scatterlist.h>
+
+#include "omap24xxcam.h"
+
+/*
+ *
+ * DMA hardware.
+ *
+ */
+
+/* Ack all interrupt on CSR and IRQSTATUS_L0 */
+static void omap24xxcam_dmahw_ack_all(unsigned long base)
+{
+ u32 csr;
+ int i;
+
+ for (i = 0; i < NUM_CAMDMA_CHANNELS; ++i) {
+ csr = omap24xxcam_reg_in(base, CAMDMA_CSR(i));
+ /* ack interrupt in CSR */
+ omap24xxcam_reg_out(base, CAMDMA_CSR(i), csr);
+ }
+ omap24xxcam_reg_out(base, CAMDMA_IRQSTATUS_L0, 0xf);
+}
+
+/* Ack dmach on CSR and IRQSTATUS_L0 */
+static u32 omap24xxcam_dmahw_ack_ch(unsigned long base, int dmach)
+{
+ u32 csr;
+
+ csr = omap24xxcam_reg_in(base, CAMDMA_CSR(dmach));
+ /* ack interrupt in CSR */
+ omap24xxcam_reg_out(base, CAMDMA_CSR(dmach), csr);
+ /* ack interrupt in IRQSTATUS */
+ omap24xxcam_reg_out(base, CAMDMA_IRQSTATUS_L0, (1 << dmach));
+
+ return csr;
+}
+
+static int omap24xxcam_dmahw_running(unsigned long base, int dmach)
+{
+ return omap24xxcam_reg_in(base, CAMDMA_CCR(dmach)) & CAMDMA_CCR_ENABLE;
+}
+
+static void omap24xxcam_dmahw_transfer_setup(unsigned long base, int dmach,
+ dma_addr_t start, u32 len)
+{
+ omap24xxcam_reg_out(base, CAMDMA_CCR(dmach),
+ CAMDMA_CCR_SEL_SRC_DST_SYNC
+ | CAMDMA_CCR_BS
+ | CAMDMA_CCR_DST_AMODE_POST_INC
+ | CAMDMA_CCR_SRC_AMODE_POST_INC
+ | CAMDMA_CCR_FS
+ | CAMDMA_CCR_WR_ACTIVE
+ | CAMDMA_CCR_RD_ACTIVE
+ | CAMDMA_CCR_SYNCHRO_CAMERA);
+ omap24xxcam_reg_out(base, CAMDMA_CLNK_CTRL(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CEN(dmach), len);
+ omap24xxcam_reg_out(base, CAMDMA_CFN(dmach), 1);
+ omap24xxcam_reg_out(base, CAMDMA_CSDP(dmach),
+ CAMDMA_CSDP_WRITE_MODE_POSTED
+ | CAMDMA_CSDP_DST_BURST_EN_32
+ | CAMDMA_CSDP_DST_PACKED
+ | CAMDMA_CSDP_SRC_BURST_EN_32
+ | CAMDMA_CSDP_SRC_PACKED
+ | CAMDMA_CSDP_DATA_TYPE_8BITS);
+ omap24xxcam_reg_out(base, CAMDMA_CSSA(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CDSA(dmach), start);
+ omap24xxcam_reg_out(base, CAMDMA_CSEI(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CSFI(dmach), DMA_THRESHOLD);
+ omap24xxcam_reg_out(base, CAMDMA_CDEI(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CDFI(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CSR(dmach),
+ CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR
+ | CAMDMA_CSR_BLOCK
+ | CAMDMA_CSR_DROP);
+ omap24xxcam_reg_out(base, CAMDMA_CICR(dmach),
+ CAMDMA_CICR_MISALIGNED_ERR_IE
+ | CAMDMA_CICR_SECURE_ERR_IE
+ | CAMDMA_CICR_TRANS_ERR_IE
+ | CAMDMA_CICR_BLOCK_IE
+ | CAMDMA_CICR_DROP_IE);
+}
+
+static void omap24xxcam_dmahw_transfer_start(unsigned long base, int dmach)
+{
+ omap24xxcam_reg_out(base, CAMDMA_CCR(dmach),
+ CAMDMA_CCR_SEL_SRC_DST_SYNC
+ | CAMDMA_CCR_BS
+ | CAMDMA_CCR_DST_AMODE_POST_INC
+ | CAMDMA_CCR_SRC_AMODE_POST_INC
+ | CAMDMA_CCR_ENABLE
+ | CAMDMA_CCR_FS
+ | CAMDMA_CCR_SYNCHRO_CAMERA);
+}
+
+static void omap24xxcam_dmahw_transfer_chain(unsigned long base, int dmach,
+ int free_dmach)
+{
+ int prev_dmach, ch;
+
+ if (dmach == 0)
+ prev_dmach = NUM_CAMDMA_CHANNELS - 1;
+ else
+ prev_dmach = dmach - 1;
+ omap24xxcam_reg_out(base, CAMDMA_CLNK_CTRL(prev_dmach),
+ CAMDMA_CLNK_CTRL_ENABLE_LNK | dmach);
+ /* Did we chain the DMA transfer before the previous one
+ * finished?
+ */
+ ch = (dmach + free_dmach) % NUM_CAMDMA_CHANNELS;
+ while (!(omap24xxcam_reg_in(base, CAMDMA_CCR(ch))
+ & CAMDMA_CCR_ENABLE)) {
+ if (ch == dmach) {
+ /* The previous transfer has ended and this one
+ * hasn't started, so we must not have chained
+ * to the previous one in time. We'll have to
+ * start it now.
+ */
+ omap24xxcam_dmahw_transfer_start(base, dmach);
+ break;
+ } else
+ ch = (ch + 1) % NUM_CAMDMA_CHANNELS;
+ }
+}
+
+/* Abort all chained DMA transfers. After all transfers have been
+ * aborted and the DMA controller is idle, the completion routines for
+ * any aborted transfers will be called in sequence. The DMA
+ * controller may not be idle after this routine completes, because
+ * the completion routines might start new transfers.
+ */
+static void omap24xxcam_dmahw_abort_ch(unsigned long base, int dmach)
+{
+ /* mask all interrupts from this channel */
+ omap24xxcam_reg_out(base, CAMDMA_CICR(dmach), 0);
+ /* unlink this channel */
+ omap24xxcam_reg_merge(base, CAMDMA_CLNK_CTRL(dmach), 0,
+ CAMDMA_CLNK_CTRL_ENABLE_LNK);
+ /* disable this channel */
+ omap24xxcam_reg_merge(base, CAMDMA_CCR(dmach), 0, CAMDMA_CCR_ENABLE);
+}
+
+static void omap24xxcam_dmahw_init(unsigned long base)
+{
+ omap24xxcam_reg_out(base, CAMDMA_OCP_SYSCONFIG,
+ CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY
+ | CAMDMA_OCP_SYSCONFIG_SIDLEMODE_FIDLE
+ | CAMDMA_OCP_SYSCONFIG_AUTOIDLE);
+
+ omap24xxcam_reg_merge(base, CAMDMA_GCR, 0x10,
+ CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH);
+
+ omap24xxcam_reg_out(base, CAMDMA_IRQENABLE_L0, 0xf);
+}
+
+/*
+ *
+ * Individual DMA channel handling.
+ *
+ */
+
+/* Start a DMA transfer from the camera to memory.
+ * Returns zero if the transfer was successfully started, or non-zero if all
+ * DMA channels are already in use or starting is currently inhibited.
+ */
+static int omap24xxcam_dma_start(struct omap24xxcam_dma *dma, dma_addr_t start,
+ u32 len, dma_callback_t callback, void *arg)
+{
+ unsigned long flags;
+ int dmach;
+
+ spin_lock_irqsave(&dma->lock, flags);
+
+ if (!dma->free_dmach || atomic_read(&dma->dma_stop)) {
+ spin_unlock_irqrestore(&dma->lock, flags);
+ return -EBUSY;
+ }
+
+ dmach = dma->next_dmach;
+
+ dma->ch_state[dmach].callback = callback;
+ dma->ch_state[dmach].arg = arg;
+
+ omap24xxcam_dmahw_transfer_setup(dma->base, dmach, start, len);
+
+ /* We're ready to start the DMA transfer. */
+
+ if (dma->free_dmach < NUM_CAMDMA_CHANNELS) {
+ /* A transfer is already in progress, so try to chain to it. */
+ omap24xxcam_dmahw_transfer_chain(dma->base, dmach,
+ dma->free_dmach);
+ } else {
+ /* No transfer is in progress, so we'll just start this one
+ * now.
+ */
+ omap24xxcam_dmahw_transfer_start(dma->base, dmach);
+ }
+
+ dma->next_dmach = (dma->next_dmach + 1) % NUM_CAMDMA_CHANNELS;
+ dma->free_dmach--;
+
+ spin_unlock_irqrestore(&dma->lock, flags);
+
+ return 0;
+}
+
+/* Abort all chained DMA transfers. After all transfers have been
+ * aborted and the DMA controller is idle, the completion routines for
+ * any aborted transfers will be called in sequence. The DMA
+ * controller may not be idle after this routine completes, because
+ * the completion routines might start new transfers.
+ */
+static void omap24xxcam_dma_abort(struct omap24xxcam_dma *dma, u32 csr)
+{
+ unsigned long flags;
+ int dmach, i, free_dmach;
+ dma_callback_t callback;
+ void *arg;
+
+ spin_lock_irqsave(&dma->lock, flags);
+
+ /* stop any DMA transfers in progress */
+ dmach = (dma->next_dmach + dma->free_dmach) % NUM_CAMDMA_CHANNELS;
+ for (i = 0; i < NUM_CAMDMA_CHANNELS; i++) {
+ omap24xxcam_dmahw_abort_ch(dma->base, dmach);
+ dmach = (dmach + 1) % NUM_CAMDMA_CHANNELS;
+ }
+
+ /* We have to be careful here because the callback routine
+ * might start a new DMA transfer, and we only want to abort
+ * transfers that were started before this routine was called.
+ */
+ free_dmach = dma->free_dmach;
+ while ((dma->free_dmach < NUM_CAMDMA_CHANNELS) &&
+ (free_dmach < NUM_CAMDMA_CHANNELS)) {
+ dmach = (dma->next_dmach + dma->free_dmach)
+ % NUM_CAMDMA_CHANNELS;
+ callback = dma->ch_state[dmach].callback;
+ arg = dma->ch_state[dmach].arg;
+ dma->free_dmach++;
+ free_dmach++;
+ if (callback) {
+ /* leave interrupts disabled during callback */
+ spin_unlock(&dma->lock);
+ (*callback) (dma, csr, arg);
+ spin_lock(&dma->lock);
+ }
+ }
+
+ spin_unlock_irqrestore(&dma->lock, flags);
+}
+
+/* Abort all chained DMA transfers. After all transfers have been
+ * aborted and the DMA controller is idle, the completion routines for
+ * any aborted transfers will be called in sequence. If the completion
+ * routines attempt to start a new DMA transfer it will fail, so the
+ * DMA controller will be idle after this routine completes.
+ */
+static void omap24xxcam_dma_stop(struct omap24xxcam_dma *dma, u32 csr)
+{
+ atomic_inc(&dma->dma_stop);
+ omap24xxcam_dma_abort(dma, csr);
+ atomic_dec(&dma->dma_stop);
+}
+
+/* Camera DMA interrupt service routine. */
+void omap24xxcam_dma_isr(struct omap24xxcam_dma *dma)
+{
+ int dmach;
+ dma_callback_t callback;
+ void *arg;
+ u32 csr;
+ const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+
+ spin_lock(&dma->lock);
+
+ if (dma->free_dmach == NUM_CAMDMA_CHANNELS) {
+ /* A camera DMA interrupt occurred while all channels
+ * are idle, so we'll acknowledge the interrupt in the
+ * IRQSTATUS register and exit.
+ */
+ omap24xxcam_dmahw_ack_all(dma->base);
+ spin_unlock(&dma->lock);
+ return;
+ }
+
+ while (dma->free_dmach < NUM_CAMDMA_CHANNELS) {
+ dmach = (dma->next_dmach + dma->free_dmach)
+ % NUM_CAMDMA_CHANNELS;
+ if (omap24xxcam_dmahw_running(dma->base, dmach)) {
+ /* This buffer hasn't finished yet, so we're done. */
+ break;
+ }
+ csr = omap24xxcam_dmahw_ack_ch(dma->base, dmach);
+ if (csr & csr_error) {
+ /* A DMA error occurred, so stop all DMA
+ * transfers in progress.
+ */
+ spin_unlock(&dma->lock);
+ omap24xxcam_dma_stop(dma, csr);
+ return;
+ } else {
+ callback = dma->ch_state[dmach].callback;
+ arg = dma->ch_state[dmach].arg;
+ dma->free_dmach++;
+ if (callback) {
+ spin_unlock(&dma->lock);
+ (*callback) (dma, csr, arg);
+ spin_lock(&dma->lock);
+ }
+ }
+ }
+
+ spin_unlock(&dma->lock);
+
+ omap24xxcam_sgdma_process(
+ container_of(dma, struct omap24xxcam_sgdma, dma));
+}
+
+void omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dma->lock, flags);
+
+ omap24xxcam_dmahw_init(dma->base);
+
+ spin_unlock_irqrestore(&dma->lock, flags);
+}
+
+static void omap24xxcam_dma_init(struct omap24xxcam_dma *dma,
+ unsigned long base)
+{
+ int ch;
+
+ /* group all channels on DMA IRQ0 and unmask irq */
+ spin_lock_init(&dma->lock);
+ dma->base = base;
+ dma->free_dmach = NUM_CAMDMA_CHANNELS;
+ dma->next_dmach = 0;
+ for (ch = 0; ch < NUM_CAMDMA_CHANNELS; ch++) {
+ dma->ch_state[ch].callback = NULL;
+ dma->ch_state[ch].arg = NULL;
+ }
+}
+
+/*
+ *
+ * Scatter-gather DMA.
+ *
+ * High-level DMA construct for transferring whole picture frames to
+ * memory that is discontinuous.
+ *
+ */
+
+/* DMA completion routine for the scatter-gather DMA fragments. */
+static void omap24xxcam_sgdma_callback(struct omap24xxcam_dma *dma, u32 csr,
+ void *arg)
+{
+ struct omap24xxcam_sgdma *sgdma =
+ container_of(dma, struct omap24xxcam_sgdma, dma);
+ int sgslot = (int)arg;
+ struct sgdma_state *sg_state;
+ const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+
+ spin_lock(&sgdma->lock);
+
+ /* We got an interrupt, we can remove the timer */
+ del_timer(&sgdma->reset_timer);
+
+ sg_state = sgdma->sg_state + sgslot;
+ if (!sg_state->queued_sglist) {
+ spin_unlock(&sgdma->lock);
+ printk(KERN_ERR "%s: sgdma completed when none queued!\n",
+ __func__);
+ return;
+ }
+
+ sg_state->csr |= csr;
+ if (!--sg_state->queued_sglist) {
+ /* Queue for this sglist is empty, so check to see if we're
+ * done.
+ */
+ if ((sg_state->next_sglist == sg_state->sglen)
+ || (sg_state->csr & csr_error)) {
+ sgdma_callback_t callback = sg_state->callback;
+ void *arg = sg_state->arg;
+ u32 sg_csr = sg_state->csr;
+ /* All done with this sglist */
+ sgdma->free_sgdma++;
+ if (callback) {
+ spin_unlock(&sgdma->lock);
+ (*callback) (sgdma, sg_csr, arg);
+ return;
+ }
+ }
+ }
+
+ spin_unlock(&sgdma->lock);
+}
+
+/* Start queued scatter-gather DMA transfers. */
+void omap24xxcam_sgdma_process(struct omap24xxcam_sgdma *sgdma)
+{
+ unsigned long flags;
+ int queued_sgdma, sgslot;
+ struct sgdma_state *sg_state;
+ const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+
+ spin_lock_irqsave(&sgdma->lock, flags);
+
+ queued_sgdma = NUM_SG_DMA - sgdma->free_sgdma;
+ sgslot = (sgdma->next_sgdma + sgdma->free_sgdma) % NUM_SG_DMA;
+ while (queued_sgdma > 0) {
+ sg_state = sgdma->sg_state + sgslot;
+ while ((sg_state->next_sglist < sg_state->sglen) &&
+ !(sg_state->csr & csr_error)) {
+ const struct scatterlist *sglist;
+ unsigned int len;
+
+ sglist = sg_state->sglist + sg_state->next_sglist;
+ /* try to start the next DMA transfer */
+ if (sg_state->next_sglist + 1 == sg_state->sglen) {
+ /*
+ * On the last sg, we handle the case where
+ * cam->img.pix.sizeimage % PAGE_ALIGN != 0
+ */
+ len = sg_state->len - sg_state->bytes_read;
+ } else {
+ len = sg_dma_len(sglist);
+ }
+
+ if (omap24xxcam_dma_start(&sgdma->dma,
+ sg_dma_address(sglist),
+ len,
+ omap24xxcam_sgdma_callback,
+ (void *)sgslot)) {
+ /* DMA start failed */
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+ return;
+ } else {
+ unsigned long expires;
+ /* DMA start was successful */
+ sg_state->next_sglist++;
+ sg_state->bytes_read += len;
+ sg_state->queued_sglist++;
+
+ /* We start the reset timer */
+ expires = jiffies + HZ;
+ mod_timer(&sgdma->reset_timer, expires);
+ }
+ }
+ queued_sgdma--;
+ sgslot = (sgslot + 1) % NUM_SG_DMA;
+ }
+
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+}
+
+/*
+ * Queue a scatter-gather DMA transfer from the camera to memory.
+ * Returns zero if the transfer was successfully queued, or non-zero
+ * if all of the scatter-gather slots are already in use.
+ */
+int omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma,
+ const struct scatterlist *sglist, int sglen,
+ int len, sgdma_callback_t callback, void *arg)
+{
+ unsigned long flags;
+ struct sgdma_state *sg_state;
+
+ if ((sglen < 0) || ((sglen > 0) && !sglist))
+ return -EINVAL;
+
+ spin_lock_irqsave(&sgdma->lock, flags);
+
+ if (!sgdma->free_sgdma) {
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+ return -EBUSY;
+ }
+
+ sg_state = sgdma->sg_state + sgdma->next_sgdma;
+
+ sg_state->sglist = sglist;
+ sg_state->sglen = sglen;
+ sg_state->next_sglist = 0;
+ sg_state->bytes_read = 0;
+ sg_state->len = len;
+ sg_state->queued_sglist = 0;
+ sg_state->csr = 0;
+ sg_state->callback = callback;
+ sg_state->arg = arg;
+
+ sgdma->next_sgdma = (sgdma->next_sgdma + 1) % NUM_SG_DMA;
+ sgdma->free_sgdma--;
+
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+
+ omap24xxcam_sgdma_process(sgdma);
+
+ return 0;
+}
+
+/* Sync scatter-gather DMA by aborting any DMA transfers currently in progress.
+ * Any queued scatter-gather DMA transactions that have not yet been started
+ * will remain queued. The DMA controller will be idle after this routine
+ * completes. When the scatter-gather queue is restarted, the next
+ * scatter-gather DMA transfer will begin at the start of a new transaction.
+ */
+void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma)
+{
+ unsigned long flags;
+ int sgslot;
+ struct sgdma_state *sg_state;
+ u32 csr = CAMDMA_CSR_TRANS_ERR;
+
+ /* stop any DMA transfers in progress */
+ omap24xxcam_dma_stop(&sgdma->dma, csr);
+
+ spin_lock_irqsave(&sgdma->lock, flags);
+
+ if (sgdma->free_sgdma < NUM_SG_DMA) {
+ sgslot = (sgdma->next_sgdma + sgdma->free_sgdma) % NUM_SG_DMA;
+ sg_state = sgdma->sg_state + sgslot;
+ if (sg_state->next_sglist != 0) {
+ /* This DMA transfer was in progress, so abort it. */
+ sgdma_callback_t callback = sg_state->callback;
+ void *arg = sg_state->arg;
+ sgdma->free_sgdma++;
+ if (callback) {
+ /* leave interrupts masked */
+ spin_unlock(&sgdma->lock);
+ (*callback) (sgdma, csr, arg);
+ spin_lock(&sgdma->lock);
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+}
+
+void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma,
+ unsigned long base,
+ void (*reset_callback)(unsigned long data),
+ unsigned long reset_callback_data)
+{
+ int sg;
+
+ spin_lock_init(&sgdma->lock);
+ sgdma->free_sgdma = NUM_SG_DMA;
+ sgdma->next_sgdma = 0;
+ for (sg = 0; sg < NUM_SG_DMA; sg++) {
+ sgdma->sg_state[sg].sglen = 0;
+ sgdma->sg_state[sg].next_sglist = 0;
+ sgdma->sg_state[sg].bytes_read = 0;
+ sgdma->sg_state[sg].queued_sglist = 0;
+ sgdma->sg_state[sg].csr = 0;
+ sgdma->sg_state[sg].callback = NULL;
+ sgdma->sg_state[sg].arg = NULL;
+ }
+
+ omap24xxcam_dma_init(&sgdma->dma, base);
+ setup_timer(&sgdma->reset_timer, reset_callback, reset_callback_data);
+}
diff --git a/drivers/media/video/omap24xxcam.c b/drivers/media/video/omap24xxcam.c
new file mode 100644
index 00000000..7d386414
--- /dev/null
+++ b/drivers/media/video/omap24xxcam.c
@@ -0,0 +1,1882 @@
+/*
+ * drivers/media/video/omap24xxcam.c
+ *
+ * OMAP 2 camera block driver.
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ * Copyright (C) 2007-2008 Nokia Corporation.
+ *
+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+ *
+ * Based on code from Andy Lowe <source@mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/videodev2.h>
+#include <linux/pci.h> /* needed for videobufs */
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+
+#include "omap24xxcam.h"
+
+#define OMAP24XXCAM_VERSION "0.0.1"
+
+#define RESET_TIMEOUT_NS 10000
+
+static void omap24xxcam_reset(struct omap24xxcam_device *cam);
+static int omap24xxcam_sensor_if_enable(struct omap24xxcam_device *cam);
+static void omap24xxcam_device_unregister(struct v4l2_int_device *s);
+static int omap24xxcam_remove(struct platform_device *pdev);
+
+/* module parameters */
+static int video_nr = -1; /* video device minor (-1 ==> auto assign) */
+/*
+ * Maximum amount of memory to use for capture buffers.
+ * Default is 4800KB, enough to double-buffer SXGA.
+ */
+static int capture_mem = 1280 * 960 * 2 * 2;
+
+static struct v4l2_int_device omap24xxcam;
+
+/*
+ *
+ * Clocks.
+ *
+ */
+
+static void omap24xxcam_clock_put(struct omap24xxcam_device *cam)
+{
+ if (cam->ick != NULL && !IS_ERR(cam->ick))
+ clk_put(cam->ick);
+ if (cam->fck != NULL && !IS_ERR(cam->fck))
+ clk_put(cam->fck);
+
+ cam->ick = cam->fck = NULL;
+}
+
+static int omap24xxcam_clock_get(struct omap24xxcam_device *cam)
+{
+ int rval = 0;
+
+ cam->fck = clk_get(cam->dev, "fck");
+ if (IS_ERR(cam->fck)) {
+ dev_err(cam->dev, "can't get camera fck");
+ rval = PTR_ERR(cam->fck);
+ omap24xxcam_clock_put(cam);
+ return rval;
+ }
+
+ cam->ick = clk_get(cam->dev, "ick");
+ if (IS_ERR(cam->ick)) {
+ dev_err(cam->dev, "can't get camera ick");
+ rval = PTR_ERR(cam->ick);
+ omap24xxcam_clock_put(cam);
+ }
+
+ return rval;
+}
+
+static void omap24xxcam_clock_on(struct omap24xxcam_device *cam)
+{
+ clk_enable(cam->fck);
+ clk_enable(cam->ick);
+}
+
+static void omap24xxcam_clock_off(struct omap24xxcam_device *cam)
+{
+ clk_disable(cam->fck);
+ clk_disable(cam->ick);
+}
+
+/*
+ *
+ * Camera core
+ *
+ */
+
+/*
+ * Set xclk.
+ *
+ * To disable xclk, use value zero.
+ */
+static void omap24xxcam_core_xclk_set(const struct omap24xxcam_device *cam,
+ u32 xclk)
+{
+ if (xclk) {
+ u32 divisor = CAM_MCLK / xclk;
+
+ if (divisor == 1)
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
+ CC_CTRL_XCLK,
+ CC_CTRL_XCLK_DIV_BYPASS);
+ else
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
+ CC_CTRL_XCLK, divisor);
+ } else
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
+ CC_CTRL_XCLK, CC_CTRL_XCLK_DIV_STABLE_LOW);
+}
+
+static void omap24xxcam_core_hwinit(const struct omap24xxcam_device *cam)
+{
+ /*
+ * Setting the camera core AUTOIDLE bit causes problems with frame
+ * synchronization, so we will clear the AUTOIDLE bit instead.
+ */
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_SYSCONFIG,
+ CC_SYSCONFIG_AUTOIDLE);
+
+ /* program the camera interface DMA packet size */
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL_DMA,
+ CC_CTRL_DMA_EN | (DMA_THRESHOLD / 4 - 1));
+
+ /* enable camera core error interrupts */
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_IRQENABLE,
+ CC_IRQENABLE_FW_ERR_IRQ
+ | CC_IRQENABLE_FSC_ERR_IRQ
+ | CC_IRQENABLE_SSC_ERR_IRQ
+ | CC_IRQENABLE_FIFO_OF_IRQ);
+}
+
+/*
+ * Enable the camera core.
+ *
+ * Data transfer to the camera DMA starts from next starting frame.
+ */
+static void omap24xxcam_core_enable(const struct omap24xxcam_device *cam)
+{
+
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL,
+ cam->cc_ctrl);
+}
+
+/*
+ * Disable camera core.
+ *
+ * The data transfer will be stopped immediately (CC_CTRL_CC_RST). The
+ * core internal state machines will be reset. Use
+ * CC_CTRL_CC_FRAME_TRIG instead if you want to transfer the current
+ * frame completely.
+ */
+static void omap24xxcam_core_disable(const struct omap24xxcam_device *cam)
+{
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL,
+ CC_CTRL_CC_RST);
+}
+
+/* Interrupt service routine for camera core interrupts. */
+static void omap24xxcam_core_isr(struct omap24xxcam_device *cam)
+{
+ u32 cc_irqstatus;
+ const u32 cc_irqstatus_err =
+ CC_IRQSTATUS_FW_ERR_IRQ
+ | CC_IRQSTATUS_FSC_ERR_IRQ
+ | CC_IRQSTATUS_SSC_ERR_IRQ
+ | CC_IRQSTATUS_FIFO_UF_IRQ
+ | CC_IRQSTATUS_FIFO_OF_IRQ;
+
+ cc_irqstatus = omap24xxcam_reg_in(cam->mmio_base + CC_REG_OFFSET,
+ CC_IRQSTATUS);
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_IRQSTATUS,
+ cc_irqstatus);
+
+ if (cc_irqstatus & cc_irqstatus_err
+ && !atomic_read(&cam->in_reset)) {
+ dev_dbg(cam->dev, "resetting camera, cc_irqstatus 0x%x\n",
+ cc_irqstatus);
+ omap24xxcam_reset(cam);
+ }
+}
+
+/*
+ *
+ * videobuf_buffer handling.
+ *
+ * Memory for mmapped videobuf_buffers is not allocated
+ * conventionally, but by several kmalloc allocations and then
+ * creating the scatterlist on our own. User-space buffers are handled
+ * normally.
+ *
+ */
+
+/*
+ * Free the memory-mapped buffer memory allocated for a
+ * videobuf_buffer and the associated scatterlist.
+ */
+static void omap24xxcam_vbq_free_mmap_buffer(struct videobuf_buffer *vb)
+{
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+ size_t alloc_size;
+ struct page *page;
+ int i;
+
+ if (dma->sglist == NULL)
+ return;
+
+ i = dma->sglen;
+ while (i) {
+ i--;
+ alloc_size = sg_dma_len(&dma->sglist[i]);
+ page = sg_page(&dma->sglist[i]);
+ do {
+ ClearPageReserved(page++);
+ } while (alloc_size -= PAGE_SIZE);
+ __free_pages(sg_page(&dma->sglist[i]),
+ get_order(sg_dma_len(&dma->sglist[i])));
+ }
+
+ kfree(dma->sglist);
+ dma->sglist = NULL;
+}
+
+/* Release all memory related to the videobuf_queue. */
+static void omap24xxcam_vbq_free_mmap_buffers(struct videobuf_queue *vbq)
+{
+ int i;
+
+ mutex_lock(&vbq->vb_lock);
+
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ if (NULL == vbq->bufs[i])
+ continue;
+ if (V4L2_MEMORY_MMAP != vbq->bufs[i]->memory)
+ continue;
+ vbq->ops->buf_release(vbq, vbq->bufs[i]);
+ omap24xxcam_vbq_free_mmap_buffer(vbq->bufs[i]);
+ kfree(vbq->bufs[i]);
+ vbq->bufs[i] = NULL;
+ }
+
+ mutex_unlock(&vbq->vb_lock);
+
+ videobuf_mmap_free(vbq);
+}
+
+/*
+ * Allocate physically as contiguous as possible buffer for video
+ * frame and allocate and build DMA scatter-gather list for it.
+ */
+static int omap24xxcam_vbq_alloc_mmap_buffer(struct videobuf_buffer *vb)
+{
+ unsigned int order;
+ size_t alloc_size, size = vb->bsize; /* vb->bsize is page aligned */
+ struct page *page;
+ int max_pages, err = 0, i = 0;
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+
+ /*
+ * allocate maximum size scatter-gather list. Note this is
+ * overhead. We may not use as many entries as we allocate
+ */
+ max_pages = vb->bsize >> PAGE_SHIFT;
+ dma->sglist = kcalloc(max_pages, sizeof(*dma->sglist), GFP_KERNEL);
+ if (dma->sglist == NULL) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ while (size) {
+ order = get_order(size);
+ /*
+ * do not over-allocate even if we would get larger
+ * contiguous chunk that way
+ */
+ if ((PAGE_SIZE << order) > size)
+ order--;
+
+ /* try to allocate as many contiguous pages as possible */
+ page = alloc_pages(GFP_KERNEL, order);
+ /* if allocation fails, try to allocate smaller amount */
+ while (page == NULL) {
+ order--;
+ page = alloc_pages(GFP_KERNEL, order);
+ if (page == NULL && !order) {
+ err = -ENOMEM;
+ goto out;
+ }
+ }
+ size -= (PAGE_SIZE << order);
+
+ /* append allocated chunk of pages into scatter-gather list */
+ sg_set_page(&dma->sglist[i], page, PAGE_SIZE << order, 0);
+ dma->sglen++;
+ i++;
+
+ alloc_size = (PAGE_SIZE << order);
+
+ /* clear pages before giving them to user space */
+ memset(page_address(page), 0, alloc_size);
+
+ /* mark allocated pages reserved */
+ do {
+ SetPageReserved(page++);
+ } while (alloc_size -= PAGE_SIZE);
+ }
+ /*
+ * REVISIT: not fully correct to assign nr_pages == sglen but
+ * video-buf is passing nr_pages for e.g. unmap_sg calls
+ */
+ dma->nr_pages = dma->sglen;
+ dma->direction = PCI_DMA_FROMDEVICE;
+
+ return 0;
+
+out:
+ omap24xxcam_vbq_free_mmap_buffer(vb);
+ return err;
+}
+
+static int omap24xxcam_vbq_alloc_mmap_buffers(struct videobuf_queue *vbq,
+ unsigned int count)
+{
+ int i, err = 0;
+ struct omap24xxcam_fh *fh =
+ container_of(vbq, struct omap24xxcam_fh, vbq);
+
+ mutex_lock(&vbq->vb_lock);
+
+ for (i = 0; i < count; i++) {
+ err = omap24xxcam_vbq_alloc_mmap_buffer(vbq->bufs[i]);
+ if (err)
+ goto out;
+ dev_dbg(fh->cam->dev, "sglen is %d for buffer %d\n",
+ videobuf_to_dma(vbq->bufs[i])->sglen, i);
+ }
+
+ mutex_unlock(&vbq->vb_lock);
+
+ return 0;
+out:
+ while (i) {
+ i--;
+ omap24xxcam_vbq_free_mmap_buffer(vbq->bufs[i]);
+ }
+
+ mutex_unlock(&vbq->vb_lock);
+
+ return err;
+}
+
+/*
+ * This routine is called from interrupt context when a scatter-gather DMA
+ * transfer of a videobuf_buffer completes.
+ */
+static void omap24xxcam_vbq_complete(struct omap24xxcam_sgdma *sgdma,
+ u32 csr, void *arg)
+{
+ struct omap24xxcam_device *cam =
+ container_of(sgdma, struct omap24xxcam_device, sgdma);
+ struct omap24xxcam_fh *fh = cam->streaming->private_data;
+ struct videobuf_buffer *vb = (struct videobuf_buffer *)arg;
+ const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+ if (--cam->sgdma_in_queue == 0)
+ omap24xxcam_core_disable(cam);
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+
+ do_gettimeofday(&vb->ts);
+ vb->field_count = atomic_add_return(2, &fh->field_count);
+ if (csr & csr_error) {
+ vb->state = VIDEOBUF_ERROR;
+ if (!atomic_read(&fh->cam->in_reset)) {
+ dev_dbg(cam->dev, "resetting camera, csr 0x%x\n", csr);
+ omap24xxcam_reset(cam);
+ }
+ } else
+ vb->state = VIDEOBUF_DONE;
+ wake_up(&vb->done);
+}
+
+static void omap24xxcam_vbq_release(struct videobuf_queue *vbq,
+ struct videobuf_buffer *vb)
+{
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+
+ /* wait for buffer, especially to get out of the sgdma queue */
+ videobuf_waiton(vbq, vb, 0, 0);
+ if (vb->memory == V4L2_MEMORY_MMAP) {
+ dma_unmap_sg(vbq->dev, dma->sglist, dma->sglen,
+ dma->direction);
+ dma->direction = DMA_NONE;
+ } else {
+ videobuf_dma_unmap(vbq->dev, videobuf_to_dma(vb));
+ videobuf_dma_free(videobuf_to_dma(vb));
+ }
+
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+/*
+ * Limit the number of available kernel image capture buffers based on the
+ * number requested, the currently selected image size, and the maximum
+ * amount of memory permitted for kernel capture buffers.
+ */
+static int omap24xxcam_vbq_setup(struct videobuf_queue *vbq, unsigned int *cnt,
+ unsigned int *size)
+{
+ struct omap24xxcam_fh *fh = vbq->priv_data;
+
+ if (*cnt <= 0)
+ *cnt = VIDEO_MAX_FRAME; /* supply a default number of buffers */
+
+ if (*cnt > VIDEO_MAX_FRAME)
+ *cnt = VIDEO_MAX_FRAME;
+
+ *size = fh->pix.sizeimage;
+
+ /* accessing fh->cam->capture_mem is ok, it's constant */
+ if (*size * *cnt > fh->cam->capture_mem)
+ *cnt = fh->cam->capture_mem / *size;
+
+ return 0;
+}
+
+static int omap24xxcam_dma_iolock(struct videobuf_queue *vbq,
+ struct videobuf_dmabuf *dma)
+{
+ int err = 0;
+
+ dma->direction = PCI_DMA_FROMDEVICE;
+ if (!dma_map_sg(vbq->dev, dma->sglist, dma->sglen, dma->direction)) {
+ kfree(dma->sglist);
+ dma->sglist = NULL;
+ dma->sglen = 0;
+ err = -EIO;
+ }
+
+ return err;
+}
+
+static int omap24xxcam_vbq_prepare(struct videobuf_queue *vbq,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct omap24xxcam_fh *fh = vbq->priv_data;
+ int err = 0;
+
+ /*
+ * Accessing pix here is okay since it's constant while
+ * streaming is on (and we only get called then).
+ */
+ if (vb->baddr) {
+ /* This is a userspace buffer. */
+ if (fh->pix.sizeimage > vb->bsize) {
+ /* The buffer isn't big enough. */
+ err = -EINVAL;
+ } else
+ vb->size = fh->pix.sizeimage;
+ } else {
+ if (vb->state != VIDEOBUF_NEEDS_INIT) {
+ /*
+ * We have a kernel bounce buffer that has
+ * already been allocated.
+ */
+ if (fh->pix.sizeimage > vb->size) {
+ /*
+ * The image size has been changed to
+ * a larger size since this buffer was
+ * allocated, so we need to free and
+ * reallocate it.
+ */
+ omap24xxcam_vbq_release(vbq, vb);
+ vb->size = fh->pix.sizeimage;
+ }
+ } else {
+ /* We need to allocate a new kernel bounce buffer. */
+ vb->size = fh->pix.sizeimage;
+ }
+ }
+
+ if (err)
+ return err;
+
+ vb->width = fh->pix.width;
+ vb->height = fh->pix.height;
+ vb->field = field;
+
+ if (vb->state == VIDEOBUF_NEEDS_INIT) {
+ if (vb->memory == V4L2_MEMORY_MMAP)
+ /*
+ * we have built the scatter-gather list by ourself so
+ * do the scatter-gather mapping as well
+ */
+ err = omap24xxcam_dma_iolock(vbq, videobuf_to_dma(vb));
+ else
+ err = videobuf_iolock(vbq, vb, NULL);
+ }
+
+ if (!err)
+ vb->state = VIDEOBUF_PREPARED;
+ else
+ omap24xxcam_vbq_release(vbq, vb);
+
+ return err;
+}
+
+static void omap24xxcam_vbq_queue(struct videobuf_queue *vbq,
+ struct videobuf_buffer *vb)
+{
+ struct omap24xxcam_fh *fh = vbq->priv_data;
+ struct omap24xxcam_device *cam = fh->cam;
+ enum videobuf_state state = vb->state;
+ unsigned long flags;
+ int err;
+
+ /*
+ * FIXME: We're marking the buffer active since we have no
+ * pretty way of marking it active exactly when the
+ * scatter-gather transfer starts.
+ */
+ vb->state = VIDEOBUF_ACTIVE;
+
+ err = omap24xxcam_sgdma_queue(&fh->cam->sgdma,
+ videobuf_to_dma(vb)->sglist,
+ videobuf_to_dma(vb)->sglen, vb->size,
+ omap24xxcam_vbq_complete, vb);
+
+ if (!err) {
+ spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+ if (++cam->sgdma_in_queue == 1
+ && !atomic_read(&cam->in_reset))
+ omap24xxcam_core_enable(cam);
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+ } else {
+ /*
+ * Oops. We're not supposed to get any errors here.
+ * The only way we could get an error is if we ran out
+ * of scatter-gather DMA slots, but we are supposed to
+ * have at least as many scatter-gather DMA slots as
+ * video buffers so that can't happen.
+ */
+ dev_err(cam->dev, "failed to queue a video buffer for dma!\n");
+ dev_err(cam->dev, "likely a bug in the driver!\n");
+ vb->state = state;
+ }
+}
+
+static struct videobuf_queue_ops omap24xxcam_vbq_ops = {
+ .buf_setup = omap24xxcam_vbq_setup,
+ .buf_prepare = omap24xxcam_vbq_prepare,
+ .buf_queue = omap24xxcam_vbq_queue,
+ .buf_release = omap24xxcam_vbq_release,
+};
+
+/*
+ *
+ * OMAP main camera system
+ *
+ */
+
+/*
+ * Reset camera block to power-on state.
+ */
+static void omap24xxcam_poweron_reset(struct omap24xxcam_device *cam)
+{
+ int max_loop = RESET_TIMEOUT_NS;
+
+ /* Reset whole camera subsystem */
+ omap24xxcam_reg_out(cam->mmio_base,
+ CAM_SYSCONFIG,
+ CAM_SYSCONFIG_SOFTRESET);
+
+ /* Wait till it's finished */
+ while (!(omap24xxcam_reg_in(cam->mmio_base, CAM_SYSSTATUS)
+ & CAM_SYSSTATUS_RESETDONE)
+ && --max_loop) {
+ ndelay(1);
+ }
+
+ if (!(omap24xxcam_reg_in(cam->mmio_base, CAM_SYSSTATUS)
+ & CAM_SYSSTATUS_RESETDONE))
+ dev_err(cam->dev, "camera soft reset timeout\n");
+}
+
+/*
+ * (Re)initialise the camera block.
+ */
+static void omap24xxcam_hwinit(struct omap24xxcam_device *cam)
+{
+ omap24xxcam_poweron_reset(cam);
+
+ /* set the camera subsystem autoidle bit */
+ omap24xxcam_reg_out(cam->mmio_base, CAM_SYSCONFIG,
+ CAM_SYSCONFIG_AUTOIDLE);
+
+ /* set the camera MMU autoidle bit */
+ omap24xxcam_reg_out(cam->mmio_base,
+ CAMMMU_REG_OFFSET + CAMMMU_SYSCONFIG,
+ CAMMMU_SYSCONFIG_AUTOIDLE);
+
+ omap24xxcam_core_hwinit(cam);
+
+ omap24xxcam_dma_hwinit(&cam->sgdma.dma);
+}
+
+/*
+ * Callback for dma transfer stalling.
+ */
+static void omap24xxcam_stalled_dma_reset(unsigned long data)
+{
+ struct omap24xxcam_device *cam = (struct omap24xxcam_device *)data;
+
+ if (!atomic_read(&cam->in_reset)) {
+ dev_dbg(cam->dev, "dma stalled, resetting camera\n");
+ omap24xxcam_reset(cam);
+ }
+}
+
+/*
+ * Stop capture. Mark we're doing a reset, stop DMA transfers and
+ * core. (No new scatter-gather transfers will be queued whilst
+ * in_reset is non-zero.)
+ *
+ * If omap24xxcam_capture_stop is called from several places at
+ * once, only the first call will have an effect. Similarly, the last
+ * call omap24xxcam_streaming_cont will have effect.
+ *
+ * Serialisation is ensured by using cam->core_enable_disable_lock.
+ */
+static void omap24xxcam_capture_stop(struct omap24xxcam_device *cam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+
+ if (atomic_inc_return(&cam->in_reset) != 1) {
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+ return;
+ }
+
+ omap24xxcam_core_disable(cam);
+
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+
+ omap24xxcam_sgdma_sync(&cam->sgdma);
+}
+
+/*
+ * Reset and continue streaming.
+ *
+ * Note: Resetting the camera FIFO via the CC_RST bit in the CC_CTRL
+ * register is supposed to be sufficient to recover from a camera
+ * interface error, but it doesn't seem to be enough. If we only do
+ * that then subsequent image captures are out of sync by either one
+ * or two times DMA_THRESHOLD bytes. Resetting and re-initializing the
+ * entire camera subsystem prevents the problem with frame
+ * synchronization.
+ */
+static void omap24xxcam_capture_cont(struct omap24xxcam_device *cam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+
+ if (atomic_read(&cam->in_reset) != 1)
+ goto out;
+
+ omap24xxcam_hwinit(cam);
+
+ omap24xxcam_sensor_if_enable(cam);
+
+ omap24xxcam_sgdma_process(&cam->sgdma);
+
+ if (cam->sgdma_in_queue)
+ omap24xxcam_core_enable(cam);
+
+out:
+ atomic_dec(&cam->in_reset);
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+}
+
+static ssize_t
+omap24xxcam_streaming_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct omap24xxcam_device *cam = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", cam->streaming ? "active" : "inactive");
+}
+static DEVICE_ATTR(streaming, S_IRUGO, omap24xxcam_streaming_show, NULL);
+
+/*
+ * Stop capture and restart it. I.e. reset the camera during use.
+ */
+static void omap24xxcam_reset(struct omap24xxcam_device *cam)
+{
+ omap24xxcam_capture_stop(cam);
+ omap24xxcam_capture_cont(cam);
+}
+
+/*
+ * The main interrupt handler.
+ */
+static irqreturn_t omap24xxcam_isr(int irq, void *arg)
+{
+ struct omap24xxcam_device *cam = (struct omap24xxcam_device *)arg;
+ u32 irqstatus;
+ unsigned int irqhandled = 0;
+
+ irqstatus = omap24xxcam_reg_in(cam->mmio_base, CAM_IRQSTATUS);
+
+ if (irqstatus &
+ (CAM_IRQSTATUS_DMA_IRQ2 | CAM_IRQSTATUS_DMA_IRQ1
+ | CAM_IRQSTATUS_DMA_IRQ0)) {
+ omap24xxcam_dma_isr(&cam->sgdma.dma);
+ irqhandled = 1;
+ }
+ if (irqstatus & CAM_IRQSTATUS_CC_IRQ) {
+ omap24xxcam_core_isr(cam);
+ irqhandled = 1;
+ }
+ if (irqstatus & CAM_IRQSTATUS_MMU_IRQ)
+ dev_err(cam->dev, "unhandled camera MMU interrupt!\n");
+
+ return IRQ_RETVAL(irqhandled);
+}
+
+/*
+ *
+ * Sensor handling.
+ *
+ */
+
+/*
+ * Enable the external sensor interface. Try to negotiate interface
+ * parameters with the sensor and start using the new ones. The calls
+ * to sensor_if_enable and sensor_if_disable need not to be balanced.
+ */
+static int omap24xxcam_sensor_if_enable(struct omap24xxcam_device *cam)
+{
+ int rval;
+ struct v4l2_ifparm p;
+
+ rval = vidioc_int_g_ifparm(cam->sdev, &p);
+ if (rval) {
+ dev_err(cam->dev, "vidioc_int_g_ifparm failed with %d\n", rval);
+ return rval;
+ }
+
+ cam->if_type = p.if_type;
+
+ cam->cc_ctrl = CC_CTRL_CC_EN;
+
+ switch (p.if_type) {
+ case V4L2_IF_TYPE_BT656:
+ if (p.u.bt656.frame_start_on_rising_vs)
+ cam->cc_ctrl |= CC_CTRL_NOBT_SYNCHRO;
+ if (p.u.bt656.bt_sync_correct)
+ cam->cc_ctrl |= CC_CTRL_BT_CORRECT;
+ if (p.u.bt656.swap)
+ cam->cc_ctrl |= CC_CTRL_PAR_ORDERCAM;
+ if (p.u.bt656.latch_clk_inv)
+ cam->cc_ctrl |= CC_CTRL_PAR_CLK_POL;
+ if (p.u.bt656.nobt_hs_inv)
+ cam->cc_ctrl |= CC_CTRL_NOBT_HS_POL;
+ if (p.u.bt656.nobt_vs_inv)
+ cam->cc_ctrl |= CC_CTRL_NOBT_VS_POL;
+
+ switch (p.u.bt656.mode) {
+ case V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT8;
+ break;
+ case V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT10;
+ break;
+ case V4L2_IF_TYPE_BT656_MODE_NOBT_12BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT12;
+ break;
+ case V4L2_IF_TYPE_BT656_MODE_BT_8BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_BT8;
+ break;
+ case V4L2_IF_TYPE_BT656_MODE_BT_10BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_BT10;
+ break;
+ default:
+ dev_err(cam->dev,
+ "bt656 interface mode %d not supported\n",
+ p.u.bt656.mode);
+ return -EINVAL;
+ }
+ /*
+ * The clock rate that the sensor wants has changed.
+ * We have to adjust the xclk from OMAP 2 side to
+ * match the sensor's wish as closely as possible.
+ */
+ if (p.u.bt656.clock_curr != cam->if_u.bt656.xclk) {
+ u32 xclk = p.u.bt656.clock_curr;
+ u32 divisor;
+
+ if (xclk == 0)
+ return -EINVAL;
+
+ if (xclk > CAM_MCLK)
+ xclk = CAM_MCLK;
+
+ divisor = CAM_MCLK / xclk;
+ if (divisor * xclk < CAM_MCLK)
+ divisor++;
+ if (CAM_MCLK / divisor < p.u.bt656.clock_min
+ && divisor > 1)
+ divisor--;
+ if (divisor > 30)
+ divisor = 30;
+
+ xclk = CAM_MCLK / divisor;
+
+ if (xclk < p.u.bt656.clock_min
+ || xclk > p.u.bt656.clock_max)
+ return -EINVAL;
+
+ cam->if_u.bt656.xclk = xclk;
+ }
+ omap24xxcam_core_xclk_set(cam, cam->if_u.bt656.xclk);
+ break;
+ default:
+ /* FIXME: how about other interfaces? */
+ dev_err(cam->dev, "interface type %d not supported\n",
+ p.if_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void omap24xxcam_sensor_if_disable(const struct omap24xxcam_device *cam)
+{
+ switch (cam->if_type) {
+ case V4L2_IF_TYPE_BT656:
+ omap24xxcam_core_xclk_set(cam, 0);
+ break;
+ }
+}
+
+/*
+ * Initialise the sensor hardware.
+ */
+static int omap24xxcam_sensor_init(struct omap24xxcam_device *cam)
+{
+ int err = 0;
+ struct v4l2_int_device *sdev = cam->sdev;
+
+ omap24xxcam_clock_on(cam);
+ err = omap24xxcam_sensor_if_enable(cam);
+ if (err) {
+ dev_err(cam->dev, "sensor interface could not be enabled at "
+ "initialisation, %d\n", err);
+ cam->sdev = NULL;
+ goto out;
+ }
+
+ /* power up sensor during sensor initialization */
+ vidioc_int_s_power(sdev, 1);
+
+ err = vidioc_int_dev_init(sdev);
+ if (err) {
+ dev_err(cam->dev, "cannot initialize sensor, error %d\n", err);
+ /* Sensor init failed --- it's nonexistent to us! */
+ cam->sdev = NULL;
+ goto out;
+ }
+
+ dev_info(cam->dev, "sensor is %s\n", sdev->name);
+
+out:
+ omap24xxcam_sensor_if_disable(cam);
+ omap24xxcam_clock_off(cam);
+
+ vidioc_int_s_power(sdev, 0);
+
+ return err;
+}
+
+static void omap24xxcam_sensor_exit(struct omap24xxcam_device *cam)
+{
+ if (cam->sdev)
+ vidioc_int_dev_exit(cam->sdev);
+}
+
+static void omap24xxcam_sensor_disable(struct omap24xxcam_device *cam)
+{
+ omap24xxcam_sensor_if_disable(cam);
+ omap24xxcam_clock_off(cam);
+ vidioc_int_s_power(cam->sdev, 0);
+}
+
+/*
+ * Power-up and configure camera sensor. It's ready for capturing now.
+ */
+static int omap24xxcam_sensor_enable(struct omap24xxcam_device *cam)
+{
+ int rval;
+
+ omap24xxcam_clock_on(cam);
+
+ omap24xxcam_sensor_if_enable(cam);
+
+ rval = vidioc_int_s_power(cam->sdev, 1);
+ if (rval)
+ goto out;
+
+ rval = vidioc_int_init(cam->sdev);
+ if (rval)
+ goto out;
+
+ return 0;
+
+out:
+ omap24xxcam_sensor_disable(cam);
+
+ return rval;
+}
+
+static void omap24xxcam_sensor_reset_work(struct work_struct *work)
+{
+ struct omap24xxcam_device *cam =
+ container_of(work, struct omap24xxcam_device,
+ sensor_reset_work);
+
+ if (atomic_read(&cam->reset_disable))
+ return;
+
+ omap24xxcam_capture_stop(cam);
+
+ if (vidioc_int_reset(cam->sdev) == 0) {
+ vidioc_int_init(cam->sdev);
+ } else {
+ /* Can't reset it by vidioc_int_reset. */
+ omap24xxcam_sensor_disable(cam);
+ omap24xxcam_sensor_enable(cam);
+ }
+
+ omap24xxcam_capture_cont(cam);
+}
+
+/*
+ *
+ * IOCTL interface.
+ *
+ */
+
+static int vidioc_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+
+ strlcpy(cap->driver, CAM_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, cam->vfd->name, sizeof(cap->card));
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ rval = vidioc_int_enum_fmt_cap(cam->sdev, f);
+
+ return rval;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_g_fmt_cap(cam->sdev, f);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ rval = -EBUSY;
+ goto out;
+ }
+
+ rval = vidioc_int_s_fmt_cap(cam->sdev, f);
+
+out:
+ mutex_unlock(&cam->mutex);
+
+ if (!rval) {
+ mutex_lock(&ofh->vbq.vb_lock);
+ ofh->pix = f->fmt.pix;
+ mutex_unlock(&ofh->vbq.vb_lock);
+ }
+
+ memset(f, 0, sizeof(*f));
+ vidioc_g_fmt_vid_cap(file, fh, f);
+
+ return rval;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_try_fmt_cap(cam->sdev, f);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_reqbufs(struct file *file, void *fh,
+ struct v4l2_requestbuffers *b)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ mutex_unlock(&cam->mutex);
+ return -EBUSY;
+ }
+
+ omap24xxcam_vbq_free_mmap_buffers(&ofh->vbq);
+ mutex_unlock(&cam->mutex);
+
+ rval = videobuf_reqbufs(&ofh->vbq, b);
+
+ /*
+ * Either videobuf_reqbufs failed or the buffers are not
+ * memory-mapped (which would need special attention).
+ */
+ if (rval < 0 || b->memory != V4L2_MEMORY_MMAP)
+ goto out;
+
+ rval = omap24xxcam_vbq_alloc_mmap_buffers(&ofh->vbq, rval);
+ if (rval)
+ omap24xxcam_vbq_free_mmap_buffers(&ofh->vbq);
+
+out:
+ return rval;
+}
+
+static int vidioc_querybuf(struct file *file, void *fh,
+ struct v4l2_buffer *b)
+{
+ struct omap24xxcam_fh *ofh = fh;
+
+ return videobuf_querybuf(&ofh->vbq, b);
+}
+
+static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct omap24xxcam_fh *ofh = fh;
+
+ return videobuf_qbuf(&ofh->vbq, b);
+}
+
+static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ struct videobuf_buffer *vb;
+ int rval;
+
+videobuf_dqbuf_again:
+ rval = videobuf_dqbuf(&ofh->vbq, b, file->f_flags & O_NONBLOCK);
+ if (rval)
+ goto out;
+
+ vb = ofh->vbq.bufs[b->index];
+
+ mutex_lock(&cam->mutex);
+ /* _needs_reset returns -EIO if reset is required. */
+ rval = vidioc_int_g_needs_reset(cam->sdev, (void *)vb->baddr);
+ mutex_unlock(&cam->mutex);
+ if (rval == -EIO)
+ schedule_work(&cam->sensor_reset_work);
+ else
+ rval = 0;
+
+out:
+ /*
+ * This is a hack. We don't want to show -EIO to the user
+ * space. Requeue the buffer and try again if we're not doing
+ * this in non-blocking mode.
+ */
+ if (rval == -EIO) {
+ videobuf_qbuf(&ofh->vbq, b);
+ if (!(file->f_flags & O_NONBLOCK))
+ goto videobuf_dqbuf_again;
+ /*
+ * We don't have a videobuf_buffer now --- maybe next
+ * time...
+ */
+ rval = -EAGAIN;
+ }
+
+ return rval;
+}
+
+static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ rval = -EBUSY;
+ goto out;
+ }
+
+ rval = omap24xxcam_sensor_if_enable(cam);
+ if (rval) {
+ dev_dbg(cam->dev, "vidioc_int_g_ifparm failed\n");
+ goto out;
+ }
+
+ rval = videobuf_streamon(&ofh->vbq);
+ if (!rval) {
+ cam->streaming = file;
+ sysfs_notify(&cam->dev->kobj, NULL, "streaming");
+ }
+
+out:
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ struct videobuf_queue *q = &ofh->vbq;
+ int rval;
+
+ atomic_inc(&cam->reset_disable);
+
+ flush_work_sync(&cam->sensor_reset_work);
+
+ rval = videobuf_streamoff(q);
+ if (!rval) {
+ mutex_lock(&cam->mutex);
+ cam->streaming = NULL;
+ mutex_unlock(&cam->mutex);
+ sysfs_notify(&cam->dev->kobj, NULL, "streaming");
+ }
+
+ atomic_dec(&cam->reset_disable);
+
+ return rval;
+}
+
+static int vidioc_enum_input(struct file *file, void *fh,
+ struct v4l2_input *inp)
+{
+ if (inp->index > 0)
+ return -EINVAL;
+
+ strlcpy(inp->name, "camera", sizeof(inp->name));
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
+{
+ *i = 0;
+
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int i)
+{
+ if (i > 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *fh,
+ struct v4l2_queryctrl *a)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ rval = vidioc_int_queryctrl(cam->sdev, a);
+
+ return rval;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *fh,
+ struct v4l2_control *a)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_g_ctrl(cam->sdev, a);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *fh,
+ struct v4l2_control *a)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_s_ctrl(cam->sdev, a);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a) {
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_g_parm(cam->sdev, a);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_s_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ struct v4l2_streamparm old_streamparm;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ rval = -EBUSY;
+ goto out;
+ }
+
+ old_streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ rval = vidioc_int_g_parm(cam->sdev, &old_streamparm);
+ if (rval)
+ goto out;
+
+ rval = vidioc_int_s_parm(cam->sdev, a);
+ if (rval)
+ goto out;
+
+ rval = omap24xxcam_sensor_if_enable(cam);
+ /*
+ * Revert to old streaming parameters if enabling sensor
+ * interface with the new ones failed.
+ */
+ if (rval)
+ vidioc_int_s_parm(cam->sdev, &old_streamparm);
+
+out:
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+/*
+ *
+ * File operations.
+ *
+ */
+
+static unsigned int omap24xxcam_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct omap24xxcam_fh *fh = file->private_data;
+ struct omap24xxcam_device *cam = fh->cam;
+ struct videobuf_buffer *vb;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming != file) {
+ mutex_unlock(&cam->mutex);
+ return POLLERR;
+ }
+ mutex_unlock(&cam->mutex);
+
+ mutex_lock(&fh->vbq.vb_lock);
+ if (list_empty(&fh->vbq.stream)) {
+ mutex_unlock(&fh->vbq.vb_lock);
+ return POLLERR;
+ }
+ vb = list_entry(fh->vbq.stream.next, struct videobuf_buffer, stream);
+ mutex_unlock(&fh->vbq.vb_lock);
+
+ poll_wait(file, &vb->done, wait);
+
+ if (vb->state == VIDEOBUF_DONE || vb->state == VIDEOBUF_ERROR)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+static int omap24xxcam_mmap_buffers(struct file *file,
+ struct vm_area_struct *vma)
+{
+ struct omap24xxcam_fh *fh = file->private_data;
+ struct omap24xxcam_device *cam = fh->cam;
+ struct videobuf_queue *vbq = &fh->vbq;
+ unsigned int first, last, size, i, j;
+ int err = 0;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ mutex_unlock(&cam->mutex);
+ return -EBUSY;
+ }
+ mutex_unlock(&cam->mutex);
+ mutex_lock(&vbq->vb_lock);
+
+ /* look for first buffer to map */
+ for (first = 0; first < VIDEO_MAX_FRAME; first++) {
+ if (NULL == vbq->bufs[first])
+ continue;
+ if (V4L2_MEMORY_MMAP != vbq->bufs[first]->memory)
+ continue;
+ if (vbq->bufs[first]->boff == (vma->vm_pgoff << PAGE_SHIFT))
+ break;
+ }
+
+ /* look for last buffer to map */
+ for (size = 0, last = first; last < VIDEO_MAX_FRAME; last++) {
+ if (NULL == vbq->bufs[last])
+ continue;
+ if (V4L2_MEMORY_MMAP != vbq->bufs[last]->memory)
+ continue;
+ size += vbq->bufs[last]->bsize;
+ if (size == (vma->vm_end - vma->vm_start))
+ break;
+ }
+
+ size = 0;
+ for (i = first; i <= last && i < VIDEO_MAX_FRAME; i++) {
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vbq->bufs[i]);
+
+ for (j = 0; j < dma->sglen; j++) {
+ err = remap_pfn_range(
+ vma, vma->vm_start + size,
+ page_to_pfn(sg_page(&dma->sglist[j])),
+ sg_dma_len(&dma->sglist[j]), vma->vm_page_prot);
+ if (err)
+ goto out;
+ size += sg_dma_len(&dma->sglist[j]);
+ }
+ }
+
+out:
+ mutex_unlock(&vbq->vb_lock);
+
+ return err;
+}
+
+static int omap24xxcam_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct omap24xxcam_fh *fh = file->private_data;
+ int rval;
+
+ /* let the video-buf mapper check arguments and set-up structures */
+ rval = videobuf_mmap_mapper(&fh->vbq, vma);
+ if (rval)
+ return rval;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ /* do mapping to our allocated buffers */
+ rval = omap24xxcam_mmap_buffers(file, vma);
+ /*
+ * In case of error, free vma->vm_private_data allocated by
+ * videobuf_mmap_mapper.
+ */
+ if (rval)
+ kfree(vma->vm_private_data);
+
+ return rval;
+}
+
+static int omap24xxcam_open(struct file *file)
+{
+ struct omap24xxcam_device *cam = omap24xxcam.priv;
+ struct omap24xxcam_fh *fh;
+ struct v4l2_format format;
+
+ if (!cam || !cam->vfd)
+ return -ENODEV;
+
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (fh == NULL)
+ return -ENOMEM;
+
+ mutex_lock(&cam->mutex);
+ if (cam->sdev == NULL || !try_module_get(cam->sdev->module)) {
+ mutex_unlock(&cam->mutex);
+ goto out_try_module_get;
+ }
+
+ if (atomic_inc_return(&cam->users) == 1) {
+ omap24xxcam_hwinit(cam);
+ if (omap24xxcam_sensor_enable(cam)) {
+ mutex_unlock(&cam->mutex);
+ goto out_omap24xxcam_sensor_enable;
+ }
+ }
+ mutex_unlock(&cam->mutex);
+
+ fh->cam = cam;
+ mutex_lock(&cam->mutex);
+ vidioc_int_g_fmt_cap(cam->sdev, &format);
+ mutex_unlock(&cam->mutex);
+ /* FIXME: how about fh->pix when there are more users? */
+ fh->pix = format.fmt.pix;
+
+ file->private_data = fh;
+
+ spin_lock_init(&fh->vbq_lock);
+
+ videobuf_queue_sg_init(&fh->vbq, &omap24xxcam_vbq_ops, NULL,
+ &fh->vbq_lock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_NONE,
+ sizeof(struct videobuf_buffer), fh, NULL);
+
+ return 0;
+
+out_omap24xxcam_sensor_enable:
+ omap24xxcam_poweron_reset(cam);
+ module_put(cam->sdev->module);
+
+out_try_module_get:
+ kfree(fh);
+
+ return -ENODEV;
+}
+
+static int omap24xxcam_release(struct file *file)
+{
+ struct omap24xxcam_fh *fh = file->private_data;
+ struct omap24xxcam_device *cam = fh->cam;
+
+ atomic_inc(&cam->reset_disable);
+
+ flush_work_sync(&cam->sensor_reset_work);
+
+ /* stop streaming capture */
+ videobuf_streamoff(&fh->vbq);
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming == file) {
+ cam->streaming = NULL;
+ mutex_unlock(&cam->mutex);
+ sysfs_notify(&cam->dev->kobj, NULL, "streaming");
+ } else {
+ mutex_unlock(&cam->mutex);
+ }
+
+ atomic_dec(&cam->reset_disable);
+
+ omap24xxcam_vbq_free_mmap_buffers(&fh->vbq);
+
+ /*
+ * Make sure the reset work we might have scheduled is not
+ * pending! It may be run *only* if we have users. (And it may
+ * not be scheduled anymore since streaming is already
+ * disabled.)
+ */
+ flush_work_sync(&cam->sensor_reset_work);
+
+ mutex_lock(&cam->mutex);
+ if (atomic_dec_return(&cam->users) == 0) {
+ omap24xxcam_sensor_disable(cam);
+ omap24xxcam_poweron_reset(cam);
+ }
+ mutex_unlock(&cam->mutex);
+
+ file->private_data = NULL;
+
+ module_put(cam->sdev->module);
+ kfree(fh);
+
+ return 0;
+}
+
+static struct v4l2_file_operations omap24xxcam_fops = {
+ .ioctl = video_ioctl2,
+ .poll = omap24xxcam_poll,
+ .mmap = omap24xxcam_mmap,
+ .open = omap24xxcam_open,
+ .release = omap24xxcam_release,
+};
+
+/*
+ *
+ * Power management.
+ *
+ */
+
+#ifdef CONFIG_PM
+static int omap24xxcam_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
+
+ if (atomic_read(&cam->users) == 0)
+ return 0;
+
+ if (!atomic_read(&cam->reset_disable))
+ omap24xxcam_capture_stop(cam);
+
+ omap24xxcam_sensor_disable(cam);
+ omap24xxcam_poweron_reset(cam);
+
+ return 0;
+}
+
+static int omap24xxcam_resume(struct platform_device *pdev)
+{
+ struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
+
+ if (atomic_read(&cam->users) == 0)
+ return 0;
+
+ omap24xxcam_hwinit(cam);
+ omap24xxcam_sensor_enable(cam);
+
+ if (!atomic_read(&cam->reset_disable))
+ omap24xxcam_capture_cont(cam);
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static const struct v4l2_ioctl_ops omap24xxcam_ioctl_fops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_parm = vidioc_g_parm,
+ .vidioc_s_parm = vidioc_s_parm,
+};
+
+/*
+ *
+ * Camera device (i.e. /dev/video).
+ *
+ */
+
+static int omap24xxcam_device_register(struct v4l2_int_device *s)
+{
+ struct omap24xxcam_device *cam = s->u.slave->master->priv;
+ struct video_device *vfd;
+ int rval;
+
+ /* We already have a slave. */
+ if (cam->sdev)
+ return -EBUSY;
+
+ cam->sdev = s;
+
+ if (device_create_file(cam->dev, &dev_attr_streaming) != 0) {
+ dev_err(cam->dev, "could not register sysfs entry\n");
+ rval = -EBUSY;
+ goto err;
+ }
+
+ /* initialize the video_device struct */
+ vfd = cam->vfd = video_device_alloc();
+ if (!vfd) {
+ dev_err(cam->dev, "could not allocate video device struct\n");
+ rval = -ENOMEM;
+ goto err;
+ }
+ vfd->release = video_device_release;
+
+ vfd->parent = cam->dev;
+
+ strlcpy(vfd->name, CAM_NAME, sizeof(vfd->name));
+ vfd->fops = &omap24xxcam_fops;
+ vfd->ioctl_ops = &omap24xxcam_ioctl_fops;
+
+ omap24xxcam_hwinit(cam);
+
+ rval = omap24xxcam_sensor_init(cam);
+ if (rval)
+ goto err;
+
+ if (video_register_device(vfd, VFL_TYPE_GRABBER, video_nr) < 0) {
+ dev_err(cam->dev, "could not register V4L device\n");
+ rval = -EBUSY;
+ goto err;
+ }
+
+ omap24xxcam_poweron_reset(cam);
+
+ dev_info(cam->dev, "registered device %s\n",
+ video_device_node_name(vfd));
+
+ return 0;
+
+err:
+ omap24xxcam_device_unregister(s);
+
+ return rval;
+}
+
+static void omap24xxcam_device_unregister(struct v4l2_int_device *s)
+{
+ struct omap24xxcam_device *cam = s->u.slave->master->priv;
+
+ omap24xxcam_sensor_exit(cam);
+
+ if (cam->vfd) {
+ if (!video_is_registered(cam->vfd)) {
+ /*
+ * The device was never registered, so release the
+ * video_device struct directly.
+ */
+ video_device_release(cam->vfd);
+ } else {
+ /*
+ * The unregister function will release the
+ * video_device struct as well as
+ * unregistering it.
+ */
+ video_unregister_device(cam->vfd);
+ }
+ cam->vfd = NULL;
+ }
+
+ device_remove_file(cam->dev, &dev_attr_streaming);
+
+ cam->sdev = NULL;
+}
+
+static struct v4l2_int_master omap24xxcam_master = {
+ .attach = omap24xxcam_device_register,
+ .detach = omap24xxcam_device_unregister,
+};
+
+static struct v4l2_int_device omap24xxcam = {
+ .module = THIS_MODULE,
+ .name = CAM_NAME,
+ .type = v4l2_int_type_master,
+ .u = {
+ .master = &omap24xxcam_master
+ },
+};
+
+/*
+ *
+ * Driver initialisation and deinitialisation.
+ *
+ */
+
+static int __devinit omap24xxcam_probe(struct platform_device *pdev)
+{
+ struct omap24xxcam_device *cam;
+ struct resource *mem;
+ int irq;
+
+ cam = kzalloc(sizeof(*cam), GFP_KERNEL);
+ if (!cam) {
+ dev_err(&pdev->dev, "could not allocate memory\n");
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, cam);
+
+ cam->dev = &pdev->dev;
+
+ /*
+ * Impose a lower limit on the amount of memory allocated for
+ * capture. We require at least enough memory to double-buffer
+ * QVGA (300KB).
+ */
+ if (capture_mem < 320 * 240 * 2 * 2)
+ capture_mem = 320 * 240 * 2 * 2;
+ cam->capture_mem = capture_mem;
+
+ /* request the mem region for the camera registers */
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(cam->dev, "no mem resource?\n");
+ goto err;
+ }
+ if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) {
+ dev_err(cam->dev,
+ "cannot reserve camera register I/O region\n");
+ goto err;
+ }
+ cam->mmio_base_phys = mem->start;
+ cam->mmio_size = resource_size(mem);
+
+ /* map the region */
+ cam->mmio_base = (unsigned long)
+ ioremap_nocache(cam->mmio_base_phys, cam->mmio_size);
+ if (!cam->mmio_base) {
+ dev_err(cam->dev, "cannot map camera register I/O region\n");
+ goto err;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_err(cam->dev, "no irq for camera?\n");
+ goto err;
+ }
+
+ /* install the interrupt service routine */
+ if (request_irq(irq, omap24xxcam_isr, 0, CAM_NAME, cam)) {
+ dev_err(cam->dev,
+ "could not install interrupt service routine\n");
+ goto err;
+ }
+ cam->irq = irq;
+
+ if (omap24xxcam_clock_get(cam))
+ goto err;
+
+ INIT_WORK(&cam->sensor_reset_work, omap24xxcam_sensor_reset_work);
+
+ mutex_init(&cam->mutex);
+ spin_lock_init(&cam->core_enable_disable_lock);
+
+ omap24xxcam_sgdma_init(&cam->sgdma,
+ cam->mmio_base + CAMDMA_REG_OFFSET,
+ omap24xxcam_stalled_dma_reset,
+ (unsigned long)cam);
+
+ omap24xxcam.priv = cam;
+
+ if (v4l2_int_device_register(&omap24xxcam))
+ goto err;
+
+ return 0;
+
+err:
+ omap24xxcam_remove(pdev);
+ return -ENODEV;
+}
+
+static int omap24xxcam_remove(struct platform_device *pdev)
+{
+ struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
+
+ if (!cam)
+ return 0;
+
+ if (omap24xxcam.priv != NULL)
+ v4l2_int_device_unregister(&omap24xxcam);
+ omap24xxcam.priv = NULL;
+
+ omap24xxcam_clock_put(cam);
+
+ if (cam->irq) {
+ free_irq(cam->irq, cam);
+ cam->irq = 0;
+ }
+
+ if (cam->mmio_base) {
+ iounmap((void *)cam->mmio_base);
+ cam->mmio_base = 0;
+ }
+
+ if (cam->mmio_base_phys) {
+ release_mem_region(cam->mmio_base_phys, cam->mmio_size);
+ cam->mmio_base_phys = 0;
+ }
+
+ kfree(cam);
+
+ return 0;
+}
+
+static struct platform_driver omap24xxcam_driver = {
+ .probe = omap24xxcam_probe,
+ .remove = omap24xxcam_remove,
+#ifdef CONFIG_PM
+ .suspend = omap24xxcam_suspend,
+ .resume = omap24xxcam_resume,
+#endif
+ .driver = {
+ .name = CAM_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(omap24xxcam_driver);
+
+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
+MODULE_DESCRIPTION("OMAP24xx Video for Linux camera driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(OMAP24XXCAM_VERSION);
+module_param(video_nr, int, 0);
+MODULE_PARM_DESC(video_nr,
+ "Minor number for video device (-1 ==> auto assign)");
+module_param(capture_mem, int, 0);
+MODULE_PARM_DESC(capture_mem, "Maximum amount of memory for capture "
+ "buffers (default 4800kiB)");
diff --git a/drivers/media/video/omap24xxcam.h b/drivers/media/video/omap24xxcam.h
new file mode 100644
index 00000000..2ce67f5a
--- /dev/null
+++ b/drivers/media/video/omap24xxcam.h
@@ -0,0 +1,593 @@
+/*
+ * drivers/media/video/omap24xxcam.h
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+ *
+ * Based on code from Andy Lowe <source@mvista.com>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP24XXCAM_H
+#define OMAP24XXCAM_H
+
+#include <media/videobuf-dma-sg.h>
+#include <media/v4l2-int-device.h>
+
+/*
+ *
+ * General driver related definitions.
+ *
+ */
+
+#define CAM_NAME "omap24xxcam"
+
+#define CAM_MCLK 96000000
+
+/* number of bytes transferred per DMA request */
+#define DMA_THRESHOLD 32
+
+/*
+ * NUM_CAMDMA_CHANNELS is the number of logical channels provided by
+ * the camera DMA controller.
+ */
+#define NUM_CAMDMA_CHANNELS 4
+
+/*
+ * NUM_SG_DMA is the number of scatter-gather DMA transfers that can
+ * be queued. (We don't have any overlay sglists now.)
+ */
+#define NUM_SG_DMA (VIDEO_MAX_FRAME)
+
+/*
+ *
+ * Register definitions.
+ *
+ */
+
+/* subsystem register block offsets */
+#define CC_REG_OFFSET 0x00000400
+#define CAMDMA_REG_OFFSET 0x00000800
+#define CAMMMU_REG_OFFSET 0x00000C00
+
+/* define camera subsystem register offsets */
+#define CAM_REVISION 0x000
+#define CAM_SYSCONFIG 0x010
+#define CAM_SYSSTATUS 0x014
+#define CAM_IRQSTATUS 0x018
+#define CAM_GPO 0x040
+#define CAM_GPI 0x050
+
+/* define camera core register offsets */
+#define CC_REVISION 0x000
+#define CC_SYSCONFIG 0x010
+#define CC_SYSSTATUS 0x014
+#define CC_IRQSTATUS 0x018
+#define CC_IRQENABLE 0x01C
+#define CC_CTRL 0x040
+#define CC_CTRL_DMA 0x044
+#define CC_CTRL_XCLK 0x048
+#define CC_FIFODATA 0x04C
+#define CC_TEST 0x050
+#define CC_GENPAR 0x054
+#define CC_CCPFSCR 0x058
+#define CC_CCPFECR 0x05C
+#define CC_CCPLSCR 0x060
+#define CC_CCPLECR 0x064
+#define CC_CCPDFR 0x068
+
+/* define camera dma register offsets */
+#define CAMDMA_REVISION 0x000
+#define CAMDMA_IRQSTATUS_L0 0x008
+#define CAMDMA_IRQSTATUS_L1 0x00C
+#define CAMDMA_IRQSTATUS_L2 0x010
+#define CAMDMA_IRQSTATUS_L3 0x014
+#define CAMDMA_IRQENABLE_L0 0x018
+#define CAMDMA_IRQENABLE_L1 0x01C
+#define CAMDMA_IRQENABLE_L2 0x020
+#define CAMDMA_IRQENABLE_L3 0x024
+#define CAMDMA_SYSSTATUS 0x028
+#define CAMDMA_OCP_SYSCONFIG 0x02C
+#define CAMDMA_CAPS_0 0x064
+#define CAMDMA_CAPS_2 0x06C
+#define CAMDMA_CAPS_3 0x070
+#define CAMDMA_CAPS_4 0x074
+#define CAMDMA_GCR 0x078
+#define CAMDMA_CCR(n) (0x080 + (n)*0x60)
+#define CAMDMA_CLNK_CTRL(n) (0x084 + (n)*0x60)
+#define CAMDMA_CICR(n) (0x088 + (n)*0x60)
+#define CAMDMA_CSR(n) (0x08C + (n)*0x60)
+#define CAMDMA_CSDP(n) (0x090 + (n)*0x60)
+#define CAMDMA_CEN(n) (0x094 + (n)*0x60)
+#define CAMDMA_CFN(n) (0x098 + (n)*0x60)
+#define CAMDMA_CSSA(n) (0x09C + (n)*0x60)
+#define CAMDMA_CDSA(n) (0x0A0 + (n)*0x60)
+#define CAMDMA_CSEI(n) (0x0A4 + (n)*0x60)
+#define CAMDMA_CSFI(n) (0x0A8 + (n)*0x60)
+#define CAMDMA_CDEI(n) (0x0AC + (n)*0x60)
+#define CAMDMA_CDFI(n) (0x0B0 + (n)*0x60)
+#define CAMDMA_CSAC(n) (0x0B4 + (n)*0x60)
+#define CAMDMA_CDAC(n) (0x0B8 + (n)*0x60)
+#define CAMDMA_CCEN(n) (0x0BC + (n)*0x60)
+#define CAMDMA_CCFN(n) (0x0C0 + (n)*0x60)
+#define CAMDMA_COLOR(n) (0x0C4 + (n)*0x60)
+
+/* define camera mmu register offsets */
+#define CAMMMU_REVISION 0x000
+#define CAMMMU_SYSCONFIG 0x010
+#define CAMMMU_SYSSTATUS 0x014
+#define CAMMMU_IRQSTATUS 0x018
+#define CAMMMU_IRQENABLE 0x01C
+#define CAMMMU_WALKING_ST 0x040
+#define CAMMMU_CNTL 0x044
+#define CAMMMU_FAULT_AD 0x048
+#define CAMMMU_TTB 0x04C
+#define CAMMMU_LOCK 0x050
+#define CAMMMU_LD_TLB 0x054
+#define CAMMMU_CAM 0x058
+#define CAMMMU_RAM 0x05C
+#define CAMMMU_GFLUSH 0x060
+#define CAMMMU_FLUSH_ENTRY 0x064
+#define CAMMMU_READ_CAM 0x068
+#define CAMMMU_READ_RAM 0x06C
+#define CAMMMU_EMU_FAULT_AD 0x070
+
+/* Define bit fields within selected registers */
+#define CAM_REVISION_MAJOR (15 << 4)
+#define CAM_REVISION_MAJOR_SHIFT 4
+#define CAM_REVISION_MINOR (15 << 0)
+#define CAM_REVISION_MINOR_SHIFT 0
+
+#define CAM_SYSCONFIG_SOFTRESET (1 << 1)
+#define CAM_SYSCONFIG_AUTOIDLE (1 << 0)
+
+#define CAM_SYSSTATUS_RESETDONE (1 << 0)
+
+#define CAM_IRQSTATUS_CC_IRQ (1 << 4)
+#define CAM_IRQSTATUS_MMU_IRQ (1 << 3)
+#define CAM_IRQSTATUS_DMA_IRQ2 (1 << 2)
+#define CAM_IRQSTATUS_DMA_IRQ1 (1 << 1)
+#define CAM_IRQSTATUS_DMA_IRQ0 (1 << 0)
+
+#define CAM_GPO_CAM_S_P_EN (1 << 1)
+#define CAM_GPO_CAM_CCP_MODE (1 << 0)
+
+#define CAM_GPI_CC_DMA_REQ1 (1 << 24)
+#define CAP_GPI_CC_DMA_REQ0 (1 << 23)
+#define CAP_GPI_CAM_MSTANDBY (1 << 21)
+#define CAP_GPI_CAM_WAIT (1 << 20)
+#define CAP_GPI_CAM_S_DATA (1 << 17)
+#define CAP_GPI_CAM_S_CLK (1 << 16)
+#define CAP_GPI_CAM_P_DATA (0xFFF << 3)
+#define CAP_GPI_CAM_P_DATA_SHIFT 3
+#define CAP_GPI_CAM_P_VS (1 << 2)
+#define CAP_GPI_CAM_P_HS (1 << 1)
+#define CAP_GPI_CAM_P_CLK (1 << 0)
+
+#define CC_REVISION_MAJOR (15 << 4)
+#define CC_REVISION_MAJOR_SHIFT 4
+#define CC_REVISION_MINOR (15 << 0)
+#define CC_REVISION_MINOR_SHIFT 0
+
+#define CC_SYSCONFIG_SIDLEMODE (3 << 3)
+#define CC_SYSCONFIG_SIDLEMODE_FIDLE (0 << 3)
+#define CC_SYSCONFIG_SIDLEMODE_NIDLE (1 << 3)
+#define CC_SYSCONFIG_SOFTRESET (1 << 1)
+#define CC_SYSCONFIG_AUTOIDLE (1 << 0)
+
+#define CC_SYSSTATUS_RESETDONE (1 << 0)
+
+#define CC_IRQSTATUS_FS_IRQ (1 << 19)
+#define CC_IRQSTATUS_LE_IRQ (1 << 18)
+#define CC_IRQSTATUS_LS_IRQ (1 << 17)
+#define CC_IRQSTATUS_FE_IRQ (1 << 16)
+#define CC_IRQSTATUS_FW_ERR_IRQ (1 << 10)
+#define CC_IRQSTATUS_FSC_ERR_IRQ (1 << 9)
+#define CC_IRQSTATUS_SSC_ERR_IRQ (1 << 8)
+#define CC_IRQSTATUS_FIFO_NOEMPTY_IRQ (1 << 4)
+#define CC_IRQSTATUS_FIFO_FULL_IRQ (1 << 3)
+#define CC_IRQSTATUS_FIFO_THR_IRQ (1 << 2)
+#define CC_IRQSTATUS_FIFO_OF_IRQ (1 << 1)
+#define CC_IRQSTATUS_FIFO_UF_IRQ (1 << 0)
+
+#define CC_IRQENABLE_FS_IRQ (1 << 19)
+#define CC_IRQENABLE_LE_IRQ (1 << 18)
+#define CC_IRQENABLE_LS_IRQ (1 << 17)
+#define CC_IRQENABLE_FE_IRQ (1 << 16)
+#define CC_IRQENABLE_FW_ERR_IRQ (1 << 10)
+#define CC_IRQENABLE_FSC_ERR_IRQ (1 << 9)
+#define CC_IRQENABLE_SSC_ERR_IRQ (1 << 8)
+#define CC_IRQENABLE_FIFO_NOEMPTY_IRQ (1 << 4)
+#define CC_IRQENABLE_FIFO_FULL_IRQ (1 << 3)
+#define CC_IRQENABLE_FIFO_THR_IRQ (1 << 2)
+#define CC_IRQENABLE_FIFO_OF_IRQ (1 << 1)
+#define CC_IRQENABLE_FIFO_UF_IRQ (1 << 0)
+
+#define CC_CTRL_CC_ONE_SHOT (1 << 20)
+#define CC_CTRL_CC_IF_SYNCHRO (1 << 19)
+#define CC_CTRL_CC_RST (1 << 18)
+#define CC_CTRL_CC_FRAME_TRIG (1 << 17)
+#define CC_CTRL_CC_EN (1 << 16)
+#define CC_CTRL_NOBT_SYNCHRO (1 << 13)
+#define CC_CTRL_BT_CORRECT (1 << 12)
+#define CC_CTRL_PAR_ORDERCAM (1 << 11)
+#define CC_CTRL_PAR_CLK_POL (1 << 10)
+#define CC_CTRL_NOBT_HS_POL (1 << 9)
+#define CC_CTRL_NOBT_VS_POL (1 << 8)
+#define CC_CTRL_PAR_MODE (7 << 1)
+#define CC_CTRL_PAR_MODE_SHIFT 1
+#define CC_CTRL_PAR_MODE_NOBT8 (0 << 1)
+#define CC_CTRL_PAR_MODE_NOBT10 (1 << 1)
+#define CC_CTRL_PAR_MODE_NOBT12 (2 << 1)
+#define CC_CTRL_PAR_MODE_BT8 (4 << 1)
+#define CC_CTRL_PAR_MODE_BT10 (5 << 1)
+#define CC_CTRL_PAR_MODE_FIFOTEST (7 << 1)
+#define CC_CTRL_CCP_MODE (1 << 0)
+
+#define CC_CTRL_DMA_EN (1 << 8)
+#define CC_CTRL_DMA_FIFO_THRESHOLD (0x7F << 0)
+#define CC_CTRL_DMA_FIFO_THRESHOLD_SHIFT 0
+
+#define CC_CTRL_XCLK_DIV (0x1F << 0)
+#define CC_CTRL_XCLK_DIV_SHIFT 0
+#define CC_CTRL_XCLK_DIV_STABLE_LOW (0 << 0)
+#define CC_CTRL_XCLK_DIV_STABLE_HIGH (1 << 0)
+#define CC_CTRL_XCLK_DIV_BYPASS (31 << 0)
+
+#define CC_TEST_FIFO_RD_POINTER (0xFF << 24)
+#define CC_TEST_FIFO_RD_POINTER_SHIFT 24
+#define CC_TEST_FIFO_WR_POINTER (0xFF << 16)
+#define CC_TEST_FIFO_WR_POINTER_SHIFT 16
+#define CC_TEST_FIFO_LEVEL (0xFF << 8)
+#define CC_TEST_FIFO_LEVEL_SHIFT 8
+#define CC_TEST_FIFO_LEVEL_PEAK (0xFF << 0)
+#define CC_TEST_FIFO_LEVEL_PEAK_SHIFT 0
+
+#define CC_GENPAR_FIFO_DEPTH (7 << 0)
+#define CC_GENPAR_FIFO_DEPTH_SHIFT 0
+
+#define CC_CCPDFR_ALPHA (0xFF << 8)
+#define CC_CCPDFR_ALPHA_SHIFT 8
+#define CC_CCPDFR_DATAFORMAT (15 << 0)
+#define CC_CCPDFR_DATAFORMAT_SHIFT 0
+#define CC_CCPDFR_DATAFORMAT_YUV422BE (0 << 0)
+#define CC_CCPDFR_DATAFORMAT_YUV422 (1 << 0)
+#define CC_CCPDFR_DATAFORMAT_YUV420 (2 << 0)
+#define CC_CCPDFR_DATAFORMAT_RGB444 (4 << 0)
+#define CC_CCPDFR_DATAFORMAT_RGB565 (5 << 0)
+#define CC_CCPDFR_DATAFORMAT_RGB888NDE (6 << 0)
+#define CC_CCPDFR_DATAFORMAT_RGB888 (7 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW8NDE (8 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW8 (9 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW10NDE (10 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW10 (11 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW12NDE (12 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW12 (13 << 0)
+#define CC_CCPDFR_DATAFORMAT_JPEG8 (15 << 0)
+
+#define CAMDMA_REVISION_MAJOR (15 << 4)
+#define CAMDMA_REVISION_MAJOR_SHIFT 4
+#define CAMDMA_REVISION_MINOR (15 << 0)
+#define CAMDMA_REVISION_MINOR_SHIFT 0
+
+#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE (3 << 12)
+#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY (0 << 12)
+#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_NSTANDBY (1 << 12)
+#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_SSTANDBY (2 << 12)
+#define CAMDMA_OCP_SYSCONFIG_FUNC_CLOCK (1 << 9)
+#define CAMDMA_OCP_SYSCONFIG_OCP_CLOCK (1 << 8)
+#define CAMDMA_OCP_SYSCONFIG_EMUFREE (1 << 5)
+#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE (3 << 3)
+#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_FIDLE (0 << 3)
+#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_NIDLE (1 << 3)
+#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_SIDLE (2 << 3)
+#define CAMDMA_OCP_SYSCONFIG_SOFTRESET (1 << 1)
+#define CAMDMA_OCP_SYSCONFIG_AUTOIDLE (1 << 0)
+
+#define CAMDMA_SYSSTATUS_RESETDONE (1 << 0)
+
+#define CAMDMA_GCR_ARBITRATION_RATE (0xFF << 16)
+#define CAMDMA_GCR_ARBITRATION_RATE_SHIFT 16
+#define CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH (0xFF << 0)
+#define CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH_SHIFT 0
+
+#define CAMDMA_CCR_SEL_SRC_DST_SYNC (1 << 24)
+#define CAMDMA_CCR_PREFETCH (1 << 23)
+#define CAMDMA_CCR_SUPERVISOR (1 << 22)
+#define CAMDMA_CCR_SECURE (1 << 21)
+#define CAMDMA_CCR_BS (1 << 18)
+#define CAMDMA_CCR_TRANSPARENT_COPY_ENABLE (1 << 17)
+#define CAMDMA_CCR_CONSTANT_FILL_ENABLE (1 << 16)
+#define CAMDMA_CCR_DST_AMODE (3 << 14)
+#define CAMDMA_CCR_DST_AMODE_CONST_ADDR (0 << 14)
+#define CAMDMA_CCR_DST_AMODE_POST_INC (1 << 14)
+#define CAMDMA_CCR_DST_AMODE_SGL_IDX (2 << 14)
+#define CAMDMA_CCR_DST_AMODE_DBL_IDX (3 << 14)
+#define CAMDMA_CCR_SRC_AMODE (3 << 12)
+#define CAMDMA_CCR_SRC_AMODE_CONST_ADDR (0 << 12)
+#define CAMDMA_CCR_SRC_AMODE_POST_INC (1 << 12)
+#define CAMDMA_CCR_SRC_AMODE_SGL_IDX (2 << 12)
+#define CAMDMA_CCR_SRC_AMODE_DBL_IDX (3 << 12)
+#define CAMDMA_CCR_WR_ACTIVE (1 << 10)
+#define CAMDMA_CCR_RD_ACTIVE (1 << 9)
+#define CAMDMA_CCR_SUSPEND_SENSITIVE (1 << 8)
+#define CAMDMA_CCR_ENABLE (1 << 7)
+#define CAMDMA_CCR_PRIO (1 << 6)
+#define CAMDMA_CCR_FS (1 << 5)
+#define CAMDMA_CCR_SYNCHRO ((3 << 19) | (31 << 0))
+#define CAMDMA_CCR_SYNCHRO_CAMERA 0x01
+
+#define CAMDMA_CLNK_CTRL_ENABLE_LNK (1 << 15)
+#define CAMDMA_CLNK_CTRL_NEXTLCH_ID (0x1F << 0)
+#define CAMDMA_CLNK_CTRL_NEXTLCH_ID_SHIFT 0
+
+#define CAMDMA_CICR_MISALIGNED_ERR_IE (1 << 11)
+#define CAMDMA_CICR_SUPERVISOR_ERR_IE (1 << 10)
+#define CAMDMA_CICR_SECURE_ERR_IE (1 << 9)
+#define CAMDMA_CICR_TRANS_ERR_IE (1 << 8)
+#define CAMDMA_CICR_PACKET_IE (1 << 7)
+#define CAMDMA_CICR_BLOCK_IE (1 << 5)
+#define CAMDMA_CICR_LAST_IE (1 << 4)
+#define CAMDMA_CICR_FRAME_IE (1 << 3)
+#define CAMDMA_CICR_HALF_IE (1 << 2)
+#define CAMDMA_CICR_DROP_IE (1 << 1)
+
+#define CAMDMA_CSR_MISALIGNED_ERR (1 << 11)
+#define CAMDMA_CSR_SUPERVISOR_ERR (1 << 10)
+#define CAMDMA_CSR_SECURE_ERR (1 << 9)
+#define CAMDMA_CSR_TRANS_ERR (1 << 8)
+#define CAMDMA_CSR_PACKET (1 << 7)
+#define CAMDMA_CSR_SYNC (1 << 6)
+#define CAMDMA_CSR_BLOCK (1 << 5)
+#define CAMDMA_CSR_LAST (1 << 4)
+#define CAMDMA_CSR_FRAME (1 << 3)
+#define CAMDMA_CSR_HALF (1 << 2)
+#define CAMDMA_CSR_DROP (1 << 1)
+
+#define CAMDMA_CSDP_SRC_ENDIANNESS (1 << 21)
+#define CAMDMA_CSDP_SRC_ENDIANNESS_LOCK (1 << 20)
+#define CAMDMA_CSDP_DST_ENDIANNESS (1 << 19)
+#define CAMDMA_CSDP_DST_ENDIANNESS_LOCK (1 << 18)
+#define CAMDMA_CSDP_WRITE_MODE (3 << 16)
+#define CAMDMA_CSDP_WRITE_MODE_WRNP (0 << 16)
+#define CAMDMA_CSDP_WRITE_MODE_POSTED (1 << 16)
+#define CAMDMA_CSDP_WRITE_MODE_POSTED_LAST_WRNP (2 << 16)
+#define CAMDMA_CSDP_DST_BURST_EN (3 << 14)
+#define CAMDMA_CSDP_DST_BURST_EN_1 (0 << 14)
+#define CAMDMA_CSDP_DST_BURST_EN_16 (1 << 14)
+#define CAMDMA_CSDP_DST_BURST_EN_32 (2 << 14)
+#define CAMDMA_CSDP_DST_BURST_EN_64 (3 << 14)
+#define CAMDMA_CSDP_DST_PACKED (1 << 13)
+#define CAMDMA_CSDP_WR_ADD_TRSLT (15 << 9)
+#define CAMDMA_CSDP_WR_ADD_TRSLT_ENABLE_MREQADD (3 << 9)
+#define CAMDMA_CSDP_SRC_BURST_EN (3 << 7)
+#define CAMDMA_CSDP_SRC_BURST_EN_1 (0 << 7)
+#define CAMDMA_CSDP_SRC_BURST_EN_16 (1 << 7)
+#define CAMDMA_CSDP_SRC_BURST_EN_32 (2 << 7)
+#define CAMDMA_CSDP_SRC_BURST_EN_64 (3 << 7)
+#define CAMDMA_CSDP_SRC_PACKED (1 << 6)
+#define CAMDMA_CSDP_RD_ADD_TRSLT (15 << 2)
+#define CAMDMA_CSDP_RD_ADD_TRSLT_ENABLE_MREQADD (3 << 2)
+#define CAMDMA_CSDP_DATA_TYPE (3 << 0)
+#define CAMDMA_CSDP_DATA_TYPE_8BITS (0 << 0)
+#define CAMDMA_CSDP_DATA_TYPE_16BITS (1 << 0)
+#define CAMDMA_CSDP_DATA_TYPE_32BITS (2 << 0)
+
+#define CAMMMU_SYSCONFIG_AUTOIDLE (1 << 0)
+
+/*
+ *
+ * Declarations.
+ *
+ */
+
+/* forward declarations */
+struct omap24xxcam_sgdma;
+struct omap24xxcam_dma;
+
+typedef void (*sgdma_callback_t)(struct omap24xxcam_sgdma *cam,
+ u32 status, void *arg);
+typedef void (*dma_callback_t)(struct omap24xxcam_dma *cam,
+ u32 status, void *arg);
+
+struct channel_state {
+ dma_callback_t callback;
+ void *arg;
+};
+
+/* sgdma state for each of the possible videobuf_buffers + 2 overlays */
+struct sgdma_state {
+ const struct scatterlist *sglist;
+ int sglen; /* number of sglist entries */
+ int next_sglist; /* index of next sglist entry to process */
+ unsigned int bytes_read; /* number of bytes read */
+ unsigned int len; /* total length of sglist (excluding
+ * bytes due to page alignment) */
+ int queued_sglist; /* number of sglist entries queued for DMA */
+ u32 csr; /* DMA return code */
+ sgdma_callback_t callback;
+ void *arg;
+};
+
+/* physical DMA channel management */
+struct omap24xxcam_dma {
+ spinlock_t lock; /* Lock for the whole structure. */
+
+ unsigned long base; /* base address for dma controller */
+
+ /* While dma_stop!=0, an attempt to start a new DMA transfer will
+ * fail.
+ */
+ atomic_t dma_stop;
+ int free_dmach; /* number of dma channels free */
+ int next_dmach; /* index of next dma channel to use */
+ struct channel_state ch_state[NUM_CAMDMA_CHANNELS];
+};
+
+/* scatter-gather DMA (scatterlist stuff) management */
+struct omap24xxcam_sgdma {
+ struct omap24xxcam_dma dma;
+
+ spinlock_t lock; /* Lock for the fields below. */
+ int free_sgdma; /* number of free sg dma slots */
+ int next_sgdma; /* index of next sg dma slot to use */
+ struct sgdma_state sg_state[NUM_SG_DMA];
+
+ /* Reset timer data */
+ struct timer_list reset_timer;
+};
+
+/* per-device data structure */
+struct omap24xxcam_device {
+ /*** mutex ***/
+ /*
+ * mutex serialises access to this structure. Also camera
+ * opening and releasing is synchronised by this.
+ */
+ struct mutex mutex;
+
+ /*** general driver state information ***/
+ atomic_t users;
+ /*
+ * Lock to serialise core enabling and disabling and access to
+ * sgdma_in_queue.
+ */
+ spinlock_t core_enable_disable_lock;
+ /*
+ * Number or sgdma requests in scatter-gather queue, protected
+ * by the lock above.
+ */
+ int sgdma_in_queue;
+ /*
+ * Sensor interface parameters: interface type, CC_CTRL
+ * register value and interface specific data.
+ */
+ int if_type;
+ union {
+ struct parallel {
+ u32 xclk;
+ } bt656;
+ } if_u;
+ u32 cc_ctrl;
+
+ /*** subsystem structures ***/
+ struct omap24xxcam_sgdma sgdma;
+
+ /*** hardware resources ***/
+ unsigned int irq;
+ unsigned long mmio_base;
+ unsigned long mmio_base_phys;
+ unsigned long mmio_size;
+
+ /*** interfaces and device ***/
+ struct v4l2_int_device *sdev;
+ struct device *dev;
+ struct video_device *vfd;
+
+ /*** camera and sensor reset related stuff ***/
+ struct work_struct sensor_reset_work;
+ /*
+ * We're in the middle of a reset. Don't enable core if this
+ * is non-zero! This exists to help decisionmaking in a case
+ * where videobuf_qbuf is called while we are in the middle of
+ * a reset.
+ */
+ atomic_t in_reset;
+ /*
+ * Non-zero if we don't want any resets for now. Used to
+ * prevent reset work to run when we're about to stop
+ * streaming.
+ */
+ atomic_t reset_disable;
+
+ /*** video device parameters ***/
+ int capture_mem;
+
+ /*** camera module clocks ***/
+ struct clk *fck;
+ struct clk *ick;
+
+ /*** capture data ***/
+ /* file handle, if streaming is on */
+ struct file *streaming;
+};
+
+/* Per-file handle data. */
+struct omap24xxcam_fh {
+ spinlock_t vbq_lock; /* spinlock for the videobuf queue */
+ struct videobuf_queue vbq;
+ struct v4l2_pix_format pix; /* serialise pix by vbq->lock */
+ atomic_t field_count; /* field counter for videobuf_buffer */
+ /* accessing cam here doesn't need serialisation: it's constant */
+ struct omap24xxcam_device *cam;
+};
+
+/*
+ *
+ * Register I/O functions.
+ *
+ */
+
+static inline u32 omap24xxcam_reg_in(unsigned long base, u32 offset)
+{
+ return readl(base + offset);
+}
+
+static inline u32 omap24xxcam_reg_out(unsigned long base, u32 offset,
+ u32 val)
+{
+ writel(val, base + offset);
+ return val;
+}
+
+static inline u32 omap24xxcam_reg_merge(unsigned long base, u32 offset,
+ u32 val, u32 mask)
+{
+ u32 addr = base + offset;
+ u32 new_val = (readl(addr) & ~mask) | (val & mask);
+
+ writel(new_val, addr);
+ return new_val;
+}
+
+/*
+ *
+ * Function prototypes.
+ *
+ */
+
+/* dma prototypes */
+
+void omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma);
+void omap24xxcam_dma_isr(struct omap24xxcam_dma *dma);
+
+/* sgdma prototypes */
+
+void omap24xxcam_sgdma_process(struct omap24xxcam_sgdma *sgdma);
+int omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma,
+ const struct scatterlist *sglist, int sglen,
+ int len, sgdma_callback_t callback, void *arg);
+void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma);
+void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma,
+ unsigned long base,
+ void (*reset_callback)(unsigned long data),
+ unsigned long reset_callback_data);
+void omap24xxcam_sgdma_exit(struct omap24xxcam_sgdma *sgdma);
+
+#endif
diff --git a/drivers/media/video/omap3isp/Makefile b/drivers/media/video/omap3isp/Makefile
new file mode 100644
index 00000000..e8847e79
--- /dev/null
+++ b/drivers/media/video/omap3isp/Makefile
@@ -0,0 +1,11 @@
+# Makefile for OMAP3 ISP driver
+
+ccflags-$(CONFIG_VIDEO_OMAP3_DEBUG) += -DDEBUG
+
+omap3-isp-objs += \
+ isp.o ispqueue.o ispvideo.o \
+ ispcsiphy.o ispccp2.o ispcsi2.o \
+ ispccdc.o isppreview.o ispresizer.o \
+ ispstat.o isph3a_aewb.o isph3a_af.o isphist.o
+
+obj-$(CONFIG_VIDEO_OMAP3) += omap3-isp.o
diff --git a/drivers/media/video/omap3isp/cfa_coef_table.h b/drivers/media/video/omap3isp/cfa_coef_table.h
new file mode 100644
index 00000000..c60df0ed
--- /dev/null
+++ b/drivers/media/video/omap3isp/cfa_coef_table.h
@@ -0,0 +1,61 @@
+/*
+ * cfa_coef_table.h
+ *
+ * TI OMAP3 ISP - CFA coefficients table
+ *
+ * Copyright (C) 2009-2010 Nokia Corporation
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+244, 0, 247, 0, 12, 27, 36, 247, 250, 0, 27, 0, 4, 250, 12, 244,
+248, 0, 0, 0, 0, 40, 0, 0, 244, 12, 250, 4, 0, 27, 0, 250,
+247, 36, 27, 12, 0, 247, 0, 244, 0, 0, 40, 0, 0, 0, 0, 248,
+244, 0, 247, 0, 12, 27, 36, 247, 250, 0, 27, 0, 4, 250, 12, 244,
+248, 0, 0, 0, 0, 40, 0, 0, 244, 12, 250, 4, 0, 27, 0, 250,
+247, 36, 27, 12, 0, 247, 0, 244, 0, 0, 40, 0, 0, 0, 0, 248,
+244, 0, 247, 0, 12, 27, 36, 247, 250, 0, 27, 0, 4, 250, 12, 244,
+248, 0, 0, 0, 0, 40, 0, 0, 244, 12, 250, 4, 0, 27, 0, 250,
+247, 36, 27, 12, 0, 247, 0, 244, 0, 0, 40, 0, 0, 0, 0, 248,
+ 0, 247, 0, 244, 247, 36, 27, 12, 0, 27, 0, 250, 244, 12, 250, 4,
+ 0, 0, 0, 248, 0, 0, 40, 0, 4, 250, 12, 244, 250, 0, 27, 0,
+ 12, 27, 36, 247, 244, 0, 247, 0, 0, 40, 0, 0, 248, 0, 0, 0,
+ 0, 247, 0, 244, 247, 36, 27, 12, 0, 27, 0, 250, 244, 12, 250, 4,
+ 0, 0, 0, 248, 0, 0, 40, 0, 4, 250, 12, 244, 250, 0, 27, 0,
+ 12, 27, 36, 247, 244, 0, 247, 0, 0, 40, 0, 0, 248, 0, 0, 0,
+ 0, 247, 0, 244, 247, 36, 27, 12, 0, 27, 0, 250, 244, 12, 250, 4,
+ 0, 0, 0, 248, 0, 0, 40, 0, 4, 250, 12, 244, 250, 0, 27, 0,
+ 12, 27, 36, 247, 244, 0, 247, 0, 0, 40, 0, 0, 248, 0, 0, 0,
+ 4, 250, 12, 244, 250, 0, 27, 0, 12, 27, 36, 247, 244, 0, 247, 0,
+ 0, 0, 0, 248, 0, 0, 40, 0, 0, 247, 0, 244, 247, 36, 27, 12,
+ 0, 27, 0, 250, 244, 12, 250, 4, 0, 40, 0, 0, 248, 0, 0, 0,
+ 4, 250, 12, 244, 250, 0, 27, 0, 12, 27, 36, 247, 244, 0, 247, 0,
+ 0, 0, 0, 248, 0, 0, 40, 0, 0, 247, 0, 244, 247, 36, 27, 12,
+ 0, 27, 0, 250, 244, 12, 250, 4, 0, 40, 0, 0, 248, 0, 0, 0,
+ 4, 250, 12, 244, 250, 0, 27, 0, 12, 27, 36, 247, 244, 0, 247, 0,
+ 0, 0, 0, 248, 0, 0, 40, 0, 0, 247, 0, 244, 247, 36, 27, 12,
+ 0, 27, 0, 250, 244, 12, 250, 4, 0, 40, 0, 0, 248, 0, 0, 0,
+244, 12, 250, 4, 0, 27, 0, 250, 247, 36, 27, 12, 0, 247, 0, 244,
+248, 0, 0, 0, 0, 40, 0, 0, 244, 0, 247, 0, 12, 27, 36, 247,
+250, 0, 27, 0, 4, 250, 12, 244, 0, 0, 40, 0, 0, 0, 0, 248,
+244, 12, 250, 4, 0, 27, 0, 250, 247, 36, 27, 12, 0, 247, 0, 244,
+248, 0, 0, 0, 0, 40, 0, 0, 244, 0, 247, 0, 12, 27, 36, 247,
+250, 0, 27, 0, 4, 250, 12, 244, 0, 0, 40, 0, 0, 0, 0, 248,
+244, 12, 250, 4, 0, 27, 0, 250, 247, 36, 27, 12, 0, 247, 0, 244,
+248, 0, 0, 0, 0, 40, 0, 0, 244, 0, 247, 0, 12, 27, 36, 247,
+250, 0, 27, 0, 4, 250, 12, 244, 0, 0, 40, 0, 0, 0, 0, 248
diff --git a/drivers/media/video/omap3isp/gamma_table.h b/drivers/media/video/omap3isp/gamma_table.h
new file mode 100644
index 00000000..78deebf7
--- /dev/null
+++ b/drivers/media/video/omap3isp/gamma_table.h
@@ -0,0 +1,90 @@
+/*
+ * gamma_table.h
+ *
+ * TI OMAP3 ISP - Default gamma table for all components
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+ 0, 0, 1, 2, 3, 3, 4, 5, 6, 8, 10, 12, 14, 16, 18, 20,
+ 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 36, 37, 39, 40, 41, 42,
+ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 63, 64, 65, 66, 66, 67, 68, 69, 69, 70,
+ 71, 72, 72, 73, 74, 75, 75, 76, 77, 78, 78, 79, 80, 81, 81, 82,
+ 83, 84, 84, 85, 86, 87, 88, 88, 89, 90, 91, 91, 92, 93, 94, 94,
+ 95, 96, 97, 97, 98, 98, 99, 99, 100, 100, 101, 101, 102, 103, 104, 104,
+105, 106, 107, 108, 108, 109, 110, 111, 111, 112, 113, 114, 114, 115, 116, 117,
+117, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 125,
+126, 126, 127, 127, 128, 128, 129, 129, 130, 130, 131, 131, 132, 132, 133, 133,
+134, 134, 135, 135, 136, 136, 137, 137, 138, 138, 139, 139, 140, 140, 141, 141,
+142, 142, 143, 143, 144, 144, 145, 145, 146, 146, 147, 147, 148, 148, 149, 149,
+150, 150, 151, 151, 152, 152, 153, 153, 153, 153, 154, 154, 154, 154, 155, 155,
+156, 156, 157, 157, 158, 158, 158, 159, 159, 159, 160, 160, 160, 161, 161, 162,
+162, 163, 163, 164, 164, 164, 164, 165, 165, 165, 165, 166, 166, 167, 167, 168,
+168, 169, 169, 170, 170, 170, 170, 171, 171, 171, 171, 172, 172, 173, 173, 174,
+174, 175, 175, 176, 176, 176, 176, 177, 177, 177, 177, 178, 178, 178, 178, 179,
+179, 179, 179, 180, 180, 180, 180, 181, 181, 181, 181, 182, 182, 182, 182, 183,
+183, 183, 183, 184, 184, 184, 184, 185, 185, 185, 185, 186, 186, 186, 186, 187,
+187, 187, 187, 188, 188, 188, 188, 189, 189, 189, 189, 190, 190, 190, 190, 191,
+191, 191, 191, 192, 192, 192, 192, 193, 193, 193, 193, 194, 194, 194, 194, 195,
+195, 195, 195, 196, 196, 196, 196, 197, 197, 197, 197, 198, 198, 198, 198, 199,
+199, 199, 199, 200, 200, 200, 200, 201, 201, 201, 201, 202, 202, 202, 203, 203,
+203, 203, 204, 204, 204, 204, 205, 205, 205, 205, 206, 206, 206, 206, 207, 207,
+207, 207, 208, 208, 208, 208, 209, 209, 209, 209, 210, 210, 210, 210, 210, 210,
+210, 210, 210, 210, 210, 210, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211,
+211, 212, 212, 212, 212, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213,
+213, 214, 214, 214, 214, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215,
+216, 216, 216, 216, 217, 217, 217, 217, 218, 218, 218, 218, 219, 219, 219, 219,
+219, 219, 219, 219, 219, 219, 219, 219, 220, 220, 220, 220, 221, 221, 221, 221,
+221, 221, 221, 221, 221, 221, 221, 222, 222, 222, 222, 223, 223, 223, 223, 223,
+223, 223, 223, 223, 223, 223, 223, 224, 224, 224, 224, 225, 225, 225, 225, 225,
+225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 226, 226,
+226, 226, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 228, 228,
+228, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 230, 230, 230,
+230, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 232, 232, 232,
+232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232,
+233, 233, 233, 233, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 235,
+235, 235, 235, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236,
+236, 236, 236, 236, 236, 236, 237, 237, 237, 237, 238, 238, 238, 238, 238, 238,
+238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238,
+238, 238, 238, 238, 238, 239, 239, 239, 239, 240, 240, 240, 240, 240, 240, 240,
+240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240,
+240, 240, 240, 240, 241, 241, 241, 241, 242, 242, 242, 242, 242, 242, 242, 242,
+242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242,
+242, 242, 243, 243, 243, 243, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244,
+244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244,
+244, 245, 245, 245, 245, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246,
+246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246,
+246, 246, 246, 246, 246, 246, 246, 247, 247, 247, 247, 248, 248, 248, 248, 248,
+248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248,
+248, 248, 248, 248, 248, 248, 249, 249, 249, 249, 250, 250, 250, 250, 250, 250,
+250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250,
+250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250,
+250, 250, 250, 250, 251, 251, 251, 251, 252, 252, 252, 252, 252, 252, 252, 252,
+252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252,
+252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252,
+252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252,
+252, 252, 252, 252, 252, 252, 252, 252, 253, 253, 253, 253, 253, 253, 253, 253,
+253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253,
+253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253,
+253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253,
+253, 254, 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
diff --git a/drivers/media/video/omap3isp/isp.c b/drivers/media/video/omap3isp/isp.c
new file mode 100644
index 00000000..12d5f923
--- /dev/null
+++ b/drivers/media/video/omap3isp/isp.c
@@ -0,0 +1,2235 @@
+/*
+ * isp.c
+ *
+ * TI OMAP3 ISP - Core
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2007-2009 Texas Instruments, Inc.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * Contributors:
+ * Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ * David Cohen <dacohen@gmail.com>
+ * Stanimir Varbanov <svarbanov@mm-sol.com>
+ * Vimarsh Zutshi <vimarsh.zutshi@gmail.com>
+ * Tuukka Toivonen <tuukkat76@gmail.com>
+ * Sergio Aguirre <saaguirre@ti.com>
+ * Antti Koskipaa <akoskipa@gmail.com>
+ * Ivan T. Ivanov <iivanov@mm-sol.com>
+ * RaniSuneela <r-m@ti.com>
+ * Atanas Filipov <afilipov@mm-sol.com>
+ * Gjorgji Rosikopulos <grosikopulos@mm-sol.com>
+ * Hiroshi DOYU <hiroshi.doyu@nokia.com>
+ * Nayden Kanchev <nkanchev@mm-sol.com>
+ * Phil Carmody <ext-phil.2.carmody@nokia.com>
+ * Artem Bityutskiy <artem.bityutskiy@nokia.com>
+ * Dominic Curran <dcurran@ti.com>
+ * Ilkka Myllyperkio <ilkka.myllyperkio@sofica.fi>
+ * Pallavi Kulkarni <p-kulkarni@ti.com>
+ * Vaibhav Hiremath <hvaibhav@ti.com>
+ * Mohit Jalori <mjalori@ti.com>
+ * Sameer Venkatraman <sameerv@ti.com>
+ * Senthilvadivu Guruswamy <svadivu@ti.com>
+ * Thara Gopinath <thara@ti.com>
+ * Toni Leinonen <toni.leinonen@nokia.com>
+ * Troy Laramy <t-laramy@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <asm/cacheflush.h>
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+
+#include "isp.h"
+#include "ispreg.h"
+#include "ispccdc.h"
+#include "isppreview.h"
+#include "ispresizer.h"
+#include "ispcsi2.h"
+#include "ispccp2.h"
+#include "isph3a.h"
+#include "isphist.h"
+
+static unsigned int autoidle;
+module_param(autoidle, int, 0444);
+MODULE_PARM_DESC(autoidle, "Enable OMAP3ISP AUTOIDLE support");
+
+static void isp_save_ctx(struct isp_device *isp);
+
+static void isp_restore_ctx(struct isp_device *isp);
+
+static const struct isp_res_mapping isp_res_maps[] = {
+ {
+ .isp_rev = ISP_REVISION_2_0,
+ .map = 1 << OMAP3_ISP_IOMEM_MAIN |
+ 1 << OMAP3_ISP_IOMEM_CCP2 |
+ 1 << OMAP3_ISP_IOMEM_CCDC |
+ 1 << OMAP3_ISP_IOMEM_HIST |
+ 1 << OMAP3_ISP_IOMEM_H3A |
+ 1 << OMAP3_ISP_IOMEM_PREV |
+ 1 << OMAP3_ISP_IOMEM_RESZ |
+ 1 << OMAP3_ISP_IOMEM_SBL |
+ 1 << OMAP3_ISP_IOMEM_CSI2A_REGS1 |
+ 1 << OMAP3_ISP_IOMEM_CSIPHY2,
+ },
+ {
+ .isp_rev = ISP_REVISION_15_0,
+ .map = 1 << OMAP3_ISP_IOMEM_MAIN |
+ 1 << OMAP3_ISP_IOMEM_CCP2 |
+ 1 << OMAP3_ISP_IOMEM_CCDC |
+ 1 << OMAP3_ISP_IOMEM_HIST |
+ 1 << OMAP3_ISP_IOMEM_H3A |
+ 1 << OMAP3_ISP_IOMEM_PREV |
+ 1 << OMAP3_ISP_IOMEM_RESZ |
+ 1 << OMAP3_ISP_IOMEM_SBL |
+ 1 << OMAP3_ISP_IOMEM_CSI2A_REGS1 |
+ 1 << OMAP3_ISP_IOMEM_CSIPHY2 |
+ 1 << OMAP3_ISP_IOMEM_CSI2A_REGS2 |
+ 1 << OMAP3_ISP_IOMEM_CSI2C_REGS1 |
+ 1 << OMAP3_ISP_IOMEM_CSIPHY1 |
+ 1 << OMAP3_ISP_IOMEM_CSI2C_REGS2,
+ },
+};
+
+/* Structure for saving/restoring ISP module registers */
+static struct isp_reg isp_reg_list[] = {
+ {OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG, 0},
+ {OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, 0},
+ {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, 0},
+ {0, ISP_TOK_TERM, 0}
+};
+
+/*
+ * omap3isp_flush - Post pending L3 bus writes by doing a register readback
+ * @isp: OMAP3 ISP device
+ *
+ * In order to force posting of pending writes, we need to write and
+ * readback the same register, in this case the revision register.
+ *
+ * See this link for reference:
+ * http://www.mail-archive.com/linux-omap@vger.kernel.org/msg08149.html
+ */
+void omap3isp_flush(struct isp_device *isp)
+{
+ isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION);
+ isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION);
+}
+
+/*
+ * isp_enable_interrupts - Enable ISP interrupts.
+ * @isp: OMAP3 ISP device
+ */
+static void isp_enable_interrupts(struct isp_device *isp)
+{
+ static const u32 irq = IRQ0ENABLE_CSIA_IRQ
+ | IRQ0ENABLE_CSIB_IRQ
+ | IRQ0ENABLE_CCDC_LSC_PREF_ERR_IRQ
+ | IRQ0ENABLE_CCDC_LSC_DONE_IRQ
+ | IRQ0ENABLE_CCDC_VD0_IRQ
+ | IRQ0ENABLE_CCDC_VD1_IRQ
+ | IRQ0ENABLE_HS_VS_IRQ
+ | IRQ0ENABLE_HIST_DONE_IRQ
+ | IRQ0ENABLE_H3A_AWB_DONE_IRQ
+ | IRQ0ENABLE_H3A_AF_DONE_IRQ
+ | IRQ0ENABLE_PRV_DONE_IRQ
+ | IRQ0ENABLE_RSZ_DONE_IRQ;
+
+ isp_reg_writel(isp, irq, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
+ isp_reg_writel(isp, irq, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE);
+}
+
+/*
+ * isp_disable_interrupts - Disable ISP interrupts.
+ * @isp: OMAP3 ISP device
+ */
+static void isp_disable_interrupts(struct isp_device *isp)
+{
+ isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE);
+}
+
+/**
+ * isp_set_xclk - Configures the specified cam_xclk to the desired frequency.
+ * @isp: OMAP3 ISP device
+ * @xclk: Desired frequency of the clock in Hz. 0 = stable low, 1 is stable high
+ * @xclksel: XCLK to configure (0 = A, 1 = B).
+ *
+ * Configures the specified MCLK divisor in the ISP timing control register
+ * (TCTRL_CTRL) to generate the desired xclk clock value.
+ *
+ * Divisor = cam_mclk_hz / xclk
+ *
+ * Returns the final frequency that is actually being generated
+ **/
+static u32 isp_set_xclk(struct isp_device *isp, u32 xclk, u8 xclksel)
+{
+ u32 divisor;
+ u32 currentxclk;
+ unsigned long mclk_hz;
+
+ if (!omap3isp_get(isp))
+ return 0;
+
+ mclk_hz = clk_get_rate(isp->clock[ISP_CLK_CAM_MCLK]);
+
+ if (xclk >= mclk_hz) {
+ divisor = ISPTCTRL_CTRL_DIV_BYPASS;
+ currentxclk = mclk_hz;
+ } else if (xclk >= 2) {
+ divisor = mclk_hz / xclk;
+ if (divisor >= ISPTCTRL_CTRL_DIV_BYPASS)
+ divisor = ISPTCTRL_CTRL_DIV_BYPASS - 1;
+ currentxclk = mclk_hz / divisor;
+ } else {
+ divisor = xclk;
+ currentxclk = 0;
+ }
+
+ switch (xclksel) {
+ case ISP_XCLK_A:
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
+ ISPTCTRL_CTRL_DIVA_MASK,
+ divisor << ISPTCTRL_CTRL_DIVA_SHIFT);
+ dev_dbg(isp->dev, "isp_set_xclk(): cam_xclka set to %d Hz\n",
+ currentxclk);
+ break;
+ case ISP_XCLK_B:
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
+ ISPTCTRL_CTRL_DIVB_MASK,
+ divisor << ISPTCTRL_CTRL_DIVB_SHIFT);
+ dev_dbg(isp->dev, "isp_set_xclk(): cam_xclkb set to %d Hz\n",
+ currentxclk);
+ break;
+ case ISP_XCLK_NONE:
+ default:
+ omap3isp_put(isp);
+ dev_dbg(isp->dev, "ISP_ERR: isp_set_xclk(): Invalid requested "
+ "xclk. Must be 0 (A) or 1 (B).\n");
+ return -EINVAL;
+ }
+
+ /* Do we go from stable whatever to clock? */
+ if (divisor >= 2 && isp->xclk_divisor[xclksel - 1] < 2)
+ omap3isp_get(isp);
+ /* Stopping the clock. */
+ else if (divisor < 2 && isp->xclk_divisor[xclksel - 1] >= 2)
+ omap3isp_put(isp);
+
+ isp->xclk_divisor[xclksel - 1] = divisor;
+
+ omap3isp_put(isp);
+
+ return currentxclk;
+}
+
+/*
+ * isp_power_settings - Sysconfig settings, for Power Management.
+ * @isp: OMAP3 ISP device
+ * @idle: Consider idle state.
+ *
+ * Sets the power settings for the ISP, and SBL bus.
+ */
+static void isp_power_settings(struct isp_device *isp, int idle)
+{
+ isp_reg_writel(isp,
+ ((idle ? ISP_SYSCONFIG_MIDLEMODE_SMARTSTANDBY :
+ ISP_SYSCONFIG_MIDLEMODE_FORCESTANDBY) <<
+ ISP_SYSCONFIG_MIDLEMODE_SHIFT) |
+ ((isp->revision == ISP_REVISION_15_0) ?
+ ISP_SYSCONFIG_AUTOIDLE : 0),
+ OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG);
+
+ if (isp->autoidle)
+ isp_reg_writel(isp, ISPCTRL_SBL_AUTOIDLE, OMAP3_ISP_IOMEM_MAIN,
+ ISP_CTRL);
+}
+
+/*
+ * Configure the bridge and lane shifter. Valid inputs are
+ *
+ * CCDC_INPUT_PARALLEL: Parallel interface
+ * CCDC_INPUT_CSI2A: CSI2a receiver
+ * CCDC_INPUT_CCP2B: CCP2b receiver
+ * CCDC_INPUT_CSI2C: CSI2c receiver
+ *
+ * The bridge and lane shifter are configured according to the selected input
+ * and the ISP platform data.
+ */
+void omap3isp_configure_bridge(struct isp_device *isp,
+ enum ccdc_input_entity input,
+ const struct isp_parallel_platform_data *pdata,
+ unsigned int shift)
+{
+ u32 ispctrl_val;
+
+ ispctrl_val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL);
+ ispctrl_val &= ~ISPCTRL_SHIFT_MASK;
+ ispctrl_val &= ~ISPCTRL_PAR_CLK_POL_INV;
+ ispctrl_val &= ~ISPCTRL_PAR_SER_CLK_SEL_MASK;
+ ispctrl_val &= ~ISPCTRL_PAR_BRIDGE_MASK;
+
+ switch (input) {
+ case CCDC_INPUT_PARALLEL:
+ ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_PARALLEL;
+ ispctrl_val |= pdata->clk_pol << ISPCTRL_PAR_CLK_POL_SHIFT;
+ ispctrl_val |= pdata->bridge << ISPCTRL_PAR_BRIDGE_SHIFT;
+ shift += pdata->data_lane_shift * 2;
+ break;
+
+ case CCDC_INPUT_CSI2A:
+ ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIA;
+ break;
+
+ case CCDC_INPUT_CCP2B:
+ ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIB;
+ break;
+
+ case CCDC_INPUT_CSI2C:
+ ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIC;
+ break;
+
+ default:
+ return;
+ }
+
+ ispctrl_val |= ((shift/2) << ISPCTRL_SHIFT_SHIFT) & ISPCTRL_SHIFT_MASK;
+
+ ispctrl_val &= ~ISPCTRL_SYNC_DETECT_MASK;
+ ispctrl_val |= ISPCTRL_SYNC_DETECT_VSRISE;
+
+ isp_reg_writel(isp, ispctrl_val, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL);
+}
+
+/**
+ * isp_set_pixel_clock - Configures the ISP pixel clock
+ * @isp: OMAP3 ISP device
+ * @pixelclk: Average pixel clock in Hz
+ *
+ * Set the average pixel clock required by the sensor. The ISP will use the
+ * lowest possible memory bandwidth settings compatible with the clock.
+ **/
+static void isp_set_pixel_clock(struct isp_device *isp, unsigned int pixelclk)
+{
+ isp->isp_ccdc.vpcfg.pixelclk = pixelclk;
+}
+
+void omap3isp_hist_dma_done(struct isp_device *isp)
+{
+ if (omap3isp_ccdc_busy(&isp->isp_ccdc) ||
+ omap3isp_stat_pcr_busy(&isp->isp_hist)) {
+ /* Histogram cannot be enabled in this frame anymore */
+ atomic_set(&isp->isp_hist.buf_err, 1);
+ dev_dbg(isp->dev, "hist: Out of synchronization with "
+ "CCDC. Ignoring next buffer.\n");
+ }
+}
+
+static inline void isp_isr_dbg(struct isp_device *isp, u32 irqstatus)
+{
+ static const char *name[] = {
+ "CSIA_IRQ",
+ "res1",
+ "res2",
+ "CSIB_LCM_IRQ",
+ "CSIB_IRQ",
+ "res5",
+ "res6",
+ "res7",
+ "CCDC_VD0_IRQ",
+ "CCDC_VD1_IRQ",
+ "CCDC_VD2_IRQ",
+ "CCDC_ERR_IRQ",
+ "H3A_AF_DONE_IRQ",
+ "H3A_AWB_DONE_IRQ",
+ "res14",
+ "res15",
+ "HIST_DONE_IRQ",
+ "CCDC_LSC_DONE",
+ "CCDC_LSC_PREFETCH_COMPLETED",
+ "CCDC_LSC_PREFETCH_ERROR",
+ "PRV_DONE_IRQ",
+ "CBUFF_IRQ",
+ "res22",
+ "res23",
+ "RSZ_DONE_IRQ",
+ "OVF_IRQ",
+ "res26",
+ "res27",
+ "MMU_ERR_IRQ",
+ "OCP_ERR_IRQ",
+ "SEC_ERR_IRQ",
+ "HS_VS_IRQ",
+ };
+ int i;
+
+ dev_dbg(isp->dev, "ISP IRQ: ");
+
+ for (i = 0; i < ARRAY_SIZE(name); i++) {
+ if ((1 << i) & irqstatus)
+ printk(KERN_CONT "%s ", name[i]);
+ }
+ printk(KERN_CONT "\n");
+}
+
+static void isp_isr_sbl(struct isp_device *isp)
+{
+ struct device *dev = isp->dev;
+ struct isp_pipeline *pipe;
+ u32 sbl_pcr;
+
+ /*
+ * Handle shared buffer logic overflows for video buffers.
+ * ISPSBL_PCR_CCDCPRV_2_RSZ_OVF can be safely ignored.
+ */
+ sbl_pcr = isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_PCR);
+ isp_reg_writel(isp, sbl_pcr, OMAP3_ISP_IOMEM_SBL, ISPSBL_PCR);
+ sbl_pcr &= ~ISPSBL_PCR_CCDCPRV_2_RSZ_OVF;
+
+ if (sbl_pcr)
+ dev_dbg(dev, "SBL overflow (PCR = 0x%08x)\n", sbl_pcr);
+
+ if (sbl_pcr & ISPSBL_PCR_CSIB_WBL_OVF) {
+ pipe = to_isp_pipeline(&isp->isp_ccp2.subdev.entity);
+ if (pipe != NULL)
+ pipe->error = true;
+ }
+
+ if (sbl_pcr & ISPSBL_PCR_CSIA_WBL_OVF) {
+ pipe = to_isp_pipeline(&isp->isp_csi2a.subdev.entity);
+ if (pipe != NULL)
+ pipe->error = true;
+ }
+
+ if (sbl_pcr & ISPSBL_PCR_CCDC_WBL_OVF) {
+ pipe = to_isp_pipeline(&isp->isp_ccdc.subdev.entity);
+ if (pipe != NULL)
+ pipe->error = true;
+ }
+
+ if (sbl_pcr & ISPSBL_PCR_PRV_WBL_OVF) {
+ pipe = to_isp_pipeline(&isp->isp_prev.subdev.entity);
+ if (pipe != NULL)
+ pipe->error = true;
+ }
+
+ if (sbl_pcr & (ISPSBL_PCR_RSZ1_WBL_OVF
+ | ISPSBL_PCR_RSZ2_WBL_OVF
+ | ISPSBL_PCR_RSZ3_WBL_OVF
+ | ISPSBL_PCR_RSZ4_WBL_OVF)) {
+ pipe = to_isp_pipeline(&isp->isp_res.subdev.entity);
+ if (pipe != NULL)
+ pipe->error = true;
+ }
+
+ if (sbl_pcr & ISPSBL_PCR_H3A_AF_WBL_OVF)
+ omap3isp_stat_sbl_overflow(&isp->isp_af);
+
+ if (sbl_pcr & ISPSBL_PCR_H3A_AEAWB_WBL_OVF)
+ omap3isp_stat_sbl_overflow(&isp->isp_aewb);
+}
+
+/*
+ * isp_isr - Interrupt Service Routine for Camera ISP module.
+ * @irq: Not used currently.
+ * @_isp: Pointer to the OMAP3 ISP device
+ *
+ * Handles the corresponding callback if plugged in.
+ *
+ * Returns IRQ_HANDLED when IRQ was correctly handled, or IRQ_NONE when the
+ * IRQ wasn't handled.
+ */
+static irqreturn_t isp_isr(int irq, void *_isp)
+{
+ static const u32 ccdc_events = IRQ0STATUS_CCDC_LSC_PREF_ERR_IRQ |
+ IRQ0STATUS_CCDC_LSC_DONE_IRQ |
+ IRQ0STATUS_CCDC_VD0_IRQ |
+ IRQ0STATUS_CCDC_VD1_IRQ |
+ IRQ0STATUS_HS_VS_IRQ;
+ struct isp_device *isp = _isp;
+ u32 irqstatus;
+
+ irqstatus = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
+ isp_reg_writel(isp, irqstatus, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
+
+ isp_isr_sbl(isp);
+
+ if (irqstatus & IRQ0STATUS_CSIA_IRQ)
+ omap3isp_csi2_isr(&isp->isp_csi2a);
+
+ if (irqstatus & IRQ0STATUS_CSIB_IRQ)
+ omap3isp_ccp2_isr(&isp->isp_ccp2);
+
+ if (irqstatus & IRQ0STATUS_CCDC_VD0_IRQ) {
+ if (isp->isp_ccdc.output & CCDC_OUTPUT_PREVIEW)
+ omap3isp_preview_isr_frame_sync(&isp->isp_prev);
+ if (isp->isp_ccdc.output & CCDC_OUTPUT_RESIZER)
+ omap3isp_resizer_isr_frame_sync(&isp->isp_res);
+ omap3isp_stat_isr_frame_sync(&isp->isp_aewb);
+ omap3isp_stat_isr_frame_sync(&isp->isp_af);
+ omap3isp_stat_isr_frame_sync(&isp->isp_hist);
+ }
+
+ if (irqstatus & ccdc_events)
+ omap3isp_ccdc_isr(&isp->isp_ccdc, irqstatus & ccdc_events);
+
+ if (irqstatus & IRQ0STATUS_PRV_DONE_IRQ) {
+ if (isp->isp_prev.output & PREVIEW_OUTPUT_RESIZER)
+ omap3isp_resizer_isr_frame_sync(&isp->isp_res);
+ omap3isp_preview_isr(&isp->isp_prev);
+ }
+
+ if (irqstatus & IRQ0STATUS_RSZ_DONE_IRQ)
+ omap3isp_resizer_isr(&isp->isp_res);
+
+ if (irqstatus & IRQ0STATUS_H3A_AWB_DONE_IRQ)
+ omap3isp_stat_isr(&isp->isp_aewb);
+
+ if (irqstatus & IRQ0STATUS_H3A_AF_DONE_IRQ)
+ omap3isp_stat_isr(&isp->isp_af);
+
+ if (irqstatus & IRQ0STATUS_HIST_DONE_IRQ)
+ omap3isp_stat_isr(&isp->isp_hist);
+
+ omap3isp_flush(isp);
+
+#if defined(DEBUG) && defined(ISP_ISR_DEBUG)
+ isp_isr_dbg(isp, irqstatus);
+#endif
+
+ return IRQ_HANDLED;
+}
+
+/* -----------------------------------------------------------------------------
+ * Pipeline power management
+ *
+ * Entities must be powered up when part of a pipeline that contains at least
+ * one open video device node.
+ *
+ * To achieve this use the entity use_count field to track the number of users.
+ * For entities corresponding to video device nodes the use_count field stores
+ * the users count of the node. For entities corresponding to subdevs the
+ * use_count field stores the total number of users of all video device nodes
+ * in the pipeline.
+ *
+ * The omap3isp_pipeline_pm_use() function must be called in the open() and
+ * close() handlers of video device nodes. It increments or decrements the use
+ * count of all subdev entities in the pipeline.
+ *
+ * To react to link management on powered pipelines, the link setup notification
+ * callback updates the use count of all entities in the source and sink sides
+ * of the link.
+ */
+
+/*
+ * isp_pipeline_pm_use_count - Count the number of users of a pipeline
+ * @entity: The entity
+ *
+ * Return the total number of users of all video device nodes in the pipeline.
+ */
+static int isp_pipeline_pm_use_count(struct media_entity *entity)
+{
+ struct media_entity_graph graph;
+ int use = 0;
+
+ media_entity_graph_walk_start(&graph, entity);
+
+ while ((entity = media_entity_graph_walk_next(&graph))) {
+ if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE)
+ use += entity->use_count;
+ }
+
+ return use;
+}
+
+/*
+ * isp_pipeline_pm_power_one - Apply power change to an entity
+ * @entity: The entity
+ * @change: Use count change
+ *
+ * Change the entity use count by @change. If the entity is a subdev update its
+ * power state by calling the core::s_power operation when the use count goes
+ * from 0 to != 0 or from != 0 to 0.
+ *
+ * Return 0 on success or a negative error code on failure.
+ */
+static int isp_pipeline_pm_power_one(struct media_entity *entity, int change)
+{
+ struct v4l2_subdev *subdev;
+ int ret;
+
+ subdev = media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV
+ ? media_entity_to_v4l2_subdev(entity) : NULL;
+
+ if (entity->use_count == 0 && change > 0 && subdev != NULL) {
+ ret = v4l2_subdev_call(subdev, core, s_power, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+ }
+
+ entity->use_count += change;
+ WARN_ON(entity->use_count < 0);
+
+ if (entity->use_count == 0 && change < 0 && subdev != NULL)
+ v4l2_subdev_call(subdev, core, s_power, 0);
+
+ return 0;
+}
+
+/*
+ * isp_pipeline_pm_power - Apply power change to all entities in a pipeline
+ * @entity: The entity
+ * @change: Use count change
+ *
+ * Walk the pipeline to update the use count and the power state of all non-node
+ * entities.
+ *
+ * Return 0 on success or a negative error code on failure.
+ */
+static int isp_pipeline_pm_power(struct media_entity *entity, int change)
+{
+ struct media_entity_graph graph;
+ struct media_entity *first = entity;
+ int ret = 0;
+
+ if (!change)
+ return 0;
+
+ media_entity_graph_walk_start(&graph, entity);
+
+ while (!ret && (entity = media_entity_graph_walk_next(&graph)))
+ if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
+ ret = isp_pipeline_pm_power_one(entity, change);
+
+ if (!ret)
+ return 0;
+
+ media_entity_graph_walk_start(&graph, first);
+
+ while ((first = media_entity_graph_walk_next(&graph))
+ && first != entity)
+ if (media_entity_type(first) != MEDIA_ENT_T_DEVNODE)
+ isp_pipeline_pm_power_one(first, -change);
+
+ return ret;
+}
+
+/*
+ * omap3isp_pipeline_pm_use - Update the use count of an entity
+ * @entity: The entity
+ * @use: Use (1) or stop using (0) the entity
+ *
+ * Update the use count of all entities in the pipeline and power entities on or
+ * off accordingly.
+ *
+ * Return 0 on success or a negative error code on failure. Powering entities
+ * off is assumed to never fail. No failure can occur when the use parameter is
+ * set to 0.
+ */
+int omap3isp_pipeline_pm_use(struct media_entity *entity, int use)
+{
+ int change = use ? 1 : -1;
+ int ret;
+
+ mutex_lock(&entity->parent->graph_mutex);
+
+ /* Apply use count to node. */
+ entity->use_count += change;
+ WARN_ON(entity->use_count < 0);
+
+ /* Apply power change to connected non-nodes. */
+ ret = isp_pipeline_pm_power(entity, change);
+ if (ret < 0)
+ entity->use_count -= change;
+
+ mutex_unlock(&entity->parent->graph_mutex);
+
+ return ret;
+}
+
+/*
+ * isp_pipeline_link_notify - Link management notification callback
+ * @source: Pad at the start of the link
+ * @sink: Pad at the end of the link
+ * @flags: New link flags that will be applied
+ *
+ * React to link management on powered pipelines by updating the use count of
+ * all entities in the source and sink sides of the link. Entities are powered
+ * on or off accordingly.
+ *
+ * Return 0 on success or a negative error code on failure. Powering entities
+ * off is assumed to never fail. This function will not fail for disconnection
+ * events.
+ */
+static int isp_pipeline_link_notify(struct media_pad *source,
+ struct media_pad *sink, u32 flags)
+{
+ int source_use = isp_pipeline_pm_use_count(source->entity);
+ int sink_use = isp_pipeline_pm_use_count(sink->entity);
+ int ret;
+
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ /* Powering off entities is assumed to never fail. */
+ isp_pipeline_pm_power(source->entity, -sink_use);
+ isp_pipeline_pm_power(sink->entity, -source_use);
+ return 0;
+ }
+
+ ret = isp_pipeline_pm_power(source->entity, sink_use);
+ if (ret < 0)
+ return ret;
+
+ ret = isp_pipeline_pm_power(sink->entity, source_use);
+ if (ret < 0)
+ isp_pipeline_pm_power(source->entity, -sink_use);
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Pipeline stream management
+ */
+
+/*
+ * isp_pipeline_enable - Enable streaming on a pipeline
+ * @pipe: ISP pipeline
+ * @mode: Stream mode (single shot or continuous)
+ *
+ * Walk the entities chain starting at the pipeline output video node and start
+ * all modules in the chain in the given mode.
+ *
+ * Return 0 if successful, or the return value of the failed video::s_stream
+ * operation otherwise.
+ */
+static int isp_pipeline_enable(struct isp_pipeline *pipe,
+ enum isp_pipeline_stream_state mode)
+{
+ struct isp_device *isp = pipe->output->isp;
+ struct media_entity *entity;
+ struct media_pad *pad;
+ struct v4l2_subdev *subdev;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&pipe->lock, flags);
+ pipe->state &= ~(ISP_PIPELINE_IDLE_INPUT | ISP_PIPELINE_IDLE_OUTPUT);
+ spin_unlock_irqrestore(&pipe->lock, flags);
+
+ pipe->do_propagation = false;
+
+ entity = &pipe->output->video.entity;
+ while (1) {
+ pad = &entity->pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+
+ pad = media_entity_remote_source(pad);
+ if (pad == NULL ||
+ media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ break;
+
+ entity = pad->entity;
+ subdev = media_entity_to_v4l2_subdev(entity);
+
+ ret = v4l2_subdev_call(subdev, video, s_stream, mode);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+
+ if (subdev == &isp->isp_ccdc.subdev) {
+ v4l2_subdev_call(&isp->isp_aewb.subdev, video,
+ s_stream, mode);
+ v4l2_subdev_call(&isp->isp_af.subdev, video,
+ s_stream, mode);
+ v4l2_subdev_call(&isp->isp_hist.subdev, video,
+ s_stream, mode);
+ pipe->do_propagation = true;
+ }
+ }
+
+ /* Frame number propagation. In continuous streaming mode the number
+ * is incremented in the frame start ISR. In mem-to-mem mode
+ * singleshot is used and frame start IRQs are not available.
+ * Thus we have to increment the number here.
+ */
+ if (pipe->do_propagation && mode == ISP_PIPELINE_STREAM_SINGLESHOT)
+ atomic_inc(&pipe->frame_number);
+
+ return 0;
+}
+
+static int isp_pipeline_wait_resizer(struct isp_device *isp)
+{
+ return omap3isp_resizer_busy(&isp->isp_res);
+}
+
+static int isp_pipeline_wait_preview(struct isp_device *isp)
+{
+ return omap3isp_preview_busy(&isp->isp_prev);
+}
+
+static int isp_pipeline_wait_ccdc(struct isp_device *isp)
+{
+ return omap3isp_stat_busy(&isp->isp_af)
+ || omap3isp_stat_busy(&isp->isp_aewb)
+ || omap3isp_stat_busy(&isp->isp_hist)
+ || omap3isp_ccdc_busy(&isp->isp_ccdc);
+}
+
+#define ISP_STOP_TIMEOUT msecs_to_jiffies(1000)
+
+static int isp_pipeline_wait(struct isp_device *isp,
+ int(*busy)(struct isp_device *isp))
+{
+ unsigned long timeout = jiffies + ISP_STOP_TIMEOUT;
+
+ while (!time_after(jiffies, timeout)) {
+ if (!busy(isp))
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * isp_pipeline_disable - Disable streaming on a pipeline
+ * @pipe: ISP pipeline
+ *
+ * Walk the entities chain starting at the pipeline output video node and stop
+ * all modules in the chain. Wait synchronously for the modules to be stopped if
+ * necessary.
+ *
+ * Return 0 if all modules have been properly stopped, or -ETIMEDOUT if a module
+ * can't be stopped (in which case a software reset of the ISP is probably
+ * necessary).
+ */
+static int isp_pipeline_disable(struct isp_pipeline *pipe)
+{
+ struct isp_device *isp = pipe->output->isp;
+ struct media_entity *entity;
+ struct media_pad *pad;
+ struct v4l2_subdev *subdev;
+ int failure = 0;
+ int ret;
+
+ /*
+ * We need to stop all the modules after CCDC first or they'll
+ * never stop since they may not get a full frame from CCDC.
+ */
+ entity = &pipe->output->video.entity;
+ while (1) {
+ pad = &entity->pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+
+ pad = media_entity_remote_source(pad);
+ if (pad == NULL ||
+ media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ break;
+
+ entity = pad->entity;
+ subdev = media_entity_to_v4l2_subdev(entity);
+
+ if (subdev == &isp->isp_ccdc.subdev) {
+ v4l2_subdev_call(&isp->isp_aewb.subdev,
+ video, s_stream, 0);
+ v4l2_subdev_call(&isp->isp_af.subdev,
+ video, s_stream, 0);
+ v4l2_subdev_call(&isp->isp_hist.subdev,
+ video, s_stream, 0);
+ }
+
+ v4l2_subdev_call(subdev, video, s_stream, 0);
+
+ if (subdev == &isp->isp_res.subdev)
+ ret = isp_pipeline_wait(isp, isp_pipeline_wait_resizer);
+ else if (subdev == &isp->isp_prev.subdev)
+ ret = isp_pipeline_wait(isp, isp_pipeline_wait_preview);
+ else if (subdev == &isp->isp_ccdc.subdev)
+ ret = isp_pipeline_wait(isp, isp_pipeline_wait_ccdc);
+ else
+ ret = 0;
+
+ if (ret) {
+ dev_info(isp->dev, "Unable to stop %s\n", subdev->name);
+ failure = -ETIMEDOUT;
+ }
+ }
+
+ if (failure < 0)
+ isp->needs_reset = true;
+
+ return failure;
+}
+
+/*
+ * omap3isp_pipeline_set_stream - Enable/disable streaming on a pipeline
+ * @pipe: ISP pipeline
+ * @state: Stream state (stopped, single shot or continuous)
+ *
+ * Set the pipeline to the given stream state. Pipelines can be started in
+ * single-shot or continuous mode.
+ *
+ * Return 0 if successful, or the return value of the failed video::s_stream
+ * operation otherwise. The pipeline state is not updated when the operation
+ * fails, except when stopping the pipeline.
+ */
+int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe,
+ enum isp_pipeline_stream_state state)
+{
+ int ret;
+
+ if (state == ISP_PIPELINE_STREAM_STOPPED)
+ ret = isp_pipeline_disable(pipe);
+ else
+ ret = isp_pipeline_enable(pipe, state);
+
+ if (ret == 0 || state == ISP_PIPELINE_STREAM_STOPPED)
+ pipe->stream_state = state;
+
+ return ret;
+}
+
+/*
+ * isp_pipeline_resume - Resume streaming on a pipeline
+ * @pipe: ISP pipeline
+ *
+ * Resume video output and input and re-enable pipeline.
+ */
+static void isp_pipeline_resume(struct isp_pipeline *pipe)
+{
+ int singleshot = pipe->stream_state == ISP_PIPELINE_STREAM_SINGLESHOT;
+
+ omap3isp_video_resume(pipe->output, !singleshot);
+ if (singleshot)
+ omap3isp_video_resume(pipe->input, 0);
+ isp_pipeline_enable(pipe, pipe->stream_state);
+}
+
+/*
+ * isp_pipeline_suspend - Suspend streaming on a pipeline
+ * @pipe: ISP pipeline
+ *
+ * Suspend pipeline.
+ */
+static void isp_pipeline_suspend(struct isp_pipeline *pipe)
+{
+ isp_pipeline_disable(pipe);
+}
+
+/*
+ * isp_pipeline_is_last - Verify if entity has an enabled link to the output
+ * video node
+ * @me: ISP module's media entity
+ *
+ * Returns 1 if the entity has an enabled link to the output video node or 0
+ * otherwise. It's true only while pipeline can have no more than one output
+ * node.
+ */
+static int isp_pipeline_is_last(struct media_entity *me)
+{
+ struct isp_pipeline *pipe;
+ struct media_pad *pad;
+
+ if (!me->pipe)
+ return 0;
+ pipe = to_isp_pipeline(me);
+ if (pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED)
+ return 0;
+ pad = media_entity_remote_source(&pipe->output->pad);
+ return pad->entity == me;
+}
+
+/*
+ * isp_suspend_module_pipeline - Suspend pipeline to which belongs the module
+ * @me: ISP module's media entity
+ *
+ * Suspend the whole pipeline if module's entity has an enabled link to the
+ * output video node. It works only while pipeline can have no more than one
+ * output node.
+ */
+static void isp_suspend_module_pipeline(struct media_entity *me)
+{
+ if (isp_pipeline_is_last(me))
+ isp_pipeline_suspend(to_isp_pipeline(me));
+}
+
+/*
+ * isp_resume_module_pipeline - Resume pipeline to which belongs the module
+ * @me: ISP module's media entity
+ *
+ * Resume the whole pipeline if module's entity has an enabled link to the
+ * output video node. It works only while pipeline can have no more than one
+ * output node.
+ */
+static void isp_resume_module_pipeline(struct media_entity *me)
+{
+ if (isp_pipeline_is_last(me))
+ isp_pipeline_resume(to_isp_pipeline(me));
+}
+
+/*
+ * isp_suspend_modules - Suspend ISP submodules.
+ * @isp: OMAP3 ISP device
+ *
+ * Returns 0 if suspend left in idle state all the submodules properly,
+ * or returns 1 if a general Reset is required to suspend the submodules.
+ */
+static int isp_suspend_modules(struct isp_device *isp)
+{
+ unsigned long timeout;
+
+ omap3isp_stat_suspend(&isp->isp_aewb);
+ omap3isp_stat_suspend(&isp->isp_af);
+ omap3isp_stat_suspend(&isp->isp_hist);
+ isp_suspend_module_pipeline(&isp->isp_res.subdev.entity);
+ isp_suspend_module_pipeline(&isp->isp_prev.subdev.entity);
+ isp_suspend_module_pipeline(&isp->isp_ccdc.subdev.entity);
+ isp_suspend_module_pipeline(&isp->isp_csi2a.subdev.entity);
+ isp_suspend_module_pipeline(&isp->isp_ccp2.subdev.entity);
+
+ timeout = jiffies + ISP_STOP_TIMEOUT;
+ while (omap3isp_stat_busy(&isp->isp_af)
+ || omap3isp_stat_busy(&isp->isp_aewb)
+ || omap3isp_stat_busy(&isp->isp_hist)
+ || omap3isp_preview_busy(&isp->isp_prev)
+ || omap3isp_resizer_busy(&isp->isp_res)
+ || omap3isp_ccdc_busy(&isp->isp_ccdc)) {
+ if (time_after(jiffies, timeout)) {
+ dev_info(isp->dev, "can't stop modules.\n");
+ return 1;
+ }
+ msleep(1);
+ }
+
+ return 0;
+}
+
+/*
+ * isp_resume_modules - Resume ISP submodules.
+ * @isp: OMAP3 ISP device
+ */
+static void isp_resume_modules(struct isp_device *isp)
+{
+ omap3isp_stat_resume(&isp->isp_aewb);
+ omap3isp_stat_resume(&isp->isp_af);
+ omap3isp_stat_resume(&isp->isp_hist);
+ isp_resume_module_pipeline(&isp->isp_res.subdev.entity);
+ isp_resume_module_pipeline(&isp->isp_prev.subdev.entity);
+ isp_resume_module_pipeline(&isp->isp_ccdc.subdev.entity);
+ isp_resume_module_pipeline(&isp->isp_csi2a.subdev.entity);
+ isp_resume_module_pipeline(&isp->isp_ccp2.subdev.entity);
+}
+
+/*
+ * isp_reset - Reset ISP with a timeout wait for idle.
+ * @isp: OMAP3 ISP device
+ */
+static int isp_reset(struct isp_device *isp)
+{
+ unsigned long timeout = 0;
+
+ isp_reg_writel(isp,
+ isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG)
+ | ISP_SYSCONFIG_SOFTRESET,
+ OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG);
+ while (!(isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN,
+ ISP_SYSSTATUS) & 0x1)) {
+ if (timeout++ > 10000) {
+ dev_alert(isp->dev, "cannot reset ISP\n");
+ return -ETIMEDOUT;
+ }
+ udelay(1);
+ }
+
+ return 0;
+}
+
+/*
+ * isp_save_context - Saves the values of the ISP module registers.
+ * @isp: OMAP3 ISP device
+ * @reg_list: Structure containing pairs of register address and value to
+ * modify on OMAP.
+ */
+static void
+isp_save_context(struct isp_device *isp, struct isp_reg *reg_list)
+{
+ struct isp_reg *next = reg_list;
+
+ for (; next->reg != ISP_TOK_TERM; next++)
+ next->val = isp_reg_readl(isp, next->mmio_range, next->reg);
+}
+
+/*
+ * isp_restore_context - Restores the values of the ISP module registers.
+ * @isp: OMAP3 ISP device
+ * @reg_list: Structure containing pairs of register address and value to
+ * modify on OMAP.
+ */
+static void
+isp_restore_context(struct isp_device *isp, struct isp_reg *reg_list)
+{
+ struct isp_reg *next = reg_list;
+
+ for (; next->reg != ISP_TOK_TERM; next++)
+ isp_reg_writel(isp, next->val, next->mmio_range, next->reg);
+}
+
+/*
+ * isp_save_ctx - Saves ISP, CCDC, HIST, H3A, PREV, RESZ & MMU context.
+ * @isp: OMAP3 ISP device
+ *
+ * Routine for saving the context of each module in the ISP.
+ * CCDC, HIST, H3A, PREV, RESZ and MMU.
+ */
+static void isp_save_ctx(struct isp_device *isp)
+{
+ isp_save_context(isp, isp_reg_list);
+ omap_iommu_save_ctx(isp->dev);
+}
+
+/*
+ * isp_restore_ctx - Restores ISP, CCDC, HIST, H3A, PREV, RESZ & MMU context.
+ * @isp: OMAP3 ISP device
+ *
+ * Routine for restoring the context of each module in the ISP.
+ * CCDC, HIST, H3A, PREV, RESZ and MMU.
+ */
+static void isp_restore_ctx(struct isp_device *isp)
+{
+ isp_restore_context(isp, isp_reg_list);
+ omap_iommu_restore_ctx(isp->dev);
+ omap3isp_ccdc_restore_context(isp);
+ omap3isp_preview_restore_context(isp);
+}
+
+/* -----------------------------------------------------------------------------
+ * SBL resources management
+ */
+#define OMAP3_ISP_SBL_READ (OMAP3_ISP_SBL_CSI1_READ | \
+ OMAP3_ISP_SBL_CCDC_LSC_READ | \
+ OMAP3_ISP_SBL_PREVIEW_READ | \
+ OMAP3_ISP_SBL_RESIZER_READ)
+#define OMAP3_ISP_SBL_WRITE (OMAP3_ISP_SBL_CSI1_WRITE | \
+ OMAP3_ISP_SBL_CSI2A_WRITE | \
+ OMAP3_ISP_SBL_CSI2C_WRITE | \
+ OMAP3_ISP_SBL_CCDC_WRITE | \
+ OMAP3_ISP_SBL_PREVIEW_WRITE)
+
+void omap3isp_sbl_enable(struct isp_device *isp, enum isp_sbl_resource res)
+{
+ u32 sbl = 0;
+
+ isp->sbl_resources |= res;
+
+ if (isp->sbl_resources & OMAP3_ISP_SBL_CSI1_READ)
+ sbl |= ISPCTRL_SBL_SHARED_RPORTA;
+
+ if (isp->sbl_resources & OMAP3_ISP_SBL_CCDC_LSC_READ)
+ sbl |= ISPCTRL_SBL_SHARED_RPORTB;
+
+ if (isp->sbl_resources & OMAP3_ISP_SBL_CSI2C_WRITE)
+ sbl |= ISPCTRL_SBL_SHARED_WPORTC;
+
+ if (isp->sbl_resources & OMAP3_ISP_SBL_RESIZER_WRITE)
+ sbl |= ISPCTRL_SBL_WR0_RAM_EN;
+
+ if (isp->sbl_resources & OMAP3_ISP_SBL_WRITE)
+ sbl |= ISPCTRL_SBL_WR1_RAM_EN;
+
+ if (isp->sbl_resources & OMAP3_ISP_SBL_READ)
+ sbl |= ISPCTRL_SBL_RD_RAM_EN;
+
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, sbl);
+}
+
+void omap3isp_sbl_disable(struct isp_device *isp, enum isp_sbl_resource res)
+{
+ u32 sbl = 0;
+
+ isp->sbl_resources &= ~res;
+
+ if (!(isp->sbl_resources & OMAP3_ISP_SBL_CSI1_READ))
+ sbl |= ISPCTRL_SBL_SHARED_RPORTA;
+
+ if (!(isp->sbl_resources & OMAP3_ISP_SBL_CCDC_LSC_READ))
+ sbl |= ISPCTRL_SBL_SHARED_RPORTB;
+
+ if (!(isp->sbl_resources & OMAP3_ISP_SBL_CSI2C_WRITE))
+ sbl |= ISPCTRL_SBL_SHARED_WPORTC;
+
+ if (!(isp->sbl_resources & OMAP3_ISP_SBL_RESIZER_WRITE))
+ sbl |= ISPCTRL_SBL_WR0_RAM_EN;
+
+ if (!(isp->sbl_resources & OMAP3_ISP_SBL_WRITE))
+ sbl |= ISPCTRL_SBL_WR1_RAM_EN;
+
+ if (!(isp->sbl_resources & OMAP3_ISP_SBL_READ))
+ sbl |= ISPCTRL_SBL_RD_RAM_EN;
+
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, sbl);
+}
+
+/*
+ * isp_module_sync_idle - Helper to sync module with its idle state
+ * @me: ISP submodule's media entity
+ * @wait: ISP submodule's wait queue for streamoff/interrupt synchronization
+ * @stopping: flag which tells module wants to stop
+ *
+ * This function checks if ISP submodule needs to wait for next interrupt. If
+ * yes, makes the caller to sleep while waiting for such event.
+ */
+int omap3isp_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
+ atomic_t *stopping)
+{
+ struct isp_pipeline *pipe = to_isp_pipeline(me);
+
+ if (pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED ||
+ (pipe->stream_state == ISP_PIPELINE_STREAM_SINGLESHOT &&
+ !isp_pipeline_ready(pipe)))
+ return 0;
+
+ /*
+ * atomic_set() doesn't include memory barrier on ARM platform for SMP
+ * scenario. We'll call it here to avoid race conditions.
+ */
+ atomic_set(stopping, 1);
+ smp_mb();
+
+ /*
+ * If module is the last one, it's writing to memory. In this case,
+ * it's necessary to check if the module is already paused due to
+ * DMA queue underrun or if it has to wait for next interrupt to be
+ * idle.
+ * If it isn't the last one, the function won't sleep but *stopping
+ * will still be set to warn next submodule caller's interrupt the
+ * module wants to be idle.
+ */
+ if (isp_pipeline_is_last(me)) {
+ struct isp_video *video = pipe->output;
+ unsigned long flags;
+ spin_lock_irqsave(&video->queue->irqlock, flags);
+ if (video->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_UNDERRUN) {
+ spin_unlock_irqrestore(&video->queue->irqlock, flags);
+ atomic_set(stopping, 0);
+ smp_mb();
+ return 0;
+ }
+ spin_unlock_irqrestore(&video->queue->irqlock, flags);
+ if (!wait_event_timeout(*wait, !atomic_read(stopping),
+ msecs_to_jiffies(1000))) {
+ atomic_set(stopping, 0);
+ smp_mb();
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * omap3isp_module_sync_is_stopped - Helper to verify if module was stopping
+ * @wait: ISP submodule's wait queue for streamoff/interrupt synchronization
+ * @stopping: flag which tells module wants to stop
+ *
+ * This function checks if ISP submodule was stopping. In case of yes, it
+ * notices the caller by setting stopping to 0 and waking up the wait queue.
+ * Returns 1 if it was stopping or 0 otherwise.
+ */
+int omap3isp_module_sync_is_stopping(wait_queue_head_t *wait,
+ atomic_t *stopping)
+{
+ if (atomic_cmpxchg(stopping, 1, 0)) {
+ wake_up(wait);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* --------------------------------------------------------------------------
+ * Clock management
+ */
+
+#define ISPCTRL_CLKS_MASK (ISPCTRL_H3A_CLK_EN | \
+ ISPCTRL_HIST_CLK_EN | \
+ ISPCTRL_RSZ_CLK_EN | \
+ (ISPCTRL_CCDC_CLK_EN | ISPCTRL_CCDC_RAM_EN) | \
+ (ISPCTRL_PREV_CLK_EN | ISPCTRL_PREV_RAM_EN))
+
+static void __isp_subclk_update(struct isp_device *isp)
+{
+ u32 clk = 0;
+
+ if (isp->subclk_resources & OMAP3_ISP_SUBCLK_H3A)
+ clk |= ISPCTRL_H3A_CLK_EN;
+
+ if (isp->subclk_resources & OMAP3_ISP_SUBCLK_HIST)
+ clk |= ISPCTRL_HIST_CLK_EN;
+
+ if (isp->subclk_resources & OMAP3_ISP_SUBCLK_RESIZER)
+ clk |= ISPCTRL_RSZ_CLK_EN;
+
+ /* NOTE: For CCDC & Preview submodules, we need to affect internal
+ * RAM as well.
+ */
+ if (isp->subclk_resources & OMAP3_ISP_SUBCLK_CCDC)
+ clk |= ISPCTRL_CCDC_CLK_EN | ISPCTRL_CCDC_RAM_EN;
+
+ if (isp->subclk_resources & OMAP3_ISP_SUBCLK_PREVIEW)
+ clk |= ISPCTRL_PREV_CLK_EN | ISPCTRL_PREV_RAM_EN;
+
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL,
+ ISPCTRL_CLKS_MASK, clk);
+}
+
+void omap3isp_subclk_enable(struct isp_device *isp,
+ enum isp_subclk_resource res)
+{
+ isp->subclk_resources |= res;
+
+ __isp_subclk_update(isp);
+}
+
+void omap3isp_subclk_disable(struct isp_device *isp,
+ enum isp_subclk_resource res)
+{
+ isp->subclk_resources &= ~res;
+
+ __isp_subclk_update(isp);
+}
+
+/*
+ * isp_enable_clocks - Enable ISP clocks
+ * @isp: OMAP3 ISP device
+ *
+ * Return 0 if successful, or clk_enable return value if any of tthem fails.
+ */
+static int isp_enable_clocks(struct isp_device *isp)
+{
+ int r;
+ unsigned long rate;
+ int divisor;
+
+ /*
+ * cam_mclk clock chain:
+ * dpll4 -> dpll4_m5 -> dpll4_m5x2 -> cam_mclk
+ *
+ * In OMAP3630 dpll4_m5x2 != 2 x dpll4_m5 but both are
+ * set to the same value. Hence the rate set for dpll4_m5
+ * has to be twice of what is set on OMAP3430 to get
+ * the required value for cam_mclk
+ */
+ if (cpu_is_omap3630())
+ divisor = 1;
+ else
+ divisor = 2;
+
+ r = clk_enable(isp->clock[ISP_CLK_CAM_ICK]);
+ if (r) {
+ dev_err(isp->dev, "clk_enable cam_ick failed\n");
+ goto out_clk_enable_ick;
+ }
+ r = clk_set_rate(isp->clock[ISP_CLK_DPLL4_M5_CK],
+ CM_CAM_MCLK_HZ/divisor);
+ if (r) {
+ dev_err(isp->dev, "clk_set_rate for dpll4_m5_ck failed\n");
+ goto out_clk_enable_mclk;
+ }
+ r = clk_enable(isp->clock[ISP_CLK_CAM_MCLK]);
+ if (r) {
+ dev_err(isp->dev, "clk_enable cam_mclk failed\n");
+ goto out_clk_enable_mclk;
+ }
+ rate = clk_get_rate(isp->clock[ISP_CLK_CAM_MCLK]);
+ if (rate != CM_CAM_MCLK_HZ)
+ dev_warn(isp->dev, "unexpected cam_mclk rate:\n"
+ " expected : %d\n"
+ " actual : %ld\n", CM_CAM_MCLK_HZ, rate);
+ r = clk_enable(isp->clock[ISP_CLK_CSI2_FCK]);
+ if (r) {
+ dev_err(isp->dev, "clk_enable csi2_fck failed\n");
+ goto out_clk_enable_csi2_fclk;
+ }
+ return 0;
+
+out_clk_enable_csi2_fclk:
+ clk_disable(isp->clock[ISP_CLK_CAM_MCLK]);
+out_clk_enable_mclk:
+ clk_disable(isp->clock[ISP_CLK_CAM_ICK]);
+out_clk_enable_ick:
+ return r;
+}
+
+/*
+ * isp_disable_clocks - Disable ISP clocks
+ * @isp: OMAP3 ISP device
+ */
+static void isp_disable_clocks(struct isp_device *isp)
+{
+ clk_disable(isp->clock[ISP_CLK_CAM_ICK]);
+ clk_disable(isp->clock[ISP_CLK_CAM_MCLK]);
+ clk_disable(isp->clock[ISP_CLK_CSI2_FCK]);
+}
+
+static const char *isp_clocks[] = {
+ "cam_ick",
+ "cam_mclk",
+ "dpll4_m5_ck",
+ "csi2_96m_fck",
+ "l3_ick",
+};
+
+static void isp_put_clocks(struct isp_device *isp)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(isp_clocks); ++i) {
+ if (isp->clock[i]) {
+ clk_put(isp->clock[i]);
+ isp->clock[i] = NULL;
+ }
+ }
+}
+
+static int isp_get_clocks(struct isp_device *isp)
+{
+ struct clk *clk;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(isp_clocks); ++i) {
+ clk = clk_get(isp->dev, isp_clocks[i]);
+ if (IS_ERR(clk)) {
+ dev_err(isp->dev, "clk_get %s failed\n", isp_clocks[i]);
+ isp_put_clocks(isp);
+ return PTR_ERR(clk);
+ }
+
+ isp->clock[i] = clk;
+ }
+
+ return 0;
+}
+
+/*
+ * omap3isp_get - Acquire the ISP resource.
+ *
+ * Initializes the clocks for the first acquire.
+ *
+ * Increment the reference count on the ISP. If the first reference is taken,
+ * enable clocks and power-up all submodules.
+ *
+ * Return a pointer to the ISP device structure, or NULL if an error occurred.
+ */
+struct isp_device *omap3isp_get(struct isp_device *isp)
+{
+ struct isp_device *__isp = isp;
+
+ if (isp == NULL)
+ return NULL;
+
+ mutex_lock(&isp->isp_mutex);
+ if (isp->ref_count > 0)
+ goto out;
+
+ if (isp_enable_clocks(isp) < 0) {
+ __isp = NULL;
+ goto out;
+ }
+
+ /* We don't want to restore context before saving it! */
+ if (isp->has_context)
+ isp_restore_ctx(isp);
+ else
+ isp->has_context = 1;
+
+ isp_enable_interrupts(isp);
+
+out:
+ if (__isp != NULL)
+ isp->ref_count++;
+ mutex_unlock(&isp->isp_mutex);
+
+ return __isp;
+}
+
+/*
+ * omap3isp_put - Release the ISP
+ *
+ * Decrement the reference count on the ISP. If the last reference is released,
+ * power-down all submodules, disable clocks and free temporary buffers.
+ */
+void omap3isp_put(struct isp_device *isp)
+{
+ if (isp == NULL)
+ return;
+
+ mutex_lock(&isp->isp_mutex);
+ BUG_ON(isp->ref_count == 0);
+ if (--isp->ref_count == 0) {
+ isp_disable_interrupts(isp);
+ isp_save_ctx(isp);
+ if (isp->needs_reset) {
+ isp_reset(isp);
+ isp->needs_reset = false;
+ }
+ isp_disable_clocks(isp);
+ }
+ mutex_unlock(&isp->isp_mutex);
+}
+
+/* --------------------------------------------------------------------------
+ * Platform device driver
+ */
+
+/*
+ * omap3isp_print_status - Prints the values of the ISP Control Module registers
+ * @isp: OMAP3 ISP device
+ */
+#define ISP_PRINT_REGISTER(isp, name)\
+ dev_dbg(isp->dev, "###ISP " #name "=0x%08x\n", \
+ isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_##name))
+#define SBL_PRINT_REGISTER(isp, name)\
+ dev_dbg(isp->dev, "###SBL " #name "=0x%08x\n", \
+ isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_##name))
+
+void omap3isp_print_status(struct isp_device *isp)
+{
+ dev_dbg(isp->dev, "-------------ISP Register dump--------------\n");
+
+ ISP_PRINT_REGISTER(isp, SYSCONFIG);
+ ISP_PRINT_REGISTER(isp, SYSSTATUS);
+ ISP_PRINT_REGISTER(isp, IRQ0ENABLE);
+ ISP_PRINT_REGISTER(isp, IRQ0STATUS);
+ ISP_PRINT_REGISTER(isp, TCTRL_GRESET_LENGTH);
+ ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_REPLAY);
+ ISP_PRINT_REGISTER(isp, CTRL);
+ ISP_PRINT_REGISTER(isp, TCTRL_CTRL);
+ ISP_PRINT_REGISTER(isp, TCTRL_FRAME);
+ ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_DELAY);
+ ISP_PRINT_REGISTER(isp, TCTRL_STRB_DELAY);
+ ISP_PRINT_REGISTER(isp, TCTRL_SHUT_DELAY);
+ ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_LENGTH);
+ ISP_PRINT_REGISTER(isp, TCTRL_STRB_LENGTH);
+ ISP_PRINT_REGISTER(isp, TCTRL_SHUT_LENGTH);
+
+ SBL_PRINT_REGISTER(isp, PCR);
+ SBL_PRINT_REGISTER(isp, SDR_REQ_EXP);
+
+ dev_dbg(isp->dev, "--------------------------------------------\n");
+}
+
+#ifdef CONFIG_PM
+
+/*
+ * Power management support.
+ *
+ * As the ISP can't properly handle an input video stream interruption on a non
+ * frame boundary, the ISP pipelines need to be stopped before sensors get
+ * suspended. However, as suspending the sensors can require a running clock,
+ * which can be provided by the ISP, the ISP can't be completely suspended
+ * before the sensor.
+ *
+ * To solve this problem power management support is split into prepare/complete
+ * and suspend/resume operations. The pipelines are stopped in prepare() and the
+ * ISP clocks get disabled in suspend(). Similarly, the clocks are reenabled in
+ * resume(), and the the pipelines are restarted in complete().
+ *
+ * TODO: PM dependencies between the ISP and sensors are not modeled explicitly
+ * yet.
+ */
+static int isp_pm_prepare(struct device *dev)
+{
+ struct isp_device *isp = dev_get_drvdata(dev);
+ int reset;
+
+ WARN_ON(mutex_is_locked(&isp->isp_mutex));
+
+ if (isp->ref_count == 0)
+ return 0;
+
+ reset = isp_suspend_modules(isp);
+ isp_disable_interrupts(isp);
+ isp_save_ctx(isp);
+ if (reset)
+ isp_reset(isp);
+
+ return 0;
+}
+
+static int isp_pm_suspend(struct device *dev)
+{
+ struct isp_device *isp = dev_get_drvdata(dev);
+
+ WARN_ON(mutex_is_locked(&isp->isp_mutex));
+
+ if (isp->ref_count)
+ isp_disable_clocks(isp);
+
+ return 0;
+}
+
+static int isp_pm_resume(struct device *dev)
+{
+ struct isp_device *isp = dev_get_drvdata(dev);
+
+ if (isp->ref_count == 0)
+ return 0;
+
+ return isp_enable_clocks(isp);
+}
+
+static void isp_pm_complete(struct device *dev)
+{
+ struct isp_device *isp = dev_get_drvdata(dev);
+
+ if (isp->ref_count == 0)
+ return;
+
+ isp_restore_ctx(isp);
+ isp_enable_interrupts(isp);
+ isp_resume_modules(isp);
+}
+
+#else
+
+#define isp_pm_prepare NULL
+#define isp_pm_suspend NULL
+#define isp_pm_resume NULL
+#define isp_pm_complete NULL
+
+#endif /* CONFIG_PM */
+
+static void isp_unregister_entities(struct isp_device *isp)
+{
+ omap3isp_csi2_unregister_entities(&isp->isp_csi2a);
+ omap3isp_ccp2_unregister_entities(&isp->isp_ccp2);
+ omap3isp_ccdc_unregister_entities(&isp->isp_ccdc);
+ omap3isp_preview_unregister_entities(&isp->isp_prev);
+ omap3isp_resizer_unregister_entities(&isp->isp_res);
+ omap3isp_stat_unregister_entities(&isp->isp_aewb);
+ omap3isp_stat_unregister_entities(&isp->isp_af);
+ omap3isp_stat_unregister_entities(&isp->isp_hist);
+
+ v4l2_device_unregister(&isp->v4l2_dev);
+ media_device_unregister(&isp->media_dev);
+}
+
+/*
+ * isp_register_subdev_group - Register a group of subdevices
+ * @isp: OMAP3 ISP device
+ * @board_info: I2C subdevs board information array
+ *
+ * Register all I2C subdevices in the board_info array. The array must be
+ * terminated by a NULL entry, and the first entry must be the sensor.
+ *
+ * Return a pointer to the sensor media entity if it has been successfully
+ * registered, or NULL otherwise.
+ */
+static struct v4l2_subdev *
+isp_register_subdev_group(struct isp_device *isp,
+ struct isp_subdev_i2c_board_info *board_info)
+{
+ struct v4l2_subdev *sensor = NULL;
+ unsigned int first;
+
+ if (board_info->board_info == NULL)
+ return NULL;
+
+ for (first = 1; board_info->board_info; ++board_info, first = 0) {
+ struct v4l2_subdev *subdev;
+ struct i2c_adapter *adapter;
+
+ adapter = i2c_get_adapter(board_info->i2c_adapter_id);
+ if (adapter == NULL) {
+ printk(KERN_ERR "%s: Unable to get I2C adapter %d for "
+ "device %s\n", __func__,
+ board_info->i2c_adapter_id,
+ board_info->board_info->type);
+ continue;
+ }
+
+ subdev = v4l2_i2c_new_subdev_board(&isp->v4l2_dev, adapter,
+ board_info->board_info, NULL);
+ if (subdev == NULL) {
+ printk(KERN_ERR "%s: Unable to register subdev %s\n",
+ __func__, board_info->board_info->type);
+ continue;
+ }
+
+ if (first)
+ sensor = subdev;
+ }
+
+ return sensor;
+}
+
+static int isp_register_entities(struct isp_device *isp)
+{
+ struct isp_platform_data *pdata = isp->pdata;
+ struct isp_v4l2_subdevs_group *subdevs;
+ int ret;
+
+ isp->media_dev.dev = isp->dev;
+ strlcpy(isp->media_dev.model, "TI OMAP3 ISP",
+ sizeof(isp->media_dev.model));
+ isp->media_dev.hw_revision = isp->revision;
+ isp->media_dev.link_notify = isp_pipeline_link_notify;
+ ret = media_device_register(&isp->media_dev);
+ if (ret < 0) {
+ printk(KERN_ERR "%s: Media device registration failed (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ isp->v4l2_dev.mdev = &isp->media_dev;
+ ret = v4l2_device_register(isp->dev, &isp->v4l2_dev);
+ if (ret < 0) {
+ printk(KERN_ERR "%s: V4L2 device registration failed (%d)\n",
+ __func__, ret);
+ goto done;
+ }
+
+ /* Register internal entities */
+ ret = omap3isp_ccp2_register_entities(&isp->isp_ccp2, &isp->v4l2_dev);
+ if (ret < 0)
+ goto done;
+
+ ret = omap3isp_csi2_register_entities(&isp->isp_csi2a, &isp->v4l2_dev);
+ if (ret < 0)
+ goto done;
+
+ ret = omap3isp_ccdc_register_entities(&isp->isp_ccdc, &isp->v4l2_dev);
+ if (ret < 0)
+ goto done;
+
+ ret = omap3isp_preview_register_entities(&isp->isp_prev,
+ &isp->v4l2_dev);
+ if (ret < 0)
+ goto done;
+
+ ret = omap3isp_resizer_register_entities(&isp->isp_res, &isp->v4l2_dev);
+ if (ret < 0)
+ goto done;
+
+ ret = omap3isp_stat_register_entities(&isp->isp_aewb, &isp->v4l2_dev);
+ if (ret < 0)
+ goto done;
+
+ ret = omap3isp_stat_register_entities(&isp->isp_af, &isp->v4l2_dev);
+ if (ret < 0)
+ goto done;
+
+ ret = omap3isp_stat_register_entities(&isp->isp_hist, &isp->v4l2_dev);
+ if (ret < 0)
+ goto done;
+
+ /* Register external entities */
+ for (subdevs = pdata->subdevs; subdevs && subdevs->subdevs; ++subdevs) {
+ struct v4l2_subdev *sensor;
+ struct media_entity *input;
+ unsigned int flags;
+ unsigned int pad;
+
+ sensor = isp_register_subdev_group(isp, subdevs->subdevs);
+ if (sensor == NULL)
+ continue;
+
+ sensor->host_priv = subdevs;
+
+ /* Connect the sensor to the correct interface module. Parallel
+ * sensors are connected directly to the CCDC, while serial
+ * sensors are connected to the CSI2a, CCP2b or CSI2c receiver
+ * through CSIPHY1 or CSIPHY2.
+ */
+ switch (subdevs->interface) {
+ case ISP_INTERFACE_PARALLEL:
+ input = &isp->isp_ccdc.subdev.entity;
+ pad = CCDC_PAD_SINK;
+ flags = 0;
+ break;
+
+ case ISP_INTERFACE_CSI2A_PHY2:
+ input = &isp->isp_csi2a.subdev.entity;
+ pad = CSI2_PAD_SINK;
+ flags = MEDIA_LNK_FL_IMMUTABLE
+ | MEDIA_LNK_FL_ENABLED;
+ break;
+
+ case ISP_INTERFACE_CCP2B_PHY1:
+ case ISP_INTERFACE_CCP2B_PHY2:
+ input = &isp->isp_ccp2.subdev.entity;
+ pad = CCP2_PAD_SINK;
+ flags = 0;
+ break;
+
+ case ISP_INTERFACE_CSI2C_PHY1:
+ input = &isp->isp_csi2c.subdev.entity;
+ pad = CSI2_PAD_SINK;
+ flags = MEDIA_LNK_FL_IMMUTABLE
+ | MEDIA_LNK_FL_ENABLED;
+ break;
+
+ default:
+ printk(KERN_ERR "%s: invalid interface type %u\n",
+ __func__, subdevs->interface);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = media_entity_create_link(&sensor->entity, 0, input, pad,
+ flags);
+ if (ret < 0)
+ goto done;
+ }
+
+ ret = v4l2_device_register_subdev_nodes(&isp->v4l2_dev);
+
+done:
+ if (ret < 0)
+ isp_unregister_entities(isp);
+
+ return ret;
+}
+
+static void isp_cleanup_modules(struct isp_device *isp)
+{
+ omap3isp_h3a_aewb_cleanup(isp);
+ omap3isp_h3a_af_cleanup(isp);
+ omap3isp_hist_cleanup(isp);
+ omap3isp_resizer_cleanup(isp);
+ omap3isp_preview_cleanup(isp);
+ omap3isp_ccdc_cleanup(isp);
+ omap3isp_ccp2_cleanup(isp);
+ omap3isp_csi2_cleanup(isp);
+}
+
+static int isp_initialize_modules(struct isp_device *isp)
+{
+ int ret;
+
+ ret = omap3isp_csiphy_init(isp);
+ if (ret < 0) {
+ dev_err(isp->dev, "CSI PHY initialization failed\n");
+ goto error_csiphy;
+ }
+
+ ret = omap3isp_csi2_init(isp);
+ if (ret < 0) {
+ dev_err(isp->dev, "CSI2 initialization failed\n");
+ goto error_csi2;
+ }
+
+ ret = omap3isp_ccp2_init(isp);
+ if (ret < 0) {
+ dev_err(isp->dev, "CCP2 initialization failed\n");
+ goto error_ccp2;
+ }
+
+ ret = omap3isp_ccdc_init(isp);
+ if (ret < 0) {
+ dev_err(isp->dev, "CCDC initialization failed\n");
+ goto error_ccdc;
+ }
+
+ ret = omap3isp_preview_init(isp);
+ if (ret < 0) {
+ dev_err(isp->dev, "Preview initialization failed\n");
+ goto error_preview;
+ }
+
+ ret = omap3isp_resizer_init(isp);
+ if (ret < 0) {
+ dev_err(isp->dev, "Resizer initialization failed\n");
+ goto error_resizer;
+ }
+
+ ret = omap3isp_hist_init(isp);
+ if (ret < 0) {
+ dev_err(isp->dev, "Histogram initialization failed\n");
+ goto error_hist;
+ }
+
+ ret = omap3isp_h3a_aewb_init(isp);
+ if (ret < 0) {
+ dev_err(isp->dev, "H3A AEWB initialization failed\n");
+ goto error_h3a_aewb;
+ }
+
+ ret = omap3isp_h3a_af_init(isp);
+ if (ret < 0) {
+ dev_err(isp->dev, "H3A AF initialization failed\n");
+ goto error_h3a_af;
+ }
+
+ /* Connect the submodules. */
+ ret = media_entity_create_link(
+ &isp->isp_csi2a.subdev.entity, CSI2_PAD_SOURCE,
+ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0);
+ if (ret < 0)
+ goto error_link;
+
+ ret = media_entity_create_link(
+ &isp->isp_ccp2.subdev.entity, CCP2_PAD_SOURCE,
+ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0);
+ if (ret < 0)
+ goto error_link;
+
+ ret = media_entity_create_link(
+ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP,
+ &isp->isp_prev.subdev.entity, PREV_PAD_SINK, 0);
+ if (ret < 0)
+ goto error_link;
+
+ ret = media_entity_create_link(
+ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_OF,
+ &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0);
+ if (ret < 0)
+ goto error_link;
+
+ ret = media_entity_create_link(
+ &isp->isp_prev.subdev.entity, PREV_PAD_SOURCE,
+ &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0);
+ if (ret < 0)
+ goto error_link;
+
+ ret = media_entity_create_link(
+ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP,
+ &isp->isp_aewb.subdev.entity, 0,
+ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
+ if (ret < 0)
+ goto error_link;
+
+ ret = media_entity_create_link(
+ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP,
+ &isp->isp_af.subdev.entity, 0,
+ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
+ if (ret < 0)
+ goto error_link;
+
+ ret = media_entity_create_link(
+ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP,
+ &isp->isp_hist.subdev.entity, 0,
+ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
+ if (ret < 0)
+ goto error_link;
+
+ return 0;
+
+error_link:
+ omap3isp_h3a_af_cleanup(isp);
+error_h3a_af:
+ omap3isp_h3a_aewb_cleanup(isp);
+error_h3a_aewb:
+ omap3isp_hist_cleanup(isp);
+error_hist:
+ omap3isp_resizer_cleanup(isp);
+error_resizer:
+ omap3isp_preview_cleanup(isp);
+error_preview:
+ omap3isp_ccdc_cleanup(isp);
+error_ccdc:
+ omap3isp_ccp2_cleanup(isp);
+error_ccp2:
+ omap3isp_csi2_cleanup(isp);
+error_csi2:
+error_csiphy:
+ return ret;
+}
+
+/*
+ * isp_remove - Remove ISP platform device
+ * @pdev: Pointer to ISP platform device
+ *
+ * Always returns 0.
+ */
+static int isp_remove(struct platform_device *pdev)
+{
+ struct isp_device *isp = platform_get_drvdata(pdev);
+ int i;
+
+ isp_unregister_entities(isp);
+ isp_cleanup_modules(isp);
+
+ omap3isp_get(isp);
+ iommu_detach_device(isp->domain, &pdev->dev);
+ iommu_domain_free(isp->domain);
+ omap3isp_put(isp);
+
+ free_irq(isp->irq_num, isp);
+ isp_put_clocks(isp);
+
+ for (i = 0; i < OMAP3_ISP_IOMEM_LAST; i++) {
+ if (isp->mmio_base[i]) {
+ iounmap(isp->mmio_base[i]);
+ isp->mmio_base[i] = NULL;
+ }
+
+ if (isp->mmio_base_phys[i]) {
+ release_mem_region(isp->mmio_base_phys[i],
+ isp->mmio_size[i]);
+ isp->mmio_base_phys[i] = 0;
+ }
+ }
+
+ regulator_put(isp->isp_csiphy1.vdd);
+ regulator_put(isp->isp_csiphy2.vdd);
+ kfree(isp);
+
+ return 0;
+}
+
+static int isp_map_mem_resource(struct platform_device *pdev,
+ struct isp_device *isp,
+ enum isp_mem_resources res)
+{
+ struct resource *mem;
+
+ /* request the mem region for the camera registers */
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, res);
+ if (!mem) {
+ dev_err(isp->dev, "no mem resource?\n");
+ return -ENODEV;
+ }
+
+ if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) {
+ dev_err(isp->dev,
+ "cannot reserve camera register I/O region\n");
+ return -ENODEV;
+ }
+ isp->mmio_base_phys[res] = mem->start;
+ isp->mmio_size[res] = resource_size(mem);
+
+ /* map the region */
+ isp->mmio_base[res] = ioremap_nocache(isp->mmio_base_phys[res],
+ isp->mmio_size[res]);
+ if (!isp->mmio_base[res]) {
+ dev_err(isp->dev, "cannot map camera register I/O region\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/*
+ * isp_probe - Probe ISP platform device
+ * @pdev: Pointer to ISP platform device
+ *
+ * Returns 0 if successful,
+ * -ENOMEM if no memory available,
+ * -ENODEV if no platform device resources found
+ * or no space for remapping registers,
+ * -EINVAL if couldn't install ISR,
+ * or clk_get return error value.
+ */
+static int isp_probe(struct platform_device *pdev)
+{
+ struct isp_platform_data *pdata = pdev->dev.platform_data;
+ struct isp_device *isp;
+ int ret;
+ int i, m;
+
+ if (pdata == NULL)
+ return -EINVAL;
+
+ isp = kzalloc(sizeof(*isp), GFP_KERNEL);
+ if (!isp) {
+ dev_err(&pdev->dev, "could not allocate memory\n");
+ return -ENOMEM;
+ }
+
+ isp->autoidle = autoidle;
+ isp->platform_cb.set_xclk = isp_set_xclk;
+ isp->platform_cb.set_pixel_clock = isp_set_pixel_clock;
+
+ mutex_init(&isp->isp_mutex);
+ spin_lock_init(&isp->stat_lock);
+
+ isp->dev = &pdev->dev;
+ isp->pdata = pdata;
+ isp->ref_count = 0;
+
+ isp->raw_dmamask = DMA_BIT_MASK(32);
+ isp->dev->dma_mask = &isp->raw_dmamask;
+ isp->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ platform_set_drvdata(pdev, isp);
+
+ /* Regulators */
+ isp->isp_csiphy1.vdd = regulator_get(&pdev->dev, "VDD_CSIPHY1");
+ isp->isp_csiphy2.vdd = regulator_get(&pdev->dev, "VDD_CSIPHY2");
+
+ /* Clocks */
+ ret = isp_map_mem_resource(pdev, isp, OMAP3_ISP_IOMEM_MAIN);
+ if (ret < 0)
+ goto error;
+
+ ret = isp_get_clocks(isp);
+ if (ret < 0)
+ goto error;
+
+ if (omap3isp_get(isp) == NULL)
+ goto error;
+
+ ret = isp_reset(isp);
+ if (ret < 0)
+ goto error_isp;
+
+ /* Memory resources */
+ isp->revision = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION);
+ dev_info(isp->dev, "Revision %d.%d found\n",
+ (isp->revision & 0xf0) >> 4, isp->revision & 0x0f);
+
+ for (m = 0; m < ARRAY_SIZE(isp_res_maps); m++)
+ if (isp->revision == isp_res_maps[m].isp_rev)
+ break;
+
+ if (m == ARRAY_SIZE(isp_res_maps)) {
+ dev_err(isp->dev, "No resource map found for ISP rev %d.%d\n",
+ (isp->revision & 0xf0) >> 4, isp->revision & 0xf);
+ ret = -ENODEV;
+ goto error_isp;
+ }
+
+ for (i = 1; i < OMAP3_ISP_IOMEM_LAST; i++) {
+ if (isp_res_maps[m].map & 1 << i) {
+ ret = isp_map_mem_resource(pdev, isp, i);
+ if (ret)
+ goto error_isp;
+ }
+ }
+
+ isp->domain = iommu_domain_alloc(pdev->dev.bus);
+ if (!isp->domain) {
+ dev_err(isp->dev, "can't alloc iommu domain\n");
+ ret = -ENOMEM;
+ goto error_isp;
+ }
+
+ ret = iommu_attach_device(isp->domain, &pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "can't attach iommu device: %d\n", ret);
+ goto free_domain;
+ }
+
+ /* Interrupt */
+ isp->irq_num = platform_get_irq(pdev, 0);
+ if (isp->irq_num <= 0) {
+ dev_err(isp->dev, "No IRQ resource\n");
+ ret = -ENODEV;
+ goto detach_dev;
+ }
+
+ if (request_irq(isp->irq_num, isp_isr, IRQF_SHARED, "OMAP3 ISP", isp)) {
+ dev_err(isp->dev, "Unable to request IRQ\n");
+ ret = -EINVAL;
+ goto detach_dev;
+ }
+
+ /* Entities */
+ ret = isp_initialize_modules(isp);
+ if (ret < 0)
+ goto error_irq;
+
+ ret = isp_register_entities(isp);
+ if (ret < 0)
+ goto error_modules;
+
+ isp_power_settings(isp, 1);
+ omap3isp_put(isp);
+
+ return 0;
+
+error_modules:
+ isp_cleanup_modules(isp);
+error_irq:
+ free_irq(isp->irq_num, isp);
+detach_dev:
+ iommu_detach_device(isp->domain, &pdev->dev);
+free_domain:
+ iommu_domain_free(isp->domain);
+error_isp:
+ omap3isp_put(isp);
+error:
+ isp_put_clocks(isp);
+
+ for (i = 0; i < OMAP3_ISP_IOMEM_LAST; i++) {
+ if (isp->mmio_base[i]) {
+ iounmap(isp->mmio_base[i]);
+ isp->mmio_base[i] = NULL;
+ }
+
+ if (isp->mmio_base_phys[i]) {
+ release_mem_region(isp->mmio_base_phys[i],
+ isp->mmio_size[i]);
+ isp->mmio_base_phys[i] = 0;
+ }
+ }
+ regulator_put(isp->isp_csiphy2.vdd);
+ regulator_put(isp->isp_csiphy1.vdd);
+ platform_set_drvdata(pdev, NULL);
+
+ mutex_destroy(&isp->isp_mutex);
+ kfree(isp);
+
+ return ret;
+}
+
+static const struct dev_pm_ops omap3isp_pm_ops = {
+ .prepare = isp_pm_prepare,
+ .suspend = isp_pm_suspend,
+ .resume = isp_pm_resume,
+ .complete = isp_pm_complete,
+};
+
+static struct platform_device_id omap3isp_id_table[] = {
+ { "omap3isp", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, omap3isp_id_table);
+
+static struct platform_driver omap3isp_driver = {
+ .probe = isp_probe,
+ .remove = isp_remove,
+ .id_table = omap3isp_id_table,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "omap3isp",
+ .pm = &omap3isp_pm_ops,
+ },
+};
+
+module_platform_driver(omap3isp_driver);
+
+MODULE_AUTHOR("Nokia Corporation");
+MODULE_DESCRIPTION("TI OMAP3 ISP driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(ISP_VIDEO_DRIVER_VERSION);
diff --git a/drivers/media/video/omap3isp/isp.h b/drivers/media/video/omap3isp/isp.h
new file mode 100644
index 00000000..d96603eb
--- /dev/null
+++ b/drivers/media/video/omap3isp/isp.h
@@ -0,0 +1,355 @@
+/*
+ * isp.h
+ *
+ * TI OMAP3 ISP - Core
+ *
+ * Copyright (C) 2009-2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP3_ISP_CORE_H
+#define OMAP3_ISP_CORE_H
+
+#include <media/omap3isp.h>
+#include <media/v4l2-device.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/wait.h>
+#include <linux/iommu.h>
+#include <plat/iommu.h>
+#include <plat/iovmm.h>
+
+#include "ispstat.h"
+#include "ispccdc.h"
+#include "ispreg.h"
+#include "ispresizer.h"
+#include "isppreview.h"
+#include "ispcsiphy.h"
+#include "ispcsi2.h"
+#include "ispccp2.h"
+
+#define IOMMU_FLAG (IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_8)
+
+#define ISP_TOK_TERM 0xFFFFFFFF /*
+ * terminating token for ISP
+ * modules reg list
+ */
+#define to_isp_device(ptr_module) \
+ container_of(ptr_module, struct isp_device, isp_##ptr_module)
+#define to_device(ptr_module) \
+ (to_isp_device(ptr_module)->dev)
+
+enum isp_mem_resources {
+ OMAP3_ISP_IOMEM_MAIN,
+ OMAP3_ISP_IOMEM_CCP2,
+ OMAP3_ISP_IOMEM_CCDC,
+ OMAP3_ISP_IOMEM_HIST,
+ OMAP3_ISP_IOMEM_H3A,
+ OMAP3_ISP_IOMEM_PREV,
+ OMAP3_ISP_IOMEM_RESZ,
+ OMAP3_ISP_IOMEM_SBL,
+ OMAP3_ISP_IOMEM_CSI2A_REGS1,
+ OMAP3_ISP_IOMEM_CSIPHY2,
+ OMAP3_ISP_IOMEM_CSI2A_REGS2,
+ OMAP3_ISP_IOMEM_CSI2C_REGS1,
+ OMAP3_ISP_IOMEM_CSIPHY1,
+ OMAP3_ISP_IOMEM_CSI2C_REGS2,
+ OMAP3_ISP_IOMEM_LAST
+};
+
+enum isp_sbl_resource {
+ OMAP3_ISP_SBL_CSI1_READ = 0x1,
+ OMAP3_ISP_SBL_CSI1_WRITE = 0x2,
+ OMAP3_ISP_SBL_CSI2A_WRITE = 0x4,
+ OMAP3_ISP_SBL_CSI2C_WRITE = 0x8,
+ OMAP3_ISP_SBL_CCDC_LSC_READ = 0x10,
+ OMAP3_ISP_SBL_CCDC_WRITE = 0x20,
+ OMAP3_ISP_SBL_PREVIEW_READ = 0x40,
+ OMAP3_ISP_SBL_PREVIEW_WRITE = 0x80,
+ OMAP3_ISP_SBL_RESIZER_READ = 0x100,
+ OMAP3_ISP_SBL_RESIZER_WRITE = 0x200,
+};
+
+enum isp_subclk_resource {
+ OMAP3_ISP_SUBCLK_CCDC = (1 << 0),
+ OMAP3_ISP_SUBCLK_H3A = (1 << 1),
+ OMAP3_ISP_SUBCLK_HIST = (1 << 2),
+ OMAP3_ISP_SUBCLK_PREVIEW = (1 << 3),
+ OMAP3_ISP_SUBCLK_RESIZER = (1 << 4),
+};
+
+/* ISP: OMAP 34xx ES 1.0 */
+#define ISP_REVISION_1_0 0x10
+/* ISP2: OMAP 34xx ES 2.0, 2.1 and 3.0 */
+#define ISP_REVISION_2_0 0x20
+/* ISP2P: OMAP 36xx */
+#define ISP_REVISION_15_0 0xF0
+
+/*
+ * struct isp_res_mapping - Map ISP io resources to ISP revision.
+ * @isp_rev: ISP_REVISION_x_x
+ * @map: bitmap for enum isp_mem_resources
+ */
+struct isp_res_mapping {
+ u32 isp_rev;
+ u32 map;
+};
+
+/*
+ * struct isp_reg - Structure for ISP register values.
+ * @reg: 32-bit Register address.
+ * @val: 32-bit Register value.
+ */
+struct isp_reg {
+ enum isp_mem_resources mmio_range;
+ u32 reg;
+ u32 val;
+};
+
+struct isp_platform_callback {
+ u32 (*set_xclk)(struct isp_device *isp, u32 xclk, u8 xclksel);
+ int (*csiphy_config)(struct isp_csiphy *phy,
+ struct isp_csiphy_dphy_cfg *dphy,
+ struct isp_csiphy_lanes_cfg *lanes);
+ void (*set_pixel_clock)(struct isp_device *isp, unsigned int pixelclk);
+};
+
+/*
+ * struct isp_device - ISP device structure.
+ * @dev: Device pointer specific to the OMAP3 ISP.
+ * @revision: Stores current ISP module revision.
+ * @irq_num: Currently used IRQ number.
+ * @mmio_base: Array with kernel base addresses for ioremapped ISP register
+ * regions.
+ * @mmio_base_phys: Array with physical L4 bus addresses for ISP register
+ * regions.
+ * @mmio_size: Array with ISP register regions size in bytes.
+ * @raw_dmamask: Raw DMA mask
+ * @stat_lock: Spinlock for handling statistics
+ * @isp_mutex: Mutex for serializing requests to ISP.
+ * @has_context: Context has been saved at least once and can be restored.
+ * @ref_count: Reference count for handling multiple ISP requests.
+ * @cam_ick: Pointer to camera interface clock structure.
+ * @cam_mclk: Pointer to camera functional clock structure.
+ * @dpll4_m5_ck: Pointer to DPLL4 M5 clock structure.
+ * @csi2_fck: Pointer to camera CSI2 complexIO clock structure.
+ * @l3_ick: Pointer to OMAP3 L3 bus interface clock.
+ * @irq: Currently attached ISP ISR callbacks information structure.
+ * @isp_af: Pointer to current settings for ISP AutoFocus SCM.
+ * @isp_hist: Pointer to current settings for ISP Histogram SCM.
+ * @isp_h3a: Pointer to current settings for ISP Auto Exposure and
+ * White Balance SCM.
+ * @isp_res: Pointer to current settings for ISP Resizer.
+ * @isp_prev: Pointer to current settings for ISP Preview.
+ * @isp_ccdc: Pointer to current settings for ISP CCDC.
+ * @iommu: Pointer to requested IOMMU instance for ISP.
+ * @platform_cb: ISP driver callback function pointers for platform code
+ *
+ * This structure is used to store the OMAP ISP Information.
+ */
+struct isp_device {
+ struct v4l2_device v4l2_dev;
+ struct media_device media_dev;
+ struct device *dev;
+ u32 revision;
+
+ /* platform HW resources */
+ struct isp_platform_data *pdata;
+ unsigned int irq_num;
+
+ void __iomem *mmio_base[OMAP3_ISP_IOMEM_LAST];
+ unsigned long mmio_base_phys[OMAP3_ISP_IOMEM_LAST];
+ resource_size_t mmio_size[OMAP3_ISP_IOMEM_LAST];
+
+ u64 raw_dmamask;
+
+ /* ISP Obj */
+ spinlock_t stat_lock; /* common lock for statistic drivers */
+ struct mutex isp_mutex; /* For handling ref_count field */
+ bool needs_reset;
+ int has_context;
+ int ref_count;
+ unsigned int autoidle;
+ u32 xclk_divisor[2]; /* Two clocks, a and b. */
+#define ISP_CLK_CAM_ICK 0
+#define ISP_CLK_CAM_MCLK 1
+#define ISP_CLK_DPLL4_M5_CK 2
+#define ISP_CLK_CSI2_FCK 3
+#define ISP_CLK_L3_ICK 4
+ struct clk *clock[5];
+
+ /* ISP modules */
+ struct ispstat isp_af;
+ struct ispstat isp_aewb;
+ struct ispstat isp_hist;
+ struct isp_res_device isp_res;
+ struct isp_prev_device isp_prev;
+ struct isp_ccdc_device isp_ccdc;
+ struct isp_csi2_device isp_csi2a;
+ struct isp_csi2_device isp_csi2c;
+ struct isp_ccp2_device isp_ccp2;
+ struct isp_csiphy isp_csiphy1;
+ struct isp_csiphy isp_csiphy2;
+
+ unsigned int sbl_resources;
+ unsigned int subclk_resources;
+
+ struct iommu_domain *domain;
+
+ struct isp_platform_callback platform_cb;
+};
+
+#define v4l2_dev_to_isp_device(dev) \
+ container_of(dev, struct isp_device, v4l2_dev)
+
+void omap3isp_hist_dma_done(struct isp_device *isp);
+
+void omap3isp_flush(struct isp_device *isp);
+
+int omap3isp_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
+ atomic_t *stopping);
+
+int omap3isp_module_sync_is_stopping(wait_queue_head_t *wait,
+ atomic_t *stopping);
+
+int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe,
+ enum isp_pipeline_stream_state state);
+void omap3isp_configure_bridge(struct isp_device *isp,
+ enum ccdc_input_entity input,
+ const struct isp_parallel_platform_data *pdata,
+ unsigned int shift);
+
+#define ISP_XCLK_NONE 0
+#define ISP_XCLK_A 1
+#define ISP_XCLK_B 2
+
+struct isp_device *omap3isp_get(struct isp_device *isp);
+void omap3isp_put(struct isp_device *isp);
+
+void omap3isp_print_status(struct isp_device *isp);
+
+void omap3isp_sbl_enable(struct isp_device *isp, enum isp_sbl_resource res);
+void omap3isp_sbl_disable(struct isp_device *isp, enum isp_sbl_resource res);
+
+void omap3isp_subclk_enable(struct isp_device *isp,
+ enum isp_subclk_resource res);
+void omap3isp_subclk_disable(struct isp_device *isp,
+ enum isp_subclk_resource res);
+
+int omap3isp_pipeline_pm_use(struct media_entity *entity, int use);
+
+int omap3isp_register_entities(struct platform_device *pdev,
+ struct v4l2_device *v4l2_dev);
+void omap3isp_unregister_entities(struct platform_device *pdev);
+
+/*
+ * isp_reg_readl - Read value of an OMAP3 ISP register
+ * @dev: Device pointer specific to the OMAP3 ISP.
+ * @isp_mmio_range: Range to which the register offset refers to.
+ * @reg_offset: Register offset to read from.
+ *
+ * Returns an unsigned 32 bit value with the required register contents.
+ */
+static inline
+u32 isp_reg_readl(struct isp_device *isp, enum isp_mem_resources isp_mmio_range,
+ u32 reg_offset)
+{
+ return __raw_readl(isp->mmio_base[isp_mmio_range] + reg_offset);
+}
+
+/*
+ * isp_reg_writel - Write value to an OMAP3 ISP register
+ * @dev: Device pointer specific to the OMAP3 ISP.
+ * @reg_value: 32 bit value to write to the register.
+ * @isp_mmio_range: Range to which the register offset refers to.
+ * @reg_offset: Register offset to write into.
+ */
+static inline
+void isp_reg_writel(struct isp_device *isp, u32 reg_value,
+ enum isp_mem_resources isp_mmio_range, u32 reg_offset)
+{
+ __raw_writel(reg_value, isp->mmio_base[isp_mmio_range] + reg_offset);
+}
+
+/*
+ * isp_reg_and - Clear individual bits in an OMAP3 ISP register
+ * @dev: Device pointer specific to the OMAP3 ISP.
+ * @mmio_range: Range to which the register offset refers to.
+ * @reg: Register offset to work on.
+ * @clr_bits: 32 bit value which would be cleared in the register.
+ */
+static inline
+void isp_reg_clr(struct isp_device *isp, enum isp_mem_resources mmio_range,
+ u32 reg, u32 clr_bits)
+{
+ u32 v = isp_reg_readl(isp, mmio_range, reg);
+
+ isp_reg_writel(isp, v & ~clr_bits, mmio_range, reg);
+}
+
+/*
+ * isp_reg_set - Set individual bits in an OMAP3 ISP register
+ * @dev: Device pointer specific to the OMAP3 ISP.
+ * @mmio_range: Range to which the register offset refers to.
+ * @reg: Register offset to work on.
+ * @set_bits: 32 bit value which would be set in the register.
+ */
+static inline
+void isp_reg_set(struct isp_device *isp, enum isp_mem_resources mmio_range,
+ u32 reg, u32 set_bits)
+{
+ u32 v = isp_reg_readl(isp, mmio_range, reg);
+
+ isp_reg_writel(isp, v | set_bits, mmio_range, reg);
+}
+
+/*
+ * isp_reg_clr_set - Clear and set invidial bits in an OMAP3 ISP register
+ * @dev: Device pointer specific to the OMAP3 ISP.
+ * @mmio_range: Range to which the register offset refers to.
+ * @reg: Register offset to work on.
+ * @clr_bits: 32 bit value which would be cleared in the register.
+ * @set_bits: 32 bit value which would be set in the register.
+ *
+ * The clear operation is done first, and then the set operation.
+ */
+static inline
+void isp_reg_clr_set(struct isp_device *isp, enum isp_mem_resources mmio_range,
+ u32 reg, u32 clr_bits, u32 set_bits)
+{
+ u32 v = isp_reg_readl(isp, mmio_range, reg);
+
+ isp_reg_writel(isp, (v & ~clr_bits) | set_bits, mmio_range, reg);
+}
+
+static inline enum v4l2_buf_type
+isp_pad_buffer_type(const struct v4l2_subdev *subdev, int pad)
+{
+ if (pad >= subdev->entity.num_pads)
+ return 0;
+
+ if (subdev->entity.pads[pad].flags & MEDIA_PAD_FL_SINK)
+ return V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ else
+ return V4L2_BUF_TYPE_VIDEO_CAPTURE;
+}
+
+#endif /* OMAP3_ISP_CORE_H */
diff --git a/drivers/media/video/omap3isp/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c
new file mode 100644
index 00000000..eaabc27f
--- /dev/null
+++ b/drivers/media/video/omap3isp/ispccdc.c
@@ -0,0 +1,2315 @@
+/*
+ * ispccdc.c
+ *
+ * TI OMAP3 ISP - CCDC module
+ *
+ * Copyright (C) 2009-2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <media/v4l2-event.h>
+
+#include "isp.h"
+#include "ispreg.h"
+#include "ispccdc.h"
+
+static struct v4l2_mbus_framefmt *
+__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
+ unsigned int pad, enum v4l2_subdev_format_whence which);
+
+static const unsigned int ccdc_fmts[] = {
+ V4L2_MBUS_FMT_Y8_1X8,
+ V4L2_MBUS_FMT_Y10_1X10,
+ V4L2_MBUS_FMT_Y12_1X12,
+ V4L2_MBUS_FMT_SGRBG8_1X8,
+ V4L2_MBUS_FMT_SRGGB8_1X8,
+ V4L2_MBUS_FMT_SBGGR8_1X8,
+ V4L2_MBUS_FMT_SGBRG8_1X8,
+ V4L2_MBUS_FMT_SGRBG10_1X10,
+ V4L2_MBUS_FMT_SRGGB10_1X10,
+ V4L2_MBUS_FMT_SBGGR10_1X10,
+ V4L2_MBUS_FMT_SGBRG10_1X10,
+ V4L2_MBUS_FMT_SGRBG12_1X12,
+ V4L2_MBUS_FMT_SRGGB12_1X12,
+ V4L2_MBUS_FMT_SBGGR12_1X12,
+ V4L2_MBUS_FMT_SGBRG12_1X12,
+};
+
+/*
+ * ccdc_print_status - Print current CCDC Module register values.
+ * @ccdc: Pointer to ISP CCDC device.
+ *
+ * Also prints other debug information stored in the CCDC module.
+ */
+#define CCDC_PRINT_REGISTER(isp, name)\
+ dev_dbg(isp->dev, "###CCDC " #name "=0x%08x\n", \
+ isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_##name))
+
+static void ccdc_print_status(struct isp_ccdc_device *ccdc)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+
+ dev_dbg(isp->dev, "-------------CCDC Register dump-------------\n");
+
+ CCDC_PRINT_REGISTER(isp, PCR);
+ CCDC_PRINT_REGISTER(isp, SYN_MODE);
+ CCDC_PRINT_REGISTER(isp, HD_VD_WID);
+ CCDC_PRINT_REGISTER(isp, PIX_LINES);
+ CCDC_PRINT_REGISTER(isp, HORZ_INFO);
+ CCDC_PRINT_REGISTER(isp, VERT_START);
+ CCDC_PRINT_REGISTER(isp, VERT_LINES);
+ CCDC_PRINT_REGISTER(isp, CULLING);
+ CCDC_PRINT_REGISTER(isp, HSIZE_OFF);
+ CCDC_PRINT_REGISTER(isp, SDOFST);
+ CCDC_PRINT_REGISTER(isp, SDR_ADDR);
+ CCDC_PRINT_REGISTER(isp, CLAMP);
+ CCDC_PRINT_REGISTER(isp, DCSUB);
+ CCDC_PRINT_REGISTER(isp, COLPTN);
+ CCDC_PRINT_REGISTER(isp, BLKCMP);
+ CCDC_PRINT_REGISTER(isp, FPC);
+ CCDC_PRINT_REGISTER(isp, FPC_ADDR);
+ CCDC_PRINT_REGISTER(isp, VDINT);
+ CCDC_PRINT_REGISTER(isp, ALAW);
+ CCDC_PRINT_REGISTER(isp, REC656IF);
+ CCDC_PRINT_REGISTER(isp, CFG);
+ CCDC_PRINT_REGISTER(isp, FMTCFG);
+ CCDC_PRINT_REGISTER(isp, FMT_HORZ);
+ CCDC_PRINT_REGISTER(isp, FMT_VERT);
+ CCDC_PRINT_REGISTER(isp, PRGEVEN0);
+ CCDC_PRINT_REGISTER(isp, PRGEVEN1);
+ CCDC_PRINT_REGISTER(isp, PRGODD0);
+ CCDC_PRINT_REGISTER(isp, PRGODD1);
+ CCDC_PRINT_REGISTER(isp, VP_OUT);
+ CCDC_PRINT_REGISTER(isp, LSC_CONFIG);
+ CCDC_PRINT_REGISTER(isp, LSC_INITIAL);
+ CCDC_PRINT_REGISTER(isp, LSC_TABLE_BASE);
+ CCDC_PRINT_REGISTER(isp, LSC_TABLE_OFFSET);
+
+ dev_dbg(isp->dev, "--------------------------------------------\n");
+}
+
+/*
+ * omap3isp_ccdc_busy - Get busy state of the CCDC.
+ * @ccdc: Pointer to ISP CCDC device.
+ */
+int omap3isp_ccdc_busy(struct isp_ccdc_device *ccdc)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+
+ return isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR) &
+ ISPCCDC_PCR_BUSY;
+}
+
+/* -----------------------------------------------------------------------------
+ * Lens Shading Compensation
+ */
+
+/*
+ * ccdc_lsc_validate_config - Check that LSC configuration is valid.
+ * @ccdc: Pointer to ISP CCDC device.
+ * @lsc_cfg: the LSC configuration to check.
+ *
+ * Returns 0 if the LSC configuration is valid, or -EINVAL if invalid.
+ */
+static int ccdc_lsc_validate_config(struct isp_ccdc_device *ccdc,
+ struct omap3isp_ccdc_lsc_config *lsc_cfg)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+ struct v4l2_mbus_framefmt *format;
+ unsigned int paxel_width, paxel_height;
+ unsigned int paxel_shift_x, paxel_shift_y;
+ unsigned int min_width, min_height, min_size;
+ unsigned int input_width, input_height;
+
+ paxel_shift_x = lsc_cfg->gain_mode_m;
+ paxel_shift_y = lsc_cfg->gain_mode_n;
+
+ if ((paxel_shift_x < 2) || (paxel_shift_x > 6) ||
+ (paxel_shift_y < 2) || (paxel_shift_y > 6)) {
+ dev_dbg(isp->dev, "CCDC: LSC: Invalid paxel size\n");
+ return -EINVAL;
+ }
+
+ if (lsc_cfg->offset & 3) {
+ dev_dbg(isp->dev, "CCDC: LSC: Offset must be a multiple of "
+ "4\n");
+ return -EINVAL;
+ }
+
+ if ((lsc_cfg->initial_x & 1) || (lsc_cfg->initial_y & 1)) {
+ dev_dbg(isp->dev, "CCDC: LSC: initial_x and y must be even\n");
+ return -EINVAL;
+ }
+
+ format = __ccdc_get_format(ccdc, NULL, CCDC_PAD_SINK,
+ V4L2_SUBDEV_FORMAT_ACTIVE);
+ input_width = format->width;
+ input_height = format->height;
+
+ /* Calculate minimum bytesize for validation */
+ paxel_width = 1 << paxel_shift_x;
+ min_width = ((input_width + lsc_cfg->initial_x + paxel_width - 1)
+ >> paxel_shift_x) + 1;
+
+ paxel_height = 1 << paxel_shift_y;
+ min_height = ((input_height + lsc_cfg->initial_y + paxel_height - 1)
+ >> paxel_shift_y) + 1;
+
+ min_size = 4 * min_width * min_height;
+ if (min_size > lsc_cfg->size) {
+ dev_dbg(isp->dev, "CCDC: LSC: too small table\n");
+ return -EINVAL;
+ }
+ if (lsc_cfg->offset < (min_width * 4)) {
+ dev_dbg(isp->dev, "CCDC: LSC: Offset is too small\n");
+ return -EINVAL;
+ }
+ if ((lsc_cfg->size / lsc_cfg->offset) < min_height) {
+ dev_dbg(isp->dev, "CCDC: LSC: Wrong size/offset combination\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * ccdc_lsc_program_table - Program Lens Shading Compensation table address.
+ * @ccdc: Pointer to ISP CCDC device.
+ */
+static void ccdc_lsc_program_table(struct isp_ccdc_device *ccdc, u32 addr)
+{
+ isp_reg_writel(to_isp_device(ccdc), addr,
+ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_TABLE_BASE);
+}
+
+/*
+ * ccdc_lsc_setup_regs - Configures the lens shading compensation module
+ * @ccdc: Pointer to ISP CCDC device.
+ */
+static void ccdc_lsc_setup_regs(struct isp_ccdc_device *ccdc,
+ struct omap3isp_ccdc_lsc_config *cfg)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+ int reg;
+
+ isp_reg_writel(isp, cfg->offset, OMAP3_ISP_IOMEM_CCDC,
+ ISPCCDC_LSC_TABLE_OFFSET);
+
+ reg = 0;
+ reg |= cfg->gain_mode_n << ISPCCDC_LSC_GAIN_MODE_N_SHIFT;
+ reg |= cfg->gain_mode_m << ISPCCDC_LSC_GAIN_MODE_M_SHIFT;
+ reg |= cfg->gain_format << ISPCCDC_LSC_GAIN_FORMAT_SHIFT;
+ isp_reg_writel(isp, reg, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG);
+
+ reg = 0;
+ reg &= ~ISPCCDC_LSC_INITIAL_X_MASK;
+ reg |= cfg->initial_x << ISPCCDC_LSC_INITIAL_X_SHIFT;
+ reg &= ~ISPCCDC_LSC_INITIAL_Y_MASK;
+ reg |= cfg->initial_y << ISPCCDC_LSC_INITIAL_Y_SHIFT;
+ isp_reg_writel(isp, reg, OMAP3_ISP_IOMEM_CCDC,
+ ISPCCDC_LSC_INITIAL);
+}
+
+static int ccdc_lsc_wait_prefetch(struct isp_ccdc_device *ccdc)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+ unsigned int wait;
+
+ isp_reg_writel(isp, IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ,
+ OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
+
+ /* timeout 1 ms */
+ for (wait = 0; wait < 1000; wait++) {
+ if (isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS) &
+ IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ) {
+ isp_reg_writel(isp, IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ,
+ OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
+ return 0;
+ }
+
+ rmb();
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
+/*
+ * __ccdc_lsc_enable - Enables/Disables the Lens Shading Compensation module.
+ * @ccdc: Pointer to ISP CCDC device.
+ * @enable: 0 Disables LSC, 1 Enables LSC.
+ */
+static int __ccdc_lsc_enable(struct isp_ccdc_device *ccdc, int enable)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+ const struct v4l2_mbus_framefmt *format =
+ __ccdc_get_format(ccdc, NULL, CCDC_PAD_SINK,
+ V4L2_SUBDEV_FORMAT_ACTIVE);
+
+ if ((format->code != V4L2_MBUS_FMT_SGRBG10_1X10) &&
+ (format->code != V4L2_MBUS_FMT_SRGGB10_1X10) &&
+ (format->code != V4L2_MBUS_FMT_SBGGR10_1X10) &&
+ (format->code != V4L2_MBUS_FMT_SGBRG10_1X10))
+ return -EINVAL;
+
+ if (enable)
+ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CCDC_LSC_READ);
+
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG,
+ ISPCCDC_LSC_ENABLE, enable ? ISPCCDC_LSC_ENABLE : 0);
+
+ if (enable) {
+ if (ccdc_lsc_wait_prefetch(ccdc) < 0) {
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC,
+ ISPCCDC_LSC_CONFIG, ISPCCDC_LSC_ENABLE);
+ ccdc->lsc.state = LSC_STATE_STOPPED;
+ dev_warn(to_device(ccdc), "LSC prefecth timeout\n");
+ return -ETIMEDOUT;
+ }
+ ccdc->lsc.state = LSC_STATE_RUNNING;
+ } else {
+ ccdc->lsc.state = LSC_STATE_STOPPING;
+ }
+
+ return 0;
+}
+
+static int ccdc_lsc_busy(struct isp_ccdc_device *ccdc)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+
+ return isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG) &
+ ISPCCDC_LSC_BUSY;
+}
+
+/* __ccdc_lsc_configure - Apply a new configuration to the LSC engine
+ * @ccdc: Pointer to ISP CCDC device
+ * @req: New configuration request
+ *
+ * context: in_interrupt()
+ */
+static int __ccdc_lsc_configure(struct isp_ccdc_device *ccdc,
+ struct ispccdc_lsc_config_req *req)
+{
+ if (!req->enable)
+ return -EINVAL;
+
+ if (ccdc_lsc_validate_config(ccdc, &req->config) < 0) {
+ dev_dbg(to_device(ccdc), "Discard LSC configuration\n");
+ return -EINVAL;
+ }
+
+ if (ccdc_lsc_busy(ccdc))
+ return -EBUSY;
+
+ ccdc_lsc_setup_regs(ccdc, &req->config);
+ ccdc_lsc_program_table(ccdc, req->table);
+ return 0;
+}
+
+/*
+ * ccdc_lsc_error_handler - Handle LSC prefetch error scenario.
+ * @ccdc: Pointer to ISP CCDC device.
+ *
+ * Disables LSC, and defers enablement to shadow registers update time.
+ */
+static void ccdc_lsc_error_handler(struct isp_ccdc_device *ccdc)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+ /*
+ * From OMAP3 TRM: When this event is pending, the module
+ * goes into transparent mode (output =input). Normal
+ * operation can be resumed at the start of the next frame
+ * after:
+ * 1) Clearing this event
+ * 2) Disabling the LSC module
+ * 3) Enabling it
+ */
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG,
+ ISPCCDC_LSC_ENABLE);
+ ccdc->lsc.state = LSC_STATE_STOPPED;
+}
+
+static void ccdc_lsc_free_request(struct isp_ccdc_device *ccdc,
+ struct ispccdc_lsc_config_req *req)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+
+ if (req == NULL)
+ return;
+
+ if (req->iovm)
+ dma_unmap_sg(isp->dev, req->iovm->sgt->sgl,
+ req->iovm->sgt->nents, DMA_TO_DEVICE);
+ if (req->table)
+ omap_iommu_vfree(isp->domain, isp->dev, req->table);
+ kfree(req);
+}
+
+static void ccdc_lsc_free_queue(struct isp_ccdc_device *ccdc,
+ struct list_head *queue)
+{
+ struct ispccdc_lsc_config_req *req, *n;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
+ list_for_each_entry_safe(req, n, queue, list) {
+ list_del(&req->list);
+ spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
+ ccdc_lsc_free_request(ccdc, req);
+ spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
+ }
+ spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
+}
+
+static void ccdc_lsc_free_table_work(struct work_struct *work)
+{
+ struct isp_ccdc_device *ccdc;
+ struct ispccdc_lsc *lsc;
+
+ lsc = container_of(work, struct ispccdc_lsc, table_work);
+ ccdc = container_of(lsc, struct isp_ccdc_device, lsc);
+
+ ccdc_lsc_free_queue(ccdc, &lsc->free_queue);
+}
+
+/*
+ * ccdc_lsc_config - Configure the LSC module from a userspace request
+ *
+ * Store the request LSC configuration in the LSC engine request pointer. The
+ * configuration will be applied to the hardware when the CCDC will be enabled,
+ * or at the next LSC interrupt if the CCDC is already running.
+ */
+static int ccdc_lsc_config(struct isp_ccdc_device *ccdc,
+ struct omap3isp_ccdc_update_config *config)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+ struct ispccdc_lsc_config_req *req;
+ unsigned long flags;
+ void *table;
+ u16 update;
+ int ret;
+
+ update = config->update &
+ (OMAP3ISP_CCDC_CONFIG_LSC | OMAP3ISP_CCDC_TBL_LSC);
+ if (!update)
+ return 0;
+
+ if (update != (OMAP3ISP_CCDC_CONFIG_LSC | OMAP3ISP_CCDC_TBL_LSC)) {
+ dev_dbg(to_device(ccdc), "%s: Both LSC configuration and table "
+ "need to be supplied\n", __func__);
+ return -EINVAL;
+ }
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (req == NULL)
+ return -ENOMEM;
+
+ if (config->flag & OMAP3ISP_CCDC_CONFIG_LSC) {
+ if (copy_from_user(&req->config, config->lsc_cfg,
+ sizeof(req->config))) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ req->enable = 1;
+
+ req->table = omap_iommu_vmalloc(isp->domain, isp->dev, 0,
+ req->config.size, IOMMU_FLAG);
+ if (IS_ERR_VALUE(req->table)) {
+ req->table = 0;
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ req->iovm = omap_find_iovm_area(isp->dev, req->table);
+ if (req->iovm == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ if (!dma_map_sg(isp->dev, req->iovm->sgt->sgl,
+ req->iovm->sgt->nents, DMA_TO_DEVICE)) {
+ ret = -ENOMEM;
+ req->iovm = NULL;
+ goto done;
+ }
+
+ dma_sync_sg_for_cpu(isp->dev, req->iovm->sgt->sgl,
+ req->iovm->sgt->nents, DMA_TO_DEVICE);
+
+ table = omap_da_to_va(isp->dev, req->table);
+ if (copy_from_user(table, config->lsc, req->config.size)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ dma_sync_sg_for_device(isp->dev, req->iovm->sgt->sgl,
+ req->iovm->sgt->nents, DMA_TO_DEVICE);
+ }
+
+ spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
+ if (ccdc->lsc.request) {
+ list_add_tail(&ccdc->lsc.request->list, &ccdc->lsc.free_queue);
+ schedule_work(&ccdc->lsc.table_work);
+ }
+ ccdc->lsc.request = req;
+ spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
+
+ ret = 0;
+
+done:
+ if (ret < 0)
+ ccdc_lsc_free_request(ccdc, req);
+
+ return ret;
+}
+
+static inline int ccdc_lsc_is_configured(struct isp_ccdc_device *ccdc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
+ if (ccdc->lsc.active) {
+ spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
+ return 1;
+ }
+ spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
+ return 0;
+}
+
+static int ccdc_lsc_enable(struct isp_ccdc_device *ccdc)
+{
+ struct ispccdc_lsc *lsc = &ccdc->lsc;
+
+ if (lsc->state != LSC_STATE_STOPPED)
+ return -EINVAL;
+
+ if (lsc->active) {
+ list_add_tail(&lsc->active->list, &lsc->free_queue);
+ lsc->active = NULL;
+ }
+
+ if (__ccdc_lsc_configure(ccdc, lsc->request) < 0) {
+ omap3isp_sbl_disable(to_isp_device(ccdc),
+ OMAP3_ISP_SBL_CCDC_LSC_READ);
+ list_add_tail(&lsc->request->list, &lsc->free_queue);
+ lsc->request = NULL;
+ goto done;
+ }
+
+ lsc->active = lsc->request;
+ lsc->request = NULL;
+ __ccdc_lsc_enable(ccdc, 1);
+
+done:
+ if (!list_empty(&lsc->free_queue))
+ schedule_work(&lsc->table_work);
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Parameters configuration
+ */
+
+/*
+ * ccdc_configure_clamp - Configure optical-black or digital clamping
+ * @ccdc: Pointer to ISP CCDC device.
+ *
+ * The CCDC performs either optical-black or digital clamp. Configure and enable
+ * the selected clamp method.
+ */
+static void ccdc_configure_clamp(struct isp_ccdc_device *ccdc)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+ u32 clamp;
+
+ if (ccdc->obclamp) {
+ clamp = ccdc->clamp.obgain << ISPCCDC_CLAMP_OBGAIN_SHIFT;
+ clamp |= ccdc->clamp.oblen << ISPCCDC_CLAMP_OBSLEN_SHIFT;
+ clamp |= ccdc->clamp.oblines << ISPCCDC_CLAMP_OBSLN_SHIFT;
+ clamp |= ccdc->clamp.obstpixel << ISPCCDC_CLAMP_OBST_SHIFT;
+ isp_reg_writel(isp, clamp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CLAMP);
+ } else {
+ isp_reg_writel(isp, ccdc->clamp.dcsubval,
+ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_DCSUB);
+ }
+
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CLAMP,
+ ISPCCDC_CLAMP_CLAMPEN,
+ ccdc->obclamp ? ISPCCDC_CLAMP_CLAMPEN : 0);
+}
+
+/*
+ * ccdc_configure_fpc - Configure Faulty Pixel Correction
+ * @ccdc: Pointer to ISP CCDC device.
+ */
+static void ccdc_configure_fpc(struct isp_ccdc_device *ccdc)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC, ISPCCDC_FPC_FPCEN);
+
+ if (!ccdc->fpc_en)
+ return;
+
+ isp_reg_writel(isp, ccdc->fpc.fpcaddr, OMAP3_ISP_IOMEM_CCDC,
+ ISPCCDC_FPC_ADDR);
+ /* The FPNUM field must be set before enabling FPC. */
+ isp_reg_writel(isp, (ccdc->fpc.fpnum << ISPCCDC_FPC_FPNUM_SHIFT),
+ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC);
+ isp_reg_writel(isp, (ccdc->fpc.fpnum << ISPCCDC_FPC_FPNUM_SHIFT) |
+ ISPCCDC_FPC_FPCEN, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC);
+}
+
+/*
+ * ccdc_configure_black_comp - Configure Black Level Compensation.
+ * @ccdc: Pointer to ISP CCDC device.
+ */
+static void ccdc_configure_black_comp(struct isp_ccdc_device *ccdc)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+ u32 blcomp;
+
+ blcomp = ccdc->blcomp.b_mg << ISPCCDC_BLKCMP_B_MG_SHIFT;
+ blcomp |= ccdc->blcomp.gb_g << ISPCCDC_BLKCMP_GB_G_SHIFT;
+ blcomp |= ccdc->blcomp.gr_cy << ISPCCDC_BLKCMP_GR_CY_SHIFT;
+ blcomp |= ccdc->blcomp.r_ye << ISPCCDC_BLKCMP_R_YE_SHIFT;
+
+ isp_reg_writel(isp, blcomp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_BLKCMP);
+}
+
+/*
+ * ccdc_configure_lpf - Configure Low-Pass Filter (LPF).
+ * @ccdc: Pointer to ISP CCDC device.
+ */
+static void ccdc_configure_lpf(struct isp_ccdc_device *ccdc)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE,
+ ISPCCDC_SYN_MODE_LPF,
+ ccdc->lpf ? ISPCCDC_SYN_MODE_LPF : 0);
+}
+
+/*
+ * ccdc_configure_alaw - Configure A-law compression.
+ * @ccdc: Pointer to ISP CCDC device.
+ */
+static void ccdc_configure_alaw(struct isp_ccdc_device *ccdc)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+ u32 alaw = 0;
+
+ switch (ccdc->syncif.datsz) {
+ case 8:
+ return;
+
+ case 10:
+ alaw = ISPCCDC_ALAW_GWDI_9_0;
+ break;
+ case 11:
+ alaw = ISPCCDC_ALAW_GWDI_10_1;
+ break;
+ case 12:
+ alaw = ISPCCDC_ALAW_GWDI_11_2;
+ break;
+ case 13:
+ alaw = ISPCCDC_ALAW_GWDI_12_3;
+ break;
+ }
+
+ if (ccdc->alaw)
+ alaw |= ISPCCDC_ALAW_CCDTBL;
+
+ isp_reg_writel(isp, alaw, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_ALAW);
+}
+
+/*
+ * ccdc_config_imgattr - Configure sensor image specific attributes.
+ * @ccdc: Pointer to ISP CCDC device.
+ * @colptn: Color pattern of the sensor.
+ */
+static void ccdc_config_imgattr(struct isp_ccdc_device *ccdc, u32 colptn)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+
+ isp_reg_writel(isp, colptn, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_COLPTN);
+}
+
+/*
+ * ccdc_config - Set CCDC configuration from userspace
+ * @ccdc: Pointer to ISP CCDC device.
+ * @userspace_add: Structure containing CCDC configuration sent from userspace.
+ *
+ * Returns 0 if successful, -EINVAL if the pointer to the configuration
+ * structure is null, or the copy_from_user function fails to copy user space
+ * memory to kernel space memory.
+ */
+static int ccdc_config(struct isp_ccdc_device *ccdc,
+ struct omap3isp_ccdc_update_config *ccdc_struct)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ccdc->lock, flags);
+ ccdc->shadow_update = 1;
+ spin_unlock_irqrestore(&ccdc->lock, flags);
+
+ if (OMAP3ISP_CCDC_ALAW & ccdc_struct->update) {
+ ccdc->alaw = !!(OMAP3ISP_CCDC_ALAW & ccdc_struct->flag);
+ ccdc->update |= OMAP3ISP_CCDC_ALAW;
+ }
+
+ if (OMAP3ISP_CCDC_LPF & ccdc_struct->update) {
+ ccdc->lpf = !!(OMAP3ISP_CCDC_LPF & ccdc_struct->flag);
+ ccdc->update |= OMAP3ISP_CCDC_LPF;
+ }
+
+ if (OMAP3ISP_CCDC_BLCLAMP & ccdc_struct->update) {
+ if (copy_from_user(&ccdc->clamp, ccdc_struct->bclamp,
+ sizeof(ccdc->clamp))) {
+ ccdc->shadow_update = 0;
+ return -EFAULT;
+ }
+
+ ccdc->obclamp = !!(OMAP3ISP_CCDC_BLCLAMP & ccdc_struct->flag);
+ ccdc->update |= OMAP3ISP_CCDC_BLCLAMP;
+ }
+
+ if (OMAP3ISP_CCDC_BCOMP & ccdc_struct->update) {
+ if (copy_from_user(&ccdc->blcomp, ccdc_struct->blcomp,
+ sizeof(ccdc->blcomp))) {
+ ccdc->shadow_update = 0;
+ return -EFAULT;
+ }
+
+ ccdc->update |= OMAP3ISP_CCDC_BCOMP;
+ }
+
+ ccdc->shadow_update = 0;
+
+ if (OMAP3ISP_CCDC_FPC & ccdc_struct->update) {
+ u32 table_old = 0;
+ u32 table_new;
+ u32 size;
+
+ if (ccdc->state != ISP_PIPELINE_STREAM_STOPPED)
+ return -EBUSY;
+
+ ccdc->fpc_en = !!(OMAP3ISP_CCDC_FPC & ccdc_struct->flag);
+
+ if (ccdc->fpc_en) {
+ if (copy_from_user(&ccdc->fpc, ccdc_struct->fpc,
+ sizeof(ccdc->fpc)))
+ return -EFAULT;
+
+ /*
+ * table_new must be 64-bytes aligned, but it's
+ * already done by omap_iommu_vmalloc().
+ */
+ size = ccdc->fpc.fpnum * 4;
+ table_new = omap_iommu_vmalloc(isp->domain, isp->dev,
+ 0, size, IOMMU_FLAG);
+ if (IS_ERR_VALUE(table_new))
+ return -ENOMEM;
+
+ if (copy_from_user(omap_da_to_va(isp->dev, table_new),
+ (__force void __user *)
+ ccdc->fpc.fpcaddr, size)) {
+ omap_iommu_vfree(isp->domain, isp->dev,
+ table_new);
+ return -EFAULT;
+ }
+
+ table_old = ccdc->fpc.fpcaddr;
+ ccdc->fpc.fpcaddr = table_new;
+ }
+
+ ccdc_configure_fpc(ccdc);
+ if (table_old != 0)
+ omap_iommu_vfree(isp->domain, isp->dev, table_old);
+ }
+
+ return ccdc_lsc_config(ccdc, ccdc_struct);
+}
+
+static void ccdc_apply_controls(struct isp_ccdc_device *ccdc)
+{
+ if (ccdc->update & OMAP3ISP_CCDC_ALAW) {
+ ccdc_configure_alaw(ccdc);
+ ccdc->update &= ~OMAP3ISP_CCDC_ALAW;
+ }
+
+ if (ccdc->update & OMAP3ISP_CCDC_LPF) {
+ ccdc_configure_lpf(ccdc);
+ ccdc->update &= ~OMAP3ISP_CCDC_LPF;
+ }
+
+ if (ccdc->update & OMAP3ISP_CCDC_BLCLAMP) {
+ ccdc_configure_clamp(ccdc);
+ ccdc->update &= ~OMAP3ISP_CCDC_BLCLAMP;
+ }
+
+ if (ccdc->update & OMAP3ISP_CCDC_BCOMP) {
+ ccdc_configure_black_comp(ccdc);
+ ccdc->update &= ~OMAP3ISP_CCDC_BCOMP;
+ }
+}
+
+/*
+ * omap3isp_ccdc_restore_context - Restore values of the CCDC module registers
+ * @dev: Pointer to ISP device
+ */
+void omap3isp_ccdc_restore_context(struct isp_device *isp)
+{
+ struct isp_ccdc_device *ccdc = &isp->isp_ccdc;
+
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, ISPCCDC_CFG_VDLC);
+
+ ccdc->update = OMAP3ISP_CCDC_ALAW | OMAP3ISP_CCDC_LPF
+ | OMAP3ISP_CCDC_BLCLAMP | OMAP3ISP_CCDC_BCOMP;
+ ccdc_apply_controls(ccdc);
+ ccdc_configure_fpc(ccdc);
+}
+
+/* -----------------------------------------------------------------------------
+ * Format- and pipeline-related configuration helpers
+ */
+
+/*
+ * ccdc_config_vp - Configure the Video Port.
+ * @ccdc: Pointer to ISP CCDC device.
+ */
+static void ccdc_config_vp(struct isp_ccdc_device *ccdc)
+{
+ struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity);
+ struct isp_device *isp = to_isp_device(ccdc);
+ unsigned long l3_ick = pipe->l3_ick;
+ unsigned int max_div = isp->revision == ISP_REVISION_15_0 ? 64 : 8;
+ unsigned int div = 0;
+ u32 fmtcfg_vp;
+
+ fmtcfg_vp = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG)
+ & ~(ISPCCDC_FMTCFG_VPIN_MASK | ISPCCDC_FMTCFG_VPIF_FRQ_MASK);
+
+ switch (ccdc->syncif.datsz) {
+ case 8:
+ case 10:
+ fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_9_0;
+ break;
+ case 11:
+ fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_10_1;
+ break;
+ case 12:
+ fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_11_2;
+ break;
+ case 13:
+ fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_12_3;
+ break;
+ };
+
+ if (pipe->input)
+ div = DIV_ROUND_UP(l3_ick, pipe->max_rate);
+ else if (ccdc->vpcfg.pixelclk)
+ div = l3_ick / ccdc->vpcfg.pixelclk;
+
+ div = clamp(div, 2U, max_div);
+ fmtcfg_vp |= (div - 2) << ISPCCDC_FMTCFG_VPIF_FRQ_SHIFT;
+
+ isp_reg_writel(isp, fmtcfg_vp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG);
+}
+
+/*
+ * ccdc_enable_vp - Enable Video Port.
+ * @ccdc: Pointer to ISP CCDC device.
+ * @enable: 0 Disables VP, 1 Enables VP
+ *
+ * This is needed for outputting image to Preview, H3A and HIST ISP submodules.
+ */
+static void ccdc_enable_vp(struct isp_ccdc_device *ccdc, u8 enable)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG,
+ ISPCCDC_FMTCFG_VPEN, enable ? ISPCCDC_FMTCFG_VPEN : 0);
+}
+
+/*
+ * ccdc_config_outlineoffset - Configure memory saving output line offset
+ * @ccdc: Pointer to ISP CCDC device.
+ * @offset: Address offset to start a new line. Must be twice the
+ * Output width and aligned on 32 byte boundary
+ * @oddeven: Specifies the odd/even line pattern to be chosen to store the
+ * output.
+ * @numlines: Set the value 0-3 for +1-4lines, 4-7 for -1-4lines.
+ *
+ * - Configures the output line offset when stored in memory
+ * - Sets the odd/even line pattern to store the output
+ * (EVENEVEN (1), ODDEVEN (2), EVENODD (3), ODDODD (4))
+ * - Configures the number of even and odd line fields in case of rearranging
+ * the lines.
+ */
+static void ccdc_config_outlineoffset(struct isp_ccdc_device *ccdc,
+ u32 offset, u8 oddeven, u8 numlines)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+
+ isp_reg_writel(isp, offset & 0xffff,
+ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HSIZE_OFF);
+
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
+ ISPCCDC_SDOFST_FINV);
+
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
+ ISPCCDC_SDOFST_FOFST_4L);
+
+ switch (oddeven) {
+ case EVENEVEN:
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
+ (numlines & 0x7) << ISPCCDC_SDOFST_LOFST0_SHIFT);
+ break;
+ case ODDEVEN:
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
+ (numlines & 0x7) << ISPCCDC_SDOFST_LOFST1_SHIFT);
+ break;
+ case EVENODD:
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
+ (numlines & 0x7) << ISPCCDC_SDOFST_LOFST2_SHIFT);
+ break;
+ case ODDODD:
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
+ (numlines & 0x7) << ISPCCDC_SDOFST_LOFST3_SHIFT);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * ccdc_set_outaddr - Set memory address to save output image
+ * @ccdc: Pointer to ISP CCDC device.
+ * @addr: ISP MMU Mapped 32-bit memory address aligned on 32 byte boundary.
+ *
+ * Sets the memory address where the output will be saved.
+ */
+static void ccdc_set_outaddr(struct isp_ccdc_device *ccdc, u32 addr)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+
+ isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDR_ADDR);
+}
+
+/*
+ * omap3isp_ccdc_max_rate - Calculate maximum input data rate based on the input
+ * @ccdc: Pointer to ISP CCDC device.
+ * @max_rate: Maximum calculated data rate.
+ *
+ * Returns in *max_rate less value between calculated and passed
+ */
+void omap3isp_ccdc_max_rate(struct isp_ccdc_device *ccdc,
+ unsigned int *max_rate)
+{
+ struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity);
+ unsigned int rate;
+
+ if (pipe == NULL)
+ return;
+
+ /*
+ * TRM says that for parallel sensors the maximum data rate
+ * should be 90% form L3/2 clock, otherwise just L3/2.
+ */
+ if (ccdc->input == CCDC_INPUT_PARALLEL)
+ rate = pipe->l3_ick / 2 * 9 / 10;
+ else
+ rate = pipe->l3_ick / 2;
+
+ *max_rate = min(*max_rate, rate);
+}
+
+/*
+ * ccdc_config_sync_if - Set CCDC sync interface configuration
+ * @ccdc: Pointer to ISP CCDC device.
+ * @syncif: Structure containing the sync parameters like field state, CCDC in
+ * master/slave mode, raw/yuv data, polarity of data, field, hs, vs
+ * signals.
+ */
+static void ccdc_config_sync_if(struct isp_ccdc_device *ccdc,
+ struct ispccdc_syncif *syncif)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+ u32 syn_mode = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC,
+ ISPCCDC_SYN_MODE);
+
+ syn_mode |= ISPCCDC_SYN_MODE_VDHDEN;
+
+ if (syncif->fldstat)
+ syn_mode |= ISPCCDC_SYN_MODE_FLDSTAT;
+ else
+ syn_mode &= ~ISPCCDC_SYN_MODE_FLDSTAT;
+
+ syn_mode &= ~ISPCCDC_SYN_MODE_DATSIZ_MASK;
+ switch (syncif->datsz) {
+ case 8:
+ syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_8;
+ break;
+ case 10:
+ syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_10;
+ break;
+ case 11:
+ syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_11;
+ break;
+ case 12:
+ syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_12;
+ break;
+ };
+
+ if (syncif->fldmode)
+ syn_mode |= ISPCCDC_SYN_MODE_FLDMODE;
+ else
+ syn_mode &= ~ISPCCDC_SYN_MODE_FLDMODE;
+
+ if (syncif->datapol)
+ syn_mode |= ISPCCDC_SYN_MODE_DATAPOL;
+ else
+ syn_mode &= ~ISPCCDC_SYN_MODE_DATAPOL;
+
+ if (syncif->fldpol)
+ syn_mode |= ISPCCDC_SYN_MODE_FLDPOL;
+ else
+ syn_mode &= ~ISPCCDC_SYN_MODE_FLDPOL;
+
+ if (syncif->hdpol)
+ syn_mode |= ISPCCDC_SYN_MODE_HDPOL;
+ else
+ syn_mode &= ~ISPCCDC_SYN_MODE_HDPOL;
+
+ if (syncif->vdpol)
+ syn_mode |= ISPCCDC_SYN_MODE_VDPOL;
+ else
+ syn_mode &= ~ISPCCDC_SYN_MODE_VDPOL;
+
+ if (syncif->ccdc_mastermode) {
+ syn_mode |= ISPCCDC_SYN_MODE_FLDOUT | ISPCCDC_SYN_MODE_VDHDOUT;
+ isp_reg_writel(isp,
+ syncif->hs_width << ISPCCDC_HD_VD_WID_HDW_SHIFT
+ | syncif->vs_width << ISPCCDC_HD_VD_WID_VDW_SHIFT,
+ OMAP3_ISP_IOMEM_CCDC,
+ ISPCCDC_HD_VD_WID);
+
+ isp_reg_writel(isp,
+ syncif->ppln << ISPCCDC_PIX_LINES_PPLN_SHIFT
+ | syncif->hlprf << ISPCCDC_PIX_LINES_HLPRF_SHIFT,
+ OMAP3_ISP_IOMEM_CCDC,
+ ISPCCDC_PIX_LINES);
+ } else
+ syn_mode &= ~(ISPCCDC_SYN_MODE_FLDOUT |
+ ISPCCDC_SYN_MODE_VDHDOUT);
+
+ isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE);
+
+ if (!syncif->bt_r656_en)
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF,
+ ISPCCDC_REC656IF_R656ON);
+}
+
+/* CCDC formats descriptions */
+static const u32 ccdc_sgrbg_pattern =
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC0_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC1_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC2_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC3_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC0_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC1_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC2_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC3_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC0_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC1_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC2_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC3_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC0_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC1_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC2_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC3_SHIFT;
+
+static const u32 ccdc_srggb_pattern =
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC0_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC1_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC2_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC3_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC0_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC1_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC2_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC3_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC0_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC1_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC2_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC3_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC0_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC1_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC2_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC3_SHIFT;
+
+static const u32 ccdc_sbggr_pattern =
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC0_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC1_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC2_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC3_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC0_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC1_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC2_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC3_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC0_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC1_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC2_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC3_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC0_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC1_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC2_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC3_SHIFT;
+
+static const u32 ccdc_sgbrg_pattern =
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC0_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC1_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC2_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC3_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC0_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC1_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC2_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC3_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC0_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC1_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC2_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC3_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC0_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC1_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC2_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC3_SHIFT;
+
+static void ccdc_configure(struct isp_ccdc_device *ccdc)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+ struct isp_parallel_platform_data *pdata = NULL;
+ struct v4l2_subdev *sensor;
+ struct v4l2_mbus_framefmt *format;
+ const struct isp_format_info *fmt_info;
+ struct v4l2_subdev_format fmt_src;
+ unsigned int depth_out;
+ unsigned int depth_in = 0;
+ struct media_pad *pad;
+ unsigned long flags;
+ unsigned int shift;
+ u32 syn_mode;
+ u32 ccdc_pattern;
+
+ pad = media_entity_remote_source(&ccdc->pads[CCDC_PAD_SINK]);
+ sensor = media_entity_to_v4l2_subdev(pad->entity);
+ if (ccdc->input == CCDC_INPUT_PARALLEL)
+ pdata = &((struct isp_v4l2_subdevs_group *)sensor->host_priv)
+ ->bus.parallel;
+
+ /* Compute shift value for lane shifter to configure the bridge. */
+ fmt_src.pad = pad->index;
+ fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ if (!v4l2_subdev_call(sensor, pad, get_fmt, NULL, &fmt_src)) {
+ fmt_info = omap3isp_video_format_info(fmt_src.format.code);
+ depth_in = fmt_info->bpp;
+ }
+
+ fmt_info = omap3isp_video_format_info
+ (isp->isp_ccdc.formats[CCDC_PAD_SINK].code);
+ depth_out = fmt_info->bpp;
+
+ shift = depth_in - depth_out;
+ omap3isp_configure_bridge(isp, ccdc->input, pdata, shift);
+
+ ccdc->syncif.datsz = depth_out;
+ ccdc->syncif.hdpol = pdata ? pdata->hs_pol : 0;
+ ccdc->syncif.vdpol = pdata ? pdata->vs_pol : 0;
+ ccdc_config_sync_if(ccdc, &ccdc->syncif);
+
+ /* CCDC_PAD_SINK */
+ format = &ccdc->formats[CCDC_PAD_SINK];
+
+ syn_mode = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE);
+
+ /* Use the raw, unprocessed data when writing to memory. The H3A and
+ * histogram modules are still fed with lens shading corrected data.
+ */
+ syn_mode &= ~ISPCCDC_SYN_MODE_VP2SDR;
+
+ if (ccdc->output & CCDC_OUTPUT_MEMORY)
+ syn_mode |= ISPCCDC_SYN_MODE_WEN;
+ else
+ syn_mode &= ~ISPCCDC_SYN_MODE_WEN;
+
+ if (ccdc->output & CCDC_OUTPUT_RESIZER)
+ syn_mode |= ISPCCDC_SYN_MODE_SDR2RSZ;
+ else
+ syn_mode &= ~ISPCCDC_SYN_MODE_SDR2RSZ;
+
+ /* Use PACK8 mode for 1byte per pixel formats. */
+ if (omap3isp_video_format_info(format->code)->bpp <= 8)
+ syn_mode |= ISPCCDC_SYN_MODE_PACK8;
+ else
+ syn_mode &= ~ISPCCDC_SYN_MODE_PACK8;
+
+ isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE);
+
+ /* Mosaic filter */
+ switch (format->code) {
+ case V4L2_MBUS_FMT_SRGGB10_1X10:
+ case V4L2_MBUS_FMT_SRGGB12_1X12:
+ ccdc_pattern = ccdc_srggb_pattern;
+ break;
+ case V4L2_MBUS_FMT_SBGGR10_1X10:
+ case V4L2_MBUS_FMT_SBGGR12_1X12:
+ ccdc_pattern = ccdc_sbggr_pattern;
+ break;
+ case V4L2_MBUS_FMT_SGBRG10_1X10:
+ case V4L2_MBUS_FMT_SGBRG12_1X12:
+ ccdc_pattern = ccdc_sgbrg_pattern;
+ break;
+ default:
+ /* Use GRBG */
+ ccdc_pattern = ccdc_sgrbg_pattern;
+ break;
+ }
+ ccdc_config_imgattr(ccdc, ccdc_pattern);
+
+ /* Generate VD0 on the last line of the image and VD1 on the
+ * 2/3 height line.
+ */
+ isp_reg_writel(isp, ((format->height - 2) << ISPCCDC_VDINT_0_SHIFT) |
+ ((format->height * 2 / 3) << ISPCCDC_VDINT_1_SHIFT),
+ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VDINT);
+
+ /* CCDC_PAD_SOURCE_OF */
+ format = &ccdc->formats[CCDC_PAD_SOURCE_OF];
+
+ isp_reg_writel(isp, (0 << ISPCCDC_HORZ_INFO_SPH_SHIFT) |
+ ((format->width - 1) << ISPCCDC_HORZ_INFO_NPH_SHIFT),
+ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HORZ_INFO);
+ isp_reg_writel(isp, 0 << ISPCCDC_VERT_START_SLV0_SHIFT,
+ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_START);
+ isp_reg_writel(isp, (format->height - 1)
+ << ISPCCDC_VERT_LINES_NLV_SHIFT,
+ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES);
+
+ ccdc_config_outlineoffset(ccdc, ccdc->video_out.bpl_value, 0, 0);
+
+ /* CCDC_PAD_SOURCE_VP */
+ format = &ccdc->formats[CCDC_PAD_SOURCE_VP];
+
+ isp_reg_writel(isp, (0 << ISPCCDC_FMT_HORZ_FMTSPH_SHIFT) |
+ (format->width << ISPCCDC_FMT_HORZ_FMTLNH_SHIFT),
+ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_HORZ);
+ isp_reg_writel(isp, (0 << ISPCCDC_FMT_VERT_FMTSLV_SHIFT) |
+ ((format->height + 1) << ISPCCDC_FMT_VERT_FMTLNV_SHIFT),
+ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_VERT);
+
+ isp_reg_writel(isp, (format->width << ISPCCDC_VP_OUT_HORZ_NUM_SHIFT) |
+ (format->height << ISPCCDC_VP_OUT_VERT_NUM_SHIFT),
+ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VP_OUT);
+
+ spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
+ if (ccdc->lsc.request == NULL)
+ goto unlock;
+
+ WARN_ON(ccdc->lsc.active);
+
+ /* Get last good LSC configuration. If it is not supported for
+ * the current active resolution discard it.
+ */
+ if (ccdc->lsc.active == NULL &&
+ __ccdc_lsc_configure(ccdc, ccdc->lsc.request) == 0) {
+ ccdc->lsc.active = ccdc->lsc.request;
+ } else {
+ list_add_tail(&ccdc->lsc.request->list, &ccdc->lsc.free_queue);
+ schedule_work(&ccdc->lsc.table_work);
+ }
+
+ ccdc->lsc.request = NULL;
+
+unlock:
+ spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
+
+ ccdc_apply_controls(ccdc);
+}
+
+static void __ccdc_enable(struct isp_ccdc_device *ccdc, int enable)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR,
+ ISPCCDC_PCR_EN, enable ? ISPCCDC_PCR_EN : 0);
+}
+
+static int ccdc_disable(struct isp_ccdc_device *ccdc)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&ccdc->lock, flags);
+ if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS)
+ ccdc->stopping = CCDC_STOP_REQUEST;
+ spin_unlock_irqrestore(&ccdc->lock, flags);
+
+ ret = wait_event_timeout(ccdc->wait,
+ ccdc->stopping == CCDC_STOP_FINISHED,
+ msecs_to_jiffies(2000));
+ if (ret == 0) {
+ ret = -ETIMEDOUT;
+ dev_warn(to_device(ccdc), "CCDC stop timeout!\n");
+ }
+
+ omap3isp_sbl_disable(to_isp_device(ccdc), OMAP3_ISP_SBL_CCDC_LSC_READ);
+
+ mutex_lock(&ccdc->ioctl_lock);
+ ccdc_lsc_free_request(ccdc, ccdc->lsc.request);
+ ccdc->lsc.request = ccdc->lsc.active;
+ ccdc->lsc.active = NULL;
+ cancel_work_sync(&ccdc->lsc.table_work);
+ ccdc_lsc_free_queue(ccdc, &ccdc->lsc.free_queue);
+ mutex_unlock(&ccdc->ioctl_lock);
+
+ ccdc->stopping = CCDC_STOP_NOT_REQUESTED;
+
+ return ret > 0 ? 0 : ret;
+}
+
+static void ccdc_enable(struct isp_ccdc_device *ccdc)
+{
+ if (ccdc_lsc_is_configured(ccdc))
+ __ccdc_lsc_enable(ccdc, 1);
+ __ccdc_enable(ccdc, 1);
+}
+
+/* -----------------------------------------------------------------------------
+ * Interrupt handling
+ */
+
+/*
+ * ccdc_sbl_busy - Poll idle state of CCDC and related SBL memory write bits
+ * @ccdc: Pointer to ISP CCDC device.
+ *
+ * Returns zero if the CCDC is idle and the image has been written to
+ * memory, too.
+ */
+static int ccdc_sbl_busy(struct isp_ccdc_device *ccdc)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+
+ return omap3isp_ccdc_busy(ccdc)
+ | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_0) &
+ ISPSBL_CCDC_WR_0_DATA_READY)
+ | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_1) &
+ ISPSBL_CCDC_WR_0_DATA_READY)
+ | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_2) &
+ ISPSBL_CCDC_WR_0_DATA_READY)
+ | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_3) &
+ ISPSBL_CCDC_WR_0_DATA_READY);
+}
+
+/*
+ * ccdc_sbl_wait_idle - Wait until the CCDC and related SBL are idle
+ * @ccdc: Pointer to ISP CCDC device.
+ * @max_wait: Max retry count in us for wait for idle/busy transition.
+ */
+static int ccdc_sbl_wait_idle(struct isp_ccdc_device *ccdc,
+ unsigned int max_wait)
+{
+ unsigned int wait = 0;
+
+ if (max_wait == 0)
+ max_wait = 10000; /* 10 ms */
+
+ for (wait = 0; wait <= max_wait; wait++) {
+ if (!ccdc_sbl_busy(ccdc))
+ return 0;
+
+ rmb();
+ udelay(1);
+ }
+
+ return -EBUSY;
+}
+
+/* __ccdc_handle_stopping - Handle CCDC and/or LSC stopping sequence
+ * @ccdc: Pointer to ISP CCDC device.
+ * @event: Pointing which event trigger handler
+ *
+ * Return 1 when the event and stopping request combination is satisfied,
+ * zero otherwise.
+ */
+static int __ccdc_handle_stopping(struct isp_ccdc_device *ccdc, u32 event)
+{
+ int rval = 0;
+
+ switch ((ccdc->stopping & 3) | event) {
+ case CCDC_STOP_REQUEST | CCDC_EVENT_VD1:
+ if (ccdc->lsc.state != LSC_STATE_STOPPED)
+ __ccdc_lsc_enable(ccdc, 0);
+ __ccdc_enable(ccdc, 0);
+ ccdc->stopping = CCDC_STOP_EXECUTED;
+ return 1;
+
+ case CCDC_STOP_EXECUTED | CCDC_EVENT_VD0:
+ ccdc->stopping |= CCDC_STOP_CCDC_FINISHED;
+ if (ccdc->lsc.state == LSC_STATE_STOPPED)
+ ccdc->stopping |= CCDC_STOP_LSC_FINISHED;
+ rval = 1;
+ break;
+
+ case CCDC_STOP_EXECUTED | CCDC_EVENT_LSC_DONE:
+ ccdc->stopping |= CCDC_STOP_LSC_FINISHED;
+ rval = 1;
+ break;
+
+ case CCDC_STOP_EXECUTED | CCDC_EVENT_VD1:
+ return 1;
+ }
+
+ if (ccdc->stopping == CCDC_STOP_FINISHED) {
+ wake_up(&ccdc->wait);
+ rval = 1;
+ }
+
+ return rval;
+}
+
+static void ccdc_hs_vs_isr(struct isp_ccdc_device *ccdc)
+{
+ struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity);
+ struct video_device *vdev = ccdc->subdev.devnode;
+ struct v4l2_event event;
+
+ memset(&event, 0, sizeof(event));
+ event.type = V4L2_EVENT_FRAME_SYNC;
+ event.u.frame_sync.frame_sequence = atomic_read(&pipe->frame_number);
+
+ v4l2_event_queue(vdev, &event);
+}
+
+/*
+ * ccdc_lsc_isr - Handle LSC events
+ * @ccdc: Pointer to ISP CCDC device.
+ * @events: LSC events
+ */
+static void ccdc_lsc_isr(struct isp_ccdc_device *ccdc, u32 events)
+{
+ unsigned long flags;
+
+ if (events & IRQ0STATUS_CCDC_LSC_PREF_ERR_IRQ) {
+ struct isp_pipeline *pipe =
+ to_isp_pipeline(&ccdc->subdev.entity);
+
+ ccdc_lsc_error_handler(ccdc);
+ pipe->error = true;
+ dev_dbg(to_device(ccdc), "lsc prefetch error\n");
+ }
+
+ if (!(events & IRQ0STATUS_CCDC_LSC_DONE_IRQ))
+ return;
+
+ /* LSC_DONE interrupt occur, there are two cases
+ * 1. stopping for reconfiguration
+ * 2. stopping because of STREAM OFF command
+ */
+ spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
+
+ if (ccdc->lsc.state == LSC_STATE_STOPPING)
+ ccdc->lsc.state = LSC_STATE_STOPPED;
+
+ if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_LSC_DONE))
+ goto done;
+
+ if (ccdc->lsc.state != LSC_STATE_RECONFIG)
+ goto done;
+
+ /* LSC is in STOPPING state, change to the new state */
+ ccdc->lsc.state = LSC_STATE_STOPPED;
+
+ /* This is an exception. Start of frame and LSC_DONE interrupt
+ * have been received on the same time. Skip this event and wait
+ * for better times.
+ */
+ if (events & IRQ0STATUS_HS_VS_IRQ)
+ goto done;
+
+ /* The LSC engine is stopped at this point. Enable it if there's a
+ * pending request.
+ */
+ if (ccdc->lsc.request == NULL)
+ goto done;
+
+ ccdc_lsc_enable(ccdc);
+
+done:
+ spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
+}
+
+static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc)
+{
+ struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity);
+ struct isp_device *isp = to_isp_device(ccdc);
+ struct isp_buffer *buffer;
+ int restart = 0;
+
+ /* The CCDC generates VD0 interrupts even when disabled (the datasheet
+ * doesn't explicitly state if that's supposed to happen or not, so it
+ * can be considered as a hardware bug or as a feature, but we have to
+ * deal with it anyway). Disabling the CCDC when no buffer is available
+ * would thus not be enough, we need to handle the situation explicitly.
+ */
+ if (list_empty(&ccdc->video_out.dmaqueue))
+ goto done;
+
+ /* We're in continuous mode, and memory writes were disabled due to a
+ * buffer underrun. Reenable them now that we have a buffer. The buffer
+ * address has been set in ccdc_video_queue.
+ */
+ if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS && ccdc->underrun) {
+ restart = 1;
+ ccdc->underrun = 0;
+ goto done;
+ }
+
+ if (ccdc_sbl_wait_idle(ccdc, 1000)) {
+ dev_info(isp->dev, "CCDC won't become idle!\n");
+ goto done;
+ }
+
+ buffer = omap3isp_video_buffer_next(&ccdc->video_out);
+ if (buffer != NULL) {
+ ccdc_set_outaddr(ccdc, buffer->isp_addr);
+ restart = 1;
+ }
+
+ pipe->state |= ISP_PIPELINE_IDLE_OUTPUT;
+
+ if (ccdc->state == ISP_PIPELINE_STREAM_SINGLESHOT &&
+ isp_pipeline_ready(pipe))
+ omap3isp_pipeline_set_stream(pipe,
+ ISP_PIPELINE_STREAM_SINGLESHOT);
+
+done:
+ return restart;
+}
+
+/*
+ * ccdc_vd0_isr - Handle VD0 event
+ * @ccdc: Pointer to ISP CCDC device.
+ *
+ * Executes LSC deferred enablement before next frame starts.
+ */
+static void ccdc_vd0_isr(struct isp_ccdc_device *ccdc)
+{
+ unsigned long flags;
+ int restart = 0;
+
+ if (ccdc->output & CCDC_OUTPUT_MEMORY)
+ restart = ccdc_isr_buffer(ccdc);
+
+ spin_lock_irqsave(&ccdc->lock, flags);
+ if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_VD0)) {
+ spin_unlock_irqrestore(&ccdc->lock, flags);
+ return;
+ }
+
+ if (!ccdc->shadow_update)
+ ccdc_apply_controls(ccdc);
+ spin_unlock_irqrestore(&ccdc->lock, flags);
+
+ if (restart)
+ ccdc_enable(ccdc);
+}
+
+/*
+ * ccdc_vd1_isr - Handle VD1 event
+ * @ccdc: Pointer to ISP CCDC device.
+ */
+static void ccdc_vd1_isr(struct isp_ccdc_device *ccdc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
+
+ /*
+ * Depending on the CCDC pipeline state, CCDC stopping should be
+ * handled differently. In SINGLESHOT we emulate an internal CCDC
+ * stopping because the CCDC hw works only in continuous mode.
+ * When CONTINUOUS pipeline state is used and the CCDC writes it's
+ * data to memory the CCDC and LSC are stopped immediately but
+ * without change the CCDC stopping state machine. The CCDC
+ * stopping state machine should be used only when user request
+ * for stopping is received (SINGLESHOT is an exeption).
+ */
+ switch (ccdc->state) {
+ case ISP_PIPELINE_STREAM_SINGLESHOT:
+ ccdc->stopping = CCDC_STOP_REQUEST;
+ break;
+
+ case ISP_PIPELINE_STREAM_CONTINUOUS:
+ if (ccdc->output & CCDC_OUTPUT_MEMORY) {
+ if (ccdc->lsc.state != LSC_STATE_STOPPED)
+ __ccdc_lsc_enable(ccdc, 0);
+ __ccdc_enable(ccdc, 0);
+ }
+ break;
+
+ case ISP_PIPELINE_STREAM_STOPPED:
+ break;
+ }
+
+ if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_VD1))
+ goto done;
+
+ if (ccdc->lsc.request == NULL)
+ goto done;
+
+ /*
+ * LSC need to be reconfigured. Stop it here and on next LSC_DONE IRQ
+ * do the appropriate changes in registers
+ */
+ if (ccdc->lsc.state == LSC_STATE_RUNNING) {
+ __ccdc_lsc_enable(ccdc, 0);
+ ccdc->lsc.state = LSC_STATE_RECONFIG;
+ goto done;
+ }
+
+ /* LSC has been in STOPPED state, enable it */
+ if (ccdc->lsc.state == LSC_STATE_STOPPED)
+ ccdc_lsc_enable(ccdc);
+
+done:
+ spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
+}
+
+/*
+ * omap3isp_ccdc_isr - Configure CCDC during interframe time.
+ * @ccdc: Pointer to ISP CCDC device.
+ * @events: CCDC events
+ */
+int omap3isp_ccdc_isr(struct isp_ccdc_device *ccdc, u32 events)
+{
+ if (ccdc->state == ISP_PIPELINE_STREAM_STOPPED)
+ return 0;
+
+ if (events & IRQ0STATUS_CCDC_VD1_IRQ)
+ ccdc_vd1_isr(ccdc);
+
+ ccdc_lsc_isr(ccdc, events);
+
+ if (events & IRQ0STATUS_CCDC_VD0_IRQ)
+ ccdc_vd0_isr(ccdc);
+
+ if (events & IRQ0STATUS_HS_VS_IRQ)
+ ccdc_hs_vs_isr(ccdc);
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP video operations
+ */
+
+static int ccdc_video_queue(struct isp_video *video, struct isp_buffer *buffer)
+{
+ struct isp_ccdc_device *ccdc = &video->isp->isp_ccdc;
+
+ if (!(ccdc->output & CCDC_OUTPUT_MEMORY))
+ return -ENODEV;
+
+ ccdc_set_outaddr(ccdc, buffer->isp_addr);
+
+ /* We now have a buffer queued on the output, restart the pipeline
+ * on the next CCDC interrupt if running in continuous mode (or when
+ * starting the stream).
+ */
+ ccdc->underrun = 1;
+
+ return 0;
+}
+
+static const struct isp_video_operations ccdc_video_ops = {
+ .queue = ccdc_video_queue,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+/*
+ * ccdc_ioctl - CCDC module private ioctl's
+ * @sd: ISP CCDC V4L2 subdevice
+ * @cmd: ioctl command
+ * @arg: ioctl argument
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+static long ccdc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
+ int ret;
+
+ switch (cmd) {
+ case VIDIOC_OMAP3ISP_CCDC_CFG:
+ mutex_lock(&ccdc->ioctl_lock);
+ ret = ccdc_config(ccdc, arg);
+ mutex_unlock(&ccdc->ioctl_lock);
+ break;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return ret;
+}
+
+static int ccdc_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ if (sub->type != V4L2_EVENT_FRAME_SYNC)
+ return -EINVAL;
+
+ /* line number is zero at frame start */
+ if (sub->id != 0)
+ return -EINVAL;
+
+ return v4l2_event_subscribe(fh, sub, OMAP3ISP_CCDC_NEVENTS);
+}
+
+static int ccdc_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ return v4l2_event_unsubscribe(fh, sub);
+}
+
+/*
+ * ccdc_set_stream - Enable/Disable streaming on the CCDC module
+ * @sd: ISP CCDC V4L2 subdevice
+ * @enable: Enable/disable stream
+ *
+ * When writing to memory, the CCDC hardware can't be enabled without a memory
+ * buffer to write to. As the s_stream operation is called in response to a
+ * STREAMON call without any buffer queued yet, just update the enabled field
+ * and return immediately. The CCDC will be enabled in ccdc_isr_buffer().
+ *
+ * When not writing to memory enable the CCDC immediately.
+ */
+static int ccdc_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
+ struct isp_device *isp = to_isp_device(ccdc);
+ int ret = 0;
+
+ if (ccdc->state == ISP_PIPELINE_STREAM_STOPPED) {
+ if (enable == ISP_PIPELINE_STREAM_STOPPED)
+ return 0;
+
+ omap3isp_subclk_enable(isp, OMAP3_ISP_SUBCLK_CCDC);
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG,
+ ISPCCDC_CFG_VDLC);
+
+ ccdc_configure(ccdc);
+
+ /* TODO: Don't configure the video port if all of its output
+ * links are inactive.
+ */
+ ccdc_config_vp(ccdc);
+ ccdc_enable_vp(ccdc, 1);
+ ccdc_print_status(ccdc);
+ }
+
+ switch (enable) {
+ case ISP_PIPELINE_STREAM_CONTINUOUS:
+ if (ccdc->output & CCDC_OUTPUT_MEMORY)
+ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CCDC_WRITE);
+
+ if (ccdc->underrun || !(ccdc->output & CCDC_OUTPUT_MEMORY))
+ ccdc_enable(ccdc);
+
+ ccdc->underrun = 0;
+ break;
+
+ case ISP_PIPELINE_STREAM_SINGLESHOT:
+ if (ccdc->output & CCDC_OUTPUT_MEMORY &&
+ ccdc->state != ISP_PIPELINE_STREAM_SINGLESHOT)
+ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CCDC_WRITE);
+
+ ccdc_enable(ccdc);
+ break;
+
+ case ISP_PIPELINE_STREAM_STOPPED:
+ ret = ccdc_disable(ccdc);
+ if (ccdc->output & CCDC_OUTPUT_MEMORY)
+ omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_CCDC_WRITE);
+ omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_CCDC);
+ ccdc->underrun = 0;
+ break;
+ }
+
+ ccdc->state = enable;
+ return ret;
+}
+
+static struct v4l2_mbus_framefmt *
+__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(fh, pad);
+ else
+ return &ccdc->formats[pad];
+}
+
+/*
+ * ccdc_try_format - Try video format on a pad
+ * @ccdc: ISP CCDC device
+ * @fh : V4L2 subdev file handle
+ * @pad: Pad number
+ * @fmt: Format
+ */
+static void
+ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
+ unsigned int pad, struct v4l2_mbus_framefmt *fmt,
+ enum v4l2_subdev_format_whence which)
+{
+ struct v4l2_mbus_framefmt *format;
+ const struct isp_format_info *info;
+ unsigned int width = fmt->width;
+ unsigned int height = fmt->height;
+ unsigned int i;
+
+ switch (pad) {
+ case CCDC_PAD_SINK:
+ /* TODO: If the CCDC output formatter pad is connected directly
+ * to the resizer, only YUV formats can be used.
+ */
+ for (i = 0; i < ARRAY_SIZE(ccdc_fmts); i++) {
+ if (fmt->code == ccdc_fmts[i])
+ break;
+ }
+
+ /* If not found, use SGRBG10 as default */
+ if (i >= ARRAY_SIZE(ccdc_fmts))
+ fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+
+ /* Clamp the input size. */
+ fmt->width = clamp_t(u32, width, 32, 4096);
+ fmt->height = clamp_t(u32, height, 32, 4096);
+ break;
+
+ case CCDC_PAD_SOURCE_OF:
+ format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which);
+ memcpy(fmt, format, sizeof(*fmt));
+
+ /* The data formatter truncates the number of horizontal output
+ * pixels to a multiple of 16. To avoid clipping data, allow
+ * callers to request an output size bigger than the input size
+ * up to the nearest multiple of 16.
+ */
+ fmt->width = clamp_t(u32, width, 32, fmt->width + 15);
+ fmt->width &= ~15;
+ fmt->height = clamp_t(u32, height, 32, fmt->height);
+ break;
+
+ case CCDC_PAD_SOURCE_VP:
+ format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which);
+ memcpy(fmt, format, sizeof(*fmt));
+
+ /* The video port interface truncates the data to 10 bits. */
+ info = omap3isp_video_format_info(fmt->code);
+ fmt->code = info->truncated;
+
+ /* The number of lines that can be clocked out from the video
+ * port output must be at least one line less than the number
+ * of input lines.
+ */
+ fmt->width = clamp_t(u32, width, 32, fmt->width);
+ fmt->height = clamp_t(u32, height, 32, fmt->height - 1);
+ break;
+ }
+
+ /* Data is written to memory unpacked, each 10-bit or 12-bit pixel is
+ * stored on 2 bytes.
+ */
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ fmt->field = V4L2_FIELD_NONE;
+}
+
+/*
+ * ccdc_enum_mbus_code - Handle pixel format enumeration
+ * @sd : pointer to v4l2 subdev structure
+ * @fh : V4L2 subdev file handle
+ * @code : pointer to v4l2_subdev_mbus_code_enum structure
+ * return -EINVAL or zero on success
+ */
+static int ccdc_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ switch (code->pad) {
+ case CCDC_PAD_SINK:
+ if (code->index >= ARRAY_SIZE(ccdc_fmts))
+ return -EINVAL;
+
+ code->code = ccdc_fmts[code->index];
+ break;
+
+ case CCDC_PAD_SOURCE_OF:
+ case CCDC_PAD_SOURCE_VP:
+ /* No format conversion inside CCDC */
+ if (code->index != 0)
+ return -EINVAL;
+
+ format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK,
+ V4L2_SUBDEV_FORMAT_TRY);
+
+ code->code = format->code;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ccdc_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt format;
+
+ if (fse->index != 0)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = 1;
+ format.height = 1;
+ ccdc_try_format(ccdc, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+ fse->min_width = format.width;
+ fse->min_height = format.height;
+
+ if (format.code != fse->code)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = -1;
+ format.height = -1;
+ ccdc_try_format(ccdc, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+ fse->max_width = format.width;
+ fse->max_height = format.height;
+
+ return 0;
+}
+
+/*
+ * ccdc_get_format - Retrieve the video format on a pad
+ * @sd : ISP CCDC V4L2 subdevice
+ * @fh : V4L2 subdev file handle
+ * @fmt: Format
+ *
+ * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
+ * to the format type.
+ */
+static int ccdc_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ fmt->format = *format;
+ return 0;
+}
+
+/*
+ * ccdc_set_format - Set the video format on a pad
+ * @sd : ISP CCDC V4L2 subdevice
+ * @fh : V4L2 subdev file handle
+ * @fmt: Format
+ *
+ * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
+ * to the format type.
+ */
+static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ ccdc_try_format(ccdc, fh, fmt->pad, &fmt->format, fmt->which);
+ *format = fmt->format;
+
+ /* Propagate the format from sink to source */
+ if (fmt->pad == CCDC_PAD_SINK) {
+ format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF,
+ fmt->which);
+ *format = fmt->format;
+ ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_OF, format,
+ fmt->which);
+
+ format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_VP,
+ fmt->which);
+ *format = fmt->format;
+ ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_VP, format,
+ fmt->which);
+ }
+
+ return 0;
+}
+
+/*
+ * ccdc_init_formats - Initialize formats on all pads
+ * @sd: ISP CCDC V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ *
+ * Initialize all pad formats with default values. If fh is not NULL, try
+ * formats are initialized on the file handle. Otherwise active formats are
+ * initialized on the device.
+ */
+static int ccdc_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_subdev_format format;
+
+ memset(&format, 0, sizeof(format));
+ format.pad = CCDC_PAD_SINK;
+ format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
+ format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ format.format.width = 4096;
+ format.format.height = 4096;
+ ccdc_set_format(sd, fh, &format);
+
+ return 0;
+}
+
+/* V4L2 subdev core operations */
+static const struct v4l2_subdev_core_ops ccdc_v4l2_core_ops = {
+ .ioctl = ccdc_ioctl,
+ .subscribe_event = ccdc_subscribe_event,
+ .unsubscribe_event = ccdc_unsubscribe_event,
+};
+
+/* V4L2 subdev video operations */
+static const struct v4l2_subdev_video_ops ccdc_v4l2_video_ops = {
+ .s_stream = ccdc_set_stream,
+};
+
+/* V4L2 subdev pad operations */
+static const struct v4l2_subdev_pad_ops ccdc_v4l2_pad_ops = {
+ .enum_mbus_code = ccdc_enum_mbus_code,
+ .enum_frame_size = ccdc_enum_frame_size,
+ .get_fmt = ccdc_get_format,
+ .set_fmt = ccdc_set_format,
+};
+
+/* V4L2 subdev operations */
+static const struct v4l2_subdev_ops ccdc_v4l2_ops = {
+ .core = &ccdc_v4l2_core_ops,
+ .video = &ccdc_v4l2_video_ops,
+ .pad = &ccdc_v4l2_pad_ops,
+};
+
+/* V4L2 subdev internal operations */
+static const struct v4l2_subdev_internal_ops ccdc_v4l2_internal_ops = {
+ .open = ccdc_init_formats,
+};
+
+/* -----------------------------------------------------------------------------
+ * Media entity operations
+ */
+
+/*
+ * ccdc_link_setup - Setup CCDC connections
+ * @entity: CCDC media entity
+ * @local: Pad at the local end of the link
+ * @remote: Pad at the remote end of the link
+ * @flags: Link flags
+ *
+ * return -EINVAL or zero on success
+ */
+static int ccdc_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
+ struct isp_device *isp = to_isp_device(ccdc);
+
+ switch (local->index | media_entity_type(remote->entity)) {
+ case CCDC_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ /* Read from the sensor (parallel interface), CCP2, CSI2a or
+ * CSI2c.
+ */
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ ccdc->input = CCDC_INPUT_NONE;
+ break;
+ }
+
+ if (ccdc->input != CCDC_INPUT_NONE)
+ return -EBUSY;
+
+ if (remote->entity == &isp->isp_ccp2.subdev.entity)
+ ccdc->input = CCDC_INPUT_CCP2B;
+ else if (remote->entity == &isp->isp_csi2a.subdev.entity)
+ ccdc->input = CCDC_INPUT_CSI2A;
+ else if (remote->entity == &isp->isp_csi2c.subdev.entity)
+ ccdc->input = CCDC_INPUT_CSI2C;
+ else
+ ccdc->input = CCDC_INPUT_PARALLEL;
+
+ break;
+
+ /*
+ * The ISP core doesn't support pipelines with multiple video outputs.
+ * Revisit this when it will be implemented, and return -EBUSY for now.
+ */
+
+ case CCDC_PAD_SOURCE_VP | MEDIA_ENT_T_V4L2_SUBDEV:
+ /* Write to preview engine, histogram and H3A. When none of
+ * those links are active, the video port can be disabled.
+ */
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (ccdc->output & ~CCDC_OUTPUT_PREVIEW)
+ return -EBUSY;
+ ccdc->output |= CCDC_OUTPUT_PREVIEW;
+ } else {
+ ccdc->output &= ~CCDC_OUTPUT_PREVIEW;
+ }
+ break;
+
+ case CCDC_PAD_SOURCE_OF | MEDIA_ENT_T_DEVNODE:
+ /* Write to memory */
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (ccdc->output & ~CCDC_OUTPUT_MEMORY)
+ return -EBUSY;
+ ccdc->output |= CCDC_OUTPUT_MEMORY;
+ } else {
+ ccdc->output &= ~CCDC_OUTPUT_MEMORY;
+ }
+ break;
+
+ case CCDC_PAD_SOURCE_OF | MEDIA_ENT_T_V4L2_SUBDEV:
+ /* Write to resizer */
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (ccdc->output & ~CCDC_OUTPUT_RESIZER)
+ return -EBUSY;
+ ccdc->output |= CCDC_OUTPUT_RESIZER;
+ } else {
+ ccdc->output &= ~CCDC_OUTPUT_RESIZER;
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* media operations */
+static const struct media_entity_operations ccdc_media_ops = {
+ .link_setup = ccdc_link_setup,
+};
+
+void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc)
+{
+ v4l2_device_unregister_subdev(&ccdc->subdev);
+ omap3isp_video_unregister(&ccdc->video_out);
+}
+
+int omap3isp_ccdc_register_entities(struct isp_ccdc_device *ccdc,
+ struct v4l2_device *vdev)
+{
+ int ret;
+
+ /* Register the subdev and video node. */
+ ret = v4l2_device_register_subdev(vdev, &ccdc->subdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap3isp_video_register(&ccdc->video_out, vdev);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ omap3isp_ccdc_unregister_entities(ccdc);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP CCDC initialisation and cleanup
+ */
+
+/*
+ * ccdc_init_entities - Initialize V4L2 subdev and media entity
+ * @ccdc: ISP CCDC module
+ *
+ * Return 0 on success and a negative error code on failure.
+ */
+static int ccdc_init_entities(struct isp_ccdc_device *ccdc)
+{
+ struct v4l2_subdev *sd = &ccdc->subdev;
+ struct media_pad *pads = ccdc->pads;
+ struct media_entity *me = &sd->entity;
+ int ret;
+
+ ccdc->input = CCDC_INPUT_NONE;
+
+ v4l2_subdev_init(sd, &ccdc_v4l2_ops);
+ sd->internal_ops = &ccdc_v4l2_internal_ops;
+ strlcpy(sd->name, "OMAP3 ISP CCDC", sizeof(sd->name));
+ sd->grp_id = 1 << 16; /* group ID for isp subdevs */
+ v4l2_set_subdevdata(sd, ccdc);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ pads[CCDC_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[CCDC_PAD_SOURCE_VP].flags = MEDIA_PAD_FL_SOURCE;
+ pads[CCDC_PAD_SOURCE_OF].flags = MEDIA_PAD_FL_SOURCE;
+
+ me->ops = &ccdc_media_ops;
+ ret = media_entity_init(me, CCDC_PADS_NUM, pads, 0);
+ if (ret < 0)
+ return ret;
+
+ ccdc_init_formats(sd, NULL);
+
+ ccdc->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ ccdc->video_out.ops = &ccdc_video_ops;
+ ccdc->video_out.isp = to_isp_device(ccdc);
+ ccdc->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3;
+ ccdc->video_out.bpl_alignment = 32;
+
+ ret = omap3isp_video_init(&ccdc->video_out, "CCDC");
+ if (ret < 0)
+ goto error_video;
+
+ /* Connect the CCDC subdev to the video node. */
+ ret = media_entity_create_link(&ccdc->subdev.entity, CCDC_PAD_SOURCE_OF,
+ &ccdc->video_out.video.entity, 0, 0);
+ if (ret < 0)
+ goto error_link;
+
+ return 0;
+
+error_link:
+ omap3isp_video_cleanup(&ccdc->video_out);
+error_video:
+ media_entity_cleanup(me);
+ return ret;
+}
+
+/*
+ * omap3isp_ccdc_init - CCDC module initialization.
+ * @dev: Device pointer specific to the OMAP3 ISP.
+ *
+ * TODO: Get the initialisation values from platform data.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+int omap3isp_ccdc_init(struct isp_device *isp)
+{
+ struct isp_ccdc_device *ccdc = &isp->isp_ccdc;
+ int ret;
+
+ spin_lock_init(&ccdc->lock);
+ init_waitqueue_head(&ccdc->wait);
+ mutex_init(&ccdc->ioctl_lock);
+
+ ccdc->stopping = CCDC_STOP_NOT_REQUESTED;
+
+ INIT_WORK(&ccdc->lsc.table_work, ccdc_lsc_free_table_work);
+ ccdc->lsc.state = LSC_STATE_STOPPED;
+ INIT_LIST_HEAD(&ccdc->lsc.free_queue);
+ spin_lock_init(&ccdc->lsc.req_lock);
+
+ ccdc->syncif.ccdc_mastermode = 0;
+ ccdc->syncif.datapol = 0;
+ ccdc->syncif.datsz = 0;
+ ccdc->syncif.fldmode = 0;
+ ccdc->syncif.fldout = 0;
+ ccdc->syncif.fldpol = 0;
+ ccdc->syncif.fldstat = 0;
+
+ ccdc->clamp.oblen = 0;
+ ccdc->clamp.dcsubval = 0;
+
+ ccdc->vpcfg.pixelclk = 0;
+
+ ccdc->update = OMAP3ISP_CCDC_BLCLAMP;
+ ccdc_apply_controls(ccdc);
+
+ ret = ccdc_init_entities(ccdc);
+ if (ret < 0) {
+ mutex_destroy(&ccdc->ioctl_lock);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * omap3isp_ccdc_cleanup - CCDC module cleanup.
+ * @dev: Device pointer specific to the OMAP3 ISP.
+ */
+void omap3isp_ccdc_cleanup(struct isp_device *isp)
+{
+ struct isp_ccdc_device *ccdc = &isp->isp_ccdc;
+
+ omap3isp_video_cleanup(&ccdc->video_out);
+ media_entity_cleanup(&ccdc->subdev.entity);
+
+ /* Free LSC requests. As the CCDC is stopped there's no active request,
+ * so only the pending request and the free queue need to be handled.
+ */
+ ccdc_lsc_free_request(ccdc, ccdc->lsc.request);
+ cancel_work_sync(&ccdc->lsc.table_work);
+ ccdc_lsc_free_queue(ccdc, &ccdc->lsc.free_queue);
+
+ if (ccdc->fpc.fpcaddr != 0)
+ omap_iommu_vfree(isp->domain, isp->dev, ccdc->fpc.fpcaddr);
+
+ mutex_destroy(&ccdc->ioctl_lock);
+}
diff --git a/drivers/media/video/omap3isp/ispccdc.h b/drivers/media/video/omap3isp/ispccdc.h
new file mode 100644
index 00000000..6d0264ba
--- /dev/null
+++ b/drivers/media/video/omap3isp/ispccdc.h
@@ -0,0 +1,217 @@
+/*
+ * ispccdc.h
+ *
+ * TI OMAP3 ISP - CCDC module
+ *
+ * Copyright (C) 2009-2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP3_ISP_CCDC_H
+#define OMAP3_ISP_CCDC_H
+
+#include <linux/omap3isp.h>
+#include <linux/workqueue.h>
+
+#include "ispvideo.h"
+
+enum ccdc_input_entity {
+ CCDC_INPUT_NONE,
+ CCDC_INPUT_PARALLEL,
+ CCDC_INPUT_CSI2A,
+ CCDC_INPUT_CCP2B,
+ CCDC_INPUT_CSI2C
+};
+
+#define CCDC_OUTPUT_MEMORY (1 << 0)
+#define CCDC_OUTPUT_PREVIEW (1 << 1)
+#define CCDC_OUTPUT_RESIZER (1 << 2)
+
+#define OMAP3ISP_CCDC_NEVENTS 16
+
+/*
+ * struct ispccdc_syncif - Structure for Sync Interface between sensor and CCDC
+ * @ccdc_mastermode: Master mode. 1 - Master, 0 - Slave.
+ * @fldstat: Field state. 0 - Odd Field, 1 - Even Field.
+ * @datsz: Data size.
+ * @fldmode: 0 - Progressive, 1 - Interlaced.
+ * @datapol: 0 - Positive, 1 - Negative.
+ * @fldpol: 0 - Positive, 1 - Negative.
+ * @hdpol: 0 - Positive, 1 - Negative.
+ * @vdpol: 0 - Positive, 1 - Negative.
+ * @fldout: 0 - Input, 1 - Output.
+ * @hs_width: Width of the Horizontal Sync pulse, used for HS/VS Output.
+ * @vs_width: Width of the Vertical Sync pulse, used for HS/VS Output.
+ * @ppln: Number of pixels per line, used for HS/VS Output.
+ * @hlprf: Number of half lines per frame, used for HS/VS Output.
+ * @bt_r656_en: 1 - Enable ITU-R BT656 mode, 0 - Sync mode.
+ */
+struct ispccdc_syncif {
+ u8 ccdc_mastermode;
+ u8 fldstat;
+ u8 datsz;
+ u8 fldmode;
+ u8 datapol;
+ u8 fldpol;
+ u8 hdpol;
+ u8 vdpol;
+ u8 fldout;
+ u8 hs_width;
+ u8 vs_width;
+ u8 ppln;
+ u8 hlprf;
+ u8 bt_r656_en;
+};
+
+/*
+ * struct ispccdc_vp - Structure for Video Port parameters
+ * @pixelclk: Input pixel clock in Hz
+ */
+struct ispccdc_vp {
+ unsigned int pixelclk;
+};
+
+enum ispccdc_lsc_state {
+ LSC_STATE_STOPPED = 0,
+ LSC_STATE_STOPPING = 1,
+ LSC_STATE_RUNNING = 2,
+ LSC_STATE_RECONFIG = 3,
+};
+
+struct ispccdc_lsc_config_req {
+ struct list_head list;
+ struct omap3isp_ccdc_lsc_config config;
+ unsigned char enable;
+ u32 table;
+ struct iovm_struct *iovm;
+};
+
+/*
+ * ispccdc_lsc - CCDC LSC parameters
+ * @update_config: Set when user changes config
+ * @request_enable: Whether LSC is requested to be enabled
+ * @config: LSC config set by user
+ * @update_table: Set when user provides a new LSC table to table_new
+ * @table_new: LSC table set by user, ISP address
+ * @table_inuse: LSC table currently in use, ISP address
+ */
+struct ispccdc_lsc {
+ enum ispccdc_lsc_state state;
+ struct work_struct table_work;
+
+ /* LSC queue of configurations */
+ spinlock_t req_lock;
+ struct ispccdc_lsc_config_req *request; /* requested configuration */
+ struct ispccdc_lsc_config_req *active; /* active configuration */
+ struct list_head free_queue; /* configurations for freeing */
+};
+
+#define CCDC_STOP_NOT_REQUESTED 0x00
+#define CCDC_STOP_REQUEST 0x01
+#define CCDC_STOP_EXECUTED (0x02 | CCDC_STOP_REQUEST)
+#define CCDC_STOP_CCDC_FINISHED 0x04
+#define CCDC_STOP_LSC_FINISHED 0x08
+#define CCDC_STOP_FINISHED \
+ (CCDC_STOP_EXECUTED | CCDC_STOP_CCDC_FINISHED | CCDC_STOP_LSC_FINISHED)
+
+#define CCDC_EVENT_VD1 0x10
+#define CCDC_EVENT_VD0 0x20
+#define CCDC_EVENT_LSC_DONE 0x40
+
+/* Sink and source CCDC pads */
+#define CCDC_PAD_SINK 0
+#define CCDC_PAD_SOURCE_OF 1
+#define CCDC_PAD_SOURCE_VP 2
+#define CCDC_PADS_NUM 3
+
+/*
+ * struct isp_ccdc_device - Structure for the CCDC module to store its own
+ * information
+ * @subdev: V4L2 subdevice
+ * @pads: Sink and source media entity pads
+ * @formats: Active video formats
+ * @input: Active input
+ * @output: Active outputs
+ * @video_out: Output video node
+ * @alaw: A-law compression enabled (1) or disabled (0)
+ * @lpf: Low pass filter enabled (1) or disabled (0)
+ * @obclamp: Optical-black clamp enabled (1) or disabled (0)
+ * @fpc_en: Faulty pixels correction enabled (1) or disabled (0)
+ * @blcomp: Black level compensation configuration
+ * @clamp: Optical-black or digital clamp configuration
+ * @fpc: Faulty pixels correction configuration
+ * @lsc: Lens shading compensation configuration
+ * @update: Bitmask of controls to update during the next interrupt
+ * @shadow_update: Controls update in progress by userspace
+ * @syncif: Interface synchronization configuration
+ * @vpcfg: Video port configuration
+ * @underrun: A buffer underrun occurred and a new buffer has been queued
+ * @state: Streaming state
+ * @lock: Serializes shadow_update with interrupt handler
+ * @wait: Wait queue used to stop the module
+ * @stopping: Stopping state
+ * @ioctl_lock: Serializes ioctl calls and LSC requests freeing
+ */
+struct isp_ccdc_device {
+ struct v4l2_subdev subdev;
+ struct media_pad pads[CCDC_PADS_NUM];
+ struct v4l2_mbus_framefmt formats[CCDC_PADS_NUM];
+
+ enum ccdc_input_entity input;
+ unsigned int output;
+ struct isp_video video_out;
+
+ unsigned int alaw:1,
+ lpf:1,
+ obclamp:1,
+ fpc_en:1;
+ struct omap3isp_ccdc_blcomp blcomp;
+ struct omap3isp_ccdc_bclamp clamp;
+ struct omap3isp_ccdc_fpc fpc;
+ struct ispccdc_lsc lsc;
+ unsigned int update;
+ unsigned int shadow_update;
+
+ struct ispccdc_syncif syncif;
+ struct ispccdc_vp vpcfg;
+
+ unsigned int underrun:1;
+ enum isp_pipeline_stream_state state;
+ spinlock_t lock;
+ wait_queue_head_t wait;
+ unsigned int stopping;
+ struct mutex ioctl_lock;
+};
+
+struct isp_device;
+
+int omap3isp_ccdc_init(struct isp_device *isp);
+void omap3isp_ccdc_cleanup(struct isp_device *isp);
+int omap3isp_ccdc_register_entities(struct isp_ccdc_device *ccdc,
+ struct v4l2_device *vdev);
+void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc);
+
+int omap3isp_ccdc_busy(struct isp_ccdc_device *isp_ccdc);
+int omap3isp_ccdc_isr(struct isp_ccdc_device *isp_ccdc, u32 events);
+void omap3isp_ccdc_restore_context(struct isp_device *isp);
+void omap3isp_ccdc_max_rate(struct isp_ccdc_device *ccdc,
+ unsigned int *max_rate);
+
+#endif /* OMAP3_ISP_CCDC_H */
diff --git a/drivers/media/video/omap3isp/ispccp2.c b/drivers/media/video/omap3isp/ispccp2.c
new file mode 100644
index 00000000..70ddbf35
--- /dev/null
+++ b/drivers/media/video/omap3isp/ispccp2.c
@@ -0,0 +1,1193 @@
+/*
+ * ispccp2.c
+ *
+ * TI OMAP3 ISP - CCP2 module
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 Texas Instruments, Inc.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+#include <linux/regulator/consumer.h>
+
+#include "isp.h"
+#include "ispreg.h"
+#include "ispccp2.h"
+
+/* Number of LCX channels */
+#define CCP2_LCx_CHANS_NUM 3
+/* Max/Min size for CCP2 video port */
+#define ISPCCP2_DAT_START_MIN 0
+#define ISPCCP2_DAT_START_MAX 4095
+#define ISPCCP2_DAT_SIZE_MIN 0
+#define ISPCCP2_DAT_SIZE_MAX 4095
+#define ISPCCP2_VPCLK_FRACDIV 65536
+#define ISPCCP2_LCx_CTRL_FORMAT_RAW8_DPCM10_VP 0x12
+#define ISPCCP2_LCx_CTRL_FORMAT_RAW10_VP 0x16
+/* Max/Min size for CCP2 memory channel */
+#define ISPCCP2_LCM_HSIZE_COUNT_MIN 16
+#define ISPCCP2_LCM_HSIZE_COUNT_MAX 8191
+#define ISPCCP2_LCM_HSIZE_SKIP_MIN 0
+#define ISPCCP2_LCM_HSIZE_SKIP_MAX 8191
+#define ISPCCP2_LCM_VSIZE_MIN 1
+#define ISPCCP2_LCM_VSIZE_MAX 8191
+#define ISPCCP2_LCM_HWORDS_MIN 1
+#define ISPCCP2_LCM_HWORDS_MAX 4095
+#define ISPCCP2_LCM_CTRL_BURST_SIZE_32X 5
+#define ISPCCP2_LCM_CTRL_READ_THROTTLE_FULL 0
+#define ISPCCP2_LCM_CTRL_SRC_DECOMPR_DPCM10 2
+#define ISPCCP2_LCM_CTRL_SRC_FORMAT_RAW8 2
+#define ISPCCP2_LCM_CTRL_SRC_FORMAT_RAW10 3
+#define ISPCCP2_LCM_CTRL_DST_FORMAT_RAW10 3
+#define ISPCCP2_LCM_CTRL_DST_PORT_VP 0
+#define ISPCCP2_LCM_CTRL_DST_PORT_MEM 1
+
+/* Set only the required bits */
+#define BIT_SET(var, shift, mask, val) \
+ do { \
+ var = ((var) & ~((mask) << (shift))) \
+ | ((val) << (shift)); \
+ } while (0)
+
+/*
+ * ccp2_print_status - Print current CCP2 module register values.
+ */
+#define CCP2_PRINT_REGISTER(isp, name)\
+ dev_dbg(isp->dev, "###CCP2 " #name "=0x%08x\n", \
+ isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_##name))
+
+static void ccp2_print_status(struct isp_ccp2_device *ccp2)
+{
+ struct isp_device *isp = to_isp_device(ccp2);
+
+ dev_dbg(isp->dev, "-------------CCP2 Register dump-------------\n");
+
+ CCP2_PRINT_REGISTER(isp, SYSCONFIG);
+ CCP2_PRINT_REGISTER(isp, SYSSTATUS);
+ CCP2_PRINT_REGISTER(isp, LC01_IRQENABLE);
+ CCP2_PRINT_REGISTER(isp, LC01_IRQSTATUS);
+ CCP2_PRINT_REGISTER(isp, LC23_IRQENABLE);
+ CCP2_PRINT_REGISTER(isp, LC23_IRQSTATUS);
+ CCP2_PRINT_REGISTER(isp, LCM_IRQENABLE);
+ CCP2_PRINT_REGISTER(isp, LCM_IRQSTATUS);
+ CCP2_PRINT_REGISTER(isp, CTRL);
+ CCP2_PRINT_REGISTER(isp, LCx_CTRL(0));
+ CCP2_PRINT_REGISTER(isp, LCx_CODE(0));
+ CCP2_PRINT_REGISTER(isp, LCx_STAT_START(0));
+ CCP2_PRINT_REGISTER(isp, LCx_STAT_SIZE(0));
+ CCP2_PRINT_REGISTER(isp, LCx_SOF_ADDR(0));
+ CCP2_PRINT_REGISTER(isp, LCx_EOF_ADDR(0));
+ CCP2_PRINT_REGISTER(isp, LCx_DAT_START(0));
+ CCP2_PRINT_REGISTER(isp, LCx_DAT_SIZE(0));
+ CCP2_PRINT_REGISTER(isp, LCx_DAT_PING_ADDR(0));
+ CCP2_PRINT_REGISTER(isp, LCx_DAT_PONG_ADDR(0));
+ CCP2_PRINT_REGISTER(isp, LCx_DAT_OFST(0));
+ CCP2_PRINT_REGISTER(isp, LCM_CTRL);
+ CCP2_PRINT_REGISTER(isp, LCM_VSIZE);
+ CCP2_PRINT_REGISTER(isp, LCM_HSIZE);
+ CCP2_PRINT_REGISTER(isp, LCM_PREFETCH);
+ CCP2_PRINT_REGISTER(isp, LCM_SRC_ADDR);
+ CCP2_PRINT_REGISTER(isp, LCM_SRC_OFST);
+ CCP2_PRINT_REGISTER(isp, LCM_DST_ADDR);
+ CCP2_PRINT_REGISTER(isp, LCM_DST_OFST);
+
+ dev_dbg(isp->dev, "--------------------------------------------\n");
+}
+
+/*
+ * ccp2_reset - Reset the CCP2
+ * @ccp2: pointer to ISP CCP2 device
+ */
+static void ccp2_reset(struct isp_ccp2_device *ccp2)
+{
+ struct isp_device *isp = to_isp_device(ccp2);
+ int i = 0;
+
+ /* Reset the CSI1/CCP2B and wait for reset to complete */
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_SYSCONFIG,
+ ISPCCP2_SYSCONFIG_SOFT_RESET);
+ while (!(isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_SYSSTATUS) &
+ ISPCCP2_SYSSTATUS_RESET_DONE)) {
+ udelay(10);
+ if (i++ > 10) { /* try read 10 times */
+ dev_warn(isp->dev,
+ "omap3_isp: timeout waiting for ccp2 reset\n");
+ break;
+ }
+ }
+}
+
+/*
+ * ccp2_pwr_cfg - Configure the power mode settings
+ * @ccp2: pointer to ISP CCP2 device
+ */
+static void ccp2_pwr_cfg(struct isp_ccp2_device *ccp2)
+{
+ struct isp_device *isp = to_isp_device(ccp2);
+
+ isp_reg_writel(isp, ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SMART |
+ ((isp->revision == ISP_REVISION_15_0 && isp->autoidle) ?
+ ISPCCP2_SYSCONFIG_AUTO_IDLE : 0),
+ OMAP3_ISP_IOMEM_CCP2, ISPCCP2_SYSCONFIG);
+}
+
+/*
+ * ccp2_if_enable - Enable CCP2 interface.
+ * @ccp2: pointer to ISP CCP2 device
+ * @enable: enable/disable flag
+ */
+static void ccp2_if_enable(struct isp_ccp2_device *ccp2, u8 enable)
+{
+ struct isp_device *isp = to_isp_device(ccp2);
+ struct isp_pipeline *pipe = to_isp_pipeline(&ccp2->subdev.entity);
+ int i;
+
+ if (enable && ccp2->vdds_csib)
+ regulator_enable(ccp2->vdds_csib);
+
+ /* Enable/Disable all the LCx channels */
+ for (i = 0; i < CCP2_LCx_CHANS_NUM; i++)
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_CTRL(i),
+ ISPCCP2_LCx_CTRL_CHAN_EN,
+ enable ? ISPCCP2_LCx_CTRL_CHAN_EN : 0);
+
+ /* Enable/Disable ccp2 interface in ccp2 mode */
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL,
+ ISPCCP2_CTRL_MODE | ISPCCP2_CTRL_IF_EN,
+ enable ? (ISPCCP2_CTRL_MODE | ISPCCP2_CTRL_IF_EN) : 0);
+
+ /* For frame count propagation */
+ if (pipe->do_propagation) {
+ /* We may want the Frame Start IRQ from LC0 */
+ if (enable)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2,
+ ISPCCP2_LC01_IRQENABLE,
+ ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCP2,
+ ISPCCP2_LC01_IRQENABLE,
+ ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ);
+ }
+
+ if (!enable && ccp2->vdds_csib)
+ regulator_disable(ccp2->vdds_csib);
+}
+
+/*
+ * ccp2_mem_enable - Enable CCP2 memory interface.
+ * @ccp2: pointer to ISP CCP2 device
+ * @enable: enable/disable flag
+ */
+static void ccp2_mem_enable(struct isp_ccp2_device *ccp2, u8 enable)
+{
+ struct isp_device *isp = to_isp_device(ccp2);
+
+ if (enable)
+ ccp2_if_enable(ccp2, 0);
+
+ /* Enable/Disable ccp2 interface in ccp2 mode */
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL,
+ ISPCCP2_CTRL_MODE, enable ? ISPCCP2_CTRL_MODE : 0);
+
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_CTRL,
+ ISPCCP2_LCM_CTRL_CHAN_EN,
+ enable ? ISPCCP2_LCM_CTRL_CHAN_EN : 0);
+}
+
+/*
+ * ccp2_phyif_config - Initialize CCP2 phy interface config
+ * @ccp2: Pointer to ISP CCP2 device
+ * @config: CCP2 platform data
+ *
+ * Configure the CCP2 physical interface module from platform data.
+ *
+ * Returns -EIO if strobe is chosen in CSI1 mode, or 0 on success.
+ */
+static int ccp2_phyif_config(struct isp_ccp2_device *ccp2,
+ const struct isp_ccp2_platform_data *pdata)
+{
+ struct isp_device *isp = to_isp_device(ccp2);
+ u32 val;
+
+ /* CCP2B mode */
+ val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL) |
+ ISPCCP2_CTRL_IO_OUT_SEL | ISPCCP2_CTRL_MODE;
+ /* Data/strobe physical layer */
+ BIT_SET(val, ISPCCP2_CTRL_PHY_SEL_SHIFT, ISPCCP2_CTRL_PHY_SEL_MASK,
+ pdata->phy_layer);
+ BIT_SET(val, ISPCCP2_CTRL_INV_SHIFT, ISPCCP2_CTRL_INV_MASK,
+ pdata->strobe_clk_pol);
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL);
+
+ val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL);
+ if (!(val & ISPCCP2_CTRL_MODE)) {
+ if (pdata->ccp2_mode == ISP_CCP2_MODE_CCP2)
+ dev_warn(isp->dev, "OMAP3 CCP2 bus not available\n");
+ if (pdata->phy_layer == ISP_CCP2_PHY_DATA_STROBE)
+ /* Strobe mode requires CCP2 */
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * ccp2_vp_config - Initialize CCP2 video port interface.
+ * @ccp2: Pointer to ISP CCP2 device
+ * @vpclk_div: Video port divisor
+ *
+ * Configure the CCP2 video port with the given clock divisor. The valid divisor
+ * values depend on the ISP revision:
+ *
+ * - revision 1.0 and 2.0 1 to 4
+ * - revision 15.0 1 to 65536
+ *
+ * The exact divisor value used might differ from the requested value, as ISP
+ * revision 15.0 represent the divisor by 65536 divided by an integer.
+ */
+static void ccp2_vp_config(struct isp_ccp2_device *ccp2,
+ unsigned int vpclk_div)
+{
+ struct isp_device *isp = to_isp_device(ccp2);
+ u32 val;
+
+ /* ISPCCP2_CTRL Video port */
+ val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL);
+ val |= ISPCCP2_CTRL_VP_ONLY_EN; /* Disable the memory write port */
+
+ if (isp->revision == ISP_REVISION_15_0) {
+ vpclk_div = clamp_t(unsigned int, vpclk_div, 1, 65536);
+ vpclk_div = min(ISPCCP2_VPCLK_FRACDIV / vpclk_div, 65535U);
+ BIT_SET(val, ISPCCP2_CTRL_VPCLK_DIV_SHIFT,
+ ISPCCP2_CTRL_VPCLK_DIV_MASK, vpclk_div);
+ } else {
+ vpclk_div = clamp_t(unsigned int, vpclk_div, 1, 4);
+ BIT_SET(val, ISPCCP2_CTRL_VP_OUT_CTRL_SHIFT,
+ ISPCCP2_CTRL_VP_OUT_CTRL_MASK, vpclk_div - 1);
+ }
+
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL);
+}
+
+/*
+ * ccp2_lcx_config - Initialize CCP2 logical channel interface.
+ * @ccp2: Pointer to ISP CCP2 device
+ * @config: Pointer to ISP LCx config structure.
+ *
+ * This will analyze the parameters passed by the interface config
+ * and configure CSI1/CCP2 logical channel
+ *
+ */
+static void ccp2_lcx_config(struct isp_ccp2_device *ccp2,
+ struct isp_interface_lcx_config *config)
+{
+ struct isp_device *isp = to_isp_device(ccp2);
+ u32 val, format;
+
+ switch (config->format) {
+ case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8:
+ format = ISPCCP2_LCx_CTRL_FORMAT_RAW8_DPCM10_VP;
+ break;
+ case V4L2_MBUS_FMT_SGRBG10_1X10:
+ default:
+ format = ISPCCP2_LCx_CTRL_FORMAT_RAW10_VP; /* RAW10+VP */
+ break;
+ }
+ /* ISPCCP2_LCx_CTRL logical channel #0 */
+ val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_CTRL(0))
+ | (ISPCCP2_LCx_CTRL_REGION_EN); /* Region */
+
+ if (isp->revision == ISP_REVISION_15_0) {
+ /* CRC */
+ BIT_SET(val, ISPCCP2_LCx_CTRL_CRC_SHIFT_15_0,
+ ISPCCP2_LCx_CTRL_CRC_MASK,
+ config->crc);
+ /* Format = RAW10+VP or RAW8+DPCM10+VP*/
+ BIT_SET(val, ISPCCP2_LCx_CTRL_FORMAT_SHIFT_15_0,
+ ISPCCP2_LCx_CTRL_FORMAT_MASK_15_0, format);
+ } else {
+ BIT_SET(val, ISPCCP2_LCx_CTRL_CRC_SHIFT,
+ ISPCCP2_LCx_CTRL_CRC_MASK,
+ config->crc);
+
+ BIT_SET(val, ISPCCP2_LCx_CTRL_FORMAT_SHIFT,
+ ISPCCP2_LCx_CTRL_FORMAT_MASK, format);
+ }
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_CTRL(0));
+
+ /* ISPCCP2_DAT_START for logical channel #0 */
+ isp_reg_writel(isp, config->data_start << ISPCCP2_LCx_DAT_SHIFT,
+ OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_DAT_START(0));
+
+ /* ISPCCP2_DAT_SIZE for logical channel #0 */
+ isp_reg_writel(isp, config->data_size << ISPCCP2_LCx_DAT_SHIFT,
+ OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_DAT_SIZE(0));
+
+ /* Enable error IRQs for logical channel #0 */
+ val = ISPCCP2_LC01_IRQSTATUS_LC0_FIFO_OVF_IRQ |
+ ISPCCP2_LC01_IRQSTATUS_LC0_CRC_IRQ |
+ ISPCCP2_LC01_IRQSTATUS_LC0_FSP_IRQ |
+ ISPCCP2_LC01_IRQSTATUS_LC0_FW_IRQ |
+ ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ |
+ ISPCCP2_LC01_IRQSTATUS_LC0_FSC_IRQ |
+ ISPCCP2_LC01_IRQSTATUS_LC0_SSC_IRQ;
+
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LC01_IRQSTATUS);
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LC01_IRQENABLE, val);
+}
+
+/*
+ * ccp2_if_configure - Configure ccp2 with data from sensor
+ * @ccp2: Pointer to ISP CCP2 device
+ *
+ * Return 0 on success or a negative error code
+ */
+static int ccp2_if_configure(struct isp_ccp2_device *ccp2)
+{
+ const struct isp_v4l2_subdevs_group *pdata;
+ struct v4l2_mbus_framefmt *format;
+ struct media_pad *pad;
+ struct v4l2_subdev *sensor;
+ u32 lines = 0;
+ int ret;
+
+ ccp2_pwr_cfg(ccp2);
+
+ pad = media_entity_remote_source(&ccp2->pads[CCP2_PAD_SINK]);
+ sensor = media_entity_to_v4l2_subdev(pad->entity);
+ pdata = sensor->host_priv;
+
+ ret = ccp2_phyif_config(ccp2, &pdata->bus.ccp2);
+ if (ret < 0)
+ return ret;
+
+ ccp2_vp_config(ccp2, pdata->bus.ccp2.vpclk_div + 1);
+
+ v4l2_subdev_call(sensor, sensor, g_skip_top_lines, &lines);
+
+ format = &ccp2->formats[CCP2_PAD_SINK];
+
+ ccp2->if_cfg.data_start = lines;
+ ccp2->if_cfg.crc = pdata->bus.ccp2.crc;
+ ccp2->if_cfg.format = format->code;
+ ccp2->if_cfg.data_size = format->height;
+
+ ccp2_lcx_config(ccp2, &ccp2->if_cfg);
+
+ return 0;
+}
+
+static int ccp2_adjust_bandwidth(struct isp_ccp2_device *ccp2)
+{
+ struct isp_pipeline *pipe = to_isp_pipeline(&ccp2->subdev.entity);
+ struct isp_device *isp = to_isp_device(ccp2);
+ const struct v4l2_mbus_framefmt *ofmt = &ccp2->formats[CCP2_PAD_SOURCE];
+ unsigned long l3_ick = pipe->l3_ick;
+ struct v4l2_fract *timeperframe;
+ unsigned int vpclk_div = 2;
+ unsigned int value;
+ u64 bound;
+ u64 area;
+
+ /* Compute the minimum clock divisor, based on the pipeline maximum
+ * data rate. This is an absolute lower bound if we don't want SBL
+ * overflows, so round the value up.
+ */
+ vpclk_div = max_t(unsigned int, DIV_ROUND_UP(l3_ick, pipe->max_rate),
+ vpclk_div);
+
+ /* Compute the maximum clock divisor, based on the requested frame rate.
+ * This is a soft lower bound to achieve a frame rate equal or higher
+ * than the requested value, so round the value down.
+ */
+ timeperframe = &pipe->max_timeperframe;
+
+ if (timeperframe->numerator) {
+ area = ofmt->width * ofmt->height;
+ bound = div_u64(area * timeperframe->denominator,
+ timeperframe->numerator);
+ value = min_t(u64, bound, l3_ick);
+ vpclk_div = max_t(unsigned int, l3_ick / value, vpclk_div);
+ }
+
+ dev_dbg(isp->dev, "%s: minimum clock divisor = %u\n", __func__,
+ vpclk_div);
+
+ return vpclk_div;
+}
+
+/*
+ * ccp2_mem_configure - Initialize CCP2 memory input/output interface
+ * @ccp2: Pointer to ISP CCP2 device
+ * @config: Pointer to ISP mem interface config structure
+ *
+ * This will analyze the parameters passed by the interface config
+ * structure, and configure the respective registers for proper
+ * CSI1/CCP2 memory input.
+ */
+static void ccp2_mem_configure(struct isp_ccp2_device *ccp2,
+ struct isp_interface_mem_config *config)
+{
+ struct isp_device *isp = to_isp_device(ccp2);
+ u32 sink_pixcode = ccp2->formats[CCP2_PAD_SINK].code;
+ u32 source_pixcode = ccp2->formats[CCP2_PAD_SOURCE].code;
+ unsigned int dpcm_decompress = 0;
+ u32 val, hwords;
+
+ if (sink_pixcode != source_pixcode &&
+ sink_pixcode == V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8)
+ dpcm_decompress = 1;
+
+ ccp2_pwr_cfg(ccp2);
+
+ /* Hsize, Skip */
+ isp_reg_writel(isp, ISPCCP2_LCM_HSIZE_SKIP_MIN |
+ (config->hsize_count << ISPCCP2_LCM_HSIZE_SHIFT),
+ OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_HSIZE);
+
+ /* Vsize, no. of lines */
+ isp_reg_writel(isp, config->vsize_count << ISPCCP2_LCM_VSIZE_SHIFT,
+ OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_VSIZE);
+
+ if (ccp2->video_in.bpl_padding == 0)
+ config->src_ofst = 0;
+ else
+ config->src_ofst = ccp2->video_in.bpl_value;
+
+ isp_reg_writel(isp, config->src_ofst, OMAP3_ISP_IOMEM_CCP2,
+ ISPCCP2_LCM_SRC_OFST);
+
+ /* Source and Destination formats */
+ val = ISPCCP2_LCM_CTRL_DST_FORMAT_RAW10 <<
+ ISPCCP2_LCM_CTRL_DST_FORMAT_SHIFT;
+
+ if (dpcm_decompress) {
+ /* source format is RAW8 */
+ val |= ISPCCP2_LCM_CTRL_SRC_FORMAT_RAW8 <<
+ ISPCCP2_LCM_CTRL_SRC_FORMAT_SHIFT;
+
+ /* RAW8 + DPCM10 - simple predictor */
+ val |= ISPCCP2_LCM_CTRL_SRC_DPCM_PRED;
+
+ /* enable source DPCM decompression */
+ val |= ISPCCP2_LCM_CTRL_SRC_DECOMPR_DPCM10 <<
+ ISPCCP2_LCM_CTRL_SRC_DECOMPR_SHIFT;
+ } else {
+ /* source format is RAW10 */
+ val |= ISPCCP2_LCM_CTRL_SRC_FORMAT_RAW10 <<
+ ISPCCP2_LCM_CTRL_SRC_FORMAT_SHIFT;
+ }
+
+ /* Burst size to 32x64 */
+ val |= ISPCCP2_LCM_CTRL_BURST_SIZE_32X <<
+ ISPCCP2_LCM_CTRL_BURST_SIZE_SHIFT;
+
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_CTRL);
+
+ /* Prefetch setup */
+ if (dpcm_decompress)
+ hwords = (ISPCCP2_LCM_HSIZE_SKIP_MIN +
+ config->hsize_count) >> 3;
+ else
+ hwords = (ISPCCP2_LCM_HSIZE_SKIP_MIN +
+ config->hsize_count) >> 2;
+
+ isp_reg_writel(isp, hwords << ISPCCP2_LCM_PREFETCH_SHIFT,
+ OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_PREFETCH);
+
+ /* Video port */
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL,
+ ISPCCP2_CTRL_IO_OUT_SEL | ISPCCP2_CTRL_MODE);
+ ccp2_vp_config(ccp2, ccp2_adjust_bandwidth(ccp2));
+
+ /* Clear LCM interrupts */
+ isp_reg_writel(isp, ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ |
+ ISPCCP2_LCM_IRQSTATUS_EOF_IRQ,
+ OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_IRQSTATUS);
+
+ /* Enable LCM interupts */
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_IRQENABLE,
+ ISPCCP2_LCM_IRQSTATUS_EOF_IRQ |
+ ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ);
+}
+
+/*
+ * ccp2_set_inaddr - Sets memory address of input frame.
+ * @ccp2: Pointer to ISP CCP2 device
+ * @addr: 32bit memory address aligned on 32byte boundary.
+ *
+ * Configures the memory address from which the input frame is to be read.
+ */
+static void ccp2_set_inaddr(struct isp_ccp2_device *ccp2, u32 addr)
+{
+ struct isp_device *isp = to_isp_device(ccp2);
+
+ isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_SRC_ADDR);
+}
+
+/* -----------------------------------------------------------------------------
+ * Interrupt handling
+ */
+
+static void ccp2_isr_buffer(struct isp_ccp2_device *ccp2)
+{
+ struct isp_pipeline *pipe = to_isp_pipeline(&ccp2->subdev.entity);
+ struct isp_buffer *buffer;
+
+ buffer = omap3isp_video_buffer_next(&ccp2->video_in);
+ if (buffer != NULL)
+ ccp2_set_inaddr(ccp2, buffer->isp_addr);
+
+ pipe->state |= ISP_PIPELINE_IDLE_INPUT;
+
+ if (ccp2->state == ISP_PIPELINE_STREAM_SINGLESHOT) {
+ if (isp_pipeline_ready(pipe))
+ omap3isp_pipeline_set_stream(pipe,
+ ISP_PIPELINE_STREAM_SINGLESHOT);
+ }
+}
+
+/*
+ * omap3isp_ccp2_isr - Handle ISP CCP2 interrupts
+ * @ccp2: Pointer to ISP CCP2 device
+ *
+ * This will handle the CCP2 interrupts
+ */
+void omap3isp_ccp2_isr(struct isp_ccp2_device *ccp2)
+{
+ struct isp_pipeline *pipe = to_isp_pipeline(&ccp2->subdev.entity);
+ struct isp_device *isp = to_isp_device(ccp2);
+ static const u32 ISPCCP2_LC01_ERROR =
+ ISPCCP2_LC01_IRQSTATUS_LC0_FIFO_OVF_IRQ |
+ ISPCCP2_LC01_IRQSTATUS_LC0_CRC_IRQ |
+ ISPCCP2_LC01_IRQSTATUS_LC0_FSP_IRQ |
+ ISPCCP2_LC01_IRQSTATUS_LC0_FW_IRQ |
+ ISPCCP2_LC01_IRQSTATUS_LC0_FSC_IRQ |
+ ISPCCP2_LC01_IRQSTATUS_LC0_SSC_IRQ;
+ u32 lcx_irqstatus, lcm_irqstatus;
+
+ /* First clear the interrupts */
+ lcx_irqstatus = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2,
+ ISPCCP2_LC01_IRQSTATUS);
+ isp_reg_writel(isp, lcx_irqstatus, OMAP3_ISP_IOMEM_CCP2,
+ ISPCCP2_LC01_IRQSTATUS);
+
+ lcm_irqstatus = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2,
+ ISPCCP2_LCM_IRQSTATUS);
+ isp_reg_writel(isp, lcm_irqstatus, OMAP3_ISP_IOMEM_CCP2,
+ ISPCCP2_LCM_IRQSTATUS);
+ /* Errors */
+ if (lcx_irqstatus & ISPCCP2_LC01_ERROR) {
+ pipe->error = true;
+ dev_dbg(isp->dev, "CCP2 err:%x\n", lcx_irqstatus);
+ return;
+ }
+
+ if (lcm_irqstatus & ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ) {
+ pipe->error = true;
+ dev_dbg(isp->dev, "CCP2 OCP err:%x\n", lcm_irqstatus);
+ }
+
+ if (omap3isp_module_sync_is_stopping(&ccp2->wait, &ccp2->stopping))
+ return;
+
+ /* Frame number propagation */
+ if (lcx_irqstatus & ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ) {
+ struct isp_pipeline *pipe =
+ to_isp_pipeline(&ccp2->subdev.entity);
+ if (pipe->do_propagation)
+ atomic_inc(&pipe->frame_number);
+ }
+
+ /* Handle queued buffers on frame end interrupts */
+ if (lcm_irqstatus & ISPCCP2_LCM_IRQSTATUS_EOF_IRQ)
+ ccp2_isr_buffer(ccp2);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+static const unsigned int ccp2_fmts[] = {
+ V4L2_MBUS_FMT_SGRBG10_1X10,
+ V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
+};
+
+/*
+ * __ccp2_get_format - helper function for getting ccp2 format
+ * @ccp2 : Pointer to ISP CCP2 device
+ * @fh : V4L2 subdev file handle
+ * @pad : pad number
+ * @which : wanted subdev format
+ * return format structure or NULL on error
+ */
+static struct v4l2_mbus_framefmt *
+__ccp2_get_format(struct isp_ccp2_device *ccp2, struct v4l2_subdev_fh *fh,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(fh, pad);
+ else
+ return &ccp2->formats[pad];
+}
+
+/*
+ * ccp2_try_format - Handle try format by pad subdev method
+ * @ccp2 : Pointer to ISP CCP2 device
+ * @fh : V4L2 subdev file handle
+ * @pad : pad num
+ * @fmt : pointer to v4l2 mbus format structure
+ * @which : wanted subdev format
+ */
+static void ccp2_try_format(struct isp_ccp2_device *ccp2,
+ struct v4l2_subdev_fh *fh, unsigned int pad,
+ struct v4l2_mbus_framefmt *fmt,
+ enum v4l2_subdev_format_whence which)
+{
+ struct v4l2_mbus_framefmt *format;
+
+ switch (pad) {
+ case CCP2_PAD_SINK:
+ if (fmt->code != V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8)
+ fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+
+ if (ccp2->input == CCP2_INPUT_SENSOR) {
+ fmt->width = clamp_t(u32, fmt->width,
+ ISPCCP2_DAT_START_MIN,
+ ISPCCP2_DAT_START_MAX);
+ fmt->height = clamp_t(u32, fmt->height,
+ ISPCCP2_DAT_SIZE_MIN,
+ ISPCCP2_DAT_SIZE_MAX);
+ } else if (ccp2->input == CCP2_INPUT_MEMORY) {
+ fmt->width = clamp_t(u32, fmt->width,
+ ISPCCP2_LCM_HSIZE_COUNT_MIN,
+ ISPCCP2_LCM_HSIZE_COUNT_MAX);
+ fmt->height = clamp_t(u32, fmt->height,
+ ISPCCP2_LCM_VSIZE_MIN,
+ ISPCCP2_LCM_VSIZE_MAX);
+ }
+ break;
+
+ case CCP2_PAD_SOURCE:
+ /* Source format - copy sink format and change pixel code
+ * to SGRBG10_1X10 as we don't support CCP2 write to memory.
+ * When CCP2 write to memory feature will be added this
+ * should be changed properly.
+ */
+ format = __ccp2_get_format(ccp2, fh, CCP2_PAD_SINK, which);
+ memcpy(fmt, format, sizeof(*fmt));
+ fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ break;
+ }
+
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+}
+
+/*
+ * ccp2_enum_mbus_code - Handle pixel format enumeration
+ * @sd : pointer to v4l2 subdev structure
+ * @fh : V4L2 subdev file handle
+ * @code : pointer to v4l2_subdev_mbus_code_enum structure
+ * return -EINVAL or zero on success
+ */
+static int ccp2_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ if (code->pad == CCP2_PAD_SINK) {
+ if (code->index >= ARRAY_SIZE(ccp2_fmts))
+ return -EINVAL;
+
+ code->code = ccp2_fmts[code->index];
+ } else {
+ if (code->index != 0)
+ return -EINVAL;
+
+ format = __ccp2_get_format(ccp2, fh, CCP2_PAD_SINK,
+ V4L2_SUBDEV_FORMAT_TRY);
+ code->code = format->code;
+ }
+
+ return 0;
+}
+
+static int ccp2_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt format;
+
+ if (fse->index != 0)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = 1;
+ format.height = 1;
+ ccp2_try_format(ccp2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+ fse->min_width = format.width;
+ fse->min_height = format.height;
+
+ if (format.code != fse->code)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = -1;
+ format.height = -1;
+ ccp2_try_format(ccp2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+ fse->max_width = format.width;
+ fse->max_height = format.height;
+
+ return 0;
+}
+
+/*
+ * ccp2_get_format - Handle get format by pads subdev method
+ * @sd : pointer to v4l2 subdev structure
+ * @fh : V4L2 subdev file handle
+ * @fmt : pointer to v4l2 subdev format structure
+ * return -EINVAL or zero on success
+ */
+static int ccp2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __ccp2_get_format(ccp2, fh, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ fmt->format = *format;
+ return 0;
+}
+
+/*
+ * ccp2_set_format - Handle set format by pads subdev method
+ * @sd : pointer to v4l2 subdev structure
+ * @fh : V4L2 subdev file handle
+ * @fmt : pointer to v4l2 subdev format structure
+ * returns zero
+ */
+static int ccp2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __ccp2_get_format(ccp2, fh, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ ccp2_try_format(ccp2, fh, fmt->pad, &fmt->format, fmt->which);
+ *format = fmt->format;
+
+ /* Propagate the format from sink to source */
+ if (fmt->pad == CCP2_PAD_SINK) {
+ format = __ccp2_get_format(ccp2, fh, CCP2_PAD_SOURCE,
+ fmt->which);
+ *format = fmt->format;
+ ccp2_try_format(ccp2, fh, CCP2_PAD_SOURCE, format, fmt->which);
+ }
+
+ return 0;
+}
+
+/*
+ * ccp2_init_formats - Initialize formats on all pads
+ * @sd: ISP CCP2 V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ *
+ * Initialize all pad formats with default values. If fh is not NULL, try
+ * formats are initialized on the file handle. Otherwise active formats are
+ * initialized on the device.
+ */
+static int ccp2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_subdev_format format;
+
+ memset(&format, 0, sizeof(format));
+ format.pad = CCP2_PAD_SINK;
+ format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
+ format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ format.format.width = 4096;
+ format.format.height = 4096;
+ ccp2_set_format(sd, fh, &format);
+
+ return 0;
+}
+
+/*
+ * ccp2_s_stream - Enable/Disable streaming on ccp2 subdev
+ * @sd : pointer to v4l2 subdev structure
+ * @enable: 1 == Enable, 0 == Disable
+ * return zero
+ */
+static int ccp2_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd);
+ struct isp_device *isp = to_isp_device(ccp2);
+ struct device *dev = to_device(ccp2);
+ int ret;
+
+ if (ccp2->state == ISP_PIPELINE_STREAM_STOPPED) {
+ if (enable == ISP_PIPELINE_STREAM_STOPPED)
+ return 0;
+ atomic_set(&ccp2->stopping, 0);
+ }
+
+ switch (enable) {
+ case ISP_PIPELINE_STREAM_CONTINUOUS:
+ if (ccp2->phy) {
+ ret = omap3isp_csiphy_acquire(ccp2->phy);
+ if (ret < 0)
+ return ret;
+ }
+
+ ccp2_if_configure(ccp2);
+ ccp2_print_status(ccp2);
+
+ /* Enable CSI1/CCP2 interface */
+ ccp2_if_enable(ccp2, 1);
+ break;
+
+ case ISP_PIPELINE_STREAM_SINGLESHOT:
+ if (ccp2->state != ISP_PIPELINE_STREAM_SINGLESHOT) {
+ struct v4l2_mbus_framefmt *format;
+
+ format = &ccp2->formats[CCP2_PAD_SINK];
+
+ ccp2->mem_cfg.hsize_count = format->width;
+ ccp2->mem_cfg.vsize_count = format->height;
+ ccp2->mem_cfg.src_ofst = 0;
+
+ ccp2_mem_configure(ccp2, &ccp2->mem_cfg);
+ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CSI1_READ);
+ ccp2_print_status(ccp2);
+ }
+ ccp2_mem_enable(ccp2, 1);
+ break;
+
+ case ISP_PIPELINE_STREAM_STOPPED:
+ if (omap3isp_module_sync_idle(&sd->entity, &ccp2->wait,
+ &ccp2->stopping))
+ dev_dbg(dev, "%s: module stop timeout.\n", sd->name);
+ if (ccp2->input == CCP2_INPUT_MEMORY) {
+ ccp2_mem_enable(ccp2, 0);
+ omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_CSI1_READ);
+ } else if (ccp2->input == CCP2_INPUT_SENSOR) {
+ /* Disable CSI1/CCP2 interface */
+ ccp2_if_enable(ccp2, 0);
+ if (ccp2->phy)
+ omap3isp_csiphy_release(ccp2->phy);
+ }
+ break;
+ }
+
+ ccp2->state = enable;
+ return 0;
+}
+
+/* subdev video operations */
+static const struct v4l2_subdev_video_ops ccp2_sd_video_ops = {
+ .s_stream = ccp2_s_stream,
+};
+
+/* subdev pad operations */
+static const struct v4l2_subdev_pad_ops ccp2_sd_pad_ops = {
+ .enum_mbus_code = ccp2_enum_mbus_code,
+ .enum_frame_size = ccp2_enum_frame_size,
+ .get_fmt = ccp2_get_format,
+ .set_fmt = ccp2_set_format,
+};
+
+/* subdev operations */
+static const struct v4l2_subdev_ops ccp2_sd_ops = {
+ .video = &ccp2_sd_video_ops,
+ .pad = &ccp2_sd_pad_ops,
+};
+
+/* subdev internal operations */
+static const struct v4l2_subdev_internal_ops ccp2_sd_internal_ops = {
+ .open = ccp2_init_formats,
+};
+
+/* --------------------------------------------------------------------------
+ * ISP ccp2 video device node
+ */
+
+/*
+ * ccp2_video_queue - Queue video buffer.
+ * @video : Pointer to isp video structure
+ * @buffer: Pointer to isp_buffer structure
+ * return -EIO or zero on success
+ */
+static int ccp2_video_queue(struct isp_video *video, struct isp_buffer *buffer)
+{
+ struct isp_ccp2_device *ccp2 = &video->isp->isp_ccp2;
+
+ ccp2_set_inaddr(ccp2, buffer->isp_addr);
+ return 0;
+}
+
+static const struct isp_video_operations ccp2_video_ops = {
+ .queue = ccp2_video_queue,
+};
+
+/* -----------------------------------------------------------------------------
+ * Media entity operations
+ */
+
+/*
+ * ccp2_link_setup - Setup ccp2 connections.
+ * @entity : Pointer to media entity structure
+ * @local : Pointer to local pad array
+ * @remote : Pointer to remote pad array
+ * @flags : Link flags
+ * return -EINVAL on error or zero on success
+ */
+static int ccp2_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd);
+
+ switch (local->index | media_entity_type(remote->entity)) {
+ case CCP2_PAD_SINK | MEDIA_ENT_T_DEVNODE:
+ /* read from memory */
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (ccp2->input == CCP2_INPUT_SENSOR)
+ return -EBUSY;
+ ccp2->input = CCP2_INPUT_MEMORY;
+ } else {
+ if (ccp2->input == CCP2_INPUT_MEMORY)
+ ccp2->input = CCP2_INPUT_NONE;
+ }
+ break;
+
+ case CCP2_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ /* read from sensor/phy */
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (ccp2->input == CCP2_INPUT_MEMORY)
+ return -EBUSY;
+ ccp2->input = CCP2_INPUT_SENSOR;
+ } else {
+ if (ccp2->input == CCP2_INPUT_SENSOR)
+ ccp2->input = CCP2_INPUT_NONE;
+ } break;
+
+ case CCP2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
+ /* write to video port/ccdc */
+ if (flags & MEDIA_LNK_FL_ENABLED)
+ ccp2->output = CCP2_OUTPUT_CCDC;
+ else
+ ccp2->output = CCP2_OUTPUT_NONE;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* media operations */
+static const struct media_entity_operations ccp2_media_ops = {
+ .link_setup = ccp2_link_setup,
+};
+
+/*
+ * omap3isp_ccp2_unregister_entities - Unregister media entities: subdev
+ * @ccp2: Pointer to ISP CCP2 device
+ */
+void omap3isp_ccp2_unregister_entities(struct isp_ccp2_device *ccp2)
+{
+ v4l2_device_unregister_subdev(&ccp2->subdev);
+ omap3isp_video_unregister(&ccp2->video_in);
+}
+
+/*
+ * omap3isp_ccp2_register_entities - Register the subdev media entity
+ * @ccp2: Pointer to ISP CCP2 device
+ * @vdev: Pointer to v4l device
+ * return negative error code or zero on success
+ */
+
+int omap3isp_ccp2_register_entities(struct isp_ccp2_device *ccp2,
+ struct v4l2_device *vdev)
+{
+ int ret;
+
+ /* Register the subdev and video nodes. */
+ ret = v4l2_device_register_subdev(vdev, &ccp2->subdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap3isp_video_register(&ccp2->video_in, vdev);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ omap3isp_ccp2_unregister_entities(ccp2);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP ccp2 initialisation and cleanup
+ */
+
+/*
+ * ccp2_init_entities - Initialize ccp2 subdev and media entity.
+ * @ccp2: Pointer to ISP CCP2 device
+ * return negative error code or zero on success
+ */
+static int ccp2_init_entities(struct isp_ccp2_device *ccp2)
+{
+ struct v4l2_subdev *sd = &ccp2->subdev;
+ struct media_pad *pads = ccp2->pads;
+ struct media_entity *me = &sd->entity;
+ int ret;
+
+ ccp2->input = CCP2_INPUT_NONE;
+ ccp2->output = CCP2_OUTPUT_NONE;
+
+ v4l2_subdev_init(sd, &ccp2_sd_ops);
+ sd->internal_ops = &ccp2_sd_internal_ops;
+ strlcpy(sd->name, "OMAP3 ISP CCP2", sizeof(sd->name));
+ sd->grp_id = 1 << 16; /* group ID for isp subdevs */
+ v4l2_set_subdevdata(sd, ccp2);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ pads[CCP2_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[CCP2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+
+ me->ops = &ccp2_media_ops;
+ ret = media_entity_init(me, CCP2_PADS_NUM, pads, 0);
+ if (ret < 0)
+ return ret;
+
+ ccp2_init_formats(sd, NULL);
+
+ /*
+ * The CCP2 has weird line alignment requirements, possibly caused by
+ * DPCM8 decompression. Line length for data read from memory must be a
+ * multiple of 128 bits (16 bytes) in continuous mode (when no padding
+ * is present at end of lines). Additionally, if padding is used, the
+ * padded line length must be a multiple of 32 bytes. To simplify the
+ * implementation we use a fixed 32 bytes alignment regardless of the
+ * input format and width. If strict 128 bits alignment support is
+ * required ispvideo will need to be made aware of this special dual
+ * alignement requirements.
+ */
+ ccp2->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ ccp2->video_in.bpl_alignment = 32;
+ ccp2->video_in.bpl_max = 0xffffffe0;
+ ccp2->video_in.isp = to_isp_device(ccp2);
+ ccp2->video_in.ops = &ccp2_video_ops;
+ ccp2->video_in.capture_mem = PAGE_ALIGN(4096 * 4096) * 3;
+
+ ret = omap3isp_video_init(&ccp2->video_in, "CCP2");
+ if (ret < 0)
+ goto error_video;
+
+ /* Connect the video node to the ccp2 subdev. */
+ ret = media_entity_create_link(&ccp2->video_in.video.entity, 0,
+ &ccp2->subdev.entity, CCP2_PAD_SINK, 0);
+ if (ret < 0)
+ goto error_link;
+
+ return 0;
+
+error_link:
+ omap3isp_video_cleanup(&ccp2->video_in);
+error_video:
+ media_entity_cleanup(&ccp2->subdev.entity);
+ return ret;
+}
+
+/*
+ * omap3isp_ccp2_init - CCP2 initialization.
+ * @isp : Pointer to ISP device
+ * return negative error code or zero on success
+ */
+int omap3isp_ccp2_init(struct isp_device *isp)
+{
+ struct isp_ccp2_device *ccp2 = &isp->isp_ccp2;
+ int ret;
+
+ init_waitqueue_head(&ccp2->wait);
+
+ /*
+ * On the OMAP34xx the CSI1 receiver is operated in the CSIb IO
+ * complex, which is powered by vdds_csib power rail. Hence the
+ * request for the regulator.
+ *
+ * On the OMAP36xx, the CCP2 uses the CSI PHY1 or PHY2, shared with
+ * the CSI2c or CSI2a receivers. The PHY then needs to be explicitly
+ * configured.
+ *
+ * TODO: Don't hardcode the usage of PHY1 (shared with CSI2c).
+ */
+ if (isp->revision == ISP_REVISION_2_0) {
+ ccp2->vdds_csib = regulator_get(isp->dev, "vdds_csib");
+ if (IS_ERR(ccp2->vdds_csib)) {
+ dev_dbg(isp->dev,
+ "Could not get regulator vdds_csib\n");
+ ccp2->vdds_csib = NULL;
+ }
+ } else if (isp->revision == ISP_REVISION_15_0) {
+ ccp2->phy = &isp->isp_csiphy1;
+ }
+
+ ret = ccp2_init_entities(ccp2);
+ if (ret < 0) {
+ regulator_put(ccp2->vdds_csib);
+ return ret;
+ }
+
+ ccp2_reset(ccp2);
+ return 0;
+}
+
+/*
+ * omap3isp_ccp2_cleanup - CCP2 un-initialization
+ * @isp : Pointer to ISP device
+ */
+void omap3isp_ccp2_cleanup(struct isp_device *isp)
+{
+ struct isp_ccp2_device *ccp2 = &isp->isp_ccp2;
+
+ omap3isp_video_cleanup(&ccp2->video_in);
+ media_entity_cleanup(&ccp2->subdev.entity);
+
+ regulator_put(ccp2->vdds_csib);
+}
diff --git a/drivers/media/video/omap3isp/ispccp2.h b/drivers/media/video/omap3isp/ispccp2.h
new file mode 100644
index 00000000..76d65f45
--- /dev/null
+++ b/drivers/media/video/omap3isp/ispccp2.h
@@ -0,0 +1,98 @@
+/*
+ * ispccp2.h
+ *
+ * TI OMAP3 ISP - CCP2 module
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 Texas Instruments, Inc.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP3_ISP_CCP2_H
+#define OMAP3_ISP_CCP2_H
+
+#include <linux/videodev2.h>
+
+struct isp_device;
+struct isp_csiphy;
+
+/* Sink and source ccp2 pads */
+#define CCP2_PAD_SINK 0
+#define CCP2_PAD_SOURCE 1
+#define CCP2_PADS_NUM 2
+
+/* CCP2 input media entity */
+enum ccp2_input_entity {
+ CCP2_INPUT_NONE,
+ CCP2_INPUT_SENSOR,
+ CCP2_INPUT_MEMORY,
+};
+
+/* CCP2 output media entity */
+enum ccp2_output_entity {
+ CCP2_OUTPUT_NONE,
+ CCP2_OUTPUT_CCDC,
+ CCP2_OUTPUT_MEMORY,
+};
+
+
+/* Logical channel configuration */
+struct isp_interface_lcx_config {
+ int crc;
+ u32 data_start;
+ u32 data_size;
+ u32 format;
+};
+
+/* Memory channel configuration */
+struct isp_interface_mem_config {
+ u32 dst_port;
+ u32 vsize_count;
+ u32 hsize_count;
+ u32 src_ofst;
+ u32 dst_ofst;
+};
+
+/* CCP2 device */
+struct isp_ccp2_device {
+ struct v4l2_subdev subdev;
+ struct v4l2_mbus_framefmt formats[CCP2_PADS_NUM];
+ struct media_pad pads[CCP2_PADS_NUM];
+
+ enum ccp2_input_entity input;
+ enum ccp2_output_entity output;
+ struct isp_interface_lcx_config if_cfg;
+ struct isp_interface_mem_config mem_cfg;
+ struct isp_video video_in;
+ struct isp_csiphy *phy;
+ struct regulator *vdds_csib;
+ enum isp_pipeline_stream_state state;
+ wait_queue_head_t wait;
+ atomic_t stopping;
+};
+
+/* Function declarations */
+int omap3isp_ccp2_init(struct isp_device *isp);
+void omap3isp_ccp2_cleanup(struct isp_device *isp);
+int omap3isp_ccp2_register_entities(struct isp_ccp2_device *ccp2,
+ struct v4l2_device *vdev);
+void omap3isp_ccp2_unregister_entities(struct isp_ccp2_device *ccp2);
+void omap3isp_ccp2_isr(struct isp_ccp2_device *ccp2);
+
+#endif /* OMAP3_ISP_CCP2_H */
diff --git a/drivers/media/video/omap3isp/ispcsi2.c b/drivers/media/video/omap3isp/ispcsi2.c
new file mode 100644
index 00000000..fcb51689
--- /dev/null
+++ b/drivers/media/video/omap3isp/ispcsi2.c
@@ -0,0 +1,1318 @@
+/*
+ * ispcsi2.c
+ *
+ * TI OMAP3 ISP - CSI2 module
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+#include <linux/delay.h>
+#include <media/v4l2-common.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/mm.h>
+
+#include "isp.h"
+#include "ispreg.h"
+#include "ispcsi2.h"
+
+/*
+ * csi2_if_enable - Enable CSI2 Receiver interface.
+ * @enable: enable flag
+ *
+ */
+static void csi2_if_enable(struct isp_device *isp,
+ struct isp_csi2_device *csi2, u8 enable)
+{
+ struct isp_csi2_ctrl_cfg *currctrl = &csi2->ctrl;
+
+ isp_reg_clr_set(isp, csi2->regs1, ISPCSI2_CTRL, ISPCSI2_CTRL_IF_EN,
+ enable ? ISPCSI2_CTRL_IF_EN : 0);
+
+ currctrl->if_enable = enable;
+}
+
+/*
+ * csi2_recv_config - CSI2 receiver module configuration.
+ * @currctrl: isp_csi2_ctrl_cfg structure
+ *
+ */
+static void csi2_recv_config(struct isp_device *isp,
+ struct isp_csi2_device *csi2,
+ struct isp_csi2_ctrl_cfg *currctrl)
+{
+ u32 reg;
+
+ reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTRL);
+
+ if (currctrl->frame_mode)
+ reg |= ISPCSI2_CTRL_FRAME;
+ else
+ reg &= ~ISPCSI2_CTRL_FRAME;
+
+ if (currctrl->vp_clk_enable)
+ reg |= ISPCSI2_CTRL_VP_CLK_EN;
+ else
+ reg &= ~ISPCSI2_CTRL_VP_CLK_EN;
+
+ if (currctrl->vp_only_enable)
+ reg |= ISPCSI2_CTRL_VP_ONLY_EN;
+ else
+ reg &= ~ISPCSI2_CTRL_VP_ONLY_EN;
+
+ reg &= ~ISPCSI2_CTRL_VP_OUT_CTRL_MASK;
+ reg |= currctrl->vp_out_ctrl << ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT;
+
+ if (currctrl->ecc_enable)
+ reg |= ISPCSI2_CTRL_ECC_EN;
+ else
+ reg &= ~ISPCSI2_CTRL_ECC_EN;
+
+ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTRL);
+}
+
+static const unsigned int csi2_input_fmts[] = {
+ V4L2_MBUS_FMT_SGRBG10_1X10,
+ V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
+ V4L2_MBUS_FMT_SRGGB10_1X10,
+ V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8,
+ V4L2_MBUS_FMT_SBGGR10_1X10,
+ V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8,
+ V4L2_MBUS_FMT_SGBRG10_1X10,
+ V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8,
+};
+
+/* To set the format on the CSI2 requires a mapping function that takes
+ * the following inputs:
+ * - 2 different formats (at this time)
+ * - 2 destinations (mem, vp+mem) (vp only handled separately)
+ * - 2 decompression options (on, off)
+ * - 2 isp revisions (certain format must be handled differently on OMAP3630)
+ * Output should be CSI2 frame format code
+ * Array indices as follows: [format][dest][decompr][is_3630]
+ * Not all combinations are valid. 0 means invalid.
+ */
+static const u16 __csi2_fmt_map[2][2][2][2] = {
+ /* RAW10 formats */
+ {
+ /* Output to memory */
+ {
+ /* No DPCM decompression */
+ { CSI2_PIX_FMT_RAW10_EXP16, CSI2_PIX_FMT_RAW10_EXP16 },
+ /* DPCM decompression */
+ { 0, 0 },
+ },
+ /* Output to both */
+ {
+ /* No DPCM decompression */
+ { CSI2_PIX_FMT_RAW10_EXP16_VP,
+ CSI2_PIX_FMT_RAW10_EXP16_VP },
+ /* DPCM decompression */
+ { 0, 0 },
+ },
+ },
+ /* RAW10 DPCM8 formats */
+ {
+ /* Output to memory */
+ {
+ /* No DPCM decompression */
+ { CSI2_PIX_FMT_RAW8, CSI2_USERDEF_8BIT_DATA1 },
+ /* DPCM decompression */
+ { CSI2_PIX_FMT_RAW8_DPCM10_EXP16,
+ CSI2_USERDEF_8BIT_DATA1_DPCM10 },
+ },
+ /* Output to both */
+ {
+ /* No DPCM decompression */
+ { CSI2_PIX_FMT_RAW8_VP,
+ CSI2_PIX_FMT_RAW8_VP },
+ /* DPCM decompression */
+ { CSI2_PIX_FMT_RAW8_DPCM10_VP,
+ CSI2_USERDEF_8BIT_DATA1_DPCM10_VP },
+ },
+ },
+};
+
+/*
+ * csi2_ctx_map_format - Map CSI2 sink media bus format to CSI2 format ID
+ * @csi2: ISP CSI2 device
+ *
+ * Returns CSI2 physical format id
+ */
+static u16 csi2_ctx_map_format(struct isp_csi2_device *csi2)
+{
+ const struct v4l2_mbus_framefmt *fmt = &csi2->formats[CSI2_PAD_SINK];
+ int fmtidx, destidx, is_3630;
+
+ switch (fmt->code) {
+ case V4L2_MBUS_FMT_SGRBG10_1X10:
+ case V4L2_MBUS_FMT_SRGGB10_1X10:
+ case V4L2_MBUS_FMT_SBGGR10_1X10:
+ case V4L2_MBUS_FMT_SGBRG10_1X10:
+ fmtidx = 0;
+ break;
+ case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8:
+ case V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8:
+ case V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8:
+ case V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8:
+ fmtidx = 1;
+ break;
+ default:
+ WARN(1, KERN_ERR "CSI2: pixel format %08x unsupported!\n",
+ fmt->code);
+ return 0;
+ }
+
+ if (!(csi2->output & CSI2_OUTPUT_CCDC) &&
+ !(csi2->output & CSI2_OUTPUT_MEMORY)) {
+ /* Neither output enabled is a valid combination */
+ return CSI2_PIX_FMT_OTHERS;
+ }
+
+ /* If we need to skip frames at the beginning of the stream disable the
+ * video port to avoid sending the skipped frames to the CCDC.
+ */
+ destidx = csi2->frame_skip ? 0 : !!(csi2->output & CSI2_OUTPUT_CCDC);
+ is_3630 = csi2->isp->revision == ISP_REVISION_15_0;
+
+ return __csi2_fmt_map[fmtidx][destidx][csi2->dpcm_decompress][is_3630];
+}
+
+/*
+ * csi2_set_outaddr - Set memory address to save output image
+ * @csi2: Pointer to ISP CSI2a device.
+ * @addr: ISP MMU Mapped 32-bit memory address aligned on 32 byte boundary.
+ *
+ * Sets the memory address where the output will be saved.
+ *
+ * Returns 0 if successful, or -EINVAL if the address is not in the 32 byte
+ * boundary.
+ */
+static void csi2_set_outaddr(struct isp_csi2_device *csi2, u32 addr)
+{
+ struct isp_device *isp = csi2->isp;
+ struct isp_csi2_ctx_cfg *ctx = &csi2->contexts[0];
+
+ ctx->ping_addr = addr;
+ ctx->pong_addr = addr;
+ isp_reg_writel(isp, ctx->ping_addr,
+ csi2->regs1, ISPCSI2_CTX_DAT_PING_ADDR(ctx->ctxnum));
+ isp_reg_writel(isp, ctx->pong_addr,
+ csi2->regs1, ISPCSI2_CTX_DAT_PONG_ADDR(ctx->ctxnum));
+}
+
+/*
+ * is_usr_def_mapping - Checks whether USER_DEF_MAPPING should
+ * be enabled by CSI2.
+ * @format_id: mapped format id
+ *
+ */
+static inline int is_usr_def_mapping(u32 format_id)
+{
+ return (format_id & 0x40) ? 1 : 0;
+}
+
+/*
+ * csi2_ctx_enable - Enable specified CSI2 context
+ * @ctxnum: Context number, valid between 0 and 7 values.
+ * @enable: enable
+ *
+ */
+static void csi2_ctx_enable(struct isp_device *isp,
+ struct isp_csi2_device *csi2, u8 ctxnum, u8 enable)
+{
+ struct isp_csi2_ctx_cfg *ctx = &csi2->contexts[ctxnum];
+ unsigned int skip = 0;
+ u32 reg;
+
+ reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL1(ctxnum));
+
+ if (enable) {
+ if (csi2->frame_skip)
+ skip = csi2->frame_skip;
+ else if (csi2->output & CSI2_OUTPUT_MEMORY)
+ skip = 1;
+
+ reg &= ~ISPCSI2_CTX_CTRL1_COUNT_MASK;
+ reg |= ISPCSI2_CTX_CTRL1_COUNT_UNLOCK
+ | (skip << ISPCSI2_CTX_CTRL1_COUNT_SHIFT)
+ | ISPCSI2_CTX_CTRL1_CTX_EN;
+ } else {
+ reg &= ~ISPCSI2_CTX_CTRL1_CTX_EN;
+ }
+
+ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL1(ctxnum));
+ ctx->enabled = enable;
+}
+
+/*
+ * csi2_ctx_config - CSI2 context configuration.
+ * @ctx: context configuration
+ *
+ */
+static void csi2_ctx_config(struct isp_device *isp,
+ struct isp_csi2_device *csi2,
+ struct isp_csi2_ctx_cfg *ctx)
+{
+ u32 reg;
+
+ /* Set up CSI2_CTx_CTRL1 */
+ reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL1(ctx->ctxnum));
+
+ if (ctx->eof_enabled)
+ reg |= ISPCSI2_CTX_CTRL1_EOF_EN;
+ else
+ reg &= ~ISPCSI2_CTX_CTRL1_EOF_EN;
+
+ if (ctx->eol_enabled)
+ reg |= ISPCSI2_CTX_CTRL1_EOL_EN;
+ else
+ reg &= ~ISPCSI2_CTX_CTRL1_EOL_EN;
+
+ if (ctx->checksum_enabled)
+ reg |= ISPCSI2_CTX_CTRL1_CS_EN;
+ else
+ reg &= ~ISPCSI2_CTX_CTRL1_CS_EN;
+
+ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL1(ctx->ctxnum));
+
+ /* Set up CSI2_CTx_CTRL2 */
+ reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL2(ctx->ctxnum));
+
+ reg &= ~(ISPCSI2_CTX_CTRL2_VIRTUAL_ID_MASK);
+ reg |= ctx->virtual_id << ISPCSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT;
+
+ reg &= ~(ISPCSI2_CTX_CTRL2_FORMAT_MASK);
+ reg |= ctx->format_id << ISPCSI2_CTX_CTRL2_FORMAT_SHIFT;
+
+ if (ctx->dpcm_decompress) {
+ if (ctx->dpcm_predictor)
+ reg |= ISPCSI2_CTX_CTRL2_DPCM_PRED;
+ else
+ reg &= ~ISPCSI2_CTX_CTRL2_DPCM_PRED;
+ }
+
+ if (is_usr_def_mapping(ctx->format_id)) {
+ reg &= ~ISPCSI2_CTX_CTRL2_USER_DEF_MAP_MASK;
+ reg |= 2 << ISPCSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT;
+ }
+
+ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL2(ctx->ctxnum));
+
+ /* Set up CSI2_CTx_CTRL3 */
+ reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL3(ctx->ctxnum));
+ reg &= ~(ISPCSI2_CTX_CTRL3_ALPHA_MASK);
+ reg |= (ctx->alpha << ISPCSI2_CTX_CTRL3_ALPHA_SHIFT);
+
+ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL3(ctx->ctxnum));
+
+ /* Set up CSI2_CTx_DAT_OFST */
+ reg = isp_reg_readl(isp, csi2->regs1,
+ ISPCSI2_CTX_DAT_OFST(ctx->ctxnum));
+ reg &= ~ISPCSI2_CTX_DAT_OFST_OFST_MASK;
+ reg |= ctx->data_offset << ISPCSI2_CTX_DAT_OFST_OFST_SHIFT;
+ isp_reg_writel(isp, reg, csi2->regs1,
+ ISPCSI2_CTX_DAT_OFST(ctx->ctxnum));
+
+ isp_reg_writel(isp, ctx->ping_addr,
+ csi2->regs1, ISPCSI2_CTX_DAT_PING_ADDR(ctx->ctxnum));
+
+ isp_reg_writel(isp, ctx->pong_addr,
+ csi2->regs1, ISPCSI2_CTX_DAT_PONG_ADDR(ctx->ctxnum));
+}
+
+/*
+ * csi2_timing_config - CSI2 timing configuration.
+ * @timing: csi2_timing_cfg structure
+ */
+static void csi2_timing_config(struct isp_device *isp,
+ struct isp_csi2_device *csi2,
+ struct isp_csi2_timing_cfg *timing)
+{
+ u32 reg;
+
+ reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_TIMING);
+
+ if (timing->force_rx_mode)
+ reg |= ISPCSI2_TIMING_FORCE_RX_MODE_IO(timing->ionum);
+ else
+ reg &= ~ISPCSI2_TIMING_FORCE_RX_MODE_IO(timing->ionum);
+
+ if (timing->stop_state_16x)
+ reg |= ISPCSI2_TIMING_STOP_STATE_X16_IO(timing->ionum);
+ else
+ reg &= ~ISPCSI2_TIMING_STOP_STATE_X16_IO(timing->ionum);
+
+ if (timing->stop_state_4x)
+ reg |= ISPCSI2_TIMING_STOP_STATE_X4_IO(timing->ionum);
+ else
+ reg &= ~ISPCSI2_TIMING_STOP_STATE_X4_IO(timing->ionum);
+
+ reg &= ~ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_MASK(timing->ionum);
+ reg |= timing->stop_state_counter <<
+ ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_SHIFT(timing->ionum);
+
+ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_TIMING);
+}
+
+/*
+ * csi2_irq_ctx_set - Enables CSI2 Context IRQs.
+ * @enable: Enable/disable CSI2 Context interrupts
+ */
+static void csi2_irq_ctx_set(struct isp_device *isp,
+ struct isp_csi2_device *csi2, int enable)
+{
+ u32 reg = ISPCSI2_CTX_IRQSTATUS_FE_IRQ;
+ int i;
+
+ if (csi2->use_fs_irq)
+ reg |= ISPCSI2_CTX_IRQSTATUS_FS_IRQ;
+
+ for (i = 0; i < 8; i++) {
+ isp_reg_writel(isp, reg, csi2->regs1,
+ ISPCSI2_CTX_IRQSTATUS(i));
+ if (enable)
+ isp_reg_set(isp, csi2->regs1, ISPCSI2_CTX_IRQENABLE(i),
+ reg);
+ else
+ isp_reg_clr(isp, csi2->regs1, ISPCSI2_CTX_IRQENABLE(i),
+ reg);
+ }
+}
+
+/*
+ * csi2_irq_complexio1_set - Enables CSI2 ComplexIO IRQs.
+ * @enable: Enable/disable CSI2 ComplexIO #1 interrupts
+ */
+static void csi2_irq_complexio1_set(struct isp_device *isp,
+ struct isp_csi2_device *csi2, int enable)
+{
+ u32 reg;
+ reg = ISPCSI2_PHY_IRQENABLE_STATEALLULPMEXIT |
+ ISPCSI2_PHY_IRQENABLE_STATEALLULPMENTER |
+ ISPCSI2_PHY_IRQENABLE_STATEULPM5 |
+ ISPCSI2_PHY_IRQENABLE_ERRCONTROL5 |
+ ISPCSI2_PHY_IRQENABLE_ERRESC5 |
+ ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS5 |
+ ISPCSI2_PHY_IRQENABLE_ERRSOTHS5 |
+ ISPCSI2_PHY_IRQENABLE_STATEULPM4 |
+ ISPCSI2_PHY_IRQENABLE_ERRCONTROL4 |
+ ISPCSI2_PHY_IRQENABLE_ERRESC4 |
+ ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS4 |
+ ISPCSI2_PHY_IRQENABLE_ERRSOTHS4 |
+ ISPCSI2_PHY_IRQENABLE_STATEULPM3 |
+ ISPCSI2_PHY_IRQENABLE_ERRCONTROL3 |
+ ISPCSI2_PHY_IRQENABLE_ERRESC3 |
+ ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS3 |
+ ISPCSI2_PHY_IRQENABLE_ERRSOTHS3 |
+ ISPCSI2_PHY_IRQENABLE_STATEULPM2 |
+ ISPCSI2_PHY_IRQENABLE_ERRCONTROL2 |
+ ISPCSI2_PHY_IRQENABLE_ERRESC2 |
+ ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS2 |
+ ISPCSI2_PHY_IRQENABLE_ERRSOTHS2 |
+ ISPCSI2_PHY_IRQENABLE_STATEULPM1 |
+ ISPCSI2_PHY_IRQENABLE_ERRCONTROL1 |
+ ISPCSI2_PHY_IRQENABLE_ERRESC1 |
+ ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS1 |
+ ISPCSI2_PHY_IRQENABLE_ERRSOTHS1;
+ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_PHY_IRQSTATUS);
+ if (enable)
+ reg |= isp_reg_readl(isp, csi2->regs1, ISPCSI2_PHY_IRQENABLE);
+ else
+ reg = 0;
+ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_PHY_IRQENABLE);
+}
+
+/*
+ * csi2_irq_status_set - Enables CSI2 Status IRQs.
+ * @enable: Enable/disable CSI2 Status interrupts
+ */
+static void csi2_irq_status_set(struct isp_device *isp,
+ struct isp_csi2_device *csi2, int enable)
+{
+ u32 reg;
+ reg = ISPCSI2_IRQSTATUS_OCP_ERR_IRQ |
+ ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ |
+ ISPCSI2_IRQSTATUS_ECC_CORRECTION_IRQ |
+ ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ |
+ ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ |
+ ISPCSI2_IRQSTATUS_COMPLEXIO1_ERR_IRQ |
+ ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ |
+ ISPCSI2_IRQSTATUS_CONTEXT(0);
+ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_IRQSTATUS);
+ if (enable)
+ reg |= isp_reg_readl(isp, csi2->regs1, ISPCSI2_IRQENABLE);
+ else
+ reg = 0;
+
+ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_IRQENABLE);
+}
+
+/*
+ * omap3isp_csi2_reset - Resets the CSI2 module.
+ *
+ * Must be called with the phy lock held.
+ *
+ * Returns 0 if successful, or -EBUSY if power command didn't respond.
+ */
+int omap3isp_csi2_reset(struct isp_csi2_device *csi2)
+{
+ struct isp_device *isp = csi2->isp;
+ u8 soft_reset_retries = 0;
+ u32 reg;
+ int i;
+
+ if (!csi2->available)
+ return -ENODEV;
+
+ if (csi2->phy->phy_in_use)
+ return -EBUSY;
+
+ isp_reg_set(isp, csi2->regs1, ISPCSI2_SYSCONFIG,
+ ISPCSI2_SYSCONFIG_SOFT_RESET);
+
+ do {
+ reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_SYSSTATUS) &
+ ISPCSI2_SYSSTATUS_RESET_DONE;
+ if (reg == ISPCSI2_SYSSTATUS_RESET_DONE)
+ break;
+ soft_reset_retries++;
+ if (soft_reset_retries < 5)
+ udelay(100);
+ } while (soft_reset_retries < 5);
+
+ if (soft_reset_retries == 5) {
+ printk(KERN_ERR "CSI2: Soft reset try count exceeded!\n");
+ return -EBUSY;
+ }
+
+ if (isp->revision == ISP_REVISION_15_0)
+ isp_reg_set(isp, csi2->regs1, ISPCSI2_PHY_CFG,
+ ISPCSI2_PHY_CFG_RESET_CTRL);
+
+ i = 100;
+ do {
+ reg = isp_reg_readl(isp, csi2->phy->phy_regs, ISPCSIPHY_REG1)
+ & ISPCSIPHY_REG1_RESET_DONE_CTRLCLK;
+ if (reg == ISPCSIPHY_REG1_RESET_DONE_CTRLCLK)
+ break;
+ udelay(100);
+ } while (--i > 0);
+
+ if (i == 0) {
+ printk(KERN_ERR
+ "CSI2: Reset for CSI2_96M_FCLK domain Failed!\n");
+ return -EBUSY;
+ }
+
+ if (isp->autoidle)
+ isp_reg_clr_set(isp, csi2->regs1, ISPCSI2_SYSCONFIG,
+ ISPCSI2_SYSCONFIG_MSTANDBY_MODE_MASK |
+ ISPCSI2_SYSCONFIG_AUTO_IDLE,
+ ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SMART |
+ ((isp->revision == ISP_REVISION_15_0) ?
+ ISPCSI2_SYSCONFIG_AUTO_IDLE : 0));
+ else
+ isp_reg_clr_set(isp, csi2->regs1, ISPCSI2_SYSCONFIG,
+ ISPCSI2_SYSCONFIG_MSTANDBY_MODE_MASK |
+ ISPCSI2_SYSCONFIG_AUTO_IDLE,
+ ISPCSI2_SYSCONFIG_MSTANDBY_MODE_NO);
+
+ return 0;
+}
+
+static int csi2_configure(struct isp_csi2_device *csi2)
+{
+ const struct isp_v4l2_subdevs_group *pdata;
+ struct isp_device *isp = csi2->isp;
+ struct isp_csi2_timing_cfg *timing = &csi2->timing[0];
+ struct v4l2_subdev *sensor;
+ struct media_pad *pad;
+
+ /*
+ * CSI2 fields that can be updated while the context has
+ * been enabled or the interface has been enabled are not
+ * updated dynamically currently. So we do not allow to
+ * reconfigure if either has been enabled
+ */
+ if (csi2->contexts[0].enabled || csi2->ctrl.if_enable)
+ return -EBUSY;
+
+ pad = media_entity_remote_source(&csi2->pads[CSI2_PAD_SINK]);
+ sensor = media_entity_to_v4l2_subdev(pad->entity);
+ pdata = sensor->host_priv;
+
+ csi2->frame_skip = 0;
+ v4l2_subdev_call(sensor, sensor, g_skip_frames, &csi2->frame_skip);
+
+ csi2->ctrl.vp_out_ctrl = pdata->bus.csi2.vpclk_div;
+ csi2->ctrl.frame_mode = ISP_CSI2_FRAME_IMMEDIATE;
+ csi2->ctrl.ecc_enable = pdata->bus.csi2.crc;
+
+ timing->ionum = 1;
+ timing->force_rx_mode = 1;
+ timing->stop_state_16x = 1;
+ timing->stop_state_4x = 1;
+ timing->stop_state_counter = 0x1FF;
+
+ /*
+ * The CSI2 receiver can't do any format conversion except DPCM
+ * decompression, so every set_format call configures both pads
+ * and enables DPCM decompression as a special case:
+ */
+ if (csi2->formats[CSI2_PAD_SINK].code !=
+ csi2->formats[CSI2_PAD_SOURCE].code)
+ csi2->dpcm_decompress = true;
+ else
+ csi2->dpcm_decompress = false;
+
+ csi2->contexts[0].format_id = csi2_ctx_map_format(csi2);
+
+ if (csi2->video_out.bpl_padding == 0)
+ csi2->contexts[0].data_offset = 0;
+ else
+ csi2->contexts[0].data_offset = csi2->video_out.bpl_value;
+
+ /*
+ * Enable end of frame and end of line signals generation for
+ * context 0. These signals are generated from CSI2 receiver to
+ * qualify the last pixel of a frame and the last pixel of a line.
+ * Without enabling the signals CSI2 receiver writes data to memory
+ * beyond buffer size and/or data line offset is not handled correctly.
+ */
+ csi2->contexts[0].eof_enabled = 1;
+ csi2->contexts[0].eol_enabled = 1;
+
+ csi2_irq_complexio1_set(isp, csi2, 1);
+ csi2_irq_ctx_set(isp, csi2, 1);
+ csi2_irq_status_set(isp, csi2, 1);
+
+ /* Set configuration (timings, format and links) */
+ csi2_timing_config(isp, csi2, timing);
+ csi2_recv_config(isp, csi2, &csi2->ctrl);
+ csi2_ctx_config(isp, csi2, &csi2->contexts[0]);
+
+ return 0;
+}
+
+/*
+ * csi2_print_status - Prints CSI2 debug information.
+ */
+#define CSI2_PRINT_REGISTER(isp, regs, name)\
+ dev_dbg(isp->dev, "###CSI2 " #name "=0x%08x\n", \
+ isp_reg_readl(isp, regs, ISPCSI2_##name))
+
+static void csi2_print_status(struct isp_csi2_device *csi2)
+{
+ struct isp_device *isp = csi2->isp;
+
+ if (!csi2->available)
+ return;
+
+ dev_dbg(isp->dev, "-------------CSI2 Register dump-------------\n");
+
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, SYSCONFIG);
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, SYSSTATUS);
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, IRQENABLE);
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, IRQSTATUS);
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, CTRL);
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, DBG_H);
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, GNQ);
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, PHY_CFG);
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, PHY_IRQSTATUS);
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, SHORT_PACKET);
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, PHY_IRQENABLE);
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, DBG_P);
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, TIMING);
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_CTRL1(0));
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_CTRL2(0));
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_DAT_OFST(0));
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_DAT_PING_ADDR(0));
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_DAT_PONG_ADDR(0));
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_IRQENABLE(0));
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_IRQSTATUS(0));
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_CTRL3(0));
+
+ dev_dbg(isp->dev, "--------------------------------------------\n");
+}
+
+/* -----------------------------------------------------------------------------
+ * Interrupt handling
+ */
+
+/*
+ * csi2_isr_buffer - Does buffer handling at end-of-frame
+ * when writing to memory.
+ */
+static void csi2_isr_buffer(struct isp_csi2_device *csi2)
+{
+ struct isp_device *isp = csi2->isp;
+ struct isp_buffer *buffer;
+
+ csi2_ctx_enable(isp, csi2, 0, 0);
+
+ buffer = omap3isp_video_buffer_next(&csi2->video_out);
+
+ /*
+ * Let video queue operation restart engine if there is an underrun
+ * condition.
+ */
+ if (buffer == NULL)
+ return;
+
+ csi2_set_outaddr(csi2, buffer->isp_addr);
+ csi2_ctx_enable(isp, csi2, 0, 1);
+}
+
+static void csi2_isr_ctx(struct isp_csi2_device *csi2,
+ struct isp_csi2_ctx_cfg *ctx)
+{
+ struct isp_device *isp = csi2->isp;
+ unsigned int n = ctx->ctxnum;
+ u32 status;
+
+ status = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(n));
+ isp_reg_writel(isp, status, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(n));
+
+ /* Propagate frame number */
+ if (status & ISPCSI2_CTX_IRQSTATUS_FS_IRQ) {
+ struct isp_pipeline *pipe =
+ to_isp_pipeline(&csi2->subdev.entity);
+ if (pipe->do_propagation)
+ atomic_inc(&pipe->frame_number);
+ }
+
+ if (!(status & ISPCSI2_CTX_IRQSTATUS_FE_IRQ))
+ return;
+
+ /* Skip interrupts until we reach the frame skip count. The CSI2 will be
+ * automatically disabled, as the frame skip count has been programmed
+ * in the CSI2_CTx_CTRL1::COUNT field, so reenable it.
+ *
+ * It would have been nice to rely on the FRAME_NUMBER interrupt instead
+ * but it turned out that the interrupt is only generated when the CSI2
+ * writes to memory (the CSI2_CTx_CTRL1::COUNT field is decreased
+ * correctly and reaches 0 when data is forwarded to the video port only
+ * but no interrupt arrives). Maybe a CSI2 hardware bug.
+ */
+ if (csi2->frame_skip) {
+ csi2->frame_skip--;
+ if (csi2->frame_skip == 0) {
+ ctx->format_id = csi2_ctx_map_format(csi2);
+ csi2_ctx_config(isp, csi2, ctx);
+ csi2_ctx_enable(isp, csi2, n, 1);
+ }
+ return;
+ }
+
+ if (csi2->output & CSI2_OUTPUT_MEMORY)
+ csi2_isr_buffer(csi2);
+}
+
+/*
+ * omap3isp_csi2_isr - CSI2 interrupt handling.
+ */
+void omap3isp_csi2_isr(struct isp_csi2_device *csi2)
+{
+ struct isp_pipeline *pipe = to_isp_pipeline(&csi2->subdev.entity);
+ u32 csi2_irqstatus, cpxio1_irqstatus;
+ struct isp_device *isp = csi2->isp;
+
+ if (!csi2->available)
+ return;
+
+ csi2_irqstatus = isp_reg_readl(isp, csi2->regs1, ISPCSI2_IRQSTATUS);
+ isp_reg_writel(isp, csi2_irqstatus, csi2->regs1, ISPCSI2_IRQSTATUS);
+
+ /* Failure Cases */
+ if (csi2_irqstatus & ISPCSI2_IRQSTATUS_COMPLEXIO1_ERR_IRQ) {
+ cpxio1_irqstatus = isp_reg_readl(isp, csi2->regs1,
+ ISPCSI2_PHY_IRQSTATUS);
+ isp_reg_writel(isp, cpxio1_irqstatus,
+ csi2->regs1, ISPCSI2_PHY_IRQSTATUS);
+ dev_dbg(isp->dev, "CSI2: ComplexIO Error IRQ "
+ "%x\n", cpxio1_irqstatus);
+ pipe->error = true;
+ }
+
+ if (csi2_irqstatus & (ISPCSI2_IRQSTATUS_OCP_ERR_IRQ |
+ ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ |
+ ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ |
+ ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ |
+ ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ)) {
+ dev_dbg(isp->dev, "CSI2 Err:"
+ " OCP:%d,"
+ " Short_pack:%d,"
+ " ECC:%d,"
+ " CPXIO2:%d,"
+ " FIFO_OVF:%d,"
+ "\n",
+ (csi2_irqstatus &
+ ISPCSI2_IRQSTATUS_OCP_ERR_IRQ) ? 1 : 0,
+ (csi2_irqstatus &
+ ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ) ? 1 : 0,
+ (csi2_irqstatus &
+ ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ) ? 1 : 0,
+ (csi2_irqstatus &
+ ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ) ? 1 : 0,
+ (csi2_irqstatus &
+ ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ) ? 1 : 0);
+ pipe->error = true;
+ }
+
+ if (omap3isp_module_sync_is_stopping(&csi2->wait, &csi2->stopping))
+ return;
+
+ /* Successful cases */
+ if (csi2_irqstatus & ISPCSI2_IRQSTATUS_CONTEXT(0))
+ csi2_isr_ctx(csi2, &csi2->contexts[0]);
+
+ if (csi2_irqstatus & ISPCSI2_IRQSTATUS_ECC_CORRECTION_IRQ)
+ dev_dbg(isp->dev, "CSI2: ECC correction done\n");
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP video operations
+ */
+
+/*
+ * csi2_queue - Queues the first buffer when using memory output
+ * @video: The video node
+ * @buffer: buffer to queue
+ */
+static int csi2_queue(struct isp_video *video, struct isp_buffer *buffer)
+{
+ struct isp_device *isp = video->isp;
+ struct isp_csi2_device *csi2 = &isp->isp_csi2a;
+
+ csi2_set_outaddr(csi2, buffer->isp_addr);
+
+ /*
+ * If streaming was enabled before there was a buffer queued
+ * or underrun happened in the ISR, the hardware was not enabled
+ * and DMA queue flag ISP_VIDEO_DMAQUEUE_UNDERRUN is still set.
+ * Enable it now.
+ */
+ if (csi2->video_out.dmaqueue_flags & ISP_VIDEO_DMAQUEUE_UNDERRUN) {
+ /* Enable / disable context 0 and IRQs */
+ csi2_if_enable(isp, csi2, 1);
+ csi2_ctx_enable(isp, csi2, 0, 1);
+ isp_video_dmaqueue_flags_clr(&csi2->video_out);
+ }
+
+ return 0;
+}
+
+static const struct isp_video_operations csi2_ispvideo_ops = {
+ .queue = csi2_queue,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+static struct v4l2_mbus_framefmt *
+__csi2_get_format(struct isp_csi2_device *csi2, struct v4l2_subdev_fh *fh,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(fh, pad);
+ else
+ return &csi2->formats[pad];
+}
+
+static void
+csi2_try_format(struct isp_csi2_device *csi2, struct v4l2_subdev_fh *fh,
+ unsigned int pad, struct v4l2_mbus_framefmt *fmt,
+ enum v4l2_subdev_format_whence which)
+{
+ enum v4l2_mbus_pixelcode pixelcode;
+ struct v4l2_mbus_framefmt *format;
+ const struct isp_format_info *info;
+ unsigned int i;
+
+ switch (pad) {
+ case CSI2_PAD_SINK:
+ /* Clamp the width and height to valid range (1-8191). */
+ for (i = 0; i < ARRAY_SIZE(csi2_input_fmts); i++) {
+ if (fmt->code == csi2_input_fmts[i])
+ break;
+ }
+
+ /* If not found, use SGRBG10 as default */
+ if (i >= ARRAY_SIZE(csi2_input_fmts))
+ fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+
+ fmt->width = clamp_t(u32, fmt->width, 1, 8191);
+ fmt->height = clamp_t(u32, fmt->height, 1, 8191);
+ break;
+
+ case CSI2_PAD_SOURCE:
+ /* Source format same as sink format, except for DPCM
+ * compression.
+ */
+ pixelcode = fmt->code;
+ format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK, which);
+ memcpy(fmt, format, sizeof(*fmt));
+
+ /*
+ * Only Allow DPCM decompression, and check that the
+ * pattern is preserved
+ */
+ info = omap3isp_video_format_info(fmt->code);
+ if (info->uncompressed == pixelcode)
+ fmt->code = pixelcode;
+ break;
+ }
+
+ /* RGB, non-interlaced */
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ fmt->field = V4L2_FIELD_NONE;
+}
+
+/*
+ * csi2_enum_mbus_code - Handle pixel format enumeration
+ * @sd : pointer to v4l2 subdev structure
+ * @fh : V4L2 subdev file handle
+ * @code : pointer to v4l2_subdev_mbus_code_enum structure
+ * return -EINVAL or zero on success
+ */
+static int csi2_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+ const struct isp_format_info *info;
+
+ if (code->pad == CSI2_PAD_SINK) {
+ if (code->index >= ARRAY_SIZE(csi2_input_fmts))
+ return -EINVAL;
+
+ code->code = csi2_input_fmts[code->index];
+ } else {
+ format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK,
+ V4L2_SUBDEV_FORMAT_TRY);
+ switch (code->index) {
+ case 0:
+ /* Passthrough sink pad code */
+ code->code = format->code;
+ break;
+ case 1:
+ /* Uncompressed code */
+ info = omap3isp_video_format_info(format->code);
+ if (info->uncompressed == format->code)
+ return -EINVAL;
+
+ code->code = info->uncompressed;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int csi2_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt format;
+
+ if (fse->index != 0)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = 1;
+ format.height = 1;
+ csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+ fse->min_width = format.width;
+ fse->min_height = format.height;
+
+ if (format.code != fse->code)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = -1;
+ format.height = -1;
+ csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+ fse->max_width = format.width;
+ fse->max_height = format.height;
+
+ return 0;
+}
+
+/*
+ * csi2_get_format - Handle get format by pads subdev method
+ * @sd : pointer to v4l2 subdev structure
+ * @fh : V4L2 subdev file handle
+ * @fmt: pointer to v4l2 subdev format structure
+ * return -EINVAL or zero on success
+ */
+static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ fmt->format = *format;
+ return 0;
+}
+
+/*
+ * csi2_set_format - Handle set format by pads subdev method
+ * @sd : pointer to v4l2 subdev structure
+ * @fh : V4L2 subdev file handle
+ * @fmt: pointer to v4l2 subdev format structure
+ * return -EINVAL or zero on success
+ */
+static int csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ csi2_try_format(csi2, fh, fmt->pad, &fmt->format, fmt->which);
+ *format = fmt->format;
+
+ /* Propagate the format from sink to source */
+ if (fmt->pad == CSI2_PAD_SINK) {
+ format = __csi2_get_format(csi2, fh, CSI2_PAD_SOURCE,
+ fmt->which);
+ *format = fmt->format;
+ csi2_try_format(csi2, fh, CSI2_PAD_SOURCE, format, fmt->which);
+ }
+
+ return 0;
+}
+
+/*
+ * csi2_init_formats - Initialize formats on all pads
+ * @sd: ISP CSI2 V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ *
+ * Initialize all pad formats with default values. If fh is not NULL, try
+ * formats are initialized on the file handle. Otherwise active formats are
+ * initialized on the device.
+ */
+static int csi2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_subdev_format format;
+
+ memset(&format, 0, sizeof(format));
+ format.pad = CSI2_PAD_SINK;
+ format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
+ format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ format.format.width = 4096;
+ format.format.height = 4096;
+ csi2_set_format(sd, fh, &format);
+
+ return 0;
+}
+
+/*
+ * csi2_set_stream - Enable/Disable streaming on the CSI2 module
+ * @sd: ISP CSI2 V4L2 subdevice
+ * @enable: ISP pipeline stream state
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+static int csi2_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+ struct isp_device *isp = csi2->isp;
+ struct isp_pipeline *pipe = to_isp_pipeline(&csi2->subdev.entity);
+ struct isp_video *video_out = &csi2->video_out;
+
+ switch (enable) {
+ case ISP_PIPELINE_STREAM_CONTINUOUS:
+ if (omap3isp_csiphy_acquire(csi2->phy) < 0)
+ return -ENODEV;
+ csi2->use_fs_irq = pipe->do_propagation;
+ if (csi2->output & CSI2_OUTPUT_MEMORY)
+ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CSI2A_WRITE);
+ csi2_configure(csi2);
+ csi2_print_status(csi2);
+
+ /*
+ * When outputting to memory with no buffer available, let the
+ * buffer queue handler start the hardware. A DMA queue flag
+ * ISP_VIDEO_DMAQUEUE_QUEUED will be set as soon as there is
+ * a buffer available.
+ */
+ if (csi2->output & CSI2_OUTPUT_MEMORY &&
+ !(video_out->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED))
+ break;
+ /* Enable context 0 and IRQs */
+ atomic_set(&csi2->stopping, 0);
+ csi2_ctx_enable(isp, csi2, 0, 1);
+ csi2_if_enable(isp, csi2, 1);
+ isp_video_dmaqueue_flags_clr(video_out);
+ break;
+
+ case ISP_PIPELINE_STREAM_STOPPED:
+ if (csi2->state == ISP_PIPELINE_STREAM_STOPPED)
+ return 0;
+ if (omap3isp_module_sync_idle(&sd->entity, &csi2->wait,
+ &csi2->stopping))
+ dev_dbg(isp->dev, "%s: module stop timeout.\n",
+ sd->name);
+ csi2_ctx_enable(isp, csi2, 0, 0);
+ csi2_if_enable(isp, csi2, 0);
+ csi2_irq_ctx_set(isp, csi2, 0);
+ omap3isp_csiphy_release(csi2->phy);
+ isp_video_dmaqueue_flags_clr(video_out);
+ omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_CSI2A_WRITE);
+ break;
+ }
+
+ csi2->state = enable;
+ return 0;
+}
+
+/* subdev video operations */
+static const struct v4l2_subdev_video_ops csi2_video_ops = {
+ .s_stream = csi2_set_stream,
+};
+
+/* subdev pad operations */
+static const struct v4l2_subdev_pad_ops csi2_pad_ops = {
+ .enum_mbus_code = csi2_enum_mbus_code,
+ .enum_frame_size = csi2_enum_frame_size,
+ .get_fmt = csi2_get_format,
+ .set_fmt = csi2_set_format,
+};
+
+/* subdev operations */
+static const struct v4l2_subdev_ops csi2_ops = {
+ .video = &csi2_video_ops,
+ .pad = &csi2_pad_ops,
+};
+
+/* subdev internal operations */
+static const struct v4l2_subdev_internal_ops csi2_internal_ops = {
+ .open = csi2_init_formats,
+};
+
+/* -----------------------------------------------------------------------------
+ * Media entity operations
+ */
+
+/*
+ * csi2_link_setup - Setup CSI2 connections.
+ * @entity : Pointer to media entity structure
+ * @local : Pointer to local pad array
+ * @remote : Pointer to remote pad array
+ * @flags : Link flags
+ * return -EINVAL or zero on success
+ */
+static int csi2_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+ struct isp_csi2_ctrl_cfg *ctrl = &csi2->ctrl;
+
+ /*
+ * The ISP core doesn't support pipelines with multiple video outputs.
+ * Revisit this when it will be implemented, and return -EBUSY for now.
+ */
+
+ switch (local->index | media_entity_type(remote->entity)) {
+ case CSI2_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (csi2->output & ~CSI2_OUTPUT_MEMORY)
+ return -EBUSY;
+ csi2->output |= CSI2_OUTPUT_MEMORY;
+ } else {
+ csi2->output &= ~CSI2_OUTPUT_MEMORY;
+ }
+ break;
+
+ case CSI2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (csi2->output & ~CSI2_OUTPUT_CCDC)
+ return -EBUSY;
+ csi2->output |= CSI2_OUTPUT_CCDC;
+ } else {
+ csi2->output &= ~CSI2_OUTPUT_CCDC;
+ }
+ break;
+
+ default:
+ /* Link from camera to CSI2 is fixed... */
+ return -EINVAL;
+ }
+
+ ctrl->vp_only_enable =
+ (csi2->output & CSI2_OUTPUT_MEMORY) ? false : true;
+ ctrl->vp_clk_enable = !!(csi2->output & CSI2_OUTPUT_CCDC);
+
+ return 0;
+}
+
+/* media operations */
+static const struct media_entity_operations csi2_media_ops = {
+ .link_setup = csi2_link_setup,
+};
+
+void omap3isp_csi2_unregister_entities(struct isp_csi2_device *csi2)
+{
+ v4l2_device_unregister_subdev(&csi2->subdev);
+ omap3isp_video_unregister(&csi2->video_out);
+}
+
+int omap3isp_csi2_register_entities(struct isp_csi2_device *csi2,
+ struct v4l2_device *vdev)
+{
+ int ret;
+
+ /* Register the subdev and video nodes. */
+ ret = v4l2_device_register_subdev(vdev, &csi2->subdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap3isp_video_register(&csi2->video_out, vdev);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ omap3isp_csi2_unregister_entities(csi2);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP CSI2 initialisation and cleanup
+ */
+
+/*
+ * csi2_init_entities - Initialize subdev and media entity.
+ * @csi2: Pointer to csi2 structure.
+ * return -ENOMEM or zero on success
+ */
+static int csi2_init_entities(struct isp_csi2_device *csi2)
+{
+ struct v4l2_subdev *sd = &csi2->subdev;
+ struct media_pad *pads = csi2->pads;
+ struct media_entity *me = &sd->entity;
+ int ret;
+
+ v4l2_subdev_init(sd, &csi2_ops);
+ sd->internal_ops = &csi2_internal_ops;
+ strlcpy(sd->name, "OMAP3 ISP CSI2a", sizeof(sd->name));
+
+ sd->grp_id = 1 << 16; /* group ID for isp subdevs */
+ v4l2_set_subdevdata(sd, csi2);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ pads[CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+ pads[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+
+ me->ops = &csi2_media_ops;
+ ret = media_entity_init(me, CSI2_PADS_NUM, pads, 0);
+ if (ret < 0)
+ return ret;
+
+ csi2_init_formats(sd, NULL);
+
+ /* Video device node */
+ csi2->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ csi2->video_out.ops = &csi2_ispvideo_ops;
+ csi2->video_out.bpl_alignment = 32;
+ csi2->video_out.bpl_zero_padding = 1;
+ csi2->video_out.bpl_max = 0x1ffe0;
+ csi2->video_out.isp = csi2->isp;
+ csi2->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3;
+
+ ret = omap3isp_video_init(&csi2->video_out, "CSI2a");
+ if (ret < 0)
+ goto error_video;
+
+ /* Connect the CSI2 subdev to the video node. */
+ ret = media_entity_create_link(&csi2->subdev.entity, CSI2_PAD_SOURCE,
+ &csi2->video_out.video.entity, 0, 0);
+ if (ret < 0)
+ goto error_link;
+
+ return 0;
+
+error_link:
+ omap3isp_video_cleanup(&csi2->video_out);
+error_video:
+ media_entity_cleanup(&csi2->subdev.entity);
+ return ret;
+}
+
+/*
+ * omap3isp_csi2_init - Routine for module driver init
+ */
+int omap3isp_csi2_init(struct isp_device *isp)
+{
+ struct isp_csi2_device *csi2a = &isp->isp_csi2a;
+ struct isp_csi2_device *csi2c = &isp->isp_csi2c;
+ int ret;
+
+ csi2a->isp = isp;
+ csi2a->available = 1;
+ csi2a->regs1 = OMAP3_ISP_IOMEM_CSI2A_REGS1;
+ csi2a->regs2 = OMAP3_ISP_IOMEM_CSI2A_REGS2;
+ csi2a->phy = &isp->isp_csiphy2;
+ csi2a->state = ISP_PIPELINE_STREAM_STOPPED;
+ init_waitqueue_head(&csi2a->wait);
+
+ ret = csi2_init_entities(csi2a);
+ if (ret < 0)
+ return ret;
+
+ if (isp->revision == ISP_REVISION_15_0) {
+ csi2c->isp = isp;
+ csi2c->available = 1;
+ csi2c->regs1 = OMAP3_ISP_IOMEM_CSI2C_REGS1;
+ csi2c->regs2 = OMAP3_ISP_IOMEM_CSI2C_REGS2;
+ csi2c->phy = &isp->isp_csiphy1;
+ csi2c->state = ISP_PIPELINE_STREAM_STOPPED;
+ init_waitqueue_head(&csi2c->wait);
+ }
+
+ return 0;
+}
+
+/*
+ * omap3isp_csi2_cleanup - Routine for module driver cleanup
+ */
+void omap3isp_csi2_cleanup(struct isp_device *isp)
+{
+ struct isp_csi2_device *csi2a = &isp->isp_csi2a;
+
+ omap3isp_video_cleanup(&csi2a->video_out);
+ media_entity_cleanup(&csi2a->subdev.entity);
+}
diff --git a/drivers/media/video/omap3isp/ispcsi2.h b/drivers/media/video/omap3isp/ispcsi2.h
new file mode 100644
index 00000000..885ad79a
--- /dev/null
+++ b/drivers/media/video/omap3isp/ispcsi2.h
@@ -0,0 +1,166 @@
+/*
+ * ispcsi2.h
+ *
+ * TI OMAP3 ISP - CSI2 module
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP3_ISP_CSI2_H
+#define OMAP3_ISP_CSI2_H
+
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+struct isp_csiphy;
+
+/* This is not an exhaustive list */
+enum isp_csi2_pix_formats {
+ CSI2_PIX_FMT_OTHERS = 0,
+ CSI2_PIX_FMT_YUV422_8BIT = 0x1e,
+ CSI2_PIX_FMT_YUV422_8BIT_VP = 0x9e,
+ CSI2_PIX_FMT_RAW10_EXP16 = 0xab,
+ CSI2_PIX_FMT_RAW10_EXP16_VP = 0x12f,
+ CSI2_PIX_FMT_RAW8 = 0x2a,
+ CSI2_PIX_FMT_RAW8_DPCM10_EXP16 = 0x2aa,
+ CSI2_PIX_FMT_RAW8_DPCM10_VP = 0x32a,
+ CSI2_PIX_FMT_RAW8_VP = 0x12a,
+ CSI2_USERDEF_8BIT_DATA1_DPCM10_VP = 0x340,
+ CSI2_USERDEF_8BIT_DATA1_DPCM10 = 0x2c0,
+ CSI2_USERDEF_8BIT_DATA1 = 0x40,
+};
+
+enum isp_csi2_irqevents {
+ OCP_ERR_IRQ = 0x4000,
+ SHORT_PACKET_IRQ = 0x2000,
+ ECC_CORRECTION_IRQ = 0x1000,
+ ECC_NO_CORRECTION_IRQ = 0x800,
+ COMPLEXIO2_ERR_IRQ = 0x400,
+ COMPLEXIO1_ERR_IRQ = 0x200,
+ FIFO_OVF_IRQ = 0x100,
+ CONTEXT7 = 0x80,
+ CONTEXT6 = 0x40,
+ CONTEXT5 = 0x20,
+ CONTEXT4 = 0x10,
+ CONTEXT3 = 0x8,
+ CONTEXT2 = 0x4,
+ CONTEXT1 = 0x2,
+ CONTEXT0 = 0x1,
+};
+
+enum isp_csi2_ctx_irqevents {
+ CTX_ECC_CORRECTION = 0x100,
+ CTX_LINE_NUMBER = 0x80,
+ CTX_FRAME_NUMBER = 0x40,
+ CTX_CS = 0x20,
+ CTX_LE = 0x8,
+ CTX_LS = 0x4,
+ CTX_FE = 0x2,
+ CTX_FS = 0x1,
+};
+
+enum isp_csi2_frame_mode {
+ ISP_CSI2_FRAME_IMMEDIATE,
+ ISP_CSI2_FRAME_AFTERFEC,
+};
+
+#define ISP_CSI2_MAX_CTX_NUM 7
+
+struct isp_csi2_ctx_cfg {
+ u8 ctxnum; /* context number 0 - 7 */
+ u8 dpcm_decompress;
+
+ /* Fields in CSI2_CTx_CTRL2 - locked by CSI2_CTx_CTRL1.CTX_EN */
+ u8 virtual_id;
+ u16 format_id; /* as in CSI2_CTx_CTRL2[9:0] */
+ u8 dpcm_predictor; /* 1: simple, 0: advanced */
+
+ /* Fields in CSI2_CTx_CTRL1/3 - Shadowed */
+ u16 alpha;
+ u16 data_offset;
+ u32 ping_addr;
+ u32 pong_addr;
+ u8 eof_enabled;
+ u8 eol_enabled;
+ u8 checksum_enabled;
+ u8 enabled;
+};
+
+struct isp_csi2_timing_cfg {
+ u8 ionum; /* IO1 or IO2 as in CSI2_TIMING */
+ unsigned force_rx_mode:1;
+ unsigned stop_state_16x:1;
+ unsigned stop_state_4x:1;
+ u16 stop_state_counter;
+};
+
+struct isp_csi2_ctrl_cfg {
+ bool vp_clk_enable;
+ bool vp_only_enable;
+ u8 vp_out_ctrl;
+ enum isp_csi2_frame_mode frame_mode;
+ bool ecc_enable;
+ bool if_enable;
+};
+
+#define CSI2_PAD_SINK 0
+#define CSI2_PAD_SOURCE 1
+#define CSI2_PADS_NUM 2
+
+#define CSI2_OUTPUT_CCDC (1 << 0)
+#define CSI2_OUTPUT_MEMORY (1 << 1)
+
+struct isp_csi2_device {
+ struct v4l2_subdev subdev;
+ struct media_pad pads[CSI2_PADS_NUM];
+ struct v4l2_mbus_framefmt formats[CSI2_PADS_NUM];
+
+ struct isp_video video_out;
+ struct isp_device *isp;
+
+ u8 available; /* Is the IP present on the silicon? */
+
+ /* mem resources - enums as defined in enum isp_mem_resources */
+ u8 regs1;
+ u8 regs2;
+
+ u32 output; /* output to CCDC, memory or both? */
+ bool dpcm_decompress;
+ unsigned int frame_skip;
+ bool use_fs_irq;
+
+ struct isp_csiphy *phy;
+ struct isp_csi2_ctx_cfg contexts[ISP_CSI2_MAX_CTX_NUM + 1];
+ struct isp_csi2_timing_cfg timing[2];
+ struct isp_csi2_ctrl_cfg ctrl;
+ enum isp_pipeline_stream_state state;
+ wait_queue_head_t wait;
+ atomic_t stopping;
+};
+
+void omap3isp_csi2_isr(struct isp_csi2_device *csi2);
+int omap3isp_csi2_reset(struct isp_csi2_device *csi2);
+int omap3isp_csi2_init(struct isp_device *isp);
+void omap3isp_csi2_cleanup(struct isp_device *isp);
+void omap3isp_csi2_unregister_entities(struct isp_csi2_device *csi2);
+int omap3isp_csi2_register_entities(struct isp_csi2_device *csi2,
+ struct v4l2_device *vdev);
+#endif /* OMAP3_ISP_CSI2_H */
diff --git a/drivers/media/video/omap3isp/ispcsiphy.c b/drivers/media/video/omap3isp/ispcsiphy.c
new file mode 100644
index 00000000..5be37ce7
--- /dev/null
+++ b/drivers/media/video/omap3isp/ispcsiphy.c
@@ -0,0 +1,247 @@
+/*
+ * ispcsiphy.c
+ *
+ * TI OMAP3 ISP - CSI PHY module
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/regulator/consumer.h>
+
+#include "isp.h"
+#include "ispreg.h"
+#include "ispcsiphy.h"
+
+/*
+ * csiphy_lanes_config - Configuration of CSIPHY lanes.
+ *
+ * Updates HW configuration.
+ * Called with phy->mutex taken.
+ */
+static void csiphy_lanes_config(struct isp_csiphy *phy)
+{
+ unsigned int i;
+ u32 reg;
+
+ reg = isp_reg_readl(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG);
+
+ for (i = 0; i < phy->num_data_lanes; i++) {
+ reg &= ~(ISPCSI2_PHY_CFG_DATA_POL_MASK(i + 1) |
+ ISPCSI2_PHY_CFG_DATA_POSITION_MASK(i + 1));
+ reg |= (phy->lanes.data[i].pol <<
+ ISPCSI2_PHY_CFG_DATA_POL_SHIFT(i + 1));
+ reg |= (phy->lanes.data[i].pos <<
+ ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(i + 1));
+ }
+
+ reg &= ~(ISPCSI2_PHY_CFG_CLOCK_POL_MASK |
+ ISPCSI2_PHY_CFG_CLOCK_POSITION_MASK);
+ reg |= phy->lanes.clk.pol << ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT;
+ reg |= phy->lanes.clk.pos << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT;
+
+ isp_reg_writel(phy->isp, reg, phy->cfg_regs, ISPCSI2_PHY_CFG);
+}
+
+/*
+ * csiphy_power_autoswitch_enable
+ * @enable: Sets or clears the autoswitch function enable flag.
+ */
+static void csiphy_power_autoswitch_enable(struct isp_csiphy *phy, bool enable)
+{
+ isp_reg_clr_set(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG,
+ ISPCSI2_PHY_CFG_PWR_AUTO,
+ enable ? ISPCSI2_PHY_CFG_PWR_AUTO : 0);
+}
+
+/*
+ * csiphy_set_power
+ * @power: Power state to be set.
+ *
+ * Returns 0 if successful, or -EBUSY if the retry count is exceeded.
+ */
+static int csiphy_set_power(struct isp_csiphy *phy, u32 power)
+{
+ u32 reg;
+ u8 retry_count;
+
+ isp_reg_clr_set(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG,
+ ISPCSI2_PHY_CFG_PWR_CMD_MASK, power);
+
+ retry_count = 0;
+ do {
+ udelay(50);
+ reg = isp_reg_readl(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG) &
+ ISPCSI2_PHY_CFG_PWR_STATUS_MASK;
+
+ if (reg != power >> 2)
+ retry_count++;
+
+ } while ((reg != power >> 2) && (retry_count < 100));
+
+ if (retry_count == 100) {
+ printk(KERN_ERR "CSI2 CIO set power failed!\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+/*
+ * csiphy_dphy_config - Configure CSI2 D-PHY parameters.
+ *
+ * Called with phy->mutex taken.
+ */
+static void csiphy_dphy_config(struct isp_csiphy *phy)
+{
+ u32 reg;
+
+ /* Set up ISPCSIPHY_REG0 */
+ reg = isp_reg_readl(phy->isp, phy->phy_regs, ISPCSIPHY_REG0);
+
+ reg &= ~(ISPCSIPHY_REG0_THS_TERM_MASK |
+ ISPCSIPHY_REG0_THS_SETTLE_MASK);
+ reg |= phy->dphy.ths_term << ISPCSIPHY_REG0_THS_TERM_SHIFT;
+ reg |= phy->dphy.ths_settle << ISPCSIPHY_REG0_THS_SETTLE_SHIFT;
+
+ isp_reg_writel(phy->isp, reg, phy->phy_regs, ISPCSIPHY_REG0);
+
+ /* Set up ISPCSIPHY_REG1 */
+ reg = isp_reg_readl(phy->isp, phy->phy_regs, ISPCSIPHY_REG1);
+
+ reg &= ~(ISPCSIPHY_REG1_TCLK_TERM_MASK |
+ ISPCSIPHY_REG1_TCLK_MISS_MASK |
+ ISPCSIPHY_REG1_TCLK_SETTLE_MASK);
+ reg |= phy->dphy.tclk_term << ISPCSIPHY_REG1_TCLK_TERM_SHIFT;
+ reg |= phy->dphy.tclk_miss << ISPCSIPHY_REG1_TCLK_MISS_SHIFT;
+ reg |= phy->dphy.tclk_settle << ISPCSIPHY_REG1_TCLK_SETTLE_SHIFT;
+
+ isp_reg_writel(phy->isp, reg, phy->phy_regs, ISPCSIPHY_REG1);
+}
+
+static int csiphy_config(struct isp_csiphy *phy,
+ struct isp_csiphy_dphy_cfg *dphy,
+ struct isp_csiphy_lanes_cfg *lanes)
+{
+ unsigned int used_lanes = 0;
+ unsigned int i;
+
+ /* Clock and data lanes verification */
+ for (i = 0; i < phy->num_data_lanes; i++) {
+ if (lanes->data[i].pol > 1 || lanes->data[i].pos > 3)
+ return -EINVAL;
+
+ if (used_lanes & (1 << lanes->data[i].pos))
+ return -EINVAL;
+
+ used_lanes |= 1 << lanes->data[i].pos;
+ }
+
+ if (lanes->clk.pol > 1 || lanes->clk.pos > 3)
+ return -EINVAL;
+
+ if (lanes->clk.pos == 0 || used_lanes & (1 << lanes->clk.pos))
+ return -EINVAL;
+
+ mutex_lock(&phy->mutex);
+ phy->dphy = *dphy;
+ phy->lanes = *lanes;
+ mutex_unlock(&phy->mutex);
+
+ return 0;
+}
+
+int omap3isp_csiphy_acquire(struct isp_csiphy *phy)
+{
+ int rval;
+
+ if (phy->vdd == NULL) {
+ dev_err(phy->isp->dev, "Power regulator for CSI PHY not "
+ "available\n");
+ return -ENODEV;
+ }
+
+ mutex_lock(&phy->mutex);
+
+ rval = regulator_enable(phy->vdd);
+ if (rval < 0)
+ goto done;
+
+ omap3isp_csi2_reset(phy->csi2);
+
+ csiphy_dphy_config(phy);
+ csiphy_lanes_config(phy);
+
+ rval = csiphy_set_power(phy, ISPCSI2_PHY_CFG_PWR_CMD_ON);
+ if (rval) {
+ regulator_disable(phy->vdd);
+ goto done;
+ }
+
+ csiphy_power_autoswitch_enable(phy, true);
+ phy->phy_in_use = 1;
+
+done:
+ mutex_unlock(&phy->mutex);
+ return rval;
+}
+
+void omap3isp_csiphy_release(struct isp_csiphy *phy)
+{
+ mutex_lock(&phy->mutex);
+ if (phy->phy_in_use) {
+ csiphy_power_autoswitch_enable(phy, false);
+ csiphy_set_power(phy, ISPCSI2_PHY_CFG_PWR_CMD_OFF);
+ regulator_disable(phy->vdd);
+ phy->phy_in_use = 0;
+ }
+ mutex_unlock(&phy->mutex);
+}
+
+/*
+ * omap3isp_csiphy_init - Initialize the CSI PHY frontends
+ */
+int omap3isp_csiphy_init(struct isp_device *isp)
+{
+ struct isp_csiphy *phy1 = &isp->isp_csiphy1;
+ struct isp_csiphy *phy2 = &isp->isp_csiphy2;
+
+ isp->platform_cb.csiphy_config = csiphy_config;
+
+ phy2->isp = isp;
+ phy2->csi2 = &isp->isp_csi2a;
+ phy2->num_data_lanes = ISP_CSIPHY2_NUM_DATA_LANES;
+ phy2->cfg_regs = OMAP3_ISP_IOMEM_CSI2A_REGS1;
+ phy2->phy_regs = OMAP3_ISP_IOMEM_CSIPHY2;
+ mutex_init(&phy2->mutex);
+
+ if (isp->revision == ISP_REVISION_15_0) {
+ phy1->isp = isp;
+ phy1->csi2 = &isp->isp_csi2c;
+ phy1->num_data_lanes = ISP_CSIPHY1_NUM_DATA_LANES;
+ phy1->cfg_regs = OMAP3_ISP_IOMEM_CSI2C_REGS1;
+ phy1->phy_regs = OMAP3_ISP_IOMEM_CSIPHY1;
+ mutex_init(&phy1->mutex);
+ }
+
+ return 0;
+}
diff --git a/drivers/media/video/omap3isp/ispcsiphy.h b/drivers/media/video/omap3isp/ispcsiphy.h
new file mode 100644
index 00000000..9596dc68
--- /dev/null
+++ b/drivers/media/video/omap3isp/ispcsiphy.h
@@ -0,0 +1,74 @@
+/*
+ * ispcsiphy.h
+ *
+ * TI OMAP3 ISP - CSI PHY module
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP3_ISP_CSI_PHY_H
+#define OMAP3_ISP_CSI_PHY_H
+
+struct isp_csi2_device;
+struct regulator;
+
+struct csiphy_lane {
+ u8 pos;
+ u8 pol;
+};
+
+#define ISP_CSIPHY2_NUM_DATA_LANES 2
+#define ISP_CSIPHY1_NUM_DATA_LANES 1
+
+struct isp_csiphy_lanes_cfg {
+ struct csiphy_lane data[ISP_CSIPHY2_NUM_DATA_LANES];
+ struct csiphy_lane clk;
+};
+
+struct isp_csiphy_dphy_cfg {
+ u8 ths_term;
+ u8 ths_settle;
+ u8 tclk_term;
+ unsigned tclk_miss:1;
+ u8 tclk_settle;
+};
+
+struct isp_csiphy {
+ struct isp_device *isp;
+ struct mutex mutex; /* serialize csiphy configuration */
+ u8 phy_in_use;
+ struct isp_csi2_device *csi2;
+ struct regulator *vdd;
+
+ /* mem resources - enums as defined in enum isp_mem_resources */
+ unsigned int cfg_regs;
+ unsigned int phy_regs;
+
+ u8 num_data_lanes; /* number of CSI2 Data Lanes supported */
+ struct isp_csiphy_lanes_cfg lanes;
+ struct isp_csiphy_dphy_cfg dphy;
+};
+
+int omap3isp_csiphy_acquire(struct isp_csiphy *phy);
+void omap3isp_csiphy_release(struct isp_csiphy *phy);
+int omap3isp_csiphy_init(struct isp_device *isp);
+
+#endif /* OMAP3_ISP_CSI_PHY_H */
diff --git a/drivers/media/video/omap3isp/isph3a.h b/drivers/media/video/omap3isp/isph3a.h
new file mode 100644
index 00000000..fb09fd4c
--- /dev/null
+++ b/drivers/media/video/omap3isp/isph3a.h
@@ -0,0 +1,117 @@
+/*
+ * isph3a.h
+ *
+ * TI OMAP3 ISP - H3A AF module
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Contacts: David Cohen <dacohen@gmail.com>
+ * Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP3_ISP_H3A_H
+#define OMAP3_ISP_H3A_H
+
+#include <linux/omap3isp.h>
+
+/*
+ * ----------
+ * -H3A AEWB-
+ * ----------
+ */
+
+#define AEWB_PACKET_SIZE 16
+#define AEWB_SATURATION_LIMIT 0x3ff
+
+/* Flags for changed registers */
+#define PCR_CHNG (1 << 0)
+#define AEWWIN1_CHNG (1 << 1)
+#define AEWINSTART_CHNG (1 << 2)
+#define AEWINBLK_CHNG (1 << 3)
+#define AEWSUBWIN_CHNG (1 << 4)
+#define PRV_WBDGAIN_CHNG (1 << 5)
+#define PRV_WBGAIN_CHNG (1 << 6)
+
+/* ISPH3A REGISTERS bits */
+#define ISPH3A_PCR_AF_EN (1 << 0)
+#define ISPH3A_PCR_AF_ALAW_EN (1 << 1)
+#define ISPH3A_PCR_AF_MED_EN (1 << 2)
+#define ISPH3A_PCR_AF_BUSY (1 << 15)
+#define ISPH3A_PCR_AEW_EN (1 << 16)
+#define ISPH3A_PCR_AEW_ALAW_EN (1 << 17)
+#define ISPH3A_PCR_AEW_BUSY (1 << 18)
+#define ISPH3A_PCR_AEW_MASK (ISPH3A_PCR_AEW_ALAW_EN | \
+ ISPH3A_PCR_AEW_AVE2LMT_MASK)
+
+/*
+ * --------
+ * -H3A AF-
+ * --------
+ */
+
+/* Peripheral Revision */
+#define AFPID 0x0
+
+#define AFCOEF_OFFSET 0x00000004 /* COEF base address */
+
+/* PCR fields */
+#define AF_BUSYAF (1 << 15)
+#define AF_FVMODE (1 << 14)
+#define AF_RGBPOS (0x7 << 11)
+#define AF_MED_TH (0xFF << 3)
+#define AF_MED_EN (1 << 2)
+#define AF_ALAW_EN (1 << 1)
+#define AF_EN (1 << 0)
+#define AF_PCR_MASK (AF_FVMODE | AF_RGBPOS | AF_MED_TH | \
+ AF_MED_EN | AF_ALAW_EN)
+
+/* AFPAX1 fields */
+#define AF_PAXW (0x7F << 16)
+#define AF_PAXH 0x7F
+
+/* AFPAX2 fields */
+#define AF_AFINCV (0xF << 13)
+#define AF_PAXVC (0x7F << 6)
+#define AF_PAXHC 0x3F
+
+/* AFPAXSTART fields */
+#define AF_PAXSH (0xFFF<<16)
+#define AF_PAXSV 0xFFF
+
+/* COEFFICIENT MASK */
+#define AF_COEF_MASK0 0xFFF
+#define AF_COEF_MASK1 (0xFFF<<16)
+
+/* BIT SHIFTS */
+#define AF_RGBPOS_SHIFT 11
+#define AF_MED_TH_SHIFT 3
+#define AF_PAXW_SHIFT 16
+#define AF_LINE_INCR_SHIFT 13
+#define AF_VT_COUNT_SHIFT 6
+#define AF_HZ_START_SHIFT 16
+#define AF_COEF_SHIFT 16
+
+/* Init and cleanup functions */
+int omap3isp_h3a_aewb_init(struct isp_device *isp);
+int omap3isp_h3a_af_init(struct isp_device *isp);
+
+void omap3isp_h3a_aewb_cleanup(struct isp_device *isp);
+void omap3isp_h3a_af_cleanup(struct isp_device *isp);
+
+#endif /* OMAP3_ISP_H3A_H */
diff --git a/drivers/media/video/omap3isp/isph3a_aewb.c b/drivers/media/video/omap3isp/isph3a_aewb.c
new file mode 100644
index 00000000..a3c76bf1
--- /dev/null
+++ b/drivers/media/video/omap3isp/isph3a_aewb.c
@@ -0,0 +1,374 @@
+/*
+ * isph3a.c
+ *
+ * TI OMAP3 ISP - H3A module
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Contacts: David Cohen <dacohen@gmail.com>
+ * Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include "isp.h"
+#include "isph3a.h"
+#include "ispstat.h"
+
+/*
+ * h3a_aewb_update_regs - Helper function to update h3a registers.
+ */
+static void h3a_aewb_setup_regs(struct ispstat *aewb, void *priv)
+{
+ struct omap3isp_h3a_aewb_config *conf = priv;
+ u32 pcr;
+ u32 win1;
+ u32 start;
+ u32 blk;
+ u32 subwin;
+
+ if (aewb->state == ISPSTAT_DISABLED)
+ return;
+
+ isp_reg_writel(aewb->isp, aewb->active_buf->iommu_addr,
+ OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWBUFST);
+
+ if (!aewb->update)
+ return;
+
+ /* Converting config metadata into reg values */
+ pcr = conf->saturation_limit << ISPH3A_PCR_AEW_AVE2LMT_SHIFT;
+ pcr |= !!conf->alaw_enable << ISPH3A_PCR_AEW_ALAW_EN_SHIFT;
+
+ win1 = ((conf->win_height >> 1) - 1) << ISPH3A_AEWWIN1_WINH_SHIFT;
+ win1 |= ((conf->win_width >> 1) - 1) << ISPH3A_AEWWIN1_WINW_SHIFT;
+ win1 |= (conf->ver_win_count - 1) << ISPH3A_AEWWIN1_WINVC_SHIFT;
+ win1 |= (conf->hor_win_count - 1) << ISPH3A_AEWWIN1_WINHC_SHIFT;
+
+ start = conf->hor_win_start << ISPH3A_AEWINSTART_WINSH_SHIFT;
+ start |= conf->ver_win_start << ISPH3A_AEWINSTART_WINSV_SHIFT;
+
+ blk = conf->blk_ver_win_start << ISPH3A_AEWINBLK_WINSV_SHIFT;
+ blk |= ((conf->blk_win_height >> 1) - 1) << ISPH3A_AEWINBLK_WINH_SHIFT;
+
+ subwin = ((conf->subsample_ver_inc >> 1) - 1) <<
+ ISPH3A_AEWSUBWIN_AEWINCV_SHIFT;
+ subwin |= ((conf->subsample_hor_inc >> 1) - 1) <<
+ ISPH3A_AEWSUBWIN_AEWINCH_SHIFT;
+
+ isp_reg_writel(aewb->isp, win1, OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWWIN1);
+ isp_reg_writel(aewb->isp, start, OMAP3_ISP_IOMEM_H3A,
+ ISPH3A_AEWINSTART);
+ isp_reg_writel(aewb->isp, blk, OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWINBLK);
+ isp_reg_writel(aewb->isp, subwin, OMAP3_ISP_IOMEM_H3A,
+ ISPH3A_AEWSUBWIN);
+ isp_reg_clr_set(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR,
+ ISPH3A_PCR_AEW_MASK, pcr);
+
+ aewb->update = 0;
+ aewb->config_counter += aewb->inc_config;
+ aewb->inc_config = 0;
+ aewb->buf_size = conf->buf_size;
+}
+
+static void h3a_aewb_enable(struct ispstat *aewb, int enable)
+{
+ if (enable) {
+ isp_reg_set(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR,
+ ISPH3A_PCR_AEW_EN);
+ /* This bit is already set if AF is enabled */
+ if (aewb->isp->isp_af.state != ISPSTAT_ENABLED)
+ isp_reg_set(aewb->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL,
+ ISPCTRL_H3A_CLK_EN);
+ } else {
+ isp_reg_clr(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR,
+ ISPH3A_PCR_AEW_EN);
+ /* This bit can't be cleared if AF is enabled */
+ if (aewb->isp->isp_af.state != ISPSTAT_ENABLED)
+ isp_reg_clr(aewb->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL,
+ ISPCTRL_H3A_CLK_EN);
+ }
+}
+
+static int h3a_aewb_busy(struct ispstat *aewb)
+{
+ return isp_reg_readl(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR)
+ & ISPH3A_PCR_BUSYAEAWB;
+}
+
+static u32 h3a_aewb_get_buf_size(struct omap3isp_h3a_aewb_config *conf)
+{
+ /* Number of configured windows + extra row for black data */
+ u32 win_count = (conf->ver_win_count + 1) * conf->hor_win_count;
+
+ /*
+ * Unsaturated block counts for each 8 windows.
+ * 1 extra for the last (win_count % 8) windows if win_count is not
+ * divisible by 8.
+ */
+ win_count += (win_count + 7) / 8;
+
+ return win_count * AEWB_PACKET_SIZE;
+}
+
+static int h3a_aewb_validate_params(struct ispstat *aewb, void *new_conf)
+{
+ struct omap3isp_h3a_aewb_config *user_cfg = new_conf;
+ u32 buf_size;
+
+ if (unlikely(user_cfg->saturation_limit >
+ OMAP3ISP_AEWB_MAX_SATURATION_LIM))
+ return -EINVAL;
+
+ if (unlikely(user_cfg->win_height < OMAP3ISP_AEWB_MIN_WIN_H ||
+ user_cfg->win_height > OMAP3ISP_AEWB_MAX_WIN_H ||
+ user_cfg->win_height & 0x01))
+ return -EINVAL;
+
+ if (unlikely(user_cfg->win_width < OMAP3ISP_AEWB_MIN_WIN_W ||
+ user_cfg->win_width > OMAP3ISP_AEWB_MAX_WIN_W ||
+ user_cfg->win_width & 0x01))
+ return -EINVAL;
+
+ if (unlikely(user_cfg->ver_win_count < OMAP3ISP_AEWB_MIN_WINVC ||
+ user_cfg->ver_win_count > OMAP3ISP_AEWB_MAX_WINVC))
+ return -EINVAL;
+
+ if (unlikely(user_cfg->hor_win_count < OMAP3ISP_AEWB_MIN_WINHC ||
+ user_cfg->hor_win_count > OMAP3ISP_AEWB_MAX_WINHC))
+ return -EINVAL;
+
+ if (unlikely(user_cfg->ver_win_start > OMAP3ISP_AEWB_MAX_WINSTART))
+ return -EINVAL;
+
+ if (unlikely(user_cfg->hor_win_start > OMAP3ISP_AEWB_MAX_WINSTART))
+ return -EINVAL;
+
+ if (unlikely(user_cfg->blk_ver_win_start > OMAP3ISP_AEWB_MAX_WINSTART))
+ return -EINVAL;
+
+ if (unlikely(user_cfg->blk_win_height < OMAP3ISP_AEWB_MIN_WIN_H ||
+ user_cfg->blk_win_height > OMAP3ISP_AEWB_MAX_WIN_H ||
+ user_cfg->blk_win_height & 0x01))
+ return -EINVAL;
+
+ if (unlikely(user_cfg->subsample_ver_inc < OMAP3ISP_AEWB_MIN_SUB_INC ||
+ user_cfg->subsample_ver_inc > OMAP3ISP_AEWB_MAX_SUB_INC ||
+ user_cfg->subsample_ver_inc & 0x01))
+ return -EINVAL;
+
+ if (unlikely(user_cfg->subsample_hor_inc < OMAP3ISP_AEWB_MIN_SUB_INC ||
+ user_cfg->subsample_hor_inc > OMAP3ISP_AEWB_MAX_SUB_INC ||
+ user_cfg->subsample_hor_inc & 0x01))
+ return -EINVAL;
+
+ buf_size = h3a_aewb_get_buf_size(user_cfg);
+ if (buf_size > user_cfg->buf_size)
+ user_cfg->buf_size = buf_size;
+ else if (user_cfg->buf_size > OMAP3ISP_AEWB_MAX_BUF_SIZE)
+ user_cfg->buf_size = OMAP3ISP_AEWB_MAX_BUF_SIZE;
+
+ return 0;
+}
+
+/*
+ * h3a_aewb_set_params - Helper function to check & store user given params.
+ * @new_conf: Pointer to AE and AWB parameters struct.
+ *
+ * As most of them are busy-lock registers, need to wait until AEW_BUSY = 0 to
+ * program them during ISR.
+ */
+static void h3a_aewb_set_params(struct ispstat *aewb, void *new_conf)
+{
+ struct omap3isp_h3a_aewb_config *user_cfg = new_conf;
+ struct omap3isp_h3a_aewb_config *cur_cfg = aewb->priv;
+ int update = 0;
+
+ if (cur_cfg->saturation_limit != user_cfg->saturation_limit) {
+ cur_cfg->saturation_limit = user_cfg->saturation_limit;
+ update = 1;
+ }
+ if (cur_cfg->alaw_enable != user_cfg->alaw_enable) {
+ cur_cfg->alaw_enable = user_cfg->alaw_enable;
+ update = 1;
+ }
+ if (cur_cfg->win_height != user_cfg->win_height) {
+ cur_cfg->win_height = user_cfg->win_height;
+ update = 1;
+ }
+ if (cur_cfg->win_width != user_cfg->win_width) {
+ cur_cfg->win_width = user_cfg->win_width;
+ update = 1;
+ }
+ if (cur_cfg->ver_win_count != user_cfg->ver_win_count) {
+ cur_cfg->ver_win_count = user_cfg->ver_win_count;
+ update = 1;
+ }
+ if (cur_cfg->hor_win_count != user_cfg->hor_win_count) {
+ cur_cfg->hor_win_count = user_cfg->hor_win_count;
+ update = 1;
+ }
+ if (cur_cfg->ver_win_start != user_cfg->ver_win_start) {
+ cur_cfg->ver_win_start = user_cfg->ver_win_start;
+ update = 1;
+ }
+ if (cur_cfg->hor_win_start != user_cfg->hor_win_start) {
+ cur_cfg->hor_win_start = user_cfg->hor_win_start;
+ update = 1;
+ }
+ if (cur_cfg->blk_ver_win_start != user_cfg->blk_ver_win_start) {
+ cur_cfg->blk_ver_win_start = user_cfg->blk_ver_win_start;
+ update = 1;
+ }
+ if (cur_cfg->blk_win_height != user_cfg->blk_win_height) {
+ cur_cfg->blk_win_height = user_cfg->blk_win_height;
+ update = 1;
+ }
+ if (cur_cfg->subsample_ver_inc != user_cfg->subsample_ver_inc) {
+ cur_cfg->subsample_ver_inc = user_cfg->subsample_ver_inc;
+ update = 1;
+ }
+ if (cur_cfg->subsample_hor_inc != user_cfg->subsample_hor_inc) {
+ cur_cfg->subsample_hor_inc = user_cfg->subsample_hor_inc;
+ update = 1;
+ }
+
+ if (update || !aewb->configured) {
+ aewb->inc_config++;
+ aewb->update = 1;
+ cur_cfg->buf_size = h3a_aewb_get_buf_size(cur_cfg);
+ }
+}
+
+static long h3a_aewb_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+ struct ispstat *stat = v4l2_get_subdevdata(sd);
+
+ switch (cmd) {
+ case VIDIOC_OMAP3ISP_AEWB_CFG:
+ return omap3isp_stat_config(stat, arg);
+ case VIDIOC_OMAP3ISP_STAT_REQ:
+ return omap3isp_stat_request_statistics(stat, arg);
+ case VIDIOC_OMAP3ISP_STAT_EN: {
+ unsigned long *en = arg;
+ return omap3isp_stat_enable(stat, !!*en);
+ }
+ }
+
+ return -ENOIOCTLCMD;
+}
+
+static const struct ispstat_ops h3a_aewb_ops = {
+ .validate_params = h3a_aewb_validate_params,
+ .set_params = h3a_aewb_set_params,
+ .setup_regs = h3a_aewb_setup_regs,
+ .enable = h3a_aewb_enable,
+ .busy = h3a_aewb_busy,
+};
+
+static const struct v4l2_subdev_core_ops h3a_aewb_subdev_core_ops = {
+ .ioctl = h3a_aewb_ioctl,
+ .subscribe_event = omap3isp_stat_subscribe_event,
+ .unsubscribe_event = omap3isp_stat_unsubscribe_event,
+};
+
+static const struct v4l2_subdev_video_ops h3a_aewb_subdev_video_ops = {
+ .s_stream = omap3isp_stat_s_stream,
+};
+
+static const struct v4l2_subdev_ops h3a_aewb_subdev_ops = {
+ .core = &h3a_aewb_subdev_core_ops,
+ .video = &h3a_aewb_subdev_video_ops,
+};
+
+/*
+ * omap3isp_h3a_aewb_init - Module Initialisation.
+ */
+int omap3isp_h3a_aewb_init(struct isp_device *isp)
+{
+ struct ispstat *aewb = &isp->isp_aewb;
+ struct omap3isp_h3a_aewb_config *aewb_cfg;
+ struct omap3isp_h3a_aewb_config *aewb_recover_cfg;
+ int ret;
+
+ aewb_cfg = kzalloc(sizeof(*aewb_cfg), GFP_KERNEL);
+ if (!aewb_cfg)
+ return -ENOMEM;
+
+ memset(aewb, 0, sizeof(*aewb));
+ aewb->ops = &h3a_aewb_ops;
+ aewb->priv = aewb_cfg;
+ aewb->dma_ch = -1;
+ aewb->event_type = V4L2_EVENT_OMAP3ISP_AEWB;
+ aewb->isp = isp;
+
+ /* Set recover state configuration */
+ aewb_recover_cfg = kzalloc(sizeof(*aewb_recover_cfg), GFP_KERNEL);
+ if (!aewb_recover_cfg) {
+ dev_err(aewb->isp->dev, "AEWB: cannot allocate memory for "
+ "recover configuration.\n");
+ ret = -ENOMEM;
+ goto err_recover_alloc;
+ }
+
+ aewb_recover_cfg->saturation_limit = OMAP3ISP_AEWB_MAX_SATURATION_LIM;
+ aewb_recover_cfg->win_height = OMAP3ISP_AEWB_MIN_WIN_H;
+ aewb_recover_cfg->win_width = OMAP3ISP_AEWB_MIN_WIN_W;
+ aewb_recover_cfg->ver_win_count = OMAP3ISP_AEWB_MIN_WINVC;
+ aewb_recover_cfg->hor_win_count = OMAP3ISP_AEWB_MIN_WINHC;
+ aewb_recover_cfg->blk_ver_win_start = aewb_recover_cfg->ver_win_start +
+ aewb_recover_cfg->win_height * aewb_recover_cfg->ver_win_count;
+ aewb_recover_cfg->blk_win_height = OMAP3ISP_AEWB_MIN_WIN_H;
+ aewb_recover_cfg->subsample_ver_inc = OMAP3ISP_AEWB_MIN_SUB_INC;
+ aewb_recover_cfg->subsample_hor_inc = OMAP3ISP_AEWB_MIN_SUB_INC;
+
+ if (h3a_aewb_validate_params(aewb, aewb_recover_cfg)) {
+ dev_err(aewb->isp->dev, "AEWB: recover configuration is "
+ "invalid.\n");
+ ret = -EINVAL;
+ goto err_conf;
+ }
+
+ aewb_recover_cfg->buf_size = h3a_aewb_get_buf_size(aewb_recover_cfg);
+ aewb->recover_priv = aewb_recover_cfg;
+
+ ret = omap3isp_stat_init(aewb, "AEWB", &h3a_aewb_subdev_ops);
+ if (ret)
+ goto err_conf;
+
+ return 0;
+
+err_conf:
+ kfree(aewb_recover_cfg);
+err_recover_alloc:
+ kfree(aewb_cfg);
+
+ return ret;
+}
+
+/*
+ * omap3isp_h3a_aewb_cleanup - Module exit.
+ */
+void omap3isp_h3a_aewb_cleanup(struct isp_device *isp)
+{
+ kfree(isp->isp_aewb.priv);
+ kfree(isp->isp_aewb.recover_priv);
+ omap3isp_stat_cleanup(&isp->isp_aewb);
+}
diff --git a/drivers/media/video/omap3isp/isph3a_af.c b/drivers/media/video/omap3isp/isph3a_af.c
new file mode 100644
index 00000000..58e0bc41
--- /dev/null
+++ b/drivers/media/video/omap3isp/isph3a_af.c
@@ -0,0 +1,429 @@
+/*
+ * isph3a_af.c
+ *
+ * TI OMAP3 ISP - H3A AF module
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Contacts: David Cohen <dacohen@gmail.com>
+ * Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+/* Linux specific include files */
+#include <linux/device.h>
+#include <linux/slab.h>
+
+#include "isp.h"
+#include "isph3a.h"
+#include "ispstat.h"
+
+#define IS_OUT_OF_BOUNDS(value, min, max) \
+ (((value) < (min)) || ((value) > (max)))
+
+static void h3a_af_setup_regs(struct ispstat *af, void *priv)
+{
+ struct omap3isp_h3a_af_config *conf = priv;
+ u32 pcr;
+ u32 pax1;
+ u32 pax2;
+ u32 paxstart;
+ u32 coef;
+ u32 base_coef_set0;
+ u32 base_coef_set1;
+ int index;
+
+ if (af->state == ISPSTAT_DISABLED)
+ return;
+
+ isp_reg_writel(af->isp, af->active_buf->iommu_addr, OMAP3_ISP_IOMEM_H3A,
+ ISPH3A_AFBUFST);
+
+ if (!af->update)
+ return;
+
+ /* Configure Hardware Registers */
+ pax1 = ((conf->paxel.width >> 1) - 1) << AF_PAXW_SHIFT;
+ /* Set height in AFPAX1 */
+ pax1 |= (conf->paxel.height >> 1) - 1;
+ isp_reg_writel(af->isp, pax1, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX1);
+
+ /* Configure AFPAX2 Register */
+ /* Set Line Increment in AFPAX2 Register */
+ pax2 = ((conf->paxel.line_inc >> 1) - 1) << AF_LINE_INCR_SHIFT;
+ /* Set Vertical Count */
+ pax2 |= (conf->paxel.v_cnt - 1) << AF_VT_COUNT_SHIFT;
+ /* Set Horizontal Count */
+ pax2 |= (conf->paxel.h_cnt - 1);
+ isp_reg_writel(af->isp, pax2, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX2);
+
+ /* Configure PAXSTART Register */
+ /*Configure Horizontal Start */
+ paxstart = conf->paxel.h_start << AF_HZ_START_SHIFT;
+ /* Configure Vertical Start */
+ paxstart |= conf->paxel.v_start;
+ isp_reg_writel(af->isp, paxstart, OMAP3_ISP_IOMEM_H3A,
+ ISPH3A_AFPAXSTART);
+
+ /*SetIIRSH Register */
+ isp_reg_writel(af->isp, conf->iir.h_start,
+ OMAP3_ISP_IOMEM_H3A, ISPH3A_AFIIRSH);
+
+ base_coef_set0 = ISPH3A_AFCOEF010;
+ base_coef_set1 = ISPH3A_AFCOEF110;
+ for (index = 0; index <= 8; index += 2) {
+ /*Set IIR Filter0 Coefficients */
+ coef = 0;
+ coef |= conf->iir.coeff_set0[index];
+ coef |= conf->iir.coeff_set0[index + 1] <<
+ AF_COEF_SHIFT;
+ isp_reg_writel(af->isp, coef, OMAP3_ISP_IOMEM_H3A,
+ base_coef_set0);
+ base_coef_set0 += AFCOEF_OFFSET;
+
+ /*Set IIR Filter1 Coefficients */
+ coef = 0;
+ coef |= conf->iir.coeff_set1[index];
+ coef |= conf->iir.coeff_set1[index + 1] <<
+ AF_COEF_SHIFT;
+ isp_reg_writel(af->isp, coef, OMAP3_ISP_IOMEM_H3A,
+ base_coef_set1);
+ base_coef_set1 += AFCOEF_OFFSET;
+ }
+ /* set AFCOEF0010 Register */
+ isp_reg_writel(af->isp, conf->iir.coeff_set0[10],
+ OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF0010);
+ /* set AFCOEF1010 Register */
+ isp_reg_writel(af->isp, conf->iir.coeff_set1[10],
+ OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF1010);
+
+ /* PCR Register */
+ /* Set RGB Position */
+ pcr = conf->rgb_pos << AF_RGBPOS_SHIFT;
+ /* Set Accumulator Mode */
+ if (conf->fvmode == OMAP3ISP_AF_MODE_PEAK)
+ pcr |= AF_FVMODE;
+ /* Set A-law */
+ if (conf->alaw_enable)
+ pcr |= AF_ALAW_EN;
+ /* HMF Configurations */
+ if (conf->hmf.enable) {
+ /* Enable HMF */
+ pcr |= AF_MED_EN;
+ /* Set Median Threshold */
+ pcr |= conf->hmf.threshold << AF_MED_TH_SHIFT;
+ }
+ /* Set PCR Register */
+ isp_reg_clr_set(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR,
+ AF_PCR_MASK, pcr);
+
+ af->update = 0;
+ af->config_counter += af->inc_config;
+ af->inc_config = 0;
+ af->buf_size = conf->buf_size;
+}
+
+static void h3a_af_enable(struct ispstat *af, int enable)
+{
+ if (enable) {
+ isp_reg_set(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR,
+ ISPH3A_PCR_AF_EN);
+ /* This bit is already set if AEWB is enabled */
+ if (af->isp->isp_aewb.state != ISPSTAT_ENABLED)
+ isp_reg_set(af->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL,
+ ISPCTRL_H3A_CLK_EN);
+ } else {
+ isp_reg_clr(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR,
+ ISPH3A_PCR_AF_EN);
+ /* This bit can't be cleared if AEWB is enabled */
+ if (af->isp->isp_aewb.state != ISPSTAT_ENABLED)
+ isp_reg_clr(af->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL,
+ ISPCTRL_H3A_CLK_EN);
+ }
+}
+
+static int h3a_af_busy(struct ispstat *af)
+{
+ return isp_reg_readl(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR)
+ & ISPH3A_PCR_BUSYAF;
+}
+
+static u32 h3a_af_get_buf_size(struct omap3isp_h3a_af_config *conf)
+{
+ return conf->paxel.h_cnt * conf->paxel.v_cnt * OMAP3ISP_AF_PAXEL_SIZE;
+}
+
+/* Function to check paxel parameters */
+static int h3a_af_validate_params(struct ispstat *af, void *new_conf)
+{
+ struct omap3isp_h3a_af_config *user_cfg = new_conf;
+ struct omap3isp_h3a_af_paxel *paxel_cfg = &user_cfg->paxel;
+ struct omap3isp_h3a_af_iir *iir_cfg = &user_cfg->iir;
+ int index;
+ u32 buf_size;
+
+ /* Check horizontal Count */
+ if (IS_OUT_OF_BOUNDS(paxel_cfg->h_cnt,
+ OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MIN,
+ OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MAX))
+ return -EINVAL;
+
+ /* Check Vertical Count */
+ if (IS_OUT_OF_BOUNDS(paxel_cfg->v_cnt,
+ OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MIN,
+ OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MAX))
+ return -EINVAL;
+
+ if (IS_OUT_OF_BOUNDS(paxel_cfg->height, OMAP3ISP_AF_PAXEL_HEIGHT_MIN,
+ OMAP3ISP_AF_PAXEL_HEIGHT_MAX) ||
+ paxel_cfg->height % 2)
+ return -EINVAL;
+
+ /* Check width */
+ if (IS_OUT_OF_BOUNDS(paxel_cfg->width, OMAP3ISP_AF_PAXEL_WIDTH_MIN,
+ OMAP3ISP_AF_PAXEL_WIDTH_MAX) ||
+ paxel_cfg->width % 2)
+ return -EINVAL;
+
+ /* Check Line Increment */
+ if (IS_OUT_OF_BOUNDS(paxel_cfg->line_inc,
+ OMAP3ISP_AF_PAXEL_INCREMENT_MIN,
+ OMAP3ISP_AF_PAXEL_INCREMENT_MAX) ||
+ paxel_cfg->line_inc % 2)
+ return -EINVAL;
+
+ /* Check Horizontal Start */
+ if ((paxel_cfg->h_start < iir_cfg->h_start) ||
+ IS_OUT_OF_BOUNDS(paxel_cfg->h_start,
+ OMAP3ISP_AF_PAXEL_HZSTART_MIN,
+ OMAP3ISP_AF_PAXEL_HZSTART_MAX))
+ return -EINVAL;
+
+ /* Check IIR */
+ for (index = 0; index < OMAP3ISP_AF_NUM_COEF; index++) {
+ if ((iir_cfg->coeff_set0[index]) > OMAP3ISP_AF_COEF_MAX)
+ return -EINVAL;
+
+ if ((iir_cfg->coeff_set1[index]) > OMAP3ISP_AF_COEF_MAX)
+ return -EINVAL;
+ }
+
+ if (IS_OUT_OF_BOUNDS(iir_cfg->h_start, OMAP3ISP_AF_IIRSH_MIN,
+ OMAP3ISP_AF_IIRSH_MAX))
+ return -EINVAL;
+
+ /* Hack: If paxel size is 12, the 10th AF window may be corrupted */
+ if ((paxel_cfg->h_cnt * paxel_cfg->v_cnt > 9) &&
+ (paxel_cfg->width * paxel_cfg->height == 12))
+ return -EINVAL;
+
+ buf_size = h3a_af_get_buf_size(user_cfg);
+ if (buf_size > user_cfg->buf_size)
+ /* User buf_size request wasn't enough */
+ user_cfg->buf_size = buf_size;
+ else if (user_cfg->buf_size > OMAP3ISP_AF_MAX_BUF_SIZE)
+ user_cfg->buf_size = OMAP3ISP_AF_MAX_BUF_SIZE;
+
+ return 0;
+}
+
+/* Update local parameters */
+static void h3a_af_set_params(struct ispstat *af, void *new_conf)
+{
+ struct omap3isp_h3a_af_config *user_cfg = new_conf;
+ struct omap3isp_h3a_af_config *cur_cfg = af->priv;
+ int update = 0;
+ int index;
+
+ /* alaw */
+ if (cur_cfg->alaw_enable != user_cfg->alaw_enable) {
+ update = 1;
+ goto out;
+ }
+
+ /* hmf */
+ if (cur_cfg->hmf.enable != user_cfg->hmf.enable) {
+ update = 1;
+ goto out;
+ }
+ if (cur_cfg->hmf.threshold != user_cfg->hmf.threshold) {
+ update = 1;
+ goto out;
+ }
+
+ /* rgbpos */
+ if (cur_cfg->rgb_pos != user_cfg->rgb_pos) {
+ update = 1;
+ goto out;
+ }
+
+ /* iir */
+ if (cur_cfg->iir.h_start != user_cfg->iir.h_start) {
+ update = 1;
+ goto out;
+ }
+ for (index = 0; index < OMAP3ISP_AF_NUM_COEF; index++) {
+ if (cur_cfg->iir.coeff_set0[index] !=
+ user_cfg->iir.coeff_set0[index]) {
+ update = 1;
+ goto out;
+ }
+ if (cur_cfg->iir.coeff_set1[index] !=
+ user_cfg->iir.coeff_set1[index]) {
+ update = 1;
+ goto out;
+ }
+ }
+
+ /* paxel */
+ if ((cur_cfg->paxel.width != user_cfg->paxel.width) ||
+ (cur_cfg->paxel.height != user_cfg->paxel.height) ||
+ (cur_cfg->paxel.h_start != user_cfg->paxel.h_start) ||
+ (cur_cfg->paxel.v_start != user_cfg->paxel.v_start) ||
+ (cur_cfg->paxel.h_cnt != user_cfg->paxel.h_cnt) ||
+ (cur_cfg->paxel.v_cnt != user_cfg->paxel.v_cnt) ||
+ (cur_cfg->paxel.line_inc != user_cfg->paxel.line_inc)) {
+ update = 1;
+ goto out;
+ }
+
+ /* af_mode */
+ if (cur_cfg->fvmode != user_cfg->fvmode)
+ update = 1;
+
+out:
+ if (update || !af->configured) {
+ memcpy(cur_cfg, user_cfg, sizeof(*cur_cfg));
+ af->inc_config++;
+ af->update = 1;
+ /*
+ * User might be asked for a bigger buffer than necessary for
+ * this configuration. In order to return the right amount of
+ * data during buffer request, let's calculate the size here
+ * instead of stick with user_cfg->buf_size.
+ */
+ cur_cfg->buf_size = h3a_af_get_buf_size(cur_cfg);
+ }
+}
+
+static long h3a_af_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+ struct ispstat *stat = v4l2_get_subdevdata(sd);
+
+ switch (cmd) {
+ case VIDIOC_OMAP3ISP_AF_CFG:
+ return omap3isp_stat_config(stat, arg);
+ case VIDIOC_OMAP3ISP_STAT_REQ:
+ return omap3isp_stat_request_statistics(stat, arg);
+ case VIDIOC_OMAP3ISP_STAT_EN: {
+ int *en = arg;
+ return omap3isp_stat_enable(stat, !!*en);
+ }
+ }
+
+ return -ENOIOCTLCMD;
+
+}
+
+static const struct ispstat_ops h3a_af_ops = {
+ .validate_params = h3a_af_validate_params,
+ .set_params = h3a_af_set_params,
+ .setup_regs = h3a_af_setup_regs,
+ .enable = h3a_af_enable,
+ .busy = h3a_af_busy,
+};
+
+static const struct v4l2_subdev_core_ops h3a_af_subdev_core_ops = {
+ .ioctl = h3a_af_ioctl,
+ .subscribe_event = omap3isp_stat_subscribe_event,
+ .unsubscribe_event = omap3isp_stat_unsubscribe_event,
+};
+
+static const struct v4l2_subdev_video_ops h3a_af_subdev_video_ops = {
+ .s_stream = omap3isp_stat_s_stream,
+};
+
+static const struct v4l2_subdev_ops h3a_af_subdev_ops = {
+ .core = &h3a_af_subdev_core_ops,
+ .video = &h3a_af_subdev_video_ops,
+};
+
+/* Function to register the AF character device driver. */
+int omap3isp_h3a_af_init(struct isp_device *isp)
+{
+ struct ispstat *af = &isp->isp_af;
+ struct omap3isp_h3a_af_config *af_cfg;
+ struct omap3isp_h3a_af_config *af_recover_cfg;
+ int ret;
+
+ af_cfg = kzalloc(sizeof(*af_cfg), GFP_KERNEL);
+ if (af_cfg == NULL)
+ return -ENOMEM;
+
+ memset(af, 0, sizeof(*af));
+ af->ops = &h3a_af_ops;
+ af->priv = af_cfg;
+ af->dma_ch = -1;
+ af->event_type = V4L2_EVENT_OMAP3ISP_AF;
+ af->isp = isp;
+
+ /* Set recover state configuration */
+ af_recover_cfg = kzalloc(sizeof(*af_recover_cfg), GFP_KERNEL);
+ if (!af_recover_cfg) {
+ dev_err(af->isp->dev, "AF: cannot allocate memory for recover "
+ "configuration.\n");
+ ret = -ENOMEM;
+ goto err_recover_alloc;
+ }
+
+ af_recover_cfg->paxel.h_start = OMAP3ISP_AF_PAXEL_HZSTART_MIN;
+ af_recover_cfg->paxel.width = OMAP3ISP_AF_PAXEL_WIDTH_MIN;
+ af_recover_cfg->paxel.height = OMAP3ISP_AF_PAXEL_HEIGHT_MIN;
+ af_recover_cfg->paxel.h_cnt = OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MIN;
+ af_recover_cfg->paxel.v_cnt = OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MIN;
+ af_recover_cfg->paxel.line_inc = OMAP3ISP_AF_PAXEL_INCREMENT_MIN;
+ if (h3a_af_validate_params(af, af_recover_cfg)) {
+ dev_err(af->isp->dev, "AF: recover configuration is "
+ "invalid.\n");
+ ret = -EINVAL;
+ goto err_conf;
+ }
+
+ af_recover_cfg->buf_size = h3a_af_get_buf_size(af_recover_cfg);
+ af->recover_priv = af_recover_cfg;
+
+ ret = omap3isp_stat_init(af, "AF", &h3a_af_subdev_ops);
+ if (ret)
+ goto err_conf;
+
+ return 0;
+
+err_conf:
+ kfree(af_recover_cfg);
+err_recover_alloc:
+ kfree(af_cfg);
+
+ return ret;
+}
+
+void omap3isp_h3a_af_cleanup(struct isp_device *isp)
+{
+ kfree(isp->isp_af.priv);
+ kfree(isp->isp_af.recover_priv);
+ omap3isp_stat_cleanup(&isp->isp_af);
+}
diff --git a/drivers/media/video/omap3isp/isphist.c b/drivers/media/video/omap3isp/isphist.c
new file mode 100644
index 00000000..1163907b
--- /dev/null
+++ b/drivers/media/video/omap3isp/isphist.c
@@ -0,0 +1,520 @@
+/*
+ * isphist.c
+ *
+ * TI OMAP3 ISP - Histogram module
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Contacts: David Cohen <dacohen@gmail.com>
+ * Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/device.h>
+
+#include "isp.h"
+#include "ispreg.h"
+#include "isphist.h"
+
+#define HIST_CONFIG_DMA 1
+
+#define HIST_USING_DMA(hist) ((hist)->dma_ch >= 0)
+
+/*
+ * hist_reset_mem - clear Histogram memory before start stats engine.
+ */
+static void hist_reset_mem(struct ispstat *hist)
+{
+ struct isp_device *isp = hist->isp;
+ struct omap3isp_hist_config *conf = hist->priv;
+ unsigned int i;
+
+ isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR);
+
+ /*
+ * By setting it, the histogram internal buffer is being cleared at the
+ * same time it's being read. This bit must be cleared afterwards.
+ */
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR);
+
+ /*
+ * We'll clear 4 words at each iteration for optimization. It avoids
+ * 3/4 of the jumps. We also know HIST_MEM_SIZE is divisible by 4.
+ */
+ for (i = OMAP3ISP_HIST_MEM_SIZE / 4; i > 0; i--) {
+ isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA);
+ isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA);
+ isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA);
+ isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA);
+ }
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR);
+
+ hist->wait_acc_frames = conf->num_acc_frames;
+}
+
+static void hist_dma_config(struct ispstat *hist)
+{
+ hist->dma_config.data_type = OMAP_DMA_DATA_TYPE_S32;
+ hist->dma_config.sync_mode = OMAP_DMA_SYNC_ELEMENT;
+ hist->dma_config.frame_count = 1;
+ hist->dma_config.src_amode = OMAP_DMA_AMODE_CONSTANT;
+ hist->dma_config.src_start = OMAP3ISP_HIST_REG_BASE + ISPHIST_DATA;
+ hist->dma_config.dst_amode = OMAP_DMA_AMODE_POST_INC;
+ hist->dma_config.src_or_dst_synch = OMAP_DMA_SRC_SYNC;
+}
+
+/*
+ * hist_setup_regs - Helper function to update Histogram registers.
+ */
+static void hist_setup_regs(struct ispstat *hist, void *priv)
+{
+ struct isp_device *isp = hist->isp;
+ struct omap3isp_hist_config *conf = priv;
+ int c;
+ u32 cnt;
+ u32 wb_gain;
+ u32 reg_hor[OMAP3ISP_HIST_MAX_REGIONS];
+ u32 reg_ver[OMAP3ISP_HIST_MAX_REGIONS];
+
+ if (!hist->update || hist->state == ISPSTAT_DISABLED ||
+ hist->state == ISPSTAT_DISABLING)
+ return;
+
+ cnt = conf->cfa << ISPHIST_CNT_CFA_SHIFT;
+
+ wb_gain = conf->wg[0] << ISPHIST_WB_GAIN_WG00_SHIFT;
+ wb_gain |= conf->wg[1] << ISPHIST_WB_GAIN_WG01_SHIFT;
+ wb_gain |= conf->wg[2] << ISPHIST_WB_GAIN_WG02_SHIFT;
+ if (conf->cfa == OMAP3ISP_HIST_CFA_BAYER)
+ wb_gain |= conf->wg[3] << ISPHIST_WB_GAIN_WG03_SHIFT;
+
+ /* Regions size and position */
+ for (c = 0; c < OMAP3ISP_HIST_MAX_REGIONS; c++) {
+ if (c < conf->num_regions) {
+ reg_hor[c] = conf->region[c].h_start <<
+ ISPHIST_REG_START_SHIFT;
+ reg_hor[c] = conf->region[c].h_end <<
+ ISPHIST_REG_END_SHIFT;
+ reg_ver[c] = conf->region[c].v_start <<
+ ISPHIST_REG_START_SHIFT;
+ reg_ver[c] = conf->region[c].v_end <<
+ ISPHIST_REG_END_SHIFT;
+ } else {
+ reg_hor[c] = 0;
+ reg_ver[c] = 0;
+ }
+ }
+
+ cnt |= conf->hist_bins << ISPHIST_CNT_BINS_SHIFT;
+ switch (conf->hist_bins) {
+ case OMAP3ISP_HIST_BINS_256:
+ cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 8) <<
+ ISPHIST_CNT_SHIFT_SHIFT;
+ break;
+ case OMAP3ISP_HIST_BINS_128:
+ cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 7) <<
+ ISPHIST_CNT_SHIFT_SHIFT;
+ break;
+ case OMAP3ISP_HIST_BINS_64:
+ cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 6) <<
+ ISPHIST_CNT_SHIFT_SHIFT;
+ break;
+ default: /* OMAP3ISP_HIST_BINS_32 */
+ cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 5) <<
+ ISPHIST_CNT_SHIFT_SHIFT;
+ break;
+ }
+
+ hist_reset_mem(hist);
+
+ isp_reg_writel(isp, cnt, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT);
+ isp_reg_writel(isp, wb_gain, OMAP3_ISP_IOMEM_HIST, ISPHIST_WB_GAIN);
+ isp_reg_writel(isp, reg_hor[0], OMAP3_ISP_IOMEM_HIST, ISPHIST_R0_HORZ);
+ isp_reg_writel(isp, reg_ver[0], OMAP3_ISP_IOMEM_HIST, ISPHIST_R0_VERT);
+ isp_reg_writel(isp, reg_hor[1], OMAP3_ISP_IOMEM_HIST, ISPHIST_R1_HORZ);
+ isp_reg_writel(isp, reg_ver[1], OMAP3_ISP_IOMEM_HIST, ISPHIST_R1_VERT);
+ isp_reg_writel(isp, reg_hor[2], OMAP3_ISP_IOMEM_HIST, ISPHIST_R2_HORZ);
+ isp_reg_writel(isp, reg_ver[2], OMAP3_ISP_IOMEM_HIST, ISPHIST_R2_VERT);
+ isp_reg_writel(isp, reg_hor[3], OMAP3_ISP_IOMEM_HIST, ISPHIST_R3_HORZ);
+ isp_reg_writel(isp, reg_ver[3], OMAP3_ISP_IOMEM_HIST, ISPHIST_R3_VERT);
+
+ hist->update = 0;
+ hist->config_counter += hist->inc_config;
+ hist->inc_config = 0;
+ hist->buf_size = conf->buf_size;
+}
+
+static void hist_enable(struct ispstat *hist, int enable)
+{
+ if (enable) {
+ isp_reg_set(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR,
+ ISPHIST_PCR_ENABLE);
+ isp_reg_set(hist->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL,
+ ISPCTRL_HIST_CLK_EN);
+ } else {
+ isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR,
+ ISPHIST_PCR_ENABLE);
+ isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL,
+ ISPCTRL_HIST_CLK_EN);
+ }
+}
+
+static int hist_busy(struct ispstat *hist)
+{
+ return isp_reg_readl(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR)
+ & ISPHIST_PCR_BUSY;
+}
+
+static void hist_dma_cb(int lch, u16 ch_status, void *data)
+{
+ struct ispstat *hist = data;
+
+ if (ch_status & ~OMAP_DMA_BLOCK_IRQ) {
+ dev_dbg(hist->isp->dev, "hist: DMA error. status = 0x%04x\n",
+ ch_status);
+ omap_stop_dma(lch);
+ hist_reset_mem(hist);
+ atomic_set(&hist->buf_err, 1);
+ }
+ isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT,
+ ISPHIST_CNT_CLEAR);
+
+ omap3isp_stat_dma_isr(hist);
+ if (hist->state != ISPSTAT_DISABLED)
+ omap3isp_hist_dma_done(hist->isp);
+}
+
+static int hist_buf_dma(struct ispstat *hist)
+{
+ dma_addr_t dma_addr = hist->active_buf->dma_addr;
+
+ if (unlikely(!dma_addr)) {
+ dev_dbg(hist->isp->dev, "hist: invalid DMA buffer address\n");
+ hist_reset_mem(hist);
+ return STAT_NO_BUF;
+ }
+
+ isp_reg_writel(hist->isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR);
+ isp_reg_set(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT,
+ ISPHIST_CNT_CLEAR);
+ omap3isp_flush(hist->isp);
+ hist->dma_config.dst_start = dma_addr;
+ hist->dma_config.elem_count = hist->buf_size / sizeof(u32);
+ omap_set_dma_params(hist->dma_ch, &hist->dma_config);
+
+ omap_start_dma(hist->dma_ch);
+
+ return STAT_BUF_WAITING_DMA;
+}
+
+static int hist_buf_pio(struct ispstat *hist)
+{
+ struct isp_device *isp = hist->isp;
+ u32 *buf = hist->active_buf->virt_addr;
+ unsigned int i;
+
+ if (!buf) {
+ dev_dbg(isp->dev, "hist: invalid PIO buffer address\n");
+ hist_reset_mem(hist);
+ return STAT_NO_BUF;
+ }
+
+ isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR);
+
+ /*
+ * By setting it, the histogram internal buffer is being cleared at the
+ * same time it's being read. This bit must be cleared just after all
+ * data is acquired.
+ */
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR);
+
+ /*
+ * We'll read 4 times a 4-bytes-word at each iteration for
+ * optimization. It avoids 3/4 of the jumps. We also know buf_size is
+ * divisible by 16.
+ */
+ for (i = hist->buf_size / 16; i > 0; i--) {
+ *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA);
+ *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA);
+ *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA);
+ *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA);
+ }
+ isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT,
+ ISPHIST_CNT_CLEAR);
+
+ return STAT_BUF_DONE;
+}
+
+/*
+ * hist_buf_process - Callback from ISP driver for HIST interrupt.
+ */
+static int hist_buf_process(struct ispstat *hist)
+{
+ struct omap3isp_hist_config *user_cfg = hist->priv;
+ int ret;
+
+ if (atomic_read(&hist->buf_err) || hist->state != ISPSTAT_ENABLED) {
+ hist_reset_mem(hist);
+ return STAT_NO_BUF;
+ }
+
+ if (--(hist->wait_acc_frames))
+ return STAT_NO_BUF;
+
+ if (HIST_USING_DMA(hist))
+ ret = hist_buf_dma(hist);
+ else
+ ret = hist_buf_pio(hist);
+
+ hist->wait_acc_frames = user_cfg->num_acc_frames;
+
+ return ret;
+}
+
+static u32 hist_get_buf_size(struct omap3isp_hist_config *conf)
+{
+ return OMAP3ISP_HIST_MEM_SIZE_BINS(conf->hist_bins) * conf->num_regions;
+}
+
+/*
+ * hist_validate_params - Helper function to check user given params.
+ * @user_cfg: Pointer to user configuration structure.
+ *
+ * Returns 0 on success configuration.
+ */
+static int hist_validate_params(struct ispstat *hist, void *new_conf)
+{
+ struct omap3isp_hist_config *user_cfg = new_conf;
+ int c;
+ u32 buf_size;
+
+ if (user_cfg->cfa > OMAP3ISP_HIST_CFA_FOVEONX3)
+ return -EINVAL;
+
+ /* Regions size and position */
+
+ if ((user_cfg->num_regions < OMAP3ISP_HIST_MIN_REGIONS) ||
+ (user_cfg->num_regions > OMAP3ISP_HIST_MAX_REGIONS))
+ return -EINVAL;
+
+ /* Regions */
+ for (c = 0; c < user_cfg->num_regions; c++) {
+ if (user_cfg->region[c].h_start & ~ISPHIST_REG_START_END_MASK)
+ return -EINVAL;
+ if (user_cfg->region[c].h_end & ~ISPHIST_REG_START_END_MASK)
+ return -EINVAL;
+ if (user_cfg->region[c].v_start & ~ISPHIST_REG_START_END_MASK)
+ return -EINVAL;
+ if (user_cfg->region[c].v_end & ~ISPHIST_REG_START_END_MASK)
+ return -EINVAL;
+ if (user_cfg->region[c].h_start > user_cfg->region[c].h_end)
+ return -EINVAL;
+ if (user_cfg->region[c].v_start > user_cfg->region[c].v_end)
+ return -EINVAL;
+ }
+
+ switch (user_cfg->num_regions) {
+ case 1:
+ if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_256)
+ return -EINVAL;
+ break;
+ case 2:
+ if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_128)
+ return -EINVAL;
+ break;
+ default: /* 3 or 4 */
+ if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_64)
+ return -EINVAL;
+ break;
+ }
+
+ buf_size = hist_get_buf_size(user_cfg);
+ if (buf_size > user_cfg->buf_size)
+ /* User's buf_size request wasn't enoght */
+ user_cfg->buf_size = buf_size;
+ else if (user_cfg->buf_size > OMAP3ISP_HIST_MAX_BUF_SIZE)
+ user_cfg->buf_size = OMAP3ISP_HIST_MAX_BUF_SIZE;
+
+ return 0;
+}
+
+static int hist_comp_params(struct ispstat *hist,
+ struct omap3isp_hist_config *user_cfg)
+{
+ struct omap3isp_hist_config *cur_cfg = hist->priv;
+ int c;
+
+ if (cur_cfg->cfa != user_cfg->cfa)
+ return 1;
+
+ if (cur_cfg->num_acc_frames != user_cfg->num_acc_frames)
+ return 1;
+
+ if (cur_cfg->hist_bins != user_cfg->hist_bins)
+ return 1;
+
+ for (c = 0; c < OMAP3ISP_HIST_MAX_WG; c++) {
+ if (c == 3 && user_cfg->cfa == OMAP3ISP_HIST_CFA_FOVEONX3)
+ break;
+ else if (cur_cfg->wg[c] != user_cfg->wg[c])
+ return 1;
+ }
+
+ if (cur_cfg->num_regions != user_cfg->num_regions)
+ return 1;
+
+ /* Regions */
+ for (c = 0; c < user_cfg->num_regions; c++) {
+ if (cur_cfg->region[c].h_start != user_cfg->region[c].h_start)
+ return 1;
+ if (cur_cfg->region[c].h_end != user_cfg->region[c].h_end)
+ return 1;
+ if (cur_cfg->region[c].v_start != user_cfg->region[c].v_start)
+ return 1;
+ if (cur_cfg->region[c].v_end != user_cfg->region[c].v_end)
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * hist_update_params - Helper function to check and store user given params.
+ * @new_conf: Pointer to user configuration structure.
+ */
+static void hist_set_params(struct ispstat *hist, void *new_conf)
+{
+ struct omap3isp_hist_config *user_cfg = new_conf;
+ struct omap3isp_hist_config *cur_cfg = hist->priv;
+
+ if (!hist->configured || hist_comp_params(hist, user_cfg)) {
+ memcpy(cur_cfg, user_cfg, sizeof(*user_cfg));
+ if (user_cfg->num_acc_frames == 0)
+ user_cfg->num_acc_frames = 1;
+ hist->inc_config++;
+ hist->update = 1;
+ /*
+ * User might be asked for a bigger buffer than necessary for
+ * this configuration. In order to return the right amount of
+ * data during buffer request, let's calculate the size here
+ * instead of stick with user_cfg->buf_size.
+ */
+ cur_cfg->buf_size = hist_get_buf_size(cur_cfg);
+
+ }
+}
+
+static long hist_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+ struct ispstat *stat = v4l2_get_subdevdata(sd);
+
+ switch (cmd) {
+ case VIDIOC_OMAP3ISP_HIST_CFG:
+ return omap3isp_stat_config(stat, arg);
+ case VIDIOC_OMAP3ISP_STAT_REQ:
+ return omap3isp_stat_request_statistics(stat, arg);
+ case VIDIOC_OMAP3ISP_STAT_EN: {
+ int *en = arg;
+ return omap3isp_stat_enable(stat, !!*en);
+ }
+ }
+
+ return -ENOIOCTLCMD;
+
+}
+
+static const struct ispstat_ops hist_ops = {
+ .validate_params = hist_validate_params,
+ .set_params = hist_set_params,
+ .setup_regs = hist_setup_regs,
+ .enable = hist_enable,
+ .busy = hist_busy,
+ .buf_process = hist_buf_process,
+};
+
+static const struct v4l2_subdev_core_ops hist_subdev_core_ops = {
+ .ioctl = hist_ioctl,
+ .subscribe_event = omap3isp_stat_subscribe_event,
+ .unsubscribe_event = omap3isp_stat_unsubscribe_event,
+};
+
+static const struct v4l2_subdev_video_ops hist_subdev_video_ops = {
+ .s_stream = omap3isp_stat_s_stream,
+};
+
+static const struct v4l2_subdev_ops hist_subdev_ops = {
+ .core = &hist_subdev_core_ops,
+ .video = &hist_subdev_video_ops,
+};
+
+/*
+ * omap3isp_hist_init - Module Initialization.
+ */
+int omap3isp_hist_init(struct isp_device *isp)
+{
+ struct ispstat *hist = &isp->isp_hist;
+ struct omap3isp_hist_config *hist_cfg;
+ int ret = -1;
+
+ hist_cfg = kzalloc(sizeof(*hist_cfg), GFP_KERNEL);
+ if (hist_cfg == NULL)
+ return -ENOMEM;
+
+ memset(hist, 0, sizeof(*hist));
+ if (HIST_CONFIG_DMA)
+ ret = omap_request_dma(OMAP24XX_DMA_NO_DEVICE, "DMA_ISP_HIST",
+ hist_dma_cb, hist, &hist->dma_ch);
+ if (ret) {
+ if (HIST_CONFIG_DMA)
+ dev_warn(isp->dev, "hist: DMA request channel failed. "
+ "Using PIO only.\n");
+ hist->dma_ch = -1;
+ } else {
+ dev_dbg(isp->dev, "hist: DMA channel = %d\n", hist->dma_ch);
+ hist_dma_config(hist);
+ omap_enable_dma_irq(hist->dma_ch, OMAP_DMA_BLOCK_IRQ);
+ }
+
+ hist->ops = &hist_ops;
+ hist->priv = hist_cfg;
+ hist->event_type = V4L2_EVENT_OMAP3ISP_HIST;
+ hist->isp = isp;
+
+ ret = omap3isp_stat_init(hist, "histogram", &hist_subdev_ops);
+ if (ret) {
+ kfree(hist_cfg);
+ if (HIST_USING_DMA(hist))
+ omap_free_dma(hist->dma_ch);
+ }
+
+ return ret;
+}
+
+/*
+ * omap3isp_hist_cleanup - Module cleanup.
+ */
+void omap3isp_hist_cleanup(struct isp_device *isp)
+{
+ if (HIST_USING_DMA(&isp->isp_hist))
+ omap_free_dma(isp->isp_hist.dma_ch);
+ kfree(isp->isp_hist.priv);
+ omap3isp_stat_cleanup(&isp->isp_hist);
+}
diff --git a/drivers/media/video/omap3isp/isphist.h b/drivers/media/video/omap3isp/isphist.h
new file mode 100644
index 00000000..0b2a38ec
--- /dev/null
+++ b/drivers/media/video/omap3isp/isphist.h
@@ -0,0 +1,40 @@
+/*
+ * isphist.h
+ *
+ * TI OMAP3 ISP - Histogram module
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Contacts: David Cohen <dacohen@gmail.com>
+ * Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP3_ISP_HIST_H
+#define OMAP3_ISP_HIST_H
+
+#include <linux/omap3isp.h>
+
+#define ISPHIST_IN_BIT_WIDTH_CCDC 10
+
+struct isp_device;
+
+int omap3isp_hist_init(struct isp_device *isp);
+void omap3isp_hist_cleanup(struct isp_device *isp);
+
+#endif /* OMAP3_ISP_HIST */
diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c
new file mode 100644
index 00000000..6d0fb2c8
--- /dev/null
+++ b/drivers/media/video/omap3isp/isppreview.c
@@ -0,0 +1,2227 @@
+/*
+ * isppreview.c
+ *
+ * TI OMAP3 ISP driver - Preview module
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/device.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+
+#include "isp.h"
+#include "ispreg.h"
+#include "isppreview.h"
+
+/* Default values in Office Fluorescent Light for RGBtoRGB Blending */
+static struct omap3isp_prev_rgbtorgb flr_rgb2rgb = {
+ { /* RGB-RGB Matrix */
+ {0x01E2, 0x0F30, 0x0FEE},
+ {0x0F9B, 0x01AC, 0x0FB9},
+ {0x0FE0, 0x0EC0, 0x0260}
+ }, /* RGB Offset */
+ {0x0000, 0x0000, 0x0000}
+};
+
+/* Default values in Office Fluorescent Light for RGB to YUV Conversion*/
+static struct omap3isp_prev_csc flr_prev_csc = {
+ { /* CSC Coef Matrix */
+ {66, 129, 25},
+ {-38, -75, 112},
+ {112, -94 , -18}
+ }, /* CSC Offset */
+ {0x0, 0x0, 0x0}
+};
+
+/* Default values in Office Fluorescent Light for CFA Gradient*/
+#define FLR_CFA_GRADTHRS_HORZ 0x28
+#define FLR_CFA_GRADTHRS_VERT 0x28
+
+/* Default values in Office Fluorescent Light for Chroma Suppression*/
+#define FLR_CSUP_GAIN 0x0D
+#define FLR_CSUP_THRES 0xEB
+
+/* Default values in Office Fluorescent Light for Noise Filter*/
+#define FLR_NF_STRGTH 0x03
+
+/* Default values for White Balance */
+#define FLR_WBAL_DGAIN 0x100
+#define FLR_WBAL_COEF 0x20
+
+/* Default values in Office Fluorescent Light for Black Adjustment*/
+#define FLR_BLKADJ_BLUE 0x0
+#define FLR_BLKADJ_GREEN 0x0
+#define FLR_BLKADJ_RED 0x0
+
+#define DEF_DETECT_CORRECT_VAL 0xe
+
+/*
+ * Margins and image size limits.
+ *
+ * The preview engine crops several rows and columns internally depending on
+ * which filters are enabled. To avoid format changes when the filters are
+ * enabled or disabled (which would prevent them from being turned on or off
+ * during streaming), the driver assumes all the filters are enabled when
+ * computing sink crop and source format limits.
+ *
+ * If a filter is disabled, additional cropping is automatically added at the
+ * preview engine input by the driver to avoid overflow at line and frame end.
+ * This is completely transparent for applications.
+ *
+ * Median filter 4 pixels
+ * Noise filter,
+ * Faulty pixels correction 4 pixels, 4 lines
+ * CFA filter 4 pixels, 4 lines in Bayer mode
+ * 2 lines in other modes
+ * Color suppression 2 pixels
+ * or luma enhancement
+ * -------------------------------------------------------------
+ * Maximum total 14 pixels, 8 lines
+ *
+ * The color suppression and luma enhancement filters are applied after bayer to
+ * YUV conversion. They thus can crop one pixel on the left and one pixel on the
+ * right side of the image without changing the color pattern. When both those
+ * filters are disabled, the driver must crop the two pixels on the same side of
+ * the image to avoid changing the bayer pattern. The left margin is thus set to
+ * 8 pixels and the right margin to 6 pixels.
+ */
+
+#define PREV_MARGIN_LEFT 8
+#define PREV_MARGIN_RIGHT 6
+#define PREV_MARGIN_TOP 4
+#define PREV_MARGIN_BOTTOM 4
+
+#define PREV_MIN_IN_WIDTH 64
+#define PREV_MIN_IN_HEIGHT 8
+#define PREV_MAX_IN_HEIGHT 16384
+
+#define PREV_MIN_OUT_WIDTH 0
+#define PREV_MIN_OUT_HEIGHT 0
+#define PREV_MAX_OUT_WIDTH_REV_1 1280
+#define PREV_MAX_OUT_WIDTH_REV_2 3300
+#define PREV_MAX_OUT_WIDTH_REV_15 4096
+
+/*
+ * Coeficient Tables for the submodules in Preview.
+ * Array is initialised with the values from.the tables text file.
+ */
+
+/*
+ * CFA Filter Coefficient Table
+ *
+ */
+static u32 cfa_coef_table[] = {
+#include "cfa_coef_table.h"
+};
+
+/*
+ * Default Gamma Correction Table - All components
+ */
+static u32 gamma_table[] = {
+#include "gamma_table.h"
+};
+
+/*
+ * Noise Filter Threshold table
+ */
+static u32 noise_filter_table[] = {
+#include "noise_filter_table.h"
+};
+
+/*
+ * Luminance Enhancement Table
+ */
+static u32 luma_enhance_table[] = {
+#include "luma_enhance_table.h"
+};
+
+/*
+ * preview_enable_invalaw - Enable/Disable Inverse A-Law module in Preview.
+ * @enable: 1 - Reverse the A-Law done in CCDC.
+ */
+static void
+preview_enable_invalaw(struct isp_prev_device *prev, u8 enable)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ if (enable)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_WIDTH | ISPPRV_PCR_INVALAW);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_WIDTH | ISPPRV_PCR_INVALAW);
+}
+
+/*
+ * preview_enable_drkframe_capture - Enable/Disable of the darkframe capture.
+ * @prev -
+ * @enable: 1 - Enable, 0 - Disable
+ *
+ * NOTE: PRV_WSDR_ADDR and PRV_WADD_OFFSET must be set also
+ * The process is applied for each captured frame.
+ */
+static void
+preview_enable_drkframe_capture(struct isp_prev_device *prev, u8 enable)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ if (enable)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_DRKFCAP);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_DRKFCAP);
+}
+
+/*
+ * preview_enable_drkframe - Enable/Disable of the darkframe subtract.
+ * @enable: 1 - Acquires memory bandwidth since the pixels in each frame is
+ * subtracted with the pixels in the current frame.
+ *
+ * The process is applied for each captured frame.
+ */
+static void
+preview_enable_drkframe(struct isp_prev_device *prev, u8 enable)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ if (enable)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_DRKFEN);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_DRKFEN);
+}
+
+/*
+ * preview_config_drkf_shadcomp - Configures shift value in shading comp.
+ * @scomp_shtval: 3bit value of shift used in shading compensation.
+ */
+static void
+preview_config_drkf_shadcomp(struct isp_prev_device *prev,
+ const void *scomp_shtval)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ const u32 *shtval = scomp_shtval;
+
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_SCOMP_SFT_MASK,
+ *shtval << ISPPRV_PCR_SCOMP_SFT_SHIFT);
+}
+
+/*
+ * preview_enable_hmed - Enables/Disables of the Horizontal Median Filter.
+ * @enable: 1 - Enables Horizontal Median Filter.
+ */
+static void
+preview_enable_hmed(struct isp_prev_device *prev, u8 enable)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ if (enable)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_HMEDEN);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_HMEDEN);
+}
+
+/*
+ * preview_config_hmed - Configures the Horizontal Median Filter.
+ * @prev_hmed: Structure containing the odd and even distance between the
+ * pixels in the image along with the filter threshold.
+ */
+static void
+preview_config_hmed(struct isp_prev_device *prev, const void *prev_hmed)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ const struct omap3isp_prev_hmed *hmed = prev_hmed;
+
+ isp_reg_writel(isp, (hmed->odddist == 1 ? 0 : ISPPRV_HMED_ODDDIST) |
+ (hmed->evendist == 1 ? 0 : ISPPRV_HMED_EVENDIST) |
+ (hmed->thres << ISPPRV_HMED_THRESHOLD_SHIFT),
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_HMED);
+}
+
+/*
+ * preview_config_noisefilter - Configures the Noise Filter.
+ * @prev_nf: Structure containing the noisefilter table, strength to be used
+ * for the noise filter and the defect correction enable flag.
+ */
+static void
+preview_config_noisefilter(struct isp_prev_device *prev, const void *prev_nf)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ const struct omap3isp_prev_nf *nf = prev_nf;
+ unsigned int i;
+
+ isp_reg_writel(isp, nf->spread, OMAP3_ISP_IOMEM_PREV, ISPPRV_NF);
+ isp_reg_writel(isp, ISPPRV_NF_TABLE_ADDR,
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR);
+ for (i = 0; i < OMAP3ISP_PREV_NF_TBL_SIZE; i++) {
+ isp_reg_writel(isp, nf->table[i],
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA);
+ }
+}
+
+/*
+ * preview_config_dcor - Configures the defect correction
+ * @prev_dcor: Structure containing the defect correct thresholds
+ */
+static void
+preview_config_dcor(struct isp_prev_device *prev, const void *prev_dcor)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ const struct omap3isp_prev_dcor *dcor = prev_dcor;
+
+ isp_reg_writel(isp, dcor->detect_correct[0],
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR0);
+ isp_reg_writel(isp, dcor->detect_correct[1],
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR1);
+ isp_reg_writel(isp, dcor->detect_correct[2],
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR2);
+ isp_reg_writel(isp, dcor->detect_correct[3],
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR3);
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_DCCOUP,
+ dcor->couplet_mode_en ? ISPPRV_PCR_DCCOUP : 0);
+}
+
+/*
+ * preview_config_cfa - Configures the CFA Interpolation parameters.
+ * @prev_cfa: Structure containing the CFA interpolation table, CFA format
+ * in the image, vertical and horizontal gradient threshold.
+ */
+static void
+preview_config_cfa(struct isp_prev_device *prev, const void *prev_cfa)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ const struct omap3isp_prev_cfa *cfa = prev_cfa;
+ unsigned int i;
+
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_CFAFMT_MASK,
+ cfa->format << ISPPRV_PCR_CFAFMT_SHIFT);
+
+ isp_reg_writel(isp,
+ (cfa->gradthrs_vert << ISPPRV_CFA_GRADTH_VER_SHIFT) |
+ (cfa->gradthrs_horz << ISPPRV_CFA_GRADTH_HOR_SHIFT),
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_CFA);
+
+ isp_reg_writel(isp, ISPPRV_CFA_TABLE_ADDR,
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR);
+
+ for (i = 0; i < OMAP3ISP_PREV_CFA_TBL_SIZE; i++) {
+ isp_reg_writel(isp, cfa->table[i],
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA);
+ }
+}
+
+/*
+ * preview_config_gammacorrn - Configures the Gamma Correction table values
+ * @gtable: Structure containing the table for red, blue, green gamma table.
+ */
+static void
+preview_config_gammacorrn(struct isp_prev_device *prev, const void *gtable)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ const struct omap3isp_prev_gtables *gt = gtable;
+ unsigned int i;
+
+ isp_reg_writel(isp, ISPPRV_REDGAMMA_TABLE_ADDR,
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR);
+ for (i = 0; i < OMAP3ISP_PREV_GAMMA_TBL_SIZE; i++)
+ isp_reg_writel(isp, gt->red[i], OMAP3_ISP_IOMEM_PREV,
+ ISPPRV_SET_TBL_DATA);
+
+ isp_reg_writel(isp, ISPPRV_GREENGAMMA_TABLE_ADDR,
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR);
+ for (i = 0; i < OMAP3ISP_PREV_GAMMA_TBL_SIZE; i++)
+ isp_reg_writel(isp, gt->green[i], OMAP3_ISP_IOMEM_PREV,
+ ISPPRV_SET_TBL_DATA);
+
+ isp_reg_writel(isp, ISPPRV_BLUEGAMMA_TABLE_ADDR,
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR);
+ for (i = 0; i < OMAP3ISP_PREV_GAMMA_TBL_SIZE; i++)
+ isp_reg_writel(isp, gt->blue[i], OMAP3_ISP_IOMEM_PREV,
+ ISPPRV_SET_TBL_DATA);
+}
+
+/*
+ * preview_config_luma_enhancement - Sets the Luminance Enhancement table.
+ * @ytable: Structure containing the table for Luminance Enhancement table.
+ */
+static void
+preview_config_luma_enhancement(struct isp_prev_device *prev,
+ const void *ytable)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ const struct omap3isp_prev_luma *yt = ytable;
+ unsigned int i;
+
+ isp_reg_writel(isp, ISPPRV_YENH_TABLE_ADDR,
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR);
+ for (i = 0; i < OMAP3ISP_PREV_YENH_TBL_SIZE; i++) {
+ isp_reg_writel(isp, yt->table[i],
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA);
+ }
+}
+
+/*
+ * preview_config_chroma_suppression - Configures the Chroma Suppression.
+ * @csup: Structure containing the threshold value for suppression
+ * and the hypass filter enable flag.
+ */
+static void
+preview_config_chroma_suppression(struct isp_prev_device *prev,
+ const void *csup)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ const struct omap3isp_prev_csup *cs = csup;
+
+ isp_reg_writel(isp,
+ cs->gain | (cs->thres << ISPPRV_CSUP_THRES_SHIFT) |
+ (cs->hypf_en << ISPPRV_CSUP_HPYF_SHIFT),
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_CSUP);
+}
+
+/*
+ * preview_enable_noisefilter - Enables/Disables the Noise Filter.
+ * @enable: 1 - Enables the Noise Filter.
+ */
+static void
+preview_enable_noisefilter(struct isp_prev_device *prev, u8 enable)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ if (enable)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_NFEN);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_NFEN);
+}
+
+/*
+ * preview_enable_dcor - Enables/Disables the defect correction.
+ * @enable: 1 - Enables the defect correction.
+ */
+static void
+preview_enable_dcor(struct isp_prev_device *prev, u8 enable)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ if (enable)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_DCOREN);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_DCOREN);
+}
+
+/*
+ * preview_enable_cfa - Enable/Disable the CFA Interpolation.
+ * @enable: 1 - Enables the CFA.
+ */
+static void
+preview_enable_cfa(struct isp_prev_device *prev, u8 enable)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ if (enable)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_CFAEN);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_CFAEN);
+}
+
+/*
+ * preview_enable_gammabypass - Enables/Disables the GammaByPass
+ * @enable: 1 - Bypasses Gamma - 10bit input is cropped to 8MSB.
+ * 0 - Goes through Gamma Correction. input and output is 10bit.
+ */
+static void
+preview_enable_gammabypass(struct isp_prev_device *prev, u8 enable)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ if (enable)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_GAMMA_BYPASS);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_GAMMA_BYPASS);
+}
+
+/*
+ * preview_enable_luma_enhancement - Enables/Disables Luminance Enhancement
+ * @enable: 1 - Enable the Luminance Enhancement.
+ */
+static void
+preview_enable_luma_enhancement(struct isp_prev_device *prev, u8 enable)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ if (enable)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_YNENHEN);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_YNENHEN);
+}
+
+/*
+ * preview_enable_chroma_suppression - Enables/Disables Chrominance Suppr.
+ * @enable: 1 - Enable the Chrominance Suppression.
+ */
+static void
+preview_enable_chroma_suppression(struct isp_prev_device *prev, u8 enable)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ if (enable)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_SUPEN);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_SUPEN);
+}
+
+/*
+ * preview_config_whitebalance - Configures the White Balance parameters.
+ * @prev_wbal: Structure containing the digital gain and white balance
+ * coefficient.
+ *
+ * Coefficient matrix always with default values.
+ */
+static void
+preview_config_whitebalance(struct isp_prev_device *prev, const void *prev_wbal)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ const struct omap3isp_prev_wbal *wbal = prev_wbal;
+ u32 val;
+
+ isp_reg_writel(isp, wbal->dgain, OMAP3_ISP_IOMEM_PREV, ISPPRV_WB_DGAIN);
+
+ val = wbal->coef0 << ISPPRV_WBGAIN_COEF0_SHIFT;
+ val |= wbal->coef1 << ISPPRV_WBGAIN_COEF1_SHIFT;
+ val |= wbal->coef2 << ISPPRV_WBGAIN_COEF2_SHIFT;
+ val |= wbal->coef3 << ISPPRV_WBGAIN_COEF3_SHIFT;
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_WBGAIN);
+
+ isp_reg_writel(isp,
+ ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N0_0_SHIFT |
+ ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N0_1_SHIFT |
+ ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N0_2_SHIFT |
+ ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N0_3_SHIFT |
+ ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N1_0_SHIFT |
+ ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N1_1_SHIFT |
+ ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N1_2_SHIFT |
+ ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N1_3_SHIFT |
+ ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N2_0_SHIFT |
+ ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N2_1_SHIFT |
+ ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N2_2_SHIFT |
+ ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N2_3_SHIFT |
+ ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N3_0_SHIFT |
+ ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N3_1_SHIFT |
+ ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N3_2_SHIFT |
+ ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N3_3_SHIFT,
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_WBSEL);
+}
+
+/*
+ * preview_config_blkadj - Configures the Black Adjustment parameters.
+ * @prev_blkadj: Structure containing the black adjustment towards red, green,
+ * blue.
+ */
+static void
+preview_config_blkadj(struct isp_prev_device *prev, const void *prev_blkadj)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ const struct omap3isp_prev_blkadj *blkadj = prev_blkadj;
+
+ isp_reg_writel(isp, (blkadj->blue << ISPPRV_BLKADJOFF_B_SHIFT) |
+ (blkadj->green << ISPPRV_BLKADJOFF_G_SHIFT) |
+ (blkadj->red << ISPPRV_BLKADJOFF_R_SHIFT),
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_BLKADJOFF);
+}
+
+/*
+ * preview_config_rgb_blending - Configures the RGB-RGB Blending matrix.
+ * @rgb2rgb: Structure containing the rgb to rgb blending matrix and the rgb
+ * offset.
+ */
+static void
+preview_config_rgb_blending(struct isp_prev_device *prev, const void *rgb2rgb)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ const struct omap3isp_prev_rgbtorgb *rgbrgb = rgb2rgb;
+ u32 val;
+
+ val = (rgbrgb->matrix[0][0] & 0xfff) << ISPPRV_RGB_MAT1_MTX_RR_SHIFT;
+ val |= (rgbrgb->matrix[0][1] & 0xfff) << ISPPRV_RGB_MAT1_MTX_GR_SHIFT;
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT1);
+
+ val = (rgbrgb->matrix[0][2] & 0xfff) << ISPPRV_RGB_MAT2_MTX_BR_SHIFT;
+ val |= (rgbrgb->matrix[1][0] & 0xfff) << ISPPRV_RGB_MAT2_MTX_RG_SHIFT;
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT2);
+
+ val = (rgbrgb->matrix[1][1] & 0xfff) << ISPPRV_RGB_MAT3_MTX_GG_SHIFT;
+ val |= (rgbrgb->matrix[1][2] & 0xfff) << ISPPRV_RGB_MAT3_MTX_BG_SHIFT;
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT3);
+
+ val = (rgbrgb->matrix[2][0] & 0xfff) << ISPPRV_RGB_MAT4_MTX_RB_SHIFT;
+ val |= (rgbrgb->matrix[2][1] & 0xfff) << ISPPRV_RGB_MAT4_MTX_GB_SHIFT;
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT4);
+
+ val = (rgbrgb->matrix[2][2] & 0xfff) << ISPPRV_RGB_MAT5_MTX_BB_SHIFT;
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT5);
+
+ val = (rgbrgb->offset[0] & 0x3ff) << ISPPRV_RGB_OFF1_MTX_OFFR_SHIFT;
+ val |= (rgbrgb->offset[1] & 0x3ff) << ISPPRV_RGB_OFF1_MTX_OFFG_SHIFT;
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_OFF1);
+
+ val = (rgbrgb->offset[2] & 0x3ff) << ISPPRV_RGB_OFF2_MTX_OFFB_SHIFT;
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_OFF2);
+}
+
+/*
+ * Configures the RGB-YCbYCr conversion matrix
+ * @prev_csc: Structure containing the RGB to YCbYCr matrix and the
+ * YCbCr offset.
+ */
+static void
+preview_config_rgb_to_ycbcr(struct isp_prev_device *prev, const void *prev_csc)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ const struct omap3isp_prev_csc *csc = prev_csc;
+ u32 val;
+
+ val = (csc->matrix[0][0] & 0x3ff) << ISPPRV_CSC0_RY_SHIFT;
+ val |= (csc->matrix[0][1] & 0x3ff) << ISPPRV_CSC0_GY_SHIFT;
+ val |= (csc->matrix[0][2] & 0x3ff) << ISPPRV_CSC0_BY_SHIFT;
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC0);
+
+ val = (csc->matrix[1][0] & 0x3ff) << ISPPRV_CSC1_RCB_SHIFT;
+ val |= (csc->matrix[1][1] & 0x3ff) << ISPPRV_CSC1_GCB_SHIFT;
+ val |= (csc->matrix[1][2] & 0x3ff) << ISPPRV_CSC1_BCB_SHIFT;
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC1);
+
+ val = (csc->matrix[2][0] & 0x3ff) << ISPPRV_CSC2_RCR_SHIFT;
+ val |= (csc->matrix[2][1] & 0x3ff) << ISPPRV_CSC2_GCR_SHIFT;
+ val |= (csc->matrix[2][2] & 0x3ff) << ISPPRV_CSC2_BCR_SHIFT;
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC2);
+
+ val = (csc->offset[0] & 0xff) << ISPPRV_CSC_OFFSET_Y_SHIFT;
+ val |= (csc->offset[1] & 0xff) << ISPPRV_CSC_OFFSET_CB_SHIFT;
+ val |= (csc->offset[2] & 0xff) << ISPPRV_CSC_OFFSET_CR_SHIFT;
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC_OFFSET);
+}
+
+/*
+ * preview_update_contrast - Updates the contrast.
+ * @contrast: Pointer to hold the current programmed contrast value.
+ *
+ * Value should be programmed before enabling the module.
+ */
+static void
+preview_update_contrast(struct isp_prev_device *prev, u8 contrast)
+{
+ struct prev_params *params = &prev->params;
+
+ if (params->contrast != (contrast * ISPPRV_CONTRAST_UNITS)) {
+ params->contrast = contrast * ISPPRV_CONTRAST_UNITS;
+ prev->update |= PREV_CONTRAST;
+ }
+}
+
+/*
+ * preview_config_contrast - Configures the Contrast.
+ * @params: Contrast value (u8 pointer, U8Q0 format).
+ *
+ * Value should be programmed before enabling the module.
+ */
+static void
+preview_config_contrast(struct isp_prev_device *prev, const void *params)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_CNT_BRT,
+ 0xff << ISPPRV_CNT_BRT_CNT_SHIFT,
+ *(u8 *)params << ISPPRV_CNT_BRT_CNT_SHIFT);
+}
+
+/*
+ * preview_update_brightness - Updates the brightness in preview module.
+ * @brightness: Pointer to hold the current programmed brightness value.
+ *
+ */
+static void
+preview_update_brightness(struct isp_prev_device *prev, u8 brightness)
+{
+ struct prev_params *params = &prev->params;
+
+ if (params->brightness != (brightness * ISPPRV_BRIGHT_UNITS)) {
+ params->brightness = brightness * ISPPRV_BRIGHT_UNITS;
+ prev->update |= PREV_BRIGHTNESS;
+ }
+}
+
+/*
+ * preview_config_brightness - Configures the brightness.
+ * @params: Brightness value (u8 pointer, U8Q0 format).
+ */
+static void
+preview_config_brightness(struct isp_prev_device *prev, const void *params)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_CNT_BRT,
+ 0xff << ISPPRV_CNT_BRT_BRT_SHIFT,
+ *(u8 *)params << ISPPRV_CNT_BRT_BRT_SHIFT);
+}
+
+/*
+ * preview_config_yc_range - Configures the max and min Y and C values.
+ * @yclimit: Structure containing the range of Y and C values.
+ */
+static void
+preview_config_yc_range(struct isp_prev_device *prev, const void *yclimit)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ const struct omap3isp_prev_yclimit *yc = yclimit;
+
+ isp_reg_writel(isp,
+ yc->maxC << ISPPRV_SETUP_YC_MAXC_SHIFT |
+ yc->maxY << ISPPRV_SETUP_YC_MAXY_SHIFT |
+ yc->minC << ISPPRV_SETUP_YC_MINC_SHIFT |
+ yc->minY << ISPPRV_SETUP_YC_MINY_SHIFT,
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_SETUP_YC);
+}
+
+/* preview parameters update structure */
+struct preview_update {
+ int cfg_bit;
+ int feature_bit;
+ void (*config)(struct isp_prev_device *, const void *);
+ void (*enable)(struct isp_prev_device *, u8);
+};
+
+static struct preview_update update_attrs[] = {
+ {OMAP3ISP_PREV_LUMAENH, PREV_LUMA_ENHANCE,
+ preview_config_luma_enhancement,
+ preview_enable_luma_enhancement},
+ {OMAP3ISP_PREV_INVALAW, PREV_INVERSE_ALAW,
+ NULL,
+ preview_enable_invalaw},
+ {OMAP3ISP_PREV_HRZ_MED, PREV_HORZ_MEDIAN_FILTER,
+ preview_config_hmed,
+ preview_enable_hmed},
+ {OMAP3ISP_PREV_CFA, PREV_CFA,
+ preview_config_cfa,
+ preview_enable_cfa},
+ {OMAP3ISP_PREV_CHROMA_SUPP, PREV_CHROMA_SUPPRESS,
+ preview_config_chroma_suppression,
+ preview_enable_chroma_suppression},
+ {OMAP3ISP_PREV_WB, PREV_WB,
+ preview_config_whitebalance,
+ NULL},
+ {OMAP3ISP_PREV_BLKADJ, PREV_BLKADJ,
+ preview_config_blkadj,
+ NULL},
+ {OMAP3ISP_PREV_RGB2RGB, PREV_RGB2RGB,
+ preview_config_rgb_blending,
+ NULL},
+ {OMAP3ISP_PREV_COLOR_CONV, PREV_COLOR_CONV,
+ preview_config_rgb_to_ycbcr,
+ NULL},
+ {OMAP3ISP_PREV_YC_LIMIT, PREV_YCLIMITS,
+ preview_config_yc_range,
+ NULL},
+ {OMAP3ISP_PREV_DEFECT_COR, PREV_DEFECT_COR,
+ preview_config_dcor,
+ preview_enable_dcor},
+ {OMAP3ISP_PREV_GAMMABYPASS, PREV_GAMMA_BYPASS,
+ NULL,
+ preview_enable_gammabypass},
+ {OMAP3ISP_PREV_DRK_FRM_CAPTURE, PREV_DARK_FRAME_CAPTURE,
+ NULL,
+ preview_enable_drkframe_capture},
+ {OMAP3ISP_PREV_DRK_FRM_SUBTRACT, PREV_DARK_FRAME_SUBTRACT,
+ NULL,
+ preview_enable_drkframe},
+ {OMAP3ISP_PREV_LENS_SHADING, PREV_LENS_SHADING,
+ preview_config_drkf_shadcomp,
+ preview_enable_drkframe},
+ {OMAP3ISP_PREV_NF, PREV_NOISE_FILTER,
+ preview_config_noisefilter,
+ preview_enable_noisefilter},
+ {OMAP3ISP_PREV_GAMMA, PREV_GAMMA,
+ preview_config_gammacorrn,
+ NULL},
+ {-1, PREV_CONTRAST,
+ preview_config_contrast,
+ NULL},
+ {-1, PREV_BRIGHTNESS,
+ preview_config_brightness,
+ NULL},
+};
+
+/*
+ * __preview_get_ptrs - helper function which return pointers to members
+ * of params and config structures.
+ * @params - pointer to preview_params structure.
+ * @param - return pointer to appropriate structure field.
+ * @configs - pointer to update config structure.
+ * @config - return pointer to appropriate structure field.
+ * @bit - for which feature to return pointers.
+ * Return size of corresponding prev_params member
+ */
+static u32
+__preview_get_ptrs(struct prev_params *params, void **param,
+ struct omap3isp_prev_update_config *configs,
+ void __user **config, u32 bit)
+{
+#define CHKARG(cfgs, cfg, field) \
+ if (cfgs && cfg) { \
+ *(cfg) = (cfgs)->field; \
+ }
+
+ switch (bit) {
+ case PREV_HORZ_MEDIAN_FILTER:
+ *param = &params->hmed;
+ CHKARG(configs, config, hmed)
+ return sizeof(params->hmed);
+ case PREV_NOISE_FILTER:
+ *param = &params->nf;
+ CHKARG(configs, config, nf)
+ return sizeof(params->nf);
+ break;
+ case PREV_CFA:
+ *param = &params->cfa;
+ CHKARG(configs, config, cfa)
+ return sizeof(params->cfa);
+ case PREV_LUMA_ENHANCE:
+ *param = &params->luma;
+ CHKARG(configs, config, luma)
+ return sizeof(params->luma);
+ case PREV_CHROMA_SUPPRESS:
+ *param = &params->csup;
+ CHKARG(configs, config, csup)
+ return sizeof(params->csup);
+ case PREV_DEFECT_COR:
+ *param = &params->dcor;
+ CHKARG(configs, config, dcor)
+ return sizeof(params->dcor);
+ case PREV_BLKADJ:
+ *param = &params->blk_adj;
+ CHKARG(configs, config, blkadj)
+ return sizeof(params->blk_adj);
+ case PREV_YCLIMITS:
+ *param = &params->yclimit;
+ CHKARG(configs, config, yclimit)
+ return sizeof(params->yclimit);
+ case PREV_RGB2RGB:
+ *param = &params->rgb2rgb;
+ CHKARG(configs, config, rgb2rgb)
+ return sizeof(params->rgb2rgb);
+ case PREV_COLOR_CONV:
+ *param = &params->rgb2ycbcr;
+ CHKARG(configs, config, csc)
+ return sizeof(params->rgb2ycbcr);
+ case PREV_WB:
+ *param = &params->wbal;
+ CHKARG(configs, config, wbal)
+ return sizeof(params->wbal);
+ case PREV_GAMMA:
+ *param = &params->gamma;
+ CHKARG(configs, config, gamma)
+ return sizeof(params->gamma);
+ case PREV_CONTRAST:
+ *param = &params->contrast;
+ return 0;
+ case PREV_BRIGHTNESS:
+ *param = &params->brightness;
+ return 0;
+ default:
+ *param = NULL;
+ *config = NULL;
+ break;
+ }
+ return 0;
+}
+
+/*
+ * preview_config - Copy and update local structure with userspace preview
+ * configuration.
+ * @prev: ISP preview engine
+ * @cfg: Configuration
+ *
+ * Return zero if success or -EFAULT if the configuration can't be copied from
+ * userspace.
+ */
+static int preview_config(struct isp_prev_device *prev,
+ struct omap3isp_prev_update_config *cfg)
+{
+ struct prev_params *params;
+ struct preview_update *attr;
+ int i, bit, rval = 0;
+
+ params = &prev->params;
+
+ if (prev->state != ISP_PIPELINE_STREAM_STOPPED) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&prev->lock, flags);
+ prev->shadow_update = 1;
+ spin_unlock_irqrestore(&prev->lock, flags);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(update_attrs); i++) {
+ attr = &update_attrs[i];
+ bit = 0;
+
+ if (!(cfg->update & attr->cfg_bit))
+ continue;
+
+ bit = cfg->flag & attr->cfg_bit;
+ if (bit) {
+ void *to = NULL, __user *from = NULL;
+ unsigned long sz = 0;
+
+ sz = __preview_get_ptrs(params, &to, cfg, &from,
+ bit);
+ if (to && from && sz) {
+ if (copy_from_user(to, from, sz)) {
+ rval = -EFAULT;
+ break;
+ }
+ }
+ params->features |= attr->feature_bit;
+ } else {
+ params->features &= ~attr->feature_bit;
+ }
+
+ prev->update |= attr->feature_bit;
+ }
+
+ prev->shadow_update = 0;
+ return rval;
+}
+
+/*
+ * preview_setup_hw - Setup preview registers and/or internal memory
+ * @prev: pointer to preview private structure
+ * Note: can be called from interrupt context
+ * Return none
+ */
+static void preview_setup_hw(struct isp_prev_device *prev)
+{
+ struct prev_params *params = &prev->params;
+ struct preview_update *attr;
+ int i, bit;
+ void *param_ptr;
+
+ for (i = 0; i < ARRAY_SIZE(update_attrs); i++) {
+ attr = &update_attrs[i];
+
+ if (!(prev->update & attr->feature_bit))
+ continue;
+ bit = params->features & attr->feature_bit;
+ if (bit) {
+ if (attr->config) {
+ __preview_get_ptrs(params, &param_ptr, NULL,
+ NULL, bit);
+ attr->config(prev, param_ptr);
+ }
+ if (attr->enable)
+ attr->enable(prev, 1);
+ } else
+ if (attr->enable)
+ attr->enable(prev, 0);
+
+ prev->update &= ~attr->feature_bit;
+ }
+}
+
+/*
+ * preview_config_ycpos - Configure byte layout of YUV image.
+ * @mode: Indicates the required byte layout.
+ */
+static void
+preview_config_ycpos(struct isp_prev_device *prev,
+ enum v4l2_mbus_pixelcode pixelcode)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ enum preview_ycpos_mode mode;
+
+ switch (pixelcode) {
+ case V4L2_MBUS_FMT_YUYV8_1X16:
+ mode = YCPOS_CrYCbY;
+ break;
+ case V4L2_MBUS_FMT_UYVY8_1X16:
+ mode = YCPOS_YCrYCb;
+ break;
+ default:
+ return;
+ }
+
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_YCPOS_CrYCbY,
+ mode << ISPPRV_PCR_YCPOS_SHIFT);
+}
+
+/*
+ * preview_config_averager - Enable / disable / configure averager
+ * @average: Average value to be configured.
+ */
+static void preview_config_averager(struct isp_prev_device *prev, u8 average)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ int reg = 0;
+
+ if (prev->params.cfa.format == OMAP3ISP_CFAFMT_BAYER)
+ reg = ISPPRV_AVE_EVENDIST_2 << ISPPRV_AVE_EVENDIST_SHIFT |
+ ISPPRV_AVE_ODDDIST_2 << ISPPRV_AVE_ODDDIST_SHIFT |
+ average;
+ else if (prev->params.cfa.format == OMAP3ISP_CFAFMT_RGBFOVEON)
+ reg = ISPPRV_AVE_EVENDIST_3 << ISPPRV_AVE_EVENDIST_SHIFT |
+ ISPPRV_AVE_ODDDIST_3 << ISPPRV_AVE_ODDDIST_SHIFT |
+ average;
+ isp_reg_writel(isp, reg, OMAP3_ISP_IOMEM_PREV, ISPPRV_AVE);
+}
+
+/*
+ * preview_config_input_size - Configure the input frame size
+ *
+ * The preview engine crops several rows and columns internally depending on
+ * which processing blocks are enabled. The driver assumes all those blocks are
+ * enabled when reporting source pad formats to userspace. If this assumption is
+ * not true, rows and columns must be manually cropped at the preview engine
+ * input to avoid overflows at the end of lines and frames.
+ *
+ * See the explanation at the PREV_MARGIN_* definitions for more details.
+ */
+static void preview_config_input_size(struct isp_prev_device *prev)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ struct prev_params *params = &prev->params;
+ unsigned int sph = prev->crop.left;
+ unsigned int eph = prev->crop.left + prev->crop.width - 1;
+ unsigned int slv = prev->crop.top;
+ unsigned int elv = prev->crop.top + prev->crop.height - 1;
+
+ if (params->features & PREV_CFA) {
+ sph -= 2;
+ eph += 2;
+ slv -= 2;
+ elv += 2;
+ }
+ if (params->features & (PREV_DEFECT_COR | PREV_NOISE_FILTER)) {
+ sph -= 2;
+ eph += 2;
+ slv -= 2;
+ elv += 2;
+ }
+ if (params->features & PREV_HORZ_MEDIAN_FILTER) {
+ sph -= 2;
+ eph += 2;
+ }
+ if (params->features & (PREV_CHROMA_SUPPRESS | PREV_LUMA_ENHANCE))
+ sph -= 2;
+
+ isp_reg_writel(isp, (sph << ISPPRV_HORZ_INFO_SPH_SHIFT) | eph,
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_HORZ_INFO);
+ isp_reg_writel(isp, (slv << ISPPRV_VERT_INFO_SLV_SHIFT) | elv,
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_VERT_INFO);
+}
+
+/*
+ * preview_config_inlineoffset - Configures the Read address line offset.
+ * @prev: Preview module
+ * @offset: Line offset
+ *
+ * According to the TRM, the line offset must be aligned on a 32 bytes boundary.
+ * However, a hardware bug requires the memory start address to be aligned on a
+ * 64 bytes boundary, so the offset probably should be aligned on 64 bytes as
+ * well.
+ */
+static void
+preview_config_inlineoffset(struct isp_prev_device *prev, u32 offset)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ isp_reg_writel(isp, offset & 0xffff, OMAP3_ISP_IOMEM_PREV,
+ ISPPRV_RADR_OFFSET);
+}
+
+/*
+ * preview_set_inaddr - Sets memory address of input frame.
+ * @addr: 32bit memory address aligned on 32byte boundary.
+ *
+ * Configures the memory address from which the input frame is to be read.
+ */
+static void preview_set_inaddr(struct isp_prev_device *prev, u32 addr)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_PREV, ISPPRV_RSDR_ADDR);
+}
+
+/*
+ * preview_config_outlineoffset - Configures the Write address line offset.
+ * @offset: Line Offset for the preview output.
+ *
+ * The offset must be a multiple of 32 bytes.
+ */
+static void preview_config_outlineoffset(struct isp_prev_device *prev,
+ u32 offset)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ isp_reg_writel(isp, offset & 0xffff, OMAP3_ISP_IOMEM_PREV,
+ ISPPRV_WADD_OFFSET);
+}
+
+/*
+ * preview_set_outaddr - Sets the memory address to store output frame
+ * @addr: 32bit memory address aligned on 32byte boundary.
+ *
+ * Configures the memory address to which the output frame is written.
+ */
+static void preview_set_outaddr(struct isp_prev_device *prev, u32 addr)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_PREV, ISPPRV_WSDR_ADDR);
+}
+
+static void preview_adjust_bandwidth(struct isp_prev_device *prev)
+{
+ struct isp_pipeline *pipe = to_isp_pipeline(&prev->subdev.entity);
+ struct isp_device *isp = to_isp_device(prev);
+ const struct v4l2_mbus_framefmt *ifmt = &prev->formats[PREV_PAD_SINK];
+ unsigned long l3_ick = pipe->l3_ick;
+ struct v4l2_fract *timeperframe;
+ unsigned int cycles_per_frame;
+ unsigned int requests_per_frame;
+ unsigned int cycles_per_request;
+ unsigned int minimum;
+ unsigned int maximum;
+ unsigned int value;
+
+ if (prev->input != PREVIEW_INPUT_MEMORY) {
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP,
+ ISPSBL_SDR_REQ_PRV_EXP_MASK);
+ return;
+ }
+
+ /* Compute the minimum number of cycles per request, based on the
+ * pipeline maximum data rate. This is an absolute lower bound if we
+ * don't want SBL overflows, so round the value up.
+ */
+ cycles_per_request = div_u64((u64)l3_ick / 2 * 256 + pipe->max_rate - 1,
+ pipe->max_rate);
+ minimum = DIV_ROUND_UP(cycles_per_request, 32);
+
+ /* Compute the maximum number of cycles per request, based on the
+ * requested frame rate. This is a soft upper bound to achieve a frame
+ * rate equal or higher than the requested value, so round the value
+ * down.
+ */
+ timeperframe = &pipe->max_timeperframe;
+
+ requests_per_frame = DIV_ROUND_UP(ifmt->width * 2, 256) * ifmt->height;
+ cycles_per_frame = div_u64((u64)l3_ick * timeperframe->numerator,
+ timeperframe->denominator);
+ cycles_per_request = cycles_per_frame / requests_per_frame;
+
+ maximum = cycles_per_request / 32;
+
+ value = max(minimum, maximum);
+
+ dev_dbg(isp->dev, "%s: cycles per request = %u\n", __func__, value);
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP,
+ ISPSBL_SDR_REQ_PRV_EXP_MASK,
+ value << ISPSBL_SDR_REQ_PRV_EXP_SHIFT);
+}
+
+/*
+ * omap3isp_preview_busy - Gets busy state of preview module.
+ */
+int omap3isp_preview_busy(struct isp_prev_device *prev)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ return isp_reg_readl(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR)
+ & ISPPRV_PCR_BUSY;
+}
+
+/*
+ * omap3isp_preview_restore_context - Restores the values of preview registers
+ */
+void omap3isp_preview_restore_context(struct isp_device *isp)
+{
+ isp->isp_prev.update = PREV_FEATURES_END - 1;
+ preview_setup_hw(&isp->isp_prev);
+}
+
+/*
+ * preview_print_status - Dump preview module registers to the kernel log
+ */
+#define PREV_PRINT_REGISTER(isp, name)\
+ dev_dbg(isp->dev, "###PRV " #name "=0x%08x\n", \
+ isp_reg_readl(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_##name))
+
+static void preview_print_status(struct isp_prev_device *prev)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ dev_dbg(isp->dev, "-------------Preview Register dump----------\n");
+
+ PREV_PRINT_REGISTER(isp, PCR);
+ PREV_PRINT_REGISTER(isp, HORZ_INFO);
+ PREV_PRINT_REGISTER(isp, VERT_INFO);
+ PREV_PRINT_REGISTER(isp, RSDR_ADDR);
+ PREV_PRINT_REGISTER(isp, RADR_OFFSET);
+ PREV_PRINT_REGISTER(isp, DSDR_ADDR);
+ PREV_PRINT_REGISTER(isp, DRKF_OFFSET);
+ PREV_PRINT_REGISTER(isp, WSDR_ADDR);
+ PREV_PRINT_REGISTER(isp, WADD_OFFSET);
+ PREV_PRINT_REGISTER(isp, AVE);
+ PREV_PRINT_REGISTER(isp, HMED);
+ PREV_PRINT_REGISTER(isp, NF);
+ PREV_PRINT_REGISTER(isp, WB_DGAIN);
+ PREV_PRINT_REGISTER(isp, WBGAIN);
+ PREV_PRINT_REGISTER(isp, WBSEL);
+ PREV_PRINT_REGISTER(isp, CFA);
+ PREV_PRINT_REGISTER(isp, BLKADJOFF);
+ PREV_PRINT_REGISTER(isp, RGB_MAT1);
+ PREV_PRINT_REGISTER(isp, RGB_MAT2);
+ PREV_PRINT_REGISTER(isp, RGB_MAT3);
+ PREV_PRINT_REGISTER(isp, RGB_MAT4);
+ PREV_PRINT_REGISTER(isp, RGB_MAT5);
+ PREV_PRINT_REGISTER(isp, RGB_OFF1);
+ PREV_PRINT_REGISTER(isp, RGB_OFF2);
+ PREV_PRINT_REGISTER(isp, CSC0);
+ PREV_PRINT_REGISTER(isp, CSC1);
+ PREV_PRINT_REGISTER(isp, CSC2);
+ PREV_PRINT_REGISTER(isp, CSC_OFFSET);
+ PREV_PRINT_REGISTER(isp, CNT_BRT);
+ PREV_PRINT_REGISTER(isp, CSUP);
+ PREV_PRINT_REGISTER(isp, SETUP_YC);
+ PREV_PRINT_REGISTER(isp, SET_TBL_ADDR);
+ PREV_PRINT_REGISTER(isp, CDC_THR0);
+ PREV_PRINT_REGISTER(isp, CDC_THR1);
+ PREV_PRINT_REGISTER(isp, CDC_THR2);
+ PREV_PRINT_REGISTER(isp, CDC_THR3);
+
+ dev_dbg(isp->dev, "--------------------------------------------\n");
+}
+
+/*
+ * preview_init_params - init image processing parameters.
+ * @prev: pointer to previewer private structure
+ * return none
+ */
+static void preview_init_params(struct isp_prev_device *prev)
+{
+ struct prev_params *params = &prev->params;
+ int i = 0;
+
+ /* Init values */
+ params->contrast = ISPPRV_CONTRAST_DEF * ISPPRV_CONTRAST_UNITS;
+ params->brightness = ISPPRV_BRIGHT_DEF * ISPPRV_BRIGHT_UNITS;
+ params->cfa.format = OMAP3ISP_CFAFMT_BAYER;
+ memcpy(params->cfa.table, cfa_coef_table,
+ sizeof(params->cfa.table));
+ params->cfa.gradthrs_horz = FLR_CFA_GRADTHRS_HORZ;
+ params->cfa.gradthrs_vert = FLR_CFA_GRADTHRS_VERT;
+ params->csup.gain = FLR_CSUP_GAIN;
+ params->csup.thres = FLR_CSUP_THRES;
+ params->csup.hypf_en = 0;
+ memcpy(params->luma.table, luma_enhance_table,
+ sizeof(params->luma.table));
+ params->nf.spread = FLR_NF_STRGTH;
+ memcpy(params->nf.table, noise_filter_table, sizeof(params->nf.table));
+ params->dcor.couplet_mode_en = 1;
+ for (i = 0; i < OMAP3ISP_PREV_DETECT_CORRECT_CHANNELS; i++)
+ params->dcor.detect_correct[i] = DEF_DETECT_CORRECT_VAL;
+ memcpy(params->gamma.blue, gamma_table, sizeof(params->gamma.blue));
+ memcpy(params->gamma.green, gamma_table, sizeof(params->gamma.green));
+ memcpy(params->gamma.red, gamma_table, sizeof(params->gamma.red));
+ params->wbal.dgain = FLR_WBAL_DGAIN;
+ params->wbal.coef0 = FLR_WBAL_COEF;
+ params->wbal.coef1 = FLR_WBAL_COEF;
+ params->wbal.coef2 = FLR_WBAL_COEF;
+ params->wbal.coef3 = FLR_WBAL_COEF;
+ params->blk_adj.red = FLR_BLKADJ_RED;
+ params->blk_adj.green = FLR_BLKADJ_GREEN;
+ params->blk_adj.blue = FLR_BLKADJ_BLUE;
+ params->rgb2rgb = flr_rgb2rgb;
+ params->rgb2ycbcr = flr_prev_csc;
+ params->yclimit.minC = ISPPRV_YC_MIN;
+ params->yclimit.maxC = ISPPRV_YC_MAX;
+ params->yclimit.minY = ISPPRV_YC_MIN;
+ params->yclimit.maxY = ISPPRV_YC_MAX;
+
+ params->features = PREV_CFA | PREV_DEFECT_COR | PREV_NOISE_FILTER
+ | PREV_GAMMA | PREV_BLKADJ | PREV_YCLIMITS
+ | PREV_RGB2RGB | PREV_COLOR_CONV | PREV_WB
+ | PREV_BRIGHTNESS | PREV_CONTRAST;
+
+ prev->update = PREV_FEATURES_END - 1;
+}
+
+/*
+ * preview_max_out_width - Handle previewer hardware ouput limitations
+ * @isp_revision : ISP revision
+ * returns maximum width output for current isp revision
+ */
+static unsigned int preview_max_out_width(struct isp_prev_device *prev)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ switch (isp->revision) {
+ case ISP_REVISION_1_0:
+ return PREV_MAX_OUT_WIDTH_REV_1;
+
+ case ISP_REVISION_2_0:
+ default:
+ return PREV_MAX_OUT_WIDTH_REV_2;
+
+ case ISP_REVISION_15_0:
+ return PREV_MAX_OUT_WIDTH_REV_15;
+ }
+}
+
+static void preview_configure(struct isp_prev_device *prev)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ struct v4l2_mbus_framefmt *format;
+
+ preview_setup_hw(prev);
+
+ if (prev->output & PREVIEW_OUTPUT_MEMORY)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_SDRPORT);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_SDRPORT);
+
+ if (prev->output & PREVIEW_OUTPUT_RESIZER)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_RSZPORT);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_RSZPORT);
+
+ /* PREV_PAD_SINK */
+ format = &prev->formats[PREV_PAD_SINK];
+
+ preview_adjust_bandwidth(prev);
+
+ preview_config_input_size(prev);
+
+ if (prev->input == PREVIEW_INPUT_CCDC)
+ preview_config_inlineoffset(prev, 0);
+ else
+ preview_config_inlineoffset(prev,
+ ALIGN(format->width, 0x20) * 2);
+
+ /* PREV_PAD_SOURCE */
+ format = &prev->formats[PREV_PAD_SOURCE];
+
+ if (prev->output & PREVIEW_OUTPUT_MEMORY)
+ preview_config_outlineoffset(prev,
+ ALIGN(format->width, 0x10) * 2);
+
+ preview_config_averager(prev, 0);
+ preview_config_ycpos(prev, format->code);
+}
+
+/* -----------------------------------------------------------------------------
+ * Interrupt handling
+ */
+
+static void preview_enable_oneshot(struct isp_prev_device *prev)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ /* The PCR.SOURCE bit is automatically reset to 0 when the PCR.ENABLE
+ * bit is set. As the preview engine is used in single-shot mode, we
+ * need to set PCR.SOURCE before enabling the preview engine.
+ */
+ if (prev->input == PREVIEW_INPUT_MEMORY)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_SOURCE);
+
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_EN | ISPPRV_PCR_ONESHOT);
+}
+
+void omap3isp_preview_isr_frame_sync(struct isp_prev_device *prev)
+{
+ /*
+ * If ISP_VIDEO_DMAQUEUE_QUEUED is set, DMA queue had an underrun
+ * condition, the module was paused and now we have a buffer queued
+ * on the output again. Restart the pipeline if running in continuous
+ * mode.
+ */
+ if (prev->state == ISP_PIPELINE_STREAM_CONTINUOUS &&
+ prev->video_out.dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) {
+ preview_enable_oneshot(prev);
+ isp_video_dmaqueue_flags_clr(&prev->video_out);
+ }
+}
+
+static void preview_isr_buffer(struct isp_prev_device *prev)
+{
+ struct isp_pipeline *pipe = to_isp_pipeline(&prev->subdev.entity);
+ struct isp_buffer *buffer;
+ int restart = 0;
+
+ if (prev->input == PREVIEW_INPUT_MEMORY) {
+ buffer = omap3isp_video_buffer_next(&prev->video_in);
+ if (buffer != NULL)
+ preview_set_inaddr(prev, buffer->isp_addr);
+ pipe->state |= ISP_PIPELINE_IDLE_INPUT;
+ }
+
+ if (prev->output & PREVIEW_OUTPUT_MEMORY) {
+ buffer = omap3isp_video_buffer_next(&prev->video_out);
+ if (buffer != NULL) {
+ preview_set_outaddr(prev, buffer->isp_addr);
+ restart = 1;
+ }
+ pipe->state |= ISP_PIPELINE_IDLE_OUTPUT;
+ }
+
+ switch (prev->state) {
+ case ISP_PIPELINE_STREAM_SINGLESHOT:
+ if (isp_pipeline_ready(pipe))
+ omap3isp_pipeline_set_stream(pipe,
+ ISP_PIPELINE_STREAM_SINGLESHOT);
+ break;
+
+ case ISP_PIPELINE_STREAM_CONTINUOUS:
+ /* If an underrun occurs, the video queue operation handler will
+ * restart the preview engine. Otherwise restart it immediately.
+ */
+ if (restart)
+ preview_enable_oneshot(prev);
+ break;
+
+ case ISP_PIPELINE_STREAM_STOPPED:
+ default:
+ return;
+ }
+}
+
+/*
+ * omap3isp_preview_isr - ISP preview engine interrupt handler
+ *
+ * Manage the preview engine video buffers and configure shadowed registers.
+ */
+void omap3isp_preview_isr(struct isp_prev_device *prev)
+{
+ unsigned long flags;
+
+ if (omap3isp_module_sync_is_stopping(&prev->wait, &prev->stopping))
+ return;
+
+ spin_lock_irqsave(&prev->lock, flags);
+ if (prev->shadow_update)
+ goto done;
+
+ preview_setup_hw(prev);
+ preview_config_input_size(prev);
+
+done:
+ spin_unlock_irqrestore(&prev->lock, flags);
+
+ if (prev->input == PREVIEW_INPUT_MEMORY ||
+ prev->output & PREVIEW_OUTPUT_MEMORY)
+ preview_isr_buffer(prev);
+ else if (prev->state == ISP_PIPELINE_STREAM_CONTINUOUS)
+ preview_enable_oneshot(prev);
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP video operations
+ */
+
+static int preview_video_queue(struct isp_video *video,
+ struct isp_buffer *buffer)
+{
+ struct isp_prev_device *prev = &video->isp->isp_prev;
+
+ if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ preview_set_inaddr(prev, buffer->isp_addr);
+
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ preview_set_outaddr(prev, buffer->isp_addr);
+
+ return 0;
+}
+
+static const struct isp_video_operations preview_video_ops = {
+ .queue = preview_video_queue,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+/*
+ * preview_s_ctrl - Handle set control subdev method
+ * @ctrl: pointer to v4l2 control structure
+ */
+static int preview_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct isp_prev_device *prev =
+ container_of(ctrl->handler, struct isp_prev_device, ctrls);
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ preview_update_brightness(prev, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ preview_update_contrast(prev, ctrl->val);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops preview_ctrl_ops = {
+ .s_ctrl = preview_s_ctrl,
+};
+
+/*
+ * preview_ioctl - Handle preview module private ioctl's
+ * @prev: pointer to preview context structure
+ * @cmd: configuration command
+ * @arg: configuration argument
+ * return -EINVAL or zero on success
+ */
+static long preview_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+ struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
+
+ switch (cmd) {
+ case VIDIOC_OMAP3ISP_PRV_CFG:
+ return preview_config(prev, arg);
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+/*
+ * preview_set_stream - Enable/Disable streaming on preview subdev
+ * @sd : pointer to v4l2 subdev structure
+ * @enable: 1 == Enable, 0 == Disable
+ * return -EINVAL or zero on success
+ */
+static int preview_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
+ struct isp_video *video_out = &prev->video_out;
+ struct isp_device *isp = to_isp_device(prev);
+ struct device *dev = to_device(prev);
+ unsigned long flags;
+
+ if (prev->state == ISP_PIPELINE_STREAM_STOPPED) {
+ if (enable == ISP_PIPELINE_STREAM_STOPPED)
+ return 0;
+
+ omap3isp_subclk_enable(isp, OMAP3_ISP_SUBCLK_PREVIEW);
+ preview_configure(prev);
+ atomic_set(&prev->stopping, 0);
+ preview_print_status(prev);
+ }
+
+ switch (enable) {
+ case ISP_PIPELINE_STREAM_CONTINUOUS:
+ if (prev->output & PREVIEW_OUTPUT_MEMORY)
+ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_PREVIEW_WRITE);
+
+ if (video_out->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED ||
+ !(prev->output & PREVIEW_OUTPUT_MEMORY))
+ preview_enable_oneshot(prev);
+
+ isp_video_dmaqueue_flags_clr(video_out);
+ break;
+
+ case ISP_PIPELINE_STREAM_SINGLESHOT:
+ if (prev->input == PREVIEW_INPUT_MEMORY)
+ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_PREVIEW_READ);
+ if (prev->output & PREVIEW_OUTPUT_MEMORY)
+ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_PREVIEW_WRITE);
+
+ preview_enable_oneshot(prev);
+ break;
+
+ case ISP_PIPELINE_STREAM_STOPPED:
+ if (omap3isp_module_sync_idle(&sd->entity, &prev->wait,
+ &prev->stopping))
+ dev_dbg(dev, "%s: stop timeout.\n", sd->name);
+ spin_lock_irqsave(&prev->lock, flags);
+ omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_PREVIEW_READ);
+ omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_PREVIEW_WRITE);
+ omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_PREVIEW);
+ spin_unlock_irqrestore(&prev->lock, flags);
+ isp_video_dmaqueue_flags_clr(video_out);
+ break;
+ }
+
+ prev->state = enable;
+ return 0;
+}
+
+static struct v4l2_mbus_framefmt *
+__preview_get_format(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(fh, pad);
+ else
+ return &prev->formats[pad];
+}
+
+static struct v4l2_rect *
+__preview_get_crop(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh,
+ enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_crop(fh, PREV_PAD_SINK);
+ else
+ return &prev->crop;
+}
+
+/* previewer format descriptions */
+static const unsigned int preview_input_fmts[] = {
+ V4L2_MBUS_FMT_SGRBG10_1X10,
+ V4L2_MBUS_FMT_SRGGB10_1X10,
+ V4L2_MBUS_FMT_SBGGR10_1X10,
+ V4L2_MBUS_FMT_SGBRG10_1X10,
+};
+
+static const unsigned int preview_output_fmts[] = {
+ V4L2_MBUS_FMT_UYVY8_1X16,
+ V4L2_MBUS_FMT_YUYV8_1X16,
+};
+
+/*
+ * preview_try_format - Validate a format
+ * @prev: ISP preview engine
+ * @fh: V4L2 subdev file handle
+ * @pad: pad number
+ * @fmt: format to be validated
+ * @which: try/active format selector
+ *
+ * Validate and adjust the given format for the given pad based on the preview
+ * engine limits and the format and crop rectangles on other pads.
+ */
+static void preview_try_format(struct isp_prev_device *prev,
+ struct v4l2_subdev_fh *fh, unsigned int pad,
+ struct v4l2_mbus_framefmt *fmt,
+ enum v4l2_subdev_format_whence which)
+{
+ enum v4l2_mbus_pixelcode pixelcode;
+ struct v4l2_rect *crop;
+ unsigned int i;
+
+ switch (pad) {
+ case PREV_PAD_SINK:
+ /* When reading data from the CCDC, the input size has already
+ * been mangled by the CCDC output pad so it can be accepted
+ * as-is.
+ *
+ * When reading data from memory, clamp the requested width and
+ * height. The TRM doesn't specify a minimum input height, make
+ * sure we got enough lines to enable the noise filter and color
+ * filter array interpolation.
+ */
+ if (prev->input == PREVIEW_INPUT_MEMORY) {
+ fmt->width = clamp_t(u32, fmt->width, PREV_MIN_IN_WIDTH,
+ preview_max_out_width(prev));
+ fmt->height = clamp_t(u32, fmt->height,
+ PREV_MIN_IN_HEIGHT,
+ PREV_MAX_IN_HEIGHT);
+ }
+
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+
+ for (i = 0; i < ARRAY_SIZE(preview_input_fmts); i++) {
+ if (fmt->code == preview_input_fmts[i])
+ break;
+ }
+
+ /* If not found, use SGRBG10 as default */
+ if (i >= ARRAY_SIZE(preview_input_fmts))
+ fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ break;
+
+ case PREV_PAD_SOURCE:
+ pixelcode = fmt->code;
+ *fmt = *__preview_get_format(prev, fh, PREV_PAD_SINK, which);
+
+ switch (pixelcode) {
+ case V4L2_MBUS_FMT_YUYV8_1X16:
+ case V4L2_MBUS_FMT_UYVY8_1X16:
+ fmt->code = pixelcode;
+ break;
+
+ default:
+ fmt->code = V4L2_MBUS_FMT_YUYV8_1X16;
+ break;
+ }
+
+ /* The preview module output size is configurable through the
+ * averager (horizontal scaling by 1/1, 1/2, 1/4 or 1/8). This
+ * is not supported yet, hardcode the output size to the crop
+ * rectangle size.
+ */
+ crop = __preview_get_crop(prev, fh, which);
+ fmt->width = crop->width;
+ fmt->height = crop->height;
+
+ fmt->colorspace = V4L2_COLORSPACE_JPEG;
+ break;
+ }
+
+ fmt->field = V4L2_FIELD_NONE;
+}
+
+/*
+ * preview_try_crop - Validate a crop rectangle
+ * @prev: ISP preview engine
+ * @sink: format on the sink pad
+ * @crop: crop rectangle to be validated
+ *
+ * The preview engine crops lines and columns for its internal operation,
+ * depending on which filters are enabled. Enforce minimum crop margins to
+ * handle that transparently for userspace.
+ *
+ * See the explanation at the PREV_MARGIN_* definitions for more details.
+ */
+static void preview_try_crop(struct isp_prev_device *prev,
+ const struct v4l2_mbus_framefmt *sink,
+ struct v4l2_rect *crop)
+{
+ unsigned int left = PREV_MARGIN_LEFT;
+ unsigned int right = sink->width - PREV_MARGIN_RIGHT;
+ unsigned int top = PREV_MARGIN_TOP;
+ unsigned int bottom = sink->height - PREV_MARGIN_BOTTOM;
+
+ /* When processing data on-the-fly from the CCDC, at least 2 pixels must
+ * be cropped from the left and right sides of the image. As we don't
+ * know which filters will be enabled, increase the left and right
+ * margins by two.
+ */
+ if (prev->input == PREVIEW_INPUT_CCDC) {
+ left += 2;
+ right -= 2;
+ }
+
+ /* Restrict left/top to even values to keep the Bayer pattern. */
+ crop->left &= ~1;
+ crop->top &= ~1;
+
+ crop->left = clamp_t(u32, crop->left, left, right - PREV_MIN_OUT_WIDTH);
+ crop->top = clamp_t(u32, crop->top, top, bottom - PREV_MIN_OUT_HEIGHT);
+ crop->width = clamp_t(u32, crop->width, PREV_MIN_OUT_WIDTH,
+ right - crop->left);
+ crop->height = clamp_t(u32, crop->height, PREV_MIN_OUT_HEIGHT,
+ bottom - crop->top);
+}
+
+/*
+ * preview_enum_mbus_code - Handle pixel format enumeration
+ * @sd : pointer to v4l2 subdev structure
+ * @fh : V4L2 subdev file handle
+ * @code : pointer to v4l2_subdev_mbus_code_enum structure
+ * return -EINVAL or zero on success
+ */
+static int preview_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ switch (code->pad) {
+ case PREV_PAD_SINK:
+ if (code->index >= ARRAY_SIZE(preview_input_fmts))
+ return -EINVAL;
+
+ code->code = preview_input_fmts[code->index];
+ break;
+ case PREV_PAD_SOURCE:
+ if (code->index >= ARRAY_SIZE(preview_output_fmts))
+ return -EINVAL;
+
+ code->code = preview_output_fmts[code->index];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int preview_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt format;
+
+ if (fse->index != 0)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = 1;
+ format.height = 1;
+ preview_try_format(prev, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+ fse->min_width = format.width;
+ fse->min_height = format.height;
+
+ if (format.code != fse->code)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = -1;
+ format.height = -1;
+ preview_try_format(prev, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+ fse->max_width = format.width;
+ fse->max_height = format.height;
+
+ return 0;
+}
+
+/*
+ * preview_get_crop - Retrieve the crop rectangle on a pad
+ * @sd: ISP preview V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ * @crop: crop rectangle
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+static int preview_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
+
+ /* Cropping is only supported on the sink pad. */
+ if (crop->pad != PREV_PAD_SINK)
+ return -EINVAL;
+
+ crop->rect = *__preview_get_crop(prev, fh, crop->which);
+ return 0;
+}
+
+/*
+ * preview_set_crop - Retrieve the crop rectangle on a pad
+ * @sd: ISP preview V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ * @crop: crop rectangle
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+static int preview_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ /* Cropping is only supported on the sink pad. */
+ if (crop->pad != PREV_PAD_SINK)
+ return -EINVAL;
+
+ /* The crop rectangle can't be changed while streaming. */
+ if (prev->state != ISP_PIPELINE_STREAM_STOPPED)
+ return -EBUSY;
+
+ format = __preview_get_format(prev, fh, PREV_PAD_SINK, crop->which);
+ preview_try_crop(prev, format, &crop->rect);
+ *__preview_get_crop(prev, fh, crop->which) = crop->rect;
+
+ /* Update the source format. */
+ format = __preview_get_format(prev, fh, PREV_PAD_SOURCE, crop->which);
+ preview_try_format(prev, fh, PREV_PAD_SOURCE, format, crop->which);
+
+ return 0;
+}
+
+/*
+ * preview_get_format - Handle get format by pads subdev method
+ * @sd : pointer to v4l2 subdev structure
+ * @fh : V4L2 subdev file handle
+ * @fmt: pointer to v4l2 subdev format structure
+ * return -EINVAL or zero on success
+ */
+static int preview_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __preview_get_format(prev, fh, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ fmt->format = *format;
+ return 0;
+}
+
+/*
+ * preview_set_format - Handle set format by pads subdev method
+ * @sd : pointer to v4l2 subdev structure
+ * @fh : V4L2 subdev file handle
+ * @fmt: pointer to v4l2 subdev format structure
+ * return -EINVAL or zero on success
+ */
+static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *crop;
+
+ format = __preview_get_format(prev, fh, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ preview_try_format(prev, fh, fmt->pad, &fmt->format, fmt->which);
+ *format = fmt->format;
+
+ /* Propagate the format from sink to source */
+ if (fmt->pad == PREV_PAD_SINK) {
+ /* Reset the crop rectangle. */
+ crop = __preview_get_crop(prev, fh, fmt->which);
+ crop->left = 0;
+ crop->top = 0;
+ crop->width = fmt->format.width;
+ crop->height = fmt->format.height;
+
+ preview_try_crop(prev, &fmt->format, crop);
+
+ /* Update the source format. */
+ format = __preview_get_format(prev, fh, PREV_PAD_SOURCE,
+ fmt->which);
+ preview_try_format(prev, fh, PREV_PAD_SOURCE, format,
+ fmt->which);
+ }
+
+ return 0;
+}
+
+/*
+ * preview_init_formats - Initialize formats on all pads
+ * @sd: ISP preview V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ *
+ * Initialize all pad formats with default values. If fh is not NULL, try
+ * formats are initialized on the file handle. Otherwise active formats are
+ * initialized on the device.
+ */
+static int preview_init_formats(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_subdev_format format;
+
+ memset(&format, 0, sizeof(format));
+ format.pad = PREV_PAD_SINK;
+ format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
+ format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ format.format.width = 4096;
+ format.format.height = 4096;
+ preview_set_format(sd, fh, &format);
+
+ return 0;
+}
+
+/* subdev core operations */
+static const struct v4l2_subdev_core_ops preview_v4l2_core_ops = {
+ .ioctl = preview_ioctl,
+};
+
+/* subdev video operations */
+static const struct v4l2_subdev_video_ops preview_v4l2_video_ops = {
+ .s_stream = preview_set_stream,
+};
+
+/* subdev pad operations */
+static const struct v4l2_subdev_pad_ops preview_v4l2_pad_ops = {
+ .enum_mbus_code = preview_enum_mbus_code,
+ .enum_frame_size = preview_enum_frame_size,
+ .get_fmt = preview_get_format,
+ .set_fmt = preview_set_format,
+ .get_crop = preview_get_crop,
+ .set_crop = preview_set_crop,
+};
+
+/* subdev operations */
+static const struct v4l2_subdev_ops preview_v4l2_ops = {
+ .core = &preview_v4l2_core_ops,
+ .video = &preview_v4l2_video_ops,
+ .pad = &preview_v4l2_pad_ops,
+};
+
+/* subdev internal operations */
+static const struct v4l2_subdev_internal_ops preview_v4l2_internal_ops = {
+ .open = preview_init_formats,
+};
+
+/* -----------------------------------------------------------------------------
+ * Media entity operations
+ */
+
+/*
+ * preview_link_setup - Setup previewer connections.
+ * @entity : Pointer to media entity structure
+ * @local : Pointer to local pad array
+ * @remote : Pointer to remote pad array
+ * @flags : Link flags
+ * return -EINVAL or zero on success
+ */
+static int preview_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
+
+ switch (local->index | media_entity_type(remote->entity)) {
+ case PREV_PAD_SINK | MEDIA_ENT_T_DEVNODE:
+ /* read from memory */
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (prev->input == PREVIEW_INPUT_CCDC)
+ return -EBUSY;
+ prev->input = PREVIEW_INPUT_MEMORY;
+ } else {
+ if (prev->input == PREVIEW_INPUT_MEMORY)
+ prev->input = PREVIEW_INPUT_NONE;
+ }
+ break;
+
+ case PREV_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ /* read from ccdc */
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (prev->input == PREVIEW_INPUT_MEMORY)
+ return -EBUSY;
+ prev->input = PREVIEW_INPUT_CCDC;
+ } else {
+ if (prev->input == PREVIEW_INPUT_CCDC)
+ prev->input = PREVIEW_INPUT_NONE;
+ }
+ break;
+
+ /*
+ * The ISP core doesn't support pipelines with multiple video outputs.
+ * Revisit this when it will be implemented, and return -EBUSY for now.
+ */
+
+ case PREV_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
+ /* write to memory */
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (prev->output & ~PREVIEW_OUTPUT_MEMORY)
+ return -EBUSY;
+ prev->output |= PREVIEW_OUTPUT_MEMORY;
+ } else {
+ prev->output &= ~PREVIEW_OUTPUT_MEMORY;
+ }
+ break;
+
+ case PREV_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
+ /* write to resizer */
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (prev->output & ~PREVIEW_OUTPUT_RESIZER)
+ return -EBUSY;
+ prev->output |= PREVIEW_OUTPUT_RESIZER;
+ } else {
+ prev->output &= ~PREVIEW_OUTPUT_RESIZER;
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* media operations */
+static const struct media_entity_operations preview_media_ops = {
+ .link_setup = preview_link_setup,
+};
+
+void omap3isp_preview_unregister_entities(struct isp_prev_device *prev)
+{
+ v4l2_device_unregister_subdev(&prev->subdev);
+ omap3isp_video_unregister(&prev->video_in);
+ omap3isp_video_unregister(&prev->video_out);
+}
+
+int omap3isp_preview_register_entities(struct isp_prev_device *prev,
+ struct v4l2_device *vdev)
+{
+ int ret;
+
+ /* Register the subdev and video nodes. */
+ ret = v4l2_device_register_subdev(vdev, &prev->subdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap3isp_video_register(&prev->video_in, vdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap3isp_video_register(&prev->video_out, vdev);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ omap3isp_preview_unregister_entities(prev);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP previewer initialisation and cleanup
+ */
+
+/*
+ * preview_init_entities - Initialize subdev and media entity.
+ * @prev : Pointer to preview structure
+ * return -ENOMEM or zero on success
+ */
+static int preview_init_entities(struct isp_prev_device *prev)
+{
+ struct v4l2_subdev *sd = &prev->subdev;
+ struct media_pad *pads = prev->pads;
+ struct media_entity *me = &sd->entity;
+ int ret;
+
+ prev->input = PREVIEW_INPUT_NONE;
+
+ v4l2_subdev_init(sd, &preview_v4l2_ops);
+ sd->internal_ops = &preview_v4l2_internal_ops;
+ strlcpy(sd->name, "OMAP3 ISP preview", sizeof(sd->name));
+ sd->grp_id = 1 << 16; /* group ID for isp subdevs */
+ v4l2_set_subdevdata(sd, prev);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ v4l2_ctrl_handler_init(&prev->ctrls, 2);
+ v4l2_ctrl_new_std(&prev->ctrls, &preview_ctrl_ops, V4L2_CID_BRIGHTNESS,
+ ISPPRV_BRIGHT_LOW, ISPPRV_BRIGHT_HIGH,
+ ISPPRV_BRIGHT_STEP, ISPPRV_BRIGHT_DEF);
+ v4l2_ctrl_new_std(&prev->ctrls, &preview_ctrl_ops, V4L2_CID_CONTRAST,
+ ISPPRV_CONTRAST_LOW, ISPPRV_CONTRAST_HIGH,
+ ISPPRV_CONTRAST_STEP, ISPPRV_CONTRAST_DEF);
+ v4l2_ctrl_handler_setup(&prev->ctrls);
+ sd->ctrl_handler = &prev->ctrls;
+
+ pads[PREV_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[PREV_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+
+ me->ops = &preview_media_ops;
+ ret = media_entity_init(me, PREV_PADS_NUM, pads, 0);
+ if (ret < 0)
+ return ret;
+
+ preview_init_formats(sd, NULL);
+
+ /* According to the OMAP34xx TRM, video buffers need to be aligned on a
+ * 32 bytes boundary. However, an undocumented hardware bug requires a
+ * 64 bytes boundary at the preview engine input.
+ */
+ prev->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ prev->video_in.ops = &preview_video_ops;
+ prev->video_in.isp = to_isp_device(prev);
+ prev->video_in.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3;
+ prev->video_in.bpl_alignment = 64;
+ prev->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ prev->video_out.ops = &preview_video_ops;
+ prev->video_out.isp = to_isp_device(prev);
+ prev->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3;
+ prev->video_out.bpl_alignment = 32;
+
+ ret = omap3isp_video_init(&prev->video_in, "preview");
+ if (ret < 0)
+ goto error_video_in;
+
+ ret = omap3isp_video_init(&prev->video_out, "preview");
+ if (ret < 0)
+ goto error_video_out;
+
+ /* Connect the video nodes to the previewer subdev. */
+ ret = media_entity_create_link(&prev->video_in.video.entity, 0,
+ &prev->subdev.entity, PREV_PAD_SINK, 0);
+ if (ret < 0)
+ goto error_link;
+
+ ret = media_entity_create_link(&prev->subdev.entity, PREV_PAD_SOURCE,
+ &prev->video_out.video.entity, 0, 0);
+ if (ret < 0)
+ goto error_link;
+
+ return 0;
+
+error_link:
+ omap3isp_video_cleanup(&prev->video_out);
+error_video_out:
+ omap3isp_video_cleanup(&prev->video_in);
+error_video_in:
+ media_entity_cleanup(&prev->subdev.entity);
+ return ret;
+}
+
+/*
+ * isp_preview_init - Previewer initialization.
+ * @dev : Pointer to ISP device
+ * return -ENOMEM or zero on success
+ */
+int omap3isp_preview_init(struct isp_device *isp)
+{
+ struct isp_prev_device *prev = &isp->isp_prev;
+
+ spin_lock_init(&prev->lock);
+ init_waitqueue_head(&prev->wait);
+ preview_init_params(prev);
+
+ return preview_init_entities(prev);
+}
+
+void omap3isp_preview_cleanup(struct isp_device *isp)
+{
+ struct isp_prev_device *prev = &isp->isp_prev;
+
+ v4l2_ctrl_handler_free(&prev->ctrls);
+ omap3isp_video_cleanup(&prev->video_in);
+ omap3isp_video_cleanup(&prev->video_out);
+ media_entity_cleanup(&prev->subdev.entity);
+}
diff --git a/drivers/media/video/omap3isp/isppreview.h b/drivers/media/video/omap3isp/isppreview.h
new file mode 100644
index 00000000..09686607
--- /dev/null
+++ b/drivers/media/video/omap3isp/isppreview.h
@@ -0,0 +1,207 @@
+/*
+ * isppreview.h
+ *
+ * TI OMAP3 ISP - Preview module
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP3_ISP_PREVIEW_H
+#define OMAP3_ISP_PREVIEW_H
+
+#include <linux/omap3isp.h>
+#include <linux/types.h>
+#include <media/v4l2-ctrls.h>
+
+#include "ispvideo.h"
+
+#define ISPPRV_BRIGHT_STEP 0x1
+#define ISPPRV_BRIGHT_DEF 0x0
+#define ISPPRV_BRIGHT_LOW 0x0
+#define ISPPRV_BRIGHT_HIGH 0xFF
+#define ISPPRV_BRIGHT_UNITS 0x1
+
+#define ISPPRV_CONTRAST_STEP 0x1
+#define ISPPRV_CONTRAST_DEF 0x10
+#define ISPPRV_CONTRAST_LOW 0x0
+#define ISPPRV_CONTRAST_HIGH 0xFF
+#define ISPPRV_CONTRAST_UNITS 0x1
+
+/* Features list */
+#define PREV_LUMA_ENHANCE OMAP3ISP_PREV_LUMAENH
+#define PREV_INVERSE_ALAW OMAP3ISP_PREV_INVALAW
+#define PREV_HORZ_MEDIAN_FILTER OMAP3ISP_PREV_HRZ_MED
+#define PREV_CFA OMAP3ISP_PREV_CFA
+#define PREV_CHROMA_SUPPRESS OMAP3ISP_PREV_CHROMA_SUPP
+#define PREV_WB OMAP3ISP_PREV_WB
+#define PREV_BLKADJ OMAP3ISP_PREV_BLKADJ
+#define PREV_RGB2RGB OMAP3ISP_PREV_RGB2RGB
+#define PREV_COLOR_CONV OMAP3ISP_PREV_COLOR_CONV
+#define PREV_YCLIMITS OMAP3ISP_PREV_YC_LIMIT
+#define PREV_DEFECT_COR OMAP3ISP_PREV_DEFECT_COR
+#define PREV_GAMMA_BYPASS OMAP3ISP_PREV_GAMMABYPASS
+#define PREV_DARK_FRAME_CAPTURE OMAP3ISP_PREV_DRK_FRM_CAPTURE
+#define PREV_DARK_FRAME_SUBTRACT OMAP3ISP_PREV_DRK_FRM_SUBTRACT
+#define PREV_LENS_SHADING OMAP3ISP_PREV_LENS_SHADING
+#define PREV_NOISE_FILTER OMAP3ISP_PREV_NF
+#define PREV_GAMMA OMAP3ISP_PREV_GAMMA
+
+#define PREV_CONTRAST (1 << 17)
+#define PREV_BRIGHTNESS (1 << 18)
+#define PREV_AVERAGER (1 << 19)
+#define PREV_FEATURES_END (1 << 20)
+
+enum preview_input_entity {
+ PREVIEW_INPUT_NONE,
+ PREVIEW_INPUT_CCDC,
+ PREVIEW_INPUT_MEMORY,
+};
+
+#define PREVIEW_OUTPUT_RESIZER (1 << 1)
+#define PREVIEW_OUTPUT_MEMORY (1 << 2)
+
+/* Configure byte layout of YUV image */
+enum preview_ycpos_mode {
+ YCPOS_YCrYCb = 0,
+ YCPOS_YCbYCr = 1,
+ YCPOS_CbYCrY = 2,
+ YCPOS_CrYCbY = 3
+};
+
+/*
+ * struct prev_params - Structure for all configuration
+ * @features: Set of features enabled.
+ * @cfa: CFA coefficients.
+ * @csup: Chroma suppression coefficients.
+ * @luma: Luma enhancement coefficients.
+ * @nf: Noise filter coefficients.
+ * @dcor: Noise filter coefficients.
+ * @gamma: Gamma coefficients.
+ * @wbal: White Balance parameters.
+ * @blk_adj: Black adjustment parameters.
+ * @rgb2rgb: RGB blending parameters.
+ * @rgb2ycbcr: RGB to ycbcr parameters.
+ * @hmed: Horizontal median filter.
+ * @yclimit: YC limits parameters.
+ * @contrast: Contrast.
+ * @brightness: Brightness.
+ */
+struct prev_params {
+ u32 features;
+ struct omap3isp_prev_cfa cfa;
+ struct omap3isp_prev_csup csup;
+ struct omap3isp_prev_luma luma;
+ struct omap3isp_prev_nf nf;
+ struct omap3isp_prev_dcor dcor;
+ struct omap3isp_prev_gtables gamma;
+ struct omap3isp_prev_wbal wbal;
+ struct omap3isp_prev_blkadj blk_adj;
+ struct omap3isp_prev_rgbtorgb rgb2rgb;
+ struct omap3isp_prev_csc rgb2ycbcr;
+ struct omap3isp_prev_hmed hmed;
+ struct omap3isp_prev_yclimit yclimit;
+ u8 contrast;
+ u8 brightness;
+};
+
+/*
+ * struct isptables_update - Structure for Table Configuration.
+ * @update: Specifies which tables should be updated.
+ * @flag: Specifies which tables should be enabled.
+ * @nf: Pointer to structure for Noise Filter
+ * @lsc: Pointer to LSC gain table. (currently not used)
+ * @gamma: Pointer to gamma correction tables.
+ * @cfa: Pointer to color filter array configuration.
+ * @wbal: Pointer to colour and digital gain configuration.
+ */
+struct isptables_update {
+ u32 update;
+ u32 flag;
+ struct omap3isp_prev_nf *nf;
+ u32 *lsc;
+ struct omap3isp_prev_gtables *gamma;
+ struct omap3isp_prev_cfa *cfa;
+ struct omap3isp_prev_wbal *wbal;
+};
+
+/* Sink and source previewer pads */
+#define PREV_PAD_SINK 0
+#define PREV_PAD_SOURCE 1
+#define PREV_PADS_NUM 2
+
+/*
+ * struct isp_prev_device - Structure for storing ISP Preview module information
+ * @subdev: V4L2 subdevice
+ * @pads: Media entity pads
+ * @formats: Active formats at the subdev pad
+ * @crop: Active crop rectangle
+ * @input: Module currently connected to the input pad
+ * @output: Bitmask of the active output
+ * @video_in: Input video entity
+ * @video_out: Output video entity
+ * @params: Module configuration data
+ * @shadow_update: If set, update the hardware configured in the next interrupt
+ * @underrun: Whether the preview entity has queued buffers on the output
+ * @state: Current preview pipeline state
+ * @lock: Shadow update lock
+ * @update: Bitmask of the parameters to be updated
+ *
+ * This structure is used to store the OMAP ISP Preview module Information.
+ */
+struct isp_prev_device {
+ struct v4l2_subdev subdev;
+ struct media_pad pads[PREV_PADS_NUM];
+ struct v4l2_mbus_framefmt formats[PREV_PADS_NUM];
+ struct v4l2_rect crop;
+
+ struct v4l2_ctrl_handler ctrls;
+
+ enum preview_input_entity input;
+ unsigned int output;
+ struct isp_video video_in;
+ struct isp_video video_out;
+
+ struct prev_params params;
+ unsigned int shadow_update:1;
+ enum isp_pipeline_stream_state state;
+ wait_queue_head_t wait;
+ atomic_t stopping;
+ spinlock_t lock;
+ u32 update;
+};
+
+struct isp_device;
+
+int omap3isp_preview_init(struct isp_device *isp);
+void omap3isp_preview_cleanup(struct isp_device *isp);
+
+int omap3isp_preview_register_entities(struct isp_prev_device *prv,
+ struct v4l2_device *vdev);
+void omap3isp_preview_unregister_entities(struct isp_prev_device *prv);
+
+void omap3isp_preview_isr_frame_sync(struct isp_prev_device *prev);
+void omap3isp_preview_isr(struct isp_prev_device *prev);
+
+int omap3isp_preview_busy(struct isp_prev_device *isp_prev);
+
+void omap3isp_preview_restore_context(struct isp_device *isp);
+
+#endif /* OMAP3_ISP_PREVIEW_H */
diff --git a/drivers/media/video/omap3isp/ispqueue.c b/drivers/media/video/omap3isp/ispqueue.c
new file mode 100644
index 00000000..9bebb1e5
--- /dev/null
+++ b/drivers/media/video/omap3isp/ispqueue.c
@@ -0,0 +1,1157 @@
+/*
+ * ispqueue.c
+ *
+ * TI OMAP3 ISP - Video buffers queue handling
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <asm/cacheflush.h>
+#include <linux/dma-mapping.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/poll.h>
+#include <linux/scatterlist.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include "ispqueue.h"
+
+/* -----------------------------------------------------------------------------
+ * Video buffers management
+ */
+
+/*
+ * isp_video_buffer_cache_sync - Keep the buffers coherent between CPU and ISP
+ *
+ * The typical operation required here is Cache Invalidation across
+ * the (user space) buffer address range. And this _must_ be done
+ * at QBUF stage (and *only* at QBUF).
+ *
+ * We try to use optimal cache invalidation function:
+ * - dmac_map_area:
+ * - used when the number of pages are _low_.
+ * - it becomes quite slow as the number of pages increase.
+ * - for 648x492 viewfinder (150 pages) it takes 1.3 ms.
+ * - for 5 Mpix buffer (2491 pages) it takes between 25-50 ms.
+ *
+ * - flush_cache_all:
+ * - used when the number of pages are _high_.
+ * - time taken in the range of 500-900 us.
+ * - has a higher penalty but, as whole dcache + icache is invalidated
+ */
+/*
+ * FIXME: dmac_inv_range crashes randomly on the user space buffer
+ * address. Fall back to flush_cache_all for now.
+ */
+#define ISP_CACHE_FLUSH_PAGES_MAX 0
+
+static void isp_video_buffer_cache_sync(struct isp_video_buffer *buf)
+{
+ if (buf->skip_cache)
+ return;
+
+ if (buf->vbuf.m.userptr == 0 || buf->npages == 0 ||
+ buf->npages > ISP_CACHE_FLUSH_PAGES_MAX)
+ flush_cache_all();
+ else {
+ dmac_map_area((void *)buf->vbuf.m.userptr, buf->vbuf.length,
+ DMA_FROM_DEVICE);
+ outer_inv_range(buf->vbuf.m.userptr,
+ buf->vbuf.m.userptr + buf->vbuf.length);
+ }
+}
+
+/*
+ * isp_video_buffer_lock_vma - Prevent VMAs from being unmapped
+ *
+ * Lock the VMAs underlying the given buffer into memory. This avoids the
+ * userspace buffer mapping from being swapped out, making VIPT cache handling
+ * easier.
+ *
+ * Note that the pages will not be freed as the buffers have been locked to
+ * memory using by a call to get_user_pages(), but the userspace mapping could
+ * still disappear if the VMAs are not locked. This is caused by the memory
+ * management code trying to be as lock-less as possible, which results in the
+ * userspace mapping manager not finding out that the pages are locked under
+ * some conditions.
+ */
+static int isp_video_buffer_lock_vma(struct isp_video_buffer *buf, int lock)
+{
+ struct vm_area_struct *vma;
+ unsigned long start;
+ unsigned long end;
+ int ret = 0;
+
+ if (buf->vbuf.memory == V4L2_MEMORY_MMAP)
+ return 0;
+
+ /* We can be called from workqueue context if the current task dies to
+ * unlock the VMAs. In that case there's no current memory management
+ * context so unlocking can't be performed, but the VMAs have been or
+ * are getting destroyed anyway so it doesn't really matter.
+ */
+ if (!current || !current->mm)
+ return lock ? -EINVAL : 0;
+
+ start = buf->vbuf.m.userptr;
+ end = buf->vbuf.m.userptr + buf->vbuf.length - 1;
+
+ down_write(&current->mm->mmap_sem);
+ spin_lock(&current->mm->page_table_lock);
+
+ do {
+ vma = find_vma(current->mm, start);
+ if (vma == NULL) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ if (lock)
+ vma->vm_flags |= VM_LOCKED;
+ else
+ vma->vm_flags &= ~VM_LOCKED;
+
+ start = vma->vm_end + 1;
+ } while (vma->vm_end < end);
+
+ if (lock)
+ buf->vm_flags |= VM_LOCKED;
+ else
+ buf->vm_flags &= ~VM_LOCKED;
+
+out:
+ spin_unlock(&current->mm->page_table_lock);
+ up_write(&current->mm->mmap_sem);
+ return ret;
+}
+
+/*
+ * isp_video_buffer_sglist_kernel - Build a scatter list for a vmalloc'ed buffer
+ *
+ * Iterate over the vmalloc'ed area and create a scatter list entry for every
+ * page.
+ */
+static int isp_video_buffer_sglist_kernel(struct isp_video_buffer *buf)
+{
+ struct scatterlist *sglist;
+ unsigned int npages;
+ unsigned int i;
+ void *addr;
+
+ addr = buf->vaddr;
+ npages = PAGE_ALIGN(buf->vbuf.length) >> PAGE_SHIFT;
+
+ sglist = vmalloc(npages * sizeof(*sglist));
+ if (sglist == NULL)
+ return -ENOMEM;
+
+ sg_init_table(sglist, npages);
+
+ for (i = 0; i < npages; ++i, addr += PAGE_SIZE) {
+ struct page *page = vmalloc_to_page(addr);
+
+ if (page == NULL || PageHighMem(page)) {
+ vfree(sglist);
+ return -EINVAL;
+ }
+
+ sg_set_page(&sglist[i], page, PAGE_SIZE, 0);
+ }
+
+ buf->sglen = npages;
+ buf->sglist = sglist;
+
+ return 0;
+}
+
+/*
+ * isp_video_buffer_sglist_user - Build a scatter list for a userspace buffer
+ *
+ * Walk the buffer pages list and create a 1:1 mapping to a scatter list.
+ */
+static int isp_video_buffer_sglist_user(struct isp_video_buffer *buf)
+{
+ struct scatterlist *sglist;
+ unsigned int offset = buf->offset;
+ unsigned int i;
+
+ sglist = vmalloc(buf->npages * sizeof(*sglist));
+ if (sglist == NULL)
+ return -ENOMEM;
+
+ sg_init_table(sglist, buf->npages);
+
+ for (i = 0; i < buf->npages; ++i) {
+ if (PageHighMem(buf->pages[i])) {
+ vfree(sglist);
+ return -EINVAL;
+ }
+
+ sg_set_page(&sglist[i], buf->pages[i], PAGE_SIZE - offset,
+ offset);
+ offset = 0;
+ }
+
+ buf->sglen = buf->npages;
+ buf->sglist = sglist;
+
+ return 0;
+}
+
+/*
+ * isp_video_buffer_sglist_pfnmap - Build a scatter list for a VM_PFNMAP buffer
+ *
+ * Create a scatter list of physically contiguous pages starting at the buffer
+ * memory physical address.
+ */
+static int isp_video_buffer_sglist_pfnmap(struct isp_video_buffer *buf)
+{
+ struct scatterlist *sglist;
+ unsigned int offset = buf->offset;
+ unsigned long pfn = buf->paddr >> PAGE_SHIFT;
+ unsigned int i;
+
+ sglist = vmalloc(buf->npages * sizeof(*sglist));
+ if (sglist == NULL)
+ return -ENOMEM;
+
+ sg_init_table(sglist, buf->npages);
+
+ for (i = 0; i < buf->npages; ++i, ++pfn) {
+ sg_set_page(&sglist[i], pfn_to_page(pfn), PAGE_SIZE - offset,
+ offset);
+ /* PFNMAP buffers will not get DMA-mapped, set the DMA address
+ * manually.
+ */
+ sg_dma_address(&sglist[i]) = (pfn << PAGE_SHIFT) + offset;
+ offset = 0;
+ }
+
+ buf->sglen = buf->npages;
+ buf->sglist = sglist;
+
+ return 0;
+}
+
+/*
+ * isp_video_buffer_cleanup - Release pages for a userspace VMA.
+ *
+ * Release pages locked by a call isp_video_buffer_prepare_user and free the
+ * pages table.
+ */
+static void isp_video_buffer_cleanup(struct isp_video_buffer *buf)
+{
+ enum dma_data_direction direction;
+ unsigned int i;
+
+ if (buf->queue->ops->buffer_cleanup)
+ buf->queue->ops->buffer_cleanup(buf);
+
+ if (!(buf->vm_flags & VM_PFNMAP)) {
+ direction = buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE
+ ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+ dma_unmap_sg(buf->queue->dev, buf->sglist, buf->sglen,
+ direction);
+ }
+
+ vfree(buf->sglist);
+ buf->sglist = NULL;
+ buf->sglen = 0;
+
+ if (buf->pages != NULL) {
+ isp_video_buffer_lock_vma(buf, 0);
+
+ for (i = 0; i < buf->npages; ++i)
+ page_cache_release(buf->pages[i]);
+
+ vfree(buf->pages);
+ buf->pages = NULL;
+ }
+
+ buf->npages = 0;
+ buf->skip_cache = false;
+}
+
+/*
+ * isp_video_buffer_prepare_user - Pin userspace VMA pages to memory.
+ *
+ * This function creates a list of pages for a userspace VMA. The number of
+ * pages is first computed based on the buffer size, and pages are then
+ * retrieved by a call to get_user_pages.
+ *
+ * Pages are pinned to memory by get_user_pages, making them available for DMA
+ * transfers. However, due to memory management optimization, it seems the
+ * get_user_pages doesn't guarantee that the pinned pages will not be written
+ * to swap and removed from the userspace mapping(s). When this happens, a page
+ * fault can be generated when accessing those unmapped pages.
+ *
+ * If the fault is triggered by a page table walk caused by VIPT cache
+ * management operations, the page fault handler might oops if the MM semaphore
+ * is held, as it can't handle kernel page faults in that case. To fix that, a
+ * fixup entry needs to be added to the cache management code, or the userspace
+ * VMA must be locked to avoid removing pages from the userspace mapping in the
+ * first place.
+ *
+ * If the number of pages retrieved is smaller than the number required by the
+ * buffer size, the function returns -EFAULT.
+ */
+static int isp_video_buffer_prepare_user(struct isp_video_buffer *buf)
+{
+ unsigned long data;
+ unsigned int first;
+ unsigned int last;
+ int ret;
+
+ data = buf->vbuf.m.userptr;
+ first = (data & PAGE_MASK) >> PAGE_SHIFT;
+ last = ((data + buf->vbuf.length - 1) & PAGE_MASK) >> PAGE_SHIFT;
+
+ buf->offset = data & ~PAGE_MASK;
+ buf->npages = last - first + 1;
+ buf->pages = vmalloc(buf->npages * sizeof(buf->pages[0]));
+ if (buf->pages == NULL)
+ return -ENOMEM;
+
+ down_read(&current->mm->mmap_sem);
+ ret = get_user_pages(current, current->mm, data & PAGE_MASK,
+ buf->npages,
+ buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE, 0,
+ buf->pages, NULL);
+ up_read(&current->mm->mmap_sem);
+
+ if (ret != buf->npages) {
+ buf->npages = ret < 0 ? 0 : ret;
+ isp_video_buffer_cleanup(buf);
+ return -EFAULT;
+ }
+
+ ret = isp_video_buffer_lock_vma(buf, 1);
+ if (ret < 0)
+ isp_video_buffer_cleanup(buf);
+
+ return ret;
+}
+
+/*
+ * isp_video_buffer_prepare_pfnmap - Validate a VM_PFNMAP userspace buffer
+ *
+ * Userspace VM_PFNMAP buffers are supported only if they are contiguous in
+ * memory and if they span a single VMA.
+ *
+ * Return 0 if the buffer is valid, or -EFAULT otherwise.
+ */
+static int isp_video_buffer_prepare_pfnmap(struct isp_video_buffer *buf)
+{
+ struct vm_area_struct *vma;
+ unsigned long prev_pfn;
+ unsigned long this_pfn;
+ unsigned long start;
+ unsigned long end;
+ dma_addr_t pa;
+ int ret = -EFAULT;
+
+ start = buf->vbuf.m.userptr;
+ end = buf->vbuf.m.userptr + buf->vbuf.length - 1;
+
+ buf->offset = start & ~PAGE_MASK;
+ buf->npages = (end >> PAGE_SHIFT) - (start >> PAGE_SHIFT) + 1;
+ buf->pages = NULL;
+
+ down_read(&current->mm->mmap_sem);
+ vma = find_vma(current->mm, start);
+ if (vma == NULL || vma->vm_end < end)
+ goto done;
+
+ for (prev_pfn = 0; start <= end; start += PAGE_SIZE) {
+ ret = follow_pfn(vma, start, &this_pfn);
+ if (ret)
+ goto done;
+
+ if (prev_pfn == 0)
+ pa = this_pfn << PAGE_SHIFT;
+ else if (this_pfn != prev_pfn + 1) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ prev_pfn = this_pfn;
+ }
+
+ buf->paddr = pa + buf->offset;
+ ret = 0;
+
+done:
+ up_read(&current->mm->mmap_sem);
+ return ret;
+}
+
+/*
+ * isp_video_buffer_prepare_vm_flags - Get VMA flags for a userspace address
+ *
+ * This function locates the VMAs for the buffer's userspace address and checks
+ * that their flags match. The only flag that we need to care for at the moment
+ * is VM_PFNMAP.
+ *
+ * The buffer vm_flags field is set to the first VMA flags.
+ *
+ * Return -EFAULT if no VMA can be found for part of the buffer, or if the VMAs
+ * have incompatible flags.
+ */
+static int isp_video_buffer_prepare_vm_flags(struct isp_video_buffer *buf)
+{
+ struct vm_area_struct *vma;
+ pgprot_t vm_page_prot;
+ unsigned long start;
+ unsigned long end;
+ int ret = -EFAULT;
+
+ start = buf->vbuf.m.userptr;
+ end = buf->vbuf.m.userptr + buf->vbuf.length - 1;
+
+ down_read(&current->mm->mmap_sem);
+
+ do {
+ vma = find_vma(current->mm, start);
+ if (vma == NULL)
+ goto done;
+
+ if (start == buf->vbuf.m.userptr) {
+ buf->vm_flags = vma->vm_flags;
+ vm_page_prot = vma->vm_page_prot;
+ }
+
+ if ((buf->vm_flags ^ vma->vm_flags) & VM_PFNMAP)
+ goto done;
+
+ if (vm_page_prot != vma->vm_page_prot)
+ goto done;
+
+ start = vma->vm_end + 1;
+ } while (vma->vm_end < end);
+
+ /* Skip cache management to enhance performances for non-cached or
+ * write-combining buffers.
+ */
+ if (vm_page_prot == pgprot_noncached(vm_page_prot) ||
+ vm_page_prot == pgprot_writecombine(vm_page_prot))
+ buf->skip_cache = true;
+
+ ret = 0;
+
+done:
+ up_read(&current->mm->mmap_sem);
+ return ret;
+}
+
+/*
+ * isp_video_buffer_prepare - Make a buffer ready for operation
+ *
+ * Preparing a buffer involves:
+ *
+ * - validating VMAs (userspace buffers only)
+ * - locking pages and VMAs into memory (userspace buffers only)
+ * - building page and scatter-gather lists
+ * - mapping buffers for DMA operation
+ * - performing driver-specific preparation
+ *
+ * The function must be called in userspace context with a valid mm context
+ * (this excludes cleanup paths such as sys_close when the userspace process
+ * segfaults).
+ */
+static int isp_video_buffer_prepare(struct isp_video_buffer *buf)
+{
+ enum dma_data_direction direction;
+ int ret;
+
+ switch (buf->vbuf.memory) {
+ case V4L2_MEMORY_MMAP:
+ ret = isp_video_buffer_sglist_kernel(buf);
+ break;
+
+ case V4L2_MEMORY_USERPTR:
+ ret = isp_video_buffer_prepare_vm_flags(buf);
+ if (ret < 0)
+ return ret;
+
+ if (buf->vm_flags & VM_PFNMAP) {
+ ret = isp_video_buffer_prepare_pfnmap(buf);
+ if (ret < 0)
+ return ret;
+
+ ret = isp_video_buffer_sglist_pfnmap(buf);
+ } else {
+ ret = isp_video_buffer_prepare_user(buf);
+ if (ret < 0)
+ return ret;
+
+ ret = isp_video_buffer_sglist_user(buf);
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (ret < 0)
+ goto done;
+
+ if (!(buf->vm_flags & VM_PFNMAP)) {
+ direction = buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE
+ ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+ ret = dma_map_sg(buf->queue->dev, buf->sglist, buf->sglen,
+ direction);
+ if (ret != buf->sglen) {
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+
+ if (buf->queue->ops->buffer_prepare)
+ ret = buf->queue->ops->buffer_prepare(buf);
+
+done:
+ if (ret < 0) {
+ isp_video_buffer_cleanup(buf);
+ return ret;
+ }
+
+ return ret;
+}
+
+/*
+ * isp_video_queue_query - Query the status of a given buffer
+ *
+ * Locking: must be called with the queue lock held.
+ */
+static void isp_video_buffer_query(struct isp_video_buffer *buf,
+ struct v4l2_buffer *vbuf)
+{
+ memcpy(vbuf, &buf->vbuf, sizeof(*vbuf));
+
+ if (buf->vma_use_count)
+ vbuf->flags |= V4L2_BUF_FLAG_MAPPED;
+
+ switch (buf->state) {
+ case ISP_BUF_STATE_ERROR:
+ vbuf->flags |= V4L2_BUF_FLAG_ERROR;
+ case ISP_BUF_STATE_DONE:
+ vbuf->flags |= V4L2_BUF_FLAG_DONE;
+ case ISP_BUF_STATE_QUEUED:
+ case ISP_BUF_STATE_ACTIVE:
+ vbuf->flags |= V4L2_BUF_FLAG_QUEUED;
+ break;
+ case ISP_BUF_STATE_IDLE:
+ default:
+ break;
+ }
+}
+
+/*
+ * isp_video_buffer_wait - Wait for a buffer to be ready
+ *
+ * In non-blocking mode, return immediately with 0 if the buffer is ready or
+ * -EAGAIN if the buffer is in the QUEUED or ACTIVE state.
+ *
+ * In blocking mode, wait (interruptibly but with no timeout) on the buffer wait
+ * queue using the same condition.
+ */
+static int isp_video_buffer_wait(struct isp_video_buffer *buf, int nonblocking)
+{
+ if (nonblocking) {
+ return (buf->state != ISP_BUF_STATE_QUEUED &&
+ buf->state != ISP_BUF_STATE_ACTIVE)
+ ? 0 : -EAGAIN;
+ }
+
+ return wait_event_interruptible(buf->wait,
+ buf->state != ISP_BUF_STATE_QUEUED &&
+ buf->state != ISP_BUF_STATE_ACTIVE);
+}
+
+/* -----------------------------------------------------------------------------
+ * Queue management
+ */
+
+/*
+ * isp_video_queue_free - Free video buffers memory
+ *
+ * Buffers can only be freed if the queue isn't streaming and if no buffer is
+ * mapped to userspace. Return -EBUSY if those conditions aren't statisfied.
+ *
+ * This function must be called with the queue lock held.
+ */
+static int isp_video_queue_free(struct isp_video_queue *queue)
+{
+ unsigned int i;
+
+ if (queue->streaming)
+ return -EBUSY;
+
+ for (i = 0; i < queue->count; ++i) {
+ if (queue->buffers[i]->vma_use_count != 0)
+ return -EBUSY;
+ }
+
+ for (i = 0; i < queue->count; ++i) {
+ struct isp_video_buffer *buf = queue->buffers[i];
+
+ isp_video_buffer_cleanup(buf);
+
+ vfree(buf->vaddr);
+ buf->vaddr = NULL;
+
+ kfree(buf);
+ queue->buffers[i] = NULL;
+ }
+
+ INIT_LIST_HEAD(&queue->queue);
+ queue->count = 0;
+ return 0;
+}
+
+/*
+ * isp_video_queue_alloc - Allocate video buffers memory
+ *
+ * This function must be called with the queue lock held.
+ */
+static int isp_video_queue_alloc(struct isp_video_queue *queue,
+ unsigned int nbuffers,
+ unsigned int size, enum v4l2_memory memory)
+{
+ struct isp_video_buffer *buf;
+ unsigned int i;
+ void *mem;
+ int ret;
+
+ /* Start by freeing the buffers. */
+ ret = isp_video_queue_free(queue);
+ if (ret < 0)
+ return ret;
+
+ /* Bail out of no buffers should be allocated. */
+ if (nbuffers == 0)
+ return 0;
+
+ /* Initialize the allocated buffers. */
+ for (i = 0; i < nbuffers; ++i) {
+ buf = kzalloc(queue->bufsize, GFP_KERNEL);
+ if (buf == NULL)
+ break;
+
+ if (memory == V4L2_MEMORY_MMAP) {
+ /* Allocate video buffers memory for mmap mode. Align
+ * the size to the page size.
+ */
+ mem = vmalloc_32_user(PAGE_ALIGN(size));
+ if (mem == NULL) {
+ kfree(buf);
+ break;
+ }
+
+ buf->vbuf.m.offset = i * PAGE_ALIGN(size);
+ buf->vaddr = mem;
+ }
+
+ buf->vbuf.index = i;
+ buf->vbuf.length = size;
+ buf->vbuf.type = queue->type;
+ buf->vbuf.field = V4L2_FIELD_NONE;
+ buf->vbuf.memory = memory;
+
+ buf->queue = queue;
+ init_waitqueue_head(&buf->wait);
+
+ queue->buffers[i] = buf;
+ }
+
+ if (i == 0)
+ return -ENOMEM;
+
+ queue->count = i;
+ return nbuffers;
+}
+
+/**
+ * omap3isp_video_queue_cleanup - Clean up the video buffers queue
+ * @queue: Video buffers queue
+ *
+ * Free all allocated resources and clean up the video buffers queue. The queue
+ * must not be busy (no ongoing video stream) and buffers must have been
+ * unmapped.
+ *
+ * Return 0 on success or -EBUSY if the queue is busy or buffers haven't been
+ * unmapped.
+ */
+int omap3isp_video_queue_cleanup(struct isp_video_queue *queue)
+{
+ return isp_video_queue_free(queue);
+}
+
+/**
+ * omap3isp_video_queue_init - Initialize the video buffers queue
+ * @queue: Video buffers queue
+ * @type: V4L2 buffer type (capture or output)
+ * @ops: Driver-specific queue operations
+ * @dev: Device used for DMA operations
+ * @bufsize: Size of the driver-specific buffer structure
+ *
+ * Initialize the video buffers queue with the supplied parameters.
+ *
+ * The queue type must be one of V4L2_BUF_TYPE_VIDEO_CAPTURE or
+ * V4L2_BUF_TYPE_VIDEO_OUTPUT. Other buffer types are not supported yet.
+ *
+ * Buffer objects will be allocated using the given buffer size to allow room
+ * for driver-specific fields. Driver-specific buffer structures must start
+ * with a struct isp_video_buffer field. Drivers with no driver-specific buffer
+ * structure must pass the size of the isp_video_buffer structure in the bufsize
+ * parameter.
+ *
+ * Return 0 on success.
+ */
+int omap3isp_video_queue_init(struct isp_video_queue *queue,
+ enum v4l2_buf_type type,
+ const struct isp_video_queue_operations *ops,
+ struct device *dev, unsigned int bufsize)
+{
+ INIT_LIST_HEAD(&queue->queue);
+ mutex_init(&queue->lock);
+ spin_lock_init(&queue->irqlock);
+
+ queue->type = type;
+ queue->ops = ops;
+ queue->dev = dev;
+ queue->bufsize = bufsize;
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 operations
+ */
+
+/**
+ * omap3isp_video_queue_reqbufs - Allocate video buffers memory
+ *
+ * This function is intended to be used as a VIDIOC_REQBUFS ioctl handler. It
+ * allocated video buffer objects and, for MMAP buffers, buffer memory.
+ *
+ * If the number of buffers is 0, all buffers are freed and the function returns
+ * without performing any allocation.
+ *
+ * If the number of buffers is not 0, currently allocated buffers (if any) are
+ * freed and the requested number of buffers are allocated. Depending on
+ * driver-specific requirements and on memory availability, a number of buffer
+ * smaller or bigger than requested can be allocated. This isn't considered as
+ * an error.
+ *
+ * Return 0 on success or one of the following error codes:
+ *
+ * -EINVAL if the buffer type or index are invalid
+ * -EBUSY if the queue is busy (streaming or buffers mapped)
+ * -ENOMEM if the buffers can't be allocated due to an out-of-memory condition
+ */
+int omap3isp_video_queue_reqbufs(struct isp_video_queue *queue,
+ struct v4l2_requestbuffers *rb)
+{
+ unsigned int nbuffers = rb->count;
+ unsigned int size;
+ int ret;
+
+ if (rb->type != queue->type)
+ return -EINVAL;
+
+ queue->ops->queue_prepare(queue, &nbuffers, &size);
+ if (size == 0)
+ return -EINVAL;
+
+ nbuffers = min_t(unsigned int, nbuffers, ISP_VIDEO_MAX_BUFFERS);
+
+ mutex_lock(&queue->lock);
+
+ ret = isp_video_queue_alloc(queue, nbuffers, size, rb->memory);
+ if (ret < 0)
+ goto done;
+
+ rb->count = ret;
+ ret = 0;
+
+done:
+ mutex_unlock(&queue->lock);
+ return ret;
+}
+
+/**
+ * omap3isp_video_queue_querybuf - Query the status of a buffer in a queue
+ *
+ * This function is intended to be used as a VIDIOC_QUERYBUF ioctl handler. It
+ * returns the status of a given video buffer.
+ *
+ * Return 0 on success or -EINVAL if the buffer type or index are invalid.
+ */
+int omap3isp_video_queue_querybuf(struct isp_video_queue *queue,
+ struct v4l2_buffer *vbuf)
+{
+ struct isp_video_buffer *buf;
+ int ret = 0;
+
+ if (vbuf->type != queue->type)
+ return -EINVAL;
+
+ mutex_lock(&queue->lock);
+
+ if (vbuf->index >= queue->count) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ buf = queue->buffers[vbuf->index];
+ isp_video_buffer_query(buf, vbuf);
+
+done:
+ mutex_unlock(&queue->lock);
+ return ret;
+}
+
+/**
+ * omap3isp_video_queue_qbuf - Queue a buffer
+ *
+ * This function is intended to be used as a VIDIOC_QBUF ioctl handler.
+ *
+ * The v4l2_buffer structure passed from userspace is first sanity tested. If
+ * sane, the buffer is then processed and added to the main queue and, if the
+ * queue is streaming, to the IRQ queue.
+ *
+ * Before being enqueued, USERPTR buffers are checked for address changes. If
+ * the buffer has a different userspace address, the old memory area is unlocked
+ * and the new memory area is locked.
+ */
+int omap3isp_video_queue_qbuf(struct isp_video_queue *queue,
+ struct v4l2_buffer *vbuf)
+{
+ struct isp_video_buffer *buf;
+ unsigned long flags;
+ int ret = -EINVAL;
+
+ if (vbuf->type != queue->type)
+ goto done;
+
+ mutex_lock(&queue->lock);
+
+ if (vbuf->index >= queue->count)
+ goto done;
+
+ buf = queue->buffers[vbuf->index];
+
+ if (vbuf->memory != buf->vbuf.memory)
+ goto done;
+
+ if (buf->state != ISP_BUF_STATE_IDLE)
+ goto done;
+
+ if (vbuf->memory == V4L2_MEMORY_USERPTR &&
+ vbuf->length < buf->vbuf.length)
+ goto done;
+
+ if (vbuf->memory == V4L2_MEMORY_USERPTR &&
+ vbuf->m.userptr != buf->vbuf.m.userptr) {
+ isp_video_buffer_cleanup(buf);
+ buf->vbuf.m.userptr = vbuf->m.userptr;
+ buf->prepared = 0;
+ }
+
+ if (!buf->prepared) {
+ ret = isp_video_buffer_prepare(buf);
+ if (ret < 0)
+ goto done;
+ buf->prepared = 1;
+ }
+
+ isp_video_buffer_cache_sync(buf);
+
+ buf->state = ISP_BUF_STATE_QUEUED;
+ list_add_tail(&buf->stream, &queue->queue);
+
+ if (queue->streaming) {
+ spin_lock_irqsave(&queue->irqlock, flags);
+ queue->ops->buffer_queue(buf);
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+ }
+
+ ret = 0;
+
+done:
+ mutex_unlock(&queue->lock);
+ return ret;
+}
+
+/**
+ * omap3isp_video_queue_dqbuf - Dequeue a buffer
+ *
+ * This function is intended to be used as a VIDIOC_DQBUF ioctl handler.
+ *
+ * The v4l2_buffer structure passed from userspace is first sanity tested. If
+ * sane, the buffer is then processed and added to the main queue and, if the
+ * queue is streaming, to the IRQ queue.
+ *
+ * Before being enqueued, USERPTR buffers are checked for address changes. If
+ * the buffer has a different userspace address, the old memory area is unlocked
+ * and the new memory area is locked.
+ */
+int omap3isp_video_queue_dqbuf(struct isp_video_queue *queue,
+ struct v4l2_buffer *vbuf, int nonblocking)
+{
+ struct isp_video_buffer *buf;
+ int ret;
+
+ if (vbuf->type != queue->type)
+ return -EINVAL;
+
+ mutex_lock(&queue->lock);
+
+ if (list_empty(&queue->queue)) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ buf = list_first_entry(&queue->queue, struct isp_video_buffer, stream);
+ ret = isp_video_buffer_wait(buf, nonblocking);
+ if (ret < 0)
+ goto done;
+
+ list_del(&buf->stream);
+
+ isp_video_buffer_query(buf, vbuf);
+ buf->state = ISP_BUF_STATE_IDLE;
+ vbuf->flags &= ~V4L2_BUF_FLAG_QUEUED;
+
+done:
+ mutex_unlock(&queue->lock);
+ return ret;
+}
+
+/**
+ * omap3isp_video_queue_streamon - Start streaming
+ *
+ * This function is intended to be used as a VIDIOC_STREAMON ioctl handler. It
+ * starts streaming on the queue and calls the buffer_queue operation for all
+ * queued buffers.
+ *
+ * Return 0 on success.
+ */
+int omap3isp_video_queue_streamon(struct isp_video_queue *queue)
+{
+ struct isp_video_buffer *buf;
+ unsigned long flags;
+
+ mutex_lock(&queue->lock);
+
+ if (queue->streaming)
+ goto done;
+
+ queue->streaming = 1;
+
+ spin_lock_irqsave(&queue->irqlock, flags);
+ list_for_each_entry(buf, &queue->queue, stream)
+ queue->ops->buffer_queue(buf);
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+
+done:
+ mutex_unlock(&queue->lock);
+ return 0;
+}
+
+/**
+ * omap3isp_video_queue_streamoff - Stop streaming
+ *
+ * This function is intended to be used as a VIDIOC_STREAMOFF ioctl handler. It
+ * stops streaming on the queue and wakes up all the buffers.
+ *
+ * Drivers must stop the hardware and synchronize with interrupt handlers and/or
+ * delayed works before calling this function to make sure no buffer will be
+ * touched by the driver and/or hardware.
+ */
+void omap3isp_video_queue_streamoff(struct isp_video_queue *queue)
+{
+ struct isp_video_buffer *buf;
+ unsigned long flags;
+ unsigned int i;
+
+ mutex_lock(&queue->lock);
+
+ if (!queue->streaming)
+ goto done;
+
+ queue->streaming = 0;
+
+ spin_lock_irqsave(&queue->irqlock, flags);
+ for (i = 0; i < queue->count; ++i) {
+ buf = queue->buffers[i];
+
+ if (buf->state == ISP_BUF_STATE_ACTIVE)
+ wake_up(&buf->wait);
+
+ buf->state = ISP_BUF_STATE_IDLE;
+ }
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+
+ INIT_LIST_HEAD(&queue->queue);
+
+done:
+ mutex_unlock(&queue->lock);
+}
+
+/**
+ * omap3isp_video_queue_discard_done - Discard all buffers marked as DONE
+ *
+ * This function is intended to be used with suspend/resume operations. It
+ * discards all 'done' buffers as they would be too old to be requested after
+ * resume.
+ *
+ * Drivers must stop the hardware and synchronize with interrupt handlers and/or
+ * delayed works before calling this function to make sure no buffer will be
+ * touched by the driver and/or hardware.
+ */
+void omap3isp_video_queue_discard_done(struct isp_video_queue *queue)
+{
+ struct isp_video_buffer *buf;
+ unsigned int i;
+
+ mutex_lock(&queue->lock);
+
+ if (!queue->streaming)
+ goto done;
+
+ for (i = 0; i < queue->count; ++i) {
+ buf = queue->buffers[i];
+
+ if (buf->state == ISP_BUF_STATE_DONE)
+ buf->state = ISP_BUF_STATE_ERROR;
+ }
+
+done:
+ mutex_unlock(&queue->lock);
+}
+
+static void isp_video_queue_vm_open(struct vm_area_struct *vma)
+{
+ struct isp_video_buffer *buf = vma->vm_private_data;
+
+ buf->vma_use_count++;
+}
+
+static void isp_video_queue_vm_close(struct vm_area_struct *vma)
+{
+ struct isp_video_buffer *buf = vma->vm_private_data;
+
+ buf->vma_use_count--;
+}
+
+static const struct vm_operations_struct isp_video_queue_vm_ops = {
+ .open = isp_video_queue_vm_open,
+ .close = isp_video_queue_vm_close,
+};
+
+/**
+ * omap3isp_video_queue_mmap - Map buffers to userspace
+ *
+ * This function is intended to be used as an mmap() file operation handler. It
+ * maps a buffer to userspace based on the VMA offset.
+ *
+ * Only buffers of memory type MMAP are supported.
+ */
+int omap3isp_video_queue_mmap(struct isp_video_queue *queue,
+ struct vm_area_struct *vma)
+{
+ struct isp_video_buffer *uninitialized_var(buf);
+ unsigned long size;
+ unsigned int i;
+ int ret = 0;
+
+ mutex_lock(&queue->lock);
+
+ for (i = 0; i < queue->count; ++i) {
+ buf = queue->buffers[i];
+ if ((buf->vbuf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff)
+ break;
+ }
+
+ if (i == queue->count) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ size = vma->vm_end - vma->vm_start;
+
+ if (buf->vbuf.memory != V4L2_MEMORY_MMAP ||
+ size != PAGE_ALIGN(buf->vbuf.length)) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = remap_vmalloc_range(vma, buf->vaddr, 0);
+ if (ret < 0)
+ goto done;
+
+ vma->vm_ops = &isp_video_queue_vm_ops;
+ vma->vm_private_data = buf;
+ isp_video_queue_vm_open(vma);
+
+done:
+ mutex_unlock(&queue->lock);
+ return ret;
+}
+
+/**
+ * omap3isp_video_queue_poll - Poll video queue state
+ *
+ * This function is intended to be used as a poll() file operation handler. It
+ * polls the state of the video buffer at the front of the queue and returns an
+ * events mask.
+ *
+ * If no buffer is present at the front of the queue, POLLERR is returned.
+ */
+unsigned int omap3isp_video_queue_poll(struct isp_video_queue *queue,
+ struct file *file, poll_table *wait)
+{
+ struct isp_video_buffer *buf;
+ unsigned int mask = 0;
+
+ mutex_lock(&queue->lock);
+ if (list_empty(&queue->queue)) {
+ mask |= POLLERR;
+ goto done;
+ }
+ buf = list_first_entry(&queue->queue, struct isp_video_buffer, stream);
+
+ poll_wait(file, &buf->wait, wait);
+ if (buf->state == ISP_BUF_STATE_DONE ||
+ buf->state == ISP_BUF_STATE_ERROR) {
+ if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ mask |= POLLIN | POLLRDNORM;
+ else
+ mask |= POLLOUT | POLLWRNORM;
+ }
+
+done:
+ mutex_unlock(&queue->lock);
+ return mask;
+}
diff --git a/drivers/media/video/omap3isp/ispqueue.h b/drivers/media/video/omap3isp/ispqueue.h
new file mode 100644
index 00000000..92c5a121
--- /dev/null
+++ b/drivers/media/video/omap3isp/ispqueue.h
@@ -0,0 +1,187 @@
+/*
+ * ispqueue.h
+ *
+ * TI OMAP3 ISP - Video buffers queue handling
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP3_ISP_QUEUE_H
+#define OMAP3_ISP_QUEUE_H
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <linux/wait.h>
+
+struct isp_video_queue;
+struct page;
+struct scatterlist;
+
+#define ISP_VIDEO_MAX_BUFFERS 16
+
+/**
+ * enum isp_video_buffer_state - ISP video buffer state
+ * @ISP_BUF_STATE_IDLE: The buffer is under userspace control (dequeued
+ * or not queued yet).
+ * @ISP_BUF_STATE_QUEUED: The buffer has been queued but isn't used by the
+ * device yet.
+ * @ISP_BUF_STATE_ACTIVE: The buffer is in use for an active video transfer.
+ * @ISP_BUF_STATE_ERROR: The device is done with the buffer and an error
+ * occurred. For capture device the buffer likely contains corrupted data or
+ * no data at all.
+ * @ISP_BUF_STATE_DONE: The device is done with the buffer and no error occurred.
+ * For capture devices the buffer contains valid data.
+ */
+enum isp_video_buffer_state {
+ ISP_BUF_STATE_IDLE,
+ ISP_BUF_STATE_QUEUED,
+ ISP_BUF_STATE_ACTIVE,
+ ISP_BUF_STATE_ERROR,
+ ISP_BUF_STATE_DONE,
+};
+
+/**
+ * struct isp_video_buffer - ISP video buffer
+ * @vma_use_count: Number of times the buffer is mmap'ed to userspace
+ * @stream: List head for insertion into main queue
+ * @queue: ISP buffers queue this buffer belongs to
+ * @prepared: Whether the buffer has been prepared
+ * @skip_cache: Whether to skip cache management operations for this buffer
+ * @vaddr: Memory virtual address (for kernel buffers)
+ * @vm_flags: Buffer VMA flags (for userspace buffers)
+ * @offset: Offset inside the first page (for userspace buffers)
+ * @npages: Number of pages (for userspace buffers)
+ * @pages: Pages table (for userspace non-VM_PFNMAP buffers)
+ * @paddr: Memory physical address (for userspace VM_PFNMAP buffers)
+ * @sglen: Number of elements in the scatter list (for non-VM_PFNMAP buffers)
+ * @sglist: Scatter list (for non-VM_PFNMAP buffers)
+ * @vbuf: V4L2 buffer
+ * @irqlist: List head for insertion into IRQ queue
+ * @state: Current buffer state
+ * @wait: Wait queue to signal buffer completion
+ */
+struct isp_video_buffer {
+ unsigned long vma_use_count;
+ struct list_head stream;
+ struct isp_video_queue *queue;
+ unsigned int prepared:1;
+ bool skip_cache;
+
+ /* For kernel buffers. */
+ void *vaddr;
+
+ /* For userspace buffers. */
+ unsigned long vm_flags;
+ unsigned long offset;
+ unsigned int npages;
+ struct page **pages;
+ dma_addr_t paddr;
+
+ /* For all buffers except VM_PFNMAP. */
+ unsigned int sglen;
+ struct scatterlist *sglist;
+
+ /* Touched by the interrupt handler. */
+ struct v4l2_buffer vbuf;
+ struct list_head irqlist;
+ enum isp_video_buffer_state state;
+ wait_queue_head_t wait;
+};
+
+#define to_isp_video_buffer(vb) container_of(vb, struct isp_video_buffer, vb)
+
+/**
+ * struct isp_video_queue_operations - Driver-specific operations
+ * @queue_prepare: Called before allocating buffers. Drivers should clamp the
+ * number of buffers according to their requirements, and must return the
+ * buffer size in bytes.
+ * @buffer_prepare: Called the first time a buffer is queued, or after changing
+ * the userspace memory address for a USERPTR buffer, with the queue lock
+ * held. Drivers should perform device-specific buffer preparation (such as
+ * mapping the buffer memory in an IOMMU). This operation is optional.
+ * @buffer_queue: Called when a buffer is being added to the queue with the
+ * queue irqlock spinlock held.
+ * @buffer_cleanup: Called before freeing buffers, or before changing the
+ * userspace memory address for a USERPTR buffer, with the queue lock held.
+ * Drivers must perform cleanup operations required to undo the
+ * buffer_prepare call. This operation is optional.
+ */
+struct isp_video_queue_operations {
+ void (*queue_prepare)(struct isp_video_queue *queue,
+ unsigned int *nbuffers, unsigned int *size);
+ int (*buffer_prepare)(struct isp_video_buffer *buf);
+ void (*buffer_queue)(struct isp_video_buffer *buf);
+ void (*buffer_cleanup)(struct isp_video_buffer *buf);
+};
+
+/**
+ * struct isp_video_queue - ISP video buffers queue
+ * @type: Type of video buffers handled by this queue
+ * @ops: Queue operations
+ * @dev: Device used for DMA operations
+ * @bufsize: Size of a driver-specific buffer object
+ * @count: Number of currently allocated buffers
+ * @buffers: ISP video buffers
+ * @lock: Mutex to protect access to the buffers, main queue and state
+ * @irqlock: Spinlock to protect access to the IRQ queue
+ * @streaming: Queue state, indicates whether the queue is streaming
+ * @queue: List of all queued buffers
+ */
+struct isp_video_queue {
+ enum v4l2_buf_type type;
+ const struct isp_video_queue_operations *ops;
+ struct device *dev;
+ unsigned int bufsize;
+
+ unsigned int count;
+ struct isp_video_buffer *buffers[ISP_VIDEO_MAX_BUFFERS];
+ struct mutex lock;
+ spinlock_t irqlock;
+
+ unsigned int streaming:1;
+
+ struct list_head queue;
+};
+
+int omap3isp_video_queue_cleanup(struct isp_video_queue *queue);
+int omap3isp_video_queue_init(struct isp_video_queue *queue,
+ enum v4l2_buf_type type,
+ const struct isp_video_queue_operations *ops,
+ struct device *dev, unsigned int bufsize);
+
+int omap3isp_video_queue_reqbufs(struct isp_video_queue *queue,
+ struct v4l2_requestbuffers *rb);
+int omap3isp_video_queue_querybuf(struct isp_video_queue *queue,
+ struct v4l2_buffer *vbuf);
+int omap3isp_video_queue_qbuf(struct isp_video_queue *queue,
+ struct v4l2_buffer *vbuf);
+int omap3isp_video_queue_dqbuf(struct isp_video_queue *queue,
+ struct v4l2_buffer *vbuf, int nonblocking);
+int omap3isp_video_queue_streamon(struct isp_video_queue *queue);
+void omap3isp_video_queue_streamoff(struct isp_video_queue *queue);
+void omap3isp_video_queue_discard_done(struct isp_video_queue *queue);
+int omap3isp_video_queue_mmap(struct isp_video_queue *queue,
+ struct vm_area_struct *vma);
+unsigned int omap3isp_video_queue_poll(struct isp_video_queue *queue,
+ struct file *file, poll_table *wait);
+
+#endif /* OMAP3_ISP_QUEUE_H */
diff --git a/drivers/media/video/omap3isp/ispreg.h b/drivers/media/video/omap3isp/ispreg.h
new file mode 100644
index 00000000..084ea77d
--- /dev/null
+++ b/drivers/media/video/omap3isp/ispreg.h
@@ -0,0 +1,1586 @@
+/*
+ * ispreg.h
+ *
+ * TI OMAP3 ISP - Registers definitions
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP3_ISP_REG_H
+#define OMAP3_ISP_REG_H
+
+#include <plat/omap34xx.h>
+
+
+#define CM_CAM_MCLK_HZ 172800000 /* Hz */
+
+/* ISP Submodules offset */
+
+#define OMAP3ISP_REG_BASE OMAP3430_ISP_BASE
+#define OMAP3ISP_REG(offset) (OMAP3ISP_REG_BASE + (offset))
+
+#define OMAP3ISP_CCP2_REG_OFFSET 0x0400
+#define OMAP3ISP_CCP2_REG_BASE (OMAP3ISP_REG_BASE + \
+ OMAP3ISP_CCP2_REG_OFFSET)
+#define OMAP3ISP_CCP2_REG(offset) (OMAP3ISP_CCP2_REG_BASE + (offset))
+
+#define OMAP3ISP_CCDC_REG_OFFSET 0x0600
+#define OMAP3ISP_CCDC_REG_BASE (OMAP3ISP_REG_BASE + \
+ OMAP3ISP_CCDC_REG_OFFSET)
+#define OMAP3ISP_CCDC_REG(offset) (OMAP3ISP_CCDC_REG_BASE + (offset))
+
+#define OMAP3ISP_HIST_REG_OFFSET 0x0A00
+#define OMAP3ISP_HIST_REG_BASE (OMAP3ISP_REG_BASE + \
+ OMAP3ISP_HIST_REG_OFFSET)
+#define OMAP3ISP_HIST_REG(offset) (OMAP3ISP_HIST_REG_BASE + (offset))
+
+#define OMAP3ISP_H3A_REG_OFFSET 0x0C00
+#define OMAP3ISP_H3A_REG_BASE (OMAP3ISP_REG_BASE + \
+ OMAP3ISP_H3A_REG_OFFSET)
+#define OMAP3ISP_H3A_REG(offset) (OMAP3ISP_H3A_REG_BASE + (offset))
+
+#define OMAP3ISP_PREV_REG_OFFSET 0x0E00
+#define OMAP3ISP_PREV_REG_BASE (OMAP3ISP_REG_BASE + \
+ OMAP3ISP_PREV_REG_OFFSET)
+#define OMAP3ISP_PREV_REG(offset) (OMAP3ISP_PREV_REG_BASE + (offset))
+
+#define OMAP3ISP_RESZ_REG_OFFSET 0x1000
+#define OMAP3ISP_RESZ_REG_BASE (OMAP3ISP_REG_BASE + \
+ OMAP3ISP_RESZ_REG_OFFSET)
+#define OMAP3ISP_RESZ_REG(offset) (OMAP3ISP_RESZ_REG_BASE + (offset))
+
+#define OMAP3ISP_SBL_REG_OFFSET 0x1200
+#define OMAP3ISP_SBL_REG_BASE (OMAP3ISP_REG_BASE + \
+ OMAP3ISP_SBL_REG_OFFSET)
+#define OMAP3ISP_SBL_REG(offset) (OMAP3ISP_SBL_REG_BASE + (offset))
+
+#define OMAP3ISP_CSI2A_REGS1_REG_OFFSET 0x1800
+#define OMAP3ISP_CSI2A_REGS1_REG_BASE (OMAP3ISP_REG_BASE + \
+ OMAP3ISP_CSI2A_REGS1_REG_OFFSET)
+#define OMAP3ISP_CSI2A_REGS1_REG(offset) \
+ (OMAP3ISP_CSI2A_REGS1_REG_BASE + (offset))
+
+#define OMAP3ISP_CSIPHY2_REG_OFFSET 0x1970
+#define OMAP3ISP_CSIPHY2_REG_BASE (OMAP3ISP_REG_BASE + \
+ OMAP3ISP_CSIPHY2_REG_OFFSET)
+#define OMAP3ISP_CSIPHY2_REG(offset) (OMAP3ISP_CSIPHY2_REG_BASE + (offset))
+
+#define OMAP3ISP_CSI2A_REGS2_REG_OFFSET 0x19C0
+#define OMAP3ISP_CSI2A_REGS2_REG_BASE (OMAP3ISP_REG_BASE + \
+ OMAP3ISP_CSI2A_REGS2_REG_OFFSET)
+#define OMAP3ISP_CSI2A_REGS2_REG(offset) \
+ (OMAP3ISP_CSI2A_REGS2_REG_BASE + (offset))
+
+#define OMAP3ISP_CSI2C_REGS1_REG_OFFSET 0x1C00
+#define OMAP3ISP_CSI2C_REGS1_REG_BASE (OMAP3ISP_REG_BASE + \
+ OMAP3ISP_CSI2C_REGS1_REG_OFFSET)
+#define OMAP3ISP_CSI2C_REGS1_REG(offset) \
+ (OMAP3ISP_CSI2C_REGS1_REG_BASE + (offset))
+
+#define OMAP3ISP_CSIPHY1_REG_OFFSET 0x1D70
+#define OMAP3ISP_CSIPHY1_REG_BASE (OMAP3ISP_REG_BASE + \
+ OMAP3ISP_CSIPHY1_REG_OFFSET)
+#define OMAP3ISP_CSIPHY1_REG(offset) (OMAP3ISP_CSIPHY1_REG_BASE + (offset))
+
+#define OMAP3ISP_CSI2C_REGS2_REG_OFFSET 0x1DC0
+#define OMAP3ISP_CSI2C_REGS2_REG_BASE (OMAP3ISP_REG_BASE + \
+ OMAP3ISP_CSI2C_REGS2_REG_OFFSET)
+#define OMAP3ISP_CSI2C_REGS2_REG(offset) \
+ (OMAP3ISP_CSI2C_REGS2_REG_BASE + (offset))
+
+/* ISP module register offset */
+
+#define ISP_REVISION (0x000)
+#define ISP_SYSCONFIG (0x004)
+#define ISP_SYSSTATUS (0x008)
+#define ISP_IRQ0ENABLE (0x00C)
+#define ISP_IRQ0STATUS (0x010)
+#define ISP_IRQ1ENABLE (0x014)
+#define ISP_IRQ1STATUS (0x018)
+#define ISP_TCTRL_GRESET_LENGTH (0x030)
+#define ISP_TCTRL_PSTRB_REPLAY (0x034)
+#define ISP_CTRL (0x040)
+#define ISP_SECURE (0x044)
+#define ISP_TCTRL_CTRL (0x050)
+#define ISP_TCTRL_FRAME (0x054)
+#define ISP_TCTRL_PSTRB_DELAY (0x058)
+#define ISP_TCTRL_STRB_DELAY (0x05C)
+#define ISP_TCTRL_SHUT_DELAY (0x060)
+#define ISP_TCTRL_PSTRB_LENGTH (0x064)
+#define ISP_TCTRL_STRB_LENGTH (0x068)
+#define ISP_TCTRL_SHUT_LENGTH (0x06C)
+#define ISP_PING_PONG_ADDR (0x070)
+#define ISP_PING_PONG_MEM_RANGE (0x074)
+#define ISP_PING_PONG_BUF_SIZE (0x078)
+
+/* CCP2 receiver registers */
+
+#define ISPCCP2_REVISION (0x000)
+#define ISPCCP2_SYSCONFIG (0x004)
+#define ISPCCP2_SYSCONFIG_SOFT_RESET (1 << 1)
+#define ISPCCP2_SYSCONFIG_AUTO_IDLE 0x1
+#define ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SHIFT 12
+#define ISPCCP2_SYSCONFIG_MSTANDBY_MODE_FORCE \
+ (0x0 << ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SHIFT)
+#define ISPCCP2_SYSCONFIG_MSTANDBY_MODE_NO \
+ (0x1 << ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SHIFT)
+#define ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SMART \
+ (0x2 << ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SHIFT)
+#define ISPCCP2_SYSSTATUS (0x008)
+#define ISPCCP2_SYSSTATUS_RESET_DONE (1 << 0)
+#define ISPCCP2_LC01_IRQENABLE (0x00C)
+#define ISPCCP2_LC01_IRQSTATUS (0x010)
+#define ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ (1 << 11)
+#define ISPCCP2_LC01_IRQSTATUS_LC0_LE_IRQ (1 << 10)
+#define ISPCCP2_LC01_IRQSTATUS_LC0_LS_IRQ (1 << 9)
+#define ISPCCP2_LC01_IRQSTATUS_LC0_FE_IRQ (1 << 8)
+#define ISPCCP2_LC01_IRQSTATUS_LC0_COUNT_IRQ (1 << 7)
+#define ISPCCP2_LC01_IRQSTATUS_LC0_FIFO_OVF_IRQ (1 << 5)
+#define ISPCCP2_LC01_IRQSTATUS_LC0_CRC_IRQ (1 << 4)
+#define ISPCCP2_LC01_IRQSTATUS_LC0_FSP_IRQ (1 << 3)
+#define ISPCCP2_LC01_IRQSTATUS_LC0_FW_IRQ (1 << 2)
+#define ISPCCP2_LC01_IRQSTATUS_LC0_FSC_IRQ (1 << 1)
+#define ISPCCP2_LC01_IRQSTATUS_LC0_SSC_IRQ (1 << 0)
+
+#define ISPCCP2_LC23_IRQENABLE (0x014)
+#define ISPCCP2_LC23_IRQSTATUS (0x018)
+#define ISPCCP2_LCM_IRQENABLE (0x02C)
+#define ISPCCP2_LCM_IRQSTATUS_EOF_IRQ (1 << 0)
+#define ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ (1 << 1)
+#define ISPCCP2_LCM_IRQSTATUS (0x030)
+#define ISPCCP2_CTRL (0x040)
+#define ISPCCP2_CTRL_IF_EN (1 << 0)
+#define ISPCCP2_CTRL_PHY_SEL (1 << 1)
+#define ISPCCP2_CTRL_PHY_SEL_CLOCK (0 << 1)
+#define ISPCCP2_CTRL_PHY_SEL_STROBE (1 << 1)
+#define ISPCCP2_CTRL_PHY_SEL_MASK 0x1
+#define ISPCCP2_CTRL_PHY_SEL_SHIFT 1
+#define ISPCCP2_CTRL_IO_OUT_SEL (1 << 2)
+#define ISPCCP2_CTRL_MODE (1 << 4)
+#define ISPCCP2_CTRL_VP_CLK_FORCE_ON (1 << 9)
+#define ISPCCP2_CTRL_INV (1 << 10)
+#define ISPCCP2_CTRL_INV_MASK 0x1
+#define ISPCCP2_CTRL_INV_SHIFT 10
+#define ISPCCP2_CTRL_VP_ONLY_EN (1 << 11)
+#define ISPCCP2_CTRL_VP_CLK_POL (1 << 12)
+#define ISPCCP2_CTRL_VPCLK_DIV_SHIFT 15
+#define ISPCCP2_CTRL_VPCLK_DIV_MASK 0x1ffff /* [31:15] */
+#define ISPCCP2_CTRL_VP_OUT_CTRL_SHIFT 8 /* 3430 bits */
+#define ISPCCP2_CTRL_VP_OUT_CTRL_MASK 0x3 /* 3430 bits */
+#define ISPCCP2_DBG (0x044)
+#define ISPCCP2_GNQ (0x048)
+#define ISPCCP2_LCx_CTRL(x) ((0x050)+0x30*(x))
+#define ISPCCP2_LCx_CTRL_CHAN_EN (1 << 0)
+#define ISPCCP2_LCx_CTRL_CRC_EN (1 << 19)
+#define ISPCCP2_LCx_CTRL_CRC_MASK 0x1
+#define ISPCCP2_LCx_CTRL_CRC_SHIFT 2
+#define ISPCCP2_LCx_CTRL_CRC_SHIFT_15_0 19
+#define ISPCCP2_LCx_CTRL_REGION_EN (1 << 1)
+#define ISPCCP2_LCx_CTRL_REGION_MASK 0x1
+#define ISPCCP2_LCx_CTRL_REGION_SHIFT 1
+#define ISPCCP2_LCx_CTRL_FORMAT_MASK_15_0 0x3f
+#define ISPCCP2_LCx_CTRL_FORMAT_SHIFT_15_0 0x2
+#define ISPCCP2_LCx_CTRL_FORMAT_MASK 0x1f
+#define ISPCCP2_LCx_CTRL_FORMAT_SHIFT 0x3
+#define ISPCCP2_LCx_CODE(x) ((0x054)+0x30*(x))
+#define ISPCCP2_LCx_STAT_START(x) ((0x058)+0x30*(x))
+#define ISPCCP2_LCx_STAT_SIZE(x) ((0x05C)+0x30*(x))
+#define ISPCCP2_LCx_SOF_ADDR(x) ((0x060)+0x30*(x))
+#define ISPCCP2_LCx_EOF_ADDR(x) ((0x064)+0x30*(x))
+#define ISPCCP2_LCx_DAT_START(x) ((0x068)+0x30*(x))
+#define ISPCCP2_LCx_DAT_SIZE(x) ((0x06C)+0x30*(x))
+#define ISPCCP2_LCx_DAT_MASK 0xFFF
+#define ISPCCP2_LCx_DAT_SHIFT 16
+#define ISPCCP2_LCx_DAT_PING_ADDR(x) ((0x070)+0x30*(x))
+#define ISPCCP2_LCx_DAT_PONG_ADDR(x) ((0x074)+0x30*(x))
+#define ISPCCP2_LCx_DAT_OFST(x) ((0x078)+0x30*(x))
+#define ISPCCP2_LCM_CTRL (0x1D0)
+#define ISPCCP2_LCM_CTRL_CHAN_EN (1 << 0)
+#define ISPCCP2_LCM_CTRL_DST_PORT (1 << 2)
+#define ISPCCP2_LCM_CTRL_DST_PORT_SHIFT 2
+#define ISPCCP2_LCM_CTRL_READ_THROTTLE_SHIFT 3
+#define ISPCCP2_LCM_CTRL_READ_THROTTLE_MASK 0x11
+#define ISPCCP2_LCM_CTRL_BURST_SIZE_SHIFT 5
+#define ISPCCP2_LCM_CTRL_BURST_SIZE_MASK 0x7
+#define ISPCCP2_LCM_CTRL_SRC_FORMAT_SHIFT 16
+#define ISPCCP2_LCM_CTRL_SRC_FORMAT_MASK 0x7
+#define ISPCCP2_LCM_CTRL_SRC_DECOMPR_SHIFT 20
+#define ISPCCP2_LCM_CTRL_SRC_DECOMPR_MASK 0x3
+#define ISPCCP2_LCM_CTRL_SRC_DPCM_PRED (1 << 22)
+#define ISPCCP2_LCM_CTRL_SRC_PACK (1 << 23)
+#define ISPCCP2_LCM_CTRL_DST_FORMAT_SHIFT 24
+#define ISPCCP2_LCM_CTRL_DST_FORMAT_MASK 0x7
+#define ISPCCP2_LCM_VSIZE (0x1D4)
+#define ISPCCP2_LCM_VSIZE_SHIFT 16
+#define ISPCCP2_LCM_HSIZE (0x1D8)
+#define ISPCCP2_LCM_HSIZE_SHIFT 16
+#define ISPCCP2_LCM_PREFETCH (0x1DC)
+#define ISPCCP2_LCM_PREFETCH_SHIFT 3
+#define ISPCCP2_LCM_SRC_ADDR (0x1E0)
+#define ISPCCP2_LCM_SRC_OFST (0x1E4)
+#define ISPCCP2_LCM_DST_ADDR (0x1E8)
+#define ISPCCP2_LCM_DST_OFST (0x1EC)
+
+/* CCDC module register offset */
+
+#define ISPCCDC_PID (0x000)
+#define ISPCCDC_PCR (0x004)
+#define ISPCCDC_SYN_MODE (0x008)
+#define ISPCCDC_HD_VD_WID (0x00C)
+#define ISPCCDC_PIX_LINES (0x010)
+#define ISPCCDC_HORZ_INFO (0x014)
+#define ISPCCDC_VERT_START (0x018)
+#define ISPCCDC_VERT_LINES (0x01C)
+#define ISPCCDC_CULLING (0x020)
+#define ISPCCDC_HSIZE_OFF (0x024)
+#define ISPCCDC_SDOFST (0x028)
+#define ISPCCDC_SDR_ADDR (0x02C)
+#define ISPCCDC_CLAMP (0x030)
+#define ISPCCDC_DCSUB (0x034)
+#define ISPCCDC_COLPTN (0x038)
+#define ISPCCDC_BLKCMP (0x03C)
+#define ISPCCDC_FPC (0x040)
+#define ISPCCDC_FPC_ADDR (0x044)
+#define ISPCCDC_VDINT (0x048)
+#define ISPCCDC_ALAW (0x04C)
+#define ISPCCDC_REC656IF (0x050)
+#define ISPCCDC_CFG (0x054)
+#define ISPCCDC_FMTCFG (0x058)
+#define ISPCCDC_FMT_HORZ (0x05C)
+#define ISPCCDC_FMT_VERT (0x060)
+#define ISPCCDC_FMT_ADDR0 (0x064)
+#define ISPCCDC_FMT_ADDR1 (0x068)
+#define ISPCCDC_FMT_ADDR2 (0x06C)
+#define ISPCCDC_FMT_ADDR3 (0x070)
+#define ISPCCDC_FMT_ADDR4 (0x074)
+#define ISPCCDC_FMT_ADDR5 (0x078)
+#define ISPCCDC_FMT_ADDR6 (0x07C)
+#define ISPCCDC_FMT_ADDR7 (0x080)
+#define ISPCCDC_PRGEVEN0 (0x084)
+#define ISPCCDC_PRGEVEN1 (0x088)
+#define ISPCCDC_PRGODD0 (0x08C)
+#define ISPCCDC_PRGODD1 (0x090)
+#define ISPCCDC_VP_OUT (0x094)
+
+#define ISPCCDC_LSC_CONFIG (0x098)
+#define ISPCCDC_LSC_INITIAL (0x09C)
+#define ISPCCDC_LSC_TABLE_BASE (0x0A0)
+#define ISPCCDC_LSC_TABLE_OFFSET (0x0A4)
+
+/* SBL */
+#define ISPSBL_PCR 0x4
+#define ISPSBL_PCR_H3A_AEAWB_WBL_OVF (1 << 16)
+#define ISPSBL_PCR_H3A_AF_WBL_OVF (1 << 17)
+#define ISPSBL_PCR_RSZ4_WBL_OVF (1 << 18)
+#define ISPSBL_PCR_RSZ3_WBL_OVF (1 << 19)
+#define ISPSBL_PCR_RSZ2_WBL_OVF (1 << 20)
+#define ISPSBL_PCR_RSZ1_WBL_OVF (1 << 21)
+#define ISPSBL_PCR_PRV_WBL_OVF (1 << 22)
+#define ISPSBL_PCR_CCDC_WBL_OVF (1 << 23)
+#define ISPSBL_PCR_CCDCPRV_2_RSZ_OVF (1 << 24)
+#define ISPSBL_PCR_CSIA_WBL_OVF (1 << 25)
+#define ISPSBL_PCR_CSIB_WBL_OVF (1 << 26)
+#define ISPSBL_CCDC_WR_0 (0x028)
+#define ISPSBL_CCDC_WR_0_DATA_READY (1 << 21)
+#define ISPSBL_CCDC_WR_1 (0x02C)
+#define ISPSBL_CCDC_WR_2 (0x030)
+#define ISPSBL_CCDC_WR_3 (0x034)
+
+#define ISPSBL_SDR_REQ_EXP 0xF8
+#define ISPSBL_SDR_REQ_HIST_EXP_SHIFT 0
+#define ISPSBL_SDR_REQ_HIST_EXP_MASK (0x3FF)
+#define ISPSBL_SDR_REQ_RSZ_EXP_SHIFT 10
+#define ISPSBL_SDR_REQ_RSZ_EXP_MASK (0x3FF << ISPSBL_SDR_REQ_RSZ_EXP_SHIFT)
+#define ISPSBL_SDR_REQ_PRV_EXP_SHIFT 20
+#define ISPSBL_SDR_REQ_PRV_EXP_MASK (0x3FF << ISPSBL_SDR_REQ_PRV_EXP_SHIFT)
+
+/* Histogram registers */
+#define ISPHIST_PID (0x000)
+#define ISPHIST_PCR (0x004)
+#define ISPHIST_CNT (0x008)
+#define ISPHIST_WB_GAIN (0x00C)
+#define ISPHIST_R0_HORZ (0x010)
+#define ISPHIST_R0_VERT (0x014)
+#define ISPHIST_R1_HORZ (0x018)
+#define ISPHIST_R1_VERT (0x01C)
+#define ISPHIST_R2_HORZ (0x020)
+#define ISPHIST_R2_VERT (0x024)
+#define ISPHIST_R3_HORZ (0x028)
+#define ISPHIST_R3_VERT (0x02C)
+#define ISPHIST_ADDR (0x030)
+#define ISPHIST_DATA (0x034)
+#define ISPHIST_RADD (0x038)
+#define ISPHIST_RADD_OFF (0x03C)
+#define ISPHIST_H_V_INFO (0x040)
+
+/* H3A module registers */
+#define ISPH3A_PID (0x000)
+#define ISPH3A_PCR (0x004)
+#define ISPH3A_AEWWIN1 (0x04C)
+#define ISPH3A_AEWINSTART (0x050)
+#define ISPH3A_AEWINBLK (0x054)
+#define ISPH3A_AEWSUBWIN (0x058)
+#define ISPH3A_AEWBUFST (0x05C)
+#define ISPH3A_AFPAX1 (0x008)
+#define ISPH3A_AFPAX2 (0x00C)
+#define ISPH3A_AFPAXSTART (0x010)
+#define ISPH3A_AFIIRSH (0x014)
+#define ISPH3A_AFBUFST (0x018)
+#define ISPH3A_AFCOEF010 (0x01C)
+#define ISPH3A_AFCOEF032 (0x020)
+#define ISPH3A_AFCOEF054 (0x024)
+#define ISPH3A_AFCOEF076 (0x028)
+#define ISPH3A_AFCOEF098 (0x02C)
+#define ISPH3A_AFCOEF0010 (0x030)
+#define ISPH3A_AFCOEF110 (0x034)
+#define ISPH3A_AFCOEF132 (0x038)
+#define ISPH3A_AFCOEF154 (0x03C)
+#define ISPH3A_AFCOEF176 (0x040)
+#define ISPH3A_AFCOEF198 (0x044)
+#define ISPH3A_AFCOEF1010 (0x048)
+
+#define ISPPRV_PCR (0x004)
+#define ISPPRV_HORZ_INFO (0x008)
+#define ISPPRV_VERT_INFO (0x00C)
+#define ISPPRV_RSDR_ADDR (0x010)
+#define ISPPRV_RADR_OFFSET (0x014)
+#define ISPPRV_DSDR_ADDR (0x018)
+#define ISPPRV_DRKF_OFFSET (0x01C)
+#define ISPPRV_WSDR_ADDR (0x020)
+#define ISPPRV_WADD_OFFSET (0x024)
+#define ISPPRV_AVE (0x028)
+#define ISPPRV_HMED (0x02C)
+#define ISPPRV_NF (0x030)
+#define ISPPRV_WB_DGAIN (0x034)
+#define ISPPRV_WBGAIN (0x038)
+#define ISPPRV_WBSEL (0x03C)
+#define ISPPRV_CFA (0x040)
+#define ISPPRV_BLKADJOFF (0x044)
+#define ISPPRV_RGB_MAT1 (0x048)
+#define ISPPRV_RGB_MAT2 (0x04C)
+#define ISPPRV_RGB_MAT3 (0x050)
+#define ISPPRV_RGB_MAT4 (0x054)
+#define ISPPRV_RGB_MAT5 (0x058)
+#define ISPPRV_RGB_OFF1 (0x05C)
+#define ISPPRV_RGB_OFF2 (0x060)
+#define ISPPRV_CSC0 (0x064)
+#define ISPPRV_CSC1 (0x068)
+#define ISPPRV_CSC2 (0x06C)
+#define ISPPRV_CSC_OFFSET (0x070)
+#define ISPPRV_CNT_BRT (0x074)
+#define ISPPRV_CSUP (0x078)
+#define ISPPRV_SETUP_YC (0x07C)
+#define ISPPRV_SET_TBL_ADDR (0x080)
+#define ISPPRV_SET_TBL_DATA (0x084)
+#define ISPPRV_CDC_THR0 (0x090)
+#define ISPPRV_CDC_THR1 (ISPPRV_CDC_THR0 + (0x4))
+#define ISPPRV_CDC_THR2 (ISPPRV_CDC_THR0 + (0x4) * 2)
+#define ISPPRV_CDC_THR3 (ISPPRV_CDC_THR0 + (0x4) * 3)
+
+#define ISPPRV_REDGAMMA_TABLE_ADDR 0x0000
+#define ISPPRV_GREENGAMMA_TABLE_ADDR 0x0400
+#define ISPPRV_BLUEGAMMA_TABLE_ADDR 0x0800
+#define ISPPRV_NF_TABLE_ADDR 0x0C00
+#define ISPPRV_YENH_TABLE_ADDR 0x1000
+#define ISPPRV_CFA_TABLE_ADDR 0x1400
+
+#define ISPRSZ_MIN_OUTPUT 64
+#define ISPRSZ_MAX_OUTPUT 3312
+
+/* Resizer module register offset */
+#define ISPRSZ_PID (0x000)
+#define ISPRSZ_PCR (0x004)
+#define ISPRSZ_CNT (0x008)
+#define ISPRSZ_OUT_SIZE (0x00C)
+#define ISPRSZ_IN_START (0x010)
+#define ISPRSZ_IN_SIZE (0x014)
+#define ISPRSZ_SDR_INADD (0x018)
+#define ISPRSZ_SDR_INOFF (0x01C)
+#define ISPRSZ_SDR_OUTADD (0x020)
+#define ISPRSZ_SDR_OUTOFF (0x024)
+#define ISPRSZ_HFILT10 (0x028)
+#define ISPRSZ_HFILT32 (0x02C)
+#define ISPRSZ_HFILT54 (0x030)
+#define ISPRSZ_HFILT76 (0x034)
+#define ISPRSZ_HFILT98 (0x038)
+#define ISPRSZ_HFILT1110 (0x03C)
+#define ISPRSZ_HFILT1312 (0x040)
+#define ISPRSZ_HFILT1514 (0x044)
+#define ISPRSZ_HFILT1716 (0x048)
+#define ISPRSZ_HFILT1918 (0x04C)
+#define ISPRSZ_HFILT2120 (0x050)
+#define ISPRSZ_HFILT2322 (0x054)
+#define ISPRSZ_HFILT2524 (0x058)
+#define ISPRSZ_HFILT2726 (0x05C)
+#define ISPRSZ_HFILT2928 (0x060)
+#define ISPRSZ_HFILT3130 (0x064)
+#define ISPRSZ_VFILT10 (0x068)
+#define ISPRSZ_VFILT32 (0x06C)
+#define ISPRSZ_VFILT54 (0x070)
+#define ISPRSZ_VFILT76 (0x074)
+#define ISPRSZ_VFILT98 (0x078)
+#define ISPRSZ_VFILT1110 (0x07C)
+#define ISPRSZ_VFILT1312 (0x080)
+#define ISPRSZ_VFILT1514 (0x084)
+#define ISPRSZ_VFILT1716 (0x088)
+#define ISPRSZ_VFILT1918 (0x08C)
+#define ISPRSZ_VFILT2120 (0x090)
+#define ISPRSZ_VFILT2322 (0x094)
+#define ISPRSZ_VFILT2524 (0x098)
+#define ISPRSZ_VFILT2726 (0x09C)
+#define ISPRSZ_VFILT2928 (0x0A0)
+#define ISPRSZ_VFILT3130 (0x0A4)
+#define ISPRSZ_YENH (0x0A8)
+
+#define ISP_INT_CLR 0xFF113F11
+#define ISPPRV_PCR_EN 1
+#define ISPPRV_PCR_BUSY (1 << 1)
+#define ISPPRV_PCR_SOURCE (1 << 2)
+#define ISPPRV_PCR_ONESHOT (1 << 3)
+#define ISPPRV_PCR_WIDTH (1 << 4)
+#define ISPPRV_PCR_INVALAW (1 << 5)
+#define ISPPRV_PCR_DRKFEN (1 << 6)
+#define ISPPRV_PCR_DRKFCAP (1 << 7)
+#define ISPPRV_PCR_HMEDEN (1 << 8)
+#define ISPPRV_PCR_NFEN (1 << 9)
+#define ISPPRV_PCR_CFAEN (1 << 10)
+#define ISPPRV_PCR_CFAFMT_SHIFT 11
+#define ISPPRV_PCR_CFAFMT_MASK 0x7800
+#define ISPPRV_PCR_CFAFMT_BAYER (0 << 11)
+#define ISPPRV_PCR_CFAFMT_SONYVGA (1 << 11)
+#define ISPPRV_PCR_CFAFMT_RGBFOVEON (2 << 11)
+#define ISPPRV_PCR_CFAFMT_DNSPL (3 << 11)
+#define ISPPRV_PCR_CFAFMT_HONEYCOMB (4 << 11)
+#define ISPPRV_PCR_CFAFMT_RRGGBBFOVEON (5 << 11)
+#define ISPPRV_PCR_YNENHEN (1 << 15)
+#define ISPPRV_PCR_SUPEN (1 << 16)
+#define ISPPRV_PCR_YCPOS_SHIFT 17
+#define ISPPRV_PCR_YCPOS_YCrYCb (0 << 17)
+#define ISPPRV_PCR_YCPOS_YCbYCr (1 << 17)
+#define ISPPRV_PCR_YCPOS_CbYCrY (2 << 17)
+#define ISPPRV_PCR_YCPOS_CrYCbY (3 << 17)
+#define ISPPRV_PCR_RSZPORT (1 << 19)
+#define ISPPRV_PCR_SDRPORT (1 << 20)
+#define ISPPRV_PCR_SCOMP_EN (1 << 21)
+#define ISPPRV_PCR_SCOMP_SFT_SHIFT (22)
+#define ISPPRV_PCR_SCOMP_SFT_MASK (7 << 22)
+#define ISPPRV_PCR_GAMMA_BYPASS (1 << 26)
+#define ISPPRV_PCR_DCOREN (1 << 27)
+#define ISPPRV_PCR_DCCOUP (1 << 28)
+#define ISPPRV_PCR_DRK_FAIL (1 << 31)
+
+#define ISPPRV_HORZ_INFO_EPH_SHIFT 0
+#define ISPPRV_HORZ_INFO_EPH_MASK 0x3fff
+#define ISPPRV_HORZ_INFO_SPH_SHIFT 16
+#define ISPPRV_HORZ_INFO_SPH_MASK 0x3fff0
+
+#define ISPPRV_VERT_INFO_ELV_SHIFT 0
+#define ISPPRV_VERT_INFO_ELV_MASK 0x3fff
+#define ISPPRV_VERT_INFO_SLV_SHIFT 16
+#define ISPPRV_VERT_INFO_SLV_MASK 0x3fff0
+
+#define ISPPRV_AVE_EVENDIST_SHIFT 2
+#define ISPPRV_AVE_EVENDIST_1 0x0
+#define ISPPRV_AVE_EVENDIST_2 0x1
+#define ISPPRV_AVE_EVENDIST_3 0x2
+#define ISPPRV_AVE_EVENDIST_4 0x3
+#define ISPPRV_AVE_ODDDIST_SHIFT 4
+#define ISPPRV_AVE_ODDDIST_1 0x0
+#define ISPPRV_AVE_ODDDIST_2 0x1
+#define ISPPRV_AVE_ODDDIST_3 0x2
+#define ISPPRV_AVE_ODDDIST_4 0x3
+
+#define ISPPRV_HMED_THRESHOLD_SHIFT 0
+#define ISPPRV_HMED_EVENDIST (1 << 8)
+#define ISPPRV_HMED_ODDDIST (1 << 9)
+
+#define ISPPRV_WBGAIN_COEF0_SHIFT 0
+#define ISPPRV_WBGAIN_COEF1_SHIFT 8
+#define ISPPRV_WBGAIN_COEF2_SHIFT 16
+#define ISPPRV_WBGAIN_COEF3_SHIFT 24
+
+#define ISPPRV_WBSEL_COEF0 0x0
+#define ISPPRV_WBSEL_COEF1 0x1
+#define ISPPRV_WBSEL_COEF2 0x2
+#define ISPPRV_WBSEL_COEF3 0x3
+
+#define ISPPRV_WBSEL_N0_0_SHIFT 0
+#define ISPPRV_WBSEL_N0_1_SHIFT 2
+#define ISPPRV_WBSEL_N0_2_SHIFT 4
+#define ISPPRV_WBSEL_N0_3_SHIFT 6
+#define ISPPRV_WBSEL_N1_0_SHIFT 8
+#define ISPPRV_WBSEL_N1_1_SHIFT 10
+#define ISPPRV_WBSEL_N1_2_SHIFT 12
+#define ISPPRV_WBSEL_N1_3_SHIFT 14
+#define ISPPRV_WBSEL_N2_0_SHIFT 16
+#define ISPPRV_WBSEL_N2_1_SHIFT 18
+#define ISPPRV_WBSEL_N2_2_SHIFT 20
+#define ISPPRV_WBSEL_N2_3_SHIFT 22
+#define ISPPRV_WBSEL_N3_0_SHIFT 24
+#define ISPPRV_WBSEL_N3_1_SHIFT 26
+#define ISPPRV_WBSEL_N3_2_SHIFT 28
+#define ISPPRV_WBSEL_N3_3_SHIFT 30
+
+#define ISPPRV_CFA_GRADTH_HOR_SHIFT 0
+#define ISPPRV_CFA_GRADTH_VER_SHIFT 8
+
+#define ISPPRV_BLKADJOFF_B_SHIFT 0
+#define ISPPRV_BLKADJOFF_G_SHIFT 8
+#define ISPPRV_BLKADJOFF_R_SHIFT 16
+
+#define ISPPRV_RGB_MAT1_MTX_RR_SHIFT 0
+#define ISPPRV_RGB_MAT1_MTX_GR_SHIFT 16
+
+#define ISPPRV_RGB_MAT2_MTX_BR_SHIFT 0
+#define ISPPRV_RGB_MAT2_MTX_RG_SHIFT 16
+
+#define ISPPRV_RGB_MAT3_MTX_GG_SHIFT 0
+#define ISPPRV_RGB_MAT3_MTX_BG_SHIFT 16
+
+#define ISPPRV_RGB_MAT4_MTX_RB_SHIFT 0
+#define ISPPRV_RGB_MAT4_MTX_GB_SHIFT 16
+
+#define ISPPRV_RGB_MAT5_MTX_BB_SHIFT 0
+
+#define ISPPRV_RGB_OFF1_MTX_OFFG_SHIFT 0
+#define ISPPRV_RGB_OFF1_MTX_OFFR_SHIFT 16
+
+#define ISPPRV_RGB_OFF2_MTX_OFFB_SHIFT 0
+
+#define ISPPRV_CSC0_RY_SHIFT 0
+#define ISPPRV_CSC0_GY_SHIFT 10
+#define ISPPRV_CSC0_BY_SHIFT 20
+
+#define ISPPRV_CSC1_RCB_SHIFT 0
+#define ISPPRV_CSC1_GCB_SHIFT 10
+#define ISPPRV_CSC1_BCB_SHIFT 20
+
+#define ISPPRV_CSC2_RCR_SHIFT 0
+#define ISPPRV_CSC2_GCR_SHIFT 10
+#define ISPPRV_CSC2_BCR_SHIFT 20
+
+#define ISPPRV_CSC_OFFSET_CR_SHIFT 0
+#define ISPPRV_CSC_OFFSET_CB_SHIFT 8
+#define ISPPRV_CSC_OFFSET_Y_SHIFT 16
+
+#define ISPPRV_CNT_BRT_BRT_SHIFT 0
+#define ISPPRV_CNT_BRT_CNT_SHIFT 8
+
+#define ISPPRV_CONTRAST_MAX 0x10
+#define ISPPRV_CONTRAST_MIN 0xFF
+#define ISPPRV_BRIGHT_MIN 0x00
+#define ISPPRV_BRIGHT_MAX 0xFF
+
+#define ISPPRV_CSUP_CSUPG_SHIFT 0
+#define ISPPRV_CSUP_THRES_SHIFT 8
+#define ISPPRV_CSUP_HPYF_SHIFT 16
+
+#define ISPPRV_SETUP_YC_MINC_SHIFT 0
+#define ISPPRV_SETUP_YC_MAXC_SHIFT 8
+#define ISPPRV_SETUP_YC_MINY_SHIFT 16
+#define ISPPRV_SETUP_YC_MAXY_SHIFT 24
+#define ISPPRV_YC_MAX 0xFF
+#define ISPPRV_YC_MIN 0x0
+
+/* Define bit fields within selected registers */
+#define ISP_REVISION_SHIFT 0
+
+#define ISP_SYSCONFIG_AUTOIDLE (1 << 0)
+#define ISP_SYSCONFIG_SOFTRESET (1 << 1)
+#define ISP_SYSCONFIG_MIDLEMODE_SHIFT 12
+#define ISP_SYSCONFIG_MIDLEMODE_FORCESTANDBY 0x0
+#define ISP_SYSCONFIG_MIDLEMODE_NOSTANBY 0x1
+#define ISP_SYSCONFIG_MIDLEMODE_SMARTSTANDBY 0x2
+
+#define ISP_SYSSTATUS_RESETDONE 0
+
+#define IRQ0ENABLE_CSIA_IRQ (1 << 0)
+#define IRQ0ENABLE_CSIC_IRQ (1 << 1)
+#define IRQ0ENABLE_CCP2_LCM_IRQ (1 << 3)
+#define IRQ0ENABLE_CCP2_LC0_IRQ (1 << 4)
+#define IRQ0ENABLE_CCP2_LC1_IRQ (1 << 5)
+#define IRQ0ENABLE_CCP2_LC2_IRQ (1 << 6)
+#define IRQ0ENABLE_CCP2_LC3_IRQ (1 << 7)
+#define IRQ0ENABLE_CSIB_IRQ (IRQ0ENABLE_CCP2_LCM_IRQ | \
+ IRQ0ENABLE_CCP2_LC0_IRQ | \
+ IRQ0ENABLE_CCP2_LC1_IRQ | \
+ IRQ0ENABLE_CCP2_LC2_IRQ | \
+ IRQ0ENABLE_CCP2_LC3_IRQ)
+
+#define IRQ0ENABLE_CCDC_VD0_IRQ (1 << 8)
+#define IRQ0ENABLE_CCDC_VD1_IRQ (1 << 9)
+#define IRQ0ENABLE_CCDC_VD2_IRQ (1 << 10)
+#define IRQ0ENABLE_CCDC_ERR_IRQ (1 << 11)
+#define IRQ0ENABLE_H3A_AF_DONE_IRQ (1 << 12)
+#define IRQ0ENABLE_H3A_AWB_DONE_IRQ (1 << 13)
+#define IRQ0ENABLE_HIST_DONE_IRQ (1 << 16)
+#define IRQ0ENABLE_CCDC_LSC_DONE_IRQ (1 << 17)
+#define IRQ0ENABLE_CCDC_LSC_PREF_COMP_IRQ (1 << 18)
+#define IRQ0ENABLE_CCDC_LSC_PREF_ERR_IRQ (1 << 19)
+#define IRQ0ENABLE_PRV_DONE_IRQ (1 << 20)
+#define IRQ0ENABLE_RSZ_DONE_IRQ (1 << 24)
+#define IRQ0ENABLE_OVF_IRQ (1 << 25)
+#define IRQ0ENABLE_PING_IRQ (1 << 26)
+#define IRQ0ENABLE_PONG_IRQ (1 << 27)
+#define IRQ0ENABLE_MMU_ERR_IRQ (1 << 28)
+#define IRQ0ENABLE_OCP_ERR_IRQ (1 << 29)
+#define IRQ0ENABLE_SEC_ERR_IRQ (1 << 30)
+#define IRQ0ENABLE_HS_VS_IRQ (1 << 31)
+
+#define IRQ0STATUS_CSIA_IRQ (1 << 0)
+#define IRQ0STATUS_CSI2C_IRQ (1 << 1)
+#define IRQ0STATUS_CCP2_LCM_IRQ (1 << 3)
+#define IRQ0STATUS_CCP2_LC0_IRQ (1 << 4)
+#define IRQ0STATUS_CSIB_IRQ (IRQ0STATUS_CCP2_LCM_IRQ | \
+ IRQ0STATUS_CCP2_LC0_IRQ)
+
+#define IRQ0STATUS_CSIB_LC1_IRQ (1 << 5)
+#define IRQ0STATUS_CSIB_LC2_IRQ (1 << 6)
+#define IRQ0STATUS_CSIB_LC3_IRQ (1 << 7)
+#define IRQ0STATUS_CCDC_VD0_IRQ (1 << 8)
+#define IRQ0STATUS_CCDC_VD1_IRQ (1 << 9)
+#define IRQ0STATUS_CCDC_VD2_IRQ (1 << 10)
+#define IRQ0STATUS_CCDC_ERR_IRQ (1 << 11)
+#define IRQ0STATUS_H3A_AF_DONE_IRQ (1 << 12)
+#define IRQ0STATUS_H3A_AWB_DONE_IRQ (1 << 13)
+#define IRQ0STATUS_HIST_DONE_IRQ (1 << 16)
+#define IRQ0STATUS_CCDC_LSC_DONE_IRQ (1 << 17)
+#define IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ (1 << 18)
+#define IRQ0STATUS_CCDC_LSC_PREF_ERR_IRQ (1 << 19)
+#define IRQ0STATUS_PRV_DONE_IRQ (1 << 20)
+#define IRQ0STATUS_RSZ_DONE_IRQ (1 << 24)
+#define IRQ0STATUS_OVF_IRQ (1 << 25)
+#define IRQ0STATUS_PING_IRQ (1 << 26)
+#define IRQ0STATUS_PONG_IRQ (1 << 27)
+#define IRQ0STATUS_MMU_ERR_IRQ (1 << 28)
+#define IRQ0STATUS_OCP_ERR_IRQ (1 << 29)
+#define IRQ0STATUS_SEC_ERR_IRQ (1 << 30)
+#define IRQ0STATUS_HS_VS_IRQ (1 << 31)
+
+#define TCTRL_GRESET_LEN 0
+
+#define TCTRL_PSTRB_REPLAY_DELAY 0
+#define TCTRL_PSTRB_REPLAY_COUNTER_SHIFT 25
+
+#define ISPCTRL_PAR_SER_CLK_SEL_PARALLEL 0x0
+#define ISPCTRL_PAR_SER_CLK_SEL_CSIA 0x1
+#define ISPCTRL_PAR_SER_CLK_SEL_CSIB 0x2
+#define ISPCTRL_PAR_SER_CLK_SEL_CSIC 0x3
+#define ISPCTRL_PAR_SER_CLK_SEL_MASK 0x3
+
+#define ISPCTRL_PAR_BRIDGE_SHIFT 2
+#define ISPCTRL_PAR_BRIDGE_DISABLE (0x0 << 2)
+#define ISPCTRL_PAR_BRIDGE_LENDIAN (0x2 << 2)
+#define ISPCTRL_PAR_BRIDGE_BENDIAN (0x3 << 2)
+#define ISPCTRL_PAR_BRIDGE_MASK (0x3 << 2)
+
+#define ISPCTRL_PAR_CLK_POL_SHIFT 4
+#define ISPCTRL_PAR_CLK_POL_INV (1 << 4)
+#define ISPCTRL_PING_PONG_EN (1 << 5)
+#define ISPCTRL_SHIFT_SHIFT 6
+#define ISPCTRL_SHIFT_0 (0x0 << 6)
+#define ISPCTRL_SHIFT_2 (0x1 << 6)
+#define ISPCTRL_SHIFT_4 (0x2 << 6)
+#define ISPCTRL_SHIFT_MASK (0x3 << 6)
+
+#define ISPCTRL_CCDC_CLK_EN (1 << 8)
+#define ISPCTRL_SCMP_CLK_EN (1 << 9)
+#define ISPCTRL_H3A_CLK_EN (1 << 10)
+#define ISPCTRL_HIST_CLK_EN (1 << 11)
+#define ISPCTRL_PREV_CLK_EN (1 << 12)
+#define ISPCTRL_RSZ_CLK_EN (1 << 13)
+#define ISPCTRL_SYNC_DETECT_SHIFT 14
+#define ISPCTRL_SYNC_DETECT_HSFALL (0x0 << ISPCTRL_SYNC_DETECT_SHIFT)
+#define ISPCTRL_SYNC_DETECT_HSRISE (0x1 << ISPCTRL_SYNC_DETECT_SHIFT)
+#define ISPCTRL_SYNC_DETECT_VSFALL (0x2 << ISPCTRL_SYNC_DETECT_SHIFT)
+#define ISPCTRL_SYNC_DETECT_VSRISE (0x3 << ISPCTRL_SYNC_DETECT_SHIFT)
+#define ISPCTRL_SYNC_DETECT_MASK (0x3 << ISPCTRL_SYNC_DETECT_SHIFT)
+
+#define ISPCTRL_CCDC_RAM_EN (1 << 16)
+#define ISPCTRL_PREV_RAM_EN (1 << 17)
+#define ISPCTRL_SBL_RD_RAM_EN (1 << 18)
+#define ISPCTRL_SBL_WR1_RAM_EN (1 << 19)
+#define ISPCTRL_SBL_WR0_RAM_EN (1 << 20)
+#define ISPCTRL_SBL_AUTOIDLE (1 << 21)
+#define ISPCTRL_SBL_SHARED_WPORTC (1 << 26)
+#define ISPCTRL_SBL_SHARED_RPORTA (1 << 27)
+#define ISPCTRL_SBL_SHARED_RPORTB (1 << 28)
+#define ISPCTRL_JPEG_FLUSH (1 << 30)
+#define ISPCTRL_CCDC_FLUSH (1 << 31)
+
+#define ISPSECURE_SECUREMODE 0
+
+#define ISPTCTRL_CTRL_DIV_LOW 0x0
+#define ISPTCTRL_CTRL_DIV_HIGH 0x1
+#define ISPTCTRL_CTRL_DIV_BYPASS 0x1F
+
+#define ISPTCTRL_CTRL_DIVA_SHIFT 0
+#define ISPTCTRL_CTRL_DIVA_MASK (0x1F << ISPTCTRL_CTRL_DIVA_SHIFT)
+
+#define ISPTCTRL_CTRL_DIVB_SHIFT 5
+#define ISPTCTRL_CTRL_DIVB_MASK (0x1F << ISPTCTRL_CTRL_DIVB_SHIFT)
+
+#define ISPTCTRL_CTRL_DIVC_SHIFT 10
+#define ISPTCTRL_CTRL_DIVC_NOCLOCK (0x0 << 10)
+
+#define ISPTCTRL_CTRL_SHUTEN (1 << 21)
+#define ISPTCTRL_CTRL_PSTRBEN (1 << 22)
+#define ISPTCTRL_CTRL_STRBEN (1 << 23)
+#define ISPTCTRL_CTRL_SHUTPOL (1 << 24)
+#define ISPTCTRL_CTRL_STRBPSTRBPOL (1 << 26)
+
+#define ISPTCTRL_CTRL_INSEL_SHIFT 27
+#define ISPTCTRL_CTRL_INSEL_PARALLEL (0x0 << 27)
+#define ISPTCTRL_CTRL_INSEL_CSIA (0x1 << 27)
+#define ISPTCTRL_CTRL_INSEL_CSIB (0x2 << 27)
+
+#define ISPTCTRL_CTRL_GRESETEn (1 << 29)
+#define ISPTCTRL_CTRL_GRESETPOL (1 << 30)
+#define ISPTCTRL_CTRL_GRESETDIR (1 << 31)
+
+#define ISPTCTRL_FRAME_SHUT_SHIFT 0
+#define ISPTCTRL_FRAME_PSTRB_SHIFT 6
+#define ISPTCTRL_FRAME_STRB_SHIFT 12
+
+#define ISPCCDC_PID_PREV_SHIFT 0
+#define ISPCCDC_PID_CID_SHIFT 8
+#define ISPCCDC_PID_TID_SHIFT 16
+
+#define ISPCCDC_PCR_EN 1
+#define ISPCCDC_PCR_BUSY (1 << 1)
+
+#define ISPCCDC_SYN_MODE_VDHDOUT 0x1
+#define ISPCCDC_SYN_MODE_FLDOUT (1 << 1)
+#define ISPCCDC_SYN_MODE_VDPOL (1 << 2)
+#define ISPCCDC_SYN_MODE_HDPOL (1 << 3)
+#define ISPCCDC_SYN_MODE_FLDPOL (1 << 4)
+#define ISPCCDC_SYN_MODE_EXWEN (1 << 5)
+#define ISPCCDC_SYN_MODE_DATAPOL (1 << 6)
+#define ISPCCDC_SYN_MODE_FLDMODE (1 << 7)
+#define ISPCCDC_SYN_MODE_DATSIZ_MASK (0x7 << 8)
+#define ISPCCDC_SYN_MODE_DATSIZ_8_16 (0x0 << 8)
+#define ISPCCDC_SYN_MODE_DATSIZ_12 (0x4 << 8)
+#define ISPCCDC_SYN_MODE_DATSIZ_11 (0x5 << 8)
+#define ISPCCDC_SYN_MODE_DATSIZ_10 (0x6 << 8)
+#define ISPCCDC_SYN_MODE_DATSIZ_8 (0x7 << 8)
+#define ISPCCDC_SYN_MODE_PACK8 (1 << 11)
+#define ISPCCDC_SYN_MODE_INPMOD_MASK (3 << 12)
+#define ISPCCDC_SYN_MODE_INPMOD_RAW (0 << 12)
+#define ISPCCDC_SYN_MODE_INPMOD_YCBCR16 (1 << 12)
+#define ISPCCDC_SYN_MODE_INPMOD_YCBCR8 (2 << 12)
+#define ISPCCDC_SYN_MODE_LPF (1 << 14)
+#define ISPCCDC_SYN_MODE_FLDSTAT (1 << 15)
+#define ISPCCDC_SYN_MODE_VDHDEN (1 << 16)
+#define ISPCCDC_SYN_MODE_WEN (1 << 17)
+#define ISPCCDC_SYN_MODE_VP2SDR (1 << 18)
+#define ISPCCDC_SYN_MODE_SDR2RSZ (1 << 19)
+
+#define ISPCCDC_HD_VD_WID_VDW_SHIFT 0
+#define ISPCCDC_HD_VD_WID_HDW_SHIFT 16
+
+#define ISPCCDC_PIX_LINES_HLPRF_SHIFT 0
+#define ISPCCDC_PIX_LINES_PPLN_SHIFT 16
+
+#define ISPCCDC_HORZ_INFO_NPH_SHIFT 0
+#define ISPCCDC_HORZ_INFO_NPH_MASK 0x00007fff
+#define ISPCCDC_HORZ_INFO_SPH_SHIFT 16
+#define ISPCCDC_HORZ_INFO_SPH_MASK 0x7fff0000
+
+#define ISPCCDC_VERT_START_SLV1_SHIFT 0
+#define ISPCCDC_VERT_START_SLV0_SHIFT 16
+#define ISPCCDC_VERT_START_SLV0_MASK 0x7fff0000
+
+#define ISPCCDC_VERT_LINES_NLV_SHIFT 0
+#define ISPCCDC_VERT_LINES_NLV_MASK 0x00007fff
+
+#define ISPCCDC_CULLING_CULV_SHIFT 0
+#define ISPCCDC_CULLING_CULHODD_SHIFT 16
+#define ISPCCDC_CULLING_CULHEVN_SHIFT 24
+
+#define ISPCCDC_HSIZE_OFF_SHIFT 0
+
+#define ISPCCDC_SDOFST_FINV (1 << 14)
+#define ISPCCDC_SDOFST_FOFST_1L 0
+#define ISPCCDC_SDOFST_FOFST_4L (3 << 12)
+#define ISPCCDC_SDOFST_LOFST3_SHIFT 0
+#define ISPCCDC_SDOFST_LOFST2_SHIFT 3
+#define ISPCCDC_SDOFST_LOFST1_SHIFT 6
+#define ISPCCDC_SDOFST_LOFST0_SHIFT 9
+#define EVENEVEN 1
+#define ODDEVEN 2
+#define EVENODD 3
+#define ODDODD 4
+
+#define ISPCCDC_CLAMP_OBGAIN_SHIFT 0
+#define ISPCCDC_CLAMP_OBST_SHIFT 10
+#define ISPCCDC_CLAMP_OBSLN_SHIFT 25
+#define ISPCCDC_CLAMP_OBSLEN_SHIFT 28
+#define ISPCCDC_CLAMP_CLAMPEN (1 << 31)
+
+#define ISPCCDC_COLPTN_R_Ye 0x0
+#define ISPCCDC_COLPTN_Gr_Cy 0x1
+#define ISPCCDC_COLPTN_Gb_G 0x2
+#define ISPCCDC_COLPTN_B_Mg 0x3
+#define ISPCCDC_COLPTN_CP0PLC0_SHIFT 0
+#define ISPCCDC_COLPTN_CP0PLC1_SHIFT 2
+#define ISPCCDC_COLPTN_CP0PLC2_SHIFT 4
+#define ISPCCDC_COLPTN_CP0PLC3_SHIFT 6
+#define ISPCCDC_COLPTN_CP1PLC0_SHIFT 8
+#define ISPCCDC_COLPTN_CP1PLC1_SHIFT 10
+#define ISPCCDC_COLPTN_CP1PLC2_SHIFT 12
+#define ISPCCDC_COLPTN_CP1PLC3_SHIFT 14
+#define ISPCCDC_COLPTN_CP2PLC0_SHIFT 16
+#define ISPCCDC_COLPTN_CP2PLC1_SHIFT 18
+#define ISPCCDC_COLPTN_CP2PLC2_SHIFT 20
+#define ISPCCDC_COLPTN_CP2PLC3_SHIFT 22
+#define ISPCCDC_COLPTN_CP3PLC0_SHIFT 24
+#define ISPCCDC_COLPTN_CP3PLC1_SHIFT 26
+#define ISPCCDC_COLPTN_CP3PLC2_SHIFT 28
+#define ISPCCDC_COLPTN_CP3PLC3_SHIFT 30
+
+#define ISPCCDC_BLKCMP_B_MG_SHIFT 0
+#define ISPCCDC_BLKCMP_GB_G_SHIFT 8
+#define ISPCCDC_BLKCMP_GR_CY_SHIFT 16
+#define ISPCCDC_BLKCMP_R_YE_SHIFT 24
+
+#define ISPCCDC_FPC_FPNUM_SHIFT 0
+#define ISPCCDC_FPC_FPCEN (1 << 15)
+#define ISPCCDC_FPC_FPERR (1 << 16)
+
+#define ISPCCDC_VDINT_1_SHIFT 0
+#define ISPCCDC_VDINT_1_MASK 0x00007fff
+#define ISPCCDC_VDINT_0_SHIFT 16
+#define ISPCCDC_VDINT_0_MASK 0x7fff0000
+
+#define ISPCCDC_ALAW_GWDI_12_3 (0x3 << 0)
+#define ISPCCDC_ALAW_GWDI_11_2 (0x4 << 0)
+#define ISPCCDC_ALAW_GWDI_10_1 (0x5 << 0)
+#define ISPCCDC_ALAW_GWDI_9_0 (0x6 << 0)
+#define ISPCCDC_ALAW_CCDTBL (1 << 3)
+
+#define ISPCCDC_REC656IF_R656ON 1
+#define ISPCCDC_REC656IF_ECCFVH (1 << 1)
+
+#define ISPCCDC_CFG_BW656 (1 << 5)
+#define ISPCCDC_CFG_FIDMD_SHIFT 6
+#define ISPCCDC_CFG_WENLOG (1 << 8)
+#define ISPCCDC_CFG_WENLOG_AND (0 << 8)
+#define ISPCCDC_CFG_WENLOG_OR (1 << 8)
+#define ISPCCDC_CFG_Y8POS (1 << 11)
+#define ISPCCDC_CFG_BSWD (1 << 12)
+#define ISPCCDC_CFG_MSBINVI (1 << 13)
+#define ISPCCDC_CFG_VDLC (1 << 15)
+
+#define ISPCCDC_FMTCFG_FMTEN 0x1
+#define ISPCCDC_FMTCFG_LNALT (1 << 1)
+#define ISPCCDC_FMTCFG_LNUM_SHIFT 2
+#define ISPCCDC_FMTCFG_PLEN_ODD_SHIFT 4
+#define ISPCCDC_FMTCFG_PLEN_EVEN_SHIFT 8
+#define ISPCCDC_FMTCFG_VPIN_MASK 0x00007000
+#define ISPCCDC_FMTCFG_VPIN_12_3 (0x3 << 12)
+#define ISPCCDC_FMTCFG_VPIN_11_2 (0x4 << 12)
+#define ISPCCDC_FMTCFG_VPIN_10_1 (0x5 << 12)
+#define ISPCCDC_FMTCFG_VPIN_9_0 (0x6 << 12)
+#define ISPCCDC_FMTCFG_VPEN (1 << 15)
+
+#define ISPCCDC_FMTCFG_VPIF_FRQ_MASK 0x003f0000
+#define ISPCCDC_FMTCFG_VPIF_FRQ_SHIFT 16
+#define ISPCCDC_FMTCFG_VPIF_FRQ_BY2 (0x0 << 16)
+#define ISPCCDC_FMTCFG_VPIF_FRQ_BY3 (0x1 << 16)
+#define ISPCCDC_FMTCFG_VPIF_FRQ_BY4 (0x2 << 16)
+#define ISPCCDC_FMTCFG_VPIF_FRQ_BY5 (0x3 << 16)
+#define ISPCCDC_FMTCFG_VPIF_FRQ_BY6 (0x4 << 16)
+
+#define ISPCCDC_FMT_HORZ_FMTLNH_SHIFT 0
+#define ISPCCDC_FMT_HORZ_FMTSPH_SHIFT 16
+
+#define ISPCCDC_FMT_VERT_FMTLNV_SHIFT 0
+#define ISPCCDC_FMT_VERT_FMTSLV_SHIFT 16
+
+#define ISPCCDC_FMT_HORZ_FMTSPH_MASK 0x1fff0000
+#define ISPCCDC_FMT_HORZ_FMTLNH_MASK 0x00001fff
+
+#define ISPCCDC_FMT_VERT_FMTSLV_MASK 0x1fff0000
+#define ISPCCDC_FMT_VERT_FMTLNV_MASK 0x00001fff
+
+#define ISPCCDC_VP_OUT_HORZ_ST_SHIFT 0
+#define ISPCCDC_VP_OUT_HORZ_NUM_SHIFT 4
+#define ISPCCDC_VP_OUT_VERT_NUM_SHIFT 17
+
+#define ISPRSZ_PID_PREV_SHIFT 0
+#define ISPRSZ_PID_CID_SHIFT 8
+#define ISPRSZ_PID_TID_SHIFT 16
+
+#define ISPRSZ_PCR_ENABLE (1 << 0)
+#define ISPRSZ_PCR_BUSY (1 << 1)
+#define ISPRSZ_PCR_ONESHOT (1 << 2)
+
+#define ISPRSZ_CNT_HRSZ_SHIFT 0
+#define ISPRSZ_CNT_HRSZ_MASK \
+ (0x3FF << ISPRSZ_CNT_HRSZ_SHIFT)
+#define ISPRSZ_CNT_VRSZ_SHIFT 10
+#define ISPRSZ_CNT_VRSZ_MASK \
+ (0x3FF << ISPRSZ_CNT_VRSZ_SHIFT)
+#define ISPRSZ_CNT_HSTPH_SHIFT 20
+#define ISPRSZ_CNT_HSTPH_MASK (0x7 << ISPRSZ_CNT_HSTPH_SHIFT)
+#define ISPRSZ_CNT_VSTPH_SHIFT 23
+#define ISPRSZ_CNT_VSTPH_MASK (0x7 << ISPRSZ_CNT_VSTPH_SHIFT)
+#define ISPRSZ_CNT_YCPOS (1 << 26)
+#define ISPRSZ_CNT_INPTYP (1 << 27)
+#define ISPRSZ_CNT_INPSRC (1 << 28)
+#define ISPRSZ_CNT_CBILIN (1 << 29)
+
+#define ISPRSZ_OUT_SIZE_HORZ_SHIFT 0
+#define ISPRSZ_OUT_SIZE_HORZ_MASK \
+ (0xFFF << ISPRSZ_OUT_SIZE_HORZ_SHIFT)
+#define ISPRSZ_OUT_SIZE_VERT_SHIFT 16
+#define ISPRSZ_OUT_SIZE_VERT_MASK \
+ (0xFFF << ISPRSZ_OUT_SIZE_VERT_SHIFT)
+
+#define ISPRSZ_IN_START_HORZ_ST_SHIFT 0
+#define ISPRSZ_IN_START_HORZ_ST_MASK \
+ (0x1FFF << ISPRSZ_IN_START_HORZ_ST_SHIFT)
+#define ISPRSZ_IN_START_VERT_ST_SHIFT 16
+#define ISPRSZ_IN_START_VERT_ST_MASK \
+ (0x1FFF << ISPRSZ_IN_START_VERT_ST_SHIFT)
+
+#define ISPRSZ_IN_SIZE_HORZ_SHIFT 0
+#define ISPRSZ_IN_SIZE_HORZ_MASK \
+ (0x1FFF << ISPRSZ_IN_SIZE_HORZ_SHIFT)
+#define ISPRSZ_IN_SIZE_VERT_SHIFT 16
+#define ISPRSZ_IN_SIZE_VERT_MASK \
+ (0x1FFF << ISPRSZ_IN_SIZE_VERT_SHIFT)
+
+#define ISPRSZ_SDR_INADD_ADDR_SHIFT 0
+#define ISPRSZ_SDR_INADD_ADDR_MASK 0xFFFFFFFF
+
+#define ISPRSZ_SDR_INOFF_OFFSET_SHIFT 0
+#define ISPRSZ_SDR_INOFF_OFFSET_MASK \
+ (0xFFFF << ISPRSZ_SDR_INOFF_OFFSET_SHIFT)
+
+#define ISPRSZ_SDR_OUTADD_ADDR_SHIFT 0
+#define ISPRSZ_SDR_OUTADD_ADDR_MASK 0xFFFFFFFF
+
+
+#define ISPRSZ_SDR_OUTOFF_OFFSET_SHIFT 0
+#define ISPRSZ_SDR_OUTOFF_OFFSET_MASK \
+ (0xFFFF << ISPRSZ_SDR_OUTOFF_OFFSET_SHIFT)
+
+#define ISPRSZ_HFILT_COEF0_SHIFT 0
+#define ISPRSZ_HFILT_COEF0_MASK \
+ (0x3FF << ISPRSZ_HFILT_COEF0_SHIFT)
+#define ISPRSZ_HFILT_COEF1_SHIFT 16
+#define ISPRSZ_HFILT_COEF1_MASK \
+ (0x3FF << ISPRSZ_HFILT_COEF1_SHIFT)
+
+#define ISPRSZ_HFILT32_COEF2_SHIFT 0
+#define ISPRSZ_HFILT32_COEF2_MASK 0x3FF
+#define ISPRSZ_HFILT32_COEF3_SHIFT 16
+#define ISPRSZ_HFILT32_COEF3_MASK 0x3FF0000
+
+#define ISPRSZ_HFILT54_COEF4_SHIFT 0
+#define ISPRSZ_HFILT54_COEF4_MASK 0x3FF
+#define ISPRSZ_HFILT54_COEF5_SHIFT 16
+#define ISPRSZ_HFILT54_COEF5_MASK 0x3FF0000
+
+#define ISPRSZ_HFILT76_COEFF6_SHIFT 0
+#define ISPRSZ_HFILT76_COEFF6_MASK 0x3FF
+#define ISPRSZ_HFILT76_COEFF7_SHIFT 16
+#define ISPRSZ_HFILT76_COEFF7_MASK 0x3FF0000
+
+#define ISPRSZ_HFILT98_COEFF8_SHIFT 0
+#define ISPRSZ_HFILT98_COEFF8_MASK 0x3FF
+#define ISPRSZ_HFILT98_COEFF9_SHIFT 16
+#define ISPRSZ_HFILT98_COEFF9_MASK 0x3FF0000
+
+#define ISPRSZ_HFILT1110_COEF10_SHIFT 0
+#define ISPRSZ_HFILT1110_COEF10_MASK 0x3FF
+#define ISPRSZ_HFILT1110_COEF11_SHIFT 16
+#define ISPRSZ_HFILT1110_COEF11_MASK 0x3FF0000
+
+#define ISPRSZ_HFILT1312_COEFF12_SHIFT 0
+#define ISPRSZ_HFILT1312_COEFF12_MASK 0x3FF
+#define ISPRSZ_HFILT1312_COEFF13_SHIFT 16
+#define ISPRSZ_HFILT1312_COEFF13_MASK 0x3FF0000
+
+#define ISPRSZ_HFILT1514_COEFF14_SHIFT 0
+#define ISPRSZ_HFILT1514_COEFF14_MASK 0x3FF
+#define ISPRSZ_HFILT1514_COEFF15_SHIFT 16
+#define ISPRSZ_HFILT1514_COEFF15_MASK 0x3FF0000
+
+#define ISPRSZ_HFILT1716_COEF16_SHIFT 0
+#define ISPRSZ_HFILT1716_COEF16_MASK 0x3FF
+#define ISPRSZ_HFILT1716_COEF17_SHIFT 16
+#define ISPRSZ_HFILT1716_COEF17_MASK 0x3FF0000
+
+#define ISPRSZ_HFILT1918_COEF18_SHIFT 0
+#define ISPRSZ_HFILT1918_COEF18_MASK 0x3FF
+#define ISPRSZ_HFILT1918_COEF19_SHIFT 16
+#define ISPRSZ_HFILT1918_COEF19_MASK 0x3FF0000
+
+#define ISPRSZ_HFILT2120_COEF20_SHIFT 0
+#define ISPRSZ_HFILT2120_COEF20_MASK 0x3FF
+#define ISPRSZ_HFILT2120_COEF21_SHIFT 16
+#define ISPRSZ_HFILT2120_COEF21_MASK 0x3FF0000
+
+#define ISPRSZ_HFILT2322_COEF22_SHIFT 0
+#define ISPRSZ_HFILT2322_COEF22_MASK 0x3FF
+#define ISPRSZ_HFILT2322_COEF23_SHIFT 16
+#define ISPRSZ_HFILT2322_COEF23_MASK 0x3FF0000
+
+#define ISPRSZ_HFILT2524_COEF24_SHIFT 0
+#define ISPRSZ_HFILT2524_COEF24_MASK 0x3FF
+#define ISPRSZ_HFILT2524_COEF25_SHIFT 16
+#define ISPRSZ_HFILT2524_COEF25_MASK 0x3FF0000
+
+#define ISPRSZ_HFILT2726_COEF26_SHIFT 0
+#define ISPRSZ_HFILT2726_COEF26_MASK 0x3FF
+#define ISPRSZ_HFILT2726_COEF27_SHIFT 16
+#define ISPRSZ_HFILT2726_COEF27_MASK 0x3FF0000
+
+#define ISPRSZ_HFILT2928_COEF28_SHIFT 0
+#define ISPRSZ_HFILT2928_COEF28_MASK 0x3FF
+#define ISPRSZ_HFILT2928_COEF29_SHIFT 16
+#define ISPRSZ_HFILT2928_COEF29_MASK 0x3FF0000
+
+#define ISPRSZ_HFILT3130_COEF30_SHIFT 0
+#define ISPRSZ_HFILT3130_COEF30_MASK 0x3FF
+#define ISPRSZ_HFILT3130_COEF31_SHIFT 16
+#define ISPRSZ_HFILT3130_COEF31_MASK 0x3FF0000
+
+#define ISPRSZ_VFILT_COEF0_SHIFT 0
+#define ISPRSZ_VFILT_COEF0_MASK \
+ (0x3FF << ISPRSZ_VFILT_COEF0_SHIFT)
+#define ISPRSZ_VFILT_COEF1_SHIFT 16
+#define ISPRSZ_VFILT_COEF1_MASK \
+ (0x3FF << ISPRSZ_VFILT_COEF1_SHIFT)
+
+#define ISPRSZ_VFILT10_COEF0_SHIFT 0
+#define ISPRSZ_VFILT10_COEF0_MASK 0x3FF
+#define ISPRSZ_VFILT10_COEF1_SHIFT 16
+#define ISPRSZ_VFILT10_COEF1_MASK 0x3FF0000
+
+#define ISPRSZ_VFILT32_COEF2_SHIFT 0
+#define ISPRSZ_VFILT32_COEF2_MASK 0x3FF
+#define ISPRSZ_VFILT32_COEF3_SHIFT 16
+#define ISPRSZ_VFILT32_COEF3_MASK 0x3FF0000
+
+#define ISPRSZ_VFILT54_COEF4_SHIFT 0
+#define ISPRSZ_VFILT54_COEF4_MASK 0x3FF
+#define ISPRSZ_VFILT54_COEF5_SHIFT 16
+#define ISPRSZ_VFILT54_COEF5_MASK 0x3FF0000
+
+#define ISPRSZ_VFILT76_COEFF6_SHIFT 0
+#define ISPRSZ_VFILT76_COEFF6_MASK 0x3FF
+#define ISPRSZ_VFILT76_COEFF7_SHIFT 16
+#define ISPRSZ_VFILT76_COEFF7_MASK 0x3FF0000
+
+#define ISPRSZ_VFILT98_COEFF8_SHIFT 0
+#define ISPRSZ_VFILT98_COEFF8_MASK 0x3FF
+#define ISPRSZ_VFILT98_COEFF9_SHIFT 16
+#define ISPRSZ_VFILT98_COEFF9_MASK 0x3FF0000
+
+#define ISPRSZ_VFILT1110_COEF10_SHIFT 0
+#define ISPRSZ_VFILT1110_COEF10_MASK 0x3FF
+#define ISPRSZ_VFILT1110_COEF11_SHIFT 16
+#define ISPRSZ_VFILT1110_COEF11_MASK 0x3FF0000
+
+#define ISPRSZ_VFILT1312_COEFF12_SHIFT 0
+#define ISPRSZ_VFILT1312_COEFF12_MASK 0x3FF
+#define ISPRSZ_VFILT1312_COEFF13_SHIFT 16
+#define ISPRSZ_VFILT1312_COEFF13_MASK 0x3FF0000
+
+#define ISPRSZ_VFILT1514_COEFF14_SHIFT 0
+#define ISPRSZ_VFILT1514_COEFF14_MASK 0x3FF
+#define ISPRSZ_VFILT1514_COEFF15_SHIFT 16
+#define ISPRSZ_VFILT1514_COEFF15_MASK 0x3FF0000
+
+#define ISPRSZ_VFILT1716_COEF16_SHIFT 0
+#define ISPRSZ_VFILT1716_COEF16_MASK 0x3FF
+#define ISPRSZ_VFILT1716_COEF17_SHIFT 16
+#define ISPRSZ_VFILT1716_COEF17_MASK 0x3FF0000
+
+#define ISPRSZ_VFILT1918_COEF18_SHIFT 0
+#define ISPRSZ_VFILT1918_COEF18_MASK 0x3FF
+#define ISPRSZ_VFILT1918_COEF19_SHIFT 16
+#define ISPRSZ_VFILT1918_COEF19_MASK 0x3FF0000
+
+#define ISPRSZ_VFILT2120_COEF20_SHIFT 0
+#define ISPRSZ_VFILT2120_COEF20_MASK 0x3FF
+#define ISPRSZ_VFILT2120_COEF21_SHIFT 16
+#define ISPRSZ_VFILT2120_COEF21_MASK 0x3FF0000
+
+#define ISPRSZ_VFILT2322_COEF22_SHIFT 0
+#define ISPRSZ_VFILT2322_COEF22_MASK 0x3FF
+#define ISPRSZ_VFILT2322_COEF23_SHIFT 16
+#define ISPRSZ_VFILT2322_COEF23_MASK 0x3FF0000
+
+#define ISPRSZ_VFILT2524_COEF24_SHIFT 0
+#define ISPRSZ_VFILT2524_COEF24_MASK 0x3FF
+#define ISPRSZ_VFILT2524_COEF25_SHIFT 16
+#define ISPRSZ_VFILT2524_COEF25_MASK 0x3FF0000
+
+#define ISPRSZ_VFILT2726_COEF26_SHIFT 0
+#define ISPRSZ_VFILT2726_COEF26_MASK 0x3FF
+#define ISPRSZ_VFILT2726_COEF27_SHIFT 16
+#define ISPRSZ_VFILT2726_COEF27_MASK 0x3FF0000
+
+#define ISPRSZ_VFILT2928_COEF28_SHIFT 0
+#define ISPRSZ_VFILT2928_COEF28_MASK 0x3FF
+#define ISPRSZ_VFILT2928_COEF29_SHIFT 16
+#define ISPRSZ_VFILT2928_COEF29_MASK 0x3FF0000
+
+#define ISPRSZ_VFILT3130_COEF30_SHIFT 0
+#define ISPRSZ_VFILT3130_COEF30_MASK 0x3FF
+#define ISPRSZ_VFILT3130_COEF31_SHIFT 16
+#define ISPRSZ_VFILT3130_COEF31_MASK 0x3FF0000
+
+#define ISPRSZ_YENH_CORE_SHIFT 0
+#define ISPRSZ_YENH_CORE_MASK \
+ (0xFF << ISPRSZ_YENH_CORE_SHIFT)
+#define ISPRSZ_YENH_SLOP_SHIFT 8
+#define ISPRSZ_YENH_SLOP_MASK \
+ (0xF << ISPRSZ_YENH_SLOP_SHIFT)
+#define ISPRSZ_YENH_GAIN_SHIFT 12
+#define ISPRSZ_YENH_GAIN_MASK \
+ (0xF << ISPRSZ_YENH_GAIN_SHIFT)
+#define ISPRSZ_YENH_ALGO_SHIFT 16
+#define ISPRSZ_YENH_ALGO_MASK \
+ (0x3 << ISPRSZ_YENH_ALGO_SHIFT)
+
+#define ISPH3A_PCR_AEW_ALAW_EN_SHIFT 1
+#define ISPH3A_PCR_AF_MED_TH_SHIFT 3
+#define ISPH3A_PCR_AF_RGBPOS_SHIFT 11
+#define ISPH3A_PCR_AEW_AVE2LMT_SHIFT 22
+#define ISPH3A_PCR_AEW_AVE2LMT_MASK 0xFFC00000
+#define ISPH3A_PCR_BUSYAF (1 << 15)
+#define ISPH3A_PCR_BUSYAEAWB (1 << 18)
+
+#define ISPH3A_AEWWIN1_WINHC_SHIFT 0
+#define ISPH3A_AEWWIN1_WINHC_MASK 0x3F
+#define ISPH3A_AEWWIN1_WINVC_SHIFT 6
+#define ISPH3A_AEWWIN1_WINVC_MASK 0x1FC0
+#define ISPH3A_AEWWIN1_WINW_SHIFT 13
+#define ISPH3A_AEWWIN1_WINW_MASK 0xFE000
+#define ISPH3A_AEWWIN1_WINH_SHIFT 24
+#define ISPH3A_AEWWIN1_WINH_MASK 0x7F000000
+
+#define ISPH3A_AEWINSTART_WINSH_SHIFT 0
+#define ISPH3A_AEWINSTART_WINSH_MASK 0x0FFF
+#define ISPH3A_AEWINSTART_WINSV_SHIFT 16
+#define ISPH3A_AEWINSTART_WINSV_MASK 0x0FFF0000
+
+#define ISPH3A_AEWINBLK_WINH_SHIFT 0
+#define ISPH3A_AEWINBLK_WINH_MASK 0x7F
+#define ISPH3A_AEWINBLK_WINSV_SHIFT 16
+#define ISPH3A_AEWINBLK_WINSV_MASK 0x0FFF0000
+
+#define ISPH3A_AEWSUBWIN_AEWINCH_SHIFT 0
+#define ISPH3A_AEWSUBWIN_AEWINCH_MASK 0x0F
+#define ISPH3A_AEWSUBWIN_AEWINCV_SHIFT 8
+#define ISPH3A_AEWSUBWIN_AEWINCV_MASK 0x0F00
+
+#define ISPHIST_PCR_ENABLE_SHIFT 0
+#define ISPHIST_PCR_ENABLE_MASK 0x01
+#define ISPHIST_PCR_ENABLE (1 << ISPHIST_PCR_ENABLE_SHIFT)
+#define ISPHIST_PCR_BUSY 0x02
+
+#define ISPHIST_CNT_DATASIZE_SHIFT 8
+#define ISPHIST_CNT_DATASIZE_MASK 0x0100
+#define ISPHIST_CNT_CLEAR_SHIFT 7
+#define ISPHIST_CNT_CLEAR_MASK 0x080
+#define ISPHIST_CNT_CLEAR (1 << ISPHIST_CNT_CLEAR_SHIFT)
+#define ISPHIST_CNT_CFA_SHIFT 6
+#define ISPHIST_CNT_CFA_MASK 0x040
+#define ISPHIST_CNT_BINS_SHIFT 4
+#define ISPHIST_CNT_BINS_MASK 0x030
+#define ISPHIST_CNT_SOURCE_SHIFT 3
+#define ISPHIST_CNT_SOURCE_MASK 0x08
+#define ISPHIST_CNT_SHIFT_SHIFT 0
+#define ISPHIST_CNT_SHIFT_MASK 0x07
+
+#define ISPHIST_WB_GAIN_WG00_SHIFT 24
+#define ISPHIST_WB_GAIN_WG00_MASK 0xFF000000
+#define ISPHIST_WB_GAIN_WG01_SHIFT 16
+#define ISPHIST_WB_GAIN_WG01_MASK 0xFF0000
+#define ISPHIST_WB_GAIN_WG02_SHIFT 8
+#define ISPHIST_WB_GAIN_WG02_MASK 0xFF00
+#define ISPHIST_WB_GAIN_WG03_SHIFT 0
+#define ISPHIST_WB_GAIN_WG03_MASK 0xFF
+
+#define ISPHIST_REG_START_END_MASK 0x3FFF
+#define ISPHIST_REG_START_SHIFT 16
+#define ISPHIST_REG_END_SHIFT 0
+#define ISPHIST_REG_START_MASK (ISPHIST_REG_START_END_MASK << \
+ ISPHIST_REG_START_SHIFT)
+#define ISPHIST_REG_END_MASK (ISPHIST_REG_START_END_MASK << \
+ ISPHIST_REG_END_SHIFT)
+
+#define ISPHIST_REG_MASK (ISPHIST_REG_START_MASK | \
+ ISPHIST_REG_END_MASK)
+
+#define ISPHIST_ADDR_SHIFT 0
+#define ISPHIST_ADDR_MASK 0x3FF
+
+#define ISPHIST_DATA_SHIFT 0
+#define ISPHIST_DATA_MASK 0xFFFFF
+
+#define ISPHIST_RADD_SHIFT 0
+#define ISPHIST_RADD_MASK 0xFFFFFFFF
+
+#define ISPHIST_RADD_OFF_SHIFT 0
+#define ISPHIST_RADD_OFF_MASK 0xFFFF
+
+#define ISPHIST_HV_INFO_HSIZE_SHIFT 16
+#define ISPHIST_HV_INFO_HSIZE_MASK 0x3FFF0000
+#define ISPHIST_HV_INFO_VSIZE_SHIFT 0
+#define ISPHIST_HV_INFO_VSIZE_MASK 0x3FFF
+
+#define ISPHIST_HV_INFO_MASK 0x3FFF3FFF
+
+#define ISPCCDC_LSC_ENABLE 1
+#define ISPCCDC_LSC_BUSY (1 << 7)
+#define ISPCCDC_LSC_GAIN_MODE_N_MASK 0x700
+#define ISPCCDC_LSC_GAIN_MODE_N_SHIFT 8
+#define ISPCCDC_LSC_GAIN_MODE_M_MASK 0x3800
+#define ISPCCDC_LSC_GAIN_MODE_M_SHIFT 12
+#define ISPCCDC_LSC_GAIN_FORMAT_MASK 0xE
+#define ISPCCDC_LSC_GAIN_FORMAT_SHIFT 1
+#define ISPCCDC_LSC_AFTER_REFORMATTER_MASK (1<<6)
+
+#define ISPCCDC_LSC_INITIAL_X_MASK 0x3F
+#define ISPCCDC_LSC_INITIAL_X_SHIFT 0
+#define ISPCCDC_LSC_INITIAL_Y_MASK 0x3F0000
+#define ISPCCDC_LSC_INITIAL_Y_SHIFT 16
+
+/* -----------------------------------------------------------------------------
+ * CSI2 receiver registers (ES2.0)
+ */
+
+#define ISPCSI2_REVISION (0x000)
+#define ISPCSI2_SYSCONFIG (0x010)
+#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT 12
+#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_MASK \
+ (0x3 << ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT)
+#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_FORCE \
+ (0x0 << ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT)
+#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_NO \
+ (0x1 << ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT)
+#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SMART \
+ (0x2 << ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT)
+#define ISPCSI2_SYSCONFIG_SOFT_RESET (1 << 1)
+#define ISPCSI2_SYSCONFIG_AUTO_IDLE (1 << 0)
+
+#define ISPCSI2_SYSSTATUS (0x014)
+#define ISPCSI2_SYSSTATUS_RESET_DONE (1 << 0)
+
+#define ISPCSI2_IRQSTATUS (0x018)
+#define ISPCSI2_IRQSTATUS_OCP_ERR_IRQ (1 << 14)
+#define ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ (1 << 13)
+#define ISPCSI2_IRQSTATUS_ECC_CORRECTION_IRQ (1 << 12)
+#define ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ (1 << 11)
+#define ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ (1 << 10)
+#define ISPCSI2_IRQSTATUS_COMPLEXIO1_ERR_IRQ (1 << 9)
+#define ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ (1 << 8)
+#define ISPCSI2_IRQSTATUS_CONTEXT(n) (1 << (n))
+
+#define ISPCSI2_IRQENABLE (0x01c)
+#define ISPCSI2_CTRL (0x040)
+#define ISPCSI2_CTRL_VP_CLK_EN (1 << 15)
+#define ISPCSI2_CTRL_VP_ONLY_EN (1 << 11)
+#define ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT 8
+#define ISPCSI2_CTRL_VP_OUT_CTRL_MASK \
+ (3 << ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT)
+#define ISPCSI2_CTRL_DBG_EN (1 << 7)
+#define ISPCSI2_CTRL_BURST_SIZE_SHIFT 5
+#define ISPCSI2_CTRL_BURST_SIZE_MASK \
+ (3 << ISPCSI2_CTRL_BURST_SIZE_SHIFT)
+#define ISPCSI2_CTRL_FRAME (1 << 3)
+#define ISPCSI2_CTRL_ECC_EN (1 << 2)
+#define ISPCSI2_CTRL_SECURE (1 << 1)
+#define ISPCSI2_CTRL_IF_EN (1 << 0)
+
+#define ISPCSI2_DBG_H (0x044)
+#define ISPCSI2_GNQ (0x048)
+#define ISPCSI2_PHY_CFG (0x050)
+#define ISPCSI2_PHY_CFG_RESET_CTRL (1 << 30)
+#define ISPCSI2_PHY_CFG_RESET_DONE (1 << 29)
+#define ISPCSI2_PHY_CFG_PWR_CMD_SHIFT 27
+#define ISPCSI2_PHY_CFG_PWR_CMD_MASK \
+ (0x3 << ISPCSI2_PHY_CFG_PWR_CMD_SHIFT)
+#define ISPCSI2_PHY_CFG_PWR_CMD_OFF \
+ (0x0 << ISPCSI2_PHY_CFG_PWR_CMD_SHIFT)
+#define ISPCSI2_PHY_CFG_PWR_CMD_ON \
+ (0x1 << ISPCSI2_PHY_CFG_PWR_CMD_SHIFT)
+#define ISPCSI2_PHY_CFG_PWR_CMD_ULPW \
+ (0x2 << ISPCSI2_PHY_CFG_PWR_CMD_SHIFT)
+#define ISPCSI2_PHY_CFG_PWR_STATUS_SHIFT 25
+#define ISPCSI2_PHY_CFG_PWR_STATUS_MASK \
+ (0x3 << ISPCSI2_PHY_CFG_PWR_STATUS_SHIFT)
+#define ISPCSI2_PHY_CFG_PWR_STATUS_OFF \
+ (0x0 << ISPCSI2_PHY_CFG_PWR_STATUS_SHIFT)
+#define ISPCSI2_PHY_CFG_PWR_STATUS_ON \
+ (0x1 << ISPCSI2_PHY_CFG_PWR_STATUS_SHIFT)
+#define ISPCSI2_PHY_CFG_PWR_STATUS_ULPW \
+ (0x2 << ISPCSI2_PHY_CFG_PWR_STATUS_SHIFT)
+#define ISPCSI2_PHY_CFG_PWR_AUTO (1 << 24)
+
+#define ISPCSI2_PHY_CFG_DATA_POL_SHIFT(n) (3 + ((n) * 4))
+#define ISPCSI2_PHY_CFG_DATA_POL_MASK(n) \
+ (0x1 << ISPCSI2_PHY_CFG_DATA_POL_SHIFT(n))
+#define ISPCSI2_PHY_CFG_DATA_POL_PN(n) \
+ (0x0 << ISPCSI2_PHY_CFG_DATA_POL_SHIFT(n))
+#define ISPCSI2_PHY_CFG_DATA_POL_NP(n) \
+ (0x1 << ISPCSI2_PHY_CFG_DATA_POL_SHIFT(n))
+
+#define ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n) ((n) * 4)
+#define ISPCSI2_PHY_CFG_DATA_POSITION_MASK(n) \
+ (0x7 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n))
+#define ISPCSI2_PHY_CFG_DATA_POSITION_NC(n) \
+ (0x0 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n))
+#define ISPCSI2_PHY_CFG_DATA_POSITION_1(n) \
+ (0x1 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n))
+#define ISPCSI2_PHY_CFG_DATA_POSITION_2(n) \
+ (0x2 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n))
+#define ISPCSI2_PHY_CFG_DATA_POSITION_3(n) \
+ (0x3 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n))
+#define ISPCSI2_PHY_CFG_DATA_POSITION_4(n) \
+ (0x4 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n))
+#define ISPCSI2_PHY_CFG_DATA_POSITION_5(n) \
+ (0x5 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n))
+
+#define ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT 3
+#define ISPCSI2_PHY_CFG_CLOCK_POL_MASK \
+ (0x1 << ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT)
+#define ISPCSI2_PHY_CFG_CLOCK_POL_PN \
+ (0x0 << ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT)
+#define ISPCSI2_PHY_CFG_CLOCK_POL_NP \
+ (0x1 << ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT)
+
+#define ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT 0
+#define ISPCSI2_PHY_CFG_CLOCK_POSITION_MASK \
+ (0x7 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT)
+#define ISPCSI2_PHY_CFG_CLOCK_POSITION_1 \
+ (0x1 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT)
+#define ISPCSI2_PHY_CFG_CLOCK_POSITION_2 \
+ (0x2 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT)
+#define ISPCSI2_PHY_CFG_CLOCK_POSITION_3 \
+ (0x3 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT)
+#define ISPCSI2_PHY_CFG_CLOCK_POSITION_4 \
+ (0x4 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT)
+#define ISPCSI2_PHY_CFG_CLOCK_POSITION_5 \
+ (0x5 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT)
+
+#define ISPCSI2_PHY_IRQSTATUS (0x054)
+#define ISPCSI2_PHY_IRQSTATUS_STATEALLULPMEXIT (1 << 26)
+#define ISPCSI2_PHY_IRQSTATUS_STATEALLULPMENTER (1 << 25)
+#define ISPCSI2_PHY_IRQSTATUS_STATEULPM5 (1 << 24)
+#define ISPCSI2_PHY_IRQSTATUS_STATEULPM4 (1 << 23)
+#define ISPCSI2_PHY_IRQSTATUS_STATEULPM3 (1 << 22)
+#define ISPCSI2_PHY_IRQSTATUS_STATEULPM2 (1 << 21)
+#define ISPCSI2_PHY_IRQSTATUS_STATEULPM1 (1 << 20)
+#define ISPCSI2_PHY_IRQSTATUS_ERRCONTROL5 (1 << 19)
+#define ISPCSI2_PHY_IRQSTATUS_ERRCONTROL4 (1 << 18)
+#define ISPCSI2_PHY_IRQSTATUS_ERRCONTROL3 (1 << 17)
+#define ISPCSI2_PHY_IRQSTATUS_ERRCONTROL2 (1 << 16)
+#define ISPCSI2_PHY_IRQSTATUS_ERRCONTROL1 (1 << 15)
+#define ISPCSI2_PHY_IRQSTATUS_ERRESC5 (1 << 14)
+#define ISPCSI2_PHY_IRQSTATUS_ERRESC4 (1 << 13)
+#define ISPCSI2_PHY_IRQSTATUS_ERRESC3 (1 << 12)
+#define ISPCSI2_PHY_IRQSTATUS_ERRESC2 (1 << 11)
+#define ISPCSI2_PHY_IRQSTATUS_ERRESC1 (1 << 10)
+#define ISPCSI2_PHY_IRQSTATUS_ERRSOTSYNCHS5 (1 << 9)
+#define ISPCSI2_PHY_IRQSTATUS_ERRSOTSYNCHS4 (1 << 8)
+#define ISPCSI2_PHY_IRQSTATUS_ERRSOTSYNCHS3 (1 << 7)
+#define ISPCSI2_PHY_IRQSTATUS_ERRSOTSYNCHS2 (1 << 6)
+#define ISPCSI2_PHY_IRQSTATUS_ERRSOTSYNCHS1 (1 << 5)
+#define ISPCSI2_PHY_IRQSTATUS_ERRSOTHS5 (1 << 4)
+#define ISPCSI2_PHY_IRQSTATUS_ERRSOTHS4 (1 << 3)
+#define ISPCSI2_PHY_IRQSTATUS_ERRSOTHS3 (1 << 2)
+#define ISPCSI2_PHY_IRQSTATUS_ERRSOTHS2 (1 << 1)
+#define ISPCSI2_PHY_IRQSTATUS_ERRSOTHS1 1
+
+#define ISPCSI2_SHORT_PACKET (0x05c)
+#define ISPCSI2_PHY_IRQENABLE (0x060)
+#define ISPCSI2_PHY_IRQENABLE_STATEALLULPMEXIT (1 << 26)
+#define ISPCSI2_PHY_IRQENABLE_STATEALLULPMENTER (1 << 25)
+#define ISPCSI2_PHY_IRQENABLE_STATEULPM5 (1 << 24)
+#define ISPCSI2_PHY_IRQENABLE_STATEULPM4 (1 << 23)
+#define ISPCSI2_PHY_IRQENABLE_STATEULPM3 (1 << 22)
+#define ISPCSI2_PHY_IRQENABLE_STATEULPM2 (1 << 21)
+#define ISPCSI2_PHY_IRQENABLE_STATEULPM1 (1 << 20)
+#define ISPCSI2_PHY_IRQENABLE_ERRCONTROL5 (1 << 19)
+#define ISPCSI2_PHY_IRQENABLE_ERRCONTROL4 (1 << 18)
+#define ISPCSI2_PHY_IRQENABLE_ERRCONTROL3 (1 << 17)
+#define ISPCSI2_PHY_IRQENABLE_ERRCONTROL2 (1 << 16)
+#define ISPCSI2_PHY_IRQENABLE_ERRCONTROL1 (1 << 15)
+#define ISPCSI2_PHY_IRQENABLE_ERRESC5 (1 << 14)
+#define ISPCSI2_PHY_IRQENABLE_ERRESC4 (1 << 13)
+#define ISPCSI2_PHY_IRQENABLE_ERRESC3 (1 << 12)
+#define ISPCSI2_PHY_IRQENABLE_ERRESC2 (1 << 11)
+#define ISPCSI2_PHY_IRQENABLE_ERRESC1 (1 << 10)
+#define ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS5 (1 << 9)
+#define ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS4 (1 << 8)
+#define ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS3 (1 << 7)
+#define ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS2 (1 << 6)
+#define ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS1 (1 << 5)
+#define ISPCSI2_PHY_IRQENABLE_ERRSOTHS5 (1 << 4)
+#define ISPCSI2_PHY_IRQENABLE_ERRSOTHS4 (1 << 3)
+#define ISPCSI2_PHY_IRQENABLE_ERRSOTHS3 (1 << 2)
+#define ISPCSI2_PHY_IRQENABLE_ERRSOTHS2 (1 << 1)
+#define ISPCSI2_PHY_IRQENABLE_ERRSOTHS1 (1 << 0)
+
+#define ISPCSI2_DBG_P (0x068)
+#define ISPCSI2_TIMING (0x06c)
+#define ISPCSI2_TIMING_FORCE_RX_MODE_IO(n) (1 << ((16 * ((n) - 1)) + 15))
+#define ISPCSI2_TIMING_STOP_STATE_X16_IO(n) (1 << ((16 * ((n) - 1)) + 14))
+#define ISPCSI2_TIMING_STOP_STATE_X4_IO(n) (1 << ((16 * ((n) - 1)) + 13))
+#define ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_SHIFT(n) (16 * ((n) - 1))
+#define ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_MASK(n) \
+ (0x1fff << ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_SHIFT(n))
+
+#define ISPCSI2_CTX_CTRL1(n) ((0x070) + 0x20 * (n))
+#define ISPCSI2_CTX_CTRL1_COUNT_SHIFT 8
+#define ISPCSI2_CTX_CTRL1_COUNT_MASK \
+ (0xff << ISPCSI2_CTX_CTRL1_COUNT_SHIFT)
+#define ISPCSI2_CTX_CTRL1_EOF_EN (1 << 7)
+#define ISPCSI2_CTX_CTRL1_EOL_EN (1 << 6)
+#define ISPCSI2_CTX_CTRL1_CS_EN (1 << 5)
+#define ISPCSI2_CTX_CTRL1_COUNT_UNLOCK (1 << 4)
+#define ISPCSI2_CTX_CTRL1_PING_PONG (1 << 3)
+#define ISPCSI2_CTX_CTRL1_CTX_EN (1 << 0)
+
+#define ISPCSI2_CTX_CTRL2(n) ((0x074) + 0x20 * (n))
+#define ISPCSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT 13
+#define ISPCSI2_CTX_CTRL2_USER_DEF_MAP_MASK \
+ (0x3 << ISPCSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT)
+#define ISPCSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT 11
+#define ISPCSI2_CTX_CTRL2_VIRTUAL_ID_MASK \
+ (0x3 << ISPCSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT)
+#define ISPCSI2_CTX_CTRL2_DPCM_PRED (1 << 10)
+#define ISPCSI2_CTX_CTRL2_FORMAT_SHIFT 0
+#define ISPCSI2_CTX_CTRL2_FORMAT_MASK \
+ (0x3ff << ISPCSI2_CTX_CTRL2_FORMAT_SHIFT)
+#define ISPCSI2_CTX_CTRL2_FRAME_SHIFT 16
+#define ISPCSI2_CTX_CTRL2_FRAME_MASK \
+ (0xffff << ISPCSI2_CTX_CTRL2_FRAME_SHIFT)
+
+#define ISPCSI2_CTX_DAT_OFST(n) ((0x078) + 0x20 * (n))
+#define ISPCSI2_CTX_DAT_OFST_OFST_SHIFT 0
+#define ISPCSI2_CTX_DAT_OFST_OFST_MASK \
+ (0x1ffe0 << ISPCSI2_CTX_DAT_OFST_OFST_SHIFT)
+
+#define ISPCSI2_CTX_DAT_PING_ADDR(n) ((0x07c) + 0x20 * (n))
+#define ISPCSI2_CTX_DAT_PONG_ADDR(n) ((0x080) + 0x20 * (n))
+#define ISPCSI2_CTX_IRQENABLE(n) ((0x084) + 0x20 * (n))
+#define ISPCSI2_CTX_IRQENABLE_ECC_CORRECTION_IRQ (1 << 8)
+#define ISPCSI2_CTX_IRQENABLE_LINE_NUMBER_IRQ (1 << 7)
+#define ISPCSI2_CTX_IRQENABLE_FRAME_NUMBER_IRQ (1 << 6)
+#define ISPCSI2_CTX_IRQENABLE_CS_IRQ (1 << 5)
+#define ISPCSI2_CTX_IRQENABLE_LE_IRQ (1 << 3)
+#define ISPCSI2_CTX_IRQENABLE_LS_IRQ (1 << 2)
+#define ISPCSI2_CTX_IRQENABLE_FE_IRQ (1 << 1)
+#define ISPCSI2_CTX_IRQENABLE_FS_IRQ (1 << 0)
+
+#define ISPCSI2_CTX_IRQSTATUS(n) ((0x088) + 0x20 * (n))
+#define ISPCSI2_CTX_IRQSTATUS_ECC_CORRECTION_IRQ (1 << 8)
+#define ISPCSI2_CTX_IRQSTATUS_LINE_NUMBER_IRQ (1 << 7)
+#define ISPCSI2_CTX_IRQSTATUS_FRAME_NUMBER_IRQ (1 << 6)
+#define ISPCSI2_CTX_IRQSTATUS_CS_IRQ (1 << 5)
+#define ISPCSI2_CTX_IRQSTATUS_LE_IRQ (1 << 3)
+#define ISPCSI2_CTX_IRQSTATUS_LS_IRQ (1 << 2)
+#define ISPCSI2_CTX_IRQSTATUS_FE_IRQ (1 << 1)
+#define ISPCSI2_CTX_IRQSTATUS_FS_IRQ (1 << 0)
+
+#define ISPCSI2_CTX_CTRL3(n) ((0x08c) + 0x20 * (n))
+#define ISPCSI2_CTX_CTRL3_ALPHA_SHIFT 5
+#define ISPCSI2_CTX_CTRL3_ALPHA_MASK \
+ (0x3fff << ISPCSI2_CTX_CTRL3_ALPHA_SHIFT)
+
+/* This instance is for OMAP3630 only */
+#define ISPCSI2_CTX_TRANSCODEH(n) (0x000 + 0x8 * (n))
+#define ISPCSI2_CTX_TRANSCODEH_HCOUNT_SHIFT 16
+#define ISPCSI2_CTX_TRANSCODEH_HCOUNT_MASK \
+ (0x1fff << ISPCSI2_CTX_TRANSCODEH_HCOUNT_SHIFT)
+#define ISPCSI2_CTX_TRANSCODEH_HSKIP_SHIFT 0
+#define ISPCSI2_CTX_TRANSCODEH_HSKIP_MASK \
+ (0x1fff << ISPCSI2_CTX_TRANSCODEH_HCOUNT_SHIFT)
+#define ISPCSI2_CTX_TRANSCODEV(n) (0x004 + 0x8 * (n))
+#define ISPCSI2_CTX_TRANSCODEV_VCOUNT_SHIFT 16
+#define ISPCSI2_CTX_TRANSCODEV_VCOUNT_MASK \
+ (0x1fff << ISPCSI2_CTX_TRANSCODEV_VCOUNT_SHIFT)
+#define ISPCSI2_CTX_TRANSCODEV_VSKIP_SHIFT 0
+#define ISPCSI2_CTX_TRANSCODEV_VSKIP_MASK \
+ (0x1fff << ISPCSI2_CTX_TRANSCODEV_VCOUNT_SHIFT)
+
+/* -----------------------------------------------------------------------------
+ * CSI PHY registers
+ */
+
+#define ISPCSIPHY_REG0 (0x000)
+#define ISPCSIPHY_REG0_THS_TERM_SHIFT 8
+#define ISPCSIPHY_REG0_THS_TERM_MASK \
+ (0xff << ISPCSIPHY_REG0_THS_TERM_SHIFT)
+#define ISPCSIPHY_REG0_THS_SETTLE_SHIFT 0
+#define ISPCSIPHY_REG0_THS_SETTLE_MASK \
+ (0xff << ISPCSIPHY_REG0_THS_SETTLE_SHIFT)
+
+#define ISPCSIPHY_REG1 (0x004)
+#define ISPCSIPHY_REG1_RESET_DONE_CTRLCLK (1 << 29)
+/* This field is for OMAP3630 only */
+#define ISPCSIPHY_REG1_CLOCK_MISS_DETECTOR_STATUS (1 << 25)
+#define ISPCSIPHY_REG1_TCLK_TERM_SHIFT 18
+#define ISPCSIPHY_REG1_TCLK_TERM_MASK \
+ (0x7f << ISPCSIPHY_REG1_TCLK_TERM_SHIFT)
+#define ISPCSIPHY_REG1_DPHY_HS_SYNC_PATTERN_SHIFT 10
+#define ISPCSIPHY_REG1_DPHY_HS_SYNC_PATTERN_MASK \
+ (0xff << ISPCSIPHY_REG1_DPHY_HS_SYNC_PATTERN)
+/* This field is for OMAP3430 only */
+#define ISPCSIPHY_REG1_TCLK_MISS_SHIFT 8
+#define ISPCSIPHY_REG1_TCLK_MISS_MASK \
+ (0x3 << ISPCSIPHY_REG1_TCLK_MISS_SHIFT)
+/* This field is for OMAP3630 only */
+#define ISPCSIPHY_REG1_CTRLCLK_DIV_FACTOR_SHIFT 8
+#define ISPCSIPHY_REG1_CTRLCLK_DIV_FACTOR_MASK \
+ (0x3 << ISPCSIPHY_REG1_CTRLCLK_DIV_FACTOR_SHIFT)
+#define ISPCSIPHY_REG1_TCLK_SETTLE_SHIFT 0
+#define ISPCSIPHY_REG1_TCLK_SETTLE_MASK \
+ (0xff << ISPCSIPHY_REG1_TCLK_SETTLE_SHIFT)
+
+/* This register is for OMAP3630 only */
+#define ISPCSIPHY_REG2 (0x008)
+#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC0_SHIFT 30
+#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC0_MASK \
+ (0x3 << ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC0_SHIFT)
+#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC1_SHIFT 28
+#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC1_MASK \
+ (0x3 << ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC1_SHIFT)
+#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC2_SHIFT 26
+#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC2_MASK \
+ (0x3 << ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC2_SHIFT)
+#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC3_SHIFT 24
+#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC3_MASK \
+ (0x3 << ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC3_SHIFT)
+#define ISPCSIPHY_REG2_CCP2_SYNC_PATTERN_SHIFT 0
+#define ISPCSIPHY_REG2_CCP2_SYNC_PATTERN_MASK \
+ (0x7fffff << ISPCSIPHY_REG2_CCP2_SYNC_PATTERN_SHIFT)
+
+#endif /* OMAP3_ISP_REG_H */
diff --git a/drivers/media/video/omap3isp/ispresizer.c b/drivers/media/video/omap3isp/ispresizer.c
new file mode 100644
index 00000000..6958a9e3
--- /dev/null
+++ b/drivers/media/video/omap3isp/ispresizer.c
@@ -0,0 +1,1737 @@
+/*
+ * ispresizer.c
+ *
+ * TI OMAP3 ISP - Resizer module
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/device.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+
+#include "isp.h"
+#include "ispreg.h"
+#include "ispresizer.h"
+
+/*
+ * Resizer Constants
+ */
+#define MIN_RESIZE_VALUE 64
+#define MID_RESIZE_VALUE 512
+#define MAX_RESIZE_VALUE 1024
+
+#define MIN_IN_WIDTH 32
+#define MIN_IN_HEIGHT 32
+#define MAX_IN_WIDTH_MEMORY_MODE 4095
+#define MAX_IN_WIDTH_ONTHEFLY_MODE_ES1 1280
+#define MAX_IN_WIDTH_ONTHEFLY_MODE_ES2 4095
+#define MAX_IN_HEIGHT 4095
+
+#define MIN_OUT_WIDTH 16
+#define MIN_OUT_HEIGHT 2
+#define MAX_OUT_HEIGHT 4095
+
+/*
+ * Resizer Use Constraints
+ * "TRM ES3.1, table 12-46"
+ */
+#define MAX_4TAP_OUT_WIDTH_ES1 1280
+#define MAX_7TAP_OUT_WIDTH_ES1 640
+#define MAX_4TAP_OUT_WIDTH_ES2 3312
+#define MAX_7TAP_OUT_WIDTH_ES2 1650
+#define MAX_4TAP_OUT_WIDTH_3630 4096
+#define MAX_7TAP_OUT_WIDTH_3630 2048
+
+/*
+ * Constants for ratio calculation
+ */
+#define RESIZE_DIVISOR 256
+#define DEFAULT_PHASE 1
+
+/*
+ * Default (and only) configuration of filter coefficients.
+ * 7-tap mode is for scale factors 0.25x to 0.5x.
+ * 4-tap mode is for scale factors 0.5x to 4.0x.
+ * There shouldn't be any reason to recalculate these, EVER.
+ */
+static const struct isprsz_coef filter_coefs = {
+ /* For 8-phase 4-tap horizontal filter: */
+ {
+ 0x0000, 0x0100, 0x0000, 0x0000,
+ 0x03FA, 0x00F6, 0x0010, 0x0000,
+ 0x03F9, 0x00DB, 0x002C, 0x0000,
+ 0x03FB, 0x00B3, 0x0053, 0x03FF,
+ 0x03FD, 0x0082, 0x0084, 0x03FD,
+ 0x03FF, 0x0053, 0x00B3, 0x03FB,
+ 0x0000, 0x002C, 0x00DB, 0x03F9,
+ 0x0000, 0x0010, 0x00F6, 0x03FA
+ },
+ /* For 8-phase 4-tap vertical filter: */
+ {
+ 0x0000, 0x0100, 0x0000, 0x0000,
+ 0x03FA, 0x00F6, 0x0010, 0x0000,
+ 0x03F9, 0x00DB, 0x002C, 0x0000,
+ 0x03FB, 0x00B3, 0x0053, 0x03FF,
+ 0x03FD, 0x0082, 0x0084, 0x03FD,
+ 0x03FF, 0x0053, 0x00B3, 0x03FB,
+ 0x0000, 0x002C, 0x00DB, 0x03F9,
+ 0x0000, 0x0010, 0x00F6, 0x03FA
+ },
+ /* For 4-phase 7-tap horizontal filter: */
+ #define DUMMY 0
+ {
+ 0x0004, 0x0023, 0x005A, 0x0058, 0x0023, 0x0004, 0x0000, DUMMY,
+ 0x0002, 0x0018, 0x004d, 0x0060, 0x0031, 0x0008, 0x0000, DUMMY,
+ 0x0001, 0x000f, 0x003f, 0x0062, 0x003f, 0x000f, 0x0001, DUMMY,
+ 0x0000, 0x0008, 0x0031, 0x0060, 0x004d, 0x0018, 0x0002, DUMMY
+ },
+ /* For 4-phase 7-tap vertical filter: */
+ {
+ 0x0004, 0x0023, 0x005A, 0x0058, 0x0023, 0x0004, 0x0000, DUMMY,
+ 0x0002, 0x0018, 0x004d, 0x0060, 0x0031, 0x0008, 0x0000, DUMMY,
+ 0x0001, 0x000f, 0x003f, 0x0062, 0x003f, 0x000f, 0x0001, DUMMY,
+ 0x0000, 0x0008, 0x0031, 0x0060, 0x004d, 0x0018, 0x0002, DUMMY
+ }
+ /*
+ * The dummy padding is required in 7-tap mode because of how the
+ * registers are arranged physically.
+ */
+ #undef DUMMY
+};
+
+/*
+ * __resizer_get_format - helper function for getting resizer format
+ * @res : pointer to resizer private structure
+ * @pad : pad number
+ * @fh : V4L2 subdev file handle
+ * @which : wanted subdev format
+ * return zero
+ */
+static struct v4l2_mbus_framefmt *
+__resizer_get_format(struct isp_res_device *res, struct v4l2_subdev_fh *fh,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(fh, pad);
+ else
+ return &res->formats[pad];
+}
+
+/*
+ * __resizer_get_crop - helper function for getting resizer crop rectangle
+ * @res : pointer to resizer private structure
+ * @fh : V4L2 subdev file handle
+ * @which : wanted subdev crop rectangle
+ */
+static struct v4l2_rect *
+__resizer_get_crop(struct isp_res_device *res, struct v4l2_subdev_fh *fh,
+ enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_crop(fh, RESZ_PAD_SINK);
+ else
+ return &res->crop.request;
+}
+
+/*
+ * resizer_set_filters - Set resizer filters
+ * @res: Device context.
+ * @h_coeff: horizontal coefficient
+ * @v_coeff: vertical coefficient
+ * Return none
+ */
+static void resizer_set_filters(struct isp_res_device *res, const u16 *h_coeff,
+ const u16 *v_coeff)
+{
+ struct isp_device *isp = to_isp_device(res);
+ u32 startaddr_h, startaddr_v, tmp_h, tmp_v;
+ int i;
+
+ startaddr_h = ISPRSZ_HFILT10;
+ startaddr_v = ISPRSZ_VFILT10;
+
+ for (i = 0; i < COEFF_CNT; i += 2) {
+ tmp_h = h_coeff[i] |
+ (h_coeff[i + 1] << ISPRSZ_HFILT_COEF1_SHIFT);
+ tmp_v = v_coeff[i] |
+ (v_coeff[i + 1] << ISPRSZ_VFILT_COEF1_SHIFT);
+ isp_reg_writel(isp, tmp_h, OMAP3_ISP_IOMEM_RESZ, startaddr_h);
+ isp_reg_writel(isp, tmp_v, OMAP3_ISP_IOMEM_RESZ, startaddr_v);
+ startaddr_h += 4;
+ startaddr_v += 4;
+ }
+}
+
+/*
+ * resizer_set_bilinear - Chrominance horizontal algorithm select
+ * @res: Device context.
+ * @type: Filtering interpolation type.
+ *
+ * Filtering that is same as luminance processing is
+ * intended only for downsampling, and bilinear interpolation
+ * is intended only for upsampling.
+ */
+static void resizer_set_bilinear(struct isp_res_device *res,
+ enum resizer_chroma_algo type)
+{
+ struct isp_device *isp = to_isp_device(res);
+
+ if (type == RSZ_BILINEAR)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
+ ISPRSZ_CNT_CBILIN);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
+ ISPRSZ_CNT_CBILIN);
+}
+
+/*
+ * resizer_set_ycpos - Luminance and chrominance order
+ * @res: Device context.
+ * @order: order type.
+ */
+static void resizer_set_ycpos(struct isp_res_device *res,
+ enum v4l2_mbus_pixelcode pixelcode)
+{
+ struct isp_device *isp = to_isp_device(res);
+
+ switch (pixelcode) {
+ case V4L2_MBUS_FMT_YUYV8_1X16:
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
+ ISPRSZ_CNT_YCPOS);
+ break;
+ case V4L2_MBUS_FMT_UYVY8_1X16:
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
+ ISPRSZ_CNT_YCPOS);
+ break;
+ default:
+ return;
+ }
+}
+
+/*
+ * resizer_set_phase - Setup horizontal and vertical starting phase
+ * @res: Device context.
+ * @h_phase: horizontal phase parameters.
+ * @v_phase: vertical phase parameters.
+ *
+ * Horizontal and vertical phase range is 0 to 7
+ */
+static void resizer_set_phase(struct isp_res_device *res, u32 h_phase,
+ u32 v_phase)
+{
+ struct isp_device *isp = to_isp_device(res);
+ u32 rgval = 0;
+
+ rgval = isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT) &
+ ~(ISPRSZ_CNT_HSTPH_MASK | ISPRSZ_CNT_VSTPH_MASK);
+ rgval |= (h_phase << ISPRSZ_CNT_HSTPH_SHIFT) & ISPRSZ_CNT_HSTPH_MASK;
+ rgval |= (v_phase << ISPRSZ_CNT_VSTPH_SHIFT) & ISPRSZ_CNT_VSTPH_MASK;
+
+ isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT);
+}
+
+/*
+ * resizer_set_luma - Setup luminance enhancer parameters
+ * @res: Device context.
+ * @luma: Structure for luminance enhancer parameters.
+ *
+ * Algorithm select:
+ * 0x0: Disable
+ * 0x1: [-1 2 -1]/2 high-pass filter
+ * 0x2: [-1 -2 6 -2 -1]/4 high-pass filter
+ *
+ * Maximum gain:
+ * The data is coded in U4Q4 representation.
+ *
+ * Slope:
+ * The data is coded in U4Q4 representation.
+ *
+ * Coring offset:
+ * The data is coded in U8Q0 representation.
+ *
+ * The new luminance value is computed as:
+ * Y += HPF(Y) x max(GAIN, (HPF(Y) - CORE) x SLOP + 8) >> 4.
+ */
+static void resizer_set_luma(struct isp_res_device *res,
+ struct resizer_luma_yenh *luma)
+{
+ struct isp_device *isp = to_isp_device(res);
+ u32 rgval = 0;
+
+ rgval = (luma->algo << ISPRSZ_YENH_ALGO_SHIFT)
+ & ISPRSZ_YENH_ALGO_MASK;
+ rgval |= (luma->gain << ISPRSZ_YENH_GAIN_SHIFT)
+ & ISPRSZ_YENH_GAIN_MASK;
+ rgval |= (luma->slope << ISPRSZ_YENH_SLOP_SHIFT)
+ & ISPRSZ_YENH_SLOP_MASK;
+ rgval |= (luma->core << ISPRSZ_YENH_CORE_SHIFT)
+ & ISPRSZ_YENH_CORE_MASK;
+
+ isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_YENH);
+}
+
+/*
+ * resizer_set_source - Input source select
+ * @res: Device context.
+ * @source: Input source type
+ *
+ * If this field is set to RESIZER_INPUT_VP, the resizer input is fed from
+ * Preview/CCDC engine, otherwise from memory.
+ */
+static void resizer_set_source(struct isp_res_device *res,
+ enum resizer_input_entity source)
+{
+ struct isp_device *isp = to_isp_device(res);
+
+ if (source == RESIZER_INPUT_MEMORY)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
+ ISPRSZ_CNT_INPSRC);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
+ ISPRSZ_CNT_INPSRC);
+}
+
+/*
+ * resizer_set_ratio - Setup horizontal and vertical resizing value
+ * @res: Device context.
+ * @ratio: Structure for ratio parameters.
+ *
+ * Resizing range from 64 to 1024
+ */
+static void resizer_set_ratio(struct isp_res_device *res,
+ const struct resizer_ratio *ratio)
+{
+ struct isp_device *isp = to_isp_device(res);
+ const u16 *h_filter, *v_filter;
+ u32 rgval = 0;
+
+ rgval = isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT) &
+ ~(ISPRSZ_CNT_HRSZ_MASK | ISPRSZ_CNT_VRSZ_MASK);
+ rgval |= ((ratio->horz - 1) << ISPRSZ_CNT_HRSZ_SHIFT)
+ & ISPRSZ_CNT_HRSZ_MASK;
+ rgval |= ((ratio->vert - 1) << ISPRSZ_CNT_VRSZ_SHIFT)
+ & ISPRSZ_CNT_VRSZ_MASK;
+ isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT);
+
+ /* prepare horizontal filter coefficients */
+ if (ratio->horz > MID_RESIZE_VALUE)
+ h_filter = &filter_coefs.h_filter_coef_7tap[0];
+ else
+ h_filter = &filter_coefs.h_filter_coef_4tap[0];
+
+ /* prepare vertical filter coefficients */
+ if (ratio->vert > MID_RESIZE_VALUE)
+ v_filter = &filter_coefs.v_filter_coef_7tap[0];
+ else
+ v_filter = &filter_coefs.v_filter_coef_4tap[0];
+
+ resizer_set_filters(res, h_filter, v_filter);
+}
+
+/*
+ * resizer_set_dst_size - Setup the output height and width
+ * @res: Device context.
+ * @width: Output width.
+ * @height: Output height.
+ *
+ * Width :
+ * The value must be EVEN.
+ *
+ * Height:
+ * The number of bytes written to SDRAM must be
+ * a multiple of 16-bytes if the vertical resizing factor
+ * is greater than 1x (upsizing)
+ */
+static void resizer_set_output_size(struct isp_res_device *res,
+ u32 width, u32 height)
+{
+ struct isp_device *isp = to_isp_device(res);
+ u32 rgval = 0;
+
+ dev_dbg(isp->dev, "Output size[w/h]: %dx%d\n", width, height);
+ rgval = (width << ISPRSZ_OUT_SIZE_HORZ_SHIFT)
+ & ISPRSZ_OUT_SIZE_HORZ_MASK;
+ rgval |= (height << ISPRSZ_OUT_SIZE_VERT_SHIFT)
+ & ISPRSZ_OUT_SIZE_VERT_MASK;
+ isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_OUT_SIZE);
+}
+
+/*
+ * resizer_set_output_offset - Setup memory offset for the output lines.
+ * @res: Device context.
+ * @offset: Memory offset.
+ *
+ * The 5 LSBs are forced to be zeros by the hardware to align on a 32-byte
+ * boundary; the 5 LSBs are read-only. For optimal use of SDRAM bandwidth,
+ * the SDRAM line offset must be set on a 256-byte boundary
+ */
+static void resizer_set_output_offset(struct isp_res_device *res, u32 offset)
+{
+ struct isp_device *isp = to_isp_device(res);
+
+ isp_reg_writel(isp, offset, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTOFF);
+}
+
+/*
+ * resizer_set_start - Setup vertical and horizontal start position
+ * @res: Device context.
+ * @left: Horizontal start position.
+ * @top: Vertical start position.
+ *
+ * Vertical start line:
+ * This field makes sense only when the resizer obtains its input
+ * from the preview engine/CCDC
+ *
+ * Horizontal start pixel:
+ * Pixels are coded on 16 bits for YUV and 8 bits for color separate data.
+ * When the resizer gets its input from SDRAM, this field must be set
+ * to <= 15 for YUV 16-bit data and <= 31 for 8-bit color separate data
+ */
+static void resizer_set_start(struct isp_res_device *res, u32 left, u32 top)
+{
+ struct isp_device *isp = to_isp_device(res);
+ u32 rgval = 0;
+
+ rgval = (left << ISPRSZ_IN_START_HORZ_ST_SHIFT)
+ & ISPRSZ_IN_START_HORZ_ST_MASK;
+ rgval |= (top << ISPRSZ_IN_START_VERT_ST_SHIFT)
+ & ISPRSZ_IN_START_VERT_ST_MASK;
+
+ isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_START);
+}
+
+/*
+ * resizer_set_input_size - Setup the input size
+ * @res: Device context.
+ * @width: The range is 0 to 4095 pixels
+ * @height: The range is 0 to 4095 lines
+ */
+static void resizer_set_input_size(struct isp_res_device *res,
+ u32 width, u32 height)
+{
+ struct isp_device *isp = to_isp_device(res);
+ u32 rgval = 0;
+
+ dev_dbg(isp->dev, "Input size[w/h]: %dx%d\n", width, height);
+
+ rgval = (width << ISPRSZ_IN_SIZE_HORZ_SHIFT)
+ & ISPRSZ_IN_SIZE_HORZ_MASK;
+ rgval |= (height << ISPRSZ_IN_SIZE_VERT_SHIFT)
+ & ISPRSZ_IN_SIZE_VERT_MASK;
+
+ isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_SIZE);
+}
+
+/*
+ * resizer_set_src_offs - Setup the memory offset for the input lines
+ * @res: Device context.
+ * @offset: Memory offset.
+ *
+ * The 5 LSBs are forced to be zeros by the hardware to align on a 32-byte
+ * boundary; the 5 LSBs are read-only. This field must be programmed to be
+ * 0x0 if the resizer input is from preview engine/CCDC.
+ */
+static void resizer_set_input_offset(struct isp_res_device *res, u32 offset)
+{
+ struct isp_device *isp = to_isp_device(res);
+
+ isp_reg_writel(isp, offset, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INOFF);
+}
+
+/*
+ * resizer_set_intype - Input type select
+ * @res: Device context.
+ * @type: Pixel format type.
+ */
+static void resizer_set_intype(struct isp_res_device *res,
+ enum resizer_colors_type type)
+{
+ struct isp_device *isp = to_isp_device(res);
+
+ if (type == RSZ_COLOR8)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
+ ISPRSZ_CNT_INPTYP);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
+ ISPRSZ_CNT_INPTYP);
+}
+
+/*
+ * __resizer_set_inaddr - Helper function for set input address
+ * @res : pointer to resizer private data structure
+ * @addr: input address
+ * return none
+ */
+static void __resizer_set_inaddr(struct isp_res_device *res, u32 addr)
+{
+ struct isp_device *isp = to_isp_device(res);
+
+ isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INADD);
+}
+
+/*
+ * The data rate at the horizontal resizer output must not exceed half the
+ * functional clock or 100 MP/s, whichever is lower. According to the TRM
+ * there's no similar requirement for the vertical resizer output. However
+ * experience showed that vertical upscaling by 4 leads to SBL overflows (with
+ * data rates at the resizer output exceeding 300 MP/s). Limiting the resizer
+ * output data rate to the functional clock or 200 MP/s, whichever is lower,
+ * seems to get rid of SBL overflows.
+ *
+ * The maximum data rate at the output of the horizontal resizer can thus be
+ * computed with
+ *
+ * max intermediate rate <= L3 clock * input height / output height
+ * max intermediate rate <= L3 clock / 2
+ *
+ * The maximum data rate at the resizer input is then
+ *
+ * max input rate <= max intermediate rate * input width / output width
+ *
+ * where the input width and height are the resizer input crop rectangle size.
+ * The TRM doesn't clearly explain if that's a maximum instant data rate or a
+ * maximum average data rate.
+ */
+void omap3isp_resizer_max_rate(struct isp_res_device *res,
+ unsigned int *max_rate)
+{
+ struct isp_pipeline *pipe = to_isp_pipeline(&res->subdev.entity);
+ const struct v4l2_mbus_framefmt *ofmt = &res->formats[RESZ_PAD_SOURCE];
+ unsigned long limit = min(pipe->l3_ick, 200000000UL);
+ unsigned long clock;
+
+ clock = div_u64((u64)limit * res->crop.active.height, ofmt->height);
+ clock = min(clock, limit / 2);
+ *max_rate = div_u64((u64)clock * res->crop.active.width, ofmt->width);
+}
+
+/*
+ * When the resizer processes images from memory, the driver must slow down read
+ * requests on the input to at least comply with the internal data rate
+ * requirements. If the application real-time requirements can cope with slower
+ * processing, the resizer can be slowed down even more to put less pressure on
+ * the overall system.
+ *
+ * When the resizer processes images on the fly (either from the CCDC or the
+ * preview module), the same data rate requirements apply but they can't be
+ * enforced at the resizer level. The image input module (sensor, CCP2 or
+ * preview module) must not provide image data faster than the resizer can
+ * process.
+ *
+ * For live image pipelines, the data rate is set by the frame format, size and
+ * rate. The sensor output frame rate must not exceed the maximum resizer data
+ * rate.
+ *
+ * The resizer slows down read requests by inserting wait cycles in the SBL
+ * requests. The maximum number of 256-byte requests per second can be computed
+ * as (the data rate is multiplied by 2 to convert from pixels per second to
+ * bytes per second)
+ *
+ * request per second = data rate * 2 / 256
+ * cycles per request = cycles per second / requests per second
+ *
+ * The number of cycles per second is controlled by the L3 clock, leading to
+ *
+ * cycles per request = L3 frequency / 2 * 256 / data rate
+ */
+static void resizer_adjust_bandwidth(struct isp_res_device *res)
+{
+ struct isp_pipeline *pipe = to_isp_pipeline(&res->subdev.entity);
+ struct isp_device *isp = to_isp_device(res);
+ unsigned long l3_ick = pipe->l3_ick;
+ struct v4l2_fract *timeperframe;
+ unsigned int cycles_per_frame;
+ unsigned int requests_per_frame;
+ unsigned int cycles_per_request;
+ unsigned int granularity;
+ unsigned int minimum;
+ unsigned int maximum;
+ unsigned int value;
+
+ if (res->input != RESIZER_INPUT_MEMORY) {
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP,
+ ISPSBL_SDR_REQ_RSZ_EXP_MASK);
+ return;
+ }
+
+ switch (isp->revision) {
+ case ISP_REVISION_1_0:
+ case ISP_REVISION_2_0:
+ default:
+ granularity = 1024;
+ break;
+
+ case ISP_REVISION_15_0:
+ granularity = 32;
+ break;
+ }
+
+ /* Compute the minimum number of cycles per request, based on the
+ * pipeline maximum data rate. This is an absolute lower bound if we
+ * don't want SBL overflows, so round the value up.
+ */
+ cycles_per_request = div_u64((u64)l3_ick / 2 * 256 + pipe->max_rate - 1,
+ pipe->max_rate);
+ minimum = DIV_ROUND_UP(cycles_per_request, granularity);
+
+ /* Compute the maximum number of cycles per request, based on the
+ * requested frame rate. This is a soft upper bound to achieve a frame
+ * rate equal or higher than the requested value, so round the value
+ * down.
+ */
+ timeperframe = &pipe->max_timeperframe;
+
+ requests_per_frame = DIV_ROUND_UP(res->crop.active.width * 2, 256)
+ * res->crop.active.height;
+ cycles_per_frame = div_u64((u64)l3_ick * timeperframe->numerator,
+ timeperframe->denominator);
+ cycles_per_request = cycles_per_frame / requests_per_frame;
+
+ maximum = cycles_per_request / granularity;
+
+ value = max(minimum, maximum);
+
+ dev_dbg(isp->dev, "%s: cycles per request = %u\n", __func__, value);
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP,
+ ISPSBL_SDR_REQ_RSZ_EXP_MASK,
+ value << ISPSBL_SDR_REQ_RSZ_EXP_SHIFT);
+}
+
+/*
+ * omap3isp_resizer_busy - Checks if ISP resizer is busy.
+ *
+ * Returns busy field from ISPRSZ_PCR register.
+ */
+int omap3isp_resizer_busy(struct isp_res_device *res)
+{
+ struct isp_device *isp = to_isp_device(res);
+
+ return isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR) &
+ ISPRSZ_PCR_BUSY;
+}
+
+/*
+ * resizer_set_inaddr - Sets the memory address of the input frame.
+ * @addr: 32bit memory address aligned on 32byte boundary.
+ */
+static void resizer_set_inaddr(struct isp_res_device *res, u32 addr)
+{
+ res->addr_base = addr;
+
+ /* This will handle crop settings in stream off state */
+ if (res->crop_offset)
+ addr += res->crop_offset & ~0x1f;
+
+ __resizer_set_inaddr(res, addr);
+}
+
+/*
+ * Configures the memory address to which the output frame is written.
+ * @addr: 32bit memory address aligned on 32byte boundary.
+ * Note: For SBL efficiency reasons the address should be on a 256-byte
+ * boundary.
+ */
+static void resizer_set_outaddr(struct isp_res_device *res, u32 addr)
+{
+ struct isp_device *isp = to_isp_device(res);
+
+ /*
+ * Set output address. This needs to be in its own function
+ * because it changes often.
+ */
+ isp_reg_writel(isp, addr << ISPRSZ_SDR_OUTADD_ADDR_SHIFT,
+ OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTADD);
+}
+
+/*
+ * resizer_print_status - Prints the values of the resizer module registers.
+ */
+#define RSZ_PRINT_REGISTER(isp, name)\
+ dev_dbg(isp->dev, "###RSZ " #name "=0x%08x\n", \
+ isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_##name))
+
+static void resizer_print_status(struct isp_res_device *res)
+{
+ struct isp_device *isp = to_isp_device(res);
+
+ dev_dbg(isp->dev, "-------------Resizer Register dump----------\n");
+
+ RSZ_PRINT_REGISTER(isp, PCR);
+ RSZ_PRINT_REGISTER(isp, CNT);
+ RSZ_PRINT_REGISTER(isp, OUT_SIZE);
+ RSZ_PRINT_REGISTER(isp, IN_START);
+ RSZ_PRINT_REGISTER(isp, IN_SIZE);
+ RSZ_PRINT_REGISTER(isp, SDR_INADD);
+ RSZ_PRINT_REGISTER(isp, SDR_INOFF);
+ RSZ_PRINT_REGISTER(isp, SDR_OUTADD);
+ RSZ_PRINT_REGISTER(isp, SDR_OUTOFF);
+ RSZ_PRINT_REGISTER(isp, YENH);
+
+ dev_dbg(isp->dev, "--------------------------------------------\n");
+}
+
+/*
+ * resizer_calc_ratios - Helper function for calculate resizer ratios
+ * @res: pointer to resizer private data structure
+ * @input: input frame size
+ * @output: output frame size
+ * @ratio : return calculated ratios
+ * return none
+ *
+ * The resizer uses a polyphase sample rate converter. The upsampling filter
+ * has a fixed number of phases that depend on the resizing ratio. As the ratio
+ * computation depends on the number of phases, we need to compute a first
+ * approximation and then refine it.
+ *
+ * The input/output/ratio relationship is given by the OMAP34xx TRM:
+ *
+ * - 8-phase, 4-tap mode (RSZ = 64 ~ 512)
+ * iw = (32 * sph + (ow - 1) * hrsz + 16) >> 8 + 7
+ * ih = (32 * spv + (oh - 1) * vrsz + 16) >> 8 + 4
+ * - 4-phase, 7-tap mode (RSZ = 513 ~ 1024)
+ * iw = (64 * sph + (ow - 1) * hrsz + 32) >> 8 + 7
+ * ih = (64 * spv + (oh - 1) * vrsz + 32) >> 8 + 7
+ *
+ * iw and ih are the input width and height after cropping. Those equations need
+ * to be satisfied exactly for the resizer to work correctly.
+ *
+ * The equations can't be easily reverted, as the >> 8 operation is not linear.
+ * In addition, not all input sizes can be achieved for a given output size. To
+ * get the highest input size lower than or equal to the requested input size,
+ * we need to compute the highest resizing ratio that satisfies the following
+ * inequality (taking the 4-tap mode width equation as an example)
+ *
+ * iw >= (32 * sph + (ow - 1) * hrsz + 16) >> 8 - 7
+ *
+ * (where iw is the requested input width) which can be rewritten as
+ *
+ * iw - 7 >= (32 * sph + (ow - 1) * hrsz + 16) >> 8
+ * (iw - 7) << 8 >= 32 * sph + (ow - 1) * hrsz + 16 - b
+ * ((iw - 7) << 8) + b >= 32 * sph + (ow - 1) * hrsz + 16
+ *
+ * where b is the value of the 8 least significant bits of the right hand side
+ * expression of the last inequality. The highest resizing ratio value will be
+ * achieved when b is equal to its maximum value of 255. That resizing ratio
+ * value will still satisfy the original inequality, as b will disappear when
+ * the expression will be shifted right by 8.
+ *
+ * The reverted the equations thus become
+ *
+ * - 8-phase, 4-tap mode
+ * hrsz = ((iw - 7) * 256 + 255 - 16 - 32 * sph) / (ow - 1)
+ * vrsz = ((ih - 4) * 256 + 255 - 16 - 32 * spv) / (oh - 1)
+ * - 4-phase, 7-tap mode
+ * hrsz = ((iw - 7) * 256 + 255 - 32 - 64 * sph) / (ow - 1)
+ * vrsz = ((ih - 7) * 256 + 255 - 32 - 64 * spv) / (oh - 1)
+ *
+ * The ratios are integer values, and are rounded down to ensure that the
+ * cropped input size is not bigger than the uncropped input size.
+ *
+ * As the number of phases/taps, used to select the correct equations to compute
+ * the ratio, depends on the ratio, we start with the 4-tap mode equations to
+ * compute an approximation of the ratio, and switch to the 7-tap mode equations
+ * if the approximation is higher than the ratio threshold.
+ *
+ * As the 7-tap mode equations will return a ratio smaller than or equal to the
+ * 4-tap mode equations, the resulting ratio could become lower than or equal to
+ * the ratio threshold. This 'equations loop' isn't an issue as long as the
+ * correct equations are used to compute the final input size. Starting with the
+ * 4-tap mode equations ensure that, in case of values resulting in a 'ratio
+ * loop', the smallest of the ratio values will be used, never exceeding the
+ * requested input size.
+ *
+ * We first clamp the output size according to the hardware capabilitie to avoid
+ * auto-cropping the input more than required to satisfy the TRM equations. The
+ * minimum output size is achieved with a scaling factor of 1024. It is thus
+ * computed using the 7-tap equations.
+ *
+ * min ow = ((iw - 7) * 256 - 32 - 64 * sph) / 1024 + 1
+ * min oh = ((ih - 7) * 256 - 32 - 64 * spv) / 1024 + 1
+ *
+ * Similarly, the maximum output size is achieved with a scaling factor of 64
+ * and computed using the 4-tap equations.
+ *
+ * max ow = ((iw - 7) * 256 + 255 - 16 - 32 * sph) / 64 + 1
+ * max oh = ((ih - 4) * 256 + 255 - 16 - 32 * spv) / 64 + 1
+ *
+ * The additional +255 term compensates for the round down operation performed
+ * by the TRM equations when shifting the value right by 8 bits.
+ *
+ * We then compute and clamp the ratios (x1/4 ~ x4). Clamping the output size to
+ * the maximum value guarantees that the ratio value will never be smaller than
+ * the minimum, but it could still slightly exceed the maximum. Clamping the
+ * ratio will thus result in a resizing factor slightly larger than the
+ * requested value.
+ *
+ * To accommodate that, and make sure the TRM equations are satisfied exactly, we
+ * compute the input crop rectangle as the last step.
+ *
+ * As if the situation wasn't complex enough, the maximum output width depends
+ * on the vertical resizing ratio. Fortunately, the output height doesn't
+ * depend on the horizontal resizing ratio. We can then start by computing the
+ * output height and the vertical ratio, and then move to computing the output
+ * width and the horizontal ratio.
+ */
+static void resizer_calc_ratios(struct isp_res_device *res,
+ struct v4l2_rect *input,
+ struct v4l2_mbus_framefmt *output,
+ struct resizer_ratio *ratio)
+{
+ struct isp_device *isp = to_isp_device(res);
+ const unsigned int spv = DEFAULT_PHASE;
+ const unsigned int sph = DEFAULT_PHASE;
+ unsigned int upscaled_width;
+ unsigned int upscaled_height;
+ unsigned int min_width;
+ unsigned int min_height;
+ unsigned int max_width;
+ unsigned int max_height;
+ unsigned int width_alignment;
+ unsigned int width;
+ unsigned int height;
+
+ /*
+ * Clamp the output height based on the hardware capabilities and
+ * compute the vertical resizing ratio.
+ */
+ min_height = ((input->height - 7) * 256 - 32 - 64 * spv) / 1024 + 1;
+ min_height = max_t(unsigned int, min_height, MIN_OUT_HEIGHT);
+ max_height = ((input->height - 4) * 256 + 255 - 16 - 32 * spv) / 64 + 1;
+ max_height = min_t(unsigned int, max_height, MAX_OUT_HEIGHT);
+ output->height = clamp(output->height, min_height, max_height);
+
+ ratio->vert = ((input->height - 4) * 256 + 255 - 16 - 32 * spv)
+ / (output->height - 1);
+ if (ratio->vert > MID_RESIZE_VALUE)
+ ratio->vert = ((input->height - 7) * 256 + 255 - 32 - 64 * spv)
+ / (output->height - 1);
+ ratio->vert = clamp_t(unsigned int, ratio->vert,
+ MIN_RESIZE_VALUE, MAX_RESIZE_VALUE);
+
+ if (ratio->vert <= MID_RESIZE_VALUE) {
+ upscaled_height = (output->height - 1) * ratio->vert
+ + 32 * spv + 16;
+ height = (upscaled_height >> 8) + 4;
+ } else {
+ upscaled_height = (output->height - 1) * ratio->vert
+ + 64 * spv + 32;
+ height = (upscaled_height >> 8) + 7;
+ }
+
+ /*
+ * Compute the minimum and maximum output widths based on the hardware
+ * capabilities. The maximum depends on the vertical resizing ratio.
+ */
+ min_width = ((input->width - 7) * 256 - 32 - 64 * sph) / 1024 + 1;
+ min_width = max_t(unsigned int, min_width, MIN_OUT_WIDTH);
+
+ if (ratio->vert <= MID_RESIZE_VALUE) {
+ switch (isp->revision) {
+ case ISP_REVISION_1_0:
+ max_width = MAX_4TAP_OUT_WIDTH_ES1;
+ break;
+
+ case ISP_REVISION_2_0:
+ default:
+ max_width = MAX_4TAP_OUT_WIDTH_ES2;
+ break;
+
+ case ISP_REVISION_15_0:
+ max_width = MAX_4TAP_OUT_WIDTH_3630;
+ break;
+ }
+ } else {
+ switch (isp->revision) {
+ case ISP_REVISION_1_0:
+ max_width = MAX_7TAP_OUT_WIDTH_ES1;
+ break;
+
+ case ISP_REVISION_2_0:
+ default:
+ max_width = MAX_7TAP_OUT_WIDTH_ES2;
+ break;
+
+ case ISP_REVISION_15_0:
+ max_width = MAX_7TAP_OUT_WIDTH_3630;
+ break;
+ }
+ }
+ max_width = min(((input->width - 7) * 256 + 255 - 16 - 32 * sph) / 64
+ + 1, max_width);
+
+ /*
+ * The output width must be even, and must be a multiple of 16 bytes
+ * when upscaling vertically. Clamp the output width to the valid range.
+ * Take the alignment into account (the maximum width in 7-tap mode on
+ * ES2 isn't a multiple of 8) and align the result up to make sure it
+ * won't be smaller than the minimum.
+ */
+ width_alignment = ratio->vert < 256 ? 8 : 2;
+ output->width = clamp(output->width, min_width,
+ max_width & ~(width_alignment - 1));
+ output->width = ALIGN(output->width, width_alignment);
+
+ ratio->horz = ((input->width - 7) * 256 + 255 - 16 - 32 * sph)
+ / (output->width - 1);
+ if (ratio->horz > MID_RESIZE_VALUE)
+ ratio->horz = ((input->width - 7) * 256 + 255 - 32 - 64 * sph)
+ / (output->width - 1);
+ ratio->horz = clamp_t(unsigned int, ratio->horz,
+ MIN_RESIZE_VALUE, MAX_RESIZE_VALUE);
+
+ if (ratio->horz <= MID_RESIZE_VALUE) {
+ upscaled_width = (output->width - 1) * ratio->horz
+ + 32 * sph + 16;
+ width = (upscaled_width >> 8) + 7;
+ } else {
+ upscaled_width = (output->width - 1) * ratio->horz
+ + 64 * sph + 32;
+ width = (upscaled_width >> 8) + 7;
+ }
+
+ /* Center the new crop rectangle. */
+ input->left += (input->width - width) / 2;
+ input->top += (input->height - height) / 2;
+ input->width = width;
+ input->height = height;
+}
+
+/*
+ * resizer_set_crop_params - Setup hardware with cropping parameters
+ * @res : resizer private structure
+ * @crop_rect : current crop rectangle
+ * @ratio : resizer ratios
+ * return none
+ */
+static void resizer_set_crop_params(struct isp_res_device *res,
+ const struct v4l2_mbus_framefmt *input,
+ const struct v4l2_mbus_framefmt *output)
+{
+ resizer_set_ratio(res, &res->ratio);
+
+ /* Set chrominance horizontal algorithm */
+ if (res->ratio.horz >= RESIZE_DIVISOR)
+ resizer_set_bilinear(res, RSZ_THE_SAME);
+ else
+ resizer_set_bilinear(res, RSZ_BILINEAR);
+
+ resizer_adjust_bandwidth(res);
+
+ if (res->input == RESIZER_INPUT_MEMORY) {
+ /* Calculate additional offset for crop */
+ res->crop_offset = (res->crop.active.top * input->width +
+ res->crop.active.left) * 2;
+ /*
+ * Write lowest 4 bits of horizontal pixel offset (in pixels),
+ * vertical start must be 0.
+ */
+ resizer_set_start(res, (res->crop_offset / 2) & 0xf, 0);
+
+ /*
+ * Set start (read) address for cropping, in bytes.
+ * Lowest 5 bits must be zero.
+ */
+ __resizer_set_inaddr(res,
+ res->addr_base + (res->crop_offset & ~0x1f));
+ } else {
+ /*
+ * Set vertical start line and horizontal starting pixel.
+ * If the input is from CCDC/PREV, horizontal start field is
+ * in bytes (twice number of pixels).
+ */
+ resizer_set_start(res, res->crop.active.left * 2,
+ res->crop.active.top);
+ /* Input address and offset must be 0 for preview/ccdc input */
+ __resizer_set_inaddr(res, 0);
+ resizer_set_input_offset(res, 0);
+ }
+
+ /* Set the input size */
+ resizer_set_input_size(res, res->crop.active.width,
+ res->crop.active.height);
+}
+
+static void resizer_configure(struct isp_res_device *res)
+{
+ struct v4l2_mbus_framefmt *informat, *outformat;
+ struct resizer_luma_yenh luma = {0, 0, 0, 0};
+
+ resizer_set_source(res, res->input);
+
+ informat = &res->formats[RESZ_PAD_SINK];
+ outformat = &res->formats[RESZ_PAD_SOURCE];
+
+ /* RESZ_PAD_SINK */
+ if (res->input == RESIZER_INPUT_VP)
+ resizer_set_input_offset(res, 0);
+ else
+ resizer_set_input_offset(res, ALIGN(informat->width, 0x10) * 2);
+
+ /* YUV422 interleaved, default phase, no luma enhancement */
+ resizer_set_intype(res, RSZ_YUV422);
+ resizer_set_ycpos(res, informat->code);
+ resizer_set_phase(res, DEFAULT_PHASE, DEFAULT_PHASE);
+ resizer_set_luma(res, &luma);
+
+ /* RESZ_PAD_SOURCE */
+ resizer_set_output_offset(res, ALIGN(outformat->width * 2, 32));
+ resizer_set_output_size(res, outformat->width, outformat->height);
+
+ resizer_set_crop_params(res, informat, outformat);
+}
+
+/* -----------------------------------------------------------------------------
+ * Interrupt handling
+ */
+
+static void resizer_enable_oneshot(struct isp_res_device *res)
+{
+ struct isp_device *isp = to_isp_device(res);
+
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR,
+ ISPRSZ_PCR_ENABLE | ISPRSZ_PCR_ONESHOT);
+}
+
+void omap3isp_resizer_isr_frame_sync(struct isp_res_device *res)
+{
+ /*
+ * If ISP_VIDEO_DMAQUEUE_QUEUED is set, DMA queue had an underrun
+ * condition, the module was paused and now we have a buffer queued
+ * on the output again. Restart the pipeline if running in continuous
+ * mode.
+ */
+ if (res->state == ISP_PIPELINE_STREAM_CONTINUOUS &&
+ res->video_out.dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) {
+ resizer_enable_oneshot(res);
+ isp_video_dmaqueue_flags_clr(&res->video_out);
+ }
+}
+
+static void resizer_isr_buffer(struct isp_res_device *res)
+{
+ struct isp_pipeline *pipe = to_isp_pipeline(&res->subdev.entity);
+ struct isp_buffer *buffer;
+ int restart = 0;
+
+ if (res->state == ISP_PIPELINE_STREAM_STOPPED)
+ return;
+
+ /* Complete the output buffer and, if reading from memory, the input
+ * buffer.
+ */
+ buffer = omap3isp_video_buffer_next(&res->video_out);
+ if (buffer != NULL) {
+ resizer_set_outaddr(res, buffer->isp_addr);
+ restart = 1;
+ }
+
+ pipe->state |= ISP_PIPELINE_IDLE_OUTPUT;
+
+ if (res->input == RESIZER_INPUT_MEMORY) {
+ buffer = omap3isp_video_buffer_next(&res->video_in);
+ if (buffer != NULL)
+ resizer_set_inaddr(res, buffer->isp_addr);
+ pipe->state |= ISP_PIPELINE_IDLE_INPUT;
+ }
+
+ if (res->state == ISP_PIPELINE_STREAM_SINGLESHOT) {
+ if (isp_pipeline_ready(pipe))
+ omap3isp_pipeline_set_stream(pipe,
+ ISP_PIPELINE_STREAM_SINGLESHOT);
+ } else {
+ /* If an underrun occurs, the video queue operation handler will
+ * restart the resizer. Otherwise restart it immediately.
+ */
+ if (restart)
+ resizer_enable_oneshot(res);
+ }
+}
+
+/*
+ * omap3isp_resizer_isr - ISP resizer interrupt handler
+ *
+ * Manage the resizer video buffers and configure shadowed and busy-locked
+ * registers.
+ */
+void omap3isp_resizer_isr(struct isp_res_device *res)
+{
+ struct v4l2_mbus_framefmt *informat, *outformat;
+
+ if (omap3isp_module_sync_is_stopping(&res->wait, &res->stopping))
+ return;
+
+ if (res->applycrop) {
+ outformat = __resizer_get_format(res, NULL, RESZ_PAD_SOURCE,
+ V4L2_SUBDEV_FORMAT_ACTIVE);
+ informat = __resizer_get_format(res, NULL, RESZ_PAD_SINK,
+ V4L2_SUBDEV_FORMAT_ACTIVE);
+ resizer_set_crop_params(res, informat, outformat);
+ res->applycrop = 0;
+ }
+
+ resizer_isr_buffer(res);
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP video operations
+ */
+
+static int resizer_video_queue(struct isp_video *video,
+ struct isp_buffer *buffer)
+{
+ struct isp_res_device *res = &video->isp->isp_res;
+
+ if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ resizer_set_inaddr(res, buffer->isp_addr);
+
+ /*
+ * We now have a buffer queued on the output. Despite what the
+ * TRM says, the resizer can't be restarted immediately.
+ * Enabling it in one shot mode in the middle of a frame (or at
+ * least asynchronously to the frame) results in the output
+ * being shifted randomly left/right and up/down, as if the
+ * hardware didn't synchronize itself to the beginning of the
+ * frame correctly.
+ *
+ * Restart the resizer on the next sync interrupt if running in
+ * continuous mode or when starting the stream.
+ */
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ resizer_set_outaddr(res, buffer->isp_addr);
+
+ return 0;
+}
+
+static const struct isp_video_operations resizer_video_ops = {
+ .queue = resizer_video_queue,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+/*
+ * resizer_set_stream - Enable/Disable streaming on resizer subdev
+ * @sd: ISP resizer V4L2 subdev
+ * @enable: 1 == Enable, 0 == Disable
+ *
+ * The resizer hardware can't be enabled without a memory buffer to write to.
+ * As the s_stream operation is called in response to a STREAMON call without
+ * any buffer queued yet, just update the state field and return immediately.
+ * The resizer will be enabled in resizer_video_queue().
+ */
+static int resizer_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct isp_res_device *res = v4l2_get_subdevdata(sd);
+ struct isp_video *video_out = &res->video_out;
+ struct isp_device *isp = to_isp_device(res);
+ struct device *dev = to_device(res);
+
+ if (res->state == ISP_PIPELINE_STREAM_STOPPED) {
+ if (enable == ISP_PIPELINE_STREAM_STOPPED)
+ return 0;
+
+ omap3isp_subclk_enable(isp, OMAP3_ISP_SUBCLK_RESIZER);
+ resizer_configure(res);
+ resizer_print_status(res);
+ }
+
+ switch (enable) {
+ case ISP_PIPELINE_STREAM_CONTINUOUS:
+ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_WRITE);
+ if (video_out->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) {
+ resizer_enable_oneshot(res);
+ isp_video_dmaqueue_flags_clr(video_out);
+ }
+ break;
+
+ case ISP_PIPELINE_STREAM_SINGLESHOT:
+ if (res->input == RESIZER_INPUT_MEMORY)
+ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_READ);
+ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_WRITE);
+
+ resizer_enable_oneshot(res);
+ break;
+
+ case ISP_PIPELINE_STREAM_STOPPED:
+ if (omap3isp_module_sync_idle(&sd->entity, &res->wait,
+ &res->stopping))
+ dev_dbg(dev, "%s: module stop timeout.\n", sd->name);
+ omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_RESIZER_READ |
+ OMAP3_ISP_SBL_RESIZER_WRITE);
+ omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_RESIZER);
+ isp_video_dmaqueue_flags_clr(video_out);
+ break;
+ }
+
+ res->state = enable;
+ return 0;
+}
+
+/*
+ * resizer_g_crop - handle get crop subdev operation
+ * @sd : pointer to v4l2 subdev structure
+ * @pad : subdev pad
+ * @crop : pointer to crop structure
+ * @which : active or try format
+ * return zero
+ */
+static int resizer_g_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct isp_res_device *res = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+ struct resizer_ratio ratio;
+
+ /* Only sink pad has crop capability */
+ if (crop->pad != RESZ_PAD_SINK)
+ return -EINVAL;
+
+ format = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, crop->which);
+ crop->rect = *__resizer_get_crop(res, fh, crop->which);
+ resizer_calc_ratios(res, &crop->rect, format, &ratio);
+
+ return 0;
+}
+
+/*
+ * resizer_try_crop - mangles crop parameters.
+ */
+static void resizer_try_crop(const struct v4l2_mbus_framefmt *sink,
+ const struct v4l2_mbus_framefmt *source,
+ struct v4l2_rect *crop)
+{
+ const unsigned int spv = DEFAULT_PHASE;
+ const unsigned int sph = DEFAULT_PHASE;
+
+ /* Crop rectangle is constrained to the output size so that zoom ratio
+ * cannot exceed +/-4.0.
+ */
+ unsigned int min_width =
+ ((32 * sph + (source->width - 1) * 64 + 16) >> 8) + 7;
+ unsigned int min_height =
+ ((32 * spv + (source->height - 1) * 64 + 16) >> 8) + 4;
+ unsigned int max_width =
+ ((64 * sph + (source->width - 1) * 1024 + 32) >> 8) + 7;
+ unsigned int max_height =
+ ((64 * spv + (source->height - 1) * 1024 + 32) >> 8) + 7;
+
+ crop->width = clamp_t(u32, crop->width, min_width, max_width);
+ crop->height = clamp_t(u32, crop->height, min_height, max_height);
+
+ /* Crop can not go beyond of the input rectangle */
+ crop->left = clamp_t(u32, crop->left, 0, sink->width - MIN_IN_WIDTH);
+ crop->width = clamp_t(u32, crop->width, MIN_IN_WIDTH,
+ sink->width - crop->left);
+ crop->top = clamp_t(u32, crop->top, 0, sink->height - MIN_IN_HEIGHT);
+ crop->height = clamp_t(u32, crop->height, MIN_IN_HEIGHT,
+ sink->height - crop->top);
+}
+
+/*
+ * resizer_s_crop - handle set crop subdev operation
+ * @sd : pointer to v4l2 subdev structure
+ * @pad : subdev pad
+ * @crop : pointer to crop structure
+ * @which : active or try format
+ * return -EINVAL or zero when succeed
+ */
+static int resizer_s_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct isp_res_device *res = v4l2_get_subdevdata(sd);
+ struct isp_device *isp = to_isp_device(res);
+ struct v4l2_mbus_framefmt *format_sink, *format_source;
+ struct resizer_ratio ratio;
+
+ /* Only sink pad has crop capability */
+ if (crop->pad != RESZ_PAD_SINK)
+ return -EINVAL;
+
+ format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK,
+ crop->which);
+ format_source = __resizer_get_format(res, fh, RESZ_PAD_SOURCE,
+ crop->which);
+
+ dev_dbg(isp->dev, "%s: L=%d,T=%d,W=%d,H=%d,which=%d\n", __func__,
+ crop->rect.left, crop->rect.top, crop->rect.width,
+ crop->rect.height, crop->which);
+
+ dev_dbg(isp->dev, "%s: input=%dx%d, output=%dx%d\n", __func__,
+ format_sink->width, format_sink->height,
+ format_source->width, format_source->height);
+
+ resizer_try_crop(format_sink, format_source, &crop->rect);
+ *__resizer_get_crop(res, fh, crop->which) = crop->rect;
+ resizer_calc_ratios(res, &crop->rect, format_source, &ratio);
+
+ if (crop->which == V4L2_SUBDEV_FORMAT_TRY)
+ return 0;
+
+ res->ratio = ratio;
+ res->crop.active = crop->rect;
+
+ /*
+ * s_crop can be called while streaming is on. In this case
+ * the crop values will be set in the next IRQ.
+ */
+ if (res->state != ISP_PIPELINE_STREAM_STOPPED)
+ res->applycrop = 1;
+
+ return 0;
+}
+
+/* resizer pixel formats */
+static const unsigned int resizer_formats[] = {
+ V4L2_MBUS_FMT_UYVY8_1X16,
+ V4L2_MBUS_FMT_YUYV8_1X16,
+};
+
+static unsigned int resizer_max_in_width(struct isp_res_device *res)
+{
+ struct isp_device *isp = to_isp_device(res);
+
+ if (res->input == RESIZER_INPUT_MEMORY) {
+ return MAX_IN_WIDTH_MEMORY_MODE;
+ } else {
+ if (isp->revision == ISP_REVISION_1_0)
+ return MAX_IN_WIDTH_ONTHEFLY_MODE_ES1;
+ else
+ return MAX_IN_WIDTH_ONTHEFLY_MODE_ES2;
+ }
+}
+
+/*
+ * resizer_try_format - Handle try format by pad subdev method
+ * @res : ISP resizer device
+ * @fh : V4L2 subdev file handle
+ * @pad : pad num
+ * @fmt : pointer to v4l2 format structure
+ * @which : wanted subdev format
+ */
+static void resizer_try_format(struct isp_res_device *res,
+ struct v4l2_subdev_fh *fh, unsigned int pad,
+ struct v4l2_mbus_framefmt *fmt,
+ enum v4l2_subdev_format_whence which)
+{
+ struct v4l2_mbus_framefmt *format;
+ struct resizer_ratio ratio;
+ struct v4l2_rect crop;
+
+ switch (pad) {
+ case RESZ_PAD_SINK:
+ if (fmt->code != V4L2_MBUS_FMT_YUYV8_1X16 &&
+ fmt->code != V4L2_MBUS_FMT_UYVY8_1X16)
+ fmt->code = V4L2_MBUS_FMT_YUYV8_1X16;
+
+ fmt->width = clamp_t(u32, fmt->width, MIN_IN_WIDTH,
+ resizer_max_in_width(res));
+ fmt->height = clamp_t(u32, fmt->height, MIN_IN_HEIGHT,
+ MAX_IN_HEIGHT);
+ break;
+
+ case RESZ_PAD_SOURCE:
+ format = __resizer_get_format(res, fh, RESZ_PAD_SINK, which);
+ fmt->code = format->code;
+
+ crop = *__resizer_get_crop(res, fh, which);
+ resizer_calc_ratios(res, &crop, fmt, &ratio);
+ break;
+ }
+
+ fmt->colorspace = V4L2_COLORSPACE_JPEG;
+ fmt->field = V4L2_FIELD_NONE;
+}
+
+/*
+ * resizer_enum_mbus_code - Handle pixel format enumeration
+ * @sd : pointer to v4l2 subdev structure
+ * @fh : V4L2 subdev file handle
+ * @code : pointer to v4l2_subdev_mbus_code_enum structure
+ * return -EINVAL or zero on success
+ */
+static int resizer_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct isp_res_device *res = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ if (code->pad == RESZ_PAD_SINK) {
+ if (code->index >= ARRAY_SIZE(resizer_formats))
+ return -EINVAL;
+
+ code->code = resizer_formats[code->index];
+ } else {
+ if (code->index != 0)
+ return -EINVAL;
+
+ format = __resizer_get_format(res, fh, RESZ_PAD_SINK,
+ V4L2_SUBDEV_FORMAT_TRY);
+ code->code = format->code;
+ }
+
+ return 0;
+}
+
+static int resizer_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct isp_res_device *res = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt format;
+
+ if (fse->index != 0)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = 1;
+ format.height = 1;
+ resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+ fse->min_width = format.width;
+ fse->min_height = format.height;
+
+ if (format.code != fse->code)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = -1;
+ format.height = -1;
+ resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+ fse->max_width = format.width;
+ fse->max_height = format.height;
+
+ return 0;
+}
+
+/*
+ * resizer_get_format - Handle get format by pads subdev method
+ * @sd : pointer to v4l2 subdev structure
+ * @fh : V4L2 subdev file handle
+ * @fmt : pointer to v4l2 subdev format structure
+ * return -EINVAL or zero on success
+ */
+static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct isp_res_device *res = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __resizer_get_format(res, fh, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ fmt->format = *format;
+ return 0;
+}
+
+/*
+ * resizer_set_format - Handle set format by pads subdev method
+ * @sd : pointer to v4l2 subdev structure
+ * @fh : V4L2 subdev file handle
+ * @fmt : pointer to v4l2 subdev format structure
+ * return -EINVAL or zero on success
+ */
+static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct isp_res_device *res = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *crop;
+
+ format = __resizer_get_format(res, fh, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ resizer_try_format(res, fh, fmt->pad, &fmt->format, fmt->which);
+ *format = fmt->format;
+
+ if (fmt->pad == RESZ_PAD_SINK) {
+ /* reset crop rectangle */
+ crop = __resizer_get_crop(res, fh, fmt->which);
+ crop->left = 0;
+ crop->top = 0;
+ crop->width = fmt->format.width;
+ crop->height = fmt->format.height;
+
+ /* Propagate the format from sink to source */
+ format = __resizer_get_format(res, fh, RESZ_PAD_SOURCE,
+ fmt->which);
+ *format = fmt->format;
+ resizer_try_format(res, fh, RESZ_PAD_SOURCE, format,
+ fmt->which);
+ }
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ /* Compute and store the active crop rectangle and resizer
+ * ratios. format already points to the source pad active
+ * format.
+ */
+ res->crop.active = res->crop.request;
+ resizer_calc_ratios(res, &res->crop.active, format,
+ &res->ratio);
+ }
+
+ return 0;
+}
+
+/*
+ * resizer_init_formats - Initialize formats on all pads
+ * @sd: ISP resizer V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ *
+ * Initialize all pad formats with default values. If fh is not NULL, try
+ * formats are initialized on the file handle. Otherwise active formats are
+ * initialized on the device.
+ */
+static int resizer_init_formats(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_subdev_format format;
+
+ memset(&format, 0, sizeof(format));
+ format.pad = RESZ_PAD_SINK;
+ format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
+ format.format.code = V4L2_MBUS_FMT_YUYV8_1X16;
+ format.format.width = 4096;
+ format.format.height = 4096;
+ resizer_set_format(sd, fh, &format);
+
+ return 0;
+}
+
+/* subdev video operations */
+static const struct v4l2_subdev_video_ops resizer_v4l2_video_ops = {
+ .s_stream = resizer_set_stream,
+};
+
+/* subdev pad operations */
+static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = {
+ .enum_mbus_code = resizer_enum_mbus_code,
+ .enum_frame_size = resizer_enum_frame_size,
+ .get_fmt = resizer_get_format,
+ .set_fmt = resizer_set_format,
+ .get_crop = resizer_g_crop,
+ .set_crop = resizer_s_crop,
+};
+
+/* subdev operations */
+static const struct v4l2_subdev_ops resizer_v4l2_ops = {
+ .video = &resizer_v4l2_video_ops,
+ .pad = &resizer_v4l2_pad_ops,
+};
+
+/* subdev internal operations */
+static const struct v4l2_subdev_internal_ops resizer_v4l2_internal_ops = {
+ .open = resizer_init_formats,
+};
+
+/* -----------------------------------------------------------------------------
+ * Media entity operations
+ */
+
+/*
+ * resizer_link_setup - Setup resizer connections.
+ * @entity : Pointer to media entity structure
+ * @local : Pointer to local pad array
+ * @remote : Pointer to remote pad array
+ * @flags : Link flags
+ * return -EINVAL or zero on success
+ */
+static int resizer_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct isp_res_device *res = v4l2_get_subdevdata(sd);
+
+ switch (local->index | media_entity_type(remote->entity)) {
+ case RESZ_PAD_SINK | MEDIA_ENT_T_DEVNODE:
+ /* read from memory */
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (res->input == RESIZER_INPUT_VP)
+ return -EBUSY;
+ res->input = RESIZER_INPUT_MEMORY;
+ } else {
+ if (res->input == RESIZER_INPUT_MEMORY)
+ res->input = RESIZER_INPUT_NONE;
+ }
+ break;
+
+ case RESZ_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ /* read from ccdc or previewer */
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (res->input == RESIZER_INPUT_MEMORY)
+ return -EBUSY;
+ res->input = RESIZER_INPUT_VP;
+ } else {
+ if (res->input == RESIZER_INPUT_VP)
+ res->input = RESIZER_INPUT_NONE;
+ }
+ break;
+
+ case RESZ_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
+ /* resizer always write to memory */
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* media operations */
+static const struct media_entity_operations resizer_media_ops = {
+ .link_setup = resizer_link_setup,
+};
+
+void omap3isp_resizer_unregister_entities(struct isp_res_device *res)
+{
+ v4l2_device_unregister_subdev(&res->subdev);
+ omap3isp_video_unregister(&res->video_in);
+ omap3isp_video_unregister(&res->video_out);
+}
+
+int omap3isp_resizer_register_entities(struct isp_res_device *res,
+ struct v4l2_device *vdev)
+{
+ int ret;
+
+ /* Register the subdev and video nodes. */
+ ret = v4l2_device_register_subdev(vdev, &res->subdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap3isp_video_register(&res->video_in, vdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap3isp_video_register(&res->video_out, vdev);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ omap3isp_resizer_unregister_entities(res);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP resizer initialization and cleanup
+ */
+
+/*
+ * resizer_init_entities - Initialize resizer subdev and media entity.
+ * @res : Pointer to resizer device structure
+ * return -ENOMEM or zero on success
+ */
+static int resizer_init_entities(struct isp_res_device *res)
+{
+ struct v4l2_subdev *sd = &res->subdev;
+ struct media_pad *pads = res->pads;
+ struct media_entity *me = &sd->entity;
+ int ret;
+
+ res->input = RESIZER_INPUT_NONE;
+
+ v4l2_subdev_init(sd, &resizer_v4l2_ops);
+ sd->internal_ops = &resizer_v4l2_internal_ops;
+ strlcpy(sd->name, "OMAP3 ISP resizer", sizeof(sd->name));
+ sd->grp_id = 1 << 16; /* group ID for isp subdevs */
+ v4l2_set_subdevdata(sd, res);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ pads[RESZ_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[RESZ_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+
+ me->ops = &resizer_media_ops;
+ ret = media_entity_init(me, RESZ_PADS_NUM, pads, 0);
+ if (ret < 0)
+ return ret;
+
+ resizer_init_formats(sd, NULL);
+
+ res->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ res->video_in.ops = &resizer_video_ops;
+ res->video_in.isp = to_isp_device(res);
+ res->video_in.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3;
+ res->video_in.bpl_alignment = 32;
+ res->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ res->video_out.ops = &resizer_video_ops;
+ res->video_out.isp = to_isp_device(res);
+ res->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3;
+ res->video_out.bpl_alignment = 32;
+
+ ret = omap3isp_video_init(&res->video_in, "resizer");
+ if (ret < 0)
+ goto error_video_in;
+
+ ret = omap3isp_video_init(&res->video_out, "resizer");
+ if (ret < 0)
+ goto error_video_out;
+
+ /* Connect the video nodes to the resizer subdev. */
+ ret = media_entity_create_link(&res->video_in.video.entity, 0,
+ &res->subdev.entity, RESZ_PAD_SINK, 0);
+ if (ret < 0)
+ goto error_link;
+
+ ret = media_entity_create_link(&res->subdev.entity, RESZ_PAD_SOURCE,
+ &res->video_out.video.entity, 0, 0);
+ if (ret < 0)
+ goto error_link;
+
+ return 0;
+
+error_link:
+ omap3isp_video_cleanup(&res->video_out);
+error_video_out:
+ omap3isp_video_cleanup(&res->video_in);
+error_video_in:
+ media_entity_cleanup(&res->subdev.entity);
+ return ret;
+}
+
+/*
+ * isp_resizer_init - Resizer initialization.
+ * @isp : Pointer to ISP device
+ * return -ENOMEM or zero on success
+ */
+int omap3isp_resizer_init(struct isp_device *isp)
+{
+ struct isp_res_device *res = &isp->isp_res;
+
+ init_waitqueue_head(&res->wait);
+ atomic_set(&res->stopping, 0);
+ return resizer_init_entities(res);
+}
+
+void omap3isp_resizer_cleanup(struct isp_device *isp)
+{
+ struct isp_res_device *res = &isp->isp_res;
+
+ omap3isp_video_cleanup(&res->video_in);
+ omap3isp_video_cleanup(&res->video_out);
+ media_entity_cleanup(&res->subdev.entity);
+}
diff --git a/drivers/media/video/omap3isp/ispresizer.h b/drivers/media/video/omap3isp/ispresizer.h
new file mode 100644
index 00000000..70c1c0e1
--- /dev/null
+++ b/drivers/media/video/omap3isp/ispresizer.h
@@ -0,0 +1,146 @@
+/*
+ * ispresizer.h
+ *
+ * TI OMAP3 ISP - Resizer module
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP3_ISP_RESIZER_H
+#define OMAP3_ISP_RESIZER_H
+
+#include <linux/types.h>
+
+/*
+ * Constants for filter coefficents count
+ */
+#define COEFF_CNT 32
+
+/*
+ * struct isprsz_coef - Structure for resizer filter coeffcients.
+ * @h_filter_coef_4tap: Horizontal filter coefficients for 8-phase/4-tap
+ * mode (.5x-4x)
+ * @v_filter_coef_4tap: Vertical filter coefficients for 8-phase/4-tap
+ * mode (.5x-4x)
+ * @h_filter_coef_7tap: Horizontal filter coefficients for 4-phase/7-tap
+ * mode (.25x-.5x)
+ * @v_filter_coef_7tap: Vertical filter coefficients for 4-phase/7-tap
+ * mode (.25x-.5x)
+ */
+struct isprsz_coef {
+ u16 h_filter_coef_4tap[32];
+ u16 v_filter_coef_4tap[32];
+ /* Every 8th value is a dummy value in the following arrays: */
+ u16 h_filter_coef_7tap[32];
+ u16 v_filter_coef_7tap[32];
+};
+
+/* Chrominance horizontal algorithm */
+enum resizer_chroma_algo {
+ RSZ_THE_SAME = 0, /* Chrominance the same as Luminance */
+ RSZ_BILINEAR = 1, /* Chrominance uses bilinear interpolation */
+};
+
+/* Resizer input type select */
+enum resizer_colors_type {
+ RSZ_YUV422 = 0, /* YUV422 color is interleaved */
+ RSZ_COLOR8 = 1, /* Color separate data on 8 bits */
+};
+
+/*
+ * Structure for horizontal and vertical resizing value
+ */
+struct resizer_ratio {
+ u32 horz;
+ u32 vert;
+};
+
+/*
+ * Structure for luminance enhancer parameters.
+ */
+struct resizer_luma_yenh {
+ u8 algo; /* algorithm select. */
+ u8 gain; /* maximum gain. */
+ u8 slope; /* slope. */
+ u8 core; /* core offset. */
+};
+
+enum resizer_input_entity {
+ RESIZER_INPUT_NONE,
+ RESIZER_INPUT_VP, /* input video port - prev or ccdc */
+ RESIZER_INPUT_MEMORY,
+};
+
+/* Sink and source resizer pads */
+#define RESZ_PAD_SINK 0
+#define RESZ_PAD_SOURCE 1
+#define RESZ_PADS_NUM 2
+
+/*
+ * struct isp_res_device - OMAP3 ISP resizer module
+ * @crop.request: Crop rectangle requested by the user
+ * @crop.active: Active crop rectangle (based on hardware requirements)
+ */
+struct isp_res_device {
+ struct v4l2_subdev subdev;
+ struct media_pad pads[RESZ_PADS_NUM];
+ struct v4l2_mbus_framefmt formats[RESZ_PADS_NUM];
+
+ enum resizer_input_entity input;
+ struct isp_video video_in;
+ struct isp_video video_out;
+
+ u32 addr_base; /* stored source buffer address in memory mode */
+ u32 crop_offset; /* additional offset for crop in memory mode */
+ struct resizer_ratio ratio;
+ int pm_state;
+ unsigned int applycrop:1;
+ enum isp_pipeline_stream_state state;
+ wait_queue_head_t wait;
+ atomic_t stopping;
+
+ struct {
+ struct v4l2_rect request;
+ struct v4l2_rect active;
+ } crop;
+};
+
+struct isp_device;
+
+int omap3isp_resizer_init(struct isp_device *isp);
+void omap3isp_resizer_cleanup(struct isp_device *isp);
+
+int omap3isp_resizer_register_entities(struct isp_res_device *res,
+ struct v4l2_device *vdev);
+void omap3isp_resizer_unregister_entities(struct isp_res_device *res);
+void omap3isp_resizer_isr_frame_sync(struct isp_res_device *res);
+void omap3isp_resizer_isr(struct isp_res_device *isp_res);
+
+void omap3isp_resizer_max_rate(struct isp_res_device *res,
+ unsigned int *max_rate);
+
+void omap3isp_resizer_suspend(struct isp_res_device *isp_res);
+
+void omap3isp_resizer_resume(struct isp_res_device *isp_res);
+
+int omap3isp_resizer_busy(struct isp_res_device *isp_res);
+
+#endif /* OMAP3_ISP_RESIZER_H */
diff --git a/drivers/media/video/omap3isp/ispstat.c b/drivers/media/video/omap3isp/ispstat.c
new file mode 100644
index 00000000..11871ecc
--- /dev/null
+++ b/drivers/media/video/omap3isp/ispstat.c
@@ -0,0 +1,1102 @@
+/*
+ * ispstat.c
+ *
+ * TI OMAP3 ISP - Statistics core
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc
+ *
+ * Contacts: David Cohen <dacohen@gmail.com>
+ * Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include "isp.h"
+
+#define IS_COHERENT_BUF(stat) ((stat)->dma_ch >= 0)
+
+/*
+ * MAGIC_SIZE must always be the greatest common divisor of
+ * AEWB_PACKET_SIZE and AF_PAXEL_SIZE.
+ */
+#define MAGIC_SIZE 16
+#define MAGIC_NUM 0x55
+
+/* HACK: AF module seems to be writing one more paxel data than it should. */
+#define AF_EXTRA_DATA OMAP3ISP_AF_PAXEL_SIZE
+
+/*
+ * HACK: H3A modules go to an invalid state after have a SBL overflow. It makes
+ * the next buffer to start to be written in the same point where the overflow
+ * occurred instead of the configured address. The only known way to make it to
+ * go back to a valid state is having a valid buffer processing. Of course it
+ * requires at least a doubled buffer size to avoid an access to invalid memory
+ * region. But it does not fix everything. It may happen more than one
+ * consecutive SBL overflows. In that case, it might be unpredictable how many
+ * buffers the allocated memory should fit. For that case, a recover
+ * configuration was created. It produces the minimum buffer size for each H3A
+ * module and decrease the change for more SBL overflows. This recover state
+ * will be enabled every time a SBL overflow occur. As the output buffer size
+ * isn't big, it's possible to have an extra size able to fit many recover
+ * buffers making it extreamily unlikely to have an access to invalid memory
+ * region.
+ */
+#define NUM_H3A_RECOVER_BUFS 10
+
+/*
+ * HACK: Because of HW issues the generic layer sometimes need to have
+ * different behaviour for different statistic modules.
+ */
+#define IS_H3A_AF(stat) ((stat) == &(stat)->isp->isp_af)
+#define IS_H3A_AEWB(stat) ((stat) == &(stat)->isp->isp_aewb)
+#define IS_H3A(stat) (IS_H3A_AF(stat) || IS_H3A_AEWB(stat))
+
+static void __isp_stat_buf_sync_magic(struct ispstat *stat,
+ struct ispstat_buffer *buf,
+ u32 buf_size, enum dma_data_direction dir,
+ void (*dma_sync)(struct device *,
+ dma_addr_t, unsigned long, size_t,
+ enum dma_data_direction))
+{
+ struct device *dev = stat->isp->dev;
+ struct page *pg;
+ dma_addr_t dma_addr;
+ u32 offset;
+
+ /* Initial magic words */
+ pg = vmalloc_to_page(buf->virt_addr);
+ dma_addr = pfn_to_dma(dev, page_to_pfn(pg));
+ dma_sync(dev, dma_addr, 0, MAGIC_SIZE, dir);
+
+ /* Final magic words */
+ pg = vmalloc_to_page(buf->virt_addr + buf_size);
+ dma_addr = pfn_to_dma(dev, page_to_pfn(pg));
+ offset = ((u32)buf->virt_addr + buf_size) & ~PAGE_MASK;
+ dma_sync(dev, dma_addr, offset, MAGIC_SIZE, dir);
+}
+
+static void isp_stat_buf_sync_magic_for_device(struct ispstat *stat,
+ struct ispstat_buffer *buf,
+ u32 buf_size,
+ enum dma_data_direction dir)
+{
+ if (IS_COHERENT_BUF(stat))
+ return;
+
+ __isp_stat_buf_sync_magic(stat, buf, buf_size, dir,
+ dma_sync_single_range_for_device);
+}
+
+static void isp_stat_buf_sync_magic_for_cpu(struct ispstat *stat,
+ struct ispstat_buffer *buf,
+ u32 buf_size,
+ enum dma_data_direction dir)
+{
+ if (IS_COHERENT_BUF(stat))
+ return;
+
+ __isp_stat_buf_sync_magic(stat, buf, buf_size, dir,
+ dma_sync_single_range_for_cpu);
+}
+
+static int isp_stat_buf_check_magic(struct ispstat *stat,
+ struct ispstat_buffer *buf)
+{
+ const u32 buf_size = IS_H3A_AF(stat) ?
+ buf->buf_size + AF_EXTRA_DATA : buf->buf_size;
+ u8 *w;
+ u8 *end;
+ int ret = -EINVAL;
+
+ isp_stat_buf_sync_magic_for_cpu(stat, buf, buf_size, DMA_FROM_DEVICE);
+
+ /* Checking initial magic numbers. They shouldn't be here anymore. */
+ for (w = buf->virt_addr, end = w + MAGIC_SIZE; w < end; w++)
+ if (likely(*w != MAGIC_NUM))
+ ret = 0;
+
+ if (ret) {
+ dev_dbg(stat->isp->dev, "%s: beginning magic check does not "
+ "match.\n", stat->subdev.name);
+ return ret;
+ }
+
+ /* Checking magic numbers at the end. They must be still here. */
+ for (w = buf->virt_addr + buf_size, end = w + MAGIC_SIZE;
+ w < end; w++) {
+ if (unlikely(*w != MAGIC_NUM)) {
+ dev_dbg(stat->isp->dev, "%s: endding magic check does "
+ "not match.\n", stat->subdev.name);
+ return -EINVAL;
+ }
+ }
+
+ isp_stat_buf_sync_magic_for_device(stat, buf, buf_size,
+ DMA_FROM_DEVICE);
+
+ return 0;
+}
+
+static void isp_stat_buf_insert_magic(struct ispstat *stat,
+ struct ispstat_buffer *buf)
+{
+ const u32 buf_size = IS_H3A_AF(stat) ?
+ stat->buf_size + AF_EXTRA_DATA : stat->buf_size;
+
+ isp_stat_buf_sync_magic_for_cpu(stat, buf, buf_size, DMA_FROM_DEVICE);
+
+ /*
+ * Inserting MAGIC_NUM at the beginning and end of the buffer.
+ * buf->buf_size is set only after the buffer is queued. For now the
+ * right buf_size for the current configuration is pointed by
+ * stat->buf_size.
+ */
+ memset(buf->virt_addr, MAGIC_NUM, MAGIC_SIZE);
+ memset(buf->virt_addr + buf_size, MAGIC_NUM, MAGIC_SIZE);
+
+ isp_stat_buf_sync_magic_for_device(stat, buf, buf_size,
+ DMA_BIDIRECTIONAL);
+}
+
+static void isp_stat_buf_sync_for_device(struct ispstat *stat,
+ struct ispstat_buffer *buf)
+{
+ if (IS_COHERENT_BUF(stat))
+ return;
+
+ dma_sync_sg_for_device(stat->isp->dev, buf->iovm->sgt->sgl,
+ buf->iovm->sgt->nents, DMA_FROM_DEVICE);
+}
+
+static void isp_stat_buf_sync_for_cpu(struct ispstat *stat,
+ struct ispstat_buffer *buf)
+{
+ if (IS_COHERENT_BUF(stat))
+ return;
+
+ dma_sync_sg_for_cpu(stat->isp->dev, buf->iovm->sgt->sgl,
+ buf->iovm->sgt->nents, DMA_FROM_DEVICE);
+}
+
+static void isp_stat_buf_clear(struct ispstat *stat)
+{
+ int i;
+
+ for (i = 0; i < STAT_MAX_BUFS; i++)
+ stat->buf[i].empty = 1;
+}
+
+static struct ispstat_buffer *
+__isp_stat_buf_find(struct ispstat *stat, int look_empty)
+{
+ struct ispstat_buffer *found = NULL;
+ int i;
+
+ for (i = 0; i < STAT_MAX_BUFS; i++) {
+ struct ispstat_buffer *curr = &stat->buf[i];
+
+ /*
+ * Don't select the buffer which is being copied to
+ * userspace or used by the module.
+ */
+ if (curr == stat->locked_buf || curr == stat->active_buf)
+ continue;
+
+ /* Don't select uninitialised buffers if it's not required */
+ if (!look_empty && curr->empty)
+ continue;
+
+ /* Pick uninitialised buffer over anything else if look_empty */
+ if (curr->empty) {
+ found = curr;
+ break;
+ }
+
+ /* Choose the oldest buffer */
+ if (!found ||
+ (s32)curr->frame_number - (s32)found->frame_number < 0)
+ found = curr;
+ }
+
+ return found;
+}
+
+static inline struct ispstat_buffer *
+isp_stat_buf_find_oldest(struct ispstat *stat)
+{
+ return __isp_stat_buf_find(stat, 0);
+}
+
+static inline struct ispstat_buffer *
+isp_stat_buf_find_oldest_or_empty(struct ispstat *stat)
+{
+ return __isp_stat_buf_find(stat, 1);
+}
+
+static int isp_stat_buf_queue(struct ispstat *stat)
+{
+ if (!stat->active_buf)
+ return STAT_NO_BUF;
+
+ do_gettimeofday(&stat->active_buf->ts);
+
+ stat->active_buf->buf_size = stat->buf_size;
+ if (isp_stat_buf_check_magic(stat, stat->active_buf)) {
+ dev_dbg(stat->isp->dev, "%s: data wasn't properly written.\n",
+ stat->subdev.name);
+ return STAT_NO_BUF;
+ }
+ stat->active_buf->config_counter = stat->config_counter;
+ stat->active_buf->frame_number = stat->frame_number;
+ stat->active_buf->empty = 0;
+ stat->active_buf = NULL;
+
+ return STAT_BUF_DONE;
+}
+
+/* Get next free buffer to write the statistics to and mark it active. */
+static void isp_stat_buf_next(struct ispstat *stat)
+{
+ if (unlikely(stat->active_buf))
+ /* Overwriting unused active buffer */
+ dev_dbg(stat->isp->dev, "%s: new buffer requested without "
+ "queuing active one.\n",
+ stat->subdev.name);
+ else
+ stat->active_buf = isp_stat_buf_find_oldest_or_empty(stat);
+}
+
+static void isp_stat_buf_release(struct ispstat *stat)
+{
+ unsigned long flags;
+
+ isp_stat_buf_sync_for_device(stat, stat->locked_buf);
+ spin_lock_irqsave(&stat->isp->stat_lock, flags);
+ stat->locked_buf = NULL;
+ spin_unlock_irqrestore(&stat->isp->stat_lock, flags);
+}
+
+/* Get buffer to userspace. */
+static struct ispstat_buffer *isp_stat_buf_get(struct ispstat *stat,
+ struct omap3isp_stat_data *data)
+{
+ int rval = 0;
+ unsigned long flags;
+ struct ispstat_buffer *buf;
+
+ spin_lock_irqsave(&stat->isp->stat_lock, flags);
+
+ while (1) {
+ buf = isp_stat_buf_find_oldest(stat);
+ if (!buf) {
+ spin_unlock_irqrestore(&stat->isp->stat_lock, flags);
+ dev_dbg(stat->isp->dev, "%s: cannot find a buffer.\n",
+ stat->subdev.name);
+ return ERR_PTR(-EBUSY);
+ }
+ if (isp_stat_buf_check_magic(stat, buf)) {
+ dev_dbg(stat->isp->dev, "%s: current buffer has "
+ "corrupted data\n.", stat->subdev.name);
+ /* Mark empty because it doesn't have valid data. */
+ buf->empty = 1;
+ } else {
+ /* Buffer isn't corrupted. */
+ break;
+ }
+ }
+
+ stat->locked_buf = buf;
+
+ spin_unlock_irqrestore(&stat->isp->stat_lock, flags);
+
+ if (buf->buf_size > data->buf_size) {
+ dev_warn(stat->isp->dev, "%s: userspace's buffer size is "
+ "not enough.\n", stat->subdev.name);
+ isp_stat_buf_release(stat);
+ return ERR_PTR(-EINVAL);
+ }
+
+ isp_stat_buf_sync_for_cpu(stat, buf);
+
+ rval = copy_to_user(data->buf,
+ buf->virt_addr,
+ buf->buf_size);
+
+ if (rval) {
+ dev_info(stat->isp->dev,
+ "%s: failed copying %d bytes of stat data\n",
+ stat->subdev.name, rval);
+ buf = ERR_PTR(-EFAULT);
+ isp_stat_buf_release(stat);
+ }
+
+ return buf;
+}
+
+static void isp_stat_bufs_free(struct ispstat *stat)
+{
+ struct isp_device *isp = stat->isp;
+ int i;
+
+ for (i = 0; i < STAT_MAX_BUFS; i++) {
+ struct ispstat_buffer *buf = &stat->buf[i];
+
+ if (!IS_COHERENT_BUF(stat)) {
+ if (IS_ERR_OR_NULL((void *)buf->iommu_addr))
+ continue;
+ if (buf->iovm)
+ dma_unmap_sg(isp->dev, buf->iovm->sgt->sgl,
+ buf->iovm->sgt->nents,
+ DMA_FROM_DEVICE);
+ omap_iommu_vfree(isp->domain, isp->dev,
+ buf->iommu_addr);
+ } else {
+ if (!buf->virt_addr)
+ continue;
+ dma_free_coherent(stat->isp->dev, stat->buf_alloc_size,
+ buf->virt_addr, buf->dma_addr);
+ }
+ buf->iommu_addr = 0;
+ buf->iovm = NULL;
+ buf->dma_addr = 0;
+ buf->virt_addr = NULL;
+ buf->empty = 1;
+ }
+
+ dev_dbg(stat->isp->dev, "%s: all buffers were freed.\n",
+ stat->subdev.name);
+
+ stat->buf_alloc_size = 0;
+ stat->active_buf = NULL;
+}
+
+static int isp_stat_bufs_alloc_iommu(struct ispstat *stat, unsigned int size)
+{
+ struct isp_device *isp = stat->isp;
+ int i;
+
+ stat->buf_alloc_size = size;
+
+ for (i = 0; i < STAT_MAX_BUFS; i++) {
+ struct ispstat_buffer *buf = &stat->buf[i];
+ struct iovm_struct *iovm;
+
+ WARN_ON(buf->dma_addr);
+ buf->iommu_addr = omap_iommu_vmalloc(isp->domain, isp->dev, 0,
+ size, IOMMU_FLAG);
+ if (IS_ERR((void *)buf->iommu_addr)) {
+ dev_err(stat->isp->dev,
+ "%s: Can't acquire memory for "
+ "buffer %d\n", stat->subdev.name, i);
+ isp_stat_bufs_free(stat);
+ return -ENOMEM;
+ }
+
+ iovm = omap_find_iovm_area(isp->dev, buf->iommu_addr);
+ if (!iovm ||
+ !dma_map_sg(isp->dev, iovm->sgt->sgl, iovm->sgt->nents,
+ DMA_FROM_DEVICE)) {
+ isp_stat_bufs_free(stat);
+ return -ENOMEM;
+ }
+ buf->iovm = iovm;
+
+ buf->virt_addr = omap_da_to_va(stat->isp->dev,
+ (u32)buf->iommu_addr);
+ buf->empty = 1;
+ dev_dbg(stat->isp->dev, "%s: buffer[%d] allocated."
+ "iommu_addr=0x%08lx virt_addr=0x%08lx",
+ stat->subdev.name, i, buf->iommu_addr,
+ (unsigned long)buf->virt_addr);
+ }
+
+ return 0;
+}
+
+static int isp_stat_bufs_alloc_dma(struct ispstat *stat, unsigned int size)
+{
+ int i;
+
+ stat->buf_alloc_size = size;
+
+ for (i = 0; i < STAT_MAX_BUFS; i++) {
+ struct ispstat_buffer *buf = &stat->buf[i];
+
+ WARN_ON(buf->iommu_addr);
+ buf->virt_addr = dma_alloc_coherent(stat->isp->dev, size,
+ &buf->dma_addr, GFP_KERNEL | GFP_DMA);
+
+ if (!buf->virt_addr || !buf->dma_addr) {
+ dev_info(stat->isp->dev,
+ "%s: Can't acquire memory for "
+ "DMA buffer %d\n", stat->subdev.name, i);
+ isp_stat_bufs_free(stat);
+ return -ENOMEM;
+ }
+ buf->empty = 1;
+
+ dev_dbg(stat->isp->dev, "%s: buffer[%d] allocated."
+ "dma_addr=0x%08lx virt_addr=0x%08lx\n",
+ stat->subdev.name, i, (unsigned long)buf->dma_addr,
+ (unsigned long)buf->virt_addr);
+ }
+
+ return 0;
+}
+
+static int isp_stat_bufs_alloc(struct ispstat *stat, u32 size)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&stat->isp->stat_lock, flags);
+
+ BUG_ON(stat->locked_buf != NULL);
+
+ /* Are the old buffers big enough? */
+ if (stat->buf_alloc_size >= size) {
+ spin_unlock_irqrestore(&stat->isp->stat_lock, flags);
+ return 0;
+ }
+
+ if (stat->state != ISPSTAT_DISABLED || stat->buf_processing) {
+ dev_info(stat->isp->dev,
+ "%s: trying to allocate memory when busy\n",
+ stat->subdev.name);
+ spin_unlock_irqrestore(&stat->isp->stat_lock, flags);
+ return -EBUSY;
+ }
+
+ spin_unlock_irqrestore(&stat->isp->stat_lock, flags);
+
+ isp_stat_bufs_free(stat);
+
+ if (IS_COHERENT_BUF(stat))
+ return isp_stat_bufs_alloc_dma(stat, size);
+ else
+ return isp_stat_bufs_alloc_iommu(stat, size);
+}
+
+static void isp_stat_queue_event(struct ispstat *stat, int err)
+{
+ struct video_device *vdev = stat->subdev.devnode;
+ struct v4l2_event event;
+ struct omap3isp_stat_event_status *status = (void *)event.u.data;
+
+ memset(&event, 0, sizeof(event));
+ if (!err) {
+ status->frame_number = stat->frame_number;
+ status->config_counter = stat->config_counter;
+ } else {
+ status->buf_err = 1;
+ }
+ event.type = stat->event_type;
+ v4l2_event_queue(vdev, &event);
+}
+
+
+/*
+ * omap3isp_stat_request_statistics - Request statistics.
+ * @data: Pointer to return statistics data.
+ *
+ * Returns 0 if successful.
+ */
+int omap3isp_stat_request_statistics(struct ispstat *stat,
+ struct omap3isp_stat_data *data)
+{
+ struct ispstat_buffer *buf;
+
+ if (stat->state != ISPSTAT_ENABLED) {
+ dev_dbg(stat->isp->dev, "%s: engine not enabled.\n",
+ stat->subdev.name);
+ return -EINVAL;
+ }
+
+ mutex_lock(&stat->ioctl_lock);
+ buf = isp_stat_buf_get(stat, data);
+ if (IS_ERR(buf)) {
+ mutex_unlock(&stat->ioctl_lock);
+ return PTR_ERR(buf);
+ }
+
+ data->ts = buf->ts;
+ data->config_counter = buf->config_counter;
+ data->frame_number = buf->frame_number;
+ data->buf_size = buf->buf_size;
+
+ buf->empty = 1;
+ isp_stat_buf_release(stat);
+ mutex_unlock(&stat->ioctl_lock);
+
+ return 0;
+}
+
+/*
+ * omap3isp_stat_config - Receives new statistic engine configuration.
+ * @new_conf: Pointer to config structure.
+ *
+ * Returns 0 if successful, -EINVAL if new_conf pointer is NULL, -ENOMEM if
+ * was unable to allocate memory for the buffer, or other errors if parameters
+ * are invalid.
+ */
+int omap3isp_stat_config(struct ispstat *stat, void *new_conf)
+{
+ int ret;
+ unsigned long irqflags;
+ struct ispstat_generic_config *user_cfg = new_conf;
+ u32 buf_size = user_cfg->buf_size;
+
+ if (!new_conf) {
+ dev_dbg(stat->isp->dev, "%s: configuration is NULL\n",
+ stat->subdev.name);
+ return -EINVAL;
+ }
+
+ mutex_lock(&stat->ioctl_lock);
+
+ dev_dbg(stat->isp->dev, "%s: configuring module with buffer "
+ "size=0x%08lx\n", stat->subdev.name, (unsigned long)buf_size);
+
+ ret = stat->ops->validate_params(stat, new_conf);
+ if (ret) {
+ mutex_unlock(&stat->ioctl_lock);
+ dev_dbg(stat->isp->dev, "%s: configuration values are "
+ "invalid.\n", stat->subdev.name);
+ return ret;
+ }
+
+ if (buf_size != user_cfg->buf_size)
+ dev_dbg(stat->isp->dev, "%s: driver has corrected buffer size "
+ "request to 0x%08lx\n", stat->subdev.name,
+ (unsigned long)user_cfg->buf_size);
+
+ /*
+ * Hack: H3A modules may need a doubled buffer size to avoid access
+ * to a invalid memory address after a SBL overflow.
+ * The buffer size is always PAGE_ALIGNED.
+ * Hack 2: MAGIC_SIZE is added to buf_size so a magic word can be
+ * inserted at the end to data integrity check purpose.
+ * Hack 3: AF module writes one paxel data more than it should, so
+ * the buffer allocation must consider it to avoid invalid memory
+ * access.
+ * Hack 4: H3A need to allocate extra space for the recover state.
+ */
+ if (IS_H3A(stat)) {
+ buf_size = user_cfg->buf_size * 2 + MAGIC_SIZE;
+ if (IS_H3A_AF(stat))
+ /*
+ * Adding one extra paxel data size for each recover
+ * buffer + 2 regular ones.
+ */
+ buf_size += AF_EXTRA_DATA * (NUM_H3A_RECOVER_BUFS + 2);
+ if (stat->recover_priv) {
+ struct ispstat_generic_config *recover_cfg =
+ stat->recover_priv;
+ buf_size += recover_cfg->buf_size *
+ NUM_H3A_RECOVER_BUFS;
+ }
+ buf_size = PAGE_ALIGN(buf_size);
+ } else { /* Histogram */
+ buf_size = PAGE_ALIGN(user_cfg->buf_size + MAGIC_SIZE);
+ }
+
+ ret = isp_stat_bufs_alloc(stat, buf_size);
+ if (ret) {
+ mutex_unlock(&stat->ioctl_lock);
+ return ret;
+ }
+
+ spin_lock_irqsave(&stat->isp->stat_lock, irqflags);
+ stat->ops->set_params(stat, new_conf);
+ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
+
+ /*
+ * Returning the right future config_counter for this setup, so
+ * userspace can *know* when it has been applied.
+ */
+ user_cfg->config_counter = stat->config_counter + stat->inc_config;
+
+ /* Module has a valid configuration. */
+ stat->configured = 1;
+ dev_dbg(stat->isp->dev, "%s: module has been successfully "
+ "configured.\n", stat->subdev.name);
+
+ mutex_unlock(&stat->ioctl_lock);
+
+ return 0;
+}
+
+/*
+ * isp_stat_buf_process - Process statistic buffers.
+ * @buf_state: points out if buffer is ready to be processed. It's necessary
+ * because histogram needs to copy the data from internal memory
+ * before be able to process the buffer.
+ */
+static int isp_stat_buf_process(struct ispstat *stat, int buf_state)
+{
+ int ret = STAT_NO_BUF;
+
+ if (!atomic_add_unless(&stat->buf_err, -1, 0) &&
+ buf_state == STAT_BUF_DONE && stat->state == ISPSTAT_ENABLED) {
+ ret = isp_stat_buf_queue(stat);
+ isp_stat_buf_next(stat);
+ }
+
+ return ret;
+}
+
+int omap3isp_stat_pcr_busy(struct ispstat *stat)
+{
+ return stat->ops->busy(stat);
+}
+
+int omap3isp_stat_busy(struct ispstat *stat)
+{
+ return omap3isp_stat_pcr_busy(stat) | stat->buf_processing |
+ (stat->state != ISPSTAT_DISABLED);
+}
+
+/*
+ * isp_stat_pcr_enable - Disables/Enables statistic engines.
+ * @pcr_enable: 0/1 - Disables/Enables the engine.
+ *
+ * Must be called from ISP driver when the module is idle and synchronized
+ * with CCDC.
+ */
+static void isp_stat_pcr_enable(struct ispstat *stat, u8 pcr_enable)
+{
+ if ((stat->state != ISPSTAT_ENABLING &&
+ stat->state != ISPSTAT_ENABLED) && pcr_enable)
+ /* Userspace has disabled the module. Aborting. */
+ return;
+
+ stat->ops->enable(stat, pcr_enable);
+ if (stat->state == ISPSTAT_DISABLING && !pcr_enable)
+ stat->state = ISPSTAT_DISABLED;
+ else if (stat->state == ISPSTAT_ENABLING && pcr_enable)
+ stat->state = ISPSTAT_ENABLED;
+}
+
+void omap3isp_stat_suspend(struct ispstat *stat)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&stat->isp->stat_lock, flags);
+
+ if (stat->state != ISPSTAT_DISABLED)
+ stat->ops->enable(stat, 0);
+ if (stat->state == ISPSTAT_ENABLED)
+ stat->state = ISPSTAT_SUSPENDED;
+
+ spin_unlock_irqrestore(&stat->isp->stat_lock, flags);
+}
+
+void omap3isp_stat_resume(struct ispstat *stat)
+{
+ /* Module will be re-enabled with its pipeline */
+ if (stat->state == ISPSTAT_SUSPENDED)
+ stat->state = ISPSTAT_ENABLING;
+}
+
+static void isp_stat_try_enable(struct ispstat *stat)
+{
+ unsigned long irqflags;
+
+ if (stat->priv == NULL)
+ /* driver wasn't initialised */
+ return;
+
+ spin_lock_irqsave(&stat->isp->stat_lock, irqflags);
+ if (stat->state == ISPSTAT_ENABLING && !stat->buf_processing &&
+ stat->buf_alloc_size) {
+ /*
+ * Userspace's requested to enable the engine but it wasn't yet.
+ * Let's do that now.
+ */
+ stat->update = 1;
+ isp_stat_buf_next(stat);
+ stat->ops->setup_regs(stat, stat->priv);
+ isp_stat_buf_insert_magic(stat, stat->active_buf);
+
+ /*
+ * H3A module has some hw issues which forces the driver to
+ * ignore next buffers even if it was disabled in the meantime.
+ * On the other hand, Histogram shouldn't ignore buffers anymore
+ * if it's being enabled.
+ */
+ if (!IS_H3A(stat))
+ atomic_set(&stat->buf_err, 0);
+
+ isp_stat_pcr_enable(stat, 1);
+ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
+ dev_dbg(stat->isp->dev, "%s: module is enabled.\n",
+ stat->subdev.name);
+ } else {
+ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
+ }
+}
+
+void omap3isp_stat_isr_frame_sync(struct ispstat *stat)
+{
+ isp_stat_try_enable(stat);
+}
+
+void omap3isp_stat_sbl_overflow(struct ispstat *stat)
+{
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&stat->isp->stat_lock, irqflags);
+ /*
+ * Due to a H3A hw issue which prevents the next buffer to start from
+ * the correct memory address, 2 buffers must be ignored.
+ */
+ atomic_set(&stat->buf_err, 2);
+
+ /*
+ * If more than one SBL overflow happen in a row, H3A module may access
+ * invalid memory region.
+ * stat->sbl_ovl_recover is set to tell to the driver to temporarily use
+ * a soft configuration which helps to avoid consecutive overflows.
+ */
+ if (stat->recover_priv)
+ stat->sbl_ovl_recover = 1;
+ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
+}
+
+/*
+ * omap3isp_stat_enable - Disable/Enable statistic engine as soon as possible
+ * @enable: 0/1 - Disables/Enables the engine.
+ *
+ * Client should configure all the module registers before this.
+ * This function can be called from a userspace request.
+ */
+int omap3isp_stat_enable(struct ispstat *stat, u8 enable)
+{
+ unsigned long irqflags;
+
+ dev_dbg(stat->isp->dev, "%s: user wants to %s module.\n",
+ stat->subdev.name, enable ? "enable" : "disable");
+
+ /* Prevent enabling while configuring */
+ mutex_lock(&stat->ioctl_lock);
+
+ spin_lock_irqsave(&stat->isp->stat_lock, irqflags);
+
+ if (!stat->configured && enable) {
+ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
+ mutex_unlock(&stat->ioctl_lock);
+ dev_dbg(stat->isp->dev, "%s: cannot enable module as it's "
+ "never been successfully configured so far.\n",
+ stat->subdev.name);
+ return -EINVAL;
+ }
+
+ if (enable) {
+ if (stat->state == ISPSTAT_DISABLING)
+ /* Previous disabling request wasn't done yet */
+ stat->state = ISPSTAT_ENABLED;
+ else if (stat->state == ISPSTAT_DISABLED)
+ /* Module is now being enabled */
+ stat->state = ISPSTAT_ENABLING;
+ } else {
+ if (stat->state == ISPSTAT_ENABLING) {
+ /* Previous enabling request wasn't done yet */
+ stat->state = ISPSTAT_DISABLED;
+ } else if (stat->state == ISPSTAT_ENABLED) {
+ /* Module is now being disabled */
+ stat->state = ISPSTAT_DISABLING;
+ isp_stat_buf_clear(stat);
+ }
+ }
+
+ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
+ mutex_unlock(&stat->ioctl_lock);
+
+ return 0;
+}
+
+int omap3isp_stat_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ struct ispstat *stat = v4l2_get_subdevdata(subdev);
+
+ if (enable) {
+ /*
+ * Only set enable PCR bit if the module was previously
+ * enabled through ioct.
+ */
+ isp_stat_try_enable(stat);
+ } else {
+ unsigned long flags;
+ /* Disable PCR bit and config enable field */
+ omap3isp_stat_enable(stat, 0);
+ spin_lock_irqsave(&stat->isp->stat_lock, flags);
+ stat->ops->enable(stat, 0);
+ spin_unlock_irqrestore(&stat->isp->stat_lock, flags);
+
+ /*
+ * If module isn't busy, a new interrupt may come or not to
+ * set the state to DISABLED. As Histogram needs to read its
+ * internal memory to clear it, let interrupt handler
+ * responsible of changing state to DISABLED. If the last
+ * interrupt is coming, it's still safe as the handler will
+ * ignore the second time when state is already set to DISABLED.
+ * It's necessary to synchronize Histogram with streamoff, once
+ * the module may be considered idle before last SDMA transfer
+ * starts if we return here.
+ */
+ if (!omap3isp_stat_pcr_busy(stat))
+ omap3isp_stat_isr(stat);
+
+ dev_dbg(stat->isp->dev, "%s: module is being disabled\n",
+ stat->subdev.name);
+ }
+
+ return 0;
+}
+
+/*
+ * __stat_isr - Interrupt handler for statistic drivers
+ */
+static void __stat_isr(struct ispstat *stat, int from_dma)
+{
+ int ret = STAT_BUF_DONE;
+ int buf_processing;
+ unsigned long irqflags;
+ struct isp_pipeline *pipe;
+
+ /*
+ * stat->buf_processing must be set before disable module. It's
+ * necessary to not inform too early the buffers aren't busy in case
+ * of SDMA is going to be used.
+ */
+ spin_lock_irqsave(&stat->isp->stat_lock, irqflags);
+ if (stat->state == ISPSTAT_DISABLED) {
+ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
+ return;
+ }
+ buf_processing = stat->buf_processing;
+ stat->buf_processing = 1;
+ stat->ops->enable(stat, 0);
+
+ if (buf_processing && !from_dma) {
+ if (stat->state == ISPSTAT_ENABLED) {
+ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
+ dev_err(stat->isp->dev,
+ "%s: interrupt occurred when module was still "
+ "processing a buffer.\n", stat->subdev.name);
+ ret = STAT_NO_BUF;
+ goto out;
+ } else {
+ /*
+ * Interrupt handler was called from streamoff when
+ * the module wasn't busy anymore to ensure it is being
+ * disabled after process last buffer. If such buffer
+ * processing has already started, no need to do
+ * anything else.
+ */
+ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
+ return;
+ }
+ }
+ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
+
+ /* If it's busy we can't process this buffer anymore */
+ if (!omap3isp_stat_pcr_busy(stat)) {
+ if (!from_dma && stat->ops->buf_process)
+ /* Module still need to copy data to buffer. */
+ ret = stat->ops->buf_process(stat);
+ if (ret == STAT_BUF_WAITING_DMA)
+ /* Buffer is not ready yet */
+ return;
+
+ spin_lock_irqsave(&stat->isp->stat_lock, irqflags);
+
+ /*
+ * Histogram needs to read its internal memory to clear it
+ * before be disabled. For that reason, common statistic layer
+ * can return only after call stat's buf_process() operator.
+ */
+ if (stat->state == ISPSTAT_DISABLING) {
+ stat->state = ISPSTAT_DISABLED;
+ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
+ stat->buf_processing = 0;
+ return;
+ }
+ pipe = to_isp_pipeline(&stat->subdev.entity);
+ stat->frame_number = atomic_read(&pipe->frame_number);
+
+ /*
+ * Before this point, 'ret' stores the buffer's status if it's
+ * ready to be processed. Afterwards, it holds the status if
+ * it was processed successfully.
+ */
+ ret = isp_stat_buf_process(stat, ret);
+
+ if (likely(!stat->sbl_ovl_recover)) {
+ stat->ops->setup_regs(stat, stat->priv);
+ } else {
+ /*
+ * Using recover config to increase the chance to have
+ * a good buffer processing and make the H3A module to
+ * go back to a valid state.
+ */
+ stat->update = 1;
+ stat->ops->setup_regs(stat, stat->recover_priv);
+ stat->sbl_ovl_recover = 0;
+
+ /*
+ * Set 'update' in case of the module needs to use
+ * regular configuration after next buffer.
+ */
+ stat->update = 1;
+ }
+
+ isp_stat_buf_insert_magic(stat, stat->active_buf);
+
+ /*
+ * Hack: H3A modules may access invalid memory address or send
+ * corrupted data to userspace if more than 1 SBL overflow
+ * happens in a row without re-writing its buffer's start memory
+ * address in the meantime. Such situation is avoided if the
+ * module is not immediately re-enabled when the ISR misses the
+ * timing to process the buffer and to setup the registers.
+ * Because of that, pcr_enable(1) was moved to inside this 'if'
+ * block. But the next interruption will still happen as during
+ * pcr_enable(0) the module was busy.
+ */
+ isp_stat_pcr_enable(stat, 1);
+ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
+ } else {
+ /*
+ * If a SBL overflow occurs and the H3A driver misses the timing
+ * to process the buffer, stat->buf_err is set and won't be
+ * cleared now. So the next buffer will be correctly ignored.
+ * It's necessary due to a hw issue which makes the next H3A
+ * buffer to start from the memory address where the previous
+ * one stopped, instead of start where it was configured to.
+ * Do not "stat->buf_err = 0" here.
+ */
+
+ if (stat->ops->buf_process)
+ /*
+ * Driver may need to erase current data prior to
+ * process a new buffer. If it misses the timing, the
+ * next buffer might be wrong. So should be ignored.
+ * It happens only for Histogram.
+ */
+ atomic_set(&stat->buf_err, 1);
+
+ ret = STAT_NO_BUF;
+ dev_dbg(stat->isp->dev, "%s: cannot process buffer, "
+ "device is busy.\n", stat->subdev.name);
+ }
+
+out:
+ stat->buf_processing = 0;
+ isp_stat_queue_event(stat, ret != STAT_BUF_DONE);
+}
+
+void omap3isp_stat_isr(struct ispstat *stat)
+{
+ __stat_isr(stat, 0);
+}
+
+void omap3isp_stat_dma_isr(struct ispstat *stat)
+{
+ __stat_isr(stat, 1);
+}
+
+int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev,
+ struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ struct ispstat *stat = v4l2_get_subdevdata(subdev);
+
+ if (sub->type != stat->event_type)
+ return -EINVAL;
+
+ return v4l2_event_subscribe(fh, sub, STAT_NEVENTS);
+}
+
+int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev,
+ struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ return v4l2_event_unsubscribe(fh, sub);
+}
+
+void omap3isp_stat_unregister_entities(struct ispstat *stat)
+{
+ v4l2_device_unregister_subdev(&stat->subdev);
+}
+
+int omap3isp_stat_register_entities(struct ispstat *stat,
+ struct v4l2_device *vdev)
+{
+ return v4l2_device_register_subdev(vdev, &stat->subdev);
+}
+
+static int isp_stat_init_entities(struct ispstat *stat, const char *name,
+ const struct v4l2_subdev_ops *sd_ops)
+{
+ struct v4l2_subdev *subdev = &stat->subdev;
+ struct media_entity *me = &subdev->entity;
+
+ v4l2_subdev_init(subdev, sd_ops);
+ snprintf(subdev->name, V4L2_SUBDEV_NAME_SIZE, "OMAP3 ISP %s", name);
+ subdev->grp_id = 1 << 16; /* group ID for isp subdevs */
+ subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
+ v4l2_set_subdevdata(subdev, stat);
+
+ stat->pad.flags = MEDIA_PAD_FL_SINK;
+ me->ops = NULL;
+
+ return media_entity_init(me, 1, &stat->pad, 0);
+}
+
+int omap3isp_stat_init(struct ispstat *stat, const char *name,
+ const struct v4l2_subdev_ops *sd_ops)
+{
+ int ret;
+
+ stat->buf = kcalloc(STAT_MAX_BUFS, sizeof(*stat->buf), GFP_KERNEL);
+ if (!stat->buf)
+ return -ENOMEM;
+
+ isp_stat_buf_clear(stat);
+ mutex_init(&stat->ioctl_lock);
+ atomic_set(&stat->buf_err, 0);
+
+ ret = isp_stat_init_entities(stat, name, sd_ops);
+ if (ret < 0) {
+ mutex_destroy(&stat->ioctl_lock);
+ kfree(stat->buf);
+ }
+
+ return ret;
+}
+
+void omap3isp_stat_cleanup(struct ispstat *stat)
+{
+ media_entity_cleanup(&stat->subdev.entity);
+ mutex_destroy(&stat->ioctl_lock);
+ isp_stat_bufs_free(stat);
+ kfree(stat->buf);
+}
diff --git a/drivers/media/video/omap3isp/ispstat.h b/drivers/media/video/omap3isp/ispstat.h
new file mode 100644
index 00000000..9b7c8654
--- /dev/null
+++ b/drivers/media/video/omap3isp/ispstat.h
@@ -0,0 +1,169 @@
+/*
+ * ispstat.h
+ *
+ * TI OMAP3 ISP - Statistics core
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc
+ *
+ * Contacts: David Cohen <dacohen@gmail.com>
+ * Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP3_ISP_STAT_H
+#define OMAP3_ISP_STAT_H
+
+#include <linux/types.h>
+#include <linux/omap3isp.h>
+#include <plat/dma.h>
+#include <media/v4l2-event.h>
+
+#include "isp.h"
+#include "ispvideo.h"
+
+#define STAT_MAX_BUFS 5
+#define STAT_NEVENTS 8
+
+#define STAT_BUF_DONE 0 /* Buffer is ready */
+#define STAT_NO_BUF 1 /* An error has occurred */
+#define STAT_BUF_WAITING_DMA 2 /* Histogram only: DMA is running */
+
+struct ispstat;
+
+struct ispstat_buffer {
+ unsigned long iommu_addr;
+ struct iovm_struct *iovm;
+ void *virt_addr;
+ dma_addr_t dma_addr;
+ struct timeval ts;
+ u32 buf_size;
+ u32 frame_number;
+ u16 config_counter;
+ u8 empty;
+};
+
+struct ispstat_ops {
+ /*
+ * Validate new params configuration.
+ * new_conf->buf_size value must be changed to the exact buffer size
+ * necessary for the new configuration if it's smaller.
+ */
+ int (*validate_params)(struct ispstat *stat, void *new_conf);
+
+ /*
+ * Save new params configuration.
+ * stat->priv->buf_size value must be set to the exact buffer size for
+ * the new configuration.
+ * stat->update is set to 1 if new configuration is different than
+ * current one.
+ */
+ void (*set_params)(struct ispstat *stat, void *new_conf);
+
+ /* Apply stored configuration. */
+ void (*setup_regs)(struct ispstat *stat, void *priv);
+
+ /* Enable/Disable module. */
+ void (*enable)(struct ispstat *stat, int enable);
+
+ /* Verify is module is busy. */
+ int (*busy)(struct ispstat *stat);
+
+ /* Used for specific operations during generic buf process task. */
+ int (*buf_process)(struct ispstat *stat);
+};
+
+enum ispstat_state_t {
+ ISPSTAT_DISABLED = 0,
+ ISPSTAT_DISABLING,
+ ISPSTAT_ENABLED,
+ ISPSTAT_ENABLING,
+ ISPSTAT_SUSPENDED,
+};
+
+struct ispstat {
+ struct v4l2_subdev subdev;
+ struct media_pad pad; /* sink pad */
+
+ /* Control */
+ unsigned configured:1;
+ unsigned update:1;
+ unsigned buf_processing:1;
+ unsigned sbl_ovl_recover:1;
+ u8 inc_config;
+ atomic_t buf_err;
+ enum ispstat_state_t state; /* enabling/disabling state */
+ struct omap_dma_channel_params dma_config;
+ struct isp_device *isp;
+ void *priv; /* pointer to priv config struct */
+ void *recover_priv; /* pointer to recover priv configuration */
+ struct mutex ioctl_lock; /* serialize private ioctl */
+
+ const struct ispstat_ops *ops;
+
+ /* Buffer */
+ u8 wait_acc_frames;
+ u16 config_counter;
+ u32 frame_number;
+ u32 buf_size;
+ u32 buf_alloc_size;
+ int dma_ch;
+ unsigned long event_type;
+ struct ispstat_buffer *buf;
+ struct ispstat_buffer *active_buf;
+ struct ispstat_buffer *locked_buf;
+};
+
+struct ispstat_generic_config {
+ /*
+ * Fields must be in the same order as in:
+ * - omap3isp_h3a_aewb_config
+ * - omap3isp_h3a_af_config
+ * - omap3isp_hist_config
+ */
+ u32 buf_size;
+ u16 config_counter;
+};
+
+int omap3isp_stat_config(struct ispstat *stat, void *new_conf);
+int omap3isp_stat_request_statistics(struct ispstat *stat,
+ struct omap3isp_stat_data *data);
+int omap3isp_stat_init(struct ispstat *stat, const char *name,
+ const struct v4l2_subdev_ops *sd_ops);
+void omap3isp_stat_cleanup(struct ispstat *stat);
+int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev,
+ struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub);
+int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev,
+ struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub);
+int omap3isp_stat_s_stream(struct v4l2_subdev *subdev, int enable);
+
+int omap3isp_stat_busy(struct ispstat *stat);
+int omap3isp_stat_pcr_busy(struct ispstat *stat);
+void omap3isp_stat_suspend(struct ispstat *stat);
+void omap3isp_stat_resume(struct ispstat *stat);
+int omap3isp_stat_enable(struct ispstat *stat, u8 enable);
+void omap3isp_stat_sbl_overflow(struct ispstat *stat);
+void omap3isp_stat_isr(struct ispstat *stat);
+void omap3isp_stat_isr_frame_sync(struct ispstat *stat);
+void omap3isp_stat_dma_isr(struct ispstat *stat);
+int omap3isp_stat_register_entities(struct ispstat *stat,
+ struct v4l2_device *vdev);
+void omap3isp_stat_unregister_entities(struct ispstat *stat);
+
+#endif /* OMAP3_ISP_STAT_H */
diff --git a/drivers/media/video/omap3isp/ispvideo.c b/drivers/media/video/omap3isp/ispvideo.c
new file mode 100644
index 00000000..b0207005
--- /dev/null
+++ b/drivers/media/video/omap3isp/ispvideo.c
@@ -0,0 +1,1361 @@
+/*
+ * ispvideo.c
+ *
+ * TI OMAP3 ISP - Generic video node
+ *
+ * Copyright (C) 2009-2010 Nokia Corporation
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <asm/cacheflush.h>
+#include <linux/clk.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/pagemap.h>
+#include <linux/scatterlist.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <plat/iommu.h>
+#include <plat/iovmm.h>
+#include <plat/omap-pm.h>
+
+#include "ispvideo.h"
+#include "isp.h"
+
+
+/* -----------------------------------------------------------------------------
+ * Helper functions
+ */
+
+static struct isp_format_info formats[] = {
+ { V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8,
+ V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8,
+ V4L2_PIX_FMT_GREY, 8, },
+ { V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y10_1X10,
+ V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y8_1X8,
+ V4L2_PIX_FMT_Y10, 10, },
+ { V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y10_1X10,
+ V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y8_1X8,
+ V4L2_PIX_FMT_Y12, 12, },
+ { V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8,
+ V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8,
+ V4L2_PIX_FMT_SBGGR8, 8, },
+ { V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8,
+ V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8,
+ V4L2_PIX_FMT_SGBRG8, 8, },
+ { V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8,
+ V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8,
+ V4L2_PIX_FMT_SGRBG8, 8, },
+ { V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8,
+ V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8,
+ V4L2_PIX_FMT_SRGGB8, 8, },
+ { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
+ V4L2_MBUS_FMT_SGRBG10_1X10, 0,
+ V4L2_PIX_FMT_SGRBG10DPCM8, 8, },
+ { V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10,
+ V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR8_1X8,
+ V4L2_PIX_FMT_SBGGR10, 10, },
+ { V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG10_1X10,
+ V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG8_1X8,
+ V4L2_PIX_FMT_SGBRG10, 10, },
+ { V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG10_1X10,
+ V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG8_1X8,
+ V4L2_PIX_FMT_SGRBG10, 10, },
+ { V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB10_1X10,
+ V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB8_1X8,
+ V4L2_PIX_FMT_SRGGB10, 10, },
+ { V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR10_1X10,
+ V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR8_1X8,
+ V4L2_PIX_FMT_SBGGR12, 12, },
+ { V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG10_1X10,
+ V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG8_1X8,
+ V4L2_PIX_FMT_SGBRG12, 12, },
+ { V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG10_1X10,
+ V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG8_1X8,
+ V4L2_PIX_FMT_SGRBG12, 12, },
+ { V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB10_1X10,
+ V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB8_1X8,
+ V4L2_PIX_FMT_SRGGB12, 12, },
+ { V4L2_MBUS_FMT_UYVY8_1X16, V4L2_MBUS_FMT_UYVY8_1X16,
+ V4L2_MBUS_FMT_UYVY8_1X16, 0,
+ V4L2_PIX_FMT_UYVY, 16, },
+ { V4L2_MBUS_FMT_YUYV8_1X16, V4L2_MBUS_FMT_YUYV8_1X16,
+ V4L2_MBUS_FMT_YUYV8_1X16, 0,
+ V4L2_PIX_FMT_YUYV, 16, },
+};
+
+const struct isp_format_info *
+omap3isp_video_format_info(enum v4l2_mbus_pixelcode code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+ if (formats[i].code == code)
+ return &formats[i];
+ }
+
+ return NULL;
+}
+
+/*
+ * Decide whether desired output pixel code can be obtained with
+ * the lane shifter by shifting the input pixel code.
+ * @in: input pixelcode to shifter
+ * @out: output pixelcode from shifter
+ * @additional_shift: # of bits the sensor's LSB is offset from CAMEXT[0]
+ *
+ * return true if the combination is possible
+ * return false otherwise
+ */
+static bool isp_video_is_shiftable(enum v4l2_mbus_pixelcode in,
+ enum v4l2_mbus_pixelcode out,
+ unsigned int additional_shift)
+{
+ const struct isp_format_info *in_info, *out_info;
+
+ if (in == out)
+ return true;
+
+ in_info = omap3isp_video_format_info(in);
+ out_info = omap3isp_video_format_info(out);
+
+ if ((in_info->flavor == 0) || (out_info->flavor == 0))
+ return false;
+
+ if (in_info->flavor != out_info->flavor)
+ return false;
+
+ return in_info->bpp - out_info->bpp + additional_shift <= 6;
+}
+
+/*
+ * isp_video_mbus_to_pix - Convert v4l2_mbus_framefmt to v4l2_pix_format
+ * @video: ISP video instance
+ * @mbus: v4l2_mbus_framefmt format (input)
+ * @pix: v4l2_pix_format format (output)
+ *
+ * Fill the output pix structure with information from the input mbus format.
+ * The bytesperline and sizeimage fields are computed from the requested bytes
+ * per line value in the pix format and information from the video instance.
+ *
+ * Return the number of padding bytes at end of line.
+ */
+static unsigned int isp_video_mbus_to_pix(const struct isp_video *video,
+ const struct v4l2_mbus_framefmt *mbus,
+ struct v4l2_pix_format *pix)
+{
+ unsigned int bpl = pix->bytesperline;
+ unsigned int min_bpl;
+ unsigned int i;
+
+ memset(pix, 0, sizeof(*pix));
+ pix->width = mbus->width;
+ pix->height = mbus->height;
+
+ for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+ if (formats[i].code == mbus->code)
+ break;
+ }
+
+ if (WARN_ON(i == ARRAY_SIZE(formats)))
+ return 0;
+
+ min_bpl = pix->width * ALIGN(formats[i].bpp, 8) / 8;
+
+ /* Clamp the requested bytes per line value. If the maximum bytes per
+ * line value is zero, the module doesn't support user configurable line
+ * sizes. Override the requested value with the minimum in that case.
+ */
+ if (video->bpl_max)
+ bpl = clamp(bpl, min_bpl, video->bpl_max);
+ else
+ bpl = min_bpl;
+
+ if (!video->bpl_zero_padding || bpl != min_bpl)
+ bpl = ALIGN(bpl, video->bpl_alignment);
+
+ pix->pixelformat = formats[i].pixelformat;
+ pix->bytesperline = bpl;
+ pix->sizeimage = pix->bytesperline * pix->height;
+ pix->colorspace = mbus->colorspace;
+ pix->field = mbus->field;
+
+ return bpl - min_bpl;
+}
+
+static void isp_video_pix_to_mbus(const struct v4l2_pix_format *pix,
+ struct v4l2_mbus_framefmt *mbus)
+{
+ unsigned int i;
+
+ memset(mbus, 0, sizeof(*mbus));
+ mbus->width = pix->width;
+ mbus->height = pix->height;
+
+ /* Skip the last format in the loop so that it will be selected if no
+ * match is found.
+ */
+ for (i = 0; i < ARRAY_SIZE(formats) - 1; ++i) {
+ if (formats[i].pixelformat == pix->pixelformat)
+ break;
+ }
+
+ mbus->code = formats[i].code;
+ mbus->colorspace = pix->colorspace;
+ mbus->field = pix->field;
+}
+
+static struct v4l2_subdev *
+isp_video_remote_subdev(struct isp_video *video, u32 *pad)
+{
+ struct media_pad *remote;
+
+ remote = media_entity_remote_source(&video->pad);
+
+ if (remote == NULL ||
+ media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ return NULL;
+
+ if (pad)
+ *pad = remote->index;
+
+ return media_entity_to_v4l2_subdev(remote->entity);
+}
+
+/* Return a pointer to the ISP video instance at the far end of the pipeline. */
+static struct isp_video *
+isp_video_far_end(struct isp_video *video)
+{
+ struct media_entity_graph graph;
+ struct media_entity *entity = &video->video.entity;
+ struct media_device *mdev = entity->parent;
+ struct isp_video *far_end = NULL;
+
+ mutex_lock(&mdev->graph_mutex);
+ media_entity_graph_walk_start(&graph, entity);
+
+ while ((entity = media_entity_graph_walk_next(&graph))) {
+ if (entity == &video->video.entity)
+ continue;
+
+ if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
+ continue;
+
+ far_end = to_isp_video(media_entity_to_video_device(entity));
+ if (far_end->type != video->type)
+ break;
+
+ far_end = NULL;
+ }
+
+ mutex_unlock(&mdev->graph_mutex);
+ return far_end;
+}
+
+/*
+ * Validate a pipeline by checking both ends of all links for format
+ * discrepancies.
+ *
+ * Compute the minimum time per frame value as the maximum of time per frame
+ * limits reported by every block in the pipeline.
+ *
+ * Return 0 if all formats match, or -EPIPE if at least one link is found with
+ * different formats on its two ends or if the pipeline doesn't start with a
+ * video source (either a subdev with no input pad, or a non-subdev entity).
+ */
+static int isp_video_validate_pipeline(struct isp_pipeline *pipe)
+{
+ struct isp_device *isp = pipe->output->isp;
+ struct v4l2_subdev_format fmt_source;
+ struct v4l2_subdev_format fmt_sink;
+ struct media_pad *pad;
+ struct v4l2_subdev *subdev;
+ int ret;
+
+ pipe->max_rate = pipe->l3_ick;
+
+ subdev = isp_video_remote_subdev(pipe->output, NULL);
+ if (subdev == NULL)
+ return -EPIPE;
+
+ while (1) {
+ unsigned int shifter_link;
+ /* Retrieve the sink format */
+ pad = &subdev->entity.pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+
+ fmt_sink.pad = pad->index;
+ fmt_sink.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_sink);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return -EPIPE;
+
+ /* Update the maximum frame rate */
+ if (subdev == &isp->isp_res.subdev)
+ omap3isp_resizer_max_rate(&isp->isp_res,
+ &pipe->max_rate);
+
+ /* Check ccdc maximum data rate when data comes from sensor
+ * TODO: Include ccdc rate in pipe->max_rate and compare the
+ * total pipe rate with the input data rate from sensor.
+ */
+ if (subdev == &isp->isp_ccdc.subdev && pipe->input == NULL) {
+ unsigned int rate = UINT_MAX;
+
+ omap3isp_ccdc_max_rate(&isp->isp_ccdc, &rate);
+ if (isp->isp_ccdc.vpcfg.pixelclk > rate)
+ return -ENOSPC;
+ }
+
+ /* If sink pad is on CCDC, the link has the lane shifter
+ * in the middle of it. */
+ shifter_link = subdev == &isp->isp_ccdc.subdev;
+
+ /* Retrieve the source format. Return an error if no source
+ * entity can be found, and stop checking the pipeline if the
+ * source entity isn't a subdev.
+ */
+ pad = media_entity_remote_source(pad);
+ if (pad == NULL)
+ return -EPIPE;
+
+ if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ break;
+
+ subdev = media_entity_to_v4l2_subdev(pad->entity);
+
+ fmt_source.pad = pad->index;
+ fmt_source.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_source);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return -EPIPE;
+
+ /* Check if the two ends match */
+ if (fmt_source.format.width != fmt_sink.format.width ||
+ fmt_source.format.height != fmt_sink.format.height)
+ return -EPIPE;
+
+ if (shifter_link) {
+ unsigned int parallel_shift = 0;
+ if (isp->isp_ccdc.input == CCDC_INPUT_PARALLEL) {
+ struct isp_parallel_platform_data *pdata =
+ &((struct isp_v4l2_subdevs_group *)
+ subdev->host_priv)->bus.parallel;
+ parallel_shift = pdata->data_lane_shift * 2;
+ }
+ if (!isp_video_is_shiftable(fmt_source.format.code,
+ fmt_sink.format.code,
+ parallel_shift))
+ return -EPIPE;
+ } else if (fmt_source.format.code != fmt_sink.format.code)
+ return -EPIPE;
+ }
+
+ return 0;
+}
+
+static int
+__isp_video_get_format(struct isp_video *video, struct v4l2_format *format)
+{
+ struct v4l2_subdev_format fmt;
+ struct v4l2_subdev *subdev;
+ u32 pad;
+ int ret;
+
+ subdev = isp_video_remote_subdev(video, &pad);
+ if (subdev == NULL)
+ return -EINVAL;
+
+ mutex_lock(&video->mutex);
+
+ fmt.pad = pad;
+ fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
+ if (ret == -ENOIOCTLCMD)
+ ret = -EINVAL;
+
+ mutex_unlock(&video->mutex);
+
+ if (ret)
+ return ret;
+
+ format->type = video->type;
+ return isp_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix);
+}
+
+static int
+isp_video_check_format(struct isp_video *video, struct isp_video_fh *vfh)
+{
+ struct v4l2_format format;
+ int ret;
+
+ memcpy(&format, &vfh->format, sizeof(format));
+ ret = __isp_video_get_format(video, &format);
+ if (ret < 0)
+ return ret;
+
+ if (vfh->format.fmt.pix.pixelformat != format.fmt.pix.pixelformat ||
+ vfh->format.fmt.pix.height != format.fmt.pix.height ||
+ vfh->format.fmt.pix.width != format.fmt.pix.width ||
+ vfh->format.fmt.pix.bytesperline != format.fmt.pix.bytesperline ||
+ vfh->format.fmt.pix.sizeimage != format.fmt.pix.sizeimage)
+ return -EINVAL;
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * IOMMU management
+ */
+
+#define IOMMU_FLAG (IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_8)
+
+/*
+ * ispmmu_vmap - Wrapper for Virtual memory mapping of a scatter gather list
+ * @dev: Device pointer specific to the OMAP3 ISP.
+ * @sglist: Pointer to source Scatter gather list to allocate.
+ * @sglen: Number of elements of the scatter-gatter list.
+ *
+ * Returns a resulting mapped device address by the ISP MMU, or -ENOMEM if
+ * we ran out of memory.
+ */
+static dma_addr_t
+ispmmu_vmap(struct isp_device *isp, const struct scatterlist *sglist, int sglen)
+{
+ struct sg_table *sgt;
+ u32 da;
+
+ sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
+ if (sgt == NULL)
+ return -ENOMEM;
+
+ sgt->sgl = (struct scatterlist *)sglist;
+ sgt->nents = sglen;
+ sgt->orig_nents = sglen;
+
+ da = omap_iommu_vmap(isp->domain, isp->dev, 0, sgt, IOMMU_FLAG);
+ if (IS_ERR_VALUE(da))
+ kfree(sgt);
+
+ return da;
+}
+
+/*
+ * ispmmu_vunmap - Unmap a device address from the ISP MMU
+ * @dev: Device pointer specific to the OMAP3 ISP.
+ * @da: Device address generated from a ispmmu_vmap call.
+ */
+static void ispmmu_vunmap(struct isp_device *isp, dma_addr_t da)
+{
+ struct sg_table *sgt;
+
+ sgt = omap_iommu_vunmap(isp->domain, isp->dev, (u32)da);
+ kfree(sgt);
+}
+
+/* -----------------------------------------------------------------------------
+ * Video queue operations
+ */
+
+static void isp_video_queue_prepare(struct isp_video_queue *queue,
+ unsigned int *nbuffers, unsigned int *size)
+{
+ struct isp_video_fh *vfh =
+ container_of(queue, struct isp_video_fh, queue);
+ struct isp_video *video = vfh->video;
+
+ *size = vfh->format.fmt.pix.sizeimage;
+ if (*size == 0)
+ return;
+
+ *nbuffers = min(*nbuffers, video->capture_mem / PAGE_ALIGN(*size));
+}
+
+static void isp_video_buffer_cleanup(struct isp_video_buffer *buf)
+{
+ struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue);
+ struct isp_buffer *buffer = to_isp_buffer(buf);
+ struct isp_video *video = vfh->video;
+
+ if (buffer->isp_addr) {
+ ispmmu_vunmap(video->isp, buffer->isp_addr);
+ buffer->isp_addr = 0;
+ }
+}
+
+static int isp_video_buffer_prepare(struct isp_video_buffer *buf)
+{
+ struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue);
+ struct isp_buffer *buffer = to_isp_buffer(buf);
+ struct isp_video *video = vfh->video;
+ unsigned long addr;
+
+ addr = ispmmu_vmap(video->isp, buf->sglist, buf->sglen);
+ if (IS_ERR_VALUE(addr))
+ return -EIO;
+
+ if (!IS_ALIGNED(addr, 32)) {
+ dev_dbg(video->isp->dev, "Buffer address must be "
+ "aligned to 32 bytes boundary.\n");
+ ispmmu_vunmap(video->isp, buffer->isp_addr);
+ return -EINVAL;
+ }
+
+ buf->vbuf.bytesused = vfh->format.fmt.pix.sizeimage;
+ buffer->isp_addr = addr;
+ return 0;
+}
+
+/*
+ * isp_video_buffer_queue - Add buffer to streaming queue
+ * @buf: Video buffer
+ *
+ * In memory-to-memory mode, start streaming on the pipeline if buffers are
+ * queued on both the input and the output, if the pipeline isn't already busy.
+ * If the pipeline is busy, it will be restarted in the output module interrupt
+ * handler.
+ */
+static void isp_video_buffer_queue(struct isp_video_buffer *buf)
+{
+ struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue);
+ struct isp_buffer *buffer = to_isp_buffer(buf);
+ struct isp_video *video = vfh->video;
+ struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity);
+ enum isp_pipeline_state state;
+ unsigned long flags;
+ unsigned int empty;
+ unsigned int start;
+
+ empty = list_empty(&video->dmaqueue);
+ list_add_tail(&buffer->buffer.irqlist, &video->dmaqueue);
+
+ if (empty) {
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ state = ISP_PIPELINE_QUEUE_OUTPUT;
+ else
+ state = ISP_PIPELINE_QUEUE_INPUT;
+
+ spin_lock_irqsave(&pipe->lock, flags);
+ pipe->state |= state;
+ video->ops->queue(video, buffer);
+ video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_QUEUED;
+
+ start = isp_pipeline_ready(pipe);
+ if (start)
+ pipe->state |= ISP_PIPELINE_STREAM;
+ spin_unlock_irqrestore(&pipe->lock, flags);
+
+ if (start)
+ omap3isp_pipeline_set_stream(pipe,
+ ISP_PIPELINE_STREAM_SINGLESHOT);
+ }
+}
+
+static const struct isp_video_queue_operations isp_video_queue_ops = {
+ .queue_prepare = &isp_video_queue_prepare,
+ .buffer_prepare = &isp_video_buffer_prepare,
+ .buffer_queue = &isp_video_buffer_queue,
+ .buffer_cleanup = &isp_video_buffer_cleanup,
+};
+
+/*
+ * omap3isp_video_buffer_next - Complete the current buffer and return the next
+ * @video: ISP video object
+ *
+ * Remove the current video buffer from the DMA queue and fill its timestamp,
+ * field count and state fields before waking up its completion handler.
+ *
+ * For capture video nodes the buffer state is set to ISP_BUF_STATE_DONE if no
+ * error has been flagged in the pipeline, or to ISP_BUF_STATE_ERROR otherwise.
+ * For video output nodes the buffer state is always set to ISP_BUF_STATE_DONE.
+ *
+ * The DMA queue is expected to contain at least one buffer.
+ *
+ * Return a pointer to the next buffer in the DMA queue, or NULL if the queue is
+ * empty.
+ */
+struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video)
+{
+ struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity);
+ struct isp_video_queue *queue = video->queue;
+ enum isp_pipeline_state state;
+ struct isp_video_buffer *buf;
+ unsigned long flags;
+ struct timespec ts;
+
+ spin_lock_irqsave(&queue->irqlock, flags);
+ if (WARN_ON(list_empty(&video->dmaqueue))) {
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+ return NULL;
+ }
+
+ buf = list_first_entry(&video->dmaqueue, struct isp_video_buffer,
+ irqlist);
+ list_del(&buf->irqlist);
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+
+ ktime_get_ts(&ts);
+ buf->vbuf.timestamp.tv_sec = ts.tv_sec;
+ buf->vbuf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
+
+ /* Do frame number propagation only if this is the output video node.
+ * Frame number either comes from the CSI receivers or it gets
+ * incremented here if H3A is not active.
+ * Note: There is no guarantee that the output buffer will finish
+ * first, so the input number might lag behind by 1 in some cases.
+ */
+ if (video == pipe->output && !pipe->do_propagation)
+ buf->vbuf.sequence = atomic_inc_return(&pipe->frame_number);
+ else
+ buf->vbuf.sequence = atomic_read(&pipe->frame_number);
+
+ /* Report pipeline errors to userspace on the capture device side. */
+ if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->error) {
+ buf->state = ISP_BUF_STATE_ERROR;
+ pipe->error = false;
+ } else {
+ buf->state = ISP_BUF_STATE_DONE;
+ }
+
+ wake_up(&buf->wait);
+
+ if (list_empty(&video->dmaqueue)) {
+ if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ state = ISP_PIPELINE_QUEUE_OUTPUT
+ | ISP_PIPELINE_STREAM;
+ else
+ state = ISP_PIPELINE_QUEUE_INPUT
+ | ISP_PIPELINE_STREAM;
+
+ spin_lock_irqsave(&pipe->lock, flags);
+ pipe->state &= ~state;
+ if (video->pipe.stream_state == ISP_PIPELINE_STREAM_CONTINUOUS)
+ video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN;
+ spin_unlock_irqrestore(&pipe->lock, flags);
+ return NULL;
+ }
+
+ if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) {
+ spin_lock_irqsave(&pipe->lock, flags);
+ pipe->state &= ~ISP_PIPELINE_STREAM;
+ spin_unlock_irqrestore(&pipe->lock, flags);
+ }
+
+ buf = list_first_entry(&video->dmaqueue, struct isp_video_buffer,
+ irqlist);
+ buf->state = ISP_BUF_STATE_ACTIVE;
+ return to_isp_buffer(buf);
+}
+
+/*
+ * omap3isp_video_resume - Perform resume operation on the buffers
+ * @video: ISP video object
+ * @continuous: Pipeline is in single shot mode if 0 or continuous mode otherwise
+ *
+ * This function is intended to be used on suspend/resume scenario. It
+ * requests video queue layer to discard buffers marked as DONE if it's in
+ * continuous mode and requests ISP modules to queue again the ACTIVE buffer
+ * if there's any.
+ */
+void omap3isp_video_resume(struct isp_video *video, int continuous)
+{
+ struct isp_buffer *buf = NULL;
+
+ if (continuous && video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ omap3isp_video_queue_discard_done(video->queue);
+
+ if (!list_empty(&video->dmaqueue)) {
+ buf = list_first_entry(&video->dmaqueue,
+ struct isp_buffer, buffer.irqlist);
+ video->ops->queue(video, buf);
+ video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_QUEUED;
+ } else {
+ if (continuous)
+ video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN;
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 ioctls
+ */
+
+static int
+isp_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
+{
+ struct isp_video *video = video_drvdata(file);
+
+ strlcpy(cap->driver, ISP_VIDEO_DRIVER_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, video->video.name, sizeof(cap->card));
+ strlcpy(cap->bus_info, "media", sizeof(cap->bus_info));
+
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ else
+ cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+static int
+isp_video_get_format(struct file *file, void *fh, struct v4l2_format *format)
+{
+ struct isp_video_fh *vfh = to_isp_video_fh(fh);
+ struct isp_video *video = video_drvdata(file);
+
+ if (format->type != video->type)
+ return -EINVAL;
+
+ mutex_lock(&video->mutex);
+ *format = vfh->format;
+ mutex_unlock(&video->mutex);
+
+ return 0;
+}
+
+static int
+isp_video_set_format(struct file *file, void *fh, struct v4l2_format *format)
+{
+ struct isp_video_fh *vfh = to_isp_video_fh(fh);
+ struct isp_video *video = video_drvdata(file);
+ struct v4l2_mbus_framefmt fmt;
+
+ if (format->type != video->type)
+ return -EINVAL;
+
+ mutex_lock(&video->mutex);
+
+ /* Fill the bytesperline and sizeimage fields by converting to media bus
+ * format and back to pixel format.
+ */
+ isp_video_pix_to_mbus(&format->fmt.pix, &fmt);
+ isp_video_mbus_to_pix(video, &fmt, &format->fmt.pix);
+
+ vfh->format = *format;
+
+ mutex_unlock(&video->mutex);
+ return 0;
+}
+
+static int
+isp_video_try_format(struct file *file, void *fh, struct v4l2_format *format)
+{
+ struct isp_video *video = video_drvdata(file);
+ struct v4l2_subdev_format fmt;
+ struct v4l2_subdev *subdev;
+ u32 pad;
+ int ret;
+
+ if (format->type != video->type)
+ return -EINVAL;
+
+ subdev = isp_video_remote_subdev(video, &pad);
+ if (subdev == NULL)
+ return -EINVAL;
+
+ isp_video_pix_to_mbus(&format->fmt.pix, &fmt.format);
+
+ fmt.pad = pad;
+ fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
+ if (ret)
+ return ret == -ENOIOCTLCMD ? -EINVAL : ret;
+
+ isp_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix);
+ return 0;
+}
+
+static int
+isp_video_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap)
+{
+ struct isp_video *video = video_drvdata(file);
+ struct v4l2_subdev *subdev;
+ int ret;
+
+ subdev = isp_video_remote_subdev(video, NULL);
+ if (subdev == NULL)
+ return -EINVAL;
+
+ mutex_lock(&video->mutex);
+ ret = v4l2_subdev_call(subdev, video, cropcap, cropcap);
+ mutex_unlock(&video->mutex);
+
+ return ret == -ENOIOCTLCMD ? -EINVAL : ret;
+}
+
+static int
+isp_video_get_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+{
+ struct isp_video *video = video_drvdata(file);
+ struct v4l2_subdev_format format;
+ struct v4l2_subdev *subdev;
+ u32 pad;
+ int ret;
+
+ subdev = isp_video_remote_subdev(video, &pad);
+ if (subdev == NULL)
+ return -EINVAL;
+
+ /* Try the get crop operation first and fallback to get format if not
+ * implemented.
+ */
+ ret = v4l2_subdev_call(subdev, video, g_crop, crop);
+ if (ret != -ENOIOCTLCMD)
+ return ret;
+
+ format.pad = pad;
+ format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &format);
+ if (ret < 0)
+ return ret == -ENOIOCTLCMD ? -EINVAL : ret;
+
+ crop->c.left = 0;
+ crop->c.top = 0;
+ crop->c.width = format.format.width;
+ crop->c.height = format.format.height;
+
+ return 0;
+}
+
+static int
+isp_video_set_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+{
+ struct isp_video *video = video_drvdata(file);
+ struct v4l2_subdev *subdev;
+ int ret;
+
+ subdev = isp_video_remote_subdev(video, NULL);
+ if (subdev == NULL)
+ return -EINVAL;
+
+ mutex_lock(&video->mutex);
+ ret = v4l2_subdev_call(subdev, video, s_crop, crop);
+ mutex_unlock(&video->mutex);
+
+ return ret == -ENOIOCTLCMD ? -EINVAL : ret;
+}
+
+static int
+isp_video_get_param(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+ struct isp_video_fh *vfh = to_isp_video_fh(fh);
+ struct isp_video *video = video_drvdata(file);
+
+ if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
+ video->type != a->type)
+ return -EINVAL;
+
+ memset(a, 0, sizeof(*a));
+ a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ a->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
+ a->parm.output.timeperframe = vfh->timeperframe;
+
+ return 0;
+}
+
+static int
+isp_video_set_param(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+ struct isp_video_fh *vfh = to_isp_video_fh(fh);
+ struct isp_video *video = video_drvdata(file);
+
+ if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
+ video->type != a->type)
+ return -EINVAL;
+
+ if (a->parm.output.timeperframe.denominator == 0)
+ a->parm.output.timeperframe.denominator = 1;
+
+ vfh->timeperframe = a->parm.output.timeperframe;
+
+ return 0;
+}
+
+static int
+isp_video_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb)
+{
+ struct isp_video_fh *vfh = to_isp_video_fh(fh);
+
+ return omap3isp_video_queue_reqbufs(&vfh->queue, rb);
+}
+
+static int
+isp_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct isp_video_fh *vfh = to_isp_video_fh(fh);
+
+ return omap3isp_video_queue_querybuf(&vfh->queue, b);
+}
+
+static int
+isp_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct isp_video_fh *vfh = to_isp_video_fh(fh);
+
+ return omap3isp_video_queue_qbuf(&vfh->queue, b);
+}
+
+static int
+isp_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct isp_video_fh *vfh = to_isp_video_fh(fh);
+
+ return omap3isp_video_queue_dqbuf(&vfh->queue, b,
+ file->f_flags & O_NONBLOCK);
+}
+
+/*
+ * Stream management
+ *
+ * Every ISP pipeline has a single input and a single output. The input can be
+ * either a sensor or a video node. The output is always a video node.
+ *
+ * As every pipeline has an output video node, the ISP video objects at the
+ * pipeline output stores the pipeline state. It tracks the streaming state of
+ * both the input and output, as well as the availability of buffers.
+ *
+ * In sensor-to-memory mode, frames are always available at the pipeline input.
+ * Starting the sensor usually requires I2C transfers and must be done in
+ * interruptible context. The pipeline is started and stopped synchronously
+ * to the stream on/off commands. All modules in the pipeline will get their
+ * subdev set stream handler called. The module at the end of the pipeline must
+ * delay starting the hardware until buffers are available at its output.
+ *
+ * In memory-to-memory mode, starting/stopping the stream requires
+ * synchronization between the input and output. ISP modules can't be stopped
+ * in the middle of a frame, and at least some of the modules seem to become
+ * busy as soon as they're started, even if they don't receive a frame start
+ * event. For that reason frames need to be processed in single-shot mode. The
+ * driver needs to wait until a frame is completely processed and written to
+ * memory before restarting the pipeline for the next frame. Pipelined
+ * processing might be possible but requires more testing.
+ *
+ * Stream start must be delayed until buffers are available at both the input
+ * and output. The pipeline must be started in the videobuf queue callback with
+ * the buffers queue spinlock held. The modules subdev set stream operation must
+ * not sleep.
+ */
+static int
+isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
+{
+ struct isp_video_fh *vfh = to_isp_video_fh(fh);
+ struct isp_video *video = video_drvdata(file);
+ enum isp_pipeline_state state;
+ struct isp_pipeline *pipe;
+ struct isp_video *far_end;
+ unsigned long flags;
+ int ret;
+
+ if (type != video->type)
+ return -EINVAL;
+
+ mutex_lock(&video->stream_lock);
+
+ if (video->streaming) {
+ mutex_unlock(&video->stream_lock);
+ return -EBUSY;
+ }
+
+ /* Start streaming on the pipeline. No link touching an entity in the
+ * pipeline can be activated or deactivated once streaming is started.
+ */
+ pipe = video->video.entity.pipe
+ ? to_isp_pipeline(&video->video.entity) : &video->pipe;
+ media_entity_pipeline_start(&video->video.entity, &pipe->pipe);
+
+ /* Verify that the currently configured format matches the output of
+ * the connected subdev.
+ */
+ ret = isp_video_check_format(video, vfh);
+ if (ret < 0)
+ goto error;
+
+ video->bpl_padding = ret;
+ video->bpl_value = vfh->format.fmt.pix.bytesperline;
+
+ /* Find the ISP video node connected at the far end of the pipeline and
+ * update the pipeline.
+ */
+ far_end = isp_video_far_end(video);
+
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ state = ISP_PIPELINE_STREAM_OUTPUT | ISP_PIPELINE_IDLE_OUTPUT;
+ pipe->input = far_end;
+ pipe->output = video;
+ } else {
+ if (far_end == NULL) {
+ ret = -EPIPE;
+ goto error;
+ }
+
+ state = ISP_PIPELINE_STREAM_INPUT | ISP_PIPELINE_IDLE_INPUT;
+ pipe->input = video;
+ pipe->output = far_end;
+ }
+
+ if (video->isp->pdata->set_constraints)
+ video->isp->pdata->set_constraints(video->isp, true);
+ pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]);
+
+ /* Validate the pipeline and update its state. */
+ ret = isp_video_validate_pipeline(pipe);
+ if (ret < 0)
+ goto error;
+
+ pipe->error = false;
+
+ spin_lock_irqsave(&pipe->lock, flags);
+ pipe->state &= ~ISP_PIPELINE_STREAM;
+ pipe->state |= state;
+ spin_unlock_irqrestore(&pipe->lock, flags);
+
+ /* Set the maximum time per frame as the value requested by userspace.
+ * This is a soft limit that can be overridden if the hardware doesn't
+ * support the request limit.
+ */
+ if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ pipe->max_timeperframe = vfh->timeperframe;
+
+ video->queue = &vfh->queue;
+ INIT_LIST_HEAD(&video->dmaqueue);
+ atomic_set(&pipe->frame_number, -1);
+
+ ret = omap3isp_video_queue_streamon(&vfh->queue);
+ if (ret < 0)
+ goto error;
+
+ /* In sensor-to-memory mode, the stream can be started synchronously
+ * to the stream on command. In memory-to-memory mode, it will be
+ * started when buffers are queued on both the input and output.
+ */
+ if (pipe->input == NULL) {
+ ret = omap3isp_pipeline_set_stream(pipe,
+ ISP_PIPELINE_STREAM_CONTINUOUS);
+ if (ret < 0)
+ goto error;
+ spin_lock_irqsave(&video->queue->irqlock, flags);
+ if (list_empty(&video->dmaqueue))
+ video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN;
+ spin_unlock_irqrestore(&video->queue->irqlock, flags);
+ }
+
+error:
+ if (ret < 0) {
+ omap3isp_video_queue_streamoff(&vfh->queue);
+ if (video->isp->pdata->set_constraints)
+ video->isp->pdata->set_constraints(video->isp, false);
+ media_entity_pipeline_stop(&video->video.entity);
+ /* The DMA queue must be emptied here, otherwise CCDC interrupts
+ * that will get triggered the next time the CCDC is powered up
+ * will try to access buffers that might have been freed but
+ * still present in the DMA queue. This can easily get triggered
+ * if the above omap3isp_pipeline_set_stream() call fails on a
+ * system with a free-running sensor.
+ */
+ INIT_LIST_HEAD(&video->dmaqueue);
+ video->queue = NULL;
+ }
+
+ if (!ret)
+ video->streaming = 1;
+
+ mutex_unlock(&video->stream_lock);
+ return ret;
+}
+
+static int
+isp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
+{
+ struct isp_video_fh *vfh = to_isp_video_fh(fh);
+ struct isp_video *video = video_drvdata(file);
+ struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity);
+ enum isp_pipeline_state state;
+ unsigned int streaming;
+ unsigned long flags;
+
+ if (type != video->type)
+ return -EINVAL;
+
+ mutex_lock(&video->stream_lock);
+
+ /* Make sure we're not streaming yet. */
+ mutex_lock(&vfh->queue.lock);
+ streaming = vfh->queue.streaming;
+ mutex_unlock(&vfh->queue.lock);
+
+ if (!streaming)
+ goto done;
+
+ /* Update the pipeline state. */
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ state = ISP_PIPELINE_STREAM_OUTPUT
+ | ISP_PIPELINE_QUEUE_OUTPUT;
+ else
+ state = ISP_PIPELINE_STREAM_INPUT
+ | ISP_PIPELINE_QUEUE_INPUT;
+
+ spin_lock_irqsave(&pipe->lock, flags);
+ pipe->state &= ~state;
+ spin_unlock_irqrestore(&pipe->lock, flags);
+
+ /* Stop the stream. */
+ omap3isp_pipeline_set_stream(pipe, ISP_PIPELINE_STREAM_STOPPED);
+ omap3isp_video_queue_streamoff(&vfh->queue);
+ video->queue = NULL;
+ video->streaming = 0;
+
+ if (video->isp->pdata->set_constraints)
+ video->isp->pdata->set_constraints(video->isp, false);
+ media_entity_pipeline_stop(&video->video.entity);
+
+done:
+ mutex_unlock(&video->stream_lock);
+ return 0;
+}
+
+static int
+isp_video_enum_input(struct file *file, void *fh, struct v4l2_input *input)
+{
+ if (input->index > 0)
+ return -EINVAL;
+
+ strlcpy(input->name, "camera", sizeof(input->name));
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+
+ return 0;
+}
+
+static int
+isp_video_g_input(struct file *file, void *fh, unsigned int *input)
+{
+ *input = 0;
+
+ return 0;
+}
+
+static int
+isp_video_s_input(struct file *file, void *fh, unsigned int input)
+{
+ return input == 0 ? 0 : -EINVAL;
+}
+
+static const struct v4l2_ioctl_ops isp_video_ioctl_ops = {
+ .vidioc_querycap = isp_video_querycap,
+ .vidioc_g_fmt_vid_cap = isp_video_get_format,
+ .vidioc_s_fmt_vid_cap = isp_video_set_format,
+ .vidioc_try_fmt_vid_cap = isp_video_try_format,
+ .vidioc_g_fmt_vid_out = isp_video_get_format,
+ .vidioc_s_fmt_vid_out = isp_video_set_format,
+ .vidioc_try_fmt_vid_out = isp_video_try_format,
+ .vidioc_cropcap = isp_video_cropcap,
+ .vidioc_g_crop = isp_video_get_crop,
+ .vidioc_s_crop = isp_video_set_crop,
+ .vidioc_g_parm = isp_video_get_param,
+ .vidioc_s_parm = isp_video_set_param,
+ .vidioc_reqbufs = isp_video_reqbufs,
+ .vidioc_querybuf = isp_video_querybuf,
+ .vidioc_qbuf = isp_video_qbuf,
+ .vidioc_dqbuf = isp_video_dqbuf,
+ .vidioc_streamon = isp_video_streamon,
+ .vidioc_streamoff = isp_video_streamoff,
+ .vidioc_enum_input = isp_video_enum_input,
+ .vidioc_g_input = isp_video_g_input,
+ .vidioc_s_input = isp_video_s_input,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 file operations
+ */
+
+static int isp_video_open(struct file *file)
+{
+ struct isp_video *video = video_drvdata(file);
+ struct isp_video_fh *handle;
+ int ret = 0;
+
+ handle = kzalloc(sizeof(*handle), GFP_KERNEL);
+ if (handle == NULL)
+ return -ENOMEM;
+
+ v4l2_fh_init(&handle->vfh, &video->video);
+ v4l2_fh_add(&handle->vfh);
+
+ /* If this is the first user, initialise the pipeline. */
+ if (omap3isp_get(video->isp) == NULL) {
+ ret = -EBUSY;
+ goto done;
+ }
+
+ ret = omap3isp_pipeline_pm_use(&video->video.entity, 1);
+ if (ret < 0) {
+ omap3isp_put(video->isp);
+ goto done;
+ }
+
+ omap3isp_video_queue_init(&handle->queue, video->type,
+ &isp_video_queue_ops, video->isp->dev,
+ sizeof(struct isp_buffer));
+
+ memset(&handle->format, 0, sizeof(handle->format));
+ handle->format.type = video->type;
+ handle->timeperframe.denominator = 1;
+
+ handle->video = video;
+ file->private_data = &handle->vfh;
+
+done:
+ if (ret < 0) {
+ v4l2_fh_del(&handle->vfh);
+ kfree(handle);
+ }
+
+ return ret;
+}
+
+static int isp_video_release(struct file *file)
+{
+ struct isp_video *video = video_drvdata(file);
+ struct v4l2_fh *vfh = file->private_data;
+ struct isp_video_fh *handle = to_isp_video_fh(vfh);
+
+ /* Disable streaming and free the buffers queue resources. */
+ isp_video_streamoff(file, vfh, video->type);
+
+ mutex_lock(&handle->queue.lock);
+ omap3isp_video_queue_cleanup(&handle->queue);
+ mutex_unlock(&handle->queue.lock);
+
+ omap3isp_pipeline_pm_use(&video->video.entity, 0);
+
+ /* Release the file handle. */
+ v4l2_fh_del(vfh);
+ kfree(handle);
+ file->private_data = NULL;
+
+ omap3isp_put(video->isp);
+
+ return 0;
+}
+
+static unsigned int isp_video_poll(struct file *file, poll_table *wait)
+{
+ struct isp_video_fh *vfh = to_isp_video_fh(file->private_data);
+ struct isp_video_queue *queue = &vfh->queue;
+
+ return omap3isp_video_queue_poll(queue, file, wait);
+}
+
+static int isp_video_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct isp_video_fh *vfh = to_isp_video_fh(file->private_data);
+
+ return omap3isp_video_queue_mmap(&vfh->queue, vma);
+}
+
+static struct v4l2_file_operations isp_video_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = video_ioctl2,
+ .open = isp_video_open,
+ .release = isp_video_release,
+ .poll = isp_video_poll,
+ .mmap = isp_video_mmap,
+};
+
+/* -----------------------------------------------------------------------------
+ * ISP video core
+ */
+
+static const struct isp_video_operations isp_video_dummy_ops = {
+};
+
+int omap3isp_video_init(struct isp_video *video, const char *name)
+{
+ const char *direction;
+ int ret;
+
+ switch (video->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ direction = "output";
+ video->pad.flags = MEDIA_PAD_FL_SINK;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ direction = "input";
+ video->pad.flags = MEDIA_PAD_FL_SOURCE;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ ret = media_entity_init(&video->video.entity, 1, &video->pad, 0);
+ if (ret < 0)
+ return ret;
+
+ mutex_init(&video->mutex);
+ atomic_set(&video->active, 0);
+
+ spin_lock_init(&video->pipe.lock);
+ mutex_init(&video->stream_lock);
+
+ /* Initialize the video device. */
+ if (video->ops == NULL)
+ video->ops = &isp_video_dummy_ops;
+
+ video->video.fops = &isp_video_fops;
+ snprintf(video->video.name, sizeof(video->video.name),
+ "OMAP3 ISP %s %s", name, direction);
+ video->video.vfl_type = VFL_TYPE_GRABBER;
+ video->video.release = video_device_release_empty;
+ video->video.ioctl_ops = &isp_video_ioctl_ops;
+ video->pipe.stream_state = ISP_PIPELINE_STREAM_STOPPED;
+
+ video_set_drvdata(&video->video, video);
+
+ return 0;
+}
+
+void omap3isp_video_cleanup(struct isp_video *video)
+{
+ media_entity_cleanup(&video->video.entity);
+ mutex_destroy(&video->stream_lock);
+ mutex_destroy(&video->mutex);
+}
+
+int omap3isp_video_register(struct isp_video *video, struct v4l2_device *vdev)
+{
+ int ret;
+
+ video->video.v4l2_dev = vdev;
+
+ ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1);
+ if (ret < 0)
+ printk(KERN_ERR "%s: could not register video device (%d)\n",
+ __func__, ret);
+
+ return ret;
+}
+
+void omap3isp_video_unregister(struct isp_video *video)
+{
+ if (video_is_registered(&video->video))
+ video_unregister_device(&video->video);
+}
diff --git a/drivers/media/video/omap3isp/ispvideo.h b/drivers/media/video/omap3isp/ispvideo.h
new file mode 100644
index 00000000..d91bdb91
--- /dev/null
+++ b/drivers/media/video/omap3isp/ispvideo.h
@@ -0,0 +1,209 @@
+/*
+ * ispvideo.h
+ *
+ * TI OMAP3 ISP - Generic video node
+ *
+ * Copyright (C) 2009-2010 Nokia Corporation
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP3_ISP_VIDEO_H
+#define OMAP3_ISP_VIDEO_H
+
+#include <linux/v4l2-mediabus.h>
+#include <media/media-entity.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+
+#include "ispqueue.h"
+
+#define ISP_VIDEO_DRIVER_NAME "ispvideo"
+#define ISP_VIDEO_DRIVER_VERSION "0.0.2"
+
+struct isp_device;
+struct isp_video;
+struct v4l2_mbus_framefmt;
+struct v4l2_pix_format;
+
+/*
+ * struct isp_format_info - ISP media bus format information
+ * @code: V4L2 media bus format code
+ * @truncated: V4L2 media bus format code for the same format truncated to 10
+ * bits. Identical to @code if the format is 10 bits wide or less.
+ * @uncompressed: V4L2 media bus format code for the corresponding uncompressed
+ * format. Identical to @code if the format is not DPCM compressed.
+ * @flavor: V4L2 media bus format code for the same pixel layout but
+ * shifted to be 8 bits per pixel. =0 if format is not shiftable.
+ * @pixelformat: V4L2 pixel format FCC identifier
+ * @bpp: Bits per pixel
+ */
+struct isp_format_info {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_mbus_pixelcode truncated;
+ enum v4l2_mbus_pixelcode uncompressed;
+ enum v4l2_mbus_pixelcode flavor;
+ u32 pixelformat;
+ unsigned int bpp;
+};
+
+enum isp_pipeline_stream_state {
+ ISP_PIPELINE_STREAM_STOPPED = 0,
+ ISP_PIPELINE_STREAM_CONTINUOUS = 1,
+ ISP_PIPELINE_STREAM_SINGLESHOT = 2,
+};
+
+enum isp_pipeline_state {
+ /* The stream has been started on the input video node. */
+ ISP_PIPELINE_STREAM_INPUT = 1,
+ /* The stream has been started on the output video node. */
+ ISP_PIPELINE_STREAM_OUTPUT = 2,
+ /* At least one buffer is queued on the input video node. */
+ ISP_PIPELINE_QUEUE_INPUT = 4,
+ /* At least one buffer is queued on the output video node. */
+ ISP_PIPELINE_QUEUE_OUTPUT = 8,
+ /* The input entity is idle, ready to be started. */
+ ISP_PIPELINE_IDLE_INPUT = 16,
+ /* The output entity is idle, ready to be started. */
+ ISP_PIPELINE_IDLE_OUTPUT = 32,
+ /* The pipeline is currently streaming. */
+ ISP_PIPELINE_STREAM = 64,
+};
+
+/*
+ * struct isp_pipeline - An ISP hardware pipeline
+ * @error: A hardware error occurred during capture
+ */
+struct isp_pipeline {
+ struct media_pipeline pipe;
+ spinlock_t lock; /* Pipeline state and queue flags */
+ unsigned int state;
+ enum isp_pipeline_stream_state stream_state;
+ struct isp_video *input;
+ struct isp_video *output;
+ unsigned long l3_ick;
+ unsigned int max_rate;
+ atomic_t frame_number;
+ bool do_propagation; /* of frame number */
+ bool error;
+ struct v4l2_fract max_timeperframe;
+};
+
+#define to_isp_pipeline(__e) \
+ container_of((__e)->pipe, struct isp_pipeline, pipe)
+
+static inline int isp_pipeline_ready(struct isp_pipeline *pipe)
+{
+ return pipe->state == (ISP_PIPELINE_STREAM_INPUT |
+ ISP_PIPELINE_STREAM_OUTPUT |
+ ISP_PIPELINE_QUEUE_INPUT |
+ ISP_PIPELINE_QUEUE_OUTPUT |
+ ISP_PIPELINE_IDLE_INPUT |
+ ISP_PIPELINE_IDLE_OUTPUT);
+}
+
+/*
+ * struct isp_buffer - ISP buffer
+ * @buffer: ISP video buffer
+ * @isp_addr: MMU mapped address (a.k.a. device address) of the buffer.
+ */
+struct isp_buffer {
+ struct isp_video_buffer buffer;
+ dma_addr_t isp_addr;
+};
+
+#define to_isp_buffer(buf) container_of(buf, struct isp_buffer, buffer)
+
+enum isp_video_dmaqueue_flags {
+ /* Set if DMA queue becomes empty when ISP_PIPELINE_STREAM_CONTINUOUS */
+ ISP_VIDEO_DMAQUEUE_UNDERRUN = (1 << 0),
+ /* Set when queuing buffer to an empty DMA queue */
+ ISP_VIDEO_DMAQUEUE_QUEUED = (1 << 1),
+};
+
+#define isp_video_dmaqueue_flags_clr(video) \
+ ({ (video)->dmaqueue_flags = 0; })
+
+/*
+ * struct isp_video_operations - ISP video operations
+ * @queue: Resume streaming when a buffer is queued. Called on VIDIOC_QBUF
+ * if there was no buffer previously queued.
+ */
+struct isp_video_operations {
+ int(*queue)(struct isp_video *video, struct isp_buffer *buffer);
+};
+
+struct isp_video {
+ struct video_device video;
+ enum v4l2_buf_type type;
+ struct media_pad pad;
+
+ struct mutex mutex; /* format and crop settings */
+ atomic_t active;
+
+ struct isp_device *isp;
+
+ unsigned int capture_mem;
+ unsigned int bpl_alignment; /* alignment value */
+ unsigned int bpl_zero_padding; /* whether the alignment is optional */
+ unsigned int bpl_max; /* maximum bytes per line value */
+ unsigned int bpl_value; /* bytes per line value */
+ unsigned int bpl_padding; /* padding at end of line */
+
+ /* Entity video node streaming */
+ unsigned int streaming:1;
+
+ /* Pipeline state */
+ struct isp_pipeline pipe;
+ struct mutex stream_lock; /* pipeline and stream states */
+
+ /* Video buffers queue */
+ struct isp_video_queue *queue;
+ struct list_head dmaqueue;
+ enum isp_video_dmaqueue_flags dmaqueue_flags;
+
+ const struct isp_video_operations *ops;
+};
+
+#define to_isp_video(vdev) container_of(vdev, struct isp_video, video)
+
+struct isp_video_fh {
+ struct v4l2_fh vfh;
+ struct isp_video *video;
+ struct isp_video_queue queue;
+ struct v4l2_format format;
+ struct v4l2_fract timeperframe;
+};
+
+#define to_isp_video_fh(fh) container_of(fh, struct isp_video_fh, vfh)
+#define isp_video_queue_to_isp_video_fh(q) \
+ container_of(q, struct isp_video_fh, queue)
+
+int omap3isp_video_init(struct isp_video *video, const char *name);
+void omap3isp_video_cleanup(struct isp_video *video);
+int omap3isp_video_register(struct isp_video *video,
+ struct v4l2_device *vdev);
+void omap3isp_video_unregister(struct isp_video *video);
+struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video);
+void omap3isp_video_resume(struct isp_video *video, int continuous);
+struct media_pad *omap3isp_video_remote_pad(struct isp_video *video);
+
+const struct isp_format_info *
+omap3isp_video_format_info(enum v4l2_mbus_pixelcode code);
+
+#endif /* OMAP3_ISP_VIDEO_H */
diff --git a/drivers/media/video/omap3isp/luma_enhance_table.h b/drivers/media/video/omap3isp/luma_enhance_table.h
new file mode 100644
index 00000000..098b45e2
--- /dev/null
+++ b/drivers/media/video/omap3isp/luma_enhance_table.h
@@ -0,0 +1,42 @@
+/*
+ * luma_enhance_table.h
+ *
+ * TI OMAP3 ISP - Luminance enhancement table
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+1047552, 1047552, 1047552, 1047552, 1047552, 1047552, 1047552, 1047552,
+1047552, 1047552, 1047552, 1047552, 1047552, 1047552, 1047552, 1047552,
+1047552, 1047552, 1047552, 1047552, 1047552, 1047552, 1047552, 1047552,
+1047552, 1047552, 1047552, 1047552, 1048575, 1047551, 1046527, 1045503,
+1044479, 1043455, 1042431, 1041407, 1040383, 1039359, 1038335, 1037311,
+1036287, 1035263, 1034239, 1033215, 1032191, 1031167, 1030143, 1028096,
+1028096, 1028096, 1028096, 1028096, 1028096, 1028096, 1028096, 1028096,
+1028096, 1028100, 1032196, 1036292, 1040388, 1044484, 0, 0,
+ 0, 5, 5125, 10245, 15365, 20485, 25605, 30720,
+ 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720,
+ 30720, 30720, 31743, 30719, 29695, 28671, 27647, 26623,
+ 25599, 24575, 23551, 22527, 21503, 20479, 19455, 18431,
+ 17407, 16383, 15359, 14335, 13311, 12287, 11263, 10239,
+ 9215, 8191, 7167, 6143, 5119, 4095, 3071, 1024,
+ 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024,
+ 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024
diff --git a/drivers/media/video/omap3isp/noise_filter_table.h b/drivers/media/video/omap3isp/noise_filter_table.h
new file mode 100644
index 00000000..d50451a4
--- /dev/null
+++ b/drivers/media/video/omap3isp/noise_filter_table.h
@@ -0,0 +1,30 @@
+/*
+ * noise_filter_table.h
+ *
+ * TI OMAP3 ISP - Noise filter table
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
+31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31
diff --git a/drivers/media/video/ov2640.c b/drivers/media/video/ov2640.c
new file mode 100644
index 00000000..3c2c5d3b
--- /dev/null
+++ b/drivers/media/video/ov2640.c
@@ -0,0 +1,1110 @@
+/*
+ * ov2640 Camera Driver
+ *
+ * Copyright (C) 2010 Alberto Panizzo <maramaopercheseimorto@gmail.com>
+ *
+ * Based on ov772x, ov9640 drivers and previous non merged implementations.
+ *
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2006, OmniVision
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/videodev2.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-ctrls.h>
+
+#define VAL_SET(x, mask, rshift, lshift) \
+ ((((x) >> rshift) & mask) << lshift)
+/*
+ * DSP registers
+ * register offset for BANK_SEL == BANK_SEL_DSP
+ */
+#define R_BYPASS 0x05 /* Bypass DSP */
+#define R_BYPASS_DSP_BYPAS 0x01 /* Bypass DSP, sensor out directly */
+#define R_BYPASS_USE_DSP 0x00 /* Use the internal DSP */
+#define QS 0x44 /* Quantization Scale Factor */
+#define CTRLI 0x50
+#define CTRLI_LP_DP 0x80
+#define CTRLI_ROUND 0x40
+#define CTRLI_V_DIV_SET(x) VAL_SET(x, 0x3, 0, 3)
+#define CTRLI_H_DIV_SET(x) VAL_SET(x, 0x3, 0, 0)
+#define HSIZE 0x51 /* H_SIZE[7:0] (real/4) */
+#define HSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0)
+#define VSIZE 0x52 /* V_SIZE[7:0] (real/4) */
+#define VSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0)
+#define XOFFL 0x53 /* OFFSET_X[7:0] */
+#define XOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0)
+#define YOFFL 0x54 /* OFFSET_Y[7:0] */
+#define YOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0)
+#define VHYX 0x55 /* Offset and size completion */
+#define VHYX_VSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 7)
+#define VHYX_HSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 3)
+#define VHYX_YOFF_SET(x) VAL_SET(x, 0x3, 8, 4)
+#define VHYX_XOFF_SET(x) VAL_SET(x, 0x3, 8, 0)
+#define DPRP 0x56
+#define TEST 0x57 /* Horizontal size completion */
+#define TEST_HSIZE_SET(x) VAL_SET(x, 0x1, (9+2), 7)
+#define ZMOW 0x5A /* Zoom: Out Width OUTW[7:0] (real/4) */
+#define ZMOW_OUTW_SET(x) VAL_SET(x, 0xFF, 2, 0)
+#define ZMOH 0x5B /* Zoom: Out Height OUTH[7:0] (real/4) */
+#define ZMOH_OUTH_SET(x) VAL_SET(x, 0xFF, 2, 0)
+#define ZMHH 0x5C /* Zoom: Speed and H&W completion */
+#define ZMHH_ZSPEED_SET(x) VAL_SET(x, 0x0F, 0, 4)
+#define ZMHH_OUTH_SET(x) VAL_SET(x, 0x1, (8+2), 2)
+#define ZMHH_OUTW_SET(x) VAL_SET(x, 0x3, (8+2), 0)
+#define BPADDR 0x7C /* SDE Indirect Register Access: Address */
+#define BPDATA 0x7D /* SDE Indirect Register Access: Data */
+#define CTRL2 0x86 /* DSP Module enable 2 */
+#define CTRL2_DCW_EN 0x20
+#define CTRL2_SDE_EN 0x10
+#define CTRL2_UV_ADJ_EN 0x08
+#define CTRL2_UV_AVG_EN 0x04
+#define CTRL2_CMX_EN 0x01
+#define CTRL3 0x87 /* DSP Module enable 3 */
+#define CTRL3_BPC_EN 0x80
+#define CTRL3_WPC_EN 0x40
+#define SIZEL 0x8C /* Image Size Completion */
+#define SIZEL_HSIZE8_11_SET(x) VAL_SET(x, 0x1, 11, 6)
+#define SIZEL_HSIZE8_SET(x) VAL_SET(x, 0x7, 0, 3)
+#define SIZEL_VSIZE8_SET(x) VAL_SET(x, 0x7, 0, 0)
+#define HSIZE8 0xC0 /* Image Horizontal Size HSIZE[10:3] */
+#define HSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0)
+#define VSIZE8 0xC1 /* Image Vertical Size VSIZE[10:3] */
+#define VSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0)
+#define CTRL0 0xC2 /* DSP Module enable 0 */
+#define CTRL0_AEC_EN 0x80
+#define CTRL0_AEC_SEL 0x40
+#define CTRL0_STAT_SEL 0x20
+#define CTRL0_VFIRST 0x10
+#define CTRL0_YUV422 0x08
+#define CTRL0_YUV_EN 0x04
+#define CTRL0_RGB_EN 0x02
+#define CTRL0_RAW_EN 0x01
+#define CTRL1 0xC3 /* DSP Module enable 1 */
+#define CTRL1_CIP 0x80
+#define CTRL1_DMY 0x40
+#define CTRL1_RAW_GMA 0x20
+#define CTRL1_DG 0x10
+#define CTRL1_AWB 0x08
+#define CTRL1_AWB_GAIN 0x04
+#define CTRL1_LENC 0x02
+#define CTRL1_PRE 0x01
+#define R_DVP_SP 0xD3 /* DVP output speed control */
+#define R_DVP_SP_AUTO_MODE 0x80
+#define R_DVP_SP_DVP_MASK 0x3F /* DVP PCLK = sysclk (48)/[6:0] (YUV0);
+ * = sysclk (48)/(2*[6:0]) (RAW);*/
+#define IMAGE_MODE 0xDA /* Image Output Format Select */
+#define IMAGE_MODE_Y8_DVP_EN 0x40
+#define IMAGE_MODE_JPEG_EN 0x10
+#define IMAGE_MODE_YUV422 0x00
+#define IMAGE_MODE_RAW10 0x04 /* (DVP) */
+#define IMAGE_MODE_RGB565 0x08
+#define IMAGE_MODE_HREF_VSYNC 0x02 /* HREF timing select in DVP JPEG output
+ * mode (0 for HREF is same as sensor) */
+#define IMAGE_MODE_LBYTE_FIRST 0x01 /* Byte swap enable for DVP
+ * 1: Low byte first UYVY (C2[4] =0)
+ * VYUY (C2[4] =1)
+ * 0: High byte first YUYV (C2[4]=0)
+ * YVYU (C2[4] = 1) */
+#define RESET 0xE0 /* Reset */
+#define RESET_MICROC 0x40
+#define RESET_SCCB 0x20
+#define RESET_JPEG 0x10
+#define RESET_DVP 0x04
+#define RESET_IPU 0x02
+#define RESET_CIF 0x01
+#define REGED 0xED /* Register ED */
+#define REGED_CLK_OUT_DIS 0x10
+#define MS_SP 0xF0 /* SCCB Master Speed */
+#define SS_ID 0xF7 /* SCCB Slave ID */
+#define SS_CTRL 0xF8 /* SCCB Slave Control */
+#define SS_CTRL_ADD_AUTO_INC 0x20
+#define SS_CTRL_EN 0x08
+#define SS_CTRL_DELAY_CLK 0x04
+#define SS_CTRL_ACC_EN 0x02
+#define SS_CTRL_SEN_PASS_THR 0x01
+#define MC_BIST 0xF9 /* Microcontroller misc register */
+#define MC_BIST_RESET 0x80 /* Microcontroller Reset */
+#define MC_BIST_BOOT_ROM_SEL 0x40
+#define MC_BIST_12KB_SEL 0x20
+#define MC_BIST_12KB_MASK 0x30
+#define MC_BIST_512KB_SEL 0x08
+#define MC_BIST_512KB_MASK 0x0C
+#define MC_BIST_BUSY_BIT_R 0x02
+#define MC_BIST_MC_RES_ONE_SH_W 0x02
+#define MC_BIST_LAUNCH 0x01
+#define BANK_SEL 0xFF /* Register Bank Select */
+#define BANK_SEL_DSP 0x00
+#define BANK_SEL_SENS 0x01
+
+/*
+ * Sensor registers
+ * register offset for BANK_SEL == BANK_SEL_SENS
+ */
+#define GAIN 0x00 /* AGC - Gain control gain setting */
+#define COM1 0x03 /* Common control 1 */
+#define COM1_1_DUMMY_FR 0x40
+#define COM1_3_DUMMY_FR 0x80
+#define COM1_7_DUMMY_FR 0xC0
+#define COM1_VWIN_LSB_UXGA 0x0F
+#define COM1_VWIN_LSB_SVGA 0x0A
+#define COM1_VWIN_LSB_CIF 0x06
+#define REG04 0x04 /* Register 04 */
+#define REG04_DEF 0x20 /* Always set */
+#define REG04_HFLIP_IMG 0x80 /* Horizontal mirror image ON/OFF */
+#define REG04_VFLIP_IMG 0x40 /* Vertical flip image ON/OFF */
+#define REG04_VREF_EN 0x10
+#define REG04_HREF_EN 0x08
+#define REG04_AEC_SET(x) VAL_SET(x, 0x3, 0, 0)
+#define REG08 0x08 /* Frame Exposure One-pin Control Pre-charge Row Num */
+#define COM2 0x09 /* Common control 2 */
+#define COM2_SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */
+ /* Output drive capability */
+#define COM2_OCAP_Nx_SET(N) (((N) - 1) & 0x03) /* N = [1x .. 4x] */
+#define PID 0x0A /* Product ID Number MSB */
+#define VER 0x0B /* Product ID Number LSB */
+#define COM3 0x0C /* Common control 3 */
+#define COM3_BAND_50H 0x04 /* 0 For Banding at 60H */
+#define COM3_BAND_AUTO 0x02 /* Auto Banding */
+#define COM3_SING_FR_SNAPSH 0x01 /* 0 For enable live video output after the
+ * snapshot sequence*/
+#define AEC 0x10 /* AEC[9:2] Exposure Value */
+#define CLKRC 0x11 /* Internal clock */
+#define CLKRC_EN 0x80
+#define CLKRC_DIV_SET(x) (((x) - 1) & 0x1F) /* CLK = XVCLK/(x) */
+#define COM7 0x12 /* Common control 7 */
+#define COM7_SRST 0x80 /* Initiates system reset. All registers are
+ * set to factory default values after which
+ * the chip resumes normal operation */
+#define COM7_RES_UXGA 0x00 /* Resolution selectors for UXGA */
+#define COM7_RES_SVGA 0x40 /* SVGA */
+#define COM7_RES_CIF 0x20 /* CIF */
+#define COM7_ZOOM_EN 0x04 /* Enable Zoom mode */
+#define COM7_COLOR_BAR_TEST 0x02 /* Enable Color Bar Test Pattern */
+#define COM8 0x13 /* Common control 8 */
+#define COM8_DEF 0xC0 /* Banding filter ON/OFF */
+#define COM8_BNDF_EN 0x20 /* Banding filter ON/OFF */
+#define COM8_AGC_EN 0x04 /* AGC Auto/Manual control selection */
+#define COM8_AEC_EN 0x01 /* Auto/Manual Exposure control */
+#define COM9 0x14 /* Common control 9
+ * Automatic gain ceiling - maximum AGC value [7:5]*/
+#define COM9_AGC_GAIN_2x 0x00 /* 000 : 2x */
+#define COM9_AGC_GAIN_4x 0x20 /* 001 : 4x */
+#define COM9_AGC_GAIN_8x 0x40 /* 010 : 8x */
+#define COM9_AGC_GAIN_16x 0x60 /* 011 : 16x */
+#define COM9_AGC_GAIN_32x 0x80 /* 100 : 32x */
+#define COM9_AGC_GAIN_64x 0xA0 /* 101 : 64x */
+#define COM9_AGC_GAIN_128x 0xC0 /* 110 : 128x */
+#define COM10 0x15 /* Common control 10 */
+#define COM10_PCLK_HREF 0x20 /* PCLK output qualified by HREF */
+#define COM10_PCLK_RISE 0x10 /* Data is updated at the rising edge of
+ * PCLK (user can latch data at the next
+ * falling edge of PCLK).
+ * 0 otherwise. */
+#define COM10_HREF_INV 0x08 /* Invert HREF polarity:
+ * HREF negative for valid data*/
+#define COM10_VSINC_INV 0x02 /* Invert VSYNC polarity */
+#define HSTART 0x17 /* Horizontal Window start MSB 8 bit */
+#define HEND 0x18 /* Horizontal Window end MSB 8 bit */
+#define VSTART 0x19 /* Vertical Window start MSB 8 bit */
+#define VEND 0x1A /* Vertical Window end MSB 8 bit */
+#define MIDH 0x1C /* Manufacturer ID byte - high */
+#define MIDL 0x1D /* Manufacturer ID byte - low */
+#define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */
+#define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */
+#define VV 0x26 /* AGC/AEC Fast mode operating region */
+#define VV_HIGH_TH_SET(x) VAL_SET(x, 0xF, 0, 4)
+#define VV_LOW_TH_SET(x) VAL_SET(x, 0xF, 0, 0)
+#define REG2A 0x2A /* Dummy pixel insert MSB */
+#define FRARL 0x2B /* Dummy pixel insert LSB */
+#define ADDVFL 0x2D /* LSB of insert dummy lines in Vertical direction */
+#define ADDVFH 0x2E /* MSB of insert dummy lines in Vertical direction */
+#define YAVG 0x2F /* Y/G Channel Average value */
+#define REG32 0x32 /* Common Control 32 */
+#define REG32_PCLK_DIV_2 0x80 /* PCLK freq divided by 2 */
+#define REG32_PCLK_DIV_4 0xC0 /* PCLK freq divided by 4 */
+#define ARCOM2 0x34 /* Zoom: Horizontal start point */
+#define REG45 0x45 /* Register 45 */
+#define FLL 0x46 /* Frame Length Adjustment LSBs */
+#define FLH 0x47 /* Frame Length Adjustment MSBs */
+#define COM19 0x48 /* Zoom: Vertical start point */
+#define ZOOMS 0x49 /* Zoom: Vertical start point */
+#define COM22 0x4B /* Flash light control */
+#define COM25 0x4E /* For Banding operations */
+#define BD50 0x4F /* 50Hz Banding AEC 8 LSBs */
+#define BD60 0x50 /* 60Hz Banding AEC 8 LSBs */
+#define REG5D 0x5D /* AVGsel[7:0], 16-zone average weight option */
+#define REG5E 0x5E /* AVGsel[15:8], 16-zone average weight option */
+#define REG5F 0x5F /* AVGsel[23:16], 16-zone average weight option */
+#define REG60 0x60 /* AVGsel[31:24], 16-zone average weight option */
+#define HISTO_LOW 0x61 /* Histogram Algorithm Low Level */
+#define HISTO_HIGH 0x62 /* Histogram Algorithm High Level */
+
+/*
+ * ID
+ */
+#define MANUFACTURER_ID 0x7FA2
+#define PID_OV2640 0x2642
+#define VERSION(pid, ver) ((pid << 8) | (ver & 0xFF))
+
+/*
+ * Struct
+ */
+struct regval_list {
+ u8 reg_num;
+ u8 value;
+};
+
+/* Supported resolutions */
+enum ov2640_width {
+ W_QCIF = 176,
+ W_QVGA = 320,
+ W_CIF = 352,
+ W_VGA = 640,
+ W_SVGA = 800,
+ W_XGA = 1024,
+ W_SXGA = 1280,
+ W_UXGA = 1600,
+};
+
+enum ov2640_height {
+ H_QCIF = 144,
+ H_QVGA = 240,
+ H_CIF = 288,
+ H_VGA = 480,
+ H_SVGA = 600,
+ H_XGA = 768,
+ H_SXGA = 1024,
+ H_UXGA = 1200,
+};
+
+struct ov2640_win_size {
+ char *name;
+ enum ov2640_width width;
+ enum ov2640_height height;
+ const struct regval_list *regs;
+};
+
+
+struct ov2640_priv {
+ struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
+ enum v4l2_mbus_pixelcode cfmt_code;
+ const struct ov2640_win_size *win;
+ int model;
+};
+
+/*
+ * Registers settings
+ */
+
+#define ENDMARKER { 0xff, 0xff }
+
+static const struct regval_list ov2640_init_regs[] = {
+ { BANK_SEL, BANK_SEL_DSP },
+ { 0x2c, 0xff },
+ { 0x2e, 0xdf },
+ { BANK_SEL, BANK_SEL_SENS },
+ { 0x3c, 0x32 },
+ { CLKRC, CLKRC_DIV_SET(1) },
+ { COM2, COM2_OCAP_Nx_SET(3) },
+ { REG04, REG04_DEF | REG04_HREF_EN },
+ { COM8, COM8_DEF | COM8_BNDF_EN | COM8_AGC_EN | COM8_AEC_EN },
+ { COM9, COM9_AGC_GAIN_8x | 0x08},
+ { 0x2c, 0x0c },
+ { 0x33, 0x78 },
+ { 0x3a, 0x33 },
+ { 0x3b, 0xfb },
+ { 0x3e, 0x00 },
+ { 0x43, 0x11 },
+ { 0x16, 0x10 },
+ { 0x39, 0x02 },
+ { 0x35, 0x88 },
+ { 0x22, 0x0a },
+ { 0x37, 0x40 },
+ { 0x23, 0x00 },
+ { ARCOM2, 0xa0 },
+ { 0x06, 0x02 },
+ { 0x06, 0x88 },
+ { 0x07, 0xc0 },
+ { 0x0d, 0xb7 },
+ { 0x0e, 0x01 },
+ { 0x4c, 0x00 },
+ { 0x4a, 0x81 },
+ { 0x21, 0x99 },
+ { AEW, 0x40 },
+ { AEB, 0x38 },
+ { VV, VV_HIGH_TH_SET(0x08) | VV_LOW_TH_SET(0x02) },
+ { 0x5c, 0x00 },
+ { 0x63, 0x00 },
+ { FLL, 0x22 },
+ { COM3, 0x38 | COM3_BAND_AUTO },
+ { REG5D, 0x55 },
+ { REG5E, 0x7d },
+ { REG5F, 0x7d },
+ { REG60, 0x55 },
+ { HISTO_LOW, 0x70 },
+ { HISTO_HIGH, 0x80 },
+ { 0x7c, 0x05 },
+ { 0x20, 0x80 },
+ { 0x28, 0x30 },
+ { 0x6c, 0x00 },
+ { 0x6d, 0x80 },
+ { 0x6e, 0x00 },
+ { 0x70, 0x02 },
+ { 0x71, 0x94 },
+ { 0x73, 0xc1 },
+ { 0x3d, 0x34 },
+ { COM7, COM7_RES_UXGA | COM7_ZOOM_EN },
+ { 0x5a, 0x57 },
+ { BD50, 0xbb },
+ { BD60, 0x9c },
+ { BANK_SEL, BANK_SEL_DSP },
+ { 0xe5, 0x7f },
+ { MC_BIST, MC_BIST_RESET | MC_BIST_BOOT_ROM_SEL },
+ { 0x41, 0x24 },
+ { RESET, RESET_JPEG | RESET_DVP },
+ { 0x76, 0xff },
+ { 0x33, 0xa0 },
+ { 0x42, 0x20 },
+ { 0x43, 0x18 },
+ { 0x4c, 0x00 },
+ { CTRL3, CTRL3_BPC_EN | CTRL3_WPC_EN | 0x10 },
+ { 0x88, 0x3f },
+ { 0xd7, 0x03 },
+ { 0xd9, 0x10 },
+ { R_DVP_SP , R_DVP_SP_AUTO_MODE | 0x2 },
+ { 0xc8, 0x08 },
+ { 0xc9, 0x80 },
+ { BPADDR, 0x00 },
+ { BPDATA, 0x00 },
+ { BPADDR, 0x03 },
+ { BPDATA, 0x48 },
+ { BPDATA, 0x48 },
+ { BPADDR, 0x08 },
+ { BPDATA, 0x20 },
+ { BPDATA, 0x10 },
+ { BPDATA, 0x0e },
+ { 0x90, 0x00 },
+ { 0x91, 0x0e },
+ { 0x91, 0x1a },
+ { 0x91, 0x31 },
+ { 0x91, 0x5a },
+ { 0x91, 0x69 },
+ { 0x91, 0x75 },
+ { 0x91, 0x7e },
+ { 0x91, 0x88 },
+ { 0x91, 0x8f },
+ { 0x91, 0x96 },
+ { 0x91, 0xa3 },
+ { 0x91, 0xaf },
+ { 0x91, 0xc4 },
+ { 0x91, 0xd7 },
+ { 0x91, 0xe8 },
+ { 0x91, 0x20 },
+ { 0x92, 0x00 },
+ { 0x93, 0x06 },
+ { 0x93, 0xe3 },
+ { 0x93, 0x03 },
+ { 0x93, 0x03 },
+ { 0x93, 0x00 },
+ { 0x93, 0x02 },
+ { 0x93, 0x00 },
+ { 0x93, 0x00 },
+ { 0x93, 0x00 },
+ { 0x93, 0x00 },
+ { 0x93, 0x00 },
+ { 0x93, 0x00 },
+ { 0x93, 0x00 },
+ { 0x96, 0x00 },
+ { 0x97, 0x08 },
+ { 0x97, 0x19 },
+ { 0x97, 0x02 },
+ { 0x97, 0x0c },
+ { 0x97, 0x24 },
+ { 0x97, 0x30 },
+ { 0x97, 0x28 },
+ { 0x97, 0x26 },
+ { 0x97, 0x02 },
+ { 0x97, 0x98 },
+ { 0x97, 0x80 },
+ { 0x97, 0x00 },
+ { 0x97, 0x00 },
+ { 0xa4, 0x00 },
+ { 0xa8, 0x00 },
+ { 0xc5, 0x11 },
+ { 0xc6, 0x51 },
+ { 0xbf, 0x80 },
+ { 0xc7, 0x10 },
+ { 0xb6, 0x66 },
+ { 0xb8, 0xA5 },
+ { 0xb7, 0x64 },
+ { 0xb9, 0x7C },
+ { 0xb3, 0xaf },
+ { 0xb4, 0x97 },
+ { 0xb5, 0xFF },
+ { 0xb0, 0xC5 },
+ { 0xb1, 0x94 },
+ { 0xb2, 0x0f },
+ { 0xc4, 0x5c },
+ { 0xa6, 0x00 },
+ { 0xa7, 0x20 },
+ { 0xa7, 0xd8 },
+ { 0xa7, 0x1b },
+ { 0xa7, 0x31 },
+ { 0xa7, 0x00 },
+ { 0xa7, 0x18 },
+ { 0xa7, 0x20 },
+ { 0xa7, 0xd8 },
+ { 0xa7, 0x19 },
+ { 0xa7, 0x31 },
+ { 0xa7, 0x00 },
+ { 0xa7, 0x18 },
+ { 0xa7, 0x20 },
+ { 0xa7, 0xd8 },
+ { 0xa7, 0x19 },
+ { 0xa7, 0x31 },
+ { 0xa7, 0x00 },
+ { 0xa7, 0x18 },
+ { 0x7f, 0x00 },
+ { 0xe5, 0x1f },
+ { 0xe1, 0x77 },
+ { 0xdd, 0x7f },
+ { CTRL0, CTRL0_YUV422 | CTRL0_YUV_EN | CTRL0_RGB_EN },
+ ENDMARKER,
+};
+
+/*
+ * Register settings for window size
+ * The preamble, setup the internal DSP to input an UXGA (1600x1200) image.
+ * Then the different zooming configurations will setup the output image size.
+ */
+static const struct regval_list ov2640_size_change_preamble_regs[] = {
+ { BANK_SEL, BANK_SEL_DSP },
+ { RESET, RESET_DVP },
+ { HSIZE8, HSIZE8_SET(W_UXGA) },
+ { VSIZE8, VSIZE8_SET(H_UXGA) },
+ { CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN |
+ CTRL2_UV_AVG_EN | CTRL2_CMX_EN | CTRL2_UV_ADJ_EN },
+ { HSIZE, HSIZE_SET(W_UXGA) },
+ { VSIZE, VSIZE_SET(H_UXGA) },
+ { XOFFL, XOFFL_SET(0) },
+ { YOFFL, YOFFL_SET(0) },
+ { VHYX, VHYX_HSIZE_SET(W_UXGA) | VHYX_VSIZE_SET(H_UXGA) |
+ VHYX_XOFF_SET(0) | VHYX_YOFF_SET(0)},
+ { TEST, TEST_HSIZE_SET(W_UXGA) },
+ ENDMARKER,
+};
+
+#define PER_SIZE_REG_SEQ(x, y, v_div, h_div, pclk_div) \
+ { CTRLI, CTRLI_LP_DP | CTRLI_V_DIV_SET(v_div) | \
+ CTRLI_H_DIV_SET(h_div)}, \
+ { ZMOW, ZMOW_OUTW_SET(x) }, \
+ { ZMOH, ZMOH_OUTH_SET(y) }, \
+ { ZMHH, ZMHH_OUTW_SET(x) | ZMHH_OUTH_SET(y) }, \
+ { R_DVP_SP, pclk_div }, \
+ { RESET, 0x00}
+
+static const struct regval_list ov2640_qcif_regs[] = {
+ PER_SIZE_REG_SEQ(W_QCIF, H_QCIF, 3, 3, 4),
+ ENDMARKER,
+};
+
+static const struct regval_list ov2640_qvga_regs[] = {
+ PER_SIZE_REG_SEQ(W_QVGA, H_QVGA, 2, 2, 4),
+ ENDMARKER,
+};
+
+static const struct regval_list ov2640_cif_regs[] = {
+ PER_SIZE_REG_SEQ(W_CIF, H_CIF, 2, 2, 8),
+ ENDMARKER,
+};
+
+static const struct regval_list ov2640_vga_regs[] = {
+ PER_SIZE_REG_SEQ(W_VGA, H_VGA, 0, 0, 2),
+ ENDMARKER,
+};
+
+static const struct regval_list ov2640_svga_regs[] = {
+ PER_SIZE_REG_SEQ(W_SVGA, H_SVGA, 1, 1, 2),
+ ENDMARKER,
+};
+
+static const struct regval_list ov2640_xga_regs[] = {
+ PER_SIZE_REG_SEQ(W_XGA, H_XGA, 0, 0, 2),
+ { CTRLI, 0x00},
+ ENDMARKER,
+};
+
+static const struct regval_list ov2640_sxga_regs[] = {
+ PER_SIZE_REG_SEQ(W_SXGA, H_SXGA, 0, 0, 2),
+ { CTRLI, 0x00},
+ { R_DVP_SP, 2 | R_DVP_SP_AUTO_MODE },
+ ENDMARKER,
+};
+
+static const struct regval_list ov2640_uxga_regs[] = {
+ PER_SIZE_REG_SEQ(W_UXGA, H_UXGA, 0, 0, 0),
+ { CTRLI, 0x00},
+ { R_DVP_SP, 0 | R_DVP_SP_AUTO_MODE },
+ ENDMARKER,
+};
+
+#define OV2640_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r }
+
+static const struct ov2640_win_size ov2640_supported_win_sizes[] = {
+ OV2640_SIZE("QCIF", W_QCIF, H_QCIF, ov2640_qcif_regs),
+ OV2640_SIZE("QVGA", W_QVGA, H_QVGA, ov2640_qvga_regs),
+ OV2640_SIZE("CIF", W_CIF, H_CIF, ov2640_cif_regs),
+ OV2640_SIZE("VGA", W_VGA, H_VGA, ov2640_vga_regs),
+ OV2640_SIZE("SVGA", W_SVGA, H_SVGA, ov2640_svga_regs),
+ OV2640_SIZE("XGA", W_XGA, H_XGA, ov2640_xga_regs),
+ OV2640_SIZE("SXGA", W_SXGA, H_SXGA, ov2640_sxga_regs),
+ OV2640_SIZE("UXGA", W_UXGA, H_UXGA, ov2640_uxga_regs),
+};
+
+/*
+ * Register settings for pixel formats
+ */
+static const struct regval_list ov2640_format_change_preamble_regs[] = {
+ { BANK_SEL, BANK_SEL_DSP },
+ { R_BYPASS, R_BYPASS_USE_DSP },
+ ENDMARKER,
+};
+
+static const struct regval_list ov2640_yuv422_regs[] = {
+ { IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_YUV422 },
+ { 0xD7, 0x01 },
+ { 0x33, 0xa0 },
+ { 0xe1, 0x67 },
+ { RESET, 0x00 },
+ { R_BYPASS, R_BYPASS_USE_DSP },
+ ENDMARKER,
+};
+
+static const struct regval_list ov2640_rgb565_regs[] = {
+ { IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_RGB565 },
+ { 0xd7, 0x03 },
+ { RESET, 0x00 },
+ { R_BYPASS, R_BYPASS_USE_DSP },
+ ENDMARKER,
+};
+
+static enum v4l2_mbus_pixelcode ov2640_codes[] = {
+ V4L2_MBUS_FMT_UYVY8_2X8,
+ V4L2_MBUS_FMT_RGB565_2X8_LE,
+};
+
+/*
+ * General functions
+ */
+static struct ov2640_priv *to_ov2640(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct ov2640_priv,
+ subdev);
+}
+
+static int ov2640_write_array(struct i2c_client *client,
+ const struct regval_list *vals)
+{
+ int ret;
+
+ while ((vals->reg_num != 0xff) || (vals->value != 0xff)) {
+ ret = i2c_smbus_write_byte_data(client,
+ vals->reg_num, vals->value);
+ dev_vdbg(&client->dev, "array: 0x%02x, 0x%02x",
+ vals->reg_num, vals->value);
+
+ if (ret < 0)
+ return ret;
+ vals++;
+ }
+ return 0;
+}
+
+static int ov2640_mask_set(struct i2c_client *client,
+ u8 reg, u8 mask, u8 set)
+{
+ s32 val = i2c_smbus_read_byte_data(client, reg);
+ if (val < 0)
+ return val;
+
+ val &= ~mask;
+ val |= set & mask;
+
+ dev_vdbg(&client->dev, "masks: 0x%02x, 0x%02x", reg, val);
+
+ return i2c_smbus_write_byte_data(client, reg, val);
+}
+
+static int ov2640_reset(struct i2c_client *client)
+{
+ int ret;
+ const struct regval_list reset_seq[] = {
+ {BANK_SEL, BANK_SEL_SENS},
+ {COM7, COM7_SRST},
+ ENDMARKER,
+ };
+
+ ret = ov2640_write_array(client, reset_seq);
+ if (ret)
+ goto err;
+
+ msleep(5);
+err:
+ dev_dbg(&client->dev, "%s: (ret %d)", __func__, ret);
+ return ret;
+}
+
+/*
+ * soc_camera_ops functions
+ */
+static int ov2640_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ return 0;
+}
+
+static int ov2640_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd =
+ &container_of(ctrl->handler, struct ov2640_priv, hdl)->subdev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 val;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ val = ctrl->val ? REG04_VFLIP_IMG : 0x00;
+ return ov2640_mask_set(client, REG04, REG04_VFLIP_IMG, val);
+ case V4L2_CID_HFLIP:
+ val = ctrl->val ? REG04_HFLIP_IMG : 0x00;
+ return ov2640_mask_set(client, REG04, REG04_HFLIP_IMG, val);
+ }
+
+ return -EINVAL;
+}
+
+static int ov2640_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov2640_priv *priv = to_ov2640(client);
+
+ id->ident = priv->model;
+ id->revision = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov2640_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ reg->size = 1;
+ if (reg->reg > 0xff)
+ return -EINVAL;
+
+ ret = i2c_smbus_read_byte_data(client, reg->reg);
+ if (ret < 0)
+ return ret;
+
+ reg->val = ret;
+
+ return 0;
+}
+
+static int ov2640_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->reg > 0xff ||
+ reg->val > 0xff)
+ return -EINVAL;
+
+ return i2c_smbus_write_byte_data(client, reg->reg, reg->val);
+}
+#endif
+
+/* Select the nearest higher resolution for capture */
+static const struct ov2640_win_size *ov2640_select_win(u32 *width, u32 *height)
+{
+ int i, default_size = ARRAY_SIZE(ov2640_supported_win_sizes) - 1;
+
+ for (i = 0; i < ARRAY_SIZE(ov2640_supported_win_sizes); i++) {
+ if (ov2640_supported_win_sizes[i].width >= *width &&
+ ov2640_supported_win_sizes[i].height >= *height) {
+ *width = ov2640_supported_win_sizes[i].width;
+ *height = ov2640_supported_win_sizes[i].height;
+ return &ov2640_supported_win_sizes[i];
+ }
+ }
+
+ *width = ov2640_supported_win_sizes[default_size].width;
+ *height = ov2640_supported_win_sizes[default_size].height;
+ return &ov2640_supported_win_sizes[default_size];
+}
+
+static int ov2640_set_params(struct i2c_client *client, u32 *width, u32 *height,
+ enum v4l2_mbus_pixelcode code)
+{
+ struct ov2640_priv *priv = to_ov2640(client);
+ const struct regval_list *selected_cfmt_regs;
+ int ret;
+
+ /* select win */
+ priv->win = ov2640_select_win(width, height);
+
+ /* select format */
+ priv->cfmt_code = 0;
+ switch (code) {
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ dev_dbg(&client->dev, "%s: Selected cfmt RGB565", __func__);
+ selected_cfmt_regs = ov2640_rgb565_regs;
+ break;
+ default:
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ dev_dbg(&client->dev, "%s: Selected cfmt YUV422", __func__);
+ selected_cfmt_regs = ov2640_yuv422_regs;
+ }
+
+ /* reset hardware */
+ ov2640_reset(client);
+
+ /* initialize the sensor with default data */
+ dev_dbg(&client->dev, "%s: Init default", __func__);
+ ret = ov2640_write_array(client, ov2640_init_regs);
+ if (ret < 0)
+ goto err;
+
+ /* select preamble */
+ dev_dbg(&client->dev, "%s: Set size to %s", __func__, priv->win->name);
+ ret = ov2640_write_array(client, ov2640_size_change_preamble_regs);
+ if (ret < 0)
+ goto err;
+
+ /* set size win */
+ ret = ov2640_write_array(client, priv->win->regs);
+ if (ret < 0)
+ goto err;
+
+ /* cfmt preamble */
+ dev_dbg(&client->dev, "%s: Set cfmt", __func__);
+ ret = ov2640_write_array(client, ov2640_format_change_preamble_regs);
+ if (ret < 0)
+ goto err;
+
+ /* set cfmt */
+ ret = ov2640_write_array(client, selected_cfmt_regs);
+ if (ret < 0)
+ goto err;
+
+ priv->cfmt_code = code;
+ *width = priv->win->width;
+ *height = priv->win->height;
+
+ return 0;
+
+err:
+ dev_err(&client->dev, "%s: Error %d", __func__, ret);
+ ov2640_reset(client);
+ priv->win = NULL;
+
+ return ret;
+}
+
+static int ov2640_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov2640_priv *priv = to_ov2640(client);
+
+ if (!priv->win) {
+ u32 width = W_SVGA, height = H_SVGA;
+ int ret = ov2640_set_params(client, &width, &height,
+ V4L2_MBUS_FMT_UYVY8_2X8);
+ if (ret < 0)
+ return ret;
+ }
+
+ mf->width = priv->win->width;
+ mf->height = priv->win->height;
+ mf->code = priv->cfmt_code;
+
+ switch (mf->code) {
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
+ break;
+ default:
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ }
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int ov2640_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+
+ switch (mf->code) {
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
+ break;
+ default:
+ mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ }
+
+ ret = ov2640_set_params(client, &mf->width, &mf->height, mf->code);
+
+ return ret;
+}
+
+static int ov2640_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct ov2640_win_size *win;
+
+ /*
+ * select suitable win
+ */
+ win = ov2640_select_win(&mf->width, &mf->height);
+
+ mf->field = V4L2_FIELD_NONE;
+
+ switch (mf->code) {
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
+ break;
+ default:
+ mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ }
+
+ return 0;
+}
+
+static int ov2640_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= ARRAY_SIZE(ov2640_codes))
+ return -EINVAL;
+
+ *code = ov2640_codes[index];
+ return 0;
+}
+
+static int ov2640_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ a->c.left = 0;
+ a->c.top = 0;
+ a->c.width = W_UXGA;
+ a->c.height = H_UXGA;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int ov2640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ a->bounds.width = W_UXGA;
+ a->bounds.height = H_UXGA;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int ov2640_video_probe(struct i2c_client *client)
+{
+ struct ov2640_priv *priv = to_ov2640(client);
+ u8 pid, ver, midh, midl;
+ const char *devname;
+ int ret;
+
+ /*
+ * check and show product ID and manufacturer ID
+ */
+ i2c_smbus_write_byte_data(client, BANK_SEL, BANK_SEL_SENS);
+ pid = i2c_smbus_read_byte_data(client, PID);
+ ver = i2c_smbus_read_byte_data(client, VER);
+ midh = i2c_smbus_read_byte_data(client, MIDH);
+ midl = i2c_smbus_read_byte_data(client, MIDL);
+
+ switch (VERSION(pid, ver)) {
+ case PID_OV2640:
+ devname = "ov2640";
+ priv->model = V4L2_IDENT_OV2640;
+ break;
+ default:
+ dev_err(&client->dev,
+ "Product ID error %x:%x\n", pid, ver);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ dev_info(&client->dev,
+ "%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
+ devname, pid, ver, midh, midl);
+
+ return v4l2_ctrl_handler_setup(&priv->hdl);
+
+err:
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops ov2640_ctrl_ops = {
+ .s_ctrl = ov2640_s_ctrl,
+};
+
+static struct v4l2_subdev_core_ops ov2640_subdev_core_ops = {
+ .g_chip_ident = ov2640_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = ov2640_g_register,
+ .s_register = ov2640_s_register,
+#endif
+};
+
+static int ov2640_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops ov2640_subdev_video_ops = {
+ .s_stream = ov2640_s_stream,
+ .g_mbus_fmt = ov2640_g_fmt,
+ .s_mbus_fmt = ov2640_s_fmt,
+ .try_mbus_fmt = ov2640_try_fmt,
+ .cropcap = ov2640_cropcap,
+ .g_crop = ov2640_g_crop,
+ .enum_mbus_fmt = ov2640_enum_fmt,
+ .g_mbus_config = ov2640_g_mbus_config,
+};
+
+static struct v4l2_subdev_ops ov2640_subdev_ops = {
+ .core = &ov2640_subdev_core_ops,
+ .video = &ov2640_subdev_video_ops,
+};
+
+/*
+ * i2c_driver functions
+ */
+static int ov2640_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct ov2640_priv *priv;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ int ret;
+
+ if (!icl) {
+ dev_err(&adapter->dev,
+ "OV2640: Missing platform_data for driver\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&adapter->dev,
+ "OV2640: I2C-Adapter doesn't support SMBUS\n");
+ return -EIO;
+ }
+
+ priv = kzalloc(sizeof(struct ov2640_priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&adapter->dev,
+ "Failed to allocate memory for private data!\n");
+ return -ENOMEM;
+ }
+
+ v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops);
+ v4l2_ctrl_handler_init(&priv->hdl, 2);
+ v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ priv->subdev.ctrl_handler = &priv->hdl;
+ if (priv->hdl.error) {
+ int err = priv->hdl.error;
+
+ kfree(priv);
+ return err;
+ }
+
+ ret = ov2640_video_probe(client);
+ if (ret) {
+ v4l2_ctrl_handler_free(&priv->hdl);
+ kfree(priv);
+ } else {
+ dev_info(&adapter->dev, "OV2640 Probed\n");
+ }
+
+ return ret;
+}
+
+static int ov2640_remove(struct i2c_client *client)
+{
+ struct ov2640_priv *priv = to_ov2640(client);
+
+ v4l2_device_unregister_subdev(&priv->subdev);
+ v4l2_ctrl_handler_free(&priv->hdl);
+ kfree(priv);
+ return 0;
+}
+
+static const struct i2c_device_id ov2640_id[] = {
+ { "ov2640", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ov2640_id);
+
+static struct i2c_driver ov2640_i2c_driver = {
+ .driver = {
+ .name = "ov2640",
+ },
+ .probe = ov2640_probe,
+ .remove = ov2640_remove,
+ .id_table = ov2640_id,
+};
+
+module_i2c_driver(ov2640_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for Omni Vision 2640 sensor");
+MODULE_AUTHOR("Alberto Panizzo");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/ov5642.c b/drivers/media/video/ov5642.c
new file mode 100644
index 00000000..80e07794
--- /dev/null
+++ b/drivers/media/video/ov5642.c
@@ -0,0 +1,1075 @@
+/*
+ * Driver for OV5642 CMOS Image Sensor from Omnivision
+ *
+ * Copyright (C) 2011, Bastian Hecht <hechtb@gmail.com>
+ *
+ * Based on Sony IMX074 Camera Driver
+ * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * Based on Omnivision OV7670 Camera Driver
+ * Copyright (C) 2006-7 Jonathan Corbet <corbet@lwn.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/module.h>
+#include <linux/v4l2-mediabus.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-subdev.h>
+
+/* OV5642 registers */
+#define REG_CHIP_ID_HIGH 0x300a
+#define REG_CHIP_ID_LOW 0x300b
+
+#define REG_WINDOW_START_X_HIGH 0x3800
+#define REG_WINDOW_START_X_LOW 0x3801
+#define REG_WINDOW_START_Y_HIGH 0x3802
+#define REG_WINDOW_START_Y_LOW 0x3803
+#define REG_WINDOW_WIDTH_HIGH 0x3804
+#define REG_WINDOW_WIDTH_LOW 0x3805
+#define REG_WINDOW_HEIGHT_HIGH 0x3806
+#define REG_WINDOW_HEIGHT_LOW 0x3807
+#define REG_OUT_WIDTH_HIGH 0x3808
+#define REG_OUT_WIDTH_LOW 0x3809
+#define REG_OUT_HEIGHT_HIGH 0x380a
+#define REG_OUT_HEIGHT_LOW 0x380b
+#define REG_OUT_TOTAL_WIDTH_HIGH 0x380c
+#define REG_OUT_TOTAL_WIDTH_LOW 0x380d
+#define REG_OUT_TOTAL_HEIGHT_HIGH 0x380e
+#define REG_OUT_TOTAL_HEIGHT_LOW 0x380f
+#define REG_OUTPUT_FORMAT 0x4300
+#define REG_ISP_CTRL_01 0x5001
+#define REG_AVG_WINDOW_END_X_HIGH 0x5682
+#define REG_AVG_WINDOW_END_X_LOW 0x5683
+#define REG_AVG_WINDOW_END_Y_HIGH 0x5686
+#define REG_AVG_WINDOW_END_Y_LOW 0x5687
+
+/* active pixel array size */
+#define OV5642_SENSOR_SIZE_X 2592
+#define OV5642_SENSOR_SIZE_Y 1944
+
+/*
+ * About OV5642 resolution, cropping and binning:
+ * This sensor supports it all, at least in the feature description.
+ * Unfortunately, no combination of appropriate registers settings could make
+ * the chip work the intended way. As it works with predefined register lists,
+ * some undocumented registers are presumably changed there to achieve their
+ * goals.
+ * This driver currently only works for resolutions up to 720 lines with a
+ * 1:1 scale. Hopefully these restrictions will be removed in the future.
+ */
+#define OV5642_MAX_WIDTH OV5642_SENSOR_SIZE_X
+#define OV5642_MAX_HEIGHT 720
+
+/* default sizes */
+#define OV5642_DEFAULT_WIDTH 1280
+#define OV5642_DEFAULT_HEIGHT OV5642_MAX_HEIGHT
+
+/* minimum extra blanking */
+#define BLANKING_EXTRA_WIDTH 500
+#define BLANKING_EXTRA_HEIGHT 20
+
+/*
+ * the sensor's autoexposure is buggy when setting total_height low.
+ * It tries to expose longer than 1 frame period without taking care of it
+ * and this leads to weird output. So we set 1000 lines as minimum.
+ */
+#define BLANKING_MIN_HEIGHT 1000
+
+struct regval_list {
+ u16 reg_num;
+ u8 value;
+};
+
+static struct regval_list ov5642_default_regs_init[] = {
+ { 0x3103, 0x93 },
+ { 0x3008, 0x82 },
+ { 0x3017, 0x7f },
+ { 0x3018, 0xfc },
+ { 0x3810, 0xc2 },
+ { 0x3615, 0xf0 },
+ { 0x3000, 0x0 },
+ { 0x3001, 0x0 },
+ { 0x3002, 0x0 },
+ { 0x3003, 0x0 },
+ { 0x3004, 0xff },
+ { 0x3030, 0x2b },
+ { 0x3011, 0x8 },
+ { 0x3010, 0x10 },
+ { 0x3604, 0x60 },
+ { 0x3622, 0x60 },
+ { 0x3621, 0x9 },
+ { 0x3709, 0x0 },
+ { 0x4000, 0x21 },
+ { 0x401d, 0x22 },
+ { 0x3600, 0x54 },
+ { 0x3605, 0x4 },
+ { 0x3606, 0x3f },
+ { 0x3c01, 0x80 },
+ { 0x300d, 0x22 },
+ { 0x3623, 0x22 },
+ { 0x5000, 0x4f },
+ { 0x5020, 0x4 },
+ { 0x5181, 0x79 },
+ { 0x5182, 0x0 },
+ { 0x5185, 0x22 },
+ { 0x5197, 0x1 },
+ { 0x5500, 0xa },
+ { 0x5504, 0x0 },
+ { 0x5505, 0x7f },
+ { 0x5080, 0x8 },
+ { 0x300e, 0x18 },
+ { 0x4610, 0x0 },
+ { 0x471d, 0x5 },
+ { 0x4708, 0x6 },
+ { 0x370c, 0xa0 },
+ { 0x5687, 0x94 },
+ { 0x501f, 0x0 },
+ { 0x5000, 0x4f },
+ { 0x5001, 0xcf },
+ { 0x4300, 0x30 },
+ { 0x4300, 0x30 },
+ { 0x460b, 0x35 },
+ { 0x471d, 0x0 },
+ { 0x3002, 0xc },
+ { 0x3002, 0x0 },
+ { 0x4713, 0x3 },
+ { 0x471c, 0x50 },
+ { 0x4721, 0x2 },
+ { 0x4402, 0x90 },
+ { 0x460c, 0x22 },
+ { 0x3815, 0x44 },
+ { 0x3503, 0x7 },
+ { 0x3501, 0x73 },
+ { 0x3502, 0x80 },
+ { 0x350b, 0x0 },
+ { 0x3818, 0xc8 },
+ { 0x3824, 0x11 },
+ { 0x3a00, 0x78 },
+ { 0x3a1a, 0x4 },
+ { 0x3a13, 0x30 },
+ { 0x3a18, 0x0 },
+ { 0x3a19, 0x7c },
+ { 0x3a08, 0x12 },
+ { 0x3a09, 0xc0 },
+ { 0x3a0a, 0xf },
+ { 0x3a0b, 0xa0 },
+ { 0x350c, 0x7 },
+ { 0x350d, 0xd0 },
+ { 0x3a0d, 0x8 },
+ { 0x3a0e, 0x6 },
+ { 0x3500, 0x0 },
+ { 0x3501, 0x0 },
+ { 0x3502, 0x0 },
+ { 0x350a, 0x0 },
+ { 0x350b, 0x0 },
+ { 0x3503, 0x0 },
+ { 0x3a0f, 0x3c },
+ { 0x3a10, 0x32 },
+ { 0x3a1b, 0x3c },
+ { 0x3a1e, 0x32 },
+ { 0x3a11, 0x80 },
+ { 0x3a1f, 0x20 },
+ { 0x3030, 0x2b },
+ { 0x3a02, 0x0 },
+ { 0x3a03, 0x7d },
+ { 0x3a04, 0x0 },
+ { 0x3a14, 0x0 },
+ { 0x3a15, 0x7d },
+ { 0x3a16, 0x0 },
+ { 0x3a00, 0x78 },
+ { 0x3a08, 0x9 },
+ { 0x3a09, 0x60 },
+ { 0x3a0a, 0x7 },
+ { 0x3a0b, 0xd0 },
+ { 0x3a0d, 0x10 },
+ { 0x3a0e, 0xd },
+ { 0x4407, 0x4 },
+ { 0x5193, 0x70 },
+ { 0x589b, 0x0 },
+ { 0x589a, 0xc0 },
+ { 0x401e, 0x20 },
+ { 0x4001, 0x42 },
+ { 0x401c, 0x6 },
+ { 0x3825, 0xac },
+ { 0x3827, 0xc },
+ { 0x528a, 0x1 },
+ { 0x528b, 0x4 },
+ { 0x528c, 0x8 },
+ { 0x528d, 0x10 },
+ { 0x528e, 0x20 },
+ { 0x528f, 0x28 },
+ { 0x5290, 0x30 },
+ { 0x5292, 0x0 },
+ { 0x5293, 0x1 },
+ { 0x5294, 0x0 },
+ { 0x5295, 0x4 },
+ { 0x5296, 0x0 },
+ { 0x5297, 0x8 },
+ { 0x5298, 0x0 },
+ { 0x5299, 0x10 },
+ { 0x529a, 0x0 },
+ { 0x529b, 0x20 },
+ { 0x529c, 0x0 },
+ { 0x529d, 0x28 },
+ { 0x529e, 0x0 },
+ { 0x529f, 0x30 },
+ { 0x5282, 0x0 },
+ { 0x5300, 0x0 },
+ { 0x5301, 0x20 },
+ { 0x5302, 0x0 },
+ { 0x5303, 0x7c },
+ { 0x530c, 0x0 },
+ { 0x530d, 0xc },
+ { 0x530e, 0x20 },
+ { 0x530f, 0x80 },
+ { 0x5310, 0x20 },
+ { 0x5311, 0x80 },
+ { 0x5308, 0x20 },
+ { 0x5309, 0x40 },
+ { 0x5304, 0x0 },
+ { 0x5305, 0x30 },
+ { 0x5306, 0x0 },
+ { 0x5307, 0x80 },
+ { 0x5314, 0x8 },
+ { 0x5315, 0x20 },
+ { 0x5319, 0x30 },
+ { 0x5316, 0x10 },
+ { 0x5317, 0x0 },
+ { 0x5318, 0x2 },
+ { 0x5380, 0x1 },
+ { 0x5381, 0x0 },
+ { 0x5382, 0x0 },
+ { 0x5383, 0x4e },
+ { 0x5384, 0x0 },
+ { 0x5385, 0xf },
+ { 0x5386, 0x0 },
+ { 0x5387, 0x0 },
+ { 0x5388, 0x1 },
+ { 0x5389, 0x15 },
+ { 0x538a, 0x0 },
+ { 0x538b, 0x31 },
+ { 0x538c, 0x0 },
+ { 0x538d, 0x0 },
+ { 0x538e, 0x0 },
+ { 0x538f, 0xf },
+ { 0x5390, 0x0 },
+ { 0x5391, 0xab },
+ { 0x5392, 0x0 },
+ { 0x5393, 0xa2 },
+ { 0x5394, 0x8 },
+ { 0x5480, 0x14 },
+ { 0x5481, 0x21 },
+ { 0x5482, 0x36 },
+ { 0x5483, 0x57 },
+ { 0x5484, 0x65 },
+ { 0x5485, 0x71 },
+ { 0x5486, 0x7d },
+ { 0x5487, 0x87 },
+ { 0x5488, 0x91 },
+ { 0x5489, 0x9a },
+ { 0x548a, 0xaa },
+ { 0x548b, 0xb8 },
+ { 0x548c, 0xcd },
+ { 0x548d, 0xdd },
+ { 0x548e, 0xea },
+ { 0x548f, 0x1d },
+ { 0x5490, 0x5 },
+ { 0x5491, 0x0 },
+ { 0x5492, 0x4 },
+ { 0x5493, 0x20 },
+ { 0x5494, 0x3 },
+ { 0x5495, 0x60 },
+ { 0x5496, 0x2 },
+ { 0x5497, 0xb8 },
+ { 0x5498, 0x2 },
+ { 0x5499, 0x86 },
+ { 0x549a, 0x2 },
+ { 0x549b, 0x5b },
+ { 0x549c, 0x2 },
+ { 0x549d, 0x3b },
+ { 0x549e, 0x2 },
+ { 0x549f, 0x1c },
+ { 0x54a0, 0x2 },
+ { 0x54a1, 0x4 },
+ { 0x54a2, 0x1 },
+ { 0x54a3, 0xed },
+ { 0x54a4, 0x1 },
+ { 0x54a5, 0xc5 },
+ { 0x54a6, 0x1 },
+ { 0x54a7, 0xa5 },
+ { 0x54a8, 0x1 },
+ { 0x54a9, 0x6c },
+ { 0x54aa, 0x1 },
+ { 0x54ab, 0x41 },
+ { 0x54ac, 0x1 },
+ { 0x54ad, 0x20 },
+ { 0x54ae, 0x0 },
+ { 0x54af, 0x16 },
+ { 0x54b0, 0x1 },
+ { 0x54b1, 0x20 },
+ { 0x54b2, 0x0 },
+ { 0x54b3, 0x10 },
+ { 0x54b4, 0x0 },
+ { 0x54b5, 0xf0 },
+ { 0x54b6, 0x0 },
+ { 0x54b7, 0xdf },
+ { 0x5402, 0x3f },
+ { 0x5403, 0x0 },
+ { 0x3406, 0x0 },
+ { 0x5180, 0xff },
+ { 0x5181, 0x52 },
+ { 0x5182, 0x11 },
+ { 0x5183, 0x14 },
+ { 0x5184, 0x25 },
+ { 0x5185, 0x24 },
+ { 0x5186, 0x6 },
+ { 0x5187, 0x8 },
+ { 0x5188, 0x8 },
+ { 0x5189, 0x7c },
+ { 0x518a, 0x60 },
+ { 0x518b, 0xb2 },
+ { 0x518c, 0xb2 },
+ { 0x518d, 0x44 },
+ { 0x518e, 0x3d },
+ { 0x518f, 0x58 },
+ { 0x5190, 0x46 },
+ { 0x5191, 0xf8 },
+ { 0x5192, 0x4 },
+ { 0x5193, 0x70 },
+ { 0x5194, 0xf0 },
+ { 0x5195, 0xf0 },
+ { 0x5196, 0x3 },
+ { 0x5197, 0x1 },
+ { 0x5198, 0x4 },
+ { 0x5199, 0x12 },
+ { 0x519a, 0x4 },
+ { 0x519b, 0x0 },
+ { 0x519c, 0x6 },
+ { 0x519d, 0x82 },
+ { 0x519e, 0x0 },
+ { 0x5025, 0x80 },
+ { 0x3a0f, 0x38 },
+ { 0x3a10, 0x30 },
+ { 0x3a1b, 0x3a },
+ { 0x3a1e, 0x2e },
+ { 0x3a11, 0x60 },
+ { 0x3a1f, 0x10 },
+ { 0x5688, 0xa6 },
+ { 0x5689, 0x6a },
+ { 0x568a, 0xea },
+ { 0x568b, 0xae },
+ { 0x568c, 0xa6 },
+ { 0x568d, 0x6a },
+ { 0x568e, 0x62 },
+ { 0x568f, 0x26 },
+ { 0x5583, 0x40 },
+ { 0x5584, 0x40 },
+ { 0x5580, 0x2 },
+ { 0x5000, 0xcf },
+ { 0x5800, 0x27 },
+ { 0x5801, 0x19 },
+ { 0x5802, 0x12 },
+ { 0x5803, 0xf },
+ { 0x5804, 0x10 },
+ { 0x5805, 0x15 },
+ { 0x5806, 0x1e },
+ { 0x5807, 0x2f },
+ { 0x5808, 0x15 },
+ { 0x5809, 0xd },
+ { 0x580a, 0xa },
+ { 0x580b, 0x9 },
+ { 0x580c, 0xa },
+ { 0x580d, 0xc },
+ { 0x580e, 0x12 },
+ { 0x580f, 0x19 },
+ { 0x5810, 0xb },
+ { 0x5811, 0x7 },
+ { 0x5812, 0x4 },
+ { 0x5813, 0x3 },
+ { 0x5814, 0x3 },
+ { 0x5815, 0x6 },
+ { 0x5816, 0xa },
+ { 0x5817, 0xf },
+ { 0x5818, 0xa },
+ { 0x5819, 0x5 },
+ { 0x581a, 0x1 },
+ { 0x581b, 0x0 },
+ { 0x581c, 0x0 },
+ { 0x581d, 0x3 },
+ { 0x581e, 0x8 },
+ { 0x581f, 0xc },
+ { 0x5820, 0xa },
+ { 0x5821, 0x5 },
+ { 0x5822, 0x1 },
+ { 0x5823, 0x0 },
+ { 0x5824, 0x0 },
+ { 0x5825, 0x3 },
+ { 0x5826, 0x8 },
+ { 0x5827, 0xc },
+ { 0x5828, 0xe },
+ { 0x5829, 0x8 },
+ { 0x582a, 0x6 },
+ { 0x582b, 0x4 },
+ { 0x582c, 0x5 },
+ { 0x582d, 0x7 },
+ { 0x582e, 0xb },
+ { 0x582f, 0x12 },
+ { 0x5830, 0x18 },
+ { 0x5831, 0x10 },
+ { 0x5832, 0xc },
+ { 0x5833, 0xa },
+ { 0x5834, 0xb },
+ { 0x5835, 0xe },
+ { 0x5836, 0x15 },
+ { 0x5837, 0x19 },
+ { 0x5838, 0x32 },
+ { 0x5839, 0x1f },
+ { 0x583a, 0x18 },
+ { 0x583b, 0x16 },
+ { 0x583c, 0x17 },
+ { 0x583d, 0x1e },
+ { 0x583e, 0x26 },
+ { 0x583f, 0x53 },
+ { 0x5840, 0x10 },
+ { 0x5841, 0xf },
+ { 0x5842, 0xd },
+ { 0x5843, 0xc },
+ { 0x5844, 0xe },
+ { 0x5845, 0x9 },
+ { 0x5846, 0x11 },
+ { 0x5847, 0x10 },
+ { 0x5848, 0x10 },
+ { 0x5849, 0x10 },
+ { 0x584a, 0x10 },
+ { 0x584b, 0xe },
+ { 0x584c, 0x10 },
+ { 0x584d, 0x10 },
+ { 0x584e, 0x11 },
+ { 0x584f, 0x10 },
+ { 0x5850, 0xf },
+ { 0x5851, 0xc },
+ { 0x5852, 0xf },
+ { 0x5853, 0x10 },
+ { 0x5854, 0x10 },
+ { 0x5855, 0xf },
+ { 0x5856, 0xe },
+ { 0x5857, 0xb },
+ { 0x5858, 0x10 },
+ { 0x5859, 0xd },
+ { 0x585a, 0xd },
+ { 0x585b, 0xc },
+ { 0x585c, 0xc },
+ { 0x585d, 0xc },
+ { 0x585e, 0xb },
+ { 0x585f, 0xc },
+ { 0x5860, 0xc },
+ { 0x5861, 0xc },
+ { 0x5862, 0xd },
+ { 0x5863, 0x8 },
+ { 0x5864, 0x11 },
+ { 0x5865, 0x18 },
+ { 0x5866, 0x18 },
+ { 0x5867, 0x19 },
+ { 0x5868, 0x17 },
+ { 0x5869, 0x19 },
+ { 0x586a, 0x16 },
+ { 0x586b, 0x13 },
+ { 0x586c, 0x13 },
+ { 0x586d, 0x12 },
+ { 0x586e, 0x13 },
+ { 0x586f, 0x16 },
+ { 0x5870, 0x14 },
+ { 0x5871, 0x12 },
+ { 0x5872, 0x10 },
+ { 0x5873, 0x11 },
+ { 0x5874, 0x11 },
+ { 0x5875, 0x16 },
+ { 0x5876, 0x14 },
+ { 0x5877, 0x11 },
+ { 0x5878, 0x10 },
+ { 0x5879, 0xf },
+ { 0x587a, 0x10 },
+ { 0x587b, 0x14 },
+ { 0x587c, 0x13 },
+ { 0x587d, 0x12 },
+ { 0x587e, 0x11 },
+ { 0x587f, 0x11 },
+ { 0x5880, 0x12 },
+ { 0x5881, 0x15 },
+ { 0x5882, 0x14 },
+ { 0x5883, 0x15 },
+ { 0x5884, 0x15 },
+ { 0x5885, 0x15 },
+ { 0x5886, 0x13 },
+ { 0x5887, 0x17 },
+ { 0x3710, 0x10 },
+ { 0x3632, 0x51 },
+ { 0x3702, 0x10 },
+ { 0x3703, 0xb2 },
+ { 0x3704, 0x18 },
+ { 0x370b, 0x40 },
+ { 0x370d, 0x3 },
+ { 0x3631, 0x1 },
+ { 0x3632, 0x52 },
+ { 0x3606, 0x24 },
+ { 0x3620, 0x96 },
+ { 0x5785, 0x7 },
+ { 0x3a13, 0x30 },
+ { 0x3600, 0x52 },
+ { 0x3604, 0x48 },
+ { 0x3606, 0x1b },
+ { 0x370d, 0xb },
+ { 0x370f, 0xc0 },
+ { 0x3709, 0x1 },
+ { 0x3823, 0x0 },
+ { 0x5007, 0x0 },
+ { 0x5009, 0x0 },
+ { 0x5011, 0x0 },
+ { 0x5013, 0x0 },
+ { 0x519e, 0x0 },
+ { 0x5086, 0x0 },
+ { 0x5087, 0x0 },
+ { 0x5088, 0x0 },
+ { 0x5089, 0x0 },
+ { 0x302b, 0x0 },
+ { 0x3503, 0x7 },
+ { 0x3011, 0x8 },
+ { 0x350c, 0x2 },
+ { 0x350d, 0xe4 },
+ { 0x3621, 0xc9 },
+ { 0x370a, 0x81 },
+ { 0xffff, 0xff },
+};
+
+static struct regval_list ov5642_default_regs_finalise[] = {
+ { 0x3810, 0xc2 },
+ { 0x3818, 0xc9 },
+ { 0x381c, 0x10 },
+ { 0x381d, 0xa0 },
+ { 0x381e, 0x5 },
+ { 0x381f, 0xb0 },
+ { 0x3820, 0x0 },
+ { 0x3821, 0x0 },
+ { 0x3824, 0x11 },
+ { 0x3a08, 0x1b },
+ { 0x3a09, 0xc0 },
+ { 0x3a0a, 0x17 },
+ { 0x3a0b, 0x20 },
+ { 0x3a0d, 0x2 },
+ { 0x3a0e, 0x1 },
+ { 0x401c, 0x4 },
+ { 0x5682, 0x5 },
+ { 0x5683, 0x0 },
+ { 0x5686, 0x2 },
+ { 0x5687, 0xcc },
+ { 0x5001, 0x4f },
+ { 0x589b, 0x6 },
+ { 0x589a, 0xc5 },
+ { 0x3503, 0x0 },
+ { 0x460c, 0x20 },
+ { 0x460b, 0x37 },
+ { 0x471c, 0xd0 },
+ { 0x471d, 0x5 },
+ { 0x3815, 0x1 },
+ { 0x3818, 0xc1 },
+ { 0x501f, 0x0 },
+ { 0x5002, 0xe0 },
+ { 0x4300, 0x32 }, /* UYVY */
+ { 0x3002, 0x1c },
+ { 0x4800, 0x14 },
+ { 0x4801, 0xf },
+ { 0x3007, 0x3b },
+ { 0x300e, 0x4 },
+ { 0x4803, 0x50 },
+ { 0x3815, 0x1 },
+ { 0x4713, 0x2 },
+ { 0x4842, 0x1 },
+ { 0x300f, 0xe },
+ { 0x3003, 0x3 },
+ { 0x3003, 0x1 },
+ { 0xffff, 0xff },
+};
+
+struct ov5642_datafmt {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+};
+
+struct ov5642 {
+ struct v4l2_subdev subdev;
+ const struct ov5642_datafmt *fmt;
+ struct v4l2_rect crop_rect;
+
+ /* blanking information */
+ int total_width;
+ int total_height;
+};
+
+static const struct ov5642_datafmt ov5642_colour_fmts[] = {
+ {V4L2_MBUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG},
+};
+
+static struct ov5642 *to_ov5642(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct ov5642, subdev);
+}
+
+/* Find a data format by a pixel code in an array */
+static const struct ov5642_datafmt
+ *ov5642_find_datafmt(enum v4l2_mbus_pixelcode code)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ov5642_colour_fmts); i++)
+ if (ov5642_colour_fmts[i].code == code)
+ return ov5642_colour_fmts + i;
+
+ return NULL;
+}
+
+static int reg_read(struct i2c_client *client, u16 reg, u8 *val)
+{
+ int ret;
+ /* We have 16-bit i2c addresses - care for endianess */
+ unsigned char data[2] = { reg >> 8, reg & 0xff };
+
+ ret = i2c_master_send(client, data, 2);
+ if (ret < 2) {
+ dev_err(&client->dev, "%s: i2c read error, reg: %x\n",
+ __func__, reg);
+ return ret < 0 ? ret : -EIO;
+ }
+
+ ret = i2c_master_recv(client, val, 1);
+ if (ret < 1) {
+ dev_err(&client->dev, "%s: i2c read error, reg: %x\n",
+ __func__, reg);
+ return ret < 0 ? ret : -EIO;
+ }
+ return 0;
+}
+
+static int reg_write(struct i2c_client *client, u16 reg, u8 val)
+{
+ int ret;
+ unsigned char data[3] = { reg >> 8, reg & 0xff, val };
+
+ ret = i2c_master_send(client, data, 3);
+ if (ret < 3) {
+ dev_err(&client->dev, "%s: i2c write error, reg: %x\n",
+ __func__, reg);
+ return ret < 0 ? ret : -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * convenience function to write 16 bit register values that are split up
+ * into two consecutive high and low parts
+ */
+static int reg_write16(struct i2c_client *client, u16 reg, u16 val16)
+{
+ int ret;
+
+ ret = reg_write(client, reg, val16 >> 8);
+ if (ret)
+ return ret;
+ return reg_write(client, reg + 1, val16 & 0x00ff);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov5642_get_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+ u8 val;
+
+ if (reg->reg & ~0xffff)
+ return -EINVAL;
+
+ reg->size = 1;
+
+ ret = reg_read(client, reg->reg, &val);
+ if (!ret)
+ reg->val = (__u64)val;
+
+ return ret;
+}
+
+static int ov5642_set_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->reg & ~0xffff || reg->val & ~0xff)
+ return -EINVAL;
+
+ return reg_write(client, reg->reg, reg->val);
+}
+#endif
+
+static int ov5642_write_array(struct i2c_client *client,
+ struct regval_list *vals)
+{
+ while (vals->reg_num != 0xffff || vals->value != 0xff) {
+ int ret = reg_write(client, vals->reg_num, vals->value);
+ if (ret < 0)
+ return ret;
+ vals++;
+ }
+ dev_dbg(&client->dev, "Register list loaded\n");
+ return 0;
+}
+
+static int ov5642_set_resolution(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5642 *priv = to_ov5642(client);
+ int width = priv->crop_rect.width;
+ int height = priv->crop_rect.height;
+ int total_width = priv->total_width;
+ int total_height = priv->total_height;
+ int start_x = (OV5642_SENSOR_SIZE_X - width) / 2;
+ int start_y = (OV5642_SENSOR_SIZE_Y - height) / 2;
+ int ret;
+
+ /*
+ * This should set the starting point for cropping.
+ * Doesn't work so far.
+ */
+ ret = reg_write16(client, REG_WINDOW_START_X_HIGH, start_x);
+ if (!ret)
+ ret = reg_write16(client, REG_WINDOW_START_Y_HIGH, start_y);
+ if (!ret) {
+ priv->crop_rect.left = start_x;
+ priv->crop_rect.top = start_y;
+ }
+
+ if (!ret)
+ ret = reg_write16(client, REG_WINDOW_WIDTH_HIGH, width);
+ if (!ret)
+ ret = reg_write16(client, REG_WINDOW_HEIGHT_HIGH, height);
+ if (ret)
+ return ret;
+ priv->crop_rect.width = width;
+ priv->crop_rect.height = height;
+
+ /* Set the output window size. Only 1:1 scale is supported so far. */
+ ret = reg_write16(client, REG_OUT_WIDTH_HIGH, width);
+ if (!ret)
+ ret = reg_write16(client, REG_OUT_HEIGHT_HIGH, height);
+
+ /* Total width = output size + blanking */
+ if (!ret)
+ ret = reg_write16(client, REG_OUT_TOTAL_WIDTH_HIGH, total_width);
+ if (!ret)
+ ret = reg_write16(client, REG_OUT_TOTAL_HEIGHT_HIGH, total_height);
+
+ /* Sets the window for AWB calculations */
+ if (!ret)
+ ret = reg_write16(client, REG_AVG_WINDOW_END_X_HIGH, width);
+ if (!ret)
+ ret = reg_write16(client, REG_AVG_WINDOW_END_Y_HIGH, height);
+
+ return ret;
+}
+
+static int ov5642_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5642 *priv = to_ov5642(client);
+ const struct ov5642_datafmt *fmt = ov5642_find_datafmt(mf->code);
+
+ mf->width = priv->crop_rect.width;
+ mf->height = priv->crop_rect.height;
+
+ if (!fmt) {
+ mf->code = ov5642_colour_fmts[0].code;
+ mf->colorspace = ov5642_colour_fmts[0].colorspace;
+ }
+
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int ov5642_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5642 *priv = to_ov5642(client);
+
+ /* MIPI CSI could have changed the format, double-check */
+ if (!ov5642_find_datafmt(mf->code))
+ return -EINVAL;
+
+ ov5642_try_fmt(sd, mf);
+ priv->fmt = ov5642_find_datafmt(mf->code);
+
+ return 0;
+}
+
+static int ov5642_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5642 *priv = to_ov5642(client);
+
+ const struct ov5642_datafmt *fmt = priv->fmt;
+
+ mf->code = fmt->code;
+ mf->colorspace = fmt->colorspace;
+ mf->width = priv->crop_rect.width;
+ mf->height = priv->crop_rect.height;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int ov5642_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= ARRAY_SIZE(ov5642_colour_fmts))
+ return -EINVAL;
+
+ *code = ov5642_colour_fmts[index].code;
+ return 0;
+}
+
+static int ov5642_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+
+ if (id->match.addr != client->addr)
+ return -ENODEV;
+
+ id->ident = V4L2_IDENT_OV5642;
+ id->revision = 0;
+
+ return 0;
+}
+
+static int ov5642_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5642 *priv = to_ov5642(client);
+ struct v4l2_rect *rect = &a->c;
+ int ret;
+
+ v4l_bound_align_image(&rect->width, 48, OV5642_MAX_WIDTH, 1,
+ &rect->height, 32, OV5642_MAX_HEIGHT, 1, 0);
+
+ priv->crop_rect.width = rect->width;
+ priv->crop_rect.height = rect->height;
+ priv->total_width = rect->width + BLANKING_EXTRA_WIDTH;
+ priv->total_height = max_t(int, rect->height +
+ BLANKING_EXTRA_HEIGHT,
+ BLANKING_MIN_HEIGHT);
+ priv->crop_rect.width = rect->width;
+ priv->crop_rect.height = rect->height;
+
+ ret = ov5642_write_array(client, ov5642_default_regs_init);
+ if (!ret)
+ ret = ov5642_set_resolution(sd);
+ if (!ret)
+ ret = ov5642_write_array(client, ov5642_default_regs_finalise);
+
+ return ret;
+}
+
+static int ov5642_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5642 *priv = to_ov5642(client);
+ struct v4l2_rect *rect = &a->c;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ *rect = priv->crop_rect;
+
+ return 0;
+}
+
+static int ov5642_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ a->bounds.width = OV5642_MAX_WIDTH;
+ a->bounds.height = OV5642_MAX_HEIGHT;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int ov5642_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ cfg->type = V4L2_MBUS_CSI2;
+ cfg->flags = V4L2_MBUS_CSI2_2_LANE | V4L2_MBUS_CSI2_CHANNEL_0 |
+ V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+
+ return 0;
+}
+
+static int ov5642_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client;
+ int ret;
+
+ if (!on)
+ return 0;
+
+ client = v4l2_get_subdevdata(sd);
+ ret = ov5642_write_array(client, ov5642_default_regs_init);
+ if (!ret)
+ ret = ov5642_set_resolution(sd);
+ if (!ret)
+ ret = ov5642_write_array(client, ov5642_default_regs_finalise);
+
+ return ret;
+}
+
+static struct v4l2_subdev_video_ops ov5642_subdev_video_ops = {
+ .s_mbus_fmt = ov5642_s_fmt,
+ .g_mbus_fmt = ov5642_g_fmt,
+ .try_mbus_fmt = ov5642_try_fmt,
+ .enum_mbus_fmt = ov5642_enum_fmt,
+ .s_crop = ov5642_s_crop,
+ .g_crop = ov5642_g_crop,
+ .cropcap = ov5642_cropcap,
+ .g_mbus_config = ov5642_g_mbus_config,
+};
+
+static struct v4l2_subdev_core_ops ov5642_subdev_core_ops = {
+ .s_power = ov5642_s_power,
+ .g_chip_ident = ov5642_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = ov5642_get_register,
+ .s_register = ov5642_set_register,
+#endif
+};
+
+static struct v4l2_subdev_ops ov5642_subdev_ops = {
+ .core = &ov5642_subdev_core_ops,
+ .video = &ov5642_subdev_video_ops,
+};
+
+static int ov5642_video_probe(struct i2c_client *client)
+{
+ int ret;
+ u8 id_high, id_low;
+ u16 id;
+
+ /* Read sensor Model ID */
+ ret = reg_read(client, REG_CHIP_ID_HIGH, &id_high);
+ if (ret < 0)
+ return ret;
+
+ id = id_high << 8;
+
+ ret = reg_read(client, REG_CHIP_ID_LOW, &id_low);
+ if (ret < 0)
+ return ret;
+
+ id |= id_low;
+
+ dev_info(&client->dev, "Chip ID 0x%04x detected\n", id);
+
+ if (id != 0x5642)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int ov5642_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct ov5642 *priv;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ int ret;
+
+ if (!icl) {
+ dev_err(&client->dev, "OV5642: missing platform data!\n");
+ return -EINVAL;
+ }
+
+ priv = kzalloc(sizeof(struct ov5642), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ v4l2_i2c_subdev_init(&priv->subdev, client, &ov5642_subdev_ops);
+
+ priv->fmt = &ov5642_colour_fmts[0];
+
+ priv->crop_rect.width = OV5642_DEFAULT_WIDTH;
+ priv->crop_rect.height = OV5642_DEFAULT_HEIGHT;
+ priv->crop_rect.left = (OV5642_MAX_WIDTH - OV5642_DEFAULT_WIDTH) / 2;
+ priv->crop_rect.top = (OV5642_MAX_HEIGHT - OV5642_DEFAULT_HEIGHT) / 2;
+ priv->crop_rect.width = OV5642_DEFAULT_WIDTH;
+ priv->crop_rect.height = OV5642_DEFAULT_HEIGHT;
+ priv->total_width = OV5642_DEFAULT_WIDTH + BLANKING_EXTRA_WIDTH;
+ priv->total_height = BLANKING_MIN_HEIGHT;
+
+ ret = ov5642_video_probe(client);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ kfree(priv);
+ return ret;
+}
+
+static int ov5642_remove(struct i2c_client *client)
+{
+ struct ov5642 *priv = to_ov5642(client);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ if (icl->free_bus)
+ icl->free_bus(icl);
+ kfree(priv);
+
+ return 0;
+}
+
+static const struct i2c_device_id ov5642_id[] = {
+ { "ov5642", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ov5642_id);
+
+static struct i2c_driver ov5642_i2c_driver = {
+ .driver = {
+ .name = "ov5642",
+ },
+ .probe = ov5642_probe,
+ .remove = ov5642_remove,
+ .id_table = ov5642_id,
+};
+
+module_i2c_driver(ov5642_i2c_driver);
+
+MODULE_DESCRIPTION("Omnivision OV5642 Camera driver");
+MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/ov6650.c b/drivers/media/video/ov6650.c
new file mode 100644
index 00000000..3e028b19
--- /dev/null
+++ b/drivers/media/video/ov6650.c
@@ -0,0 +1,1053 @@
+/*
+ * V4L2 SoC Camera driver for OmniVision OV6650 Camera Sensor
+ *
+ * Copyright (C) 2010 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
+ *
+ * Based on OmniVision OV96xx Camera Driver
+ * Copyright (C) 2009 Marek Vasut <marek.vasut@gmail.com>
+ *
+ * Based on ov772x camera driver:
+ * Copyright (C) 2008 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on ov7670 and soc_camera_platform driver,
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
+ * Copyright (C) 2008 Magnus Damm
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * Hardware specific bits initialy based on former work by Matt Callow
+ * drivers/media/video/omap/sensor_ov6650.c
+ * Copyright (C) 2006 Matt Callow
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/module.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+
+/* Register definitions */
+#define REG_GAIN 0x00 /* range 00 - 3F */
+#define REG_BLUE 0x01
+#define REG_RED 0x02
+#define REG_SAT 0x03 /* [7:4] saturation [0:3] reserved */
+#define REG_HUE 0x04 /* [7:6] rsrvd [5] hue en [4:0] hue */
+
+#define REG_BRT 0x06
+
+#define REG_PIDH 0x0a
+#define REG_PIDL 0x0b
+
+#define REG_AECH 0x10
+#define REG_CLKRC 0x11 /* Data Format and Internal Clock */
+ /* [7:6] Input system clock (MHz)*/
+ /* 00=8, 01=12, 10=16, 11=24 */
+ /* [5:0]: Internal Clock Pre-Scaler */
+#define REG_COMA 0x12 /* [7] Reset */
+#define REG_COMB 0x13
+#define REG_COMC 0x14
+#define REG_COMD 0x15
+#define REG_COML 0x16
+#define REG_HSTRT 0x17
+#define REG_HSTOP 0x18
+#define REG_VSTRT 0x19
+#define REG_VSTOP 0x1a
+#define REG_PSHFT 0x1b
+#define REG_MIDH 0x1c
+#define REG_MIDL 0x1d
+#define REG_HSYNS 0x1e
+#define REG_HSYNE 0x1f
+#define REG_COME 0x20
+#define REG_YOFF 0x21
+#define REG_UOFF 0x22
+#define REG_VOFF 0x23
+#define REG_AEW 0x24
+#define REG_AEB 0x25
+#define REG_COMF 0x26
+#define REG_COMG 0x27
+#define REG_COMH 0x28
+#define REG_COMI 0x29
+
+#define REG_FRARL 0x2b
+#define REG_COMJ 0x2c
+#define REG_COMK 0x2d
+#define REG_AVGY 0x2e
+#define REG_REF0 0x2f
+#define REG_REF1 0x30
+#define REG_REF2 0x31
+#define REG_FRAJH 0x32
+#define REG_FRAJL 0x33
+#define REG_FACT 0x34
+#define REG_L1AEC 0x35
+#define REG_AVGU 0x36
+#define REG_AVGV 0x37
+
+#define REG_SPCB 0x60
+#define REG_SPCC 0x61
+#define REG_GAM1 0x62
+#define REG_GAM2 0x63
+#define REG_GAM3 0x64
+#define REG_SPCD 0x65
+
+#define REG_SPCE 0x68
+#define REG_ADCL 0x69
+
+#define REG_RMCO 0x6c
+#define REG_GMCO 0x6d
+#define REG_BMCO 0x6e
+
+
+/* Register bits, values, etc. */
+#define OV6650_PIDH 0x66 /* high byte of product ID number */
+#define OV6650_PIDL 0x50 /* low byte of product ID number */
+#define OV6650_MIDH 0x7F /* high byte of mfg ID */
+#define OV6650_MIDL 0xA2 /* low byte of mfg ID */
+
+#define DEF_GAIN 0x00
+#define DEF_BLUE 0x80
+#define DEF_RED 0x80
+
+#define SAT_SHIFT 4
+#define SAT_MASK (0xf << SAT_SHIFT)
+#define SET_SAT(x) (((x) << SAT_SHIFT) & SAT_MASK)
+
+#define HUE_EN BIT(5)
+#define HUE_MASK 0x1f
+#define DEF_HUE 0x10
+#define SET_HUE(x) (HUE_EN | ((x) & HUE_MASK))
+
+#define DEF_AECH 0x4D
+
+#define CLKRC_6MHz 0x00
+#define CLKRC_12MHz 0x40
+#define CLKRC_16MHz 0x80
+#define CLKRC_24MHz 0xc0
+#define CLKRC_DIV_MASK 0x3f
+#define GET_CLKRC_DIV(x) (((x) & CLKRC_DIV_MASK) + 1)
+
+#define COMA_RESET BIT(7)
+#define COMA_QCIF BIT(5)
+#define COMA_RAW_RGB BIT(4)
+#define COMA_RGB BIT(3)
+#define COMA_BW BIT(2)
+#define COMA_WORD_SWAP BIT(1)
+#define COMA_BYTE_SWAP BIT(0)
+#define DEF_COMA 0x00
+
+#define COMB_FLIP_V BIT(7)
+#define COMB_FLIP_H BIT(5)
+#define COMB_BAND_FILTER BIT(4)
+#define COMB_AWB BIT(2)
+#define COMB_AGC BIT(1)
+#define COMB_AEC BIT(0)
+#define DEF_COMB 0x5f
+
+#define COML_ONE_CHANNEL BIT(7)
+
+#define DEF_HSTRT 0x24
+#define DEF_HSTOP 0xd4
+#define DEF_VSTRT 0x04
+#define DEF_VSTOP 0x94
+
+#define COMF_HREF_LOW BIT(4)
+
+#define COMJ_PCLK_RISING BIT(4)
+#define COMJ_VSYNC_HIGH BIT(0)
+
+/* supported resolutions */
+#define W_QCIF (DEF_HSTOP - DEF_HSTRT)
+#define W_CIF (W_QCIF << 1)
+#define H_QCIF (DEF_VSTOP - DEF_VSTRT)
+#define H_CIF (H_QCIF << 1)
+
+#define FRAME_RATE_MAX 30
+
+
+struct ov6650_reg {
+ u8 reg;
+ u8 val;
+};
+
+struct ov6650 {
+ struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
+ struct {
+ /* exposure/autoexposure cluster */
+ struct v4l2_ctrl *autoexposure;
+ struct v4l2_ctrl *exposure;
+ };
+ struct {
+ /* gain/autogain cluster */
+ struct v4l2_ctrl *autogain;
+ struct v4l2_ctrl *gain;
+ };
+ struct {
+ /* blue/red/autowhitebalance cluster */
+ struct v4l2_ctrl *autowb;
+ struct v4l2_ctrl *blue;
+ struct v4l2_ctrl *red;
+ };
+ bool half_scale; /* scale down output by 2 */
+ struct v4l2_rect rect; /* sensor cropping window */
+ unsigned long pclk_limit; /* from host */
+ unsigned long pclk_max; /* from resolution and format */
+ struct v4l2_fract tpf; /* as requested with s_parm */
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+};
+
+
+static enum v4l2_mbus_pixelcode ov6650_codes[] = {
+ V4L2_MBUS_FMT_YUYV8_2X8,
+ V4L2_MBUS_FMT_UYVY8_2X8,
+ V4L2_MBUS_FMT_YVYU8_2X8,
+ V4L2_MBUS_FMT_VYUY8_2X8,
+ V4L2_MBUS_FMT_SBGGR8_1X8,
+ V4L2_MBUS_FMT_Y8_1X8,
+};
+
+/* read a register */
+static int ov6650_reg_read(struct i2c_client *client, u8 reg, u8 *val)
+{
+ int ret;
+ u8 data = reg;
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &data,
+ };
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret < 0)
+ goto err;
+
+ msg.flags = I2C_M_RD;
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret < 0)
+ goto err;
+
+ *val = data;
+ return 0;
+
+err:
+ dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg);
+ return ret;
+}
+
+/* write a register */
+static int ov6650_reg_write(struct i2c_client *client, u8 reg, u8 val)
+{
+ int ret;
+ unsigned char data[2] = { reg, val };
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = data,
+ };
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ udelay(100);
+
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg);
+ return ret;
+ }
+ return 0;
+}
+
+
+/* Read a register, alter its bits, write it back */
+static int ov6650_reg_rmw(struct i2c_client *client, u8 reg, u8 set, u8 mask)
+{
+ u8 val;
+ int ret;
+
+ ret = ov6650_reg_read(client, reg, &val);
+ if (ret) {
+ dev_err(&client->dev,
+ "[Read]-Modify-Write of register 0x%02x failed!\n",
+ reg);
+ return ret;
+ }
+
+ val &= ~mask;
+ val |= set;
+
+ ret = ov6650_reg_write(client, reg, val);
+ if (ret)
+ dev_err(&client->dev,
+ "Read-Modify-[Write] of register 0x%02x failed!\n",
+ reg);
+
+ return ret;
+}
+
+static struct ov6650 *to_ov6650(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct ov6650, subdev);
+}
+
+/* Start/Stop streaming from the device */
+static int ov6650_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ return 0;
+}
+
+/* Get status of additional camera capabilities */
+static int ov6550_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov6650 *priv = container_of(ctrl->handler, struct ov6650, hdl);
+ struct v4l2_subdev *sd = &priv->subdev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ uint8_t reg, reg2;
+ int ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTOGAIN:
+ ret = ov6650_reg_read(client, REG_GAIN, &reg);
+ if (!ret)
+ priv->gain->val = reg;
+ return ret;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ ret = ov6650_reg_read(client, REG_BLUE, &reg);
+ if (!ret)
+ ret = ov6650_reg_read(client, REG_RED, &reg2);
+ if (!ret) {
+ priv->blue->val = reg;
+ priv->red->val = reg2;
+ }
+ return ret;
+ case V4L2_CID_EXPOSURE_AUTO:
+ ret = ov6650_reg_read(client, REG_AECH, &reg);
+ if (!ret)
+ priv->exposure->val = reg;
+ return ret;
+ }
+ return -EINVAL;
+}
+
+/* Set status of additional camera capabilities */
+static int ov6550_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov6650 *priv = container_of(ctrl->handler, struct ov6650, hdl);
+ struct v4l2_subdev *sd = &priv->subdev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTOGAIN:
+ ret = ov6650_reg_rmw(client, REG_COMB,
+ ctrl->val ? COMB_AGC : 0, COMB_AGC);
+ if (!ret && !ctrl->val)
+ ret = ov6650_reg_write(client, REG_GAIN, priv->gain->val);
+ return ret;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ ret = ov6650_reg_rmw(client, REG_COMB,
+ ctrl->val ? COMB_AWB : 0, COMB_AWB);
+ if (!ret && !ctrl->val) {
+ ret = ov6650_reg_write(client, REG_BLUE, priv->blue->val);
+ if (!ret)
+ ret = ov6650_reg_write(client, REG_RED,
+ priv->red->val);
+ }
+ return ret;
+ case V4L2_CID_SATURATION:
+ return ov6650_reg_rmw(client, REG_SAT, SET_SAT(ctrl->val),
+ SAT_MASK);
+ case V4L2_CID_HUE:
+ return ov6650_reg_rmw(client, REG_HUE, SET_HUE(ctrl->val),
+ HUE_MASK);
+ case V4L2_CID_BRIGHTNESS:
+ return ov6650_reg_write(client, REG_BRT, ctrl->val);
+ case V4L2_CID_EXPOSURE_AUTO:
+ ret = ov6650_reg_rmw(client, REG_COMB, ctrl->val ==
+ V4L2_EXPOSURE_AUTO ? COMB_AEC : 0, COMB_AEC);
+ if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL)
+ ret = ov6650_reg_write(client, REG_AECH,
+ priv->exposure->val);
+ return ret;
+ case V4L2_CID_GAMMA:
+ return ov6650_reg_write(client, REG_GAM1, ctrl->val);
+ case V4L2_CID_VFLIP:
+ return ov6650_reg_rmw(client, REG_COMB,
+ ctrl->val ? COMB_FLIP_V : 0, COMB_FLIP_V);
+ case V4L2_CID_HFLIP:
+ return ov6650_reg_rmw(client, REG_COMB,
+ ctrl->val ? COMB_FLIP_H : 0, COMB_FLIP_H);
+ }
+
+ return -EINVAL;
+}
+
+/* Get chip identification */
+static int ov6650_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ id->ident = V4L2_IDENT_OV6650;
+ id->revision = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov6650_get_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+ u8 val;
+
+ if (reg->reg & ~0xff)
+ return -EINVAL;
+
+ reg->size = 1;
+
+ ret = ov6650_reg_read(client, reg->reg, &val);
+ if (!ret)
+ reg->val = (__u64)val;
+
+ return ret;
+}
+
+static int ov6650_set_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->reg & ~0xff || reg->val & ~0xff)
+ return -EINVAL;
+
+ return ov6650_reg_write(client, reg->reg, reg->val);
+}
+#endif
+
+static int ov6650_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov6650 *priv = to_ov6650(client);
+
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->c = priv->rect;
+
+ return 0;
+}
+
+static int ov6650_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov6650 *priv = to_ov6650(client);
+ struct v4l2_rect *rect = &a->c;
+ int ret;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ rect->left = ALIGN(rect->left, 2);
+ rect->width = ALIGN(rect->width, 2);
+ rect->top = ALIGN(rect->top, 2);
+ rect->height = ALIGN(rect->height, 2);
+ soc_camera_limit_side(&rect->left, &rect->width,
+ DEF_HSTRT << 1, 2, W_CIF);
+ soc_camera_limit_side(&rect->top, &rect->height,
+ DEF_VSTRT << 1, 2, H_CIF);
+
+ ret = ov6650_reg_write(client, REG_HSTRT, rect->left >> 1);
+ if (!ret) {
+ priv->rect.left = rect->left;
+ ret = ov6650_reg_write(client, REG_HSTOP,
+ (rect->left + rect->width) >> 1);
+ }
+ if (!ret) {
+ priv->rect.width = rect->width;
+ ret = ov6650_reg_write(client, REG_VSTRT, rect->top >> 1);
+ }
+ if (!ret) {
+ priv->rect.top = rect->top;
+ ret = ov6650_reg_write(client, REG_VSTOP,
+ (rect->top + rect->height) >> 1);
+ }
+ if (!ret)
+ priv->rect.height = rect->height;
+
+ return ret;
+}
+
+static int ov6650_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ a->bounds.left = DEF_HSTRT << 1;
+ a->bounds.top = DEF_VSTRT << 1;
+ a->bounds.width = W_CIF;
+ a->bounds.height = H_CIF;
+ a->defrect = a->bounds;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int ov6650_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov6650 *priv = to_ov6650(client);
+
+ mf->width = priv->rect.width >> priv->half_scale;
+ mf->height = priv->rect.height >> priv->half_scale;
+ mf->code = priv->code;
+ mf->colorspace = priv->colorspace;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static bool is_unscaled_ok(int width, int height, struct v4l2_rect *rect)
+{
+ return width > rect->width >> 1 || height > rect->height >> 1;
+}
+
+static u8 to_clkrc(struct v4l2_fract *timeperframe,
+ unsigned long pclk_limit, unsigned long pclk_max)
+{
+ unsigned long pclk;
+
+ if (timeperframe->numerator && timeperframe->denominator)
+ pclk = pclk_max * timeperframe->denominator /
+ (FRAME_RATE_MAX * timeperframe->numerator);
+ else
+ pclk = pclk_max;
+
+ if (pclk_limit && pclk_limit < pclk)
+ pclk = pclk_limit;
+
+ return (pclk_max - 1) / pclk;
+}
+
+/* set the format we will capture in */
+static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_device *icd = v4l2_get_subdev_hostdata(sd);
+ struct soc_camera_sense *sense = icd->sense;
+ struct ov6650 *priv = to_ov6650(client);
+ bool half_scale = !is_unscaled_ok(mf->width, mf->height, &priv->rect);
+ struct v4l2_crop a = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .c = {
+ .left = priv->rect.left + (priv->rect.width >> 1) -
+ (mf->width >> (1 - half_scale)),
+ .top = priv->rect.top + (priv->rect.height >> 1) -
+ (mf->height >> (1 - half_scale)),
+ .width = mf->width << half_scale,
+ .height = mf->height << half_scale,
+ },
+ };
+ enum v4l2_mbus_pixelcode code = mf->code;
+ unsigned long mclk, pclk;
+ u8 coma_set = 0, coma_mask = 0, coml_set, coml_mask, clkrc;
+ int ret;
+
+ /* select color matrix configuration for given color encoding */
+ switch (code) {
+ case V4L2_MBUS_FMT_Y8_1X8:
+ dev_dbg(&client->dev, "pixel format GREY8_1X8\n");
+ coma_mask |= COMA_RGB | COMA_WORD_SWAP | COMA_BYTE_SWAP;
+ coma_set |= COMA_BW;
+ break;
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ dev_dbg(&client->dev, "pixel format YUYV8_2X8_LE\n");
+ coma_mask |= COMA_RGB | COMA_BW | COMA_BYTE_SWAP;
+ coma_set |= COMA_WORD_SWAP;
+ break;
+ case V4L2_MBUS_FMT_YVYU8_2X8:
+ dev_dbg(&client->dev, "pixel format YVYU8_2X8_LE (untested)\n");
+ coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP |
+ COMA_BYTE_SWAP;
+ break;
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ dev_dbg(&client->dev, "pixel format YUYV8_2X8_BE\n");
+ if (half_scale) {
+ coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP;
+ coma_set |= COMA_BYTE_SWAP;
+ } else {
+ coma_mask |= COMA_RGB | COMA_BW;
+ coma_set |= COMA_BYTE_SWAP | COMA_WORD_SWAP;
+ }
+ break;
+ case V4L2_MBUS_FMT_VYUY8_2X8:
+ dev_dbg(&client->dev, "pixel format YVYU8_2X8_BE (untested)\n");
+ if (half_scale) {
+ coma_mask |= COMA_RGB | COMA_BW;
+ coma_set |= COMA_BYTE_SWAP | COMA_WORD_SWAP;
+ } else {
+ coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP;
+ coma_set |= COMA_BYTE_SWAP;
+ }
+ break;
+ case V4L2_MBUS_FMT_SBGGR8_1X8:
+ dev_dbg(&client->dev, "pixel format SBGGR8_1X8 (untested)\n");
+ coma_mask |= COMA_BW | COMA_BYTE_SWAP | COMA_WORD_SWAP;
+ coma_set |= COMA_RAW_RGB | COMA_RGB;
+ break;
+ default:
+ dev_err(&client->dev, "Pixel format not handled: 0x%x\n", code);
+ return -EINVAL;
+ }
+ priv->code = code;
+
+ if (code == V4L2_MBUS_FMT_Y8_1X8 ||
+ code == V4L2_MBUS_FMT_SBGGR8_1X8) {
+ coml_mask = COML_ONE_CHANNEL;
+ coml_set = 0;
+ priv->pclk_max = 4000000;
+ } else {
+ coml_mask = 0;
+ coml_set = COML_ONE_CHANNEL;
+ priv->pclk_max = 8000000;
+ }
+
+ if (code == V4L2_MBUS_FMT_SBGGR8_1X8)
+ priv->colorspace = V4L2_COLORSPACE_SRGB;
+ else if (code != 0)
+ priv->colorspace = V4L2_COLORSPACE_JPEG;
+
+ if (half_scale) {
+ dev_dbg(&client->dev, "max resolution: QCIF\n");
+ coma_set |= COMA_QCIF;
+ priv->pclk_max /= 2;
+ } else {
+ dev_dbg(&client->dev, "max resolution: CIF\n");
+ coma_mask |= COMA_QCIF;
+ }
+ priv->half_scale = half_scale;
+
+ if (sense) {
+ if (sense->master_clock == 8000000) {
+ dev_dbg(&client->dev, "8MHz input clock\n");
+ clkrc = CLKRC_6MHz;
+ } else if (sense->master_clock == 12000000) {
+ dev_dbg(&client->dev, "12MHz input clock\n");
+ clkrc = CLKRC_12MHz;
+ } else if (sense->master_clock == 16000000) {
+ dev_dbg(&client->dev, "16MHz input clock\n");
+ clkrc = CLKRC_16MHz;
+ } else if (sense->master_clock == 24000000) {
+ dev_dbg(&client->dev, "24MHz input clock\n");
+ clkrc = CLKRC_24MHz;
+ } else {
+ dev_err(&client->dev,
+ "unsupported input clock, check platform data\n");
+ return -EINVAL;
+ }
+ mclk = sense->master_clock;
+ priv->pclk_limit = sense->pixel_clock_max;
+ } else {
+ clkrc = CLKRC_24MHz;
+ mclk = 24000000;
+ priv->pclk_limit = 0;
+ dev_dbg(&client->dev, "using default 24MHz input clock\n");
+ }
+
+ clkrc |= to_clkrc(&priv->tpf, priv->pclk_limit, priv->pclk_max);
+
+ pclk = priv->pclk_max / GET_CLKRC_DIV(clkrc);
+ dev_dbg(&client->dev, "pixel clock divider: %ld.%ld\n",
+ mclk / pclk, 10 * mclk % pclk / pclk);
+
+ ret = ov6650_s_crop(sd, &a);
+ if (!ret)
+ ret = ov6650_reg_rmw(client, REG_COMA, coma_set, coma_mask);
+ if (!ret)
+ ret = ov6650_reg_write(client, REG_CLKRC, clkrc);
+ if (!ret)
+ ret = ov6650_reg_rmw(client, REG_COML, coml_set, coml_mask);
+
+ if (!ret) {
+ mf->colorspace = priv->colorspace;
+ mf->width = priv->rect.width >> half_scale;
+ mf->height = priv->rect.height >> half_scale;
+ }
+
+ return ret;
+}
+
+static int ov6650_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov6650 *priv = to_ov6650(client);
+
+ if (is_unscaled_ok(mf->width, mf->height, &priv->rect))
+ v4l_bound_align_image(&mf->width, 2, W_CIF, 1,
+ &mf->height, 2, H_CIF, 1, 0);
+
+ mf->field = V4L2_FIELD_NONE;
+
+ switch (mf->code) {
+ case V4L2_MBUS_FMT_Y10_1X10:
+ mf->code = V4L2_MBUS_FMT_Y8_1X8;
+ case V4L2_MBUS_FMT_Y8_1X8:
+ case V4L2_MBUS_FMT_YVYU8_2X8:
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ case V4L2_MBUS_FMT_VYUY8_2X8:
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ break;
+ default:
+ mf->code = V4L2_MBUS_FMT_SBGGR8_1X8;
+ case V4L2_MBUS_FMT_SBGGR8_1X8:
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
+ break;
+ }
+
+ return 0;
+}
+
+static int ov6650_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= ARRAY_SIZE(ov6650_codes))
+ return -EINVAL;
+
+ *code = ov6650_codes[index];
+ return 0;
+}
+
+static int ov6650_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov6650 *priv = to_ov6650(client);
+ struct v4l2_captureparm *cp = &parms->parm.capture;
+
+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ memset(cp, 0, sizeof(*cp));
+ cp->capability = V4L2_CAP_TIMEPERFRAME;
+ cp->timeperframe.numerator = GET_CLKRC_DIV(to_clkrc(&priv->tpf,
+ priv->pclk_limit, priv->pclk_max));
+ cp->timeperframe.denominator = FRAME_RATE_MAX;
+
+ dev_dbg(&client->dev, "Frame interval: %u/%u s\n",
+ cp->timeperframe.numerator, cp->timeperframe.denominator);
+
+ return 0;
+}
+
+static int ov6650_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov6650 *priv = to_ov6650(client);
+ struct v4l2_captureparm *cp = &parms->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+ int div, ret;
+ u8 clkrc;
+
+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (cp->extendedmode != 0)
+ return -EINVAL;
+
+ if (tpf->numerator == 0 || tpf->denominator == 0)
+ div = 1; /* Reset to full rate */
+ else
+ div = (tpf->numerator * FRAME_RATE_MAX) / tpf->denominator;
+
+ if (div == 0)
+ div = 1;
+ else if (div > GET_CLKRC_DIV(CLKRC_DIV_MASK))
+ div = GET_CLKRC_DIV(CLKRC_DIV_MASK);
+
+ /*
+ * Keep result to be used as tpf limit
+ * for subseqent clock divider calculations
+ */
+ priv->tpf.numerator = div;
+ priv->tpf.denominator = FRAME_RATE_MAX;
+
+ clkrc = to_clkrc(&priv->tpf, priv->pclk_limit, priv->pclk_max);
+
+ ret = ov6650_reg_rmw(client, REG_CLKRC, clkrc, CLKRC_DIV_MASK);
+ if (!ret) {
+ tpf->numerator = GET_CLKRC_DIV(clkrc);
+ tpf->denominator = FRAME_RATE_MAX;
+ }
+
+ return ret;
+}
+
+/* Soft reset the camera. This has nothing to do with the RESET pin! */
+static int ov6650_reset(struct i2c_client *client)
+{
+ int ret;
+
+ dev_dbg(&client->dev, "reset\n");
+
+ ret = ov6650_reg_rmw(client, REG_COMA, COMA_RESET, 0);
+ if (ret)
+ dev_err(&client->dev,
+ "An error occurred while entering soft reset!\n");
+
+ return ret;
+}
+
+/* program default register values */
+static int ov6650_prog_dflt(struct i2c_client *client)
+{
+ int ret;
+
+ dev_dbg(&client->dev, "initializing\n");
+
+ ret = ov6650_reg_write(client, REG_COMA, 0); /* ~COMA_RESET */
+ if (!ret)
+ ret = ov6650_reg_rmw(client, REG_COMB, 0, COMB_BAND_FILTER);
+
+ return ret;
+}
+
+static int ov6650_video_probe(struct i2c_client *client)
+{
+ u8 pidh, pidl, midh, midl;
+ int ret = 0;
+
+ /*
+ * check and show product ID and manufacturer ID
+ */
+ ret = ov6650_reg_read(client, REG_PIDH, &pidh);
+ if (!ret)
+ ret = ov6650_reg_read(client, REG_PIDL, &pidl);
+ if (!ret)
+ ret = ov6650_reg_read(client, REG_MIDH, &midh);
+ if (!ret)
+ ret = ov6650_reg_read(client, REG_MIDL, &midl);
+
+ if (ret)
+ return ret;
+
+ if ((pidh != OV6650_PIDH) || (pidl != OV6650_PIDL)) {
+ dev_err(&client->dev, "Product ID error 0x%02x:0x%02x\n",
+ pidh, pidl);
+ return -ENODEV;
+ }
+
+ dev_info(&client->dev,
+ "ov6650 Product ID 0x%02x:0x%02x Manufacturer ID 0x%02x:0x%02x\n",
+ pidh, pidl, midh, midl);
+
+ ret = ov6650_reset(client);
+ if (!ret)
+ ret = ov6650_prog_dflt(client);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops ov6550_ctrl_ops = {
+ .g_volatile_ctrl = ov6550_g_volatile_ctrl,
+ .s_ctrl = ov6550_s_ctrl,
+};
+
+static struct v4l2_subdev_core_ops ov6650_core_ops = {
+ .g_chip_ident = ov6650_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = ov6650_get_register,
+ .s_register = ov6650_set_register,
+#endif
+};
+
+/* Request bus settings on camera side */
+static int ov6650_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_MASTER |
+ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+/* Alter bus settings on camera side */
+static int ov6650_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ unsigned long flags = soc_camera_apply_board_flags(icl, cfg);
+ int ret;
+
+ if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
+ ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_PCLK_RISING, 0);
+ else
+ ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_PCLK_RISING);
+ if (ret)
+ return ret;
+
+ if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ ret = ov6650_reg_rmw(client, REG_COMF, COMF_HREF_LOW, 0);
+ else
+ ret = ov6650_reg_rmw(client, REG_COMF, 0, COMF_HREF_LOW);
+ if (ret)
+ return ret;
+
+ if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
+ ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_VSYNC_HIGH, 0);
+ else
+ ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_VSYNC_HIGH);
+
+ return ret;
+}
+
+static struct v4l2_subdev_video_ops ov6650_video_ops = {
+ .s_stream = ov6650_s_stream,
+ .g_mbus_fmt = ov6650_g_fmt,
+ .s_mbus_fmt = ov6650_s_fmt,
+ .try_mbus_fmt = ov6650_try_fmt,
+ .enum_mbus_fmt = ov6650_enum_fmt,
+ .cropcap = ov6650_cropcap,
+ .g_crop = ov6650_g_crop,
+ .s_crop = ov6650_s_crop,
+ .g_parm = ov6650_g_parm,
+ .s_parm = ov6650_s_parm,
+ .g_mbus_config = ov6650_g_mbus_config,
+ .s_mbus_config = ov6650_s_mbus_config,
+};
+
+static struct v4l2_subdev_ops ov6650_subdev_ops = {
+ .core = &ov6650_core_ops,
+ .video = &ov6650_video_ops,
+};
+
+/*
+ * i2c_driver function
+ */
+static int ov6650_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct ov6650 *priv;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ int ret;
+
+ if (!icl) {
+ dev_err(&client->dev, "Missing platform_data for driver\n");
+ return -EINVAL;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&client->dev,
+ "Failed to allocate memory for private data!\n");
+ return -ENOMEM;
+ }
+
+ v4l2_i2c_subdev_init(&priv->subdev, client, &ov6650_subdev_ops);
+ v4l2_ctrl_handler_init(&priv->hdl, 13);
+ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ priv->autogain = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ priv->gain = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_GAIN, 0, 0x3f, 1, DEF_GAIN);
+ priv->autowb = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+ priv->blue = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_BLUE_BALANCE, 0, 0xff, 1, DEF_BLUE);
+ priv->red = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_RED_BALANCE, 0, 0xff, 1, DEF_RED);
+ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 0xf, 1, 0x8);
+ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_HUE, 0, HUE_MASK, 1, DEF_HUE);
+ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 0xff, 1, 0x80);
+ priv->autoexposure = v4l2_ctrl_new_std_menu(&priv->hdl,
+ &ov6550_ctrl_ops, V4L2_CID_EXPOSURE_AUTO,
+ V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO);
+ priv->exposure = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 0xff, 1, DEF_AECH);
+ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_GAMMA, 0, 0xff, 1, 0x12);
+
+ priv->subdev.ctrl_handler = &priv->hdl;
+ if (priv->hdl.error) {
+ int err = priv->hdl.error;
+
+ kfree(priv);
+ return err;
+ }
+ v4l2_ctrl_auto_cluster(2, &priv->autogain, 0, true);
+ v4l2_ctrl_auto_cluster(3, &priv->autowb, 0, true);
+ v4l2_ctrl_auto_cluster(2, &priv->autoexposure,
+ V4L2_EXPOSURE_MANUAL, true);
+
+ priv->rect.left = DEF_HSTRT << 1;
+ priv->rect.top = DEF_VSTRT << 1;
+ priv->rect.width = W_CIF;
+ priv->rect.height = H_CIF;
+ priv->half_scale = false;
+ priv->code = V4L2_MBUS_FMT_YUYV8_2X8;
+ priv->colorspace = V4L2_COLORSPACE_JPEG;
+
+ ret = ov6650_video_probe(client);
+ if (!ret)
+ ret = v4l2_ctrl_handler_setup(&priv->hdl);
+
+ if (ret) {
+ v4l2_ctrl_handler_free(&priv->hdl);
+ kfree(priv);
+ }
+
+ return ret;
+}
+
+static int ov6650_remove(struct i2c_client *client)
+{
+ struct ov6650 *priv = to_ov6650(client);
+
+ v4l2_device_unregister_subdev(&priv->subdev);
+ v4l2_ctrl_handler_free(&priv->hdl);
+ kfree(priv);
+ return 0;
+}
+
+static const struct i2c_device_id ov6650_id[] = {
+ { "ov6650", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ov6650_id);
+
+static struct i2c_driver ov6650_i2c_driver = {
+ .driver = {
+ .name = "ov6650",
+ },
+ .probe = ov6650_probe,
+ .remove = ov6650_remove,
+ .id_table = ov6650_id,
+};
+
+module_i2c_driver(ov6650_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV6650");
+MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c
new file mode 100644
index 00000000..e7c82b29
--- /dev/null
+++ b/drivers/media/video/ov7670.c
@@ -0,0 +1,1586 @@
+/*
+ * A V4L2 driver for OmniVision OV7670 cameras.
+ *
+ * Copyright 2006 One Laptop Per Child Association, Inc. Written
+ * by Jonathan Corbet with substantial inspiration from Mark
+ * McClelland's ovcamchip code.
+ *
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
+ *
+ * This file may be distributed under the terms of the GNU General
+ * Public License, version 2.
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-mediabus.h>
+#include <media/ov7670.h>
+
+MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
+MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors");
+MODULE_LICENSE("GPL");
+
+static bool debug;
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+/*
+ * Basic window sizes. These probably belong somewhere more globally
+ * useful.
+ */
+#define VGA_WIDTH 640
+#define VGA_HEIGHT 480
+#define QVGA_WIDTH 320
+#define QVGA_HEIGHT 240
+#define CIF_WIDTH 352
+#define CIF_HEIGHT 288
+#define QCIF_WIDTH 176
+#define QCIF_HEIGHT 144
+
+/*
+ * The 7670 sits on i2c with ID 0x42
+ */
+#define OV7670_I2C_ADDR 0x42
+
+/* Registers */
+#define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */
+#define REG_BLUE 0x01 /* blue gain */
+#define REG_RED 0x02 /* red gain */
+#define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */
+#define REG_COM1 0x04 /* Control 1 */
+#define COM1_CCIR656 0x40 /* CCIR656 enable */
+#define REG_BAVE 0x05 /* U/B Average level */
+#define REG_GbAVE 0x06 /* Y/Gb Average level */
+#define REG_AECHH 0x07 /* AEC MS 5 bits */
+#define REG_RAVE 0x08 /* V/R Average level */
+#define REG_COM2 0x09 /* Control 2 */
+#define COM2_SSLEEP 0x10 /* Soft sleep mode */
+#define REG_PID 0x0a /* Product ID MSB */
+#define REG_VER 0x0b /* Product ID LSB */
+#define REG_COM3 0x0c /* Control 3 */
+#define COM3_SWAP 0x40 /* Byte swap */
+#define COM3_SCALEEN 0x08 /* Enable scaling */
+#define COM3_DCWEN 0x04 /* Enable downsamp/crop/window */
+#define REG_COM4 0x0d /* Control 4 */
+#define REG_COM5 0x0e /* All "reserved" */
+#define REG_COM6 0x0f /* Control 6 */
+#define REG_AECH 0x10 /* More bits of AEC value */
+#define REG_CLKRC 0x11 /* Clocl control */
+#define CLK_EXT 0x40 /* Use external clock directly */
+#define CLK_SCALE 0x3f /* Mask for internal clock scale */
+#define REG_COM7 0x12 /* Control 7 */
+#define COM7_RESET 0x80 /* Register reset */
+#define COM7_FMT_MASK 0x38
+#define COM7_FMT_VGA 0x00
+#define COM7_FMT_CIF 0x20 /* CIF format */
+#define COM7_FMT_QVGA 0x10 /* QVGA format */
+#define COM7_FMT_QCIF 0x08 /* QCIF format */
+#define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */
+#define COM7_YUV 0x00 /* YUV */
+#define COM7_BAYER 0x01 /* Bayer format */
+#define COM7_PBAYER 0x05 /* "Processed bayer" */
+#define REG_COM8 0x13 /* Control 8 */
+#define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */
+#define COM8_AECSTEP 0x40 /* Unlimited AEC step size */
+#define COM8_BFILT 0x20 /* Band filter enable */
+#define COM8_AGC 0x04 /* Auto gain enable */
+#define COM8_AWB 0x02 /* White balance enable */
+#define COM8_AEC 0x01 /* Auto exposure enable */
+#define REG_COM9 0x14 /* Control 9 - gain ceiling */
+#define REG_COM10 0x15 /* Control 10 */
+#define COM10_HSYNC 0x40 /* HSYNC instead of HREF */
+#define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */
+#define COM10_HREF_REV 0x08 /* Reverse HREF */
+#define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */
+#define COM10_VS_NEG 0x02 /* VSYNC negative */
+#define COM10_HS_NEG 0x01 /* HSYNC negative */
+#define REG_HSTART 0x17 /* Horiz start high bits */
+#define REG_HSTOP 0x18 /* Horiz stop high bits */
+#define REG_VSTART 0x19 /* Vert start high bits */
+#define REG_VSTOP 0x1a /* Vert stop high bits */
+#define REG_PSHFT 0x1b /* Pixel delay after HREF */
+#define REG_MIDH 0x1c /* Manuf. ID high */
+#define REG_MIDL 0x1d /* Manuf. ID low */
+#define REG_MVFP 0x1e /* Mirror / vflip */
+#define MVFP_MIRROR 0x20 /* Mirror image */
+#define MVFP_FLIP 0x10 /* Vertical flip */
+
+#define REG_AEW 0x24 /* AGC upper limit */
+#define REG_AEB 0x25 /* AGC lower limit */
+#define REG_VPT 0x26 /* AGC/AEC fast mode op region */
+#define REG_HSYST 0x30 /* HSYNC rising edge delay */
+#define REG_HSYEN 0x31 /* HSYNC falling edge delay */
+#define REG_HREF 0x32 /* HREF pieces */
+#define REG_TSLB 0x3a /* lots of stuff */
+#define TSLB_YLAST 0x04 /* UYVY or VYUY - see com13 */
+#define REG_COM11 0x3b /* Control 11 */
+#define COM11_NIGHT 0x80 /* NIght mode enable */
+#define COM11_NMFR 0x60 /* Two bit NM frame rate */
+#define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */
+#define COM11_50HZ 0x08 /* Manual 50Hz select */
+#define COM11_EXP 0x02
+#define REG_COM12 0x3c /* Control 12 */
+#define COM12_HREF 0x80 /* HREF always */
+#define REG_COM13 0x3d /* Control 13 */
+#define COM13_GAMMA 0x80 /* Gamma enable */
+#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */
+#define COM13_UVSWAP 0x01 /* V before U - w/TSLB */
+#define REG_COM14 0x3e /* Control 14 */
+#define COM14_DCWEN 0x10 /* DCW/PCLK-scale enable */
+#define REG_EDGE 0x3f /* Edge enhancement factor */
+#define REG_COM15 0x40 /* Control 15 */
+#define COM15_R10F0 0x00 /* Data range 10 to F0 */
+#define COM15_R01FE 0x80 /* 01 to FE */
+#define COM15_R00FF 0xc0 /* 00 to FF */
+#define COM15_RGB565 0x10 /* RGB565 output */
+#define COM15_RGB555 0x30 /* RGB555 output */
+#define REG_COM16 0x41 /* Control 16 */
+#define COM16_AWBGAIN 0x08 /* AWB gain enable */
+#define REG_COM17 0x42 /* Control 17 */
+#define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */
+#define COM17_CBAR 0x08 /* DSP Color bar */
+
+/*
+ * This matrix defines how the colors are generated, must be
+ * tweaked to adjust hue and saturation.
+ *
+ * Order: v-red, v-green, v-blue, u-red, u-green, u-blue
+ *
+ * They are nine-bit signed quantities, with the sign bit
+ * stored in 0x58. Sign for v-red is bit 0, and up from there.
+ */
+#define REG_CMATRIX_BASE 0x4f
+#define CMATRIX_LEN 6
+#define REG_CMATRIX_SIGN 0x58
+
+
+#define REG_BRIGHT 0x55 /* Brightness */
+#define REG_CONTRAS 0x56 /* Contrast control */
+
+#define REG_GFIX 0x69 /* Fix gain control */
+
+#define REG_REG76 0x76 /* OV's name */
+#define R76_BLKPCOR 0x80 /* Black pixel correction enable */
+#define R76_WHTPCOR 0x40 /* White pixel correction enable */
+
+#define REG_RGB444 0x8c /* RGB 444 control */
+#define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */
+#define R444_RGBX 0x01 /* Empty nibble at end */
+
+#define REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */
+#define REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */
+
+#define REG_BD50MAX 0xa5 /* 50hz banding step limit */
+#define REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */
+#define REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */
+#define REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */
+#define REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */
+#define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */
+#define REG_BD60MAX 0xab /* 60hz banding step limit */
+
+
+/*
+ * Information we maintain about a known sensor.
+ */
+struct ov7670_format_struct; /* coming later */
+struct ov7670_info {
+ struct v4l2_subdev sd;
+ struct ov7670_format_struct *fmt; /* Current format */
+ unsigned char sat; /* Saturation value */
+ int hue; /* Hue value */
+ int min_width; /* Filter out smaller sizes */
+ int min_height; /* Filter out smaller sizes */
+ int clock_speed; /* External clock speed (MHz) */
+ u8 clkrc; /* Clock divider value */
+ bool use_smbus; /* Use smbus I/O instead of I2C */
+};
+
+static inline struct ov7670_info *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct ov7670_info, sd);
+}
+
+
+
+/*
+ * The default register settings, as obtained from OmniVision. There
+ * is really no making sense of most of these - lots of "reserved" values
+ * and such.
+ *
+ * These settings give VGA YUYV.
+ */
+
+struct regval_list {
+ unsigned char reg_num;
+ unsigned char value;
+};
+
+static struct regval_list ov7670_default_regs[] = {
+ { REG_COM7, COM7_RESET },
+/*
+ * Clock scale: 3 = 15fps
+ * 2 = 20fps
+ * 1 = 30fps
+ */
+ { REG_CLKRC, 0x1 }, /* OV: clock scale (30 fps) */
+ { REG_TSLB, 0x04 }, /* OV */
+ { REG_COM7, 0 }, /* VGA */
+ /*
+ * Set the hardware window. These values from OV don't entirely
+ * make sense - hstop is less than hstart. But they work...
+ */
+ { REG_HSTART, 0x13 }, { REG_HSTOP, 0x01 },
+ { REG_HREF, 0xb6 }, { REG_VSTART, 0x02 },
+ { REG_VSTOP, 0x7a }, { REG_VREF, 0x0a },
+
+ { REG_COM3, 0 }, { REG_COM14, 0 },
+ /* Mystery scaling numbers */
+ { 0x70, 0x3a }, { 0x71, 0x35 },
+ { 0x72, 0x11 }, { 0x73, 0xf0 },
+ { 0xa2, 0x02 }, { REG_COM10, 0x0 },
+
+ /* Gamma curve values */
+ { 0x7a, 0x20 }, { 0x7b, 0x10 },
+ { 0x7c, 0x1e }, { 0x7d, 0x35 },
+ { 0x7e, 0x5a }, { 0x7f, 0x69 },
+ { 0x80, 0x76 }, { 0x81, 0x80 },
+ { 0x82, 0x88 }, { 0x83, 0x8f },
+ { 0x84, 0x96 }, { 0x85, 0xa3 },
+ { 0x86, 0xaf }, { 0x87, 0xc4 },
+ { 0x88, 0xd7 }, { 0x89, 0xe8 },
+
+ /* AGC and AEC parameters. Note we start by disabling those features,
+ then turn them only after tweaking the values. */
+ { REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT },
+ { REG_GAIN, 0 }, { REG_AECH, 0 },
+ { REG_COM4, 0x40 }, /* magic reserved bit */
+ { REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */
+ { REG_BD50MAX, 0x05 }, { REG_BD60MAX, 0x07 },
+ { REG_AEW, 0x95 }, { REG_AEB, 0x33 },
+ { REG_VPT, 0xe3 }, { REG_HAECC1, 0x78 },
+ { REG_HAECC2, 0x68 }, { 0xa1, 0x03 }, /* magic */
+ { REG_HAECC3, 0xd8 }, { REG_HAECC4, 0xd8 },
+ { REG_HAECC5, 0xf0 }, { REG_HAECC6, 0x90 },
+ { REG_HAECC7, 0x94 },
+ { REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC },
+
+ /* Almost all of these are magic "reserved" values. */
+ { REG_COM5, 0x61 }, { REG_COM6, 0x4b },
+ { 0x16, 0x02 }, { REG_MVFP, 0x07 },
+ { 0x21, 0x02 }, { 0x22, 0x91 },
+ { 0x29, 0x07 }, { 0x33, 0x0b },
+ { 0x35, 0x0b }, { 0x37, 0x1d },
+ { 0x38, 0x71 }, { 0x39, 0x2a },
+ { REG_COM12, 0x78 }, { 0x4d, 0x40 },
+ { 0x4e, 0x20 }, { REG_GFIX, 0 },
+ { 0x6b, 0x4a }, { 0x74, 0x10 },
+ { 0x8d, 0x4f }, { 0x8e, 0 },
+ { 0x8f, 0 }, { 0x90, 0 },
+ { 0x91, 0 }, { 0x96, 0 },
+ { 0x9a, 0 }, { 0xb0, 0x84 },
+ { 0xb1, 0x0c }, { 0xb2, 0x0e },
+ { 0xb3, 0x82 }, { 0xb8, 0x0a },
+
+ /* More reserved magic, some of which tweaks white balance */
+ { 0x43, 0x0a }, { 0x44, 0xf0 },
+ { 0x45, 0x34 }, { 0x46, 0x58 },
+ { 0x47, 0x28 }, { 0x48, 0x3a },
+ { 0x59, 0x88 }, { 0x5a, 0x88 },
+ { 0x5b, 0x44 }, { 0x5c, 0x67 },
+ { 0x5d, 0x49 }, { 0x5e, 0x0e },
+ { 0x6c, 0x0a }, { 0x6d, 0x55 },
+ { 0x6e, 0x11 }, { 0x6f, 0x9f }, /* "9e for advance AWB" */
+ { 0x6a, 0x40 }, { REG_BLUE, 0x40 },
+ { REG_RED, 0x60 },
+ { REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC|COM8_AWB },
+
+ /* Matrix coefficients */
+ { 0x4f, 0x80 }, { 0x50, 0x80 },
+ { 0x51, 0 }, { 0x52, 0x22 },
+ { 0x53, 0x5e }, { 0x54, 0x80 },
+ { 0x58, 0x9e },
+
+ { REG_COM16, COM16_AWBGAIN }, { REG_EDGE, 0 },
+ { 0x75, 0x05 }, { 0x76, 0xe1 },
+ { 0x4c, 0 }, { 0x77, 0x01 },
+ { REG_COM13, 0xc3 }, { 0x4b, 0x09 },
+ { 0xc9, 0x60 }, { REG_COM16, 0x38 },
+ { 0x56, 0x40 },
+
+ { 0x34, 0x11 }, { REG_COM11, COM11_EXP|COM11_HZAUTO },
+ { 0xa4, 0x88 }, { 0x96, 0 },
+ { 0x97, 0x30 }, { 0x98, 0x20 },
+ { 0x99, 0x30 }, { 0x9a, 0x84 },
+ { 0x9b, 0x29 }, { 0x9c, 0x03 },
+ { 0x9d, 0x4c }, { 0x9e, 0x3f },
+ { 0x78, 0x04 },
+
+ /* Extra-weird stuff. Some sort of multiplexor register */
+ { 0x79, 0x01 }, { 0xc8, 0xf0 },
+ { 0x79, 0x0f }, { 0xc8, 0x00 },
+ { 0x79, 0x10 }, { 0xc8, 0x7e },
+ { 0x79, 0x0a }, { 0xc8, 0x80 },
+ { 0x79, 0x0b }, { 0xc8, 0x01 },
+ { 0x79, 0x0c }, { 0xc8, 0x0f },
+ { 0x79, 0x0d }, { 0xc8, 0x20 },
+ { 0x79, 0x09 }, { 0xc8, 0x80 },
+ { 0x79, 0x02 }, { 0xc8, 0xc0 },
+ { 0x79, 0x03 }, { 0xc8, 0x40 },
+ { 0x79, 0x05 }, { 0xc8, 0x30 },
+ { 0x79, 0x26 },
+
+ { 0xff, 0xff }, /* END MARKER */
+};
+
+
+/*
+ * Here we'll try to encapsulate the changes for just the output
+ * video format.
+ *
+ * RGB656 and YUV422 come from OV; RGB444 is homebrewed.
+ *
+ * IMPORTANT RULE: the first entry must be for COM7, see ov7670_s_fmt for why.
+ */
+
+
+static struct regval_list ov7670_fmt_yuv422[] = {
+ { REG_COM7, 0x0 }, /* Selects YUV mode */
+ { REG_RGB444, 0 }, /* No RGB444 please */
+ { REG_COM1, 0 }, /* CCIR601 */
+ { REG_COM15, COM15_R00FF },
+ { REG_COM9, 0x18 }, /* 4x gain ceiling; 0x8 is reserved bit */
+ { 0x4f, 0x80 }, /* "matrix coefficient 1" */
+ { 0x50, 0x80 }, /* "matrix coefficient 2" */
+ { 0x51, 0 }, /* vb */
+ { 0x52, 0x22 }, /* "matrix coefficient 4" */
+ { 0x53, 0x5e }, /* "matrix coefficient 5" */
+ { 0x54, 0x80 }, /* "matrix coefficient 6" */
+ { REG_COM13, COM13_GAMMA|COM13_UVSAT },
+ { 0xff, 0xff },
+};
+
+static struct regval_list ov7670_fmt_rgb565[] = {
+ { REG_COM7, COM7_RGB }, /* Selects RGB mode */
+ { REG_RGB444, 0 }, /* No RGB444 please */
+ { REG_COM1, 0x0 }, /* CCIR601 */
+ { REG_COM15, COM15_RGB565 },
+ { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */
+ { 0x4f, 0xb3 }, /* "matrix coefficient 1" */
+ { 0x50, 0xb3 }, /* "matrix coefficient 2" */
+ { 0x51, 0 }, /* vb */
+ { 0x52, 0x3d }, /* "matrix coefficient 4" */
+ { 0x53, 0xa7 }, /* "matrix coefficient 5" */
+ { 0x54, 0xe4 }, /* "matrix coefficient 6" */
+ { REG_COM13, COM13_GAMMA|COM13_UVSAT },
+ { 0xff, 0xff },
+};
+
+static struct regval_list ov7670_fmt_rgb444[] = {
+ { REG_COM7, COM7_RGB }, /* Selects RGB mode */
+ { REG_RGB444, R444_ENABLE }, /* Enable xxxxrrrr ggggbbbb */
+ { REG_COM1, 0x0 }, /* CCIR601 */
+ { REG_COM15, COM15_R01FE|COM15_RGB565 }, /* Data range needed? */
+ { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */
+ { 0x4f, 0xb3 }, /* "matrix coefficient 1" */
+ { 0x50, 0xb3 }, /* "matrix coefficient 2" */
+ { 0x51, 0 }, /* vb */
+ { 0x52, 0x3d }, /* "matrix coefficient 4" */
+ { 0x53, 0xa7 }, /* "matrix coefficient 5" */
+ { 0x54, 0xe4 }, /* "matrix coefficient 6" */
+ { REG_COM13, COM13_GAMMA|COM13_UVSAT|0x2 }, /* Magic rsvd bit */
+ { 0xff, 0xff },
+};
+
+static struct regval_list ov7670_fmt_raw[] = {
+ { REG_COM7, COM7_BAYER },
+ { REG_COM13, 0x08 }, /* No gamma, magic rsvd bit */
+ { REG_COM16, 0x3d }, /* Edge enhancement, denoise */
+ { REG_REG76, 0xe1 }, /* Pix correction, magic rsvd */
+ { 0xff, 0xff },
+};
+
+
+
+/*
+ * Low-level register I/O.
+ *
+ * Note that there are two versions of these. On the XO 1, the
+ * i2c controller only does SMBUS, so that's what we use. The
+ * ov7670 is not really an SMBUS device, though, so the communication
+ * is not always entirely reliable.
+ */
+static int ov7670_read_smbus(struct v4l2_subdev *sd, unsigned char reg,
+ unsigned char *value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, reg);
+ if (ret >= 0) {
+ *value = (unsigned char)ret;
+ ret = 0;
+ }
+ return ret;
+}
+
+
+static int ov7670_write_smbus(struct v4l2_subdev *sd, unsigned char reg,
+ unsigned char value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret = i2c_smbus_write_byte_data(client, reg, value);
+
+ if (reg == REG_COM7 && (value & COM7_RESET))
+ msleep(5); /* Wait for reset to run */
+ return ret;
+}
+
+/*
+ * On most platforms, we'd rather do straight i2c I/O.
+ */
+static int ov7670_read_i2c(struct v4l2_subdev *sd, unsigned char reg,
+ unsigned char *value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 data = reg;
+ struct i2c_msg msg;
+ int ret;
+
+ /*
+ * Send out the register address...
+ */
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = 1;
+ msg.buf = &data;
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret < 0) {
+ printk(KERN_ERR "Error %d on register write\n", ret);
+ return ret;
+ }
+ /*
+ * ...then read back the result.
+ */
+ msg.flags = I2C_M_RD;
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret >= 0) {
+ *value = data;
+ ret = 0;
+ }
+ return ret;
+}
+
+
+static int ov7670_write_i2c(struct v4l2_subdev *sd, unsigned char reg,
+ unsigned char value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct i2c_msg msg;
+ unsigned char data[2] = { reg, value };
+ int ret;
+
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = 2;
+ msg.buf = data;
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret > 0)
+ ret = 0;
+ if (reg == REG_COM7 && (value & COM7_RESET))
+ msleep(5); /* Wait for reset to run */
+ return ret;
+}
+
+static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg,
+ unsigned char *value)
+{
+ struct ov7670_info *info = to_state(sd);
+ if (info->use_smbus)
+ return ov7670_read_smbus(sd, reg, value);
+ else
+ return ov7670_read_i2c(sd, reg, value);
+}
+
+static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg,
+ unsigned char value)
+{
+ struct ov7670_info *info = to_state(sd);
+ if (info->use_smbus)
+ return ov7670_write_smbus(sd, reg, value);
+ else
+ return ov7670_write_i2c(sd, reg, value);
+}
+
+/*
+ * Write a list of register settings; ff/ff stops the process.
+ */
+static int ov7670_write_array(struct v4l2_subdev *sd, struct regval_list *vals)
+{
+ while (vals->reg_num != 0xff || vals->value != 0xff) {
+ int ret = ov7670_write(sd, vals->reg_num, vals->value);
+ if (ret < 0)
+ return ret;
+ vals++;
+ }
+ return 0;
+}
+
+
+/*
+ * Stuff that knows about the sensor.
+ */
+static int ov7670_reset(struct v4l2_subdev *sd, u32 val)
+{
+ ov7670_write(sd, REG_COM7, COM7_RESET);
+ msleep(1);
+ return 0;
+}
+
+
+static int ov7670_init(struct v4l2_subdev *sd, u32 val)
+{
+ return ov7670_write_array(sd, ov7670_default_regs);
+}
+
+
+
+static int ov7670_detect(struct v4l2_subdev *sd)
+{
+ unsigned char v;
+ int ret;
+
+ ret = ov7670_init(sd, 0);
+ if (ret < 0)
+ return ret;
+ ret = ov7670_read(sd, REG_MIDH, &v);
+ if (ret < 0)
+ return ret;
+ if (v != 0x7f) /* OV manuf. id. */
+ return -ENODEV;
+ ret = ov7670_read(sd, REG_MIDL, &v);
+ if (ret < 0)
+ return ret;
+ if (v != 0xa2)
+ return -ENODEV;
+ /*
+ * OK, we know we have an OmniVision chip...but which one?
+ */
+ ret = ov7670_read(sd, REG_PID, &v);
+ if (ret < 0)
+ return ret;
+ if (v != 0x76) /* PID + VER = 0x76 / 0x73 */
+ return -ENODEV;
+ ret = ov7670_read(sd, REG_VER, &v);
+ if (ret < 0)
+ return ret;
+ if (v != 0x73) /* PID + VER = 0x76 / 0x73 */
+ return -ENODEV;
+ return 0;
+}
+
+
+/*
+ * Store information about the video data format. The color matrix
+ * is deeply tied into the format, so keep the relevant values here.
+ * The magic matrix numbers come from OmniVision.
+ */
+static struct ov7670_format_struct {
+ enum v4l2_mbus_pixelcode mbus_code;
+ enum v4l2_colorspace colorspace;
+ struct regval_list *regs;
+ int cmatrix[CMATRIX_LEN];
+} ov7670_formats[] = {
+ {
+ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .regs = ov7670_fmt_yuv422,
+ .cmatrix = { 128, -128, 0, -34, -94, 128 },
+ },
+ {
+ .mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .regs = ov7670_fmt_rgb444,
+ .cmatrix = { 179, -179, 0, -61, -176, 228 },
+ },
+ {
+ .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .regs = ov7670_fmt_rgb565,
+ .cmatrix = { 179, -179, 0, -61, -176, 228 },
+ },
+ {
+ .mbus_code = V4L2_MBUS_FMT_SBGGR8_1X8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .regs = ov7670_fmt_raw,
+ .cmatrix = { 0, 0, 0, 0, 0, 0 },
+ },
+};
+#define N_OV7670_FMTS ARRAY_SIZE(ov7670_formats)
+
+
+/*
+ * Then there is the issue of window sizes. Try to capture the info here.
+ */
+
+/*
+ * QCIF mode is done (by OV) in a very strange way - it actually looks like
+ * VGA with weird scaling options - they do *not* use the canned QCIF mode
+ * which is allegedly provided by the sensor. So here's the weird register
+ * settings.
+ */
+static struct regval_list ov7670_qcif_regs[] = {
+ { REG_COM3, COM3_SCALEEN|COM3_DCWEN },
+ { REG_COM3, COM3_DCWEN },
+ { REG_COM14, COM14_DCWEN | 0x01},
+ { 0x73, 0xf1 },
+ { 0xa2, 0x52 },
+ { 0x7b, 0x1c },
+ { 0x7c, 0x28 },
+ { 0x7d, 0x3c },
+ { 0x7f, 0x69 },
+ { REG_COM9, 0x38 },
+ { 0xa1, 0x0b },
+ { 0x74, 0x19 },
+ { 0x9a, 0x80 },
+ { 0x43, 0x14 },
+ { REG_COM13, 0xc0 },
+ { 0xff, 0xff },
+};
+
+static struct ov7670_win_size {
+ int width;
+ int height;
+ unsigned char com7_bit;
+ int hstart; /* Start/stop values for the camera. Note */
+ int hstop; /* that they do not always make complete */
+ int vstart; /* sense to humans, but evidently the sensor */
+ int vstop; /* will do the right thing... */
+ struct regval_list *regs; /* Regs to tweak */
+/* h/vref stuff */
+} ov7670_win_sizes[] = {
+ /* VGA */
+ {
+ .width = VGA_WIDTH,
+ .height = VGA_HEIGHT,
+ .com7_bit = COM7_FMT_VGA,
+ .hstart = 158, /* These values from */
+ .hstop = 14, /* Omnivision */
+ .vstart = 10,
+ .vstop = 490,
+ .regs = NULL,
+ },
+ /* CIF */
+ {
+ .width = CIF_WIDTH,
+ .height = CIF_HEIGHT,
+ .com7_bit = COM7_FMT_CIF,
+ .hstart = 170, /* Empirically determined */
+ .hstop = 90,
+ .vstart = 14,
+ .vstop = 494,
+ .regs = NULL,
+ },
+ /* QVGA */
+ {
+ .width = QVGA_WIDTH,
+ .height = QVGA_HEIGHT,
+ .com7_bit = COM7_FMT_QVGA,
+ .hstart = 168, /* Empirically determined */
+ .hstop = 24,
+ .vstart = 12,
+ .vstop = 492,
+ .regs = NULL,
+ },
+ /* QCIF */
+ {
+ .width = QCIF_WIDTH,
+ .height = QCIF_HEIGHT,
+ .com7_bit = COM7_FMT_VGA, /* see comment above */
+ .hstart = 456, /* Empirically determined */
+ .hstop = 24,
+ .vstart = 14,
+ .vstop = 494,
+ .regs = ov7670_qcif_regs,
+ },
+};
+
+#define N_WIN_SIZES (ARRAY_SIZE(ov7670_win_sizes))
+
+
+/*
+ * Store a set of start/stop values into the camera.
+ */
+static int ov7670_set_hw(struct v4l2_subdev *sd, int hstart, int hstop,
+ int vstart, int vstop)
+{
+ int ret;
+ unsigned char v;
+/*
+ * Horizontal: 11 bits, top 8 live in hstart and hstop. Bottom 3 of
+ * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is
+ * a mystery "edge offset" value in the top two bits of href.
+ */
+ ret = ov7670_write(sd, REG_HSTART, (hstart >> 3) & 0xff);
+ ret += ov7670_write(sd, REG_HSTOP, (hstop >> 3) & 0xff);
+ ret += ov7670_read(sd, REG_HREF, &v);
+ v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7);
+ msleep(10);
+ ret += ov7670_write(sd, REG_HREF, v);
+/*
+ * Vertical: similar arrangement, but only 10 bits.
+ */
+ ret += ov7670_write(sd, REG_VSTART, (vstart >> 2) & 0xff);
+ ret += ov7670_write(sd, REG_VSTOP, (vstop >> 2) & 0xff);
+ ret += ov7670_read(sd, REG_VREF, &v);
+ v = (v & 0xf0) | ((vstop & 0x3) << 2) | (vstart & 0x3);
+ msleep(10);
+ ret += ov7670_write(sd, REG_VREF, v);
+ return ret;
+}
+
+
+static int ov7670_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= N_OV7670_FMTS)
+ return -EINVAL;
+
+ *code = ov7670_formats[index].mbus_code;
+ return 0;
+}
+
+static int ov7670_try_fmt_internal(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt,
+ struct ov7670_format_struct **ret_fmt,
+ struct ov7670_win_size **ret_wsize)
+{
+ int index;
+ struct ov7670_win_size *wsize;
+
+ for (index = 0; index < N_OV7670_FMTS; index++)
+ if (ov7670_formats[index].mbus_code == fmt->code)
+ break;
+ if (index >= N_OV7670_FMTS) {
+ /* default to first format */
+ index = 0;
+ fmt->code = ov7670_formats[0].mbus_code;
+ }
+ if (ret_fmt != NULL)
+ *ret_fmt = ov7670_formats + index;
+ /*
+ * Fields: the OV devices claim to be progressive.
+ */
+ fmt->field = V4L2_FIELD_NONE;
+ /*
+ * Round requested image size down to the nearest
+ * we support, but not below the smallest.
+ */
+ for (wsize = ov7670_win_sizes; wsize < ov7670_win_sizes + N_WIN_SIZES;
+ wsize++)
+ if (fmt->width >= wsize->width && fmt->height >= wsize->height)
+ break;
+ if (wsize >= ov7670_win_sizes + N_WIN_SIZES)
+ wsize--; /* Take the smallest one */
+ if (ret_wsize != NULL)
+ *ret_wsize = wsize;
+ /*
+ * Note the size we'll actually handle.
+ */
+ fmt->width = wsize->width;
+ fmt->height = wsize->height;
+ fmt->colorspace = ov7670_formats[index].colorspace;
+ return 0;
+}
+
+static int ov7670_try_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ return ov7670_try_fmt_internal(sd, fmt, NULL, NULL);
+}
+
+/*
+ * Set a format.
+ */
+static int ov7670_s_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct ov7670_format_struct *ovfmt;
+ struct ov7670_win_size *wsize;
+ struct ov7670_info *info = to_state(sd);
+ unsigned char com7;
+ int ret;
+
+ ret = ov7670_try_fmt_internal(sd, fmt, &ovfmt, &wsize);
+
+ if (ret)
+ return ret;
+ /*
+ * COM7 is a pain in the ass, it doesn't like to be read then
+ * quickly written afterward. But we have everything we need
+ * to set it absolutely here, as long as the format-specific
+ * register sets list it first.
+ */
+ com7 = ovfmt->regs[0].value;
+ com7 |= wsize->com7_bit;
+ ov7670_write(sd, REG_COM7, com7);
+ /*
+ * Now write the rest of the array. Also store start/stops
+ */
+ ov7670_write_array(sd, ovfmt->regs + 1);
+ ov7670_set_hw(sd, wsize->hstart, wsize->hstop, wsize->vstart,
+ wsize->vstop);
+ ret = 0;
+ if (wsize->regs)
+ ret = ov7670_write_array(sd, wsize->regs);
+ info->fmt = ovfmt;
+
+ /*
+ * If we're running RGB565, we must rewrite clkrc after setting
+ * the other parameters or the image looks poor. If we're *not*
+ * doing RGB565, we must not rewrite clkrc or the image looks
+ * *really* poor.
+ *
+ * (Update) Now that we retain clkrc state, we should be able
+ * to write it unconditionally, and that will make the frame
+ * rate persistent too.
+ */
+ if (ret == 0)
+ ret = ov7670_write(sd, REG_CLKRC, info->clkrc);
+ return 0;
+}
+
+/*
+ * Implement G/S_PARM. There is a "high quality" mode we could try
+ * to do someday; for now, we just do the frame rate tweak.
+ */
+static int ov7670_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+{
+ struct v4l2_captureparm *cp = &parms->parm.capture;
+ struct ov7670_info *info = to_state(sd);
+
+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ memset(cp, 0, sizeof(struct v4l2_captureparm));
+ cp->capability = V4L2_CAP_TIMEPERFRAME;
+ cp->timeperframe.numerator = 1;
+ cp->timeperframe.denominator = info->clock_speed;
+ if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1)
+ cp->timeperframe.denominator /= (info->clkrc & CLK_SCALE);
+ return 0;
+}
+
+static int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+{
+ struct v4l2_captureparm *cp = &parms->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+ struct ov7670_info *info = to_state(sd);
+ int div;
+
+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (cp->extendedmode != 0)
+ return -EINVAL;
+
+ if (tpf->numerator == 0 || tpf->denominator == 0)
+ div = 1; /* Reset to full rate */
+ else
+ div = (tpf->numerator * info->clock_speed) / tpf->denominator;
+ if (div == 0)
+ div = 1;
+ else if (div > CLK_SCALE)
+ div = CLK_SCALE;
+ info->clkrc = (info->clkrc & 0x80) | div;
+ tpf->numerator = 1;
+ tpf->denominator = info->clock_speed / div;
+ return ov7670_write(sd, REG_CLKRC, info->clkrc);
+}
+
+
+/*
+ * Frame intervals. Since frame rates are controlled with the clock
+ * divider, we can only do 30/n for integer n values. So no continuous
+ * or stepwise options. Here we just pick a handful of logical values.
+ */
+
+static int ov7670_frame_rates[] = { 30, 15, 10, 5, 1 };
+
+static int ov7670_enum_frameintervals(struct v4l2_subdev *sd,
+ struct v4l2_frmivalenum *interval)
+{
+ if (interval->index >= ARRAY_SIZE(ov7670_frame_rates))
+ return -EINVAL;
+ interval->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ interval->discrete.numerator = 1;
+ interval->discrete.denominator = ov7670_frame_rates[interval->index];
+ return 0;
+}
+
+/*
+ * Frame size enumeration
+ */
+static int ov7670_enum_framesizes(struct v4l2_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct ov7670_info *info = to_state(sd);
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ /*
+ * If a minimum width/height was requested, filter out the capture
+ * windows that fall outside that.
+ */
+ for (i = 0; i < N_WIN_SIZES; i++) {
+ struct ov7670_win_size *win = &ov7670_win_sizes[index];
+ if (info->min_width && win->width < info->min_width)
+ continue;
+ if (info->min_height && win->height < info->min_height)
+ continue;
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * Code for dealing with controls.
+ */
+
+static int ov7670_store_cmatrix(struct v4l2_subdev *sd,
+ int matrix[CMATRIX_LEN])
+{
+ int i, ret;
+ unsigned char signbits = 0;
+
+ /*
+ * Weird crap seems to exist in the upper part of
+ * the sign bits register, so let's preserve it.
+ */
+ ret = ov7670_read(sd, REG_CMATRIX_SIGN, &signbits);
+ signbits &= 0xc0;
+
+ for (i = 0; i < CMATRIX_LEN; i++) {
+ unsigned char raw;
+
+ if (matrix[i] < 0) {
+ signbits |= (1 << i);
+ if (matrix[i] < -255)
+ raw = 0xff;
+ else
+ raw = (-1 * matrix[i]) & 0xff;
+ }
+ else {
+ if (matrix[i] > 255)
+ raw = 0xff;
+ else
+ raw = matrix[i] & 0xff;
+ }
+ ret += ov7670_write(sd, REG_CMATRIX_BASE + i, raw);
+ }
+ ret += ov7670_write(sd, REG_CMATRIX_SIGN, signbits);
+ return ret;
+}
+
+
+/*
+ * Hue also requires messing with the color matrix. It also requires
+ * trig functions, which tend not to be well supported in the kernel.
+ * So here is a simple table of sine values, 0-90 degrees, in steps
+ * of five degrees. Values are multiplied by 1000.
+ *
+ * The following naive approximate trig functions require an argument
+ * carefully limited to -180 <= theta <= 180.
+ */
+#define SIN_STEP 5
+static const int ov7670_sin_table[] = {
+ 0, 87, 173, 258, 342, 422,
+ 499, 573, 642, 707, 766, 819,
+ 866, 906, 939, 965, 984, 996,
+ 1000
+};
+
+static int ov7670_sine(int theta)
+{
+ int chs = 1;
+ int sine;
+
+ if (theta < 0) {
+ theta = -theta;
+ chs = -1;
+ }
+ if (theta <= 90)
+ sine = ov7670_sin_table[theta/SIN_STEP];
+ else {
+ theta -= 90;
+ sine = 1000 - ov7670_sin_table[theta/SIN_STEP];
+ }
+ return sine*chs;
+}
+
+static int ov7670_cosine(int theta)
+{
+ theta = 90 - theta;
+ if (theta > 180)
+ theta -= 360;
+ else if (theta < -180)
+ theta += 360;
+ return ov7670_sine(theta);
+}
+
+
+
+
+static void ov7670_calc_cmatrix(struct ov7670_info *info,
+ int matrix[CMATRIX_LEN])
+{
+ int i;
+ /*
+ * Apply the current saturation setting first.
+ */
+ for (i = 0; i < CMATRIX_LEN; i++)
+ matrix[i] = (info->fmt->cmatrix[i]*info->sat) >> 7;
+ /*
+ * Then, if need be, rotate the hue value.
+ */
+ if (info->hue != 0) {
+ int sinth, costh, tmpmatrix[CMATRIX_LEN];
+
+ memcpy(tmpmatrix, matrix, CMATRIX_LEN*sizeof(int));
+ sinth = ov7670_sine(info->hue);
+ costh = ov7670_cosine(info->hue);
+
+ matrix[0] = (matrix[3]*sinth + matrix[0]*costh)/1000;
+ matrix[1] = (matrix[4]*sinth + matrix[1]*costh)/1000;
+ matrix[2] = (matrix[5]*sinth + matrix[2]*costh)/1000;
+ matrix[3] = (matrix[3]*costh - matrix[0]*sinth)/1000;
+ matrix[4] = (matrix[4]*costh - matrix[1]*sinth)/1000;
+ matrix[5] = (matrix[5]*costh - matrix[2]*sinth)/1000;
+ }
+}
+
+
+
+static int ov7670_s_sat(struct v4l2_subdev *sd, int value)
+{
+ struct ov7670_info *info = to_state(sd);
+ int matrix[CMATRIX_LEN];
+ int ret;
+
+ info->sat = value;
+ ov7670_calc_cmatrix(info, matrix);
+ ret = ov7670_store_cmatrix(sd, matrix);
+ return ret;
+}
+
+static int ov7670_g_sat(struct v4l2_subdev *sd, __s32 *value)
+{
+ struct ov7670_info *info = to_state(sd);
+
+ *value = info->sat;
+ return 0;
+}
+
+static int ov7670_s_hue(struct v4l2_subdev *sd, int value)
+{
+ struct ov7670_info *info = to_state(sd);
+ int matrix[CMATRIX_LEN];
+ int ret;
+
+ if (value < -180 || value > 180)
+ return -EINVAL;
+ info->hue = value;
+ ov7670_calc_cmatrix(info, matrix);
+ ret = ov7670_store_cmatrix(sd, matrix);
+ return ret;
+}
+
+
+static int ov7670_g_hue(struct v4l2_subdev *sd, __s32 *value)
+{
+ struct ov7670_info *info = to_state(sd);
+
+ *value = info->hue;
+ return 0;
+}
+
+
+/*
+ * Some weird registers seem to store values in a sign/magnitude format!
+ */
+static unsigned char ov7670_sm_to_abs(unsigned char v)
+{
+ if ((v & 0x80) == 0)
+ return v + 128;
+ return 128 - (v & 0x7f);
+}
+
+
+static unsigned char ov7670_abs_to_sm(unsigned char v)
+{
+ if (v > 127)
+ return v & 0x7f;
+ return (128 - v) | 0x80;
+}
+
+static int ov7670_s_brightness(struct v4l2_subdev *sd, int value)
+{
+ unsigned char com8 = 0, v;
+ int ret;
+
+ ov7670_read(sd, REG_COM8, &com8);
+ com8 &= ~COM8_AEC;
+ ov7670_write(sd, REG_COM8, com8);
+ v = ov7670_abs_to_sm(value);
+ ret = ov7670_write(sd, REG_BRIGHT, v);
+ return ret;
+}
+
+static int ov7670_g_brightness(struct v4l2_subdev *sd, __s32 *value)
+{
+ unsigned char v = 0;
+ int ret = ov7670_read(sd, REG_BRIGHT, &v);
+
+ *value = ov7670_sm_to_abs(v);
+ return ret;
+}
+
+static int ov7670_s_contrast(struct v4l2_subdev *sd, int value)
+{
+ return ov7670_write(sd, REG_CONTRAS, (unsigned char) value);
+}
+
+static int ov7670_g_contrast(struct v4l2_subdev *sd, __s32 *value)
+{
+ unsigned char v = 0;
+ int ret = ov7670_read(sd, REG_CONTRAS, &v);
+
+ *value = v;
+ return ret;
+}
+
+static int ov7670_g_hflip(struct v4l2_subdev *sd, __s32 *value)
+{
+ int ret;
+ unsigned char v = 0;
+
+ ret = ov7670_read(sd, REG_MVFP, &v);
+ *value = (v & MVFP_MIRROR) == MVFP_MIRROR;
+ return ret;
+}
+
+
+static int ov7670_s_hflip(struct v4l2_subdev *sd, int value)
+{
+ unsigned char v = 0;
+ int ret;
+
+ ret = ov7670_read(sd, REG_MVFP, &v);
+ if (value)
+ v |= MVFP_MIRROR;
+ else
+ v &= ~MVFP_MIRROR;
+ msleep(10); /* FIXME */
+ ret += ov7670_write(sd, REG_MVFP, v);
+ return ret;
+}
+
+
+
+static int ov7670_g_vflip(struct v4l2_subdev *sd, __s32 *value)
+{
+ int ret;
+ unsigned char v = 0;
+
+ ret = ov7670_read(sd, REG_MVFP, &v);
+ *value = (v & MVFP_FLIP) == MVFP_FLIP;
+ return ret;
+}
+
+
+static int ov7670_s_vflip(struct v4l2_subdev *sd, int value)
+{
+ unsigned char v = 0;
+ int ret;
+
+ ret = ov7670_read(sd, REG_MVFP, &v);
+ if (value)
+ v |= MVFP_FLIP;
+ else
+ v &= ~MVFP_FLIP;
+ msleep(10); /* FIXME */
+ ret += ov7670_write(sd, REG_MVFP, v);
+ return ret;
+}
+
+/*
+ * GAIN is split between REG_GAIN and REG_VREF[7:6]. If one believes
+ * the data sheet, the VREF parts should be the most significant, but
+ * experience shows otherwise. There seems to be little value in
+ * messing with the VREF bits, so we leave them alone.
+ */
+static int ov7670_g_gain(struct v4l2_subdev *sd, __s32 *value)
+{
+ int ret;
+ unsigned char gain;
+
+ ret = ov7670_read(sd, REG_GAIN, &gain);
+ *value = gain;
+ return ret;
+}
+
+static int ov7670_s_gain(struct v4l2_subdev *sd, int value)
+{
+ int ret;
+ unsigned char com8;
+
+ ret = ov7670_write(sd, REG_GAIN, value & 0xff);
+ /* Have to turn off AGC as well */
+ if (ret == 0) {
+ ret = ov7670_read(sd, REG_COM8, &com8);
+ ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AGC);
+ }
+ return ret;
+}
+
+/*
+ * Tweak autogain.
+ */
+static int ov7670_g_autogain(struct v4l2_subdev *sd, __s32 *value)
+{
+ int ret;
+ unsigned char com8;
+
+ ret = ov7670_read(sd, REG_COM8, &com8);
+ *value = (com8 & COM8_AGC) != 0;
+ return ret;
+}
+
+static int ov7670_s_autogain(struct v4l2_subdev *sd, int value)
+{
+ int ret;
+ unsigned char com8;
+
+ ret = ov7670_read(sd, REG_COM8, &com8);
+ if (ret == 0) {
+ if (value)
+ com8 |= COM8_AGC;
+ else
+ com8 &= ~COM8_AGC;
+ ret = ov7670_write(sd, REG_COM8, com8);
+ }
+ return ret;
+}
+
+/*
+ * Exposure is spread all over the place: top 6 bits in AECHH, middle
+ * 8 in AECH, and two stashed in COM1 just for the hell of it.
+ */
+static int ov7670_g_exp(struct v4l2_subdev *sd, __s32 *value)
+{
+ int ret;
+ unsigned char com1, aech, aechh;
+
+ ret = ov7670_read(sd, REG_COM1, &com1) +
+ ov7670_read(sd, REG_AECH, &aech) +
+ ov7670_read(sd, REG_AECHH, &aechh);
+ *value = ((aechh & 0x3f) << 10) | (aech << 2) | (com1 & 0x03);
+ return ret;
+}
+
+static int ov7670_s_exp(struct v4l2_subdev *sd, int value)
+{
+ int ret;
+ unsigned char com1, com8, aech, aechh;
+
+ ret = ov7670_read(sd, REG_COM1, &com1) +
+ ov7670_read(sd, REG_COM8, &com8);
+ ov7670_read(sd, REG_AECHH, &aechh);
+ if (ret)
+ return ret;
+
+ com1 = (com1 & 0xfc) | (value & 0x03);
+ aech = (value >> 2) & 0xff;
+ aechh = (aechh & 0xc0) | ((value >> 10) & 0x3f);
+ ret = ov7670_write(sd, REG_COM1, com1) +
+ ov7670_write(sd, REG_AECH, aech) +
+ ov7670_write(sd, REG_AECHH, aechh);
+ /* Have to turn off AEC as well */
+ if (ret == 0)
+ ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AEC);
+ return ret;
+}
+
+/*
+ * Tweak autoexposure.
+ */
+static int ov7670_g_autoexp(struct v4l2_subdev *sd, __s32 *value)
+{
+ int ret;
+ unsigned char com8;
+ enum v4l2_exposure_auto_type *atype = (enum v4l2_exposure_auto_type *) value;
+
+ ret = ov7670_read(sd, REG_COM8, &com8);
+ if (com8 & COM8_AEC)
+ *atype = V4L2_EXPOSURE_AUTO;
+ else
+ *atype = V4L2_EXPOSURE_MANUAL;
+ return ret;
+}
+
+static int ov7670_s_autoexp(struct v4l2_subdev *sd,
+ enum v4l2_exposure_auto_type value)
+{
+ int ret;
+ unsigned char com8;
+
+ ret = ov7670_read(sd, REG_COM8, &com8);
+ if (ret == 0) {
+ if (value == V4L2_EXPOSURE_AUTO)
+ com8 |= COM8_AEC;
+ else
+ com8 &= ~COM8_AEC;
+ ret = ov7670_write(sd, REG_COM8, com8);
+ }
+ return ret;
+}
+
+
+
+static int ov7670_queryctrl(struct v4l2_subdev *sd,
+ struct v4l2_queryctrl *qc)
+{
+ /* Fill in min, max, step and default value for these controls. */
+ switch (qc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
+ case V4L2_CID_CONTRAST:
+ return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64);
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_SATURATION:
+ return v4l2_ctrl_query_fill(qc, 0, 256, 1, 128);
+ case V4L2_CID_HUE:
+ return v4l2_ctrl_query_fill(qc, -180, 180, 5, 0);
+ case V4L2_CID_GAIN:
+ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
+ case V4L2_CID_AUTOGAIN:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
+ case V4L2_CID_EXPOSURE:
+ return v4l2_ctrl_query_fill(qc, 0, 65535, 1, 500);
+ case V4L2_CID_EXPOSURE_AUTO:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int ov7670_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ return ov7670_g_brightness(sd, &ctrl->value);
+ case V4L2_CID_CONTRAST:
+ return ov7670_g_contrast(sd, &ctrl->value);
+ case V4L2_CID_SATURATION:
+ return ov7670_g_sat(sd, &ctrl->value);
+ case V4L2_CID_HUE:
+ return ov7670_g_hue(sd, &ctrl->value);
+ case V4L2_CID_VFLIP:
+ return ov7670_g_vflip(sd, &ctrl->value);
+ case V4L2_CID_HFLIP:
+ return ov7670_g_hflip(sd, &ctrl->value);
+ case V4L2_CID_GAIN:
+ return ov7670_g_gain(sd, &ctrl->value);
+ case V4L2_CID_AUTOGAIN:
+ return ov7670_g_autogain(sd, &ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return ov7670_g_exp(sd, &ctrl->value);
+ case V4L2_CID_EXPOSURE_AUTO:
+ return ov7670_g_autoexp(sd, &ctrl->value);
+ }
+ return -EINVAL;
+}
+
+static int ov7670_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ return ov7670_s_brightness(sd, ctrl->value);
+ case V4L2_CID_CONTRAST:
+ return ov7670_s_contrast(sd, ctrl->value);
+ case V4L2_CID_SATURATION:
+ return ov7670_s_sat(sd, ctrl->value);
+ case V4L2_CID_HUE:
+ return ov7670_s_hue(sd, ctrl->value);
+ case V4L2_CID_VFLIP:
+ return ov7670_s_vflip(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return ov7670_s_hflip(sd, ctrl->value);
+ case V4L2_CID_GAIN:
+ return ov7670_s_gain(sd, ctrl->value);
+ case V4L2_CID_AUTOGAIN:
+ return ov7670_s_autogain(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return ov7670_s_exp(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE_AUTO:
+ return ov7670_s_autoexp(sd,
+ (enum v4l2_exposure_auto_type) ctrl->value);
+ }
+ return -EINVAL;
+}
+
+static int ov7670_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_OV7670, 0);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ unsigned char val = 0;
+ int ret;
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ ret = ov7670_read(sd, reg->reg & 0xff, &val);
+ reg->val = val;
+ reg->size = 1;
+ return ret;
+}
+
+static int ov7670_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ ov7670_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ return 0;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops ov7670_core_ops = {
+ .g_chip_ident = ov7670_g_chip_ident,
+ .g_ctrl = ov7670_g_ctrl,
+ .s_ctrl = ov7670_s_ctrl,
+ .queryctrl = ov7670_queryctrl,
+ .reset = ov7670_reset,
+ .init = ov7670_init,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = ov7670_g_register,
+ .s_register = ov7670_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops ov7670_video_ops = {
+ .enum_mbus_fmt = ov7670_enum_mbus_fmt,
+ .try_mbus_fmt = ov7670_try_mbus_fmt,
+ .s_mbus_fmt = ov7670_s_mbus_fmt,
+ .s_parm = ov7670_s_parm,
+ .g_parm = ov7670_g_parm,
+ .enum_frameintervals = ov7670_enum_frameintervals,
+ .enum_framesizes = ov7670_enum_framesizes,
+};
+
+static const struct v4l2_subdev_ops ov7670_ops = {
+ .core = &ov7670_core_ops,
+ .video = &ov7670_video_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int ov7670_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct v4l2_subdev *sd;
+ struct ov7670_info *info;
+ int ret;
+
+ info = kzalloc(sizeof(struct ov7670_info), GFP_KERNEL);
+ if (info == NULL)
+ return -ENOMEM;
+ sd = &info->sd;
+ v4l2_i2c_subdev_init(sd, client, &ov7670_ops);
+
+ info->clock_speed = 30; /* default: a guess */
+ if (client->dev.platform_data) {
+ struct ov7670_config *config = client->dev.platform_data;
+
+ /*
+ * Must apply configuration before initializing device, because it
+ * selects I/O method.
+ */
+ info->min_width = config->min_width;
+ info->min_height = config->min_height;
+ info->use_smbus = config->use_smbus;
+
+ if (config->clock_speed)
+ info->clock_speed = config->clock_speed;
+ }
+
+ /* Make sure it's an ov7670 */
+ ret = ov7670_detect(sd);
+ if (ret) {
+ v4l_dbg(1, debug, client,
+ "chip found @ 0x%x (%s) is not an ov7670 chip.\n",
+ client->addr << 1, client->adapter->name);
+ kfree(info);
+ return ret;
+ }
+ v4l_info(client, "chip found @ 0x%02x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ info->fmt = &ov7670_formats[0];
+ info->sat = 128; /* Review this */
+ info->clkrc = info->clock_speed / 30;
+ return 0;
+}
+
+
+static int ov7670_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
+ return 0;
+}
+
+static const struct i2c_device_id ov7670_id[] = {
+ { "ov7670", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ov7670_id);
+
+static struct i2c_driver ov7670_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ov7670",
+ },
+ .probe = ov7670_probe,
+ .remove = ov7670_remove,
+ .id_table = ov7670_id,
+};
+
+module_i2c_driver(ov7670_driver);
diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c
new file mode 100644
index 00000000..74e77d32
--- /dev/null
+++ b/drivers/media/video/ov772x.c
@@ -0,0 +1,1130 @@
+/*
+ * ov772x Camera Driver
+ *
+ * Copyright (C) 2008 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on ov7670 and soc_camera_platform driver,
+ *
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
+ * Copyright (C) 2008 Magnus Damm
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/videodev2.h>
+
+#include <media/ov772x.h>
+#include <media/soc_camera.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-subdev.h>
+
+/*
+ * register offset
+ */
+#define GAIN 0x00 /* AGC - Gain control gain setting */
+#define BLUE 0x01 /* AWB - Blue channel gain setting */
+#define RED 0x02 /* AWB - Red channel gain setting */
+#define GREEN 0x03 /* AWB - Green channel gain setting */
+#define COM1 0x04 /* Common control 1 */
+#define BAVG 0x05 /* U/B Average Level */
+#define GAVG 0x06 /* Y/Gb Average Level */
+#define RAVG 0x07 /* V/R Average Level */
+#define AECH 0x08 /* Exposure Value - AEC MSBs */
+#define COM2 0x09 /* Common control 2 */
+#define PID 0x0A /* Product ID Number MSB */
+#define VER 0x0B /* Product ID Number LSB */
+#define COM3 0x0C /* Common control 3 */
+#define COM4 0x0D /* Common control 4 */
+#define COM5 0x0E /* Common control 5 */
+#define COM6 0x0F /* Common control 6 */
+#define AEC 0x10 /* Exposure Value */
+#define CLKRC 0x11 /* Internal clock */
+#define COM7 0x12 /* Common control 7 */
+#define COM8 0x13 /* Common control 8 */
+#define COM9 0x14 /* Common control 9 */
+#define COM10 0x15 /* Common control 10 */
+#define REG16 0x16 /* Register 16 */
+#define HSTART 0x17 /* Horizontal sensor size */
+#define HSIZE 0x18 /* Horizontal frame (HREF column) end high 8-bit */
+#define VSTART 0x19 /* Vertical frame (row) start high 8-bit */
+#define VSIZE 0x1A /* Vertical sensor size */
+#define PSHFT 0x1B /* Data format - pixel delay select */
+#define MIDH 0x1C /* Manufacturer ID byte - high */
+#define MIDL 0x1D /* Manufacturer ID byte - low */
+#define LAEC 0x1F /* Fine AEC value */
+#define COM11 0x20 /* Common control 11 */
+#define BDBASE 0x22 /* Banding filter Minimum AEC value */
+#define DBSTEP 0x23 /* Banding filter Maximum Setp */
+#define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */
+#define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */
+#define VPT 0x26 /* AGC/AEC Fast mode operating region */
+#define REG28 0x28 /* Register 28 */
+#define HOUTSIZE 0x29 /* Horizontal data output size MSBs */
+#define EXHCH 0x2A /* Dummy pixel insert MSB */
+#define EXHCL 0x2B /* Dummy pixel insert LSB */
+#define VOUTSIZE 0x2C /* Vertical data output size MSBs */
+#define ADVFL 0x2D /* LSB of insert dummy lines in Vertical direction */
+#define ADVFH 0x2E /* MSG of insert dummy lines in Vertical direction */
+#define YAVE 0x2F /* Y/G Channel Average value */
+#define LUMHTH 0x30 /* Histogram AEC/AGC Luminance high level threshold */
+#define LUMLTH 0x31 /* Histogram AEC/AGC Luminance low level threshold */
+#define HREF 0x32 /* Image start and size control */
+#define DM_LNL 0x33 /* Dummy line low 8 bits */
+#define DM_LNH 0x34 /* Dummy line high 8 bits */
+#define ADOFF_B 0x35 /* AD offset compensation value for B channel */
+#define ADOFF_R 0x36 /* AD offset compensation value for R channel */
+#define ADOFF_GB 0x37 /* AD offset compensation value for Gb channel */
+#define ADOFF_GR 0x38 /* AD offset compensation value for Gr channel */
+#define OFF_B 0x39 /* Analog process B channel offset value */
+#define OFF_R 0x3A /* Analog process R channel offset value */
+#define OFF_GB 0x3B /* Analog process Gb channel offset value */
+#define OFF_GR 0x3C /* Analog process Gr channel offset value */
+#define COM12 0x3D /* Common control 12 */
+#define COM13 0x3E /* Common control 13 */
+#define COM14 0x3F /* Common control 14 */
+#define COM15 0x40 /* Common control 15*/
+#define COM16 0x41 /* Common control 16 */
+#define TGT_B 0x42 /* BLC blue channel target value */
+#define TGT_R 0x43 /* BLC red channel target value */
+#define TGT_GB 0x44 /* BLC Gb channel target value */
+#define TGT_GR 0x45 /* BLC Gr channel target value */
+/* for ov7720 */
+#define LCC0 0x46 /* Lens correction control 0 */
+#define LCC1 0x47 /* Lens correction option 1 - X coordinate */
+#define LCC2 0x48 /* Lens correction option 2 - Y coordinate */
+#define LCC3 0x49 /* Lens correction option 3 */
+#define LCC4 0x4A /* Lens correction option 4 - radius of the circular */
+#define LCC5 0x4B /* Lens correction option 5 */
+#define LCC6 0x4C /* Lens correction option 6 */
+/* for ov7725 */
+#define LC_CTR 0x46 /* Lens correction control */
+#define LC_XC 0x47 /* X coordinate of lens correction center relative */
+#define LC_YC 0x48 /* Y coordinate of lens correction center relative */
+#define LC_COEF 0x49 /* Lens correction coefficient */
+#define LC_RADI 0x4A /* Lens correction radius */
+#define LC_COEFB 0x4B /* Lens B channel compensation coefficient */
+#define LC_COEFR 0x4C /* Lens R channel compensation coefficient */
+
+#define FIXGAIN 0x4D /* Analog fix gain amplifer */
+#define AREF0 0x4E /* Sensor reference control */
+#define AREF1 0x4F /* Sensor reference current control */
+#define AREF2 0x50 /* Analog reference control */
+#define AREF3 0x51 /* ADC reference control */
+#define AREF4 0x52 /* ADC reference control */
+#define AREF5 0x53 /* ADC reference control */
+#define AREF6 0x54 /* Analog reference control */
+#define AREF7 0x55 /* Analog reference control */
+#define UFIX 0x60 /* U channel fixed value output */
+#define VFIX 0x61 /* V channel fixed value output */
+#define AWBB_BLK 0x62 /* AWB option for advanced AWB */
+#define AWB_CTRL0 0x63 /* AWB control byte 0 */
+#define DSP_CTRL1 0x64 /* DSP control byte 1 */
+#define DSP_CTRL2 0x65 /* DSP control byte 2 */
+#define DSP_CTRL3 0x66 /* DSP control byte 3 */
+#define DSP_CTRL4 0x67 /* DSP control byte 4 */
+#define AWB_BIAS 0x68 /* AWB BLC level clip */
+#define AWB_CTRL1 0x69 /* AWB control 1 */
+#define AWB_CTRL2 0x6A /* AWB control 2 */
+#define AWB_CTRL3 0x6B /* AWB control 3 */
+#define AWB_CTRL4 0x6C /* AWB control 4 */
+#define AWB_CTRL5 0x6D /* AWB control 5 */
+#define AWB_CTRL6 0x6E /* AWB control 6 */
+#define AWB_CTRL7 0x6F /* AWB control 7 */
+#define AWB_CTRL8 0x70 /* AWB control 8 */
+#define AWB_CTRL9 0x71 /* AWB control 9 */
+#define AWB_CTRL10 0x72 /* AWB control 10 */
+#define AWB_CTRL11 0x73 /* AWB control 11 */
+#define AWB_CTRL12 0x74 /* AWB control 12 */
+#define AWB_CTRL13 0x75 /* AWB control 13 */
+#define AWB_CTRL14 0x76 /* AWB control 14 */
+#define AWB_CTRL15 0x77 /* AWB control 15 */
+#define AWB_CTRL16 0x78 /* AWB control 16 */
+#define AWB_CTRL17 0x79 /* AWB control 17 */
+#define AWB_CTRL18 0x7A /* AWB control 18 */
+#define AWB_CTRL19 0x7B /* AWB control 19 */
+#define AWB_CTRL20 0x7C /* AWB control 20 */
+#define AWB_CTRL21 0x7D /* AWB control 21 */
+#define GAM1 0x7E /* Gamma Curve 1st segment input end point */
+#define GAM2 0x7F /* Gamma Curve 2nd segment input end point */
+#define GAM3 0x80 /* Gamma Curve 3rd segment input end point */
+#define GAM4 0x81 /* Gamma Curve 4th segment input end point */
+#define GAM5 0x82 /* Gamma Curve 5th segment input end point */
+#define GAM6 0x83 /* Gamma Curve 6th segment input end point */
+#define GAM7 0x84 /* Gamma Curve 7th segment input end point */
+#define GAM8 0x85 /* Gamma Curve 8th segment input end point */
+#define GAM9 0x86 /* Gamma Curve 9th segment input end point */
+#define GAM10 0x87 /* Gamma Curve 10th segment input end point */
+#define GAM11 0x88 /* Gamma Curve 11th segment input end point */
+#define GAM12 0x89 /* Gamma Curve 12th segment input end point */
+#define GAM13 0x8A /* Gamma Curve 13th segment input end point */
+#define GAM14 0x8B /* Gamma Curve 14th segment input end point */
+#define GAM15 0x8C /* Gamma Curve 15th segment input end point */
+#define SLOP 0x8D /* Gamma curve highest segment slope */
+#define DNSTH 0x8E /* De-noise threshold */
+#define EDGE_STRNGT 0x8F /* Edge strength control when manual mode */
+#define EDGE_TRSHLD 0x90 /* Edge threshold control when manual mode */
+#define DNSOFF 0x91 /* Auto De-noise threshold control */
+#define EDGE_UPPER 0x92 /* Edge strength upper limit when Auto mode */
+#define EDGE_LOWER 0x93 /* Edge strength lower limit when Auto mode */
+#define MTX1 0x94 /* Matrix coefficient 1 */
+#define MTX2 0x95 /* Matrix coefficient 2 */
+#define MTX3 0x96 /* Matrix coefficient 3 */
+#define MTX4 0x97 /* Matrix coefficient 4 */
+#define MTX5 0x98 /* Matrix coefficient 5 */
+#define MTX6 0x99 /* Matrix coefficient 6 */
+#define MTX_CTRL 0x9A /* Matrix control */
+#define BRIGHT 0x9B /* Brightness control */
+#define CNTRST 0x9C /* Contrast contrast */
+#define CNTRST_CTRL 0x9D /* Contrast contrast center */
+#define UVAD_J0 0x9E /* Auto UV adjust contrast 0 */
+#define UVAD_J1 0x9F /* Auto UV adjust contrast 1 */
+#define SCAL0 0xA0 /* Scaling control 0 */
+#define SCAL1 0xA1 /* Scaling control 1 */
+#define SCAL2 0xA2 /* Scaling control 2 */
+#define FIFODLYM 0xA3 /* FIFO manual mode delay control */
+#define FIFODLYA 0xA4 /* FIFO auto mode delay control */
+#define SDE 0xA6 /* Special digital effect control */
+#define USAT 0xA7 /* U component saturation control */
+#define VSAT 0xA8 /* V component saturation control */
+/* for ov7720 */
+#define HUE0 0xA9 /* Hue control 0 */
+#define HUE1 0xAA /* Hue control 1 */
+/* for ov7725 */
+#define HUECOS 0xA9 /* Cosine value */
+#define HUESIN 0xAA /* Sine value */
+
+#define SIGN 0xAB /* Sign bit for Hue and contrast */
+#define DSPAUTO 0xAC /* DSP auto function ON/OFF control */
+
+/*
+ * register detail
+ */
+
+/* COM2 */
+#define SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */
+ /* Output drive capability */
+#define OCAP_1x 0x00 /* 1x */
+#define OCAP_2x 0x01 /* 2x */
+#define OCAP_3x 0x02 /* 3x */
+#define OCAP_4x 0x03 /* 4x */
+
+/* COM3 */
+#define SWAP_MASK (SWAP_RGB | SWAP_YUV | SWAP_ML)
+#define IMG_MASK (VFLIP_IMG | HFLIP_IMG)
+
+#define VFLIP_IMG 0x80 /* Vertical flip image ON/OFF selection */
+#define HFLIP_IMG 0x40 /* Horizontal mirror image ON/OFF selection */
+#define SWAP_RGB 0x20 /* Swap B/R output sequence in RGB mode */
+#define SWAP_YUV 0x10 /* Swap Y/UV output sequence in YUV mode */
+#define SWAP_ML 0x08 /* Swap output MSB/LSB */
+ /* Tri-state option for output clock */
+#define NOTRI_CLOCK 0x04 /* 0: Tri-state at this period */
+ /* 1: No tri-state at this period */
+ /* Tri-state option for output data */
+#define NOTRI_DATA 0x02 /* 0: Tri-state at this period */
+ /* 1: No tri-state at this period */
+#define SCOLOR_TEST 0x01 /* Sensor color bar test pattern */
+
+/* COM4 */
+ /* PLL frequency control */
+#define PLL_BYPASS 0x00 /* 00: Bypass PLL */
+#define PLL_4x 0x40 /* 01: PLL 4x */
+#define PLL_6x 0x80 /* 10: PLL 6x */
+#define PLL_8x 0xc0 /* 11: PLL 8x */
+ /* AEC evaluate window */
+#define AEC_FULL 0x00 /* 00: Full window */
+#define AEC_1p2 0x10 /* 01: 1/2 window */
+#define AEC_1p4 0x20 /* 10: 1/4 window */
+#define AEC_2p3 0x30 /* 11: Low 2/3 window */
+
+/* COM5 */
+#define AFR_ON_OFF 0x80 /* Auto frame rate control ON/OFF selection */
+#define AFR_SPPED 0x40 /* Auto frame rate control speed selection */
+ /* Auto frame rate max rate control */
+#define AFR_NO_RATE 0x00 /* No reduction of frame rate */
+#define AFR_1p2 0x10 /* Max reduction to 1/2 frame rate */
+#define AFR_1p4 0x20 /* Max reduction to 1/4 frame rate */
+#define AFR_1p8 0x30 /* Max reduction to 1/8 frame rate */
+ /* Auto frame rate active point control */
+#define AF_2x 0x00 /* Add frame when AGC reaches 2x gain */
+#define AF_4x 0x04 /* Add frame when AGC reaches 4x gain */
+#define AF_8x 0x08 /* Add frame when AGC reaches 8x gain */
+#define AF_16x 0x0c /* Add frame when AGC reaches 16x gain */
+ /* AEC max step control */
+#define AEC_NO_LIMIT 0x01 /* 0 : AEC incease step has limit */
+ /* 1 : No limit to AEC increase step */
+
+/* COM7 */
+ /* SCCB Register Reset */
+#define SCCB_RESET 0x80 /* 0 : No change */
+ /* 1 : Resets all registers to default */
+ /* Resolution selection */
+#define SLCT_MASK 0x40 /* Mask of VGA or QVGA */
+#define SLCT_VGA 0x00 /* 0 : VGA */
+#define SLCT_QVGA 0x40 /* 1 : QVGA */
+#define ITU656_ON_OFF 0x20 /* ITU656 protocol ON/OFF selection */
+ /* RGB output format control */
+#define FMT_MASK 0x0c /* Mask of color format */
+#define FMT_GBR422 0x00 /* 00 : GBR 4:2:2 */
+#define FMT_RGB565 0x04 /* 01 : RGB 565 */
+#define FMT_RGB555 0x08 /* 10 : RGB 555 */
+#define FMT_RGB444 0x0c /* 11 : RGB 444 */
+ /* Output format control */
+#define OFMT_MASK 0x03 /* Mask of output format */
+#define OFMT_YUV 0x00 /* 00 : YUV */
+#define OFMT_P_BRAW 0x01 /* 01 : Processed Bayer RAW */
+#define OFMT_RGB 0x02 /* 10 : RGB */
+#define OFMT_BRAW 0x03 /* 11 : Bayer RAW */
+
+/* COM8 */
+#define FAST_ALGO 0x80 /* Enable fast AGC/AEC algorithm */
+ /* AEC Setp size limit */
+#define UNLMT_STEP 0x40 /* 0 : Step size is limited */
+ /* 1 : Unlimited step size */
+#define BNDF_ON_OFF 0x20 /* Banding filter ON/OFF */
+#define AEC_BND 0x10 /* Enable AEC below banding value */
+#define AEC_ON_OFF 0x08 /* Fine AEC ON/OFF control */
+#define AGC_ON 0x04 /* AGC Enable */
+#define AWB_ON 0x02 /* AWB Enable */
+#define AEC_ON 0x01 /* AEC Enable */
+
+/* COM9 */
+#define BASE_AECAGC 0x80 /* Histogram or average based AEC/AGC */
+ /* Automatic gain ceiling - maximum AGC value */
+#define GAIN_2x 0x00 /* 000 : 2x */
+#define GAIN_4x 0x10 /* 001 : 4x */
+#define GAIN_8x 0x20 /* 010 : 8x */
+#define GAIN_16x 0x30 /* 011 : 16x */
+#define GAIN_32x 0x40 /* 100 : 32x */
+#define GAIN_64x 0x50 /* 101 : 64x */
+#define GAIN_128x 0x60 /* 110 : 128x */
+#define DROP_VSYNC 0x04 /* Drop VSYNC output of corrupt frame */
+#define DROP_HREF 0x02 /* Drop HREF output of corrupt frame */
+
+/* COM11 */
+#define SGLF_ON_OFF 0x02 /* Single frame ON/OFF selection */
+#define SGLF_TRIG 0x01 /* Single frame transfer trigger */
+
+/* EXHCH */
+#define VSIZE_LSB 0x04 /* Vertical data output size LSB */
+
+/* DSP_CTRL1 */
+#define FIFO_ON 0x80 /* FIFO enable/disable selection */
+#define UV_ON_OFF 0x40 /* UV adjust function ON/OFF selection */
+#define YUV444_2_422 0x20 /* YUV444 to 422 UV channel option selection */
+#define CLR_MTRX_ON_OFF 0x10 /* Color matrix ON/OFF selection */
+#define INTPLT_ON_OFF 0x08 /* Interpolation ON/OFF selection */
+#define GMM_ON_OFF 0x04 /* Gamma function ON/OFF selection */
+#define AUTO_BLK_ON_OFF 0x02 /* Black defect auto correction ON/OFF */
+#define AUTO_WHT_ON_OFF 0x01 /* White define auto correction ON/OFF */
+
+/* DSP_CTRL3 */
+#define UV_MASK 0x80 /* UV output sequence option */
+#define UV_ON 0x80 /* ON */
+#define UV_OFF 0x00 /* OFF */
+#define CBAR_MASK 0x20 /* DSP Color bar mask */
+#define CBAR_ON 0x20 /* ON */
+#define CBAR_OFF 0x00 /* OFF */
+
+/* HSTART */
+#define HST_VGA 0x23
+#define HST_QVGA 0x3F
+
+/* HSIZE */
+#define HSZ_VGA 0xA0
+#define HSZ_QVGA 0x50
+
+/* VSTART */
+#define VST_VGA 0x07
+#define VST_QVGA 0x03
+
+/* VSIZE */
+#define VSZ_VGA 0xF0
+#define VSZ_QVGA 0x78
+
+/* HOUTSIZE */
+#define HOSZ_VGA 0xA0
+#define HOSZ_QVGA 0x50
+
+/* VOUTSIZE */
+#define VOSZ_VGA 0xF0
+#define VOSZ_QVGA 0x78
+
+/* DSPAUTO (DSP Auto Function ON/OFF Control) */
+#define AWB_ACTRL 0x80 /* AWB auto threshold control */
+#define DENOISE_ACTRL 0x40 /* De-noise auto threshold control */
+#define EDGE_ACTRL 0x20 /* Edge enhancement auto strength control */
+#define UV_ACTRL 0x10 /* UV adjust auto slope control */
+#define SCAL0_ACTRL 0x08 /* Auto scaling factor control */
+#define SCAL1_2_ACTRL 0x04 /* Auto scaling factor control */
+
+/*
+ * ID
+ */
+#define OV7720 0x7720
+#define OV7725 0x7721
+#define VERSION(pid, ver) ((pid<<8)|(ver&0xFF))
+
+/*
+ * struct
+ */
+struct regval_list {
+ unsigned char reg_num;
+ unsigned char value;
+};
+
+struct ov772x_color_format {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+ u8 dsp3;
+ u8 com3;
+ u8 com7;
+};
+
+struct ov772x_win_size {
+ char *name;
+ __u32 width;
+ __u32 height;
+ unsigned char com7_bit;
+ const struct regval_list *regs;
+};
+
+struct ov772x_priv {
+ struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
+ struct ov772x_camera_info *info;
+ const struct ov772x_color_format *cfmt;
+ const struct ov772x_win_size *win;
+ int model;
+ unsigned short flag_vflip:1;
+ unsigned short flag_hflip:1;
+ /* band_filter = COM8[5] ? 256 - BDBASE : 0 */
+ unsigned short band_filter;
+};
+
+#define ENDMARKER { 0xff, 0xff }
+
+/*
+ * register setting for window size
+ */
+static const struct regval_list ov772x_qvga_regs[] = {
+ { HSTART, HST_QVGA },
+ { HSIZE, HSZ_QVGA },
+ { VSTART, VST_QVGA },
+ { VSIZE, VSZ_QVGA },
+ { HOUTSIZE, HOSZ_QVGA },
+ { VOUTSIZE, VOSZ_QVGA },
+ ENDMARKER,
+};
+
+static const struct regval_list ov772x_vga_regs[] = {
+ { HSTART, HST_VGA },
+ { HSIZE, HSZ_VGA },
+ { VSTART, VST_VGA },
+ { VSIZE, VSZ_VGA },
+ { HOUTSIZE, HOSZ_VGA },
+ { VOUTSIZE, VOSZ_VGA },
+ ENDMARKER,
+};
+
+/*
+ * supported color format list
+ */
+static const struct ov772x_color_format ov772x_cfmts[] = {
+ {
+ .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .dsp3 = 0x0,
+ .com3 = SWAP_YUV,
+ .com7 = OFMT_YUV,
+ },
+ {
+ .code = V4L2_MBUS_FMT_YVYU8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .dsp3 = UV_ON,
+ .com3 = SWAP_YUV,
+ .com7 = OFMT_YUV,
+ },
+ {
+ .code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .dsp3 = 0x0,
+ .com3 = 0x0,
+ .com7 = OFMT_YUV,
+ },
+ {
+ .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .dsp3 = 0x0,
+ .com3 = SWAP_RGB,
+ .com7 = FMT_RGB555 | OFMT_RGB,
+ },
+ {
+ .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .dsp3 = 0x0,
+ .com3 = 0x0,
+ .com7 = FMT_RGB555 | OFMT_RGB,
+ },
+ {
+ .code = V4L2_MBUS_FMT_RGB565_2X8_LE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .dsp3 = 0x0,
+ .com3 = SWAP_RGB,
+ .com7 = FMT_RGB565 | OFMT_RGB,
+ },
+ {
+ .code = V4L2_MBUS_FMT_RGB565_2X8_BE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .dsp3 = 0x0,
+ .com3 = 0x0,
+ .com7 = FMT_RGB565 | OFMT_RGB,
+ },
+};
+
+
+/*
+ * window size list
+ */
+#define VGA_WIDTH 640
+#define VGA_HEIGHT 480
+#define QVGA_WIDTH 320
+#define QVGA_HEIGHT 240
+#define MAX_WIDTH VGA_WIDTH
+#define MAX_HEIGHT VGA_HEIGHT
+
+static const struct ov772x_win_size ov772x_win_vga = {
+ .name = "VGA",
+ .width = VGA_WIDTH,
+ .height = VGA_HEIGHT,
+ .com7_bit = SLCT_VGA,
+ .regs = ov772x_vga_regs,
+};
+
+static const struct ov772x_win_size ov772x_win_qvga = {
+ .name = "QVGA",
+ .width = QVGA_WIDTH,
+ .height = QVGA_HEIGHT,
+ .com7_bit = SLCT_QVGA,
+ .regs = ov772x_qvga_regs,
+};
+
+/*
+ * general function
+ */
+
+static struct ov772x_priv *to_ov772x(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct ov772x_priv,
+ subdev);
+}
+
+static int ov772x_write_array(struct i2c_client *client,
+ const struct regval_list *vals)
+{
+ while (vals->reg_num != 0xff) {
+ int ret = i2c_smbus_write_byte_data(client,
+ vals->reg_num,
+ vals->value);
+ if (ret < 0)
+ return ret;
+ vals++;
+ }
+ return 0;
+}
+
+static int ov772x_mask_set(struct i2c_client *client,
+ u8 command,
+ u8 mask,
+ u8 set)
+{
+ s32 val = i2c_smbus_read_byte_data(client, command);
+ if (val < 0)
+ return val;
+
+ val &= ~mask;
+ val |= set & mask;
+
+ return i2c_smbus_write_byte_data(client, command, val);
+}
+
+static int ov772x_reset(struct i2c_client *client)
+{
+ int ret = i2c_smbus_write_byte_data(client, COM7, SCCB_RESET);
+ msleep(1);
+ return ret;
+}
+
+/*
+ * soc_camera_ops function
+ */
+
+static int ov772x_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov772x_priv *priv = container_of(sd, struct ov772x_priv, subdev);
+
+ if (!enable) {
+ ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE);
+ return 0;
+ }
+
+ if (!priv->win || !priv->cfmt) {
+ dev_err(&client->dev, "norm or win select error\n");
+ return -EPERM;
+ }
+
+ ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, 0);
+
+ dev_dbg(&client->dev, "format %d, win %s\n",
+ priv->cfmt->code, priv->win->name);
+
+ return 0;
+}
+
+static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov772x_priv *priv = container_of(ctrl->handler,
+ struct ov772x_priv, hdl);
+ struct v4l2_subdev *sd = &priv->subdev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret = 0;
+ u8 val;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ val = ctrl->val ? VFLIP_IMG : 0x00;
+ priv->flag_vflip = ctrl->val;
+ if (priv->info->flags & OV772X_FLAG_VFLIP)
+ val ^= VFLIP_IMG;
+ return ov772x_mask_set(client, COM3, VFLIP_IMG, val);
+ case V4L2_CID_HFLIP:
+ val = ctrl->val ? HFLIP_IMG : 0x00;
+ priv->flag_hflip = ctrl->val;
+ if (priv->info->flags & OV772X_FLAG_HFLIP)
+ val ^= HFLIP_IMG;
+ return ov772x_mask_set(client, COM3, HFLIP_IMG, val);
+ case V4L2_CID_BAND_STOP_FILTER:
+ if (!ctrl->val) {
+ /* Switch the filter off, it is on now */
+ ret = ov772x_mask_set(client, BDBASE, 0xff, 0xff);
+ if (!ret)
+ ret = ov772x_mask_set(client, COM8,
+ BNDF_ON_OFF, 0);
+ } else {
+ /* Switch the filter on, set AEC low limit */
+ val = 256 - ctrl->val;
+ ret = ov772x_mask_set(client, COM8,
+ BNDF_ON_OFF, BNDF_ON_OFF);
+ if (!ret)
+ ret = ov772x_mask_set(client, BDBASE,
+ 0xff, val);
+ }
+ if (!ret)
+ priv->band_filter = ctrl->val;
+ return ret;
+ }
+
+ return -EINVAL;
+}
+
+static int ov772x_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct ov772x_priv *priv = container_of(sd, struct ov772x_priv, subdev);
+
+ id->ident = priv->model;
+ id->revision = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov772x_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ reg->size = 1;
+ if (reg->reg > 0xff)
+ return -EINVAL;
+
+ ret = i2c_smbus_read_byte_data(client, reg->reg);
+ if (ret < 0)
+ return ret;
+
+ reg->val = (__u64)ret;
+
+ return 0;
+}
+
+static int ov772x_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->reg > 0xff ||
+ reg->val > 0xff)
+ return -EINVAL;
+
+ return i2c_smbus_write_byte_data(client, reg->reg, reg->val);
+}
+#endif
+
+static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height)
+{
+ __u32 diff;
+ const struct ov772x_win_size *win;
+
+ /* default is QVGA */
+ diff = abs(width - ov772x_win_qvga.width) +
+ abs(height - ov772x_win_qvga.height);
+ win = &ov772x_win_qvga;
+
+ /* VGA */
+ if (diff >
+ abs(width - ov772x_win_vga.width) +
+ abs(height - ov772x_win_vga.height))
+ win = &ov772x_win_vga;
+
+ return win;
+}
+
+static int ov772x_set_params(struct i2c_client *client, u32 *width, u32 *height,
+ enum v4l2_mbus_pixelcode code)
+{
+ struct ov772x_priv *priv = to_ov772x(client);
+ int ret = -EINVAL;
+ u8 val;
+ int i;
+
+ /*
+ * select format
+ */
+ priv->cfmt = NULL;
+ for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++) {
+ if (code == ov772x_cfmts[i].code) {
+ priv->cfmt = ov772x_cfmts + i;
+ break;
+ }
+ }
+ if (!priv->cfmt)
+ goto ov772x_set_fmt_error;
+
+ /*
+ * select win
+ */
+ priv->win = ov772x_select_win(*width, *height);
+
+ /*
+ * reset hardware
+ */
+ ov772x_reset(client);
+
+ /*
+ * Edge Ctrl
+ */
+ if (priv->info->edgectrl.strength & OV772X_MANUAL_EDGE_CTRL) {
+
+ /*
+ * Manual Edge Control Mode
+ *
+ * Edge auto strength bit is set by default.
+ * Remove it when manual mode.
+ */
+
+ ret = ov772x_mask_set(client, DSPAUTO, EDGE_ACTRL, 0x00);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+
+ ret = ov772x_mask_set(client,
+ EDGE_TRSHLD, OV772X_EDGE_THRESHOLD_MASK,
+ priv->info->edgectrl.threshold);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+
+ ret = ov772x_mask_set(client,
+ EDGE_STRNGT, OV772X_EDGE_STRENGTH_MASK,
+ priv->info->edgectrl.strength);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+
+ } else if (priv->info->edgectrl.upper > priv->info->edgectrl.lower) {
+ /*
+ * Auto Edge Control Mode
+ *
+ * set upper and lower limit
+ */
+ ret = ov772x_mask_set(client,
+ EDGE_UPPER, OV772X_EDGE_UPPER_MASK,
+ priv->info->edgectrl.upper);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+
+ ret = ov772x_mask_set(client,
+ EDGE_LOWER, OV772X_EDGE_LOWER_MASK,
+ priv->info->edgectrl.lower);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+ }
+
+ /*
+ * set size format
+ */
+ ret = ov772x_write_array(client, priv->win->regs);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+
+ /*
+ * set DSP_CTRL3
+ */
+ val = priv->cfmt->dsp3;
+ if (val) {
+ ret = ov772x_mask_set(client,
+ DSP_CTRL3, UV_MASK, val);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+ }
+
+ /*
+ * set COM3
+ */
+ val = priv->cfmt->com3;
+ if (priv->info->flags & OV772X_FLAG_VFLIP)
+ val |= VFLIP_IMG;
+ if (priv->info->flags & OV772X_FLAG_HFLIP)
+ val |= HFLIP_IMG;
+ if (priv->flag_vflip)
+ val ^= VFLIP_IMG;
+ if (priv->flag_hflip)
+ val ^= HFLIP_IMG;
+
+ ret = ov772x_mask_set(client,
+ COM3, SWAP_MASK | IMG_MASK, val);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+
+ /*
+ * set COM7
+ */
+ val = priv->win->com7_bit | priv->cfmt->com7;
+ ret = ov772x_mask_set(client,
+ COM7, SLCT_MASK | FMT_MASK | OFMT_MASK,
+ val);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+
+ /*
+ * set COM8
+ */
+ if (priv->band_filter) {
+ ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, 1);
+ if (!ret)
+ ret = ov772x_mask_set(client, BDBASE,
+ 0xff, 256 - priv->band_filter);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+ }
+
+ *width = priv->win->width;
+ *height = priv->win->height;
+
+ return ret;
+
+ov772x_set_fmt_error:
+
+ ov772x_reset(client);
+ priv->win = NULL;
+ priv->cfmt = NULL;
+
+ return ret;
+}
+
+static int ov772x_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ a->c.left = 0;
+ a->c.top = 0;
+ a->c.width = VGA_WIDTH;
+ a->c.height = VGA_HEIGHT;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int ov772x_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ a->bounds.width = VGA_WIDTH;
+ a->bounds.height = VGA_HEIGHT;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int ov772x_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov772x_priv *priv = container_of(sd, struct ov772x_priv, subdev);
+
+ if (!priv->win || !priv->cfmt) {
+ u32 width = VGA_WIDTH, height = VGA_HEIGHT;
+ int ret = ov772x_set_params(client, &width, &height,
+ V4L2_MBUS_FMT_YUYV8_2X8);
+ if (ret < 0)
+ return ret;
+ }
+
+ mf->width = priv->win->width;
+ mf->height = priv->win->height;
+ mf->code = priv->cfmt->code;
+ mf->colorspace = priv->cfmt->colorspace;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int ov772x_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov772x_priv *priv = container_of(sd, struct ov772x_priv, subdev);
+ int ret = ov772x_set_params(client, &mf->width, &mf->height,
+ mf->code);
+
+ if (!ret)
+ mf->colorspace = priv->cfmt->colorspace;
+
+ return ret;
+}
+
+static int ov772x_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct ov772x_priv *priv = container_of(sd, struct ov772x_priv, subdev);
+ const struct ov772x_win_size *win;
+ int i;
+
+ /*
+ * select suitable win
+ */
+ win = ov772x_select_win(mf->width, mf->height);
+
+ mf->width = win->width;
+ mf->height = win->height;
+ mf->field = V4L2_FIELD_NONE;
+
+ for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++)
+ if (mf->code == ov772x_cfmts[i].code)
+ break;
+
+ if (i == ARRAY_SIZE(ov772x_cfmts)) {
+ /* Unsupported format requested. Propose either */
+ if (priv->cfmt) {
+ /* the current one or */
+ mf->colorspace = priv->cfmt->colorspace;
+ mf->code = priv->cfmt->code;
+ } else {
+ /* the default one */
+ mf->colorspace = ov772x_cfmts[0].colorspace;
+ mf->code = ov772x_cfmts[0].code;
+ }
+ } else {
+ /* Also return the colorspace */
+ mf->colorspace = ov772x_cfmts[i].colorspace;
+ }
+
+ return 0;
+}
+
+static int ov772x_video_probe(struct i2c_client *client)
+{
+ struct ov772x_priv *priv = to_ov772x(client);
+ u8 pid, ver;
+ const char *devname;
+
+ /*
+ * check and show product ID and manufacturer ID
+ */
+ pid = i2c_smbus_read_byte_data(client, PID);
+ ver = i2c_smbus_read_byte_data(client, VER);
+
+ switch (VERSION(pid, ver)) {
+ case OV7720:
+ devname = "ov7720";
+ priv->model = V4L2_IDENT_OV7720;
+ break;
+ case OV7725:
+ devname = "ov7725";
+ priv->model = V4L2_IDENT_OV7725;
+ break;
+ default:
+ dev_err(&client->dev,
+ "Product ID error %x:%x\n", pid, ver);
+ return -ENODEV;
+ }
+
+ dev_info(&client->dev,
+ "%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
+ devname,
+ pid,
+ ver,
+ i2c_smbus_read_byte_data(client, MIDH),
+ i2c_smbus_read_byte_data(client, MIDL));
+ return v4l2_ctrl_handler_setup(&priv->hdl);
+}
+
+static const struct v4l2_ctrl_ops ov772x_ctrl_ops = {
+ .s_ctrl = ov772x_s_ctrl,
+};
+
+static struct v4l2_subdev_core_ops ov772x_subdev_core_ops = {
+ .g_chip_ident = ov772x_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = ov772x_g_register,
+ .s_register = ov772x_s_register,
+#endif
+};
+
+static int ov772x_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= ARRAY_SIZE(ov772x_cfmts))
+ return -EINVAL;
+
+ *code = ov772x_cfmts[index].code;
+ return 0;
+}
+
+static int ov772x_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops ov772x_subdev_video_ops = {
+ .s_stream = ov772x_s_stream,
+ .g_mbus_fmt = ov772x_g_fmt,
+ .s_mbus_fmt = ov772x_s_fmt,
+ .try_mbus_fmt = ov772x_try_fmt,
+ .cropcap = ov772x_cropcap,
+ .g_crop = ov772x_g_crop,
+ .enum_mbus_fmt = ov772x_enum_fmt,
+ .g_mbus_config = ov772x_g_mbus_config,
+};
+
+static struct v4l2_subdev_ops ov772x_subdev_ops = {
+ .core = &ov772x_subdev_core_ops,
+ .video = &ov772x_subdev_video_ops,
+};
+
+/*
+ * i2c_driver function
+ */
+
+static int ov772x_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct ov772x_priv *priv;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ int ret;
+
+ if (!icl || !icl->priv) {
+ dev_err(&client->dev, "OV772X: missing platform data!\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&adapter->dev,
+ "I2C-Adapter doesn't support "
+ "I2C_FUNC_SMBUS_BYTE_DATA\n");
+ return -EIO;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->info = icl->priv;
+
+ v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops);
+ v4l2_ctrl_handler_init(&priv->hdl, 3);
+ v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
+ V4L2_CID_BAND_STOP_FILTER, 0, 256, 1, 0);
+ priv->subdev.ctrl_handler = &priv->hdl;
+ if (priv->hdl.error) {
+ int err = priv->hdl.error;
+
+ kfree(priv);
+ return err;
+ }
+
+ ret = ov772x_video_probe(client);
+ if (ret) {
+ v4l2_ctrl_handler_free(&priv->hdl);
+ kfree(priv);
+ }
+
+ return ret;
+}
+
+static int ov772x_remove(struct i2c_client *client)
+{
+ struct ov772x_priv *priv = to_ov772x(client);
+
+ v4l2_device_unregister_subdev(&priv->subdev);
+ v4l2_ctrl_handler_free(&priv->hdl);
+ kfree(priv);
+ return 0;
+}
+
+static const struct i2c_device_id ov772x_id[] = {
+ { "ov772x", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ov772x_id);
+
+static struct i2c_driver ov772x_i2c_driver = {
+ .driver = {
+ .name = "ov772x",
+ },
+ .probe = ov772x_probe,
+ .remove = ov772x_remove,
+ .id_table = ov772x_id,
+};
+
+module_i2c_driver(ov772x_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for ov772x");
+MODULE_AUTHOR("Kuninori Morimoto");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c
new file mode 100644
index 00000000..23412deb
--- /dev/null
+++ b/drivers/media/video/ov9640.c
@@ -0,0 +1,745 @@
+/*
+ * OmniVision OV96xx Camera Driver
+ *
+ * Copyright (C) 2009 Marek Vasut <marek.vasut@gmail.com>
+ *
+ * Based on ov772x camera driver:
+ *
+ * Copyright (C) 2008 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on ov7670 and soc_camera_platform driver,
+ *
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
+ * Copyright (C) 2008 Magnus Damm
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/videodev2.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+
+#include "ov9640.h"
+
+#define to_ov9640_sensor(sd) container_of(sd, struct ov9640_priv, subdev)
+
+/* default register setup */
+static const struct ov9640_reg ov9640_regs_dflt[] = {
+ { OV9640_COM5, OV9640_COM5_SYSCLK | OV9640_COM5_LONGEXP },
+ { OV9640_COM6, OV9640_COM6_OPT_BLC | OV9640_COM6_ADBLC_BIAS |
+ OV9640_COM6_FMT_RST | OV9640_COM6_ADBLC_OPTEN },
+ { OV9640_PSHFT, OV9640_PSHFT_VAL(0x01) },
+ { OV9640_ACOM, OV9640_ACOM_2X_ANALOG | OV9640_ACOM_RSVD },
+ { OV9640_TSLB, OV9640_TSLB_YUYV_UYVY },
+ { OV9640_COM16, OV9640_COM16_RB_AVG },
+
+ /* Gamma curve P */
+ { 0x6c, 0x40 }, { 0x6d, 0x30 }, { 0x6e, 0x4b }, { 0x6f, 0x60 },
+ { 0x70, 0x70 }, { 0x71, 0x70 }, { 0x72, 0x70 }, { 0x73, 0x70 },
+ { 0x74, 0x60 }, { 0x75, 0x60 }, { 0x76, 0x50 }, { 0x77, 0x48 },
+ { 0x78, 0x3a }, { 0x79, 0x2e }, { 0x7a, 0x28 }, { 0x7b, 0x22 },
+
+ /* Gamma curve T */
+ { 0x7c, 0x04 }, { 0x7d, 0x07 }, { 0x7e, 0x10 }, { 0x7f, 0x28 },
+ { 0x80, 0x36 }, { 0x81, 0x44 }, { 0x82, 0x52 }, { 0x83, 0x60 },
+ { 0x84, 0x6c }, { 0x85, 0x78 }, { 0x86, 0x8c }, { 0x87, 0x9e },
+ { 0x88, 0xbb }, { 0x89, 0xd2 }, { 0x8a, 0xe6 },
+};
+
+/* Configurations
+ * NOTE: for YUV, alter the following registers:
+ * COM12 |= OV9640_COM12_YUV_AVG
+ *
+ * for RGB, alter the following registers:
+ * COM7 |= OV9640_COM7_RGB
+ * COM13 |= OV9640_COM13_RGB_AVG
+ * COM15 |= proper RGB color encoding mode
+ */
+static const struct ov9640_reg ov9640_regs_qqcif[] = {
+ { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x0f) },
+ { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP },
+ { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD },
+ { OV9640_COM7, OV9640_COM7_QCIF },
+ { OV9640_COM12, OV9640_COM12_RSVD },
+ { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
+ { OV9640_COM15, OV9640_COM15_OR_10F0 },
+};
+
+static const struct ov9640_reg ov9640_regs_qqvga[] = {
+ { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) },
+ { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP },
+ { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD },
+ { OV9640_COM7, OV9640_COM7_QVGA },
+ { OV9640_COM12, OV9640_COM12_RSVD },
+ { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
+ { OV9640_COM15, OV9640_COM15_OR_10F0 },
+};
+
+static const struct ov9640_reg ov9640_regs_qcif[] = {
+ { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) },
+ { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD },
+ { OV9640_COM7, OV9640_COM7_QCIF },
+ { OV9640_COM12, OV9640_COM12_RSVD },
+ { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
+ { OV9640_COM15, OV9640_COM15_OR_10F0 },
+};
+
+static const struct ov9640_reg ov9640_regs_qvga[] = {
+ { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) },
+ { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD },
+ { OV9640_COM7, OV9640_COM7_QVGA },
+ { OV9640_COM12, OV9640_COM12_RSVD },
+ { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
+ { OV9640_COM15, OV9640_COM15_OR_10F0 },
+};
+
+static const struct ov9640_reg ov9640_regs_cif[] = {
+ { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) },
+ { OV9640_COM3, OV9640_COM3_VP },
+ { OV9640_COM7, OV9640_COM7_CIF },
+ { OV9640_COM12, OV9640_COM12_RSVD },
+ { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
+ { OV9640_COM15, OV9640_COM15_OR_10F0 },
+};
+
+static const struct ov9640_reg ov9640_regs_vga[] = {
+ { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) },
+ { OV9640_COM3, OV9640_COM3_VP },
+ { OV9640_COM7, OV9640_COM7_VGA },
+ { OV9640_COM12, OV9640_COM12_RSVD },
+ { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
+ { OV9640_COM15, OV9640_COM15_OR_10F0 },
+};
+
+static const struct ov9640_reg ov9640_regs_sxga[] = {
+ { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) },
+ { OV9640_COM3, OV9640_COM3_VP },
+ { OV9640_COM7, 0 },
+ { OV9640_COM12, OV9640_COM12_RSVD },
+ { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
+ { OV9640_COM15, OV9640_COM15_OR_10F0 },
+};
+
+static const struct ov9640_reg ov9640_regs_yuv[] = {
+ { OV9640_MTX1, 0x58 },
+ { OV9640_MTX2, 0x48 },
+ { OV9640_MTX3, 0x10 },
+ { OV9640_MTX4, 0x28 },
+ { OV9640_MTX5, 0x48 },
+ { OV9640_MTX6, 0x70 },
+ { OV9640_MTX7, 0x40 },
+ { OV9640_MTX8, 0x40 },
+ { OV9640_MTX9, 0x40 },
+ { OV9640_MTXS, 0x0f },
+};
+
+static const struct ov9640_reg ov9640_regs_rgb[] = {
+ { OV9640_MTX1, 0x71 },
+ { OV9640_MTX2, 0x3e },
+ { OV9640_MTX3, 0x0c },
+ { OV9640_MTX4, 0x33 },
+ { OV9640_MTX5, 0x72 },
+ { OV9640_MTX6, 0x00 },
+ { OV9640_MTX7, 0x2b },
+ { OV9640_MTX8, 0x66 },
+ { OV9640_MTX9, 0xd2 },
+ { OV9640_MTXS, 0x65 },
+};
+
+static enum v4l2_mbus_pixelcode ov9640_codes[] = {
+ V4L2_MBUS_FMT_UYVY8_2X8,
+ V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE,
+ V4L2_MBUS_FMT_RGB565_2X8_LE,
+};
+
+/* read a register */
+static int ov9640_reg_read(struct i2c_client *client, u8 reg, u8 *val)
+{
+ int ret;
+ u8 data = reg;
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &data,
+ };
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret < 0)
+ goto err;
+
+ msg.flags = I2C_M_RD;
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret < 0)
+ goto err;
+
+ *val = data;
+ return 0;
+
+err:
+ dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg);
+ return ret;
+}
+
+/* write a register */
+static int ov9640_reg_write(struct i2c_client *client, u8 reg, u8 val)
+{
+ int ret;
+ u8 _val;
+ unsigned char data[2] = { reg, val };
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = data,
+ };
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg);
+ return ret;
+ }
+
+ /* we have to read the register back ... no idea why, maybe HW bug */
+ ret = ov9640_reg_read(client, reg, &_val);
+ if (ret)
+ dev_err(&client->dev,
+ "Failed reading back register 0x%02x!\n", reg);
+
+ return 0;
+}
+
+
+/* Read a register, alter its bits, write it back */
+static int ov9640_reg_rmw(struct i2c_client *client, u8 reg, u8 set, u8 unset)
+{
+ u8 val;
+ int ret;
+
+ ret = ov9640_reg_read(client, reg, &val);
+ if (ret) {
+ dev_err(&client->dev,
+ "[Read]-Modify-Write of register %02x failed!\n", reg);
+ return val;
+ }
+
+ val |= set;
+ val &= ~unset;
+
+ ret = ov9640_reg_write(client, reg, val);
+ if (ret)
+ dev_err(&client->dev,
+ "Read-Modify-[Write] of register %02x failed!\n", reg);
+
+ return ret;
+}
+
+/* Soft reset the camera. This has nothing to do with the RESET pin! */
+static int ov9640_reset(struct i2c_client *client)
+{
+ int ret;
+
+ ret = ov9640_reg_write(client, OV9640_COM7, OV9640_COM7_SCCB_RESET);
+ if (ret)
+ dev_err(&client->dev,
+ "An error occurred while entering soft reset!\n");
+
+ return ret;
+}
+
+/* Start/Stop streaming from the device */
+static int ov9640_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ return 0;
+}
+
+/* Set status of additional camera capabilities */
+static int ov9640_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov9640_priv *priv = container_of(ctrl->handler, struct ov9640_priv, hdl);
+ struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev);
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ if (ctrl->val)
+ return ov9640_reg_rmw(client, OV9640_MVFP,
+ OV9640_MVFP_V, 0);
+ return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_V);
+ case V4L2_CID_HFLIP:
+ if (ctrl->val)
+ return ov9640_reg_rmw(client, OV9640_MVFP,
+ OV9640_MVFP_H, 0);
+ return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_H);
+ }
+ return -EINVAL;
+}
+
+/* Get chip identification */
+static int ov9640_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct ov9640_priv *priv = to_ov9640_sensor(sd);
+
+ id->ident = priv->model;
+ id->revision = priv->revision;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov9640_get_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+ u8 val;
+
+ if (reg->reg & ~0xff)
+ return -EINVAL;
+
+ reg->size = 1;
+
+ ret = ov9640_reg_read(client, reg->reg, &val);
+ if (ret)
+ return ret;
+
+ reg->val = (__u64)val;
+
+ return 0;
+}
+
+static int ov9640_set_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->reg & ~0xff || reg->val & ~0xff)
+ return -EINVAL;
+
+ return ov9640_reg_write(client, reg->reg, reg->val);
+}
+#endif
+
+/* select nearest higher resolution for capture */
+static void ov9640_res_roundup(u32 *width, u32 *height)
+{
+ int i;
+ enum { QQCIF, QQVGA, QCIF, QVGA, CIF, VGA, SXGA };
+ int res_x[] = { 88, 160, 176, 320, 352, 640, 1280 };
+ int res_y[] = { 72, 120, 144, 240, 288, 480, 960 };
+
+ for (i = 0; i < ARRAY_SIZE(res_x); i++) {
+ if (res_x[i] >= *width && res_y[i] >= *height) {
+ *width = res_x[i];
+ *height = res_y[i];
+ return;
+ }
+ }
+
+ *width = res_x[SXGA];
+ *height = res_y[SXGA];
+}
+
+/* Prepare necessary register changes depending on color encoding */
+static void ov9640_alter_regs(enum v4l2_mbus_pixelcode code,
+ struct ov9640_reg_alt *alt)
+{
+ switch (code) {
+ default:
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ alt->com12 = OV9640_COM12_YUV_AVG;
+ alt->com13 = OV9640_COM13_Y_DELAY_EN |
+ OV9640_COM13_YUV_DLY(0x01);
+ break;
+ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
+ alt->com7 = OV9640_COM7_RGB;
+ alt->com13 = OV9640_COM13_RGB_AVG;
+ alt->com15 = OV9640_COM15_RGB_555;
+ break;
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ alt->com7 = OV9640_COM7_RGB;
+ alt->com13 = OV9640_COM13_RGB_AVG;
+ alt->com15 = OV9640_COM15_RGB_565;
+ break;
+ };
+}
+
+/* Setup registers according to resolution and color encoding */
+static int ov9640_write_regs(struct i2c_client *client, u32 width,
+ enum v4l2_mbus_pixelcode code, struct ov9640_reg_alt *alts)
+{
+ const struct ov9640_reg *ov9640_regs, *matrix_regs;
+ int ov9640_regs_len, matrix_regs_len;
+ int i, ret;
+ u8 val;
+
+ /* select register configuration for given resolution */
+ switch (width) {
+ case W_QQCIF:
+ ov9640_regs = ov9640_regs_qqcif;
+ ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqcif);
+ break;
+ case W_QQVGA:
+ ov9640_regs = ov9640_regs_qqvga;
+ ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqvga);
+ break;
+ case W_QCIF:
+ ov9640_regs = ov9640_regs_qcif;
+ ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qcif);
+ break;
+ case W_QVGA:
+ ov9640_regs = ov9640_regs_qvga;
+ ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qvga);
+ break;
+ case W_CIF:
+ ov9640_regs = ov9640_regs_cif;
+ ov9640_regs_len = ARRAY_SIZE(ov9640_regs_cif);
+ break;
+ case W_VGA:
+ ov9640_regs = ov9640_regs_vga;
+ ov9640_regs_len = ARRAY_SIZE(ov9640_regs_vga);
+ break;
+ case W_SXGA:
+ ov9640_regs = ov9640_regs_sxga;
+ ov9640_regs_len = ARRAY_SIZE(ov9640_regs_sxga);
+ break;
+ default:
+ dev_err(&client->dev, "Failed to select resolution!\n");
+ return -EINVAL;
+ }
+
+ /* select color matrix configuration for given color encoding */
+ if (code == V4L2_MBUS_FMT_UYVY8_2X8) {
+ matrix_regs = ov9640_regs_yuv;
+ matrix_regs_len = ARRAY_SIZE(ov9640_regs_yuv);
+ } else {
+ matrix_regs = ov9640_regs_rgb;
+ matrix_regs_len = ARRAY_SIZE(ov9640_regs_rgb);
+ }
+
+ /* write register settings into the module */
+ for (i = 0; i < ov9640_regs_len; i++) {
+ val = ov9640_regs[i].val;
+
+ switch (ov9640_regs[i].reg) {
+ case OV9640_COM7:
+ val |= alts->com7;
+ break;
+ case OV9640_COM12:
+ val |= alts->com12;
+ break;
+ case OV9640_COM13:
+ val |= alts->com13;
+ break;
+ case OV9640_COM15:
+ val |= alts->com15;
+ break;
+ }
+
+ ret = ov9640_reg_write(client, ov9640_regs[i].reg, val);
+ if (ret)
+ return ret;
+ }
+
+ /* write color matrix configuration into the module */
+ for (i = 0; i < matrix_regs_len; i++) {
+ ret = ov9640_reg_write(client, matrix_regs[i].reg,
+ matrix_regs[i].val);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/* program default register values */
+static int ov9640_prog_dflt(struct i2c_client *client)
+{
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(ov9640_regs_dflt); i++) {
+ ret = ov9640_reg_write(client, ov9640_regs_dflt[i].reg,
+ ov9640_regs_dflt[i].val);
+ if (ret)
+ return ret;
+ }
+
+ /* wait for the changes to actually happen, 140ms are not enough yet */
+ mdelay(150);
+
+ return 0;
+}
+
+/* set the format we will capture in */
+static int ov9640_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov9640_reg_alt alts = {0};
+ enum v4l2_colorspace cspace;
+ enum v4l2_mbus_pixelcode code = mf->code;
+ int ret;
+
+ ov9640_res_roundup(&mf->width, &mf->height);
+ ov9640_alter_regs(mf->code, &alts);
+
+ ov9640_reset(client);
+
+ ret = ov9640_prog_dflt(client);
+ if (ret)
+ return ret;
+
+ switch (code) {
+ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ cspace = V4L2_COLORSPACE_SRGB;
+ break;
+ default:
+ code = V4L2_MBUS_FMT_UYVY8_2X8;
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ cspace = V4L2_COLORSPACE_JPEG;
+ }
+
+ ret = ov9640_write_regs(client, mf->width, code, &alts);
+ if (!ret) {
+ mf->code = code;
+ mf->colorspace = cspace;
+ }
+
+ return ret;
+}
+
+static int ov9640_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ ov9640_res_roundup(&mf->width, &mf->height);
+
+ mf->field = V4L2_FIELD_NONE;
+
+ switch (mf->code) {
+ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
+ break;
+ default:
+ mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ }
+
+ return 0;
+}
+
+static int ov9640_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= ARRAY_SIZE(ov9640_codes))
+ return -EINVAL;
+
+ *code = ov9640_codes[index];
+ return 0;
+}
+
+static int ov9640_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ a->c.left = 0;
+ a->c.top = 0;
+ a->c.width = W_SXGA;
+ a->c.height = H_SXGA;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int ov9640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ a->bounds.width = W_SXGA;
+ a->bounds.height = H_SXGA;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int ov9640_video_probe(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov9640_priv *priv = to_ov9640_sensor(sd);
+ u8 pid, ver, midh, midl;
+ const char *devname;
+ int ret = 0;
+
+ /*
+ * check and show product ID and manufacturer ID
+ */
+
+ ret = ov9640_reg_read(client, OV9640_PID, &pid);
+ if (!ret)
+ ret = ov9640_reg_read(client, OV9640_VER, &ver);
+ if (!ret)
+ ret = ov9640_reg_read(client, OV9640_MIDH, &midh);
+ if (!ret)
+ ret = ov9640_reg_read(client, OV9640_MIDL, &midl);
+ if (ret)
+ return ret;
+
+ switch (VERSION(pid, ver)) {
+ case OV9640_V2:
+ devname = "ov9640";
+ priv->model = V4L2_IDENT_OV9640;
+ priv->revision = 2;
+ case OV9640_V3:
+ devname = "ov9640";
+ priv->model = V4L2_IDENT_OV9640;
+ priv->revision = 3;
+ break;
+ default:
+ dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver);
+ return -ENODEV;
+ }
+
+ dev_info(&client->dev, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
+ devname, pid, ver, midh, midl);
+
+ return v4l2_ctrl_handler_setup(&priv->hdl);
+}
+
+static const struct v4l2_ctrl_ops ov9640_ctrl_ops = {
+ .s_ctrl = ov9640_s_ctrl,
+};
+
+static struct v4l2_subdev_core_ops ov9640_core_ops = {
+ .g_chip_ident = ov9640_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = ov9640_get_register,
+ .s_register = ov9640_set_register,
+#endif
+
+};
+
+/* Request bus settings on camera side */
+static int ov9640_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops ov9640_video_ops = {
+ .s_stream = ov9640_s_stream,
+ .s_mbus_fmt = ov9640_s_fmt,
+ .try_mbus_fmt = ov9640_try_fmt,
+ .enum_mbus_fmt = ov9640_enum_fmt,
+ .cropcap = ov9640_cropcap,
+ .g_crop = ov9640_g_crop,
+ .g_mbus_config = ov9640_g_mbus_config,
+};
+
+static struct v4l2_subdev_ops ov9640_subdev_ops = {
+ .core = &ov9640_core_ops,
+ .video = &ov9640_video_ops,
+};
+
+/*
+ * i2c_driver function
+ */
+static int ov9640_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct ov9640_priv *priv;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ int ret;
+
+ if (!icl) {
+ dev_err(&client->dev, "Missing platform_data for driver\n");
+ return -EINVAL;
+ }
+
+ priv = kzalloc(sizeof(struct ov9640_priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&client->dev,
+ "Failed to allocate memory for private data!\n");
+ return -ENOMEM;
+ }
+
+ v4l2_i2c_subdev_init(&priv->subdev, client, &ov9640_subdev_ops);
+
+ v4l2_ctrl_handler_init(&priv->hdl, 2);
+ v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ priv->subdev.ctrl_handler = &priv->hdl;
+ if (priv->hdl.error) {
+ int err = priv->hdl.error;
+
+ kfree(priv);
+ return err;
+ }
+
+ ret = ov9640_video_probe(client);
+
+ if (ret) {
+ v4l2_ctrl_handler_free(&priv->hdl);
+ kfree(priv);
+ }
+
+ return ret;
+}
+
+static int ov9640_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov9640_priv *priv = to_ov9640_sensor(sd);
+
+ v4l2_device_unregister_subdev(&priv->subdev);
+ v4l2_ctrl_handler_free(&priv->hdl);
+ kfree(priv);
+ return 0;
+}
+
+static const struct i2c_device_id ov9640_id[] = {
+ { "ov9640", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ov9640_id);
+
+static struct i2c_driver ov9640_i2c_driver = {
+ .driver = {
+ .name = "ov9640",
+ },
+ .probe = ov9640_probe,
+ .remove = ov9640_remove,
+ .id_table = ov9640_id,
+};
+
+module_i2c_driver(ov9640_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV96xx");
+MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/ov9640.h b/drivers/media/video/ov9640.h
new file mode 100644
index 00000000..6b33a972
--- /dev/null
+++ b/drivers/media/video/ov9640.h
@@ -0,0 +1,207 @@
+/*
+ * OmniVision OV96xx Camera Header File
+ *
+ * Copyright (C) 2009 Marek Vasut <marek.vasut@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __DRIVERS_MEDIA_VIDEO_OV9640_H__
+#define __DRIVERS_MEDIA_VIDEO_OV9640_H__
+
+/* Register definitions */
+#define OV9640_GAIN 0x00
+#define OV9640_BLUE 0x01
+#define OV9640_RED 0x02
+#define OV9640_VFER 0x03
+#define OV9640_COM1 0x04
+#define OV9640_BAVE 0x05
+#define OV9640_GEAVE 0x06
+#define OV9640_RSID 0x07
+#define OV9640_RAVE 0x08
+#define OV9640_COM2 0x09
+#define OV9640_PID 0x0a
+#define OV9640_VER 0x0b
+#define OV9640_COM3 0x0c
+#define OV9640_COM4 0x0d
+#define OV9640_COM5 0x0e
+#define OV9640_COM6 0x0f
+#define OV9640_AECH 0x10
+#define OV9640_CLKRC 0x11
+#define OV9640_COM7 0x12
+#define OV9640_COM8 0x13
+#define OV9640_COM9 0x14
+#define OV9640_COM10 0x15
+/* 0x16 - RESERVED */
+#define OV9640_HSTART 0x17
+#define OV9640_HSTOP 0x18
+#define OV9640_VSTART 0x19
+#define OV9640_VSTOP 0x1a
+#define OV9640_PSHFT 0x1b
+#define OV9640_MIDH 0x1c
+#define OV9640_MIDL 0x1d
+#define OV9640_MVFP 0x1e
+#define OV9640_LAEC 0x1f
+#define OV9640_BOS 0x20
+#define OV9640_GBOS 0x21
+#define OV9640_GROS 0x22
+#define OV9640_ROS 0x23
+#define OV9640_AEW 0x24
+#define OV9640_AEB 0x25
+#define OV9640_VPT 0x26
+#define OV9640_BBIAS 0x27
+#define OV9640_GBBIAS 0x28
+/* 0x29 - RESERVED */
+#define OV9640_EXHCH 0x2a
+#define OV9640_EXHCL 0x2b
+#define OV9640_RBIAS 0x2c
+#define OV9640_ADVFL 0x2d
+#define OV9640_ADVFH 0x2e
+#define OV9640_YAVE 0x2f
+#define OV9640_HSYST 0x30
+#define OV9640_HSYEN 0x31
+#define OV9640_HREF 0x32
+#define OV9640_CHLF 0x33
+#define OV9640_ARBLM 0x34
+/* 0x35..0x36 - RESERVED */
+#define OV9640_ADC 0x37
+#define OV9640_ACOM 0x38
+#define OV9640_OFON 0x39
+#define OV9640_TSLB 0x3a
+#define OV9640_COM11 0x3b
+#define OV9640_COM12 0x3c
+#define OV9640_COM13 0x3d
+#define OV9640_COM14 0x3e
+#define OV9640_EDGE 0x3f
+#define OV9640_COM15 0x40
+#define OV9640_COM16 0x41
+#define OV9640_COM17 0x42
+/* 0x43..0x4e - RESERVED */
+#define OV9640_MTX1 0x4f
+#define OV9640_MTX2 0x50
+#define OV9640_MTX3 0x51
+#define OV9640_MTX4 0x52
+#define OV9640_MTX5 0x53
+#define OV9640_MTX6 0x54
+#define OV9640_MTX7 0x55
+#define OV9640_MTX8 0x56
+#define OV9640_MTX9 0x57
+#define OV9640_MTXS 0x58
+/* 0x59..0x61 - RESERVED */
+#define OV9640_LCC1 0x62
+#define OV9640_LCC2 0x63
+#define OV9640_LCC3 0x64
+#define OV9640_LCC4 0x65
+#define OV9640_LCC5 0x66
+#define OV9640_MANU 0x67
+#define OV9640_MANV 0x68
+#define OV9640_HV 0x69
+#define OV9640_MBD 0x6a
+#define OV9640_DBLV 0x6b
+#define OV9640_GSP 0x6c /* ... till 0x7b */
+#define OV9640_GST 0x7c /* ... till 0x8a */
+
+#define OV9640_CLKRC_DPLL_EN 0x80
+#define OV9640_CLKRC_DIRECT 0x40
+#define OV9640_CLKRC_DIV(x) ((x) & 0x3f)
+
+#define OV9640_PSHFT_VAL(x) ((x) & 0xff)
+
+#define OV9640_ACOM_2X_ANALOG 0x80
+#define OV9640_ACOM_RSVD 0x12
+
+#define OV9640_MVFP_V 0x10
+#define OV9640_MVFP_H 0x20
+
+#define OV9640_COM1_HREF_NOSKIP 0x00
+#define OV9640_COM1_HREF_2SKIP 0x04
+#define OV9640_COM1_HREF_3SKIP 0x08
+#define OV9640_COM1_QQFMT 0x20
+
+#define OV9640_COM2_SSM 0x10
+
+#define OV9640_COM3_VP 0x04
+
+#define OV9640_COM4_QQ_VP 0x80
+#define OV9640_COM4_RSVD 0x40
+
+#define OV9640_COM5_SYSCLK 0x80
+#define OV9640_COM5_LONGEXP 0x01
+
+#define OV9640_COM6_OPT_BLC 0x40
+#define OV9640_COM6_ADBLC_BIAS 0x08
+#define OV9640_COM6_FMT_RST 0x82
+#define OV9640_COM6_ADBLC_OPTEN 0x01
+
+#define OV9640_COM7_RAW_RGB 0x01
+#define OV9640_COM7_RGB 0x04
+#define OV9640_COM7_QCIF 0x08
+#define OV9640_COM7_QVGA 0x10
+#define OV9640_COM7_CIF 0x20
+#define OV9640_COM7_VGA 0x40
+#define OV9640_COM7_SCCB_RESET 0x80
+
+#define OV9640_TSLB_YVYU_YUYV 0x04
+#define OV9640_TSLB_YUYV_UYVY 0x08
+
+#define OV9640_COM12_YUV_AVG 0x04
+#define OV9640_COM12_RSVD 0x40
+
+#define OV9640_COM13_GAMMA_NONE 0x00
+#define OV9640_COM13_GAMMA_Y 0x40
+#define OV9640_COM13_GAMMA_RAW 0x80
+#define OV9640_COM13_RGB_AVG 0x20
+#define OV9640_COM13_MATRIX_EN 0x10
+#define OV9640_COM13_Y_DELAY_EN 0x08
+#define OV9640_COM13_YUV_DLY(x) ((x) & 0x07)
+
+#define OV9640_COM15_OR_00FF 0x00
+#define OV9640_COM15_OR_01FE 0x40
+#define OV9640_COM15_OR_10F0 0xc0
+#define OV9640_COM15_RGB_NORM 0x00
+#define OV9640_COM15_RGB_565 0x10
+#define OV9640_COM15_RGB_555 0x30
+
+#define OV9640_COM16_RB_AVG 0x01
+
+/* IDs */
+#define OV9640_V2 0x9648
+#define OV9640_V3 0x9649
+#define VERSION(pid, ver) (((pid) << 8) | ((ver) & 0xFF))
+
+/* supported resolutions */
+enum {
+ W_QQCIF = 88,
+ W_QQVGA = 160,
+ W_QCIF = 176,
+ W_QVGA = 320,
+ W_CIF = 352,
+ W_VGA = 640,
+ W_SXGA = 1280
+};
+#define H_SXGA 960
+
+/* Misc. structures */
+struct ov9640_reg_alt {
+ u8 com7;
+ u8 com12;
+ u8 com13;
+ u8 com15;
+};
+
+struct ov9640_reg {
+ u8 reg;
+ u8 val;
+};
+
+struct ov9640_priv {
+ struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
+
+ int model;
+ int revision;
+};
+
+#endif /* __DRIVERS_MEDIA_VIDEO_OV9640_H__ */
diff --git a/drivers/media/video/ov9740.c b/drivers/media/video/ov9740.c
new file mode 100644
index 00000000..3eb07c22
--- /dev/null
+++ b/drivers/media/video/ov9740.c
@@ -0,0 +1,1005 @@
+/*
+ * OmniVision OV9740 Camera Driver
+ *
+ * Copyright (C) 2011 NVIDIA Corporation
+ *
+ * Based on ov9640 camera driver.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+
+#define to_ov9740(sd) container_of(sd, struct ov9740_priv, subdev)
+
+/* General Status Registers */
+#define OV9740_MODEL_ID_HI 0x0000
+#define OV9740_MODEL_ID_LO 0x0001
+#define OV9740_REVISION_NUMBER 0x0002
+#define OV9740_MANUFACTURER_ID 0x0003
+#define OV9740_SMIA_VERSION 0x0004
+
+/* General Setup Registers */
+#define OV9740_MODE_SELECT 0x0100
+#define OV9740_IMAGE_ORT 0x0101
+#define OV9740_SOFTWARE_RESET 0x0103
+#define OV9740_GRP_PARAM_HOLD 0x0104
+#define OV9740_MSK_CORRUP_FM 0x0105
+
+/* Timing Setting */
+#define OV9740_FRM_LENGTH_LN_HI 0x0340 /* VTS */
+#define OV9740_FRM_LENGTH_LN_LO 0x0341 /* VTS */
+#define OV9740_LN_LENGTH_PCK_HI 0x0342 /* HTS */
+#define OV9740_LN_LENGTH_PCK_LO 0x0343 /* HTS */
+#define OV9740_X_ADDR_START_HI 0x0344
+#define OV9740_X_ADDR_START_LO 0x0345
+#define OV9740_Y_ADDR_START_HI 0x0346
+#define OV9740_Y_ADDR_START_LO 0x0347
+#define OV9740_X_ADDR_END_HI 0x0348
+#define OV9740_X_ADDR_END_LO 0x0349
+#define OV9740_Y_ADDR_END_HI 0x034a
+#define OV9740_Y_ADDR_END_LO 0x034b
+#define OV9740_X_OUTPUT_SIZE_HI 0x034c
+#define OV9740_X_OUTPUT_SIZE_LO 0x034d
+#define OV9740_Y_OUTPUT_SIZE_HI 0x034e
+#define OV9740_Y_OUTPUT_SIZE_LO 0x034f
+
+/* IO Control Registers */
+#define OV9740_IO_CREL00 0x3002
+#define OV9740_IO_CREL01 0x3004
+#define OV9740_IO_CREL02 0x3005
+#define OV9740_IO_OUTPUT_SEL01 0x3026
+#define OV9740_IO_OUTPUT_SEL02 0x3027
+
+/* AWB Registers */
+#define OV9740_AWB_MANUAL_CTRL 0x3406
+
+/* Analog Control Registers */
+#define OV9740_ANALOG_CTRL01 0x3601
+#define OV9740_ANALOG_CTRL02 0x3602
+#define OV9740_ANALOG_CTRL03 0x3603
+#define OV9740_ANALOG_CTRL04 0x3604
+#define OV9740_ANALOG_CTRL10 0x3610
+#define OV9740_ANALOG_CTRL12 0x3612
+#define OV9740_ANALOG_CTRL15 0x3615
+#define OV9740_ANALOG_CTRL20 0x3620
+#define OV9740_ANALOG_CTRL21 0x3621
+#define OV9740_ANALOG_CTRL22 0x3622
+#define OV9740_ANALOG_CTRL30 0x3630
+#define OV9740_ANALOG_CTRL31 0x3631
+#define OV9740_ANALOG_CTRL32 0x3632
+#define OV9740_ANALOG_CTRL33 0x3633
+
+/* Sensor Control */
+#define OV9740_SENSOR_CTRL03 0x3703
+#define OV9740_SENSOR_CTRL04 0x3704
+#define OV9740_SENSOR_CTRL05 0x3705
+#define OV9740_SENSOR_CTRL07 0x3707
+
+/* Timing Control */
+#define OV9740_TIMING_CTRL17 0x3817
+#define OV9740_TIMING_CTRL19 0x3819
+#define OV9740_TIMING_CTRL33 0x3833
+#define OV9740_TIMING_CTRL35 0x3835
+
+/* Banding Filter */
+#define OV9740_AEC_MAXEXPO_60_H 0x3a02
+#define OV9740_AEC_MAXEXPO_60_L 0x3a03
+#define OV9740_AEC_B50_STEP_HI 0x3a08
+#define OV9740_AEC_B50_STEP_LO 0x3a09
+#define OV9740_AEC_B60_STEP_HI 0x3a0a
+#define OV9740_AEC_B60_STEP_LO 0x3a0b
+#define OV9740_AEC_CTRL0D 0x3a0d
+#define OV9740_AEC_CTRL0E 0x3a0e
+#define OV9740_AEC_MAXEXPO_50_H 0x3a14
+#define OV9740_AEC_MAXEXPO_50_L 0x3a15
+
+/* AEC/AGC Control */
+#define OV9740_AEC_ENABLE 0x3503
+#define OV9740_GAIN_CEILING_01 0x3a18
+#define OV9740_GAIN_CEILING_02 0x3a19
+#define OV9740_AEC_HI_THRESHOLD 0x3a11
+#define OV9740_AEC_3A1A 0x3a1a
+#define OV9740_AEC_CTRL1B_WPT2 0x3a1b
+#define OV9740_AEC_CTRL0F_WPT 0x3a0f
+#define OV9740_AEC_CTRL10_BPT 0x3a10
+#define OV9740_AEC_CTRL1E_BPT2 0x3a1e
+#define OV9740_AEC_LO_THRESHOLD 0x3a1f
+
+/* BLC Control */
+#define OV9740_BLC_AUTO_ENABLE 0x4002
+#define OV9740_BLC_MODE 0x4005
+
+/* VFIFO */
+#define OV9740_VFIFO_READ_START_HI 0x4608
+#define OV9740_VFIFO_READ_START_LO 0x4609
+
+/* DVP Control */
+#define OV9740_DVP_VSYNC_CTRL02 0x4702
+#define OV9740_DVP_VSYNC_MODE 0x4704
+#define OV9740_DVP_VSYNC_CTRL06 0x4706
+
+/* PLL Setting */
+#define OV9740_PLL_MODE_CTRL01 0x3104
+#define OV9740_PRE_PLL_CLK_DIV 0x0305
+#define OV9740_PLL_MULTIPLIER 0x0307
+#define OV9740_VT_SYS_CLK_DIV 0x0303
+#define OV9740_VT_PIX_CLK_DIV 0x0301
+#define OV9740_PLL_CTRL3010 0x3010
+#define OV9740_VFIFO_CTRL00 0x460e
+
+/* ISP Control */
+#define OV9740_ISP_CTRL00 0x5000
+#define OV9740_ISP_CTRL01 0x5001
+#define OV9740_ISP_CTRL03 0x5003
+#define OV9740_ISP_CTRL05 0x5005
+#define OV9740_ISP_CTRL12 0x5012
+#define OV9740_ISP_CTRL19 0x5019
+#define OV9740_ISP_CTRL1A 0x501a
+#define OV9740_ISP_CTRL1E 0x501e
+#define OV9740_ISP_CTRL1F 0x501f
+#define OV9740_ISP_CTRL20 0x5020
+#define OV9740_ISP_CTRL21 0x5021
+
+/* AWB */
+#define OV9740_AWB_CTRL00 0x5180
+#define OV9740_AWB_CTRL01 0x5181
+#define OV9740_AWB_CTRL02 0x5182
+#define OV9740_AWB_CTRL03 0x5183
+#define OV9740_AWB_ADV_CTRL01 0x5184
+#define OV9740_AWB_ADV_CTRL02 0x5185
+#define OV9740_AWB_ADV_CTRL03 0x5186
+#define OV9740_AWB_ADV_CTRL04 0x5187
+#define OV9740_AWB_ADV_CTRL05 0x5188
+#define OV9740_AWB_ADV_CTRL06 0x5189
+#define OV9740_AWB_ADV_CTRL07 0x518a
+#define OV9740_AWB_ADV_CTRL08 0x518b
+#define OV9740_AWB_ADV_CTRL09 0x518c
+#define OV9740_AWB_ADV_CTRL10 0x518d
+#define OV9740_AWB_ADV_CTRL11 0x518e
+#define OV9740_AWB_CTRL0F 0x518f
+#define OV9740_AWB_CTRL10 0x5190
+#define OV9740_AWB_CTRL11 0x5191
+#define OV9740_AWB_CTRL12 0x5192
+#define OV9740_AWB_CTRL13 0x5193
+#define OV9740_AWB_CTRL14 0x5194
+
+/* MIPI Control */
+#define OV9740_MIPI_CTRL00 0x4800
+#define OV9740_MIPI_3837 0x3837
+#define OV9740_MIPI_CTRL01 0x4801
+#define OV9740_MIPI_CTRL03 0x4803
+#define OV9740_MIPI_CTRL05 0x4805
+#define OV9740_VFIFO_RD_CTRL 0x4601
+#define OV9740_MIPI_CTRL_3012 0x3012
+#define OV9740_SC_CMMM_MIPI_CTR 0x3014
+
+#define OV9740_MAX_WIDTH 1280
+#define OV9740_MAX_HEIGHT 720
+
+/* Misc. structures */
+struct ov9740_reg {
+ u16 reg;
+ u8 val;
+};
+
+struct ov9740_priv {
+ struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
+
+ int ident;
+ u16 model;
+ u8 revision;
+ u8 manid;
+ u8 smiaver;
+
+ bool flag_vflip;
+ bool flag_hflip;
+
+ /* For suspend/resume. */
+ struct v4l2_mbus_framefmt current_mf;
+ bool current_enable;
+};
+
+static const struct ov9740_reg ov9740_defaults[] = {
+ /* Software Reset */
+ { OV9740_SOFTWARE_RESET, 0x01 },
+
+ /* Banding Filter */
+ { OV9740_AEC_B50_STEP_HI, 0x00 },
+ { OV9740_AEC_B50_STEP_LO, 0xe8 },
+ { OV9740_AEC_CTRL0E, 0x03 },
+ { OV9740_AEC_MAXEXPO_50_H, 0x15 },
+ { OV9740_AEC_MAXEXPO_50_L, 0xc6 },
+ { OV9740_AEC_B60_STEP_HI, 0x00 },
+ { OV9740_AEC_B60_STEP_LO, 0xc0 },
+ { OV9740_AEC_CTRL0D, 0x04 },
+ { OV9740_AEC_MAXEXPO_60_H, 0x18 },
+ { OV9740_AEC_MAXEXPO_60_L, 0x20 },
+
+ /* LC */
+ { 0x5842, 0x02 }, { 0x5843, 0x5e }, { 0x5844, 0x04 }, { 0x5845, 0x32 },
+ { 0x5846, 0x03 }, { 0x5847, 0x29 }, { 0x5848, 0x02 }, { 0x5849, 0xcc },
+
+ /* Un-documented OV9740 registers */
+ { 0x5800, 0x29 }, { 0x5801, 0x25 }, { 0x5802, 0x20 }, { 0x5803, 0x21 },
+ { 0x5804, 0x26 }, { 0x5805, 0x2e }, { 0x5806, 0x11 }, { 0x5807, 0x0c },
+ { 0x5808, 0x09 }, { 0x5809, 0x0a }, { 0x580a, 0x0e }, { 0x580b, 0x16 },
+ { 0x580c, 0x06 }, { 0x580d, 0x02 }, { 0x580e, 0x00 }, { 0x580f, 0x00 },
+ { 0x5810, 0x04 }, { 0x5811, 0x0a }, { 0x5812, 0x05 }, { 0x5813, 0x02 },
+ { 0x5814, 0x00 }, { 0x5815, 0x00 }, { 0x5816, 0x03 }, { 0x5817, 0x09 },
+ { 0x5818, 0x0f }, { 0x5819, 0x0a }, { 0x581a, 0x07 }, { 0x581b, 0x08 },
+ { 0x581c, 0x0b }, { 0x581d, 0x14 }, { 0x581e, 0x28 }, { 0x581f, 0x23 },
+ { 0x5820, 0x1d }, { 0x5821, 0x1e }, { 0x5822, 0x24 }, { 0x5823, 0x2a },
+ { 0x5824, 0x4f }, { 0x5825, 0x6f }, { 0x5826, 0x5f }, { 0x5827, 0x7f },
+ { 0x5828, 0x9f }, { 0x5829, 0x5f }, { 0x582a, 0x8f }, { 0x582b, 0x9e },
+ { 0x582c, 0x8f }, { 0x582d, 0x9f }, { 0x582e, 0x4f }, { 0x582f, 0x87 },
+ { 0x5830, 0x86 }, { 0x5831, 0x97 }, { 0x5832, 0xae }, { 0x5833, 0x3f },
+ { 0x5834, 0x8e }, { 0x5835, 0x7c }, { 0x5836, 0x7e }, { 0x5837, 0xaf },
+ { 0x5838, 0x8f }, { 0x5839, 0x8f }, { 0x583a, 0x9f }, { 0x583b, 0x7f },
+ { 0x583c, 0x5f },
+
+ /* Y Gamma */
+ { 0x5480, 0x07 }, { 0x5481, 0x18 }, { 0x5482, 0x2c }, { 0x5483, 0x4e },
+ { 0x5484, 0x5e }, { 0x5485, 0x6b }, { 0x5486, 0x77 }, { 0x5487, 0x82 },
+ { 0x5488, 0x8c }, { 0x5489, 0x95 }, { 0x548a, 0xa4 }, { 0x548b, 0xb1 },
+ { 0x548c, 0xc6 }, { 0x548d, 0xd8 }, { 0x548e, 0xe9 },
+
+ /* UV Gamma */
+ { 0x5490, 0x0f }, { 0x5491, 0xff }, { 0x5492, 0x0d }, { 0x5493, 0x05 },
+ { 0x5494, 0x07 }, { 0x5495, 0x1a }, { 0x5496, 0x04 }, { 0x5497, 0x01 },
+ { 0x5498, 0x03 }, { 0x5499, 0x53 }, { 0x549a, 0x02 }, { 0x549b, 0xeb },
+ { 0x549c, 0x02 }, { 0x549d, 0xa0 }, { 0x549e, 0x02 }, { 0x549f, 0x67 },
+ { 0x54a0, 0x02 }, { 0x54a1, 0x3b }, { 0x54a2, 0x02 }, { 0x54a3, 0x18 },
+ { 0x54a4, 0x01 }, { 0x54a5, 0xe7 }, { 0x54a6, 0x01 }, { 0x54a7, 0xc3 },
+ { 0x54a8, 0x01 }, { 0x54a9, 0x94 }, { 0x54aa, 0x01 }, { 0x54ab, 0x72 },
+ { 0x54ac, 0x01 }, { 0x54ad, 0x57 },
+
+ /* AWB */
+ { OV9740_AWB_CTRL00, 0xf0 },
+ { OV9740_AWB_CTRL01, 0x00 },
+ { OV9740_AWB_CTRL02, 0x41 },
+ { OV9740_AWB_CTRL03, 0x42 },
+ { OV9740_AWB_ADV_CTRL01, 0x8a },
+ { OV9740_AWB_ADV_CTRL02, 0x61 },
+ { OV9740_AWB_ADV_CTRL03, 0xce },
+ { OV9740_AWB_ADV_CTRL04, 0xa8 },
+ { OV9740_AWB_ADV_CTRL05, 0x17 },
+ { OV9740_AWB_ADV_CTRL06, 0x1f },
+ { OV9740_AWB_ADV_CTRL07, 0x27 },
+ { OV9740_AWB_ADV_CTRL08, 0x41 },
+ { OV9740_AWB_ADV_CTRL09, 0x34 },
+ { OV9740_AWB_ADV_CTRL10, 0xf0 },
+ { OV9740_AWB_ADV_CTRL11, 0x10 },
+ { OV9740_AWB_CTRL0F, 0xff },
+ { OV9740_AWB_CTRL10, 0x00 },
+ { OV9740_AWB_CTRL11, 0xff },
+ { OV9740_AWB_CTRL12, 0x00 },
+ { OV9740_AWB_CTRL13, 0xff },
+ { OV9740_AWB_CTRL14, 0x00 },
+
+ /* CIP */
+ { 0x530d, 0x12 },
+
+ /* CMX */
+ { 0x5380, 0x01 }, { 0x5381, 0x00 }, { 0x5382, 0x00 }, { 0x5383, 0x17 },
+ { 0x5384, 0x00 }, { 0x5385, 0x01 }, { 0x5386, 0x00 }, { 0x5387, 0x00 },
+ { 0x5388, 0x00 }, { 0x5389, 0xe0 }, { 0x538a, 0x00 }, { 0x538b, 0x20 },
+ { 0x538c, 0x00 }, { 0x538d, 0x00 }, { 0x538e, 0x00 }, { 0x538f, 0x16 },
+ { 0x5390, 0x00 }, { 0x5391, 0x9c }, { 0x5392, 0x00 }, { 0x5393, 0xa0 },
+ { 0x5394, 0x18 },
+
+ /* 50/60 Detection */
+ { 0x3c0a, 0x9c }, { 0x3c0b, 0x3f },
+
+ /* Output Select */
+ { OV9740_IO_OUTPUT_SEL01, 0x00 },
+ { OV9740_IO_OUTPUT_SEL02, 0x00 },
+ { OV9740_IO_CREL00, 0x00 },
+ { OV9740_IO_CREL01, 0x00 },
+ { OV9740_IO_CREL02, 0x00 },
+
+ /* AWB Control */
+ { OV9740_AWB_MANUAL_CTRL, 0x00 },
+
+ /* Analog Control */
+ { OV9740_ANALOG_CTRL03, 0xaa },
+ { OV9740_ANALOG_CTRL32, 0x2f },
+ { OV9740_ANALOG_CTRL20, 0x66 },
+ { OV9740_ANALOG_CTRL21, 0xc0 },
+ { OV9740_ANALOG_CTRL31, 0x52 },
+ { OV9740_ANALOG_CTRL33, 0x50 },
+ { OV9740_ANALOG_CTRL30, 0xca },
+ { OV9740_ANALOG_CTRL04, 0x0c },
+ { OV9740_ANALOG_CTRL01, 0x40 },
+ { OV9740_ANALOG_CTRL02, 0x16 },
+ { OV9740_ANALOG_CTRL10, 0xa1 },
+ { OV9740_ANALOG_CTRL12, 0x24 },
+ { OV9740_ANALOG_CTRL22, 0x9f },
+ { OV9740_ANALOG_CTRL15, 0xf0 },
+
+ /* Sensor Control */
+ { OV9740_SENSOR_CTRL03, 0x42 },
+ { OV9740_SENSOR_CTRL04, 0x10 },
+ { OV9740_SENSOR_CTRL05, 0x45 },
+ { OV9740_SENSOR_CTRL07, 0x14 },
+
+ /* Timing Control */
+ { OV9740_TIMING_CTRL33, 0x04 },
+ { OV9740_TIMING_CTRL35, 0x02 },
+ { OV9740_TIMING_CTRL19, 0x6e },
+ { OV9740_TIMING_CTRL17, 0x94 },
+
+ /* AEC/AGC Control */
+ { OV9740_AEC_ENABLE, 0x10 },
+ { OV9740_GAIN_CEILING_01, 0x00 },
+ { OV9740_GAIN_CEILING_02, 0x7f },
+ { OV9740_AEC_HI_THRESHOLD, 0xa0 },
+ { OV9740_AEC_3A1A, 0x05 },
+ { OV9740_AEC_CTRL1B_WPT2, 0x50 },
+ { OV9740_AEC_CTRL0F_WPT, 0x50 },
+ { OV9740_AEC_CTRL10_BPT, 0x4c },
+ { OV9740_AEC_CTRL1E_BPT2, 0x4c },
+ { OV9740_AEC_LO_THRESHOLD, 0x26 },
+
+ /* BLC Control */
+ { OV9740_BLC_AUTO_ENABLE, 0x45 },
+ { OV9740_BLC_MODE, 0x18 },
+
+ /* DVP Control */
+ { OV9740_DVP_VSYNC_CTRL02, 0x04 },
+ { OV9740_DVP_VSYNC_MODE, 0x00 },
+ { OV9740_DVP_VSYNC_CTRL06, 0x08 },
+
+ /* PLL Setting */
+ { OV9740_PLL_MODE_CTRL01, 0x20 },
+ { OV9740_PRE_PLL_CLK_DIV, 0x03 },
+ { OV9740_PLL_MULTIPLIER, 0x4c },
+ { OV9740_VT_SYS_CLK_DIV, 0x01 },
+ { OV9740_VT_PIX_CLK_DIV, 0x08 },
+ { OV9740_PLL_CTRL3010, 0x01 },
+ { OV9740_VFIFO_CTRL00, 0x82 },
+
+ /* Timing Setting */
+ /* VTS */
+ { OV9740_FRM_LENGTH_LN_HI, 0x03 },
+ { OV9740_FRM_LENGTH_LN_LO, 0x07 },
+ /* HTS */
+ { OV9740_LN_LENGTH_PCK_HI, 0x06 },
+ { OV9740_LN_LENGTH_PCK_LO, 0x62 },
+
+ /* MIPI Control */
+ { OV9740_MIPI_CTRL00, 0x44 }, /* 0x64 for discontinuous clk */
+ { OV9740_MIPI_3837, 0x01 },
+ { OV9740_MIPI_CTRL01, 0x0f },
+ { OV9740_MIPI_CTRL03, 0x05 },
+ { OV9740_MIPI_CTRL05, 0x10 },
+ { OV9740_VFIFO_RD_CTRL, 0x16 },
+ { OV9740_MIPI_CTRL_3012, 0x70 },
+ { OV9740_SC_CMMM_MIPI_CTR, 0x01 },
+
+ /* YUYV order */
+ { OV9740_ISP_CTRL19, 0x02 },
+};
+
+static enum v4l2_mbus_pixelcode ov9740_codes[] = {
+ V4L2_MBUS_FMT_YUYV8_2X8,
+};
+
+/* read a register */
+static int ov9740_reg_read(struct i2c_client *client, u16 reg, u8 *val)
+{
+ int ret;
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = (u8 *)&reg,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = val,
+ },
+ };
+
+ reg = swab16(reg);
+
+ ret = i2c_transfer(client->adapter, msg, 2);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed reading register 0x%04x!\n", reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* write a register */
+static int ov9740_reg_write(struct i2c_client *client, u16 reg, u8 val)
+{
+ struct i2c_msg msg;
+ struct {
+ u16 reg;
+ u8 val;
+ } __packed buf;
+ int ret;
+
+ reg = swab16(reg);
+
+ buf.reg = reg;
+ buf.val = val;
+
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = 3;
+ msg.buf = (u8 *)&buf;
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed writing register 0x%04x!\n", reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+
+/* Read a register, alter its bits, write it back */
+static int ov9740_reg_rmw(struct i2c_client *client, u16 reg, u8 set, u8 unset)
+{
+ u8 val;
+ int ret;
+
+ ret = ov9740_reg_read(client, reg, &val);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "[Read]-Modify-Write of register 0x%04x failed!\n",
+ reg);
+ return ret;
+ }
+
+ val |= set;
+ val &= ~unset;
+
+ ret = ov9740_reg_write(client, reg, val);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "Read-Modify-[Write] of register 0x%04x failed!\n",
+ reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ov9740_reg_write_array(struct i2c_client *client,
+ const struct ov9740_reg *regarray,
+ int regarraylen)
+{
+ int i;
+ int ret;
+
+ for (i = 0; i < regarraylen; i++) {
+ ret = ov9740_reg_write(client,
+ regarray[i].reg, regarray[i].val);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Start/Stop streaming from the device */
+static int ov9740_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov9740_priv *priv = to_ov9740(sd);
+ int ret;
+
+ /* Program orientation register. */
+ if (priv->flag_vflip)
+ ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0x2, 0);
+ else
+ ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0, 0x2);
+ if (ret < 0)
+ return ret;
+
+ if (priv->flag_hflip)
+ ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0x1, 0);
+ else
+ ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0, 0x1);
+ if (ret < 0)
+ return ret;
+
+ if (enable) {
+ dev_dbg(&client->dev, "Enabling Streaming\n");
+ /* Start Streaming */
+ ret = ov9740_reg_write(client, OV9740_MODE_SELECT, 0x01);
+
+ } else {
+ dev_dbg(&client->dev, "Disabling Streaming\n");
+ /* Software Reset */
+ ret = ov9740_reg_write(client, OV9740_SOFTWARE_RESET, 0x01);
+ if (!ret)
+ /* Setting Streaming to Standby */
+ ret = ov9740_reg_write(client, OV9740_MODE_SELECT,
+ 0x00);
+ }
+
+ priv->current_enable = enable;
+
+ return ret;
+}
+
+/* select nearest higher resolution for capture */
+static void ov9740_res_roundup(u32 *width, u32 *height)
+{
+ /* Width must be a multiple of 4 pixels. */
+ *width = ALIGN(*width, 4);
+
+ /* Max resolution is 1280x720 (720p). */
+ if (*width > OV9740_MAX_WIDTH)
+ *width = OV9740_MAX_WIDTH;
+
+ if (*height > OV9740_MAX_HEIGHT)
+ *height = OV9740_MAX_HEIGHT;
+}
+
+/* Setup registers according to resolution and color encoding */
+static int ov9740_set_res(struct i2c_client *client, u32 width, u32 height)
+{
+ u32 x_start;
+ u32 y_start;
+ u32 x_end;
+ u32 y_end;
+ bool scaling = 0;
+ u32 scale_input_x;
+ u32 scale_input_y;
+ int ret;
+
+ if ((width != OV9740_MAX_WIDTH) || (height != OV9740_MAX_HEIGHT))
+ scaling = 1;
+
+ /*
+ * Try to use as much of the sensor area as possible when supporting
+ * smaller resolutions. Depending on the aspect ratio of the
+ * chosen resolution, we can either use the full width of the sensor,
+ * or the full height of the sensor (or both if the aspect ratio is
+ * the same as 1280x720.
+ */
+ if ((OV9740_MAX_WIDTH * height) > (OV9740_MAX_HEIGHT * width)) {
+ scale_input_x = (OV9740_MAX_HEIGHT * width) / height;
+ scale_input_y = OV9740_MAX_HEIGHT;
+ } else {
+ scale_input_x = OV9740_MAX_WIDTH;
+ scale_input_y = (OV9740_MAX_WIDTH * height) / width;
+ }
+
+ /* These describe the area of the sensor to use. */
+ x_start = (OV9740_MAX_WIDTH - scale_input_x) / 2;
+ y_start = (OV9740_MAX_HEIGHT - scale_input_y) / 2;
+ x_end = x_start + scale_input_x - 1;
+ y_end = y_start + scale_input_y - 1;
+
+ ret = ov9740_reg_write(client, OV9740_X_ADDR_START_HI, x_start >> 8);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_X_ADDR_START_LO, x_start & 0xff);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_Y_ADDR_START_HI, y_start >> 8);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_Y_ADDR_START_LO, y_start & 0xff);
+ if (ret)
+ goto done;
+
+ ret = ov9740_reg_write(client, OV9740_X_ADDR_END_HI, x_end >> 8);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_X_ADDR_END_LO, x_end & 0xff);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_Y_ADDR_END_HI, y_end >> 8);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_Y_ADDR_END_LO, y_end & 0xff);
+ if (ret)
+ goto done;
+
+ ret = ov9740_reg_write(client, OV9740_X_OUTPUT_SIZE_HI, width >> 8);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_X_OUTPUT_SIZE_LO, width & 0xff);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_Y_OUTPUT_SIZE_HI, height >> 8);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_Y_OUTPUT_SIZE_LO, height & 0xff);
+ if (ret)
+ goto done;
+
+ ret = ov9740_reg_write(client, OV9740_ISP_CTRL1E, scale_input_x >> 8);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_ISP_CTRL1F, scale_input_x & 0xff);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_ISP_CTRL20, scale_input_y >> 8);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_ISP_CTRL21, scale_input_y & 0xff);
+ if (ret)
+ goto done;
+
+ ret = ov9740_reg_write(client, OV9740_VFIFO_READ_START_HI,
+ (scale_input_x - width) >> 8);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_VFIFO_READ_START_LO,
+ (scale_input_x - width) & 0xff);
+ if (ret)
+ goto done;
+
+ ret = ov9740_reg_write(client, OV9740_ISP_CTRL00, 0xff);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_ISP_CTRL01, 0xef |
+ (scaling << 4));
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_ISP_CTRL03, 0xff);
+
+done:
+ return ret;
+}
+
+/* set the format we will capture in */
+static int ov9740_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov9740_priv *priv = to_ov9740(sd);
+ enum v4l2_colorspace cspace;
+ enum v4l2_mbus_pixelcode code = mf->code;
+ int ret;
+
+ ov9740_res_roundup(&mf->width, &mf->height);
+
+ switch (code) {
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ cspace = V4L2_COLORSPACE_SRGB;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = ov9740_reg_write_array(client, ov9740_defaults,
+ ARRAY_SIZE(ov9740_defaults));
+ if (ret < 0)
+ return ret;
+
+ ret = ov9740_set_res(client, mf->width, mf->height);
+ if (ret < 0)
+ return ret;
+
+ mf->code = code;
+ mf->colorspace = cspace;
+
+ memcpy(&priv->current_mf, mf, sizeof(struct v4l2_mbus_framefmt));
+
+ return ret;
+}
+
+static int ov9740_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ ov9740_res_roundup(&mf->width, &mf->height);
+
+ mf->field = V4L2_FIELD_NONE;
+ mf->code = V4L2_MBUS_FMT_YUYV8_2X8;
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
+
+ return 0;
+}
+
+static int ov9740_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= ARRAY_SIZE(ov9740_codes))
+ return -EINVAL;
+
+ *code = ov9740_codes[index];
+
+ return 0;
+}
+
+static int ov9740_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ a->bounds.width = OV9740_MAX_WIDTH;
+ a->bounds.height = OV9740_MAX_HEIGHT;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int ov9740_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ a->c.left = 0;
+ a->c.top = 0;
+ a->c.width = OV9740_MAX_WIDTH;
+ a->c.height = OV9740_MAX_HEIGHT;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+/* Set status of additional camera capabilities */
+static int ov9740_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov9740_priv *priv =
+ container_of(ctrl->handler, struct ov9740_priv, hdl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ priv->flag_vflip = ctrl->val;
+ break;
+ case V4L2_CID_HFLIP:
+ priv->flag_hflip = ctrl->val;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Get chip identification */
+static int ov9740_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct ov9740_priv *priv = to_ov9740(sd);
+
+ id->ident = priv->ident;
+ id->revision = priv->revision;
+
+ return 0;
+}
+
+static int ov9740_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct ov9740_priv *priv = to_ov9740(sd);
+
+ if (!priv->current_enable)
+ return 0;
+
+ if (on) {
+ ov9740_s_fmt(sd, &priv->current_mf);
+ ov9740_s_stream(sd, priv->current_enable);
+ } else {
+ ov9740_s_stream(sd, 0);
+ priv->current_enable = true;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov9740_get_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+ u8 val;
+
+ if (reg->reg & ~0xffff)
+ return -EINVAL;
+
+ reg->size = 2;
+
+ ret = ov9740_reg_read(client, reg->reg, &val);
+ if (ret)
+ return ret;
+
+ reg->val = (__u64)val;
+
+ return ret;
+}
+
+static int ov9740_set_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->reg & ~0xffff || reg->val & ~0xff)
+ return -EINVAL;
+
+ return ov9740_reg_write(client, reg->reg, reg->val);
+}
+#endif
+
+static int ov9740_video_probe(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov9740_priv *priv = to_ov9740(sd);
+ u8 modelhi, modello;
+ int ret;
+
+ /*
+ * check and show product ID and manufacturer ID
+ */
+ ret = ov9740_reg_read(client, OV9740_MODEL_ID_HI, &modelhi);
+ if (ret < 0)
+ goto err;
+
+ ret = ov9740_reg_read(client, OV9740_MODEL_ID_LO, &modello);
+ if (ret < 0)
+ goto err;
+
+ priv->model = (modelhi << 8) | modello;
+
+ ret = ov9740_reg_read(client, OV9740_REVISION_NUMBER, &priv->revision);
+ if (ret < 0)
+ goto err;
+
+ ret = ov9740_reg_read(client, OV9740_MANUFACTURER_ID, &priv->manid);
+ if (ret < 0)
+ goto err;
+
+ ret = ov9740_reg_read(client, OV9740_SMIA_VERSION, &priv->smiaver);
+ if (ret < 0)
+ goto err;
+
+ if (priv->model != 0x9740) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ priv->ident = V4L2_IDENT_OV9740;
+
+ dev_info(&client->dev, "ov9740 Model ID 0x%04x, Revision 0x%02x, "
+ "Manufacturer 0x%02x, SMIA Version 0x%02x\n",
+ priv->model, priv->revision, priv->manid, priv->smiaver);
+
+err:
+ return ret;
+}
+
+/* Request bus settings on camera side */
+static int ov9740_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops ov9740_video_ops = {
+ .s_stream = ov9740_s_stream,
+ .s_mbus_fmt = ov9740_s_fmt,
+ .try_mbus_fmt = ov9740_try_fmt,
+ .enum_mbus_fmt = ov9740_enum_fmt,
+ .cropcap = ov9740_cropcap,
+ .g_crop = ov9740_g_crop,
+ .g_mbus_config = ov9740_g_mbus_config,
+};
+
+static struct v4l2_subdev_core_ops ov9740_core_ops = {
+ .g_chip_ident = ov9740_g_chip_ident,
+ .s_power = ov9740_s_power,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = ov9740_get_register,
+ .s_register = ov9740_set_register,
+#endif
+};
+
+static struct v4l2_subdev_ops ov9740_subdev_ops = {
+ .core = &ov9740_core_ops,
+ .video = &ov9740_video_ops,
+};
+
+static const struct v4l2_ctrl_ops ov9740_ctrl_ops = {
+ .s_ctrl = ov9740_s_ctrl,
+};
+
+/*
+ * i2c_driver function
+ */
+static int ov9740_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct ov9740_priv *priv;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ int ret;
+
+ if (!icl) {
+ dev_err(&client->dev, "Missing platform_data for driver\n");
+ return -EINVAL;
+ }
+
+ priv = kzalloc(sizeof(struct ov9740_priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&client->dev, "Failed to allocate private data!\n");
+ return -ENOMEM;
+ }
+
+ v4l2_i2c_subdev_init(&priv->subdev, client, &ov9740_subdev_ops);
+ v4l2_ctrl_handler_init(&priv->hdl, 13);
+ v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ priv->subdev.ctrl_handler = &priv->hdl;
+ if (priv->hdl.error) {
+ int err = priv->hdl.error;
+
+ kfree(priv);
+ return err;
+ }
+
+ ret = ov9740_video_probe(client);
+ if (!ret)
+ ret = v4l2_ctrl_handler_setup(&priv->hdl);
+ if (ret < 0) {
+ v4l2_ctrl_handler_free(&priv->hdl);
+ kfree(priv);
+ }
+
+ return ret;
+}
+
+static int ov9740_remove(struct i2c_client *client)
+{
+ struct ov9740_priv *priv = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(&priv->subdev);
+ v4l2_ctrl_handler_free(&priv->hdl);
+ kfree(priv);
+ return 0;
+}
+
+static const struct i2c_device_id ov9740_id[] = {
+ { "ov9740", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ov9740_id);
+
+static struct i2c_driver ov9740_i2c_driver = {
+ .driver = {
+ .name = "ov9740",
+ },
+ .probe = ov9740_probe,
+ .remove = ov9740_remove,
+ .id_table = ov9740_id,
+};
+
+module_i2c_driver(ov9740_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV9740");
+MODULE_AUTHOR("Andrew Chew <achew@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/pms.c b/drivers/media/video/pms.c
new file mode 100644
index 00000000..e753b5e4
--- /dev/null
+++ b/drivers/media/video/pms.c
@@ -0,0 +1,1144 @@
+/*
+ * Media Vision Pro Movie Studio
+ * or
+ * "all you need is an I2C bus some RAM and a prayer"
+ *
+ * This draws heavily on code
+ *
+ * (c) Wolfgang Koehler, wolf@first.gmd.de, Dec. 1994
+ * Kiefernring 15
+ * 14478 Potsdam, Germany
+ *
+ * Most of this code is directly derived from his userspace driver.
+ * His driver works so send any reports to alan@lxorguk.ukuu.org.uk
+ * unless the userspace driver also doesn't work for you...
+ *
+ * Changes:
+ * 25-11-2009 Hans Verkuil <hverkuil@xs4all.nl>
+ * - converted to version 2 of the V4L API.
+ * 08/07/2003 Daniele Bellucci <bellucda@tiscali.it>
+ * - pms_capture: report back -EFAULT
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+#include <asm/io.h>
+
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.4");
+
+#define MOTOROLA 1
+#define PHILIPS2 2 /* SAA7191 */
+#define PHILIPS1 3
+#define MVVMEMORYWIDTH 0x40 /* 512 bytes */
+
+struct i2c_info {
+ u8 slave;
+ u8 sub;
+ u8 data;
+ u8 hits;
+};
+
+struct pms {
+ struct v4l2_device v4l2_dev;
+ struct video_device vdev;
+ int height;
+ int width;
+ int depth;
+ int input;
+ s32 brightness, saturation, hue, contrast;
+ struct mutex lock;
+ int i2c_count;
+ struct i2c_info i2cinfo[64];
+
+ int decoder;
+ int standard; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */
+ v4l2_std_id std;
+ int io;
+ int data;
+ void __iomem *mem;
+};
+
+static struct pms pms_card;
+
+/*
+ * I/O ports and Shared Memory
+ */
+
+static int io_port = 0x250;
+module_param(io_port, int, 0);
+
+static int mem_base = 0xc8000;
+module_param(mem_base, int, 0);
+
+static int video_nr = -1;
+module_param(video_nr, int, 0);
+
+
+static inline void mvv_write(struct pms *dev, u8 index, u8 value)
+{
+ outw(index | (value << 8), dev->io);
+}
+
+static inline u8 mvv_read(struct pms *dev, u8 index)
+{
+ outb(index, dev->io);
+ return inb(dev->data);
+}
+
+static int pms_i2c_stat(struct pms *dev, u8 slave)
+{
+ int counter = 0;
+ int i;
+
+ outb(0x28, dev->io);
+
+ while ((inb(dev->data) & 0x01) == 0)
+ if (counter++ == 256)
+ break;
+
+ while ((inb(dev->data) & 0x01) != 0)
+ if (counter++ == 256)
+ break;
+
+ outb(slave, dev->io);
+
+ counter = 0;
+ while ((inb(dev->data) & 0x01) == 0)
+ if (counter++ == 256)
+ break;
+
+ while ((inb(dev->data) & 0x01) != 0)
+ if (counter++ == 256)
+ break;
+
+ for (i = 0; i < 12; i++) {
+ char st = inb(dev->data);
+
+ if ((st & 2) != 0)
+ return -1;
+ if ((st & 1) == 0)
+ break;
+ }
+ outb(0x29, dev->io);
+ return inb(dev->data);
+}
+
+static int pms_i2c_write(struct pms *dev, u16 slave, u16 sub, u16 data)
+{
+ int skip = 0;
+ int count;
+ int i;
+
+ for (i = 0; i < dev->i2c_count; i++) {
+ if ((dev->i2cinfo[i].slave == slave) &&
+ (dev->i2cinfo[i].sub == sub)) {
+ if (dev->i2cinfo[i].data == data)
+ skip = 1;
+ dev->i2cinfo[i].data = data;
+ i = dev->i2c_count + 1;
+ }
+ }
+
+ if (i == dev->i2c_count && dev->i2c_count < 64) {
+ dev->i2cinfo[dev->i2c_count].slave = slave;
+ dev->i2cinfo[dev->i2c_count].sub = sub;
+ dev->i2cinfo[dev->i2c_count].data = data;
+ dev->i2c_count++;
+ }
+
+ if (skip)
+ return 0;
+
+ mvv_write(dev, 0x29, sub);
+ mvv_write(dev, 0x2A, data);
+ mvv_write(dev, 0x28, slave);
+
+ outb(0x28, dev->io);
+
+ count = 0;
+ while ((inb(dev->data) & 1) == 0)
+ if (count > 255)
+ break;
+ while ((inb(dev->data) & 1) != 0)
+ if (count > 255)
+ break;
+
+ count = inb(dev->data);
+
+ if (count & 2)
+ return -1;
+ return count;
+}
+
+static int pms_i2c_read(struct pms *dev, int slave, int sub)
+{
+ int i;
+
+ for (i = 0; i < dev->i2c_count; i++) {
+ if (dev->i2cinfo[i].slave == slave && dev->i2cinfo[i].sub == sub)
+ return dev->i2cinfo[i].data;
+ }
+ return 0;
+}
+
+
+static void pms_i2c_andor(struct pms *dev, int slave, int sub, int and, int or)
+{
+ u8 tmp;
+
+ tmp = pms_i2c_read(dev, slave, sub);
+ tmp = (tmp & and) | or;
+ pms_i2c_write(dev, slave, sub, tmp);
+}
+
+/*
+ * Control functions
+ */
+
+
+static void pms_videosource(struct pms *dev, short source)
+{
+ switch (dev->decoder) {
+ case MOTOROLA:
+ break;
+ case PHILIPS2:
+ pms_i2c_andor(dev, 0x8a, 0x06, 0x7f, source ? 0x80 : 0);
+ break;
+ case PHILIPS1:
+ break;
+ }
+ mvv_write(dev, 0x2E, 0x31);
+ /* Was: mvv_write(dev, 0x2E, source ? 0x31 : 0x30);
+ But could not make this work correctly. Only Composite input
+ worked for me. */
+}
+
+static void pms_hue(struct pms *dev, short hue)
+{
+ switch (dev->decoder) {
+ case MOTOROLA:
+ pms_i2c_write(dev, 0x8a, 0x00, hue);
+ break;
+ case PHILIPS2:
+ pms_i2c_write(dev, 0x8a, 0x07, hue);
+ break;
+ case PHILIPS1:
+ pms_i2c_write(dev, 0x42, 0x07, hue);
+ break;
+ }
+}
+
+static void pms_saturation(struct pms *dev, short sat)
+{
+ switch (dev->decoder) {
+ case MOTOROLA:
+ pms_i2c_write(dev, 0x8a, 0x00, sat);
+ break;
+ case PHILIPS1:
+ pms_i2c_write(dev, 0x42, 0x12, sat);
+ break;
+ }
+}
+
+
+static void pms_contrast(struct pms *dev, short contrast)
+{
+ switch (dev->decoder) {
+ case MOTOROLA:
+ pms_i2c_write(dev, 0x8a, 0x00, contrast);
+ break;
+ case PHILIPS1:
+ pms_i2c_write(dev, 0x42, 0x13, contrast);
+ break;
+ }
+}
+
+static void pms_brightness(struct pms *dev, short brightness)
+{
+ switch (dev->decoder) {
+ case MOTOROLA:
+ pms_i2c_write(dev, 0x8a, 0x00, brightness);
+ pms_i2c_write(dev, 0x8a, 0x00, brightness);
+ pms_i2c_write(dev, 0x8a, 0x00, brightness);
+ break;
+ case PHILIPS1:
+ pms_i2c_write(dev, 0x42, 0x19, brightness);
+ break;
+ }
+}
+
+
+static void pms_format(struct pms *dev, short format)
+{
+ int target;
+
+ dev->standard = format;
+
+ if (dev->decoder == PHILIPS1)
+ target = 0x42;
+ else if (dev->decoder == PHILIPS2)
+ target = 0x8a;
+ else
+ return;
+
+ switch (format) {
+ case 0: /* Auto */
+ pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
+ pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x80);
+ break;
+ case 1: /* NTSC */
+ pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
+ pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x40);
+ break;
+ case 2: /* PAL */
+ pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
+ pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00);
+ break;
+ case 3: /* SECAM */
+ pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x01);
+ pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00);
+ break;
+ }
+}
+
+#ifdef FOR_FUTURE_EXPANSION
+
+/*
+ * These features of the PMS card are not currently exposes. They
+ * could become a private v4l ioctl for PMSCONFIG or somesuch if
+ * people need it. We also don't yet use the PMS interrupt.
+ */
+
+static void pms_hstart(struct pms *dev, short start)
+{
+ switch (dev->decoder) {
+ case PHILIPS1:
+ pms_i2c_write(dev, 0x8a, 0x05, start);
+ pms_i2c_write(dev, 0x8a, 0x18, start);
+ break;
+ case PHILIPS2:
+ pms_i2c_write(dev, 0x42, 0x05, start);
+ pms_i2c_write(dev, 0x42, 0x18, start);
+ break;
+ }
+}
+
+/*
+ * Bandpass filters
+ */
+
+static void pms_bandpass(struct pms *dev, short pass)
+{
+ if (dev->decoder == PHILIPS2)
+ pms_i2c_andor(dev, 0x8a, 0x06, 0xcf, (pass & 0x03) << 4);
+ else if (dev->decoder == PHILIPS1)
+ pms_i2c_andor(dev, 0x42, 0x06, 0xcf, (pass & 0x03) << 4);
+}
+
+static void pms_antisnow(struct pms *dev, short snow)
+{
+ if (dev->decoder == PHILIPS2)
+ pms_i2c_andor(dev, 0x8a, 0x06, 0xf3, (snow & 0x03) << 2);
+ else if (dev->decoder == PHILIPS1)
+ pms_i2c_andor(dev, 0x42, 0x06, 0xf3, (snow & 0x03) << 2);
+}
+
+static void pms_sharpness(struct pms *dev, short sharp)
+{
+ if (dev->decoder == PHILIPS2)
+ pms_i2c_andor(dev, 0x8a, 0x06, 0xfc, sharp & 0x03);
+ else if (dev->decoder == PHILIPS1)
+ pms_i2c_andor(dev, 0x42, 0x06, 0xfc, sharp & 0x03);
+}
+
+static void pms_chromaagc(struct pms *dev, short agc)
+{
+ if (dev->decoder == PHILIPS2)
+ pms_i2c_andor(dev, 0x8a, 0x0c, 0x9f, (agc & 0x03) << 5);
+ else if (dev->decoder == PHILIPS1)
+ pms_i2c_andor(dev, 0x42, 0x0c, 0x9f, (agc & 0x03) << 5);
+}
+
+static void pms_vertnoise(struct pms *dev, short noise)
+{
+ if (dev->decoder == PHILIPS2)
+ pms_i2c_andor(dev, 0x8a, 0x10, 0xfc, noise & 3);
+ else if (dev->decoder == PHILIPS1)
+ pms_i2c_andor(dev, 0x42, 0x10, 0xfc, noise & 3);
+}
+
+static void pms_forcecolour(struct pms *dev, short colour)
+{
+ if (dev->decoder == PHILIPS2)
+ pms_i2c_andor(dev, 0x8a, 0x0c, 0x7f, (colour & 1) << 7);
+ else if (dev->decoder == PHILIPS1)
+ pms_i2c_andor(dev, 0x42, 0x0c, 0x7, (colour & 1) << 7);
+}
+
+static void pms_antigamma(struct pms *dev, short gamma)
+{
+ if (dev->decoder == PHILIPS2)
+ pms_i2c_andor(dev, 0xb8, 0x00, 0x7f, (gamma & 1) << 7);
+ else if (dev->decoder == PHILIPS1)
+ pms_i2c_andor(dev, 0x42, 0x20, 0x7, (gamma & 1) << 7);
+}
+
+static void pms_prefilter(struct pms *dev, short filter)
+{
+ if (dev->decoder == PHILIPS2)
+ pms_i2c_andor(dev, 0x8a, 0x06, 0xbf, (filter & 1) << 6);
+ else if (dev->decoder == PHILIPS1)
+ pms_i2c_andor(dev, 0x42, 0x06, 0xbf, (filter & 1) << 6);
+}
+
+static void pms_hfilter(struct pms *dev, short filter)
+{
+ if (dev->decoder == PHILIPS2)
+ pms_i2c_andor(dev, 0xb8, 0x04, 0x1f, (filter & 7) << 5);
+ else if (dev->decoder == PHILIPS1)
+ pms_i2c_andor(dev, 0x42, 0x24, 0x1f, (filter & 7) << 5);
+}
+
+static void pms_vfilter(struct pms *dev, short filter)
+{
+ if (dev->decoder == PHILIPS2)
+ pms_i2c_andor(dev, 0xb8, 0x08, 0x9f, (filter & 3) << 5);
+ else if (dev->decoder == PHILIPS1)
+ pms_i2c_andor(dev, 0x42, 0x28, 0x9f, (filter & 3) << 5);
+}
+
+static void pms_killcolour(struct pms *dev, short colour)
+{
+ if (dev->decoder == PHILIPS2) {
+ pms_i2c_andor(dev, 0x8a, 0x08, 0x07, (colour & 0x1f) << 3);
+ pms_i2c_andor(dev, 0x8a, 0x09, 0x07, (colour & 0x1f) << 3);
+ } else if (dev->decoder == PHILIPS1) {
+ pms_i2c_andor(dev, 0x42, 0x08, 0x07, (colour & 0x1f) << 3);
+ pms_i2c_andor(dev, 0x42, 0x09, 0x07, (colour & 0x1f) << 3);
+ }
+}
+
+static void pms_chromagain(struct pms *dev, short chroma)
+{
+ if (dev->decoder == PHILIPS2)
+ pms_i2c_write(dev, 0x8a, 0x11, chroma);
+ else if (dev->decoder == PHILIPS1)
+ pms_i2c_write(dev, 0x42, 0x11, chroma);
+}
+
+
+static void pms_spacialcompl(struct pms *dev, short data)
+{
+ mvv_write(dev, 0x3b, data);
+}
+
+static void pms_spacialcomph(struct pms *dev, short data)
+{
+ mvv_write(dev, 0x3a, data);
+}
+
+static void pms_vstart(struct pms *dev, short start)
+{
+ mvv_write(dev, 0x16, start);
+ mvv_write(dev, 0x17, (start >> 8) & 0x01);
+}
+
+#endif
+
+static void pms_secamcross(struct pms *dev, short cross)
+{
+ if (dev->decoder == PHILIPS2)
+ pms_i2c_andor(dev, 0x8a, 0x0f, 0xdf, (cross & 1) << 5);
+ else if (dev->decoder == PHILIPS1)
+ pms_i2c_andor(dev, 0x42, 0x0f, 0xdf, (cross & 1) << 5);
+}
+
+
+static void pms_swsense(struct pms *dev, short sense)
+{
+ if (dev->decoder == PHILIPS2) {
+ pms_i2c_write(dev, 0x8a, 0x0a, sense);
+ pms_i2c_write(dev, 0x8a, 0x0b, sense);
+ } else if (dev->decoder == PHILIPS1) {
+ pms_i2c_write(dev, 0x42, 0x0a, sense);
+ pms_i2c_write(dev, 0x42, 0x0b, sense);
+ }
+}
+
+
+static void pms_framerate(struct pms *dev, short frr)
+{
+ int fps = (dev->std & V4L2_STD_525_60) ? 30 : 25;
+
+ if (frr == 0)
+ return;
+ fps = fps/frr;
+ mvv_write(dev, 0x14, 0x80 | fps);
+ mvv_write(dev, 0x15, 1);
+}
+
+static void pms_vert(struct pms *dev, u8 deciden, u8 decinum)
+{
+ mvv_write(dev, 0x1c, deciden); /* Denominator */
+ mvv_write(dev, 0x1d, decinum); /* Numerator */
+}
+
+/*
+ * Turn 16bit ratios into best small ratio the chipset can grok
+ */
+
+static void pms_vertdeci(struct pms *dev, unsigned short decinum, unsigned short deciden)
+{
+ /* Knock it down by / 5 once */
+ if (decinum % 5 == 0) {
+ deciden /= 5;
+ decinum /= 5;
+ }
+ /*
+ * 3's
+ */
+ while (decinum % 3 == 0 && deciden % 3 == 0) {
+ deciden /= 3;
+ decinum /= 3;
+ }
+ /*
+ * 2's
+ */
+ while (decinum % 2 == 0 && deciden % 2 == 0) {
+ decinum /= 2;
+ deciden /= 2;
+ }
+ /*
+ * Fudgyify
+ */
+ while (deciden > 32) {
+ deciden /= 2;
+ decinum = (decinum + 1) / 2;
+ }
+ if (deciden == 32)
+ deciden--;
+ pms_vert(dev, deciden, decinum);
+}
+
+static void pms_horzdeci(struct pms *dev, short decinum, short deciden)
+{
+ if (decinum <= 512) {
+ if (decinum % 5 == 0) {
+ decinum /= 5;
+ deciden /= 5;
+ }
+ } else {
+ decinum = 512;
+ deciden = 640; /* 768 would be ideal */
+ }
+
+ while (((decinum | deciden) & 1) == 0) {
+ decinum >>= 1;
+ deciden >>= 1;
+ }
+ while (deciden > 32) {
+ deciden >>= 1;
+ decinum = (decinum + 1) >> 1;
+ }
+ if (deciden == 32)
+ deciden--;
+
+ mvv_write(dev, 0x24, 0x80 | deciden);
+ mvv_write(dev, 0x25, decinum);
+}
+
+static void pms_resolution(struct pms *dev, short width, short height)
+{
+ int fg_height;
+
+ fg_height = height;
+ if (fg_height > 280)
+ fg_height = 280;
+
+ mvv_write(dev, 0x18, fg_height);
+ mvv_write(dev, 0x19, fg_height >> 8);
+
+ if (dev->std & V4L2_STD_525_60) {
+ mvv_write(dev, 0x1a, 0xfc);
+ mvv_write(dev, 0x1b, 0x00);
+ if (height > fg_height)
+ pms_vertdeci(dev, 240, 240);
+ else
+ pms_vertdeci(dev, fg_height, 240);
+ } else {
+ mvv_write(dev, 0x1a, 0x1a);
+ mvv_write(dev, 0x1b, 0x01);
+ if (fg_height > 256)
+ pms_vertdeci(dev, 270, 270);
+ else
+ pms_vertdeci(dev, fg_height, 270);
+ }
+ mvv_write(dev, 0x12, 0);
+ mvv_write(dev, 0x13, MVVMEMORYWIDTH);
+ mvv_write(dev, 0x42, 0x00);
+ mvv_write(dev, 0x43, 0x00);
+ mvv_write(dev, 0x44, MVVMEMORYWIDTH);
+
+ mvv_write(dev, 0x22, width + 8);
+ mvv_write(dev, 0x23, (width + 8) >> 8);
+
+ if (dev->std & V4L2_STD_525_60)
+ pms_horzdeci(dev, width, 640);
+ else
+ pms_horzdeci(dev, width + 8, 768);
+
+ mvv_write(dev, 0x30, mvv_read(dev, 0x30) & 0xfe);
+ mvv_write(dev, 0x08, mvv_read(dev, 0x08) | 0x01);
+ mvv_write(dev, 0x01, mvv_read(dev, 0x01) & 0xfd);
+ mvv_write(dev, 0x32, 0x00);
+ mvv_write(dev, 0x33, MVVMEMORYWIDTH);
+}
+
+
+/*
+ * Set Input
+ */
+
+static void pms_vcrinput(struct pms *dev, short input)
+{
+ if (dev->decoder == PHILIPS2)
+ pms_i2c_andor(dev, 0x8a, 0x0d, 0x7f, (input & 1) << 7);
+ else if (dev->decoder == PHILIPS1)
+ pms_i2c_andor(dev, 0x42, 0x0d, 0x7f, (input & 1) << 7);
+}
+
+
+static int pms_capture(struct pms *dev, char __user *buf, int rgb555, int count)
+{
+ int y;
+ int dw = 2 * dev->width;
+ char tmp[dw + 32]; /* using a temp buffer is faster than direct */
+ int cnt = 0;
+ int len = 0;
+ unsigned char r8 = 0x5; /* value for reg8 */
+
+ if (rgb555)
+ r8 |= 0x20; /* else use untranslated rgb = 565 */
+ mvv_write(dev, 0x08, r8); /* capture rgb555/565, init DRAM, PC enable */
+
+/* printf("%d %d %d %d %d %x %x\n",width,height,voff,nom,den,mvv_buf); */
+
+ for (y = 0; y < dev->height; y++) {
+ writeb(0, dev->mem); /* synchronisiert neue Zeile */
+
+ /*
+ * This is in truth a fifo, be very careful as if you
+ * forgot this odd things will occur 8)
+ */
+
+ memcpy_fromio(tmp, dev->mem, dw + 32); /* discard 16 word */
+ cnt -= dev->height;
+ while (cnt <= 0) {
+ /*
+ * Don't copy too far
+ */
+ int dt = dw;
+ if (dt + len > count)
+ dt = count - len;
+ cnt += dev->height;
+ if (copy_to_user(buf, tmp + 32, dt))
+ return len ? len : -EFAULT;
+ buf += dt;
+ len += dt;
+ }
+ }
+ return len;
+}
+
+
+/*
+ * Video4linux interfacing
+ */
+
+static int pms_querycap(struct file *file, void *priv,
+ struct v4l2_capability *vcap)
+{
+ struct pms *dev = video_drvdata(file);
+
+ strlcpy(vcap->driver, dev->v4l2_dev.name, sizeof(vcap->driver));
+ strlcpy(vcap->card, "Mediavision PMS", sizeof(vcap->card));
+ strlcpy(vcap->bus_info, "ISA", sizeof(vcap->bus_info));
+ vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
+ return 0;
+}
+
+static int pms_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
+{
+ static const char *inputs[4] = {
+ "Composite",
+ "S-Video",
+ "Composite (VCR)",
+ "S-Video (VCR)"
+ };
+
+ if (vin->index > 3)
+ return -EINVAL;
+ strlcpy(vin->name, inputs[vin->index], sizeof(vin->name));
+ vin->type = V4L2_INPUT_TYPE_CAMERA;
+ vin->audioset = 0;
+ vin->tuner = 0;
+ vin->std = V4L2_STD_ALL;
+ vin->status = 0;
+ return 0;
+}
+
+static int pms_g_input(struct file *file, void *fh, unsigned int *inp)
+{
+ struct pms *dev = video_drvdata(file);
+
+ *inp = dev->input;
+ return 0;
+}
+
+static int pms_s_input(struct file *file, void *fh, unsigned int inp)
+{
+ struct pms *dev = video_drvdata(file);
+
+ if (inp > 3)
+ return -EINVAL;
+
+ mutex_lock(&dev->lock);
+ dev->input = inp;
+ pms_videosource(dev, inp & 1);
+ pms_vcrinput(dev, inp >> 1);
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int pms_g_std(struct file *file, void *fh, v4l2_std_id *std)
+{
+ struct pms *dev = video_drvdata(file);
+
+ *std = dev->std;
+ return 0;
+}
+
+static int pms_s_std(struct file *file, void *fh, v4l2_std_id *std)
+{
+ struct pms *dev = video_drvdata(file);
+ int ret = 0;
+
+ dev->std = *std;
+ mutex_lock(&dev->lock);
+ if (dev->std & V4L2_STD_NTSC) {
+ pms_framerate(dev, 30);
+ pms_secamcross(dev, 0);
+ pms_format(dev, 1);
+ } else if (dev->std & V4L2_STD_PAL) {
+ pms_framerate(dev, 25);
+ pms_secamcross(dev, 0);
+ pms_format(dev, 2);
+ } else if (dev->std & V4L2_STD_SECAM) {
+ pms_framerate(dev, 25);
+ pms_secamcross(dev, 1);
+ pms_format(dev, 2);
+ } else {
+ ret = -EINVAL;
+ }
+ /*
+ switch (v->mode) {
+ case VIDEO_MODE_AUTO:
+ pms_framerate(dev, 25);
+ pms_secamcross(dev, 0);
+ pms_format(dev, 0);
+ break;
+ }*/
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int pms_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 139);
+ case V4L2_CID_CONTRAST:
+ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 70);
+ case V4L2_CID_SATURATION:
+ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 64);
+ case V4L2_CID_HUE:
+ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int pms_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct pms *dev = video_drvdata(file);
+ int ret = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = dev->brightness;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->value = dev->contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ ctrl->value = dev->saturation;
+ break;
+ case V4L2_CID_HUE:
+ ctrl->value = dev->hue;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int pms_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct pms *dev = video_drvdata(file);
+ int ret = 0;
+
+ mutex_lock(&dev->lock);
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ dev->brightness = ctrl->value;
+ pms_brightness(dev, dev->brightness);
+ break;
+ case V4L2_CID_CONTRAST:
+ dev->contrast = ctrl->value;
+ pms_contrast(dev, dev->contrast);
+ break;
+ case V4L2_CID_SATURATION:
+ dev->saturation = ctrl->value;
+ pms_saturation(dev, dev->saturation);
+ break;
+ case V4L2_CID_HUE:
+ dev->hue = ctrl->value;
+ pms_hue(dev, dev->hue);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ mutex_unlock(&dev->lock);
+ return ret;
+}
+
+static int pms_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct pms *dev = video_drvdata(file);
+ struct v4l2_pix_format *pix = &fmt->fmt.pix;
+
+ pix->width = dev->width;
+ pix->height = dev->height;
+ pix->pixelformat = dev->width == 15 ?
+ V4L2_PIX_FMT_RGB555 : V4L2_PIX_FMT_RGB565;
+ pix->field = V4L2_FIELD_NONE;
+ pix->bytesperline = 2 * dev->width;
+ pix->sizeimage = 2 * dev->width * dev->height;
+ /* Just a guess */
+ pix->colorspace = V4L2_COLORSPACE_SRGB;
+ return 0;
+}
+
+static int pms_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct v4l2_pix_format *pix = &fmt->fmt.pix;
+
+ if (pix->height < 16 || pix->height > 480)
+ return -EINVAL;
+ if (pix->width < 16 || pix->width > 640)
+ return -EINVAL;
+ if (pix->pixelformat != V4L2_PIX_FMT_RGB555 &&
+ pix->pixelformat != V4L2_PIX_FMT_RGB565)
+ return -EINVAL;
+ pix->field = V4L2_FIELD_NONE;
+ pix->bytesperline = 2 * pix->width;
+ pix->sizeimage = 2 * pix->width * pix->height;
+ /* Just a guess */
+ pix->colorspace = V4L2_COLORSPACE_SRGB;
+ return 0;
+}
+
+static int pms_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct pms *dev = video_drvdata(file);
+ struct v4l2_pix_format *pix = &fmt->fmt.pix;
+ int ret = pms_try_fmt_vid_cap(file, fh, fmt);
+
+ if (ret)
+ return ret;
+ mutex_lock(&dev->lock);
+ dev->width = pix->width;
+ dev->height = pix->height;
+ dev->depth = (pix->pixelformat == V4L2_PIX_FMT_RGB555) ? 15 : 16;
+ pms_resolution(dev, dev->width, dev->height);
+ /* Ok we figured out what to use from our wide choice */
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int pms_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
+{
+ static struct v4l2_fmtdesc formats[] = {
+ { 0, 0, 0,
+ "RGB 5:5:5", V4L2_PIX_FMT_RGB555,
+ { 0, 0, 0, 0 }
+ },
+ { 0, 0, 0,
+ "RGB 5:6:5", V4L2_PIX_FMT_RGB565,
+ { 0, 0, 0, 0 }
+ },
+ };
+ enum v4l2_buf_type type = fmt->type;
+
+ if (fmt->index > 1)
+ return -EINVAL;
+
+ *fmt = formats[fmt->index];
+ fmt->type = type;
+ return 0;
+}
+
+static ssize_t pms_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct pms *dev = video_drvdata(file);
+ int len;
+
+ mutex_lock(&dev->lock);
+ len = pms_capture(dev, buf, (dev->depth == 15), count);
+ mutex_unlock(&dev->lock);
+ return len;
+}
+
+static const struct v4l2_file_operations pms_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = video_ioctl2,
+ .read = pms_read,
+};
+
+static const struct v4l2_ioctl_ops pms_ioctl_ops = {
+ .vidioc_querycap = pms_querycap,
+ .vidioc_g_input = pms_g_input,
+ .vidioc_s_input = pms_s_input,
+ .vidioc_enum_input = pms_enum_input,
+ .vidioc_g_std = pms_g_std,
+ .vidioc_s_std = pms_s_std,
+ .vidioc_queryctrl = pms_queryctrl,
+ .vidioc_g_ctrl = pms_g_ctrl,
+ .vidioc_s_ctrl = pms_s_ctrl,
+ .vidioc_enum_fmt_vid_cap = pms_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = pms_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = pms_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = pms_try_fmt_vid_cap,
+};
+
+/*
+ * Probe for and initialise the Mediavision PMS
+ */
+
+static int init_mediavision(struct pms *dev)
+{
+ int id;
+ int idec, decst;
+ int i;
+ static const unsigned char i2c_defs[] = {
+ 0x4c, 0x30, 0x00, 0xe8,
+ 0xb6, 0xe2, 0x00, 0x00,
+ 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0x98,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x34, 0x0a, 0xf4, 0xce,
+ 0xe4
+ };
+
+ dev->mem = ioremap(mem_base, 0x800);
+ if (!dev->mem)
+ return -ENOMEM;
+
+ if (!request_region(0x9a01, 1, "Mediavision PMS config")) {
+ printk(KERN_WARNING "mediavision: unable to detect: 0x9a01 in use.\n");
+ iounmap(dev->mem);
+ return -EBUSY;
+ }
+ if (!request_region(dev->io, 3, "Mediavision PMS")) {
+ printk(KERN_WARNING "mediavision: I/O port %d in use.\n", dev->io);
+ release_region(0x9a01, 1);
+ iounmap(dev->mem);
+ return -EBUSY;
+ }
+ outb(0xb8, 0x9a01); /* Unlock */
+ outb(dev->io >> 4, 0x9a01); /* Set IO port */
+
+
+ id = mvv_read(dev, 3);
+ decst = pms_i2c_stat(dev, 0x43);
+
+ if (decst != -1)
+ idec = 2;
+ else if (pms_i2c_stat(dev, 0xb9) != -1)
+ idec = 3;
+ else if (pms_i2c_stat(dev, 0x8b) != -1)
+ idec = 1;
+ else
+ idec = 0;
+
+ printk(KERN_INFO "PMS type is %d\n", idec);
+ if (idec == 0) {
+ release_region(dev->io, 3);
+ release_region(0x9a01, 1);
+ iounmap(dev->mem);
+ return -ENODEV;
+ }
+
+ /*
+ * Ok we have a PMS of some sort
+ */
+
+ mvv_write(dev, 0x04, mem_base >> 12); /* Set the memory area */
+
+ /* Ok now load the defaults */
+
+ for (i = 0; i < 0x19; i++) {
+ if (i2c_defs[i] == 0xff)
+ pms_i2c_andor(dev, 0x8a, i, 0x07, 0x00);
+ else
+ pms_i2c_write(dev, 0x8a, i, i2c_defs[i]);
+ }
+
+ pms_i2c_write(dev, 0xb8, 0x00, 0x12);
+ pms_i2c_write(dev, 0xb8, 0x04, 0x00);
+ pms_i2c_write(dev, 0xb8, 0x07, 0x00);
+ pms_i2c_write(dev, 0xb8, 0x08, 0x00);
+ pms_i2c_write(dev, 0xb8, 0x09, 0xff);
+ pms_i2c_write(dev, 0xb8, 0x0a, 0x00);
+ pms_i2c_write(dev, 0xb8, 0x0b, 0x10);
+ pms_i2c_write(dev, 0xb8, 0x10, 0x03);
+
+ mvv_write(dev, 0x01, 0x00);
+ mvv_write(dev, 0x05, 0xa0);
+ mvv_write(dev, 0x08, 0x25);
+ mvv_write(dev, 0x09, 0x00);
+ mvv_write(dev, 0x0a, 0x20 | MVVMEMORYWIDTH);
+
+ mvv_write(dev, 0x10, 0x02);
+ mvv_write(dev, 0x1e, 0x0c);
+ mvv_write(dev, 0x1f, 0x03);
+ mvv_write(dev, 0x26, 0x06);
+
+ mvv_write(dev, 0x2b, 0x00);
+ mvv_write(dev, 0x2c, 0x20);
+ mvv_write(dev, 0x2d, 0x00);
+ mvv_write(dev, 0x2f, 0x70);
+ mvv_write(dev, 0x32, 0x00);
+ mvv_write(dev, 0x33, MVVMEMORYWIDTH);
+ mvv_write(dev, 0x34, 0x00);
+ mvv_write(dev, 0x35, 0x00);
+ mvv_write(dev, 0x3a, 0x80);
+ mvv_write(dev, 0x3b, 0x10);
+ mvv_write(dev, 0x20, 0x00);
+ mvv_write(dev, 0x21, 0x00);
+ mvv_write(dev, 0x30, 0x22);
+ return 0;
+}
+
+/*
+ * Initialization and module stuff
+ */
+
+#ifndef MODULE
+static int enable;
+module_param(enable, int, 0);
+#endif
+
+static int __init pms_init(void)
+{
+ struct pms *dev = &pms_card;
+ struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
+ int res;
+
+ strlcpy(v4l2_dev->name, "pms", sizeof(v4l2_dev->name));
+
+ v4l2_info(v4l2_dev, "Mediavision Pro Movie Studio driver 0.03\n");
+
+#ifndef MODULE
+ if (!enable) {
+ v4l2_err(v4l2_dev,
+ "PMS: not enabled, use pms.enable=1 to probe\n");
+ return -ENODEV;
+ }
+#endif
+
+ dev->decoder = PHILIPS2;
+ dev->io = io_port;
+ dev->data = io_port + 1;
+
+ if (init_mediavision(dev)) {
+ v4l2_err(v4l2_dev, "Board not found.\n");
+ return -ENODEV;
+ }
+
+ res = v4l2_device_register(NULL, v4l2_dev);
+ if (res < 0) {
+ v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
+ return res;
+ }
+
+ strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
+ dev->vdev.v4l2_dev = v4l2_dev;
+ dev->vdev.fops = &pms_fops;
+ dev->vdev.ioctl_ops = &pms_ioctl_ops;
+ dev->vdev.release = video_device_release_empty;
+ video_set_drvdata(&dev->vdev, dev);
+ mutex_init(&dev->lock);
+ dev->std = V4L2_STD_NTSC_M;
+ dev->height = 240;
+ dev->width = 320;
+ dev->depth = 15;
+ dev->brightness = 139;
+ dev->contrast = 70;
+ dev->hue = 0;
+ dev->saturation = 64;
+ pms_swsense(dev, 75);
+ pms_resolution(dev, 320, 240);
+ pms_videosource(dev, 0);
+ pms_vcrinput(dev, 0);
+ if (video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
+ v4l2_device_unregister(&dev->v4l2_dev);
+ release_region(dev->io, 3);
+ release_region(0x9a01, 1);
+ iounmap(dev->mem);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void __exit pms_exit(void)
+{
+ struct pms *dev = &pms_card;
+
+ video_unregister_device(&dev->vdev);
+ release_region(dev->io, 3);
+ release_region(0x9a01, 1);
+ iounmap(dev->mem);
+}
+
+module_init(pms_init);
+module_exit(pms_exit);
diff --git a/drivers/media/video/pvrusb2/Kconfig b/drivers/media/video/pvrusb2/Kconfig
new file mode 100644
index 00000000..f9b6001e
--- /dev/null
+++ b/drivers/media/video/pvrusb2/Kconfig
@@ -0,0 +1,66 @@
+config VIDEO_PVRUSB2
+ tristate "Hauppauge WinTV-PVR USB2 support"
+ depends on VIDEO_V4L2 && I2C
+ depends on VIDEO_MEDIA # Avoids pvrusb = Y / DVB = M
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ select VIDEO_CX2341X
+ select VIDEO_SAA711X
+ select VIDEO_CX25840
+ select VIDEO_MSP3400
+ select VIDEO_WM8775
+ select VIDEO_CS53L32A
+ ---help---
+ This is a video4linux driver for Conexant 23416 based
+ usb2 personal video recorder devices.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pvrusb2
+
+config VIDEO_PVRUSB2_SYSFS
+ bool "pvrusb2 sysfs support (EXPERIMENTAL)"
+ default y
+ depends on VIDEO_PVRUSB2 && SYSFS && EXPERIMENTAL
+ ---help---
+ This option enables the operation of a sysfs based
+ interface for query and control of the pvrusb2 driver.
+
+ This is not generally needed for v4l applications,
+ although certain applications are optimized to take
+ advantage of this feature.
+
+ If you are in doubt, say Y.
+
+ Note: This feature is experimental and subject to change.
+
+config VIDEO_PVRUSB2_DVB
+ bool "pvrusb2 ATSC/DVB support (EXPERIMENTAL)"
+ default y
+ depends on VIDEO_PVRUSB2 && DVB_CORE && EXPERIMENTAL
+ select DVB_LGDT330X if !DVB_FE_CUSTOMISE
+ select DVB_S5H1409 if !DVB_FE_CUSTOMISE
+ select DVB_S5H1411 if !DVB_FE_CUSTOMISE
+ select DVB_TDA10048 if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE
+ select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE
+ select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE
+ ---help---
+
+ This option enables a DVB interface for the pvrusb2 driver.
+ If your device does not support digital television, this
+ feature will have no affect on the driver's operation.
+
+ If you are in doubt, say Y.
+
+config VIDEO_PVRUSB2_DEBUGIFC
+ bool "pvrusb2 debug interface"
+ depends on VIDEO_PVRUSB2_SYSFS
+ ---help---
+ This option enables the inclusion of a debug interface
+ in the pvrusb2 driver, hosted through sysfs.
+
+ You do not need to select this option unless you plan
+ on debugging the driver or performing a manual firmware
+ extraction.
+
+ If you are in doubt, say N.
diff --git a/drivers/media/video/pvrusb2/Makefile b/drivers/media/video/pvrusb2/Makefile
new file mode 100644
index 00000000..c17f37d9
--- /dev/null
+++ b/drivers/media/video/pvrusb2/Makefile
@@ -0,0 +1,22 @@
+obj-pvrusb2-sysfs-$(CONFIG_VIDEO_PVRUSB2_SYSFS) := pvrusb2-sysfs.o
+obj-pvrusb2-debugifc-$(CONFIG_VIDEO_PVRUSB2_DEBUGIFC) := pvrusb2-debugifc.o
+obj-pvrusb2-dvb-$(CONFIG_VIDEO_PVRUSB2_DVB) := pvrusb2-dvb.o
+
+pvrusb2-objs := pvrusb2-i2c-core.o \
+ pvrusb2-audio.o \
+ pvrusb2-encoder.o pvrusb2-video-v4l.o \
+ pvrusb2-eeprom.o \
+ pvrusb2-main.o pvrusb2-hdw.o pvrusb2-v4l2.o \
+ pvrusb2-ctrl.o pvrusb2-std.o pvrusb2-devattr.o \
+ pvrusb2-context.o pvrusb2-io.o pvrusb2-ioread.o \
+ pvrusb2-cx2584x-v4l.o pvrusb2-wm8775.o \
+ pvrusb2-cs53l32a.o \
+ $(obj-pvrusb2-dvb-y) \
+ $(obj-pvrusb2-sysfs-y) $(obj-pvrusb2-debugifc-y)
+
+obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o
+
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/pvrusb2/pvrusb2-audio.c b/drivers/media/video/pvrusb2/pvrusb2-audio.c
new file mode 100644
index 00000000..cc06d5e4
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-audio.c
@@ -0,0 +1,96 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "pvrusb2-audio.h"
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-debug.h"
+#include <linux/videodev2.h>
+#include <media/msp3400.h>
+#include <media/v4l2-common.h>
+
+
+struct routing_scheme {
+ const int *def;
+ unsigned int cnt;
+};
+
+static const int routing_scheme0[] = {
+ [PVR2_CVAL_INPUT_TV] = MSP_INPUT_DEFAULT,
+ [PVR2_CVAL_INPUT_RADIO] = MSP_INPUT(MSP_IN_SCART2,
+ MSP_IN_TUNER1,
+ MSP_DSP_IN_SCART,
+ MSP_DSP_IN_SCART),
+ [PVR2_CVAL_INPUT_COMPOSITE] = MSP_INPUT(MSP_IN_SCART1,
+ MSP_IN_TUNER1,
+ MSP_DSP_IN_SCART,
+ MSP_DSP_IN_SCART),
+ [PVR2_CVAL_INPUT_SVIDEO] = MSP_INPUT(MSP_IN_SCART1,
+ MSP_IN_TUNER1,
+ MSP_DSP_IN_SCART,
+ MSP_DSP_IN_SCART),
+};
+
+static const struct routing_scheme routing_def0 = {
+ .def = routing_scheme0,
+ .cnt = ARRAY_SIZE(routing_scheme0),
+};
+
+static const struct routing_scheme *routing_schemes[] = {
+ [PVR2_ROUTING_SCHEME_HAUPPAUGE] = &routing_def0,
+};
+
+void pvr2_msp3400_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd)
+{
+ if (hdw->input_dirty || hdw->force_dirty) {
+ const struct routing_scheme *sp;
+ unsigned int sid = hdw->hdw_desc->signal_routing_scheme;
+ u32 input;
+
+ pvr2_trace(PVR2_TRACE_CHIPS, "subdev msp3400 v4l2 set_stereo");
+ sp = (sid < ARRAY_SIZE(routing_schemes)) ?
+ routing_schemes[sid] : NULL;
+
+ if ((sp != NULL) &&
+ (hdw->input_val >= 0) &&
+ (hdw->input_val < sp->cnt)) {
+ input = sp->def[hdw->input_val];
+ } else {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "*** WARNING *** subdev msp3400 set_input:"
+ " Invalid routing scheme (%u)"
+ " and/or input (%d)",
+ sid, hdw->input_val);
+ return;
+ }
+ sd->ops->audio->s_routing(sd, input,
+ MSP_OUTPUT(MSP_SC_IN_DSP_SCART1), 0);
+ }
+}
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-audio.h b/drivers/media/video/pvrusb2/pvrusb2-audio.h
new file mode 100644
index 00000000..e3e63d75
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-audio.h
@@ -0,0 +1,37 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __PVRUSB2_AUDIO_H
+#define __PVRUSB2_AUDIO_H
+
+#include "pvrusb2-hdw-internal.h"
+void pvr2_msp3400_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *);
+#endif /* __PVRUSB2_AUDIO_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-context.c b/drivers/media/video/pvrusb2/pvrusb2-context.c
new file mode 100644
index 00000000..7c19ff72
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-context.c
@@ -0,0 +1,431 @@
+/*
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "pvrusb2-context.h"
+#include "pvrusb2-io.h"
+#include "pvrusb2-ioread.h"
+#include "pvrusb2-hdw.h"
+#include "pvrusb2-debug.h"
+#include <linux/wait.h>
+#include <linux/kthread.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+
+static struct pvr2_context *pvr2_context_exist_first;
+static struct pvr2_context *pvr2_context_exist_last;
+static struct pvr2_context *pvr2_context_notify_first;
+static struct pvr2_context *pvr2_context_notify_last;
+static DEFINE_MUTEX(pvr2_context_mutex);
+static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_sync_data);
+static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_cleanup_data);
+static int pvr2_context_cleanup_flag;
+static int pvr2_context_cleaned_flag;
+static struct task_struct *pvr2_context_thread_ptr;
+
+
+static void pvr2_context_set_notify(struct pvr2_context *mp, int fl)
+{
+ int signal_flag = 0;
+ mutex_lock(&pvr2_context_mutex);
+ if (fl) {
+ if (!mp->notify_flag) {
+ signal_flag = (pvr2_context_notify_first == NULL);
+ mp->notify_prev = pvr2_context_notify_last;
+ mp->notify_next = NULL;
+ pvr2_context_notify_last = mp;
+ if (mp->notify_prev) {
+ mp->notify_prev->notify_next = mp;
+ } else {
+ pvr2_context_notify_first = mp;
+ }
+ mp->notify_flag = !0;
+ }
+ } else {
+ if (mp->notify_flag) {
+ mp->notify_flag = 0;
+ if (mp->notify_next) {
+ mp->notify_next->notify_prev = mp->notify_prev;
+ } else {
+ pvr2_context_notify_last = mp->notify_prev;
+ }
+ if (mp->notify_prev) {
+ mp->notify_prev->notify_next = mp->notify_next;
+ } else {
+ pvr2_context_notify_first = mp->notify_next;
+ }
+ }
+ }
+ mutex_unlock(&pvr2_context_mutex);
+ if (signal_flag) wake_up(&pvr2_context_sync_data);
+}
+
+
+static void pvr2_context_destroy(struct pvr2_context *mp)
+{
+ pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (destroy)",mp);
+ if (mp->hdw) pvr2_hdw_destroy(mp->hdw);
+ pvr2_context_set_notify(mp, 0);
+ mutex_lock(&pvr2_context_mutex);
+ if (mp->exist_next) {
+ mp->exist_next->exist_prev = mp->exist_prev;
+ } else {
+ pvr2_context_exist_last = mp->exist_prev;
+ }
+ if (mp->exist_prev) {
+ mp->exist_prev->exist_next = mp->exist_next;
+ } else {
+ pvr2_context_exist_first = mp->exist_next;
+ }
+ if (!pvr2_context_exist_first) {
+ /* Trigger wakeup on control thread in case it is waiting
+ for an exit condition. */
+ wake_up(&pvr2_context_sync_data);
+ }
+ mutex_unlock(&pvr2_context_mutex);
+ kfree(mp);
+}
+
+
+static void pvr2_context_notify(struct pvr2_context *mp)
+{
+ pvr2_context_set_notify(mp,!0);
+}
+
+
+static void pvr2_context_check(struct pvr2_context *mp)
+{
+ struct pvr2_channel *ch1, *ch2;
+ pvr2_trace(PVR2_TRACE_CTXT,
+ "pvr2_context %p (notify)", mp);
+ if (!mp->initialized_flag && !mp->disconnect_flag) {
+ mp->initialized_flag = !0;
+ pvr2_trace(PVR2_TRACE_CTXT,
+ "pvr2_context %p (initialize)", mp);
+ /* Finish hardware initialization */
+ if (pvr2_hdw_initialize(mp->hdw,
+ (void (*)(void *))pvr2_context_notify,
+ mp)) {
+ mp->video_stream.stream =
+ pvr2_hdw_get_video_stream(mp->hdw);
+ /* Trigger interface initialization. By doing this
+ here initialization runs in our own safe and
+ cozy thread context. */
+ if (mp->setup_func) mp->setup_func(mp);
+ } else {
+ pvr2_trace(PVR2_TRACE_CTXT,
+ "pvr2_context %p (thread skipping setup)",
+ mp);
+ /* Even though initialization did not succeed,
+ we're still going to continue anyway. We need
+ to do this in order to await the expected
+ disconnect (which we will detect in the normal
+ course of operation). */
+ }
+ }
+
+ for (ch1 = mp->mc_first; ch1; ch1 = ch2) {
+ ch2 = ch1->mc_next;
+ if (ch1->check_func) ch1->check_func(ch1);
+ }
+
+ if (mp->disconnect_flag && !mp->mc_first) {
+ /* Go away... */
+ pvr2_context_destroy(mp);
+ return;
+ }
+}
+
+
+static int pvr2_context_shutok(void)
+{
+ return pvr2_context_cleanup_flag && (pvr2_context_exist_first == NULL);
+}
+
+
+static int pvr2_context_thread_func(void *foo)
+{
+ struct pvr2_context *mp;
+
+ pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread start");
+
+ do {
+ while ((mp = pvr2_context_notify_first) != NULL) {
+ pvr2_context_set_notify(mp, 0);
+ pvr2_context_check(mp);
+ }
+ wait_event_interruptible(
+ pvr2_context_sync_data,
+ ((pvr2_context_notify_first != NULL) ||
+ pvr2_context_shutok()));
+ } while (!pvr2_context_shutok());
+
+ pvr2_context_cleaned_flag = !0;
+ wake_up(&pvr2_context_cleanup_data);
+
+ pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread cleaned up");
+
+ wait_event_interruptible(
+ pvr2_context_sync_data,
+ kthread_should_stop());
+
+ pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread end");
+
+ return 0;
+}
+
+
+int pvr2_context_global_init(void)
+{
+ pvr2_context_thread_ptr = kthread_run(pvr2_context_thread_func,
+ NULL,
+ "pvrusb2-context");
+ return (pvr2_context_thread_ptr ? 0 : -ENOMEM);
+}
+
+
+void pvr2_context_global_done(void)
+{
+ pvr2_context_cleanup_flag = !0;
+ wake_up(&pvr2_context_sync_data);
+ wait_event_interruptible(
+ pvr2_context_cleanup_data,
+ pvr2_context_cleaned_flag);
+ kthread_stop(pvr2_context_thread_ptr);
+}
+
+
+struct pvr2_context *pvr2_context_create(
+ struct usb_interface *intf,
+ const struct usb_device_id *devid,
+ void (*setup_func)(struct pvr2_context *))
+{
+ struct pvr2_context *mp = NULL;
+ mp = kzalloc(sizeof(*mp),GFP_KERNEL);
+ if (!mp) goto done;
+ pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (create)",mp);
+ mp->setup_func = setup_func;
+ mutex_init(&mp->mutex);
+ mutex_lock(&pvr2_context_mutex);
+ mp->exist_prev = pvr2_context_exist_last;
+ mp->exist_next = NULL;
+ pvr2_context_exist_last = mp;
+ if (mp->exist_prev) {
+ mp->exist_prev->exist_next = mp;
+ } else {
+ pvr2_context_exist_first = mp;
+ }
+ mutex_unlock(&pvr2_context_mutex);
+ mp->hdw = pvr2_hdw_create(intf,devid);
+ if (!mp->hdw) {
+ pvr2_context_destroy(mp);
+ mp = NULL;
+ goto done;
+ }
+ pvr2_context_set_notify(mp, !0);
+ done:
+ return mp;
+}
+
+
+static void pvr2_context_reset_input_limits(struct pvr2_context *mp)
+{
+ unsigned int tmsk,mmsk;
+ struct pvr2_channel *cp;
+ struct pvr2_hdw *hdw = mp->hdw;
+ mmsk = pvr2_hdw_get_input_available(hdw);
+ tmsk = mmsk;
+ for (cp = mp->mc_first; cp; cp = cp->mc_next) {
+ if (!cp->input_mask) continue;
+ tmsk &= cp->input_mask;
+ }
+ pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk);
+ pvr2_hdw_commit_ctl(hdw);
+}
+
+
+static void pvr2_context_enter(struct pvr2_context *mp)
+{
+ mutex_lock(&mp->mutex);
+}
+
+
+static void pvr2_context_exit(struct pvr2_context *mp)
+{
+ int destroy_flag = 0;
+ if (!(mp->mc_first || !mp->disconnect_flag)) {
+ destroy_flag = !0;
+ }
+ mutex_unlock(&mp->mutex);
+ if (destroy_flag) pvr2_context_notify(mp);
+}
+
+
+void pvr2_context_disconnect(struct pvr2_context *mp)
+{
+ pvr2_hdw_disconnect(mp->hdw);
+ mp->disconnect_flag = !0;
+ pvr2_context_notify(mp);
+}
+
+
+void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp)
+{
+ pvr2_context_enter(mp);
+ cp->hdw = mp->hdw;
+ cp->mc_head = mp;
+ cp->mc_next = NULL;
+ cp->mc_prev = mp->mc_last;
+ if (mp->mc_last) {
+ mp->mc_last->mc_next = cp;
+ } else {
+ mp->mc_first = cp;
+ }
+ mp->mc_last = cp;
+ pvr2_context_exit(mp);
+}
+
+
+static void pvr2_channel_disclaim_stream(struct pvr2_channel *cp)
+{
+ if (!cp->stream) return;
+ pvr2_stream_kill(cp->stream->stream);
+ cp->stream->user = NULL;
+ cp->stream = NULL;
+}
+
+
+void pvr2_channel_done(struct pvr2_channel *cp)
+{
+ struct pvr2_context *mp = cp->mc_head;
+ pvr2_context_enter(mp);
+ cp->input_mask = 0;
+ pvr2_channel_disclaim_stream(cp);
+ pvr2_context_reset_input_limits(mp);
+ if (cp->mc_next) {
+ cp->mc_next->mc_prev = cp->mc_prev;
+ } else {
+ mp->mc_last = cp->mc_prev;
+ }
+ if (cp->mc_prev) {
+ cp->mc_prev->mc_next = cp->mc_next;
+ } else {
+ mp->mc_first = cp->mc_next;
+ }
+ cp->hdw = NULL;
+ pvr2_context_exit(mp);
+}
+
+
+int pvr2_channel_limit_inputs(struct pvr2_channel *cp,unsigned int cmsk)
+{
+ unsigned int tmsk,mmsk;
+ int ret = 0;
+ struct pvr2_channel *p2;
+ struct pvr2_hdw *hdw = cp->hdw;
+
+ mmsk = pvr2_hdw_get_input_available(hdw);
+ cmsk &= mmsk;
+ if (cmsk == cp->input_mask) {
+ /* No change; nothing to do */
+ return 0;
+ }
+
+ pvr2_context_enter(cp->mc_head);
+ do {
+ if (!cmsk) {
+ cp->input_mask = 0;
+ pvr2_context_reset_input_limits(cp->mc_head);
+ break;
+ }
+ tmsk = mmsk;
+ for (p2 = cp->mc_head->mc_first; p2; p2 = p2->mc_next) {
+ if (p2 == cp) continue;
+ if (!p2->input_mask) continue;
+ tmsk &= p2->input_mask;
+ }
+ if (!(tmsk & cmsk)) {
+ ret = -EPERM;
+ break;
+ }
+ tmsk &= cmsk;
+ if ((ret = pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk)) != 0) {
+ /* Internal failure changing allowed list; probably
+ should not happen, but react if it does. */
+ break;
+ }
+ cp->input_mask = cmsk;
+ pvr2_hdw_commit_ctl(hdw);
+ } while (0);
+ pvr2_context_exit(cp->mc_head);
+ return ret;
+}
+
+
+unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *cp)
+{
+ return cp->input_mask;
+}
+
+
+int pvr2_channel_claim_stream(struct pvr2_channel *cp,
+ struct pvr2_context_stream *sp)
+{
+ int code = 0;
+ pvr2_context_enter(cp->mc_head); do {
+ if (sp == cp->stream) break;
+ if (sp && sp->user) {
+ code = -EBUSY;
+ break;
+ }
+ pvr2_channel_disclaim_stream(cp);
+ if (!sp) break;
+ sp->user = cp;
+ cp->stream = sp;
+ } while (0); pvr2_context_exit(cp->mc_head);
+ return code;
+}
+
+
+// This is the marker for the real beginning of a legitimate mpeg2 stream.
+static char stream_sync_key[] = {
+ 0x00, 0x00, 0x01, 0xba,
+};
+
+struct pvr2_ioread *pvr2_channel_create_mpeg_stream(
+ struct pvr2_context_stream *sp)
+{
+ struct pvr2_ioread *cp;
+ cp = pvr2_ioread_create();
+ if (!cp) return NULL;
+ pvr2_ioread_setup(cp,sp->stream);
+ pvr2_ioread_set_sync_key(cp,stream_sync_key,sizeof(stream_sync_key));
+ return cp;
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-context.h b/drivers/media/video/pvrusb2/pvrusb2-context.h
new file mode 100644
index 00000000..d657e53b
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-context.h
@@ -0,0 +1,94 @@
+/*
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef __PVRUSB2_CONTEXT_H
+#define __PVRUSB2_CONTEXT_H
+
+#include <linux/mutex.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+
+struct pvr2_hdw; /* hardware interface - defined elsewhere */
+struct pvr2_stream; /* stream interface - defined elsewhere */
+
+struct pvr2_context; /* All central state */
+struct pvr2_channel; /* One I/O pathway to a user */
+struct pvr2_context_stream; /* Wrapper for a stream */
+struct pvr2_ioread; /* Low level stream structure */
+
+struct pvr2_context_stream {
+ struct pvr2_channel *user;
+ struct pvr2_stream *stream;
+};
+
+struct pvr2_context {
+ struct pvr2_channel *mc_first;
+ struct pvr2_channel *mc_last;
+ struct pvr2_context *exist_next;
+ struct pvr2_context *exist_prev;
+ struct pvr2_context *notify_next;
+ struct pvr2_context *notify_prev;
+ struct pvr2_hdw *hdw;
+ struct pvr2_context_stream video_stream;
+ struct mutex mutex;
+ int notify_flag;
+ int initialized_flag;
+ int disconnect_flag;
+
+ /* Called after pvr2_context initialization is complete */
+ void (*setup_func)(struct pvr2_context *);
+
+};
+
+struct pvr2_channel {
+ struct pvr2_context *mc_head;
+ struct pvr2_channel *mc_next;
+ struct pvr2_channel *mc_prev;
+ struct pvr2_context_stream *stream;
+ struct pvr2_hdw *hdw;
+ unsigned int input_mask;
+ void (*check_func)(struct pvr2_channel *);
+};
+
+struct pvr2_context *pvr2_context_create(struct usb_interface *intf,
+ const struct usb_device_id *devid,
+ void (*setup_func)(struct pvr2_context *));
+void pvr2_context_disconnect(struct pvr2_context *);
+
+void pvr2_channel_init(struct pvr2_channel *,struct pvr2_context *);
+void pvr2_channel_done(struct pvr2_channel *);
+int pvr2_channel_limit_inputs(struct pvr2_channel *,unsigned int);
+unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *);
+int pvr2_channel_claim_stream(struct pvr2_channel *,
+ struct pvr2_context_stream *);
+struct pvr2_ioread *pvr2_channel_create_mpeg_stream(
+ struct pvr2_context_stream *);
+
+int pvr2_context_global_init(void);
+void pvr2_context_global_done(void);
+
+#endif /* __PVRUSB2_CONTEXT_H */
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c b/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c
new file mode 100644
index 00000000..88320900
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c
@@ -0,0 +1,95 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+
+ This source file is specifically designed to interface with the
+ v4l-dvb cs53l32a module.
+
+*/
+
+#include "pvrusb2-cs53l32a.h"
+
+
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-debug.h"
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <linux/errno.h>
+
+struct routing_scheme {
+ const int *def;
+ unsigned int cnt;
+};
+
+
+static const int routing_scheme1[] = {
+ [PVR2_CVAL_INPUT_TV] = 2, /* 1 or 2 seems to work here */
+ [PVR2_CVAL_INPUT_RADIO] = 2,
+ [PVR2_CVAL_INPUT_COMPOSITE] = 0,
+ [PVR2_CVAL_INPUT_SVIDEO] = 0,
+};
+
+static const struct routing_scheme routing_def1 = {
+ .def = routing_scheme1,
+ .cnt = ARRAY_SIZE(routing_scheme1),
+};
+
+static const struct routing_scheme *routing_schemes[] = {
+ [PVR2_ROUTING_SCHEME_ONAIR] = &routing_def1,
+};
+
+
+void pvr2_cs53l32a_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd)
+{
+ if (hdw->input_dirty || hdw->force_dirty) {
+ const struct routing_scheme *sp;
+ unsigned int sid = hdw->hdw_desc->signal_routing_scheme;
+ u32 input;
+ pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_input(%d)",
+ hdw->input_val);
+ sp = (sid < ARRAY_SIZE(routing_schemes)) ?
+ routing_schemes[sid] : NULL;
+ if ((sp == NULL) ||
+ (hdw->input_val < 0) ||
+ (hdw->input_val >= sp->cnt)) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "*** WARNING *** subdev v4l2 set_input:"
+ " Invalid routing scheme (%u)"
+ " and/or input (%d)",
+ sid, hdw->input_val);
+ return;
+ }
+ input = sp->def[hdw->input_val];
+ sd->ops->audio->s_routing(sd, input, 0, 0);
+ }
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.h b/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.h
new file mode 100644
index 00000000..53ba548b
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.h
@@ -0,0 +1,48 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __PVRUSB2_CS53L32A_H
+#define __PVRUSB2_CS53L32A_H
+
+/*
+
+ This module connects the pvrusb2 driver to the I2C chip level
+ driver which handles device video processing. This interface is
+ used internally by the driver; higher level code should only
+ interact through the interface provided by pvrusb2-hdw.h.
+
+*/
+
+
+#include "pvrusb2-hdw-internal.h"
+void pvr2_cs53l32a_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *);
+
+#endif /* __PVRUSB2_AUDIO_CS53L32A_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-ctrl.c b/drivers/media/video/pvrusb2/pvrusb2-ctrl.c
new file mode 100644
index 00000000..7d5a7139
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-ctrl.c
@@ -0,0 +1,609 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "pvrusb2-ctrl.h"
+#include "pvrusb2-hdw-internal.h"
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mutex.h>
+
+
+static int pvr2_ctrl_range_check(struct pvr2_ctrl *cptr,int val)
+{
+ if (cptr->info->check_value) {
+ if (!cptr->info->check_value(cptr,val)) return -ERANGE;
+ } else if (cptr->info->type == pvr2_ctl_enum) {
+ if (val < 0) return -ERANGE;
+ if (val >= cptr->info->def.type_enum.count) return -ERANGE;
+ } else {
+ int lim;
+ lim = cptr->info->def.type_int.min_value;
+ if (cptr->info->get_min_value) {
+ cptr->info->get_min_value(cptr,&lim);
+ }
+ if (val < lim) return -ERANGE;
+ lim = cptr->info->def.type_int.max_value;
+ if (cptr->info->get_max_value) {
+ cptr->info->get_max_value(cptr,&lim);
+ }
+ if (val > lim) return -ERANGE;
+ }
+ return 0;
+}
+
+
+/* Set the given control. */
+int pvr2_ctrl_set_value(struct pvr2_ctrl *cptr,int val)
+{
+ return pvr2_ctrl_set_mask_value(cptr,~0,val);
+}
+
+
+/* Set/clear specific bits of the given control. */
+int pvr2_ctrl_set_mask_value(struct pvr2_ctrl *cptr,int mask,int val)
+{
+ int ret = 0;
+ if (!cptr) return -EINVAL;
+ LOCK_TAKE(cptr->hdw->big_lock); do {
+ if (cptr->info->set_value) {
+ if (cptr->info->type == pvr2_ctl_bitmask) {
+ mask &= cptr->info->def.type_bitmask.valid_bits;
+ } else if ((cptr->info->type == pvr2_ctl_int)||
+ (cptr->info->type == pvr2_ctl_enum)) {
+ ret = pvr2_ctrl_range_check(cptr,val);
+ if (ret < 0) break;
+ } else if (cptr->info->type != pvr2_ctl_bool) {
+ break;
+ }
+ ret = cptr->info->set_value(cptr,mask,val);
+ } else {
+ ret = -EPERM;
+ }
+ } while(0); LOCK_GIVE(cptr->hdw->big_lock);
+ return ret;
+}
+
+
+/* Get the current value of the given control. */
+int pvr2_ctrl_get_value(struct pvr2_ctrl *cptr,int *valptr)
+{
+ int ret = 0;
+ if (!cptr) return -EINVAL;
+ LOCK_TAKE(cptr->hdw->big_lock); do {
+ ret = cptr->info->get_value(cptr,valptr);
+ } while(0); LOCK_GIVE(cptr->hdw->big_lock);
+ return ret;
+}
+
+
+/* Retrieve control's type */
+enum pvr2_ctl_type pvr2_ctrl_get_type(struct pvr2_ctrl *cptr)
+{
+ if (!cptr) return pvr2_ctl_int;
+ return cptr->info->type;
+}
+
+
+/* Retrieve control's maximum value (int type) */
+int pvr2_ctrl_get_max(struct pvr2_ctrl *cptr)
+{
+ int ret = 0;
+ if (!cptr) return 0;
+ LOCK_TAKE(cptr->hdw->big_lock); do {
+ if (cptr->info->get_max_value) {
+ cptr->info->get_max_value(cptr,&ret);
+ } else if (cptr->info->type == pvr2_ctl_int) {
+ ret = cptr->info->def.type_int.max_value;
+ }
+ } while(0); LOCK_GIVE(cptr->hdw->big_lock);
+ return ret;
+}
+
+
+/* Retrieve control's minimum value (int type) */
+int pvr2_ctrl_get_min(struct pvr2_ctrl *cptr)
+{
+ int ret = 0;
+ if (!cptr) return 0;
+ LOCK_TAKE(cptr->hdw->big_lock); do {
+ if (cptr->info->get_min_value) {
+ cptr->info->get_min_value(cptr,&ret);
+ } else if (cptr->info->type == pvr2_ctl_int) {
+ ret = cptr->info->def.type_int.min_value;
+ }
+ } while(0); LOCK_GIVE(cptr->hdw->big_lock);
+ return ret;
+}
+
+
+/* Retrieve control's default value (any type) */
+int pvr2_ctrl_get_def(struct pvr2_ctrl *cptr, int *valptr)
+{
+ int ret = 0;
+ if (!cptr) return -EINVAL;
+ LOCK_TAKE(cptr->hdw->big_lock); do {
+ if (cptr->info->get_def_value) {
+ ret = cptr->info->get_def_value(cptr, valptr);
+ } else {
+ *valptr = cptr->info->default_value;
+ }
+ } while(0); LOCK_GIVE(cptr->hdw->big_lock);
+ return ret;
+}
+
+
+/* Retrieve control's enumeration count (enum only) */
+int pvr2_ctrl_get_cnt(struct pvr2_ctrl *cptr)
+{
+ int ret = 0;
+ if (!cptr) return 0;
+ LOCK_TAKE(cptr->hdw->big_lock); do {
+ if (cptr->info->type == pvr2_ctl_enum) {
+ ret = cptr->info->def.type_enum.count;
+ }
+ } while(0); LOCK_GIVE(cptr->hdw->big_lock);
+ return ret;
+}
+
+
+/* Retrieve control's valid mask bits (bit mask only) */
+int pvr2_ctrl_get_mask(struct pvr2_ctrl *cptr)
+{
+ int ret = 0;
+ if (!cptr) return 0;
+ LOCK_TAKE(cptr->hdw->big_lock); do {
+ if (cptr->info->type == pvr2_ctl_bitmask) {
+ ret = cptr->info->def.type_bitmask.valid_bits;
+ }
+ } while(0); LOCK_GIVE(cptr->hdw->big_lock);
+ return ret;
+}
+
+
+/* Retrieve the control's name */
+const char *pvr2_ctrl_get_name(struct pvr2_ctrl *cptr)
+{
+ if (!cptr) return NULL;
+ return cptr->info->name;
+}
+
+
+/* Retrieve the control's desc */
+const char *pvr2_ctrl_get_desc(struct pvr2_ctrl *cptr)
+{
+ if (!cptr) return NULL;
+ return cptr->info->desc;
+}
+
+
+/* Retrieve a control enumeration or bit mask value */
+int pvr2_ctrl_get_valname(struct pvr2_ctrl *cptr,int val,
+ char *bptr,unsigned int bmax,
+ unsigned int *blen)
+{
+ int ret = -EINVAL;
+ if (!cptr) return 0;
+ *blen = 0;
+ LOCK_TAKE(cptr->hdw->big_lock); do {
+ if (cptr->info->type == pvr2_ctl_enum) {
+ const char * const *names;
+ names = cptr->info->def.type_enum.value_names;
+ if (pvr2_ctrl_range_check(cptr,val) == 0) {
+ if (names[val]) {
+ *blen = scnprintf(
+ bptr,bmax,"%s",
+ names[val]);
+ } else {
+ *blen = 0;
+ }
+ ret = 0;
+ }
+ } else if (cptr->info->type == pvr2_ctl_bitmask) {
+ const char **names;
+ unsigned int idx;
+ int msk;
+ names = cptr->info->def.type_bitmask.bit_names;
+ val &= cptr->info->def.type_bitmask.valid_bits;
+ for (idx = 0, msk = 1; val; idx++, msk <<= 1) {
+ if (val & msk) {
+ *blen = scnprintf(bptr,bmax,"%s",
+ names[idx]);
+ ret = 0;
+ break;
+ }
+ }
+ }
+ } while(0); LOCK_GIVE(cptr->hdw->big_lock);
+ return ret;
+}
+
+
+/* Return V4L ID for this control or zero if none */
+int pvr2_ctrl_get_v4lid(struct pvr2_ctrl *cptr)
+{
+ if (!cptr) return 0;
+ return cptr->info->v4l_id;
+}
+
+
+unsigned int pvr2_ctrl_get_v4lflags(struct pvr2_ctrl *cptr)
+{
+ unsigned int flags = 0;
+
+ if (cptr->info->get_v4lflags) {
+ flags = cptr->info->get_v4lflags(cptr);
+ }
+
+ if (cptr->info->set_value) {
+ flags &= ~V4L2_CTRL_FLAG_READ_ONLY;
+ } else {
+ flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ }
+
+ return flags;
+}
+
+
+/* Return true if control is writable */
+int pvr2_ctrl_is_writable(struct pvr2_ctrl *cptr)
+{
+ if (!cptr) return 0;
+ return cptr->info->set_value != NULL;
+}
+
+
+/* Return true if control has custom symbolic representation */
+int pvr2_ctrl_has_custom_symbols(struct pvr2_ctrl *cptr)
+{
+ if (!cptr) return 0;
+ if (!cptr->info->val_to_sym) return 0;
+ if (!cptr->info->sym_to_val) return 0;
+ return !0;
+}
+
+
+/* Convert a given mask/val to a custom symbolic value */
+int pvr2_ctrl_custom_value_to_sym(struct pvr2_ctrl *cptr,
+ int mask,int val,
+ char *buf,unsigned int maxlen,
+ unsigned int *len)
+{
+ if (!cptr) return -EINVAL;
+ if (!cptr->info->val_to_sym) return -EINVAL;
+ return cptr->info->val_to_sym(cptr,mask,val,buf,maxlen,len);
+}
+
+
+/* Convert a symbolic value to a mask/value pair */
+int pvr2_ctrl_custom_sym_to_value(struct pvr2_ctrl *cptr,
+ const char *buf,unsigned int len,
+ int *maskptr,int *valptr)
+{
+ if (!cptr) return -EINVAL;
+ if (!cptr->info->sym_to_val) return -EINVAL;
+ return cptr->info->sym_to_val(cptr,buf,len,maskptr,valptr);
+}
+
+
+static unsigned int gen_bitmask_string(int msk,int val,int msk_only,
+ const char **names,
+ char *ptr,unsigned int len)
+{
+ unsigned int idx;
+ long sm,um;
+ int spcFl;
+ unsigned int uc,cnt;
+ const char *idStr;
+
+ spcFl = 0;
+ uc = 0;
+ um = 0;
+ for (idx = 0, sm = 1; msk; idx++, sm <<= 1) {
+ if (sm & msk) {
+ msk &= ~sm;
+ idStr = names[idx];
+ if (idStr) {
+ cnt = scnprintf(ptr,len,"%s%s%s",
+ (spcFl ? " " : ""),
+ (msk_only ? "" :
+ ((val & sm) ? "+" : "-")),
+ idStr);
+ ptr += cnt; len -= cnt; uc += cnt;
+ spcFl = !0;
+ } else {
+ um |= sm;
+ }
+ }
+ }
+ if (um) {
+ if (msk_only) {
+ cnt = scnprintf(ptr,len,"%s0x%lx",
+ (spcFl ? " " : ""),
+ um);
+ ptr += cnt; len -= cnt; uc += cnt;
+ spcFl = !0;
+ } else if (um & val) {
+ cnt = scnprintf(ptr,len,"%s+0x%lx",
+ (spcFl ? " " : ""),
+ um & val);
+ ptr += cnt; len -= cnt; uc += cnt;
+ spcFl = !0;
+ } else if (um & ~val) {
+ cnt = scnprintf(ptr,len,"%s+0x%lx",
+ (spcFl ? " " : ""),
+ um & ~val);
+ ptr += cnt; len -= cnt; uc += cnt;
+ spcFl = !0;
+ }
+ }
+ return uc;
+}
+
+
+static const char *boolNames[] = {
+ "false",
+ "true",
+ "no",
+ "yes",
+};
+
+
+static int parse_token(const char *ptr,unsigned int len,
+ int *valptr,
+ const char * const *names, unsigned int namecnt)
+{
+ char buf[33];
+ unsigned int slen;
+ unsigned int idx;
+ int negfl;
+ char *p2;
+ *valptr = 0;
+ if (!names) namecnt = 0;
+ for (idx = 0; idx < namecnt; idx++) {
+ if (!names[idx]) continue;
+ slen = strlen(names[idx]);
+ if (slen != len) continue;
+ if (memcmp(names[idx],ptr,slen)) continue;
+ *valptr = idx;
+ return 0;
+ }
+ negfl = 0;
+ if ((*ptr == '-') || (*ptr == '+')) {
+ negfl = (*ptr == '-');
+ ptr++; len--;
+ }
+ if (len >= sizeof(buf)) return -EINVAL;
+ memcpy(buf,ptr,len);
+ buf[len] = 0;
+ *valptr = simple_strtol(buf,&p2,0);
+ if (negfl) *valptr = -(*valptr);
+ if (*p2) return -EINVAL;
+ return 1;
+}
+
+
+static int parse_mtoken(const char *ptr,unsigned int len,
+ int *valptr,
+ const char **names,int valid_bits)
+{
+ char buf[33];
+ unsigned int slen;
+ unsigned int idx;
+ char *p2;
+ int msk;
+ *valptr = 0;
+ for (idx = 0, msk = 1; valid_bits; idx++, msk <<= 1) {
+ if (!(msk & valid_bits)) continue;
+ valid_bits &= ~msk;
+ if (!names[idx]) continue;
+ slen = strlen(names[idx]);
+ if (slen != len) continue;
+ if (memcmp(names[idx],ptr,slen)) continue;
+ *valptr = msk;
+ return 0;
+ }
+ if (len >= sizeof(buf)) return -EINVAL;
+ memcpy(buf,ptr,len);
+ buf[len] = 0;
+ *valptr = simple_strtol(buf,&p2,0);
+ if (*p2) return -EINVAL;
+ return 0;
+}
+
+
+static int parse_tlist(const char *ptr,unsigned int len,
+ int *maskptr,int *valptr,
+ const char **names,int valid_bits)
+{
+ unsigned int cnt;
+ int mask,val,kv,mode,ret;
+ mask = 0;
+ val = 0;
+ ret = 0;
+ while (len) {
+ cnt = 0;
+ while ((cnt < len) &&
+ ((ptr[cnt] <= 32) ||
+ (ptr[cnt] >= 127))) cnt++;
+ ptr += cnt;
+ len -= cnt;
+ mode = 0;
+ if ((*ptr == '-') || (*ptr == '+')) {
+ mode = (*ptr == '-') ? -1 : 1;
+ ptr++;
+ len--;
+ }
+ cnt = 0;
+ while (cnt < len) {
+ if (ptr[cnt] <= 32) break;
+ if (ptr[cnt] >= 127) break;
+ cnt++;
+ }
+ if (!cnt) break;
+ if (parse_mtoken(ptr,cnt,&kv,names,valid_bits)) {
+ ret = -EINVAL;
+ break;
+ }
+ ptr += cnt;
+ len -= cnt;
+ switch (mode) {
+ case 0:
+ mask = valid_bits;
+ val |= kv;
+ break;
+ case -1:
+ mask |= kv;
+ val &= ~kv;
+ break;
+ case 1:
+ mask |= kv;
+ val |= kv;
+ break;
+ default:
+ break;
+ }
+ }
+ *maskptr = mask;
+ *valptr = val;
+ return ret;
+}
+
+
+/* Convert a symbolic value to a mask/value pair */
+int pvr2_ctrl_sym_to_value(struct pvr2_ctrl *cptr,
+ const char *ptr,unsigned int len,
+ int *maskptr,int *valptr)
+{
+ int ret = -EINVAL;
+ unsigned int cnt;
+
+ *maskptr = 0;
+ *valptr = 0;
+
+ cnt = 0;
+ while ((cnt < len) && ((ptr[cnt] <= 32) || (ptr[cnt] >= 127))) cnt++;
+ len -= cnt; ptr += cnt;
+ cnt = 0;
+ while ((cnt < len) && ((ptr[len-(cnt+1)] <= 32) ||
+ (ptr[len-(cnt+1)] >= 127))) cnt++;
+ len -= cnt;
+
+ if (!len) return -EINVAL;
+
+ LOCK_TAKE(cptr->hdw->big_lock); do {
+ if (cptr->info->type == pvr2_ctl_int) {
+ ret = parse_token(ptr,len,valptr,NULL,0);
+ if (ret >= 0) {
+ ret = pvr2_ctrl_range_check(cptr,*valptr);
+ }
+ *maskptr = ~0;
+ } else if (cptr->info->type == pvr2_ctl_bool) {
+ ret = parse_token(ptr,len,valptr,boolNames,
+ ARRAY_SIZE(boolNames));
+ if (ret == 1) {
+ *valptr = *valptr ? !0 : 0;
+ } else if (ret == 0) {
+ *valptr = (*valptr & 1) ? !0 : 0;
+ }
+ *maskptr = 1;
+ } else if (cptr->info->type == pvr2_ctl_enum) {
+ ret = parse_token(
+ ptr,len,valptr,
+ cptr->info->def.type_enum.value_names,
+ cptr->info->def.type_enum.count);
+ if (ret >= 0) {
+ ret = pvr2_ctrl_range_check(cptr,*valptr);
+ }
+ *maskptr = ~0;
+ } else if (cptr->info->type == pvr2_ctl_bitmask) {
+ ret = parse_tlist(
+ ptr,len,maskptr,valptr,
+ cptr->info->def.type_bitmask.bit_names,
+ cptr->info->def.type_bitmask.valid_bits);
+ }
+ } while(0); LOCK_GIVE(cptr->hdw->big_lock);
+ return ret;
+}
+
+
+/* Convert a given mask/val to a symbolic value */
+int pvr2_ctrl_value_to_sym_internal(struct pvr2_ctrl *cptr,
+ int mask,int val,
+ char *buf,unsigned int maxlen,
+ unsigned int *len)
+{
+ int ret = -EINVAL;
+
+ *len = 0;
+ if (cptr->info->type == pvr2_ctl_int) {
+ *len = scnprintf(buf,maxlen,"%d",val);
+ ret = 0;
+ } else if (cptr->info->type == pvr2_ctl_bool) {
+ *len = scnprintf(buf,maxlen,"%s",val ? "true" : "false");
+ ret = 0;
+ } else if (cptr->info->type == pvr2_ctl_enum) {
+ const char * const *names;
+ names = cptr->info->def.type_enum.value_names;
+ if ((val >= 0) &&
+ (val < cptr->info->def.type_enum.count)) {
+ if (names[val]) {
+ *len = scnprintf(
+ buf,maxlen,"%s",
+ names[val]);
+ } else {
+ *len = 0;
+ }
+ ret = 0;
+ }
+ } else if (cptr->info->type == pvr2_ctl_bitmask) {
+ *len = gen_bitmask_string(
+ val & mask & cptr->info->def.type_bitmask.valid_bits,
+ ~0,!0,
+ cptr->info->def.type_bitmask.bit_names,
+ buf,maxlen);
+ }
+ return ret;
+}
+
+
+/* Convert a given mask/val to a symbolic value */
+int pvr2_ctrl_value_to_sym(struct pvr2_ctrl *cptr,
+ int mask,int val,
+ char *buf,unsigned int maxlen,
+ unsigned int *len)
+{
+ int ret;
+ LOCK_TAKE(cptr->hdw->big_lock); do {
+ ret = pvr2_ctrl_value_to_sym_internal(cptr,mask,val,
+ buf,maxlen,len);
+ } while(0); LOCK_GIVE(cptr->hdw->big_lock);
+ return ret;
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-ctrl.h b/drivers/media/video/pvrusb2/pvrusb2-ctrl.h
new file mode 100644
index 00000000..794ff901
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-ctrl.h
@@ -0,0 +1,122 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef __PVRUSB2_CTRL_H
+#define __PVRUSB2_CTRL_H
+
+struct pvr2_ctrl;
+
+enum pvr2_ctl_type {
+ pvr2_ctl_int = 0,
+ pvr2_ctl_enum = 1,
+ pvr2_ctl_bitmask = 2,
+ pvr2_ctl_bool = 3,
+};
+
+
+/* Set the given control. */
+int pvr2_ctrl_set_value(struct pvr2_ctrl *,int val);
+
+/* Set/clear specific bits of the given control. */
+int pvr2_ctrl_set_mask_value(struct pvr2_ctrl *,int mask,int val);
+
+/* Get the current value of the given control. */
+int pvr2_ctrl_get_value(struct pvr2_ctrl *,int *valptr);
+
+/* Retrieve control's type */
+enum pvr2_ctl_type pvr2_ctrl_get_type(struct pvr2_ctrl *);
+
+/* Retrieve control's maximum value (int type) */
+int pvr2_ctrl_get_max(struct pvr2_ctrl *);
+
+/* Retrieve control's minimum value (int type) */
+int pvr2_ctrl_get_min(struct pvr2_ctrl *);
+
+/* Retrieve control's default value (any type) */
+int pvr2_ctrl_get_def(struct pvr2_ctrl *, int *valptr);
+
+/* Retrieve control's enumeration count (enum only) */
+int pvr2_ctrl_get_cnt(struct pvr2_ctrl *);
+
+/* Retrieve control's valid mask bits (bit mask only) */
+int pvr2_ctrl_get_mask(struct pvr2_ctrl *);
+
+/* Retrieve the control's name */
+const char *pvr2_ctrl_get_name(struct pvr2_ctrl *);
+
+/* Retrieve the control's desc */
+const char *pvr2_ctrl_get_desc(struct pvr2_ctrl *);
+
+/* Retrieve a control enumeration or bit mask value */
+int pvr2_ctrl_get_valname(struct pvr2_ctrl *,int,char *,unsigned int,
+ unsigned int *);
+
+/* Return true if control is writable */
+int pvr2_ctrl_is_writable(struct pvr2_ctrl *);
+
+/* Return V4L flags value for control (or zero if there is no v4l control
+ actually under this control) */
+unsigned int pvr2_ctrl_get_v4lflags(struct pvr2_ctrl *);
+
+/* Return V4L ID for this control or zero if none */
+int pvr2_ctrl_get_v4lid(struct pvr2_ctrl *);
+
+/* Return true if control has custom symbolic representation */
+int pvr2_ctrl_has_custom_symbols(struct pvr2_ctrl *);
+
+/* Convert a given mask/val to a custom symbolic value */
+int pvr2_ctrl_custom_value_to_sym(struct pvr2_ctrl *,
+ int mask,int val,
+ char *buf,unsigned int maxlen,
+ unsigned int *len);
+
+/* Convert a symbolic value to a mask/value pair */
+int pvr2_ctrl_custom_sym_to_value(struct pvr2_ctrl *,
+ const char *buf,unsigned int len,
+ int *maskptr,int *valptr);
+
+/* Convert a given mask/val to a symbolic value */
+int pvr2_ctrl_value_to_sym(struct pvr2_ctrl *,
+ int mask,int val,
+ char *buf,unsigned int maxlen,
+ unsigned int *len);
+
+/* Convert a symbolic value to a mask/value pair */
+int pvr2_ctrl_sym_to_value(struct pvr2_ctrl *,
+ const char *buf,unsigned int len,
+ int *maskptr,int *valptr);
+
+/* Convert a given mask/val to a symbolic value - must already be
+ inside of critical region. */
+int pvr2_ctrl_value_to_sym_internal(struct pvr2_ctrl *,
+ int mask,int val,
+ char *buf,unsigned int maxlen,
+ unsigned int *len);
+
+#endif /* __PVRUSB2_CTRL_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
new file mode 100644
index 00000000..c514d0b9
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
@@ -0,0 +1,166 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+
+ This source file is specifically designed to interface with the
+ cx2584x, in kernels 2.6.16 or newer.
+
+*/
+
+#include "pvrusb2-cx2584x-v4l.h"
+#include "pvrusb2-video-v4l.h"
+
+
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-debug.h"
+#include <media/cx25840.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <linux/errno.h>
+
+
+struct routing_scheme_item {
+ int vid;
+ int aud;
+};
+
+struct routing_scheme {
+ const struct routing_scheme_item *def;
+ unsigned int cnt;
+};
+
+static const struct routing_scheme_item routing_scheme0[] = {
+ [PVR2_CVAL_INPUT_TV] = {
+ .vid = CX25840_COMPOSITE7,
+ .aud = CX25840_AUDIO8,
+ },
+ [PVR2_CVAL_INPUT_RADIO] = { /* Treat the same as composite */
+ .vid = CX25840_COMPOSITE3,
+ .aud = CX25840_AUDIO_SERIAL,
+ },
+ [PVR2_CVAL_INPUT_COMPOSITE] = {
+ .vid = CX25840_COMPOSITE3,
+ .aud = CX25840_AUDIO_SERIAL,
+ },
+ [PVR2_CVAL_INPUT_SVIDEO] = {
+ .vid = CX25840_SVIDEO1,
+ .aud = CX25840_AUDIO_SERIAL,
+ },
+};
+
+static const struct routing_scheme routing_def0 = {
+ .def = routing_scheme0,
+ .cnt = ARRAY_SIZE(routing_scheme0),
+};
+
+/* Specific to gotview device */
+static const struct routing_scheme_item routing_schemegv[] = {
+ [PVR2_CVAL_INPUT_TV] = {
+ .vid = CX25840_COMPOSITE2,
+ .aud = CX25840_AUDIO5,
+ },
+ [PVR2_CVAL_INPUT_RADIO] = {
+ /* line-in is used for radio and composite. A GPIO is
+ used to switch between the two choices. */
+ .vid = CX25840_COMPOSITE1,
+ .aud = CX25840_AUDIO_SERIAL,
+ },
+ [PVR2_CVAL_INPUT_COMPOSITE] = {
+ .vid = CX25840_COMPOSITE1,
+ .aud = CX25840_AUDIO_SERIAL,
+ },
+ [PVR2_CVAL_INPUT_SVIDEO] = {
+ .vid = (CX25840_SVIDEO_LUMA3|CX25840_SVIDEO_CHROMA4),
+ .aud = CX25840_AUDIO_SERIAL,
+ },
+};
+
+static const struct routing_scheme routing_defgv = {
+ .def = routing_schemegv,
+ .cnt = ARRAY_SIZE(routing_schemegv),
+};
+
+/* Specific to grabster av400 device */
+static const struct routing_scheme_item routing_schemeav400[] = {
+ [PVR2_CVAL_INPUT_COMPOSITE] = {
+ .vid = CX25840_COMPOSITE1,
+ .aud = CX25840_AUDIO_SERIAL,
+ },
+ [PVR2_CVAL_INPUT_SVIDEO] = {
+ .vid = (CX25840_SVIDEO_LUMA2|CX25840_SVIDEO_CHROMA4),
+ .aud = CX25840_AUDIO_SERIAL,
+ },
+};
+
+static const struct routing_scheme routing_defav400 = {
+ .def = routing_schemeav400,
+ .cnt = ARRAY_SIZE(routing_schemeav400),
+};
+
+static const struct routing_scheme *routing_schemes[] = {
+ [PVR2_ROUTING_SCHEME_HAUPPAUGE] = &routing_def0,
+ [PVR2_ROUTING_SCHEME_GOTVIEW] = &routing_defgv,
+ [PVR2_ROUTING_SCHEME_AV400] = &routing_defav400,
+};
+
+void pvr2_cx25840_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd)
+{
+ pvr2_trace(PVR2_TRACE_CHIPS, "subdev cx2584x update...");
+ if (hdw->input_dirty || hdw->force_dirty) {
+ enum cx25840_video_input vid_input;
+ enum cx25840_audio_input aud_input;
+ const struct routing_scheme *sp;
+ unsigned int sid = hdw->hdw_desc->signal_routing_scheme;
+
+ sp = (sid < ARRAY_SIZE(routing_schemes)) ?
+ routing_schemes[sid] : NULL;
+ if ((sp == NULL) ||
+ (hdw->input_val < 0) ||
+ (hdw->input_val >= sp->cnt)) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "*** WARNING *** subdev cx2584x set_input:"
+ " Invalid routing scheme (%u)"
+ " and/or input (%d)",
+ sid, hdw->input_val);
+ return;
+ }
+ vid_input = sp->def[hdw->input_val].vid;
+ aud_input = sp->def[hdw->input_val].aud;
+ pvr2_trace(PVR2_TRACE_CHIPS,
+ "subdev cx2584x set_input vid=0x%x aud=0x%x",
+ vid_input, aud_input);
+ sd->ops->video->s_routing(sd, (u32)vid_input, 0, 0);
+ sd->ops->audio->s_routing(sd, (u32)aud_input, 0, 0);
+ }
+}
+
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h
new file mode 100644
index 00000000..e35c2322
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h
@@ -0,0 +1,52 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __PVRUSB2_CX2584X_V4L_H
+#define __PVRUSB2_CX2584X_V4L_H
+
+/*
+
+ This module connects the pvrusb2 driver to the I2C chip level
+ driver which handles combined device audio & video processing.
+ This interface is used internally by the driver; higher level code
+ should only interact through the interface provided by
+ pvrusb2-hdw.h.
+
+*/
+
+
+
+#include "pvrusb2-hdw-internal.h"
+
+void pvr2_cx25840_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *sd);
+
+
+#endif /* __PVRUSB2_CX2584X_V4L_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-debug.h b/drivers/media/video/pvrusb2/pvrusb2-debug.h
new file mode 100644
index 00000000..be79249f
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-debug.h
@@ -0,0 +1,69 @@
+/*
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef __PVRUSB2_DEBUG_H
+#define __PVRUSB2_DEBUG_H
+
+extern int pvrusb2_debug;
+
+#define pvr2_trace(msk, fmt, arg...) do {if(msk & pvrusb2_debug) printk(KERN_INFO "pvrusb2: " fmt "\n", ##arg); } while (0)
+
+/* These are listed in *rough* order of decreasing usefulness and
+ increasing noise level. */
+#define PVR2_TRACE_INFO (1 << 0) /* Normal messages */
+#define PVR2_TRACE_ERROR_LEGS (1 << 1) /* error messages */
+#define PVR2_TRACE_TOLERANCE (1 << 2) /* track tolerance-affected errors */
+#define PVR2_TRACE_TRAP (1 << 3) /* Trap & report app misbehavior */
+#define PVR2_TRACE_STD (1 << 4) /* Log video standard stuff */
+#define PVR2_TRACE_INIT (1 << 5) /* misc initialization steps */
+#define PVR2_TRACE_START_STOP (1 << 6) /* Streaming start / stop */
+#define PVR2_TRACE_CTL (1 << 7) /* commit of control changes */
+#define PVR2_TRACE_STATE (1 << 8) /* Device state changes */
+#define PVR2_TRACE_STBITS (1 << 9) /* Individual bit state changes */
+#define PVR2_TRACE_EEPROM (1 << 10) /* eeprom parsing / report */
+#define PVR2_TRACE_STRUCT (1 << 11) /* internal struct creation */
+#define PVR2_TRACE_OPEN_CLOSE (1 << 12) /* application open / close */
+#define PVR2_TRACE_CTXT (1 << 13) /* Main context tracking */
+#define PVR2_TRACE_SYSFS (1 << 14) /* Sysfs driven I/O */
+#define PVR2_TRACE_FIRMWARE (1 << 15) /* firmware upload actions */
+#define PVR2_TRACE_CHIPS (1 << 16) /* chip broadcast operation */
+#define PVR2_TRACE_I2C (1 << 17) /* I2C related stuff */
+#define PVR2_TRACE_I2C_CMD (1 << 18) /* Software commands to I2C modules */
+#define PVR2_TRACE_I2C_CORE (1 << 19) /* I2C core debugging */
+#define PVR2_TRACE_I2C_TRAF (1 << 20) /* I2C traffic through the adapter */
+#define PVR2_TRACE_V4LIOCTL (1 << 21) /* v4l ioctl details */
+#define PVR2_TRACE_ENCODER (1 << 22) /* mpeg2 encoder operation */
+#define PVR2_TRACE_BUF_POOL (1 << 23) /* Track buffer pool management */
+#define PVR2_TRACE_BUF_FLOW (1 << 24) /* Track buffer flow in system */
+#define PVR2_TRACE_DATA_FLOW (1 << 25) /* Track data flow */
+#define PVR2_TRACE_DEBUGIFC (1 << 26) /* Debug interface actions */
+#define PVR2_TRACE_GPIO (1 << 27) /* GPIO state bit changes */
+#define PVR2_TRACE_DVB_FEED (1 << 28) /* DVB transport feed debug */
+
+
+#endif /* __PVRUSB2_HDW_INTERNAL_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c
new file mode 100644
index 00000000..4279ebb8
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c
@@ -0,0 +1,335 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/string.h>
+#include "pvrusb2-debugifc.h"
+#include "pvrusb2-hdw.h"
+#include "pvrusb2-debug.h"
+
+struct debugifc_mask_item {
+ const char *name;
+ unsigned long msk;
+};
+
+
+static unsigned int debugifc_count_whitespace(const char *buf,
+ unsigned int count)
+{
+ unsigned int scnt;
+ char ch;
+
+ for (scnt = 0; scnt < count; scnt++) {
+ ch = buf[scnt];
+ if (ch == ' ') continue;
+ if (ch == '\t') continue;
+ if (ch == '\n') continue;
+ break;
+ }
+ return scnt;
+}
+
+
+static unsigned int debugifc_count_nonwhitespace(const char *buf,
+ unsigned int count)
+{
+ unsigned int scnt;
+ char ch;
+
+ for (scnt = 0; scnt < count; scnt++) {
+ ch = buf[scnt];
+ if (ch == ' ') break;
+ if (ch == '\t') break;
+ if (ch == '\n') break;
+ }
+ return scnt;
+}
+
+
+static unsigned int debugifc_isolate_word(const char *buf,unsigned int count,
+ const char **wstrPtr,
+ unsigned int *wlenPtr)
+{
+ const char *wptr;
+ unsigned int consume_cnt = 0;
+ unsigned int wlen;
+ unsigned int scnt;
+
+ wptr = NULL;
+ wlen = 0;
+ scnt = debugifc_count_whitespace(buf,count);
+ consume_cnt += scnt; count -= scnt; buf += scnt;
+ if (!count) goto done;
+
+ scnt = debugifc_count_nonwhitespace(buf,count);
+ if (!scnt) goto done;
+ wptr = buf;
+ wlen = scnt;
+ consume_cnt += scnt; count -= scnt; buf += scnt;
+
+ done:
+ *wstrPtr = wptr;
+ *wlenPtr = wlen;
+ return consume_cnt;
+}
+
+
+static int debugifc_parse_unsigned_number(const char *buf,unsigned int count,
+ u32 *num_ptr)
+{
+ u32 result = 0;
+ int radix = 10;
+ if ((count >= 2) && (buf[0] == '0') &&
+ ((buf[1] == 'x') || (buf[1] == 'X'))) {
+ radix = 16;
+ count -= 2;
+ buf += 2;
+ } else if ((count >= 1) && (buf[0] == '0')) {
+ radix = 8;
+ }
+
+ while (count--) {
+ int val = hex_to_bin(*buf++);
+ if (val < 0 || val >= radix)
+ return -EINVAL;
+ result *= radix;
+ result += val;
+ }
+ *num_ptr = result;
+ return 0;
+}
+
+
+static int debugifc_match_keyword(const char *buf,unsigned int count,
+ const char *keyword)
+{
+ unsigned int kl;
+ if (!keyword) return 0;
+ kl = strlen(keyword);
+ if (kl != count) return 0;
+ return !memcmp(buf,keyword,kl);
+}
+
+
+int pvr2_debugifc_print_info(struct pvr2_hdw *hdw,char *buf,unsigned int acnt)
+{
+ int bcnt = 0;
+ int ccnt;
+ ccnt = scnprintf(buf, acnt, "Driver hardware description: %s\n",
+ pvr2_hdw_get_desc(hdw));
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = scnprintf(buf,acnt,"Driver state info:\n");
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = pvr2_hdw_state_report(hdw,buf,acnt);
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+
+ return bcnt;
+}
+
+
+int pvr2_debugifc_print_status(struct pvr2_hdw *hdw,
+ char *buf,unsigned int acnt)
+{
+ int bcnt = 0;
+ int ccnt;
+ int ret;
+ u32 gpio_dir,gpio_in,gpio_out;
+ struct pvr2_stream_stats stats;
+ struct pvr2_stream *sp;
+
+ ret = pvr2_hdw_is_hsm(hdw);
+ ccnt = scnprintf(buf,acnt,"USB link speed: %s\n",
+ (ret < 0 ? "FAIL" : (ret ? "high" : "full")));
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+
+ gpio_dir = 0; gpio_in = 0; gpio_out = 0;
+ pvr2_hdw_gpio_get_dir(hdw,&gpio_dir);
+ pvr2_hdw_gpio_get_out(hdw,&gpio_out);
+ pvr2_hdw_gpio_get_in(hdw,&gpio_in);
+ ccnt = scnprintf(buf,acnt,"GPIO state: dir=0x%x in=0x%x out=0x%x\n",
+ gpio_dir,gpio_in,gpio_out);
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+
+ ccnt = scnprintf(buf,acnt,"Streaming is %s\n",
+ pvr2_hdw_get_streaming(hdw) ? "on" : "off");
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+
+
+ sp = pvr2_hdw_get_video_stream(hdw);
+ if (sp) {
+ pvr2_stream_get_stats(sp, &stats, 0);
+ ccnt = scnprintf(
+ buf,acnt,
+ "Bytes streamed=%u"
+ " URBs: queued=%u idle=%u ready=%u"
+ " processed=%u failed=%u\n",
+ stats.bytes_processed,
+ stats.buffers_in_queue,
+ stats.buffers_in_idle,
+ stats.buffers_in_ready,
+ stats.buffers_processed,
+ stats.buffers_failed);
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ }
+
+ return bcnt;
+}
+
+
+static int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf,
+ unsigned int count)
+{
+ const char *wptr;
+ unsigned int wlen;
+ unsigned int scnt;
+
+ scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
+ if (!scnt) return 0;
+ count -= scnt; buf += scnt;
+ if (!wptr) return 0;
+
+ pvr2_trace(PVR2_TRACE_DEBUGIFC,"debugifc cmd: \"%.*s\"",wlen,wptr);
+ if (debugifc_match_keyword(wptr,wlen,"reset")) {
+ scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
+ if (!scnt) return -EINVAL;
+ count -= scnt; buf += scnt;
+ if (!wptr) return -EINVAL;
+ if (debugifc_match_keyword(wptr,wlen,"cpu")) {
+ pvr2_hdw_cpureset_assert(hdw,!0);
+ pvr2_hdw_cpureset_assert(hdw,0);
+ return 0;
+ } else if (debugifc_match_keyword(wptr,wlen,"bus")) {
+ pvr2_hdw_device_reset(hdw);
+ } else if (debugifc_match_keyword(wptr,wlen,"soft")) {
+ return pvr2_hdw_cmd_powerup(hdw);
+ } else if (debugifc_match_keyword(wptr,wlen,"deep")) {
+ return pvr2_hdw_cmd_deep_reset(hdw);
+ } else if (debugifc_match_keyword(wptr,wlen,"firmware")) {
+ return pvr2_upload_firmware2(hdw);
+ } else if (debugifc_match_keyword(wptr,wlen,"decoder")) {
+ return pvr2_hdw_cmd_decoder_reset(hdw);
+ } else if (debugifc_match_keyword(wptr,wlen,"worker")) {
+ return pvr2_hdw_untrip(hdw);
+ } else if (debugifc_match_keyword(wptr,wlen,"usbstats")) {
+ pvr2_stream_get_stats(pvr2_hdw_get_video_stream(hdw),
+ NULL, !0);
+ return 0;
+ }
+ return -EINVAL;
+ } else if (debugifc_match_keyword(wptr,wlen,"cpufw")) {
+ scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
+ if (!scnt) return -EINVAL;
+ count -= scnt; buf += scnt;
+ if (!wptr) return -EINVAL;
+ if (debugifc_match_keyword(wptr,wlen,"fetch")) {
+ scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
+ if (scnt && wptr) {
+ count -= scnt; buf += scnt;
+ if (debugifc_match_keyword(wptr, wlen,
+ "prom")) {
+ pvr2_hdw_cpufw_set_enabled(hdw, 2, !0);
+ } else if (debugifc_match_keyword(wptr, wlen,
+ "ram8k")) {
+ pvr2_hdw_cpufw_set_enabled(hdw, 0, !0);
+ } else if (debugifc_match_keyword(wptr, wlen,
+ "ram16k")) {
+ pvr2_hdw_cpufw_set_enabled(hdw, 1, !0);
+ } else {
+ return -EINVAL;
+ }
+ }
+ pvr2_hdw_cpufw_set_enabled(hdw,0,!0);
+ return 0;
+ } else if (debugifc_match_keyword(wptr,wlen,"done")) {
+ pvr2_hdw_cpufw_set_enabled(hdw,0,0);
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+ } else if (debugifc_match_keyword(wptr,wlen,"gpio")) {
+ int dir_fl = 0;
+ int ret;
+ u32 msk,val;
+ scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
+ if (!scnt) return -EINVAL;
+ count -= scnt; buf += scnt;
+ if (!wptr) return -EINVAL;
+ if (debugifc_match_keyword(wptr,wlen,"dir")) {
+ dir_fl = !0;
+ } else if (!debugifc_match_keyword(wptr,wlen,"out")) {
+ return -EINVAL;
+ }
+ scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
+ if (!scnt) return -EINVAL;
+ count -= scnt; buf += scnt;
+ if (!wptr) return -EINVAL;
+ ret = debugifc_parse_unsigned_number(wptr,wlen,&msk);
+ if (ret) return ret;
+ scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
+ if (wptr) {
+ ret = debugifc_parse_unsigned_number(wptr,wlen,&val);
+ if (ret) return ret;
+ } else {
+ val = msk;
+ msk = 0xffffffff;
+ }
+ if (dir_fl) {
+ ret = pvr2_hdw_gpio_chg_dir(hdw,msk,val);
+ } else {
+ ret = pvr2_hdw_gpio_chg_out(hdw,msk,val);
+ }
+ return ret;
+ }
+ pvr2_trace(PVR2_TRACE_DEBUGIFC,
+ "debugifc failed to recognize cmd: \"%.*s\"",wlen,wptr);
+ return -EINVAL;
+}
+
+
+int pvr2_debugifc_docmd(struct pvr2_hdw *hdw,const char *buf,
+ unsigned int count)
+{
+ unsigned int bcnt = 0;
+ int ret;
+
+ while (count) {
+ for (bcnt = 0; bcnt < count; bcnt++) {
+ if (buf[bcnt] == '\n') break;
+ }
+
+ ret = pvr2_debugifc_do1cmd(hdw,buf,bcnt);
+ if (ret < 0) return ret;
+ if (bcnt < count) bcnt++;
+ buf += bcnt;
+ count -= bcnt;
+ }
+
+ return 0;
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.h b/drivers/media/video/pvrusb2/pvrusb2-debugifc.h
new file mode 100644
index 00000000..2f8d4676
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-debugifc.h
@@ -0,0 +1,52 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef __PVRUSB2_DEBUGIFC_H
+#define __PVRUSB2_DEBUGIFC_H
+
+struct pvr2_hdw;
+
+/* Print general status of driver. This will also trigger a probe of
+ the USB link. Unlike print_info(), this one synchronizes with the
+ driver so the information should be self-consistent (but it will
+ hang if the driver is wedged). */
+int pvr2_debugifc_print_info(struct pvr2_hdw *,
+ char *buf_ptr, unsigned int buf_size);
+
+/* Non-intrusively print some useful debugging info from inside the
+ driver. This should work even if the driver appears to be
+ wedged. */
+int pvr2_debugifc_print_status(struct pvr2_hdw *,
+ char *buf_ptr,unsigned int buf_size);
+
+/* Parse a string command into a driver action. */
+int pvr2_debugifc_docmd(struct pvr2_hdw *,
+ const char *buf_ptr,unsigned int buf_size);
+
+#endif /* __PVRUSB2_DEBUGIFC_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/drivers/media/video/pvrusb2/pvrusb2-devattr.c
new file mode 100644
index 00000000..d8c89827
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.c
@@ -0,0 +1,569 @@
+/*
+ *
+ *
+ * Copyright (C) 2007 Mike Isely <isely@pobox.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+
+This source file should encompass ALL per-device type information for the
+driver. To define a new device, add elements to the pvr2_device_table and
+pvr2_device_desc structures.
+
+*/
+
+#include "pvrusb2-devattr.h"
+#include <linux/usb.h>
+#include <linux/module.h>
+/* This is needed in order to pull in tuner type ids... */
+#include <linux/i2c.h>
+#include <media/tuner.h>
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+#include "pvrusb2-hdw-internal.h"
+#include "lgdt330x.h"
+#include "s5h1409.h"
+#include "s5h1411.h"
+#include "tda10048.h"
+#include "tda18271.h"
+#include "tda8290.h"
+#include "tuner-simple.h"
+#endif
+
+
+/*------------------------------------------------------------------------*/
+/* Hauppauge PVR-USB2 Model 29xxx */
+
+static const struct pvr2_device_client_desc pvr2_cli_29xxx[] = {
+ { .module_id = PVR2_CLIENT_ID_SAA7115 },
+ { .module_id = PVR2_CLIENT_ID_MSP3400 },
+ { .module_id = PVR2_CLIENT_ID_TUNER },
+ { .module_id = PVR2_CLIENT_ID_DEMOD },
+};
+
+static const char *pvr2_fw1_names_29xxx[] = {
+ "v4l-pvrusb2-29xxx-01.fw",
+};
+
+static const struct pvr2_device_desc pvr2_device_29xxx = {
+ .description = "WinTV PVR USB2 Model 29xxx",
+ .shortname = "29xxx",
+ .client_table.lst = pvr2_cli_29xxx,
+ .client_table.cnt = ARRAY_SIZE(pvr2_cli_29xxx),
+ .fx2_firmware.lst = pvr2_fw1_names_29xxx,
+ .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_29xxx),
+ .flag_has_hauppauge_rom = !0,
+ .flag_has_analogtuner = !0,
+ .flag_has_fmradio = !0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
+ .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE,
+ .ir_scheme = PVR2_IR_SCHEME_29XXX,
+};
+
+
+
+/*------------------------------------------------------------------------*/
+/* Hauppauge PVR-USB2 Model 24xxx */
+
+static const struct pvr2_device_client_desc pvr2_cli_24xxx[] = {
+ { .module_id = PVR2_CLIENT_ID_CX25840 },
+ { .module_id = PVR2_CLIENT_ID_TUNER },
+ { .module_id = PVR2_CLIENT_ID_WM8775 },
+ { .module_id = PVR2_CLIENT_ID_DEMOD },
+};
+
+static const char *pvr2_fw1_names_24xxx[] = {
+ "v4l-pvrusb2-24xxx-01.fw",
+};
+
+static const struct pvr2_device_desc pvr2_device_24xxx = {
+ .description = "WinTV PVR USB2 Model 24xxx",
+ .shortname = "24xxx",
+ .client_table.lst = pvr2_cli_24xxx,
+ .client_table.cnt = ARRAY_SIZE(pvr2_cli_24xxx),
+ .fx2_firmware.lst = pvr2_fw1_names_24xxx,
+ .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_24xxx),
+ .flag_has_cx25840 = !0,
+ .flag_has_wm8775 = !0,
+ .flag_has_hauppauge_rom = !0,
+ .flag_has_analogtuner = !0,
+ .flag_has_fmradio = !0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
+ .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE,
+ .ir_scheme = PVR2_IR_SCHEME_24XXX,
+};
+
+
+
+/*------------------------------------------------------------------------*/
+/* GOTVIEW USB2.0 DVD2 */
+
+static const struct pvr2_device_client_desc pvr2_cli_gotview_2[] = {
+ { .module_id = PVR2_CLIENT_ID_CX25840 },
+ { .module_id = PVR2_CLIENT_ID_TUNER },
+ { .module_id = PVR2_CLIENT_ID_DEMOD },
+};
+
+static const struct pvr2_device_desc pvr2_device_gotview_2 = {
+ .description = "Gotview USB 2.0 DVD 2",
+ .shortname = "gv2",
+ .client_table.lst = pvr2_cli_gotview_2,
+ .client_table.cnt = ARRAY_SIZE(pvr2_cli_gotview_2),
+ .flag_has_cx25840 = !0,
+ .default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .flag_has_analogtuner = !0,
+ .flag_has_fmradio = !0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_GOTVIEW,
+};
+
+
+
+/*------------------------------------------------------------------------*/
+/* GOTVIEW USB2.0 DVD Deluxe */
+
+/* (same module list as gotview_2) */
+
+static const struct pvr2_device_desc pvr2_device_gotview_2d = {
+ .description = "Gotview USB 2.0 DVD Deluxe",
+ .shortname = "gv2d",
+ .client_table.lst = pvr2_cli_gotview_2,
+ .client_table.cnt = ARRAY_SIZE(pvr2_cli_gotview_2),
+ .flag_has_cx25840 = !0,
+ .default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .flag_has_analogtuner = !0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_GOTVIEW,
+};
+
+
+
+/*------------------------------------------------------------------------*/
+/* Terratec Grabster AV400 */
+
+static const struct pvr2_device_client_desc pvr2_cli_av400[] = {
+ { .module_id = PVR2_CLIENT_ID_CX25840 },
+};
+
+static const struct pvr2_device_desc pvr2_device_av400 = {
+ .description = "Terratec Grabster AV400",
+ .shortname = "av400",
+ .flag_is_experimental = 1,
+ .client_table.lst = pvr2_cli_av400,
+ .client_table.cnt = ARRAY_SIZE(pvr2_cli_av400),
+ .flag_has_cx25840 = !0,
+ .flag_has_analogtuner = 0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_AV400,
+};
+
+
+
+/*------------------------------------------------------------------------*/
+/* OnAir Creator */
+
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+static struct lgdt330x_config pvr2_lgdt3303_config = {
+ .demod_address = 0x0e,
+ .demod_chip = LGDT3303,
+ .clock_polarity_flip = 1,
+};
+
+static int pvr2_lgdt3303_attach(struct pvr2_dvb_adapter *adap)
+{
+ adap->fe = dvb_attach(lgdt330x_attach, &pvr2_lgdt3303_config,
+ &adap->channel.hdw->i2c_adap);
+ if (adap->fe)
+ return 0;
+
+ return -EIO;
+}
+
+static int pvr2_lgh06xf_attach(struct pvr2_dvb_adapter *adap)
+{
+ dvb_attach(simple_tuner_attach, adap->fe,
+ &adap->channel.hdw->i2c_adap, 0x61,
+ TUNER_LG_TDVS_H06XF);
+
+ return 0;
+}
+
+static const struct pvr2_dvb_props pvr2_onair_creator_fe_props = {
+ .frontend_attach = pvr2_lgdt3303_attach,
+ .tuner_attach = pvr2_lgh06xf_attach,
+};
+#endif
+
+static const struct pvr2_device_client_desc pvr2_cli_onair_creator[] = {
+ { .module_id = PVR2_CLIENT_ID_SAA7115 },
+ { .module_id = PVR2_CLIENT_ID_CS53L32A },
+ { .module_id = PVR2_CLIENT_ID_TUNER },
+};
+
+static const struct pvr2_device_desc pvr2_device_onair_creator = {
+ .description = "OnAir Creator Hybrid USB tuner",
+ .shortname = "oac",
+ .client_table.lst = pvr2_cli_onair_creator,
+ .client_table.cnt = ARRAY_SIZE(pvr2_cli_onair_creator),
+ .default_tuner_type = TUNER_LG_TDVS_H06XF,
+ .flag_has_analogtuner = !0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
+ .flag_digital_requires_cx23416 = !0,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_ONAIR,
+ .digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR,
+ .default_std_mask = V4L2_STD_NTSC_M,
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+ .dvb_props = &pvr2_onair_creator_fe_props,
+#endif
+};
+
+
+
+/*------------------------------------------------------------------------*/
+/* OnAir USB 2.0 */
+
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+static struct lgdt330x_config pvr2_lgdt3302_config = {
+ .demod_address = 0x0e,
+ .demod_chip = LGDT3302,
+};
+
+static int pvr2_lgdt3302_attach(struct pvr2_dvb_adapter *adap)
+{
+ adap->fe = dvb_attach(lgdt330x_attach, &pvr2_lgdt3302_config,
+ &adap->channel.hdw->i2c_adap);
+ if (adap->fe)
+ return 0;
+
+ return -EIO;
+}
+
+static int pvr2_fcv1236d_attach(struct pvr2_dvb_adapter *adap)
+{
+ dvb_attach(simple_tuner_attach, adap->fe,
+ &adap->channel.hdw->i2c_adap, 0x61,
+ TUNER_PHILIPS_FCV1236D);
+
+ return 0;
+}
+
+static const struct pvr2_dvb_props pvr2_onair_usb2_fe_props = {
+ .frontend_attach = pvr2_lgdt3302_attach,
+ .tuner_attach = pvr2_fcv1236d_attach,
+};
+#endif
+
+static const struct pvr2_device_client_desc pvr2_cli_onair_usb2[] = {
+ { .module_id = PVR2_CLIENT_ID_SAA7115 },
+ { .module_id = PVR2_CLIENT_ID_CS53L32A },
+ { .module_id = PVR2_CLIENT_ID_TUNER },
+};
+
+static const struct pvr2_device_desc pvr2_device_onair_usb2 = {
+ .description = "OnAir USB2 Hybrid USB tuner",
+ .shortname = "oa2",
+ .client_table.lst = pvr2_cli_onair_usb2,
+ .client_table.cnt = ARRAY_SIZE(pvr2_cli_onair_usb2),
+ .default_tuner_type = TUNER_PHILIPS_FCV1236D,
+ .flag_has_analogtuner = !0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
+ .flag_digital_requires_cx23416 = !0,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_ONAIR,
+ .digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR,
+ .default_std_mask = V4L2_STD_NTSC_M,
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+ .dvb_props = &pvr2_onair_usb2_fe_props,
+#endif
+};
+
+
+
+/*------------------------------------------------------------------------*/
+/* Hauppauge PVR-USB2 Model 73xxx */
+
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+static struct tda10048_config hauppauge_tda10048_config = {
+ .demod_address = 0x10 >> 1,
+ .output_mode = TDA10048_PARALLEL_OUTPUT,
+ .fwbulkwritelen = TDA10048_BULKWRITE_50,
+ .inversion = TDA10048_INVERSION_ON,
+ .dtv6_if_freq_khz = TDA10048_IF_3300,
+ .dtv7_if_freq_khz = TDA10048_IF_3800,
+ .dtv8_if_freq_khz = TDA10048_IF_4300,
+ .clk_freq_khz = TDA10048_CLK_16000,
+ .disable_gate_access = 1,
+};
+
+static struct tda829x_config tda829x_no_probe = {
+ .probe_tuner = TDA829X_DONT_PROBE,
+};
+
+static struct tda18271_std_map hauppauge_tda18271_dvbt_std_map = {
+ .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+ .dvbt_7 = { .if_freq = 3800, .agc_mode = 3, .std = 5,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+ .dvbt_8 = { .if_freq = 4300, .agc_mode = 3, .std = 6,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+};
+
+static struct tda18271_config hauppauge_tda18271_dvb_config = {
+ .std_map = &hauppauge_tda18271_dvbt_std_map,
+ .gate = TDA18271_GATE_ANALOG,
+ .output_opt = TDA18271_OUTPUT_LT_OFF,
+};
+
+static int pvr2_tda10048_attach(struct pvr2_dvb_adapter *adap)
+{
+ adap->fe = dvb_attach(tda10048_attach, &hauppauge_tda10048_config,
+ &adap->channel.hdw->i2c_adap);
+ if (adap->fe)
+ return 0;
+
+ return -EIO;
+}
+
+static int pvr2_73xxx_tda18271_8295_attach(struct pvr2_dvb_adapter *adap)
+{
+ dvb_attach(tda829x_attach, adap->fe,
+ &adap->channel.hdw->i2c_adap, 0x42,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, adap->fe, 0x60,
+ &adap->channel.hdw->i2c_adap,
+ &hauppauge_tda18271_dvb_config);
+
+ return 0;
+}
+
+static const struct pvr2_dvb_props pvr2_73xxx_dvb_props = {
+ .frontend_attach = pvr2_tda10048_attach,
+ .tuner_attach = pvr2_73xxx_tda18271_8295_attach,
+};
+#endif
+
+static const struct pvr2_device_client_desc pvr2_cli_73xxx[] = {
+ { .module_id = PVR2_CLIENT_ID_CX25840 },
+ { .module_id = PVR2_CLIENT_ID_TUNER,
+ .i2c_address_list = "\x42"},
+};
+
+static const char *pvr2_fw1_names_73xxx[] = {
+ "v4l-pvrusb2-73xxx-01.fw",
+};
+
+static const struct pvr2_device_desc pvr2_device_73xxx = {
+ .description = "WinTV HVR-1900 Model 73xxx",
+ .shortname = "73xxx",
+ .client_table.lst = pvr2_cli_73xxx,
+ .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx),
+ .fx2_firmware.lst = pvr2_fw1_names_73xxx,
+ .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_73xxx),
+ .flag_has_cx25840 = !0,
+ .flag_has_hauppauge_rom = !0,
+ .flag_has_analogtuner = !0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
+ .flag_fx2_16kb = !0,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
+ .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE,
+ .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE,
+ .ir_scheme = PVR2_IR_SCHEME_ZILOG,
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+ .dvb_props = &pvr2_73xxx_dvb_props,
+#endif
+};
+
+
+
+/*------------------------------------------------------------------------*/
+/* Hauppauge PVR-USB2 Model 75xxx */
+
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+static struct s5h1409_config pvr2_s5h1409_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_PARALLEL_OUTPUT,
+ .gpio = S5H1409_GPIO_OFF,
+ .qam_if = 4000,
+ .inversion = S5H1409_INVERSION_ON,
+ .status_mode = S5H1409_DEMODLOCKING,
+};
+
+static struct s5h1411_config pvr2_s5h1411_config = {
+ .output_mode = S5H1411_PARALLEL_OUTPUT,
+ .gpio = S5H1411_GPIO_OFF,
+ .vsb_if = S5H1411_IF_44000,
+ .qam_if = S5H1411_IF_4000,
+ .inversion = S5H1411_INVERSION_ON,
+ .status_mode = S5H1411_DEMODLOCKING,
+};
+
+static struct tda18271_std_map hauppauge_tda18271_std_map = {
+ .atsc_6 = { .if_freq = 5380, .agc_mode = 3, .std = 3,
+ .if_lvl = 6, .rfagc_top = 0x37, },
+ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0,
+ .if_lvl = 6, .rfagc_top = 0x37, },
+};
+
+static struct tda18271_config hauppauge_tda18271_config = {
+ .std_map = &hauppauge_tda18271_std_map,
+ .gate = TDA18271_GATE_ANALOG,
+ .output_opt = TDA18271_OUTPUT_LT_OFF,
+};
+
+static int pvr2_s5h1409_attach(struct pvr2_dvb_adapter *adap)
+{
+ adap->fe = dvb_attach(s5h1409_attach, &pvr2_s5h1409_config,
+ &adap->channel.hdw->i2c_adap);
+ if (adap->fe)
+ return 0;
+
+ return -EIO;
+}
+
+static int pvr2_s5h1411_attach(struct pvr2_dvb_adapter *adap)
+{
+ adap->fe = dvb_attach(s5h1411_attach, &pvr2_s5h1411_config,
+ &adap->channel.hdw->i2c_adap);
+ if (adap->fe)
+ return 0;
+
+ return -EIO;
+}
+
+static int pvr2_tda18271_8295_attach(struct pvr2_dvb_adapter *adap)
+{
+ dvb_attach(tda829x_attach, adap->fe,
+ &adap->channel.hdw->i2c_adap, 0x42,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, adap->fe, 0x60,
+ &adap->channel.hdw->i2c_adap,
+ &hauppauge_tda18271_config);
+
+ return 0;
+}
+
+static const struct pvr2_dvb_props pvr2_750xx_dvb_props = {
+ .frontend_attach = pvr2_s5h1409_attach,
+ .tuner_attach = pvr2_tda18271_8295_attach,
+};
+
+static const struct pvr2_dvb_props pvr2_751xx_dvb_props = {
+ .frontend_attach = pvr2_s5h1411_attach,
+ .tuner_attach = pvr2_tda18271_8295_attach,
+};
+#endif
+
+static const char *pvr2_fw1_names_75xxx[] = {
+ "v4l-pvrusb2-73xxx-01.fw",
+};
+
+static const struct pvr2_device_desc pvr2_device_750xx = {
+ .description = "WinTV HVR-1950 Model 750xx",
+ .shortname = "750xx",
+ .client_table.lst = pvr2_cli_73xxx,
+ .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx),
+ .fx2_firmware.lst = pvr2_fw1_names_75xxx,
+ .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx),
+ .flag_has_cx25840 = !0,
+ .flag_has_hauppauge_rom = !0,
+ .flag_has_analogtuner = !0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
+ .flag_fx2_16kb = !0,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
+ .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE,
+ .default_std_mask = V4L2_STD_NTSC_M,
+ .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE,
+ .ir_scheme = PVR2_IR_SCHEME_ZILOG,
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+ .dvb_props = &pvr2_750xx_dvb_props,
+#endif
+};
+
+static const struct pvr2_device_desc pvr2_device_751xx = {
+ .description = "WinTV HVR-1950 Model 751xx",
+ .shortname = "751xx",
+ .client_table.lst = pvr2_cli_73xxx,
+ .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx),
+ .fx2_firmware.lst = pvr2_fw1_names_75xxx,
+ .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx),
+ .flag_has_cx25840 = !0,
+ .flag_has_hauppauge_rom = !0,
+ .flag_has_analogtuner = !0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
+ .flag_fx2_16kb = !0,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
+ .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE,
+ .default_std_mask = V4L2_STD_NTSC_M,
+ .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE,
+ .ir_scheme = PVR2_IR_SCHEME_ZILOG,
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+ .dvb_props = &pvr2_751xx_dvb_props,
+#endif
+};
+
+
+
+/*------------------------------------------------------------------------*/
+
+struct usb_device_id pvr2_device_table[] = {
+ { USB_DEVICE(0x2040, 0x2900),
+ .driver_info = (kernel_ulong_t)&pvr2_device_29xxx},
+ { USB_DEVICE(0x2040, 0x2950), /* Logically identical to 2900 */
+ .driver_info = (kernel_ulong_t)&pvr2_device_29xxx},
+ { USB_DEVICE(0x2040, 0x2400),
+ .driver_info = (kernel_ulong_t)&pvr2_device_24xxx},
+ { USB_DEVICE(0x1164, 0x0622),
+ .driver_info = (kernel_ulong_t)&pvr2_device_gotview_2},
+ { USB_DEVICE(0x1164, 0x0602),
+ .driver_info = (kernel_ulong_t)&pvr2_device_gotview_2d},
+ { USB_DEVICE(0x11ba, 0x1003),
+ .driver_info = (kernel_ulong_t)&pvr2_device_onair_creator},
+ { USB_DEVICE(0x11ba, 0x1001),
+ .driver_info = (kernel_ulong_t)&pvr2_device_onair_usb2},
+ { USB_DEVICE(0x2040, 0x7300),
+ .driver_info = (kernel_ulong_t)&pvr2_device_73xxx},
+ { USB_DEVICE(0x2040, 0x7500),
+ .driver_info = (kernel_ulong_t)&pvr2_device_750xx},
+ { USB_DEVICE(0x2040, 0x7501),
+ .driver_info = (kernel_ulong_t)&pvr2_device_751xx},
+ { USB_DEVICE(0x0ccd, 0x0039),
+ .driver_info = (kernel_ulong_t)&pvr2_device_av400},
+ { }
+};
+
+MODULE_DEVICE_TABLE(usb, pvr2_device_table);
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/drivers/media/video/pvrusb2/pvrusb2-devattr.h
new file mode 100644
index 00000000..273c8d4b
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.h
@@ -0,0 +1,199 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef __PVRUSB2_DEVATTR_H
+#define __PVRUSB2_DEVATTR_H
+
+#include <linux/mod_devicetable.h>
+#include <linux/videodev2.h>
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+#include "pvrusb2-dvb.h"
+#endif
+
+/*
+
+ This header defines structures used to describe attributes of a device.
+
+*/
+
+
+#define PVR2_CLIENT_ID_NULL 0
+#define PVR2_CLIENT_ID_MSP3400 1
+#define PVR2_CLIENT_ID_CX25840 2
+#define PVR2_CLIENT_ID_SAA7115 3
+#define PVR2_CLIENT_ID_TUNER 4
+#define PVR2_CLIENT_ID_CS53L32A 5
+#define PVR2_CLIENT_ID_WM8775 6
+#define PVR2_CLIENT_ID_DEMOD 7
+
+struct pvr2_device_client_desc {
+ /* One ovr PVR2_CLIENT_ID_xxxx */
+ unsigned char module_id;
+
+ /* Null-terminated array of I2C addresses to try in order
+ initialize the module. It's safe to make this null terminated
+ since we're never going to encounter an i2c device with an
+ address of zero. If this is a null pointer or zero-length,
+ then no I2C addresses have been specified, in which case we'll
+ try some compiled in defaults for now. */
+ unsigned char *i2c_address_list;
+};
+
+struct pvr2_device_client_table {
+ const struct pvr2_device_client_desc *lst;
+ unsigned char cnt;
+};
+
+
+struct pvr2_string_table {
+ const char **lst;
+ unsigned int cnt;
+};
+
+#define PVR2_ROUTING_SCHEME_HAUPPAUGE 0
+#define PVR2_ROUTING_SCHEME_GOTVIEW 1
+#define PVR2_ROUTING_SCHEME_ONAIR 2
+#define PVR2_ROUTING_SCHEME_AV400 3
+
+#define PVR2_DIGITAL_SCHEME_NONE 0
+#define PVR2_DIGITAL_SCHEME_HAUPPAUGE 1
+#define PVR2_DIGITAL_SCHEME_ONAIR 2
+
+#define PVR2_LED_SCHEME_NONE 0
+#define PVR2_LED_SCHEME_HAUPPAUGE 1
+
+#define PVR2_IR_SCHEME_NONE 0
+#define PVR2_IR_SCHEME_24XXX 1 /* FX2-controlled IR */
+#define PVR2_IR_SCHEME_ZILOG 2 /* HVR-1950 style (must be taken out of reset) */
+#define PVR2_IR_SCHEME_24XXX_MCE 3 /* 24xxx MCE device */
+#define PVR2_IR_SCHEME_29XXX 4 /* Original 29xxx device */
+
+/* This describes a particular hardware type (except for the USB device ID
+ which must live in a separate structure due to environmental
+ constraints). See the top of pvrusb2-hdw.c for where this is
+ instantiated. */
+struct pvr2_device_desc {
+ /* Single line text description of hardware */
+ const char *description;
+
+ /* Single token identifier for hardware */
+ const char *shortname;
+
+ /* List of additional client modules we need to load */
+ struct pvr2_string_table client_modules;
+
+ /* List of defined client modules we need to load */
+ struct pvr2_device_client_table client_table;
+
+ /* List of FX2 firmware file names we should search; if empty then
+ FX2 firmware check / load is skipped and we assume the device
+ was initialized from internal ROM. */
+ struct pvr2_string_table fx2_firmware;
+
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+ /* callback functions to handle attachment of digital tuner & demod */
+ const struct pvr2_dvb_props *dvb_props;
+
+#endif
+ /* Initial standard bits to use for this device, if not zero.
+ Anything set here is also implied as an available standard.
+ Note: This is ignored if overridden on the module load line via
+ the video_std module option. */
+ v4l2_std_id default_std_mask;
+
+ /* V4L tuner type ID to use with this device (only used if the
+ driver could not discover the type any other way). */
+ int default_tuner_type;
+
+ /* Signal routing scheme used by device, contains one of
+ PVR2_ROUTING_SCHEME_XXX. Schemes have to be defined as we
+ encounter them. This is an arbitrary integer scheme id; its
+ meaning is contained entirely within the driver and is
+ interpreted by logic which must send commands to the chip-level
+ drivers (search for things which touch this field). */
+ unsigned char signal_routing_scheme;
+
+ /* Indicates scheme for controlling device's LED (if any). The
+ driver will turn on the LED when streaming is underway. This
+ contains one of PVR2_LED_SCHEME_XXX. */
+ unsigned char led_scheme;
+
+ /* Control scheme to use if there is a digital tuner. This
+ contains one of PVR2_DIGITAL_SCHEME_XXX. This is an arbitrary
+ integer scheme id; its meaning is contained entirely within the
+ driver and is interpreted by logic which must control the
+ streaming pathway (search for things which touch this field). */
+ unsigned char digital_control_scheme;
+
+ /* If set, we don't bother trying to load cx23416 firmware. */
+ unsigned int flag_skip_cx23416_firmware:1;
+
+ /* If set, the encoder must be healthy in order for digital mode to
+ work (otherwise we assume that digital streaming will work even
+ if we fail to locate firmware for the encoder). If the device
+ doesn't support digital streaming then this flag has no
+ effect. */
+ unsigned int flag_digital_requires_cx23416:1;
+
+ /* Device has a hauppauge eeprom which we can interrogate. */
+ unsigned int flag_has_hauppauge_rom:1;
+
+ /* Device does not require a powerup command to be issued. */
+ unsigned int flag_no_powerup:1;
+
+ /* Device has a cx25840 - this enables special additional logic to
+ handle it. */
+ unsigned int flag_has_cx25840:1;
+
+ /* Device has a wm8775 - this enables special additional logic to
+ ensure that it is found. */
+ unsigned int flag_has_wm8775:1;
+
+ /* Indicate IR scheme of hardware. If not set, then it is assumed
+ that IR can work without any help from the driver. */
+ unsigned int ir_scheme:3;
+
+ /* These bits define which kinds of sources the device can handle.
+ Note: Digital tuner presence is inferred by the
+ digital_control_scheme enumeration. */
+ unsigned int flag_has_fmradio:1; /* Has FM radio receiver */
+ unsigned int flag_has_analogtuner:1; /* Has analog tuner */
+ unsigned int flag_has_composite:1; /* Has composite input */
+ unsigned int flag_has_svideo:1; /* Has s-video input */
+ unsigned int flag_fx2_16kb:1; /* 16KB FX2 firmware OK here */
+
+ /* If this driver is considered experimental, i.e. not all aspects
+ are working correctly and/or it is untested, mark that fact
+ with this flag. */
+ unsigned int flag_is_experimental:1;
+};
+
+extern struct usb_device_id pvr2_device_table[];
+
+#endif /* __PVRUSB2_HDW_INTERNAL_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-dvb.c b/drivers/media/video/pvrusb2/pvrusb2-dvb.c
new file mode 100644
index 00000000..8c957934
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-dvb.c
@@ -0,0 +1,435 @@
+/*
+ * pvrusb2-dvb.c - linux-dvb api interface to the pvrusb2 driver.
+ *
+ * Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include "dvbdev.h"
+#include "pvrusb2-debug.h"
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-hdw.h"
+#include "pvrusb2-io.h"
+#include "pvrusb2-dvb.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static int pvr2_dvb_feed_func(struct pvr2_dvb_adapter *adap)
+{
+ int ret;
+ unsigned int count;
+ struct pvr2_buffer *bp;
+ struct pvr2_stream *stream;
+
+ pvr2_trace(PVR2_TRACE_DVB_FEED, "dvb feed thread started");
+ set_freezable();
+
+ stream = adap->channel.stream->stream;
+
+ for (;;) {
+ if (kthread_should_stop()) break;
+
+ /* Not sure about this... */
+ try_to_freeze();
+
+ bp = pvr2_stream_get_ready_buffer(stream);
+ if (bp != NULL) {
+ count = pvr2_buffer_get_count(bp);
+ if (count) {
+ dvb_dmx_swfilter(
+ &adap->demux,
+ adap->buffer_storage[
+ pvr2_buffer_get_id(bp)],
+ count);
+ } else {
+ ret = pvr2_buffer_get_status(bp);
+ if (ret < 0) break;
+ }
+ ret = pvr2_buffer_queue(bp);
+ if (ret < 0) break;
+
+ /* Since we know we did something to a buffer,
+ just go back and try again. No point in
+ blocking unless we really ran out of
+ buffers to process. */
+ continue;
+ }
+
+
+ /* Wait until more buffers become available or we're
+ told not to wait any longer. */
+ ret = wait_event_interruptible(
+ adap->buffer_wait_data,
+ (pvr2_stream_get_ready_count(stream) > 0) ||
+ kthread_should_stop());
+ if (ret < 0) break;
+ }
+
+ /* If we get here and ret is < 0, then an error has occurred.
+ Probably would be a good idea to communicate that to DVB core... */
+
+ pvr2_trace(PVR2_TRACE_DVB_FEED, "dvb feed thread stopped");
+
+ return 0;
+}
+
+static int pvr2_dvb_feed_thread(void *data)
+{
+ int stat = pvr2_dvb_feed_func(data);
+ /* from videobuf-dvb.c: */
+ while (!kthread_should_stop()) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule();
+ }
+ return stat;
+}
+
+static void pvr2_dvb_notify(struct pvr2_dvb_adapter *adap)
+{
+ wake_up(&adap->buffer_wait_data);
+}
+
+static void pvr2_dvb_stream_end(struct pvr2_dvb_adapter *adap)
+{
+ unsigned int idx;
+ struct pvr2_stream *stream;
+
+ if (adap->thread) {
+ kthread_stop(adap->thread);
+ adap->thread = NULL;
+ }
+
+ if (adap->channel.stream) {
+ stream = adap->channel.stream->stream;
+ } else {
+ stream = NULL;
+ }
+ if (stream) {
+ pvr2_hdw_set_streaming(adap->channel.hdw, 0);
+ pvr2_stream_set_callback(stream, NULL, NULL);
+ pvr2_stream_kill(stream);
+ pvr2_stream_set_buffer_count(stream, 0);
+ pvr2_channel_claim_stream(&adap->channel, NULL);
+ }
+
+ if (adap->stream_run) {
+ for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) {
+ if (!(adap->buffer_storage[idx])) continue;
+ kfree(adap->buffer_storage[idx]);
+ adap->buffer_storage[idx] = NULL;
+ }
+ adap->stream_run = 0;
+ }
+}
+
+static int pvr2_dvb_stream_do_start(struct pvr2_dvb_adapter *adap)
+{
+ struct pvr2_context *pvr = adap->channel.mc_head;
+ unsigned int idx;
+ int ret;
+ struct pvr2_buffer *bp;
+ struct pvr2_stream *stream = NULL;
+
+ if (adap->stream_run) return -EIO;
+
+ ret = pvr2_channel_claim_stream(&adap->channel, &pvr->video_stream);
+ /* somebody else already has the stream */
+ if (ret < 0) return ret;
+
+ stream = adap->channel.stream->stream;
+
+ for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) {
+ adap->buffer_storage[idx] = kmalloc(PVR2_DVB_BUFFER_SIZE,
+ GFP_KERNEL);
+ if (!(adap->buffer_storage[idx])) return -ENOMEM;
+ }
+
+ pvr2_stream_set_callback(pvr->video_stream.stream,
+ (pvr2_stream_callback) pvr2_dvb_notify, adap);
+
+ ret = pvr2_stream_set_buffer_count(stream, PVR2_DVB_BUFFER_COUNT);
+ if (ret < 0) return ret;
+
+ for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) {
+ bp = pvr2_stream_get_buffer(stream, idx);
+ pvr2_buffer_set_buffer(bp,
+ adap->buffer_storage[idx],
+ PVR2_DVB_BUFFER_SIZE);
+ }
+
+ ret = pvr2_hdw_set_streaming(adap->channel.hdw, 1);
+ if (ret < 0) return ret;
+
+ while ((bp = pvr2_stream_get_idle_buffer(stream)) != NULL) {
+ ret = pvr2_buffer_queue(bp);
+ if (ret < 0) return ret;
+ }
+
+ adap->thread = kthread_run(pvr2_dvb_feed_thread, adap, "pvrusb2-dvb");
+
+ if (IS_ERR(adap->thread)) {
+ ret = PTR_ERR(adap->thread);
+ adap->thread = NULL;
+ return ret;
+ }
+
+ adap->stream_run = !0;
+
+ return 0;
+}
+
+static int pvr2_dvb_stream_start(struct pvr2_dvb_adapter *adap)
+{
+ int ret = pvr2_dvb_stream_do_start(adap);
+ if (ret < 0) pvr2_dvb_stream_end(adap);
+ return ret;
+}
+
+static int pvr2_dvb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff)
+{
+ struct pvr2_dvb_adapter *adap = dvbdmxfeed->demux->priv;
+ int ret = 0;
+
+ if (adap == NULL) return -ENODEV;
+
+ mutex_lock(&adap->lock);
+ do {
+ if (onoff) {
+ if (!adap->feedcount) {
+ pvr2_trace(PVR2_TRACE_DVB_FEED,
+ "start feeding demux");
+ ret = pvr2_dvb_stream_start(adap);
+ if (ret < 0) break;
+ }
+ (adap->feedcount)++;
+ } else if (adap->feedcount > 0) {
+ (adap->feedcount)--;
+ if (!adap->feedcount) {
+ pvr2_trace(PVR2_TRACE_DVB_FEED,
+ "stop feeding demux");
+ pvr2_dvb_stream_end(adap);
+ }
+ }
+ } while (0);
+ mutex_unlock(&adap->lock);
+
+ return ret;
+}
+
+static int pvr2_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ pvr2_trace(PVR2_TRACE_DVB_FEED, "start pid: 0x%04x", dvbdmxfeed->pid);
+ return pvr2_dvb_ctrl_feed(dvbdmxfeed, 1);
+}
+
+static int pvr2_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ pvr2_trace(PVR2_TRACE_DVB_FEED, "stop pid: 0x%04x", dvbdmxfeed->pid);
+ return pvr2_dvb_ctrl_feed(dvbdmxfeed, 0);
+}
+
+static int pvr2_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire)
+{
+ struct pvr2_dvb_adapter *adap = fe->dvb->priv;
+ return pvr2_channel_limit_inputs(
+ &adap->channel,
+ (acquire ? (1 << PVR2_CVAL_INPUT_DTV) : 0));
+}
+
+static int pvr2_dvb_adapter_init(struct pvr2_dvb_adapter *adap)
+{
+ int ret;
+
+ ret = dvb_register_adapter(&adap->dvb_adap, "pvrusb2-dvb",
+ THIS_MODULE/*&hdw->usb_dev->owner*/,
+ &adap->channel.hdw->usb_dev->dev,
+ adapter_nr);
+ if (ret < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "dvb_register_adapter failed: error %d", ret);
+ goto err;
+ }
+ adap->dvb_adap.priv = adap;
+
+ adap->demux.dmx.capabilities = DMX_TS_FILTERING |
+ DMX_SECTION_FILTERING |
+ DMX_MEMORY_BASED_FILTERING;
+ adap->demux.priv = adap;
+ adap->demux.filternum = 256;
+ adap->demux.feednum = 256;
+ adap->demux.start_feed = pvr2_dvb_start_feed;
+ adap->demux.stop_feed = pvr2_dvb_stop_feed;
+ adap->demux.write_to_decoder = NULL;
+
+ ret = dvb_dmx_init(&adap->demux);
+ if (ret < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "dvb_dmx_init failed: error %d", ret);
+ goto err_dmx;
+ }
+
+ adap->dmxdev.filternum = adap->demux.filternum;
+ adap->dmxdev.demux = &adap->demux.dmx;
+ adap->dmxdev.capabilities = 0;
+
+ ret = dvb_dmxdev_init(&adap->dmxdev, &adap->dvb_adap);
+ if (ret < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "dvb_dmxdev_init failed: error %d", ret);
+ goto err_dmx_dev;
+ }
+
+ dvb_net_init(&adap->dvb_adap, &adap->dvb_net, &adap->demux.dmx);
+
+ return 0;
+
+err_dmx_dev:
+ dvb_dmx_release(&adap->demux);
+err_dmx:
+ dvb_unregister_adapter(&adap->dvb_adap);
+err:
+ return ret;
+}
+
+static int pvr2_dvb_adapter_exit(struct pvr2_dvb_adapter *adap)
+{
+ pvr2_trace(PVR2_TRACE_INFO, "unregistering DVB devices");
+ dvb_net_release(&adap->dvb_net);
+ adap->demux.dmx.close(&adap->demux.dmx);
+ dvb_dmxdev_release(&adap->dmxdev);
+ dvb_dmx_release(&adap->demux);
+ dvb_unregister_adapter(&adap->dvb_adap);
+ return 0;
+}
+
+static int pvr2_dvb_frontend_init(struct pvr2_dvb_adapter *adap)
+{
+ struct pvr2_hdw *hdw = adap->channel.hdw;
+ const struct pvr2_dvb_props *dvb_props = hdw->hdw_desc->dvb_props;
+ int ret = 0;
+
+ if (dvb_props == NULL) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS, "fe_props not defined!");
+ return -EINVAL;
+ }
+
+ ret = pvr2_channel_limit_inputs(
+ &adap->channel,
+ (1 << PVR2_CVAL_INPUT_DTV));
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "failed to grab control of dtv input (code=%d)",
+ ret);
+ return ret;
+ }
+
+ if (dvb_props->frontend_attach == NULL) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "frontend_attach not defined!");
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if ((dvb_props->frontend_attach(adap) == 0) && (adap->fe)) {
+
+ if (dvb_register_frontend(&adap->dvb_adap, adap->fe)) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "frontend registration failed!");
+ dvb_frontend_detach(adap->fe);
+ adap->fe = NULL;
+ ret = -ENODEV;
+ goto done;
+ }
+
+ if (dvb_props->tuner_attach)
+ dvb_props->tuner_attach(adap);
+
+ if (adap->fe->ops.analog_ops.standby)
+ adap->fe->ops.analog_ops.standby(adap->fe);
+
+ /* Ensure all frontends negotiate bus access */
+ adap->fe->ops.ts_bus_ctrl = pvr2_dvb_bus_ctrl;
+
+ } else {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "no frontend was attached!");
+ ret = -ENODEV;
+ return ret;
+ }
+
+ done:
+ pvr2_channel_limit_inputs(&adap->channel, 0);
+ return ret;
+}
+
+static int pvr2_dvb_frontend_exit(struct pvr2_dvb_adapter *adap)
+{
+ if (adap->fe != NULL) {
+ dvb_unregister_frontend(adap->fe);
+ dvb_frontend_detach(adap->fe);
+ }
+ return 0;
+}
+
+static void pvr2_dvb_destroy(struct pvr2_dvb_adapter *adap)
+{
+ pvr2_dvb_stream_end(adap);
+ pvr2_dvb_frontend_exit(adap);
+ pvr2_dvb_adapter_exit(adap);
+ pvr2_channel_done(&adap->channel);
+ kfree(adap);
+}
+
+static void pvr2_dvb_internal_check(struct pvr2_channel *chp)
+{
+ struct pvr2_dvb_adapter *adap;
+ adap = container_of(chp, struct pvr2_dvb_adapter, channel);
+ if (!adap->channel.mc_head->disconnect_flag) return;
+ pvr2_dvb_destroy(adap);
+}
+
+struct pvr2_dvb_adapter *pvr2_dvb_create(struct pvr2_context *pvr)
+{
+ int ret = 0;
+ struct pvr2_dvb_adapter *adap;
+ if (!pvr->hdw->hdw_desc->dvb_props) {
+ /* Device lacks a digital interface so don't set up
+ the DVB side of the driver either. For now. */
+ return NULL;
+ }
+ adap = kzalloc(sizeof(*adap), GFP_KERNEL);
+ if (!adap) return adap;
+ pvr2_channel_init(&adap->channel, pvr);
+ adap->channel.check_func = pvr2_dvb_internal_check;
+ init_waitqueue_head(&adap->buffer_wait_data);
+ mutex_init(&adap->lock);
+ ret = pvr2_dvb_adapter_init(adap);
+ if (ret < 0) goto fail1;
+ ret = pvr2_dvb_frontend_init(adap);
+ if (ret < 0) goto fail2;
+ return adap;
+
+fail2:
+ pvr2_dvb_adapter_exit(adap);
+fail1:
+ pvr2_channel_done(&adap->channel);
+ return NULL;
+}
+
diff --git a/drivers/media/video/pvrusb2/pvrusb2-dvb.h b/drivers/media/video/pvrusb2/pvrusb2-dvb.h
new file mode 100644
index 00000000..884ff916
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-dvb.h
@@ -0,0 +1,41 @@
+#ifndef __PVRUSB2_DVB_H__
+#define __PVRUSB2_DVB_H__
+
+#include "dvb_frontend.h"
+#include "dvb_demux.h"
+#include "dvb_net.h"
+#include "dmxdev.h"
+#include "pvrusb2-context.h"
+
+#define PVR2_DVB_BUFFER_COUNT 32
+#define PVR2_DVB_BUFFER_SIZE PAGE_ALIGN(0x4000)
+
+struct pvr2_dvb_adapter {
+ struct pvr2_channel channel;
+
+ struct dvb_adapter dvb_adap;
+ struct dmxdev dmxdev;
+ struct dvb_demux demux;
+ struct dvb_net dvb_net;
+ struct dvb_frontend *fe;
+
+ int feedcount;
+ int max_feed_count;
+
+ struct task_struct *thread;
+ struct mutex lock;
+
+ unsigned int stream_run:1;
+
+ wait_queue_head_t buffer_wait_data;
+ char *buffer_storage[PVR2_DVB_BUFFER_COUNT];
+};
+
+struct pvr2_dvb_props {
+ int (*frontend_attach) (struct pvr2_dvb_adapter *);
+ int (*tuner_attach) (struct pvr2_dvb_adapter *);
+};
+
+struct pvr2_dvb_adapter *pvr2_dvb_create(struct pvr2_context *pvr);
+
+#endif /* __PVRUSB2_DVB_H__ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-eeprom.c b/drivers/media/video/pvrusb2/pvrusb2-eeprom.c
new file mode 100644
index 00000000..9515f3a6
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-eeprom.c
@@ -0,0 +1,164 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/slab.h>
+#include "pvrusb2-eeprom.h"
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-debug.h"
+
+#define trace_eeprom(...) pvr2_trace(PVR2_TRACE_EEPROM,__VA_ARGS__)
+
+
+
+/*
+
+ Read and analyze data in the eeprom. Use tveeprom to figure out
+ the packet structure, since this is another Hauppauge device and
+ internally it has a family resemblance to ivtv-type devices
+
+*/
+
+#include <media/tveeprom.h>
+
+/* We seem to only be interested in the last 128 bytes of the EEPROM */
+#define EEPROM_SIZE 128
+
+/* Grab EEPROM contents, needed for direct method. */
+static u8 *pvr2_eeprom_fetch(struct pvr2_hdw *hdw)
+{
+ struct i2c_msg msg[2];
+ u8 *eeprom;
+ u8 iadd[2];
+ u8 addr;
+ u16 eepromSize;
+ unsigned int offs;
+ int ret;
+ int mode16 = 0;
+ unsigned pcnt,tcnt;
+ eeprom = kmalloc(EEPROM_SIZE,GFP_KERNEL);
+ if (!eeprom) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Failed to allocate memory"
+ " required to read eeprom");
+ return NULL;
+ }
+
+ trace_eeprom("Value for eeprom addr from controller was 0x%x",
+ hdw->eeprom_addr);
+ addr = hdw->eeprom_addr;
+ /* Seems that if the high bit is set, then the *real* eeprom
+ address is shifted right now bit position (noticed this in
+ newer PVR USB2 hardware) */
+ if (addr & 0x80) addr >>= 1;
+
+ /* FX2 documentation states that a 16bit-addressed eeprom is
+ expected if the I2C address is an odd number (yeah, this is
+ strange but it's what they do) */
+ mode16 = (addr & 1);
+ eepromSize = (mode16 ? 4096 : 256);
+ trace_eeprom("Examining %d byte eeprom at location 0x%x"
+ " using %d bit addressing",eepromSize,addr,
+ mode16 ? 16 : 8);
+
+ msg[0].addr = addr;
+ msg[0].flags = 0;
+ msg[0].len = mode16 ? 2 : 1;
+ msg[0].buf = iadd;
+ msg[1].addr = addr;
+ msg[1].flags = I2C_M_RD;
+
+ /* We have to do the actual eeprom data fetch ourselves, because
+ (1) we're only fetching part of the eeprom, and (2) if we were
+ getting the whole thing our I2C driver can't grab it in one
+ pass - which is what tveeprom is otherwise going to attempt */
+ memset(eeprom,0,EEPROM_SIZE);
+ for (tcnt = 0; tcnt < EEPROM_SIZE; tcnt += pcnt) {
+ pcnt = 16;
+ if (pcnt + tcnt > EEPROM_SIZE) pcnt = EEPROM_SIZE-tcnt;
+ offs = tcnt + (eepromSize - EEPROM_SIZE);
+ if (mode16) {
+ iadd[0] = offs >> 8;
+ iadd[1] = offs;
+ } else {
+ iadd[0] = offs;
+ }
+ msg[1].len = pcnt;
+ msg[1].buf = eeprom+tcnt;
+ if ((ret = i2c_transfer(&hdw->i2c_adap,
+ msg,ARRAY_SIZE(msg))) != 2) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "eeprom fetch set offs err=%d",ret);
+ kfree(eeprom);
+ return NULL;
+ }
+ }
+ return eeprom;
+}
+
+
+/* Directly call eeprom analysis function within tveeprom. */
+int pvr2_eeprom_analyze(struct pvr2_hdw *hdw)
+{
+ u8 *eeprom;
+ struct tveeprom tvdata;
+
+ memset(&tvdata,0,sizeof(tvdata));
+
+ eeprom = pvr2_eeprom_fetch(hdw);
+ if (!eeprom) return -EINVAL;
+
+ {
+ struct i2c_client fake_client;
+ /* Newer version expects a useless client interface */
+ fake_client.addr = hdw->eeprom_addr;
+ fake_client.adapter = &hdw->i2c_adap;
+ tveeprom_hauppauge_analog(&fake_client,&tvdata,eeprom);
+ }
+
+ trace_eeprom("eeprom assumed v4l tveeprom module");
+ trace_eeprom("eeprom direct call results:");
+ trace_eeprom("has_radio=%d",tvdata.has_radio);
+ trace_eeprom("tuner_type=%d",tvdata.tuner_type);
+ trace_eeprom("tuner_formats=0x%x",tvdata.tuner_formats);
+ trace_eeprom("audio_processor=%d",tvdata.audio_processor);
+ trace_eeprom("model=%d",tvdata.model);
+ trace_eeprom("revision=%d",tvdata.revision);
+ trace_eeprom("serial_number=%d",tvdata.serial_number);
+ trace_eeprom("rev_str=%s",tvdata.rev_str);
+ hdw->tuner_type = tvdata.tuner_type;
+ hdw->tuner_updated = !0;
+ hdw->serial_number = tvdata.serial_number;
+ hdw->std_mask_eeprom = tvdata.tuner_formats;
+
+ kfree(eeprom);
+
+ return 0;
+}
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-eeprom.h b/drivers/media/video/pvrusb2/pvrusb2-eeprom.h
new file mode 100644
index 00000000..cca3216f
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-eeprom.h
@@ -0,0 +1,39 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __PVRUSB2_EEPROM_H
+#define __PVRUSB2_EEPROM_H
+
+struct pvr2_hdw;
+
+int pvr2_eeprom_analyze(struct pvr2_hdw *);
+
+#endif /* __PVRUSB2_EEPROM_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/drivers/media/video/pvrusb2/pvrusb2-encoder.c
new file mode 100644
index 00000000..e046fdae
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.c
@@ -0,0 +1,552 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/device.h> // for linux/firmware.h
+#include <linux/firmware.h>
+#include "pvrusb2-util.h"
+#include "pvrusb2-encoder.h"
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-debug.h"
+#include "pvrusb2-fx2-cmd.h"
+
+
+
+/* Firmware mailbox flags - definitions found from ivtv */
+#define IVTV_MBOX_FIRMWARE_DONE 0x00000004
+#define IVTV_MBOX_DRIVER_DONE 0x00000002
+#define IVTV_MBOX_DRIVER_BUSY 0x00000001
+
+#define MBOX_BASE 0x44
+
+
+static int pvr2_encoder_write_words(struct pvr2_hdw *hdw,
+ unsigned int offs,
+ const u32 *data, unsigned int dlen)
+{
+ unsigned int idx,addr;
+ unsigned int bAddr;
+ int ret;
+ unsigned int chunkCnt;
+
+ /*
+
+ Format: First byte must be 0x01. Remaining 32 bit words are
+ spread out into chunks of 7 bytes each, with the first 4 bytes
+ being the data word (little endian), and the next 3 bytes
+ being the address where that data word is to be written (big
+ endian). Repeat request for additional words, with offset
+ adjusted accordingly.
+
+ */
+ while (dlen) {
+ chunkCnt = 8;
+ if (chunkCnt > dlen) chunkCnt = dlen;
+ memset(hdw->cmd_buffer,0,sizeof(hdw->cmd_buffer));
+ bAddr = 0;
+ hdw->cmd_buffer[bAddr++] = FX2CMD_MEM_WRITE_DWORD;
+ for (idx = 0; idx < chunkCnt; idx++) {
+ addr = idx + offs;
+ hdw->cmd_buffer[bAddr+6] = (addr & 0xffu);
+ hdw->cmd_buffer[bAddr+5] = ((addr>>8) & 0xffu);
+ hdw->cmd_buffer[bAddr+4] = ((addr>>16) & 0xffu);
+ PVR2_DECOMPOSE_LE(hdw->cmd_buffer, bAddr,data[idx]);
+ bAddr += 7;
+ }
+ ret = pvr2_send_request(hdw,
+ hdw->cmd_buffer,1+(chunkCnt*7),
+ NULL,0);
+ if (ret) return ret;
+ data += chunkCnt;
+ dlen -= chunkCnt;
+ offs += chunkCnt;
+ }
+
+ return 0;
+}
+
+
+static int pvr2_encoder_read_words(struct pvr2_hdw *hdw,
+ unsigned int offs,
+ u32 *data, unsigned int dlen)
+{
+ unsigned int idx;
+ int ret;
+ unsigned int chunkCnt;
+
+ /*
+
+ Format: First byte must be 0x02 (status check) or 0x28 (read
+ back block of 32 bit words). Next 6 bytes must be zero,
+ followed by a single byte of MBOX_BASE+offset for portion to
+ be read. Returned data is packed set of 32 bits words that
+ were read.
+
+ */
+
+ while (dlen) {
+ chunkCnt = 16;
+ if (chunkCnt > dlen) chunkCnt = dlen;
+ if (chunkCnt < 16) chunkCnt = 1;
+ hdw->cmd_buffer[0] =
+ ((chunkCnt == 1) ?
+ FX2CMD_MEM_READ_DWORD : FX2CMD_MEM_READ_64BYTES);
+ hdw->cmd_buffer[1] = 0;
+ hdw->cmd_buffer[2] = 0;
+ hdw->cmd_buffer[3] = 0;
+ hdw->cmd_buffer[4] = 0;
+ hdw->cmd_buffer[5] = ((offs>>16) & 0xffu);
+ hdw->cmd_buffer[6] = ((offs>>8) & 0xffu);
+ hdw->cmd_buffer[7] = (offs & 0xffu);
+ ret = pvr2_send_request(hdw,
+ hdw->cmd_buffer,8,
+ hdw->cmd_buffer,
+ (chunkCnt == 1 ? 4 : 16 * 4));
+ if (ret) return ret;
+
+ for (idx = 0; idx < chunkCnt; idx++) {
+ data[idx] = PVR2_COMPOSE_LE(hdw->cmd_buffer,idx*4);
+ }
+ data += chunkCnt;
+ dlen -= chunkCnt;
+ offs += chunkCnt;
+ }
+
+ return 0;
+}
+
+
+/* This prototype is set up to be compatible with the
+ cx2341x_mbox_func prototype in cx2341x.h, which should be in
+ kernels 2.6.18 or later. We do this so that we can enable
+ cx2341x.ko to write to our encoder (by handing it a pointer to this
+ function). For earlier kernels this doesn't really matter. */
+static int pvr2_encoder_cmd(void *ctxt,
+ u32 cmd,
+ int arg_cnt_send,
+ int arg_cnt_recv,
+ u32 *argp)
+{
+ unsigned int poll_count;
+ unsigned int try_count = 0;
+ int retry_flag;
+ int ret = 0;
+ unsigned int idx;
+ /* These sizes look to be limited by the FX2 firmware implementation */
+ u32 wrData[16];
+ u32 rdData[16];
+ struct pvr2_hdw *hdw = (struct pvr2_hdw *)ctxt;
+
+
+ /*
+
+ The encoder seems to speak entirely using blocks 32 bit words.
+ In ivtv driver terms, this is a mailbox at MBOX_BASE which we
+ populate with data and watch what the hardware does with it.
+ The first word is a set of flags used to control the
+ transaction, the second word is the command to execute, the
+ third byte is zero (ivtv driver suggests that this is some
+ kind of return value), and the fourth byte is a specified
+ timeout (windows driver always uses 0x00060000 except for one
+ case when it is zero). All successive words are the argument
+ words for the command.
+
+ First, write out the entire set of words, with the first word
+ being zero.
+
+ Next, write out just the first word again, but set it to
+ IVTV_MBOX_DRIVER_DONE | IVTV_DRIVER_BUSY this time (which
+ probably means "go").
+
+ Next, read back the return count words. Check the first word,
+ which should have IVTV_MBOX_FIRMWARE_DONE set. If however
+ that bit is not set, then the command isn't done so repeat the
+ read until it is set.
+
+ Finally, write out just the first word again, but set it to
+ 0x0 this time (which probably means "idle").
+
+ */
+
+ if (arg_cnt_send > (ARRAY_SIZE(wrData) - 4)) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Failed to write cx23416 command"
+ " - too many input arguments"
+ " (was given %u limit %lu)",
+ arg_cnt_send, (long unsigned) ARRAY_SIZE(wrData) - 4);
+ return -EINVAL;
+ }
+
+ if (arg_cnt_recv > (ARRAY_SIZE(rdData) - 4)) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Failed to write cx23416 command"
+ " - too many return arguments"
+ " (was given %u limit %lu)",
+ arg_cnt_recv, (long unsigned) ARRAY_SIZE(rdData) - 4);
+ return -EINVAL;
+ }
+
+
+ LOCK_TAKE(hdw->ctl_lock); do {
+
+ if (!hdw->state_encoder_ok) {
+ ret = -EIO;
+ break;
+ }
+
+ retry_flag = 0;
+ try_count++;
+ ret = 0;
+ wrData[0] = 0;
+ wrData[1] = cmd;
+ wrData[2] = 0;
+ wrData[3] = 0x00060000;
+ for (idx = 0; idx < arg_cnt_send; idx++) {
+ wrData[idx+4] = argp[idx];
+ }
+ for (; idx < ARRAY_SIZE(wrData) - 4; idx++) {
+ wrData[idx+4] = 0;
+ }
+
+ ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,idx);
+ if (ret) break;
+ wrData[0] = IVTV_MBOX_DRIVER_DONE|IVTV_MBOX_DRIVER_BUSY;
+ ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,1);
+ if (ret) break;
+ poll_count = 0;
+ while (1) {
+ poll_count++;
+ ret = pvr2_encoder_read_words(hdw,MBOX_BASE,rdData,
+ arg_cnt_recv+4);
+ if (ret) {
+ break;
+ }
+ if (rdData[0] & IVTV_MBOX_FIRMWARE_DONE) {
+ break;
+ }
+ if (rdData[0] && (poll_count < 1000)) continue;
+ if (!rdData[0]) {
+ retry_flag = !0;
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Encoder timed out waiting for us"
+ "; arranging to retry");
+ } else {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "***WARNING*** device's encoder"
+ " appears to be stuck"
+ " (status=0x%08x)",rdData[0]);
+ }
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Encoder command: 0x%02x",cmd);
+ for (idx = 4; idx < arg_cnt_send; idx++) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Encoder arg%d: 0x%08x",
+ idx-3,wrData[idx]);
+ }
+ ret = -EBUSY;
+ break;
+ }
+ if (retry_flag) {
+ if (try_count < 20) continue;
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Too many retries...");
+ ret = -EBUSY;
+ }
+ if (ret) {
+ del_timer_sync(&hdw->encoder_run_timer);
+ hdw->state_encoder_ok = 0;
+ pvr2_trace(PVR2_TRACE_STBITS,
+ "State bit %s <-- %s",
+ "state_encoder_ok",
+ (hdw->state_encoder_ok ? "true" : "false"));
+ if (hdw->state_encoder_runok) {
+ hdw->state_encoder_runok = 0;
+ pvr2_trace(PVR2_TRACE_STBITS,
+ "State bit %s <-- %s",
+ "state_encoder_runok",
+ (hdw->state_encoder_runok ?
+ "true" : "false"));
+ }
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Giving up on command."
+ " This is normally recovered via a firmware"
+ " reload and re-initialization; concern"
+ " is only warranted if this happens repeatedly"
+ " and rapidly.");
+ break;
+ }
+ wrData[0] = 0x7;
+ for (idx = 0; idx < arg_cnt_recv; idx++) {
+ argp[idx] = rdData[idx+4];
+ }
+
+ wrData[0] = 0x0;
+ ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,1);
+ if (ret) break;
+
+ } while(0); LOCK_GIVE(hdw->ctl_lock);
+
+ return ret;
+}
+
+
+static int pvr2_encoder_vcmd(struct pvr2_hdw *hdw, int cmd,
+ int args, ...)
+{
+ va_list vl;
+ unsigned int idx;
+ u32 data[12];
+
+ if (args > ARRAY_SIZE(data)) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Failed to write cx23416 command"
+ " - too many arguments"
+ " (was given %u limit %lu)",
+ args, (long unsigned) ARRAY_SIZE(data));
+ return -EINVAL;
+ }
+
+ va_start(vl, args);
+ for (idx = 0; idx < args; idx++) {
+ data[idx] = va_arg(vl, u32);
+ }
+ va_end(vl);
+
+ return pvr2_encoder_cmd(hdw,cmd,args,0,data);
+}
+
+
+/* This implements some extra setup for the encoder that seems to be
+ specific to the PVR USB2 hardware. */
+static int pvr2_encoder_prep_config(struct pvr2_hdw *hdw)
+{
+ int ret = 0;
+ int encMisc3Arg = 0;
+
+#if 0
+ /* This inexplicable bit happens in the Hauppauge windows
+ driver (for both 24xxx and 29xxx devices). However I
+ currently see no difference in behavior with or without
+ this stuff. Leave this here as a note of its existence,
+ but don't use it. */
+ LOCK_TAKE(hdw->ctl_lock); do {
+ u32 dat[1];
+ dat[0] = 0x80000640;
+ pvr2_encoder_write_words(hdw,0x01fe,dat,1);
+ pvr2_encoder_write_words(hdw,0x023e,dat,1);
+ } while(0); LOCK_GIVE(hdw->ctl_lock);
+#endif
+
+ /* Mike Isely <isely@pobox.com> 26-Jan-2006 The windows driver
+ sends the following list of ENC_MISC commands (for both
+ 24xxx and 29xxx devices). Meanings are not entirely clear,
+ however without the ENC_MISC(3,1) command then we risk
+ random perpetual video corruption whenever the video input
+ breaks up for a moment (like when switching channels). */
+
+
+#if 0
+ /* This ENC_MISC(5,0) command seems to hurt 29xxx sync
+ performance on channel changes, but is not a problem on
+ 24xxx devices. */
+ ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 5,0,0,0);
+#endif
+
+ /* This ENC_MISC(3,encMisc3Arg) command is critical - without
+ it there will eventually be video corruption. Also, the
+ saa7115 case is strange - the Windows driver is passing 1
+ regardless of device type but if we have 1 for saa7115
+ devices the video turns sluggish. */
+ if (hdw->hdw_desc->flag_has_cx25840) {
+ encMisc3Arg = 1;
+ } else {
+ encMisc3Arg = 0;
+ }
+ ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 3,
+ encMisc3Arg,0,0);
+
+ ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 8,0,0,0);
+
+#if 0
+ /* This ENC_MISC(4,1) command is poisonous, so it is commented
+ out. But I'm leaving it here anyway to document its
+ existence in the Windows driver. The effect of this
+ command is that apps displaying the stream become sluggish
+ with stuttering video. */
+ ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 4,1,0,0);
+#endif
+
+ ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 0,3,0,0);
+ ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4,15,0,0,0);
+
+ /* prevent the PTSs from slowly drifting away in the generated
+ MPEG stream */
+ ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC, 2, 4, 1);
+
+ return ret;
+}
+
+int pvr2_encoder_adjust(struct pvr2_hdw *hdw)
+{
+ int ret;
+ ret = cx2341x_update(hdw,pvr2_encoder_cmd,
+ (hdw->enc_cur_valid ? &hdw->enc_cur_state : NULL),
+ &hdw->enc_ctl_state);
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Error from cx2341x module code=%d",ret);
+ } else {
+ memcpy(&hdw->enc_cur_state,&hdw->enc_ctl_state,
+ sizeof(struct cx2341x_mpeg_params));
+ hdw->enc_cur_valid = !0;
+ }
+ return ret;
+}
+
+
+int pvr2_encoder_configure(struct pvr2_hdw *hdw)
+{
+ int ret;
+ int val;
+ pvr2_trace(PVR2_TRACE_ENCODER,"pvr2_encoder_configure"
+ " (cx2341x module)");
+ hdw->enc_ctl_state.port = CX2341X_PORT_STREAMING;
+ hdw->enc_ctl_state.width = hdw->res_hor_val;
+ hdw->enc_ctl_state.height = hdw->res_ver_val;
+ hdw->enc_ctl_state.is_50hz = ((hdw->std_mask_cur & V4L2_STD_525_60) ?
+ 0 : 1);
+
+ ret = 0;
+
+ ret |= pvr2_encoder_prep_config(hdw);
+
+ /* saa7115: 0xf0 */
+ val = 0xf0;
+ if (hdw->hdw_desc->flag_has_cx25840) {
+ /* ivtv cx25840: 0x140 */
+ val = 0x140;
+ }
+
+ if (!ret) ret = pvr2_encoder_vcmd(
+ hdw,CX2341X_ENC_SET_NUM_VSYNC_LINES, 2,
+ val, val);
+
+ /* setup firmware to notify us about some events (don't know why...) */
+ if (!ret) ret = pvr2_encoder_vcmd(
+ hdw,CX2341X_ENC_SET_EVENT_NOTIFICATION, 4,
+ 0, 0, 0x10000000, 0xffffffff);
+
+ if (!ret) ret = pvr2_encoder_vcmd(
+ hdw,CX2341X_ENC_SET_VBI_LINE, 5,
+ 0xffffffff,0,0,0,0);
+
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Failed to configure cx23416");
+ return ret;
+ }
+
+ ret = pvr2_encoder_adjust(hdw);
+ if (ret) return ret;
+
+ ret = pvr2_encoder_vcmd(
+ hdw, CX2341X_ENC_INITIALIZE_INPUT, 0);
+
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Failed to initialize cx23416 video input");
+ return ret;
+ }
+
+ return 0;
+}
+
+
+int pvr2_encoder_start(struct pvr2_hdw *hdw)
+{
+ int status;
+
+ /* unmask some interrupts */
+ pvr2_write_register(hdw, 0x0048, 0xbfffffff);
+
+ pvr2_encoder_vcmd(hdw,CX2341X_ENC_MUTE_VIDEO,1,
+ hdw->input_val == PVR2_CVAL_INPUT_RADIO ? 1 : 0);
+
+ switch (hdw->active_stream_type) {
+ case pvr2_config_vbi:
+ status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
+ 0x01,0x14);
+ break;
+ case pvr2_config_mpeg:
+ status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
+ 0,0x13);
+ break;
+ default: /* Unhandled cases for now */
+ status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
+ 0,0x13);
+ break;
+ }
+ return status;
+}
+
+int pvr2_encoder_stop(struct pvr2_hdw *hdw)
+{
+ int status;
+
+ /* mask all interrupts */
+ pvr2_write_register(hdw, 0x0048, 0xffffffff);
+
+ switch (hdw->active_stream_type) {
+ case pvr2_config_vbi:
+ status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
+ 0x01,0x01,0x14);
+ break;
+ case pvr2_config_mpeg:
+ status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
+ 0x01,0,0x13);
+ break;
+ default: /* Unhandled cases for now */
+ status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
+ 0x01,0,0x13);
+ break;
+ }
+
+ return status;
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.h b/drivers/media/video/pvrusb2/pvrusb2-encoder.h
new file mode 100644
index 00000000..232fefbc
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.h
@@ -0,0 +1,42 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __PVRUSB2_ENCODER_H
+#define __PVRUSB2_ENCODER_H
+
+struct pvr2_hdw;
+
+int pvr2_encoder_adjust(struct pvr2_hdw *);
+int pvr2_encoder_configure(struct pvr2_hdw *);
+int pvr2_encoder_start(struct pvr2_hdw *);
+int pvr2_encoder_stop(struct pvr2_hdw *);
+
+#endif /* __PVRUSB2_ENCODER_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h b/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h
new file mode 100644
index 00000000..614755ea
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h
@@ -0,0 +1,72 @@
+/*
+ *
+ *
+ * Copyright (C) 2007 Michael Krufky <mkrufky@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef _PVRUSB2_FX2_CMD_H_
+#define _PVRUSB2_FX2_CMD_H_
+
+#define FX2CMD_MEM_WRITE_DWORD 0x01u
+#define FX2CMD_MEM_READ_DWORD 0x02u
+
+#define FX2CMD_HCW_ZILOG_RESET 0x10u /* 1=reset 0=release */
+
+#define FX2CMD_MEM_READ_64BYTES 0x28u
+
+#define FX2CMD_REG_WRITE 0x04u
+#define FX2CMD_REG_READ 0x05u
+#define FX2CMD_MEMSEL 0x06u
+
+#define FX2CMD_I2C_WRITE 0x08u
+#define FX2CMD_I2C_READ 0x09u
+
+#define FX2CMD_GET_USB_SPEED 0x0bu
+
+#define FX2CMD_STREAMING_ON 0x36u
+#define FX2CMD_STREAMING_OFF 0x37u
+
+#define FX2CMD_FWPOST1 0x52u
+
+#define FX2CMD_POWER_OFF 0xdcu
+#define FX2CMD_POWER_ON 0xdeu
+
+#define FX2CMD_DEEP_RESET 0xddu
+
+#define FX2CMD_GET_EEPROM_ADDR 0xebu
+#define FX2CMD_GET_IR_CODE 0xecu
+
+#define FX2CMD_HCW_DEMOD_RESETIN 0xf0u
+#define FX2CMD_HCW_DTV_STREAMING_ON 0xf1u
+#define FX2CMD_HCW_DTV_STREAMING_OFF 0xf2u
+
+#define FX2CMD_ONAIR_DTV_STREAMING_ON 0xa0u
+#define FX2CMD_ONAIR_DTV_STREAMING_OFF 0xa1u
+#define FX2CMD_ONAIR_DTV_POWER_ON 0xa2u
+#define FX2CMD_ONAIR_DTV_POWER_OFF 0xa3u
+
+#endif /* _PVRUSB2_FX2_CMD_H_ */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
new file mode 100644
index 00000000..305e6aaa
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
@@ -0,0 +1,408 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef __PVRUSB2_HDW_INTERNAL_H
+#define __PVRUSB2_HDW_INTERNAL_H
+
+/*
+
+ This header sets up all the internal structures and definitions needed to
+ track and coordinate the driver's interaction with the hardware. ONLY
+ source files which actually implement part of that whole circus should be
+ including this header. Higher levels, like the external layers to the
+ various public APIs (V4L, sysfs, etc) should NOT ever include this
+ private, internal header. This means that pvrusb2-hdw, pvrusb2-encoder,
+ etc will include this, but pvrusb2-v4l should not.
+
+*/
+
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include "pvrusb2-hdw.h"
+#include "pvrusb2-io.h"
+#include <media/v4l2-device.h>
+#include <media/cx2341x.h>
+#include <media/ir-kbd-i2c.h>
+#include "pvrusb2-devattr.h"
+
+/* Legal values for PVR2_CID_HSM */
+#define PVR2_CVAL_HSM_FAIL 0
+#define PVR2_CVAL_HSM_FULL 1
+#define PVR2_CVAL_HSM_HIGH 2
+
+#define PVR2_VID_ENDPOINT 0x84
+#define PVR2_UNK_ENDPOINT 0x86 /* maybe raw yuv ? */
+#define PVR2_VBI_ENDPOINT 0x88
+
+#define PVR2_CTL_BUFFSIZE 64
+
+#define FREQTABLE_SIZE 500
+
+#define LOCK_TAKE(x) do { mutex_lock(&x##_mutex); x##_held = !0; } while (0)
+#define LOCK_GIVE(x) do { x##_held = 0; mutex_unlock(&x##_mutex); } while (0)
+
+typedef int (*pvr2_ctlf_is_dirty)(struct pvr2_ctrl *);
+typedef void (*pvr2_ctlf_clear_dirty)(struct pvr2_ctrl *);
+typedef int (*pvr2_ctlf_check_value)(struct pvr2_ctrl *,int);
+typedef int (*pvr2_ctlf_get_value)(struct pvr2_ctrl *,int *);
+typedef int (*pvr2_ctlf_set_value)(struct pvr2_ctrl *,int msk,int val);
+typedef int (*pvr2_ctlf_val_to_sym)(struct pvr2_ctrl *,int msk,int val,
+ char *,unsigned int,unsigned int *);
+typedef int (*pvr2_ctlf_sym_to_val)(struct pvr2_ctrl *,
+ const char *,unsigned int,
+ int *mskp,int *valp);
+typedef unsigned int (*pvr2_ctlf_get_v4lflags)(struct pvr2_ctrl *);
+
+/* This structure describes a specific control. A table of these is set up
+ in pvrusb2-hdw.c. */
+struct pvr2_ctl_info {
+ /* Control's name suitable for use as an identifier */
+ const char *name;
+
+ /* Short description of control */
+ const char *desc;
+
+ /* Control's implementation */
+ pvr2_ctlf_get_value get_value; /* Get its value */
+ pvr2_ctlf_get_value get_def_value; /* Get its default value */
+ pvr2_ctlf_get_value get_min_value; /* Get minimum allowed value */
+ pvr2_ctlf_get_value get_max_value; /* Get maximum allowed value */
+ pvr2_ctlf_set_value set_value; /* Set its value */
+ pvr2_ctlf_check_value check_value; /* Check that value is valid */
+ pvr2_ctlf_val_to_sym val_to_sym; /* Custom convert value->symbol */
+ pvr2_ctlf_sym_to_val sym_to_val; /* Custom convert symbol->value */
+ pvr2_ctlf_is_dirty is_dirty; /* Return true if dirty */
+ pvr2_ctlf_clear_dirty clear_dirty; /* Clear dirty state */
+ pvr2_ctlf_get_v4lflags get_v4lflags;/* Retrieve v4l flags */
+
+ /* Control's type (int, enum, bitmask) */
+ enum pvr2_ctl_type type;
+
+ /* Associated V4L control ID, if any */
+ int v4l_id;
+
+ /* Associated driver internal ID, if any */
+ int internal_id;
+
+ /* Don't implicitly initialize this control's value */
+ int skip_init;
+
+ /* Starting value for this control */
+ int default_value;
+
+ /* Type-specific control information */
+ union {
+ struct { /* Integer control */
+ long min_value; /* lower limit */
+ long max_value; /* upper limit */
+ } type_int;
+ struct { /* enumerated control */
+ unsigned int count; /* enum value count */
+ const char * const *value_names; /* symbol names */
+ } type_enum;
+ struct { /* bitmask control */
+ unsigned int valid_bits; /* bits in use */
+ const char **bit_names; /* symbol name/bit */
+ } type_bitmask;
+ } def;
+};
+
+
+/* Same as pvr2_ctl_info, but includes storage for the control description */
+#define PVR2_CTLD_INFO_DESC_SIZE 32
+struct pvr2_ctld_info {
+ struct pvr2_ctl_info info;
+ char desc[PVR2_CTLD_INFO_DESC_SIZE];
+};
+
+struct pvr2_ctrl {
+ const struct pvr2_ctl_info *info;
+ struct pvr2_hdw *hdw;
+};
+
+
+
+/* Disposition of firmware1 loading situation */
+#define FW1_STATE_UNKNOWN 0
+#define FW1_STATE_MISSING 1
+#define FW1_STATE_FAILED 2
+#define FW1_STATE_RELOAD 3
+#define FW1_STATE_OK 4
+
+/* What state the device is in if it is a hybrid */
+#define PVR2_PATHWAY_UNKNOWN 0
+#define PVR2_PATHWAY_ANALOG 1
+#define PVR2_PATHWAY_DIGITAL 2
+
+typedef int (*pvr2_i2c_func)(struct pvr2_hdw *,u8,u8 *,u16,u8 *, u16);
+#define PVR2_I2C_FUNC_CNT 128
+
+/* This structure contains all state data directly needed to
+ manipulate the hardware (as opposed to complying with a kernel
+ interface) */
+struct pvr2_hdw {
+ /* Underlying USB device handle */
+ struct usb_device *usb_dev;
+ struct usb_interface *usb_intf;
+
+ /* Our handle into the v4l2 sub-device architecture */
+ struct v4l2_device v4l2_dev;
+ /* Device description, anything that must adjust behavior based on
+ device specific info will use information held here. */
+ const struct pvr2_device_desc *hdw_desc;
+
+ /* Kernel worker thread handling */
+ struct workqueue_struct *workqueue;
+ struct work_struct workpoll; /* Update driver state */
+
+ /* Video spigot */
+ struct pvr2_stream *vid_stream;
+
+ /* Mutex for all hardware state control */
+ struct mutex big_lock_mutex;
+ int big_lock_held; /* For debugging */
+
+ /* This is a simple string which identifies the instance of this
+ driver. It is unique within the set of existing devices, but
+ there is no attempt to keep the name consistent with the same
+ physical device each time. */
+ char name[32];
+
+ /* This is a simple string which identifies the physical device
+ instance itself - if possible. (If not possible, then it is
+ based on the specific driver instance, similar to name above.)
+ The idea here is that userspace might hopefully be able to use
+ this recognize specific tuners. It will encode a serial number,
+ if available. */
+ char identifier[32];
+
+ /* I2C stuff */
+ struct i2c_adapter i2c_adap;
+ struct i2c_algorithm i2c_algo;
+ pvr2_i2c_func i2c_func[PVR2_I2C_FUNC_CNT];
+ int i2c_cx25840_hack_state;
+ int i2c_linked;
+
+ /* IR related */
+ unsigned int ir_scheme_active; /* IR scheme as seen from the outside */
+ struct IR_i2c_init_data ir_init_data; /* params passed to IR modules */
+
+ /* Frequency table */
+ unsigned int freqTable[FREQTABLE_SIZE];
+ unsigned int freqProgSlot;
+
+ /* Stuff for handling low level control interaction with device */
+ struct mutex ctl_lock_mutex;
+ int ctl_lock_held; /* For debugging */
+ struct urb *ctl_write_urb;
+ struct urb *ctl_read_urb;
+ unsigned char *ctl_write_buffer;
+ unsigned char *ctl_read_buffer;
+ int ctl_write_pend_flag;
+ int ctl_read_pend_flag;
+ int ctl_timeout_flag;
+ struct completion ctl_done;
+ unsigned char cmd_buffer[PVR2_CTL_BUFFSIZE];
+ int cmd_debug_state; // Low level command debugging info
+ unsigned char cmd_debug_code; //
+ unsigned int cmd_debug_write_len; //
+ unsigned int cmd_debug_read_len; //
+
+ /* Bits of state that describe what is going on with various parts
+ of the driver. */
+ int state_pathway_ok; /* Pathway config is ok */
+ int state_encoder_ok; /* Encoder is operational */
+ int state_encoder_run; /* Encoder is running */
+ int state_encoder_config; /* Encoder is configured */
+ int state_encoder_waitok; /* Encoder pre-wait done */
+ int state_encoder_runok; /* Encoder has run for >= .25 sec */
+ int state_decoder_run; /* Decoder is running */
+ int state_decoder_ready; /* Decoder is stabilized & streamable */
+ int state_usbstream_run; /* FX2 is streaming */
+ int state_decoder_quiescent; /* Decoder idle for minimal interval */
+ int state_pipeline_config; /* Pipeline is configured */
+ int state_pipeline_req; /* Somebody wants to stream */
+ int state_pipeline_pause; /* Pipeline must be paused */
+ int state_pipeline_idle; /* Pipeline not running */
+
+ /* This is the master state of the driver. It is the combined
+ result of other bits of state. Examining this will indicate the
+ overall state of the driver. Values here are one of
+ PVR2_STATE_xxxx */
+ unsigned int master_state;
+
+ /* True if device led is currently on */
+ int led_on;
+
+ /* True if states must be re-evaluated */
+ int state_stale;
+
+ void (*state_func)(void *);
+ void *state_data;
+
+ /* Timer for measuring required decoder settling time before we're
+ allowed to fire it up again. */
+ struct timer_list quiescent_timer;
+
+ /* Timer for measuring decoder stabilization time, which is the
+ amount of time we need to let the decoder run before we can
+ trust its output (otherwise the encoder might see garbage and
+ then fail to start correctly). */
+ struct timer_list decoder_stabilization_timer;
+
+ /* Timer for measuring encoder pre-wait time */
+ struct timer_list encoder_wait_timer;
+
+ /* Timer for measuring encoder minimum run time */
+ struct timer_list encoder_run_timer;
+
+ /* Place to block while waiting for state changes */
+ wait_queue_head_t state_wait_data;
+
+
+ int force_dirty; /* consider all controls dirty if true */
+ int flag_ok; /* device in known good state */
+ int flag_modulefail; /* true if at least one module failed to load */
+ int flag_disconnected; /* flag_ok == 0 due to disconnect */
+ int flag_init_ok; /* true if structure is fully initialized */
+ int fw1_state; /* current situation with fw1 */
+ int pathway_state; /* one of PVR2_PATHWAY_xxx */
+ int flag_decoder_missed;/* We've noticed missing decoder */
+ int flag_tripped; /* Indicates overall failure to start */
+
+ unsigned int decoder_client_id;
+
+ // CPU firmware info (used to help find / save firmware data)
+ char *fw_buffer;
+ unsigned int fw_size;
+ int fw_cpu_flag; /* True if we are dealing with the CPU */
+
+ /* Tuner / frequency control stuff */
+ unsigned int tuner_type;
+ int tuner_updated;
+ unsigned int freqValTelevision; /* Current freq for tv mode */
+ unsigned int freqValRadio; /* Current freq for radio mode */
+ unsigned int freqSlotTelevision; /* Current slot for tv mode */
+ unsigned int freqSlotRadio; /* Current slot for radio mode */
+ unsigned int freqSelector; /* 0=radio 1=television */
+ int freqDirty;
+
+ /* Current tuner info - this information is polled from the I2C bus */
+ struct v4l2_tuner tuner_signal_info;
+ int tuner_signal_stale;
+
+ /* Cropping capability info */
+ struct v4l2_cropcap cropcap_info;
+ int cropcap_stale;
+
+ /* Video standard handling */
+ v4l2_std_id std_mask_eeprom; // Hardware supported selections
+ v4l2_std_id std_mask_avail; // Which standards we may select from
+ v4l2_std_id std_mask_cur; // Currently selected standard(s)
+ unsigned int std_enum_cnt; // # of enumerated standards
+ int std_enum_cur; // selected standard enumeration value
+ int std_dirty; // True if std_mask_cur has changed
+ struct pvr2_ctl_info std_info_enum;
+ struct pvr2_ctl_info std_info_avail;
+ struct pvr2_ctl_info std_info_cur;
+ struct v4l2_standard *std_defs;
+ const char **std_enum_names;
+
+ // Generated string names, one per actual V4L2 standard
+ const char *std_mask_ptrs[32];
+ char std_mask_names[32][10];
+
+ int unit_number; /* ID for driver instance */
+ unsigned long serial_number; /* ID for hardware itself */
+
+ char bus_info[32]; /* Bus location info */
+
+ /* Minor numbers used by v4l logic (yes, this is a hack, as there
+ should be no v4l junk here). Probably a better way to do this. */
+ int v4l_minor_number_video;
+ int v4l_minor_number_vbi;
+ int v4l_minor_number_radio;
+
+ /* Bit mask of PVR2_CVAL_INPUT choices which are valid for the hardware */
+ unsigned int input_avail_mask;
+ /* Bit mask of PVR2_CVAL_INPUT choices which are currently allowed */
+ unsigned int input_allowed_mask;
+
+ /* Location of eeprom or a negative number if none */
+ int eeprom_addr;
+
+ enum pvr2_config active_stream_type;
+ enum pvr2_config desired_stream_type;
+
+ /* Control state needed for cx2341x module */
+ struct cx2341x_mpeg_params enc_cur_state;
+ struct cx2341x_mpeg_params enc_ctl_state;
+ /* True if an encoder attribute has changed */
+ int enc_stale;
+ /* True if an unsafe encoder attribute has changed */
+ int enc_unsafe_stale;
+ /* True if enc_cur_state is valid */
+ int enc_cur_valid;
+
+ /* Control state */
+#define VCREATE_DATA(lab) int lab##_val; int lab##_dirty
+ VCREATE_DATA(brightness);
+ VCREATE_DATA(contrast);
+ VCREATE_DATA(saturation);
+ VCREATE_DATA(hue);
+ VCREATE_DATA(volume);
+ VCREATE_DATA(balance);
+ VCREATE_DATA(bass);
+ VCREATE_DATA(treble);
+ VCREATE_DATA(mute);
+ VCREATE_DATA(cropl);
+ VCREATE_DATA(cropt);
+ VCREATE_DATA(cropw);
+ VCREATE_DATA(croph);
+ VCREATE_DATA(input);
+ VCREATE_DATA(audiomode);
+ VCREATE_DATA(res_hor);
+ VCREATE_DATA(res_ver);
+ VCREATE_DATA(srate);
+#undef VCREATE_DATA
+
+ struct pvr2_ctld_info *mpeg_ctrl_info;
+
+ struct pvr2_ctrl *controls;
+ unsigned int control_cnt;
+};
+
+/* This function gets the current frequency */
+unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *);
+
+void pvr2_hdw_status_poll(struct pvr2_hdw *);
+
+#endif /* __PVRUSB2_HDW_INTERNAL_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
new file mode 100644
index 00000000..ebc2c7e3
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
@@ -0,0 +1,5331 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/tuner.h>
+#include "pvrusb2.h"
+#include "pvrusb2-std.h"
+#include "pvrusb2-util.h"
+#include "pvrusb2-hdw.h"
+#include "pvrusb2-i2c-core.h"
+#include "pvrusb2-eeprom.h"
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-encoder.h"
+#include "pvrusb2-debug.h"
+#include "pvrusb2-fx2-cmd.h"
+#include "pvrusb2-wm8775.h"
+#include "pvrusb2-video-v4l.h"
+#include "pvrusb2-cx2584x-v4l.h"
+#include "pvrusb2-cs53l32a.h"
+#include "pvrusb2-audio.h"
+
+#define TV_MIN_FREQ 55250000L
+#define TV_MAX_FREQ 850000000L
+
+/* This defines a minimum interval that the decoder must remain quiet
+ before we are allowed to start it running. */
+#define TIME_MSEC_DECODER_WAIT 50
+
+/* This defines a minimum interval that the decoder must be allowed to run
+ before we can safely begin using its streaming output. */
+#define TIME_MSEC_DECODER_STABILIZATION_WAIT 300
+
+/* This defines a minimum interval that the encoder must remain quiet
+ before we are allowed to configure it. */
+#define TIME_MSEC_ENCODER_WAIT 50
+
+/* This defines the minimum interval that the encoder must successfully run
+ before we consider that the encoder has run at least once since its
+ firmware has been loaded. This measurement is in important for cases
+ where we can't do something until we know that the encoder has been run
+ at least once. */
+#define TIME_MSEC_ENCODER_OK 250
+
+static struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = NULL};
+static DEFINE_MUTEX(pvr2_unit_mtx);
+
+static int ctlchg;
+static int procreload;
+static int tuner[PVR_NUM] = { [0 ... PVR_NUM-1] = -1 };
+static int tolerance[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 };
+static int video_std[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 };
+static int init_pause_msec;
+
+module_param(ctlchg, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(ctlchg, "0=optimize ctl change 1=always accept new ctl value");
+module_param(init_pause_msec, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(init_pause_msec, "hardware initialization settling delay");
+module_param(procreload, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(procreload,
+ "Attempt init failure recovery with firmware reload");
+module_param_array(tuner, int, NULL, 0444);
+MODULE_PARM_DESC(tuner,"specify installed tuner type");
+module_param_array(video_std, int, NULL, 0444);
+MODULE_PARM_DESC(video_std,"specify initial video standard");
+module_param_array(tolerance, int, NULL, 0444);
+MODULE_PARM_DESC(tolerance,"specify stream error tolerance");
+
+/* US Broadcast channel 3 (61.25 MHz), to help with testing */
+static int default_tv_freq = 61250000L;
+/* 104.3 MHz, a usable FM station for my area */
+static int default_radio_freq = 104300000L;
+
+module_param_named(tv_freq, default_tv_freq, int, 0444);
+MODULE_PARM_DESC(tv_freq, "specify initial television frequency");
+module_param_named(radio_freq, default_radio_freq, int, 0444);
+MODULE_PARM_DESC(radio_freq, "specify initial radio frequency");
+
+#define PVR2_CTL_WRITE_ENDPOINT 0x01
+#define PVR2_CTL_READ_ENDPOINT 0x81
+
+#define PVR2_GPIO_IN 0x9008
+#define PVR2_GPIO_OUT 0x900c
+#define PVR2_GPIO_DIR 0x9020
+
+#define trace_firmware(...) pvr2_trace(PVR2_TRACE_FIRMWARE,__VA_ARGS__)
+
+#define PVR2_FIRMWARE_ENDPOINT 0x02
+
+/* size of a firmware chunk */
+#define FIRMWARE_CHUNK_SIZE 0x2000
+
+typedef void (*pvr2_subdev_update_func)(struct pvr2_hdw *,
+ struct v4l2_subdev *);
+
+static const pvr2_subdev_update_func pvr2_module_update_functions[] = {
+ [PVR2_CLIENT_ID_WM8775] = pvr2_wm8775_subdev_update,
+ [PVR2_CLIENT_ID_SAA7115] = pvr2_saa7115_subdev_update,
+ [PVR2_CLIENT_ID_MSP3400] = pvr2_msp3400_subdev_update,
+ [PVR2_CLIENT_ID_CX25840] = pvr2_cx25840_subdev_update,
+ [PVR2_CLIENT_ID_CS53L32A] = pvr2_cs53l32a_subdev_update,
+};
+
+static const char *module_names[] = {
+ [PVR2_CLIENT_ID_MSP3400] = "msp3400",
+ [PVR2_CLIENT_ID_CX25840] = "cx25840",
+ [PVR2_CLIENT_ID_SAA7115] = "saa7115",
+ [PVR2_CLIENT_ID_TUNER] = "tuner",
+ [PVR2_CLIENT_ID_DEMOD] = "tuner",
+ [PVR2_CLIENT_ID_CS53L32A] = "cs53l32a",
+ [PVR2_CLIENT_ID_WM8775] = "wm8775",
+};
+
+
+static const unsigned char *module_i2c_addresses[] = {
+ [PVR2_CLIENT_ID_TUNER] = "\x60\x61\x62\x63",
+ [PVR2_CLIENT_ID_DEMOD] = "\x43",
+ [PVR2_CLIENT_ID_MSP3400] = "\x40",
+ [PVR2_CLIENT_ID_SAA7115] = "\x21",
+ [PVR2_CLIENT_ID_WM8775] = "\x1b",
+ [PVR2_CLIENT_ID_CX25840] = "\x44",
+ [PVR2_CLIENT_ID_CS53L32A] = "\x11",
+};
+
+
+static const char *ir_scheme_names[] = {
+ [PVR2_IR_SCHEME_NONE] = "none",
+ [PVR2_IR_SCHEME_29XXX] = "29xxx",
+ [PVR2_IR_SCHEME_24XXX] = "24xxx (29xxx emulation)",
+ [PVR2_IR_SCHEME_24XXX_MCE] = "24xxx (MCE device)",
+ [PVR2_IR_SCHEME_ZILOG] = "Zilog",
+};
+
+
+/* Define the list of additional controls we'll dynamically construct based
+ on query of the cx2341x module. */
+struct pvr2_mpeg_ids {
+ const char *strid;
+ int id;
+};
+static const struct pvr2_mpeg_ids mpeg_ids[] = {
+ {
+ .strid = "audio_layer",
+ .id = V4L2_CID_MPEG_AUDIO_ENCODING,
+ },{
+ .strid = "audio_bitrate",
+ .id = V4L2_CID_MPEG_AUDIO_L2_BITRATE,
+ },{
+ /* Already using audio_mode elsewhere :-( */
+ .strid = "mpeg_audio_mode",
+ .id = V4L2_CID_MPEG_AUDIO_MODE,
+ },{
+ .strid = "mpeg_audio_mode_extension",
+ .id = V4L2_CID_MPEG_AUDIO_MODE_EXTENSION,
+ },{
+ .strid = "audio_emphasis",
+ .id = V4L2_CID_MPEG_AUDIO_EMPHASIS,
+ },{
+ .strid = "audio_crc",
+ .id = V4L2_CID_MPEG_AUDIO_CRC,
+ },{
+ .strid = "video_aspect",
+ .id = V4L2_CID_MPEG_VIDEO_ASPECT,
+ },{
+ .strid = "video_b_frames",
+ .id = V4L2_CID_MPEG_VIDEO_B_FRAMES,
+ },{
+ .strid = "video_gop_size",
+ .id = V4L2_CID_MPEG_VIDEO_GOP_SIZE,
+ },{
+ .strid = "video_gop_closure",
+ .id = V4L2_CID_MPEG_VIDEO_GOP_CLOSURE,
+ },{
+ .strid = "video_bitrate_mode",
+ .id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+ },{
+ .strid = "video_bitrate",
+ .id = V4L2_CID_MPEG_VIDEO_BITRATE,
+ },{
+ .strid = "video_bitrate_peak",
+ .id = V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
+ },{
+ .strid = "video_temporal_decimation",
+ .id = V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION,
+ },{
+ .strid = "stream_type",
+ .id = V4L2_CID_MPEG_STREAM_TYPE,
+ },{
+ .strid = "video_spatial_filter_mode",
+ .id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE,
+ },{
+ .strid = "video_spatial_filter",
+ .id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER,
+ },{
+ .strid = "video_luma_spatial_filter_type",
+ .id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE,
+ },{
+ .strid = "video_chroma_spatial_filter_type",
+ .id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE,
+ },{
+ .strid = "video_temporal_filter_mode",
+ .id = V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE,
+ },{
+ .strid = "video_temporal_filter",
+ .id = V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER,
+ },{
+ .strid = "video_median_filter_type",
+ .id = V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE,
+ },{
+ .strid = "video_luma_median_filter_top",
+ .id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP,
+ },{
+ .strid = "video_luma_median_filter_bottom",
+ .id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM,
+ },{
+ .strid = "video_chroma_median_filter_top",
+ .id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP,
+ },{
+ .strid = "video_chroma_median_filter_bottom",
+ .id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM,
+ }
+};
+#define MPEGDEF_COUNT ARRAY_SIZE(mpeg_ids)
+
+
+static const char *control_values_srate[] = {
+ [V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100] = "44.1 kHz",
+ [V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000] = "48 kHz",
+ [V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000] = "32 kHz",
+};
+
+
+
+static const char *control_values_input[] = {
+ [PVR2_CVAL_INPUT_TV] = "television", /*xawtv needs this name*/
+ [PVR2_CVAL_INPUT_DTV] = "dtv",
+ [PVR2_CVAL_INPUT_RADIO] = "radio",
+ [PVR2_CVAL_INPUT_SVIDEO] = "s-video",
+ [PVR2_CVAL_INPUT_COMPOSITE] = "composite",
+};
+
+
+static const char *control_values_audiomode[] = {
+ [V4L2_TUNER_MODE_MONO] = "Mono",
+ [V4L2_TUNER_MODE_STEREO] = "Stereo",
+ [V4L2_TUNER_MODE_LANG1] = "Lang1",
+ [V4L2_TUNER_MODE_LANG2] = "Lang2",
+ [V4L2_TUNER_MODE_LANG1_LANG2] = "Lang1+Lang2",
+};
+
+
+static const char *control_values_hsm[] = {
+ [PVR2_CVAL_HSM_FAIL] = "Fail",
+ [PVR2_CVAL_HSM_HIGH] = "High",
+ [PVR2_CVAL_HSM_FULL] = "Full",
+};
+
+
+static const char *pvr2_state_names[] = {
+ [PVR2_STATE_NONE] = "none",
+ [PVR2_STATE_DEAD] = "dead",
+ [PVR2_STATE_COLD] = "cold",
+ [PVR2_STATE_WARM] = "warm",
+ [PVR2_STATE_ERROR] = "error",
+ [PVR2_STATE_READY] = "ready",
+ [PVR2_STATE_RUN] = "run",
+};
+
+
+struct pvr2_fx2cmd_descdef {
+ unsigned char id;
+ unsigned char *desc;
+};
+
+static const struct pvr2_fx2cmd_descdef pvr2_fx2cmd_desc[] = {
+ {FX2CMD_MEM_WRITE_DWORD, "write encoder dword"},
+ {FX2CMD_MEM_READ_DWORD, "read encoder dword"},
+ {FX2CMD_HCW_ZILOG_RESET, "zilog IR reset control"},
+ {FX2CMD_MEM_READ_64BYTES, "read encoder 64bytes"},
+ {FX2CMD_REG_WRITE, "write encoder register"},
+ {FX2CMD_REG_READ, "read encoder register"},
+ {FX2CMD_MEMSEL, "encoder memsel"},
+ {FX2CMD_I2C_WRITE, "i2c write"},
+ {FX2CMD_I2C_READ, "i2c read"},
+ {FX2CMD_GET_USB_SPEED, "get USB speed"},
+ {FX2CMD_STREAMING_ON, "stream on"},
+ {FX2CMD_STREAMING_OFF, "stream off"},
+ {FX2CMD_FWPOST1, "fwpost1"},
+ {FX2CMD_POWER_OFF, "power off"},
+ {FX2CMD_POWER_ON, "power on"},
+ {FX2CMD_DEEP_RESET, "deep reset"},
+ {FX2CMD_GET_EEPROM_ADDR, "get rom addr"},
+ {FX2CMD_GET_IR_CODE, "get IR code"},
+ {FX2CMD_HCW_DEMOD_RESETIN, "hcw demod resetin"},
+ {FX2CMD_HCW_DTV_STREAMING_ON, "hcw dtv stream on"},
+ {FX2CMD_HCW_DTV_STREAMING_OFF, "hcw dtv stream off"},
+ {FX2CMD_ONAIR_DTV_STREAMING_ON, "onair dtv stream on"},
+ {FX2CMD_ONAIR_DTV_STREAMING_OFF, "onair dtv stream off"},
+ {FX2CMD_ONAIR_DTV_POWER_ON, "onair dtv power on"},
+ {FX2CMD_ONAIR_DTV_POWER_OFF, "onair dtv power off"},
+};
+
+
+static int pvr2_hdw_set_input(struct pvr2_hdw *hdw,int v);
+static void pvr2_hdw_state_sched(struct pvr2_hdw *);
+static int pvr2_hdw_state_eval(struct pvr2_hdw *);
+static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *,unsigned long);
+static void pvr2_hdw_worker_poll(struct work_struct *work);
+static int pvr2_hdw_wait(struct pvr2_hdw *,int state);
+static int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *);
+static void pvr2_hdw_state_log_state(struct pvr2_hdw *);
+static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl);
+static int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw);
+static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw);
+static void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw);
+static void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw);
+static void pvr2_hdw_quiescent_timeout(unsigned long);
+static void pvr2_hdw_decoder_stabilization_timeout(unsigned long);
+static void pvr2_hdw_encoder_wait_timeout(unsigned long);
+static void pvr2_hdw_encoder_run_timeout(unsigned long);
+static int pvr2_issue_simple_cmd(struct pvr2_hdw *,u32);
+static int pvr2_send_request_ex(struct pvr2_hdw *hdw,
+ unsigned int timeout,int probe_fl,
+ void *write_data,unsigned int write_len,
+ void *read_data,unsigned int read_len);
+static int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw);
+
+
+static void trace_stbit(const char *name,int val)
+{
+ pvr2_trace(PVR2_TRACE_STBITS,
+ "State bit %s <-- %s",
+ name,(val ? "true" : "false"));
+}
+
+static int ctrl_channelfreq_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ struct pvr2_hdw *hdw = cptr->hdw;
+ if ((hdw->freqProgSlot > 0) && (hdw->freqProgSlot <= FREQTABLE_SIZE)) {
+ *vp = hdw->freqTable[hdw->freqProgSlot-1];
+ } else {
+ *vp = 0;
+ }
+ return 0;
+}
+
+static int ctrl_channelfreq_set(struct pvr2_ctrl *cptr,int m,int v)
+{
+ struct pvr2_hdw *hdw = cptr->hdw;
+ unsigned int slotId = hdw->freqProgSlot;
+ if ((slotId > 0) && (slotId <= FREQTABLE_SIZE)) {
+ hdw->freqTable[slotId-1] = v;
+ /* Handle side effects correctly - if we're tuned to this
+ slot, then forgot the slot id relation since the stored
+ frequency has been changed. */
+ if (hdw->freqSelector) {
+ if (hdw->freqSlotRadio == slotId) {
+ hdw->freqSlotRadio = 0;
+ }
+ } else {
+ if (hdw->freqSlotTelevision == slotId) {
+ hdw->freqSlotTelevision = 0;
+ }
+ }
+ }
+ return 0;
+}
+
+static int ctrl_channelprog_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ *vp = cptr->hdw->freqProgSlot;
+ return 0;
+}
+
+static int ctrl_channelprog_set(struct pvr2_ctrl *cptr,int m,int v)
+{
+ struct pvr2_hdw *hdw = cptr->hdw;
+ if ((v >= 0) && (v <= FREQTABLE_SIZE)) {
+ hdw->freqProgSlot = v;
+ }
+ return 0;
+}
+
+static int ctrl_channel_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ struct pvr2_hdw *hdw = cptr->hdw;
+ *vp = hdw->freqSelector ? hdw->freqSlotRadio : hdw->freqSlotTelevision;
+ return 0;
+}
+
+static int ctrl_channel_set(struct pvr2_ctrl *cptr,int m,int slotId)
+{
+ unsigned freq = 0;
+ struct pvr2_hdw *hdw = cptr->hdw;
+ if ((slotId < 0) || (slotId > FREQTABLE_SIZE)) return 0;
+ if (slotId > 0) {
+ freq = hdw->freqTable[slotId-1];
+ if (!freq) return 0;
+ pvr2_hdw_set_cur_freq(hdw,freq);
+ }
+ if (hdw->freqSelector) {
+ hdw->freqSlotRadio = slotId;
+ } else {
+ hdw->freqSlotTelevision = slotId;
+ }
+ return 0;
+}
+
+static int ctrl_freq_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ *vp = pvr2_hdw_get_cur_freq(cptr->hdw);
+ return 0;
+}
+
+static int ctrl_freq_is_dirty(struct pvr2_ctrl *cptr)
+{
+ return cptr->hdw->freqDirty != 0;
+}
+
+static void ctrl_freq_clear_dirty(struct pvr2_ctrl *cptr)
+{
+ cptr->hdw->freqDirty = 0;
+}
+
+static int ctrl_freq_set(struct pvr2_ctrl *cptr,int m,int v)
+{
+ pvr2_hdw_set_cur_freq(cptr->hdw,v);
+ return 0;
+}
+
+static int ctrl_cropl_min_get(struct pvr2_ctrl *cptr, int *left)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *left = cap->bounds.left;
+ return 0;
+}
+
+static int ctrl_cropl_max_get(struct pvr2_ctrl *cptr, int *left)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *left = cap->bounds.left;
+ if (cap->bounds.width > cptr->hdw->cropw_val) {
+ *left += cap->bounds.width - cptr->hdw->cropw_val;
+ }
+ return 0;
+}
+
+static int ctrl_cropt_min_get(struct pvr2_ctrl *cptr, int *top)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *top = cap->bounds.top;
+ return 0;
+}
+
+static int ctrl_cropt_max_get(struct pvr2_ctrl *cptr, int *top)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *top = cap->bounds.top;
+ if (cap->bounds.height > cptr->hdw->croph_val) {
+ *top += cap->bounds.height - cptr->hdw->croph_val;
+ }
+ return 0;
+}
+
+static int ctrl_cropw_max_get(struct pvr2_ctrl *cptr, int *width)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat, bleftend, cleft;
+
+ stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ bleftend = cap->bounds.left+cap->bounds.width;
+ cleft = cptr->hdw->cropl_val;
+
+ *width = cleft < bleftend ? bleftend-cleft : 0;
+ return 0;
+}
+
+static int ctrl_croph_max_get(struct pvr2_ctrl *cptr, int *height)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat, btopend, ctop;
+
+ stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ btopend = cap->bounds.top+cap->bounds.height;
+ ctop = cptr->hdw->cropt_val;
+
+ *height = ctop < btopend ? btopend-ctop : 0;
+ return 0;
+}
+
+static int ctrl_get_cropcapbl(struct pvr2_ctrl *cptr, int *val)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *val = cap->bounds.left;
+ return 0;
+}
+
+static int ctrl_get_cropcapbt(struct pvr2_ctrl *cptr, int *val)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *val = cap->bounds.top;
+ return 0;
+}
+
+static int ctrl_get_cropcapbw(struct pvr2_ctrl *cptr, int *val)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *val = cap->bounds.width;
+ return 0;
+}
+
+static int ctrl_get_cropcapbh(struct pvr2_ctrl *cptr, int *val)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *val = cap->bounds.height;
+ return 0;
+}
+
+static int ctrl_get_cropcapdl(struct pvr2_ctrl *cptr, int *val)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *val = cap->defrect.left;
+ return 0;
+}
+
+static int ctrl_get_cropcapdt(struct pvr2_ctrl *cptr, int *val)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *val = cap->defrect.top;
+ return 0;
+}
+
+static int ctrl_get_cropcapdw(struct pvr2_ctrl *cptr, int *val)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *val = cap->defrect.width;
+ return 0;
+}
+
+static int ctrl_get_cropcapdh(struct pvr2_ctrl *cptr, int *val)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *val = cap->defrect.height;
+ return 0;
+}
+
+static int ctrl_get_cropcappan(struct pvr2_ctrl *cptr, int *val)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *val = cap->pixelaspect.numerator;
+ return 0;
+}
+
+static int ctrl_get_cropcappad(struct pvr2_ctrl *cptr, int *val)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *val = cap->pixelaspect.denominator;
+ return 0;
+}
+
+static int ctrl_vres_max_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ /* Actual maximum depends on the video standard in effect. */
+ if (cptr->hdw->std_mask_cur & V4L2_STD_525_60) {
+ *vp = 480;
+ } else {
+ *vp = 576;
+ }
+ return 0;
+}
+
+static int ctrl_vres_min_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ /* Actual minimum depends on device digitizer type. */
+ if (cptr->hdw->hdw_desc->flag_has_cx25840) {
+ *vp = 75;
+ } else {
+ *vp = 17;
+ }
+ return 0;
+}
+
+static int ctrl_get_input(struct pvr2_ctrl *cptr,int *vp)
+{
+ *vp = cptr->hdw->input_val;
+ return 0;
+}
+
+static int ctrl_check_input(struct pvr2_ctrl *cptr,int v)
+{
+ return ((1 << v) & cptr->hdw->input_allowed_mask) != 0;
+}
+
+static int ctrl_set_input(struct pvr2_ctrl *cptr,int m,int v)
+{
+ return pvr2_hdw_set_input(cptr->hdw,v);
+}
+
+static int ctrl_isdirty_input(struct pvr2_ctrl *cptr)
+{
+ return cptr->hdw->input_dirty != 0;
+}
+
+static void ctrl_cleardirty_input(struct pvr2_ctrl *cptr)
+{
+ cptr->hdw->input_dirty = 0;
+}
+
+
+static int ctrl_freq_max_get(struct pvr2_ctrl *cptr, int *vp)
+{
+ unsigned long fv;
+ struct pvr2_hdw *hdw = cptr->hdw;
+ if (hdw->tuner_signal_stale) {
+ pvr2_hdw_status_poll(hdw);
+ }
+ fv = hdw->tuner_signal_info.rangehigh;
+ if (!fv) {
+ /* Safety fallback */
+ *vp = TV_MAX_FREQ;
+ return 0;
+ }
+ if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) {
+ fv = (fv * 125) / 2;
+ } else {
+ fv = fv * 62500;
+ }
+ *vp = fv;
+ return 0;
+}
+
+static int ctrl_freq_min_get(struct pvr2_ctrl *cptr, int *vp)
+{
+ unsigned long fv;
+ struct pvr2_hdw *hdw = cptr->hdw;
+ if (hdw->tuner_signal_stale) {
+ pvr2_hdw_status_poll(hdw);
+ }
+ fv = hdw->tuner_signal_info.rangelow;
+ if (!fv) {
+ /* Safety fallback */
+ *vp = TV_MIN_FREQ;
+ return 0;
+ }
+ if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) {
+ fv = (fv * 125) / 2;
+ } else {
+ fv = fv * 62500;
+ }
+ *vp = fv;
+ return 0;
+}
+
+static int ctrl_cx2341x_is_dirty(struct pvr2_ctrl *cptr)
+{
+ return cptr->hdw->enc_stale != 0;
+}
+
+static void ctrl_cx2341x_clear_dirty(struct pvr2_ctrl *cptr)
+{
+ cptr->hdw->enc_stale = 0;
+ cptr->hdw->enc_unsafe_stale = 0;
+}
+
+static int ctrl_cx2341x_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ int ret;
+ struct v4l2_ext_controls cs;
+ struct v4l2_ext_control c1;
+ memset(&cs,0,sizeof(cs));
+ memset(&c1,0,sizeof(c1));
+ cs.controls = &c1;
+ cs.count = 1;
+ c1.id = cptr->info->v4l_id;
+ ret = cx2341x_ext_ctrls(&cptr->hdw->enc_ctl_state, 0, &cs,
+ VIDIOC_G_EXT_CTRLS);
+ if (ret) return ret;
+ *vp = c1.value;
+ return 0;
+}
+
+static int ctrl_cx2341x_set(struct pvr2_ctrl *cptr,int m,int v)
+{
+ int ret;
+ struct pvr2_hdw *hdw = cptr->hdw;
+ struct v4l2_ext_controls cs;
+ struct v4l2_ext_control c1;
+ memset(&cs,0,sizeof(cs));
+ memset(&c1,0,sizeof(c1));
+ cs.controls = &c1;
+ cs.count = 1;
+ c1.id = cptr->info->v4l_id;
+ c1.value = v;
+ ret = cx2341x_ext_ctrls(&hdw->enc_ctl_state,
+ hdw->state_encoder_run, &cs,
+ VIDIOC_S_EXT_CTRLS);
+ if (ret == -EBUSY) {
+ /* Oops. cx2341x is telling us it's not safe to change
+ this control while we're capturing. Make a note of this
+ fact so that the pipeline will be stopped the next time
+ controls are committed. Then go on ahead and store this
+ change anyway. */
+ ret = cx2341x_ext_ctrls(&hdw->enc_ctl_state,
+ 0, &cs,
+ VIDIOC_S_EXT_CTRLS);
+ if (!ret) hdw->enc_unsafe_stale = !0;
+ }
+ if (ret) return ret;
+ hdw->enc_stale = !0;
+ return 0;
+}
+
+static unsigned int ctrl_cx2341x_getv4lflags(struct pvr2_ctrl *cptr)
+{
+ struct v4l2_queryctrl qctrl;
+ struct pvr2_ctl_info *info;
+ qctrl.id = cptr->info->v4l_id;
+ cx2341x_ctrl_query(&cptr->hdw->enc_ctl_state,&qctrl);
+ /* Strip out the const so we can adjust a function pointer. It's
+ OK to do this here because we know this is a dynamically created
+ control, so the underlying storage for the info pointer is (a)
+ private to us, and (b) not in read-only storage. Either we do
+ this or we significantly complicate the underlying control
+ implementation. */
+ info = (struct pvr2_ctl_info *)(cptr->info);
+ if (qctrl.flags & V4L2_CTRL_FLAG_READ_ONLY) {
+ if (info->set_value) {
+ info->set_value = NULL;
+ }
+ } else {
+ if (!(info->set_value)) {
+ info->set_value = ctrl_cx2341x_set;
+ }
+ }
+ return qctrl.flags;
+}
+
+static int ctrl_streamingenabled_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ *vp = cptr->hdw->state_pipeline_req;
+ return 0;
+}
+
+static int ctrl_masterstate_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ *vp = cptr->hdw->master_state;
+ return 0;
+}
+
+static int ctrl_hsm_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ int result = pvr2_hdw_is_hsm(cptr->hdw);
+ *vp = PVR2_CVAL_HSM_FULL;
+ if (result < 0) *vp = PVR2_CVAL_HSM_FAIL;
+ if (result) *vp = PVR2_CVAL_HSM_HIGH;
+ return 0;
+}
+
+static int ctrl_stdavail_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ *vp = cptr->hdw->std_mask_avail;
+ return 0;
+}
+
+static int ctrl_stdavail_set(struct pvr2_ctrl *cptr,int m,int v)
+{
+ struct pvr2_hdw *hdw = cptr->hdw;
+ v4l2_std_id ns;
+ ns = hdw->std_mask_avail;
+ ns = (ns & ~m) | (v & m);
+ if (ns == hdw->std_mask_avail) return 0;
+ hdw->std_mask_avail = ns;
+ pvr2_hdw_internal_set_std_avail(hdw);
+ pvr2_hdw_internal_find_stdenum(hdw);
+ return 0;
+}
+
+static int ctrl_std_val_to_sym(struct pvr2_ctrl *cptr,int msk,int val,
+ char *bufPtr,unsigned int bufSize,
+ unsigned int *len)
+{
+ *len = pvr2_std_id_to_str(bufPtr,bufSize,msk & val);
+ return 0;
+}
+
+static int ctrl_std_sym_to_val(struct pvr2_ctrl *cptr,
+ const char *bufPtr,unsigned int bufSize,
+ int *mskp,int *valp)
+{
+ int ret;
+ v4l2_std_id id;
+ ret = pvr2_std_str_to_id(&id,bufPtr,bufSize);
+ if (ret < 0) return ret;
+ if (mskp) *mskp = id;
+ if (valp) *valp = id;
+ return 0;
+}
+
+static int ctrl_stdcur_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ *vp = cptr->hdw->std_mask_cur;
+ return 0;
+}
+
+static int ctrl_stdcur_set(struct pvr2_ctrl *cptr,int m,int v)
+{
+ struct pvr2_hdw *hdw = cptr->hdw;
+ v4l2_std_id ns;
+ ns = hdw->std_mask_cur;
+ ns = (ns & ~m) | (v & m);
+ if (ns == hdw->std_mask_cur) return 0;
+ hdw->std_mask_cur = ns;
+ hdw->std_dirty = !0;
+ pvr2_hdw_internal_find_stdenum(hdw);
+ return 0;
+}
+
+static int ctrl_stdcur_is_dirty(struct pvr2_ctrl *cptr)
+{
+ return cptr->hdw->std_dirty != 0;
+}
+
+static void ctrl_stdcur_clear_dirty(struct pvr2_ctrl *cptr)
+{
+ cptr->hdw->std_dirty = 0;
+}
+
+static int ctrl_signal_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ struct pvr2_hdw *hdw = cptr->hdw;
+ pvr2_hdw_status_poll(hdw);
+ *vp = hdw->tuner_signal_info.signal;
+ return 0;
+}
+
+static int ctrl_audio_modes_present_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ int val = 0;
+ unsigned int subchan;
+ struct pvr2_hdw *hdw = cptr->hdw;
+ pvr2_hdw_status_poll(hdw);
+ subchan = hdw->tuner_signal_info.rxsubchans;
+ if (subchan & V4L2_TUNER_SUB_MONO) {
+ val |= (1 << V4L2_TUNER_MODE_MONO);
+ }
+ if (subchan & V4L2_TUNER_SUB_STEREO) {
+ val |= (1 << V4L2_TUNER_MODE_STEREO);
+ }
+ if (subchan & V4L2_TUNER_SUB_LANG1) {
+ val |= (1 << V4L2_TUNER_MODE_LANG1);
+ }
+ if (subchan & V4L2_TUNER_SUB_LANG2) {
+ val |= (1 << V4L2_TUNER_MODE_LANG2);
+ }
+ *vp = val;
+ return 0;
+}
+
+
+static int ctrl_stdenumcur_set(struct pvr2_ctrl *cptr,int m,int v)
+{
+ struct pvr2_hdw *hdw = cptr->hdw;
+ if (v < 0) return -EINVAL;
+ if (v > hdw->std_enum_cnt) return -EINVAL;
+ hdw->std_enum_cur = v;
+ if (!v) return 0;
+ v--;
+ if (hdw->std_mask_cur == hdw->std_defs[v].id) return 0;
+ hdw->std_mask_cur = hdw->std_defs[v].id;
+ hdw->std_dirty = !0;
+ return 0;
+}
+
+
+static int ctrl_stdenumcur_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ *vp = cptr->hdw->std_enum_cur;
+ return 0;
+}
+
+
+static int ctrl_stdenumcur_is_dirty(struct pvr2_ctrl *cptr)
+{
+ return cptr->hdw->std_dirty != 0;
+}
+
+
+static void ctrl_stdenumcur_clear_dirty(struct pvr2_ctrl *cptr)
+{
+ cptr->hdw->std_dirty = 0;
+}
+
+
+#define DEFINT(vmin,vmax) \
+ .type = pvr2_ctl_int, \
+ .def.type_int.min_value = vmin, \
+ .def.type_int.max_value = vmax
+
+#define DEFENUM(tab) \
+ .type = pvr2_ctl_enum, \
+ .def.type_enum.count = ARRAY_SIZE(tab), \
+ .def.type_enum.value_names = tab
+
+#define DEFBOOL \
+ .type = pvr2_ctl_bool
+
+#define DEFMASK(msk,tab) \
+ .type = pvr2_ctl_bitmask, \
+ .def.type_bitmask.valid_bits = msk, \
+ .def.type_bitmask.bit_names = tab
+
+#define DEFREF(vname) \
+ .set_value = ctrl_set_##vname, \
+ .get_value = ctrl_get_##vname, \
+ .is_dirty = ctrl_isdirty_##vname, \
+ .clear_dirty = ctrl_cleardirty_##vname
+
+
+#define VCREATE_FUNCS(vname) \
+static int ctrl_get_##vname(struct pvr2_ctrl *cptr,int *vp) \
+{*vp = cptr->hdw->vname##_val; return 0;} \
+static int ctrl_set_##vname(struct pvr2_ctrl *cptr,int m,int v) \
+{cptr->hdw->vname##_val = v; cptr->hdw->vname##_dirty = !0; return 0;} \
+static int ctrl_isdirty_##vname(struct pvr2_ctrl *cptr) \
+{return cptr->hdw->vname##_dirty != 0;} \
+static void ctrl_cleardirty_##vname(struct pvr2_ctrl *cptr) \
+{cptr->hdw->vname##_dirty = 0;}
+
+VCREATE_FUNCS(brightness)
+VCREATE_FUNCS(contrast)
+VCREATE_FUNCS(saturation)
+VCREATE_FUNCS(hue)
+VCREATE_FUNCS(volume)
+VCREATE_FUNCS(balance)
+VCREATE_FUNCS(bass)
+VCREATE_FUNCS(treble)
+VCREATE_FUNCS(mute)
+VCREATE_FUNCS(cropl)
+VCREATE_FUNCS(cropt)
+VCREATE_FUNCS(cropw)
+VCREATE_FUNCS(croph)
+VCREATE_FUNCS(audiomode)
+VCREATE_FUNCS(res_hor)
+VCREATE_FUNCS(res_ver)
+VCREATE_FUNCS(srate)
+
+/* Table definition of all controls which can be manipulated */
+static const struct pvr2_ctl_info control_defs[] = {
+ {
+ .v4l_id = V4L2_CID_BRIGHTNESS,
+ .desc = "Brightness",
+ .name = "brightness",
+ .default_value = 128,
+ DEFREF(brightness),
+ DEFINT(0,255),
+ },{
+ .v4l_id = V4L2_CID_CONTRAST,
+ .desc = "Contrast",
+ .name = "contrast",
+ .default_value = 68,
+ DEFREF(contrast),
+ DEFINT(0,127),
+ },{
+ .v4l_id = V4L2_CID_SATURATION,
+ .desc = "Saturation",
+ .name = "saturation",
+ .default_value = 64,
+ DEFREF(saturation),
+ DEFINT(0,127),
+ },{
+ .v4l_id = V4L2_CID_HUE,
+ .desc = "Hue",
+ .name = "hue",
+ .default_value = 0,
+ DEFREF(hue),
+ DEFINT(-128,127),
+ },{
+ .v4l_id = V4L2_CID_AUDIO_VOLUME,
+ .desc = "Volume",
+ .name = "volume",
+ .default_value = 62000,
+ DEFREF(volume),
+ DEFINT(0,65535),
+ },{
+ .v4l_id = V4L2_CID_AUDIO_BALANCE,
+ .desc = "Balance",
+ .name = "balance",
+ .default_value = 0,
+ DEFREF(balance),
+ DEFINT(-32768,32767),
+ },{
+ .v4l_id = V4L2_CID_AUDIO_BASS,
+ .desc = "Bass",
+ .name = "bass",
+ .default_value = 0,
+ DEFREF(bass),
+ DEFINT(-32768,32767),
+ },{
+ .v4l_id = V4L2_CID_AUDIO_TREBLE,
+ .desc = "Treble",
+ .name = "treble",
+ .default_value = 0,
+ DEFREF(treble),
+ DEFINT(-32768,32767),
+ },{
+ .v4l_id = V4L2_CID_AUDIO_MUTE,
+ .desc = "Mute",
+ .name = "mute",
+ .default_value = 0,
+ DEFREF(mute),
+ DEFBOOL,
+ }, {
+ .desc = "Capture crop left margin",
+ .name = "crop_left",
+ .internal_id = PVR2_CID_CROPL,
+ .default_value = 0,
+ DEFREF(cropl),
+ DEFINT(-129, 340),
+ .get_min_value = ctrl_cropl_min_get,
+ .get_max_value = ctrl_cropl_max_get,
+ .get_def_value = ctrl_get_cropcapdl,
+ }, {
+ .desc = "Capture crop top margin",
+ .name = "crop_top",
+ .internal_id = PVR2_CID_CROPT,
+ .default_value = 0,
+ DEFREF(cropt),
+ DEFINT(-35, 544),
+ .get_min_value = ctrl_cropt_min_get,
+ .get_max_value = ctrl_cropt_max_get,
+ .get_def_value = ctrl_get_cropcapdt,
+ }, {
+ .desc = "Capture crop width",
+ .name = "crop_width",
+ .internal_id = PVR2_CID_CROPW,
+ .default_value = 720,
+ DEFREF(cropw),
+ DEFINT(0, 864),
+ .get_max_value = ctrl_cropw_max_get,
+ .get_def_value = ctrl_get_cropcapdw,
+ }, {
+ .desc = "Capture crop height",
+ .name = "crop_height",
+ .internal_id = PVR2_CID_CROPH,
+ .default_value = 480,
+ DEFREF(croph),
+ DEFINT(0, 576),
+ .get_max_value = ctrl_croph_max_get,
+ .get_def_value = ctrl_get_cropcapdh,
+ }, {
+ .desc = "Capture capability pixel aspect numerator",
+ .name = "cropcap_pixel_numerator",
+ .internal_id = PVR2_CID_CROPCAPPAN,
+ .get_value = ctrl_get_cropcappan,
+ }, {
+ .desc = "Capture capability pixel aspect denominator",
+ .name = "cropcap_pixel_denominator",
+ .internal_id = PVR2_CID_CROPCAPPAD,
+ .get_value = ctrl_get_cropcappad,
+ }, {
+ .desc = "Capture capability bounds top",
+ .name = "cropcap_bounds_top",
+ .internal_id = PVR2_CID_CROPCAPBT,
+ .get_value = ctrl_get_cropcapbt,
+ }, {
+ .desc = "Capture capability bounds left",
+ .name = "cropcap_bounds_left",
+ .internal_id = PVR2_CID_CROPCAPBL,
+ .get_value = ctrl_get_cropcapbl,
+ }, {
+ .desc = "Capture capability bounds width",
+ .name = "cropcap_bounds_width",
+ .internal_id = PVR2_CID_CROPCAPBW,
+ .get_value = ctrl_get_cropcapbw,
+ }, {
+ .desc = "Capture capability bounds height",
+ .name = "cropcap_bounds_height",
+ .internal_id = PVR2_CID_CROPCAPBH,
+ .get_value = ctrl_get_cropcapbh,
+ },{
+ .desc = "Video Source",
+ .name = "input",
+ .internal_id = PVR2_CID_INPUT,
+ .default_value = PVR2_CVAL_INPUT_TV,
+ .check_value = ctrl_check_input,
+ DEFREF(input),
+ DEFENUM(control_values_input),
+ },{
+ .desc = "Audio Mode",
+ .name = "audio_mode",
+ .internal_id = PVR2_CID_AUDIOMODE,
+ .default_value = V4L2_TUNER_MODE_STEREO,
+ DEFREF(audiomode),
+ DEFENUM(control_values_audiomode),
+ },{
+ .desc = "Horizontal capture resolution",
+ .name = "resolution_hor",
+ .internal_id = PVR2_CID_HRES,
+ .default_value = 720,
+ DEFREF(res_hor),
+ DEFINT(19,720),
+ },{
+ .desc = "Vertical capture resolution",
+ .name = "resolution_ver",
+ .internal_id = PVR2_CID_VRES,
+ .default_value = 480,
+ DEFREF(res_ver),
+ DEFINT(17,576),
+ /* Hook in check for video standard and adjust maximum
+ depending on the standard. */
+ .get_max_value = ctrl_vres_max_get,
+ .get_min_value = ctrl_vres_min_get,
+ },{
+ .v4l_id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ,
+ .default_value = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000,
+ .desc = "Audio Sampling Frequency",
+ .name = "srate",
+ DEFREF(srate),
+ DEFENUM(control_values_srate),
+ },{
+ .desc = "Tuner Frequency (Hz)",
+ .name = "frequency",
+ .internal_id = PVR2_CID_FREQUENCY,
+ .default_value = 0,
+ .set_value = ctrl_freq_set,
+ .get_value = ctrl_freq_get,
+ .is_dirty = ctrl_freq_is_dirty,
+ .clear_dirty = ctrl_freq_clear_dirty,
+ DEFINT(0,0),
+ /* Hook in check for input value (tv/radio) and adjust
+ max/min values accordingly */
+ .get_max_value = ctrl_freq_max_get,
+ .get_min_value = ctrl_freq_min_get,
+ },{
+ .desc = "Channel",
+ .name = "channel",
+ .set_value = ctrl_channel_set,
+ .get_value = ctrl_channel_get,
+ DEFINT(0,FREQTABLE_SIZE),
+ },{
+ .desc = "Channel Program Frequency",
+ .name = "freq_table_value",
+ .set_value = ctrl_channelfreq_set,
+ .get_value = ctrl_channelfreq_get,
+ DEFINT(0,0),
+ /* Hook in check for input value (tv/radio) and adjust
+ max/min values accordingly */
+ .get_max_value = ctrl_freq_max_get,
+ .get_min_value = ctrl_freq_min_get,
+ },{
+ .desc = "Channel Program ID",
+ .name = "freq_table_channel",
+ .set_value = ctrl_channelprog_set,
+ .get_value = ctrl_channelprog_get,
+ DEFINT(0,FREQTABLE_SIZE),
+ },{
+ .desc = "Streaming Enabled",
+ .name = "streaming_enabled",
+ .get_value = ctrl_streamingenabled_get,
+ DEFBOOL,
+ },{
+ .desc = "USB Speed",
+ .name = "usb_speed",
+ .get_value = ctrl_hsm_get,
+ DEFENUM(control_values_hsm),
+ },{
+ .desc = "Master State",
+ .name = "master_state",
+ .get_value = ctrl_masterstate_get,
+ DEFENUM(pvr2_state_names),
+ },{
+ .desc = "Signal Present",
+ .name = "signal_present",
+ .get_value = ctrl_signal_get,
+ DEFINT(0,65535),
+ },{
+ .desc = "Audio Modes Present",
+ .name = "audio_modes_present",
+ .get_value = ctrl_audio_modes_present_get,
+ /* For this type we "borrow" the V4L2_TUNER_MODE enum from
+ v4l. Nothing outside of this module cares about this,
+ but I reuse it in order to also reuse the
+ control_values_audiomode string table. */
+ DEFMASK(((1 << V4L2_TUNER_MODE_MONO)|
+ (1 << V4L2_TUNER_MODE_STEREO)|
+ (1 << V4L2_TUNER_MODE_LANG1)|
+ (1 << V4L2_TUNER_MODE_LANG2)),
+ control_values_audiomode),
+ },{
+ .desc = "Video Standards Available Mask",
+ .name = "video_standard_mask_available",
+ .internal_id = PVR2_CID_STDAVAIL,
+ .skip_init = !0,
+ .get_value = ctrl_stdavail_get,
+ .set_value = ctrl_stdavail_set,
+ .val_to_sym = ctrl_std_val_to_sym,
+ .sym_to_val = ctrl_std_sym_to_val,
+ .type = pvr2_ctl_bitmask,
+ },{
+ .desc = "Video Standards In Use Mask",
+ .name = "video_standard_mask_active",
+ .internal_id = PVR2_CID_STDCUR,
+ .skip_init = !0,
+ .get_value = ctrl_stdcur_get,
+ .set_value = ctrl_stdcur_set,
+ .is_dirty = ctrl_stdcur_is_dirty,
+ .clear_dirty = ctrl_stdcur_clear_dirty,
+ .val_to_sym = ctrl_std_val_to_sym,
+ .sym_to_val = ctrl_std_sym_to_val,
+ .type = pvr2_ctl_bitmask,
+ },{
+ .desc = "Video Standard Name",
+ .name = "video_standard",
+ .internal_id = PVR2_CID_STDENUM,
+ .skip_init = !0,
+ .get_value = ctrl_stdenumcur_get,
+ .set_value = ctrl_stdenumcur_set,
+ .is_dirty = ctrl_stdenumcur_is_dirty,
+ .clear_dirty = ctrl_stdenumcur_clear_dirty,
+ .type = pvr2_ctl_enum,
+ }
+};
+
+#define CTRLDEF_COUNT ARRAY_SIZE(control_defs)
+
+
+const char *pvr2_config_get_name(enum pvr2_config cfg)
+{
+ switch (cfg) {
+ case pvr2_config_empty: return "empty";
+ case pvr2_config_mpeg: return "mpeg";
+ case pvr2_config_vbi: return "vbi";
+ case pvr2_config_pcm: return "pcm";
+ case pvr2_config_rawvideo: return "raw video";
+ }
+ return "<unknown>";
+}
+
+
+struct usb_device *pvr2_hdw_get_dev(struct pvr2_hdw *hdw)
+{
+ return hdw->usb_dev;
+}
+
+
+unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *hdw)
+{
+ return hdw->serial_number;
+}
+
+
+const char *pvr2_hdw_get_bus_info(struct pvr2_hdw *hdw)
+{
+ return hdw->bus_info;
+}
+
+
+const char *pvr2_hdw_get_device_identifier(struct pvr2_hdw *hdw)
+{
+ return hdw->identifier;
+}
+
+
+unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *hdw)
+{
+ return hdw->freqSelector ? hdw->freqValTelevision : hdw->freqValRadio;
+}
+
+/* Set the currently tuned frequency and account for all possible
+ driver-core side effects of this action. */
+static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *hdw,unsigned long val)
+{
+ if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
+ if (hdw->freqSelector) {
+ /* Swing over to radio frequency selection */
+ hdw->freqSelector = 0;
+ hdw->freqDirty = !0;
+ }
+ if (hdw->freqValRadio != val) {
+ hdw->freqValRadio = val;
+ hdw->freqSlotRadio = 0;
+ hdw->freqDirty = !0;
+ }
+ } else {
+ if (!(hdw->freqSelector)) {
+ /* Swing over to television frequency selection */
+ hdw->freqSelector = 1;
+ hdw->freqDirty = !0;
+ }
+ if (hdw->freqValTelevision != val) {
+ hdw->freqValTelevision = val;
+ hdw->freqSlotTelevision = 0;
+ hdw->freqDirty = !0;
+ }
+ }
+}
+
+int pvr2_hdw_get_unit_number(struct pvr2_hdw *hdw)
+{
+ return hdw->unit_number;
+}
+
+
+/* Attempt to locate one of the given set of files. Messages are logged
+ appropriate to what has been found. The return value will be 0 or
+ greater on success (it will be the index of the file name found) and
+ fw_entry will be filled in. Otherwise a negative error is returned on
+ failure. If the return value is -ENOENT then no viable firmware file
+ could be located. */
+static int pvr2_locate_firmware(struct pvr2_hdw *hdw,
+ const struct firmware **fw_entry,
+ const char *fwtypename,
+ unsigned int fwcount,
+ const char *fwnames[])
+{
+ unsigned int idx;
+ int ret = -EINVAL;
+ for (idx = 0; idx < fwcount; idx++) {
+ ret = request_firmware(fw_entry,
+ fwnames[idx],
+ &hdw->usb_dev->dev);
+ if (!ret) {
+ trace_firmware("Located %s firmware: %s;"
+ " uploading...",
+ fwtypename,
+ fwnames[idx]);
+ return idx;
+ }
+ if (ret == -ENOENT) continue;
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "request_firmware fatal error with code=%d",ret);
+ return ret;
+ }
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "***WARNING***"
+ " Device %s firmware"
+ " seems to be missing.",
+ fwtypename);
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Did you install the pvrusb2 firmware files"
+ " in their proper location?");
+ if (fwcount == 1) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "request_firmware unable to locate %s file %s",
+ fwtypename,fwnames[0]);
+ } else {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "request_firmware unable to locate"
+ " one of the following %s files:",
+ fwtypename);
+ for (idx = 0; idx < fwcount; idx++) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "request_firmware: Failed to find %s",
+ fwnames[idx]);
+ }
+ }
+ return ret;
+}
+
+
+/*
+ * pvr2_upload_firmware1().
+ *
+ * Send the 8051 firmware to the device. After the upload, arrange for
+ * device to re-enumerate.
+ *
+ * NOTE : the pointer to the firmware data given by request_firmware()
+ * is not suitable for an usb transaction.
+ *
+ */
+static int pvr2_upload_firmware1(struct pvr2_hdw *hdw)
+{
+ const struct firmware *fw_entry = NULL;
+ void *fw_ptr;
+ unsigned int pipe;
+ unsigned int fwsize;
+ int ret;
+ u16 address;
+
+ if (!hdw->hdw_desc->fx2_firmware.cnt) {
+ hdw->fw1_state = FW1_STATE_OK;
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Connected device type defines"
+ " no firmware to upload; ignoring firmware");
+ return -ENOTTY;
+ }
+
+ hdw->fw1_state = FW1_STATE_FAILED; // default result
+
+ trace_firmware("pvr2_upload_firmware1");
+
+ ret = pvr2_locate_firmware(hdw,&fw_entry,"fx2 controller",
+ hdw->hdw_desc->fx2_firmware.cnt,
+ hdw->hdw_desc->fx2_firmware.lst);
+ if (ret < 0) {
+ if (ret == -ENOENT) hdw->fw1_state = FW1_STATE_MISSING;
+ return ret;
+ }
+
+ usb_clear_halt(hdw->usb_dev, usb_sndbulkpipe(hdw->usb_dev, 0 & 0x7f));
+
+ pipe = usb_sndctrlpipe(hdw->usb_dev, 0);
+ fwsize = fw_entry->size;
+
+ if ((fwsize != 0x2000) &&
+ (!(hdw->hdw_desc->flag_fx2_16kb && (fwsize == 0x4000)))) {
+ if (hdw->hdw_desc->flag_fx2_16kb) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Wrong fx2 firmware size"
+ " (expected 8192 or 16384, got %u)",
+ fwsize);
+ } else {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Wrong fx2 firmware size"
+ " (expected 8192, got %u)",
+ fwsize);
+ }
+ release_firmware(fw_entry);
+ return -ENOMEM;
+ }
+
+ fw_ptr = kmalloc(0x800, GFP_KERNEL);
+ if (fw_ptr == NULL){
+ release_firmware(fw_entry);
+ return -ENOMEM;
+ }
+
+ /* We have to hold the CPU during firmware upload. */
+ pvr2_hdw_cpureset_assert(hdw,1);
+
+ /* upload the firmware to address 0000-1fff in 2048 (=0x800) bytes
+ chunk. */
+
+ ret = 0;
+ for (address = 0; address < fwsize; address += 0x800) {
+ memcpy(fw_ptr, fw_entry->data + address, 0x800);
+ ret += usb_control_msg(hdw->usb_dev, pipe, 0xa0, 0x40, address,
+ 0, fw_ptr, 0x800, HZ);
+ }
+
+ trace_firmware("Upload done, releasing device's CPU");
+
+ /* Now release the CPU. It will disconnect and reconnect later. */
+ pvr2_hdw_cpureset_assert(hdw,0);
+
+ kfree(fw_ptr);
+ release_firmware(fw_entry);
+
+ trace_firmware("Upload done (%d bytes sent)",ret);
+
+ /* We should have written fwsize bytes */
+ if (ret == fwsize) {
+ hdw->fw1_state = FW1_STATE_RELOAD;
+ return 0;
+ }
+
+ return -EIO;
+}
+
+
+/*
+ * pvr2_upload_firmware2()
+ *
+ * This uploads encoder firmware on endpoint 2.
+ *
+ */
+
+int pvr2_upload_firmware2(struct pvr2_hdw *hdw)
+{
+ const struct firmware *fw_entry = NULL;
+ void *fw_ptr;
+ unsigned int pipe, fw_len, fw_done, bcnt, icnt;
+ int actual_length;
+ int ret = 0;
+ int fwidx;
+ static const char *fw_files[] = {
+ CX2341X_FIRM_ENC_FILENAME,
+ };
+
+ if (hdw->hdw_desc->flag_skip_cx23416_firmware) {
+ return 0;
+ }
+
+ trace_firmware("pvr2_upload_firmware2");
+
+ ret = pvr2_locate_firmware(hdw,&fw_entry,"encoder",
+ ARRAY_SIZE(fw_files), fw_files);
+ if (ret < 0) return ret;
+ fwidx = ret;
+ ret = 0;
+ /* Since we're about to completely reinitialize the encoder,
+ invalidate our cached copy of its configuration state. Next
+ time we configure the encoder, then we'll fully configure it. */
+ hdw->enc_cur_valid = 0;
+
+ /* Encoder is about to be reset so note that as far as we're
+ concerned now, the encoder has never been run. */
+ del_timer_sync(&hdw->encoder_run_timer);
+ if (hdw->state_encoder_runok) {
+ hdw->state_encoder_runok = 0;
+ trace_stbit("state_encoder_runok",hdw->state_encoder_runok);
+ }
+
+ /* First prepare firmware loading */
+ ret |= pvr2_write_register(hdw, 0x0048, 0xffffffff); /*interrupt mask*/
+ ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000088); /*gpio dir*/
+ ret |= pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000008); /*gpio output state*/
+ ret |= pvr2_hdw_cmd_deep_reset(hdw);
+ ret |= pvr2_write_register(hdw, 0xa064, 0x00000000); /*APU command*/
+ ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000408); /*gpio dir*/
+ ret |= pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000008); /*gpio output state*/
+ ret |= pvr2_write_register(hdw, 0x9058, 0xffffffed); /*VPU ctrl*/
+ ret |= pvr2_write_register(hdw, 0x9054, 0xfffffffd); /*reset hw blocks*/
+ ret |= pvr2_write_register(hdw, 0x07f8, 0x80000800); /*encoder SDRAM refresh*/
+ ret |= pvr2_write_register(hdw, 0x07fc, 0x0000001a); /*encoder SDRAM pre-charge*/
+ ret |= pvr2_write_register(hdw, 0x0700, 0x00000000); /*I2C clock*/
+ ret |= pvr2_write_register(hdw, 0xaa00, 0x00000000); /*unknown*/
+ ret |= pvr2_write_register(hdw, 0xaa04, 0x00057810); /*unknown*/
+ ret |= pvr2_write_register(hdw, 0xaa10, 0x00148500); /*unknown*/
+ ret |= pvr2_write_register(hdw, 0xaa18, 0x00840000); /*unknown*/
+ ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_FWPOST1);
+ ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_MEMSEL | (1 << 8) | (0 << 16));
+
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "firmware2 upload prep failed, ret=%d",ret);
+ release_firmware(fw_entry);
+ goto done;
+ }
+
+ /* Now send firmware */
+
+ fw_len = fw_entry->size;
+
+ if (fw_len % sizeof(u32)) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "size of %s firmware"
+ " must be a multiple of %zu bytes",
+ fw_files[fwidx],sizeof(u32));
+ release_firmware(fw_entry);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ fw_ptr = kmalloc(FIRMWARE_CHUNK_SIZE, GFP_KERNEL);
+ if (fw_ptr == NULL){
+ release_firmware(fw_entry);
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "failed to allocate memory for firmware2 upload");
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ pipe = usb_sndbulkpipe(hdw->usb_dev, PVR2_FIRMWARE_ENDPOINT);
+
+ fw_done = 0;
+ for (fw_done = 0; fw_done < fw_len;) {
+ bcnt = fw_len - fw_done;
+ if (bcnt > FIRMWARE_CHUNK_SIZE) bcnt = FIRMWARE_CHUNK_SIZE;
+ memcpy(fw_ptr, fw_entry->data + fw_done, bcnt);
+ /* Usbsnoop log shows that we must swap bytes... */
+ /* Some background info: The data being swapped here is a
+ firmware image destined for the mpeg encoder chip that
+ lives at the other end of a USB endpoint. The encoder
+ chip always talks in 32 bit chunks and its storage is
+ organized into 32 bit words. However from the file
+ system to the encoder chip everything is purely a byte
+ stream. The firmware file's contents are always 32 bit
+ swapped from what the encoder expects. Thus the need
+ always exists to swap the bytes regardless of the endian
+ type of the host processor and therefore swab32() makes
+ the most sense. */
+ for (icnt = 0; icnt < bcnt/4 ; icnt++)
+ ((u32 *)fw_ptr)[icnt] = swab32(((u32 *)fw_ptr)[icnt]);
+
+ ret |= usb_bulk_msg(hdw->usb_dev, pipe, fw_ptr,bcnt,
+ &actual_length, HZ);
+ ret |= (actual_length != bcnt);
+ if (ret) break;
+ fw_done += bcnt;
+ }
+
+ trace_firmware("upload of %s : %i / %i ",
+ fw_files[fwidx],fw_done,fw_len);
+
+ kfree(fw_ptr);
+ release_firmware(fw_entry);
+
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "firmware2 upload transfer failure");
+ goto done;
+ }
+
+ /* Finish upload */
+
+ ret |= pvr2_write_register(hdw, 0x9054, 0xffffffff); /*reset hw blocks*/
+ ret |= pvr2_write_register(hdw, 0x9058, 0xffffffe8); /*VPU ctrl*/
+ ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_MEMSEL | (1 << 8) | (0 << 16));
+
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "firmware2 upload post-proc failure");
+ }
+
+ done:
+ if (hdw->hdw_desc->signal_routing_scheme ==
+ PVR2_ROUTING_SCHEME_GOTVIEW) {
+ /* Ensure that GPIO 11 is set to output for GOTVIEW
+ hardware. */
+ pvr2_hdw_gpio_chg_dir(hdw,(1 << 11),~0);
+ }
+ return ret;
+}
+
+
+static const char *pvr2_get_state_name(unsigned int st)
+{
+ if (st < ARRAY_SIZE(pvr2_state_names)) {
+ return pvr2_state_names[st];
+ }
+ return "???";
+}
+
+static int pvr2_decoder_enable(struct pvr2_hdw *hdw,int enablefl)
+{
+ /* Even though we really only care about the video decoder chip at
+ this point, we'll broadcast stream on/off to all sub-devices
+ anyway, just in case somebody else wants to hear the
+ command... */
+ pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 stream=%s",
+ (enablefl ? "on" : "off"));
+ v4l2_device_call_all(&hdw->v4l2_dev, 0, video, s_stream, enablefl);
+ v4l2_device_call_all(&hdw->v4l2_dev, 0, audio, s_stream, enablefl);
+ if (hdw->decoder_client_id) {
+ /* We get here if the encoder has been noticed. Otherwise
+ we'll issue a warning to the user (which should
+ normally never happen). */
+ return 0;
+ }
+ if (!hdw->flag_decoder_missed) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "WARNING: No decoder present");
+ hdw->flag_decoder_missed = !0;
+ trace_stbit("flag_decoder_missed",
+ hdw->flag_decoder_missed);
+ }
+ return -EIO;
+}
+
+
+int pvr2_hdw_get_state(struct pvr2_hdw *hdw)
+{
+ return hdw->master_state;
+}
+
+
+static int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *hdw)
+{
+ if (!hdw->flag_tripped) return 0;
+ hdw->flag_tripped = 0;
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Clearing driver error statuss");
+ return !0;
+}
+
+
+int pvr2_hdw_untrip(struct pvr2_hdw *hdw)
+{
+ int fl;
+ LOCK_TAKE(hdw->big_lock); do {
+ fl = pvr2_hdw_untrip_unlocked(hdw);
+ } while (0); LOCK_GIVE(hdw->big_lock);
+ if (fl) pvr2_hdw_state_sched(hdw);
+ return 0;
+}
+
+
+
+
+int pvr2_hdw_get_streaming(struct pvr2_hdw *hdw)
+{
+ return hdw->state_pipeline_req != 0;
+}
+
+
+int pvr2_hdw_set_streaming(struct pvr2_hdw *hdw,int enable_flag)
+{
+ int ret,st;
+ LOCK_TAKE(hdw->big_lock); do {
+ pvr2_hdw_untrip_unlocked(hdw);
+ if ((!enable_flag) != !(hdw->state_pipeline_req)) {
+ hdw->state_pipeline_req = enable_flag != 0;
+ pvr2_trace(PVR2_TRACE_START_STOP,
+ "/*--TRACE_STREAM--*/ %s",
+ enable_flag ? "enable" : "disable");
+ }
+ pvr2_hdw_state_sched(hdw);
+ } while (0); LOCK_GIVE(hdw->big_lock);
+ if ((ret = pvr2_hdw_wait(hdw,0)) < 0) return ret;
+ if (enable_flag) {
+ while ((st = hdw->master_state) != PVR2_STATE_RUN) {
+ if (st != PVR2_STATE_READY) return -EIO;
+ if ((ret = pvr2_hdw_wait(hdw,st)) < 0) return ret;
+ }
+ }
+ return 0;
+}
+
+
+int pvr2_hdw_set_stream_type(struct pvr2_hdw *hdw,enum pvr2_config config)
+{
+ int fl;
+ LOCK_TAKE(hdw->big_lock);
+ if ((fl = (hdw->desired_stream_type != config)) != 0) {
+ hdw->desired_stream_type = config;
+ hdw->state_pipeline_config = 0;
+ trace_stbit("state_pipeline_config",
+ hdw->state_pipeline_config);
+ pvr2_hdw_state_sched(hdw);
+ }
+ LOCK_GIVE(hdw->big_lock);
+ if (fl) return 0;
+ return pvr2_hdw_wait(hdw,0);
+}
+
+
+static int get_default_tuner_type(struct pvr2_hdw *hdw)
+{
+ int unit_number = hdw->unit_number;
+ int tp = -1;
+ if ((unit_number >= 0) && (unit_number < PVR_NUM)) {
+ tp = tuner[unit_number];
+ }
+ if (tp < 0) return -EINVAL;
+ hdw->tuner_type = tp;
+ hdw->tuner_updated = !0;
+ return 0;
+}
+
+
+static v4l2_std_id get_default_standard(struct pvr2_hdw *hdw)
+{
+ int unit_number = hdw->unit_number;
+ int tp = 0;
+ if ((unit_number >= 0) && (unit_number < PVR_NUM)) {
+ tp = video_std[unit_number];
+ if (tp) return tp;
+ }
+ return 0;
+}
+
+
+static unsigned int get_default_error_tolerance(struct pvr2_hdw *hdw)
+{
+ int unit_number = hdw->unit_number;
+ int tp = 0;
+ if ((unit_number >= 0) && (unit_number < PVR_NUM)) {
+ tp = tolerance[unit_number];
+ }
+ return tp;
+}
+
+
+static int pvr2_hdw_check_firmware(struct pvr2_hdw *hdw)
+{
+ /* Try a harmless request to fetch the eeprom's address over
+ endpoint 1. See what happens. Only the full FX2 image can
+ respond to this. If this probe fails then likely the FX2
+ firmware needs be loaded. */
+ int result;
+ LOCK_TAKE(hdw->ctl_lock); do {
+ hdw->cmd_buffer[0] = FX2CMD_GET_EEPROM_ADDR;
+ result = pvr2_send_request_ex(hdw,HZ*1,!0,
+ hdw->cmd_buffer,1,
+ hdw->cmd_buffer,1);
+ if (result < 0) break;
+ } while(0); LOCK_GIVE(hdw->ctl_lock);
+ if (result) {
+ pvr2_trace(PVR2_TRACE_INIT,
+ "Probe of device endpoint 1 result status %d",
+ result);
+ } else {
+ pvr2_trace(PVR2_TRACE_INIT,
+ "Probe of device endpoint 1 succeeded");
+ }
+ return result == 0;
+}
+
+struct pvr2_std_hack {
+ v4l2_std_id pat; /* Pattern to match */
+ v4l2_std_id msk; /* Which bits we care about */
+ v4l2_std_id std; /* What additional standards or default to set */
+};
+
+/* This data structure labels specific combinations of standards from
+ tveeprom that we'll try to recognize. If we recognize one, then assume
+ a specified default standard to use. This is here because tveeprom only
+ tells us about available standards not the intended default standard (if
+ any) for the device in question. We guess the default based on what has
+ been reported as available. Note that this is only for guessing a
+ default - which can always be overridden explicitly - and if the user
+ has otherwise named a default then that default will always be used in
+ place of this table. */
+static const struct pvr2_std_hack std_eeprom_maps[] = {
+ { /* PAL(B/G) */
+ .pat = V4L2_STD_B|V4L2_STD_GH,
+ .std = V4L2_STD_PAL_B|V4L2_STD_PAL_B1|V4L2_STD_PAL_G,
+ },
+ { /* NTSC(M) */
+ .pat = V4L2_STD_MN,
+ .std = V4L2_STD_NTSC_M,
+ },
+ { /* PAL(I) */
+ .pat = V4L2_STD_PAL_I,
+ .std = V4L2_STD_PAL_I,
+ },
+ { /* SECAM(L/L') */
+ .pat = V4L2_STD_SECAM_L|V4L2_STD_SECAM_LC,
+ .std = V4L2_STD_SECAM_L|V4L2_STD_SECAM_LC,
+ },
+ { /* PAL(D/D1/K) */
+ .pat = V4L2_STD_DK,
+ .std = V4L2_STD_PAL_D|V4L2_STD_PAL_D1|V4L2_STD_PAL_K,
+ },
+};
+
+static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw)
+{
+ char buf[40];
+ unsigned int bcnt;
+ v4l2_std_id std1,std2,std3;
+
+ std1 = get_default_standard(hdw);
+ std3 = std1 ? 0 : hdw->hdw_desc->default_std_mask;
+
+ bcnt = pvr2_std_id_to_str(buf,sizeof(buf),hdw->std_mask_eeprom);
+ pvr2_trace(PVR2_TRACE_STD,
+ "Supported video standard(s) reported available"
+ " in hardware: %.*s",
+ bcnt,buf);
+
+ hdw->std_mask_avail = hdw->std_mask_eeprom;
+
+ std2 = (std1|std3) & ~hdw->std_mask_avail;
+ if (std2) {
+ bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std2);
+ pvr2_trace(PVR2_TRACE_STD,
+ "Expanding supported video standards"
+ " to include: %.*s",
+ bcnt,buf);
+ hdw->std_mask_avail |= std2;
+ }
+
+ pvr2_hdw_internal_set_std_avail(hdw);
+
+ if (std1) {
+ bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std1);
+ pvr2_trace(PVR2_TRACE_STD,
+ "Initial video standard forced to %.*s",
+ bcnt,buf);
+ hdw->std_mask_cur = std1;
+ hdw->std_dirty = !0;
+ pvr2_hdw_internal_find_stdenum(hdw);
+ return;
+ }
+ if (std3) {
+ bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std3);
+ pvr2_trace(PVR2_TRACE_STD,
+ "Initial video standard"
+ " (determined by device type): %.*s",bcnt,buf);
+ hdw->std_mask_cur = std3;
+ hdw->std_dirty = !0;
+ pvr2_hdw_internal_find_stdenum(hdw);
+ return;
+ }
+
+ {
+ unsigned int idx;
+ for (idx = 0; idx < ARRAY_SIZE(std_eeprom_maps); idx++) {
+ if (std_eeprom_maps[idx].msk ?
+ ((std_eeprom_maps[idx].pat ^
+ hdw->std_mask_eeprom) &
+ std_eeprom_maps[idx].msk) :
+ (std_eeprom_maps[idx].pat !=
+ hdw->std_mask_eeprom)) continue;
+ bcnt = pvr2_std_id_to_str(buf,sizeof(buf),
+ std_eeprom_maps[idx].std);
+ pvr2_trace(PVR2_TRACE_STD,
+ "Initial video standard guessed as %.*s",
+ bcnt,buf);
+ hdw->std_mask_cur = std_eeprom_maps[idx].std;
+ hdw->std_dirty = !0;
+ pvr2_hdw_internal_find_stdenum(hdw);
+ return;
+ }
+ }
+
+ if (hdw->std_enum_cnt > 1) {
+ // Autoselect the first listed standard
+ hdw->std_enum_cur = 1;
+ hdw->std_mask_cur = hdw->std_defs[hdw->std_enum_cur-1].id;
+ hdw->std_dirty = !0;
+ pvr2_trace(PVR2_TRACE_STD,
+ "Initial video standard auto-selected to %s",
+ hdw->std_defs[hdw->std_enum_cur-1].name);
+ return;
+ }
+
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Unable to select a viable initial video standard");
+}
+
+
+static unsigned int pvr2_copy_i2c_addr_list(
+ unsigned short *dst, const unsigned char *src,
+ unsigned int dst_max)
+{
+ unsigned int cnt = 0;
+ if (!src) return 0;
+ while (src[cnt] && (cnt + 1) < dst_max) {
+ dst[cnt] = src[cnt];
+ cnt++;
+ }
+ dst[cnt] = I2C_CLIENT_END;
+ return cnt;
+}
+
+
+static void pvr2_hdw_cx25840_vbi_hack(struct pvr2_hdw *hdw)
+{
+ /*
+ Mike Isely <isely@pobox.com> 19-Nov-2006 - This bit of nuttiness
+ for cx25840 causes that module to correctly set up its video
+ scaling. This is really a problem in the cx25840 module itself,
+ but we work around it here. The problem has not been seen in
+ ivtv because there VBI is supported and set up. We don't do VBI
+ here (at least not yet) and thus we never attempted to even set
+ it up.
+ */
+ struct v4l2_format fmt;
+ if (hdw->decoder_client_id != PVR2_CLIENT_ID_CX25840) {
+ /* We're not using a cx25840 so don't enable the hack */
+ return;
+ }
+
+ pvr2_trace(PVR2_TRACE_INIT,
+ "Module ID %u:"
+ " Executing cx25840 VBI hack",
+ hdw->decoder_client_id);
+ memset(&fmt, 0, sizeof(fmt));
+ fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
+ fmt.fmt.sliced.service_lines[0][21] = V4L2_SLICED_CAPTION_525;
+ fmt.fmt.sliced.service_lines[1][21] = V4L2_SLICED_CAPTION_525;
+ v4l2_device_call_all(&hdw->v4l2_dev, hdw->decoder_client_id,
+ vbi, s_sliced_fmt, &fmt.fmt.sliced);
+}
+
+
+static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw,
+ const struct pvr2_device_client_desc *cd)
+{
+ const char *fname;
+ unsigned char mid;
+ struct v4l2_subdev *sd;
+ unsigned int i2ccnt;
+ const unsigned char *p;
+ /* Arbitrary count - max # i2c addresses we will probe */
+ unsigned short i2caddr[25];
+
+ mid = cd->module_id;
+ fname = (mid < ARRAY_SIZE(module_names)) ? module_names[mid] : NULL;
+ if (!fname) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Module ID %u for device %s has no name?"
+ " The driver might have a configuration problem.",
+ mid,
+ hdw->hdw_desc->description);
+ return -EINVAL;
+ }
+ pvr2_trace(PVR2_TRACE_INIT,
+ "Module ID %u (%s) for device %s being loaded...",
+ mid, fname,
+ hdw->hdw_desc->description);
+
+ i2ccnt = pvr2_copy_i2c_addr_list(i2caddr, cd->i2c_address_list,
+ ARRAY_SIZE(i2caddr));
+ if (!i2ccnt && ((p = (mid < ARRAY_SIZE(module_i2c_addresses)) ?
+ module_i2c_addresses[mid] : NULL) != NULL)) {
+ /* Second chance: Try default i2c address list */
+ i2ccnt = pvr2_copy_i2c_addr_list(i2caddr, p,
+ ARRAY_SIZE(i2caddr));
+ if (i2ccnt) {
+ pvr2_trace(PVR2_TRACE_INIT,
+ "Module ID %u:"
+ " Using default i2c address list",
+ mid);
+ }
+ }
+
+ if (!i2ccnt) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Module ID %u (%s) for device %s:"
+ " No i2c addresses."
+ " The driver might have a configuration problem.",
+ mid, fname, hdw->hdw_desc->description);
+ return -EINVAL;
+ }
+
+ if (i2ccnt == 1) {
+ pvr2_trace(PVR2_TRACE_INIT,
+ "Module ID %u:"
+ " Setting up with specified i2c address 0x%x",
+ mid, i2caddr[0]);
+ sd = v4l2_i2c_new_subdev(&hdw->v4l2_dev, &hdw->i2c_adap,
+ fname, i2caddr[0], NULL);
+ } else {
+ pvr2_trace(PVR2_TRACE_INIT,
+ "Module ID %u:"
+ " Setting up with address probe list",
+ mid);
+ sd = v4l2_i2c_new_subdev(&hdw->v4l2_dev, &hdw->i2c_adap,
+ fname, 0, i2caddr);
+ }
+
+ if (!sd) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Module ID %u (%s) for device %s failed to load."
+ " Possible missing sub-device kernel module or"
+ " initialization failure within module.",
+ mid, fname, hdw->hdw_desc->description);
+ return -EIO;
+ }
+
+ /* Tag this sub-device instance with the module ID we know about.
+ In other places we'll use that tag to determine if the instance
+ requires special handling. */
+ sd->grp_id = mid;
+
+ pvr2_trace(PVR2_TRACE_INFO, "Attached sub-driver %s", fname);
+
+
+ /* client-specific setup... */
+ switch (mid) {
+ case PVR2_CLIENT_ID_CX25840:
+ case PVR2_CLIENT_ID_SAA7115:
+ hdw->decoder_client_id = mid;
+ break;
+ default: break;
+ }
+
+ return 0;
+}
+
+
+static void pvr2_hdw_load_modules(struct pvr2_hdw *hdw)
+{
+ unsigned int idx;
+ const struct pvr2_string_table *cm;
+ const struct pvr2_device_client_table *ct;
+ int okFl = !0;
+
+ cm = &hdw->hdw_desc->client_modules;
+ for (idx = 0; idx < cm->cnt; idx++) {
+ request_module(cm->lst[idx]);
+ }
+
+ ct = &hdw->hdw_desc->client_table;
+ for (idx = 0; idx < ct->cnt; idx++) {
+ if (pvr2_hdw_load_subdev(hdw, &ct->lst[idx]) < 0) okFl = 0;
+ }
+ if (!okFl) {
+ hdw->flag_modulefail = !0;
+ pvr2_hdw_render_useless(hdw);
+ }
+}
+
+
+static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
+{
+ int ret;
+ unsigned int idx;
+ struct pvr2_ctrl *cptr;
+ int reloadFl = 0;
+ if (hdw->hdw_desc->fx2_firmware.cnt) {
+ if (!reloadFl) {
+ reloadFl =
+ (hdw->usb_intf->cur_altsetting->desc.bNumEndpoints
+ == 0);
+ if (reloadFl) {
+ pvr2_trace(PVR2_TRACE_INIT,
+ "USB endpoint config looks strange"
+ "; possibly firmware needs to be"
+ " loaded");
+ }
+ }
+ if (!reloadFl) {
+ reloadFl = !pvr2_hdw_check_firmware(hdw);
+ if (reloadFl) {
+ pvr2_trace(PVR2_TRACE_INIT,
+ "Check for FX2 firmware failed"
+ "; possibly firmware needs to be"
+ " loaded");
+ }
+ }
+ if (reloadFl) {
+ if (pvr2_upload_firmware1(hdw) != 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Failure uploading firmware1");
+ }
+ return;
+ }
+ }
+ hdw->fw1_state = FW1_STATE_OK;
+
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+
+ hdw->force_dirty = !0;
+
+ if (!hdw->hdw_desc->flag_no_powerup) {
+ pvr2_hdw_cmd_powerup(hdw);
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+ }
+
+ /* Take the IR chip out of reset, if appropriate */
+ if (hdw->ir_scheme_active == PVR2_IR_SCHEME_ZILOG) {
+ pvr2_issue_simple_cmd(hdw,
+ FX2CMD_HCW_ZILOG_RESET |
+ (1 << 8) |
+ ((0) << 16));
+ }
+
+ // This step MUST happen after the earlier powerup step.
+ pvr2_i2c_core_init(hdw);
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+
+ pvr2_hdw_load_modules(hdw);
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+
+ v4l2_device_call_all(&hdw->v4l2_dev, 0, core, load_fw);
+
+ for (idx = 0; idx < CTRLDEF_COUNT; idx++) {
+ cptr = hdw->controls + idx;
+ if (cptr->info->skip_init) continue;
+ if (!cptr->info->set_value) continue;
+ cptr->info->set_value(cptr,~0,cptr->info->default_value);
+ }
+
+ pvr2_hdw_cx25840_vbi_hack(hdw);
+
+ /* Set up special default values for the television and radio
+ frequencies here. It's not really important what these defaults
+ are, but I set them to something usable in the Chicago area just
+ to make driver testing a little easier. */
+
+ hdw->freqValTelevision = default_tv_freq;
+ hdw->freqValRadio = default_radio_freq;
+
+ // Do not use pvr2_reset_ctl_endpoints() here. It is not
+ // thread-safe against the normal pvr2_send_request() mechanism.
+ // (We should make it thread safe).
+
+ if (hdw->hdw_desc->flag_has_hauppauge_rom) {
+ ret = pvr2_hdw_get_eeprom_addr(hdw);
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+ if (ret < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Unable to determine location of eeprom,"
+ " skipping");
+ } else {
+ hdw->eeprom_addr = ret;
+ pvr2_eeprom_analyze(hdw);
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+ }
+ } else {
+ hdw->tuner_type = hdw->hdw_desc->default_tuner_type;
+ hdw->tuner_updated = !0;
+ hdw->std_mask_eeprom = V4L2_STD_ALL;
+ }
+
+ if (hdw->serial_number) {
+ idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1,
+ "sn-%lu", hdw->serial_number);
+ } else if (hdw->unit_number >= 0) {
+ idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1,
+ "unit-%c",
+ hdw->unit_number + 'a');
+ } else {
+ idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1,
+ "unit-??");
+ }
+ hdw->identifier[idx] = 0;
+
+ pvr2_hdw_setup_std(hdw);
+
+ if (!get_default_tuner_type(hdw)) {
+ pvr2_trace(PVR2_TRACE_INIT,
+ "pvr2_hdw_setup: Tuner type overridden to %d",
+ hdw->tuner_type);
+ }
+
+
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+
+ if (hdw->hdw_desc->signal_routing_scheme ==
+ PVR2_ROUTING_SCHEME_GOTVIEW) {
+ /* Ensure that GPIO 11 is set to output for GOTVIEW
+ hardware. */
+ pvr2_hdw_gpio_chg_dir(hdw,(1 << 11),~0);
+ }
+
+ pvr2_hdw_commit_setup(hdw);
+
+ hdw->vid_stream = pvr2_stream_create();
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+ pvr2_trace(PVR2_TRACE_INIT,
+ "pvr2_hdw_setup: video stream is %p",hdw->vid_stream);
+ if (hdw->vid_stream) {
+ idx = get_default_error_tolerance(hdw);
+ if (idx) {
+ pvr2_trace(PVR2_TRACE_INIT,
+ "pvr2_hdw_setup: video stream %p"
+ " setting tolerance %u",
+ hdw->vid_stream,idx);
+ }
+ pvr2_stream_setup(hdw->vid_stream,hdw->usb_dev,
+ PVR2_VID_ENDPOINT,idx);
+ }
+
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+
+ hdw->flag_init_ok = !0;
+
+ pvr2_hdw_state_sched(hdw);
+}
+
+
+/* Set up the structure and attempt to put the device into a usable state.
+ This can be a time-consuming operation, which is why it is not done
+ internally as part of the create() step. */
+static void pvr2_hdw_setup(struct pvr2_hdw *hdw)
+{
+ pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) begin",hdw);
+ do {
+ pvr2_hdw_setup_low(hdw);
+ pvr2_trace(PVR2_TRACE_INIT,
+ "pvr2_hdw_setup(hdw=%p) done, ok=%d init_ok=%d",
+ hdw,pvr2_hdw_dev_ok(hdw),hdw->flag_init_ok);
+ if (pvr2_hdw_dev_ok(hdw)) {
+ if (hdw->flag_init_ok) {
+ pvr2_trace(
+ PVR2_TRACE_INFO,
+ "Device initialization"
+ " completed successfully.");
+ break;
+ }
+ if (hdw->fw1_state == FW1_STATE_RELOAD) {
+ pvr2_trace(
+ PVR2_TRACE_INFO,
+ "Device microcontroller firmware"
+ " (re)loaded; it should now reset"
+ " and reconnect.");
+ break;
+ }
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Device initialization was not successful.");
+ if (hdw->fw1_state == FW1_STATE_MISSING) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Giving up since device"
+ " microcontroller firmware"
+ " appears to be missing.");
+ break;
+ }
+ }
+ if (hdw->flag_modulefail) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "***WARNING*** pvrusb2 driver initialization"
+ " failed due to the failure of one or more"
+ " sub-device kernel modules.");
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "You need to resolve the failing condition"
+ " before this driver can function. There"
+ " should be some earlier messages giving more"
+ " information about the problem.");
+ break;
+ }
+ if (procreload) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Attempting pvrusb2 recovery by reloading"
+ " primary firmware.");
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "If this works, device should disconnect"
+ " and reconnect in a sane state.");
+ hdw->fw1_state = FW1_STATE_UNKNOWN;
+ pvr2_upload_firmware1(hdw);
+ } else {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "***WARNING*** pvrusb2 device hardware"
+ " appears to be jammed"
+ " and I can't clear it.");
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "You might need to power cycle"
+ " the pvrusb2 device"
+ " in order to recover.");
+ }
+ } while (0);
+ pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) end",hdw);
+}
+
+
+/* Perform second stage initialization. Set callback pointer first so that
+ we can avoid a possible initialization race (if the kernel thread runs
+ before the callback has been set). */
+int pvr2_hdw_initialize(struct pvr2_hdw *hdw,
+ void (*callback_func)(void *),
+ void *callback_data)
+{
+ LOCK_TAKE(hdw->big_lock); do {
+ if (hdw->flag_disconnected) {
+ /* Handle a race here: If we're already
+ disconnected by this point, then give up. If we
+ get past this then we'll remain connected for
+ the duration of initialization since the entire
+ initialization sequence is now protected by the
+ big_lock. */
+ break;
+ }
+ hdw->state_data = callback_data;
+ hdw->state_func = callback_func;
+ pvr2_hdw_setup(hdw);
+ } while (0); LOCK_GIVE(hdw->big_lock);
+ return hdw->flag_init_ok;
+}
+
+
+/* Create, set up, and return a structure for interacting with the
+ underlying hardware. */
+struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
+ const struct usb_device_id *devid)
+{
+ unsigned int idx,cnt1,cnt2,m;
+ struct pvr2_hdw *hdw = NULL;
+ int valid_std_mask;
+ struct pvr2_ctrl *cptr;
+ struct usb_device *usb_dev;
+ const struct pvr2_device_desc *hdw_desc;
+ __u8 ifnum;
+ struct v4l2_queryctrl qctrl;
+ struct pvr2_ctl_info *ciptr;
+
+ usb_dev = interface_to_usbdev(intf);
+
+ hdw_desc = (const struct pvr2_device_desc *)(devid->driver_info);
+
+ if (hdw_desc == NULL) {
+ pvr2_trace(PVR2_TRACE_INIT, "pvr2_hdw_create:"
+ " No device description pointer,"
+ " unable to continue.");
+ pvr2_trace(PVR2_TRACE_INIT, "If you have a new device type,"
+ " please contact Mike Isely <isely@pobox.com>"
+ " to get it included in the driver\n");
+ goto fail;
+ }
+
+ hdw = kzalloc(sizeof(*hdw),GFP_KERNEL);
+ pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_create: hdw=%p, type \"%s\"",
+ hdw,hdw_desc->description);
+ pvr2_trace(PVR2_TRACE_INFO, "Hardware description: %s",
+ hdw_desc->description);
+ if (hdw_desc->flag_is_experimental) {
+ pvr2_trace(PVR2_TRACE_INFO, "**********");
+ pvr2_trace(PVR2_TRACE_INFO,
+ "WARNING: Support for this device (%s) is"
+ " experimental.", hdw_desc->description);
+ pvr2_trace(PVR2_TRACE_INFO,
+ "Important functionality might not be"
+ " entirely working.");
+ pvr2_trace(PVR2_TRACE_INFO,
+ "Please consider contacting the driver author to"
+ " help with further stabilization of the driver.");
+ pvr2_trace(PVR2_TRACE_INFO, "**********");
+ }
+ if (!hdw) goto fail;
+
+ init_timer(&hdw->quiescent_timer);
+ hdw->quiescent_timer.data = (unsigned long)hdw;
+ hdw->quiescent_timer.function = pvr2_hdw_quiescent_timeout;
+
+ init_timer(&hdw->decoder_stabilization_timer);
+ hdw->decoder_stabilization_timer.data = (unsigned long)hdw;
+ hdw->decoder_stabilization_timer.function =
+ pvr2_hdw_decoder_stabilization_timeout;
+
+ init_timer(&hdw->encoder_wait_timer);
+ hdw->encoder_wait_timer.data = (unsigned long)hdw;
+ hdw->encoder_wait_timer.function = pvr2_hdw_encoder_wait_timeout;
+
+ init_timer(&hdw->encoder_run_timer);
+ hdw->encoder_run_timer.data = (unsigned long)hdw;
+ hdw->encoder_run_timer.function = pvr2_hdw_encoder_run_timeout;
+
+ hdw->master_state = PVR2_STATE_DEAD;
+
+ init_waitqueue_head(&hdw->state_wait_data);
+
+ hdw->tuner_signal_stale = !0;
+ cx2341x_fill_defaults(&hdw->enc_ctl_state);
+
+ /* Calculate which inputs are OK */
+ m = 0;
+ if (hdw_desc->flag_has_analogtuner) m |= 1 << PVR2_CVAL_INPUT_TV;
+ if (hdw_desc->digital_control_scheme != PVR2_DIGITAL_SCHEME_NONE) {
+ m |= 1 << PVR2_CVAL_INPUT_DTV;
+ }
+ if (hdw_desc->flag_has_svideo) m |= 1 << PVR2_CVAL_INPUT_SVIDEO;
+ if (hdw_desc->flag_has_composite) m |= 1 << PVR2_CVAL_INPUT_COMPOSITE;
+ if (hdw_desc->flag_has_fmradio) m |= 1 << PVR2_CVAL_INPUT_RADIO;
+ hdw->input_avail_mask = m;
+ hdw->input_allowed_mask = hdw->input_avail_mask;
+
+ /* If not a hybrid device, pathway_state never changes. So
+ initialize it here to what it should forever be. */
+ if (!(hdw->input_avail_mask & (1 << PVR2_CVAL_INPUT_DTV))) {
+ hdw->pathway_state = PVR2_PATHWAY_ANALOG;
+ } else if (!(hdw->input_avail_mask & (1 << PVR2_CVAL_INPUT_TV))) {
+ hdw->pathway_state = PVR2_PATHWAY_DIGITAL;
+ }
+
+ hdw->control_cnt = CTRLDEF_COUNT;
+ hdw->control_cnt += MPEGDEF_COUNT;
+ hdw->controls = kzalloc(sizeof(struct pvr2_ctrl) * hdw->control_cnt,
+ GFP_KERNEL);
+ if (!hdw->controls) goto fail;
+ hdw->hdw_desc = hdw_desc;
+ hdw->ir_scheme_active = hdw->hdw_desc->ir_scheme;
+ for (idx = 0; idx < hdw->control_cnt; idx++) {
+ cptr = hdw->controls + idx;
+ cptr->hdw = hdw;
+ }
+ for (idx = 0; idx < 32; idx++) {
+ hdw->std_mask_ptrs[idx] = hdw->std_mask_names[idx];
+ }
+ for (idx = 0; idx < CTRLDEF_COUNT; idx++) {
+ cptr = hdw->controls + idx;
+ cptr->info = control_defs+idx;
+ }
+
+ /* Ensure that default input choice is a valid one. */
+ m = hdw->input_avail_mask;
+ if (m) for (idx = 0; idx < (sizeof(m) << 3); idx++) {
+ if (!((1 << idx) & m)) continue;
+ hdw->input_val = idx;
+ break;
+ }
+
+ /* Define and configure additional controls from cx2341x module. */
+ hdw->mpeg_ctrl_info = kcalloc(MPEGDEF_COUNT,
+ sizeof(*(hdw->mpeg_ctrl_info)),
+ GFP_KERNEL);
+ if (!hdw->mpeg_ctrl_info) goto fail;
+ for (idx = 0; idx < MPEGDEF_COUNT; idx++) {
+ cptr = hdw->controls + idx + CTRLDEF_COUNT;
+ ciptr = &(hdw->mpeg_ctrl_info[idx].info);
+ ciptr->desc = hdw->mpeg_ctrl_info[idx].desc;
+ ciptr->name = mpeg_ids[idx].strid;
+ ciptr->v4l_id = mpeg_ids[idx].id;
+ ciptr->skip_init = !0;
+ ciptr->get_value = ctrl_cx2341x_get;
+ ciptr->get_v4lflags = ctrl_cx2341x_getv4lflags;
+ ciptr->is_dirty = ctrl_cx2341x_is_dirty;
+ if (!idx) ciptr->clear_dirty = ctrl_cx2341x_clear_dirty;
+ qctrl.id = ciptr->v4l_id;
+ cx2341x_ctrl_query(&hdw->enc_ctl_state,&qctrl);
+ if (!(qctrl.flags & V4L2_CTRL_FLAG_READ_ONLY)) {
+ ciptr->set_value = ctrl_cx2341x_set;
+ }
+ strncpy(hdw->mpeg_ctrl_info[idx].desc,qctrl.name,
+ PVR2_CTLD_INFO_DESC_SIZE);
+ hdw->mpeg_ctrl_info[idx].desc[PVR2_CTLD_INFO_DESC_SIZE-1] = 0;
+ ciptr->default_value = qctrl.default_value;
+ switch (qctrl.type) {
+ default:
+ case V4L2_CTRL_TYPE_INTEGER:
+ ciptr->type = pvr2_ctl_int;
+ ciptr->def.type_int.min_value = qctrl.minimum;
+ ciptr->def.type_int.max_value = qctrl.maximum;
+ break;
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ ciptr->type = pvr2_ctl_bool;
+ break;
+ case V4L2_CTRL_TYPE_MENU:
+ ciptr->type = pvr2_ctl_enum;
+ ciptr->def.type_enum.value_names =
+ cx2341x_ctrl_get_menu(&hdw->enc_ctl_state,
+ ciptr->v4l_id);
+ for (cnt1 = 0;
+ ciptr->def.type_enum.value_names[cnt1] != NULL;
+ cnt1++) { }
+ ciptr->def.type_enum.count = cnt1;
+ break;
+ }
+ cptr->info = ciptr;
+ }
+
+ // Initialize video standard enum dynamic control
+ cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDENUM);
+ if (cptr) {
+ memcpy(&hdw->std_info_enum,cptr->info,
+ sizeof(hdw->std_info_enum));
+ cptr->info = &hdw->std_info_enum;
+
+ }
+ // Initialize control data regarding video standard masks
+ valid_std_mask = pvr2_std_get_usable();
+ for (idx = 0; idx < 32; idx++) {
+ if (!(valid_std_mask & (1 << idx))) continue;
+ cnt1 = pvr2_std_id_to_str(
+ hdw->std_mask_names[idx],
+ sizeof(hdw->std_mask_names[idx])-1,
+ 1 << idx);
+ hdw->std_mask_names[idx][cnt1] = 0;
+ }
+ cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDAVAIL);
+ if (cptr) {
+ memcpy(&hdw->std_info_avail,cptr->info,
+ sizeof(hdw->std_info_avail));
+ cptr->info = &hdw->std_info_avail;
+ hdw->std_info_avail.def.type_bitmask.bit_names =
+ hdw->std_mask_ptrs;
+ hdw->std_info_avail.def.type_bitmask.valid_bits =
+ valid_std_mask;
+ }
+ cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR);
+ if (cptr) {
+ memcpy(&hdw->std_info_cur,cptr->info,
+ sizeof(hdw->std_info_cur));
+ cptr->info = &hdw->std_info_cur;
+ hdw->std_info_cur.def.type_bitmask.bit_names =
+ hdw->std_mask_ptrs;
+ hdw->std_info_avail.def.type_bitmask.valid_bits =
+ valid_std_mask;
+ }
+
+ hdw->cropcap_stale = !0;
+ hdw->eeprom_addr = -1;
+ hdw->unit_number = -1;
+ hdw->v4l_minor_number_video = -1;
+ hdw->v4l_minor_number_vbi = -1;
+ hdw->v4l_minor_number_radio = -1;
+ hdw->ctl_write_buffer = kmalloc(PVR2_CTL_BUFFSIZE,GFP_KERNEL);
+ if (!hdw->ctl_write_buffer) goto fail;
+ hdw->ctl_read_buffer = kmalloc(PVR2_CTL_BUFFSIZE,GFP_KERNEL);
+ if (!hdw->ctl_read_buffer) goto fail;
+ hdw->ctl_write_urb = usb_alloc_urb(0,GFP_KERNEL);
+ if (!hdw->ctl_write_urb) goto fail;
+ hdw->ctl_read_urb = usb_alloc_urb(0,GFP_KERNEL);
+ if (!hdw->ctl_read_urb) goto fail;
+
+ if (v4l2_device_register(&intf->dev, &hdw->v4l2_dev) != 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Error registering with v4l core, giving up");
+ goto fail;
+ }
+ mutex_lock(&pvr2_unit_mtx); do {
+ for (idx = 0; idx < PVR_NUM; idx++) {
+ if (unit_pointers[idx]) continue;
+ hdw->unit_number = idx;
+ unit_pointers[idx] = hdw;
+ break;
+ }
+ } while (0); mutex_unlock(&pvr2_unit_mtx);
+
+ cnt1 = 0;
+ cnt2 = scnprintf(hdw->name+cnt1,sizeof(hdw->name)-cnt1,"pvrusb2");
+ cnt1 += cnt2;
+ if (hdw->unit_number >= 0) {
+ cnt2 = scnprintf(hdw->name+cnt1,sizeof(hdw->name)-cnt1,"_%c",
+ ('a' + hdw->unit_number));
+ cnt1 += cnt2;
+ }
+ if (cnt1 >= sizeof(hdw->name)) cnt1 = sizeof(hdw->name)-1;
+ hdw->name[cnt1] = 0;
+
+ hdw->workqueue = create_singlethread_workqueue(hdw->name);
+ INIT_WORK(&hdw->workpoll,pvr2_hdw_worker_poll);
+
+ pvr2_trace(PVR2_TRACE_INIT,"Driver unit number is %d, name is %s",
+ hdw->unit_number,hdw->name);
+
+ hdw->tuner_type = -1;
+ hdw->flag_ok = !0;
+
+ hdw->usb_intf = intf;
+ hdw->usb_dev = usb_dev;
+
+ usb_make_path(hdw->usb_dev, hdw->bus_info, sizeof(hdw->bus_info));
+
+ ifnum = hdw->usb_intf->cur_altsetting->desc.bInterfaceNumber;
+ usb_set_interface(hdw->usb_dev,ifnum,0);
+
+ mutex_init(&hdw->ctl_lock_mutex);
+ mutex_init(&hdw->big_lock_mutex);
+
+ return hdw;
+ fail:
+ if (hdw) {
+ del_timer_sync(&hdw->quiescent_timer);
+ del_timer_sync(&hdw->decoder_stabilization_timer);
+ del_timer_sync(&hdw->encoder_run_timer);
+ del_timer_sync(&hdw->encoder_wait_timer);
+ if (hdw->workqueue) {
+ flush_workqueue(hdw->workqueue);
+ destroy_workqueue(hdw->workqueue);
+ hdw->workqueue = NULL;
+ }
+ usb_free_urb(hdw->ctl_read_urb);
+ usb_free_urb(hdw->ctl_write_urb);
+ kfree(hdw->ctl_read_buffer);
+ kfree(hdw->ctl_write_buffer);
+ kfree(hdw->controls);
+ kfree(hdw->mpeg_ctrl_info);
+ kfree(hdw->std_defs);
+ kfree(hdw->std_enum_names);
+ kfree(hdw);
+ }
+ return NULL;
+}
+
+
+/* Remove _all_ associations between this driver and the underlying USB
+ layer. */
+static void pvr2_hdw_remove_usb_stuff(struct pvr2_hdw *hdw)
+{
+ if (hdw->flag_disconnected) return;
+ pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_remove_usb_stuff: hdw=%p",hdw);
+ if (hdw->ctl_read_urb) {
+ usb_kill_urb(hdw->ctl_read_urb);
+ usb_free_urb(hdw->ctl_read_urb);
+ hdw->ctl_read_urb = NULL;
+ }
+ if (hdw->ctl_write_urb) {
+ usb_kill_urb(hdw->ctl_write_urb);
+ usb_free_urb(hdw->ctl_write_urb);
+ hdw->ctl_write_urb = NULL;
+ }
+ if (hdw->ctl_read_buffer) {
+ kfree(hdw->ctl_read_buffer);
+ hdw->ctl_read_buffer = NULL;
+ }
+ if (hdw->ctl_write_buffer) {
+ kfree(hdw->ctl_write_buffer);
+ hdw->ctl_write_buffer = NULL;
+ }
+ hdw->flag_disconnected = !0;
+ /* If we don't do this, then there will be a dangling struct device
+ reference to our disappearing device persisting inside the V4L
+ core... */
+ v4l2_device_disconnect(&hdw->v4l2_dev);
+ hdw->usb_dev = NULL;
+ hdw->usb_intf = NULL;
+ pvr2_hdw_render_useless(hdw);
+}
+
+
+/* Destroy hardware interaction structure */
+void pvr2_hdw_destroy(struct pvr2_hdw *hdw)
+{
+ if (!hdw) return;
+ pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_destroy: hdw=%p",hdw);
+ if (hdw->workqueue) {
+ flush_workqueue(hdw->workqueue);
+ destroy_workqueue(hdw->workqueue);
+ hdw->workqueue = NULL;
+ }
+ del_timer_sync(&hdw->quiescent_timer);
+ del_timer_sync(&hdw->decoder_stabilization_timer);
+ del_timer_sync(&hdw->encoder_run_timer);
+ del_timer_sync(&hdw->encoder_wait_timer);
+ if (hdw->fw_buffer) {
+ kfree(hdw->fw_buffer);
+ hdw->fw_buffer = NULL;
+ }
+ if (hdw->vid_stream) {
+ pvr2_stream_destroy(hdw->vid_stream);
+ hdw->vid_stream = NULL;
+ }
+ pvr2_i2c_core_done(hdw);
+ v4l2_device_unregister(&hdw->v4l2_dev);
+ pvr2_hdw_remove_usb_stuff(hdw);
+ mutex_lock(&pvr2_unit_mtx); do {
+ if ((hdw->unit_number >= 0) &&
+ (hdw->unit_number < PVR_NUM) &&
+ (unit_pointers[hdw->unit_number] == hdw)) {
+ unit_pointers[hdw->unit_number] = NULL;
+ }
+ } while (0); mutex_unlock(&pvr2_unit_mtx);
+ kfree(hdw->controls);
+ kfree(hdw->mpeg_ctrl_info);
+ kfree(hdw->std_defs);
+ kfree(hdw->std_enum_names);
+ kfree(hdw);
+}
+
+
+int pvr2_hdw_dev_ok(struct pvr2_hdw *hdw)
+{
+ return (hdw && hdw->flag_ok);
+}
+
+
+/* Called when hardware has been unplugged */
+void pvr2_hdw_disconnect(struct pvr2_hdw *hdw)
+{
+ pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_disconnect(hdw=%p)",hdw);
+ LOCK_TAKE(hdw->big_lock);
+ LOCK_TAKE(hdw->ctl_lock);
+ pvr2_hdw_remove_usb_stuff(hdw);
+ LOCK_GIVE(hdw->ctl_lock);
+ LOCK_GIVE(hdw->big_lock);
+}
+
+
+// Attempt to autoselect an appropriate value for std_enum_cur given
+// whatever is currently in std_mask_cur
+static void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw)
+{
+ unsigned int idx;
+ for (idx = 1; idx < hdw->std_enum_cnt; idx++) {
+ if (hdw->std_defs[idx-1].id == hdw->std_mask_cur) {
+ hdw->std_enum_cur = idx;
+ return;
+ }
+ }
+ hdw->std_enum_cur = 0;
+}
+
+
+// Calculate correct set of enumerated standards based on currently known
+// set of available standards bits.
+static void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw)
+{
+ struct v4l2_standard *newstd;
+ unsigned int std_cnt;
+ unsigned int idx;
+
+ newstd = pvr2_std_create_enum(&std_cnt,hdw->std_mask_avail);
+
+ if (hdw->std_defs) {
+ kfree(hdw->std_defs);
+ hdw->std_defs = NULL;
+ }
+ hdw->std_enum_cnt = 0;
+ if (hdw->std_enum_names) {
+ kfree(hdw->std_enum_names);
+ hdw->std_enum_names = NULL;
+ }
+
+ if (!std_cnt) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "WARNING: Failed to identify any viable standards");
+ }
+
+ /* Set up the dynamic control for this standard */
+ hdw->std_enum_names = kmalloc(sizeof(char *)*(std_cnt+1),GFP_KERNEL);
+ if (hdw->std_enum_names) {
+ hdw->std_enum_names[0] = "none";
+ for (idx = 0; idx < std_cnt; idx++)
+ hdw->std_enum_names[idx+1] = newstd[idx].name;
+ hdw->std_info_enum.def.type_enum.value_names =
+ hdw->std_enum_names;
+ hdw->std_info_enum.def.type_enum.count = std_cnt+1;
+ } else {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "WARNING: Failed to alloc memory for names");
+ hdw->std_info_enum.def.type_enum.value_names = NULL;
+ hdw->std_info_enum.def.type_enum.count = 0;
+ }
+ hdw->std_defs = newstd;
+ hdw->std_enum_cnt = std_cnt+1;
+ hdw->std_enum_cur = 0;
+ hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail;
+}
+
+
+int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw,
+ struct v4l2_standard *std,
+ unsigned int idx)
+{
+ int ret = -EINVAL;
+ if (!idx) return ret;
+ LOCK_TAKE(hdw->big_lock); do {
+ if (idx >= hdw->std_enum_cnt) break;
+ idx--;
+ memcpy(std,hdw->std_defs+idx,sizeof(*std));
+ ret = 0;
+ } while (0); LOCK_GIVE(hdw->big_lock);
+ return ret;
+}
+
+
+/* Get the number of defined controls */
+unsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *hdw)
+{
+ return hdw->control_cnt;
+}
+
+
+/* Retrieve a control handle given its index (0..count-1) */
+struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_index(struct pvr2_hdw *hdw,
+ unsigned int idx)
+{
+ if (idx >= hdw->control_cnt) return NULL;
+ return hdw->controls + idx;
+}
+
+
+/* Retrieve a control handle given its index (0..count-1) */
+struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_id(struct pvr2_hdw *hdw,
+ unsigned int ctl_id)
+{
+ struct pvr2_ctrl *cptr;
+ unsigned int idx;
+ int i;
+
+ /* This could be made a lot more efficient, but for now... */
+ for (idx = 0; idx < hdw->control_cnt; idx++) {
+ cptr = hdw->controls + idx;
+ i = cptr->info->internal_id;
+ if (i && (i == ctl_id)) return cptr;
+ }
+ return NULL;
+}
+
+
+/* Given a V4L ID, retrieve the control structure associated with it. */
+struct pvr2_ctrl *pvr2_hdw_get_ctrl_v4l(struct pvr2_hdw *hdw,unsigned int ctl_id)
+{
+ struct pvr2_ctrl *cptr;
+ unsigned int idx;
+ int i;
+
+ /* This could be made a lot more efficient, but for now... */
+ for (idx = 0; idx < hdw->control_cnt; idx++) {
+ cptr = hdw->controls + idx;
+ i = cptr->info->v4l_id;
+ if (i && (i == ctl_id)) return cptr;
+ }
+ return NULL;
+}
+
+
+/* Given a V4L ID for its immediate predecessor, retrieve the control
+ structure associated with it. */
+struct pvr2_ctrl *pvr2_hdw_get_ctrl_nextv4l(struct pvr2_hdw *hdw,
+ unsigned int ctl_id)
+{
+ struct pvr2_ctrl *cptr,*cp2;
+ unsigned int idx;
+ int i;
+
+ /* This could be made a lot more efficient, but for now... */
+ cp2 = NULL;
+ for (idx = 0; idx < hdw->control_cnt; idx++) {
+ cptr = hdw->controls + idx;
+ i = cptr->info->v4l_id;
+ if (!i) continue;
+ if (i <= ctl_id) continue;
+ if (cp2 && (cp2->info->v4l_id < i)) continue;
+ cp2 = cptr;
+ }
+ return cp2;
+ return NULL;
+}
+
+
+static const char *get_ctrl_typename(enum pvr2_ctl_type tp)
+{
+ switch (tp) {
+ case pvr2_ctl_int: return "integer";
+ case pvr2_ctl_enum: return "enum";
+ case pvr2_ctl_bool: return "boolean";
+ case pvr2_ctl_bitmask: return "bitmask";
+ }
+ return "";
+}
+
+
+static void pvr2_subdev_set_control(struct pvr2_hdw *hdw, int id,
+ const char *name, int val)
+{
+ struct v4l2_control ctrl;
+ pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 %s=%d", name, val);
+ memset(&ctrl, 0, sizeof(ctrl));
+ ctrl.id = id;
+ ctrl.value = val;
+ v4l2_device_call_all(&hdw->v4l2_dev, 0, core, s_ctrl, &ctrl);
+}
+
+#define PVR2_SUBDEV_SET_CONTROL(hdw, id, lab) \
+ if ((hdw)->lab##_dirty || (hdw)->force_dirty) { \
+ pvr2_subdev_set_control(hdw, id, #lab, (hdw)->lab##_val); \
+ }
+
+int pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw, v4l2_std_id *std)
+{
+ v4l2_device_call_all(&hdw->v4l2_dev, 0,
+ video, querystd, std);
+ return 0;
+}
+
+/* Execute whatever commands are required to update the state of all the
+ sub-devices so that they match our current control values. */
+static void pvr2_subdev_update(struct pvr2_hdw *hdw)
+{
+ struct v4l2_subdev *sd;
+ unsigned int id;
+ pvr2_subdev_update_func fp;
+
+ pvr2_trace(PVR2_TRACE_CHIPS, "subdev update...");
+
+ if (hdw->tuner_updated || hdw->force_dirty) {
+ struct tuner_setup setup;
+ pvr2_trace(PVR2_TRACE_CHIPS, "subdev tuner set_type(%d)",
+ hdw->tuner_type);
+ if (((int)(hdw->tuner_type)) >= 0) {
+ memset(&setup, 0, sizeof(setup));
+ setup.addr = ADDR_UNSET;
+ setup.type = hdw->tuner_type;
+ setup.mode_mask = T_RADIO | T_ANALOG_TV;
+ v4l2_device_call_all(&hdw->v4l2_dev, 0,
+ tuner, s_type_addr, &setup);
+ }
+ }
+
+ if (hdw->input_dirty || hdw->std_dirty || hdw->force_dirty) {
+ pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_standard");
+ if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
+ v4l2_device_call_all(&hdw->v4l2_dev, 0,
+ tuner, s_radio);
+ } else {
+ v4l2_std_id vs;
+ vs = hdw->std_mask_cur;
+ v4l2_device_call_all(&hdw->v4l2_dev, 0,
+ core, s_std, vs);
+ pvr2_hdw_cx25840_vbi_hack(hdw);
+ }
+ hdw->tuner_signal_stale = !0;
+ hdw->cropcap_stale = !0;
+ }
+
+ PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_BRIGHTNESS, brightness);
+ PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_CONTRAST, contrast);
+ PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_SATURATION, saturation);
+ PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_HUE, hue);
+ PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_MUTE, mute);
+ PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_VOLUME, volume);
+ PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_BALANCE, balance);
+ PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_BASS, bass);
+ PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_TREBLE, treble);
+
+ if (hdw->input_dirty || hdw->audiomode_dirty || hdw->force_dirty) {
+ struct v4l2_tuner vt;
+ memset(&vt, 0, sizeof(vt));
+ vt.type = (hdw->input_val == PVR2_CVAL_INPUT_RADIO) ?
+ V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ vt.audmode = hdw->audiomode_val;
+ v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, s_tuner, &vt);
+ }
+
+ if (hdw->freqDirty || hdw->force_dirty) {
+ unsigned long fv;
+ struct v4l2_frequency freq;
+ fv = pvr2_hdw_get_cur_freq(hdw);
+ pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_freq(%lu)", fv);
+ if (hdw->tuner_signal_stale) pvr2_hdw_status_poll(hdw);
+ memset(&freq, 0, sizeof(freq));
+ if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) {
+ /* ((fv * 1000) / 62500) */
+ freq.frequency = (fv * 2) / 125;
+ } else {
+ freq.frequency = fv / 62500;
+ }
+ /* tuner-core currently doesn't seem to care about this, but
+ let's set it anyway for completeness. */
+ if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
+ freq.type = V4L2_TUNER_RADIO;
+ } else {
+ freq.type = V4L2_TUNER_ANALOG_TV;
+ }
+ freq.tuner = 0;
+ v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner,
+ s_frequency, &freq);
+ }
+
+ if (hdw->res_hor_dirty || hdw->res_ver_dirty || hdw->force_dirty) {
+ struct v4l2_mbus_framefmt fmt;
+ memset(&fmt, 0, sizeof(fmt));
+ fmt.width = hdw->res_hor_val;
+ fmt.height = hdw->res_ver_val;
+ fmt.code = V4L2_MBUS_FMT_FIXED;
+ pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_size(%dx%d)",
+ fmt.width, fmt.height);
+ v4l2_device_call_all(&hdw->v4l2_dev, 0, video, s_mbus_fmt, &fmt);
+ }
+
+ if (hdw->srate_dirty || hdw->force_dirty) {
+ u32 val;
+ pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_audio %d",
+ hdw->srate_val);
+ switch (hdw->srate_val) {
+ default:
+ case V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000:
+ val = 48000;
+ break;
+ case V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100:
+ val = 44100;
+ break;
+ case V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000:
+ val = 32000;
+ break;
+ }
+ v4l2_device_call_all(&hdw->v4l2_dev, 0,
+ audio, s_clock_freq, val);
+ }
+
+ /* Unable to set crop parameters; there is apparently no equivalent
+ for VIDIOC_S_CROP */
+
+ v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev) {
+ id = sd->grp_id;
+ if (id >= ARRAY_SIZE(pvr2_module_update_functions)) continue;
+ fp = pvr2_module_update_functions[id];
+ if (!fp) continue;
+ (*fp)(hdw, sd);
+ }
+
+ if (hdw->tuner_signal_stale || hdw->cropcap_stale) {
+ pvr2_hdw_status_poll(hdw);
+ }
+}
+
+
+/* Figure out if we need to commit control changes. If so, mark internal
+ state flags to indicate this fact and return true. Otherwise do nothing
+ else and return false. */
+static int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw)
+{
+ unsigned int idx;
+ struct pvr2_ctrl *cptr;
+ int value;
+ int commit_flag = hdw->force_dirty;
+ char buf[100];
+ unsigned int bcnt,ccnt;
+
+ for (idx = 0; idx < hdw->control_cnt; idx++) {
+ cptr = hdw->controls + idx;
+ if (!cptr->info->is_dirty) continue;
+ if (!cptr->info->is_dirty(cptr)) continue;
+ commit_flag = !0;
+
+ if (!(pvrusb2_debug & PVR2_TRACE_CTL)) continue;
+ bcnt = scnprintf(buf,sizeof(buf),"\"%s\" <-- ",
+ cptr->info->name);
+ value = 0;
+ cptr->info->get_value(cptr,&value);
+ pvr2_ctrl_value_to_sym_internal(cptr,~0,value,
+ buf+bcnt,
+ sizeof(buf)-bcnt,&ccnt);
+ bcnt += ccnt;
+ bcnt += scnprintf(buf+bcnt,sizeof(buf)-bcnt," <%s>",
+ get_ctrl_typename(cptr->info->type));
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*--TRACE_COMMIT--*/ %.*s",
+ bcnt,buf);
+ }
+
+ if (!commit_flag) {
+ /* Nothing has changed */
+ return 0;
+ }
+
+ hdw->state_pipeline_config = 0;
+ trace_stbit("state_pipeline_config",hdw->state_pipeline_config);
+ pvr2_hdw_state_sched(hdw);
+
+ return !0;
+}
+
+
+/* Perform all operations needed to commit all control changes. This must
+ be performed in synchronization with the pipeline state and is thus
+ expected to be called as part of the driver's worker thread. Return
+ true if commit successful, otherwise return false to indicate that
+ commit isn't possible at this time. */
+static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw)
+{
+ unsigned int idx;
+ struct pvr2_ctrl *cptr;
+ int disruptive_change;
+
+ if (hdw->input_dirty && hdw->state_pathway_ok &&
+ (((hdw->input_val == PVR2_CVAL_INPUT_DTV) ?
+ PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG) !=
+ hdw->pathway_state)) {
+ /* Change of mode being asked for... */
+ hdw->state_pathway_ok = 0;
+ trace_stbit("state_pathway_ok", hdw->state_pathway_ok);
+ }
+ if (!hdw->state_pathway_ok) {
+ /* Can't commit anything until pathway is ok. */
+ return 0;
+ }
+
+ /* Handle some required side effects when the video standard is
+ changed.... */
+ if (hdw->std_dirty) {
+ int nvres;
+ int gop_size;
+ if (hdw->std_mask_cur & V4L2_STD_525_60) {
+ nvres = 480;
+ gop_size = 15;
+ } else {
+ nvres = 576;
+ gop_size = 12;
+ }
+ /* Rewrite the vertical resolution to be appropriate to the
+ video standard that has been selected. */
+ if (nvres != hdw->res_ver_val) {
+ hdw->res_ver_val = nvres;
+ hdw->res_ver_dirty = !0;
+ }
+ /* Rewrite the GOP size to be appropriate to the video
+ standard that has been selected. */
+ if (gop_size != hdw->enc_ctl_state.video_gop_size) {
+ struct v4l2_ext_controls cs;
+ struct v4l2_ext_control c1;
+ memset(&cs, 0, sizeof(cs));
+ memset(&c1, 0, sizeof(c1));
+ cs.controls = &c1;
+ cs.count = 1;
+ c1.id = V4L2_CID_MPEG_VIDEO_GOP_SIZE;
+ c1.value = gop_size;
+ cx2341x_ext_ctrls(&hdw->enc_ctl_state, 0, &cs,
+ VIDIOC_S_EXT_CTRLS);
+ }
+ }
+
+ /* The broadcast decoder can only scale down, so if
+ * res_*_dirty && crop window < output format ==> enlarge crop.
+ *
+ * The mpeg encoder receives fields of res_hor_val dots and
+ * res_ver_val halflines. Limits: hor<=720, ver<=576.
+ */
+ if (hdw->res_hor_dirty && hdw->cropw_val < hdw->res_hor_val) {
+ hdw->cropw_val = hdw->res_hor_val;
+ hdw->cropw_dirty = !0;
+ } else if (hdw->cropw_dirty) {
+ hdw->res_hor_dirty = !0; /* must rescale */
+ hdw->res_hor_val = min(720, hdw->cropw_val);
+ }
+ if (hdw->res_ver_dirty && hdw->croph_val < hdw->res_ver_val) {
+ hdw->croph_val = hdw->res_ver_val;
+ hdw->croph_dirty = !0;
+ } else if (hdw->croph_dirty) {
+ int nvres = hdw->std_mask_cur & V4L2_STD_525_60 ? 480 : 576;
+ hdw->res_ver_dirty = !0;
+ hdw->res_ver_val = min(nvres, hdw->croph_val);
+ }
+
+ /* If any of the below has changed, then we can't do the update
+ while the pipeline is running. Pipeline must be paused first
+ and decoder -> encoder connection be made quiescent before we
+ can proceed. */
+ disruptive_change =
+ (hdw->std_dirty ||
+ hdw->enc_unsafe_stale ||
+ hdw->srate_dirty ||
+ hdw->res_ver_dirty ||
+ hdw->res_hor_dirty ||
+ hdw->cropw_dirty ||
+ hdw->croph_dirty ||
+ hdw->input_dirty ||
+ (hdw->active_stream_type != hdw->desired_stream_type));
+ if (disruptive_change && !hdw->state_pipeline_idle) {
+ /* Pipeline is not idle; we can't proceed. Arrange to
+ cause pipeline to stop so that we can try this again
+ later.... */
+ hdw->state_pipeline_pause = !0;
+ return 0;
+ }
+
+ if (hdw->srate_dirty) {
+ /* Write new sample rate into control structure since
+ * the master copy is stale. We must track srate
+ * separate from the mpeg control structure because
+ * other logic also uses this value. */
+ struct v4l2_ext_controls cs;
+ struct v4l2_ext_control c1;
+ memset(&cs,0,sizeof(cs));
+ memset(&c1,0,sizeof(c1));
+ cs.controls = &c1;
+ cs.count = 1;
+ c1.id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ;
+ c1.value = hdw->srate_val;
+ cx2341x_ext_ctrls(&hdw->enc_ctl_state, 0, &cs,VIDIOC_S_EXT_CTRLS);
+ }
+
+ if (hdw->active_stream_type != hdw->desired_stream_type) {
+ /* Handle any side effects of stream config here */
+ hdw->active_stream_type = hdw->desired_stream_type;
+ }
+
+ if (hdw->hdw_desc->signal_routing_scheme ==
+ PVR2_ROUTING_SCHEME_GOTVIEW) {
+ u32 b;
+ /* Handle GOTVIEW audio switching */
+ pvr2_hdw_gpio_get_out(hdw,&b);
+ if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
+ /* Set GPIO 11 */
+ pvr2_hdw_gpio_chg_out(hdw,(1 << 11),~0);
+ } else {
+ /* Clear GPIO 11 */
+ pvr2_hdw_gpio_chg_out(hdw,(1 << 11),0);
+ }
+ }
+
+ /* Check and update state for all sub-devices. */
+ pvr2_subdev_update(hdw);
+
+ hdw->tuner_updated = 0;
+ hdw->force_dirty = 0;
+ for (idx = 0; idx < hdw->control_cnt; idx++) {
+ cptr = hdw->controls + idx;
+ if (!cptr->info->clear_dirty) continue;
+ cptr->info->clear_dirty(cptr);
+ }
+
+ if ((hdw->pathway_state == PVR2_PATHWAY_ANALOG) &&
+ hdw->state_encoder_run) {
+ /* If encoder isn't running or it can't be touched, then
+ this will get worked out later when we start the
+ encoder. */
+ if (pvr2_encoder_adjust(hdw) < 0) return !0;
+ }
+
+ hdw->state_pipeline_config = !0;
+ /* Hardware state may have changed in a way to cause the cropping
+ capabilities to have changed. So mark it stale, which will
+ cause a later re-fetch. */
+ trace_stbit("state_pipeline_config",hdw->state_pipeline_config);
+ return !0;
+}
+
+
+int pvr2_hdw_commit_ctl(struct pvr2_hdw *hdw)
+{
+ int fl;
+ LOCK_TAKE(hdw->big_lock);
+ fl = pvr2_hdw_commit_setup(hdw);
+ LOCK_GIVE(hdw->big_lock);
+ if (!fl) return 0;
+ return pvr2_hdw_wait(hdw,0);
+}
+
+
+static void pvr2_hdw_worker_poll(struct work_struct *work)
+{
+ int fl = 0;
+ struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,workpoll);
+ LOCK_TAKE(hdw->big_lock); do {
+ fl = pvr2_hdw_state_eval(hdw);
+ } while (0); LOCK_GIVE(hdw->big_lock);
+ if (fl && hdw->state_func) {
+ hdw->state_func(hdw->state_data);
+ }
+}
+
+
+static int pvr2_hdw_wait(struct pvr2_hdw *hdw,int state)
+{
+ return wait_event_interruptible(
+ hdw->state_wait_data,
+ (hdw->state_stale == 0) &&
+ (!state || (hdw->master_state != state)));
+}
+
+
+/* Return name for this driver instance */
+const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw)
+{
+ return hdw->name;
+}
+
+
+const char *pvr2_hdw_get_desc(struct pvr2_hdw *hdw)
+{
+ return hdw->hdw_desc->description;
+}
+
+
+const char *pvr2_hdw_get_type(struct pvr2_hdw *hdw)
+{
+ return hdw->hdw_desc->shortname;
+}
+
+
+int pvr2_hdw_is_hsm(struct pvr2_hdw *hdw)
+{
+ int result;
+ LOCK_TAKE(hdw->ctl_lock); do {
+ hdw->cmd_buffer[0] = FX2CMD_GET_USB_SPEED;
+ result = pvr2_send_request(hdw,
+ hdw->cmd_buffer,1,
+ hdw->cmd_buffer,1);
+ if (result < 0) break;
+ result = (hdw->cmd_buffer[0] != 0);
+ } while(0); LOCK_GIVE(hdw->ctl_lock);
+ return result;
+}
+
+
+/* Execute poll of tuner status */
+void pvr2_hdw_execute_tuner_poll(struct pvr2_hdw *hdw)
+{
+ LOCK_TAKE(hdw->big_lock); do {
+ pvr2_hdw_status_poll(hdw);
+ } while (0); LOCK_GIVE(hdw->big_lock);
+}
+
+
+static int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw)
+{
+ if (!hdw->cropcap_stale) {
+ return 0;
+ }
+ pvr2_hdw_status_poll(hdw);
+ if (hdw->cropcap_stale) {
+ return -EIO;
+ }
+ return 0;
+}
+
+
+/* Return information about cropping capabilities */
+int pvr2_hdw_get_cropcap(struct pvr2_hdw *hdw, struct v4l2_cropcap *pp)
+{
+ int stat = 0;
+ LOCK_TAKE(hdw->big_lock);
+ stat = pvr2_hdw_check_cropcap(hdw);
+ if (!stat) {
+ memcpy(pp, &hdw->cropcap_info, sizeof(hdw->cropcap_info));
+ }
+ LOCK_GIVE(hdw->big_lock);
+ return stat;
+}
+
+
+/* Return information about the tuner */
+int pvr2_hdw_get_tuner_status(struct pvr2_hdw *hdw,struct v4l2_tuner *vtp)
+{
+ LOCK_TAKE(hdw->big_lock); do {
+ if (hdw->tuner_signal_stale) {
+ pvr2_hdw_status_poll(hdw);
+ }
+ memcpy(vtp,&hdw->tuner_signal_info,sizeof(struct v4l2_tuner));
+ } while (0); LOCK_GIVE(hdw->big_lock);
+ return 0;
+}
+
+
+/* Get handle to video output stream */
+struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *hp)
+{
+ return hp->vid_stream;
+}
+
+
+void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw)
+{
+ int nr = pvr2_hdw_get_unit_number(hdw);
+ LOCK_TAKE(hdw->big_lock); do {
+ printk(KERN_INFO "pvrusb2: ================= START STATUS CARD #%d =================\n", nr);
+ v4l2_device_call_all(&hdw->v4l2_dev, 0, core, log_status);
+ pvr2_trace(PVR2_TRACE_INFO,"cx2341x config:");
+ cx2341x_log_status(&hdw->enc_ctl_state, "pvrusb2");
+ pvr2_hdw_state_log_state(hdw);
+ printk(KERN_INFO "pvrusb2: ================== END STATUS CARD #%d ==================\n", nr);
+ } while (0); LOCK_GIVE(hdw->big_lock);
+}
+
+
+/* Grab EEPROM contents, needed for direct method. */
+#define EEPROM_SIZE 8192
+#define trace_eeprom(...) pvr2_trace(PVR2_TRACE_EEPROM,__VA_ARGS__)
+static u8 *pvr2_full_eeprom_fetch(struct pvr2_hdw *hdw)
+{
+ struct i2c_msg msg[2];
+ u8 *eeprom;
+ u8 iadd[2];
+ u8 addr;
+ u16 eepromSize;
+ unsigned int offs;
+ int ret;
+ int mode16 = 0;
+ unsigned pcnt,tcnt;
+ eeprom = kmalloc(EEPROM_SIZE,GFP_KERNEL);
+ if (!eeprom) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Failed to allocate memory"
+ " required to read eeprom");
+ return NULL;
+ }
+
+ trace_eeprom("Value for eeprom addr from controller was 0x%x",
+ hdw->eeprom_addr);
+ addr = hdw->eeprom_addr;
+ /* Seems that if the high bit is set, then the *real* eeprom
+ address is shifted right now bit position (noticed this in
+ newer PVR USB2 hardware) */
+ if (addr & 0x80) addr >>= 1;
+
+ /* FX2 documentation states that a 16bit-addressed eeprom is
+ expected if the I2C address is an odd number (yeah, this is
+ strange but it's what they do) */
+ mode16 = (addr & 1);
+ eepromSize = (mode16 ? EEPROM_SIZE : 256);
+ trace_eeprom("Examining %d byte eeprom at location 0x%x"
+ " using %d bit addressing",eepromSize,addr,
+ mode16 ? 16 : 8);
+
+ msg[0].addr = addr;
+ msg[0].flags = 0;
+ msg[0].len = mode16 ? 2 : 1;
+ msg[0].buf = iadd;
+ msg[1].addr = addr;
+ msg[1].flags = I2C_M_RD;
+
+ /* We have to do the actual eeprom data fetch ourselves, because
+ (1) we're only fetching part of the eeprom, and (2) if we were
+ getting the whole thing our I2C driver can't grab it in one
+ pass - which is what tveeprom is otherwise going to attempt */
+ memset(eeprom,0,EEPROM_SIZE);
+ for (tcnt = 0; tcnt < EEPROM_SIZE; tcnt += pcnt) {
+ pcnt = 16;
+ if (pcnt + tcnt > EEPROM_SIZE) pcnt = EEPROM_SIZE-tcnt;
+ offs = tcnt + (eepromSize - EEPROM_SIZE);
+ if (mode16) {
+ iadd[0] = offs >> 8;
+ iadd[1] = offs;
+ } else {
+ iadd[0] = offs;
+ }
+ msg[1].len = pcnt;
+ msg[1].buf = eeprom+tcnt;
+ if ((ret = i2c_transfer(&hdw->i2c_adap,
+ msg,ARRAY_SIZE(msg))) != 2) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "eeprom fetch set offs err=%d",ret);
+ kfree(eeprom);
+ return NULL;
+ }
+ }
+ return eeprom;
+}
+
+
+void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw,
+ int mode,
+ int enable_flag)
+{
+ int ret;
+ u16 address;
+ unsigned int pipe;
+ LOCK_TAKE(hdw->big_lock); do {
+ if ((hdw->fw_buffer == NULL) == !enable_flag) break;
+
+ if (!enable_flag) {
+ pvr2_trace(PVR2_TRACE_FIRMWARE,
+ "Cleaning up after CPU firmware fetch");
+ kfree(hdw->fw_buffer);
+ hdw->fw_buffer = NULL;
+ hdw->fw_size = 0;
+ if (hdw->fw_cpu_flag) {
+ /* Now release the CPU. It will disconnect
+ and reconnect later. */
+ pvr2_hdw_cpureset_assert(hdw,0);
+ }
+ break;
+ }
+
+ hdw->fw_cpu_flag = (mode != 2);
+ if (hdw->fw_cpu_flag) {
+ hdw->fw_size = (mode == 1) ? 0x4000 : 0x2000;
+ pvr2_trace(PVR2_TRACE_FIRMWARE,
+ "Preparing to suck out CPU firmware"
+ " (size=%u)", hdw->fw_size);
+ hdw->fw_buffer = kzalloc(hdw->fw_size,GFP_KERNEL);
+ if (!hdw->fw_buffer) {
+ hdw->fw_size = 0;
+ break;
+ }
+
+ /* We have to hold the CPU during firmware upload. */
+ pvr2_hdw_cpureset_assert(hdw,1);
+
+ /* download the firmware from address 0000-1fff in 2048
+ (=0x800) bytes chunk. */
+
+ pvr2_trace(PVR2_TRACE_FIRMWARE,
+ "Grabbing CPU firmware");
+ pipe = usb_rcvctrlpipe(hdw->usb_dev, 0);
+ for(address = 0; address < hdw->fw_size;
+ address += 0x800) {
+ ret = usb_control_msg(hdw->usb_dev,pipe,
+ 0xa0,0xc0,
+ address,0,
+ hdw->fw_buffer+address,
+ 0x800,HZ);
+ if (ret < 0) break;
+ }
+
+ pvr2_trace(PVR2_TRACE_FIRMWARE,
+ "Done grabbing CPU firmware");
+ } else {
+ pvr2_trace(PVR2_TRACE_FIRMWARE,
+ "Sucking down EEPROM contents");
+ hdw->fw_buffer = pvr2_full_eeprom_fetch(hdw);
+ if (!hdw->fw_buffer) {
+ pvr2_trace(PVR2_TRACE_FIRMWARE,
+ "EEPROM content suck failed.");
+ break;
+ }
+ hdw->fw_size = EEPROM_SIZE;
+ pvr2_trace(PVR2_TRACE_FIRMWARE,
+ "Done sucking down EEPROM contents");
+ }
+
+ } while (0); LOCK_GIVE(hdw->big_lock);
+}
+
+
+/* Return true if we're in a mode for retrieval CPU firmware */
+int pvr2_hdw_cpufw_get_enabled(struct pvr2_hdw *hdw)
+{
+ return hdw->fw_buffer != NULL;
+}
+
+
+int pvr2_hdw_cpufw_get(struct pvr2_hdw *hdw,unsigned int offs,
+ char *buf,unsigned int cnt)
+{
+ int ret = -EINVAL;
+ LOCK_TAKE(hdw->big_lock); do {
+ if (!buf) break;
+ if (!cnt) break;
+
+ if (!hdw->fw_buffer) {
+ ret = -EIO;
+ break;
+ }
+
+ if (offs >= hdw->fw_size) {
+ pvr2_trace(PVR2_TRACE_FIRMWARE,
+ "Read firmware data offs=%d EOF",
+ offs);
+ ret = 0;
+ break;
+ }
+
+ if (offs + cnt > hdw->fw_size) cnt = hdw->fw_size - offs;
+
+ memcpy(buf,hdw->fw_buffer+offs,cnt);
+
+ pvr2_trace(PVR2_TRACE_FIRMWARE,
+ "Read firmware data offs=%d cnt=%d",
+ offs,cnt);
+ ret = cnt;
+ } while (0); LOCK_GIVE(hdw->big_lock);
+
+ return ret;
+}
+
+
+int pvr2_hdw_v4l_get_minor_number(struct pvr2_hdw *hdw,
+ enum pvr2_v4l_type index)
+{
+ switch (index) {
+ case pvr2_v4l_type_video: return hdw->v4l_minor_number_video;
+ case pvr2_v4l_type_vbi: return hdw->v4l_minor_number_vbi;
+ case pvr2_v4l_type_radio: return hdw->v4l_minor_number_radio;
+ default: return -1;
+ }
+}
+
+
+/* Store a v4l minor device number */
+void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *hdw,
+ enum pvr2_v4l_type index,int v)
+{
+ switch (index) {
+ case pvr2_v4l_type_video: hdw->v4l_minor_number_video = v;
+ case pvr2_v4l_type_vbi: hdw->v4l_minor_number_vbi = v;
+ case pvr2_v4l_type_radio: hdw->v4l_minor_number_radio = v;
+ default: break;
+ }
+}
+
+
+static void pvr2_ctl_write_complete(struct urb *urb)
+{
+ struct pvr2_hdw *hdw = urb->context;
+ hdw->ctl_write_pend_flag = 0;
+ if (hdw->ctl_read_pend_flag) return;
+ complete(&hdw->ctl_done);
+}
+
+
+static void pvr2_ctl_read_complete(struct urb *urb)
+{
+ struct pvr2_hdw *hdw = urb->context;
+ hdw->ctl_read_pend_flag = 0;
+ if (hdw->ctl_write_pend_flag) return;
+ complete(&hdw->ctl_done);
+}
+
+
+static void pvr2_ctl_timeout(unsigned long data)
+{
+ struct pvr2_hdw *hdw = (struct pvr2_hdw *)data;
+ if (hdw->ctl_write_pend_flag || hdw->ctl_read_pend_flag) {
+ hdw->ctl_timeout_flag = !0;
+ if (hdw->ctl_write_pend_flag)
+ usb_unlink_urb(hdw->ctl_write_urb);
+ if (hdw->ctl_read_pend_flag)
+ usb_unlink_urb(hdw->ctl_read_urb);
+ }
+}
+
+
+/* Issue a command and get a response from the device. This extended
+ version includes a probe flag (which if set means that device errors
+ should not be logged or treated as fatal) and a timeout in jiffies.
+ This can be used to non-lethally probe the health of endpoint 1. */
+static int pvr2_send_request_ex(struct pvr2_hdw *hdw,
+ unsigned int timeout,int probe_fl,
+ void *write_data,unsigned int write_len,
+ void *read_data,unsigned int read_len)
+{
+ unsigned int idx;
+ int status = 0;
+ struct timer_list timer;
+ if (!hdw->ctl_lock_held) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Attempted to execute control transfer"
+ " without lock!!");
+ return -EDEADLK;
+ }
+ if (!hdw->flag_ok && !probe_fl) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Attempted to execute control transfer"
+ " when device not ok");
+ return -EIO;
+ }
+ if (!(hdw->ctl_read_urb && hdw->ctl_write_urb)) {
+ if (!probe_fl) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Attempted to execute control transfer"
+ " when USB is disconnected");
+ }
+ return -ENOTTY;
+ }
+
+ /* Ensure that we have sane parameters */
+ if (!write_data) write_len = 0;
+ if (!read_data) read_len = 0;
+ if (write_len > PVR2_CTL_BUFFSIZE) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Attempted to execute %d byte"
+ " control-write transfer (limit=%d)",
+ write_len,PVR2_CTL_BUFFSIZE);
+ return -EINVAL;
+ }
+ if (read_len > PVR2_CTL_BUFFSIZE) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Attempted to execute %d byte"
+ " control-read transfer (limit=%d)",
+ write_len,PVR2_CTL_BUFFSIZE);
+ return -EINVAL;
+ }
+ if ((!write_len) && (!read_len)) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Attempted to execute null control transfer?");
+ return -EINVAL;
+ }
+
+
+ hdw->cmd_debug_state = 1;
+ if (write_len) {
+ hdw->cmd_debug_code = ((unsigned char *)write_data)[0];
+ } else {
+ hdw->cmd_debug_code = 0;
+ }
+ hdw->cmd_debug_write_len = write_len;
+ hdw->cmd_debug_read_len = read_len;
+
+ /* Initialize common stuff */
+ init_completion(&hdw->ctl_done);
+ hdw->ctl_timeout_flag = 0;
+ hdw->ctl_write_pend_flag = 0;
+ hdw->ctl_read_pend_flag = 0;
+ init_timer(&timer);
+ timer.expires = jiffies + timeout;
+ timer.data = (unsigned long)hdw;
+ timer.function = pvr2_ctl_timeout;
+
+ if (write_len) {
+ hdw->cmd_debug_state = 2;
+ /* Transfer write data to internal buffer */
+ for (idx = 0; idx < write_len; idx++) {
+ hdw->ctl_write_buffer[idx] =
+ ((unsigned char *)write_data)[idx];
+ }
+ /* Initiate a write request */
+ usb_fill_bulk_urb(hdw->ctl_write_urb,
+ hdw->usb_dev,
+ usb_sndbulkpipe(hdw->usb_dev,
+ PVR2_CTL_WRITE_ENDPOINT),
+ hdw->ctl_write_buffer,
+ write_len,
+ pvr2_ctl_write_complete,
+ hdw);
+ hdw->ctl_write_urb->actual_length = 0;
+ hdw->ctl_write_pend_flag = !0;
+ status = usb_submit_urb(hdw->ctl_write_urb,GFP_KERNEL);
+ if (status < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Failed to submit write-control"
+ " URB status=%d",status);
+ hdw->ctl_write_pend_flag = 0;
+ goto done;
+ }
+ }
+
+ if (read_len) {
+ hdw->cmd_debug_state = 3;
+ memset(hdw->ctl_read_buffer,0x43,read_len);
+ /* Initiate a read request */
+ usb_fill_bulk_urb(hdw->ctl_read_urb,
+ hdw->usb_dev,
+ usb_rcvbulkpipe(hdw->usb_dev,
+ PVR2_CTL_READ_ENDPOINT),
+ hdw->ctl_read_buffer,
+ read_len,
+ pvr2_ctl_read_complete,
+ hdw);
+ hdw->ctl_read_urb->actual_length = 0;
+ hdw->ctl_read_pend_flag = !0;
+ status = usb_submit_urb(hdw->ctl_read_urb,GFP_KERNEL);
+ if (status < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Failed to submit read-control"
+ " URB status=%d",status);
+ hdw->ctl_read_pend_flag = 0;
+ goto done;
+ }
+ }
+
+ /* Start timer */
+ add_timer(&timer);
+
+ /* Now wait for all I/O to complete */
+ hdw->cmd_debug_state = 4;
+ while (hdw->ctl_write_pend_flag || hdw->ctl_read_pend_flag) {
+ wait_for_completion(&hdw->ctl_done);
+ }
+ hdw->cmd_debug_state = 5;
+
+ /* Stop timer */
+ del_timer_sync(&timer);
+
+ hdw->cmd_debug_state = 6;
+ status = 0;
+
+ if (hdw->ctl_timeout_flag) {
+ status = -ETIMEDOUT;
+ if (!probe_fl) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Timed out control-write");
+ }
+ goto done;
+ }
+
+ if (write_len) {
+ /* Validate results of write request */
+ if ((hdw->ctl_write_urb->status != 0) &&
+ (hdw->ctl_write_urb->status != -ENOENT) &&
+ (hdw->ctl_write_urb->status != -ESHUTDOWN) &&
+ (hdw->ctl_write_urb->status != -ECONNRESET)) {
+ /* USB subsystem is reporting some kind of failure
+ on the write */
+ status = hdw->ctl_write_urb->status;
+ if (!probe_fl) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "control-write URB failure,"
+ " status=%d",
+ status);
+ }
+ goto done;
+ }
+ if (hdw->ctl_write_urb->actual_length < write_len) {
+ /* Failed to write enough data */
+ status = -EIO;
+ if (!probe_fl) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "control-write URB short,"
+ " expected=%d got=%d",
+ write_len,
+ hdw->ctl_write_urb->actual_length);
+ }
+ goto done;
+ }
+ }
+ if (read_len) {
+ /* Validate results of read request */
+ if ((hdw->ctl_read_urb->status != 0) &&
+ (hdw->ctl_read_urb->status != -ENOENT) &&
+ (hdw->ctl_read_urb->status != -ESHUTDOWN) &&
+ (hdw->ctl_read_urb->status != -ECONNRESET)) {
+ /* USB subsystem is reporting some kind of failure
+ on the read */
+ status = hdw->ctl_read_urb->status;
+ if (!probe_fl) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "control-read URB failure,"
+ " status=%d",
+ status);
+ }
+ goto done;
+ }
+ if (hdw->ctl_read_urb->actual_length < read_len) {
+ /* Failed to read enough data */
+ status = -EIO;
+ if (!probe_fl) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "control-read URB short,"
+ " expected=%d got=%d",
+ read_len,
+ hdw->ctl_read_urb->actual_length);
+ }
+ goto done;
+ }
+ /* Transfer retrieved data out from internal buffer */
+ for (idx = 0; idx < read_len; idx++) {
+ ((unsigned char *)read_data)[idx] =
+ hdw->ctl_read_buffer[idx];
+ }
+ }
+
+ done:
+
+ hdw->cmd_debug_state = 0;
+ if ((status < 0) && (!probe_fl)) {
+ pvr2_hdw_render_useless(hdw);
+ }
+ return status;
+}
+
+
+int pvr2_send_request(struct pvr2_hdw *hdw,
+ void *write_data,unsigned int write_len,
+ void *read_data,unsigned int read_len)
+{
+ return pvr2_send_request_ex(hdw,HZ*4,0,
+ write_data,write_len,
+ read_data,read_len);
+}
+
+
+static int pvr2_issue_simple_cmd(struct pvr2_hdw *hdw,u32 cmdcode)
+{
+ int ret;
+ unsigned int cnt = 1;
+ unsigned int args = 0;
+ LOCK_TAKE(hdw->ctl_lock);
+ hdw->cmd_buffer[0] = cmdcode & 0xffu;
+ args = (cmdcode >> 8) & 0xffu;
+ args = (args > 2) ? 2 : args;
+ if (args) {
+ cnt += args;
+ hdw->cmd_buffer[1] = (cmdcode >> 16) & 0xffu;
+ if (args > 1) {
+ hdw->cmd_buffer[2] = (cmdcode >> 24) & 0xffu;
+ }
+ }
+ if (pvrusb2_debug & PVR2_TRACE_INIT) {
+ unsigned int idx;
+ unsigned int ccnt,bcnt;
+ char tbuf[50];
+ cmdcode &= 0xffu;
+ bcnt = 0;
+ ccnt = scnprintf(tbuf+bcnt,
+ sizeof(tbuf)-bcnt,
+ "Sending FX2 command 0x%x",cmdcode);
+ bcnt += ccnt;
+ for (idx = 0; idx < ARRAY_SIZE(pvr2_fx2cmd_desc); idx++) {
+ if (pvr2_fx2cmd_desc[idx].id == cmdcode) {
+ ccnt = scnprintf(tbuf+bcnt,
+ sizeof(tbuf)-bcnt,
+ " \"%s\"",
+ pvr2_fx2cmd_desc[idx].desc);
+ bcnt += ccnt;
+ break;
+ }
+ }
+ if (args) {
+ ccnt = scnprintf(tbuf+bcnt,
+ sizeof(tbuf)-bcnt,
+ " (%u",hdw->cmd_buffer[1]);
+ bcnt += ccnt;
+ if (args > 1) {
+ ccnt = scnprintf(tbuf+bcnt,
+ sizeof(tbuf)-bcnt,
+ ",%u",hdw->cmd_buffer[2]);
+ bcnt += ccnt;
+ }
+ ccnt = scnprintf(tbuf+bcnt,
+ sizeof(tbuf)-bcnt,
+ ")");
+ bcnt += ccnt;
+ }
+ pvr2_trace(PVR2_TRACE_INIT,"%.*s",bcnt,tbuf);
+ }
+ ret = pvr2_send_request(hdw,hdw->cmd_buffer,cnt,NULL,0);
+ LOCK_GIVE(hdw->ctl_lock);
+ return ret;
+}
+
+
+int pvr2_write_register(struct pvr2_hdw *hdw, u16 reg, u32 data)
+{
+ int ret;
+
+ LOCK_TAKE(hdw->ctl_lock);
+
+ hdw->cmd_buffer[0] = FX2CMD_REG_WRITE; /* write register prefix */
+ PVR2_DECOMPOSE_LE(hdw->cmd_buffer,1,data);
+ hdw->cmd_buffer[5] = 0;
+ hdw->cmd_buffer[6] = (reg >> 8) & 0xff;
+ hdw->cmd_buffer[7] = reg & 0xff;
+
+
+ ret = pvr2_send_request(hdw, hdw->cmd_buffer, 8, hdw->cmd_buffer, 0);
+
+ LOCK_GIVE(hdw->ctl_lock);
+
+ return ret;
+}
+
+
+static int pvr2_read_register(struct pvr2_hdw *hdw, u16 reg, u32 *data)
+{
+ int ret = 0;
+
+ LOCK_TAKE(hdw->ctl_lock);
+
+ hdw->cmd_buffer[0] = FX2CMD_REG_READ; /* read register prefix */
+ hdw->cmd_buffer[1] = 0;
+ hdw->cmd_buffer[2] = 0;
+ hdw->cmd_buffer[3] = 0;
+ hdw->cmd_buffer[4] = 0;
+ hdw->cmd_buffer[5] = 0;
+ hdw->cmd_buffer[6] = (reg >> 8) & 0xff;
+ hdw->cmd_buffer[7] = reg & 0xff;
+
+ ret |= pvr2_send_request(hdw, hdw->cmd_buffer, 8, hdw->cmd_buffer, 4);
+ *data = PVR2_COMPOSE_LE(hdw->cmd_buffer,0);
+
+ LOCK_GIVE(hdw->ctl_lock);
+
+ return ret;
+}
+
+
+void pvr2_hdw_render_useless(struct pvr2_hdw *hdw)
+{
+ if (!hdw->flag_ok) return;
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Device being rendered inoperable");
+ if (hdw->vid_stream) {
+ pvr2_stream_setup(hdw->vid_stream,NULL,0,0);
+ }
+ hdw->flag_ok = 0;
+ trace_stbit("flag_ok",hdw->flag_ok);
+ pvr2_hdw_state_sched(hdw);
+}
+
+
+void pvr2_hdw_device_reset(struct pvr2_hdw *hdw)
+{
+ int ret;
+ pvr2_trace(PVR2_TRACE_INIT,"Performing a device reset...");
+ ret = usb_lock_device_for_reset(hdw->usb_dev,NULL);
+ if (ret == 0) {
+ ret = usb_reset_device(hdw->usb_dev);
+ usb_unlock_device(hdw->usb_dev);
+ } else {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Failed to lock USB device ret=%d",ret);
+ }
+ if (init_pause_msec) {
+ pvr2_trace(PVR2_TRACE_INFO,
+ "Waiting %u msec for hardware to settle",
+ init_pause_msec);
+ msleep(init_pause_msec);
+ }
+
+}
+
+
+void pvr2_hdw_cpureset_assert(struct pvr2_hdw *hdw,int val)
+{
+ char *da;
+ unsigned int pipe;
+ int ret;
+
+ if (!hdw->usb_dev) return;
+
+ da = kmalloc(16, GFP_KERNEL);
+
+ if (da == NULL) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Unable to allocate memory to control CPU reset");
+ return;
+ }
+
+ pvr2_trace(PVR2_TRACE_INIT,"cpureset_assert(%d)",val);
+
+ da[0] = val ? 0x01 : 0x00;
+
+ /* Write the CPUCS register on the 8051. The lsb of the register
+ is the reset bit; a 1 asserts reset while a 0 clears it. */
+ pipe = usb_sndctrlpipe(hdw->usb_dev, 0);
+ ret = usb_control_msg(hdw->usb_dev,pipe,0xa0,0x40,0xe600,0,da,1,HZ);
+ if (ret < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "cpureset_assert(%d) error=%d",val,ret);
+ pvr2_hdw_render_useless(hdw);
+ }
+
+ kfree(da);
+}
+
+
+int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *hdw)
+{
+ return pvr2_issue_simple_cmd(hdw,FX2CMD_DEEP_RESET);
+}
+
+
+int pvr2_hdw_cmd_powerup(struct pvr2_hdw *hdw)
+{
+ return pvr2_issue_simple_cmd(hdw,FX2CMD_POWER_ON);
+}
+
+
+int pvr2_hdw_cmd_powerdown(struct pvr2_hdw *hdw)
+{
+ return pvr2_issue_simple_cmd(hdw,FX2CMD_POWER_OFF);
+}
+
+
+int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw)
+{
+ pvr2_trace(PVR2_TRACE_INIT,
+ "Requesting decoder reset");
+ if (hdw->decoder_client_id) {
+ v4l2_device_call_all(&hdw->v4l2_dev, hdw->decoder_client_id,
+ core, reset, 0);
+ pvr2_hdw_cx25840_vbi_hack(hdw);
+ return 0;
+ }
+ pvr2_trace(PVR2_TRACE_INIT,
+ "Unable to reset decoder: nothing attached");
+ return -ENOTTY;
+}
+
+
+static int pvr2_hdw_cmd_hcw_demod_reset(struct pvr2_hdw *hdw, int onoff)
+{
+ hdw->flag_ok = !0;
+ return pvr2_issue_simple_cmd(hdw,
+ FX2CMD_HCW_DEMOD_RESETIN |
+ (1 << 8) |
+ ((onoff ? 1 : 0) << 16));
+}
+
+
+static int pvr2_hdw_cmd_onair_fe_power_ctrl(struct pvr2_hdw *hdw, int onoff)
+{
+ hdw->flag_ok = !0;
+ return pvr2_issue_simple_cmd(hdw,(onoff ?
+ FX2CMD_ONAIR_DTV_POWER_ON :
+ FX2CMD_ONAIR_DTV_POWER_OFF));
+}
+
+
+static int pvr2_hdw_cmd_onair_digital_path_ctrl(struct pvr2_hdw *hdw,
+ int onoff)
+{
+ return pvr2_issue_simple_cmd(hdw,(onoff ?
+ FX2CMD_ONAIR_DTV_STREAMING_ON :
+ FX2CMD_ONAIR_DTV_STREAMING_OFF));
+}
+
+
+static void pvr2_hdw_cmd_modeswitch(struct pvr2_hdw *hdw,int digitalFl)
+{
+ int cmode;
+ /* Compare digital/analog desired setting with current setting. If
+ they don't match, fix it... */
+ cmode = (digitalFl ? PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG);
+ if (cmode == hdw->pathway_state) {
+ /* They match; nothing to do */
+ return;
+ }
+
+ switch (hdw->hdw_desc->digital_control_scheme) {
+ case PVR2_DIGITAL_SCHEME_HAUPPAUGE:
+ pvr2_hdw_cmd_hcw_demod_reset(hdw,digitalFl);
+ if (cmode == PVR2_PATHWAY_ANALOG) {
+ /* If moving to analog mode, also force the decoder
+ to reset. If no decoder is attached, then it's
+ ok to ignore this because if/when the decoder
+ attaches, it will reset itself at that time. */
+ pvr2_hdw_cmd_decoder_reset(hdw);
+ }
+ break;
+ case PVR2_DIGITAL_SCHEME_ONAIR:
+ /* Supposedly we should always have the power on whether in
+ digital or analog mode. But for now do what appears to
+ work... */
+ pvr2_hdw_cmd_onair_fe_power_ctrl(hdw,digitalFl);
+ break;
+ default: break;
+ }
+
+ pvr2_hdw_untrip_unlocked(hdw);
+ hdw->pathway_state = cmode;
+}
+
+
+static void pvr2_led_ctrl_hauppauge(struct pvr2_hdw *hdw, int onoff)
+{
+ /* change some GPIO data
+ *
+ * note: bit d7 of dir appears to control the LED,
+ * so we shut it off here.
+ *
+ */
+ if (onoff) {
+ pvr2_hdw_gpio_chg_dir(hdw, 0xffffffff, 0x00000481);
+ } else {
+ pvr2_hdw_gpio_chg_dir(hdw, 0xffffffff, 0x00000401);
+ }
+ pvr2_hdw_gpio_chg_out(hdw, 0xffffffff, 0x00000000);
+}
+
+
+typedef void (*led_method_func)(struct pvr2_hdw *,int);
+
+static led_method_func led_methods[] = {
+ [PVR2_LED_SCHEME_HAUPPAUGE] = pvr2_led_ctrl_hauppauge,
+};
+
+
+/* Toggle LED */
+static void pvr2_led_ctrl(struct pvr2_hdw *hdw,int onoff)
+{
+ unsigned int scheme_id;
+ led_method_func fp;
+
+ if ((!onoff) == (!hdw->led_on)) return;
+
+ hdw->led_on = onoff != 0;
+
+ scheme_id = hdw->hdw_desc->led_scheme;
+ if (scheme_id < ARRAY_SIZE(led_methods)) {
+ fp = led_methods[scheme_id];
+ } else {
+ fp = NULL;
+ }
+
+ if (fp) (*fp)(hdw,onoff);
+}
+
+
+/* Stop / start video stream transport */
+static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl)
+{
+ int ret;
+
+ /* If we're in analog mode, then just issue the usual analog
+ command. */
+ if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) {
+ return pvr2_issue_simple_cmd(hdw,
+ (runFl ?
+ FX2CMD_STREAMING_ON :
+ FX2CMD_STREAMING_OFF));
+ /*Note: Not reached */
+ }
+
+ if (hdw->pathway_state != PVR2_PATHWAY_DIGITAL) {
+ /* Whoops, we don't know what mode we're in... */
+ return -EINVAL;
+ }
+
+ /* To get here we have to be in digital mode. The mechanism here
+ is unfortunately different for different vendors. So we switch
+ on the device's digital scheme attribute in order to figure out
+ what to do. */
+ switch (hdw->hdw_desc->digital_control_scheme) {
+ case PVR2_DIGITAL_SCHEME_HAUPPAUGE:
+ return pvr2_issue_simple_cmd(hdw,
+ (runFl ?
+ FX2CMD_HCW_DTV_STREAMING_ON :
+ FX2CMD_HCW_DTV_STREAMING_OFF));
+ case PVR2_DIGITAL_SCHEME_ONAIR:
+ ret = pvr2_issue_simple_cmd(hdw,
+ (runFl ?
+ FX2CMD_STREAMING_ON :
+ FX2CMD_STREAMING_OFF));
+ if (ret) return ret;
+ return pvr2_hdw_cmd_onair_digital_path_ctrl(hdw,runFl);
+ default:
+ return -EINVAL;
+ }
+}
+
+
+/* Evaluate whether or not state_pathway_ok can change */
+static int state_eval_pathway_ok(struct pvr2_hdw *hdw)
+{
+ if (hdw->state_pathway_ok) {
+ /* Nothing to do if pathway is already ok */
+ return 0;
+ }
+ if (!hdw->state_pipeline_idle) {
+ /* Not allowed to change anything if pipeline is not idle */
+ return 0;
+ }
+ pvr2_hdw_cmd_modeswitch(hdw,hdw->input_val == PVR2_CVAL_INPUT_DTV);
+ hdw->state_pathway_ok = !0;
+ trace_stbit("state_pathway_ok",hdw->state_pathway_ok);
+ return !0;
+}
+
+
+/* Evaluate whether or not state_encoder_ok can change */
+static int state_eval_encoder_ok(struct pvr2_hdw *hdw)
+{
+ if (hdw->state_encoder_ok) return 0;
+ if (hdw->flag_tripped) return 0;
+ if (hdw->state_encoder_run) return 0;
+ if (hdw->state_encoder_config) return 0;
+ if (hdw->state_decoder_run) return 0;
+ if (hdw->state_usbstream_run) return 0;
+ if (hdw->pathway_state == PVR2_PATHWAY_DIGITAL) {
+ if (!hdw->hdw_desc->flag_digital_requires_cx23416) return 0;
+ } else if (hdw->pathway_state != PVR2_PATHWAY_ANALOG) {
+ return 0;
+ }
+
+ if (pvr2_upload_firmware2(hdw) < 0) {
+ hdw->flag_tripped = !0;
+ trace_stbit("flag_tripped",hdw->flag_tripped);
+ return !0;
+ }
+ hdw->state_encoder_ok = !0;
+ trace_stbit("state_encoder_ok",hdw->state_encoder_ok);
+ return !0;
+}
+
+
+/* Evaluate whether or not state_encoder_config can change */
+static int state_eval_encoder_config(struct pvr2_hdw *hdw)
+{
+ if (hdw->state_encoder_config) {
+ if (hdw->state_encoder_ok) {
+ if (hdw->state_pipeline_req &&
+ !hdw->state_pipeline_pause) return 0;
+ }
+ hdw->state_encoder_config = 0;
+ hdw->state_encoder_waitok = 0;
+ trace_stbit("state_encoder_waitok",hdw->state_encoder_waitok);
+ /* paranoia - solve race if timer just completed */
+ del_timer_sync(&hdw->encoder_wait_timer);
+ } else {
+ if (!hdw->state_pathway_ok ||
+ (hdw->pathway_state != PVR2_PATHWAY_ANALOG) ||
+ !hdw->state_encoder_ok ||
+ !hdw->state_pipeline_idle ||
+ hdw->state_pipeline_pause ||
+ !hdw->state_pipeline_req ||
+ !hdw->state_pipeline_config) {
+ /* We must reset the enforced wait interval if
+ anything has happened that might have disturbed
+ the encoder. This should be a rare case. */
+ if (timer_pending(&hdw->encoder_wait_timer)) {
+ del_timer_sync(&hdw->encoder_wait_timer);
+ }
+ if (hdw->state_encoder_waitok) {
+ /* Must clear the state - therefore we did
+ something to a state bit and must also
+ return true. */
+ hdw->state_encoder_waitok = 0;
+ trace_stbit("state_encoder_waitok",
+ hdw->state_encoder_waitok);
+ return !0;
+ }
+ return 0;
+ }
+ if (!hdw->state_encoder_waitok) {
+ if (!timer_pending(&hdw->encoder_wait_timer)) {
+ /* waitok flag wasn't set and timer isn't
+ running. Check flag once more to avoid
+ a race then start the timer. This is
+ the point when we measure out a minimal
+ quiet interval before doing something to
+ the encoder. */
+ if (!hdw->state_encoder_waitok) {
+ hdw->encoder_wait_timer.expires =
+ jiffies +
+ (HZ * TIME_MSEC_ENCODER_WAIT
+ / 1000);
+ add_timer(&hdw->encoder_wait_timer);
+ }
+ }
+ /* We can't continue until we know we have been
+ quiet for the interval measured by this
+ timer. */
+ return 0;
+ }
+ pvr2_encoder_configure(hdw);
+ if (hdw->state_encoder_ok) hdw->state_encoder_config = !0;
+ }
+ trace_stbit("state_encoder_config",hdw->state_encoder_config);
+ return !0;
+}
+
+
+/* Return true if the encoder should not be running. */
+static int state_check_disable_encoder_run(struct pvr2_hdw *hdw)
+{
+ if (!hdw->state_encoder_ok) {
+ /* Encoder isn't healthy at the moment, so stop it. */
+ return !0;
+ }
+ if (!hdw->state_pathway_ok) {
+ /* Mode is not understood at the moment (i.e. it wants to
+ change), so encoder must be stopped. */
+ return !0;
+ }
+
+ switch (hdw->pathway_state) {
+ case PVR2_PATHWAY_ANALOG:
+ if (!hdw->state_decoder_run) {
+ /* We're in analog mode and the decoder is not
+ running; thus the encoder should be stopped as
+ well. */
+ return !0;
+ }
+ break;
+ case PVR2_PATHWAY_DIGITAL:
+ if (hdw->state_encoder_runok) {
+ /* This is a funny case. We're in digital mode so
+ really the encoder should be stopped. However
+ if it really is running, only kill it after
+ runok has been set. This gives a chance for the
+ onair quirk to function (encoder must run
+ briefly first, at least once, before onair
+ digital streaming can work). */
+ return !0;
+ }
+ break;
+ default:
+ /* Unknown mode; so encoder should be stopped. */
+ return !0;
+ }
+
+ /* If we get here, we haven't found a reason to stop the
+ encoder. */
+ return 0;
+}
+
+
+/* Return true if the encoder should be running. */
+static int state_check_enable_encoder_run(struct pvr2_hdw *hdw)
+{
+ if (!hdw->state_encoder_ok) {
+ /* Don't run the encoder if it isn't healthy... */
+ return 0;
+ }
+ if (!hdw->state_pathway_ok) {
+ /* Don't run the encoder if we don't (yet) know what mode
+ we need to be in... */
+ return 0;
+ }
+
+ switch (hdw->pathway_state) {
+ case PVR2_PATHWAY_ANALOG:
+ if (hdw->state_decoder_run && hdw->state_decoder_ready) {
+ /* In analog mode, if the decoder is running, then
+ run the encoder. */
+ return !0;
+ }
+ break;
+ case PVR2_PATHWAY_DIGITAL:
+ if ((hdw->hdw_desc->digital_control_scheme ==
+ PVR2_DIGITAL_SCHEME_ONAIR) &&
+ !hdw->state_encoder_runok) {
+ /* This is a quirk. OnAir hardware won't stream
+ digital until the encoder has been run at least
+ once, for a minimal period of time (empiricially
+ measured to be 1/4 second). So if we're on
+ OnAir hardware and the encoder has never been
+ run at all, then start the encoder. Normal
+ state machine logic in the driver will
+ automatically handle the remaining bits. */
+ return !0;
+ }
+ break;
+ default:
+ /* For completeness (unknown mode; encoder won't run ever) */
+ break;
+ }
+ /* If we get here, then we haven't found any reason to run the
+ encoder, so don't run it. */
+ return 0;
+}
+
+
+/* Evaluate whether or not state_encoder_run can change */
+static int state_eval_encoder_run(struct pvr2_hdw *hdw)
+{
+ if (hdw->state_encoder_run) {
+ if (!state_check_disable_encoder_run(hdw)) return 0;
+ if (hdw->state_encoder_ok) {
+ del_timer_sync(&hdw->encoder_run_timer);
+ if (pvr2_encoder_stop(hdw) < 0) return !0;
+ }
+ hdw->state_encoder_run = 0;
+ } else {
+ if (!state_check_enable_encoder_run(hdw)) return 0;
+ if (pvr2_encoder_start(hdw) < 0) return !0;
+ hdw->state_encoder_run = !0;
+ if (!hdw->state_encoder_runok) {
+ hdw->encoder_run_timer.expires =
+ jiffies + (HZ * TIME_MSEC_ENCODER_OK / 1000);
+ add_timer(&hdw->encoder_run_timer);
+ }
+ }
+ trace_stbit("state_encoder_run",hdw->state_encoder_run);
+ return !0;
+}
+
+
+/* Timeout function for quiescent timer. */
+static void pvr2_hdw_quiescent_timeout(unsigned long data)
+{
+ struct pvr2_hdw *hdw = (struct pvr2_hdw *)data;
+ hdw->state_decoder_quiescent = !0;
+ trace_stbit("state_decoder_quiescent",hdw->state_decoder_quiescent);
+ hdw->state_stale = !0;
+ queue_work(hdw->workqueue,&hdw->workpoll);
+}
+
+
+/* Timeout function for decoder stabilization timer. */
+static void pvr2_hdw_decoder_stabilization_timeout(unsigned long data)
+{
+ struct pvr2_hdw *hdw = (struct pvr2_hdw *)data;
+ hdw->state_decoder_ready = !0;
+ trace_stbit("state_decoder_ready", hdw->state_decoder_ready);
+ hdw->state_stale = !0;
+ queue_work(hdw->workqueue, &hdw->workpoll);
+}
+
+
+/* Timeout function for encoder wait timer. */
+static void pvr2_hdw_encoder_wait_timeout(unsigned long data)
+{
+ struct pvr2_hdw *hdw = (struct pvr2_hdw *)data;
+ hdw->state_encoder_waitok = !0;
+ trace_stbit("state_encoder_waitok",hdw->state_encoder_waitok);
+ hdw->state_stale = !0;
+ queue_work(hdw->workqueue,&hdw->workpoll);
+}
+
+
+/* Timeout function for encoder run timer. */
+static void pvr2_hdw_encoder_run_timeout(unsigned long data)
+{
+ struct pvr2_hdw *hdw = (struct pvr2_hdw *)data;
+ if (!hdw->state_encoder_runok) {
+ hdw->state_encoder_runok = !0;
+ trace_stbit("state_encoder_runok",hdw->state_encoder_runok);
+ hdw->state_stale = !0;
+ queue_work(hdw->workqueue,&hdw->workpoll);
+ }
+}
+
+
+/* Evaluate whether or not state_decoder_run can change */
+static int state_eval_decoder_run(struct pvr2_hdw *hdw)
+{
+ if (hdw->state_decoder_run) {
+ if (hdw->state_encoder_ok) {
+ if (hdw->state_pipeline_req &&
+ !hdw->state_pipeline_pause &&
+ hdw->state_pathway_ok) return 0;
+ }
+ if (!hdw->flag_decoder_missed) {
+ pvr2_decoder_enable(hdw,0);
+ }
+ hdw->state_decoder_quiescent = 0;
+ hdw->state_decoder_run = 0;
+ /* paranoia - solve race if timer(s) just completed */
+ del_timer_sync(&hdw->quiescent_timer);
+ /* Kill the stabilization timer, in case we're killing the
+ encoder before the previous stabilization interval has
+ been properly timed. */
+ del_timer_sync(&hdw->decoder_stabilization_timer);
+ hdw->state_decoder_ready = 0;
+ } else {
+ if (!hdw->state_decoder_quiescent) {
+ if (!timer_pending(&hdw->quiescent_timer)) {
+ /* We don't do something about the
+ quiescent timer until right here because
+ we also want to catch cases where the
+ decoder was already not running (like
+ after initialization) as opposed to
+ knowing that we had just stopped it.
+ The second flag check is here to cover a
+ race - the timer could have run and set
+ this flag just after the previous check
+ but before we did the pending check. */
+ if (!hdw->state_decoder_quiescent) {
+ hdw->quiescent_timer.expires =
+ jiffies +
+ (HZ * TIME_MSEC_DECODER_WAIT
+ / 1000);
+ add_timer(&hdw->quiescent_timer);
+ }
+ }
+ /* Don't allow decoder to start again until it has
+ been quiesced first. This little detail should
+ hopefully further stabilize the encoder. */
+ return 0;
+ }
+ if (!hdw->state_pathway_ok ||
+ (hdw->pathway_state != PVR2_PATHWAY_ANALOG) ||
+ !hdw->state_pipeline_req ||
+ hdw->state_pipeline_pause ||
+ !hdw->state_pipeline_config ||
+ !hdw->state_encoder_config ||
+ !hdw->state_encoder_ok) return 0;
+ del_timer_sync(&hdw->quiescent_timer);
+ if (hdw->flag_decoder_missed) return 0;
+ if (pvr2_decoder_enable(hdw,!0) < 0) return 0;
+ hdw->state_decoder_quiescent = 0;
+ hdw->state_decoder_ready = 0;
+ hdw->state_decoder_run = !0;
+ if (hdw->decoder_client_id == PVR2_CLIENT_ID_SAA7115) {
+ hdw->decoder_stabilization_timer.expires =
+ jiffies +
+ (HZ * TIME_MSEC_DECODER_STABILIZATION_WAIT /
+ 1000);
+ add_timer(&hdw->decoder_stabilization_timer);
+ } else {
+ hdw->state_decoder_ready = !0;
+ }
+ }
+ trace_stbit("state_decoder_quiescent",hdw->state_decoder_quiescent);
+ trace_stbit("state_decoder_run",hdw->state_decoder_run);
+ trace_stbit("state_decoder_ready", hdw->state_decoder_ready);
+ return !0;
+}
+
+
+/* Evaluate whether or not state_usbstream_run can change */
+static int state_eval_usbstream_run(struct pvr2_hdw *hdw)
+{
+ if (hdw->state_usbstream_run) {
+ int fl = !0;
+ if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) {
+ fl = (hdw->state_encoder_ok &&
+ hdw->state_encoder_run);
+ } else if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) &&
+ (hdw->hdw_desc->flag_digital_requires_cx23416)) {
+ fl = hdw->state_encoder_ok;
+ }
+ if (fl &&
+ hdw->state_pipeline_req &&
+ !hdw->state_pipeline_pause &&
+ hdw->state_pathway_ok) {
+ return 0;
+ }
+ pvr2_hdw_cmd_usbstream(hdw,0);
+ hdw->state_usbstream_run = 0;
+ } else {
+ if (!hdw->state_pipeline_req ||
+ hdw->state_pipeline_pause ||
+ !hdw->state_pathway_ok) return 0;
+ if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) {
+ if (!hdw->state_encoder_ok ||
+ !hdw->state_encoder_run) return 0;
+ } else if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) &&
+ (hdw->hdw_desc->flag_digital_requires_cx23416)) {
+ if (!hdw->state_encoder_ok) return 0;
+ if (hdw->state_encoder_run) return 0;
+ if (hdw->hdw_desc->digital_control_scheme ==
+ PVR2_DIGITAL_SCHEME_ONAIR) {
+ /* OnAir digital receivers won't stream
+ unless the analog encoder has run first.
+ Why? I have no idea. But don't even
+ try until we know the analog side is
+ known to have run. */
+ if (!hdw->state_encoder_runok) return 0;
+ }
+ }
+ if (pvr2_hdw_cmd_usbstream(hdw,!0) < 0) return 0;
+ hdw->state_usbstream_run = !0;
+ }
+ trace_stbit("state_usbstream_run",hdw->state_usbstream_run);
+ return !0;
+}
+
+
+/* Attempt to configure pipeline, if needed */
+static int state_eval_pipeline_config(struct pvr2_hdw *hdw)
+{
+ if (hdw->state_pipeline_config ||
+ hdw->state_pipeline_pause) return 0;
+ pvr2_hdw_commit_execute(hdw);
+ return !0;
+}
+
+
+/* Update pipeline idle and pipeline pause tracking states based on other
+ inputs. This must be called whenever the other relevant inputs have
+ changed. */
+static int state_update_pipeline_state(struct pvr2_hdw *hdw)
+{
+ unsigned int st;
+ int updatedFl = 0;
+ /* Update pipeline state */
+ st = !(hdw->state_encoder_run ||
+ hdw->state_decoder_run ||
+ hdw->state_usbstream_run ||
+ (!hdw->state_decoder_quiescent));
+ if (!st != !hdw->state_pipeline_idle) {
+ hdw->state_pipeline_idle = st;
+ updatedFl = !0;
+ }
+ if (hdw->state_pipeline_idle && hdw->state_pipeline_pause) {
+ hdw->state_pipeline_pause = 0;
+ updatedFl = !0;
+ }
+ return updatedFl;
+}
+
+
+typedef int (*state_eval_func)(struct pvr2_hdw *);
+
+/* Set of functions to be run to evaluate various states in the driver. */
+static const state_eval_func eval_funcs[] = {
+ state_eval_pathway_ok,
+ state_eval_pipeline_config,
+ state_eval_encoder_ok,
+ state_eval_encoder_config,
+ state_eval_decoder_run,
+ state_eval_encoder_run,
+ state_eval_usbstream_run,
+};
+
+
+/* Process various states and return true if we did anything interesting. */
+static int pvr2_hdw_state_update(struct pvr2_hdw *hdw)
+{
+ unsigned int i;
+ int state_updated = 0;
+ int check_flag;
+
+ if (!hdw->state_stale) return 0;
+ if ((hdw->fw1_state != FW1_STATE_OK) ||
+ !hdw->flag_ok) {
+ hdw->state_stale = 0;
+ return !0;
+ }
+ /* This loop is the heart of the entire driver. It keeps trying to
+ evaluate various bits of driver state until nothing changes for
+ one full iteration. Each "bit of state" tracks some global
+ aspect of the driver, e.g. whether decoder should run, if
+ pipeline is configured, usb streaming is on, etc. We separately
+ evaluate each of those questions based on other driver state to
+ arrive at the correct running configuration. */
+ do {
+ check_flag = 0;
+ state_update_pipeline_state(hdw);
+ /* Iterate over each bit of state */
+ for (i = 0; (i<ARRAY_SIZE(eval_funcs)) && hdw->flag_ok; i++) {
+ if ((*eval_funcs[i])(hdw)) {
+ check_flag = !0;
+ state_updated = !0;
+ state_update_pipeline_state(hdw);
+ }
+ }
+ } while (check_flag && hdw->flag_ok);
+ hdw->state_stale = 0;
+ trace_stbit("state_stale",hdw->state_stale);
+ return state_updated;
+}
+
+
+static unsigned int print_input_mask(unsigned int msk,
+ char *buf,unsigned int acnt)
+{
+ unsigned int idx,ccnt;
+ unsigned int tcnt = 0;
+ for (idx = 0; idx < ARRAY_SIZE(control_values_input); idx++) {
+ if (!((1 << idx) & msk)) continue;
+ ccnt = scnprintf(buf+tcnt,
+ acnt-tcnt,
+ "%s%s",
+ (tcnt ? ", " : ""),
+ control_values_input[idx]);
+ tcnt += ccnt;
+ }
+ return tcnt;
+}
+
+
+static const char *pvr2_pathway_state_name(int id)
+{
+ switch (id) {
+ case PVR2_PATHWAY_ANALOG: return "analog";
+ case PVR2_PATHWAY_DIGITAL: return "digital";
+ default: return "unknown";
+ }
+}
+
+
+static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which,
+ char *buf,unsigned int acnt)
+{
+ switch (which) {
+ case 0:
+ return scnprintf(
+ buf,acnt,
+ "driver:%s%s%s%s%s <mode=%s>",
+ (hdw->flag_ok ? " <ok>" : " <fail>"),
+ (hdw->flag_init_ok ? " <init>" : " <uninitialized>"),
+ (hdw->flag_disconnected ? " <disconnected>" :
+ " <connected>"),
+ (hdw->flag_tripped ? " <tripped>" : ""),
+ (hdw->flag_decoder_missed ? " <no decoder>" : ""),
+ pvr2_pathway_state_name(hdw->pathway_state));
+
+ case 1:
+ return scnprintf(
+ buf,acnt,
+ "pipeline:%s%s%s%s",
+ (hdw->state_pipeline_idle ? " <idle>" : ""),
+ (hdw->state_pipeline_config ?
+ " <configok>" : " <stale>"),
+ (hdw->state_pipeline_req ? " <req>" : ""),
+ (hdw->state_pipeline_pause ? " <pause>" : ""));
+ case 2:
+ return scnprintf(
+ buf,acnt,
+ "worker:%s%s%s%s%s%s%s",
+ (hdw->state_decoder_run ?
+ (hdw->state_decoder_ready ?
+ "<decode:run>" : " <decode:start>") :
+ (hdw->state_decoder_quiescent ?
+ "" : " <decode:stop>")),
+ (hdw->state_decoder_quiescent ?
+ " <decode:quiescent>" : ""),
+ (hdw->state_encoder_ok ?
+ "" : " <encode:init>"),
+ (hdw->state_encoder_run ?
+ (hdw->state_encoder_runok ?
+ " <encode:run>" :
+ " <encode:firstrun>") :
+ (hdw->state_encoder_runok ?
+ " <encode:stop>" :
+ " <encode:virgin>")),
+ (hdw->state_encoder_config ?
+ " <encode:configok>" :
+ (hdw->state_encoder_waitok ?
+ "" : " <encode:waitok>")),
+ (hdw->state_usbstream_run ?
+ " <usb:run>" : " <usb:stop>"),
+ (hdw->state_pathway_ok ?
+ " <pathway:ok>" : ""));
+ case 3:
+ return scnprintf(
+ buf,acnt,
+ "state: %s",
+ pvr2_get_state_name(hdw->master_state));
+ case 4: {
+ unsigned int tcnt = 0;
+ unsigned int ccnt;
+
+ ccnt = scnprintf(buf,
+ acnt,
+ "Hardware supported inputs: ");
+ tcnt += ccnt;
+ tcnt += print_input_mask(hdw->input_avail_mask,
+ buf+tcnt,
+ acnt-tcnt);
+ if (hdw->input_avail_mask != hdw->input_allowed_mask) {
+ ccnt = scnprintf(buf+tcnt,
+ acnt-tcnt,
+ "; allowed inputs: ");
+ tcnt += ccnt;
+ tcnt += print_input_mask(hdw->input_allowed_mask,
+ buf+tcnt,
+ acnt-tcnt);
+ }
+ return tcnt;
+ }
+ case 5: {
+ struct pvr2_stream_stats stats;
+ if (!hdw->vid_stream) break;
+ pvr2_stream_get_stats(hdw->vid_stream,
+ &stats,
+ 0);
+ return scnprintf(
+ buf,acnt,
+ "Bytes streamed=%u"
+ " URBs: queued=%u idle=%u ready=%u"
+ " processed=%u failed=%u",
+ stats.bytes_processed,
+ stats.buffers_in_queue,
+ stats.buffers_in_idle,
+ stats.buffers_in_ready,
+ stats.buffers_processed,
+ stats.buffers_failed);
+ }
+ case 6: {
+ unsigned int id = hdw->ir_scheme_active;
+ return scnprintf(buf, acnt, "ir scheme: id=%d %s", id,
+ (id >= ARRAY_SIZE(ir_scheme_names) ?
+ "?" : ir_scheme_names[id]));
+ }
+ default: break;
+ }
+ return 0;
+}
+
+
+/* Generate report containing info about attached sub-devices and attached
+ i2c clients, including an indication of which attached i2c clients are
+ actually sub-devices. */
+static unsigned int pvr2_hdw_report_clients(struct pvr2_hdw *hdw,
+ char *buf, unsigned int acnt)
+{
+ struct v4l2_subdev *sd;
+ unsigned int tcnt = 0;
+ unsigned int ccnt;
+ struct i2c_client *client;
+ const char *p;
+ unsigned int id;
+
+ ccnt = scnprintf(buf, acnt, "Associated v4l2-subdev drivers and I2C clients:\n");
+ tcnt += ccnt;
+ v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev) {
+ id = sd->grp_id;
+ p = NULL;
+ if (id < ARRAY_SIZE(module_names)) p = module_names[id];
+ if (p) {
+ ccnt = scnprintf(buf + tcnt, acnt - tcnt, " %s:", p);
+ tcnt += ccnt;
+ } else {
+ ccnt = scnprintf(buf + tcnt, acnt - tcnt,
+ " (unknown id=%u):", id);
+ tcnt += ccnt;
+ }
+ client = v4l2_get_subdevdata(sd);
+ if (client) {
+ ccnt = scnprintf(buf + tcnt, acnt - tcnt,
+ " %s @ %02x\n", client->name,
+ client->addr);
+ tcnt += ccnt;
+ } else {
+ ccnt = scnprintf(buf + tcnt, acnt - tcnt,
+ " no i2c client\n");
+ tcnt += ccnt;
+ }
+ }
+ return tcnt;
+}
+
+
+unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw,
+ char *buf,unsigned int acnt)
+{
+ unsigned int bcnt,ccnt,idx;
+ bcnt = 0;
+ LOCK_TAKE(hdw->big_lock);
+ for (idx = 0; ; idx++) {
+ ccnt = pvr2_hdw_report_unlocked(hdw,idx,buf,acnt);
+ if (!ccnt) break;
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ if (!acnt) break;
+ buf[0] = '\n'; ccnt = 1;
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ }
+ ccnt = pvr2_hdw_report_clients(hdw, buf, acnt);
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ LOCK_GIVE(hdw->big_lock);
+ return bcnt;
+}
+
+
+static void pvr2_hdw_state_log_state(struct pvr2_hdw *hdw)
+{
+ char buf[256];
+ unsigned int idx, ccnt;
+ unsigned int lcnt, ucnt;
+
+ for (idx = 0; ; idx++) {
+ ccnt = pvr2_hdw_report_unlocked(hdw,idx,buf,sizeof(buf));
+ if (!ccnt) break;
+ printk(KERN_INFO "%s %.*s\n",hdw->name,ccnt,buf);
+ }
+ ccnt = pvr2_hdw_report_clients(hdw, buf, sizeof(buf));
+ ucnt = 0;
+ while (ucnt < ccnt) {
+ lcnt = 0;
+ while ((lcnt + ucnt < ccnt) && (buf[lcnt + ucnt] != '\n')) {
+ lcnt++;
+ }
+ printk(KERN_INFO "%s %.*s\n", hdw->name, lcnt, buf + ucnt);
+ ucnt += lcnt + 1;
+ }
+}
+
+
+/* Evaluate and update the driver's current state, taking various actions
+ as appropriate for the update. */
+static int pvr2_hdw_state_eval(struct pvr2_hdw *hdw)
+{
+ unsigned int st;
+ int state_updated = 0;
+ int callback_flag = 0;
+ int analog_mode;
+
+ pvr2_trace(PVR2_TRACE_STBITS,
+ "Drive state check START");
+ if (pvrusb2_debug & PVR2_TRACE_STBITS) {
+ pvr2_hdw_state_log_state(hdw);
+ }
+
+ /* Process all state and get back over disposition */
+ state_updated = pvr2_hdw_state_update(hdw);
+
+ analog_mode = (hdw->pathway_state != PVR2_PATHWAY_DIGITAL);
+
+ /* Update master state based upon all other states. */
+ if (!hdw->flag_ok) {
+ st = PVR2_STATE_DEAD;
+ } else if (hdw->fw1_state != FW1_STATE_OK) {
+ st = PVR2_STATE_COLD;
+ } else if ((analog_mode ||
+ hdw->hdw_desc->flag_digital_requires_cx23416) &&
+ !hdw->state_encoder_ok) {
+ st = PVR2_STATE_WARM;
+ } else if (hdw->flag_tripped ||
+ (analog_mode && hdw->flag_decoder_missed)) {
+ st = PVR2_STATE_ERROR;
+ } else if (hdw->state_usbstream_run &&
+ (!analog_mode ||
+ (hdw->state_encoder_run && hdw->state_decoder_run))) {
+ st = PVR2_STATE_RUN;
+ } else {
+ st = PVR2_STATE_READY;
+ }
+ if (hdw->master_state != st) {
+ pvr2_trace(PVR2_TRACE_STATE,
+ "Device state change from %s to %s",
+ pvr2_get_state_name(hdw->master_state),
+ pvr2_get_state_name(st));
+ pvr2_led_ctrl(hdw,st == PVR2_STATE_RUN);
+ hdw->master_state = st;
+ state_updated = !0;
+ callback_flag = !0;
+ }
+ if (state_updated) {
+ /* Trigger anyone waiting on any state changes here. */
+ wake_up(&hdw->state_wait_data);
+ }
+
+ if (pvrusb2_debug & PVR2_TRACE_STBITS) {
+ pvr2_hdw_state_log_state(hdw);
+ }
+ pvr2_trace(PVR2_TRACE_STBITS,
+ "Drive state check DONE callback=%d",callback_flag);
+
+ return callback_flag;
+}
+
+
+/* Cause kernel thread to check / update driver state */
+static void pvr2_hdw_state_sched(struct pvr2_hdw *hdw)
+{
+ if (hdw->state_stale) return;
+ hdw->state_stale = !0;
+ trace_stbit("state_stale",hdw->state_stale);
+ queue_work(hdw->workqueue,&hdw->workpoll);
+}
+
+
+int pvr2_hdw_gpio_get_dir(struct pvr2_hdw *hdw,u32 *dp)
+{
+ return pvr2_read_register(hdw,PVR2_GPIO_DIR,dp);
+}
+
+
+int pvr2_hdw_gpio_get_out(struct pvr2_hdw *hdw,u32 *dp)
+{
+ return pvr2_read_register(hdw,PVR2_GPIO_OUT,dp);
+}
+
+
+int pvr2_hdw_gpio_get_in(struct pvr2_hdw *hdw,u32 *dp)
+{
+ return pvr2_read_register(hdw,PVR2_GPIO_IN,dp);
+}
+
+
+int pvr2_hdw_gpio_chg_dir(struct pvr2_hdw *hdw,u32 msk,u32 val)
+{
+ u32 cval,nval;
+ int ret;
+ if (~msk) {
+ ret = pvr2_read_register(hdw,PVR2_GPIO_DIR,&cval);
+ if (ret) return ret;
+ nval = (cval & ~msk) | (val & msk);
+ pvr2_trace(PVR2_TRACE_GPIO,
+ "GPIO direction changing 0x%x:0x%x"
+ " from 0x%x to 0x%x",
+ msk,val,cval,nval);
+ } else {
+ nval = val;
+ pvr2_trace(PVR2_TRACE_GPIO,
+ "GPIO direction changing to 0x%x",nval);
+ }
+ return pvr2_write_register(hdw,PVR2_GPIO_DIR,nval);
+}
+
+
+int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val)
+{
+ u32 cval,nval;
+ int ret;
+ if (~msk) {
+ ret = pvr2_read_register(hdw,PVR2_GPIO_OUT,&cval);
+ if (ret) return ret;
+ nval = (cval & ~msk) | (val & msk);
+ pvr2_trace(PVR2_TRACE_GPIO,
+ "GPIO output changing 0x%x:0x%x from 0x%x to 0x%x",
+ msk,val,cval,nval);
+ } else {
+ nval = val;
+ pvr2_trace(PVR2_TRACE_GPIO,
+ "GPIO output changing to 0x%x",nval);
+ }
+ return pvr2_write_register(hdw,PVR2_GPIO_OUT,nval);
+}
+
+
+void pvr2_hdw_status_poll(struct pvr2_hdw *hdw)
+{
+ struct v4l2_tuner *vtp = &hdw->tuner_signal_info;
+ memset(vtp, 0, sizeof(*vtp));
+ vtp->type = (hdw->input_val == PVR2_CVAL_INPUT_RADIO) ?
+ V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ hdw->tuner_signal_stale = 0;
+ /* Note: There apparently is no replacement for VIDIOC_CROPCAP
+ using v4l2-subdev - therefore we can't support that AT ALL right
+ now. (Of course, no sub-drivers seem to implement it either.
+ But now it's a a chicken and egg problem...) */
+ v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, g_tuner, vtp);
+ pvr2_trace(PVR2_TRACE_CHIPS, "subdev status poll"
+ " type=%u strength=%u audio=0x%x cap=0x%x"
+ " low=%u hi=%u",
+ vtp->type,
+ vtp->signal, vtp->rxsubchans, vtp->capability,
+ vtp->rangelow, vtp->rangehigh);
+
+ /* We have to do this to avoid getting into constant polling if
+ there's nobody to answer a poll of cropcap info. */
+ hdw->cropcap_stale = 0;
+}
+
+
+unsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *hdw)
+{
+ return hdw->input_avail_mask;
+}
+
+
+unsigned int pvr2_hdw_get_input_allowed(struct pvr2_hdw *hdw)
+{
+ return hdw->input_allowed_mask;
+}
+
+
+static int pvr2_hdw_set_input(struct pvr2_hdw *hdw,int v)
+{
+ if (hdw->input_val != v) {
+ hdw->input_val = v;
+ hdw->input_dirty = !0;
+ }
+
+ /* Handle side effects - if we switch to a mode that needs the RF
+ tuner, then select the right frequency choice as well and mark
+ it dirty. */
+ if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
+ hdw->freqSelector = 0;
+ hdw->freqDirty = !0;
+ } else if ((hdw->input_val == PVR2_CVAL_INPUT_TV) ||
+ (hdw->input_val == PVR2_CVAL_INPUT_DTV)) {
+ hdw->freqSelector = 1;
+ hdw->freqDirty = !0;
+ }
+ return 0;
+}
+
+
+int pvr2_hdw_set_input_allowed(struct pvr2_hdw *hdw,
+ unsigned int change_mask,
+ unsigned int change_val)
+{
+ int ret = 0;
+ unsigned int nv,m,idx;
+ LOCK_TAKE(hdw->big_lock);
+ do {
+ nv = hdw->input_allowed_mask & ~change_mask;
+ nv |= (change_val & change_mask);
+ nv &= hdw->input_avail_mask;
+ if (!nv) {
+ /* No legal modes left; return error instead. */
+ ret = -EPERM;
+ break;
+ }
+ hdw->input_allowed_mask = nv;
+ if ((1 << hdw->input_val) & hdw->input_allowed_mask) {
+ /* Current mode is still in the allowed mask, so
+ we're done. */
+ break;
+ }
+ /* Select and switch to a mode that is still in the allowed
+ mask */
+ if (!hdw->input_allowed_mask) {
+ /* Nothing legal; give up */
+ break;
+ }
+ m = hdw->input_allowed_mask;
+ for (idx = 0; idx < (sizeof(m) << 3); idx++) {
+ if (!((1 << idx) & m)) continue;
+ pvr2_hdw_set_input(hdw,idx);
+ break;
+ }
+ } while (0);
+ LOCK_GIVE(hdw->big_lock);
+ return ret;
+}
+
+
+/* Find I2C address of eeprom */
+static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw)
+{
+ int result;
+ LOCK_TAKE(hdw->ctl_lock); do {
+ hdw->cmd_buffer[0] = FX2CMD_GET_EEPROM_ADDR;
+ result = pvr2_send_request(hdw,
+ hdw->cmd_buffer,1,
+ hdw->cmd_buffer,1);
+ if (result < 0) break;
+ result = hdw->cmd_buffer[0];
+ } while(0); LOCK_GIVE(hdw->ctl_lock);
+ return result;
+}
+
+
+int pvr2_hdw_register_access(struct pvr2_hdw *hdw,
+ struct v4l2_dbg_match *match, u64 reg_id,
+ int setFl, u64 *val_ptr)
+{
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ struct v4l2_dbg_register req;
+ int stat = 0;
+ int okFl = 0;
+
+ if (!capable(CAP_SYS_ADMIN)) return -EPERM;
+
+ req.match = *match;
+ req.reg = reg_id;
+ if (setFl) req.val = *val_ptr;
+ /* It would be nice to know if a sub-device answered the request */
+ v4l2_device_call_all(&hdw->v4l2_dev, 0, core, g_register, &req);
+ if (!setFl) *val_ptr = req.val;
+ if (okFl) {
+ return stat;
+ }
+ return -EINVAL;
+#else
+ return -ENOSYS;
+#endif
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
new file mode 100644
index 00000000..66546580
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
@@ -0,0 +1,367 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef __PVRUSB2_HDW_H
+#define __PVRUSB2_HDW_H
+
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include "pvrusb2-io.h"
+#include "pvrusb2-ctrl.h"
+
+
+/* Private internal control ids, look these up with
+ pvr2_hdw_get_ctrl_by_id() - these are NOT visible in V4L */
+#define PVR2_CID_STDENUM 1
+#define PVR2_CID_STDCUR 2
+#define PVR2_CID_STDAVAIL 3
+#define PVR2_CID_INPUT 4
+#define PVR2_CID_AUDIOMODE 5
+#define PVR2_CID_FREQUENCY 6
+#define PVR2_CID_HRES 7
+#define PVR2_CID_VRES 8
+#define PVR2_CID_CROPL 9
+#define PVR2_CID_CROPT 10
+#define PVR2_CID_CROPW 11
+#define PVR2_CID_CROPH 12
+#define PVR2_CID_CROPCAPPAN 13
+#define PVR2_CID_CROPCAPPAD 14
+#define PVR2_CID_CROPCAPBL 15
+#define PVR2_CID_CROPCAPBT 16
+#define PVR2_CID_CROPCAPBW 17
+#define PVR2_CID_CROPCAPBH 18
+
+/* Legal values for the INPUT state variable */
+#define PVR2_CVAL_INPUT_TV 0
+#define PVR2_CVAL_INPUT_DTV 1
+#define PVR2_CVAL_INPUT_COMPOSITE 2
+#define PVR2_CVAL_INPUT_SVIDEO 3
+#define PVR2_CVAL_INPUT_RADIO 4
+
+enum pvr2_config {
+ pvr2_config_empty, /* No configuration */
+ pvr2_config_mpeg, /* Encoded / compressed video */
+ pvr2_config_vbi, /* Standard vbi info */
+ pvr2_config_pcm, /* Audio raw pcm stream */
+ pvr2_config_rawvideo, /* Video raw frames */
+};
+
+enum pvr2_v4l_type {
+ pvr2_v4l_type_video,
+ pvr2_v4l_type_vbi,
+ pvr2_v4l_type_radio,
+};
+
+/* Major states that we can be in:
+ *
+ * DEAD - Device is in an unusable state and cannot be recovered. This
+ * can happen if we completely lose the ability to communicate with it
+ * (but it might still on the bus). In this state there's nothing we can
+ * do; it must be replugged in order to recover.
+ *
+ * COLD - Device is in an unusable state, needs microcontroller firmware.
+ *
+ * WARM - We can communicate with the device and the proper
+ * microcontroller firmware is running, but other device initialization is
+ * still needed (e.g. encoder firmware).
+ *
+ * ERROR - A problem prevents capture operation (e.g. encoder firmware
+ * missing).
+ *
+ * READY - Device is operational, but not streaming.
+ *
+ * RUN - Device is streaming.
+ *
+ */
+#define PVR2_STATE_NONE 0
+#define PVR2_STATE_DEAD 1
+#define PVR2_STATE_COLD 2
+#define PVR2_STATE_WARM 3
+#define PVR2_STATE_ERROR 4
+#define PVR2_STATE_READY 5
+#define PVR2_STATE_RUN 6
+
+/* Translate configuration enum to a string label */
+const char *pvr2_config_get_name(enum pvr2_config);
+
+struct pvr2_hdw;
+
+/* Create and return a structure for interacting with the underlying
+ hardware */
+struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
+ const struct usb_device_id *devid);
+
+/* Perform second stage initialization, passing in a notification callback
+ for when the master state changes. */
+int pvr2_hdw_initialize(struct pvr2_hdw *,
+ void (*callback_func)(void *),
+ void *callback_data);
+
+/* Destroy hardware interaction structure */
+void pvr2_hdw_destroy(struct pvr2_hdw *);
+
+/* Return true if in the ready (normal) state */
+int pvr2_hdw_dev_ok(struct pvr2_hdw *);
+
+/* Return small integer number [1..N] for logical instance number of this
+ device. This is useful for indexing array-valued module parameters. */
+int pvr2_hdw_get_unit_number(struct pvr2_hdw *);
+
+/* Get pointer to underlying USB device */
+struct usb_device *pvr2_hdw_get_dev(struct pvr2_hdw *);
+
+/* Retrieve serial number of device */
+unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *);
+
+/* Retrieve bus location info of device */
+const char *pvr2_hdw_get_bus_info(struct pvr2_hdw *);
+
+/* Retrieve per-instance string identifier for this specific device */
+const char *pvr2_hdw_get_device_identifier(struct pvr2_hdw *);
+
+/* Called when hardware has been unplugged */
+void pvr2_hdw_disconnect(struct pvr2_hdw *);
+
+/* Get the number of defined controls */
+unsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *);
+
+/* Retrieve a control handle given its index (0..count-1) */
+struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_index(struct pvr2_hdw *,unsigned int);
+
+/* Retrieve a control handle given its internal ID (if any) */
+struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_id(struct pvr2_hdw *,unsigned int);
+
+/* Retrieve a control handle given its V4L ID (if any) */
+struct pvr2_ctrl *pvr2_hdw_get_ctrl_v4l(struct pvr2_hdw *,unsigned int ctl_id);
+
+/* Retrieve a control handle given its immediate predecessor V4L ID (if any) */
+struct pvr2_ctrl *pvr2_hdw_get_ctrl_nextv4l(struct pvr2_hdw *,
+ unsigned int ctl_id);
+
+/* Commit all control changes made up to this point */
+int pvr2_hdw_commit_ctl(struct pvr2_hdw *);
+
+/* Return a bit mask of valid input selections for this device. Mask bits
+ * will be according to PVR_CVAL_INPUT_xxxx definitions. */
+unsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *);
+
+/* Return a bit mask of allowed input selections for this device. Mask bits
+ * will be according to PVR_CVAL_INPUT_xxxx definitions. */
+unsigned int pvr2_hdw_get_input_allowed(struct pvr2_hdw *);
+
+/* Change the set of allowed input selections for this device. Both
+ change_mask and change_valu are mask bits according to
+ PVR_CVAL_INPUT_xxxx definitions. The change_mask parameter indicate
+ which settings are being changed and the change_val parameter indicates
+ whether corresponding settings are being set or cleared. */
+int pvr2_hdw_set_input_allowed(struct pvr2_hdw *,
+ unsigned int change_mask,
+ unsigned int change_val);
+
+/* Return name for this driver instance */
+const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *);
+
+/* Mark tuner status stale so that it will be re-fetched */
+void pvr2_hdw_execute_tuner_poll(struct pvr2_hdw *);
+
+/* Return information about the tuner */
+int pvr2_hdw_get_tuner_status(struct pvr2_hdw *,struct v4l2_tuner *);
+
+/* Return information about cropping capabilities */
+int pvr2_hdw_get_cropcap(struct pvr2_hdw *, struct v4l2_cropcap *);
+
+/* Query device and see if it thinks it is on a high-speed USB link */
+int pvr2_hdw_is_hsm(struct pvr2_hdw *);
+
+/* Return a string token representative of the hardware type */
+const char *pvr2_hdw_get_type(struct pvr2_hdw *);
+
+/* Return a single line description of the hardware type */
+const char *pvr2_hdw_get_desc(struct pvr2_hdw *);
+
+/* Turn streaming on/off */
+int pvr2_hdw_set_streaming(struct pvr2_hdw *,int);
+
+/* Find out if streaming is on */
+int pvr2_hdw_get_streaming(struct pvr2_hdw *);
+
+/* Retrieve driver overall state */
+int pvr2_hdw_get_state(struct pvr2_hdw *);
+
+/* Configure the type of stream to generate */
+int pvr2_hdw_set_stream_type(struct pvr2_hdw *, enum pvr2_config);
+
+/* Get handle to video output stream */
+struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *);
+
+/* Emit a video standard struct */
+int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw,struct v4l2_standard *std,
+ unsigned int idx);
+
+/* Get the detected video standard */
+int pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw, v4l2_std_id *std);
+
+/* Enable / disable retrieval of CPU firmware or prom contents. This must
+ be enabled before pvr2_hdw_cpufw_get() will function. Note that doing
+ this may prevent the device from running (and leaving this mode may
+ imply a device reset). */
+void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *,
+ int mode, /* 0=8KB FX2, 1=16KB FX2, 2=PROM */
+ int enable_flag);
+
+/* Return true if we're in a mode for retrieval CPU firmware */
+int pvr2_hdw_cpufw_get_enabled(struct pvr2_hdw *);
+
+/* Retrieve a piece of the CPU's firmware at the given offset. Return
+ value is the number of bytes retrieved or zero if we're past the end or
+ an error otherwise (e.g. if firmware retrieval is not enabled). */
+int pvr2_hdw_cpufw_get(struct pvr2_hdw *,unsigned int offs,
+ char *buf,unsigned int cnt);
+
+/* Retrieve a previously stored v4l minor device number */
+int pvr2_hdw_v4l_get_minor_number(struct pvr2_hdw *,enum pvr2_v4l_type index);
+
+/* Store a v4l minor device number */
+void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *,
+ enum pvr2_v4l_type index,int);
+
+/* Direct read/write access to chip's registers:
+ match - specify criteria to identify target chip (this is a v4l dbg struct)
+ reg_id - register number to access
+ setFl - true to set the register, false to read it
+ val_ptr - storage location for source / result. */
+int pvr2_hdw_register_access(struct pvr2_hdw *,
+ struct v4l2_dbg_match *match, u64 reg_id,
+ int setFl, u64 *val_ptr);
+
+/* The following entry points are all lower level things you normally don't
+ want to worry about. */
+
+/* Issue a command and get a response from the device. LOTS of higher
+ level stuff is built on this. */
+int pvr2_send_request(struct pvr2_hdw *,
+ void *write_ptr,unsigned int write_len,
+ void *read_ptr,unsigned int read_len);
+
+/* Slightly higher level device communication functions. */
+int pvr2_write_register(struct pvr2_hdw *, u16, u32);
+
+/* Call if for any reason we can't talk to the hardware anymore - this will
+ cause the driver to stop flailing on the device. */
+void pvr2_hdw_render_useless(struct pvr2_hdw *);
+
+/* Set / clear 8051's reset bit */
+void pvr2_hdw_cpureset_assert(struct pvr2_hdw *,int);
+
+/* Execute a USB-commanded device reset */
+void pvr2_hdw_device_reset(struct pvr2_hdw *);
+
+/* Reset worker's error trapping circuit breaker */
+int pvr2_hdw_untrip(struct pvr2_hdw *);
+
+/* Execute hard reset command (after this point it's likely that the
+ encoder will have to be reconfigured). This also clears the "useless"
+ state. */
+int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *);
+
+/* Execute simple reset command */
+int pvr2_hdw_cmd_powerup(struct pvr2_hdw *);
+
+/* suspend */
+int pvr2_hdw_cmd_powerdown(struct pvr2_hdw *);
+
+/* Order decoder to reset */
+int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *);
+
+/* Direct manipulation of GPIO bits */
+int pvr2_hdw_gpio_get_dir(struct pvr2_hdw *hdw,u32 *);
+int pvr2_hdw_gpio_get_out(struct pvr2_hdw *hdw,u32 *);
+int pvr2_hdw_gpio_get_in(struct pvr2_hdw *hdw,u32 *);
+int pvr2_hdw_gpio_chg_dir(struct pvr2_hdw *hdw,u32 msk,u32 val);
+int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val);
+
+/* This data structure is specifically for the next function... */
+struct pvr2_hdw_debug_info {
+ int big_lock_held;
+ int ctl_lock_held;
+ int flag_disconnected;
+ int flag_init_ok;
+ int flag_ok;
+ int fw1_state;
+ int flag_decoder_missed;
+ int flag_tripped;
+ int state_encoder_ok;
+ int state_encoder_run;
+ int state_decoder_run;
+ int state_decoder_ready;
+ int state_usbstream_run;
+ int state_decoder_quiescent;
+ int state_pipeline_config;
+ int state_pipeline_req;
+ int state_pipeline_pause;
+ int state_pipeline_idle;
+ int cmd_debug_state;
+ int cmd_debug_write_len;
+ int cmd_debug_read_len;
+ int cmd_debug_write_pend;
+ int cmd_debug_read_pend;
+ int cmd_debug_timeout;
+ int cmd_debug_rstatus;
+ int cmd_debug_wstatus;
+ unsigned char cmd_code;
+};
+
+/* Non-intrusively retrieve internal state info - this is useful for
+ diagnosing lockups. Note that this operation is completed without any
+ kind of locking and so it is not atomic and may yield inconsistent
+ results. This is *purely* a debugging aid. */
+void pvr2_hdw_get_debug_info_unlocked(const struct pvr2_hdw *hdw,
+ struct pvr2_hdw_debug_info *);
+
+/* Intrusively retrieve internal state info - this is useful for
+ diagnosing overall driver state. This operation synchronizes against
+ the overall driver mutex - so if there are locking problems this will
+ likely hang! This is *purely* a debugging aid. */
+void pvr2_hdw_get_debug_info_locked(struct pvr2_hdw *hdw,
+ struct pvr2_hdw_debug_info *);
+
+/* Report out several lines of text that describes driver internal state.
+ Results are written into the passed-in buffer. */
+unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw,
+ char *buf_ptr,unsigned int buf_size);
+
+/* Cause modules to log their state once */
+void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw);
+
+/* Cause encoder firmware to be uploaded into the device. This is normally
+ done autonomously, but the interface is exported here because it is also
+ a debugging aid. */
+int pvr2_upload_firmware2(struct pvr2_hdw *hdw);
+
+#endif /* __PVRUSB2_HDW_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
new file mode 100644
index 00000000..885ce11f
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
@@ -0,0 +1,698 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <media/ir-kbd-i2c.h>
+#include "pvrusb2-i2c-core.h"
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-debug.h"
+#include "pvrusb2-fx2-cmd.h"
+#include "pvrusb2.h"
+
+#define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__)
+
+/*
+
+ This module attempts to implement a compliant I2C adapter for the pvrusb2
+ device.
+
+*/
+
+static unsigned int i2c_scan;
+module_param(i2c_scan, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time");
+
+static int ir_mode[PVR_NUM] = { [0 ... PVR_NUM-1] = 1 };
+module_param_array(ir_mode, int, NULL, 0444);
+MODULE_PARM_DESC(ir_mode,"specify: 0=disable IR reception, 1=normal IR");
+
+static int pvr2_disable_ir_video;
+module_param_named(disable_autoload_ir_video, pvr2_disable_ir_video,
+ int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(disable_autoload_ir_video,
+ "1=do not try to autoload ir_video IR receiver");
+
+static int pvr2_i2c_write(struct pvr2_hdw *hdw, /* Context */
+ u8 i2c_addr, /* I2C address we're talking to */
+ u8 *data, /* Data to write */
+ u16 length) /* Size of data to write */
+{
+ /* Return value - default 0 means success */
+ int ret;
+
+
+ if (!data) length = 0;
+ if (length > (sizeof(hdw->cmd_buffer) - 3)) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Killing an I2C write to %u that is too large"
+ " (desired=%u limit=%u)",
+ i2c_addr,
+ length,(unsigned int)(sizeof(hdw->cmd_buffer) - 3));
+ return -ENOTSUPP;
+ }
+
+ LOCK_TAKE(hdw->ctl_lock);
+
+ /* Clear the command buffer (likely to be paranoia) */
+ memset(hdw->cmd_buffer, 0, sizeof(hdw->cmd_buffer));
+
+ /* Set up command buffer for an I2C write */
+ hdw->cmd_buffer[0] = FX2CMD_I2C_WRITE; /* write prefix */
+ hdw->cmd_buffer[1] = i2c_addr; /* i2c addr of chip */
+ hdw->cmd_buffer[2] = length; /* length of what follows */
+ if (length) memcpy(hdw->cmd_buffer + 3, data, length);
+
+ /* Do the operation */
+ ret = pvr2_send_request(hdw,
+ hdw->cmd_buffer,
+ length + 3,
+ hdw->cmd_buffer,
+ 1);
+ if (!ret) {
+ if (hdw->cmd_buffer[0] != 8) {
+ ret = -EIO;
+ if (hdw->cmd_buffer[0] != 7) {
+ trace_i2c("unexpected status"
+ " from i2_write[%d]: %d",
+ i2c_addr,hdw->cmd_buffer[0]);
+ }
+ }
+ }
+
+ LOCK_GIVE(hdw->ctl_lock);
+
+ return ret;
+}
+
+static int pvr2_i2c_read(struct pvr2_hdw *hdw, /* Context */
+ u8 i2c_addr, /* I2C address we're talking to */
+ u8 *data, /* Data to write */
+ u16 dlen, /* Size of data to write */
+ u8 *res, /* Where to put data we read */
+ u16 rlen) /* Amount of data to read */
+{
+ /* Return value - default 0 means success */
+ int ret;
+
+
+ if (!data) dlen = 0;
+ if (dlen > (sizeof(hdw->cmd_buffer) - 4)) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Killing an I2C read to %u that has wlen too large"
+ " (desired=%u limit=%u)",
+ i2c_addr,
+ dlen,(unsigned int)(sizeof(hdw->cmd_buffer) - 4));
+ return -ENOTSUPP;
+ }
+ if (res && (rlen > (sizeof(hdw->cmd_buffer) - 1))) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Killing an I2C read to %u that has rlen too large"
+ " (desired=%u limit=%u)",
+ i2c_addr,
+ rlen,(unsigned int)(sizeof(hdw->cmd_buffer) - 1));
+ return -ENOTSUPP;
+ }
+
+ LOCK_TAKE(hdw->ctl_lock);
+
+ /* Clear the command buffer (likely to be paranoia) */
+ memset(hdw->cmd_buffer, 0, sizeof(hdw->cmd_buffer));
+
+ /* Set up command buffer for an I2C write followed by a read */
+ hdw->cmd_buffer[0] = FX2CMD_I2C_READ; /* read prefix */
+ hdw->cmd_buffer[1] = dlen; /* arg length */
+ hdw->cmd_buffer[2] = rlen; /* answer length. Device will send one
+ more byte (status). */
+ hdw->cmd_buffer[3] = i2c_addr; /* i2c addr of chip */
+ if (dlen) memcpy(hdw->cmd_buffer + 4, data, dlen);
+
+ /* Do the operation */
+ ret = pvr2_send_request(hdw,
+ hdw->cmd_buffer,
+ 4 + dlen,
+ hdw->cmd_buffer,
+ rlen + 1);
+ if (!ret) {
+ if (hdw->cmd_buffer[0] != 8) {
+ ret = -EIO;
+ if (hdw->cmd_buffer[0] != 7) {
+ trace_i2c("unexpected status"
+ " from i2_read[%d]: %d",
+ i2c_addr,hdw->cmd_buffer[0]);
+ }
+ }
+ }
+
+ /* Copy back the result */
+ if (res && rlen) {
+ if (ret) {
+ /* Error, just blank out the return buffer */
+ memset(res, 0, rlen);
+ } else {
+ memcpy(res, hdw->cmd_buffer + 1, rlen);
+ }
+ }
+
+ LOCK_GIVE(hdw->ctl_lock);
+
+ return ret;
+}
+
+/* This is the common low level entry point for doing I2C operations to the
+ hardware. */
+static int pvr2_i2c_basic_op(struct pvr2_hdw *hdw,
+ u8 i2c_addr,
+ u8 *wdata,
+ u16 wlen,
+ u8 *rdata,
+ u16 rlen)
+{
+ if (!rdata) rlen = 0;
+ if (!wdata) wlen = 0;
+ if (rlen || !wlen) {
+ return pvr2_i2c_read(hdw,i2c_addr,wdata,wlen,rdata,rlen);
+ } else {
+ return pvr2_i2c_write(hdw,i2c_addr,wdata,wlen);
+ }
+}
+
+
+/* This is a special entry point for cases of I2C transaction attempts to
+ the IR receiver. The implementation here simulates the IR receiver by
+ issuing a command to the FX2 firmware and using that response to return
+ what the real I2C receiver would have returned. We use this for 24xxx
+ devices, where the IR receiver chip has been removed and replaced with
+ FX2 related logic. */
+static int i2c_24xxx_ir(struct pvr2_hdw *hdw,
+ u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen)
+{
+ u8 dat[4];
+ unsigned int stat;
+
+ if (!(rlen || wlen)) {
+ /* This is a probe attempt. Just let it succeed. */
+ return 0;
+ }
+
+ /* We don't understand this kind of transaction */
+ if ((wlen != 0) || (rlen == 0)) return -EIO;
+
+ if (rlen < 3) {
+ /* Mike Isely <isely@pobox.com> Appears to be a probe
+ attempt from lirc. Just fill in zeroes and return. If
+ we try instead to do the full transaction here, then bad
+ things seem to happen within the lirc driver module
+ (version 0.8.0-7 sources from Debian, when run under
+ vanilla 2.6.17.6 kernel) - and I don't have the patience
+ to chase it down. */
+ if (rlen > 0) rdata[0] = 0;
+ if (rlen > 1) rdata[1] = 0;
+ return 0;
+ }
+
+ /* Issue a command to the FX2 to read the IR receiver. */
+ LOCK_TAKE(hdw->ctl_lock); do {
+ hdw->cmd_buffer[0] = FX2CMD_GET_IR_CODE;
+ stat = pvr2_send_request(hdw,
+ hdw->cmd_buffer,1,
+ hdw->cmd_buffer,4);
+ dat[0] = hdw->cmd_buffer[0];
+ dat[1] = hdw->cmd_buffer[1];
+ dat[2] = hdw->cmd_buffer[2];
+ dat[3] = hdw->cmd_buffer[3];
+ } while (0); LOCK_GIVE(hdw->ctl_lock);
+
+ /* Give up if that operation failed. */
+ if (stat != 0) return stat;
+
+ /* Mangle the results into something that looks like the real IR
+ receiver. */
+ rdata[2] = 0xc1;
+ if (dat[0] != 1) {
+ /* No code received. */
+ rdata[0] = 0;
+ rdata[1] = 0;
+ } else {
+ u16 val;
+ /* Mash the FX2 firmware-provided IR code into something
+ that the normal i2c chip-level driver expects. */
+ val = dat[1];
+ val <<= 8;
+ val |= dat[2];
+ val >>= 1;
+ val &= ~0x0003;
+ val |= 0x8000;
+ rdata[0] = (val >> 8) & 0xffu;
+ rdata[1] = val & 0xffu;
+ }
+
+ return 0;
+}
+
+/* This is a special entry point that is entered if an I2C operation is
+ attempted to a wm8775 chip on model 24xxx hardware. Autodetect of this
+ part doesn't work, but we know it is really there. So let's look for
+ the autodetect attempt and just return success if we see that. */
+static int i2c_hack_wm8775(struct pvr2_hdw *hdw,
+ u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen)
+{
+ if (!(rlen || wlen)) {
+ // This is a probe attempt. Just let it succeed.
+ return 0;
+ }
+ return pvr2_i2c_basic_op(hdw,i2c_addr,wdata,wlen,rdata,rlen);
+}
+
+/* This is an entry point designed to always fail any attempt to perform a
+ transfer. We use this to cause certain I2C addresses to not be
+ probed. */
+static int i2c_black_hole(struct pvr2_hdw *hdw,
+ u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen)
+{
+ return -EIO;
+}
+
+/* This is a special entry point that is entered if an I2C operation is
+ attempted to a cx25840 chip on model 24xxx hardware. This chip can
+ sometimes wedge itself. Worse still, when this happens msp3400 can
+ falsely detect this part and then the system gets hosed up after msp3400
+ gets confused and dies. What we want to do here is try to keep msp3400
+ away and also try to notice if the chip is wedged and send a warning to
+ the system log. */
+static int i2c_hack_cx25840(struct pvr2_hdw *hdw,
+ u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen)
+{
+ int ret;
+ unsigned int subaddr;
+ u8 wbuf[2];
+ int state = hdw->i2c_cx25840_hack_state;
+
+ if (!(rlen || wlen)) {
+ // Probe attempt - always just succeed and don't bother the
+ // hardware (this helps to make the state machine further
+ // down somewhat easier).
+ return 0;
+ }
+
+ if (state == 3) {
+ return pvr2_i2c_basic_op(hdw,i2c_addr,wdata,wlen,rdata,rlen);
+ }
+
+ /* We're looking for the exact pattern where the revision register
+ is being read. The cx25840 module will always look at the
+ revision register first. Any other pattern of access therefore
+ has to be a probe attempt from somebody else so we'll reject it.
+ Normally we could just let each client just probe the part
+ anyway, but when the cx25840 is wedged, msp3400 will get a false
+ positive and that just screws things up... */
+
+ if (wlen == 0) {
+ switch (state) {
+ case 1: subaddr = 0x0100; break;
+ case 2: subaddr = 0x0101; break;
+ default: goto fail;
+ }
+ } else if (wlen == 2) {
+ subaddr = (wdata[0] << 8) | wdata[1];
+ switch (subaddr) {
+ case 0x0100: state = 1; break;
+ case 0x0101: state = 2; break;
+ default: goto fail;
+ }
+ } else {
+ goto fail;
+ }
+ if (!rlen) goto success;
+ state = 0;
+ if (rlen != 1) goto fail;
+
+ /* If we get to here then we have a legitimate read for one of the
+ two revision bytes, so pass it through. */
+ wbuf[0] = subaddr >> 8;
+ wbuf[1] = subaddr;
+ ret = pvr2_i2c_basic_op(hdw,i2c_addr,wbuf,2,rdata,rlen);
+
+ if ((ret != 0) || (*rdata == 0x04) || (*rdata == 0x0a)) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "WARNING: Detected a wedged cx25840 chip;"
+ " the device will not work.");
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "WARNING: Try power cycling the pvrusb2 device.");
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "WARNING: Disabling further access to the device"
+ " to prevent other foul-ups.");
+ // This blocks all further communication with the part.
+ hdw->i2c_func[0x44] = NULL;
+ pvr2_hdw_render_useless(hdw);
+ goto fail;
+ }
+
+ /* Success! */
+ pvr2_trace(PVR2_TRACE_CHIPS,"cx25840 appears to be OK.");
+ state = 3;
+
+ success:
+ hdw->i2c_cx25840_hack_state = state;
+ return 0;
+
+ fail:
+ hdw->i2c_cx25840_hack_state = state;
+ return -EIO;
+}
+
+/* This is a very, very limited I2C adapter implementation. We can only
+ support what we actually know will work on the device... */
+static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg msgs[],
+ int num)
+{
+ int ret = -ENOTSUPP;
+ pvr2_i2c_func funcp = NULL;
+ struct pvr2_hdw *hdw = (struct pvr2_hdw *)(i2c_adap->algo_data);
+
+ if (!num) {
+ ret = -EINVAL;
+ goto done;
+ }
+ if (msgs[0].addr < PVR2_I2C_FUNC_CNT) {
+ funcp = hdw->i2c_func[msgs[0].addr];
+ }
+ if (!funcp) {
+ ret = -EIO;
+ goto done;
+ }
+
+ if (num == 1) {
+ if (msgs[0].flags & I2C_M_RD) {
+ /* Simple read */
+ u16 tcnt,bcnt,offs;
+ if (!msgs[0].len) {
+ /* Length == 0 read. This is a probe. */
+ if (funcp(hdw,msgs[0].addr,NULL,0,NULL,0)) {
+ ret = -EIO;
+ goto done;
+ }
+ ret = 1;
+ goto done;
+ }
+ /* If the read is short enough we'll do the whole
+ thing atomically. Otherwise we have no choice
+ but to break apart the reads. */
+ tcnt = msgs[0].len;
+ offs = 0;
+ while (tcnt) {
+ bcnt = tcnt;
+ if (bcnt > sizeof(hdw->cmd_buffer)-1) {
+ bcnt = sizeof(hdw->cmd_buffer)-1;
+ }
+ if (funcp(hdw,msgs[0].addr,NULL,0,
+ msgs[0].buf+offs,bcnt)) {
+ ret = -EIO;
+ goto done;
+ }
+ offs += bcnt;
+ tcnt -= bcnt;
+ }
+ ret = 1;
+ goto done;
+ } else {
+ /* Simple write */
+ ret = 1;
+ if (funcp(hdw,msgs[0].addr,
+ msgs[0].buf,msgs[0].len,NULL,0)) {
+ ret = -EIO;
+ }
+ goto done;
+ }
+ } else if (num == 2) {
+ if (msgs[0].addr != msgs[1].addr) {
+ trace_i2c("i2c refusing 2 phase transfer with"
+ " conflicting target addresses");
+ ret = -ENOTSUPP;
+ goto done;
+ }
+ if ((!((msgs[0].flags & I2C_M_RD))) &&
+ (msgs[1].flags & I2C_M_RD)) {
+ u16 tcnt,bcnt,wcnt,offs;
+ /* Write followed by atomic read. If the read
+ portion is short enough we'll do the whole thing
+ atomically. Otherwise we have no choice but to
+ break apart the reads. */
+ tcnt = msgs[1].len;
+ wcnt = msgs[0].len;
+ offs = 0;
+ while (tcnt || wcnt) {
+ bcnt = tcnt;
+ if (bcnt > sizeof(hdw->cmd_buffer)-1) {
+ bcnt = sizeof(hdw->cmd_buffer)-1;
+ }
+ if (funcp(hdw,msgs[0].addr,
+ msgs[0].buf,wcnt,
+ msgs[1].buf+offs,bcnt)) {
+ ret = -EIO;
+ goto done;
+ }
+ offs += bcnt;
+ tcnt -= bcnt;
+ wcnt = 0;
+ }
+ ret = 2;
+ goto done;
+ } else {
+ trace_i2c("i2c refusing complex transfer"
+ " read0=%d read1=%d",
+ (msgs[0].flags & I2C_M_RD),
+ (msgs[1].flags & I2C_M_RD));
+ }
+ } else {
+ trace_i2c("i2c refusing %d phase transfer",num);
+ }
+
+ done:
+ if (pvrusb2_debug & PVR2_TRACE_I2C_TRAF) {
+ unsigned int idx,offs,cnt;
+ for (idx = 0; idx < num; idx++) {
+ cnt = msgs[idx].len;
+ printk(KERN_INFO
+ "pvrusb2 i2c xfer %u/%u:"
+ " addr=0x%x len=%d %s",
+ idx+1,num,
+ msgs[idx].addr,
+ cnt,
+ (msgs[idx].flags & I2C_M_RD ?
+ "read" : "write"));
+ if ((ret > 0) || !(msgs[idx].flags & I2C_M_RD)) {
+ if (cnt > 8) cnt = 8;
+ printk(" [");
+ for (offs = 0; offs < (cnt>8?8:cnt); offs++) {
+ if (offs) printk(" ");
+ printk("%02x",msgs[idx].buf[offs]);
+ }
+ if (offs < cnt) printk(" ...");
+ printk("]");
+ }
+ if (idx+1 == num) {
+ printk(" result=%d",ret);
+ }
+ printk("\n");
+ }
+ if (!num) {
+ printk(KERN_INFO
+ "pvrusb2 i2c xfer null transfer result=%d\n",
+ ret);
+ }
+ }
+ return ret;
+}
+
+static u32 pvr2_i2c_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm pvr2_i2c_algo_template = {
+ .master_xfer = pvr2_i2c_xfer,
+ .functionality = pvr2_i2c_functionality,
+};
+
+static struct i2c_adapter pvr2_i2c_adap_template = {
+ .owner = THIS_MODULE,
+ .class = 0,
+};
+
+
+/* Return true if device exists at given address */
+static int do_i2c_probe(struct pvr2_hdw *hdw, int addr)
+{
+ struct i2c_msg msg[1];
+ int rc;
+ msg[0].addr = 0;
+ msg[0].flags = I2C_M_RD;
+ msg[0].len = 0;
+ msg[0].buf = NULL;
+ msg[0].addr = addr;
+ rc = i2c_transfer(&hdw->i2c_adap, msg, ARRAY_SIZE(msg));
+ return rc == 1;
+}
+
+static void do_i2c_scan(struct pvr2_hdw *hdw)
+{
+ int i;
+ printk(KERN_INFO "%s: i2c scan beginning\n", hdw->name);
+ for (i = 0; i < 128; i++) {
+ if (do_i2c_probe(hdw, i)) {
+ printk(KERN_INFO "%s: i2c scan: found device @ 0x%x\n",
+ hdw->name, i);
+ }
+ }
+ printk(KERN_INFO "%s: i2c scan done.\n", hdw->name);
+}
+
+static void pvr2_i2c_register_ir(struct pvr2_hdw *hdw)
+{
+ struct i2c_board_info info;
+ struct IR_i2c_init_data *init_data = &hdw->ir_init_data;
+ if (pvr2_disable_ir_video) {
+ pvr2_trace(PVR2_TRACE_INFO,
+ "Automatic binding of ir_video has been disabled.");
+ return;
+ }
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ switch (hdw->ir_scheme_active) {
+ case PVR2_IR_SCHEME_24XXX: /* FX2-controlled IR */
+ case PVR2_IR_SCHEME_29XXX: /* Original 29xxx device */
+ init_data->ir_codes = RC_MAP_HAUPPAUGE;
+ init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP;
+ init_data->type = RC_TYPE_RC5;
+ init_data->name = hdw->hdw_desc->description;
+ init_data->polling_interval = 100; /* ms From ir-kbd-i2c */
+ /* IR Receiver */
+ info.addr = 0x18;
+ info.platform_data = init_data;
+ strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
+ pvr2_trace(PVR2_TRACE_INFO, "Binding %s to i2c address 0x%02x.",
+ info.type, info.addr);
+ i2c_new_device(&hdw->i2c_adap, &info);
+ break;
+ case PVR2_IR_SCHEME_ZILOG: /* HVR-1950 style */
+ case PVR2_IR_SCHEME_24XXX_MCE: /* 24xxx MCE device */
+ init_data->ir_codes = RC_MAP_HAUPPAUGE;
+ init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR;
+ init_data->type = RC_TYPE_RC5;
+ init_data->name = hdw->hdw_desc->description;
+ /* IR Receiver */
+ info.addr = 0x71;
+ info.platform_data = init_data;
+ strlcpy(info.type, "ir_rx_z8f0811_haup", I2C_NAME_SIZE);
+ pvr2_trace(PVR2_TRACE_INFO, "Binding %s to i2c address 0x%02x.",
+ info.type, info.addr);
+ i2c_new_device(&hdw->i2c_adap, &info);
+ /* IR Trasmitter */
+ info.addr = 0x70;
+ info.platform_data = init_data;
+ strlcpy(info.type, "ir_tx_z8f0811_haup", I2C_NAME_SIZE);
+ pvr2_trace(PVR2_TRACE_INFO, "Binding %s to i2c address 0x%02x.",
+ info.type, info.addr);
+ i2c_new_device(&hdw->i2c_adap, &info);
+ break;
+ default:
+ /* The device either doesn't support I2C-based IR or we
+ don't know (yet) how to operate IR on the device. */
+ break;
+ }
+}
+
+void pvr2_i2c_core_init(struct pvr2_hdw *hdw)
+{
+ unsigned int idx;
+
+ /* The default action for all possible I2C addresses is just to do
+ the transfer normally. */
+ for (idx = 0; idx < PVR2_I2C_FUNC_CNT; idx++) {
+ hdw->i2c_func[idx] = pvr2_i2c_basic_op;
+ }
+
+ /* However, deal with various special cases for 24xxx hardware. */
+ if (ir_mode[hdw->unit_number] == 0) {
+ printk(KERN_INFO "%s: IR disabled\n",hdw->name);
+ hdw->i2c_func[0x18] = i2c_black_hole;
+ } else if (ir_mode[hdw->unit_number] == 1) {
+ if (hdw->ir_scheme_active == PVR2_IR_SCHEME_24XXX) {
+ /* Set up translation so that our IR looks like a
+ 29xxx device */
+ hdw->i2c_func[0x18] = i2c_24xxx_ir;
+ }
+ }
+ if (hdw->hdw_desc->flag_has_cx25840) {
+ hdw->i2c_func[0x44] = i2c_hack_cx25840;
+ }
+ if (hdw->hdw_desc->flag_has_wm8775) {
+ hdw->i2c_func[0x1b] = i2c_hack_wm8775;
+ }
+
+ // Configure the adapter and set up everything else related to it.
+ memcpy(&hdw->i2c_adap,&pvr2_i2c_adap_template,sizeof(hdw->i2c_adap));
+ memcpy(&hdw->i2c_algo,&pvr2_i2c_algo_template,sizeof(hdw->i2c_algo));
+ strlcpy(hdw->i2c_adap.name,hdw->name,sizeof(hdw->i2c_adap.name));
+ hdw->i2c_adap.dev.parent = &hdw->usb_dev->dev;
+ hdw->i2c_adap.algo = &hdw->i2c_algo;
+ hdw->i2c_adap.algo_data = hdw;
+ hdw->i2c_linked = !0;
+ i2c_set_adapdata(&hdw->i2c_adap, &hdw->v4l2_dev);
+ i2c_add_adapter(&hdw->i2c_adap);
+ if (hdw->i2c_func[0x18] == i2c_24xxx_ir) {
+ /* Probe for a different type of IR receiver on this
+ device. This is really the only way to differentiate
+ older 24xxx devices from 24xxx variants that include an
+ IR blaster. If the IR blaster is present, the IR
+ receiver is part of that chip and thus we must disable
+ the emulated IR receiver. */
+ if (do_i2c_probe(hdw, 0x71)) {
+ pvr2_trace(PVR2_TRACE_INFO,
+ "Device has newer IR hardware;"
+ " disabling unneeded virtual IR device");
+ hdw->i2c_func[0x18] = NULL;
+ /* Remember that this is a different device... */
+ hdw->ir_scheme_active = PVR2_IR_SCHEME_24XXX_MCE;
+ }
+ }
+ if (i2c_scan) do_i2c_scan(hdw);
+
+ pvr2_i2c_register_ir(hdw);
+}
+
+void pvr2_i2c_core_done(struct pvr2_hdw *hdw)
+{
+ if (hdw->i2c_linked) {
+ i2c_del_adapter(&hdw->i2c_adap);
+ hdw->i2c_linked = 0;
+ }
+}
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h
new file mode 100644
index 00000000..6a757692
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h
@@ -0,0 +1,40 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef __PVRUSB2_I2C_CORE_H
+#define __PVRUSB2_I2C_CORE_H
+
+struct pvr2_hdw;
+
+void pvr2_i2c_core_init(struct pvr2_hdw *);
+void pvr2_i2c_core_done(struct pvr2_hdw *);
+
+
+#endif /* __PVRUSB2_I2C_ADAPTER_H */
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-io.c b/drivers/media/video/pvrusb2/pvrusb2-io.c
new file mode 100644
index 00000000..20b6ae0b
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-io.c
@@ -0,0 +1,695 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "pvrusb2-io.h"
+#include "pvrusb2-debug.h"
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+
+static const char *pvr2_buffer_state_decode(enum pvr2_buffer_state);
+
+#define BUFFER_SIG 0x47653271
+
+// #define SANITY_CHECK_BUFFERS
+
+
+#ifdef SANITY_CHECK_BUFFERS
+#define BUFFER_CHECK(bp) do { \
+ if ((bp)->signature != BUFFER_SIG) { \
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS, \
+ "Buffer %p is bad at %s:%d", \
+ (bp),__FILE__,__LINE__); \
+ pvr2_buffer_describe(bp,"BadSig"); \
+ BUG(); \
+ } \
+} while (0)
+#else
+#define BUFFER_CHECK(bp) do {} while(0)
+#endif
+
+struct pvr2_stream {
+ /* Buffers queued for reading */
+ struct list_head queued_list;
+ unsigned int q_count;
+ unsigned int q_bcount;
+ /* Buffers with retrieved data */
+ struct list_head ready_list;
+ unsigned int r_count;
+ unsigned int r_bcount;
+ /* Buffers available for use */
+ struct list_head idle_list;
+ unsigned int i_count;
+ unsigned int i_bcount;
+ /* Pointers to all buffers */
+ struct pvr2_buffer **buffers;
+ /* Array size of buffers */
+ unsigned int buffer_slot_count;
+ /* Total buffers actually in circulation */
+ unsigned int buffer_total_count;
+ /* Designed number of buffers to be in circulation */
+ unsigned int buffer_target_count;
+ /* Executed when ready list become non-empty */
+ pvr2_stream_callback callback_func;
+ void *callback_data;
+ /* Context for transfer endpoint */
+ struct usb_device *dev;
+ int endpoint;
+ /* Overhead for mutex enforcement */
+ spinlock_t list_lock;
+ struct mutex mutex;
+ /* Tracking state for tolerating errors */
+ unsigned int fail_count;
+ unsigned int fail_tolerance;
+
+ unsigned int buffers_processed;
+ unsigned int buffers_failed;
+ unsigned int bytes_processed;
+};
+
+struct pvr2_buffer {
+ int id;
+ int signature;
+ enum pvr2_buffer_state state;
+ void *ptr; /* Pointer to storage area */
+ unsigned int max_count; /* Size of storage area */
+ unsigned int used_count; /* Amount of valid data in storage area */
+ int status; /* Transfer result status */
+ struct pvr2_stream *stream;
+ struct list_head list_overhead;
+ struct urb *purb;
+};
+
+static const char *pvr2_buffer_state_decode(enum pvr2_buffer_state st)
+{
+ switch (st) {
+ case pvr2_buffer_state_none: return "none";
+ case pvr2_buffer_state_idle: return "idle";
+ case pvr2_buffer_state_queued: return "queued";
+ case pvr2_buffer_state_ready: return "ready";
+ }
+ return "unknown";
+}
+
+#ifdef SANITY_CHECK_BUFFERS
+static void pvr2_buffer_describe(struct pvr2_buffer *bp,const char *msg)
+{
+ pvr2_trace(PVR2_TRACE_INFO,
+ "buffer%s%s %p state=%s id=%d status=%d"
+ " stream=%p purb=%p sig=0x%x",
+ (msg ? " " : ""),
+ (msg ? msg : ""),
+ bp,
+ (bp ? pvr2_buffer_state_decode(bp->state) : "(invalid)"),
+ (bp ? bp->id : 0),
+ (bp ? bp->status : 0),
+ (bp ? bp->stream : NULL),
+ (bp ? bp->purb : NULL),
+ (bp ? bp->signature : 0));
+}
+#endif /* SANITY_CHECK_BUFFERS */
+
+static void pvr2_buffer_remove(struct pvr2_buffer *bp)
+{
+ unsigned int *cnt;
+ unsigned int *bcnt;
+ unsigned int ccnt;
+ struct pvr2_stream *sp = bp->stream;
+ switch (bp->state) {
+ case pvr2_buffer_state_idle:
+ cnt = &sp->i_count;
+ bcnt = &sp->i_bcount;
+ ccnt = bp->max_count;
+ break;
+ case pvr2_buffer_state_queued:
+ cnt = &sp->q_count;
+ bcnt = &sp->q_bcount;
+ ccnt = bp->max_count;
+ break;
+ case pvr2_buffer_state_ready:
+ cnt = &sp->r_count;
+ bcnt = &sp->r_bcount;
+ ccnt = bp->used_count;
+ break;
+ default:
+ return;
+ }
+ list_del_init(&bp->list_overhead);
+ (*cnt)--;
+ (*bcnt) -= ccnt;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/"
+ " bufferPool %8s dec cap=%07d cnt=%02d",
+ pvr2_buffer_state_decode(bp->state),*bcnt,*cnt);
+ bp->state = pvr2_buffer_state_none;
+}
+
+static void pvr2_buffer_set_none(struct pvr2_buffer *bp)
+{
+ unsigned long irq_flags;
+ struct pvr2_stream *sp;
+ BUFFER_CHECK(bp);
+ sp = bp->stream;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s",
+ bp,
+ pvr2_buffer_state_decode(bp->state),
+ pvr2_buffer_state_decode(pvr2_buffer_state_none));
+ spin_lock_irqsave(&sp->list_lock,irq_flags);
+ pvr2_buffer_remove(bp);
+ spin_unlock_irqrestore(&sp->list_lock,irq_flags);
+}
+
+static int pvr2_buffer_set_ready(struct pvr2_buffer *bp)
+{
+ int fl;
+ unsigned long irq_flags;
+ struct pvr2_stream *sp;
+ BUFFER_CHECK(bp);
+ sp = bp->stream;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s",
+ bp,
+ pvr2_buffer_state_decode(bp->state),
+ pvr2_buffer_state_decode(pvr2_buffer_state_ready));
+ spin_lock_irqsave(&sp->list_lock,irq_flags);
+ fl = (sp->r_count == 0);
+ pvr2_buffer_remove(bp);
+ list_add_tail(&bp->list_overhead,&sp->ready_list);
+ bp->state = pvr2_buffer_state_ready;
+ (sp->r_count)++;
+ sp->r_bcount += bp->used_count;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/"
+ " bufferPool %8s inc cap=%07d cnt=%02d",
+ pvr2_buffer_state_decode(bp->state),
+ sp->r_bcount,sp->r_count);
+ spin_unlock_irqrestore(&sp->list_lock,irq_flags);
+ return fl;
+}
+
+static void pvr2_buffer_set_idle(struct pvr2_buffer *bp)
+{
+ unsigned long irq_flags;
+ struct pvr2_stream *sp;
+ BUFFER_CHECK(bp);
+ sp = bp->stream;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s",
+ bp,
+ pvr2_buffer_state_decode(bp->state),
+ pvr2_buffer_state_decode(pvr2_buffer_state_idle));
+ spin_lock_irqsave(&sp->list_lock,irq_flags);
+ pvr2_buffer_remove(bp);
+ list_add_tail(&bp->list_overhead,&sp->idle_list);
+ bp->state = pvr2_buffer_state_idle;
+ (sp->i_count)++;
+ sp->i_bcount += bp->max_count;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/"
+ " bufferPool %8s inc cap=%07d cnt=%02d",
+ pvr2_buffer_state_decode(bp->state),
+ sp->i_bcount,sp->i_count);
+ spin_unlock_irqrestore(&sp->list_lock,irq_flags);
+}
+
+static void pvr2_buffer_set_queued(struct pvr2_buffer *bp)
+{
+ unsigned long irq_flags;
+ struct pvr2_stream *sp;
+ BUFFER_CHECK(bp);
+ sp = bp->stream;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s",
+ bp,
+ pvr2_buffer_state_decode(bp->state),
+ pvr2_buffer_state_decode(pvr2_buffer_state_queued));
+ spin_lock_irqsave(&sp->list_lock,irq_flags);
+ pvr2_buffer_remove(bp);
+ list_add_tail(&bp->list_overhead,&sp->queued_list);
+ bp->state = pvr2_buffer_state_queued;
+ (sp->q_count)++;
+ sp->q_bcount += bp->max_count;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/"
+ " bufferPool %8s inc cap=%07d cnt=%02d",
+ pvr2_buffer_state_decode(bp->state),
+ sp->q_bcount,sp->q_count);
+ spin_unlock_irqrestore(&sp->list_lock,irq_flags);
+}
+
+static void pvr2_buffer_wipe(struct pvr2_buffer *bp)
+{
+ if (bp->state == pvr2_buffer_state_queued) {
+ usb_kill_urb(bp->purb);
+ }
+}
+
+static int pvr2_buffer_init(struct pvr2_buffer *bp,
+ struct pvr2_stream *sp,
+ unsigned int id)
+{
+ memset(bp,0,sizeof(*bp));
+ bp->signature = BUFFER_SIG;
+ bp->id = id;
+ pvr2_trace(PVR2_TRACE_BUF_POOL,
+ "/*---TRACE_FLOW---*/ bufferInit %p stream=%p",bp,sp);
+ bp->stream = sp;
+ bp->state = pvr2_buffer_state_none;
+ INIT_LIST_HEAD(&bp->list_overhead);
+ bp->purb = usb_alloc_urb(0,GFP_KERNEL);
+ if (! bp->purb) return -ENOMEM;
+#ifdef SANITY_CHECK_BUFFERS
+ pvr2_buffer_describe(bp,"create");
+#endif
+ return 0;
+}
+
+static void pvr2_buffer_done(struct pvr2_buffer *bp)
+{
+#ifdef SANITY_CHECK_BUFFERS
+ pvr2_buffer_describe(bp,"delete");
+#endif
+ pvr2_buffer_wipe(bp);
+ pvr2_buffer_set_none(bp);
+ bp->signature = 0;
+ bp->stream = NULL;
+ usb_free_urb(bp->purb);
+ pvr2_trace(PVR2_TRACE_BUF_POOL,"/*---TRACE_FLOW---*/"
+ " bufferDone %p",bp);
+}
+
+static int pvr2_stream_buffer_count(struct pvr2_stream *sp,unsigned int cnt)
+{
+ int ret;
+ unsigned int scnt;
+
+ /* Allocate buffers pointer array in multiples of 32 entries */
+ if (cnt == sp->buffer_total_count) return 0;
+
+ pvr2_trace(PVR2_TRACE_BUF_POOL,
+ "/*---TRACE_FLOW---*/ poolResize "
+ " stream=%p cur=%d adj=%+d",
+ sp,
+ sp->buffer_total_count,
+ cnt-sp->buffer_total_count);
+
+ scnt = cnt & ~0x1f;
+ if (cnt > scnt) scnt += 0x20;
+
+ if (cnt > sp->buffer_total_count) {
+ if (scnt > sp->buffer_slot_count) {
+ struct pvr2_buffer **nb;
+ nb = kmalloc(scnt * sizeof(*nb),GFP_KERNEL);
+ if (!nb) return -ENOMEM;
+ if (sp->buffer_slot_count) {
+ memcpy(nb,sp->buffers,
+ sp->buffer_slot_count * sizeof(*nb));
+ kfree(sp->buffers);
+ }
+ sp->buffers = nb;
+ sp->buffer_slot_count = scnt;
+ }
+ while (sp->buffer_total_count < cnt) {
+ struct pvr2_buffer *bp;
+ bp = kmalloc(sizeof(*bp),GFP_KERNEL);
+ if (!bp) return -ENOMEM;
+ ret = pvr2_buffer_init(bp,sp,sp->buffer_total_count);
+ if (ret) {
+ kfree(bp);
+ return -ENOMEM;
+ }
+ sp->buffers[sp->buffer_total_count] = bp;
+ (sp->buffer_total_count)++;
+ pvr2_buffer_set_idle(bp);
+ }
+ } else {
+ while (sp->buffer_total_count > cnt) {
+ struct pvr2_buffer *bp;
+ bp = sp->buffers[sp->buffer_total_count - 1];
+ /* Paranoia */
+ sp->buffers[sp->buffer_total_count - 1] = NULL;
+ (sp->buffer_total_count)--;
+ pvr2_buffer_done(bp);
+ kfree(bp);
+ }
+ if (scnt < sp->buffer_slot_count) {
+ struct pvr2_buffer **nb = NULL;
+ if (scnt) {
+ nb = kmalloc(scnt * sizeof(*nb),GFP_KERNEL);
+ if (!nb) return -ENOMEM;
+ memcpy(nb,sp->buffers,scnt * sizeof(*nb));
+ }
+ kfree(sp->buffers);
+ sp->buffers = nb;
+ sp->buffer_slot_count = scnt;
+ }
+ }
+ return 0;
+}
+
+static int pvr2_stream_achieve_buffer_count(struct pvr2_stream *sp)
+{
+ struct pvr2_buffer *bp;
+ unsigned int cnt;
+
+ if (sp->buffer_total_count == sp->buffer_target_count) return 0;
+
+ pvr2_trace(PVR2_TRACE_BUF_POOL,
+ "/*---TRACE_FLOW---*/"
+ " poolCheck stream=%p cur=%d tgt=%d",
+ sp,sp->buffer_total_count,sp->buffer_target_count);
+
+ if (sp->buffer_total_count < sp->buffer_target_count) {
+ return pvr2_stream_buffer_count(sp,sp->buffer_target_count);
+ }
+
+ cnt = 0;
+ while ((sp->buffer_total_count - cnt) > sp->buffer_target_count) {
+ bp = sp->buffers[sp->buffer_total_count - (cnt + 1)];
+ if (bp->state != pvr2_buffer_state_idle) break;
+ cnt++;
+ }
+ if (cnt) {
+ pvr2_stream_buffer_count(sp,sp->buffer_total_count - cnt);
+ }
+
+ return 0;
+}
+
+static void pvr2_stream_internal_flush(struct pvr2_stream *sp)
+{
+ struct list_head *lp;
+ struct pvr2_buffer *bp1;
+ while ((lp = sp->queued_list.next) != &sp->queued_list) {
+ bp1 = list_entry(lp,struct pvr2_buffer,list_overhead);
+ pvr2_buffer_wipe(bp1);
+ /* At this point, we should be guaranteed that no
+ completion callback may happen on this buffer. But it's
+ possible that it might have completed after we noticed
+ it but before we wiped it. So double check its status
+ here first. */
+ if (bp1->state != pvr2_buffer_state_queued) continue;
+ pvr2_buffer_set_idle(bp1);
+ }
+ if (sp->buffer_total_count != sp->buffer_target_count) {
+ pvr2_stream_achieve_buffer_count(sp);
+ }
+}
+
+static void pvr2_stream_init(struct pvr2_stream *sp)
+{
+ spin_lock_init(&sp->list_lock);
+ mutex_init(&sp->mutex);
+ INIT_LIST_HEAD(&sp->queued_list);
+ INIT_LIST_HEAD(&sp->ready_list);
+ INIT_LIST_HEAD(&sp->idle_list);
+}
+
+static void pvr2_stream_done(struct pvr2_stream *sp)
+{
+ mutex_lock(&sp->mutex); do {
+ pvr2_stream_internal_flush(sp);
+ pvr2_stream_buffer_count(sp,0);
+ } while (0); mutex_unlock(&sp->mutex);
+}
+
+static void buffer_complete(struct urb *urb)
+{
+ struct pvr2_buffer *bp = urb->context;
+ struct pvr2_stream *sp;
+ unsigned long irq_flags;
+ BUFFER_CHECK(bp);
+ sp = bp->stream;
+ bp->used_count = 0;
+ bp->status = 0;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/ bufferComplete %p stat=%d cnt=%d",
+ bp,urb->status,urb->actual_length);
+ spin_lock_irqsave(&sp->list_lock,irq_flags);
+ if ((!(urb->status)) ||
+ (urb->status == -ENOENT) ||
+ (urb->status == -ECONNRESET) ||
+ (urb->status == -ESHUTDOWN)) {
+ (sp->buffers_processed)++;
+ sp->bytes_processed += urb->actual_length;
+ bp->used_count = urb->actual_length;
+ if (sp->fail_count) {
+ pvr2_trace(PVR2_TRACE_TOLERANCE,
+ "stream %p transfer ok"
+ " - fail count reset",sp);
+ sp->fail_count = 0;
+ }
+ } else if (sp->fail_count < sp->fail_tolerance) {
+ // We can tolerate this error, because we're below the
+ // threshold...
+ (sp->fail_count)++;
+ (sp->buffers_failed)++;
+ pvr2_trace(PVR2_TRACE_TOLERANCE,
+ "stream %p ignoring error %d"
+ " - fail count increased to %u",
+ sp,urb->status,sp->fail_count);
+ } else {
+ (sp->buffers_failed)++;
+ bp->status = urb->status;
+ }
+ spin_unlock_irqrestore(&sp->list_lock,irq_flags);
+ pvr2_buffer_set_ready(bp);
+ if (sp && sp->callback_func) {
+ sp->callback_func(sp->callback_data);
+ }
+}
+
+struct pvr2_stream *pvr2_stream_create(void)
+{
+ struct pvr2_stream *sp;
+ sp = kzalloc(sizeof(*sp),GFP_KERNEL);
+ if (!sp) return sp;
+ pvr2_trace(PVR2_TRACE_INIT,"pvr2_stream_create: sp=%p",sp);
+ pvr2_stream_init(sp);
+ return sp;
+}
+
+void pvr2_stream_destroy(struct pvr2_stream *sp)
+{
+ if (!sp) return;
+ pvr2_trace(PVR2_TRACE_INIT,"pvr2_stream_destroy: sp=%p",sp);
+ pvr2_stream_done(sp);
+ kfree(sp);
+}
+
+void pvr2_stream_setup(struct pvr2_stream *sp,
+ struct usb_device *dev,
+ int endpoint,
+ unsigned int tolerance)
+{
+ mutex_lock(&sp->mutex); do {
+ pvr2_stream_internal_flush(sp);
+ sp->dev = dev;
+ sp->endpoint = endpoint;
+ sp->fail_tolerance = tolerance;
+ } while(0); mutex_unlock(&sp->mutex);
+}
+
+void pvr2_stream_set_callback(struct pvr2_stream *sp,
+ pvr2_stream_callback func,
+ void *data)
+{
+ unsigned long irq_flags;
+ mutex_lock(&sp->mutex); do {
+ spin_lock_irqsave(&sp->list_lock,irq_flags);
+ sp->callback_data = data;
+ sp->callback_func = func;
+ spin_unlock_irqrestore(&sp->list_lock,irq_flags);
+ } while(0); mutex_unlock(&sp->mutex);
+}
+
+void pvr2_stream_get_stats(struct pvr2_stream *sp,
+ struct pvr2_stream_stats *stats,
+ int zero_counts)
+{
+ unsigned long irq_flags;
+ spin_lock_irqsave(&sp->list_lock,irq_flags);
+ if (stats) {
+ stats->buffers_in_queue = sp->q_count;
+ stats->buffers_in_idle = sp->i_count;
+ stats->buffers_in_ready = sp->r_count;
+ stats->buffers_processed = sp->buffers_processed;
+ stats->buffers_failed = sp->buffers_failed;
+ stats->bytes_processed = sp->bytes_processed;
+ }
+ if (zero_counts) {
+ sp->buffers_processed = 0;
+ sp->buffers_failed = 0;
+ sp->bytes_processed = 0;
+ }
+ spin_unlock_irqrestore(&sp->list_lock,irq_flags);
+}
+
+/* Query / set the nominal buffer count */
+int pvr2_stream_get_buffer_count(struct pvr2_stream *sp)
+{
+ return sp->buffer_target_count;
+}
+
+int pvr2_stream_set_buffer_count(struct pvr2_stream *sp,unsigned int cnt)
+{
+ int ret;
+ if (sp->buffer_target_count == cnt) return 0;
+ mutex_lock(&sp->mutex); do {
+ sp->buffer_target_count = cnt;
+ ret = pvr2_stream_achieve_buffer_count(sp);
+ } while(0); mutex_unlock(&sp->mutex);
+ return ret;
+}
+
+struct pvr2_buffer *pvr2_stream_get_idle_buffer(struct pvr2_stream *sp)
+{
+ struct list_head *lp = sp->idle_list.next;
+ if (lp == &sp->idle_list) return NULL;
+ return list_entry(lp,struct pvr2_buffer,list_overhead);
+}
+
+struct pvr2_buffer *pvr2_stream_get_ready_buffer(struct pvr2_stream *sp)
+{
+ struct list_head *lp = sp->ready_list.next;
+ if (lp == &sp->ready_list) return NULL;
+ return list_entry(lp,struct pvr2_buffer,list_overhead);
+}
+
+struct pvr2_buffer *pvr2_stream_get_buffer(struct pvr2_stream *sp,int id)
+{
+ if (id < 0) return NULL;
+ if (id >= sp->buffer_total_count) return NULL;
+ return sp->buffers[id];
+}
+
+int pvr2_stream_get_ready_count(struct pvr2_stream *sp)
+{
+ return sp->r_count;
+}
+
+void pvr2_stream_kill(struct pvr2_stream *sp)
+{
+ struct pvr2_buffer *bp;
+ mutex_lock(&sp->mutex); do {
+ pvr2_stream_internal_flush(sp);
+ while ((bp = pvr2_stream_get_ready_buffer(sp)) != NULL) {
+ pvr2_buffer_set_idle(bp);
+ }
+ if (sp->buffer_total_count != sp->buffer_target_count) {
+ pvr2_stream_achieve_buffer_count(sp);
+ }
+ } while(0); mutex_unlock(&sp->mutex);
+}
+
+int pvr2_buffer_queue(struct pvr2_buffer *bp)
+{
+#undef SEED_BUFFER
+#ifdef SEED_BUFFER
+ unsigned int idx;
+ unsigned int val;
+#endif
+ int ret = 0;
+ struct pvr2_stream *sp;
+ if (!bp) return -EINVAL;
+ sp = bp->stream;
+ mutex_lock(&sp->mutex); do {
+ pvr2_buffer_wipe(bp);
+ if (!sp->dev) {
+ ret = -EIO;
+ break;
+ }
+ pvr2_buffer_set_queued(bp);
+#ifdef SEED_BUFFER
+ for (idx = 0; idx < (bp->max_count) / 4; idx++) {
+ val = bp->id << 24;
+ val |= idx;
+ ((unsigned int *)(bp->ptr))[idx] = val;
+ }
+#endif
+ bp->status = -EINPROGRESS;
+ usb_fill_bulk_urb(bp->purb, // struct urb *urb
+ sp->dev, // struct usb_device *dev
+ // endpoint (below)
+ usb_rcvbulkpipe(sp->dev,sp->endpoint),
+ bp->ptr, // void *transfer_buffer
+ bp->max_count, // int buffer_length
+ buffer_complete,
+ bp);
+ usb_submit_urb(bp->purb,GFP_KERNEL);
+ } while(0); mutex_unlock(&sp->mutex);
+ return ret;
+}
+
+int pvr2_buffer_set_buffer(struct pvr2_buffer *bp,void *ptr,unsigned int cnt)
+{
+ int ret = 0;
+ unsigned long irq_flags;
+ struct pvr2_stream *sp;
+ if (!bp) return -EINVAL;
+ sp = bp->stream;
+ mutex_lock(&sp->mutex); do {
+ spin_lock_irqsave(&sp->list_lock,irq_flags);
+ if (bp->state != pvr2_buffer_state_idle) {
+ ret = -EPERM;
+ } else {
+ bp->ptr = ptr;
+ bp->stream->i_bcount -= bp->max_count;
+ bp->max_count = cnt;
+ bp->stream->i_bcount += bp->max_count;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/ bufferPool "
+ " %8s cap cap=%07d cnt=%02d",
+ pvr2_buffer_state_decode(
+ pvr2_buffer_state_idle),
+ bp->stream->i_bcount,bp->stream->i_count);
+ }
+ spin_unlock_irqrestore(&sp->list_lock,irq_flags);
+ } while(0); mutex_unlock(&sp->mutex);
+ return ret;
+}
+
+unsigned int pvr2_buffer_get_count(struct pvr2_buffer *bp)
+{
+ return bp->used_count;
+}
+
+int pvr2_buffer_get_status(struct pvr2_buffer *bp)
+{
+ return bp->status;
+}
+
+int pvr2_buffer_get_id(struct pvr2_buffer *bp)
+{
+ return bp->id;
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-io.h b/drivers/media/video/pvrusb2/pvrusb2-io.h
new file mode 100644
index 00000000..afb7e87c
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-io.h
@@ -0,0 +1,102 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef __PVRUSB2_IO_H
+#define __PVRUSB2_IO_H
+
+#include <linux/usb.h>
+#include <linux/list.h>
+
+typedef void (*pvr2_stream_callback)(void *);
+
+enum pvr2_buffer_state {
+ pvr2_buffer_state_none = 0, // Not on any list
+ pvr2_buffer_state_idle = 1, // Buffer is ready to be used again
+ pvr2_buffer_state_queued = 2, // Buffer has been queued for filling
+ pvr2_buffer_state_ready = 3, // Buffer has data available
+};
+
+struct pvr2_stream;
+struct pvr2_buffer;
+
+struct pvr2_stream_stats {
+ unsigned int buffers_in_queue;
+ unsigned int buffers_in_idle;
+ unsigned int buffers_in_ready;
+ unsigned int buffers_processed;
+ unsigned int buffers_failed;
+ unsigned int bytes_processed;
+};
+
+/* Initialize / tear down stream structure */
+struct pvr2_stream *pvr2_stream_create(void);
+void pvr2_stream_destroy(struct pvr2_stream *);
+void pvr2_stream_setup(struct pvr2_stream *,
+ struct usb_device *dev,int endpoint,
+ unsigned int tolerance);
+void pvr2_stream_set_callback(struct pvr2_stream *,
+ pvr2_stream_callback func,
+ void *data);
+void pvr2_stream_get_stats(struct pvr2_stream *,
+ struct pvr2_stream_stats *,
+ int zero_counts);
+
+/* Query / set the nominal buffer count */
+int pvr2_stream_get_buffer_count(struct pvr2_stream *);
+int pvr2_stream_set_buffer_count(struct pvr2_stream *,unsigned int);
+
+/* Get a pointer to a buffer that is either idle, ready, or is specified
+ named. */
+struct pvr2_buffer *pvr2_stream_get_idle_buffer(struct pvr2_stream *);
+struct pvr2_buffer *pvr2_stream_get_ready_buffer(struct pvr2_stream *);
+struct pvr2_buffer *pvr2_stream_get_buffer(struct pvr2_stream *sp,int id);
+
+/* Find out how many buffers are idle or ready */
+int pvr2_stream_get_ready_count(struct pvr2_stream *);
+
+
+/* Kill all pending buffers and throw away any ready buffers as well */
+void pvr2_stream_kill(struct pvr2_stream *);
+
+/* Set up the actual storage for a buffer */
+int pvr2_buffer_set_buffer(struct pvr2_buffer *,void *ptr,unsigned int cnt);
+
+/* Find out size of data in the given ready buffer */
+unsigned int pvr2_buffer_get_count(struct pvr2_buffer *);
+
+/* Retrieve completion code for given ready buffer */
+int pvr2_buffer_get_status(struct pvr2_buffer *);
+
+/* Retrieve ID of given buffer */
+int pvr2_buffer_get_id(struct pvr2_buffer *);
+
+/* Start reading into given buffer (kill it if needed) */
+int pvr2_buffer_queue(struct pvr2_buffer *);
+
+#endif /* __PVRUSB2_IO_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-ioread.c b/drivers/media/video/pvrusb2/pvrusb2-ioread.c
new file mode 100644
index 00000000..bba6115c
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-ioread.c
@@ -0,0 +1,512 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "pvrusb2-ioread.h"
+#include "pvrusb2-debug.h"
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <asm/uaccess.h>
+
+#define BUFFER_COUNT 32
+#define BUFFER_SIZE PAGE_ALIGN(0x4000)
+
+struct pvr2_ioread {
+ struct pvr2_stream *stream;
+ char *buffer_storage[BUFFER_COUNT];
+ char *sync_key_ptr;
+ unsigned int sync_key_len;
+ unsigned int sync_buf_offs;
+ unsigned int sync_state;
+ unsigned int sync_trashed_count;
+ int enabled; // Streaming is on
+ int spigot_open; // OK to pass data to client
+ int stream_running; // Passing data to client now
+
+ /* State relevant to current buffer being read */
+ struct pvr2_buffer *c_buf;
+ char *c_data_ptr;
+ unsigned int c_data_len;
+ unsigned int c_data_offs;
+ struct mutex mutex;
+};
+
+static int pvr2_ioread_init(struct pvr2_ioread *cp)
+{
+ unsigned int idx;
+
+ cp->stream = NULL;
+ mutex_init(&cp->mutex);
+
+ for (idx = 0; idx < BUFFER_COUNT; idx++) {
+ cp->buffer_storage[idx] = kmalloc(BUFFER_SIZE,GFP_KERNEL);
+ if (!(cp->buffer_storage[idx])) break;
+ }
+
+ if (idx < BUFFER_COUNT) {
+ // An allocation appears to have failed
+ for (idx = 0; idx < BUFFER_COUNT; idx++) {
+ if (!(cp->buffer_storage[idx])) continue;
+ kfree(cp->buffer_storage[idx]);
+ }
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void pvr2_ioread_done(struct pvr2_ioread *cp)
+{
+ unsigned int idx;
+
+ pvr2_ioread_setup(cp,NULL);
+ for (idx = 0; idx < BUFFER_COUNT; idx++) {
+ if (!(cp->buffer_storage[idx])) continue;
+ kfree(cp->buffer_storage[idx]);
+ }
+}
+
+struct pvr2_ioread *pvr2_ioread_create(void)
+{
+ struct pvr2_ioread *cp;
+ cp = kzalloc(sizeof(*cp),GFP_KERNEL);
+ if (!cp) return NULL;
+ pvr2_trace(PVR2_TRACE_STRUCT,"pvr2_ioread_create id=%p",cp);
+ if (pvr2_ioread_init(cp) < 0) {
+ kfree(cp);
+ return NULL;
+ }
+ return cp;
+}
+
+void pvr2_ioread_destroy(struct pvr2_ioread *cp)
+{
+ if (!cp) return;
+ pvr2_ioread_done(cp);
+ pvr2_trace(PVR2_TRACE_STRUCT,"pvr2_ioread_destroy id=%p",cp);
+ if (cp->sync_key_ptr) {
+ kfree(cp->sync_key_ptr);
+ cp->sync_key_ptr = NULL;
+ }
+ kfree(cp);
+}
+
+void pvr2_ioread_set_sync_key(struct pvr2_ioread *cp,
+ const char *sync_key_ptr,
+ unsigned int sync_key_len)
+{
+ if (!cp) return;
+
+ if (!sync_key_ptr) sync_key_len = 0;
+ if ((sync_key_len == cp->sync_key_len) &&
+ ((!sync_key_len) ||
+ (!memcmp(sync_key_ptr,cp->sync_key_ptr,sync_key_len)))) return;
+
+ if (sync_key_len != cp->sync_key_len) {
+ if (cp->sync_key_ptr) {
+ kfree(cp->sync_key_ptr);
+ cp->sync_key_ptr = NULL;
+ }
+ cp->sync_key_len = 0;
+ if (sync_key_len) {
+ cp->sync_key_ptr = kmalloc(sync_key_len,GFP_KERNEL);
+ if (cp->sync_key_ptr) {
+ cp->sync_key_len = sync_key_len;
+ }
+ }
+ }
+ if (!cp->sync_key_len) return;
+ memcpy(cp->sync_key_ptr,sync_key_ptr,cp->sync_key_len);
+}
+
+static void pvr2_ioread_stop(struct pvr2_ioread *cp)
+{
+ if (!(cp->enabled)) return;
+ pvr2_trace(PVR2_TRACE_START_STOP,
+ "/*---TRACE_READ---*/ pvr2_ioread_stop id=%p",cp);
+ pvr2_stream_kill(cp->stream);
+ cp->c_buf = NULL;
+ cp->c_data_ptr = NULL;
+ cp->c_data_len = 0;
+ cp->c_data_offs = 0;
+ cp->enabled = 0;
+ cp->stream_running = 0;
+ cp->spigot_open = 0;
+ if (cp->sync_state) {
+ pvr2_trace(PVR2_TRACE_DATA_FLOW,
+ "/*---TRACE_READ---*/ sync_state <== 0");
+ cp->sync_state = 0;
+ }
+}
+
+static int pvr2_ioread_start(struct pvr2_ioread *cp)
+{
+ int stat;
+ struct pvr2_buffer *bp;
+ if (cp->enabled) return 0;
+ if (!(cp->stream)) return 0;
+ pvr2_trace(PVR2_TRACE_START_STOP,
+ "/*---TRACE_READ---*/ pvr2_ioread_start id=%p",cp);
+ while ((bp = pvr2_stream_get_idle_buffer(cp->stream)) != NULL) {
+ stat = pvr2_buffer_queue(bp);
+ if (stat < 0) {
+ pvr2_trace(PVR2_TRACE_DATA_FLOW,
+ "/*---TRACE_READ---*/"
+ " pvr2_ioread_start id=%p"
+ " error=%d",
+ cp,stat);
+ pvr2_ioread_stop(cp);
+ return stat;
+ }
+ }
+ cp->enabled = !0;
+ cp->c_buf = NULL;
+ cp->c_data_ptr = NULL;
+ cp->c_data_len = 0;
+ cp->c_data_offs = 0;
+ cp->stream_running = 0;
+ if (cp->sync_key_len) {
+ pvr2_trace(PVR2_TRACE_DATA_FLOW,
+ "/*---TRACE_READ---*/ sync_state <== 1");
+ cp->sync_state = 1;
+ cp->sync_trashed_count = 0;
+ cp->sync_buf_offs = 0;
+ }
+ cp->spigot_open = 0;
+ return 0;
+}
+
+struct pvr2_stream *pvr2_ioread_get_stream(struct pvr2_ioread *cp)
+{
+ return cp->stream;
+}
+
+int pvr2_ioread_setup(struct pvr2_ioread *cp,struct pvr2_stream *sp)
+{
+ int ret;
+ unsigned int idx;
+ struct pvr2_buffer *bp;
+
+ mutex_lock(&cp->mutex); do {
+ if (cp->stream) {
+ pvr2_trace(PVR2_TRACE_START_STOP,
+ "/*---TRACE_READ---*/"
+ " pvr2_ioread_setup (tear-down) id=%p",cp);
+ pvr2_ioread_stop(cp);
+ pvr2_stream_kill(cp->stream);
+ if (pvr2_stream_get_buffer_count(cp->stream)) {
+ pvr2_stream_set_buffer_count(cp->stream,0);
+ }
+ cp->stream = NULL;
+ }
+ if (sp) {
+ pvr2_trace(PVR2_TRACE_START_STOP,
+ "/*---TRACE_READ---*/"
+ " pvr2_ioread_setup (setup) id=%p",cp);
+ pvr2_stream_kill(sp);
+ ret = pvr2_stream_set_buffer_count(sp,BUFFER_COUNT);
+ if (ret < 0) {
+ mutex_unlock(&cp->mutex);
+ return ret;
+ }
+ for (idx = 0; idx < BUFFER_COUNT; idx++) {
+ bp = pvr2_stream_get_buffer(sp,idx);
+ pvr2_buffer_set_buffer(bp,
+ cp->buffer_storage[idx],
+ BUFFER_SIZE);
+ }
+ cp->stream = sp;
+ }
+ } while (0); mutex_unlock(&cp->mutex);
+
+ return 0;
+}
+
+int pvr2_ioread_set_enabled(struct pvr2_ioread *cp,int fl)
+{
+ int ret = 0;
+ if ((!fl) == (!(cp->enabled))) return ret;
+
+ mutex_lock(&cp->mutex); do {
+ if (fl) {
+ ret = pvr2_ioread_start(cp);
+ } else {
+ pvr2_ioread_stop(cp);
+ }
+ } while (0); mutex_unlock(&cp->mutex);
+ return ret;
+}
+
+static int pvr2_ioread_get_buffer(struct pvr2_ioread *cp)
+{
+ int stat;
+
+ while (cp->c_data_len <= cp->c_data_offs) {
+ if (cp->c_buf) {
+ // Flush out current buffer first.
+ stat = pvr2_buffer_queue(cp->c_buf);
+ if (stat < 0) {
+ // Streaming error...
+ pvr2_trace(PVR2_TRACE_DATA_FLOW,
+ "/*---TRACE_READ---*/"
+ " pvr2_ioread_read id=%p"
+ " queue_error=%d",
+ cp,stat);
+ pvr2_ioread_stop(cp);
+ return 0;
+ }
+ cp->c_buf = NULL;
+ cp->c_data_ptr = NULL;
+ cp->c_data_len = 0;
+ cp->c_data_offs = 0;
+ }
+ // Now get a freshly filled buffer.
+ cp->c_buf = pvr2_stream_get_ready_buffer(cp->stream);
+ if (!cp->c_buf) break; // Nothing ready; done.
+ cp->c_data_len = pvr2_buffer_get_count(cp->c_buf);
+ if (!cp->c_data_len) {
+ // Nothing transferred. Was there an error?
+ stat = pvr2_buffer_get_status(cp->c_buf);
+ if (stat < 0) {
+ // Streaming error...
+ pvr2_trace(PVR2_TRACE_DATA_FLOW,
+ "/*---TRACE_READ---*/"
+ " pvr2_ioread_read id=%p"
+ " buffer_error=%d",
+ cp,stat);
+ pvr2_ioread_stop(cp);
+ // Give up.
+ return 0;
+ }
+ // Start over...
+ continue;
+ }
+ cp->c_data_offs = 0;
+ cp->c_data_ptr = cp->buffer_storage[
+ pvr2_buffer_get_id(cp->c_buf)];
+ }
+ return !0;
+}
+
+static void pvr2_ioread_filter(struct pvr2_ioread *cp)
+{
+ unsigned int idx;
+ if (!cp->enabled) return;
+ if (cp->sync_state != 1) return;
+
+ // Search the stream for our synchronization key. This is made
+ // complicated by the fact that in order to be honest with
+ // ourselves here we must search across buffer boundaries...
+ mutex_lock(&cp->mutex); while (1) {
+ // Ensure we have a buffer
+ if (!pvr2_ioread_get_buffer(cp)) break;
+ if (!cp->c_data_len) break;
+
+ // Now walk the buffer contents until we match the key or
+ // run out of buffer data.
+ for (idx = cp->c_data_offs; idx < cp->c_data_len; idx++) {
+ if (cp->sync_buf_offs >= cp->sync_key_len) break;
+ if (cp->c_data_ptr[idx] ==
+ cp->sync_key_ptr[cp->sync_buf_offs]) {
+ // Found the next key byte
+ (cp->sync_buf_offs)++;
+ } else {
+ // Whoops, mismatched. Start key over...
+ cp->sync_buf_offs = 0;
+ }
+ }
+
+ // Consume what we've walked through
+ cp->c_data_offs += idx;
+ cp->sync_trashed_count += idx;
+
+ // If we've found the key, then update state and get out.
+ if (cp->sync_buf_offs >= cp->sync_key_len) {
+ cp->sync_trashed_count -= cp->sync_key_len;
+ pvr2_trace(PVR2_TRACE_DATA_FLOW,
+ "/*---TRACE_READ---*/"
+ " sync_state <== 2 (skipped %u bytes)",
+ cp->sync_trashed_count);
+ cp->sync_state = 2;
+ cp->sync_buf_offs = 0;
+ break;
+ }
+
+ if (cp->c_data_offs < cp->c_data_len) {
+ // Sanity check - should NEVER get here
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "ERROR: pvr2_ioread filter sync problem"
+ " len=%u offs=%u",
+ cp->c_data_len,cp->c_data_offs);
+ // Get out so we don't get stuck in an infinite
+ // loop.
+ break;
+ }
+
+ continue; // (for clarity)
+ } mutex_unlock(&cp->mutex);
+}
+
+int pvr2_ioread_avail(struct pvr2_ioread *cp)
+{
+ int ret;
+ if (!(cp->enabled)) {
+ // Stream is not enabled; so this is an I/O error
+ return -EIO;
+ }
+
+ if (cp->sync_state == 1) {
+ pvr2_ioread_filter(cp);
+ if (cp->sync_state == 1) return -EAGAIN;
+ }
+
+ ret = 0;
+ if (cp->stream_running) {
+ if (!pvr2_stream_get_ready_count(cp->stream)) {
+ // No data available at all right now.
+ ret = -EAGAIN;
+ }
+ } else {
+ if (pvr2_stream_get_ready_count(cp->stream) < BUFFER_COUNT/2) {
+ // Haven't buffered up enough yet; try again later
+ ret = -EAGAIN;
+ }
+ }
+
+ if ((!(cp->spigot_open)) != (!(ret == 0))) {
+ cp->spigot_open = (ret == 0);
+ pvr2_trace(PVR2_TRACE_DATA_FLOW,
+ "/*---TRACE_READ---*/ data is %s",
+ cp->spigot_open ? "available" : "pending");
+ }
+
+ return ret;
+}
+
+int pvr2_ioread_read(struct pvr2_ioread *cp,void __user *buf,unsigned int cnt)
+{
+ unsigned int copied_cnt;
+ unsigned int bcnt;
+ const char *src;
+ int stat;
+ int ret = 0;
+ unsigned int req_cnt = cnt;
+
+ if (!cnt) {
+ pvr2_trace(PVR2_TRACE_TRAP,
+ "/*---TRACE_READ---*/ pvr2_ioread_read id=%p"
+ " ZERO Request? Returning zero.",cp);
+ return 0;
+ }
+
+ stat = pvr2_ioread_avail(cp);
+ if (stat < 0) return stat;
+
+ cp->stream_running = !0;
+
+ mutex_lock(&cp->mutex); do {
+
+ // Suck data out of the buffers and copy to the user
+ copied_cnt = 0;
+ if (!buf) cnt = 0;
+ while (1) {
+ if (!pvr2_ioread_get_buffer(cp)) {
+ ret = -EIO;
+ break;
+ }
+
+ if (!cnt) break;
+
+ if (cp->sync_state == 2) {
+ // We're repeating the sync key data into
+ // the stream.
+ src = cp->sync_key_ptr + cp->sync_buf_offs;
+ bcnt = cp->sync_key_len - cp->sync_buf_offs;
+ } else {
+ // Normal buffer copy
+ src = cp->c_data_ptr + cp->c_data_offs;
+ bcnt = cp->c_data_len - cp->c_data_offs;
+ }
+
+ if (!bcnt) break;
+
+ // Don't run past user's buffer
+ if (bcnt > cnt) bcnt = cnt;
+
+ if (copy_to_user(buf,src,bcnt)) {
+ // User supplied a bad pointer?
+ // Give up - this *will* cause data
+ // to be lost.
+ ret = -EFAULT;
+ break;
+ }
+ cnt -= bcnt;
+ buf += bcnt;
+ copied_cnt += bcnt;
+
+ if (cp->sync_state == 2) {
+ // Update offset inside sync key that we're
+ // repeating back out.
+ cp->sync_buf_offs += bcnt;
+ if (cp->sync_buf_offs >= cp->sync_key_len) {
+ // Consumed entire key; switch mode
+ // to normal.
+ pvr2_trace(PVR2_TRACE_DATA_FLOW,
+ "/*---TRACE_READ---*/"
+ " sync_state <== 0");
+ cp->sync_state = 0;
+ }
+ } else {
+ // Update buffer offset.
+ cp->c_data_offs += bcnt;
+ }
+ }
+
+ } while (0); mutex_unlock(&cp->mutex);
+
+ if (!ret) {
+ if (copied_cnt) {
+ // If anything was copied, return that count
+ ret = copied_cnt;
+ } else {
+ // Nothing copied; suggest to caller that another
+ // attempt should be tried again later
+ ret = -EAGAIN;
+ }
+ }
+
+ pvr2_trace(PVR2_TRACE_DATA_FLOW,
+ "/*---TRACE_READ---*/ pvr2_ioread_read"
+ " id=%p request=%d result=%d",
+ cp,req_cnt,ret);
+ return ret;
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-ioread.h b/drivers/media/video/pvrusb2/pvrusb2-ioread.h
new file mode 100644
index 00000000..100e0780
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-ioread.h
@@ -0,0 +1,48 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef __PVRUSB2_IOREAD_H
+#define __PVRUSB2_IOREAD_H
+
+#include "pvrusb2-io.h"
+
+struct pvr2_ioread;
+
+struct pvr2_ioread *pvr2_ioread_create(void);
+void pvr2_ioread_destroy(struct pvr2_ioread *);
+int pvr2_ioread_setup(struct pvr2_ioread *,struct pvr2_stream *);
+struct pvr2_stream *pvr2_ioread_get_stream(struct pvr2_ioread *);
+void pvr2_ioread_set_sync_key(struct pvr2_ioread *,
+ const char *sync_key_ptr,
+ unsigned int sync_key_len);
+int pvr2_ioread_set_enabled(struct pvr2_ioread *,int fl);
+int pvr2_ioread_read(struct pvr2_ioread *,void __user *buf,unsigned int cnt);
+int pvr2_ioread_avail(struct pvr2_ioread *);
+
+#endif /* __PVRUSB2_IOREAD_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-main.c b/drivers/media/video/pvrusb2/pvrusb2-main.c
new file mode 100644
index 00000000..c1d9bb61
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-main.c
@@ -0,0 +1,182 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+
+#include "pvrusb2-hdw.h"
+#include "pvrusb2-devattr.h"
+#include "pvrusb2-context.h"
+#include "pvrusb2-debug.h"
+#include "pvrusb2-v4l2.h"
+#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS
+#include "pvrusb2-sysfs.h"
+#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */
+
+#define DRIVER_AUTHOR "Mike Isely <isely@pobox.com>"
+#define DRIVER_DESC "Hauppauge WinTV-PVR-USB2 MPEG2 Encoder/Tuner"
+#define DRIVER_VERSION "V4L in-tree version"
+
+#define DEFAULT_DEBUG_MASK (PVR2_TRACE_ERROR_LEGS| \
+ PVR2_TRACE_INFO| \
+ PVR2_TRACE_STD| \
+ PVR2_TRACE_TOLERANCE| \
+ PVR2_TRACE_TRAP| \
+ 0)
+
+int pvrusb2_debug = DEFAULT_DEBUG_MASK;
+
+module_param_named(debug,pvrusb2_debug,int,S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug trace mask");
+
+#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS
+static struct pvr2_sysfs_class *class_ptr = NULL;
+#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */
+
+static void pvr_setup_attach(struct pvr2_context *pvr)
+{
+ /* Create association with v4l layer */
+ pvr2_v4l2_create(pvr);
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+ /* Create association with dvb layer */
+ pvr2_dvb_create(pvr);
+#endif
+#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS
+ pvr2_sysfs_create(pvr,class_ptr);
+#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */
+}
+
+static int pvr_probe(struct usb_interface *intf,
+ const struct usb_device_id *devid)
+{
+ struct pvr2_context *pvr;
+
+ /* Create underlying hardware interface */
+ pvr = pvr2_context_create(intf,devid,pvr_setup_attach);
+ if (!pvr) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Failed to create hdw handler");
+ return -ENOMEM;
+ }
+
+ pvr2_trace(PVR2_TRACE_INIT,"pvr_probe(pvr=%p)",pvr);
+
+ usb_set_intfdata(intf, pvr);
+
+ return 0;
+}
+
+/*
+ * pvr_disconnect()
+ *
+ */
+static void pvr_disconnect(struct usb_interface *intf)
+{
+ struct pvr2_context *pvr = usb_get_intfdata(intf);
+
+ pvr2_trace(PVR2_TRACE_INIT,"pvr_disconnect(pvr=%p) BEGIN",pvr);
+
+ usb_set_intfdata (intf, NULL);
+ pvr2_context_disconnect(pvr);
+
+ pvr2_trace(PVR2_TRACE_INIT,"pvr_disconnect(pvr=%p) DONE",pvr);
+
+}
+
+static struct usb_driver pvr_driver = {
+ .name = "pvrusb2",
+ .id_table = pvr2_device_table,
+ .probe = pvr_probe,
+ .disconnect = pvr_disconnect
+};
+
+/*
+ * pvr_init() / pvr_exit()
+ *
+ * This code is run to initialize/exit the driver.
+ *
+ */
+static int __init pvr_init(void)
+{
+ int ret;
+
+ pvr2_trace(PVR2_TRACE_INIT,"pvr_init");
+
+ ret = pvr2_context_global_init();
+ if (ret != 0) {
+ pvr2_trace(PVR2_TRACE_INIT,"pvr_init failure code=%d",ret);
+ return ret;
+ }
+
+#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS
+ class_ptr = pvr2_sysfs_class_create();
+#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */
+
+ ret = usb_register(&pvr_driver);
+
+ if (ret == 0)
+ printk(KERN_INFO "pvrusb2: " DRIVER_VERSION ":"
+ DRIVER_DESC "\n");
+ if (pvrusb2_debug)
+ printk(KERN_INFO "pvrusb2: Debug mask is %d (0x%x)\n",
+ pvrusb2_debug,pvrusb2_debug);
+
+ pvr2_trace(PVR2_TRACE_INIT,"pvr_init complete");
+
+ return ret;
+}
+
+static void __exit pvr_exit(void)
+{
+ pvr2_trace(PVR2_TRACE_INIT,"pvr_exit");
+
+ usb_deregister(&pvr_driver);
+
+ pvr2_context_global_done();
+
+#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS
+ pvr2_sysfs_class_destroy(class_ptr);
+#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */
+
+ pvr2_trace(PVR2_TRACE_INIT,"pvr_exit complete");
+}
+
+module_init(pvr_init);
+module_exit(pvr_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.9.1");
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-std.c b/drivers/media/video/pvrusb2/pvrusb2-std.c
new file mode 100644
index 00000000..453627b0
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-std.c
@@ -0,0 +1,411 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "pvrusb2-std.h"
+#include "pvrusb2-debug.h"
+#include <asm/string.h>
+#include <linux/slab.h>
+
+struct std_name {
+ const char *name;
+ v4l2_std_id id;
+};
+
+
+#define CSTD_PAL \
+ (V4L2_STD_PAL_B| \
+ V4L2_STD_PAL_B1| \
+ V4L2_STD_PAL_G| \
+ V4L2_STD_PAL_H| \
+ V4L2_STD_PAL_I| \
+ V4L2_STD_PAL_D| \
+ V4L2_STD_PAL_D1| \
+ V4L2_STD_PAL_K| \
+ V4L2_STD_PAL_M| \
+ V4L2_STD_PAL_N| \
+ V4L2_STD_PAL_Nc| \
+ V4L2_STD_PAL_60)
+
+#define CSTD_NTSC \
+ (V4L2_STD_NTSC_M| \
+ V4L2_STD_NTSC_M_JP| \
+ V4L2_STD_NTSC_M_KR| \
+ V4L2_STD_NTSC_443)
+
+#define CSTD_ATSC \
+ (V4L2_STD_ATSC_8_VSB| \
+ V4L2_STD_ATSC_16_VSB)
+
+#define CSTD_SECAM \
+ (V4L2_STD_SECAM_B| \
+ V4L2_STD_SECAM_D| \
+ V4L2_STD_SECAM_G| \
+ V4L2_STD_SECAM_H| \
+ V4L2_STD_SECAM_K| \
+ V4L2_STD_SECAM_K1| \
+ V4L2_STD_SECAM_L| \
+ V4L2_STD_SECAM_LC)
+
+#define TSTD_B (V4L2_STD_PAL_B|V4L2_STD_SECAM_B)
+#define TSTD_B1 (V4L2_STD_PAL_B1)
+#define TSTD_D (V4L2_STD_PAL_D|V4L2_STD_SECAM_D)
+#define TSTD_D1 (V4L2_STD_PAL_D1)
+#define TSTD_G (V4L2_STD_PAL_G|V4L2_STD_SECAM_G)
+#define TSTD_H (V4L2_STD_PAL_H|V4L2_STD_SECAM_H)
+#define TSTD_I (V4L2_STD_PAL_I)
+#define TSTD_K (V4L2_STD_PAL_K|V4L2_STD_SECAM_K)
+#define TSTD_K1 (V4L2_STD_SECAM_K1)
+#define TSTD_L (V4L2_STD_SECAM_L)
+#define TSTD_M (V4L2_STD_PAL_M|V4L2_STD_NTSC_M)
+#define TSTD_N (V4L2_STD_PAL_N)
+#define TSTD_Nc (V4L2_STD_PAL_Nc)
+#define TSTD_60 (V4L2_STD_PAL_60)
+
+#define CSTD_ALL (CSTD_PAL|CSTD_NTSC|CSTD_ATSC|CSTD_SECAM)
+
+/* Mapping of standard bits to color system */
+static const struct std_name std_groups[] = {
+ {"PAL",CSTD_PAL},
+ {"NTSC",CSTD_NTSC},
+ {"SECAM",CSTD_SECAM},
+ {"ATSC",CSTD_ATSC},
+};
+
+/* Mapping of standard bits to modulation system */
+static const struct std_name std_items[] = {
+ {"B",TSTD_B},
+ {"B1",TSTD_B1},
+ {"D",TSTD_D},
+ {"D1",TSTD_D1},
+ {"G",TSTD_G},
+ {"H",TSTD_H},
+ {"I",TSTD_I},
+ {"K",TSTD_K},
+ {"K1",TSTD_K1},
+ {"L",TSTD_L},
+ {"LC",V4L2_STD_SECAM_LC},
+ {"M",TSTD_M},
+ {"Mj",V4L2_STD_NTSC_M_JP},
+ {"443",V4L2_STD_NTSC_443},
+ {"Mk",V4L2_STD_NTSC_M_KR},
+ {"N",TSTD_N},
+ {"Nc",TSTD_Nc},
+ {"60",TSTD_60},
+ {"8VSB",V4L2_STD_ATSC_8_VSB},
+ {"16VSB",V4L2_STD_ATSC_16_VSB},
+};
+
+
+// Search an array of std_name structures and return a pointer to the
+// element with the matching name.
+static const struct std_name *find_std_name(const struct std_name *arrPtr,
+ unsigned int arrSize,
+ const char *bufPtr,
+ unsigned int bufSize)
+{
+ unsigned int idx;
+ const struct std_name *p;
+ for (idx = 0; idx < arrSize; idx++) {
+ p = arrPtr + idx;
+ if (strlen(p->name) != bufSize) continue;
+ if (!memcmp(bufPtr,p->name,bufSize)) return p;
+ }
+ return NULL;
+}
+
+
+int pvr2_std_str_to_id(v4l2_std_id *idPtr,const char *bufPtr,
+ unsigned int bufSize)
+{
+ v4l2_std_id id = 0;
+ v4l2_std_id cmsk = 0;
+ v4l2_std_id t;
+ int mMode = 0;
+ unsigned int cnt;
+ char ch;
+ const struct std_name *sp;
+
+ while (bufSize) {
+ if (!mMode) {
+ cnt = 0;
+ while ((cnt < bufSize) && (bufPtr[cnt] != '-')) cnt++;
+ if (cnt >= bufSize) return 0; // No more characters
+ sp = find_std_name(std_groups, ARRAY_SIZE(std_groups),
+ bufPtr,cnt);
+ if (!sp) return 0; // Illegal color system name
+ cnt++;
+ bufPtr += cnt;
+ bufSize -= cnt;
+ mMode = !0;
+ cmsk = sp->id;
+ continue;
+ }
+ cnt = 0;
+ while (cnt < bufSize) {
+ ch = bufPtr[cnt];
+ if (ch == ';') {
+ mMode = 0;
+ break;
+ }
+ if (ch == '/') break;
+ cnt++;
+ }
+ sp = find_std_name(std_items, ARRAY_SIZE(std_items),
+ bufPtr,cnt);
+ if (!sp) return 0; // Illegal modulation system ID
+ t = sp->id & cmsk;
+ if (!t) return 0; // Specific color + modulation system illegal
+ id |= t;
+ if (cnt < bufSize) cnt++;
+ bufPtr += cnt;
+ bufSize -= cnt;
+ }
+
+ if (idPtr) *idPtr = id;
+ return !0;
+}
+
+
+unsigned int pvr2_std_id_to_str(char *bufPtr, unsigned int bufSize,
+ v4l2_std_id id)
+{
+ unsigned int idx1,idx2;
+ const struct std_name *ip,*gp;
+ int gfl,cfl;
+ unsigned int c1,c2;
+ cfl = 0;
+ c1 = 0;
+ for (idx1 = 0; idx1 < ARRAY_SIZE(std_groups); idx1++) {
+ gp = std_groups + idx1;
+ gfl = 0;
+ for (idx2 = 0; idx2 < ARRAY_SIZE(std_items); idx2++) {
+ ip = std_items + idx2;
+ if (!(gp->id & ip->id & id)) continue;
+ if (!gfl) {
+ if (cfl) {
+ c2 = scnprintf(bufPtr,bufSize,";");
+ c1 += c2;
+ bufSize -= c2;
+ bufPtr += c2;
+ }
+ cfl = !0;
+ c2 = scnprintf(bufPtr,bufSize,
+ "%s-",gp->name);
+ gfl = !0;
+ } else {
+ c2 = scnprintf(bufPtr,bufSize,"/");
+ }
+ c1 += c2;
+ bufSize -= c2;
+ bufPtr += c2;
+ c2 = scnprintf(bufPtr,bufSize,
+ ip->name);
+ c1 += c2;
+ bufSize -= c2;
+ bufPtr += c2;
+ }
+ }
+ return c1;
+}
+
+
+// Template data for possible enumerated video standards. Here we group
+// standards which share common frame rates and resolution.
+static struct v4l2_standard generic_standards[] = {
+ {
+ .id = (TSTD_B|TSTD_B1|
+ TSTD_D|TSTD_D1|
+ TSTD_G|
+ TSTD_H|
+ TSTD_I|
+ TSTD_K|TSTD_K1|
+ TSTD_L|
+ V4L2_STD_SECAM_LC |
+ TSTD_N|TSTD_Nc),
+ .frameperiod =
+ {
+ .numerator = 1,
+ .denominator= 25
+ },
+ .framelines = 625,
+ .reserved = {0,0,0,0}
+ }, {
+ .id = (TSTD_M|
+ V4L2_STD_NTSC_M_JP|
+ V4L2_STD_NTSC_M_KR),
+ .frameperiod =
+ {
+ .numerator = 1001,
+ .denominator= 30000
+ },
+ .framelines = 525,
+ .reserved = {0,0,0,0}
+ }, { // This is a total wild guess
+ .id = (TSTD_60),
+ .frameperiod =
+ {
+ .numerator = 1001,
+ .denominator= 30000
+ },
+ .framelines = 525,
+ .reserved = {0,0,0,0}
+ }, { // This is total wild guess
+ .id = V4L2_STD_NTSC_443,
+ .frameperiod =
+ {
+ .numerator = 1001,
+ .denominator= 30000
+ },
+ .framelines = 525,
+ .reserved = {0,0,0,0}
+ }
+};
+
+static struct v4l2_standard *match_std(v4l2_std_id id)
+{
+ unsigned int idx;
+ for (idx = 0; idx < ARRAY_SIZE(generic_standards); idx++) {
+ if (generic_standards[idx].id & id) {
+ return generic_standards + idx;
+ }
+ }
+ return NULL;
+}
+
+static int pvr2_std_fill(struct v4l2_standard *std,v4l2_std_id id)
+{
+ struct v4l2_standard *template;
+ int idx;
+ unsigned int bcnt;
+ template = match_std(id);
+ if (!template) return 0;
+ idx = std->index;
+ memcpy(std,template,sizeof(*template));
+ std->index = idx;
+ std->id = id;
+ bcnt = pvr2_std_id_to_str(std->name,sizeof(std->name)-1,id);
+ std->name[bcnt] = 0;
+ pvr2_trace(PVR2_TRACE_STD,"Set up standard idx=%u name=%s",
+ std->index,std->name);
+ return !0;
+}
+
+/* These are special cases of combined standards that we should enumerate
+ separately if the component pieces are present. */
+static v4l2_std_id std_mixes[] = {
+ V4L2_STD_PAL_B | V4L2_STD_PAL_G,
+ V4L2_STD_PAL_D | V4L2_STD_PAL_K,
+ V4L2_STD_SECAM_B | V4L2_STD_SECAM_G,
+ V4L2_STD_SECAM_D | V4L2_STD_SECAM_K,
+};
+
+struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr,
+ v4l2_std_id id)
+{
+ unsigned int std_cnt = 0;
+ unsigned int idx,bcnt,idx2;
+ v4l2_std_id idmsk,cmsk,fmsk;
+ struct v4l2_standard *stddefs;
+
+ if (pvrusb2_debug & PVR2_TRACE_STD) {
+ char buf[100];
+ bcnt = pvr2_std_id_to_str(buf,sizeof(buf),id);
+ pvr2_trace(
+ PVR2_TRACE_STD,"Mapping standards mask=0x%x (%.*s)",
+ (int)id,bcnt,buf);
+ }
+
+ *countptr = 0;
+ std_cnt = 0;
+ fmsk = 0;
+ for (idmsk = 1, cmsk = id; cmsk; idmsk <<= 1) {
+ if (!(idmsk & cmsk)) continue;
+ cmsk &= ~idmsk;
+ if (match_std(idmsk)) {
+ std_cnt++;
+ continue;
+ }
+ fmsk |= idmsk;
+ }
+
+ for (idx2 = 0; idx2 < ARRAY_SIZE(std_mixes); idx2++) {
+ if ((id & std_mixes[idx2]) == std_mixes[idx2]) std_cnt++;
+ }
+
+ /* Don't complain about ATSC standard values */
+ fmsk &= ~CSTD_ATSC;
+
+ if (fmsk) {
+ char buf[100];
+ bcnt = pvr2_std_id_to_str(buf,sizeof(buf),fmsk);
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "WARNING:"
+ " Failed to classify the following standard(s): %.*s",
+ bcnt,buf);
+ }
+
+ pvr2_trace(PVR2_TRACE_STD,"Setting up %u unique standard(s)",
+ std_cnt);
+ if (!std_cnt) return NULL; // paranoia
+
+ stddefs = kzalloc(sizeof(struct v4l2_standard) * std_cnt,
+ GFP_KERNEL);
+ if (!stddefs)
+ return NULL;
+
+ for (idx = 0; idx < std_cnt; idx++)
+ stddefs[idx].index = idx;
+
+ idx = 0;
+
+ /* Enumerate potential special cases */
+ for (idx2 = 0; (idx2 < ARRAY_SIZE(std_mixes)) && (idx < std_cnt);
+ idx2++) {
+ if (!(id & std_mixes[idx2])) continue;
+ if (pvr2_std_fill(stddefs+idx,std_mixes[idx2])) idx++;
+ }
+ /* Now enumerate individual pieces */
+ for (idmsk = 1, cmsk = id; cmsk && (idx < std_cnt); idmsk <<= 1) {
+ if (!(idmsk & cmsk)) continue;
+ cmsk &= ~idmsk;
+ if (!pvr2_std_fill(stddefs+idx,idmsk)) continue;
+ idx++;
+ }
+
+ *countptr = std_cnt;
+ return stddefs;
+}
+
+v4l2_std_id pvr2_std_get_usable(void)
+{
+ return CSTD_ALL;
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-std.h b/drivers/media/video/pvrusb2/pvrusb2-std.h
new file mode 100644
index 00000000..a35c53d0
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-std.h
@@ -0,0 +1,59 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef __PVRUSB2_STD_H
+#define __PVRUSB2_STD_H
+
+#include <linux/videodev2.h>
+
+// Convert string describing one or more video standards into a mask of V4L
+// standard bits. Return true if conversion succeeds otherwise return
+// false. String is expected to be of the form: C1-x/y;C2-a/b where C1 and
+// C2 are color system names (e.g. "PAL", "NTSC") and x, y, a, and b are
+// modulation schemes (e.g. "M", "B", "G", etc).
+int pvr2_std_str_to_id(v4l2_std_id *idPtr,const char *bufPtr,
+ unsigned int bufSize);
+
+// Convert any arbitrary set of video standard bits into an unambiguous
+// readable string. Return value is the number of bytes consumed in the
+// buffer. The formatted string is of a form that can be parsed by our
+// sibling std_std_to_id() function.
+unsigned int pvr2_std_id_to_str(char *bufPtr, unsigned int bufSize,
+ v4l2_std_id id);
+
+// Create an array of suitable v4l2_standard structures given a bit mask of
+// video standards to support. The array is allocated from the heap, and
+// the number of elements is returned in the first argument.
+struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr,
+ v4l2_std_id id);
+
+// Return mask of which video standard bits are valid
+v4l2_std_id pvr2_std_get_usable(void);
+
+#endif /* __PVRUSB2_STD_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
new file mode 100644
index 00000000..6ef1335b
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
@@ -0,0 +1,861 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "pvrusb2-sysfs.h"
+#include "pvrusb2-hdw.h"
+#include "pvrusb2-debug.h"
+#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
+#include "pvrusb2-debugifc.h"
+#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */
+
+#define pvr2_sysfs_trace(...) pvr2_trace(PVR2_TRACE_SYSFS,__VA_ARGS__)
+
+struct pvr2_sysfs {
+ struct pvr2_channel channel;
+ struct device *class_dev;
+#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
+ struct pvr2_sysfs_debugifc *debugifc;
+#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */
+ struct pvr2_sysfs_ctl_item *item_first;
+ struct pvr2_sysfs_ctl_item *item_last;
+ struct device_attribute attr_v4l_minor_number;
+ struct device_attribute attr_v4l_radio_minor_number;
+ struct device_attribute attr_unit_number;
+ struct device_attribute attr_bus_info;
+ struct device_attribute attr_hdw_name;
+ struct device_attribute attr_hdw_desc;
+ int v4l_minor_number_created_ok;
+ int v4l_radio_minor_number_created_ok;
+ int unit_number_created_ok;
+ int bus_info_created_ok;
+ int hdw_name_created_ok;
+ int hdw_desc_created_ok;
+};
+
+#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
+struct pvr2_sysfs_debugifc {
+ struct device_attribute attr_debugcmd;
+ struct device_attribute attr_debuginfo;
+ int debugcmd_created_ok;
+ int debuginfo_created_ok;
+};
+#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */
+
+struct pvr2_sysfs_ctl_item {
+ struct device_attribute attr_name;
+ struct device_attribute attr_type;
+ struct device_attribute attr_min;
+ struct device_attribute attr_max;
+ struct device_attribute attr_def;
+ struct device_attribute attr_enum;
+ struct device_attribute attr_bits;
+ struct device_attribute attr_val;
+ struct device_attribute attr_custom;
+ struct pvr2_ctrl *cptr;
+ int ctl_id;
+ struct pvr2_sysfs *chptr;
+ struct pvr2_sysfs_ctl_item *item_next;
+ struct attribute *attr_gen[8];
+ struct attribute_group grp;
+ int created_ok;
+ char name[80];
+};
+
+struct pvr2_sysfs_class {
+ struct class class;
+};
+
+static ssize_t show_name(struct device *class_dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pvr2_sysfs_ctl_item *cip;
+ const char *name;
+ cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_name);
+ name = pvr2_ctrl_get_desc(cip->cptr);
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_name(cid=%d) is %s",
+ cip->chptr, cip->ctl_id, name);
+ if (!name) return -EINVAL;
+ return scnprintf(buf, PAGE_SIZE, "%s\n", name);
+}
+
+static ssize_t show_type(struct device *class_dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pvr2_sysfs_ctl_item *cip;
+ const char *name;
+ enum pvr2_ctl_type tp;
+ cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_type);
+ tp = pvr2_ctrl_get_type(cip->cptr);
+ switch (tp) {
+ case pvr2_ctl_int: name = "integer"; break;
+ case pvr2_ctl_enum: name = "enum"; break;
+ case pvr2_ctl_bitmask: name = "bitmask"; break;
+ case pvr2_ctl_bool: name = "boolean"; break;
+ default: name = "?"; break;
+ }
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_type(cid=%d) is %s",
+ cip->chptr, cip->ctl_id, name);
+ if (!name) return -EINVAL;
+ return scnprintf(buf, PAGE_SIZE, "%s\n", name);
+}
+
+static ssize_t show_min(struct device *class_dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pvr2_sysfs_ctl_item *cip;
+ long val;
+ cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_min);
+ val = pvr2_ctrl_get_min(cip->cptr);
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_min(cid=%d) is %ld",
+ cip->chptr, cip->ctl_id, val);
+ return scnprintf(buf, PAGE_SIZE, "%ld\n", val);
+}
+
+static ssize_t show_max(struct device *class_dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pvr2_sysfs_ctl_item *cip;
+ long val;
+ cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_max);
+ val = pvr2_ctrl_get_max(cip->cptr);
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_max(cid=%d) is %ld",
+ cip->chptr, cip->ctl_id, val);
+ return scnprintf(buf, PAGE_SIZE, "%ld\n", val);
+}
+
+static ssize_t show_def(struct device *class_dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pvr2_sysfs_ctl_item *cip;
+ int val;
+ int ret;
+ unsigned int cnt = 0;
+ cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_def);
+ ret = pvr2_ctrl_get_def(cip->cptr, &val);
+ if (ret < 0) return ret;
+ ret = pvr2_ctrl_value_to_sym(cip->cptr, ~0, val,
+ buf, PAGE_SIZE - 1, &cnt);
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_def(cid=%d) is %.*s (%d)",
+ cip->chptr, cip->ctl_id, cnt, buf, val);
+ buf[cnt] = '\n';
+ return cnt + 1;
+}
+
+static ssize_t show_val_norm(struct device *class_dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pvr2_sysfs_ctl_item *cip;
+ int val;
+ int ret;
+ unsigned int cnt = 0;
+ cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_val);
+ ret = pvr2_ctrl_get_value(cip->cptr, &val);
+ if (ret < 0) return ret;
+ ret = pvr2_ctrl_value_to_sym(cip->cptr, ~0, val,
+ buf, PAGE_SIZE - 1, &cnt);
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_norm(cid=%d) is %.*s (%d)",
+ cip->chptr, cip->ctl_id, cnt, buf, val);
+ buf[cnt] = '\n';
+ return cnt+1;
+}
+
+static ssize_t show_val_custom(struct device *class_dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pvr2_sysfs_ctl_item *cip;
+ int val;
+ int ret;
+ unsigned int cnt = 0;
+ cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_custom);
+ ret = pvr2_ctrl_get_value(cip->cptr, &val);
+ if (ret < 0) return ret;
+ ret = pvr2_ctrl_custom_value_to_sym(cip->cptr, ~0, val,
+ buf, PAGE_SIZE - 1, &cnt);
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_custom(cid=%d) is %.*s (%d)",
+ cip->chptr, cip->ctl_id, cnt, buf, val);
+ buf[cnt] = '\n';
+ return cnt+1;
+}
+
+static ssize_t show_enum(struct device *class_dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pvr2_sysfs_ctl_item *cip;
+ long val;
+ unsigned int bcnt, ccnt, ecnt;
+ cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_enum);
+ ecnt = pvr2_ctrl_get_cnt(cip->cptr);
+ bcnt = 0;
+ for (val = 0; val < ecnt; val++) {
+ pvr2_ctrl_get_valname(cip->cptr, val, buf + bcnt,
+ PAGE_SIZE - bcnt, &ccnt);
+ if (!ccnt) continue;
+ bcnt += ccnt;
+ if (bcnt >= PAGE_SIZE) break;
+ buf[bcnt] = '\n';
+ bcnt++;
+ }
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_enum(cid=%d)",
+ cip->chptr, cip->ctl_id);
+ return bcnt;
+}
+
+static ssize_t show_bits(struct device *class_dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pvr2_sysfs_ctl_item *cip;
+ int valid_bits, msk;
+ unsigned int bcnt, ccnt;
+ cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_bits);
+ valid_bits = pvr2_ctrl_get_mask(cip->cptr);
+ bcnt = 0;
+ for (msk = 1; valid_bits; msk <<= 1) {
+ if (!(msk & valid_bits)) continue;
+ valid_bits &= ~msk;
+ pvr2_ctrl_get_valname(cip->cptr, msk, buf + bcnt,
+ PAGE_SIZE - bcnt, &ccnt);
+ bcnt += ccnt;
+ if (bcnt >= PAGE_SIZE) break;
+ buf[bcnt] = '\n';
+ bcnt++;
+ }
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_bits(cid=%d)",
+ cip->chptr, cip->ctl_id);
+ return bcnt;
+}
+
+static int store_val_any(struct pvr2_sysfs_ctl_item *cip, int customfl,
+ const char *buf,unsigned int count)
+{
+ int ret;
+ int mask,val;
+ if (customfl) {
+ ret = pvr2_ctrl_custom_sym_to_value(cip->cptr, buf, count,
+ &mask, &val);
+ } else {
+ ret = pvr2_ctrl_sym_to_value(cip->cptr, buf, count,
+ &mask, &val);
+ }
+ if (ret < 0) return ret;
+ ret = pvr2_ctrl_set_mask_value(cip->cptr, mask, val);
+ pvr2_hdw_commit_ctl(cip->chptr->channel.hdw);
+ return ret;
+}
+
+static ssize_t store_val_norm(struct device *class_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pvr2_sysfs_ctl_item *cip;
+ int ret;
+ cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_val);
+ pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_norm(cid=%d) \"%.*s\"",
+ cip->chptr, cip->ctl_id, (int)count, buf);
+ ret = store_val_any(cip, 0, buf, count);
+ if (!ret) ret = count;
+ return ret;
+}
+
+static ssize_t store_val_custom(struct device *class_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pvr2_sysfs_ctl_item *cip;
+ int ret;
+ cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_custom);
+ pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_custom(cid=%d) \"%.*s\"",
+ cip->chptr, cip->ctl_id, (int)count, buf);
+ ret = store_val_any(cip, 1, buf, count);
+ if (!ret) ret = count;
+ return ret;
+}
+
+static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id)
+{
+ struct pvr2_sysfs_ctl_item *cip;
+ struct pvr2_ctrl *cptr;
+ unsigned int cnt,acnt;
+ int ret;
+
+ cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,ctl_id);
+ if (!cptr) return;
+
+ cip = kzalloc(sizeof(*cip),GFP_KERNEL);
+ if (!cip) return;
+ pvr2_sysfs_trace("Creating pvr2_sysfs_ctl_item id=%p",cip);
+
+ cip->cptr = cptr;
+ cip->ctl_id = ctl_id;
+
+ cip->chptr = sfp;
+ cip->item_next = NULL;
+ if (sfp->item_last) {
+ sfp->item_last->item_next = cip;
+ } else {
+ sfp->item_first = cip;
+ }
+ sfp->item_last = cip;
+
+ sysfs_attr_init(&cip->attr_name.attr);
+ cip->attr_name.attr.name = "name";
+ cip->attr_name.attr.mode = S_IRUGO;
+ cip->attr_name.show = show_name;
+
+ sysfs_attr_init(&cip->attr_type.attr);
+ cip->attr_type.attr.name = "type";
+ cip->attr_type.attr.mode = S_IRUGO;
+ cip->attr_type.show = show_type;
+
+ sysfs_attr_init(&cip->attr_min.attr);
+ cip->attr_min.attr.name = "min_val";
+ cip->attr_min.attr.mode = S_IRUGO;
+ cip->attr_min.show = show_min;
+
+ sysfs_attr_init(&cip->attr_max.attr);
+ cip->attr_max.attr.name = "max_val";
+ cip->attr_max.attr.mode = S_IRUGO;
+ cip->attr_max.show = show_max;
+
+ sysfs_attr_init(&cip->attr_def.attr);
+ cip->attr_def.attr.name = "def_val";
+ cip->attr_def.attr.mode = S_IRUGO;
+ cip->attr_def.show = show_def;
+
+ sysfs_attr_init(&cip->attr_val.attr);
+ cip->attr_val.attr.name = "cur_val";
+ cip->attr_val.attr.mode = S_IRUGO;
+
+ sysfs_attr_init(&cip->attr_custom.attr);
+ cip->attr_custom.attr.name = "custom_val";
+ cip->attr_custom.attr.mode = S_IRUGO;
+
+ sysfs_attr_init(&cip->attr_enum.attr);
+ cip->attr_enum.attr.name = "enum_val";
+ cip->attr_enum.attr.mode = S_IRUGO;
+ cip->attr_enum.show = show_enum;
+
+ sysfs_attr_init(&cip->attr_bits.attr);
+ cip->attr_bits.attr.name = "bit_val";
+ cip->attr_bits.attr.mode = S_IRUGO;
+ cip->attr_bits.show = show_bits;
+
+ if (pvr2_ctrl_is_writable(cptr)) {
+ cip->attr_val.attr.mode |= S_IWUSR|S_IWGRP;
+ cip->attr_custom.attr.mode |= S_IWUSR|S_IWGRP;
+ }
+
+ acnt = 0;
+ cip->attr_gen[acnt++] = &cip->attr_name.attr;
+ cip->attr_gen[acnt++] = &cip->attr_type.attr;
+ cip->attr_gen[acnt++] = &cip->attr_val.attr;
+ cip->attr_gen[acnt++] = &cip->attr_def.attr;
+ cip->attr_val.show = show_val_norm;
+ cip->attr_val.store = store_val_norm;
+ if (pvr2_ctrl_has_custom_symbols(cptr)) {
+ cip->attr_gen[acnt++] = &cip->attr_custom.attr;
+ cip->attr_custom.show = show_val_custom;
+ cip->attr_custom.store = store_val_custom;
+ }
+ switch (pvr2_ctrl_get_type(cptr)) {
+ case pvr2_ctl_enum:
+ // Control is an enumeration
+ cip->attr_gen[acnt++] = &cip->attr_enum.attr;
+ break;
+ case pvr2_ctl_int:
+ // Control is an integer
+ cip->attr_gen[acnt++] = &cip->attr_min.attr;
+ cip->attr_gen[acnt++] = &cip->attr_max.attr;
+ break;
+ case pvr2_ctl_bitmask:
+ // Control is an bitmask
+ cip->attr_gen[acnt++] = &cip->attr_bits.attr;
+ break;
+ default: break;
+ }
+
+ cnt = scnprintf(cip->name,sizeof(cip->name)-1,"ctl_%s",
+ pvr2_ctrl_get_name(cptr));
+ cip->name[cnt] = 0;
+ cip->grp.name = cip->name;
+ cip->grp.attrs = cip->attr_gen;
+
+ ret = sysfs_create_group(&sfp->class_dev->kobj,&cip->grp);
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "sysfs_create_group error: %d",
+ ret);
+ return;
+ }
+ cip->created_ok = !0;
+}
+
+#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
+static ssize_t debuginfo_show(struct device *, struct device_attribute *,
+ char *);
+static ssize_t debugcmd_show(struct device *, struct device_attribute *,
+ char *);
+static ssize_t debugcmd_store(struct device *, struct device_attribute *,
+ const char *, size_t count);
+
+static void pvr2_sysfs_add_debugifc(struct pvr2_sysfs *sfp)
+{
+ struct pvr2_sysfs_debugifc *dip;
+ int ret;
+
+ dip = kzalloc(sizeof(*dip),GFP_KERNEL);
+ if (!dip) return;
+ sysfs_attr_init(&dip->attr_debugcmd.attr);
+ dip->attr_debugcmd.attr.name = "debugcmd";
+ dip->attr_debugcmd.attr.mode = S_IRUGO|S_IWUSR|S_IWGRP;
+ dip->attr_debugcmd.show = debugcmd_show;
+ dip->attr_debugcmd.store = debugcmd_store;
+ sysfs_attr_init(&dip->attr_debuginfo.attr);
+ dip->attr_debuginfo.attr.name = "debuginfo";
+ dip->attr_debuginfo.attr.mode = S_IRUGO;
+ dip->attr_debuginfo.show = debuginfo_show;
+ sfp->debugifc = dip;
+ ret = device_create_file(sfp->class_dev,&dip->attr_debugcmd);
+ if (ret < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "device_create_file error: %d",
+ ret);
+ } else {
+ dip->debugcmd_created_ok = !0;
+ }
+ ret = device_create_file(sfp->class_dev,&dip->attr_debuginfo);
+ if (ret < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "device_create_file error: %d",
+ ret);
+ } else {
+ dip->debuginfo_created_ok = !0;
+ }
+}
+
+
+static void pvr2_sysfs_tear_down_debugifc(struct pvr2_sysfs *sfp)
+{
+ if (!sfp->debugifc) return;
+ if (sfp->debugifc->debuginfo_created_ok) {
+ device_remove_file(sfp->class_dev,
+ &sfp->debugifc->attr_debuginfo);
+ }
+ if (sfp->debugifc->debugcmd_created_ok) {
+ device_remove_file(sfp->class_dev,
+ &sfp->debugifc->attr_debugcmd);
+ }
+ kfree(sfp->debugifc);
+ sfp->debugifc = NULL;
+}
+#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */
+
+
+static void pvr2_sysfs_add_controls(struct pvr2_sysfs *sfp)
+{
+ unsigned int idx,cnt;
+ cnt = pvr2_hdw_get_ctrl_count(sfp->channel.hdw);
+ for (idx = 0; idx < cnt; idx++) {
+ pvr2_sysfs_add_control(sfp,idx);
+ }
+}
+
+
+static void pvr2_sysfs_tear_down_controls(struct pvr2_sysfs *sfp)
+{
+ struct pvr2_sysfs_ctl_item *cip1,*cip2;
+ for (cip1 = sfp->item_first; cip1; cip1 = cip2) {
+ cip2 = cip1->item_next;
+ if (cip1->created_ok) {
+ sysfs_remove_group(&sfp->class_dev->kobj,&cip1->grp);
+ }
+ pvr2_sysfs_trace("Destroying pvr2_sysfs_ctl_item id=%p",cip1);
+ kfree(cip1);
+ }
+}
+
+
+static void pvr2_sysfs_class_release(struct class *class)
+{
+ struct pvr2_sysfs_class *clp;
+ clp = container_of(class,struct pvr2_sysfs_class,class);
+ pvr2_sysfs_trace("Destroying pvr2_sysfs_class id=%p",clp);
+ kfree(clp);
+}
+
+
+static void pvr2_sysfs_release(struct device *class_dev)
+{
+ pvr2_sysfs_trace("Releasing class_dev id=%p",class_dev);
+ kfree(class_dev);
+}
+
+
+static void class_dev_destroy(struct pvr2_sysfs *sfp)
+{
+ struct device *dev;
+ if (!sfp->class_dev) return;
+#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
+ pvr2_sysfs_tear_down_debugifc(sfp);
+#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */
+ pvr2_sysfs_tear_down_controls(sfp);
+ if (sfp->hdw_desc_created_ok) {
+ device_remove_file(sfp->class_dev,
+ &sfp->attr_hdw_desc);
+ }
+ if (sfp->hdw_name_created_ok) {
+ device_remove_file(sfp->class_dev,
+ &sfp->attr_hdw_name);
+ }
+ if (sfp->bus_info_created_ok) {
+ device_remove_file(sfp->class_dev,
+ &sfp->attr_bus_info);
+ }
+ if (sfp->v4l_minor_number_created_ok) {
+ device_remove_file(sfp->class_dev,
+ &sfp->attr_v4l_minor_number);
+ }
+ if (sfp->v4l_radio_minor_number_created_ok) {
+ device_remove_file(sfp->class_dev,
+ &sfp->attr_v4l_radio_minor_number);
+ }
+ if (sfp->unit_number_created_ok) {
+ device_remove_file(sfp->class_dev,
+ &sfp->attr_unit_number);
+ }
+ pvr2_sysfs_trace("Destroying class_dev id=%p",sfp->class_dev);
+ dev_set_drvdata(sfp->class_dev, NULL);
+ dev = sfp->class_dev->parent;
+ sfp->class_dev->parent = NULL;
+ put_device(dev);
+ device_unregister(sfp->class_dev);
+ sfp->class_dev = NULL;
+}
+
+
+static ssize_t v4l_minor_number_show(struct device *class_dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pvr2_sysfs *sfp;
+ sfp = dev_get_drvdata(class_dev);
+ if (!sfp) return -EINVAL;
+ return scnprintf(buf,PAGE_SIZE,"%d\n",
+ pvr2_hdw_v4l_get_minor_number(sfp->channel.hdw,
+ pvr2_v4l_type_video));
+}
+
+
+static ssize_t bus_info_show(struct device *class_dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pvr2_sysfs *sfp;
+ sfp = dev_get_drvdata(class_dev);
+ if (!sfp) return -EINVAL;
+ return scnprintf(buf,PAGE_SIZE,"%s\n",
+ pvr2_hdw_get_bus_info(sfp->channel.hdw));
+}
+
+
+static ssize_t hdw_name_show(struct device *class_dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pvr2_sysfs *sfp;
+ sfp = dev_get_drvdata(class_dev);
+ if (!sfp) return -EINVAL;
+ return scnprintf(buf,PAGE_SIZE,"%s\n",
+ pvr2_hdw_get_type(sfp->channel.hdw));
+}
+
+
+static ssize_t hdw_desc_show(struct device *class_dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pvr2_sysfs *sfp;
+ sfp = dev_get_drvdata(class_dev);
+ if (!sfp) return -EINVAL;
+ return scnprintf(buf,PAGE_SIZE,"%s\n",
+ pvr2_hdw_get_desc(sfp->channel.hdw));
+}
+
+
+static ssize_t v4l_radio_minor_number_show(struct device *class_dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pvr2_sysfs *sfp;
+ sfp = dev_get_drvdata(class_dev);
+ if (!sfp) return -EINVAL;
+ return scnprintf(buf,PAGE_SIZE,"%d\n",
+ pvr2_hdw_v4l_get_minor_number(sfp->channel.hdw,
+ pvr2_v4l_type_radio));
+}
+
+
+static ssize_t unit_number_show(struct device *class_dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pvr2_sysfs *sfp;
+ sfp = dev_get_drvdata(class_dev);
+ if (!sfp) return -EINVAL;
+ return scnprintf(buf,PAGE_SIZE,"%d\n",
+ pvr2_hdw_get_unit_number(sfp->channel.hdw));
+}
+
+
+static void class_dev_create(struct pvr2_sysfs *sfp,
+ struct pvr2_sysfs_class *class_ptr)
+{
+ struct usb_device *usb_dev;
+ struct device *class_dev;
+ int ret;
+
+ usb_dev = pvr2_hdw_get_dev(sfp->channel.hdw);
+ if (!usb_dev) return;
+ class_dev = kzalloc(sizeof(*class_dev),GFP_KERNEL);
+ if (!class_dev) return;
+
+ pvr2_sysfs_trace("Creating class_dev id=%p",class_dev);
+
+ class_dev->class = &class_ptr->class;
+
+ dev_set_name(class_dev, "%s",
+ pvr2_hdw_get_device_identifier(sfp->channel.hdw));
+
+ class_dev->parent = get_device(&usb_dev->dev);
+
+ sfp->class_dev = class_dev;
+ dev_set_drvdata(class_dev, sfp);
+ ret = device_register(class_dev);
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "device_register failed");
+ put_device(class_dev);
+ return;
+ }
+
+ sysfs_attr_init(&sfp->attr_v4l_minor_number.attr);
+ sfp->attr_v4l_minor_number.attr.name = "v4l_minor_number";
+ sfp->attr_v4l_minor_number.attr.mode = S_IRUGO;
+ sfp->attr_v4l_minor_number.show = v4l_minor_number_show;
+ sfp->attr_v4l_minor_number.store = NULL;
+ ret = device_create_file(sfp->class_dev,
+ &sfp->attr_v4l_minor_number);
+ if (ret < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "device_create_file error: %d",
+ ret);
+ } else {
+ sfp->v4l_minor_number_created_ok = !0;
+ }
+
+ sysfs_attr_init(&sfp->attr_v4l_radio_minor_number.attr);
+ sfp->attr_v4l_radio_minor_number.attr.name = "v4l_radio_minor_number";
+ sfp->attr_v4l_radio_minor_number.attr.mode = S_IRUGO;
+ sfp->attr_v4l_radio_minor_number.show = v4l_radio_minor_number_show;
+ sfp->attr_v4l_radio_minor_number.store = NULL;
+ ret = device_create_file(sfp->class_dev,
+ &sfp->attr_v4l_radio_minor_number);
+ if (ret < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "device_create_file error: %d",
+ ret);
+ } else {
+ sfp->v4l_radio_minor_number_created_ok = !0;
+ }
+
+ sysfs_attr_init(&sfp->attr_unit_number.attr);
+ sfp->attr_unit_number.attr.name = "unit_number";
+ sfp->attr_unit_number.attr.mode = S_IRUGO;
+ sfp->attr_unit_number.show = unit_number_show;
+ sfp->attr_unit_number.store = NULL;
+ ret = device_create_file(sfp->class_dev,&sfp->attr_unit_number);
+ if (ret < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "device_create_file error: %d",
+ ret);
+ } else {
+ sfp->unit_number_created_ok = !0;
+ }
+
+ sysfs_attr_init(&sfp->attr_bus_info.attr);
+ sfp->attr_bus_info.attr.name = "bus_info_str";
+ sfp->attr_bus_info.attr.mode = S_IRUGO;
+ sfp->attr_bus_info.show = bus_info_show;
+ sfp->attr_bus_info.store = NULL;
+ ret = device_create_file(sfp->class_dev,
+ &sfp->attr_bus_info);
+ if (ret < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "device_create_file error: %d",
+ ret);
+ } else {
+ sfp->bus_info_created_ok = !0;
+ }
+
+ sysfs_attr_init(&sfp->attr_hdw_name.attr);
+ sfp->attr_hdw_name.attr.name = "device_hardware_type";
+ sfp->attr_hdw_name.attr.mode = S_IRUGO;
+ sfp->attr_hdw_name.show = hdw_name_show;
+ sfp->attr_hdw_name.store = NULL;
+ ret = device_create_file(sfp->class_dev,
+ &sfp->attr_hdw_name);
+ if (ret < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "device_create_file error: %d",
+ ret);
+ } else {
+ sfp->hdw_name_created_ok = !0;
+ }
+
+ sysfs_attr_init(&sfp->attr_hdw_desc.attr);
+ sfp->attr_hdw_desc.attr.name = "device_hardware_description";
+ sfp->attr_hdw_desc.attr.mode = S_IRUGO;
+ sfp->attr_hdw_desc.show = hdw_desc_show;
+ sfp->attr_hdw_desc.store = NULL;
+ ret = device_create_file(sfp->class_dev,
+ &sfp->attr_hdw_desc);
+ if (ret < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "device_create_file error: %d",
+ ret);
+ } else {
+ sfp->hdw_desc_created_ok = !0;
+ }
+
+ pvr2_sysfs_add_controls(sfp);
+#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
+ pvr2_sysfs_add_debugifc(sfp);
+#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */
+}
+
+
+static void pvr2_sysfs_internal_check(struct pvr2_channel *chp)
+{
+ struct pvr2_sysfs *sfp;
+ sfp = container_of(chp,struct pvr2_sysfs,channel);
+ if (!sfp->channel.mc_head->disconnect_flag) return;
+ pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_sysfs id=%p",sfp);
+ class_dev_destroy(sfp);
+ pvr2_channel_done(&sfp->channel);
+ kfree(sfp);
+}
+
+
+struct pvr2_sysfs *pvr2_sysfs_create(struct pvr2_context *mp,
+ struct pvr2_sysfs_class *class_ptr)
+{
+ struct pvr2_sysfs *sfp;
+ sfp = kzalloc(sizeof(*sfp),GFP_KERNEL);
+ if (!sfp) return sfp;
+ pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_sysfs id=%p",sfp);
+ pvr2_channel_init(&sfp->channel,mp);
+ sfp->channel.check_func = pvr2_sysfs_internal_check;
+
+ class_dev_create(sfp,class_ptr);
+ return sfp;
+}
+
+
+
+struct pvr2_sysfs_class *pvr2_sysfs_class_create(void)
+{
+ struct pvr2_sysfs_class *clp;
+ clp = kzalloc(sizeof(*clp),GFP_KERNEL);
+ if (!clp) return clp;
+ pvr2_sysfs_trace("Creating and registering pvr2_sysfs_class id=%p",
+ clp);
+ clp->class.name = "pvrusb2";
+ clp->class.class_release = pvr2_sysfs_class_release;
+ clp->class.dev_release = pvr2_sysfs_release;
+ if (class_register(&clp->class)) {
+ pvr2_sysfs_trace(
+ "Registration failed for pvr2_sysfs_class id=%p",clp);
+ kfree(clp);
+ clp = NULL;
+ }
+ return clp;
+}
+
+
+void pvr2_sysfs_class_destroy(struct pvr2_sysfs_class *clp)
+{
+ pvr2_sysfs_trace("Unregistering pvr2_sysfs_class id=%p", clp);
+ class_unregister(&clp->class);
+}
+
+
+#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
+static ssize_t debuginfo_show(struct device *class_dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pvr2_sysfs *sfp;
+ sfp = dev_get_drvdata(class_dev);
+ if (!sfp) return -EINVAL;
+ pvr2_hdw_trigger_module_log(sfp->channel.hdw);
+ return pvr2_debugifc_print_info(sfp->channel.hdw,buf,PAGE_SIZE);
+}
+
+
+static ssize_t debugcmd_show(struct device *class_dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pvr2_sysfs *sfp;
+ sfp = dev_get_drvdata(class_dev);
+ if (!sfp) return -EINVAL;
+ return pvr2_debugifc_print_status(sfp->channel.hdw,buf,PAGE_SIZE);
+}
+
+
+static ssize_t debugcmd_store(struct device *class_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pvr2_sysfs *sfp;
+ int ret;
+
+ sfp = dev_get_drvdata(class_dev);
+ if (!sfp) return -EINVAL;
+
+ ret = pvr2_debugifc_docmd(sfp->channel.hdw,buf,count);
+ if (ret < 0) return ret;
+ return count;
+}
+#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.h b/drivers/media/video/pvrusb2/pvrusb2-sysfs.h
new file mode 100644
index 00000000..6d875bfe
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.h
@@ -0,0 +1,46 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef __PVRUSB2_SYSFS_H
+#define __PVRUSB2_SYSFS_H
+
+#include <linux/list.h>
+#include <linux/sysfs.h>
+#include "pvrusb2-context.h"
+
+struct pvr2_sysfs;
+struct pvr2_sysfs_class;
+
+struct pvr2_sysfs_class *pvr2_sysfs_class_create(void);
+void pvr2_sysfs_class_destroy(struct pvr2_sysfs_class *);
+
+struct pvr2_sysfs *pvr2_sysfs_create(struct pvr2_context *,
+ struct pvr2_sysfs_class *);
+
+#endif /* __PVRUSB2_SYSFS_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-util.h b/drivers/media/video/pvrusb2/pvrusb2-util.h
new file mode 100644
index 00000000..92b75544
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-util.h
@@ -0,0 +1,62 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef __PVRUSB2_UTIL_H
+#define __PVRUSB2_UTIL_H
+
+#define PVR2_DECOMPOSE_LE(t,i,d) \
+ do { \
+ (t)[i] = (d) & 0xff;\
+ (t)[i+1] = ((d) >> 8) & 0xff;\
+ (t)[i+2] = ((d) >> 16) & 0xff;\
+ (t)[i+3] = ((d) >> 24) & 0xff;\
+ } while(0)
+
+#define PVR2_DECOMPOSE_BE(t,i,d) \
+ do { \
+ (t)[i+3] = (d) & 0xff;\
+ (t)[i+2] = ((d) >> 8) & 0xff;\
+ (t)[i+1] = ((d) >> 16) & 0xff;\
+ (t)[i] = ((d) >> 24) & 0xff;\
+ } while(0)
+
+#define PVR2_COMPOSE_LE(t,i) \
+ ((((u32)((t)[i+3])) << 24) | \
+ (((u32)((t)[i+2])) << 16) | \
+ (((u32)((t)[i+1])) << 8) | \
+ ((u32)((t)[i])))
+
+#define PVR2_COMPOSE_BE(t,i) \
+ ((((u32)((t)[i])) << 24) | \
+ (((u32)((t)[i+1])) << 16) | \
+ (((u32)((t)[i+2])) << 8) | \
+ ((u32)((t)[i+3])))
+
+
+#endif /* __PVRUSB2_UTIL_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
new file mode 100644
index 00000000..e1111d96
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
@@ -0,0 +1,1364 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include "pvrusb2-context.h"
+#include "pvrusb2-hdw.h"
+#include "pvrusb2.h"
+#include "pvrusb2-debug.h"
+#include "pvrusb2-v4l2.h"
+#include "pvrusb2-ioread.h"
+#include <linux/videodev2.h>
+#include <linux/module.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+
+struct pvr2_v4l2_dev;
+struct pvr2_v4l2_fh;
+struct pvr2_v4l2;
+
+struct pvr2_v4l2_dev {
+ struct video_device devbase; /* MUST be first! */
+ struct pvr2_v4l2 *v4lp;
+ struct pvr2_context_stream *stream;
+ /* Information about this device: */
+ enum pvr2_config config; /* Expected stream format */
+ int v4l_type; /* V4L defined type for this device node */
+ enum pvr2_v4l_type minor_type; /* pvr2-understood minor device type */
+};
+
+struct pvr2_v4l2_fh {
+ struct pvr2_channel channel;
+ struct pvr2_v4l2_dev *pdi;
+ enum v4l2_priority prio;
+ struct pvr2_ioread *rhp;
+ struct file *file;
+ struct pvr2_v4l2 *vhead;
+ struct pvr2_v4l2_fh *vnext;
+ struct pvr2_v4l2_fh *vprev;
+ wait_queue_head_t wait_data;
+ int fw_mode_flag;
+ /* Map contiguous ordinal value to input id */
+ unsigned char *input_map;
+ unsigned int input_cnt;
+};
+
+struct pvr2_v4l2 {
+ struct pvr2_channel channel;
+ struct pvr2_v4l2_fh *vfirst;
+ struct pvr2_v4l2_fh *vlast;
+
+ struct v4l2_prio_state prio;
+
+ /* streams - Note that these must be separately, individually,
+ * allocated pointers. This is because the v4l core is going to
+ * manage their deletion - separately, individually... */
+ struct pvr2_v4l2_dev *dev_video;
+ struct pvr2_v4l2_dev *dev_radio;
+};
+
+static int video_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1};
+module_param_array(video_nr, int, NULL, 0444);
+MODULE_PARM_DESC(video_nr, "Offset for device's video dev minor");
+static int radio_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1};
+module_param_array(radio_nr, int, NULL, 0444);
+MODULE_PARM_DESC(radio_nr, "Offset for device's radio dev minor");
+static int vbi_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1};
+module_param_array(vbi_nr, int, NULL, 0444);
+MODULE_PARM_DESC(vbi_nr, "Offset for device's vbi dev minor");
+
+static struct v4l2_capability pvr_capability ={
+ .driver = "pvrusb2",
+ .card = "Hauppauge WinTV pvr-usb2",
+ .bus_info = "usb",
+ .version = LINUX_VERSION_CODE,
+ .capabilities = (V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO |
+ V4L2_CAP_READWRITE),
+};
+
+static struct v4l2_fmtdesc pvr_fmtdesc [] = {
+ {
+ .index = 0,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = V4L2_FMT_FLAG_COMPRESSED,
+ .description = "MPEG1/2",
+ // This should really be V4L2_PIX_FMT_MPEG, but xawtv
+ // breaks when I do that.
+ .pixelformat = 0, // V4L2_PIX_FMT_MPEG,
+ .reserved = { 0, 0, 0, 0 }
+ }
+};
+
+#define PVR_FORMAT_PIX 0
+#define PVR_FORMAT_VBI 1
+
+static struct v4l2_format pvr_format [] = {
+ [PVR_FORMAT_PIX] = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .fmt = {
+ .pix = {
+ .width = 720,
+ .height = 576,
+ // This should really be V4L2_PIX_FMT_MPEG,
+ // but xawtv breaks when I do that.
+ .pixelformat = 0, // V4L2_PIX_FMT_MPEG,
+ .field = V4L2_FIELD_INTERLACED,
+ .bytesperline = 0, // doesn't make sense
+ // here
+ //FIXME : Don't know what to put here...
+ .sizeimage = (32*1024),
+ .colorspace = 0, // doesn't make sense here
+ .priv = 0
+ }
+ }
+ },
+ [PVR_FORMAT_VBI] = {
+ .type = V4L2_BUF_TYPE_VBI_CAPTURE,
+ .fmt = {
+ .vbi = {
+ .sampling_rate = 27000000,
+ .offset = 248,
+ .samples_per_line = 1443,
+ .sample_format = V4L2_PIX_FMT_GREY,
+ .start = { 0, 0 },
+ .count = { 0, 0 },
+ .flags = 0,
+ .reserved = { 0, 0 }
+ }
+ }
+ }
+};
+
+
+/*
+ * pvr_ioctl()
+ *
+ * This is part of Video 4 Linux API. The procedure handles ioctl() calls.
+ *
+ */
+static long pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_v4l2 *vp = fh->vhead;
+ struct pvr2_v4l2_dev *pdi = fh->pdi;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ long ret = -EINVAL;
+
+ if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) {
+ v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw),cmd);
+ }
+
+ if (!pvr2_hdw_dev_ok(hdw)) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "ioctl failed - bad or no context");
+ return -EFAULT;
+ }
+
+ /* check priority */
+ switch (cmd) {
+ case VIDIOC_S_CTRL:
+ case VIDIOC_S_STD:
+ case VIDIOC_S_INPUT:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_S_FREQUENCY:
+ ret = v4l2_prio_check(&vp->prio, fh->prio);
+ if (ret)
+ return ret;
+ }
+
+ switch (cmd) {
+ case VIDIOC_QUERYCAP:
+ {
+ struct v4l2_capability *cap = arg;
+
+ memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability));
+ strlcpy(cap->bus_info,pvr2_hdw_get_bus_info(hdw),
+ sizeof(cap->bus_info));
+ strlcpy(cap->card,pvr2_hdw_get_desc(hdw),sizeof(cap->card));
+
+ ret = 0;
+ break;
+ }
+
+ case VIDIOC_G_PRIORITY:
+ {
+ enum v4l2_priority *p = arg;
+
+ *p = v4l2_prio_max(&vp->prio);
+ ret = 0;
+ break;
+ }
+
+ case VIDIOC_S_PRIORITY:
+ {
+ enum v4l2_priority *prio = arg;
+
+ ret = v4l2_prio_change(&vp->prio, &fh->prio, *prio);
+ break;
+ }
+
+ case VIDIOC_ENUMSTD:
+ {
+ struct v4l2_standard *vs = (struct v4l2_standard *)arg;
+ int idx = vs->index;
+ ret = pvr2_hdw_get_stdenum_value(hdw,vs,idx+1);
+ break;
+ }
+
+ case VIDIOC_QUERYSTD:
+ {
+ v4l2_std_id *std = arg;
+ *std = V4L2_STD_ALL;
+ ret = pvr2_hdw_get_detected_std(hdw, std);
+ break;
+ }
+
+ case VIDIOC_G_STD:
+ {
+ int val = 0;
+ ret = pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR),&val);
+ *(v4l2_std_id *)arg = val;
+ break;
+ }
+
+ case VIDIOC_S_STD:
+ {
+ ret = pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR),
+ *(v4l2_std_id *)arg);
+ break;
+ }
+
+ case VIDIOC_ENUMINPUT:
+ {
+ struct pvr2_ctrl *cptr;
+ struct v4l2_input *vi = (struct v4l2_input *)arg;
+ struct v4l2_input tmp;
+ unsigned int cnt;
+ int val;
+
+ cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT);
+
+ memset(&tmp,0,sizeof(tmp));
+ tmp.index = vi->index;
+ ret = 0;
+ if (vi->index >= fh->input_cnt) {
+ ret = -EINVAL;
+ break;
+ }
+ val = fh->input_map[vi->index];
+ switch (val) {
+ case PVR2_CVAL_INPUT_TV:
+ case PVR2_CVAL_INPUT_DTV:
+ case PVR2_CVAL_INPUT_RADIO:
+ tmp.type = V4L2_INPUT_TYPE_TUNER;
+ break;
+ case PVR2_CVAL_INPUT_SVIDEO:
+ case PVR2_CVAL_INPUT_COMPOSITE:
+ tmp.type = V4L2_INPUT_TYPE_CAMERA;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ if (ret < 0) break;
+
+ cnt = 0;
+ pvr2_ctrl_get_valname(cptr,val,
+ tmp.name,sizeof(tmp.name)-1,&cnt);
+ tmp.name[cnt] = 0;
+
+ /* Don't bother with audioset, since this driver currently
+ always switches the audio whenever the video is
+ switched. */
+
+ /* Handling std is a tougher problem. It doesn't make
+ sense in cases where a device might be multi-standard.
+ We could just copy out the current value for the
+ standard, but it can change over time. For now just
+ leave it zero. */
+
+ memcpy(vi, &tmp, sizeof(tmp));
+
+ ret = 0;
+ break;
+ }
+
+ case VIDIOC_G_INPUT:
+ {
+ unsigned int idx;
+ struct pvr2_ctrl *cptr;
+ struct v4l2_input *vi = (struct v4l2_input *)arg;
+ int val;
+ cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT);
+ val = 0;
+ ret = pvr2_ctrl_get_value(cptr,&val);
+ vi->index = 0;
+ for (idx = 0; idx < fh->input_cnt; idx++) {
+ if (fh->input_map[idx] == val) {
+ vi->index = idx;
+ break;
+ }
+ }
+ break;
+ }
+
+ case VIDIOC_S_INPUT:
+ {
+ struct v4l2_input *vi = (struct v4l2_input *)arg;
+ if (vi->index >= fh->input_cnt) {
+ ret = -ERANGE;
+ break;
+ }
+ ret = pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT),
+ fh->input_map[vi->index]);
+ break;
+ }
+
+ case VIDIOC_ENUMAUDIO:
+ {
+ /* pkt: FIXME: We are returning one "fake" input here
+ which could very well be called "whatever_we_like".
+ This is for apps that want to see an audio input
+ just to feel comfortable, as well as to test if
+ it can do stereo or sth. There is actually no guarantee
+ that the actual audio input cannot change behind the app's
+ back, but most applications should not mind that either.
+
+ Hopefully, mplayer people will work with us on this (this
+ whole mess is to support mplayer pvr://), or Hans will come
+ up with a more standard way to say "we have inputs but we
+ don 't want you to change them independent of video" which
+ will sort this mess.
+ */
+ struct v4l2_audio *vin = arg;
+ ret = -EINVAL;
+ if (vin->index > 0) break;
+ strncpy(vin->name, "PVRUSB2 Audio",14);
+ vin->capability = V4L2_AUDCAP_STEREO;
+ ret = 0;
+ break;
+ break;
+ }
+
+ case VIDIOC_G_AUDIO:
+ {
+ /* pkt: FIXME: see above comment (VIDIOC_ENUMAUDIO) */
+ struct v4l2_audio *vin = arg;
+ memset(vin,0,sizeof(*vin));
+ vin->index = 0;
+ strncpy(vin->name, "PVRUSB2 Audio",14);
+ vin->capability = V4L2_AUDCAP_STEREO;
+ ret = 0;
+ break;
+ }
+
+ case VIDIOC_G_TUNER:
+ {
+ struct v4l2_tuner *vt = (struct v4l2_tuner *)arg;
+
+ if (vt->index != 0) break; /* Only answer for the 1st tuner */
+
+ pvr2_hdw_execute_tuner_poll(hdw);
+ ret = pvr2_hdw_get_tuner_status(hdw,vt);
+ break;
+ }
+
+ case VIDIOC_S_TUNER:
+ {
+ struct v4l2_tuner *vt=(struct v4l2_tuner *)arg;
+
+ if (vt->index != 0)
+ break;
+
+ ret = pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_AUDIOMODE),
+ vt->audmode);
+ break;
+ }
+
+ case VIDIOC_S_FREQUENCY:
+ {
+ const struct v4l2_frequency *vf = (struct v4l2_frequency *)arg;
+ unsigned long fv;
+ struct v4l2_tuner vt;
+ int cur_input;
+ struct pvr2_ctrl *ctrlp;
+ ret = pvr2_hdw_get_tuner_status(hdw,&vt);
+ if (ret != 0) break;
+ ctrlp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT);
+ ret = pvr2_ctrl_get_value(ctrlp,&cur_input);
+ if (ret != 0) break;
+ if (vf->type == V4L2_TUNER_RADIO) {
+ if (cur_input != PVR2_CVAL_INPUT_RADIO) {
+ pvr2_ctrl_set_value(ctrlp,
+ PVR2_CVAL_INPUT_RADIO);
+ }
+ } else {
+ if (cur_input == PVR2_CVAL_INPUT_RADIO) {
+ pvr2_ctrl_set_value(ctrlp,
+ PVR2_CVAL_INPUT_TV);
+ }
+ }
+ fv = vf->frequency;
+ if (vt.capability & V4L2_TUNER_CAP_LOW) {
+ fv = (fv * 125) / 2;
+ } else {
+ fv = fv * 62500;
+ }
+ ret = pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY),fv);
+ break;
+ }
+
+ case VIDIOC_G_FREQUENCY:
+ {
+ struct v4l2_frequency *vf = (struct v4l2_frequency *)arg;
+ int val = 0;
+ int cur_input;
+ struct v4l2_tuner vt;
+ ret = pvr2_hdw_get_tuner_status(hdw,&vt);
+ if (ret != 0) break;
+ ret = pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY),
+ &val);
+ if (ret != 0) break;
+ pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT),
+ &cur_input);
+ if (cur_input == PVR2_CVAL_INPUT_RADIO) {
+ vf->type = V4L2_TUNER_RADIO;
+ } else {
+ vf->type = V4L2_TUNER_ANALOG_TV;
+ }
+ if (vt.capability & V4L2_TUNER_CAP_LOW) {
+ val = (val * 2) / 125;
+ } else {
+ val /= 62500;
+ }
+ vf->frequency = val;
+ break;
+ }
+
+ case VIDIOC_ENUM_FMT:
+ {
+ struct v4l2_fmtdesc *fd = (struct v4l2_fmtdesc *)arg;
+
+ /* Only one format is supported : mpeg.*/
+ if (fd->index != 0)
+ break;
+
+ memcpy(fd, pvr_fmtdesc, sizeof(struct v4l2_fmtdesc));
+ ret = 0;
+ break;
+ }
+
+ case VIDIOC_G_FMT:
+ {
+ struct v4l2_format *vf = (struct v4l2_format *)arg;
+ int val;
+ switch(vf->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ memcpy(vf, &pvr_format[PVR_FORMAT_PIX],
+ sizeof(struct v4l2_format));
+ val = 0;
+ pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_HRES),
+ &val);
+ vf->fmt.pix.width = val;
+ val = 0;
+ pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_VRES),
+ &val);
+ vf->fmt.pix.height = val;
+ ret = 0;
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ // ????? Still need to figure out to do VBI correctly
+ ret = -EINVAL;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ break;
+ }
+
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_S_FMT:
+ {
+ struct v4l2_format *vf = (struct v4l2_format *)arg;
+
+ ret = 0;
+ switch(vf->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+ int lmin,lmax,ldef;
+ struct pvr2_ctrl *hcp,*vcp;
+ int h = vf->fmt.pix.height;
+ int w = vf->fmt.pix.width;
+ hcp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_HRES);
+ vcp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_VRES);
+
+ lmin = pvr2_ctrl_get_min(hcp);
+ lmax = pvr2_ctrl_get_max(hcp);
+ pvr2_ctrl_get_def(hcp, &ldef);
+ if (w == -1) {
+ w = ldef;
+ } else if (w < lmin) {
+ w = lmin;
+ } else if (w > lmax) {
+ w = lmax;
+ }
+ lmin = pvr2_ctrl_get_min(vcp);
+ lmax = pvr2_ctrl_get_max(vcp);
+ pvr2_ctrl_get_def(vcp, &ldef);
+ if (h == -1) {
+ h = ldef;
+ } else if (h < lmin) {
+ h = lmin;
+ } else if (h > lmax) {
+ h = lmax;
+ }
+
+ memcpy(vf, &pvr_format[PVR_FORMAT_PIX],
+ sizeof(struct v4l2_format));
+ vf->fmt.pix.width = w;
+ vf->fmt.pix.height = h;
+
+ if (cmd == VIDIOC_S_FMT) {
+ pvr2_ctrl_set_value(hcp,vf->fmt.pix.width);
+ pvr2_ctrl_set_value(vcp,vf->fmt.pix.height);
+ }
+ } break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ // ????? Still need to figure out to do VBI correctly
+ ret = -EINVAL;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ break;
+ }
+
+ case VIDIOC_STREAMON:
+ {
+ if (!fh->pdi->stream) {
+ /* No stream defined for this node. This means
+ that we're not currently allowed to stream from
+ this node. */
+ ret = -EPERM;
+ break;
+ }
+ ret = pvr2_hdw_set_stream_type(hdw,pdi->config);
+ if (ret < 0) return ret;
+ ret = pvr2_hdw_set_streaming(hdw,!0);
+ break;
+ }
+
+ case VIDIOC_STREAMOFF:
+ {
+ if (!fh->pdi->stream) {
+ /* No stream defined for this node. This means
+ that we're not currently allowed to stream from
+ this node. */
+ ret = -EPERM;
+ break;
+ }
+ ret = pvr2_hdw_set_streaming(hdw,0);
+ break;
+ }
+
+ case VIDIOC_QUERYCTRL:
+ {
+ struct pvr2_ctrl *cptr;
+ int val;
+ struct v4l2_queryctrl *vc = (struct v4l2_queryctrl *)arg;
+ ret = 0;
+ if (vc->id & V4L2_CTRL_FLAG_NEXT_CTRL) {
+ cptr = pvr2_hdw_get_ctrl_nextv4l(
+ hdw,(vc->id & ~V4L2_CTRL_FLAG_NEXT_CTRL));
+ if (cptr) vc->id = pvr2_ctrl_get_v4lid(cptr);
+ } else {
+ cptr = pvr2_hdw_get_ctrl_v4l(hdw,vc->id);
+ }
+ if (!cptr) {
+ pvr2_trace(PVR2_TRACE_V4LIOCTL,
+ "QUERYCTRL id=0x%x not implemented here",
+ vc->id);
+ ret = -EINVAL;
+ break;
+ }
+
+ pvr2_trace(PVR2_TRACE_V4LIOCTL,
+ "QUERYCTRL id=0x%x mapping name=%s (%s)",
+ vc->id,pvr2_ctrl_get_name(cptr),
+ pvr2_ctrl_get_desc(cptr));
+ strlcpy(vc->name,pvr2_ctrl_get_desc(cptr),sizeof(vc->name));
+ vc->flags = pvr2_ctrl_get_v4lflags(cptr);
+ pvr2_ctrl_get_def(cptr, &val);
+ vc->default_value = val;
+ switch (pvr2_ctrl_get_type(cptr)) {
+ case pvr2_ctl_enum:
+ vc->type = V4L2_CTRL_TYPE_MENU;
+ vc->minimum = 0;
+ vc->maximum = pvr2_ctrl_get_cnt(cptr) - 1;
+ vc->step = 1;
+ break;
+ case pvr2_ctl_bool:
+ vc->type = V4L2_CTRL_TYPE_BOOLEAN;
+ vc->minimum = 0;
+ vc->maximum = 1;
+ vc->step = 1;
+ break;
+ case pvr2_ctl_int:
+ vc->type = V4L2_CTRL_TYPE_INTEGER;
+ vc->minimum = pvr2_ctrl_get_min(cptr);
+ vc->maximum = pvr2_ctrl_get_max(cptr);
+ vc->step = 1;
+ break;
+ default:
+ pvr2_trace(PVR2_TRACE_V4LIOCTL,
+ "QUERYCTRL id=0x%x name=%s not mappable",
+ vc->id,pvr2_ctrl_get_name(cptr));
+ ret = -EINVAL;
+ break;
+ }
+ break;
+ }
+
+ case VIDIOC_QUERYMENU:
+ {
+ struct v4l2_querymenu *vm = (struct v4l2_querymenu *)arg;
+ unsigned int cnt = 0;
+ ret = pvr2_ctrl_get_valname(pvr2_hdw_get_ctrl_v4l(hdw,vm->id),
+ vm->index,
+ vm->name,sizeof(vm->name)-1,
+ &cnt);
+ vm->name[cnt] = 0;
+ break;
+ }
+
+ case VIDIOC_G_CTRL:
+ {
+ struct v4l2_control *vc = (struct v4l2_control *)arg;
+ int val = 0;
+ ret = pvr2_ctrl_get_value(pvr2_hdw_get_ctrl_v4l(hdw,vc->id),
+ &val);
+ vc->value = val;
+ break;
+ }
+
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *vc = (struct v4l2_control *)arg;
+ ret = pvr2_ctrl_set_value(pvr2_hdw_get_ctrl_v4l(hdw,vc->id),
+ vc->value);
+ break;
+ }
+
+ case VIDIOC_G_EXT_CTRLS:
+ {
+ struct v4l2_ext_controls *ctls =
+ (struct v4l2_ext_controls *)arg;
+ struct v4l2_ext_control *ctrl;
+ unsigned int idx;
+ int val;
+ ret = 0;
+ for (idx = 0; idx < ctls->count; idx++) {
+ ctrl = ctls->controls + idx;
+ ret = pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_v4l(hdw,ctrl->id),&val);
+ if (ret) {
+ ctls->error_idx = idx;
+ break;
+ }
+ /* Ensure that if read as a 64 bit value, the user
+ will still get a hopefully sane value */
+ ctrl->value64 = 0;
+ ctrl->value = val;
+ }
+ break;
+ }
+
+ case VIDIOC_S_EXT_CTRLS:
+ {
+ struct v4l2_ext_controls *ctls =
+ (struct v4l2_ext_controls *)arg;
+ struct v4l2_ext_control *ctrl;
+ unsigned int idx;
+ ret = 0;
+ for (idx = 0; idx < ctls->count; idx++) {
+ ctrl = ctls->controls + idx;
+ ret = pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_v4l(hdw,ctrl->id),
+ ctrl->value);
+ if (ret) {
+ ctls->error_idx = idx;
+ break;
+ }
+ }
+ break;
+ }
+
+ case VIDIOC_TRY_EXT_CTRLS:
+ {
+ struct v4l2_ext_controls *ctls =
+ (struct v4l2_ext_controls *)arg;
+ struct v4l2_ext_control *ctrl;
+ struct pvr2_ctrl *pctl;
+ unsigned int idx;
+ /* For the moment just validate that the requested control
+ actually exists. */
+ ret = 0;
+ for (idx = 0; idx < ctls->count; idx++) {
+ ctrl = ctls->controls + idx;
+ pctl = pvr2_hdw_get_ctrl_v4l(hdw,ctrl->id);
+ if (!pctl) {
+ ret = -EINVAL;
+ ctls->error_idx = idx;
+ break;
+ }
+ }
+ break;
+ }
+
+ case VIDIOC_CROPCAP:
+ {
+ struct v4l2_cropcap *cap = (struct v4l2_cropcap *)arg;
+ if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = pvr2_hdw_get_cropcap(hdw, cap);
+ cap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* paranoia */
+ break;
+ }
+ case VIDIOC_G_CROP:
+ {
+ struct v4l2_crop *crop = (struct v4l2_crop *)arg;
+ int val = 0;
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), &val);
+ if (ret != 0) {
+ ret = -EINVAL;
+ break;
+ }
+ crop->c.left = val;
+ ret = pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), &val);
+ if (ret != 0) {
+ ret = -EINVAL;
+ break;
+ }
+ crop->c.top = val;
+ ret = pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), &val);
+ if (ret != 0) {
+ ret = -EINVAL;
+ break;
+ }
+ crop->c.width = val;
+ ret = pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), &val);
+ if (ret != 0) {
+ ret = -EINVAL;
+ break;
+ }
+ crop->c.height = val;
+ }
+ case VIDIOC_S_CROP:
+ {
+ struct v4l2_crop *crop = (struct v4l2_crop *)arg;
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL),
+ crop->c.left);
+ if (ret != 0) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT),
+ crop->c.top);
+ if (ret != 0) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW),
+ crop->c.width);
+ if (ret != 0) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH),
+ crop->c.height);
+ if (ret != 0) {
+ ret = -EINVAL;
+ break;
+ }
+ }
+ case VIDIOC_LOG_STATUS:
+ {
+ pvr2_hdw_trigger_module_log(hdw);
+ ret = 0;
+ break;
+ }
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ case VIDIOC_DBG_S_REGISTER:
+ case VIDIOC_DBG_G_REGISTER:
+ {
+ u64 val;
+ struct v4l2_dbg_register *req = (struct v4l2_dbg_register *)arg;
+ if (cmd == VIDIOC_DBG_S_REGISTER) val = req->val;
+ ret = pvr2_hdw_register_access(
+ hdw, &req->match, req->reg,
+ cmd == VIDIOC_DBG_S_REGISTER, &val);
+ if (cmd == VIDIOC_DBG_G_REGISTER) req->val = val;
+ break;
+ }
+#endif
+
+ default :
+ ret = -ENOTTY;
+ break;
+ }
+
+ pvr2_hdw_commit_ctl(hdw);
+
+ if (ret < 0) {
+ if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) {
+ pvr2_trace(PVR2_TRACE_V4LIOCTL,
+ "pvr2_v4l2_do_ioctl failure, ret=%ld", ret);
+ } else {
+ if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) {
+ pvr2_trace(PVR2_TRACE_V4LIOCTL,
+ "pvr2_v4l2_do_ioctl failure, ret=%ld"
+ " command was:", ret);
+ v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw),
+ cmd);
+ }
+ }
+ } else {
+ pvr2_trace(PVR2_TRACE_V4LIOCTL,
+ "pvr2_v4l2_do_ioctl complete, ret=%ld (0x%lx)",
+ ret, ret);
+ }
+ return ret;
+}
+
+static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip)
+{
+ struct pvr2_hdw *hdw = dip->v4lp->channel.mc_head->hdw;
+ enum pvr2_config cfg = dip->config;
+ char msg[80];
+ unsigned int mcnt;
+
+ /* Construct the unregistration message *before* we actually
+ perform the unregistration step. By doing it this way we don't
+ have to worry about potentially touching deleted resources. */
+ mcnt = scnprintf(msg, sizeof(msg) - 1,
+ "pvrusb2: unregistered device %s [%s]",
+ video_device_node_name(&dip->devbase),
+ pvr2_config_get_name(cfg));
+ msg[mcnt] = 0;
+
+ pvr2_hdw_v4l_store_minor_number(hdw,dip->minor_type,-1);
+
+ /* Paranoia */
+ dip->v4lp = NULL;
+ dip->stream = NULL;
+
+ /* Actual deallocation happens later when all internal references
+ are gone. */
+ video_unregister_device(&dip->devbase);
+
+ printk(KERN_INFO "%s\n", msg);
+
+}
+
+
+static void pvr2_v4l2_dev_disassociate_parent(struct pvr2_v4l2_dev *dip)
+{
+ if (!dip) return;
+ if (!dip->devbase.parent) return;
+ dip->devbase.parent = NULL;
+ device_move(&dip->devbase.dev, NULL, DPM_ORDER_NONE);
+}
+
+
+static void pvr2_v4l2_destroy_no_lock(struct pvr2_v4l2 *vp)
+{
+ if (vp->dev_video) {
+ pvr2_v4l2_dev_destroy(vp->dev_video);
+ vp->dev_video = NULL;
+ }
+ if (vp->dev_radio) {
+ pvr2_v4l2_dev_destroy(vp->dev_radio);
+ vp->dev_radio = NULL;
+ }
+
+ pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_v4l2 id=%p",vp);
+ pvr2_channel_done(&vp->channel);
+ kfree(vp);
+}
+
+
+static void pvr2_video_device_release(struct video_device *vdev)
+{
+ struct pvr2_v4l2_dev *dev;
+ dev = container_of(vdev,struct pvr2_v4l2_dev,devbase);
+ kfree(dev);
+}
+
+
+static void pvr2_v4l2_internal_check(struct pvr2_channel *chp)
+{
+ struct pvr2_v4l2 *vp;
+ vp = container_of(chp,struct pvr2_v4l2,channel);
+ if (!vp->channel.mc_head->disconnect_flag) return;
+ pvr2_v4l2_dev_disassociate_parent(vp->dev_video);
+ pvr2_v4l2_dev_disassociate_parent(vp->dev_radio);
+ if (vp->vfirst) return;
+ pvr2_v4l2_destroy_no_lock(vp);
+}
+
+
+static long pvr2_v4l2_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+
+ return video_usercopy(file, cmd, arg, pvr2_v4l2_do_ioctl);
+}
+
+
+static int pvr2_v4l2_release(struct file *file)
+{
+ struct pvr2_v4l2_fh *fhp = file->private_data;
+ struct pvr2_v4l2 *vp = fhp->vhead;
+ struct pvr2_hdw *hdw = fhp->channel.mc_head->hdw;
+
+ pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_release");
+
+ if (fhp->rhp) {
+ struct pvr2_stream *sp;
+ pvr2_hdw_set_streaming(hdw,0);
+ sp = pvr2_ioread_get_stream(fhp->rhp);
+ if (sp) pvr2_stream_set_callback(sp,NULL,NULL);
+ pvr2_ioread_destroy(fhp->rhp);
+ fhp->rhp = NULL;
+ }
+
+ v4l2_prio_close(&vp->prio, fhp->prio);
+ file->private_data = NULL;
+
+ if (fhp->vnext) {
+ fhp->vnext->vprev = fhp->vprev;
+ } else {
+ vp->vlast = fhp->vprev;
+ }
+ if (fhp->vprev) {
+ fhp->vprev->vnext = fhp->vnext;
+ } else {
+ vp->vfirst = fhp->vnext;
+ }
+ fhp->vnext = NULL;
+ fhp->vprev = NULL;
+ fhp->vhead = NULL;
+ pvr2_channel_done(&fhp->channel);
+ pvr2_trace(PVR2_TRACE_STRUCT,
+ "Destroying pvr_v4l2_fh id=%p",fhp);
+ if (fhp->input_map) {
+ kfree(fhp->input_map);
+ fhp->input_map = NULL;
+ }
+ kfree(fhp);
+ if (vp->channel.mc_head->disconnect_flag && !vp->vfirst) {
+ pvr2_v4l2_destroy_no_lock(vp);
+ }
+ return 0;
+}
+
+
+static int pvr2_v4l2_open(struct file *file)
+{
+ struct pvr2_v4l2_dev *dip; /* Our own context pointer */
+ struct pvr2_v4l2_fh *fhp;
+ struct pvr2_v4l2 *vp;
+ struct pvr2_hdw *hdw;
+ unsigned int input_mask = 0;
+ unsigned int input_cnt,idx;
+ int ret = 0;
+
+ dip = container_of(video_devdata(file),struct pvr2_v4l2_dev,devbase);
+
+ vp = dip->v4lp;
+ hdw = vp->channel.hdw;
+
+ pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_open");
+
+ if (!pvr2_hdw_dev_ok(hdw)) {
+ pvr2_trace(PVR2_TRACE_OPEN_CLOSE,
+ "pvr2_v4l2_open: hardware not ready");
+ return -EIO;
+ }
+
+ fhp = kzalloc(sizeof(*fhp),GFP_KERNEL);
+ if (!fhp) {
+ return -ENOMEM;
+ }
+
+ init_waitqueue_head(&fhp->wait_data);
+ fhp->pdi = dip;
+
+ pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_v4l2_fh id=%p",fhp);
+ pvr2_channel_init(&fhp->channel,vp->channel.mc_head);
+
+ if (dip->v4l_type == VFL_TYPE_RADIO) {
+ /* Opening device as a radio, legal input selection subset
+ is just the radio. */
+ input_mask = (1 << PVR2_CVAL_INPUT_RADIO);
+ } else {
+ /* Opening the main V4L device, legal input selection
+ subset includes all analog inputs. */
+ input_mask = ((1 << PVR2_CVAL_INPUT_RADIO) |
+ (1 << PVR2_CVAL_INPUT_TV) |
+ (1 << PVR2_CVAL_INPUT_COMPOSITE) |
+ (1 << PVR2_CVAL_INPUT_SVIDEO));
+ }
+ ret = pvr2_channel_limit_inputs(&fhp->channel,input_mask);
+ if (ret) {
+ pvr2_channel_done(&fhp->channel);
+ pvr2_trace(PVR2_TRACE_STRUCT,
+ "Destroying pvr_v4l2_fh id=%p (input mask error)",
+ fhp);
+
+ kfree(fhp);
+ return ret;
+ }
+
+ input_mask &= pvr2_hdw_get_input_available(hdw);
+ input_cnt = 0;
+ for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) {
+ if (input_mask & (1 << idx)) input_cnt++;
+ }
+ fhp->input_cnt = input_cnt;
+ fhp->input_map = kzalloc(input_cnt,GFP_KERNEL);
+ if (!fhp->input_map) {
+ pvr2_channel_done(&fhp->channel);
+ pvr2_trace(PVR2_TRACE_STRUCT,
+ "Destroying pvr_v4l2_fh id=%p (input map failure)",
+ fhp);
+ kfree(fhp);
+ return -ENOMEM;
+ }
+ input_cnt = 0;
+ for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) {
+ if (!(input_mask & (1 << idx))) continue;
+ fhp->input_map[input_cnt++] = idx;
+ }
+
+ fhp->vnext = NULL;
+ fhp->vprev = vp->vlast;
+ if (vp->vlast) {
+ vp->vlast->vnext = fhp;
+ } else {
+ vp->vfirst = fhp;
+ }
+ vp->vlast = fhp;
+ fhp->vhead = vp;
+
+ fhp->file = file;
+ file->private_data = fhp;
+ v4l2_prio_open(&vp->prio, &fhp->prio);
+
+ fhp->fw_mode_flag = pvr2_hdw_cpufw_get_enabled(hdw);
+
+ return 0;
+}
+
+
+static void pvr2_v4l2_notify(struct pvr2_v4l2_fh *fhp)
+{
+ wake_up(&fhp->wait_data);
+}
+
+static int pvr2_v4l2_iosetup(struct pvr2_v4l2_fh *fh)
+{
+ int ret;
+ struct pvr2_stream *sp;
+ struct pvr2_hdw *hdw;
+ if (fh->rhp) return 0;
+
+ if (!fh->pdi->stream) {
+ /* No stream defined for this node. This means that we're
+ not currently allowed to stream from this node. */
+ return -EPERM;
+ }
+
+ /* First read() attempt. Try to claim the stream and start
+ it... */
+ if ((ret = pvr2_channel_claim_stream(&fh->channel,
+ fh->pdi->stream)) != 0) {
+ /* Someone else must already have it */
+ return ret;
+ }
+
+ fh->rhp = pvr2_channel_create_mpeg_stream(fh->pdi->stream);
+ if (!fh->rhp) {
+ pvr2_channel_claim_stream(&fh->channel,NULL);
+ return -ENOMEM;
+ }
+
+ hdw = fh->channel.mc_head->hdw;
+ sp = fh->pdi->stream->stream;
+ pvr2_stream_set_callback(sp,(pvr2_stream_callback)pvr2_v4l2_notify,fh);
+ pvr2_hdw_set_stream_type(hdw,fh->pdi->config);
+ if ((ret = pvr2_hdw_set_streaming(hdw,!0)) < 0) return ret;
+ return pvr2_ioread_set_enabled(fh->rhp,!0);
+}
+
+
+static ssize_t pvr2_v4l2_read(struct file *file,
+ char __user *buff, size_t count, loff_t *ppos)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ int ret;
+
+ if (fh->fw_mode_flag) {
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ char *tbuf;
+ int c1,c2;
+ int tcnt = 0;
+ unsigned int offs = *ppos;
+
+ tbuf = kmalloc(PAGE_SIZE,GFP_KERNEL);
+ if (!tbuf) return -ENOMEM;
+
+ while (count) {
+ c1 = count;
+ if (c1 > PAGE_SIZE) c1 = PAGE_SIZE;
+ c2 = pvr2_hdw_cpufw_get(hdw,offs,tbuf,c1);
+ if (c2 < 0) {
+ tcnt = c2;
+ break;
+ }
+ if (!c2) break;
+ if (copy_to_user(buff,tbuf,c2)) {
+ tcnt = -EFAULT;
+ break;
+ }
+ offs += c2;
+ tcnt += c2;
+ buff += c2;
+ count -= c2;
+ *ppos += c2;
+ }
+ kfree(tbuf);
+ return tcnt;
+ }
+
+ if (!fh->rhp) {
+ ret = pvr2_v4l2_iosetup(fh);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ for (;;) {
+ ret = pvr2_ioread_read(fh->rhp,buff,count);
+ if (ret >= 0) break;
+ if (ret != -EAGAIN) break;
+ if (file->f_flags & O_NONBLOCK) break;
+ /* Doing blocking I/O. Wait here. */
+ ret = wait_event_interruptible(
+ fh->wait_data,
+ pvr2_ioread_avail(fh->rhp) >= 0);
+ if (ret < 0) break;
+ }
+
+ return ret;
+}
+
+
+static unsigned int pvr2_v4l2_poll(struct file *file, poll_table *wait)
+{
+ unsigned int mask = 0;
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ int ret;
+
+ if (fh->fw_mode_flag) {
+ mask |= POLLIN | POLLRDNORM;
+ return mask;
+ }
+
+ if (!fh->rhp) {
+ ret = pvr2_v4l2_iosetup(fh);
+ if (ret) return POLLERR;
+ }
+
+ poll_wait(file,&fh->wait_data,wait);
+
+ if (pvr2_ioread_avail(fh->rhp) >= 0) {
+ mask |= POLLIN | POLLRDNORM;
+ }
+
+ return mask;
+}
+
+
+static const struct v4l2_file_operations vdev_fops = {
+ .owner = THIS_MODULE,
+ .open = pvr2_v4l2_open,
+ .release = pvr2_v4l2_release,
+ .read = pvr2_v4l2_read,
+ .ioctl = pvr2_v4l2_ioctl,
+ .poll = pvr2_v4l2_poll,
+};
+
+
+static struct video_device vdev_template = {
+ .fops = &vdev_fops,
+};
+
+
+static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip,
+ struct pvr2_v4l2 *vp,
+ int v4l_type)
+{
+ struct usb_device *usbdev;
+ int mindevnum;
+ int unit_number;
+ int *nr_ptr = NULL;
+ dip->v4lp = vp;
+
+ usbdev = pvr2_hdw_get_dev(vp->channel.mc_head->hdw);
+ dip->v4l_type = v4l_type;
+ switch (v4l_type) {
+ case VFL_TYPE_GRABBER:
+ dip->stream = &vp->channel.mc_head->video_stream;
+ dip->config = pvr2_config_mpeg;
+ dip->minor_type = pvr2_v4l_type_video;
+ nr_ptr = video_nr;
+ if (!dip->stream) {
+ pr_err(KBUILD_MODNAME
+ ": Failed to set up pvrusb2 v4l video dev"
+ " due to missing stream instance\n");
+ return;
+ }
+ break;
+ case VFL_TYPE_VBI:
+ dip->config = pvr2_config_vbi;
+ dip->minor_type = pvr2_v4l_type_vbi;
+ nr_ptr = vbi_nr;
+ break;
+ case VFL_TYPE_RADIO:
+ dip->stream = &vp->channel.mc_head->video_stream;
+ dip->config = pvr2_config_mpeg;
+ dip->minor_type = pvr2_v4l_type_radio;
+ nr_ptr = radio_nr;
+ break;
+ default:
+ /* Bail out (this should be impossible) */
+ pr_err(KBUILD_MODNAME ": Failed to set up pvrusb2 v4l dev"
+ " due to unrecognized config\n");
+ return;
+ }
+
+ memcpy(&dip->devbase,&vdev_template,sizeof(vdev_template));
+ dip->devbase.release = pvr2_video_device_release;
+
+ mindevnum = -1;
+ unit_number = pvr2_hdw_get_unit_number(vp->channel.mc_head->hdw);
+ if (nr_ptr && (unit_number >= 0) && (unit_number < PVR_NUM)) {
+ mindevnum = nr_ptr[unit_number];
+ }
+ dip->devbase.parent = &usbdev->dev;
+ if ((video_register_device(&dip->devbase,
+ dip->v4l_type, mindevnum) < 0) &&
+ (video_register_device(&dip->devbase,
+ dip->v4l_type, -1) < 0)) {
+ pr_err(KBUILD_MODNAME
+ ": Failed to register pvrusb2 v4l device\n");
+ }
+
+ printk(KERN_INFO "pvrusb2: registered device %s [%s]\n",
+ video_device_node_name(&dip->devbase),
+ pvr2_config_get_name(dip->config));
+
+ pvr2_hdw_v4l_store_minor_number(vp->channel.mc_head->hdw,
+ dip->minor_type,dip->devbase.minor);
+}
+
+
+struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp)
+{
+ struct pvr2_v4l2 *vp;
+
+ vp = kzalloc(sizeof(*vp),GFP_KERNEL);
+ if (!vp) return vp;
+ pvr2_channel_init(&vp->channel,mnp);
+ pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_v4l2 id=%p",vp);
+
+ vp->channel.check_func = pvr2_v4l2_internal_check;
+
+ /* register streams */
+ vp->dev_video = kzalloc(sizeof(*vp->dev_video),GFP_KERNEL);
+ if (!vp->dev_video) goto fail;
+ pvr2_v4l2_dev_init(vp->dev_video,vp,VFL_TYPE_GRABBER);
+ if (pvr2_hdw_get_input_available(vp->channel.mc_head->hdw) &
+ (1 << PVR2_CVAL_INPUT_RADIO)) {
+ vp->dev_radio = kzalloc(sizeof(*vp->dev_radio),GFP_KERNEL);
+ if (!vp->dev_radio) goto fail;
+ pvr2_v4l2_dev_init(vp->dev_radio,vp,VFL_TYPE_RADIO);
+ }
+
+ return vp;
+ fail:
+ pvr2_trace(PVR2_TRACE_STRUCT,"Failure creating pvr2_v4l2 id=%p",vp);
+ pvr2_v4l2_destroy_no_lock(vp);
+ return NULL;
+}
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.h b/drivers/media/video/pvrusb2/pvrusb2-v4l2.h
new file mode 100644
index 00000000..34c011a7
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.h
@@ -0,0 +1,39 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef __PVRUSB2_V4L2_H
+#define __PVRUSB2_V4L2_H
+
+#include "pvrusb2-context.h"
+
+struct pvr2_v4l2;
+
+struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *);
+
+#endif /* __PVRUSB2_V4L2_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c
new file mode 100644
index 00000000..2e205c99
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c
@@ -0,0 +1,114 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+
+ This source file is specifically designed to interface with the
+ saa711x support that is available in the v4l available starting
+ with linux 2.6.15.
+
+*/
+
+#include "pvrusb2-video-v4l.h"
+
+
+
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-debug.h"
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/saa7115.h>
+#include <linux/errno.h>
+
+struct routing_scheme {
+ const int *def;
+ unsigned int cnt;
+};
+
+
+static const int routing_scheme0[] = {
+ [PVR2_CVAL_INPUT_TV] = SAA7115_COMPOSITE4,
+ /* In radio mode, we mute the video, but point at one
+ spot just to stay consistent */
+ [PVR2_CVAL_INPUT_RADIO] = SAA7115_COMPOSITE5,
+ [PVR2_CVAL_INPUT_COMPOSITE] = SAA7115_COMPOSITE5,
+ [PVR2_CVAL_INPUT_SVIDEO] = SAA7115_SVIDEO2,
+};
+
+static const struct routing_scheme routing_def0 = {
+ .def = routing_scheme0,
+ .cnt = ARRAY_SIZE(routing_scheme0),
+};
+
+static const int routing_scheme1[] = {
+ [PVR2_CVAL_INPUT_TV] = SAA7115_COMPOSITE4,
+ [PVR2_CVAL_INPUT_RADIO] = SAA7115_COMPOSITE5,
+ [PVR2_CVAL_INPUT_COMPOSITE] = SAA7115_COMPOSITE3,
+ [PVR2_CVAL_INPUT_SVIDEO] = SAA7115_SVIDEO2, /* or SVIDEO0, it seems */
+};
+
+static const struct routing_scheme routing_def1 = {
+ .def = routing_scheme1,
+ .cnt = ARRAY_SIZE(routing_scheme1),
+};
+
+static const struct routing_scheme *routing_schemes[] = {
+ [PVR2_ROUTING_SCHEME_HAUPPAUGE] = &routing_def0,
+ [PVR2_ROUTING_SCHEME_ONAIR] = &routing_def1,
+};
+
+void pvr2_saa7115_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd)
+{
+ if (hdw->input_dirty || hdw->force_dirty) {
+ const struct routing_scheme *sp;
+ unsigned int sid = hdw->hdw_desc->signal_routing_scheme;
+ u32 input;
+
+ pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_input(%d)",
+ hdw->input_val);
+
+ sp = (sid < ARRAY_SIZE(routing_schemes)) ?
+ routing_schemes[sid] : NULL;
+ if ((sp == NULL) ||
+ (hdw->input_val < 0) ||
+ (hdw->input_val >= sp->cnt)) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "*** WARNING *** subdev v4l2 set_input:"
+ " Invalid routing scheme (%u)"
+ " and/or input (%d)",
+ sid, hdw->input_val);
+ return;
+ }
+ input = sp->def[hdw->input_val];
+ sd->ops->video->s_routing(sd, input, 0, 0);
+ }
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h
new file mode 100644
index 00000000..3b0bd5db
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h
@@ -0,0 +1,48 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __PVRUSB2_VIDEO_V4L_H
+#define __PVRUSB2_VIDEO_V4L_H
+
+/*
+
+ This module connects the pvrusb2 driver to the I2C chip level
+ driver which handles device video processing. This interface is
+ used internally by the driver; higher level code should only
+ interact through the interface provided by pvrusb2-hdw.h.
+
+*/
+
+
+#include "pvrusb2-hdw-internal.h"
+void pvr2_saa7115_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *);
+
+#endif /* __PVRUSB2_VIDEO_V4L_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-wm8775.c b/drivers/media/video/pvrusb2/pvrusb2-wm8775.c
new file mode 100644
index 00000000..3ac8d751
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-wm8775.c
@@ -0,0 +1,70 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+
+ This source file is specifically designed to interface with the
+ wm8775.
+
+*/
+
+#include "pvrusb2-wm8775.h"
+
+
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-debug.h"
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <linux/errno.h>
+
+void pvr2_wm8775_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd)
+{
+ if (hdw->input_dirty || hdw->force_dirty) {
+ u32 input;
+
+ switch (hdw->input_val) {
+ case PVR2_CVAL_INPUT_RADIO:
+ input = 1;
+ break;
+ default:
+ /* All other cases just use the second input */
+ input = 2;
+ break;
+ }
+ pvr2_trace(PVR2_TRACE_CHIPS, "subdev wm8775"
+ " set_input(val=%d route=0x%x)",
+ hdw->input_val, input);
+
+ sd->ops->audio->s_routing(sd, input, 0, 0);
+ }
+}
+
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-wm8775.h b/drivers/media/video/pvrusb2/pvrusb2-wm8775.h
new file mode 100644
index 00000000..0577bc72
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-wm8775.h
@@ -0,0 +1,52 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __PVRUSB2_WM8775_H
+#define __PVRUSB2_WM8775_H
+
+/*
+
+ This module connects the pvrusb2 driver to the I2C chip level
+ driver which performs analog -> digital audio conversion for
+ external audio inputs. This interface is used internally by the
+ driver; higher level code should only interact through the
+ interface provided by pvrusb2-hdw.h.
+
+*/
+
+
+
+#include "pvrusb2-hdw-internal.h"
+
+void pvr2_wm8775_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *sd);
+
+
+#endif /* __PVRUSB2_WM8775_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2.h b/drivers/media/video/pvrusb2/pvrusb2.h
new file mode 100644
index 00000000..240de9b3
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2.h
@@ -0,0 +1,42 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __PVRUSB2_H
+#define __PVRUSB2_H
+
+/* Maximum number of pvrusb2 instances we can track at once. You
+ might want to increase this - however the driver operation will not
+ be impaired if it is too small. Instead additional units just
+ won't have an ID assigned and it might not be possible to specify
+ module parameters for those extra units. */
+#define PVR_NUM 20
+
+#endif /* __PVRUSB2_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pwc/Kconfig b/drivers/media/video/pwc/Kconfig
new file mode 100644
index 00000000..d63d0a85
--- /dev/null
+++ b/drivers/media/video/pwc/Kconfig
@@ -0,0 +1,48 @@
+config USB_PWC
+ tristate "USB Philips Cameras"
+ depends on VIDEO_V4L2
+ select VIDEOBUF2_VMALLOC
+ ---help---
+ Say Y or M here if you want to use one of these Philips & OEM
+ webcams:
+ * Philips PCA645, PCA646
+ * Philips PCVC675, PCVC680, PCVC690
+ * Philips PCVC720/40, PCVC730, PCVC740, PCVC750
+ * Philips SPC900NC
+ * Askey VC010
+ * Logitech QuickCam Pro 3000, 4000, 'Zoom', 'Notebook Pro'
+ and 'Orbit'/'Sphere'
+ * Samsung MPC-C10, MPC-C30
+ * Creative Webcam 5, Pro Ex
+ * SOTEC Afina Eye
+ * Visionite VCS-UC300, VCS-UM100
+
+ The PCA635, PCVC665 and PCVC720/20 are not supported by this driver
+ and never will be, but the 665 and 720/20 are supported by other
+ drivers.
+
+ Some newer logitech webcams are not handled by this driver but by the
+ Usb Video Class driver (linux-uvc).
+
+ The built-in microphone is enabled by selecting USB Audio support.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pwc.
+
+config USB_PWC_DEBUG
+ bool "USB Philips Cameras verbose debug"
+ depends on USB_PWC
+ help
+ Say Y here in order to have the pwc driver generate verbose debugging
+ messages.
+ A special module options 'trace' is used to control the verbosity.
+
+config USB_PWC_INPUT_EVDEV
+ bool "USB Philips Cameras input events device support"
+ default y
+ depends on USB_PWC && (USB_PWC=INPUT || INPUT=y)
+ ---help---
+ This option makes USB Philips cameras register the snapshot button as
+ an input device to report button events.
+
+ If you are in doubt, say Y.
diff --git a/drivers/media/video/pwc/Makefile b/drivers/media/video/pwc/Makefile
new file mode 100644
index 00000000..f5c8ec26
--- /dev/null
+++ b/drivers/media/video/pwc/Makefile
@@ -0,0 +1,4 @@
+pwc-objs := pwc-if.o pwc-misc.o pwc-ctrl.o pwc-v4l.o pwc-uncompress.o
+pwc-objs += pwc-dec1.o pwc-dec23.o pwc-kiara.o pwc-timon.o
+
+obj-$(CONFIG_USB_PWC) += pwc.o
diff --git a/drivers/media/video/pwc/philips.txt b/drivers/media/video/pwc/philips.txt
new file mode 100644
index 00000000..d38dd791
--- /dev/null
+++ b/drivers/media/video/pwc/philips.txt
@@ -0,0 +1,236 @@
+This file contains some additional information for the Philips and OEM webcams.
+E-mail: webcam@smcc.demon.nl Last updated: 2004-01-19
+Site: http://www.smcc.demon.nl/webcam/
+
+As of this moment, the following cameras are supported:
+ * Philips PCA645
+ * Philips PCA646
+ * Philips PCVC675
+ * Philips PCVC680
+ * Philips PCVC690
+ * Philips PCVC720/40
+ * Philips PCVC730
+ * Philips PCVC740
+ * Philips PCVC750
+ * Askey VC010
+ * Creative Labs Webcam 5
+ * Creative Labs Webcam Pro Ex
+ * Logitech QuickCam 3000 Pro
+ * Logitech QuickCam 4000 Pro
+ * Logitech QuickCam Notebook Pro
+ * Logitech QuickCam Zoom
+ * Logitech QuickCam Orbit
+ * Logitech QuickCam Sphere
+ * Samsung MPC-C10
+ * Samsung MPC-C30
+ * Sotec Afina Eye
+ * AME CU-001
+ * Visionite VCS-UM100
+ * Visionite VCS-UC300
+
+The main webpage for the Philips driver is at the address above. It contains
+a lot of extra information, a FAQ, and the binary plugin 'PWCX'. This plugin
+contains decompression routines that allow you to use higher image sizes and
+framerates; in addition the webcam uses less bandwidth on the USB bus (handy
+if you want to run more than 1 camera simultaneously). These routines fall
+under a NDA, and may therefore not be distributed as source; however, its use
+is completely optional.
+
+You can build this code either into your kernel, or as a module. I recommend
+the latter, since it makes troubleshooting a lot easier. The built-in
+microphone is supported through the USB Audio class.
+
+When you load the module you can set some default settings for the
+camera; some programs depend on a particular image-size or -format and
+don't know how to set it properly in the driver. The options are:
+
+size
+ Can be one of 'sqcif', 'qsif', 'qcif', 'sif', 'cif' or
+ 'vga', for an image size of resp. 128x96, 160x120, 176x144,
+ 320x240, 352x288 and 640x480 (of course, only for those cameras that
+ support these resolutions).
+
+fps
+ Specifies the desired framerate. Is an integer in the range of 4-30.
+
+fbufs
+ This parameter specifies the number of internal buffers to use for storing
+ frames from the cam. This will help if the process that reads images from
+ the cam is a bit slow or momentarily busy. However, on slow machines it
+ only introduces lag, so choose carefully. The default is 3, which is
+ reasonable. You can set it between 2 and 5.
+
+mbufs
+ This is an integer between 1 and 10. It will tell the module the number of
+ buffers to reserve for mmap(), VIDIOCCGMBUF, VIDIOCMCAPTURE and friends.
+ The default is 2, which is adequate for most applications (double
+ buffering).
+
+ Should you experience a lot of 'Dumping frame...' messages during
+ grabbing with a tool that uses mmap(), you might want to increase if.
+ However, it doesn't really buffer images, it just gives you a bit more
+ slack when your program is behind. But you need a multi-threaded or
+ forked program to really take advantage of these buffers.
+
+ The absolute maximum is 10, but don't set it too high! Every buffer takes
+ up 460 KB of RAM, so unless you have a lot of memory setting this to
+ something more than 4 is an absolute waste. This memory is only
+ allocated during open(), so nothing is wasted when the camera is not in
+ use.
+
+power_save
+ When power_save is enabled (set to 1), the module will try to shut down
+ the cam on close() and re-activate on open(). This will save power and
+ turn off the LED. Not all cameras support this though (the 645 and 646
+ don't have power saving at all), and some models don't work either (they
+ will shut down, but never wake up). Consider this experimental. By
+ default this option is disabled.
+
+compression (only useful with the plugin)
+ With this option you can control the compression factor that the camera
+ uses to squeeze the image through the USB bus. You can set the
+ parameter between 0 and 3:
+ 0 = prefer uncompressed images; if the requested mode is not available
+ in an uncompressed format, the driver will silently switch to low
+ compression.
+ 1 = low compression.
+ 2 = medium compression.
+ 3 = high compression.
+
+ High compression takes less bandwidth of course, but it could also
+ introduce some unwanted artefacts. The default is 2, medium compression.
+ See the FAQ on the website for an overview of which modes require
+ compression.
+
+ The compression parameter does not apply to the 645 and 646 cameras
+ and OEM models derived from those (only a few). Most cams honour this
+ parameter.
+
+leds
+ This settings takes 2 integers, that define the on/off time for the LED
+ (in milliseconds). One of the interesting things that you can do with
+ this is let the LED blink while the camera is in use. This:
+
+ leds=500,500
+
+ will blink the LED once every second. But with:
+
+ leds=0,0
+
+ the LED never goes on, making it suitable for silent surveillance.
+
+ By default the camera's LED is on solid while in use, and turned off
+ when the camera is not used anymore.
+
+ This parameter works only with the ToUCam range of cameras (720, 730, 740,
+ 750) and OEMs. For other cameras this command is silently ignored, and
+ the LED cannot be controlled.
+
+ Finally: this parameters does not take effect UNTIL the first time you
+ open the camera device. Until then, the LED remains on.
+
+dev_hint
+ A long standing problem with USB devices is their dynamic nature: you
+ never know what device a camera gets assigned; it depends on module load
+ order, the hub configuration, the order in which devices are plugged in,
+ and the phase of the moon (i.e. it can be random). With this option you
+ can give the driver a hint as to what video device node (/dev/videoX) it
+ should use with a specific camera. This is also handy if you have two
+ cameras of the same model.
+
+ A camera is specified by its type (the number from the camera model,
+ like PCA645, PCVC750VC, etc) and optionally the serial number (visible
+ in /proc/bus/usb/devices). A hint consists of a string with the following
+ format:
+
+ [type[.serialnumber]:]node
+
+ The square brackets mean that both the type and the serialnumber are
+ optional, but a serialnumber cannot be specified without a type (which
+ would be rather pointless). The serialnumber is separated from the type
+ by a '.'; the node number by a ':'.
+
+ This somewhat cryptic syntax is best explained by a few examples:
+
+ dev_hint=3,5 The first detected cam gets assigned
+ /dev/video3, the second /dev/video5. Any
+ other cameras will get the first free
+ available slot (see below).
+
+ dev_hint=645:1,680:2 The PCA645 camera will get /dev/video1,
+ and a PCVC680 /dev/video2.
+
+ dev_hint=645.0123:3,645.4567:0 The PCA645 camera with serialnumber
+ 0123 goes to /dev/video3, the same
+ camera model with the 4567 serial
+ gets /dev/video0.
+
+ dev_hint=750:1,4,5,6 The PCVC750 camera will get /dev/video1, the
+ next 3 Philips cams will use /dev/video4
+ through /dev/video6.
+
+ Some points worth knowing:
+ - Serialnumbers are case sensitive and must be written full, including
+ leading zeroes (it's treated as a string).
+ - If a device node is already occupied, registration will fail and
+ the webcam is not available.
+ - You can have up to 64 video devices; be sure to make enough device
+ nodes in /dev if you want to spread the numbers.
+ After /dev/video9 comes /dev/video10 (not /dev/videoA).
+ - If a camera does not match any dev_hint, it will simply get assigned
+ the first available device node, just as it used to be.
+
+trace
+ In order to better detect problems, it is now possible to turn on a
+ 'trace' of some of the calls the module makes; it logs all items in your
+ kernel log at debug level.
+
+ The trace variable is a bitmask; each bit represents a certain feature.
+ If you want to trace something, look up the bit value(s) in the table
+ below, add the values together and supply that to the trace variable.
+
+ Value Value Description Default
+ (dec) (hex)
+ 1 0x1 Module initialization; this will log messages On
+ while loading and unloading the module
+
+ 2 0x2 probe() and disconnect() traces On
+
+ 4 0x4 Trace open() and close() calls Off
+
+ 8 0x8 read(), mmap() and associated ioctl() calls Off
+
+ 16 0x10 Memory allocation of buffers, etc. Off
+
+ 32 0x20 Showing underflow, overflow and Dumping frame On
+ messages
+
+ 64 0x40 Show viewport and image sizes Off
+
+ 128 0x80 PWCX debugging Off
+
+ For example, to trace the open() & read() functions, sum 8 + 4 = 12,
+ so you would supply trace=12 during insmod or modprobe. If
+ you want to turn the initialization and probing tracing off, set trace=0.
+ The default value for trace is 35 (0x23).
+
+
+
+Example:
+
+ # modprobe pwc size=cif fps=15 power_save=1
+
+The fbufs, mbufs and trace parameters are global and apply to all connected
+cameras. Each camera has its own set of buffers.
+
+size and fps only specify defaults when you open() the device; this is to
+accommodate some tools that don't set the size. You can change these
+settings after open() with the Video4Linux ioctl() calls. The default of
+defaults is QCIF size at 10 fps.
+
+The compression parameter is semiglobal; it sets the initial compression
+preference for all camera's, but this parameter can be set per camera with
+the VIDIOCPWCSCQUAL ioctl() call.
+
+All parameters are optional.
+
diff --git a/drivers/media/video/pwc/pwc-ctrl.c b/drivers/media/video/pwc/pwc-ctrl.c
new file mode 100644
index 00000000..1f506fde
--- /dev/null
+++ b/drivers/media/video/pwc/pwc-ctrl.c
@@ -0,0 +1,553 @@
+/* Driver for Philips webcam
+ Functions that send various control messages to the webcam, including
+ video modes.
+ (C) 1999-2003 Nemosoft Unv.
+ (C) 2004-2006 Luc Saillard (luc@saillard.org)
+ (C) 2011 Hans de Goede <hdegoede@redhat.com>
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+ The decompression routines have been implemented by reverse-engineering the
+ Nemosoft binary pwcx module. Caveat emptor.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/*
+ Changes
+ 2001/08/03 Alvarado Added methods for changing white balance and
+ red/green gains
+ */
+
+/* Control functions for the cam; brightness, contrast, video mode, etc. */
+
+#ifdef __KERNEL__
+#include <asm/uaccess.h>
+#endif
+#include <asm/errno.h>
+
+#include "pwc.h"
+#include "pwc-kiara.h"
+#include "pwc-timon.h"
+#include "pwc-dec1.h"
+#include "pwc-dec23.h"
+
+/* Selectors for status controls used only in this file */
+#define GET_STATUS_B00 0x0B00
+#define SENSOR_TYPE_FORMATTER1 0x0C00
+#define GET_STATUS_3000 0x3000
+#define READ_RAW_Y_MEAN_FORMATTER 0x3100
+#define SET_POWER_SAVE_MODE_FORMATTER 0x3200
+#define MIRROR_IMAGE_FORMATTER 0x3300
+#define LED_FORMATTER 0x3400
+#define LOWLIGHT 0x3500
+#define GET_STATUS_3600 0x3600
+#define SENSOR_TYPE_FORMATTER2 0x3700
+#define GET_STATUS_3800 0x3800
+#define GET_STATUS_4000 0x4000
+#define GET_STATUS_4100 0x4100 /* Get */
+#define CTL_STATUS_4200 0x4200 /* [GS] 1 */
+
+/* Formatters for the Video Endpoint controls [GS]ET_EP_STREAM_CTL */
+#define VIDEO_OUTPUT_CONTROL_FORMATTER 0x0100
+
+static const char *size2name[PSZ_MAX] =
+{
+ "subQCIF",
+ "QSIF",
+ "QCIF",
+ "SIF",
+ "CIF",
+ "VGA",
+};
+
+/********/
+
+/* Entries for the Nala (645/646) camera; the Nala doesn't have compression
+ preferences, so you either get compressed or non-compressed streams.
+
+ An alternate value of 0 means this mode is not available at all.
+ */
+
+#define PWC_FPS_MAX_NALA 8
+
+struct Nala_table_entry {
+ char alternate; /* USB alternate setting */
+ int compressed; /* Compressed yes/no */
+
+ unsigned char mode[3]; /* precomputed mode table */
+};
+
+static unsigned int Nala_fps_vector[PWC_FPS_MAX_NALA] = { 4, 5, 7, 10, 12, 15, 20, 24 };
+
+static struct Nala_table_entry Nala_table[PSZ_MAX][PWC_FPS_MAX_NALA] =
+{
+#include "pwc-nala.h"
+};
+
+/****************************************************************************/
+
+static int recv_control_msg(struct pwc_device *pdev,
+ u8 request, u16 value, int recv_count)
+{
+ int rc;
+
+ rc = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
+ request,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, pdev->vcinterface,
+ pdev->ctrl_buf, recv_count, USB_CTRL_GET_TIMEOUT);
+ if (rc < 0)
+ PWC_ERROR("recv_control_msg error %d req %02x val %04x\n",
+ rc, request, value);
+ return rc;
+}
+
+static inline int send_video_command(struct pwc_device *pdev,
+ int index, const unsigned char *buf, int buflen)
+{
+ int rc;
+
+ memcpy(pdev->ctrl_buf, buf, buflen);
+
+ rc = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
+ SET_EP_STREAM_CTL,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ VIDEO_OUTPUT_CONTROL_FORMATTER, index,
+ pdev->ctrl_buf, buflen, USB_CTRL_SET_TIMEOUT);
+ if (rc >= 0)
+ memcpy(pdev->cmd_buf, buf, buflen);
+ else
+ PWC_ERROR("send_video_command error %d\n", rc);
+
+ return rc;
+}
+
+int send_control_msg(struct pwc_device *pdev,
+ u8 request, u16 value, void *buf, int buflen)
+{
+ return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
+ request,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, pdev->vcinterface,
+ buf, buflen, USB_CTRL_SET_TIMEOUT);
+}
+
+static int set_video_mode_Nala(struct pwc_device *pdev, int size, int pixfmt,
+ int frames, int *compression, int send_to_cam)
+{
+ int fps, ret = 0;
+ struct Nala_table_entry *pEntry;
+ int frames2frames[31] =
+ { /* closest match of framerate */
+ 0, 0, 0, 0, 4, /* 0-4 */
+ 5, 5, 7, 7, 10, /* 5-9 */
+ 10, 10, 12, 12, 15, /* 10-14 */
+ 15, 15, 15, 20, 20, /* 15-19 */
+ 20, 20, 20, 24, 24, /* 20-24 */
+ 24, 24, 24, 24, 24, /* 25-29 */
+ 24 /* 30 */
+ };
+ int frames2table[31] =
+ { 0, 0, 0, 0, 0, /* 0-4 */
+ 1, 1, 1, 2, 2, /* 5-9 */
+ 3, 3, 4, 4, 4, /* 10-14 */
+ 5, 5, 5, 5, 5, /* 15-19 */
+ 6, 6, 6, 6, 7, /* 20-24 */
+ 7, 7, 7, 7, 7, /* 25-29 */
+ 7 /* 30 */
+ };
+
+ if (size < 0 || size > PSZ_CIF)
+ return -EINVAL;
+ if (frames < 4)
+ frames = 4;
+ else if (frames > 25)
+ frames = 25;
+ frames = frames2frames[frames];
+ fps = frames2table[frames];
+ pEntry = &Nala_table[size][fps];
+ if (pEntry->alternate == 0)
+ return -EINVAL;
+
+ if (send_to_cam)
+ ret = send_video_command(pdev, pdev->vendpoint,
+ pEntry->mode, 3);
+ if (ret < 0)
+ return ret;
+
+ if (pEntry->compressed && pixfmt == V4L2_PIX_FMT_YUV420)
+ pwc_dec1_init(pdev, pEntry->mode);
+
+ /* Set various parameters */
+ pdev->pixfmt = pixfmt;
+ pdev->vframes = frames;
+ pdev->valternate = pEntry->alternate;
+ pdev->width = pwc_image_sizes[size][0];
+ pdev->height = pwc_image_sizes[size][1];
+ pdev->frame_size = (pdev->width * pdev->height * 3) / 2;
+ if (pEntry->compressed) {
+ if (pdev->release < 5) { /* 4 fold compression */
+ pdev->vbandlength = 528;
+ pdev->frame_size /= 4;
+ }
+ else {
+ pdev->vbandlength = 704;
+ pdev->frame_size /= 3;
+ }
+ }
+ else
+ pdev->vbandlength = 0;
+
+ /* Let pwc-if.c:isoc_init know we don't support higher compression */
+ *compression = 3;
+
+ return 0;
+}
+
+
+static int set_video_mode_Timon(struct pwc_device *pdev, int size, int pixfmt,
+ int frames, int *compression, int send_to_cam)
+{
+ const struct Timon_table_entry *pChoose;
+ int fps, ret = 0;
+
+ if (size >= PSZ_MAX || *compression < 0 || *compression > 3)
+ return -EINVAL;
+ if (frames < 5)
+ frames = 5;
+ else if (size == PSZ_VGA && frames > 15)
+ frames = 15;
+ else if (frames > 30)
+ frames = 30;
+ fps = (frames / 5) - 1;
+
+ /* Find a supported framerate with progressively higher compression */
+ pChoose = NULL;
+ while (*compression <= 3) {
+ pChoose = &Timon_table[size][fps][*compression];
+ if (pChoose->alternate != 0)
+ break;
+ (*compression)++;
+ }
+ if (pChoose == NULL || pChoose->alternate == 0)
+ return -ENOENT; /* Not supported. */
+
+ if (send_to_cam)
+ ret = send_video_command(pdev, pdev->vendpoint,
+ pChoose->mode, 13);
+ if (ret < 0)
+ return ret;
+
+ if (pChoose->bandlength > 0 && pixfmt == V4L2_PIX_FMT_YUV420)
+ pwc_dec23_init(pdev, pChoose->mode);
+
+ /* Set various parameters */
+ pdev->pixfmt = pixfmt;
+ pdev->vframes = (fps + 1) * 5;
+ pdev->valternate = pChoose->alternate;
+ pdev->width = pwc_image_sizes[size][0];
+ pdev->height = pwc_image_sizes[size][1];
+ pdev->vbandlength = pChoose->bandlength;
+ if (pChoose->bandlength > 0)
+ pdev->frame_size = (pChoose->bandlength * pdev->height) / 4;
+ else
+ pdev->frame_size = (pdev->width * pdev->height * 12) / 8;
+ return 0;
+}
+
+
+static int set_video_mode_Kiara(struct pwc_device *pdev, int size, int pixfmt,
+ int frames, int *compression, int send_to_cam)
+{
+ const struct Kiara_table_entry *pChoose = NULL;
+ int fps, ret = 0;
+
+ if (size >= PSZ_MAX || *compression < 0 || *compression > 3)
+ return -EINVAL;
+ if (frames < 5)
+ frames = 5;
+ else if (size == PSZ_VGA && frames > 15)
+ frames = 15;
+ else if (frames > 30)
+ frames = 30;
+ fps = (frames / 5) - 1;
+
+ /* Find a supported framerate with progressively higher compression */
+ while (*compression <= 3) {
+ pChoose = &Kiara_table[size][fps][*compression];
+ if (pChoose->alternate != 0)
+ break;
+ (*compression)++;
+ }
+ if (pChoose == NULL || pChoose->alternate == 0)
+ return -ENOENT; /* Not supported. */
+
+ /* Firmware bug: video endpoint is 5, but commands are sent to endpoint 4 */
+ if (send_to_cam)
+ ret = send_video_command(pdev, 4, pChoose->mode, 12);
+ if (ret < 0)
+ return ret;
+
+ if (pChoose->bandlength > 0 && pixfmt == V4L2_PIX_FMT_YUV420)
+ pwc_dec23_init(pdev, pChoose->mode);
+
+ /* All set and go */
+ pdev->pixfmt = pixfmt;
+ pdev->vframes = (fps + 1) * 5;
+ pdev->valternate = pChoose->alternate;
+ pdev->width = pwc_image_sizes[size][0];
+ pdev->height = pwc_image_sizes[size][1];
+ pdev->vbandlength = pChoose->bandlength;
+ if (pdev->vbandlength > 0)
+ pdev->frame_size = (pdev->vbandlength * pdev->height) / 4;
+ else
+ pdev->frame_size = (pdev->width * pdev->height * 12) / 8;
+ PWC_TRACE("frame_size=%d, vframes=%d, vsize=%d, vbandlength=%d\n",
+ pdev->frame_size, pdev->vframes, size, pdev->vbandlength);
+ return 0;
+}
+
+int pwc_set_video_mode(struct pwc_device *pdev, int width, int height,
+ int pixfmt, int frames, int *compression, int send_to_cam)
+{
+ int ret, size;
+
+ PWC_DEBUG_FLOW("set_video_mode(%dx%d @ %d, pixfmt %08x).\n",
+ width, height, frames, pixfmt);
+ size = pwc_get_size(pdev, width, height);
+ PWC_TRACE("decode_size = %d.\n", size);
+
+ if (DEVICE_USE_CODEC1(pdev->type)) {
+ ret = set_video_mode_Nala(pdev, size, pixfmt, frames,
+ compression, send_to_cam);
+ } else if (DEVICE_USE_CODEC3(pdev->type)) {
+ ret = set_video_mode_Kiara(pdev, size, pixfmt, frames,
+ compression, send_to_cam);
+ } else {
+ ret = set_video_mode_Timon(pdev, size, pixfmt, frames,
+ compression, send_to_cam);
+ }
+ if (ret < 0) {
+ PWC_ERROR("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret);
+ return ret;
+ }
+ pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size;
+ PWC_DEBUG_SIZE("Set resolution to %dx%d\n", pdev->width, pdev->height);
+ return 0;
+}
+
+static unsigned int pwc_get_fps_Nala(struct pwc_device *pdev, unsigned int index, unsigned int size)
+{
+ unsigned int i;
+
+ for (i = 0; i < PWC_FPS_MAX_NALA; i++) {
+ if (Nala_table[size][i].alternate) {
+ if (index--==0) return Nala_fps_vector[i];
+ }
+ }
+ return 0;
+}
+
+static unsigned int pwc_get_fps_Kiara(struct pwc_device *pdev, unsigned int index, unsigned int size)
+{
+ unsigned int i;
+
+ for (i = 0; i < PWC_FPS_MAX_KIARA; i++) {
+ if (Kiara_table[size][i][3].alternate) {
+ if (index--==0) return Kiara_fps_vector[i];
+ }
+ }
+ return 0;
+}
+
+static unsigned int pwc_get_fps_Timon(struct pwc_device *pdev, unsigned int index, unsigned int size)
+{
+ unsigned int i;
+
+ for (i=0; i < PWC_FPS_MAX_TIMON; i++) {
+ if (Timon_table[size][i][3].alternate) {
+ if (index--==0) return Timon_fps_vector[i];
+ }
+ }
+ return 0;
+}
+
+unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size)
+{
+ unsigned int ret;
+
+ if (DEVICE_USE_CODEC1(pdev->type)) {
+ ret = pwc_get_fps_Nala(pdev, index, size);
+
+ } else if (DEVICE_USE_CODEC3(pdev->type)) {
+ ret = pwc_get_fps_Kiara(pdev, index, size);
+
+ } else {
+ ret = pwc_get_fps_Timon(pdev, index, size);
+ }
+
+ return ret;
+}
+
+int pwc_get_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data)
+{
+ int ret;
+
+ ret = recv_control_msg(pdev, request, value, 1);
+ if (ret < 0)
+ return ret;
+
+ *data = pdev->ctrl_buf[0];
+ return 0;
+}
+
+int pwc_set_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, u8 data)
+{
+ int ret;
+
+ pdev->ctrl_buf[0] = data;
+ ret = send_control_msg(pdev, request, value, pdev->ctrl_buf, 1);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+int pwc_get_s8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data)
+{
+ int ret;
+
+ ret = recv_control_msg(pdev, request, value, 1);
+ if (ret < 0)
+ return ret;
+
+ *data = ((s8 *)pdev->ctrl_buf)[0];
+ return 0;
+}
+
+int pwc_get_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data)
+{
+ int ret;
+
+ ret = recv_control_msg(pdev, request, value, 2);
+ if (ret < 0)
+ return ret;
+
+ *data = (pdev->ctrl_buf[1] << 8) | pdev->ctrl_buf[0];
+ return 0;
+}
+
+int pwc_set_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, u16 data)
+{
+ int ret;
+
+ pdev->ctrl_buf[0] = data & 0xff;
+ pdev->ctrl_buf[1] = data >> 8;
+ ret = send_control_msg(pdev, request, value, pdev->ctrl_buf, 2);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+int pwc_button_ctrl(struct pwc_device *pdev, u16 value)
+{
+ int ret;
+
+ ret = send_control_msg(pdev, SET_STATUS_CTL, value, NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/* POWER */
+void pwc_camera_power(struct pwc_device *pdev, int power)
+{
+ int r;
+
+ if (!pdev->power_save)
+ return;
+
+ if (pdev->type < 675 || (pdev->type < 730 && pdev->release < 6))
+ return; /* Not supported by Nala or Timon < release 6 */
+
+ if (power)
+ pdev->ctrl_buf[0] = 0x00; /* active */
+ else
+ pdev->ctrl_buf[0] = 0xFF; /* power save */
+ r = send_control_msg(pdev, SET_STATUS_CTL,
+ SET_POWER_SAVE_MODE_FORMATTER, pdev->ctrl_buf, 1);
+ if (r < 0)
+ PWC_ERROR("Failed to power %s camera (%d)\n",
+ power ? "on" : "off", r);
+}
+
+int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value)
+{
+ int r;
+
+ if (pdev->type < 730)
+ return 0;
+ on_value /= 100;
+ off_value /= 100;
+ if (on_value < 0)
+ on_value = 0;
+ if (on_value > 0xff)
+ on_value = 0xff;
+ if (off_value < 0)
+ off_value = 0;
+ if (off_value > 0xff)
+ off_value = 0xff;
+
+ pdev->ctrl_buf[0] = on_value;
+ pdev->ctrl_buf[1] = off_value;
+
+ r = send_control_msg(pdev,
+ SET_STATUS_CTL, LED_FORMATTER, pdev->ctrl_buf, 2);
+ if (r < 0)
+ PWC_ERROR("Failed to set LED on/off time (%d)\n", r);
+
+ return r;
+}
+
+#ifdef CONFIG_USB_PWC_DEBUG
+int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor)
+{
+ int ret = -1, request;
+
+ if (pdev->type < 675)
+ request = SENSOR_TYPE_FORMATTER1;
+ else if (pdev->type < 730)
+ return -1; /* The Vesta series doesn't have this call */
+ else
+ request = SENSOR_TYPE_FORMATTER2;
+
+ ret = recv_control_msg(pdev, GET_STATUS_CTL, request, 1);
+ if (ret < 0)
+ return ret;
+ if (pdev->type < 675)
+ *sensor = pdev->ctrl_buf[0] | 0x100;
+ else
+ *sensor = pdev->ctrl_buf[0];
+ return 0;
+}
+#endif
diff --git a/drivers/media/video/pwc/pwc-dec1.c b/drivers/media/video/pwc/pwc-dec1.c
new file mode 100644
index 00000000..e899036a
--- /dev/null
+++ b/drivers/media/video/pwc/pwc-dec1.c
@@ -0,0 +1,32 @@
+/* Linux driver for Philips webcam
+ Decompression for chipset version 1
+ (C) 2004-2006 Luc Saillard (luc@saillard.org)
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+ The decompression routines have been implemented by reverse-engineering the
+ Nemosoft binary pwcx module. Caveat emptor.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+#include "pwc.h"
+
+void pwc_dec1_init(struct pwc_device *pdev, const unsigned char *cmd)
+{
+ struct pwc_dec1_private *pdec = &pdev->dec1;
+
+ pdec->version = pdev->release;
+}
diff --git a/drivers/media/video/pwc/pwc-dec1.h b/drivers/media/video/pwc/pwc-dec1.h
new file mode 100644
index 00000000..c565ef8f
--- /dev/null
+++ b/drivers/media/video/pwc/pwc-dec1.h
@@ -0,0 +1,39 @@
+/* Linux driver for Philips webcam
+ (C) 2004-2006 Luc Saillard (luc@saillard.org)
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+ The decompression routines have been implemented by reverse-engineering the
+ Nemosoft binary pwcx module. Caveat emptor.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef PWC_DEC1_H
+#define PWC_DEC1_H
+
+#include <linux/mutex.h>
+
+struct pwc_device;
+
+struct pwc_dec1_private
+{
+ int version;
+};
+
+void pwc_dec1_init(struct pwc_device *pdev, const unsigned char *cmd);
+
+#endif
diff --git a/drivers/media/video/pwc/pwc-dec23.c b/drivers/media/video/pwc/pwc-dec23.c
new file mode 100644
index 00000000..3792fedf
--- /dev/null
+++ b/drivers/media/video/pwc/pwc-dec23.c
@@ -0,0 +1,691 @@
+/* Linux driver for Philips webcam
+ Decompression for chipset version 2 et 3
+ (C) 2004-2006 Luc Saillard (luc@saillard.org)
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+ The decompression routines have been implemented by reverse-engineering the
+ Nemosoft binary pwcx module. Caveat emptor.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+#include "pwc-timon.h"
+#include "pwc-kiara.h"
+#include "pwc-dec23.h"
+
+#include <linux/string.h>
+#include <linux/slab.h>
+
+/*
+ * USE_LOOKUP_TABLE_TO_CLAMP
+ * 0: use a C version of this tests: { a<0?0:(a>255?255:a) }
+ * 1: use a faster lookup table for cpu with a big cache (intel)
+ */
+#define USE_LOOKUP_TABLE_TO_CLAMP 1
+/*
+ * UNROLL_LOOP_FOR_COPYING_BLOCK
+ * 0: use a loop for a smaller code (but little slower)
+ * 1: when unrolling the loop, gcc produces some faster code (perhaps only
+ * valid for intel processor class). Activating this option, automaticaly
+ * activate USE_LOOKUP_TABLE_TO_CLAMP
+ */
+#define UNROLL_LOOP_FOR_COPY 1
+#if UNROLL_LOOP_FOR_COPY
+# undef USE_LOOKUP_TABLE_TO_CLAMP
+# define USE_LOOKUP_TABLE_TO_CLAMP 1
+#endif
+
+static void build_subblock_pattern(struct pwc_dec23_private *pdec)
+{
+ static const unsigned int initial_values[12] = {
+ -0x526500, -0x221200, 0x221200, 0x526500,
+ -0x3de200, 0x3de200,
+ -0x6db480, -0x2d5d00, 0x2d5d00, 0x6db480,
+ -0x12c200, 0x12c200
+
+ };
+ static const unsigned int values_derivated[12] = {
+ 0xa4ca, 0x4424, -0x4424, -0xa4ca,
+ 0x7bc4, -0x7bc4,
+ 0xdb69, 0x5aba, -0x5aba, -0xdb69,
+ 0x2584, -0x2584
+ };
+ unsigned int temp_values[12];
+ int i, j;
+
+ memcpy(temp_values, initial_values, sizeof(initial_values));
+ for (i = 0; i < 256; i++) {
+ for (j = 0; j < 12; j++) {
+ pdec->table_subblock[i][j] = temp_values[j];
+ temp_values[j] += values_derivated[j];
+ }
+ }
+}
+
+static void build_bit_powermask_table(struct pwc_dec23_private *pdec)
+{
+ unsigned char *p;
+ unsigned int bit, byte, mask, val;
+ unsigned int bitpower = 1;
+
+ for (bit = 0; bit < 8; bit++) {
+ mask = bitpower - 1;
+ p = pdec->table_bitpowermask[bit];
+ for (byte = 0; byte < 256; byte++) {
+ val = (byte & mask);
+ if (byte & bitpower)
+ val = -val;
+ *p++ = val;
+ }
+ bitpower<<=1;
+ }
+}
+
+
+static void build_table_color(const unsigned int romtable[16][8],
+ unsigned char p0004[16][1024],
+ unsigned char p8004[16][256])
+{
+ int compression_mode, j, k, bit, pw;
+ unsigned char *p0, *p8;
+ const unsigned int *r;
+
+ /* We have 16 compressions tables */
+ for (compression_mode = 0; compression_mode < 16; compression_mode++) {
+ p0 = p0004[compression_mode];
+ p8 = p8004[compression_mode];
+ r = romtable[compression_mode];
+
+ for (j = 0; j < 8; j++, r++, p0 += 128) {
+
+ for (k = 0; k < 16; k++) {
+ if (k == 0)
+ bit = 1;
+ else if (k >= 1 && k < 3)
+ bit = (r[0] >> 15) & 7;
+ else if (k >= 3 && k < 6)
+ bit = (r[0] >> 12) & 7;
+ else if (k >= 6 && k < 10)
+ bit = (r[0] >> 9) & 7;
+ else if (k >= 10 && k < 13)
+ bit = (r[0] >> 6) & 7;
+ else if (k >= 13 && k < 15)
+ bit = (r[0] >> 3) & 7;
+ else
+ bit = (r[0]) & 7;
+ if (k == 0)
+ *p8++ = 8;
+ else
+ *p8++ = j - bit;
+ *p8++ = bit;
+
+ pw = 1 << bit;
+ p0[k + 0x00] = (1 * pw) + 0x80;
+ p0[k + 0x10] = (2 * pw) + 0x80;
+ p0[k + 0x20] = (3 * pw) + 0x80;
+ p0[k + 0x30] = (4 * pw) + 0x80;
+ p0[k + 0x40] = (-1 * pw) + 0x80;
+ p0[k + 0x50] = (-2 * pw) + 0x80;
+ p0[k + 0x60] = (-3 * pw) + 0x80;
+ p0[k + 0x70] = (-4 * pw) + 0x80;
+ } /* end of for (k=0; k<16; k++, p8++) */
+ } /* end of for (j=0; j<8; j++ , table++) */
+ } /* end of foreach compression_mode */
+}
+
+/*
+ *
+ */
+static void fill_table_dc00_d800(struct pwc_dec23_private *pdec)
+{
+#define SCALEBITS 15
+#define ONE_HALF (1UL << (SCALEBITS - 1))
+ int i;
+ unsigned int offset1 = ONE_HALF;
+ unsigned int offset2 = 0x0000;
+
+ for (i=0; i<256; i++) {
+ pdec->table_dc00[i] = offset1 & ~(ONE_HALF);
+ pdec->table_d800[i] = offset2;
+
+ offset1 += 0x7bc4;
+ offset2 += 0x7bc4;
+ }
+}
+
+/*
+ * To decode the stream:
+ * if look_bits(2) == 0: # op == 2 in the lookup table
+ * skip_bits(2)
+ * end of the stream
+ * elif look_bits(3) == 7: # op == 1 in the lookup table
+ * skip_bits(3)
+ * yyyy = get_bits(4)
+ * xxxx = get_bits(8)
+ * else: # op == 0 in the lookup table
+ * skip_bits(x)
+ *
+ * For speedup processing, we build a lookup table and we takes the first 6 bits.
+ *
+ * struct {
+ * unsigned char op; // operation to execute
+ * unsigned char bits; // bits use to perform operation
+ * unsigned char offset1; // offset to add to access in the table_0004 % 16
+ * unsigned char offset2; // offset to add to access in the table_0004
+ * }
+ *
+ * How to build this table ?
+ * op == 2 when (i%4)==0
+ * op == 1 when (i%8)==7
+ * op == 0 otherwise
+ *
+ */
+static const unsigned char hash_table_ops[64*4] = {
+ 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x01, 0x00,
+ 0x00, 0x04, 0x01, 0x10,
+ 0x00, 0x06, 0x01, 0x30,
+ 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x01, 0x40,
+ 0x00, 0x05, 0x01, 0x20,
+ 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x01, 0x00,
+ 0x00, 0x04, 0x01, 0x50,
+ 0x00, 0x05, 0x02, 0x00,
+ 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x01, 0x40,
+ 0x00, 0x05, 0x03, 0x00,
+ 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x01, 0x00,
+ 0x00, 0x04, 0x01, 0x10,
+ 0x00, 0x06, 0x02, 0x10,
+ 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x01, 0x40,
+ 0x00, 0x05, 0x01, 0x60,
+ 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x01, 0x00,
+ 0x00, 0x04, 0x01, 0x50,
+ 0x00, 0x05, 0x02, 0x40,
+ 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x01, 0x40,
+ 0x00, 0x05, 0x03, 0x40,
+ 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x01, 0x00,
+ 0x00, 0x04, 0x01, 0x10,
+ 0x00, 0x06, 0x01, 0x70,
+ 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x01, 0x40,
+ 0x00, 0x05, 0x01, 0x20,
+ 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x01, 0x00,
+ 0x00, 0x04, 0x01, 0x50,
+ 0x00, 0x05, 0x02, 0x00,
+ 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x01, 0x40,
+ 0x00, 0x05, 0x03, 0x00,
+ 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x01, 0x00,
+ 0x00, 0x04, 0x01, 0x10,
+ 0x00, 0x06, 0x02, 0x50,
+ 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x01, 0x40,
+ 0x00, 0x05, 0x01, 0x60,
+ 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x01, 0x00,
+ 0x00, 0x04, 0x01, 0x50,
+ 0x00, 0x05, 0x02, 0x40,
+ 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x01, 0x40,
+ 0x00, 0x05, 0x03, 0x40,
+ 0x01, 0x00, 0x00, 0x00
+};
+
+/*
+ *
+ */
+static const unsigned int MulIdx[16][16] = {
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},
+ {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3,},
+ {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3,},
+ {4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4,},
+ {6, 7, 8, 9, 7, 10, 11, 8, 8, 11, 10, 7, 9, 8, 7, 6,},
+ {4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 4,},
+ {1, 3, 0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3, 0, 2,},
+ {0, 3, 3, 0, 1, 2, 2, 1, 2, 1, 1, 2, 3, 0, 0, 3,},
+ {0, 1, 2, 3, 3, 2, 1, 0, 3, 2, 1, 0, 0, 1, 2, 3,},
+ {1, 1, 1, 1, 3, 3, 3, 3, 0, 0, 0, 0, 2, 2, 2, 2,},
+ {7, 10, 11, 8, 9, 8, 7, 6, 6, 7, 8, 9, 8, 11, 10, 7,},
+ {4, 5, 5, 4, 5, 4, 4, 5, 5, 4, 4, 5, 4, 5, 5, 4,},
+ {7, 9, 6, 8, 10, 8, 7, 11, 11, 7, 8, 10, 8, 6, 9, 7,},
+ {1, 3, 0, 2, 2, 0, 3, 1, 2, 0, 3, 1, 1, 3, 0, 2,},
+ {1, 2, 2, 1, 3, 0, 0, 3, 0, 3, 3, 0, 2, 1, 1, 2,},
+ {10, 8, 7, 11, 8, 6, 9, 7, 7, 9, 6, 8, 11, 7, 8, 10}
+};
+
+#if USE_LOOKUP_TABLE_TO_CLAMP
+#define MAX_OUTER_CROP_VALUE (512)
+static unsigned char pwc_crop_table[256 + 2*MAX_OUTER_CROP_VALUE];
+#define CLAMP(x) (pwc_crop_table[MAX_OUTER_CROP_VALUE+(x)])
+#else
+#define CLAMP(x) ((x)>255?255:((x)<0?0:x))
+#endif
+
+
+/* If the type or the command change, we rebuild the lookup table */
+void pwc_dec23_init(struct pwc_device *pdev, const unsigned char *cmd)
+{
+ int flags, version, shift, i;
+ struct pwc_dec23_private *pdec = &pdev->dec23;
+
+ mutex_init(&pdec->lock);
+
+ if (pdec->last_cmd_valid && pdec->last_cmd == cmd[2])
+ return;
+
+ if (DEVICE_USE_CODEC3(pdev->type)) {
+ flags = cmd[2] & 0x18;
+ if (flags == 8)
+ pdec->nbits = 7; /* More bits, mean more bits to encode the stream, but better quality */
+ else if (flags == 0x10)
+ pdec->nbits = 8;
+ else
+ pdec->nbits = 6;
+
+ version = cmd[2] >> 5;
+ build_table_color(KiaraRomTable[version][0], pdec->table_0004_pass1, pdec->table_8004_pass1);
+ build_table_color(KiaraRomTable[version][1], pdec->table_0004_pass2, pdec->table_8004_pass2);
+
+ } else {
+
+ flags = cmd[2] & 6;
+ if (flags == 2)
+ pdec->nbits = 7;
+ else if (flags == 4)
+ pdec->nbits = 8;
+ else
+ pdec->nbits = 6;
+
+ version = cmd[2] >> 3;
+ build_table_color(TimonRomTable[version][0], pdec->table_0004_pass1, pdec->table_8004_pass1);
+ build_table_color(TimonRomTable[version][1], pdec->table_0004_pass2, pdec->table_8004_pass2);
+ }
+
+ /* Informations can be coded on a variable number of bits but never less than 8 */
+ shift = 8 - pdec->nbits;
+ pdec->scalebits = SCALEBITS - shift;
+ pdec->nbitsmask = 0xFF >> shift;
+
+ fill_table_dc00_d800(pdec);
+ build_subblock_pattern(pdec);
+ build_bit_powermask_table(pdec);
+
+#if USE_LOOKUP_TABLE_TO_CLAMP
+ /* Build the static table to clamp value [0-255] */
+ for (i=0;i<MAX_OUTER_CROP_VALUE;i++)
+ pwc_crop_table[i] = 0;
+ for (i=0; i<256; i++)
+ pwc_crop_table[MAX_OUTER_CROP_VALUE+i] = i;
+ for (i=0; i<MAX_OUTER_CROP_VALUE; i++)
+ pwc_crop_table[MAX_OUTER_CROP_VALUE+256+i] = 255;
+#endif
+
+ pdec->last_cmd = cmd[2];
+ pdec->last_cmd_valid = 1;
+}
+
+/*
+ * Copy the 4x4 image block to Y plane buffer
+ */
+static void copy_image_block_Y(const int *src, unsigned char *dst, unsigned int bytes_per_line, unsigned int scalebits)
+{
+#if UNROLL_LOOP_FOR_COPY
+ const unsigned char *cm = pwc_crop_table+MAX_OUTER_CROP_VALUE;
+ const int *c = src;
+ unsigned char *d = dst;
+
+ *d++ = cm[c[0] >> scalebits];
+ *d++ = cm[c[1] >> scalebits];
+ *d++ = cm[c[2] >> scalebits];
+ *d++ = cm[c[3] >> scalebits];
+
+ d = dst + bytes_per_line;
+ *d++ = cm[c[4] >> scalebits];
+ *d++ = cm[c[5] >> scalebits];
+ *d++ = cm[c[6] >> scalebits];
+ *d++ = cm[c[7] >> scalebits];
+
+ d = dst + bytes_per_line*2;
+ *d++ = cm[c[8] >> scalebits];
+ *d++ = cm[c[9] >> scalebits];
+ *d++ = cm[c[10] >> scalebits];
+ *d++ = cm[c[11] >> scalebits];
+
+ d = dst + bytes_per_line*3;
+ *d++ = cm[c[12] >> scalebits];
+ *d++ = cm[c[13] >> scalebits];
+ *d++ = cm[c[14] >> scalebits];
+ *d++ = cm[c[15] >> scalebits];
+#else
+ int i;
+ const int *c = src;
+ unsigned char *d = dst;
+ for (i = 0; i < 4; i++, c++)
+ *d++ = CLAMP((*c) >> scalebits);
+
+ d = dst + bytes_per_line;
+ for (i = 0; i < 4; i++, c++)
+ *d++ = CLAMP((*c) >> scalebits);
+
+ d = dst + bytes_per_line*2;
+ for (i = 0; i < 4; i++, c++)
+ *d++ = CLAMP((*c) >> scalebits);
+
+ d = dst + bytes_per_line*3;
+ for (i = 0; i < 4; i++, c++)
+ *d++ = CLAMP((*c) >> scalebits);
+#endif
+}
+
+/*
+ * Copy the 4x4 image block to a CrCb plane buffer
+ *
+ */
+static void copy_image_block_CrCb(const int *src, unsigned char *dst, unsigned int bytes_per_line, unsigned int scalebits)
+{
+#if UNROLL_LOOP_FOR_COPY
+ /* Unroll all loops */
+ const unsigned char *cm = pwc_crop_table+MAX_OUTER_CROP_VALUE;
+ const int *c = src;
+ unsigned char *d = dst;
+
+ *d++ = cm[c[0] >> scalebits];
+ *d++ = cm[c[4] >> scalebits];
+ *d++ = cm[c[1] >> scalebits];
+ *d++ = cm[c[5] >> scalebits];
+ *d++ = cm[c[2] >> scalebits];
+ *d++ = cm[c[6] >> scalebits];
+ *d++ = cm[c[3] >> scalebits];
+ *d++ = cm[c[7] >> scalebits];
+
+ d = dst + bytes_per_line;
+ *d++ = cm[c[12] >> scalebits];
+ *d++ = cm[c[8] >> scalebits];
+ *d++ = cm[c[13] >> scalebits];
+ *d++ = cm[c[9] >> scalebits];
+ *d++ = cm[c[14] >> scalebits];
+ *d++ = cm[c[10] >> scalebits];
+ *d++ = cm[c[15] >> scalebits];
+ *d++ = cm[c[11] >> scalebits];
+#else
+ int i;
+ const int *c1 = src;
+ const int *c2 = src + 4;
+ unsigned char *d = dst;
+
+ for (i = 0; i < 4; i++, c1++, c2++) {
+ *d++ = CLAMP((*c1) >> scalebits);
+ *d++ = CLAMP((*c2) >> scalebits);
+ }
+ c1 = src + 12;
+ d = dst + bytes_per_line;
+ for (i = 0; i < 4; i++, c1++, c2++) {
+ *d++ = CLAMP((*c1) >> scalebits);
+ *d++ = CLAMP((*c2) >> scalebits);
+ }
+#endif
+}
+
+/*
+ * To manage the stream, we keep bits in a 32 bits register.
+ * fill_nbits(n): fill the reservoir with at least n bits
+ * skip_bits(n): discard n bits from the reservoir
+ * get_bits(n): fill the reservoir, returns the first n bits and discard the
+ * bits from the reservoir.
+ * __get_nbits(n): faster version of get_bits(n), but asumes that the reservoir
+ * contains at least n bits. bits returned is discarded.
+ */
+#define fill_nbits(pdec, nbits_wanted) do { \
+ while (pdec->nbits_in_reservoir<(nbits_wanted)) \
+ { \
+ pdec->reservoir |= (*(pdec->stream)++) << (pdec->nbits_in_reservoir); \
+ pdec->nbits_in_reservoir += 8; \
+ } \
+} while(0);
+
+#define skip_nbits(pdec, nbits_to_skip) do { \
+ pdec->reservoir >>= (nbits_to_skip); \
+ pdec->nbits_in_reservoir -= (nbits_to_skip); \
+} while(0);
+
+#define get_nbits(pdec, nbits_wanted, result) do { \
+ fill_nbits(pdec, nbits_wanted); \
+ result = (pdec->reservoir) & ((1U<<(nbits_wanted))-1); \
+ skip_nbits(pdec, nbits_wanted); \
+} while(0);
+
+#define __get_nbits(pdec, nbits_wanted, result) do { \
+ result = (pdec->reservoir) & ((1U<<(nbits_wanted))-1); \
+ skip_nbits(pdec, nbits_wanted); \
+} while(0);
+
+#define look_nbits(pdec, nbits_wanted) \
+ ((pdec->reservoir) & ((1U<<(nbits_wanted))-1))
+
+/*
+ * Decode a 4x4 pixel block
+ */
+static void decode_block(struct pwc_dec23_private *pdec,
+ const unsigned char *ptable0004,
+ const unsigned char *ptable8004)
+{
+ unsigned int primary_color;
+ unsigned int channel_v, offset1, op;
+ int i;
+
+ fill_nbits(pdec, 16);
+ __get_nbits(pdec, pdec->nbits, primary_color);
+
+ if (look_nbits(pdec,2) == 0) {
+ skip_nbits(pdec, 2);
+ /* Very simple, the color is the same for all pixels of the square */
+ for (i = 0; i < 16; i++)
+ pdec->temp_colors[i] = pdec->table_dc00[primary_color];
+
+ return;
+ }
+
+ /* This block is encoded with small pattern */
+ for (i = 0; i < 16; i++)
+ pdec->temp_colors[i] = pdec->table_d800[primary_color];
+
+ __get_nbits(pdec, 3, channel_v);
+ channel_v = ((channel_v & 1) << 2) | (channel_v & 2) | ((channel_v & 4) >> 2);
+
+ ptable0004 += (channel_v * 128);
+ ptable8004 += (channel_v * 32);
+
+ offset1 = 0;
+ do
+ {
+ unsigned int htable_idx, rows = 0;
+ const unsigned int *block;
+
+ /* [ zzzz y x x ]
+ * xx == 00 :=> end of the block def, remove the two bits from the stream
+ * yxx == 111
+ * yxx == any other value
+ *
+ */
+ fill_nbits(pdec, 16);
+ htable_idx = look_nbits(pdec, 6);
+ op = hash_table_ops[htable_idx * 4];
+
+ if (op == 2) {
+ skip_nbits(pdec, 2);
+
+ } else if (op == 1) {
+ /* 15bits [ xxxx xxxx yyyy 111 ]
+ * yyy => offset in the table8004
+ * xxx => offset in the tabled004 (tree)
+ */
+ unsigned int mask, shift;
+ unsigned int nbits, col1;
+ unsigned int yyyy;
+
+ skip_nbits(pdec, 3);
+ /* offset1 += yyyy */
+ __get_nbits(pdec, 4, yyyy);
+ offset1 += 1 + yyyy;
+ offset1 &= 0x0F;
+ nbits = ptable8004[offset1 * 2];
+
+ /* col1 = xxxx xxxx */
+ __get_nbits(pdec, nbits+1, col1);
+
+ /* Bit mask table */
+ mask = pdec->table_bitpowermask[nbits][col1];
+ shift = ptable8004[offset1 * 2 + 1];
+ rows = ((mask << shift) + 0x80) & 0xFF;
+
+ block = pdec->table_subblock[rows];
+ for (i = 0; i < 16; i++)
+ pdec->temp_colors[i] += block[MulIdx[offset1][i]];
+
+ } else {
+ /* op == 0
+ * offset1 is coded on 3 bits
+ */
+ unsigned int shift;
+
+ offset1 += hash_table_ops [htable_idx * 4 + 2];
+ offset1 &= 0x0F;
+
+ rows = ptable0004[offset1 + hash_table_ops [htable_idx * 4 + 3]];
+ block = pdec->table_subblock[rows];
+ for (i = 0; i < 16; i++)
+ pdec->temp_colors[i] += block[MulIdx[offset1][i]];
+
+ shift = hash_table_ops[htable_idx * 4 + 1];
+ skip_nbits(pdec, shift);
+ }
+
+ } while (op != 2);
+
+}
+
+static void DecompressBand23(struct pwc_dec23_private *pdec,
+ const unsigned char *rawyuv,
+ unsigned char *planar_y,
+ unsigned char *planar_u,
+ unsigned char *planar_v,
+ unsigned int compressed_image_width,
+ unsigned int real_image_width)
+{
+ int compression_index, nblocks;
+ const unsigned char *ptable0004;
+ const unsigned char *ptable8004;
+
+ pdec->reservoir = 0;
+ pdec->nbits_in_reservoir = 0;
+ pdec->stream = rawyuv + 1; /* The first byte of the stream is skipped */
+
+ get_nbits(pdec, 4, compression_index);
+
+ /* pass 1: uncompress Y component */
+ nblocks = compressed_image_width / 4;
+
+ ptable0004 = pdec->table_0004_pass1[compression_index];
+ ptable8004 = pdec->table_8004_pass1[compression_index];
+
+ /* Each block decode a square of 4x4 */
+ while (nblocks) {
+ decode_block(pdec, ptable0004, ptable8004);
+ copy_image_block_Y(pdec->temp_colors, planar_y, real_image_width, pdec->scalebits);
+ planar_y += 4;
+ nblocks--;
+ }
+
+ /* pass 2: uncompress UV component */
+ nblocks = compressed_image_width / 8;
+
+ ptable0004 = pdec->table_0004_pass2[compression_index];
+ ptable8004 = pdec->table_8004_pass2[compression_index];
+
+ /* Each block decode a square of 4x4 */
+ while (nblocks) {
+ decode_block(pdec, ptable0004, ptable8004);
+ copy_image_block_CrCb(pdec->temp_colors, planar_u, real_image_width/2, pdec->scalebits);
+
+ decode_block(pdec, ptable0004, ptable8004);
+ copy_image_block_CrCb(pdec->temp_colors, planar_v, real_image_width/2, pdec->scalebits);
+
+ planar_v += 8;
+ planar_u += 8;
+ nblocks -= 2;
+ }
+
+}
+
+/**
+ *
+ * Uncompress a pwc23 buffer.
+ *
+ * src: raw data
+ * dst: image output
+ */
+void pwc_dec23_decompress(struct pwc_device *pdev,
+ const void *src,
+ void *dst)
+{
+ int bandlines_left, bytes_per_block;
+ struct pwc_dec23_private *pdec = &pdev->dec23;
+
+ /* YUV420P image format */
+ unsigned char *pout_planar_y;
+ unsigned char *pout_planar_u;
+ unsigned char *pout_planar_v;
+ unsigned int plane_size;
+
+ mutex_lock(&pdec->lock);
+
+ bandlines_left = pdev->height / 4;
+ bytes_per_block = pdev->width * 4;
+ plane_size = pdev->height * pdev->width;
+
+ pout_planar_y = dst;
+ pout_planar_u = dst + plane_size;
+ pout_planar_v = dst + plane_size + plane_size / 4;
+
+ while (bandlines_left--) {
+ DecompressBand23(pdec, src,
+ pout_planar_y, pout_planar_u, pout_planar_v,
+ pdev->width, pdev->width);
+ src += pdev->vbandlength;
+ pout_planar_y += bytes_per_block;
+ pout_planar_u += pdev->width;
+ pout_planar_v += pdev->width;
+ }
+ mutex_unlock(&pdec->lock);
+}
diff --git a/drivers/media/video/pwc/pwc-dec23.h b/drivers/media/video/pwc/pwc-dec23.h
new file mode 100644
index 00000000..c655b1c1
--- /dev/null
+++ b/drivers/media/video/pwc/pwc-dec23.h
@@ -0,0 +1,61 @@
+/* Linux driver for Philips webcam
+ (C) 2004-2006 Luc Saillard (luc@saillard.org)
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+ The decompression routines have been implemented by reverse-engineering the
+ Nemosoft binary pwcx module. Caveat emptor.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef PWC_DEC23_H
+#define PWC_DEC23_H
+
+struct pwc_device;
+
+struct pwc_dec23_private
+{
+ struct mutex lock;
+
+ unsigned char last_cmd, last_cmd_valid;
+
+ unsigned int scalebits;
+ unsigned int nbitsmask, nbits; /* Number of bits of a color in the compressed stream */
+
+ unsigned int reservoir;
+ unsigned int nbits_in_reservoir;
+
+ const unsigned char *stream;
+ int temp_colors[16];
+
+ unsigned char table_0004_pass1[16][1024];
+ unsigned char table_0004_pass2[16][1024];
+ unsigned char table_8004_pass1[16][256];
+ unsigned char table_8004_pass2[16][256];
+ unsigned int table_subblock[256][12];
+
+ unsigned char table_bitpowermask[8][256];
+ unsigned int table_d800[256];
+ unsigned int table_dc00[256];
+
+};
+
+void pwc_dec23_init(struct pwc_device *pdev, const unsigned char *cmd);
+void pwc_dec23_decompress(struct pwc_device *pdev,
+ const void *src,
+ void *dst);
+#endif
diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c
new file mode 100644
index 00000000..122fbd00
--- /dev/null
+++ b/drivers/media/video/pwc/pwc-if.c
@@ -0,0 +1,1243 @@
+/* Linux driver for Philips webcam
+ USB and Video4Linux interface part.
+ (C) 1999-2004 Nemosoft Unv.
+ (C) 2004-2006 Luc Saillard (luc@saillard.org)
+ (C) 2011 Hans de Goede <hdegoede@redhat.com>
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+ The decompression routines have been implemented by reverse-engineering the
+ Nemosoft binary pwcx module. Caveat emptor.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+/*
+ This code forms the interface between the USB layers and the Philips
+ specific stuff. Some adanved stuff of the driver falls under an
+ NDA, signed between me and Philips B.V., Eindhoven, the Netherlands, and
+ is thus not distributed in source form. The binary pwcx.o module
+ contains the code that falls under the NDA.
+
+ In case you're wondering: 'pwc' stands for "Philips WebCam", but
+ I really didn't want to type 'philips_web_cam' every time (I'm lazy as
+ any Linux kernel hacker, but I don't like uncomprehensible abbreviations
+ without explanation).
+
+ Oh yes, convention: to disctinguish between all the various pointers to
+ device-structures, I use these names for the pointer variables:
+ udev: struct usb_device *
+ vdev: struct video_device (member of pwc_dev)
+ pdev: struct pwc_devive *
+*/
+
+/* Contributors:
+ - Alvarado: adding whitebalance code
+ - Alistar Moire: QuickCam 3000 Pro device/product ID
+ - Tony Hoyle: Creative Labs Webcam 5 device/product ID
+ - Mark Burazin: solving hang in VIDIOCSYNC when camera gets unplugged
+ - Jk Fang: Sotec Afina Eye ID
+ - Xavier Roche: QuickCam Pro 4000 ID
+ - Jens Knudsen: QuickCam Zoom ID
+ - J. Debert: QuickCam for Notebooks ID
+ - Pham Thanh Nam: webcam snapshot button as an event input device
+*/
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#ifdef CONFIG_USB_PWC_INPUT_EVDEV
+#include <linux/usb/input.h>
+#endif
+#include <linux/vmalloc.h>
+#include <asm/io.h>
+#include <linux/kernel.h> /* simple_strtol() */
+
+#include "pwc.h"
+#include "pwc-kiara.h"
+#include "pwc-timon.h"
+#include "pwc-dec23.h"
+#include "pwc-dec1.h"
+
+/* Function prototypes and driver templates */
+
+/* hotplug device table support */
+static const struct usb_device_id pwc_device_table [] = {
+ { USB_DEVICE(0x0471, 0x0302) }, /* Philips models */
+ { USB_DEVICE(0x0471, 0x0303) },
+ { USB_DEVICE(0x0471, 0x0304) },
+ { USB_DEVICE(0x0471, 0x0307) },
+ { USB_DEVICE(0x0471, 0x0308) },
+ { USB_DEVICE(0x0471, 0x030C) },
+ { USB_DEVICE(0x0471, 0x0310) },
+ { USB_DEVICE(0x0471, 0x0311) }, /* Philips ToUcam PRO II */
+ { USB_DEVICE(0x0471, 0x0312) },
+ { USB_DEVICE(0x0471, 0x0313) }, /* the 'new' 720K */
+ { USB_DEVICE(0x0471, 0x0329) }, /* Philips SPC 900NC PC Camera */
+ { USB_DEVICE(0x069A, 0x0001) }, /* Askey */
+ { USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam Pro 3000 */
+ { USB_DEVICE(0x046D, 0x08B1) }, /* Logitech QuickCam Notebook Pro */
+ { USB_DEVICE(0x046D, 0x08B2) }, /* Logitech QuickCam Pro 4000 */
+ { USB_DEVICE(0x046D, 0x08B3) }, /* Logitech QuickCam Zoom (old model) */
+ { USB_DEVICE(0x046D, 0x08B4) }, /* Logitech QuickCam Zoom (new model) */
+ { USB_DEVICE(0x046D, 0x08B5) }, /* Logitech QuickCam Orbit/Sphere */
+ { USB_DEVICE(0x046D, 0x08B6) }, /* Cisco VT Camera */
+ { USB_DEVICE(0x046D, 0x08B7) }, /* Logitech ViewPort AV 100 */
+ { USB_DEVICE(0x046D, 0x08B8) }, /* Logitech (reserved) */
+ { USB_DEVICE(0x055D, 0x9000) }, /* Samsung MPC-C10 */
+ { USB_DEVICE(0x055D, 0x9001) }, /* Samsung MPC-C30 */
+ { USB_DEVICE(0x055D, 0x9002) }, /* Samsung SNC-35E (Ver3.0) */
+ { USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */
+ { USB_DEVICE(0x041E, 0x4011) }, /* Creative Webcam Pro Ex */
+ { USB_DEVICE(0x04CC, 0x8116) }, /* Afina Eye */
+ { USB_DEVICE(0x06BE, 0x8116) }, /* new Afina Eye */
+ { USB_DEVICE(0x0d81, 0x1910) }, /* Visionite */
+ { USB_DEVICE(0x0d81, 0x1900) },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, pwc_device_table);
+
+static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id);
+static void usb_pwc_disconnect(struct usb_interface *intf);
+static void pwc_isoc_cleanup(struct pwc_device *pdev);
+
+static struct usb_driver pwc_driver = {
+ .name = "Philips webcam", /* name */
+ .id_table = pwc_device_table,
+ .probe = usb_pwc_probe, /* probe() */
+ .disconnect = usb_pwc_disconnect, /* disconnect() */
+};
+
+#define MAX_DEV_HINTS 20
+#define MAX_ISOC_ERRORS 20
+
+#ifdef CONFIG_USB_PWC_DEBUG
+ int pwc_trace = PWC_DEBUG_LEVEL;
+#endif
+static int power_save = -1;
+static int leds[2] = { 100, 0 };
+
+/***/
+
+static int pwc_video_close(struct file *file);
+static ssize_t pwc_video_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos);
+static unsigned int pwc_video_poll(struct file *file, poll_table *wait);
+static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma);
+
+static const struct v4l2_file_operations pwc_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = pwc_video_close,
+ .read = pwc_video_read,
+ .poll = pwc_video_poll,
+ .mmap = pwc_video_mmap,
+ .unlocked_ioctl = video_ioctl2,
+};
+static struct video_device pwc_template = {
+ .name = "Philips Webcam", /* Filled in later */
+ .release = video_device_release_empty,
+ .fops = &pwc_fops,
+ .ioctl_ops = &pwc_ioctl_ops,
+};
+
+/***************************************************************************/
+/* Private functions */
+
+struct pwc_frame_buf *pwc_get_next_fill_buf(struct pwc_device *pdev)
+{
+ unsigned long flags = 0;
+ struct pwc_frame_buf *buf = NULL;
+
+ spin_lock_irqsave(&pdev->queued_bufs_lock, flags);
+ if (list_empty(&pdev->queued_bufs))
+ goto leave;
+
+ buf = list_entry(pdev->queued_bufs.next, struct pwc_frame_buf, list);
+ list_del(&buf->list);
+leave:
+ spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
+ return buf;
+}
+
+static void pwc_snapshot_button(struct pwc_device *pdev, int down)
+{
+ if (down) {
+ PWC_TRACE("Snapshot button pressed.\n");
+ } else {
+ PWC_TRACE("Snapshot button released.\n");
+ }
+
+#ifdef CONFIG_USB_PWC_INPUT_EVDEV
+ if (pdev->button_dev) {
+ input_report_key(pdev->button_dev, KEY_CAMERA, down);
+ input_sync(pdev->button_dev);
+ }
+#endif
+}
+
+static void pwc_frame_complete(struct pwc_device *pdev)
+{
+ struct pwc_frame_buf *fbuf = pdev->fill_buf;
+
+ /* The ToUCam Fun CMOS sensor causes the firmware to send 2 or 3 bogus
+ frames on the USB wire after an exposure change. This conditition is
+ however detected in the cam and a bit is set in the header.
+ */
+ if (pdev->type == 730) {
+ unsigned char *ptr = (unsigned char *)fbuf->data;
+
+ if (ptr[1] == 1 && ptr[0] & 0x10) {
+ PWC_TRACE("Hyundai CMOS sensor bug. Dropping frame.\n");
+ pdev->drop_frames += 2;
+ }
+ if ((ptr[0] ^ pdev->vmirror) & 0x01) {
+ pwc_snapshot_button(pdev, ptr[0] & 0x01);
+ }
+ if ((ptr[0] ^ pdev->vmirror) & 0x02) {
+ if (ptr[0] & 0x02)
+ PWC_TRACE("Image is mirrored.\n");
+ else
+ PWC_TRACE("Image is normal.\n");
+ }
+ pdev->vmirror = ptr[0] & 0x03;
+ /* Sometimes the trailer of the 730 is still sent as a 4 byte packet
+ after a short frame; this condition is filtered out specifically. A 4 byte
+ frame doesn't make sense anyway.
+ So we get either this sequence:
+ drop_bit set -> 4 byte frame -> short frame -> good frame
+ Or this one:
+ drop_bit set -> short frame -> good frame
+ So we drop either 3 or 2 frames in all!
+ */
+ if (fbuf->filled == 4)
+ pdev->drop_frames++;
+ } else if (pdev->type == 740 || pdev->type == 720) {
+ unsigned char *ptr = (unsigned char *)fbuf->data;
+ if ((ptr[0] ^ pdev->vmirror) & 0x01) {
+ pwc_snapshot_button(pdev, ptr[0] & 0x01);
+ }
+ pdev->vmirror = ptr[0] & 0x03;
+ }
+
+ /* In case we were instructed to drop the frame, do so silently. */
+ if (pdev->drop_frames > 0) {
+ pdev->drop_frames--;
+ } else {
+ /* Check for underflow first */
+ if (fbuf->filled < pdev->frame_total_size) {
+ PWC_DEBUG_FLOW("Frame buffer underflow (%d bytes);"
+ " discarded.\n", fbuf->filled);
+ } else {
+ fbuf->vb.v4l2_buf.field = V4L2_FIELD_NONE;
+ fbuf->vb.v4l2_buf.sequence = pdev->vframe_count;
+ vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE);
+ pdev->fill_buf = NULL;
+ pdev->vsync = 0;
+ }
+ } /* !drop_frames */
+ pdev->vframe_count++;
+}
+
+/* This gets called for the Isochronous pipe (video). This is done in
+ * interrupt time, so it has to be fast, not crash, and not stall. Neat.
+ */
+static void pwc_isoc_handler(struct urb *urb)
+{
+ struct pwc_device *pdev = (struct pwc_device *)urb->context;
+ int i, fst, flen;
+ unsigned char *iso_buf = NULL;
+
+ if (urb->status == -ENOENT || urb->status == -ECONNRESET ||
+ urb->status == -ESHUTDOWN) {
+ PWC_DEBUG_OPEN("URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a");
+ return;
+ }
+
+ if (pdev->fill_buf == NULL)
+ pdev->fill_buf = pwc_get_next_fill_buf(pdev);
+
+ if (urb->status != 0) {
+ const char *errmsg;
+
+ errmsg = "Unknown";
+ switch(urb->status) {
+ case -ENOSR: errmsg = "Buffer error (overrun)"; break;
+ case -EPIPE: errmsg = "Stalled (device not responding)"; break;
+ case -EOVERFLOW: errmsg = "Babble (bad cable?)"; break;
+ case -EPROTO: errmsg = "Bit-stuff error (bad cable?)"; break;
+ case -EILSEQ: errmsg = "CRC/Timeout (could be anything)"; break;
+ case -ETIME: errmsg = "Device does not respond"; break;
+ }
+ PWC_ERROR("pwc_isoc_handler() called with status %d [%s].\n",
+ urb->status, errmsg);
+ /* Give up after a number of contiguous errors */
+ if (++pdev->visoc_errors > MAX_ISOC_ERRORS)
+ {
+ PWC_ERROR("Too many ISOC errors, bailing out.\n");
+ if (pdev->fill_buf) {
+ vb2_buffer_done(&pdev->fill_buf->vb,
+ VB2_BUF_STATE_ERROR);
+ pdev->fill_buf = NULL;
+ }
+ }
+ pdev->vsync = 0; /* Drop the current frame */
+ goto handler_end;
+ }
+
+ /* Reset ISOC error counter. We did get here, after all. */
+ pdev->visoc_errors = 0;
+
+ /* vsync: 0 = don't copy data
+ 1 = sync-hunt
+ 2 = synched
+ */
+ /* Compact data */
+ for (i = 0; i < urb->number_of_packets; i++) {
+ fst = urb->iso_frame_desc[i].status;
+ flen = urb->iso_frame_desc[i].actual_length;
+ iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+ if (fst != 0) {
+ PWC_ERROR("Iso frame %d has error %d\n", i, fst);
+ continue;
+ }
+ if (flen > 0 && pdev->vsync) {
+ struct pwc_frame_buf *fbuf = pdev->fill_buf;
+
+ if (pdev->vsync == 1) {
+ do_gettimeofday(&fbuf->vb.v4l2_buf.timestamp);
+ pdev->vsync = 2;
+ }
+
+ if (flen + fbuf->filled > pdev->frame_total_size) {
+ PWC_ERROR("Frame overflow (%d > %d)\n",
+ flen + fbuf->filled,
+ pdev->frame_total_size);
+ pdev->vsync = 0; /* Let's wait for an EOF */
+ } else {
+ memcpy(fbuf->data + fbuf->filled, iso_buf,
+ flen);
+ fbuf->filled += flen;
+ }
+ }
+ if (flen < pdev->vlast_packet_size) {
+ /* Shorter packet... end of frame */
+ if (pdev->vsync == 2)
+ pwc_frame_complete(pdev);
+ if (pdev->fill_buf == NULL)
+ pdev->fill_buf = pwc_get_next_fill_buf(pdev);
+ if (pdev->fill_buf) {
+ pdev->fill_buf->filled = 0;
+ pdev->vsync = 1;
+ }
+ }
+ pdev->vlast_packet_size = flen;
+ }
+
+handler_end:
+ i = usb_submit_urb(urb, GFP_ATOMIC);
+ if (i != 0)
+ PWC_ERROR("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i);
+}
+
+static int pwc_isoc_init(struct pwc_device *pdev)
+{
+ struct usb_device *udev;
+ struct urb *urb;
+ int i, j, ret;
+ struct usb_interface *intf;
+ struct usb_host_interface *idesc = NULL;
+ int compression = 0; /* 0..3 = uncompressed..high */
+
+ if (pdev->iso_init)
+ return 0;
+
+ pdev->vsync = 0;
+ pdev->vlast_packet_size = 0;
+ pdev->fill_buf = NULL;
+ pdev->vframe_count = 0;
+ pdev->visoc_errors = 0;
+ udev = pdev->udev;
+
+retry:
+ /* We first try with low compression and then retry with a higher
+ compression setting if there is not enough bandwidth. */
+ ret = pwc_set_video_mode(pdev, pdev->width, pdev->height, pdev->pixfmt,
+ pdev->vframes, &compression, 1);
+
+ /* Get the current alternate interface, adjust packet size */
+ intf = usb_ifnum_to_if(udev, 0);
+ if (intf)
+ idesc = usb_altnum_to_altsetting(intf, pdev->valternate);
+ if (!idesc)
+ return -EIO;
+
+ /* Search video endpoint */
+ pdev->vmax_packet_size = -1;
+ for (i = 0; i < idesc->desc.bNumEndpoints; i++) {
+ if ((idesc->endpoint[i].desc.bEndpointAddress & 0xF) == pdev->vendpoint) {
+ pdev->vmax_packet_size = le16_to_cpu(idesc->endpoint[i].desc.wMaxPacketSize);
+ break;
+ }
+ }
+
+ if (pdev->vmax_packet_size < 0 || pdev->vmax_packet_size > ISO_MAX_FRAME_SIZE) {
+ PWC_ERROR("Failed to find packet size for video endpoint in current alternate setting.\n");
+ return -ENFILE; /* Odd error, that should be noticeable */
+ }
+
+ /* Set alternate interface */
+ PWC_DEBUG_OPEN("Setting alternate interface %d\n", pdev->valternate);
+ ret = usb_set_interface(pdev->udev, 0, pdev->valternate);
+ if (ret == -ENOSPC && compression < 3) {
+ compression++;
+ goto retry;
+ }
+ if (ret < 0)
+ return ret;
+
+ /* Allocate and init Isochronuous urbs */
+ for (i = 0; i < MAX_ISO_BUFS; i++) {
+ urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL);
+ if (urb == NULL) {
+ PWC_ERROR("Failed to allocate urb %d\n", i);
+ pdev->iso_init = 1;
+ pwc_isoc_cleanup(pdev);
+ return -ENOMEM;
+ }
+ pdev->urbs[i] = urb;
+ PWC_DEBUG_MEMORY("Allocated URB at 0x%p\n", urb);
+
+ urb->interval = 1; // devik
+ urb->dev = udev;
+ urb->pipe = usb_rcvisocpipe(udev, pdev->vendpoint);
+ urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+ urb->transfer_buffer = usb_alloc_coherent(udev,
+ ISO_BUFFER_SIZE,
+ GFP_KERNEL,
+ &urb->transfer_dma);
+ if (urb->transfer_buffer == NULL) {
+ PWC_ERROR("Failed to allocate urb buffer %d\n", i);
+ pdev->iso_init = 1;
+ pwc_isoc_cleanup(pdev);
+ return -ENOMEM;
+ }
+ urb->transfer_buffer_length = ISO_BUFFER_SIZE;
+ urb->complete = pwc_isoc_handler;
+ urb->context = pdev;
+ urb->start_frame = 0;
+ urb->number_of_packets = ISO_FRAMES_PER_DESC;
+ for (j = 0; j < ISO_FRAMES_PER_DESC; j++) {
+ urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE;
+ urb->iso_frame_desc[j].length = pdev->vmax_packet_size;
+ }
+ }
+
+ /* link */
+ for (i = 0; i < MAX_ISO_BUFS; i++) {
+ ret = usb_submit_urb(pdev->urbs[i], GFP_KERNEL);
+ if (ret == -ENOSPC && compression < 3) {
+ compression++;
+ pdev->iso_init = 1;
+ pwc_isoc_cleanup(pdev);
+ goto retry;
+ }
+ if (ret) {
+ PWC_ERROR("isoc_init() submit_urb %d failed with error %d\n", i, ret);
+ pdev->iso_init = 1;
+ pwc_isoc_cleanup(pdev);
+ return ret;
+ }
+ PWC_DEBUG_MEMORY("URB 0x%p submitted.\n", pdev->urbs[i]);
+ }
+
+ /* All is done... */
+ pdev->iso_init = 1;
+ PWC_DEBUG_OPEN("<< pwc_isoc_init()\n");
+ return 0;
+}
+
+static void pwc_iso_stop(struct pwc_device *pdev)
+{
+ int i;
+
+ /* Unlinking ISOC buffers one by one */
+ for (i = 0; i < MAX_ISO_BUFS; i++) {
+ if (pdev->urbs[i]) {
+ PWC_DEBUG_MEMORY("Unlinking URB %p\n", pdev->urbs[i]);
+ usb_kill_urb(pdev->urbs[i]);
+ }
+ }
+}
+
+static void pwc_iso_free(struct pwc_device *pdev)
+{
+ int i;
+
+ /* Freeing ISOC buffers one by one */
+ for (i = 0; i < MAX_ISO_BUFS; i++) {
+ if (pdev->urbs[i]) {
+ PWC_DEBUG_MEMORY("Freeing URB\n");
+ if (pdev->urbs[i]->transfer_buffer) {
+ usb_free_coherent(pdev->udev,
+ pdev->urbs[i]->transfer_buffer_length,
+ pdev->urbs[i]->transfer_buffer,
+ pdev->urbs[i]->transfer_dma);
+ }
+ usb_free_urb(pdev->urbs[i]);
+ pdev->urbs[i] = NULL;
+ }
+ }
+}
+
+static void pwc_isoc_cleanup(struct pwc_device *pdev)
+{
+ PWC_DEBUG_OPEN(">> pwc_isoc_cleanup()\n");
+
+ if (pdev->iso_init == 0)
+ return;
+
+ pwc_iso_stop(pdev);
+ pwc_iso_free(pdev);
+ usb_set_interface(pdev->udev, 0, 0);
+
+ pdev->iso_init = 0;
+ PWC_DEBUG_OPEN("<< pwc_isoc_cleanup()\n");
+}
+
+static void pwc_cleanup_queued_bufs(struct pwc_device *pdev)
+{
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&pdev->queued_bufs_lock, flags);
+ while (!list_empty(&pdev->queued_bufs)) {
+ struct pwc_frame_buf *buf;
+
+ buf = list_entry(pdev->queued_bufs.next, struct pwc_frame_buf,
+ list);
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+ spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
+}
+
+#ifdef CONFIG_USB_PWC_DEBUG
+static const char *pwc_sensor_type_to_string(unsigned int sensor_type)
+{
+ switch(sensor_type) {
+ case 0x00:
+ return "Hyundai CMOS sensor";
+ case 0x20:
+ return "Sony CCD sensor + TDA8787";
+ case 0x2E:
+ return "Sony CCD sensor + Exas 98L59";
+ case 0x2F:
+ return "Sony CCD sensor + ADI 9804";
+ case 0x30:
+ return "Sharp CCD sensor + TDA8787";
+ case 0x3E:
+ return "Sharp CCD sensor + Exas 98L59";
+ case 0x3F:
+ return "Sharp CCD sensor + ADI 9804";
+ case 0x40:
+ return "UPA 1021 sensor";
+ case 0x100:
+ return "VGA sensor";
+ case 0x101:
+ return "PAL MR sensor";
+ default:
+ return "unknown type of sensor";
+ }
+}
+#endif
+
+/***************************************************************************/
+/* Video4Linux functions */
+
+int pwc_test_n_set_capt_file(struct pwc_device *pdev, struct file *file)
+{
+ int r = 0;
+
+ mutex_lock(&pdev->capt_file_lock);
+ if (pdev->capt_file != NULL &&
+ pdev->capt_file != file) {
+ r = -EBUSY;
+ goto leave;
+ }
+ pdev->capt_file = file;
+leave:
+ mutex_unlock(&pdev->capt_file_lock);
+ return r;
+}
+
+static void pwc_video_release(struct v4l2_device *v)
+{
+ struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev);
+
+ v4l2_ctrl_handler_free(&pdev->ctrl_handler);
+ kfree(pdev->ctrl_buf);
+ kfree(pdev);
+}
+
+static int pwc_video_close(struct file *file)
+{
+ struct pwc_device *pdev = video_drvdata(file);
+
+ if (pdev->capt_file == file) {
+ vb2_queue_release(&pdev->vb_queue);
+ pdev->capt_file = NULL;
+ }
+ return v4l2_fh_release(file);
+}
+
+static ssize_t pwc_video_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct pwc_device *pdev = video_drvdata(file);
+
+ if (!pdev->udev)
+ return -ENODEV;
+
+ if (pwc_test_n_set_capt_file(pdev, file))
+ return -EBUSY;
+
+ return vb2_read(&pdev->vb_queue, buf, count, ppos,
+ file->f_flags & O_NONBLOCK);
+}
+
+static unsigned int pwc_video_poll(struct file *file, poll_table *wait)
+{
+ struct pwc_device *pdev = video_drvdata(file);
+
+ if (!pdev->udev)
+ return POLL_ERR;
+
+ return vb2_poll(&pdev->vb_queue, file, wait);
+}
+
+static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct pwc_device *pdev = video_drvdata(file);
+
+ if (pdev->capt_file != file)
+ return -EBUSY;
+
+ return vb2_mmap(&pdev->vb_queue, vma);
+}
+
+/***************************************************************************/
+/* Videobuf2 operations */
+
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct pwc_device *pdev = vb2_get_drv_priv(vq);
+ int size;
+
+ if (*nbuffers < MIN_FRAMES)
+ *nbuffers = MIN_FRAMES;
+ else if (*nbuffers > MAX_FRAMES)
+ *nbuffers = MAX_FRAMES;
+
+ *nplanes = 1;
+
+ size = pwc_get_size(pdev, MAX_WIDTH, MAX_HEIGHT);
+ sizes[0] = PAGE_ALIGN(pwc_image_sizes[size][0] *
+ pwc_image_sizes[size][1] * 3 / 2);
+
+ return 0;
+}
+
+static int buffer_init(struct vb2_buffer *vb)
+{
+ struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
+
+ /* need vmalloc since frame buffer > 128K */
+ buf->data = vzalloc(PWC_FRAME_SIZE);
+ if (buf->data == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int buffer_prepare(struct vb2_buffer *vb)
+{
+ struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
+
+ /* Don't allow queing new buffers after device disconnection */
+ if (!pdev->udev)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int buffer_finish(struct vb2_buffer *vb)
+{
+ struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
+ struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
+
+ /*
+ * Application has called dqbuf and is getting back a buffer we've
+ * filled, take the pwc data we've stored in buf->data and decompress
+ * it into a usable format, storing the result in the vb2_buffer
+ */
+ return pwc_decompress(pdev, buf);
+}
+
+static void buffer_cleanup(struct vb2_buffer *vb)
+{
+ struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
+
+ vfree(buf->data);
+}
+
+static void buffer_queue(struct vb2_buffer *vb)
+{
+ struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
+ struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&pdev->queued_bufs_lock, flags);
+ /* Check the device has not disconnected between prep and queuing */
+ if (pdev->udev)
+ list_add_tail(&buf->list, &pdev->queued_bufs);
+ else
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
+}
+
+static int start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct pwc_device *pdev = vb2_get_drv_priv(vq);
+ int r;
+
+ mutex_lock(&pdev->udevlock);
+ if (!pdev->udev) {
+ r = -ENODEV;
+ goto leave;
+ }
+
+ /* Turn on camera and set LEDS on */
+ pwc_camera_power(pdev, 1);
+ pwc_set_leds(pdev, leds[0], leds[1]);
+
+ r = pwc_isoc_init(pdev);
+ if (r) {
+ /* If we failed turn camera and LEDS back off */
+ pwc_set_leds(pdev, 0, 0);
+ pwc_camera_power(pdev, 0);
+ /* And cleanup any queued bufs!! */
+ pwc_cleanup_queued_bufs(pdev);
+ }
+leave:
+ mutex_unlock(&pdev->udevlock);
+ return r;
+}
+
+static int stop_streaming(struct vb2_queue *vq)
+{
+ struct pwc_device *pdev = vb2_get_drv_priv(vq);
+
+ mutex_lock(&pdev->udevlock);
+ if (pdev->udev) {
+ pwc_set_leds(pdev, 0, 0);
+ pwc_camera_power(pdev, 0);
+ pwc_isoc_cleanup(pdev);
+ }
+ mutex_unlock(&pdev->udevlock);
+
+ pwc_cleanup_queued_bufs(pdev);
+
+ return 0;
+}
+
+static struct vb2_ops pwc_vb_queue_ops = {
+ .queue_setup = queue_setup,
+ .buf_init = buffer_init,
+ .buf_prepare = buffer_prepare,
+ .buf_finish = buffer_finish,
+ .buf_cleanup = buffer_cleanup,
+ .buf_queue = buffer_queue,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
+};
+
+/***************************************************************************/
+/* USB functions */
+
+/* This function gets called when a new device is plugged in or the usb core
+ * is loaded.
+ */
+
+static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct pwc_device *pdev = NULL;
+ int vendor_id, product_id, type_id;
+ int rc;
+ int features = 0;
+ int compression = 0;
+ int my_power_save = power_save;
+ char serial_number[30], *name;
+
+ vendor_id = le16_to_cpu(udev->descriptor.idVendor);
+ product_id = le16_to_cpu(udev->descriptor.idProduct);
+
+ /* Check if we can handle this device */
+ PWC_DEBUG_PROBE("probe() called [%04X %04X], if %d\n",
+ vendor_id, product_id,
+ intf->altsetting->desc.bInterfaceNumber);
+
+ /* the interfaces are probed one by one. We are only interested in the
+ video interface (0) now.
+ Interface 1 is the Audio Control, and interface 2 Audio itself.
+ */
+ if (intf->altsetting->desc.bInterfaceNumber > 0)
+ return -ENODEV;
+
+ if (vendor_id == 0x0471) {
+ switch (product_id) {
+ case 0x0302:
+ PWC_INFO("Philips PCA645VC USB webcam detected.\n");
+ name = "Philips 645 webcam";
+ type_id = 645;
+ break;
+ case 0x0303:
+ PWC_INFO("Philips PCA646VC USB webcam detected.\n");
+ name = "Philips 646 webcam";
+ type_id = 646;
+ break;
+ case 0x0304:
+ PWC_INFO("Askey VC010 type 2 USB webcam detected.\n");
+ name = "Askey VC010 webcam";
+ type_id = 646;
+ break;
+ case 0x0307:
+ PWC_INFO("Philips PCVC675K (Vesta) USB webcam detected.\n");
+ name = "Philips 675 webcam";
+ type_id = 675;
+ break;
+ case 0x0308:
+ PWC_INFO("Philips PCVC680K (Vesta Pro) USB webcam detected.\n");
+ name = "Philips 680 webcam";
+ type_id = 680;
+ break;
+ case 0x030C:
+ PWC_INFO("Philips PCVC690K (Vesta Pro Scan) USB webcam detected.\n");
+ name = "Philips 690 webcam";
+ type_id = 690;
+ break;
+ case 0x0310:
+ PWC_INFO("Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) USB webcam detected.\n");
+ name = "Philips 730 webcam";
+ type_id = 730;
+ break;
+ case 0x0311:
+ PWC_INFO("Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) USB webcam detected.\n");
+ name = "Philips 740 webcam";
+ type_id = 740;
+ break;
+ case 0x0312:
+ PWC_INFO("Philips PCVC750K (ToUCam Pro Scan) USB webcam detected.\n");
+ name = "Philips 750 webcam";
+ type_id = 750;
+ break;
+ case 0x0313:
+ PWC_INFO("Philips PCVC720K/40 (ToUCam XS) USB webcam detected.\n");
+ name = "Philips 720K/40 webcam";
+ type_id = 720;
+ break;
+ case 0x0329:
+ PWC_INFO("Philips SPC 900NC USB webcam detected.\n");
+ name = "Philips SPC 900NC webcam";
+ type_id = 740;
+ break;
+ default:
+ return -ENODEV;
+ break;
+ }
+ }
+ else if (vendor_id == 0x069A) {
+ switch(product_id) {
+ case 0x0001:
+ PWC_INFO("Askey VC010 type 1 USB webcam detected.\n");
+ name = "Askey VC010 webcam";
+ type_id = 645;
+ break;
+ default:
+ return -ENODEV;
+ break;
+ }
+ }
+ else if (vendor_id == 0x046d) {
+ switch(product_id) {
+ case 0x08b0:
+ PWC_INFO("Logitech QuickCam Pro 3000 USB webcam detected.\n");
+ name = "Logitech QuickCam Pro 3000";
+ type_id = 740; /* CCD sensor */
+ break;
+ case 0x08b1:
+ PWC_INFO("Logitech QuickCam Notebook Pro USB webcam detected.\n");
+ name = "Logitech QuickCam Notebook Pro";
+ type_id = 740; /* CCD sensor */
+ break;
+ case 0x08b2:
+ PWC_INFO("Logitech QuickCam 4000 Pro USB webcam detected.\n");
+ name = "Logitech QuickCam Pro 4000";
+ type_id = 740; /* CCD sensor */
+ if (my_power_save == -1)
+ my_power_save = 1;
+ break;
+ case 0x08b3:
+ PWC_INFO("Logitech QuickCam Zoom USB webcam detected.\n");
+ name = "Logitech QuickCam Zoom";
+ type_id = 740; /* CCD sensor */
+ break;
+ case 0x08B4:
+ PWC_INFO("Logitech QuickCam Zoom (new model) USB webcam detected.\n");
+ name = "Logitech QuickCam Zoom";
+ type_id = 740; /* CCD sensor */
+ if (my_power_save == -1)
+ my_power_save = 1;
+ break;
+ case 0x08b5:
+ PWC_INFO("Logitech QuickCam Orbit/Sphere USB webcam detected.\n");
+ name = "Logitech QuickCam Orbit";
+ type_id = 740; /* CCD sensor */
+ if (my_power_save == -1)
+ my_power_save = 1;
+ features |= FEATURE_MOTOR_PANTILT;
+ break;
+ case 0x08b6:
+ PWC_INFO("Logitech/Cisco VT Camera webcam detected.\n");
+ name = "Cisco VT Camera";
+ type_id = 740; /* CCD sensor */
+ break;
+ case 0x08b7:
+ PWC_INFO("Logitech ViewPort AV 100 webcam detected.\n");
+ name = "Logitech ViewPort AV 100";
+ type_id = 740; /* CCD sensor */
+ break;
+ case 0x08b8: /* Where this released? */
+ PWC_INFO("Logitech QuickCam detected (reserved ID).\n");
+ name = "Logitech QuickCam (res.)";
+ type_id = 730; /* Assuming CMOS */
+ break;
+ default:
+ return -ENODEV;
+ break;
+ }
+ }
+ else if (vendor_id == 0x055d) {
+ /* I don't know the difference between the C10 and the C30;
+ I suppose the difference is the sensor, but both cameras
+ work equally well with a type_id of 675
+ */
+ switch(product_id) {
+ case 0x9000:
+ PWC_INFO("Samsung MPC-C10 USB webcam detected.\n");
+ name = "Samsung MPC-C10";
+ type_id = 675;
+ break;
+ case 0x9001:
+ PWC_INFO("Samsung MPC-C30 USB webcam detected.\n");
+ name = "Samsung MPC-C30";
+ type_id = 675;
+ break;
+ case 0x9002:
+ PWC_INFO("Samsung SNC-35E (v3.0) USB webcam detected.\n");
+ name = "Samsung MPC-C30";
+ type_id = 740;
+ break;
+ default:
+ return -ENODEV;
+ break;
+ }
+ }
+ else if (vendor_id == 0x041e) {
+ switch(product_id) {
+ case 0x400c:
+ PWC_INFO("Creative Labs Webcam 5 detected.\n");
+ name = "Creative Labs Webcam 5";
+ type_id = 730;
+ if (my_power_save == -1)
+ my_power_save = 1;
+ break;
+ case 0x4011:
+ PWC_INFO("Creative Labs Webcam Pro Ex detected.\n");
+ name = "Creative Labs Webcam Pro Ex";
+ type_id = 740;
+ break;
+ default:
+ return -ENODEV;
+ break;
+ }
+ }
+ else if (vendor_id == 0x04cc) {
+ switch(product_id) {
+ case 0x8116:
+ PWC_INFO("Sotec Afina Eye USB webcam detected.\n");
+ name = "Sotec Afina Eye";
+ type_id = 730;
+ break;
+ default:
+ return -ENODEV;
+ break;
+ }
+ }
+ else if (vendor_id == 0x06be) {
+ switch(product_id) {
+ case 0x8116:
+ /* This is essentially the same cam as the Sotec Afina Eye */
+ PWC_INFO("AME Co. Afina Eye USB webcam detected.\n");
+ name = "AME Co. Afina Eye";
+ type_id = 750;
+ break;
+ default:
+ return -ENODEV;
+ break;
+ }
+
+ }
+ else if (vendor_id == 0x0d81) {
+ switch(product_id) {
+ case 0x1900:
+ PWC_INFO("Visionite VCS-UC300 USB webcam detected.\n");
+ name = "Visionite VCS-UC300";
+ type_id = 740; /* CCD sensor */
+ break;
+ case 0x1910:
+ PWC_INFO("Visionite VCS-UM100 USB webcam detected.\n");
+ name = "Visionite VCS-UM100";
+ type_id = 730; /* CMOS sensor */
+ break;
+ default:
+ return -ENODEV;
+ break;
+ }
+ }
+ else
+ return -ENODEV; /* Not any of the know types; but the list keeps growing. */
+
+ if (my_power_save == -1)
+ my_power_save = 0;
+
+ memset(serial_number, 0, 30);
+ usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29);
+ PWC_DEBUG_PROBE("Device serial number is %s\n", serial_number);
+
+ if (udev->descriptor.bNumConfigurations > 1)
+ PWC_WARNING("Warning: more than 1 configuration available.\n");
+
+ /* Allocate structure, initialize pointers, mutexes, etc. and link it to the usb_device */
+ pdev = kzalloc(sizeof(struct pwc_device), GFP_KERNEL);
+ if (pdev == NULL) {
+ PWC_ERROR("Oops, could not allocate memory for pwc_device.\n");
+ return -ENOMEM;
+ }
+ pdev->type = type_id;
+ pdev->features = features;
+ pwc_construct(pdev); /* set min/max sizes correct */
+
+ mutex_init(&pdev->capt_file_lock);
+ mutex_init(&pdev->udevlock);
+ spin_lock_init(&pdev->queued_bufs_lock);
+ INIT_LIST_HEAD(&pdev->queued_bufs);
+
+ pdev->udev = udev;
+ pdev->power_save = my_power_save;
+
+ /* Init videobuf2 queue structure */
+ memset(&pdev->vb_queue, 0, sizeof(pdev->vb_queue));
+ pdev->vb_queue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ pdev->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
+ pdev->vb_queue.drv_priv = pdev;
+ pdev->vb_queue.buf_struct_size = sizeof(struct pwc_frame_buf);
+ pdev->vb_queue.ops = &pwc_vb_queue_ops;
+ pdev->vb_queue.mem_ops = &vb2_vmalloc_memops;
+ vb2_queue_init(&pdev->vb_queue);
+
+ /* Init video_device structure */
+ memcpy(&pdev->vdev, &pwc_template, sizeof(pwc_template));
+ strcpy(pdev->vdev.name, name);
+ set_bit(V4L2_FL_USE_FH_PRIO, &pdev->vdev.flags);
+ video_set_drvdata(&pdev->vdev, pdev);
+
+ pdev->release = le16_to_cpu(udev->descriptor.bcdDevice);
+ PWC_DEBUG_PROBE("Release: %04x\n", pdev->release);
+
+ /* Allocate USB command buffers */
+ pdev->ctrl_buf = kmalloc(sizeof(pdev->cmd_buf), GFP_KERNEL);
+ if (!pdev->ctrl_buf) {
+ PWC_ERROR("Oops, could not allocate memory for pwc_device.\n");
+ rc = -ENOMEM;
+ goto err_free_mem;
+ }
+
+#ifdef CONFIG_USB_PWC_DEBUG
+ /* Query sensor type */
+ if (pwc_get_cmos_sensor(pdev, &rc) >= 0) {
+ PWC_DEBUG_OPEN("This %s camera is equipped with a %s (%d).\n",
+ pdev->vdev.name,
+ pwc_sensor_type_to_string(rc), rc);
+ }
+#endif
+
+ /* Set the leds off */
+ pwc_set_leds(pdev, 0, 0);
+
+ /* Setup intial videomode */
+ rc = pwc_set_video_mode(pdev, MAX_WIDTH, MAX_HEIGHT,
+ V4L2_PIX_FMT_YUV420, 30, &compression, 1);
+ if (rc)
+ goto err_free_mem;
+
+ /* Register controls (and read default values from camera */
+ rc = pwc_init_controls(pdev);
+ if (rc) {
+ PWC_ERROR("Failed to register v4l2 controls (%d).\n", rc);
+ goto err_free_mem;
+ }
+
+ /* And powerdown the camera until streaming starts */
+ pwc_camera_power(pdev, 0);
+
+ /* Register the v4l2_device structure */
+ pdev->v4l2_dev.release = pwc_video_release;
+ rc = v4l2_device_register(&intf->dev, &pdev->v4l2_dev);
+ if (rc) {
+ PWC_ERROR("Failed to register v4l2-device (%d).\n", rc);
+ goto err_free_controls;
+ }
+
+ pdev->v4l2_dev.ctrl_handler = &pdev->ctrl_handler;
+ pdev->vdev.v4l2_dev = &pdev->v4l2_dev;
+
+ rc = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, -1);
+ if (rc < 0) {
+ PWC_ERROR("Failed to register as video device (%d).\n", rc);
+ goto err_unregister_v4l2_dev;
+ }
+ PWC_INFO("Registered as %s.\n", video_device_node_name(&pdev->vdev));
+
+#ifdef CONFIG_USB_PWC_INPUT_EVDEV
+ /* register webcam snapshot button input device */
+ pdev->button_dev = input_allocate_device();
+ if (!pdev->button_dev) {
+ PWC_ERROR("Err, insufficient memory for webcam snapshot button device.");
+ rc = -ENOMEM;
+ goto err_video_unreg;
+ }
+
+ usb_make_path(udev, pdev->button_phys, sizeof(pdev->button_phys));
+ strlcat(pdev->button_phys, "/input0", sizeof(pdev->button_phys));
+
+ pdev->button_dev->name = "PWC snapshot button";
+ pdev->button_dev->phys = pdev->button_phys;
+ usb_to_input_id(pdev->udev, &pdev->button_dev->id);
+ pdev->button_dev->dev.parent = &pdev->udev->dev;
+ pdev->button_dev->evbit[0] = BIT_MASK(EV_KEY);
+ pdev->button_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA);
+
+ rc = input_register_device(pdev->button_dev);
+ if (rc) {
+ input_free_device(pdev->button_dev);
+ pdev->button_dev = NULL;
+ goto err_video_unreg;
+ }
+#endif
+
+ return 0;
+
+err_video_unreg:
+ video_unregister_device(&pdev->vdev);
+err_unregister_v4l2_dev:
+ v4l2_device_unregister(&pdev->v4l2_dev);
+err_free_controls:
+ v4l2_ctrl_handler_free(&pdev->ctrl_handler);
+err_free_mem:
+ kfree(pdev->ctrl_buf);
+ kfree(pdev);
+ return rc;
+}
+
+/* The user yanked out the cable... */
+static void usb_pwc_disconnect(struct usb_interface *intf)
+{
+ struct v4l2_device *v = usb_get_intfdata(intf);
+ struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev);
+
+ mutex_lock(&pdev->udevlock);
+ /* No need to keep the urbs around after disconnection */
+ pwc_isoc_cleanup(pdev);
+ pdev->udev = NULL;
+ mutex_unlock(&pdev->udevlock);
+
+ pwc_cleanup_queued_bufs(pdev);
+
+ video_unregister_device(&pdev->vdev);
+ v4l2_device_unregister(&pdev->v4l2_dev);
+
+#ifdef CONFIG_USB_PWC_INPUT_EVDEV
+ if (pdev->button_dev)
+ input_unregister_device(pdev->button_dev);
+#endif
+
+ v4l2_device_put(&pdev->v4l2_dev);
+}
+
+
+/*
+ * Initialization code & module stuff
+ */
+
+static unsigned int leds_nargs;
+
+#ifdef CONFIG_USB_PWC_DEBUG
+module_param_named(trace, pwc_trace, int, 0644);
+#endif
+module_param(power_save, int, 0644);
+module_param_array(leds, int, &leds_nargs, 0444);
+
+#ifdef CONFIG_USB_PWC_DEBUG
+MODULE_PARM_DESC(trace, "For debugging purposes");
+#endif
+MODULE_PARM_DESC(power_save, "Turn power saving for new cameras on or off");
+MODULE_PARM_DESC(leds, "LED on,off time in milliseconds");
+
+MODULE_DESCRIPTION("Philips & OEM USB webcam driver");
+MODULE_AUTHOR("Luc Saillard <luc@saillard.org>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("pwcx");
+MODULE_VERSION( PWC_VERSION );
+
+static int __init usb_pwc_init(void)
+{
+ return usb_register(&pwc_driver);
+}
+
+static void __exit usb_pwc_exit(void)
+{
+ usb_deregister(&pwc_driver);
+}
+
+module_init(usb_pwc_init);
+module_exit(usb_pwc_exit);
diff --git a/drivers/media/video/pwc/pwc-kiara.c b/drivers/media/video/pwc/pwc-kiara.c
new file mode 100644
index 00000000..e5f4fd81
--- /dev/null
+++ b/drivers/media/video/pwc/pwc-kiara.c
@@ -0,0 +1,892 @@
+/* Linux driver for Philips webcam
+ (C) 2004-2006 Luc Saillard (luc@saillard.org)
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+ The decompression routines have been implemented by reverse-engineering the
+ Nemosoft binary pwcx module. Caveat emptor.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+
+/* This tables contains entries for the 730/740/750 (Kiara) camera, with
+ 4 different qualities (no compression, low, medium, high).
+ It lists the bandwidth requirements for said mode by its alternate interface
+ number. An alternate of 0 means that the mode is unavailable.
+
+ There are 6 * 4 * 4 entries:
+ 6 different resolutions subqcif, qsif, qcif, sif, cif, vga
+ 6 framerates: 5, 10, 15, 20, 25, 30
+ 4 compression modi: none, low, medium, high
+
+ When an uncompressed mode is not available, the next available compressed mode
+ will be chosen (unless the decompressor is absent). Sometimes there are only
+ 1 or 2 compressed modes available; in that case entries are duplicated.
+*/
+
+
+#include "pwc-kiara.h"
+
+const unsigned int Kiara_fps_vector[PWC_FPS_MAX_KIARA] = { 5, 10, 15, 20, 25, 30 };
+
+const struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4] =
+{
+ /* SQCIF */
+ {
+ /* 5 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 10 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 15 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 20 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 25 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 30 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ },
+ /* QSIF */
+ {
+ /* 5 fps */
+ {
+ {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}},
+ {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}},
+ {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}},
+ {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}},
+ },
+ /* 10 fps */
+ {
+ {2, 291, 0, {0x1C, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x23, 0x01, 0x80}},
+ {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}},
+ {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}},
+ {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}},
+ },
+ /* 15 fps */
+ {
+ {3, 437, 0, {0x1B, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xB5, 0x01, 0x80}},
+ {2, 292, 640, {0x13, 0xF4, 0x30, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x20, 0x24, 0x01, 0x80}},
+ {2, 292, 640, {0x13, 0xF4, 0x30, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x20, 0x24, 0x01, 0x80}},
+ {1, 192, 420, {0x13, 0xF4, 0x30, 0x0D, 0x1B, 0x0C, 0x53, 0x1E, 0x18, 0xC0, 0x00, 0x80}},
+ },
+ /* 20 fps */
+ {
+ {4, 589, 0, {0x1A, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x4D, 0x02, 0x80}},
+ {3, 448, 730, {0x12, 0xF4, 0x30, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x18, 0xC0, 0x01, 0x80}},
+ {2, 292, 476, {0x12, 0xF4, 0x30, 0x0E, 0xD8, 0x0E, 0x10, 0x19, 0x18, 0x24, 0x01, 0x80}},
+ {1, 192, 312, {0x12, 0xF4, 0x50, 0x09, 0xB3, 0x08, 0xEB, 0x1E, 0x18, 0xC0, 0x00, 0x80}},
+ },
+ /* 25 fps */
+ {
+ {5, 703, 0, {0x19, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xBF, 0x02, 0x80}},
+ {3, 447, 610, {0x11, 0xF4, 0x30, 0x13, 0x0B, 0x12, 0x43, 0x14, 0x28, 0xBF, 0x01, 0x80}},
+ {2, 292, 398, {0x11, 0xF4, 0x50, 0x0C, 0x6C, 0x0B, 0xA4, 0x1E, 0x28, 0x24, 0x01, 0x80}},
+ {1, 193, 262, {0x11, 0xF4, 0x50, 0x08, 0x23, 0x07, 0x5B, 0x1E, 0x28, 0xC1, 0x00, 0x80}},
+ },
+ /* 30 fps */
+ {
+ {8, 874, 0, {0x18, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x6A, 0x03, 0x80}},
+ {5, 704, 730, {0x10, 0xF4, 0x30, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x28, 0xC0, 0x02, 0x80}},
+ {3, 448, 492, {0x10, 0xF4, 0x30, 0x0F, 0x5D, 0x0E, 0x95, 0x15, 0x28, 0xC0, 0x01, 0x80}},
+ {2, 292, 320, {0x10, 0xF4, 0x50, 0x09, 0xFB, 0x09, 0x33, 0x1E, 0x28, 0x24, 0x01, 0x80}},
+ },
+ },
+ /* QCIF */
+ {
+ /* 5 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 10 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 15 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 20 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 25 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 30 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ },
+ /* SIF */
+ {
+ /* 5 fps */
+ {
+ {4, 582, 0, {0x0D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x46, 0x02, 0x80}},
+ {3, 387, 1276, {0x05, 0xF4, 0x30, 0x27, 0xD8, 0x26, 0x48, 0x03, 0x10, 0x83, 0x01, 0x80}},
+ {2, 291, 960, {0x05, 0xF4, 0x30, 0x1D, 0xF2, 0x1C, 0x62, 0x04, 0x10, 0x23, 0x01, 0x80}},
+ {1, 191, 630, {0x05, 0xF4, 0x50, 0x13, 0xA9, 0x12, 0x19, 0x05, 0x18, 0xBF, 0x00, 0x80}},
+ },
+ /* 10 fps */
+ {
+ {0, },
+ {6, 775, 1278, {0x04, 0xF4, 0x30, 0x27, 0xE8, 0x26, 0x58, 0x05, 0x30, 0x07, 0x03, 0x80}},
+ {3, 447, 736, {0x04, 0xF4, 0x30, 0x16, 0xFB, 0x15, 0x6B, 0x05, 0x28, 0xBF, 0x01, 0x80}},
+ {2, 292, 480, {0x04, 0xF4, 0x70, 0x0E, 0xF9, 0x0D, 0x69, 0x09, 0x28, 0x24, 0x01, 0x80}},
+ },
+ /* 15 fps */
+ {
+ {0, },
+ {9, 955, 1050, {0x03, 0xF4, 0x30, 0x20, 0xCF, 0x1F, 0x3F, 0x06, 0x48, 0xBB, 0x03, 0x80}},
+ {4, 592, 650, {0x03, 0xF4, 0x30, 0x14, 0x44, 0x12, 0xB4, 0x08, 0x30, 0x50, 0x02, 0x80}},
+ {3, 448, 492, {0x03, 0xF4, 0x50, 0x0F, 0x52, 0x0D, 0xC2, 0x09, 0x38, 0xC0, 0x01, 0x80}},
+ },
+ /* 20 fps */
+ {
+ {0, },
+ {9, 958, 782, {0x02, 0xF4, 0x30, 0x18, 0x6A, 0x16, 0xDA, 0x0B, 0x58, 0xBE, 0x03, 0x80}},
+ {5, 703, 574, {0x02, 0xF4, 0x50, 0x11, 0xE7, 0x10, 0x57, 0x0B, 0x40, 0xBF, 0x02, 0x80}},
+ {3, 446, 364, {0x02, 0xF4, 0x90, 0x0B, 0x5C, 0x09, 0xCC, 0x0E, 0x38, 0xBE, 0x01, 0x80}},
+ },
+ /* 25 fps */
+ {
+ {0, },
+ {9, 958, 654, {0x01, 0xF4, 0x30, 0x14, 0x66, 0x12, 0xD6, 0x0B, 0x50, 0xBE, 0x03, 0x80}},
+ {6, 776, 530, {0x01, 0xF4, 0x50, 0x10, 0x8C, 0x0E, 0xFC, 0x0C, 0x48, 0x08, 0x03, 0x80}},
+ {4, 592, 404, {0x01, 0xF4, 0x70, 0x0C, 0x96, 0x0B, 0x06, 0x0B, 0x48, 0x50, 0x02, 0x80}},
+ },
+ /* 30 fps */
+ {
+ {0, },
+ {9, 957, 526, {0x00, 0xF4, 0x50, 0x10, 0x68, 0x0E, 0xD8, 0x0D, 0x58, 0xBD, 0x03, 0x80}},
+ {6, 775, 426, {0x00, 0xF4, 0x70, 0x0D, 0x48, 0x0B, 0xB8, 0x0F, 0x50, 0x07, 0x03, 0x80}},
+ {4, 590, 324, {0x00, 0x7A, 0x88, 0x0A, 0x1C, 0x08, 0xB4, 0x0E, 0x50, 0x4E, 0x02, 0x80}},
+ },
+ },
+ /* CIF */
+ {
+ /* 5 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 10 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 15 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 20 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 25 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 30 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ },
+ /* VGA */
+ {
+ /* 5 fps */
+ {
+ {0, },
+ {6, 773, 1272, {0x25, 0xF4, 0x30, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x03, 0x80}},
+ {4, 592, 976, {0x25, 0xF4, 0x50, 0x1E, 0x78, 0x1B, 0x58, 0x03, 0x30, 0x50, 0x02, 0x80}},
+ {3, 448, 738, {0x25, 0xF4, 0x90, 0x17, 0x0C, 0x13, 0xEC, 0x04, 0x30, 0xC0, 0x01, 0x80}},
+ },
+ /* 10 fps */
+ {
+ {0, },
+ {9, 956, 788, {0x24, 0xF4, 0x70, 0x18, 0x9C, 0x15, 0x7C, 0x03, 0x48, 0xBC, 0x03, 0x80}},
+ {6, 776, 640, {0x24, 0xF4, 0xB0, 0x13, 0xFC, 0x11, 0x2C, 0x04, 0x48, 0x08, 0x03, 0x80}},
+ {4, 592, 488, {0x24, 0x7A, 0xE8, 0x0F, 0x3C, 0x0C, 0x6C, 0x06, 0x48, 0x50, 0x02, 0x80}},
+ },
+ /* 15 fps */
+ {
+ {0, },
+ {9, 957, 526, {0x23, 0x7A, 0xE8, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x03, 0x80}},
+ {9, 957, 526, {0x23, 0x7A, 0xE8, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x03, 0x80}},
+ {8, 895, 492, {0x23, 0x7A, 0xE8, 0x0F, 0x5D, 0x0C, 0x8D, 0x06, 0x58, 0x7F, 0x03, 0x80}},
+ },
+ /* 20 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 25 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 30 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ },
+};
+
+
+/*
+ * Rom table for kiara chips
+ *
+ * 32 roms tables (one for each resolution ?)
+ * 2 tables per roms (one for each passes) (Y, and U&V)
+ * 128 bytes per passes
+ */
+
+const unsigned int KiaraRomTable [8][2][16][8] =
+{
+ { /* version 0 */
+ { /* version 0, passes 0 */
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000001,0x00000001},
+ {0x00000000,0x00000000,0x00000009,0x00000009,
+ 0x00000009,0x00000009,0x00000009,0x00000009},
+ {0x00000000,0x00000000,0x00000009,0x00000049,
+ 0x00000049,0x00000049,0x00000049,0x00000049},
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000049,0x00000249,0x0000024a,0x00000049},
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000249,0x00000249,0x0000024a,0x0000024a},
+ {0x00000000,0x00000000,0x00000049,0x00000249,
+ 0x00000249,0x0000124a,0x0000024a,0x0000024a},
+ {0x00000000,0x00000000,0x00000049,0x00000249,
+ 0x0000124a,0x00009252,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x00009252,0x00009292,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x00000249,0x00001249,
+ 0x00009292,0x00009292,0x00009493,0x000124db},
+ {0x00000000,0x00000000,0x00000249,0x0000924a,
+ 0x00009492,0x0000a49b,0x0000a49b,0x000124db},
+ {0x00000000,0x00000000,0x00001249,0x00009252,
+ 0x0000a493,0x000124db,0x000124db,0x000126dc},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x000124db,0x000126dc,0x000136e4,0x000126dc},
+ {0x00000000,0x00000000,0x00009292,0x0000a49b,
+ 0x000124db,0x000136e4,0x000136e4,0x000136e4},
+ {0x00000000,0x00000000,0x00009292,0x0000a49b,
+ 0x000126dc,0x0001b724,0x0001b92d,0x0001b925},
+ {0x00000000,0x00000000,0x00009492,0x000124db,
+ 0x000136e4,0x0001b925,0x0001c96e,0x0001c92d},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ },
+ { /* version 0, passes 1 */
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000},
+ {0x00000000,0x00000000,0x00000001,0x00000009,
+ 0x00000009,0x00000009,0x00000009,0x00000001},
+ {0x00000000,0x00000000,0x00000009,0x00000009,
+ 0x00000049,0x00000049,0x00000049,0x00000049},
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000049,0x00000049,0x0000024a,0x0000024a},
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000249,0x00000249,0x0000024a,0x0000024a},
+ {0x00000000,0x00000000,0x00000049,0x00000249,
+ 0x00000249,0x00000249,0x0000024a,0x00001252},
+ {0x00000000,0x00000000,0x00000049,0x00001249,
+ 0x0000124a,0x0000124a,0x00001252,0x00009292},
+ {0x00000000,0x00000000,0x00000249,0x00001249,
+ 0x00009252,0x00009252,0x00009292,0x00009493},
+ {0x00000000,0x00000000,0x00000249,0x0000924a,
+ 0x00009292,0x00009292,0x00009292,0x00009493},
+ {0x00000000,0x00000000,0x00000249,0x00009292,
+ 0x00009492,0x00009493,0x0000a49b,0x00009493},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x0000a493,0x000124db,0x000126dc,0x000126dc},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x000126dc,0x000136e4,0x000136e4},
+ {0x00000000,0x00000000,0x00009252,0x00009493,
+ 0x000126dc,0x000126dc,0x000136e4,0x000136e4},
+ {0x00000000,0x00000000,0x00009292,0x0000a49b,
+ 0x000136e4,0x000136e4,0x0001b725,0x0001b724},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ }
+ },
+ { /* version 1 */
+ { /* version 1, passes 0 */
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000001},
+ {0x00000000,0x00000000,0x00000009,0x00000009,
+ 0x00000009,0x00000009,0x00000009,0x00000009},
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000049,0x00000049,0x00000049,0x00000049},
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000049,0x00000249,0x0000024a,0x0000024a},
+ {0x00000000,0x00000000,0x00000049,0x00000249,
+ 0x00000249,0x00000249,0x0000024a,0x00001252},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x00000249,0x0000124a,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x0000124a,0x0000124a,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x00000249,0x00001249,
+ 0x0000124a,0x00009252,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x00000249,0x00001249,
+ 0x00009252,0x00009292,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x00000249,0x00001249,
+ 0x00009252,0x00009292,0x00009493,0x00009493},
+ {0x00000000,0x00000000,0x00000249,0x0000924a,
+ 0x00009252,0x00009493,0x00009493,0x00009493},
+ {0x00000000,0x00000000,0x00000249,0x0000924a,
+ 0x00009292,0x00009493,0x00009493,0x00009493},
+ {0x00000000,0x00000000,0x00000249,0x00009252,
+ 0x00009492,0x00009493,0x0000a49b,0x0000a49b},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x00009492,0x000124db,0x000124db,0x000124db},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x000126dc,0x000126dc,0x000126dc},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ },
+ { /* version 1, passes 1 */
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000},
+ {0x00000000,0x00000000,0x00000049,0x00000009,
+ 0x00000049,0x00000009,0x00000001,0x00000000},
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000049,0x00000049,0x00000049,0x00000000},
+ {0x00000000,0x00000000,0x00000249,0x00000049,
+ 0x00000249,0x00000049,0x0000024a,0x00000001},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x00000249,0x00000249,0x0000024a,0x00000001},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x00000249,0x00000249,0x0000024a,0x00000001},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x00000249,0x00000249,0x0000024a,0x00000009},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x0000124a,0x0000124a,0x0000024a,0x00000009},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x0000124a,0x0000124a,0x0000024a,0x00000009},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x0000124a,0x00009252,0x00001252,0x00000049},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x0000124a,0x00009292,0x00001252,0x00000049},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x0000124a,0x00009292,0x00001252,0x00000049},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009252,0x00009292,0x00001252,0x0000024a},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009292,0x00009292,0x00001252,0x0000024a},
+ {0x00000000,0x00000000,0x0000924a,0x0000924a,
+ 0x00009492,0x00009493,0x00009292,0x00001252},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ }
+ },
+ { /* version 2 */
+ { /* version 2, passes 0 */
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000049,0x00000049,0x0000024a,0x0000024a},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x00000249,0x0000124a,0x00001252,0x00009292},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x0000124a,0x00009252,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x00000249,0x00001249,
+ 0x0000124a,0x00009292,0x00009493,0x00009493},
+ {0x00000000,0x00000000,0x00000249,0x00001249,
+ 0x00009252,0x00009493,0x00009493,0x0000a49b},
+ {0x00000000,0x00000000,0x00000249,0x0000924a,
+ 0x00009292,0x00009493,0x0000a49b,0x0000a49b},
+ {0x00000000,0x00000000,0x00001249,0x0000924a,
+ 0x00009292,0x00009493,0x0000a49b,0x000124db},
+ {0x00000000,0x00000000,0x00001249,0x00009252,
+ 0x00009492,0x0000a49b,0x0000a49b,0x000124db},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x00009492,0x000124db,0x000124db,0x000126dc},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x0000a493,0x000124db,0x000126dc,0x000126dc},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x000124db,0x000126dc,0x000136e4},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x000126dc,0x000136e4,0x000136e4},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0001249b,0x000126dc,0x000136e4,0x000136e4},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000124db,0x000136e4,0x000136e4,0x0001b724},
+ {0x00000000,0x00000000,0x00009252,0x000124db,
+ 0x000126dc,0x0001b724,0x0001b725,0x0001b925},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ },
+ { /* version 2, passes 1 */
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000049,0x00000049,0x00000049,0x00000049},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x00000249,0x00000249,0x0000024a,0x00000049},
+ {0x00000000,0x00000000,0x00001249,0x00000249,
+ 0x0000124a,0x0000124a,0x00001252,0x00000049},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x0000124a,0x0000124a,0x00009292,0x0000024a},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009252,0x00009292,0x00009292,0x0000024a},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009252,0x00009292,0x0000a49b,0x0000024a},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009292,0x00009493,0x0000a49b,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009292,0x00009493,0x0000a49b,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x0000924a,
+ 0x00009492,0x0000a49b,0x0000a49b,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x00009252,
+ 0x00009492,0x0000a49b,0x0000a49b,0x00009292},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x00009492,0x0000a49b,0x0000a49b,0x00009292},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x0000a49b,0x0000a49b,0x00009292},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x0000a49b,0x0000a49b,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x000124db,0x0000a49b,0x00009493},
+ {0x00000000,0x00000000,0x00009252,0x0000a49b,
+ 0x0001249b,0x000126dc,0x000124db,0x0000a49b},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ }
+ },
+ { /* version 3 */
+ { /* version 3, passes 0 */
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x0000124a,0x0000124a,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009292,0x00009493,0x0000a49b,0x0000a49b},
+ {0x00000000,0x00000000,0x00001249,0x0000924a,
+ 0x00009492,0x0000a49b,0x0000a49b,0x000124db},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x00009492,0x000124db,0x000126dc,0x000126dc},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x000124db,0x000126dc,0x000126dc},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x000126dc,0x000136e4,0x000136e4},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x000126dc,0x000136e4,0x0001b724},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0001249b,0x000126dc,0x000136e4,0x0001b724},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x000126dc,0x000136e4,0x0001b724},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x000136e4,0x0001b725,0x0001b724},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000124db,0x000136e4,0x0001b725,0x0001b925},
+ {0x00000000,0x00000000,0x00009292,0x0000a49b,
+ 0x000126dc,0x000136e4,0x0001b92d,0x0001b925},
+ {0x00000000,0x00000000,0x00009292,0x0000a49b,
+ 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d},
+ {0x00000000,0x00000000,0x00009492,0x000124db,
+ 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d},
+ {0x00000000,0x00000000,0x0000a492,0x000126db,
+ 0x000136e4,0x0001b925,0x00025bb6,0x00024b77},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ },
+ { /* version 3, passes 1 */
+ {0x00000000,0x00000000,0x00001249,0x00000249,
+ 0x0000124a,0x0000124a,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009252,0x00009292,0x00009292,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x0000924a,
+ 0x00009492,0x00009493,0x0000a49b,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x00009252,
+ 0x00009492,0x0000a49b,0x0000a49b,0x00009292},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x00009492,0x0000a49b,0x0000a49b,0x00009292},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x0000a49b,0x000126dc,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x0000a49b,0x000126dc,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x0000a49b,0x000126dc,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x000124db,0x000126dc,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x000124db,0x000126dc,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0000a493,0x000124db,0x000126dc,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x000126dc,0x000126dc,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000124db,0x000136e4,0x000126dc,0x000124db},
+ {0x00000000,0x00000000,0x00009492,0x0000a49b,
+ 0x000136e4,0x000136e4,0x000126dc,0x000124db},
+ {0x00000000,0x00000000,0x0000a492,0x000124db,
+ 0x0001b724,0x0001b724,0x000136e4,0x000126dc},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ }
+ },
+ { /* version 4 */
+ { /* version 4, passes 0 */
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000049,0x00000049,0x00000049,0x00000049},
+ {0x00000000,0x00000000,0x00000249,0x00000049,
+ 0x00000249,0x00000249,0x0000024a,0x00000049},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x0000124a,0x00009252,0x00001252,0x0000024a},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009252,0x00009292,0x00009493,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x0000924a,
+ 0x00009292,0x00009493,0x00009493,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x00009492,0x0000a49b,0x0000a49b,0x00009292},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x000124db,0x000124db,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x000124db,0x000126dc,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x000124db,0x000126dc,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0001249b,0x000126dc,0x000126dc,0x000124db},
+ {0x00000000,0x00000000,0x00009252,0x00009493,
+ 0x000124db,0x000136e4,0x000136e4,0x000126dc},
+ {0x00000000,0x00000000,0x00009252,0x0000a49b,
+ 0x000124db,0x000136e4,0x000136e4,0x000126dc},
+ {0x00000000,0x00000000,0x00009292,0x0000a49b,
+ 0x000126dc,0x000136e4,0x000136e4,0x000136e4},
+ {0x00000000,0x00000000,0x00009492,0x0000a49b,
+ 0x000126dc,0x0001b724,0x0001b725,0x0001b724},
+ {0x00000000,0x00000000,0x0000a492,0x000124db,
+ 0x000136e4,0x0001b925,0x0001b92d,0x0001b925},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ },
+ { /* version 4, passes 1 */
+ {0x00000000,0x00000000,0x00000249,0x00000049,
+ 0x00000009,0x00000009,0x00000009,0x00000009},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x00000049,0x00000049,0x00000009,0x00000009},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x0000124a,0x00000249,0x00000049,0x00000049},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x0000124a,0x0000124a,0x00000049,0x00000049},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009252,0x0000124a,0x0000024a,0x0000024a},
+ {0x00000000,0x00000000,0x00001249,0x0000924a,
+ 0x00009252,0x0000124a,0x0000024a,0x0000024a},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x00009492,0x00009252,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x00009292,0x00009292,0x00001252},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x00009292,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x00009493,0x00009493,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0000a493,0x0000a49b,0x00009493,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0000a493,0x0000a49b,0x0000a49b,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x000124db,0x0000a49b,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000136e4,0x000126dc,0x000124db,0x0000a49b},
+ {0x00000000,0x00000000,0x00009252,0x000124db,
+ 0x0001b724,0x000136e4,0x000126dc,0x000124db},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ }
+ },
+ { /* version 5 */
+ { /* version 5, passes 0 */
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x00000249,0x00000249,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009252,0x00009292,0x00009292,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x0000924a,
+ 0x00009492,0x0000a49b,0x0000a49b,0x00009292},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x0000a49b,0x000124db,0x00009493},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x000124db,0x000126dc,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x000126dc,0x000126dc,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x000126dc,0x000136e4,0x000124db},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000126dc,0x000136e4,0x000136e4,0x000126dc},
+ {0x00000000,0x00000000,0x00009292,0x0000a49b,
+ 0x000126dc,0x000136e4,0x000136e4,0x000126dc},
+ {0x00000000,0x00000000,0x00009292,0x0000a49b,
+ 0x000126dc,0x0001b724,0x0001b725,0x000136e4},
+ {0x00000000,0x00000000,0x00009292,0x0000a49b,
+ 0x000136e4,0x0001b724,0x0001b92d,0x0001b724},
+ {0x00000000,0x00000000,0x00009492,0x0000a49b,
+ 0x000136e4,0x0001b724,0x0001b92d,0x0001b724},
+ {0x00000000,0x00000000,0x00009492,0x000124db,
+ 0x000136e4,0x0001b925,0x0001c96e,0x0001b925},
+ {0x00000000,0x00000000,0x00009492,0x000124db,
+ 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d},
+ {0x00000000,0x00000000,0x0000a492,0x000126db,
+ 0x0001c924,0x0002496d,0x00025bb6,0x00024b77},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ },
+ { /* version 5, passes 1 */
+ {0x00000000,0x00000000,0x00001249,0x00000249,
+ 0x00000249,0x00000249,0x0000024a,0x0000024a},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x0000124a,0x0000124a,0x0000024a,0x0000024a},
+ {0x00000000,0x00000000,0x00001249,0x0000924a,
+ 0x00009252,0x00009252,0x0000024a,0x0000024a},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x00009492,0x0000a49b,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x0000a49b,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x0000a49b,0x00009292,0x00001252},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0000a493,0x0000a49b,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0000a493,0x0000a49b,0x00009493,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x000124db,0x00009493,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x000124db,0x00009493,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000124db,0x000124db,0x0000a49b,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x000124db,
+ 0x000126dc,0x000126dc,0x0000a49b,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x000124db,
+ 0x000136e4,0x000126dc,0x000124db,0x0000a49b},
+ {0x00000000,0x00000000,0x00009292,0x000124db,
+ 0x000136e4,0x000126dc,0x000124db,0x0000a49b},
+ {0x00000000,0x00000000,0x00009492,0x000126db,
+ 0x0001b724,0x000136e4,0x000126dc,0x000124db},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ }
+ },
+ { /* version 6 */
+ { /* version 6, passes 0 */
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009252,0x00009292,0x00009493,0x00009493},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x0000a493,0x0000a49b,0x0000a49b,0x00009493},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x000124db,0x000124db,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x000126dc,0x000126dc,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x000126dc,0x000136e4,0x000124db},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000126dc,0x000136e4,0x000136e4,0x000126dc},
+ {0x00000000,0x00000000,0x00009292,0x0000a49b,
+ 0x000126dc,0x0001b724,0x0001b725,0x000126dc},
+ {0x00000000,0x00000000,0x00009292,0x0000a49b,
+ 0x000136e4,0x0001b724,0x0001b92d,0x000136e4},
+ {0x00000000,0x00000000,0x00009492,0x0000a49b,
+ 0x000136e4,0x0001b724,0x0001b92d,0x0001b724},
+ {0x00000000,0x00000000,0x00009492,0x000124db,
+ 0x000136e4,0x0001b724,0x0001b92d,0x0001b724},
+ {0x00000000,0x00000000,0x00009492,0x000124db,
+ 0x000136e4,0x0001b925,0x0001b92d,0x0001b925},
+ {0x00000000,0x00000000,0x00009492,0x000124db,
+ 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d},
+ {0x00000000,0x00000000,0x0000a492,0x000124db,
+ 0x0001b724,0x0001c92d,0x0001c96e,0x0001c92d},
+ {0x00000000,0x00000000,0x0000a492,0x000124db,
+ 0x0001b724,0x0001c92d,0x00024b76,0x0002496e},
+ {0x00000000,0x00000000,0x00012492,0x000126db,
+ 0x0001c924,0x00024b6d,0x0002ddb6,0x00025bbf},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ },
+ { /* version 6, passes 1 */
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x0000124a,0x0000124a,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x00009492,0x00009252,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x00009292,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0000a493,0x0000a49b,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0000a493,0x0000a49b,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x0000a49b,0x00009493,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000124db,0x000124db,0x00009493,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000124db,0x000124db,0x0000a49b,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x000124db,
+ 0x000126dc,0x000124db,0x0000a49b,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x000124db,
+ 0x000126dc,0x000126dc,0x0000a49b,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x000124db,
+ 0x000136e4,0x000126dc,0x000124db,0x0000a49b},
+ {0x00000000,0x00000000,0x00009492,0x000126db,
+ 0x000136e4,0x000126dc,0x000124db,0x0000a49b},
+ {0x00000000,0x00000000,0x00009492,0x000126db,
+ 0x0001b724,0x000136e4,0x000126dc,0x000124db},
+ {0x00000000,0x00000000,0x00009492,0x000126db,
+ 0x0001b724,0x000136e4,0x000126dc,0x000124db},
+ {0x00000000,0x00000000,0x0000a492,0x000136db,
+ 0x0001c924,0x0001b724,0x000136e4,0x000126dc},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ }
+ },
+ { /* version 7 */
+ { /* version 7, passes 0 */
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009252,0x00009292,0x00009493,0x00009493},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x000124db,0x000126dc,0x00009493},
+ {0x00000000,0x00000000,0x00001249,0x0000a49b,
+ 0x0001249b,0x000126dc,0x000126dc,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x000126dc,0x000136e4,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000126dc,0x000136e4,0x0001b725,0x000124db},
+ {0x00000000,0x00000000,0x00009292,0x0000a49b,
+ 0x000136e4,0x0001b724,0x0001b725,0x000126dc},
+ {0x00000000,0x00000000,0x00009292,0x000124db,
+ 0x000136e4,0x0001b724,0x0001b725,0x000126dc},
+ {0x00000000,0x00000000,0x00009492,0x000124db,
+ 0x000136e4,0x0001b724,0x0001c96e,0x000136e4},
+ {0x00000000,0x00000000,0x00009492,0x000124db,
+ 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724},
+ {0x00000000,0x00000000,0x0000a492,0x000124db,
+ 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724},
+ {0x00000000,0x00000000,0x0000a492,0x000124db,
+ 0x0001b724,0x0001c92d,0x0001c96e,0x0001b925},
+ {0x00000000,0x00000000,0x0000a492,0x000126db,
+ 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d},
+ {0x00000000,0x00000000,0x0000a492,0x000126db,
+ 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d},
+ {0x00000000,0x00000000,0x0000a492,0x000126db,
+ 0x0001b924,0x0001c92d,0x00024b76,0x0002496e},
+ {0x00000000,0x00000000,0x00012492,0x000136db,
+ 0x00024924,0x00024b6d,0x0002ddb6,0x00025bbf},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ },
+ { /* version 7, passes 1 */
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x0000124a,0x0000124a,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x00009492,0x00009292,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0000a493,0x0000a49b,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0000a493,0x0000a49b,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0000a493,0x0000a49b,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000126dc,0x0000a49b,0x00009493,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x000124db,
+ 0x000126dc,0x000124db,0x00009493,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x000124db,
+ 0x000136e4,0x000124db,0x0000a49b,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x000136db,
+ 0x0001b724,0x000124db,0x0000a49b,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x000136db,
+ 0x0001b724,0x000126dc,0x0000a49b,0x0000a49b},
+ {0x00000000,0x00000000,0x00009292,0x000136db,
+ 0x0001b724,0x000126dc,0x000124db,0x0000a49b},
+ {0x00000000,0x00000000,0x00009492,0x000136db,
+ 0x0001b724,0x000126dc,0x000124db,0x0000a49b},
+ {0x00000000,0x00000000,0x0000a492,0x000136db,
+ 0x0001b724,0x000136e4,0x000126dc,0x000124db},
+ {0x00000000,0x00000000,0x0000a492,0x000136db,
+ 0x0001b724,0x000136e4,0x000126dc,0x000124db},
+ {0x00000000,0x00000000,0x00012492,0x0001b6db,
+ 0x0001c924,0x0001b724,0x000136e4,0x000126dc},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ }
+ }
+};
+
diff --git a/drivers/media/video/pwc/pwc-kiara.h b/drivers/media/video/pwc/pwc-kiara.h
new file mode 100644
index 00000000..8e02b7ac
--- /dev/null
+++ b/drivers/media/video/pwc/pwc-kiara.h
@@ -0,0 +1,48 @@
+/* Linux driver for Philips webcam
+ (C) 2004-2006 Luc Saillard (luc@saillard.org)
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+ The decompression routines have been implemented by reverse-engineering the
+ Nemosoft binary pwcx module. Caveat emptor.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/* Entries for the Kiara (730/740/750) camera */
+
+#ifndef PWC_KIARA_H
+#define PWC_KIARA_H
+
+#include "pwc.h"
+
+#define PWC_FPS_MAX_KIARA 6
+
+struct Kiara_table_entry
+{
+ char alternate; /* USB alternate interface */
+ unsigned short packetsize; /* Normal packet size */
+ unsigned short bandlength; /* Bandlength when decompressing */
+ unsigned char mode[12]; /* precomputed mode settings for cam */
+};
+
+extern const struct Kiara_table_entry Kiara_table[PSZ_MAX][PWC_FPS_MAX_KIARA][4];
+extern const unsigned int KiaraRomTable[8][2][16][8];
+extern const unsigned int Kiara_fps_vector[PWC_FPS_MAX_KIARA];
+
+#endif
+
+
diff --git a/drivers/media/video/pwc/pwc-misc.c b/drivers/media/video/pwc/pwc-misc.c
new file mode 100644
index 00000000..9be5adff
--- /dev/null
+++ b/drivers/media/video/pwc/pwc-misc.c
@@ -0,0 +1,93 @@
+/* Linux driver for Philips webcam
+ Various miscellaneous functions and tables.
+ (C) 1999-2003 Nemosoft Unv.
+ (C) 2004-2006 Luc Saillard (luc@saillard.org)
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+ The decompression routines have been implemented by reverse-engineering the
+ Nemosoft binary pwcx module. Caveat emptor.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+
+#include "pwc.h"
+
+const int pwc_image_sizes[PSZ_MAX][2] =
+{
+ { 128, 96 }, /* sqcif */
+ { 160, 120 }, /* qsif */
+ { 176, 144 }, /* qcif */
+ { 320, 240 }, /* sif */
+ { 352, 288 }, /* cif */
+ { 640, 480 }, /* vga */
+};
+
+/* x,y -> PSZ_ */
+int pwc_get_size(struct pwc_device *pdev, int width, int height)
+{
+ int i;
+
+ /* Find the largest size supported by the camera that fits into the
+ requested size. */
+ for (i = PSZ_MAX - 1; i >= 0; i--) {
+ if (!(pdev->image_mask & (1 << i)))
+ continue;
+
+ if (pwc_image_sizes[i][0] <= width &&
+ pwc_image_sizes[i][1] <= height)
+ return i;
+ }
+
+ /* No mode found, return the smallest mode we have */
+ for (i = 0; i < PSZ_MAX; i++) {
+ if (pdev->image_mask & (1 << i))
+ return i;
+ }
+
+ /* Never reached there always is atleast one supported mode */
+ return 0;
+}
+
+/* initialize variables depending on type and decompressor */
+void pwc_construct(struct pwc_device *pdev)
+{
+ if (DEVICE_USE_CODEC1(pdev->type)) {
+
+ pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QCIF | 1 << PSZ_CIF;
+ pdev->vcinterface = 2;
+ pdev->vendpoint = 4;
+ pdev->frame_header_size = 0;
+ pdev->frame_trailer_size = 0;
+
+ } else if (DEVICE_USE_CODEC3(pdev->type)) {
+
+ pdev->image_mask = 1 << PSZ_QSIF | 1 << PSZ_SIF | 1 << PSZ_VGA;
+ pdev->vcinterface = 3;
+ pdev->vendpoint = 5;
+ pdev->frame_header_size = TOUCAM_HEADER_SIZE;
+ pdev->frame_trailer_size = TOUCAM_TRAILER_SIZE;
+
+ } else /* if (DEVICE_USE_CODEC2(pdev->type)) */ {
+
+ pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QSIF | 1 << PSZ_QCIF | 1 << PSZ_SIF | 1 << PSZ_CIF | 1 << PSZ_VGA;
+ pdev->vcinterface = 3;
+ pdev->vendpoint = 4;
+ pdev->frame_header_size = 0;
+ pdev->frame_trailer_size = 0;
+ }
+}
diff --git a/drivers/media/video/pwc/pwc-nala.h b/drivers/media/video/pwc/pwc-nala.h
new file mode 100644
index 00000000..168c73ef
--- /dev/null
+++ b/drivers/media/video/pwc/pwc-nala.h
@@ -0,0 +1,66 @@
+ /* SQCIF */
+ {
+ {0, 0, {0x04, 0x01, 0x03}},
+ {8, 0, {0x05, 0x01, 0x03}},
+ {7, 0, {0x08, 0x01, 0x03}},
+ {7, 0, {0x0A, 0x01, 0x03}},
+ {6, 0, {0x0C, 0x01, 0x03}},
+ {5, 0, {0x0F, 0x01, 0x03}},
+ {4, 0, {0x14, 0x01, 0x03}},
+ {3, 0, {0x18, 0x01, 0x03}},
+ },
+ /* QSIF */
+ {
+ {0},
+ {0},
+ {0},
+ {0},
+ {0},
+ {0},
+ {0},
+ {0},
+ },
+ /* QCIF */
+ {
+ {0, 0, {0x04, 0x01, 0x02}},
+ {8, 0, {0x05, 0x01, 0x02}},
+ {7, 0, {0x08, 0x01, 0x02}},
+ {6, 0, {0x0A, 0x01, 0x02}},
+ {5, 0, {0x0C, 0x01, 0x02}},
+ {4, 0, {0x0F, 0x01, 0x02}},
+ {1, 0, {0x14, 0x01, 0x02}},
+ {1, 0, {0x18, 0x01, 0x02}},
+ },
+ /* SIF */
+ {
+ {0},
+ {0},
+ {0},
+ {0},
+ {0},
+ {0},
+ {0},
+ {0},
+ },
+ /* CIF */
+ {
+ {4, 0, {0x04, 0x01, 0x01}},
+ {7, 1, {0x05, 0x03, 0x01}},
+ {6, 1, {0x08, 0x03, 0x01}},
+ {4, 1, {0x0A, 0x03, 0x01}},
+ {3, 1, {0x0C, 0x03, 0x01}},
+ {2, 1, {0x0F, 0x03, 0x01}},
+ {0},
+ {0},
+ },
+ /* VGA */
+ {
+ {0},
+ {0},
+ {0},
+ {0},
+ {0},
+ {0},
+ {0},
+ {0},
+ },
diff --git a/drivers/media/video/pwc/pwc-timon.c b/drivers/media/video/pwc/pwc-timon.c
new file mode 100644
index 00000000..c56c174b
--- /dev/null
+++ b/drivers/media/video/pwc/pwc-timon.c
@@ -0,0 +1,1448 @@
+/* Linux driver for Philips webcam
+ (C) 2004-2006 Luc Saillard (luc@saillard.org)
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+ The decompression routines have been implemented by reverse-engineering the
+ Nemosoft binary pwcx module. Caveat emptor.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+
+/* This tables contains entries for the 675/680/690 (Timon) camera, with
+ 4 different qualities (no compression, low, medium, high).
+ It lists the bandwidth requirements for said mode by its alternate interface
+ number. An alternate of 0 means that the mode is unavailable.
+
+ There are 6 * 4 * 4 entries:
+ 6 different resolutions subqcif, qsif, qcif, sif, cif, vga
+ 6 framerates: 5, 10, 15, 20, 25, 30
+ 4 compression modi: none, low, medium, high
+
+ When an uncompressed mode is not available, the next available compressed mode
+ will be chosen (unless the decompressor is absent). Sometimes there are only
+ 1 or 2 compressed modes available; in that case entries are duplicated.
+*/
+
+#include "pwc-timon.h"
+
+const unsigned int Timon_fps_vector[PWC_FPS_MAX_TIMON] = { 5, 10, 15, 20, 25, 30 };
+
+const struct Timon_table_entry Timon_table[PSZ_MAX][PWC_FPS_MAX_TIMON][4] =
+{
+ /* SQCIF */
+ {
+ /* 5 fps */
+ {
+ {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}},
+ {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}},
+ {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}},
+ {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}},
+ },
+ /* 10 fps */
+ {
+ {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}},
+ {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}},
+ {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}},
+ {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}},
+ },
+ /* 15 fps */
+ {
+ {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}},
+ {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}},
+ {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}},
+ {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}},
+ },
+ /* 20 fps */
+ {
+ {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}},
+ {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}},
+ {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}},
+ {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}},
+ },
+ /* 25 fps */
+ {
+ {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}},
+ {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}},
+ {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}},
+ {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}},
+ },
+ /* 30 fps */
+ {
+ {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}},
+ {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}},
+ {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}},
+ {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}},
+ },
+ },
+ /* QSIF */
+ {
+ /* 5 fps */
+ {
+ {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}},
+ {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}},
+ {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}},
+ {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}},
+ },
+ /* 10 fps */
+ {
+ {2, 291, 0, {0x2C, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x23, 0xA1, 0xC0, 0x02}},
+ {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}},
+ {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}},
+ {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}},
+ },
+ /* 15 fps */
+ {
+ {3, 437, 0, {0x2B, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xB5, 0x6D, 0xC0, 0x02}},
+ {2, 291, 640, {0x2B, 0xF4, 0x05, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x08, 0x23, 0xA1, 0xC0, 0x02}},
+ {2, 291, 640, {0x2B, 0xF4, 0x05, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x08, 0x23, 0xA1, 0xC0, 0x02}},
+ {1, 191, 420, {0x2B, 0xF4, 0x0D, 0x0D, 0x1B, 0x0C, 0x53, 0x1E, 0x08, 0xBF, 0xF4, 0xC0, 0x02}},
+ },
+ /* 20 fps */
+ {
+ {4, 588, 0, {0x2A, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x4C, 0x52, 0xC0, 0x02}},
+ {3, 447, 730, {0x2A, 0xF4, 0x05, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x18, 0xBF, 0x69, 0xC0, 0x02}},
+ {2, 292, 476, {0x2A, 0xF4, 0x0D, 0x0E, 0xD8, 0x0E, 0x10, 0x19, 0x18, 0x24, 0xA1, 0xC0, 0x02}},
+ {1, 192, 312, {0x2A, 0xF4, 0x1D, 0x09, 0xB3, 0x08, 0xEB, 0x1E, 0x18, 0xC0, 0xF4, 0xC0, 0x02}},
+ },
+ /* 25 fps */
+ {
+ {5, 703, 0, {0x29, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xBF, 0x42, 0xC0, 0x02}},
+ {3, 447, 610, {0x29, 0xF4, 0x05, 0x13, 0x0B, 0x12, 0x43, 0x14, 0x18, 0xBF, 0x69, 0xC0, 0x02}},
+ {2, 292, 398, {0x29, 0xF4, 0x0D, 0x0C, 0x6C, 0x0B, 0xA4, 0x1E, 0x18, 0x24, 0xA1, 0xC0, 0x02}},
+ {1, 192, 262, {0x29, 0xF4, 0x25, 0x08, 0x23, 0x07, 0x5B, 0x1E, 0x18, 0xC0, 0xF4, 0xC0, 0x02}},
+ },
+ /* 30 fps */
+ {
+ {8, 873, 0, {0x28, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x69, 0x37, 0xC0, 0x02}},
+ {5, 704, 774, {0x28, 0xF4, 0x05, 0x18, 0x21, 0x17, 0x59, 0x0F, 0x18, 0xC0, 0x42, 0xC0, 0x02}},
+ {3, 448, 492, {0x28, 0xF4, 0x05, 0x0F, 0x5D, 0x0E, 0x95, 0x15, 0x18, 0xC0, 0x69, 0xC0, 0x02}},
+ {2, 291, 320, {0x28, 0xF4, 0x1D, 0x09, 0xFB, 0x09, 0x33, 0x1E, 0x18, 0x23, 0xA1, 0xC0, 0x02}},
+ },
+ },
+ /* QCIF */
+ {
+ /* 5 fps */
+ {
+ {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}},
+ {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}},
+ {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}},
+ {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}},
+ },
+ /* 10 fps */
+ {
+ {3, 385, 0, {0x0C, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x81, 0x79, 0xC0, 0x02}},
+ {2, 291, 800, {0x0C, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x11, 0x08, 0x23, 0xA1, 0xC0, 0x02}},
+ {2, 291, 800, {0x0C, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x11, 0x08, 0x23, 0xA1, 0xC0, 0x02}},
+ {1, 194, 532, {0x0C, 0xF4, 0x05, 0x10, 0x9A, 0x0F, 0xBE, 0x1B, 0x08, 0xC2, 0xF0, 0xC0, 0x02}},
+ },
+ /* 15 fps */
+ {
+ {4, 577, 0, {0x0B, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x41, 0x52, 0xC0, 0x02}},
+ {3, 447, 818, {0x0B, 0xF4, 0x05, 0x19, 0x89, 0x18, 0xAD, 0x0F, 0x10, 0xBF, 0x69, 0xC0, 0x02}},
+ {2, 292, 534, {0x0B, 0xF4, 0x05, 0x10, 0xA3, 0x0F, 0xC7, 0x19, 0x10, 0x24, 0xA1, 0xC0, 0x02}},
+ {1, 195, 356, {0x0B, 0xF4, 0x15, 0x0B, 0x11, 0x0A, 0x35, 0x1E, 0x10, 0xC3, 0xF0, 0xC0, 0x02}},
+ },
+ /* 20 fps */
+ {
+ {6, 776, 0, {0x0A, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x08, 0x3F, 0xC0, 0x02}},
+ {4, 591, 804, {0x0A, 0xF4, 0x05, 0x19, 0x1E, 0x18, 0x42, 0x0F, 0x18, 0x4F, 0x4E, 0xC0, 0x02}},
+ {3, 447, 608, {0x0A, 0xF4, 0x05, 0x12, 0xFD, 0x12, 0x21, 0x15, 0x18, 0xBF, 0x69, 0xC0, 0x02}},
+ {2, 291, 396, {0x0A, 0xF4, 0x15, 0x0C, 0x5E, 0x0B, 0x82, 0x1E, 0x18, 0x23, 0xA1, 0xC0, 0x02}},
+ },
+ /* 25 fps */
+ {
+ {9, 928, 0, {0x09, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xA0, 0x33, 0xC0, 0x02}},
+ {5, 703, 800, {0x09, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x10, 0x18, 0xBF, 0x42, 0xC0, 0x02}},
+ {3, 447, 508, {0x09, 0xF4, 0x0D, 0x0F, 0xD2, 0x0E, 0xF6, 0x1B, 0x18, 0xBF, 0x69, 0xC0, 0x02}},
+ {2, 292, 332, {0x09, 0xF4, 0x1D, 0x0A, 0x5A, 0x09, 0x7E, 0x1E, 0x18, 0x24, 0xA1, 0xC0, 0x02}},
+ },
+ /* 30 fps */
+ {
+ {0, },
+ {9, 956, 876, {0x08, 0xF4, 0x05, 0x1B, 0x58, 0x1A, 0x7C, 0x0E, 0x20, 0xBC, 0x33, 0x10, 0x02}},
+ {4, 592, 542, {0x08, 0xF4, 0x05, 0x10, 0xE4, 0x10, 0x08, 0x17, 0x20, 0x50, 0x4E, 0x10, 0x02}},
+ {2, 291, 266, {0x08, 0xF4, 0x25, 0x08, 0x48, 0x07, 0x6C, 0x1E, 0x20, 0x23, 0xA1, 0x10, 0x02}},
+ },
+ },
+ /* SIF */
+ {
+ /* 5 fps */
+ {
+ {4, 582, 0, {0x35, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x46, 0x52, 0x60, 0x02}},
+ {3, 387, 1276, {0x35, 0xF4, 0x05, 0x27, 0xD8, 0x26, 0x48, 0x03, 0x10, 0x83, 0x79, 0x60, 0x02}},
+ {2, 291, 960, {0x35, 0xF4, 0x0D, 0x1D, 0xF2, 0x1C, 0x62, 0x04, 0x10, 0x23, 0xA1, 0x60, 0x02}},
+ {1, 191, 630, {0x35, 0xF4, 0x1D, 0x13, 0xA9, 0x12, 0x19, 0x05, 0x08, 0xBF, 0xF4, 0x60, 0x02}},
+ },
+ /* 10 fps */
+ {
+ {0, },
+ {6, 775, 1278, {0x34, 0xF4, 0x05, 0x27, 0xE8, 0x26, 0x58, 0x05, 0x30, 0x07, 0x3F, 0x10, 0x02}},
+ {3, 447, 736, {0x34, 0xF4, 0x15, 0x16, 0xFB, 0x15, 0x6B, 0x05, 0x18, 0xBF, 0x69, 0x10, 0x02}},
+ {2, 291, 480, {0x34, 0xF4, 0x2D, 0x0E, 0xF9, 0x0D, 0x69, 0x09, 0x18, 0x23, 0xA1, 0x10, 0x02}},
+ },
+ /* 15 fps */
+ {
+ {0, },
+ {9, 955, 1050, {0x33, 0xF4, 0x05, 0x20, 0xCF, 0x1F, 0x3F, 0x06, 0x48, 0xBB, 0x33, 0x10, 0x02}},
+ {4, 591, 650, {0x33, 0xF4, 0x15, 0x14, 0x44, 0x12, 0xB4, 0x08, 0x30, 0x4F, 0x4E, 0x10, 0x02}},
+ {3, 448, 492, {0x33, 0xF4, 0x25, 0x0F, 0x52, 0x0D, 0xC2, 0x09, 0x28, 0xC0, 0x69, 0x10, 0x02}},
+ },
+ /* 20 fps */
+ {
+ {0, },
+ {9, 958, 782, {0x32, 0xF4, 0x0D, 0x18, 0x6A, 0x16, 0xDA, 0x0B, 0x58, 0xBE, 0x33, 0xD0, 0x02}},
+ {5, 703, 574, {0x32, 0xF4, 0x1D, 0x11, 0xE7, 0x10, 0x57, 0x0B, 0x40, 0xBF, 0x42, 0xD0, 0x02}},
+ {3, 446, 364, {0x32, 0xF4, 0x3D, 0x0B, 0x5C, 0x09, 0xCC, 0x0E, 0x30, 0xBE, 0x69, 0xD0, 0x02}},
+ },
+ /* 25 fps */
+ {
+ {0, },
+ {9, 958, 654, {0x31, 0xF4, 0x15, 0x14, 0x66, 0x12, 0xD6, 0x0B, 0x50, 0xBE, 0x33, 0x90, 0x02}},
+ {6, 776, 530, {0x31, 0xF4, 0x25, 0x10, 0x8C, 0x0E, 0xFC, 0x0C, 0x48, 0x08, 0x3F, 0x90, 0x02}},
+ {4, 592, 404, {0x31, 0xF4, 0x35, 0x0C, 0x96, 0x0B, 0x06, 0x0B, 0x38, 0x50, 0x4E, 0x90, 0x02}},
+ },
+ /* 30 fps */
+ {
+ {0, },
+ {9, 957, 526, {0x30, 0xF4, 0x25, 0x10, 0x68, 0x0E, 0xD8, 0x0D, 0x58, 0xBD, 0x33, 0x60, 0x02}},
+ {6, 775, 426, {0x30, 0xF4, 0x35, 0x0D, 0x48, 0x0B, 0xB8, 0x0F, 0x50, 0x07, 0x3F, 0x60, 0x02}},
+ {4, 590, 324, {0x30, 0x7A, 0x4B, 0x0A, 0x1C, 0x08, 0xB4, 0x0E, 0x40, 0x4E, 0x52, 0x60, 0x02}},
+ },
+ },
+ /* CIF */
+ {
+ /* 5 fps */
+ {
+ {6, 771, 0, {0x15, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x3F, 0x80, 0x02}},
+ {4, 465, 1278, {0x15, 0xF4, 0x05, 0x27, 0xEE, 0x26, 0x36, 0x03, 0x18, 0xD1, 0x65, 0x80, 0x02}},
+ {2, 291, 800, {0x15, 0xF4, 0x15, 0x18, 0xF4, 0x17, 0x3C, 0x05, 0x18, 0x23, 0xA1, 0x80, 0x02}},
+ {1, 193, 528, {0x15, 0xF4, 0x2D, 0x10, 0x7E, 0x0E, 0xC6, 0x0A, 0x18, 0xC1, 0xF4, 0x80, 0x02}},
+ },
+ /* 10 fps */
+ {
+ {0, },
+ {9, 932, 1278, {0x14, 0xF4, 0x05, 0x27, 0xEE, 0x26, 0x36, 0x04, 0x30, 0xA4, 0x33, 0x10, 0x02}},
+ {4, 591, 812, {0x14, 0xF4, 0x15, 0x19, 0x56, 0x17, 0x9E, 0x06, 0x28, 0x4F, 0x4E, 0x10, 0x02}},
+ {2, 291, 400, {0x14, 0xF4, 0x3D, 0x0C, 0x7A, 0x0A, 0xC2, 0x0E, 0x28, 0x23, 0xA1, 0x10, 0x02}},
+ },
+ /* 15 fps */
+ {
+ {0, },
+ {9, 956, 876, {0x13, 0xF4, 0x0D, 0x1B, 0x58, 0x19, 0xA0, 0x05, 0x38, 0xBC, 0x33, 0x60, 0x02}},
+ {5, 703, 644, {0x13, 0xF4, 0x1D, 0x14, 0x1C, 0x12, 0x64, 0x08, 0x38, 0xBF, 0x42, 0x60, 0x02}},
+ {3, 448, 410, {0x13, 0xF4, 0x3D, 0x0C, 0xC4, 0x0B, 0x0C, 0x0E, 0x38, 0xC0, 0x69, 0x60, 0x02}},
+ },
+ /* 20 fps */
+ {
+ {0, },
+ {9, 956, 650, {0x12, 0xF4, 0x1D, 0x14, 0x4A, 0x12, 0x92, 0x09, 0x48, 0xBC, 0x33, 0x10, 0x03}},
+ {6, 776, 528, {0x12, 0xF4, 0x2D, 0x10, 0x7E, 0x0E, 0xC6, 0x0A, 0x40, 0x08, 0x3F, 0x10, 0x03}},
+ {4, 591, 402, {0x12, 0xF4, 0x3D, 0x0C, 0x8F, 0x0A, 0xD7, 0x0E, 0x40, 0x4F, 0x4E, 0x10, 0x03}},
+ },
+ /* 25 fps */
+ {
+ {0, },
+ {9, 956, 544, {0x11, 0xF4, 0x25, 0x10, 0xF4, 0x0F, 0x3C, 0x0A, 0x48, 0xBC, 0x33, 0xC0, 0x02}},
+ {7, 840, 478, {0x11, 0xF4, 0x2D, 0x0E, 0xEB, 0x0D, 0x33, 0x0B, 0x48, 0x48, 0x3B, 0xC0, 0x02}},
+ {5, 703, 400, {0x11, 0xF4, 0x3D, 0x0C, 0x7A, 0x0A, 0xC2, 0x0E, 0x48, 0xBF, 0x42, 0xC0, 0x02}},
+ },
+ /* 30 fps */
+ {
+ {0, },
+ {9, 956, 438, {0x10, 0xF4, 0x35, 0x0D, 0xAC, 0x0B, 0xF4, 0x0D, 0x50, 0xBC, 0x33, 0x10, 0x02}},
+ {7, 838, 384, {0x10, 0xF4, 0x45, 0x0B, 0xFD, 0x0A, 0x45, 0x0F, 0x50, 0x46, 0x3B, 0x10, 0x02}},
+ {6, 773, 354, {0x10, 0x7A, 0x4B, 0x0B, 0x0C, 0x09, 0x80, 0x10, 0x50, 0x05, 0x3F, 0x10, 0x02}},
+ },
+ },
+ /* VGA */
+ {
+ /* 5 fps */
+ {
+ {0, },
+ {6, 773, 1272, {0x1D, 0xF4, 0x15, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x3F, 0x10, 0x02}},
+ {4, 592, 976, {0x1D, 0xF4, 0x25, 0x1E, 0x78, 0x1B, 0x58, 0x03, 0x30, 0x50, 0x4E, 0x10, 0x02}},
+ {3, 448, 738, {0x1D, 0xF4, 0x3D, 0x17, 0x0C, 0x13, 0xEC, 0x04, 0x30, 0xC0, 0x69, 0x10, 0x02}},
+ },
+ /* 10 fps */
+ {
+ {0, },
+ {9, 956, 788, {0x1C, 0xF4, 0x35, 0x18, 0x9C, 0x15, 0x7C, 0x03, 0x48, 0xBC, 0x33, 0x10, 0x02}},
+ {6, 776, 640, {0x1C, 0x7A, 0x53, 0x13, 0xFC, 0x11, 0x2C, 0x04, 0x48, 0x08, 0x3F, 0x10, 0x02}},
+ {4, 592, 488, {0x1C, 0x7A, 0x6B, 0x0F, 0x3C, 0x0C, 0x6C, 0x06, 0x48, 0x50, 0x4E, 0x10, 0x02}},
+ },
+ /* 15 fps */
+ {
+ {0, },
+ {9, 957, 526, {0x1B, 0x7A, 0x63, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x33, 0x80, 0x02}},
+ {9, 957, 526, {0x1B, 0x7A, 0x63, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x33, 0x80, 0x02}},
+ {8, 895, 492, {0x1B, 0x7A, 0x6B, 0x0F, 0x5D, 0x0C, 0x8D, 0x06, 0x58, 0x7F, 0x37, 0x80, 0x02}},
+ },
+ /* 20 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 25 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ /* 30 fps */
+ {
+ {0, },
+ {0, },
+ {0, },
+ {0, },
+ },
+ },
+};
+
+/*
+ * 16 versions:
+ * 2 tables (one for Y, and one for U&V)
+ * 16 levels of details per tables
+ * 8 blocs
+ */
+
+const unsigned int TimonRomTable [16][2][16][8] =
+{
+ { /* version 0 */
+ { /* version 0, passes 0 */
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000001},
+ {0x00000000,0x00000000,0x00000001,0x00000001,
+ 0x00000001,0x00000001,0x00000001,0x00000001},
+ {0x00000000,0x00000000,0x00000001,0x00000001,
+ 0x00000001,0x00000009,0x00000009,0x00000009},
+ {0x00000000,0x00000000,0x00000009,0x00000001,
+ 0x00000009,0x00000009,0x00000009,0x00000009},
+ {0x00000000,0x00000000,0x00000009,0x00000009,
+ 0x00000009,0x00000009,0x00000049,0x00000009},
+ {0x00000000,0x00000000,0x00000009,0x00000009,
+ 0x00000009,0x00000049,0x00000049,0x00000049},
+ {0x00000000,0x00000000,0x00000009,0x00000009,
+ 0x00000049,0x00000049,0x00000049,0x00000049},
+ {0x00000000,0x00000000,0x00000009,0x00000049,
+ 0x00000049,0x00000049,0x00000049,0x00000049},
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000049,0x00000049,0x0000024a,0x0000024a},
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000049,0x00000249,0x0000024a,0x0000024a},
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000249,0x00000249,0x0000024a,0x0000024a},
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000249,0x00000249,0x00001252,0x0000024a},
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000249,0x0000124a,0x00001252,0x0000024a},
+ {0x00000000,0x00000000,0x00000049,0x00000249,
+ 0x00000249,0x0000124a,0x00001252,0x0000024a},
+ {0x00000000,0x00000000,0x00000249,0x00001249,
+ 0x0000124a,0x00009252,0x00009292,0x00001252},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ },
+ { /* version 0, passes 1 */
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000},
+ {0x00000000,0x00000000,0x00000001,0x00000001,
+ 0x00000001,0x00000001,0x00000000,0x00000000},
+ {0x00000000,0x00000000,0x00000009,0x00000001,
+ 0x00000001,0x00000009,0x00000000,0x00000000},
+ {0x00000000,0x00000000,0x00000009,0x00000009,
+ 0x00000009,0x00000009,0x00000000,0x00000000},
+ {0x00000000,0x00000000,0x00000009,0x00000009,
+ 0x00000009,0x00000009,0x00000001,0x00000000},
+ {0x00000000,0x00000000,0x00000049,0x00000009,
+ 0x00000009,0x00000049,0x00000001,0x00000001},
+ {0x00000000,0x00000000,0x00000049,0x00000009,
+ 0x00000009,0x00000049,0x00000001,0x00000001},
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000049,0x00000049,0x00000009,0x00000001},
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000049,0x00000049,0x00000009,0x00000001},
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000049,0x00000049,0x00000009,0x00000001},
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000049,0x00000049,0x00000009,0x00000009},
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000049,0x00000249,0x00000049,0x00000009},
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000049,0x00000249,0x00000049,0x00000009},
+ {0x00000000,0x00000000,0x00000249,0x00000049,
+ 0x00000249,0x00000249,0x00000049,0x00000009},
+ {0x00000000,0x00000000,0x00001249,0x00000249,
+ 0x0000124a,0x0000124a,0x0000024a,0x00000049},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ }
+ },
+ { /* version 1 */
+ { /* version 1, passes 0 */
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000001},
+ {0x00000000,0x00000000,0x00000001,0x00000001,
+ 0x00000001,0x00000009,0x00000009,0x00000009},
+ {0x00000000,0x00000000,0x00000009,0x00000009,
+ 0x00000009,0x00000009,0x00000009,0x00000009},
+ {0x00000000,0x00000000,0x00000009,0x00000009,
+ 0x00000009,0x00000049,0x00000049,0x00000049},
+ {0x00000000,0x00000000,0x00000009,0x00000049,
+ 0x00000049,0x00000049,0x00000049,0x00000049},
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000049,0x00000249,0x0000024a,0x0000024a},
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000249,0x00000249,0x0000024a,0x0000024a},
+ {0x00000000,0x00000000,0x00000049,0x00000249,
+ 0x00000249,0x00000249,0x0000024a,0x00001252},
+ {0x00000000,0x00000000,0x00000049,0x00000249,
+ 0x00000249,0x0000124a,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x00000049,0x00000249,
+ 0x0000124a,0x0000124a,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x0000124a,0x0000124a,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x00000249,0x00001249,
+ 0x0000124a,0x00009252,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x00000249,0x00001249,
+ 0x00009252,0x00009252,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x00000249,0x0000924a,
+ 0x00009292,0x00009493,0x00009493,0x00009493},
+ {0x00000000,0x00000000,0x00001249,0x00009252,
+ 0x00009492,0x0000a49b,0x0000a49b,0x0000a49b},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ },
+ { /* version 1, passes 1 */
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000},
+ {0x00000000,0x00000000,0x00000009,0x00000009,
+ 0x00000009,0x00000001,0x00000001,0x00000000},
+ {0x00000000,0x00000000,0x00000009,0x00000009,
+ 0x00000009,0x00000009,0x00000001,0x00000000},
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000049,0x00000009,0x00000001,0x00000000},
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000049,0x00000049,0x00000001,0x00000001},
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000049,0x00000049,0x00000009,0x00000001},
+ {0x00000000,0x00000000,0x00000249,0x00000049,
+ 0x00000049,0x00000249,0x00000009,0x00000001},
+ {0x00000000,0x00000000,0x00000249,0x00000049,
+ 0x00000249,0x00000249,0x00000009,0x00000009},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x00000249,0x00000249,0x00000049,0x00000009},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x00000249,0x0000124a,0x00000049,0x00000009},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x00000249,0x0000124a,0x00000049,0x00000009},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x00000249,0x0000124a,0x0000024a,0x00000049},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x0000124a,0x0000124a,0x0000024a,0x00000049},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x0000124a,0x0000124a,0x0000024a,0x00000049},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009252,0x00009252,0x00001252,0x0000024a},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ }
+ },
+ { /* version 2 */
+ { /* version 2, passes 0 */
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000001},
+ {0x00000000,0x00000000,0x00000009,0x00000009,
+ 0x00000009,0x00000009,0x00000009,0x00000009},
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000049,0x00000049,0x00000049,0x00000049},
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000049,0x00000249,0x0000024a,0x0000024a},
+ {0x00000000,0x00000000,0x00000049,0x00000249,
+ 0x00000249,0x00000249,0x0000024a,0x00001252},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x00000249,0x0000124a,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x0000124a,0x0000124a,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x00000249,0x00001249,
+ 0x0000124a,0x00009252,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x00000249,0x00001249,
+ 0x00009252,0x00009292,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x00000249,0x00001249,
+ 0x00009252,0x00009292,0x00009493,0x00009493},
+ {0x00000000,0x00000000,0x00000249,0x0000924a,
+ 0x00009252,0x00009493,0x00009493,0x00009493},
+ {0x00000000,0x00000000,0x00000249,0x0000924a,
+ 0x00009292,0x00009493,0x00009493,0x00009493},
+ {0x00000000,0x00000000,0x00000249,0x00009252,
+ 0x00009492,0x00009493,0x0000a49b,0x0000a49b},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x00009492,0x000124db,0x000124db,0x000124db},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x000126dc,0x000126dc,0x000126dc},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ },
+ { /* version 2, passes 1 */
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000},
+ {0x00000000,0x00000000,0x00000049,0x00000009,
+ 0x00000049,0x00000009,0x00000001,0x00000000},
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000049,0x00000049,0x00000049,0x00000000},
+ {0x00000000,0x00000000,0x00000249,0x00000049,
+ 0x00000249,0x00000049,0x0000024a,0x00000001},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x00000249,0x00000249,0x0000024a,0x00000001},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x00000249,0x00000249,0x0000024a,0x00000001},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x00000249,0x00000249,0x0000024a,0x00000009},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x0000124a,0x0000124a,0x0000024a,0x00000009},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x0000124a,0x0000124a,0x0000024a,0x00000009},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x0000124a,0x00009252,0x00001252,0x00000049},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x0000124a,0x00009292,0x00001252,0x00000049},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x0000124a,0x00009292,0x00001252,0x00000049},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009252,0x00009292,0x00001252,0x0000024a},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009292,0x00009292,0x00001252,0x0000024a},
+ {0x00000000,0x00000000,0x0000924a,0x0000924a,
+ 0x00009492,0x00009493,0x00009292,0x00001252},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ }
+ },
+ { /* version 3 */
+ { /* version 3, passes 0 */
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000001},
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000049,0x00000049,0x00000049,0x00000049},
+ {0x00000000,0x00000000,0x00000049,0x00000249,
+ 0x00000249,0x00000249,0x00001252,0x0000024a},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x00000249,0x0000124a,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x0000124a,0x00009252,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x00000249,0x00001249,
+ 0x0000124a,0x00009292,0x00009292,0x00009493},
+ {0x00000000,0x00000000,0x00000249,0x00001249,
+ 0x00009252,0x00009292,0x00009493,0x00009493},
+ {0x00000000,0x00000000,0x00000249,0x00001249,
+ 0x00009292,0x00009493,0x00009493,0x00009493},
+ {0x00000000,0x00000000,0x00000249,0x00009252,
+ 0x00009292,0x00009493,0x0000a49b,0x0000a49b},
+ {0x00000000,0x00000000,0x00001249,0x00009252,
+ 0x00009292,0x0000a49b,0x0000a49b,0x0000a49b},
+ {0x00000000,0x00000000,0x00001249,0x00009252,
+ 0x00009492,0x0000a49b,0x0000a49b,0x0000a49b},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x00009492,0x0000a49b,0x000124db,0x000124db},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x0000a493,0x0000a49b,0x000124db,0x000124db},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0001249b,0x000126dc,0x000136e4,0x000126dc},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000124db,0x000136e4,0x0001b725,0x000136e4},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ },
+ { /* version 3, passes 1 */
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000},
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000049,0x00000049,0x00000001,0x00000000},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x00000249,0x00000249,0x00000049,0x00000001},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x00000249,0x0000124a,0x00001252,0x00000001},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x0000124a,0x0000124a,0x00001252,0x00000009},
+ {0x00000000,0x00000000,0x00000249,0x00001249,
+ 0x0000124a,0x00009252,0x00009292,0x00000009},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x0000124a,0x00009252,0x00009292,0x00000049},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009252,0x00009252,0x00009292,0x00000049},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009252,0x00009493,0x00009292,0x0000024a},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009252,0x00009493,0x00009292,0x0000024a},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009252,0x00009493,0x00009493,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x0000924a,
+ 0x00009292,0x00009493,0x00009493,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x0000924a,
+ 0x00009492,0x00009493,0x00009493,0x00009292},
+ {0x00000000,0x00000000,0x00001249,0x00009252,
+ 0x00009492,0x0000a49b,0x00009493,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x00009292,
+ 0x0000a493,0x000124db,0x0000a49b,0x00009493},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ }
+ },
+ { /* version 4 */
+ { /* version 4, passes 0 */
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000049,0x00000049,0x0000024a,0x0000024a},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x00000249,0x0000124a,0x00001252,0x00009292},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x0000124a,0x00009252,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x00000249,0x00001249,
+ 0x0000124a,0x00009292,0x00009493,0x00009493},
+ {0x00000000,0x00000000,0x00000249,0x00001249,
+ 0x00009252,0x00009493,0x00009493,0x0000a49b},
+ {0x00000000,0x00000000,0x00000249,0x0000924a,
+ 0x00009292,0x00009493,0x0000a49b,0x0000a49b},
+ {0x00000000,0x00000000,0x00001249,0x0000924a,
+ 0x00009292,0x00009493,0x0000a49b,0x000124db},
+ {0x00000000,0x00000000,0x00001249,0x00009252,
+ 0x00009492,0x0000a49b,0x0000a49b,0x000124db},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x00009492,0x000124db,0x000124db,0x000126dc},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x0000a493,0x000124db,0x000126dc,0x000126dc},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x000124db,0x000126dc,0x000136e4},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x000126dc,0x000136e4,0x000136e4},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0001249b,0x000126dc,0x000136e4,0x000136e4},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000124db,0x000136e4,0x000136e4,0x0001b724},
+ {0x00000000,0x00000000,0x00009252,0x000124db,
+ 0x000126dc,0x0001b724,0x0001b725,0x0001b925},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ },
+ { /* version 4, passes 1 */
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000049,0x00000049,0x00000049,0x00000049},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x00000249,0x00000249,0x0000024a,0x00000049},
+ {0x00000000,0x00000000,0x00001249,0x00000249,
+ 0x0000124a,0x0000124a,0x00001252,0x00000049},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x0000124a,0x0000124a,0x00009292,0x0000024a},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009252,0x00009292,0x00009292,0x0000024a},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009252,0x00009292,0x0000a49b,0x0000024a},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009292,0x00009493,0x0000a49b,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009292,0x00009493,0x0000a49b,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x0000924a,
+ 0x00009492,0x0000a49b,0x0000a49b,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x00009252,
+ 0x00009492,0x0000a49b,0x0000a49b,0x00009292},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x00009492,0x0000a49b,0x0000a49b,0x00009292},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x0000a49b,0x0000a49b,0x00009292},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x0000a49b,0x0000a49b,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x000124db,0x0000a49b,0x00009493},
+ {0x00000000,0x00000000,0x00009252,0x0000a49b,
+ 0x0001249b,0x000126dc,0x000124db,0x0000a49b},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ }
+ },
+ { /* version 5 */
+ { /* version 5, passes 0 */
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x00000249,0x0000124a,0x00001252,0x00009292},
+ {0x00000000,0x00000000,0x00000249,0x00001249,
+ 0x0000124a,0x00009292,0x00009292,0x00009493},
+ {0x00000000,0x00000000,0x00000249,0x0000924a,
+ 0x00009292,0x00009493,0x0000a49b,0x0000a49b},
+ {0x00000000,0x00000000,0x00001249,0x0000924a,
+ 0x00009292,0x00009493,0x0000a49b,0x0000a49b},
+ {0x00000000,0x00000000,0x00001249,0x0000924a,
+ 0x00009492,0x0000a49b,0x0000a49b,0x000124db},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x00009492,0x0000a49b,0x000124db,0x000124db},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x0000a493,0x000124db,0x000124db,0x000126dc},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x000124db,0x000126dc,0x000126dc},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x000126dc,0x000136e4,0x000136e4},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0001249b,0x000126dc,0x000136e4,0x000136e4},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0001249b,0x000126dc,0x000136e4,0x000136e4},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0001249b,0x000126dc,0x0001b725,0x0001b724},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000124db,0x000126dc,0x0001b725,0x0001b724},
+ {0x00000000,0x00000000,0x00009292,0x0000a49b,
+ 0x000126dc,0x000136e4,0x0001b92d,0x0001b925},
+ {0x00000000,0x00000000,0x00009492,0x000124db,
+ 0x000136e4,0x0001b724,0x0001c96e,0x0001c92d},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ },
+ { /* version 5, passes 1 */
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x0000124a,0x00000249,0x0000024a,0x0000024a},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x0000124a,0x0000124a,0x00001252,0x0000024a},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009292,0x00009493,0x00009493,0x0000024a},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009292,0x00009493,0x00009493,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009292,0x00009493,0x0000a49b,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x0000924a,
+ 0x00009492,0x00009493,0x000124db,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x00009492,0x00009493,0x000124db,0x00009292},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x00009492,0x0000a49b,0x000124db,0x00009292},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x0000a49b,0x000124db,0x00009292},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x000124db,0x000124db,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x000124db,0x000124db,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x000124db,0x000124db,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x000124db,0x000124db,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000124db,0x000126dc,0x000124db,0x0000a49b},
+ {0x00000000,0x00000000,0x00009252,0x000124db,
+ 0x000126dc,0x000136e4,0x000126dc,0x000124db},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ }
+ },
+ { /* version 6 */
+ { /* version 6, passes 0 */
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x0000124a,0x0000124a,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009292,0x00009493,0x0000a49b,0x0000a49b},
+ {0x00000000,0x00000000,0x00001249,0x0000924a,
+ 0x00009492,0x0000a49b,0x0000a49b,0x000124db},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x00009492,0x000124db,0x000126dc,0x000126dc},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x000124db,0x000126dc,0x000126dc},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x000126dc,0x000136e4,0x000136e4},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x000126dc,0x000136e4,0x0001b724},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0001249b,0x000126dc,0x000136e4,0x0001b724},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x000126dc,0x000136e4,0x0001b724},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x000136e4,0x0001b725,0x0001b724},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000124db,0x000136e4,0x0001b725,0x0001b925},
+ {0x00000000,0x00000000,0x00009292,0x0000a49b,
+ 0x000126dc,0x000136e4,0x0001b92d,0x0001b925},
+ {0x00000000,0x00000000,0x00009292,0x0000a49b,
+ 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d},
+ {0x00000000,0x00000000,0x00009492,0x000124db,
+ 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d},
+ {0x00000000,0x00000000,0x0000a492,0x000126db,
+ 0x000136e4,0x0001b925,0x00025bb6,0x00024b77},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ },
+ { /* version 6, passes 1 */
+ {0x00000000,0x00000000,0x00001249,0x00000249,
+ 0x0000124a,0x0000124a,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009252,0x00009292,0x00009292,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x0000924a,
+ 0x00009492,0x00009493,0x0000a49b,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x00009252,
+ 0x00009492,0x0000a49b,0x0000a49b,0x00009292},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x00009492,0x0000a49b,0x0000a49b,0x00009292},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x0000a49b,0x000126dc,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x0000a49b,0x000126dc,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x0000a49b,0x000126dc,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x000124db,0x000126dc,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x000124db,0x000126dc,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0000a493,0x000124db,0x000126dc,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x000126dc,0x000126dc,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000124db,0x000136e4,0x000126dc,0x000124db},
+ {0x00000000,0x00000000,0x00009492,0x0000a49b,
+ 0x000136e4,0x000136e4,0x000126dc,0x000124db},
+ {0x00000000,0x00000000,0x0000a492,0x000124db,
+ 0x0001b724,0x0001b724,0x000136e4,0x000126dc},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ }
+ },
+ { /* version 7 */
+ { /* version 7, passes 0 */
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009292,0x00009493,0x0000a49b,0x000124db},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x0000a493,0x0000a49b,0x000124db,0x000126dc},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x000124db,0x000126dc,0x000136e4},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x000124db,0x000136e4,0x000136e4},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0001249b,0x000126dc,0x000136e4,0x000136e4},
+ {0x00000000,0x00000000,0x00001249,0x0000a49b,
+ 0x0001249b,0x000126dc,0x000136e4,0x0001b724},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x000126dc,0x000136e4,0x0001b724},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000124db,0x000136e4,0x0001b725,0x0001b724},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000126dc,0x000136e4,0x0001b725,0x0001b925},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000126dc,0x0001b724,0x0001b92d,0x0001b925},
+ {0x00000000,0x00000000,0x00009292,0x0000a49b,
+ 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d},
+ {0x00000000,0x00000000,0x00009292,0x000124db,
+ 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d},
+ {0x00000000,0x00000000,0x00009492,0x000124db,
+ 0x000136e4,0x0001b724,0x0001c96e,0x0002496e},
+ {0x00000000,0x00000000,0x00009492,0x000126db,
+ 0x000136e4,0x0001b925,0x0001c96e,0x0002496e},
+ {0x00000000,0x00000000,0x0000a492,0x000136db,
+ 0x0001b724,0x0002496d,0x00025bb6,0x00025bbf},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ },
+ { /* version 7, passes 1 */
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009252,0x00009292,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x00001249,0x0000924a,
+ 0x00009492,0x00009493,0x00009493,0x00009292},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x0000a49b,0x0000a49b,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x0000a49b,0x000124db,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x000124db,0x000124db,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0000a493,0x000124db,0x000136e4,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0000a493,0x000124db,0x000136e4,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x000124db,0x000136e4,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x000126dc,0x000136e4,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x000126dc,0x000136e4,0x000124db},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000126dc,0x000136e4,0x000136e4,0x000124db},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000126dc,0x000136e4,0x000136e4,0x000124db},
+ {0x00000000,0x00000000,0x0000924a,0x000124db,
+ 0x000136e4,0x000136e4,0x000136e4,0x000126dc},
+ {0x00000000,0x00000000,0x0000a492,0x000124db,
+ 0x000136e4,0x0001b724,0x000136e4,0x000126dc},
+ {0x00000000,0x00000000,0x00012492,0x000126db,
+ 0x0001b724,0x0001b925,0x0001b725,0x000136e4},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ }
+ },
+ { /* version 8 */
+ { /* version 8, passes 0 */
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009292,0x00009493,0x0000a49b,0x000124db},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x0000a493,0x000124db,0x000126dc,0x000126dc},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x000124db,0x000126dc,0x000136e4},
+ {0x00000000,0x00000000,0x00001249,0x0000a49b,
+ 0x0001249b,0x000126dc,0x000136e4,0x0001b724},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x000126dc,0x000136e4,0x0001b724},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000124db,0x000136e4,0x0001b725,0x0001b724},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000126dc,0x000136e4,0x0001b725,0x0001b925},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d},
+ {0x00000000,0x00000000,0x00009252,0x000124db,
+ 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d},
+ {0x00000000,0x00000000,0x00009292,0x000124db,
+ 0x000126dc,0x0001b925,0x0001c96e,0x0001c92d},
+ {0x00000000,0x00000000,0x00009492,0x000124db,
+ 0x000136e4,0x0001b925,0x0001c96e,0x0001c92d},
+ {0x00000000,0x00000000,0x00009492,0x000124db,
+ 0x000136e4,0x0001b925,0x00024b76,0x00024b77},
+ {0x00000000,0x00000000,0x00009492,0x000126db,
+ 0x000136e4,0x0001b925,0x00024b76,0x00025bbf},
+ {0x00000000,0x00000000,0x0000a492,0x000126db,
+ 0x000136e4,0x0001c92d,0x00024b76,0x00025bbf},
+ {0x00000000,0x00000000,0x00012492,0x000136db,
+ 0x0001b724,0x00024b6d,0x0002ddb6,0x0002efff},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ },
+ { /* version 8, passes 1 */
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009252,0x00009493,0x00009493,0x00009493},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x0000a493,0x0000a49b,0x0000a49b,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x0000a49b,0x000124db,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x000124db,0x000126dc,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0000a493,0x000124db,0x000126dc,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0000a493,0x000124db,0x000136e4,0x000124db},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x000126dc,0x000136e4,0x000124db},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000126dc,0x000126dc,0x000136e4,0x000126dc},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000126dc,0x000136e4,0x000136e4,0x000126dc},
+ {0x00000000,0x00000000,0x0000924a,0x000124db,
+ 0x000126dc,0x000136e4,0x000136e4,0x000126dc},
+ {0x00000000,0x00000000,0x0000924a,0x000124db,
+ 0x000126dc,0x000136e4,0x000136e4,0x000136e4},
+ {0x00000000,0x00000000,0x00009292,0x000124db,
+ 0x000136e4,0x0001b724,0x0001b725,0x000136e4},
+ {0x00000000,0x00000000,0x00009492,0x000126db,
+ 0x000136e4,0x0001b925,0x0001b725,0x0001b724},
+ {0x00000000,0x00000000,0x00009492,0x000126db,
+ 0x000136e4,0x0001b925,0x0001b725,0x0001b724},
+ {0x00000000,0x00000000,0x0000a492,0x000136db,
+ 0x0001b724,0x0002496d,0x0001b92d,0x0001b925},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ }
+ },
+ { /* version 9 */
+ { /* version 9, passes 0 */
+ {0x00000000,0x00000000,0x00000049,0x00000049,
+ 0x00000049,0x00000049,0x00000049,0x00000049},
+ {0x00000000,0x00000000,0x00000249,0x00000049,
+ 0x00000249,0x00000249,0x0000024a,0x00000049},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x0000124a,0x00009252,0x00001252,0x0000024a},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009252,0x00009292,0x00009493,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x0000924a,
+ 0x00009292,0x00009493,0x00009493,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x00009492,0x0000a49b,0x0000a49b,0x00009292},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x000124db,0x000124db,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x000124db,0x000126dc,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x000124db,0x000126dc,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0001249b,0x000126dc,0x000126dc,0x000124db},
+ {0x00000000,0x00000000,0x00009252,0x00009493,
+ 0x000124db,0x000136e4,0x000136e4,0x000126dc},
+ {0x00000000,0x00000000,0x00009252,0x0000a49b,
+ 0x000124db,0x000136e4,0x000136e4,0x000126dc},
+ {0x00000000,0x00000000,0x00009292,0x0000a49b,
+ 0x000126dc,0x000136e4,0x000136e4,0x000136e4},
+ {0x00000000,0x00000000,0x00009492,0x0000a49b,
+ 0x000126dc,0x0001b724,0x0001b725,0x0001b724},
+ {0x00000000,0x00000000,0x0000a492,0x000124db,
+ 0x000136e4,0x0001b925,0x0001b92d,0x0001b925},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ },
+ { /* version 9, passes 1 */
+ {0x00000000,0x00000000,0x00000249,0x00000049,
+ 0x00000009,0x00000009,0x00000009,0x00000009},
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x00000049,0x00000049,0x00000009,0x00000009},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x0000124a,0x00000249,0x00000049,0x00000049},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x0000124a,0x0000124a,0x00000049,0x00000049},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009252,0x0000124a,0x0000024a,0x0000024a},
+ {0x00000000,0x00000000,0x00001249,0x0000924a,
+ 0x00009252,0x0000124a,0x0000024a,0x0000024a},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x00009492,0x00009252,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x00009292,0x00009292,0x00001252},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x00009292,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x00009493,0x00009493,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0000a493,0x0000a49b,0x00009493,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0000a493,0x0000a49b,0x0000a49b,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x000124db,0x0000a49b,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000136e4,0x000126dc,0x000124db,0x0000a49b},
+ {0x00000000,0x00000000,0x00009252,0x000124db,
+ 0x0001b724,0x000136e4,0x000126dc,0x000124db},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ }
+ },
+ { /* version 10 */
+ { /* version 10, passes 0 */
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x00000249,0x00000249,0x0000024a,0x0000024a},
+ {0x00000000,0x00000000,0x00000249,0x00001249,
+ 0x00009252,0x00009292,0x00009292,0x0000024a},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009252,0x00009292,0x00009292,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x0000924a,
+ 0x00009492,0x00009493,0x0000a49b,0x00009292},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x00009492,0x000124db,0x000124db,0x00009292},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x000124db,0x000124db,0x00009493},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x000124db,0x000126dc,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x000124db,0x000126dc,0x000124db},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0001249b,0x000126dc,0x000126dc,0x000124db},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000124db,0x000126dc,0x000136e4,0x000126dc},
+ {0x00000000,0x00000000,0x00009252,0x0000a49b,
+ 0x000124db,0x000136e4,0x000136e4,0x000136e4},
+ {0x00000000,0x00000000,0x00009292,0x0000a49b,
+ 0x000126dc,0x000136e4,0x000136e4,0x000136e4},
+ {0x00000000,0x00000000,0x00009492,0x0000a49b,
+ 0x000126dc,0x0001b724,0x0001b92d,0x0001b724},
+ {0x00000000,0x00000000,0x00009492,0x000124db,
+ 0x000126dc,0x0001b925,0x0001b92d,0x0001b925},
+ {0x00000000,0x00000000,0x0000a492,0x000126db,
+ 0x000136e4,0x0002496d,0x0001c96e,0x0001c92d},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ },
+ { /* version 10, passes 1 */
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x00000049,0x00000049,0x00000049,0x00000049},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x0000124a,0x00000249,0x00000049,0x00000049},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x0000124a,0x00009252,0x0000024a,0x00000049},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009252,0x00009493,0x0000024a,0x0000024a},
+ {0x00000000,0x00000000,0x00001249,0x00009252,
+ 0x00009492,0x00009493,0x00001252,0x0000024a},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x00009492,0x00009493,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x00009492,0x00009493,0x00009292,0x00001252},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x00009493,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x0000a49b,0x00009493,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x0000a49b,0x00009493,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0000a493,0x000124db,0x0000a49b,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0000a493,0x000124db,0x0000a49b,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x000124db,
+ 0x000136e4,0x000126dc,0x000124db,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x000124db,
+ 0x000136e4,0x000126dc,0x000124db,0x0000a49b},
+ {0x00000000,0x00000000,0x00009252,0x000126db,
+ 0x0001b724,0x000136e4,0x000126dc,0x000124db},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ }
+ },
+ { /* version 11 */
+ { /* version 11, passes 0 */
+ {0x00000000,0x00000000,0x00000249,0x00000249,
+ 0x00000249,0x00000249,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009252,0x00009292,0x00009292,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x0000924a,
+ 0x00009492,0x0000a49b,0x0000a49b,0x00009292},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x0000a49b,0x000124db,0x00009493},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x000124db,0x000126dc,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x000126dc,0x000126dc,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x000126dc,0x000136e4,0x000124db},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000126dc,0x000136e4,0x000136e4,0x000126dc},
+ {0x00000000,0x00000000,0x00009292,0x0000a49b,
+ 0x000126dc,0x000136e4,0x000136e4,0x000126dc},
+ {0x00000000,0x00000000,0x00009292,0x0000a49b,
+ 0x000126dc,0x0001b724,0x0001b725,0x000136e4},
+ {0x00000000,0x00000000,0x00009292,0x0000a49b,
+ 0x000136e4,0x0001b724,0x0001b92d,0x0001b724},
+ {0x00000000,0x00000000,0x00009492,0x0000a49b,
+ 0x000136e4,0x0001b724,0x0001b92d,0x0001b724},
+ {0x00000000,0x00000000,0x00009492,0x000124db,
+ 0x000136e4,0x0001b925,0x0001c96e,0x0001b925},
+ {0x00000000,0x00000000,0x00009492,0x000124db,
+ 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d},
+ {0x00000000,0x00000000,0x0000a492,0x000126db,
+ 0x0001c924,0x0002496d,0x00025bb6,0x00024b77},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ },
+ { /* version 11, passes 1 */
+ {0x00000000,0x00000000,0x00001249,0x00000249,
+ 0x00000249,0x00000249,0x0000024a,0x0000024a},
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x0000124a,0x0000124a,0x0000024a,0x0000024a},
+ {0x00000000,0x00000000,0x00001249,0x0000924a,
+ 0x00009252,0x00009252,0x0000024a,0x0000024a},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x00009492,0x0000a49b,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x0000a49b,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x0000a49b,0x00009292,0x00001252},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0000a493,0x0000a49b,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0000a493,0x0000a49b,0x00009493,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x000124db,0x00009493,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x000124db,0x00009493,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000124db,0x000124db,0x0000a49b,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x000124db,
+ 0x000126dc,0x000126dc,0x0000a49b,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x000124db,
+ 0x000136e4,0x000126dc,0x000124db,0x0000a49b},
+ {0x00000000,0x00000000,0x00009292,0x000124db,
+ 0x000136e4,0x000126dc,0x000124db,0x0000a49b},
+ {0x00000000,0x00000000,0x00009492,0x000126db,
+ 0x0001b724,0x000136e4,0x000126dc,0x000124db},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ }
+ },
+ { /* version 12 */
+ { /* version 12, passes 0 */
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009252,0x00009292,0x00009493,0x00009493},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x0000a493,0x0000a49b,0x0000a49b,0x00009493},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x000124db,0x000124db,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x000126dc,0x000126dc,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x000126dc,0x000136e4,0x000124db},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000126dc,0x000136e4,0x000136e4,0x000126dc},
+ {0x00000000,0x00000000,0x00009292,0x0000a49b,
+ 0x000126dc,0x0001b724,0x0001b725,0x000126dc},
+ {0x00000000,0x00000000,0x00009292,0x0000a49b,
+ 0x000136e4,0x0001b724,0x0001b92d,0x000136e4},
+ {0x00000000,0x00000000,0x00009492,0x0000a49b,
+ 0x000136e4,0x0001b724,0x0001b92d,0x0001b724},
+ {0x00000000,0x00000000,0x00009492,0x000124db,
+ 0x000136e4,0x0001b724,0x0001b92d,0x0001b724},
+ {0x00000000,0x00000000,0x00009492,0x000124db,
+ 0x000136e4,0x0001b925,0x0001b92d,0x0001b925},
+ {0x00000000,0x00000000,0x00009492,0x000124db,
+ 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d},
+ {0x00000000,0x00000000,0x0000a492,0x000124db,
+ 0x0001b724,0x0001c92d,0x0001c96e,0x0001c92d},
+ {0x00000000,0x00000000,0x0000a492,0x000124db,
+ 0x0001b724,0x0001c92d,0x00024b76,0x0002496e},
+ {0x00000000,0x00000000,0x00012492,0x000126db,
+ 0x0001c924,0x00024b6d,0x0002ddb6,0x00025bbf},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ },
+ { /* version 12, passes 1 */
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x0000124a,0x0000124a,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x00001249,0x00009292,
+ 0x00009492,0x00009252,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x00009292,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0000a493,0x0000a49b,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0000a493,0x0000a49b,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x0000a49b,0x00009493,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000124db,0x000124db,0x00009493,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000124db,0x000124db,0x0000a49b,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x000124db,
+ 0x000126dc,0x000124db,0x0000a49b,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x000124db,
+ 0x000126dc,0x000126dc,0x0000a49b,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x000124db,
+ 0x000136e4,0x000126dc,0x000124db,0x0000a49b},
+ {0x00000000,0x00000000,0x00009492,0x000126db,
+ 0x000136e4,0x000126dc,0x000124db,0x0000a49b},
+ {0x00000000,0x00000000,0x00009492,0x000126db,
+ 0x0001b724,0x000136e4,0x000126dc,0x000124db},
+ {0x00000000,0x00000000,0x00009492,0x000126db,
+ 0x0001b724,0x000136e4,0x000126dc,0x000124db},
+ {0x00000000,0x00000000,0x0000a492,0x000136db,
+ 0x0001c924,0x0001b724,0x000136e4,0x000126dc},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ }
+ },
+ { /* version 13 */
+ { /* version 13, passes 0 */
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x00009252,0x00009292,0x00009493,0x00009493},
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x000124db,0x000126dc,0x00009493},
+ {0x00000000,0x00000000,0x00001249,0x0000a49b,
+ 0x0001249b,0x000126dc,0x000126dc,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x000126dc,0x000136e4,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000126dc,0x000136e4,0x0001b725,0x000124db},
+ {0x00000000,0x00000000,0x00009292,0x0000a49b,
+ 0x000136e4,0x0001b724,0x0001b725,0x000126dc},
+ {0x00000000,0x00000000,0x00009292,0x000124db,
+ 0x000136e4,0x0001b724,0x0001b725,0x000126dc},
+ {0x00000000,0x00000000,0x00009492,0x000124db,
+ 0x000136e4,0x0001b724,0x0001c96e,0x000136e4},
+ {0x00000000,0x00000000,0x00009492,0x000124db,
+ 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724},
+ {0x00000000,0x00000000,0x0000a492,0x000124db,
+ 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724},
+ {0x00000000,0x00000000,0x0000a492,0x000124db,
+ 0x0001b724,0x0001c92d,0x0001c96e,0x0001b925},
+ {0x00000000,0x00000000,0x0000a492,0x000126db,
+ 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d},
+ {0x00000000,0x00000000,0x0000a492,0x000126db,
+ 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d},
+ {0x00000000,0x00000000,0x0000a492,0x000126db,
+ 0x0001b924,0x0001c92d,0x00024b76,0x0002496e},
+ {0x00000000,0x00000000,0x00012492,0x000136db,
+ 0x00024924,0x00024b6d,0x0002ddb6,0x00025bbf},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ },
+ { /* version 13, passes 1 */
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x0000124a,0x0000124a,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x00009492,0x00009292,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0000a493,0x0000a49b,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0000a493,0x0000a49b,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0000a493,0x0000a49b,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000126dc,0x0000a49b,0x00009493,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x000124db,
+ 0x000126dc,0x000124db,0x00009493,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x000124db,
+ 0x000136e4,0x000124db,0x0000a49b,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x000136db,
+ 0x0001b724,0x000124db,0x0000a49b,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x000136db,
+ 0x0001b724,0x000126dc,0x0000a49b,0x0000a49b},
+ {0x00000000,0x00000000,0x00009292,0x000136db,
+ 0x0001b724,0x000126dc,0x000124db,0x0000a49b},
+ {0x00000000,0x00000000,0x00009492,0x000136db,
+ 0x0001b724,0x000126dc,0x000124db,0x0000a49b},
+ {0x00000000,0x00000000,0x0000a492,0x000136db,
+ 0x0001b724,0x000136e4,0x000126dc,0x000124db},
+ {0x00000000,0x00000000,0x0000a492,0x000136db,
+ 0x0001b724,0x000136e4,0x000126dc,0x000124db},
+ {0x00000000,0x00000000,0x00012492,0x0001b6db,
+ 0x0001c924,0x0001b724,0x000136e4,0x000126dc},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ }
+ },
+ { /* version 14 */
+ { /* version 14, passes 0 */
+ {0x00000000,0x00000000,0x00001249,0x0000924a,
+ 0x00009292,0x00009493,0x00009493,0x00009493},
+ {0x00000000,0x00000000,0x00001249,0x0000a49b,
+ 0x0000a493,0x000124db,0x000126dc,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x000126dc,0x000136e4,0x0000a49b},
+ {0x00000000,0x00000000,0x0000924a,0x000124db,
+ 0x000126dc,0x000136e4,0x0001b725,0x000124db},
+ {0x00000000,0x00000000,0x00009292,0x000124db,
+ 0x000126dc,0x0001b724,0x0001b92d,0x000126dc},
+ {0x00000000,0x00000000,0x00009492,0x000124db,
+ 0x000136e4,0x0001b724,0x0001b92d,0x000126dc},
+ {0x00000000,0x00000000,0x00009492,0x000124db,
+ 0x000136e4,0x0001c92d,0x0001c96e,0x000136e4},
+ {0x00000000,0x00000000,0x00009492,0x000124db,
+ 0x0001b724,0x0001c92d,0x0001c96e,0x0001b724},
+ {0x00000000,0x00000000,0x0000a492,0x000124db,
+ 0x0001b724,0x0001c92d,0x00024b76,0x0001b925},
+ {0x00000000,0x00000000,0x0000a492,0x000126db,
+ 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d},
+ {0x00000000,0x00000000,0x0000a492,0x000126db,
+ 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d},
+ {0x00000000,0x00000000,0x0000a492,0x000136db,
+ 0x0001b724,0x0001c92d,0x00024b76,0x0002496e},
+ {0x00000000,0x00000000,0x0000a492,0x000136db,
+ 0x0001b924,0x0002496d,0x00024b76,0x00024b77},
+ {0x00000000,0x00000000,0x0000a492,0x000136db,
+ 0x0001b924,0x00024b6d,0x0002ddb6,0x00025bbf},
+ {0x00000000,0x00000000,0x00012492,0x0001b6db,
+ 0x00024924,0x0002db6d,0x00036db6,0x0002efff},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ },
+ { /* version 14, passes 1 */
+ {0x00000000,0x00000000,0x00001249,0x00001249,
+ 0x0000124a,0x0000124a,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x0000924a,0x00009493,
+ 0x0000a493,0x00009292,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0000a493,0x0000a49b,0x00001252,0x00001252},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x000136e4,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x000136e4,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x000124db,
+ 0x000136e4,0x000136e4,0x00009493,0x00009292},
+ {0x00000000,0x00000000,0x00009492,0x000136db,
+ 0x0001b724,0x000136e4,0x00009493,0x00009493},
+ {0x00000000,0x00000000,0x00009492,0x000136db,
+ 0x0001b724,0x000136e4,0x0000a49b,0x00009493},
+ {0x00000000,0x00000000,0x00009492,0x000136db,
+ 0x0001b724,0x000136e4,0x0000a49b,0x00009493},
+ {0x00000000,0x00000000,0x00009492,0x000136db,
+ 0x0001b724,0x000136e4,0x0000a49b,0x0000a49b},
+ {0x00000000,0x00000000,0x0000a492,0x000136db,
+ 0x0001b724,0x000136e4,0x000124db,0x0000a49b},
+ {0x00000000,0x00000000,0x0000a492,0x000136db,
+ 0x0001b724,0x000136e4,0x000124db,0x0000a49b},
+ {0x00000000,0x00000000,0x0000a492,0x000136db,
+ 0x0001b724,0x000136e4,0x000126dc,0x000124db},
+ {0x00000000,0x00000000,0x0000a492,0x000136db,
+ 0x0001b724,0x000136e4,0x000126dc,0x000124db},
+ {0x00000000,0x00000000,0x00012492,0x0001b6db,
+ 0x0001c924,0x0001b724,0x000136e4,0x000126dc},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ }
+ },
+ { /* version 15 */
+ { /* version 15, passes 0 */
+ {0x00000000,0x00000000,0x00001249,0x00009493,
+ 0x0000a493,0x0000a49b,0x000124db,0x000124db},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0001249b,0x000126dc,0x000136e4,0x000124db},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x000126dc,0x0001b724,0x0001b725,0x000126dc},
+ {0x00000000,0x00000000,0x0000924a,0x000124db,
+ 0x000136e4,0x0001b724,0x0001b92d,0x000126dc},
+ {0x00000000,0x00000000,0x00009492,0x000124db,
+ 0x000136e4,0x0001b925,0x0001c96e,0x000136e4},
+ {0x00000000,0x00000000,0x00009492,0x000124db,
+ 0x0001b724,0x0001c92d,0x0001c96e,0x0001b724},
+ {0x00000000,0x00000000,0x0000a492,0x000124db,
+ 0x0001b724,0x0001c92d,0x0001c96e,0x0001b724},
+ {0x00000000,0x00000000,0x0000a492,0x000126db,
+ 0x0001b724,0x0001c92d,0x0001c96e,0x0001b925},
+ {0x00000000,0x00000000,0x0000a492,0x000126db,
+ 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d},
+ {0x00000000,0x00000000,0x0000a492,0x000136db,
+ 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d},
+ {0x00000000,0x00000000,0x0000a492,0x000136db,
+ 0x0001b924,0x0002496d,0x00024b76,0x0002496e},
+ {0x00000000,0x00000000,0x0000a492,0x000136db,
+ 0x0001c924,0x0002496d,0x00025bb6,0x00024b77},
+ {0x00000000,0x00000000,0x0000a492,0x000136db,
+ 0x0001c924,0x00024b6d,0x00025bb6,0x00024b77},
+ {0x00000000,0x00000000,0x00012492,0x000136db,
+ 0x0001c924,0x00024b6d,0x0002ddb6,0x00025bbf},
+ {0x00000000,0x00000000,0x00012492,0x0001b6db,
+ 0x00024924,0x0002db6d,0x00036db6,0x0002efff},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ },
+ { /* version 15, passes 1 */
+ {0x00000000,0x00000000,0x0000924a,0x0000924a,
+ 0x00009292,0x00009292,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+ 0x0000a493,0x000124db,0x00009292,0x00009292},
+ {0x00000000,0x00000000,0x0000924a,0x000124db,
+ 0x000124db,0x0001b724,0x00009493,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x000124db,
+ 0x000126dc,0x0001b724,0x00009493,0x00009493},
+ {0x00000000,0x00000000,0x0000924a,0x000124db,
+ 0x000136e4,0x0001b724,0x0000a49b,0x0000a49b},
+ {0x00000000,0x00000000,0x00009292,0x000136db,
+ 0x0001b724,0x0001b724,0x0000a49b,0x0000a49b},
+ {0x00000000,0x00000000,0x00009492,0x000136db,
+ 0x0001c924,0x0001b724,0x000124db,0x000124db},
+ {0x00000000,0x00000000,0x00009492,0x000136db,
+ 0x0001c924,0x0001b724,0x000124db,0x000124db},
+ {0x00000000,0x00000000,0x0000a492,0x000136db,
+ 0x0001c924,0x0001b724,0x000126dc,0x000126dc},
+ {0x00000000,0x00000000,0x0000a492,0x000136db,
+ 0x0001c924,0x0001b925,0x000126dc,0x000126dc},
+ {0x00000000,0x00000000,0x0000a492,0x000136db,
+ 0x0001c924,0x0001b925,0x000136e4,0x000136e4},
+ {0x00000000,0x00000000,0x0000a492,0x000136db,
+ 0x0001c924,0x0001b925,0x000136e4,0x000136e4},
+ {0x00000000,0x00000000,0x0000a492,0x000136db,
+ 0x0001c924,0x0001b925,0x0001b725,0x0001b724},
+ {0x00000000,0x00000000,0x00012492,0x000136db,
+ 0x0001c924,0x0001b925,0x0001b725,0x0001b724},
+ {0x00000000,0x00000000,0x00012492,0x0001b6db,
+ 0x00024924,0x0002496d,0x0001b92d,0x0001b925},
+ {0x00000000,0x00000000,0x00000000,0x00000000,
+ 0x00000000,0x00000000,0x00000000,0x00000000}
+ }
+ }
+};
diff --git a/drivers/media/video/pwc/pwc-timon.h b/drivers/media/video/pwc/pwc-timon.h
new file mode 100644
index 00000000..270c5b90
--- /dev/null
+++ b/drivers/media/video/pwc/pwc-timon.h
@@ -0,0 +1,63 @@
+/* Linux driver for Philips webcam
+ (C) 2004-2006 Luc Saillard (luc@saillard.org)
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+ The decompression routines have been implemented by reverse-engineering the
+ Nemosoft binary pwcx module. Caveat emptor.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+
+
+/* This tables contains entries for the 675/680/690 (Timon) camera, with
+ 4 different qualities (no compression, low, medium, high).
+ It lists the bandwidth requirements for said mode by its alternate interface
+ number. An alternate of 0 means that the mode is unavailable.
+
+ There are 6 * 4 * 4 entries:
+ 6 different resolutions subqcif, qsif, qcif, sif, cif, vga
+ 6 framerates: 5, 10, 15, 20, 25, 30
+ 4 compression modi: none, low, medium, high
+
+ When an uncompressed mode is not available, the next available compressed mode
+ will be chosen (unless the decompressor is absent). Sometimes there are only
+ 1 or 2 compressed modes available; in that case entries are duplicated.
+*/
+
+#ifndef PWC_TIMON_H
+#define PWC_TIMON_H
+
+#include "pwc.h"
+
+#define PWC_FPS_MAX_TIMON 6
+
+struct Timon_table_entry
+{
+ char alternate; /* USB alternate interface */
+ unsigned short packetsize; /* Normal packet size */
+ unsigned short bandlength; /* Bandlength when decompressing */
+ unsigned char mode[13]; /* precomputed mode settings for cam */
+};
+
+extern const struct Timon_table_entry Timon_table[PSZ_MAX][PWC_FPS_MAX_TIMON][4];
+extern const unsigned int TimonRomTable [16][2][16][8];
+extern const unsigned int Timon_fps_vector[PWC_FPS_MAX_TIMON];
+
+#endif
+
+
diff --git a/drivers/media/video/pwc/pwc-uncompress.c b/drivers/media/video/pwc/pwc-uncompress.c
new file mode 100644
index 00000000..b65903fb
--- /dev/null
+++ b/drivers/media/video/pwc/pwc-uncompress.c
@@ -0,0 +1,107 @@
+/* Linux driver for Philips webcam
+ Decompression frontend.
+ (C) 1999-2003 Nemosoft Unv.
+ (C) 2004-2006 Luc Saillard (luc@saillard.org)
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+ The decompression routines have been implemented by reverse-engineering the
+ Nemosoft binary pwcx module. Caveat emptor.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ vim: set ts=8:
+*/
+
+#include <asm/current.h>
+#include <asm/types.h>
+
+#include "pwc.h"
+#include "pwc-dec1.h"
+#include "pwc-dec23.h"
+
+int pwc_decompress(struct pwc_device *pdev, struct pwc_frame_buf *fbuf)
+{
+ int n, line, col;
+ void *yuv, *image;
+ u16 *src;
+ u16 *dsty, *dstu, *dstv;
+
+ image = vb2_plane_vaddr(&fbuf->vb, 0);
+
+ yuv = fbuf->data + pdev->frame_header_size; /* Skip header */
+
+ /* Raw format; that's easy... */
+ if (pdev->pixfmt != V4L2_PIX_FMT_YUV420)
+ {
+ struct pwc_raw_frame *raw_frame = image;
+ raw_frame->type = cpu_to_le16(pdev->type);
+ raw_frame->vbandlength = cpu_to_le16(pdev->vbandlength);
+ /* cmd_buf is always 4 bytes, but sometimes, only the
+ * first 3 bytes is filled (Nala case). We can
+ * determine this using the type of the webcam */
+ memcpy(raw_frame->cmd, pdev->cmd_buf, 4);
+ memcpy(raw_frame+1, yuv, pdev->frame_size);
+ vb2_set_plane_payload(&fbuf->vb, 0,
+ pdev->frame_size + sizeof(struct pwc_raw_frame));
+ return 0;
+ }
+
+ vb2_set_plane_payload(&fbuf->vb, 0,
+ pdev->width * pdev->height * 3 / 2);
+
+ if (pdev->vbandlength == 0) {
+ /* Uncompressed mode.
+ *
+ * We do some byte shuffling here to go from the
+ * native format to YUV420P.
+ */
+ src = (u16 *)yuv;
+ n = pdev->width * pdev->height;
+ dsty = (u16 *)(image);
+ dstu = (u16 *)(image + n);
+ dstv = (u16 *)(image + n + n / 4);
+
+ for (line = 0; line < pdev->height; line++) {
+ for (col = 0; col < pdev->width; col += 4) {
+ *dsty++ = *src++;
+ *dsty++ = *src++;
+ if (line & 1)
+ *dstv++ = *src++;
+ else
+ *dstu++ = *src++;
+ }
+ }
+
+ return 0;
+ }
+
+ /*
+ * Compressed;
+ * the decompressor routines will write the data in planar format
+ * immediately.
+ */
+ if (DEVICE_USE_CODEC1(pdev->type)) {
+
+ /* TODO & FIXME */
+ PWC_ERROR("This chipset is not supported for now\n");
+ return -ENXIO; /* No such device or address: missing decompressor */
+
+ } else {
+ pwc_dec23_decompress(pdev, yuv, image);
+ }
+ return 0;
+}
diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c
new file mode 100644
index 00000000..2834e3e6
--- /dev/null
+++ b/drivers/media/video/pwc/pwc-v4l.c
@@ -0,0 +1,1169 @@
+/* Linux driver for Philips webcam
+ USB and Video4Linux interface part.
+ (C) 1999-2004 Nemosoft Unv.
+ (C) 2004-2006 Luc Saillard (luc@saillard.org)
+ (C) 2011 Hans de Goede <hdegoede@redhat.com>
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+ The decompression routines have been implemented by reverse-engineering the
+ Nemosoft binary pwcx module. Caveat emptor.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/vmalloc.h>
+#include <linux/jiffies.h>
+#include <asm/io.h>
+
+#include "pwc.h"
+
+#define PWC_CID_CUSTOM(ctrl) ((V4L2_CID_USER_BASE | 0xf000) + custom_ ## ctrl)
+
+static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl);
+static int pwc_s_ctrl(struct v4l2_ctrl *ctrl);
+
+static const struct v4l2_ctrl_ops pwc_ctrl_ops = {
+ .g_volatile_ctrl = pwc_g_volatile_ctrl,
+ .s_ctrl = pwc_s_ctrl,
+};
+
+enum { awb_indoor, awb_outdoor, awb_fl, awb_manual, awb_auto };
+enum { custom_autocontour, custom_contour, custom_noise_reduction,
+ custom_awb_speed, custom_awb_delay,
+ custom_save_user, custom_restore_user, custom_restore_factory };
+
+const char * const pwc_auto_whitebal_qmenu[] = {
+ "Indoor (Incandescant Lighting) Mode",
+ "Outdoor (Sunlight) Mode",
+ "Indoor (Fluorescent Lighting) Mode",
+ "Manual Mode",
+ "Auto Mode",
+ NULL
+};
+
+static const struct v4l2_ctrl_config pwc_auto_white_balance_cfg = {
+ .ops = &pwc_ctrl_ops,
+ .id = V4L2_CID_AUTO_WHITE_BALANCE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .max = awb_auto,
+ .qmenu = pwc_auto_whitebal_qmenu,
+};
+
+static const struct v4l2_ctrl_config pwc_autocontour_cfg = {
+ .ops = &pwc_ctrl_ops,
+ .id = PWC_CID_CUSTOM(autocontour),
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto contour",
+ .min = 0,
+ .max = 1,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config pwc_contour_cfg = {
+ .ops = &pwc_ctrl_ops,
+ .id = PWC_CID_CUSTOM(contour),
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contour",
+ .flags = V4L2_CTRL_FLAG_SLIDER,
+ .min = 0,
+ .max = 63,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config pwc_backlight_cfg = {
+ .ops = &pwc_ctrl_ops,
+ .id = V4L2_CID_BACKLIGHT_COMPENSATION,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .min = 0,
+ .max = 1,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config pwc_flicker_cfg = {
+ .ops = &pwc_ctrl_ops,
+ .id = V4L2_CID_BAND_STOP_FILTER,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .min = 0,
+ .max = 1,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config pwc_noise_reduction_cfg = {
+ .ops = &pwc_ctrl_ops,
+ .id = PWC_CID_CUSTOM(noise_reduction),
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Dynamic Noise Reduction",
+ .min = 0,
+ .max = 3,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config pwc_save_user_cfg = {
+ .ops = &pwc_ctrl_ops,
+ .id = PWC_CID_CUSTOM(save_user),
+ .type = V4L2_CTRL_TYPE_BUTTON,
+ .name = "Save User Settings",
+};
+
+static const struct v4l2_ctrl_config pwc_restore_user_cfg = {
+ .ops = &pwc_ctrl_ops,
+ .id = PWC_CID_CUSTOM(restore_user),
+ .type = V4L2_CTRL_TYPE_BUTTON,
+ .name = "Restore User Settings",
+};
+
+static const struct v4l2_ctrl_config pwc_restore_factory_cfg = {
+ .ops = &pwc_ctrl_ops,
+ .id = PWC_CID_CUSTOM(restore_factory),
+ .type = V4L2_CTRL_TYPE_BUTTON,
+ .name = "Restore Factory Settings",
+};
+
+static const struct v4l2_ctrl_config pwc_awb_speed_cfg = {
+ .ops = &pwc_ctrl_ops,
+ .id = PWC_CID_CUSTOM(awb_speed),
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Auto White Balance Speed",
+ .min = 1,
+ .max = 32,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config pwc_awb_delay_cfg = {
+ .ops = &pwc_ctrl_ops,
+ .id = PWC_CID_CUSTOM(awb_delay),
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Auto White Balance Delay",
+ .min = 0,
+ .max = 63,
+ .step = 1,
+};
+
+int pwc_init_controls(struct pwc_device *pdev)
+{
+ struct v4l2_ctrl_handler *hdl;
+ struct v4l2_ctrl_config cfg;
+ int r, def;
+
+ hdl = &pdev->ctrl_handler;
+ r = v4l2_ctrl_handler_init(hdl, 20);
+ if (r)
+ return r;
+
+ /* Brightness, contrast, saturation, gamma */
+ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, BRIGHTNESS_FORMATTER, &def);
+ if (r || def > 127)
+ def = 63;
+ pdev->brightness = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 127, 1, def);
+
+ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, CONTRAST_FORMATTER, &def);
+ if (r || def > 63)
+ def = 31;
+ pdev->contrast = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 63, 1, def);
+
+ if (pdev->type >= 675) {
+ if (pdev->type < 730)
+ pdev->saturation_fmt = SATURATION_MODE_FORMATTER2;
+ else
+ pdev->saturation_fmt = SATURATION_MODE_FORMATTER1;
+ r = pwc_get_s8_ctrl(pdev, GET_CHROM_CTL, pdev->saturation_fmt,
+ &def);
+ if (r || def < -100 || def > 100)
+ def = 0;
+ pdev->saturation = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+ V4L2_CID_SATURATION, -100, 100, 1, def);
+ }
+
+ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, GAMMA_FORMATTER, &def);
+ if (r || def > 31)
+ def = 15;
+ pdev->gamma = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+ V4L2_CID_GAMMA, 0, 31, 1, def);
+
+ /* auto white balance, red gain, blue gain */
+ r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, WB_MODE_FORMATTER, &def);
+ if (r || def > awb_auto)
+ def = awb_auto;
+ cfg = pwc_auto_white_balance_cfg;
+ cfg.name = v4l2_ctrl_get_name(cfg.id);
+ cfg.def = def;
+ pdev->auto_white_balance = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
+ /* check auto controls to avoid NULL deref in v4l2_ctrl_auto_cluster */
+ if (!pdev->auto_white_balance)
+ return hdl->error;
+
+ r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL,
+ PRESET_MANUAL_RED_GAIN_FORMATTER, &def);
+ if (r)
+ def = 127;
+ pdev->red_balance = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+ V4L2_CID_RED_BALANCE, 0, 255, 1, def);
+
+ r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL,
+ PRESET_MANUAL_BLUE_GAIN_FORMATTER, &def);
+ if (r)
+ def = 127;
+ pdev->blue_balance = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+ V4L2_CID_BLUE_BALANCE, 0, 255, 1, def);
+
+ v4l2_ctrl_auto_cluster(3, &pdev->auto_white_balance, awb_manual, true);
+
+ /* autogain, gain */
+ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, AGC_MODE_FORMATTER, &def);
+ if (r || (def != 0 && def != 0xff))
+ def = 0;
+ /* Note a register value if 0 means auto gain is on */
+ pdev->autogain = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, def == 0);
+ if (!pdev->autogain)
+ return hdl->error;
+
+ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, PRESET_AGC_FORMATTER, &def);
+ if (r || def > 63)
+ def = 31;
+ pdev->gain = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+ V4L2_CID_GAIN, 0, 63, 1, def);
+
+ /* auto exposure, exposure */
+ if (DEVICE_USE_CODEC2(pdev->type)) {
+ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, SHUTTER_MODE_FORMATTER,
+ &def);
+ if (r || (def != 0 && def != 0xff))
+ def = 0;
+ /*
+ * def = 0 auto, def = ff manual
+ * menu idx 0 = auto, idx 1 = manual
+ */
+ pdev->exposure_auto = v4l2_ctrl_new_std_menu(hdl,
+ &pwc_ctrl_ops,
+ V4L2_CID_EXPOSURE_AUTO,
+ 1, 0, def != 0);
+ if (!pdev->exposure_auto)
+ return hdl->error;
+
+ /* GET_LUM_CTL, PRESET_SHUTTER_FORMATTER is unreliable */
+ r = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL,
+ READ_SHUTTER_FORMATTER, &def);
+ if (r || def > 655)
+ def = 655;
+ pdev->exposure = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 655, 1, def);
+ /* CODEC2: separate auto gain & auto exposure */
+ v4l2_ctrl_auto_cluster(2, &pdev->autogain, 0, true);
+ v4l2_ctrl_auto_cluster(2, &pdev->exposure_auto,
+ V4L2_EXPOSURE_MANUAL, true);
+ } else if (DEVICE_USE_CODEC3(pdev->type)) {
+ /* GET_LUM_CTL, PRESET_SHUTTER_FORMATTER is unreliable */
+ r = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL,
+ READ_SHUTTER_FORMATTER, &def);
+ if (r || def > 255)
+ def = 255;
+ pdev->exposure = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 255, 1, def);
+ /* CODEC3: both gain and exposure controlled by autogain */
+ pdev->autogain_expo_cluster[0] = pdev->autogain;
+ pdev->autogain_expo_cluster[1] = pdev->gain;
+ pdev->autogain_expo_cluster[2] = pdev->exposure;
+ v4l2_ctrl_auto_cluster(3, pdev->autogain_expo_cluster,
+ 0, true);
+ }
+
+ /* color / bw setting */
+ r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, COLOUR_MODE_FORMATTER,
+ &def);
+ if (r || (def != 0 && def != 0xff))
+ def = 0xff;
+ /* def = 0 bw, def = ff color, menu idx 0 = color, idx 1 = bw */
+ pdev->colorfx = v4l2_ctrl_new_std_menu(hdl, &pwc_ctrl_ops,
+ V4L2_CID_COLORFX, 1, 0, def == 0);
+
+ /* autocontour, contour */
+ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, AUTO_CONTOUR_FORMATTER, &def);
+ if (r || (def != 0 && def != 0xff))
+ def = 0;
+ cfg = pwc_autocontour_cfg;
+ cfg.def = def == 0;
+ pdev->autocontour = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
+ if (!pdev->autocontour)
+ return hdl->error;
+
+ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, PRESET_CONTOUR_FORMATTER, &def);
+ if (r || def > 63)
+ def = 31;
+ cfg = pwc_contour_cfg;
+ cfg.def = def;
+ pdev->contour = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
+
+ v4l2_ctrl_auto_cluster(2, &pdev->autocontour, 0, false);
+
+ /* backlight */
+ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL,
+ BACK_LIGHT_COMPENSATION_FORMATTER, &def);
+ if (r || (def != 0 && def != 0xff))
+ def = 0;
+ cfg = pwc_backlight_cfg;
+ cfg.name = v4l2_ctrl_get_name(cfg.id);
+ cfg.def = def == 0;
+ pdev->backlight = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
+
+ /* flikker rediction */
+ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL,
+ FLICKERLESS_MODE_FORMATTER, &def);
+ if (r || (def != 0 && def != 0xff))
+ def = 0;
+ cfg = pwc_flicker_cfg;
+ cfg.name = v4l2_ctrl_get_name(cfg.id);
+ cfg.def = def == 0;
+ pdev->flicker = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
+
+ /* Dynamic noise reduction */
+ r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL,
+ DYNAMIC_NOISE_CONTROL_FORMATTER, &def);
+ if (r || def > 3)
+ def = 2;
+ cfg = pwc_noise_reduction_cfg;
+ cfg.def = def;
+ pdev->noise_reduction = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
+
+ /* Save / Restore User / Factory Settings */
+ pdev->save_user = v4l2_ctrl_new_custom(hdl, &pwc_save_user_cfg, NULL);
+ pdev->restore_user = v4l2_ctrl_new_custom(hdl, &pwc_restore_user_cfg,
+ NULL);
+ if (pdev->restore_user)
+ pdev->restore_user->flags |= V4L2_CTRL_FLAG_UPDATE;
+ pdev->restore_factory = v4l2_ctrl_new_custom(hdl,
+ &pwc_restore_factory_cfg,
+ NULL);
+ if (pdev->restore_factory)
+ pdev->restore_factory->flags |= V4L2_CTRL_FLAG_UPDATE;
+
+ /* Auto White Balance speed & delay */
+ r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL,
+ AWB_CONTROL_SPEED_FORMATTER, &def);
+ if (r || def < 1 || def > 32)
+ def = 1;
+ cfg = pwc_awb_speed_cfg;
+ cfg.def = def;
+ pdev->awb_speed = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
+
+ r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL,
+ AWB_CONTROL_DELAY_FORMATTER, &def);
+ if (r || def > 63)
+ def = 0;
+ cfg = pwc_awb_delay_cfg;
+ cfg.def = def;
+ pdev->awb_delay = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
+
+ if (!(pdev->features & FEATURE_MOTOR_PANTILT))
+ return hdl->error;
+
+ /* Motor pan / tilt / reset */
+ pdev->motor_pan = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+ V4L2_CID_PAN_RELATIVE, -4480, 4480, 64, 0);
+ if (!pdev->motor_pan)
+ return hdl->error;
+ pdev->motor_tilt = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+ V4L2_CID_TILT_RELATIVE, -1920, 1920, 64, 0);
+ pdev->motor_pan_reset = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+ V4L2_CID_PAN_RESET, 0, 0, 0, 0);
+ pdev->motor_tilt_reset = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops,
+ V4L2_CID_TILT_RESET, 0, 0, 0, 0);
+ v4l2_ctrl_cluster(4, &pdev->motor_pan);
+
+ return hdl->error;
+}
+
+static void pwc_vidioc_fill_fmt(struct v4l2_format *f,
+ int width, int height, u32 pixfmt)
+{
+ memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format));
+ f->fmt.pix.width = width;
+ f->fmt.pix.height = height;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.pixelformat = pixfmt;
+ f->fmt.pix.bytesperline = f->fmt.pix.width;
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.width * 3 / 2;
+ PWC_DEBUG_IOCTL("pwc_vidioc_fill_fmt() "
+ "width=%d, height=%d, bytesperline=%d, sizeimage=%d, pixelformat=%c%c%c%c\n",
+ f->fmt.pix.width,
+ f->fmt.pix.height,
+ f->fmt.pix.bytesperline,
+ f->fmt.pix.sizeimage,
+ (f->fmt.pix.pixelformat)&255,
+ (f->fmt.pix.pixelformat>>8)&255,
+ (f->fmt.pix.pixelformat>>16)&255,
+ (f->fmt.pix.pixelformat>>24)&255);
+}
+
+/* ioctl(VIDIOC_TRY_FMT) */
+static int pwc_vidioc_try_fmt(struct pwc_device *pdev, struct v4l2_format *f)
+{
+ int size;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ PWC_DEBUG_IOCTL("Bad video type must be V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+ return -EINVAL;
+ }
+
+ switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YUV420:
+ break;
+ case V4L2_PIX_FMT_PWC1:
+ if (DEVICE_USE_CODEC23(pdev->type)) {
+ PWC_DEBUG_IOCTL("codec1 is only supported for old pwc webcam\n");
+ return -EINVAL;
+ }
+ break;
+ case V4L2_PIX_FMT_PWC2:
+ if (DEVICE_USE_CODEC1(pdev->type)) {
+ PWC_DEBUG_IOCTL("codec23 is only supported for new pwc webcam\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ PWC_DEBUG_IOCTL("Unsupported pixel format\n");
+ return -EINVAL;
+
+ }
+
+ size = pwc_get_size(pdev, f->fmt.pix.width, f->fmt.pix.height);
+ pwc_vidioc_fill_fmt(f,
+ pwc_image_sizes[size][0],
+ pwc_image_sizes[size][1],
+ f->fmt.pix.pixelformat);
+
+ return 0;
+}
+
+/* ioctl(VIDIOC_SET_FMT) */
+
+static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct pwc_device *pdev = video_drvdata(file);
+ int ret, pixelformat, compression = 0;
+
+ if (pwc_test_n_set_capt_file(pdev, file))
+ return -EBUSY;
+
+ ret = pwc_vidioc_try_fmt(pdev, f);
+ if (ret < 0)
+ return ret;
+
+ pixelformat = f->fmt.pix.pixelformat;
+
+ mutex_lock(&pdev->udevlock);
+ if (!pdev->udev) {
+ ret = -ENODEV;
+ goto leave;
+ }
+
+ if (pdev->iso_init) {
+ ret = -EBUSY;
+ goto leave;
+ }
+
+ PWC_DEBUG_IOCTL("Trying to set format to: width=%d height=%d fps=%d "
+ "format=%c%c%c%c\n",
+ f->fmt.pix.width, f->fmt.pix.height, pdev->vframes,
+ (pixelformat)&255,
+ (pixelformat>>8)&255,
+ (pixelformat>>16)&255,
+ (pixelformat>>24)&255);
+
+ ret = pwc_set_video_mode(pdev, f->fmt.pix.width, f->fmt.pix.height,
+ pixelformat, 30, &compression, 0);
+
+ PWC_DEBUG_IOCTL("pwc_set_video_mode(), return=%d\n", ret);
+
+ pwc_vidioc_fill_fmt(f, pdev->width, pdev->height, pdev->pixfmt);
+leave:
+ mutex_unlock(&pdev->udevlock);
+ return ret;
+}
+
+static int pwc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
+{
+ struct pwc_device *pdev = video_drvdata(file);
+
+ if (!pdev->udev)
+ return -ENODEV;
+
+ strcpy(cap->driver, PWC_NAME);
+ strlcpy(cap->card, pdev->vdev.name, sizeof(cap->card));
+ usb_make_path(pdev->udev, cap->bus_info, sizeof(cap->bus_info));
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_STREAMING |
+ V4L2_CAP_READWRITE;
+ return 0;
+}
+
+static int pwc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
+{
+ if (i->index) /* Only one INPUT is supported */
+ return -EINVAL;
+
+ strcpy(i->name, "usb");
+ return 0;
+}
+
+static int pwc_g_input(struct file *file, void *fh, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int pwc_s_input(struct file *file, void *fh, unsigned int i)
+{
+ return i ? -EINVAL : 0;
+}
+
+static int pwc_g_volatile_ctrl_unlocked(struct v4l2_ctrl *ctrl)
+{
+ struct pwc_device *pdev =
+ container_of(ctrl->handler, struct pwc_device, ctrl_handler);
+ int ret = 0;
+
+ if (!pdev->udev)
+ return -ENODEV;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ if (pdev->color_bal_valid &&
+ (pdev->auto_white_balance->val != awb_auto ||
+ time_before(jiffies,
+ pdev->last_color_bal_update + HZ / 4))) {
+ pdev->red_balance->val = pdev->last_red_balance;
+ pdev->blue_balance->val = pdev->last_blue_balance;
+ break;
+ }
+ ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL,
+ READ_RED_GAIN_FORMATTER,
+ &pdev->red_balance->val);
+ if (ret)
+ break;
+ ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL,
+ READ_BLUE_GAIN_FORMATTER,
+ &pdev->blue_balance->val);
+ if (ret)
+ break;
+ pdev->last_red_balance = pdev->red_balance->val;
+ pdev->last_blue_balance = pdev->blue_balance->val;
+ pdev->last_color_bal_update = jiffies;
+ pdev->color_bal_valid = true;
+ break;
+ case V4L2_CID_AUTOGAIN:
+ if (pdev->gain_valid && time_before(jiffies,
+ pdev->last_gain_update + HZ / 4)) {
+ pdev->gain->val = pdev->last_gain;
+ break;
+ }
+ ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL,
+ READ_AGC_FORMATTER, &pdev->gain->val);
+ if (ret)
+ break;
+ pdev->last_gain = pdev->gain->val;
+ pdev->last_gain_update = jiffies;
+ pdev->gain_valid = true;
+ if (!DEVICE_USE_CODEC3(pdev->type))
+ break;
+ /* Fall through for CODEC3 where autogain also controls expo */
+ case V4L2_CID_EXPOSURE_AUTO:
+ if (pdev->exposure_valid && time_before(jiffies,
+ pdev->last_exposure_update + HZ / 4)) {
+ pdev->exposure->val = pdev->last_exposure;
+ break;
+ }
+ ret = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL,
+ READ_SHUTTER_FORMATTER,
+ &pdev->exposure->val);
+ if (ret)
+ break;
+ pdev->last_exposure = pdev->exposure->val;
+ pdev->last_exposure_update = jiffies;
+ pdev->exposure_valid = true;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ if (ret)
+ PWC_ERROR("g_ctrl %s error %d\n", ctrl->name, ret);
+
+ return ret;
+}
+
+static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct pwc_device *pdev =
+ container_of(ctrl->handler, struct pwc_device, ctrl_handler);
+ int ret;
+
+ mutex_lock(&pdev->udevlock);
+ ret = pwc_g_volatile_ctrl_unlocked(ctrl);
+ mutex_unlock(&pdev->udevlock);
+ return ret;
+}
+
+static int pwc_set_awb(struct pwc_device *pdev)
+{
+ int ret;
+
+ if (pdev->auto_white_balance->is_new) {
+ ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
+ WB_MODE_FORMATTER,
+ pdev->auto_white_balance->val);
+ if (ret)
+ return ret;
+
+ if (pdev->auto_white_balance->val != awb_manual)
+ pdev->color_bal_valid = false; /* Force cache update */
+
+ /*
+ * If this is a preset, update our red / blue balance values
+ * so that events get generated for the new preset values
+ */
+ if (pdev->auto_white_balance->val == awb_indoor ||
+ pdev->auto_white_balance->val == awb_outdoor ||
+ pdev->auto_white_balance->val == awb_fl)
+ pwc_g_volatile_ctrl_unlocked(pdev->auto_white_balance);
+ }
+ if (pdev->auto_white_balance->val != awb_manual)
+ return 0;
+
+ if (pdev->red_balance->is_new) {
+ ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
+ PRESET_MANUAL_RED_GAIN_FORMATTER,
+ pdev->red_balance->val);
+ if (ret)
+ return ret;
+ }
+
+ if (pdev->blue_balance->is_new) {
+ ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
+ PRESET_MANUAL_BLUE_GAIN_FORMATTER,
+ pdev->blue_balance->val);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+/* For CODEC2 models which have separate autogain and auto exposure */
+static int pwc_set_autogain(struct pwc_device *pdev)
+{
+ int ret;
+
+ if (pdev->autogain->is_new) {
+ ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+ AGC_MODE_FORMATTER,
+ pdev->autogain->val ? 0 : 0xff);
+ if (ret)
+ return ret;
+
+ if (pdev->autogain->val)
+ pdev->gain_valid = false; /* Force cache update */
+ }
+
+ if (pdev->autogain->val)
+ return 0;
+
+ if (pdev->gain->is_new) {
+ ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+ PRESET_AGC_FORMATTER,
+ pdev->gain->val);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+/* For CODEC2 models which have separate autogain and auto exposure */
+static int pwc_set_exposure_auto(struct pwc_device *pdev)
+{
+ int ret;
+ int is_auto = pdev->exposure_auto->val == V4L2_EXPOSURE_AUTO;
+
+ if (pdev->exposure_auto->is_new) {
+ ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+ SHUTTER_MODE_FORMATTER,
+ is_auto ? 0 : 0xff);
+ if (ret)
+ return ret;
+
+ if (is_auto)
+ pdev->exposure_valid = false; /* Force cache update */
+ }
+
+ if (is_auto)
+ return 0;
+
+ if (pdev->exposure->is_new) {
+ ret = pwc_set_u16_ctrl(pdev, SET_LUM_CTL,
+ PRESET_SHUTTER_FORMATTER,
+ pdev->exposure->val);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+/* For CODEC3 models which have autogain controlling both gain and exposure */
+static int pwc_set_autogain_expo(struct pwc_device *pdev)
+{
+ int ret;
+
+ if (pdev->autogain->is_new) {
+ ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+ AGC_MODE_FORMATTER,
+ pdev->autogain->val ? 0 : 0xff);
+ if (ret)
+ return ret;
+
+ if (pdev->autogain->val) {
+ pdev->gain_valid = false; /* Force cache update */
+ pdev->exposure_valid = false; /* Force cache update */
+ }
+ }
+
+ if (pdev->autogain->val)
+ return 0;
+
+ if (pdev->gain->is_new) {
+ ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+ PRESET_AGC_FORMATTER,
+ pdev->gain->val);
+ if (ret)
+ return ret;
+ }
+
+ if (pdev->exposure->is_new) {
+ ret = pwc_set_u16_ctrl(pdev, SET_LUM_CTL,
+ PRESET_SHUTTER_FORMATTER,
+ pdev->exposure->val);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int pwc_set_motor(struct pwc_device *pdev)
+{
+ int ret;
+
+ pdev->ctrl_buf[0] = 0;
+ if (pdev->motor_pan_reset->is_new)
+ pdev->ctrl_buf[0] |= 0x01;
+ if (pdev->motor_tilt_reset->is_new)
+ pdev->ctrl_buf[0] |= 0x02;
+ if (pdev->motor_pan_reset->is_new || pdev->motor_tilt_reset->is_new) {
+ ret = send_control_msg(pdev, SET_MPT_CTL,
+ PT_RESET_CONTROL_FORMATTER,
+ pdev->ctrl_buf, 1);
+ if (ret < 0)
+ return ret;
+ }
+
+ memset(pdev->ctrl_buf, 0, 4);
+ if (pdev->motor_pan->is_new) {
+ pdev->ctrl_buf[0] = pdev->motor_pan->val & 0xFF;
+ pdev->ctrl_buf[1] = (pdev->motor_pan->val >> 8);
+ }
+ if (pdev->motor_tilt->is_new) {
+ pdev->ctrl_buf[2] = pdev->motor_tilt->val & 0xFF;
+ pdev->ctrl_buf[3] = (pdev->motor_tilt->val >> 8);
+ }
+ if (pdev->motor_pan->is_new || pdev->motor_tilt->is_new) {
+ ret = send_control_msg(pdev, SET_MPT_CTL,
+ PT_RELATIVE_CONTROL_FORMATTER,
+ pdev->ctrl_buf, 4);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int pwc_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct pwc_device *pdev =
+ container_of(ctrl->handler, struct pwc_device, ctrl_handler);
+ int ret = 0;
+
+ mutex_lock(&pdev->udevlock);
+
+ if (!pdev->udev) {
+ ret = -ENODEV;
+ goto leave;
+ }
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+ BRIGHTNESS_FORMATTER, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+ CONTRAST_FORMATTER, ctrl->val);
+ break;
+ case V4L2_CID_SATURATION:
+ ret = pwc_set_s8_ctrl(pdev, SET_CHROM_CTL,
+ pdev->saturation_fmt, ctrl->val);
+ break;
+ case V4L2_CID_GAMMA:
+ ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+ GAMMA_FORMATTER, ctrl->val);
+ break;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ ret = pwc_set_awb(pdev);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ if (DEVICE_USE_CODEC2(pdev->type))
+ ret = pwc_set_autogain(pdev);
+ else if (DEVICE_USE_CODEC3(pdev->type))
+ ret = pwc_set_autogain_expo(pdev);
+ else
+ ret = -EINVAL;
+ break;
+ case V4L2_CID_EXPOSURE_AUTO:
+ if (DEVICE_USE_CODEC2(pdev->type))
+ ret = pwc_set_exposure_auto(pdev);
+ else
+ ret = -EINVAL;
+ break;
+ case V4L2_CID_COLORFX:
+ ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
+ COLOUR_MODE_FORMATTER,
+ ctrl->val ? 0 : 0xff);
+ break;
+ case PWC_CID_CUSTOM(autocontour):
+ if (pdev->autocontour->is_new) {
+ ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+ AUTO_CONTOUR_FORMATTER,
+ pdev->autocontour->val ? 0 : 0xff);
+ }
+ if (ret == 0 && pdev->contour->is_new) {
+ ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+ PRESET_CONTOUR_FORMATTER,
+ pdev->contour->val);
+ }
+ break;
+ case V4L2_CID_BACKLIGHT_COMPENSATION:
+ ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+ BACK_LIGHT_COMPENSATION_FORMATTER,
+ ctrl->val ? 0 : 0xff);
+ break;
+ case V4L2_CID_BAND_STOP_FILTER:
+ ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+ FLICKERLESS_MODE_FORMATTER,
+ ctrl->val ? 0 : 0xff);
+ break;
+ case PWC_CID_CUSTOM(noise_reduction):
+ ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
+ DYNAMIC_NOISE_CONTROL_FORMATTER,
+ ctrl->val);
+ break;
+ case PWC_CID_CUSTOM(save_user):
+ ret = pwc_button_ctrl(pdev, SAVE_USER_DEFAULTS_FORMATTER);
+ break;
+ case PWC_CID_CUSTOM(restore_user):
+ ret = pwc_button_ctrl(pdev, RESTORE_USER_DEFAULTS_FORMATTER);
+ break;
+ case PWC_CID_CUSTOM(restore_factory):
+ ret = pwc_button_ctrl(pdev,
+ RESTORE_FACTORY_DEFAULTS_FORMATTER);
+ break;
+ case PWC_CID_CUSTOM(awb_speed):
+ ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
+ AWB_CONTROL_SPEED_FORMATTER,
+ ctrl->val);
+ break;
+ case PWC_CID_CUSTOM(awb_delay):
+ ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL,
+ AWB_CONTROL_DELAY_FORMATTER,
+ ctrl->val);
+ break;
+ case V4L2_CID_PAN_RELATIVE:
+ ret = pwc_set_motor(pdev);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ if (ret)
+ PWC_ERROR("s_ctrl %s error %d\n", ctrl->name, ret);
+
+leave:
+ mutex_unlock(&pdev->udevlock);
+ return ret;
+}
+
+static int pwc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+ struct pwc_device *pdev = video_drvdata(file);
+
+ /* We only support two format: the raw format, and YUV */
+ switch (f->index) {
+ case 0:
+ /* RAW format */
+ f->pixelformat = pdev->type <= 646 ? V4L2_PIX_FMT_PWC1 : V4L2_PIX_FMT_PWC2;
+ f->flags = V4L2_FMT_FLAG_COMPRESSED;
+ strlcpy(f->description, "Raw Philips Webcam", sizeof(f->description));
+ break;
+ case 1:
+ f->pixelformat = V4L2_PIX_FMT_YUV420;
+ strlcpy(f->description, "4:2:0, planar, Y-Cb-Cr", sizeof(f->description));
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int pwc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct pwc_device *pdev = video_drvdata(file);
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ mutex_lock(&pdev->udevlock); /* To avoid race with s_fmt */
+ PWC_DEBUG_IOCTL("ioctl(VIDIOC_G_FMT) return size %dx%d\n",
+ pdev->width, pdev->height);
+ pwc_vidioc_fill_fmt(f, pdev->width, pdev->height, pdev->pixfmt);
+ mutex_unlock(&pdev->udevlock);
+ return 0;
+}
+
+static int pwc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct pwc_device *pdev = video_drvdata(file);
+
+ return pwc_vidioc_try_fmt(pdev, f);
+}
+
+static int pwc_reqbufs(struct file *file, void *fh,
+ struct v4l2_requestbuffers *rb)
+{
+ struct pwc_device *pdev = video_drvdata(file);
+
+ if (pwc_test_n_set_capt_file(pdev, file))
+ return -EBUSY;
+
+ return vb2_reqbufs(&pdev->vb_queue, rb);
+}
+
+static int pwc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+ struct pwc_device *pdev = video_drvdata(file);
+
+ return vb2_querybuf(&pdev->vb_queue, buf);
+}
+
+static int pwc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+ struct pwc_device *pdev = video_drvdata(file);
+
+ if (!pdev->udev)
+ return -ENODEV;
+
+ if (pdev->capt_file != file)
+ return -EBUSY;
+
+ return vb2_qbuf(&pdev->vb_queue, buf);
+}
+
+static int pwc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+ struct pwc_device *pdev = video_drvdata(file);
+
+ if (!pdev->udev)
+ return -ENODEV;
+
+ if (pdev->capt_file != file)
+ return -EBUSY;
+
+ return vb2_dqbuf(&pdev->vb_queue, buf, file->f_flags & O_NONBLOCK);
+}
+
+static int pwc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+ struct pwc_device *pdev = video_drvdata(file);
+
+ if (!pdev->udev)
+ return -ENODEV;
+
+ if (pdev->capt_file != file)
+ return -EBUSY;
+
+ return vb2_streamon(&pdev->vb_queue, i);
+}
+
+static int pwc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+ struct pwc_device *pdev = video_drvdata(file);
+
+ if (!pdev->udev)
+ return -ENODEV;
+
+ if (pdev->capt_file != file)
+ return -EBUSY;
+
+ return vb2_streamoff(&pdev->vb_queue, i);
+}
+
+static int pwc_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct pwc_device *pdev = video_drvdata(file);
+ unsigned int i = 0, index = fsize->index;
+
+ if (fsize->pixel_format == V4L2_PIX_FMT_YUV420 ||
+ (fsize->pixel_format == V4L2_PIX_FMT_PWC1 &&
+ DEVICE_USE_CODEC1(pdev->type)) ||
+ (fsize->pixel_format == V4L2_PIX_FMT_PWC2 &&
+ DEVICE_USE_CODEC23(pdev->type))) {
+ for (i = 0; i < PSZ_MAX; i++) {
+ if (!(pdev->image_mask & (1UL << i)))
+ continue;
+ if (!index--) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = pwc_image_sizes[i][0];
+ fsize->discrete.height = pwc_image_sizes[i][1];
+ return 0;
+ }
+ }
+ }
+ return -EINVAL;
+}
+
+static int pwc_enum_frameintervals(struct file *file, void *fh,
+ struct v4l2_frmivalenum *fival)
+{
+ struct pwc_device *pdev = video_drvdata(file);
+ int size = -1;
+ unsigned int i;
+
+ for (i = 0; i < PSZ_MAX; i++) {
+ if (pwc_image_sizes[i][0] == fival->width &&
+ pwc_image_sizes[i][1] == fival->height) {
+ size = i;
+ break;
+ }
+ }
+
+ /* TODO: Support raw format */
+ if (size < 0 || fival->pixel_format != V4L2_PIX_FMT_YUV420)
+ return -EINVAL;
+
+ i = pwc_get_fps(pdev, fival->index, size);
+ if (!i)
+ return -EINVAL;
+
+ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ fival->discrete.numerator = 1;
+ fival->discrete.denominator = i;
+
+ return 0;
+}
+
+static int pwc_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *parm)
+{
+ struct pwc_device *pdev = video_drvdata(file);
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ memset(parm, 0, sizeof(*parm));
+
+ parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ parm->parm.capture.readbuffers = MIN_FRAMES;
+ parm->parm.capture.capability |= V4L2_CAP_TIMEPERFRAME;
+ parm->parm.capture.timeperframe.denominator = pdev->vframes;
+ parm->parm.capture.timeperframe.numerator = 1;
+
+ return 0;
+}
+
+static int pwc_s_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *parm)
+{
+ struct pwc_device *pdev = video_drvdata(file);
+ int compression = 0;
+ int ret, fps;
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ parm->parm.capture.timeperframe.numerator == 0)
+ return -EINVAL;
+
+ if (pwc_test_n_set_capt_file(pdev, file))
+ return -EBUSY;
+
+ fps = parm->parm.capture.timeperframe.denominator /
+ parm->parm.capture.timeperframe.numerator;
+
+ mutex_lock(&pdev->udevlock);
+ if (!pdev->udev) {
+ ret = -ENODEV;
+ goto leave;
+ }
+
+ if (pdev->iso_init) {
+ ret = -EBUSY;
+ goto leave;
+ }
+
+ ret = pwc_set_video_mode(pdev, pdev->width, pdev->height, pdev->pixfmt,
+ fps, &compression, 0);
+
+ pwc_g_parm(file, fh, parm);
+
+leave:
+ mutex_unlock(&pdev->udevlock);
+ return ret;
+}
+
+const struct v4l2_ioctl_ops pwc_ioctl_ops = {
+ .vidioc_querycap = pwc_querycap,
+ .vidioc_enum_input = pwc_enum_input,
+ .vidioc_g_input = pwc_g_input,
+ .vidioc_s_input = pwc_s_input,
+ .vidioc_enum_fmt_vid_cap = pwc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = pwc_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = pwc_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = pwc_try_fmt_vid_cap,
+ .vidioc_reqbufs = pwc_reqbufs,
+ .vidioc_querybuf = pwc_querybuf,
+ .vidioc_qbuf = pwc_qbuf,
+ .vidioc_dqbuf = pwc_dqbuf,
+ .vidioc_streamon = pwc_streamon,
+ .vidioc_streamoff = pwc_streamoff,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_enum_framesizes = pwc_enum_framesizes,
+ .vidioc_enum_frameintervals = pwc_enum_frameintervals,
+ .vidioc_g_parm = pwc_g_parm,
+ .vidioc_s_parm = pwc_s_parm,
+};
diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h
new file mode 100644
index 00000000..e4d4d711
--- /dev/null
+++ b/drivers/media/video/pwc/pwc.h
@@ -0,0 +1,395 @@
+/* (C) 1999-2003 Nemosoft Unv.
+ (C) 2004-2006 Luc Saillard (luc@saillard.org)
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+ The decompression routines have been implemented by reverse-engineering the
+ Nemosoft binary pwcx module. Caveat emptor.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef PWC_H
+#define PWC_H
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/mutex.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <asm/errno.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-vmalloc.h>
+#ifdef CONFIG_USB_PWC_INPUT_EVDEV
+#include <linux/input.h>
+#endif
+#include "pwc-dec1.h"
+#include "pwc-dec23.h"
+
+/* Version block */
+#define PWC_VERSION "10.0.15"
+#define PWC_NAME "pwc"
+#define PFX PWC_NAME ": "
+
+
+/* Trace certain actions in the driver */
+#define PWC_DEBUG_LEVEL_MODULE (1<<0)
+#define PWC_DEBUG_LEVEL_PROBE (1<<1)
+#define PWC_DEBUG_LEVEL_OPEN (1<<2)
+#define PWC_DEBUG_LEVEL_READ (1<<3)
+#define PWC_DEBUG_LEVEL_MEMORY (1<<4)
+#define PWC_DEBUG_LEVEL_FLOW (1<<5)
+#define PWC_DEBUG_LEVEL_SIZE (1<<6)
+#define PWC_DEBUG_LEVEL_IOCTL (1<<7)
+#define PWC_DEBUG_LEVEL_TRACE (1<<8)
+
+#define PWC_DEBUG_MODULE(fmt, args...) PWC_DEBUG(MODULE, fmt, ##args)
+#define PWC_DEBUG_PROBE(fmt, args...) PWC_DEBUG(PROBE, fmt, ##args)
+#define PWC_DEBUG_OPEN(fmt, args...) PWC_DEBUG(OPEN, fmt, ##args)
+#define PWC_DEBUG_READ(fmt, args...) PWC_DEBUG(READ, fmt, ##args)
+#define PWC_DEBUG_MEMORY(fmt, args...) PWC_DEBUG(MEMORY, fmt, ##args)
+#define PWC_DEBUG_FLOW(fmt, args...) PWC_DEBUG(FLOW, fmt, ##args)
+#define PWC_DEBUG_SIZE(fmt, args...) PWC_DEBUG(SIZE, fmt, ##args)
+#define PWC_DEBUG_IOCTL(fmt, args...) PWC_DEBUG(IOCTL, fmt, ##args)
+#define PWC_DEBUG_TRACE(fmt, args...) PWC_DEBUG(TRACE, fmt, ##args)
+
+
+#ifdef CONFIG_USB_PWC_DEBUG
+
+#define PWC_DEBUG_LEVEL (PWC_DEBUG_LEVEL_MODULE)
+
+#define PWC_DEBUG(level, fmt, args...) do {\
+ if ((PWC_DEBUG_LEVEL_ ##level) & pwc_trace) \
+ printk(KERN_DEBUG PFX fmt, ##args); \
+ } while (0)
+
+#define PWC_ERROR(fmt, args...) printk(KERN_ERR PFX fmt, ##args)
+#define PWC_WARNING(fmt, args...) printk(KERN_WARNING PFX fmt, ##args)
+#define PWC_INFO(fmt, args...) printk(KERN_INFO PFX fmt, ##args)
+#define PWC_TRACE(fmt, args...) PWC_DEBUG(TRACE, fmt, ##args)
+
+#else /* if ! CONFIG_USB_PWC_DEBUG */
+
+#define PWC_ERROR(fmt, args...) printk(KERN_ERR PFX fmt, ##args)
+#define PWC_WARNING(fmt, args...) printk(KERN_WARNING PFX fmt, ##args)
+#define PWC_INFO(fmt, args...) printk(KERN_INFO PFX fmt, ##args)
+#define PWC_TRACE(fmt, args...) do { } while(0)
+#define PWC_DEBUG(level, fmt, args...) do { } while(0)
+
+#define pwc_trace 0
+
+#endif
+
+/* Defines for ToUCam cameras */
+#define TOUCAM_HEADER_SIZE 8
+#define TOUCAM_TRAILER_SIZE 4
+
+#define FEATURE_MOTOR_PANTILT 0x0001
+#define FEATURE_CODEC1 0x0002
+#define FEATURE_CODEC2 0x0004
+
+#define MAX_WIDTH 640
+#define MAX_HEIGHT 480
+
+/* Ignore errors in the first N frames, to allow for startup delays */
+#define FRAME_LOWMARK 5
+
+/* Size and number of buffers for the ISO pipe. */
+#define MAX_ISO_BUFS 3
+#define ISO_FRAMES_PER_DESC 10
+#define ISO_MAX_FRAME_SIZE 960
+#define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE)
+
+/* Maximum size after decompression is 640x480 YUV data, 1.5 * 640 * 480 */
+#define PWC_FRAME_SIZE (460800 + TOUCAM_HEADER_SIZE + TOUCAM_TRAILER_SIZE)
+
+/* Absolute minimum and maximum number of buffers available for mmap() */
+#define MIN_FRAMES 2
+#define MAX_FRAMES 16
+
+/* Some macros to quickly find the type of a webcam */
+#define DEVICE_USE_CODEC1(x) ((x)<675)
+#define DEVICE_USE_CODEC2(x) ((x)>=675 && (x)<700)
+#define DEVICE_USE_CODEC3(x) ((x)>=700)
+#define DEVICE_USE_CODEC23(x) ((x)>=675)
+
+/* Request types: video */
+#define SET_LUM_CTL 0x01
+#define GET_LUM_CTL 0x02
+#define SET_CHROM_CTL 0x03
+#define GET_CHROM_CTL 0x04
+#define SET_STATUS_CTL 0x05
+#define GET_STATUS_CTL 0x06
+#define SET_EP_STREAM_CTL 0x07
+#define GET_EP_STREAM_CTL 0x08
+#define GET_XX_CTL 0x09
+#define SET_XX_CTL 0x0A
+#define GET_XY_CTL 0x0B
+#define SET_XY_CTL 0x0C
+#define SET_MPT_CTL 0x0D
+#define GET_MPT_CTL 0x0E
+
+/* Selectors for the Luminance controls [GS]ET_LUM_CTL */
+#define AGC_MODE_FORMATTER 0x2000
+#define PRESET_AGC_FORMATTER 0x2100
+#define SHUTTER_MODE_FORMATTER 0x2200
+#define PRESET_SHUTTER_FORMATTER 0x2300
+#define PRESET_CONTOUR_FORMATTER 0x2400
+#define AUTO_CONTOUR_FORMATTER 0x2500
+#define BACK_LIGHT_COMPENSATION_FORMATTER 0x2600
+#define CONTRAST_FORMATTER 0x2700
+#define DYNAMIC_NOISE_CONTROL_FORMATTER 0x2800
+#define FLICKERLESS_MODE_FORMATTER 0x2900
+#define AE_CONTROL_SPEED 0x2A00
+#define BRIGHTNESS_FORMATTER 0x2B00
+#define GAMMA_FORMATTER 0x2C00
+
+/* Selectors for the Chrominance controls [GS]ET_CHROM_CTL */
+#define WB_MODE_FORMATTER 0x1000
+#define AWB_CONTROL_SPEED_FORMATTER 0x1100
+#define AWB_CONTROL_DELAY_FORMATTER 0x1200
+#define PRESET_MANUAL_RED_GAIN_FORMATTER 0x1300
+#define PRESET_MANUAL_BLUE_GAIN_FORMATTER 0x1400
+#define COLOUR_MODE_FORMATTER 0x1500
+#define SATURATION_MODE_FORMATTER1 0x1600
+#define SATURATION_MODE_FORMATTER2 0x1700
+
+/* Selectors for the Status controls [GS]ET_STATUS_CTL */
+#define SAVE_USER_DEFAULTS_FORMATTER 0x0200
+#define RESTORE_USER_DEFAULTS_FORMATTER 0x0300
+#define RESTORE_FACTORY_DEFAULTS_FORMATTER 0x0400
+#define READ_AGC_FORMATTER 0x0500
+#define READ_SHUTTER_FORMATTER 0x0600
+#define READ_RED_GAIN_FORMATTER 0x0700
+#define READ_BLUE_GAIN_FORMATTER 0x0800
+
+/* Formatters for the motorized pan & tilt [GS]ET_MPT_CTL */
+#define PT_RELATIVE_CONTROL_FORMATTER 0x01
+#define PT_RESET_CONTROL_FORMATTER 0x02
+#define PT_STATUS_FORMATTER 0x03
+
+/* Enumeration of image sizes */
+#define PSZ_SQCIF 0x00
+#define PSZ_QSIF 0x01
+#define PSZ_QCIF 0x02
+#define PSZ_SIF 0x03
+#define PSZ_CIF 0x04
+#define PSZ_VGA 0x05
+#define PSZ_MAX 6
+
+struct pwc_raw_frame {
+ __le16 type; /* type of the webcam */
+ __le16 vbandlength; /* Size of 4 lines compressed (used by the
+ decompressor) */
+ __u8 cmd[4]; /* the four byte of the command (in case of
+ nala, only the first 3 bytes is filled) */
+ __u8 rawframe[0]; /* frame_size = H / 4 * vbandlength */
+} __packed;
+
+/* intermediate buffers with raw data from the USB cam */
+struct pwc_frame_buf
+{
+ struct vb2_buffer vb; /* common v4l buffer stuff -- must be first */
+ struct list_head list;
+ void *data;
+ int filled; /* number of bytes filled */
+};
+
+struct pwc_device
+{
+ struct video_device vdev;
+ struct v4l2_device v4l2_dev;
+
+ /* Pointer to our usb_device, may be NULL after unplug */
+ struct usb_device *udev;
+ struct mutex udevlock;
+
+ /* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */
+ int type;
+ int release; /* release number */
+ int features; /* feature bits */
+
+ /*** Video data ***/
+ struct file *capt_file; /* file doing video capture */
+ struct mutex capt_file_lock;
+ int vendpoint; /* video isoc endpoint */
+ int vcinterface; /* video control interface */
+ int valternate; /* alternate interface needed */
+ int vframes; /* frames-per-second */
+ int pixfmt; /* pixelformat: V4L2_PIX_FMT_YUV420 or _PWCX */
+ int vframe_count; /* received frames */
+ int vmax_packet_size; /* USB maxpacket size */
+ int vlast_packet_size; /* for frame synchronisation */
+ int visoc_errors; /* number of contiguous ISOC errors */
+ int vbandlength; /* compressed band length; 0 is uncompressed */
+ char vsync; /* used by isoc handler */
+ char vmirror; /* for ToUCaM series */
+ char power_save; /* Do powersaving for this cam */
+
+ unsigned char cmd_buf[13];
+ unsigned char *ctrl_buf;
+
+ struct urb *urbs[MAX_ISO_BUFS];
+ char iso_init;
+
+ /* videobuf2 queue and queued buffers list */
+ struct vb2_queue vb_queue;
+ struct list_head queued_bufs;
+ spinlock_t queued_bufs_lock;
+
+ /*
+ * Frame currently being filled, this only gets touched by the
+ * isoc urb complete handler, and by stream start / stop since
+ * start / stop touch it before / after starting / killing the urbs
+ * no locking is needed around this
+ */
+ struct pwc_frame_buf *fill_buf;
+
+ int frame_header_size, frame_trailer_size;
+ int frame_size;
+ int frame_total_size; /* including header & trailer */
+ int drop_frames;
+
+ union { /* private data for decompression engine */
+ struct pwc_dec1_private dec1;
+ struct pwc_dec23_private dec23;
+ };
+
+ /*
+ * We have an 'image' and a 'view', where 'image' is the fixed-size img
+ * as delivered by the camera, and 'view' is the size requested by the
+ * program. The camera image is centered in this viewport, laced with
+ * a gray or black border. view_min <= image <= view <= view_max;
+ */
+ int image_mask; /* supported sizes */
+ int width, height; /* current resolution */
+
+#ifdef CONFIG_USB_PWC_INPUT_EVDEV
+ struct input_dev *button_dev; /* webcam snapshot button input */
+ char button_phys[64];
+#endif
+
+ /* controls */
+ struct v4l2_ctrl_handler ctrl_handler;
+ u16 saturation_fmt;
+ struct v4l2_ctrl *brightness;
+ struct v4l2_ctrl *contrast;
+ struct v4l2_ctrl *saturation;
+ struct v4l2_ctrl *gamma;
+ struct {
+ /* awb / red-blue balance cluster */
+ struct v4l2_ctrl *auto_white_balance;
+ struct v4l2_ctrl *red_balance;
+ struct v4l2_ctrl *blue_balance;
+ /* usb ctrl transfers are slow, so we cache things */
+ int color_bal_valid;
+ unsigned long last_color_bal_update; /* In jiffies */
+ s32 last_red_balance;
+ s32 last_blue_balance;
+ };
+ struct {
+ /* autogain / gain cluster */
+ struct v4l2_ctrl *autogain;
+ struct v4l2_ctrl *gain;
+ int gain_valid;
+ unsigned long last_gain_update; /* In jiffies */
+ s32 last_gain;
+ };
+ struct {
+ /* exposure_auto / exposure cluster */
+ struct v4l2_ctrl *exposure_auto;
+ struct v4l2_ctrl *exposure;
+ int exposure_valid;
+ unsigned long last_exposure_update; /* In jiffies */
+ s32 last_exposure;
+ };
+ struct v4l2_ctrl *colorfx;
+ struct {
+ /* autocontour/contour cluster */
+ struct v4l2_ctrl *autocontour;
+ struct v4l2_ctrl *contour;
+ };
+ struct v4l2_ctrl *backlight;
+ struct v4l2_ctrl *flicker;
+ struct v4l2_ctrl *noise_reduction;
+ struct v4l2_ctrl *save_user;
+ struct v4l2_ctrl *restore_user;
+ struct v4l2_ctrl *restore_factory;
+ struct v4l2_ctrl *awb_speed;
+ struct v4l2_ctrl *awb_delay;
+ struct {
+ /* motor control cluster */
+ struct v4l2_ctrl *motor_pan;
+ struct v4l2_ctrl *motor_tilt;
+ struct v4l2_ctrl *motor_pan_reset;
+ struct v4l2_ctrl *motor_tilt_reset;
+ };
+ /* CODEC3 models have both gain and exposure controlled by autogain */
+ struct v4l2_ctrl *autogain_expo_cluster[3];
+};
+
+/* Global variables */
+#ifdef CONFIG_USB_PWC_DEBUG
+extern int pwc_trace;
+#endif
+
+int pwc_test_n_set_capt_file(struct pwc_device *pdev, struct file *file);
+
+/** Functions in pwc-misc.c */
+/* sizes in pixels */
+extern const int pwc_image_sizes[PSZ_MAX][2];
+
+int pwc_get_size(struct pwc_device *pdev, int width, int height);
+void pwc_construct(struct pwc_device *pdev);
+
+/** Functions in pwc-ctrl.c */
+/* Request a certain video mode. Returns < 0 if not possible */
+extern int pwc_set_video_mode(struct pwc_device *pdev, int width, int height,
+ int pixfmt, int frames, int *compression, int send_to_cam);
+extern unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size);
+extern int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value);
+extern int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor);
+extern int send_control_msg(struct pwc_device *pdev,
+ u8 request, u16 value, void *buf, int buflen);
+
+/* Control get / set helpers */
+int pwc_get_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data);
+int pwc_set_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, u8 data);
+int pwc_get_s8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data);
+#define pwc_set_s8_ctrl pwc_set_u8_ctrl
+int pwc_get_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *dat);
+int pwc_set_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, u16 data);
+int pwc_button_ctrl(struct pwc_device *pdev, u16 value);
+int pwc_init_controls(struct pwc_device *pdev);
+
+/* Power down or up the camera; not supported by all models */
+extern void pwc_camera_power(struct pwc_device *pdev, int power);
+
+extern const struct v4l2_ioctl_ops pwc_ioctl_ops;
+
+/** pwc-uncompress.c */
+/* Expand frame to image, possibly including decompression. Uses read_frame and fill_image */
+int pwc_decompress(struct pwc_device *pdev, struct pwc_frame_buf *fbuf);
+
+#endif
diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c
new file mode 100644
index 00000000..5a413f44
--- /dev/null
+++ b/drivers/media/video/pxa_camera.c
@@ -0,0 +1,1861 @@
+/*
+ * V4L2 Driver for PXA camera host
+ *
+ * Copyright (C) 2006, Sascha Hauer, Pengutronix
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/videobuf-dma-sg.h>
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
+
+#include <linux/videodev2.h>
+
+#include <mach/dma.h>
+#include <mach/camera.h>
+
+#define PXA_CAM_VERSION "0.0.6"
+#define PXA_CAM_DRV_NAME "pxa27x-camera"
+
+/* Camera Interface */
+#define CICR0 0x0000
+#define CICR1 0x0004
+#define CICR2 0x0008
+#define CICR3 0x000C
+#define CICR4 0x0010
+#define CISR 0x0014
+#define CIFR 0x0018
+#define CITOR 0x001C
+#define CIBR0 0x0028
+#define CIBR1 0x0030
+#define CIBR2 0x0038
+
+#define CICR0_DMAEN (1 << 31) /* DMA request enable */
+#define CICR0_PAR_EN (1 << 30) /* Parity enable */
+#define CICR0_SL_CAP_EN (1 << 29) /* Capture enable for slave mode */
+#define CICR0_ENB (1 << 28) /* Camera interface enable */
+#define CICR0_DIS (1 << 27) /* Camera interface disable */
+#define CICR0_SIM (0x7 << 24) /* Sensor interface mode mask */
+#define CICR0_TOM (1 << 9) /* Time-out mask */
+#define CICR0_RDAVM (1 << 8) /* Receive-data-available mask */
+#define CICR0_FEM (1 << 7) /* FIFO-empty mask */
+#define CICR0_EOLM (1 << 6) /* End-of-line mask */
+#define CICR0_PERRM (1 << 5) /* Parity-error mask */
+#define CICR0_QDM (1 << 4) /* Quick-disable mask */
+#define CICR0_CDM (1 << 3) /* Disable-done mask */
+#define CICR0_SOFM (1 << 2) /* Start-of-frame mask */
+#define CICR0_EOFM (1 << 1) /* End-of-frame mask */
+#define CICR0_FOM (1 << 0) /* FIFO-overrun mask */
+
+#define CICR1_TBIT (1 << 31) /* Transparency bit */
+#define CICR1_RGBT_CONV (0x3 << 29) /* RGBT conversion mask */
+#define CICR1_PPL (0x7ff << 15) /* Pixels per line mask */
+#define CICR1_RGB_CONV (0x7 << 12) /* RGB conversion mask */
+#define CICR1_RGB_F (1 << 11) /* RGB format */
+#define CICR1_YCBCR_F (1 << 10) /* YCbCr format */
+#define CICR1_RGB_BPP (0x7 << 7) /* RGB bis per pixel mask */
+#define CICR1_RAW_BPP (0x3 << 5) /* Raw bis per pixel mask */
+#define CICR1_COLOR_SP (0x3 << 3) /* Color space mask */
+#define CICR1_DW (0x7 << 0) /* Data width mask */
+
+#define CICR2_BLW (0xff << 24) /* Beginning-of-line pixel clock
+ wait count mask */
+#define CICR2_ELW (0xff << 16) /* End-of-line pixel clock
+ wait count mask */
+#define CICR2_HSW (0x3f << 10) /* Horizontal sync pulse width mask */
+#define CICR2_BFPW (0x3f << 3) /* Beginning-of-frame pixel clock
+ wait count mask */
+#define CICR2_FSW (0x7 << 0) /* Frame stabilization
+ wait count mask */
+
+#define CICR3_BFW (0xff << 24) /* Beginning-of-frame line clock
+ wait count mask */
+#define CICR3_EFW (0xff << 16) /* End-of-frame line clock
+ wait count mask */
+#define CICR3_VSW (0x3f << 10) /* Vertical sync pulse width mask */
+#define CICR3_BFPW (0x3f << 3) /* Beginning-of-frame pixel clock
+ wait count mask */
+#define CICR3_LPF (0x7ff << 0) /* Lines per frame mask */
+
+#define CICR4_MCLK_DLY (0x3 << 24) /* MCLK Data Capture Delay mask */
+#define CICR4_PCLK_EN (1 << 23) /* Pixel clock enable */
+#define CICR4_PCP (1 << 22) /* Pixel clock polarity */
+#define CICR4_HSP (1 << 21) /* Horizontal sync polarity */
+#define CICR4_VSP (1 << 20) /* Vertical sync polarity */
+#define CICR4_MCLK_EN (1 << 19) /* MCLK enable */
+#define CICR4_FR_RATE (0x7 << 8) /* Frame rate mask */
+#define CICR4_DIV (0xff << 0) /* Clock divisor mask */
+
+#define CISR_FTO (1 << 15) /* FIFO time-out */
+#define CISR_RDAV_2 (1 << 14) /* Channel 2 receive data available */
+#define CISR_RDAV_1 (1 << 13) /* Channel 1 receive data available */
+#define CISR_RDAV_0 (1 << 12) /* Channel 0 receive data available */
+#define CISR_FEMPTY_2 (1 << 11) /* Channel 2 FIFO empty */
+#define CISR_FEMPTY_1 (1 << 10) /* Channel 1 FIFO empty */
+#define CISR_FEMPTY_0 (1 << 9) /* Channel 0 FIFO empty */
+#define CISR_EOL (1 << 8) /* End of line */
+#define CISR_PAR_ERR (1 << 7) /* Parity error */
+#define CISR_CQD (1 << 6) /* Camera interface quick disable */
+#define CISR_CDD (1 << 5) /* Camera interface disable done */
+#define CISR_SOF (1 << 4) /* Start of frame */
+#define CISR_EOF (1 << 3) /* End of frame */
+#define CISR_IFO_2 (1 << 2) /* FIFO overrun for Channel 2 */
+#define CISR_IFO_1 (1 << 1) /* FIFO overrun for Channel 1 */
+#define CISR_IFO_0 (1 << 0) /* FIFO overrun for Channel 0 */
+
+#define CIFR_FLVL2 (0x7f << 23) /* FIFO 2 level mask */
+#define CIFR_FLVL1 (0x7f << 16) /* FIFO 1 level mask */
+#define CIFR_FLVL0 (0xff << 8) /* FIFO 0 level mask */
+#define CIFR_THL_0 (0x3 << 4) /* Threshold Level for Channel 0 FIFO */
+#define CIFR_RESET_F (1 << 3) /* Reset input FIFOs */
+#define CIFR_FEN2 (1 << 2) /* FIFO enable for channel 2 */
+#define CIFR_FEN1 (1 << 1) /* FIFO enable for channel 1 */
+#define CIFR_FEN0 (1 << 0) /* FIFO enable for channel 0 */
+
+#define CICR0_SIM_MP (0 << 24)
+#define CICR0_SIM_SP (1 << 24)
+#define CICR0_SIM_MS (2 << 24)
+#define CICR0_SIM_EP (3 << 24)
+#define CICR0_SIM_ES (4 << 24)
+
+#define CICR1_DW_VAL(x) ((x) & CICR1_DW) /* Data bus width */
+#define CICR1_PPL_VAL(x) (((x) << 15) & CICR1_PPL) /* Pixels per line */
+#define CICR1_COLOR_SP_VAL(x) (((x) << 3) & CICR1_COLOR_SP) /* color space */
+#define CICR1_RGB_BPP_VAL(x) (((x) << 7) & CICR1_RGB_BPP) /* bpp for rgb */
+#define CICR1_RGBT_CONV_VAL(x) (((x) << 29) & CICR1_RGBT_CONV) /* rgbt conv */
+
+#define CICR2_BLW_VAL(x) (((x) << 24) & CICR2_BLW) /* Beginning-of-line pixel clock wait count */
+#define CICR2_ELW_VAL(x) (((x) << 16) & CICR2_ELW) /* End-of-line pixel clock wait count */
+#define CICR2_HSW_VAL(x) (((x) << 10) & CICR2_HSW) /* Horizontal sync pulse width */
+#define CICR2_BFPW_VAL(x) (((x) << 3) & CICR2_BFPW) /* Beginning-of-frame pixel clock wait count */
+#define CICR2_FSW_VAL(x) (((x) << 0) & CICR2_FSW) /* Frame stabilization wait count */
+
+#define CICR3_BFW_VAL(x) (((x) << 24) & CICR3_BFW) /* Beginning-of-frame line clock wait count */
+#define CICR3_EFW_VAL(x) (((x) << 16) & CICR3_EFW) /* End-of-frame line clock wait count */
+#define CICR3_VSW_VAL(x) (((x) << 11) & CICR3_VSW) /* Vertical sync pulse width */
+#define CICR3_LPF_VAL(x) (((x) << 0) & CICR3_LPF) /* Lines per frame */
+
+#define CICR0_IRQ_MASK (CICR0_TOM | CICR0_RDAVM | CICR0_FEM | CICR0_EOLM | \
+ CICR0_PERRM | CICR0_QDM | CICR0_CDM | CICR0_SOFM | \
+ CICR0_EOFM | CICR0_FOM)
+
+/*
+ * Structures
+ */
+enum pxa_camera_active_dma {
+ DMA_Y = 0x1,
+ DMA_U = 0x2,
+ DMA_V = 0x4,
+};
+
+/* descriptor needed for the PXA DMA engine */
+struct pxa_cam_dma {
+ dma_addr_t sg_dma;
+ struct pxa_dma_desc *sg_cpu;
+ size_t sg_size;
+ int sglen;
+};
+
+/* buffer for one video frame */
+struct pxa_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+ enum v4l2_mbus_pixelcode code;
+ /* our descriptor lists for Y, U and V channels */
+ struct pxa_cam_dma dmas[3];
+ int inwork;
+ enum pxa_camera_active_dma active_dma;
+};
+
+struct pxa_camera_dev {
+ struct soc_camera_host soc_host;
+ /*
+ * PXA27x is only supposed to handle one camera on its Quick Capture
+ * interface. If anyone ever builds hardware to enable more than
+ * one camera, they will have to modify this driver too
+ */
+ struct soc_camera_device *icd;
+ struct clk *clk;
+
+ unsigned int irq;
+ void __iomem *base;
+
+ int channels;
+ unsigned int dma_chans[3];
+
+ struct pxacamera_platform_data *pdata;
+ struct resource *res;
+ unsigned long platform_flags;
+ unsigned long ciclk;
+ unsigned long mclk;
+ u32 mclk_divisor;
+ u16 width_flags; /* max 10 bits */
+
+ struct list_head capture;
+
+ spinlock_t lock;
+
+ struct pxa_buffer *active;
+ struct pxa_dma_desc *sg_tail[3];
+
+ u32 save_cicr[5];
+};
+
+struct pxa_cam {
+ unsigned long flags;
+};
+
+static const char *pxa_cam_driver_description = "PXA_Camera";
+
+static unsigned int vid_limit = 16; /* Video memory limit, in Mb */
+
+/*
+ * Videobuf operations
+ */
+static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
+ unsigned int *size)
+{
+ struct soc_camera_device *icd = vq->priv_data;
+ int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+
+ if (bytes_per_line < 0)
+ return bytes_per_line;
+
+ dev_dbg(icd->parent, "count=%d, size=%d\n", *count, *size);
+
+ *size = bytes_per_line * icd->user_height;
+
+ if (0 == *count)
+ *count = 32;
+ if (*size * *count > vid_limit * 1024 * 1024)
+ *count = (vid_limit * 1024 * 1024) / *size;
+
+ return 0;
+}
+
+static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf)
+{
+ struct soc_camera_device *icd = vq->priv_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
+ int i;
+
+ BUG_ON(in_interrupt());
+
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+ &buf->vb, buf->vb.baddr, buf->vb.bsize);
+
+ /*
+ * This waits until this buffer is out of danger, i.e., until it is no
+ * longer in STATE_QUEUED or STATE_ACTIVE
+ */
+ videobuf_waiton(vq, &buf->vb, 0, 0);
+ videobuf_dma_unmap(vq->dev, dma);
+ videobuf_dma_free(dma);
+
+ for (i = 0; i < ARRAY_SIZE(buf->dmas); i++) {
+ if (buf->dmas[i].sg_cpu)
+ dma_free_coherent(ici->v4l2_dev.dev,
+ buf->dmas[i].sg_size,
+ buf->dmas[i].sg_cpu,
+ buf->dmas[i].sg_dma);
+ buf->dmas[i].sg_cpu = NULL;
+ }
+
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int calculate_dma_sglen(struct scatterlist *sglist, int sglen,
+ int sg_first_ofs, int size)
+{
+ int i, offset, dma_len, xfer_len;
+ struct scatterlist *sg;
+
+ offset = sg_first_ofs;
+ for_each_sg(sglist, sg, sglen, i) {
+ dma_len = sg_dma_len(sg);
+
+ /* PXA27x Developer's Manual 27.4.4.1: round up to 8 bytes */
+ xfer_len = roundup(min(dma_len - offset, size), 8);
+
+ size = max(0, size - xfer_len);
+ offset = 0;
+ if (size == 0)
+ break;
+ }
+
+ BUG_ON(size != 0);
+ return i + 1;
+}
+
+/**
+ * pxa_init_dma_channel - init dma descriptors
+ * @pcdev: pxa camera device
+ * @buf: pxa buffer to find pxa dma channel
+ * @dma: dma video buffer
+ * @channel: dma channel (0 => 'Y', 1 => 'U', 2 => 'V')
+ * @cibr: camera Receive Buffer Register
+ * @size: bytes to transfer
+ * @sg_first: first element of sg_list
+ * @sg_first_ofs: offset in first element of sg_list
+ *
+ * Prepares the pxa dma descriptors to transfer one camera channel.
+ * Beware sg_first and sg_first_ofs are both input and output parameters.
+ *
+ * Returns 0 or -ENOMEM if no coherent memory is available
+ */
+static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev,
+ struct pxa_buffer *buf,
+ struct videobuf_dmabuf *dma, int channel,
+ int cibr, int size,
+ struct scatterlist **sg_first, int *sg_first_ofs)
+{
+ struct pxa_cam_dma *pxa_dma = &buf->dmas[channel];
+ struct device *dev = pcdev->soc_host.v4l2_dev.dev;
+ struct scatterlist *sg;
+ int i, offset, sglen;
+ int dma_len = 0, xfer_len = 0;
+
+ if (pxa_dma->sg_cpu)
+ dma_free_coherent(dev, pxa_dma->sg_size,
+ pxa_dma->sg_cpu, pxa_dma->sg_dma);
+
+ sglen = calculate_dma_sglen(*sg_first, dma->sglen,
+ *sg_first_ofs, size);
+
+ pxa_dma->sg_size = (sglen + 1) * sizeof(struct pxa_dma_desc);
+ pxa_dma->sg_cpu = dma_alloc_coherent(dev, pxa_dma->sg_size,
+ &pxa_dma->sg_dma, GFP_KERNEL);
+ if (!pxa_dma->sg_cpu)
+ return -ENOMEM;
+
+ pxa_dma->sglen = sglen;
+ offset = *sg_first_ofs;
+
+ dev_dbg(dev, "DMA: sg_first=%p, sglen=%d, ofs=%d, dma.desc=%x\n",
+ *sg_first, sglen, *sg_first_ofs, pxa_dma->sg_dma);
+
+
+ for_each_sg(*sg_first, sg, sglen, i) {
+ dma_len = sg_dma_len(sg);
+
+ /* PXA27x Developer's Manual 27.4.4.1: round up to 8 bytes */
+ xfer_len = roundup(min(dma_len - offset, size), 8);
+
+ size = max(0, size - xfer_len);
+
+ pxa_dma->sg_cpu[i].dsadr = pcdev->res->start + cibr;
+ pxa_dma->sg_cpu[i].dtadr = sg_dma_address(sg) + offset;
+ pxa_dma->sg_cpu[i].dcmd =
+ DCMD_FLOWSRC | DCMD_BURST8 | DCMD_INCTRGADDR | xfer_len;
+#ifdef DEBUG
+ if (!i)
+ pxa_dma->sg_cpu[i].dcmd |= DCMD_STARTIRQEN;
+#endif
+ pxa_dma->sg_cpu[i].ddadr =
+ pxa_dma->sg_dma + (i + 1) * sizeof(struct pxa_dma_desc);
+
+ dev_vdbg(dev, "DMA: desc.%08x->@phys=0x%08x, len=%d\n",
+ pxa_dma->sg_dma + i * sizeof(struct pxa_dma_desc),
+ sg_dma_address(sg) + offset, xfer_len);
+ offset = 0;
+
+ if (size == 0)
+ break;
+ }
+
+ pxa_dma->sg_cpu[sglen].ddadr = DDADR_STOP;
+ pxa_dma->sg_cpu[sglen].dcmd = DCMD_FLOWSRC | DCMD_BURST8 | DCMD_ENDIRQEN;
+
+ /*
+ * Handle 1 special case :
+ * - in 3 planes (YUV422P format), we might finish with xfer_len equal
+ * to dma_len (end on PAGE boundary). In this case, the sg element
+ * for next plane should be the next after the last used to store the
+ * last scatter gather RAM page
+ */
+ if (xfer_len >= dma_len) {
+ *sg_first_ofs = xfer_len - dma_len;
+ *sg_first = sg_next(sg);
+ } else {
+ *sg_first_ofs = xfer_len;
+ *sg_first = sg;
+ }
+
+ return 0;
+}
+
+static void pxa_videobuf_set_actdma(struct pxa_camera_dev *pcdev,
+ struct pxa_buffer *buf)
+{
+ buf->active_dma = DMA_Y;
+ if (pcdev->channels == 3)
+ buf->active_dma |= DMA_U | DMA_V;
+}
+
+/*
+ * Please check the DMA prepared buffer structure in :
+ * Documentation/video4linux/pxa_camera.txt
+ * Please check also in pxa_camera_check_link_miss() to understand why DMA chain
+ * modification while DMA chain is running will work anyway.
+ */
+static int pxa_videobuf_prepare(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb, enum v4l2_field field)
+{
+ struct soc_camera_device *icd = vq->priv_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct pxa_camera_dev *pcdev = ici->priv;
+ struct device *dev = pcdev->soc_host.v4l2_dev.dev;
+ struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
+ int ret;
+ int size_y, size_u = 0, size_v = 0;
+ int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+
+ if (bytes_per_line < 0)
+ return bytes_per_line;
+
+ dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+ vb, vb->baddr, vb->bsize);
+
+ /* Added list head initialization on alloc */
+ WARN_ON(!list_empty(&vb->queue));
+
+#ifdef DEBUG
+ /*
+ * This can be useful if you want to see if we actually fill
+ * the buffer with something
+ */
+ memset((void *)vb->baddr, 0xaa, vb->bsize);
+#endif
+
+ BUG_ON(NULL == icd->current_fmt);
+
+ /*
+ * I think, in buf_prepare you only have to protect global data,
+ * the actual buffer is yours
+ */
+ buf->inwork = 1;
+
+ if (buf->code != icd->current_fmt->code ||
+ vb->width != icd->user_width ||
+ vb->height != icd->user_height ||
+ vb->field != field) {
+ buf->code = icd->current_fmt->code;
+ vb->width = icd->user_width;
+ vb->height = icd->user_height;
+ vb->field = field;
+ vb->state = VIDEOBUF_NEEDS_INIT;
+ }
+
+ vb->size = bytes_per_line * vb->height;
+ if (0 != vb->baddr && vb->bsize < vb->size) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (vb->state == VIDEOBUF_NEEDS_INIT) {
+ int size = vb->size;
+ int next_ofs = 0;
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+ struct scatterlist *sg;
+
+ ret = videobuf_iolock(vq, vb, NULL);
+ if (ret)
+ goto fail;
+
+ if (pcdev->channels == 3) {
+ size_y = size / 2;
+ size_u = size_v = size / 4;
+ } else {
+ size_y = size;
+ }
+
+ sg = dma->sglist;
+
+ /* init DMA for Y channel */
+ ret = pxa_init_dma_channel(pcdev, buf, dma, 0, CIBR0, size_y,
+ &sg, &next_ofs);
+ if (ret) {
+ dev_err(dev, "DMA initialization for Y/RGB failed\n");
+ goto fail;
+ }
+
+ /* init DMA for U channel */
+ if (size_u)
+ ret = pxa_init_dma_channel(pcdev, buf, dma, 1, CIBR1,
+ size_u, &sg, &next_ofs);
+ if (ret) {
+ dev_err(dev, "DMA initialization for U failed\n");
+ goto fail_u;
+ }
+
+ /* init DMA for V channel */
+ if (size_v)
+ ret = pxa_init_dma_channel(pcdev, buf, dma, 2, CIBR2,
+ size_v, &sg, &next_ofs);
+ if (ret) {
+ dev_err(dev, "DMA initialization for V failed\n");
+ goto fail_v;
+ }
+
+ vb->state = VIDEOBUF_PREPARED;
+ }
+
+ buf->inwork = 0;
+ pxa_videobuf_set_actdma(pcdev, buf);
+
+ return 0;
+
+fail_v:
+ dma_free_coherent(dev, buf->dmas[1].sg_size,
+ buf->dmas[1].sg_cpu, buf->dmas[1].sg_dma);
+fail_u:
+ dma_free_coherent(dev, buf->dmas[0].sg_size,
+ buf->dmas[0].sg_cpu, buf->dmas[0].sg_dma);
+fail:
+ free_buffer(vq, buf);
+out:
+ buf->inwork = 0;
+ return ret;
+}
+
+/**
+ * pxa_dma_start_channels - start DMA channel for active buffer
+ * @pcdev: pxa camera device
+ *
+ * Initialize DMA channels to the beginning of the active video buffer, and
+ * start these channels.
+ */
+static void pxa_dma_start_channels(struct pxa_camera_dev *pcdev)
+{
+ int i;
+ struct pxa_buffer *active;
+
+ active = pcdev->active;
+
+ for (i = 0; i < pcdev->channels; i++) {
+ dev_dbg(pcdev->soc_host.v4l2_dev.dev,
+ "%s (channel=%d) ddadr=%08x\n", __func__,
+ i, active->dmas[i].sg_dma);
+ DDADR(pcdev->dma_chans[i]) = active->dmas[i].sg_dma;
+ DCSR(pcdev->dma_chans[i]) = DCSR_RUN;
+ }
+}
+
+static void pxa_dma_stop_channels(struct pxa_camera_dev *pcdev)
+{
+ int i;
+
+ for (i = 0; i < pcdev->channels; i++) {
+ dev_dbg(pcdev->soc_host.v4l2_dev.dev,
+ "%s (channel=%d)\n", __func__, i);
+ DCSR(pcdev->dma_chans[i]) = 0;
+ }
+}
+
+static void pxa_dma_add_tail_buf(struct pxa_camera_dev *pcdev,
+ struct pxa_buffer *buf)
+{
+ int i;
+ struct pxa_dma_desc *buf_last_desc;
+
+ for (i = 0; i < pcdev->channels; i++) {
+ buf_last_desc = buf->dmas[i].sg_cpu + buf->dmas[i].sglen;
+ buf_last_desc->ddadr = DDADR_STOP;
+
+ if (pcdev->sg_tail[i])
+ /* Link the new buffer to the old tail */
+ pcdev->sg_tail[i]->ddadr = buf->dmas[i].sg_dma;
+
+ /* Update the channel tail */
+ pcdev->sg_tail[i] = buf_last_desc;
+ }
+}
+
+/**
+ * pxa_camera_start_capture - start video capturing
+ * @pcdev: camera device
+ *
+ * Launch capturing. DMA channels should not be active yet. They should get
+ * activated at the end of frame interrupt, to capture only whole frames, and
+ * never begin the capture of a partial frame.
+ */
+static void pxa_camera_start_capture(struct pxa_camera_dev *pcdev)
+{
+ unsigned long cicr0;
+
+ dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s\n", __func__);
+ /* Enable End-Of-Frame Interrupt */
+ cicr0 = __raw_readl(pcdev->base + CICR0) | CICR0_ENB;
+ cicr0 &= ~CICR0_EOFM;
+ __raw_writel(cicr0, pcdev->base + CICR0);
+}
+
+static void pxa_camera_stop_capture(struct pxa_camera_dev *pcdev)
+{
+ unsigned long cicr0;
+
+ pxa_dma_stop_channels(pcdev);
+
+ cicr0 = __raw_readl(pcdev->base + CICR0) & ~CICR0_ENB;
+ __raw_writel(cicr0, pcdev->base + CICR0);
+
+ pcdev->active = NULL;
+ dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s\n", __func__);
+}
+
+/* Called under spinlock_irqsave(&pcdev->lock, ...) */
+static void pxa_videobuf_queue(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct soc_camera_device *icd = vq->priv_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct pxa_camera_dev *pcdev = ici->priv;
+ struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
+
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d active=%p\n",
+ __func__, vb, vb->baddr, vb->bsize, pcdev->active);
+
+ list_add_tail(&vb->queue, &pcdev->capture);
+
+ vb->state = VIDEOBUF_ACTIVE;
+ pxa_dma_add_tail_buf(pcdev, buf);
+
+ if (!pcdev->active)
+ pxa_camera_start_capture(pcdev);
+}
+
+static void pxa_videobuf_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
+#ifdef DEBUG
+ struct soc_camera_device *icd = vq->priv_data;
+ struct device *dev = icd->parent;
+
+ dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+ vb, vb->baddr, vb->bsize);
+
+ switch (vb->state) {
+ case VIDEOBUF_ACTIVE:
+ dev_dbg(dev, "%s (active)\n", __func__);
+ break;
+ case VIDEOBUF_QUEUED:
+ dev_dbg(dev, "%s (queued)\n", __func__);
+ break;
+ case VIDEOBUF_PREPARED:
+ dev_dbg(dev, "%s (prepared)\n", __func__);
+ break;
+ default:
+ dev_dbg(dev, "%s (unknown)\n", __func__);
+ break;
+ }
+#endif
+
+ free_buffer(vq, buf);
+}
+
+static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev,
+ struct videobuf_buffer *vb,
+ struct pxa_buffer *buf)
+{
+ int i;
+
+ /* _init is used to debug races, see comment in pxa_camera_reqbufs() */
+ list_del_init(&vb->queue);
+ vb->state = VIDEOBUF_DONE;
+ do_gettimeofday(&vb->ts);
+ vb->field_count++;
+ wake_up(&vb->done);
+ dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s dequeud buffer (vb=0x%p)\n",
+ __func__, vb);
+
+ if (list_empty(&pcdev->capture)) {
+ pxa_camera_stop_capture(pcdev);
+ for (i = 0; i < pcdev->channels; i++)
+ pcdev->sg_tail[i] = NULL;
+ return;
+ }
+
+ pcdev->active = list_entry(pcdev->capture.next,
+ struct pxa_buffer, vb.queue);
+}
+
+/**
+ * pxa_camera_check_link_miss - check missed DMA linking
+ * @pcdev: camera device
+ *
+ * The DMA chaining is done with DMA running. This means a tiny temporal window
+ * remains, where a buffer is queued on the chain, while the chain is already
+ * stopped. This means the tailed buffer would never be transferred by DMA.
+ * This function restarts the capture for this corner case, where :
+ * - DADR() == DADDR_STOP
+ * - a videobuffer is queued on the pcdev->capture list
+ *
+ * Please check the "DMA hot chaining timeslice issue" in
+ * Documentation/video4linux/pxa_camera.txt
+ *
+ * Context: should only be called within the dma irq handler
+ */
+static void pxa_camera_check_link_miss(struct pxa_camera_dev *pcdev)
+{
+ int i, is_dma_stopped = 1;
+
+ for (i = 0; i < pcdev->channels; i++)
+ if (DDADR(pcdev->dma_chans[i]) != DDADR_STOP)
+ is_dma_stopped = 0;
+ dev_dbg(pcdev->soc_host.v4l2_dev.dev,
+ "%s : top queued buffer=%p, dma_stopped=%d\n",
+ __func__, pcdev->active, is_dma_stopped);
+ if (pcdev->active && is_dma_stopped)
+ pxa_camera_start_capture(pcdev);
+}
+
+static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev,
+ enum pxa_camera_active_dma act_dma)
+{
+ struct device *dev = pcdev->soc_host.v4l2_dev.dev;
+ struct pxa_buffer *buf;
+ unsigned long flags;
+ u32 status, camera_status, overrun;
+ struct videobuf_buffer *vb;
+
+ spin_lock_irqsave(&pcdev->lock, flags);
+
+ status = DCSR(channel);
+ DCSR(channel) = status;
+
+ camera_status = __raw_readl(pcdev->base + CISR);
+ overrun = CISR_IFO_0;
+ if (pcdev->channels == 3)
+ overrun |= CISR_IFO_1 | CISR_IFO_2;
+
+ if (status & DCSR_BUSERR) {
+ dev_err(dev, "DMA Bus Error IRQ!\n");
+ goto out;
+ }
+
+ if (!(status & (DCSR_ENDINTR | DCSR_STARTINTR))) {
+ dev_err(dev, "Unknown DMA IRQ source, status: 0x%08x\n",
+ status);
+ goto out;
+ }
+
+ /*
+ * pcdev->active should not be NULL in DMA irq handler.
+ *
+ * But there is one corner case : if capture was stopped due to an
+ * overrun of channel 1, and at that same channel 2 was completed.
+ *
+ * When handling the overrun in DMA irq for channel 1, we'll stop the
+ * capture and restart it (and thus set pcdev->active to NULL). But the
+ * DMA irq handler will already be pending for channel 2. So on entering
+ * the DMA irq handler for channel 2 there will be no active buffer, yet
+ * that is normal.
+ */
+ if (!pcdev->active)
+ goto out;
+
+ vb = &pcdev->active->vb;
+ buf = container_of(vb, struct pxa_buffer, vb);
+ WARN_ON(buf->inwork || list_empty(&vb->queue));
+
+ dev_dbg(dev, "%s channel=%d %s%s(vb=0x%p) dma.desc=%x\n",
+ __func__, channel, status & DCSR_STARTINTR ? "SOF " : "",
+ status & DCSR_ENDINTR ? "EOF " : "", vb, DDADR(channel));
+
+ if (status & DCSR_ENDINTR) {
+ /*
+ * It's normal if the last frame creates an overrun, as there
+ * are no more DMA descriptors to fetch from QCI fifos
+ */
+ if (camera_status & overrun &&
+ !list_is_last(pcdev->capture.next, &pcdev->capture)) {
+ dev_dbg(dev, "FIFO overrun! CISR: %x\n",
+ camera_status);
+ pxa_camera_stop_capture(pcdev);
+ pxa_camera_start_capture(pcdev);
+ goto out;
+ }
+ buf->active_dma &= ~act_dma;
+ if (!buf->active_dma) {
+ pxa_camera_wakeup(pcdev, vb, buf);
+ pxa_camera_check_link_miss(pcdev);
+ }
+ }
+
+out:
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+}
+
+static void pxa_camera_dma_irq_y(int channel, void *data)
+{
+ struct pxa_camera_dev *pcdev = data;
+ pxa_camera_dma_irq(channel, pcdev, DMA_Y);
+}
+
+static void pxa_camera_dma_irq_u(int channel, void *data)
+{
+ struct pxa_camera_dev *pcdev = data;
+ pxa_camera_dma_irq(channel, pcdev, DMA_U);
+}
+
+static void pxa_camera_dma_irq_v(int channel, void *data)
+{
+ struct pxa_camera_dev *pcdev = data;
+ pxa_camera_dma_irq(channel, pcdev, DMA_V);
+}
+
+static struct videobuf_queue_ops pxa_videobuf_ops = {
+ .buf_setup = pxa_videobuf_setup,
+ .buf_prepare = pxa_videobuf_prepare,
+ .buf_queue = pxa_videobuf_queue,
+ .buf_release = pxa_videobuf_release,
+};
+
+static void pxa_camera_init_videobuf(struct videobuf_queue *q,
+ struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct pxa_camera_dev *pcdev = ici->priv;
+
+ /*
+ * We must pass NULL as dev pointer, then all pci_* dma operations
+ * transform to normal dma_* ones.
+ */
+ videobuf_queue_sg_init(q, &pxa_videobuf_ops, NULL, &pcdev->lock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
+ sizeof(struct pxa_buffer), icd, &icd->video_lock);
+}
+
+static u32 mclk_get_divisor(struct platform_device *pdev,
+ struct pxa_camera_dev *pcdev)
+{
+ unsigned long mclk = pcdev->mclk;
+ struct device *dev = &pdev->dev;
+ u32 div;
+ unsigned long lcdclk;
+
+ lcdclk = clk_get_rate(pcdev->clk);
+ pcdev->ciclk = lcdclk;
+
+ /* mclk <= ciclk / 4 (27.4.2) */
+ if (mclk > lcdclk / 4) {
+ mclk = lcdclk / 4;
+ dev_warn(dev, "Limiting master clock to %lu\n", mclk);
+ }
+
+ /* We verify mclk != 0, so if anyone breaks it, here comes their Oops */
+ div = (lcdclk + 2 * mclk - 1) / (2 * mclk) - 1;
+
+ /* If we're not supplying MCLK, leave it at 0 */
+ if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN)
+ pcdev->mclk = lcdclk / (2 * (div + 1));
+
+ dev_dbg(dev, "LCD clock %luHz, target freq %luHz, divisor %u\n",
+ lcdclk, mclk, div);
+
+ return div;
+}
+
+static void recalculate_fifo_timeout(struct pxa_camera_dev *pcdev,
+ unsigned long pclk)
+{
+ /* We want a timeout > 1 pixel time, not ">=" */
+ u32 ciclk_per_pixel = pcdev->ciclk / pclk + 1;
+
+ __raw_writel(ciclk_per_pixel, pcdev->base + CITOR);
+}
+
+static void pxa_camera_activate(struct pxa_camera_dev *pcdev)
+{
+ u32 cicr4 = 0;
+
+ /* disable all interrupts */
+ __raw_writel(0x3ff, pcdev->base + CICR0);
+
+ if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN)
+ cicr4 |= CICR4_PCLK_EN;
+ if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN)
+ cicr4 |= CICR4_MCLK_EN;
+ if (pcdev->platform_flags & PXA_CAMERA_PCP)
+ cicr4 |= CICR4_PCP;
+ if (pcdev->platform_flags & PXA_CAMERA_HSP)
+ cicr4 |= CICR4_HSP;
+ if (pcdev->platform_flags & PXA_CAMERA_VSP)
+ cicr4 |= CICR4_VSP;
+
+ __raw_writel(pcdev->mclk_divisor | cicr4, pcdev->base + CICR4);
+
+ if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN)
+ /* Initialise the timeout under the assumption pclk = mclk */
+ recalculate_fifo_timeout(pcdev, pcdev->mclk);
+ else
+ /* "Safe default" - 13MHz */
+ recalculate_fifo_timeout(pcdev, 13000000);
+
+ clk_prepare_enable(pcdev->clk);
+}
+
+static void pxa_camera_deactivate(struct pxa_camera_dev *pcdev)
+{
+ clk_disable_unprepare(pcdev->clk);
+}
+
+static irqreturn_t pxa_camera_irq(int irq, void *data)
+{
+ struct pxa_camera_dev *pcdev = data;
+ unsigned long status, cifr, cicr0;
+ struct pxa_buffer *buf;
+ struct videobuf_buffer *vb;
+
+ status = __raw_readl(pcdev->base + CISR);
+ dev_dbg(pcdev->soc_host.v4l2_dev.dev,
+ "Camera interrupt status 0x%lx\n", status);
+
+ if (!status)
+ return IRQ_NONE;
+
+ __raw_writel(status, pcdev->base + CISR);
+
+ if (status & CISR_EOF) {
+ /* Reset the FIFOs */
+ cifr = __raw_readl(pcdev->base + CIFR) | CIFR_RESET_F;
+ __raw_writel(cifr, pcdev->base + CIFR);
+
+ pcdev->active = list_first_entry(&pcdev->capture,
+ struct pxa_buffer, vb.queue);
+ vb = &pcdev->active->vb;
+ buf = container_of(vb, struct pxa_buffer, vb);
+ pxa_videobuf_set_actdma(pcdev, buf);
+
+ pxa_dma_start_channels(pcdev);
+
+ cicr0 = __raw_readl(pcdev->base + CICR0) | CICR0_EOFM;
+ __raw_writel(cicr0, pcdev->base + CICR0);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * The following two functions absolutely depend on the fact, that
+ * there can be only one camera on PXA quick capture interface
+ * Called with .video_lock held
+ */
+static int pxa_camera_add_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct pxa_camera_dev *pcdev = ici->priv;
+
+ if (pcdev->icd)
+ return -EBUSY;
+
+ pxa_camera_activate(pcdev);
+
+ pcdev->icd = icd;
+
+ dev_info(icd->parent, "PXA Camera driver attached to camera %d\n",
+ icd->devnum);
+
+ return 0;
+}
+
+/* Called with .video_lock held */
+static void pxa_camera_remove_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct pxa_camera_dev *pcdev = ici->priv;
+
+ BUG_ON(icd != pcdev->icd);
+
+ dev_info(icd->parent, "PXA Camera driver detached from camera %d\n",
+ icd->devnum);
+
+ /* disable capture, disable interrupts */
+ __raw_writel(0x3ff, pcdev->base + CICR0);
+
+ /* Stop DMA engine */
+ DCSR(pcdev->dma_chans[0]) = 0;
+ DCSR(pcdev->dma_chans[1]) = 0;
+ DCSR(pcdev->dma_chans[2]) = 0;
+
+ pxa_camera_deactivate(pcdev);
+
+ pcdev->icd = NULL;
+}
+
+static int test_platform_param(struct pxa_camera_dev *pcdev,
+ unsigned char buswidth, unsigned long *flags)
+{
+ /*
+ * Platform specified synchronization and pixel clock polarities are
+ * only a recommendation and are only used during probing. The PXA270
+ * quick capture interface supports both.
+ */
+ *flags = (pcdev->platform_flags & PXA_CAMERA_MASTER ?
+ V4L2_MBUS_MASTER : V4L2_MBUS_SLAVE) |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_HSYNC_ACTIVE_LOW |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_VSYNC_ACTIVE_LOW |
+ V4L2_MBUS_DATA_ACTIVE_HIGH |
+ V4L2_MBUS_PCLK_SAMPLE_RISING |
+ V4L2_MBUS_PCLK_SAMPLE_FALLING;
+
+ /* If requested data width is supported by the platform, use it */
+ if ((1 << (buswidth - 1)) & pcdev->width_flags)
+ return 0;
+
+ return -EINVAL;
+}
+
+static void pxa_camera_setup_cicr(struct soc_camera_device *icd,
+ unsigned long flags, __u32 pixfmt)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct pxa_camera_dev *pcdev = ici->priv;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ unsigned long dw, bpp;
+ u32 cicr0, cicr1, cicr2, cicr3, cicr4 = 0, y_skip_top;
+ int ret = v4l2_subdev_call(sd, sensor, g_skip_top_lines, &y_skip_top);
+
+ if (ret < 0)
+ y_skip_top = 0;
+
+ /*
+ * Datawidth is now guaranteed to be equal to one of the three values.
+ * We fix bit-per-pixel equal to data-width...
+ */
+ switch (icd->current_fmt->host_fmt->bits_per_sample) {
+ case 10:
+ dw = 4;
+ bpp = 0x40;
+ break;
+ case 9:
+ dw = 3;
+ bpp = 0x20;
+ break;
+ default:
+ /*
+ * Actually it can only be 8 now,
+ * default is just to silence compiler warnings
+ */
+ case 8:
+ dw = 2;
+ bpp = 0;
+ }
+
+ if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN)
+ cicr4 |= CICR4_PCLK_EN;
+ if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN)
+ cicr4 |= CICR4_MCLK_EN;
+ if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
+ cicr4 |= CICR4_PCP;
+ if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ cicr4 |= CICR4_HSP;
+ if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ cicr4 |= CICR4_VSP;
+
+ cicr0 = __raw_readl(pcdev->base + CICR0);
+ if (cicr0 & CICR0_ENB)
+ __raw_writel(cicr0 & ~CICR0_ENB, pcdev->base + CICR0);
+
+ cicr1 = CICR1_PPL_VAL(icd->user_width - 1) | bpp | dw;
+
+ switch (pixfmt) {
+ case V4L2_PIX_FMT_YUV422P:
+ pcdev->channels = 3;
+ cicr1 |= CICR1_YCBCR_F;
+ /*
+ * Normally, pxa bus wants as input UYVY format. We allow all
+ * reorderings of the YUV422 format, as no processing is done,
+ * and the YUV stream is just passed through without any
+ * transformation. Note that UYVY is the only format that
+ * should be used if pxa framebuffer Overlay2 is used.
+ */
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ cicr1 |= CICR1_COLOR_SP_VAL(2);
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ cicr1 |= CICR1_RGB_BPP_VAL(1) | CICR1_RGBT_CONV_VAL(2) |
+ CICR1_TBIT | CICR1_COLOR_SP_VAL(1);
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ cicr1 |= CICR1_COLOR_SP_VAL(1) | CICR1_RGB_BPP_VAL(2);
+ break;
+ }
+
+ cicr2 = 0;
+ cicr3 = CICR3_LPF_VAL(icd->user_height - 1) |
+ CICR3_BFW_VAL(min((u32)255, y_skip_top));
+ cicr4 |= pcdev->mclk_divisor;
+
+ __raw_writel(cicr1, pcdev->base + CICR1);
+ __raw_writel(cicr2, pcdev->base + CICR2);
+ __raw_writel(cicr3, pcdev->base + CICR3);
+ __raw_writel(cicr4, pcdev->base + CICR4);
+
+ /* CIF interrupts are not used, only DMA */
+ cicr0 = (cicr0 & CICR0_ENB) | (pcdev->platform_flags & PXA_CAMERA_MASTER ?
+ CICR0_SIM_MP : (CICR0_SL_CAP_EN | CICR0_SIM_SP));
+ cicr0 |= CICR0_DMAEN | CICR0_IRQ_MASK;
+ __raw_writel(cicr0, pcdev->base + CICR0);
+}
+
+static int pxa_camera_set_bus_param(struct soc_camera_device *icd)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct pxa_camera_dev *pcdev = ici->priv;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ u32 pixfmt = icd->current_fmt->host_fmt->fourcc;
+ unsigned long bus_flags, common_flags;
+ int ret;
+ struct pxa_cam *cam = icd->host_priv;
+
+ ret = test_platform_param(pcdev, icd->current_fmt->host_fmt->bits_per_sample,
+ &bus_flags);
+ if (ret < 0)
+ return ret;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg,
+ bus_flags);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%lx\n",
+ cfg.flags, bus_flags);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = bus_flags;
+ }
+
+ pcdev->channels = 1;
+
+ /* Make choises, based on platform preferences */
+ if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
+ if (pcdev->platform_flags & PXA_CAMERA_HSP)
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
+ else
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
+ }
+
+ if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
+ if (pcdev->platform_flags & PXA_CAMERA_VSP)
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
+ else
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
+ }
+
+ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+ (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
+ if (pcdev->platform_flags & PXA_CAMERA_PCP)
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
+ else
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
+ }
+
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n",
+ common_flags, ret);
+ return ret;
+ }
+
+ cam->flags = common_flags;
+
+ pxa_camera_setup_cicr(icd, common_flags, pixfmt);
+
+ return 0;
+}
+
+static int pxa_camera_try_bus_param(struct soc_camera_device *icd,
+ unsigned char buswidth)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct pxa_camera_dev *pcdev = ici->priv;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long bus_flags, common_flags;
+ int ret = test_platform_param(pcdev, buswidth, &bus_flags);
+
+ if (ret < 0)
+ return ret;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg,
+ bus_flags);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%lx\n",
+ cfg.flags, bus_flags);
+ return -EINVAL;
+ }
+ } else if (ret == -ENOIOCTLCMD) {
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static const struct soc_mbus_pixelfmt pxa_camera_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .name = "Planar YUV422 16 bit",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+};
+
+/* This will be corrected as we get more formats */
+static bool pxa_camera_packing_supported(const struct soc_mbus_pixelfmt *fmt)
+{
+ return fmt->packing == SOC_MBUS_PACKING_NONE ||
+ (fmt->bits_per_sample == 8 &&
+ fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) ||
+ (fmt->bits_per_sample > 8 &&
+ fmt->packing == SOC_MBUS_PACKING_EXTEND16);
+}
+
+static int pxa_camera_get_formats(struct soc_camera_device *icd, unsigned int idx,
+ struct soc_camera_format_xlate *xlate)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ int formats = 0, ret;
+ struct pxa_cam *cam;
+ enum v4l2_mbus_pixelcode code;
+ const struct soc_mbus_pixelfmt *fmt;
+
+ ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
+ if (ret < 0)
+ /* No more formats */
+ return 0;
+
+ fmt = soc_mbus_get_fmtdesc(code);
+ if (!fmt) {
+ dev_err(dev, "Invalid format code #%u: %d\n", idx, code);
+ return 0;
+ }
+
+ /* This also checks support for the requested bits-per-sample */
+ ret = pxa_camera_try_bus_param(icd, fmt->bits_per_sample);
+ if (ret < 0)
+ return 0;
+
+ if (!icd->host_priv) {
+ cam = kzalloc(sizeof(*cam), GFP_KERNEL);
+ if (!cam)
+ return -ENOMEM;
+
+ icd->host_priv = cam;
+ } else {
+ cam = icd->host_priv;
+ }
+
+ switch (code) {
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = &pxa_camera_formats[0];
+ xlate->code = code;
+ xlate++;
+ dev_dbg(dev, "Providing format %s using code %d\n",
+ pxa_camera_formats[0].name, code);
+ }
+ case V4L2_MBUS_FMT_VYUY8_2X8:
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ case V4L2_MBUS_FMT_YVYU8_2X8:
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
+ if (xlate)
+ dev_dbg(dev, "Providing format %s packed\n",
+ fmt->name);
+ break;
+ default:
+ if (!pxa_camera_packing_supported(fmt))
+ return 0;
+ if (xlate)
+ dev_dbg(dev,
+ "Providing format %s in pass-through mode\n",
+ fmt->name);
+ }
+
+ /* Generic pass-through */
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = fmt;
+ xlate->code = code;
+ xlate++;
+ }
+
+ return formats;
+}
+
+static void pxa_camera_put_formats(struct soc_camera_device *icd)
+{
+ kfree(icd->host_priv);
+ icd->host_priv = NULL;
+}
+
+static int pxa_camera_check_frame(u32 width, u32 height)
+{
+ /* limit to pxa hardware capabilities */
+ return height < 32 || height > 2048 || width < 48 || width > 2048 ||
+ (width & 0x01);
+}
+
+static int pxa_camera_set_crop(struct soc_camera_device *icd,
+ struct v4l2_crop *a)
+{
+ struct v4l2_rect *rect = &a->c;
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
+ struct pxa_camera_dev *pcdev = ici->priv;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_sense sense = {
+ .master_clock = pcdev->mclk,
+ .pixel_clock_max = pcdev->ciclk / 4,
+ };
+ struct v4l2_mbus_framefmt mf;
+ struct pxa_cam *cam = icd->host_priv;
+ u32 fourcc = icd->current_fmt->host_fmt->fourcc;
+ int ret;
+
+ /* If PCLK is used to latch data from the sensor, check sense */
+ if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN)
+ icd->sense = &sense;
+
+ ret = v4l2_subdev_call(sd, video, s_crop, a);
+
+ icd->sense = NULL;
+
+ if (ret < 0) {
+ dev_warn(dev, "Failed to crop to %ux%u@%u:%u\n",
+ rect->width, rect->height, rect->left, rect->top);
+ return ret;
+ }
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ if (pxa_camera_check_frame(mf.width, mf.height)) {
+ /*
+ * Camera cropping produced a frame beyond our capabilities.
+ * FIXME: just extract a subframe, that we can process.
+ */
+ v4l_bound_align_image(&mf.width, 48, 2048, 1,
+ &mf.height, 32, 2048, 0,
+ fourcc == V4L2_PIX_FMT_YUV422P ? 4 : 0);
+ ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ if (pxa_camera_check_frame(mf.width, mf.height)) {
+ dev_warn(icd->parent,
+ "Inconsistent state. Use S_FMT to repair\n");
+ return -EINVAL;
+ }
+ }
+
+ if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) {
+ if (sense.pixel_clock > sense.pixel_clock_max) {
+ dev_err(dev,
+ "pixel clock %lu set by the camera too high!",
+ sense.pixel_clock);
+ return -EIO;
+ }
+ recalculate_fifo_timeout(pcdev, sense.pixel_clock);
+ }
+
+ icd->user_width = mf.width;
+ icd->user_height = mf.height;
+
+ pxa_camera_setup_cicr(icd, cam->flags, fourcc);
+
+ return ret;
+}
+
+static int pxa_camera_set_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
+ struct pxa_camera_dev *pcdev = ici->priv;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate = NULL;
+ struct soc_camera_sense sense = {
+ .master_clock = pcdev->mclk,
+ .pixel_clock_max = pcdev->ciclk / 4,
+ };
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_mbus_framefmt mf;
+ int ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+ if (!xlate) {
+ dev_warn(dev, "Format %x not found\n", pix->pixelformat);
+ return -EINVAL;
+ }
+
+ /* If PCLK is used to latch data from the sensor, check sense */
+ if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN)
+ /* The caller holds a mutex. */
+ icd->sense = &sense;
+
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
+
+ ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
+
+ if (mf.code != xlate->code)
+ return -EINVAL;
+
+ icd->sense = NULL;
+
+ if (ret < 0) {
+ dev_warn(dev, "Failed to configure for format %x\n",
+ pix->pixelformat);
+ } else if (pxa_camera_check_frame(mf.width, mf.height)) {
+ dev_warn(dev,
+ "Camera driver produced an unsupported frame %dx%d\n",
+ mf.width, mf.height);
+ ret = -EINVAL;
+ } else if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) {
+ if (sense.pixel_clock > sense.pixel_clock_max) {
+ dev_err(dev,
+ "pixel clock %lu set by the camera too high!",
+ sense.pixel_clock);
+ return -EIO;
+ }
+ recalculate_fifo_timeout(pcdev, sense.pixel_clock);
+ }
+
+ if (ret < 0)
+ return ret;
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->field = mf.field;
+ pix->colorspace = mf.colorspace;
+ icd->current_fmt = xlate;
+
+ return ret;
+}
+
+static int pxa_camera_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_mbus_framefmt mf;
+ __u32 pixfmt = pix->pixelformat;
+ int ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (!xlate) {
+ dev_warn(icd->parent, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+
+ /*
+ * Limit to pxa hardware capabilities. YUV422P planar format requires
+ * images size to be a multiple of 16 bytes. If not, zeros will be
+ * inserted between Y and U planes, and U and V planes, which violates
+ * the YUV422P standard.
+ */
+ v4l_bound_align_image(&pix->width, 48, 2048, 1,
+ &pix->height, 32, 2048, 0,
+ pixfmt == V4L2_PIX_FMT_YUV422P ? 4 : 0);
+
+ /* limit to sensor capabilities */
+ mf.width = pix->width;
+ mf.height = pix->height;
+ /* Only progressive video supported so far */
+ mf.field = V4L2_FIELD_NONE;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
+
+ ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->colorspace = mf.colorspace;
+
+ switch (mf.field) {
+ case V4L2_FIELD_ANY:
+ case V4L2_FIELD_NONE:
+ pix->field = V4L2_FIELD_NONE;
+ break;
+ default:
+ /* TODO: support interlaced at least in pass-through mode */
+ dev_err(icd->parent, "Field type %d unsupported.\n",
+ mf.field);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int pxa_camera_reqbufs(struct soc_camera_device *icd,
+ struct v4l2_requestbuffers *p)
+{
+ int i;
+
+ /*
+ * This is for locking debugging only. I removed spinlocks and now I
+ * check whether .prepare is ever called on a linked buffer, or whether
+ * a dma IRQ can occur for an in-work or unlinked buffer. Until now
+ * it hadn't triggered
+ */
+ for (i = 0; i < p->count; i++) {
+ struct pxa_buffer *buf = container_of(icd->vb_vidq.bufs[i],
+ struct pxa_buffer, vb);
+ buf->inwork = 0;
+ INIT_LIST_HEAD(&buf->vb.queue);
+ }
+
+ return 0;
+}
+
+static unsigned int pxa_camera_poll(struct file *file, poll_table *pt)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct pxa_buffer *buf;
+
+ buf = list_entry(icd->vb_vidq.stream.next, struct pxa_buffer,
+ vb.stream);
+
+ poll_wait(file, &buf->vb.done, pt);
+
+ if (buf->vb.state == VIDEOBUF_DONE ||
+ buf->vb.state == VIDEOBUF_ERROR)
+ return POLLIN|POLLRDNORM;
+
+ return 0;
+}
+
+static int pxa_camera_querycap(struct soc_camera_host *ici,
+ struct v4l2_capability *cap)
+{
+ /* cap->name is set by the firendly caller:-> */
+ strlcpy(cap->card, pxa_cam_driver_description, sizeof(cap->card));
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+static int pxa_camera_suspend(struct device *dev)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
+ struct pxa_camera_dev *pcdev = ici->priv;
+ int i = 0, ret = 0;
+
+ pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR0);
+ pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR1);
+ pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR2);
+ pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR3);
+ pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR4);
+
+ if (pcdev->icd) {
+ struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->icd);
+ ret = v4l2_subdev_call(sd, core, s_power, 0);
+ if (ret == -ENOIOCTLCMD)
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int pxa_camera_resume(struct device *dev)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
+ struct pxa_camera_dev *pcdev = ici->priv;
+ int i = 0, ret = 0;
+
+ DRCMR(68) = pcdev->dma_chans[0] | DRCMR_MAPVLD;
+ DRCMR(69) = pcdev->dma_chans[1] | DRCMR_MAPVLD;
+ DRCMR(70) = pcdev->dma_chans[2] | DRCMR_MAPVLD;
+
+ __raw_writel(pcdev->save_cicr[i++] & ~CICR0_ENB, pcdev->base + CICR0);
+ __raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR1);
+ __raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR2);
+ __raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR3);
+ __raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR4);
+
+ if (pcdev->icd) {
+ struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->icd);
+ ret = v4l2_subdev_call(sd, core, s_power, 1);
+ if (ret == -ENOIOCTLCMD)
+ ret = 0;
+ }
+
+ /* Restart frame capture if active buffer exists */
+ if (!ret && pcdev->active)
+ pxa_camera_start_capture(pcdev);
+
+ return ret;
+}
+
+static struct soc_camera_host_ops pxa_soc_camera_host_ops = {
+ .owner = THIS_MODULE,
+ .add = pxa_camera_add_device,
+ .remove = pxa_camera_remove_device,
+ .set_crop = pxa_camera_set_crop,
+ .get_formats = pxa_camera_get_formats,
+ .put_formats = pxa_camera_put_formats,
+ .set_fmt = pxa_camera_set_fmt,
+ .try_fmt = pxa_camera_try_fmt,
+ .init_videobuf = pxa_camera_init_videobuf,
+ .reqbufs = pxa_camera_reqbufs,
+ .poll = pxa_camera_poll,
+ .querycap = pxa_camera_querycap,
+ .set_bus_param = pxa_camera_set_bus_param,
+};
+
+static int __devinit pxa_camera_probe(struct platform_device *pdev)
+{
+ struct pxa_camera_dev *pcdev;
+ struct resource *res;
+ void __iomem *base;
+ int irq;
+ int err = 0;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (!res || irq < 0) {
+ err = -ENODEV;
+ goto exit;
+ }
+
+ pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL);
+ if (!pcdev) {
+ dev_err(&pdev->dev, "Could not allocate pcdev\n");
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ pcdev->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(pcdev->clk)) {
+ err = PTR_ERR(pcdev->clk);
+ goto exit_kfree;
+ }
+
+ pcdev->res = res;
+
+ pcdev->pdata = pdev->dev.platform_data;
+ pcdev->platform_flags = pcdev->pdata->flags;
+ if (!(pcdev->platform_flags & (PXA_CAMERA_DATAWIDTH_8 |
+ PXA_CAMERA_DATAWIDTH_9 | PXA_CAMERA_DATAWIDTH_10))) {
+ /*
+ * Platform hasn't set available data widths. This is bad.
+ * Warn and use a default.
+ */
+ dev_warn(&pdev->dev, "WARNING! Platform hasn't set available "
+ "data widths, using default 10 bit\n");
+ pcdev->platform_flags |= PXA_CAMERA_DATAWIDTH_10;
+ }
+ if (pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_8)
+ pcdev->width_flags = 1 << 7;
+ if (pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_9)
+ pcdev->width_flags |= 1 << 8;
+ if (pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_10)
+ pcdev->width_flags |= 1 << 9;
+ pcdev->mclk = pcdev->pdata->mclk_10khz * 10000;
+ if (!pcdev->mclk) {
+ dev_warn(&pdev->dev,
+ "mclk == 0! Please, fix your platform data. "
+ "Using default 20MHz\n");
+ pcdev->mclk = 20000000;
+ }
+
+ pcdev->mclk_divisor = mclk_get_divisor(pdev, pcdev);
+
+ INIT_LIST_HEAD(&pcdev->capture);
+ spin_lock_init(&pcdev->lock);
+
+ /*
+ * Request the regions.
+ */
+ if (!request_mem_region(res->start, resource_size(res),
+ PXA_CAM_DRV_NAME)) {
+ err = -EBUSY;
+ goto exit_clk;
+ }
+
+ base = ioremap(res->start, resource_size(res));
+ if (!base) {
+ err = -ENOMEM;
+ goto exit_release;
+ }
+ pcdev->irq = irq;
+ pcdev->base = base;
+
+ /* request dma */
+ err = pxa_request_dma("CI_Y", DMA_PRIO_HIGH,
+ pxa_camera_dma_irq_y, pcdev);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Can't request DMA for Y\n");
+ goto exit_iounmap;
+ }
+ pcdev->dma_chans[0] = err;
+ dev_dbg(&pdev->dev, "got DMA channel %d\n", pcdev->dma_chans[0]);
+
+ err = pxa_request_dma("CI_U", DMA_PRIO_HIGH,
+ pxa_camera_dma_irq_u, pcdev);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Can't request DMA for U\n");
+ goto exit_free_dma_y;
+ }
+ pcdev->dma_chans[1] = err;
+ dev_dbg(&pdev->dev, "got DMA channel (U) %d\n", pcdev->dma_chans[1]);
+
+ err = pxa_request_dma("CI_V", DMA_PRIO_HIGH,
+ pxa_camera_dma_irq_v, pcdev);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Can't request DMA for V\n");
+ goto exit_free_dma_u;
+ }
+ pcdev->dma_chans[2] = err;
+ dev_dbg(&pdev->dev, "got DMA channel (V) %d\n", pcdev->dma_chans[2]);
+
+ DRCMR(68) = pcdev->dma_chans[0] | DRCMR_MAPVLD;
+ DRCMR(69) = pcdev->dma_chans[1] | DRCMR_MAPVLD;
+ DRCMR(70) = pcdev->dma_chans[2] | DRCMR_MAPVLD;
+
+ /* request irq */
+ err = request_irq(pcdev->irq, pxa_camera_irq, 0, PXA_CAM_DRV_NAME,
+ pcdev);
+ if (err) {
+ dev_err(&pdev->dev, "Camera interrupt register failed \n");
+ goto exit_free_dma;
+ }
+
+ pcdev->soc_host.drv_name = PXA_CAM_DRV_NAME;
+ pcdev->soc_host.ops = &pxa_soc_camera_host_ops;
+ pcdev->soc_host.priv = pcdev;
+ pcdev->soc_host.v4l2_dev.dev = &pdev->dev;
+ pcdev->soc_host.nr = pdev->id;
+
+ err = soc_camera_host_register(&pcdev->soc_host);
+ if (err)
+ goto exit_free_irq;
+
+ return 0;
+
+exit_free_irq:
+ free_irq(pcdev->irq, pcdev);
+exit_free_dma:
+ pxa_free_dma(pcdev->dma_chans[2]);
+exit_free_dma_u:
+ pxa_free_dma(pcdev->dma_chans[1]);
+exit_free_dma_y:
+ pxa_free_dma(pcdev->dma_chans[0]);
+exit_iounmap:
+ iounmap(base);
+exit_release:
+ release_mem_region(res->start, resource_size(res));
+exit_clk:
+ clk_put(pcdev->clk);
+exit_kfree:
+ kfree(pcdev);
+exit:
+ return err;
+}
+
+static int __devexit pxa_camera_remove(struct platform_device *pdev)
+{
+ struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+ struct pxa_camera_dev *pcdev = container_of(soc_host,
+ struct pxa_camera_dev, soc_host);
+ struct resource *res;
+
+ clk_put(pcdev->clk);
+
+ pxa_free_dma(pcdev->dma_chans[0]);
+ pxa_free_dma(pcdev->dma_chans[1]);
+ pxa_free_dma(pcdev->dma_chans[2]);
+ free_irq(pcdev->irq, pcdev);
+
+ soc_camera_host_unregister(soc_host);
+
+ iounmap(pcdev->base);
+
+ res = pcdev->res;
+ release_mem_region(res->start, resource_size(res));
+
+ kfree(pcdev);
+
+ dev_info(&pdev->dev, "PXA Camera driver unloaded\n");
+
+ return 0;
+}
+
+static struct dev_pm_ops pxa_camera_pm = {
+ .suspend = pxa_camera_suspend,
+ .resume = pxa_camera_resume,
+};
+
+static struct platform_driver pxa_camera_driver = {
+ .driver = {
+ .name = PXA_CAM_DRV_NAME,
+ .pm = &pxa_camera_pm,
+ },
+ .probe = pxa_camera_probe,
+ .remove = __devexit_p(pxa_camera_remove),
+};
+
+module_platform_driver(pxa_camera_driver);
+
+MODULE_DESCRIPTION("PXA27x SoC Camera Host driver");
+MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(PXA_CAM_VERSION);
+MODULE_ALIAS("platform:" PXA_CAM_DRV_NAME);
diff --git a/drivers/media/video/rj54n1cb0c.c b/drivers/media/video/rj54n1cb0c.c
new file mode 100644
index 00000000..f6419b22
--- /dev/null
+++ b/drivers/media/video/rj54n1cb0c.c
@@ -0,0 +1,1414 @@
+/*
+ * Driver for RJ54N1CB0C CMOS Image Sensor from Sharp
+ *
+ * Copyright (C) 2009, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/videodev2.h>
+#include <linux/module.h>
+
+#include <media/rj54n1cb0c.h>
+#include <media/soc_camera.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+
+#define RJ54N1_DEV_CODE 0x0400
+#define RJ54N1_DEV_CODE2 0x0401
+#define RJ54N1_OUT_SEL 0x0403
+#define RJ54N1_XY_OUTPUT_SIZE_S_H 0x0404
+#define RJ54N1_X_OUTPUT_SIZE_S_L 0x0405
+#define RJ54N1_Y_OUTPUT_SIZE_S_L 0x0406
+#define RJ54N1_XY_OUTPUT_SIZE_P_H 0x0407
+#define RJ54N1_X_OUTPUT_SIZE_P_L 0x0408
+#define RJ54N1_Y_OUTPUT_SIZE_P_L 0x0409
+#define RJ54N1_LINE_LENGTH_PCK_S_H 0x040a
+#define RJ54N1_LINE_LENGTH_PCK_S_L 0x040b
+#define RJ54N1_LINE_LENGTH_PCK_P_H 0x040c
+#define RJ54N1_LINE_LENGTH_PCK_P_L 0x040d
+#define RJ54N1_RESIZE_N 0x040e
+#define RJ54N1_RESIZE_N_STEP 0x040f
+#define RJ54N1_RESIZE_STEP 0x0410
+#define RJ54N1_RESIZE_HOLD_H 0x0411
+#define RJ54N1_RESIZE_HOLD_L 0x0412
+#define RJ54N1_H_OBEN_OFS 0x0413
+#define RJ54N1_V_OBEN_OFS 0x0414
+#define RJ54N1_RESIZE_CONTROL 0x0415
+#define RJ54N1_STILL_CONTROL 0x0417
+#define RJ54N1_INC_USE_SEL_H 0x0425
+#define RJ54N1_INC_USE_SEL_L 0x0426
+#define RJ54N1_MIRROR_STILL_MODE 0x0427
+#define RJ54N1_INIT_START 0x0428
+#define RJ54N1_SCALE_1_2_LEV 0x0429
+#define RJ54N1_SCALE_4_LEV 0x042a
+#define RJ54N1_Y_GAIN 0x04d8
+#define RJ54N1_APT_GAIN_UP 0x04fa
+#define RJ54N1_RA_SEL_UL 0x0530
+#define RJ54N1_BYTE_SWAP 0x0531
+#define RJ54N1_OUT_SIGPO 0x053b
+#define RJ54N1_WB_SEL_WEIGHT_I 0x054e
+#define RJ54N1_BIT8_WB 0x0569
+#define RJ54N1_HCAPS_WB 0x056a
+#define RJ54N1_VCAPS_WB 0x056b
+#define RJ54N1_HCAPE_WB 0x056c
+#define RJ54N1_VCAPE_WB 0x056d
+#define RJ54N1_EXPOSURE_CONTROL 0x058c
+#define RJ54N1_FRAME_LENGTH_S_H 0x0595
+#define RJ54N1_FRAME_LENGTH_S_L 0x0596
+#define RJ54N1_FRAME_LENGTH_P_H 0x0597
+#define RJ54N1_FRAME_LENGTH_P_L 0x0598
+#define RJ54N1_PEAK_H 0x05b7
+#define RJ54N1_PEAK_50 0x05b8
+#define RJ54N1_PEAK_60 0x05b9
+#define RJ54N1_PEAK_DIFF 0x05ba
+#define RJ54N1_IOC 0x05ef
+#define RJ54N1_TG_BYPASS 0x0700
+#define RJ54N1_PLL_L 0x0701
+#define RJ54N1_PLL_N 0x0702
+#define RJ54N1_PLL_EN 0x0704
+#define RJ54N1_RATIO_TG 0x0706
+#define RJ54N1_RATIO_T 0x0707
+#define RJ54N1_RATIO_R 0x0708
+#define RJ54N1_RAMP_TGCLK_EN 0x0709
+#define RJ54N1_OCLK_DSP 0x0710
+#define RJ54N1_RATIO_OP 0x0711
+#define RJ54N1_RATIO_O 0x0712
+#define RJ54N1_OCLK_SEL_EN 0x0713
+#define RJ54N1_CLK_RST 0x0717
+#define RJ54N1_RESET_STANDBY 0x0718
+#define RJ54N1_FWFLG 0x07fe
+
+#define E_EXCLK (1 << 7)
+#define SOFT_STDBY (1 << 4)
+#define SEN_RSTX (1 << 2)
+#define TG_RSTX (1 << 1)
+#define DSP_RSTX (1 << 0)
+
+#define RESIZE_HOLD_SEL (1 << 2)
+#define RESIZE_GO (1 << 1)
+
+/*
+ * When cropping, the camera automatically centers the cropped region, there
+ * doesn't seem to be a way to specify an explicit location of the rectangle.
+ */
+#define RJ54N1_COLUMN_SKIP 0
+#define RJ54N1_ROW_SKIP 0
+#define RJ54N1_MAX_WIDTH 1600
+#define RJ54N1_MAX_HEIGHT 1200
+
+#define PLL_L 2
+#define PLL_N 0x31
+
+/* I2C addresses: 0x50, 0x51, 0x60, 0x61 */
+
+/* RJ54N1CB0C has only one fixed colorspace per pixelcode */
+struct rj54n1_datafmt {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+};
+
+/* Find a data format by a pixel code in an array */
+static const struct rj54n1_datafmt *rj54n1_find_datafmt(
+ enum v4l2_mbus_pixelcode code, const struct rj54n1_datafmt *fmt,
+ int n)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ if (fmt[i].code == code)
+ return fmt + i;
+
+ return NULL;
+}
+
+static const struct rj54n1_datafmt rj54n1_colour_fmts[] = {
+ {V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG},
+ {V4L2_MBUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG},
+ {V4L2_MBUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB},
+};
+
+struct rj54n1_clock_div {
+ u8 ratio_tg; /* can be 0 or an odd number */
+ u8 ratio_t;
+ u8 ratio_r;
+ u8 ratio_op;
+ u8 ratio_o;
+};
+
+struct rj54n1 {
+ struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
+ struct rj54n1_clock_div clk_div;
+ const struct rj54n1_datafmt *fmt;
+ struct v4l2_rect rect; /* Sensor window */
+ unsigned int tgclk_mhz;
+ bool auto_wb;
+ unsigned short width; /* Output window */
+ unsigned short height;
+ unsigned short resize; /* Sensor * 1024 / resize = Output */
+ unsigned short scale;
+ u8 bank;
+};
+
+struct rj54n1_reg_val {
+ u16 reg;
+ u8 val;
+};
+
+static const struct rj54n1_reg_val bank_4[] = {
+ {0x417, 0},
+ {0x42c, 0},
+ {0x42d, 0xf0},
+ {0x42e, 0},
+ {0x42f, 0x50},
+ {0x430, 0xf5},
+ {0x431, 0x16},
+ {0x432, 0x20},
+ {0x433, 0},
+ {0x434, 0xc8},
+ {0x43c, 8},
+ {0x43e, 0x90},
+ {0x445, 0x83},
+ {0x4ba, 0x58},
+ {0x4bb, 4},
+ {0x4bc, 0x20},
+ {0x4db, 4},
+ {0x4fe, 2},
+};
+
+static const struct rj54n1_reg_val bank_5[] = {
+ {0x514, 0},
+ {0x516, 0},
+ {0x518, 0},
+ {0x51a, 0},
+ {0x51d, 0xff},
+ {0x56f, 0x28},
+ {0x575, 0x40},
+ {0x5bc, 0x48},
+ {0x5c1, 6},
+ {0x5e5, 0x11},
+ {0x5e6, 0x43},
+ {0x5e7, 0x33},
+ {0x5e8, 0x21},
+ {0x5e9, 0x30},
+ {0x5ea, 0x0},
+ {0x5eb, 0xa5},
+ {0x5ec, 0xff},
+ {0x5fe, 2},
+};
+
+static const struct rj54n1_reg_val bank_7[] = {
+ {0x70a, 0},
+ {0x714, 0xff},
+ {0x715, 0xff},
+ {0x716, 0x1f},
+ {0x7FE, 2},
+};
+
+static const struct rj54n1_reg_val bank_8[] = {
+ {0x800, 0x00},
+ {0x801, 0x01},
+ {0x802, 0x61},
+ {0x805, 0x00},
+ {0x806, 0x00},
+ {0x807, 0x00},
+ {0x808, 0x00},
+ {0x809, 0x01},
+ {0x80A, 0x61},
+ {0x80B, 0x00},
+ {0x80C, 0x01},
+ {0x80D, 0x00},
+ {0x80E, 0x00},
+ {0x80F, 0x00},
+ {0x810, 0x00},
+ {0x811, 0x01},
+ {0x812, 0x61},
+ {0x813, 0x00},
+ {0x814, 0x11},
+ {0x815, 0x00},
+ {0x816, 0x41},
+ {0x817, 0x00},
+ {0x818, 0x51},
+ {0x819, 0x01},
+ {0x81A, 0x1F},
+ {0x81B, 0x00},
+ {0x81C, 0x01},
+ {0x81D, 0x00},
+ {0x81E, 0x11},
+ {0x81F, 0x00},
+ {0x820, 0x41},
+ {0x821, 0x00},
+ {0x822, 0x51},
+ {0x823, 0x00},
+ {0x824, 0x00},
+ {0x825, 0x00},
+ {0x826, 0x47},
+ {0x827, 0x01},
+ {0x828, 0x4F},
+ {0x829, 0x00},
+ {0x82A, 0x00},
+ {0x82B, 0x00},
+ {0x82C, 0x30},
+ {0x82D, 0x00},
+ {0x82E, 0x40},
+ {0x82F, 0x00},
+ {0x830, 0xB3},
+ {0x831, 0x00},
+ {0x832, 0xE3},
+ {0x833, 0x00},
+ {0x834, 0x00},
+ {0x835, 0x00},
+ {0x836, 0x00},
+ {0x837, 0x00},
+ {0x838, 0x00},
+ {0x839, 0x01},
+ {0x83A, 0x61},
+ {0x83B, 0x00},
+ {0x83C, 0x01},
+ {0x83D, 0x00},
+ {0x83E, 0x00},
+ {0x83F, 0x00},
+ {0x840, 0x00},
+ {0x841, 0x01},
+ {0x842, 0x61},
+ {0x843, 0x00},
+ {0x844, 0x1D},
+ {0x845, 0x00},
+ {0x846, 0x00},
+ {0x847, 0x00},
+ {0x848, 0x00},
+ {0x849, 0x01},
+ {0x84A, 0x1F},
+ {0x84B, 0x00},
+ {0x84C, 0x05},
+ {0x84D, 0x00},
+ {0x84E, 0x19},
+ {0x84F, 0x01},
+ {0x850, 0x21},
+ {0x851, 0x01},
+ {0x852, 0x5D},
+ {0x853, 0x00},
+ {0x854, 0x00},
+ {0x855, 0x00},
+ {0x856, 0x19},
+ {0x857, 0x01},
+ {0x858, 0x21},
+ {0x859, 0x00},
+ {0x85A, 0x00},
+ {0x85B, 0x00},
+ {0x85C, 0x00},
+ {0x85D, 0x00},
+ {0x85E, 0x00},
+ {0x85F, 0x00},
+ {0x860, 0xB3},
+ {0x861, 0x00},
+ {0x862, 0xE3},
+ {0x863, 0x00},
+ {0x864, 0x00},
+ {0x865, 0x00},
+ {0x866, 0x00},
+ {0x867, 0x00},
+ {0x868, 0x00},
+ {0x869, 0xE2},
+ {0x86A, 0x00},
+ {0x86B, 0x01},
+ {0x86C, 0x06},
+ {0x86D, 0x00},
+ {0x86E, 0x00},
+ {0x86F, 0x00},
+ {0x870, 0x60},
+ {0x871, 0x8C},
+ {0x872, 0x10},
+ {0x873, 0x00},
+ {0x874, 0xE0},
+ {0x875, 0x00},
+ {0x876, 0x27},
+ {0x877, 0x01},
+ {0x878, 0x00},
+ {0x879, 0x00},
+ {0x87A, 0x00},
+ {0x87B, 0x03},
+ {0x87C, 0x00},
+ {0x87D, 0x00},
+ {0x87E, 0x00},
+ {0x87F, 0x00},
+ {0x880, 0x00},
+ {0x881, 0x00},
+ {0x882, 0x00},
+ {0x883, 0x00},
+ {0x884, 0x00},
+ {0x885, 0x00},
+ {0x886, 0xF8},
+ {0x887, 0x00},
+ {0x888, 0x03},
+ {0x889, 0x00},
+ {0x88A, 0x64},
+ {0x88B, 0x00},
+ {0x88C, 0x03},
+ {0x88D, 0x00},
+ {0x88E, 0xB1},
+ {0x88F, 0x00},
+ {0x890, 0x03},
+ {0x891, 0x01},
+ {0x892, 0x1D},
+ {0x893, 0x00},
+ {0x894, 0x03},
+ {0x895, 0x01},
+ {0x896, 0x4B},
+ {0x897, 0x00},
+ {0x898, 0xE5},
+ {0x899, 0x00},
+ {0x89A, 0x01},
+ {0x89B, 0x00},
+ {0x89C, 0x01},
+ {0x89D, 0x04},
+ {0x89E, 0xC8},
+ {0x89F, 0x00},
+ {0x8A0, 0x01},
+ {0x8A1, 0x01},
+ {0x8A2, 0x61},
+ {0x8A3, 0x00},
+ {0x8A4, 0x01},
+ {0x8A5, 0x00},
+ {0x8A6, 0x00},
+ {0x8A7, 0x00},
+ {0x8A8, 0x00},
+ {0x8A9, 0x00},
+ {0x8AA, 0x7F},
+ {0x8AB, 0x03},
+ {0x8AC, 0x00},
+ {0x8AD, 0x00},
+ {0x8AE, 0x00},
+ {0x8AF, 0x00},
+ {0x8B0, 0x00},
+ {0x8B1, 0x00},
+ {0x8B6, 0x00},
+ {0x8B7, 0x01},
+ {0x8B8, 0x00},
+ {0x8B9, 0x00},
+ {0x8BA, 0x02},
+ {0x8BB, 0x00},
+ {0x8BC, 0xFF},
+ {0x8BD, 0x00},
+ {0x8FE, 2},
+};
+
+static const struct rj54n1_reg_val bank_10[] = {
+ {0x10bf, 0x69}
+};
+
+/* Clock dividers - these are default register values, divider = register + 1 */
+static const struct rj54n1_clock_div clk_div = {
+ .ratio_tg = 3 /* default: 5 */,
+ .ratio_t = 4 /* default: 1 */,
+ .ratio_r = 4 /* default: 0 */,
+ .ratio_op = 1 /* default: 5 */,
+ .ratio_o = 9 /* default: 0 */,
+};
+
+static struct rj54n1 *to_rj54n1(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct rj54n1, subdev);
+}
+
+static int reg_read(struct i2c_client *client, const u16 reg)
+{
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+ int ret;
+
+ /* set bank */
+ if (rj54n1->bank != reg >> 8) {
+ dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8);
+ ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8);
+ if (ret < 0)
+ return ret;
+ rj54n1->bank = reg >> 8;
+ }
+ return i2c_smbus_read_byte_data(client, reg & 0xff);
+}
+
+static int reg_write(struct i2c_client *client, const u16 reg,
+ const u8 data)
+{
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+ int ret;
+
+ /* set bank */
+ if (rj54n1->bank != reg >> 8) {
+ dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8);
+ ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8);
+ if (ret < 0)
+ return ret;
+ rj54n1->bank = reg >> 8;
+ }
+ dev_dbg(&client->dev, "[0x%x] = 0x%x\n", reg & 0xff, data);
+ return i2c_smbus_write_byte_data(client, reg & 0xff, data);
+}
+
+static int reg_set(struct i2c_client *client, const u16 reg,
+ const u8 data, const u8 mask)
+{
+ int ret;
+
+ ret = reg_read(client, reg);
+ if (ret < 0)
+ return ret;
+ return reg_write(client, reg, (ret & ~mask) | (data & mask));
+}
+
+static int reg_write_multiple(struct i2c_client *client,
+ const struct rj54n1_reg_val *rv, const int n)
+{
+ int i, ret;
+
+ for (i = 0; i < n; i++) {
+ ret = reg_write(client, rv->reg, rv->val);
+ if (ret < 0)
+ return ret;
+ rv++;
+ }
+
+ return 0;
+}
+
+static int rj54n1_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= ARRAY_SIZE(rj54n1_colour_fmts))
+ return -EINVAL;
+
+ *code = rj54n1_colour_fmts[index].code;
+ return 0;
+}
+
+static int rj54n1_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ /* Switch between preview and still shot modes */
+ return reg_set(client, RJ54N1_STILL_CONTROL, (!enable) << 7, 0x80);
+}
+
+static int rj54n1_set_rect(struct i2c_client *client,
+ u16 reg_x, u16 reg_y, u16 reg_xy,
+ u32 width, u32 height)
+{
+ int ret;
+
+ ret = reg_write(client, reg_xy,
+ ((width >> 4) & 0x70) |
+ ((height >> 8) & 7));
+
+ if (!ret)
+ ret = reg_write(client, reg_x, width & 0xff);
+ if (!ret)
+ ret = reg_write(client, reg_y, height & 0xff);
+
+ return ret;
+}
+
+/*
+ * Some commands, specifically certain initialisation sequences, require
+ * a commit operation.
+ */
+static int rj54n1_commit(struct i2c_client *client)
+{
+ int ret = reg_write(client, RJ54N1_INIT_START, 1);
+ msleep(10);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_INIT_START, 0);
+ return ret;
+}
+
+static int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h,
+ s32 *out_w, s32 *out_h);
+
+static int rj54n1_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+ struct v4l2_rect *rect = &a->c;
+ int dummy = 0, output_w, output_h,
+ input_w = rect->width, input_h = rect->height;
+ int ret;
+
+ /* arbitrary minimum width and height, edges unimportant */
+ soc_camera_limit_side(&dummy, &input_w,
+ RJ54N1_COLUMN_SKIP, 8, RJ54N1_MAX_WIDTH);
+
+ soc_camera_limit_side(&dummy, &input_h,
+ RJ54N1_ROW_SKIP, 8, RJ54N1_MAX_HEIGHT);
+
+ output_w = (input_w * 1024 + rj54n1->resize / 2) / rj54n1->resize;
+ output_h = (input_h * 1024 + rj54n1->resize / 2) / rj54n1->resize;
+
+ dev_dbg(&client->dev, "Scaling for %dx%d : %u = %dx%d\n",
+ input_w, input_h, rj54n1->resize, output_w, output_h);
+
+ ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h);
+ if (ret < 0)
+ return ret;
+
+ rj54n1->width = output_w;
+ rj54n1->height = output_h;
+ rj54n1->resize = ret;
+ rj54n1->rect.width = input_w;
+ rj54n1->rect.height = input_h;
+
+ return 0;
+}
+
+static int rj54n1_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+
+ a->c = rj54n1->rect;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int rj54n1_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = RJ54N1_COLUMN_SKIP;
+ a->bounds.top = RJ54N1_ROW_SKIP;
+ a->bounds.width = RJ54N1_MAX_WIDTH;
+ a->bounds.height = RJ54N1_MAX_HEIGHT;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int rj54n1_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+
+ mf->code = rj54n1->fmt->code;
+ mf->colorspace = rj54n1->fmt->colorspace;
+ mf->field = V4L2_FIELD_NONE;
+ mf->width = rj54n1->width;
+ mf->height = rj54n1->height;
+
+ return 0;
+}
+
+/*
+ * The actual geometry configuration routine. It scales the input window into
+ * the output one, updates the window sizes and returns an error or the resize
+ * coefficient on success. Note: we only use the "Fixed Scaling" on this camera.
+ */
+static int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h,
+ s32 *out_w, s32 *out_h)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+ unsigned int skip, resize, input_w = *in_w, input_h = *in_h,
+ output_w = *out_w, output_h = *out_h;
+ u16 inc_sel, wb_bit8, wb_left, wb_right, wb_top, wb_bottom;
+ unsigned int peak, peak_50, peak_60;
+ int ret;
+
+ /*
+ * We have a problem with crops, where the window is larger than 512x384
+ * and output window is larger than a half of the input one. In this
+ * case we have to either reduce the input window to equal or below
+ * 512x384 or the output window to equal or below 1/2 of the input.
+ */
+ if (output_w > max(512U, input_w / 2)) {
+ if (2 * output_w > RJ54N1_MAX_WIDTH) {
+ input_w = RJ54N1_MAX_WIDTH;
+ output_w = RJ54N1_MAX_WIDTH / 2;
+ } else {
+ input_w = output_w * 2;
+ }
+
+ dev_dbg(&client->dev, "Adjusted output width: in %u, out %u\n",
+ input_w, output_w);
+ }
+
+ if (output_h > max(384U, input_h / 2)) {
+ if (2 * output_h > RJ54N1_MAX_HEIGHT) {
+ input_h = RJ54N1_MAX_HEIGHT;
+ output_h = RJ54N1_MAX_HEIGHT / 2;
+ } else {
+ input_h = output_h * 2;
+ }
+
+ dev_dbg(&client->dev, "Adjusted output height: in %u, out %u\n",
+ input_h, output_h);
+ }
+
+ /* Idea: use the read mode for snapshots, handle separate geometries */
+ ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_S_L,
+ RJ54N1_Y_OUTPUT_SIZE_S_L,
+ RJ54N1_XY_OUTPUT_SIZE_S_H, output_w, output_h);
+ if (!ret)
+ ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_P_L,
+ RJ54N1_Y_OUTPUT_SIZE_P_L,
+ RJ54N1_XY_OUTPUT_SIZE_P_H, output_w, output_h);
+
+ if (ret < 0)
+ return ret;
+
+ if (output_w > input_w && output_h > input_h) {
+ input_w = output_w;
+ input_h = output_h;
+
+ resize = 1024;
+ } else {
+ unsigned int resize_x, resize_y;
+ resize_x = (input_w * 1024 + output_w / 2) / output_w;
+ resize_y = (input_h * 1024 + output_h / 2) / output_h;
+
+ /* We want max(resize_x, resize_y), check if it still fits */
+ if (resize_x > resize_y &&
+ (output_h * resize_x + 512) / 1024 > RJ54N1_MAX_HEIGHT)
+ resize = (RJ54N1_MAX_HEIGHT * 1024 + output_h / 2) /
+ output_h;
+ else if (resize_y > resize_x &&
+ (output_w * resize_y + 512) / 1024 > RJ54N1_MAX_WIDTH)
+ resize = (RJ54N1_MAX_WIDTH * 1024 + output_w / 2) /
+ output_w;
+ else
+ resize = max(resize_x, resize_y);
+
+ /* Prohibited value ranges */
+ switch (resize) {
+ case 2040 ... 2047:
+ resize = 2039;
+ break;
+ case 4080 ... 4095:
+ resize = 4079;
+ break;
+ case 8160 ... 8191:
+ resize = 8159;
+ break;
+ case 16320 ... 16384:
+ resize = 16319;
+ }
+ }
+
+ /* Set scaling */
+ ret = reg_write(client, RJ54N1_RESIZE_HOLD_L, resize & 0xff);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RESIZE_HOLD_H, resize >> 8);
+
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Configure a skipping bitmask. The sensor will select a skipping value
+ * among set bits automatically. This is very unclear in the datasheet
+ * too. I was told, in this register one enables all skipping values,
+ * that are required for a specific resize, and the camera selects
+ * automatically, which ones to use. But it is unclear how to identify,
+ * which cropping values are needed. Secondly, why don't we just set all
+ * bits and let the camera choose? Would it increase processing time and
+ * reduce the framerate? Using 0xfffc for INC_USE_SEL doesn't seem to
+ * improve the image quality or stability for larger frames (see comment
+ * above), but I didn't check the framerate.
+ */
+ skip = min(resize / 1024, 15U);
+
+ inc_sel = 1 << skip;
+
+ if (inc_sel <= 2)
+ inc_sel = 0xc;
+ else if (resize & 1023 && skip < 15)
+ inc_sel |= 1 << (skip + 1);
+
+ ret = reg_write(client, RJ54N1_INC_USE_SEL_L, inc_sel & 0xfc);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_INC_USE_SEL_H, inc_sel >> 8);
+
+ if (!rj54n1->auto_wb) {
+ /* Auto white balance window */
+ wb_left = output_w / 16;
+ wb_right = (3 * output_w / 4 - 3) / 4;
+ wb_top = output_h / 16;
+ wb_bottom = (3 * output_h / 4 - 3) / 4;
+ wb_bit8 = ((wb_left >> 2) & 0x40) | ((wb_top >> 4) & 0x10) |
+ ((wb_right >> 6) & 4) | ((wb_bottom >> 8) & 1);
+
+ if (!ret)
+ ret = reg_write(client, RJ54N1_BIT8_WB, wb_bit8);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_HCAPS_WB, wb_left);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_VCAPS_WB, wb_top);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_HCAPE_WB, wb_right);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_VCAPE_WB, wb_bottom);
+ }
+
+ /* Antiflicker */
+ peak = 12 * RJ54N1_MAX_WIDTH * (1 << 14) * resize / rj54n1->tgclk_mhz /
+ 10000;
+ peak_50 = peak / 6;
+ peak_60 = peak / 5;
+
+ if (!ret)
+ ret = reg_write(client, RJ54N1_PEAK_H,
+ ((peak_50 >> 4) & 0xf0) | (peak_60 >> 8));
+ if (!ret)
+ ret = reg_write(client, RJ54N1_PEAK_50, peak_50);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_PEAK_60, peak_60);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_PEAK_DIFF, peak / 150);
+
+ /* Start resizing */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RESIZE_CONTROL,
+ RESIZE_HOLD_SEL | RESIZE_GO | 1);
+
+ if (ret < 0)
+ return ret;
+
+ /* Constant taken from manufacturer's example */
+ msleep(230);
+
+ ret = reg_write(client, RJ54N1_RESIZE_CONTROL, RESIZE_HOLD_SEL | 1);
+ if (ret < 0)
+ return ret;
+
+ *in_w = (output_w * resize + 512) / 1024;
+ *in_h = (output_h * resize + 512) / 1024;
+ *out_w = output_w;
+ *out_h = output_h;
+
+ dev_dbg(&client->dev, "Scaled for %dx%d : %u = %ux%u, skip %u\n",
+ *in_w, *in_h, resize, output_w, output_h, skip);
+
+ return resize;
+}
+
+static int rj54n1_set_clock(struct i2c_client *client)
+{
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+ int ret;
+
+ /* Enable external clock */
+ ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK | SOFT_STDBY);
+ /* Leave stand-by. Note: use this when implementing suspend / resume */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK);
+
+ if (!ret)
+ ret = reg_write(client, RJ54N1_PLL_L, PLL_L);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_PLL_N, PLL_N);
+
+ /* TGCLK dividers */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RATIO_TG,
+ rj54n1->clk_div.ratio_tg);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RATIO_T,
+ rj54n1->clk_div.ratio_t);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RATIO_R,
+ rj54n1->clk_div.ratio_r);
+
+ /* Enable TGCLK & RAMP */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RAMP_TGCLK_EN, 3);
+
+ /* Disable clock output */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_OCLK_DSP, 0);
+
+ /* Set divisors */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RATIO_OP,
+ rj54n1->clk_div.ratio_op);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RATIO_O,
+ rj54n1->clk_div.ratio_o);
+
+ /* Enable OCLK */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1);
+
+ /* Use PLL for Timing Generator, write 2 to reserved bits */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_TG_BYPASS, 2);
+
+ /* Take sensor out of reset */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RESET_STANDBY,
+ E_EXCLK | SEN_RSTX);
+ /* Enable PLL */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_PLL_EN, 1);
+
+ /* Wait for PLL to stabilise */
+ msleep(10);
+
+ /* Enable clock to frequency divider */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_CLK_RST, 1);
+
+ if (!ret)
+ ret = reg_read(client, RJ54N1_CLK_RST);
+ if (ret != 1) {
+ dev_err(&client->dev,
+ "Resetting RJ54N1CB0C clock failed: %d!\n", ret);
+ return -EIO;
+ }
+
+ /* Start the PLL */
+ ret = reg_set(client, RJ54N1_OCLK_DSP, 1, 1);
+
+ /* Enable OCLK */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1);
+
+ return ret;
+}
+
+static int rj54n1_reg_init(struct i2c_client *client)
+{
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+ int ret = rj54n1_set_clock(client);
+
+ if (!ret)
+ ret = reg_write_multiple(client, bank_7, ARRAY_SIZE(bank_7));
+ if (!ret)
+ ret = reg_write_multiple(client, bank_10, ARRAY_SIZE(bank_10));
+
+ /* Set binning divisors */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_SCALE_1_2_LEV, 3 | (7 << 4));
+ if (!ret)
+ ret = reg_write(client, RJ54N1_SCALE_4_LEV, 0xf);
+
+ /* Switch to fixed resize mode */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RESIZE_CONTROL,
+ RESIZE_HOLD_SEL | 1);
+
+ /* Set gain */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_Y_GAIN, 0x84);
+
+ /*
+ * Mirror the image back: default is upside down and left-to-right...
+ * Set manual preview / still shot switching
+ */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_MIRROR_STILL_MODE, 0x27);
+
+ if (!ret)
+ ret = reg_write_multiple(client, bank_4, ARRAY_SIZE(bank_4));
+
+ /* Auto exposure area */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_EXPOSURE_CONTROL, 0x80);
+ /* Check current auto WB config */
+ if (!ret)
+ ret = reg_read(client, RJ54N1_WB_SEL_WEIGHT_I);
+ if (ret >= 0) {
+ rj54n1->auto_wb = ret & 0x80;
+ ret = reg_write_multiple(client, bank_5, ARRAY_SIZE(bank_5));
+ }
+ if (!ret)
+ ret = reg_write_multiple(client, bank_8, ARRAY_SIZE(bank_8));
+
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RESET_STANDBY,
+ E_EXCLK | DSP_RSTX | SEN_RSTX);
+
+ /* Commit init */
+ if (!ret)
+ ret = rj54n1_commit(client);
+
+ /* Take DSP, TG, sensor out of reset */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RESET_STANDBY,
+ E_EXCLK | DSP_RSTX | TG_RSTX | SEN_RSTX);
+
+ /* Start register update? Same register as 0x?FE in many bank_* sets */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_FWFLG, 2);
+
+ /* Constant taken from manufacturer's example */
+ msleep(700);
+
+ return ret;
+}
+
+static int rj54n1_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+ const struct rj54n1_datafmt *fmt;
+ int align = mf->code == V4L2_MBUS_FMT_SBGGR10_1X10 ||
+ mf->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE ||
+ mf->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE ||
+ mf->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE ||
+ mf->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE;
+
+ dev_dbg(&client->dev, "%s: code = %d, width = %u, height = %u\n",
+ __func__, mf->code, mf->width, mf->height);
+
+ fmt = rj54n1_find_datafmt(mf->code, rj54n1_colour_fmts,
+ ARRAY_SIZE(rj54n1_colour_fmts));
+ if (!fmt) {
+ fmt = rj54n1->fmt;
+ mf->code = fmt->code;
+ }
+
+ mf->field = V4L2_FIELD_NONE;
+ mf->colorspace = fmt->colorspace;
+
+ v4l_bound_align_image(&mf->width, 112, RJ54N1_MAX_WIDTH, align,
+ &mf->height, 84, RJ54N1_MAX_HEIGHT, align, 0);
+
+ return 0;
+}
+
+static int rj54n1_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+ const struct rj54n1_datafmt *fmt;
+ int output_w, output_h, max_w, max_h,
+ input_w = rj54n1->rect.width, input_h = rj54n1->rect.height;
+ int ret;
+
+ /*
+ * The host driver can call us without .try_fmt(), so, we have to take
+ * care ourseleves
+ */
+ rj54n1_try_fmt(sd, mf);
+
+ /*
+ * Verify if the sensor has just been powered on. TODO: replace this
+ * with proper PM, when a suitable API is available.
+ */
+ ret = reg_read(client, RJ54N1_RESET_STANDBY);
+ if (ret < 0)
+ return ret;
+
+ if (!(ret & E_EXCLK)) {
+ ret = rj54n1_reg_init(client);
+ if (ret < 0)
+ return ret;
+ }
+
+ dev_dbg(&client->dev, "%s: code = %d, width = %u, height = %u\n",
+ __func__, mf->code, mf->width, mf->height);
+
+ /* RA_SEL_UL is only relevant for raw modes, ignored otherwise. */
+ switch (mf->code) {
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ ret = reg_write(client, RJ54N1_OUT_SEL, 0);
+ if (!ret)
+ ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8);
+ break;
+ case V4L2_MBUS_FMT_YVYU8_2X8:
+ ret = reg_write(client, RJ54N1_OUT_SEL, 0);
+ if (!ret)
+ ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8);
+ break;
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ ret = reg_write(client, RJ54N1_OUT_SEL, 0x11);
+ if (!ret)
+ ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8);
+ break;
+ case V4L2_MBUS_FMT_RGB565_2X8_BE:
+ ret = reg_write(client, RJ54N1_OUT_SEL, 0x11);
+ if (!ret)
+ ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8);
+ break;
+ case V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE:
+ ret = reg_write(client, RJ54N1_OUT_SEL, 4);
+ if (!ret)
+ ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RA_SEL_UL, 0);
+ break;
+ case V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE:
+ ret = reg_write(client, RJ54N1_OUT_SEL, 4);
+ if (!ret)
+ ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RA_SEL_UL, 8);
+ break;
+ case V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE:
+ ret = reg_write(client, RJ54N1_OUT_SEL, 4);
+ if (!ret)
+ ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RA_SEL_UL, 0);
+ break;
+ case V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE:
+ ret = reg_write(client, RJ54N1_OUT_SEL, 4);
+ if (!ret)
+ ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RA_SEL_UL, 8);
+ break;
+ case V4L2_MBUS_FMT_SBGGR10_1X10:
+ ret = reg_write(client, RJ54N1_OUT_SEL, 5);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ /* Special case: a raw mode with 10 bits of data per clock tick */
+ if (!ret)
+ ret = reg_set(client, RJ54N1_OCLK_SEL_EN,
+ (mf->code == V4L2_MBUS_FMT_SBGGR10_1X10) << 1, 2);
+
+ if (ret < 0)
+ return ret;
+
+ /* Supported scales 1:1 >= scale > 1:16 */
+ max_w = mf->width * (16 * 1024 - 1) / 1024;
+ if (input_w > max_w)
+ input_w = max_w;
+ max_h = mf->height * (16 * 1024 - 1) / 1024;
+ if (input_h > max_h)
+ input_h = max_h;
+
+ output_w = mf->width;
+ output_h = mf->height;
+
+ ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h);
+ if (ret < 0)
+ return ret;
+
+ fmt = rj54n1_find_datafmt(mf->code, rj54n1_colour_fmts,
+ ARRAY_SIZE(rj54n1_colour_fmts));
+
+ rj54n1->fmt = fmt;
+ rj54n1->resize = ret;
+ rj54n1->rect.width = input_w;
+ rj54n1->rect.height = input_h;
+ rj54n1->width = output_w;
+ rj54n1->height = output_h;
+
+ mf->width = output_w;
+ mf->height = output_h;
+ mf->field = V4L2_FIELD_NONE;
+ mf->colorspace = fmt->colorspace;
+
+ return 0;
+}
+
+static int rj54n1_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+
+ if (id->match.addr != client->addr)
+ return -ENODEV;
+
+ id->ident = V4L2_IDENT_RJ54N1CB0C;
+ id->revision = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int rj54n1_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR ||
+ reg->reg < 0x400 || reg->reg > 0x1fff)
+ /* Registers > 0x0800 are only available from Sharp support */
+ return -EINVAL;
+
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ reg->size = 1;
+ reg->val = reg_read(client, reg->reg);
+
+ if (reg->val > 0xff)
+ return -EIO;
+
+ return 0;
+}
+
+static int rj54n1_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR ||
+ reg->reg < 0x400 || reg->reg > 0x1fff)
+ /* Registers >= 0x0800 are only available from Sharp support */
+ return -EINVAL;
+
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ if (reg_write(client, reg->reg, reg->val) < 0)
+ return -EIO;
+
+ return 0;
+}
+#endif
+
+static int rj54n1_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct rj54n1 *rj54n1 = container_of(ctrl->handler, struct rj54n1, hdl);
+ struct v4l2_subdev *sd = &rj54n1->subdev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int data;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ if (ctrl->val)
+ data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 1);
+ else
+ data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 1, 1);
+ if (data < 0)
+ return -EIO;
+ return 0;
+ case V4L2_CID_HFLIP:
+ if (ctrl->val)
+ data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 2);
+ else
+ data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 2, 2);
+ if (data < 0)
+ return -EIO;
+ return 0;
+ case V4L2_CID_GAIN:
+ if (reg_write(client, RJ54N1_Y_GAIN, ctrl->val * 2) < 0)
+ return -EIO;
+ return 0;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ /* Auto WB area - whole image */
+ if (reg_set(client, RJ54N1_WB_SEL_WEIGHT_I, ctrl->val << 7,
+ 0x80) < 0)
+ return -EIO;
+ rj54n1->auto_wb = ctrl->val;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static const struct v4l2_ctrl_ops rj54n1_ctrl_ops = {
+ .s_ctrl = rj54n1_s_ctrl,
+};
+
+static struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = {
+ .g_chip_ident = rj54n1_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = rj54n1_g_register,
+ .s_register = rj54n1_s_register,
+#endif
+};
+
+static int rj54n1_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags =
+ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING |
+ V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static int rj54n1_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ /* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */
+ if (soc_camera_apply_board_flags(icl, cfg) &
+ V4L2_MBUS_PCLK_SAMPLE_RISING)
+ return reg_write(client, RJ54N1_OUT_SIGPO, 1 << 4);
+ else
+ return reg_write(client, RJ54N1_OUT_SIGPO, 0);
+}
+
+static struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = {
+ .s_stream = rj54n1_s_stream,
+ .s_mbus_fmt = rj54n1_s_fmt,
+ .g_mbus_fmt = rj54n1_g_fmt,
+ .try_mbus_fmt = rj54n1_try_fmt,
+ .enum_mbus_fmt = rj54n1_enum_fmt,
+ .g_crop = rj54n1_g_crop,
+ .s_crop = rj54n1_s_crop,
+ .cropcap = rj54n1_cropcap,
+ .g_mbus_config = rj54n1_g_mbus_config,
+ .s_mbus_config = rj54n1_s_mbus_config,
+};
+
+static struct v4l2_subdev_ops rj54n1_subdev_ops = {
+ .core = &rj54n1_subdev_core_ops,
+ .video = &rj54n1_subdev_video_ops,
+};
+
+/*
+ * Interface active, can use i2c. If it fails, it can indeed mean, that
+ * this wasn't our capture interface, so, we wait for the right one
+ */
+static int rj54n1_video_probe(struct i2c_client *client,
+ struct rj54n1_pdata *priv)
+{
+ int data1, data2;
+ int ret;
+
+ /* Read out the chip version register */
+ data1 = reg_read(client, RJ54N1_DEV_CODE);
+ data2 = reg_read(client, RJ54N1_DEV_CODE2);
+
+ if (data1 != 0x51 || data2 != 0x10) {
+ ret = -ENODEV;
+ dev_info(&client->dev, "No RJ54N1CB0C found, read 0x%x:0x%x\n",
+ data1, data2);
+ goto ei2c;
+ }
+
+ /* Configure IOCTL polarity from the platform data: 0 or 1 << 7. */
+ ret = reg_write(client, RJ54N1_IOC, priv->ioctl_high << 7);
+ if (ret < 0)
+ goto ei2c;
+
+ dev_info(&client->dev, "Detected a RJ54N1CB0C chip ID 0x%x:0x%x\n",
+ data1, data2);
+
+ei2c:
+ return ret;
+}
+
+static int rj54n1_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct rj54n1 *rj54n1;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct rj54n1_pdata *rj54n1_priv;
+ int ret;
+
+ if (!icl || !icl->priv) {
+ dev_err(&client->dev, "RJ54N1CB0C: missing platform data!\n");
+ return -EINVAL;
+ }
+
+ rj54n1_priv = icl->priv;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_warn(&adapter->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
+ return -EIO;
+ }
+
+ rj54n1 = kzalloc(sizeof(struct rj54n1), GFP_KERNEL);
+ if (!rj54n1)
+ return -ENOMEM;
+
+ v4l2_i2c_subdev_init(&rj54n1->subdev, client, &rj54n1_subdev_ops);
+ v4l2_ctrl_handler_init(&rj54n1->hdl, 4);
+ v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
+ V4L2_CID_GAIN, 0, 127, 1, 66);
+ v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
+ V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+ rj54n1->subdev.ctrl_handler = &rj54n1->hdl;
+ if (rj54n1->hdl.error) {
+ int err = rj54n1->hdl.error;
+
+ kfree(rj54n1);
+ return err;
+ }
+
+ rj54n1->clk_div = clk_div;
+ rj54n1->rect.left = RJ54N1_COLUMN_SKIP;
+ rj54n1->rect.top = RJ54N1_ROW_SKIP;
+ rj54n1->rect.width = RJ54N1_MAX_WIDTH;
+ rj54n1->rect.height = RJ54N1_MAX_HEIGHT;
+ rj54n1->width = RJ54N1_MAX_WIDTH;
+ rj54n1->height = RJ54N1_MAX_HEIGHT;
+ rj54n1->fmt = &rj54n1_colour_fmts[0];
+ rj54n1->resize = 1024;
+ rj54n1->tgclk_mhz = (rj54n1_priv->mclk_freq / PLL_L * PLL_N) /
+ (clk_div.ratio_tg + 1) / (clk_div.ratio_t + 1);
+
+ ret = rj54n1_video_probe(client, rj54n1_priv);
+ if (ret < 0) {
+ v4l2_ctrl_handler_free(&rj54n1->hdl);
+ kfree(rj54n1);
+ return ret;
+ }
+ return v4l2_ctrl_handler_setup(&rj54n1->hdl);
+}
+
+static int rj54n1_remove(struct i2c_client *client)
+{
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ v4l2_device_unregister_subdev(&rj54n1->subdev);
+ if (icl->free_bus)
+ icl->free_bus(icl);
+ v4l2_ctrl_handler_free(&rj54n1->hdl);
+ kfree(rj54n1);
+
+ return 0;
+}
+
+static const struct i2c_device_id rj54n1_id[] = {
+ { "rj54n1cb0c", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rj54n1_id);
+
+static struct i2c_driver rj54n1_i2c_driver = {
+ .driver = {
+ .name = "rj54n1cb0c",
+ },
+ .probe = rj54n1_probe,
+ .remove = rj54n1_remove,
+ .id_table = rj54n1_id,
+};
+
+module_i2c_driver(rj54n1_i2c_driver);
+
+MODULE_DESCRIPTION("Sharp RJ54N1CB0C Camera driver");
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c
new file mode 100644
index 00000000..4894cbb1
--- /dev/null
+++ b/drivers/media/video/s2255drv.c
@@ -0,0 +1,2689 @@
+/*
+ * s2255drv.c - a driver for the Sensoray 2255 USB video capture device
+ *
+ * Copyright (C) 2007-2010 by Sensoray Company Inc.
+ * Dean Anderson
+ *
+ * Some video buffer code based on vivi driver:
+ *
+ * Sensoray 2255 device supports 4 simultaneous channels.
+ * The channels are not "crossbar" inputs, they are physically
+ * attached to separate video decoders.
+ *
+ * Because of USB2.0 bandwidth limitations. There is only a
+ * certain amount of data which may be transferred at one time.
+ *
+ * Example maximum bandwidth utilization:
+ *
+ * -full size, color mode YUYV or YUV422P: 2 channels at once
+ * -full or half size Grey scale: all 4 channels at once
+ * -half size, color mode YUYV or YUV422P: all 4 channels at once
+ * -full size, color mode YUYV or YUV422P 1/2 frame rate: all 4 channels
+ * at once.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/mm.h>
+#include <media/videobuf-vmalloc.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <linux/vmalloc.h>
+#include <linux/usb.h>
+
+#define S2255_VERSION "1.22.1"
+#define FIRMWARE_FILE_NAME "f2255usb.bin"
+
+/* default JPEG quality */
+#define S2255_DEF_JPEG_QUAL 50
+/* vendor request in */
+#define S2255_VR_IN 0
+/* vendor request out */
+#define S2255_VR_OUT 1
+/* firmware query */
+#define S2255_VR_FW 0x30
+/* USB endpoint number for configuring the device */
+#define S2255_CONFIG_EP 2
+/* maximum time for DSP to start responding after last FW word loaded(ms) */
+#define S2255_DSP_BOOTTIME 800
+/* maximum time to wait for firmware to load (ms) */
+#define S2255_LOAD_TIMEOUT (5000 + S2255_DSP_BOOTTIME)
+#define S2255_DEF_BUFS 16
+#define S2255_SETMODE_TIMEOUT 500
+#define S2255_VIDSTATUS_TIMEOUT 350
+#define S2255_MARKER_FRAME cpu_to_le32(0x2255DA4AL)
+#define S2255_MARKER_RESPONSE cpu_to_le32(0x2255ACACL)
+#define S2255_RESPONSE_SETMODE cpu_to_le32(0x01)
+#define S2255_RESPONSE_FW cpu_to_le32(0x10)
+#define S2255_RESPONSE_STATUS cpu_to_le32(0x20)
+#define S2255_USB_XFER_SIZE (16 * 1024)
+#define MAX_CHANNELS 4
+#define SYS_FRAMES 4
+/* maximum size is PAL full size plus room for the marker header(s) */
+#define SYS_FRAMES_MAXSIZE (720*288*2*2 + 4096)
+#define DEF_USB_BLOCK S2255_USB_XFER_SIZE
+#define LINE_SZ_4CIFS_NTSC 640
+#define LINE_SZ_2CIFS_NTSC 640
+#define LINE_SZ_1CIFS_NTSC 320
+#define LINE_SZ_4CIFS_PAL 704
+#define LINE_SZ_2CIFS_PAL 704
+#define LINE_SZ_1CIFS_PAL 352
+#define NUM_LINES_4CIFS_NTSC 240
+#define NUM_LINES_2CIFS_NTSC 240
+#define NUM_LINES_1CIFS_NTSC 240
+#define NUM_LINES_4CIFS_PAL 288
+#define NUM_LINES_2CIFS_PAL 288
+#define NUM_LINES_1CIFS_PAL 288
+#define LINE_SZ_DEF 640
+#define NUM_LINES_DEF 240
+
+
+/* predefined settings */
+#define FORMAT_NTSC 1
+#define FORMAT_PAL 2
+
+#define SCALE_4CIFS 1 /* 640x480(NTSC) or 704x576(PAL) */
+#define SCALE_2CIFS 2 /* 640x240(NTSC) or 704x288(PAL) */
+#define SCALE_1CIFS 3 /* 320x240(NTSC) or 352x288(PAL) */
+/* SCALE_4CIFSI is the 2 fields interpolated into one */
+#define SCALE_4CIFSI 4 /* 640x480(NTSC) or 704x576(PAL) high quality */
+
+#define COLOR_YUVPL 1 /* YUV planar */
+#define COLOR_YUVPK 2 /* YUV packed */
+#define COLOR_Y8 4 /* monochrome */
+#define COLOR_JPG 5 /* JPEG */
+
+#define MASK_COLOR 0x000000ff
+#define MASK_JPG_QUALITY 0x0000ff00
+#define MASK_INPUT_TYPE 0x000f0000
+/* frame decimation. */
+#define FDEC_1 1 /* capture every frame. default */
+#define FDEC_2 2 /* capture every 2nd frame */
+#define FDEC_3 3 /* capture every 3rd frame */
+#define FDEC_5 5 /* capture every 5th frame */
+
+/*-------------------------------------------------------
+ * Default mode parameters.
+ *-------------------------------------------------------*/
+#define DEF_SCALE SCALE_4CIFS
+#define DEF_COLOR COLOR_YUVPL
+#define DEF_FDEC FDEC_1
+#define DEF_BRIGHT 0
+#define DEF_CONTRAST 0x5c
+#define DEF_SATURATION 0x80
+#define DEF_HUE 0
+
+/* usb config commands */
+#define IN_DATA_TOKEN cpu_to_le32(0x2255c0de)
+#define CMD_2255 0xc2255000
+#define CMD_SET_MODE cpu_to_le32((CMD_2255 | 0x10))
+#define CMD_START cpu_to_le32((CMD_2255 | 0x20))
+#define CMD_STOP cpu_to_le32((CMD_2255 | 0x30))
+#define CMD_STATUS cpu_to_le32((CMD_2255 | 0x40))
+
+struct s2255_mode {
+ u32 format; /* input video format (NTSC, PAL) */
+ u32 scale; /* output video scale */
+ u32 color; /* output video color format */
+ u32 fdec; /* frame decimation */
+ u32 bright; /* brightness */
+ u32 contrast; /* contrast */
+ u32 saturation; /* saturation */
+ u32 hue; /* hue (NTSC only)*/
+ u32 single; /* capture 1 frame at a time (!=0), continuously (==0)*/
+ u32 usb_block; /* block size. should be 4096 of DEF_USB_BLOCK */
+ u32 restart; /* if DSP requires restart */
+};
+
+
+#define S2255_READ_IDLE 0
+#define S2255_READ_FRAME 1
+
+/* frame structure */
+struct s2255_framei {
+ unsigned long size;
+ unsigned long ulState; /* ulState:S2255_READ_IDLE, S2255_READ_FRAME*/
+ void *lpvbits; /* image data */
+ unsigned long cur_size; /* current data copied to it */
+};
+
+/* image buffer structure */
+struct s2255_bufferi {
+ unsigned long dwFrames; /* number of frames in buffer */
+ struct s2255_framei frame[SYS_FRAMES]; /* array of FRAME structures */
+};
+
+#define DEF_MODEI_NTSC_CONT {FORMAT_NTSC, DEF_SCALE, DEF_COLOR, \
+ DEF_FDEC, DEF_BRIGHT, DEF_CONTRAST, DEF_SATURATION, \
+ DEF_HUE, 0, DEF_USB_BLOCK, 0}
+
+struct s2255_dmaqueue {
+ struct list_head active;
+ struct s2255_dev *dev;
+};
+
+/* for firmware loading, fw_state */
+#define S2255_FW_NOTLOADED 0
+#define S2255_FW_LOADED_DSPWAIT 1
+#define S2255_FW_SUCCESS 2
+#define S2255_FW_FAILED 3
+#define S2255_FW_DISCONNECTING 4
+#define S2255_FW_MARKER cpu_to_le32(0x22552f2f)
+/* 2255 read states */
+#define S2255_READ_IDLE 0
+#define S2255_READ_FRAME 1
+struct s2255_fw {
+ int fw_loaded;
+ int fw_size;
+ struct urb *fw_urb;
+ atomic_t fw_state;
+ void *pfw_data;
+ wait_queue_head_t wait_fw;
+ const struct firmware *fw;
+};
+
+struct s2255_pipeinfo {
+ u32 max_transfer_size;
+ u32 cur_transfer_size;
+ u8 *transfer_buffer;
+ u32 state;
+ void *stream_urb;
+ void *dev; /* back pointer to s2255_dev struct*/
+ u32 err_count;
+ u32 idx;
+};
+
+struct s2255_fmt; /*forward declaration */
+struct s2255_dev;
+
+struct s2255_channel {
+ struct video_device vdev;
+ int resources;
+ struct s2255_dmaqueue vidq;
+ struct s2255_bufferi buffer;
+ struct s2255_mode mode;
+ /* jpeg compression */
+ struct v4l2_jpegcompression jc;
+ /* capture parameters (for high quality mode full size) */
+ struct v4l2_captureparm cap_parm;
+ int cur_frame;
+ int last_frame;
+
+ int b_acquire;
+ /* allocated image size */
+ unsigned long req_image_size;
+ /* received packet size */
+ unsigned long pkt_size;
+ int bad_payload;
+ unsigned long frame_count;
+ /* if JPEG image */
+ int jpg_size;
+ /* if channel configured to default state */
+ int configured;
+ wait_queue_head_t wait_setmode;
+ int setmode_ready;
+ /* video status items */
+ int vidstatus;
+ wait_queue_head_t wait_vidstatus;
+ int vidstatus_ready;
+ unsigned int width;
+ unsigned int height;
+ const struct s2255_fmt *fmt;
+ int idx; /* channel number on device, 0-3 */
+};
+
+
+struct s2255_dev {
+ struct s2255_channel channel[MAX_CHANNELS];
+ struct v4l2_device v4l2_dev;
+ atomic_t num_channels;
+ int frames;
+ struct mutex lock; /* channels[].vdev.lock */
+ struct mutex open_lock;
+ struct usb_device *udev;
+ struct usb_interface *interface;
+ u8 read_endpoint;
+ struct timer_list timer;
+ struct s2255_fw *fw_data;
+ struct s2255_pipeinfo pipe;
+ u32 cc; /* current channel */
+ int frame_ready;
+ int chn_ready;
+ spinlock_t slock;
+ /* dsp firmware version (f2255usb.bin) */
+ int dsp_fw_ver;
+ u16 pid; /* product id */
+};
+
+static inline struct s2255_dev *to_s2255_dev(struct v4l2_device *v4l2_dev)
+{
+ return container_of(v4l2_dev, struct s2255_dev, v4l2_dev);
+}
+
+struct s2255_fmt {
+ char *name;
+ u32 fourcc;
+ int depth;
+};
+
+/* buffer for one video frame */
+struct s2255_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+ const struct s2255_fmt *fmt;
+};
+
+struct s2255_fh {
+ struct s2255_dev *dev;
+ struct videobuf_queue vb_vidq;
+ enum v4l2_buf_type type;
+ struct s2255_channel *channel;
+ int resources;
+};
+
+/* current cypress EEPROM firmware version */
+#define S2255_CUR_USB_FWVER ((3 << 8) | 12)
+/* current DSP FW version */
+#define S2255_CUR_DSP_FWVER 10104
+/* Need DSP version 5+ for video status feature */
+#define S2255_MIN_DSP_STATUS 5
+#define S2255_MIN_DSP_COLORFILTER 8
+#define S2255_NORMS (V4L2_STD_PAL | V4L2_STD_NTSC)
+
+/* private V4L2 controls */
+
+/*
+ * The following chart displays how COLORFILTER should be set
+ * =========================================================
+ * = fourcc = COLORFILTER =
+ * = ===============================
+ * = = 0 = 1 =
+ * =========================================================
+ * = V4L2_PIX_FMT_GREY(Y8) = monochrome from = monochrome=
+ * = = s-video or = composite =
+ * = = B/W camera = input =
+ * =========================================================
+ * = other = color, svideo = color, =
+ * = = = composite =
+ * =========================================================
+ *
+ * Notes:
+ * channels 0-3 on 2255 are composite
+ * channels 0-1 on 2257 are composite, 2-3 are s-video
+ * If COLORFILTER is 0 with a composite color camera connected,
+ * the output will appear monochrome but hatching
+ * will occur.
+ * COLORFILTER is different from "color killer" and "color effects"
+ * for reasons above.
+ */
+#define S2255_V4L2_YC_ON 1
+#define S2255_V4L2_YC_OFF 0
+#define V4L2_CID_PRIVATE_COLORFILTER (V4L2_CID_PRIVATE_BASE + 0)
+
+/* frame prefix size (sent once every frame) */
+#define PREFIX_SIZE 512
+
+/* Channels on box are in reverse order */
+static unsigned long G_chnmap[MAX_CHANNELS] = {3, 2, 1, 0};
+
+static int debug;
+static int *s2255_debug = &debug;
+
+static int s2255_start_readpipe(struct s2255_dev *dev);
+static void s2255_stop_readpipe(struct s2255_dev *dev);
+static int s2255_start_acquire(struct s2255_channel *channel);
+static int s2255_stop_acquire(struct s2255_channel *channel);
+static void s2255_fillbuff(struct s2255_channel *chn, struct s2255_buffer *buf,
+ int jpgsize);
+static int s2255_set_mode(struct s2255_channel *chan, struct s2255_mode *mode);
+static int s2255_board_shutdown(struct s2255_dev *dev);
+static void s2255_fwload_start(struct s2255_dev *dev, int reset);
+static void s2255_destroy(struct s2255_dev *dev);
+static long s2255_vendor_req(struct s2255_dev *dev, unsigned char req,
+ u16 index, u16 value, void *buf,
+ s32 buf_len, int bOut);
+
+/* dev_err macro with driver name */
+#define S2255_DRIVER_NAME "s2255"
+#define s2255_dev_err(dev, fmt, arg...) \
+ dev_err(dev, S2255_DRIVER_NAME " - " fmt, ##arg)
+
+#define dprintk(level, fmt, arg...) \
+ do { \
+ if (*s2255_debug >= (level)) { \
+ printk(KERN_DEBUG S2255_DRIVER_NAME \
+ ": " fmt, ##arg); \
+ } \
+ } while (0)
+
+static struct usb_driver s2255_driver;
+
+/* Declare static vars that will be used as parameters */
+static unsigned int vid_limit = 16; /* Video memory limit, in Mb */
+
+/* start video number */
+static int video_nr = -1; /* /dev/videoN, -1 for autodetect */
+
+/* Enable jpeg capture. */
+static int jpeg_enable = 1;
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level(0-100) default 0");
+module_param(vid_limit, int, 0644);
+MODULE_PARM_DESC(vid_limit, "video memory limit(Mb)");
+module_param(video_nr, int, 0644);
+MODULE_PARM_DESC(video_nr, "start video minor(-1 default autodetect)");
+module_param(jpeg_enable, int, 0644);
+MODULE_PARM_DESC(jpeg_enable, "Jpeg enable(1-on 0-off) default 1");
+
+/* USB device table */
+#define USB_SENSORAY_VID 0x1943
+static struct usb_device_id s2255_table[] = {
+ {USB_DEVICE(USB_SENSORAY_VID, 0x2255)},
+ {USB_DEVICE(USB_SENSORAY_VID, 0x2257)}, /*same family as 2255*/
+ { } /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, s2255_table);
+
+#define BUFFER_TIMEOUT msecs_to_jiffies(400)
+
+/* image formats. */
+/* JPEG formats must be defined last to support jpeg_enable parameter */
+static const struct s2255_fmt formats[] = {
+ {
+ .name = "4:2:2, planar, YUV422P",
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .depth = 16
+
+ }, {
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16
+
+ }, {
+ .name = "4:2:2, packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = 16
+ }, {
+ .name = "8bpp GREY",
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .depth = 8
+ }, {
+ .name = "JPG",
+ .fourcc = V4L2_PIX_FMT_JPEG,
+ .depth = 24
+ }, {
+ .name = "MJPG",
+ .fourcc = V4L2_PIX_FMT_MJPEG,
+ .depth = 24
+ }
+};
+
+static int norm_maxw(struct video_device *vdev)
+{
+ return (vdev->current_norm & V4L2_STD_NTSC) ?
+ LINE_SZ_4CIFS_NTSC : LINE_SZ_4CIFS_PAL;
+}
+
+static int norm_maxh(struct video_device *vdev)
+{
+ return (vdev->current_norm & V4L2_STD_NTSC) ?
+ (NUM_LINES_1CIFS_NTSC * 2) : (NUM_LINES_1CIFS_PAL * 2);
+}
+
+static int norm_minw(struct video_device *vdev)
+{
+ return (vdev->current_norm & V4L2_STD_NTSC) ?
+ LINE_SZ_1CIFS_NTSC : LINE_SZ_1CIFS_PAL;
+}
+
+static int norm_minh(struct video_device *vdev)
+{
+ return (vdev->current_norm & V4L2_STD_NTSC) ?
+ (NUM_LINES_1CIFS_NTSC) : (NUM_LINES_1CIFS_PAL);
+}
+
+
+/*
+ * TODO: fixme: move YUV reordering to hardware
+ * converts 2255 planar format to yuyv or uyvy
+ */
+static void planar422p_to_yuv_packed(const unsigned char *in,
+ unsigned char *out,
+ int width, int height,
+ int fmt)
+{
+ unsigned char *pY;
+ unsigned char *pCb;
+ unsigned char *pCr;
+ unsigned long size = height * width;
+ unsigned int i;
+ pY = (unsigned char *)in;
+ pCr = (unsigned char *)in + height * width;
+ pCb = (unsigned char *)in + height * width + (height * width / 2);
+ for (i = 0; i < size * 2; i += 4) {
+ out[i] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCr++;
+ out[i + 1] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCr++ : *pY++;
+ out[i + 2] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCb++;
+ out[i + 3] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCb++ : *pY++;
+ }
+ return;
+}
+
+static void s2255_reset_dsppower(struct s2255_dev *dev)
+{
+ s2255_vendor_req(dev, 0x40, 0x0000, 0x0001, NULL, 0, 1);
+ msleep(10);
+ s2255_vendor_req(dev, 0x50, 0x0000, 0x0000, NULL, 0, 1);
+ msleep(600);
+ s2255_vendor_req(dev, 0x10, 0x0000, 0x0000, NULL, 0, 1);
+ return;
+}
+
+/* kickstarts the firmware loading. from probe
+ */
+static void s2255_timer(unsigned long user_data)
+{
+ struct s2255_fw *data = (struct s2255_fw *)user_data;
+ dprintk(100, "%s\n", __func__);
+ if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) {
+ printk(KERN_ERR "s2255: can't submit urb\n");
+ atomic_set(&data->fw_state, S2255_FW_FAILED);
+ /* wake up anything waiting for the firmware */
+ wake_up(&data->wait_fw);
+ return;
+ }
+}
+
+
+/* this loads the firmware asynchronously.
+ Originally this was done synchroously in probe.
+ But it is better to load it asynchronously here than block
+ inside the probe function. Blocking inside probe affects boot time.
+ FW loading is triggered by the timer in the probe function
+*/
+static void s2255_fwchunk_complete(struct urb *urb)
+{
+ struct s2255_fw *data = urb->context;
+ struct usb_device *udev = urb->dev;
+ int len;
+ dprintk(100, "%s: udev %p urb %p", __func__, udev, urb);
+ if (urb->status) {
+ dev_err(&udev->dev, "URB failed with status %d\n", urb->status);
+ atomic_set(&data->fw_state, S2255_FW_FAILED);
+ /* wake up anything waiting for the firmware */
+ wake_up(&data->wait_fw);
+ return;
+ }
+ if (data->fw_urb == NULL) {
+ s2255_dev_err(&udev->dev, "disconnected\n");
+ atomic_set(&data->fw_state, S2255_FW_FAILED);
+ /* wake up anything waiting for the firmware */
+ wake_up(&data->wait_fw);
+ return;
+ }
+#define CHUNK_SIZE 512
+ /* all USB transfers must be done with continuous kernel memory.
+ can't allocate more than 128k in current linux kernel, so
+ upload the firmware in chunks
+ */
+ if (data->fw_loaded < data->fw_size) {
+ len = (data->fw_loaded + CHUNK_SIZE) > data->fw_size ?
+ data->fw_size % CHUNK_SIZE : CHUNK_SIZE;
+
+ if (len < CHUNK_SIZE)
+ memset(data->pfw_data, 0, CHUNK_SIZE);
+
+ dprintk(100, "completed len %d, loaded %d \n", len,
+ data->fw_loaded);
+
+ memcpy(data->pfw_data,
+ (char *) data->fw->data + data->fw_loaded, len);
+
+ usb_fill_bulk_urb(data->fw_urb, udev, usb_sndbulkpipe(udev, 2),
+ data->pfw_data, CHUNK_SIZE,
+ s2255_fwchunk_complete, data);
+ if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) {
+ dev_err(&udev->dev, "failed submit URB\n");
+ atomic_set(&data->fw_state, S2255_FW_FAILED);
+ /* wake up anything waiting for the firmware */
+ wake_up(&data->wait_fw);
+ return;
+ }
+ data->fw_loaded += len;
+ } else {
+ atomic_set(&data->fw_state, S2255_FW_LOADED_DSPWAIT);
+ dprintk(100, "%s: firmware upload complete\n", __func__);
+ }
+ return;
+
+}
+
+static int s2255_got_frame(struct s2255_channel *channel, int jpgsize)
+{
+ struct s2255_dmaqueue *dma_q = &channel->vidq;
+ struct s2255_buffer *buf;
+ struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
+ unsigned long flags = 0;
+ int rc = 0;
+ spin_lock_irqsave(&dev->slock, flags);
+ if (list_empty(&dma_q->active)) {
+ dprintk(1, "No active queue to serve\n");
+ rc = -1;
+ goto unlock;
+ }
+ buf = list_entry(dma_q->active.next,
+ struct s2255_buffer, vb.queue);
+ list_del(&buf->vb.queue);
+ do_gettimeofday(&buf->vb.ts);
+ s2255_fillbuff(channel, buf, jpgsize);
+ wake_up(&buf->vb.done);
+ dprintk(2, "%s: [buf/i] [%p/%d]\n", __func__, buf, buf->vb.i);
+unlock:
+ spin_unlock_irqrestore(&dev->slock, flags);
+ return rc;
+}
+
+static const struct s2255_fmt *format_by_fourcc(int fourcc)
+{
+ unsigned int i;
+ for (i = 0; i < ARRAY_SIZE(formats); i++) {
+ if (-1 == formats[i].fourcc)
+ continue;
+ if (!jpeg_enable && ((formats[i].fourcc == V4L2_PIX_FMT_JPEG) ||
+ (formats[i].fourcc == V4L2_PIX_FMT_MJPEG)))
+ continue;
+ if (formats[i].fourcc == fourcc)
+ return formats + i;
+ }
+ return NULL;
+}
+
+/* video buffer vmalloc implementation based partly on VIVI driver which is
+ * Copyright (c) 2006 by
+ * Mauro Carvalho Chehab <mchehab--a.t--infradead.org>
+ * Ted Walther <ted--a.t--enumera.com>
+ * John Sokol <sokol--a.t--videotechnology.com>
+ * http://v4l.videotechnology.com/
+ *
+ */
+static void s2255_fillbuff(struct s2255_channel *channel,
+ struct s2255_buffer *buf, int jpgsize)
+{
+ int pos = 0;
+ struct timeval ts;
+ const char *tmpbuf;
+ char *vbuf = videobuf_to_vmalloc(&buf->vb);
+ unsigned long last_frame;
+ struct s2255_framei *frm;
+
+ if (!vbuf)
+ return;
+ last_frame = channel->last_frame;
+ if (last_frame != -1) {
+ frm = &channel->buffer.frame[last_frame];
+ tmpbuf =
+ (const char *)channel->buffer.frame[last_frame].lpvbits;
+ switch (buf->fmt->fourcc) {
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_UYVY:
+ planar422p_to_yuv_packed((const unsigned char *)tmpbuf,
+ vbuf, buf->vb.width,
+ buf->vb.height,
+ buf->fmt->fourcc);
+ break;
+ case V4L2_PIX_FMT_GREY:
+ memcpy(vbuf, tmpbuf, buf->vb.width * buf->vb.height);
+ break;
+ case V4L2_PIX_FMT_JPEG:
+ case V4L2_PIX_FMT_MJPEG:
+ buf->vb.size = jpgsize;
+ memcpy(vbuf, tmpbuf, buf->vb.size);
+ break;
+ case V4L2_PIX_FMT_YUV422P:
+ memcpy(vbuf, tmpbuf,
+ buf->vb.width * buf->vb.height * 2);
+ break;
+ default:
+ printk(KERN_DEBUG "s2255: unknown format?\n");
+ }
+ channel->last_frame = -1;
+ } else {
+ printk(KERN_ERR "s2255: =======no frame\n");
+ return;
+
+ }
+ dprintk(2, "s2255fill at : Buffer 0x%08lx size= %d\n",
+ (unsigned long)vbuf, pos);
+ /* tell v4l buffer was filled */
+
+ buf->vb.field_count = channel->frame_count * 2;
+ do_gettimeofday(&ts);
+ buf->vb.ts = ts;
+ buf->vb.state = VIDEOBUF_DONE;
+}
+
+
+/* ------------------------------------------------------------------
+ Videobuf operations
+ ------------------------------------------------------------------*/
+
+static int buffer_setup(struct videobuf_queue *vq, unsigned int *count,
+ unsigned int *size)
+{
+ struct s2255_fh *fh = vq->priv_data;
+ struct s2255_channel *channel = fh->channel;
+ *size = channel->width * channel->height * (channel->fmt->depth >> 3);
+
+ if (0 == *count)
+ *count = S2255_DEF_BUFS;
+
+ if (*size * *count > vid_limit * 1024 * 1024)
+ *count = (vid_limit * 1024 * 1024) / *size;
+
+ return 0;
+}
+
+static void free_buffer(struct videobuf_queue *vq, struct s2255_buffer *buf)
+{
+ dprintk(4, "%s\n", __func__);
+
+ videobuf_vmalloc_free(&buf->vb);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct s2255_fh *fh = vq->priv_data;
+ struct s2255_channel *channel = fh->channel;
+ struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb);
+ int rc;
+ int w = channel->width;
+ int h = channel->height;
+ dprintk(4, "%s, field=%d\n", __func__, field);
+ if (channel->fmt == NULL)
+ return -EINVAL;
+
+ if ((w < norm_minw(&channel->vdev)) ||
+ (w > norm_maxw(&channel->vdev)) ||
+ (h < norm_minh(&channel->vdev)) ||
+ (h > norm_maxh(&channel->vdev))) {
+ dprintk(4, "invalid buffer prepare\n");
+ return -EINVAL;
+ }
+ buf->vb.size = w * h * (channel->fmt->depth >> 3);
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) {
+ dprintk(4, "invalid buffer prepare\n");
+ return -EINVAL;
+ }
+
+ buf->fmt = channel->fmt;
+ buf->vb.width = w;
+ buf->vb.height = h;
+ buf->vb.field = field;
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ rc = videobuf_iolock(vq, &buf->vb, NULL);
+ if (rc < 0)
+ goto fail;
+ }
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+fail:
+ free_buffer(vq, buf);
+ return rc;
+}
+
+static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb);
+ struct s2255_fh *fh = vq->priv_data;
+ struct s2255_channel *channel = fh->channel;
+ struct s2255_dmaqueue *vidq = &channel->vidq;
+ dprintk(1, "%s\n", __func__);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ list_add_tail(&buf->vb.queue, &vidq->active);
+}
+
+static void buffer_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb);
+ struct s2255_fh *fh = vq->priv_data;
+ dprintk(4, "%s %d\n", __func__, fh->channel->idx);
+ free_buffer(vq, buf);
+}
+
+static struct videobuf_queue_ops s2255_video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+
+static int res_get(struct s2255_fh *fh)
+{
+ struct s2255_channel *channel = fh->channel;
+ /* is it free? */
+ if (channel->resources)
+ return 0; /* no, someone else uses it */
+ /* it's free, grab it */
+ channel->resources = 1;
+ fh->resources = 1;
+ dprintk(1, "s2255: res: get\n");
+ return 1;
+}
+
+static int res_locked(struct s2255_fh *fh)
+{
+ return fh->channel->resources;
+}
+
+static int res_check(struct s2255_fh *fh)
+{
+ return fh->resources;
+}
+
+
+static void res_free(struct s2255_fh *fh)
+{
+ struct s2255_channel *channel = fh->channel;
+ channel->resources = 0;
+ fh->resources = 0;
+ dprintk(1, "res: put\n");
+}
+
+static int vidioc_querymenu(struct file *file, void *priv,
+ struct v4l2_querymenu *qmenu)
+{
+ static const char *colorfilter[] = {
+ "Off",
+ "On",
+ NULL
+ };
+ if (qmenu->id == V4L2_CID_PRIVATE_COLORFILTER) {
+ int i;
+ const char **menu_items = colorfilter;
+ for (i = 0; i < qmenu->index && menu_items[i]; i++)
+ ; /* do nothing (from v4l2-common.c) */
+ if (menu_items[i] == NULL || menu_items[i][0] == '\0')
+ return -EINVAL;
+ strlcpy(qmenu->name, menu_items[qmenu->index],
+ sizeof(qmenu->name));
+ return 0;
+ }
+ return v4l2_ctrl_query_menu(qmenu, NULL, NULL);
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct s2255_fh *fh = file->private_data;
+ struct s2255_dev *dev = fh->dev;
+ strlcpy(cap->driver, "s2255", sizeof(cap->driver));
+ strlcpy(cap->card, "s2255", sizeof(cap->card));
+ usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ int index = f->index;
+
+ if (index >= ARRAY_SIZE(formats))
+ return -EINVAL;
+ if (!jpeg_enable && ((formats[index].fourcc == V4L2_PIX_FMT_JPEG) ||
+ (formats[index].fourcc == V4L2_PIX_FMT_MJPEG)))
+ return -EINVAL;
+ dprintk(4, "name %s\n", formats[index].name);
+ strlcpy(f->description, formats[index].name, sizeof(f->description));
+ f->pixelformat = formats[index].fourcc;
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct s2255_fh *fh = priv;
+ struct s2255_channel *channel = fh->channel;
+
+ f->fmt.pix.width = channel->width;
+ f->fmt.pix.height = channel->height;
+ f->fmt.pix.field = fh->vb_vidq.field;
+ f->fmt.pix.pixelformat = channel->fmt->fourcc;
+ f->fmt.pix.bytesperline = f->fmt.pix.width * (channel->fmt->depth >> 3);
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ const struct s2255_fmt *fmt;
+ enum v4l2_field field;
+ int b_any_field = 0;
+ struct s2255_fh *fh = priv;
+ struct s2255_channel *channel = fh->channel;
+ int is_ntsc;
+ is_ntsc =
+ (channel->vdev.current_norm & V4L2_STD_NTSC) ? 1 : 0;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+
+ if (fmt == NULL)
+ return -EINVAL;
+
+ field = f->fmt.pix.field;
+ if (field == V4L2_FIELD_ANY)
+ b_any_field = 1;
+
+ dprintk(50, "%s NTSC: %d suggested width: %d, height: %d\n",
+ __func__, is_ntsc, f->fmt.pix.width, f->fmt.pix.height);
+ if (is_ntsc) {
+ /* NTSC */
+ if (f->fmt.pix.height >= NUM_LINES_1CIFS_NTSC * 2) {
+ f->fmt.pix.height = NUM_LINES_1CIFS_NTSC * 2;
+ if (b_any_field) {
+ field = V4L2_FIELD_SEQ_TB;
+ } else if (!((field == V4L2_FIELD_INTERLACED) ||
+ (field == V4L2_FIELD_SEQ_TB) ||
+ (field == V4L2_FIELD_INTERLACED_TB))) {
+ dprintk(1, "unsupported field setting\n");
+ return -EINVAL;
+ }
+ } else {
+ f->fmt.pix.height = NUM_LINES_1CIFS_NTSC;
+ if (b_any_field) {
+ field = V4L2_FIELD_TOP;
+ } else if (!((field == V4L2_FIELD_TOP) ||
+ (field == V4L2_FIELD_BOTTOM))) {
+ dprintk(1, "unsupported field setting\n");
+ return -EINVAL;
+ }
+
+ }
+ if (f->fmt.pix.width >= LINE_SZ_4CIFS_NTSC)
+ f->fmt.pix.width = LINE_SZ_4CIFS_NTSC;
+ else if (f->fmt.pix.width >= LINE_SZ_2CIFS_NTSC)
+ f->fmt.pix.width = LINE_SZ_2CIFS_NTSC;
+ else if (f->fmt.pix.width >= LINE_SZ_1CIFS_NTSC)
+ f->fmt.pix.width = LINE_SZ_1CIFS_NTSC;
+ else
+ f->fmt.pix.width = LINE_SZ_1CIFS_NTSC;
+ } else {
+ /* PAL */
+ if (f->fmt.pix.height >= NUM_LINES_1CIFS_PAL * 2) {
+ f->fmt.pix.height = NUM_LINES_1CIFS_PAL * 2;
+ if (b_any_field) {
+ field = V4L2_FIELD_SEQ_TB;
+ } else if (!((field == V4L2_FIELD_INTERLACED) ||
+ (field == V4L2_FIELD_SEQ_TB) ||
+ (field == V4L2_FIELD_INTERLACED_TB))) {
+ dprintk(1, "unsupported field setting\n");
+ return -EINVAL;
+ }
+ } else {
+ f->fmt.pix.height = NUM_LINES_1CIFS_PAL;
+ if (b_any_field) {
+ field = V4L2_FIELD_TOP;
+ } else if (!((field == V4L2_FIELD_TOP) ||
+ (field == V4L2_FIELD_BOTTOM))) {
+ dprintk(1, "unsupported field setting\n");
+ return -EINVAL;
+ }
+ }
+ if (f->fmt.pix.width >= LINE_SZ_4CIFS_PAL) {
+ f->fmt.pix.width = LINE_SZ_4CIFS_PAL;
+ field = V4L2_FIELD_SEQ_TB;
+ } else if (f->fmt.pix.width >= LINE_SZ_2CIFS_PAL) {
+ f->fmt.pix.width = LINE_SZ_2CIFS_PAL;
+ field = V4L2_FIELD_TOP;
+ } else if (f->fmt.pix.width >= LINE_SZ_1CIFS_PAL) {
+ f->fmt.pix.width = LINE_SZ_1CIFS_PAL;
+ field = V4L2_FIELD_TOP;
+ } else {
+ f->fmt.pix.width = LINE_SZ_1CIFS_PAL;
+ field = V4L2_FIELD_TOP;
+ }
+ }
+ f->fmt.pix.field = field;
+ f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+ dprintk(50, "%s: set width %d height %d field %d\n", __func__,
+ f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct s2255_fh *fh = priv;
+ struct s2255_channel *channel = fh->channel;
+ const struct s2255_fmt *fmt;
+ struct videobuf_queue *q = &fh->vb_vidq;
+ struct s2255_mode mode;
+ int ret;
+ int norm;
+
+ ret = vidioc_try_fmt_vid_cap(file, fh, f);
+
+ if (ret < 0)
+ return ret;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+
+ if (fmt == NULL)
+ return -EINVAL;
+
+ mutex_lock(&q->vb_lock);
+
+ if (videobuf_queue_is_busy(&fh->vb_vidq)) {
+ dprintk(1, "queue busy\n");
+ ret = -EBUSY;
+ goto out_s_fmt;
+ }
+
+ if (res_locked(fh)) {
+ dprintk(1, "%s: channel busy\n", __func__);
+ ret = -EBUSY;
+ goto out_s_fmt;
+ }
+ mode = channel->mode;
+ channel->fmt = fmt;
+ channel->width = f->fmt.pix.width;
+ channel->height = f->fmt.pix.height;
+ fh->vb_vidq.field = f->fmt.pix.field;
+ fh->type = f->type;
+ norm = norm_minw(&channel->vdev);
+ if (channel->width > norm_minw(&channel->vdev)) {
+ if (channel->height > norm_minh(&channel->vdev)) {
+ if (channel->cap_parm.capturemode &
+ V4L2_MODE_HIGHQUALITY)
+ mode.scale = SCALE_4CIFSI;
+ else
+ mode.scale = SCALE_4CIFS;
+ } else
+ mode.scale = SCALE_2CIFS;
+
+ } else {
+ mode.scale = SCALE_1CIFS;
+ }
+ /* color mode */
+ switch (channel->fmt->fourcc) {
+ case V4L2_PIX_FMT_GREY:
+ mode.color &= ~MASK_COLOR;
+ mode.color |= COLOR_Y8;
+ break;
+ case V4L2_PIX_FMT_JPEG:
+ case V4L2_PIX_FMT_MJPEG:
+ mode.color &= ~MASK_COLOR;
+ mode.color |= COLOR_JPG;
+ mode.color |= (channel->jc.quality << 8);
+ break;
+ case V4L2_PIX_FMT_YUV422P:
+ mode.color &= ~MASK_COLOR;
+ mode.color |= COLOR_YUVPL;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_UYVY:
+ default:
+ mode.color &= ~MASK_COLOR;
+ mode.color |= COLOR_YUVPK;
+ break;
+ }
+ if ((mode.color & MASK_COLOR) != (channel->mode.color & MASK_COLOR))
+ mode.restart = 1;
+ else if (mode.scale != channel->mode.scale)
+ mode.restart = 1;
+ else if (mode.format != channel->mode.format)
+ mode.restart = 1;
+ channel->mode = mode;
+ (void) s2255_set_mode(channel, &mode);
+ ret = 0;
+out_s_fmt:
+ mutex_unlock(&q->vb_lock);
+ return ret;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ int rc;
+ struct s2255_fh *fh = priv;
+ rc = videobuf_reqbufs(&fh->vb_vidq, p);
+ return rc;
+}
+
+static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ int rc;
+ struct s2255_fh *fh = priv;
+ rc = videobuf_querybuf(&fh->vb_vidq, p);
+ return rc;
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ int rc;
+ struct s2255_fh *fh = priv;
+ rc = videobuf_qbuf(&fh->vb_vidq, p);
+ return rc;
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ int rc;
+ struct s2255_fh *fh = priv;
+ rc = videobuf_dqbuf(&fh->vb_vidq, p, file->f_flags & O_NONBLOCK);
+ return rc;
+}
+
+/* write to the configuration pipe, synchronously */
+static int s2255_write_config(struct usb_device *udev, unsigned char *pbuf,
+ int size)
+{
+ int pipe;
+ int done;
+ long retval = -1;
+ if (udev) {
+ pipe = usb_sndbulkpipe(udev, S2255_CONFIG_EP);
+ retval = usb_bulk_msg(udev, pipe, pbuf, size, &done, 500);
+ }
+ return retval;
+}
+
+static u32 get_transfer_size(struct s2255_mode *mode)
+{
+ int linesPerFrame = LINE_SZ_DEF;
+ int pixelsPerLine = NUM_LINES_DEF;
+ u32 outImageSize;
+ u32 usbInSize;
+ unsigned int mask_mult;
+
+ if (mode == NULL)
+ return 0;
+
+ if (mode->format == FORMAT_NTSC) {
+ switch (mode->scale) {
+ case SCALE_4CIFS:
+ case SCALE_4CIFSI:
+ linesPerFrame = NUM_LINES_4CIFS_NTSC * 2;
+ pixelsPerLine = LINE_SZ_4CIFS_NTSC;
+ break;
+ case SCALE_2CIFS:
+ linesPerFrame = NUM_LINES_2CIFS_NTSC;
+ pixelsPerLine = LINE_SZ_2CIFS_NTSC;
+ break;
+ case SCALE_1CIFS:
+ linesPerFrame = NUM_LINES_1CIFS_NTSC;
+ pixelsPerLine = LINE_SZ_1CIFS_NTSC;
+ break;
+ default:
+ break;
+ }
+ } else if (mode->format == FORMAT_PAL) {
+ switch (mode->scale) {
+ case SCALE_4CIFS:
+ case SCALE_4CIFSI:
+ linesPerFrame = NUM_LINES_4CIFS_PAL * 2;
+ pixelsPerLine = LINE_SZ_4CIFS_PAL;
+ break;
+ case SCALE_2CIFS:
+ linesPerFrame = NUM_LINES_2CIFS_PAL;
+ pixelsPerLine = LINE_SZ_2CIFS_PAL;
+ break;
+ case SCALE_1CIFS:
+ linesPerFrame = NUM_LINES_1CIFS_PAL;
+ pixelsPerLine = LINE_SZ_1CIFS_PAL;
+ break;
+ default:
+ break;
+ }
+ }
+ outImageSize = linesPerFrame * pixelsPerLine;
+ if ((mode->color & MASK_COLOR) != COLOR_Y8) {
+ /* 2 bytes/pixel if not monochrome */
+ outImageSize *= 2;
+ }
+
+ /* total bytes to send including prefix and 4K padding;
+ must be a multiple of USB_READ_SIZE */
+ usbInSize = outImageSize + PREFIX_SIZE; /* always send prefix */
+ mask_mult = 0xffffffffUL - DEF_USB_BLOCK + 1;
+ /* if size not a multiple of USB_READ_SIZE */
+ if (usbInSize & ~mask_mult)
+ usbInSize = (usbInSize & mask_mult) + (DEF_USB_BLOCK);
+ return usbInSize;
+}
+
+static void s2255_print_cfg(struct s2255_dev *sdev, struct s2255_mode *mode)
+{
+ struct device *dev = &sdev->udev->dev;
+ dev_info(dev, "------------------------------------------------\n");
+ dev_info(dev, "format: %d\nscale %d\n", mode->format, mode->scale);
+ dev_info(dev, "fdec: %d\ncolor %d\n", mode->fdec, mode->color);
+ dev_info(dev, "bright: 0x%x\n", mode->bright);
+ dev_info(dev, "------------------------------------------------\n");
+}
+
+/*
+ * set mode is the function which controls the DSP.
+ * the restart parameter in struct s2255_mode should be set whenever
+ * the image size could change via color format, video system or image
+ * size.
+ * When the restart parameter is set, we sleep for ONE frame to allow the
+ * DSP time to get the new frame
+ */
+static int s2255_set_mode(struct s2255_channel *channel,
+ struct s2255_mode *mode)
+{
+ int res;
+ __le32 *buffer;
+ unsigned long chn_rev;
+ struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
+ chn_rev = G_chnmap[channel->idx];
+ dprintk(3, "%s channel: %d\n", __func__, channel->idx);
+ /* if JPEG, set the quality */
+ if ((mode->color & MASK_COLOR) == COLOR_JPG) {
+ mode->color &= ~MASK_COLOR;
+ mode->color |= COLOR_JPG;
+ mode->color &= ~MASK_JPG_QUALITY;
+ mode->color |= (channel->jc.quality << 8);
+ }
+ /* save the mode */
+ channel->mode = *mode;
+ channel->req_image_size = get_transfer_size(mode);
+ dprintk(1, "%s: reqsize %ld\n", __func__, channel->req_image_size);
+ buffer = kzalloc(512, GFP_KERNEL);
+ if (buffer == NULL) {
+ dev_err(&dev->udev->dev, "out of mem\n");
+ return -ENOMEM;
+ }
+ /* set the mode */
+ buffer[0] = IN_DATA_TOKEN;
+ buffer[1] = (__le32) cpu_to_le32(chn_rev);
+ buffer[2] = CMD_SET_MODE;
+ memcpy(&buffer[3], &channel->mode, sizeof(struct s2255_mode));
+ channel->setmode_ready = 0;
+ res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
+ if (debug)
+ s2255_print_cfg(dev, mode);
+ kfree(buffer);
+ /* wait at least 3 frames before continuing */
+ if (mode->restart) {
+ wait_event_timeout(channel->wait_setmode,
+ (channel->setmode_ready != 0),
+ msecs_to_jiffies(S2255_SETMODE_TIMEOUT));
+ if (channel->setmode_ready != 1) {
+ printk(KERN_DEBUG "s2255: no set mode response\n");
+ res = -EFAULT;
+ }
+ }
+ /* clear the restart flag */
+ channel->mode.restart = 0;
+ dprintk(1, "%s chn %d, result: %d\n", __func__, channel->idx, res);
+ return res;
+}
+
+static int s2255_cmd_status(struct s2255_channel *channel, u32 *pstatus)
+{
+ int res;
+ __le32 *buffer;
+ u32 chn_rev;
+ struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
+ chn_rev = G_chnmap[channel->idx];
+ dprintk(4, "%s chan %d\n", __func__, channel->idx);
+ buffer = kzalloc(512, GFP_KERNEL);
+ if (buffer == NULL) {
+ dev_err(&dev->udev->dev, "out of mem\n");
+ return -ENOMEM;
+ }
+ /* form the get vid status command */
+ buffer[0] = IN_DATA_TOKEN;
+ buffer[1] = (__le32) cpu_to_le32(chn_rev);
+ buffer[2] = CMD_STATUS;
+ *pstatus = 0;
+ channel->vidstatus_ready = 0;
+ res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
+ kfree(buffer);
+ wait_event_timeout(channel->wait_vidstatus,
+ (channel->vidstatus_ready != 0),
+ msecs_to_jiffies(S2255_VIDSTATUS_TIMEOUT));
+ if (channel->vidstatus_ready != 1) {
+ printk(KERN_DEBUG "s2255: no vidstatus response\n");
+ res = -EFAULT;
+ }
+ *pstatus = channel->vidstatus;
+ dprintk(4, "%s, vid status %d\n", __func__, *pstatus);
+ return res;
+}
+
+static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ int res;
+ struct s2255_fh *fh = priv;
+ struct s2255_dev *dev = fh->dev;
+ struct s2255_channel *channel = fh->channel;
+ int j;
+ dprintk(4, "%s\n", __func__);
+ if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ dev_err(&dev->udev->dev, "invalid fh type0\n");
+ return -EINVAL;
+ }
+ if (i != fh->type) {
+ dev_err(&dev->udev->dev, "invalid fh type1\n");
+ return -EINVAL;
+ }
+
+ if (!res_get(fh)) {
+ s2255_dev_err(&dev->udev->dev, "stream busy\n");
+ return -EBUSY;
+ }
+ channel->last_frame = -1;
+ channel->bad_payload = 0;
+ channel->cur_frame = 0;
+ channel->frame_count = 0;
+ for (j = 0; j < SYS_FRAMES; j++) {
+ channel->buffer.frame[j].ulState = S2255_READ_IDLE;
+ channel->buffer.frame[j].cur_size = 0;
+ }
+ res = videobuf_streamon(&fh->vb_vidq);
+ if (res == 0) {
+ s2255_start_acquire(channel);
+ channel->b_acquire = 1;
+ } else
+ res_free(fh);
+
+ return res;
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct s2255_fh *fh = priv;
+ dprintk(4, "%s\n, channel: %d", __func__, fh->channel->idx);
+ if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ printk(KERN_ERR "invalid fh type0\n");
+ return -EINVAL;
+ }
+ if (i != fh->type) {
+ printk(KERN_ERR "invalid type i\n");
+ return -EINVAL;
+ }
+ s2255_stop_acquire(fh->channel);
+ videobuf_streamoff(&fh->vb_vidq);
+ res_free(fh);
+ return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i)
+{
+ struct s2255_fh *fh = priv;
+ struct s2255_mode mode;
+ struct videobuf_queue *q = &fh->vb_vidq;
+ int ret = 0;
+ mutex_lock(&q->vb_lock);
+ if (videobuf_queue_is_busy(q)) {
+ dprintk(1, "queue busy\n");
+ ret = -EBUSY;
+ goto out_s_std;
+ }
+ if (res_locked(fh)) {
+ dprintk(1, "can't change standard after started\n");
+ ret = -EBUSY;
+ goto out_s_std;
+ }
+ mode = fh->channel->mode;
+ if (*i & V4L2_STD_NTSC) {
+ dprintk(4, "%s NTSC\n", __func__);
+ /* if changing format, reset frame decimation/intervals */
+ if (mode.format != FORMAT_NTSC) {
+ mode.restart = 1;
+ mode.format = FORMAT_NTSC;
+ mode.fdec = FDEC_1;
+ }
+ } else if (*i & V4L2_STD_PAL) {
+ dprintk(4, "%s PAL\n", __func__);
+ if (mode.format != FORMAT_PAL) {
+ mode.restart = 1;
+ mode.format = FORMAT_PAL;
+ mode.fdec = FDEC_1;
+ }
+ } else {
+ ret = -EINVAL;
+ }
+ if (mode.restart)
+ s2255_set_mode(fh->channel, &mode);
+out_s_std:
+ mutex_unlock(&q->vb_lock);
+ return ret;
+}
+
+/* Sensoray 2255 is a multiple channel capture device.
+ It does not have a "crossbar" of inputs.
+ We use one V4L device per channel. The user must
+ be aware that certain combinations are not allowed.
+ For instance, you cannot do full FPS on more than 2 channels(2 videodevs)
+ at once in color(you can do full fps on 4 channels with greyscale.
+*/
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *inp)
+{
+ struct s2255_fh *fh = priv;
+ struct s2255_dev *dev = fh->dev;
+ struct s2255_channel *channel = fh->channel;
+ u32 status = 0;
+ if (inp->index != 0)
+ return -EINVAL;
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ inp->std = S2255_NORMS;
+ inp->status = 0;
+ if (dev->dsp_fw_ver >= S2255_MIN_DSP_STATUS) {
+ int rc;
+ rc = s2255_cmd_status(fh->channel, &status);
+ dprintk(4, "s2255_cmd_status rc: %d status %x\n", rc, status);
+ if (rc == 0)
+ inp->status = (status & 0x01) ? 0
+ : V4L2_IN_ST_NO_SIGNAL;
+ }
+ switch (dev->pid) {
+ case 0x2255:
+ default:
+ strlcpy(inp->name, "Composite", sizeof(inp->name));
+ break;
+ case 0x2257:
+ strlcpy(inp->name, (channel->idx < 2) ? "Composite" : "S-Video",
+ sizeof(inp->name));
+ break;
+ }
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ if (i > 0)
+ return -EINVAL;
+ return 0;
+}
+
+/* --- controls ---------------------------------------------- */
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ struct s2255_fh *fh = priv;
+ struct s2255_channel *channel = fh->channel;
+ struct s2255_dev *dev = fh->dev;
+ switch (qc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ v4l2_ctrl_query_fill(qc, -127, 127, 1, DEF_BRIGHT);
+ break;
+ case V4L2_CID_CONTRAST:
+ v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_CONTRAST);
+ break;
+ case V4L2_CID_SATURATION:
+ v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_SATURATION);
+ break;
+ case V4L2_CID_HUE:
+ v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_HUE);
+ break;
+ case V4L2_CID_PRIVATE_COLORFILTER:
+ if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER)
+ return -EINVAL;
+ if ((dev->pid == 0x2257) && (channel->idx > 1))
+ return -EINVAL;
+ strlcpy(qc->name, "Color Filter", sizeof(qc->name));
+ qc->type = V4L2_CTRL_TYPE_MENU;
+ qc->minimum = 0;
+ qc->maximum = 1;
+ qc->step = 1;
+ qc->default_value = 1;
+ qc->flags = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ dprintk(4, "%s, id %d\n", __func__, qc->id);
+ return 0;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct s2255_fh *fh = priv;
+ struct s2255_dev *dev = fh->dev;
+ struct s2255_channel *channel = fh->channel;
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = channel->mode.bright;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->value = channel->mode.contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ ctrl->value = channel->mode.saturation;
+ break;
+ case V4L2_CID_HUE:
+ ctrl->value = channel->mode.hue;
+ break;
+ case V4L2_CID_PRIVATE_COLORFILTER:
+ if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER)
+ return -EINVAL;
+ if ((dev->pid == 0x2257) && (channel->idx > 1))
+ return -EINVAL;
+ ctrl->value = !((channel->mode.color & MASK_INPUT_TYPE) >> 16);
+ break;
+ default:
+ return -EINVAL;
+ }
+ dprintk(4, "%s, id %d val %d\n", __func__, ctrl->id, ctrl->value);
+ return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct s2255_fh *fh = priv;
+ struct s2255_channel *channel = fh->channel;
+ struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
+ struct s2255_mode mode;
+ mode = channel->mode;
+ dprintk(4, "%s\n", __func__);
+ /* update the mode to the corresponding value */
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ mode.bright = ctrl->value;
+ break;
+ case V4L2_CID_CONTRAST:
+ mode.contrast = ctrl->value;
+ break;
+ case V4L2_CID_HUE:
+ mode.hue = ctrl->value;
+ break;
+ case V4L2_CID_SATURATION:
+ mode.saturation = ctrl->value;
+ break;
+ case V4L2_CID_PRIVATE_COLORFILTER:
+ if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER)
+ return -EINVAL;
+ if ((dev->pid == 0x2257) && (channel->idx > 1))
+ return -EINVAL;
+ mode.color &= ~MASK_INPUT_TYPE;
+ mode.color |= ((ctrl->value ? 0 : 1) << 16);
+ break;
+ default:
+ return -EINVAL;
+ }
+ mode.restart = 0;
+ /* set mode here. Note: stream does not need restarted.
+ some V4L programs restart stream unnecessarily
+ after a s_crtl.
+ */
+ s2255_set_mode(fh->channel, &mode);
+ return 0;
+}
+
+static int vidioc_g_jpegcomp(struct file *file, void *priv,
+ struct v4l2_jpegcompression *jc)
+{
+ struct s2255_fh *fh = priv;
+ struct s2255_channel *channel = fh->channel;
+ *jc = channel->jc;
+ dprintk(2, "%s: quality %d\n", __func__, jc->quality);
+ return 0;
+}
+
+static int vidioc_s_jpegcomp(struct file *file, void *priv,
+ struct v4l2_jpegcompression *jc)
+{
+ struct s2255_fh *fh = priv;
+ struct s2255_channel *channel = fh->channel;
+ if (jc->quality < 0 || jc->quality > 100)
+ return -EINVAL;
+ channel->jc.quality = jc->quality;
+ dprintk(2, "%s: quality %d\n", __func__, jc->quality);
+ return 0;
+}
+
+static int vidioc_g_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *sp)
+{
+ struct s2255_fh *fh = priv;
+ __u32 def_num, def_dem;
+ struct s2255_channel *channel = fh->channel;
+ if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ memset(sp, 0, sizeof(struct v4l2_streamparm));
+ sp->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ sp->parm.capture.capturemode = channel->cap_parm.capturemode;
+ def_num = (channel->mode.format == FORMAT_NTSC) ? 1001 : 1000;
+ def_dem = (channel->mode.format == FORMAT_NTSC) ? 30000 : 25000;
+ sp->parm.capture.timeperframe.denominator = def_dem;
+ switch (channel->mode.fdec) {
+ default:
+ case FDEC_1:
+ sp->parm.capture.timeperframe.numerator = def_num;
+ break;
+ case FDEC_2:
+ sp->parm.capture.timeperframe.numerator = def_num * 2;
+ break;
+ case FDEC_3:
+ sp->parm.capture.timeperframe.numerator = def_num * 3;
+ break;
+ case FDEC_5:
+ sp->parm.capture.timeperframe.numerator = def_num * 5;
+ break;
+ }
+ dprintk(4, "%s capture mode, %d timeperframe %d/%d\n", __func__,
+ sp->parm.capture.capturemode,
+ sp->parm.capture.timeperframe.numerator,
+ sp->parm.capture.timeperframe.denominator);
+ return 0;
+}
+
+static int vidioc_s_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *sp)
+{
+ struct s2255_fh *fh = priv;
+ struct s2255_channel *channel = fh->channel;
+ struct s2255_mode mode;
+ int fdec = FDEC_1;
+ __u32 def_num, def_dem;
+ if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ mode = channel->mode;
+ /* high quality capture mode requires a stream restart */
+ if (channel->cap_parm.capturemode
+ != sp->parm.capture.capturemode && res_locked(fh))
+ return -EBUSY;
+ def_num = (mode.format == FORMAT_NTSC) ? 1001 : 1000;
+ def_dem = (mode.format == FORMAT_NTSC) ? 30000 : 25000;
+ if (def_dem != sp->parm.capture.timeperframe.denominator)
+ sp->parm.capture.timeperframe.numerator = def_num;
+ else if (sp->parm.capture.timeperframe.numerator <= def_num)
+ sp->parm.capture.timeperframe.numerator = def_num;
+ else if (sp->parm.capture.timeperframe.numerator <= (def_num * 2)) {
+ sp->parm.capture.timeperframe.numerator = def_num * 2;
+ fdec = FDEC_2;
+ } else if (sp->parm.capture.timeperframe.numerator <= (def_num * 3)) {
+ sp->parm.capture.timeperframe.numerator = def_num * 3;
+ fdec = FDEC_3;
+ } else {
+ sp->parm.capture.timeperframe.numerator = def_num * 5;
+ fdec = FDEC_5;
+ }
+ mode.fdec = fdec;
+ sp->parm.capture.timeperframe.denominator = def_dem;
+ s2255_set_mode(channel, &mode);
+ dprintk(4, "%s capture mode, %d timeperframe %d/%d, fdec %d\n",
+ __func__,
+ sp->parm.capture.capturemode,
+ sp->parm.capture.timeperframe.numerator,
+ sp->parm.capture.timeperframe.denominator, fdec);
+ return 0;
+}
+
+static int vidioc_enum_frameintervals(struct file *file, void *priv,
+ struct v4l2_frmivalenum *fe)
+{
+ int is_ntsc = 0;
+#define NUM_FRAME_ENUMS 4
+ int frm_dec[NUM_FRAME_ENUMS] = {1, 2, 3, 5};
+ if (fe->index < 0 || fe->index >= NUM_FRAME_ENUMS)
+ return -EINVAL;
+ switch (fe->width) {
+ case 640:
+ if (fe->height != 240 && fe->height != 480)
+ return -EINVAL;
+ is_ntsc = 1;
+ break;
+ case 320:
+ if (fe->height != 240)
+ return -EINVAL;
+ is_ntsc = 1;
+ break;
+ case 704:
+ if (fe->height != 288 && fe->height != 576)
+ return -EINVAL;
+ break;
+ case 352:
+ if (fe->height != 288)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ fe->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ fe->discrete.denominator = is_ntsc ? 30000 : 25000;
+ fe->discrete.numerator = (is_ntsc ? 1001 : 1000) * frm_dec[fe->index];
+ dprintk(4, "%s discrete %d/%d\n", __func__, fe->discrete.numerator,
+ fe->discrete.denominator);
+ return 0;
+}
+
+static int s2255_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct s2255_channel *channel = video_drvdata(file);
+ struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev);
+ struct s2255_fh *fh;
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ int state;
+ dprintk(1, "s2255: open called (dev=%s)\n",
+ video_device_node_name(vdev));
+ /*
+ * open lock necessary to prevent multiple instances
+ * of v4l-conf (or other programs) from simultaneously
+ * reloading firmware.
+ */
+ mutex_lock(&dev->open_lock);
+ state = atomic_read(&dev->fw_data->fw_state);
+ switch (state) {
+ case S2255_FW_DISCONNECTING:
+ mutex_unlock(&dev->open_lock);
+ return -ENODEV;
+ case S2255_FW_FAILED:
+ s2255_dev_err(&dev->udev->dev,
+ "firmware load failed. retrying.\n");
+ s2255_fwload_start(dev, 1);
+ wait_event_timeout(dev->fw_data->wait_fw,
+ ((atomic_read(&dev->fw_data->fw_state)
+ == S2255_FW_SUCCESS) ||
+ (atomic_read(&dev->fw_data->fw_state)
+ == S2255_FW_DISCONNECTING)),
+ msecs_to_jiffies(S2255_LOAD_TIMEOUT));
+ /* state may have changed, re-read */
+ state = atomic_read(&dev->fw_data->fw_state);
+ break;
+ case S2255_FW_NOTLOADED:
+ case S2255_FW_LOADED_DSPWAIT:
+ /* give S2255_LOAD_TIMEOUT time for firmware to load in case
+ driver loaded and then device immediately opened */
+ printk(KERN_INFO "%s waiting for firmware load\n", __func__);
+ wait_event_timeout(dev->fw_data->wait_fw,
+ ((atomic_read(&dev->fw_data->fw_state)
+ == S2255_FW_SUCCESS) ||
+ (atomic_read(&dev->fw_data->fw_state)
+ == S2255_FW_DISCONNECTING)),
+ msecs_to_jiffies(S2255_LOAD_TIMEOUT));
+ /* state may have changed, re-read */
+ state = atomic_read(&dev->fw_data->fw_state);
+ break;
+ case S2255_FW_SUCCESS:
+ default:
+ break;
+ }
+ /* state may have changed in above switch statement */
+ switch (state) {
+ case S2255_FW_SUCCESS:
+ break;
+ case S2255_FW_FAILED:
+ printk(KERN_INFO "2255 firmware load failed.\n");
+ mutex_unlock(&dev->open_lock);
+ return -ENODEV;
+ case S2255_FW_DISCONNECTING:
+ printk(KERN_INFO "%s: disconnecting\n", __func__);
+ mutex_unlock(&dev->open_lock);
+ return -ENODEV;
+ case S2255_FW_LOADED_DSPWAIT:
+ case S2255_FW_NOTLOADED:
+ printk(KERN_INFO "%s: firmware not loaded yet"
+ "please try again later\n",
+ __func__);
+ /*
+ * Timeout on firmware load means device unusable.
+ * Set firmware failure state.
+ * On next s2255_open the firmware will be reloaded.
+ */
+ atomic_set(&dev->fw_data->fw_state,
+ S2255_FW_FAILED);
+ mutex_unlock(&dev->open_lock);
+ return -EAGAIN;
+ default:
+ printk(KERN_INFO "%s: unknown state\n", __func__);
+ mutex_unlock(&dev->open_lock);
+ return -EFAULT;
+ }
+ mutex_unlock(&dev->open_lock);
+ /* allocate + initialize per filehandle data */
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (NULL == fh)
+ return -ENOMEM;
+ file->private_data = fh;
+ fh->dev = dev;
+ fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ fh->channel = channel;
+ if (!channel->configured) {
+ /* configure channel to default state */
+ channel->fmt = &formats[0];
+ s2255_set_mode(channel, &channel->mode);
+ channel->configured = 1;
+ }
+ dprintk(1, "%s: dev=%s type=%s\n", __func__,
+ video_device_node_name(vdev), v4l2_type_names[type]);
+ dprintk(2, "%s: fh=0x%08lx, dev=0x%08lx, vidq=0x%08lx\n", __func__,
+ (unsigned long)fh, (unsigned long)dev,
+ (unsigned long)&channel->vidq);
+ dprintk(4, "%s: list_empty active=%d\n", __func__,
+ list_empty(&channel->vidq.active));
+ videobuf_queue_vmalloc_init(&fh->vb_vidq, &s2255_video_qops,
+ NULL, &dev->slock,
+ fh->type,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct s2255_buffer),
+ fh, vdev->lock);
+ return 0;
+}
+
+
+static unsigned int s2255_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct s2255_fh *fh = file->private_data;
+ int rc;
+ dprintk(100, "%s\n", __func__);
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type)
+ return POLLERR;
+ rc = videobuf_poll_stream(file, &fh->vb_vidq, wait);
+ return rc;
+}
+
+static void s2255_destroy(struct s2255_dev *dev)
+{
+ /* board shutdown stops the read pipe if it is running */
+ s2255_board_shutdown(dev);
+ /* make sure firmware still not trying to load */
+ del_timer(&dev->timer); /* only started in .probe and .open */
+ if (dev->fw_data->fw_urb) {
+ usb_kill_urb(dev->fw_data->fw_urb);
+ usb_free_urb(dev->fw_data->fw_urb);
+ dev->fw_data->fw_urb = NULL;
+ }
+ if (dev->fw_data->fw)
+ release_firmware(dev->fw_data->fw);
+ kfree(dev->fw_data->pfw_data);
+ kfree(dev->fw_data);
+ /* reset the DSP so firmware can be reloaded next time */
+ s2255_reset_dsppower(dev);
+ mutex_destroy(&dev->open_lock);
+ mutex_destroy(&dev->lock);
+ usb_put_dev(dev->udev);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ dprintk(1, "%s", __func__);
+ kfree(dev);
+}
+
+static int s2255_release(struct file *file)
+{
+ struct s2255_fh *fh = file->private_data;
+ struct s2255_dev *dev = fh->dev;
+ struct video_device *vdev = video_devdata(file);
+ struct s2255_channel *channel = fh->channel;
+ if (!dev)
+ return -ENODEV;
+ /* turn off stream */
+ if (res_check(fh)) {
+ if (channel->b_acquire)
+ s2255_stop_acquire(fh->channel);
+ videobuf_streamoff(&fh->vb_vidq);
+ res_free(fh);
+ }
+ videobuf_mmap_free(&fh->vb_vidq);
+ dprintk(1, "%s (dev=%s)\n", __func__, video_device_node_name(vdev));
+ kfree(fh);
+ return 0;
+}
+
+static int s2255_mmap_v4l(struct file *file, struct vm_area_struct *vma)
+{
+ struct s2255_fh *fh = file->private_data;
+ int ret;
+
+ if (!fh)
+ return -ENODEV;
+ dprintk(4, "%s, vma=0x%08lx\n", __func__, (unsigned long)vma);
+ ret = videobuf_mmap_mapper(&fh->vb_vidq, vma);
+ dprintk(4, "%s vma start=0x%08lx, size=%ld, ret=%d\n", __func__,
+ (unsigned long)vma->vm_start,
+ (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret);
+ return ret;
+}
+
+static const struct v4l2_file_operations s2255_fops_v4l = {
+ .owner = THIS_MODULE,
+ .open = s2255_open,
+ .release = s2255_release,
+ .poll = s2255_poll,
+ .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
+ .mmap = s2255_mmap_v4l,
+};
+
+static const struct v4l2_ioctl_ops s2255_ioctl_ops = {
+ .vidioc_querymenu = vidioc_querymenu,
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_s_jpegcomp = vidioc_s_jpegcomp,
+ .vidioc_g_jpegcomp = vidioc_g_jpegcomp,
+ .vidioc_s_parm = vidioc_s_parm,
+ .vidioc_g_parm = vidioc_g_parm,
+ .vidioc_enum_frameintervals = vidioc_enum_frameintervals,
+};
+
+static void s2255_video_device_release(struct video_device *vdev)
+{
+ struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev);
+ dprintk(4, "%s, chnls: %d \n", __func__,
+ atomic_read(&dev->num_channels));
+ if (atomic_dec_and_test(&dev->num_channels))
+ s2255_destroy(dev);
+ return;
+}
+
+static struct video_device template = {
+ .name = "s2255v",
+ .fops = &s2255_fops_v4l,
+ .ioctl_ops = &s2255_ioctl_ops,
+ .release = s2255_video_device_release,
+ .tvnorms = S2255_NORMS,
+ .current_norm = V4L2_STD_NTSC_M,
+};
+
+static int s2255_probe_v4l(struct s2255_dev *dev)
+{
+ int ret;
+ int i;
+ int cur_nr = video_nr;
+ struct s2255_channel *channel;
+ ret = v4l2_device_register(&dev->interface->dev, &dev->v4l2_dev);
+ if (ret)
+ return ret;
+ /* initialize all video 4 linux */
+ /* register 4 video devices */
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ channel = &dev->channel[i];
+ INIT_LIST_HEAD(&channel->vidq.active);
+ channel->vidq.dev = dev;
+ /* register 4 video devices */
+ channel->vdev = template;
+ channel->vdev.lock = &dev->lock;
+ channel->vdev.v4l2_dev = &dev->v4l2_dev;
+ video_set_drvdata(&channel->vdev, channel);
+ if (video_nr == -1)
+ ret = video_register_device(&channel->vdev,
+ VFL_TYPE_GRABBER,
+ video_nr);
+ else
+ ret = video_register_device(&channel->vdev,
+ VFL_TYPE_GRABBER,
+ cur_nr + i);
+
+ if (ret) {
+ dev_err(&dev->udev->dev,
+ "failed to register video device!\n");
+ break;
+ }
+ atomic_inc(&dev->num_channels);
+ v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
+ video_device_node_name(&channel->vdev));
+
+ }
+ printk(KERN_INFO "Sensoray 2255 V4L driver Revision: %s\n",
+ S2255_VERSION);
+ /* if no channels registered, return error and probe will fail*/
+ if (atomic_read(&dev->num_channels) == 0) {
+ v4l2_device_unregister(&dev->v4l2_dev);
+ return ret;
+ }
+ if (atomic_read(&dev->num_channels) != MAX_CHANNELS)
+ printk(KERN_WARNING "s2255: Not all channels available.\n");
+ return 0;
+}
+
+/* this function moves the usb stream read pipe data
+ * into the system buffers.
+ * returns 0 on success, EAGAIN if more data to process( call this
+ * function again).
+ *
+ * Received frame structure:
+ * bytes 0-3: marker : 0x2255DA4AL (S2255_MARKER_FRAME)
+ * bytes 4-7: channel: 0-3
+ * bytes 8-11: payload size: size of the frame
+ * bytes 12-payloadsize+12: frame data
+ */
+static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info)
+{
+ char *pdest;
+ u32 offset = 0;
+ int bframe = 0;
+ char *psrc;
+ unsigned long copy_size;
+ unsigned long size;
+ s32 idx = -1;
+ struct s2255_framei *frm;
+ unsigned char *pdata;
+ struct s2255_channel *channel;
+ dprintk(100, "buffer to user\n");
+ channel = &dev->channel[dev->cc];
+ idx = channel->cur_frame;
+ frm = &channel->buffer.frame[idx];
+ if (frm->ulState == S2255_READ_IDLE) {
+ int jj;
+ unsigned int cc;
+ __le32 *pdword; /*data from dsp is little endian */
+ int payload;
+ /* search for marker codes */
+ pdata = (unsigned char *)pipe_info->transfer_buffer;
+ pdword = (__le32 *)pdata;
+ for (jj = 0; jj < (pipe_info->cur_transfer_size - 12); jj++) {
+ switch (*pdword) {
+ case S2255_MARKER_FRAME:
+ dprintk(4, "found frame marker at offset:"
+ " %d [%x %x]\n", jj, pdata[0],
+ pdata[1]);
+ offset = jj + PREFIX_SIZE;
+ bframe = 1;
+ cc = le32_to_cpu(pdword[1]);
+ if (cc >= MAX_CHANNELS) {
+ printk(KERN_ERR
+ "bad channel\n");
+ return -EINVAL;
+ }
+ /* reverse it */
+ dev->cc = G_chnmap[cc];
+ channel = &dev->channel[dev->cc];
+ payload = le32_to_cpu(pdword[3]);
+ if (payload > channel->req_image_size) {
+ channel->bad_payload++;
+ /* discard the bad frame */
+ return -EINVAL;
+ }
+ channel->pkt_size = payload;
+ channel->jpg_size = le32_to_cpu(pdword[4]);
+ break;
+ case S2255_MARKER_RESPONSE:
+
+ pdata += DEF_USB_BLOCK;
+ jj += DEF_USB_BLOCK;
+ if (le32_to_cpu(pdword[1]) >= MAX_CHANNELS)
+ break;
+ cc = G_chnmap[le32_to_cpu(pdword[1])];
+ if (cc >= MAX_CHANNELS)
+ break;
+ channel = &dev->channel[cc];
+ switch (pdword[2]) {
+ case S2255_RESPONSE_SETMODE:
+ /* check if channel valid */
+ /* set mode ready */
+ channel->setmode_ready = 1;
+ wake_up(&channel->wait_setmode);
+ dprintk(5, "setmode ready %d\n", cc);
+ break;
+ case S2255_RESPONSE_FW:
+ dev->chn_ready |= (1 << cc);
+ if ((dev->chn_ready & 0x0f) != 0x0f)
+ break;
+ /* all channels ready */
+ printk(KERN_INFO "s2255: fw loaded\n");
+ atomic_set(&dev->fw_data->fw_state,
+ S2255_FW_SUCCESS);
+ wake_up(&dev->fw_data->wait_fw);
+ break;
+ case S2255_RESPONSE_STATUS:
+ channel->vidstatus = le32_to_cpu(pdword[3]);
+ channel->vidstatus_ready = 1;
+ wake_up(&channel->wait_vidstatus);
+ dprintk(5, "got vidstatus %x chan %d\n",
+ le32_to_cpu(pdword[3]), cc);
+ break;
+ default:
+ printk(KERN_INFO "s2255 unknown resp\n");
+ }
+ default:
+ pdata++;
+ break;
+ }
+ if (bframe)
+ break;
+ } /* for */
+ if (!bframe)
+ return -EINVAL;
+ }
+ channel = &dev->channel[dev->cc];
+ idx = channel->cur_frame;
+ frm = &channel->buffer.frame[idx];
+ /* search done. now find out if should be acquiring on this channel */
+ if (!channel->b_acquire) {
+ /* we found a frame, but this channel is turned off */
+ frm->ulState = S2255_READ_IDLE;
+ return -EINVAL;
+ }
+
+ if (frm->ulState == S2255_READ_IDLE) {
+ frm->ulState = S2255_READ_FRAME;
+ frm->cur_size = 0;
+ }
+
+ /* skip the marker 512 bytes (and offset if out of sync) */
+ psrc = (u8 *)pipe_info->transfer_buffer + offset;
+
+
+ if (frm->lpvbits == NULL) {
+ dprintk(1, "s2255 frame buffer == NULL.%p %p %d %d",
+ frm, dev, dev->cc, idx);
+ return -ENOMEM;
+ }
+
+ pdest = frm->lpvbits + frm->cur_size;
+
+ copy_size = (pipe_info->cur_transfer_size - offset);
+
+ size = channel->pkt_size - PREFIX_SIZE;
+
+ /* sanity check on pdest */
+ if ((copy_size + frm->cur_size) < channel->req_image_size)
+ memcpy(pdest, psrc, copy_size);
+
+ frm->cur_size += copy_size;
+ dprintk(4, "cur_size size %lu size %lu \n", frm->cur_size, size);
+
+ if (frm->cur_size >= size) {
+ dprintk(2, "****************[%d]Buffer[%d]full*************\n",
+ dev->cc, idx);
+ channel->last_frame = channel->cur_frame;
+ channel->cur_frame++;
+ /* end of system frame ring buffer, start at zero */
+ if ((channel->cur_frame == SYS_FRAMES) ||
+ (channel->cur_frame == channel->buffer.dwFrames))
+ channel->cur_frame = 0;
+ /* frame ready */
+ if (channel->b_acquire)
+ s2255_got_frame(channel, channel->jpg_size);
+ channel->frame_count++;
+ frm->ulState = S2255_READ_IDLE;
+ frm->cur_size = 0;
+
+ }
+ /* done successfully */
+ return 0;
+}
+
+static void s2255_read_video_callback(struct s2255_dev *dev,
+ struct s2255_pipeinfo *pipe_info)
+{
+ int res;
+ dprintk(50, "callback read video \n");
+
+ if (dev->cc >= MAX_CHANNELS) {
+ dev->cc = 0;
+ dev_err(&dev->udev->dev, "invalid channel\n");
+ return;
+ }
+ /* otherwise copy to the system buffers */
+ res = save_frame(dev, pipe_info);
+ if (res != 0)
+ dprintk(4, "s2255: read callback failed\n");
+
+ dprintk(50, "callback read video done\n");
+ return;
+}
+
+static long s2255_vendor_req(struct s2255_dev *dev, unsigned char Request,
+ u16 Index, u16 Value, void *TransferBuffer,
+ s32 TransferBufferLength, int bOut)
+{
+ int r;
+ if (!bOut) {
+ r = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
+ Request,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE |
+ USB_DIR_IN,
+ Value, Index, TransferBuffer,
+ TransferBufferLength, HZ * 5);
+ } else {
+ r = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
+ Request, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ Value, Index, TransferBuffer,
+ TransferBufferLength, HZ * 5);
+ }
+ return r;
+}
+
+/*
+ * retrieve FX2 firmware version. future use.
+ * @param dev pointer to device extension
+ * @return -1 for fail, else returns firmware version as an int(16 bits)
+ */
+static int s2255_get_fx2fw(struct s2255_dev *dev)
+{
+ int fw;
+ int ret;
+ unsigned char transBuffer[64];
+ ret = s2255_vendor_req(dev, S2255_VR_FW, 0, 0, transBuffer, 2,
+ S2255_VR_IN);
+ if (ret < 0)
+ dprintk(2, "get fw error: %x\n", ret);
+ fw = transBuffer[0] + (transBuffer[1] << 8);
+ dprintk(2, "Get FW %x %x\n", transBuffer[0], transBuffer[1]);
+ return fw;
+}
+
+/*
+ * Create the system ring buffer to copy frames into from the
+ * usb read pipe.
+ */
+static int s2255_create_sys_buffers(struct s2255_channel *channel)
+{
+ unsigned long i;
+ unsigned long reqsize;
+ dprintk(1, "create sys buffers\n");
+ channel->buffer.dwFrames = SYS_FRAMES;
+ /* always allocate maximum size(PAL) for system buffers */
+ reqsize = SYS_FRAMES_MAXSIZE;
+
+ if (reqsize > SYS_FRAMES_MAXSIZE)
+ reqsize = SYS_FRAMES_MAXSIZE;
+
+ for (i = 0; i < SYS_FRAMES; i++) {
+ /* allocate the frames */
+ channel->buffer.frame[i].lpvbits = vmalloc(reqsize);
+ dprintk(1, "valloc %p chan %d, idx %lu, pdata %p\n",
+ &channel->buffer.frame[i], channel->idx, i,
+ channel->buffer.frame[i].lpvbits);
+ channel->buffer.frame[i].size = reqsize;
+ if (channel->buffer.frame[i].lpvbits == NULL) {
+ printk(KERN_INFO "out of memory. using less frames\n");
+ channel->buffer.dwFrames = i;
+ break;
+ }
+ }
+
+ /* make sure internal states are set */
+ for (i = 0; i < SYS_FRAMES; i++) {
+ channel->buffer.frame[i].ulState = 0;
+ channel->buffer.frame[i].cur_size = 0;
+ }
+
+ channel->cur_frame = 0;
+ channel->last_frame = -1;
+ return 0;
+}
+
+static int s2255_release_sys_buffers(struct s2255_channel *channel)
+{
+ unsigned long i;
+ dprintk(1, "release sys buffers\n");
+ for (i = 0; i < SYS_FRAMES; i++) {
+ if (channel->buffer.frame[i].lpvbits) {
+ dprintk(1, "vfree %p\n",
+ channel->buffer.frame[i].lpvbits);
+ vfree(channel->buffer.frame[i].lpvbits);
+ }
+ channel->buffer.frame[i].lpvbits = NULL;
+ }
+ return 0;
+}
+
+static int s2255_board_init(struct s2255_dev *dev)
+{
+ struct s2255_mode mode_def = DEF_MODEI_NTSC_CONT;
+ int fw_ver;
+ int j;
+ struct s2255_pipeinfo *pipe = &dev->pipe;
+ dprintk(4, "board init: %p", dev);
+ memset(pipe, 0, sizeof(*pipe));
+ pipe->dev = dev;
+ pipe->cur_transfer_size = S2255_USB_XFER_SIZE;
+ pipe->max_transfer_size = S2255_USB_XFER_SIZE;
+
+ pipe->transfer_buffer = kzalloc(pipe->max_transfer_size,
+ GFP_KERNEL);
+ if (pipe->transfer_buffer == NULL) {
+ dprintk(1, "out of memory!\n");
+ return -ENOMEM;
+ }
+ /* query the firmware */
+ fw_ver = s2255_get_fx2fw(dev);
+
+ printk(KERN_INFO "s2255: usb firmware version %d.%d\n",
+ (fw_ver >> 8) & 0xff,
+ fw_ver & 0xff);
+
+ if (fw_ver < S2255_CUR_USB_FWVER)
+ printk(KERN_INFO "s2255: newer USB firmware available\n");
+
+ for (j = 0; j < MAX_CHANNELS; j++) {
+ struct s2255_channel *channel = &dev->channel[j];
+ channel->b_acquire = 0;
+ channel->mode = mode_def;
+ if (dev->pid == 0x2257 && j > 1)
+ channel->mode.color |= (1 << 16);
+ channel->jc.quality = S2255_DEF_JPEG_QUAL;
+ channel->width = LINE_SZ_4CIFS_NTSC;
+ channel->height = NUM_LINES_4CIFS_NTSC * 2;
+ channel->fmt = &formats[0];
+ channel->mode.restart = 1;
+ channel->req_image_size = get_transfer_size(&mode_def);
+ channel->frame_count = 0;
+ /* create the system buffers */
+ s2255_create_sys_buffers(channel);
+ }
+ /* start read pipe */
+ s2255_start_readpipe(dev);
+ dprintk(1, "%s: success\n", __func__);
+ return 0;
+}
+
+static int s2255_board_shutdown(struct s2255_dev *dev)
+{
+ u32 i;
+ dprintk(1, "%s: dev: %p", __func__, dev);
+
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ if (dev->channel[i].b_acquire)
+ s2255_stop_acquire(&dev->channel[i]);
+ }
+ s2255_stop_readpipe(dev);
+ for (i = 0; i < MAX_CHANNELS; i++)
+ s2255_release_sys_buffers(&dev->channel[i]);
+ /* release transfer buffer */
+ kfree(dev->pipe.transfer_buffer);
+ return 0;
+}
+
+static void read_pipe_completion(struct urb *purb)
+{
+ struct s2255_pipeinfo *pipe_info;
+ struct s2255_dev *dev;
+ int status;
+ int pipe;
+ pipe_info = purb->context;
+ dprintk(100, "%s: urb:%p, status %d\n", __func__, purb,
+ purb->status);
+ if (pipe_info == NULL) {
+ dev_err(&purb->dev->dev, "no context!\n");
+ return;
+ }
+
+ dev = pipe_info->dev;
+ if (dev == NULL) {
+ dev_err(&purb->dev->dev, "no context!\n");
+ return;
+ }
+ status = purb->status;
+ /* if shutting down, do not resubmit, exit immediately */
+ if (status == -ESHUTDOWN) {
+ dprintk(2, "%s: err shutdown\n", __func__);
+ pipe_info->err_count++;
+ return;
+ }
+
+ if (pipe_info->state == 0) {
+ dprintk(2, "%s: exiting USB pipe", __func__);
+ return;
+ }
+
+ if (status == 0)
+ s2255_read_video_callback(dev, pipe_info);
+ else {
+ pipe_info->err_count++;
+ dprintk(1, "%s: failed URB %d\n", __func__, status);
+ }
+
+ pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint);
+ /* reuse urb */
+ usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev,
+ pipe,
+ pipe_info->transfer_buffer,
+ pipe_info->cur_transfer_size,
+ read_pipe_completion, pipe_info);
+
+ if (pipe_info->state != 0) {
+ if (usb_submit_urb(pipe_info->stream_urb, GFP_ATOMIC)) {
+ dev_err(&dev->udev->dev, "error submitting urb\n");
+ }
+ } else {
+ dprintk(2, "%s :complete state 0\n", __func__);
+ }
+ return;
+}
+
+static int s2255_start_readpipe(struct s2255_dev *dev)
+{
+ int pipe;
+ int retval;
+ struct s2255_pipeinfo *pipe_info = &dev->pipe;
+ pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint);
+ dprintk(2, "%s: IN %d\n", __func__, dev->read_endpoint);
+ pipe_info->state = 1;
+ pipe_info->err_count = 0;
+ pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!pipe_info->stream_urb) {
+ dev_err(&dev->udev->dev,
+ "ReadStream: Unable to alloc URB\n");
+ return -ENOMEM;
+ }
+ /* transfer buffer allocated in board_init */
+ usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev,
+ pipe,
+ pipe_info->transfer_buffer,
+ pipe_info->cur_transfer_size,
+ read_pipe_completion, pipe_info);
+ retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL);
+ if (retval) {
+ printk(KERN_ERR "s2255: start read pipe failed\n");
+ return retval;
+ }
+ return 0;
+}
+
+/* starts acquisition process */
+static int s2255_start_acquire(struct s2255_channel *channel)
+{
+ unsigned char *buffer;
+ int res;
+ unsigned long chn_rev;
+ int j;
+ struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
+ chn_rev = G_chnmap[channel->idx];
+ buffer = kzalloc(512, GFP_KERNEL);
+ if (buffer == NULL) {
+ dev_err(&dev->udev->dev, "out of mem\n");
+ return -ENOMEM;
+ }
+
+ channel->last_frame = -1;
+ channel->bad_payload = 0;
+ channel->cur_frame = 0;
+ for (j = 0; j < SYS_FRAMES; j++) {
+ channel->buffer.frame[j].ulState = 0;
+ channel->buffer.frame[j].cur_size = 0;
+ }
+
+ /* send the start command */
+ *(__le32 *) buffer = IN_DATA_TOKEN;
+ *((__le32 *) buffer + 1) = (__le32) cpu_to_le32(chn_rev);
+ *((__le32 *) buffer + 2) = CMD_START;
+ res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
+ if (res != 0)
+ dev_err(&dev->udev->dev, "CMD_START error\n");
+
+ dprintk(2, "start acquire exit[%d] %d \n", channel->idx, res);
+ kfree(buffer);
+ return 0;
+}
+
+static int s2255_stop_acquire(struct s2255_channel *channel)
+{
+ unsigned char *buffer;
+ int res;
+ unsigned long chn_rev;
+ struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
+ chn_rev = G_chnmap[channel->idx];
+ buffer = kzalloc(512, GFP_KERNEL);
+ if (buffer == NULL) {
+ dev_err(&dev->udev->dev, "out of mem\n");
+ return -ENOMEM;
+ }
+ /* send the stop command */
+ *(__le32 *) buffer = IN_DATA_TOKEN;
+ *((__le32 *) buffer + 1) = (__le32) cpu_to_le32(chn_rev);
+ *((__le32 *) buffer + 2) = CMD_STOP;
+ res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
+ if (res != 0)
+ dev_err(&dev->udev->dev, "CMD_STOP error\n");
+ kfree(buffer);
+ channel->b_acquire = 0;
+ dprintk(4, "%s: chn %d, res %d\n", __func__, channel->idx, res);
+ return res;
+}
+
+static void s2255_stop_readpipe(struct s2255_dev *dev)
+{
+ struct s2255_pipeinfo *pipe = &dev->pipe;
+
+ pipe->state = 0;
+ if (pipe->stream_urb) {
+ /* cancel urb */
+ usb_kill_urb(pipe->stream_urb);
+ usb_free_urb(pipe->stream_urb);
+ pipe->stream_urb = NULL;
+ }
+ dprintk(4, "%s", __func__);
+ return;
+}
+
+static void s2255_fwload_start(struct s2255_dev *dev, int reset)
+{
+ if (reset)
+ s2255_reset_dsppower(dev);
+ dev->fw_data->fw_size = dev->fw_data->fw->size;
+ atomic_set(&dev->fw_data->fw_state, S2255_FW_NOTLOADED);
+ memcpy(dev->fw_data->pfw_data,
+ dev->fw_data->fw->data, CHUNK_SIZE);
+ dev->fw_data->fw_loaded = CHUNK_SIZE;
+ usb_fill_bulk_urb(dev->fw_data->fw_urb, dev->udev,
+ usb_sndbulkpipe(dev->udev, 2),
+ dev->fw_data->pfw_data,
+ CHUNK_SIZE, s2255_fwchunk_complete,
+ dev->fw_data);
+ mod_timer(&dev->timer, jiffies + HZ);
+}
+
+/* standard usb probe function */
+static int s2255_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct s2255_dev *dev = NULL;
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int i;
+ int retval = -ENOMEM;
+ __le32 *pdata;
+ int fw_size;
+ dprintk(2, "%s\n", __func__);
+ /* allocate memory for our device state and initialize it to zero */
+ dev = kzalloc(sizeof(struct s2255_dev), GFP_KERNEL);
+ if (dev == NULL) {
+ s2255_dev_err(&interface->dev, "out of memory\n");
+ return -ENOMEM;
+ }
+ atomic_set(&dev->num_channels, 0);
+ dev->pid = id->idProduct;
+ dev->fw_data = kzalloc(sizeof(struct s2255_fw), GFP_KERNEL);
+ if (!dev->fw_data)
+ goto errorFWDATA1;
+ mutex_init(&dev->lock);
+ mutex_init(&dev->open_lock);
+ /* grab usb_device and save it */
+ dev->udev = usb_get_dev(interface_to_usbdev(interface));
+ if (dev->udev == NULL) {
+ dev_err(&interface->dev, "null usb device\n");
+ retval = -ENODEV;
+ goto errorUDEV;
+ }
+ dprintk(1, "dev: %p, udev %p interface %p\n", dev,
+ dev->udev, interface);
+ dev->interface = interface;
+ /* set up the endpoint information */
+ iface_desc = interface->cur_altsetting;
+ dprintk(1, "num endpoints %d\n", iface_desc->desc.bNumEndpoints);
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+ if (!dev->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) {
+ /* we found the bulk in endpoint */
+ dev->read_endpoint = endpoint->bEndpointAddress;
+ }
+ }
+
+ if (!dev->read_endpoint) {
+ dev_err(&interface->dev, "Could not find bulk-in endpoint\n");
+ goto errorEP;
+ }
+ init_timer(&dev->timer);
+ dev->timer.function = s2255_timer;
+ dev->timer.data = (unsigned long)dev->fw_data;
+ init_waitqueue_head(&dev->fw_data->wait_fw);
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ struct s2255_channel *channel = &dev->channel[i];
+ dev->channel[i].idx = i;
+ init_waitqueue_head(&channel->wait_setmode);
+ init_waitqueue_head(&channel->wait_vidstatus);
+ }
+
+ dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->fw_data->fw_urb) {
+ dev_err(&interface->dev, "out of memory!\n");
+ goto errorFWURB;
+ }
+
+ dev->fw_data->pfw_data = kzalloc(CHUNK_SIZE, GFP_KERNEL);
+ if (!dev->fw_data->pfw_data) {
+ dev_err(&interface->dev, "out of memory!\n");
+ goto errorFWDATA2;
+ }
+ /* load the first chunk */
+ if (request_firmware(&dev->fw_data->fw,
+ FIRMWARE_FILE_NAME, &dev->udev->dev)) {
+ printk(KERN_ERR "sensoray 2255 failed to get firmware\n");
+ goto errorREQFW;
+ }
+ /* check the firmware is valid */
+ fw_size = dev->fw_data->fw->size;
+ pdata = (__le32 *) &dev->fw_data->fw->data[fw_size - 8];
+
+ if (*pdata != S2255_FW_MARKER) {
+ printk(KERN_INFO "Firmware invalid.\n");
+ retval = -ENODEV;
+ goto errorFWMARKER;
+ } else {
+ /* make sure firmware is the latest */
+ __le32 *pRel;
+ pRel = (__le32 *) &dev->fw_data->fw->data[fw_size - 4];
+ printk(KERN_INFO "s2255 dsp fw version %x\n", *pRel);
+ dev->dsp_fw_ver = le32_to_cpu(*pRel);
+ if (dev->dsp_fw_ver < S2255_CUR_DSP_FWVER)
+ printk(KERN_INFO "s2255: f2255usb.bin out of date.\n");
+ if (dev->pid == 0x2257 &&
+ dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER)
+ printk(KERN_WARNING "s2255: 2257 requires firmware %d"
+ " or above.\n", S2255_MIN_DSP_COLORFILTER);
+ }
+ usb_reset_device(dev->udev);
+ /* load 2255 board specific */
+ retval = s2255_board_init(dev);
+ if (retval)
+ goto errorBOARDINIT;
+ spin_lock_init(&dev->slock);
+ s2255_fwload_start(dev, 0);
+ /* loads v4l specific */
+ retval = s2255_probe_v4l(dev);
+ if (retval)
+ goto errorBOARDINIT;
+ dev_info(&interface->dev, "Sensoray 2255 detected\n");
+ return 0;
+errorBOARDINIT:
+ s2255_board_shutdown(dev);
+errorFWMARKER:
+ release_firmware(dev->fw_data->fw);
+errorREQFW:
+ kfree(dev->fw_data->pfw_data);
+errorFWDATA2:
+ usb_free_urb(dev->fw_data->fw_urb);
+errorFWURB:
+ del_timer(&dev->timer);
+errorEP:
+ usb_put_dev(dev->udev);
+errorUDEV:
+ kfree(dev->fw_data);
+ mutex_destroy(&dev->open_lock);
+ mutex_destroy(&dev->lock);
+errorFWDATA1:
+ kfree(dev);
+ printk(KERN_WARNING "Sensoray 2255 driver load failed: 0x%x\n", retval);
+ return retval;
+}
+
+/* disconnect routine. when board is removed physically or with rmmod */
+static void s2255_disconnect(struct usb_interface *interface)
+{
+ struct s2255_dev *dev = to_s2255_dev(usb_get_intfdata(interface));
+ int i;
+ int channels = atomic_read(&dev->num_channels);
+ mutex_lock(&dev->lock);
+ v4l2_device_disconnect(&dev->v4l2_dev);
+ mutex_unlock(&dev->lock);
+ /*see comments in the uvc_driver.c usb disconnect function */
+ atomic_inc(&dev->num_channels);
+ /* unregister each video device. */
+ for (i = 0; i < channels; i++)
+ video_unregister_device(&dev->channel[i].vdev);
+ /* wake up any of our timers */
+ atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING);
+ wake_up(&dev->fw_data->wait_fw);
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ dev->channel[i].setmode_ready = 1;
+ wake_up(&dev->channel[i].wait_setmode);
+ dev->channel[i].vidstatus_ready = 1;
+ wake_up(&dev->channel[i].wait_vidstatus);
+ }
+ if (atomic_dec_and_test(&dev->num_channels))
+ s2255_destroy(dev);
+ dev_info(&interface->dev, "%s\n", __func__);
+}
+
+static struct usb_driver s2255_driver = {
+ .name = S2255_DRIVER_NAME,
+ .probe = s2255_probe,
+ .disconnect = s2255_disconnect,
+ .id_table = s2255_table,
+};
+
+module_usb_driver(s2255_driver);
+
+MODULE_DESCRIPTION("Sensoray 2255 Video for Linux driver");
+MODULE_AUTHOR("Dean Anderson (Sensoray Company Inc.)");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(S2255_VERSION);
diff --git a/drivers/media/video/s5k6aa.c b/drivers/media/video/s5k6aa.c
new file mode 100644
index 00000000..6625e46a
--- /dev/null
+++ b/drivers/media/video/s5k6aa.c
@@ -0,0 +1,1670 @@
+/*
+ * Driver for Samsung S5K6AAFX SXGA 1/6" 1.3M CMOS Image Sensor
+ * with embedded SoC ISP.
+ *
+ * Copyright (C) 2011, Samsung Electronics Co., Ltd.
+ * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * Based on a driver authored by Dongsoo Nathaniel Kim.
+ * Copyright (C) 2009, Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/media.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-mediabus.h>
+#include <media/s5k6aa.h>
+
+static int debug;
+module_param(debug, int, 0644);
+
+#define DRIVER_NAME "S5K6AA"
+
+/* The token to indicate array termination */
+#define S5K6AA_TERM 0xffff
+#define S5K6AA_OUT_WIDTH_DEF 640
+#define S5K6AA_OUT_HEIGHT_DEF 480
+#define S5K6AA_WIN_WIDTH_MAX 1280
+#define S5K6AA_WIN_HEIGHT_MAX 1024
+#define S5K6AA_WIN_WIDTH_MIN 8
+#define S5K6AA_WIN_HEIGHT_MIN 8
+
+/*
+ * H/W register Interface (0xD0000000 - 0xD0000FFF)
+ */
+#define AHB_MSB_ADDR_PTR 0xfcfc
+#define GEN_REG_OFFSH 0xd000
+#define REG_CMDWR_ADDRH 0x0028
+#define REG_CMDWR_ADDRL 0x002a
+#define REG_CMDRD_ADDRH 0x002c
+#define REG_CMDRD_ADDRL 0x002e
+#define REG_CMDBUF0_ADDR 0x0f12
+#define REG_CMDBUF1_ADDR 0x0f10
+
+/*
+ * Host S/W Register interface (0x70000000 - 0x70002000)
+ * The value of the two most significant address bytes is 0x7000,
+ * (HOST_SWIF_OFFS_H). The register addresses below specify 2 LSBs.
+ */
+#define HOST_SWIF_OFFSH 0x7000
+
+/* Initialization parameters */
+/* Master clock frequency in KHz */
+#define REG_I_INCLK_FREQ_L 0x01b8
+#define REG_I_INCLK_FREQ_H 0x01ba
+#define MIN_MCLK_FREQ_KHZ 6000U
+#define MAX_MCLK_FREQ_KHZ 27000U
+#define REG_I_USE_NPVI_CLOCKS 0x01c6
+#define REG_I_USE_NMIPI_CLOCKS 0x01c8
+
+/* Clock configurations, n = 0..2. REG_I_* frequency unit is 4 kHz. */
+#define REG_I_OPCLK_4KHZ(n) ((n) * 6 + 0x01cc)
+#define REG_I_MIN_OUTRATE_4KHZ(n) ((n) * 6 + 0x01ce)
+#define REG_I_MAX_OUTRATE_4KHZ(n) ((n) * 6 + 0x01d0)
+#define SYS_PLL_OUT_FREQ (48000000 / 4000)
+#define PCLK_FREQ_MIN (24000000 / 4000)
+#define PCLK_FREQ_MAX (48000000 / 4000)
+#define REG_I_INIT_PARAMS_UPDATED 0x01e0
+#define REG_I_ERROR_INFO 0x01e2
+
+/* General purpose parameters */
+#define REG_USER_BRIGHTNESS 0x01e4
+#define REG_USER_CONTRAST 0x01e6
+#define REG_USER_SATURATION 0x01e8
+#define REG_USER_SHARPBLUR 0x01ea
+
+#define REG_G_SPEC_EFFECTS 0x01ee
+#define REG_G_ENABLE_PREV 0x01f0
+#define REG_G_ENABLE_PREV_CHG 0x01f2
+#define REG_G_NEW_CFG_SYNC 0x01f8
+#define REG_G_PREVZOOM_IN_WIDTH 0x020a
+#define REG_G_PREVZOOM_IN_HEIGHT 0x020c
+#define REG_G_PREVZOOM_IN_XOFFS 0x020e
+#define REG_G_PREVZOOM_IN_YOFFS 0x0210
+#define REG_G_INPUTS_CHANGE_REQ 0x021a
+#define REG_G_ACTIVE_PREV_CFG 0x021c
+#define REG_G_PREV_CFG_CHG 0x021e
+#define REG_G_PREV_OPEN_AFTER_CH 0x0220
+#define REG_G_PREV_CFG_ERROR 0x0222
+
+/* Preview control section. n = 0...4. */
+#define PREG(n, x) ((n) * 0x26 + x)
+#define REG_P_OUT_WIDTH(n) PREG(n, 0x0242)
+#define REG_P_OUT_HEIGHT(n) PREG(n, 0x0244)
+#define REG_P_FMT(n) PREG(n, 0x0246)
+#define REG_P_MAX_OUT_RATE(n) PREG(n, 0x0248)
+#define REG_P_MIN_OUT_RATE(n) PREG(n, 0x024a)
+#define REG_P_PVI_MASK(n) PREG(n, 0x024c)
+#define REG_P_CLK_INDEX(n) PREG(n, 0x024e)
+#define REG_P_FR_RATE_TYPE(n) PREG(n, 0x0250)
+#define FR_RATE_DYNAMIC 0
+#define FR_RATE_FIXED 1
+#define FR_RATE_FIXED_ACCURATE 2
+#define REG_P_FR_RATE_Q_TYPE(n) PREG(n, 0x0252)
+#define FR_RATE_Q_BEST_FRRATE 1 /* Binning enabled */
+#define FR_RATE_Q_BEST_QUALITY 2 /* Binning disabled */
+/* Frame period in 0.1 ms units */
+#define REG_P_MAX_FR_TIME(n) PREG(n, 0x0254)
+#define REG_P_MIN_FR_TIME(n) PREG(n, 0x0256)
+/* Conversion to REG_P_[MAX/MIN]_FR_TIME value; __t: time in us */
+#define US_TO_FR_TIME(__t) ((__t) / 100)
+#define S5K6AA_MIN_FR_TIME 33300 /* us */
+#define S5K6AA_MAX_FR_TIME 650000 /* us */
+#define S5K6AA_MAX_HIGHRES_FR_TIME 666 /* x100 us */
+/* The below 5 registers are for "device correction" values */
+#define REG_P_COLORTEMP(n) PREG(n, 0x025e)
+#define REG_P_PREV_MIRROR(n) PREG(n, 0x0262)
+
+/* Extended image property controls */
+/* Exposure time in 10 us units */
+#define REG_SF_USR_EXPOSURE_L 0x03c6
+#define REG_SF_USR_EXPOSURE_H 0x03c8
+#define REG_SF_USR_EXPOSURE_CHG 0x03ca
+#define REG_SF_USR_TOT_GAIN 0x03cc
+#define REG_SF_USR_TOT_GAIN_CHG 0x03ce
+#define REG_SF_RGAIN 0x03d0
+#define REG_SF_RGAIN_CHG 0x03d2
+#define REG_SF_GGAIN 0x03d4
+#define REG_SF_GGAIN_CHG 0x03d6
+#define REG_SF_BGAIN 0x03d8
+#define REG_SF_BGAIN_CHG 0x03da
+#define REG_SF_FLICKER_QUANT 0x03dc
+#define REG_SF_FLICKER_QUANT_CHG 0x03de
+
+/* Output interface (parallel/MIPI) setup */
+#define REG_OIF_EN_MIPI_LANES 0x03fa
+#define REG_OIF_EN_PACKETS 0x03fc
+#define REG_OIF_CFG_CHG 0x03fe
+
+/* Auto-algorithms enable mask */
+#define REG_DBG_AUTOALG_EN 0x0400
+#define AALG_ALL_EN_MASK (1 << 0)
+#define AALG_AE_EN_MASK (1 << 1)
+#define AALG_DIVLEI_EN_MASK (1 << 2)
+#define AALG_WB_EN_MASK (1 << 3)
+#define AALG_FLICKER_EN_MASK (1 << 5)
+#define AALG_FIT_EN_MASK (1 << 6)
+#define AALG_WRHW_EN_MASK (1 << 7)
+
+/* Firmware revision information */
+#define REG_FW_APIVER 0x012e
+#define S5K6AAFX_FW_APIVER 0x0001
+#define REG_FW_REVISION 0x0130
+
+/* For now we use only one user configuration register set */
+#define S5K6AA_MAX_PRESETS 1
+
+static const char * const s5k6aa_supply_names[] = {
+ "vdd_core", /* Digital core supply 1.5V (1.4V to 1.6V) */
+ "vdda", /* Analog power supply 2.8V (2.6V to 3.0V) */
+ "vdd_reg", /* Regulator input power 1.8V (1.7V to 1.9V)
+ or 2.8V (2.6V to 3.0) */
+ "vddio", /* I/O supply 1.8V (1.65V to 1.95V)
+ or 2.8V (2.5V to 3.1V) */
+};
+#define S5K6AA_NUM_SUPPLIES ARRAY_SIZE(s5k6aa_supply_names)
+
+enum s5k6aa_gpio_id {
+ STBY,
+ RST,
+ GPIO_NUM,
+};
+
+struct s5k6aa_regval {
+ u16 addr;
+ u16 val;
+};
+
+struct s5k6aa_pixfmt {
+ enum v4l2_mbus_pixelcode code;
+ u32 colorspace;
+ /* REG_P_FMT(x) register value */
+ u16 reg_p_fmt;
+};
+
+struct s5k6aa_preset {
+ /* output pixel format and resolution */
+ struct v4l2_mbus_framefmt mbus_fmt;
+ u8 clk_id;
+ u8 index;
+};
+
+struct s5k6aa_ctrls {
+ struct v4l2_ctrl_handler handler;
+ /* Auto / manual white balance cluster */
+ struct v4l2_ctrl *awb;
+ struct v4l2_ctrl *gain_red;
+ struct v4l2_ctrl *gain_blue;
+ struct v4l2_ctrl *gain_green;
+ /* Mirror cluster */
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+ /* Auto exposure / manual exposure and gain cluster */
+ struct v4l2_ctrl *auto_exp;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *gain;
+};
+
+struct s5k6aa_interval {
+ u16 reg_fr_time;
+ struct v4l2_fract interval;
+ /* Maximum rectangle for the interval */
+ struct v4l2_frmsize_discrete size;
+};
+
+struct s5k6aa {
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+
+ enum v4l2_mbus_type bus_type;
+ u8 mipi_lanes;
+
+ int (*s_power)(int enable);
+ struct regulator_bulk_data supplies[S5K6AA_NUM_SUPPLIES];
+ struct s5k6aa_gpio gpio[GPIO_NUM];
+
+ /* external master clock frequency */
+ unsigned long mclk_frequency;
+ /* ISP internal master clock frequency */
+ u16 clk_fop;
+ /* output pixel clock frequency range */
+ u16 pclk_fmin;
+ u16 pclk_fmax;
+
+ unsigned int inv_hflip:1;
+ unsigned int inv_vflip:1;
+
+ /* protects the struct members below */
+ struct mutex lock;
+
+ /* sensor matrix scan window */
+ struct v4l2_rect ccd_rect;
+
+ struct s5k6aa_ctrls ctrls;
+ struct s5k6aa_preset presets[S5K6AA_MAX_PRESETS];
+ struct s5k6aa_preset *preset;
+ const struct s5k6aa_interval *fiv;
+
+ unsigned int streaming:1;
+ unsigned int apply_cfg:1;
+ unsigned int apply_crop:1;
+ unsigned int power;
+};
+
+static struct s5k6aa_regval s5k6aa_analog_config[] = {
+ /* Analog settings */
+ { 0x112a, 0x0000 }, { 0x1132, 0x0000 },
+ { 0x113e, 0x0000 }, { 0x115c, 0x0000 },
+ { 0x1164, 0x0000 }, { 0x1174, 0x0000 },
+ { 0x1178, 0x0000 }, { 0x077a, 0x0000 },
+ { 0x077c, 0x0000 }, { 0x077e, 0x0000 },
+ { 0x0780, 0x0000 }, { 0x0782, 0x0000 },
+ { 0x0784, 0x0000 }, { 0x0786, 0x0000 },
+ { 0x0788, 0x0000 }, { 0x07a2, 0x0000 },
+ { 0x07a4, 0x0000 }, { 0x07a6, 0x0000 },
+ { 0x07a8, 0x0000 }, { 0x07b6, 0x0000 },
+ { 0x07b8, 0x0002 }, { 0x07ba, 0x0004 },
+ { 0x07bc, 0x0004 }, { 0x07be, 0x0005 },
+ { 0x07c0, 0x0005 }, { S5K6AA_TERM, 0 },
+};
+
+/* TODO: Add RGB888 and Bayer format */
+static const struct s5k6aa_pixfmt s5k6aa_formats[] = {
+ { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG, 5 },
+ /* range 16-240 */
+ { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_REC709, 6 },
+ { V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_JPEG, 0 },
+};
+
+static const struct s5k6aa_interval s5k6aa_intervals[] = {
+ { 1000, {10000, 1000000}, {1280, 1024} }, /* 10 fps */
+ { 666, {15000, 1000000}, {1280, 1024} }, /* 15 fps */
+ { 500, {20000, 1000000}, {1280, 720} }, /* 20 fps */
+ { 400, {25000, 1000000}, {640, 480} }, /* 25 fps */
+ { 333, {33300, 1000000}, {640, 480} }, /* 30 fps */
+};
+
+#define S5K6AA_INTERVAL_DEF_INDEX 1 /* 15 fps */
+
+static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct s5k6aa, ctrls.handler)->sd;
+}
+
+static inline struct s5k6aa *to_s5k6aa(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct s5k6aa, sd);
+}
+
+/* Set initial values for all preview presets */
+static void s5k6aa_presets_data_init(struct s5k6aa *s5k6aa)
+{
+ struct s5k6aa_preset *preset = &s5k6aa->presets[0];
+ int i;
+
+ for (i = 0; i < S5K6AA_MAX_PRESETS; i++) {
+ preset->mbus_fmt.width = S5K6AA_OUT_WIDTH_DEF;
+ preset->mbus_fmt.height = S5K6AA_OUT_HEIGHT_DEF;
+ preset->mbus_fmt.code = s5k6aa_formats[0].code;
+ preset->index = i;
+ preset->clk_id = 0;
+ preset++;
+ }
+
+ s5k6aa->fiv = &s5k6aa_intervals[S5K6AA_INTERVAL_DEF_INDEX];
+ s5k6aa->preset = &s5k6aa->presets[0];
+}
+
+static int s5k6aa_i2c_read(struct i2c_client *client, u16 addr, u16 *val)
+{
+ u8 wbuf[2] = {addr >> 8, addr & 0xFF};
+ struct i2c_msg msg[2];
+ u8 rbuf[2];
+ int ret;
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 2;
+ msg[0].buf = wbuf;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 2;
+ msg[1].buf = rbuf;
+
+ ret = i2c_transfer(client->adapter, msg, 2);
+ *val = be16_to_cpu(*((u16 *)rbuf));
+
+ v4l2_dbg(3, debug, client, "i2c_read: 0x%04X : 0x%04x\n", addr, *val);
+
+ return ret == 2 ? 0 : ret;
+}
+
+static int s5k6aa_i2c_write(struct i2c_client *client, u16 addr, u16 val)
+{
+ u8 buf[4] = {addr >> 8, addr & 0xFF, val >> 8, val & 0xFF};
+
+ int ret = i2c_master_send(client, buf, 4);
+ v4l2_dbg(3, debug, client, "i2c_write: 0x%04X : 0x%04x\n", addr, val);
+
+ return ret == 4 ? 0 : ret;
+}
+
+/* The command register write, assumes Command_Wr_addH = 0x7000. */
+static int s5k6aa_write(struct i2c_client *c, u16 addr, u16 val)
+{
+ int ret = s5k6aa_i2c_write(c, REG_CMDWR_ADDRL, addr);
+ if (ret)
+ return ret;
+ return s5k6aa_i2c_write(c, REG_CMDBUF0_ADDR, val);
+}
+
+/* The command register read, assumes Command_Rd_addH = 0x7000. */
+static int s5k6aa_read(struct i2c_client *client, u16 addr, u16 *val)
+{
+ int ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRL, addr);
+ if (ret)
+ return ret;
+ return s5k6aa_i2c_read(client, REG_CMDBUF0_ADDR, val);
+}
+
+static int s5k6aa_write_array(struct v4l2_subdev *sd,
+ const struct s5k6aa_regval *msg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u16 addr_incr = 0;
+ int ret = 0;
+
+ while (msg->addr != S5K6AA_TERM) {
+ if (addr_incr != 2)
+ ret = s5k6aa_i2c_write(client, REG_CMDWR_ADDRL,
+ msg->addr);
+ if (ret)
+ break;
+ ret = s5k6aa_i2c_write(client, REG_CMDBUF0_ADDR, msg->val);
+ if (ret)
+ break;
+ /* Assume that msg->addr is always less than 0xfffc */
+ addr_incr = (msg + 1)->addr - msg->addr;
+ msg++;
+ }
+
+ return ret;
+}
+
+/* Configure the AHB high address bytes for GTG registers access */
+static int s5k6aa_set_ahb_address(struct i2c_client *client)
+{
+ int ret = s5k6aa_i2c_write(client, AHB_MSB_ADDR_PTR, GEN_REG_OFFSH);
+ if (ret)
+ return ret;
+ ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRH, HOST_SWIF_OFFSH);
+ if (ret)
+ return ret;
+ return s5k6aa_i2c_write(client, REG_CMDWR_ADDRH, HOST_SWIF_OFFSH);
+}
+
+/**
+ * s5k6aa_configure_pixel_clock - apply ISP main clock/PLL configuration
+ *
+ * Configure the internal ISP PLL for the required output frequency.
+ * Locking: called with s5k6aa.lock mutex held.
+ */
+static int s5k6aa_configure_pixel_clocks(struct s5k6aa *s5k6aa)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd);
+ unsigned long fmclk = s5k6aa->mclk_frequency / 1000;
+ u16 status;
+ int ret;
+
+ if (WARN(fmclk < MIN_MCLK_FREQ_KHZ || fmclk > MAX_MCLK_FREQ_KHZ,
+ "Invalid clock frequency: %ld\n", fmclk))
+ return -EINVAL;
+
+ s5k6aa->pclk_fmin = PCLK_FREQ_MIN;
+ s5k6aa->pclk_fmax = PCLK_FREQ_MAX;
+ s5k6aa->clk_fop = SYS_PLL_OUT_FREQ;
+
+ /* External input clock frequency in kHz */
+ ret = s5k6aa_write(c, REG_I_INCLK_FREQ_H, fmclk >> 16);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_I_INCLK_FREQ_L, fmclk & 0xFFFF);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_I_USE_NPVI_CLOCKS, 1);
+ /* Internal PLL frequency */
+ if (!ret)
+ ret = s5k6aa_write(c, REG_I_OPCLK_4KHZ(0), s5k6aa->clk_fop);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_I_MIN_OUTRATE_4KHZ(0),
+ s5k6aa->pclk_fmin);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_I_MAX_OUTRATE_4KHZ(0),
+ s5k6aa->pclk_fmax);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_I_INIT_PARAMS_UPDATED, 1);
+ if (!ret)
+ ret = s5k6aa_read(c, REG_I_ERROR_INFO, &status);
+
+ return ret ? ret : (status ? -EINVAL : 0);
+}
+
+/* Set horizontal and vertical image flipping */
+static int s5k6aa_set_mirror(struct s5k6aa *s5k6aa, int horiz_flip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ int index = s5k6aa->preset->index;
+
+ unsigned int vflip = s5k6aa->ctrls.vflip->val ^ s5k6aa->inv_vflip;
+ unsigned int flip = (horiz_flip ^ s5k6aa->inv_hflip) | (vflip << 1);
+
+ return s5k6aa_write(client, REG_P_PREV_MIRROR(index), flip);
+}
+
+/* Configure auto/manual white balance and R/G/B gains */
+static int s5k6aa_set_awb(struct s5k6aa *s5k6aa, int awb)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd);
+ struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls;
+ u16 reg;
+
+ int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, &reg);
+
+ if (!ret && !awb) {
+ ret = s5k6aa_write(c, REG_SF_RGAIN, ctrls->gain_red->val);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_SF_RGAIN_CHG, 1);
+ if (ret)
+ return ret;
+
+ ret = s5k6aa_write(c, REG_SF_GGAIN, ctrls->gain_green->val);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_SF_GGAIN_CHG, 1);
+ if (ret)
+ return ret;
+
+ ret = s5k6aa_write(c, REG_SF_BGAIN, ctrls->gain_blue->val);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_SF_BGAIN_CHG, 1);
+ }
+ if (!ret) {
+ reg = awb ? reg | AALG_WB_EN_MASK : reg & ~AALG_WB_EN_MASK;
+ ret = s5k6aa_write(c, REG_DBG_AUTOALG_EN, reg);
+ }
+
+ return ret;
+}
+
+/* Program FW with exposure time, 'exposure' in us units */
+static int s5k6aa_set_user_exposure(struct i2c_client *client, int exposure)
+{
+ unsigned int time = exposure / 10;
+
+ int ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_L, time & 0xffff);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_H, time >> 16);
+ if (ret)
+ return ret;
+ return s5k6aa_write(client, REG_SF_USR_EXPOSURE_CHG, 1);
+}
+
+static int s5k6aa_set_user_gain(struct i2c_client *client, int gain)
+{
+ int ret = s5k6aa_write(client, REG_SF_USR_TOT_GAIN, gain);
+ if (ret)
+ return ret;
+ return s5k6aa_write(client, REG_SF_USR_TOT_GAIN_CHG, 1);
+}
+
+/* Set auto/manual exposure and total gain */
+static int s5k6aa_set_auto_exposure(struct s5k6aa *s5k6aa, int value)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd);
+ unsigned int exp_time = s5k6aa->ctrls.exposure->val;
+ u16 auto_alg;
+
+ int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, &auto_alg);
+ if (ret)
+ return ret;
+
+ v4l2_dbg(1, debug, c, "man_exp: %d, auto_exp: %d, a_alg: 0x%x\n",
+ exp_time, value, auto_alg);
+
+ if (value == V4L2_EXPOSURE_AUTO) {
+ auto_alg |= AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK;
+ } else {
+ ret = s5k6aa_set_user_exposure(c, exp_time);
+ if (ret)
+ return ret;
+ ret = s5k6aa_set_user_gain(c, s5k6aa->ctrls.gain->val);
+ if (ret)
+ return ret;
+ auto_alg &= ~(AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK);
+ }
+
+ return s5k6aa_write(c, REG_DBG_AUTOALG_EN, auto_alg);
+}
+
+static int s5k6aa_set_anti_flicker(struct s5k6aa *s5k6aa, int value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ u16 auto_alg;
+ int ret;
+
+ ret = s5k6aa_read(client, REG_DBG_AUTOALG_EN, &auto_alg);
+ if (ret)
+ return ret;
+
+ if (value == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) {
+ auto_alg |= AALG_FLICKER_EN_MASK;
+ } else {
+ auto_alg &= ~AALG_FLICKER_EN_MASK;
+ /* The V4L2_CID_LINE_FREQUENCY control values match
+ * the register values */
+ ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT, value);
+ if (ret)
+ return ret;
+ ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT_CHG, 1);
+ if (ret)
+ return ret;
+ }
+
+ return s5k6aa_write(client, REG_DBG_AUTOALG_EN, auto_alg);
+}
+
+static int s5k6aa_set_colorfx(struct s5k6aa *s5k6aa, int val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ static const struct v4l2_control colorfx[] = {
+ { V4L2_COLORFX_NONE, 0 },
+ { V4L2_COLORFX_BW, 1 },
+ { V4L2_COLORFX_NEGATIVE, 2 },
+ { V4L2_COLORFX_SEPIA, 3 },
+ { V4L2_COLORFX_SKY_BLUE, 4 },
+ { V4L2_COLORFX_SKETCH, 5 },
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(colorfx); i++) {
+ if (colorfx[i].id == val)
+ return s5k6aa_write(client, REG_G_SPEC_EFFECTS,
+ colorfx[i].value);
+ }
+ return -EINVAL;
+}
+
+static int s5k6aa_preview_config_status(struct i2c_client *client)
+{
+ u16 error = 0;
+ int ret = s5k6aa_read(client, REG_G_PREV_CFG_ERROR, &error);
+
+ v4l2_dbg(1, debug, client, "error: 0x%x (%d)\n", error, ret);
+ return ret ? ret : (error ? -EINVAL : 0);
+}
+
+static int s5k6aa_get_pixfmt_index(struct s5k6aa *s5k6aa,
+ struct v4l2_mbus_framefmt *mf)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(s5k6aa_formats); i++)
+ if (mf->colorspace == s5k6aa_formats[i].colorspace &&
+ mf->code == s5k6aa_formats[i].code)
+ return i;
+ return 0;
+}
+
+static int s5k6aa_set_output_framefmt(struct s5k6aa *s5k6aa,
+ struct s5k6aa_preset *preset)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ int fmt_index = s5k6aa_get_pixfmt_index(s5k6aa, &preset->mbus_fmt);
+ int ret;
+
+ ret = s5k6aa_write(client, REG_P_OUT_WIDTH(preset->index),
+ preset->mbus_fmt.width);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_OUT_HEIGHT(preset->index),
+ preset->mbus_fmt.height);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_FMT(preset->index),
+ s5k6aa_formats[fmt_index].reg_p_fmt);
+ return ret;
+}
+
+static int s5k6aa_set_input_params(struct s5k6aa *s5k6aa)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd);
+ struct v4l2_rect *r = &s5k6aa->ccd_rect;
+ int ret;
+
+ ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_WIDTH, r->width);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_HEIGHT, r->height);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_XOFFS, r->left);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_YOFFS, r->top);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_G_INPUTS_CHANGE_REQ, 1);
+ if (!ret)
+ s5k6aa->apply_crop = 0;
+
+ return ret;
+}
+
+/**
+ * s5k6aa_configure_video_bus - configure the video output interface
+ * @bus_type: video bus type: parallel or MIPI-CSI
+ * @nlanes: number of MIPI lanes to be used (MIPI-CSI only)
+ *
+ * Note: Only parallel bus operation has been tested.
+ */
+static int s5k6aa_configure_video_bus(struct s5k6aa *s5k6aa,
+ enum v4l2_mbus_type bus_type, int nlanes)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ u16 cfg = 0;
+ int ret;
+
+ /*
+ * TODO: The sensor is supposed to support BT.601 and BT.656
+ * but there is nothing indicating how to switch between both
+ * in the datasheet. For now default BT.601 interface is assumed.
+ */
+ if (bus_type == V4L2_MBUS_CSI2)
+ cfg = nlanes;
+ else if (bus_type != V4L2_MBUS_PARALLEL)
+ return -EINVAL;
+
+ ret = s5k6aa_write(client, REG_OIF_EN_MIPI_LANES, cfg);
+ if (ret)
+ return ret;
+ return s5k6aa_write(client, REG_OIF_CFG_CHG, 1);
+}
+
+/* This function should be called when switching to new user configuration set*/
+static int s5k6aa_new_config_sync(struct i2c_client *client, int timeout,
+ int cid)
+{
+ unsigned long end = jiffies + msecs_to_jiffies(timeout);
+ u16 reg = 1;
+ int ret;
+
+ ret = s5k6aa_write(client, REG_G_ACTIVE_PREV_CFG, cid);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_G_NEW_CFG_SYNC, 1);
+ if (timeout == 0)
+ return ret;
+
+ while (ret >= 0 && time_is_after_jiffies(end)) {
+ ret = s5k6aa_read(client, REG_G_NEW_CFG_SYNC, &reg);
+ if (!reg)
+ return 0;
+ usleep_range(1000, 5000);
+ }
+ return ret ? ret : -ETIMEDOUT;
+}
+
+/**
+ * s5k6aa_set_prev_config - write user preview register set
+ *
+ * Configure output resolution and color fromat, pixel clock
+ * frequency range, device frame rate type and frame period range.
+ */
+static int s5k6aa_set_prev_config(struct s5k6aa *s5k6aa,
+ struct s5k6aa_preset *preset)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ int idx = preset->index;
+ u16 frame_rate_q;
+ int ret;
+
+ if (s5k6aa->fiv->reg_fr_time >= S5K6AA_MAX_HIGHRES_FR_TIME)
+ frame_rate_q = FR_RATE_Q_BEST_FRRATE;
+ else
+ frame_rate_q = FR_RATE_Q_BEST_QUALITY;
+
+ ret = s5k6aa_set_output_framefmt(s5k6aa, preset);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_MAX_OUT_RATE(idx),
+ s5k6aa->pclk_fmax);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_MIN_OUT_RATE(idx),
+ s5k6aa->pclk_fmin);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_CLK_INDEX(idx),
+ preset->clk_id);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_FR_RATE_TYPE(idx),
+ FR_RATE_DYNAMIC);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_FR_RATE_Q_TYPE(idx),
+ frame_rate_q);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_MAX_FR_TIME(idx),
+ s5k6aa->fiv->reg_fr_time + 33);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_MIN_FR_TIME(idx),
+ s5k6aa->fiv->reg_fr_time - 33);
+ if (!ret)
+ ret = s5k6aa_new_config_sync(client, 250, idx);
+ if (!ret)
+ ret = s5k6aa_preview_config_status(client);
+ if (!ret)
+ s5k6aa->apply_cfg = 0;
+
+ v4l2_dbg(1, debug, client, "Frame interval: %d +/- 3.3ms. (%d)\n",
+ s5k6aa->fiv->reg_fr_time, ret);
+ return ret;
+}
+
+/**
+ * s5k6aa_initialize_isp - basic ISP MCU initialization
+ *
+ * Configure AHB addresses for registers read/write; configure PLLs for
+ * required output pixel clock. The ISP power supply needs to be already
+ * enabled, with an optional H/W reset.
+ * Locking: called with s5k6aa.lock mutex held.
+ */
+static int s5k6aa_initialize_isp(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ int ret;
+
+ s5k6aa->apply_crop = 1;
+ s5k6aa->apply_cfg = 1;
+ msleep(100);
+
+ ret = s5k6aa_set_ahb_address(client);
+ if (ret)
+ return ret;
+ ret = s5k6aa_configure_video_bus(s5k6aa, s5k6aa->bus_type,
+ s5k6aa->mipi_lanes);
+ if (ret)
+ return ret;
+ ret = s5k6aa_write_array(sd, s5k6aa_analog_config);
+ if (ret)
+ return ret;
+ msleep(20);
+
+ return s5k6aa_configure_pixel_clocks(s5k6aa);
+}
+
+static int s5k6aa_gpio_set_value(struct s5k6aa *priv, int id, u32 val)
+{
+ if (!gpio_is_valid(priv->gpio[id].gpio))
+ return 0;
+ gpio_set_value(priv->gpio[id].gpio, !!val);
+ return 1;
+}
+
+static int s5k6aa_gpio_assert(struct s5k6aa *priv, int id)
+{
+ return s5k6aa_gpio_set_value(priv, id, priv->gpio[id].level);
+}
+
+static int s5k6aa_gpio_deassert(struct s5k6aa *priv, int id)
+{
+ return s5k6aa_gpio_set_value(priv, id, !priv->gpio[id].level);
+}
+
+static int __s5k6aa_power_on(struct s5k6aa *s5k6aa)
+{
+ int ret;
+
+ ret = regulator_bulk_enable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies);
+ if (ret)
+ return ret;
+ if (s5k6aa_gpio_deassert(s5k6aa, STBY))
+ usleep_range(150, 200);
+
+ if (s5k6aa->s_power)
+ ret = s5k6aa->s_power(1);
+ usleep_range(4000, 4000);
+
+ if (s5k6aa_gpio_deassert(s5k6aa, RST))
+ msleep(20);
+
+ return ret;
+}
+
+static int __s5k6aa_power_off(struct s5k6aa *s5k6aa)
+{
+ int ret;
+
+ if (s5k6aa_gpio_assert(s5k6aa, RST))
+ usleep_range(100, 150);
+
+ if (s5k6aa->s_power) {
+ ret = s5k6aa->s_power(0);
+ if (ret)
+ return ret;
+ }
+ if (s5k6aa_gpio_assert(s5k6aa, STBY))
+ usleep_range(50, 100);
+ s5k6aa->streaming = 0;
+
+ return regulator_bulk_disable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies);
+}
+
+/*
+ * V4L2 subdev core and video operations
+ */
+static int s5k6aa_set_power(struct v4l2_subdev *sd, int on)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ int ret = 0;
+
+ mutex_lock(&s5k6aa->lock);
+
+ if (!on == s5k6aa->power) {
+ if (on) {
+ ret = __s5k6aa_power_on(s5k6aa);
+ if (!ret)
+ ret = s5k6aa_initialize_isp(sd);
+ } else {
+ ret = __s5k6aa_power_off(s5k6aa);
+ }
+
+ if (!ret)
+ s5k6aa->power += on ? 1 : -1;
+ }
+
+ mutex_unlock(&s5k6aa->lock);
+
+ if (!on || ret || s5k6aa->power != 1)
+ return ret;
+
+ return v4l2_ctrl_handler_setup(sd->ctrl_handler);
+}
+
+static int __s5k6aa_stream(struct s5k6aa *s5k6aa, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ int ret = 0;
+
+ ret = s5k6aa_write(client, REG_G_ENABLE_PREV, enable);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_G_ENABLE_PREV_CHG, 1);
+ if (!ret)
+ s5k6aa->streaming = enable;
+
+ return ret;
+}
+
+static int s5k6aa_s_stream(struct v4l2_subdev *sd, int on)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ int ret = 0;
+
+ mutex_lock(&s5k6aa->lock);
+
+ if (s5k6aa->streaming == !on) {
+ if (!ret && s5k6aa->apply_cfg)
+ ret = s5k6aa_set_prev_config(s5k6aa, s5k6aa->preset);
+ if (s5k6aa->apply_crop)
+ ret = s5k6aa_set_input_params(s5k6aa);
+ if (!ret)
+ ret = __s5k6aa_stream(s5k6aa, !!on);
+ }
+ mutex_unlock(&s5k6aa->lock);
+
+ return ret;
+}
+
+static int s5k6aa_g_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+
+ mutex_lock(&s5k6aa->lock);
+ fi->interval = s5k6aa->fiv->interval;
+ mutex_unlock(&s5k6aa->lock);
+
+ return 0;
+}
+
+static int __s5k6aa_set_frame_interval(struct s5k6aa *s5k6aa,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct v4l2_mbus_framefmt *mbus_fmt = &s5k6aa->preset->mbus_fmt;
+ const struct s5k6aa_interval *fiv = &s5k6aa_intervals[0];
+ unsigned int err, min_err = UINT_MAX;
+ unsigned int i, fr_time;
+
+ if (fi->interval.denominator == 0)
+ return -EINVAL;
+
+ fr_time = fi->interval.numerator * 10000 / fi->interval.denominator;
+
+ for (i = 0; i < ARRAY_SIZE(s5k6aa_intervals); i++) {
+ const struct s5k6aa_interval *iv = &s5k6aa_intervals[i];
+
+ if (mbus_fmt->width > iv->size.width ||
+ mbus_fmt->height > iv->size.height)
+ continue;
+
+ err = abs(iv->reg_fr_time - fr_time);
+ if (err < min_err) {
+ fiv = iv;
+ min_err = err;
+ }
+ }
+ s5k6aa->fiv = fiv;
+
+ v4l2_dbg(1, debug, &s5k6aa->sd, "Changed frame interval to %d us\n",
+ fiv->reg_fr_time * 100);
+ return 0;
+}
+
+static int s5k6aa_s_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ int ret;
+
+ v4l2_dbg(1, debug, sd, "Setting %d/%d frame interval\n",
+ fi->interval.numerator, fi->interval.denominator);
+
+ mutex_lock(&s5k6aa->lock);
+ ret = __s5k6aa_set_frame_interval(s5k6aa, fi);
+ s5k6aa->apply_cfg = 1;
+
+ mutex_unlock(&s5k6aa->lock);
+ return ret;
+}
+
+/*
+ * V4L2 subdev pad level and video operations
+ */
+static int s5k6aa_enum_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_interval_enum *fie)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ const struct s5k6aa_interval *fi;
+ int ret = 0;
+
+ if (fie->index > ARRAY_SIZE(s5k6aa_intervals))
+ return -EINVAL;
+
+ v4l_bound_align_image(&fie->width, S5K6AA_WIN_WIDTH_MIN,
+ S5K6AA_WIN_WIDTH_MAX, 1,
+ &fie->height, S5K6AA_WIN_HEIGHT_MIN,
+ S5K6AA_WIN_HEIGHT_MAX, 1, 0);
+
+ mutex_lock(&s5k6aa->lock);
+ fi = &s5k6aa_intervals[fie->index];
+ if (fie->width > fi->size.width || fie->height > fi->size.height)
+ ret = -EINVAL;
+ else
+ fie->interval = fi->interval;
+ mutex_unlock(&s5k6aa->lock);
+
+ return ret;
+}
+
+static int s5k6aa_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index >= ARRAY_SIZE(s5k6aa_formats))
+ return -EINVAL;
+
+ code->code = s5k6aa_formats[code->index].code;
+ return 0;
+}
+
+static int s5k6aa_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ int i = ARRAY_SIZE(s5k6aa_formats);
+
+ if (fse->index > 0)
+ return -EINVAL;
+
+ while (--i)
+ if (fse->code == s5k6aa_formats[i].code)
+ break;
+
+ fse->code = s5k6aa_formats[i].code;
+ fse->min_width = S5K6AA_WIN_WIDTH_MIN;
+ fse->max_width = S5K6AA_WIN_WIDTH_MAX;
+ fse->max_height = S5K6AA_WIN_HEIGHT_MIN;
+ fse->min_height = S5K6AA_WIN_HEIGHT_MAX;
+
+ return 0;
+}
+
+static struct v4l2_rect *
+__s5k6aa_get_crop_rect(struct s5k6aa *s5k6aa, struct v4l2_subdev_fh *fh,
+ enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ return &s5k6aa->ccd_rect;
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_crop(fh, 0);
+
+ return NULL;
+}
+
+static void s5k6aa_try_format(struct s5k6aa *s5k6aa,
+ struct v4l2_mbus_framefmt *mf)
+{
+ unsigned int index;
+
+ v4l_bound_align_image(&mf->width, S5K6AA_WIN_WIDTH_MIN,
+ S5K6AA_WIN_WIDTH_MAX, 1,
+ &mf->height, S5K6AA_WIN_HEIGHT_MIN,
+ S5K6AA_WIN_HEIGHT_MAX, 1, 0);
+
+ if (mf->colorspace != V4L2_COLORSPACE_JPEG &&
+ mf->colorspace != V4L2_COLORSPACE_REC709)
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+
+ index = s5k6aa_get_pixfmt_index(s5k6aa, mf);
+
+ mf->colorspace = s5k6aa_formats[index].colorspace;
+ mf->code = s5k6aa_formats[index].code;
+ mf->field = V4L2_FIELD_NONE;
+}
+
+static int s5k6aa_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ struct v4l2_mbus_framefmt *mf;
+
+ memset(fmt->reserved, 0, sizeof(fmt->reserved));
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mf = v4l2_subdev_get_try_format(fh, 0);
+ fmt->format = *mf;
+ return 0;
+ }
+
+ mutex_lock(&s5k6aa->lock);
+ fmt->format = s5k6aa->preset->mbus_fmt;
+ mutex_unlock(&s5k6aa->lock);
+
+ return 0;
+}
+
+static int s5k6aa_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ struct s5k6aa_preset *preset = s5k6aa->preset;
+ struct v4l2_mbus_framefmt *mf;
+ struct v4l2_rect *crop;
+ int ret = 0;
+
+ mutex_lock(&s5k6aa->lock);
+ s5k6aa_try_format(s5k6aa, &fmt->format);
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+ crop = v4l2_subdev_get_try_crop(fh, 0);
+ } else {
+ if (s5k6aa->streaming) {
+ ret = -EBUSY;
+ } else {
+ mf = &preset->mbus_fmt;
+ crop = &s5k6aa->ccd_rect;
+ s5k6aa->apply_cfg = 1;
+ }
+ }
+
+ if (ret == 0) {
+ struct v4l2_subdev_frame_interval fiv = {
+ .interval = {0, 1}
+ };
+
+ *mf = fmt->format;
+ /*
+ * Make sure the crop window is valid, i.e. its size is
+ * greater than the output window, as the ISP supports
+ * only down-scaling.
+ */
+ crop->width = clamp_t(unsigned int, crop->width, mf->width,
+ S5K6AA_WIN_WIDTH_MAX);
+ crop->height = clamp_t(unsigned int, crop->height, mf->height,
+ S5K6AA_WIN_HEIGHT_MAX);
+ crop->left = clamp_t(unsigned int, crop->left, 0,
+ S5K6AA_WIN_WIDTH_MAX - crop->width);
+ crop->top = clamp_t(unsigned int, crop->top, 0,
+ S5K6AA_WIN_HEIGHT_MAX - crop->height);
+
+ /* Reset to minimum possible frame interval */
+ ret = __s5k6aa_set_frame_interval(s5k6aa, &fiv);
+ }
+ mutex_unlock(&s5k6aa->lock);
+
+ return ret;
+}
+
+static int s5k6aa_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ struct v4l2_rect *rect;
+
+ memset(crop->reserved, 0, sizeof(crop->reserved));
+ mutex_lock(&s5k6aa->lock);
+
+ rect = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which);
+ if (rect)
+ crop->rect = *rect;
+
+ mutex_unlock(&s5k6aa->lock);
+
+ v4l2_dbg(1, debug, sd, "Current crop rectangle: (%d,%d)/%dx%d\n",
+ rect->left, rect->top, rect->width, rect->height);
+
+ return 0;
+}
+
+static int s5k6aa_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ struct v4l2_mbus_framefmt *mf;
+ unsigned int max_x, max_y;
+ struct v4l2_rect *crop_r;
+
+ mutex_lock(&s5k6aa->lock);
+ crop_r = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which);
+
+ if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ mf = &s5k6aa->preset->mbus_fmt;
+ s5k6aa->apply_crop = 1;
+ } else {
+ mf = v4l2_subdev_get_try_format(fh, 0);
+ }
+ v4l_bound_align_image(&crop->rect.width, mf->width,
+ S5K6AA_WIN_WIDTH_MAX, 1,
+ &crop->rect.height, mf->height,
+ S5K6AA_WIN_HEIGHT_MAX, 1, 0);
+
+ max_x = (S5K6AA_WIN_WIDTH_MAX - crop->rect.width) & ~1;
+ max_y = (S5K6AA_WIN_HEIGHT_MAX - crop->rect.height) & ~1;
+
+ crop->rect.left = clamp_t(unsigned int, crop->rect.left, 0, max_x);
+ crop->rect.top = clamp_t(unsigned int, crop->rect.top, 0, max_y);
+
+ *crop_r = crop->rect;
+
+ mutex_unlock(&s5k6aa->lock);
+
+ v4l2_dbg(1, debug, sd, "Set crop rectangle: (%d,%d)/%dx%d\n",
+ crop_r->left, crop_r->top, crop_r->width, crop_r->height);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops s5k6aa_pad_ops = {
+ .enum_mbus_code = s5k6aa_enum_mbus_code,
+ .enum_frame_size = s5k6aa_enum_frame_size,
+ .enum_frame_interval = s5k6aa_enum_frame_interval,
+ .get_fmt = s5k6aa_get_fmt,
+ .set_fmt = s5k6aa_set_fmt,
+ .get_crop = s5k6aa_get_crop,
+ .set_crop = s5k6aa_set_crop,
+};
+
+static const struct v4l2_subdev_video_ops s5k6aa_video_ops = {
+ .g_frame_interval = s5k6aa_g_frame_interval,
+ .s_frame_interval = s5k6aa_s_frame_interval,
+ .s_stream = s5k6aa_s_stream,
+};
+
+/*
+ * V4L2 subdev controls
+ */
+
+static int s5k6aa_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ int idx, err = 0;
+
+ v4l2_dbg(1, debug, sd, "ctrl: 0x%x, value: %d\n", ctrl->id, ctrl->val);
+
+ mutex_lock(&s5k6aa->lock);
+ /*
+ * If the device is not powered up by the host driver do
+ * not apply any controls to H/W at this time. Instead
+ * the controls will be restored right after power-up.
+ */
+ if (s5k6aa->power == 0)
+ goto unlock;
+ idx = s5k6aa->preset->index;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ err = s5k6aa_set_awb(s5k6aa, ctrl->val);
+ break;
+
+ case V4L2_CID_BRIGHTNESS:
+ err = s5k6aa_write(client, REG_USER_BRIGHTNESS, ctrl->val);
+ break;
+
+ case V4L2_CID_COLORFX:
+ err = s5k6aa_set_colorfx(s5k6aa, ctrl->val);
+ break;
+
+ case V4L2_CID_CONTRAST:
+ err = s5k6aa_write(client, REG_USER_CONTRAST, ctrl->val);
+ break;
+
+ case V4L2_CID_EXPOSURE_AUTO:
+ err = s5k6aa_set_auto_exposure(s5k6aa, ctrl->val);
+ break;
+
+ case V4L2_CID_HFLIP:
+ err = s5k6aa_set_mirror(s5k6aa, ctrl->val);
+ if (err)
+ break;
+ err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1);
+ break;
+
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ err = s5k6aa_set_anti_flicker(s5k6aa, ctrl->val);
+ break;
+
+ case V4L2_CID_SATURATION:
+ err = s5k6aa_write(client, REG_USER_SATURATION, ctrl->val);
+ break;
+
+ case V4L2_CID_SHARPNESS:
+ err = s5k6aa_write(client, REG_USER_SHARPBLUR, ctrl->val);
+ break;
+
+ case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
+ err = s5k6aa_write(client, REG_P_COLORTEMP(idx), ctrl->val);
+ if (err)
+ break;
+ err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1);
+ break;
+ }
+unlock:
+ mutex_unlock(&s5k6aa->lock);
+ return err;
+}
+
+static const struct v4l2_ctrl_ops s5k6aa_ctrl_ops = {
+ .s_ctrl = s5k6aa_s_ctrl,
+};
+
+static int s5k6aa_log_status(struct v4l2_subdev *sd)
+{
+ v4l2_ctrl_handler_log_status(sd->ctrl_handler, sd->name);
+ return 0;
+}
+
+#define V4L2_CID_RED_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1001)
+#define V4L2_CID_GREEN_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1002)
+#define V4L2_CID_BLUE_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1003)
+
+static const struct v4l2_ctrl_config s5k6aa_ctrls[] = {
+ {
+ .ops = &s5k6aa_ctrl_ops,
+ .id = V4L2_CID_RED_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain, Red",
+ .min = 0,
+ .max = 256,
+ .def = 127,
+ .step = 1,
+ }, {
+ .ops = &s5k6aa_ctrl_ops,
+ .id = V4L2_CID_GREEN_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain, Green",
+ .min = 0,
+ .max = 256,
+ .def = 127,
+ .step = 1,
+ }, {
+ .ops = &s5k6aa_ctrl_ops,
+ .id = V4L2_CID_BLUE_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain, Blue",
+ .min = 0,
+ .max = 256,
+ .def = 127,
+ .step = 1,
+ },
+};
+
+static int s5k6aa_initialize_ctrls(struct s5k6aa *s5k6aa)
+{
+ const struct v4l2_ctrl_ops *ops = &s5k6aa_ctrl_ops;
+ struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls;
+ struct v4l2_ctrl_handler *hdl = &ctrls->handler;
+
+ int ret = v4l2_ctrl_handler_init(hdl, 16);
+ if (ret)
+ return ret;
+ /* Auto white balance cluster */
+ ctrls->awb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE,
+ 0, 1, 1, 1);
+ ctrls->gain_red = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[0], NULL);
+ ctrls->gain_green = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[1], NULL);
+ ctrls->gain_blue = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[2], NULL);
+ v4l2_ctrl_auto_cluster(4, &ctrls->awb, 0, false);
+
+ ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
+ ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_cluster(2, &ctrls->hflip);
+
+ ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops,
+ V4L2_CID_EXPOSURE_AUTO,
+ V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO);
+ /* Exposure time: x 1 us */
+ ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
+ 0, 6000000U, 1, 100000U);
+ /* Total gain: 256 <=> 1x */
+ ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN,
+ 0, 256, 1, 256);
+ v4l2_ctrl_auto_cluster(3, &ctrls->auto_exp, 0, false);
+
+ v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
+ V4L2_CID_POWER_LINE_FREQUENCY_AUTO);
+
+ v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX,
+ V4L2_COLORFX_SKY_BLUE, ~0x6f, V4L2_COLORFX_NONE);
+
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_WHITE_BALANCE_TEMPERATURE,
+ 0, 256, 1, 0);
+
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, -127, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -127, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -127, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS, -127, 127, 1, 0);
+
+ if (hdl->error) {
+ ret = hdl->error;
+ v4l2_ctrl_handler_free(hdl);
+ return ret;
+ }
+
+ s5k6aa->sd.ctrl_handler = hdl;
+ return 0;
+}
+
+/*
+ * V4L2 subdev internal operations
+ */
+static int s5k6aa_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
+ struct v4l2_rect *crop = v4l2_subdev_get_try_crop(fh, 0);
+
+ format->colorspace = s5k6aa_formats[0].colorspace;
+ format->code = s5k6aa_formats[0].code;
+ format->width = S5K6AA_OUT_WIDTH_DEF;
+ format->height = S5K6AA_OUT_HEIGHT_DEF;
+ format->field = V4L2_FIELD_NONE;
+
+ crop->width = S5K6AA_WIN_WIDTH_MAX;
+ crop->height = S5K6AA_WIN_HEIGHT_MAX;
+ crop->left = 0;
+ crop->top = 0;
+
+ return 0;
+}
+
+int s5k6aa_check_fw_revision(struct s5k6aa *s5k6aa)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ u16 api_ver = 0, fw_rev = 0;
+
+ int ret = s5k6aa_set_ahb_address(client);
+
+ if (!ret)
+ ret = s5k6aa_read(client, REG_FW_APIVER, &api_ver);
+ if (!ret)
+ ret = s5k6aa_read(client, REG_FW_REVISION, &fw_rev);
+ if (ret) {
+ v4l2_err(&s5k6aa->sd, "FW revision check failed!\n");
+ return ret;
+ }
+
+ v4l2_info(&s5k6aa->sd, "FW API ver.: 0x%X, FW rev.: 0x%X\n",
+ api_ver, fw_rev);
+
+ return api_ver == S5K6AAFX_FW_APIVER ? 0 : -ENODEV;
+}
+
+static int s5k6aa_registered(struct v4l2_subdev *sd)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ int ret;
+
+ mutex_lock(&s5k6aa->lock);
+ ret = __s5k6aa_power_on(s5k6aa);
+ if (!ret) {
+ msleep(100);
+ ret = s5k6aa_check_fw_revision(s5k6aa);
+ __s5k6aa_power_off(s5k6aa);
+ }
+ mutex_unlock(&s5k6aa->lock);
+
+ return ret;
+}
+
+static const struct v4l2_subdev_internal_ops s5k6aa_subdev_internal_ops = {
+ .registered = s5k6aa_registered,
+ .open = s5k6aa_open,
+};
+
+static const struct v4l2_subdev_core_ops s5k6aa_core_ops = {
+ .s_power = s5k6aa_set_power,
+ .log_status = s5k6aa_log_status,
+};
+
+static const struct v4l2_subdev_ops s5k6aa_subdev_ops = {
+ .core = &s5k6aa_core_ops,
+ .pad = &s5k6aa_pad_ops,
+ .video = &s5k6aa_video_ops,
+};
+
+/*
+ * GPIO setup
+ */
+static int s5k6aa_configure_gpio(int nr, int val, const char *name)
+{
+ unsigned long flags = val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
+ int ret;
+
+ if (!gpio_is_valid(nr))
+ return 0;
+ ret = gpio_request_one(nr, flags, name);
+ if (!ret)
+ gpio_export(nr, 0);
+ return ret;
+}
+
+static void s5k6aa_free_gpios(struct s5k6aa *s5k6aa)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(s5k6aa->gpio); i++) {
+ if (!gpio_is_valid(s5k6aa->gpio[i].gpio))
+ continue;
+ gpio_free(s5k6aa->gpio[i].gpio);
+ s5k6aa->gpio[i].gpio = -EINVAL;
+ }
+}
+
+static int s5k6aa_configure_gpios(struct s5k6aa *s5k6aa,
+ const struct s5k6aa_platform_data *pdata)
+{
+ const struct s5k6aa_gpio *gpio = &pdata->gpio_stby;
+ int ret;
+
+ s5k6aa->gpio[STBY].gpio = -EINVAL;
+ s5k6aa->gpio[RST].gpio = -EINVAL;
+
+ ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_STBY");
+ if (ret) {
+ s5k6aa_free_gpios(s5k6aa);
+ return ret;
+ }
+ s5k6aa->gpio[STBY] = *gpio;
+ if (gpio_is_valid(gpio->gpio))
+ gpio_set_value(gpio->gpio, 0);
+
+ gpio = &pdata->gpio_reset;
+ ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_RST");
+ if (ret) {
+ s5k6aa_free_gpios(s5k6aa);
+ return ret;
+ }
+ s5k6aa->gpio[RST] = *gpio;
+ if (gpio_is_valid(gpio->gpio))
+ gpio_set_value(gpio->gpio, 0);
+
+ return 0;
+}
+
+static int s5k6aa_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct s5k6aa_platform_data *pdata = client->dev.platform_data;
+ struct v4l2_subdev *sd;
+ struct s5k6aa *s5k6aa;
+ int i, ret;
+
+ if (pdata == NULL) {
+ dev_err(&client->dev, "Platform data not specified\n");
+ return -EINVAL;
+ }
+
+ if (pdata->mclk_frequency == 0) {
+ dev_err(&client->dev, "MCLK frequency not specified\n");
+ return -EINVAL;
+ }
+
+ s5k6aa = kzalloc(sizeof(*s5k6aa), GFP_KERNEL);
+ if (!s5k6aa)
+ return -ENOMEM;
+
+ mutex_init(&s5k6aa->lock);
+
+ s5k6aa->mclk_frequency = pdata->mclk_frequency;
+ s5k6aa->bus_type = pdata->bus_type;
+ s5k6aa->mipi_lanes = pdata->nlanes;
+ s5k6aa->s_power = pdata->set_power;
+ s5k6aa->inv_hflip = pdata->horiz_flip;
+ s5k6aa->inv_vflip = pdata->vert_flip;
+
+ sd = &s5k6aa->sd;
+ v4l2_i2c_subdev_init(sd, client, &s5k6aa_subdev_ops);
+ strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name));
+
+ sd->internal_ops = &s5k6aa_subdev_internal_ops;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ s5k6aa->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
+ ret = media_entity_init(&sd->entity, 1, &s5k6aa->pad, 0);
+ if (ret)
+ goto out_err1;
+
+ ret = s5k6aa_configure_gpios(s5k6aa, pdata);
+ if (ret)
+ goto out_err2;
+
+ for (i = 0; i < S5K6AA_NUM_SUPPLIES; i++)
+ s5k6aa->supplies[i].supply = s5k6aa_supply_names[i];
+
+ ret = regulator_bulk_get(&client->dev, S5K6AA_NUM_SUPPLIES,
+ s5k6aa->supplies);
+ if (ret) {
+ dev_err(&client->dev, "Failed to get regulators\n");
+ goto out_err3;
+ }
+
+ ret = s5k6aa_initialize_ctrls(s5k6aa);
+ if (ret)
+ goto out_err4;
+
+ s5k6aa_presets_data_init(s5k6aa);
+
+ s5k6aa->ccd_rect.width = S5K6AA_WIN_WIDTH_MAX;
+ s5k6aa->ccd_rect.height = S5K6AA_WIN_HEIGHT_MAX;
+ s5k6aa->ccd_rect.left = 0;
+ s5k6aa->ccd_rect.top = 0;
+
+ return 0;
+
+out_err4:
+ regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies);
+out_err3:
+ s5k6aa_free_gpios(s5k6aa);
+out_err2:
+ media_entity_cleanup(&s5k6aa->sd.entity);
+out_err1:
+ kfree(s5k6aa);
+ return ret;
+}
+
+static int s5k6aa_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+ media_entity_cleanup(&sd->entity);
+ regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies);
+ s5k6aa_free_gpios(s5k6aa);
+ kfree(s5k6aa);
+
+ return 0;
+}
+
+static const struct i2c_device_id s5k6aa_id[] = {
+ { DRIVER_NAME, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, s5k6aa_id);
+
+
+static struct i2c_driver s5k6aa_i2c_driver = {
+ .driver = {
+ .name = DRIVER_NAME
+ },
+ .probe = s5k6aa_probe,
+ .remove = s5k6aa_remove,
+ .id_table = s5k6aa_id,
+};
+
+module_i2c_driver(s5k6aa_i2c_driver);
+
+MODULE_DESCRIPTION("Samsung S5K6AA(FX) SXGA camera driver");
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/s5p-fimc/Makefile b/drivers/media/video/s5p-fimc/Makefile
new file mode 100644
index 00000000..33dec7f8
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/Makefile
@@ -0,0 +1,5 @@
+s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-capture.o fimc-mdevice.o
+s5p-csis-objs := mipi-csis.o
+
+obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS) += s5p-csis.o
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc.o
diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c
new file mode 100644
index 00000000..7e9b2c61
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/fimc-capture.c
@@ -0,0 +1,1583 @@
+/*
+ * Samsung S5P/EXYNOS4 SoC series camera interface (camera capture) driver
+ *
+ * Copyright (C) 2010 - 2011 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki, <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/bug.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/pm_runtime.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "fimc-mdevice.h"
+#include "fimc-core.h"
+
+static int fimc_init_capture(struct fimc_dev *fimc)
+{
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct fimc_sensor_info *sensor;
+ unsigned long flags;
+ int ret = 0;
+
+ if (fimc->pipeline.sensor == NULL || ctx == NULL)
+ return -ENXIO;
+ if (ctx->s_frame.fmt == NULL)
+ return -EINVAL;
+
+ sensor = v4l2_get_subdev_hostdata(fimc->pipeline.sensor);
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ fimc_prepare_dma_offset(ctx, &ctx->d_frame);
+ fimc_set_yuv_order(ctx);
+
+ fimc_hw_set_camera_polarity(fimc, sensor->pdata);
+ fimc_hw_set_camera_type(fimc, sensor->pdata);
+ fimc_hw_set_camera_source(fimc, sensor->pdata);
+ fimc_hw_set_camera_offset(fimc, &ctx->s_frame);
+
+ ret = fimc_set_scaler_info(ctx);
+ if (!ret) {
+ fimc_hw_set_input_path(ctx);
+ fimc_hw_set_prescaler(ctx);
+ fimc_hw_set_mainscaler(ctx);
+ fimc_hw_set_target_format(ctx);
+ fimc_hw_set_rotation(ctx);
+ fimc_hw_set_effect(ctx, false);
+ fimc_hw_set_output_path(ctx);
+ fimc_hw_set_out_dma(ctx);
+ if (fimc->variant->has_alpha)
+ fimc_hw_set_rgb_alpha(ctx);
+ clear_bit(ST_CAPT_APPLY_CFG, &fimc->state);
+ }
+ spin_unlock_irqrestore(&fimc->slock, flags);
+ return ret;
+}
+
+static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend)
+{
+ struct fimc_vid_cap *cap = &fimc->vid_cap;
+ struct fimc_vid_buffer *buf;
+ unsigned long flags;
+ bool streaming;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ streaming = fimc->state & (1 << ST_CAPT_ISP_STREAM);
+
+ fimc->state &= ~(1 << ST_CAPT_RUN | 1 << ST_CAPT_SHUT |
+ 1 << ST_CAPT_STREAM | 1 << ST_CAPT_ISP_STREAM);
+ if (!suspend)
+ fimc->state &= ~(1 << ST_CAPT_PEND | 1 << ST_CAPT_SUSPENDED);
+
+ /* Release unused buffers */
+ while (!suspend && !list_empty(&cap->pending_buf_q)) {
+ buf = fimc_pending_queue_pop(cap);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+ /* If suspending put unused buffers onto pending queue */
+ while (!list_empty(&cap->active_buf_q)) {
+ buf = fimc_active_queue_pop(cap);
+ if (suspend)
+ fimc_pending_queue_add(cap, buf);
+ else
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+ set_bit(ST_CAPT_SUSPENDED, &fimc->state);
+
+ fimc_hw_reset(fimc);
+ cap->buf_index = 0;
+
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ if (streaming)
+ return fimc_pipeline_s_stream(fimc, 0);
+ else
+ return 0;
+}
+
+static int fimc_stop_capture(struct fimc_dev *fimc, bool suspend)
+{
+ unsigned long flags;
+
+ if (!fimc_capture_active(fimc))
+ return 0;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ set_bit(ST_CAPT_SHUT, &fimc->state);
+ fimc_deactivate_capture(fimc);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ wait_event_timeout(fimc->irq_queue,
+ !test_bit(ST_CAPT_SHUT, &fimc->state),
+ (2*HZ/10)); /* 200 ms */
+
+ return fimc_capture_state_cleanup(fimc, suspend);
+}
+
+/**
+ * fimc_capture_config_update - apply the camera interface configuration
+ *
+ * To be called from within the interrupt handler with fimc.slock
+ * spinlock held. It updates the camera pixel crop, rotation and
+ * image flip in H/W.
+ */
+int fimc_capture_config_update(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ int ret;
+
+ if (!test_bit(ST_CAPT_APPLY_CFG, &fimc->state))
+ return 0;
+
+ spin_lock(&ctx->slock);
+ fimc_hw_set_camera_offset(fimc, &ctx->s_frame);
+ ret = fimc_set_scaler_info(ctx);
+ if (ret == 0) {
+ fimc_hw_set_prescaler(ctx);
+ fimc_hw_set_mainscaler(ctx);
+ fimc_hw_set_target_format(ctx);
+ fimc_hw_set_rotation(ctx);
+ fimc_prepare_dma_offset(ctx, &ctx->d_frame);
+ fimc_hw_set_out_dma(ctx);
+ if (fimc->variant->has_alpha)
+ fimc_hw_set_rgb_alpha(ctx);
+ clear_bit(ST_CAPT_APPLY_CFG, &fimc->state);
+ }
+ spin_unlock(&ctx->slock);
+ return ret;
+}
+
+static int start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct fimc_ctx *ctx = q->drv_priv;
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+ int min_bufs;
+ int ret;
+
+ vid_cap->frame_count = 0;
+
+ ret = fimc_init_capture(fimc);
+ if (ret)
+ goto error;
+
+ set_bit(ST_CAPT_PEND, &fimc->state);
+
+ min_bufs = fimc->vid_cap.reqbufs_count > 1 ? 2 : 1;
+
+ if (vid_cap->active_buf_cnt >= min_bufs &&
+ !test_and_set_bit(ST_CAPT_STREAM, &fimc->state)) {
+ fimc_activate_capture(ctx);
+
+ if (!test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state))
+ fimc_pipeline_s_stream(fimc, 1);
+ }
+
+ return 0;
+error:
+ fimc_capture_state_cleanup(fimc, false);
+ return ret;
+}
+
+static int stop_streaming(struct vb2_queue *q)
+{
+ struct fimc_ctx *ctx = q->drv_priv;
+ struct fimc_dev *fimc = ctx->fimc_dev;
+
+ if (!fimc_capture_active(fimc))
+ return -EINVAL;
+
+ return fimc_stop_capture(fimc, false);
+}
+
+int fimc_capture_suspend(struct fimc_dev *fimc)
+{
+ bool suspend = fimc_capture_busy(fimc);
+
+ int ret = fimc_stop_capture(fimc, suspend);
+ if (ret)
+ return ret;
+ return fimc_pipeline_shutdown(fimc);
+}
+
+static void buffer_queue(struct vb2_buffer *vb);
+
+int fimc_capture_resume(struct fimc_dev *fimc)
+{
+ struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+ struct fimc_vid_buffer *buf;
+ int i;
+
+ if (!test_and_clear_bit(ST_CAPT_SUSPENDED, &fimc->state))
+ return 0;
+
+ INIT_LIST_HEAD(&fimc->vid_cap.active_buf_q);
+ vid_cap->buf_index = 0;
+ fimc_pipeline_initialize(fimc, &fimc->vid_cap.vfd->entity,
+ false);
+ fimc_init_capture(fimc);
+
+ clear_bit(ST_CAPT_SUSPENDED, &fimc->state);
+
+ for (i = 0; i < vid_cap->reqbufs_count; i++) {
+ if (list_empty(&vid_cap->pending_buf_q))
+ break;
+ buf = fimc_pending_queue_pop(vid_cap);
+ buffer_queue(&buf->vb);
+ }
+ return 0;
+
+}
+
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *allocators[])
+{
+ const struct v4l2_pix_format_mplane *pixm = NULL;
+ struct fimc_ctx *ctx = vq->drv_priv;
+ struct fimc_frame *frame = &ctx->d_frame;
+ struct fimc_fmt *fmt = frame->fmt;
+ unsigned long wh;
+ int i;
+
+ if (pfmt) {
+ pixm = &pfmt->fmt.pix_mp;
+ fmt = fimc_find_format(&pixm->pixelformat, NULL,
+ FMT_FLAGS_CAM | FMT_FLAGS_M2M, -1);
+ wh = pixm->width * pixm->height;
+ } else {
+ wh = frame->f_width * frame->f_height;
+ }
+
+ if (fmt == NULL)
+ return -EINVAL;
+
+ *num_planes = fmt->memplanes;
+
+ for (i = 0; i < fmt->memplanes; i++) {
+ unsigned int size = (wh * fmt->depth[i]) / 8;
+ if (pixm)
+ sizes[i] = max(size, pixm->plane_fmt[i].sizeimage);
+ else
+ sizes[i] = size;
+ allocators[i] = ctx->fimc_dev->alloc_ctx;
+ }
+
+ return 0;
+}
+
+static int buffer_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct fimc_ctx *ctx = vq->drv_priv;
+ int i;
+
+ if (ctx->d_frame.fmt == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < ctx->d_frame.fmt->memplanes; i++) {
+ unsigned long size = ctx->d_frame.payload[i];
+
+ if (vb2_plane_size(vb, i) < size) {
+ v4l2_err(ctx->fimc_dev->vid_cap.vfd,
+ "User buffer too small (%ld < %ld)\n",
+ vb2_plane_size(vb, i), size);
+ return -EINVAL;
+ }
+ vb2_set_plane_payload(vb, i, size);
+ }
+
+ return 0;
+}
+
+static void buffer_queue(struct vb2_buffer *vb)
+{
+ struct fimc_vid_buffer *buf
+ = container_of(vb, struct fimc_vid_buffer, vb);
+ struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+ unsigned long flags;
+ int min_bufs;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ fimc_prepare_addr(ctx, &buf->vb, &ctx->d_frame, &buf->paddr);
+
+ if (!test_bit(ST_CAPT_SUSPENDED, &fimc->state) &&
+ !test_bit(ST_CAPT_STREAM, &fimc->state) &&
+ vid_cap->active_buf_cnt < FIMC_MAX_OUT_BUFS) {
+ /* Setup the buffer directly for processing. */
+ int buf_id = (vid_cap->reqbufs_count == 1) ? -1 :
+ vid_cap->buf_index;
+
+ fimc_hw_set_output_addr(fimc, &buf->paddr, buf_id);
+ buf->index = vid_cap->buf_index;
+ fimc_active_queue_add(vid_cap, buf);
+
+ if (++vid_cap->buf_index >= FIMC_MAX_OUT_BUFS)
+ vid_cap->buf_index = 0;
+ } else {
+ fimc_pending_queue_add(vid_cap, buf);
+ }
+
+ min_bufs = vid_cap->reqbufs_count > 1 ? 2 : 1;
+
+
+ if (vb2_is_streaming(&vid_cap->vbq) &&
+ vid_cap->active_buf_cnt >= min_bufs &&
+ !test_and_set_bit(ST_CAPT_STREAM, &fimc->state)) {
+ fimc_activate_capture(ctx);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ if (!test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state))
+ fimc_pipeline_s_stream(fimc, 1);
+ return;
+ }
+ spin_unlock_irqrestore(&fimc->slock, flags);
+}
+
+static void fimc_lock(struct vb2_queue *vq)
+{
+ struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
+ mutex_lock(&ctx->fimc_dev->lock);
+}
+
+static void fimc_unlock(struct vb2_queue *vq)
+{
+ struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
+ mutex_unlock(&ctx->fimc_dev->lock);
+}
+
+static struct vb2_ops fimc_capture_qops = {
+ .queue_setup = queue_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .wait_prepare = fimc_unlock,
+ .wait_finish = fimc_lock,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
+};
+
+/**
+ * fimc_capture_ctrls_create - initialize the control handler
+ * Initialize the capture video node control handler and fill it
+ * with the FIMC controls. Inherit any sensor's controls if the
+ * 'user_subdev_api' flag is false (default behaviour).
+ * This function need to be called with the graph mutex held.
+ */
+int fimc_capture_ctrls_create(struct fimc_dev *fimc)
+{
+ struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+ int ret;
+
+ if (WARN_ON(vid_cap->ctx == NULL))
+ return -ENXIO;
+ if (vid_cap->ctx->ctrls_rdy)
+ return 0;
+
+ ret = fimc_ctrls_create(vid_cap->ctx);
+ if (ret || vid_cap->user_subdev_api)
+ return ret;
+
+ return v4l2_ctrl_add_handler(&vid_cap->ctx->ctrl_handler,
+ fimc->pipeline.sensor->ctrl_handler);
+}
+
+static int fimc_capture_set_default_format(struct fimc_dev *fimc);
+
+static int fimc_capture_open(struct file *file)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+ int ret = v4l2_fh_open(file);
+
+ if (ret)
+ return ret;
+
+ dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state);
+
+ /* Return if the corresponding video mem2mem node is already opened. */
+ if (fimc_m2m_active(fimc))
+ return -EBUSY;
+
+ set_bit(ST_CAPT_BUSY, &fimc->state);
+ pm_runtime_get_sync(&fimc->pdev->dev);
+
+ if (++fimc->vid_cap.refcnt == 1) {
+ ret = fimc_pipeline_initialize(fimc,
+ &fimc->vid_cap.vfd->entity, true);
+ if (ret < 0) {
+ dev_err(&fimc->pdev->dev,
+ "Video pipeline initialization failed\n");
+ pm_runtime_put_sync(&fimc->pdev->dev);
+ fimc->vid_cap.refcnt--;
+ v4l2_fh_release(file);
+ clear_bit(ST_CAPT_BUSY, &fimc->state);
+ return ret;
+ }
+ ret = fimc_capture_ctrls_create(fimc);
+
+ if (!ret && !fimc->vid_cap.user_subdev_api)
+ ret = fimc_capture_set_default_format(fimc);
+ }
+ return ret;
+}
+
+static int fimc_capture_close(struct file *file)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+
+ dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state);
+
+ if (--fimc->vid_cap.refcnt == 0) {
+ clear_bit(ST_CAPT_BUSY, &fimc->state);
+ fimc_stop_capture(fimc, false);
+ fimc_pipeline_shutdown(fimc);
+ clear_bit(ST_CAPT_SUSPENDED, &fimc->state);
+ }
+
+ pm_runtime_put(&fimc->pdev->dev);
+
+ if (fimc->vid_cap.refcnt == 0) {
+ vb2_queue_release(&fimc->vid_cap.vbq);
+ fimc_ctrls_delete(fimc->vid_cap.ctx);
+ }
+ return v4l2_fh_release(file);
+}
+
+static unsigned int fimc_capture_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+
+ return vb2_poll(&fimc->vid_cap.vbq, file, wait);
+}
+
+static int fimc_capture_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+
+ return vb2_mmap(&fimc->vid_cap.vbq, vma);
+}
+
+static const struct v4l2_file_operations fimc_capture_fops = {
+ .owner = THIS_MODULE,
+ .open = fimc_capture_open,
+ .release = fimc_capture_close,
+ .poll = fimc_capture_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = fimc_capture_mmap,
+};
+
+/*
+ * Format and crop negotiation helpers
+ */
+
+static struct fimc_fmt *fimc_capture_try_format(struct fimc_ctx *ctx,
+ u32 *width, u32 *height,
+ u32 *code, u32 *fourcc, int pad)
+{
+ bool rotation = ctx->rotation == 90 || ctx->rotation == 270;
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct samsung_fimc_variant *var = fimc->variant;
+ struct fimc_pix_limit *pl = var->pix_limit;
+ struct fimc_frame *dst = &ctx->d_frame;
+ u32 depth, min_w, max_w, min_h, align_h = 3;
+ u32 mask = FMT_FLAGS_CAM;
+ struct fimc_fmt *ffmt;
+
+ /* Color conversion from/to JPEG is not supported */
+ if (code && ctx->s_frame.fmt && pad == FIMC_SD_PAD_SOURCE &&
+ fimc_fmt_is_jpeg(ctx->s_frame.fmt->color))
+ *code = V4L2_MBUS_FMT_JPEG_1X8;
+
+ if (fourcc && *fourcc != V4L2_PIX_FMT_JPEG && pad != FIMC_SD_PAD_SINK)
+ mask |= FMT_FLAGS_M2M;
+
+ ffmt = fimc_find_format(fourcc, code, mask, 0);
+ if (WARN_ON(!ffmt))
+ return NULL;
+ if (code)
+ *code = ffmt->mbus_code;
+ if (fourcc)
+ *fourcc = ffmt->fourcc;
+
+ if (pad == FIMC_SD_PAD_SINK) {
+ max_w = fimc_fmt_is_jpeg(ffmt->color) ?
+ pl->scaler_dis_w : pl->scaler_en_w;
+ /* Apply the camera input interface pixel constraints */
+ v4l_bound_align_image(width, max_t(u32, *width, 32), max_w, 4,
+ height, max_t(u32, *height, 32),
+ FIMC_CAMIF_MAX_HEIGHT,
+ fimc_fmt_is_jpeg(ffmt->color) ? 3 : 1,
+ 0);
+ return ffmt;
+ }
+ /* Can't scale or crop in transparent (JPEG) transfer mode */
+ if (fimc_fmt_is_jpeg(ffmt->color)) {
+ *width = ctx->s_frame.f_width;
+ *height = ctx->s_frame.f_height;
+ return ffmt;
+ }
+ /* Apply the scaler and the output DMA constraints */
+ max_w = rotation ? pl->out_rot_en_w : pl->out_rot_dis_w;
+ min_w = ctx->state & FIMC_DST_CROP ? dst->width : var->min_out_pixsize;
+ min_h = ctx->state & FIMC_DST_CROP ? dst->height : var->min_out_pixsize;
+ if (var->min_vsize_align == 1 && !rotation)
+ align_h = fimc_fmt_is_rgb(ffmt->color) ? 0 : 1;
+
+ depth = fimc_get_format_depth(ffmt);
+ v4l_bound_align_image(width, min_w, max_w,
+ ffs(var->min_out_pixsize) - 1,
+ height, min_h, FIMC_CAMIF_MAX_HEIGHT,
+ align_h,
+ 64/(ALIGN(depth, 8)));
+
+ dbg("pad%d: code: 0x%x, %dx%d. dst fmt: %dx%d",
+ pad, code ? *code : 0, *width, *height,
+ dst->f_width, dst->f_height);
+
+ return ffmt;
+}
+
+static void fimc_capture_try_crop(struct fimc_ctx *ctx, struct v4l2_rect *r,
+ int pad)
+{
+ bool rotate = ctx->rotation == 90 || ctx->rotation == 270;
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct samsung_fimc_variant *var = fimc->variant;
+ struct fimc_pix_limit *pl = var->pix_limit;
+ struct fimc_frame *sink = &ctx->s_frame;
+ u32 max_w, max_h, min_w = 0, min_h = 0, min_sz;
+ u32 align_sz = 0, align_h = 4;
+ u32 max_sc_h, max_sc_v;
+
+ /* In JPEG transparent transfer mode cropping is not supported */
+ if (fimc_fmt_is_jpeg(ctx->d_frame.fmt->color)) {
+ r->width = sink->f_width;
+ r->height = sink->f_height;
+ r->left = r->top = 0;
+ return;
+ }
+ if (pad == FIMC_SD_PAD_SOURCE) {
+ if (ctx->rotation != 90 && ctx->rotation != 270)
+ align_h = 1;
+ max_sc_h = min(SCALER_MAX_HRATIO, 1 << (ffs(sink->width) - 3));
+ max_sc_v = min(SCALER_MAX_VRATIO, 1 << (ffs(sink->height) - 1));
+ min_sz = var->min_out_pixsize;
+ } else {
+ u32 depth = fimc_get_format_depth(sink->fmt);
+ align_sz = 64/ALIGN(depth, 8);
+ min_sz = var->min_inp_pixsize;
+ min_w = min_h = min_sz;
+ max_sc_h = max_sc_v = 1;
+ }
+ /*
+ * For the crop rectangle at source pad the following constraints
+ * must be met:
+ * - it must fit in the sink pad format rectangle (f_width/f_height);
+ * - maximum downscaling ratio is 64;
+ * - maximum crop size depends if the rotator is used or not;
+ * - the sink pad format width/height must be 4 multiple of the
+ * prescaler ratios determined by sink pad size and source pad crop,
+ * the prescaler ratio is returned by fimc_get_scaler_factor().
+ */
+ max_w = min_t(u32,
+ rotate ? pl->out_rot_en_w : pl->out_rot_dis_w,
+ rotate ? sink->f_height : sink->f_width);
+ max_h = min_t(u32, FIMC_CAMIF_MAX_HEIGHT, sink->f_height);
+ if (pad == FIMC_SD_PAD_SOURCE) {
+ min_w = min_t(u32, max_w, sink->f_width / max_sc_h);
+ min_h = min_t(u32, max_h, sink->f_height / max_sc_v);
+ if (rotate) {
+ swap(max_sc_h, max_sc_v);
+ swap(min_w, min_h);
+ }
+ }
+ v4l_bound_align_image(&r->width, min_w, max_w, ffs(min_sz) - 1,
+ &r->height, min_h, max_h, align_h,
+ align_sz);
+ /* Adjust left/top if cropping rectangle is out of bounds */
+ r->left = clamp_t(u32, r->left, 0, sink->f_width - r->width);
+ r->top = clamp_t(u32, r->top, 0, sink->f_height - r->height);
+ r->left = round_down(r->left, var->hor_offs_align);
+
+ dbg("pad%d: (%d,%d)/%dx%d, sink fmt: %dx%d",
+ pad, r->left, r->top, r->width, r->height,
+ sink->f_width, sink->f_height);
+}
+
+/*
+ * The video node ioctl operations
+ */
+static int fimc_vidioc_querycap_capture(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+
+ strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1);
+ strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1);
+ cap->bus_info[0] = 0;
+ cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE_MPLANE;
+
+ return 0;
+}
+
+static int fimc_cap_enum_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct fimc_fmt *fmt;
+
+ fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM | FMT_FLAGS_M2M,
+ f->index);
+ if (!fmt)
+ return -EINVAL;
+ strncpy(f->description, fmt->name, sizeof(f->description) - 1);
+ f->pixelformat = fmt->fourcc;
+ if (fmt->fourcc == V4L2_MBUS_FMT_JPEG_1X8)
+ f->flags |= V4L2_FMT_FLAG_COMPRESSED;
+ return 0;
+}
+
+/**
+ * fimc_pipeline_try_format - negotiate and/or set formats at pipeline
+ * elements
+ * @ctx: FIMC capture context
+ * @tfmt: media bus format to try/set on subdevs
+ * @fmt_id: fimc pixel format id corresponding to returned @tfmt (output)
+ * @set: true to set format on subdevs, false to try only
+ */
+static int fimc_pipeline_try_format(struct fimc_ctx *ctx,
+ struct v4l2_mbus_framefmt *tfmt,
+ struct fimc_fmt **fmt_id,
+ bool set)
+{
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct v4l2_subdev *sd = fimc->pipeline.sensor;
+ struct v4l2_subdev *csis = fimc->pipeline.csis;
+ struct v4l2_subdev_format sfmt;
+ struct v4l2_mbus_framefmt *mf = &sfmt.format;
+ struct fimc_fmt *ffmt = NULL;
+ int ret, i = 0;
+
+ if (WARN_ON(!sd || !tfmt))
+ return -EINVAL;
+
+ memset(&sfmt, 0, sizeof(sfmt));
+ sfmt.format = *tfmt;
+
+ sfmt.which = set ? V4L2_SUBDEV_FORMAT_ACTIVE : V4L2_SUBDEV_FORMAT_TRY;
+ while (1) {
+ ffmt = fimc_find_format(NULL, mf->code != 0 ? &mf->code : NULL,
+ FMT_FLAGS_CAM, i++);
+ if (ffmt == NULL) {
+ /*
+ * Notify user-space if common pixel code for
+ * host and sensor does not exist.
+ */
+ return -EINVAL;
+ }
+ mf->code = tfmt->code = ffmt->mbus_code;
+
+ ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sfmt);
+ if (ret)
+ return ret;
+ if (mf->code != tfmt->code) {
+ mf->code = 0;
+ continue;
+ }
+ if (mf->width != tfmt->width || mf->height != tfmt->height) {
+ u32 fcc = ffmt->fourcc;
+ tfmt->width = mf->width;
+ tfmt->height = mf->height;
+ ffmt = fimc_capture_try_format(ctx,
+ &tfmt->width, &tfmt->height,
+ NULL, &fcc, FIMC_SD_PAD_SOURCE);
+ if (ffmt && ffmt->mbus_code)
+ mf->code = ffmt->mbus_code;
+ if (mf->width != tfmt->width ||
+ mf->height != tfmt->height)
+ continue;
+ tfmt->code = mf->code;
+ }
+ if (csis)
+ ret = v4l2_subdev_call(csis, pad, set_fmt, NULL, &sfmt);
+
+ if (mf->code == tfmt->code &&
+ mf->width == tfmt->width && mf->height == tfmt->height)
+ break;
+ }
+
+ if (fmt_id && ffmt)
+ *fmt_id = ffmt;
+ *tfmt = *mf;
+
+ dbg("code: 0x%x, %dx%d, %p", mf->code, mf->width, mf->height, ffmt);
+ return 0;
+}
+
+static int fimc_cap_g_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
+ return fimc_fill_format(&ctx->d_frame, f);
+}
+
+static int fimc_cap_try_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct v4l2_mbus_framefmt mf;
+ struct fimc_fmt *ffmt = NULL;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_JPEG) {
+ fimc_capture_try_format(ctx, &pix->width, &pix->height,
+ NULL, &pix->pixelformat,
+ FIMC_SD_PAD_SINK);
+ ctx->s_frame.f_width = pix->width;
+ ctx->s_frame.f_height = pix->height;
+ }
+ ffmt = fimc_capture_try_format(ctx, &pix->width, &pix->height,
+ NULL, &pix->pixelformat,
+ FIMC_SD_PAD_SOURCE);
+ if (!ffmt)
+ return -EINVAL;
+
+ if (!fimc->vid_cap.user_subdev_api) {
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.code = ffmt->mbus_code;
+ fimc_md_graph_lock(fimc);
+ fimc_pipeline_try_format(ctx, &mf, &ffmt, false);
+ fimc_md_graph_unlock(fimc);
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ if (ffmt)
+ pix->pixelformat = ffmt->fourcc;
+ }
+
+ fimc_adjust_mplane_format(ffmt, pix->width, pix->height, pix);
+ return 0;
+}
+
+static void fimc_capture_mark_jpeg_xfer(struct fimc_ctx *ctx, bool jpeg)
+{
+ ctx->scaler.enabled = !jpeg;
+ fimc_ctrls_activate(ctx, !jpeg);
+
+ if (jpeg)
+ set_bit(ST_CAPT_JPEG, &ctx->fimc_dev->state);
+ else
+ clear_bit(ST_CAPT_JPEG, &ctx->fimc_dev->state);
+}
+
+static int fimc_capture_set_format(struct fimc_dev *fimc, struct v4l2_format *f)
+{
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+ struct v4l2_mbus_framefmt *mf = &fimc->vid_cap.mf;
+ struct fimc_frame *ff = &ctx->d_frame;
+ struct fimc_fmt *s_fmt = NULL;
+ int ret, i;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+ if (vb2_is_busy(&fimc->vid_cap.vbq))
+ return -EBUSY;
+
+ /* Pre-configure format at camera interface input, for JPEG only */
+ if (pix->pixelformat == V4L2_PIX_FMT_JPEG) {
+ fimc_capture_try_format(ctx, &pix->width, &pix->height,
+ NULL, &pix->pixelformat,
+ FIMC_SD_PAD_SINK);
+ ctx->s_frame.f_width = pix->width;
+ ctx->s_frame.f_height = pix->height;
+ }
+ /* Try the format at the scaler and the DMA output */
+ ff->fmt = fimc_capture_try_format(ctx, &pix->width, &pix->height,
+ NULL, &pix->pixelformat,
+ FIMC_SD_PAD_SOURCE);
+ if (!ff->fmt)
+ return -EINVAL;
+
+ /* Update RGB Alpha control state and value range */
+ fimc_alpha_ctrl_update(ctx);
+
+ /* Try to match format at the host and the sensor */
+ if (!fimc->vid_cap.user_subdev_api) {
+ mf->code = ff->fmt->mbus_code;
+ mf->width = pix->width;
+ mf->height = pix->height;
+
+ fimc_md_graph_lock(fimc);
+ ret = fimc_pipeline_try_format(ctx, mf, &s_fmt, true);
+ fimc_md_graph_unlock(fimc);
+ if (ret)
+ return ret;
+ pix->width = mf->width;
+ pix->height = mf->height;
+ }
+ fimc_adjust_mplane_format(ff->fmt, pix->width, pix->height, pix);
+ for (i = 0; i < ff->fmt->colplanes; i++)
+ ff->payload[i] =
+ (pix->width * pix->height * ff->fmt->depth[i]) / 8;
+
+ set_frame_bounds(ff, pix->width, pix->height);
+ /* Reset the composition rectangle if not yet configured */
+ if (!(ctx->state & FIMC_DST_CROP))
+ set_frame_crop(ff, 0, 0, pix->width, pix->height);
+
+ fimc_capture_mark_jpeg_xfer(ctx, fimc_fmt_is_jpeg(ff->fmt->color));
+
+ /* Reset cropping and set format at the camera interface input */
+ if (!fimc->vid_cap.user_subdev_api) {
+ ctx->s_frame.fmt = s_fmt;
+ set_frame_bounds(&ctx->s_frame, pix->width, pix->height);
+ set_frame_crop(&ctx->s_frame, 0, 0, pix->width, pix->height);
+ }
+
+ return ret;
+}
+
+static int fimc_cap_s_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+
+ return fimc_capture_set_format(fimc, f);
+}
+
+static int fimc_cap_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct v4l2_subdev *sd = fimc->pipeline.sensor;
+
+ if (i->index != 0)
+ return -EINVAL;
+
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ if (sd)
+ strlcpy(i->name, sd->name, sizeof(i->name));
+ return 0;
+}
+
+static int fimc_cap_s_input(struct file *file, void *priv, unsigned int i)
+{
+ return i == 0 ? i : -EINVAL;
+}
+
+static int fimc_cap_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+/**
+ * fimc_pipeline_validate - check for formats inconsistencies
+ * between source and sink pad of each link
+ *
+ * Return 0 if all formats match or -EPIPE otherwise.
+ */
+static int fimc_pipeline_validate(struct fimc_dev *fimc)
+{
+ struct v4l2_subdev_format sink_fmt, src_fmt;
+ struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+ struct v4l2_subdev *sd;
+ struct media_pad *pad;
+ int ret;
+
+ /* Start with the video capture node pad */
+ pad = media_entity_remote_source(&vid_cap->vd_pad);
+ if (pad == NULL)
+ return -EPIPE;
+ /* FIMC.{N} subdevice */
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+
+ while (1) {
+ /* Retrieve format at the sink pad */
+ pad = &sd->entity.pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+ /* Don't call FIMC subdev operation to avoid nested locking */
+ if (sd == fimc->vid_cap.subdev) {
+ struct fimc_frame *ff = &vid_cap->ctx->s_frame;
+ sink_fmt.format.width = ff->f_width;
+ sink_fmt.format.height = ff->f_height;
+ sink_fmt.format.code = ff->fmt ? ff->fmt->mbus_code : 0;
+ } else {
+ sink_fmt.pad = pad->index;
+ sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sink_fmt);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return -EPIPE;
+ }
+ /* Retrieve format at the source pad */
+ pad = media_entity_remote_source(pad);
+ if (pad == NULL ||
+ media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ break;
+
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+ src_fmt.pad = pad->index;
+ src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return -EPIPE;
+
+ if (src_fmt.format.width != sink_fmt.format.width ||
+ src_fmt.format.height != sink_fmt.format.height ||
+ src_fmt.format.code != sink_fmt.format.code)
+ return -EPIPE;
+ }
+ return 0;
+}
+
+static int fimc_cap_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_pipeline *p = &fimc->pipeline;
+ int ret;
+
+ if (fimc_capture_active(fimc))
+ return -EBUSY;
+
+ media_entity_pipeline_start(&p->sensor->entity, p->pipe);
+
+ if (fimc->vid_cap.user_subdev_api) {
+ ret = fimc_pipeline_validate(fimc);
+ if (ret)
+ return ret;
+ }
+ return vb2_streamon(&fimc->vid_cap.vbq, type);
+}
+
+static int fimc_cap_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct v4l2_subdev *sd = fimc->pipeline.sensor;
+ int ret;
+
+ ret = vb2_streamoff(&fimc->vid_cap.vbq, type);
+ if (ret == 0)
+ media_entity_pipeline_stop(&sd->entity);
+ return ret;
+}
+
+static int fimc_cap_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+ int ret = vb2_reqbufs(&fimc->vid_cap.vbq, reqbufs);
+
+ if (!ret)
+ fimc->vid_cap.reqbufs_count = reqbufs->count;
+ return ret;
+}
+
+static int fimc_cap_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+
+ return vb2_querybuf(&fimc->vid_cap.vbq, buf);
+}
+
+static int fimc_cap_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+
+ return vb2_qbuf(&fimc->vid_cap.vbq, buf);
+}
+
+static int fimc_cap_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+
+ return vb2_dqbuf(&fimc->vid_cap.vbq, buf, file->f_flags & O_NONBLOCK);
+}
+
+static int fimc_cap_create_bufs(struct file *file, void *priv,
+ struct v4l2_create_buffers *create)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+
+ return vb2_create_bufs(&fimc->vid_cap.vbq, create);
+}
+
+static int fimc_cap_prepare_buf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+
+ return vb2_prepare_buf(&fimc->vid_cap.vbq, b);
+}
+
+static int fimc_cap_g_selection(struct file *file, void *fh,
+ struct v4l2_selection *s)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct fimc_frame *f = &ctx->s_frame;
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ f = &ctx->d_frame;
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = f->o_width;
+ s->r.height = f->o_height;
+ return 0;
+
+ case V4L2_SEL_TGT_COMPOSE_ACTIVE:
+ f = &ctx->d_frame;
+ case V4L2_SEL_TGT_CROP_ACTIVE:
+ s->r.left = f->offs_h;
+ s->r.top = f->offs_v;
+ s->r.width = f->width;
+ s->r.height = f->height;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */
+int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b)
+{
+ if (a->left < b->left || a->top < b->top)
+ return 0;
+ if (a->left + a->width > b->left + b->width)
+ return 0;
+ if (a->top + a->height > b->top + b->height)
+ return 0;
+
+ return 1;
+}
+
+static int fimc_cap_s_selection(struct file *file, void *fh,
+ struct v4l2_selection *s)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct v4l2_rect rect = s->r;
+ struct fimc_frame *f;
+ unsigned long flags;
+ unsigned int pad;
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ case V4L2_SEL_TGT_COMPOSE_ACTIVE:
+ f = &ctx->d_frame;
+ pad = FIMC_SD_PAD_SOURCE;
+ break;
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_ACTIVE:
+ f = &ctx->s_frame;
+ pad = FIMC_SD_PAD_SINK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ fimc_capture_try_crop(ctx, &rect, pad);
+
+ if (s->flags & V4L2_SEL_FLAG_LE &&
+ !enclosed_rectangle(&rect, &s->r))
+ return -ERANGE;
+
+ if (s->flags & V4L2_SEL_FLAG_GE &&
+ !enclosed_rectangle(&s->r, &rect))
+ return -ERANGE;
+
+ s->r = rect;
+ spin_lock_irqsave(&fimc->slock, flags);
+ set_frame_crop(f, s->r.left, s->r.top, s->r.width,
+ s->r.height);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = {
+ .vidioc_querycap = fimc_vidioc_querycap_capture,
+
+ .vidioc_enum_fmt_vid_cap_mplane = fimc_cap_enum_fmt_mplane,
+ .vidioc_try_fmt_vid_cap_mplane = fimc_cap_try_fmt_mplane,
+ .vidioc_s_fmt_vid_cap_mplane = fimc_cap_s_fmt_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = fimc_cap_g_fmt_mplane,
+
+ .vidioc_reqbufs = fimc_cap_reqbufs,
+ .vidioc_querybuf = fimc_cap_querybuf,
+
+ .vidioc_qbuf = fimc_cap_qbuf,
+ .vidioc_dqbuf = fimc_cap_dqbuf,
+
+ .vidioc_prepare_buf = fimc_cap_prepare_buf,
+ .vidioc_create_bufs = fimc_cap_create_bufs,
+
+ .vidioc_streamon = fimc_cap_streamon,
+ .vidioc_streamoff = fimc_cap_streamoff,
+
+ .vidioc_g_selection = fimc_cap_g_selection,
+ .vidioc_s_selection = fimc_cap_s_selection,
+
+ .vidioc_enum_input = fimc_cap_enum_input,
+ .vidioc_s_input = fimc_cap_s_input,
+ .vidioc_g_input = fimc_cap_g_input,
+};
+
+/* Capture subdev media entity operations */
+static int fimc_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
+
+ if (media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ return -EINVAL;
+
+ if (WARN_ON(fimc == NULL))
+ return 0;
+
+ dbg("%s --> %s, flags: 0x%x. input: 0x%x",
+ local->entity->name, remote->entity->name, flags,
+ fimc->vid_cap.input);
+
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (fimc->vid_cap.input != 0)
+ return -EBUSY;
+ fimc->vid_cap.input = sd->grp_id;
+ return 0;
+ }
+
+ fimc->vid_cap.input = 0;
+ return 0;
+}
+
+static const struct media_entity_operations fimc_sd_media_ops = {
+ .link_setup = fimc_link_setup,
+};
+
+/**
+ * fimc_sensor_notify - v4l2_device notification from a sensor subdev
+ * @sd: pointer to a subdev generating the notification
+ * @notification: the notification type, must be S5P_FIMC_TX_END_NOTIFY
+ * @arg: pointer to an u32 type integer that stores the frame payload value
+ *
+ * The End Of Frame notification sent by sensor subdev in its still capture
+ * mode. If there is only a single VSYNC generated by the sensor at the
+ * beginning of a frame transmission, FIMC does not issue the LastIrq
+ * (end of frame) interrupt. And this notification is used to complete the
+ * frame capture and returning a buffer to user-space. Subdev drivers should
+ * call this notification from their last 'End of frame capture' interrupt.
+ */
+void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification,
+ void *arg)
+{
+ struct fimc_sensor_info *sensor;
+ struct fimc_vid_buffer *buf;
+ struct fimc_md *fmd;
+ struct fimc_dev *fimc;
+ unsigned long flags;
+
+ if (sd == NULL)
+ return;
+
+ sensor = v4l2_get_subdev_hostdata(sd);
+ fmd = entity_to_fimc_mdev(&sd->entity);
+
+ spin_lock_irqsave(&fmd->slock, flags);
+ fimc = sensor ? sensor->host : NULL;
+
+ if (fimc && arg && notification == S5P_FIMC_TX_END_NOTIFY &&
+ test_bit(ST_CAPT_PEND, &fimc->state)) {
+ unsigned long irq_flags;
+ spin_lock_irqsave(&fimc->slock, irq_flags);
+ if (!list_empty(&fimc->vid_cap.active_buf_q)) {
+ buf = list_entry(fimc->vid_cap.active_buf_q.next,
+ struct fimc_vid_buffer, list);
+ vb2_set_plane_payload(&buf->vb, 0, *((u32 *)arg));
+ }
+ fimc_capture_irq_handler(fimc, true);
+ fimc_deactivate_capture(fimc);
+ spin_unlock_irqrestore(&fimc->slock, irq_flags);
+ }
+ spin_unlock_irqrestore(&fmd->slock, flags);
+}
+
+static int fimc_subdev_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct fimc_fmt *fmt;
+
+ fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, code->index);
+ if (!fmt)
+ return -EINVAL;
+ code->code = fmt->mbus_code;
+ return 0;
+}
+
+static int fimc_subdev_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct v4l2_mbus_framefmt *mf;
+ struct fimc_frame *ff;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+ fmt->format = *mf;
+ return 0;
+ }
+ mf = &fmt->format;
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ ff = fmt->pad == FIMC_SD_PAD_SINK ? &ctx->s_frame : &ctx->d_frame;
+
+ mutex_lock(&fimc->lock);
+ /* The pixel code is same on both input and output pad */
+ if (!WARN_ON(ctx->s_frame.fmt == NULL))
+ mf->code = ctx->s_frame.fmt->mbus_code;
+ mf->width = ff->f_width;
+ mf->height = ff->f_height;
+ mutex_unlock(&fimc->lock);
+
+ return 0;
+}
+
+static int fimc_subdev_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *mf = &fmt->format;
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct fimc_frame *ff;
+ struct fimc_fmt *ffmt;
+
+ dbg("pad%d: code: 0x%x, %dx%d",
+ fmt->pad, mf->code, mf->width, mf->height);
+
+ if (fmt->pad == FIMC_SD_PAD_SOURCE &&
+ vb2_is_busy(&fimc->vid_cap.vbq))
+ return -EBUSY;
+
+ mutex_lock(&fimc->lock);
+ ffmt = fimc_capture_try_format(ctx, &mf->width, &mf->height,
+ &mf->code, NULL, fmt->pad);
+ mutex_unlock(&fimc->lock);
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+ *mf = fmt->format;
+ return 0;
+ }
+ /* Update RGB Alpha control state and value range */
+ fimc_alpha_ctrl_update(ctx);
+
+ fimc_capture_mark_jpeg_xfer(ctx, fimc_fmt_is_jpeg(ffmt->color));
+
+ ff = fmt->pad == FIMC_SD_PAD_SINK ?
+ &ctx->s_frame : &ctx->d_frame;
+
+ mutex_lock(&fimc->lock);
+ set_frame_bounds(ff, mf->width, mf->height);
+ fimc->vid_cap.mf = *mf;
+ ff->fmt = ffmt;
+
+ /* Reset the crop rectangle if required. */
+ if (!(fmt->pad == FIMC_SD_PAD_SOURCE && (ctx->state & FIMC_DST_CROP)))
+ set_frame_crop(ff, 0, 0, mf->width, mf->height);
+
+ if (fmt->pad == FIMC_SD_PAD_SINK)
+ ctx->state &= ~FIMC_DST_CROP;
+ mutex_unlock(&fimc->lock);
+ return 0;
+}
+
+static int fimc_subdev_get_crop(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct v4l2_rect *r = &crop->rect;
+ struct fimc_frame *ff;
+
+ if (crop->which == V4L2_SUBDEV_FORMAT_TRY) {
+ crop->rect = *v4l2_subdev_get_try_crop(fh, crop->pad);
+ return 0;
+ }
+ ff = crop->pad == FIMC_SD_PAD_SINK ?
+ &ctx->s_frame : &ctx->d_frame;
+
+ mutex_lock(&fimc->lock);
+ r->left = ff->offs_h;
+ r->top = ff->offs_v;
+ r->width = ff->width;
+ r->height = ff->height;
+ mutex_unlock(&fimc->lock);
+
+ dbg("ff:%p, pad%d: l:%d, t:%d, %dx%d, f_w: %d, f_h: %d",
+ ff, crop->pad, r->left, r->top, r->width, r->height,
+ ff->f_width, ff->f_height);
+
+ return 0;
+}
+
+static int fimc_subdev_set_crop(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct v4l2_rect *r = &crop->rect;
+ struct fimc_frame *ff;
+ unsigned long flags;
+
+ dbg("(%d,%d)/%dx%d", r->left, r->top, r->width, r->height);
+
+ ff = crop->pad == FIMC_SD_PAD_SOURCE ?
+ &ctx->d_frame : &ctx->s_frame;
+
+ mutex_lock(&fimc->lock);
+ fimc_capture_try_crop(ctx, r, crop->pad);
+
+ if (crop->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mutex_unlock(&fimc->lock);
+ *v4l2_subdev_get_try_crop(fh, crop->pad) = *r;
+ return 0;
+ }
+ spin_lock_irqsave(&fimc->slock, flags);
+ set_frame_crop(ff, r->left, r->top, r->width, r->height);
+ if (crop->pad == FIMC_SD_PAD_SOURCE)
+ ctx->state |= FIMC_DST_CROP;
+
+ set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ dbg("pad%d: (%d,%d)/%dx%d", crop->pad, r->left, r->top,
+ r->width, r->height);
+
+ mutex_unlock(&fimc->lock);
+ return 0;
+}
+
+static struct v4l2_subdev_pad_ops fimc_subdev_pad_ops = {
+ .enum_mbus_code = fimc_subdev_enum_mbus_code,
+ .get_fmt = fimc_subdev_get_fmt,
+ .set_fmt = fimc_subdev_set_fmt,
+ .get_crop = fimc_subdev_get_crop,
+ .set_crop = fimc_subdev_set_crop,
+};
+
+static struct v4l2_subdev_ops fimc_subdev_ops = {
+ .pad = &fimc_subdev_pad_ops,
+};
+
+static int fimc_create_capture_subdev(struct fimc_dev *fimc,
+ struct v4l2_device *v4l2_dev)
+{
+ struct v4l2_subdev *sd;
+ int ret;
+
+ sd = kzalloc(sizeof(*sd), GFP_KERNEL);
+ if (!sd)
+ return -ENOMEM;
+
+ v4l2_subdev_init(sd, &fimc_subdev_ops);
+ sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(sd->name, sizeof(sd->name), "FIMC.%d", fimc->pdev->id);
+
+ fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ fimc->vid_cap.sd_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM,
+ fimc->vid_cap.sd_pads, 0);
+ if (ret)
+ goto me_err;
+ ret = v4l2_device_register_subdev(v4l2_dev, sd);
+ if (ret)
+ goto sd_err;
+
+ fimc->vid_cap.subdev = sd;
+ v4l2_set_subdevdata(sd, fimc);
+ sd->entity.ops = &fimc_sd_media_ops;
+ return 0;
+sd_err:
+ media_entity_cleanup(&sd->entity);
+me_err:
+ kfree(sd);
+ return ret;
+}
+
+static void fimc_destroy_capture_subdev(struct fimc_dev *fimc)
+{
+ struct v4l2_subdev *sd = fimc->vid_cap.subdev;
+
+ if (!sd)
+ return;
+ media_entity_cleanup(&sd->entity);
+ v4l2_device_unregister_subdev(sd);
+ kfree(sd);
+ fimc->vid_cap.subdev = NULL;
+}
+
+/* Set default format at the sensor and host interface */
+static int fimc_capture_set_default_format(struct fimc_dev *fimc)
+{
+ struct v4l2_format fmt = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ .fmt.pix_mp = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+ };
+
+ return fimc_capture_set_format(fimc, &fmt);
+}
+
+/* fimc->lock must be already initialized */
+int fimc_register_capture_device(struct fimc_dev *fimc,
+ struct v4l2_device *v4l2_dev)
+{
+ struct video_device *vfd;
+ struct fimc_vid_cap *vid_cap;
+ struct fimc_ctx *ctx;
+ struct vb2_queue *q;
+ int ret = -ENOMEM;
+
+ ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->fimc_dev = fimc;
+ ctx->in_path = FIMC_CAMERA;
+ ctx->out_path = FIMC_DMA;
+ ctx->state = FIMC_CTX_CAP;
+ ctx->s_frame.fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, 0);
+ ctx->d_frame.fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, 0);
+
+ vfd = video_device_alloc();
+ if (!vfd) {
+ v4l2_err(v4l2_dev, "Failed to allocate video device\n");
+ goto err_vd_alloc;
+ }
+
+ snprintf(vfd->name, sizeof(vfd->name), "%s.capture",
+ dev_name(&fimc->pdev->dev));
+
+ vfd->fops = &fimc_capture_fops;
+ vfd->ioctl_ops = &fimc_capture_ioctl_ops;
+ vfd->v4l2_dev = v4l2_dev;
+ vfd->minor = -1;
+ vfd->release = video_device_release;
+ vfd->lock = &fimc->lock;
+ video_set_drvdata(vfd, fimc);
+
+ vid_cap = &fimc->vid_cap;
+ vid_cap->vfd = vfd;
+ vid_cap->active_buf_cnt = 0;
+ vid_cap->reqbufs_count = 0;
+ vid_cap->refcnt = 0;
+
+ INIT_LIST_HEAD(&vid_cap->pending_buf_q);
+ INIT_LIST_HEAD(&vid_cap->active_buf_q);
+ spin_lock_init(&ctx->slock);
+ vid_cap->ctx = ctx;
+
+ q = &fimc->vid_cap.vbq;
+ memset(q, 0, sizeof(*q));
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->drv_priv = fimc->vid_cap.ctx;
+ q->ops = &fimc_capture_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct fimc_vid_buffer);
+
+ vb2_queue_init(q);
+
+ fimc->vid_cap.vd_pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_init(&vfd->entity, 1, &fimc->vid_cap.vd_pad, 0);
+ if (ret)
+ goto err_ent;
+ ret = fimc_create_capture_subdev(fimc, v4l2_dev);
+ if (ret)
+ goto err_sd_reg;
+
+ vfd->ctrl_handler = &ctx->ctrl_handler;
+ return 0;
+
+err_sd_reg:
+ media_entity_cleanup(&vfd->entity);
+err_ent:
+ video_device_release(vfd);
+err_vd_alloc:
+ kfree(ctx);
+ return ret;
+}
+
+void fimc_unregister_capture_device(struct fimc_dev *fimc)
+{
+ struct video_device *vfd = fimc->vid_cap.vfd;
+
+ if (vfd) {
+ media_entity_cleanup(&vfd->entity);
+ /* Can also be called if video device was
+ not registered */
+ video_unregister_device(vfd);
+ }
+ fimc_destroy_capture_subdev(fimc);
+ kfree(fimc->vid_cap.ctx);
+ fimc->vid_cap.ctx = NULL;
+}
diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c
new file mode 100644
index 00000000..e09ba7b0
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/fimc-core.c
@@ -0,0 +1,2045 @@
+/*
+ * Samsung S5P/EXYNOS4 SoC series camera interface (video postprocessor) driver
+ *
+ * Copyright (C) 2010-2011 Samsung Electronics Co., Ltd.
+ * Contact: Sylwester Nawrocki, <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation, either version 2 of the License,
+ * or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/bug.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/list.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "fimc-core.h"
+#include "fimc-mdevice.h"
+
+static char *fimc_clocks[MAX_FIMC_CLOCKS] = {
+ "sclk_fimc", "fimc"
+};
+
+static struct fimc_fmt fimc_formats[] = {
+ {
+ .name = "RGB565",
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .depth = { 16 },
+ .color = S5P_FIMC_RGB565,
+ .memplanes = 1,
+ .colplanes = 1,
+ .flags = FMT_FLAGS_M2M,
+ }, {
+ .name = "BGR666",
+ .fourcc = V4L2_PIX_FMT_BGR666,
+ .depth = { 32 },
+ .color = S5P_FIMC_RGB666,
+ .memplanes = 1,
+ .colplanes = 1,
+ .flags = FMT_FLAGS_M2M,
+ }, {
+ .name = "ARGB8888, 32 bpp",
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .depth = { 32 },
+ .color = S5P_FIMC_RGB888,
+ .memplanes = 1,
+ .colplanes = 1,
+ .flags = FMT_FLAGS_M2M | FMT_HAS_ALPHA,
+ }, {
+ .name = "ARGB1555",
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .depth = { 16 },
+ .color = S5P_FIMC_RGB555,
+ .memplanes = 1,
+ .colplanes = 1,
+ .flags = FMT_FLAGS_M2M_OUT | FMT_HAS_ALPHA,
+ }, {
+ .name = "ARGB4444",
+ .fourcc = V4L2_PIX_FMT_RGB444,
+ .depth = { 16 },
+ .color = S5P_FIMC_RGB444,
+ .memplanes = 1,
+ .colplanes = 1,
+ .flags = FMT_FLAGS_M2M_OUT | FMT_HAS_ALPHA,
+ }, {
+ .name = "YUV 4:2:2 packed, YCbYCr",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = { 16 },
+ .color = S5P_FIMC_YCBYCR422,
+ .memplanes = 1,
+ .colplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM,
+ }, {
+ .name = "YUV 4:2:2 packed, CbYCrY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = { 16 },
+ .color = S5P_FIMC_CBYCRY422,
+ .memplanes = 1,
+ .colplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM,
+ }, {
+ .name = "YUV 4:2:2 packed, CrYCbY",
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .depth = { 16 },
+ .color = S5P_FIMC_CRYCBY422,
+ .memplanes = 1,
+ .colplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_VYUY8_2X8,
+ .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM,
+ }, {
+ .name = "YUV 4:2:2 packed, YCrYCb",
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .depth = { 16 },
+ .color = S5P_FIMC_YCRYCB422,
+ .memplanes = 1,
+ .colplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_YVYU8_2X8,
+ .flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM,
+ }, {
+ .name = "YUV 4:2:2 planar, Y/Cb/Cr",
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .depth = { 12 },
+ .color = S5P_FIMC_YCBYCR422,
+ .memplanes = 1,
+ .colplanes = 3,
+ .flags = FMT_FLAGS_M2M,
+ }, {
+ .name = "YUV 4:2:2 planar, Y/CbCr",
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .depth = { 16 },
+ .color = S5P_FIMC_YCBYCR422,
+ .memplanes = 1,
+ .colplanes = 2,
+ .flags = FMT_FLAGS_M2M,
+ }, {
+ .name = "YUV 4:2:2 planar, Y/CrCb",
+ .fourcc = V4L2_PIX_FMT_NV61,
+ .depth = { 16 },
+ .color = S5P_FIMC_YCRYCB422,
+ .memplanes = 1,
+ .colplanes = 2,
+ .flags = FMT_FLAGS_M2M,
+ }, {
+ .name = "YUV 4:2:0 planar, YCbCr",
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .depth = { 12 },
+ .color = S5P_FIMC_YCBCR420,
+ .memplanes = 1,
+ .colplanes = 3,
+ .flags = FMT_FLAGS_M2M,
+ }, {
+ .name = "YUV 4:2:0 planar, Y/CbCr",
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .depth = { 12 },
+ .color = S5P_FIMC_YCBCR420,
+ .memplanes = 1,
+ .colplanes = 2,
+ .flags = FMT_FLAGS_M2M,
+ }, {
+ .name = "YUV 4:2:0 non-contiguous 2-planar, Y/CbCr",
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .color = S5P_FIMC_YCBCR420,
+ .depth = { 8, 4 },
+ .memplanes = 2,
+ .colplanes = 2,
+ .flags = FMT_FLAGS_M2M,
+ }, {
+ .name = "YUV 4:2:0 non-contiguous 3-planar, Y/Cb/Cr",
+ .fourcc = V4L2_PIX_FMT_YUV420M,
+ .color = S5P_FIMC_YCBCR420,
+ .depth = { 8, 2, 2 },
+ .memplanes = 3,
+ .colplanes = 3,
+ .flags = FMT_FLAGS_M2M,
+ }, {
+ .name = "YUV 4:2:0 non-contiguous 2-planar, Y/CbCr, tiled",
+ .fourcc = V4L2_PIX_FMT_NV12MT,
+ .color = S5P_FIMC_YCBCR420,
+ .depth = { 8, 4 },
+ .memplanes = 2,
+ .colplanes = 2,
+ .flags = FMT_FLAGS_M2M,
+ }, {
+ .name = "JPEG encoded data",
+ .fourcc = V4L2_PIX_FMT_JPEG,
+ .color = S5P_FIMC_JPEG,
+ .depth = { 8 },
+ .memplanes = 1,
+ .colplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_JPEG_1X8,
+ .flags = FMT_FLAGS_CAM,
+ },
+};
+
+static unsigned int get_m2m_fmt_flags(unsigned int stream_type)
+{
+ if (stream_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return FMT_FLAGS_M2M_IN;
+ else
+ return FMT_FLAGS_M2M_OUT;
+}
+
+int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh,
+ int dw, int dh, int rotation)
+{
+ if (rotation == 90 || rotation == 270)
+ swap(dw, dh);
+
+ if (!ctx->scaler.enabled)
+ return (sw == dw && sh == dh) ? 0 : -EINVAL;
+
+ if ((sw >= SCALER_MAX_HRATIO * dw) || (sh >= SCALER_MAX_VRATIO * dh))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int fimc_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift)
+{
+ u32 sh = 6;
+
+ if (src >= 64 * tar)
+ return -EINVAL;
+
+ while (sh--) {
+ u32 tmp = 1 << sh;
+ if (src >= tar * tmp) {
+ *shift = sh, *ratio = tmp;
+ return 0;
+ }
+ }
+ *shift = 0, *ratio = 1;
+ return 0;
+}
+
+int fimc_set_scaler_info(struct fimc_ctx *ctx)
+{
+ struct samsung_fimc_variant *variant = ctx->fimc_dev->variant;
+ struct device *dev = &ctx->fimc_dev->pdev->dev;
+ struct fimc_scaler *sc = &ctx->scaler;
+ struct fimc_frame *s_frame = &ctx->s_frame;
+ struct fimc_frame *d_frame = &ctx->d_frame;
+ int tx, ty, sx, sy;
+ int ret;
+
+ if (ctx->rotation == 90 || ctx->rotation == 270) {
+ ty = d_frame->width;
+ tx = d_frame->height;
+ } else {
+ tx = d_frame->width;
+ ty = d_frame->height;
+ }
+ if (tx <= 0 || ty <= 0) {
+ dev_err(dev, "Invalid target size: %dx%d", tx, ty);
+ return -EINVAL;
+ }
+
+ sx = s_frame->width;
+ sy = s_frame->height;
+ if (sx <= 0 || sy <= 0) {
+ dev_err(dev, "Invalid source size: %dx%d", sx, sy);
+ return -EINVAL;
+ }
+ sc->real_width = sx;
+ sc->real_height = sy;
+
+ ret = fimc_get_scaler_factor(sx, tx, &sc->pre_hratio, &sc->hfactor);
+ if (ret)
+ return ret;
+
+ ret = fimc_get_scaler_factor(sy, ty, &sc->pre_vratio, &sc->vfactor);
+ if (ret)
+ return ret;
+
+ sc->pre_dst_width = sx / sc->pre_hratio;
+ sc->pre_dst_height = sy / sc->pre_vratio;
+
+ if (variant->has_mainscaler_ext) {
+ sc->main_hratio = (sx << 14) / (tx << sc->hfactor);
+ sc->main_vratio = (sy << 14) / (ty << sc->vfactor);
+ } else {
+ sc->main_hratio = (sx << 8) / (tx << sc->hfactor);
+ sc->main_vratio = (sy << 8) / (ty << sc->vfactor);
+
+ }
+
+ sc->scaleup_h = (tx >= sx) ? 1 : 0;
+ sc->scaleup_v = (ty >= sy) ? 1 : 0;
+
+ /* check to see if input and output size/format differ */
+ if (s_frame->fmt->color == d_frame->fmt->color
+ && s_frame->width == d_frame->width
+ && s_frame->height == d_frame->height)
+ sc->copy_mode = 1;
+ else
+ sc->copy_mode = 0;
+
+ return 0;
+}
+
+static void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state)
+{
+ struct vb2_buffer *src_vb, *dst_vb;
+
+ if (!ctx || !ctx->m2m_ctx)
+ return;
+
+ src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+ dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+
+ if (src_vb && dst_vb) {
+ v4l2_m2m_buf_done(src_vb, vb_state);
+ v4l2_m2m_buf_done(dst_vb, vb_state);
+ v4l2_m2m_job_finish(ctx->fimc_dev->m2m.m2m_dev,
+ ctx->m2m_ctx);
+ }
+}
+
+/* Complete the transaction which has been scheduled for execution. */
+static int fimc_m2m_shutdown(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ int ret;
+
+ if (!fimc_m2m_pending(fimc))
+ return 0;
+
+ fimc_ctx_state_lock_set(FIMC_CTX_SHUT, ctx);
+
+ ret = wait_event_timeout(fimc->irq_queue,
+ !fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx),
+ FIMC_SHUTDOWN_TIMEOUT);
+
+ return ret == 0 ? -ETIMEDOUT : ret;
+}
+
+static int start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct fimc_ctx *ctx = q->drv_priv;
+ int ret;
+
+ ret = pm_runtime_get_sync(&ctx->fimc_dev->pdev->dev);
+ return ret > 0 ? 0 : ret;
+}
+
+static int stop_streaming(struct vb2_queue *q)
+{
+ struct fimc_ctx *ctx = q->drv_priv;
+ int ret;
+
+ ret = fimc_m2m_shutdown(ctx);
+ if (ret == -ETIMEDOUT)
+ fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
+
+ pm_runtime_put(&ctx->fimc_dev->pdev->dev);
+ return 0;
+}
+
+void fimc_capture_irq_handler(struct fimc_dev *fimc, bool final)
+{
+ struct fimc_vid_cap *cap = &fimc->vid_cap;
+ struct fimc_vid_buffer *v_buf;
+ struct timeval *tv;
+ struct timespec ts;
+
+ if (test_and_clear_bit(ST_CAPT_SHUT, &fimc->state)) {
+ wake_up(&fimc->irq_queue);
+ return;
+ }
+
+ if (!list_empty(&cap->active_buf_q) &&
+ test_bit(ST_CAPT_RUN, &fimc->state) && final) {
+ ktime_get_real_ts(&ts);
+
+ v_buf = fimc_active_queue_pop(cap);
+
+ tv = &v_buf->vb.v4l2_buf.timestamp;
+ tv->tv_sec = ts.tv_sec;
+ tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
+ v_buf->vb.v4l2_buf.sequence = cap->frame_count++;
+
+ vb2_buffer_done(&v_buf->vb, VB2_BUF_STATE_DONE);
+ }
+
+ if (!list_empty(&cap->pending_buf_q)) {
+
+ v_buf = fimc_pending_queue_pop(cap);
+ fimc_hw_set_output_addr(fimc, &v_buf->paddr, cap->buf_index);
+ v_buf->index = cap->buf_index;
+
+ /* Move the buffer to the capture active queue */
+ fimc_active_queue_add(cap, v_buf);
+
+ dbg("next frame: %d, done frame: %d",
+ fimc_hw_get_frame_index(fimc), v_buf->index);
+
+ if (++cap->buf_index >= FIMC_MAX_OUT_BUFS)
+ cap->buf_index = 0;
+ }
+
+ if (cap->active_buf_cnt == 0) {
+ if (final)
+ clear_bit(ST_CAPT_RUN, &fimc->state);
+
+ if (++cap->buf_index >= FIMC_MAX_OUT_BUFS)
+ cap->buf_index = 0;
+ } else {
+ set_bit(ST_CAPT_RUN, &fimc->state);
+ }
+
+ fimc_capture_config_update(cap->ctx);
+
+ dbg("frame: %d, active_buf_cnt: %d",
+ fimc_hw_get_frame_index(fimc), cap->active_buf_cnt);
+}
+
+static irqreturn_t fimc_irq_handler(int irq, void *priv)
+{
+ struct fimc_dev *fimc = priv;
+ struct fimc_vid_cap *cap = &fimc->vid_cap;
+ struct fimc_ctx *ctx;
+
+ fimc_hw_clear_irq(fimc);
+
+ spin_lock(&fimc->slock);
+
+ if (test_and_clear_bit(ST_M2M_PEND, &fimc->state)) {
+ if (test_and_clear_bit(ST_M2M_SUSPENDING, &fimc->state)) {
+ set_bit(ST_M2M_SUSPENDED, &fimc->state);
+ wake_up(&fimc->irq_queue);
+ goto out;
+ }
+ ctx = v4l2_m2m_get_curr_priv(fimc->m2m.m2m_dev);
+ if (ctx != NULL) {
+ spin_unlock(&fimc->slock);
+ fimc_m2m_job_finish(ctx, VB2_BUF_STATE_DONE);
+
+ spin_lock(&ctx->slock);
+ if (ctx->state & FIMC_CTX_SHUT) {
+ ctx->state &= ~FIMC_CTX_SHUT;
+ wake_up(&fimc->irq_queue);
+ }
+ spin_unlock(&ctx->slock);
+ }
+ return IRQ_HANDLED;
+ } else if (test_bit(ST_CAPT_PEND, &fimc->state)) {
+ fimc_capture_irq_handler(fimc,
+ !test_bit(ST_CAPT_JPEG, &fimc->state));
+ if (cap->active_buf_cnt == 1) {
+ fimc_deactivate_capture(fimc);
+ clear_bit(ST_CAPT_STREAM, &fimc->state);
+ }
+ }
+out:
+ spin_unlock(&fimc->slock);
+ return IRQ_HANDLED;
+}
+
+/* The color format (colplanes, memplanes) must be already configured. */
+int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb,
+ struct fimc_frame *frame, struct fimc_addr *paddr)
+{
+ int ret = 0;
+ u32 pix_size;
+
+ if (vb == NULL || frame == NULL)
+ return -EINVAL;
+
+ pix_size = frame->width * frame->height;
+
+ dbg("memplanes= %d, colplanes= %d, pix_size= %d",
+ frame->fmt->memplanes, frame->fmt->colplanes, pix_size);
+
+ paddr->y = vb2_dma_contig_plane_dma_addr(vb, 0);
+
+ if (frame->fmt->memplanes == 1) {
+ switch (frame->fmt->colplanes) {
+ case 1:
+ paddr->cb = 0;
+ paddr->cr = 0;
+ break;
+ case 2:
+ /* decompose Y into Y/Cb */
+ paddr->cb = (u32)(paddr->y + pix_size);
+ paddr->cr = 0;
+ break;
+ case 3:
+ paddr->cb = (u32)(paddr->y + pix_size);
+ /* decompose Y into Y/Cb/Cr */
+ if (S5P_FIMC_YCBCR420 == frame->fmt->color)
+ paddr->cr = (u32)(paddr->cb
+ + (pix_size >> 2));
+ else /* 422 */
+ paddr->cr = (u32)(paddr->cb
+ + (pix_size >> 1));
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ if (frame->fmt->memplanes >= 2)
+ paddr->cb = vb2_dma_contig_plane_dma_addr(vb, 1);
+
+ if (frame->fmt->memplanes == 3)
+ paddr->cr = vb2_dma_contig_plane_dma_addr(vb, 2);
+ }
+
+ dbg("PHYS_ADDR: y= 0x%X cb= 0x%X cr= 0x%X ret= %d",
+ paddr->y, paddr->cb, paddr->cr, ret);
+
+ return ret;
+}
+
+/* Set order for 1 and 2 plane YCBCR 4:2:2 formats. */
+void fimc_set_yuv_order(struct fimc_ctx *ctx)
+{
+ /* The one only mode supported in SoC. */
+ ctx->in_order_2p = S5P_FIMC_LSB_CRCB;
+ ctx->out_order_2p = S5P_FIMC_LSB_CRCB;
+
+ /* Set order for 1 plane input formats. */
+ switch (ctx->s_frame.fmt->color) {
+ case S5P_FIMC_YCRYCB422:
+ ctx->in_order_1p = S5P_MSCTRL_ORDER422_CBYCRY;
+ break;
+ case S5P_FIMC_CBYCRY422:
+ ctx->in_order_1p = S5P_MSCTRL_ORDER422_YCRYCB;
+ break;
+ case S5P_FIMC_CRYCBY422:
+ ctx->in_order_1p = S5P_MSCTRL_ORDER422_YCBYCR;
+ break;
+ case S5P_FIMC_YCBYCR422:
+ default:
+ ctx->in_order_1p = S5P_MSCTRL_ORDER422_CRYCBY;
+ break;
+ }
+ dbg("ctx->in_order_1p= %d", ctx->in_order_1p);
+
+ switch (ctx->d_frame.fmt->color) {
+ case S5P_FIMC_YCRYCB422:
+ ctx->out_order_1p = S5P_CIOCTRL_ORDER422_CBYCRY;
+ break;
+ case S5P_FIMC_CBYCRY422:
+ ctx->out_order_1p = S5P_CIOCTRL_ORDER422_YCRYCB;
+ break;
+ case S5P_FIMC_CRYCBY422:
+ ctx->out_order_1p = S5P_CIOCTRL_ORDER422_YCBYCR;
+ break;
+ case S5P_FIMC_YCBYCR422:
+ default:
+ ctx->out_order_1p = S5P_CIOCTRL_ORDER422_CRYCBY;
+ break;
+ }
+ dbg("ctx->out_order_1p= %d", ctx->out_order_1p);
+}
+
+void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f)
+{
+ struct samsung_fimc_variant *variant = ctx->fimc_dev->variant;
+ u32 i, depth = 0;
+
+ for (i = 0; i < f->fmt->colplanes; i++)
+ depth += f->fmt->depth[i];
+
+ f->dma_offset.y_h = f->offs_h;
+ if (!variant->pix_hoff)
+ f->dma_offset.y_h *= (depth >> 3);
+
+ f->dma_offset.y_v = f->offs_v;
+
+ f->dma_offset.cb_h = f->offs_h;
+ f->dma_offset.cb_v = f->offs_v;
+
+ f->dma_offset.cr_h = f->offs_h;
+ f->dma_offset.cr_v = f->offs_v;
+
+ if (!variant->pix_hoff) {
+ if (f->fmt->colplanes == 3) {
+ f->dma_offset.cb_h >>= 1;
+ f->dma_offset.cr_h >>= 1;
+ }
+ if (f->fmt->color == S5P_FIMC_YCBCR420) {
+ f->dma_offset.cb_v >>= 1;
+ f->dma_offset.cr_v >>= 1;
+ }
+ }
+
+ dbg("in_offset: color= %d, y_h= %d, y_v= %d",
+ f->fmt->color, f->dma_offset.y_h, f->dma_offset.y_v);
+}
+
+/**
+ * fimc_prepare_config - check dimensions, operation and color mode
+ * and pre-calculate offset and the scaling coefficients.
+ *
+ * @ctx: hardware context information
+ * @flags: flags indicating which parameters to check/update
+ *
+ * Return: 0 if dimensions are valid or non zero otherwise.
+ */
+int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags)
+{
+ struct fimc_frame *s_frame, *d_frame;
+ struct vb2_buffer *vb = NULL;
+ int ret = 0;
+
+ s_frame = &ctx->s_frame;
+ d_frame = &ctx->d_frame;
+
+ if (flags & FIMC_PARAMS) {
+ /* Prepare the DMA offset ratios for scaler. */
+ fimc_prepare_dma_offset(ctx, &ctx->s_frame);
+ fimc_prepare_dma_offset(ctx, &ctx->d_frame);
+
+ if (s_frame->height > (SCALER_MAX_VRATIO * d_frame->height) ||
+ s_frame->width > (SCALER_MAX_HRATIO * d_frame->width)) {
+ err("out of scaler range");
+ return -EINVAL;
+ }
+ fimc_set_yuv_order(ctx);
+ }
+
+ if (flags & FIMC_SRC_ADDR) {
+ vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+ ret = fimc_prepare_addr(ctx, vb, s_frame, &s_frame->paddr);
+ if (ret)
+ return ret;
+ }
+
+ if (flags & FIMC_DST_ADDR) {
+ vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+ ret = fimc_prepare_addr(ctx, vb, d_frame, &d_frame->paddr);
+ }
+
+ return ret;
+}
+
+static void fimc_dma_run(void *priv)
+{
+ struct fimc_ctx *ctx = priv;
+ struct fimc_dev *fimc;
+ unsigned long flags;
+ u32 ret;
+
+ if (WARN(!ctx, "null hardware context\n"))
+ return;
+
+ fimc = ctx->fimc_dev;
+ spin_lock_irqsave(&fimc->slock, flags);
+ set_bit(ST_M2M_PEND, &fimc->state);
+
+ spin_lock(&ctx->slock);
+ ctx->state |= (FIMC_SRC_ADDR | FIMC_DST_ADDR);
+ ret = fimc_prepare_config(ctx, ctx->state);
+ if (ret)
+ goto dma_unlock;
+
+ /* Reconfigure hardware if the context has changed. */
+ if (fimc->m2m.ctx != ctx) {
+ ctx->state |= FIMC_PARAMS;
+ fimc->m2m.ctx = ctx;
+ }
+ fimc_hw_set_input_addr(fimc, &ctx->s_frame.paddr);
+
+ if (ctx->state & FIMC_PARAMS) {
+ fimc_hw_set_input_path(ctx);
+ fimc_hw_set_in_dma(ctx);
+ ret = fimc_set_scaler_info(ctx);
+ if (ret) {
+ spin_unlock(&fimc->slock);
+ goto dma_unlock;
+ }
+ fimc_hw_set_prescaler(ctx);
+ fimc_hw_set_mainscaler(ctx);
+ fimc_hw_set_target_format(ctx);
+ fimc_hw_set_rotation(ctx);
+ fimc_hw_set_effect(ctx, false);
+ }
+
+ fimc_hw_set_output_path(ctx);
+ if (ctx->state & (FIMC_DST_ADDR | FIMC_PARAMS))
+ fimc_hw_set_output_addr(fimc, &ctx->d_frame.paddr, -1);
+
+ if (ctx->state & FIMC_PARAMS) {
+ fimc_hw_set_out_dma(ctx);
+ if (fimc->variant->has_alpha)
+ fimc_hw_set_rgb_alpha(ctx);
+ }
+
+ fimc_activate_capture(ctx);
+
+ ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP |
+ FIMC_SRC_FMT | FIMC_DST_FMT);
+ fimc_hw_activate_input_dma(fimc, true);
+dma_unlock:
+ spin_unlock(&ctx->slock);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+}
+
+static void fimc_job_abort(void *priv)
+{
+ fimc_m2m_shutdown(priv);
+}
+
+static int fimc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *allocators[])
+{
+ struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
+ struct fimc_frame *f;
+ int i;
+
+ f = ctx_get_frame(ctx, vq->type);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+ /*
+ * Return number of non-contigous planes (plane buffers)
+ * depending on the configured color format.
+ */
+ if (!f->fmt)
+ return -EINVAL;
+
+ *num_planes = f->fmt->memplanes;
+ for (i = 0; i < f->fmt->memplanes; i++) {
+ sizes[i] = (f->f_width * f->f_height * f->fmt->depth[i]) / 8;
+ allocators[i] = ctx->fimc_dev->alloc_ctx;
+ }
+ return 0;
+}
+
+static int fimc_buf_prepare(struct vb2_buffer *vb)
+{
+ struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct fimc_frame *frame;
+ int i;
+
+ frame = ctx_get_frame(ctx, vb->vb2_queue->type);
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ for (i = 0; i < frame->fmt->memplanes; i++)
+ vb2_set_plane_payload(vb, i, frame->payload[i]);
+
+ return 0;
+}
+
+static void fimc_buf_queue(struct vb2_buffer *vb)
+{
+ struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+ dbg("ctx: %p, ctx->state: 0x%x", ctx, ctx->state);
+
+ if (ctx->m2m_ctx)
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+}
+
+static void fimc_lock(struct vb2_queue *vq)
+{
+ struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
+ mutex_lock(&ctx->fimc_dev->lock);
+}
+
+static void fimc_unlock(struct vb2_queue *vq)
+{
+ struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
+ mutex_unlock(&ctx->fimc_dev->lock);
+}
+
+static struct vb2_ops fimc_qops = {
+ .queue_setup = fimc_queue_setup,
+ .buf_prepare = fimc_buf_prepare,
+ .buf_queue = fimc_buf_queue,
+ .wait_prepare = fimc_unlock,
+ .wait_finish = fimc_lock,
+ .stop_streaming = stop_streaming,
+ .start_streaming = start_streaming,
+};
+
+/*
+ * V4L2 controls handling
+ */
+#define ctrl_to_ctx(__ctrl) \
+ container_of((__ctrl)->handler, struct fimc_ctx, ctrl_handler)
+
+static int __fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_ctrl *ctrl)
+{
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct samsung_fimc_variant *variant = fimc->variant;
+ unsigned int flags = FIMC_DST_FMT | FIMC_SRC_FMT;
+ int ret = 0;
+
+ if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_HFLIP:
+ ctx->hflip = ctrl->val;
+ break;
+
+ case V4L2_CID_VFLIP:
+ ctx->vflip = ctrl->val;
+ break;
+
+ case V4L2_CID_ROTATE:
+ if (fimc_capture_pending(fimc) ||
+ (ctx->state & flags) == flags) {
+ ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width,
+ ctx->s_frame.height, ctx->d_frame.width,
+ ctx->d_frame.height, ctrl->val);
+ if (ret)
+ return -EINVAL;
+ }
+ if ((ctrl->val == 90 || ctrl->val == 270) &&
+ !variant->has_out_rot)
+ return -EINVAL;
+
+ ctx->rotation = ctrl->val;
+ break;
+
+ case V4L2_CID_ALPHA_COMPONENT:
+ ctx->d_frame.alpha = ctrl->val;
+ break;
+ }
+ ctx->state |= FIMC_PARAMS;
+ set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
+ return 0;
+}
+
+static int fimc_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct fimc_ctx *ctx = ctrl_to_ctx(ctrl);
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&ctx->slock, flags);
+ ret = __fimc_s_ctrl(ctx, ctrl);
+ spin_unlock_irqrestore(&ctx->slock, flags);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops fimc_ctrl_ops = {
+ .s_ctrl = fimc_s_ctrl,
+};
+
+int fimc_ctrls_create(struct fimc_ctx *ctx)
+{
+ struct samsung_fimc_variant *variant = ctx->fimc_dev->variant;
+ unsigned int max_alpha = fimc_get_alpha_mask(ctx->d_frame.fmt);
+
+ if (ctx->ctrls_rdy)
+ return 0;
+ v4l2_ctrl_handler_init(&ctx->ctrl_handler, 4);
+
+ ctx->ctrl_rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler, &fimc_ctrl_ops,
+ V4L2_CID_ROTATE, 0, 270, 90, 0);
+ ctx->ctrl_hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &fimc_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ ctx->ctrl_vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &fimc_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ if (variant->has_alpha)
+ ctx->ctrl_alpha = v4l2_ctrl_new_std(&ctx->ctrl_handler,
+ &fimc_ctrl_ops, V4L2_CID_ALPHA_COMPONENT,
+ 0, max_alpha, 1, 0);
+ else
+ ctx->ctrl_alpha = NULL;
+
+ ctx->ctrls_rdy = ctx->ctrl_handler.error == 0;
+
+ return ctx->ctrl_handler.error;
+}
+
+void fimc_ctrls_delete(struct fimc_ctx *ctx)
+{
+ if (ctx->ctrls_rdy) {
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ ctx->ctrls_rdy = false;
+ ctx->ctrl_alpha = NULL;
+ }
+}
+
+void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active)
+{
+ unsigned int has_alpha = ctx->d_frame.fmt->flags & FMT_HAS_ALPHA;
+
+ if (!ctx->ctrls_rdy)
+ return;
+
+ mutex_lock(&ctx->ctrl_handler.lock);
+ v4l2_ctrl_activate(ctx->ctrl_rotate, active);
+ v4l2_ctrl_activate(ctx->ctrl_hflip, active);
+ v4l2_ctrl_activate(ctx->ctrl_vflip, active);
+ if (ctx->ctrl_alpha)
+ v4l2_ctrl_activate(ctx->ctrl_alpha, active && has_alpha);
+
+ if (active) {
+ ctx->rotation = ctx->ctrl_rotate->val;
+ ctx->hflip = ctx->ctrl_hflip->val;
+ ctx->vflip = ctx->ctrl_vflip->val;
+ } else {
+ ctx->rotation = 0;
+ ctx->hflip = 0;
+ ctx->vflip = 0;
+ }
+ mutex_unlock(&ctx->ctrl_handler.lock);
+}
+
+/* Update maximum value of the alpha color control */
+void fimc_alpha_ctrl_update(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct v4l2_ctrl *ctrl = ctx->ctrl_alpha;
+
+ if (ctrl == NULL || !fimc->variant->has_alpha)
+ return;
+
+ v4l2_ctrl_lock(ctrl);
+ ctrl->maximum = fimc_get_alpha_mask(ctx->d_frame.fmt);
+
+ if (ctrl->cur.val > ctrl->maximum)
+ ctrl->cur.val = ctrl->maximum;
+
+ v4l2_ctrl_unlock(ctrl);
+}
+
+/*
+ * V4L2 ioctl handlers
+ */
+static int fimc_m2m_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+ struct fimc_dev *fimc = ctx->fimc_dev;
+
+ strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1);
+ strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1);
+ cap->bus_info[0] = 0;
+ cap->capabilities = V4L2_CAP_STREAMING |
+ V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
+
+ return 0;
+}
+
+static int fimc_m2m_enum_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct fimc_fmt *fmt;
+
+ fmt = fimc_find_format(NULL, NULL, get_m2m_fmt_flags(f->type),
+ f->index);
+ if (!fmt)
+ return -EINVAL;
+
+ strncpy(f->description, fmt->name, sizeof(f->description) - 1);
+ f->pixelformat = fmt->fourcc;
+ return 0;
+}
+
+int fimc_fill_format(struct fimc_frame *frame, struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
+ int i;
+
+ pixm->width = frame->o_width;
+ pixm->height = frame->o_height;
+ pixm->field = V4L2_FIELD_NONE;
+ pixm->pixelformat = frame->fmt->fourcc;
+ pixm->colorspace = V4L2_COLORSPACE_JPEG;
+ pixm->num_planes = frame->fmt->memplanes;
+
+ for (i = 0; i < pixm->num_planes; ++i) {
+ int bpl = frame->f_width;
+ if (frame->fmt->colplanes == 1) /* packed formats */
+ bpl = (bpl * frame->fmt->depth[0]) / 8;
+ pixm->plane_fmt[i].bytesperline = bpl;
+ pixm->plane_fmt[i].sizeimage = (frame->o_width *
+ frame->o_height * frame->fmt->depth[i]) / 8;
+ }
+ return 0;
+}
+
+void fimc_fill_frame(struct fimc_frame *frame, struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
+
+ frame->f_width = pixm->plane_fmt[0].bytesperline;
+ if (frame->fmt->colplanes == 1)
+ frame->f_width = (frame->f_width * 8) / frame->fmt->depth[0];
+ frame->f_height = pixm->height;
+ frame->width = pixm->width;
+ frame->height = pixm->height;
+ frame->o_width = pixm->width;
+ frame->o_height = pixm->height;
+ frame->offs_h = 0;
+ frame->offs_v = 0;
+}
+
+/**
+ * fimc_adjust_mplane_format - adjust bytesperline/sizeimage for each plane
+ * @fmt: fimc pixel format description (input)
+ * @width: requested pixel width
+ * @height: requested pixel height
+ * @pix: multi-plane format to adjust
+ */
+void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height,
+ struct v4l2_pix_format_mplane *pix)
+{
+ u32 bytesperline = 0;
+ int i;
+
+ pix->colorspace = V4L2_COLORSPACE_JPEG;
+ pix->field = V4L2_FIELD_NONE;
+ pix->num_planes = fmt->memplanes;
+ pix->pixelformat = fmt->fourcc;
+ pix->height = height;
+ pix->width = width;
+
+ for (i = 0; i < pix->num_planes; ++i) {
+ u32 bpl = pix->plane_fmt[i].bytesperline;
+ u32 *sizeimage = &pix->plane_fmt[i].sizeimage;
+
+ if (fmt->colplanes > 1 && (bpl == 0 || bpl < pix->width))
+ bpl = pix->width; /* Planar */
+
+ if (fmt->colplanes == 1 && /* Packed */
+ (bpl == 0 || ((bpl * 8) / fmt->depth[i]) < pix->width))
+ bpl = (pix->width * fmt->depth[0]) / 8;
+
+ if (i == 0) /* Same bytesperline for each plane. */
+ bytesperline = bpl;
+
+ pix->plane_fmt[i].bytesperline = bytesperline;
+ *sizeimage = (pix->width * pix->height * fmt->depth[i]) / 8;
+ }
+}
+
+static int fimc_m2m_g_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+ struct fimc_frame *frame = ctx_get_frame(ctx, f->type);
+
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ return fimc_fill_format(frame, f);
+}
+
+/**
+ * fimc_find_format - lookup fimc color format by fourcc or media bus format
+ * @pixelformat: fourcc to match, ignored if null
+ * @mbus_code: media bus code to match, ignored if null
+ * @mask: the color flags to match
+ * @index: offset in the fimc_formats array, ignored if negative
+ */
+struct fimc_fmt *fimc_find_format(const u32 *pixelformat, const u32 *mbus_code,
+ unsigned int mask, int index)
+{
+ struct fimc_fmt *fmt, *def_fmt = NULL;
+ unsigned int i;
+ int id = 0;
+
+ if (index >= (int)ARRAY_SIZE(fimc_formats))
+ return NULL;
+
+ for (i = 0; i < ARRAY_SIZE(fimc_formats); ++i) {
+ fmt = &fimc_formats[i];
+ if (!(fmt->flags & mask))
+ continue;
+ if (pixelformat && fmt->fourcc == *pixelformat)
+ return fmt;
+ if (mbus_code && fmt->mbus_code == *mbus_code)
+ return fmt;
+ if (index == id)
+ def_fmt = fmt;
+ id++;
+ }
+ return def_fmt;
+}
+
+static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f)
+{
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct samsung_fimc_variant *variant = fimc->variant;
+ struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+ struct fimc_fmt *fmt;
+ u32 max_w, mod_x, mod_y;
+
+ if (!IS_M2M(f->type))
+ return -EINVAL;
+
+ dbg("w: %d, h: %d", pix->width, pix->height);
+
+ fmt = fimc_find_format(&pix->pixelformat, NULL,
+ get_m2m_fmt_flags(f->type), 0);
+ if (WARN(fmt == NULL, "Pixel format lookup failed"))
+ return -EINVAL;
+
+ if (pix->field == V4L2_FIELD_ANY)
+ pix->field = V4L2_FIELD_NONE;
+ else if (pix->field != V4L2_FIELD_NONE)
+ return -EINVAL;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ max_w = variant->pix_limit->scaler_dis_w;
+ mod_x = ffs(variant->min_inp_pixsize) - 1;
+ } else {
+ max_w = variant->pix_limit->out_rot_dis_w;
+ mod_x = ffs(variant->min_out_pixsize) - 1;
+ }
+
+ if (tiled_fmt(fmt)) {
+ mod_x = 6; /* 64 x 32 pixels tile */
+ mod_y = 5;
+ } else {
+ if (variant->min_vsize_align == 1)
+ mod_y = fimc_fmt_is_rgb(fmt->color) ? 0 : 1;
+ else
+ mod_y = ffs(variant->min_vsize_align) - 1;
+ }
+
+ v4l_bound_align_image(&pix->width, 16, max_w, mod_x,
+ &pix->height, 8, variant->pix_limit->scaler_dis_w, mod_y, 0);
+
+ fimc_adjust_mplane_format(fmt, pix->width, pix->height, &f->fmt.pix_mp);
+ return 0;
+}
+
+static int fimc_m2m_try_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+
+ return fimc_try_fmt_mplane(ctx, f);
+}
+
+static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct vb2_queue *vq;
+ struct fimc_frame *frame;
+ struct v4l2_pix_format_mplane *pix;
+ int i, ret = 0;
+
+ ret = fimc_try_fmt_mplane(ctx, f);
+ if (ret)
+ return ret;
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+
+ if (vb2_is_busy(vq)) {
+ v4l2_err(fimc->m2m.vfd, "queue (%d) busy\n", f->type);
+ return -EBUSY;
+ }
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ frame = &ctx->s_frame;
+ else
+ frame = &ctx->d_frame;
+
+ pix = &f->fmt.pix_mp;
+ frame->fmt = fimc_find_format(&pix->pixelformat, NULL,
+ get_m2m_fmt_flags(f->type), 0);
+ if (!frame->fmt)
+ return -EINVAL;
+
+ /* Update RGB Alpha control state and value range */
+ fimc_alpha_ctrl_update(ctx);
+
+ for (i = 0; i < frame->fmt->colplanes; i++) {
+ frame->payload[i] =
+ (pix->width * pix->height * frame->fmt->depth[i]) / 8;
+ }
+
+ fimc_fill_frame(frame, f);
+
+ ctx->scaler.enabled = 1;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ fimc_ctx_state_lock_set(FIMC_PARAMS | FIMC_DST_FMT, ctx);
+ else
+ fimc_ctx_state_lock_set(FIMC_PARAMS | FIMC_SRC_FMT, ctx);
+
+ dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height);
+
+ return 0;
+}
+
+static int fimc_m2m_reqbufs(struct file *file, void *fh,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+
+ return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
+}
+
+static int fimc_m2m_querybuf(struct file *file, void *fh,
+ struct v4l2_buffer *buf)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+
+ return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
+}
+
+static int fimc_m2m_qbuf(struct file *file, void *fh,
+ struct v4l2_buffer *buf)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+
+ return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int fimc_m2m_dqbuf(struct file *file, void *fh,
+ struct v4l2_buffer *buf)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+
+ return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int fimc_m2m_streamon(struct file *file, void *fh,
+ enum v4l2_buf_type type)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+
+ /* The source and target color format need to be set */
+ if (V4L2_TYPE_IS_OUTPUT(type)) {
+ if (!fimc_ctx_state_is_set(FIMC_SRC_FMT, ctx))
+ return -EINVAL;
+ } else if (!fimc_ctx_state_is_set(FIMC_DST_FMT, ctx)) {
+ return -EINVAL;
+ }
+
+ return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
+}
+
+static int fimc_m2m_streamoff(struct file *file, void *fh,
+ enum v4l2_buf_type type)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+
+ return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
+}
+
+static int fimc_m2m_cropcap(struct file *file, void *fh,
+ struct v4l2_cropcap *cr)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+ struct fimc_frame *frame;
+
+ frame = ctx_get_frame(ctx, cr->type);
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ cr->bounds.left = 0;
+ cr->bounds.top = 0;
+ cr->bounds.width = frame->o_width;
+ cr->bounds.height = frame->o_height;
+ cr->defrect = cr->bounds;
+
+ return 0;
+}
+
+static int fimc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+ struct fimc_frame *frame;
+
+ frame = ctx_get_frame(ctx, cr->type);
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ cr->c.left = frame->offs_h;
+ cr->c.top = frame->offs_v;
+ cr->c.width = frame->width;
+ cr->c.height = frame->height;
+
+ return 0;
+}
+
+static int fimc_m2m_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr)
+{
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct fimc_frame *f;
+ u32 min_size, halign, depth = 0;
+ int i;
+
+ if (cr->c.top < 0 || cr->c.left < 0) {
+ v4l2_err(fimc->m2m.vfd,
+ "doesn't support negative values for top & left\n");
+ return -EINVAL;
+ }
+ if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ f = &ctx->d_frame;
+ else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ f = &ctx->s_frame;
+ else
+ return -EINVAL;
+
+ min_size = (f == &ctx->s_frame) ?
+ fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize;
+
+ /* Get pixel alignment constraints. */
+ if (fimc->variant->min_vsize_align == 1)
+ halign = fimc_fmt_is_rgb(f->fmt->color) ? 0 : 1;
+ else
+ halign = ffs(fimc->variant->min_vsize_align) - 1;
+
+ for (i = 0; i < f->fmt->colplanes; i++)
+ depth += f->fmt->depth[i];
+
+ v4l_bound_align_image(&cr->c.width, min_size, f->o_width,
+ ffs(min_size) - 1,
+ &cr->c.height, min_size, f->o_height,
+ halign, 64/(ALIGN(depth, 8)));
+
+ /* adjust left/top if cropping rectangle is out of bounds */
+ if (cr->c.left + cr->c.width > f->o_width)
+ cr->c.left = f->o_width - cr->c.width;
+ if (cr->c.top + cr->c.height > f->o_height)
+ cr->c.top = f->o_height - cr->c.height;
+
+ cr->c.left = round_down(cr->c.left, min_size);
+ cr->c.top = round_down(cr->c.top, fimc->variant->hor_offs_align);
+
+ dbg("l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d",
+ cr->c.left, cr->c.top, cr->c.width, cr->c.height,
+ f->f_width, f->f_height);
+
+ return 0;
+}
+
+static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct fimc_frame *f;
+ int ret;
+
+ ret = fimc_m2m_try_crop(ctx, cr);
+ if (ret)
+ return ret;
+
+ f = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ?
+ &ctx->s_frame : &ctx->d_frame;
+
+ /* Check to see if scaling ratio is within supported range */
+ if (fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) {
+ if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ret = fimc_check_scaler_ratio(ctx, cr->c.width,
+ cr->c.height, ctx->d_frame.width,
+ ctx->d_frame.height, ctx->rotation);
+ } else {
+ ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width,
+ ctx->s_frame.height, cr->c.width,
+ cr->c.height, ctx->rotation);
+ }
+ if (ret) {
+ v4l2_err(fimc->m2m.vfd, "Out of scaler range\n");
+ return -EINVAL;
+ }
+ }
+
+ f->offs_h = cr->c.left;
+ f->offs_v = cr->c.top;
+ f->width = cr->c.width;
+ f->height = cr->c.height;
+
+ fimc_ctx_state_lock_set(FIMC_PARAMS, ctx);
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = {
+ .vidioc_querycap = fimc_m2m_querycap,
+
+ .vidioc_enum_fmt_vid_cap_mplane = fimc_m2m_enum_fmt_mplane,
+ .vidioc_enum_fmt_vid_out_mplane = fimc_m2m_enum_fmt_mplane,
+
+ .vidioc_g_fmt_vid_cap_mplane = fimc_m2m_g_fmt_mplane,
+ .vidioc_g_fmt_vid_out_mplane = fimc_m2m_g_fmt_mplane,
+
+ .vidioc_try_fmt_vid_cap_mplane = fimc_m2m_try_fmt_mplane,
+ .vidioc_try_fmt_vid_out_mplane = fimc_m2m_try_fmt_mplane,
+
+ .vidioc_s_fmt_vid_cap_mplane = fimc_m2m_s_fmt_mplane,
+ .vidioc_s_fmt_vid_out_mplane = fimc_m2m_s_fmt_mplane,
+
+ .vidioc_reqbufs = fimc_m2m_reqbufs,
+ .vidioc_querybuf = fimc_m2m_querybuf,
+
+ .vidioc_qbuf = fimc_m2m_qbuf,
+ .vidioc_dqbuf = fimc_m2m_dqbuf,
+
+ .vidioc_streamon = fimc_m2m_streamon,
+ .vidioc_streamoff = fimc_m2m_streamoff,
+
+ .vidioc_g_crop = fimc_m2m_g_crop,
+ .vidioc_s_crop = fimc_m2m_s_crop,
+ .vidioc_cropcap = fimc_m2m_cropcap
+
+};
+
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct fimc_ctx *ctx = priv;
+ int ret;
+
+ memset(src_vq, 0, sizeof(*src_vq));
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ src_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ src_vq->drv_priv = ctx;
+ src_vq->ops = &fimc_qops;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ memset(dst_vq, 0, sizeof(*dst_vq));
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ dst_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ dst_vq->drv_priv = ctx;
+ dst_vq->ops = &fimc_qops;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+
+ return vb2_queue_init(dst_vq);
+}
+
+static int fimc_m2m_open(struct file *file)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_ctx *ctx;
+ int ret;
+
+ dbg("pid: %d, state: 0x%lx, refcnt: %d",
+ task_pid_nr(current), fimc->state, fimc->vid_cap.refcnt);
+
+ /*
+ * Return if the corresponding video capture node
+ * is already opened.
+ */
+ if (fimc->vid_cap.refcnt > 0)
+ return -EBUSY;
+
+ ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ v4l2_fh_init(&ctx->fh, fimc->m2m.vfd);
+ ctx->fimc_dev = fimc;
+
+ /* Default color format */
+ ctx->s_frame.fmt = &fimc_formats[0];
+ ctx->d_frame.fmt = &fimc_formats[0];
+
+ ret = fimc_ctrls_create(ctx);
+ if (ret)
+ goto error_fh;
+
+ /* Use separate control handler per file handle */
+ ctx->fh.ctrl_handler = &ctx->ctrl_handler;
+ file->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh);
+
+ /* Setup the device context for memory-to-memory mode */
+ ctx->state = FIMC_CTX_M2M;
+ ctx->flags = 0;
+ ctx->in_path = FIMC_DMA;
+ ctx->out_path = FIMC_DMA;
+ spin_lock_init(&ctx->slock);
+
+ ctx->m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init);
+ if (IS_ERR(ctx->m2m_ctx)) {
+ ret = PTR_ERR(ctx->m2m_ctx);
+ goto error_c;
+ }
+
+ if (fimc->m2m.refcnt++ == 0)
+ set_bit(ST_M2M_RUN, &fimc->state);
+ return 0;
+
+error_c:
+ fimc_ctrls_delete(ctx);
+error_fh:
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+ return ret;
+}
+
+static int fimc_m2m_release(struct file *file)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
+ struct fimc_dev *fimc = ctx->fimc_dev;
+
+ dbg("pid: %d, state: 0x%lx, refcnt= %d",
+ task_pid_nr(current), fimc->state, fimc->m2m.refcnt);
+
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
+ fimc_ctrls_delete(ctx);
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+
+ if (--fimc->m2m.refcnt <= 0)
+ clear_bit(ST_M2M_RUN, &fimc->state);
+ kfree(ctx);
+ return 0;
+}
+
+static unsigned int fimc_m2m_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
+
+ return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+}
+
+
+static int fimc_m2m_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
+
+ return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
+}
+
+static const struct v4l2_file_operations fimc_m2m_fops = {
+ .owner = THIS_MODULE,
+ .open = fimc_m2m_open,
+ .release = fimc_m2m_release,
+ .poll = fimc_m2m_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = fimc_m2m_mmap,
+};
+
+static struct v4l2_m2m_ops m2m_ops = {
+ .device_run = fimc_dma_run,
+ .job_abort = fimc_job_abort,
+};
+
+int fimc_register_m2m_device(struct fimc_dev *fimc,
+ struct v4l2_device *v4l2_dev)
+{
+ struct video_device *vfd;
+ struct platform_device *pdev;
+ int ret = 0;
+
+ if (!fimc)
+ return -ENODEV;
+
+ pdev = fimc->pdev;
+ fimc->v4l2_dev = v4l2_dev;
+
+ vfd = video_device_alloc();
+ if (!vfd) {
+ v4l2_err(v4l2_dev, "Failed to allocate video device\n");
+ return -ENOMEM;
+ }
+
+ vfd->fops = &fimc_m2m_fops;
+ vfd->ioctl_ops = &fimc_m2m_ioctl_ops;
+ vfd->v4l2_dev = v4l2_dev;
+ vfd->minor = -1;
+ vfd->release = video_device_release;
+ vfd->lock = &fimc->lock;
+
+ snprintf(vfd->name, sizeof(vfd->name), "%s.m2m", dev_name(&pdev->dev));
+ video_set_drvdata(vfd, fimc);
+
+ fimc->m2m.vfd = vfd;
+ fimc->m2m.m2m_dev = v4l2_m2m_init(&m2m_ops);
+ if (IS_ERR(fimc->m2m.m2m_dev)) {
+ v4l2_err(v4l2_dev, "failed to initialize v4l2-m2m device\n");
+ ret = PTR_ERR(fimc->m2m.m2m_dev);
+ goto err_init;
+ }
+
+ ret = media_entity_init(&vfd->entity, 0, NULL, 0);
+ if (!ret)
+ return 0;
+
+ v4l2_m2m_release(fimc->m2m.m2m_dev);
+err_init:
+ video_device_release(fimc->m2m.vfd);
+ return ret;
+}
+
+void fimc_unregister_m2m_device(struct fimc_dev *fimc)
+{
+ if (!fimc)
+ return;
+
+ if (fimc->m2m.m2m_dev)
+ v4l2_m2m_release(fimc->m2m.m2m_dev);
+ if (fimc->m2m.vfd) {
+ media_entity_cleanup(&fimc->m2m.vfd->entity);
+ /* Can also be called if video device wasn't registered */
+ video_unregister_device(fimc->m2m.vfd);
+ }
+}
+
+static void fimc_clk_put(struct fimc_dev *fimc)
+{
+ int i;
+ for (i = 0; i < fimc->num_clocks; i++) {
+ if (IS_ERR_OR_NULL(fimc->clock[i]))
+ continue;
+ clk_unprepare(fimc->clock[i]);
+ clk_put(fimc->clock[i]);
+ fimc->clock[i] = NULL;
+ }
+}
+
+static int fimc_clk_get(struct fimc_dev *fimc)
+{
+ int i, ret;
+
+ for (i = 0; i < fimc->num_clocks; i++) {
+ fimc->clock[i] = clk_get(&fimc->pdev->dev, fimc_clocks[i]);
+ if (IS_ERR(fimc->clock[i]))
+ goto err;
+ ret = clk_prepare(fimc->clock[i]);
+ if (ret < 0) {
+ clk_put(fimc->clock[i]);
+ fimc->clock[i] = NULL;
+ goto err;
+ }
+ }
+ return 0;
+err:
+ fimc_clk_put(fimc);
+ dev_err(&fimc->pdev->dev, "failed to get clock: %s\n",
+ fimc_clocks[i]);
+ return -ENXIO;
+}
+
+static int fimc_m2m_suspend(struct fimc_dev *fimc)
+{
+ unsigned long flags;
+ int timeout;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ if (!fimc_m2m_pending(fimc)) {
+ spin_unlock_irqrestore(&fimc->slock, flags);
+ return 0;
+ }
+ clear_bit(ST_M2M_SUSPENDED, &fimc->state);
+ set_bit(ST_M2M_SUSPENDING, &fimc->state);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ timeout = wait_event_timeout(fimc->irq_queue,
+ test_bit(ST_M2M_SUSPENDED, &fimc->state),
+ FIMC_SHUTDOWN_TIMEOUT);
+
+ clear_bit(ST_M2M_SUSPENDING, &fimc->state);
+ return timeout == 0 ? -EAGAIN : 0;
+}
+
+static int fimc_m2m_resume(struct fimc_dev *fimc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ /* Clear for full H/W setup in first run after resume */
+ fimc->m2m.ctx = NULL;
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ if (test_and_clear_bit(ST_M2M_SUSPENDED, &fimc->state))
+ fimc_m2m_job_finish(fimc->m2m.ctx,
+ VB2_BUF_STATE_ERROR);
+ return 0;
+}
+
+static int fimc_probe(struct platform_device *pdev)
+{
+ struct fimc_dev *fimc;
+ struct resource *res;
+ struct samsung_fimc_driverdata *drv_data;
+ struct s5p_platform_fimc *pdata;
+ int ret = 0;
+
+ drv_data = (struct samsung_fimc_driverdata *)
+ platform_get_device_id(pdev)->driver_data;
+
+ if (pdev->id >= drv_data->num_entities) {
+ dev_err(&pdev->dev, "Invalid platform device id: %d\n",
+ pdev->id);
+ return -EINVAL;
+ }
+
+ fimc = devm_kzalloc(&pdev->dev, sizeof(*fimc), GFP_KERNEL);
+ if (!fimc)
+ return -ENOMEM;
+
+ fimc->id = pdev->id;
+
+ fimc->variant = drv_data->variant[fimc->id];
+ fimc->pdev = pdev;
+ pdata = pdev->dev.platform_data;
+ fimc->pdata = pdata;
+
+ init_waitqueue_head(&fimc->irq_queue);
+ spin_lock_init(&fimc->slock);
+ mutex_init(&fimc->lock);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ fimc->regs = devm_request_and_ioremap(&pdev->dev, res);
+ if (fimc->regs == NULL) {
+ dev_err(&pdev->dev, "Failed to obtain io memory\n");
+ return -ENOENT;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "Failed to get IRQ resource\n");
+ return -ENXIO;
+ }
+ fimc->irq = res->start;
+
+ fimc->num_clocks = MAX_FIMC_CLOCKS;
+ ret = fimc_clk_get(fimc);
+ if (ret)
+ return ret;
+ clk_set_rate(fimc->clock[CLK_BUS], drv_data->lclk_frequency);
+ clk_enable(fimc->clock[CLK_BUS]);
+
+ platform_set_drvdata(pdev, fimc);
+
+ ret = devm_request_irq(&pdev->dev, fimc->irq, fimc_irq_handler,
+ 0, pdev->name, fimc);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to install irq (%d)\n", ret);
+ goto err_clk;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (ret < 0)
+ goto err_clk;
+ /* Initialize contiguous memory allocator */
+ fimc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(fimc->alloc_ctx)) {
+ ret = PTR_ERR(fimc->alloc_ctx);
+ goto err_pm;
+ }
+
+ dev_dbg(&pdev->dev, "FIMC.%d registered successfully\n", fimc->id);
+
+ pm_runtime_put(&pdev->dev);
+ return 0;
+
+err_pm:
+ pm_runtime_put(&pdev->dev);
+err_clk:
+ fimc_clk_put(fimc);
+ return ret;
+}
+
+static int fimc_runtime_resume(struct device *dev)
+{
+ struct fimc_dev *fimc = dev_get_drvdata(dev);
+
+ dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state);
+
+ /* Enable clocks and perform basic initalization */
+ clk_enable(fimc->clock[CLK_GATE]);
+ fimc_hw_reset(fimc);
+
+ /* Resume the capture or mem-to-mem device */
+ if (fimc_capture_busy(fimc))
+ return fimc_capture_resume(fimc);
+
+ return fimc_m2m_resume(fimc);
+}
+
+static int fimc_runtime_suspend(struct device *dev)
+{
+ struct fimc_dev *fimc = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (fimc_capture_busy(fimc))
+ ret = fimc_capture_suspend(fimc);
+ else
+ ret = fimc_m2m_suspend(fimc);
+ if (!ret)
+ clk_disable(fimc->clock[CLK_GATE]);
+
+ dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state);
+ return ret;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int fimc_resume(struct device *dev)
+{
+ struct fimc_dev *fimc = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state);
+
+ /* Do not resume if the device was idle before system suspend */
+ spin_lock_irqsave(&fimc->slock, flags);
+ if (!test_and_clear_bit(ST_LPM, &fimc->state) ||
+ (!fimc_m2m_active(fimc) && !fimc_capture_busy(fimc))) {
+ spin_unlock_irqrestore(&fimc->slock, flags);
+ return 0;
+ }
+ fimc_hw_reset(fimc);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ if (fimc_capture_busy(fimc))
+ return fimc_capture_resume(fimc);
+
+ return fimc_m2m_resume(fimc);
+}
+
+static int fimc_suspend(struct device *dev)
+{
+ struct fimc_dev *fimc = dev_get_drvdata(dev);
+
+ dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state);
+
+ if (test_and_set_bit(ST_LPM, &fimc->state))
+ return 0;
+ if (fimc_capture_busy(fimc))
+ return fimc_capture_suspend(fimc);
+
+ return fimc_m2m_suspend(fimc);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static int __devexit fimc_remove(struct platform_device *pdev)
+{
+ struct fimc_dev *fimc = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+
+ vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx);
+
+ clk_disable(fimc->clock[CLK_BUS]);
+ fimc_clk_put(fimc);
+
+ dev_info(&pdev->dev, "driver unloaded\n");
+ return 0;
+}
+
+/* Image pixel limits, similar across several FIMC HW revisions. */
+static struct fimc_pix_limit s5p_pix_limit[4] = {
+ [0] = {
+ .scaler_en_w = 3264,
+ .scaler_dis_w = 8192,
+ .in_rot_en_h = 1920,
+ .in_rot_dis_w = 8192,
+ .out_rot_en_w = 1920,
+ .out_rot_dis_w = 4224,
+ },
+ [1] = {
+ .scaler_en_w = 4224,
+ .scaler_dis_w = 8192,
+ .in_rot_en_h = 1920,
+ .in_rot_dis_w = 8192,
+ .out_rot_en_w = 1920,
+ .out_rot_dis_w = 4224,
+ },
+ [2] = {
+ .scaler_en_w = 1920,
+ .scaler_dis_w = 8192,
+ .in_rot_en_h = 1280,
+ .in_rot_dis_w = 8192,
+ .out_rot_en_w = 1280,
+ .out_rot_dis_w = 1920,
+ },
+ [3] = {
+ .scaler_en_w = 1920,
+ .scaler_dis_w = 8192,
+ .in_rot_en_h = 1366,
+ .in_rot_dis_w = 8192,
+ .out_rot_en_w = 1366,
+ .out_rot_dis_w = 1920,
+ },
+};
+
+static struct samsung_fimc_variant fimc0_variant_s5p = {
+ .has_inp_rot = 1,
+ .has_out_rot = 1,
+ .has_cam_if = 1,
+ .min_inp_pixsize = 16,
+ .min_out_pixsize = 16,
+ .hor_offs_align = 8,
+ .min_vsize_align = 16,
+ .out_buf_count = 4,
+ .pix_limit = &s5p_pix_limit[0],
+};
+
+static struct samsung_fimc_variant fimc2_variant_s5p = {
+ .has_cam_if = 1,
+ .min_inp_pixsize = 16,
+ .min_out_pixsize = 16,
+ .hor_offs_align = 8,
+ .min_vsize_align = 16,
+ .out_buf_count = 4,
+ .pix_limit = &s5p_pix_limit[1],
+};
+
+static struct samsung_fimc_variant fimc0_variant_s5pv210 = {
+ .pix_hoff = 1,
+ .has_inp_rot = 1,
+ .has_out_rot = 1,
+ .has_cam_if = 1,
+ .min_inp_pixsize = 16,
+ .min_out_pixsize = 16,
+ .hor_offs_align = 8,
+ .min_vsize_align = 16,
+ .out_buf_count = 4,
+ .pix_limit = &s5p_pix_limit[1],
+};
+
+static struct samsung_fimc_variant fimc1_variant_s5pv210 = {
+ .pix_hoff = 1,
+ .has_inp_rot = 1,
+ .has_out_rot = 1,
+ .has_cam_if = 1,
+ .has_mainscaler_ext = 1,
+ .min_inp_pixsize = 16,
+ .min_out_pixsize = 16,
+ .hor_offs_align = 1,
+ .min_vsize_align = 1,
+ .out_buf_count = 4,
+ .pix_limit = &s5p_pix_limit[2],
+};
+
+static struct samsung_fimc_variant fimc2_variant_s5pv210 = {
+ .has_cam_if = 1,
+ .pix_hoff = 1,
+ .min_inp_pixsize = 16,
+ .min_out_pixsize = 16,
+ .hor_offs_align = 8,
+ .min_vsize_align = 16,
+ .out_buf_count = 4,
+ .pix_limit = &s5p_pix_limit[2],
+};
+
+static struct samsung_fimc_variant fimc0_variant_exynos4 = {
+ .pix_hoff = 1,
+ .has_inp_rot = 1,
+ .has_out_rot = 1,
+ .has_cam_if = 1,
+ .has_cistatus2 = 1,
+ .has_mainscaler_ext = 1,
+ .has_alpha = 1,
+ .min_inp_pixsize = 16,
+ .min_out_pixsize = 16,
+ .hor_offs_align = 2,
+ .min_vsize_align = 1,
+ .out_buf_count = 32,
+ .pix_limit = &s5p_pix_limit[1],
+};
+
+static struct samsung_fimc_variant fimc3_variant_exynos4 = {
+ .pix_hoff = 1,
+ .has_cam_if = 1,
+ .has_cistatus2 = 1,
+ .has_mainscaler_ext = 1,
+ .has_alpha = 1,
+ .min_inp_pixsize = 16,
+ .min_out_pixsize = 16,
+ .hor_offs_align = 2,
+ .min_vsize_align = 1,
+ .out_buf_count = 32,
+ .pix_limit = &s5p_pix_limit[3],
+};
+
+/* S5PC100 */
+static struct samsung_fimc_driverdata fimc_drvdata_s5p = {
+ .variant = {
+ [0] = &fimc0_variant_s5p,
+ [1] = &fimc0_variant_s5p,
+ [2] = &fimc2_variant_s5p,
+ },
+ .num_entities = 3,
+ .lclk_frequency = 133000000UL,
+};
+
+/* S5PV210, S5PC110 */
+static struct samsung_fimc_driverdata fimc_drvdata_s5pv210 = {
+ .variant = {
+ [0] = &fimc0_variant_s5pv210,
+ [1] = &fimc1_variant_s5pv210,
+ [2] = &fimc2_variant_s5pv210,
+ },
+ .num_entities = 3,
+ .lclk_frequency = 166000000UL,
+};
+
+/* S5PV310, S5PC210 */
+static struct samsung_fimc_driverdata fimc_drvdata_exynos4 = {
+ .variant = {
+ [0] = &fimc0_variant_exynos4,
+ [1] = &fimc0_variant_exynos4,
+ [2] = &fimc0_variant_exynos4,
+ [3] = &fimc3_variant_exynos4,
+ },
+ .num_entities = 4,
+ .lclk_frequency = 166000000UL,
+};
+
+static struct platform_device_id fimc_driver_ids[] = {
+ {
+ .name = "s5p-fimc",
+ .driver_data = (unsigned long)&fimc_drvdata_s5p,
+ }, {
+ .name = "s5pv210-fimc",
+ .driver_data = (unsigned long)&fimc_drvdata_s5pv210,
+ }, {
+ .name = "exynos4-fimc",
+ .driver_data = (unsigned long)&fimc_drvdata_exynos4,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, fimc_driver_ids);
+
+static const struct dev_pm_ops fimc_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(fimc_suspend, fimc_resume)
+ SET_RUNTIME_PM_OPS(fimc_runtime_suspend, fimc_runtime_resume, NULL)
+};
+
+static struct platform_driver fimc_driver = {
+ .probe = fimc_probe,
+ .remove = __devexit_p(fimc_remove),
+ .id_table = fimc_driver_ids,
+ .driver = {
+ .name = FIMC_MODULE_NAME,
+ .owner = THIS_MODULE,
+ .pm = &fimc_pm_ops,
+ }
+};
+
+int __init fimc_register_driver(void)
+{
+ return platform_driver_probe(&fimc_driver, fimc_probe);
+}
+
+void __exit fimc_unregister_driver(void)
+{
+ platform_driver_unregister(&fimc_driver);
+}
diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h
new file mode 100644
index 00000000..84fd8355
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/fimc-core.h
@@ -0,0 +1,826 @@
+/*
+ * Copyright (C) 2010 - 2011 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef FIMC_CORE_H_
+#define FIMC_CORE_H_
+
+/*#define DEBUG*/
+
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <linux/io.h>
+
+#include <media/media-entity.h>
+#include <media/videobuf2-core.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-mediabus.h>
+#include <media/s5p_fimc.h>
+
+#include "regs-fimc.h"
+
+#define err(fmt, args...) \
+ printk(KERN_ERR "%s:%d: " fmt "\n", __func__, __LINE__, ##args)
+
+#define dbg(fmt, args...) \
+ pr_debug("%s:%d: " fmt "\n", __func__, __LINE__, ##args)
+
+/* Time to wait for next frame VSYNC interrupt while stopping operation. */
+#define FIMC_SHUTDOWN_TIMEOUT ((100*HZ)/1000)
+#define MAX_FIMC_CLOCKS 2
+#define FIMC_MODULE_NAME "s5p-fimc"
+#define FIMC_MAX_DEVS 4
+#define FIMC_MAX_OUT_BUFS 4
+#define SCALER_MAX_HRATIO 64
+#define SCALER_MAX_VRATIO 64
+#define DMA_MIN_SIZE 8
+#define FIMC_CAMIF_MAX_HEIGHT 0x2000
+
+/* indices to the clocks array */
+enum {
+ CLK_BUS,
+ CLK_GATE,
+};
+
+enum fimc_dev_flags {
+ ST_LPM,
+ /* m2m node */
+ ST_M2M_RUN,
+ ST_M2M_PEND,
+ ST_M2M_SUSPENDING,
+ ST_M2M_SUSPENDED,
+ /* capture node */
+ ST_CAPT_PEND,
+ ST_CAPT_RUN,
+ ST_CAPT_STREAM,
+ ST_CAPT_ISP_STREAM,
+ ST_CAPT_SUSPENDED,
+ ST_CAPT_SHUT,
+ ST_CAPT_BUSY,
+ ST_CAPT_APPLY_CFG,
+ ST_CAPT_JPEG,
+};
+
+#define fimc_m2m_active(dev) test_bit(ST_M2M_RUN, &(dev)->state)
+#define fimc_m2m_pending(dev) test_bit(ST_M2M_PEND, &(dev)->state)
+
+#define fimc_capture_running(dev) test_bit(ST_CAPT_RUN, &(dev)->state)
+#define fimc_capture_pending(dev) test_bit(ST_CAPT_PEND, &(dev)->state)
+#define fimc_capture_busy(dev) test_bit(ST_CAPT_BUSY, &(dev)->state)
+
+enum fimc_datapath {
+ FIMC_CAMERA,
+ FIMC_DMA,
+ FIMC_LCDFIFO,
+ FIMC_WRITEBACK
+};
+
+enum fimc_color_fmt {
+ S5P_FIMC_RGB444 = 0x10,
+ S5P_FIMC_RGB555,
+ S5P_FIMC_RGB565,
+ S5P_FIMC_RGB666,
+ S5P_FIMC_RGB888,
+ S5P_FIMC_RGB30_LOCAL,
+ S5P_FIMC_YCBCR420 = 0x20,
+ S5P_FIMC_YCBYCR422,
+ S5P_FIMC_YCRYCB422,
+ S5P_FIMC_CBYCRY422,
+ S5P_FIMC_CRYCBY422,
+ S5P_FIMC_YCBCR444_LOCAL,
+ S5P_FIMC_JPEG = 0x40,
+};
+
+#define fimc_fmt_is_rgb(x) (!!((x) & 0x10))
+#define fimc_fmt_is_jpeg(x) (!!((x) & 0x40))
+
+#define IS_M2M(__strt) ((__strt) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE || \
+ __strt == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+
+/* Cb/Cr chrominance components order for 2 plane Y/CbCr 4:2:2 formats. */
+#define S5P_FIMC_LSB_CRCB S5P_CIOCTRL_ORDER422_2P_LSB_CRCB
+
+/* The embedded image effect selection */
+#define S5P_FIMC_EFFECT_ORIGINAL S5P_CIIMGEFF_FIN_BYPASS
+#define S5P_FIMC_EFFECT_ARBITRARY S5P_CIIMGEFF_FIN_ARBITRARY
+#define S5P_FIMC_EFFECT_NEGATIVE S5P_CIIMGEFF_FIN_NEGATIVE
+#define S5P_FIMC_EFFECT_ARTFREEZE S5P_CIIMGEFF_FIN_ARTFREEZE
+#define S5P_FIMC_EFFECT_EMBOSSING S5P_CIIMGEFF_FIN_EMBOSSING
+#define S5P_FIMC_EFFECT_SIKHOUETTE S5P_CIIMGEFF_FIN_SILHOUETTE
+
+/* The hardware context state. */
+#define FIMC_PARAMS (1 << 0)
+#define FIMC_SRC_ADDR (1 << 1)
+#define FIMC_DST_ADDR (1 << 2)
+#define FIMC_SRC_FMT (1 << 3)
+#define FIMC_DST_FMT (1 << 4)
+#define FIMC_DST_CROP (1 << 5)
+#define FIMC_CTX_M2M (1 << 16)
+#define FIMC_CTX_CAP (1 << 17)
+#define FIMC_CTX_SHUT (1 << 18)
+
+/* Image conversion flags */
+#define FIMC_IN_DMA_ACCESS_TILED (1 << 0)
+#define FIMC_IN_DMA_ACCESS_LINEAR (0 << 0)
+#define FIMC_OUT_DMA_ACCESS_TILED (1 << 1)
+#define FIMC_OUT_DMA_ACCESS_LINEAR (0 << 1)
+#define FIMC_SCAN_MODE_PROGRESSIVE (0 << 2)
+#define FIMC_SCAN_MODE_INTERLACED (1 << 2)
+/*
+ * YCbCr data dynamic range for RGB-YUV color conversion.
+ * Y/Cb/Cr: (0 ~ 255) */
+#define FIMC_COLOR_RANGE_WIDE (0 << 3)
+/* Y (16 ~ 235), Cb/Cr (16 ~ 240) */
+#define FIMC_COLOR_RANGE_NARROW (1 << 3)
+
+/**
+ * struct fimc_fmt - the driver's internal color format data
+ * @mbus_code: Media Bus pixel code, -1 if not applicable
+ * @name: format description
+ * @fourcc: the fourcc code for this format, 0 if not applicable
+ * @color: the corresponding fimc_color_fmt
+ * @memplanes: number of physically non-contiguous data planes
+ * @colplanes: number of physically contiguous data planes
+ * @depth: per plane driver's private 'number of bits per pixel'
+ * @flags: flags indicating which operation mode format applies to
+ */
+struct fimc_fmt {
+ enum v4l2_mbus_pixelcode mbus_code;
+ char *name;
+ u32 fourcc;
+ u32 color;
+ u16 memplanes;
+ u16 colplanes;
+ u8 depth[VIDEO_MAX_PLANES];
+ u16 flags;
+#define FMT_FLAGS_CAM (1 << 0)
+#define FMT_FLAGS_M2M_IN (1 << 1)
+#define FMT_FLAGS_M2M_OUT (1 << 2)
+#define FMT_FLAGS_M2M (1 << 1 | 1 << 2)
+#define FMT_HAS_ALPHA (1 << 3)
+};
+
+/**
+ * struct fimc_dma_offset - pixel offset information for DMA
+ * @y_h: y value horizontal offset
+ * @y_v: y value vertical offset
+ * @cb_h: cb value horizontal offset
+ * @cb_v: cb value vertical offset
+ * @cr_h: cr value horizontal offset
+ * @cr_v: cr value vertical offset
+ */
+struct fimc_dma_offset {
+ int y_h;
+ int y_v;
+ int cb_h;
+ int cb_v;
+ int cr_h;
+ int cr_v;
+};
+
+/**
+ * struct fimc_effect - color effect information
+ * @type: effect type
+ * @pat_cb: cr value when type is "arbitrary"
+ * @pat_cr: cr value when type is "arbitrary"
+ */
+struct fimc_effect {
+ u32 type;
+ u8 pat_cb;
+ u8 pat_cr;
+};
+
+/**
+ * struct fimc_scaler - the configuration data for FIMC inetrnal scaler
+ * @scaleup_h: flag indicating scaling up horizontally
+ * @scaleup_v: flag indicating scaling up vertically
+ * @copy_mode: flag indicating transparent DMA transfer (no scaling
+ * and color format conversion)
+ * @enabled: flag indicating if the scaler is used
+ * @hfactor: horizontal shift factor
+ * @vfactor: vertical shift factor
+ * @pre_hratio: horizontal ratio of the prescaler
+ * @pre_vratio: vertical ratio of the prescaler
+ * @pre_dst_width: the prescaler's destination width
+ * @pre_dst_height: the prescaler's destination height
+ * @main_hratio: the main scaler's horizontal ratio
+ * @main_vratio: the main scaler's vertical ratio
+ * @real_width: source pixel (width - offset)
+ * @real_height: source pixel (height - offset)
+ */
+struct fimc_scaler {
+ unsigned int scaleup_h:1;
+ unsigned int scaleup_v:1;
+ unsigned int copy_mode:1;
+ unsigned int enabled:1;
+ u32 hfactor;
+ u32 vfactor;
+ u32 pre_hratio;
+ u32 pre_vratio;
+ u32 pre_dst_width;
+ u32 pre_dst_height;
+ u32 main_hratio;
+ u32 main_vratio;
+ u32 real_width;
+ u32 real_height;
+};
+
+/**
+ * struct fimc_addr - the FIMC physical address set for DMA
+ * @y: luminance plane physical address
+ * @cb: Cb plane physical address
+ * @cr: Cr plane physical address
+ */
+struct fimc_addr {
+ u32 y;
+ u32 cb;
+ u32 cr;
+};
+
+/**
+ * struct fimc_vid_buffer - the driver's video buffer
+ * @vb: v4l videobuf buffer
+ * @list: linked list structure for buffer queue
+ * @paddr: precalculated physical address set
+ * @index: buffer index for the output DMA engine
+ */
+struct fimc_vid_buffer {
+ struct vb2_buffer vb;
+ struct list_head list;
+ struct fimc_addr paddr;
+ int index;
+};
+
+/**
+ * struct fimc_frame - source/target frame properties
+ * @f_width: image full width (virtual screen size)
+ * @f_height: image full height (virtual screen size)
+ * @o_width: original image width as set by S_FMT
+ * @o_height: original image height as set by S_FMT
+ * @offs_h: image horizontal pixel offset
+ * @offs_v: image vertical pixel offset
+ * @width: image pixel width
+ * @height: image pixel weight
+ * @payload: image size in bytes (w x h x bpp)
+ * @paddr: image frame buffer physical addresses
+ * @dma_offset: DMA offset in bytes
+ * @fmt: fimc color format pointer
+ */
+struct fimc_frame {
+ u32 f_width;
+ u32 f_height;
+ u32 o_width;
+ u32 o_height;
+ u32 offs_h;
+ u32 offs_v;
+ u32 width;
+ u32 height;
+ unsigned long payload[VIDEO_MAX_PLANES];
+ struct fimc_addr paddr;
+ struct fimc_dma_offset dma_offset;
+ struct fimc_fmt *fmt;
+ u8 alpha;
+};
+
+/**
+ * struct fimc_m2m_device - v4l2 memory-to-memory device data
+ * @vfd: the video device node for v4l2 m2m mode
+ * @m2m_dev: v4l2 memory-to-memory device data
+ * @ctx: hardware context data
+ * @refcnt: the reference counter
+ */
+struct fimc_m2m_device {
+ struct video_device *vfd;
+ struct v4l2_m2m_dev *m2m_dev;
+ struct fimc_ctx *ctx;
+ int refcnt;
+};
+
+#define FIMC_SD_PAD_SINK 0
+#define FIMC_SD_PAD_SOURCE 1
+#define FIMC_SD_PADS_NUM 2
+
+/**
+ * struct fimc_vid_cap - camera capture device information
+ * @ctx: hardware context data
+ * @vfd: video device node for camera capture mode
+ * @subdev: subdev exposing the FIMC processing block
+ * @vd_pad: fimc video capture node pad
+ * @sd_pads: fimc video processing block pads
+ * @mf: media bus format at the FIMC camera input (and the scaler output) pad
+ * @pending_buf_q: the pending buffer queue head
+ * @active_buf_q: the queue head of buffers scheduled in hardware
+ * @vbq: the capture am video buffer queue
+ * @active_buf_cnt: number of video buffers scheduled in hardware
+ * @buf_index: index for managing the output DMA buffers
+ * @frame_count: the frame counter for statistics
+ * @reqbufs_count: the number of buffers requested in REQBUFS ioctl
+ * @input_index: input (camera sensor) index
+ * @refcnt: driver's private reference counter
+ * @input: capture input type, grp_id of the attached subdev
+ * @user_subdev_api: true if subdevs are not configured by the host driver
+ */
+struct fimc_vid_cap {
+ struct fimc_ctx *ctx;
+ struct vb2_alloc_ctx *alloc_ctx;
+ struct video_device *vfd;
+ struct v4l2_subdev *subdev;
+ struct media_pad vd_pad;
+ struct v4l2_mbus_framefmt mf;
+ struct media_pad sd_pads[FIMC_SD_PADS_NUM];
+ struct list_head pending_buf_q;
+ struct list_head active_buf_q;
+ struct vb2_queue vbq;
+ int active_buf_cnt;
+ int buf_index;
+ unsigned int frame_count;
+ unsigned int reqbufs_count;
+ int input_index;
+ int refcnt;
+ u32 input;
+ bool user_subdev_api;
+};
+
+/**
+ * struct fimc_pix_limit - image pixel size limits in various IP configurations
+ *
+ * @scaler_en_w: max input pixel width when the scaler is enabled
+ * @scaler_dis_w: max input pixel width when the scaler is disabled
+ * @in_rot_en_h: max input width with the input rotator is on
+ * @in_rot_dis_w: max input width with the input rotator is off
+ * @out_rot_en_w: max output width with the output rotator on
+ * @out_rot_dis_w: max output width with the output rotator off
+ */
+struct fimc_pix_limit {
+ u16 scaler_en_w;
+ u16 scaler_dis_w;
+ u16 in_rot_en_h;
+ u16 in_rot_dis_w;
+ u16 out_rot_en_w;
+ u16 out_rot_dis_w;
+};
+
+/**
+ * struct samsung_fimc_variant - camera interface variant information
+ *
+ * @pix_hoff: indicate whether horizontal offset is in pixels or in bytes
+ * @has_inp_rot: set if has input rotator
+ * @has_out_rot: set if has output rotator
+ * @has_cistatus2: 1 if CISTATUS2 register is present in this IP revision
+ * @has_mainscaler_ext: 1 if extended mainscaler ratios in CIEXTEN register
+ * are present in this IP revision
+ * @has_cam_if: set if this instance has a camera input interface
+ * @pix_limit: pixel size constraints for the scaler
+ * @min_inp_pixsize: minimum input pixel size
+ * @min_out_pixsize: minimum output pixel size
+ * @hor_offs_align: horizontal pixel offset aligment
+ * @min_vsize_align: minimum vertical pixel size alignment
+ * @out_buf_count: the number of buffers in output DMA sequence
+ */
+struct samsung_fimc_variant {
+ unsigned int pix_hoff:1;
+ unsigned int has_inp_rot:1;
+ unsigned int has_out_rot:1;
+ unsigned int has_cistatus2:1;
+ unsigned int has_mainscaler_ext:1;
+ unsigned int has_cam_if:1;
+ unsigned int has_alpha:1;
+ struct fimc_pix_limit *pix_limit;
+ u16 min_inp_pixsize;
+ u16 min_out_pixsize;
+ u16 hor_offs_align;
+ u16 min_vsize_align;
+ u16 out_buf_count;
+};
+
+/**
+ * struct samsung_fimc_driverdata - per device type driver data for init time.
+ *
+ * @variant: the variant information for this driver.
+ * @dev_cnt: number of fimc sub-devices available in SoC
+ * @lclk_frequency: fimc bus clock frequency
+ */
+struct samsung_fimc_driverdata {
+ struct samsung_fimc_variant *variant[FIMC_MAX_DEVS];
+ unsigned long lclk_frequency;
+ int num_entities;
+};
+
+struct fimc_pipeline {
+ struct media_pipeline *pipe;
+ struct v4l2_subdev *sensor;
+ struct v4l2_subdev *csis;
+};
+
+struct fimc_ctx;
+
+/**
+ * struct fimc_dev - abstraction for FIMC entity
+ * @slock: the spinlock protecting this data structure
+ * @lock: the mutex protecting this data structure
+ * @pdev: pointer to the FIMC platform device
+ * @pdata: pointer to the device platform data
+ * @variant: the IP variant information
+ * @id: FIMC device index (0..FIMC_MAX_DEVS)
+ * @num_clocks: the number of clocks managed by this device instance
+ * @clock: clocks required for FIMC operation
+ * @regs: the mapped hardware registers
+ * @irq: FIMC interrupt number
+ * @irq_queue: interrupt handler waitqueue
+ * @v4l2_dev: root v4l2_device
+ * @m2m: memory-to-memory V4L2 device information
+ * @vid_cap: camera capture device information
+ * @state: flags used to synchronize m2m and capture mode operation
+ * @alloc_ctx: videobuf2 memory allocator context
+ * @pipeline: fimc video capture pipeline data structure
+ */
+struct fimc_dev {
+ spinlock_t slock;
+ struct mutex lock;
+ struct platform_device *pdev;
+ struct s5p_platform_fimc *pdata;
+ struct samsung_fimc_variant *variant;
+ u16 id;
+ u16 num_clocks;
+ struct clk *clock[MAX_FIMC_CLOCKS];
+ void __iomem *regs;
+ int irq;
+ wait_queue_head_t irq_queue;
+ struct v4l2_device *v4l2_dev;
+ struct fimc_m2m_device m2m;
+ struct fimc_vid_cap vid_cap;
+ unsigned long state;
+ struct vb2_alloc_ctx *alloc_ctx;
+ struct fimc_pipeline pipeline;
+};
+
+/**
+ * fimc_ctx - the device context data
+ * @slock: spinlock protecting this data structure
+ * @s_frame: source frame properties
+ * @d_frame: destination frame properties
+ * @out_order_1p: output 1-plane YCBCR order
+ * @out_order_2p: output 2-plane YCBCR order
+ * @in_order_1p input 1-plane YCBCR order
+ * @in_order_2p: input 2-plane YCBCR order
+ * @in_path: input mode (DMA or camera)
+ * @out_path: output mode (DMA or FIFO)
+ * @scaler: image scaler properties
+ * @effect: image effect
+ * @rotation: image clockwise rotation in degrees
+ * @hflip: indicates image horizontal flip if set
+ * @vflip: indicates image vertical flip if set
+ * @flags: additional flags for image conversion
+ * @state: flags to keep track of user configuration
+ * @fimc_dev: the FIMC device this context applies to
+ * @m2m_ctx: memory-to-memory device context
+ * @fh: v4l2 file handle
+ * @ctrl_handler: v4l2 controls handler
+ * @ctrl_rotate image rotation control
+ * @ctrl_hflip horizontal flip control
+ * @ctrl_vflip vertical flip control
+ * @ctrl_alpha RGB alpha control
+ * @ctrls_rdy: true if the control handler is initialized
+ */
+struct fimc_ctx {
+ spinlock_t slock;
+ struct fimc_frame s_frame;
+ struct fimc_frame d_frame;
+ u32 out_order_1p;
+ u32 out_order_2p;
+ u32 in_order_1p;
+ u32 in_order_2p;
+ enum fimc_datapath in_path;
+ enum fimc_datapath out_path;
+ struct fimc_scaler scaler;
+ struct fimc_effect effect;
+ int rotation;
+ unsigned int hflip:1;
+ unsigned int vflip:1;
+ u32 flags;
+ u32 state;
+ struct fimc_dev *fimc_dev;
+ struct v4l2_m2m_ctx *m2m_ctx;
+ struct v4l2_fh fh;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl *ctrl_rotate;
+ struct v4l2_ctrl *ctrl_hflip;
+ struct v4l2_ctrl *ctrl_vflip;
+ struct v4l2_ctrl *ctrl_alpha;
+ bool ctrls_rdy;
+};
+
+#define fh_to_ctx(__fh) container_of(__fh, struct fimc_ctx, fh)
+
+static inline void set_frame_bounds(struct fimc_frame *f, u32 width, u32 height)
+{
+ f->o_width = width;
+ f->o_height = height;
+ f->f_width = width;
+ f->f_height = height;
+}
+
+static inline void set_frame_crop(struct fimc_frame *f,
+ u32 left, u32 top, u32 width, u32 height)
+{
+ f->offs_h = left;
+ f->offs_v = top;
+ f->width = width;
+ f->height = height;
+}
+
+static inline u32 fimc_get_format_depth(struct fimc_fmt *ff)
+{
+ u32 i, depth = 0;
+
+ if (ff != NULL)
+ for (i = 0; i < ff->colplanes; i++)
+ depth += ff->depth[i];
+ return depth;
+}
+
+static inline bool fimc_capture_active(struct fimc_dev *fimc)
+{
+ unsigned long flags;
+ bool ret;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ ret = !!(fimc->state & (1 << ST_CAPT_RUN) ||
+ fimc->state & (1 << ST_CAPT_PEND));
+ spin_unlock_irqrestore(&fimc->slock, flags);
+ return ret;
+}
+
+static inline void fimc_ctx_state_lock_set(u32 state, struct fimc_ctx *ctx)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctx->slock, flags);
+ ctx->state |= state;
+ spin_unlock_irqrestore(&ctx->slock, flags);
+}
+
+static inline bool fimc_ctx_state_is_set(u32 mask, struct fimc_ctx *ctx)
+{
+ unsigned long flags;
+ bool ret;
+
+ spin_lock_irqsave(&ctx->slock, flags);
+ ret = (ctx->state & mask) == mask;
+ spin_unlock_irqrestore(&ctx->slock, flags);
+ return ret;
+}
+
+static inline int tiled_fmt(struct fimc_fmt *fmt)
+{
+ return fmt->fourcc == V4L2_PIX_FMT_NV12MT;
+}
+
+/* Return the alpha component bit mask */
+static inline int fimc_get_alpha_mask(struct fimc_fmt *fmt)
+{
+ switch (fmt->color) {
+ case S5P_FIMC_RGB444: return 0x0f;
+ case S5P_FIMC_RGB555: return 0x01;
+ case S5P_FIMC_RGB888: return 0xff;
+ default: return 0;
+ };
+}
+
+static inline void fimc_hw_clear_irq(struct fimc_dev *dev)
+{
+ u32 cfg = readl(dev->regs + S5P_CIGCTRL);
+ cfg |= S5P_CIGCTRL_IRQ_CLR;
+ writel(cfg, dev->regs + S5P_CIGCTRL);
+}
+
+static inline void fimc_hw_enable_scaler(struct fimc_dev *dev, bool on)
+{
+ u32 cfg = readl(dev->regs + S5P_CISCCTRL);
+ if (on)
+ cfg |= S5P_CISCCTRL_SCALERSTART;
+ else
+ cfg &= ~S5P_CISCCTRL_SCALERSTART;
+ writel(cfg, dev->regs + S5P_CISCCTRL);
+}
+
+static inline void fimc_hw_activate_input_dma(struct fimc_dev *dev, bool on)
+{
+ u32 cfg = readl(dev->regs + S5P_MSCTRL);
+ if (on)
+ cfg |= S5P_MSCTRL_ENVID;
+ else
+ cfg &= ~S5P_MSCTRL_ENVID;
+ writel(cfg, dev->regs + S5P_MSCTRL);
+}
+
+static inline void fimc_hw_dis_capture(struct fimc_dev *dev)
+{
+ u32 cfg = readl(dev->regs + S5P_CIIMGCPT);
+ cfg &= ~(S5P_CIIMGCPT_IMGCPTEN | S5P_CIIMGCPT_IMGCPTEN_SC);
+ writel(cfg, dev->regs + S5P_CIIMGCPT);
+}
+
+/**
+ * fimc_hw_set_dma_seq - configure output DMA buffer sequence
+ * @mask: each bit corresponds to one of 32 output buffer registers set
+ * 1 to include buffer in the sequence, 0 to disable
+ *
+ * This function mask output DMA ring buffers, i.e. it allows to configure
+ * which of the output buffer address registers will be used by the DMA
+ * engine.
+ */
+static inline void fimc_hw_set_dma_seq(struct fimc_dev *dev, u32 mask)
+{
+ writel(mask, dev->regs + S5P_CIFCNTSEQ);
+}
+
+static inline struct fimc_frame *ctx_get_frame(struct fimc_ctx *ctx,
+ enum v4l2_buf_type type)
+{
+ struct fimc_frame *frame;
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE == type) {
+ if (fimc_ctx_state_is_set(FIMC_CTX_M2M, ctx))
+ frame = &ctx->s_frame;
+ else
+ return ERR_PTR(-EINVAL);
+ } else if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == type) {
+ frame = &ctx->d_frame;
+ } else {
+ v4l2_err(ctx->fimc_dev->v4l2_dev,
+ "Wrong buffer/video queue type (%d)\n", type);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return frame;
+}
+
+/* Return an index to the buffer actually being written. */
+static inline u32 fimc_hw_get_frame_index(struct fimc_dev *dev)
+{
+ u32 reg;
+
+ if (dev->variant->has_cistatus2) {
+ reg = readl(dev->regs + S5P_CISTATUS2) & 0x3F;
+ return reg > 0 ? --reg : reg;
+ } else {
+ reg = readl(dev->regs + S5P_CISTATUS);
+ return (reg & S5P_CISTATUS_FRAMECNT_MASK) >>
+ S5P_CISTATUS_FRAMECNT_SHIFT;
+ }
+}
+
+/* -----------------------------------------------------*/
+/* fimc-reg.c */
+void fimc_hw_reset(struct fimc_dev *fimc);
+void fimc_hw_set_rotation(struct fimc_ctx *ctx);
+void fimc_hw_set_target_format(struct fimc_ctx *ctx);
+void fimc_hw_set_out_dma(struct fimc_ctx *ctx);
+void fimc_hw_en_lastirq(struct fimc_dev *fimc, int enable);
+void fimc_hw_en_irq(struct fimc_dev *fimc, int enable);
+void fimc_hw_set_prescaler(struct fimc_ctx *ctx);
+void fimc_hw_set_mainscaler(struct fimc_ctx *ctx);
+void fimc_hw_en_capture(struct fimc_ctx *ctx);
+void fimc_hw_set_effect(struct fimc_ctx *ctx, bool active);
+void fimc_hw_set_rgb_alpha(struct fimc_ctx *ctx);
+void fimc_hw_set_in_dma(struct fimc_ctx *ctx);
+void fimc_hw_set_input_path(struct fimc_ctx *ctx);
+void fimc_hw_set_output_path(struct fimc_ctx *ctx);
+void fimc_hw_set_input_addr(struct fimc_dev *fimc, struct fimc_addr *paddr);
+void fimc_hw_set_output_addr(struct fimc_dev *fimc, struct fimc_addr *paddr,
+ int index);
+int fimc_hw_set_camera_source(struct fimc_dev *fimc,
+ struct s5p_fimc_isp_info *cam);
+int fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f);
+int fimc_hw_set_camera_polarity(struct fimc_dev *fimc,
+ struct s5p_fimc_isp_info *cam);
+int fimc_hw_set_camera_type(struct fimc_dev *fimc,
+ struct s5p_fimc_isp_info *cam);
+
+/* -----------------------------------------------------*/
+/* fimc-core.c */
+int fimc_vidioc_enum_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f);
+int fimc_ctrls_create(struct fimc_ctx *ctx);
+void fimc_ctrls_delete(struct fimc_ctx *ctx);
+void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active);
+void fimc_alpha_ctrl_update(struct fimc_ctx *ctx);
+int fimc_fill_format(struct fimc_frame *frame, struct v4l2_format *f);
+void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height,
+ struct v4l2_pix_format_mplane *pix);
+struct fimc_fmt *fimc_find_format(const u32 *pixelformat, const u32 *mbus_code,
+ unsigned int mask, int index);
+
+int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh,
+ int dw, int dh, int rotation);
+int fimc_set_scaler_info(struct fimc_ctx *ctx);
+int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags);
+int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb,
+ struct fimc_frame *frame, struct fimc_addr *paddr);
+void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f);
+void fimc_set_yuv_order(struct fimc_ctx *ctx);
+void fimc_fill_frame(struct fimc_frame *frame, struct v4l2_format *f);
+void fimc_capture_irq_handler(struct fimc_dev *fimc, bool done);
+
+int fimc_register_m2m_device(struct fimc_dev *fimc,
+ struct v4l2_device *v4l2_dev);
+void fimc_unregister_m2m_device(struct fimc_dev *fimc);
+int fimc_register_driver(void);
+void fimc_unregister_driver(void);
+
+/* -----------------------------------------------------*/
+/* fimc-capture.c */
+int fimc_register_capture_device(struct fimc_dev *fimc,
+ struct v4l2_device *v4l2_dev);
+void fimc_unregister_capture_device(struct fimc_dev *fimc);
+int fimc_capture_ctrls_create(struct fimc_dev *fimc);
+int fimc_vid_cap_buf_queue(struct fimc_dev *fimc,
+ struct fimc_vid_buffer *fimc_vb);
+void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification,
+ void *arg);
+int fimc_capture_suspend(struct fimc_dev *fimc);
+int fimc_capture_resume(struct fimc_dev *fimc);
+int fimc_capture_config_update(struct fimc_ctx *ctx);
+
+/* Locking: the caller holds fimc->slock */
+static inline void fimc_activate_capture(struct fimc_ctx *ctx)
+{
+ fimc_hw_enable_scaler(ctx->fimc_dev, ctx->scaler.enabled);
+ fimc_hw_en_capture(ctx);
+}
+
+static inline void fimc_deactivate_capture(struct fimc_dev *fimc)
+{
+ fimc_hw_en_lastirq(fimc, true);
+ fimc_hw_dis_capture(fimc);
+ fimc_hw_enable_scaler(fimc, false);
+ fimc_hw_en_lastirq(fimc, false);
+}
+
+/*
+ * Buffer list manipulation functions. Must be called with fimc.slock held.
+ */
+
+/**
+ * fimc_active_queue_add - add buffer to the capture active buffers queue
+ * @buf: buffer to add to the active buffers list
+ */
+static inline void fimc_active_queue_add(struct fimc_vid_cap *vid_cap,
+ struct fimc_vid_buffer *buf)
+{
+ list_add_tail(&buf->list, &vid_cap->active_buf_q);
+ vid_cap->active_buf_cnt++;
+}
+
+/**
+ * fimc_active_queue_pop - pop buffer from the capture active buffers queue
+ *
+ * The caller must assure the active_buf_q list is not empty.
+ */
+static inline struct fimc_vid_buffer *fimc_active_queue_pop(
+ struct fimc_vid_cap *vid_cap)
+{
+ struct fimc_vid_buffer *buf;
+ buf = list_entry(vid_cap->active_buf_q.next,
+ struct fimc_vid_buffer, list);
+ list_del(&buf->list);
+ vid_cap->active_buf_cnt--;
+ return buf;
+}
+
+/**
+ * fimc_pending_queue_add - add buffer to the capture pending buffers queue
+ * @buf: buffer to add to the pending buffers list
+ */
+static inline void fimc_pending_queue_add(struct fimc_vid_cap *vid_cap,
+ struct fimc_vid_buffer *buf)
+{
+ list_add_tail(&buf->list, &vid_cap->pending_buf_q);
+}
+
+/**
+ * fimc_pending_queue_pop - pop buffer from the capture pending buffers queue
+ *
+ * The caller must assure the pending_buf_q list is not empty.
+ */
+static inline struct fimc_vid_buffer *fimc_pending_queue_pop(
+ struct fimc_vid_cap *vid_cap)
+{
+ struct fimc_vid_buffer *buf;
+ buf = list_entry(vid_cap->pending_buf_q.next,
+ struct fimc_vid_buffer, list);
+ list_del(&buf->list);
+ return buf;
+}
+
+#endif /* FIMC_CORE_H_ */
diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.c b/drivers/media/video/s5p-fimc/fimc-mdevice.c
new file mode 100644
index 00000000..62ed37e4
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/fimc-mdevice.c
@@ -0,0 +1,862 @@
+/*
+ * S5P/EXYNOS4 SoC series camera host interface media device driver
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Contact: Sylwester Nawrocki, <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation, either version 2 of the License,
+ * or (at your option) any later version.
+ */
+
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <media/v4l2-ctrls.h>
+#include <media/media-device.h>
+
+#include "fimc-core.h"
+#include "fimc-mdevice.h"
+#include "mipi-csis.h"
+
+static int __fimc_md_set_camclk(struct fimc_md *fmd,
+ struct fimc_sensor_info *s_info,
+ bool on);
+/**
+ * fimc_pipeline_prepare - update pipeline information with subdevice pointers
+ * @fimc: fimc device terminating the pipeline
+ *
+ * Caller holds the graph mutex.
+ */
+void fimc_pipeline_prepare(struct fimc_dev *fimc, struct media_entity *me)
+{
+ struct media_entity_graph graph;
+ struct v4l2_subdev *sd;
+
+ media_entity_graph_walk_start(&graph, me);
+
+ while ((me = media_entity_graph_walk_next(&graph))) {
+ if (media_entity_type(me) != MEDIA_ENT_T_V4L2_SUBDEV)
+ continue;
+ sd = media_entity_to_v4l2_subdev(me);
+
+ if (sd->grp_id == SENSOR_GROUP_ID)
+ fimc->pipeline.sensor = sd;
+ else if (sd->grp_id == CSIS_GROUP_ID)
+ fimc->pipeline.csis = sd;
+ }
+}
+
+/**
+ * __subdev_set_power - change power state of a single subdev
+ * @sd: subdevice to change power state for
+ * @on: 1 to enable power or 0 to disable
+ *
+ * Return result of s_power subdev operation or -ENXIO if sd argument
+ * is NULL. Return 0 if the subdevice does not implement s_power.
+ */
+static int __subdev_set_power(struct v4l2_subdev *sd, int on)
+{
+ int *use_count;
+ int ret;
+
+ if (sd == NULL)
+ return -ENXIO;
+
+ use_count = &sd->entity.use_count;
+ if (on && (*use_count)++ > 0)
+ return 0;
+ else if (!on && (*use_count == 0 || --(*use_count) > 0))
+ return 0;
+ ret = v4l2_subdev_call(sd, core, s_power, on);
+
+ return ret != -ENOIOCTLCMD ? ret : 0;
+}
+
+/**
+ * fimc_pipeline_s_power - change power state of all pipeline subdevs
+ * @fimc: fimc device terminating the pipeline
+ * @state: 1 to enable power or 0 for power down
+ *
+ * Need to be called with the graph mutex held.
+ */
+int fimc_pipeline_s_power(struct fimc_dev *fimc, int state)
+{
+ int ret = 0;
+
+ if (fimc->pipeline.sensor == NULL)
+ return -ENXIO;
+
+ if (state) {
+ ret = __subdev_set_power(fimc->pipeline.csis, 1);
+ if (ret && ret != -ENXIO)
+ return ret;
+ return __subdev_set_power(fimc->pipeline.sensor, 1);
+ }
+
+ ret = __subdev_set_power(fimc->pipeline.sensor, 0);
+ if (ret)
+ return ret;
+ ret = __subdev_set_power(fimc->pipeline.csis, 0);
+
+ return ret == -ENXIO ? 0 : ret;
+}
+
+/**
+ * __fimc_pipeline_initialize - update the pipeline information, enable power
+ * of all pipeline subdevs and the sensor clock
+ * @me: media entity to start graph walk with
+ * @prep: true to acquire sensor (and csis) subdevs
+ *
+ * This function must be called with the graph mutex held.
+ */
+static int __fimc_pipeline_initialize(struct fimc_dev *fimc,
+ struct media_entity *me, bool prep)
+{
+ int ret;
+
+ if (prep)
+ fimc_pipeline_prepare(fimc, me);
+ if (fimc->pipeline.sensor == NULL)
+ return -EINVAL;
+ ret = fimc_md_set_camclk(fimc->pipeline.sensor, true);
+ if (ret)
+ return ret;
+ return fimc_pipeline_s_power(fimc, 1);
+}
+
+int fimc_pipeline_initialize(struct fimc_dev *fimc, struct media_entity *me,
+ bool prep)
+{
+ int ret;
+
+ mutex_lock(&me->parent->graph_mutex);
+ ret = __fimc_pipeline_initialize(fimc, me, prep);
+ mutex_unlock(&me->parent->graph_mutex);
+
+ return ret;
+}
+
+/**
+ * __fimc_pipeline_shutdown - disable the sensor clock and pipeline power
+ * @fimc: fimc device terminating the pipeline
+ *
+ * Disable power of all subdevs in the pipeline and turn off the external
+ * sensor clock.
+ * Called with the graph mutex held.
+ */
+int __fimc_pipeline_shutdown(struct fimc_dev *fimc)
+{
+ int ret = 0;
+
+ if (fimc->pipeline.sensor) {
+ ret = fimc_pipeline_s_power(fimc, 0);
+ fimc_md_set_camclk(fimc->pipeline.sensor, false);
+ }
+ return ret == -ENXIO ? 0 : ret;
+}
+
+int fimc_pipeline_shutdown(struct fimc_dev *fimc)
+{
+ struct media_entity *me = &fimc->vid_cap.vfd->entity;
+ int ret;
+
+ mutex_lock(&me->parent->graph_mutex);
+ ret = __fimc_pipeline_shutdown(fimc);
+ mutex_unlock(&me->parent->graph_mutex);
+
+ return ret;
+}
+
+/**
+ * fimc_pipeline_s_stream - invoke s_stream on pipeline subdevs
+ * @fimc: fimc device terminating the pipeline
+ * @on: passed as the s_stream call argument
+ */
+int fimc_pipeline_s_stream(struct fimc_dev *fimc, int on)
+{
+ struct fimc_pipeline *p = &fimc->pipeline;
+ int ret = 0;
+
+ if (p->sensor == NULL)
+ return -ENODEV;
+
+ if ((on && p->csis) || !on)
+ ret = v4l2_subdev_call(on ? p->csis : p->sensor,
+ video, s_stream, on);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+ if ((!on && p->csis) || on)
+ ret = v4l2_subdev_call(on ? p->sensor : p->csis,
+ video, s_stream, on);
+ return ret == -ENOIOCTLCMD ? 0 : ret;
+}
+
+/*
+ * Sensor subdevice helper functions
+ */
+static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd,
+ struct fimc_sensor_info *s_info)
+{
+ struct i2c_adapter *adapter;
+ struct v4l2_subdev *sd = NULL;
+
+ if (!s_info || !fmd)
+ return NULL;
+
+ adapter = i2c_get_adapter(s_info->pdata->i2c_bus_num);
+ if (!adapter)
+ return NULL;
+ sd = v4l2_i2c_new_subdev_board(&fmd->v4l2_dev, adapter,
+ s_info->pdata->board_info, NULL);
+ if (IS_ERR_OR_NULL(sd)) {
+ i2c_put_adapter(adapter);
+ v4l2_err(&fmd->v4l2_dev, "Failed to acquire subdev\n");
+ return NULL;
+ }
+ v4l2_set_subdev_hostdata(sd, s_info);
+ sd->grp_id = SENSOR_GROUP_ID;
+
+ v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice %s\n",
+ s_info->pdata->board_info->type);
+ return sd;
+}
+
+static void fimc_md_unregister_sensor(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct i2c_adapter *adapter;
+
+ if (!client)
+ return;
+ v4l2_device_unregister_subdev(sd);
+ adapter = client->adapter;
+ i2c_unregister_device(client);
+ if (adapter)
+ i2c_put_adapter(adapter);
+}
+
+static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
+{
+ struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data;
+ struct fimc_dev *fd = NULL;
+ int num_clients, ret, i;
+
+ /*
+ * Runtime resume one of the FIMC entities to make sure
+ * the sclk_cam clocks are not globally disabled.
+ */
+ for (i = 0; !fd && i < ARRAY_SIZE(fmd->fimc); i++)
+ if (fmd->fimc[i])
+ fd = fmd->fimc[i];
+ if (!fd)
+ return -ENXIO;
+ ret = pm_runtime_get_sync(&fd->pdev->dev);
+ if (ret < 0)
+ return ret;
+
+ WARN_ON(pdata->num_clients > ARRAY_SIZE(fmd->sensor));
+ num_clients = min_t(u32, pdata->num_clients, ARRAY_SIZE(fmd->sensor));
+
+ fmd->num_sensors = num_clients;
+ for (i = 0; i < num_clients; i++) {
+ fmd->sensor[i].pdata = &pdata->isp_info[i];
+ ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true);
+ if (ret)
+ break;
+ fmd->sensor[i].subdev =
+ fimc_md_register_sensor(fmd, &fmd->sensor[i]);
+ ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], false);
+ if (ret)
+ break;
+ }
+ pm_runtime_put(&fd->pdev->dev);
+ return ret;
+}
+
+/*
+ * MIPI CSIS and FIMC platform devices registration.
+ */
+static int fimc_register_callback(struct device *dev, void *p)
+{
+ struct fimc_dev *fimc = dev_get_drvdata(dev);
+ struct fimc_md *fmd = p;
+ int ret;
+
+ if (!fimc || !fimc->pdev)
+ return 0;
+ if (fimc->pdev->id < 0 || fimc->pdev->id >= FIMC_MAX_DEVS)
+ return 0;
+
+ fmd->fimc[fimc->pdev->id] = fimc;
+ ret = fimc_register_m2m_device(fimc, &fmd->v4l2_dev);
+ if (ret)
+ return ret;
+ ret = fimc_register_capture_device(fimc, &fmd->v4l2_dev);
+ if (!ret)
+ fimc->vid_cap.user_subdev_api = fmd->user_subdev_api;
+ return ret;
+}
+
+static int csis_register_callback(struct device *dev, void *p)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct platform_device *pdev;
+ struct fimc_md *fmd = p;
+ int id, ret;
+
+ if (!sd)
+ return 0;
+ pdev = v4l2_get_subdevdata(sd);
+ if (!pdev || pdev->id < 0 || pdev->id >= CSIS_MAX_ENTITIES)
+ return 0;
+ v4l2_info(sd, "csis%d sd: %s\n", pdev->id, sd->name);
+
+ id = pdev->id < 0 ? 0 : pdev->id;
+ fmd->csis[id].sd = sd;
+ sd->grp_id = CSIS_GROUP_ID;
+ ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
+ if (ret)
+ v4l2_err(&fmd->v4l2_dev,
+ "Failed to register CSIS subdevice: %d\n", ret);
+ return ret;
+}
+
+/**
+ * fimc_md_register_platform_entities - register FIMC and CSIS media entities
+ */
+static int fimc_md_register_platform_entities(struct fimc_md *fmd)
+{
+ struct device_driver *driver;
+ int ret;
+
+ driver = driver_find(FIMC_MODULE_NAME, &platform_bus_type);
+ if (!driver)
+ return -ENODEV;
+ ret = driver_for_each_device(driver, NULL, fmd,
+ fimc_register_callback);
+ if (ret)
+ return ret;
+
+ driver = driver_find(CSIS_DRIVER_NAME, &platform_bus_type);
+ if (driver)
+ ret = driver_for_each_device(driver, NULL, fmd,
+ csis_register_callback);
+ return ret;
+}
+
+static void fimc_md_unregister_entities(struct fimc_md *fmd)
+{
+ int i;
+
+ for (i = 0; i < FIMC_MAX_DEVS; i++) {
+ if (fmd->fimc[i] == NULL)
+ continue;
+ fimc_unregister_m2m_device(fmd->fimc[i]);
+ fimc_unregister_capture_device(fmd->fimc[i]);
+ fmd->fimc[i] = NULL;
+ }
+ for (i = 0; i < CSIS_MAX_ENTITIES; i++) {
+ if (fmd->csis[i].sd == NULL)
+ continue;
+ v4l2_device_unregister_subdev(fmd->csis[i].sd);
+ fmd->csis[i].sd = NULL;
+ }
+ for (i = 0; i < fmd->num_sensors; i++) {
+ if (fmd->sensor[i].subdev == NULL)
+ continue;
+ fimc_md_unregister_sensor(fmd->sensor[i].subdev);
+ fmd->sensor[i].subdev = NULL;
+ }
+}
+
+static int fimc_md_register_video_nodes(struct fimc_md *fmd)
+{
+ struct video_device *vdev;
+ int i, ret = 0;
+
+ for (i = 0; i < FIMC_MAX_DEVS && !ret; i++) {
+ if (!fmd->fimc[i])
+ continue;
+
+ vdev = fmd->fimc[i]->m2m.vfd;
+ if (vdev) {
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ if (ret)
+ break;
+ v4l2_info(&fmd->v4l2_dev, "Registered %s as /dev/%s\n",
+ vdev->name, video_device_node_name(vdev));
+ }
+
+ vdev = fmd->fimc[i]->vid_cap.vfd;
+ if (vdev == NULL)
+ continue;
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ v4l2_info(&fmd->v4l2_dev, "Registered %s as /dev/%s\n",
+ vdev->name, video_device_node_name(vdev));
+ }
+
+ return ret;
+}
+
+/**
+ * __fimc_md_create_fimc_links - create links to all FIMC entities
+ * @fmd: fimc media device
+ * @source: the source entity to create links to all fimc entities from
+ * @sensor: sensor subdev linked to FIMC[fimc_id] entity, may be null
+ * @pad: the source entity pad index
+ * @fimc_id: index of the fimc device for which link should be enabled
+ */
+static int __fimc_md_create_fimc_links(struct fimc_md *fmd,
+ struct media_entity *source,
+ struct v4l2_subdev *sensor,
+ int pad, int fimc_id)
+{
+ struct fimc_sensor_info *s_info;
+ struct media_entity *sink;
+ unsigned int flags;
+ int ret, i;
+
+ for (i = 0; i < FIMC_MAX_DEVS; i++) {
+ if (!fmd->fimc[i])
+ break;
+ /*
+ * Some FIMC variants are not fitted with camera capture
+ * interface. Skip creating a link from sensor for those.
+ */
+ if (sensor->grp_id == SENSOR_GROUP_ID &&
+ !fmd->fimc[i]->variant->has_cam_if)
+ continue;
+
+ flags = (i == fimc_id) ? MEDIA_LNK_FL_ENABLED : 0;
+ sink = &fmd->fimc[i]->vid_cap.subdev->entity;
+ ret = media_entity_create_link(source, pad, sink,
+ FIMC_SD_PAD_SINK, flags);
+ if (ret)
+ return ret;
+
+ /* Notify FIMC capture subdev entity */
+ ret = media_entity_call(sink, link_setup, &sink->pads[0],
+ &source->pads[pad], flags);
+ if (ret)
+ break;
+
+ v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]",
+ source->name, flags ? '=' : '-', sink->name);
+
+ if (flags == 0)
+ continue;
+ s_info = v4l2_get_subdev_hostdata(sensor);
+ if (!WARN_ON(s_info == NULL)) {
+ unsigned long irq_flags;
+ spin_lock_irqsave(&fmd->slock, irq_flags);
+ s_info->host = fmd->fimc[i];
+ spin_unlock_irqrestore(&fmd->slock, irq_flags);
+ }
+ }
+ return 0;
+}
+
+/**
+ * fimc_md_create_links - create default links between registered entities
+ *
+ * Parallel interface sensor entities are connected directly to FIMC capture
+ * entities. The sensors using MIPI CSIS bus are connected through immutable
+ * link with CSI receiver entity specified by mux_id. Any registered CSIS
+ * entity has a link to each registered FIMC capture entity. Enabled links
+ * are created by default between each subsequent registered sensor and
+ * subsequent FIMC capture entity. The number of default active links is
+ * determined by the number of available sensors or FIMC entities,
+ * whichever is less.
+ */
+static int fimc_md_create_links(struct fimc_md *fmd)
+{
+ struct v4l2_subdev *sensor, *csis;
+ struct s5p_fimc_isp_info *pdata;
+ struct fimc_sensor_info *s_info;
+ struct media_entity *source, *sink;
+ int i, pad, fimc_id = 0;
+ int ret = 0;
+ u32 flags;
+
+ for (i = 0; i < fmd->num_sensors; i++) {
+ if (fmd->sensor[i].subdev == NULL)
+ continue;
+
+ sensor = fmd->sensor[i].subdev;
+ s_info = v4l2_get_subdev_hostdata(sensor);
+ if (!s_info || !s_info->pdata)
+ continue;
+
+ source = NULL;
+ pdata = s_info->pdata;
+
+ switch (pdata->bus_type) {
+ case FIMC_MIPI_CSI2:
+ if (WARN(pdata->mux_id >= CSIS_MAX_ENTITIES,
+ "Wrong CSI channel id: %d\n", pdata->mux_id))
+ return -EINVAL;
+
+ csis = fmd->csis[pdata->mux_id].sd;
+ if (WARN(csis == NULL,
+ "MIPI-CSI interface specified "
+ "but s5p-csis module is not loaded!\n"))
+ return -EINVAL;
+
+ ret = media_entity_create_link(&sensor->entity, 0,
+ &csis->entity, CSIS_PAD_SINK,
+ MEDIA_LNK_FL_IMMUTABLE |
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ return ret;
+
+ v4l2_info(&fmd->v4l2_dev, "created link [%s] => [%s]",
+ sensor->entity.name, csis->entity.name);
+
+ source = &csis->entity;
+ pad = CSIS_PAD_SOURCE;
+ break;
+
+ case FIMC_ITU_601...FIMC_ITU_656:
+ source = &sensor->entity;
+ pad = 0;
+ break;
+
+ default:
+ v4l2_err(&fmd->v4l2_dev, "Wrong bus_type: %x\n",
+ pdata->bus_type);
+ return -EINVAL;
+ }
+ if (source == NULL)
+ continue;
+
+ ret = __fimc_md_create_fimc_links(fmd, source, sensor, pad,
+ fimc_id++);
+ }
+ /* Create immutable links between each FIMC's subdev and video node */
+ flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
+ for (i = 0; i < FIMC_MAX_DEVS; i++) {
+ if (!fmd->fimc[i])
+ continue;
+ source = &fmd->fimc[i]->vid_cap.subdev->entity;
+ sink = &fmd->fimc[i]->vid_cap.vfd->entity;
+ ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE,
+ sink, 0, flags);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * The peripheral sensor clock management.
+ */
+static int fimc_md_get_clocks(struct fimc_md *fmd)
+{
+ char clk_name[32];
+ struct clk *clock;
+ int i;
+
+ for (i = 0; i < FIMC_MAX_CAMCLKS; i++) {
+ snprintf(clk_name, sizeof(clk_name), "sclk_cam%u", i);
+ clock = clk_get(NULL, clk_name);
+ if (IS_ERR_OR_NULL(clock)) {
+ v4l2_err(&fmd->v4l2_dev, "Failed to get clock: %s",
+ clk_name);
+ return -ENXIO;
+ }
+ fmd->camclk[i].clock = clock;
+ }
+ return 0;
+}
+
+static void fimc_md_put_clocks(struct fimc_md *fmd)
+{
+ int i = FIMC_MAX_CAMCLKS;
+
+ while (--i >= 0) {
+ if (IS_ERR_OR_NULL(fmd->camclk[i].clock))
+ continue;
+ clk_put(fmd->camclk[i].clock);
+ fmd->camclk[i].clock = NULL;
+ }
+}
+
+static int __fimc_md_set_camclk(struct fimc_md *fmd,
+ struct fimc_sensor_info *s_info,
+ bool on)
+{
+ struct s5p_fimc_isp_info *pdata = s_info->pdata;
+ struct fimc_camclk_info *camclk;
+ int ret = 0;
+
+ if (WARN_ON(pdata->clk_id >= FIMC_MAX_CAMCLKS) || fmd == NULL)
+ return -EINVAL;
+
+ if (s_info->clk_on == on)
+ return 0;
+ camclk = &fmd->camclk[pdata->clk_id];
+
+ dbg("camclk %d, f: %lu, clk: %p, on: %d",
+ pdata->clk_id, pdata->clk_frequency, camclk, on);
+
+ if (on) {
+ if (camclk->use_count > 0 &&
+ camclk->frequency != pdata->clk_frequency)
+ return -EINVAL;
+
+ if (camclk->use_count++ == 0) {
+ clk_set_rate(camclk->clock, pdata->clk_frequency);
+ camclk->frequency = pdata->clk_frequency;
+ ret = clk_enable(camclk->clock);
+ }
+ s_info->clk_on = 1;
+ dbg("Enabled camclk %d: f: %lu", pdata->clk_id,
+ clk_get_rate(camclk->clock));
+
+ return ret;
+ }
+
+ if (WARN_ON(camclk->use_count == 0))
+ return 0;
+
+ if (--camclk->use_count == 0) {
+ clk_disable(camclk->clock);
+ s_info->clk_on = 0;
+ dbg("Disabled camclk %d", pdata->clk_id);
+ }
+ return ret;
+}
+
+/**
+ * fimc_md_set_camclk - peripheral sensor clock setup
+ * @sd: sensor subdev to configure sclk_cam clock for
+ * @on: 1 to enable or 0 to disable the clock
+ *
+ * There are 2 separate clock outputs available in the SoC for external
+ * image processors. These clocks are shared between all registered FIMC
+ * devices to which sensors can be attached, either directly or through
+ * the MIPI CSI receiver. The clock is allowed here to be used by
+ * multiple sensors concurrently if they use same frequency.
+ * The per sensor subdev clk_on attribute helps to synchronize accesses
+ * to the sclk_cam clocks from the video and media device nodes.
+ * This function should only be called when the graph mutex is held.
+ */
+int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on)
+{
+ struct fimc_sensor_info *s_info = v4l2_get_subdev_hostdata(sd);
+ struct fimc_md *fmd = entity_to_fimc_mdev(&sd->entity);
+
+ return __fimc_md_set_camclk(fmd, s_info, on);
+}
+
+static int fimc_md_link_notify(struct media_pad *source,
+ struct media_pad *sink, u32 flags)
+{
+ struct v4l2_subdev *sd;
+ struct fimc_dev *fimc;
+ int ret = 0;
+
+ if (media_entity_type(sink->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ return 0;
+
+ sd = media_entity_to_v4l2_subdev(sink->entity);
+ fimc = v4l2_get_subdevdata(sd);
+
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ ret = __fimc_pipeline_shutdown(fimc);
+ fimc->pipeline.sensor = NULL;
+ fimc->pipeline.csis = NULL;
+
+ mutex_lock(&fimc->lock);
+ fimc_ctrls_delete(fimc->vid_cap.ctx);
+ mutex_unlock(&fimc->lock);
+ return ret;
+ }
+ /*
+ * Link activation. Enable power of pipeline elements only if the
+ * pipeline is already in use, i.e. its video node is opened.
+ * Recreate the controls destroyed during the link deactivation.
+ */
+ mutex_lock(&fimc->lock);
+ if (fimc->vid_cap.refcnt > 0) {
+ ret = __fimc_pipeline_initialize(fimc, source->entity, true);
+ if (!ret)
+ ret = fimc_capture_ctrls_create(fimc);
+ }
+ mutex_unlock(&fimc->lock);
+
+ return ret ? -EPIPE : ret;
+}
+
+static ssize_t fimc_md_sysfs_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct fimc_md *fmd = platform_get_drvdata(pdev);
+
+ if (fmd->user_subdev_api)
+ return strlcpy(buf, "Sub-device API (sub-dev)\n", PAGE_SIZE);
+
+ return strlcpy(buf, "V4L2 video node only API (vid-dev)\n", PAGE_SIZE);
+}
+
+static ssize_t fimc_md_sysfs_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct fimc_md *fmd = platform_get_drvdata(pdev);
+ bool subdev_api;
+ int i;
+
+ if (!strcmp(buf, "vid-dev\n"))
+ subdev_api = false;
+ else if (!strcmp(buf, "sub-dev\n"))
+ subdev_api = true;
+ else
+ return count;
+
+ fmd->user_subdev_api = subdev_api;
+ for (i = 0; i < FIMC_MAX_DEVS; i++)
+ if (fmd->fimc[i])
+ fmd->fimc[i]->vid_cap.user_subdev_api = subdev_api;
+ return count;
+}
+/*
+ * This device attribute is to select video pipeline configuration method.
+ * There are following valid values:
+ * vid-dev - for V4L2 video node API only, subdevice will be configured
+ * by the host driver.
+ * sub-dev - for media controller API, subdevs must be configured in user
+ * space before starting streaming.
+ */
+static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO,
+ fimc_md_sysfs_show, fimc_md_sysfs_store);
+
+static int __devinit fimc_md_probe(struct platform_device *pdev)
+{
+ struct v4l2_device *v4l2_dev;
+ struct fimc_md *fmd;
+ int ret;
+
+ fmd = devm_kzalloc(&pdev->dev, sizeof(*fmd), GFP_KERNEL);
+ if (!fmd)
+ return -ENOMEM;
+
+ spin_lock_init(&fmd->slock);
+ fmd->pdev = pdev;
+
+ strlcpy(fmd->media_dev.model, "SAMSUNG S5P FIMC",
+ sizeof(fmd->media_dev.model));
+ fmd->media_dev.link_notify = fimc_md_link_notify;
+ fmd->media_dev.dev = &pdev->dev;
+
+ v4l2_dev = &fmd->v4l2_dev;
+ v4l2_dev->mdev = &fmd->media_dev;
+ v4l2_dev->notify = fimc_sensor_notify;
+ snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s",
+ dev_name(&pdev->dev));
+
+ ret = v4l2_device_register(&pdev->dev, &fmd->v4l2_dev);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret);
+ return ret;
+ }
+ ret = media_device_register(&fmd->media_dev);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret);
+ goto err2;
+ }
+ ret = fimc_md_get_clocks(fmd);
+ if (ret)
+ goto err3;
+
+ fmd->user_subdev_api = false;
+ ret = fimc_md_register_platform_entities(fmd);
+ if (ret)
+ goto err3;
+
+ if (pdev->dev.platform_data) {
+ ret = fimc_md_register_sensor_entities(fmd);
+ if (ret)
+ goto err3;
+ }
+ ret = fimc_md_create_links(fmd);
+ if (ret)
+ goto err3;
+ ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev);
+ if (ret)
+ goto err3;
+ ret = fimc_md_register_video_nodes(fmd);
+ if (ret)
+ goto err3;
+
+ ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode);
+ if (!ret) {
+ platform_set_drvdata(pdev, fmd);
+ return 0;
+ }
+err3:
+ media_device_unregister(&fmd->media_dev);
+ fimc_md_put_clocks(fmd);
+ fimc_md_unregister_entities(fmd);
+err2:
+ v4l2_device_unregister(&fmd->v4l2_dev);
+ return ret;
+}
+
+static int __devexit fimc_md_remove(struct platform_device *pdev)
+{
+ struct fimc_md *fmd = platform_get_drvdata(pdev);
+
+ if (!fmd)
+ return 0;
+ device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode);
+ fimc_md_unregister_entities(fmd);
+ media_device_unregister(&fmd->media_dev);
+ fimc_md_put_clocks(fmd);
+ return 0;
+}
+
+static struct platform_driver fimc_md_driver = {
+ .probe = fimc_md_probe,
+ .remove = __devexit_p(fimc_md_remove),
+ .driver = {
+ .name = "s5p-fimc-md",
+ .owner = THIS_MODULE,
+ }
+};
+
+int __init fimc_md_init(void)
+{
+ int ret;
+ request_module("s5p-csis");
+ ret = fimc_register_driver();
+ if (ret)
+ return ret;
+ return platform_driver_register(&fimc_md_driver);
+}
+void __exit fimc_md_exit(void)
+{
+ platform_driver_unregister(&fimc_md_driver);
+ fimc_unregister_driver();
+}
+
+module_init(fimc_md_init);
+module_exit(fimc_md_exit);
+
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_DESCRIPTION("S5P FIMC camera host interface/video postprocessor driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("2.0.1");
diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.h b/drivers/media/video/s5p-fimc/fimc-mdevice.h
new file mode 100644
index 00000000..da378082
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/fimc-mdevice.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef FIMC_MDEVICE_H_
+#define FIMC_MDEVICE_H_
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <media/media-device.h>
+#include <media/media-entity.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include "fimc-core.h"
+#include "mipi-csis.h"
+
+/* Group IDs of sensor, MIPI CSIS and the writeback subdevs. */
+#define SENSOR_GROUP_ID (1 << 8)
+#define CSIS_GROUP_ID (1 << 9)
+#define WRITEBACK_GROUP_ID (1 << 10)
+
+#define FIMC_MAX_SENSORS 8
+#define FIMC_MAX_CAMCLKS 2
+
+struct fimc_csis_info {
+ struct v4l2_subdev *sd;
+ int id;
+};
+
+struct fimc_camclk_info {
+ struct clk *clock;
+ int use_count;
+ unsigned long frequency;
+};
+
+/**
+ * struct fimc_sensor_info - image data source subdev information
+ * @pdata: sensor's atrributes passed as media device's platform data
+ * @subdev: image sensor v4l2 subdev
+ * @host: fimc device the sensor is currently linked to
+ * @clk_on: sclk_cam clock's state associated with this subdev
+ *
+ * This data structure applies to image sensor and the writeback subdevs.
+ */
+struct fimc_sensor_info {
+ struct s5p_fimc_isp_info *pdata;
+ struct v4l2_subdev *subdev;
+ struct fimc_dev *host;
+ bool clk_on;
+};
+
+/**
+ * struct fimc_md - fimc media device information
+ * @csis: MIPI CSIS subdevs data
+ * @sensor: array of registered sensor subdevs
+ * @num_sensors: actual number of registered sensors
+ * @camclk: external sensor clock information
+ * @fimc: array of registered fimc devices
+ * @media_dev: top level media device
+ * @v4l2_dev: top level v4l2_device holding up the subdevs
+ * @pdev: platform device this media device is hooked up into
+ * @user_subdev_api: true if subdevs are not configured by the host driver
+ * @slock: spinlock protecting @sensor array
+ */
+struct fimc_md {
+ struct fimc_csis_info csis[CSIS_MAX_ENTITIES];
+ struct fimc_sensor_info sensor[FIMC_MAX_SENSORS];
+ int num_sensors;
+ struct fimc_camclk_info camclk[FIMC_MAX_CAMCLKS];
+ struct fimc_dev *fimc[FIMC_MAX_DEVS];
+ struct media_device media_dev;
+ struct v4l2_device v4l2_dev;
+ struct platform_device *pdev;
+ bool user_subdev_api;
+ spinlock_t slock;
+};
+
+#define is_subdev_pad(pad) (pad == NULL || \
+ media_entity_type(pad->entity) == MEDIA_ENT_T_V4L2_SUBDEV)
+
+#define me_subtype(me) \
+ ((me->type) & (MEDIA_ENT_TYPE_MASK | MEDIA_ENT_SUBTYPE_MASK))
+
+#define subdev_has_devnode(__sd) (__sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE)
+
+static inline struct fimc_md *entity_to_fimc_mdev(struct media_entity *me)
+{
+ return me->parent == NULL ? NULL :
+ container_of(me->parent, struct fimc_md, media_dev);
+}
+
+static inline void fimc_md_graph_lock(struct fimc_dev *fimc)
+{
+ BUG_ON(fimc->vid_cap.vfd == NULL);
+ mutex_lock(&fimc->vid_cap.vfd->entity.parent->graph_mutex);
+}
+
+static inline void fimc_md_graph_unlock(struct fimc_dev *fimc)
+{
+ BUG_ON(fimc->vid_cap.vfd == NULL);
+ mutex_unlock(&fimc->vid_cap.vfd->entity.parent->graph_mutex);
+}
+
+int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on);
+void fimc_pipeline_prepare(struct fimc_dev *fimc, struct media_entity *me);
+int fimc_pipeline_initialize(struct fimc_dev *fimc, struct media_entity *me,
+ bool resume);
+int fimc_pipeline_shutdown(struct fimc_dev *fimc);
+int fimc_pipeline_s_power(struct fimc_dev *fimc, int state);
+int fimc_pipeline_s_stream(struct fimc_dev *fimc, int state);
+
+#endif
diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c
new file mode 100644
index 00000000..15466d05
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/fimc-reg.c
@@ -0,0 +1,723 @@
+/*
+ * Register interface file for Samsung Camera Interface (FIMC) driver
+ *
+ * Copyright (c) 2010 Samsung Electronics
+ *
+ * Sylwester Nawrocki, s.nawrocki@samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <mach/map.h>
+#include <media/s5p_fimc.h>
+
+#include "fimc-core.h"
+
+
+void fimc_hw_reset(struct fimc_dev *dev)
+{
+ u32 cfg;
+
+ cfg = readl(dev->regs + S5P_CISRCFMT);
+ cfg |= S5P_CISRCFMT_ITU601_8BIT;
+ writel(cfg, dev->regs + S5P_CISRCFMT);
+
+ /* Software reset. */
+ cfg = readl(dev->regs + S5P_CIGCTRL);
+ cfg |= (S5P_CIGCTRL_SWRST | S5P_CIGCTRL_IRQ_LEVEL);
+ writel(cfg, dev->regs + S5P_CIGCTRL);
+ udelay(10);
+
+ cfg = readl(dev->regs + S5P_CIGCTRL);
+ cfg &= ~S5P_CIGCTRL_SWRST;
+ writel(cfg, dev->regs + S5P_CIGCTRL);
+
+ if (dev->variant->out_buf_count > 4)
+ fimc_hw_set_dma_seq(dev, 0xF);
+}
+
+static u32 fimc_hw_get_in_flip(struct fimc_ctx *ctx)
+{
+ u32 flip = S5P_MSCTRL_FLIP_NORMAL;
+
+ if (ctx->hflip)
+ flip = S5P_MSCTRL_FLIP_X_MIRROR;
+ if (ctx->vflip)
+ flip = S5P_MSCTRL_FLIP_Y_MIRROR;
+
+ if (ctx->rotation <= 90)
+ return flip;
+
+ return (flip ^ S5P_MSCTRL_FLIP_180) & S5P_MSCTRL_FLIP_180;
+}
+
+static u32 fimc_hw_get_target_flip(struct fimc_ctx *ctx)
+{
+ u32 flip = S5P_CITRGFMT_FLIP_NORMAL;
+
+ if (ctx->hflip)
+ flip |= S5P_CITRGFMT_FLIP_X_MIRROR;
+ if (ctx->vflip)
+ flip |= S5P_CITRGFMT_FLIP_Y_MIRROR;
+
+ if (ctx->rotation <= 90)
+ return flip;
+
+ return (flip ^ S5P_CITRGFMT_FLIP_180) & S5P_CITRGFMT_FLIP_180;
+}
+
+void fimc_hw_set_rotation(struct fimc_ctx *ctx)
+{
+ u32 cfg, flip;
+ struct fimc_dev *dev = ctx->fimc_dev;
+
+ cfg = readl(dev->regs + S5P_CITRGFMT);
+ cfg &= ~(S5P_CITRGFMT_INROT90 | S5P_CITRGFMT_OUTROT90 |
+ S5P_CITRGFMT_FLIP_180);
+
+ /*
+ * The input and output rotator cannot work simultaneously.
+ * Use the output rotator in output DMA mode or the input rotator
+ * in direct fifo output mode.
+ */
+ if (ctx->rotation == 90 || ctx->rotation == 270) {
+ if (ctx->out_path == FIMC_LCDFIFO)
+ cfg |= S5P_CITRGFMT_INROT90;
+ else
+ cfg |= S5P_CITRGFMT_OUTROT90;
+ }
+
+ if (ctx->out_path == FIMC_DMA) {
+ cfg |= fimc_hw_get_target_flip(ctx);
+ writel(cfg, dev->regs + S5P_CITRGFMT);
+ } else {
+ /* LCD FIFO path */
+ flip = readl(dev->regs + S5P_MSCTRL);
+ flip &= ~S5P_MSCTRL_FLIP_MASK;
+ flip |= fimc_hw_get_in_flip(ctx);
+ writel(flip, dev->regs + S5P_MSCTRL);
+ }
+}
+
+void fimc_hw_set_target_format(struct fimc_ctx *ctx)
+{
+ u32 cfg;
+ struct fimc_dev *dev = ctx->fimc_dev;
+ struct fimc_frame *frame = &ctx->d_frame;
+
+ dbg("w= %d, h= %d color: %d", frame->width,
+ frame->height, frame->fmt->color);
+
+ cfg = readl(dev->regs + S5P_CITRGFMT);
+ cfg &= ~(S5P_CITRGFMT_FMT_MASK | S5P_CITRGFMT_HSIZE_MASK |
+ S5P_CITRGFMT_VSIZE_MASK);
+
+ switch (frame->fmt->color) {
+ case S5P_FIMC_RGB444...S5P_FIMC_RGB888:
+ cfg |= S5P_CITRGFMT_RGB;
+ break;
+ case S5P_FIMC_YCBCR420:
+ cfg |= S5P_CITRGFMT_YCBCR420;
+ break;
+ case S5P_FIMC_YCBYCR422...S5P_FIMC_CRYCBY422:
+ if (frame->fmt->colplanes == 1)
+ cfg |= S5P_CITRGFMT_YCBCR422_1P;
+ else
+ cfg |= S5P_CITRGFMT_YCBCR422;
+ break;
+ default:
+ break;
+ }
+
+ if (ctx->rotation == 90 || ctx->rotation == 270) {
+ cfg |= S5P_CITRGFMT_HSIZE(frame->height);
+ cfg |= S5P_CITRGFMT_VSIZE(frame->width);
+ } else {
+
+ cfg |= S5P_CITRGFMT_HSIZE(frame->width);
+ cfg |= S5P_CITRGFMT_VSIZE(frame->height);
+ }
+
+ writel(cfg, dev->regs + S5P_CITRGFMT);
+
+ cfg = readl(dev->regs + S5P_CITAREA) & ~S5P_CITAREA_MASK;
+ cfg |= (frame->width * frame->height);
+ writel(cfg, dev->regs + S5P_CITAREA);
+}
+
+static void fimc_hw_set_out_dma_size(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+ struct fimc_frame *frame = &ctx->d_frame;
+ u32 cfg;
+
+ cfg = S5P_ORIG_SIZE_HOR(frame->f_width);
+ cfg |= S5P_ORIG_SIZE_VER(frame->f_height);
+ writel(cfg, dev->regs + S5P_ORGOSIZE);
+
+ /* Select color space conversion equation (HD/SD size).*/
+ cfg = readl(dev->regs + S5P_CIGCTRL);
+ if (frame->f_width >= 1280) /* HD */
+ cfg |= S5P_CIGCTRL_CSC_ITU601_709;
+ else /* SD */
+ cfg &= ~S5P_CIGCTRL_CSC_ITU601_709;
+ writel(cfg, dev->regs + S5P_CIGCTRL);
+
+}
+
+void fimc_hw_set_out_dma(struct fimc_ctx *ctx)
+{
+ u32 cfg;
+ struct fimc_dev *dev = ctx->fimc_dev;
+ struct fimc_frame *frame = &ctx->d_frame;
+ struct fimc_dma_offset *offset = &frame->dma_offset;
+ struct fimc_fmt *fmt = frame->fmt;
+
+ /* Set the input dma offsets. */
+ cfg = 0;
+ cfg |= S5P_CIO_OFFS_HOR(offset->y_h);
+ cfg |= S5P_CIO_OFFS_VER(offset->y_v);
+ writel(cfg, dev->regs + S5P_CIOYOFF);
+
+ cfg = 0;
+ cfg |= S5P_CIO_OFFS_HOR(offset->cb_h);
+ cfg |= S5P_CIO_OFFS_VER(offset->cb_v);
+ writel(cfg, dev->regs + S5P_CIOCBOFF);
+
+ cfg = 0;
+ cfg |= S5P_CIO_OFFS_HOR(offset->cr_h);
+ cfg |= S5P_CIO_OFFS_VER(offset->cr_v);
+ writel(cfg, dev->regs + S5P_CIOCROFF);
+
+ fimc_hw_set_out_dma_size(ctx);
+
+ /* Configure chroma components order. */
+ cfg = readl(dev->regs + S5P_CIOCTRL);
+
+ cfg &= ~(S5P_CIOCTRL_ORDER2P_MASK | S5P_CIOCTRL_ORDER422_MASK |
+ S5P_CIOCTRL_YCBCR_PLANE_MASK | S5P_CIOCTRL_RGB16FMT_MASK);
+
+ if (fmt->colplanes == 1)
+ cfg |= ctx->out_order_1p;
+ else if (fmt->colplanes == 2)
+ cfg |= ctx->out_order_2p | S5P_CIOCTRL_YCBCR_2PLANE;
+ else if (fmt->colplanes == 3)
+ cfg |= S5P_CIOCTRL_YCBCR_3PLANE;
+
+ if (fmt->color == S5P_FIMC_RGB565)
+ cfg |= S5P_CIOCTRL_RGB565;
+ else if (fmt->color == S5P_FIMC_RGB555)
+ cfg |= S5P_CIOCTRL_ARGB1555;
+ else if (fmt->color == S5P_FIMC_RGB444)
+ cfg |= S5P_CIOCTRL_ARGB4444;
+
+ writel(cfg, dev->regs + S5P_CIOCTRL);
+}
+
+static void fimc_hw_en_autoload(struct fimc_dev *dev, int enable)
+{
+ u32 cfg = readl(dev->regs + S5P_ORGISIZE);
+ if (enable)
+ cfg |= S5P_CIREAL_ISIZE_AUTOLOAD_EN;
+ else
+ cfg &= ~S5P_CIREAL_ISIZE_AUTOLOAD_EN;
+ writel(cfg, dev->regs + S5P_ORGISIZE);
+}
+
+void fimc_hw_en_lastirq(struct fimc_dev *dev, int enable)
+{
+ u32 cfg = readl(dev->regs + S5P_CIOCTRL);
+ if (enable)
+ cfg |= S5P_CIOCTRL_LASTIRQ_ENABLE;
+ else
+ cfg &= ~S5P_CIOCTRL_LASTIRQ_ENABLE;
+ writel(cfg, dev->regs + S5P_CIOCTRL);
+}
+
+void fimc_hw_set_prescaler(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+ struct fimc_scaler *sc = &ctx->scaler;
+ u32 cfg, shfactor;
+
+ shfactor = 10 - (sc->hfactor + sc->vfactor);
+
+ cfg = S5P_CISCPRERATIO_SHFACTOR(shfactor);
+ cfg |= S5P_CISCPRERATIO_HOR(sc->pre_hratio);
+ cfg |= S5P_CISCPRERATIO_VER(sc->pre_vratio);
+ writel(cfg, dev->regs + S5P_CISCPRERATIO);
+
+ cfg = S5P_CISCPREDST_WIDTH(sc->pre_dst_width);
+ cfg |= S5P_CISCPREDST_HEIGHT(sc->pre_dst_height);
+ writel(cfg, dev->regs + S5P_CISCPREDST);
+}
+
+static void fimc_hw_set_scaler(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+ struct fimc_scaler *sc = &ctx->scaler;
+ struct fimc_frame *src_frame = &ctx->s_frame;
+ struct fimc_frame *dst_frame = &ctx->d_frame;
+
+ u32 cfg = readl(dev->regs + S5P_CISCCTRL);
+
+ cfg &= ~(S5P_CISCCTRL_CSCR2Y_WIDE | S5P_CISCCTRL_CSCY2R_WIDE |
+ S5P_CISCCTRL_SCALEUP_H | S5P_CISCCTRL_SCALEUP_V |
+ S5P_CISCCTRL_SCALERBYPASS | S5P_CISCCTRL_ONE2ONE |
+ S5P_CISCCTRL_INRGB_FMT_MASK | S5P_CISCCTRL_OUTRGB_FMT_MASK |
+ S5P_CISCCTRL_INTERLACE | S5P_CISCCTRL_RGB_EXT);
+
+ if (!(ctx->flags & FIMC_COLOR_RANGE_NARROW))
+ cfg |= (S5P_CISCCTRL_CSCR2Y_WIDE | S5P_CISCCTRL_CSCY2R_WIDE);
+
+ if (!sc->enabled)
+ cfg |= S5P_CISCCTRL_SCALERBYPASS;
+
+ if (sc->scaleup_h)
+ cfg |= S5P_CISCCTRL_SCALEUP_H;
+
+ if (sc->scaleup_v)
+ cfg |= S5P_CISCCTRL_SCALEUP_V;
+
+ if (sc->copy_mode)
+ cfg |= S5P_CISCCTRL_ONE2ONE;
+
+ if (ctx->in_path == FIMC_DMA) {
+ switch (src_frame->fmt->color) {
+ case S5P_FIMC_RGB565:
+ cfg |= S5P_CISCCTRL_INRGB_FMT_RGB565;
+ break;
+ case S5P_FIMC_RGB666:
+ cfg |= S5P_CISCCTRL_INRGB_FMT_RGB666;
+ break;
+ case S5P_FIMC_RGB888:
+ cfg |= S5P_CISCCTRL_INRGB_FMT_RGB888;
+ break;
+ }
+ }
+
+ if (ctx->out_path == FIMC_DMA) {
+ u32 color = dst_frame->fmt->color;
+
+ if (color >= S5P_FIMC_RGB444 && color <= S5P_FIMC_RGB565)
+ cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB565;
+ else if (color == S5P_FIMC_RGB666)
+ cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB666;
+ else if (color == S5P_FIMC_RGB888)
+ cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB888;
+ } else {
+ cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB888;
+
+ if (ctx->flags & FIMC_SCAN_MODE_INTERLACED)
+ cfg |= S5P_CISCCTRL_INTERLACE;
+ }
+
+ writel(cfg, dev->regs + S5P_CISCCTRL);
+}
+
+void fimc_hw_set_mainscaler(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+ struct samsung_fimc_variant *variant = dev->variant;
+ struct fimc_scaler *sc = &ctx->scaler;
+ u32 cfg;
+
+ dbg("main_hratio= 0x%X main_vratio= 0x%X",
+ sc->main_hratio, sc->main_vratio);
+
+ fimc_hw_set_scaler(ctx);
+
+ cfg = readl(dev->regs + S5P_CISCCTRL);
+ cfg &= ~(S5P_CISCCTRL_MHRATIO_MASK | S5P_CISCCTRL_MVRATIO_MASK);
+
+ if (variant->has_mainscaler_ext) {
+ cfg |= S5P_CISCCTRL_MHRATIO_EXT(sc->main_hratio);
+ cfg |= S5P_CISCCTRL_MVRATIO_EXT(sc->main_vratio);
+ writel(cfg, dev->regs + S5P_CISCCTRL);
+
+ cfg = readl(dev->regs + S5P_CIEXTEN);
+
+ cfg &= ~(S5P_CIEXTEN_MVRATIO_EXT_MASK |
+ S5P_CIEXTEN_MHRATIO_EXT_MASK);
+ cfg |= S5P_CIEXTEN_MHRATIO_EXT(sc->main_hratio);
+ cfg |= S5P_CIEXTEN_MVRATIO_EXT(sc->main_vratio);
+ writel(cfg, dev->regs + S5P_CIEXTEN);
+ } else {
+ cfg |= S5P_CISCCTRL_MHRATIO(sc->main_hratio);
+ cfg |= S5P_CISCCTRL_MVRATIO(sc->main_vratio);
+ writel(cfg, dev->regs + S5P_CISCCTRL);
+ }
+}
+
+void fimc_hw_en_capture(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+
+ u32 cfg = readl(dev->regs + S5P_CIIMGCPT);
+
+ if (ctx->out_path == FIMC_DMA) {
+ /* one shot mode */
+ cfg |= S5P_CIIMGCPT_CPT_FREN_ENABLE | S5P_CIIMGCPT_IMGCPTEN;
+ } else {
+ /* Continuous frame capture mode (freerun). */
+ cfg &= ~(S5P_CIIMGCPT_CPT_FREN_ENABLE |
+ S5P_CIIMGCPT_CPT_FRMOD_CNT);
+ cfg |= S5P_CIIMGCPT_IMGCPTEN;
+ }
+
+ if (ctx->scaler.enabled)
+ cfg |= S5P_CIIMGCPT_IMGCPTEN_SC;
+
+ writel(cfg | S5P_CIIMGCPT_IMGCPTEN, dev->regs + S5P_CIIMGCPT);
+}
+
+void fimc_hw_set_effect(struct fimc_ctx *ctx, bool active)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+ struct fimc_effect *effect = &ctx->effect;
+ u32 cfg = 0;
+
+ if (active) {
+ cfg |= S5P_CIIMGEFF_IE_SC_AFTER | S5P_CIIMGEFF_IE_ENABLE;
+ cfg |= effect->type;
+ if (effect->type == S5P_FIMC_EFFECT_ARBITRARY) {
+ cfg |= S5P_CIIMGEFF_PAT_CB(effect->pat_cb);
+ cfg |= S5P_CIIMGEFF_PAT_CR(effect->pat_cr);
+ }
+ }
+
+ writel(cfg, dev->regs + S5P_CIIMGEFF);
+}
+
+void fimc_hw_set_rgb_alpha(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+ struct fimc_frame *frame = &ctx->d_frame;
+ u32 cfg;
+
+ if (!(frame->fmt->flags & FMT_HAS_ALPHA))
+ return;
+
+ cfg = readl(dev->regs + S5P_CIOCTRL);
+ cfg &= ~S5P_CIOCTRL_ALPHA_OUT_MASK;
+ cfg |= (frame->alpha << 4);
+ writel(cfg, dev->regs + S5P_CIOCTRL);
+}
+
+static void fimc_hw_set_in_dma_size(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+ struct fimc_frame *frame = &ctx->s_frame;
+ u32 cfg_o = 0;
+ u32 cfg_r = 0;
+
+ if (FIMC_LCDFIFO == ctx->out_path)
+ cfg_r |= S5P_CIREAL_ISIZE_AUTOLOAD_EN;
+
+ cfg_o |= S5P_ORIG_SIZE_HOR(frame->f_width);
+ cfg_o |= S5P_ORIG_SIZE_VER(frame->f_height);
+ cfg_r |= S5P_CIREAL_ISIZE_WIDTH(frame->width);
+ cfg_r |= S5P_CIREAL_ISIZE_HEIGHT(frame->height);
+
+ writel(cfg_o, dev->regs + S5P_ORGISIZE);
+ writel(cfg_r, dev->regs + S5P_CIREAL_ISIZE);
+}
+
+void fimc_hw_set_in_dma(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+ struct fimc_frame *frame = &ctx->s_frame;
+ struct fimc_dma_offset *offset = &frame->dma_offset;
+ u32 cfg;
+
+ /* Set the pixel offsets. */
+ cfg = S5P_CIO_OFFS_HOR(offset->y_h);
+ cfg |= S5P_CIO_OFFS_VER(offset->y_v);
+ writel(cfg, dev->regs + S5P_CIIYOFF);
+
+ cfg = S5P_CIO_OFFS_HOR(offset->cb_h);
+ cfg |= S5P_CIO_OFFS_VER(offset->cb_v);
+ writel(cfg, dev->regs + S5P_CIICBOFF);
+
+ cfg = S5P_CIO_OFFS_HOR(offset->cr_h);
+ cfg |= S5P_CIO_OFFS_VER(offset->cr_v);
+ writel(cfg, dev->regs + S5P_CIICROFF);
+
+ /* Input original and real size. */
+ fimc_hw_set_in_dma_size(ctx);
+
+ /* Use DMA autoload only in FIFO mode. */
+ fimc_hw_en_autoload(dev, ctx->out_path == FIMC_LCDFIFO);
+
+ /* Set the input DMA to process single frame only. */
+ cfg = readl(dev->regs + S5P_MSCTRL);
+ cfg &= ~(S5P_MSCTRL_INFORMAT_MASK
+ | S5P_MSCTRL_IN_BURST_COUNT_MASK
+ | S5P_MSCTRL_INPUT_MASK
+ | S5P_MSCTRL_C_INT_IN_MASK
+ | S5P_MSCTRL_2P_IN_ORDER_MASK);
+
+ cfg |= (S5P_MSCTRL_IN_BURST_COUNT(4)
+ | S5P_MSCTRL_INPUT_MEMORY
+ | S5P_MSCTRL_FIFO_CTRL_FULL);
+
+ switch (frame->fmt->color) {
+ case S5P_FIMC_RGB565...S5P_FIMC_RGB888:
+ cfg |= S5P_MSCTRL_INFORMAT_RGB;
+ break;
+ case S5P_FIMC_YCBCR420:
+ cfg |= S5P_MSCTRL_INFORMAT_YCBCR420;
+
+ if (frame->fmt->colplanes == 2)
+ cfg |= ctx->in_order_2p | S5P_MSCTRL_C_INT_IN_2PLANE;
+ else
+ cfg |= S5P_MSCTRL_C_INT_IN_3PLANE;
+
+ break;
+ case S5P_FIMC_YCBYCR422...S5P_FIMC_CRYCBY422:
+ if (frame->fmt->colplanes == 1) {
+ cfg |= ctx->in_order_1p
+ | S5P_MSCTRL_INFORMAT_YCBCR422_1P;
+ } else {
+ cfg |= S5P_MSCTRL_INFORMAT_YCBCR422;
+
+ if (frame->fmt->colplanes == 2)
+ cfg |= ctx->in_order_2p
+ | S5P_MSCTRL_C_INT_IN_2PLANE;
+ else
+ cfg |= S5P_MSCTRL_C_INT_IN_3PLANE;
+ }
+ break;
+ default:
+ break;
+ }
+
+ writel(cfg, dev->regs + S5P_MSCTRL);
+
+ /* Input/output DMA linear/tiled mode. */
+ cfg = readl(dev->regs + S5P_CIDMAPARAM);
+ cfg &= ~S5P_CIDMAPARAM_TILE_MASK;
+
+ if (tiled_fmt(ctx->s_frame.fmt))
+ cfg |= S5P_CIDMAPARAM_R_64X32;
+
+ if (tiled_fmt(ctx->d_frame.fmt))
+ cfg |= S5P_CIDMAPARAM_W_64X32;
+
+ writel(cfg, dev->regs + S5P_CIDMAPARAM);
+}
+
+
+void fimc_hw_set_input_path(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+
+ u32 cfg = readl(dev->regs + S5P_MSCTRL);
+ cfg &= ~S5P_MSCTRL_INPUT_MASK;
+
+ if (ctx->in_path == FIMC_DMA)
+ cfg |= S5P_MSCTRL_INPUT_MEMORY;
+ else
+ cfg |= S5P_MSCTRL_INPUT_EXTCAM;
+
+ writel(cfg, dev->regs + S5P_MSCTRL);
+}
+
+void fimc_hw_set_output_path(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+
+ u32 cfg = readl(dev->regs + S5P_CISCCTRL);
+ cfg &= ~S5P_CISCCTRL_LCDPATHEN_FIFO;
+ if (ctx->out_path == FIMC_LCDFIFO)
+ cfg |= S5P_CISCCTRL_LCDPATHEN_FIFO;
+ writel(cfg, dev->regs + S5P_CISCCTRL);
+}
+
+void fimc_hw_set_input_addr(struct fimc_dev *dev, struct fimc_addr *paddr)
+{
+ u32 cfg = readl(dev->regs + S5P_CIREAL_ISIZE);
+ cfg |= S5P_CIREAL_ISIZE_ADDR_CH_DIS;
+ writel(cfg, dev->regs + S5P_CIREAL_ISIZE);
+
+ writel(paddr->y, dev->regs + S5P_CIIYSA(0));
+ writel(paddr->cb, dev->regs + S5P_CIICBSA(0));
+ writel(paddr->cr, dev->regs + S5P_CIICRSA(0));
+
+ cfg &= ~S5P_CIREAL_ISIZE_ADDR_CH_DIS;
+ writel(cfg, dev->regs + S5P_CIREAL_ISIZE);
+}
+
+void fimc_hw_set_output_addr(struct fimc_dev *dev,
+ struct fimc_addr *paddr, int index)
+{
+ int i = (index == -1) ? 0 : index;
+ do {
+ writel(paddr->y, dev->regs + S5P_CIOYSA(i));
+ writel(paddr->cb, dev->regs + S5P_CIOCBSA(i));
+ writel(paddr->cr, dev->regs + S5P_CIOCRSA(i));
+ dbg("dst_buf[%d]: 0x%X, cb: 0x%X, cr: 0x%X",
+ i, paddr->y, paddr->cb, paddr->cr);
+ } while (index == -1 && ++i < FIMC_MAX_OUT_BUFS);
+}
+
+int fimc_hw_set_camera_polarity(struct fimc_dev *fimc,
+ struct s5p_fimc_isp_info *cam)
+{
+ u32 cfg = readl(fimc->regs + S5P_CIGCTRL);
+
+ cfg &= ~(S5P_CIGCTRL_INVPOLPCLK | S5P_CIGCTRL_INVPOLVSYNC |
+ S5P_CIGCTRL_INVPOLHREF | S5P_CIGCTRL_INVPOLHSYNC |
+ S5P_CIGCTRL_INVPOLFIELD);
+
+ if (cam->flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
+ cfg |= S5P_CIGCTRL_INVPOLPCLK;
+
+ if (cam->flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ cfg |= S5P_CIGCTRL_INVPOLVSYNC;
+
+ if (cam->flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ cfg |= S5P_CIGCTRL_INVPOLHREF;
+
+ if (cam->flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ cfg |= S5P_CIGCTRL_INVPOLHSYNC;
+
+ if (cam->flags & V4L2_MBUS_FIELD_EVEN_LOW)
+ cfg |= S5P_CIGCTRL_INVPOLFIELD;
+
+ writel(cfg, fimc->regs + S5P_CIGCTRL);
+
+ return 0;
+}
+
+int fimc_hw_set_camera_source(struct fimc_dev *fimc,
+ struct s5p_fimc_isp_info *cam)
+{
+ struct fimc_frame *f = &fimc->vid_cap.ctx->s_frame;
+ u32 cfg = 0;
+ u32 bus_width;
+ int i;
+
+ static const struct {
+ u32 pixelcode;
+ u32 cisrcfmt;
+ u16 bus_width;
+ } pix_desc[] = {
+ { V4L2_MBUS_FMT_YUYV8_2X8, S5P_CISRCFMT_ORDER422_YCBYCR, 8 },
+ { V4L2_MBUS_FMT_YVYU8_2X8, S5P_CISRCFMT_ORDER422_YCRYCB, 8 },
+ { V4L2_MBUS_FMT_VYUY8_2X8, S5P_CISRCFMT_ORDER422_CRYCBY, 8 },
+ { V4L2_MBUS_FMT_UYVY8_2X8, S5P_CISRCFMT_ORDER422_CBYCRY, 8 },
+ /* TODO: Add pixel codes for 16-bit bus width */
+ };
+
+ if (cam->bus_type == FIMC_ITU_601 || cam->bus_type == FIMC_ITU_656) {
+ for (i = 0; i < ARRAY_SIZE(pix_desc); i++) {
+ if (fimc->vid_cap.mf.code == pix_desc[i].pixelcode) {
+ cfg = pix_desc[i].cisrcfmt;
+ bus_width = pix_desc[i].bus_width;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(pix_desc)) {
+ v4l2_err(fimc->vid_cap.vfd,
+ "Camera color format not supported: %d\n",
+ fimc->vid_cap.mf.code);
+ return -EINVAL;
+ }
+
+ if (cam->bus_type == FIMC_ITU_601) {
+ if (bus_width == 8)
+ cfg |= S5P_CISRCFMT_ITU601_8BIT;
+ else if (bus_width == 16)
+ cfg |= S5P_CISRCFMT_ITU601_16BIT;
+ } /* else defaults to ITU-R BT.656 8-bit */
+ } else if (cam->bus_type == FIMC_MIPI_CSI2) {
+ if (fimc_fmt_is_jpeg(f->fmt->color))
+ cfg |= S5P_CISRCFMT_ITU601_8BIT;
+ }
+
+ cfg |= S5P_CISRCFMT_HSIZE(f->o_width) | S5P_CISRCFMT_VSIZE(f->o_height);
+ writel(cfg, fimc->regs + S5P_CISRCFMT);
+ return 0;
+}
+
+
+int fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f)
+{
+ u32 hoff2, voff2;
+
+ u32 cfg = readl(fimc->regs + S5P_CIWDOFST);
+
+ cfg &= ~(S5P_CIWDOFST_HOROFF_MASK | S5P_CIWDOFST_VEROFF_MASK);
+ cfg |= S5P_CIWDOFST_OFF_EN |
+ S5P_CIWDOFST_HOROFF(f->offs_h) |
+ S5P_CIWDOFST_VEROFF(f->offs_v);
+
+ writel(cfg, fimc->regs + S5P_CIWDOFST);
+
+ /* See CIWDOFSTn register description in the datasheet for details. */
+ hoff2 = f->o_width - f->width - f->offs_h;
+ voff2 = f->o_height - f->height - f->offs_v;
+ cfg = S5P_CIWDOFST2_HOROFF(hoff2) | S5P_CIWDOFST2_VEROFF(voff2);
+
+ writel(cfg, fimc->regs + S5P_CIWDOFST2);
+ return 0;
+}
+
+int fimc_hw_set_camera_type(struct fimc_dev *fimc,
+ struct s5p_fimc_isp_info *cam)
+{
+ u32 cfg, tmp;
+ struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+
+ cfg = readl(fimc->regs + S5P_CIGCTRL);
+
+ /* Select ITU B interface, disable Writeback path and test pattern. */
+ cfg &= ~(S5P_CIGCTRL_TESTPAT_MASK | S5P_CIGCTRL_SELCAM_ITU_A |
+ S5P_CIGCTRL_SELCAM_MIPI | S5P_CIGCTRL_CAMIF_SELWB |
+ S5P_CIGCTRL_SELCAM_MIPI_A | S5P_CIGCTRL_CAM_JPEG);
+
+ if (cam->bus_type == FIMC_MIPI_CSI2) {
+ cfg |= S5P_CIGCTRL_SELCAM_MIPI;
+
+ if (cam->mux_id == 0)
+ cfg |= S5P_CIGCTRL_SELCAM_MIPI_A;
+
+ /* TODO: add remaining supported formats. */
+ switch (vid_cap->mf.code) {
+ case V4L2_MBUS_FMT_VYUY8_2X8:
+ tmp = S5P_CSIIMGFMT_YCBCR422_8BIT;
+ break;
+ case V4L2_MBUS_FMT_JPEG_1X8:
+ tmp = S5P_CSIIMGFMT_USER(1);
+ cfg |= S5P_CIGCTRL_CAM_JPEG;
+ break;
+ default:
+ v4l2_err(fimc->vid_cap.vfd,
+ "Not supported camera pixel format: %d",
+ vid_cap->mf.code);
+ return -EINVAL;
+ }
+ tmp |= (cam->csi_data_align == 32) << 8;
+
+ writel(tmp, fimc->regs + S5P_CSIIMGFMT);
+
+ } else if (cam->bus_type == FIMC_ITU_601 ||
+ cam->bus_type == FIMC_ITU_656) {
+ if (cam->mux_id == 0) /* ITU-A, ITU-B: 0, 1 */
+ cfg |= S5P_CIGCTRL_SELCAM_ITU_A;
+ } else if (cam->bus_type == FIMC_LCD_WB) {
+ cfg |= S5P_CIGCTRL_CAMIF_SELWB;
+ } else {
+ err("invalid camera bus type selected\n");
+ return -EINVAL;
+ }
+ writel(cfg, fimc->regs + S5P_CIGCTRL);
+
+ return 0;
+}
diff --git a/drivers/media/video/s5p-fimc/mipi-csis.c b/drivers/media/video/s5p-fimc/mipi-csis.c
new file mode 100644
index 00000000..f44f6903
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/mipi-csis.c
@@ -0,0 +1,729 @@
+/*
+ * Samsung S5P/EXYNOS4 SoC series MIPI-CSI receiver driver
+ *
+ * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd.
+ * Sylwester Nawrocki, <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/memory.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-subdev.h>
+#include <plat/mipi_csis.h>
+#include "mipi-csis.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+/* Register map definition */
+
+/* CSIS global control */
+#define S5PCSIS_CTRL 0x00
+#define S5PCSIS_CTRL_DPDN_DEFAULT (0 << 31)
+#define S5PCSIS_CTRL_DPDN_SWAP (1 << 31)
+#define S5PCSIS_CTRL_ALIGN_32BIT (1 << 20)
+#define S5PCSIS_CTRL_UPDATE_SHADOW (1 << 16)
+#define S5PCSIS_CTRL_WCLK_EXTCLK (1 << 8)
+#define S5PCSIS_CTRL_RESET (1 << 4)
+#define S5PCSIS_CTRL_ENABLE (1 << 0)
+
+/* D-PHY control */
+#define S5PCSIS_DPHYCTRL 0x04
+#define S5PCSIS_DPHYCTRL_HSS_MASK (0x1f << 27)
+#define S5PCSIS_DPHYCTRL_ENABLE (0x1f << 0)
+
+#define S5PCSIS_CONFIG 0x08
+#define S5PCSIS_CFG_FMT_YCBCR422_8BIT (0x1e << 2)
+#define S5PCSIS_CFG_FMT_RAW8 (0x2a << 2)
+#define S5PCSIS_CFG_FMT_RAW10 (0x2b << 2)
+#define S5PCSIS_CFG_FMT_RAW12 (0x2c << 2)
+/* User defined formats, x = 1...4 */
+#define S5PCSIS_CFG_FMT_USER(x) ((0x30 + x - 1) << 2)
+#define S5PCSIS_CFG_FMT_MASK (0x3f << 2)
+#define S5PCSIS_CFG_NR_LANE_MASK 3
+
+/* Interrupt mask. */
+#define S5PCSIS_INTMSK 0x10
+#define S5PCSIS_INTMSK_EN_ALL 0xf000003f
+#define S5PCSIS_INTSRC 0x14
+
+/* Pixel resolution */
+#define S5PCSIS_RESOL 0x2c
+#define CSIS_MAX_PIX_WIDTH 0xffff
+#define CSIS_MAX_PIX_HEIGHT 0xffff
+
+enum {
+ CSIS_CLK_MUX,
+ CSIS_CLK_GATE,
+};
+
+static char *csi_clock_name[] = {
+ [CSIS_CLK_MUX] = "sclk_csis",
+ [CSIS_CLK_GATE] = "csis",
+};
+#define NUM_CSIS_CLOCKS ARRAY_SIZE(csi_clock_name)
+
+static const char * const csis_supply_name[] = {
+ "vdd11", /* 1.1V or 1.2V (s5pc100) MIPI CSI suppply */
+ "vdd18", /* VDD 1.8V and MIPI CSI PLL supply */
+};
+#define CSIS_NUM_SUPPLIES ARRAY_SIZE(csis_supply_name)
+
+enum {
+ ST_POWERED = 1,
+ ST_STREAMING = 2,
+ ST_SUSPENDED = 4,
+};
+
+/**
+ * struct csis_state - the driver's internal state data structure
+ * @lock: mutex serializing the subdev and power management operations,
+ * protecting @format and @flags members
+ * @pads: CSIS pads array
+ * @sd: v4l2_subdev associated with CSIS device instance
+ * @pdev: CSIS platform device
+ * @regs: mmaped I/O registers memory
+ * @clock: CSIS clocks
+ * @irq: requested s5p-mipi-csis irq number
+ * @flags: the state variable for power and streaming control
+ * @csis_fmt: current CSIS pixel format
+ * @format: common media bus format for the source and sink pad
+ */
+struct csis_state {
+ struct mutex lock;
+ struct media_pad pads[CSIS_PADS_NUM];
+ struct v4l2_subdev sd;
+ struct platform_device *pdev;
+ void __iomem *regs;
+ struct regulator_bulk_data supplies[CSIS_NUM_SUPPLIES];
+ struct clk *clock[NUM_CSIS_CLOCKS];
+ int irq;
+ u32 flags;
+ const struct csis_pix_format *csis_fmt;
+ struct v4l2_mbus_framefmt format;
+};
+
+/**
+ * struct csis_pix_format - CSIS pixel format description
+ * @pix_width_alignment: horizontal pixel alignment, width will be
+ * multiple of 2^pix_width_alignment
+ * @code: corresponding media bus code
+ * @fmt_reg: S5PCSIS_CONFIG register value
+ */
+struct csis_pix_format {
+ unsigned int pix_width_alignment;
+ enum v4l2_mbus_pixelcode code;
+ u32 fmt_reg;
+};
+
+static const struct csis_pix_format s5pcsis_formats[] = {
+ {
+ .code = V4L2_MBUS_FMT_VYUY8_2X8,
+ .fmt_reg = S5PCSIS_CFG_FMT_YCBCR422_8BIT,
+ }, {
+ .code = V4L2_MBUS_FMT_JPEG_1X8,
+ .fmt_reg = S5PCSIS_CFG_FMT_USER(1),
+ },
+};
+
+#define s5pcsis_write(__csis, __r, __v) writel(__v, __csis->regs + __r)
+#define s5pcsis_read(__csis, __r) readl(__csis->regs + __r)
+
+static struct csis_state *sd_to_csis_state(struct v4l2_subdev *sdev)
+{
+ return container_of(sdev, struct csis_state, sd);
+}
+
+static const struct csis_pix_format *find_csis_format(
+ struct v4l2_mbus_framefmt *mf)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(s5pcsis_formats); i++)
+ if (mf->code == s5pcsis_formats[i].code)
+ return &s5pcsis_formats[i];
+ return NULL;
+}
+
+static void s5pcsis_enable_interrupts(struct csis_state *state, bool on)
+{
+ u32 val = s5pcsis_read(state, S5PCSIS_INTMSK);
+
+ val = on ? val | S5PCSIS_INTMSK_EN_ALL :
+ val & ~S5PCSIS_INTMSK_EN_ALL;
+ s5pcsis_write(state, S5PCSIS_INTMSK, val);
+}
+
+static void s5pcsis_reset(struct csis_state *state)
+{
+ u32 val = s5pcsis_read(state, S5PCSIS_CTRL);
+
+ s5pcsis_write(state, S5PCSIS_CTRL, val | S5PCSIS_CTRL_RESET);
+ udelay(10);
+}
+
+static void s5pcsis_system_enable(struct csis_state *state, int on)
+{
+ u32 val;
+
+ val = s5pcsis_read(state, S5PCSIS_CTRL);
+ if (on)
+ val |= S5PCSIS_CTRL_ENABLE;
+ else
+ val &= ~S5PCSIS_CTRL_ENABLE;
+ s5pcsis_write(state, S5PCSIS_CTRL, val);
+
+ val = s5pcsis_read(state, S5PCSIS_DPHYCTRL);
+ if (on)
+ val |= S5PCSIS_DPHYCTRL_ENABLE;
+ else
+ val &= ~S5PCSIS_DPHYCTRL_ENABLE;
+ s5pcsis_write(state, S5PCSIS_DPHYCTRL, val);
+}
+
+/* Called with the state.lock mutex held */
+static void __s5pcsis_set_format(struct csis_state *state)
+{
+ struct v4l2_mbus_framefmt *mf = &state->format;
+ u32 val;
+
+ v4l2_dbg(1, debug, &state->sd, "fmt: %d, %d x %d\n",
+ mf->code, mf->width, mf->height);
+
+ /* Color format */
+ val = s5pcsis_read(state, S5PCSIS_CONFIG);
+ val = (val & ~S5PCSIS_CFG_FMT_MASK) | state->csis_fmt->fmt_reg;
+ s5pcsis_write(state, S5PCSIS_CONFIG, val);
+
+ /* Pixel resolution */
+ val = (mf->width << 16) | mf->height;
+ s5pcsis_write(state, S5PCSIS_RESOL, val);
+}
+
+static void s5pcsis_set_hsync_settle(struct csis_state *state, int settle)
+{
+ u32 val = s5pcsis_read(state, S5PCSIS_DPHYCTRL);
+
+ val = (val & ~S5PCSIS_DPHYCTRL_HSS_MASK) | (settle << 27);
+ s5pcsis_write(state, S5PCSIS_DPHYCTRL, val);
+}
+
+static void s5pcsis_set_params(struct csis_state *state)
+{
+ struct s5p_platform_mipi_csis *pdata = state->pdev->dev.platform_data;
+ u32 val;
+
+ val = s5pcsis_read(state, S5PCSIS_CONFIG);
+ val = (val & ~S5PCSIS_CFG_NR_LANE_MASK) | (pdata->lanes - 1);
+ s5pcsis_write(state, S5PCSIS_CONFIG, val);
+
+ __s5pcsis_set_format(state);
+ s5pcsis_set_hsync_settle(state, pdata->hs_settle);
+
+ val = s5pcsis_read(state, S5PCSIS_CTRL);
+ if (pdata->alignment == 32)
+ val |= S5PCSIS_CTRL_ALIGN_32BIT;
+ else /* 24-bits */
+ val &= ~S5PCSIS_CTRL_ALIGN_32BIT;
+ /* Not using external clock. */
+ val &= ~S5PCSIS_CTRL_WCLK_EXTCLK;
+ s5pcsis_write(state, S5PCSIS_CTRL, val);
+
+ /* Update the shadow register. */
+ val = s5pcsis_read(state, S5PCSIS_CTRL);
+ s5pcsis_write(state, S5PCSIS_CTRL, val | S5PCSIS_CTRL_UPDATE_SHADOW);
+}
+
+static void s5pcsis_clk_put(struct csis_state *state)
+{
+ int i;
+
+ for (i = 0; i < NUM_CSIS_CLOCKS; i++) {
+ if (IS_ERR_OR_NULL(state->clock[i]))
+ continue;
+ clk_unprepare(state->clock[i]);
+ clk_put(state->clock[i]);
+ state->clock[i] = NULL;
+ }
+}
+
+static int s5pcsis_clk_get(struct csis_state *state)
+{
+ struct device *dev = &state->pdev->dev;
+ int i, ret;
+
+ for (i = 0; i < NUM_CSIS_CLOCKS; i++) {
+ state->clock[i] = clk_get(dev, csi_clock_name[i]);
+ if (IS_ERR(state->clock[i]))
+ goto err;
+ ret = clk_prepare(state->clock[i]);
+ if (ret < 0) {
+ clk_put(state->clock[i]);
+ state->clock[i] = NULL;
+ goto err;
+ }
+ }
+ return 0;
+err:
+ s5pcsis_clk_put(state);
+ dev_err(dev, "failed to get clock: %s\n", csi_clock_name[i]);
+ return -ENXIO;
+}
+
+static int s5pcsis_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct csis_state *state = sd_to_csis_state(sd);
+ struct device *dev = &state->pdev->dev;
+
+ if (on)
+ return pm_runtime_get_sync(dev);
+
+ return pm_runtime_put_sync(dev);
+}
+
+static void s5pcsis_start_stream(struct csis_state *state)
+{
+ s5pcsis_reset(state);
+ s5pcsis_set_params(state);
+ s5pcsis_system_enable(state, true);
+ s5pcsis_enable_interrupts(state, true);
+}
+
+static void s5pcsis_stop_stream(struct csis_state *state)
+{
+ s5pcsis_enable_interrupts(state, false);
+ s5pcsis_system_enable(state, false);
+}
+
+/* v4l2_subdev operations */
+static int s5pcsis_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct csis_state *state = sd_to_csis_state(sd);
+ int ret = 0;
+
+ v4l2_dbg(1, debug, sd, "%s: %d, state: 0x%x\n",
+ __func__, enable, state->flags);
+
+ if (enable) {
+ ret = pm_runtime_get_sync(&state->pdev->dev);
+ if (ret && ret != 1)
+ return ret;
+ }
+ mutex_lock(&state->lock);
+ if (enable) {
+ if (state->flags & ST_SUSPENDED) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+ s5pcsis_start_stream(state);
+ state->flags |= ST_STREAMING;
+ } else {
+ s5pcsis_stop_stream(state);
+ state->flags &= ~ST_STREAMING;
+ }
+unlock:
+ mutex_unlock(&state->lock);
+ if (!enable)
+ pm_runtime_put(&state->pdev->dev);
+
+ return ret == 1 ? 0 : ret;
+}
+
+static int s5pcsis_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index >= ARRAY_SIZE(s5pcsis_formats))
+ return -EINVAL;
+
+ code->code = s5pcsis_formats[code->index].code;
+ return 0;
+}
+
+static struct csis_pix_format const *s5pcsis_try_format(
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct csis_pix_format const *csis_fmt;
+
+ csis_fmt = find_csis_format(mf);
+ if (csis_fmt == NULL)
+ csis_fmt = &s5pcsis_formats[0];
+
+ mf->code = csis_fmt->code;
+ v4l_bound_align_image(&mf->width, 1, CSIS_MAX_PIX_WIDTH,
+ csis_fmt->pix_width_alignment,
+ &mf->height, 1, CSIS_MAX_PIX_HEIGHT, 1,
+ 0);
+ return csis_fmt;
+}
+
+static struct v4l2_mbus_framefmt *__s5pcsis_get_format(
+ struct csis_state *state, struct v4l2_subdev_fh *fh,
+ u32 pad, enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL;
+
+ return &state->format;
+}
+
+static int s5pcsis_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct csis_state *state = sd_to_csis_state(sd);
+ struct csis_pix_format const *csis_fmt;
+ struct v4l2_mbus_framefmt *mf;
+
+ if (fmt->pad != CSIS_PAD_SOURCE && fmt->pad != CSIS_PAD_SINK)
+ return -EINVAL;
+
+ mf = __s5pcsis_get_format(state, fh, fmt->pad, fmt->which);
+
+ if (fmt->pad == CSIS_PAD_SOURCE) {
+ if (mf) {
+ mutex_lock(&state->lock);
+ fmt->format = *mf;
+ mutex_unlock(&state->lock);
+ }
+ return 0;
+ }
+ csis_fmt = s5pcsis_try_format(&fmt->format);
+ if (mf) {
+ mutex_lock(&state->lock);
+ *mf = fmt->format;
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ state->csis_fmt = csis_fmt;
+ mutex_unlock(&state->lock);
+ }
+ return 0;
+}
+
+static int s5pcsis_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct csis_state *state = sd_to_csis_state(sd);
+ struct v4l2_mbus_framefmt *mf;
+
+ if (fmt->pad != CSIS_PAD_SOURCE && fmt->pad != CSIS_PAD_SINK)
+ return -EINVAL;
+
+ mf = __s5pcsis_get_format(state, fh, fmt->pad, fmt->which);
+ if (!mf)
+ return -EINVAL;
+
+ mutex_lock(&state->lock);
+ fmt->format = *mf;
+ mutex_unlock(&state->lock);
+ return 0;
+}
+
+static int s5pcsis_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
+
+ format->colorspace = V4L2_COLORSPACE_JPEG;
+ format->code = s5pcsis_formats[0].code;
+ format->width = S5PCSIS_DEF_PIX_WIDTH;
+ format->height = S5PCSIS_DEF_PIX_HEIGHT;
+ format->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops s5pcsis_sd_internal_ops = {
+ .open = s5pcsis_open,
+};
+
+static struct v4l2_subdev_core_ops s5pcsis_core_ops = {
+ .s_power = s5pcsis_s_power,
+};
+
+static struct v4l2_subdev_pad_ops s5pcsis_pad_ops = {
+ .enum_mbus_code = s5pcsis_enum_mbus_code,
+ .get_fmt = s5pcsis_get_fmt,
+ .set_fmt = s5pcsis_set_fmt,
+};
+
+static struct v4l2_subdev_video_ops s5pcsis_video_ops = {
+ .s_stream = s5pcsis_s_stream,
+};
+
+static struct v4l2_subdev_ops s5pcsis_subdev_ops = {
+ .core = &s5pcsis_core_ops,
+ .pad = &s5pcsis_pad_ops,
+ .video = &s5pcsis_video_ops,
+};
+
+static irqreturn_t s5pcsis_irq_handler(int irq, void *dev_id)
+{
+ struct csis_state *state = dev_id;
+ u32 val;
+
+ /* Just clear the interrupt pending bits. */
+ val = s5pcsis_read(state, S5PCSIS_INTSRC);
+ s5pcsis_write(state, S5PCSIS_INTSRC, val);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit s5pcsis_probe(struct platform_device *pdev)
+{
+ struct s5p_platform_mipi_csis *pdata;
+ struct resource *mem_res;
+ struct csis_state *state;
+ int ret = -ENOMEM;
+ int i;
+
+ state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ mutex_init(&state->lock);
+ state->pdev = pdev;
+
+ pdata = pdev->dev.platform_data;
+ if (pdata == NULL || pdata->phy_enable == NULL) {
+ dev_err(&pdev->dev, "Platform data not fully specified\n");
+ return -EINVAL;
+ }
+
+ if ((pdev->id == 1 && pdata->lanes > CSIS1_MAX_LANES) ||
+ pdata->lanes > CSIS0_MAX_LANES) {
+ dev_err(&pdev->dev, "Unsupported number of data lanes: %d\n",
+ pdata->lanes);
+ return -EINVAL;
+ }
+
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ state->regs = devm_request_and_ioremap(&pdev->dev, mem_res);
+ if (state->regs == NULL) {
+ dev_err(&pdev->dev, "Failed to request and remap io memory\n");
+ return -ENXIO;
+ }
+
+ state->irq = platform_get_irq(pdev, 0);
+ if (state->irq < 0) {
+ dev_err(&pdev->dev, "Failed to get irq\n");
+ return state->irq;
+ }
+
+ for (i = 0; i < CSIS_NUM_SUPPLIES; i++)
+ state->supplies[i].supply = csis_supply_name[i];
+
+ ret = regulator_bulk_get(&pdev->dev, CSIS_NUM_SUPPLIES,
+ state->supplies);
+ if (ret)
+ return ret;
+
+ ret = s5pcsis_clk_get(state);
+ if (ret)
+ goto e_clkput;
+
+ clk_enable(state->clock[CSIS_CLK_MUX]);
+ if (pdata->clk_rate)
+ clk_set_rate(state->clock[CSIS_CLK_MUX], pdata->clk_rate);
+ else
+ dev_WARN(&pdev->dev, "No clock frequency specified!\n");
+
+ ret = devm_request_irq(&pdev->dev, state->irq, s5pcsis_irq_handler,
+ 0, dev_name(&pdev->dev), state);
+ if (ret) {
+ dev_err(&pdev->dev, "Interrupt request failed\n");
+ goto e_regput;
+ }
+
+ v4l2_subdev_init(&state->sd, &s5pcsis_subdev_ops);
+ state->sd.owner = THIS_MODULE;
+ strlcpy(state->sd.name, dev_name(&pdev->dev), sizeof(state->sd.name));
+ state->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ state->csis_fmt = &s5pcsis_formats[0];
+
+ state->format.code = s5pcsis_formats[0].code;
+ state->format.width = S5PCSIS_DEF_PIX_WIDTH;
+ state->format.height = S5PCSIS_DEF_PIX_HEIGHT;
+
+ state->pads[CSIS_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ state->pads[CSIS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&state->sd.entity,
+ CSIS_PADS_NUM, state->pads, 0);
+ if (ret < 0)
+ goto e_clkput;
+
+ /* This allows to retrieve the platform device id by the host driver */
+ v4l2_set_subdevdata(&state->sd, pdev);
+
+ /* .. and a pointer to the subdev. */
+ platform_set_drvdata(pdev, &state->sd);
+
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+
+e_regput:
+ regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies);
+e_clkput:
+ clk_disable(state->clock[CSIS_CLK_MUX]);
+ s5pcsis_clk_put(state);
+ return ret;
+}
+
+static int s5pcsis_pm_suspend(struct device *dev, bool runtime)
+{
+ struct s5p_platform_mipi_csis *pdata = dev->platform_data;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+ struct csis_state *state = sd_to_csis_state(sd);
+ int ret = 0;
+
+ v4l2_dbg(1, debug, sd, "%s: flags: 0x%x\n",
+ __func__, state->flags);
+
+ mutex_lock(&state->lock);
+ if (state->flags & ST_POWERED) {
+ s5pcsis_stop_stream(state);
+ ret = pdata->phy_enable(state->pdev, false);
+ if (ret)
+ goto unlock;
+ ret = regulator_bulk_disable(CSIS_NUM_SUPPLIES,
+ state->supplies);
+ if (ret)
+ goto unlock;
+ clk_disable(state->clock[CSIS_CLK_GATE]);
+ state->flags &= ~ST_POWERED;
+ if (!runtime)
+ state->flags |= ST_SUSPENDED;
+ }
+ unlock:
+ mutex_unlock(&state->lock);
+ return ret ? -EAGAIN : 0;
+}
+
+static int s5pcsis_pm_resume(struct device *dev, bool runtime)
+{
+ struct s5p_platform_mipi_csis *pdata = dev->platform_data;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+ struct csis_state *state = sd_to_csis_state(sd);
+ int ret = 0;
+
+ v4l2_dbg(1, debug, sd, "%s: flags: 0x%x\n",
+ __func__, state->flags);
+
+ mutex_lock(&state->lock);
+ if (!runtime && !(state->flags & ST_SUSPENDED))
+ goto unlock;
+
+ if (!(state->flags & ST_POWERED)) {
+ ret = regulator_bulk_enable(CSIS_NUM_SUPPLIES,
+ state->supplies);
+ if (ret)
+ goto unlock;
+ ret = pdata->phy_enable(state->pdev, true);
+ if (!ret) {
+ state->flags |= ST_POWERED;
+ } else {
+ regulator_bulk_disable(CSIS_NUM_SUPPLIES,
+ state->supplies);
+ goto unlock;
+ }
+ clk_enable(state->clock[CSIS_CLK_GATE]);
+ }
+ if (state->flags & ST_STREAMING)
+ s5pcsis_start_stream(state);
+
+ state->flags &= ~ST_SUSPENDED;
+ unlock:
+ mutex_unlock(&state->lock);
+ return ret ? -EAGAIN : 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int s5pcsis_suspend(struct device *dev)
+{
+ return s5pcsis_pm_suspend(dev, false);
+}
+
+static int s5pcsis_resume(struct device *dev)
+{
+ return s5pcsis_pm_resume(dev, false);
+}
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+static int s5pcsis_runtime_suspend(struct device *dev)
+{
+ return s5pcsis_pm_suspend(dev, true);
+}
+
+static int s5pcsis_runtime_resume(struct device *dev)
+{
+ return s5pcsis_pm_resume(dev, true);
+}
+#endif
+
+static int __devexit s5pcsis_remove(struct platform_device *pdev)
+{
+ struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+ struct csis_state *state = sd_to_csis_state(sd);
+
+ pm_runtime_disable(&pdev->dev);
+ s5pcsis_pm_suspend(&pdev->dev, false);
+ clk_disable(state->clock[CSIS_CLK_MUX]);
+ pm_runtime_set_suspended(&pdev->dev);
+ s5pcsis_clk_put(state);
+ regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies);
+
+ media_entity_cleanup(&state->sd.entity);
+
+ return 0;
+}
+
+static const struct dev_pm_ops s5pcsis_pm_ops = {
+ SET_RUNTIME_PM_OPS(s5pcsis_runtime_suspend, s5pcsis_runtime_resume,
+ NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(s5pcsis_suspend, s5pcsis_resume)
+};
+
+static struct platform_driver s5pcsis_driver = {
+ .probe = s5pcsis_probe,
+ .remove = __devexit_p(s5pcsis_remove),
+ .driver = {
+ .name = CSIS_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .pm = &s5pcsis_pm_ops,
+ },
+};
+
+static int __init s5pcsis_init(void)
+{
+ return platform_driver_probe(&s5pcsis_driver, s5pcsis_probe);
+}
+
+static void __exit s5pcsis_exit(void)
+{
+ platform_driver_unregister(&s5pcsis_driver);
+}
+
+module_init(s5pcsis_init);
+module_exit(s5pcsis_exit);
+
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_DESCRIPTION("S5P/EXYNOS4 MIPI CSI receiver driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/s5p-fimc/mipi-csis.h b/drivers/media/video/s5p-fimc/mipi-csis.h
new file mode 100644
index 00000000..27092863
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/mipi-csis.h
@@ -0,0 +1,25 @@
+/*
+ * Samsung S5P/EXYNOS4 SoC series MIPI-CSI receiver driver
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef S5P_MIPI_CSIS_H_
+#define S5P_MIPI_CSIS_H_
+
+#define CSIS_DRIVER_NAME "s5p-mipi-csis"
+#define CSIS_MAX_ENTITIES 2
+#define CSIS0_MAX_LANES 4
+#define CSIS1_MAX_LANES 2
+
+#define CSIS_PAD_SINK 0
+#define CSIS_PAD_SOURCE 1
+#define CSIS_PADS_NUM 2
+
+#define S5PCSIS_DEF_PIX_WIDTH 640
+#define S5PCSIS_DEF_PIX_HEIGHT 480
+
+#endif
diff --git a/drivers/media/video/s5p-fimc/regs-fimc.h b/drivers/media/video/s5p-fimc/regs-fimc.h
new file mode 100644
index 00000000..c7a5bc51
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/regs-fimc.h
@@ -0,0 +1,301 @@
+/*
+ * Register definition file for Samsung Camera Interface (FIMC) driver
+ *
+ * Copyright (c) 2010 Samsung Electronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef REGS_FIMC_H_
+#define REGS_FIMC_H_
+
+/* Input source format */
+#define S5P_CISRCFMT 0x00
+#define S5P_CISRCFMT_ITU601_8BIT (1 << 31)
+#define S5P_CISRCFMT_ITU601_16BIT (1 << 29)
+#define S5P_CISRCFMT_ORDER422_YCBYCR (0 << 14)
+#define S5P_CISRCFMT_ORDER422_YCRYCB (1 << 14)
+#define S5P_CISRCFMT_ORDER422_CBYCRY (2 << 14)
+#define S5P_CISRCFMT_ORDER422_CRYCBY (3 << 14)
+#define S5P_CISRCFMT_HSIZE(x) ((x) << 16)
+#define S5P_CISRCFMT_VSIZE(x) ((x) << 0)
+
+/* Window offset */
+#define S5P_CIWDOFST 0x04
+#define S5P_CIWDOFST_OFF_EN (1 << 31)
+#define S5P_CIWDOFST_CLROVFIY (1 << 30)
+#define S5P_CIWDOFST_CLROVRLB (1 << 29)
+#define S5P_CIWDOFST_HOROFF_MASK (0x7ff << 16)
+#define S5P_CIWDOFST_CLROVFICB (1 << 15)
+#define S5P_CIWDOFST_CLROVFICR (1 << 14)
+#define S5P_CIWDOFST_HOROFF(x) ((x) << 16)
+#define S5P_CIWDOFST_VEROFF(x) ((x) << 0)
+#define S5P_CIWDOFST_VEROFF_MASK (0xfff << 0)
+
+/* Global control */
+#define S5P_CIGCTRL 0x08
+#define S5P_CIGCTRL_SWRST (1 << 31)
+#define S5P_CIGCTRL_CAMRST_A (1 << 30)
+#define S5P_CIGCTRL_SELCAM_ITU_A (1 << 29)
+#define S5P_CIGCTRL_TESTPAT_NORMAL (0 << 27)
+#define S5P_CIGCTRL_TESTPAT_COLOR_BAR (1 << 27)
+#define S5P_CIGCTRL_TESTPAT_HOR_INC (2 << 27)
+#define S5P_CIGCTRL_TESTPAT_VER_INC (3 << 27)
+#define S5P_CIGCTRL_TESTPAT_MASK (3 << 27)
+#define S5P_CIGCTRL_TESTPAT_SHIFT (27)
+#define S5P_CIGCTRL_INVPOLPCLK (1 << 26)
+#define S5P_CIGCTRL_INVPOLVSYNC (1 << 25)
+#define S5P_CIGCTRL_INVPOLHREF (1 << 24)
+#define S5P_CIGCTRL_IRQ_OVFEN (1 << 22)
+#define S5P_CIGCTRL_HREF_MASK (1 << 21)
+#define S5P_CIGCTRL_IRQ_LEVEL (1 << 20)
+#define S5P_CIGCTRL_IRQ_CLR (1 << 19)
+#define S5P_CIGCTRL_IRQ_ENABLE (1 << 16)
+#define S5P_CIGCTRL_SHDW_DISABLE (1 << 12)
+#define S5P_CIGCTRL_CAM_JPEG (1 << 8)
+#define S5P_CIGCTRL_SELCAM_MIPI_A (1 << 7)
+#define S5P_CIGCTRL_CAMIF_SELWB (1 << 6)
+/* 0 - ITU601; 1 - ITU709 */
+#define S5P_CIGCTRL_CSC_ITU601_709 (1 << 5)
+#define S5P_CIGCTRL_INVPOLHSYNC (1 << 4)
+#define S5P_CIGCTRL_SELCAM_MIPI (1 << 3)
+#define S5P_CIGCTRL_INVPOLFIELD (1 << 1)
+#define S5P_CIGCTRL_INTERLACE (1 << 0)
+
+/* Window offset 2 */
+#define S5P_CIWDOFST2 0x14
+#define S5P_CIWDOFST2_HOROFF_MASK (0xfff << 16)
+#define S5P_CIWDOFST2_VEROFF_MASK (0xfff << 0)
+#define S5P_CIWDOFST2_HOROFF(x) ((x) << 16)
+#define S5P_CIWDOFST2_VEROFF(x) ((x) << 0)
+
+/* Output DMA Y/Cb/Cr plane start addresses */
+#define S5P_CIOYSA(n) (0x18 + (n) * 4)
+#define S5P_CIOCBSA(n) (0x28 + (n) * 4)
+#define S5P_CIOCRSA(n) (0x38 + (n) * 4)
+
+/* Target image format */
+#define S5P_CITRGFMT 0x48
+#define S5P_CITRGFMT_INROT90 (1 << 31)
+#define S5P_CITRGFMT_YCBCR420 (0 << 29)
+#define S5P_CITRGFMT_YCBCR422 (1 << 29)
+#define S5P_CITRGFMT_YCBCR422_1P (2 << 29)
+#define S5P_CITRGFMT_RGB (3 << 29)
+#define S5P_CITRGFMT_FMT_MASK (3 << 29)
+#define S5P_CITRGFMT_HSIZE_MASK (0xfff << 16)
+#define S5P_CITRGFMT_FLIP_SHIFT (14)
+#define S5P_CITRGFMT_FLIP_NORMAL (0 << 14)
+#define S5P_CITRGFMT_FLIP_X_MIRROR (1 << 14)
+#define S5P_CITRGFMT_FLIP_Y_MIRROR (2 << 14)
+#define S5P_CITRGFMT_FLIP_180 (3 << 14)
+#define S5P_CITRGFMT_FLIP_MASK (3 << 14)
+#define S5P_CITRGFMT_OUTROT90 (1 << 13)
+#define S5P_CITRGFMT_VSIZE_MASK (0xfff << 0)
+#define S5P_CITRGFMT_HSIZE(x) ((x) << 16)
+#define S5P_CITRGFMT_VSIZE(x) ((x) << 0)
+
+/* Output DMA control */
+#define S5P_CIOCTRL 0x4c
+#define S5P_CIOCTRL_ORDER422_MASK (3 << 0)
+#define S5P_CIOCTRL_ORDER422_CRYCBY (0 << 0)
+#define S5P_CIOCTRL_ORDER422_CBYCRY (1 << 0)
+#define S5P_CIOCTRL_ORDER422_YCRYCB (2 << 0)
+#define S5P_CIOCTRL_ORDER422_YCBYCR (3 << 0)
+#define S5P_CIOCTRL_LASTIRQ_ENABLE (1 << 2)
+#define S5P_CIOCTRL_YCBCR_3PLANE (0 << 3)
+#define S5P_CIOCTRL_YCBCR_2PLANE (1 << 3)
+#define S5P_CIOCTRL_YCBCR_PLANE_MASK (1 << 3)
+#define S5P_CIOCTRL_ALPHA_OUT_MASK (0xff << 4)
+#define S5P_CIOCTRL_RGB16FMT_MASK (3 << 16)
+#define S5P_CIOCTRL_RGB565 (0 << 16)
+#define S5P_CIOCTRL_ARGB1555 (1 << 16)
+#define S5P_CIOCTRL_ARGB4444 (2 << 16)
+#define S5P_CIOCTRL_ORDER2P_SHIFT (24)
+#define S5P_CIOCTRL_ORDER2P_MASK (3 << 24)
+#define S5P_CIOCTRL_ORDER422_2P_LSB_CRCB (0 << 24)
+
+/* Pre-scaler control 1 */
+#define S5P_CISCPRERATIO 0x50
+#define S5P_CISCPRERATIO_SHFACTOR(x) ((x) << 28)
+#define S5P_CISCPRERATIO_HOR(x) ((x) << 16)
+#define S5P_CISCPRERATIO_VER(x) ((x) << 0)
+
+#define S5P_CISCPREDST 0x54
+#define S5P_CISCPREDST_WIDTH(x) ((x) << 16)
+#define S5P_CISCPREDST_HEIGHT(x) ((x) << 0)
+
+/* Main scaler control */
+#define S5P_CISCCTRL 0x58
+#define S5P_CISCCTRL_SCALERBYPASS (1 << 31)
+#define S5P_CISCCTRL_SCALEUP_H (1 << 30)
+#define S5P_CISCCTRL_SCALEUP_V (1 << 29)
+#define S5P_CISCCTRL_CSCR2Y_WIDE (1 << 28)
+#define S5P_CISCCTRL_CSCY2R_WIDE (1 << 27)
+#define S5P_CISCCTRL_LCDPATHEN_FIFO (1 << 26)
+#define S5P_CISCCTRL_INTERLACE (1 << 25)
+#define S5P_CISCCTRL_SCALERSTART (1 << 15)
+#define S5P_CISCCTRL_INRGB_FMT_RGB565 (0 << 13)
+#define S5P_CISCCTRL_INRGB_FMT_RGB666 (1 << 13)
+#define S5P_CISCCTRL_INRGB_FMT_RGB888 (2 << 13)
+#define S5P_CISCCTRL_INRGB_FMT_MASK (3 << 13)
+#define S5P_CISCCTRL_OUTRGB_FMT_RGB565 (0 << 11)
+#define S5P_CISCCTRL_OUTRGB_FMT_RGB666 (1 << 11)
+#define S5P_CISCCTRL_OUTRGB_FMT_RGB888 (2 << 11)
+#define S5P_CISCCTRL_OUTRGB_FMT_MASK (3 << 11)
+#define S5P_CISCCTRL_RGB_EXT (1 << 10)
+#define S5P_CISCCTRL_ONE2ONE (1 << 9)
+#define S5P_CISCCTRL_MHRATIO(x) ((x) << 16)
+#define S5P_CISCCTRL_MVRATIO(x) ((x) << 0)
+#define S5P_CISCCTRL_MHRATIO_MASK (0x1ff << 16)
+#define S5P_CISCCTRL_MVRATIO_MASK (0x1ff << 0)
+#define S5P_CISCCTRL_MHRATIO_EXT(x) (((x) >> 6) << 16)
+#define S5P_CISCCTRL_MVRATIO_EXT(x) (((x) >> 6) << 0)
+
+/* Target area */
+#define S5P_CITAREA 0x5c
+#define S5P_CITAREA_MASK 0x0fffffff
+
+/* General status */
+#define S5P_CISTATUS 0x64
+#define S5P_CISTATUS_OVFIY (1 << 31)
+#define S5P_CISTATUS_OVFICB (1 << 30)
+#define S5P_CISTATUS_OVFICR (1 << 29)
+#define S5P_CISTATUS_VSYNC (1 << 28)
+#define S5P_CISTATUS_FRAMECNT_MASK (3 << 26)
+#define S5P_CISTATUS_FRAMECNT_SHIFT 26
+#define S5P_CISTATUS_WINOFF_EN (1 << 25)
+#define S5P_CISTATUS_IMGCPT_EN (1 << 22)
+#define S5P_CISTATUS_IMGCPT_SCEN (1 << 21)
+#define S5P_CISTATUS_VSYNC_A (1 << 20)
+#define S5P_CISTATUS_VSYNC_B (1 << 19)
+#define S5P_CISTATUS_OVRLB (1 << 18)
+#define S5P_CISTATUS_FRAME_END (1 << 17)
+#define S5P_CISTATUS_LASTCAPT_END (1 << 16)
+#define S5P_CISTATUS_VVALID_A (1 << 15)
+#define S5P_CISTATUS_VVALID_B (1 << 14)
+
+/* Indexes to the last and the currently processed buffer. */
+#define S5P_CISTATUS2 0x68
+
+/* Image capture control */
+#define S5P_CIIMGCPT 0xc0
+#define S5P_CIIMGCPT_IMGCPTEN (1 << 31)
+#define S5P_CIIMGCPT_IMGCPTEN_SC (1 << 30)
+#define S5P_CIIMGCPT_CPT_FREN_ENABLE (1 << 25)
+#define S5P_CIIMGCPT_CPT_FRMOD_CNT (1 << 18)
+
+/* Frame capture sequence */
+#define S5P_CICPTSEQ 0xc4
+
+/* Image effect */
+#define S5P_CIIMGEFF 0xd0
+#define S5P_CIIMGEFF_IE_ENABLE (1 << 30)
+#define S5P_CIIMGEFF_IE_SC_BEFORE (0 << 29)
+#define S5P_CIIMGEFF_IE_SC_AFTER (1 << 29)
+#define S5P_CIIMGEFF_FIN_BYPASS (0 << 26)
+#define S5P_CIIMGEFF_FIN_ARBITRARY (1 << 26)
+#define S5P_CIIMGEFF_FIN_NEGATIVE (2 << 26)
+#define S5P_CIIMGEFF_FIN_ARTFREEZE (3 << 26)
+#define S5P_CIIMGEFF_FIN_EMBOSSING (4 << 26)
+#define S5P_CIIMGEFF_FIN_SILHOUETTE (5 << 26)
+#define S5P_CIIMGEFF_FIN_MASK (7 << 26)
+#define S5P_CIIMGEFF_PAT_CBCR_MASK ((0xff < 13) | (0xff < 0))
+#define S5P_CIIMGEFF_PAT_CB(x) ((x) << 13)
+#define S5P_CIIMGEFF_PAT_CR(x) ((x) << 0)
+
+/* Input DMA Y/Cb/Cr plane start address 0/1 */
+#define S5P_CIIYSA(n) (0xd4 + (n) * 0x70)
+#define S5P_CIICBSA(n) (0xd8 + (n) * 0x70)
+#define S5P_CIICRSA(n) (0xdc + (n) * 0x70)
+
+/* Real input DMA image size */
+#define S5P_CIREAL_ISIZE 0xf8
+#define S5P_CIREAL_ISIZE_AUTOLOAD_EN (1 << 31)
+#define S5P_CIREAL_ISIZE_ADDR_CH_DIS (1 << 30)
+#define S5P_CIREAL_ISIZE_HEIGHT(x) ((x) << 16)
+#define S5P_CIREAL_ISIZE_WIDTH(x) ((x) << 0)
+
+
+/* Input DMA control */
+#define S5P_MSCTRL 0xfc
+#define S5P_MSCTRL_IN_BURST_COUNT_MASK (0xF << 24)
+#define S5P_MSCTRL_2P_IN_ORDER_MASK (3 << 16)
+#define S5P_MSCTRL_2P_IN_ORDER_SHIFT 16
+#define S5P_MSCTRL_C_INT_IN_3PLANE (0 << 15)
+#define S5P_MSCTRL_C_INT_IN_2PLANE (1 << 15)
+#define S5P_MSCTRL_C_INT_IN_MASK (1 << 15)
+#define S5P_MSCTRL_FLIP_SHIFT 13
+#define S5P_MSCTRL_FLIP_MASK (3 << 13)
+#define S5P_MSCTRL_FLIP_NORMAL (0 << 13)
+#define S5P_MSCTRL_FLIP_X_MIRROR (1 << 13)
+#define S5P_MSCTRL_FLIP_Y_MIRROR (2 << 13)
+#define S5P_MSCTRL_FLIP_180 (3 << 13)
+#define S5P_MSCTRL_FIFO_CTRL_FULL (1 << 12)
+#define S5P_MSCTRL_ORDER422_SHIFT 4
+#define S5P_MSCTRL_ORDER422_YCBYCR (0 << 4)
+#define S5P_MSCTRL_ORDER422_CBYCRY (1 << 4)
+#define S5P_MSCTRL_ORDER422_YCRYCB (2 << 4)
+#define S5P_MSCTRL_ORDER422_CRYCBY (3 << 4)
+#define S5P_MSCTRL_ORDER422_MASK (3 << 4)
+#define S5P_MSCTRL_INPUT_EXTCAM (0 << 3)
+#define S5P_MSCTRL_INPUT_MEMORY (1 << 3)
+#define S5P_MSCTRL_INPUT_MASK (1 << 3)
+#define S5P_MSCTRL_INFORMAT_YCBCR420 (0 << 1)
+#define S5P_MSCTRL_INFORMAT_YCBCR422 (1 << 1)
+#define S5P_MSCTRL_INFORMAT_YCBCR422_1P (2 << 1)
+#define S5P_MSCTRL_INFORMAT_RGB (3 << 1)
+#define S5P_MSCTRL_INFORMAT_MASK (3 << 1)
+#define S5P_MSCTRL_ENVID (1 << 0)
+#define S5P_MSCTRL_IN_BURST_COUNT(x) ((x) << 24)
+
+/* Output DMA Y/Cb/Cr offset */
+#define S5P_CIOYOFF 0x168
+#define S5P_CIOCBOFF 0x16c
+#define S5P_CIOCROFF 0x170
+
+/* Input DMA Y/Cb/Cr offset */
+#define S5P_CIIYOFF 0x174
+#define S5P_CIICBOFF 0x178
+#define S5P_CIICROFF 0x17c
+
+#define S5P_CIO_OFFS_VER(x) ((x) << 16)
+#define S5P_CIO_OFFS_HOR(x) ((x) << 0)
+
+/* Input DMA original image size */
+#define S5P_ORGISIZE 0x180
+
+/* Output DMA original image size */
+#define S5P_ORGOSIZE 0x184
+
+#define S5P_ORIG_SIZE_VER(x) ((x) << 16)
+#define S5P_ORIG_SIZE_HOR(x) ((x) << 0)
+
+/* Real output DMA image size (extension register) */
+#define S5P_CIEXTEN 0x188
+#define S5P_CIEXTEN_MHRATIO_EXT(x) (((x) & 0x3f) << 10)
+#define S5P_CIEXTEN_MVRATIO_EXT(x) ((x) & 0x3f)
+#define S5P_CIEXTEN_MHRATIO_EXT_MASK (0x3f << 10)
+#define S5P_CIEXTEN_MVRATIO_EXT_MASK 0x3f
+
+#define S5P_CIDMAPARAM 0x18c
+#define S5P_CIDMAPARAM_R_LINEAR (0 << 29)
+#define S5P_CIDMAPARAM_R_64X32 (3 << 29)
+#define S5P_CIDMAPARAM_W_LINEAR (0 << 13)
+#define S5P_CIDMAPARAM_W_64X32 (3 << 13)
+#define S5P_CIDMAPARAM_TILE_MASK ((3 << 29) | (3 << 13))
+
+/* MIPI CSI image format */
+#define S5P_CSIIMGFMT 0x194
+#define S5P_CSIIMGFMT_YCBCR422_8BIT 0x1e
+#define S5P_CSIIMGFMT_RAW8 0x2a
+#define S5P_CSIIMGFMT_RAW10 0x2b
+#define S5P_CSIIMGFMT_RAW12 0x2c
+/* User defined formats. x = 0...16. */
+#define S5P_CSIIMGFMT_USER(x) (0x30 + x - 1)
+
+/* Output frame buffer sequence mask */
+#define S5P_CIFCNTSEQ 0x1FC
+
+#endif /* REGS_FIMC_H_ */
diff --git a/drivers/media/video/s5p-g2d/Makefile b/drivers/media/video/s5p-g2d/Makefile
new file mode 100644
index 00000000..2c48c416
--- /dev/null
+++ b/drivers/media/video/s5p-g2d/Makefile
@@ -0,0 +1,3 @@
+s5p-g2d-objs := g2d.o g2d-hw.o
+
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D) += s5p-g2d.o
diff --git a/drivers/media/video/s5p-g2d/g2d-hw.c b/drivers/media/video/s5p-g2d/g2d-hw.c
new file mode 100644
index 00000000..5b86cbe4
--- /dev/null
+++ b/drivers/media/video/s5p-g2d/g2d-hw.c
@@ -0,0 +1,109 @@
+/*
+ * Samsung S5P G2D - 2D Graphics Accelerator Driver
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Kamil Debski, <k.debski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version
+ */
+
+#include <linux/io.h>
+
+#include "g2d.h"
+#include "g2d-regs.h"
+
+#define w(x, a) writel((x), d->regs + (a))
+#define r(a) readl(d->regs + (a))
+
+/* g2d_reset clears all g2d registers */
+void g2d_reset(struct g2d_dev *d)
+{
+ w(1, SOFT_RESET_REG);
+}
+
+void g2d_set_src_size(struct g2d_dev *d, struct g2d_frame *f)
+{
+ u32 n;
+
+ w(f->stride & 0xFFFF, SRC_STRIDE_REG);
+
+ n = f->o_height & 0xFFF;
+ n <<= 16;
+ n |= f->o_width & 0xFFF;
+ w(n, SRC_LEFT_TOP_REG);
+
+ n = f->bottom & 0xFFF;
+ n <<= 16;
+ n |= f->right & 0xFFF;
+ w(n, SRC_RIGHT_BOTTOM_REG);
+
+ w(f->fmt->hw, SRC_COLOR_MODE_REG);
+}
+
+void g2d_set_src_addr(struct g2d_dev *d, dma_addr_t a)
+{
+ w(a, SRC_BASE_ADDR_REG);
+}
+
+void g2d_set_dst_size(struct g2d_dev *d, struct g2d_frame *f)
+{
+ u32 n;
+
+ w(f->stride & 0xFFFF, DST_STRIDE_REG);
+
+ n = f->o_height & 0xFFF;
+ n <<= 16;
+ n |= f->o_width & 0xFFF;
+ w(n, DST_LEFT_TOP_REG);
+
+ n = f->bottom & 0xFFF;
+ n <<= 16;
+ n |= f->right & 0xFFF;
+ w(n, DST_RIGHT_BOTTOM_REG);
+
+ w(f->fmt->hw, DST_COLOR_MODE_REG);
+}
+
+void g2d_set_dst_addr(struct g2d_dev *d, dma_addr_t a)
+{
+ w(a, DST_BASE_ADDR_REG);
+}
+
+void g2d_set_rop4(struct g2d_dev *d, u32 r)
+{
+ w(r, ROP4_REG);
+}
+
+void g2d_set_flip(struct g2d_dev *d, u32 r)
+{
+ w(r, SRC_MSK_DIRECT_REG);
+}
+
+u32 g2d_cmd_stretch(u32 e)
+{
+ e &= 1;
+ return e << 4;
+}
+
+void g2d_set_cmd(struct g2d_dev *d, u32 c)
+{
+ w(c, BITBLT_COMMAND_REG);
+}
+
+void g2d_start(struct g2d_dev *d)
+{
+ /* Clear cache */
+ w(0x7, CACHECTL_REG);
+ /* Enable interrupt */
+ w(1, INTEN_REG);
+ /* Start G2D engine */
+ w(1, BITBLT_START_REG);
+}
+
+void g2d_clear_int(struct g2d_dev *d)
+{
+ w(1, INTC_PEND_REG);
+}
diff --git a/drivers/media/video/s5p-g2d/g2d-regs.h b/drivers/media/video/s5p-g2d/g2d-regs.h
new file mode 100644
index 00000000..02e1cf50
--- /dev/null
+++ b/drivers/media/video/s5p-g2d/g2d-regs.h
@@ -0,0 +1,115 @@
+/*
+ * Samsung S5P G2D - 2D Graphics Accelerator Driver
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Kamil Debski, <k.debski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version
+ */
+
+/* General Registers */
+#define SOFT_RESET_REG 0x0000 /* Software reset reg */
+#define INTEN_REG 0x0004 /* Interrupt Enable reg */
+#define INTC_PEND_REG 0x000C /* Interrupt Control Pending reg */
+#define FIFO_STAT_REG 0x0010 /* Command FIFO Status reg */
+#define AXI_ID_MODE_REG 0x0014 /* AXI Read ID Mode reg */
+#define CACHECTL_REG 0x0018 /* Cache & Buffer clear reg */
+#define AXI_MODE_REG 0x001C /* AXI Mode reg */
+
+/* Command Registers */
+#define BITBLT_START_REG 0x0100 /* BitBLT Start reg */
+#define BITBLT_COMMAND_REG 0x0104 /* Command reg for BitBLT */
+
+/* Parameter Setting Registers (Rotate & Direction) */
+#define ROTATE_REG 0x0200 /* Rotation reg */
+#define SRC_MSK_DIRECT_REG 0x0204 /* Src and Mask Direction reg */
+#define DST_PAT_DIRECT_REG 0x0208 /* Dest and Pattern Direction reg */
+
+/* Parameter Setting Registers (Src) */
+#define SRC_SELECT_REG 0x0300 /* Src Image Selection reg */
+#define SRC_BASE_ADDR_REG 0x0304 /* Src Image Base Address reg */
+#define SRC_STRIDE_REG 0x0308 /* Src Stride reg */
+#define SRC_COLOR_MODE_REG 0x030C /* Src Image Color Mode reg */
+#define SRC_LEFT_TOP_REG 0x0310 /* Src Left Top Coordinate reg */
+#define SRC_RIGHT_BOTTOM_REG 0x0314 /* Src Right Bottom Coordinate reg */
+
+/* Parameter Setting Registers (Dest) */
+#define DST_SELECT_REG 0x0400 /* Dest Image Selection reg */
+#define DST_BASE_ADDR_REG 0x0404 /* Dest Image Base Address reg */
+#define DST_STRIDE_REG 0x0408 /* Dest Stride reg */
+#define DST_COLOR_MODE_REG 0x040C /* Dest Image Color Mode reg */
+#define DST_LEFT_TOP_REG 0x0410 /* Dest Left Top Coordinate reg */
+#define DST_RIGHT_BOTTOM_REG 0x0414 /* Dest Right Bottom Coordinate reg */
+
+/* Parameter Setting Registers (Pattern) */
+#define PAT_BASE_ADDR_REG 0x0500 /* Pattern Image Base Address reg */
+#define PAT_SIZE_REG 0x0504 /* Pattern Image Size reg */
+#define PAT_COLOR_MODE_REG 0x0508 /* Pattern Image Color Mode reg */
+#define PAT_OFFSET_REG 0x050C /* Pattern Left Top Coordinate reg */
+#define PAT_STRIDE_REG 0x0510 /* Pattern Stride reg */
+
+/* Parameter Setting Registers (Mask) */
+#define MASK_BASE_ADDR_REG 0x0520 /* Mask Base Address reg */
+#define MASK_STRIDE_REG 0x0524 /* Mask Stride reg */
+
+/* Parameter Setting Registers (Clipping Window) */
+#define CW_LT_REG 0x0600 /* LeftTop coordinates of Clip Window */
+#define CW_RB_REG 0x0604 /* RightBottom coordinates of Clip
+ Window */
+
+/* Parameter Setting Registers (ROP & Alpha Setting) */
+#define THIRD_OPERAND_REG 0x0610 /* Third Operand Selection reg */
+#define ROP4_REG 0x0614 /* Raster Operation reg */
+#define ALPHA_REG 0x0618 /* Alpha value, Fading offset value */
+
+/* Parameter Setting Registers (Color) */
+#define FG_COLOR_REG 0x0700 /* Foreground Color reg */
+#define BG_COLOR_REG 0x0704 /* Background Color reg */
+#define BS_COLOR_REG 0x0708 /* Blue Screen Color reg */
+
+/* Parameter Setting Registers (Color Key) */
+#define SRC_COLORKEY_CTRL_REG 0x0710 /* Src Colorkey control reg */
+#define SRC_COLORKEY_DR_MIN_REG 0x0714 /* Src Colorkey Decision Reference
+ Min reg */
+#define SRC_COLORKEY_DR_MAX_REG 0x0718 /* Src Colorkey Decision Reference
+ Max reg */
+#define DST_COLORKEY_CTRL_REG 0x071C /* Dest Colorkey control reg */
+#define DST_COLORKEY_DR_MIN_REG 0x0720 /* Dest Colorkey Decision Reference
+ Min reg */
+#define DST_COLORKEY_DR_MAX_REG 0x0724 /* Dest Colorkey Decision Reference
+ Max reg */
+
+/* Color mode values */
+
+#define ORDER_XRGB 0
+#define ORDER_RGBX 1
+#define ORDER_XBGR 2
+#define ORDER_BGRX 3
+
+#define MODE_XRGB_8888 0
+#define MODE_ARGB_8888 1
+#define MODE_RGB_565 2
+#define MODE_XRGB_1555 3
+#define MODE_ARGB_1555 4
+#define MODE_XRGB_4444 5
+#define MODE_ARGB_4444 6
+#define MODE_PACKED_RGB_888 7
+
+#define COLOR_MODE(o, m) (((o) << 4) | (m))
+
+/* ROP4 operation values */
+#define ROP4_COPY 0xCCCC
+#define ROP4_INVERT 0x3333
+
+/* Hardware limits */
+#define MAX_WIDTH 8000
+#define MAX_HEIGHT 8000
+
+#define G2D_TIMEOUT 500
+
+#define DEFAULT_WIDTH 100
+#define DEFAULT_HEIGHT 100
+
diff --git a/drivers/media/video/s5p-g2d/g2d.c b/drivers/media/video/s5p-g2d/g2d.c
new file mode 100644
index 00000000..789de740
--- /dev/null
+++ b/drivers/media/video/s5p-g2d/g2d.c
@@ -0,0 +1,848 @@
+/*
+ * Samsung S5P G2D - 2D Graphics Accelerator Driver
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Kamil Debski, <k.debski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/version.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+
+#include <linux/platform_device.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "g2d.h"
+#include "g2d-regs.h"
+
+#define fh2ctx(__fh) container_of(__fh, struct g2d_ctx, fh)
+
+static struct g2d_fmt formats[] = {
+ {
+ .name = "XRGB_8888",
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .depth = 32,
+ .hw = COLOR_MODE(ORDER_XRGB, MODE_XRGB_8888),
+ },
+ {
+ .name = "RGB_565",
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+ .depth = 16,
+ .hw = COLOR_MODE(ORDER_XRGB, MODE_RGB_565),
+ },
+ {
+ .name = "XRGB_1555",
+ .fourcc = V4L2_PIX_FMT_RGB555X,
+ .depth = 16,
+ .hw = COLOR_MODE(ORDER_XRGB, MODE_XRGB_1555),
+ },
+ {
+ .name = "XRGB_4444",
+ .fourcc = V4L2_PIX_FMT_RGB444,
+ .depth = 16,
+ .hw = COLOR_MODE(ORDER_XRGB, MODE_XRGB_4444),
+ },
+ {
+ .name = "PACKED_RGB_888",
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .depth = 24,
+ .hw = COLOR_MODE(ORDER_XRGB, MODE_PACKED_RGB_888),
+ },
+};
+#define NUM_FORMATS ARRAY_SIZE(formats)
+
+struct g2d_frame def_frame = {
+ .width = DEFAULT_WIDTH,
+ .height = DEFAULT_HEIGHT,
+ .c_width = DEFAULT_WIDTH,
+ .c_height = DEFAULT_HEIGHT,
+ .o_width = 0,
+ .o_height = 0,
+ .fmt = &formats[0],
+ .right = DEFAULT_WIDTH,
+ .bottom = DEFAULT_HEIGHT,
+};
+
+struct g2d_fmt *find_fmt(struct v4l2_format *f)
+{
+ unsigned int i;
+ for (i = 0; i < NUM_FORMATS; i++) {
+ if (formats[i].fourcc == f->fmt.pix.pixelformat)
+ return &formats[i];
+ }
+ return NULL;
+}
+
+
+static struct g2d_frame *get_frame(struct g2d_ctx *ctx,
+ enum v4l2_buf_type type)
+{
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ return &ctx->in;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return &ctx->out;
+ default:
+ return ERR_PTR(-EINVAL);
+ }
+}
+
+static int g2d_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct g2d_ctx *ctx = vb2_get_drv_priv(vq);
+ struct g2d_frame *f = get_frame(ctx, vq->type);
+
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ sizes[0] = f->size;
+ *nplanes = 1;
+ alloc_ctxs[0] = ctx->dev->alloc_ctx;
+
+ if (*nbuffers == 0)
+ *nbuffers = 1;
+
+ return 0;
+}
+
+static int g2d_buf_prepare(struct vb2_buffer *vb)
+{
+ struct g2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct g2d_frame *f = get_frame(ctx, vb->vb2_queue->type);
+
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+ vb2_set_plane_payload(vb, 0, f->size);
+ return 0;
+}
+
+static void g2d_buf_queue(struct vb2_buffer *vb)
+{
+ struct g2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+}
+
+
+static struct vb2_ops g2d_qops = {
+ .queue_setup = g2d_queue_setup,
+ .buf_prepare = g2d_buf_prepare,
+ .buf_queue = g2d_buf_queue,
+};
+
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct g2d_ctx *ctx = priv;
+ int ret;
+
+ memset(src_vq, 0, sizeof(*src_vq));
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ src_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ src_vq->drv_priv = ctx;
+ src_vq->ops = &g2d_qops;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ memset(dst_vq, 0, sizeof(*dst_vq));
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ dst_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ dst_vq->drv_priv = ctx;
+ dst_vq->ops = &g2d_qops;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+
+ return vb2_queue_init(dst_vq);
+}
+
+static int g2d_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct g2d_ctx *ctx = container_of(ctrl->handler, struct g2d_ctx,
+ ctrl_handler);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctx->dev->ctrl_lock, flags);
+ switch (ctrl->id) {
+ case V4L2_CID_COLORFX:
+ if (ctrl->val == V4L2_COLORFX_NEGATIVE)
+ ctx->rop = ROP4_INVERT;
+ else
+ ctx->rop = ROP4_COPY;
+ break;
+
+ case V4L2_CID_HFLIP:
+ ctx->flip = ctx->ctrl_hflip->val | (ctx->ctrl_vflip->val << 1);
+ break;
+
+ }
+ spin_unlock_irqrestore(&ctx->dev->ctrl_lock, flags);
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops g2d_ctrl_ops = {
+ .s_ctrl = g2d_s_ctrl,
+};
+
+int g2d_setup_ctrls(struct g2d_ctx *ctx)
+{
+ struct g2d_dev *dev = ctx->dev;
+
+ v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3);
+
+ ctx->ctrl_hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &g2d_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+
+ ctx->ctrl_vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &g2d_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+ v4l2_ctrl_new_std_menu(
+ &ctx->ctrl_handler,
+ &g2d_ctrl_ops,
+ V4L2_CID_COLORFX,
+ V4L2_COLORFX_NEGATIVE,
+ ~((1 << V4L2_COLORFX_NONE) | (1 << V4L2_COLORFX_NEGATIVE)),
+ V4L2_COLORFX_NONE);
+
+ if (ctx->ctrl_handler.error) {
+ int err = ctx->ctrl_handler.error;
+ v4l2_err(&dev->v4l2_dev, "g2d_setup_ctrls failed\n");
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ return err;
+ }
+
+ v4l2_ctrl_cluster(2, &ctx->ctrl_hflip);
+
+ return 0;
+}
+
+static int g2d_open(struct file *file)
+{
+ struct g2d_dev *dev = video_drvdata(file);
+ struct g2d_ctx *ctx = NULL;
+ int ret = 0;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ ctx->dev = dev;
+ /* Set default formats */
+ ctx->in = def_frame;
+ ctx->out = def_frame;
+
+ ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
+ if (IS_ERR(ctx->m2m_ctx)) {
+ ret = PTR_ERR(ctx->m2m_ctx);
+ kfree(ctx);
+ return ret;
+ }
+ v4l2_fh_init(&ctx->fh, video_devdata(file));
+ file->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh);
+
+ g2d_setup_ctrls(ctx);
+
+ /* Write the default values to the ctx struct */
+ v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
+
+ ctx->fh.ctrl_handler = &ctx->ctrl_handler;
+
+ v4l2_info(&dev->v4l2_dev, "instance opened\n");
+ return 0;
+}
+
+static int g2d_release(struct file *file)
+{
+ struct g2d_dev *dev = video_drvdata(file);
+ struct g2d_ctx *ctx = fh2ctx(file->private_data);
+
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+ v4l2_info(&dev->v4l2_dev, "instance closed\n");
+ return 0;
+}
+
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strncpy(cap->driver, G2D_NAME, sizeof(cap->driver) - 1);
+ strncpy(cap->card, G2D_NAME, sizeof(cap->card) - 1);
+ cap->bus_info[0] = 0;
+ cap->version = KERNEL_VERSION(1, 0, 0);
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT
+ | V4L2_CAP_STREAMING;
+ return 0;
+}
+
+static int vidioc_enum_fmt(struct file *file, void *prv, struct v4l2_fmtdesc *f)
+{
+ struct g2d_fmt *fmt;
+ if (f->index >= NUM_FORMATS)
+ return -EINVAL;
+ fmt = &formats[f->index];
+ f->pixelformat = fmt->fourcc;
+ strncpy(f->description, fmt->name, sizeof(f->description) - 1);
+ return 0;
+}
+
+static int vidioc_g_fmt(struct file *file, void *prv, struct v4l2_format *f)
+{
+ struct g2d_ctx *ctx = prv;
+ struct vb2_queue *vq;
+ struct g2d_frame *frm;
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+ frm = get_frame(ctx, f->type);
+ if (IS_ERR(frm))
+ return PTR_ERR(frm);
+
+ f->fmt.pix.width = frm->width;
+ f->fmt.pix.height = frm->height;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.pixelformat = frm->fmt->fourcc;
+ f->fmt.pix.bytesperline = (frm->width * frm->fmt->depth) >> 3;
+ f->fmt.pix.sizeimage = frm->size;
+ return 0;
+}
+
+static int vidioc_try_fmt(struct file *file, void *prv, struct v4l2_format *f)
+{
+ struct g2d_fmt *fmt;
+ enum v4l2_field *field;
+
+ fmt = find_fmt(f);
+ if (!fmt)
+ return -EINVAL;
+
+ field = &f->fmt.pix.field;
+ if (*field == V4L2_FIELD_ANY)
+ *field = V4L2_FIELD_NONE;
+ else if (*field != V4L2_FIELD_NONE)
+ return -EINVAL;
+
+ if (f->fmt.pix.width > MAX_WIDTH)
+ f->fmt.pix.width = MAX_WIDTH;
+ if (f->fmt.pix.height > MAX_HEIGHT)
+ f->fmt.pix.height = MAX_HEIGHT;
+
+ if (f->fmt.pix.width < 1)
+ f->fmt.pix.width = 1;
+ if (f->fmt.pix.height < 1)
+ f->fmt.pix.height = 1;
+
+ f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+ return 0;
+}
+
+static int vidioc_s_fmt(struct file *file, void *prv, struct v4l2_format *f)
+{
+ struct g2d_ctx *ctx = prv;
+ struct g2d_dev *dev = ctx->dev;
+ struct vb2_queue *vq;
+ struct g2d_frame *frm;
+ struct g2d_fmt *fmt;
+ int ret = 0;
+
+ /* Adjust all values accordingly to the hardware capabilities
+ * and chosen format. */
+ ret = vidioc_try_fmt(file, prv, f);
+ if (ret)
+ return ret;
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ if (vb2_is_busy(vq)) {
+ v4l2_err(&dev->v4l2_dev, "queue (%d) bust\n", f->type);
+ return -EBUSY;
+ }
+ frm = get_frame(ctx, f->type);
+ if (IS_ERR(frm))
+ return PTR_ERR(frm);
+ fmt = find_fmt(f);
+ if (!fmt)
+ return -EINVAL;
+ frm->width = f->fmt.pix.width;
+ frm->height = f->fmt.pix.height;
+ frm->size = f->fmt.pix.sizeimage;
+ /* Reset crop settings */
+ frm->o_width = 0;
+ frm->o_height = 0;
+ frm->c_width = frm->width;
+ frm->c_height = frm->height;
+ frm->right = frm->width;
+ frm->bottom = frm->height;
+ frm->fmt = fmt;
+ frm->stride = f->fmt.pix.bytesperline;
+ return 0;
+}
+
+static unsigned int g2d_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct g2d_ctx *ctx = fh2ctx(file->private_data);
+ return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+}
+
+static int g2d_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct g2d_ctx *ctx = fh2ctx(file->private_data);
+ return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct g2d_ctx *ctx = priv;
+ return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct g2d_ctx *ctx = priv;
+ return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct g2d_ctx *ctx = priv;
+ return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct g2d_ctx *ctx = priv;
+ return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+}
+
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct g2d_ctx *ctx = priv;
+ return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
+}
+
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct g2d_ctx *ctx = priv;
+ return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
+}
+
+static int vidioc_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cr)
+{
+ struct g2d_ctx *ctx = priv;
+ struct g2d_frame *f;
+
+ f = get_frame(ctx, cr->type);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ cr->bounds.left = 0;
+ cr->bounds.top = 0;
+ cr->bounds.width = f->width;
+ cr->bounds.height = f->height;
+ cr->defrect = cr->bounds;
+ return 0;
+}
+
+static int vidioc_g_crop(struct file *file, void *prv, struct v4l2_crop *cr)
+{
+ struct g2d_ctx *ctx = prv;
+ struct g2d_frame *f;
+
+ f = get_frame(ctx, cr->type);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ cr->c.left = f->o_height;
+ cr->c.top = f->o_width;
+ cr->c.width = f->c_width;
+ cr->c.height = f->c_height;
+ return 0;
+}
+
+static int vidioc_try_crop(struct file *file, void *prv, struct v4l2_crop *cr)
+{
+ struct g2d_ctx *ctx = prv;
+ struct g2d_dev *dev = ctx->dev;
+ struct g2d_frame *f;
+
+ f = get_frame(ctx, cr->type);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ if (cr->c.top < 0 || cr->c.left < 0) {
+ v4l2_err(&dev->v4l2_dev,
+ "doesn't support negative values for top & left\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vidioc_s_crop(struct file *file, void *prv, struct v4l2_crop *cr)
+{
+ struct g2d_ctx *ctx = prv;
+ struct g2d_frame *f;
+ int ret;
+
+ ret = vidioc_try_crop(file, prv, cr);
+ if (ret)
+ return ret;
+ f = get_frame(ctx, cr->type);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ f->c_width = cr->c.width;
+ f->c_height = cr->c.height;
+ f->o_width = cr->c.left;
+ f->o_height = cr->c.top;
+ f->bottom = f->o_height + f->c_height;
+ f->right = f->o_width + f->c_width;
+ return 0;
+}
+
+static void g2d_lock(void *prv)
+{
+ struct g2d_ctx *ctx = prv;
+ struct g2d_dev *dev = ctx->dev;
+ mutex_lock(&dev->mutex);
+}
+
+static void g2d_unlock(void *prv)
+{
+ struct g2d_ctx *ctx = prv;
+ struct g2d_dev *dev = ctx->dev;
+ mutex_unlock(&dev->mutex);
+}
+
+static void job_abort(void *prv)
+{
+ struct g2d_ctx *ctx = prv;
+ struct g2d_dev *dev = ctx->dev;
+ int ret;
+
+ if (dev->curr == 0) /* No job currently running */
+ return;
+
+ ret = wait_event_timeout(dev->irq_queue,
+ dev->curr == 0,
+ msecs_to_jiffies(G2D_TIMEOUT));
+}
+
+static void device_run(void *prv)
+{
+ struct g2d_ctx *ctx = prv;
+ struct g2d_dev *dev = ctx->dev;
+ struct vb2_buffer *src, *dst;
+ unsigned long flags;
+ u32 cmd = 0;
+
+ dev->curr = ctx;
+
+ src = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+ dst = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+
+ clk_enable(dev->gate);
+ g2d_reset(dev);
+
+ spin_lock_irqsave(&dev->ctrl_lock, flags);
+
+ g2d_set_src_size(dev, &ctx->in);
+ g2d_set_src_addr(dev, vb2_dma_contig_plane_dma_addr(src, 0));
+
+ g2d_set_dst_size(dev, &ctx->out);
+ g2d_set_dst_addr(dev, vb2_dma_contig_plane_dma_addr(dst, 0));
+
+ g2d_set_rop4(dev, ctx->rop);
+ g2d_set_flip(dev, ctx->flip);
+
+ if (ctx->in.c_width != ctx->out.c_width ||
+ ctx->in.c_height != ctx->out.c_height)
+ cmd |= g2d_cmd_stretch(1);
+ g2d_set_cmd(dev, cmd);
+ g2d_start(dev);
+
+ spin_unlock_irqrestore(&dev->ctrl_lock, flags);
+}
+
+static irqreturn_t g2d_isr(int irq, void *prv)
+{
+ struct g2d_dev *dev = prv;
+ struct g2d_ctx *ctx = dev->curr;
+ struct vb2_buffer *src, *dst;
+
+ g2d_clear_int(dev);
+ clk_disable(dev->gate);
+
+ BUG_ON(ctx == 0);
+
+ src = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+ dst = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+
+ BUG_ON(src == 0);
+ BUG_ON(dst == 0);
+
+ v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE);
+ v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE);
+ v4l2_m2m_job_finish(dev->m2m_dev, ctx->m2m_ctx);
+
+ dev->curr = 0;
+ wake_up(&dev->irq_queue);
+ return IRQ_HANDLED;
+}
+
+static const struct v4l2_file_operations g2d_fops = {
+ .owner = THIS_MODULE,
+ .open = g2d_open,
+ .release = g2d_release,
+ .poll = g2d_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = g2d_mmap,
+};
+
+static const struct v4l2_ioctl_ops g2d_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt,
+
+ .vidioc_enum_fmt_vid_out = vidioc_enum_fmt,
+ .vidioc_g_fmt_vid_out = vidioc_g_fmt,
+ .vidioc_try_fmt_vid_out = vidioc_try_fmt,
+ .vidioc_s_fmt_vid_out = vidioc_s_fmt,
+
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+
+ .vidioc_g_crop = vidioc_g_crop,
+ .vidioc_s_crop = vidioc_s_crop,
+ .vidioc_cropcap = vidioc_cropcap,
+};
+
+static struct video_device g2d_videodev = {
+ .name = G2D_NAME,
+ .fops = &g2d_fops,
+ .ioctl_ops = &g2d_ioctl_ops,
+ .minor = -1,
+ .release = video_device_release,
+};
+
+static struct v4l2_m2m_ops g2d_m2m_ops = {
+ .device_run = device_run,
+ .job_abort = job_abort,
+ .lock = g2d_lock,
+ .unlock = g2d_unlock,
+};
+
+static int g2d_probe(struct platform_device *pdev)
+{
+ struct g2d_dev *dev;
+ struct video_device *vfd;
+ struct resource *res;
+ int ret = 0;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+ spin_lock_init(&dev->ctrl_lock);
+ mutex_init(&dev->mutex);
+ atomic_set(&dev->num_inst, 0);
+ init_waitqueue_head(&dev->irq_queue);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to find registers\n");
+ ret = -ENOENT;
+ goto free_dev;
+ }
+
+ dev->res_regs = request_mem_region(res->start, resource_size(res),
+ dev_name(&pdev->dev));
+
+ if (!dev->res_regs) {
+ dev_err(&pdev->dev, "failed to obtain register region\n");
+ ret = -ENOENT;
+ goto free_dev;
+ }
+
+ dev->regs = ioremap(res->start, resource_size(res));
+ if (!dev->regs) {
+ dev_err(&pdev->dev, "failed to map registers\n");
+ ret = -ENOENT;
+ goto rel_res_regs;
+ }
+
+ dev->clk = clk_get(&pdev->dev, "sclk_fimg2d");
+ if (IS_ERR_OR_NULL(dev->clk)) {
+ dev_err(&pdev->dev, "failed to get g2d clock\n");
+ ret = -ENXIO;
+ goto unmap_regs;
+ }
+
+ ret = clk_prepare(dev->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to prepare g2d clock\n");
+ goto put_clk;
+ }
+
+ dev->gate = clk_get(&pdev->dev, "fimg2d");
+ if (IS_ERR_OR_NULL(dev->gate)) {
+ dev_err(&pdev->dev, "failed to get g2d clock gate\n");
+ ret = -ENXIO;
+ goto unprep_clk;
+ }
+
+ ret = clk_prepare(dev->gate);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to prepare g2d clock gate\n");
+ goto put_clk_gate;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to find IRQ\n");
+ ret = -ENXIO;
+ goto unprep_clk_gate;
+ }
+
+ dev->irq = res->start;
+
+ ret = request_irq(dev->irq, g2d_isr, 0, pdev->name, dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to install IRQ\n");
+ goto put_clk_gate;
+ }
+
+ dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(dev->alloc_ctx)) {
+ ret = PTR_ERR(dev->alloc_ctx);
+ goto rel_irq;
+ }
+
+ ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+ if (ret)
+ goto alloc_ctx_cleanup;
+ vfd = video_device_alloc();
+ if (!vfd) {
+ v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
+ ret = -ENOMEM;
+ goto unreg_v4l2_dev;
+ }
+ *vfd = g2d_videodev;
+ vfd->lock = &dev->mutex;
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+ goto rel_vdev;
+ }
+ video_set_drvdata(vfd, dev);
+ snprintf(vfd->name, sizeof(vfd->name), "%s", g2d_videodev.name);
+ dev->vfd = vfd;
+ v4l2_info(&dev->v4l2_dev, "device registered as /dev/video%d\n",
+ vfd->num);
+ platform_set_drvdata(pdev, dev);
+ dev->m2m_dev = v4l2_m2m_init(&g2d_m2m_ops);
+ if (IS_ERR(dev->m2m_dev)) {
+ v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
+ ret = PTR_ERR(dev->m2m_dev);
+ goto unreg_video_dev;
+ }
+
+ def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3;
+
+ return 0;
+
+unreg_video_dev:
+ video_unregister_device(dev->vfd);
+rel_vdev:
+ video_device_release(vfd);
+unreg_v4l2_dev:
+ v4l2_device_unregister(&dev->v4l2_dev);
+alloc_ctx_cleanup:
+ vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
+rel_irq:
+ free_irq(dev->irq, dev);
+unprep_clk_gate:
+ clk_unprepare(dev->gate);
+put_clk_gate:
+ clk_put(dev->gate);
+unprep_clk:
+ clk_unprepare(dev->clk);
+put_clk:
+ clk_put(dev->clk);
+unmap_regs:
+ iounmap(dev->regs);
+rel_res_regs:
+ release_resource(dev->res_regs);
+free_dev:
+ kfree(dev);
+ return ret;
+}
+
+static int g2d_remove(struct platform_device *pdev)
+{
+ struct g2d_dev *dev = (struct g2d_dev *)platform_get_drvdata(pdev);
+
+ v4l2_info(&dev->v4l2_dev, "Removing " G2D_NAME);
+ v4l2_m2m_release(dev->m2m_dev);
+ video_unregister_device(dev->vfd);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
+ free_irq(dev->irq, dev);
+ clk_unprepare(dev->gate);
+ clk_put(dev->gate);
+ clk_unprepare(dev->clk);
+ clk_put(dev->clk);
+ iounmap(dev->regs);
+ release_resource(dev->res_regs);
+ kfree(dev);
+ return 0;
+}
+
+static struct platform_driver g2d_pdrv = {
+ .probe = g2d_probe,
+ .remove = g2d_remove,
+ .driver = {
+ .name = G2D_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(g2d_pdrv);
+
+MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
+MODULE_DESCRIPTION("S5P G2D 2d graphics accelerator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/s5p-g2d/g2d.h b/drivers/media/video/s5p-g2d/g2d.h
new file mode 100644
index 00000000..1b82065a
--- /dev/null
+++ b/drivers/media/video/s5p-g2d/g2d.h
@@ -0,0 +1,87 @@
+/*
+ * Samsung S5P G2D - 2D Graphics Accelerator Driver
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Kamil Debski, <k.debski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version
+ */
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+
+#define G2D_NAME "s5p-g2d"
+
+struct g2d_dev {
+ struct v4l2_device v4l2_dev;
+ struct v4l2_m2m_dev *m2m_dev;
+ struct video_device *vfd;
+ struct mutex mutex;
+ spinlock_t ctrl_lock;
+ atomic_t num_inst;
+ struct vb2_alloc_ctx *alloc_ctx;
+ struct resource *res_regs;
+ void __iomem *regs;
+ struct clk *clk;
+ struct clk *gate;
+ struct g2d_ctx *curr;
+ int irq;
+ wait_queue_head_t irq_queue;
+};
+
+struct g2d_frame {
+ /* Original dimensions */
+ u32 width;
+ u32 height;
+ /* Crop size */
+ u32 c_width;
+ u32 c_height;
+ /* Offset */
+ u32 o_width;
+ u32 o_height;
+ /* Image format */
+ struct g2d_fmt *fmt;
+ /* Variables that can calculated once and reused */
+ u32 stride;
+ u32 bottom;
+ u32 right;
+ u32 size;
+};
+
+struct g2d_ctx {
+ struct v4l2_fh fh;
+ struct g2d_dev *dev;
+ struct v4l2_m2m_ctx *m2m_ctx;
+ struct g2d_frame in;
+ struct g2d_frame out;
+ struct v4l2_ctrl *ctrl_hflip;
+ struct v4l2_ctrl *ctrl_vflip;
+ struct v4l2_ctrl_handler ctrl_handler;
+ u32 rop;
+ u32 flip;
+};
+
+struct g2d_fmt {
+ char *name;
+ u32 fourcc;
+ int depth;
+ u32 hw;
+};
+
+
+void g2d_reset(struct g2d_dev *d);
+void g2d_set_src_size(struct g2d_dev *d, struct g2d_frame *f);
+void g2d_set_src_addr(struct g2d_dev *d, dma_addr_t a);
+void g2d_set_dst_size(struct g2d_dev *d, struct g2d_frame *f);
+void g2d_set_dst_addr(struct g2d_dev *d, dma_addr_t a);
+void g2d_start(struct g2d_dev *d);
+void g2d_clear_int(struct g2d_dev *d);
+void g2d_set_rop4(struct g2d_dev *d, u32 r);
+void g2d_set_flip(struct g2d_dev *d, u32 r);
+u32 g2d_cmd_stretch(u32 e);
+void g2d_set_cmd(struct g2d_dev *d, u32 c);
+
+
diff --git a/drivers/media/video/s5p-jpeg/Makefile b/drivers/media/video/s5p-jpeg/Makefile
new file mode 100644
index 00000000..ddc2900d
--- /dev/null
+++ b/drivers/media/video/s5p-jpeg/Makefile
@@ -0,0 +1,2 @@
+s5p-jpeg-objs := jpeg-core.o
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) := s5p-jpeg.o
diff --git a/drivers/media/video/s5p-jpeg/jpeg-core.c b/drivers/media/video/s5p-jpeg/jpeg-core.c
new file mode 100644
index 00000000..5a49c307
--- /dev/null
+++ b/drivers/media/video/s5p-jpeg/jpeg-core.c
@@ -0,0 +1,1563 @@
+/* linux/drivers/media/video/s5p-jpeg/jpeg-core.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/gfp.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "jpeg-core.h"
+#include "jpeg-hw.h"
+
+static struct s5p_jpeg_fmt formats_enc[] = {
+ {
+ .name = "JPEG JFIF",
+ .fourcc = V4L2_PIX_FMT_JPEG,
+ .colplanes = 1,
+ .types = MEM2MEM_CAPTURE,
+ },
+ {
+ .name = "YUV 4:2:2 packed, YCbYCr",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .colplanes = 1,
+ .types = MEM2MEM_OUTPUT,
+ },
+ {
+ .name = "RGB565",
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .depth = 16,
+ .colplanes = 1,
+ .types = MEM2MEM_OUTPUT,
+ },
+};
+#define NUM_FORMATS_ENC ARRAY_SIZE(formats_enc)
+
+static struct s5p_jpeg_fmt formats_dec[] = {
+ {
+ .name = "YUV 4:2:0 planar, YCbCr",
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .depth = 12,
+ .colplanes = 3,
+ .h_align = 4,
+ .v_align = 4,
+ .types = MEM2MEM_CAPTURE,
+ },
+ {
+ .name = "YUV 4:2:2 packed, YCbYCr",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .colplanes = 1,
+ .h_align = 4,
+ .v_align = 3,
+ .types = MEM2MEM_CAPTURE,
+ },
+ {
+ .name = "JPEG JFIF",
+ .fourcc = V4L2_PIX_FMT_JPEG,
+ .colplanes = 1,
+ .types = MEM2MEM_OUTPUT,
+ },
+};
+#define NUM_FORMATS_DEC ARRAY_SIZE(formats_dec)
+
+static const unsigned char qtbl_luminance[4][64] = {
+ {/* level 1 - high quality */
+ 8, 6, 6, 8, 12, 14, 16, 17,
+ 6, 6, 6, 8, 10, 13, 12, 15,
+ 6, 6, 7, 8, 13, 14, 18, 24,
+ 8, 8, 8, 14, 13, 19, 24, 35,
+ 12, 10, 13, 13, 20, 26, 34, 39,
+ 14, 13, 14, 19, 26, 34, 39, 39,
+ 16, 12, 18, 24, 34, 39, 39, 39,
+ 17, 15, 24, 35, 39, 39, 39, 39
+ },
+ {/* level 2 */
+ 12, 8, 8, 12, 17, 21, 24, 23,
+ 8, 9, 9, 11, 15, 19, 18, 23,
+ 8, 9, 10, 12, 19, 20, 27, 36,
+ 12, 11, 12, 21, 20, 28, 36, 53,
+ 17, 15, 19, 20, 30, 39, 51, 59,
+ 21, 19, 20, 28, 39, 51, 59, 59,
+ 24, 18, 27, 36, 51, 59, 59, 59,
+ 23, 23, 36, 53, 59, 59, 59, 59
+ },
+ {/* level 3 */
+ 16, 11, 11, 16, 23, 27, 31, 30,
+ 11, 12, 12, 15, 20, 23, 23, 30,
+ 11, 12, 13, 16, 23, 26, 35, 47,
+ 16, 15, 16, 23, 26, 37, 47, 64,
+ 23, 20, 23, 26, 39, 51, 64, 64,
+ 27, 23, 26, 37, 51, 64, 64, 64,
+ 31, 23, 35, 47, 64, 64, 64, 64,
+ 30, 30, 47, 64, 64, 64, 64, 64
+ },
+ {/*level 4 - low quality */
+ 20, 16, 25, 39, 50, 46, 62, 68,
+ 16, 18, 23, 38, 38, 53, 65, 68,
+ 25, 23, 31, 38, 53, 65, 68, 68,
+ 39, 38, 38, 53, 65, 68, 68, 68,
+ 50, 38, 53, 65, 68, 68, 68, 68,
+ 46, 53, 65, 68, 68, 68, 68, 68,
+ 62, 65, 68, 68, 68, 68, 68, 68,
+ 68, 68, 68, 68, 68, 68, 68, 68
+ }
+};
+
+static const unsigned char qtbl_chrominance[4][64] = {
+ {/* level 1 - high quality */
+ 9, 8, 9, 11, 14, 17, 19, 24,
+ 8, 10, 9, 11, 14, 13, 17, 22,
+ 9, 9, 13, 14, 13, 15, 23, 26,
+ 11, 11, 14, 14, 15, 20, 26, 33,
+ 14, 14, 13, 15, 20, 24, 33, 39,
+ 17, 13, 15, 20, 24, 32, 39, 39,
+ 19, 17, 23, 26, 33, 39, 39, 39,
+ 24, 22, 26, 33, 39, 39, 39, 39
+ },
+ {/* level 2 */
+ 13, 11, 13, 16, 20, 20, 29, 37,
+ 11, 14, 14, 14, 16, 20, 26, 32,
+ 13, 14, 15, 17, 20, 23, 35, 40,
+ 16, 14, 17, 21, 23, 30, 40, 50,
+ 20, 16, 20, 23, 30, 37, 50, 59,
+ 20, 20, 23, 30, 37, 48, 59, 59,
+ 29, 26, 35, 40, 50, 59, 59, 59,
+ 37, 32, 40, 50, 59, 59, 59, 59
+ },
+ {/* level 3 */
+ 17, 15, 17, 21, 20, 26, 38, 48,
+ 15, 19, 18, 17, 20, 26, 35, 43,
+ 17, 18, 20, 22, 26, 30, 46, 53,
+ 21, 17, 22, 28, 30, 39, 53, 64,
+ 20, 20, 26, 30, 39, 48, 64, 64,
+ 26, 26, 30, 39, 48, 63, 64, 64,
+ 38, 35, 46, 53, 64, 64, 64, 64,
+ 48, 43, 53, 64, 64, 64, 64, 64
+ },
+ {/*level 4 - low quality */
+ 21, 25, 32, 38, 54, 68, 68, 68,
+ 25, 28, 24, 38, 54, 68, 68, 68,
+ 32, 24, 32, 43, 66, 68, 68, 68,
+ 38, 38, 43, 53, 68, 68, 68, 68,
+ 54, 54, 66, 68, 68, 68, 68, 68,
+ 68, 68, 68, 68, 68, 68, 68, 68,
+ 68, 68, 68, 68, 68, 68, 68, 68,
+ 68, 68, 68, 68, 68, 68, 68, 68
+ }
+};
+
+static const unsigned char hdctbl0[16] = {
+ 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const unsigned char hdctblg0[12] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb
+};
+static const unsigned char hactbl0[16] = {
+ 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d
+};
+static const unsigned char hactblg0[162] = {
+ 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
+ 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
+ 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
+ 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
+ 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
+ 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
+ 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
+ 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
+ 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
+ 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+ 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
+ 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
+ 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
+ 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
+ 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
+ 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
+ 0xf9, 0xfa
+};
+
+static inline struct s5p_jpeg_ctx *ctrl_to_ctx(struct v4l2_ctrl *c)
+{
+ return container_of(c->handler, struct s5p_jpeg_ctx, ctrl_handler);
+}
+
+static inline struct s5p_jpeg_ctx *fh_to_ctx(struct v4l2_fh *fh)
+{
+ return container_of(fh, struct s5p_jpeg_ctx, fh);
+}
+
+static inline void jpeg_set_qtbl(void __iomem *regs, const unsigned char *qtbl,
+ unsigned long tab, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ writel((unsigned int)qtbl[i], regs + tab + (i * 0x04));
+}
+
+static inline void jpeg_set_qtbl_lum(void __iomem *regs, int quality)
+{
+ /* this driver fills quantisation table 0 with data for luma */
+ jpeg_set_qtbl(regs, qtbl_luminance[quality], S5P_JPG_QTBL_CONTENT(0),
+ ARRAY_SIZE(qtbl_luminance[quality]));
+}
+
+static inline void jpeg_set_qtbl_chr(void __iomem *regs, int quality)
+{
+ /* this driver fills quantisation table 1 with data for chroma */
+ jpeg_set_qtbl(regs, qtbl_chrominance[quality], S5P_JPG_QTBL_CONTENT(1),
+ ARRAY_SIZE(qtbl_chrominance[quality]));
+}
+
+static inline void jpeg_set_htbl(void __iomem *regs, const unsigned char *htbl,
+ unsigned long tab, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ writel((unsigned int)htbl[i], regs + tab + (i * 0x04));
+}
+
+static inline void jpeg_set_hdctbl(void __iomem *regs)
+{
+ /* this driver fills table 0 for this component */
+ jpeg_set_htbl(regs, hdctbl0, S5P_JPG_HDCTBL(0), ARRAY_SIZE(hdctbl0));
+}
+
+static inline void jpeg_set_hdctblg(void __iomem *regs)
+{
+ /* this driver fills table 0 for this component */
+ jpeg_set_htbl(regs, hdctblg0, S5P_JPG_HDCTBLG(0), ARRAY_SIZE(hdctblg0));
+}
+
+static inline void jpeg_set_hactbl(void __iomem *regs)
+{
+ /* this driver fills table 0 for this component */
+ jpeg_set_htbl(regs, hactbl0, S5P_JPG_HACTBL(0), ARRAY_SIZE(hactbl0));
+}
+
+static inline void jpeg_set_hactblg(void __iomem *regs)
+{
+ /* this driver fills table 0 for this component */
+ jpeg_set_htbl(regs, hactblg0, S5P_JPG_HACTBLG(0), ARRAY_SIZE(hactblg0));
+}
+
+/*
+ * ============================================================================
+ * Device file operations
+ * ============================================================================
+ */
+
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq);
+static struct s5p_jpeg_fmt *s5p_jpeg_find_format(unsigned int mode,
+ __u32 pixelformat);
+static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx);
+
+static int s5p_jpeg_open(struct file *file)
+{
+ struct s5p_jpeg *jpeg = video_drvdata(file);
+ struct video_device *vfd = video_devdata(file);
+ struct s5p_jpeg_ctx *ctx;
+ struct s5p_jpeg_fmt *out_fmt;
+ int ret = 0;
+
+ ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ v4l2_fh_init(&ctx->fh, vfd);
+ /* Use separate control handler per file handle */
+ ctx->fh.ctrl_handler = &ctx->ctrl_handler;
+ file->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh);
+
+ ctx->jpeg = jpeg;
+ if (vfd == jpeg->vfd_encoder) {
+ ctx->mode = S5P_JPEG_ENCODE;
+ out_fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_RGB565);
+ } else {
+ ctx->mode = S5P_JPEG_DECODE;
+ out_fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_JPEG);
+ }
+
+ ret = s5p_jpeg_controls_create(ctx);
+ if (ret < 0)
+ goto error;
+
+ ctx->m2m_ctx = v4l2_m2m_ctx_init(jpeg->m2m_dev, ctx, queue_init);
+ if (IS_ERR(ctx->m2m_ctx)) {
+ ret = PTR_ERR(ctx->m2m_ctx);
+ goto error;
+ }
+
+ ctx->out_q.fmt = out_fmt;
+ ctx->cap_q.fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_YUYV);
+ return 0;
+
+error:
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+ return ret;
+}
+
+static int s5p_jpeg_release(struct file *file)
+{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data);
+
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+
+ return 0;
+}
+
+static unsigned int s5p_jpeg_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data);
+
+ return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+}
+
+static int s5p_jpeg_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data);
+
+ return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
+}
+
+static const struct v4l2_file_operations s5p_jpeg_fops = {
+ .owner = THIS_MODULE,
+ .open = s5p_jpeg_open,
+ .release = s5p_jpeg_release,
+ .poll = s5p_jpeg_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = s5p_jpeg_mmap,
+};
+
+/*
+ * ============================================================================
+ * video ioctl operations
+ * ============================================================================
+ */
+
+static int get_byte(struct s5p_jpeg_buffer *buf)
+{
+ if (buf->curr >= buf->size)
+ return -1;
+
+ return ((unsigned char *)buf->data)[buf->curr++];
+}
+
+static int get_word_be(struct s5p_jpeg_buffer *buf, unsigned int *word)
+{
+ unsigned int temp;
+ int byte;
+
+ byte = get_byte(buf);
+ if (byte == -1)
+ return -1;
+ temp = byte << 8;
+ byte = get_byte(buf);
+ if (byte == -1)
+ return -1;
+ *word = (unsigned int)byte | temp;
+ return 0;
+}
+
+static void skip(struct s5p_jpeg_buffer *buf, long len)
+{
+ if (len <= 0)
+ return;
+
+ while (len--)
+ get_byte(buf);
+}
+
+static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result,
+ unsigned long buffer, unsigned long size)
+{
+ int c, components, notfound;
+ unsigned int height, width, word;
+ long length;
+ struct s5p_jpeg_buffer jpeg_buffer;
+
+ jpeg_buffer.size = size;
+ jpeg_buffer.data = buffer;
+ jpeg_buffer.curr = 0;
+
+ notfound = 1;
+ while (notfound) {
+ c = get_byte(&jpeg_buffer);
+ if (c == -1)
+ break;
+ if (c != 0xff)
+ continue;
+ do
+ c = get_byte(&jpeg_buffer);
+ while (c == 0xff);
+ if (c == -1)
+ break;
+ if (c == 0)
+ continue;
+ length = 0;
+ switch (c) {
+ /* SOF0: baseline JPEG */
+ case SOF0:
+ if (get_word_be(&jpeg_buffer, &word))
+ break;
+ if (get_byte(&jpeg_buffer) == -1)
+ break;
+ if (get_word_be(&jpeg_buffer, &height))
+ break;
+ if (get_word_be(&jpeg_buffer, &width))
+ break;
+ components = get_byte(&jpeg_buffer);
+ if (components == -1)
+ break;
+ notfound = 0;
+
+ skip(&jpeg_buffer, components * 3);
+ break;
+
+ /* skip payload-less markers */
+ case RST ... RST + 7:
+ case SOI:
+ case EOI:
+ case TEM:
+ break;
+
+ /* skip uninteresting payload markers */
+ default:
+ if (get_word_be(&jpeg_buffer, &word))
+ break;
+ length = (long)word - 2;
+ skip(&jpeg_buffer, length);
+ break;
+ }
+ }
+ result->w = width;
+ result->h = height;
+ result->size = components;
+ return !notfound;
+}
+
+static int s5p_jpeg_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
+
+ if (ctx->mode == S5P_JPEG_ENCODE) {
+ strlcpy(cap->driver, S5P_JPEG_M2M_NAME " encoder",
+ sizeof(cap->driver));
+ strlcpy(cap->card, S5P_JPEG_M2M_NAME " encoder",
+ sizeof(cap->card));
+ } else {
+ strlcpy(cap->driver, S5P_JPEG_M2M_NAME " decoder",
+ sizeof(cap->driver));
+ strlcpy(cap->card, S5P_JPEG_M2M_NAME " decoder",
+ sizeof(cap->card));
+ }
+ cap->bus_info[0] = 0;
+ cap->capabilities = V4L2_CAP_STREAMING |
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VIDEO_OUTPUT;
+ return 0;
+}
+
+static int enum_fmt(struct s5p_jpeg_fmt *formats, int n,
+ struct v4l2_fmtdesc *f, u32 type)
+{
+ int i, num = 0;
+
+ for (i = 0; i < n; ++i) {
+ if (formats[i].types & type) {
+ /* index-th format of type type found ? */
+ if (num == f->index)
+ break;
+ /* Correct type but haven't reached our index yet,
+ * just increment per-type index */
+ ++num;
+ }
+ }
+
+ /* Format not found */
+ if (i >= n)
+ return -EINVAL;
+
+ strlcpy(f->description, formats[i].name, sizeof(f->description));
+ f->pixelformat = formats[i].fourcc;
+
+ return 0;
+}
+
+static int s5p_jpeg_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
+
+ if (ctx->mode == S5P_JPEG_ENCODE)
+ return enum_fmt(formats_enc, NUM_FORMATS_ENC, f,
+ MEM2MEM_CAPTURE);
+
+ return enum_fmt(formats_dec, NUM_FORMATS_DEC, f, MEM2MEM_CAPTURE);
+}
+
+static int s5p_jpeg_enum_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
+
+ if (ctx->mode == S5P_JPEG_ENCODE)
+ return enum_fmt(formats_enc, NUM_FORMATS_ENC, f,
+ MEM2MEM_OUTPUT);
+
+ return enum_fmt(formats_dec, NUM_FORMATS_DEC, f, MEM2MEM_OUTPUT);
+}
+
+static struct s5p_jpeg_q_data *get_q_data(struct s5p_jpeg_ctx *ctx,
+ enum v4l2_buf_type type)
+{
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return &ctx->out_q;
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return &ctx->cap_q;
+
+ return NULL;
+}
+
+static int s5p_jpeg_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct vb2_queue *vq;
+ struct s5p_jpeg_q_data *q_data = NULL;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct s5p_jpeg_ctx *ct = fh_to_ctx(priv);
+
+ vq = v4l2_m2m_get_vq(ct->m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ ct->mode == S5P_JPEG_DECODE && !ct->hdr_parsed)
+ return -EINVAL;
+ q_data = get_q_data(ct, f->type);
+ BUG_ON(q_data == NULL);
+
+ pix->width = q_data->w;
+ pix->height = q_data->h;
+ pix->field = V4L2_FIELD_NONE;
+ pix->pixelformat = q_data->fmt->fourcc;
+ pix->bytesperline = 0;
+ if (q_data->fmt->fourcc != V4L2_PIX_FMT_JPEG) {
+ u32 bpl = q_data->w;
+ if (q_data->fmt->colplanes == 1)
+ bpl = (bpl * q_data->fmt->depth) >> 3;
+ pix->bytesperline = bpl;
+ }
+ pix->sizeimage = q_data->size;
+
+ return 0;
+}
+
+static struct s5p_jpeg_fmt *s5p_jpeg_find_format(unsigned int mode,
+ u32 pixelformat)
+{
+ unsigned int k;
+ struct s5p_jpeg_fmt *formats;
+ int n;
+
+ if (mode == S5P_JPEG_ENCODE) {
+ formats = formats_enc;
+ n = NUM_FORMATS_ENC;
+ } else {
+ formats = formats_dec;
+ n = NUM_FORMATS_DEC;
+ }
+
+ for (k = 0; k < n; k++) {
+ struct s5p_jpeg_fmt *fmt = &formats[k];
+ if (fmt->fourcc == pixelformat)
+ return fmt;
+ }
+
+ return NULL;
+
+}
+
+static void jpeg_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax,
+ unsigned int walign,
+ u32 *h, unsigned int hmin, unsigned int hmax,
+ unsigned int halign)
+{
+ int width, height, w_step, h_step;
+
+ width = *w;
+ height = *h;
+
+ w_step = 1 << walign;
+ h_step = 1 << halign;
+ v4l_bound_align_image(w, wmin, wmax, walign, h, hmin, hmax, halign, 0);
+
+ if (*w < width && (*w + w_step) < wmax)
+ *w += w_step;
+ if (*h < height && (*h + h_step) < hmax)
+ *h += h_step;
+
+}
+
+static int vidioc_try_fmt(struct v4l2_format *f, struct s5p_jpeg_fmt *fmt,
+ struct s5p_jpeg_ctx *ctx, int q_type)
+{
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ if (pix->field == V4L2_FIELD_ANY)
+ pix->field = V4L2_FIELD_NONE;
+ else if (pix->field != V4L2_FIELD_NONE)
+ return -EINVAL;
+
+ /* V4L2 specification suggests the driver corrects the format struct
+ * if any of the dimensions is unsupported */
+ if (q_type == MEM2MEM_OUTPUT)
+ jpeg_bound_align_image(&pix->width, S5P_JPEG_MIN_WIDTH,
+ S5P_JPEG_MAX_WIDTH, 0,
+ &pix->height, S5P_JPEG_MIN_HEIGHT,
+ S5P_JPEG_MAX_HEIGHT, 0);
+ else
+ jpeg_bound_align_image(&pix->width, S5P_JPEG_MIN_WIDTH,
+ S5P_JPEG_MAX_WIDTH, fmt->h_align,
+ &pix->height, S5P_JPEG_MIN_HEIGHT,
+ S5P_JPEG_MAX_HEIGHT, fmt->v_align);
+
+ if (fmt->fourcc == V4L2_PIX_FMT_JPEG) {
+ if (pix->sizeimage <= 0)
+ pix->sizeimage = PAGE_SIZE;
+ pix->bytesperline = 0;
+ } else {
+ u32 bpl = pix->bytesperline;
+
+ if (fmt->colplanes > 1 && bpl < pix->width)
+ bpl = pix->width; /* planar */
+
+ if (fmt->colplanes == 1 && /* packed */
+ (bpl << 3) * fmt->depth < pix->width)
+ bpl = (pix->width * fmt->depth) >> 3;
+
+ pix->bytesperline = bpl;
+ pix->sizeimage = (pix->width * pix->height * fmt->depth) >> 3;
+ }
+
+ return 0;
+}
+
+static int s5p_jpeg_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
+ struct s5p_jpeg_fmt *fmt;
+
+ fmt = s5p_jpeg_find_format(ctx->mode, f->fmt.pix.pixelformat);
+ if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) {
+ v4l2_err(&ctx->jpeg->v4l2_dev,
+ "Fourcc format (0x%08x) invalid.\n",
+ f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ return vidioc_try_fmt(f, fmt, ctx, MEM2MEM_CAPTURE);
+}
+
+static int s5p_jpeg_try_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
+ struct s5p_jpeg_fmt *fmt;
+
+ fmt = s5p_jpeg_find_format(ctx->mode, f->fmt.pix.pixelformat);
+ if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) {
+ v4l2_err(&ctx->jpeg->v4l2_dev,
+ "Fourcc format (0x%08x) invalid.\n",
+ f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ return vidioc_try_fmt(f, fmt, ctx, MEM2MEM_OUTPUT);
+}
+
+static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f)
+{
+ struct vb2_queue *vq;
+ struct s5p_jpeg_q_data *q_data = NULL;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ vq = v4l2_m2m_get_vq(ct->m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ q_data = get_q_data(ct, f->type);
+ BUG_ON(q_data == NULL);
+
+ if (vb2_is_busy(vq)) {
+ v4l2_err(&ct->jpeg->v4l2_dev, "%s queue busy\n", __func__);
+ return -EBUSY;
+ }
+
+ q_data->fmt = s5p_jpeg_find_format(ct->mode, pix->pixelformat);
+ q_data->w = pix->width;
+ q_data->h = pix->height;
+ if (q_data->fmt->fourcc != V4L2_PIX_FMT_JPEG)
+ q_data->size = q_data->w * q_data->h * q_data->fmt->depth >> 3;
+ else
+ q_data->size = pix->sizeimage;
+
+ return 0;
+}
+
+static int s5p_jpeg_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ int ret;
+
+ ret = s5p_jpeg_try_fmt_vid_cap(file, priv, f);
+ if (ret)
+ return ret;
+
+ return s5p_jpeg_s_fmt(fh_to_ctx(priv), f);
+}
+
+static int s5p_jpeg_s_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ int ret;
+
+ ret = s5p_jpeg_try_fmt_vid_out(file, priv, f);
+ if (ret)
+ return ret;
+
+ return s5p_jpeg_s_fmt(fh_to_ctx(priv), f);
+}
+
+static int s5p_jpeg_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
+
+ return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
+}
+
+static int s5p_jpeg_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
+
+ return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
+}
+
+static int s5p_jpeg_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
+
+ return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int s5p_jpeg_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
+
+ return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int s5p_jpeg_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
+
+ return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
+}
+
+static int s5p_jpeg_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
+
+ return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
+}
+
+int s5p_jpeg_g_selection(struct file *file, void *priv,
+ struct v4l2_selection *s)
+{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ /* For JPEG blob active == default == bounds */
+ switch (s->target) {
+ case V4L2_SEL_TGT_CROP_ACTIVE:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE_ACTIVE:
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ s->r.width = ctx->out_q.w;
+ s->r.height = ctx->out_q.h;
+ break;
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ case V4L2_SEL_TGT_COMPOSE_PADDED:
+ s->r.width = ctx->cap_q.w;
+ s->r.height = ctx->cap_q.h;
+ break;
+ default:
+ return -EINVAL;
+ }
+ s->r.left = 0;
+ s->r.top = 0;
+ return 0;
+}
+
+/*
+ * V4L2 controls
+ */
+
+static int s5p_jpeg_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl);
+ struct s5p_jpeg *jpeg = ctx->jpeg;
+ unsigned long flags;
+
+ switch (ctrl->id) {
+ case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
+ spin_lock_irqsave(&jpeg->slock, flags);
+
+ WARN_ON(ctx->subsampling > S5P_SUBSAMPLING_MODE_GRAY);
+ if (ctx->subsampling > 2)
+ ctrl->val = V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY;
+ else
+ ctrl->val = ctx->subsampling;
+ spin_unlock_irqrestore(&jpeg->slock, flags);
+ break;
+ }
+
+ return 0;
+}
+
+static int s5p_jpeg_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctx->jpeg->slock, flags);
+
+ switch (ctrl->id) {
+ case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+ ctx->compr_quality = S5P_JPEG_COMPR_QUAL_WORST - ctrl->val;
+ break;
+ case V4L2_CID_JPEG_RESTART_INTERVAL:
+ ctx->restart_interval = ctrl->val;
+ break;
+ case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
+ ctx->subsampling = ctrl->val;
+ break;
+ }
+
+ spin_unlock_irqrestore(&ctx->jpeg->slock, flags);
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops s5p_jpeg_ctrl_ops = {
+ .g_volatile_ctrl = s5p_jpeg_g_volatile_ctrl,
+ .s_ctrl = s5p_jpeg_s_ctrl,
+};
+
+static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx)
+{
+ unsigned int mask = ~0x27; /* 444, 422, 420, GRAY */
+ struct v4l2_ctrl *ctrl;
+
+ v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3);
+
+ if (ctx->mode == S5P_JPEG_ENCODE) {
+ v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops,
+ V4L2_CID_JPEG_COMPRESSION_QUALITY,
+ 0, 3, 1, 3);
+
+ v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops,
+ V4L2_CID_JPEG_RESTART_INTERVAL,
+ 0, 3, 0xffff, 0);
+ mask = ~0x06; /* 422, 420 */
+ }
+
+ ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops,
+ V4L2_CID_JPEG_CHROMA_SUBSAMPLING,
+ V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, mask,
+ V4L2_JPEG_CHROMA_SUBSAMPLING_422);
+
+ if (ctx->ctrl_handler.error)
+ return ctx->ctrl_handler.error;
+
+ if (ctx->mode == S5P_JPEG_DECODE)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
+ V4L2_CTRL_FLAG_READ_ONLY;
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops s5p_jpeg_ioctl_ops = {
+ .vidioc_querycap = s5p_jpeg_querycap,
+
+ .vidioc_enum_fmt_vid_cap = s5p_jpeg_enum_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_out = s5p_jpeg_enum_fmt_vid_out,
+
+ .vidioc_g_fmt_vid_cap = s5p_jpeg_g_fmt,
+ .vidioc_g_fmt_vid_out = s5p_jpeg_g_fmt,
+
+ .vidioc_try_fmt_vid_cap = s5p_jpeg_try_fmt_vid_cap,
+ .vidioc_try_fmt_vid_out = s5p_jpeg_try_fmt_vid_out,
+
+ .vidioc_s_fmt_vid_cap = s5p_jpeg_s_fmt_vid_cap,
+ .vidioc_s_fmt_vid_out = s5p_jpeg_s_fmt_vid_out,
+
+ .vidioc_reqbufs = s5p_jpeg_reqbufs,
+ .vidioc_querybuf = s5p_jpeg_querybuf,
+
+ .vidioc_qbuf = s5p_jpeg_qbuf,
+ .vidioc_dqbuf = s5p_jpeg_dqbuf,
+
+ .vidioc_streamon = s5p_jpeg_streamon,
+ .vidioc_streamoff = s5p_jpeg_streamoff,
+
+ .vidioc_g_selection = s5p_jpeg_g_selection,
+};
+
+/*
+ * ============================================================================
+ * mem2mem callbacks
+ * ============================================================================
+ */
+
+static void s5p_jpeg_device_run(void *priv)
+{
+ struct s5p_jpeg_ctx *ctx = priv;
+ struct s5p_jpeg *jpeg = ctx->jpeg;
+ struct vb2_buffer *src_buf, *dst_buf;
+ unsigned long src_addr, dst_addr;
+
+ src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+ src_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0);
+ dst_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+
+ jpeg_reset(jpeg->regs);
+ jpeg_poweron(jpeg->regs);
+ jpeg_proc_mode(jpeg->regs, ctx->mode);
+ if (ctx->mode == S5P_JPEG_ENCODE) {
+ if (ctx->out_q.fmt->fourcc == V4L2_PIX_FMT_RGB565)
+ jpeg_input_raw_mode(jpeg->regs, S5P_JPEG_RAW_IN_565);
+ else
+ jpeg_input_raw_mode(jpeg->regs, S5P_JPEG_RAW_IN_422);
+ jpeg_subsampling_mode(jpeg->regs, ctx->subsampling);
+ jpeg_dri(jpeg->regs, ctx->restart_interval);
+ jpeg_x(jpeg->regs, ctx->out_q.w);
+ jpeg_y(jpeg->regs, ctx->out_q.h);
+ jpeg_imgadr(jpeg->regs, src_addr);
+ jpeg_jpgadr(jpeg->regs, dst_addr);
+
+ /* ultimately comes from sizeimage from userspace */
+ jpeg_enc_stream_int(jpeg->regs, ctx->cap_q.size);
+
+ /* JPEG RGB to YCbCr conversion matrix */
+ jpeg_coef(jpeg->regs, 1, 1, S5P_JPEG_COEF11);
+ jpeg_coef(jpeg->regs, 1, 2, S5P_JPEG_COEF12);
+ jpeg_coef(jpeg->regs, 1, 3, S5P_JPEG_COEF13);
+ jpeg_coef(jpeg->regs, 2, 1, S5P_JPEG_COEF21);
+ jpeg_coef(jpeg->regs, 2, 2, S5P_JPEG_COEF22);
+ jpeg_coef(jpeg->regs, 2, 3, S5P_JPEG_COEF23);
+ jpeg_coef(jpeg->regs, 3, 1, S5P_JPEG_COEF31);
+ jpeg_coef(jpeg->regs, 3, 2, S5P_JPEG_COEF32);
+ jpeg_coef(jpeg->regs, 3, 3, S5P_JPEG_COEF33);
+
+ /*
+ * JPEG IP allows storing 4 quantization tables
+ * We fill table 0 for luma and table 1 for chroma
+ */
+ jpeg_set_qtbl_lum(jpeg->regs, ctx->compr_quality);
+ jpeg_set_qtbl_chr(jpeg->regs, ctx->compr_quality);
+ /* use table 0 for Y */
+ jpeg_qtbl(jpeg->regs, 1, 0);
+ /* use table 1 for Cb and Cr*/
+ jpeg_qtbl(jpeg->regs, 2, 1);
+ jpeg_qtbl(jpeg->regs, 3, 1);
+
+ /* Y, Cb, Cr use Huffman table 0 */
+ jpeg_htbl_ac(jpeg->regs, 1);
+ jpeg_htbl_dc(jpeg->regs, 1);
+ jpeg_htbl_ac(jpeg->regs, 2);
+ jpeg_htbl_dc(jpeg->regs, 2);
+ jpeg_htbl_ac(jpeg->regs, 3);
+ jpeg_htbl_dc(jpeg->regs, 3);
+ } else { /* S5P_JPEG_DECODE */
+ jpeg_rst_int_enable(jpeg->regs, true);
+ jpeg_data_num_int_enable(jpeg->regs, true);
+ jpeg_final_mcu_num_int_enable(jpeg->regs, true);
+ if (ctx->cap_q.fmt->fourcc == V4L2_PIX_FMT_YUYV)
+ jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_422);
+ else
+ jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_420);
+ jpeg_jpgadr(jpeg->regs, src_addr);
+ jpeg_imgadr(jpeg->regs, dst_addr);
+ }
+
+ jpeg_start(jpeg->regs);
+}
+
+static int s5p_jpeg_job_ready(void *priv)
+{
+ struct s5p_jpeg_ctx *ctx = priv;
+
+ if (ctx->mode == S5P_JPEG_DECODE)
+ return ctx->hdr_parsed;
+ return 1;
+}
+
+static void s5p_jpeg_job_abort(void *priv)
+{
+}
+
+static struct v4l2_m2m_ops s5p_jpeg_m2m_ops = {
+ .device_run = s5p_jpeg_device_run,
+ .job_ready = s5p_jpeg_job_ready,
+ .job_abort = s5p_jpeg_job_abort,
+};
+
+/*
+ * ============================================================================
+ * Queue operations
+ * ============================================================================
+ */
+
+static int s5p_jpeg_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vq);
+ struct s5p_jpeg_q_data *q_data = NULL;
+ unsigned int size, count = *nbuffers;
+
+ q_data = get_q_data(ctx, vq->type);
+ BUG_ON(q_data == NULL);
+
+ size = q_data->size;
+
+ /*
+ * header is parsed during decoding and parsed information stored
+ * in the context so we do not allow another buffer to overwrite it
+ */
+ if (ctx->mode == S5P_JPEG_DECODE)
+ count = 1;
+
+ *nbuffers = count;
+ *nplanes = 1;
+ sizes[0] = size;
+ alloc_ctxs[0] = ctx->jpeg->alloc_ctx;
+
+ return 0;
+}
+
+static int s5p_jpeg_buf_prepare(struct vb2_buffer *vb)
+{
+ struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct s5p_jpeg_q_data *q_data = NULL;
+
+ q_data = get_q_data(ctx, vb->vb2_queue->type);
+ BUG_ON(q_data == NULL);
+
+ if (vb2_plane_size(vb, 0) < q_data->size) {
+ pr_err("%s data will not fit into plane (%lu < %lu)\n",
+ __func__, vb2_plane_size(vb, 0),
+ (long)q_data->size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, 0, q_data->size);
+
+ return 0;
+}
+
+static void s5p_jpeg_buf_queue(struct vb2_buffer *vb)
+{
+ struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+ if (ctx->mode == S5P_JPEG_DECODE &&
+ vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ struct s5p_jpeg_q_data tmp, *q_data;
+ ctx->hdr_parsed = s5p_jpeg_parse_hdr(&tmp,
+ (unsigned long)vb2_plane_vaddr(vb, 0),
+ min((unsigned long)ctx->out_q.size,
+ vb2_get_plane_payload(vb, 0)));
+ if (!ctx->hdr_parsed) {
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+ return;
+ }
+
+ q_data = &ctx->out_q;
+ q_data->w = tmp.w;
+ q_data->h = tmp.h;
+
+ q_data = &ctx->cap_q;
+ q_data->w = tmp.w;
+ q_data->h = tmp.h;
+
+ jpeg_bound_align_image(&q_data->w, S5P_JPEG_MIN_WIDTH,
+ S5P_JPEG_MAX_WIDTH, q_data->fmt->h_align,
+ &q_data->h, S5P_JPEG_MIN_HEIGHT,
+ S5P_JPEG_MAX_HEIGHT, q_data->fmt->v_align
+ );
+ q_data->size = q_data->w * q_data->h * q_data->fmt->depth >> 3;
+ }
+ if (ctx->m2m_ctx)
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+}
+
+static void s5p_jpeg_wait_prepare(struct vb2_queue *vq)
+{
+ struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vq);
+
+ mutex_unlock(&ctx->jpeg->lock);
+}
+
+static void s5p_jpeg_wait_finish(struct vb2_queue *vq)
+{
+ struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vq);
+
+ mutex_lock(&ctx->jpeg->lock);
+}
+
+static int s5p_jpeg_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(q);
+ int ret;
+
+ ret = pm_runtime_get_sync(ctx->jpeg->dev);
+
+ return ret > 0 ? 0 : ret;
+}
+
+static int s5p_jpeg_stop_streaming(struct vb2_queue *q)
+{
+ struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(q);
+
+ pm_runtime_put(ctx->jpeg->dev);
+
+ return 0;
+}
+
+static struct vb2_ops s5p_jpeg_qops = {
+ .queue_setup = s5p_jpeg_queue_setup,
+ .buf_prepare = s5p_jpeg_buf_prepare,
+ .buf_queue = s5p_jpeg_buf_queue,
+ .wait_prepare = s5p_jpeg_wait_prepare,
+ .wait_finish = s5p_jpeg_wait_finish,
+ .start_streaming = s5p_jpeg_start_streaming,
+ .stop_streaming = s5p_jpeg_stop_streaming,
+};
+
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct s5p_jpeg_ctx *ctx = priv;
+ int ret;
+
+ memset(src_vq, 0, sizeof(*src_vq));
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ src_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ src_vq->drv_priv = ctx;
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ src_vq->ops = &s5p_jpeg_qops;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ memset(dst_vq, 0, sizeof(*dst_vq));
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ dst_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ dst_vq->drv_priv = ctx;
+ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ dst_vq->ops = &s5p_jpeg_qops;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+
+ return vb2_queue_init(dst_vq);
+}
+
+/*
+ * ============================================================================
+ * ISR
+ * ============================================================================
+ */
+
+static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id)
+{
+ struct s5p_jpeg *jpeg = dev_id;
+ struct s5p_jpeg_ctx *curr_ctx;
+ struct vb2_buffer *src_buf, *dst_buf;
+ unsigned long payload_size = 0;
+ enum vb2_buffer_state state = VB2_BUF_STATE_DONE;
+ bool enc_jpeg_too_large = false;
+ bool timer_elapsed = false;
+ bool op_completed = false;
+
+ spin_lock(&jpeg->slock);
+
+ curr_ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev);
+
+ src_buf = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);
+ dst_buf = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx);
+
+ if (curr_ctx->mode == S5P_JPEG_ENCODE)
+ enc_jpeg_too_large = jpeg_enc_stream_stat(jpeg->regs);
+ timer_elapsed = jpeg_timer_stat(jpeg->regs);
+ op_completed = jpeg_result_stat_ok(jpeg->regs);
+ if (curr_ctx->mode == S5P_JPEG_DECODE)
+ op_completed = op_completed && jpeg_stream_stat_ok(jpeg->regs);
+
+ if (enc_jpeg_too_large) {
+ state = VB2_BUF_STATE_ERROR;
+ jpeg_clear_enc_stream_stat(jpeg->regs);
+ } else if (timer_elapsed) {
+ state = VB2_BUF_STATE_ERROR;
+ jpeg_clear_timer_stat(jpeg->regs);
+ } else if (!op_completed) {
+ state = VB2_BUF_STATE_ERROR;
+ } else {
+ payload_size = jpeg_compressed_size(jpeg->regs);
+ }
+
+ v4l2_m2m_buf_done(src_buf, state);
+ if (curr_ctx->mode == S5P_JPEG_ENCODE)
+ vb2_set_plane_payload(dst_buf, 0, payload_size);
+ v4l2_m2m_buf_done(dst_buf, state);
+ v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->m2m_ctx);
+
+ curr_ctx->subsampling = jpeg_get_subsampling_mode(jpeg->regs);
+ spin_unlock(&jpeg->slock);
+
+ jpeg_clear_int(jpeg->regs);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * ============================================================================
+ * Driver basic infrastructure
+ * ============================================================================
+ */
+
+static int s5p_jpeg_probe(struct platform_device *pdev)
+{
+ struct s5p_jpeg *jpeg;
+ struct resource *res;
+ int ret;
+
+ /* JPEG IP abstraction struct */
+ jpeg = kzalloc(sizeof(struct s5p_jpeg), GFP_KERNEL);
+ if (!jpeg)
+ return -ENOMEM;
+
+ mutex_init(&jpeg->lock);
+ spin_lock_init(&jpeg->slock);
+ jpeg->dev = &pdev->dev;
+
+ /* memory-mapped registers */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "cannot find IO resource\n");
+ ret = -ENOENT;
+ goto jpeg_alloc_rollback;
+ }
+
+ jpeg->ioarea = request_mem_region(res->start, resource_size(res),
+ pdev->name);
+ if (!jpeg->ioarea) {
+ dev_err(&pdev->dev, "cannot request IO\n");
+ ret = -ENXIO;
+ goto jpeg_alloc_rollback;
+ }
+
+ jpeg->regs = ioremap(res->start, resource_size(res));
+ if (!jpeg->regs) {
+ dev_err(&pdev->dev, "cannot map IO\n");
+ ret = -ENXIO;
+ goto mem_region_rollback;
+ }
+
+ dev_dbg(&pdev->dev, "registers %p (%p, %p)\n",
+ jpeg->regs, jpeg->ioarea, res);
+
+ /* interrupt service routine registration */
+ jpeg->irq = ret = platform_get_irq(pdev, 0);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "cannot find IRQ\n");
+ goto ioremap_rollback;
+ }
+
+ ret = request_irq(jpeg->irq, s5p_jpeg_irq, 0,
+ dev_name(&pdev->dev), jpeg);
+
+ if (ret) {
+ dev_err(&pdev->dev, "cannot claim IRQ %d\n", jpeg->irq);
+ goto ioremap_rollback;
+ }
+
+ /* clocks */
+ jpeg->clk = clk_get(&pdev->dev, "jpeg");
+ if (IS_ERR(jpeg->clk)) {
+ dev_err(&pdev->dev, "cannot get clock\n");
+ ret = PTR_ERR(jpeg->clk);
+ goto request_irq_rollback;
+ }
+ dev_dbg(&pdev->dev, "clock source %p\n", jpeg->clk);
+ clk_enable(jpeg->clk);
+
+ /* v4l2 device */
+ ret = v4l2_device_register(&pdev->dev, &jpeg->v4l2_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register v4l2 device\n");
+ goto clk_get_rollback;
+ }
+
+ /* mem2mem device */
+ jpeg->m2m_dev = v4l2_m2m_init(&s5p_jpeg_m2m_ops);
+ if (IS_ERR(jpeg->m2m_dev)) {
+ v4l2_err(&jpeg->v4l2_dev, "Failed to init mem2mem device\n");
+ ret = PTR_ERR(jpeg->m2m_dev);
+ goto device_register_rollback;
+ }
+
+ jpeg->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(jpeg->alloc_ctx)) {
+ v4l2_err(&jpeg->v4l2_dev, "Failed to init memory allocator\n");
+ ret = PTR_ERR(jpeg->alloc_ctx);
+ goto m2m_init_rollback;
+ }
+
+ /* JPEG encoder /dev/videoX node */
+ jpeg->vfd_encoder = video_device_alloc();
+ if (!jpeg->vfd_encoder) {
+ v4l2_err(&jpeg->v4l2_dev, "Failed to allocate video device\n");
+ ret = -ENOMEM;
+ goto vb2_allocator_rollback;
+ }
+ strlcpy(jpeg->vfd_encoder->name, S5P_JPEG_M2M_NAME,
+ sizeof(jpeg->vfd_encoder->name));
+ jpeg->vfd_encoder->fops = &s5p_jpeg_fops;
+ jpeg->vfd_encoder->ioctl_ops = &s5p_jpeg_ioctl_ops;
+ jpeg->vfd_encoder->minor = -1;
+ jpeg->vfd_encoder->release = video_device_release;
+ jpeg->vfd_encoder->lock = &jpeg->lock;
+ jpeg->vfd_encoder->v4l2_dev = &jpeg->v4l2_dev;
+
+ ret = video_register_device(jpeg->vfd_encoder, VFL_TYPE_GRABBER, -1);
+ if (ret) {
+ v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n");
+ goto enc_vdev_alloc_rollback;
+ }
+
+ video_set_drvdata(jpeg->vfd_encoder, jpeg);
+ v4l2_info(&jpeg->v4l2_dev,
+ "encoder device registered as /dev/video%d\n",
+ jpeg->vfd_encoder->num);
+
+ /* JPEG decoder /dev/videoX node */
+ jpeg->vfd_decoder = video_device_alloc();
+ if (!jpeg->vfd_decoder) {
+ v4l2_err(&jpeg->v4l2_dev, "Failed to allocate video device\n");
+ ret = -ENOMEM;
+ goto enc_vdev_register_rollback;
+ }
+ strlcpy(jpeg->vfd_decoder->name, S5P_JPEG_M2M_NAME,
+ sizeof(jpeg->vfd_decoder->name));
+ jpeg->vfd_decoder->fops = &s5p_jpeg_fops;
+ jpeg->vfd_decoder->ioctl_ops = &s5p_jpeg_ioctl_ops;
+ jpeg->vfd_decoder->minor = -1;
+ jpeg->vfd_decoder->release = video_device_release;
+ jpeg->vfd_decoder->lock = &jpeg->lock;
+ jpeg->vfd_decoder->v4l2_dev = &jpeg->v4l2_dev;
+
+ ret = video_register_device(jpeg->vfd_decoder, VFL_TYPE_GRABBER, -1);
+ if (ret) {
+ v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n");
+ goto dec_vdev_alloc_rollback;
+ }
+
+ video_set_drvdata(jpeg->vfd_decoder, jpeg);
+ v4l2_info(&jpeg->v4l2_dev,
+ "decoder device registered as /dev/video%d\n",
+ jpeg->vfd_decoder->num);
+
+ /* final statements & power management */
+ platform_set_drvdata(pdev, jpeg);
+
+ pm_runtime_enable(&pdev->dev);
+
+ v4l2_info(&jpeg->v4l2_dev, "Samsung S5P JPEG codec\n");
+
+ return 0;
+
+dec_vdev_alloc_rollback:
+ video_device_release(jpeg->vfd_decoder);
+
+enc_vdev_register_rollback:
+ video_unregister_device(jpeg->vfd_encoder);
+
+enc_vdev_alloc_rollback:
+ video_device_release(jpeg->vfd_encoder);
+
+vb2_allocator_rollback:
+ vb2_dma_contig_cleanup_ctx(jpeg->alloc_ctx);
+
+m2m_init_rollback:
+ v4l2_m2m_release(jpeg->m2m_dev);
+
+device_register_rollback:
+ v4l2_device_unregister(&jpeg->v4l2_dev);
+
+clk_get_rollback:
+ clk_disable(jpeg->clk);
+ clk_put(jpeg->clk);
+
+request_irq_rollback:
+ free_irq(jpeg->irq, jpeg);
+
+ioremap_rollback:
+ iounmap(jpeg->regs);
+
+mem_region_rollback:
+ release_resource(jpeg->ioarea);
+ release_mem_region(jpeg->ioarea->start, resource_size(jpeg->ioarea));
+
+jpeg_alloc_rollback:
+ kfree(jpeg);
+ return ret;
+}
+
+static int s5p_jpeg_remove(struct platform_device *pdev)
+{
+ struct s5p_jpeg *jpeg = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(jpeg->dev);
+
+ video_unregister_device(jpeg->vfd_decoder);
+ video_device_release(jpeg->vfd_decoder);
+ video_unregister_device(jpeg->vfd_encoder);
+ video_device_release(jpeg->vfd_encoder);
+ vb2_dma_contig_cleanup_ctx(jpeg->alloc_ctx);
+ v4l2_m2m_release(jpeg->m2m_dev);
+ v4l2_device_unregister(&jpeg->v4l2_dev);
+
+ clk_disable(jpeg->clk);
+ clk_put(jpeg->clk);
+
+ free_irq(jpeg->irq, jpeg);
+
+ iounmap(jpeg->regs);
+
+ release_resource(jpeg->ioarea);
+ release_mem_region(jpeg->ioarea->start, resource_size(jpeg->ioarea));
+ kfree(jpeg);
+
+ return 0;
+}
+
+static int s5p_jpeg_runtime_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int s5p_jpeg_runtime_resume(struct device *dev)
+{
+ struct s5p_jpeg *jpeg = dev_get_drvdata(dev);
+ /*
+ * JPEG IP allows storing two Huffman tables for each component
+ * We fill table 0 for each component
+ */
+ jpeg_set_hdctbl(jpeg->regs);
+ jpeg_set_hdctblg(jpeg->regs);
+ jpeg_set_hactbl(jpeg->regs);
+ jpeg_set_hactblg(jpeg->regs);
+ return 0;
+}
+
+static const struct dev_pm_ops s5p_jpeg_pm_ops = {
+ .runtime_suspend = s5p_jpeg_runtime_suspend,
+ .runtime_resume = s5p_jpeg_runtime_resume,
+};
+
+static struct platform_driver s5p_jpeg_driver = {
+ .probe = s5p_jpeg_probe,
+ .remove = s5p_jpeg_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = S5P_JPEG_M2M_NAME,
+ .pm = &s5p_jpeg_pm_ops,
+ },
+};
+
+static int __init
+s5p_jpeg_register(void)
+{
+ int ret;
+
+ pr_info("S5P JPEG V4L2 Driver, (c) 2011 Samsung Electronics\n");
+
+ ret = platform_driver_register(&s5p_jpeg_driver);
+
+ if (ret)
+ pr_err("%s: failed to register jpeg driver\n", __func__);
+
+ return ret;
+}
+
+static void __exit
+s5p_jpeg_unregister(void)
+{
+ platform_driver_unregister(&s5p_jpeg_driver);
+}
+
+module_init(s5p_jpeg_register);
+module_exit(s5p_jpeg_unregister);
+
+MODULE_AUTHOR("Andrzej Pietrasiewicz <andrzej.p@samsung.com>");
+MODULE_DESCRIPTION("Samsung JPEG codec driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/media/video/s5p-jpeg/jpeg-core.h b/drivers/media/video/s5p-jpeg/jpeg-core.h
new file mode 100644
index 00000000..38d7367f
--- /dev/null
+++ b/drivers/media/video/s5p-jpeg/jpeg-core.h
@@ -0,0 +1,152 @@
+/* linux/drivers/media/video/s5p-jpeg/jpeg-core.h
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef JPEG_CORE_H_
+#define JPEG_CORE_H_
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ctrls.h>
+
+#define S5P_JPEG_M2M_NAME "s5p-jpeg"
+
+/* JPEG compression quality setting */
+#define S5P_JPEG_COMPR_QUAL_BEST 0
+#define S5P_JPEG_COMPR_QUAL_WORST 3
+
+/* JPEG RGB to YCbCr conversion matrix coefficients */
+#define S5P_JPEG_COEF11 0x4d
+#define S5P_JPEG_COEF12 0x97
+#define S5P_JPEG_COEF13 0x1e
+#define S5P_JPEG_COEF21 0x2c
+#define S5P_JPEG_COEF22 0x57
+#define S5P_JPEG_COEF23 0x83
+#define S5P_JPEG_COEF31 0x83
+#define S5P_JPEG_COEF32 0x6e
+#define S5P_JPEG_COEF33 0x13
+
+/* a selection of JPEG markers */
+#define TEM 0x01
+#define SOF0 0xc0
+#define RST 0xd0
+#define SOI 0xd8
+#define EOI 0xd9
+#define DHP 0xde
+
+/* Flags that indicate a format can be used for capture/output */
+#define MEM2MEM_CAPTURE (1 << 0)
+#define MEM2MEM_OUTPUT (1 << 1)
+
+/**
+ * struct s5p_jpeg - JPEG IP abstraction
+ * @lock: the mutex protecting this structure
+ * @slock: spinlock protecting the device contexts
+ * @v4l2_dev: v4l2 device for mem2mem mode
+ * @vfd_encoder: video device node for encoder mem2mem mode
+ * @vfd_decoder: video device node for decoder mem2mem mode
+ * @m2m_dev: v4l2 mem2mem device data
+ * @ioarea: JPEG IP memory region
+ * @regs: JPEG IP registers mapping
+ * @irq: JPEG IP irq
+ * @clk: JPEG IP clock
+ * @dev: JPEG IP struct device
+ * @alloc_ctx: videobuf2 memory allocator's context
+ */
+struct s5p_jpeg {
+ struct mutex lock;
+ struct spinlock slock;
+
+ struct v4l2_device v4l2_dev;
+ struct video_device *vfd_encoder;
+ struct video_device *vfd_decoder;
+ struct v4l2_m2m_dev *m2m_dev;
+
+ struct resource *ioarea;
+ void __iomem *regs;
+ unsigned int irq;
+ struct clk *clk;
+ struct device *dev;
+ void *alloc_ctx;
+};
+
+/**
+ * struct jpeg_fmt - driver's internal color format data
+ * @name: format descritpion
+ * @fourcc: the fourcc code, 0 if not applicable
+ * @depth: number of bits per pixel
+ * @colplanes: number of color planes (1 for packed formats)
+ * @h_align: horizontal alignment order (align to 2^h_align)
+ * @v_align: vertical alignment order (align to 2^v_align)
+ * @types: types of queue this format is applicable to
+ */
+struct s5p_jpeg_fmt {
+ char *name;
+ u32 fourcc;
+ int depth;
+ int colplanes;
+ int h_align;
+ int v_align;
+ u32 types;
+};
+
+/**
+ * s5p_jpeg_q_data - parameters of one queue
+ * @fmt: driver-specific format of this queue
+ * @w: image width
+ * @h: image height
+ * @size: image buffer size in bytes
+ */
+struct s5p_jpeg_q_data {
+ struct s5p_jpeg_fmt *fmt;
+ u32 w;
+ u32 h;
+ u32 size;
+};
+
+/**
+ * s5p_jpeg_ctx - the device context data
+ * @jpeg: JPEG IP device for this context
+ * @mode: compression (encode) operation or decompression (decode)
+ * @compr_quality: destination image quality in compression (encode) mode
+ * @m2m_ctx: mem2mem device context
+ * @out_q: source (output) queue information
+ * @cap_fmt: destination (capture) queue queue information
+ * @hdr_parsed: set if header has been parsed during decompression
+ * @ctrl_handler: controls handler
+ */
+struct s5p_jpeg_ctx {
+ struct s5p_jpeg *jpeg;
+ unsigned int mode;
+ unsigned short compr_quality;
+ unsigned short restart_interval;
+ unsigned short subsampling;
+ struct v4l2_m2m_ctx *m2m_ctx;
+ struct s5p_jpeg_q_data out_q;
+ struct s5p_jpeg_q_data cap_q;
+ struct v4l2_fh fh;
+ bool hdr_parsed;
+ struct v4l2_ctrl_handler ctrl_handler;
+};
+
+/**
+ * s5p_jpeg_buffer - description of memory containing input JPEG data
+ * @size: buffer size
+ * @curr: current position in the buffer
+ * @data: pointer to the data
+ */
+struct s5p_jpeg_buffer {
+ unsigned long size;
+ unsigned long curr;
+ unsigned long data;
+};
+
+#endif /* JPEG_CORE_H */
diff --git a/drivers/media/video/s5p-jpeg/jpeg-hw.h b/drivers/media/video/s5p-jpeg/jpeg-hw.h
new file mode 100644
index 00000000..f12f0fdb
--- /dev/null
+++ b/drivers/media/video/s5p-jpeg/jpeg-hw.h
@@ -0,0 +1,357 @@
+/* linux/drivers/media/video/s5p-jpeg/jpeg-hw.h
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef JPEG_HW_H_
+#define JPEG_HW_H_
+
+#include <linux/io.h>
+#include <linux/videodev2.h>
+
+#include "jpeg-hw.h"
+#include "jpeg-regs.h"
+
+#define S5P_JPEG_MIN_WIDTH 32
+#define S5P_JPEG_MIN_HEIGHT 32
+#define S5P_JPEG_MAX_WIDTH 8192
+#define S5P_JPEG_MAX_HEIGHT 8192
+#define S5P_JPEG_ENCODE 0
+#define S5P_JPEG_DECODE 1
+#define S5P_JPEG_RAW_IN_565 0
+#define S5P_JPEG_RAW_IN_422 1
+#define S5P_JPEG_RAW_OUT_422 0
+#define S5P_JPEG_RAW_OUT_420 1
+
+static inline void jpeg_reset(void __iomem *regs)
+{
+ unsigned long reg;
+
+ writel(1, regs + S5P_JPG_SW_RESET);
+ reg = readl(regs + S5P_JPG_SW_RESET);
+ /* no other way but polling for when JPEG IP becomes operational */
+ while (reg != 0) {
+ cpu_relax();
+ reg = readl(regs + S5P_JPG_SW_RESET);
+ }
+}
+
+static inline void jpeg_poweron(void __iomem *regs)
+{
+ writel(S5P_POWER_ON, regs + S5P_JPGCLKCON);
+}
+
+static inline void jpeg_input_raw_mode(void __iomem *regs, unsigned long mode)
+{
+ unsigned long reg, m;
+
+ m = S5P_MOD_SEL_565;
+ if (mode == S5P_JPEG_RAW_IN_565)
+ m = S5P_MOD_SEL_565;
+ else if (mode == S5P_JPEG_RAW_IN_422)
+ m = S5P_MOD_SEL_422;
+
+ reg = readl(regs + S5P_JPGCMOD);
+ reg &= ~S5P_MOD_SEL_MASK;
+ reg |= m;
+ writel(reg, regs + S5P_JPGCMOD);
+}
+
+static inline void jpeg_input_raw_y16(void __iomem *regs, bool y16)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPGCMOD);
+ if (y16)
+ reg |= S5P_MODE_Y16;
+ else
+ reg &= ~S5P_MODE_Y16_MASK;
+ writel(reg, regs + S5P_JPGCMOD);
+}
+
+static inline void jpeg_proc_mode(void __iomem *regs, unsigned long mode)
+{
+ unsigned long reg, m;
+
+ m = S5P_PROC_MODE_DECOMPR;
+ if (mode == S5P_JPEG_ENCODE)
+ m = S5P_PROC_MODE_COMPR;
+ else
+ m = S5P_PROC_MODE_DECOMPR;
+ reg = readl(regs + S5P_JPGMOD);
+ reg &= ~S5P_PROC_MODE_MASK;
+ reg |= m;
+ writel(reg, regs + S5P_JPGMOD);
+}
+
+static inline void jpeg_subsampling_mode(void __iomem *regs, unsigned int mode)
+{
+ unsigned long reg, m;
+
+ if (mode == V4L2_JPEG_CHROMA_SUBSAMPLING_420)
+ m = S5P_SUBSAMPLING_MODE_420;
+ else
+ m = S5P_SUBSAMPLING_MODE_422;
+
+ reg = readl(regs + S5P_JPGMOD);
+ reg &= ~S5P_SUBSAMPLING_MODE_MASK;
+ reg |= m;
+ writel(reg, regs + S5P_JPGMOD);
+}
+
+static inline unsigned int jpeg_get_subsampling_mode(void __iomem *regs)
+{
+ return readl(regs + S5P_JPGMOD) & S5P_SUBSAMPLING_MODE_MASK;
+}
+
+static inline void jpeg_dri(void __iomem *regs, unsigned int dri)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPGDRI_U);
+ reg &= ~0xff;
+ reg |= (dri >> 8) & 0xff;
+ writel(reg, regs + S5P_JPGDRI_U);
+
+ reg = readl(regs + S5P_JPGDRI_L);
+ reg &= ~0xff;
+ reg |= dri & 0xff;
+ writel(reg, regs + S5P_JPGDRI_L);
+}
+
+static inline void jpeg_qtbl(void __iomem *regs, unsigned int t, unsigned int n)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPG_QTBL);
+ reg &= ~S5P_QT_NUMt_MASK(t);
+ reg |= (n << S5P_QT_NUMt_SHIFT(t)) & S5P_QT_NUMt_MASK(t);
+ writel(reg, regs + S5P_JPG_QTBL);
+}
+
+static inline void jpeg_htbl_ac(void __iomem *regs, unsigned int t)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPG_HTBL);
+ reg &= ~S5P_HT_NUMt_AC_MASK(t);
+ /* this driver uses table 0 for all color components */
+ reg |= (0 << S5P_HT_NUMt_AC_SHIFT(t)) & S5P_HT_NUMt_AC_MASK(t);
+ writel(reg, regs + S5P_JPG_HTBL);
+}
+
+static inline void jpeg_htbl_dc(void __iomem *regs, unsigned int t)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPG_HTBL);
+ reg &= ~S5P_HT_NUMt_DC_MASK(t);
+ /* this driver uses table 0 for all color components */
+ reg |= (0 << S5P_HT_NUMt_DC_SHIFT(t)) & S5P_HT_NUMt_DC_MASK(t);
+ writel(reg, regs + S5P_JPG_HTBL);
+}
+
+static inline void jpeg_y(void __iomem *regs, unsigned int y)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPGY_U);
+ reg &= ~0xff;
+ reg |= (y >> 8) & 0xff;
+ writel(reg, regs + S5P_JPGY_U);
+
+ reg = readl(regs + S5P_JPGY_L);
+ reg &= ~0xff;
+ reg |= y & 0xff;
+ writel(reg, regs + S5P_JPGY_L);
+}
+
+static inline void jpeg_x(void __iomem *regs, unsigned int x)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPGX_U);
+ reg &= ~0xff;
+ reg |= (x >> 8) & 0xff;
+ writel(reg, regs + S5P_JPGX_U);
+
+ reg = readl(regs + S5P_JPGX_L);
+ reg &= ~0xff;
+ reg |= x & 0xff;
+ writel(reg, regs + S5P_JPGX_L);
+}
+
+static inline void jpeg_rst_int_enable(void __iomem *regs, bool enable)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPGINTSE);
+ reg &= ~S5P_RSTm_INT_EN_MASK;
+ if (enable)
+ reg |= S5P_RSTm_INT_EN;
+ writel(reg, regs + S5P_JPGINTSE);
+}
+
+static inline void jpeg_data_num_int_enable(void __iomem *regs, bool enable)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPGINTSE);
+ reg &= ~S5P_DATA_NUM_INT_EN_MASK;
+ if (enable)
+ reg |= S5P_DATA_NUM_INT_EN;
+ writel(reg, regs + S5P_JPGINTSE);
+}
+
+static inline void jpeg_final_mcu_num_int_enable(void __iomem *regs, bool enbl)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPGINTSE);
+ reg &= ~S5P_FINAL_MCU_NUM_INT_EN_MASK;
+ if (enbl)
+ reg |= S5P_FINAL_MCU_NUM_INT_EN;
+ writel(reg, regs + S5P_JPGINTSE);
+}
+
+static inline void jpeg_timer_enable(void __iomem *regs, unsigned long val)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPG_TIMER_SE);
+ reg |= S5P_TIMER_INT_EN;
+ reg &= ~S5P_TIMER_INIT_MASK;
+ reg |= val & S5P_TIMER_INIT_MASK;
+ writel(reg, regs + S5P_JPG_TIMER_SE);
+}
+
+static inline void jpeg_timer_disable(void __iomem *regs)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPG_TIMER_SE);
+ reg &= ~S5P_TIMER_INT_EN_MASK;
+ writel(reg, regs + S5P_JPG_TIMER_SE);
+}
+
+static inline int jpeg_timer_stat(void __iomem *regs)
+{
+ return (int)((readl(regs + S5P_JPG_TIMER_ST) & S5P_TIMER_INT_STAT_MASK)
+ >> S5P_TIMER_INT_STAT_SHIFT);
+}
+
+static inline void jpeg_clear_timer_stat(void __iomem *regs)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPG_TIMER_SE);
+ reg &= ~S5P_TIMER_INT_STAT_MASK;
+ writel(reg, regs + S5P_JPG_TIMER_SE);
+}
+
+static inline void jpeg_enc_stream_int(void __iomem *regs, unsigned long size)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPG_ENC_STREAM_INTSE);
+ reg &= ~S5P_ENC_STREAM_BOUND_MASK;
+ reg |= S5P_ENC_STREAM_INT_EN;
+ reg |= size & S5P_ENC_STREAM_BOUND_MASK;
+ writel(reg, regs + S5P_JPG_ENC_STREAM_INTSE);
+}
+
+static inline int jpeg_enc_stream_stat(void __iomem *regs)
+{
+ return (int)(readl(regs + S5P_JPG_ENC_STREAM_INTST) &
+ S5P_ENC_STREAM_INT_STAT_MASK);
+}
+
+static inline void jpeg_clear_enc_stream_stat(void __iomem *regs)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPG_ENC_STREAM_INTSE);
+ reg &= ~S5P_ENC_STREAM_INT_MASK;
+ writel(reg, regs + S5P_JPG_ENC_STREAM_INTSE);
+}
+
+static inline void jpeg_outform_raw(void __iomem *regs, unsigned long format)
+{
+ unsigned long reg, f;
+
+ f = S5P_DEC_OUT_FORMAT_422;
+ if (format == S5P_JPEG_RAW_OUT_422)
+ f = S5P_DEC_OUT_FORMAT_422;
+ else if (format == S5P_JPEG_RAW_OUT_420)
+ f = S5P_DEC_OUT_FORMAT_420;
+ reg = readl(regs + S5P_JPG_OUTFORM);
+ reg &= ~S5P_DEC_OUT_FORMAT_MASK;
+ reg |= f;
+ writel(reg, regs + S5P_JPG_OUTFORM);
+}
+
+static inline void jpeg_jpgadr(void __iomem *regs, unsigned long addr)
+{
+ writel(addr, regs + S5P_JPG_JPGADR);
+}
+
+static inline void jpeg_imgadr(void __iomem *regs, unsigned long addr)
+{
+ writel(addr, regs + S5P_JPG_IMGADR);
+}
+
+static inline void jpeg_coef(void __iomem *regs, unsigned int i,
+ unsigned int j, unsigned int coef)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPG_COEF(i));
+ reg &= ~S5P_COEFn_MASK(j);
+ reg |= (coef << S5P_COEFn_SHIFT(j)) & S5P_COEFn_MASK(j);
+ writel(reg, regs + S5P_JPG_COEF(i));
+}
+
+static inline void jpeg_start(void __iomem *regs)
+{
+ writel(1, regs + S5P_JSTART);
+}
+
+static inline int jpeg_result_stat_ok(void __iomem *regs)
+{
+ return (int)((readl(regs + S5P_JPGINTST) & S5P_RESULT_STAT_MASK)
+ >> S5P_RESULT_STAT_SHIFT);
+}
+
+static inline int jpeg_stream_stat_ok(void __iomem *regs)
+{
+ return !(int)((readl(regs + S5P_JPGINTST) & S5P_STREAM_STAT_MASK)
+ >> S5P_STREAM_STAT_SHIFT);
+}
+
+static inline void jpeg_clear_int(void __iomem *regs)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPGINTST);
+ writel(S5P_INT_RELEASE, regs + S5P_JPGCOM);
+ reg = readl(regs + S5P_JPGOPR);
+}
+
+static inline unsigned int jpeg_compressed_size(void __iomem *regs)
+{
+ unsigned long jpeg_size = 0;
+
+ jpeg_size |= (readl(regs + S5P_JPGCNT_U) & 0xff) << 16;
+ jpeg_size |= (readl(regs + S5P_JPGCNT_M) & 0xff) << 8;
+ jpeg_size |= (readl(regs + S5P_JPGCNT_L) & 0xff);
+
+ return (unsigned int)jpeg_size;
+}
+
+#endif /* JPEG_HW_H_ */
diff --git a/drivers/media/video/s5p-jpeg/jpeg-regs.h b/drivers/media/video/s5p-jpeg/jpeg-regs.h
new file mode 100644
index 00000000..91f4dd5f
--- /dev/null
+++ b/drivers/media/video/s5p-jpeg/jpeg-regs.h
@@ -0,0 +1,170 @@
+/* linux/drivers/media/video/s5p-jpeg/jpeg-regs.h
+ *
+ * Register definition file for Samsung JPEG codec driver
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef JPEG_REGS_H_
+#define JPEG_REGS_H_
+
+/* JPEG mode register */
+#define S5P_JPGMOD 0x00
+#define S5P_PROC_MODE_MASK (0x1 << 3)
+#define S5P_PROC_MODE_DECOMPR (0x1 << 3)
+#define S5P_PROC_MODE_COMPR (0x0 << 3)
+#define S5P_SUBSAMPLING_MODE_MASK 0x7
+#define S5P_SUBSAMPLING_MODE_444 (0x0 << 0)
+#define S5P_SUBSAMPLING_MODE_422 (0x1 << 0)
+#define S5P_SUBSAMPLING_MODE_420 (0x2 << 0)
+#define S5P_SUBSAMPLING_MODE_GRAY (0x3 << 0)
+
+/* JPEG operation status register */
+#define S5P_JPGOPR 0x04
+
+/* Quantization tables*/
+#define S5P_JPG_QTBL 0x08
+#define S5P_QT_NUMt_SHIFT(t) (((t) - 1) << 1)
+#define S5P_QT_NUMt_MASK(t) (0x3 << S5P_QT_NUMt_SHIFT(t))
+
+/* Huffman tables */
+#define S5P_JPG_HTBL 0x0c
+#define S5P_HT_NUMt_AC_SHIFT(t) (((t) << 1) - 1)
+#define S5P_HT_NUMt_AC_MASK(t) (0x1 << S5P_HT_NUMt_AC_SHIFT(t))
+
+#define S5P_HT_NUMt_DC_SHIFT(t) (((t) - 1) << 1)
+#define S5P_HT_NUMt_DC_MASK(t) (0x1 << S5P_HT_NUMt_DC_SHIFT(t))
+
+/* JPEG restart interval register upper byte */
+#define S5P_JPGDRI_U 0x10
+
+/* JPEG restart interval register lower byte */
+#define S5P_JPGDRI_L 0x14
+
+/* JPEG vertical resolution register upper byte */
+#define S5P_JPGY_U 0x18
+
+/* JPEG vertical resolution register lower byte */
+#define S5P_JPGY_L 0x1c
+
+/* JPEG horizontal resolution register upper byte */
+#define S5P_JPGX_U 0x20
+
+/* JPEG horizontal resolution register lower byte */
+#define S5P_JPGX_L 0x24
+
+/* JPEG byte count register upper byte */
+#define S5P_JPGCNT_U 0x28
+
+/* JPEG byte count register middle byte */
+#define S5P_JPGCNT_M 0x2c
+
+/* JPEG byte count register lower byte */
+#define S5P_JPGCNT_L 0x30
+
+/* JPEG interrupt setting register */
+#define S5P_JPGINTSE 0x34
+#define S5P_RSTm_INT_EN_MASK (0x1 << 7)
+#define S5P_RSTm_INT_EN (0x1 << 7)
+#define S5P_DATA_NUM_INT_EN_MASK (0x1 << 6)
+#define S5P_DATA_NUM_INT_EN (0x1 << 6)
+#define S5P_FINAL_MCU_NUM_INT_EN_MASK (0x1 << 5)
+#define S5P_FINAL_MCU_NUM_INT_EN (0x1 << 5)
+
+/* JPEG interrupt status register */
+#define S5P_JPGINTST 0x38
+#define S5P_RESULT_STAT_SHIFT 6
+#define S5P_RESULT_STAT_MASK (0x1 << S5P_RESULT_STAT_SHIFT)
+#define S5P_STREAM_STAT_SHIFT 5
+#define S5P_STREAM_STAT_MASK (0x1 << S5P_STREAM_STAT_SHIFT)
+
+/* JPEG command register */
+#define S5P_JPGCOM 0x4c
+#define S5P_INT_RELEASE (0x1 << 2)
+
+/* Raw image data r/w address register */
+#define S5P_JPG_IMGADR 0x50
+
+/* JPEG file r/w address register */
+#define S5P_JPG_JPGADR 0x58
+
+/* Coefficient for RGB-to-YCbCr converter register */
+#define S5P_JPG_COEF(n) (0x5c + (((n) - 1) << 2))
+#define S5P_COEFn_SHIFT(j) ((3 - (j)) << 3)
+#define S5P_COEFn_MASK(j) (0xff << S5P_COEFn_SHIFT(j))
+
+/* JPEG color mode register */
+#define S5P_JPGCMOD 0x68
+#define S5P_MOD_SEL_MASK (0x7 << 5)
+#define S5P_MOD_SEL_422 (0x1 << 5)
+#define S5P_MOD_SEL_565 (0x2 << 5)
+#define S5P_MODE_Y16_MASK (0x1 << 1)
+#define S5P_MODE_Y16 (0x1 << 1)
+
+/* JPEG clock control register */
+#define S5P_JPGCLKCON 0x6c
+#define S5P_CLK_DOWN_READY (0x1 << 1)
+#define S5P_POWER_ON (0x1 << 0)
+
+/* JPEG start register */
+#define S5P_JSTART 0x70
+
+/* JPEG SW reset register */
+#define S5P_JPG_SW_RESET 0x78
+
+/* JPEG timer setting register */
+#define S5P_JPG_TIMER_SE 0x7c
+#define S5P_TIMER_INT_EN_MASK (0x1 << 31)
+#define S5P_TIMER_INT_EN (0x1 << 31)
+#define S5P_TIMER_INIT_MASK 0x7fffffff
+
+/* JPEG timer status register */
+#define S5P_JPG_TIMER_ST 0x80
+#define S5P_TIMER_INT_STAT_SHIFT 31
+#define S5P_TIMER_INT_STAT_MASK (0x1 << S5P_TIMER_INT_STAT_SHIFT)
+#define S5P_TIMER_CNT_SHIFT 0
+#define S5P_TIMER_CNT_MASK 0x7fffffff
+
+/* JPEG decompression output format register */
+#define S5P_JPG_OUTFORM 0x88
+#define S5P_DEC_OUT_FORMAT_MASK (0x1 << 0)
+#define S5P_DEC_OUT_FORMAT_422 (0x0 << 0)
+#define S5P_DEC_OUT_FORMAT_420 (0x1 << 0)
+
+/* JPEG version register */
+#define S5P_JPG_VERSION 0x8c
+
+/* JPEG compressed stream size interrupt setting register */
+#define S5P_JPG_ENC_STREAM_INTSE 0x98
+#define S5P_ENC_STREAM_INT_MASK (0x1 << 24)
+#define S5P_ENC_STREAM_INT_EN (0x1 << 24)
+#define S5P_ENC_STREAM_BOUND_MASK 0xffffff
+
+/* JPEG compressed stream size interrupt status register */
+#define S5P_JPG_ENC_STREAM_INTST 0x9c
+#define S5P_ENC_STREAM_INT_STAT_MASK 0x1
+
+/* JPEG quantizer table register */
+#define S5P_JPG_QTBL_CONTENT(n) (0x400 + (n) * 0x100)
+
+/* JPEG DC Huffman table register */
+#define S5P_JPG_HDCTBL(n) (0x800 + (n) * 0x400)
+
+/* JPEG DC Huffman table register */
+#define S5P_JPG_HDCTBLG(n) (0x840 + (n) * 0x400)
+
+/* JPEG AC Huffman table register */
+#define S5P_JPG_HACTBL(n) (0x880 + (n) * 0x400)
+
+/* JPEG AC Huffman table register */
+#define S5P_JPG_HACTBLG(n) (0x8c0 + (n) * 0x400)
+
+#endif /* JPEG_REGS_H_ */
+
diff --git a/drivers/media/video/s5p-mfc/Makefile b/drivers/media/video/s5p-mfc/Makefile
new file mode 100644
index 00000000..d0663409
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) := s5p-mfc.o
+s5p-mfc-y += s5p_mfc.o s5p_mfc_intr.o s5p_mfc_opr.o
+s5p-mfc-y += s5p_mfc_dec.o s5p_mfc_enc.o
+s5p-mfc-y += s5p_mfc_ctrl.o s5p_mfc_cmd.o
+s5p-mfc-y += s5p_mfc_pm.o s5p_mfc_shm.o
diff --git a/drivers/media/video/s5p-mfc/regs-mfc.h b/drivers/media/video/s5p-mfc/regs-mfc.h
new file mode 100644
index 00000000..053a8a87
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/regs-mfc.h
@@ -0,0 +1,413 @@
+/*
+ * Register definition file for Samsung MFC V5.1 Interface (FIMV) driver
+ *
+ * Kamil Debski, Copyright (c) 2010 Samsung Electronics
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef _REGS_FIMV_H
+#define _REGS_FIMV_H
+
+#define S5P_FIMV_REG_SIZE (S5P_FIMV_END_ADDR - S5P_FIMV_START_ADDR)
+#define S5P_FIMV_REG_COUNT ((S5P_FIMV_END_ADDR - S5P_FIMV_START_ADDR) / 4)
+
+/* Number of bits that the buffer address should be shifted for particular
+ * MFC buffers. */
+#define S5P_FIMV_START_ADDR 0x0000
+#define S5P_FIMV_END_ADDR 0xe008
+
+#define S5P_FIMV_SW_RESET 0x0000
+#define S5P_FIMV_RISC_HOST_INT 0x0008
+
+/* Command from HOST to RISC */
+#define S5P_FIMV_HOST2RISC_CMD 0x0030
+#define S5P_FIMV_HOST2RISC_ARG1 0x0034
+#define S5P_FIMV_HOST2RISC_ARG2 0x0038
+#define S5P_FIMV_HOST2RISC_ARG3 0x003c
+#define S5P_FIMV_HOST2RISC_ARG4 0x0040
+
+/* Command from RISC to HOST */
+#define S5P_FIMV_RISC2HOST_CMD 0x0044
+#define S5P_FIMV_RISC2HOST_CMD_MASK 0x1FFFF
+#define S5P_FIMV_RISC2HOST_ARG1 0x0048
+#define S5P_FIMV_RISC2HOST_ARG2 0x004c
+#define S5P_FIMV_RISC2HOST_ARG3 0x0050
+#define S5P_FIMV_RISC2HOST_ARG4 0x0054
+
+#define S5P_FIMV_FW_VERSION 0x0058
+#define S5P_FIMV_SYS_MEM_SZ 0x005c
+#define S5P_FIMV_FW_STATUS 0x0080
+
+/* Memory controller register */
+#define S5P_FIMV_MC_DRAMBASE_ADR_A 0x0508
+#define S5P_FIMV_MC_DRAMBASE_ADR_B 0x050c
+#define S5P_FIMV_MC_STATUS 0x0510
+
+/* Common register */
+#define S5P_FIMV_COMMON_BASE_A 0x0600
+#define S5P_FIMV_COMMON_BASE_B 0x0700
+
+/* Decoder */
+#define S5P_FIMV_DEC_CHROMA_ADR (S5P_FIMV_COMMON_BASE_A)
+#define S5P_FIMV_DEC_LUMA_ADR (S5P_FIMV_COMMON_BASE_B)
+
+/* H.264 decoding */
+#define S5P_FIMV_H264_VERT_NB_MV_ADR (S5P_FIMV_COMMON_BASE_A + 0x8c)
+ /* vertical neighbor motion vector */
+#define S5P_FIMV_H264_NB_IP_ADR (S5P_FIMV_COMMON_BASE_A + 0x90)
+ /* neighbor pixels for intra pred */
+#define S5P_FIMV_H264_MV_ADR (S5P_FIMV_COMMON_BASE_B + 0x80)
+ /* H264 motion vector */
+
+/* MPEG4 decoding */
+#define S5P_FIMV_MPEG4_NB_DCAC_ADR (S5P_FIMV_COMMON_BASE_A + 0x8c)
+ /* neighbor AC/DC coeff. */
+#define S5P_FIMV_MPEG4_UP_NB_MV_ADR (S5P_FIMV_COMMON_BASE_A + 0x90)
+ /* upper neighbor motion vector */
+#define S5P_FIMV_MPEG4_SA_MV_ADR (S5P_FIMV_COMMON_BASE_A + 0x94)
+ /* subseq. anchor motion vector */
+#define S5P_FIMV_MPEG4_OT_LINE_ADR (S5P_FIMV_COMMON_BASE_A + 0x98)
+ /* overlap transform line */
+#define S5P_FIMV_MPEG4_SP_ADR (S5P_FIMV_COMMON_BASE_A + 0xa8)
+ /* syntax parser */
+
+/* H.263 decoding */
+#define S5P_FIMV_H263_NB_DCAC_ADR (S5P_FIMV_COMMON_BASE_A + 0x8c)
+#define S5P_FIMV_H263_UP_NB_MV_ADR (S5P_FIMV_COMMON_BASE_A + 0x90)
+#define S5P_FIMV_H263_SA_MV_ADR (S5P_FIMV_COMMON_BASE_A + 0x94)
+#define S5P_FIMV_H263_OT_LINE_ADR (S5P_FIMV_COMMON_BASE_A + 0x98)
+
+/* VC-1 decoding */
+#define S5P_FIMV_VC1_NB_DCAC_ADR (S5P_FIMV_COMMON_BASE_A + 0x8c)
+#define S5P_FIMV_VC1_UP_NB_MV_ADR (S5P_FIMV_COMMON_BASE_A + 0x90)
+#define S5P_FIMV_VC1_SA_MV_ADR (S5P_FIMV_COMMON_BASE_A + 0x94)
+#define S5P_FIMV_VC1_OT_LINE_ADR (S5P_FIMV_COMMON_BASE_A + 0x98)
+#define S5P_FIMV_VC1_BITPLANE3_ADR (S5P_FIMV_COMMON_BASE_A + 0x9c)
+ /* bitplane3 */
+#define S5P_FIMV_VC1_BITPLANE2_ADR (S5P_FIMV_COMMON_BASE_A + 0xa0)
+ /* bitplane2 */
+#define S5P_FIMV_VC1_BITPLANE1_ADR (S5P_FIMV_COMMON_BASE_A + 0xa4)
+ /* bitplane1 */
+
+/* Encoder */
+#define S5P_FIMV_ENC_REF0_LUMA_ADR (S5P_FIMV_COMMON_BASE_A + 0x1c)
+#define S5P_FIMV_ENC_REF1_LUMA_ADR (S5P_FIMV_COMMON_BASE_A + 0x20)
+ /* reconstructed luma */
+#define S5P_FIMV_ENC_REF0_CHROMA_ADR (S5P_FIMV_COMMON_BASE_B)
+#define S5P_FIMV_ENC_REF1_CHROMA_ADR (S5P_FIMV_COMMON_BASE_B + 0x04)
+ /* reconstructed chroma */
+#define S5P_FIMV_ENC_REF2_LUMA_ADR (S5P_FIMV_COMMON_BASE_B + 0x10)
+#define S5P_FIMV_ENC_REF2_CHROMA_ADR (S5P_FIMV_COMMON_BASE_B + 0x08)
+#define S5P_FIMV_ENC_REF3_LUMA_ADR (S5P_FIMV_COMMON_BASE_B + 0x14)
+#define S5P_FIMV_ENC_REF3_CHROMA_ADR (S5P_FIMV_COMMON_BASE_B + 0x0c)
+
+/* H.264 encoding */
+#define S5P_FIMV_H264_UP_MV_ADR (S5P_FIMV_COMMON_BASE_A)
+ /* upper motion vector */
+#define S5P_FIMV_H264_NBOR_INFO_ADR (S5P_FIMV_COMMON_BASE_A + 0x04)
+ /* entropy engine's neighbor info. */
+#define S5P_FIMV_H264_UP_INTRA_MD_ADR (S5P_FIMV_COMMON_BASE_A + 0x08)
+ /* upper intra MD */
+#define S5P_FIMV_H264_COZERO_FLAG_ADR (S5P_FIMV_COMMON_BASE_A + 0x10)
+ /* direct cozero flag */
+#define S5P_FIMV_H264_UP_INTRA_PRED_ADR (S5P_FIMV_COMMON_BASE_B + 0x40)
+ /* upper intra PRED */
+
+/* H.263 encoding */
+#define S5P_FIMV_H263_UP_MV_ADR (S5P_FIMV_COMMON_BASE_A)
+ /* upper motion vector */
+#define S5P_FIMV_H263_ACDC_COEF_ADR (S5P_FIMV_COMMON_BASE_A + 0x04)
+ /* upper Q coeff. */
+
+/* MPEG4 encoding */
+#define S5P_FIMV_MPEG4_UP_MV_ADR (S5P_FIMV_COMMON_BASE_A)
+ /* upper motion vector */
+#define S5P_FIMV_MPEG4_ACDC_COEF_ADR (S5P_FIMV_COMMON_BASE_A + 0x04)
+ /* upper Q coeff. */
+#define S5P_FIMV_MPEG4_COZERO_FLAG_ADR (S5P_FIMV_COMMON_BASE_A + 0x10)
+ /* direct cozero flag */
+
+#define S5P_FIMV_ENC_REF_B_LUMA_ADR 0x062c /* ref B Luma addr */
+#define S5P_FIMV_ENC_REF_B_CHROMA_ADR 0x0630 /* ref B Chroma addr */
+
+#define S5P_FIMV_ENC_CUR_LUMA_ADR 0x0718 /* current Luma addr */
+#define S5P_FIMV_ENC_CUR_CHROMA_ADR 0x071C /* current Chroma addr */
+
+/* Codec common register */
+#define S5P_FIMV_ENC_HSIZE_PX 0x0818 /* frame width at encoder */
+#define S5P_FIMV_ENC_VSIZE_PX 0x081c /* frame height at encoder */
+#define S5P_FIMV_ENC_PROFILE 0x0830 /* profile register */
+#define S5P_FIMV_ENC_PROFILE_H264_MAIN 0
+#define S5P_FIMV_ENC_PROFILE_H264_HIGH 1
+#define S5P_FIMV_ENC_PROFILE_H264_BASELINE 2
+#define S5P_FIMV_ENC_PROFILE_MPEG4_SIMPLE 0
+#define S5P_FIMV_ENC_PROFILE_MPEG4_ADVANCED_SIMPLE 1
+#define S5P_FIMV_ENC_PIC_STRUCT 0x083c /* picture field/frame flag */
+#define S5P_FIMV_ENC_LF_CTRL 0x0848 /* loop filter control */
+#define S5P_FIMV_ENC_ALPHA_OFF 0x084c /* loop filter alpha offset */
+#define S5P_FIMV_ENC_BETA_OFF 0x0850 /* loop filter beta offset */
+#define S5P_FIMV_MR_BUSIF_CTRL 0x0854 /* hidden, bus interface ctrl */
+#define S5P_FIMV_ENC_PXL_CACHE_CTRL 0x0a00 /* pixel cache control */
+
+/* Channel & stream interface register */
+#define S5P_FIMV_SI_RTN_CHID 0x2000 /* Return CH inst ID register */
+#define S5P_FIMV_SI_CH0_INST_ID 0x2040 /* codec instance ID */
+#define S5P_FIMV_SI_CH1_INST_ID 0x2080 /* codec instance ID */
+/* Decoder */
+#define S5P_FIMV_SI_VRESOL 0x2004 /* vertical res of decoder */
+#define S5P_FIMV_SI_HRESOL 0x2008 /* horizontal res of decoder */
+#define S5P_FIMV_SI_BUF_NUMBER 0x200c /* number of frames in the
+ decoded pic */
+#define S5P_FIMV_SI_DISPLAY_Y_ADR 0x2010 /* luma addr of displayed pic */
+#define S5P_FIMV_SI_DISPLAY_C_ADR 0x2014 /* chroma addrof displayed pic */
+#define S5P_FIMV_SI_CONSUMED_BYTES 0x2018 /* Consumed number of bytes to
+ decode a frame */
+#define S5P_FIMV_SI_DISPLAY_STATUS 0x201c /* status of decoded picture */
+
+#define S5P_FIMV_SI_CH0_SB_ST_ADR 0x2044 /* start addr of stream buf */
+#define S5P_FIMV_SI_CH0_SB_FRM_SIZE 0x2048 /* size of stream buf */
+#define S5P_FIMV_SI_CH0_DESC_ADR 0x204c /* addr of descriptor buf */
+#define S5P_FIMV_SI_CH0_CPB_SIZE 0x2058 /* max size of coded pic. buf */
+#define S5P_FIMV_SI_CH0_DESC_SIZE 0x205c /* max size of descriptor buf */
+
+#define S5P_FIMV_SI_CH1_SB_ST_ADR 0x2084 /* start addr of stream buf */
+#define S5P_FIMV_SI_CH1_SB_FRM_SIZE 0x2088 /* size of stream buf */
+#define S5P_FIMV_SI_CH1_DESC_ADR 0x208c /* addr of descriptor buf */
+#define S5P_FIMV_SI_CH1_CPB_SIZE 0x2098 /* max size of coded pic. buf */
+#define S5P_FIMV_SI_CH1_DESC_SIZE 0x209c /* max size of descriptor buf */
+
+#define S5P_FIMV_CRC_LUMA0 0x2030 /* luma crc data per frame
+ (top field) */
+#define S5P_FIMV_CRC_CHROMA0 0x2034 /* chroma crc data per frame
+ (top field) */
+#define S5P_FIMV_CRC_LUMA1 0x2038 /* luma crc data per bottom
+ field */
+#define S5P_FIMV_CRC_CHROMA1 0x203c /* chroma crc data per bottom
+ field */
+
+/* Display status */
+#define S5P_FIMV_DEC_STATUS_DECODING_ONLY 0
+#define S5P_FIMV_DEC_STATUS_DECODING_DISPLAY 1
+#define S5P_FIMV_DEC_STATUS_DISPLAY_ONLY 2
+#define S5P_FIMV_DEC_STATUS_DECODING_EMPTY 3
+#define S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK 7
+#define S5P_FIMV_DEC_STATUS_PROGRESSIVE (0<<3)
+#define S5P_FIMV_DEC_STATUS_INTERLACE (1<<3)
+#define S5P_FIMV_DEC_STATUS_INTERLACE_MASK (1<<3)
+#define S5P_FIMV_DEC_STATUS_CRC_NUMBER_TWO (0<<4)
+#define S5P_FIMV_DEC_STATUS_CRC_NUMBER_FOUR (1<<4)
+#define S5P_FIMV_DEC_STATUS_CRC_NUMBER_MASK (1<<4)
+#define S5P_FIMV_DEC_STATUS_CRC_GENERATED (1<<5)
+#define S5P_FIMV_DEC_STATUS_CRC_NOT_GENERATED (0<<5)
+#define S5P_FIMV_DEC_STATUS_CRC_MASK (1<<5)
+
+#define S5P_FIMV_DEC_STATUS_RESOLUTION_MASK (3<<4)
+#define S5P_FIMV_DEC_STATUS_RESOLUTION_INC (1<<4)
+#define S5P_FIMV_DEC_STATUS_RESOLUTION_DEC (2<<4)
+
+/* Decode frame address */
+#define S5P_FIMV_DECODE_Y_ADR 0x2024
+#define S5P_FIMV_DECODE_C_ADR 0x2028
+
+/* Decoded frame tpe */
+#define S5P_FIMV_DECODE_FRAME_TYPE 0x2020
+#define S5P_FIMV_DECODE_FRAME_MASK 7
+
+#define S5P_FIMV_DECODE_FRAME_SKIPPED 0
+#define S5P_FIMV_DECODE_FRAME_I_FRAME 1
+#define S5P_FIMV_DECODE_FRAME_P_FRAME 2
+#define S5P_FIMV_DECODE_FRAME_B_FRAME 3
+#define S5P_FIMV_DECODE_FRAME_OTHER_FRAME 4
+
+/* Sizes of buffers required for decoding */
+#define S5P_FIMV_DEC_NB_IP_SIZE (32 * 1024)
+#define S5P_FIMV_DEC_VERT_NB_MV_SIZE (16 * 1024)
+#define S5P_FIMV_DEC_NB_DCAC_SIZE (16 * 1024)
+#define S5P_FIMV_DEC_UPNB_MV_SIZE (68 * 1024)
+#define S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE (136 * 1024)
+#define S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE (32 * 1024)
+#define S5P_FIMV_DEC_VC1_BITPLANE_SIZE (2 * 1024)
+#define S5P_FIMV_DEC_STX_PARSER_SIZE (68 * 1024)
+
+#define S5P_FIMV_DEC_BUF_ALIGN (8 * 1024)
+#define S5P_FIMV_ENC_BUF_ALIGN (8 * 1024)
+#define S5P_FIMV_NV12M_HALIGN 16
+#define S5P_FIMV_NV12M_LVALIGN 16
+#define S5P_FIMV_NV12M_CVALIGN 8
+#define S5P_FIMV_NV12MT_HALIGN 128
+#define S5P_FIMV_NV12MT_VALIGN 32
+#define S5P_FIMV_NV12M_SALIGN 2048
+#define S5P_FIMV_NV12MT_SALIGN 8192
+
+/* Sizes of buffers required for encoding */
+#define S5P_FIMV_ENC_UPMV_SIZE 0x10000
+#define S5P_FIMV_ENC_COLFLG_SIZE 0x10000
+#define S5P_FIMV_ENC_INTRAMD_SIZE 0x10000
+#define S5P_FIMV_ENC_INTRAPRED_SIZE 0x4000
+#define S5P_FIMV_ENC_NBORINFO_SIZE 0x10000
+#define S5P_FIMV_ENC_ACDCCOEF_SIZE 0x10000
+
+/* Encoder */
+#define S5P_FIMV_ENC_SI_STRM_SIZE 0x2004 /* stream size */
+#define S5P_FIMV_ENC_SI_PIC_CNT 0x2008 /* picture count */
+#define S5P_FIMV_ENC_SI_WRITE_PTR 0x200c /* write pointer */
+#define S5P_FIMV_ENC_SI_SLICE_TYPE 0x2010 /* slice type(I/P/B/IDR) */
+#define S5P_FIMV_ENC_SI_SLICE_TYPE_NON_CODED 0
+#define S5P_FIMV_ENC_SI_SLICE_TYPE_I 1
+#define S5P_FIMV_ENC_SI_SLICE_TYPE_P 2
+#define S5P_FIMV_ENC_SI_SLICE_TYPE_B 3
+#define S5P_FIMV_ENC_SI_SLICE_TYPE_SKIPPED 4
+#define S5P_FIMV_ENC_SI_SLICE_TYPE_OTHERS 5
+#define S5P_FIMV_ENCODED_Y_ADDR 0x2014 /* the addr of the encoded
+ luma pic */
+#define S5P_FIMV_ENCODED_C_ADDR 0x2018 /* the addr of the encoded
+ chroma pic */
+
+#define S5P_FIMV_ENC_SI_CH0_SB_ADR 0x2044 /* addr of stream buf */
+#define S5P_FIMV_ENC_SI_CH0_SB_SIZE 0x204c /* size of stream buf */
+#define S5P_FIMV_ENC_SI_CH0_CUR_Y_ADR 0x2050 /* current Luma addr */
+#define S5P_FIMV_ENC_SI_CH0_CUR_C_ADR 0x2054 /* current Chroma addr */
+#define S5P_FIMV_ENC_SI_CH0_FRAME_INS 0x2058 /* frame insertion */
+
+#define S5P_FIMV_ENC_SI_CH1_SB_ADR 0x2084 /* addr of stream buf */
+#define S5P_FIMV_ENC_SI_CH1_SB_SIZE 0x208c /* size of stream buf */
+#define S5P_FIMV_ENC_SI_CH1_CUR_Y_ADR 0x2090 /* current Luma addr */
+#define S5P_FIMV_ENC_SI_CH1_CUR_C_ADR 0x2094 /* current Chroma addr */
+#define S5P_FIMV_ENC_SI_CH1_FRAME_INS 0x2098 /* frame insertion */
+
+#define S5P_FIMV_ENC_PIC_TYPE_CTRL 0xc504 /* pic type level control */
+#define S5P_FIMV_ENC_B_RECON_WRITE_ON 0xc508 /* B frame recon write ctrl */
+#define S5P_FIMV_ENC_MSLICE_CTRL 0xc50c /* multi slice control */
+#define S5P_FIMV_ENC_MSLICE_MB 0xc510 /* MB number in the one slice */
+#define S5P_FIMV_ENC_MSLICE_BIT 0xc514 /* bit count for one slice */
+#define S5P_FIMV_ENC_CIR_CTRL 0xc518 /* number of intra refresh MB */
+#define S5P_FIMV_ENC_MAP_FOR_CUR 0xc51c /* linear or tiled mode */
+#define S5P_FIMV_ENC_PADDING_CTRL 0xc520 /* padding control */
+
+#define S5P_FIMV_ENC_RC_CONFIG 0xc5a0 /* RC config */
+#define S5P_FIMV_ENC_RC_BIT_RATE 0xc5a8 /* bit rate */
+#define S5P_FIMV_ENC_RC_QBOUND 0xc5ac /* max/min QP */
+#define S5P_FIMV_ENC_RC_RPARA 0xc5b0 /* rate control reaction coeff */
+#define S5P_FIMV_ENC_RC_MB_CTRL 0xc5b4 /* MB adaptive scaling */
+
+/* Encoder for H264 only */
+#define S5P_FIMV_ENC_H264_ENTROPY_MODE 0xd004 /* CAVLC or CABAC */
+#define S5P_FIMV_ENC_H264_ALPHA_OFF 0xd008 /* loop filter alpha offset */
+#define S5P_FIMV_ENC_H264_BETA_OFF 0xd00c /* loop filter beta offset */
+#define S5P_FIMV_ENC_H264_NUM_OF_REF 0xd010 /* number of reference for P/B */
+#define S5P_FIMV_ENC_H264_TRANS_FLAG 0xd034 /* 8x8 transform flag in PPS &
+ high profile */
+
+#define S5P_FIMV_ENC_RC_FRAME_RATE 0xd0d0 /* frame rate */
+
+/* Encoder for MPEG4 only */
+#define S5P_FIMV_ENC_MPEG4_QUART_PXL 0xe008 /* qpel interpolation ctrl */
+
+/* Additional */
+#define S5P_FIMV_SI_CH0_DPB_CONF_CTRL 0x2068 /* DPB Config Control Register */
+#define S5P_FIMV_SLICE_INT_MASK 1
+#define S5P_FIMV_SLICE_INT_SHIFT 31
+#define S5P_FIMV_DDELAY_ENA_SHIFT 30
+#define S5P_FIMV_DDELAY_VAL_MASK 0xff
+#define S5P_FIMV_DDELAY_VAL_SHIFT 16
+#define S5P_FIMV_DPB_COUNT_MASK 0xffff
+#define S5P_FIMV_DPB_FLUSH_MASK 1
+#define S5P_FIMV_DPB_FLUSH_SHIFT 14
+
+
+#define S5P_FIMV_SI_CH0_RELEASE_BUF 0x2060 /* DPB release buffer register */
+#define S5P_FIMV_SI_CH0_HOST_WR_ADR 0x2064 /* address of shared memory */
+
+/* Codec numbers */
+#define S5P_FIMV_CODEC_NONE -1
+
+#define S5P_FIMV_CODEC_H264_DEC 0
+#define S5P_FIMV_CODEC_VC1_DEC 1
+#define S5P_FIMV_CODEC_MPEG4_DEC 2
+#define S5P_FIMV_CODEC_MPEG2_DEC 3
+#define S5P_FIMV_CODEC_H263_DEC 4
+#define S5P_FIMV_CODEC_VC1RCV_DEC 5
+
+#define S5P_FIMV_CODEC_H264_ENC 16
+#define S5P_FIMV_CODEC_MPEG4_ENC 17
+#define S5P_FIMV_CODEC_H263_ENC 18
+
+/* Channel Control Register */
+#define S5P_FIMV_CH_SEQ_HEADER 1
+#define S5P_FIMV_CH_FRAME_START 2
+#define S5P_FIMV_CH_LAST_FRAME 3
+#define S5P_FIMV_CH_INIT_BUFS 4
+#define S5P_FIMV_CH_FRAME_START_REALLOC 5
+#define S5P_FIMV_CH_MASK 7
+#define S5P_FIMV_CH_SHIFT 16
+
+
+/* Host to RISC command */
+#define S5P_FIMV_H2R_CMD_EMPTY 0
+#define S5P_FIMV_H2R_CMD_OPEN_INSTANCE 1
+#define S5P_FIMV_H2R_CMD_CLOSE_INSTANCE 2
+#define S5P_FIMV_H2R_CMD_SYS_INIT 3
+#define S5P_FIMV_H2R_CMD_FLUSH 4
+#define S5P_FIMV_H2R_CMD_SLEEP 5
+#define S5P_FIMV_H2R_CMD_WAKEUP 6
+
+#define S5P_FIMV_R2H_CMD_EMPTY 0
+#define S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET 1
+#define S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET 2
+#define S5P_FIMV_R2H_CMD_RSV_RET 3
+#define S5P_FIMV_R2H_CMD_SEQ_DONE_RET 4
+#define S5P_FIMV_R2H_CMD_FRAME_DONE_RET 5
+#define S5P_FIMV_R2H_CMD_SLICE_DONE_RET 6
+#define S5P_FIMV_R2H_CMD_ENC_COMPLETE_RET 7
+#define S5P_FIMV_R2H_CMD_SYS_INIT_RET 8
+#define S5P_FIMV_R2H_CMD_FW_STATUS_RET 9
+#define S5P_FIMV_R2H_CMD_SLEEP_RET 10
+#define S5P_FIMV_R2H_CMD_WAKEUP_RET 11
+#define S5P_FIMV_R2H_CMD_FLUSH_RET 12
+#define S5P_FIMV_R2H_CMD_INIT_BUFFERS_RET 15
+#define S5P_FIMV_R2H_CMD_EDFU_INIT_RET 16
+#define S5P_FIMV_R2H_CMD_ERR_RET 32
+
+/* Error handling defines */
+#define S5P_FIMV_ERR_WARNINGS_START 145
+#define S5P_FIMV_ERR_DEC_MASK 0xFFFF
+#define S5P_FIMV_ERR_DEC_SHIFT 0
+#define S5P_FIMV_ERR_DSPL_MASK 0xFFFF0000
+#define S5P_FIMV_ERR_DSPL_SHIFT 16
+
+/* Shared memory registers' offsets */
+
+/* An offset of the start position in the stream when
+ * the start position is not aligned */
+#define S5P_FIMV_SHARED_CROP_INFO_H 0x0020
+#define S5P_FIMV_SHARED_CROP_LEFT_MASK 0xFFFF
+#define S5P_FIMV_SHARED_CROP_LEFT_SHIFT 0
+#define S5P_FIMV_SHARED_CROP_RIGHT_MASK 0xFFFF0000
+#define S5P_FIMV_SHARED_CROP_RIGHT_SHIFT 16
+#define S5P_FIMV_SHARED_CROP_INFO_V 0x0024
+#define S5P_FIMV_SHARED_CROP_TOP_MASK 0xFFFF
+#define S5P_FIMV_SHARED_CROP_TOP_SHIFT 0
+#define S5P_FIMV_SHARED_CROP_BOTTOM_MASK 0xFFFF0000
+#define S5P_FIMV_SHARED_CROP_BOTTOM_SHIFT 16
+#define S5P_FIMV_SHARED_SET_FRAME_TAG 0x0004
+#define S5P_FIMV_SHARED_GET_FRAME_TAG_TOP 0x0008
+#define S5P_FIMV_SHARED_GET_FRAME_TAG_BOT 0x000C
+#define S5P_FIMV_SHARED_START_BYTE_NUM 0x0018
+#define S5P_FIMV_SHARED_RC_VOP_TIMING 0x0030
+#define S5P_FIMV_SHARED_LUMA_DPB_SIZE 0x0064
+#define S5P_FIMV_SHARED_CHROMA_DPB_SIZE 0x0068
+#define S5P_FIMV_SHARED_MV_SIZE 0x006C
+#define S5P_FIMV_SHARED_PIC_TIME_TOP 0x0010
+#define S5P_FIMV_SHARED_PIC_TIME_BOTTOM 0x0014
+#define S5P_FIMV_SHARED_EXT_ENC_CONTROL 0x0028
+#define S5P_FIMV_SHARED_P_B_FRAME_QP 0x0070
+#define S5P_FIMV_SHARED_ASPECT_RATIO_IDC 0x0074
+#define S5P_FIMV_SHARED_EXTENDED_SAR 0x0078
+#define S5P_FIMV_SHARED_H264_I_PERIOD 0x009C
+#define S5P_FIMV_SHARED_RC_CONTROL_CONFIG 0x00A0
+
+#endif /* _REGS_FIMV_H */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc.c b/drivers/media/video/s5p-mfc/s5p_mfc.c
new file mode 100644
index 00000000..83fe461a
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc.c
@@ -0,0 +1,1252 @@
+/*
+ * Samsung S5P Multi Format Codec v 5.1
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Kamil Debski, <k.debski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/workqueue.h>
+#include <media/videobuf2-core.h>
+#include "regs-mfc.h"
+#include "s5p_mfc_ctrl.h"
+#include "s5p_mfc_debug.h"
+#include "s5p_mfc_dec.h"
+#include "s5p_mfc_enc.h"
+#include "s5p_mfc_intr.h"
+#include "s5p_mfc_opr.h"
+#include "s5p_mfc_pm.h"
+#include "s5p_mfc_shm.h"
+
+#define S5P_MFC_NAME "s5p-mfc"
+#define S5P_MFC_DEC_NAME "s5p-mfc-dec"
+#define S5P_MFC_ENC_NAME "s5p-mfc-enc"
+
+int debug;
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug level - higher value produces more verbose messages");
+
+/* Helper functions for interrupt processing */
+/* Remove from hw execution round robin */
+static void clear_work_bit(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ spin_lock(&dev->condlock);
+ clear_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock(&dev->condlock);
+}
+
+/* Wake up context wait_queue */
+static void wake_up_ctx(struct s5p_mfc_ctx *ctx, unsigned int reason,
+ unsigned int err)
+{
+ ctx->int_cond = 1;
+ ctx->int_type = reason;
+ ctx->int_err = err;
+ wake_up(&ctx->queue);
+}
+
+/* Wake up device wait_queue */
+static void wake_up_dev(struct s5p_mfc_dev *dev, unsigned int reason,
+ unsigned int err)
+{
+ dev->int_cond = 1;
+ dev->int_type = reason;
+ dev->int_err = err;
+ wake_up(&dev->queue);
+}
+
+void s5p_mfc_watchdog(unsigned long arg)
+{
+ struct s5p_mfc_dev *dev = (struct s5p_mfc_dev *)arg;
+
+ if (test_bit(0, &dev->hw_lock))
+ atomic_inc(&dev->watchdog_cnt);
+ if (atomic_read(&dev->watchdog_cnt) >= MFC_WATCHDOG_CNT) {
+ /* This means that hw is busy and no interrupts were
+ * generated by hw for the Nth time of running this
+ * watchdog timer. This usually means a serious hw
+ * error. Now it is time to kill all instances and
+ * reset the MFC. */
+ mfc_err("Time out during waiting for HW\n");
+ queue_work(dev->watchdog_workqueue, &dev->watchdog_work);
+ }
+ dev->watchdog_timer.expires = jiffies +
+ msecs_to_jiffies(MFC_WATCHDOG_INTERVAL);
+ add_timer(&dev->watchdog_timer);
+}
+
+static void s5p_mfc_watchdog_worker(struct work_struct *work)
+{
+ struct s5p_mfc_dev *dev;
+ struct s5p_mfc_ctx *ctx;
+ unsigned long flags;
+ int mutex_locked;
+ int i, ret;
+
+ dev = container_of(work, struct s5p_mfc_dev, watchdog_work);
+
+ mfc_err("Driver timeout error handling\n");
+ /* Lock the mutex that protects open and release.
+ * This is necessary as they may load and unload firmware. */
+ mutex_locked = mutex_trylock(&dev->mfc_mutex);
+ if (!mutex_locked)
+ mfc_err("Error: some instance may be closing/opening\n");
+ spin_lock_irqsave(&dev->irqlock, flags);
+
+ s5p_mfc_clock_off();
+
+ for (i = 0; i < MFC_NUM_CONTEXTS; i++) {
+ ctx = dev->ctx[i];
+ if (!ctx)
+ continue;
+ ctx->state = MFCINST_ERROR;
+ s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
+ s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
+ clear_work_bit(ctx);
+ wake_up_ctx(ctx, S5P_FIMV_R2H_CMD_ERR_RET, 0);
+ }
+ clear_bit(0, &dev->hw_lock);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ /* Double check if there is at least one instance running.
+ * If no instance is in memory than no firmware should be present */
+ if (dev->num_inst > 0) {
+ ret = s5p_mfc_reload_firmware(dev);
+ if (ret) {
+ mfc_err("Failed to reload FW\n");
+ goto unlock;
+ }
+ s5p_mfc_clock_on();
+ ret = s5p_mfc_init_hw(dev);
+ if (ret)
+ mfc_err("Failed to reinit FW\n");
+ }
+unlock:
+ if (mutex_locked)
+ mutex_unlock(&dev->mfc_mutex);
+}
+
+static enum s5p_mfc_node_type s5p_mfc_get_node_type(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (!vdev) {
+ mfc_err("failed to get video_device");
+ return MFCNODE_INVALID;
+ }
+ if (vdev->index == 0)
+ return MFCNODE_DECODER;
+ else if (vdev->index == 1)
+ return MFCNODE_ENCODER;
+ return MFCNODE_INVALID;
+}
+
+static void s5p_mfc_clear_int_flags(struct s5p_mfc_dev *dev)
+{
+ mfc_write(dev, 0, S5P_FIMV_RISC_HOST_INT);
+ mfc_write(dev, 0, S5P_FIMV_RISC2HOST_CMD);
+ mfc_write(dev, 0xffff, S5P_FIMV_SI_RTN_CHID);
+}
+
+static void s5p_mfc_handle_frame_all_extracted(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_buf *dst_buf;
+
+ ctx->state = MFCINST_FINISHED;
+ ctx->sequence++;
+ while (!list_empty(&ctx->dst_queue)) {
+ dst_buf = list_entry(ctx->dst_queue.next,
+ struct s5p_mfc_buf, list);
+ mfc_debug(2, "Cleaning up buffer: %d\n",
+ dst_buf->b->v4l2_buf.index);
+ vb2_set_plane_payload(dst_buf->b, 0, 0);
+ vb2_set_plane_payload(dst_buf->b, 1, 0);
+ list_del(&dst_buf->list);
+ ctx->dst_queue_cnt--;
+ dst_buf->b->v4l2_buf.sequence = (ctx->sequence++);
+
+ if (s5p_mfc_read_shm(ctx, PIC_TIME_TOP) ==
+ s5p_mfc_read_shm(ctx, PIC_TIME_BOT))
+ dst_buf->b->v4l2_buf.field = V4L2_FIELD_NONE;
+ else
+ dst_buf->b->v4l2_buf.field = V4L2_FIELD_INTERLACED;
+
+ ctx->dec_dst_flag &= ~(1 << dst_buf->b->v4l2_buf.index);
+ vb2_buffer_done(dst_buf->b, VB2_BUF_STATE_DONE);
+ }
+}
+
+static void s5p_mfc_handle_frame_copy_time(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_buf *dst_buf, *src_buf;
+ size_t dec_y_addr = s5p_mfc_get_dec_y_adr();
+ unsigned int frame_type = s5p_mfc_get_frame_type();
+
+ /* Copy timestamp / timecode from decoded src to dst and set
+ appropraite flags */
+ src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
+ list_for_each_entry(dst_buf, &ctx->dst_queue, list) {
+ if (vb2_dma_contig_plane_dma_addr(dst_buf->b, 0) == dec_y_addr) {
+ memcpy(&dst_buf->b->v4l2_buf.timecode,
+ &src_buf->b->v4l2_buf.timecode,
+ sizeof(struct v4l2_timecode));
+ memcpy(&dst_buf->b->v4l2_buf.timestamp,
+ &src_buf->b->v4l2_buf.timestamp,
+ sizeof(struct timeval));
+ switch (frame_type) {
+ case S5P_FIMV_DECODE_FRAME_I_FRAME:
+ dst_buf->b->v4l2_buf.flags |=
+ V4L2_BUF_FLAG_KEYFRAME;
+ break;
+ case S5P_FIMV_DECODE_FRAME_P_FRAME:
+ dst_buf->b->v4l2_buf.flags |=
+ V4L2_BUF_FLAG_PFRAME;
+ break;
+ case S5P_FIMV_DECODE_FRAME_B_FRAME:
+ dst_buf->b->v4l2_buf.flags |=
+ V4L2_BUF_FLAG_BFRAME;
+ break;
+ }
+ break;
+ }
+ }
+}
+
+static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_buf *dst_buf;
+ size_t dspl_y_addr = s5p_mfc_get_dspl_y_adr();
+ unsigned int frame_type = s5p_mfc_get_frame_type();
+ unsigned int index;
+
+ /* If frame is same as previous then skip and do not dequeue */
+ if (frame_type == S5P_FIMV_DECODE_FRAME_SKIPPED) {
+ if (!ctx->after_packed_pb)
+ ctx->sequence++;
+ ctx->after_packed_pb = 0;
+ return;
+ }
+ ctx->sequence++;
+ /* The MFC returns address of the buffer, now we have to
+ * check which videobuf does it correspond to */
+ list_for_each_entry(dst_buf, &ctx->dst_queue, list) {
+ /* Check if this is the buffer we're looking for */
+ if (vb2_dma_contig_plane_dma_addr(dst_buf->b, 0) == dspl_y_addr) {
+ list_del(&dst_buf->list);
+ ctx->dst_queue_cnt--;
+ dst_buf->b->v4l2_buf.sequence = ctx->sequence;
+ if (s5p_mfc_read_shm(ctx, PIC_TIME_TOP) ==
+ s5p_mfc_read_shm(ctx, PIC_TIME_BOT))
+ dst_buf->b->v4l2_buf.field = V4L2_FIELD_NONE;
+ else
+ dst_buf->b->v4l2_buf.field =
+ V4L2_FIELD_INTERLACED;
+ vb2_set_plane_payload(dst_buf->b, 0, ctx->luma_size);
+ vb2_set_plane_payload(dst_buf->b, 1, ctx->chroma_size);
+ clear_bit(dst_buf->b->v4l2_buf.index,
+ &ctx->dec_dst_flag);
+
+ vb2_buffer_done(dst_buf->b,
+ err ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+
+ index = dst_buf->b->v4l2_buf.index;
+ break;
+ }
+ }
+}
+
+/* Handle frame decoding interrupt */
+static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx,
+ unsigned int reason, unsigned int err)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned int dst_frame_status;
+ struct s5p_mfc_buf *src_buf;
+ unsigned long flags;
+ unsigned int res_change;
+
+ unsigned int index;
+
+ dst_frame_status = s5p_mfc_get_dspl_status()
+ & S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK;
+ res_change = s5p_mfc_get_dspl_status()
+ & S5P_FIMV_DEC_STATUS_RESOLUTION_MASK;
+ mfc_debug(2, "Frame Status: %x\n", dst_frame_status);
+ if (ctx->state == MFCINST_RES_CHANGE_INIT)
+ ctx->state = MFCINST_RES_CHANGE_FLUSH;
+ if (res_change) {
+ ctx->state = MFCINST_RES_CHANGE_INIT;
+ s5p_mfc_clear_int_flags(dev);
+ wake_up_ctx(ctx, reason, err);
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ BUG();
+ s5p_mfc_clock_off();
+ s5p_mfc_try_run(dev);
+ return;
+ }
+ if (ctx->dpb_flush_flag)
+ ctx->dpb_flush_flag = 0;
+
+ spin_lock_irqsave(&dev->irqlock, flags);
+ /* All frames remaining in the buffer have been extracted */
+ if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_EMPTY) {
+ if (ctx->state == MFCINST_RES_CHANGE_FLUSH) {
+ s5p_mfc_handle_frame_all_extracted(ctx);
+ ctx->state = MFCINST_RES_CHANGE_END;
+ goto leave_handle_frame;
+ } else {
+ s5p_mfc_handle_frame_all_extracted(ctx);
+ }
+ }
+
+ if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY ||
+ dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_ONLY)
+ s5p_mfc_handle_frame_copy_time(ctx);
+
+ /* A frame has been decoded and is in the buffer */
+ if (dst_frame_status == S5P_FIMV_DEC_STATUS_DISPLAY_ONLY ||
+ dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY) {
+ s5p_mfc_handle_frame_new(ctx, err);
+ } else {
+ mfc_debug(2, "No frame decode\n");
+ }
+ /* Mark source buffer as complete */
+ if (dst_frame_status != S5P_FIMV_DEC_STATUS_DISPLAY_ONLY
+ && !list_empty(&ctx->src_queue)) {
+ src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf,
+ list);
+ ctx->consumed_stream += s5p_mfc_get_consumed_stream();
+ if (ctx->codec_mode != S5P_FIMV_CODEC_H264_DEC &&
+ s5p_mfc_get_frame_type() == S5P_FIMV_DECODE_FRAME_P_FRAME
+ && ctx->consumed_stream + STUFF_BYTE <
+ src_buf->b->v4l2_planes[0].bytesused) {
+ /* Run MFC again on the same buffer */
+ mfc_debug(2, "Running again the same buffer\n");
+ ctx->after_packed_pb = 1;
+ } else {
+ index = src_buf->b->v4l2_buf.index;
+ mfc_debug(2, "MFC needs next buffer\n");
+ ctx->consumed_stream = 0;
+ list_del(&src_buf->list);
+ ctx->src_queue_cnt--;
+ if (s5p_mfc_err_dec(err) > 0)
+ vb2_buffer_done(src_buf->b, VB2_BUF_STATE_ERROR);
+ else
+ vb2_buffer_done(src_buf->b, VB2_BUF_STATE_DONE);
+ }
+ }
+leave_handle_frame:
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ if ((ctx->src_queue_cnt == 0 && ctx->state != MFCINST_FINISHING)
+ || ctx->dst_queue_cnt < ctx->dpb_count)
+ clear_work_bit(ctx);
+ s5p_mfc_clear_int_flags(dev);
+ wake_up_ctx(ctx, reason, err);
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ BUG();
+ s5p_mfc_clock_off();
+ s5p_mfc_try_run(dev);
+}
+
+/* Error handling for interrupt */
+static void s5p_mfc_handle_error(struct s5p_mfc_ctx *ctx,
+ unsigned int reason, unsigned int err)
+{
+ struct s5p_mfc_dev *dev;
+ unsigned long flags;
+
+ /* If no context is available then all necessary
+ * processing has been done. */
+ if (ctx == 0)
+ return;
+
+ dev = ctx->dev;
+ mfc_err("Interrupt Error: %08x\n", err);
+ s5p_mfc_clear_int_flags(dev);
+ wake_up_dev(dev, reason, err);
+
+ /* Error recovery is dependent on the state of context */
+ switch (ctx->state) {
+ case MFCINST_INIT:
+ /* This error had to happen while acquireing instance */
+ case MFCINST_GOT_INST:
+ /* This error had to happen while parsing the header */
+ case MFCINST_HEAD_PARSED:
+ /* This error had to happen while setting dst buffers */
+ case MFCINST_RETURN_INST:
+ /* This error had to happen while releasing instance */
+ clear_work_bit(ctx);
+ wake_up_ctx(ctx, reason, err);
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ BUG();
+ s5p_mfc_clock_off();
+ ctx->state = MFCINST_ERROR;
+ break;
+ case MFCINST_FINISHING:
+ case MFCINST_FINISHED:
+ case MFCINST_RUNNING:
+ /* It is higly probable that an error occured
+ * while decoding a frame */
+ clear_work_bit(ctx);
+ ctx->state = MFCINST_ERROR;
+ /* Mark all dst buffers as having an error */
+ spin_lock_irqsave(&dev->irqlock, flags);
+ s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
+ /* Mark all src buffers as having an error */
+ s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ BUG();
+ s5p_mfc_clock_off();
+ break;
+ default:
+ mfc_err("Encountered an error interrupt which had not been handled\n");
+ break;
+ }
+ return;
+}
+
+/* Header parsing interrupt handling */
+static void s5p_mfc_handle_seq_done(struct s5p_mfc_ctx *ctx,
+ unsigned int reason, unsigned int err)
+{
+ struct s5p_mfc_dev *dev;
+ unsigned int guard_width, guard_height;
+
+ if (ctx == 0)
+ return;
+ dev = ctx->dev;
+ if (ctx->c_ops->post_seq_start) {
+ if (ctx->c_ops->post_seq_start(ctx))
+ mfc_err("post_seq_start() failed\n");
+ } else {
+ ctx->img_width = s5p_mfc_get_img_width();
+ ctx->img_height = s5p_mfc_get_img_height();
+
+ ctx->buf_width = ALIGN(ctx->img_width,
+ S5P_FIMV_NV12MT_HALIGN);
+ ctx->buf_height = ALIGN(ctx->img_height,
+ S5P_FIMV_NV12MT_VALIGN);
+ mfc_debug(2, "SEQ Done: Movie dimensions %dx%d, "
+ "buffer dimensions: %dx%d\n", ctx->img_width,
+ ctx->img_height, ctx->buf_width,
+ ctx->buf_height);
+ if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC) {
+ ctx->luma_size = ALIGN(ctx->buf_width *
+ ctx->buf_height, S5P_FIMV_DEC_BUF_ALIGN);
+ ctx->chroma_size = ALIGN(ctx->buf_width *
+ ALIGN((ctx->img_height >> 1),
+ S5P_FIMV_NV12MT_VALIGN),
+ S5P_FIMV_DEC_BUF_ALIGN);
+ ctx->mv_size = ALIGN(ctx->buf_width *
+ ALIGN((ctx->buf_height >> 2),
+ S5P_FIMV_NV12MT_VALIGN),
+ S5P_FIMV_DEC_BUF_ALIGN);
+ } else {
+ guard_width = ALIGN(ctx->img_width + 24,
+ S5P_FIMV_NV12MT_HALIGN);
+ guard_height = ALIGN(ctx->img_height + 16,
+ S5P_FIMV_NV12MT_VALIGN);
+ ctx->luma_size = ALIGN(guard_width *
+ guard_height, S5P_FIMV_DEC_BUF_ALIGN);
+ guard_width = ALIGN(ctx->img_width + 16,
+ S5P_FIMV_NV12MT_HALIGN);
+ guard_height = ALIGN((ctx->img_height >> 1) + 4,
+ S5P_FIMV_NV12MT_VALIGN);
+ ctx->chroma_size = ALIGN(guard_width *
+ guard_height, S5P_FIMV_DEC_BUF_ALIGN);
+ ctx->mv_size = 0;
+ }
+ ctx->dpb_count = s5p_mfc_get_dpb_count();
+ if (ctx->img_width == 0 || ctx->img_height == 0)
+ ctx->state = MFCINST_ERROR;
+ else
+ ctx->state = MFCINST_HEAD_PARSED;
+ }
+ s5p_mfc_clear_int_flags(dev);
+ clear_work_bit(ctx);
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ BUG();
+ s5p_mfc_clock_off();
+ s5p_mfc_try_run(dev);
+ wake_up_ctx(ctx, reason, err);
+}
+
+/* Header parsing interrupt handling */
+static void s5p_mfc_handle_init_buffers(struct s5p_mfc_ctx *ctx,
+ unsigned int reason, unsigned int err)
+{
+ struct s5p_mfc_buf *src_buf;
+ struct s5p_mfc_dev *dev;
+ unsigned long flags;
+
+ if (ctx == 0)
+ return;
+ dev = ctx->dev;
+ s5p_mfc_clear_int_flags(dev);
+ ctx->int_type = reason;
+ ctx->int_err = err;
+ ctx->int_cond = 1;
+ spin_lock(&dev->condlock);
+ clear_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock(&dev->condlock);
+ if (err == 0) {
+ ctx->state = MFCINST_RUNNING;
+ if (!ctx->dpb_flush_flag) {
+ spin_lock_irqsave(&dev->irqlock, flags);
+ if (!list_empty(&ctx->src_queue)) {
+ src_buf = list_entry(ctx->src_queue.next,
+ struct s5p_mfc_buf, list);
+ list_del(&src_buf->list);
+ ctx->src_queue_cnt--;
+ vb2_buffer_done(src_buf->b,
+ VB2_BUF_STATE_DONE);
+ }
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ } else {
+ ctx->dpb_flush_flag = 0;
+ }
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ BUG();
+
+ s5p_mfc_clock_off();
+
+ wake_up(&ctx->queue);
+ s5p_mfc_try_run(dev);
+ } else {
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ BUG();
+
+ s5p_mfc_clock_off();
+
+ wake_up(&ctx->queue);
+ }
+}
+
+/* Interrupt processing */
+static irqreturn_t s5p_mfc_irq(int irq, void *priv)
+{
+ struct s5p_mfc_dev *dev = priv;
+ struct s5p_mfc_ctx *ctx;
+ unsigned int reason;
+ unsigned int err;
+
+ mfc_debug_enter();
+ /* Reset the timeout watchdog */
+ atomic_set(&dev->watchdog_cnt, 0);
+ ctx = dev->ctx[dev->curr_ctx];
+ /* Get the reason of interrupt and the error code */
+ reason = s5p_mfc_get_int_reason();
+ err = s5p_mfc_get_int_err();
+ mfc_debug(1, "Int reason: %d (err: %08x)\n", reason, err);
+ switch (reason) {
+ case S5P_FIMV_R2H_CMD_ERR_RET:
+ /* An error has occured */
+ if (ctx->state == MFCINST_RUNNING &&
+ s5p_mfc_err_dec(err) >= S5P_FIMV_ERR_WARNINGS_START)
+ s5p_mfc_handle_frame(ctx, reason, err);
+ else
+ s5p_mfc_handle_error(ctx, reason, err);
+ clear_bit(0, &dev->enter_suspend);
+ break;
+
+ case S5P_FIMV_R2H_CMD_SLICE_DONE_RET:
+ case S5P_FIMV_R2H_CMD_FRAME_DONE_RET:
+ if (ctx->c_ops->post_frame_start) {
+ if (ctx->c_ops->post_frame_start(ctx))
+ mfc_err("post_frame_start() failed\n");
+ s5p_mfc_clear_int_flags(dev);
+ wake_up_ctx(ctx, reason, err);
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ BUG();
+ s5p_mfc_clock_off();
+ s5p_mfc_try_run(dev);
+ } else {
+ s5p_mfc_handle_frame(ctx, reason, err);
+ }
+ break;
+
+ case S5P_FIMV_R2H_CMD_SEQ_DONE_RET:
+ s5p_mfc_handle_seq_done(ctx, reason, err);
+ break;
+
+ case S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET:
+ ctx->inst_no = s5p_mfc_get_inst_no();
+ ctx->state = MFCINST_GOT_INST;
+ clear_work_bit(ctx);
+ wake_up(&ctx->queue);
+ goto irq_cleanup_hw;
+
+ case S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET:
+ clear_work_bit(ctx);
+ ctx->state = MFCINST_FREE;
+ wake_up(&ctx->queue);
+ goto irq_cleanup_hw;
+
+ case S5P_FIMV_R2H_CMD_SYS_INIT_RET:
+ case S5P_FIMV_R2H_CMD_FW_STATUS_RET:
+ case S5P_FIMV_R2H_CMD_SLEEP_RET:
+ case S5P_FIMV_R2H_CMD_WAKEUP_RET:
+ if (ctx)
+ clear_work_bit(ctx);
+ s5p_mfc_clear_int_flags(dev);
+ wake_up_dev(dev, reason, err);
+ clear_bit(0, &dev->hw_lock);
+ clear_bit(0, &dev->enter_suspend);
+ break;
+
+ case S5P_FIMV_R2H_CMD_INIT_BUFFERS_RET:
+ s5p_mfc_handle_init_buffers(ctx, reason, err);
+ break;
+ default:
+ mfc_debug(2, "Unknown int reason\n");
+ s5p_mfc_clear_int_flags(dev);
+ }
+ mfc_debug_leave();
+ return IRQ_HANDLED;
+irq_cleanup_hw:
+ s5p_mfc_clear_int_flags(dev);
+ ctx->int_type = reason;
+ ctx->int_err = err;
+ ctx->int_cond = 1;
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ mfc_err("Failed to unlock hw\n");
+
+ s5p_mfc_clock_off();
+
+ s5p_mfc_try_run(dev);
+ mfc_debug(2, "Exit via irq_cleanup_hw\n");
+ return IRQ_HANDLED;
+}
+
+/* Open an MFC node */
+static int s5p_mfc_open(struct file *file)
+{
+ struct s5p_mfc_dev *dev = video_drvdata(file);
+ struct s5p_mfc_ctx *ctx = NULL;
+ struct vb2_queue *q;
+ unsigned long flags;
+ int ret = 0;
+
+ mfc_debug_enter();
+ dev->num_inst++; /* It is guarded by mfc_mutex in vfd */
+ /* Allocate memory for context */
+ ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
+ if (!ctx) {
+ mfc_err("Not enough memory\n");
+ ret = -ENOMEM;
+ goto err_alloc;
+ }
+ v4l2_fh_init(&ctx->fh, video_devdata(file));
+ file->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh);
+ ctx->dev = dev;
+ INIT_LIST_HEAD(&ctx->src_queue);
+ INIT_LIST_HEAD(&ctx->dst_queue);
+ ctx->src_queue_cnt = 0;
+ ctx->dst_queue_cnt = 0;
+ /* Get context number */
+ ctx->num = 0;
+ while (dev->ctx[ctx->num]) {
+ ctx->num++;
+ if (ctx->num >= MFC_NUM_CONTEXTS) {
+ mfc_err("Too many open contexts\n");
+ ret = -EBUSY;
+ goto err_no_ctx;
+ }
+ }
+ /* Mark context as idle */
+ spin_lock_irqsave(&dev->condlock, flags);
+ clear_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock_irqrestore(&dev->condlock, flags);
+ dev->ctx[ctx->num] = ctx;
+ if (s5p_mfc_get_node_type(file) == MFCNODE_DECODER) {
+ ctx->type = MFCINST_DECODER;
+ ctx->c_ops = get_dec_codec_ops();
+ /* Setup ctrl handler */
+ ret = s5p_mfc_dec_ctrls_setup(ctx);
+ if (ret) {
+ mfc_err("Failed to setup mfc controls\n");
+ goto err_ctrls_setup;
+ }
+ } else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) {
+ ctx->type = MFCINST_ENCODER;
+ ctx->c_ops = get_enc_codec_ops();
+ /* only for encoder */
+ INIT_LIST_HEAD(&ctx->ref_queue);
+ ctx->ref_queue_cnt = 0;
+ /* Setup ctrl handler */
+ ret = s5p_mfc_enc_ctrls_setup(ctx);
+ if (ret) {
+ mfc_err("Failed to setup mfc controls\n");
+ goto err_ctrls_setup;
+ }
+ } else {
+ ret = -ENOENT;
+ goto err_bad_node;
+ }
+ ctx->fh.ctrl_handler = &ctx->ctrl_handler;
+ ctx->inst_no = -1;
+ /* Load firmware if this is the first instance */
+ if (dev->num_inst == 1) {
+ dev->watchdog_timer.expires = jiffies +
+ msecs_to_jiffies(MFC_WATCHDOG_INTERVAL);
+ add_timer(&dev->watchdog_timer);
+ ret = s5p_mfc_power_on();
+ if (ret < 0) {
+ mfc_err("power on failed\n");
+ goto err_pwr_enable;
+ }
+ s5p_mfc_clock_on();
+ ret = s5p_mfc_alloc_and_load_firmware(dev);
+ if (ret)
+ goto err_alloc_fw;
+ /* Init the FW */
+ ret = s5p_mfc_init_hw(dev);
+ if (ret)
+ goto err_init_hw;
+ s5p_mfc_clock_off();
+ }
+ /* Init videobuf2 queue for CAPTURE */
+ q = &ctx->vq_dst;
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ q->drv_priv = &ctx->fh;
+ if (s5p_mfc_get_node_type(file) == MFCNODE_DECODER) {
+ q->io_modes = VB2_MMAP;
+ q->ops = get_dec_queue_ops();
+ } else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) {
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->ops = get_enc_queue_ops();
+ } else {
+ ret = -ENOENT;
+ goto err_queue_init;
+ }
+ q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops;
+ ret = vb2_queue_init(q);
+ if (ret) {
+ mfc_err("Failed to initialize videobuf2 queue(capture)\n");
+ goto err_queue_init;
+ }
+ /* Init videobuf2 queue for OUTPUT */
+ q = &ctx->vq_src;
+ q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ q->io_modes = VB2_MMAP;
+ q->drv_priv = &ctx->fh;
+ if (s5p_mfc_get_node_type(file) == MFCNODE_DECODER) {
+ q->io_modes = VB2_MMAP;
+ q->ops = get_dec_queue_ops();
+ } else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) {
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->ops = get_enc_queue_ops();
+ } else {
+ ret = -ENOENT;
+ goto err_queue_init;
+ }
+ q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops;
+ ret = vb2_queue_init(q);
+ if (ret) {
+ mfc_err("Failed to initialize videobuf2 queue(output)\n");
+ goto err_queue_init;
+ }
+ init_waitqueue_head(&ctx->queue);
+ mfc_debug_leave();
+ return ret;
+ /* Deinit when failure occured */
+err_queue_init:
+err_init_hw:
+ s5p_mfc_release_firmware(dev);
+err_alloc_fw:
+ dev->ctx[ctx->num] = 0;
+ del_timer_sync(&dev->watchdog_timer);
+ s5p_mfc_clock_off();
+err_pwr_enable:
+ if (dev->num_inst == 1) {
+ if (s5p_mfc_power_off() < 0)
+ mfc_err("power off failed\n");
+ s5p_mfc_release_firmware(dev);
+ }
+err_ctrls_setup:
+ s5p_mfc_dec_ctrls_delete(ctx);
+err_bad_node:
+err_no_ctx:
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+err_alloc:
+ dev->num_inst--;
+ mfc_debug_leave();
+ return ret;
+}
+
+/* Release MFC context */
+static int s5p_mfc_release(struct file *file)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(file->private_data);
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long flags;
+
+ mfc_debug_enter();
+ s5p_mfc_clock_on();
+ vb2_queue_release(&ctx->vq_src);
+ vb2_queue_release(&ctx->vq_dst);
+ /* Mark context as idle */
+ spin_lock_irqsave(&dev->condlock, flags);
+ clear_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock_irqrestore(&dev->condlock, flags);
+ /* If instance was initialised then
+ * return instance and free reosurces */
+ if (ctx->inst_no != MFC_NO_INSTANCE_SET) {
+ mfc_debug(2, "Has to free instance\n");
+ ctx->state = MFCINST_RETURN_INST;
+ spin_lock_irqsave(&dev->condlock, flags);
+ set_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock_irqrestore(&dev->condlock, flags);
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_try_run(dev);
+ /* Wait until instance is returned or timeout occured */
+ if (s5p_mfc_wait_for_done_ctx
+ (ctx, S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET, 0)) {
+ s5p_mfc_clock_off();
+ mfc_err("Err returning instance\n");
+ }
+ mfc_debug(2, "After free instance\n");
+ /* Free resources */
+ s5p_mfc_release_codec_buffers(ctx);
+ s5p_mfc_release_instance_buffer(ctx);
+ if (ctx->type == MFCINST_DECODER)
+ s5p_mfc_release_dec_desc_buffer(ctx);
+
+ ctx->inst_no = MFC_NO_INSTANCE_SET;
+ }
+ /* hardware locking scheme */
+ if (dev->curr_ctx == ctx->num)
+ clear_bit(0, &dev->hw_lock);
+ dev->num_inst--;
+ if (dev->num_inst == 0) {
+ mfc_debug(2, "Last instance - release firmware\n");
+ /* reset <-> F/W release */
+ s5p_mfc_reset(dev);
+ s5p_mfc_release_firmware(dev);
+ del_timer_sync(&dev->watchdog_timer);
+ if (s5p_mfc_power_off() < 0)
+ mfc_err("Power off failed\n");
+ }
+ mfc_debug(2, "Shutting down clock\n");
+ s5p_mfc_clock_off();
+ dev->ctx[ctx->num] = 0;
+ s5p_mfc_dec_ctrls_delete(ctx);
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+ mfc_debug_leave();
+ return 0;
+}
+
+/* Poll */
+static unsigned int s5p_mfc_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(file->private_data);
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct vb2_queue *src_q, *dst_q;
+ struct vb2_buffer *src_vb = NULL, *dst_vb = NULL;
+ unsigned int rc = 0;
+ unsigned long flags;
+
+ src_q = &ctx->vq_src;
+ dst_q = &ctx->vq_dst;
+ /*
+ * There has to be at least one buffer queued on each queued_list, which
+ * means either in driver already or waiting for driver to claim it
+ * and start processing.
+ */
+ if ((!src_q->streaming || list_empty(&src_q->queued_list))
+ && (!dst_q->streaming || list_empty(&dst_q->queued_list))) {
+ rc = POLLERR;
+ goto end;
+ }
+ mutex_unlock(&dev->mfc_mutex);
+ poll_wait(file, &src_q->done_wq, wait);
+ poll_wait(file, &dst_q->done_wq, wait);
+ mutex_lock(&dev->mfc_mutex);
+ spin_lock_irqsave(&src_q->done_lock, flags);
+ if (!list_empty(&src_q->done_list))
+ src_vb = list_first_entry(&src_q->done_list, struct vb2_buffer,
+ done_entry);
+ if (src_vb && (src_vb->state == VB2_BUF_STATE_DONE
+ || src_vb->state == VB2_BUF_STATE_ERROR))
+ rc |= POLLOUT | POLLWRNORM;
+ spin_unlock_irqrestore(&src_q->done_lock, flags);
+ spin_lock_irqsave(&dst_q->done_lock, flags);
+ if (!list_empty(&dst_q->done_list))
+ dst_vb = list_first_entry(&dst_q->done_list, struct vb2_buffer,
+ done_entry);
+ if (dst_vb && (dst_vb->state == VB2_BUF_STATE_DONE
+ || dst_vb->state == VB2_BUF_STATE_ERROR))
+ rc |= POLLIN | POLLRDNORM;
+ spin_unlock_irqrestore(&dst_q->done_lock, flags);
+end:
+ return rc;
+}
+
+/* Mmap */
+static int s5p_mfc_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(file->private_data);
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ int ret;
+ if (offset < DST_QUEUE_OFF_BASE) {
+ mfc_debug(2, "mmaping source\n");
+ ret = vb2_mmap(&ctx->vq_src, vma);
+ } else { /* capture */
+ mfc_debug(2, "mmaping destination\n");
+ vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT);
+ ret = vb2_mmap(&ctx->vq_dst, vma);
+ }
+ return ret;
+}
+
+/* v4l2 ops */
+static const struct v4l2_file_operations s5p_mfc_fops = {
+ .owner = THIS_MODULE,
+ .open = s5p_mfc_open,
+ .release = s5p_mfc_release,
+ .poll = s5p_mfc_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = s5p_mfc_mmap,
+};
+
+static int match_child(struct device *dev, void *data)
+{
+ if (!dev_name(dev))
+ return 0;
+ return !strcmp(dev_name(dev), (char *)data);
+}
+
+/* MFC probe function */
+static int s5p_mfc_probe(struct platform_device *pdev)
+{
+ struct s5p_mfc_dev *dev;
+ struct video_device *vfd;
+ struct resource *res;
+ int ret;
+
+ pr_debug("%s++\n", __func__);
+ dev = kzalloc(sizeof *dev, GFP_KERNEL);
+ if (!dev) {
+ dev_err(&pdev->dev, "Not enough memory for MFC device\n");
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&dev->irqlock);
+ spin_lock_init(&dev->condlock);
+ dev->plat_dev = pdev;
+ if (!dev->plat_dev) {
+ dev_err(&pdev->dev, "No platform data specified\n");
+ ret = -ENODEV;
+ goto err_dev;
+ }
+
+ ret = s5p_mfc_init_pm(dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to get mfc clock source\n");
+ goto err_clk;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "failed to get memory region resource\n");
+ ret = -ENOENT;
+ goto err_res;
+ }
+
+ dev->mfc_mem = request_mem_region(res->start, resource_size(res),
+ pdev->name);
+ if (dev->mfc_mem == NULL) {
+ dev_err(&pdev->dev, "failed to get memory region\n");
+ ret = -ENOENT;
+ goto err_mem_reg;
+ }
+ dev->regs_base = ioremap(dev->mfc_mem->start, resource_size(dev->mfc_mem));
+ if (dev->regs_base == NULL) {
+ dev_err(&pdev->dev, "failed to ioremap address region\n");
+ ret = -ENOENT;
+ goto err_ioremap;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "failed to get irq resource\n");
+ ret = -ENOENT;
+ goto err_get_res;
+ }
+ dev->irq = res->start;
+ ret = request_irq(dev->irq, s5p_mfc_irq, IRQF_DISABLED, pdev->name,
+ dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret);
+ goto err_req_irq;
+ }
+
+ dev->mem_dev_l = device_find_child(&dev->plat_dev->dev, "s5p-mfc-l",
+ match_child);
+ if (!dev->mem_dev_l) {
+ mfc_err("Mem child (L) device get failed\n");
+ ret = -ENODEV;
+ goto err_find_child;
+ }
+ dev->mem_dev_r = device_find_child(&dev->plat_dev->dev, "s5p-mfc-r",
+ match_child);
+ if (!dev->mem_dev_r) {
+ mfc_err("Mem child (R) device get failed\n");
+ ret = -ENODEV;
+ goto err_find_child;
+ }
+
+ dev->alloc_ctx[0] = vb2_dma_contig_init_ctx(dev->mem_dev_l);
+ if (IS_ERR_OR_NULL(dev->alloc_ctx[0])) {
+ ret = PTR_ERR(dev->alloc_ctx[0]);
+ goto err_mem_init_ctx_0;
+ }
+ dev->alloc_ctx[1] = vb2_dma_contig_init_ctx(dev->mem_dev_r);
+ if (IS_ERR_OR_NULL(dev->alloc_ctx[1])) {
+ ret = PTR_ERR(dev->alloc_ctx[1]);
+ goto err_mem_init_ctx_1;
+ }
+
+ mutex_init(&dev->mfc_mutex);
+
+ ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+ if (ret)
+ goto err_v4l2_dev_reg;
+ init_waitqueue_head(&dev->queue);
+
+ /* decoder */
+ vfd = video_device_alloc();
+ if (!vfd) {
+ v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
+ ret = -ENOMEM;
+ goto err_dec_alloc;
+ }
+ vfd->fops = &s5p_mfc_fops,
+ vfd->ioctl_ops = get_dec_v4l2_ioctl_ops();
+ vfd->release = video_device_release,
+ vfd->lock = &dev->mfc_mutex;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+ snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_DEC_NAME);
+ dev->vfd_dec = vfd;
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+ video_device_release(vfd);
+ goto err_dec_reg;
+ }
+ v4l2_info(&dev->v4l2_dev,
+ "decoder registered as /dev/video%d\n", vfd->num);
+ video_set_drvdata(vfd, dev);
+
+ /* encoder */
+ vfd = video_device_alloc();
+ if (!vfd) {
+ v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
+ ret = -ENOMEM;
+ goto err_enc_alloc;
+ }
+ vfd->fops = &s5p_mfc_fops,
+ vfd->ioctl_ops = get_enc_v4l2_ioctl_ops();
+ vfd->release = video_device_release,
+ vfd->lock = &dev->mfc_mutex;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+ snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_ENC_NAME);
+ dev->vfd_enc = vfd;
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+ video_device_release(vfd);
+ goto err_enc_reg;
+ }
+ v4l2_info(&dev->v4l2_dev,
+ "encoder registered as /dev/video%d\n", vfd->num);
+ video_set_drvdata(vfd, dev);
+ platform_set_drvdata(pdev, dev);
+
+ dev->hw_lock = 0;
+ dev->watchdog_workqueue = create_singlethread_workqueue(S5P_MFC_NAME);
+ INIT_WORK(&dev->watchdog_work, s5p_mfc_watchdog_worker);
+ atomic_set(&dev->watchdog_cnt, 0);
+ init_timer(&dev->watchdog_timer);
+ dev->watchdog_timer.data = (unsigned long)dev;
+ dev->watchdog_timer.function = s5p_mfc_watchdog;
+
+ pr_debug("%s--\n", __func__);
+ return 0;
+
+/* Deinit MFC if probe had failed */
+err_enc_reg:
+ video_device_release(dev->vfd_enc);
+err_enc_alloc:
+ video_unregister_device(dev->vfd_dec);
+err_dec_reg:
+ video_device_release(dev->vfd_dec);
+err_dec_alloc:
+ v4l2_device_unregister(&dev->v4l2_dev);
+err_v4l2_dev_reg:
+ vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]);
+err_mem_init_ctx_1:
+ vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]);
+err_mem_init_ctx_0:
+err_find_child:
+ free_irq(dev->irq, dev);
+err_req_irq:
+err_get_res:
+ iounmap(dev->regs_base);
+ dev->regs_base = NULL;
+err_ioremap:
+ release_resource(dev->mfc_mem);
+ kfree(dev->mfc_mem);
+err_mem_reg:
+err_res:
+ s5p_mfc_final_pm(dev);
+err_clk:
+err_dev:
+ kfree(dev);
+ pr_debug("%s-- with error\n", __func__);
+ return ret;
+
+}
+
+/* Remove the driver */
+static int __devexit s5p_mfc_remove(struct platform_device *pdev)
+{
+ struct s5p_mfc_dev *dev = platform_get_drvdata(pdev);
+
+ v4l2_info(&dev->v4l2_dev, "Removing %s\n", pdev->name);
+
+ del_timer_sync(&dev->watchdog_timer);
+ flush_workqueue(dev->watchdog_workqueue);
+ destroy_workqueue(dev->watchdog_workqueue);
+
+ video_unregister_device(dev->vfd_enc);
+ video_unregister_device(dev->vfd_dec);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]);
+ vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]);
+
+ free_irq(dev->irq, dev);
+ iounmap(dev->regs_base);
+ if (dev->mfc_mem) {
+ release_resource(dev->mfc_mem);
+ kfree(dev->mfc_mem);
+ dev->mfc_mem = NULL;
+ }
+ s5p_mfc_final_pm(dev);
+ kfree(dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int s5p_mfc_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev);
+ int ret;
+
+ if (m_dev->num_inst == 0)
+ return 0;
+ return s5p_mfc_sleep(m_dev);
+ if (test_and_set_bit(0, &m_dev->enter_suspend) != 0) {
+ mfc_err("Error: going to suspend for a second time\n");
+ return -EIO;
+ }
+
+ /* Check if we're processing then wait if it necessary. */
+ while (test_and_set_bit(0, &m_dev->hw_lock) != 0) {
+ /* Try and lock the HW */
+ /* Wait on the interrupt waitqueue */
+ ret = wait_event_interruptible_timeout(m_dev->queue,
+ m_dev->int_cond || m_dev->ctx[m_dev->curr_ctx]->int_cond,
+ msecs_to_jiffies(MFC_INT_TIMEOUT));
+
+ if (ret == 0) {
+ mfc_err("Waiting for hardware to finish timed out\n");
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
+static int s5p_mfc_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev);
+
+ if (m_dev->num_inst == 0)
+ return 0;
+ return s5p_mfc_wakeup(m_dev);
+}
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+static int s5p_mfc_runtime_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev);
+
+ atomic_set(&m_dev->pm.power, 0);
+ return 0;
+}
+
+static int s5p_mfc_runtime_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev);
+ int pre_power;
+
+ if (!m_dev->alloc_ctx)
+ return 0;
+ pre_power = atomic_read(&m_dev->pm.power);
+ atomic_set(&m_dev->pm.power, 1);
+ return 0;
+}
+#endif
+
+/* Power management */
+static const struct dev_pm_ops s5p_mfc_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(s5p_mfc_suspend, s5p_mfc_resume)
+ SET_RUNTIME_PM_OPS(s5p_mfc_runtime_suspend, s5p_mfc_runtime_resume,
+ NULL)
+};
+
+static struct platform_driver s5p_mfc_driver = {
+ .probe = s5p_mfc_probe,
+ .remove = __devexit_p(s5p_mfc_remove),
+ .driver = {
+ .name = S5P_MFC_NAME,
+ .owner = THIS_MODULE,
+ .pm = &s5p_mfc_pm_ops
+ },
+};
+
+module_platform_driver(s5p_mfc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
+MODULE_DESCRIPTION("Samsung S5P Multi Format Codec V4L2 driver");
+
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_cmd.c b/drivers/media/video/s5p-mfc/s5p_mfc_cmd.c
new file mode 100644
index 00000000..f0665ed1
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_cmd.c
@@ -0,0 +1,120 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_cmd.c
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "regs-mfc.h"
+#include "s5p_mfc_cmd.h"
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_debug.h"
+
+/* This function is used to send a command to the MFC */
+static int s5p_mfc_cmd_host2risc(struct s5p_mfc_dev *dev, int cmd,
+ struct s5p_mfc_cmd_args *args)
+{
+ int cur_cmd;
+ unsigned long timeout;
+
+ timeout = jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT);
+ /* wait until host to risc command register becomes 'H2R_CMD_EMPTY' */
+ do {
+ if (time_after(jiffies, timeout)) {
+ mfc_err("Timeout while waiting for hardware\n");
+ return -EIO;
+ }
+ cur_cmd = mfc_read(dev, S5P_FIMV_HOST2RISC_CMD);
+ } while (cur_cmd != S5P_FIMV_H2R_CMD_EMPTY);
+ mfc_write(dev, args->arg[0], S5P_FIMV_HOST2RISC_ARG1);
+ mfc_write(dev, args->arg[1], S5P_FIMV_HOST2RISC_ARG2);
+ mfc_write(dev, args->arg[2], S5P_FIMV_HOST2RISC_ARG3);
+ mfc_write(dev, args->arg[3], S5P_FIMV_HOST2RISC_ARG4);
+ /* Issue the command */
+ mfc_write(dev, cmd, S5P_FIMV_HOST2RISC_CMD);
+ return 0;
+}
+
+/* Initialize the MFC */
+int s5p_mfc_sys_init_cmd(struct s5p_mfc_dev *dev)
+{
+ struct s5p_mfc_cmd_args h2r_args;
+
+ memset(&h2r_args, 0, sizeof(struct s5p_mfc_cmd_args));
+ h2r_args.arg[0] = dev->fw_size;
+ return s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_SYS_INIT, &h2r_args);
+}
+
+/* Suspend the MFC hardware */
+int s5p_mfc_sleep_cmd(struct s5p_mfc_dev *dev)
+{
+ struct s5p_mfc_cmd_args h2r_args;
+
+ memset(&h2r_args, 0, sizeof(struct s5p_mfc_cmd_args));
+ return s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_SLEEP, &h2r_args);
+}
+
+/* Wake up the MFC hardware */
+int s5p_mfc_wakeup_cmd(struct s5p_mfc_dev *dev)
+{
+ struct s5p_mfc_cmd_args h2r_args;
+
+ memset(&h2r_args, 0, sizeof(struct s5p_mfc_cmd_args));
+ return s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_WAKEUP, &h2r_args);
+}
+
+
+int s5p_mfc_open_inst_cmd(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_cmd_args h2r_args;
+ int ret;
+
+ /* Preparing decoding - getting instance number */
+ mfc_debug(2, "Getting instance number (codec: %d)\n", ctx->codec_mode);
+ dev->curr_ctx = ctx->num;
+ memset(&h2r_args, 0, sizeof(struct s5p_mfc_cmd_args));
+ h2r_args.arg[0] = ctx->codec_mode;
+ h2r_args.arg[1] = 0; /* no crc & no pixelcache */
+ h2r_args.arg[2] = ctx->ctx_ofs;
+ h2r_args.arg[3] = ctx->ctx_size;
+ ret = s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_OPEN_INSTANCE,
+ &h2r_args);
+ if (ret) {
+ mfc_err("Failed to create a new instance\n");
+ ctx->state = MFCINST_ERROR;
+ }
+ return ret;
+}
+
+int s5p_mfc_close_inst_cmd(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_cmd_args h2r_args;
+ int ret;
+
+ if (ctx->state == MFCINST_FREE) {
+ mfc_err("Instance already returned\n");
+ ctx->state = MFCINST_ERROR;
+ return -EINVAL;
+ }
+ /* Closing decoding instance */
+ mfc_debug(2, "Returning instance number %d\n", ctx->inst_no);
+ dev->curr_ctx = ctx->num;
+ memset(&h2r_args, 0, sizeof(struct s5p_mfc_cmd_args));
+ h2r_args.arg[0] = ctx->inst_no;
+ ret = s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_CLOSE_INSTANCE,
+ &h2r_args);
+ if (ret) {
+ mfc_err("Failed to return an instance\n");
+ ctx->state = MFCINST_ERROR;
+ return -EINVAL;
+ }
+ return 0;
+}
+
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_cmd.h b/drivers/media/video/s5p-mfc/s5p_mfc_cmd.h
new file mode 100644
index 00000000..5ceebfe6
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_cmd.h
@@ -0,0 +1,30 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_cmd.h
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef S5P_MFC_CMD_H_
+#define S5P_MFC_CMD_H_
+
+#include "s5p_mfc_common.h"
+
+#define MAX_H2R_ARG 4
+
+struct s5p_mfc_cmd_args {
+ unsigned int arg[MAX_H2R_ARG];
+};
+
+int s5p_mfc_sys_init_cmd(struct s5p_mfc_dev *dev);
+int s5p_mfc_sleep_cmd(struct s5p_mfc_dev *dev);
+int s5p_mfc_wakeup_cmd(struct s5p_mfc_dev *dev);
+int s5p_mfc_open_inst_cmd(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_close_inst_cmd(struct s5p_mfc_ctx *ctx);
+
+#endif /* S5P_MFC_CMD_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_common.h b/drivers/media/video/s5p-mfc/s5p_mfc_common.h
new file mode 100644
index 00000000..91146fa6
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_common.h
@@ -0,0 +1,572 @@
+/*
+ * Samsung S5P Multi Format Codec v 5.0
+ *
+ * This file contains definitions of enums and structs used by the codec
+ * driver.
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Kamil Debski, <k.debski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version
+ */
+
+#ifndef S5P_MFC_COMMON_H_
+#define S5P_MFC_COMMON_H_
+
+#include "regs-mfc.h"
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+
+/* Definitions related to MFC memory */
+
+/* Offset base used to differentiate between CAPTURE and OUTPUT
+* while mmaping */
+#define DST_QUEUE_OFF_BASE (TASK_SIZE / 2)
+
+/* Offset used by the hardware to store addresses */
+#define MFC_OFFSET_SHIFT 11
+
+#define FIRMWARE_ALIGN 0x20000 /* 128KB */
+#define MFC_H264_CTX_BUF_SIZE 0x96000 /* 600KB per H264 instance */
+#define MFC_CTX_BUF_SIZE 0x2800 /* 10KB per instance */
+#define DESC_BUF_SIZE 0x20000 /* 128KB for DESC buffer */
+#define SHARED_BUF_SIZE 0x2000 /* 8KB for shared buffer */
+
+#define DEF_CPB_SIZE 0x40000 /* 512KB */
+
+#define MFC_BANK1_ALLOC_CTX 0
+#define MFC_BANK2_ALLOC_CTX 1
+
+#define MFC_BANK1_ALIGN_ORDER 13
+#define MFC_BANK2_ALIGN_ORDER 13
+#define MFC_BASE_ALIGN_ORDER 17
+
+#include <media/videobuf2-dma-contig.h>
+
+static inline dma_addr_t s5p_mfc_mem_cookie(void *a, void *b)
+{
+ /* Same functionality as the vb2_dma_contig_plane_paddr */
+ dma_addr_t *paddr = vb2_dma_contig_memops.cookie(b);
+
+ return *paddr;
+}
+
+/* MFC definitions */
+#define MFC_MAX_EXTRA_DPB 5
+#define MFC_MAX_BUFFERS 32
+#define MFC_NUM_CONTEXTS 4
+/* Interrupt timeout */
+#define MFC_INT_TIMEOUT 2000
+/* Busy wait timeout */
+#define MFC_BW_TIMEOUT 500
+/* Watchdog interval */
+#define MFC_WATCHDOG_INTERVAL 1000
+/* After how many executions watchdog should assume lock up */
+#define MFC_WATCHDOG_CNT 10
+#define MFC_NO_INSTANCE_SET -1
+#define MFC_ENC_CAP_PLANE_COUNT 1
+#define MFC_ENC_OUT_PLANE_COUNT 2
+#define STUFF_BYTE 4
+#define MFC_MAX_CTRLS 64
+
+#define mfc_read(dev, offset) readl(dev->regs_base + (offset))
+#define mfc_write(dev, data, offset) writel((data), dev->regs_base + \
+ (offset))
+
+/**
+ * enum s5p_mfc_fmt_type - type of the pixelformat
+ */
+enum s5p_mfc_fmt_type {
+ MFC_FMT_DEC,
+ MFC_FMT_ENC,
+ MFC_FMT_RAW,
+};
+
+/**
+ * enum s5p_mfc_node_type - The type of an MFC device node.
+ */
+enum s5p_mfc_node_type {
+ MFCNODE_INVALID = -1,
+ MFCNODE_DECODER = 0,
+ MFCNODE_ENCODER = 1,
+};
+
+/**
+ * enum s5p_mfc_inst_type - The type of an MFC instance.
+ */
+enum s5p_mfc_inst_type {
+ MFCINST_INVALID,
+ MFCINST_DECODER,
+ MFCINST_ENCODER,
+};
+
+/**
+ * enum s5p_mfc_inst_state - The state of an MFC instance.
+ */
+enum s5p_mfc_inst_state {
+ MFCINST_FREE = 0,
+ MFCINST_INIT = 100,
+ MFCINST_GOT_INST,
+ MFCINST_HEAD_PARSED,
+ MFCINST_BUFS_SET,
+ MFCINST_RUNNING,
+ MFCINST_FINISHING,
+ MFCINST_FINISHED,
+ MFCINST_RETURN_INST,
+ MFCINST_ERROR,
+ MFCINST_ABORT,
+ MFCINST_RES_CHANGE_INIT,
+ MFCINST_RES_CHANGE_FLUSH,
+ MFCINST_RES_CHANGE_END,
+};
+
+/**
+ * enum s5p_mfc_queue_state - The state of buffer queue.
+ */
+enum s5p_mfc_queue_state {
+ QUEUE_FREE,
+ QUEUE_BUFS_REQUESTED,
+ QUEUE_BUFS_QUERIED,
+ QUEUE_BUFS_MMAPED,
+};
+
+/**
+ * enum s5p_mfc_decode_arg - type of frame decoding
+ */
+enum s5p_mfc_decode_arg {
+ MFC_DEC_FRAME,
+ MFC_DEC_LAST_FRAME,
+ MFC_DEC_RES_CHANGE,
+};
+
+struct s5p_mfc_ctx;
+
+/**
+ * struct s5p_mfc_buf - MFC buffer
+ */
+struct s5p_mfc_buf {
+ struct list_head list;
+ struct vb2_buffer *b;
+ union {
+ struct {
+ size_t luma;
+ size_t chroma;
+ } raw;
+ size_t stream;
+ } cookie;
+ int used;
+};
+
+/**
+ * struct s5p_mfc_pm - power management data structure
+ */
+struct s5p_mfc_pm {
+ struct clk *clock;
+ struct clk *clock_gate;
+ atomic_t power;
+ struct device *device;
+};
+
+/**
+ * struct s5p_mfc_dev - The struct containing driver internal parameters.
+ *
+ * @v4l2_dev: v4l2_device
+ * @vfd_dec: video device for decoding
+ * @vfd_enc: video device for encoding
+ * @plat_dev: platform device
+ * @mem_dev_l: child device of the left memory bank (0)
+ * @mem_dev_r: child device of the right memory bank (1)
+ * @regs_base: base address of the MFC hw registers
+ * @irq: irq resource
+ * @mfc_mem: MFC registers memory resource
+ * @dec_ctrl_handler: control framework handler for decoding
+ * @enc_ctrl_handler: control framework handler for encoding
+ * @pm: power management control
+ * @num_inst: couter of active MFC instances
+ * @irqlock: lock for operations on videobuf2 queues
+ * @condlock: lock for changing/checking if a context is ready to be
+ * processed
+ * @mfc_mutex: lock for video_device
+ * @int_cond: variable used by the waitqueue
+ * @int_type: type of last interrupt
+ * @int_err: error number for last interrupt
+ * @queue: waitqueue for waiting for completion of device commands
+ * @fw_size: size of firmware
+ * @bank1: address of the beggining of bank 1 memory
+ * @bank2: address of the beggining of bank 2 memory
+ * @hw_lock: used for hardware locking
+ * @ctx: array of driver contexts
+ * @curr_ctx: number of the currently running context
+ * @ctx_work_bits: used to mark which contexts are waiting for hardware
+ * @watchdog_cnt: counter for the watchdog
+ * @watchdog_workqueue: workqueue for the watchdog
+ * @watchdog_work: worker for the watchdog
+ * @alloc_ctx: videobuf2 allocator contexts for two memory banks
+ * @enter_suspend: flag set when entering suspend
+ *
+ */
+struct s5p_mfc_dev {
+ struct v4l2_device v4l2_dev;
+ struct video_device *vfd_dec;
+ struct video_device *vfd_enc;
+ struct platform_device *plat_dev;
+ struct device *mem_dev_l;
+ struct device *mem_dev_r;
+ void __iomem *regs_base;
+ int irq;
+ struct resource *mfc_mem;
+ struct v4l2_ctrl_handler dec_ctrl_handler;
+ struct v4l2_ctrl_handler enc_ctrl_handler;
+ struct s5p_mfc_pm pm;
+ int num_inst;
+ spinlock_t irqlock; /* lock when operating on videobuf2 queues */
+ spinlock_t condlock; /* lock when changing/checking if a context is
+ ready to be processed */
+ struct mutex mfc_mutex; /* video_device lock */
+ int int_cond;
+ int int_type;
+ unsigned int int_err;
+ wait_queue_head_t queue;
+ size_t fw_size;
+ size_t bank1;
+ size_t bank2;
+ unsigned long hw_lock;
+ struct s5p_mfc_ctx *ctx[MFC_NUM_CONTEXTS];
+ int curr_ctx;
+ unsigned long ctx_work_bits;
+ atomic_t watchdog_cnt;
+ struct timer_list watchdog_timer;
+ struct workqueue_struct *watchdog_workqueue;
+ struct work_struct watchdog_work;
+ void *alloc_ctx[2];
+ unsigned long enter_suspend;
+};
+
+/**
+ * struct s5p_mfc_h264_enc_params - encoding parameters for h264
+ */
+struct s5p_mfc_h264_enc_params {
+ enum v4l2_mpeg_video_h264_profile profile;
+ enum v4l2_mpeg_video_h264_loop_filter_mode loop_filter_mode;
+ s8 loop_filter_alpha;
+ s8 loop_filter_beta;
+ enum v4l2_mpeg_video_h264_entropy_mode entropy_mode;
+ u8 max_ref_pic;
+ u8 num_ref_pic_4p;
+ int _8x8_transform;
+ int rc_mb;
+ int rc_mb_dark;
+ int rc_mb_smooth;
+ int rc_mb_static;
+ int rc_mb_activity;
+ int vui_sar;
+ u8 vui_sar_idc;
+ u16 vui_ext_sar_width;
+ u16 vui_ext_sar_height;
+ int open_gop;
+ u16 open_gop_size;
+ u8 rc_frame_qp;
+ u8 rc_min_qp;
+ u8 rc_max_qp;
+ u8 rc_p_frame_qp;
+ u8 rc_b_frame_qp;
+ enum v4l2_mpeg_video_h264_level level_v4l2;
+ int level;
+ u16 cpb_size;
+};
+
+/**
+ * struct s5p_mfc_mpeg4_enc_params - encoding parameters for h263 and mpeg4
+ */
+struct s5p_mfc_mpeg4_enc_params {
+ /* MPEG4 Only */
+ enum v4l2_mpeg_video_mpeg4_profile profile;
+ int quarter_pixel;
+ /* Common for MPEG4, H263 */
+ u16 vop_time_res;
+ u16 vop_frm_delta;
+ u8 rc_frame_qp;
+ u8 rc_min_qp;
+ u8 rc_max_qp;
+ u8 rc_p_frame_qp;
+ u8 rc_b_frame_qp;
+ enum v4l2_mpeg_video_mpeg4_level level_v4l2;
+ int level;
+};
+
+/**
+ * struct s5p_mfc_enc_params - general encoding parameters
+ */
+struct s5p_mfc_enc_params {
+ u16 width;
+ u16 height;
+
+ u16 gop_size;
+ enum v4l2_mpeg_video_multi_slice_mode slice_mode;
+ u16 slice_mb;
+ u32 slice_bit;
+ u16 intra_refresh_mb;
+ int pad;
+ u8 pad_luma;
+ u8 pad_cb;
+ u8 pad_cr;
+ int rc_frame;
+ u32 rc_bitrate;
+ u16 rc_reaction_coeff;
+ u16 vbv_size;
+
+ enum v4l2_mpeg_video_header_mode seq_hdr_mode;
+ enum v4l2_mpeg_mfc51_video_frame_skip_mode frame_skip_mode;
+ int fixed_target_bit;
+
+ u8 num_b_frame;
+ u32 rc_framerate_num;
+ u32 rc_framerate_denom;
+ int interlace;
+
+ union {
+ struct s5p_mfc_h264_enc_params h264;
+ struct s5p_mfc_mpeg4_enc_params mpeg4;
+ } codec;
+
+};
+
+/**
+ * struct s5p_mfc_codec_ops - codec ops, used by encoding
+ */
+struct s5p_mfc_codec_ops {
+ /* initialization routines */
+ int (*pre_seq_start) (struct s5p_mfc_ctx *ctx);
+ int (*post_seq_start) (struct s5p_mfc_ctx *ctx);
+ /* execution routines */
+ int (*pre_frame_start) (struct s5p_mfc_ctx *ctx);
+ int (*post_frame_start) (struct s5p_mfc_ctx *ctx);
+};
+
+#define call_cop(c, op, args...) \
+ (((c)->c_ops->op) ? \
+ ((c)->c_ops->op(args)) : 0)
+
+/**
+ * struct s5p_mfc_ctx - This struct contains the instance context
+ *
+ * @dev: pointer to the s5p_mfc_dev of the device
+ * @fh: struct v4l2_fh
+ * @num: number of the context that this structure describes
+ * @int_cond: variable used by the waitqueue
+ * @int_type: type of the last interrupt
+ * @int_err: error number received from MFC hw in the interrupt
+ * @queue: waitqueue that can be used to wait for this context to
+ * finish
+ * @src_fmt: source pixelformat information
+ * @dst_fmt: destination pixelformat information
+ * @vq_src: vb2 queue for source buffers
+ * @vq_dst: vb2 queue for destination buffers
+ * @src_queue: driver internal queue for source buffers
+ * @dst_queue: driver internal queue for destination buffers
+ * @src_queue_cnt: number of buffers queued on the source internal queue
+ * @dst_queue_cnt: number of buffers queued on the dest internal queue
+ * @type: type of the instance - decoder or encoder
+ * @state: state of the context
+ * @inst_no: number of hw instance associated with the context
+ * @img_width: width of the image that is decoded or encoded
+ * @img_height: height of the image that is decoded or encoded
+ * @buf_width: width of the buffer for processed image
+ * @buf_height: height of the buffer for processed image
+ * @luma_size: size of a luma plane
+ * @chroma_size: size of a chroma plane
+ * @mv_size: size of a motion vectors buffer
+ * @consumed_stream: number of bytes that have been used so far from the
+ * decoding buffer
+ * @dpb_flush_flag: flag used to indicate that a DPB buffers are being
+ * flushed
+ * @bank1_buf: handle to memory allocated for temporary buffers from
+ * memory bank 1
+ * @bank1_phys: address of the temporary buffers from memory bank 1
+ * @bank1_size: size of the memory allocated for temporary buffers from
+ * memory bank 1
+ * @bank2_buf: handle to memory allocated for temporary buffers from
+ * memory bank 2
+ * @bank2_phys: address of the temporary buffers from memory bank 2
+ * @bank2_size: size of the memory allocated for temporary buffers from
+ * memory bank 2
+ * @capture_state: state of the capture buffers queue
+ * @output_state: state of the output buffers queue
+ * @src_bufs: information on allocated source buffers
+ * @dst_bufs: information on allocated destination buffers
+ * @sequence: counter for the sequence number for v4l2
+ * @dec_dst_flag: flags for buffers queued in the hardware
+ * @dec_src_buf_size: size of the buffer for source buffers in decoding
+ * @codec_mode: number of codec mode used by MFC hw
+ * @slice_interface: slice interface flag
+ * @loop_filter_mpeg4: loop filter for MPEG4 flag
+ * @display_delay: value of the display delay for H264
+ * @display_delay_enable: display delay for H264 enable flag
+ * @after_packed_pb: flag used to track buffer when stream is in
+ * Packed PB format
+ * @dpb_count: count of the DPB buffers required by MFC hw
+ * @total_dpb_count: count of DPB buffers with additional buffers
+ * requested by the application
+ * @ctx_buf: handle to the memory associated with this context
+ * @ctx_phys: address of the memory associated with this context
+ * @ctx_size: size of the memory associated with this context
+ * @desc_buf: description buffer for decoding handle
+ * @desc_phys: description buffer for decoding address
+ * @shm_alloc: handle for the shared memory buffer
+ * @shm: virtual address for the shared memory buffer
+ * @shm_ofs: address offset for shared memory
+ * @enc_params: encoding parameters for MFC
+ * @enc_dst_buf_size: size of the buffers for encoder output
+ * @frame_type: used to force the type of the next encoded frame
+ * @ref_queue: list of the reference buffers for encoding
+ * @ref_queue_cnt: number of the buffers in the reference list
+ * @c_ops: ops for encoding
+ * @ctrls: array of controls, used when adding controls to the
+ * v4l2 control framework
+ * @ctrl_handler: handler for v4l2 framework
+ */
+struct s5p_mfc_ctx {
+ struct s5p_mfc_dev *dev;
+ struct v4l2_fh fh;
+
+ int num;
+
+ int int_cond;
+ int int_type;
+ unsigned int int_err;
+ wait_queue_head_t queue;
+
+ struct s5p_mfc_fmt *src_fmt;
+ struct s5p_mfc_fmt *dst_fmt;
+
+ struct vb2_queue vq_src;
+ struct vb2_queue vq_dst;
+
+ struct list_head src_queue;
+ struct list_head dst_queue;
+
+ unsigned int src_queue_cnt;
+ unsigned int dst_queue_cnt;
+
+ enum s5p_mfc_inst_type type;
+ enum s5p_mfc_inst_state state;
+ int inst_no;
+
+ /* Image parameters */
+ int img_width;
+ int img_height;
+ int buf_width;
+ int buf_height;
+
+ int luma_size;
+ int chroma_size;
+ int mv_size;
+
+ unsigned long consumed_stream;
+
+ unsigned int dpb_flush_flag;
+
+ /* Buffers */
+ void *bank1_buf;
+ size_t bank1_phys;
+ size_t bank1_size;
+
+ void *bank2_buf;
+ size_t bank2_phys;
+ size_t bank2_size;
+
+ enum s5p_mfc_queue_state capture_state;
+ enum s5p_mfc_queue_state output_state;
+
+ struct s5p_mfc_buf src_bufs[MFC_MAX_BUFFERS];
+ int src_bufs_cnt;
+ struct s5p_mfc_buf dst_bufs[MFC_MAX_BUFFERS];
+ int dst_bufs_cnt;
+
+ unsigned int sequence;
+ unsigned long dec_dst_flag;
+ size_t dec_src_buf_size;
+
+ /* Control values */
+ int codec_mode;
+ int slice_interface;
+ int loop_filter_mpeg4;
+ int display_delay;
+ int display_delay_enable;
+ int after_packed_pb;
+
+ int dpb_count;
+ int total_dpb_count;
+
+ /* Buffers */
+ void *ctx_buf;
+ size_t ctx_phys;
+ size_t ctx_ofs;
+ size_t ctx_size;
+
+ void *desc_buf;
+ size_t desc_phys;
+
+
+ void *shm_alloc;
+ void *shm;
+ size_t shm_ofs;
+
+ struct s5p_mfc_enc_params enc_params;
+
+ size_t enc_dst_buf_size;
+
+ enum v4l2_mpeg_mfc51_video_force_frame_type force_frame_type;
+
+ struct list_head ref_queue;
+ unsigned int ref_queue_cnt;
+
+ struct s5p_mfc_codec_ops *c_ops;
+
+ struct v4l2_ctrl *ctrls[MFC_MAX_CTRLS];
+ struct v4l2_ctrl_handler ctrl_handler;
+};
+
+/*
+ * struct s5p_mfc_fmt - structure used to store information about pixelformats
+ * used by the MFC
+ */
+struct s5p_mfc_fmt {
+ char *name;
+ u32 fourcc;
+ u32 codec_mode;
+ enum s5p_mfc_fmt_type type;
+ u32 num_planes;
+};
+
+/**
+ * struct mfc_control - structure used to store information about MFC controls
+ * it is used to initialize the control framework.
+ */
+struct mfc_control {
+ __u32 id;
+ enum v4l2_ctrl_type type;
+ __u8 name[32]; /* Whatever */
+ __s32 minimum; /* Note signedness */
+ __s32 maximum;
+ __s32 step;
+ __u32 menu_skip_mask;
+ __s32 default_value;
+ __u32 flags;
+ __u32 reserved[2];
+ __u8 is_volatile;
+};
+
+
+#define fh_to_ctx(__fh) container_of(__fh, struct s5p_mfc_ctx, fh)
+#define ctrl_to_ctx(__ctrl) \
+ container_of((__ctrl)->handler, struct s5p_mfc_ctx, ctrl_handler)
+
+#endif /* S5P_MFC_COMMON_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c
new file mode 100644
index 00000000..f2481a85
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c
@@ -0,0 +1,343 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include "regs-mfc.h"
+#include "s5p_mfc_cmd.h"
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_debug.h"
+#include "s5p_mfc_intr.h"
+#include "s5p_mfc_pm.h"
+
+static void *s5p_mfc_bitproc_buf;
+static size_t s5p_mfc_bitproc_phys;
+static unsigned char *s5p_mfc_bitproc_virt;
+
+/* Allocate and load firmware */
+int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev)
+{
+ struct firmware *fw_blob;
+ size_t bank2_base_phys;
+ void *b_base;
+ int err;
+
+ /* Firmare has to be present as a separate file or compiled
+ * into kernel. */
+ mfc_debug_enter();
+ err = request_firmware((const struct firmware **)&fw_blob,
+ "s5p-mfc.fw", dev->v4l2_dev.dev);
+ if (err != 0) {
+ mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n");
+ return -EINVAL;
+ }
+ dev->fw_size = ALIGN(fw_blob->size, FIRMWARE_ALIGN);
+ if (s5p_mfc_bitproc_buf) {
+ mfc_err("Attempting to allocate firmware when it seems that it is already loaded\n");
+ release_firmware(fw_blob);
+ return -ENOMEM;
+ }
+ s5p_mfc_bitproc_buf = vb2_dma_contig_memops.alloc(
+ dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], dev->fw_size);
+ if (IS_ERR(s5p_mfc_bitproc_buf)) {
+ s5p_mfc_bitproc_buf = 0;
+ mfc_err("Allocating bitprocessor buffer failed\n");
+ release_firmware(fw_blob);
+ return -ENOMEM;
+ }
+ s5p_mfc_bitproc_phys = s5p_mfc_mem_cookie(
+ dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], s5p_mfc_bitproc_buf);
+ if (s5p_mfc_bitproc_phys & ((1 << MFC_BASE_ALIGN_ORDER) - 1)) {
+ mfc_err("The base memory for bank 1 is not aligned to 128KB\n");
+ vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
+ s5p_mfc_bitproc_phys = 0;
+ s5p_mfc_bitproc_buf = 0;
+ release_firmware(fw_blob);
+ return -EIO;
+ }
+ s5p_mfc_bitproc_virt = vb2_dma_contig_memops.vaddr(s5p_mfc_bitproc_buf);
+ if (!s5p_mfc_bitproc_virt) {
+ mfc_err("Bitprocessor memory remap failed\n");
+ vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
+ s5p_mfc_bitproc_phys = 0;
+ s5p_mfc_bitproc_buf = 0;
+ release_firmware(fw_blob);
+ return -EIO;
+ }
+ dev->bank1 = s5p_mfc_bitproc_phys;
+ b_base = vb2_dma_contig_memops.alloc(
+ dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], 1 << MFC_BANK2_ALIGN_ORDER);
+ if (IS_ERR(b_base)) {
+ vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
+ s5p_mfc_bitproc_phys = 0;
+ s5p_mfc_bitproc_buf = 0;
+ mfc_err("Allocating bank2 base failed\n");
+ release_firmware(fw_blob);
+ return -ENOMEM;
+ }
+ bank2_base_phys = s5p_mfc_mem_cookie(
+ dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], b_base);
+ vb2_dma_contig_memops.put(b_base);
+ if (bank2_base_phys & ((1 << MFC_BASE_ALIGN_ORDER) - 1)) {
+ mfc_err("The base memory for bank 2 is not aligned to 128KB\n");
+ vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
+ s5p_mfc_bitproc_phys = 0;
+ s5p_mfc_bitproc_buf = 0;
+ release_firmware(fw_blob);
+ return -EIO;
+ }
+ dev->bank2 = bank2_base_phys;
+ memcpy(s5p_mfc_bitproc_virt, fw_blob->data, fw_blob->size);
+ wmb();
+ release_firmware(fw_blob);
+ mfc_debug_leave();
+ return 0;
+}
+
+/* Reload firmware to MFC */
+int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev)
+{
+ struct firmware *fw_blob;
+ int err;
+
+ /* Firmare has to be present as a separate file or compiled
+ * into kernel. */
+ mfc_debug_enter();
+ err = request_firmware((const struct firmware **)&fw_blob,
+ "s5p-mfc.fw", dev->v4l2_dev.dev);
+ if (err != 0) {
+ mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n");
+ return -EINVAL;
+ }
+ if (fw_blob->size > dev->fw_size) {
+ mfc_err("MFC firmware is too big to be loaded\n");
+ release_firmware(fw_blob);
+ return -ENOMEM;
+ }
+ if (s5p_mfc_bitproc_buf == 0 || s5p_mfc_bitproc_phys == 0) {
+ mfc_err("MFC firmware is not allocated or was not mapped correctly\n");
+ release_firmware(fw_blob);
+ return -EINVAL;
+ }
+ memcpy(s5p_mfc_bitproc_virt, fw_blob->data, fw_blob->size);
+ wmb();
+ release_firmware(fw_blob);
+ mfc_debug_leave();
+ return 0;
+}
+
+/* Release firmware memory */
+int s5p_mfc_release_firmware(struct s5p_mfc_dev *dev)
+{
+ /* Before calling this function one has to make sure
+ * that MFC is no longer processing */
+ if (!s5p_mfc_bitproc_buf)
+ return -EINVAL;
+ vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
+ s5p_mfc_bitproc_virt = 0;
+ s5p_mfc_bitproc_phys = 0;
+ s5p_mfc_bitproc_buf = 0;
+ return 0;
+}
+
+/* Reset the device */
+int s5p_mfc_reset(struct s5p_mfc_dev *dev)
+{
+ unsigned int mc_status;
+ unsigned long timeout;
+
+ mfc_debug_enter();
+ /* Stop procedure */
+ /* reset RISC */
+ mfc_write(dev, 0x3f6, S5P_FIMV_SW_RESET);
+ /* All reset except for MC */
+ mfc_write(dev, 0x3e2, S5P_FIMV_SW_RESET);
+ mdelay(10);
+
+ timeout = jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT);
+ /* Check MC status */
+ do {
+ if (time_after(jiffies, timeout)) {
+ mfc_err("Timeout while resetting MFC\n");
+ return -EIO;
+ }
+
+ mc_status = mfc_read(dev, S5P_FIMV_MC_STATUS);
+
+ } while (mc_status & 0x3);
+
+ mfc_write(dev, 0x0, S5P_FIMV_SW_RESET);
+ mfc_write(dev, 0x3fe, S5P_FIMV_SW_RESET);
+ mfc_debug_leave();
+ return 0;
+}
+
+static inline void s5p_mfc_init_memctrl(struct s5p_mfc_dev *dev)
+{
+ mfc_write(dev, dev->bank1, S5P_FIMV_MC_DRAMBASE_ADR_A);
+ mfc_write(dev, dev->bank2, S5P_FIMV_MC_DRAMBASE_ADR_B);
+ mfc_debug(2, "Bank1: %08x, Bank2: %08x\n", dev->bank1, dev->bank2);
+}
+
+static inline void s5p_mfc_clear_cmds(struct s5p_mfc_dev *dev)
+{
+ mfc_write(dev, 0xffffffff, S5P_FIMV_SI_CH0_INST_ID);
+ mfc_write(dev, 0xffffffff, S5P_FIMV_SI_CH1_INST_ID);
+ mfc_write(dev, 0, S5P_FIMV_RISC2HOST_CMD);
+ mfc_write(dev, 0, S5P_FIMV_HOST2RISC_CMD);
+}
+
+/* Initialize hardware */
+int s5p_mfc_init_hw(struct s5p_mfc_dev *dev)
+{
+ unsigned int ver;
+ int ret;
+
+ mfc_debug_enter();
+ if (!s5p_mfc_bitproc_buf)
+ return -EINVAL;
+
+ /* 0. MFC reset */
+ mfc_debug(2, "MFC reset..\n");
+ s5p_mfc_clock_on();
+ ret = s5p_mfc_reset(dev);
+ if (ret) {
+ mfc_err("Failed to reset MFC - timeout\n");
+ return ret;
+ }
+ mfc_debug(2, "Done MFC reset..\n");
+ /* 1. Set DRAM base Addr */
+ s5p_mfc_init_memctrl(dev);
+ /* 2. Initialize registers of channel I/F */
+ s5p_mfc_clear_cmds(dev);
+ /* 3. Release reset signal to the RISC */
+ s5p_mfc_clean_dev_int_flags(dev);
+ mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET);
+ mfc_debug(2, "Will now wait for completion of firmware transfer\n");
+ if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_FW_STATUS_RET)) {
+ mfc_err("Failed to load firmware\n");
+ s5p_mfc_reset(dev);
+ s5p_mfc_clock_off();
+ return -EIO;
+ }
+ s5p_mfc_clean_dev_int_flags(dev);
+ /* 4. Initialize firmware */
+ ret = s5p_mfc_sys_init_cmd(dev);
+ if (ret) {
+ mfc_err("Failed to send command to MFC - timeout\n");
+ s5p_mfc_reset(dev);
+ s5p_mfc_clock_off();
+ return ret;
+ }
+ mfc_debug(2, "Ok, now will write a command to init the system\n");
+ if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_SYS_INIT_RET)) {
+ mfc_err("Failed to load firmware\n");
+ s5p_mfc_reset(dev);
+ s5p_mfc_clock_off();
+ return -EIO;
+ }
+ dev->int_cond = 0;
+ if (dev->int_err != 0 || dev->int_type !=
+ S5P_FIMV_R2H_CMD_SYS_INIT_RET) {
+ /* Failure. */
+ mfc_err("Failed to init firmware - error: %d int: %d\n",
+ dev->int_err, dev->int_type);
+ s5p_mfc_reset(dev);
+ s5p_mfc_clock_off();
+ return -EIO;
+ }
+ ver = mfc_read(dev, S5P_FIMV_FW_VERSION);
+ mfc_debug(2, "MFC F/W version : %02xyy, %02xmm, %02xdd\n",
+ (ver >> 16) & 0xFF, (ver >> 8) & 0xFF, ver & 0xFF);
+ s5p_mfc_clock_off();
+ mfc_debug_leave();
+ return 0;
+}
+
+
+int s5p_mfc_sleep(struct s5p_mfc_dev *dev)
+{
+ int ret;
+
+ mfc_debug_enter();
+ s5p_mfc_clock_on();
+ s5p_mfc_clean_dev_int_flags(dev);
+ ret = s5p_mfc_sleep_cmd(dev);
+ if (ret) {
+ mfc_err("Failed to send command to MFC - timeout\n");
+ return ret;
+ }
+ if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_SLEEP_RET)) {
+ mfc_err("Failed to sleep\n");
+ return -EIO;
+ }
+ s5p_mfc_clock_off();
+ dev->int_cond = 0;
+ if (dev->int_err != 0 || dev->int_type !=
+ S5P_FIMV_R2H_CMD_SLEEP_RET) {
+ /* Failure. */
+ mfc_err("Failed to sleep - error: %d int: %d\n", dev->int_err,
+ dev->int_type);
+ return -EIO;
+ }
+ mfc_debug_leave();
+ return ret;
+}
+
+int s5p_mfc_wakeup(struct s5p_mfc_dev *dev)
+{
+ int ret;
+
+ mfc_debug_enter();
+ /* 0. MFC reset */
+ mfc_debug(2, "MFC reset..\n");
+ s5p_mfc_clock_on();
+ ret = s5p_mfc_reset(dev);
+ if (ret) {
+ mfc_err("Failed to reset MFC - timeout\n");
+ return ret;
+ }
+ mfc_debug(2, "Done MFC reset..\n");
+ /* 1. Set DRAM base Addr */
+ s5p_mfc_init_memctrl(dev);
+ /* 2. Initialize registers of channel I/F */
+ s5p_mfc_clear_cmds(dev);
+ s5p_mfc_clean_dev_int_flags(dev);
+ /* 3. Initialize firmware */
+ ret = s5p_mfc_wakeup_cmd(dev);
+ if (ret) {
+ mfc_err("Failed to send command to MFC - timeout\n");
+ return ret;
+ }
+ /* 4. Release reset signal to the RISC */
+ mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET);
+ mfc_debug(2, "Ok, now will write a command to wakeup the system\n");
+ if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_WAKEUP_RET)) {
+ mfc_err("Failed to load firmware\n");
+ return -EIO;
+ }
+ s5p_mfc_clock_off();
+ dev->int_cond = 0;
+ if (dev->int_err != 0 || dev->int_type !=
+ S5P_FIMV_R2H_CMD_WAKEUP_RET) {
+ /* Failure. */
+ mfc_err("Failed to wakeup - error: %d int: %d\n", dev->int_err,
+ dev->int_type);
+ return -EIO;
+ }
+ mfc_debug_leave();
+ return 0;
+}
+
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.h b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.h
new file mode 100644
index 00000000..61dc23b7
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.h
@@ -0,0 +1,29 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef S5P_MFC_CTRL_H
+#define S5P_MFC_CTRL_H
+
+#include "s5p_mfc_common.h"
+
+int s5p_mfc_release_firmware(struct s5p_mfc_dev *dev);
+int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev);
+int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev);
+
+int s5p_mfc_init_hw(struct s5p_mfc_dev *dev);
+
+int s5p_mfc_sleep(struct s5p_mfc_dev *dev);
+int s5p_mfc_wakeup(struct s5p_mfc_dev *dev);
+
+int s5p_mfc_reset(struct s5p_mfc_dev *dev);
+
+#endif /* S5P_MFC_CTRL_H */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_debug.h b/drivers/media/video/s5p-mfc/s5p_mfc_debug.h
new file mode 100644
index 00000000..ecb8616a
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_debug.h
@@ -0,0 +1,48 @@
+/*
+ * drivers/media/video/samsung/mfc5/s5p_mfc_debug.h
+ *
+ * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
+ * This file contains debug macros
+ *
+ * Kamil Debski, Copyright (c) 2011 Samsung Electronics
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef S5P_MFC_DEBUG_H_
+#define S5P_MFC_DEBUG_H_
+
+#define DEBUG
+
+#ifdef DEBUG
+extern int debug;
+
+#define mfc_debug(level, fmt, args...) \
+ do { \
+ if (debug >= level) \
+ printk(KERN_DEBUG "%s:%d: " fmt, \
+ __func__, __LINE__, ##args); \
+ } while (0)
+#else
+#define mfc_debug(level, fmt, args...)
+#endif
+
+#define mfc_debug_enter() mfc_debug(5, "enter")
+#define mfc_debug_leave() mfc_debug(5, "leave")
+
+#define mfc_err(fmt, args...) \
+ do { \
+ printk(KERN_ERR "%s:%d: " fmt, \
+ __func__, __LINE__, ##args); \
+ } while (0)
+
+#define mfc_info(fmt, args...) \
+ do { \
+ printk(KERN_INFO "%s:%d: " fmt, \
+ __func__, __LINE__, ##args); \
+ } while (0)
+
+#endif /* S5P_MFC_DEBUG_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c
new file mode 100644
index 00000000..c25ec022
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c
@@ -0,0 +1,1037 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_dec.c
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ * Kamil Debski, <k.debski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/videodev2.h>
+#include <linux/workqueue.h>
+#include <media/v4l2-ctrls.h>
+#include <media/videobuf2-core.h>
+#include "regs-mfc.h"
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_debug.h"
+#include "s5p_mfc_dec.h"
+#include "s5p_mfc_intr.h"
+#include "s5p_mfc_opr.h"
+#include "s5p_mfc_pm.h"
+#include "s5p_mfc_shm.h"
+
+static struct s5p_mfc_fmt formats[] = {
+ {
+ .name = "4:2:0 2 Planes 64x32 Tiles",
+ .fourcc = V4L2_PIX_FMT_NV12MT,
+ .codec_mode = S5P_FIMV_CODEC_NONE,
+ .type = MFC_FMT_RAW,
+ .num_planes = 2,
+ },
+ {
+ .name = "4:2:0 2 Planes",
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .codec_mode = S5P_FIMV_CODEC_NONE,
+ .type = MFC_FMT_RAW,
+ .num_planes = 2,
+ },
+ {
+ .name = "H264 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_H264,
+ .codec_mode = S5P_FIMV_CODEC_H264_DEC,
+ .type = MFC_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "H263 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_H263,
+ .codec_mode = S5P_FIMV_CODEC_H263_DEC,
+ .type = MFC_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "MPEG1 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_MPEG1,
+ .codec_mode = S5P_FIMV_CODEC_MPEG2_DEC,
+ .type = MFC_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "MPEG2 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_MPEG2,
+ .codec_mode = S5P_FIMV_CODEC_MPEG2_DEC,
+ .type = MFC_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "MPEG4 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_MPEG4,
+ .codec_mode = S5P_FIMV_CODEC_MPEG4_DEC,
+ .type = MFC_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "XviD Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_XVID,
+ .codec_mode = S5P_FIMV_CODEC_MPEG4_DEC,
+ .type = MFC_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "VC1 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_VC1_ANNEX_G,
+ .codec_mode = S5P_FIMV_CODEC_VC1_DEC,
+ .type = MFC_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "VC1 RCV Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_VC1_ANNEX_L,
+ .codec_mode = S5P_FIMV_CODEC_VC1RCV_DEC,
+ .type = MFC_FMT_DEC,
+ .num_planes = 1,
+ },
+};
+
+#define NUM_FORMATS ARRAY_SIZE(formats)
+
+/* Find selected format description */
+static struct s5p_mfc_fmt *find_format(struct v4l2_format *f, unsigned int t)
+{
+ unsigned int i;
+
+ for (i = 0; i < NUM_FORMATS; i++) {
+ if (formats[i].fourcc == f->fmt.pix_mp.pixelformat &&
+ formats[i].type == t)
+ return &formats[i];
+ }
+ return NULL;
+}
+
+static struct mfc_control controls[] = {
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "H264 Display Delay",
+ .minimum = 0,
+ .maximum = 16383,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY_ENABLE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "H264 Display Delay Enable",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mpeg4 Loop Filter Enable",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Slice Interface Enable",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MIN_BUFFERS_FOR_CAPTURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Minimum number of cap bufs",
+ .minimum = 1,
+ .maximum = 32,
+ .step = 1,
+ .default_value = 1,
+ .is_volatile = 1,
+ },
+};
+
+#define NUM_CTRLS ARRAY_SIZE(controls)
+
+/* Check whether a context should be run on hardware */
+static int s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx)
+{
+ /* Context is to parse header */
+ if (ctx->src_queue_cnt >= 1 && ctx->state == MFCINST_GOT_INST)
+ return 1;
+ /* Context is to decode a frame */
+ if (ctx->src_queue_cnt >= 1 &&
+ ctx->state == MFCINST_RUNNING &&
+ ctx->dst_queue_cnt >= ctx->dpb_count)
+ return 1;
+ /* Context is to return last frame */
+ if (ctx->state == MFCINST_FINISHING &&
+ ctx->dst_queue_cnt >= ctx->dpb_count)
+ return 1;
+ /* Context is to set buffers */
+ if (ctx->src_queue_cnt >= 1 &&
+ ctx->state == MFCINST_HEAD_PARSED &&
+ ctx->capture_state == QUEUE_BUFS_MMAPED)
+ return 1;
+ /* Resolution change */
+ if ((ctx->state == MFCINST_RES_CHANGE_INIT ||
+ ctx->state == MFCINST_RES_CHANGE_FLUSH) &&
+ ctx->dst_queue_cnt >= ctx->dpb_count)
+ return 1;
+ if (ctx->state == MFCINST_RES_CHANGE_END &&
+ ctx->src_queue_cnt >= 1)
+ return 1;
+ mfc_debug(2, "ctx is not ready\n");
+ return 0;
+}
+
+static struct s5p_mfc_codec_ops decoder_codec_ops = {
+ .pre_seq_start = NULL,
+ .post_seq_start = NULL,
+ .pre_frame_start = NULL,
+ .post_frame_start = NULL,
+};
+
+/* Query capabilities of the device */
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct s5p_mfc_dev *dev = video_drvdata(file);
+
+ strncpy(cap->driver, dev->plat_dev->name, sizeof(cap->driver) - 1);
+ strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1);
+ cap->bus_info[0] = 0;
+ cap->version = KERNEL_VERSION(1, 0, 0);
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+ V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING;
+ return 0;
+}
+
+/* Enumerate format */
+static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)
+{
+ struct s5p_mfc_fmt *fmt;
+ int i, j = 0;
+
+ for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+ if (mplane && formats[i].num_planes == 1)
+ continue;
+ else if (!mplane && formats[i].num_planes > 1)
+ continue;
+ if (out && formats[i].type != MFC_FMT_DEC)
+ continue;
+ else if (!out && formats[i].type != MFC_FMT_RAW)
+ continue;
+
+ if (j == f->index)
+ break;
+ ++j;
+ }
+ if (i == ARRAY_SIZE(formats))
+ return -EINVAL;
+ fmt = &formats[i];
+ strlcpy(f->description, fmt->name, sizeof(f->description));
+ f->pixelformat = fmt->fourcc;
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv,
+ struct v4l2_fmtdesc *f)
+{
+ return vidioc_enum_fmt(f, false, false);
+}
+
+static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,
+ struct v4l2_fmtdesc *f)
+{
+ return vidioc_enum_fmt(f, true, false);
+}
+
+static int vidioc_enum_fmt_vid_out(struct file *file, void *prov,
+ struct v4l2_fmtdesc *f)
+{
+ return vidioc_enum_fmt(f, false, true);
+}
+
+static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov,
+ struct v4l2_fmtdesc *f)
+{
+ return vidioc_enum_fmt(f, true, true);
+}
+
+/* Get format */
+static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ struct v4l2_pix_format_mplane *pix_mp;
+
+ mfc_debug_enter();
+ pix_mp = &f->fmt.pix_mp;
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+ (ctx->state == MFCINST_GOT_INST || ctx->state ==
+ MFCINST_RES_CHANGE_END)) {
+ /* If the MFC is parsing the header,
+ * so wait until it is finished */
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_wait_for_done_ctx(ctx, S5P_FIMV_R2H_CMD_SEQ_DONE_RET,
+ 0);
+ }
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+ ctx->state >= MFCINST_HEAD_PARSED &&
+ ctx->state < MFCINST_ABORT) {
+ /* This is run on CAPTURE (decode output) */
+ /* Width and height are set to the dimensions
+ of the movie, the buffer is bigger and
+ further processing stages should crop to this
+ rectangle. */
+ pix_mp->width = ctx->buf_width;
+ pix_mp->height = ctx->buf_height;
+ pix_mp->field = V4L2_FIELD_NONE;
+ pix_mp->num_planes = 2;
+ /* Set pixelformat to the format in which MFC
+ outputs the decoded frame */
+ pix_mp->pixelformat = V4L2_PIX_FMT_NV12MT;
+ pix_mp->plane_fmt[0].bytesperline = ctx->buf_width;
+ pix_mp->plane_fmt[0].sizeimage = ctx->luma_size;
+ pix_mp->plane_fmt[1].bytesperline = ctx->buf_width;
+ pix_mp->plane_fmt[1].sizeimage = ctx->chroma_size;
+ } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ /* This is run on OUTPUT
+ The buffer contains compressed image
+ so width and height have no meaning */
+ pix_mp->width = 0;
+ pix_mp->height = 0;
+ pix_mp->field = V4L2_FIELD_NONE;
+ pix_mp->plane_fmt[0].bytesperline = ctx->dec_src_buf_size;
+ pix_mp->plane_fmt[0].sizeimage = ctx->dec_src_buf_size;
+ pix_mp->pixelformat = ctx->src_fmt->fourcc;
+ pix_mp->num_planes = ctx->src_fmt->num_planes;
+ } else {
+ mfc_err("Format could not be read\n");
+ mfc_debug(2, "%s-- with error\n", __func__);
+ return -EINVAL;
+ }
+ mfc_debug_leave();
+ return 0;
+}
+
+/* Try format */
+static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct s5p_mfc_fmt *fmt;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ mfc_err("This node supports decoding only\n");
+ return -EINVAL;
+ }
+ fmt = find_format(f, MFC_FMT_DEC);
+ if (!fmt) {
+ mfc_err("Unsupported format\n");
+ return -EINVAL;
+ }
+ if (fmt->type != MFC_FMT_DEC) {
+ mfc_err("\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* Set format */
+static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct s5p_mfc_dev *dev = video_drvdata(file);
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ int ret = 0;
+ struct s5p_mfc_fmt *fmt;
+ struct v4l2_pix_format_mplane *pix_mp;
+
+ mfc_debug_enter();
+ ret = vidioc_try_fmt(file, priv, f);
+ pix_mp = &f->fmt.pix_mp;
+ if (ret)
+ return ret;
+ if (ctx->vq_src.streaming || ctx->vq_dst.streaming) {
+ v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__);
+ ret = -EBUSY;
+ goto out;
+ }
+ fmt = find_format(f, MFC_FMT_DEC);
+ if (!fmt || fmt->codec_mode == S5P_FIMV_CODEC_NONE) {
+ mfc_err("Unknown codec\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ if (fmt->type != MFC_FMT_DEC) {
+ mfc_err("Wrong format selected, you should choose "
+ "format for decoding\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ ctx->src_fmt = fmt;
+ ctx->codec_mode = fmt->codec_mode;
+ mfc_debug(2, "The codec number is: %d\n", ctx->codec_mode);
+ pix_mp->height = 0;
+ pix_mp->width = 0;
+ if (pix_mp->plane_fmt[0].sizeimage)
+ ctx->dec_src_buf_size = pix_mp->plane_fmt[0].sizeimage;
+ else
+ pix_mp->plane_fmt[0].sizeimage = ctx->dec_src_buf_size =
+ DEF_CPB_SIZE;
+ pix_mp->plane_fmt[0].bytesperline = 0;
+ ctx->state = MFCINST_INIT;
+out:
+ mfc_debug_leave();
+ return ret;
+}
+
+/* Reqeust buffers */
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct s5p_mfc_dev *dev = video_drvdata(file);
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ int ret = 0;
+ unsigned long flags;
+
+ if (reqbufs->memory != V4L2_MEMORY_MMAP) {
+ mfc_err("Only V4L2_MEMORY_MAP is supported\n");
+ return -EINVAL;
+ }
+ if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ /* Can only request buffers after an instance has been opened.*/
+ if (ctx->state == MFCINST_INIT) {
+ ctx->src_bufs_cnt = 0;
+ if (reqbufs->count == 0) {
+ mfc_debug(2, "Freeing buffers\n");
+ s5p_mfc_clock_on();
+ ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
+ s5p_mfc_clock_off();
+ return ret;
+ }
+ /* Decoding */
+ if (ctx->output_state != QUEUE_FREE) {
+ mfc_err("Bufs have already been requested\n");
+ return -EINVAL;
+ }
+ s5p_mfc_clock_on();
+ ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
+ s5p_mfc_clock_off();
+ if (ret) {
+ mfc_err("vb2_reqbufs on output failed\n");
+ return ret;
+ }
+ mfc_debug(2, "vb2_reqbufs: %d\n", ret);
+ ctx->output_state = QUEUE_BUFS_REQUESTED;
+ }
+ } else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ ctx->dst_bufs_cnt = 0;
+ if (reqbufs->count == 0) {
+ mfc_debug(2, "Freeing buffers\n");
+ s5p_mfc_clock_on();
+ ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ s5p_mfc_clock_off();
+ return ret;
+ }
+ if (ctx->capture_state != QUEUE_FREE) {
+ mfc_err("Bufs have already been requested\n");
+ return -EINVAL;
+ }
+ ctx->capture_state = QUEUE_BUFS_REQUESTED;
+ s5p_mfc_clock_on();
+ ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ s5p_mfc_clock_off();
+ if (ret) {
+ mfc_err("vb2_reqbufs on capture failed\n");
+ return ret;
+ }
+ if (reqbufs->count < ctx->dpb_count) {
+ mfc_err("Not enough buffers allocated\n");
+ reqbufs->count = 0;
+ s5p_mfc_clock_on();
+ ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ s5p_mfc_clock_off();
+ return -ENOMEM;
+ }
+ ctx->total_dpb_count = reqbufs->count;
+ ret = s5p_mfc_alloc_codec_buffers(ctx);
+ if (ret) {
+ mfc_err("Failed to allocate decoding buffers\n");
+ reqbufs->count = 0;
+ s5p_mfc_clock_on();
+ ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ s5p_mfc_clock_off();
+ return -ENOMEM;
+ }
+ if (ctx->dst_bufs_cnt == ctx->total_dpb_count) {
+ ctx->capture_state = QUEUE_BUFS_MMAPED;
+ } else {
+ mfc_err("Not all buffers passed to buf_init\n");
+ reqbufs->count = 0;
+ s5p_mfc_clock_on();
+ ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ s5p_mfc_release_codec_buffers(ctx);
+ s5p_mfc_clock_off();
+ return -ENOMEM;
+ }
+ if (s5p_mfc_ctx_ready(ctx)) {
+ spin_lock_irqsave(&dev->condlock, flags);
+ set_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock_irqrestore(&dev->condlock, flags);
+ }
+ s5p_mfc_try_run(dev);
+ s5p_mfc_wait_for_done_ctx(ctx,
+ S5P_FIMV_R2H_CMD_INIT_BUFFERS_RET, 0);
+ }
+ return ret;
+}
+
+/* Query buffer */
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ int ret;
+ int i;
+
+ if (buf->memory != V4L2_MEMORY_MMAP) {
+ mfc_err("Only mmaped buffers can be used\n");
+ return -EINVAL;
+ }
+ mfc_debug(2, "State: %d, buf->type: %d\n", ctx->state, buf->type);
+ if (ctx->state == MFCINST_INIT &&
+ buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ret = vb2_querybuf(&ctx->vq_src, buf);
+ } else if (ctx->state == MFCINST_RUNNING &&
+ buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ ret = vb2_querybuf(&ctx->vq_dst, buf);
+ for (i = 0; i < buf->length; i++)
+ buf->m.planes[i].m.mem_offset += DST_QUEUE_OFF_BASE;
+ } else {
+ mfc_err("vidioc_querybuf called in an inappropriate state\n");
+ ret = -EINVAL;
+ }
+ mfc_debug_leave();
+ return ret;
+}
+
+/* Queue a buffer */
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (ctx->state == MFCINST_ERROR) {
+ mfc_err("Call on QBUF after unrecoverable error\n");
+ return -EIO;
+ }
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return vb2_qbuf(&ctx->vq_src, buf);
+ else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return vb2_qbuf(&ctx->vq_dst, buf);
+ return -EINVAL;
+}
+
+/* Dequeue a buffer */
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (ctx->state == MFCINST_ERROR) {
+ mfc_err("Call on DQBUF after unrecoverable error\n");
+ return -EIO;
+ }
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK);
+ else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK);
+ return -EINVAL;
+}
+
+/* Stream on */
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long flags;
+ int ret = -EINVAL;
+
+ mfc_debug_enter();
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+
+ if (ctx->state == MFCINST_INIT) {
+ ctx->dst_bufs_cnt = 0;
+ ctx->src_bufs_cnt = 0;
+ ctx->capture_state = QUEUE_FREE;
+ ctx->output_state = QUEUE_FREE;
+ s5p_mfc_alloc_instance_buffer(ctx);
+ s5p_mfc_alloc_dec_temp_buffers(ctx);
+ spin_lock_irqsave(&dev->condlock, flags);
+ set_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock_irqrestore(&dev->condlock, flags);
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_try_run(dev);
+
+ if (s5p_mfc_wait_for_done_ctx(ctx,
+ S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET, 0)) {
+ /* Error or timeout */
+ mfc_err("Error getting instance from hardware\n");
+ s5p_mfc_release_instance_buffer(ctx);
+ s5p_mfc_release_dec_desc_buffer(ctx);
+ return -EIO;
+ }
+ mfc_debug(2, "Got instance number: %d\n", ctx->inst_no);
+ }
+ ret = vb2_streamon(&ctx->vq_src, type);
+ }
+ else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ ret = vb2_streamon(&ctx->vq_dst, type);
+ mfc_debug_leave();
+ return ret;
+}
+
+/* Stream off, which equals to a pause */
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return vb2_streamoff(&ctx->vq_src, type);
+ else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return vb2_streamoff(&ctx->vq_dst, type);
+ return -EINVAL;
+}
+
+/* Set controls - v4l2 control framework */
+static int s5p_mfc_dec_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct s5p_mfc_ctx *ctx = ctrl_to_ctx(ctrl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY:
+ ctx->loop_filter_mpeg4 = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY_ENABLE:
+ ctx->display_delay_enable = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER:
+ ctx->display_delay = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE:
+ ctx->slice_interface = ctrl->val;
+ break;
+ default:
+ mfc_err("Invalid control 0x%08x\n", ctrl->id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int s5p_mfc_dec_g_v_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct s5p_mfc_ctx *ctx = ctrl_to_ctx(ctrl);
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
+ if (ctx->state >= MFCINST_HEAD_PARSED &&
+ ctx->state < MFCINST_ABORT) {
+ ctrl->val = ctx->dpb_count;
+ break;
+ } else if (ctx->state != MFCINST_INIT) {
+ v4l2_err(&dev->v4l2_dev, "Decoding not initialised\n");
+ return -EINVAL;
+ }
+ /* Should wait for the header to be parsed */
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_wait_for_done_ctx(ctx,
+ S5P_FIMV_R2H_CMD_SEQ_DONE_RET, 0);
+ if (ctx->state >= MFCINST_HEAD_PARSED &&
+ ctx->state < MFCINST_ABORT) {
+ ctrl->val = ctx->dpb_count;
+ } else {
+ v4l2_err(&dev->v4l2_dev, "Decoding not initialised\n");
+ return -EINVAL;
+ }
+ break;
+ }
+ return 0;
+}
+
+
+static const struct v4l2_ctrl_ops s5p_mfc_dec_ctrl_ops = {
+ .s_ctrl = s5p_mfc_dec_s_ctrl,
+ .g_volatile_ctrl = s5p_mfc_dec_g_v_ctrl,
+};
+
+/* Get cropping information */
+static int vidioc_g_crop(struct file *file, void *priv,
+ struct v4l2_crop *cr)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ u32 left, right, top, bottom;
+
+ if (ctx->state != MFCINST_HEAD_PARSED &&
+ ctx->state != MFCINST_RUNNING && ctx->state != MFCINST_FINISHING
+ && ctx->state != MFCINST_FINISHED) {
+ mfc_err("Cannont set crop\n");
+ return -EINVAL;
+ }
+ if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_H264) {
+ left = s5p_mfc_read_shm(ctx, CROP_INFO_H);
+ right = left >> S5P_FIMV_SHARED_CROP_RIGHT_SHIFT;
+ left = left & S5P_FIMV_SHARED_CROP_LEFT_MASK;
+ top = s5p_mfc_read_shm(ctx, CROP_INFO_V);
+ bottom = top >> S5P_FIMV_SHARED_CROP_BOTTOM_SHIFT;
+ top = top & S5P_FIMV_SHARED_CROP_TOP_MASK;
+ cr->c.left = left;
+ cr->c.top = top;
+ cr->c.width = ctx->img_width - left - right;
+ cr->c.height = ctx->img_height - top - bottom;
+ mfc_debug(2, "Cropping info [h264]: l=%d t=%d "
+ "w=%d h=%d (r=%d b=%d fw=%d fh=%d\n", left, top,
+ cr->c.width, cr->c.height, right, bottom,
+ ctx->buf_width, ctx->buf_height);
+ } else {
+ cr->c.left = 0;
+ cr->c.top = 0;
+ cr->c.width = ctx->img_width;
+ cr->c.height = ctx->img_height;
+ mfc_debug(2, "Cropping info: w=%d h=%d fw=%d "
+ "fh=%d\n", cr->c.width, cr->c.height, ctx->buf_width,
+ ctx->buf_height);
+ }
+ return 0;
+}
+
+/* v4l2_ioctl_ops */
+static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane,
+ .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
+ .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt,
+ .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt,
+ .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt,
+ .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt,
+ .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_crop = vidioc_g_crop,
+};
+
+static int s5p_mfc_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt, unsigned int *buf_count,
+ unsigned int *plane_count, unsigned int psize[],
+ void *allocators[])
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
+
+ /* Video output for decoding (source)
+ * this can be set after getting an instance */
+ if (ctx->state == MFCINST_INIT &&
+ vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ /* A single plane is required for input */
+ *plane_count = 1;
+ if (*buf_count < 1)
+ *buf_count = 1;
+ if (*buf_count > MFC_MAX_BUFFERS)
+ *buf_count = MFC_MAX_BUFFERS;
+ /* Video capture for decoding (destination)
+ * this can be set after the header was parsed */
+ } else if (ctx->state == MFCINST_HEAD_PARSED &&
+ vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ /* Output plane count is 2 - one for Y and one for CbCr */
+ *plane_count = 2;
+ /* Setup buffer count */
+ if (*buf_count < ctx->dpb_count)
+ *buf_count = ctx->dpb_count;
+ if (*buf_count > ctx->dpb_count + MFC_MAX_EXTRA_DPB)
+ *buf_count = ctx->dpb_count + MFC_MAX_EXTRA_DPB;
+ if (*buf_count > MFC_MAX_BUFFERS)
+ *buf_count = MFC_MAX_BUFFERS;
+ } else {
+ mfc_err("State seems invalid. State = %d, vq->type = %d\n",
+ ctx->state, vq->type);
+ return -EINVAL;
+ }
+ mfc_debug(2, "Buffer count=%d, plane count=%d\n",
+ *buf_count, *plane_count);
+ if (ctx->state == MFCINST_HEAD_PARSED &&
+ vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ psize[0] = ctx->luma_size;
+ psize[1] = ctx->chroma_size;
+ allocators[0] = ctx->dev->alloc_ctx[MFC_BANK2_ALLOC_CTX];
+ allocators[1] = ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
+ } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
+ ctx->state == MFCINST_INIT) {
+ psize[0] = ctx->dec_src_buf_size;
+ allocators[0] = ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
+ } else {
+ mfc_err("This video node is dedicated to decoding. Decoding not initalised\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void s5p_mfc_unlock(struct vb2_queue *q)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ mutex_unlock(&dev->mfc_mutex);
+}
+
+static void s5p_mfc_lock(struct vb2_queue *q)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ mutex_lock(&dev->mfc_mutex);
+}
+
+static int s5p_mfc_buf_init(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
+ unsigned int i;
+
+ if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ if (ctx->capture_state == QUEUE_BUFS_MMAPED)
+ return 0;
+ for (i = 0; i <= ctx->src_fmt->num_planes ; i++) {
+ if (IS_ERR_OR_NULL(ERR_PTR(
+ vb2_dma_contig_plane_dma_addr(vb, i)))) {
+ mfc_err("Plane mem not allocated\n");
+ return -EINVAL;
+ }
+ }
+ if (vb2_plane_size(vb, 0) < ctx->luma_size ||
+ vb2_plane_size(vb, 1) < ctx->chroma_size) {
+ mfc_err("Plane buffer (CAPTURE) is too small\n");
+ return -EINVAL;
+ }
+ i = vb->v4l2_buf.index;
+ ctx->dst_bufs[i].b = vb;
+ ctx->dst_bufs[i].cookie.raw.luma =
+ vb2_dma_contig_plane_dma_addr(vb, 0);
+ ctx->dst_bufs[i].cookie.raw.chroma =
+ vb2_dma_contig_plane_dma_addr(vb, 1);
+ ctx->dst_bufs_cnt++;
+ } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ if (IS_ERR_OR_NULL(ERR_PTR(
+ vb2_dma_contig_plane_dma_addr(vb, 0)))) {
+ mfc_err("Plane memory not allocated\n");
+ return -EINVAL;
+ }
+ if (vb2_plane_size(vb, 0) < ctx->dec_src_buf_size) {
+ mfc_err("Plane buffer (OUTPUT) is too small\n");
+ return -EINVAL;
+ }
+
+ i = vb->v4l2_buf.index;
+ ctx->src_bufs[i].b = vb;
+ ctx->src_bufs[i].cookie.stream =
+ vb2_dma_contig_plane_dma_addr(vb, 0);
+ ctx->src_bufs_cnt++;
+ } else {
+ mfc_err("s5p_mfc_buf_init: unknown queue type\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long flags;
+
+ v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
+ if (ctx->state == MFCINST_FINISHING ||
+ ctx->state == MFCINST_FINISHED)
+ ctx->state = MFCINST_RUNNING;
+ /* If context is ready then dev = work->data;schedule it to run */
+ if (s5p_mfc_ctx_ready(ctx)) {
+ spin_lock_irqsave(&dev->condlock, flags);
+ set_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock_irqrestore(&dev->condlock, flags);
+ }
+ s5p_mfc_try_run(dev);
+ return 0;
+}
+
+static int s5p_mfc_stop_streaming(struct vb2_queue *q)
+{
+ unsigned long flags;
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+ int aborted = 0;
+
+ if ((ctx->state == MFCINST_FINISHING ||
+ ctx->state == MFCINST_RUNNING) &&
+ dev->curr_ctx == ctx->num && dev->hw_lock) {
+ ctx->state = MFCINST_ABORT;
+ s5p_mfc_wait_for_done_ctx(ctx,
+ S5P_FIMV_R2H_CMD_FRAME_DONE_RET, 0);
+ aborted = 1;
+ }
+ spin_lock_irqsave(&dev->irqlock, flags);
+ if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
+ INIT_LIST_HEAD(&ctx->dst_queue);
+ ctx->dst_queue_cnt = 0;
+ ctx->dpb_flush_flag = 1;
+ ctx->dec_dst_flag = 0;
+ }
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
+ INIT_LIST_HEAD(&ctx->src_queue);
+ ctx->src_queue_cnt = 0;
+ }
+ if (aborted)
+ ctx->state = MFCINST_RUNNING;
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ return 0;
+}
+
+
+static void s5p_mfc_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long flags;
+ struct s5p_mfc_buf *mfc_buf;
+
+ if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ mfc_buf = &ctx->src_bufs[vb->v4l2_buf.index];
+ mfc_buf->used = 0;
+ spin_lock_irqsave(&dev->irqlock, flags);
+ list_add_tail(&mfc_buf->list, &ctx->src_queue);
+ ctx->src_queue_cnt++;
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ } else if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ mfc_buf = &ctx->dst_bufs[vb->v4l2_buf.index];
+ mfc_buf->used = 0;
+ /* Mark destination as available for use by MFC */
+ spin_lock_irqsave(&dev->irqlock, flags);
+ set_bit(vb->v4l2_buf.index, &ctx->dec_dst_flag);
+ list_add_tail(&mfc_buf->list, &ctx->dst_queue);
+ ctx->dst_queue_cnt++;
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ } else {
+ mfc_err("Unsupported buffer type (%d)\n", vq->type);
+ }
+ if (s5p_mfc_ctx_ready(ctx)) {
+ spin_lock_irqsave(&dev->condlock, flags);
+ set_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock_irqrestore(&dev->condlock, flags);
+ }
+ s5p_mfc_try_run(dev);
+}
+
+static struct vb2_ops s5p_mfc_dec_qops = {
+ .queue_setup = s5p_mfc_queue_setup,
+ .wait_prepare = s5p_mfc_unlock,
+ .wait_finish = s5p_mfc_lock,
+ .buf_init = s5p_mfc_buf_init,
+ .start_streaming = s5p_mfc_start_streaming,
+ .stop_streaming = s5p_mfc_stop_streaming,
+ .buf_queue = s5p_mfc_buf_queue,
+};
+
+struct s5p_mfc_codec_ops *get_dec_codec_ops(void)
+{
+ return &decoder_codec_ops;
+}
+
+struct vb2_ops *get_dec_queue_ops(void)
+{
+ return &s5p_mfc_dec_qops;
+}
+
+const struct v4l2_ioctl_ops *get_dec_v4l2_ioctl_ops(void)
+{
+ return &s5p_mfc_dec_ioctl_ops;
+}
+
+#define IS_MFC51_PRIV(x) ((V4L2_CTRL_ID2CLASS(x) == V4L2_CTRL_CLASS_MPEG) \
+ && V4L2_CTRL_DRIVER_PRIV(x))
+
+int s5p_mfc_dec_ctrls_setup(struct s5p_mfc_ctx *ctx)
+{
+ struct v4l2_ctrl_config cfg;
+ int i;
+
+ v4l2_ctrl_handler_init(&ctx->ctrl_handler, NUM_CTRLS);
+ if (ctx->ctrl_handler.error) {
+ mfc_err("v4l2_ctrl_handler_init failed\n");
+ return ctx->ctrl_handler.error;
+ }
+
+ for (i = 0; i < NUM_CTRLS; i++) {
+ if (IS_MFC51_PRIV(controls[i].id)) {
+ cfg.ops = &s5p_mfc_dec_ctrl_ops;
+ cfg.id = controls[i].id;
+ cfg.min = controls[i].minimum;
+ cfg.max = controls[i].maximum;
+ cfg.def = controls[i].default_value;
+ cfg.name = controls[i].name;
+ cfg.type = controls[i].type;
+
+ cfg.step = controls[i].step;
+ cfg.menu_skip_mask = 0;
+
+ ctx->ctrls[i] = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
+ &cfg, NULL);
+ } else {
+ ctx->ctrls[i] = v4l2_ctrl_new_std(&ctx->ctrl_handler,
+ &s5p_mfc_dec_ctrl_ops,
+ controls[i].id, controls[i].minimum,
+ controls[i].maximum, controls[i].step,
+ controls[i].default_value);
+ }
+ if (ctx->ctrl_handler.error) {
+ mfc_err("Adding control (%d) failed\n", i);
+ return ctx->ctrl_handler.error;
+ }
+ if (controls[i].is_volatile && ctx->ctrls[i])
+ ctx->ctrls[i]->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ }
+ return 0;
+}
+
+void s5p_mfc_dec_ctrls_delete(struct s5p_mfc_ctx *ctx)
+{
+ int i;
+
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ for (i = 0; i < NUM_CTRLS; i++)
+ ctx->ctrls[i] = NULL;
+}
+
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_dec.h b/drivers/media/video/s5p-mfc/s5p_mfc_dec.h
new file mode 100644
index 00000000..fb8b215d
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_dec.h
@@ -0,0 +1,23 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_dec.h
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef S5P_MFC_DEC_H_
+#define S5P_MFC_DEC_H_
+
+struct s5p_mfc_codec_ops *get_dec_codec_ops(void);
+struct vb2_ops *get_dec_queue_ops(void);
+const struct v4l2_ioctl_ops *get_dec_v4l2_ioctl_ops(void);
+struct s5p_mfc_fmt *get_dec_def_fmt(bool src);
+int s5p_mfc_dec_ctrls_setup(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_dec_ctrls_delete(struct s5p_mfc_ctx *ctx);
+
+#endif /* S5P_MFC_DEC_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c
new file mode 100644
index 00000000..dff9dc79
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c
@@ -0,0 +1,1830 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_enc.c
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Jeongtae Park <jtp.park@samsung.com>
+ * Kamil Debski <k.debski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/version.h>
+#include <linux/videodev2.h>
+#include <linux/workqueue.h>
+#include <media/v4l2-ctrls.h>
+#include <media/videobuf2-core.h>
+#include "regs-mfc.h"
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_debug.h"
+#include "s5p_mfc_enc.h"
+#include "s5p_mfc_intr.h"
+#include "s5p_mfc_opr.h"
+
+static struct s5p_mfc_fmt formats[] = {
+ {
+ .name = "4:2:0 2 Planes 64x32 Tiles",
+ .fourcc = V4L2_PIX_FMT_NV12MT,
+ .codec_mode = S5P_FIMV_CODEC_NONE,
+ .type = MFC_FMT_RAW,
+ .num_planes = 2,
+ },
+ {
+ .name = "4:2:0 2 Planes",
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .codec_mode = S5P_FIMV_CODEC_NONE,
+ .type = MFC_FMT_RAW,
+ .num_planes = 2,
+ },
+ {
+ .name = "H264 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_H264,
+ .codec_mode = S5P_FIMV_CODEC_H264_ENC,
+ .type = MFC_FMT_ENC,
+ .num_planes = 1,
+ },
+ {
+ .name = "MPEG4 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_MPEG4,
+ .codec_mode = S5P_FIMV_CODEC_MPEG4_ENC,
+ .type = MFC_FMT_ENC,
+ .num_planes = 1,
+ },
+ {
+ .name = "H263 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_H263,
+ .codec_mode = S5P_FIMV_CODEC_H263_ENC,
+ .type = MFC_FMT_ENC,
+ .num_planes = 1,
+ },
+};
+
+#define NUM_FORMATS ARRAY_SIZE(formats)
+static struct s5p_mfc_fmt *find_format(struct v4l2_format *f, unsigned int t)
+{
+ unsigned int i;
+
+ for (i = 0; i < NUM_FORMATS; i++) {
+ if (formats[i].fourcc == f->fmt.pix_mp.pixelformat &&
+ formats[i].type == t)
+ return &formats[i];
+ }
+ return NULL;
+}
+
+static struct mfc_control controls[] = {
+ {
+ .id = V4L2_CID_MPEG_VIDEO_GOP_SIZE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE,
+ .maximum = V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES,
+ .default_value = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1900,
+ .maximum = (1 << 30) - 1,
+ .step = 1,
+ .default_value = 1900,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_PADDING,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Padding Control Enable",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_PADDING_YUV,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Padding Color YUV Value",
+ .minimum = 0,
+ .maximum = (1 << 25) - 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_BITRATE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = (1 << 30) - 1,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Rate Control Reaction Coeff.",
+ .minimum = 1,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Force frame type",
+ .minimum = V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_DISABLED,
+ .maximum = V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_NOT_CODED,
+ .default_value = V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_DISABLED,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VBV_SIZE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEADER_MODE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE,
+ .maximum = V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
+ .default_value = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Frame Skip Enable",
+ .minimum = V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_DISABLED,
+ .maximum = V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT,
+ .menu_skip_mask = 0,
+ .default_value = V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_DISABLED,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Fixed Target Bit Enable",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 0,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_B_FRAMES,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 2,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
+ .maximum = V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH,
+ .default_value = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
+ .menu_skip_mask = ~(
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) |
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) |
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH)
+ ),
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
+ .maximum = V4L2_MPEG_VIDEO_H264_LEVEL_4_0,
+ .default_value = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
+ .menu_skip_mask = ~(
+ (1 << V4L2_MPEG_VIDEO_H264_LEVEL_4_1) |
+ (1 << V4L2_MPEG_VIDEO_H264_LEVEL_4_2) |
+ (1 << V4L2_MPEG_VIDEO_H264_LEVEL_5_0) |
+ (1 << V4L2_MPEG_VIDEO_H264_LEVEL_5_1)
+ ),
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_MPEG4_LEVEL_0,
+ .maximum = V4L2_MPEG_VIDEO_MPEG4_LEVEL_5,
+ .default_value = V4L2_MPEG_VIDEO_MPEG4_LEVEL_0,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED,
+ .maximum = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY,
+ .default_value = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = -6,
+ .maximum = 6,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = -6,
+ .maximum = 6,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC,
+ .maximum = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC,
+ .default_value = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_H264_NUM_REF_PIC_FOR_P,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "The Number of Ref. Pic for P",
+ .minimum = 1,
+ .maximum = 2,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_MIN_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_MAX_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "H263 I-Frame QP value",
+ .minimum = 1,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H263_MIN_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "H263 Minimum QP value",
+ .minimum = 1,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H263_MAX_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "H263 Maximum QP value",
+ .minimum = 1,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "H263 P frame QP value",
+ .minimum = 1,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "H263 B frame QP value",
+ .minimum = 1,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "MPEG4 I-Frame QP value",
+ .minimum = 1,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "MPEG4 Minimum QP value",
+ .minimum = 1,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "MPEG4 Maximum QP value",
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "MPEG4 P frame QP value",
+ .minimum = 1,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "MPEG4 B frame QP value",
+ .minimum = 1,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_DARK,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "H264 Dark Reg Adaptive RC",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_SMOOTH,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "H264 Smooth Reg Adaptive RC",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_STATIC,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "H264 Static Reg Adaptive RC",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_ACTIVITY,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "H264 Activity Reg Adaptive RC",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED,
+ .maximum = V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED,
+ .default_value = 0,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_GOP_CLOSURE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_I_PERIOD,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE,
+ .maximum = V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE,
+ .default_value = 0,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MPEG4_QPEL,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+};
+
+#define NUM_CTRLS ARRAY_SIZE(controls)
+static const char * const *mfc51_get_menu(u32 id)
+{
+ static const char * const mfc51_video_frame_skip[] = {
+ "Disabled",
+ "Level Limit",
+ "VBV/CPB Limit",
+ NULL,
+ };
+ static const char * const mfc51_video_force_frame[] = {
+ "Disabled",
+ "I Frame",
+ "Not Coded",
+ NULL,
+ };
+ switch (id) {
+ case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE:
+ return mfc51_video_frame_skip;
+ case V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE:
+ return mfc51_video_force_frame;
+ }
+ return NULL;
+}
+
+static int s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx)
+{
+ mfc_debug(2, "src=%d, dst=%d, state=%d\n",
+ ctx->src_queue_cnt, ctx->dst_queue_cnt, ctx->state);
+ /* context is ready to make header */
+ if (ctx->state == MFCINST_GOT_INST && ctx->dst_queue_cnt >= 1)
+ return 1;
+ /* context is ready to encode a frame */
+ if (ctx->state == MFCINST_RUNNING &&
+ ctx->src_queue_cnt >= 1 && ctx->dst_queue_cnt >= 1)
+ return 1;
+ /* context is ready to encode remain frames */
+ if (ctx->state == MFCINST_FINISHING &&
+ ctx->src_queue_cnt >= 1 && ctx->dst_queue_cnt >= 1)
+ return 1;
+ mfc_debug(2, "ctx is not ready\n");
+ return 0;
+}
+
+static void cleanup_ref_queue(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_buf *mb_entry;
+ unsigned long mb_y_addr, mb_c_addr;
+
+ /* move buffers in ref queue to src queue */
+ while (!list_empty(&ctx->ref_queue)) {
+ mb_entry = list_entry((&ctx->ref_queue)->next,
+ struct s5p_mfc_buf, list);
+ mb_y_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 0);
+ mb_c_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 1);
+ list_del(&mb_entry->list);
+ ctx->ref_queue_cnt--;
+ list_add_tail(&mb_entry->list, &ctx->src_queue);
+ ctx->src_queue_cnt++;
+ }
+ mfc_debug(2, "enc src count: %d, enc ref count: %d\n",
+ ctx->src_queue_cnt, ctx->ref_queue_cnt);
+ INIT_LIST_HEAD(&ctx->ref_queue);
+ ctx->ref_queue_cnt = 0;
+}
+
+static int enc_pre_seq_start(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_buf *dst_mb;
+ unsigned long dst_addr;
+ unsigned int dst_size;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->irqlock, flags);
+ dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
+ dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0);
+ dst_size = vb2_plane_size(dst_mb->b, 0);
+ s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ return 0;
+}
+
+static int enc_post_seq_start(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_enc_params *p = &ctx->enc_params;
+ struct s5p_mfc_buf *dst_mb;
+ unsigned long flags;
+
+ if (p->seq_hdr_mode == V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) {
+ spin_lock_irqsave(&dev->irqlock, flags);
+ dst_mb = list_entry(ctx->dst_queue.next,
+ struct s5p_mfc_buf, list);
+ list_del(&dst_mb->list);
+ ctx->dst_queue_cnt--;
+ vb2_set_plane_payload(dst_mb->b, 0,
+ s5p_mfc_get_enc_strm_size());
+ vb2_buffer_done(dst_mb->b, VB2_BUF_STATE_DONE);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ }
+ ctx->state = MFCINST_RUNNING;
+ if (s5p_mfc_ctx_ready(ctx)) {
+ spin_lock_irqsave(&dev->condlock, flags);
+ set_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock_irqrestore(&dev->condlock, flags);
+ }
+ s5p_mfc_try_run(dev);
+ return 0;
+}
+
+static int enc_pre_frame_start(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_buf *dst_mb;
+ struct s5p_mfc_buf *src_mb;
+ unsigned long flags;
+ unsigned long src_y_addr, src_c_addr, dst_addr;
+ unsigned int dst_size;
+
+ spin_lock_irqsave(&dev->irqlock, flags);
+ src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
+ src_y_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 0);
+ src_c_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 1);
+ s5p_mfc_set_enc_frame_buffer(ctx, src_y_addr, src_c_addr);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+
+ spin_lock_irqsave(&dev->irqlock, flags);
+ dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
+ dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0);
+ dst_size = vb2_plane_size(dst_mb->b, 0);
+ s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+
+ return 0;
+}
+
+static int enc_post_frame_start(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_buf *mb_entry;
+ unsigned long enc_y_addr, enc_c_addr;
+ unsigned long mb_y_addr, mb_c_addr;
+ int slice_type;
+ unsigned int strm_size;
+ unsigned long flags;
+
+ slice_type = s5p_mfc_get_enc_slice_type();
+ strm_size = s5p_mfc_get_enc_strm_size();
+ mfc_debug(2, "Encoded slice type: %d", slice_type);
+ mfc_debug(2, "Encoded stream size: %d", strm_size);
+ mfc_debug(2, "Display order: %d",
+ mfc_read(dev, S5P_FIMV_ENC_SI_PIC_CNT));
+ spin_lock_irqsave(&dev->irqlock, flags);
+ if (slice_type >= 0) {
+ s5p_mfc_get_enc_frame_buffer(ctx, &enc_y_addr, &enc_c_addr);
+ list_for_each_entry(mb_entry, &ctx->src_queue, list) {
+ mb_y_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 0);
+ mb_c_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 1);
+ if ((enc_y_addr == mb_y_addr) &&
+ (enc_c_addr == mb_c_addr)) {
+ list_del(&mb_entry->list);
+ ctx->src_queue_cnt--;
+ vb2_buffer_done(mb_entry->b,
+ VB2_BUF_STATE_DONE);
+ break;
+ }
+ }
+ list_for_each_entry(mb_entry, &ctx->ref_queue, list) {
+ mb_y_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 0);
+ mb_c_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 1);
+ if ((enc_y_addr == mb_y_addr) &&
+ (enc_c_addr == mb_c_addr)) {
+ list_del(&mb_entry->list);
+ ctx->ref_queue_cnt--;
+ vb2_buffer_done(mb_entry->b,
+ VB2_BUF_STATE_DONE);
+ break;
+ }
+ }
+ }
+ if ((ctx->src_queue_cnt > 0) && (ctx->state == MFCINST_RUNNING)) {
+ mb_entry = list_entry(ctx->src_queue.next, struct s5p_mfc_buf,
+ list);
+ if (mb_entry->used) {
+ list_del(&mb_entry->list);
+ ctx->src_queue_cnt--;
+ list_add_tail(&mb_entry->list, &ctx->ref_queue);
+ ctx->ref_queue_cnt++;
+ }
+ mfc_debug(2, "enc src count: %d, enc ref count: %d\n",
+ ctx->src_queue_cnt, ctx->ref_queue_cnt);
+ }
+ if (strm_size > 0) {
+ /* at least one more dest. buffers exist always */
+ mb_entry = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf,
+ list);
+ list_del(&mb_entry->list);
+ ctx->dst_queue_cnt--;
+ switch (slice_type) {
+ case S5P_FIMV_ENC_SI_SLICE_TYPE_I:
+ mb_entry->b->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
+ break;
+ case S5P_FIMV_ENC_SI_SLICE_TYPE_P:
+ mb_entry->b->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME;
+ break;
+ case S5P_FIMV_ENC_SI_SLICE_TYPE_B:
+ mb_entry->b->v4l2_buf.flags |= V4L2_BUF_FLAG_BFRAME;
+ break;
+ }
+ vb2_set_plane_payload(mb_entry->b, 0, strm_size);
+ vb2_buffer_done(mb_entry->b, VB2_BUF_STATE_DONE);
+ }
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ if ((ctx->src_queue_cnt == 0) || (ctx->dst_queue_cnt == 0)) {
+ spin_lock(&dev->condlock);
+ clear_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock(&dev->condlock);
+ }
+ return 0;
+}
+
+static struct s5p_mfc_codec_ops encoder_codec_ops = {
+ .pre_seq_start = enc_pre_seq_start,
+ .post_seq_start = enc_post_seq_start,
+ .pre_frame_start = enc_pre_frame_start,
+ .post_frame_start = enc_post_frame_start,
+};
+
+/* Query capabilities of the device */
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct s5p_mfc_dev *dev = video_drvdata(file);
+
+ strncpy(cap->driver, dev->plat_dev->name, sizeof(cap->driver) - 1);
+ strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1);
+ cap->bus_info[0] = 0;
+ cap->version = KERNEL_VERSION(1, 0, 0);
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE_MPLANE
+ | V4L2_CAP_VIDEO_OUTPUT_MPLANE
+ | V4L2_CAP_STREAMING;
+ return 0;
+}
+
+static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)
+{
+ struct s5p_mfc_fmt *fmt;
+ int i, j = 0;
+
+ for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+ if (mplane && formats[i].num_planes == 1)
+ continue;
+ else if (!mplane && formats[i].num_planes > 1)
+ continue;
+ if (out && formats[i].type != MFC_FMT_RAW)
+ continue;
+ else if (!out && formats[i].type != MFC_FMT_ENC)
+ continue;
+ if (j == f->index) {
+ fmt = &formats[i];
+ strlcpy(f->description, fmt->name,
+ sizeof(f->description));
+ f->pixelformat = fmt->fourcc;
+ return 0;
+ }
+ ++j;
+ }
+ return -EINVAL;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv,
+ struct v4l2_fmtdesc *f)
+{
+ return vidioc_enum_fmt(f, false, false);
+}
+
+static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,
+ struct v4l2_fmtdesc *f)
+{
+ return vidioc_enum_fmt(f, true, false);
+}
+
+static int vidioc_enum_fmt_vid_out(struct file *file, void *prov,
+ struct v4l2_fmtdesc *f)
+{
+ return vidioc_enum_fmt(f, false, true);
+}
+
+static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov,
+ struct v4l2_fmtdesc *f)
+{
+ return vidioc_enum_fmt(f, true, true);
+}
+
+static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
+
+ mfc_debug(2, "f->type = %d ctx->state = %d\n", f->type, ctx->state);
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ /* This is run on output (encoder dest) */
+ pix_fmt_mp->width = 0;
+ pix_fmt_mp->height = 0;
+ pix_fmt_mp->field = V4L2_FIELD_NONE;
+ pix_fmt_mp->pixelformat = ctx->dst_fmt->fourcc;
+ pix_fmt_mp->num_planes = ctx->dst_fmt->num_planes;
+
+ pix_fmt_mp->plane_fmt[0].bytesperline = ctx->enc_dst_buf_size;
+ pix_fmt_mp->plane_fmt[0].sizeimage = ctx->enc_dst_buf_size;
+ } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ /* This is run on capture (encoder src) */
+ pix_fmt_mp->width = ctx->img_width;
+ pix_fmt_mp->height = ctx->img_height;
+
+ pix_fmt_mp->field = V4L2_FIELD_NONE;
+ pix_fmt_mp->pixelformat = ctx->src_fmt->fourcc;
+ pix_fmt_mp->num_planes = ctx->src_fmt->num_planes;
+
+ pix_fmt_mp->plane_fmt[0].bytesperline = ctx->buf_width;
+ pix_fmt_mp->plane_fmt[0].sizeimage = ctx->luma_size;
+ pix_fmt_mp->plane_fmt[1].bytesperline = ctx->buf_width;
+ pix_fmt_mp->plane_fmt[1].sizeimage = ctx->chroma_size;
+ } else {
+ mfc_err("invalid buf type\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct s5p_mfc_fmt *fmt;
+ struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ fmt = find_format(f, MFC_FMT_ENC);
+ if (!fmt) {
+ mfc_err("failed to try output format\n");
+ return -EINVAL;
+ }
+
+ if (pix_fmt_mp->plane_fmt[0].sizeimage == 0) {
+ mfc_err("must be set encoding output size\n");
+ return -EINVAL;
+ }
+
+ pix_fmt_mp->plane_fmt[0].bytesperline =
+ pix_fmt_mp->plane_fmt[0].sizeimage;
+ } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ fmt = find_format(f, MFC_FMT_RAW);
+ if (!fmt) {
+ mfc_err("failed to try output format\n");
+ return -EINVAL;
+ }
+
+ if (fmt->num_planes != pix_fmt_mp->num_planes) {
+ mfc_err("failed to try output format\n");
+ return -EINVAL;
+ }
+ } else {
+ mfc_err("invalid buf type\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct s5p_mfc_dev *dev = video_drvdata(file);
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ struct s5p_mfc_fmt *fmt;
+ struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
+ unsigned long flags;
+ int ret = 0;
+
+ ret = vidioc_try_fmt(file, priv, f);
+ if (ret)
+ return ret;
+ if (ctx->vq_src.streaming || ctx->vq_dst.streaming) {
+ v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__);
+ ret = -EBUSY;
+ goto out;
+ }
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ fmt = find_format(f, MFC_FMT_ENC);
+ if (!fmt) {
+ mfc_err("failed to set capture format\n");
+ return -EINVAL;
+ }
+ ctx->state = MFCINST_INIT;
+ ctx->dst_fmt = fmt;
+ ctx->codec_mode = ctx->dst_fmt->codec_mode;
+ ctx->enc_dst_buf_size = pix_fmt_mp->plane_fmt[0].sizeimage;
+ pix_fmt_mp->plane_fmt[0].bytesperline = 0;
+ ctx->dst_bufs_cnt = 0;
+ ctx->capture_state = QUEUE_FREE;
+ s5p_mfc_alloc_instance_buffer(ctx);
+ spin_lock_irqsave(&dev->condlock, flags);
+ set_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock_irqrestore(&dev->condlock, flags);
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_try_run(dev);
+ if (s5p_mfc_wait_for_done_ctx(ctx, \
+ S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET, 1)) {
+ /* Error or timeout */
+ mfc_err("Error getting instance from hardware\n");
+ s5p_mfc_release_instance_buffer(ctx);
+ ret = -EIO;
+ goto out;
+ }
+ mfc_debug(2, "Got instance number: %d\n", ctx->inst_no);
+ } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ fmt = find_format(f, MFC_FMT_RAW);
+ if (!fmt) {
+ mfc_err("failed to set output format\n");
+ return -EINVAL;
+ }
+ if (fmt->num_planes != pix_fmt_mp->num_planes) {
+ mfc_err("failed to set output format\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ ctx->src_fmt = fmt;
+ ctx->img_width = pix_fmt_mp->width;
+ ctx->img_height = pix_fmt_mp->height;
+ mfc_debug(2, "codec number: %d\n", ctx->src_fmt->codec_mode);
+ mfc_debug(2, "fmt - w: %d, h: %d, ctx - w: %d, h: %d\n",
+ pix_fmt_mp->width, pix_fmt_mp->height,
+ ctx->img_width, ctx->img_height);
+ if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M) {
+ ctx->buf_width = ALIGN(ctx->img_width,
+ S5P_FIMV_NV12M_HALIGN);
+ ctx->luma_size = ALIGN(ctx->img_width,
+ S5P_FIMV_NV12M_HALIGN) * ALIGN(ctx->img_height,
+ S5P_FIMV_NV12M_LVALIGN);
+ ctx->chroma_size = ALIGN(ctx->img_width,
+ S5P_FIMV_NV12M_HALIGN) * ALIGN((ctx->img_height
+ >> 1), S5P_FIMV_NV12M_CVALIGN);
+
+ ctx->luma_size = ALIGN(ctx->luma_size,
+ S5P_FIMV_NV12M_SALIGN);
+ ctx->chroma_size = ALIGN(ctx->chroma_size,
+ S5P_FIMV_NV12M_SALIGN);
+
+ pix_fmt_mp->plane_fmt[0].sizeimage = ctx->luma_size;
+ pix_fmt_mp->plane_fmt[0].bytesperline = ctx->buf_width;
+ pix_fmt_mp->plane_fmt[1].sizeimage = ctx->chroma_size;
+ pix_fmt_mp->plane_fmt[1].bytesperline = ctx->buf_width;
+
+ } else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12MT) {
+ ctx->buf_width = ALIGN(ctx->img_width,
+ S5P_FIMV_NV12MT_HALIGN);
+ ctx->luma_size = ALIGN(ctx->img_width,
+ S5P_FIMV_NV12MT_HALIGN) * ALIGN(ctx->img_height,
+ S5P_FIMV_NV12MT_VALIGN);
+ ctx->chroma_size = ALIGN(ctx->img_width,
+ S5P_FIMV_NV12MT_HALIGN) * ALIGN((ctx->img_height
+ >> 1), S5P_FIMV_NV12MT_VALIGN);
+ ctx->luma_size = ALIGN(ctx->luma_size,
+ S5P_FIMV_NV12MT_SALIGN);
+ ctx->chroma_size = ALIGN(ctx->chroma_size,
+ S5P_FIMV_NV12MT_SALIGN);
+
+ pix_fmt_mp->plane_fmt[0].sizeimage = ctx->luma_size;
+ pix_fmt_mp->plane_fmt[0].bytesperline = ctx->buf_width;
+ pix_fmt_mp->plane_fmt[1].sizeimage = ctx->chroma_size;
+ pix_fmt_mp->plane_fmt[1].bytesperline = ctx->buf_width;
+ }
+ ctx->src_bufs_cnt = 0;
+ ctx->output_state = QUEUE_FREE;
+ } else {
+ mfc_err("invalid buf type\n");
+ return -EINVAL;
+ }
+out:
+ mfc_debug_leave();
+ return ret;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ int ret = 0;
+
+ /* if memory is not mmp or userptr return error */
+ if ((reqbufs->memory != V4L2_MEMORY_MMAP) &&
+ (reqbufs->memory != V4L2_MEMORY_USERPTR))
+ return -EINVAL;
+ if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ if (ctx->capture_state != QUEUE_FREE) {
+ mfc_err("invalid capture state: %d\n",
+ ctx->capture_state);
+ return -EINVAL;
+ }
+ ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ if (ret != 0) {
+ mfc_err("error in vb2_reqbufs() for E(D)\n");
+ return ret;
+ }
+ ctx->capture_state = QUEUE_BUFS_REQUESTED;
+ ret = s5p_mfc_alloc_codec_buffers(ctx);
+ if (ret) {
+ mfc_err("Failed to allocate encoding buffers\n");
+ reqbufs->count = 0;
+ ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ return -ENOMEM;
+ }
+ } else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ if (ctx->output_state != QUEUE_FREE) {
+ mfc_err("invalid output state: %d\n",
+ ctx->output_state);
+ return -EINVAL;
+ }
+ ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
+ if (ret != 0) {
+ mfc_err("error in vb2_reqbufs() for E(S)\n");
+ return ret;
+ }
+ ctx->output_state = QUEUE_BUFS_REQUESTED;
+ } else {
+ mfc_err("invalid buf type\n");
+ return -EINVAL;
+ }
+ return ret;
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ int ret = 0;
+
+ /* if memory is not mmp or userptr return error */
+ if ((buf->memory != V4L2_MEMORY_MMAP) &&
+ (buf->memory != V4L2_MEMORY_USERPTR))
+ return -EINVAL;
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ if (ctx->state != MFCINST_GOT_INST) {
+ mfc_err("invalid context state: %d\n", ctx->state);
+ return -EINVAL;
+ }
+ ret = vb2_querybuf(&ctx->vq_dst, buf);
+ if (ret != 0) {
+ mfc_err("error in vb2_querybuf() for E(D)\n");
+ return ret;
+ }
+ buf->m.planes[0].m.mem_offset += DST_QUEUE_OFF_BASE;
+ } else if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ret = vb2_querybuf(&ctx->vq_src, buf);
+ if (ret != 0) {
+ mfc_err("error in vb2_querybuf() for E(S)\n");
+ return ret;
+ }
+ } else {
+ mfc_err("invalid buf type\n");
+ return -EINVAL;
+ }
+ return ret;
+}
+
+/* Queue a buffer */
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (ctx->state == MFCINST_ERROR) {
+ mfc_err("Call on QBUF after unrecoverable error\n");
+ return -EIO;
+ }
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return vb2_qbuf(&ctx->vq_src, buf);
+ else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return vb2_qbuf(&ctx->vq_dst, buf);
+ return -EINVAL;
+}
+
+/* Dequeue a buffer */
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (ctx->state == MFCINST_ERROR) {
+ mfc_err("Call on DQBUF after unrecoverable error\n");
+ return -EIO;
+ }
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK);
+ else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK);
+ return -EINVAL;
+}
+
+/* Stream on */
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return vb2_streamon(&ctx->vq_src, type);
+ else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return vb2_streamon(&ctx->vq_dst, type);
+ return -EINVAL;
+}
+
+/* Stream off, which equals to a pause */
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return vb2_streamoff(&ctx->vq_src, type);
+ else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return vb2_streamoff(&ctx->vq_dst, type);
+ return -EINVAL;
+}
+
+static inline int h264_level(enum v4l2_mpeg_video_h264_level lvl)
+{
+ static unsigned int t[V4L2_MPEG_VIDEO_H264_LEVEL_4_0 + 1] = {
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_1_0 */ 10,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_1B */ 9,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_1_1 */ 11,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_1_2 */ 12,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_1_3 */ 13,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_2_0 */ 20,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_2_1 */ 21,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_2_2 */ 22,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_3_0 */ 30,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_3_1 */ 31,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_3_2 */ 32,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_4_0 */ 40,
+ };
+ return t[lvl];
+}
+
+static inline int mpeg4_level(enum v4l2_mpeg_video_mpeg4_level lvl)
+{
+ static unsigned int t[V4L2_MPEG_VIDEO_MPEG4_LEVEL_5 + 1] = {
+ /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_0 */ 0,
+ /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_0B */ 9,
+ /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_1 */ 1,
+ /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_2 */ 2,
+ /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_3 */ 3,
+ /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_3B */ 7,
+ /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_4 */ 4,
+ /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_5 */ 5,
+ };
+ return t[lvl];
+}
+
+static inline int vui_sar_idc(enum v4l2_mpeg_video_h264_vui_sar_idc sar)
+{
+ static unsigned int t[V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED + 1] = {
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED */ 0,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1 */ 1,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_12x11 */ 2,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_10x11 */ 3,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_16x11 */ 4,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_40x33 */ 5,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_24x11 */ 6,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_20x11 */ 7,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_32x11 */ 8,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_80x33 */ 9,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_18x11 */ 10,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_15x11 */ 11,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_64x33 */ 12,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_160x99 */ 13,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_4x3 */ 14,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_3x2 */ 15,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_2x1 */ 16,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED */ 255,
+ };
+ return t[sar];
+}
+
+static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct s5p_mfc_ctx *ctx = ctrl_to_ctx(ctrl);
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_enc_params *p = &ctx->enc_params;
+ int ret = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ p->gop_size = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
+ p->slice_mode = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:
+ p->slice_mb = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES:
+ p->slice_bit = ctrl->val * 8;
+ break;
+ case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB:
+ p->intra_refresh_mb = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_PADDING:
+ p->pad = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_PADDING_YUV:
+ p->pad_luma = (ctrl->val >> 16) & 0xff;
+ p->pad_cb = (ctrl->val >> 8) & 0xff;
+ p->pad_cr = (ctrl->val >> 0) & 0xff;
+ break;
+ case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:
+ p->rc_frame = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ p->rc_bitrate = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF:
+ p->rc_reaction_coeff = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE:
+ ctx->force_frame_type = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VBV_SIZE:
+ p->vbv_size = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE:
+ p->codec.h264.cpb_size = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
+ p->seq_hdr_mode = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE:
+ p->frame_skip_mode = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT:
+ p->fixed_target_bit = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ p->num_b_frame = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+ switch (ctrl->val) {
+ case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN:
+ p->codec.h264.profile =
+ S5P_FIMV_ENC_PROFILE_H264_MAIN;
+ break;
+ case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH:
+ p->codec.h264.profile =
+ S5P_FIMV_ENC_PROFILE_H264_HIGH;
+ break;
+ case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE:
+ p->codec.h264.profile =
+ S5P_FIMV_ENC_PROFILE_H264_BASELINE;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+ p->codec.h264.level_v4l2 = ctrl->val;
+ p->codec.h264.level = h264_level(ctrl->val);
+ if (p->codec.h264.level < 0) {
+ mfc_err("Level number is wrong\n");
+ ret = p->codec.h264.level;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
+ p->codec.mpeg4.level_v4l2 = ctrl->val;
+ p->codec.mpeg4.level = mpeg4_level(ctrl->val);
+ if (p->codec.mpeg4.level < 0) {
+ mfc_err("Level number is wrong\n");
+ ret = p->codec.mpeg4.level;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
+ p->codec.h264.loop_filter_mode = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA:
+ p->codec.h264.loop_filter_alpha = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA:
+ p->codec.h264.loop_filter_beta = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
+ p->codec.h264.entropy_mode = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_H264_NUM_REF_PIC_FOR_P:
+ p->codec.h264.num_ref_pic_4p = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM:
+ p->codec.h264._8x8_transform = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE:
+ p->codec.h264.rc_mb = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
+ p->codec.h264.rc_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_MIN_QP:
+ p->codec.h264.rc_min_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:
+ p->codec.h264.rc_max_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:
+ p->codec.h264.rc_p_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP:
+ p->codec.h264.rc_b_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP:
+ case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP:
+ p->codec.mpeg4.rc_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP:
+ case V4L2_CID_MPEG_VIDEO_H263_MIN_QP:
+ p->codec.mpeg4.rc_min_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP:
+ case V4L2_CID_MPEG_VIDEO_H263_MAX_QP:
+ p->codec.mpeg4.rc_max_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP:
+ case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP:
+ p->codec.mpeg4.rc_p_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP:
+ case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP:
+ p->codec.mpeg4.rc_b_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_DARK:
+ p->codec.h264.rc_mb_dark = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_SMOOTH:
+ p->codec.h264.rc_mb_smooth = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_STATIC:
+ p->codec.h264.rc_mb_static = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_ACTIVITY:
+ p->codec.h264.rc_mb_activity = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE:
+ p->codec.h264.vui_sar = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:
+ p->codec.h264.vui_sar_idc = vui_sar_idc(ctrl->val);
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH:
+ p->codec.h264.vui_ext_sar_width = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT:
+ p->codec.h264.vui_ext_sar_height = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+ p->codec.h264.open_gop = !ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD:
+ p->codec.h264.open_gop_size = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
+ switch (ctrl->val) {
+ case V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE:
+ p->codec.mpeg4.profile =
+ S5P_FIMV_ENC_PROFILE_MPEG4_SIMPLE;
+ break;
+ case V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE:
+ p->codec.mpeg4.profile =
+ S5P_FIMV_ENC_PROFILE_MPEG4_ADVANCED_SIMPLE;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL:
+ p->codec.mpeg4.quarter_pixel = ctrl->val;
+ break;
+ default:
+ v4l2_err(&dev->v4l2_dev, "Invalid control, id=%d, val=%d\n",
+ ctrl->id, ctrl->val);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops s5p_mfc_enc_ctrl_ops = {
+ .s_ctrl = s5p_mfc_enc_s_ctrl,
+};
+
+int vidioc_s_parm(struct file *file, void *priv, struct v4l2_streamparm *a)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (a->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ctx->enc_params.rc_framerate_num =
+ a->parm.output.timeperframe.denominator;
+ ctx->enc_params.rc_framerate_denom =
+ a->parm.output.timeperframe.numerator;
+ } else {
+ mfc_err("Setting FPS is only possible for the output queue\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int vidioc_g_parm(struct file *file, void *priv, struct v4l2_streamparm *a)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (a->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ a->parm.output.timeperframe.denominator =
+ ctx->enc_params.rc_framerate_num;
+ a->parm.output.timeperframe.numerator =
+ ctx->enc_params.rc_framerate_denom;
+ } else {
+ mfc_err("Setting FPS is only possible for the output queue\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops s5p_mfc_enc_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane,
+ .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
+ .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt,
+ .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt,
+ .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt,
+ .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt,
+ .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_s_parm = vidioc_s_parm,
+ .vidioc_g_parm = vidioc_g_parm,
+};
+
+static int check_vb_with_fmt(struct s5p_mfc_fmt *fmt, struct vb2_buffer *vb)
+{
+ int i;
+
+ if (!fmt)
+ return -EINVAL;
+ if (fmt->num_planes != vb->num_planes) {
+ mfc_err("invalid plane number for the format\n");
+ return -EINVAL;
+ }
+ for (i = 0; i < fmt->num_planes; i++) {
+ if (!vb2_dma_contig_plane_dma_addr(vb, i)) {
+ mfc_err("failed to get plane cookie\n");
+ return -EINVAL;
+ }
+ mfc_debug(2, "index: %d, plane[%d] cookie: 0x%08zx",
+ vb->v4l2_buf.index, i,
+ vb2_dma_contig_plane_dma_addr(vb, i));
+ }
+ return 0;
+}
+
+static int s5p_mfc_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *buf_count, unsigned int *plane_count,
+ unsigned int psize[], void *allocators[])
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
+
+ if (ctx->state != MFCINST_GOT_INST) {
+ mfc_err("inavlid state: %d\n", ctx->state);
+ return -EINVAL;
+ }
+ if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ if (ctx->dst_fmt)
+ *plane_count = ctx->dst_fmt->num_planes;
+ else
+ *plane_count = MFC_ENC_CAP_PLANE_COUNT;
+ if (*buf_count < 1)
+ *buf_count = 1;
+ if (*buf_count > MFC_MAX_BUFFERS)
+ *buf_count = MFC_MAX_BUFFERS;
+ psize[0] = ctx->enc_dst_buf_size;
+ allocators[0] = ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
+ } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ if (ctx->src_fmt)
+ *plane_count = ctx->src_fmt->num_planes;
+ else
+ *plane_count = MFC_ENC_OUT_PLANE_COUNT;
+
+ if (*buf_count < 1)
+ *buf_count = 1;
+ if (*buf_count > MFC_MAX_BUFFERS)
+ *buf_count = MFC_MAX_BUFFERS;
+ psize[0] = ctx->luma_size;
+ psize[1] = ctx->chroma_size;
+ allocators[0] = ctx->dev->alloc_ctx[MFC_BANK2_ALLOC_CTX];
+ allocators[1] = ctx->dev->alloc_ctx[MFC_BANK2_ALLOC_CTX];
+ } else {
+ mfc_err("inavlid queue type: %d\n", vq->type);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void s5p_mfc_unlock(struct vb2_queue *q)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ mutex_unlock(&dev->mfc_mutex);
+}
+
+static void s5p_mfc_lock(struct vb2_queue *q)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ mutex_lock(&dev->mfc_mutex);
+}
+
+static int s5p_mfc_buf_init(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
+ unsigned int i;
+ int ret;
+
+ if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ ret = check_vb_with_fmt(ctx->dst_fmt, vb);
+ if (ret < 0)
+ return ret;
+ i = vb->v4l2_buf.index;
+ ctx->dst_bufs[i].b = vb;
+ ctx->dst_bufs[i].cookie.stream =
+ vb2_dma_contig_plane_dma_addr(vb, 0);
+ ctx->dst_bufs_cnt++;
+ } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ret = check_vb_with_fmt(ctx->src_fmt, vb);
+ if (ret < 0)
+ return ret;
+ i = vb->v4l2_buf.index;
+ ctx->src_bufs[i].b = vb;
+ ctx->src_bufs[i].cookie.raw.luma =
+ vb2_dma_contig_plane_dma_addr(vb, 0);
+ ctx->src_bufs[i].cookie.raw.chroma =
+ vb2_dma_contig_plane_dma_addr(vb, 1);
+ ctx->src_bufs_cnt++;
+ } else {
+ mfc_err("inavlid queue type: %d\n", vq->type);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int s5p_mfc_buf_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
+ int ret;
+
+ if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ ret = check_vb_with_fmt(ctx->dst_fmt, vb);
+ if (ret < 0)
+ return ret;
+ mfc_debug(2, "plane size: %ld, dst size: %d\n",
+ vb2_plane_size(vb, 0), ctx->enc_dst_buf_size);
+ if (vb2_plane_size(vb, 0) < ctx->enc_dst_buf_size) {
+ mfc_err("plane size is too small for capture\n");
+ return -EINVAL;
+ }
+ } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ret = check_vb_with_fmt(ctx->src_fmt, vb);
+ if (ret < 0)
+ return ret;
+ mfc_debug(2, "plane size: %ld, luma size: %d\n",
+ vb2_plane_size(vb, 0), ctx->luma_size);
+ mfc_debug(2, "plane size: %ld, chroma size: %d\n",
+ vb2_plane_size(vb, 1), ctx->chroma_size);
+ if (vb2_plane_size(vb, 0) < ctx->luma_size ||
+ vb2_plane_size(vb, 1) < ctx->chroma_size) {
+ mfc_err("plane size is too small for output\n");
+ return -EINVAL;
+ }
+ } else {
+ mfc_err("inavlid queue type: %d\n", vq->type);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long flags;
+
+ v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
+ /* If context is ready then dev = work->data;schedule it to run */
+ if (s5p_mfc_ctx_ready(ctx)) {
+ spin_lock_irqsave(&dev->condlock, flags);
+ set_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock_irqrestore(&dev->condlock, flags);
+ }
+ s5p_mfc_try_run(dev);
+ return 0;
+}
+
+static int s5p_mfc_stop_streaming(struct vb2_queue *q)
+{
+ unsigned long flags;
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ if ((ctx->state == MFCINST_FINISHING ||
+ ctx->state == MFCINST_RUNNING) &&
+ dev->curr_ctx == ctx->num && dev->hw_lock) {
+ ctx->state = MFCINST_ABORT;
+ s5p_mfc_wait_for_done_ctx(ctx, S5P_FIMV_R2H_CMD_FRAME_DONE_RET,
+ 0);
+ }
+ ctx->state = MFCINST_FINISHED;
+ spin_lock_irqsave(&dev->irqlock, flags);
+ if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
+ INIT_LIST_HEAD(&ctx->dst_queue);
+ ctx->dst_queue_cnt = 0;
+ }
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ cleanup_ref_queue(ctx);
+ s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
+ INIT_LIST_HEAD(&ctx->src_queue);
+ ctx->src_queue_cnt = 0;
+ }
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ return 0;
+}
+
+static void s5p_mfc_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long flags;
+ struct s5p_mfc_buf *mfc_buf;
+
+ if (ctx->state == MFCINST_ERROR) {
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+ cleanup_ref_queue(ctx);
+ return;
+ }
+ if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ mfc_buf = &ctx->dst_bufs[vb->v4l2_buf.index];
+ mfc_buf->used = 0;
+ /* Mark destination as available for use by MFC */
+ spin_lock_irqsave(&dev->irqlock, flags);
+ list_add_tail(&mfc_buf->list, &ctx->dst_queue);
+ ctx->dst_queue_cnt++;
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ mfc_buf = &ctx->src_bufs[vb->v4l2_buf.index];
+ mfc_buf->used = 0;
+ spin_lock_irqsave(&dev->irqlock, flags);
+ if (vb->v4l2_planes[0].bytesused == 0) {
+ mfc_debug(1, "change state to FINISHING\n");
+ ctx->state = MFCINST_FINISHING;
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ cleanup_ref_queue(ctx);
+ } else {
+ list_add_tail(&mfc_buf->list, &ctx->src_queue);
+ ctx->src_queue_cnt++;
+ }
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ } else {
+ mfc_err("unsupported buffer type (%d)\n", vq->type);
+ }
+ if (s5p_mfc_ctx_ready(ctx)) {
+ spin_lock_irqsave(&dev->condlock, flags);
+ set_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock_irqrestore(&dev->condlock, flags);
+ }
+ s5p_mfc_try_run(dev);
+}
+
+static struct vb2_ops s5p_mfc_enc_qops = {
+ .queue_setup = s5p_mfc_queue_setup,
+ .wait_prepare = s5p_mfc_unlock,
+ .wait_finish = s5p_mfc_lock,
+ .buf_init = s5p_mfc_buf_init,
+ .buf_prepare = s5p_mfc_buf_prepare,
+ .start_streaming = s5p_mfc_start_streaming,
+ .stop_streaming = s5p_mfc_stop_streaming,
+ .buf_queue = s5p_mfc_buf_queue,
+};
+
+struct s5p_mfc_codec_ops *get_enc_codec_ops(void)
+{
+ return &encoder_codec_ops;
+}
+
+struct vb2_ops *get_enc_queue_ops(void)
+{
+ return &s5p_mfc_enc_qops;
+}
+
+const struct v4l2_ioctl_ops *get_enc_v4l2_ioctl_ops(void)
+{
+ return &s5p_mfc_enc_ioctl_ops;
+}
+
+#define IS_MFC51_PRIV(x) ((V4L2_CTRL_ID2CLASS(x) == V4L2_CTRL_CLASS_MPEG) \
+ && V4L2_CTRL_DRIVER_PRIV(x))
+
+int s5p_mfc_enc_ctrls_setup(struct s5p_mfc_ctx *ctx)
+{
+ struct v4l2_ctrl_config cfg;
+ int i;
+
+ v4l2_ctrl_handler_init(&ctx->ctrl_handler, NUM_CTRLS);
+ if (ctx->ctrl_handler.error) {
+ mfc_err("v4l2_ctrl_handler_init failed\n");
+ return ctx->ctrl_handler.error;
+ }
+ for (i = 0; i < NUM_CTRLS; i++) {
+ if (IS_MFC51_PRIV(controls[i].id)) {
+ cfg.ops = &s5p_mfc_enc_ctrl_ops;
+ cfg.id = controls[i].id;
+ cfg.min = controls[i].minimum;
+ cfg.max = controls[i].maximum;
+ cfg.def = controls[i].default_value;
+ cfg.name = controls[i].name;
+ cfg.type = controls[i].type;
+ cfg.flags = 0;
+
+ if (cfg.type == V4L2_CTRL_TYPE_MENU) {
+ cfg.step = 0;
+ cfg.menu_skip_mask = cfg.menu_skip_mask;
+ cfg.qmenu = mfc51_get_menu(cfg.id);
+ } else {
+ cfg.step = controls[i].step;
+ cfg.menu_skip_mask = 0;
+ }
+ ctx->ctrls[i] = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
+ &cfg, NULL);
+ } else {
+ if (controls[i].type == V4L2_CTRL_TYPE_MENU) {
+ ctx->ctrls[i] = v4l2_ctrl_new_std_menu(
+ &ctx->ctrl_handler,
+ &s5p_mfc_enc_ctrl_ops, controls[i].id,
+ controls[i].maximum, 0,
+ controls[i].default_value);
+ } else {
+ ctx->ctrls[i] = v4l2_ctrl_new_std(
+ &ctx->ctrl_handler,
+ &s5p_mfc_enc_ctrl_ops, controls[i].id,
+ controls[i].minimum,
+ controls[i].maximum, controls[i].step,
+ controls[i].default_value);
+ }
+ }
+ if (ctx->ctrl_handler.error) {
+ mfc_err("Adding control (%d) failed\n", i);
+ return ctx->ctrl_handler.error;
+ }
+ if (controls[i].is_volatile && ctx->ctrls[i])
+ ctx->ctrls[i]->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ }
+ return 0;
+}
+
+void s5p_mfc_enc_ctrls_delete(struct s5p_mfc_ctx *ctx)
+{
+ int i;
+
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ for (i = 0; i < NUM_CTRLS; i++)
+ ctx->ctrls[i] = NULL;
+}
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_enc.h b/drivers/media/video/s5p-mfc/s5p_mfc_enc.h
new file mode 100644
index 00000000..405bdd3e
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_enc.h
@@ -0,0 +1,23 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_enc.h
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef S5P_MFC_ENC_H_
+#define S5P_MFC_ENC_H_
+
+struct s5p_mfc_codec_ops *get_enc_codec_ops(void);
+struct vb2_ops *get_enc_queue_ops(void);
+const struct v4l2_ioctl_ops *get_enc_v4l2_ioctl_ops(void);
+struct s5p_mfc_fmt *get_enc_def_fmt(bool src);
+int s5p_mfc_enc_ctrls_setup(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_enc_ctrls_delete(struct s5p_mfc_ctx *ctx);
+
+#endif /* S5P_MFC_ENC_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_intr.c b/drivers/media/video/s5p-mfc/s5p_mfc_intr.c
new file mode 100644
index 00000000..8f2f8bf4
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_intr.c
@@ -0,0 +1,92 @@
+/*
+ * drivers/media/video/samsung/mfc5/s5p_mfc_intr.c
+ *
+ * C file for Samsung MFC (Multi Function Codec - FIMV) driver
+ * This file contains functions used to wait for command completion.
+ *
+ * Kamil Debski, Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include "regs-mfc.h"
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_debug.h"
+#include "s5p_mfc_intr.h"
+
+int s5p_mfc_wait_for_done_dev(struct s5p_mfc_dev *dev, int command)
+{
+ int ret;
+
+ ret = wait_event_interruptible_timeout(dev->queue,
+ (dev->int_cond && (dev->int_type == command
+ || dev->int_type == S5P_FIMV_R2H_CMD_ERR_RET)),
+ msecs_to_jiffies(MFC_INT_TIMEOUT));
+ if (ret == 0) {
+ mfc_err("Interrupt (dev->int_type:%d, command:%d) timed out\n",
+ dev->int_type, command);
+ return 1;
+ } else if (ret == -ERESTARTSYS) {
+ mfc_err("Interrupted by a signal\n");
+ return 1;
+ }
+ mfc_debug(1, "Finished waiting (dev->int_type:%d, command: %d)\n",
+ dev->int_type, command);
+ if (dev->int_type == S5P_FIMV_R2H_CMD_ERR_RET)
+ return 1;
+ return 0;
+}
+
+void s5p_mfc_clean_dev_int_flags(struct s5p_mfc_dev *dev)
+{
+ dev->int_cond = 0;
+ dev->int_type = 0;
+ dev->int_err = 0;
+}
+
+int s5p_mfc_wait_for_done_ctx(struct s5p_mfc_ctx *ctx,
+ int command, int interrupt)
+{
+ int ret;
+
+ if (interrupt) {
+ ret = wait_event_interruptible_timeout(ctx->queue,
+ (ctx->int_cond && (ctx->int_type == command
+ || ctx->int_type == S5P_FIMV_R2H_CMD_ERR_RET)),
+ msecs_to_jiffies(MFC_INT_TIMEOUT));
+ } else {
+ ret = wait_event_timeout(ctx->queue,
+ (ctx->int_cond && (ctx->int_type == command
+ || ctx->int_type == S5P_FIMV_R2H_CMD_ERR_RET)),
+ msecs_to_jiffies(MFC_INT_TIMEOUT));
+ }
+ if (ret == 0) {
+ mfc_err("Interrupt (ctx->int_type:%d, command:%d) timed out\n",
+ ctx->int_type, command);
+ return 1;
+ } else if (ret == -ERESTARTSYS) {
+ mfc_err("Interrupted by a signal\n");
+ return 1;
+ }
+ mfc_debug(1, "Finished waiting (ctx->int_type:%d, command: %d)\n",
+ ctx->int_type, command);
+ if (ctx->int_type == S5P_FIMV_R2H_CMD_ERR_RET)
+ return 1;
+ return 0;
+}
+
+void s5p_mfc_clean_ctx_int_flags(struct s5p_mfc_ctx *ctx)
+{
+ ctx->int_cond = 0;
+ ctx->int_type = 0;
+ ctx->int_err = 0;
+}
+
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_intr.h b/drivers/media/video/s5p-mfc/s5p_mfc_intr.h
new file mode 100644
index 00000000..122d7732
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_intr.h
@@ -0,0 +1,26 @@
+/*
+ * drivers/media/video/samsung/mfc5/s5p_mfc_intr.h
+ *
+ * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
+ * It contains waiting functions declarations.
+ *
+ * Kamil Debski, Copyright (C) 2011 Samsung Electronics
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef S5P_MFC_INTR_H_
+#define S5P_MFC_INTR_H_
+
+#include "s5p_mfc_common.h"
+
+int s5p_mfc_wait_for_done_ctx(struct s5p_mfc_ctx *ctx,
+ int command, int interrupt);
+int s5p_mfc_wait_for_done_dev(struct s5p_mfc_dev *dev, int command);
+void s5p_mfc_clean_ctx_int_flags(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_clean_dev_int_flags(struct s5p_mfc_dev *dev);
+
+#endif /* S5P_MFC_INTR_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_opr.c b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c
new file mode 100644
index 00000000..e08b21c5
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c
@@ -0,0 +1,1397 @@
+/*
+ * drivers/media/video/samsung/mfc5/s5p_mfc_opr.c
+ *
+ * Samsung MFC (Multi Function Codec - FIMV) driver
+ * This file contains hw related functions.
+ *
+ * Kamil Debski, Copyright (c) 2011 Samsung Electronics
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "regs-mfc.h"
+#include "s5p_mfc_cmd.h"
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_ctrl.h"
+#include "s5p_mfc_debug.h"
+#include "s5p_mfc_intr.h"
+#include "s5p_mfc_opr.h"
+#include "s5p_mfc_pm.h"
+#include "s5p_mfc_shm.h"
+#include <asm/cacheflush.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+
+#define OFFSETA(x) (((x) - dev->bank1) >> MFC_OFFSET_SHIFT)
+#define OFFSETB(x) (((x) - dev->bank2) >> MFC_OFFSET_SHIFT)
+
+/* Allocate temporary buffers for decoding */
+int s5p_mfc_alloc_dec_temp_buffers(struct s5p_mfc_ctx *ctx)
+{
+ void *desc_virt;
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ ctx->desc_buf = vb2_dma_contig_memops.alloc(
+ dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], DESC_BUF_SIZE);
+ if (IS_ERR_VALUE((int)ctx->desc_buf)) {
+ ctx->desc_buf = 0;
+ mfc_err("Allocating DESC buffer failed\n");
+ return -ENOMEM;
+ }
+ ctx->desc_phys = s5p_mfc_mem_cookie(
+ dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->desc_buf);
+ BUG_ON(ctx->desc_phys & ((1 << MFC_BANK1_ALIGN_ORDER) - 1));
+ desc_virt = vb2_dma_contig_memops.vaddr(ctx->desc_buf);
+ if (desc_virt == NULL) {
+ vb2_dma_contig_memops.put(ctx->desc_buf);
+ ctx->desc_phys = 0;
+ ctx->desc_buf = 0;
+ mfc_err("Remapping DESC buffer failed\n");
+ return -ENOMEM;
+ }
+ memset(desc_virt, 0, DESC_BUF_SIZE);
+ wmb();
+ return 0;
+}
+
+/* Release temporary buffers for decoding */
+void s5p_mfc_release_dec_desc_buffer(struct s5p_mfc_ctx *ctx)
+{
+ if (ctx->desc_phys) {
+ vb2_dma_contig_memops.put(ctx->desc_buf);
+ ctx->desc_phys = 0;
+ ctx->desc_buf = 0;
+ }
+}
+
+/* Allocate codec buffers */
+int s5p_mfc_alloc_codec_buffers(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned int enc_ref_y_size = 0;
+ unsigned int enc_ref_c_size = 0;
+ unsigned int guard_width, guard_height;
+
+ if (ctx->type == MFCINST_DECODER) {
+ mfc_debug(2, "Luma size:%d Chroma size:%d MV size:%d\n",
+ ctx->luma_size, ctx->chroma_size, ctx->mv_size);
+ mfc_debug(2, "Totals bufs: %d\n", ctx->total_dpb_count);
+ } else if (ctx->type == MFCINST_ENCODER) {
+ enc_ref_y_size = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN)
+ * ALIGN(ctx->img_height, S5P_FIMV_NV12MT_VALIGN);
+ enc_ref_y_size = ALIGN(enc_ref_y_size, S5P_FIMV_NV12MT_SALIGN);
+
+ if (ctx->codec_mode == S5P_FIMV_CODEC_H264_ENC) {
+ enc_ref_c_size = ALIGN(ctx->img_width,
+ S5P_FIMV_NV12MT_HALIGN)
+ * ALIGN(ctx->img_height >> 1,
+ S5P_FIMV_NV12MT_VALIGN);
+ enc_ref_c_size = ALIGN(enc_ref_c_size,
+ S5P_FIMV_NV12MT_SALIGN);
+ } else {
+ guard_width = ALIGN(ctx->img_width + 16,
+ S5P_FIMV_NV12MT_HALIGN);
+ guard_height = ALIGN((ctx->img_height >> 1) + 4,
+ S5P_FIMV_NV12MT_VALIGN);
+ enc_ref_c_size = ALIGN(guard_width * guard_height,
+ S5P_FIMV_NV12MT_SALIGN);
+ }
+ mfc_debug(2, "recon luma size: %d chroma size: %d\n",
+ enc_ref_y_size, enc_ref_c_size);
+ } else {
+ return -EINVAL;
+ }
+ /* Codecs have different memory requirements */
+ switch (ctx->codec_mode) {
+ case S5P_FIMV_CODEC_H264_DEC:
+ ctx->bank1_size =
+ ALIGN(S5P_FIMV_DEC_NB_IP_SIZE +
+ S5P_FIMV_DEC_VERT_NB_MV_SIZE,
+ S5P_FIMV_DEC_BUF_ALIGN);
+ ctx->bank2_size = ctx->total_dpb_count * ctx->mv_size;
+ break;
+ case S5P_FIMV_CODEC_MPEG4_DEC:
+ ctx->bank1_size =
+ ALIGN(S5P_FIMV_DEC_NB_DCAC_SIZE +
+ S5P_FIMV_DEC_UPNB_MV_SIZE +
+ S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
+ S5P_FIMV_DEC_STX_PARSER_SIZE +
+ S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE,
+ S5P_FIMV_DEC_BUF_ALIGN);
+ ctx->bank2_size = 0;
+ break;
+ case S5P_FIMV_CODEC_VC1RCV_DEC:
+ case S5P_FIMV_CODEC_VC1_DEC:
+ ctx->bank1_size =
+ ALIGN(S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE +
+ S5P_FIMV_DEC_UPNB_MV_SIZE +
+ S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
+ S5P_FIMV_DEC_NB_DCAC_SIZE +
+ 3 * S5P_FIMV_DEC_VC1_BITPLANE_SIZE,
+ S5P_FIMV_DEC_BUF_ALIGN);
+ ctx->bank2_size = 0;
+ break;
+ case S5P_FIMV_CODEC_MPEG2_DEC:
+ ctx->bank1_size = 0;
+ ctx->bank2_size = 0;
+ break;
+ case S5P_FIMV_CODEC_H263_DEC:
+ ctx->bank1_size =
+ ALIGN(S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE +
+ S5P_FIMV_DEC_UPNB_MV_SIZE +
+ S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
+ S5P_FIMV_DEC_NB_DCAC_SIZE,
+ S5P_FIMV_DEC_BUF_ALIGN);
+ ctx->bank2_size = 0;
+ break;
+ case S5P_FIMV_CODEC_H264_ENC:
+ ctx->bank1_size = (enc_ref_y_size * 2) +
+ S5P_FIMV_ENC_UPMV_SIZE +
+ S5P_FIMV_ENC_COLFLG_SIZE +
+ S5P_FIMV_ENC_INTRAMD_SIZE +
+ S5P_FIMV_ENC_NBORINFO_SIZE;
+ ctx->bank2_size = (enc_ref_y_size * 2) +
+ (enc_ref_c_size * 4) +
+ S5P_FIMV_ENC_INTRAPRED_SIZE;
+ break;
+ case S5P_FIMV_CODEC_MPEG4_ENC:
+ ctx->bank1_size = (enc_ref_y_size * 2) +
+ S5P_FIMV_ENC_UPMV_SIZE +
+ S5P_FIMV_ENC_COLFLG_SIZE +
+ S5P_FIMV_ENC_ACDCCOEF_SIZE;
+ ctx->bank2_size = (enc_ref_y_size * 2) +
+ (enc_ref_c_size * 4);
+ break;
+ case S5P_FIMV_CODEC_H263_ENC:
+ ctx->bank1_size = (enc_ref_y_size * 2) +
+ S5P_FIMV_ENC_UPMV_SIZE +
+ S5P_FIMV_ENC_ACDCCOEF_SIZE;
+ ctx->bank2_size = (enc_ref_y_size * 2) +
+ (enc_ref_c_size * 4);
+ break;
+ default:
+ break;
+ }
+ /* Allocate only if memory from bank 1 is necessary */
+ if (ctx->bank1_size > 0) {
+ ctx->bank1_buf = vb2_dma_contig_memops.alloc(
+ dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_size);
+ if (IS_ERR(ctx->bank1_buf)) {
+ ctx->bank1_buf = 0;
+ printk(KERN_ERR
+ "Buf alloc for decoding failed (port A)\n");
+ return -ENOMEM;
+ }
+ ctx->bank1_phys = s5p_mfc_mem_cookie(
+ dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_buf);
+ BUG_ON(ctx->bank1_phys & ((1 << MFC_BANK1_ALIGN_ORDER) - 1));
+ }
+ /* Allocate only if memory from bank 2 is necessary */
+ if (ctx->bank2_size > 0) {
+ ctx->bank2_buf = vb2_dma_contig_memops.alloc(
+ dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], ctx->bank2_size);
+ if (IS_ERR(ctx->bank2_buf)) {
+ ctx->bank2_buf = 0;
+ mfc_err("Buf alloc for decoding failed (port B)\n");
+ return -ENOMEM;
+ }
+ ctx->bank2_phys = s5p_mfc_mem_cookie(
+ dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], ctx->bank2_buf);
+ BUG_ON(ctx->bank2_phys & ((1 << MFC_BANK2_ALIGN_ORDER) - 1));
+ }
+ return 0;
+}
+
+/* Release buffers allocated for codec */
+void s5p_mfc_release_codec_buffers(struct s5p_mfc_ctx *ctx)
+{
+ if (ctx->bank1_buf) {
+ vb2_dma_contig_memops.put(ctx->bank1_buf);
+ ctx->bank1_buf = 0;
+ ctx->bank1_phys = 0;
+ ctx->bank1_size = 0;
+ }
+ if (ctx->bank2_buf) {
+ vb2_dma_contig_memops.put(ctx->bank2_buf);
+ ctx->bank2_buf = 0;
+ ctx->bank2_phys = 0;
+ ctx->bank2_size = 0;
+ }
+}
+
+/* Allocate memory for instance data buffer */
+int s5p_mfc_alloc_instance_buffer(struct s5p_mfc_ctx *ctx)
+{
+ void *context_virt;
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC ||
+ ctx->codec_mode == S5P_FIMV_CODEC_H264_ENC)
+ ctx->ctx_size = MFC_H264_CTX_BUF_SIZE;
+ else
+ ctx->ctx_size = MFC_CTX_BUF_SIZE;
+ ctx->ctx_buf = vb2_dma_contig_memops.alloc(
+ dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->ctx_size);
+ if (IS_ERR(ctx->ctx_buf)) {
+ mfc_err("Allocating context buffer failed\n");
+ ctx->ctx_phys = 0;
+ ctx->ctx_buf = 0;
+ return -ENOMEM;
+ }
+ ctx->ctx_phys = s5p_mfc_mem_cookie(
+ dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->ctx_buf);
+ BUG_ON(ctx->ctx_phys & ((1 << MFC_BANK1_ALIGN_ORDER) - 1));
+ ctx->ctx_ofs = OFFSETA(ctx->ctx_phys);
+ context_virt = vb2_dma_contig_memops.vaddr(ctx->ctx_buf);
+ if (context_virt == NULL) {
+ mfc_err("Remapping instance buffer failed\n");
+ vb2_dma_contig_memops.put(ctx->ctx_buf);
+ ctx->ctx_phys = 0;
+ ctx->ctx_buf = 0;
+ return -ENOMEM;
+ }
+ /* Zero content of the allocated memory */
+ memset(context_virt, 0, ctx->ctx_size);
+ wmb();
+ if (s5p_mfc_init_shm(ctx) < 0) {
+ vb2_dma_contig_memops.put(ctx->ctx_buf);
+ ctx->ctx_phys = 0;
+ ctx->ctx_buf = 0;
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+/* Release instance buffer */
+void s5p_mfc_release_instance_buffer(struct s5p_mfc_ctx *ctx)
+{
+ if (ctx->ctx_buf) {
+ vb2_dma_contig_memops.put(ctx->ctx_buf);
+ ctx->ctx_phys = 0;
+ ctx->ctx_buf = 0;
+ }
+ if (ctx->shm_alloc) {
+ vb2_dma_contig_memops.put(ctx->shm_alloc);
+ ctx->shm_alloc = 0;
+ ctx->shm = 0;
+ }
+}
+
+/* Set registers for decoding temporary buffers */
+void s5p_mfc_set_dec_desc_buffer(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ mfc_write(dev, OFFSETA(ctx->desc_phys), S5P_FIMV_SI_CH0_DESC_ADR);
+ mfc_write(dev, DESC_BUF_SIZE, S5P_FIMV_SI_CH0_DESC_SIZE);
+}
+
+/* Set registers for shared buffer */
+void s5p_mfc_set_shared_buffer(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ mfc_write(dev, ctx->shm_ofs, S5P_FIMV_SI_CH0_HOST_WR_ADR);
+}
+
+/* Set registers for decoding stream buffer */
+int s5p_mfc_set_dec_stream_buffer(struct s5p_mfc_ctx *ctx, int buf_addr,
+ unsigned int start_num_byte, unsigned int buf_size)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ mfc_write(dev, OFFSETA(buf_addr), S5P_FIMV_SI_CH0_SB_ST_ADR);
+ mfc_write(dev, ctx->dec_src_buf_size, S5P_FIMV_SI_CH0_CPB_SIZE);
+ mfc_write(dev, buf_size, S5P_FIMV_SI_CH0_SB_FRM_SIZE);
+ s5p_mfc_write_shm(ctx, start_num_byte, START_BYTE_NUM);
+ return 0;
+}
+
+/* Set decoding frame buffer */
+int s5p_mfc_set_dec_frame_buffer(struct s5p_mfc_ctx *ctx)
+{
+ unsigned int frame_size, i;
+ unsigned int frame_size_ch, frame_size_mv;
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned int dpb;
+ size_t buf_addr1, buf_addr2;
+ int buf_size1, buf_size2;
+
+ buf_addr1 = ctx->bank1_phys;
+ buf_size1 = ctx->bank1_size;
+ buf_addr2 = ctx->bank2_phys;
+ buf_size2 = ctx->bank2_size;
+ dpb = mfc_read(dev, S5P_FIMV_SI_CH0_DPB_CONF_CTRL) &
+ ~S5P_FIMV_DPB_COUNT_MASK;
+ mfc_write(dev, ctx->total_dpb_count | dpb,
+ S5P_FIMV_SI_CH0_DPB_CONF_CTRL);
+ s5p_mfc_set_shared_buffer(ctx);
+ switch (ctx->codec_mode) {
+ case S5P_FIMV_CODEC_H264_DEC:
+ mfc_write(dev, OFFSETA(buf_addr1),
+ S5P_FIMV_H264_VERT_NB_MV_ADR);
+ buf_addr1 += S5P_FIMV_DEC_VERT_NB_MV_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_VERT_NB_MV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H264_NB_IP_ADR);
+ buf_addr1 += S5P_FIMV_DEC_NB_IP_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_NB_IP_SIZE;
+ break;
+ case S5P_FIMV_CODEC_MPEG4_DEC:
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_NB_DCAC_ADR);
+ buf_addr1 += S5P_FIMV_DEC_NB_DCAC_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_NB_DCAC_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_UP_NB_MV_ADR);
+ buf_addr1 += S5P_FIMV_DEC_UPNB_MV_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_UPNB_MV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_SA_MV_ADR);
+ buf_addr1 += S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_SP_ADR);
+ buf_addr1 += S5P_FIMV_DEC_STX_PARSER_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_STX_PARSER_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_OT_LINE_ADR);
+ buf_addr1 += S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+ break;
+ case S5P_FIMV_CODEC_H263_DEC:
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_OT_LINE_ADR);
+ buf_addr1 += S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_UP_NB_MV_ADR);
+ buf_addr1 += S5P_FIMV_DEC_UPNB_MV_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_UPNB_MV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_SA_MV_ADR);
+ buf_addr1 += S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_NB_DCAC_ADR);
+ buf_addr1 += S5P_FIMV_DEC_NB_DCAC_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_NB_DCAC_SIZE;
+ break;
+ case S5P_FIMV_CODEC_VC1_DEC:
+ case S5P_FIMV_CODEC_VC1RCV_DEC:
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_NB_DCAC_ADR);
+ buf_addr1 += S5P_FIMV_DEC_NB_DCAC_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_NB_DCAC_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_OT_LINE_ADR);
+ buf_addr1 += S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_UP_NB_MV_ADR);
+ buf_addr1 += S5P_FIMV_DEC_UPNB_MV_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_UPNB_MV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_SA_MV_ADR);
+ buf_addr1 += S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_BITPLANE3_ADR);
+ buf_addr1 += S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_BITPLANE2_ADR);
+ buf_addr1 += S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_BITPLANE1_ADR);
+ buf_addr1 += S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+ break;
+ case S5P_FIMV_CODEC_MPEG2_DEC:
+ break;
+ default:
+ mfc_err("Unknown codec for decoding (%x)\n",
+ ctx->codec_mode);
+ return -EINVAL;
+ break;
+ }
+ frame_size = ctx->luma_size;
+ frame_size_ch = ctx->chroma_size;
+ frame_size_mv = ctx->mv_size;
+ mfc_debug(2, "Frm size: %d ch: %d mv: %d\n", frame_size, frame_size_ch,
+ frame_size_mv);
+ for (i = 0; i < ctx->total_dpb_count; i++) {
+ /* Bank2 */
+ mfc_debug(2, "Luma %d: %x\n", i,
+ ctx->dst_bufs[i].cookie.raw.luma);
+ mfc_write(dev, OFFSETB(ctx->dst_bufs[i].cookie.raw.luma),
+ S5P_FIMV_DEC_LUMA_ADR + i * 4);
+ mfc_debug(2, "\tChroma %d: %x\n", i,
+ ctx->dst_bufs[i].cookie.raw.chroma);
+ mfc_write(dev, OFFSETA(ctx->dst_bufs[i].cookie.raw.chroma),
+ S5P_FIMV_DEC_CHROMA_ADR + i * 4);
+ if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC) {
+ mfc_debug(2, "\tBuf2: %x, size: %d\n",
+ buf_addr2, buf_size2);
+ mfc_write(dev, OFFSETB(buf_addr2),
+ S5P_FIMV_H264_MV_ADR + i * 4);
+ buf_addr2 += frame_size_mv;
+ buf_size2 -= frame_size_mv;
+ }
+ }
+ mfc_debug(2, "Buf1: %u, buf_size1: %d\n", buf_addr1, buf_size1);
+ mfc_debug(2, "Buf 1/2 size after: %d/%d (frames %d)\n",
+ buf_size1, buf_size2, ctx->total_dpb_count);
+ if (buf_size1 < 0 || buf_size2 < 0) {
+ mfc_debug(2, "Not enough memory has been allocated\n");
+ return -ENOMEM;
+ }
+ s5p_mfc_write_shm(ctx, frame_size, ALLOC_LUMA_DPB_SIZE);
+ s5p_mfc_write_shm(ctx, frame_size_ch, ALLOC_CHROMA_DPB_SIZE);
+ if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC)
+ s5p_mfc_write_shm(ctx, frame_size_mv, ALLOC_MV_SIZE);
+ mfc_write(dev, ((S5P_FIMV_CH_INIT_BUFS & S5P_FIMV_CH_MASK)
+ << S5P_FIMV_CH_SHIFT) | (ctx->inst_no),
+ S5P_FIMV_SI_CH0_INST_ID);
+ return 0;
+}
+
+/* Set registers for encoding stream buffer */
+int s5p_mfc_set_enc_stream_buffer(struct s5p_mfc_ctx *ctx,
+ unsigned long addr, unsigned int size)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ mfc_write(dev, OFFSETA(addr), S5P_FIMV_ENC_SI_CH0_SB_ADR);
+ mfc_write(dev, size, S5P_FIMV_ENC_SI_CH0_SB_SIZE);
+ return 0;
+}
+
+void s5p_mfc_set_enc_frame_buffer(struct s5p_mfc_ctx *ctx,
+ unsigned long y_addr, unsigned long c_addr)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ mfc_write(dev, OFFSETB(y_addr), S5P_FIMV_ENC_SI_CH0_CUR_Y_ADR);
+ mfc_write(dev, OFFSETB(c_addr), S5P_FIMV_ENC_SI_CH0_CUR_C_ADR);
+}
+
+void s5p_mfc_get_enc_frame_buffer(struct s5p_mfc_ctx *ctx,
+ unsigned long *y_addr, unsigned long *c_addr)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ *y_addr = dev->bank2 + (mfc_read(dev, S5P_FIMV_ENCODED_Y_ADDR)
+ << MFC_OFFSET_SHIFT);
+ *c_addr = dev->bank2 + (mfc_read(dev, S5P_FIMV_ENCODED_C_ADDR)
+ << MFC_OFFSET_SHIFT);
+}
+
+/* Set encoding ref & codec buffer */
+int s5p_mfc_set_enc_ref_buffer(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ size_t buf_addr1, buf_addr2;
+ size_t buf_size1, buf_size2;
+ unsigned int enc_ref_y_size, enc_ref_c_size;
+ unsigned int guard_width, guard_height;
+ int i;
+
+ buf_addr1 = ctx->bank1_phys;
+ buf_size1 = ctx->bank1_size;
+ buf_addr2 = ctx->bank2_phys;
+ buf_size2 = ctx->bank2_size;
+ enc_ref_y_size = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN)
+ * ALIGN(ctx->img_height, S5P_FIMV_NV12MT_VALIGN);
+ enc_ref_y_size = ALIGN(enc_ref_y_size, S5P_FIMV_NV12MT_SALIGN);
+ if (ctx->codec_mode == S5P_FIMV_CODEC_H264_ENC) {
+ enc_ref_c_size = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN)
+ * ALIGN((ctx->img_height >> 1), S5P_FIMV_NV12MT_VALIGN);
+ enc_ref_c_size = ALIGN(enc_ref_c_size, S5P_FIMV_NV12MT_SALIGN);
+ } else {
+ guard_width = ALIGN(ctx->img_width + 16,
+ S5P_FIMV_NV12MT_HALIGN);
+ guard_height = ALIGN((ctx->img_height >> 1) + 4,
+ S5P_FIMV_NV12MT_VALIGN);
+ enc_ref_c_size = ALIGN(guard_width * guard_height,
+ S5P_FIMV_NV12MT_SALIGN);
+ }
+ mfc_debug(2, "buf_size1: %d, buf_size2: %d\n", buf_size1, buf_size2);
+ switch (ctx->codec_mode) {
+ case S5P_FIMV_CODEC_H264_ENC:
+ for (i = 0; i < 2; i++) {
+ mfc_write(dev, OFFSETA(buf_addr1),
+ S5P_FIMV_ENC_REF0_LUMA_ADR + (4 * i));
+ buf_addr1 += enc_ref_y_size;
+ buf_size1 -= enc_ref_y_size;
+
+ mfc_write(dev, OFFSETB(buf_addr2),
+ S5P_FIMV_ENC_REF2_LUMA_ADR + (4 * i));
+ buf_addr2 += enc_ref_y_size;
+ buf_size2 -= enc_ref_y_size;
+ }
+ for (i = 0; i < 4; i++) {
+ mfc_write(dev, OFFSETB(buf_addr2),
+ S5P_FIMV_ENC_REF0_CHROMA_ADR + (4 * i));
+ buf_addr2 += enc_ref_c_size;
+ buf_size2 -= enc_ref_c_size;
+ }
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H264_UP_MV_ADR);
+ buf_addr1 += S5P_FIMV_ENC_UPMV_SIZE;
+ buf_size1 -= S5P_FIMV_ENC_UPMV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1),
+ S5P_FIMV_H264_COZERO_FLAG_ADR);
+ buf_addr1 += S5P_FIMV_ENC_COLFLG_SIZE;
+ buf_size1 -= S5P_FIMV_ENC_COLFLG_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1),
+ S5P_FIMV_H264_UP_INTRA_MD_ADR);
+ buf_addr1 += S5P_FIMV_ENC_INTRAMD_SIZE;
+ buf_size1 -= S5P_FIMV_ENC_INTRAMD_SIZE;
+ mfc_write(dev, OFFSETB(buf_addr2),
+ S5P_FIMV_H264_UP_INTRA_PRED_ADR);
+ buf_addr2 += S5P_FIMV_ENC_INTRAPRED_SIZE;
+ buf_size2 -= S5P_FIMV_ENC_INTRAPRED_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1),
+ S5P_FIMV_H264_NBOR_INFO_ADR);
+ buf_addr1 += S5P_FIMV_ENC_NBORINFO_SIZE;
+ buf_size1 -= S5P_FIMV_ENC_NBORINFO_SIZE;
+ mfc_debug(2, "buf_size1: %d, buf_size2: %d\n",
+ buf_size1, buf_size2);
+ break;
+ case S5P_FIMV_CODEC_MPEG4_ENC:
+ for (i = 0; i < 2; i++) {
+ mfc_write(dev, OFFSETA(buf_addr1),
+ S5P_FIMV_ENC_REF0_LUMA_ADR + (4 * i));
+ buf_addr1 += enc_ref_y_size;
+ buf_size1 -= enc_ref_y_size;
+ mfc_write(dev, OFFSETB(buf_addr2),
+ S5P_FIMV_ENC_REF2_LUMA_ADR + (4 * i));
+ buf_addr2 += enc_ref_y_size;
+ buf_size2 -= enc_ref_y_size;
+ }
+ for (i = 0; i < 4; i++) {
+ mfc_write(dev, OFFSETB(buf_addr2),
+ S5P_FIMV_ENC_REF0_CHROMA_ADR + (4 * i));
+ buf_addr2 += enc_ref_c_size;
+ buf_size2 -= enc_ref_c_size;
+ }
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_UP_MV_ADR);
+ buf_addr1 += S5P_FIMV_ENC_UPMV_SIZE;
+ buf_size1 -= S5P_FIMV_ENC_UPMV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1),
+ S5P_FIMV_MPEG4_COZERO_FLAG_ADR);
+ buf_addr1 += S5P_FIMV_ENC_COLFLG_SIZE;
+ buf_size1 -= S5P_FIMV_ENC_COLFLG_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1),
+ S5P_FIMV_MPEG4_ACDC_COEF_ADR);
+ buf_addr1 += S5P_FIMV_ENC_ACDCCOEF_SIZE;
+ buf_size1 -= S5P_FIMV_ENC_ACDCCOEF_SIZE;
+ mfc_debug(2, "buf_size1: %d, buf_size2: %d\n",
+ buf_size1, buf_size2);
+ break;
+ case S5P_FIMV_CODEC_H263_ENC:
+ for (i = 0; i < 2; i++) {
+ mfc_write(dev, OFFSETA(buf_addr1),
+ S5P_FIMV_ENC_REF0_LUMA_ADR + (4 * i));
+ buf_addr1 += enc_ref_y_size;
+ buf_size1 -= enc_ref_y_size;
+ mfc_write(dev, OFFSETB(buf_addr2),
+ S5P_FIMV_ENC_REF2_LUMA_ADR + (4 * i));
+ buf_addr2 += enc_ref_y_size;
+ buf_size2 -= enc_ref_y_size;
+ }
+ for (i = 0; i < 4; i++) {
+ mfc_write(dev, OFFSETB(buf_addr2),
+ S5P_FIMV_ENC_REF0_CHROMA_ADR + (4 * i));
+ buf_addr2 += enc_ref_c_size;
+ buf_size2 -= enc_ref_c_size;
+ }
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_UP_MV_ADR);
+ buf_addr1 += S5P_FIMV_ENC_UPMV_SIZE;
+ buf_size1 -= S5P_FIMV_ENC_UPMV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_ACDC_COEF_ADR);
+ buf_addr1 += S5P_FIMV_ENC_ACDCCOEF_SIZE;
+ buf_size1 -= S5P_FIMV_ENC_ACDCCOEF_SIZE;
+ mfc_debug(2, "buf_size1: %d, buf_size2: %d\n",
+ buf_size1, buf_size2);
+ break;
+ default:
+ mfc_err("Unknown codec set for encoding: %d\n",
+ ctx->codec_mode);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_enc_params *p = &ctx->enc_params;
+ unsigned int reg;
+ unsigned int shm;
+
+ /* width */
+ mfc_write(dev, ctx->img_width, S5P_FIMV_ENC_HSIZE_PX);
+ /* height */
+ mfc_write(dev, ctx->img_height, S5P_FIMV_ENC_VSIZE_PX);
+ /* pictype : enable, IDR period */
+ reg = mfc_read(dev, S5P_FIMV_ENC_PIC_TYPE_CTRL);
+ reg |= (1 << 18);
+ reg &= ~(0xFFFF);
+ reg |= p->gop_size;
+ mfc_write(dev, reg, S5P_FIMV_ENC_PIC_TYPE_CTRL);
+ mfc_write(dev, 0, S5P_FIMV_ENC_B_RECON_WRITE_ON);
+ /* multi-slice control */
+ /* multi-slice MB number or bit size */
+ mfc_write(dev, p->slice_mode, S5P_FIMV_ENC_MSLICE_CTRL);
+ if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) {
+ mfc_write(dev, p->slice_mb, S5P_FIMV_ENC_MSLICE_MB);
+ } else if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) {
+ mfc_write(dev, p->slice_bit, S5P_FIMV_ENC_MSLICE_BIT);
+ } else {
+ mfc_write(dev, 0, S5P_FIMV_ENC_MSLICE_MB);
+ mfc_write(dev, 0, S5P_FIMV_ENC_MSLICE_BIT);
+ }
+ /* cyclic intra refresh */
+ mfc_write(dev, p->intra_refresh_mb, S5P_FIMV_ENC_CIR_CTRL);
+ /* memory structure cur. frame */
+ if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M)
+ mfc_write(dev, 0, S5P_FIMV_ENC_MAP_FOR_CUR);
+ else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12MT)
+ mfc_write(dev, 3, S5P_FIMV_ENC_MAP_FOR_CUR);
+ /* padding control & value */
+ reg = mfc_read(dev, S5P_FIMV_ENC_PADDING_CTRL);
+ if (p->pad) {
+ /** enable */
+ reg |= (1 << 31);
+ /** cr value */
+ reg &= ~(0xFF << 16);
+ reg |= (p->pad_cr << 16);
+ /** cb value */
+ reg &= ~(0xFF << 8);
+ reg |= (p->pad_cb << 8);
+ /** y value */
+ reg &= ~(0xFF);
+ reg |= (p->pad_luma);
+ } else {
+ /** disable & all value clear */
+ reg = 0;
+ }
+ mfc_write(dev, reg, S5P_FIMV_ENC_PADDING_CTRL);
+ /* rate control config. */
+ reg = mfc_read(dev, S5P_FIMV_ENC_RC_CONFIG);
+ /** frame-level rate control */
+ reg &= ~(0x1 << 9);
+ reg |= (p->rc_frame << 9);
+ mfc_write(dev, reg, S5P_FIMV_ENC_RC_CONFIG);
+ /* bit rate */
+ if (p->rc_frame)
+ mfc_write(dev, p->rc_bitrate,
+ S5P_FIMV_ENC_RC_BIT_RATE);
+ else
+ mfc_write(dev, 0, S5P_FIMV_ENC_RC_BIT_RATE);
+ /* reaction coefficient */
+ if (p->rc_frame)
+ mfc_write(dev, p->rc_reaction_coeff, S5P_FIMV_ENC_RC_RPARA);
+ shm = s5p_mfc_read_shm(ctx, EXT_ENC_CONTROL);
+ /* seq header ctrl */
+ shm &= ~(0x1 << 3);
+ shm |= (p->seq_hdr_mode << 3);
+ /* frame skip mode */
+ shm &= ~(0x3 << 1);
+ shm |= (p->frame_skip_mode << 1);
+ s5p_mfc_write_shm(ctx, shm, EXT_ENC_CONTROL);
+ /* fixed target bit */
+ s5p_mfc_write_shm(ctx, p->fixed_target_bit, RC_CONTROL_CONFIG);
+ return 0;
+}
+
+static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_enc_params *p = &ctx->enc_params;
+ struct s5p_mfc_h264_enc_params *p_264 = &p->codec.h264;
+ unsigned int reg;
+ unsigned int shm;
+
+ s5p_mfc_set_enc_params(ctx);
+ /* pictype : number of B */
+ reg = mfc_read(dev, S5P_FIMV_ENC_PIC_TYPE_CTRL);
+ /* num_b_frame - 0 ~ 2 */
+ reg &= ~(0x3 << 16);
+ reg |= (p->num_b_frame << 16);
+ mfc_write(dev, reg, S5P_FIMV_ENC_PIC_TYPE_CTRL);
+ /* profile & level */
+ reg = mfc_read(dev, S5P_FIMV_ENC_PROFILE);
+ /* level */
+ reg &= ~(0xFF << 8);
+ reg |= (p_264->level << 8);
+ /* profile - 0 ~ 2 */
+ reg &= ~(0x3F);
+ reg |= p_264->profile;
+ mfc_write(dev, reg, S5P_FIMV_ENC_PROFILE);
+ /* interlace */
+ mfc_write(dev, p->interlace, S5P_FIMV_ENC_PIC_STRUCT);
+ /* height */
+ if (p->interlace)
+ mfc_write(dev, ctx->img_height >> 1, S5P_FIMV_ENC_VSIZE_PX);
+ /* loopfilter ctrl */
+ mfc_write(dev, p_264->loop_filter_mode, S5P_FIMV_ENC_LF_CTRL);
+ /* loopfilter alpha offset */
+ if (p_264->loop_filter_alpha < 0) {
+ reg = 0x10;
+ reg |= (0xFF - p_264->loop_filter_alpha) + 1;
+ } else {
+ reg = 0x00;
+ reg |= (p_264->loop_filter_alpha & 0xF);
+ }
+ mfc_write(dev, reg, S5P_FIMV_ENC_ALPHA_OFF);
+ /* loopfilter beta offset */
+ if (p_264->loop_filter_beta < 0) {
+ reg = 0x10;
+ reg |= (0xFF - p_264->loop_filter_beta) + 1;
+ } else {
+ reg = 0x00;
+ reg |= (p_264->loop_filter_beta & 0xF);
+ }
+ mfc_write(dev, reg, S5P_FIMV_ENC_BETA_OFF);
+ /* entropy coding mode */
+ if (p_264->entropy_mode == V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC)
+ mfc_write(dev, 1, S5P_FIMV_ENC_H264_ENTROPY_MODE);
+ else
+ mfc_write(dev, 0, S5P_FIMV_ENC_H264_ENTROPY_MODE);
+ /* number of ref. picture */
+ reg = mfc_read(dev, S5P_FIMV_ENC_H264_NUM_OF_REF);
+ /* num of ref. pictures of P */
+ reg &= ~(0x3 << 5);
+ reg |= (p_264->num_ref_pic_4p << 5);
+ /* max number of ref. pictures */
+ reg &= ~(0x1F);
+ reg |= p_264->max_ref_pic;
+ mfc_write(dev, reg, S5P_FIMV_ENC_H264_NUM_OF_REF);
+ /* 8x8 transform enable */
+ mfc_write(dev, p_264->_8x8_transform, S5P_FIMV_ENC_H264_TRANS_FLAG);
+ /* rate control config. */
+ reg = mfc_read(dev, S5P_FIMV_ENC_RC_CONFIG);
+ /* macroblock level rate control */
+ reg &= ~(0x1 << 8);
+ reg |= (p_264->rc_mb << 8);
+ /* frame QP */
+ reg &= ~(0x3F);
+ reg |= p_264->rc_frame_qp;
+ mfc_write(dev, reg, S5P_FIMV_ENC_RC_CONFIG);
+ /* frame rate */
+ if (p->rc_frame && p->rc_framerate_denom)
+ mfc_write(dev, p->rc_framerate_num * 1000
+ / p->rc_framerate_denom, S5P_FIMV_ENC_RC_FRAME_RATE);
+ else
+ mfc_write(dev, 0, S5P_FIMV_ENC_RC_FRAME_RATE);
+ /* max & min value of QP */
+ reg = mfc_read(dev, S5P_FIMV_ENC_RC_QBOUND);
+ /* max QP */
+ reg &= ~(0x3F << 8);
+ reg |= (p_264->rc_max_qp << 8);
+ /* min QP */
+ reg &= ~(0x3F);
+ reg |= p_264->rc_min_qp;
+ mfc_write(dev, reg, S5P_FIMV_ENC_RC_QBOUND);
+ /* macroblock adaptive scaling features */
+ if (p_264->rc_mb) {
+ reg = mfc_read(dev, S5P_FIMV_ENC_RC_MB_CTRL);
+ /* dark region */
+ reg &= ~(0x1 << 3);
+ reg |= (p_264->rc_mb_dark << 3);
+ /* smooth region */
+ reg &= ~(0x1 << 2);
+ reg |= (p_264->rc_mb_smooth << 2);
+ /* static region */
+ reg &= ~(0x1 << 1);
+ reg |= (p_264->rc_mb_static << 1);
+ /* high activity region */
+ reg &= ~(0x1);
+ reg |= p_264->rc_mb_activity;
+ mfc_write(dev, reg, S5P_FIMV_ENC_RC_MB_CTRL);
+ }
+ if (!p->rc_frame &&
+ !p_264->rc_mb) {
+ shm = s5p_mfc_read_shm(ctx, P_B_FRAME_QP);
+ shm &= ~(0xFFF);
+ shm |= ((p_264->rc_b_frame_qp & 0x3F) << 6);
+ shm |= (p_264->rc_p_frame_qp & 0x3F);
+ s5p_mfc_write_shm(ctx, shm, P_B_FRAME_QP);
+ }
+ /* extended encoder ctrl */
+ shm = s5p_mfc_read_shm(ctx, EXT_ENC_CONTROL);
+ /* AR VUI control */
+ shm &= ~(0x1 << 15);
+ shm |= (p_264->vui_sar << 1);
+ s5p_mfc_write_shm(ctx, shm, EXT_ENC_CONTROL);
+ if (p_264->vui_sar) {
+ /* aspect ration IDC */
+ shm = s5p_mfc_read_shm(ctx, SAMPLE_ASPECT_RATIO_IDC);
+ shm &= ~(0xFF);
+ shm |= p_264->vui_sar_idc;
+ s5p_mfc_write_shm(ctx, shm, SAMPLE_ASPECT_RATIO_IDC);
+ if (p_264->vui_sar_idc == 0xFF) {
+ /* sample AR info */
+ shm = s5p_mfc_read_shm(ctx, EXTENDED_SAR);
+ shm &= ~(0xFFFFFFFF);
+ shm |= p_264->vui_ext_sar_width << 16;
+ shm |= p_264->vui_ext_sar_height;
+ s5p_mfc_write_shm(ctx, shm, EXTENDED_SAR);
+ }
+ }
+ /* intra picture period for H.264 */
+ shm = s5p_mfc_read_shm(ctx, H264_I_PERIOD);
+ /* control */
+ shm &= ~(0x1 << 16);
+ shm |= (p_264->open_gop << 16);
+ /* value */
+ if (p_264->open_gop) {
+ shm &= ~(0xFFFF);
+ shm |= p_264->open_gop_size;
+ }
+ s5p_mfc_write_shm(ctx, shm, H264_I_PERIOD);
+ /* extended encoder ctrl */
+ shm = s5p_mfc_read_shm(ctx, EXT_ENC_CONTROL);
+ /* vbv buffer size */
+ if (p->frame_skip_mode ==
+ V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
+ shm &= ~(0xFFFF << 16);
+ shm |= (p_264->cpb_size << 16);
+ }
+ s5p_mfc_write_shm(ctx, shm, EXT_ENC_CONTROL);
+ return 0;
+}
+
+static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_enc_params *p = &ctx->enc_params;
+ struct s5p_mfc_mpeg4_enc_params *p_mpeg4 = &p->codec.mpeg4;
+ unsigned int reg;
+ unsigned int shm;
+ unsigned int framerate;
+
+ s5p_mfc_set_enc_params(ctx);
+ /* pictype : number of B */
+ reg = mfc_read(dev, S5P_FIMV_ENC_PIC_TYPE_CTRL);
+ /* num_b_frame - 0 ~ 2 */
+ reg &= ~(0x3 << 16);
+ reg |= (p->num_b_frame << 16);
+ mfc_write(dev, reg, S5P_FIMV_ENC_PIC_TYPE_CTRL);
+ /* profile & level */
+ reg = mfc_read(dev, S5P_FIMV_ENC_PROFILE);
+ /* level */
+ reg &= ~(0xFF << 8);
+ reg |= (p_mpeg4->level << 8);
+ /* profile - 0 ~ 2 */
+ reg &= ~(0x3F);
+ reg |= p_mpeg4->profile;
+ mfc_write(dev, reg, S5P_FIMV_ENC_PROFILE);
+ /* quarter_pixel */
+ mfc_write(dev, p_mpeg4->quarter_pixel, S5P_FIMV_ENC_MPEG4_QUART_PXL);
+ /* qp */
+ if (!p->rc_frame) {
+ shm = s5p_mfc_read_shm(ctx, P_B_FRAME_QP);
+ shm &= ~(0xFFF);
+ shm |= ((p_mpeg4->rc_b_frame_qp & 0x3F) << 6);
+ shm |= (p_mpeg4->rc_p_frame_qp & 0x3F);
+ s5p_mfc_write_shm(ctx, shm, P_B_FRAME_QP);
+ }
+ /* frame rate */
+ if (p->rc_frame) {
+ if (p->rc_framerate_denom > 0) {
+ framerate = p->rc_framerate_num * 1000 /
+ p->rc_framerate_denom;
+ mfc_write(dev, framerate,
+ S5P_FIMV_ENC_RC_FRAME_RATE);
+ shm = s5p_mfc_read_shm(ctx, RC_VOP_TIMING);
+ shm &= ~(0xFFFFFFFF);
+ shm |= (1 << 31);
+ shm |= ((p->rc_framerate_num & 0x7FFF) << 16);
+ shm |= (p->rc_framerate_denom & 0xFFFF);
+ s5p_mfc_write_shm(ctx, shm, RC_VOP_TIMING);
+ }
+ } else {
+ mfc_write(dev, 0, S5P_FIMV_ENC_RC_FRAME_RATE);
+ }
+ /* rate control config. */
+ reg = mfc_read(dev, S5P_FIMV_ENC_RC_CONFIG);
+ /* frame QP */
+ reg &= ~(0x3F);
+ reg |= p_mpeg4->rc_frame_qp;
+ mfc_write(dev, reg, S5P_FIMV_ENC_RC_CONFIG);
+ /* max & min value of QP */
+ reg = mfc_read(dev, S5P_FIMV_ENC_RC_QBOUND);
+ /* max QP */
+ reg &= ~(0x3F << 8);
+ reg |= (p_mpeg4->rc_max_qp << 8);
+ /* min QP */
+ reg &= ~(0x3F);
+ reg |= p_mpeg4->rc_min_qp;
+ mfc_write(dev, reg, S5P_FIMV_ENC_RC_QBOUND);
+ /* extended encoder ctrl */
+ shm = s5p_mfc_read_shm(ctx, EXT_ENC_CONTROL);
+ /* vbv buffer size */
+ if (p->frame_skip_mode ==
+ V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
+ shm &= ~(0xFFFF << 16);
+ shm |= (p->vbv_size << 16);
+ }
+ s5p_mfc_write_shm(ctx, shm, EXT_ENC_CONTROL);
+ return 0;
+}
+
+static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_enc_params *p = &ctx->enc_params;
+ struct s5p_mfc_mpeg4_enc_params *p_h263 = &p->codec.mpeg4;
+ unsigned int reg;
+ unsigned int shm;
+
+ s5p_mfc_set_enc_params(ctx);
+ /* qp */
+ if (!p->rc_frame) {
+ shm = s5p_mfc_read_shm(ctx, P_B_FRAME_QP);
+ shm &= ~(0xFFF);
+ shm |= (p_h263->rc_p_frame_qp & 0x3F);
+ s5p_mfc_write_shm(ctx, shm, P_B_FRAME_QP);
+ }
+ /* frame rate */
+ if (p->rc_frame && p->rc_framerate_denom)
+ mfc_write(dev, p->rc_framerate_num * 1000
+ / p->rc_framerate_denom, S5P_FIMV_ENC_RC_FRAME_RATE);
+ else
+ mfc_write(dev, 0, S5P_FIMV_ENC_RC_FRAME_RATE);
+ /* rate control config. */
+ reg = mfc_read(dev, S5P_FIMV_ENC_RC_CONFIG);
+ /* frame QP */
+ reg &= ~(0x3F);
+ reg |= p_h263->rc_frame_qp;
+ mfc_write(dev, reg, S5P_FIMV_ENC_RC_CONFIG);
+ /* max & min value of QP */
+ reg = mfc_read(dev, S5P_FIMV_ENC_RC_QBOUND);
+ /* max QP */
+ reg &= ~(0x3F << 8);
+ reg |= (p_h263->rc_max_qp << 8);
+ /* min QP */
+ reg &= ~(0x3F);
+ reg |= p_h263->rc_min_qp;
+ mfc_write(dev, reg, S5P_FIMV_ENC_RC_QBOUND);
+ /* extended encoder ctrl */
+ shm = s5p_mfc_read_shm(ctx, EXT_ENC_CONTROL);
+ /* vbv buffer size */
+ if (p->frame_skip_mode ==
+ V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
+ shm &= ~(0xFFFF << 16);
+ shm |= (p->vbv_size << 16);
+ }
+ s5p_mfc_write_shm(ctx, shm, EXT_ENC_CONTROL);
+ return 0;
+}
+
+/* Initialize decoding */
+int s5p_mfc_init_decode(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ s5p_mfc_set_shared_buffer(ctx);
+ /* Setup loop filter, for decoding this is only valid for MPEG4 */
+ if (ctx->codec_mode == S5P_FIMV_CODEC_MPEG4_DEC)
+ mfc_write(dev, ctx->loop_filter_mpeg4, S5P_FIMV_ENC_LF_CTRL);
+ else
+ mfc_write(dev, 0, S5P_FIMV_ENC_LF_CTRL);
+ mfc_write(dev, ((ctx->slice_interface & S5P_FIMV_SLICE_INT_MASK) <<
+ S5P_FIMV_SLICE_INT_SHIFT) | (ctx->display_delay_enable <<
+ S5P_FIMV_DDELAY_ENA_SHIFT) | ((ctx->display_delay &
+ S5P_FIMV_DDELAY_VAL_MASK) << S5P_FIMV_DDELAY_VAL_SHIFT),
+ S5P_FIMV_SI_CH0_DPB_CONF_CTRL);
+ mfc_write(dev,
+ ((S5P_FIMV_CH_SEQ_HEADER & S5P_FIMV_CH_MASK) << S5P_FIMV_CH_SHIFT)
+ | (ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID);
+ return 0;
+}
+
+static void s5p_mfc_set_flush(struct s5p_mfc_ctx *ctx, int flush)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned int dpb;
+
+ if (flush)
+ dpb = mfc_read(dev, S5P_FIMV_SI_CH0_DPB_CONF_CTRL) | (
+ S5P_FIMV_DPB_FLUSH_MASK << S5P_FIMV_DPB_FLUSH_SHIFT);
+ else
+ dpb = mfc_read(dev, S5P_FIMV_SI_CH0_DPB_CONF_CTRL) &
+ ~(S5P_FIMV_DPB_FLUSH_MASK << S5P_FIMV_DPB_FLUSH_SHIFT);
+ mfc_write(dev, dpb, S5P_FIMV_SI_CH0_DPB_CONF_CTRL);
+}
+
+/* Decode a single frame */
+int s5p_mfc_decode_one_frame(struct s5p_mfc_ctx *ctx,
+ enum s5p_mfc_decode_arg last_frame)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ mfc_write(dev, ctx->dec_dst_flag, S5P_FIMV_SI_CH0_RELEASE_BUF);
+ s5p_mfc_set_shared_buffer(ctx);
+ s5p_mfc_set_flush(ctx, ctx->dpb_flush_flag);
+ /* Issue different commands to instance basing on whether it
+ * is the last frame or not. */
+ switch (last_frame) {
+ case MFC_DEC_FRAME:
+ mfc_write(dev, ((S5P_FIMV_CH_FRAME_START & S5P_FIMV_CH_MASK) <<
+ S5P_FIMV_CH_SHIFT) | (ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID);
+ break;
+ case MFC_DEC_LAST_FRAME:
+ mfc_write(dev, ((S5P_FIMV_CH_LAST_FRAME & S5P_FIMV_CH_MASK) <<
+ S5P_FIMV_CH_SHIFT) | (ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID);
+ break;
+ case MFC_DEC_RES_CHANGE:
+ mfc_write(dev, ((S5P_FIMV_CH_FRAME_START_REALLOC &
+ S5P_FIMV_CH_MASK) << S5P_FIMV_CH_SHIFT) | (ctx->inst_no),
+ S5P_FIMV_SI_CH0_INST_ID);
+ break;
+ }
+ mfc_debug(2, "Decoding a usual frame\n");
+ return 0;
+}
+
+int s5p_mfc_init_encode(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ if (ctx->codec_mode == S5P_FIMV_CODEC_H264_ENC)
+ s5p_mfc_set_enc_params_h264(ctx);
+ else if (ctx->codec_mode == S5P_FIMV_CODEC_MPEG4_ENC)
+ s5p_mfc_set_enc_params_mpeg4(ctx);
+ else if (ctx->codec_mode == S5P_FIMV_CODEC_H263_ENC)
+ s5p_mfc_set_enc_params_h263(ctx);
+ else {
+ mfc_err("Unknown codec for encoding (%x)\n",
+ ctx->codec_mode);
+ return -EINVAL;
+ }
+ s5p_mfc_set_shared_buffer(ctx);
+ mfc_write(dev, ((S5P_FIMV_CH_SEQ_HEADER << 16) & 0x70000) |
+ (ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID);
+ return 0;
+}
+
+/* Encode a single frame */
+int s5p_mfc_encode_one_frame(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ /* memory structure cur. frame */
+ if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M)
+ mfc_write(dev, 0, S5P_FIMV_ENC_MAP_FOR_CUR);
+ else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12MT)
+ mfc_write(dev, 3, S5P_FIMV_ENC_MAP_FOR_CUR);
+ s5p_mfc_set_shared_buffer(ctx);
+ mfc_write(dev, (S5P_FIMV_CH_FRAME_START << 16 & 0x70000) |
+ (ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID);
+ return 0;
+}
+
+static int s5p_mfc_get_new_ctx(struct s5p_mfc_dev *dev)
+{
+ unsigned long flags;
+ int new_ctx;
+ int cnt;
+
+ spin_lock_irqsave(&dev->condlock, flags);
+ new_ctx = (dev->curr_ctx + 1) % MFC_NUM_CONTEXTS;
+ cnt = 0;
+ while (!test_bit(new_ctx, &dev->ctx_work_bits)) {
+ new_ctx = (new_ctx + 1) % MFC_NUM_CONTEXTS;
+ if (++cnt > MFC_NUM_CONTEXTS) {
+ /* No contexts to run */
+ spin_unlock_irqrestore(&dev->condlock, flags);
+ return -EAGAIN;
+ }
+ }
+ spin_unlock_irqrestore(&dev->condlock, flags);
+ return new_ctx;
+}
+
+static void s5p_mfc_run_res_change(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ s5p_mfc_set_dec_stream_buffer(ctx, 0, 0, 0);
+ dev->curr_ctx = ctx->num;
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_decode_one_frame(ctx, MFC_DEC_RES_CHANGE);
+}
+
+static int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx, int last_frame)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_buf *temp_vb;
+ unsigned long flags;
+ unsigned int index;
+
+ spin_lock_irqsave(&dev->irqlock, flags);
+ /* Frames are being decoded */
+ if (list_empty(&ctx->src_queue)) {
+ mfc_debug(2, "No src buffers\n");
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ return -EAGAIN;
+ }
+ /* Get the next source buffer */
+ temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
+ temp_vb->used = 1;
+ s5p_mfc_set_dec_stream_buffer(ctx,
+ vb2_dma_contig_plane_dma_addr(temp_vb->b, 0), ctx->consumed_stream,
+ temp_vb->b->v4l2_planes[0].bytesused);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ index = temp_vb->b->v4l2_buf.index;
+ dev->curr_ctx = ctx->num;
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ if (temp_vb->b->v4l2_planes[0].bytesused == 0) {
+ last_frame = MFC_DEC_LAST_FRAME;
+ mfc_debug(2, "Setting ctx->state to FINISHING\n");
+ ctx->state = MFCINST_FINISHING;
+ }
+ s5p_mfc_decode_one_frame(ctx, last_frame);
+ return 0;
+}
+
+static int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long flags;
+ struct s5p_mfc_buf *dst_mb;
+ struct s5p_mfc_buf *src_mb;
+ unsigned long src_y_addr, src_c_addr, dst_addr;
+ unsigned int dst_size;
+
+ spin_lock_irqsave(&dev->irqlock, flags);
+ if (list_empty(&ctx->src_queue)) {
+ mfc_debug(2, "no src buffers\n");
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ return -EAGAIN;
+ }
+ if (list_empty(&ctx->dst_queue)) {
+ mfc_debug(2, "no dst buffers\n");
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ return -EAGAIN;
+ }
+ src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
+ src_mb->used = 1;
+ src_y_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 0);
+ src_c_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 1);
+ s5p_mfc_set_enc_frame_buffer(ctx, src_y_addr, src_c_addr);
+ dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
+ dst_mb->used = 1;
+ dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0);
+ dst_size = vb2_plane_size(dst_mb->b, 0);
+ s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ dev->curr_ctx = ctx->num;
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_encode_one_frame(ctx);
+ return 0;
+}
+
+static void s5p_mfc_run_init_dec(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long flags;
+ struct s5p_mfc_buf *temp_vb;
+
+ /* Initializing decoding - parsing header */
+ spin_lock_irqsave(&dev->irqlock, flags);
+ mfc_debug(2, "Preparing to init decoding\n");
+ temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
+ s5p_mfc_set_dec_desc_buffer(ctx);
+ mfc_debug(2, "Header size: %d\n", temp_vb->b->v4l2_planes[0].bytesused);
+ s5p_mfc_set_dec_stream_buffer(ctx,
+ vb2_dma_contig_plane_dma_addr(temp_vb->b, 0),
+ 0, temp_vb->b->v4l2_planes[0].bytesused);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ dev->curr_ctx = ctx->num;
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_init_decode(ctx);
+}
+
+static void s5p_mfc_run_init_enc(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long flags;
+ struct s5p_mfc_buf *dst_mb;
+ unsigned long dst_addr;
+ unsigned int dst_size;
+
+ s5p_mfc_set_enc_ref_buffer(ctx);
+ spin_lock_irqsave(&dev->irqlock, flags);
+ dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
+ dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0);
+ dst_size = vb2_plane_size(dst_mb->b, 0);
+ s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ dev->curr_ctx = ctx->num;
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_init_encode(ctx);
+}
+
+static int s5p_mfc_run_init_dec_buffers(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long flags;
+ struct s5p_mfc_buf *temp_vb;
+ int ret;
+
+ /*
+ * Header was parsed now starting processing
+ * First set the output frame buffers
+ */
+ if (ctx->capture_state != QUEUE_BUFS_MMAPED) {
+ mfc_err("It seems that not all destionation buffers were "
+ "mmaped\nMFC requires that all destination are mmaped "
+ "before starting processing\n");
+ return -EAGAIN;
+ }
+ spin_lock_irqsave(&dev->irqlock, flags);
+ if (list_empty(&ctx->src_queue)) {
+ mfc_err("Header has been deallocated in the middle of"
+ " initialization\n");
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ return -EIO;
+ }
+ temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
+ mfc_debug(2, "Header size: %d\n", temp_vb->b->v4l2_planes[0].bytesused);
+ s5p_mfc_set_dec_stream_buffer(ctx,
+ vb2_dma_contig_plane_dma_addr(temp_vb->b, 0),
+ 0, temp_vb->b->v4l2_planes[0].bytesused);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ dev->curr_ctx = ctx->num;
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ ret = s5p_mfc_set_dec_frame_buffer(ctx);
+ if (ret) {
+ mfc_err("Failed to alloc frame mem\n");
+ ctx->state = MFCINST_ERROR;
+ }
+ return ret;
+}
+
+/* Try running an operation on hardware */
+void s5p_mfc_try_run(struct s5p_mfc_dev *dev)
+{
+ struct s5p_mfc_ctx *ctx;
+ int new_ctx;
+ unsigned int ret = 0;
+
+ if (test_bit(0, &dev->enter_suspend)) {
+ mfc_debug(1, "Entering suspend so do not schedule any jobs\n");
+ return;
+ }
+ /* Check whether hardware is not running */
+ if (test_and_set_bit(0, &dev->hw_lock) != 0) {
+ /* This is perfectly ok, the scheduled ctx should wait */
+ mfc_debug(1, "Couldn't lock HW\n");
+ return;
+ }
+ /* Choose the context to run */
+ new_ctx = s5p_mfc_get_new_ctx(dev);
+ if (new_ctx < 0) {
+ /* No contexts to run */
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0) {
+ mfc_err("Failed to unlock hardware\n");
+ return;
+ }
+ mfc_debug(1, "No ctx is scheduled to be run\n");
+ return;
+ }
+ ctx = dev->ctx[new_ctx];
+ /* Got context to run in ctx */
+ /*
+ * Last frame has already been sent to MFC.
+ * Now obtaining frames from MFC buffer
+ */
+ s5p_mfc_clock_on();
+ if (ctx->type == MFCINST_DECODER) {
+ s5p_mfc_set_dec_desc_buffer(ctx);
+ switch (ctx->state) {
+ case MFCINST_FINISHING:
+ s5p_mfc_run_dec_frame(ctx, MFC_DEC_LAST_FRAME);
+ break;
+ case MFCINST_RUNNING:
+ ret = s5p_mfc_run_dec_frame(ctx, MFC_DEC_FRAME);
+ break;
+ case MFCINST_INIT:
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ ret = s5p_mfc_open_inst_cmd(ctx);
+ break;
+ case MFCINST_RETURN_INST:
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ ret = s5p_mfc_close_inst_cmd(ctx);
+ break;
+ case MFCINST_GOT_INST:
+ s5p_mfc_run_init_dec(ctx);
+ break;
+ case MFCINST_HEAD_PARSED:
+ ret = s5p_mfc_run_init_dec_buffers(ctx);
+ mfc_debug(1, "head parsed\n");
+ break;
+ case MFCINST_RES_CHANGE_INIT:
+ s5p_mfc_run_res_change(ctx);
+ break;
+ case MFCINST_RES_CHANGE_FLUSH:
+ s5p_mfc_run_dec_frame(ctx, MFC_DEC_FRAME);
+ break;
+ case MFCINST_RES_CHANGE_END:
+ mfc_debug(2, "Finished remaining frames after resolution change\n");
+ ctx->capture_state = QUEUE_FREE;
+ mfc_debug(2, "Will re-init the codec\n");
+ s5p_mfc_run_init_dec(ctx);
+ break;
+ default:
+ ret = -EAGAIN;
+ }
+ } else if (ctx->type == MFCINST_ENCODER) {
+ switch (ctx->state) {
+ case MFCINST_FINISHING:
+ case MFCINST_RUNNING:
+ ret = s5p_mfc_run_enc_frame(ctx);
+ break;
+ case MFCINST_INIT:
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ ret = s5p_mfc_open_inst_cmd(ctx);
+ break;
+ case MFCINST_RETURN_INST:
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ ret = s5p_mfc_close_inst_cmd(ctx);
+ break;
+ case MFCINST_GOT_INST:
+ s5p_mfc_run_init_enc(ctx);
+ break;
+ default:
+ ret = -EAGAIN;
+ }
+ } else {
+ mfc_err("Invalid context type: %d\n", ctx->type);
+ ret = -EAGAIN;
+ }
+
+ if (ret) {
+ /* Free hardware lock */
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ mfc_err("Failed to unlock hardware\n");
+
+ /* This is in deed imporant, as no operation has been
+ * scheduled, reduce the clock count as no one will
+ * ever do this, because no interrupt related to this try_run
+ * will ever come from hardware. */
+ s5p_mfc_clock_off();
+ }
+}
+
+
+void s5p_mfc_cleanup_queue(struct list_head *lh, struct vb2_queue *vq)
+{
+ struct s5p_mfc_buf *b;
+ int i;
+
+ while (!list_empty(lh)) {
+ b = list_entry(lh->next, struct s5p_mfc_buf, list);
+ for (i = 0; i < b->b->num_planes; i++)
+ vb2_set_plane_payload(b->b, i, 0);
+ vb2_buffer_done(b->b, VB2_BUF_STATE_ERROR);
+ list_del(&b->list);
+ }
+}
+
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_opr.h b/drivers/media/video/s5p-mfc/s5p_mfc_opr.h
new file mode 100644
index 00000000..db83836e
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_opr.h
@@ -0,0 +1,91 @@
+/*
+ * drivers/media/video/samsung/mfc5/s5p_mfc_opr.h
+ *
+ * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
+ * Contains declarations of hw related functions.
+ *
+ * Kamil Debski, Copyright (C) 2011 Samsung Electronics
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef S5P_MFC_OPR_H_
+#define S5P_MFC_OPR_H_
+
+#include "s5p_mfc_common.h"
+
+int s5p_mfc_init_decode(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_init_encode(struct s5p_mfc_ctx *mfc_ctx);
+
+/* Decoding functions */
+int s5p_mfc_set_dec_frame_buffer(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_set_dec_stream_buffer(struct s5p_mfc_ctx *ctx, int buf_addr,
+ unsigned int start_num_byte,
+ unsigned int buf_size);
+
+/* Encoding functions */
+void s5p_mfc_set_enc_frame_buffer(struct s5p_mfc_ctx *ctx,
+ unsigned long y_addr, unsigned long c_addr);
+int s5p_mfc_set_enc_stream_buffer(struct s5p_mfc_ctx *ctx,
+ unsigned long addr, unsigned int size);
+void s5p_mfc_get_enc_frame_buffer(struct s5p_mfc_ctx *ctx,
+ unsigned long *y_addr, unsigned long *c_addr);
+int s5p_mfc_set_enc_ref_buffer(struct s5p_mfc_ctx *mfc_ctx);
+
+int s5p_mfc_decode_one_frame(struct s5p_mfc_ctx *ctx,
+ enum s5p_mfc_decode_arg last_frame);
+int s5p_mfc_encode_one_frame(struct s5p_mfc_ctx *mfc_ctx);
+
+/* Memory allocation */
+int s5p_mfc_alloc_dec_temp_buffers(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_set_dec_desc_buffer(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_release_dec_desc_buffer(struct s5p_mfc_ctx *ctx);
+
+int s5p_mfc_alloc_codec_buffers(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_release_codec_buffers(struct s5p_mfc_ctx *ctx);
+
+int s5p_mfc_alloc_instance_buffer(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_release_instance_buffer(struct s5p_mfc_ctx *ctx);
+
+void s5p_mfc_try_run(struct s5p_mfc_dev *dev);
+void s5p_mfc_cleanup_queue(struct list_head *lh, struct vb2_queue *vq);
+
+#define s5p_mfc_get_dspl_y_adr() (readl(dev->regs_base + \
+ S5P_FIMV_SI_DISPLAY_Y_ADR) << \
+ MFC_OFFSET_SHIFT)
+#define s5p_mfc_get_dec_y_adr() (readl(dev->regs_base + \
+ S5P_FIMV_SI_DISPLAY_Y_ADR) << \
+ MFC_OFFSET_SHIFT)
+#define s5p_mfc_get_dspl_status() readl(dev->regs_base + \
+ S5P_FIMV_SI_DISPLAY_STATUS)
+#define s5p_mfc_get_frame_type() (readl(dev->regs_base + \
+ S5P_FIMV_DECODE_FRAME_TYPE) \
+ & S5P_FIMV_DECODE_FRAME_MASK)
+#define s5p_mfc_get_consumed_stream() readl(dev->regs_base + \
+ S5P_FIMV_SI_CONSUMED_BYTES)
+#define s5p_mfc_get_int_reason() (readl(dev->regs_base + \
+ S5P_FIMV_RISC2HOST_CMD) & \
+ S5P_FIMV_RISC2HOST_CMD_MASK)
+#define s5p_mfc_get_int_err() readl(dev->regs_base + \
+ S5P_FIMV_RISC2HOST_ARG2)
+#define s5p_mfc_err_dec(x) (((x) & S5P_FIMV_ERR_DEC_MASK) >> \
+ S5P_FIMV_ERR_DEC_SHIFT)
+#define s5p_mfc_err_dspl(x) (((x) & S5P_FIMV_ERR_DSPL_MASK) >> \
+ S5P_FIMV_ERR_DSPL_SHIFT)
+#define s5p_mfc_get_img_width() readl(dev->regs_base + \
+ S5P_FIMV_SI_HRESOL)
+#define s5p_mfc_get_img_height() readl(dev->regs_base + \
+ S5P_FIMV_SI_VRESOL)
+#define s5p_mfc_get_dpb_count() readl(dev->regs_base + \
+ S5P_FIMV_SI_BUF_NUMBER)
+#define s5p_mfc_get_inst_no() readl(dev->regs_base + \
+ S5P_FIMV_RISC2HOST_ARG1)
+#define s5p_mfc_get_enc_strm_size() readl(dev->regs_base + \
+ S5P_FIMV_ENC_SI_STRM_SIZE)
+#define s5p_mfc_get_enc_slice_type() readl(dev->regs_base + \
+ S5P_FIMV_ENC_SI_SLICE_TYPE)
+
+#endif /* S5P_MFC_OPR_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_pm.c b/drivers/media/video/s5p-mfc/s5p_mfc_pm.c
new file mode 100644
index 00000000..738a607b
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_pm.c
@@ -0,0 +1,137 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_pm.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#ifdef CONFIG_PM_RUNTIME
+#include <linux/pm_runtime.h>
+#endif
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_debug.h"
+#include "s5p_mfc_pm.h"
+
+#define MFC_CLKNAME "sclk_mfc"
+#define MFC_GATE_CLK_NAME "mfc"
+
+#define CLK_DEBUG
+
+static struct s5p_mfc_pm *pm;
+static struct s5p_mfc_dev *p_dev;
+
+#ifdef CLK_DEBUG
+atomic_t clk_ref;
+#endif
+
+int s5p_mfc_init_pm(struct s5p_mfc_dev *dev)
+{
+ int ret = 0;
+
+ pm = &dev->pm;
+ p_dev = dev;
+ pm->clock_gate = clk_get(&dev->plat_dev->dev, MFC_GATE_CLK_NAME);
+ if (IS_ERR(pm->clock_gate)) {
+ mfc_err("Failed to get clock-gating control\n");
+ ret = PTR_ERR(pm->clock_gate);
+ goto err_g_ip_clk;
+ }
+
+ ret = clk_prepare(pm->clock_gate);
+ if (ret) {
+ mfc_err("Failed to preapre clock-gating control\n");
+ goto err_p_ip_clk;
+ }
+
+ pm->clock = clk_get(&dev->plat_dev->dev, MFC_CLKNAME);
+ if (IS_ERR(pm->clock)) {
+ mfc_err("Failed to get MFC clock\n");
+ ret = PTR_ERR(pm->clock);
+ goto err_g_ip_clk_2;
+ }
+
+ ret = clk_prepare(pm->clock);
+ if (ret) {
+ mfc_err("Failed to prepare MFC clock\n");
+ goto err_p_ip_clk_2;
+ }
+
+ atomic_set(&pm->power, 0);
+#ifdef CONFIG_PM_RUNTIME
+ pm->device = &dev->plat_dev->dev;
+ pm_runtime_enable(pm->device);
+#endif
+#ifdef CLK_DEBUG
+ atomic_set(&clk_ref, 0);
+#endif
+ return 0;
+err_p_ip_clk_2:
+ clk_put(pm->clock);
+err_g_ip_clk_2:
+ clk_unprepare(pm->clock_gate);
+err_p_ip_clk:
+ clk_put(pm->clock_gate);
+err_g_ip_clk:
+ return ret;
+}
+
+void s5p_mfc_final_pm(struct s5p_mfc_dev *dev)
+{
+ clk_unprepare(pm->clock_gate);
+ clk_put(pm->clock_gate);
+ clk_unprepare(pm->clock);
+ clk_put(pm->clock);
+#ifdef CONFIG_PM_RUNTIME
+ pm_runtime_disable(pm->device);
+#endif
+}
+
+int s5p_mfc_clock_on(void)
+{
+ int ret;
+#ifdef CLK_DEBUG
+ atomic_inc(&clk_ref);
+ mfc_debug(3, "+ %d", atomic_read(&clk_ref));
+#endif
+ ret = clk_enable(pm->clock_gate);
+ return ret;
+}
+
+void s5p_mfc_clock_off(void)
+{
+#ifdef CLK_DEBUG
+ atomic_dec(&clk_ref);
+ mfc_debug(3, "- %d", atomic_read(&clk_ref));
+#endif
+ clk_disable(pm->clock_gate);
+}
+
+int s5p_mfc_power_on(void)
+{
+#ifdef CONFIG_PM_RUNTIME
+ return pm_runtime_get_sync(pm->device);
+#else
+ atomic_set(&pm->power, 1);
+ return 0;
+#endif
+}
+
+int s5p_mfc_power_off(void)
+{
+#ifdef CONFIG_PM_RUNTIME
+ return pm_runtime_put_sync(pm->device);
+#else
+ atomic_set(&pm->power, 0);
+ return 0;
+#endif
+}
+
+
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_pm.h b/drivers/media/video/s5p-mfc/s5p_mfc_pm.h
new file mode 100644
index 00000000..5107914f
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_pm.h
@@ -0,0 +1,24 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_pm.h
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef S5P_MFC_PM_H_
+#define S5P_MFC_PM_H_
+
+int s5p_mfc_init_pm(struct s5p_mfc_dev *dev);
+void s5p_mfc_final_pm(struct s5p_mfc_dev *dev);
+
+int s5p_mfc_clock_on(void);
+void s5p_mfc_clock_off(void);
+int s5p_mfc_power_on(void);
+int s5p_mfc_power_off(void);
+
+#endif /* S5P_MFC_PM_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_shm.c b/drivers/media/video/s5p-mfc/s5p_mfc_shm.c
new file mode 100644
index 00000000..91fdbac8
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_shm.c
@@ -0,0 +1,47 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_shm.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifdef CONFIG_ARCH_EXYNOS4
+#include <linux/dma-mapping.h>
+#endif
+#include <linux/io.h>
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_debug.h"
+
+int s5p_mfc_init_shm(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ void *shm_alloc_ctx = dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
+
+ ctx->shm_alloc = vb2_dma_contig_memops.alloc(shm_alloc_ctx,
+ SHARED_BUF_SIZE);
+ if (IS_ERR(ctx->shm_alloc)) {
+ mfc_err("failed to allocate shared memory\n");
+ return PTR_ERR(ctx->shm_alloc);
+ }
+ /* shm_ofs only keeps the offset from base (port a) */
+ ctx->shm_ofs = s5p_mfc_mem_cookie(shm_alloc_ctx, ctx->shm_alloc)
+ - dev->bank1;
+ BUG_ON(ctx->shm_ofs & ((1 << MFC_BANK1_ALIGN_ORDER) - 1));
+ ctx->shm = vb2_dma_contig_memops.vaddr(ctx->shm_alloc);
+ if (!ctx->shm) {
+ vb2_dma_contig_memops.put(ctx->shm_alloc);
+ ctx->shm_ofs = 0;
+ ctx->shm_alloc = NULL;
+ mfc_err("failed to virt addr of shared memory\n");
+ return -ENOMEM;
+ }
+ memset((void *)ctx->shm, 0, SHARED_BUF_SIZE);
+ wmb();
+ return 0;
+}
+
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_shm.h b/drivers/media/video/s5p-mfc/s5p_mfc_shm.h
new file mode 100644
index 00000000..764eac6b
--- /dev/null
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_shm.h
@@ -0,0 +1,91 @@
+/*
+ * linux/drivers/media/video/s5p-mfc/s5p_mfc_shm.h
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef S5P_MFC_SHM_H_
+#define S5P_MFC_SHM_H_
+
+enum MFC_SHM_OFS
+{
+ EXTENEDED_DECODE_STATUS = 0x00, /* D */
+ SET_FRAME_TAG = 0x04, /* D */
+ GET_FRAME_TAG_TOP = 0x08, /* D */
+ GET_FRAME_TAG_BOT = 0x0C, /* D */
+ PIC_TIME_TOP = 0x10, /* D */
+ PIC_TIME_BOT = 0x14, /* D */
+ START_BYTE_NUM = 0x18, /* D */
+
+ CROP_INFO_H = 0x20, /* D */
+ CROP_INFO_V = 0x24, /* D */
+ EXT_ENC_CONTROL = 0x28, /* E */
+ ENC_PARAM_CHANGE = 0x2C, /* E */
+ RC_VOP_TIMING = 0x30, /* E, MPEG4 */
+ HEC_PERIOD = 0x34, /* E, MPEG4 */
+ METADATA_ENABLE = 0x38, /* C */
+ METADATA_STATUS = 0x3C, /* C */
+ METADATA_DISPLAY_INDEX = 0x40, /* C */
+ EXT_METADATA_START_ADDR = 0x44, /* C */
+ PUT_EXTRADATA = 0x48, /* C */
+ EXTRADATA_ADDR = 0x4C, /* C */
+
+ ALLOC_LUMA_DPB_SIZE = 0x64, /* D */
+ ALLOC_CHROMA_DPB_SIZE = 0x68, /* D */
+ ALLOC_MV_SIZE = 0x6C, /* D */
+ P_B_FRAME_QP = 0x70, /* E */
+ SAMPLE_ASPECT_RATIO_IDC = 0x74, /* E, H.264, depend on
+ ASPECT_RATIO_VUI_ENABLE in EXT_ENC_CONTROL */
+ EXTENDED_SAR = 0x78, /* E, H.264, depned on
+ ASPECT_RATIO_VUI_ENABLE in EXT_ENC_CONTROL */
+ DISP_PIC_PROFILE = 0x7C, /* D */
+ FLUSH_CMD_TYPE = 0x80, /* C */
+ FLUSH_CMD_INBUF1 = 0x84, /* C */
+ FLUSH_CMD_INBUF2 = 0x88, /* C */
+ FLUSH_CMD_OUTBUF = 0x8C, /* E */
+ NEW_RC_BIT_RATE = 0x90, /* E, format as RC_BIT_RATE(0xC5A8)
+ depend on RC_BIT_RATE_CHANGE in ENC_PARAM_CHANGE */
+ NEW_RC_FRAME_RATE = 0x94, /* E, format as RC_FRAME_RATE(0xD0D0)
+ depend on RC_FRAME_RATE_CHANGE in ENC_PARAM_CHANGE */
+ NEW_I_PERIOD = 0x98, /* E, format as I_FRM_CTRL(0xC504)
+ depend on I_PERIOD_CHANGE in ENC_PARAM_CHANGE */
+ H264_I_PERIOD = 0x9C, /* E, H.264, open GOP */
+ RC_CONTROL_CONFIG = 0xA0, /* E */
+ BATCH_INPUT_ADDR = 0xA4, /* E */
+ BATCH_OUTPUT_ADDR = 0xA8, /* E */
+ BATCH_OUTPUT_SIZE = 0xAC, /* E */
+ MIN_LUMA_DPB_SIZE = 0xB0, /* D */
+ DEVICE_FORMAT_ID = 0xB4, /* C */
+ H264_POC_TYPE = 0xB8, /* D */
+ MIN_CHROMA_DPB_SIZE = 0xBC, /* D */
+ DISP_PIC_FRAME_TYPE = 0xC0, /* D */
+ FREE_LUMA_DPB = 0xC4, /* D, VC1 MPEG4 */
+ ASPECT_RATIO_INFO = 0xC8, /* D, MPEG4 */
+ EXTENDED_PAR = 0xCC, /* D, MPEG4 */
+ DBG_HISTORY_INPUT0 = 0xD0, /* C */
+ DBG_HISTORY_INPUT1 = 0xD4, /* C */
+ DBG_HISTORY_OUTPUT = 0xD8, /* C */
+ HIERARCHICAL_P_QP = 0xE0, /* E, H.264 */
+};
+
+int s5p_mfc_init_shm(struct s5p_mfc_ctx *ctx);
+
+#define s5p_mfc_write_shm(ctx, x, ofs) \
+ do { \
+ writel(x, (ctx->shm + ofs)); \
+ wmb(); \
+ } while (0)
+
+static inline u32 s5p_mfc_read_shm(struct s5p_mfc_ctx *ctx, unsigned int ofs)
+{
+ rmb();
+ return readl(ctx->shm + ofs);
+}
+
+#endif /* S5P_MFC_SHM_H_ */
diff --git a/drivers/media/video/s5p-tv/Kconfig b/drivers/media/video/s5p-tv/Kconfig
new file mode 100644
index 00000000..f248b285
--- /dev/null
+++ b/drivers/media/video/s5p-tv/Kconfig
@@ -0,0 +1,86 @@
+# drivers/media/video/s5p-tv/Kconfig
+#
+# Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+# http://www.samsung.com/
+# Tomasz Stanislawski <t.stanislaws@samsung.com>
+#
+# Licensed under GPL
+
+config VIDEO_SAMSUNG_S5P_TV
+ bool "Samsung TV driver for S5P platform (experimental)"
+ depends on PLAT_S5P && PM_RUNTIME
+ depends on EXPERIMENTAL
+ default n
+ ---help---
+ Say Y here to enable selecting the TV output devices for
+ Samsung S5P platform.
+
+if VIDEO_SAMSUNG_S5P_TV
+
+config VIDEO_SAMSUNG_S5P_HDMI
+ tristate "Samsung HDMI Driver"
+ depends on VIDEO_V4L2
+ depends on VIDEO_SAMSUNG_S5P_TV
+ select VIDEO_SAMSUNG_S5P_HDMIPHY
+ help
+ Say Y here if you want support for the HDMI output
+ interface in S5P Samsung SoC. The driver can be compiled
+ as module. It is an auxiliary driver, that exposes a V4L2
+ subdev for use by other drivers. This driver requires
+ hdmiphy driver to work correctly.
+
+config VIDEO_SAMSUNG_S5P_HDMI_DEBUG
+ bool "Enable debug for HDMI Driver"
+ depends on VIDEO_SAMSUNG_S5P_HDMI
+ default n
+ help
+ Enables debugging for HDMI driver.
+
+config VIDEO_SAMSUNG_S5P_HDMIPHY
+ tristate "Samsung HDMIPHY Driver"
+ depends on VIDEO_DEV && VIDEO_V4L2 && I2C
+ depends on VIDEO_SAMSUNG_S5P_TV
+ help
+ Say Y here if you want support for the physical HDMI
+ interface in S5P Samsung SoC. The driver can be compiled
+ as module. It is an I2C driver, that exposes a V4L2
+ subdev for use by other drivers.
+
+config VIDEO_SAMSUNG_S5P_SII9234
+ tristate "Samsung SII9234 Driver"
+ depends on VIDEO_DEV && VIDEO_V4L2 && I2C
+ depends on VIDEO_SAMSUNG_S5P_TV
+ help
+ Say Y here if you want support for the MHL interface
+ in S5P Samsung SoC. The driver can be compiled
+ as module. It is an I2C driver, that exposes a V4L2
+ subdev for use by other drivers.
+
+config VIDEO_SAMSUNG_S5P_SDO
+ tristate "Samsung Analog TV Driver"
+ depends on VIDEO_DEV && VIDEO_V4L2
+ depends on VIDEO_SAMSUNG_S5P_TV
+ help
+ Say Y here if you want support for the analog TV output
+ interface in S5P Samsung SoC. The driver can be compiled
+ as module. It is an auxiliary driver, that exposes a V4L2
+ subdev for use by other drivers. This driver requires
+ hdmiphy driver to work correctly.
+
+config VIDEO_SAMSUNG_S5P_MIXER
+ tristate "Samsung Mixer and Video Processor Driver"
+ depends on VIDEO_DEV && VIDEO_V4L2
+ depends on VIDEO_SAMSUNG_S5P_TV
+ select VIDEOBUF2_DMA_CONTIG
+ help
+ Say Y here if you want support for the Mixer in Samsung S5P SoCs.
+ This device produce image data to one of output interfaces.
+
+config VIDEO_SAMSUNG_S5P_MIXER_DEBUG
+ bool "Enable debug for Mixer Driver"
+ depends on VIDEO_SAMSUNG_S5P_MIXER
+ default n
+ help
+ Enables debugging for Mixer driver.
+
+endif # VIDEO_SAMSUNG_S5P_TV
diff --git a/drivers/media/video/s5p-tv/Makefile b/drivers/media/video/s5p-tv/Makefile
new file mode 100644
index 00000000..f49e756a
--- /dev/null
+++ b/drivers/media/video/s5p-tv/Makefile
@@ -0,0 +1,19 @@
+# drivers/media/video/samsung/tvout/Makefile
+#
+# Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+# http://www.samsung.com/
+# Tomasz Stanislawski <t.stanislaws@samsung.com>
+#
+# Licensed under GPL
+
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_HDMIPHY) += s5p-hdmiphy.o
+s5p-hdmiphy-y += hdmiphy_drv.o
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_SII9234) += s5p-sii9234.o
+s5p-sii9234-y += sii9234_drv.o
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_HDMI) += s5p-hdmi.o
+s5p-hdmi-y += hdmi_drv.o
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_SDO) += s5p-sdo.o
+s5p-sdo-y += sdo_drv.o
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MIXER) += s5p-mixer.o
+s5p-mixer-y += mixer_drv.o mixer_video.o mixer_reg.o mixer_grp_layer.o mixer_vp_layer.o
+
diff --git a/drivers/media/video/s5p-tv/hdmi_drv.c b/drivers/media/video/s5p-tv/hdmi_drv.c
new file mode 100644
index 00000000..4865d25a
--- /dev/null
+++ b/drivers/media/video/s5p-tv/hdmi_drv.c
@@ -0,0 +1,1043 @@
+/*
+ * Samsung HDMI interface driver
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * Tomasz Stanislawski, <t.stanislaws@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundiation. either version 2 of the License,
+ * or (at your option) any later version
+ */
+
+#ifdef CONFIG_VIDEO_SAMSUNG_S5P_HDMI_DEBUG
+#define DEBUG
+#endif
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-subdev.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/bug.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+
+#include <media/s5p_hdmi.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+
+#include "regs-hdmi.h"
+
+MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>");
+MODULE_DESCRIPTION("Samsung HDMI");
+MODULE_LICENSE("GPL");
+
+/* default preset configured on probe */
+#define HDMI_DEFAULT_PRESET V4L2_DV_1080P60
+
+struct hdmi_resources {
+ struct clk *hdmi;
+ struct clk *sclk_hdmi;
+ struct clk *sclk_pixel;
+ struct clk *sclk_hdmiphy;
+ struct clk *hdmiphy;
+ struct regulator_bulk_data *regul_bulk;
+ int regul_count;
+};
+
+struct hdmi_device {
+ /** base address of HDMI registers */
+ void __iomem *regs;
+ /** HDMI interrupt */
+ unsigned int irq;
+ /** pointer to device parent */
+ struct device *dev;
+ /** subdev generated by HDMI device */
+ struct v4l2_subdev sd;
+ /** V4L2 device structure */
+ struct v4l2_device v4l2_dev;
+ /** subdev of HDMIPHY interface */
+ struct v4l2_subdev *phy_sd;
+ /** subdev of MHL interface */
+ struct v4l2_subdev *mhl_sd;
+ /** configuration of current graphic mode */
+ const struct hdmi_preset_conf *cur_conf;
+ /** current preset */
+ u32 cur_preset;
+ /** other resources */
+ struct hdmi_resources res;
+};
+
+struct hdmi_tg_regs {
+ u8 cmd;
+ u8 h_fsz_l;
+ u8 h_fsz_h;
+ u8 hact_st_l;
+ u8 hact_st_h;
+ u8 hact_sz_l;
+ u8 hact_sz_h;
+ u8 v_fsz_l;
+ u8 v_fsz_h;
+ u8 vsync_l;
+ u8 vsync_h;
+ u8 vsync2_l;
+ u8 vsync2_h;
+ u8 vact_st_l;
+ u8 vact_st_h;
+ u8 vact_sz_l;
+ u8 vact_sz_h;
+ u8 field_chg_l;
+ u8 field_chg_h;
+ u8 vact_st2_l;
+ u8 vact_st2_h;
+ u8 vsync_top_hdmi_l;
+ u8 vsync_top_hdmi_h;
+ u8 vsync_bot_hdmi_l;
+ u8 vsync_bot_hdmi_h;
+ u8 field_top_hdmi_l;
+ u8 field_top_hdmi_h;
+ u8 field_bot_hdmi_l;
+ u8 field_bot_hdmi_h;
+};
+
+struct hdmi_core_regs {
+ u8 h_blank[2];
+ u8 v_blank[3];
+ u8 h_v_line[3];
+ u8 vsync_pol[1];
+ u8 int_pro_mode[1];
+ u8 v_blank_f[3];
+ u8 h_sync_gen[3];
+ u8 v_sync_gen1[3];
+ u8 v_sync_gen2[3];
+ u8 v_sync_gen3[3];
+};
+
+struct hdmi_preset_conf {
+ struct hdmi_core_regs core;
+ struct hdmi_tg_regs tg;
+ struct v4l2_mbus_framefmt mbus_fmt;
+};
+
+static struct platform_device_id hdmi_driver_types[] = {
+ {
+ .name = "s5pv210-hdmi",
+ }, {
+ .name = "exynos4-hdmi",
+ }, {
+ /* end node */
+ }
+};
+
+static const struct v4l2_subdev_ops hdmi_sd_ops;
+
+static struct hdmi_device *sd_to_hdmi_dev(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct hdmi_device, sd);
+}
+
+static inline
+void hdmi_write(struct hdmi_device *hdev, u32 reg_id, u32 value)
+{
+ writel(value, hdev->regs + reg_id);
+}
+
+static inline
+void hdmi_write_mask(struct hdmi_device *hdev, u32 reg_id, u32 value, u32 mask)
+{
+ u32 old = readl(hdev->regs + reg_id);
+ value = (value & mask) | (old & ~mask);
+ writel(value, hdev->regs + reg_id);
+}
+
+static inline
+void hdmi_writeb(struct hdmi_device *hdev, u32 reg_id, u8 value)
+{
+ writeb(value, hdev->regs + reg_id);
+}
+
+static inline u32 hdmi_read(struct hdmi_device *hdev, u32 reg_id)
+{
+ return readl(hdev->regs + reg_id);
+}
+
+static irqreturn_t hdmi_irq_handler(int irq, void *dev_data)
+{
+ struct hdmi_device *hdev = dev_data;
+ u32 intc_flag;
+
+ (void)irq;
+ intc_flag = hdmi_read(hdev, HDMI_INTC_FLAG);
+ /* clearing flags for HPD plug/unplug */
+ if (intc_flag & HDMI_INTC_FLAG_HPD_UNPLUG) {
+ printk(KERN_INFO "unplugged\n");
+ hdmi_write_mask(hdev, HDMI_INTC_FLAG, ~0,
+ HDMI_INTC_FLAG_HPD_UNPLUG);
+ }
+ if (intc_flag & HDMI_INTC_FLAG_HPD_PLUG) {
+ printk(KERN_INFO "plugged\n");
+ hdmi_write_mask(hdev, HDMI_INTC_FLAG, ~0,
+ HDMI_INTC_FLAG_HPD_PLUG);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void hdmi_reg_init(struct hdmi_device *hdev)
+{
+ /* enable HPD interrupts */
+ hdmi_write_mask(hdev, HDMI_INTC_CON, ~0, HDMI_INTC_EN_GLOBAL |
+ HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
+ /* choose DVI mode */
+ hdmi_write_mask(hdev, HDMI_MODE_SEL,
+ HDMI_MODE_DVI_EN, HDMI_MODE_MASK);
+ hdmi_write_mask(hdev, HDMI_CON_2, ~0,
+ HDMI_DVI_PERAMBLE_EN | HDMI_DVI_BAND_EN);
+ /* disable bluescreen */
+ hdmi_write_mask(hdev, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
+ /* choose bluescreen (fecal) color */
+ hdmi_writeb(hdev, HDMI_BLUE_SCREEN_0, 0x12);
+ hdmi_writeb(hdev, HDMI_BLUE_SCREEN_1, 0x34);
+ hdmi_writeb(hdev, HDMI_BLUE_SCREEN_2, 0x56);
+}
+
+static void hdmi_timing_apply(struct hdmi_device *hdev,
+ const struct hdmi_preset_conf *conf)
+{
+ const struct hdmi_core_regs *core = &conf->core;
+ const struct hdmi_tg_regs *tg = &conf->tg;
+
+ /* setting core registers */
+ hdmi_writeb(hdev, HDMI_H_BLANK_0, core->h_blank[0]);
+ hdmi_writeb(hdev, HDMI_H_BLANK_1, core->h_blank[1]);
+ hdmi_writeb(hdev, HDMI_V_BLANK_0, core->v_blank[0]);
+ hdmi_writeb(hdev, HDMI_V_BLANK_1, core->v_blank[1]);
+ hdmi_writeb(hdev, HDMI_V_BLANK_2, core->v_blank[2]);
+ hdmi_writeb(hdev, HDMI_H_V_LINE_0, core->h_v_line[0]);
+ hdmi_writeb(hdev, HDMI_H_V_LINE_1, core->h_v_line[1]);
+ hdmi_writeb(hdev, HDMI_H_V_LINE_2, core->h_v_line[2]);
+ hdmi_writeb(hdev, HDMI_VSYNC_POL, core->vsync_pol[0]);
+ hdmi_writeb(hdev, HDMI_INT_PRO_MODE, core->int_pro_mode[0]);
+ hdmi_writeb(hdev, HDMI_V_BLANK_F_0, core->v_blank_f[0]);
+ hdmi_writeb(hdev, HDMI_V_BLANK_F_1, core->v_blank_f[1]);
+ hdmi_writeb(hdev, HDMI_V_BLANK_F_2, core->v_blank_f[2]);
+ hdmi_writeb(hdev, HDMI_H_SYNC_GEN_0, core->h_sync_gen[0]);
+ hdmi_writeb(hdev, HDMI_H_SYNC_GEN_1, core->h_sync_gen[1]);
+ hdmi_writeb(hdev, HDMI_H_SYNC_GEN_2, core->h_sync_gen[2]);
+ hdmi_writeb(hdev, HDMI_V_SYNC_GEN_1_0, core->v_sync_gen1[0]);
+ hdmi_writeb(hdev, HDMI_V_SYNC_GEN_1_1, core->v_sync_gen1[1]);
+ hdmi_writeb(hdev, HDMI_V_SYNC_GEN_1_2, core->v_sync_gen1[2]);
+ hdmi_writeb(hdev, HDMI_V_SYNC_GEN_2_0, core->v_sync_gen2[0]);
+ hdmi_writeb(hdev, HDMI_V_SYNC_GEN_2_1, core->v_sync_gen2[1]);
+ hdmi_writeb(hdev, HDMI_V_SYNC_GEN_2_2, core->v_sync_gen2[2]);
+ hdmi_writeb(hdev, HDMI_V_SYNC_GEN_3_0, core->v_sync_gen3[0]);
+ hdmi_writeb(hdev, HDMI_V_SYNC_GEN_3_1, core->v_sync_gen3[1]);
+ hdmi_writeb(hdev, HDMI_V_SYNC_GEN_3_2, core->v_sync_gen3[2]);
+ /* Timing generator registers */
+ hdmi_writeb(hdev, HDMI_TG_H_FSZ_L, tg->h_fsz_l);
+ hdmi_writeb(hdev, HDMI_TG_H_FSZ_H, tg->h_fsz_h);
+ hdmi_writeb(hdev, HDMI_TG_HACT_ST_L, tg->hact_st_l);
+ hdmi_writeb(hdev, HDMI_TG_HACT_ST_H, tg->hact_st_h);
+ hdmi_writeb(hdev, HDMI_TG_HACT_SZ_L, tg->hact_sz_l);
+ hdmi_writeb(hdev, HDMI_TG_HACT_SZ_H, tg->hact_sz_h);
+ hdmi_writeb(hdev, HDMI_TG_V_FSZ_L, tg->v_fsz_l);
+ hdmi_writeb(hdev, HDMI_TG_V_FSZ_H, tg->v_fsz_h);
+ hdmi_writeb(hdev, HDMI_TG_VSYNC_L, tg->vsync_l);
+ hdmi_writeb(hdev, HDMI_TG_VSYNC_H, tg->vsync_h);
+ hdmi_writeb(hdev, HDMI_TG_VSYNC2_L, tg->vsync2_l);
+ hdmi_writeb(hdev, HDMI_TG_VSYNC2_H, tg->vsync2_h);
+ hdmi_writeb(hdev, HDMI_TG_VACT_ST_L, tg->vact_st_l);
+ hdmi_writeb(hdev, HDMI_TG_VACT_ST_H, tg->vact_st_h);
+ hdmi_writeb(hdev, HDMI_TG_VACT_SZ_L, tg->vact_sz_l);
+ hdmi_writeb(hdev, HDMI_TG_VACT_SZ_H, tg->vact_sz_h);
+ hdmi_writeb(hdev, HDMI_TG_FIELD_CHG_L, tg->field_chg_l);
+ hdmi_writeb(hdev, HDMI_TG_FIELD_CHG_H, tg->field_chg_h);
+ hdmi_writeb(hdev, HDMI_TG_VACT_ST2_L, tg->vact_st2_l);
+ hdmi_writeb(hdev, HDMI_TG_VACT_ST2_H, tg->vact_st2_h);
+ hdmi_writeb(hdev, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi_l);
+ hdmi_writeb(hdev, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi_h);
+ hdmi_writeb(hdev, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi_l);
+ hdmi_writeb(hdev, HDMI_TG_VSYNC_BOT_HDMI_H, tg->vsync_bot_hdmi_h);
+ hdmi_writeb(hdev, HDMI_TG_FIELD_TOP_HDMI_L, tg->field_top_hdmi_l);
+ hdmi_writeb(hdev, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi_h);
+ hdmi_writeb(hdev, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi_l);
+ hdmi_writeb(hdev, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi_h);
+}
+
+static int hdmi_conf_apply(struct hdmi_device *hdmi_dev)
+{
+ struct device *dev = hdmi_dev->dev;
+ const struct hdmi_preset_conf *conf = hdmi_dev->cur_conf;
+ struct v4l2_dv_preset preset;
+ int ret;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ /* reset hdmiphy */
+ hdmi_write_mask(hdmi_dev, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT);
+ mdelay(10);
+ hdmi_write_mask(hdmi_dev, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT);
+ mdelay(10);
+
+ /* configure presets */
+ preset.preset = hdmi_dev->cur_preset;
+ ret = v4l2_subdev_call(hdmi_dev->phy_sd, video, s_dv_preset, &preset);
+ if (ret) {
+ dev_err(dev, "failed to set preset (%u)\n", preset.preset);
+ return ret;
+ }
+
+ /* resetting HDMI core */
+ hdmi_write_mask(hdmi_dev, HDMI_CORE_RSTOUT, 0, HDMI_CORE_SW_RSTOUT);
+ mdelay(10);
+ hdmi_write_mask(hdmi_dev, HDMI_CORE_RSTOUT, ~0, HDMI_CORE_SW_RSTOUT);
+ mdelay(10);
+
+ hdmi_reg_init(hdmi_dev);
+
+ /* setting core registers */
+ hdmi_timing_apply(hdmi_dev, conf);
+
+ return 0;
+}
+
+static void hdmi_dumpregs(struct hdmi_device *hdev, char *prefix)
+{
+#define DUMPREG(reg_id) \
+ dev_dbg(hdev->dev, "%s:" #reg_id " = %08x\n", prefix, \
+ readl(hdev->regs + reg_id))
+
+ dev_dbg(hdev->dev, "%s: ---- CONTROL REGISTERS ----\n", prefix);
+ DUMPREG(HDMI_INTC_FLAG);
+ DUMPREG(HDMI_INTC_CON);
+ DUMPREG(HDMI_HPD_STATUS);
+ DUMPREG(HDMI_PHY_RSTOUT);
+ DUMPREG(HDMI_PHY_VPLL);
+ DUMPREG(HDMI_PHY_CMU);
+ DUMPREG(HDMI_CORE_RSTOUT);
+
+ dev_dbg(hdev->dev, "%s: ---- CORE REGISTERS ----\n", prefix);
+ DUMPREG(HDMI_CON_0);
+ DUMPREG(HDMI_CON_1);
+ DUMPREG(HDMI_CON_2);
+ DUMPREG(HDMI_SYS_STATUS);
+ DUMPREG(HDMI_PHY_STATUS);
+ DUMPREG(HDMI_STATUS_EN);
+ DUMPREG(HDMI_HPD);
+ DUMPREG(HDMI_MODE_SEL);
+ DUMPREG(HDMI_HPD_GEN);
+ DUMPREG(HDMI_DC_CONTROL);
+ DUMPREG(HDMI_VIDEO_PATTERN_GEN);
+
+ dev_dbg(hdev->dev, "%s: ---- CORE SYNC REGISTERS ----\n", prefix);
+ DUMPREG(HDMI_H_BLANK_0);
+ DUMPREG(HDMI_H_BLANK_1);
+ DUMPREG(HDMI_V_BLANK_0);
+ DUMPREG(HDMI_V_BLANK_1);
+ DUMPREG(HDMI_V_BLANK_2);
+ DUMPREG(HDMI_H_V_LINE_0);
+ DUMPREG(HDMI_H_V_LINE_1);
+ DUMPREG(HDMI_H_V_LINE_2);
+ DUMPREG(HDMI_VSYNC_POL);
+ DUMPREG(HDMI_INT_PRO_MODE);
+ DUMPREG(HDMI_V_BLANK_F_0);
+ DUMPREG(HDMI_V_BLANK_F_1);
+ DUMPREG(HDMI_V_BLANK_F_2);
+ DUMPREG(HDMI_H_SYNC_GEN_0);
+ DUMPREG(HDMI_H_SYNC_GEN_1);
+ DUMPREG(HDMI_H_SYNC_GEN_2);
+ DUMPREG(HDMI_V_SYNC_GEN_1_0);
+ DUMPREG(HDMI_V_SYNC_GEN_1_1);
+ DUMPREG(HDMI_V_SYNC_GEN_1_2);
+ DUMPREG(HDMI_V_SYNC_GEN_2_0);
+ DUMPREG(HDMI_V_SYNC_GEN_2_1);
+ DUMPREG(HDMI_V_SYNC_GEN_2_2);
+ DUMPREG(HDMI_V_SYNC_GEN_3_0);
+ DUMPREG(HDMI_V_SYNC_GEN_3_1);
+ DUMPREG(HDMI_V_SYNC_GEN_3_2);
+
+ dev_dbg(hdev->dev, "%s: ---- TG REGISTERS ----\n", prefix);
+ DUMPREG(HDMI_TG_CMD);
+ DUMPREG(HDMI_TG_H_FSZ_L);
+ DUMPREG(HDMI_TG_H_FSZ_H);
+ DUMPREG(HDMI_TG_HACT_ST_L);
+ DUMPREG(HDMI_TG_HACT_ST_H);
+ DUMPREG(HDMI_TG_HACT_SZ_L);
+ DUMPREG(HDMI_TG_HACT_SZ_H);
+ DUMPREG(HDMI_TG_V_FSZ_L);
+ DUMPREG(HDMI_TG_V_FSZ_H);
+ DUMPREG(HDMI_TG_VSYNC_L);
+ DUMPREG(HDMI_TG_VSYNC_H);
+ DUMPREG(HDMI_TG_VSYNC2_L);
+ DUMPREG(HDMI_TG_VSYNC2_H);
+ DUMPREG(HDMI_TG_VACT_ST_L);
+ DUMPREG(HDMI_TG_VACT_ST_H);
+ DUMPREG(HDMI_TG_VACT_SZ_L);
+ DUMPREG(HDMI_TG_VACT_SZ_H);
+ DUMPREG(HDMI_TG_FIELD_CHG_L);
+ DUMPREG(HDMI_TG_FIELD_CHG_H);
+ DUMPREG(HDMI_TG_VACT_ST2_L);
+ DUMPREG(HDMI_TG_VACT_ST2_H);
+ DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_L);
+ DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_H);
+ DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_L);
+ DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_H);
+ DUMPREG(HDMI_TG_FIELD_TOP_HDMI_L);
+ DUMPREG(HDMI_TG_FIELD_TOP_HDMI_H);
+ DUMPREG(HDMI_TG_FIELD_BOT_HDMI_L);
+ DUMPREG(HDMI_TG_FIELD_BOT_HDMI_H);
+#undef DUMPREG
+}
+
+static const struct hdmi_preset_conf hdmi_conf_480p = {
+ .core = {
+ .h_blank = {0x8a, 0x00},
+ .v_blank = {0x0d, 0x6a, 0x01},
+ .h_v_line = {0x0d, 0xa2, 0x35},
+ .vsync_pol = {0x01},
+ .int_pro_mode = {0x00},
+ .v_blank_f = {0x00, 0x00, 0x00},
+ .h_sync_gen = {0x0e, 0x30, 0x11},
+ .v_sync_gen1 = {0x0f, 0x90, 0x00},
+ /* other don't care */
+ },
+ .tg = {
+ 0x00, /* cmd */
+ 0x5a, 0x03, /* h_fsz */
+ 0x8a, 0x00, 0xd0, 0x02, /* hact */
+ 0x0d, 0x02, /* v_fsz */
+ 0x01, 0x00, 0x33, 0x02, /* vsync */
+ 0x2d, 0x00, 0xe0, 0x01, /* vact */
+ 0x33, 0x02, /* field_chg */
+ 0x49, 0x02, /* vact_st2 */
+ 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */
+ 0x01, 0x00, 0x33, 0x02, /* field top/bot */
+ },
+ .mbus_fmt = {
+ .width = 720,
+ .height = 480,
+ .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ },
+};
+
+static const struct hdmi_preset_conf hdmi_conf_720p60 = {
+ .core = {
+ .h_blank = {0x72, 0x01},
+ .v_blank = {0xee, 0xf2, 0x00},
+ .h_v_line = {0xee, 0x22, 0x67},
+ .vsync_pol = {0x00},
+ .int_pro_mode = {0x00},
+ .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */
+ .h_sync_gen = {0x6c, 0x50, 0x02},
+ .v_sync_gen1 = {0x0a, 0x50, 0x00},
+ /* other don't care */
+ },
+ .tg = {
+ 0x00, /* cmd */
+ 0x72, 0x06, /* h_fsz */
+ 0x72, 0x01, 0x00, 0x05, /* hact */
+ 0xee, 0x02, /* v_fsz */
+ 0x01, 0x00, 0x33, 0x02, /* vsync */
+ 0x1e, 0x00, 0xd0, 0x02, /* vact */
+ 0x33, 0x02, /* field_chg */
+ 0x49, 0x02, /* vact_st2 */
+ 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */
+ 0x01, 0x00, 0x33, 0x02, /* field top/bot */
+ },
+ .mbus_fmt = {
+ .width = 1280,
+ .height = 720,
+ .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ },
+};
+
+static const struct hdmi_preset_conf hdmi_conf_1080p50 = {
+ .core = {
+ .h_blank = {0xd0, 0x02},
+ .v_blank = {0x65, 0x6c, 0x01},
+ .h_v_line = {0x65, 0x04, 0xa5},
+ .vsync_pol = {0x00},
+ .int_pro_mode = {0x00},
+ .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */
+ .h_sync_gen = {0x0e, 0xea, 0x08},
+ .v_sync_gen1 = {0x09, 0x40, 0x00},
+ /* other don't care */
+ },
+ .tg = {
+ 0x00, /* cmd */
+ 0x98, 0x08, /* h_fsz */
+ 0x18, 0x01, 0x80, 0x07, /* hact */
+ 0x65, 0x04, /* v_fsz */
+ 0x01, 0x00, 0x33, 0x02, /* vsync */
+ 0x2d, 0x00, 0x38, 0x04, /* vact */
+ 0x33, 0x02, /* field_chg */
+ 0x49, 0x02, /* vact_st2 */
+ 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */
+ 0x01, 0x00, 0x33, 0x02, /* field top/bot */
+ },
+ .mbus_fmt = {
+ .width = 1920,
+ .height = 1080,
+ .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ },
+};
+
+static const struct hdmi_preset_conf hdmi_conf_1080p60 = {
+ .core = {
+ .h_blank = {0x18, 0x01},
+ .v_blank = {0x65, 0x6c, 0x01},
+ .h_v_line = {0x65, 0x84, 0x89},
+ .vsync_pol = {0x00},
+ .int_pro_mode = {0x00},
+ .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */
+ .h_sync_gen = {0x56, 0x08, 0x02},
+ .v_sync_gen1 = {0x09, 0x40, 0x00},
+ /* other don't care */
+ },
+ .tg = {
+ 0x00, /* cmd */
+ 0x98, 0x08, /* h_fsz */
+ 0x18, 0x01, 0x80, 0x07, /* hact */
+ 0x65, 0x04, /* v_fsz */
+ 0x01, 0x00, 0x33, 0x02, /* vsync */
+ 0x2d, 0x00, 0x38, 0x04, /* vact */
+ 0x33, 0x02, /* field_chg */
+ 0x48, 0x02, /* vact_st2 */
+ 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
+ 0x01, 0x00, 0x33, 0x02, /* field top/bot */
+ },
+ .mbus_fmt = {
+ .width = 1920,
+ .height = 1080,
+ .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ },
+};
+
+static const struct {
+ u32 preset;
+ const struct hdmi_preset_conf *conf;
+} hdmi_conf[] = {
+ { V4L2_DV_480P59_94, &hdmi_conf_480p },
+ { V4L2_DV_720P59_94, &hdmi_conf_720p60 },
+ { V4L2_DV_1080P50, &hdmi_conf_1080p50 },
+ { V4L2_DV_1080P30, &hdmi_conf_1080p60 },
+ { V4L2_DV_1080P60, &hdmi_conf_1080p60 },
+};
+
+static const struct hdmi_preset_conf *hdmi_preset2conf(u32 preset)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hdmi_conf); ++i)
+ if (hdmi_conf[i].preset == preset)
+ return hdmi_conf[i].conf;
+ return NULL;
+}
+
+static int hdmi_streamon(struct hdmi_device *hdev)
+{
+ struct device *dev = hdev->dev;
+ struct hdmi_resources *res = &hdev->res;
+ int ret, tries;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ ret = v4l2_subdev_call(hdev->phy_sd, video, s_stream, 1);
+ if (ret)
+ return ret;
+
+ /* waiting for HDMIPHY's PLL to get to steady state */
+ for (tries = 100; tries; --tries) {
+ u32 val = hdmi_read(hdev, HDMI_PHY_STATUS);
+ if (val & HDMI_PHY_STATUS_READY)
+ break;
+ mdelay(1);
+ }
+ /* steady state not achieved */
+ if (tries == 0) {
+ dev_err(dev, "hdmiphy's pll could not reach steady state.\n");
+ v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0);
+ hdmi_dumpregs(hdev, "hdmiphy - s_stream");
+ return -EIO;
+ }
+
+ /* starting MHL */
+ ret = v4l2_subdev_call(hdev->mhl_sd, video, s_stream, 1);
+ if (hdev->mhl_sd && ret) {
+ v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0);
+ hdmi_dumpregs(hdev, "mhl - s_stream");
+ return -EIO;
+ }
+
+ /* hdmiphy clock is used for HDMI in streaming mode */
+ clk_disable(res->sclk_hdmi);
+ clk_set_parent(res->sclk_hdmi, res->sclk_hdmiphy);
+ clk_enable(res->sclk_hdmi);
+
+ /* enable HDMI and timing generator */
+ hdmi_write_mask(hdev, HDMI_CON_0, ~0, HDMI_EN);
+ hdmi_write_mask(hdev, HDMI_TG_CMD, ~0, HDMI_TG_EN);
+ hdmi_dumpregs(hdev, "streamon");
+ return 0;
+}
+
+static int hdmi_streamoff(struct hdmi_device *hdev)
+{
+ struct device *dev = hdev->dev;
+ struct hdmi_resources *res = &hdev->res;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ hdmi_write_mask(hdev, HDMI_CON_0, 0, HDMI_EN);
+ hdmi_write_mask(hdev, HDMI_TG_CMD, 0, HDMI_TG_EN);
+
+ /* pixel(vpll) clock is used for HDMI in config mode */
+ clk_disable(res->sclk_hdmi);
+ clk_set_parent(res->sclk_hdmi, res->sclk_pixel);
+ clk_enable(res->sclk_hdmi);
+
+ v4l2_subdev_call(hdev->mhl_sd, video, s_stream, 0);
+ v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0);
+
+ hdmi_dumpregs(hdev, "streamoff");
+ return 0;
+}
+
+static int hdmi_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
+ struct device *dev = hdev->dev;
+
+ dev_dbg(dev, "%s(%d)\n", __func__, enable);
+ if (enable)
+ return hdmi_streamon(hdev);
+ return hdmi_streamoff(hdev);
+}
+
+static void hdmi_resource_poweron(struct hdmi_resources *res)
+{
+ /* turn HDMI power on */
+ regulator_bulk_enable(res->regul_count, res->regul_bulk);
+ /* power-on hdmi physical interface */
+ clk_enable(res->hdmiphy);
+ /* use VPP as parent clock; HDMIPHY is not working yet */
+ clk_set_parent(res->sclk_hdmi, res->sclk_pixel);
+ /* turn clocks on */
+ clk_enable(res->sclk_hdmi);
+}
+
+static void hdmi_resource_poweroff(struct hdmi_resources *res)
+{
+ /* turn clocks off */
+ clk_disable(res->sclk_hdmi);
+ /* power-off hdmiphy */
+ clk_disable(res->hdmiphy);
+ /* turn HDMI power off */
+ regulator_bulk_disable(res->regul_count, res->regul_bulk);
+}
+
+static int hdmi_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
+ int ret;
+
+ if (on)
+ ret = pm_runtime_get_sync(hdev->dev);
+ else
+ ret = pm_runtime_put_sync(hdev->dev);
+ /* only values < 0 indicate errors */
+ return IS_ERR_VALUE(ret) ? ret : 0;
+}
+
+static int hdmi_s_dv_preset(struct v4l2_subdev *sd,
+ struct v4l2_dv_preset *preset)
+{
+ struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
+ struct device *dev = hdev->dev;
+ const struct hdmi_preset_conf *conf;
+
+ conf = hdmi_preset2conf(preset->preset);
+ if (conf == NULL) {
+ dev_err(dev, "preset (%u) not supported\n", preset->preset);
+ return -EINVAL;
+ }
+ hdev->cur_conf = conf;
+ hdev->cur_preset = preset->preset;
+ return 0;
+}
+
+static int hdmi_g_dv_preset(struct v4l2_subdev *sd,
+ struct v4l2_dv_preset *preset)
+{
+ memset(preset, 0, sizeof(*preset));
+ preset->preset = sd_to_hdmi_dev(sd)->cur_preset;
+ return 0;
+}
+
+static int hdmi_g_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
+ struct device *dev = hdev->dev;
+
+ dev_dbg(dev, "%s\n", __func__);
+ if (!hdev->cur_conf)
+ return -EINVAL;
+ *fmt = hdev->cur_conf->mbus_fmt;
+ return 0;
+}
+
+static int hdmi_enum_dv_presets(struct v4l2_subdev *sd,
+ struct v4l2_dv_enum_preset *preset)
+{
+ if (preset->index >= ARRAY_SIZE(hdmi_conf))
+ return -EINVAL;
+ return v4l_fill_dv_preset_info(hdmi_conf[preset->index].preset, preset);
+}
+
+static const struct v4l2_subdev_core_ops hdmi_sd_core_ops = {
+ .s_power = hdmi_s_power,
+};
+
+static const struct v4l2_subdev_video_ops hdmi_sd_video_ops = {
+ .s_dv_preset = hdmi_s_dv_preset,
+ .g_dv_preset = hdmi_g_dv_preset,
+ .enum_dv_presets = hdmi_enum_dv_presets,
+ .g_mbus_fmt = hdmi_g_mbus_fmt,
+ .s_stream = hdmi_s_stream,
+};
+
+static const struct v4l2_subdev_ops hdmi_sd_ops = {
+ .core = &hdmi_sd_core_ops,
+ .video = &hdmi_sd_video_ops,
+};
+
+static int hdmi_runtime_suspend(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
+
+ dev_dbg(dev, "%s\n", __func__);
+ v4l2_subdev_call(hdev->mhl_sd, core, s_power, 0);
+ hdmi_resource_poweroff(&hdev->res);
+ return 0;
+}
+
+static int hdmi_runtime_resume(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
+ int ret = 0;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ hdmi_resource_poweron(&hdev->res);
+
+ ret = hdmi_conf_apply(hdev);
+ if (ret)
+ goto fail;
+
+ /* starting MHL */
+ ret = v4l2_subdev_call(hdev->mhl_sd, core, s_power, 1);
+ if (hdev->mhl_sd && ret)
+ goto fail;
+
+ dev_dbg(dev, "poweron succeed\n");
+
+ return 0;
+
+fail:
+ hdmi_resource_poweroff(&hdev->res);
+ dev_err(dev, "poweron failed\n");
+
+ return ret;
+}
+
+static const struct dev_pm_ops hdmi_pm_ops = {
+ .runtime_suspend = hdmi_runtime_suspend,
+ .runtime_resume = hdmi_runtime_resume,
+};
+
+static void hdmi_resources_cleanup(struct hdmi_device *hdev)
+{
+ struct hdmi_resources *res = &hdev->res;
+
+ dev_dbg(hdev->dev, "HDMI resource cleanup\n");
+ /* put clocks, power */
+ if (res->regul_count)
+ regulator_bulk_free(res->regul_count, res->regul_bulk);
+ /* kfree is NULL-safe */
+ kfree(res->regul_bulk);
+ if (!IS_ERR_OR_NULL(res->hdmiphy))
+ clk_put(res->hdmiphy);
+ if (!IS_ERR_OR_NULL(res->sclk_hdmiphy))
+ clk_put(res->sclk_hdmiphy);
+ if (!IS_ERR_OR_NULL(res->sclk_pixel))
+ clk_put(res->sclk_pixel);
+ if (!IS_ERR_OR_NULL(res->sclk_hdmi))
+ clk_put(res->sclk_hdmi);
+ if (!IS_ERR_OR_NULL(res->hdmi))
+ clk_put(res->hdmi);
+ memset(res, 0, sizeof *res);
+}
+
+static int hdmi_resources_init(struct hdmi_device *hdev)
+{
+ struct device *dev = hdev->dev;
+ struct hdmi_resources *res = &hdev->res;
+ static char *supply[] = {
+ "hdmi-en",
+ "vdd",
+ "vdd_osc",
+ "vdd_pll",
+ };
+ int i, ret;
+
+ dev_dbg(dev, "HDMI resource init\n");
+
+ memset(res, 0, sizeof *res);
+ /* get clocks, power */
+
+ res->hdmi = clk_get(dev, "hdmi");
+ if (IS_ERR_OR_NULL(res->hdmi)) {
+ dev_err(dev, "failed to get clock 'hdmi'\n");
+ goto fail;
+ }
+ res->sclk_hdmi = clk_get(dev, "sclk_hdmi");
+ if (IS_ERR_OR_NULL(res->sclk_hdmi)) {
+ dev_err(dev, "failed to get clock 'sclk_hdmi'\n");
+ goto fail;
+ }
+ res->sclk_pixel = clk_get(dev, "sclk_pixel");
+ if (IS_ERR_OR_NULL(res->sclk_pixel)) {
+ dev_err(dev, "failed to get clock 'sclk_pixel'\n");
+ goto fail;
+ }
+ res->sclk_hdmiphy = clk_get(dev, "sclk_hdmiphy");
+ if (IS_ERR_OR_NULL(res->sclk_hdmiphy)) {
+ dev_err(dev, "failed to get clock 'sclk_hdmiphy'\n");
+ goto fail;
+ }
+ res->hdmiphy = clk_get(dev, "hdmiphy");
+ if (IS_ERR_OR_NULL(res->hdmiphy)) {
+ dev_err(dev, "failed to get clock 'hdmiphy'\n");
+ goto fail;
+ }
+ res->regul_bulk = kcalloc(ARRAY_SIZE(supply),
+ sizeof(res->regul_bulk[0]), GFP_KERNEL);
+ if (!res->regul_bulk) {
+ dev_err(dev, "failed to get memory for regulators\n");
+ goto fail;
+ }
+ for (i = 0; i < ARRAY_SIZE(supply); ++i) {
+ res->regul_bulk[i].supply = supply[i];
+ res->regul_bulk[i].consumer = NULL;
+ }
+
+ ret = regulator_bulk_get(dev, ARRAY_SIZE(supply), res->regul_bulk);
+ if (ret) {
+ dev_err(dev, "failed to get regulators\n");
+ goto fail;
+ }
+ res->regul_count = ARRAY_SIZE(supply);
+
+ return 0;
+fail:
+ dev_err(dev, "HDMI resource init - failed\n");
+ hdmi_resources_cleanup(hdev);
+ return -ENODEV;
+}
+
+static int __devinit hdmi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct i2c_adapter *adapter;
+ struct v4l2_subdev *sd;
+ struct hdmi_device *hdmi_dev = NULL;
+ struct s5p_hdmi_platform_data *pdata = dev->platform_data;
+ int ret;
+
+ dev_dbg(dev, "probe start\n");
+
+ if (!pdata) {
+ dev_err(dev, "platform data is missing\n");
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ hdmi_dev = devm_kzalloc(&pdev->dev, sizeof(*hdmi_dev), GFP_KERNEL);
+ if (!hdmi_dev) {
+ dev_err(dev, "out of memory\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ hdmi_dev->dev = dev;
+
+ ret = hdmi_resources_init(hdmi_dev);
+ if (ret)
+ goto fail;
+
+ /* mapping HDMI registers */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(dev, "get memory resource failed.\n");
+ ret = -ENXIO;
+ goto fail_init;
+ }
+
+ hdmi_dev->regs = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (hdmi_dev->regs == NULL) {
+ dev_err(dev, "register mapping failed.\n");
+ ret = -ENXIO;
+ goto fail_init;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_err(dev, "get interrupt resource failed.\n");
+ ret = -ENXIO;
+ goto fail_init;
+ }
+
+ ret = devm_request_irq(&pdev->dev, res->start, hdmi_irq_handler, 0,
+ "hdmi", hdmi_dev);
+ if (ret) {
+ dev_err(dev, "request interrupt failed.\n");
+ goto fail_init;
+ }
+ hdmi_dev->irq = res->start;
+
+ /* setting v4l2 name to prevent WARN_ON in v4l2_device_register */
+ strlcpy(hdmi_dev->v4l2_dev.name, dev_name(dev),
+ sizeof(hdmi_dev->v4l2_dev.name));
+ /* passing NULL owner prevents driver from erasing drvdata */
+ ret = v4l2_device_register(NULL, &hdmi_dev->v4l2_dev);
+ if (ret) {
+ dev_err(dev, "could not register v4l2 device.\n");
+ goto fail_init;
+ }
+
+ /* testing if hdmiphy info is present */
+ if (!pdata->hdmiphy_info) {
+ dev_err(dev, "hdmiphy info is missing in platform data\n");
+ ret = -ENXIO;
+ goto fail_vdev;
+ }
+
+ adapter = i2c_get_adapter(pdata->hdmiphy_bus);
+ if (adapter == NULL) {
+ dev_err(dev, "hdmiphy adapter request failed\n");
+ ret = -ENXIO;
+ goto fail_vdev;
+ }
+
+ hdmi_dev->phy_sd = v4l2_i2c_new_subdev_board(&hdmi_dev->v4l2_dev,
+ adapter, pdata->hdmiphy_info, NULL);
+ /* on failure or not adapter is no longer useful */
+ i2c_put_adapter(adapter);
+ if (hdmi_dev->phy_sd == NULL) {
+ dev_err(dev, "missing subdev for hdmiphy\n");
+ ret = -ENODEV;
+ goto fail_vdev;
+ }
+
+ /* initialization of MHL interface if present */
+ if (pdata->mhl_info) {
+ adapter = i2c_get_adapter(pdata->mhl_bus);
+ if (adapter == NULL) {
+ dev_err(dev, "MHL adapter request failed\n");
+ ret = -ENXIO;
+ goto fail_vdev;
+ }
+
+ hdmi_dev->mhl_sd = v4l2_i2c_new_subdev_board(
+ &hdmi_dev->v4l2_dev, adapter,
+ pdata->mhl_info, NULL);
+ /* on failure or not adapter is no longer useful */
+ i2c_put_adapter(adapter);
+ if (hdmi_dev->mhl_sd == NULL) {
+ dev_err(dev, "missing subdev for MHL\n");
+ ret = -ENODEV;
+ goto fail_vdev;
+ }
+ }
+
+ clk_enable(hdmi_dev->res.hdmi);
+
+ pm_runtime_enable(dev);
+
+ sd = &hdmi_dev->sd;
+ v4l2_subdev_init(sd, &hdmi_sd_ops);
+ sd->owner = THIS_MODULE;
+
+ strlcpy(sd->name, "s5p-hdmi", sizeof sd->name);
+ hdmi_dev->cur_preset = HDMI_DEFAULT_PRESET;
+ /* FIXME: missing fail preset is not supported */
+ hdmi_dev->cur_conf = hdmi_preset2conf(hdmi_dev->cur_preset);
+
+ /* storing subdev for call that have only access to struct device */
+ dev_set_drvdata(dev, sd);
+
+ dev_info(dev, "probe successful\n");
+
+ return 0;
+
+fail_vdev:
+ v4l2_device_unregister(&hdmi_dev->v4l2_dev);
+
+fail_init:
+ hdmi_resources_cleanup(hdmi_dev);
+
+fail:
+ dev_err(dev, "probe failed\n");
+ return ret;
+}
+
+static int __devexit hdmi_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct hdmi_device *hdmi_dev = sd_to_hdmi_dev(sd);
+
+ pm_runtime_disable(dev);
+ clk_disable(hdmi_dev->res.hdmi);
+ v4l2_device_unregister(&hdmi_dev->v4l2_dev);
+ disable_irq(hdmi_dev->irq);
+ hdmi_resources_cleanup(hdmi_dev);
+ dev_info(dev, "remove successful\n");
+
+ return 0;
+}
+
+static struct platform_driver hdmi_driver __refdata = {
+ .probe = hdmi_probe,
+ .remove = __devexit_p(hdmi_remove),
+ .id_table = hdmi_driver_types,
+ .driver = {
+ .name = "s5p-hdmi",
+ .owner = THIS_MODULE,
+ .pm = &hdmi_pm_ops,
+ }
+};
+
+module_platform_driver(hdmi_driver);
diff --git a/drivers/media/video/s5p-tv/hdmiphy_drv.c b/drivers/media/video/s5p-tv/hdmiphy_drv.c
new file mode 100644
index 00000000..0afef777
--- /dev/null
+++ b/drivers/media/video/s5p-tv/hdmiphy_drv.c
@@ -0,0 +1,178 @@
+/*
+ * Samsung HDMI Physical interface driver
+ *
+ * Copyright (C) 2010-2011 Samsung Electronics Co.Ltd
+ * Author: Tomasz Stanislawski <t.stanislaws@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/err.h>
+
+#include <media/v4l2-subdev.h>
+
+MODULE_AUTHOR("Tomasz Stanislawski <t.stanislaws@samsung.com>");
+MODULE_DESCRIPTION("Samsung HDMI Physical interface driver");
+MODULE_LICENSE("GPL");
+
+struct hdmiphy_conf {
+ u32 preset;
+ const u8 *data;
+};
+
+static const u8 hdmiphy_conf27[32] = {
+ 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
+ 0x6B, 0x10, 0x02, 0x51, 0xDf, 0xF2, 0x54, 0x87,
+ 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
+ 0x22, 0x40, 0xe3, 0x26, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const u8 hdmiphy_conf74_175[32] = {
+ 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
+ 0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
+ 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
+ 0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00,
+};
+
+static const u8 hdmiphy_conf74_25[32] = {
+ 0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
+ 0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
+ 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xe0,
+ 0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00,
+};
+
+static const u8 hdmiphy_conf148_5[32] = {
+ 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
+ 0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
+ 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
+ 0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00,
+};
+
+static const struct hdmiphy_conf hdmiphy_conf[] = {
+ { V4L2_DV_480P59_94, hdmiphy_conf27 },
+ { V4L2_DV_1080P30, hdmiphy_conf74_175 },
+ { V4L2_DV_720P59_94, hdmiphy_conf74_175 },
+ { V4L2_DV_720P60, hdmiphy_conf74_25 },
+ { V4L2_DV_1080P50, hdmiphy_conf148_5 },
+ { V4L2_DV_1080P60, hdmiphy_conf148_5 },
+};
+
+const u8 *hdmiphy_preset2conf(u32 preset)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(hdmiphy_conf); ++i)
+ if (hdmiphy_conf[i].preset == preset)
+ return hdmiphy_conf[i].data;
+ return NULL;
+}
+
+static int hdmiphy_s_power(struct v4l2_subdev *sd, int on)
+{
+ /* to be implemented */
+ return 0;
+}
+
+static int hdmiphy_s_dv_preset(struct v4l2_subdev *sd,
+ struct v4l2_dv_preset *preset)
+{
+ const u8 *data;
+ u8 buffer[32];
+ int ret;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct device *dev = &client->dev;
+
+ dev_info(dev, "s_dv_preset(preset = %d)\n", preset->preset);
+ data = hdmiphy_preset2conf(preset->preset);
+ if (!data) {
+ dev_err(dev, "format not supported\n");
+ return -EINVAL;
+ }
+
+ /* storing configuration to the device */
+ memcpy(buffer, data, 32);
+ ret = i2c_master_send(client, buffer, 32);
+ if (ret != 32) {
+ dev_err(dev, "failed to configure HDMIPHY via I2C\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int hdmiphy_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct device *dev = &client->dev;
+ u8 buffer[2];
+ int ret;
+
+ dev_info(dev, "s_stream(%d)\n", enable);
+ /* going to/from configuration from/to operation mode */
+ buffer[0] = 0x1f;
+ buffer[1] = enable ? 0x80 : 0x00;
+
+ ret = i2c_master_send(client, buffer, 2);
+ if (ret != 2) {
+ dev_err(dev, "stream (%d) failed\n", enable);
+ return -EIO;
+ }
+ return 0;
+}
+
+static const struct v4l2_subdev_core_ops hdmiphy_core_ops = {
+ .s_power = hdmiphy_s_power,
+};
+
+static const struct v4l2_subdev_video_ops hdmiphy_video_ops = {
+ .s_dv_preset = hdmiphy_s_dv_preset,
+ .s_stream = hdmiphy_s_stream,
+};
+
+static const struct v4l2_subdev_ops hdmiphy_ops = {
+ .core = &hdmiphy_core_ops,
+ .video = &hdmiphy_video_ops,
+};
+
+static int __devinit hdmiphy_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ static struct v4l2_subdev sd;
+
+ v4l2_i2c_subdev_init(&sd, client, &hdmiphy_ops);
+ dev_info(&client->dev, "probe successful\n");
+ return 0;
+}
+
+static int __devexit hdmiphy_remove(struct i2c_client *client)
+{
+ dev_info(&client->dev, "remove successful\n");
+ return 0;
+}
+
+static const struct i2c_device_id hdmiphy_id[] = {
+ { "hdmiphy", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, hdmiphy_id);
+
+static struct i2c_driver hdmiphy_driver = {
+ .driver = {
+ .name = "s5p-hdmiphy",
+ .owner = THIS_MODULE,
+ },
+ .probe = hdmiphy_probe,
+ .remove = __devexit_p(hdmiphy_remove),
+ .id_table = hdmiphy_id,
+};
+
+module_i2c_driver(hdmiphy_driver);
diff --git a/drivers/media/video/s5p-tv/mixer.h b/drivers/media/video/s5p-tv/mixer.h
new file mode 100644
index 00000000..1597078c
--- /dev/null
+++ b/drivers/media/video/s5p-tv/mixer.h
@@ -0,0 +1,364 @@
+/*
+ * Samsung TV Mixer driver
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * Tomasz Stanislawski, <t.stanislaws@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundiation. either version 2 of the License,
+ * or (at your option) any later version
+ */
+
+#ifndef SAMSUNG_MIXER_H
+#define SAMSUNG_MIXER_H
+
+#ifdef CONFIG_VIDEO_SAMSUNG_S5P_MIXER_DEBUG
+ #define DEBUG
+#endif
+
+#include <linux/fb.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-core.h>
+
+#include "regs-mixer.h"
+
+/** maximum number of output interfaces */
+#define MXR_MAX_OUTPUTS 2
+/** maximum number of input interfaces (layers) */
+#define MXR_MAX_LAYERS 3
+#define MXR_DRIVER_NAME "s5p-mixer"
+/** maximal number of planes for every layer */
+#define MXR_MAX_PLANES 2
+
+#define MXR_ENABLE 1
+#define MXR_DISABLE 0
+
+/** description of a macroblock for packed formats */
+struct mxr_block {
+ /** vertical number of pixels in macroblock */
+ unsigned int width;
+ /** horizontal number of pixels in macroblock */
+ unsigned int height;
+ /** size of block in bytes */
+ unsigned int size;
+};
+
+/** description of supported format */
+struct mxr_format {
+ /** format name/mnemonic */
+ const char *name;
+ /** fourcc identifier */
+ u32 fourcc;
+ /** colorspace identifier */
+ enum v4l2_colorspace colorspace;
+ /** number of planes in image data */
+ int num_planes;
+ /** description of block for each plane */
+ struct mxr_block plane[MXR_MAX_PLANES];
+ /** number of subframes in image data */
+ int num_subframes;
+ /** specifies to which subframe belong given plane */
+ int plane2subframe[MXR_MAX_PLANES];
+ /** internal code, driver dependant */
+ unsigned long cookie;
+};
+
+/** description of crop configuration for image */
+struct mxr_crop {
+ /** width of layer in pixels */
+ unsigned int full_width;
+ /** height of layer in pixels */
+ unsigned int full_height;
+ /** horizontal offset of first pixel to be displayed */
+ unsigned int x_offset;
+ /** vertical offset of first pixel to be displayed */
+ unsigned int y_offset;
+ /** width of displayed data in pixels */
+ unsigned int width;
+ /** height of displayed data in pixels */
+ unsigned int height;
+ /** indicate which fields are present in buffer */
+ unsigned int field;
+};
+
+/** stages of geometry operations */
+enum mxr_geometry_stage {
+ MXR_GEOMETRY_SINK,
+ MXR_GEOMETRY_COMPOSE,
+ MXR_GEOMETRY_CROP,
+ MXR_GEOMETRY_SOURCE,
+};
+
+/* flag indicating that offset should be 0 */
+#define MXR_NO_OFFSET 0x80000000
+
+/** description of transformation from source to destination image */
+struct mxr_geometry {
+ /** cropping for source image */
+ struct mxr_crop src;
+ /** cropping for destination image */
+ struct mxr_crop dst;
+ /** layer-dependant description of horizontal scaling */
+ unsigned int x_ratio;
+ /** layer-dependant description of vertical scaling */
+ unsigned int y_ratio;
+};
+
+/** instance of a buffer */
+struct mxr_buffer {
+ /** common v4l buffer stuff -- must be first */
+ struct vb2_buffer vb;
+ /** node for layer's lists */
+ struct list_head list;
+};
+
+
+/** internal states of layer */
+enum mxr_layer_state {
+ /** layers is not shown */
+ MXR_LAYER_IDLE = 0,
+ /** layer is shown */
+ MXR_LAYER_STREAMING,
+ /** state before STREAMOFF is finished */
+ MXR_LAYER_STREAMING_FINISH,
+};
+
+/** forward declarations */
+struct mxr_device;
+struct mxr_layer;
+
+/** callback for layers operation */
+struct mxr_layer_ops {
+ /* TODO: try to port it to subdev API */
+ /** handler for resource release function */
+ void (*release)(struct mxr_layer *);
+ /** setting buffer to HW */
+ void (*buffer_set)(struct mxr_layer *, struct mxr_buffer *);
+ /** setting format and geometry in HW */
+ void (*format_set)(struct mxr_layer *);
+ /** streaming stop/start */
+ void (*stream_set)(struct mxr_layer *, int);
+ /** adjusting geometry */
+ void (*fix_geometry)(struct mxr_layer *,
+ enum mxr_geometry_stage, unsigned long);
+};
+
+/** layer instance, a single window and content displayed on output */
+struct mxr_layer {
+ /** parent mixer device */
+ struct mxr_device *mdev;
+ /** layer index (unique identifier) */
+ int idx;
+ /** callbacks for layer methods */
+ struct mxr_layer_ops ops;
+ /** format array */
+ const struct mxr_format **fmt_array;
+ /** size of format array */
+ unsigned long fmt_array_size;
+
+ /** lock for protection of list and state fields */
+ spinlock_t enq_slock;
+ /** list for enqueued buffers */
+ struct list_head enq_list;
+ /** buffer currently owned by hardware in temporary registers */
+ struct mxr_buffer *update_buf;
+ /** buffer currently owned by hardware in shadow registers */
+ struct mxr_buffer *shadow_buf;
+ /** state of layer IDLE/STREAMING */
+ enum mxr_layer_state state;
+
+ /** mutex for protection of fields below */
+ struct mutex mutex;
+ /** handler for video node */
+ struct video_device vfd;
+ /** queue for output buffers */
+ struct vb2_queue vb_queue;
+ /** current image format */
+ const struct mxr_format *fmt;
+ /** current geometry of image */
+ struct mxr_geometry geo;
+};
+
+/** description of mixers output interface */
+struct mxr_output {
+ /** name of output */
+ char name[32];
+ /** output subdev */
+ struct v4l2_subdev *sd;
+ /** cookie used for configuration of registers */
+ int cookie;
+};
+
+/** specify source of output subdevs */
+struct mxr_output_conf {
+ /** name of output (connector) */
+ char *output_name;
+ /** name of module that generates output subdev */
+ char *module_name;
+ /** cookie need for mixer HW */
+ int cookie;
+};
+
+struct clk;
+struct regulator;
+
+/** auxiliary resources used my mixer */
+struct mxr_resources {
+ /** interrupt index */
+ int irq;
+ /** pointer to Mixer registers */
+ void __iomem *mxr_regs;
+ /** pointer to Video Processor registers */
+ void __iomem *vp_regs;
+ /** other resources, should used under mxr_device.mutex */
+ struct clk *mixer;
+ struct clk *vp;
+ struct clk *sclk_mixer;
+ struct clk *sclk_hdmi;
+ struct clk *sclk_dac;
+};
+
+/* event flags used */
+enum mxr_devide_flags {
+ MXR_EVENT_VSYNC = 0,
+};
+
+/** drivers instance */
+struct mxr_device {
+ /** master device */
+ struct device *dev;
+ /** state of each layer */
+ struct mxr_layer *layer[MXR_MAX_LAYERS];
+ /** state of each output */
+ struct mxr_output *output[MXR_MAX_OUTPUTS];
+ /** number of registered outputs */
+ int output_cnt;
+
+ /* video resources */
+
+ /** V4L2 device */
+ struct v4l2_device v4l2_dev;
+ /** context of allocator */
+ void *alloc_ctx;
+ /** event wait queue */
+ wait_queue_head_t event_queue;
+ /** state flags */
+ unsigned long event_flags;
+
+ /** spinlock for protection of registers */
+ spinlock_t reg_slock;
+
+ /** mutex for protection of fields below */
+ struct mutex mutex;
+ /** number of entities depndant on output configuration */
+ int n_output;
+ /** number of users that do streaming */
+ int n_streamer;
+ /** index of current output */
+ int current_output;
+ /** auxiliary resources used my mixer */
+ struct mxr_resources res;
+};
+
+/** transform device structure into mixer device */
+static inline struct mxr_device *to_mdev(struct device *dev)
+{
+ struct v4l2_device *vdev = dev_get_drvdata(dev);
+ return container_of(vdev, struct mxr_device, v4l2_dev);
+}
+
+/** get current output data, should be called under mdev's mutex */
+static inline struct mxr_output *to_output(struct mxr_device *mdev)
+{
+ return mdev->output[mdev->current_output];
+}
+
+/** get current output subdev, should be called under mdev's mutex */
+static inline struct v4l2_subdev *to_outsd(struct mxr_device *mdev)
+{
+ struct mxr_output *out = to_output(mdev);
+ return out ? out->sd : NULL;
+}
+
+/** forward declaration for mixer platform data */
+struct mxr_platform_data;
+
+/** acquiring common video resources */
+int __devinit mxr_acquire_video(struct mxr_device *mdev,
+ struct mxr_output_conf *output_cont, int output_count);
+
+/** releasing common video resources */
+void __devexit mxr_release_video(struct mxr_device *mdev);
+
+struct mxr_layer *mxr_graph_layer_create(struct mxr_device *mdev, int idx);
+struct mxr_layer *mxr_vp_layer_create(struct mxr_device *mdev, int idx);
+struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev,
+ int idx, char *name, struct mxr_layer_ops *ops);
+
+void mxr_base_layer_release(struct mxr_layer *layer);
+void mxr_layer_release(struct mxr_layer *layer);
+
+int mxr_base_layer_register(struct mxr_layer *layer);
+void mxr_base_layer_unregister(struct mxr_layer *layer);
+
+unsigned long mxr_get_plane_size(const struct mxr_block *blk,
+ unsigned int width, unsigned int height);
+
+/** adds new consumer for mixer's power */
+int __must_check mxr_power_get(struct mxr_device *mdev);
+/** removes consumer for mixer's power */
+void mxr_power_put(struct mxr_device *mdev);
+/** add new client for output configuration */
+void mxr_output_get(struct mxr_device *mdev);
+/** removes new client for output configuration */
+void mxr_output_put(struct mxr_device *mdev);
+/** add new client for streaming */
+void mxr_streamer_get(struct mxr_device *mdev);
+/** removes new client for streaming */
+void mxr_streamer_put(struct mxr_device *mdev);
+/** returns format of data delivared to current output */
+void mxr_get_mbus_fmt(struct mxr_device *mdev,
+ struct v4l2_mbus_framefmt *mbus_fmt);
+
+/* Debug */
+
+#define mxr_err(mdev, fmt, ...) dev_err(mdev->dev, fmt, ##__VA_ARGS__)
+#define mxr_warn(mdev, fmt, ...) dev_warn(mdev->dev, fmt, ##__VA_ARGS__)
+#define mxr_info(mdev, fmt, ...) dev_info(mdev->dev, fmt, ##__VA_ARGS__)
+
+#ifdef CONFIG_VIDEO_SAMSUNG_S5P_MIXER_DEBUG
+ #define mxr_dbg(mdev, fmt, ...) dev_dbg(mdev->dev, fmt, ##__VA_ARGS__)
+#else
+ #define mxr_dbg(mdev, fmt, ...) do { (void) mdev; } while (0)
+#endif
+
+/* accessing Mixer's and Video Processor's registers */
+
+void mxr_vsync_set_update(struct mxr_device *mdev, int en);
+void mxr_reg_reset(struct mxr_device *mdev);
+irqreturn_t mxr_irq_handler(int irq, void *dev_data);
+void mxr_reg_s_output(struct mxr_device *mdev, int cookie);
+void mxr_reg_streamon(struct mxr_device *mdev);
+void mxr_reg_streamoff(struct mxr_device *mdev);
+int mxr_reg_wait4vsync(struct mxr_device *mdev);
+void mxr_reg_set_mbus_fmt(struct mxr_device *mdev,
+ struct v4l2_mbus_framefmt *fmt);
+void mxr_reg_graph_layer_stream(struct mxr_device *mdev, int idx, int en);
+void mxr_reg_graph_buffer(struct mxr_device *mdev, int idx, dma_addr_t addr);
+void mxr_reg_graph_format(struct mxr_device *mdev, int idx,
+ const struct mxr_format *fmt, const struct mxr_geometry *geo);
+
+void mxr_reg_vp_layer_stream(struct mxr_device *mdev, int en);
+void mxr_reg_vp_buffer(struct mxr_device *mdev,
+ dma_addr_t luma_addr[2], dma_addr_t chroma_addr[2]);
+void mxr_reg_vp_format(struct mxr_device *mdev,
+ const struct mxr_format *fmt, const struct mxr_geometry *geo);
+void mxr_reg_dump(struct mxr_device *mdev);
+
+#endif /* SAMSUNG_MIXER_H */
+
diff --git a/drivers/media/video/s5p-tv/mixer_drv.c b/drivers/media/video/s5p-tv/mixer_drv.c
new file mode 100644
index 00000000..a2c0c25a
--- /dev/null
+++ b/drivers/media/video/s5p-tv/mixer_drv.c
@@ -0,0 +1,487 @@
+/*
+ * Samsung TV Mixer driver
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * Tomasz Stanislawski, <t.stanislaws@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundiation. either version 2 of the License,
+ * or (at your option) any later version
+ */
+
+#include "mixer.h"
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+
+MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>");
+MODULE_DESCRIPTION("Samsung MIXER");
+MODULE_LICENSE("GPL");
+
+/* --------- DRIVER PARAMETERS ---------- */
+
+static struct mxr_output_conf mxr_output_conf[] = {
+ {
+ .output_name = "S5P HDMI connector",
+ .module_name = "s5p-hdmi",
+ .cookie = 1,
+ },
+ {
+ .output_name = "S5P SDO connector",
+ .module_name = "s5p-sdo",
+ .cookie = 0,
+ },
+};
+
+void mxr_get_mbus_fmt(struct mxr_device *mdev,
+ struct v4l2_mbus_framefmt *mbus_fmt)
+{
+ struct v4l2_subdev *sd;
+ int ret;
+
+ mutex_lock(&mdev->mutex);
+ sd = to_outsd(mdev);
+ ret = v4l2_subdev_call(sd, video, g_mbus_fmt, mbus_fmt);
+ WARN(ret, "failed to get mbus_fmt for output %s\n", sd->name);
+ mutex_unlock(&mdev->mutex);
+}
+
+void mxr_streamer_get(struct mxr_device *mdev)
+{
+ mutex_lock(&mdev->mutex);
+ ++mdev->n_streamer;
+ mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_streamer);
+ if (mdev->n_streamer == 1) {
+ struct v4l2_subdev *sd = to_outsd(mdev);
+ struct v4l2_mbus_framefmt mbus_fmt;
+ struct mxr_resources *res = &mdev->res;
+ int ret;
+
+ if (to_output(mdev)->cookie == 0)
+ clk_set_parent(res->sclk_mixer, res->sclk_dac);
+ else
+ clk_set_parent(res->sclk_mixer, res->sclk_hdmi);
+ mxr_reg_s_output(mdev, to_output(mdev)->cookie);
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mbus_fmt);
+ WARN(ret, "failed to get mbus_fmt for output %s\n", sd->name);
+ ret = v4l2_subdev_call(sd, video, s_stream, 1);
+ WARN(ret, "starting stream failed for output %s\n", sd->name);
+
+ mxr_reg_set_mbus_fmt(mdev, &mbus_fmt);
+ mxr_reg_streamon(mdev);
+ ret = mxr_reg_wait4vsync(mdev);
+ WARN(ret, "failed to get vsync (%d) from output\n", ret);
+ }
+ mutex_unlock(&mdev->mutex);
+ mxr_reg_dump(mdev);
+ /* FIXME: what to do when streaming fails? */
+}
+
+void mxr_streamer_put(struct mxr_device *mdev)
+{
+ mutex_lock(&mdev->mutex);
+ --mdev->n_streamer;
+ mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_streamer);
+ if (mdev->n_streamer == 0) {
+ int ret;
+ struct v4l2_subdev *sd = to_outsd(mdev);
+
+ mxr_reg_streamoff(mdev);
+ /* vsync applies Mixer setup */
+ ret = mxr_reg_wait4vsync(mdev);
+ WARN(ret, "failed to get vsync (%d) from output\n", ret);
+ ret = v4l2_subdev_call(sd, video, s_stream, 0);
+ WARN(ret, "stopping stream failed for output %s\n", sd->name);
+ }
+ WARN(mdev->n_streamer < 0, "negative number of streamers (%d)\n",
+ mdev->n_streamer);
+ mutex_unlock(&mdev->mutex);
+ mxr_reg_dump(mdev);
+}
+
+void mxr_output_get(struct mxr_device *mdev)
+{
+ mutex_lock(&mdev->mutex);
+ ++mdev->n_output;
+ mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_output);
+ /* turn on auxiliary driver */
+ if (mdev->n_output == 1)
+ v4l2_subdev_call(to_outsd(mdev), core, s_power, 1);
+ mutex_unlock(&mdev->mutex);
+}
+
+void mxr_output_put(struct mxr_device *mdev)
+{
+ mutex_lock(&mdev->mutex);
+ --mdev->n_output;
+ mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_output);
+ /* turn on auxiliary driver */
+ if (mdev->n_output == 0)
+ v4l2_subdev_call(to_outsd(mdev), core, s_power, 0);
+ WARN(mdev->n_output < 0, "negative number of output users (%d)\n",
+ mdev->n_output);
+ mutex_unlock(&mdev->mutex);
+}
+
+int mxr_power_get(struct mxr_device *mdev)
+{
+ int ret = pm_runtime_get_sync(mdev->dev);
+
+ /* returning 1 means that power is already enabled,
+ * so zero success be returned */
+ if (IS_ERR_VALUE(ret))
+ return ret;
+ return 0;
+}
+
+void mxr_power_put(struct mxr_device *mdev)
+{
+ pm_runtime_put_sync(mdev->dev);
+}
+
+/* --------- RESOURCE MANAGEMENT -------------*/
+
+static int __devinit mxr_acquire_plat_resources(struct mxr_device *mdev,
+ struct platform_device *pdev)
+{
+ struct resource *res;
+ int ret;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mxr");
+ if (res == NULL) {
+ mxr_err(mdev, "get memory resource failed.\n");
+ ret = -ENXIO;
+ goto fail;
+ }
+
+ mdev->res.mxr_regs = ioremap(res->start, resource_size(res));
+ if (mdev->res.mxr_regs == NULL) {
+ mxr_err(mdev, "register mapping failed.\n");
+ ret = -ENXIO;
+ goto fail;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vp");
+ if (res == NULL) {
+ mxr_err(mdev, "get memory resource failed.\n");
+ ret = -ENXIO;
+ goto fail_mxr_regs;
+ }
+
+ mdev->res.vp_regs = ioremap(res->start, resource_size(res));
+ if (mdev->res.vp_regs == NULL) {
+ mxr_err(mdev, "register mapping failed.\n");
+ ret = -ENXIO;
+ goto fail_mxr_regs;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "irq");
+ if (res == NULL) {
+ mxr_err(mdev, "get interrupt resource failed.\n");
+ ret = -ENXIO;
+ goto fail_vp_regs;
+ }
+
+ ret = request_irq(res->start, mxr_irq_handler, 0, "s5p-mixer", mdev);
+ if (ret) {
+ mxr_err(mdev, "request interrupt failed.\n");
+ goto fail_vp_regs;
+ }
+ mdev->res.irq = res->start;
+
+ return 0;
+
+fail_vp_regs:
+ iounmap(mdev->res.vp_regs);
+
+fail_mxr_regs:
+ iounmap(mdev->res.mxr_regs);
+
+fail:
+ return ret;
+}
+
+static void mxr_release_plat_resources(struct mxr_device *mdev)
+{
+ free_irq(mdev->res.irq, mdev);
+ iounmap(mdev->res.vp_regs);
+ iounmap(mdev->res.mxr_regs);
+}
+
+static void mxr_release_clocks(struct mxr_device *mdev)
+{
+ struct mxr_resources *res = &mdev->res;
+
+ if (!IS_ERR_OR_NULL(res->sclk_dac))
+ clk_put(res->sclk_dac);
+ if (!IS_ERR_OR_NULL(res->sclk_hdmi))
+ clk_put(res->sclk_hdmi);
+ if (!IS_ERR_OR_NULL(res->sclk_mixer))
+ clk_put(res->sclk_mixer);
+ if (!IS_ERR_OR_NULL(res->vp))
+ clk_put(res->vp);
+ if (!IS_ERR_OR_NULL(res->mixer))
+ clk_put(res->mixer);
+}
+
+static int mxr_acquire_clocks(struct mxr_device *mdev)
+{
+ struct mxr_resources *res = &mdev->res;
+ struct device *dev = mdev->dev;
+
+ res->mixer = clk_get(dev, "mixer");
+ if (IS_ERR_OR_NULL(res->mixer)) {
+ mxr_err(mdev, "failed to get clock 'mixer'\n");
+ goto fail;
+ }
+ res->vp = clk_get(dev, "vp");
+ if (IS_ERR_OR_NULL(res->vp)) {
+ mxr_err(mdev, "failed to get clock 'vp'\n");
+ goto fail;
+ }
+ res->sclk_mixer = clk_get(dev, "sclk_mixer");
+ if (IS_ERR_OR_NULL(res->sclk_mixer)) {
+ mxr_err(mdev, "failed to get clock 'sclk_mixer'\n");
+ goto fail;
+ }
+ res->sclk_hdmi = clk_get(dev, "sclk_hdmi");
+ if (IS_ERR_OR_NULL(res->sclk_hdmi)) {
+ mxr_err(mdev, "failed to get clock 'sclk_hdmi'\n");
+ goto fail;
+ }
+ res->sclk_dac = clk_get(dev, "sclk_dac");
+ if (IS_ERR_OR_NULL(res->sclk_dac)) {
+ mxr_err(mdev, "failed to get clock 'sclk_dac'\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ mxr_release_clocks(mdev);
+ return -ENODEV;
+}
+
+static int __devinit mxr_acquire_resources(struct mxr_device *mdev,
+ struct platform_device *pdev)
+{
+ int ret;
+ ret = mxr_acquire_plat_resources(mdev, pdev);
+
+ if (ret)
+ goto fail;
+
+ ret = mxr_acquire_clocks(mdev);
+ if (ret)
+ goto fail_plat;
+
+ mxr_info(mdev, "resources acquired\n");
+ return 0;
+
+fail_plat:
+ mxr_release_plat_resources(mdev);
+fail:
+ mxr_err(mdev, "resources acquire failed\n");
+ return ret;
+}
+
+static void mxr_release_resources(struct mxr_device *mdev)
+{
+ mxr_release_clocks(mdev);
+ mxr_release_plat_resources(mdev);
+ memset(&mdev->res, 0, sizeof mdev->res);
+}
+
+static void mxr_release_layers(struct mxr_device *mdev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mdev->layer); ++i)
+ if (mdev->layer[i])
+ mxr_layer_release(mdev->layer[i]);
+}
+
+static int __devinit mxr_acquire_layers(struct mxr_device *mdev,
+ struct mxr_platform_data *pdata)
+{
+ mdev->layer[0] = mxr_graph_layer_create(mdev, 0);
+ mdev->layer[1] = mxr_graph_layer_create(mdev, 1);
+ mdev->layer[2] = mxr_vp_layer_create(mdev, 0);
+
+ if (!mdev->layer[0] || !mdev->layer[1] || !mdev->layer[2]) {
+ mxr_err(mdev, "failed to acquire layers\n");
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ mxr_release_layers(mdev);
+ return -ENODEV;
+}
+
+/* ---------- POWER MANAGEMENT ----------- */
+
+static int mxr_runtime_resume(struct device *dev)
+{
+ struct mxr_device *mdev = to_mdev(dev);
+ struct mxr_resources *res = &mdev->res;
+
+ mxr_dbg(mdev, "resume - start\n");
+ mutex_lock(&mdev->mutex);
+ /* turn clocks on */
+ clk_enable(res->mixer);
+ clk_enable(res->vp);
+ clk_enable(res->sclk_mixer);
+ /* apply default configuration */
+ mxr_reg_reset(mdev);
+ mxr_dbg(mdev, "resume - finished\n");
+
+ mutex_unlock(&mdev->mutex);
+ return 0;
+}
+
+static int mxr_runtime_suspend(struct device *dev)
+{
+ struct mxr_device *mdev = to_mdev(dev);
+ struct mxr_resources *res = &mdev->res;
+ mxr_dbg(mdev, "suspend - start\n");
+ mutex_lock(&mdev->mutex);
+ /* turn clocks off */
+ clk_disable(res->sclk_mixer);
+ clk_disable(res->vp);
+ clk_disable(res->mixer);
+ mutex_unlock(&mdev->mutex);
+ mxr_dbg(mdev, "suspend - finished\n");
+ return 0;
+}
+
+static const struct dev_pm_ops mxr_pm_ops = {
+ .runtime_suspend = mxr_runtime_suspend,
+ .runtime_resume = mxr_runtime_resume,
+};
+
+/* --------- DRIVER INITIALIZATION ---------- */
+
+static int __devinit mxr_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mxr_platform_data *pdata = dev->platform_data;
+ struct mxr_device *mdev;
+ int ret;
+
+ /* mdev does not exist yet so no mxr_dbg is used */
+ dev_info(dev, "probe start\n");
+
+ mdev = kzalloc(sizeof *mdev, GFP_KERNEL);
+ if (!mdev) {
+ mxr_err(mdev, "not enough memory.\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ /* setup pointer to master device */
+ mdev->dev = dev;
+
+ mutex_init(&mdev->mutex);
+ spin_lock_init(&mdev->reg_slock);
+ init_waitqueue_head(&mdev->event_queue);
+
+ /* acquire resources: regs, irqs, clocks, regulators */
+ ret = mxr_acquire_resources(mdev, pdev);
+ if (ret)
+ goto fail_mem;
+
+ /* configure resources for video output */
+ ret = mxr_acquire_video(mdev, mxr_output_conf,
+ ARRAY_SIZE(mxr_output_conf));
+ if (ret)
+ goto fail_resources;
+
+ /* configure layers */
+ ret = mxr_acquire_layers(mdev, pdata);
+ if (ret)
+ goto fail_video;
+
+ pm_runtime_enable(dev);
+
+ mxr_info(mdev, "probe successful\n");
+ return 0;
+
+fail_video:
+ mxr_release_video(mdev);
+
+fail_resources:
+ mxr_release_resources(mdev);
+
+fail_mem:
+ kfree(mdev);
+
+fail:
+ dev_info(dev, "probe failed\n");
+ return ret;
+}
+
+static int __devexit mxr_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mxr_device *mdev = to_mdev(dev);
+
+ pm_runtime_disable(dev);
+
+ mxr_release_layers(mdev);
+ mxr_release_video(mdev);
+ mxr_release_resources(mdev);
+
+ kfree(mdev);
+
+ dev_info(dev, "remove successful\n");
+ return 0;
+}
+
+static struct platform_driver mxr_driver __refdata = {
+ .probe = mxr_probe,
+ .remove = __devexit_p(mxr_remove),
+ .driver = {
+ .name = MXR_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .pm = &mxr_pm_ops,
+ }
+};
+
+static int __init mxr_init(void)
+{
+ int i, ret;
+ static const char banner[] __initdata = KERN_INFO
+ "Samsung TV Mixer driver, "
+ "(c) 2010-2011 Samsung Electronics Co., Ltd.\n";
+ printk(banner);
+
+ /* Loading auxiliary modules */
+ for (i = 0; i < ARRAY_SIZE(mxr_output_conf); ++i)
+ request_module(mxr_output_conf[i].module_name);
+
+ ret = platform_driver_register(&mxr_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "registration of MIXER driver failed\n");
+ return -ENXIO;
+ }
+
+ return 0;
+}
+module_init(mxr_init);
+
+static void __exit mxr_exit(void)
+{
+ platform_driver_unregister(&mxr_driver);
+}
+module_exit(mxr_exit);
diff --git a/drivers/media/video/s5p-tv/mixer_grp_layer.c b/drivers/media/video/s5p-tv/mixer_grp_layer.c
new file mode 100644
index 00000000..b93a21f5
--- /dev/null
+++ b/drivers/media/video/s5p-tv/mixer_grp_layer.c
@@ -0,0 +1,270 @@
+/*
+ * Samsung TV Mixer driver
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * Tomasz Stanislawski, <t.stanislaws@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundiation. either version 2 of the License,
+ * or (at your option) any later version
+ */
+
+#include "mixer.h"
+
+#include <media/videobuf2-dma-contig.h>
+
+/* FORMAT DEFINITIONS */
+
+static const struct mxr_format mxr_fb_fmt_rgb565 = {
+ .name = "RGB565",
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .num_planes = 1,
+ .plane = {
+ { .width = 1, .height = 1, .size = 2 },
+ },
+ .num_subframes = 1,
+ .cookie = 4,
+};
+
+static const struct mxr_format mxr_fb_fmt_argb1555 = {
+ .name = "ARGB1555",
+ .num_planes = 1,
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .plane = {
+ { .width = 1, .height = 1, .size = 2 },
+ },
+ .num_subframes = 1,
+ .cookie = 5,
+};
+
+static const struct mxr_format mxr_fb_fmt_argb4444 = {
+ .name = "ARGB4444",
+ .num_planes = 1,
+ .fourcc = V4L2_PIX_FMT_RGB444,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .plane = {
+ { .width = 1, .height = 1, .size = 2 },
+ },
+ .num_subframes = 1,
+ .cookie = 6,
+};
+
+static const struct mxr_format mxr_fb_fmt_argb8888 = {
+ .name = "ARGB8888",
+ .fourcc = V4L2_PIX_FMT_BGR32,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .num_planes = 1,
+ .plane = {
+ { .width = 1, .height = 1, .size = 4 },
+ },
+ .num_subframes = 1,
+ .cookie = 7,
+};
+
+static const struct mxr_format *mxr_graph_format[] = {
+ &mxr_fb_fmt_rgb565,
+ &mxr_fb_fmt_argb1555,
+ &mxr_fb_fmt_argb4444,
+ &mxr_fb_fmt_argb8888,
+};
+
+/* AUXILIARY CALLBACKS */
+
+static void mxr_graph_layer_release(struct mxr_layer *layer)
+{
+ mxr_base_layer_unregister(layer);
+ mxr_base_layer_release(layer);
+}
+
+static void mxr_graph_buffer_set(struct mxr_layer *layer,
+ struct mxr_buffer *buf)
+{
+ dma_addr_t addr = 0;
+
+ if (buf)
+ addr = vb2_dma_contig_plane_dma_addr(&buf->vb, 0);
+ mxr_reg_graph_buffer(layer->mdev, layer->idx, addr);
+}
+
+static void mxr_graph_stream_set(struct mxr_layer *layer, int en)
+{
+ mxr_reg_graph_layer_stream(layer->mdev, layer->idx, en);
+}
+
+static void mxr_graph_format_set(struct mxr_layer *layer)
+{
+ mxr_reg_graph_format(layer->mdev, layer->idx,
+ layer->fmt, &layer->geo);
+}
+
+static inline unsigned int closest(unsigned int x, unsigned int a,
+ unsigned int b, unsigned long flags)
+{
+ unsigned int mid = (a + b) / 2;
+
+ /* choosing closest value with constraints according to table:
+ * -------------+-----+-----+-----+-------+
+ * flags | 0 | LE | GE | LE|GE |
+ * -------------+-----+-----+-----+-------+
+ * x <= a | a | a | a | a |
+ * a < x <= mid | a | a | b | a |
+ * mid < x < b | b | a | b | b |
+ * b <= x | b | b | b | b |
+ * -------------+-----+-----+-----+-------+
+ */
+
+ /* remove all non-constraint flags */
+ flags &= V4L2_SEL_FLAG_LE | V4L2_SEL_FLAG_GE;
+
+ if (x <= a)
+ return a;
+ if (x >= b)
+ return b;
+ if (flags == V4L2_SEL_FLAG_LE)
+ return a;
+ if (flags == V4L2_SEL_FLAG_GE)
+ return b;
+ if (x <= mid)
+ return a;
+ return b;
+}
+
+static inline unsigned int do_center(unsigned int center,
+ unsigned int size, unsigned int upper, unsigned int flags)
+{
+ unsigned int lower;
+
+ if (flags & MXR_NO_OFFSET)
+ return 0;
+
+ lower = center - min(center, size / 2);
+ return min(lower, upper - size);
+}
+
+static void mxr_graph_fix_geometry(struct mxr_layer *layer,
+ enum mxr_geometry_stage stage, unsigned long flags)
+{
+ struct mxr_geometry *geo = &layer->geo;
+ struct mxr_crop *src = &geo->src;
+ struct mxr_crop *dst = &geo->dst;
+ unsigned int x_center, y_center;
+
+ switch (stage) {
+
+ case MXR_GEOMETRY_SINK: /* nothing to be fixed here */
+ flags = 0;
+ /* fall through */
+
+ case MXR_GEOMETRY_COMPOSE:
+ /* remember center of the area */
+ x_center = dst->x_offset + dst->width / 2;
+ y_center = dst->y_offset + dst->height / 2;
+ /* round up/down to 2 multiple depending on flags */
+ if (flags & V4L2_SEL_FLAG_LE) {
+ dst->width = round_down(dst->width, 2);
+ dst->height = round_down(dst->height, 2);
+ } else {
+ dst->width = round_up(dst->width, 2);
+ dst->height = round_up(dst->height, 2);
+ }
+ /* assure that compose rect is inside display area */
+ dst->width = min(dst->width, dst->full_width);
+ dst->height = min(dst->height, dst->full_height);
+
+ /* ensure that compose is reachable using 2x scaling */
+ dst->width = min(dst->width, 2 * src->full_width);
+ dst->height = min(dst->height, 2 * src->full_height);
+
+ /* setup offsets */
+ dst->x_offset = do_center(x_center, dst->width,
+ dst->full_width, flags);
+ dst->y_offset = do_center(y_center, dst->height,
+ dst->full_height, flags);
+ flags = 0;
+ /* fall through */
+
+ case MXR_GEOMETRY_CROP:
+ /* remember center of the area */
+ x_center = src->x_offset + src->width / 2;
+ y_center = src->y_offset + src->height / 2;
+ /* ensure that cropping area lies inside the buffer */
+ if (src->full_width < dst->width)
+ src->width = dst->width / 2;
+ else
+ src->width = closest(src->width, dst->width / 2,
+ dst->width, flags);
+
+ if (src->width == dst->width)
+ geo->x_ratio = 0;
+ else
+ geo->x_ratio = 1;
+
+ if (src->full_height < dst->height)
+ src->height = dst->height / 2;
+ else
+ src->height = closest(src->height, dst->height / 2,
+ dst->height, flags);
+
+ if (src->height == dst->height)
+ geo->y_ratio = 0;
+ else
+ geo->y_ratio = 1;
+
+ /* setup offsets */
+ src->x_offset = do_center(x_center, src->width,
+ src->full_width, flags);
+ src->y_offset = do_center(y_center, src->height,
+ src->full_height, flags);
+ flags = 0;
+ /* fall through */
+ case MXR_GEOMETRY_SOURCE:
+ src->full_width = clamp_val(src->full_width,
+ src->width + src->x_offset, 32767);
+ src->full_height = clamp_val(src->full_height,
+ src->height + src->y_offset, 2047);
+ };
+}
+
+/* PUBLIC API */
+
+struct mxr_layer *mxr_graph_layer_create(struct mxr_device *mdev, int idx)
+{
+ struct mxr_layer *layer;
+ int ret;
+ struct mxr_layer_ops ops = {
+ .release = mxr_graph_layer_release,
+ .buffer_set = mxr_graph_buffer_set,
+ .stream_set = mxr_graph_stream_set,
+ .format_set = mxr_graph_format_set,
+ .fix_geometry = mxr_graph_fix_geometry,
+ };
+ char name[32];
+
+ sprintf(name, "graph%d", idx);
+
+ layer = mxr_base_layer_create(mdev, idx, name, &ops);
+ if (layer == NULL) {
+ mxr_err(mdev, "failed to initialize layer(%d) base\n", idx);
+ goto fail;
+ }
+
+ layer->fmt_array = mxr_graph_format;
+ layer->fmt_array_size = ARRAY_SIZE(mxr_graph_format);
+
+ ret = mxr_base_layer_register(layer);
+ if (ret)
+ goto fail_layer;
+
+ return layer;
+
+fail_layer:
+ mxr_base_layer_release(layer);
+
+fail:
+ return NULL;
+}
+
diff --git a/drivers/media/video/s5p-tv/mixer_reg.c b/drivers/media/video/s5p-tv/mixer_reg.c
new file mode 100644
index 00000000..4800a3cb
--- /dev/null
+++ b/drivers/media/video/s5p-tv/mixer_reg.c
@@ -0,0 +1,548 @@
+/*
+ * Samsung TV Mixer driver
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * Tomasz Stanislawski, <t.stanislaws@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundiation. either version 2 of the License,
+ * or (at your option) any later version
+ */
+
+#include "mixer.h"
+#include "regs-mixer.h"
+#include "regs-vp.h"
+
+#include <linux/delay.h>
+
+/* Register access subroutines */
+
+static inline u32 vp_read(struct mxr_device *mdev, u32 reg_id)
+{
+ return readl(mdev->res.vp_regs + reg_id);
+}
+
+static inline void vp_write(struct mxr_device *mdev, u32 reg_id, u32 val)
+{
+ writel(val, mdev->res.vp_regs + reg_id);
+}
+
+static inline void vp_write_mask(struct mxr_device *mdev, u32 reg_id,
+ u32 val, u32 mask)
+{
+ u32 old = vp_read(mdev, reg_id);
+
+ val = (val & mask) | (old & ~mask);
+ writel(val, mdev->res.vp_regs + reg_id);
+}
+
+static inline u32 mxr_read(struct mxr_device *mdev, u32 reg_id)
+{
+ return readl(mdev->res.mxr_regs + reg_id);
+}
+
+static inline void mxr_write(struct mxr_device *mdev, u32 reg_id, u32 val)
+{
+ writel(val, mdev->res.mxr_regs + reg_id);
+}
+
+static inline void mxr_write_mask(struct mxr_device *mdev, u32 reg_id,
+ u32 val, u32 mask)
+{
+ u32 old = mxr_read(mdev, reg_id);
+
+ val = (val & mask) | (old & ~mask);
+ writel(val, mdev->res.mxr_regs + reg_id);
+}
+
+void mxr_vsync_set_update(struct mxr_device *mdev, int en)
+{
+ /* block update on vsync */
+ mxr_write_mask(mdev, MXR_STATUS, en ? MXR_STATUS_SYNC_ENABLE : 0,
+ MXR_STATUS_SYNC_ENABLE);
+ vp_write(mdev, VP_SHADOW_UPDATE, en ? VP_SHADOW_UPDATE_ENABLE : 0);
+}
+
+static void __mxr_reg_vp_reset(struct mxr_device *mdev)
+{
+ int tries = 100;
+
+ vp_write(mdev, VP_SRESET, VP_SRESET_PROCESSING);
+ for (tries = 100; tries; --tries) {
+ /* waiting until VP_SRESET_PROCESSING is 0 */
+ if (~vp_read(mdev, VP_SRESET) & VP_SRESET_PROCESSING)
+ break;
+ mdelay(10);
+ }
+ WARN(tries == 0, "failed to reset Video Processor\n");
+}
+
+static void mxr_reg_vp_default_filter(struct mxr_device *mdev);
+
+void mxr_reg_reset(struct mxr_device *mdev)
+{
+ unsigned long flags;
+ u32 val; /* value stored to register */
+
+ spin_lock_irqsave(&mdev->reg_slock, flags);
+ mxr_vsync_set_update(mdev, MXR_DISABLE);
+
+ /* set output in RGB888 mode */
+ mxr_write(mdev, MXR_CFG, MXR_CFG_OUT_RGB888);
+
+ /* 16 beat burst in DMA */
+ mxr_write_mask(mdev, MXR_STATUS, MXR_STATUS_16_BURST,
+ MXR_STATUS_BURST_MASK);
+
+ /* setting default layer priority: layer1 > video > layer0
+ * because typical usage scenario would be
+ * layer0 - framebuffer
+ * video - video overlay
+ * layer1 - OSD
+ */
+ val = MXR_LAYER_CFG_GRP0_VAL(1);
+ val |= MXR_LAYER_CFG_VP_VAL(2);
+ val |= MXR_LAYER_CFG_GRP1_VAL(3);
+ mxr_write(mdev, MXR_LAYER_CFG, val);
+
+ /* use dark gray background color */
+ mxr_write(mdev, MXR_BG_COLOR0, 0x808080);
+ mxr_write(mdev, MXR_BG_COLOR1, 0x808080);
+ mxr_write(mdev, MXR_BG_COLOR2, 0x808080);
+
+ /* setting graphical layers */
+
+ val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */
+ val |= MXR_GRP_CFG_BLEND_PRE_MUL; /* premul mode */
+ val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */
+
+ /* the same configuration for both layers */
+ mxr_write(mdev, MXR_GRAPHIC_CFG(0), val);
+ mxr_write(mdev, MXR_GRAPHIC_CFG(1), val);
+
+ /* configuration of Video Processor Registers */
+ __mxr_reg_vp_reset(mdev);
+ mxr_reg_vp_default_filter(mdev);
+
+ /* enable all interrupts */
+ mxr_write_mask(mdev, MXR_INT_EN, ~0, MXR_INT_EN_ALL);
+
+ mxr_vsync_set_update(mdev, MXR_ENABLE);
+ spin_unlock_irqrestore(&mdev->reg_slock, flags);
+}
+
+void mxr_reg_graph_format(struct mxr_device *mdev, int idx,
+ const struct mxr_format *fmt, const struct mxr_geometry *geo)
+{
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mdev->reg_slock, flags);
+ mxr_vsync_set_update(mdev, MXR_DISABLE);
+
+ /* setup format */
+ mxr_write_mask(mdev, MXR_GRAPHIC_CFG(idx),
+ MXR_GRP_CFG_FORMAT_VAL(fmt->cookie), MXR_GRP_CFG_FORMAT_MASK);
+
+ /* setup geometry */
+ mxr_write(mdev, MXR_GRAPHIC_SPAN(idx), geo->src.full_width);
+ val = MXR_GRP_WH_WIDTH(geo->src.width);
+ val |= MXR_GRP_WH_HEIGHT(geo->src.height);
+ val |= MXR_GRP_WH_H_SCALE(geo->x_ratio);
+ val |= MXR_GRP_WH_V_SCALE(geo->y_ratio);
+ mxr_write(mdev, MXR_GRAPHIC_WH(idx), val);
+
+ /* setup offsets in source image */
+ val = MXR_GRP_SXY_SX(geo->src.x_offset);
+ val |= MXR_GRP_SXY_SY(geo->src.y_offset);
+ mxr_write(mdev, MXR_GRAPHIC_SXY(idx), val);
+
+ /* setup offsets in display image */
+ val = MXR_GRP_DXY_DX(geo->dst.x_offset);
+ val |= MXR_GRP_DXY_DY(geo->dst.y_offset);
+ mxr_write(mdev, MXR_GRAPHIC_DXY(idx), val);
+
+ mxr_vsync_set_update(mdev, MXR_ENABLE);
+ spin_unlock_irqrestore(&mdev->reg_slock, flags);
+}
+
+void mxr_reg_vp_format(struct mxr_device *mdev,
+ const struct mxr_format *fmt, const struct mxr_geometry *geo)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mdev->reg_slock, flags);
+ mxr_vsync_set_update(mdev, MXR_DISABLE);
+
+ vp_write_mask(mdev, VP_MODE, fmt->cookie, VP_MODE_FMT_MASK);
+
+ /* setting size of input image */
+ vp_write(mdev, VP_IMG_SIZE_Y, VP_IMG_HSIZE(geo->src.full_width) |
+ VP_IMG_VSIZE(geo->src.full_height));
+ /* chroma height has to reduced by 2 to avoid chroma distorions */
+ vp_write(mdev, VP_IMG_SIZE_C, VP_IMG_HSIZE(geo->src.full_width) |
+ VP_IMG_VSIZE(geo->src.full_height / 2));
+
+ vp_write(mdev, VP_SRC_WIDTH, geo->src.width);
+ vp_write(mdev, VP_SRC_HEIGHT, geo->src.height);
+ vp_write(mdev, VP_SRC_H_POSITION,
+ VP_SRC_H_POSITION_VAL(geo->src.x_offset));
+ vp_write(mdev, VP_SRC_V_POSITION, geo->src.y_offset);
+
+ vp_write(mdev, VP_DST_WIDTH, geo->dst.width);
+ vp_write(mdev, VP_DST_H_POSITION, geo->dst.x_offset);
+ if (geo->dst.field == V4L2_FIELD_INTERLACED) {
+ vp_write(mdev, VP_DST_HEIGHT, geo->dst.height / 2);
+ vp_write(mdev, VP_DST_V_POSITION, geo->dst.y_offset / 2);
+ } else {
+ vp_write(mdev, VP_DST_HEIGHT, geo->dst.height);
+ vp_write(mdev, VP_DST_V_POSITION, geo->dst.y_offset);
+ }
+
+ vp_write(mdev, VP_H_RATIO, geo->x_ratio);
+ vp_write(mdev, VP_V_RATIO, geo->y_ratio);
+
+ vp_write(mdev, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE);
+
+ mxr_vsync_set_update(mdev, MXR_ENABLE);
+ spin_unlock_irqrestore(&mdev->reg_slock, flags);
+
+}
+
+void mxr_reg_graph_buffer(struct mxr_device *mdev, int idx, dma_addr_t addr)
+{
+ u32 val = addr ? ~0 : 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mdev->reg_slock, flags);
+ mxr_vsync_set_update(mdev, MXR_DISABLE);
+
+ if (idx == 0)
+ mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_GRP0_ENABLE);
+ else
+ mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_GRP1_ENABLE);
+ mxr_write(mdev, MXR_GRAPHIC_BASE(idx), addr);
+
+ mxr_vsync_set_update(mdev, MXR_ENABLE);
+ spin_unlock_irqrestore(&mdev->reg_slock, flags);
+}
+
+void mxr_reg_vp_buffer(struct mxr_device *mdev,
+ dma_addr_t luma_addr[2], dma_addr_t chroma_addr[2])
+{
+ u32 val = luma_addr[0] ? ~0 : 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mdev->reg_slock, flags);
+ mxr_vsync_set_update(mdev, MXR_DISABLE);
+
+ mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_VP_ENABLE);
+ vp_write_mask(mdev, VP_ENABLE, val, VP_ENABLE_ON);
+ /* TODO: fix tiled mode */
+ vp_write(mdev, VP_TOP_Y_PTR, luma_addr[0]);
+ vp_write(mdev, VP_TOP_C_PTR, chroma_addr[0]);
+ vp_write(mdev, VP_BOT_Y_PTR, luma_addr[1]);
+ vp_write(mdev, VP_BOT_C_PTR, chroma_addr[1]);
+
+ mxr_vsync_set_update(mdev, MXR_ENABLE);
+ spin_unlock_irqrestore(&mdev->reg_slock, flags);
+}
+
+static void mxr_irq_layer_handle(struct mxr_layer *layer)
+{
+ struct list_head *head = &layer->enq_list;
+ struct mxr_buffer *done;
+
+ /* skip non-existing layer */
+ if (layer == NULL)
+ return;
+
+ spin_lock(&layer->enq_slock);
+ if (layer->state == MXR_LAYER_IDLE)
+ goto done;
+
+ done = layer->shadow_buf;
+ layer->shadow_buf = layer->update_buf;
+
+ if (list_empty(head)) {
+ if (layer->state != MXR_LAYER_STREAMING)
+ layer->update_buf = NULL;
+ } else {
+ struct mxr_buffer *next;
+ next = list_first_entry(head, struct mxr_buffer, list);
+ list_del(&next->list);
+ layer->update_buf = next;
+ }
+
+ layer->ops.buffer_set(layer, layer->update_buf);
+
+ if (done && done != layer->shadow_buf)
+ vb2_buffer_done(&done->vb, VB2_BUF_STATE_DONE);
+
+done:
+ spin_unlock(&layer->enq_slock);
+}
+
+irqreturn_t mxr_irq_handler(int irq, void *dev_data)
+{
+ struct mxr_device *mdev = dev_data;
+ u32 i, val;
+
+ spin_lock(&mdev->reg_slock);
+ val = mxr_read(mdev, MXR_INT_STATUS);
+
+ /* wake up process waiting for VSYNC */
+ if (val & MXR_INT_STATUS_VSYNC) {
+ set_bit(MXR_EVENT_VSYNC, &mdev->event_flags);
+ wake_up(&mdev->event_queue);
+ }
+
+ /* clear interrupts */
+ if (~val & MXR_INT_EN_VSYNC) {
+ /* vsync interrupt use different bit for read and clear */
+ val &= ~MXR_INT_EN_VSYNC;
+ val |= MXR_INT_CLEAR_VSYNC;
+ }
+ mxr_write(mdev, MXR_INT_STATUS, val);
+
+ spin_unlock(&mdev->reg_slock);
+ /* leave on non-vsync event */
+ if (~val & MXR_INT_CLEAR_VSYNC)
+ return IRQ_HANDLED;
+ for (i = 0; i < MXR_MAX_LAYERS; ++i)
+ mxr_irq_layer_handle(mdev->layer[i]);
+ return IRQ_HANDLED;
+}
+
+void mxr_reg_s_output(struct mxr_device *mdev, int cookie)
+{
+ u32 val;
+
+ val = cookie == 0 ? MXR_CFG_DST_SDO : MXR_CFG_DST_HDMI;
+ mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_DST_MASK);
+}
+
+void mxr_reg_streamon(struct mxr_device *mdev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mdev->reg_slock, flags);
+ /* single write -> no need to block vsync update */
+
+ /* start MIXER */
+ mxr_write_mask(mdev, MXR_STATUS, ~0, MXR_STATUS_REG_RUN);
+
+ spin_unlock_irqrestore(&mdev->reg_slock, flags);
+}
+
+void mxr_reg_streamoff(struct mxr_device *mdev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mdev->reg_slock, flags);
+ /* single write -> no need to block vsync update */
+
+ /* stop MIXER */
+ mxr_write_mask(mdev, MXR_STATUS, 0, MXR_STATUS_REG_RUN);
+
+ spin_unlock_irqrestore(&mdev->reg_slock, flags);
+}
+
+int mxr_reg_wait4vsync(struct mxr_device *mdev)
+{
+ int ret;
+
+ clear_bit(MXR_EVENT_VSYNC, &mdev->event_flags);
+ /* TODO: consider adding interruptible */
+ ret = wait_event_timeout(mdev->event_queue,
+ test_bit(MXR_EVENT_VSYNC, &mdev->event_flags),
+ msecs_to_jiffies(1000));
+ if (ret > 0)
+ return 0;
+ if (ret < 0)
+ return ret;
+ mxr_warn(mdev, "no vsync detected - timeout\n");
+ return -ETIME;
+}
+
+void mxr_reg_set_mbus_fmt(struct mxr_device *mdev,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ u32 val = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mdev->reg_slock, flags);
+ mxr_vsync_set_update(mdev, MXR_DISABLE);
+
+ /* selecting colorspace accepted by output */
+ if (fmt->colorspace == V4L2_COLORSPACE_JPEG)
+ val |= MXR_CFG_OUT_YUV444;
+ else
+ val |= MXR_CFG_OUT_RGB888;
+
+ /* choosing between interlace and progressive mode */
+ if (fmt->field == V4L2_FIELD_INTERLACED)
+ val |= MXR_CFG_SCAN_INTERLACE;
+ else
+ val |= MXR_CFG_SCAN_PROGRASSIVE;
+
+ /* choosing between porper HD and SD mode */
+ if (fmt->height == 480)
+ val |= MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD;
+ else if (fmt->height == 576)
+ val |= MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD;
+ else if (fmt->height == 720)
+ val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
+ else if (fmt->height == 1080)
+ val |= MXR_CFG_SCAN_HD_1080 | MXR_CFG_SCAN_HD;
+ else
+ WARN(1, "unrecognized mbus height %u!\n", fmt->height);
+
+ mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_SCAN_MASK |
+ MXR_CFG_OUT_MASK);
+
+ val = (fmt->field == V4L2_FIELD_INTERLACED) ? ~0 : 0;
+ vp_write_mask(mdev, VP_MODE, val,
+ VP_MODE_LINE_SKIP | VP_MODE_FIELD_ID_AUTO_TOGGLING);
+
+ mxr_vsync_set_update(mdev, MXR_ENABLE);
+ spin_unlock_irqrestore(&mdev->reg_slock, flags);
+}
+
+void mxr_reg_graph_layer_stream(struct mxr_device *mdev, int idx, int en)
+{
+ /* no extra actions need to be done */
+}
+
+void mxr_reg_vp_layer_stream(struct mxr_device *mdev, int en)
+{
+ /* no extra actions need to be done */
+}
+
+static const u8 filter_y_horiz_tap8[] = {
+ 0, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 0, 0, 0,
+ 0, 2, 4, 5, 6, 6, 6, 6,
+ 6, 5, 5, 4, 3, 2, 1, 1,
+ 0, -6, -12, -16, -18, -20, -21, -20,
+ -20, -18, -16, -13, -10, -8, -5, -2,
+ 127, 126, 125, 121, 114, 107, 99, 89,
+ 79, 68, 57, 46, 35, 25, 16, 8,
+};
+
+static const u8 filter_y_vert_tap4[] = {
+ 0, -3, -6, -8, -8, -8, -8, -7,
+ -6, -5, -4, -3, -2, -1, -1, 0,
+ 127, 126, 124, 118, 111, 102, 92, 81,
+ 70, 59, 48, 37, 27, 19, 11, 5,
+ 0, 5, 11, 19, 27, 37, 48, 59,
+ 70, 81, 92, 102, 111, 118, 124, 126,
+ 0, 0, -1, -1, -2, -3, -4, -5,
+ -6, -7, -8, -8, -8, -8, -6, -3,
+};
+
+static const u8 filter_cr_horiz_tap4[] = {
+ 0, -3, -6, -8, -8, -8, -8, -7,
+ -6, -5, -4, -3, -2, -1, -1, 0,
+ 127, 126, 124, 118, 111, 102, 92, 81,
+ 70, 59, 48, 37, 27, 19, 11, 5,
+};
+
+static inline void mxr_reg_vp_filter_set(struct mxr_device *mdev,
+ int reg_id, const u8 *data, unsigned int size)
+{
+ /* assure 4-byte align */
+ BUG_ON(size & 3);
+ for (; size; size -= 4, reg_id += 4, data += 4) {
+ u32 val = (data[0] << 24) | (data[1] << 16) |
+ (data[2] << 8) | data[3];
+ vp_write(mdev, reg_id, val);
+ }
+}
+
+static void mxr_reg_vp_default_filter(struct mxr_device *mdev)
+{
+ mxr_reg_vp_filter_set(mdev, VP_POLY8_Y0_LL,
+ filter_y_horiz_tap8, sizeof filter_y_horiz_tap8);
+ mxr_reg_vp_filter_set(mdev, VP_POLY4_Y0_LL,
+ filter_y_vert_tap4, sizeof filter_y_vert_tap4);
+ mxr_reg_vp_filter_set(mdev, VP_POLY4_C0_LL,
+ filter_cr_horiz_tap4, sizeof filter_cr_horiz_tap4);
+}
+
+static void mxr_reg_mxr_dump(struct mxr_device *mdev)
+{
+#define DUMPREG(reg_id) \
+do { \
+ mxr_dbg(mdev, #reg_id " = %08x\n", \
+ (u32)readl(mdev->res.mxr_regs + reg_id)); \
+} while (0)
+
+ DUMPREG(MXR_STATUS);
+ DUMPREG(MXR_CFG);
+ DUMPREG(MXR_INT_EN);
+ DUMPREG(MXR_INT_STATUS);
+
+ DUMPREG(MXR_LAYER_CFG);
+ DUMPREG(MXR_VIDEO_CFG);
+
+ DUMPREG(MXR_GRAPHIC0_CFG);
+ DUMPREG(MXR_GRAPHIC0_BASE);
+ DUMPREG(MXR_GRAPHIC0_SPAN);
+ DUMPREG(MXR_GRAPHIC0_WH);
+ DUMPREG(MXR_GRAPHIC0_SXY);
+ DUMPREG(MXR_GRAPHIC0_DXY);
+
+ DUMPREG(MXR_GRAPHIC1_CFG);
+ DUMPREG(MXR_GRAPHIC1_BASE);
+ DUMPREG(MXR_GRAPHIC1_SPAN);
+ DUMPREG(MXR_GRAPHIC1_WH);
+ DUMPREG(MXR_GRAPHIC1_SXY);
+ DUMPREG(MXR_GRAPHIC1_DXY);
+#undef DUMPREG
+}
+
+static void mxr_reg_vp_dump(struct mxr_device *mdev)
+{
+#define DUMPREG(reg_id) \
+do { \
+ mxr_dbg(mdev, #reg_id " = %08x\n", \
+ (u32) readl(mdev->res.vp_regs + reg_id)); \
+} while (0)
+
+
+ DUMPREG(VP_ENABLE);
+ DUMPREG(VP_SRESET);
+ DUMPREG(VP_SHADOW_UPDATE);
+ DUMPREG(VP_FIELD_ID);
+ DUMPREG(VP_MODE);
+ DUMPREG(VP_IMG_SIZE_Y);
+ DUMPREG(VP_IMG_SIZE_C);
+ DUMPREG(VP_PER_RATE_CTRL);
+ DUMPREG(VP_TOP_Y_PTR);
+ DUMPREG(VP_BOT_Y_PTR);
+ DUMPREG(VP_TOP_C_PTR);
+ DUMPREG(VP_BOT_C_PTR);
+ DUMPREG(VP_ENDIAN_MODE);
+ DUMPREG(VP_SRC_H_POSITION);
+ DUMPREG(VP_SRC_V_POSITION);
+ DUMPREG(VP_SRC_WIDTH);
+ DUMPREG(VP_SRC_HEIGHT);
+ DUMPREG(VP_DST_H_POSITION);
+ DUMPREG(VP_DST_V_POSITION);
+ DUMPREG(VP_DST_WIDTH);
+ DUMPREG(VP_DST_HEIGHT);
+ DUMPREG(VP_H_RATIO);
+ DUMPREG(VP_V_RATIO);
+
+#undef DUMPREG
+}
+
+void mxr_reg_dump(struct mxr_device *mdev)
+{
+ mxr_reg_mxr_dump(mdev);
+ mxr_reg_vp_dump(mdev);
+}
+
diff --git a/drivers/media/video/s5p-tv/mixer_video.c b/drivers/media/video/s5p-tv/mixer_video.c
new file mode 100644
index 00000000..f7ca5cc1
--- /dev/null
+++ b/drivers/media/video/s5p-tv/mixer_video.c
@@ -0,0 +1,1108 @@
+/*
+ * Samsung TV Mixer driver
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * Tomasz Stanislawski, <t.stanislaws@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation. either version 2 of the License,
+ * or (at your option) any later version
+ */
+
+#include "mixer.h"
+
+#include <media/v4l2-ioctl.h>
+#include <linux/videodev2.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/timer.h>
+#include <media/videobuf2-dma-contig.h>
+
+static int find_reg_callback(struct device *dev, void *p)
+{
+ struct v4l2_subdev **sd = p;
+
+ *sd = dev_get_drvdata(dev);
+ /* non-zero value stops iteration */
+ return 1;
+}
+
+static struct v4l2_subdev *find_and_register_subdev(
+ struct mxr_device *mdev, char *module_name)
+{
+ struct device_driver *drv;
+ struct v4l2_subdev *sd = NULL;
+ int ret;
+
+ /* TODO: add waiting until probe is finished */
+ drv = driver_find(module_name, &platform_bus_type);
+ if (!drv) {
+ mxr_warn(mdev, "module %s is missing\n", module_name);
+ return NULL;
+ }
+ /* driver refcnt is increased, it is safe to iterate over devices */
+ ret = driver_for_each_device(drv, NULL, &sd, find_reg_callback);
+ /* ret == 0 means that find_reg_callback was never executed */
+ if (sd == NULL) {
+ mxr_warn(mdev, "module %s provides no subdev!\n", module_name);
+ goto done;
+ }
+ /* v4l2_device_register_subdev detects if sd is NULL */
+ ret = v4l2_device_register_subdev(&mdev->v4l2_dev, sd);
+ if (ret) {
+ mxr_warn(mdev, "failed to register subdev %s\n", sd->name);
+ sd = NULL;
+ }
+
+done:
+ return sd;
+}
+
+int __devinit mxr_acquire_video(struct mxr_device *mdev,
+ struct mxr_output_conf *output_conf, int output_count)
+{
+ struct device *dev = mdev->dev;
+ struct v4l2_device *v4l2_dev = &mdev->v4l2_dev;
+ int i;
+ int ret = 0;
+ struct v4l2_subdev *sd;
+
+ strlcpy(v4l2_dev->name, dev_name(mdev->dev), sizeof(v4l2_dev->name));
+ /* prepare context for V4L2 device */
+ ret = v4l2_device_register(dev, v4l2_dev);
+ if (ret) {
+ mxr_err(mdev, "could not register v4l2 device.\n");
+ goto fail;
+ }
+
+ mdev->alloc_ctx = vb2_dma_contig_init_ctx(mdev->dev);
+ if (IS_ERR_OR_NULL(mdev->alloc_ctx)) {
+ mxr_err(mdev, "could not acquire vb2 allocator\n");
+ goto fail_v4l2_dev;
+ }
+
+ /* registering outputs */
+ mdev->output_cnt = 0;
+ for (i = 0; i < output_count; ++i) {
+ struct mxr_output_conf *conf = &output_conf[i];
+ struct mxr_output *out;
+
+ sd = find_and_register_subdev(mdev, conf->module_name);
+ /* trying to register next output */
+ if (sd == NULL)
+ continue;
+ out = kzalloc(sizeof *out, GFP_KERNEL);
+ if (out == NULL) {
+ mxr_err(mdev, "no memory for '%s'\n",
+ conf->output_name);
+ ret = -ENOMEM;
+ /* registered subdevs are removed in fail_v4l2_dev */
+ goto fail_output;
+ }
+ strlcpy(out->name, conf->output_name, sizeof(out->name));
+ out->sd = sd;
+ out->cookie = conf->cookie;
+ mdev->output[mdev->output_cnt++] = out;
+ mxr_info(mdev, "added output '%s' from module '%s'\n",
+ conf->output_name, conf->module_name);
+ /* checking if maximal number of outputs is reached */
+ if (mdev->output_cnt >= MXR_MAX_OUTPUTS)
+ break;
+ }
+
+ if (mdev->output_cnt == 0) {
+ mxr_err(mdev, "failed to register any output\n");
+ ret = -ENODEV;
+ /* skipping fail_output because there is nothing to free */
+ goto fail_vb2_allocator;
+ }
+
+ return 0;
+
+fail_output:
+ /* kfree is NULL-safe */
+ for (i = 0; i < mdev->output_cnt; ++i)
+ kfree(mdev->output[i]);
+ memset(mdev->output, 0, sizeof mdev->output);
+
+fail_vb2_allocator:
+ /* freeing allocator context */
+ vb2_dma_contig_cleanup_ctx(mdev->alloc_ctx);
+
+fail_v4l2_dev:
+ /* NOTE: automatically unregister all subdevs */
+ v4l2_device_unregister(v4l2_dev);
+
+fail:
+ return ret;
+}
+
+void __devexit mxr_release_video(struct mxr_device *mdev)
+{
+ int i;
+
+ /* kfree is NULL-safe */
+ for (i = 0; i < mdev->output_cnt; ++i)
+ kfree(mdev->output[i]);
+
+ vb2_dma_contig_cleanup_ctx(mdev->alloc_ctx);
+ v4l2_device_unregister(&mdev->v4l2_dev);
+}
+
+static int mxr_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+
+ strlcpy(cap->driver, MXR_DRIVER_NAME, sizeof cap->driver);
+ strlcpy(cap->card, layer->vfd.name, sizeof cap->card);
+ sprintf(cap->bus_info, "%d", layer->idx);
+ cap->version = KERNEL_VERSION(0, 1, 0);
+ cap->capabilities = V4L2_CAP_STREAMING |
+ V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
+
+ return 0;
+}
+
+static void mxr_geometry_dump(struct mxr_device *mdev, struct mxr_geometry *geo)
+{
+ mxr_dbg(mdev, "src.full_size = (%u, %u)\n",
+ geo->src.full_width, geo->src.full_height);
+ mxr_dbg(mdev, "src.size = (%u, %u)\n",
+ geo->src.width, geo->src.height);
+ mxr_dbg(mdev, "src.offset = (%u, %u)\n",
+ geo->src.x_offset, geo->src.y_offset);
+ mxr_dbg(mdev, "dst.full_size = (%u, %u)\n",
+ geo->dst.full_width, geo->dst.full_height);
+ mxr_dbg(mdev, "dst.size = (%u, %u)\n",
+ geo->dst.width, geo->dst.height);
+ mxr_dbg(mdev, "dst.offset = (%u, %u)\n",
+ geo->dst.x_offset, geo->dst.y_offset);
+ mxr_dbg(mdev, "ratio = (%u, %u)\n",
+ geo->x_ratio, geo->y_ratio);
+}
+
+static void mxr_layer_default_geo(struct mxr_layer *layer)
+{
+ struct mxr_device *mdev = layer->mdev;
+ struct v4l2_mbus_framefmt mbus_fmt;
+
+ memset(&layer->geo, 0, sizeof layer->geo);
+
+ mxr_get_mbus_fmt(mdev, &mbus_fmt);
+
+ layer->geo.dst.full_width = mbus_fmt.width;
+ layer->geo.dst.full_height = mbus_fmt.height;
+ layer->geo.dst.width = layer->geo.dst.full_width;
+ layer->geo.dst.height = layer->geo.dst.full_height;
+ layer->geo.dst.field = mbus_fmt.field;
+
+ layer->geo.src.full_width = mbus_fmt.width;
+ layer->geo.src.full_height = mbus_fmt.height;
+ layer->geo.src.width = layer->geo.src.full_width;
+ layer->geo.src.height = layer->geo.src.full_height;
+
+ mxr_geometry_dump(mdev, &layer->geo);
+ layer->ops.fix_geometry(layer, MXR_GEOMETRY_SINK, 0);
+ mxr_geometry_dump(mdev, &layer->geo);
+}
+
+static void mxr_layer_update_output(struct mxr_layer *layer)
+{
+ struct mxr_device *mdev = layer->mdev;
+ struct v4l2_mbus_framefmt mbus_fmt;
+
+ mxr_get_mbus_fmt(mdev, &mbus_fmt);
+ /* checking if update is needed */
+ if (layer->geo.dst.full_width == mbus_fmt.width &&
+ layer->geo.dst.full_height == mbus_fmt.width)
+ return;
+
+ layer->geo.dst.full_width = mbus_fmt.width;
+ layer->geo.dst.full_height = mbus_fmt.height;
+ layer->geo.dst.field = mbus_fmt.field;
+ layer->ops.fix_geometry(layer, MXR_GEOMETRY_SINK, 0);
+
+ mxr_geometry_dump(mdev, &layer->geo);
+}
+
+static const struct mxr_format *find_format_by_fourcc(
+ struct mxr_layer *layer, unsigned long fourcc);
+static const struct mxr_format *find_format_by_index(
+ struct mxr_layer *layer, unsigned long index);
+
+static int mxr_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+ const struct mxr_format *fmt;
+
+ mxr_dbg(mdev, "%s\n", __func__);
+ fmt = find_format_by_index(layer, f->index);
+ if (fmt == NULL)
+ return -EINVAL;
+
+ strlcpy(f->description, fmt->name, sizeof(f->description));
+ f->pixelformat = fmt->fourcc;
+
+ return 0;
+}
+
+static unsigned int divup(unsigned int divident, unsigned int divisor)
+{
+ return (divident + divisor - 1) / divisor;
+}
+
+unsigned long mxr_get_plane_size(const struct mxr_block *blk,
+ unsigned int width, unsigned int height)
+{
+ unsigned int bl_width = divup(width, blk->width);
+ unsigned int bl_height = divup(height, blk->height);
+
+ return bl_width * bl_height * blk->size;
+}
+
+static void mxr_mplane_fill(struct v4l2_plane_pix_format *planes,
+ const struct mxr_format *fmt, u32 width, u32 height)
+{
+ int i;
+
+ /* checking if nothing to fill */
+ if (!planes)
+ return;
+
+ memset(planes, 0, sizeof(*planes) * fmt->num_subframes);
+ for (i = 0; i < fmt->num_planes; ++i) {
+ struct v4l2_plane_pix_format *plane = planes
+ + fmt->plane2subframe[i];
+ const struct mxr_block *blk = &fmt->plane[i];
+ u32 bl_width = divup(width, blk->width);
+ u32 bl_height = divup(height, blk->height);
+ u32 sizeimage = bl_width * bl_height * blk->size;
+ u16 bytesperline = bl_width * blk->size / blk->height;
+
+ plane->sizeimage += sizeimage;
+ plane->bytesperline = max(plane->bytesperline, bytesperline);
+ }
+}
+
+static int mxr_g_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+
+ pix->width = layer->geo.src.full_width;
+ pix->height = layer->geo.src.full_height;
+ pix->field = V4L2_FIELD_NONE;
+ pix->pixelformat = layer->fmt->fourcc;
+ pix->colorspace = layer->fmt->colorspace;
+ mxr_mplane_fill(pix->plane_fmt, layer->fmt, pix->width, pix->height);
+
+ return 0;
+}
+
+static int mxr_s_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ const struct mxr_format *fmt;
+ struct v4l2_pix_format_mplane *pix;
+ struct mxr_device *mdev = layer->mdev;
+ struct mxr_geometry *geo = &layer->geo;
+
+ mxr_dbg(mdev, "%s:%d\n", __func__, __LINE__);
+
+ pix = &f->fmt.pix_mp;
+ fmt = find_format_by_fourcc(layer, pix->pixelformat);
+ if (fmt == NULL) {
+ mxr_warn(mdev, "not recognized fourcc: %08x\n",
+ pix->pixelformat);
+ return -EINVAL;
+ }
+ layer->fmt = fmt;
+ /* set source size to highest accepted value */
+ geo->src.full_width = max(geo->dst.full_width, pix->width);
+ geo->src.full_height = max(geo->dst.full_height, pix->height);
+ layer->ops.fix_geometry(layer, MXR_GEOMETRY_SOURCE, 0);
+ mxr_geometry_dump(mdev, &layer->geo);
+ /* set cropping to total visible screen */
+ geo->src.width = pix->width;
+ geo->src.height = pix->height;
+ geo->src.x_offset = 0;
+ geo->src.y_offset = 0;
+ /* assure consistency of geometry */
+ layer->ops.fix_geometry(layer, MXR_GEOMETRY_CROP, MXR_NO_OFFSET);
+ mxr_geometry_dump(mdev, &layer->geo);
+ /* set full size to lowest possible value */
+ geo->src.full_width = 0;
+ geo->src.full_height = 0;
+ layer->ops.fix_geometry(layer, MXR_GEOMETRY_SOURCE, 0);
+ mxr_geometry_dump(mdev, &layer->geo);
+
+ /* returning results */
+ mxr_g_fmt(file, priv, f);
+
+ return 0;
+}
+
+static int mxr_g_selection(struct file *file, void *fh,
+ struct v4l2_selection *s)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_geometry *geo = &layer->geo;
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return -EINVAL;
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_CROP_ACTIVE:
+ s->r.left = geo->src.x_offset;
+ s->r.top = geo->src.y_offset;
+ s->r.width = geo->src.width;
+ s->r.height = geo->src.height;
+ break;
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = geo->src.full_width;
+ s->r.height = geo->src.full_height;
+ break;
+ case V4L2_SEL_TGT_COMPOSE_ACTIVE:
+ case V4L2_SEL_TGT_COMPOSE_PADDED:
+ s->r.left = geo->dst.x_offset;
+ s->r.top = geo->dst.y_offset;
+ s->r.width = geo->dst.width;
+ s->r.height = geo->dst.height;
+ break;
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = geo->dst.full_width;
+ s->r.height = geo->dst.full_height;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* returns 1 if rectangle 'a' is inside 'b' */
+static int mxr_is_rect_inside(struct v4l2_rect *a, struct v4l2_rect *b)
+{
+ if (a->left < b->left)
+ return 0;
+ if (a->top < b->top)
+ return 0;
+ if (a->left + a->width > b->left + b->width)
+ return 0;
+ if (a->top + a->height > b->top + b->height)
+ return 0;
+ return 1;
+}
+
+static int mxr_s_selection(struct file *file, void *fh,
+ struct v4l2_selection *s)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_geometry *geo = &layer->geo;
+ struct mxr_crop *target = NULL;
+ enum mxr_geometry_stage stage;
+ struct mxr_geometry tmp;
+ struct v4l2_rect res;
+
+ memset(&res, 0, sizeof res);
+
+ mxr_dbg(layer->mdev, "%s: rect: %dx%d@%d,%d\n", __func__,
+ s->r.width, s->r.height, s->r.left, s->r.top);
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return -EINVAL;
+
+ switch (s->target) {
+ /* ignore read-only targets */
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ res.width = geo->src.full_width;
+ res.height = geo->src.full_height;
+ break;
+
+ /* ignore read-only targets */
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ res.width = geo->dst.full_width;
+ res.height = geo->dst.full_height;
+ break;
+
+ case V4L2_SEL_TGT_CROP_ACTIVE:
+ target = &geo->src;
+ stage = MXR_GEOMETRY_CROP;
+ break;
+ case V4L2_SEL_TGT_COMPOSE_ACTIVE:
+ case V4L2_SEL_TGT_COMPOSE_PADDED:
+ target = &geo->dst;
+ stage = MXR_GEOMETRY_COMPOSE;
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* apply change and update geometry if needed */
+ if (target) {
+ /* backup current geometry if setup fails */
+ memcpy(&tmp, geo, sizeof tmp);
+
+ /* apply requested selection */
+ target->x_offset = s->r.left;
+ target->y_offset = s->r.top;
+ target->width = s->r.width;
+ target->height = s->r.height;
+
+ layer->ops.fix_geometry(layer, stage, s->flags);
+
+ /* retrieve update selection rectangle */
+ res.left = target->x_offset;
+ res.top = target->y_offset;
+ res.width = target->width;
+ res.height = target->height;
+
+ mxr_geometry_dump(layer->mdev, &layer->geo);
+ }
+
+ /* checking if the rectangle satisfies constraints */
+ if ((s->flags & V4L2_SEL_FLAG_LE) && !mxr_is_rect_inside(&res, &s->r))
+ goto fail;
+ if ((s->flags & V4L2_SEL_FLAG_GE) && !mxr_is_rect_inside(&s->r, &res))
+ goto fail;
+
+ /* return result rectangle */
+ s->r = res;
+
+ return 0;
+fail:
+ /* restore old geometry, which is not touched if target is NULL */
+ if (target)
+ memcpy(geo, &tmp, sizeof tmp);
+ return -ERANGE;
+}
+
+static int mxr_enum_dv_presets(struct file *file, void *fh,
+ struct v4l2_dv_enum_preset *preset)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+ int ret;
+
+ /* lock protects from changing sd_out */
+ mutex_lock(&mdev->mutex);
+ ret = v4l2_subdev_call(to_outsd(mdev), video, enum_dv_presets, preset);
+ mutex_unlock(&mdev->mutex);
+
+ return ret ? -EINVAL : 0;
+}
+
+static int mxr_s_dv_preset(struct file *file, void *fh,
+ struct v4l2_dv_preset *preset)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+ int ret;
+
+ /* lock protects from changing sd_out */
+ mutex_lock(&mdev->mutex);
+
+ /* preset change cannot be done while there is an entity
+ * dependant on output configuration
+ */
+ if (mdev->n_output > 0) {
+ mutex_unlock(&mdev->mutex);
+ return -EBUSY;
+ }
+
+ ret = v4l2_subdev_call(to_outsd(mdev), video, s_dv_preset, preset);
+
+ mutex_unlock(&mdev->mutex);
+
+ mxr_layer_update_output(layer);
+
+ /* any failure should return EINVAL according to V4L2 doc */
+ return ret ? -EINVAL : 0;
+}
+
+static int mxr_g_dv_preset(struct file *file, void *fh,
+ struct v4l2_dv_preset *preset)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+ int ret;
+
+ /* lock protects from changing sd_out */
+ mutex_lock(&mdev->mutex);
+ ret = v4l2_subdev_call(to_outsd(mdev), video, g_dv_preset, preset);
+ mutex_unlock(&mdev->mutex);
+
+ return ret ? -EINVAL : 0;
+}
+
+static int mxr_s_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+ int ret;
+
+ /* lock protects from changing sd_out */
+ mutex_lock(&mdev->mutex);
+
+ /* standard change cannot be done while there is an entity
+ * dependant on output configuration
+ */
+ if (mdev->n_output > 0) {
+ mutex_unlock(&mdev->mutex);
+ return -EBUSY;
+ }
+
+ ret = v4l2_subdev_call(to_outsd(mdev), video, s_std_output, *norm);
+
+ mutex_unlock(&mdev->mutex);
+
+ mxr_layer_update_output(layer);
+
+ return ret ? -EINVAL : 0;
+}
+
+static int mxr_g_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+ int ret;
+
+ /* lock protects from changing sd_out */
+ mutex_lock(&mdev->mutex);
+ ret = v4l2_subdev_call(to_outsd(mdev), video, g_std_output, norm);
+ mutex_unlock(&mdev->mutex);
+
+ return ret ? -EINVAL : 0;
+}
+
+static int mxr_enum_output(struct file *file, void *fh, struct v4l2_output *a)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+ struct mxr_output *out;
+ struct v4l2_subdev *sd;
+
+ if (a->index >= mdev->output_cnt)
+ return -EINVAL;
+ out = mdev->output[a->index];
+ BUG_ON(out == NULL);
+ sd = out->sd;
+ strlcpy(a->name, out->name, sizeof(a->name));
+
+ /* try to obtain supported tv norms */
+ v4l2_subdev_call(sd, video, g_tvnorms_output, &a->std);
+ a->capabilities = 0;
+ if (sd->ops->video && sd->ops->video->s_dv_preset)
+ a->capabilities |= V4L2_OUT_CAP_PRESETS;
+ if (sd->ops->video && sd->ops->video->s_std_output)
+ a->capabilities |= V4L2_OUT_CAP_STD;
+ a->type = V4L2_OUTPUT_TYPE_ANALOG;
+
+ return 0;
+}
+
+static int mxr_s_output(struct file *file, void *fh, unsigned int i)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+
+ if (i >= mdev->output_cnt || mdev->output[i] == NULL)
+ return -EINVAL;
+
+ mutex_lock(&mdev->mutex);
+ if (mdev->n_output > 0) {
+ mutex_unlock(&mdev->mutex);
+ return -EBUSY;
+ }
+ mdev->current_output = i;
+ vfd->tvnorms = 0;
+ v4l2_subdev_call(to_outsd(mdev), video, g_tvnorms_output,
+ &vfd->tvnorms);
+ mutex_unlock(&mdev->mutex);
+
+ /* update layers geometry */
+ mxr_layer_update_output(layer);
+
+ mxr_dbg(mdev, "tvnorms = %08llx\n", vfd->tvnorms);
+
+ return 0;
+}
+
+static int mxr_g_output(struct file *file, void *fh, unsigned int *p)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+
+ mutex_lock(&mdev->mutex);
+ *p = mdev->current_output;
+ mutex_unlock(&mdev->mutex);
+
+ return 0;
+}
+
+static int mxr_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+ return vb2_reqbufs(&layer->vb_queue, p);
+}
+
+static int mxr_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+ return vb2_querybuf(&layer->vb_queue, p);
+}
+
+static int mxr_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d(%d)\n", __func__, __LINE__, p->index);
+ return vb2_qbuf(&layer->vb_queue, p);
+}
+
+static int mxr_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+ return vb2_dqbuf(&layer->vb_queue, p, file->f_flags & O_NONBLOCK);
+}
+
+static int mxr_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+ return vb2_streamon(&layer->vb_queue, i);
+}
+
+static int mxr_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+ return vb2_streamoff(&layer->vb_queue, i);
+}
+
+static const struct v4l2_ioctl_ops mxr_ioctl_ops = {
+ .vidioc_querycap = mxr_querycap,
+ /* format handling */
+ .vidioc_enum_fmt_vid_out = mxr_enum_fmt,
+ .vidioc_s_fmt_vid_out_mplane = mxr_s_fmt,
+ .vidioc_g_fmt_vid_out_mplane = mxr_g_fmt,
+ /* buffer control */
+ .vidioc_reqbufs = mxr_reqbufs,
+ .vidioc_querybuf = mxr_querybuf,
+ .vidioc_qbuf = mxr_qbuf,
+ .vidioc_dqbuf = mxr_dqbuf,
+ /* Streaming control */
+ .vidioc_streamon = mxr_streamon,
+ .vidioc_streamoff = mxr_streamoff,
+ /* Preset functions */
+ .vidioc_enum_dv_presets = mxr_enum_dv_presets,
+ .vidioc_s_dv_preset = mxr_s_dv_preset,
+ .vidioc_g_dv_preset = mxr_g_dv_preset,
+ /* analog TV standard functions */
+ .vidioc_s_std = mxr_s_std,
+ .vidioc_g_std = mxr_g_std,
+ /* Output handling */
+ .vidioc_enum_output = mxr_enum_output,
+ .vidioc_s_output = mxr_s_output,
+ .vidioc_g_output = mxr_g_output,
+ /* selection ioctls */
+ .vidioc_g_selection = mxr_g_selection,
+ .vidioc_s_selection = mxr_s_selection,
+};
+
+static int mxr_video_open(struct file *file)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+ int ret = 0;
+
+ mxr_dbg(mdev, "%s:%d\n", __func__, __LINE__);
+ /* assure device probe is finished */
+ wait_for_device_probe();
+ /* creating context for file descriptor */
+ ret = v4l2_fh_open(file);
+ if (ret) {
+ mxr_err(mdev, "v4l2_fh_open failed\n");
+ return ret;
+ }
+
+ /* leaving if layer is already initialized */
+ if (!v4l2_fh_is_singular_file(file))
+ return 0;
+
+ /* FIXME: should power be enabled on open? */
+ ret = mxr_power_get(mdev);
+ if (ret) {
+ mxr_err(mdev, "power on failed\n");
+ goto fail_fh_open;
+ }
+
+ ret = vb2_queue_init(&layer->vb_queue);
+ if (ret != 0) {
+ mxr_err(mdev, "failed to initialize vb2 queue\n");
+ goto fail_power;
+ }
+ /* set default format, first on the list */
+ layer->fmt = layer->fmt_array[0];
+ /* setup default geometry */
+ mxr_layer_default_geo(layer);
+
+ return 0;
+
+fail_power:
+ mxr_power_put(mdev);
+
+fail_fh_open:
+ v4l2_fh_release(file);
+
+ return ret;
+}
+
+static unsigned int
+mxr_video_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+
+ return vb2_poll(&layer->vb_queue, file, wait);
+}
+
+static int mxr_video_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+
+ return vb2_mmap(&layer->vb_queue, vma);
+}
+
+static int mxr_video_release(struct file *file)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+ if (v4l2_fh_is_singular_file(file)) {
+ vb2_queue_release(&layer->vb_queue);
+ mxr_power_put(layer->mdev);
+ }
+ v4l2_fh_release(file);
+ return 0;
+}
+
+static const struct v4l2_file_operations mxr_fops = {
+ .owner = THIS_MODULE,
+ .open = mxr_video_open,
+ .poll = mxr_video_poll,
+ .mmap = mxr_video_mmap,
+ .release = mxr_video_release,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt,
+ unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[],
+ void *alloc_ctxs[])
+{
+ struct mxr_layer *layer = vb2_get_drv_priv(vq);
+ const struct mxr_format *fmt = layer->fmt;
+ int i;
+ struct mxr_device *mdev = layer->mdev;
+ struct v4l2_plane_pix_format planes[3];
+
+ mxr_dbg(mdev, "%s\n", __func__);
+ /* checking if format was configured */
+ if (fmt == NULL)
+ return -EINVAL;
+ mxr_dbg(mdev, "fmt = %s\n", fmt->name);
+ mxr_mplane_fill(planes, fmt, layer->geo.src.full_width,
+ layer->geo.src.full_height);
+
+ *nplanes = fmt->num_subframes;
+ for (i = 0; i < fmt->num_subframes; ++i) {
+ alloc_ctxs[i] = layer->mdev->alloc_ctx;
+ sizes[i] = PAGE_ALIGN(planes[i].sizeimage);
+ mxr_dbg(mdev, "size[%d] = %08lx\n", i, sizes[i]);
+ }
+
+ if (*nbuffers == 0)
+ *nbuffers = 1;
+
+ return 0;
+}
+
+static void buf_queue(struct vb2_buffer *vb)
+{
+ struct mxr_buffer *buffer = container_of(vb, struct mxr_buffer, vb);
+ struct mxr_layer *layer = vb2_get_drv_priv(vb->vb2_queue);
+ struct mxr_device *mdev = layer->mdev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&layer->enq_slock, flags);
+ list_add_tail(&buffer->list, &layer->enq_list);
+ spin_unlock_irqrestore(&layer->enq_slock, flags);
+
+ mxr_dbg(mdev, "queuing buffer\n");
+}
+
+static void wait_lock(struct vb2_queue *vq)
+{
+ struct mxr_layer *layer = vb2_get_drv_priv(vq);
+
+ mxr_dbg(layer->mdev, "%s\n", __func__);
+ mutex_lock(&layer->mutex);
+}
+
+static void wait_unlock(struct vb2_queue *vq)
+{
+ struct mxr_layer *layer = vb2_get_drv_priv(vq);
+
+ mxr_dbg(layer->mdev, "%s\n", __func__);
+ mutex_unlock(&layer->mutex);
+}
+
+static int start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct mxr_layer *layer = vb2_get_drv_priv(vq);
+ struct mxr_device *mdev = layer->mdev;
+ unsigned long flags;
+
+ mxr_dbg(mdev, "%s\n", __func__);
+
+ if (count == 0) {
+ mxr_dbg(mdev, "no output buffers queued\n");
+ return -EINVAL;
+ }
+
+ /* block any changes in output configuration */
+ mxr_output_get(mdev);
+
+ mxr_layer_update_output(layer);
+ layer->ops.format_set(layer);
+ /* enabling layer in hardware */
+ spin_lock_irqsave(&layer->enq_slock, flags);
+ layer->state = MXR_LAYER_STREAMING;
+ spin_unlock_irqrestore(&layer->enq_slock, flags);
+
+ layer->ops.stream_set(layer, MXR_ENABLE);
+ mxr_streamer_get(mdev);
+
+ return 0;
+}
+
+static void mxr_watchdog(unsigned long arg)
+{
+ struct mxr_layer *layer = (struct mxr_layer *) arg;
+ struct mxr_device *mdev = layer->mdev;
+ unsigned long flags;
+
+ mxr_err(mdev, "watchdog fired for layer %s\n", layer->vfd.name);
+
+ spin_lock_irqsave(&layer->enq_slock, flags);
+
+ if (layer->update_buf == layer->shadow_buf)
+ layer->update_buf = NULL;
+ if (layer->update_buf) {
+ vb2_buffer_done(&layer->update_buf->vb, VB2_BUF_STATE_ERROR);
+ layer->update_buf = NULL;
+ }
+ if (layer->shadow_buf) {
+ vb2_buffer_done(&layer->shadow_buf->vb, VB2_BUF_STATE_ERROR);
+ layer->shadow_buf = NULL;
+ }
+ spin_unlock_irqrestore(&layer->enq_slock, flags);
+}
+
+static int stop_streaming(struct vb2_queue *vq)
+{
+ struct mxr_layer *layer = vb2_get_drv_priv(vq);
+ struct mxr_device *mdev = layer->mdev;
+ unsigned long flags;
+ struct timer_list watchdog;
+ struct mxr_buffer *buf, *buf_tmp;
+
+ mxr_dbg(mdev, "%s\n", __func__);
+
+ spin_lock_irqsave(&layer->enq_slock, flags);
+
+ /* reset list */
+ layer->state = MXR_LAYER_STREAMING_FINISH;
+
+ /* set all buffer to be done */
+ list_for_each_entry_safe(buf, buf_tmp, &layer->enq_list, list) {
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+
+ spin_unlock_irqrestore(&layer->enq_slock, flags);
+
+ /* give 1 seconds to complete to complete last buffers */
+ setup_timer_on_stack(&watchdog, mxr_watchdog,
+ (unsigned long)layer);
+ mod_timer(&watchdog, jiffies + msecs_to_jiffies(1000));
+
+ /* wait until all buffers are goes to done state */
+ vb2_wait_for_all_buffers(vq);
+
+ /* stop timer if all synchronization is done */
+ del_timer_sync(&watchdog);
+ destroy_timer_on_stack(&watchdog);
+
+ /* stopping hardware */
+ spin_lock_irqsave(&layer->enq_slock, flags);
+ layer->state = MXR_LAYER_IDLE;
+ spin_unlock_irqrestore(&layer->enq_slock, flags);
+
+ /* disabling layer in hardware */
+ layer->ops.stream_set(layer, MXR_DISABLE);
+ /* remove one streamer */
+ mxr_streamer_put(mdev);
+ /* allow changes in output configuration */
+ mxr_output_put(mdev);
+ return 0;
+}
+
+static struct vb2_ops mxr_video_qops = {
+ .queue_setup = queue_setup,
+ .buf_queue = buf_queue,
+ .wait_prepare = wait_unlock,
+ .wait_finish = wait_lock,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
+};
+
+/* FIXME: try to put this functions to mxr_base_layer_create */
+int mxr_base_layer_register(struct mxr_layer *layer)
+{
+ struct mxr_device *mdev = layer->mdev;
+ int ret;
+
+ ret = video_register_device(&layer->vfd, VFL_TYPE_GRABBER, -1);
+ if (ret)
+ mxr_err(mdev, "failed to register video device\n");
+ else
+ mxr_info(mdev, "registered layer %s as /dev/video%d\n",
+ layer->vfd.name, layer->vfd.num);
+ return ret;
+}
+
+void mxr_base_layer_unregister(struct mxr_layer *layer)
+{
+ video_unregister_device(&layer->vfd);
+}
+
+void mxr_layer_release(struct mxr_layer *layer)
+{
+ if (layer->ops.release)
+ layer->ops.release(layer);
+}
+
+void mxr_base_layer_release(struct mxr_layer *layer)
+{
+ kfree(layer);
+}
+
+static void mxr_vfd_release(struct video_device *vdev)
+{
+ printk(KERN_INFO "video device release\n");
+}
+
+struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev,
+ int idx, char *name, struct mxr_layer_ops *ops)
+{
+ struct mxr_layer *layer;
+
+ layer = kzalloc(sizeof *layer, GFP_KERNEL);
+ if (layer == NULL) {
+ mxr_err(mdev, "not enough memory for layer.\n");
+ goto fail;
+ }
+
+ layer->mdev = mdev;
+ layer->idx = idx;
+ layer->ops = *ops;
+
+ spin_lock_init(&layer->enq_slock);
+ INIT_LIST_HEAD(&layer->enq_list);
+ mutex_init(&layer->mutex);
+
+ layer->vfd = (struct video_device) {
+ .minor = -1,
+ .release = mxr_vfd_release,
+ .fops = &mxr_fops,
+ .ioctl_ops = &mxr_ioctl_ops,
+ };
+ strlcpy(layer->vfd.name, name, sizeof(layer->vfd.name));
+ /* let framework control PRIORITY */
+ set_bit(V4L2_FL_USE_FH_PRIO, &layer->vfd.flags);
+
+ video_set_drvdata(&layer->vfd, layer);
+ layer->vfd.lock = &layer->mutex;
+ layer->vfd.v4l2_dev = &mdev->v4l2_dev;
+
+ layer->vb_queue = (struct vb2_queue) {
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ .io_modes = VB2_MMAP | VB2_USERPTR,
+ .drv_priv = layer,
+ .buf_struct_size = sizeof(struct mxr_buffer),
+ .ops = &mxr_video_qops,
+ .mem_ops = &vb2_dma_contig_memops,
+ };
+
+ return layer;
+
+fail:
+ return NULL;
+}
+
+static const struct mxr_format *find_format_by_fourcc(
+ struct mxr_layer *layer, unsigned long fourcc)
+{
+ int i;
+
+ for (i = 0; i < layer->fmt_array_size; ++i)
+ if (layer->fmt_array[i]->fourcc == fourcc)
+ return layer->fmt_array[i];
+ return NULL;
+}
+
+static const struct mxr_format *find_format_by_index(
+ struct mxr_layer *layer, unsigned long index)
+{
+ if (index >= layer->fmt_array_size)
+ return NULL;
+ return layer->fmt_array[index];
+}
+
diff --git a/drivers/media/video/s5p-tv/mixer_vp_layer.c b/drivers/media/video/s5p-tv/mixer_vp_layer.c
new file mode 100644
index 00000000..3d13a636
--- /dev/null
+++ b/drivers/media/video/s5p-tv/mixer_vp_layer.c
@@ -0,0 +1,241 @@
+/*
+ * Samsung TV Mixer driver
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * Tomasz Stanislawski, <t.stanislaws@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundiation. either version 2 of the License,
+ * or (at your option) any later version
+ */
+
+#include "mixer.h"
+
+#include "regs-vp.h"
+
+#include <media/videobuf2-dma-contig.h>
+
+/* FORMAT DEFINITIONS */
+static const struct mxr_format mxr_fmt_nv12 = {
+ .name = "NV12",
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .num_planes = 2,
+ .plane = {
+ { .width = 1, .height = 1, .size = 1 },
+ { .width = 2, .height = 2, .size = 2 },
+ },
+ .num_subframes = 1,
+ .cookie = VP_MODE_NV12 | VP_MODE_MEM_LINEAR,
+};
+
+static const struct mxr_format mxr_fmt_nv21 = {
+ .name = "NV21",
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .num_planes = 2,
+ .plane = {
+ { .width = 1, .height = 1, .size = 1 },
+ { .width = 2, .height = 2, .size = 2 },
+ },
+ .num_subframes = 1,
+ .cookie = VP_MODE_NV21 | VP_MODE_MEM_LINEAR,
+};
+
+static const struct mxr_format mxr_fmt_nv12m = {
+ .name = "NV12 (mplane)",
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .num_planes = 2,
+ .plane = {
+ { .width = 1, .height = 1, .size = 1 },
+ { .width = 2, .height = 2, .size = 2 },
+ },
+ .num_subframes = 2,
+ .plane2subframe = {0, 1},
+ .cookie = VP_MODE_NV12 | VP_MODE_MEM_LINEAR,
+};
+
+static const struct mxr_format mxr_fmt_nv12mt = {
+ .name = "NV12 tiled (mplane)",
+ .fourcc = V4L2_PIX_FMT_NV12MT,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .num_planes = 2,
+ .plane = {
+ { .width = 128, .height = 32, .size = 4096 },
+ { .width = 128, .height = 32, .size = 2048 },
+ },
+ .num_subframes = 2,
+ .plane2subframe = {0, 1},
+ .cookie = VP_MODE_NV12 | VP_MODE_MEM_TILED,
+};
+
+static const struct mxr_format *mxr_video_format[] = {
+ &mxr_fmt_nv12,
+ &mxr_fmt_nv21,
+ &mxr_fmt_nv12m,
+ &mxr_fmt_nv12mt,
+};
+
+/* AUXILIARY CALLBACKS */
+
+static void mxr_vp_layer_release(struct mxr_layer *layer)
+{
+ mxr_base_layer_unregister(layer);
+ mxr_base_layer_release(layer);
+}
+
+static void mxr_vp_buffer_set(struct mxr_layer *layer,
+ struct mxr_buffer *buf)
+{
+ dma_addr_t luma_addr[2] = {0, 0};
+ dma_addr_t chroma_addr[2] = {0, 0};
+
+ if (buf == NULL) {
+ mxr_reg_vp_buffer(layer->mdev, luma_addr, chroma_addr);
+ return;
+ }
+ luma_addr[0] = vb2_dma_contig_plane_dma_addr(&buf->vb, 0);
+ if (layer->fmt->num_subframes == 2) {
+ chroma_addr[0] = vb2_dma_contig_plane_dma_addr(&buf->vb, 1);
+ } else {
+ /* FIXME: mxr_get_plane_size compute integer division,
+ * which is slow and should not be performed in interrupt */
+ chroma_addr[0] = luma_addr[0] + mxr_get_plane_size(
+ &layer->fmt->plane[0], layer->geo.src.full_width,
+ layer->geo.src.full_height);
+ }
+ if (layer->fmt->cookie & VP_MODE_MEM_TILED) {
+ luma_addr[1] = luma_addr[0] + 0x40;
+ chroma_addr[1] = chroma_addr[0] + 0x40;
+ } else {
+ luma_addr[1] = luma_addr[0] + layer->geo.src.full_width;
+ chroma_addr[1] = chroma_addr[0];
+ }
+ mxr_reg_vp_buffer(layer->mdev, luma_addr, chroma_addr);
+}
+
+static void mxr_vp_stream_set(struct mxr_layer *layer, int en)
+{
+ mxr_reg_vp_layer_stream(layer->mdev, en);
+}
+
+static void mxr_vp_format_set(struct mxr_layer *layer)
+{
+ mxr_reg_vp_format(layer->mdev, layer->fmt, &layer->geo);
+}
+
+static inline unsigned int do_center(unsigned int center,
+ unsigned int size, unsigned int upper, unsigned int flags)
+{
+ unsigned int lower;
+
+ if (flags & MXR_NO_OFFSET)
+ return 0;
+
+ lower = center - min(center, size / 2);
+ return min(lower, upper - size);
+}
+
+static void mxr_vp_fix_geometry(struct mxr_layer *layer,
+ enum mxr_geometry_stage stage, unsigned long flags)
+{
+ struct mxr_geometry *geo = &layer->geo;
+ struct mxr_crop *src = &geo->src;
+ struct mxr_crop *dst = &geo->dst;
+ unsigned long x_center, y_center;
+
+ switch (stage) {
+
+ case MXR_GEOMETRY_SINK: /* nothing to be fixed here */
+ case MXR_GEOMETRY_COMPOSE:
+ /* remember center of the area */
+ x_center = dst->x_offset + dst->width / 2;
+ y_center = dst->y_offset + dst->height / 2;
+
+ /* ensure that compose is reachable using 16x scaling */
+ dst->width = clamp(dst->width, 8U, 16 * src->full_width);
+ dst->height = clamp(dst->height, 1U, 16 * src->full_height);
+
+ /* setup offsets */
+ dst->x_offset = do_center(x_center, dst->width,
+ dst->full_width, flags);
+ dst->y_offset = do_center(y_center, dst->height,
+ dst->full_height, flags);
+ flags = 0; /* remove possible MXR_NO_OFFSET flag */
+ /* fall through */
+ case MXR_GEOMETRY_CROP:
+ /* remember center of the area */
+ x_center = src->x_offset + src->width / 2;
+ y_center = src->y_offset + src->height / 2;
+
+ /* ensure scaling is between 0.25x .. 16x */
+ src->width = clamp(src->width, round_up(dst->width / 16, 4),
+ dst->width * 4);
+ src->height = clamp(src->height, round_up(dst->height / 16, 4),
+ dst->height * 4);
+
+ /* hardware limits */
+ src->width = clamp(src->width, 32U, 2047U);
+ src->height = clamp(src->height, 4U, 2047U);
+
+ /* setup offsets */
+ src->x_offset = do_center(x_center, src->width,
+ src->full_width, flags);
+ src->y_offset = do_center(y_center, src->height,
+ src->full_height, flags);
+
+ /* setting scaling ratio */
+ geo->x_ratio = (src->width << 16) / dst->width;
+ geo->y_ratio = (src->height << 16) / dst->height;
+ /* fall through */
+
+ case MXR_GEOMETRY_SOURCE:
+ src->full_width = clamp(src->full_width,
+ ALIGN(src->width + src->x_offset, 8), 8192U);
+ src->full_height = clamp(src->full_height,
+ src->height + src->y_offset, 8192U);
+ };
+}
+
+/* PUBLIC API */
+
+struct mxr_layer *mxr_vp_layer_create(struct mxr_device *mdev, int idx)
+{
+ struct mxr_layer *layer;
+ int ret;
+ struct mxr_layer_ops ops = {
+ .release = mxr_vp_layer_release,
+ .buffer_set = mxr_vp_buffer_set,
+ .stream_set = mxr_vp_stream_set,
+ .format_set = mxr_vp_format_set,
+ .fix_geometry = mxr_vp_fix_geometry,
+ };
+ char name[32];
+
+ sprintf(name, "video%d", idx);
+
+ layer = mxr_base_layer_create(mdev, idx, name, &ops);
+ if (layer == NULL) {
+ mxr_err(mdev, "failed to initialize layer(%d) base\n", idx);
+ goto fail;
+ }
+
+ layer->fmt_array = mxr_video_format;
+ layer->fmt_array_size = ARRAY_SIZE(mxr_video_format);
+
+ ret = mxr_base_layer_register(layer);
+ if (ret)
+ goto fail_layer;
+
+ return layer;
+
+fail_layer:
+ mxr_base_layer_release(layer);
+
+fail:
+ return NULL;
+}
+
diff --git a/drivers/media/video/s5p-tv/regs-hdmi.h b/drivers/media/video/s5p-tv/regs-hdmi.h
new file mode 100644
index 00000000..33247d13
--- /dev/null
+++ b/drivers/media/video/s5p-tv/regs-hdmi.h
@@ -0,0 +1,145 @@
+/* linux/arch/arm/mach-exynos4/include/mach/regs-hdmi.h
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * HDMI register header file for Samsung TVOUT driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef SAMSUNG_REGS_HDMI_H
+#define SAMSUNG_REGS_HDMI_H
+
+/*
+ * Register part
+*/
+
+#define HDMI_CTRL_BASE(x) ((x) + 0x00000000)
+#define HDMI_CORE_BASE(x) ((x) + 0x00010000)
+#define HDMI_TG_BASE(x) ((x) + 0x00050000)
+
+/* Control registers */
+#define HDMI_INTC_CON HDMI_CTRL_BASE(0x0000)
+#define HDMI_INTC_FLAG HDMI_CTRL_BASE(0x0004)
+#define HDMI_HPD_STATUS HDMI_CTRL_BASE(0x000C)
+#define HDMI_PHY_RSTOUT HDMI_CTRL_BASE(0x0014)
+#define HDMI_PHY_VPLL HDMI_CTRL_BASE(0x0018)
+#define HDMI_PHY_CMU HDMI_CTRL_BASE(0x001C)
+#define HDMI_CORE_RSTOUT HDMI_CTRL_BASE(0x0020)
+
+/* Core registers */
+#define HDMI_CON_0 HDMI_CORE_BASE(0x0000)
+#define HDMI_CON_1 HDMI_CORE_BASE(0x0004)
+#define HDMI_CON_2 HDMI_CORE_BASE(0x0008)
+#define HDMI_SYS_STATUS HDMI_CORE_BASE(0x0010)
+#define HDMI_PHY_STATUS HDMI_CORE_BASE(0x0014)
+#define HDMI_STATUS_EN HDMI_CORE_BASE(0x0020)
+#define HDMI_HPD HDMI_CORE_BASE(0x0030)
+#define HDMI_MODE_SEL HDMI_CORE_BASE(0x0040)
+#define HDMI_BLUE_SCREEN_0 HDMI_CORE_BASE(0x0050)
+#define HDMI_BLUE_SCREEN_1 HDMI_CORE_BASE(0x0054)
+#define HDMI_BLUE_SCREEN_2 HDMI_CORE_BASE(0x0058)
+#define HDMI_H_BLANK_0 HDMI_CORE_BASE(0x00A0)
+#define HDMI_H_BLANK_1 HDMI_CORE_BASE(0x00A4)
+#define HDMI_V_BLANK_0 HDMI_CORE_BASE(0x00B0)
+#define HDMI_V_BLANK_1 HDMI_CORE_BASE(0x00B4)
+#define HDMI_V_BLANK_2 HDMI_CORE_BASE(0x00B8)
+#define HDMI_H_V_LINE_0 HDMI_CORE_BASE(0x00C0)
+#define HDMI_H_V_LINE_1 HDMI_CORE_BASE(0x00C4)
+#define HDMI_H_V_LINE_2 HDMI_CORE_BASE(0x00C8)
+#define HDMI_VSYNC_POL HDMI_CORE_BASE(0x00E4)
+#define HDMI_INT_PRO_MODE HDMI_CORE_BASE(0x00E8)
+#define HDMI_V_BLANK_F_0 HDMI_CORE_BASE(0x0110)
+#define HDMI_V_BLANK_F_1 HDMI_CORE_BASE(0x0114)
+#define HDMI_V_BLANK_F_2 HDMI_CORE_BASE(0x0118)
+#define HDMI_H_SYNC_GEN_0 HDMI_CORE_BASE(0x0120)
+#define HDMI_H_SYNC_GEN_1 HDMI_CORE_BASE(0x0124)
+#define HDMI_H_SYNC_GEN_2 HDMI_CORE_BASE(0x0128)
+#define HDMI_V_SYNC_GEN_1_0 HDMI_CORE_BASE(0x0130)
+#define HDMI_V_SYNC_GEN_1_1 HDMI_CORE_BASE(0x0134)
+#define HDMI_V_SYNC_GEN_1_2 HDMI_CORE_BASE(0x0138)
+#define HDMI_V_SYNC_GEN_2_0 HDMI_CORE_BASE(0x0140)
+#define HDMI_V_SYNC_GEN_2_1 HDMI_CORE_BASE(0x0144)
+#define HDMI_V_SYNC_GEN_2_2 HDMI_CORE_BASE(0x0148)
+#define HDMI_V_SYNC_GEN_3_0 HDMI_CORE_BASE(0x0150)
+#define HDMI_V_SYNC_GEN_3_1 HDMI_CORE_BASE(0x0154)
+#define HDMI_V_SYNC_GEN_3_2 HDMI_CORE_BASE(0x0158)
+#define HDMI_AVI_CON HDMI_CORE_BASE(0x0300)
+#define HDMI_AVI_BYTE(n) HDMI_CORE_BASE(0x0320 + 4 * (n))
+#define HDMI_DC_CONTROL HDMI_CORE_BASE(0x05C0)
+#define HDMI_VIDEO_PATTERN_GEN HDMI_CORE_BASE(0x05C4)
+#define HDMI_HPD_GEN HDMI_CORE_BASE(0x05C8)
+
+/* Timing generator registers */
+#define HDMI_TG_CMD HDMI_TG_BASE(0x0000)
+#define HDMI_TG_H_FSZ_L HDMI_TG_BASE(0x0018)
+#define HDMI_TG_H_FSZ_H HDMI_TG_BASE(0x001C)
+#define HDMI_TG_HACT_ST_L HDMI_TG_BASE(0x0020)
+#define HDMI_TG_HACT_ST_H HDMI_TG_BASE(0x0024)
+#define HDMI_TG_HACT_SZ_L HDMI_TG_BASE(0x0028)
+#define HDMI_TG_HACT_SZ_H HDMI_TG_BASE(0x002C)
+#define HDMI_TG_V_FSZ_L HDMI_TG_BASE(0x0030)
+#define HDMI_TG_V_FSZ_H HDMI_TG_BASE(0x0034)
+#define HDMI_TG_VSYNC_L HDMI_TG_BASE(0x0038)
+#define HDMI_TG_VSYNC_H HDMI_TG_BASE(0x003C)
+#define HDMI_TG_VSYNC2_L HDMI_TG_BASE(0x0040)
+#define HDMI_TG_VSYNC2_H HDMI_TG_BASE(0x0044)
+#define HDMI_TG_VACT_ST_L HDMI_TG_BASE(0x0048)
+#define HDMI_TG_VACT_ST_H HDMI_TG_BASE(0x004C)
+#define HDMI_TG_VACT_SZ_L HDMI_TG_BASE(0x0050)
+#define HDMI_TG_VACT_SZ_H HDMI_TG_BASE(0x0054)
+#define HDMI_TG_FIELD_CHG_L HDMI_TG_BASE(0x0058)
+#define HDMI_TG_FIELD_CHG_H HDMI_TG_BASE(0x005C)
+#define HDMI_TG_VACT_ST2_L HDMI_TG_BASE(0x0060)
+#define HDMI_TG_VACT_ST2_H HDMI_TG_BASE(0x0064)
+#define HDMI_TG_VSYNC_TOP_HDMI_L HDMI_TG_BASE(0x0078)
+#define HDMI_TG_VSYNC_TOP_HDMI_H HDMI_TG_BASE(0x007C)
+#define HDMI_TG_VSYNC_BOT_HDMI_L HDMI_TG_BASE(0x0080)
+#define HDMI_TG_VSYNC_BOT_HDMI_H HDMI_TG_BASE(0x0084)
+#define HDMI_TG_FIELD_TOP_HDMI_L HDMI_TG_BASE(0x0088)
+#define HDMI_TG_FIELD_TOP_HDMI_H HDMI_TG_BASE(0x008C)
+#define HDMI_TG_FIELD_BOT_HDMI_L HDMI_TG_BASE(0x0090)
+#define HDMI_TG_FIELD_BOT_HDMI_H HDMI_TG_BASE(0x0094)
+
+/*
+ * Bit definition part
+ */
+
+/* HDMI_INTC_CON */
+#define HDMI_INTC_EN_GLOBAL (1 << 6)
+#define HDMI_INTC_EN_HPD_PLUG (1 << 3)
+#define HDMI_INTC_EN_HPD_UNPLUG (1 << 2)
+
+/* HDMI_INTC_FLAG */
+#define HDMI_INTC_FLAG_HPD_PLUG (1 << 3)
+#define HDMI_INTC_FLAG_HPD_UNPLUG (1 << 2)
+
+/* HDMI_PHY_RSTOUT */
+#define HDMI_PHY_SW_RSTOUT (1 << 0)
+
+/* HDMI_CORE_RSTOUT */
+#define HDMI_CORE_SW_RSTOUT (1 << 0)
+
+/* HDMI_CON_0 */
+#define HDMI_BLUE_SCR_EN (1 << 5)
+#define HDMI_EN (1 << 0)
+
+/* HDMI_CON_2 */
+#define HDMI_DVI_PERAMBLE_EN (1 << 5)
+#define HDMI_DVI_BAND_EN (1 << 1)
+
+/* HDMI_PHY_STATUS */
+#define HDMI_PHY_STATUS_READY (1 << 0)
+
+/* HDMI_MODE_SEL */
+#define HDMI_MODE_HDMI_EN (1 << 1)
+#define HDMI_MODE_DVI_EN (1 << 0)
+#define HDMI_MODE_MASK (3 << 0)
+
+/* HDMI_TG_CMD */
+#define HDMI_TG_EN (1 << 0)
+
+#endif /* SAMSUNG_REGS_HDMI_H */
diff --git a/drivers/media/video/s5p-tv/regs-mixer.h b/drivers/media/video/s5p-tv/regs-mixer.h
new file mode 100644
index 00000000..158abb43
--- /dev/null
+++ b/drivers/media/video/s5p-tv/regs-mixer.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Mixer register header file for Samsung Mixer driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+#ifndef SAMSUNG_REGS_MIXER_H
+#define SAMSUNG_REGS_MIXER_H
+
+/*
+ * Register part
+ */
+#define MXR_STATUS 0x0000
+#define MXR_CFG 0x0004
+#define MXR_INT_EN 0x0008
+#define MXR_INT_STATUS 0x000C
+#define MXR_LAYER_CFG 0x0010
+#define MXR_VIDEO_CFG 0x0014
+#define MXR_GRAPHIC0_CFG 0x0020
+#define MXR_GRAPHIC0_BASE 0x0024
+#define MXR_GRAPHIC0_SPAN 0x0028
+#define MXR_GRAPHIC0_SXY 0x002C
+#define MXR_GRAPHIC0_WH 0x0030
+#define MXR_GRAPHIC0_DXY 0x0034
+#define MXR_GRAPHIC0_BLANK 0x0038
+#define MXR_GRAPHIC1_CFG 0x0040
+#define MXR_GRAPHIC1_BASE 0x0044
+#define MXR_GRAPHIC1_SPAN 0x0048
+#define MXR_GRAPHIC1_SXY 0x004C
+#define MXR_GRAPHIC1_WH 0x0050
+#define MXR_GRAPHIC1_DXY 0x0054
+#define MXR_GRAPHIC1_BLANK 0x0058
+#define MXR_BG_CFG 0x0060
+#define MXR_BG_COLOR0 0x0064
+#define MXR_BG_COLOR1 0x0068
+#define MXR_BG_COLOR2 0x006C
+
+/* for parametrized access to layer registers */
+#define MXR_GRAPHIC_CFG(i) (0x0020 + (i) * 0x20)
+#define MXR_GRAPHIC_BASE(i) (0x0024 + (i) * 0x20)
+#define MXR_GRAPHIC_SPAN(i) (0x0028 + (i) * 0x20)
+#define MXR_GRAPHIC_SXY(i) (0x002C + (i) * 0x20)
+#define MXR_GRAPHIC_WH(i) (0x0030 + (i) * 0x20)
+#define MXR_GRAPHIC_DXY(i) (0x0034 + (i) * 0x20)
+
+/*
+ * Bit definition part
+ */
+
+/* generates mask for range of bits */
+#define MXR_MASK(high_bit, low_bit) \
+ (((2 << ((high_bit) - (low_bit))) - 1) << (low_bit))
+
+#define MXR_MASK_VAL(val, high_bit, low_bit) \
+ (((val) << (low_bit)) & MXR_MASK(high_bit, low_bit))
+
+/* bits for MXR_STATUS */
+#define MXR_STATUS_16_BURST (1 << 7)
+#define MXR_STATUS_BURST_MASK (1 << 7)
+#define MXR_STATUS_SYNC_ENABLE (1 << 2)
+#define MXR_STATUS_REG_RUN (1 << 0)
+
+/* bits for MXR_CFG */
+#define MXR_CFG_OUT_YUV444 (0 << 8)
+#define MXR_CFG_OUT_RGB888 (1 << 8)
+#define MXR_CFG_OUT_MASK (1 << 8)
+#define MXR_CFG_DST_SDO (0 << 7)
+#define MXR_CFG_DST_HDMI (1 << 7)
+#define MXR_CFG_DST_MASK (1 << 7)
+#define MXR_CFG_SCAN_HD_720 (0 << 6)
+#define MXR_CFG_SCAN_HD_1080 (1 << 6)
+#define MXR_CFG_GRP1_ENABLE (1 << 5)
+#define MXR_CFG_GRP0_ENABLE (1 << 4)
+#define MXR_CFG_VP_ENABLE (1 << 3)
+#define MXR_CFG_SCAN_INTERLACE (0 << 2)
+#define MXR_CFG_SCAN_PROGRASSIVE (1 << 2)
+#define MXR_CFG_SCAN_NTSC (0 << 1)
+#define MXR_CFG_SCAN_PAL (1 << 1)
+#define MXR_CFG_SCAN_SD (0 << 0)
+#define MXR_CFG_SCAN_HD (1 << 0)
+#define MXR_CFG_SCAN_MASK 0x47
+
+/* bits for MXR_GRAPHICn_CFG */
+#define MXR_GRP_CFG_COLOR_KEY_DISABLE (1 << 21)
+#define MXR_GRP_CFG_BLEND_PRE_MUL (1 << 20)
+#define MXR_GRP_CFG_FORMAT_VAL(x) MXR_MASK_VAL(x, 11, 8)
+#define MXR_GRP_CFG_FORMAT_MASK MXR_GRP_CFG_FORMAT_VAL(~0)
+#define MXR_GRP_CFG_ALPHA_VAL(x) MXR_MASK_VAL(x, 7, 0)
+
+/* bits for MXR_GRAPHICn_WH */
+#define MXR_GRP_WH_H_SCALE(x) MXR_MASK_VAL(x, 28, 28)
+#define MXR_GRP_WH_V_SCALE(x) MXR_MASK_VAL(x, 12, 12)
+#define MXR_GRP_WH_WIDTH(x) MXR_MASK_VAL(x, 26, 16)
+#define MXR_GRP_WH_HEIGHT(x) MXR_MASK_VAL(x, 10, 0)
+
+/* bits for MXR_GRAPHICn_SXY */
+#define MXR_GRP_SXY_SX(x) MXR_MASK_VAL(x, 26, 16)
+#define MXR_GRP_SXY_SY(x) MXR_MASK_VAL(x, 10, 0)
+
+/* bits for MXR_GRAPHICn_DXY */
+#define MXR_GRP_DXY_DX(x) MXR_MASK_VAL(x, 26, 16)
+#define MXR_GRP_DXY_DY(x) MXR_MASK_VAL(x, 10, 0)
+
+/* bits for MXR_INT_EN */
+#define MXR_INT_EN_VSYNC (1 << 11)
+#define MXR_INT_EN_ALL (0x0f << 8)
+
+/* bit for MXR_INT_STATUS */
+#define MXR_INT_CLEAR_VSYNC (1 << 11)
+#define MXR_INT_STATUS_VSYNC (1 << 0)
+
+/* bit for MXR_LAYER_CFG */
+#define MXR_LAYER_CFG_GRP1_VAL(x) MXR_MASK_VAL(x, 11, 8)
+#define MXR_LAYER_CFG_GRP0_VAL(x) MXR_MASK_VAL(x, 7, 4)
+#define MXR_LAYER_CFG_VP_VAL(x) MXR_MASK_VAL(x, 3, 0)
+
+#endif /* SAMSUNG_REGS_MIXER_H */
+
diff --git a/drivers/media/video/s5p-tv/regs-sdo.h b/drivers/media/video/s5p-tv/regs-sdo.h
new file mode 100644
index 00000000..7f7c2b8a
--- /dev/null
+++ b/drivers/media/video/s5p-tv/regs-sdo.h
@@ -0,0 +1,63 @@
+/* drivers/media/video/s5p-tv/regs-sdo.h
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * SDO register description file
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef SAMSUNG_REGS_SDO_H
+#define SAMSUNG_REGS_SDO_H
+
+/*
+ * Register part
+ */
+
+#define SDO_CLKCON 0x0000
+#define SDO_CONFIG 0x0008
+#define SDO_VBI 0x0014
+#define SDO_DAC 0x003C
+#define SDO_CCCON 0x0180
+#define SDO_IRQ 0x0280
+#define SDO_IRQMASK 0x0284
+#define SDO_VERSION 0x03D8
+
+/*
+ * Bit definition part
+ */
+
+/* SDO Clock Control Register (SDO_CLKCON) */
+#define SDO_TVOUT_SW_RESET (1 << 4)
+#define SDO_TVOUT_CLOCK_READY (1 << 1)
+#define SDO_TVOUT_CLOCK_ON (1 << 0)
+
+/* SDO Video Standard Configuration Register (SDO_CONFIG) */
+#define SDO_PROGRESSIVE (1 << 4)
+#define SDO_NTSC_M 0
+#define SDO_PAL_M 1
+#define SDO_PAL_BGHID 2
+#define SDO_PAL_N 3
+#define SDO_PAL_NC 4
+#define SDO_NTSC_443 8
+#define SDO_PAL_60 9
+#define SDO_STANDARD_MASK 0xf
+
+/* SDO VBI Configuration Register (SDO_VBI) */
+#define SDO_CVBS_WSS_INS (1 << 14)
+#define SDO_CVBS_CLOSED_CAPTION_MASK (3 << 12)
+
+/* SDO DAC Configuration Register (SDO_DAC) */
+#define SDO_POWER_ON_DAC (1 << 0)
+
+/* SDO Color Compensation On/Off Control (SDO_CCCON) */
+#define SDO_COMPENSATION_BHS_ADJ_OFF (1 << 4)
+#define SDO_COMPENSATION_CVBS_COMP_OFF (1 << 0)
+
+/* SDO Interrupt Request Register (SDO_IRQ) */
+#define SDO_VSYNC_IRQ_PEND (1 << 0)
+
+#endif /* SAMSUNG_REGS_SDO_H */
diff --git a/drivers/media/video/s5p-tv/regs-vp.h b/drivers/media/video/s5p-tv/regs-vp.h
new file mode 100644
index 00000000..6c63984e
--- /dev/null
+++ b/drivers/media/video/s5p-tv/regs-vp.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Video processor register header file for Samsung Mixer driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef SAMSUNG_REGS_VP_H
+#define SAMSUNG_REGS_VP_H
+
+/*
+ * Register part
+ */
+
+#define VP_ENABLE 0x0000
+#define VP_SRESET 0x0004
+#define VP_SHADOW_UPDATE 0x0008
+#define VP_FIELD_ID 0x000C
+#define VP_MODE 0x0010
+#define VP_IMG_SIZE_Y 0x0014
+#define VP_IMG_SIZE_C 0x0018
+#define VP_PER_RATE_CTRL 0x001C
+#define VP_TOP_Y_PTR 0x0028
+#define VP_BOT_Y_PTR 0x002C
+#define VP_TOP_C_PTR 0x0030
+#define VP_BOT_C_PTR 0x0034
+#define VP_ENDIAN_MODE 0x03CC
+#define VP_SRC_H_POSITION 0x0044
+#define VP_SRC_V_POSITION 0x0048
+#define VP_SRC_WIDTH 0x004C
+#define VP_SRC_HEIGHT 0x0050
+#define VP_DST_H_POSITION 0x0054
+#define VP_DST_V_POSITION 0x0058
+#define VP_DST_WIDTH 0x005C
+#define VP_DST_HEIGHT 0x0060
+#define VP_H_RATIO 0x0064
+#define VP_V_RATIO 0x0068
+#define VP_POLY8_Y0_LL 0x006C
+#define VP_POLY4_Y0_LL 0x00EC
+#define VP_POLY4_C0_LL 0x012C
+
+/*
+ * Bit definition part
+ */
+
+/* generates mask for range of bits */
+
+#define VP_MASK(high_bit, low_bit) \
+ (((2 << ((high_bit) - (low_bit))) - 1) << (low_bit))
+
+#define VP_MASK_VAL(val, high_bit, low_bit) \
+ (((val) << (low_bit)) & VP_MASK(high_bit, low_bit))
+
+ /* VP_ENABLE */
+#define VP_ENABLE_ON (1 << 0)
+
+/* VP_SRESET */
+#define VP_SRESET_PROCESSING (1 << 0)
+
+/* VP_SHADOW_UPDATE */
+#define VP_SHADOW_UPDATE_ENABLE (1 << 0)
+
+/* VP_MODE */
+#define VP_MODE_NV12 (0 << 6)
+#define VP_MODE_NV21 (1 << 6)
+#define VP_MODE_LINE_SKIP (1 << 5)
+#define VP_MODE_MEM_LINEAR (0 << 4)
+#define VP_MODE_MEM_TILED (1 << 4)
+#define VP_MODE_FMT_MASK (5 << 4)
+#define VP_MODE_FIELD_ID_AUTO_TOGGLING (1 << 2)
+#define VP_MODE_2D_IPC (1 << 1)
+
+/* VP_IMG_SIZE_Y */
+/* VP_IMG_SIZE_C */
+#define VP_IMG_HSIZE(x) VP_MASK_VAL(x, 29, 16)
+#define VP_IMG_VSIZE(x) VP_MASK_VAL(x, 13, 0)
+
+/* VP_SRC_H_POSITION */
+#define VP_SRC_H_POSITION_VAL(x) VP_MASK_VAL(x, 14, 4)
+
+/* VP_ENDIAN_MODE */
+#define VP_ENDIAN_MODE_LITTLE (1 << 0)
+
+#endif /* SAMSUNG_REGS_VP_H */
diff --git a/drivers/media/video/s5p-tv/sdo_drv.c b/drivers/media/video/s5p-tv/sdo_drv.c
new file mode 100644
index 00000000..f6bca2c2
--- /dev/null
+++ b/drivers/media/video/s5p-tv/sdo_drv.c
@@ -0,0 +1,452 @@
+/*
+ * Samsung Standard Definition Output (SDO) driver
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * Tomasz Stanislawski, <t.stanislaws@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundiation. either version 2 of the License,
+ * or (at your option) any later version
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-subdev.h>
+
+#include "regs-sdo.h"
+
+MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>");
+MODULE_DESCRIPTION("Samsung Standard Definition Output (SDO)");
+MODULE_LICENSE("GPL");
+
+#define SDO_DEFAULT_STD V4L2_STD_PAL
+
+struct sdo_format {
+ v4l2_std_id id;
+ /* all modes are 720 pixels wide */
+ unsigned int height;
+ unsigned int cookie;
+};
+
+struct sdo_device {
+ /** pointer to device parent */
+ struct device *dev;
+ /** base address of SDO registers */
+ void __iomem *regs;
+ /** SDO interrupt */
+ unsigned int irq;
+ /** DAC source clock */
+ struct clk *sclk_dac;
+ /** DAC clock */
+ struct clk *dac;
+ /** DAC physical interface */
+ struct clk *dacphy;
+ /** clock for control of VPLL */
+ struct clk *fout_vpll;
+ /** regulator for SDO IP power */
+ struct regulator *vdac;
+ /** regulator for SDO plug detection */
+ struct regulator *vdet;
+ /** subdev used as device interface */
+ struct v4l2_subdev sd;
+ /** current format */
+ const struct sdo_format *fmt;
+};
+
+static inline struct sdo_device *sd_to_sdev(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct sdo_device, sd);
+}
+
+static inline
+void sdo_write_mask(struct sdo_device *sdev, u32 reg_id, u32 value, u32 mask)
+{
+ u32 old = readl(sdev->regs + reg_id);
+ value = (value & mask) | (old & ~mask);
+ writel(value, sdev->regs + reg_id);
+}
+
+static inline
+void sdo_write(struct sdo_device *sdev, u32 reg_id, u32 value)
+{
+ writel(value, sdev->regs + reg_id);
+}
+
+static inline
+u32 sdo_read(struct sdo_device *sdev, u32 reg_id)
+{
+ return readl(sdev->regs + reg_id);
+}
+
+static irqreturn_t sdo_irq_handler(int irq, void *dev_data)
+{
+ struct sdo_device *sdev = dev_data;
+
+ /* clear interrupt */
+ sdo_write_mask(sdev, SDO_IRQ, ~0, SDO_VSYNC_IRQ_PEND);
+ return IRQ_HANDLED;
+}
+
+static void sdo_reg_debug(struct sdo_device *sdev)
+{
+#define DBGREG(reg_id) \
+ dev_info(sdev->dev, #reg_id " = %08x\n", \
+ sdo_read(sdev, reg_id))
+
+ DBGREG(SDO_CLKCON);
+ DBGREG(SDO_CONFIG);
+ DBGREG(SDO_VBI);
+ DBGREG(SDO_DAC);
+ DBGREG(SDO_IRQ);
+ DBGREG(SDO_IRQMASK);
+ DBGREG(SDO_VERSION);
+}
+
+static const struct sdo_format sdo_format[] = {
+ { V4L2_STD_PAL_N, .height = 576, .cookie = SDO_PAL_N },
+ { V4L2_STD_PAL_Nc, .height = 576, .cookie = SDO_PAL_NC },
+ { V4L2_STD_PAL_M, .height = 480, .cookie = SDO_PAL_M },
+ { V4L2_STD_PAL_60, .height = 480, .cookie = SDO_PAL_60 },
+ { V4L2_STD_NTSC_443, .height = 480, .cookie = SDO_NTSC_443 },
+ { V4L2_STD_PAL, .height = 576, .cookie = SDO_PAL_BGHID },
+ { V4L2_STD_NTSC_M, .height = 480, .cookie = SDO_NTSC_M },
+};
+
+static const struct sdo_format *sdo_find_format(v4l2_std_id id)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(sdo_format); ++i)
+ if (sdo_format[i].id & id)
+ return &sdo_format[i];
+ return NULL;
+}
+
+static int sdo_g_tvnorms_output(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ *std = V4L2_STD_NTSC_M | V4L2_STD_PAL_M | V4L2_STD_PAL |
+ V4L2_STD_PAL_N | V4L2_STD_PAL_Nc |
+ V4L2_STD_NTSC_443 | V4L2_STD_PAL_60;
+ return 0;
+}
+
+static int sdo_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct sdo_device *sdev = sd_to_sdev(sd);
+ const struct sdo_format *fmt;
+ fmt = sdo_find_format(std);
+ if (fmt == NULL)
+ return -EINVAL;
+ sdev->fmt = fmt;
+ return 0;
+}
+
+static int sdo_g_std_output(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ *std = sd_to_sdev(sd)->fmt->id;
+ return 0;
+}
+
+static int sdo_g_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct sdo_device *sdev = sd_to_sdev(sd);
+
+ if (!sdev->fmt)
+ return -ENXIO;
+ /* all modes are 720 pixels wide */
+ fmt->width = 720;
+ fmt->height = sdev->fmt->height;
+ fmt->code = V4L2_MBUS_FMT_FIXED;
+ fmt->field = V4L2_FIELD_INTERLACED;
+ fmt->colorspace = V4L2_COLORSPACE_JPEG;
+ return 0;
+}
+
+static int sdo_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct sdo_device *sdev = sd_to_sdev(sd);
+ struct device *dev = sdev->dev;
+ int ret;
+
+ dev_info(dev, "sdo_s_power(%d)\n", on);
+
+ if (on)
+ ret = pm_runtime_get_sync(dev);
+ else
+ ret = pm_runtime_put_sync(dev);
+
+ /* only values < 0 indicate errors */
+ return IS_ERR_VALUE(ret) ? ret : 0;
+}
+
+static int sdo_streamon(struct sdo_device *sdev)
+{
+ /* set proper clock for Timing Generator */
+ clk_set_rate(sdev->fout_vpll, 54000000);
+ dev_info(sdev->dev, "fout_vpll.rate = %lu\n",
+ clk_get_rate(sdev->fout_vpll));
+ /* enable clock in SDO */
+ sdo_write_mask(sdev, SDO_CLKCON, ~0, SDO_TVOUT_CLOCK_ON);
+ clk_enable(sdev->dacphy);
+ /* enable DAC */
+ sdo_write_mask(sdev, SDO_DAC, ~0, SDO_POWER_ON_DAC);
+ sdo_reg_debug(sdev);
+ return 0;
+}
+
+static int sdo_streamoff(struct sdo_device *sdev)
+{
+ int tries;
+
+ sdo_write_mask(sdev, SDO_DAC, 0, SDO_POWER_ON_DAC);
+ clk_disable(sdev->dacphy);
+ sdo_write_mask(sdev, SDO_CLKCON, 0, SDO_TVOUT_CLOCK_ON);
+ for (tries = 100; tries; --tries) {
+ if (sdo_read(sdev, SDO_CLKCON) & SDO_TVOUT_CLOCK_READY)
+ break;
+ mdelay(1);
+ }
+ if (tries == 0)
+ dev_err(sdev->dev, "failed to stop streaming\n");
+ return tries ? 0 : -EIO;
+}
+
+static int sdo_s_stream(struct v4l2_subdev *sd, int on)
+{
+ struct sdo_device *sdev = sd_to_sdev(sd);
+ return on ? sdo_streamon(sdev) : sdo_streamoff(sdev);
+}
+
+static const struct v4l2_subdev_core_ops sdo_sd_core_ops = {
+ .s_power = sdo_s_power,
+};
+
+static const struct v4l2_subdev_video_ops sdo_sd_video_ops = {
+ .s_std_output = sdo_s_std_output,
+ .g_std_output = sdo_g_std_output,
+ .g_tvnorms_output = sdo_g_tvnorms_output,
+ .g_mbus_fmt = sdo_g_mbus_fmt,
+ .s_stream = sdo_s_stream,
+};
+
+static const struct v4l2_subdev_ops sdo_sd_ops = {
+ .core = &sdo_sd_core_ops,
+ .video = &sdo_sd_video_ops,
+};
+
+static int sdo_runtime_suspend(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct sdo_device *sdev = sd_to_sdev(sd);
+
+ dev_info(dev, "suspend\n");
+ regulator_disable(sdev->vdet);
+ regulator_disable(sdev->vdac);
+ clk_disable(sdev->sclk_dac);
+ return 0;
+}
+
+static int sdo_runtime_resume(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct sdo_device *sdev = sd_to_sdev(sd);
+
+ dev_info(dev, "resume\n");
+ clk_enable(sdev->sclk_dac);
+ regulator_enable(sdev->vdac);
+ regulator_enable(sdev->vdet);
+
+ /* software reset */
+ sdo_write_mask(sdev, SDO_CLKCON, ~0, SDO_TVOUT_SW_RESET);
+ mdelay(10);
+ sdo_write_mask(sdev, SDO_CLKCON, 0, SDO_TVOUT_SW_RESET);
+
+ /* setting TV mode */
+ sdo_write_mask(sdev, SDO_CONFIG, sdev->fmt->cookie, SDO_STANDARD_MASK);
+ /* XXX: forcing interlaced mode using undocumented bit */
+ sdo_write_mask(sdev, SDO_CONFIG, 0, SDO_PROGRESSIVE);
+ /* turn all VBI off */
+ sdo_write_mask(sdev, SDO_VBI, 0, SDO_CVBS_WSS_INS |
+ SDO_CVBS_CLOSED_CAPTION_MASK);
+ /* turn all post processing off */
+ sdo_write_mask(sdev, SDO_CCCON, ~0, SDO_COMPENSATION_BHS_ADJ_OFF |
+ SDO_COMPENSATION_CVBS_COMP_OFF);
+ sdo_reg_debug(sdev);
+ return 0;
+}
+
+static const struct dev_pm_ops sdo_pm_ops = {
+ .runtime_suspend = sdo_runtime_suspend,
+ .runtime_resume = sdo_runtime_resume,
+};
+
+static int __devinit sdo_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct sdo_device *sdev;
+ struct resource *res;
+ int ret = 0;
+ struct clk *sclk_vpll;
+
+ dev_info(dev, "probe start\n");
+ sdev = devm_kzalloc(&pdev->dev, sizeof *sdev, GFP_KERNEL);
+ if (!sdev) {
+ dev_err(dev, "not enough memory.\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+ sdev->dev = dev;
+
+ /* mapping registers */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(dev, "get memory resource failed.\n");
+ ret = -ENXIO;
+ goto fail;
+ }
+
+ sdev->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (sdev->regs == NULL) {
+ dev_err(dev, "register mapping failed.\n");
+ ret = -ENXIO;
+ goto fail;
+ }
+
+ /* acquiring interrupt */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_err(dev, "get interrupt resource failed.\n");
+ ret = -ENXIO;
+ goto fail;
+ }
+ ret = devm_request_irq(&pdev->dev, res->start, sdo_irq_handler, 0,
+ "s5p-sdo", sdev);
+ if (ret) {
+ dev_err(dev, "request interrupt failed.\n");
+ goto fail;
+ }
+ sdev->irq = res->start;
+
+ /* acquire clocks */
+ sdev->sclk_dac = clk_get(dev, "sclk_dac");
+ if (IS_ERR_OR_NULL(sdev->sclk_dac)) {
+ dev_err(dev, "failed to get clock 'sclk_dac'\n");
+ ret = -ENXIO;
+ goto fail;
+ }
+ sdev->dac = clk_get(dev, "dac");
+ if (IS_ERR_OR_NULL(sdev->dac)) {
+ dev_err(dev, "failed to get clock 'dac'\n");
+ ret = -ENXIO;
+ goto fail_sclk_dac;
+ }
+ sdev->dacphy = clk_get(dev, "dacphy");
+ if (IS_ERR_OR_NULL(sdev->dacphy)) {
+ dev_err(dev, "failed to get clock 'dacphy'\n");
+ ret = -ENXIO;
+ goto fail_dac;
+ }
+ sclk_vpll = clk_get(dev, "sclk_vpll");
+ if (IS_ERR_OR_NULL(sclk_vpll)) {
+ dev_err(dev, "failed to get clock 'sclk_vpll'\n");
+ ret = -ENXIO;
+ goto fail_dacphy;
+ }
+ clk_set_parent(sdev->sclk_dac, sclk_vpll);
+ clk_put(sclk_vpll);
+ sdev->fout_vpll = clk_get(dev, "fout_vpll");
+ if (IS_ERR_OR_NULL(sdev->fout_vpll)) {
+ dev_err(dev, "failed to get clock 'fout_vpll'\n");
+ goto fail_dacphy;
+ }
+ dev_info(dev, "fout_vpll.rate = %lu\n", clk_get_rate(sclk_vpll));
+
+ /* acquire regulator */
+ sdev->vdac = regulator_get(dev, "vdd33a_dac");
+ if (IS_ERR_OR_NULL(sdev->vdac)) {
+ dev_err(dev, "failed to get regulator 'vdac'\n");
+ goto fail_fout_vpll;
+ }
+ sdev->vdet = regulator_get(dev, "vdet");
+ if (IS_ERR_OR_NULL(sdev->vdet)) {
+ dev_err(dev, "failed to get regulator 'vdet'\n");
+ goto fail_vdac;
+ }
+
+ /* enable gate for dac clock, because mixer uses it */
+ clk_enable(sdev->dac);
+
+ /* configure power management */
+ pm_runtime_enable(dev);
+
+ /* configuration of interface subdevice */
+ v4l2_subdev_init(&sdev->sd, &sdo_sd_ops);
+ sdev->sd.owner = THIS_MODULE;
+ strlcpy(sdev->sd.name, "s5p-sdo", sizeof sdev->sd.name);
+
+ /* set default format */
+ sdev->fmt = sdo_find_format(SDO_DEFAULT_STD);
+ BUG_ON(sdev->fmt == NULL);
+
+ /* keeping subdev in device's private for use by other drivers */
+ dev_set_drvdata(dev, &sdev->sd);
+
+ dev_info(dev, "probe succeeded\n");
+ return 0;
+
+fail_vdac:
+ regulator_put(sdev->vdac);
+fail_fout_vpll:
+ clk_put(sdev->fout_vpll);
+fail_dacphy:
+ clk_put(sdev->dacphy);
+fail_dac:
+ clk_put(sdev->dac);
+fail_sclk_dac:
+ clk_put(sdev->sclk_dac);
+fail:
+ dev_info(dev, "probe failed\n");
+ return ret;
+}
+
+static int __devexit sdo_remove(struct platform_device *pdev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(&pdev->dev);
+ struct sdo_device *sdev = sd_to_sdev(sd);
+
+ pm_runtime_disable(&pdev->dev);
+ clk_disable(sdev->dac);
+ regulator_put(sdev->vdet);
+ regulator_put(sdev->vdac);
+ clk_put(sdev->fout_vpll);
+ clk_put(sdev->dacphy);
+ clk_put(sdev->dac);
+ clk_put(sdev->sclk_dac);
+
+ dev_info(&pdev->dev, "remove successful\n");
+ return 0;
+}
+
+static struct platform_driver sdo_driver __refdata = {
+ .probe = sdo_probe,
+ .remove = __devexit_p(sdo_remove),
+ .driver = {
+ .name = "s5p-sdo",
+ .owner = THIS_MODULE,
+ .pm = &sdo_pm_ops,
+ }
+};
+
+module_platform_driver(sdo_driver);
diff --git a/drivers/media/video/s5p-tv/sii9234_drv.c b/drivers/media/video/s5p-tv/sii9234_drv.c
new file mode 100644
index 00000000..0f31eccd
--- /dev/null
+++ b/drivers/media/video/s5p-tv/sii9234_drv.c
@@ -0,0 +1,432 @@
+/*
+ * Samsung MHL interface driver
+ *
+ * Copyright (C) 2011 Samsung Electronics Co.Ltd
+ * Author: Tomasz Stanislawski <t.stanislaws@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/freezer.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/machine.h>
+#include <linux/slab.h>
+
+#include <mach/gpio.h>
+#include <plat/gpio-cfg.h>
+
+#include <media/sii9234.h>
+#include <media/v4l2-subdev.h>
+
+MODULE_AUTHOR("Tomasz Stanislawski <t.stanislaws@samsung.com>");
+MODULE_DESCRIPTION("Samsung MHL interface driver");
+MODULE_LICENSE("GPL");
+
+struct sii9234_context {
+ struct i2c_client *client;
+ struct regulator *power;
+ int gpio_n_reset;
+ struct v4l2_subdev sd;
+};
+
+static inline struct sii9234_context *sd_to_context(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct sii9234_context, sd);
+}
+
+static inline int sii9234_readb(struct i2c_client *client, int addr)
+{
+ return i2c_smbus_read_byte_data(client, addr);
+}
+
+static inline int sii9234_writeb(struct i2c_client *client, int addr, int value)
+{
+ return i2c_smbus_write_byte_data(client, addr, value);
+}
+
+static inline int sii9234_writeb_mask(struct i2c_client *client, int addr,
+ int value, int mask)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, addr);
+ if (ret < 0)
+ return ret;
+ ret = (ret & ~mask) | (value & mask);
+ return i2c_smbus_write_byte_data(client, addr, ret);
+}
+
+static inline int sii9234_readb_idx(struct i2c_client *client, int addr)
+{
+ int ret;
+ ret = i2c_smbus_write_byte_data(client, 0xbc, addr >> 8);
+ if (ret < 0)
+ return ret;
+ ret = i2c_smbus_write_byte_data(client, 0xbd, addr & 0xff);
+ if (ret < 0)
+ return ret;
+ return i2c_smbus_read_byte_data(client, 0xbe);
+}
+
+static inline int sii9234_writeb_idx(struct i2c_client *client, int addr,
+ int value)
+{
+ int ret;
+ ret = i2c_smbus_write_byte_data(client, 0xbc, addr >> 8);
+ if (ret < 0)
+ return ret;
+ ret = i2c_smbus_write_byte_data(client, 0xbd, addr & 0xff);
+ if (ret < 0)
+ return ret;
+ ret = i2c_smbus_write_byte_data(client, 0xbe, value);
+ return ret;
+}
+
+static inline int sii9234_writeb_idx_mask(struct i2c_client *client, int addr,
+ int value, int mask)
+{
+ int ret;
+
+ ret = sii9234_readb_idx(client, addr);
+ if (ret < 0)
+ return ret;
+ ret = (ret & ~mask) | (value & mask);
+ return sii9234_writeb_idx(client, addr, ret);
+}
+
+static int sii9234_reset(struct sii9234_context *ctx)
+{
+ struct i2c_client *client = ctx->client;
+ struct device *dev = &client->dev;
+ int ret, tries;
+
+ gpio_direction_output(ctx->gpio_n_reset, 1);
+ mdelay(1);
+ gpio_direction_output(ctx->gpio_n_reset, 0);
+ mdelay(1);
+ gpio_direction_output(ctx->gpio_n_reset, 1);
+ mdelay(1);
+
+ /* going to TTPI mode */
+ ret = sii9234_writeb(client, 0xc7, 0);
+ if (ret < 0) {
+ dev_err(dev, "failed to set TTPI mode\n");
+ return ret;
+ }
+ for (tries = 0; tries < 100 ; ++tries) {
+ ret = sii9234_readb(client, 0x1b);
+ if (ret > 0)
+ break;
+ if (ret < 0) {
+ dev_err(dev, "failed to reset device\n");
+ return -EIO;
+ }
+ mdelay(1);
+ }
+ if (tries == 100) {
+ dev_err(dev, "maximal number of tries reached\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int sii9234_verify_version(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ int family, rev, tpi_rev, dev_id, sub_id, hdcp, id;
+
+ family = sii9234_readb(client, 0x1b);
+ rev = sii9234_readb(client, 0x1c) & 0x0f;
+ tpi_rev = sii9234_readb(client, 0x1d) & 0x7f;
+ dev_id = sii9234_readb_idx(client, 0x0103);
+ sub_id = sii9234_readb_idx(client, 0x0102);
+ hdcp = sii9234_readb(client, 0x30);
+
+ if (family < 0 || rev < 0 || tpi_rev < 0 || dev_id < 0 ||
+ sub_id < 0 || hdcp < 0) {
+ dev_err(dev, "failed to read chip's version\n");
+ return -EIO;
+ }
+
+ id = (dev_id << 8) | sub_id;
+
+ dev_info(dev, "chip: SiL%02x family: %02x, rev: %02x\n",
+ id, family, rev);
+ dev_info(dev, "tpi_rev:%02x, hdcp: %02x\n", tpi_rev, hdcp);
+ if (id != 0x9234) {
+ dev_err(dev, "not supported chip\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static u8 data[][3] = {
+/* setup from driver created by doonsoo45.kim */
+ { 0x01, 0x05, 0x04 }, /* Enable Auto soft reset on SCDT = 0 */
+ { 0x01, 0x08, 0x35 }, /* Power Up TMDS Tx Core */
+ { 0x01, 0x0d, 0x1c }, /* HDMI Transcode mode enable */
+ { 0x01, 0x2b, 0x01 }, /* Enable HDCP Compliance workaround */
+ { 0x01, 0x79, 0x40 }, /* daniel test...MHL_INT */
+ { 0x01, 0x80, 0x34 }, /* Enable Rx PLL Clock Value */
+ { 0x01, 0x90, 0x27 }, /* Enable CBUS discovery */
+ { 0x01, 0x91, 0xe5 }, /* Skip RGND detection */
+ { 0x01, 0x92, 0x46 }, /* Force MHD mode */
+ { 0x01, 0x93, 0xdc }, /* Disable CBUS pull-up during RGND measurement */
+ { 0x01, 0x94, 0x66 }, /* 1.8V CBUS VTH & GND threshold */
+ { 0x01, 0x95, 0x31 }, /* RGND block & single discovery attempt */
+ { 0x01, 0x96, 0x22 }, /* use 1K and 2K setting */
+ { 0x01, 0xa0, 0x10 }, /* SIMG: Term mode */
+ { 0x01, 0xa1, 0xfc }, /* Disable internal Mobile HD driver */
+ { 0x01, 0xa3, 0xfa }, /* SIMG: Output Swing default EB, 3x Clk Mult */
+ { 0x01, 0xa5, 0x80 }, /* SIMG: RGND Hysterisis, 3x mode for Beast */
+ { 0x01, 0xa6, 0x0c }, /* SIMG: Swing Offset */
+ { 0x02, 0x3d, 0x3f }, /* Power up CVCC 1.2V core */
+ { 0x03, 0x00, 0x00 }, /* SIMG: correcting HW default */
+ { 0x03, 0x11, 0x01 }, /* Enable TxPLL Clock */
+ { 0x03, 0x12, 0x15 }, /* Enable Tx Clock Path & Equalizer */
+ { 0x03, 0x13, 0x60 }, /* SIMG: Set termination value */
+ { 0x03, 0x14, 0xf0 }, /* SIMG: Change CKDT level */
+ { 0x03, 0x17, 0x07 }, /* SIMG: PLL Calrefsel */
+ { 0x03, 0x1a, 0x20 }, /* VCO Cal */
+ { 0x03, 0x22, 0xe0 }, /* SIMG: Auto EQ */
+ { 0x03, 0x23, 0xc0 }, /* SIMG: Auto EQ */
+ { 0x03, 0x24, 0xa0 }, /* SIMG: Auto EQ */
+ { 0x03, 0x25, 0x80 }, /* SIMG: Auto EQ */
+ { 0x03, 0x26, 0x60 }, /* SIMG: Auto EQ */
+ { 0x03, 0x27, 0x40 }, /* SIMG: Auto EQ */
+ { 0x03, 0x28, 0x20 }, /* SIMG: Auto EQ */
+ { 0x03, 0x29, 0x00 }, /* SIMG: Auto EQ */
+ { 0x03, 0x31, 0x0b }, /* SIMG: Rx PLL BW value from I2C BW ~ 4MHz */
+ { 0x03, 0x45, 0x06 }, /* SIMG: DPLL Mode */
+ { 0x03, 0x4b, 0x06 }, /* SIMG: Correcting HW default */
+ { 0x03, 0x4c, 0xa0 }, /* Manual zone control */
+ { 0x03, 0x4d, 0x02 }, /* SIMG: PLL Mode Value (order is important) */
+};
+
+static int sii9234_set_internal(struct sii9234_context *ctx)
+{
+ struct i2c_client *client = ctx->client;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(data); ++i) {
+ int addr = (data[i][0] << 8) | data[i][1];
+ ret = sii9234_writeb_idx(client, addr, data[i][2]);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static int sii9234_runtime_suspend(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct sii9234_context *ctx = sd_to_context(sd);
+ struct i2c_client *client = ctx->client;
+
+ dev_info(dev, "suspend start\n");
+
+ sii9234_writeb_mask(client, 0x1e, 3, 3);
+ regulator_disable(ctx->power);
+
+ return 0;
+}
+
+static int sii9234_runtime_resume(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct sii9234_context *ctx = sd_to_context(sd);
+ struct i2c_client *client = ctx->client;
+ int ret;
+
+ dev_info(dev, "resume start\n");
+ regulator_enable(ctx->power);
+
+ ret = sii9234_reset(ctx);
+ if (ret)
+ goto fail;
+
+ /* enable tpi */
+ ret = sii9234_writeb_mask(client, 0x1e, 1, 0);
+ if (ret < 0)
+ goto fail;
+ ret = sii9234_set_internal(ctx);
+ if (ret < 0)
+ goto fail;
+
+ return 0;
+
+fail:
+ dev_err(dev, "failed to resume\n");
+ regulator_disable(ctx->power);
+
+ return ret;
+}
+
+static const struct dev_pm_ops sii9234_pm_ops = {
+ .runtime_suspend = sii9234_runtime_suspend,
+ .runtime_resume = sii9234_runtime_resume,
+};
+
+static int sii9234_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct sii9234_context *ctx = sd_to_context(sd);
+ int ret;
+
+ if (on)
+ ret = pm_runtime_get_sync(&ctx->client->dev);
+ else
+ ret = pm_runtime_put(&ctx->client->dev);
+ /* only values < 0 indicate errors */
+ return IS_ERR_VALUE(ret) ? ret : 0;
+}
+
+static int sii9234_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct sii9234_context *ctx = sd_to_context(sd);
+
+ /* (dis/en)able TDMS output */
+ sii9234_writeb_mask(ctx->client, 0x1a, enable ? 0 : ~0 , 1 << 4);
+ return 0;
+}
+
+static const struct v4l2_subdev_core_ops sii9234_core_ops = {
+ .s_power = sii9234_s_power,
+};
+
+static const struct v4l2_subdev_video_ops sii9234_video_ops = {
+ .s_stream = sii9234_s_stream,
+};
+
+static const struct v4l2_subdev_ops sii9234_ops = {
+ .core = &sii9234_core_ops,
+ .video = &sii9234_video_ops,
+};
+
+static int __devinit sii9234_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct sii9234_platform_data *pdata = dev->platform_data;
+ struct sii9234_context *ctx;
+ int ret;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx) {
+ dev_err(dev, "out of memory\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+ ctx->client = client;
+
+ ctx->power = regulator_get(dev, "hdmi-en");
+ if (IS_ERR(ctx->power)) {
+ dev_err(dev, "failed to acquire regulator hdmi-en\n");
+ ret = PTR_ERR(ctx->power);
+ goto fail_ctx;
+ }
+
+ ctx->gpio_n_reset = pdata->gpio_n_reset;
+ ret = gpio_request(ctx->gpio_n_reset, "MHL_RST");
+ if (ret) {
+ dev_err(dev, "failed to acquire MHL_RST gpio\n");
+ goto fail_power;
+ }
+
+ v4l2_i2c_subdev_init(&ctx->sd, client, &sii9234_ops);
+
+ pm_runtime_enable(dev);
+
+ /* enable device */
+ ret = pm_runtime_get_sync(dev);
+ if (ret)
+ goto fail_pm;
+
+ /* verify chip version */
+ ret = sii9234_verify_version(client);
+ if (ret)
+ goto fail_pm_get;
+
+ /* stop processing */
+ pm_runtime_put(dev);
+
+ dev_info(dev, "probe successful\n");
+
+ return 0;
+
+fail_pm_get:
+ pm_runtime_put_sync(dev);
+
+fail_pm:
+ pm_runtime_disable(dev);
+ gpio_free(ctx->gpio_n_reset);
+
+fail_power:
+ regulator_put(ctx->power);
+
+fail_ctx:
+ kfree(ctx);
+
+fail:
+ dev_err(dev, "probe failed\n");
+
+ return ret;
+}
+
+static int __devexit sii9234_remove(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct sii9234_context *ctx = sd_to_context(sd);
+
+ pm_runtime_disable(dev);
+ gpio_free(ctx->gpio_n_reset);
+ regulator_put(ctx->power);
+ kfree(ctx);
+
+ dev_info(dev, "remove successful\n");
+
+ return 0;
+}
+
+
+static const struct i2c_device_id sii9234_id[] = {
+ { "SII9234", 0 },
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, sii9234_id);
+static struct i2c_driver sii9234_driver = {
+ .driver = {
+ .name = "sii9234",
+ .owner = THIS_MODULE,
+ .pm = &sii9234_pm_ops,
+ },
+ .probe = sii9234_probe,
+ .remove = __devexit_p(sii9234_remove),
+ .id_table = sii9234_id,
+};
+
+static int __init sii9234_init(void)
+{
+ return i2c_add_driver(&sii9234_driver);
+}
+module_init(sii9234_init);
+
+static void __exit sii9234_exit(void)
+{
+ i2c_del_driver(&sii9234_driver);
+}
+module_exit(sii9234_exit);
diff --git a/drivers/media/video/saa6588.c b/drivers/media/video/saa6588.c
new file mode 100644
index 00000000..0caac50d
--- /dev/null
+++ b/drivers/media/video/saa6588.c
@@ -0,0 +1,542 @@
+/*
+ Driver for SAA6588 RDS decoder
+
+ (c) 2005 Hans J. Koch
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/wait.h>
+#include <asm/uaccess.h>
+
+#include <media/saa6588.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+
+
+/* insmod options */
+static unsigned int debug;
+static unsigned int xtal;
+static unsigned int mmbs;
+static unsigned int plvl;
+static unsigned int bufblocks = 100;
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable debug messages");
+module_param(xtal, int, 0);
+MODULE_PARM_DESC(xtal, "select oscillator frequency (0..3), default 0");
+module_param(mmbs, int, 0);
+MODULE_PARM_DESC(mmbs, "enable MMBS mode: 0=off (default), 1=on");
+module_param(plvl, int, 0);
+MODULE_PARM_DESC(plvl, "select pause level (0..3), default 0");
+module_param(bufblocks, int, 0);
+MODULE_PARM_DESC(bufblocks, "number of buffered blocks, default 100");
+
+MODULE_DESCRIPTION("v4l2 driver module for SAA6588 RDS decoder");
+MODULE_AUTHOR("Hans J. Koch <koch@hjk-az.de>");
+
+MODULE_LICENSE("GPL");
+
+/* ---------------------------------------------------------------------- */
+
+#define UNSET (-1U)
+#define PREFIX "saa6588: "
+#define dprintk if (debug) printk
+
+struct saa6588 {
+ struct v4l2_subdev sd;
+ struct delayed_work work;
+ spinlock_t lock;
+ unsigned char *buffer;
+ unsigned int buf_size;
+ unsigned int rd_index;
+ unsigned int wr_index;
+ unsigned int block_count;
+ unsigned char last_blocknum;
+ wait_queue_head_t read_queue;
+ int data_available_for_read;
+ u8 sync;
+};
+
+static inline struct saa6588 *to_saa6588(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct saa6588, sd);
+}
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * SAA6588 defines
+ */
+
+/* Initialization and mode control byte (0w) */
+
+/* bit 0+1 (DAC0/DAC1) */
+#define cModeStandard 0x00
+#define cModeFastPI 0x01
+#define cModeReducedRequest 0x02
+#define cModeInvalid 0x03
+
+/* bit 2 (RBDS) */
+#define cProcessingModeRDS 0x00
+#define cProcessingModeRBDS 0x04
+
+/* bit 3+4 (SYM0/SYM1) */
+#define cErrCorrectionNone 0x00
+#define cErrCorrection2Bits 0x08
+#define cErrCorrection5Bits 0x10
+#define cErrCorrectionNoneRBDS 0x18
+
+/* bit 5 (NWSY) */
+#define cSyncNormal 0x00
+#define cSyncRestart 0x20
+
+/* bit 6 (TSQD) */
+#define cSigQualityDetectOFF 0x00
+#define cSigQualityDetectON 0x40
+
+/* bit 7 (SQCM) */
+#define cSigQualityTriggered 0x00
+#define cSigQualityContinous 0x80
+
+/* Pause level and flywheel control byte (1w) */
+
+/* bits 0..5 (FEB0..FEB5) */
+#define cFlywheelMaxBlocksMask 0x3F
+#define cFlywheelDefault 0x20
+
+/* bits 6+7 (PL0/PL1) */
+#define cPauseLevel_11mV 0x00
+#define cPauseLevel_17mV 0x40
+#define cPauseLevel_27mV 0x80
+#define cPauseLevel_43mV 0xC0
+
+/* Pause time/oscillator frequency/quality detector control byte (1w) */
+
+/* bits 0..4 (SQS0..SQS4) */
+#define cQualityDetectSensMask 0x1F
+#define cQualityDetectDefault 0x0F
+
+/* bit 5 (SOSC) */
+#define cSelectOscFreqOFF 0x00
+#define cSelectOscFreqON 0x20
+
+/* bit 6+7 (PTF0/PTF1) */
+#define cOscFreq_4332kHz 0x00
+#define cOscFreq_8664kHz 0x40
+#define cOscFreq_12996kHz 0x80
+#define cOscFreq_17328kHz 0xC0
+
+/* ---------------------------------------------------------------------- */
+
+static int block_to_user_buf(struct saa6588 *s, unsigned char __user *user_buf)
+{
+ int i;
+
+ if (s->rd_index == s->wr_index) {
+ if (debug > 2)
+ dprintk(PREFIX "Read: buffer empty.\n");
+ return 0;
+ }
+
+ if (debug > 2) {
+ dprintk(PREFIX "Read: ");
+ for (i = s->rd_index; i < s->rd_index + 3; i++)
+ dprintk("0x%02x ", s->buffer[i]);
+ }
+
+ if (copy_to_user(user_buf, &s->buffer[s->rd_index], 3))
+ return -EFAULT;
+
+ s->rd_index += 3;
+ if (s->rd_index >= s->buf_size)
+ s->rd_index = 0;
+ s->block_count--;
+
+ if (debug > 2)
+ dprintk("%d blocks total.\n", s->block_count);
+
+ return 1;
+}
+
+static void read_from_buf(struct saa6588 *s, struct saa6588_command *a)
+{
+ unsigned long flags;
+
+ unsigned char __user *buf_ptr = a->buffer;
+ unsigned int i;
+ unsigned int rd_blocks;
+
+ a->result = 0;
+ if (!a->buffer)
+ return;
+
+ while (!s->data_available_for_read) {
+ int ret = wait_event_interruptible(s->read_queue,
+ s->data_available_for_read);
+ if (ret == -ERESTARTSYS) {
+ a->result = -EINTR;
+ return;
+ }
+ }
+
+ spin_lock_irqsave(&s->lock, flags);
+ rd_blocks = a->block_count;
+ if (rd_blocks > s->block_count)
+ rd_blocks = s->block_count;
+
+ if (!rd_blocks) {
+ spin_unlock_irqrestore(&s->lock, flags);
+ return;
+ }
+
+ for (i = 0; i < rd_blocks; i++) {
+ if (block_to_user_buf(s, buf_ptr)) {
+ buf_ptr += 3;
+ a->result++;
+ } else
+ break;
+ }
+ a->result *= 3;
+ s->data_available_for_read = (s->block_count > 0);
+ spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static void block_to_buf(struct saa6588 *s, unsigned char *blockbuf)
+{
+ unsigned int i;
+
+ if (debug > 3)
+ dprintk(PREFIX "New block: ");
+
+ for (i = 0; i < 3; ++i) {
+ if (debug > 3)
+ dprintk("0x%02x ", blockbuf[i]);
+ s->buffer[s->wr_index] = blockbuf[i];
+ s->wr_index++;
+ }
+
+ if (s->wr_index >= s->buf_size)
+ s->wr_index = 0;
+
+ if (s->wr_index == s->rd_index) {
+ s->rd_index += 3;
+ if (s->rd_index >= s->buf_size)
+ s->rd_index = 0;
+ } else
+ s->block_count++;
+
+ if (debug > 3)
+ dprintk("%d blocks total.\n", s->block_count);
+}
+
+static void saa6588_i2c_poll(struct saa6588 *s)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s->sd);
+ unsigned long flags;
+ unsigned char tmpbuf[6];
+ unsigned char blocknum;
+ unsigned char tmp;
+
+ /* Although we only need 3 bytes, we have to read at least 6.
+ SAA6588 returns garbage otherwise. */
+ if (6 != i2c_master_recv(client, &tmpbuf[0], 6)) {
+ if (debug > 1)
+ dprintk(PREFIX "read error!\n");
+ return;
+ }
+
+ s->sync = tmpbuf[0] & 0x10;
+ if (!s->sync)
+ return;
+ blocknum = tmpbuf[0] >> 5;
+ if (blocknum == s->last_blocknum) {
+ if (debug > 3)
+ dprintk("Saw block %d again.\n", blocknum);
+ return;
+ }
+
+ s->last_blocknum = blocknum;
+
+ /*
+ Byte order according to v4l2 specification:
+
+ Byte 0: Least Significant Byte of RDS Block
+ Byte 1: Most Significant Byte of RDS Block
+ Byte 2 Bit 7: Error bit. Indicates that an uncorrectable error
+ occurred during reception of this block.
+ Bit 6: Corrected bit. Indicates that an error was
+ corrected for this data block.
+ Bits 5-3: Same as bits 0-2.
+ Bits 2-0: Block number.
+
+ SAA6588 byte order is Status-MSB-LSB, so we have to swap the
+ first and the last of the 3 bytes block.
+ */
+
+ tmp = tmpbuf[2];
+ tmpbuf[2] = tmpbuf[0];
+ tmpbuf[0] = tmp;
+
+ /* Map 'Invalid block E' to 'Invalid Block' */
+ if (blocknum == 6)
+ blocknum = V4L2_RDS_BLOCK_INVALID;
+ /* And if are not in mmbs mode, then 'Block E' is also mapped
+ to 'Invalid Block'. As far as I can tell MMBS is discontinued,
+ and if there is ever a need to support E blocks, then please
+ contact the linux-media mailinglist. */
+ else if (!mmbs && blocknum == 5)
+ blocknum = V4L2_RDS_BLOCK_INVALID;
+ tmp = blocknum;
+ tmp |= blocknum << 3; /* Received offset == Offset Name (OK ?) */
+ if ((tmpbuf[2] & 0x03) == 0x03)
+ tmp |= V4L2_RDS_BLOCK_ERROR; /* uncorrectable error */
+ else if ((tmpbuf[2] & 0x03) != 0x00)
+ tmp |= V4L2_RDS_BLOCK_CORRECTED; /* corrected error */
+ tmpbuf[2] = tmp; /* Is this enough ? Should we also check other bits ? */
+
+ spin_lock_irqsave(&s->lock, flags);
+ block_to_buf(s, tmpbuf);
+ spin_unlock_irqrestore(&s->lock, flags);
+ s->data_available_for_read = 1;
+ wake_up_interruptible(&s->read_queue);
+}
+
+static void saa6588_work(struct work_struct *work)
+{
+ struct saa6588 *s = container_of(work, struct saa6588, work.work);
+
+ saa6588_i2c_poll(s);
+ schedule_delayed_work(&s->work, msecs_to_jiffies(20));
+}
+
+static void saa6588_configure(struct saa6588 *s)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s->sd);
+ unsigned char buf[3];
+ int rc;
+
+ buf[0] = cSyncRestart;
+ if (mmbs)
+ buf[0] |= cProcessingModeRBDS;
+
+ buf[1] = cFlywheelDefault;
+ switch (plvl) {
+ case 0:
+ buf[1] |= cPauseLevel_11mV;
+ break;
+ case 1:
+ buf[1] |= cPauseLevel_17mV;
+ break;
+ case 2:
+ buf[1] |= cPauseLevel_27mV;
+ break;
+ case 3:
+ buf[1] |= cPauseLevel_43mV;
+ break;
+ default: /* nothing */
+ break;
+ }
+
+ buf[2] = cQualityDetectDefault | cSelectOscFreqON;
+
+ switch (xtal) {
+ case 0:
+ buf[2] |= cOscFreq_4332kHz;
+ break;
+ case 1:
+ buf[2] |= cOscFreq_8664kHz;
+ break;
+ case 2:
+ buf[2] |= cOscFreq_12996kHz;
+ break;
+ case 3:
+ buf[2] |= cOscFreq_17328kHz;
+ break;
+ default: /* nothing */
+ break;
+ }
+
+ dprintk(PREFIX "writing: 0w=0x%02x 1w=0x%02x 2w=0x%02x\n",
+ buf[0], buf[1], buf[2]);
+
+ rc = i2c_master_send(client, buf, 3);
+ if (rc != 3)
+ printk(PREFIX "i2c i/o error: rc == %d (should be 3)\n", rc);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static long saa6588_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+ struct saa6588 *s = to_saa6588(sd);
+ struct saa6588_command *a = arg;
+
+ switch (cmd) {
+ /* --- open() for /dev/radio --- */
+ case SAA6588_CMD_OPEN:
+ a->result = 0; /* return error if chip doesn't work ??? */
+ break;
+ /* --- close() for /dev/radio --- */
+ case SAA6588_CMD_CLOSE:
+ s->data_available_for_read = 1;
+ wake_up_interruptible(&s->read_queue);
+ a->result = 0;
+ break;
+ /* --- read() for /dev/radio --- */
+ case SAA6588_CMD_READ:
+ read_from_buf(s, a);
+ break;
+ /* --- poll() for /dev/radio --- */
+ case SAA6588_CMD_POLL:
+ a->result = 0;
+ if (s->data_available_for_read) {
+ a->result |= POLLIN | POLLRDNORM;
+ }
+ poll_wait(a->instance, &s->read_queue, a->event_list);
+ break;
+
+ default:
+ /* nothing */
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static int saa6588_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct saa6588 *s = to_saa6588(sd);
+
+ vt->capability |= V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO;
+ if (s->sync)
+ vt->rxsubchans |= V4L2_TUNER_SUB_RDS;
+ return 0;
+}
+
+static int saa6588_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct saa6588 *s = to_saa6588(sd);
+
+ saa6588_configure(s);
+ return 0;
+}
+
+static int saa6588_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA6588, 0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops saa6588_core_ops = {
+ .g_chip_ident = saa6588_g_chip_ident,
+ .ioctl = saa6588_ioctl,
+};
+
+static const struct v4l2_subdev_tuner_ops saa6588_tuner_ops = {
+ .g_tuner = saa6588_g_tuner,
+ .s_tuner = saa6588_s_tuner,
+};
+
+static const struct v4l2_subdev_ops saa6588_ops = {
+ .core = &saa6588_core_ops,
+ .tuner = &saa6588_tuner_ops,
+};
+
+/* ---------------------------------------------------------------------- */
+
+static int saa6588_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct saa6588 *s;
+ struct v4l2_subdev *sd;
+
+ v4l_info(client, "saa6588 found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ s = kzalloc(sizeof(*s), GFP_KERNEL);
+ if (s == NULL)
+ return -ENOMEM;
+
+ s->buf_size = bufblocks * 3;
+
+ s->buffer = kmalloc(s->buf_size, GFP_KERNEL);
+ if (s->buffer == NULL) {
+ kfree(s);
+ return -ENOMEM;
+ }
+ sd = &s->sd;
+ v4l2_i2c_subdev_init(sd, client, &saa6588_ops);
+ spin_lock_init(&s->lock);
+ s->block_count = 0;
+ s->wr_index = 0;
+ s->rd_index = 0;
+ s->last_blocknum = 0xff;
+ init_waitqueue_head(&s->read_queue);
+ s->data_available_for_read = 0;
+
+ saa6588_configure(s);
+
+ /* start polling via eventd */
+ INIT_DELAYED_WORK(&s->work, saa6588_work);
+ schedule_delayed_work(&s->work, 0);
+ return 0;
+}
+
+static int saa6588_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct saa6588 *s = to_saa6588(sd);
+
+ v4l2_device_unregister_subdev(sd);
+
+ cancel_delayed_work_sync(&s->work);
+
+ kfree(s->buffer);
+ kfree(s);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct i2c_device_id saa6588_id[] = {
+ { "saa6588", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, saa6588_id);
+
+static struct i2c_driver saa6588_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "saa6588",
+ },
+ .probe = saa6588_probe,
+ .remove = saa6588_remove,
+ .id_table = saa6588_id,
+};
+
+module_i2c_driver(saa6588_driver);
diff --git a/drivers/media/video/saa7110.c b/drivers/media/video/saa7110.c
new file mode 100644
index 00000000..51cd4c8f
--- /dev/null
+++ b/drivers/media/video/saa7110.c
@@ -0,0 +1,494 @@
+/*
+ * saa7110 - Philips SAA7110(A) video decoder driver
+ *
+ * Copyright (C) 1998 Pauline Middelink <middelin@polyware.nl>
+ *
+ * Copyright (C) 1999 Wolfgang Scherr <scherr@net4you.net>
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ * - some corrections for Pinnacle Systems Inc. DC10plus card.
+ *
+ * Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * - moved over to linux>=2.4.x i2c protocol (1/1/2003)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <asm/uaccess.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+
+MODULE_DESCRIPTION("Philips SAA7110 video decoder driver");
+MODULE_AUTHOR("Pauline Middelink");
+MODULE_LICENSE("GPL");
+
+
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+#define SAA7110_MAX_INPUT 9 /* 6 CVBS, 3 SVHS */
+#define SAA7110_MAX_OUTPUT 1 /* 1 YUV */
+
+#define SAA7110_NR_REG 0x35
+
+struct saa7110 {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+ u8 reg[SAA7110_NR_REG];
+
+ v4l2_std_id norm;
+ int input;
+ int enable;
+
+ wait_queue_head_t wq;
+};
+
+static inline struct saa7110 *to_saa7110(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct saa7110, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct saa7110, hdl)->sd;
+}
+
+/* ----------------------------------------------------------------------- */
+/* I2C support functions */
+/* ----------------------------------------------------------------------- */
+
+static int saa7110_write(struct v4l2_subdev *sd, u8 reg, u8 value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct saa7110 *decoder = to_saa7110(sd);
+
+ decoder->reg[reg] = value;
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static int saa7110_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct saa7110 *decoder = to_saa7110(sd);
+ int ret = -1;
+ u8 reg = *data; /* first register to write to */
+
+ /* Sanity check */
+ if (reg + (len - 1) > SAA7110_NR_REG)
+ return ret;
+
+ /* the saa7110 has an autoincrement function, use it if
+ * the adapter understands raw I2C */
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ ret = i2c_master_send(client, data, len);
+
+ /* Cache the written data */
+ memcpy(decoder->reg + reg, data + 1, len - 1);
+ } else {
+ for (++data, --len; len; len--) {
+ ret = saa7110_write(sd, reg++, *data++);
+ if (ret < 0)
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static inline int saa7110_read(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_read_byte(client);
+}
+
+/* ----------------------------------------------------------------------- */
+/* SAA7110 functions */
+/* ----------------------------------------------------------------------- */
+
+#define FRESP_06H_COMPST 0x03 /*0x13*/
+#define FRESP_06H_SVIDEO 0x83 /*0xC0*/
+
+
+static int saa7110_selmux(struct v4l2_subdev *sd, int chan)
+{
+ static const unsigned char modes[9][8] = {
+ /* mode 0 */
+ {FRESP_06H_COMPST, 0xD9, 0x17, 0x40, 0x03,
+ 0x44, 0x75, 0x16},
+ /* mode 1 */
+ {FRESP_06H_COMPST, 0xD8, 0x17, 0x40, 0x03,
+ 0x44, 0x75, 0x16},
+ /* mode 2 */
+ {FRESP_06H_COMPST, 0xBA, 0x07, 0x91, 0x03,
+ 0x60, 0xB5, 0x05},
+ /* mode 3 */
+ {FRESP_06H_COMPST, 0xB8, 0x07, 0x91, 0x03,
+ 0x60, 0xB5, 0x05},
+ /* mode 4 */
+ {FRESP_06H_COMPST, 0x7C, 0x07, 0xD2, 0x83,
+ 0x60, 0xB5, 0x03},
+ /* mode 5 */
+ {FRESP_06H_COMPST, 0x78, 0x07, 0xD2, 0x83,
+ 0x60, 0xB5, 0x03},
+ /* mode 6 */
+ {FRESP_06H_SVIDEO, 0x59, 0x17, 0x42, 0xA3,
+ 0x44, 0x75, 0x12},
+ /* mode 7 */
+ {FRESP_06H_SVIDEO, 0x9A, 0x17, 0xB1, 0x13,
+ 0x60, 0xB5, 0x14},
+ /* mode 8 */
+ {FRESP_06H_SVIDEO, 0x3C, 0x27, 0xC1, 0x23,
+ 0x44, 0x75, 0x21}
+ };
+ struct saa7110 *decoder = to_saa7110(sd);
+ const unsigned char *ptr = modes[chan];
+
+ saa7110_write(sd, 0x06, ptr[0]); /* Luminance control */
+ saa7110_write(sd, 0x20, ptr[1]); /* Analog Control #1 */
+ saa7110_write(sd, 0x21, ptr[2]); /* Analog Control #2 */
+ saa7110_write(sd, 0x22, ptr[3]); /* Mixer Control #1 */
+ saa7110_write(sd, 0x2C, ptr[4]); /* Mixer Control #2 */
+ saa7110_write(sd, 0x30, ptr[5]); /* ADCs gain control */
+ saa7110_write(sd, 0x31, ptr[6]); /* Mixer Control #3 */
+ saa7110_write(sd, 0x21, ptr[7]); /* Analog Control #2 */
+ decoder->input = chan;
+
+ return 0;
+}
+
+static const unsigned char initseq[1 + SAA7110_NR_REG] = {
+ 0, 0x4C, 0x3C, 0x0D, 0xEF, 0xBD, 0xF2, 0x03, 0x00,
+ /* 0x08 */ 0xF8, 0xF8, 0x60, 0x60, 0x00, 0x86, 0x18, 0x90,
+ /* 0x10 */ 0x00, 0x59, 0x40, 0x46, 0x42, 0x1A, 0xFF, 0xDA,
+ /* 0x18 */ 0xF2, 0x8B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x20 */ 0xD9, 0x16, 0x40, 0x41, 0x80, 0x41, 0x80, 0x4F,
+ /* 0x28 */ 0xFE, 0x01, 0xCF, 0x0F, 0x03, 0x01, 0x03, 0x0C,
+ /* 0x30 */ 0x44, 0x71, 0x02, 0x8C, 0x02
+};
+
+static v4l2_std_id determine_norm(struct v4l2_subdev *sd)
+{
+ DEFINE_WAIT(wait);
+ struct saa7110 *decoder = to_saa7110(sd);
+ int status;
+
+ /* mode changed, start automatic detection */
+ saa7110_write_block(sd, initseq, sizeof(initseq));
+ saa7110_selmux(sd, decoder->input);
+ prepare_to_wait(&decoder->wq, &wait, TASK_UNINTERRUPTIBLE);
+ schedule_timeout(msecs_to_jiffies(250));
+ finish_wait(&decoder->wq, &wait);
+ status = saa7110_read(sd);
+ if (status & 0x40) {
+ v4l2_dbg(1, debug, sd, "status=0x%02x (no signal)\n", status);
+ return decoder->norm; /* no change*/
+ }
+ if ((status & 3) == 0) {
+ saa7110_write(sd, 0x06, 0x83);
+ if (status & 0x20) {
+ v4l2_dbg(1, debug, sd, "status=0x%02x (NTSC/no color)\n", status);
+ /*saa7110_write(sd,0x2E,0x81);*/
+ return V4L2_STD_NTSC;
+ }
+ v4l2_dbg(1, debug, sd, "status=0x%02x (PAL/no color)\n", status);
+ /*saa7110_write(sd,0x2E,0x9A);*/
+ return V4L2_STD_PAL;
+ }
+ /*saa7110_write(sd,0x06,0x03);*/
+ if (status & 0x20) { /* 60Hz */
+ v4l2_dbg(1, debug, sd, "status=0x%02x (NTSC)\n", status);
+ saa7110_write(sd, 0x0D, 0x86);
+ saa7110_write(sd, 0x0F, 0x50);
+ saa7110_write(sd, 0x11, 0x2C);
+ /*saa7110_write(sd,0x2E,0x81);*/
+ return V4L2_STD_NTSC;
+ }
+
+ /* 50Hz -> PAL/SECAM */
+ saa7110_write(sd, 0x0D, 0x86);
+ saa7110_write(sd, 0x0F, 0x10);
+ saa7110_write(sd, 0x11, 0x59);
+ /*saa7110_write(sd,0x2E,0x9A);*/
+
+ prepare_to_wait(&decoder->wq, &wait, TASK_UNINTERRUPTIBLE);
+ schedule_timeout(msecs_to_jiffies(250));
+ finish_wait(&decoder->wq, &wait);
+
+ status = saa7110_read(sd);
+ if ((status & 0x03) == 0x01) {
+ v4l2_dbg(1, debug, sd, "status=0x%02x (SECAM)\n", status);
+ saa7110_write(sd, 0x0D, 0x87);
+ return V4L2_STD_SECAM;
+ }
+ v4l2_dbg(1, debug, sd, "status=0x%02x (PAL)\n", status);
+ return V4L2_STD_PAL;
+}
+
+static int saa7110_g_input_status(struct v4l2_subdev *sd, u32 *pstatus)
+{
+ struct saa7110 *decoder = to_saa7110(sd);
+ int res = V4L2_IN_ST_NO_SIGNAL;
+ int status = saa7110_read(sd);
+
+ v4l2_dbg(1, debug, sd, "status=0x%02x norm=%llx\n",
+ status, (unsigned long long)decoder->norm);
+ if (!(status & 0x40))
+ res = 0;
+ if (!(status & 0x03))
+ res |= V4L2_IN_ST_NO_COLOR;
+
+ *pstatus = res;
+ return 0;
+}
+
+static int saa7110_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ *(v4l2_std_id *)std = determine_norm(sd);
+ return 0;
+}
+
+static int saa7110_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct saa7110 *decoder = to_saa7110(sd);
+
+ if (decoder->norm != std) {
+ decoder->norm = std;
+ /*saa7110_write(sd, 0x06, 0x03);*/
+ if (std & V4L2_STD_NTSC) {
+ saa7110_write(sd, 0x0D, 0x86);
+ saa7110_write(sd, 0x0F, 0x50);
+ saa7110_write(sd, 0x11, 0x2C);
+ /*saa7110_write(sd, 0x2E, 0x81);*/
+ v4l2_dbg(1, debug, sd, "switched to NTSC\n");
+ } else if (std & V4L2_STD_PAL) {
+ saa7110_write(sd, 0x0D, 0x86);
+ saa7110_write(sd, 0x0F, 0x10);
+ saa7110_write(sd, 0x11, 0x59);
+ /*saa7110_write(sd, 0x2E, 0x9A);*/
+ v4l2_dbg(1, debug, sd, "switched to PAL\n");
+ } else if (std & V4L2_STD_SECAM) {
+ saa7110_write(sd, 0x0D, 0x87);
+ saa7110_write(sd, 0x0F, 0x10);
+ saa7110_write(sd, 0x11, 0x59);
+ /*saa7110_write(sd, 0x2E, 0x9A);*/
+ v4l2_dbg(1, debug, sd, "switched to SECAM\n");
+ } else {
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+static int saa7110_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct saa7110 *decoder = to_saa7110(sd);
+
+ if (input >= SAA7110_MAX_INPUT) {
+ v4l2_dbg(1, debug, sd, "input=%d not available\n", input);
+ return -EINVAL;
+ }
+ if (decoder->input != input) {
+ saa7110_selmux(sd, input);
+ v4l2_dbg(1, debug, sd, "switched to input=%d\n", input);
+ }
+ return 0;
+}
+
+static int saa7110_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct saa7110 *decoder = to_saa7110(sd);
+
+ if (decoder->enable != enable) {
+ decoder->enable = enable;
+ saa7110_write(sd, 0x0E, enable ? 0x18 : 0x80);
+ v4l2_dbg(1, debug, sd, "YUV %s\n", enable ? "on" : "off");
+ }
+ return 0;
+}
+
+static int saa7110_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ saa7110_write(sd, 0x19, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ saa7110_write(sd, 0x13, ctrl->val);
+ break;
+ case V4L2_CID_SATURATION:
+ saa7110_write(sd, 0x12, ctrl->val);
+ break;
+ case V4L2_CID_HUE:
+ saa7110_write(sd, 0x07, ctrl->val);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int saa7110_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7110, 0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_ctrl_ops saa7110_ctrl_ops = {
+ .s_ctrl = saa7110_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops saa7110_core_ops = {
+ .g_chip_ident = saa7110_g_chip_ident,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+ .s_std = saa7110_s_std,
+};
+
+static const struct v4l2_subdev_video_ops saa7110_video_ops = {
+ .s_routing = saa7110_s_routing,
+ .s_stream = saa7110_s_stream,
+ .querystd = saa7110_querystd,
+ .g_input_status = saa7110_g_input_status,
+};
+
+static const struct v4l2_subdev_ops saa7110_ops = {
+ .core = &saa7110_core_ops,
+ .video = &saa7110_video_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7110_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct saa7110 *decoder;
+ struct v4l2_subdev *sd;
+ int rv;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
+ return -ENODEV;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ decoder = kzalloc(sizeof(struct saa7110), GFP_KERNEL);
+ if (!decoder)
+ return -ENOMEM;
+ sd = &decoder->sd;
+ v4l2_i2c_subdev_init(sd, client, &saa7110_ops);
+ decoder->norm = V4L2_STD_PAL;
+ decoder->input = 0;
+ decoder->enable = 1;
+ v4l2_ctrl_handler_init(&decoder->hdl, 2);
+ v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops,
+ V4L2_CID_HUE, -128, 127, 1, 0);
+ sd->ctrl_handler = &decoder->hdl;
+ if (decoder->hdl.error) {
+ int err = decoder->hdl.error;
+
+ v4l2_ctrl_handler_free(&decoder->hdl);
+ kfree(decoder);
+ return err;
+ }
+ v4l2_ctrl_handler_setup(&decoder->hdl);
+
+ init_waitqueue_head(&decoder->wq);
+
+ rv = saa7110_write_block(sd, initseq, sizeof(initseq));
+ if (rv < 0) {
+ v4l2_dbg(1, debug, sd, "init status %d\n", rv);
+ } else {
+ int ver, status;
+ saa7110_write(sd, 0x21, 0x10);
+ saa7110_write(sd, 0x0e, 0x18);
+ saa7110_write(sd, 0x0D, 0x04);
+ ver = saa7110_read(sd);
+ saa7110_write(sd, 0x0D, 0x06);
+ /*mdelay(150);*/
+ status = saa7110_read(sd);
+ v4l2_dbg(1, debug, sd, "version %x, status=0x%02x\n",
+ ver, status);
+ saa7110_write(sd, 0x0D, 0x86);
+ saa7110_write(sd, 0x0F, 0x10);
+ saa7110_write(sd, 0x11, 0x59);
+ /*saa7110_write(sd, 0x2E, 0x9A);*/
+ }
+
+ /*saa7110_selmux(sd,0);*/
+ /*determine_norm(sd);*/
+ /* setup and implicit mode 0 select has been performed */
+
+ return 0;
+}
+
+static int saa7110_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct saa7110 *decoder = to_saa7110(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&decoder->hdl);
+ kfree(decoder);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct i2c_device_id saa7110_id[] = {
+ { "saa7110", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, saa7110_id);
+
+static struct i2c_driver saa7110_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "saa7110",
+ },
+ .probe = saa7110_probe,
+ .remove = saa7110_remove,
+ .id_table = saa7110_id,
+};
+
+module_i2c_driver(saa7110_driver);
diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c
new file mode 100644
index 00000000..2107336c
--- /dev/null
+++ b/drivers/media/video/saa7115.c
@@ -0,0 +1,1727 @@
+/* saa711x - Philips SAA711x video decoder driver
+ * This driver can work with saa7111, saa7111a, saa7113, saa7114,
+ * saa7115 and saa7118.
+ *
+ * Based on saa7114 driver by Maxim Yevtyushkin, which is based on
+ * the saa7111 driver by Dave Perks.
+ *
+ * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+ * Copyright (C) 2002 Maxim Yevtyushkin <max@linuxmedialabs.com>
+ *
+ * Slight changes for video timing and attachment output by
+ * Wolfgang Scherr <scherr@net4you.net>
+ *
+ * Moved over to the linux >= 2.4.x i2c protocol (1/1/2003)
+ * by Ronald Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * Added saa7115 support by Kevin Thayer <nufan_wfk at yahoo.com>
+ * (2/17/2003)
+ *
+ * VBI support (2004) and cleanups (2005) by Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * Copyright (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org>
+ * SAA7111, SAA7113 and SAA7118 support
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "saa711x_regs.h"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/saa7115.h>
+#include <asm/div64.h>
+
+#define VRES_60HZ (480+16)
+
+MODULE_DESCRIPTION("Philips SAA7111/SAA7113/SAA7114/SAA7115/SAA7118 video decoder driver");
+MODULE_AUTHOR( "Maxim Yevtyushkin, Kevin Thayer, Chris Kennedy, "
+ "Hans Verkuil, Mauro Carvalho Chehab");
+MODULE_LICENSE("GPL");
+
+static bool debug;
+module_param(debug, bool, 0644);
+
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+
+struct saa711x_state {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+
+ struct {
+ /* chroma gain control cluster */
+ struct v4l2_ctrl *agc;
+ struct v4l2_ctrl *gain;
+ };
+
+ v4l2_std_id std;
+ int input;
+ int output;
+ int enable;
+ int radio;
+ int width;
+ int height;
+ u32 ident;
+ u32 audclk_freq;
+ u32 crystal_freq;
+ u8 ucgc;
+ u8 cgcdiv;
+ u8 apll;
+};
+
+static inline struct saa711x_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct saa711x_state, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct saa711x_state, hdl)->sd;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static inline int saa711x_write(struct v4l2_subdev *sd, u8 reg, u8 value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/* Sanity routine to check if a register is present */
+static int saa711x_has_reg(const int id, const u8 reg)
+{
+ if (id == V4L2_IDENT_SAA7111)
+ return reg < 0x20 && reg != 0x01 && reg != 0x0f &&
+ (reg < 0x13 || reg > 0x19) && reg != 0x1d && reg != 0x1e;
+ if (id == V4L2_IDENT_SAA7111A)
+ return reg < 0x20 && reg != 0x01 && reg != 0x0f &&
+ reg != 0x14 && reg != 0x18 && reg != 0x19 &&
+ reg != 0x1d && reg != 0x1e;
+
+ /* common for saa7113/4/5/8 */
+ if (unlikely((reg >= 0x3b && reg <= 0x3f) || reg == 0x5c || reg == 0x5f ||
+ reg == 0xa3 || reg == 0xa7 || reg == 0xab || reg == 0xaf || (reg >= 0xb5 && reg <= 0xb7) ||
+ reg == 0xd3 || reg == 0xd7 || reg == 0xdb || reg == 0xdf || (reg >= 0xe5 && reg <= 0xe7) ||
+ reg == 0x82 || (reg >= 0x89 && reg <= 0x8e)))
+ return 0;
+
+ switch (id) {
+ case V4L2_IDENT_SAA7113:
+ return reg != 0x14 && (reg < 0x18 || reg > 0x1e) && (reg < 0x20 || reg > 0x3f) &&
+ reg != 0x5d && reg < 0x63;
+ case V4L2_IDENT_SAA7114:
+ return (reg < 0x1a || reg > 0x1e) && (reg < 0x20 || reg > 0x2f) &&
+ (reg < 0x63 || reg > 0x7f) && reg != 0x33 && reg != 0x37 &&
+ reg != 0x81 && reg < 0xf0;
+ case V4L2_IDENT_SAA7115:
+ return (reg < 0x20 || reg > 0x2f) && reg != 0x65 && (reg < 0xfc || reg > 0xfe);
+ case V4L2_IDENT_SAA7118:
+ return (reg < 0x1a || reg > 0x1d) && (reg < 0x20 || reg > 0x22) &&
+ (reg < 0x26 || reg > 0x28) && reg != 0x33 && reg != 0x37 &&
+ (reg < 0x63 || reg > 0x7f) && reg != 0x81 && reg < 0xf0;
+ }
+ return 1;
+}
+
+static int saa711x_writeregs(struct v4l2_subdev *sd, const unsigned char *regs)
+{
+ struct saa711x_state *state = to_state(sd);
+ unsigned char reg, data;
+
+ while (*regs != 0x00) {
+ reg = *(regs++);
+ data = *(regs++);
+
+ /* According with datasheets, reserved regs should be
+ filled with 0 - seems better not to touch on they */
+ if (saa711x_has_reg(state->ident, reg)) {
+ if (saa711x_write(sd, reg, data) < 0)
+ return -1;
+ } else {
+ v4l2_dbg(1, debug, sd, "tried to access reserved reg 0x%02x\n", reg);
+ }
+ }
+ return 0;
+}
+
+static inline int saa711x_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+/* SAA7111 initialization table */
+static const unsigned char saa7111_init[] = {
+ R_01_INC_DELAY, 0x00, /* reserved */
+
+ /*front end */
+ R_02_INPUT_CNTL_1, 0xd0, /* FUSE=3, GUDL=2, MODE=0 */
+ R_03_INPUT_CNTL_2, 0x23, /* HLNRS=0, VBSL=1, WPOFF=0, HOLDG=0,
+ * GAFIX=0, GAI1=256, GAI2=256 */
+ R_04_INPUT_CNTL_3, 0x00, /* GAI1=256 */
+ R_05_INPUT_CNTL_4, 0x00, /* GAI2=256 */
+
+ /* decoder */
+ R_06_H_SYNC_START, 0xf3, /* HSB at 13(50Hz) / 17(60Hz)
+ * pixels after end of last line */
+ R_07_H_SYNC_STOP, 0xe8, /* HSS seems to be needed to
+ * work with NTSC, too */
+ R_08_SYNC_CNTL, 0xc8, /* AUFD=1, FSEL=1, EXFIL=0,
+ * VTRC=1, HPLL=0, VNOI=0 */
+ R_09_LUMA_CNTL, 0x01, /* BYPS=0, PREF=0, BPSS=0,
+ * VBLB=0, UPTCV=0, APER=1 */
+ R_0A_LUMA_BRIGHT_CNTL, 0x80,
+ R_0B_LUMA_CONTRAST_CNTL, 0x47, /* 0b - CONT=1.109 */
+ R_0C_CHROMA_SAT_CNTL, 0x40,
+ R_0D_CHROMA_HUE_CNTL, 0x00,
+ R_0E_CHROMA_CNTL_1, 0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0,
+ * FCTC=0, CHBW=1 */
+ R_0F_CHROMA_GAIN_CNTL, 0x00, /* reserved */
+ R_10_CHROMA_CNTL_2, 0x48, /* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */
+ R_11_MODE_DELAY_CNTL, 0x1c, /* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1,
+ * OEYC=1, OEHV=1, VIPB=0, COLO=0 */
+ R_12_RT_SIGNAL_CNTL, 0x00, /* 12 - output control 2 */
+ R_13_RT_X_PORT_OUT_CNTL, 0x00, /* 13 - output control 3 */
+ R_14_ANAL_ADC_COMPAT_CNTL, 0x00,
+ R_15_VGATE_START_FID_CHG, 0x00,
+ R_16_VGATE_STOP, 0x00,
+ R_17_MISC_VGATE_CONF_AND_MSB, 0x00,
+
+ 0x00, 0x00
+};
+
+/* SAA7113 init codes */
+static const unsigned char saa7113_init[] = {
+ R_01_INC_DELAY, 0x08,
+ R_02_INPUT_CNTL_1, 0xc2,
+ R_03_INPUT_CNTL_2, 0x30,
+ R_04_INPUT_CNTL_3, 0x00,
+ R_05_INPUT_CNTL_4, 0x00,
+ R_06_H_SYNC_START, 0x89,
+ R_07_H_SYNC_STOP, 0x0d,
+ R_08_SYNC_CNTL, 0x88,
+ R_09_LUMA_CNTL, 0x01,
+ R_0A_LUMA_BRIGHT_CNTL, 0x80,
+ R_0B_LUMA_CONTRAST_CNTL, 0x47,
+ R_0C_CHROMA_SAT_CNTL, 0x40,
+ R_0D_CHROMA_HUE_CNTL, 0x00,
+ R_0E_CHROMA_CNTL_1, 0x01,
+ R_0F_CHROMA_GAIN_CNTL, 0x2a,
+ R_10_CHROMA_CNTL_2, 0x08,
+ R_11_MODE_DELAY_CNTL, 0x0c,
+ R_12_RT_SIGNAL_CNTL, 0x07,
+ R_13_RT_X_PORT_OUT_CNTL, 0x00,
+ R_14_ANAL_ADC_COMPAT_CNTL, 0x00,
+ R_15_VGATE_START_FID_CHG, 0x00,
+ R_16_VGATE_STOP, 0x00,
+ R_17_MISC_VGATE_CONF_AND_MSB, 0x00,
+
+ 0x00, 0x00
+};
+
+/* If a value differs from the Hauppauge driver values, then the comment starts with
+ 'was 0xXX' to denote the Hauppauge value. Otherwise the value is identical to what the
+ Hauppauge driver sets. */
+
+/* SAA7114 and SAA7115 initialization table */
+static const unsigned char saa7115_init_auto_input[] = {
+ /* Front-End Part */
+ R_01_INC_DELAY, 0x48, /* white peak control disabled */
+ R_03_INPUT_CNTL_2, 0x20, /* was 0x30. 0x20: long vertical blanking */
+ R_04_INPUT_CNTL_3, 0x90, /* analog gain set to 0 */
+ R_05_INPUT_CNTL_4, 0x90, /* analog gain set to 0 */
+ /* Decoder Part */
+ R_06_H_SYNC_START, 0xeb, /* horiz sync begin = -21 */
+ R_07_H_SYNC_STOP, 0xe0, /* horiz sync stop = -17 */
+ R_09_LUMA_CNTL, 0x53, /* 0x53, was 0x56 for 60hz. luminance control */
+ R_0A_LUMA_BRIGHT_CNTL, 0x80, /* was 0x88. decoder brightness, 0x80 is itu standard */
+ R_0B_LUMA_CONTRAST_CNTL, 0x44, /* was 0x48. decoder contrast, 0x44 is itu standard */
+ R_0C_CHROMA_SAT_CNTL, 0x40, /* was 0x47. decoder saturation, 0x40 is itu standard */
+ R_0D_CHROMA_HUE_CNTL, 0x00,
+ R_0F_CHROMA_GAIN_CNTL, 0x00, /* use automatic gain */
+ R_10_CHROMA_CNTL_2, 0x06, /* chroma: active adaptive combfilter */
+ R_11_MODE_DELAY_CNTL, 0x00,
+ R_12_RT_SIGNAL_CNTL, 0x9d, /* RTS0 output control: VGATE */
+ R_13_RT_X_PORT_OUT_CNTL, 0x80, /* ITU656 standard mode, RTCO output enable RTCE */
+ R_14_ANAL_ADC_COMPAT_CNTL, 0x00,
+ R_18_RAW_DATA_GAIN_CNTL, 0x40, /* gain 0x00 = nominal */
+ R_19_RAW_DATA_OFF_CNTL, 0x80,
+ R_1A_COLOR_KILL_LVL_CNTL, 0x77, /* recommended value */
+ R_1B_MISC_TVVCRDET, 0x42, /* recommended value */
+ R_1C_ENHAN_COMB_CTRL1, 0xa9, /* recommended value */
+ R_1D_ENHAN_COMB_CTRL2, 0x01, /* recommended value */
+
+
+ R_80_GLOBAL_CNTL_1, 0x0, /* No tasks enabled at init */
+
+ /* Power Device Control */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset device */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* set device programmed, all in operational mode */
+ 0x00, 0x00
+};
+
+/* Used to reset saa7113, saa7114 and saa7115 */
+static const unsigned char saa7115_cfg_reset_scaler[] = {
+ R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x00, /* disable I-port output */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */
+ R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* enable I-port output */
+ 0x00, 0x00
+};
+
+/* ============== SAA7715 VIDEO templates ============= */
+
+static const unsigned char saa7115_cfg_60hz_video[] = {
+ R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */
+
+ R_15_VGATE_START_FID_CHG, 0x03,
+ R_16_VGATE_STOP, 0x11,
+ R_17_MISC_VGATE_CONF_AND_MSB, 0x9c,
+
+ R_08_SYNC_CNTL, 0x68, /* 0xBO: auto detection, 0x68 = NTSC */
+ R_0E_CHROMA_CNTL_1, 0x07, /* video autodetection is on */
+
+ R_5A_V_OFF_FOR_SLICER, 0x06, /* standard 60hz value for ITU656 line counting */
+
+ /* Task A */
+ R_90_A_TASK_HANDLING_CNTL, 0x80,
+ R_91_A_X_PORT_FORMATS_AND_CONF, 0x48,
+ R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL, 0x40,
+ R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF, 0x84,
+
+ /* hoffset low (input), 0x0002 is minimum */
+ R_94_A_HORIZ_INPUT_WINDOW_START, 0x01,
+ R_95_A_HORIZ_INPUT_WINDOW_START_MSB, 0x00,
+
+ /* hsize low (input), 0x02d0 = 720 */
+ R_96_A_HORIZ_INPUT_WINDOW_LENGTH, 0xd0,
+ R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02,
+
+ R_98_A_VERT_INPUT_WINDOW_START, 0x05,
+ R_99_A_VERT_INPUT_WINDOW_START_MSB, 0x00,
+
+ R_9A_A_VERT_INPUT_WINDOW_LENGTH, 0x0c,
+ R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB, 0x00,
+
+ R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH, 0xa0,
+ R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x05,
+
+ R_9E_A_VERT_OUTPUT_WINDOW_LENGTH, 0x0c,
+ R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB, 0x00,
+
+ /* Task B */
+ R_C0_B_TASK_HANDLING_CNTL, 0x00,
+ R_C1_B_X_PORT_FORMATS_AND_CONF, 0x08,
+ R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION, 0x00,
+ R_C3_B_I_PORT_FORMATS_AND_CONF, 0x80,
+
+ /* 0x0002 is minimum */
+ R_C4_B_HORIZ_INPUT_WINDOW_START, 0x02,
+ R_C5_B_HORIZ_INPUT_WINDOW_START_MSB, 0x00,
+
+ /* 0x02d0 = 720 */
+ R_C6_B_HORIZ_INPUT_WINDOW_LENGTH, 0xd0,
+ R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02,
+
+ /* vwindow start 0x12 = 18 */
+ R_C8_B_VERT_INPUT_WINDOW_START, 0x12,
+ R_C9_B_VERT_INPUT_WINDOW_START_MSB, 0x00,
+
+ /* vwindow length 0xf8 = 248 */
+ R_CA_B_VERT_INPUT_WINDOW_LENGTH, VRES_60HZ>>1,
+ R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB, VRES_60HZ>>9,
+
+ /* hwindow 0x02d0 = 720 */
+ R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, 0xd0,
+ R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x02,
+
+ R_F0_LFCO_PER_LINE, 0xad, /* Set PLL Register. 60hz 525 lines per frame, 27 MHz */
+ R_F1_P_I_PARAM_SELECT, 0x05, /* low bit with 0xF0 */
+ R_F5_PULSGEN_LINE_LENGTH, 0xad,
+ R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG, 0x01,
+
+ 0x00, 0x00
+};
+
+static const unsigned char saa7115_cfg_50hz_video[] = {
+ R_80_GLOBAL_CNTL_1, 0x00,
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */
+
+ R_15_VGATE_START_FID_CHG, 0x37, /* VGATE start */
+ R_16_VGATE_STOP, 0x16,
+ R_17_MISC_VGATE_CONF_AND_MSB, 0x99,
+
+ R_08_SYNC_CNTL, 0x28, /* 0x28 = PAL */
+ R_0E_CHROMA_CNTL_1, 0x07,
+
+ R_5A_V_OFF_FOR_SLICER, 0x03, /* standard 50hz value */
+
+ /* Task A */
+ R_90_A_TASK_HANDLING_CNTL, 0x81,
+ R_91_A_X_PORT_FORMATS_AND_CONF, 0x48,
+ R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL, 0x40,
+ R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF, 0x84,
+
+ /* This is weird: the datasheet says that you should use 2 as the minimum value, */
+ /* but Hauppauge uses 0, and changing that to 2 causes indeed problems (for 50hz) */
+ /* hoffset low (input), 0x0002 is minimum */
+ R_94_A_HORIZ_INPUT_WINDOW_START, 0x00,
+ R_95_A_HORIZ_INPUT_WINDOW_START_MSB, 0x00,
+
+ /* hsize low (input), 0x02d0 = 720 */
+ R_96_A_HORIZ_INPUT_WINDOW_LENGTH, 0xd0,
+ R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02,
+
+ R_98_A_VERT_INPUT_WINDOW_START, 0x03,
+ R_99_A_VERT_INPUT_WINDOW_START_MSB, 0x00,
+
+ /* vsize 0x12 = 18 */
+ R_9A_A_VERT_INPUT_WINDOW_LENGTH, 0x12,
+ R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB, 0x00,
+
+ /* hsize 0x05a0 = 1440 */
+ R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH, 0xa0,
+ R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x05, /* hsize hi (output) */
+ R_9E_A_VERT_OUTPUT_WINDOW_LENGTH, 0x12, /* vsize low (output), 0x12 = 18 */
+ R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB, 0x00, /* vsize hi (output) */
+
+ /* Task B */
+ R_C0_B_TASK_HANDLING_CNTL, 0x00,
+ R_C1_B_X_PORT_FORMATS_AND_CONF, 0x08,
+ R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION, 0x00,
+ R_C3_B_I_PORT_FORMATS_AND_CONF, 0x80,
+
+ /* This is weird: the datasheet says that you should use 2 as the minimum value, */
+ /* but Hauppauge uses 0, and changing that to 2 causes indeed problems (for 50hz) */
+ /* hoffset low (input), 0x0002 is minimum. See comment above. */
+ R_C4_B_HORIZ_INPUT_WINDOW_START, 0x00,
+ R_C5_B_HORIZ_INPUT_WINDOW_START_MSB, 0x00,
+
+ /* hsize 0x02d0 = 720 */
+ R_C6_B_HORIZ_INPUT_WINDOW_LENGTH, 0xd0,
+ R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02,
+
+ /* voffset 0x16 = 22 */
+ R_C8_B_VERT_INPUT_WINDOW_START, 0x16,
+ R_C9_B_VERT_INPUT_WINDOW_START_MSB, 0x00,
+
+ /* vsize 0x0120 = 288 */
+ R_CA_B_VERT_INPUT_WINDOW_LENGTH, 0x20,
+ R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB, 0x01,
+
+ /* hsize 0x02d0 = 720 */
+ R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, 0xd0,
+ R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x02,
+
+ R_F0_LFCO_PER_LINE, 0xb0, /* Set PLL Register. 50hz 625 lines per frame, 27 MHz */
+ R_F1_P_I_PARAM_SELECT, 0x05, /* low bit with 0xF0, (was 0x05) */
+ R_F5_PULSGEN_LINE_LENGTH, 0xb0,
+ R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG, 0x01,
+
+ 0x00, 0x00
+};
+
+/* ============== SAA7715 VIDEO templates (end) ======= */
+
+static const unsigned char saa7115_cfg_vbi_on[] = {
+ R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */
+ R_80_GLOBAL_CNTL_1, 0x30, /* Activate both tasks */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */
+ R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* Enable I-port output */
+
+ 0x00, 0x00
+};
+
+static const unsigned char saa7115_cfg_vbi_off[] = {
+ R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */
+ R_80_GLOBAL_CNTL_1, 0x20, /* Activate only task "B" */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */
+ R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* Enable I-port output */
+
+ 0x00, 0x00
+};
+
+
+static const unsigned char saa7115_init_misc[] = {
+ R_81_V_SYNC_FLD_ID_SRC_SEL_AND_RETIMED_V_F, 0x01,
+ R_83_X_PORT_I_O_ENA_AND_OUT_CLK, 0x01,
+ R_84_I_PORT_SIGNAL_DEF, 0x20,
+ R_85_I_PORT_SIGNAL_POLAR, 0x21,
+ R_86_I_PORT_FIFO_FLAG_CNTL_AND_ARBIT, 0xc5,
+ R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01,
+
+ /* Task A */
+ R_A0_A_HORIZ_PRESCALING, 0x01,
+ R_A1_A_ACCUMULATION_LENGTH, 0x00,
+ R_A2_A_PRESCALER_DC_GAIN_AND_FIR_PREFILTER, 0x00,
+
+ /* Configure controls at nominal value*/
+ R_A4_A_LUMA_BRIGHTNESS_CNTL, 0x80,
+ R_A5_A_LUMA_CONTRAST_CNTL, 0x40,
+ R_A6_A_CHROMA_SATURATION_CNTL, 0x40,
+
+ /* note: 2 x zoom ensures that VBI lines have same length as video lines. */
+ R_A8_A_HORIZ_LUMA_SCALING_INC, 0x00,
+ R_A9_A_HORIZ_LUMA_SCALING_INC_MSB, 0x02,
+
+ R_AA_A_HORIZ_LUMA_PHASE_OFF, 0x00,
+
+ /* must be horiz lum scaling / 2 */
+ R_AC_A_HORIZ_CHROMA_SCALING_INC, 0x00,
+ R_AD_A_HORIZ_CHROMA_SCALING_INC_MSB, 0x01,
+
+ /* must be offset luma / 2 */
+ R_AE_A_HORIZ_CHROMA_PHASE_OFF, 0x00,
+
+ R_B0_A_VERT_LUMA_SCALING_INC, 0x00,
+ R_B1_A_VERT_LUMA_SCALING_INC_MSB, 0x04,
+
+ R_B2_A_VERT_CHROMA_SCALING_INC, 0x00,
+ R_B3_A_VERT_CHROMA_SCALING_INC_MSB, 0x04,
+
+ R_B4_A_VERT_SCALING_MODE_CNTL, 0x01,
+
+ R_B8_A_VERT_CHROMA_PHASE_OFF_00, 0x00,
+ R_B9_A_VERT_CHROMA_PHASE_OFF_01, 0x00,
+ R_BA_A_VERT_CHROMA_PHASE_OFF_10, 0x00,
+ R_BB_A_VERT_CHROMA_PHASE_OFF_11, 0x00,
+
+ R_BC_A_VERT_LUMA_PHASE_OFF_00, 0x00,
+ R_BD_A_VERT_LUMA_PHASE_OFF_01, 0x00,
+ R_BE_A_VERT_LUMA_PHASE_OFF_10, 0x00,
+ R_BF_A_VERT_LUMA_PHASE_OFF_11, 0x00,
+
+ /* Task B */
+ R_D0_B_HORIZ_PRESCALING, 0x01,
+ R_D1_B_ACCUMULATION_LENGTH, 0x00,
+ R_D2_B_PRESCALER_DC_GAIN_AND_FIR_PREFILTER, 0x00,
+
+ /* Configure controls at nominal value*/
+ R_D4_B_LUMA_BRIGHTNESS_CNTL, 0x80,
+ R_D5_B_LUMA_CONTRAST_CNTL, 0x40,
+ R_D6_B_CHROMA_SATURATION_CNTL, 0x40,
+
+ /* hor lum scaling 0x0400 = 1 */
+ R_D8_B_HORIZ_LUMA_SCALING_INC, 0x00,
+ R_D9_B_HORIZ_LUMA_SCALING_INC_MSB, 0x04,
+
+ R_DA_B_HORIZ_LUMA_PHASE_OFF, 0x00,
+
+ /* must be hor lum scaling / 2 */
+ R_DC_B_HORIZ_CHROMA_SCALING, 0x00,
+ R_DD_B_HORIZ_CHROMA_SCALING_MSB, 0x02,
+
+ /* must be offset luma / 2 */
+ R_DE_B_HORIZ_PHASE_OFFSET_CRHOMA, 0x00,
+
+ R_E0_B_VERT_LUMA_SCALING_INC, 0x00,
+ R_E1_B_VERT_LUMA_SCALING_INC_MSB, 0x04,
+
+ R_E2_B_VERT_CHROMA_SCALING_INC, 0x00,
+ R_E3_B_VERT_CHROMA_SCALING_INC_MSB, 0x04,
+
+ R_E4_B_VERT_SCALING_MODE_CNTL, 0x01,
+
+ R_E8_B_VERT_CHROMA_PHASE_OFF_00, 0x00,
+ R_E9_B_VERT_CHROMA_PHASE_OFF_01, 0x00,
+ R_EA_B_VERT_CHROMA_PHASE_OFF_10, 0x00,
+ R_EB_B_VERT_CHROMA_PHASE_OFF_11, 0x00,
+
+ R_EC_B_VERT_LUMA_PHASE_OFF_00, 0x00,
+ R_ED_B_VERT_LUMA_PHASE_OFF_01, 0x00,
+ R_EE_B_VERT_LUMA_PHASE_OFF_10, 0x00,
+ R_EF_B_VERT_LUMA_PHASE_OFF_11, 0x00,
+
+ R_F2_NOMINAL_PLL2_DTO, 0x50, /* crystal clock = 24.576 MHz, target = 27MHz */
+ R_F3_PLL_INCREMENT, 0x46,
+ R_F4_PLL2_STATUS, 0x00,
+ R_F7_PULSE_A_POS_MSB, 0x4b, /* not the recommended settings! */
+ R_F8_PULSE_B_POS, 0x00,
+ R_F9_PULSE_B_POS_MSB, 0x4b,
+ R_FA_PULSE_C_POS, 0x00,
+ R_FB_PULSE_C_POS_MSB, 0x4b,
+
+ /* PLL2 lock detection settings: 71 lines 50% phase error */
+ R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES, 0x88,
+
+ /* Turn off VBI */
+ R_40_SLICER_CNTL_1, 0x20, /* No framing code errors allowed. */
+ R_41_LCR_BASE, 0xff,
+ R_41_LCR_BASE+1, 0xff,
+ R_41_LCR_BASE+2, 0xff,
+ R_41_LCR_BASE+3, 0xff,
+ R_41_LCR_BASE+4, 0xff,
+ R_41_LCR_BASE+5, 0xff,
+ R_41_LCR_BASE+6, 0xff,
+ R_41_LCR_BASE+7, 0xff,
+ R_41_LCR_BASE+8, 0xff,
+ R_41_LCR_BASE+9, 0xff,
+ R_41_LCR_BASE+10, 0xff,
+ R_41_LCR_BASE+11, 0xff,
+ R_41_LCR_BASE+12, 0xff,
+ R_41_LCR_BASE+13, 0xff,
+ R_41_LCR_BASE+14, 0xff,
+ R_41_LCR_BASE+15, 0xff,
+ R_41_LCR_BASE+16, 0xff,
+ R_41_LCR_BASE+17, 0xff,
+ R_41_LCR_BASE+18, 0xff,
+ R_41_LCR_BASE+19, 0xff,
+ R_41_LCR_BASE+20, 0xff,
+ R_41_LCR_BASE+21, 0xff,
+ R_41_LCR_BASE+22, 0xff,
+ R_58_PROGRAM_FRAMING_CODE, 0x40,
+ R_59_H_OFF_FOR_SLICER, 0x47,
+ R_5B_FLD_OFF_AND_MSB_FOR_H_AND_V_OFF, 0x83,
+ R_5D_DID, 0xbd,
+ R_5E_SDID, 0x35,
+
+ R_02_INPUT_CNTL_1, 0xc4, /* input tuner -> input 4, amplifier active */
+
+ R_80_GLOBAL_CNTL_1, 0x20, /* enable task B */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0,
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0,
+ 0x00, 0x00
+};
+
+static int saa711x_odd_parity(u8 c)
+{
+ c ^= (c >> 4);
+ c ^= (c >> 2);
+ c ^= (c >> 1);
+
+ return c & 1;
+}
+
+static int saa711x_decode_vps(u8 *dst, u8 *p)
+{
+ static const u8 biphase_tbl[] = {
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
+ 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
+ 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
+ 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
+ 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
+ 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87,
+ 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3,
+ 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85,
+ 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1,
+ 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
+ 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
+ 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
+ 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
+ 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86,
+ 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2,
+ 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84,
+ 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0,
+ 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
+ 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
+ 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
+ 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
+ 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ };
+ int i;
+ u8 c, err = 0;
+
+ for (i = 0; i < 2 * 13; i += 2) {
+ err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]];
+ c = (biphase_tbl[p[i + 1]] & 0xf) | ((biphase_tbl[p[i]] & 0xf) << 4);
+ dst[i / 2] = c;
+ }
+ return err & 0xf0;
+}
+
+static int saa711x_decode_wss(u8 *p)
+{
+ static const int wss_bits[8] = {
+ 0, 0, 0, 1, 0, 1, 1, 1
+ };
+ unsigned char parity;
+ int wss = 0;
+ int i;
+
+ for (i = 0; i < 16; i++) {
+ int b1 = wss_bits[p[i] & 7];
+ int b2 = wss_bits[(p[i] >> 3) & 7];
+
+ if (b1 == b2)
+ return -1;
+ wss |= b2 << i;
+ }
+ parity = wss & 15;
+ parity ^= parity >> 2;
+ parity ^= parity >> 1;
+
+ if (!(parity & 1))
+ return -1;
+
+ return wss;
+}
+
+static int saa711x_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
+{
+ struct saa711x_state *state = to_state(sd);
+ u32 acpf;
+ u32 acni;
+ u32 hz;
+ u64 f;
+ u8 acc = 0; /* reg 0x3a, audio clock control */
+
+ /* Checks for chips that don't have audio clock (saa7111, saa7113) */
+ if (!saa711x_has_reg(state->ident, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD))
+ return 0;
+
+ v4l2_dbg(1, debug, sd, "set audio clock freq: %d\n", freq);
+
+ /* sanity check */
+ if (freq < 32000 || freq > 48000)
+ return -EINVAL;
+
+ /* hz is the refresh rate times 100 */
+ hz = (state->std & V4L2_STD_525_60) ? 5994 : 5000;
+ /* acpf = (256 * freq) / field_frequency == (256 * 100 * freq) / hz */
+ acpf = (25600 * freq) / hz;
+ /* acni = (256 * freq * 2^23) / crystal_frequency =
+ (freq * 2^(8+23)) / crystal_frequency =
+ (freq << 31) / crystal_frequency */
+ f = freq;
+ f = f << 31;
+ do_div(f, state->crystal_freq);
+ acni = f;
+ if (state->ucgc) {
+ acpf = acpf * state->cgcdiv / 16;
+ acni = acni * state->cgcdiv / 16;
+ acc = 0x80;
+ if (state->cgcdiv == 3)
+ acc |= 0x40;
+ }
+ if (state->apll)
+ acc |= 0x08;
+
+ saa711x_write(sd, R_38_CLK_RATIO_AMXCLK_TO_ASCLK, 0x03);
+ saa711x_write(sd, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10);
+ saa711x_write(sd, R_3A_AUD_CLK_GEN_BASIC_SETUP, acc);
+
+ saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD, acpf & 0xff);
+ saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+1,
+ (acpf >> 8) & 0xff);
+ saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+2,
+ (acpf >> 16) & 0x03);
+
+ saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC, acni & 0xff);
+ saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+1, (acni >> 8) & 0xff);
+ saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+2, (acni >> 16) & 0x3f);
+ state->audclk_freq = freq;
+ return 0;
+}
+
+static int saa711x_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ struct saa711x_state *state = to_state(sd);
+
+ switch (ctrl->id) {
+ case V4L2_CID_CHROMA_AGC:
+ /* chroma gain cluster */
+ if (state->agc->val)
+ state->gain->val =
+ saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL) & 0x7f;
+ break;
+ }
+ return 0;
+}
+
+static int saa711x_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ struct saa711x_state *state = to_state(sd);
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, ctrl->val);
+ break;
+
+ case V4L2_CID_CONTRAST:
+ saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, ctrl->val);
+ break;
+
+ case V4L2_CID_SATURATION:
+ saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, ctrl->val);
+ break;
+
+ case V4L2_CID_HUE:
+ saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, ctrl->val);
+ break;
+
+ case V4L2_CID_CHROMA_AGC:
+ /* chroma gain cluster */
+ if (state->agc->val)
+ saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val);
+ else
+ saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val | 0x80);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int saa711x_set_size(struct v4l2_subdev *sd, int width, int height)
+{
+ struct saa711x_state *state = to_state(sd);
+ int HPSC, HFSC;
+ int VSCY;
+ int res;
+ int is_50hz = state->std & V4L2_STD_625_50;
+ int Vsrc = is_50hz ? 576 : 480;
+
+ v4l2_dbg(1, debug, sd, "decoder set size to %ix%i\n", width, height);
+
+ /* FIXME need better bounds checking here */
+ if ((width < 1) || (width > 1440))
+ return -EINVAL;
+ if ((height < 1) || (height > Vsrc))
+ return -EINVAL;
+
+ if (!saa711x_has_reg(state->ident, R_D0_B_HORIZ_PRESCALING)) {
+ /* Decoder only supports 720 columns and 480 or 576 lines */
+ if (width != 720)
+ return -EINVAL;
+ if (height != Vsrc)
+ return -EINVAL;
+ }
+
+ state->width = width;
+ state->height = height;
+
+ if (!saa711x_has_reg(state->ident, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH))
+ return 0;
+
+ /* probably have a valid size, let's set it */
+ /* Set output width/height */
+ /* width */
+
+ saa711x_write(sd, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH,
+ (u8) (width & 0xff));
+ saa711x_write(sd, R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB,
+ (u8) ((width >> 8) & 0xff));
+
+ /* Vertical Scaling uses height/2 */
+ res = height / 2;
+
+ /* On 60Hz, it is using a higher Vertical Output Size */
+ if (!is_50hz)
+ res += (VRES_60HZ - 480) >> 1;
+
+ /* height */
+ saa711x_write(sd, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH,
+ (u8) (res & 0xff));
+ saa711x_write(sd, R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB,
+ (u8) ((res >> 8) & 0xff));
+
+ /* Scaling settings */
+ /* Hprescaler is floor(inres/outres) */
+ HPSC = (int)(720 / width);
+ /* 0 is not allowed (div. by zero) */
+ HPSC = HPSC ? HPSC : 1;
+ HFSC = (int)((1024 * 720) / (HPSC * width));
+ /* FIXME hardcodes to "Task B"
+ * write H prescaler integer */
+ saa711x_write(sd, R_D0_B_HORIZ_PRESCALING,
+ (u8) (HPSC & 0x3f));
+
+ v4l2_dbg(1, debug, sd, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC);
+ /* write H fine-scaling (luminance) */
+ saa711x_write(sd, R_D8_B_HORIZ_LUMA_SCALING_INC,
+ (u8) (HFSC & 0xff));
+ saa711x_write(sd, R_D9_B_HORIZ_LUMA_SCALING_INC_MSB,
+ (u8) ((HFSC >> 8) & 0xff));
+ /* write H fine-scaling (chrominance)
+ * must be lum/2, so i'll just bitshift :) */
+ saa711x_write(sd, R_DC_B_HORIZ_CHROMA_SCALING,
+ (u8) ((HFSC >> 1) & 0xff));
+ saa711x_write(sd, R_DD_B_HORIZ_CHROMA_SCALING_MSB,
+ (u8) ((HFSC >> 9) & 0xff));
+
+ VSCY = (int)((1024 * Vsrc) / height);
+ v4l2_dbg(1, debug, sd, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY);
+
+ /* Correct Contrast and Luminance */
+ saa711x_write(sd, R_D5_B_LUMA_CONTRAST_CNTL,
+ (u8) (64 * 1024 / VSCY));
+ saa711x_write(sd, R_D6_B_CHROMA_SATURATION_CNTL,
+ (u8) (64 * 1024 / VSCY));
+
+ /* write V fine-scaling (luminance) */
+ saa711x_write(sd, R_E0_B_VERT_LUMA_SCALING_INC,
+ (u8) (VSCY & 0xff));
+ saa711x_write(sd, R_E1_B_VERT_LUMA_SCALING_INC_MSB,
+ (u8) ((VSCY >> 8) & 0xff));
+ /* write V fine-scaling (chrominance) */
+ saa711x_write(sd, R_E2_B_VERT_CHROMA_SCALING_INC,
+ (u8) (VSCY & 0xff));
+ saa711x_write(sd, R_E3_B_VERT_CHROMA_SCALING_INC_MSB,
+ (u8) ((VSCY >> 8) & 0xff));
+
+ saa711x_writeregs(sd, saa7115_cfg_reset_scaler);
+
+ /* Activates task "B" */
+ saa711x_write(sd, R_80_GLOBAL_CNTL_1,
+ saa711x_read(sd, R_80_GLOBAL_CNTL_1) | 0x20);
+
+ return 0;
+}
+
+static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct saa711x_state *state = to_state(sd);
+
+ /* Prevent unnecessary standard changes. During a standard
+ change the I-Port is temporarily disabled. Any devices
+ reading from that port can get confused.
+ Note that s_std is also used to switch from
+ radio to TV mode, so if a s_std is broadcast to
+ all I2C devices then you do not want to have an unwanted
+ side-effect here. */
+ if (std == state->std)
+ return;
+
+ state->std = std;
+
+ // This works for NTSC-M, SECAM-L and the 50Hz PAL variants.
+ if (std & V4L2_STD_525_60) {
+ v4l2_dbg(1, debug, sd, "decoder set standard 60 Hz\n");
+ saa711x_writeregs(sd, saa7115_cfg_60hz_video);
+ saa711x_set_size(sd, 720, 480);
+ } else {
+ v4l2_dbg(1, debug, sd, "decoder set standard 50 Hz\n");
+ saa711x_writeregs(sd, saa7115_cfg_50hz_video);
+ saa711x_set_size(sd, 720, 576);
+ }
+
+ /* Register 0E - Bits D6-D4 on NO-AUTO mode
+ (SAA7111 and SAA7113 doesn't have auto mode)
+ 50 Hz / 625 lines 60 Hz / 525 lines
+ 000 PAL BGDHI (4.43Mhz) NTSC M (3.58MHz)
+ 001 NTSC 4.43 (50 Hz) PAL 4.43 (60 Hz)
+ 010 Combination-PAL N (3.58MHz) NTSC 4.43 (60 Hz)
+ 011 NTSC N (3.58MHz) PAL M (3.58MHz)
+ 100 reserved NTSC-Japan (3.58MHz)
+ */
+ if (state->ident <= V4L2_IDENT_SAA7113) {
+ u8 reg = saa711x_read(sd, R_0E_CHROMA_CNTL_1) & 0x8f;
+
+ if (std == V4L2_STD_PAL_M) {
+ reg |= 0x30;
+ } else if (std == V4L2_STD_PAL_Nc) {
+ reg |= 0x20;
+ } else if (std == V4L2_STD_PAL_60) {
+ reg |= 0x10;
+ } else if (std == V4L2_STD_NTSC_M_JP) {
+ reg |= 0x40;
+ } else if (std & V4L2_STD_SECAM) {
+ reg |= 0x50;
+ }
+ saa711x_write(sd, R_0E_CHROMA_CNTL_1, reg);
+ } else {
+ /* restart task B if needed */
+ int taskb = saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10;
+
+ if (taskb && state->ident == V4L2_IDENT_SAA7114) {
+ saa711x_writeregs(sd, saa7115_cfg_vbi_on);
+ }
+
+ /* switch audio mode too! */
+ saa711x_s_clock_freq(sd, state->audclk_freq);
+ }
+}
+
+/* setup the sliced VBI lcr registers according to the sliced VBI format */
+static void saa711x_set_lcr(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt)
+{
+ struct saa711x_state *state = to_state(sd);
+ int is_50hz = (state->std & V4L2_STD_625_50);
+ u8 lcr[24];
+ int i, x;
+
+#if 1
+ /* saa7113/7114/7118 VBI support are experimental */
+ if (!saa711x_has_reg(state->ident, R_41_LCR_BASE))
+ return;
+
+#else
+ /* SAA7113 and SAA7118 also should support VBI - Need testing */
+ if (state->ident != V4L2_IDENT_SAA7115)
+ return;
+#endif
+
+ for (i = 0; i <= 23; i++)
+ lcr[i] = 0xff;
+
+ if (fmt == NULL) {
+ /* raw VBI */
+ if (is_50hz)
+ for (i = 6; i <= 23; i++)
+ lcr[i] = 0xdd;
+ else
+ for (i = 10; i <= 21; i++)
+ lcr[i] = 0xdd;
+ } else {
+ /* sliced VBI */
+ /* first clear lines that cannot be captured */
+ if (is_50hz) {
+ for (i = 0; i <= 5; i++)
+ fmt->service_lines[0][i] =
+ fmt->service_lines[1][i] = 0;
+ }
+ else {
+ for (i = 0; i <= 9; i++)
+ fmt->service_lines[0][i] =
+ fmt->service_lines[1][i] = 0;
+ for (i = 22; i <= 23; i++)
+ fmt->service_lines[0][i] =
+ fmt->service_lines[1][i] = 0;
+ }
+
+ /* Now set the lcr values according to the specified service */
+ for (i = 6; i <= 23; i++) {
+ lcr[i] = 0;
+ for (x = 0; x <= 1; x++) {
+ switch (fmt->service_lines[1-x][i]) {
+ case 0:
+ lcr[i] |= 0xf << (4 * x);
+ break;
+ case V4L2_SLICED_TELETEXT_B:
+ lcr[i] |= 1 << (4 * x);
+ break;
+ case V4L2_SLICED_CAPTION_525:
+ lcr[i] |= 4 << (4 * x);
+ break;
+ case V4L2_SLICED_WSS_625:
+ lcr[i] |= 5 << (4 * x);
+ break;
+ case V4L2_SLICED_VPS:
+ lcr[i] |= 7 << (4 * x);
+ break;
+ }
+ }
+ }
+ }
+
+ /* write the lcr registers */
+ for (i = 2; i <= 23; i++) {
+ saa711x_write(sd, i - 2 + R_41_LCR_BASE, lcr[i]);
+ }
+
+ /* enable/disable raw VBI capturing */
+ saa711x_writeregs(sd, fmt == NULL ?
+ saa7115_cfg_vbi_on :
+ saa7115_cfg_vbi_off);
+}
+
+static int saa711x_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *sliced)
+{
+ static u16 lcr2vbi[] = {
+ 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */
+ 0, V4L2_SLICED_CAPTION_525, /* 4 */
+ V4L2_SLICED_WSS_625, 0, /* 5 */
+ V4L2_SLICED_VPS, 0, 0, 0, 0, /* 7 */
+ 0, 0, 0, 0
+ };
+ int i;
+
+ memset(sliced, 0, sizeof(*sliced));
+ /* done if using raw VBI */
+ if (saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10)
+ return 0;
+ for (i = 2; i <= 23; i++) {
+ u8 v = saa711x_read(sd, i - 2 + R_41_LCR_BASE);
+
+ sliced->service_lines[0][i] = lcr2vbi[v >> 4];
+ sliced->service_lines[1][i] = lcr2vbi[v & 0xf];
+ sliced->service_set |=
+ sliced->service_lines[0][i] | sliced->service_lines[1][i];
+ }
+ return 0;
+}
+
+static int saa711x_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt)
+{
+ saa711x_set_lcr(sd, NULL);
+ return 0;
+}
+
+static int saa711x_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt)
+{
+ saa711x_set_lcr(sd, fmt);
+ return 0;
+}
+
+static int saa711x_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt)
+{
+ if (fmt->code != V4L2_MBUS_FMT_FIXED)
+ return -EINVAL;
+ fmt->field = V4L2_FIELD_INTERLACED;
+ fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ return saa711x_set_size(sd, fmt->width, fmt->height);
+}
+
+/* Decode the sliced VBI data stream as created by the saa7115.
+ The format is described in the saa7115 datasheet in Tables 25 and 26
+ and in Figure 33.
+ The current implementation uses SAV/EAV codes and not the ancillary data
+ headers. The vbi->p pointer points to the R_5E_SDID byte right after the SAV
+ code. */
+static int saa711x_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi)
+{
+ struct saa711x_state *state = to_state(sd);
+ static const char vbi_no_data_pattern[] = {
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0
+ };
+ u8 *p = vbi->p;
+ u32 wss;
+ int id1, id2; /* the ID1 and ID2 bytes from the internal header */
+
+ vbi->type = 0; /* mark result as a failure */
+ id1 = p[2];
+ id2 = p[3];
+ /* Note: the field bit is inverted for 60 Hz video */
+ if (state->std & V4L2_STD_525_60)
+ id1 ^= 0x40;
+
+ /* Skip internal header, p now points to the start of the payload */
+ p += 4;
+ vbi->p = p;
+
+ /* calculate field and line number of the VBI packet (1-23) */
+ vbi->is_second_field = ((id1 & 0x40) != 0);
+ vbi->line = (id1 & 0x3f) << 3;
+ vbi->line |= (id2 & 0x70) >> 4;
+
+ /* Obtain data type */
+ id2 &= 0xf;
+
+ /* If the VBI slicer does not detect any signal it will fill up
+ the payload buffer with 0xa0 bytes. */
+ if (!memcmp(p, vbi_no_data_pattern, sizeof(vbi_no_data_pattern)))
+ return 0;
+
+ /* decode payloads */
+ switch (id2) {
+ case 1:
+ vbi->type = V4L2_SLICED_TELETEXT_B;
+ break;
+ case 4:
+ if (!saa711x_odd_parity(p[0]) || !saa711x_odd_parity(p[1]))
+ return 0;
+ vbi->type = V4L2_SLICED_CAPTION_525;
+ break;
+ case 5:
+ wss = saa711x_decode_wss(p);
+ if (wss == -1)
+ return 0;
+ p[0] = wss & 0xff;
+ p[1] = wss >> 8;
+ vbi->type = V4L2_SLICED_WSS_625;
+ break;
+ case 7:
+ if (saa711x_decode_vps(p, p) != 0)
+ return 0;
+ vbi->type = V4L2_SLICED_VPS;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/* ============ SAA7115 AUDIO settings (end) ============= */
+
+static int saa711x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct saa711x_state *state = to_state(sd);
+ int status;
+
+ if (state->radio)
+ return 0;
+ status = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
+
+ v4l2_dbg(1, debug, sd, "status: 0x%02x\n", status);
+ vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0;
+ return 0;
+}
+
+static int saa711x_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct saa711x_state *state = to_state(sd);
+
+ state->radio = 0;
+ saa711x_set_v4lstd(sd, std);
+ return 0;
+}
+
+static int saa711x_s_radio(struct v4l2_subdev *sd)
+{
+ struct saa711x_state *state = to_state(sd);
+
+ state->radio = 1;
+ return 0;
+}
+
+static int saa711x_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct saa711x_state *state = to_state(sd);
+ u8 mask = (state->ident <= V4L2_IDENT_SAA7111A) ? 0xf8 : 0xf0;
+
+ v4l2_dbg(1, debug, sd, "decoder set input %d output %d\n",
+ input, output);
+
+ /* saa7111/3 does not have these inputs */
+ if (state->ident <= V4L2_IDENT_SAA7113 &&
+ (input == SAA7115_COMPOSITE4 ||
+ input == SAA7115_COMPOSITE5)) {
+ return -EINVAL;
+ }
+ if (input > SAA7115_SVIDEO3)
+ return -EINVAL;
+ if (state->input == input && state->output == output)
+ return 0;
+ v4l2_dbg(1, debug, sd, "now setting %s input %s output\n",
+ (input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite",
+ (output == SAA7115_IPORT_ON) ? "iport on" : "iport off");
+ state->input = input;
+
+ /* saa7111 has slightly different input numbering */
+ if (state->ident <= V4L2_IDENT_SAA7111A) {
+ if (input >= SAA7115_COMPOSITE4)
+ input -= 2;
+ /* saa7111 specific */
+ saa711x_write(sd, R_10_CHROMA_CNTL_2,
+ (saa711x_read(sd, R_10_CHROMA_CNTL_2) & 0x3f) |
+ ((output & 0xc0) ^ 0x40));
+ saa711x_write(sd, R_13_RT_X_PORT_OUT_CNTL,
+ (saa711x_read(sd, R_13_RT_X_PORT_OUT_CNTL) & 0xf0) |
+ ((output & 2) ? 0x0a : 0));
+ }
+
+ /* select mode */
+ saa711x_write(sd, R_02_INPUT_CNTL_1,
+ (saa711x_read(sd, R_02_INPUT_CNTL_1) & mask) |
+ input);
+
+ /* bypass chrominance trap for S-Video modes */
+ saa711x_write(sd, R_09_LUMA_CNTL,
+ (saa711x_read(sd, R_09_LUMA_CNTL) & 0x7f) |
+ (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0));
+
+ state->output = output;
+ if (state->ident == V4L2_IDENT_SAA7114 ||
+ state->ident == V4L2_IDENT_SAA7115) {
+ saa711x_write(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK,
+ (saa711x_read(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) |
+ (state->output & 0x01));
+ }
+ return 0;
+}
+
+static int saa711x_s_gpio(struct v4l2_subdev *sd, u32 val)
+{
+ struct saa711x_state *state = to_state(sd);
+
+ if (state->ident > V4L2_IDENT_SAA7111A)
+ return -EINVAL;
+ saa711x_write(sd, 0x11, (saa711x_read(sd, 0x11) & 0x7f) |
+ (val ? 0x80 : 0));
+ return 0;
+}
+
+static int saa711x_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct saa711x_state *state = to_state(sd);
+
+ v4l2_dbg(1, debug, sd, "%s output\n",
+ enable ? "enable" : "disable");
+
+ if (state->enable == enable)
+ return 0;
+ state->enable = enable;
+ if (!saa711x_has_reg(state->ident, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED))
+ return 0;
+ saa711x_write(sd, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, state->enable);
+ return 0;
+}
+
+static int saa711x_s_crystal_freq(struct v4l2_subdev *sd, u32 freq, u32 flags)
+{
+ struct saa711x_state *state = to_state(sd);
+
+ if (freq != SAA7115_FREQ_32_11_MHZ && freq != SAA7115_FREQ_24_576_MHZ)
+ return -EINVAL;
+ state->crystal_freq = freq;
+ state->cgcdiv = (flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4;
+ state->ucgc = (flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0;
+ state->apll = (flags & SAA7115_FREQ_FL_APLL) ? 1 : 0;
+ saa711x_s_clock_freq(sd, state->audclk_freq);
+ return 0;
+}
+
+static int saa711x_reset(struct v4l2_subdev *sd, u32 val)
+{
+ v4l2_dbg(1, debug, sd, "decoder RESET\n");
+ saa711x_writeregs(sd, saa7115_cfg_reset_scaler);
+ return 0;
+}
+
+static int saa711x_g_vbi_data(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *data)
+{
+ /* Note: the internal field ID is inverted for NTSC,
+ so data->field 0 maps to the saa7115 even field,
+ whereas for PAL it maps to the saa7115 odd field. */
+ switch (data->id) {
+ case V4L2_SLICED_WSS_625:
+ if (saa711x_read(sd, 0x6b) & 0xc0)
+ return -EIO;
+ data->data[0] = saa711x_read(sd, 0x6c);
+ data->data[1] = saa711x_read(sd, 0x6d);
+ return 0;
+ case V4L2_SLICED_CAPTION_525:
+ if (data->field == 0) {
+ /* CC */
+ if (saa711x_read(sd, 0x66) & 0x30)
+ return -EIO;
+ data->data[0] = saa711x_read(sd, 0x69);
+ data->data[1] = saa711x_read(sd, 0x6a);
+ return 0;
+ }
+ /* XDS */
+ if (saa711x_read(sd, 0x66) & 0xc0)
+ return -EIO;
+ data->data[0] = saa711x_read(sd, 0x67);
+ data->data[1] = saa711x_read(sd, 0x68);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int saa711x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ struct saa711x_state *state = to_state(sd);
+ int reg1f, reg1e;
+
+ /*
+ * The V4L2 core already initializes std with all supported
+ * Standards. All driver needs to do is to mask it, to remove
+ * standards that don't apply from the mask
+ */
+
+ reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
+ v4l2_dbg(1, debug, sd, "Status byte 2 (0x1f)=0x%02x\n", reg1f);
+
+ /* horizontal/vertical not locked */
+ if (reg1f & 0x40)
+ goto ret;
+
+ if (reg1f & 0x20)
+ *std &= V4L2_STD_525_60;
+ else
+ *std &= V4L2_STD_625_50;
+
+ if (state->ident != V4L2_IDENT_SAA7115)
+ goto ret;
+
+ reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC);
+
+ switch (reg1e & 0x03) {
+ case 1:
+ *std &= V4L2_STD_NTSC;
+ break;
+ case 2:
+ /*
+ * V4L2_STD_PAL just cover the european PAL standards.
+ * This is wrong, as the device could also be using an
+ * other PAL standard.
+ */
+ *std &= V4L2_STD_PAL | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc |
+ V4L2_STD_PAL_M | V4L2_STD_PAL_60;
+ break;
+ case 3:
+ *std &= V4L2_STD_SECAM;
+ break;
+ default:
+ /* Can't detect anything */
+ break;
+ }
+
+ v4l2_dbg(1, debug, sd, "Status byte 1 (0x1e)=0x%02x\n", reg1e);
+
+ret:
+ v4l2_dbg(1, debug, sd, "detected std mask = %08Lx\n", *std);
+
+ return 0;
+}
+
+static int saa711x_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+ struct saa711x_state *state = to_state(sd);
+ int reg1e = 0x80;
+ int reg1f;
+
+ *status = V4L2_IN_ST_NO_SIGNAL;
+ if (state->ident == V4L2_IDENT_SAA7115)
+ reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC);
+ reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
+ if ((reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80)
+ *status = 0;
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int saa711x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = saa711x_read(sd, reg->reg & 0xff);
+ reg->size = 1;
+ return 0;
+}
+
+static int saa711x_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ saa711x_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ return 0;
+}
+#endif
+
+static int saa711x_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct saa711x_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0);
+}
+
+static int saa711x_log_status(struct v4l2_subdev *sd)
+{
+ struct saa711x_state *state = to_state(sd);
+ int reg1e, reg1f;
+ int signalOk;
+ int vcr;
+
+ v4l2_info(sd, "Audio frequency: %d Hz\n", state->audclk_freq);
+ if (state->ident != V4L2_IDENT_SAA7115) {
+ /* status for the saa7114 */
+ reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
+ signalOk = (reg1f & 0xc1) == 0x81;
+ v4l2_info(sd, "Video signal: %s\n", signalOk ? "ok" : "bad");
+ v4l2_info(sd, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz");
+ return 0;
+ }
+
+ /* status for the saa7115 */
+ reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC);
+ reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
+
+ signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80;
+ vcr = !(reg1f & 0x10);
+
+ if (state->input >= 6)
+ v4l2_info(sd, "Input: S-Video %d\n", state->input - 6);
+ else
+ v4l2_info(sd, "Input: Composite %d\n", state->input);
+ v4l2_info(sd, "Video signal: %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad");
+ v4l2_info(sd, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz");
+
+ switch (reg1e & 0x03) {
+ case 1:
+ v4l2_info(sd, "Detected format: NTSC\n");
+ break;
+ case 2:
+ v4l2_info(sd, "Detected format: PAL\n");
+ break;
+ case 3:
+ v4l2_info(sd, "Detected format: SECAM\n");
+ break;
+ default:
+ v4l2_info(sd, "Detected format: BW/No color\n");
+ break;
+ }
+ v4l2_info(sd, "Width, Height: %d, %d\n", state->width, state->height);
+ v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_ctrl_ops saa711x_ctrl_ops = {
+ .s_ctrl = saa711x_s_ctrl,
+ .g_volatile_ctrl = saa711x_g_volatile_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops saa711x_core_ops = {
+ .log_status = saa711x_log_status,
+ .g_chip_ident = saa711x_g_chip_ident,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+ .s_std = saa711x_s_std,
+ .reset = saa711x_reset,
+ .s_gpio = saa711x_s_gpio,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = saa711x_g_register,
+ .s_register = saa711x_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_tuner_ops saa711x_tuner_ops = {
+ .s_radio = saa711x_s_radio,
+ .g_tuner = saa711x_g_tuner,
+};
+
+static const struct v4l2_subdev_audio_ops saa711x_audio_ops = {
+ .s_clock_freq = saa711x_s_clock_freq,
+};
+
+static const struct v4l2_subdev_video_ops saa711x_video_ops = {
+ .s_routing = saa711x_s_routing,
+ .s_crystal_freq = saa711x_s_crystal_freq,
+ .s_mbus_fmt = saa711x_s_mbus_fmt,
+ .s_stream = saa711x_s_stream,
+ .querystd = saa711x_querystd,
+ .g_input_status = saa711x_g_input_status,
+};
+
+static const struct v4l2_subdev_vbi_ops saa711x_vbi_ops = {
+ .g_vbi_data = saa711x_g_vbi_data,
+ .decode_vbi_line = saa711x_decode_vbi_line,
+ .g_sliced_fmt = saa711x_g_sliced_fmt,
+ .s_sliced_fmt = saa711x_s_sliced_fmt,
+ .s_raw_fmt = saa711x_s_raw_fmt,
+};
+
+static const struct v4l2_subdev_ops saa711x_ops = {
+ .core = &saa711x_core_ops,
+ .tuner = &saa711x_tuner_ops,
+ .audio = &saa711x_audio_ops,
+ .video = &saa711x_video_ops,
+ .vbi = &saa711x_vbi_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int saa711x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct saa711x_state *state;
+ struct v4l2_subdev *sd;
+ struct v4l2_ctrl_handler *hdl;
+ int i;
+ char name[17];
+ char chip_id;
+ int autodetect = !id || id->driver_data == 1;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ for (i = 0; i < 0x0f; i++) {
+ i2c_smbus_write_byte_data(client, 0, i);
+ name[i] = (i2c_smbus_read_byte_data(client, 0) & 0x0f) + '0';
+ if (name[i] > '9')
+ name[i] += 'a' - '9' - 1;
+ }
+ name[i] = '\0';
+
+ chip_id = name[5];
+
+ /* Check whether this chip is part of the saa711x series */
+ if (memcmp(name + 1, "f711", 4)) {
+ v4l_dbg(1, debug, client, "chip found @ 0x%x (ID %s) does not match a known saa711x chip.\n",
+ client->addr << 1, name);
+ return -ENODEV;
+ }
+
+ /* Safety check */
+ if (!autodetect && id->name[6] != chip_id) {
+ v4l_warn(client, "found saa711%c while %s was expected\n",
+ chip_id, id->name);
+ }
+ snprintf(client->name, sizeof(client->name), "saa711%c", chip_id);
+ v4l_info(client, "saa711%c found (%s) @ 0x%x (%s)\n", chip_id, name,
+ client->addr << 1, client->adapter->name);
+
+ state = kzalloc(sizeof(struct saa711x_state), GFP_KERNEL);
+ if (state == NULL)
+ return -ENOMEM;
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &saa711x_ops);
+
+ hdl = &state->hdl;
+ v4l2_ctrl_handler_init(hdl, 6);
+ /* add in ascending ID order */
+ v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+ V4L2_CID_HUE, -128, 127, 1, 0);
+ state->agc = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+ V4L2_CID_CHROMA_AGC, 0, 1, 1, 1);
+ state->gain = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+ V4L2_CID_CHROMA_GAIN, 0, 127, 1, 40);
+ sd->ctrl_handler = hdl;
+ if (hdl->error) {
+ int err = hdl->error;
+
+ v4l2_ctrl_handler_free(hdl);
+ kfree(state);
+ return err;
+ }
+ v4l2_ctrl_auto_cluster(2, &state->agc, 0, true);
+
+ state->input = -1;
+ state->output = SAA7115_IPORT_ON;
+ state->enable = 1;
+ state->radio = 0;
+ switch (chip_id) {
+ case '1':
+ state->ident = V4L2_IDENT_SAA7111;
+ if (saa711x_read(sd, R_00_CHIP_VERSION) & 0xf0) {
+ v4l_info(client, "saa7111a variant found\n");
+ state->ident = V4L2_IDENT_SAA7111A;
+ }
+ break;
+ case '3':
+ state->ident = V4L2_IDENT_SAA7113;
+ break;
+ case '4':
+ state->ident = V4L2_IDENT_SAA7114;
+ break;
+ case '5':
+ state->ident = V4L2_IDENT_SAA7115;
+ break;
+ case '8':
+ state->ident = V4L2_IDENT_SAA7118;
+ break;
+ default:
+ state->ident = V4L2_IDENT_SAA7111;
+ v4l2_info(sd, "WARNING: Chip is not known - Falling back to saa7111\n");
+ break;
+ }
+
+ state->audclk_freq = 48000;
+
+ v4l2_dbg(1, debug, sd, "writing init values\n");
+
+ /* init to 60hz/48khz */
+ state->crystal_freq = SAA7115_FREQ_24_576_MHZ;
+ switch (state->ident) {
+ case V4L2_IDENT_SAA7111:
+ case V4L2_IDENT_SAA7111A:
+ saa711x_writeregs(sd, saa7111_init);
+ break;
+ case V4L2_IDENT_SAA7113:
+ saa711x_writeregs(sd, saa7113_init);
+ break;
+ default:
+ state->crystal_freq = SAA7115_FREQ_32_11_MHZ;
+ saa711x_writeregs(sd, saa7115_init_auto_input);
+ }
+ if (state->ident > V4L2_IDENT_SAA7111A)
+ saa711x_writeregs(sd, saa7115_init_misc);
+ saa711x_set_v4lstd(sd, V4L2_STD_NTSC);
+ v4l2_ctrl_handler_setup(hdl);
+
+ v4l2_dbg(1, debug, sd, "status: (1E) 0x%02x, (1F) 0x%02x\n",
+ saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC),
+ saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC));
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa711x_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+ kfree(to_state(sd));
+ return 0;
+}
+
+static const struct i2c_device_id saa711x_id[] = {
+ { "saa7115_auto", 1 }, /* autodetect */
+ { "saa7111", 0 },
+ { "saa7113", 0 },
+ { "saa7114", 0 },
+ { "saa7115", 0 },
+ { "saa7118", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, saa711x_id);
+
+static struct i2c_driver saa711x_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "saa7115",
+ },
+ .probe = saa711x_probe,
+ .remove = saa711x_remove,
+ .id_table = saa711x_id,
+};
+
+module_i2c_driver(saa711x_driver);
diff --git a/drivers/media/video/saa711x_regs.h b/drivers/media/video/saa711x_regs.h
new file mode 100644
index 00000000..4e5f2eb0
--- /dev/null
+++ b/drivers/media/video/saa711x_regs.h
@@ -0,0 +1,549 @@
+/* saa711x - Philips SAA711x video decoder register specifications
+ *
+ * Copyright (c) 2006 Mauro Carvalho Chehab <mchehab@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define R_00_CHIP_VERSION 0x00
+/* Video Decoder */
+ /* Video Decoder - Frontend part */
+#define R_01_INC_DELAY 0x01
+#define R_02_INPUT_CNTL_1 0x02
+#define R_03_INPUT_CNTL_2 0x03
+#define R_04_INPUT_CNTL_3 0x04
+#define R_05_INPUT_CNTL_4 0x05
+ /* Video Decoder - Decoder part */
+#define R_06_H_SYNC_START 0x06
+#define R_07_H_SYNC_STOP 0x07
+#define R_08_SYNC_CNTL 0x08
+#define R_09_LUMA_CNTL 0x09
+#define R_0A_LUMA_BRIGHT_CNTL 0x0a
+#define R_0B_LUMA_CONTRAST_CNTL 0x0b
+#define R_0C_CHROMA_SAT_CNTL 0x0c
+#define R_0D_CHROMA_HUE_CNTL 0x0d
+#define R_0E_CHROMA_CNTL_1 0x0e
+#define R_0F_CHROMA_GAIN_CNTL 0x0f
+#define R_10_CHROMA_CNTL_2 0x10
+#define R_11_MODE_DELAY_CNTL 0x11
+#define R_12_RT_SIGNAL_CNTL 0x12
+#define R_13_RT_X_PORT_OUT_CNTL 0x13
+#define R_14_ANAL_ADC_COMPAT_CNTL 0x14
+#define R_15_VGATE_START_FID_CHG 0x15
+#define R_16_VGATE_STOP 0x16
+#define R_17_MISC_VGATE_CONF_AND_MSB 0x17
+#define R_18_RAW_DATA_GAIN_CNTL 0x18
+#define R_19_RAW_DATA_OFF_CNTL 0x19
+#define R_1A_COLOR_KILL_LVL_CNTL 0x1a
+#define R_1B_MISC_TVVCRDET 0x1b
+#define R_1C_ENHAN_COMB_CTRL1 0x1c
+#define R_1D_ENHAN_COMB_CTRL2 0x1d
+#define R_1E_STATUS_BYTE_1_VD_DEC 0x1e
+#define R_1F_STATUS_BYTE_2_VD_DEC 0x1f
+
+/* Component processing and interrupt masking part */
+#define R_23_INPUT_CNTL_5 0x23
+#define R_24_INPUT_CNTL_6 0x24
+#define R_25_INPUT_CNTL_7 0x25
+#define R_29_COMP_DELAY 0x29
+#define R_2A_COMP_BRIGHT_CNTL 0x2a
+#define R_2B_COMP_CONTRAST_CNTL 0x2b
+#define R_2C_COMP_SAT_CNTL 0x2c
+#define R_2D_INTERRUPT_MASK_1 0x2d
+#define R_2E_INTERRUPT_MASK_2 0x2e
+#define R_2F_INTERRUPT_MASK_3 0x2f
+
+/* Audio clock generator part */
+#define R_30_AUD_MAST_CLK_CYCLES_PER_FIELD 0x30
+#define R_34_AUD_MAST_CLK_NOMINAL_INC 0x34
+#define R_38_CLK_RATIO_AMXCLK_TO_ASCLK 0x38
+#define R_39_CLK_RATIO_ASCLK_TO_ALRCLK 0x39
+#define R_3A_AUD_CLK_GEN_BASIC_SETUP 0x3a
+
+/* General purpose VBI data slicer part */
+#define R_40_SLICER_CNTL_1 0x40
+#define R_41_LCR_BASE 0x41
+#define R_58_PROGRAM_FRAMING_CODE 0x58
+#define R_59_H_OFF_FOR_SLICER 0x59
+#define R_5A_V_OFF_FOR_SLICER 0x5a
+#define R_5B_FLD_OFF_AND_MSB_FOR_H_AND_V_OFF 0x5b
+#define R_5D_DID 0x5d
+#define R_5E_SDID 0x5e
+#define R_60_SLICER_STATUS_BYTE_0 0x60
+#define R_61_SLICER_STATUS_BYTE_1 0x61
+#define R_62_SLICER_STATUS_BYTE_2 0x62
+
+/* X port, I port and the scaler part */
+ /* Task independent global settings */
+#define R_80_GLOBAL_CNTL_1 0x80
+#define R_81_V_SYNC_FLD_ID_SRC_SEL_AND_RETIMED_V_F 0x81
+#define R_83_X_PORT_I_O_ENA_AND_OUT_CLK 0x83
+#define R_84_I_PORT_SIGNAL_DEF 0x84
+#define R_85_I_PORT_SIGNAL_POLAR 0x85
+#define R_86_I_PORT_FIFO_FLAG_CNTL_AND_ARBIT 0x86
+#define R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED 0x87
+#define R_88_POWER_SAVE_ADC_PORT_CNTL 0x88
+#define R_8F_STATUS_INFO_SCALER 0x8f
+ /* Task A definition */
+ /* Basic settings and acquisition window definition */
+#define R_90_A_TASK_HANDLING_CNTL 0x90
+#define R_91_A_X_PORT_FORMATS_AND_CONF 0x91
+#define R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL 0x92
+#define R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF 0x93
+#define R_94_A_HORIZ_INPUT_WINDOW_START 0x94
+#define R_95_A_HORIZ_INPUT_WINDOW_START_MSB 0x95
+#define R_96_A_HORIZ_INPUT_WINDOW_LENGTH 0x96
+#define R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB 0x97
+#define R_98_A_VERT_INPUT_WINDOW_START 0x98
+#define R_99_A_VERT_INPUT_WINDOW_START_MSB 0x99
+#define R_9A_A_VERT_INPUT_WINDOW_LENGTH 0x9a
+#define R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB 0x9b
+#define R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH 0x9c
+#define R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB 0x9d
+#define R_9E_A_VERT_OUTPUT_WINDOW_LENGTH 0x9e
+#define R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB 0x9f
+ /* FIR filtering and prescaling */
+#define R_A0_A_HORIZ_PRESCALING 0xa0
+#define R_A1_A_ACCUMULATION_LENGTH 0xa1
+#define R_A2_A_PRESCALER_DC_GAIN_AND_FIR_PREFILTER 0xa2
+#define R_A4_A_LUMA_BRIGHTNESS_CNTL 0xa4
+#define R_A5_A_LUMA_CONTRAST_CNTL 0xa5
+#define R_A6_A_CHROMA_SATURATION_CNTL 0xa6
+ /* Horizontal phase scaling */
+#define R_A8_A_HORIZ_LUMA_SCALING_INC 0xa8
+#define R_A9_A_HORIZ_LUMA_SCALING_INC_MSB 0xa9
+#define R_AA_A_HORIZ_LUMA_PHASE_OFF 0xaa
+#define R_AC_A_HORIZ_CHROMA_SCALING_INC 0xac
+#define R_AD_A_HORIZ_CHROMA_SCALING_INC_MSB 0xad
+#define R_AE_A_HORIZ_CHROMA_PHASE_OFF 0xae
+#define R_AF_A_HORIZ_CHROMA_PHASE_OFF_MSB 0xaf
+ /* Vertical scaling */
+#define R_B0_A_VERT_LUMA_SCALING_INC 0xb0
+#define R_B1_A_VERT_LUMA_SCALING_INC_MSB 0xb1
+#define R_B2_A_VERT_CHROMA_SCALING_INC 0xb2
+#define R_B3_A_VERT_CHROMA_SCALING_INC_MSB 0xb3
+#define R_B4_A_VERT_SCALING_MODE_CNTL 0xb4
+#define R_B8_A_VERT_CHROMA_PHASE_OFF_00 0xb8
+#define R_B9_A_VERT_CHROMA_PHASE_OFF_01 0xb9
+#define R_BA_A_VERT_CHROMA_PHASE_OFF_10 0xba
+#define R_BB_A_VERT_CHROMA_PHASE_OFF_11 0xbb
+#define R_BC_A_VERT_LUMA_PHASE_OFF_00 0xbc
+#define R_BD_A_VERT_LUMA_PHASE_OFF_01 0xbd
+#define R_BE_A_VERT_LUMA_PHASE_OFF_10 0xbe
+#define R_BF_A_VERT_LUMA_PHASE_OFF_11 0xbf
+ /* Task B definition */
+ /* Basic settings and acquisition window definition */
+#define R_C0_B_TASK_HANDLING_CNTL 0xc0
+#define R_C1_B_X_PORT_FORMATS_AND_CONF 0xc1
+#define R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION 0xc2
+#define R_C3_B_I_PORT_FORMATS_AND_CONF 0xc3
+#define R_C4_B_HORIZ_INPUT_WINDOW_START 0xc4
+#define R_C5_B_HORIZ_INPUT_WINDOW_START_MSB 0xc5
+#define R_C6_B_HORIZ_INPUT_WINDOW_LENGTH 0xc6
+#define R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB 0xc7
+#define R_C8_B_VERT_INPUT_WINDOW_START 0xc8
+#define R_C9_B_VERT_INPUT_WINDOW_START_MSB 0xc9
+#define R_CA_B_VERT_INPUT_WINDOW_LENGTH 0xca
+#define R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB 0xcb
+#define R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH 0xcc
+#define R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB 0xcd
+#define R_CE_B_VERT_OUTPUT_WINDOW_LENGTH 0xce
+#define R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB 0xcf
+ /* FIR filtering and prescaling */
+#define R_D0_B_HORIZ_PRESCALING 0xd0
+#define R_D1_B_ACCUMULATION_LENGTH 0xd1
+#define R_D2_B_PRESCALER_DC_GAIN_AND_FIR_PREFILTER 0xd2
+#define R_D4_B_LUMA_BRIGHTNESS_CNTL 0xd4
+#define R_D5_B_LUMA_CONTRAST_CNTL 0xd5
+#define R_D6_B_CHROMA_SATURATION_CNTL 0xd6
+ /* Horizontal phase scaling */
+#define R_D8_B_HORIZ_LUMA_SCALING_INC 0xd8
+#define R_D9_B_HORIZ_LUMA_SCALING_INC_MSB 0xd9
+#define R_DA_B_HORIZ_LUMA_PHASE_OFF 0xda
+#define R_DC_B_HORIZ_CHROMA_SCALING 0xdc
+#define R_DD_B_HORIZ_CHROMA_SCALING_MSB 0xdd
+#define R_DE_B_HORIZ_PHASE_OFFSET_CRHOMA 0xde
+ /* Vertical scaling */
+#define R_E0_B_VERT_LUMA_SCALING_INC 0xe0
+#define R_E1_B_VERT_LUMA_SCALING_INC_MSB 0xe1
+#define R_E2_B_VERT_CHROMA_SCALING_INC 0xe2
+#define R_E3_B_VERT_CHROMA_SCALING_INC_MSB 0xe3
+#define R_E4_B_VERT_SCALING_MODE_CNTL 0xe4
+#define R_E8_B_VERT_CHROMA_PHASE_OFF_00 0xe8
+#define R_E9_B_VERT_CHROMA_PHASE_OFF_01 0xe9
+#define R_EA_B_VERT_CHROMA_PHASE_OFF_10 0xea
+#define R_EB_B_VERT_CHROMA_PHASE_OFF_11 0xeb
+#define R_EC_B_VERT_LUMA_PHASE_OFF_00 0xec
+#define R_ED_B_VERT_LUMA_PHASE_OFF_01 0xed
+#define R_EE_B_VERT_LUMA_PHASE_OFF_10 0xee
+#define R_EF_B_VERT_LUMA_PHASE_OFF_11 0xef
+
+/* second PLL (PLL2) and Pulsegenerator Programming */
+#define R_F0_LFCO_PER_LINE 0xf0
+#define R_F1_P_I_PARAM_SELECT 0xf1
+#define R_F2_NOMINAL_PLL2_DTO 0xf2
+#define R_F3_PLL_INCREMENT 0xf3
+#define R_F4_PLL2_STATUS 0xf4
+#define R_F5_PULSGEN_LINE_LENGTH 0xf5
+#define R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG 0xf6
+#define R_F7_PULSE_A_POS_MSB 0xf7
+#define R_F8_PULSE_B_POS 0xf8
+#define R_F9_PULSE_B_POS_MSB 0xf9
+#define R_FA_PULSE_C_POS 0xfa
+#define R_FB_PULSE_C_POS_MSB 0xfb
+#define R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES 0xff
+
+#if 0
+/* Those structs will be used in the future for debug purposes */
+struct saa711x_reg_descr {
+ u8 reg;
+ int count;
+ char *name;
+};
+
+struct saa711x_reg_descr saa711x_regs[] = {
+ /* REG COUNT NAME */
+ {R_00_CHIP_VERSION,1,
+ "Chip version"},
+
+ /* Video Decoder: R_01_INC_DELAY to R_1F_STATUS_BYTE_2_VD_DEC */
+
+ /* Video Decoder - Frontend part: R_01_INC_DELAY to R_05_INPUT_CNTL_4 */
+ {R_01_INC_DELAY,1,
+ "Increment delay"},
+ {R_02_INPUT_CNTL_1,1,
+ "Analog input control 1"},
+ {R_03_INPUT_CNTL_2,1,
+ "Analog input control 2"},
+ {R_04_INPUT_CNTL_3,1,
+ "Analog input control 3"},
+ {R_05_INPUT_CNTL_4,1,
+ "Analog input control 4"},
+
+ /* Video Decoder - Decoder part: R_06_H_SYNC_START to R_1F_STATUS_BYTE_2_VD_DEC */
+ {R_06_H_SYNC_START,1,
+ "Horizontal sync start"},
+ {R_07_H_SYNC_STOP,1,
+ "Horizontal sync stop"},
+ {R_08_SYNC_CNTL,1,
+ "Sync control"},
+ {R_09_LUMA_CNTL,1,
+ "Luminance control"},
+ {R_0A_LUMA_BRIGHT_CNTL,1,
+ "Luminance brightness control"},
+ {R_0B_LUMA_CONTRAST_CNTL,1,
+ "Luminance contrast control"},
+ {R_0C_CHROMA_SAT_CNTL,1,
+ "Chrominance saturation control"},
+ {R_0D_CHROMA_HUE_CNTL,1,
+ "Chrominance hue control"},
+ {R_0E_CHROMA_CNTL_1,1,
+ "Chrominance control 1"},
+ {R_0F_CHROMA_GAIN_CNTL,1,
+ "Chrominance gain control"},
+ {R_10_CHROMA_CNTL_2,1,
+ "Chrominance control 2"},
+ {R_11_MODE_DELAY_CNTL,1,
+ "Mode/delay control"},
+ {R_12_RT_SIGNAL_CNTL,1,
+ "RT signal control"},
+ {R_13_RT_X_PORT_OUT_CNTL,1,
+ "RT/X port output control"},
+ {R_14_ANAL_ADC_COMPAT_CNTL,1,
+ "Analog/ADC/compatibility control"},
+ {R_15_VGATE_START_FID_CHG, 1,
+ "VGATE start FID change"},
+ {R_16_VGATE_STOP,1,
+ "VGATE stop"},
+ {R_17_MISC_VGATE_CONF_AND_MSB, 1,
+ "Miscellaneous VGATE configuration and MSBs"},
+ {R_18_RAW_DATA_GAIN_CNTL,1,
+ "Raw data gain control",},
+ {R_19_RAW_DATA_OFF_CNTL,1,
+ "Raw data offset control",},
+ {R_1A_COLOR_KILL_LVL_CNTL,1,
+ "Color Killer Level Control"},
+ { R_1B_MISC_TVVCRDET, 1,
+ "MISC /TVVCRDET"},
+ { R_1C_ENHAN_COMB_CTRL1, 1,
+ "Enhanced comb ctrl1"},
+ { R_1D_ENHAN_COMB_CTRL2, 1,
+ "Enhanced comb ctrl1"},
+ {R_1E_STATUS_BYTE_1_VD_DEC,1,
+ "Status byte 1 video decoder"},
+ {R_1F_STATUS_BYTE_2_VD_DEC,1,
+ "Status byte 2 video decoder"},
+
+ /* Component processing and interrupt masking part: 0x20h to R_2F_INTERRUPT_MASK_3 */
+ /* 0x20 to 0x22 - Reserved */
+ {R_23_INPUT_CNTL_5,1,
+ "Analog input control 5"},
+ {R_24_INPUT_CNTL_6,1,
+ "Analog input control 6"},
+ {R_25_INPUT_CNTL_7,1,
+ "Analog input control 7"},
+ /* 0x26 to 0x28 - Reserved */
+ {R_29_COMP_DELAY,1,
+ "Component delay"},
+ {R_2A_COMP_BRIGHT_CNTL,1,
+ "Component brightness control"},
+ {R_2B_COMP_CONTRAST_CNTL,1,
+ "Component contrast control"},
+ {R_2C_COMP_SAT_CNTL,1,
+ "Component saturation control"},
+ {R_2D_INTERRUPT_MASK_1,1,
+ "Interrupt mask 1"},
+ {R_2E_INTERRUPT_MASK_2,1,
+ "Interrupt mask 2"},
+ {R_2F_INTERRUPT_MASK_3,1,
+ "Interrupt mask 3"},
+
+ /* Audio clock generator part: R_30_AUD_MAST_CLK_CYCLES_PER_FIELD to 0x3f */
+ {R_30_AUD_MAST_CLK_CYCLES_PER_FIELD,3,
+ "Audio master clock cycles per field"},
+ /* 0x33 - Reserved */
+ {R_34_AUD_MAST_CLK_NOMINAL_INC,3,
+ "Audio master clock nominal increment"},
+ /* 0x37 - Reserved */
+ {R_38_CLK_RATIO_AMXCLK_TO_ASCLK,1,
+ "Clock ratio AMXCLK to ASCLK"},
+ {R_39_CLK_RATIO_ASCLK_TO_ALRCLK,1,
+ "Clock ratio ASCLK to ALRCLK"},
+ {R_3A_AUD_CLK_GEN_BASIC_SETUP,1,
+ "Audio clock generator basic setup"},
+ /* 0x3b-0x3f - Reserved */
+
+ /* General purpose VBI data slicer part: R_40_SLICER_CNTL_1 to 0x7f */
+ {R_40_SLICER_CNTL_1,1,
+ "Slicer control 1"},
+ {R_41_LCR,23,
+ "R_41_LCR"},
+ {R_58_PROGRAM_FRAMING_CODE,1,
+ "Programmable framing code"},
+ {R_59_H_OFF_FOR_SLICER,1,
+ "Horizontal offset for slicer"},
+ {R_5A_V_OFF_FOR_SLICER,1,
+ "Vertical offset for slicer"},
+ {R_5B_FLD_OFF_AND_MSB_FOR_H_AND_V_OFF,1,
+ "Field offset and MSBs for horizontal and vertical offset"},
+ {R_5D_DID,1,
+ "Header and data identification (R_5D_DID)"},
+ {R_5E_SDID,1,
+ "Sliced data identification (R_5E_SDID) code"},
+ {R_60_SLICER_STATUS_BYTE_0,1,
+ "Slicer status byte 0"},
+ {R_61_SLICER_STATUS_BYTE_1,1,
+ "Slicer status byte 1"},
+ {R_62_SLICER_STATUS_BYTE_2,1,
+ "Slicer status byte 2"},
+ /* 0x63-0x7f - Reserved */
+
+ /* X port, I port and the scaler part: R_80_GLOBAL_CNTL_1 to R_EF_B_VERT_LUMA_PHASE_OFF_11 */
+ /* Task independent global settings: R_80_GLOBAL_CNTL_1 to R_8F_STATUS_INFO_SCALER */
+ {R_80_GLOBAL_CNTL_1,1,
+ "Global control 1"},
+ {R_81_V_SYNC_FLD_ID_SRC_SEL_AND_RETIMED_V_F,1,
+ "Vertical sync and Field ID source selection, retimed V and F signals"},
+ /* 0x82 - Reserved */
+ {R_83_X_PORT_I_O_ENA_AND_OUT_CLK,1,
+ "X port I/O enable and output clock"},
+ {R_84_I_PORT_SIGNAL_DEF,1,
+ "I port signal definitions"},
+ {R_85_I_PORT_SIGNAL_POLAR,1,
+ "I port signal polarities"},
+ {R_86_I_PORT_FIFO_FLAG_CNTL_AND_ARBIT,1,
+ "I port FIFO flag control and arbitration"},
+ {R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 1,
+ "I port I/O enable output clock and gated"},
+ {R_88_POWER_SAVE_ADC_PORT_CNTL,1,
+ "Power save/ADC port control"},
+ /* 089-0x8e - Reserved */
+ {R_8F_STATUS_INFO_SCALER,1,
+ "Status information scaler part"},
+
+ /* Task A definition: R_90_A_TASK_HANDLING_CNTL to R_BF_A_VERT_LUMA_PHASE_OFF_11 */
+ /* Task A: Basic settings and acquisition window definition */
+ {R_90_A_TASK_HANDLING_CNTL,1,
+ "Task A: Task handling control"},
+ {R_91_A_X_PORT_FORMATS_AND_CONF,1,
+ "Task A: X port formats and configuration"},
+ {R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL,1,
+ "Task A: X port input reference signal definition"},
+ {R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF,1,
+ "Task A: I port output formats and configuration"},
+ {R_94_A_HORIZ_INPUT_WINDOW_START,2,
+ "Task A: Horizontal input window start"},
+ {R_96_A_HORIZ_INPUT_WINDOW_LENGTH,2,
+ "Task A: Horizontal input window length"},
+ {R_98_A_VERT_INPUT_WINDOW_START,2,
+ "Task A: Vertical input window start"},
+ {R_9A_A_VERT_INPUT_WINDOW_LENGTH,2,
+ "Task A: Vertical input window length"},
+ {R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH,2,
+ "Task A: Horizontal output window length"},
+ {R_9E_A_VERT_OUTPUT_WINDOW_LENGTH,2,
+ "Task A: Vertical output window length"},
+
+ /* Task A: FIR filtering and prescaling */
+ {R_A0_A_HORIZ_PRESCALING,1,
+ "Task A: Horizontal prescaling"},
+ {R_A1_A_ACCUMULATION_LENGTH,1,
+ "Task A: Accumulation length"},
+ {R_A2_A_PRESCALER_DC_GAIN_AND_FIR_PREFILTER,1,
+ "Task A: Prescaler DC gain and FIR prefilter"},
+ /* 0xa3 - Reserved */
+ {R_A4_A_LUMA_BRIGHTNESS_CNTL,1,
+ "Task A: Luminance brightness control"},
+ {R_A5_A_LUMA_CONTRAST_CNTL,1,
+ "Task A: Luminance contrast control"},
+ {R_A6_A_CHROMA_SATURATION_CNTL,1,
+ "Task A: Chrominance saturation control"},
+ /* 0xa7 - Reserved */
+
+ /* Task A: Horizontal phase scaling */
+ {R_A8_A_HORIZ_LUMA_SCALING_INC,2,
+ "Task A: Horizontal luminance scaling increment"},
+ {R_AA_A_HORIZ_LUMA_PHASE_OFF,1,
+ "Task A: Horizontal luminance phase offset"},
+ /* 0xab - Reserved */
+ {R_AC_A_HORIZ_CHROMA_SCALING_INC,2,
+ "Task A: Horizontal chrominance scaling increment"},
+ {R_AE_A_HORIZ_CHROMA_PHASE_OFF,1,
+ "Task A: Horizontal chrominance phase offset"},
+ /* 0xaf - Reserved */
+
+ /* Task A: Vertical scaling */
+ {R_B0_A_VERT_LUMA_SCALING_INC,2,
+ "Task A: Vertical luminance scaling increment"},
+ {R_B2_A_VERT_CHROMA_SCALING_INC,2,
+ "Task A: Vertical chrominance scaling increment"},
+ {R_B4_A_VERT_SCALING_MODE_CNTL,1,
+ "Task A: Vertical scaling mode control"},
+ /* 0xb5-0xb7 - Reserved */
+ {R_B8_A_VERT_CHROMA_PHASE_OFF_00,1,
+ "Task A: Vertical chrominance phase offset '00'"},
+ {R_B9_A_VERT_CHROMA_PHASE_OFF_01,1,
+ "Task A: Vertical chrominance phase offset '01'"},
+ {R_BA_A_VERT_CHROMA_PHASE_OFF_10,1,
+ "Task A: Vertical chrominance phase offset '10'"},
+ {R_BB_A_VERT_CHROMA_PHASE_OFF_11,1,
+ "Task A: Vertical chrominance phase offset '11'"},
+ {R_BC_A_VERT_LUMA_PHASE_OFF_00,1,
+ "Task A: Vertical luminance phase offset '00'"},
+ {R_BD_A_VERT_LUMA_PHASE_OFF_01,1,
+ "Task A: Vertical luminance phase offset '01'"},
+ {R_BE_A_VERT_LUMA_PHASE_OFF_10,1,
+ "Task A: Vertical luminance phase offset '10'"},
+ {R_BF_A_VERT_LUMA_PHASE_OFF_11,1,
+ "Task A: Vertical luminance phase offset '11'"},
+
+ /* Task B definition: R_C0_B_TASK_HANDLING_CNTL to R_EF_B_VERT_LUMA_PHASE_OFF_11 */
+ /* Task B: Basic settings and acquisition window definition */
+ {R_C0_B_TASK_HANDLING_CNTL,1,
+ "Task B: Task handling control"},
+ {R_C1_B_X_PORT_FORMATS_AND_CONF,1,
+ "Task B: X port formats and configuration"},
+ {R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION,1,
+ "Task B: Input reference signal definition"},
+ {R_C3_B_I_PORT_FORMATS_AND_CONF,1,
+ "Task B: I port formats and configuration"},
+ {R_C4_B_HORIZ_INPUT_WINDOW_START,2,
+ "Task B: Horizontal input window start"},
+ {R_C6_B_HORIZ_INPUT_WINDOW_LENGTH,2,
+ "Task B: Horizontal input window length"},
+ {R_C8_B_VERT_INPUT_WINDOW_START,2,
+ "Task B: Vertical input window start"},
+ {R_CA_B_VERT_INPUT_WINDOW_LENGTH,2,
+ "Task B: Vertical input window length"},
+ {R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH,2,
+ "Task B: Horizontal output window length"},
+ {R_CE_B_VERT_OUTPUT_WINDOW_LENGTH,2,
+ "Task B: Vertical output window length"},
+
+ /* Task B: FIR filtering and prescaling */
+ {R_D0_B_HORIZ_PRESCALING,1,
+ "Task B: Horizontal prescaling"},
+ {R_D1_B_ACCUMULATION_LENGTH,1,
+ "Task B: Accumulation length"},
+ {R_D2_B_PRESCALER_DC_GAIN_AND_FIR_PREFILTER,1,
+ "Task B: Prescaler DC gain and FIR prefilter"},
+ /* 0xd3 - Reserved */
+ {R_D4_B_LUMA_BRIGHTNESS_CNTL,1,
+ "Task B: Luminance brightness control"},
+ {R_D5_B_LUMA_CONTRAST_CNTL,1,
+ "Task B: Luminance contrast control"},
+ {R_D6_B_CHROMA_SATURATION_CNTL,1,
+ "Task B: Chrominance saturation control"},
+ /* 0xd7 - Reserved */
+
+ /* Task B: Horizontal phase scaling */
+ {R_D8_B_HORIZ_LUMA_SCALING_INC,2,
+ "Task B: Horizontal luminance scaling increment"},
+ {R_DA_B_HORIZ_LUMA_PHASE_OFF,1,
+ "Task B: Horizontal luminance phase offset"},
+ /* 0xdb - Reserved */
+ {R_DC_B_HORIZ_CHROMA_SCALING,2,
+ "Task B: Horizontal chrominance scaling"},
+ {R_DE_B_HORIZ_PHASE_OFFSET_CRHOMA,1,
+ "Task B: Horizontal Phase Offset Chroma"},
+ /* 0xdf - Reserved */
+
+ /* Task B: Vertical scaling */
+ {R_E0_B_VERT_LUMA_SCALING_INC,2,
+ "Task B: Vertical luminance scaling increment"},
+ {R_E2_B_VERT_CHROMA_SCALING_INC,2,
+ "Task B: Vertical chrominance scaling increment"},
+ {R_E4_B_VERT_SCALING_MODE_CNTL,1,
+ "Task B: Vertical scaling mode control"},
+ /* 0xe5-0xe7 - Reserved */
+ {R_E8_B_VERT_CHROMA_PHASE_OFF_00,1,
+ "Task B: Vertical chrominance phase offset '00'"},
+ {R_E9_B_VERT_CHROMA_PHASE_OFF_01,1,
+ "Task B: Vertical chrominance phase offset '01'"},
+ {R_EA_B_VERT_CHROMA_PHASE_OFF_10,1,
+ "Task B: Vertical chrominance phase offset '10'"},
+ {R_EB_B_VERT_CHROMA_PHASE_OFF_11,1,
+ "Task B: Vertical chrominance phase offset '11'"},
+ {R_EC_B_VERT_LUMA_PHASE_OFF_00,1,
+ "Task B: Vertical luminance phase offset '00'"},
+ {R_ED_B_VERT_LUMA_PHASE_OFF_01,1,
+ "Task B: Vertical luminance phase offset '01'"},
+ {R_EE_B_VERT_LUMA_PHASE_OFF_10,1,
+ "Task B: Vertical luminance phase offset '10'"},
+ {R_EF_B_VERT_LUMA_PHASE_OFF_11,1,
+ "Task B: Vertical luminance phase offset '11'"},
+
+ /* second PLL (PLL2) and Pulsegenerator Programming */
+ { R_F0_LFCO_PER_LINE, 1,
+ "LFCO's per line"},
+ { R_F1_P_I_PARAM_SELECT,1,
+ "P-/I- Param. Select., PLL Mode, PLL H-Src., LFCO's per line"},
+ { R_F2_NOMINAL_PLL2_DTO,1,
+ "Nominal PLL2 DTO"},
+ {R_F3_PLL_INCREMENT,1,
+ "PLL2 Increment"},
+ {R_F4_PLL2_STATUS,1,
+ "PLL2 Status"},
+ {R_F5_PULSGEN_LINE_LENGTH,1,
+ "Pulsgen. line length"},
+ {R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG,1,
+ "Pulse A Position, Pulsgen Resync., Pulsgen. H-Src., Pulsgen. line length"},
+ {R_F7_PULSE_A_POS_MSB,1,
+ "Pulse A Position"},
+ {R_F8_PULSE_B_POS,2,
+ "Pulse B Position"},
+ {R_FA_PULSE_C_POS,2,
+ "Pulse C Position"},
+ /* 0xfc to 0xfe - Reserved */
+ {R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES,1,
+ "S_PLL max. phase, error threshold, PLL2 no. of lines, threshold"},
+};
+#endif
diff --git a/drivers/media/video/saa7121.h b/drivers/media/video/saa7121.h
new file mode 100644
index 00000000..66967ae3
--- /dev/null
+++ b/drivers/media/video/saa7121.h
@@ -0,0 +1,132 @@
+/* saa7121.h - saa7121 initializations
+ Copyright (C) 1999 Nathan Laredo (laredo@gnu.org)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+#ifndef __SAA7121_H__
+#define __SAA7121_H__
+
+#define NTSC_BURST_START 0x19 /* 28 */
+#define NTSC_BURST_END 0x1d /* 29 */
+#define NTSC_CHROMA_PHASE 0x67 /* 5a */
+#define NTSC_GAINU 0x76 /* 5b */
+#define NTSC_GAINV 0xa5 /* 5c */
+#define NTSC_BLACK_LEVEL 0x2a /* 5d */
+#define NTSC_BLANKING_LEVEL 0x2e /* 5e */
+#define NTSC_VBI_BLANKING 0x2e /* 5f */
+#define NTSC_DAC_CONTROL 0x11 /* 61 */
+#define NTSC_BURST_AMP 0x3f /* 62 */
+#define NTSC_SUBC3 0x1f /* 63 */
+#define NTSC_SUBC2 0x7c /* 64 */
+#define NTSC_SUBC1 0xf0 /* 65 */
+#define NTSC_SUBC0 0x21 /* 66 */
+#define NTSC_HTRIG 0x72 /* 6c */
+#define NTSC_VTRIG 0x00 /* 6c */
+#define NTSC_MULTI 0x30 /* 6e */
+#define NTSC_CCTTX 0x11 /* 6f */
+#define NTSC_FIRST_ACTIVE 0x12 /* 7a */
+#define NTSC_LAST_ACTIVE 0x02 /* 7b */
+#define NTSC_MSB_VERTICAL 0x40 /* 7c */
+
+#define PAL_BURST_START 0x21 /* 28 */
+#define PAL_BURST_END 0x1d /* 29 */
+#define PAL_CHROMA_PHASE 0x3f /* 5a */
+#define PAL_GAINU 0x7d /* 5b */
+#define PAL_GAINV 0xaf /* 5c */
+#define PAL_BLACK_LEVEL 0x23 /* 5d */
+#define PAL_BLANKING_LEVEL 0x35 /* 5e */
+#define PAL_VBI_BLANKING 0x35 /* 5f */
+#define PAL_DAC_CONTROL 0x02 /* 61 */
+#define PAL_BURST_AMP 0x2f /* 62 */
+#define PAL_SUBC3 0xcb /* 63 */
+#define PAL_SUBC2 0x8a /* 64 */
+#define PAL_SUBC1 0x09 /* 65 */
+#define PAL_SUBC0 0x2a /* 66 */
+#define PAL_HTRIG 0x86 /* 6c */
+#define PAL_VTRIG 0x04 /* 6d */
+#define PAL_MULTI 0x20 /* 6e */
+#define PAL_CCTTX 0x15 /* 6f */
+#define PAL_FIRST_ACTIVE 0x16 /* 7a */
+#define PAL_LAST_ACTIVE 0x36 /* 7b */
+#define PAL_MSB_VERTICAL 0x40 /* 7c */
+
+/* Initialization Sequence */
+
+static __u8 init7121ntsc[] = {
+ 0x26, 0x0, 0x27, 0x0,
+ 0x28, NTSC_BURST_START, 0x29, NTSC_BURST_END,
+ 0x2a, 0x0, 0x2b, 0x0, 0x2c, 0x0, 0x2d, 0x0,
+ 0x2e, 0x0, 0x2f, 0x0, 0x30, 0x0, 0x31, 0x0,
+ 0x32, 0x0, 0x33, 0x0, 0x34, 0x0, 0x35, 0x0,
+ 0x36, 0x0, 0x37, 0x0, 0x38, 0x0, 0x39, 0x0,
+ 0x3a, 0x03, 0x3b, 0x0, 0x3c, 0x0, 0x3d, 0x0,
+ 0x3e, 0x0, 0x3f, 0x0, 0x40, 0x0, 0x41, 0x0,
+ 0x42, 0x0, 0x43, 0x0, 0x44, 0x0, 0x45, 0x0,
+ 0x46, 0x0, 0x47, 0x0, 0x48, 0x0, 0x49, 0x0,
+ 0x4a, 0x0, 0x4b, 0x0, 0x4c, 0x0, 0x4d, 0x0,
+ 0x4e, 0x0, 0x4f, 0x0, 0x50, 0x0, 0x51, 0x0,
+ 0x52, 0x0, 0x53, 0x0, 0x54, 0x0, 0x55, 0x0,
+ 0x56, 0x0, 0x57, 0x0, 0x58, 0x0, 0x59, 0x0,
+ 0x5a, NTSC_CHROMA_PHASE, 0x5b, NTSC_GAINU,
+ 0x5c, NTSC_GAINV, 0x5d, NTSC_BLACK_LEVEL,
+ 0x5e, NTSC_BLANKING_LEVEL, 0x5f, NTSC_VBI_BLANKING,
+ 0x60, 0x0, 0x61, NTSC_DAC_CONTROL,
+ 0x62, NTSC_BURST_AMP, 0x63, NTSC_SUBC3,
+ 0x64, NTSC_SUBC2, 0x65, NTSC_SUBC1,
+ 0x66, NTSC_SUBC0, 0x67, 0x80, 0x68, 0x80,
+ 0x69, 0x80, 0x6a, 0x80, 0x6b, 0x29,
+ 0x6c, NTSC_HTRIG, 0x6d, NTSC_VTRIG,
+ 0x6e, NTSC_MULTI, 0x6f, NTSC_CCTTX,
+ 0x70, 0xc9, 0x71, 0x68, 0x72, 0x60, 0x73, 0x0,
+ 0x74, 0x0, 0x75, 0x0, 0x76, 0x0, 0x77, 0x0,
+ 0x78, 0x0, 0x79, 0x0, 0x7a, NTSC_FIRST_ACTIVE,
+ 0x7b, NTSC_LAST_ACTIVE, 0x7c, NTSC_MSB_VERTICAL,
+ 0x7d, 0x0, 0x7e, 0x0, 0x7f, 0x0
+};
+#define INIT7121LEN (sizeof(init7121ntsc)/2)
+
+static __u8 init7121pal[] = {
+ 0x26, 0x0, 0x27, 0x0,
+ 0x28, PAL_BURST_START, 0x29, PAL_BURST_END,
+ 0x2a, 0x0, 0x2b, 0x0, 0x2c, 0x0, 0x2d, 0x0,
+ 0x2e, 0x0, 0x2f, 0x0, 0x30, 0x0, 0x31, 0x0,
+ 0x32, 0x0, 0x33, 0x0, 0x34, 0x0, 0x35, 0x0,
+ 0x36, 0x0, 0x37, 0x0, 0x38, 0x0, 0x39, 0x0,
+ 0x3a, 0x03, 0x3b, 0x0, 0x3c, 0x0, 0x3d, 0x0,
+ 0x3e, 0x0, 0x3f, 0x0, 0x40, 0x0, 0x41, 0x0,
+ 0x42, 0x0, 0x43, 0x0, 0x44, 0x0, 0x45, 0x0,
+ 0x46, 0x0, 0x47, 0x0, 0x48, 0x0, 0x49, 0x0,
+ 0x4a, 0x0, 0x4b, 0x0, 0x4c, 0x0, 0x4d, 0x0,
+ 0x4e, 0x0, 0x4f, 0x0, 0x50, 0x0, 0x51, 0x0,
+ 0x52, 0x0, 0x53, 0x0, 0x54, 0x0, 0x55, 0x0,
+ 0x56, 0x0, 0x57, 0x0, 0x58, 0x0, 0x59, 0x0,
+ 0x5a, PAL_CHROMA_PHASE, 0x5b, PAL_GAINU,
+ 0x5c, PAL_GAINV, 0x5d, PAL_BLACK_LEVEL,
+ 0x5e, PAL_BLANKING_LEVEL, 0x5f, PAL_VBI_BLANKING,
+ 0x60, 0x0, 0x61, PAL_DAC_CONTROL,
+ 0x62, PAL_BURST_AMP, 0x63, PAL_SUBC3,
+ 0x64, PAL_SUBC2, 0x65, PAL_SUBC1,
+ 0x66, PAL_SUBC0, 0x67, 0x80, 0x68, 0x80,
+ 0x69, 0x80, 0x6a, 0x80, 0x6b, 0x29,
+ 0x6c, PAL_HTRIG, 0x6d, PAL_VTRIG,
+ 0x6e, PAL_MULTI, 0x6f, PAL_CCTTX,
+ 0x70, 0xc9, 0x71, 0x68, 0x72, 0x60, 0x73, 0x0,
+ 0x74, 0x0, 0x75, 0x0, 0x76, 0x0, 0x77, 0x0,
+ 0x78, 0x0, 0x79, 0x0, 0x7a, PAL_FIRST_ACTIVE,
+ 0x7b, PAL_LAST_ACTIVE, 0x7c, PAL_MSB_VERTICAL,
+ 0x7d, 0x0, 0x7e, 0x0, 0x7f, 0x0
+};
+#endif
diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c
new file mode 100644
index 00000000..39c90b08
--- /dev/null
+++ b/drivers/media/video/saa7127.c
@@ -0,0 +1,855 @@
+/*
+ * saa7127 - Philips SAA7127/SAA7129 video encoder driver
+ *
+ * Copyright (C) 2003 Roy Bulter <rbulter@hetnet.nl>
+ *
+ * Based on SAA7126 video encoder driver by Gillem & Andreas Oberritter
+ *
+ * Copyright (C) 2000-2001 Gillem <htoa@gmx.net>
+ * Copyright (C) 2002 Andreas Oberritter <obi@saftware.de>
+ *
+ * Based on Stadis 4:2:2 MPEG-2 Decoder Driver by Nathan Laredo
+ *
+ * Copyright (C) 1999 Nathan Laredo <laredo@gnu.org>
+ *
+ * This driver is designed for the Hauppauge 250/350 Linux driver
+ * from the ivtv Project
+ *
+ * Copyright (C) 2003 Kevin Thayer <nufan_wfk@yahoo.com>
+ *
+ * Dual output support:
+ * Copyright (C) 2004 Eric Varsanyi
+ *
+ * NTSC Tuning and 7.5 IRE Setup
+ * Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ *
+ * VBI additions & cleanup:
+ * Copyright (C) 2004, 2005 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * Note: the saa7126 is identical to the saa7127, and the saa7128 is
+ * identical to the saa7129, except that the saa7126 and saa7128 have
+ * macrovision anti-taping support. This driver will almost certainly
+ * work fine for those chips, except of course for the missing anti-taping
+ * support.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/saa7127.h>
+
+static int debug;
+static int test_image;
+
+MODULE_DESCRIPTION("Philips SAA7127/9 video encoder driver");
+MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil");
+MODULE_LICENSE("GPL");
+module_param(debug, int, 0644);
+module_param(test_image, int, 0644);
+MODULE_PARM_DESC(debug, "debug level (0-2)");
+MODULE_PARM_DESC(test_image, "test_image (0-1)");
+
+
+/*
+ * SAA7127 registers
+ */
+
+#define SAA7127_REG_STATUS 0x00
+#define SAA7127_REG_WIDESCREEN_CONFIG 0x26
+#define SAA7127_REG_WIDESCREEN_ENABLE 0x27
+#define SAA7127_REG_BURST_START 0x28
+#define SAA7127_REG_BURST_END 0x29
+#define SAA7127_REG_COPYGEN_0 0x2a
+#define SAA7127_REG_COPYGEN_1 0x2b
+#define SAA7127_REG_COPYGEN_2 0x2c
+#define SAA7127_REG_OUTPUT_PORT_CONTROL 0x2d
+#define SAA7127_REG_GAIN_LUMINANCE_RGB 0x38
+#define SAA7127_REG_GAIN_COLORDIFF_RGB 0x39
+#define SAA7127_REG_INPUT_PORT_CONTROL_1 0x3a
+#define SAA7129_REG_FADE_KEY_COL2 0x4f
+#define SAA7127_REG_CHROMA_PHASE 0x5a
+#define SAA7127_REG_GAINU 0x5b
+#define SAA7127_REG_GAINV 0x5c
+#define SAA7127_REG_BLACK_LEVEL 0x5d
+#define SAA7127_REG_BLANKING_LEVEL 0x5e
+#define SAA7127_REG_VBI_BLANKING 0x5f
+#define SAA7127_REG_DAC_CONTROL 0x61
+#define SAA7127_REG_BURST_AMP 0x62
+#define SAA7127_REG_SUBC3 0x63
+#define SAA7127_REG_SUBC2 0x64
+#define SAA7127_REG_SUBC1 0x65
+#define SAA7127_REG_SUBC0 0x66
+#define SAA7127_REG_LINE_21_ODD_0 0x67
+#define SAA7127_REG_LINE_21_ODD_1 0x68
+#define SAA7127_REG_LINE_21_EVEN_0 0x69
+#define SAA7127_REG_LINE_21_EVEN_1 0x6a
+#define SAA7127_REG_RCV_PORT_CONTROL 0x6b
+#define SAA7127_REG_VTRIG 0x6c
+#define SAA7127_REG_HTRIG_HI 0x6d
+#define SAA7127_REG_MULTI 0x6e
+#define SAA7127_REG_CLOSED_CAPTION 0x6f
+#define SAA7127_REG_RCV2_OUTPUT_START 0x70
+#define SAA7127_REG_RCV2_OUTPUT_END 0x71
+#define SAA7127_REG_RCV2_OUTPUT_MSBS 0x72
+#define SAA7127_REG_TTX_REQUEST_H_START 0x73
+#define SAA7127_REG_TTX_REQUEST_H_DELAY_LENGTH 0x74
+#define SAA7127_REG_CSYNC_ADVANCE_VSYNC_SHIFT 0x75
+#define SAA7127_REG_TTX_ODD_REQ_VERT_START 0x76
+#define SAA7127_REG_TTX_ODD_REQ_VERT_END 0x77
+#define SAA7127_REG_TTX_EVEN_REQ_VERT_START 0x78
+#define SAA7127_REG_TTX_EVEN_REQ_VERT_END 0x79
+#define SAA7127_REG_FIRST_ACTIVE 0x7a
+#define SAA7127_REG_LAST_ACTIVE 0x7b
+#define SAA7127_REG_MSB_VERTICAL 0x7c
+#define SAA7127_REG_DISABLE_TTX_LINE_LO_0 0x7e
+#define SAA7127_REG_DISABLE_TTX_LINE_LO_1 0x7f
+
+/*
+ **********************************************************************
+ *
+ * Arrays with configuration parameters for the SAA7127
+ *
+ **********************************************************************
+ */
+
+struct i2c_reg_value {
+ unsigned char reg;
+ unsigned char value;
+};
+
+static const struct i2c_reg_value saa7129_init_config_extra[] = {
+ { SAA7127_REG_OUTPUT_PORT_CONTROL, 0x38 },
+ { SAA7127_REG_VTRIG, 0xfa },
+ { 0, 0 }
+};
+
+static const struct i2c_reg_value saa7127_init_config_common[] = {
+ { SAA7127_REG_WIDESCREEN_CONFIG, 0x0d },
+ { SAA7127_REG_WIDESCREEN_ENABLE, 0x00 },
+ { SAA7127_REG_COPYGEN_0, 0x77 },
+ { SAA7127_REG_COPYGEN_1, 0x41 },
+ { SAA7127_REG_COPYGEN_2, 0x00 }, /* Macrovision enable/disable */
+ { SAA7127_REG_OUTPUT_PORT_CONTROL, 0xbf },
+ { SAA7127_REG_GAIN_LUMINANCE_RGB, 0x00 },
+ { SAA7127_REG_GAIN_COLORDIFF_RGB, 0x00 },
+ { SAA7127_REG_INPUT_PORT_CONTROL_1, 0x80 }, /* for color bars */
+ { SAA7127_REG_LINE_21_ODD_0, 0x77 },
+ { SAA7127_REG_LINE_21_ODD_1, 0x41 },
+ { SAA7127_REG_LINE_21_EVEN_0, 0x88 },
+ { SAA7127_REG_LINE_21_EVEN_1, 0x41 },
+ { SAA7127_REG_RCV_PORT_CONTROL, 0x12 },
+ { SAA7127_REG_VTRIG, 0xf9 },
+ { SAA7127_REG_HTRIG_HI, 0x00 },
+ { SAA7127_REG_RCV2_OUTPUT_START, 0x41 },
+ { SAA7127_REG_RCV2_OUTPUT_END, 0xc3 },
+ { SAA7127_REG_RCV2_OUTPUT_MSBS, 0x00 },
+ { SAA7127_REG_TTX_REQUEST_H_START, 0x3e },
+ { SAA7127_REG_TTX_REQUEST_H_DELAY_LENGTH, 0xb8 },
+ { SAA7127_REG_CSYNC_ADVANCE_VSYNC_SHIFT, 0x03 },
+ { SAA7127_REG_TTX_ODD_REQ_VERT_START, 0x15 },
+ { SAA7127_REG_TTX_ODD_REQ_VERT_END, 0x16 },
+ { SAA7127_REG_TTX_EVEN_REQ_VERT_START, 0x15 },
+ { SAA7127_REG_TTX_EVEN_REQ_VERT_END, 0x16 },
+ { SAA7127_REG_FIRST_ACTIVE, 0x1a },
+ { SAA7127_REG_LAST_ACTIVE, 0x01 },
+ { SAA7127_REG_MSB_VERTICAL, 0xc0 },
+ { SAA7127_REG_DISABLE_TTX_LINE_LO_0, 0x00 },
+ { SAA7127_REG_DISABLE_TTX_LINE_LO_1, 0x00 },
+ { 0, 0 }
+};
+
+#define SAA7127_60HZ_DAC_CONTROL 0x15
+static const struct i2c_reg_value saa7127_init_config_60hz[] = {
+ { SAA7127_REG_BURST_START, 0x19 },
+ /* BURST_END is also used as a chip ID in saa7127_probe */
+ { SAA7127_REG_BURST_END, 0x1d },
+ { SAA7127_REG_CHROMA_PHASE, 0xa3 },
+ { SAA7127_REG_GAINU, 0x98 },
+ { SAA7127_REG_GAINV, 0xd3 },
+ { SAA7127_REG_BLACK_LEVEL, 0x39 },
+ { SAA7127_REG_BLANKING_LEVEL, 0x2e },
+ { SAA7127_REG_VBI_BLANKING, 0x2e },
+ { SAA7127_REG_DAC_CONTROL, 0x15 },
+ { SAA7127_REG_BURST_AMP, 0x4d },
+ { SAA7127_REG_SUBC3, 0x1f },
+ { SAA7127_REG_SUBC2, 0x7c },
+ { SAA7127_REG_SUBC1, 0xf0 },
+ { SAA7127_REG_SUBC0, 0x21 },
+ { SAA7127_REG_MULTI, 0x90 },
+ { SAA7127_REG_CLOSED_CAPTION, 0x11 },
+ { 0, 0 }
+};
+
+#define SAA7127_50HZ_PAL_DAC_CONTROL 0x02
+static struct i2c_reg_value saa7127_init_config_50hz_pal[] = {
+ { SAA7127_REG_BURST_START, 0x21 },
+ /* BURST_END is also used as a chip ID in saa7127_probe */
+ { SAA7127_REG_BURST_END, 0x1d },
+ { SAA7127_REG_CHROMA_PHASE, 0x3f },
+ { SAA7127_REG_GAINU, 0x7d },
+ { SAA7127_REG_GAINV, 0xaf },
+ { SAA7127_REG_BLACK_LEVEL, 0x33 },
+ { SAA7127_REG_BLANKING_LEVEL, 0x35 },
+ { SAA7127_REG_VBI_BLANKING, 0x35 },
+ { SAA7127_REG_DAC_CONTROL, 0x02 },
+ { SAA7127_REG_BURST_AMP, 0x2f },
+ { SAA7127_REG_SUBC3, 0xcb },
+ { SAA7127_REG_SUBC2, 0x8a },
+ { SAA7127_REG_SUBC1, 0x09 },
+ { SAA7127_REG_SUBC0, 0x2a },
+ { SAA7127_REG_MULTI, 0xa0 },
+ { SAA7127_REG_CLOSED_CAPTION, 0x00 },
+ { 0, 0 }
+};
+
+#define SAA7127_50HZ_SECAM_DAC_CONTROL 0x08
+static struct i2c_reg_value saa7127_init_config_50hz_secam[] = {
+ { SAA7127_REG_BURST_START, 0x21 },
+ /* BURST_END is also used as a chip ID in saa7127_probe */
+ { SAA7127_REG_BURST_END, 0x1d },
+ { SAA7127_REG_CHROMA_PHASE, 0x3f },
+ { SAA7127_REG_GAINU, 0x6a },
+ { SAA7127_REG_GAINV, 0x81 },
+ { SAA7127_REG_BLACK_LEVEL, 0x33 },
+ { SAA7127_REG_BLANKING_LEVEL, 0x35 },
+ { SAA7127_REG_VBI_BLANKING, 0x35 },
+ { SAA7127_REG_DAC_CONTROL, 0x08 },
+ { SAA7127_REG_BURST_AMP, 0x2f },
+ { SAA7127_REG_SUBC3, 0xb2 },
+ { SAA7127_REG_SUBC2, 0x3b },
+ { SAA7127_REG_SUBC1, 0xa3 },
+ { SAA7127_REG_SUBC0, 0x28 },
+ { SAA7127_REG_MULTI, 0x90 },
+ { SAA7127_REG_CLOSED_CAPTION, 0x00 },
+ { 0, 0 }
+};
+
+/*
+ **********************************************************************
+ *
+ * Encoder Struct, holds the configuration state of the encoder
+ *
+ **********************************************************************
+ */
+
+struct saa7127_state {
+ struct v4l2_subdev sd;
+ v4l2_std_id std;
+ u32 ident;
+ enum saa7127_input_type input_type;
+ enum saa7127_output_type output_type;
+ int video_enable;
+ int wss_enable;
+ u16 wss_mode;
+ int cc_enable;
+ u16 cc_data;
+ int xds_enable;
+ u16 xds_data;
+ int vps_enable;
+ u8 vps_data[5];
+ u8 reg_2d;
+ u8 reg_3a;
+ u8 reg_3a_cb; /* colorbar bit */
+ u8 reg_61;
+};
+
+static inline struct saa7127_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct saa7127_state, sd);
+}
+
+static const char * const output_strs[] =
+{
+ "S-Video + Composite",
+ "Composite",
+ "S-Video",
+ "RGB",
+ "YUV C",
+ "YUV V"
+};
+
+static const char * const wss_strs[] = {
+ "invalid",
+ "letterbox 14:9 center",
+ "letterbox 14:9 top",
+ "invalid",
+ "letterbox 16:9 top",
+ "invalid",
+ "invalid",
+ "16:9 full format anamorphic",
+ "4:3 full format",
+ "invalid",
+ "invalid",
+ "letterbox 16:9 center",
+ "invalid",
+ "letterbox >16:9 center",
+ "14:9 full format center",
+ "invalid",
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_write(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ if (i2c_smbus_write_byte_data(client, reg, val) == 0)
+ return 0;
+ }
+ v4l2_err(sd, "I2C Write Problem\n");
+ return -1;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_write_inittab(struct v4l2_subdev *sd,
+ const struct i2c_reg_value *regs)
+{
+ while (regs->reg != 0) {
+ saa7127_write(sd, regs->reg, regs->value);
+ regs++;
+ }
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_vps(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
+{
+ struct saa7127_state *state = to_state(sd);
+ int enable = (data->line != 0);
+
+ if (enable && (data->field != 0 || data->line != 16))
+ return -EINVAL;
+ if (state->vps_enable != enable) {
+ v4l2_dbg(1, debug, sd, "Turn VPS Signal %s\n", enable ? "on" : "off");
+ saa7127_write(sd, 0x54, enable << 7);
+ state->vps_enable = enable;
+ }
+ if (!enable)
+ return 0;
+
+ state->vps_data[0] = data->data[2];
+ state->vps_data[1] = data->data[8];
+ state->vps_data[2] = data->data[9];
+ state->vps_data[3] = data->data[10];
+ state->vps_data[4] = data->data[11];
+ v4l2_dbg(1, debug, sd, "Set VPS data %02x %02x %02x %02x %02x\n",
+ state->vps_data[0], state->vps_data[1],
+ state->vps_data[2], state->vps_data[3],
+ state->vps_data[4]);
+ saa7127_write(sd, 0x55, state->vps_data[0]);
+ saa7127_write(sd, 0x56, state->vps_data[1]);
+ saa7127_write(sd, 0x57, state->vps_data[2]);
+ saa7127_write(sd, 0x58, state->vps_data[3]);
+ saa7127_write(sd, 0x59, state->vps_data[4]);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_cc(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
+{
+ struct saa7127_state *state = to_state(sd);
+ u16 cc = data->data[1] << 8 | data->data[0];
+ int enable = (data->line != 0);
+
+ if (enable && (data->field != 0 || data->line != 21))
+ return -EINVAL;
+ if (state->cc_enable != enable) {
+ v4l2_dbg(1, debug, sd,
+ "Turn CC %s\n", enable ? "on" : "off");
+ saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION,
+ (state->xds_enable << 7) | (enable << 6) | 0x11);
+ state->cc_enable = enable;
+ }
+ if (!enable)
+ return 0;
+
+ v4l2_dbg(2, debug, sd, "CC data: %04x\n", cc);
+ saa7127_write(sd, SAA7127_REG_LINE_21_ODD_0, cc & 0xff);
+ saa7127_write(sd, SAA7127_REG_LINE_21_ODD_1, cc >> 8);
+ state->cc_data = cc;
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_xds(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
+{
+ struct saa7127_state *state = to_state(sd);
+ u16 xds = data->data[1] << 8 | data->data[0];
+ int enable = (data->line != 0);
+
+ if (enable && (data->field != 1 || data->line != 21))
+ return -EINVAL;
+ if (state->xds_enable != enable) {
+ v4l2_dbg(1, debug, sd, "Turn XDS %s\n", enable ? "on" : "off");
+ saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION,
+ (enable << 7) | (state->cc_enable << 6) | 0x11);
+ state->xds_enable = enable;
+ }
+ if (!enable)
+ return 0;
+
+ v4l2_dbg(2, debug, sd, "XDS data: %04x\n", xds);
+ saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_0, xds & 0xff);
+ saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_1, xds >> 8);
+ state->xds_data = xds;
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_wss(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
+{
+ struct saa7127_state *state = to_state(sd);
+ int enable = (data->line != 0);
+
+ if (enable && (data->field != 0 || data->line != 23))
+ return -EINVAL;
+ if (state->wss_enable != enable) {
+ v4l2_dbg(1, debug, sd, "Turn WSS %s\n", enable ? "on" : "off");
+ saa7127_write(sd, 0x27, enable << 7);
+ state->wss_enable = enable;
+ }
+ if (!enable)
+ return 0;
+
+ saa7127_write(sd, 0x26, data->data[0]);
+ saa7127_write(sd, 0x27, 0x80 | (data->data[1] & 0x3f));
+ v4l2_dbg(1, debug, sd,
+ "WSS mode: %s\n", wss_strs[data->data[0] & 0xf]);
+ state->wss_mode = (data->data[1] & 0x3f) << 8 | data->data[0];
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_video_enable(struct v4l2_subdev *sd, int enable)
+{
+ struct saa7127_state *state = to_state(sd);
+
+ if (enable) {
+ v4l2_dbg(1, debug, sd, "Enable Video Output\n");
+ saa7127_write(sd, 0x2d, state->reg_2d);
+ saa7127_write(sd, 0x61, state->reg_61);
+ } else {
+ v4l2_dbg(1, debug, sd, "Disable Video Output\n");
+ saa7127_write(sd, 0x2d, (state->reg_2d & 0xf0));
+ saa7127_write(sd, 0x61, (state->reg_61 | 0xc0));
+ }
+ state->video_enable = enable;
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct saa7127_state *state = to_state(sd);
+ const struct i2c_reg_value *inittab;
+
+ if (std & V4L2_STD_525_60) {
+ v4l2_dbg(1, debug, sd, "Selecting 60 Hz video Standard\n");
+ inittab = saa7127_init_config_60hz;
+ state->reg_61 = SAA7127_60HZ_DAC_CONTROL;
+
+ } else if (state->ident == V4L2_IDENT_SAA7129 &&
+ (std & V4L2_STD_SECAM) &&
+ !(std & (V4L2_STD_625_50 & ~V4L2_STD_SECAM))) {
+
+ /* If and only if SECAM, with a SAA712[89] */
+ v4l2_dbg(1, debug, sd,
+ "Selecting 50 Hz SECAM video Standard\n");
+ inittab = saa7127_init_config_50hz_secam;
+ state->reg_61 = SAA7127_50HZ_SECAM_DAC_CONTROL;
+
+ } else {
+ v4l2_dbg(1, debug, sd, "Selecting 50 Hz PAL video Standard\n");
+ inittab = saa7127_init_config_50hz_pal;
+ state->reg_61 = SAA7127_50HZ_PAL_DAC_CONTROL;
+ }
+
+ /* Write Table */
+ saa7127_write_inittab(sd, inittab);
+ state->std = std;
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_output_type(struct v4l2_subdev *sd, int output)
+{
+ struct saa7127_state *state = to_state(sd);
+
+ switch (output) {
+ case SAA7127_OUTPUT_TYPE_RGB:
+ state->reg_2d = 0x0f; /* RGB + CVBS (for sync) */
+ state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */
+ break;
+
+ case SAA7127_OUTPUT_TYPE_COMPOSITE:
+ if (state->ident == V4L2_IDENT_SAA7129)
+ state->reg_2d = 0x20; /* CVBS only */
+ else
+ state->reg_2d = 0x08; /* 00001000 CVBS only, RGB DAC's off (high impedance mode) */
+ state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */
+ break;
+
+ case SAA7127_OUTPUT_TYPE_SVIDEO:
+ if (state->ident == V4L2_IDENT_SAA7129)
+ state->reg_2d = 0x18; /* Y + C */
+ else
+ state->reg_2d = 0xff; /*11111111 croma -> R, luma -> CVBS + G + B */
+ state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */
+ break;
+
+ case SAA7127_OUTPUT_TYPE_YUV_V:
+ state->reg_2d = 0x4f; /* reg 2D = 01001111, all DAC's on, RGB + VBS */
+ state->reg_3a = 0x0b; /* reg 3A = 00001011, bypass RGB-matrix */
+ break;
+
+ case SAA7127_OUTPUT_TYPE_YUV_C:
+ state->reg_2d = 0x0f; /* reg 2D = 00001111, all DAC's on, RGB + CVBS */
+ state->reg_3a = 0x0b; /* reg 3A = 00001011, bypass RGB-matrix */
+ break;
+
+ case SAA7127_OUTPUT_TYPE_BOTH:
+ if (state->ident == V4L2_IDENT_SAA7129)
+ state->reg_2d = 0x38;
+ else
+ state->reg_2d = 0xbf;
+ state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ v4l2_dbg(1, debug, sd,
+ "Selecting %s output type\n", output_strs[output]);
+
+ /* Configure Encoder */
+ saa7127_write(sd, 0x2d, state->reg_2d);
+ saa7127_write(sd, 0x3a, state->reg_3a | state->reg_3a_cb);
+ state->output_type = output;
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_input_type(struct v4l2_subdev *sd, int input)
+{
+ struct saa7127_state *state = to_state(sd);
+
+ switch (input) {
+ case SAA7127_INPUT_TYPE_NORMAL: /* avia */
+ v4l2_dbg(1, debug, sd, "Selecting Normal Encoder Input\n");
+ state->reg_3a_cb = 0;
+ break;
+
+ case SAA7127_INPUT_TYPE_TEST_IMAGE: /* color bar */
+ v4l2_dbg(1, debug, sd, "Selecting Color Bar generator\n");
+ state->reg_3a_cb = 0x80;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ saa7127_write(sd, 0x3a, state->reg_3a | state->reg_3a_cb);
+ state->input_type = input;
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct saa7127_state *state = to_state(sd);
+
+ if (state->std == std)
+ return 0;
+ return saa7127_set_std(sd, std);
+}
+
+static int saa7127_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct saa7127_state *state = to_state(sd);
+ int rc = 0;
+
+ if (state->input_type != input)
+ rc = saa7127_set_input_type(sd, input);
+ if (rc == 0 && state->output_type != output)
+ rc = saa7127_set_output_type(sd, output);
+ return rc;
+}
+
+static int saa7127_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct saa7127_state *state = to_state(sd);
+
+ if (state->video_enable == enable)
+ return 0;
+ return saa7127_set_video_enable(sd, enable);
+}
+
+static int saa7127_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt)
+{
+ struct saa7127_state *state = to_state(sd);
+
+ memset(fmt, 0, sizeof(*fmt));
+ if (state->vps_enable)
+ fmt->service_lines[0][16] = V4L2_SLICED_VPS;
+ if (state->wss_enable)
+ fmt->service_lines[0][23] = V4L2_SLICED_WSS_625;
+ if (state->cc_enable) {
+ fmt->service_lines[0][21] = V4L2_SLICED_CAPTION_525;
+ fmt->service_lines[1][21] = V4L2_SLICED_CAPTION_525;
+ }
+ fmt->service_set =
+ (state->vps_enable ? V4L2_SLICED_VPS : 0) |
+ (state->wss_enable ? V4L2_SLICED_WSS_625 : 0) |
+ (state->cc_enable ? V4L2_SLICED_CAPTION_525 : 0);
+ return 0;
+}
+
+static int saa7127_s_vbi_data(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
+{
+ switch (data->id) {
+ case V4L2_SLICED_WSS_625:
+ return saa7127_set_wss(sd, data);
+ case V4L2_SLICED_VPS:
+ return saa7127_set_vps(sd, data);
+ case V4L2_SLICED_CAPTION_525:
+ if (data->field == 0)
+ return saa7127_set_cc(sd, data);
+ return saa7127_set_xds(sd, data);
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int saa7127_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = saa7127_read(sd, reg->reg & 0xff);
+ reg->size = 1;
+ return 0;
+}
+
+static int saa7127_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ saa7127_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ return 0;
+}
+#endif
+
+static int saa7127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct saa7127_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0);
+}
+
+static int saa7127_log_status(struct v4l2_subdev *sd)
+{
+ struct saa7127_state *state = to_state(sd);
+
+ v4l2_info(sd, "Standard: %s\n", (state->std & V4L2_STD_525_60) ? "60 Hz" : "50 Hz");
+ v4l2_info(sd, "Input: %s\n", state->input_type ? "color bars" : "normal");
+ v4l2_info(sd, "Output: %s\n", state->video_enable ?
+ output_strs[state->output_type] : "disabled");
+ v4l2_info(sd, "WSS: %s\n", state->wss_enable ?
+ wss_strs[state->wss_mode] : "disabled");
+ v4l2_info(sd, "VPS: %s\n", state->vps_enable ? "enabled" : "disabled");
+ v4l2_info(sd, "CC: %s\n", state->cc_enable ? "enabled" : "disabled");
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops saa7127_core_ops = {
+ .log_status = saa7127_log_status,
+ .g_chip_ident = saa7127_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = saa7127_g_register,
+ .s_register = saa7127_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops saa7127_video_ops = {
+ .s_std_output = saa7127_s_std_output,
+ .s_routing = saa7127_s_routing,
+ .s_stream = saa7127_s_stream,
+};
+
+static const struct v4l2_subdev_vbi_ops saa7127_vbi_ops = {
+ .s_vbi_data = saa7127_s_vbi_data,
+ .g_sliced_fmt = saa7127_g_sliced_fmt,
+};
+
+static const struct v4l2_subdev_ops saa7127_ops = {
+ .core = &saa7127_core_ops,
+ .video = &saa7127_video_ops,
+ .vbi = &saa7127_vbi_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct saa7127_state *state;
+ struct v4l2_subdev *sd;
+ struct v4l2_sliced_vbi_data vbi = { 0, 0, 0, 0 }; /* set to disabled */
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ v4l_dbg(1, debug, client, "detecting saa7127 client on address 0x%x\n",
+ client->addr << 1);
+
+ state = kzalloc(sizeof(struct saa7127_state), GFP_KERNEL);
+ if (state == NULL)
+ return -ENOMEM;
+
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &saa7127_ops);
+
+ /* First test register 0: Bits 5-7 are a version ID (should be 0),
+ and bit 2 should also be 0.
+ This is rather general, so the second test is more specific and
+ looks at the 'ending point of burst in clock cycles' which is
+ 0x1d after a reset and not expected to ever change. */
+ if ((saa7127_read(sd, 0) & 0xe4) != 0 ||
+ (saa7127_read(sd, 0x29) & 0x3f) != 0x1d) {
+ v4l2_dbg(1, debug, sd, "saa7127 not found\n");
+ kfree(state);
+ return -ENODEV;
+ }
+
+ if (id->driver_data) { /* Chip type is already known */
+ state->ident = id->driver_data;
+ } else { /* Needs detection */
+ int read_result;
+
+ /* Detect if it's an saa7129 */
+ read_result = saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2);
+ saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2, 0xaa);
+ if (saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2) == 0xaa) {
+ saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2,
+ read_result);
+ state->ident = V4L2_IDENT_SAA7129;
+ strlcpy(client->name, "saa7129", I2C_NAME_SIZE);
+ } else {
+ state->ident = V4L2_IDENT_SAA7127;
+ strlcpy(client->name, "saa7127", I2C_NAME_SIZE);
+ }
+ }
+
+ v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name,
+ client->addr << 1, client->adapter->name);
+
+ v4l2_dbg(1, debug, sd, "Configuring encoder\n");
+ saa7127_write_inittab(sd, saa7127_init_config_common);
+ saa7127_set_std(sd, V4L2_STD_NTSC);
+ saa7127_set_output_type(sd, SAA7127_OUTPUT_TYPE_BOTH);
+ saa7127_set_vps(sd, &vbi);
+ saa7127_set_wss(sd, &vbi);
+ saa7127_set_cc(sd, &vbi);
+ saa7127_set_xds(sd, &vbi);
+ if (test_image == 1)
+ /* The Encoder has an internal Colorbar generator */
+ /* This can be used for debugging */
+ saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_TEST_IMAGE);
+ else
+ saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_NORMAL);
+ saa7127_set_video_enable(sd, 1);
+
+ if (state->ident == V4L2_IDENT_SAA7129)
+ saa7127_write_inittab(sd, saa7129_init_config_extra);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ /* Turn off TV output */
+ saa7127_set_video_enable(sd, 0);
+ kfree(to_state(sd));
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct i2c_device_id saa7127_id[] = {
+ { "saa7127_auto", 0 }, /* auto-detection */
+ { "saa7126", V4L2_IDENT_SAA7127 },
+ { "saa7127", V4L2_IDENT_SAA7127 },
+ { "saa7128", V4L2_IDENT_SAA7129 },
+ { "saa7129", V4L2_IDENT_SAA7129 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, saa7127_id);
+
+static struct i2c_driver saa7127_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "saa7127",
+ },
+ .probe = saa7127_probe,
+ .remove = saa7127_remove,
+ .id_table = saa7127_id,
+};
+
+module_i2c_driver(saa7127_driver);
diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig
new file mode 100644
index 00000000..39fc0187
--- /dev/null
+++ b/drivers/media/video/saa7134/Kconfig
@@ -0,0 +1,64 @@
+config VIDEO_SAA7134
+ tristate "Philips SAA7134 support"
+ depends on VIDEO_DEV && PCI && I2C
+ select VIDEOBUF_DMA_SG
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ select CRC32
+ select VIDEO_SAA6588 if VIDEO_HELPER_CHIPS_AUTO
+ ---help---
+ This is a video4linux driver for Philips SAA713x based
+ TV cards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7134.
+
+config VIDEO_SAA7134_ALSA
+ tristate "Philips SAA7134 DMA audio support"
+ depends on VIDEO_SAA7134 && SND
+ select SND_PCM
+ ---help---
+ This is a video4linux driver for direct (DMA) audio in
+ Philips SAA713x based TV cards using ALSA
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7134-alsa.
+
+config VIDEO_SAA7134_RC
+ bool "Philips SAA7134 Remote Controller support"
+ depends on RC_CORE
+ depends on VIDEO_SAA7134
+ depends on !(RC_CORE=m && VIDEO_SAA7134=y)
+ default y
+ ---help---
+ Enables Remote Controller support on saa7134 driver.
+
+config VIDEO_SAA7134_DVB
+ tristate "DVB/ATSC Support for saa7134 based TV cards"
+ depends on VIDEO_SAA7134 && DVB_CORE
+ select VIDEOBUF_DVB
+ select DVB_PLL if !DVB_FE_CUSTOMISE
+ select DVB_MT352 if !DVB_FE_CUSTOMISE
+ select DVB_TDA1004X if !DVB_FE_CUSTOMISE
+ select DVB_NXT200X if !DVB_FE_CUSTOMISE
+ select DVB_TDA10086 if !DVB_FE_CUSTOMISE
+ select DVB_TDA826X if !DVB_FE_CUSTOMISE
+ select DVB_ISL6421 if !DVB_FE_CUSTOMISE
+ select DVB_ISL6405 if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMISE
+ select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE
+ select DVB_ZL10036 if !DVB_FE_CUSTOMISE
+ select DVB_MT312 if !DVB_FE_CUSTOMISE
+ select DVB_LNBP21 if !DVB_FE_CUSTOMISE
+ select DVB_ZL10353 if !DVB_FE_CUSTOMISE
+ select DVB_LGDT3305 if !DVB_FE_CUSTOMISE
+ select DVB_TDA10048 if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE
+ select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE
+ select DVB_ZL10039 if !DVB_FE_CUSTOMISE
+ ---help---
+ This adds support for DVB cards based on the
+ Philips saa7134 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7134-dvb.
diff --git a/drivers/media/video/saa7134/Makefile b/drivers/media/video/saa7134/Makefile
new file mode 100644
index 00000000..da389932
--- /dev/null
+++ b/drivers/media/video/saa7134/Makefile
@@ -0,0 +1,16 @@
+
+saa7134-y := saa7134-cards.o saa7134-core.o saa7134-i2c.o
+saa7134-y += saa7134-ts.o saa7134-tvaudio.o saa7134-vbi.o
+saa7134-y += saa7134-video.o
+saa7134-$(CONFIG_VIDEO_SAA7134_RC) += saa7134-input.o
+
+obj-$(CONFIG_VIDEO_SAA7134) += saa6752hs.o saa7134.o saa7134-empress.o
+
+obj-$(CONFIG_VIDEO_SAA7134_ALSA) += saa7134-alsa.o
+
+obj-$(CONFIG_VIDEO_SAA7134_DVB) += saa7134-dvb.o
+
+ccflags-y += -I$(srctree)/drivers/media/video
+ccflags-y += -I$(srctree)/drivers/media/common/tuners
+ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
+ccflags-y += -I$(srctree)/drivers/media/dvb/frontends
diff --git a/drivers/media/video/saa7134/saa6752hs.c b/drivers/media/video/saa7134/saa6752hs.c
new file mode 100644
index 00000000..f147b05b
--- /dev/null
+++ b/drivers/media/video/saa7134/saa6752hs.c
@@ -0,0 +1,1012 @@
+ /*
+ saa6752hs - i2c-driver for the saa6752hs by Philips
+
+ Copyright (C) 2004 Andrew de Quincey
+
+ AC-3 support:
+
+ Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License vs published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mvss Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/i2c.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
+#include <linux/init.h>
+#include <linux/crc32.h>
+
+#define MPEG_VIDEO_TARGET_BITRATE_MAX 27000
+#define MPEG_VIDEO_MAX_BITRATE_MAX 27000
+#define MPEG_TOTAL_TARGET_BITRATE_MAX 27000
+#define MPEG_PID_MAX ((1 << 14) - 1)
+
+
+MODULE_DESCRIPTION("device driver for saa6752hs MPEG2 encoder");
+MODULE_AUTHOR("Andrew de Quincey");
+MODULE_LICENSE("GPL");
+
+enum saa6752hs_videoformat {
+ SAA6752HS_VF_D1 = 0, /* standard D1 video format: 720x576 */
+ SAA6752HS_VF_2_3_D1 = 1,/* 2/3D1 video format: 480x576 */
+ SAA6752HS_VF_1_2_D1 = 2,/* 1/2D1 video format: 352x576 */
+ SAA6752HS_VF_SIF = 3, /* SIF video format: 352x288 */
+ SAA6752HS_VF_UNKNOWN,
+};
+
+struct saa6752hs_mpeg_params {
+ /* transport streams */
+ __u16 ts_pid_pmt;
+ __u16 ts_pid_audio;
+ __u16 ts_pid_video;
+ __u16 ts_pid_pcr;
+
+ /* audio */
+ enum v4l2_mpeg_audio_encoding au_encoding;
+ enum v4l2_mpeg_audio_l2_bitrate au_l2_bitrate;
+ enum v4l2_mpeg_audio_ac3_bitrate au_ac3_bitrate;
+
+ /* video */
+ enum v4l2_mpeg_video_aspect vi_aspect;
+ enum v4l2_mpeg_video_bitrate_mode vi_bitrate_mode;
+ __u32 vi_bitrate;
+ __u32 vi_bitrate_peak;
+};
+
+static const struct v4l2_format v4l2_format_table[] =
+{
+ [SAA6752HS_VF_D1] =
+ { .fmt = { .pix = { .width = 720, .height = 576 }}},
+ [SAA6752HS_VF_2_3_D1] =
+ { .fmt = { .pix = { .width = 480, .height = 576 }}},
+ [SAA6752HS_VF_1_2_D1] =
+ { .fmt = { .pix = { .width = 352, .height = 576 }}},
+ [SAA6752HS_VF_SIF] =
+ { .fmt = { .pix = { .width = 352, .height = 288 }}},
+ [SAA6752HS_VF_UNKNOWN] =
+ { .fmt = { .pix = { .width = 0, .height = 0}}},
+};
+
+struct saa6752hs_state {
+ struct v4l2_subdev sd;
+ int chip;
+ u32 revision;
+ int has_ac3;
+ struct saa6752hs_mpeg_params params;
+ enum saa6752hs_videoformat video_format;
+ v4l2_std_id standard;
+};
+
+enum saa6752hs_command {
+ SAA6752HS_COMMAND_RESET = 0,
+ SAA6752HS_COMMAND_STOP = 1,
+ SAA6752HS_COMMAND_START = 2,
+ SAA6752HS_COMMAND_PAUSE = 3,
+ SAA6752HS_COMMAND_RECONFIGURE = 4,
+ SAA6752HS_COMMAND_SLEEP = 5,
+ SAA6752HS_COMMAND_RECONFIGURE_FORCE = 6,
+
+ SAA6752HS_COMMAND_MAX
+};
+
+static inline struct saa6752hs_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct saa6752hs_state, sd);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static u8 PAT[] = {
+ 0xc2, /* i2c register */
+ 0x00, /* table number for encoder */
+
+ 0x47, /* sync */
+ 0x40, 0x00, /* transport_error_indicator(0), payload_unit_start(1), transport_priority(0), pid(0) */
+ 0x10, /* transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0) */
+
+ 0x00, /* PSI pointer to start of table */
+
+ 0x00, /* tid(0) */
+ 0xb0, 0x0d, /* section_syntax_indicator(1), section_length(13) */
+
+ 0x00, 0x01, /* transport_stream_id(1) */
+
+ 0xc1, /* version_number(0), current_next_indicator(1) */
+
+ 0x00, 0x00, /* section_number(0), last_section_number(0) */
+
+ 0x00, 0x01, /* program_number(1) */
+
+ 0xe0, 0x00, /* PMT PID */
+
+ 0x00, 0x00, 0x00, 0x00 /* CRC32 */
+};
+
+static u8 PMT[] = {
+ 0xc2, /* i2c register */
+ 0x01, /* table number for encoder */
+
+ 0x47, /* sync */
+ 0x40, 0x00, /* transport_error_indicator(0), payload_unit_start(1), transport_priority(0), pid */
+ 0x10, /* transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0) */
+
+ 0x00, /* PSI pointer to start of table */
+
+ 0x02, /* tid(2) */
+ 0xb0, 0x17, /* section_syntax_indicator(1), section_length(23) */
+
+ 0x00, 0x01, /* program_number(1) */
+
+ 0xc1, /* version_number(0), current_next_indicator(1) */
+
+ 0x00, 0x00, /* section_number(0), last_section_number(0) */
+
+ 0xe0, 0x00, /* PCR_PID */
+
+ 0xf0, 0x00, /* program_info_length(0) */
+
+ 0x02, 0xe0, 0x00, 0xf0, 0x00, /* video stream type(2), pid */
+ 0x04, 0xe0, 0x00, 0xf0, 0x00, /* audio stream type(4), pid */
+
+ 0x00, 0x00, 0x00, 0x00 /* CRC32 */
+};
+
+static u8 PMT_AC3[] = {
+ 0xc2, /* i2c register */
+ 0x01, /* table number for encoder(1) */
+ 0x47, /* sync */
+
+ 0x40, /* transport_error_indicator(0), payload_unit_start(1), transport_priority(0) */
+ 0x10, /* PMT PID (0x0010) */
+ 0x10, /* transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0) */
+
+ 0x00, /* PSI pointer to start of table */
+
+ 0x02, /* TID (2) */
+ 0xb0, 0x1a, /* section_syntax_indicator(1), section_length(26) */
+
+ 0x00, 0x01, /* program_number(1) */
+
+ 0xc1, /* version_number(0), current_next_indicator(1) */
+
+ 0x00, 0x00, /* section_number(0), last_section_number(0) */
+
+ 0xe1, 0x04, /* PCR_PID (0x0104) */
+
+ 0xf0, 0x00, /* program_info_length(0) */
+
+ 0x02, 0xe1, 0x00, 0xf0, 0x00, /* video stream type(2), pid */
+ 0x06, 0xe1, 0x03, 0xf0, 0x03, /* audio stream type(6), pid */
+ 0x6a, /* AC3 */
+ 0x01, /* Descriptor_length(1) */
+ 0x00, /* component_type_flag(0), bsid_flag(0), mainid_flag(0), asvc_flag(0), reserved flags(0) */
+
+ 0xED, 0xDE, 0x2D, 0xF3 /* CRC32 BE */
+};
+
+static struct saa6752hs_mpeg_params param_defaults =
+{
+ .ts_pid_pmt = 16,
+ .ts_pid_video = 260,
+ .ts_pid_audio = 256,
+ .ts_pid_pcr = 259,
+
+ .vi_aspect = V4L2_MPEG_VIDEO_ASPECT_4x3,
+ .vi_bitrate = 4000,
+ .vi_bitrate_peak = 6000,
+ .vi_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
+
+ .au_encoding = V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
+ .au_l2_bitrate = V4L2_MPEG_AUDIO_L2_BITRATE_256K,
+ .au_ac3_bitrate = V4L2_MPEG_AUDIO_AC3_BITRATE_256K,
+};
+
+/* ---------------------------------------------------------------------- */
+
+static int saa6752hs_chip_command(struct i2c_client *client,
+ enum saa6752hs_command command)
+{
+ unsigned char buf[3];
+ unsigned long timeout;
+ int status = 0;
+
+ /* execute the command */
+ switch(command) {
+ case SAA6752HS_COMMAND_RESET:
+ buf[0] = 0x00;
+ break;
+
+ case SAA6752HS_COMMAND_STOP:
+ buf[0] = 0x03;
+ break;
+
+ case SAA6752HS_COMMAND_START:
+ buf[0] = 0x02;
+ break;
+
+ case SAA6752HS_COMMAND_PAUSE:
+ buf[0] = 0x04;
+ break;
+
+ case SAA6752HS_COMMAND_RECONFIGURE:
+ buf[0] = 0x05;
+ break;
+
+ case SAA6752HS_COMMAND_SLEEP:
+ buf[0] = 0x06;
+ break;
+
+ case SAA6752HS_COMMAND_RECONFIGURE_FORCE:
+ buf[0] = 0x07;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* set it and wait for it to be so */
+ i2c_master_send(client, buf, 1);
+ timeout = jiffies + HZ * 3;
+ for (;;) {
+ /* get the current status */
+ buf[0] = 0x10;
+ i2c_master_send(client, buf, 1);
+ i2c_master_recv(client, buf, 1);
+
+ if (!(buf[0] & 0x20))
+ break;
+ if (time_after(jiffies,timeout)) {
+ status = -ETIMEDOUT;
+ break;
+ }
+
+ msleep(10);
+ }
+
+ /* delay a bit to let encoder settle */
+ msleep(50);
+
+ return status;
+}
+
+
+static inline void set_reg8(struct i2c_client *client, uint8_t reg, uint8_t val)
+{
+ u8 buf[2];
+
+ buf[0] = reg;
+ buf[1] = val;
+ i2c_master_send(client, buf, 2);
+}
+
+static inline void set_reg16(struct i2c_client *client, uint8_t reg, uint16_t val)
+{
+ u8 buf[3];
+
+ buf[0] = reg;
+ buf[1] = val >> 8;
+ buf[2] = val & 0xff;
+ i2c_master_send(client, buf, 3);
+}
+
+static int saa6752hs_set_bitrate(struct i2c_client *client,
+ struct saa6752hs_state *h)
+{
+ struct saa6752hs_mpeg_params *params = &h->params;
+ int tot_bitrate;
+ int is_384k;
+
+ /* set the bitrate mode */
+ set_reg8(client, 0x71,
+ params->vi_bitrate_mode != V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
+
+ /* set the video bitrate */
+ if (params->vi_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) {
+ /* set the target bitrate */
+ set_reg16(client, 0x80, params->vi_bitrate);
+
+ /* set the max bitrate */
+ set_reg16(client, 0x81, params->vi_bitrate_peak);
+ tot_bitrate = params->vi_bitrate_peak;
+ } else {
+ /* set the target bitrate (no max bitrate for CBR) */
+ set_reg16(client, 0x81, params->vi_bitrate);
+ tot_bitrate = params->vi_bitrate;
+ }
+
+ /* set the audio encoding */
+ set_reg8(client, 0x93,
+ params->au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3);
+
+ /* set the audio bitrate */
+ if (params->au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3)
+ is_384k = V4L2_MPEG_AUDIO_AC3_BITRATE_384K == params->au_ac3_bitrate;
+ else
+ is_384k = V4L2_MPEG_AUDIO_L2_BITRATE_384K == params->au_l2_bitrate;
+ set_reg8(client, 0x94, is_384k);
+ tot_bitrate += is_384k ? 384 : 256;
+
+ /* Note: the total max bitrate is determined by adding the video and audio
+ bitrates together and also adding an extra 768kbit/s to stay on the
+ safe side. If more control should be required, then an extra MPEG control
+ should be added. */
+ tot_bitrate += 768;
+ if (tot_bitrate > MPEG_TOTAL_TARGET_BITRATE_MAX)
+ tot_bitrate = MPEG_TOTAL_TARGET_BITRATE_MAX;
+
+ /* set the total bitrate */
+ set_reg16(client, 0xb1, tot_bitrate);
+ return 0;
+}
+
+
+static int get_ctrl(int has_ac3, struct saa6752hs_mpeg_params *params,
+ struct v4l2_ext_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG2_TS;
+ break;
+ case V4L2_CID_MPEG_STREAM_PID_PMT:
+ ctrl->value = params->ts_pid_pmt;
+ break;
+ case V4L2_CID_MPEG_STREAM_PID_AUDIO:
+ ctrl->value = params->ts_pid_audio;
+ break;
+ case V4L2_CID_MPEG_STREAM_PID_VIDEO:
+ ctrl->value = params->ts_pid_video;
+ break;
+ case V4L2_CID_MPEG_STREAM_PID_PCR:
+ ctrl->value = params->ts_pid_pcr;
+ break;
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ ctrl->value = params->au_encoding;
+ break;
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+ ctrl->value = params->au_l2_bitrate;
+ break;
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+ if (!has_ac3)
+ return -EINVAL;
+ ctrl->value = params->au_ac3_bitrate;
+ break;
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+ ctrl->value = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000;
+ break;
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_2;
+ break;
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ ctrl->value = params->vi_aspect;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ ctrl->value = params->vi_bitrate * 1000;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+ ctrl->value = params->vi_bitrate_peak * 1000;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ ctrl->value = params->vi_bitrate_mode;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int handle_ctrl(int has_ac3, struct saa6752hs_mpeg_params *params,
+ struct v4l2_ext_control *ctrl, int set)
+{
+ int old = 0, new;
+
+ new = ctrl->value;
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ old = V4L2_MPEG_STREAM_TYPE_MPEG2_TS;
+ if (set && new != old)
+ return -ERANGE;
+ new = old;
+ break;
+ case V4L2_CID_MPEG_STREAM_PID_PMT:
+ old = params->ts_pid_pmt;
+ if (set && new > MPEG_PID_MAX)
+ return -ERANGE;
+ if (new > MPEG_PID_MAX)
+ new = MPEG_PID_MAX;
+ params->ts_pid_pmt = new;
+ break;
+ case V4L2_CID_MPEG_STREAM_PID_AUDIO:
+ old = params->ts_pid_audio;
+ if (set && new > MPEG_PID_MAX)
+ return -ERANGE;
+ if (new > MPEG_PID_MAX)
+ new = MPEG_PID_MAX;
+ params->ts_pid_audio = new;
+ break;
+ case V4L2_CID_MPEG_STREAM_PID_VIDEO:
+ old = params->ts_pid_video;
+ if (set && new > MPEG_PID_MAX)
+ return -ERANGE;
+ if (new > MPEG_PID_MAX)
+ new = MPEG_PID_MAX;
+ params->ts_pid_video = new;
+ break;
+ case V4L2_CID_MPEG_STREAM_PID_PCR:
+ old = params->ts_pid_pcr;
+ if (set && new > MPEG_PID_MAX)
+ return -ERANGE;
+ if (new > MPEG_PID_MAX)
+ new = MPEG_PID_MAX;
+ params->ts_pid_pcr = new;
+ break;
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ old = params->au_encoding;
+ if (set && new != V4L2_MPEG_AUDIO_ENCODING_LAYER_2 &&
+ (!has_ac3 || new != V4L2_MPEG_AUDIO_ENCODING_AC3))
+ return -ERANGE;
+ params->au_encoding = new;
+ break;
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+ old = params->au_l2_bitrate;
+ if (set && new != V4L2_MPEG_AUDIO_L2_BITRATE_256K &&
+ new != V4L2_MPEG_AUDIO_L2_BITRATE_384K)
+ return -ERANGE;
+ if (new <= V4L2_MPEG_AUDIO_L2_BITRATE_256K)
+ new = V4L2_MPEG_AUDIO_L2_BITRATE_256K;
+ else
+ new = V4L2_MPEG_AUDIO_L2_BITRATE_384K;
+ params->au_l2_bitrate = new;
+ break;
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+ if (!has_ac3)
+ return -EINVAL;
+ old = params->au_ac3_bitrate;
+ if (set && new != V4L2_MPEG_AUDIO_AC3_BITRATE_256K &&
+ new != V4L2_MPEG_AUDIO_AC3_BITRATE_384K)
+ return -ERANGE;
+ if (new <= V4L2_MPEG_AUDIO_AC3_BITRATE_256K)
+ new = V4L2_MPEG_AUDIO_AC3_BITRATE_256K;
+ else
+ new = V4L2_MPEG_AUDIO_AC3_BITRATE_384K;
+ params->au_ac3_bitrate = new;
+ break;
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+ old = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000;
+ if (set && new != old)
+ return -ERANGE;
+ new = old;
+ break;
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ old = V4L2_MPEG_VIDEO_ENCODING_MPEG_2;
+ if (set && new != old)
+ return -ERANGE;
+ new = old;
+ break;
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ old = params->vi_aspect;
+ if (set && new != V4L2_MPEG_VIDEO_ASPECT_16x9 &&
+ new != V4L2_MPEG_VIDEO_ASPECT_4x3)
+ return -ERANGE;
+ if (new != V4L2_MPEG_VIDEO_ASPECT_16x9)
+ new = V4L2_MPEG_VIDEO_ASPECT_4x3;
+ params->vi_aspect = new;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ old = params->vi_bitrate * 1000;
+ new = 1000 * (new / 1000);
+ if (set && new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000)
+ return -ERANGE;
+ if (new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000)
+ new = MPEG_VIDEO_TARGET_BITRATE_MAX * 1000;
+ params->vi_bitrate = new / 1000;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+ old = params->vi_bitrate_peak * 1000;
+ new = 1000 * (new / 1000);
+ if (set && new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000)
+ return -ERANGE;
+ if (new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000)
+ new = MPEG_VIDEO_TARGET_BITRATE_MAX * 1000;
+ params->vi_bitrate_peak = new / 1000;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ old = params->vi_bitrate_mode;
+ params->vi_bitrate_mode = new;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ctrl->value = new;
+ return 0;
+}
+
+
+static int saa6752hs_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qctrl)
+{
+ struct saa6752hs_state *h = to_state(sd);
+ struct saa6752hs_mpeg_params *params = &h->params;
+ int err;
+
+ switch (qctrl->id) {
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
+ h->has_ac3 ? V4L2_MPEG_AUDIO_ENCODING_AC3 :
+ V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
+ 1, V4L2_MPEG_AUDIO_ENCODING_LAYER_2);
+
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_AUDIO_L2_BITRATE_256K,
+ V4L2_MPEG_AUDIO_L2_BITRATE_384K, 1,
+ V4L2_MPEG_AUDIO_L2_BITRATE_256K);
+
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+ if (!h->has_ac3)
+ return -EINVAL;
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_256K,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_384K, 1,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_256K);
+
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000,
+ V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, 1,
+ V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000);
+
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 1,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2);
+
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_VIDEO_ASPECT_4x3,
+ V4L2_MPEG_VIDEO_ASPECT_16x9, 1,
+ V4L2_MPEG_VIDEO_ASPECT_4x3);
+
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+ err = v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 8000000);
+ if (err == 0 &&
+ params->vi_bitrate_mode ==
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return err;
+
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_STREAM_TYPE_MPEG2_TS,
+ V4L2_MPEG_STREAM_TYPE_MPEG2_TS, 1,
+ V4L2_MPEG_STREAM_TYPE_MPEG2_TS);
+
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 6000000);
+ case V4L2_CID_MPEG_STREAM_PID_PMT:
+ return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 16);
+ case V4L2_CID_MPEG_STREAM_PID_AUDIO:
+ return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 260);
+ case V4L2_CID_MPEG_STREAM_PID_VIDEO:
+ return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 256);
+ case V4L2_CID_MPEG_STREAM_PID_PCR:
+ return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 259);
+
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static int saa6752hs_querymenu(struct v4l2_subdev *sd, struct v4l2_querymenu *qmenu)
+{
+ static const u32 mpeg_audio_encoding[] = {
+ V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
+ V4L2_CTRL_MENU_IDS_END
+ };
+ static const u32 mpeg_audio_ac3_encoding[] = {
+ V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
+ V4L2_MPEG_AUDIO_ENCODING_AC3,
+ V4L2_CTRL_MENU_IDS_END
+ };
+ static u32 mpeg_audio_l2_bitrate[] = {
+ V4L2_MPEG_AUDIO_L2_BITRATE_256K,
+ V4L2_MPEG_AUDIO_L2_BITRATE_384K,
+ V4L2_CTRL_MENU_IDS_END
+ };
+ static u32 mpeg_audio_ac3_bitrate[] = {
+ V4L2_MPEG_AUDIO_AC3_BITRATE_256K,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_384K,
+ V4L2_CTRL_MENU_IDS_END
+ };
+ struct saa6752hs_state *h = to_state(sd);
+ struct v4l2_queryctrl qctrl;
+ int err;
+
+ qctrl.id = qmenu->id;
+ err = saa6752hs_queryctrl(sd, &qctrl);
+ if (err)
+ return err;
+ switch (qmenu->id) {
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+ return v4l2_ctrl_query_menu_valid_items(qmenu,
+ mpeg_audio_l2_bitrate);
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+ if (!h->has_ac3)
+ return -EINVAL;
+ return v4l2_ctrl_query_menu_valid_items(qmenu,
+ mpeg_audio_ac3_bitrate);
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ return v4l2_ctrl_query_menu_valid_items(qmenu,
+ h->has_ac3 ? mpeg_audio_ac3_encoding :
+ mpeg_audio_encoding);
+ }
+ return v4l2_ctrl_query_menu(qmenu, &qctrl, NULL);
+}
+
+static int saa6752hs_init(struct v4l2_subdev *sd, u32 leading_null_bytes)
+{
+ unsigned char buf[9], buf2[4];
+ struct saa6752hs_state *h = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ unsigned size;
+ u32 crc;
+ unsigned char localPAT[256];
+ unsigned char localPMT[256];
+
+ /* Set video format - must be done first as it resets other settings */
+ set_reg8(client, 0x41, h->video_format);
+
+ /* Set number of lines in input signal */
+ set_reg8(client, 0x40, (h->standard & V4L2_STD_525_60) ? 1 : 0);
+
+ /* set bitrate */
+ saa6752hs_set_bitrate(client, h);
+
+ /* Set GOP structure {3, 13} */
+ set_reg16(client, 0x72, 0x030d);
+
+ /* Set minimum Q-scale {4} */
+ set_reg8(client, 0x82, 0x04);
+
+ /* Set maximum Q-scale {12} */
+ set_reg8(client, 0x83, 0x0c);
+
+ /* Set Output Protocol */
+ set_reg8(client, 0xd0, 0x81);
+
+ /* Set video output stream format {TS} */
+ set_reg8(client, 0xb0, 0x05);
+
+ /* Set leading null byte for TS */
+ set_reg16(client, 0xf6, leading_null_bytes);
+
+ /* compute PAT */
+ memcpy(localPAT, PAT, sizeof(PAT));
+ localPAT[17] = 0xe0 | ((h->params.ts_pid_pmt >> 8) & 0x0f);
+ localPAT[18] = h->params.ts_pid_pmt & 0xff;
+ crc = crc32_be(~0, &localPAT[7], sizeof(PAT) - 7 - 4);
+ localPAT[sizeof(PAT) - 4] = (crc >> 24) & 0xFF;
+ localPAT[sizeof(PAT) - 3] = (crc >> 16) & 0xFF;
+ localPAT[sizeof(PAT) - 2] = (crc >> 8) & 0xFF;
+ localPAT[sizeof(PAT) - 1] = crc & 0xFF;
+
+ /* compute PMT */
+ if (h->params.au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3) {
+ size = sizeof(PMT_AC3);
+ memcpy(localPMT, PMT_AC3, size);
+ } else {
+ size = sizeof(PMT);
+ memcpy(localPMT, PMT, size);
+ }
+ localPMT[3] = 0x40 | ((h->params.ts_pid_pmt >> 8) & 0x0f);
+ localPMT[4] = h->params.ts_pid_pmt & 0xff;
+ localPMT[15] = 0xE0 | ((h->params.ts_pid_pcr >> 8) & 0x0F);
+ localPMT[16] = h->params.ts_pid_pcr & 0xFF;
+ localPMT[20] = 0xE0 | ((h->params.ts_pid_video >> 8) & 0x0F);
+ localPMT[21] = h->params.ts_pid_video & 0xFF;
+ localPMT[25] = 0xE0 | ((h->params.ts_pid_audio >> 8) & 0x0F);
+ localPMT[26] = h->params.ts_pid_audio & 0xFF;
+ crc = crc32_be(~0, &localPMT[7], size - 7 - 4);
+ localPMT[size - 4] = (crc >> 24) & 0xFF;
+ localPMT[size - 3] = (crc >> 16) & 0xFF;
+ localPMT[size - 2] = (crc >> 8) & 0xFF;
+ localPMT[size - 1] = crc & 0xFF;
+
+ /* Set Audio PID */
+ set_reg16(client, 0xc1, h->params.ts_pid_audio);
+
+ /* Set Video PID */
+ set_reg16(client, 0xc0, h->params.ts_pid_video);
+
+ /* Set PCR PID */
+ set_reg16(client, 0xc4, h->params.ts_pid_pcr);
+
+ /* Send SI tables */
+ i2c_master_send(client, localPAT, sizeof(PAT));
+ i2c_master_send(client, localPMT, size);
+
+ /* mute then unmute audio. This removes buzzing artefacts */
+ set_reg8(client, 0xa4, 1);
+ set_reg8(client, 0xa4, 0);
+
+ /* start it going */
+ saa6752hs_chip_command(client, SAA6752HS_COMMAND_START);
+
+ /* readout current state */
+ buf[0] = 0xE1;
+ buf[1] = 0xA7;
+ buf[2] = 0xFE;
+ buf[3] = 0x82;
+ buf[4] = 0xB0;
+ i2c_master_send(client, buf, 5);
+ i2c_master_recv(client, buf2, 4);
+
+ /* change aspect ratio */
+ buf[0] = 0xE0;
+ buf[1] = 0xA7;
+ buf[2] = 0xFE;
+ buf[3] = 0x82;
+ buf[4] = 0xB0;
+ buf[5] = buf2[0];
+ switch (h->params.vi_aspect) {
+ case V4L2_MPEG_VIDEO_ASPECT_16x9:
+ buf[6] = buf2[1] | 0x40;
+ break;
+ case V4L2_MPEG_VIDEO_ASPECT_4x3:
+ default:
+ buf[6] = buf2[1] & 0xBF;
+ break;
+ }
+ buf[7] = buf2[2];
+ buf[8] = buf2[3];
+ i2c_master_send(client, buf, 9);
+
+ return 0;
+}
+
+static int saa6752hs_do_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls, int set)
+{
+ struct saa6752hs_state *h = to_state(sd);
+ struct saa6752hs_mpeg_params params;
+ int i;
+
+ if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+ return -EINVAL;
+
+ params = h->params;
+ for (i = 0; i < ctrls->count; i++) {
+ int err = handle_ctrl(h->has_ac3, &params, ctrls->controls + i, set);
+
+ if (err) {
+ ctrls->error_idx = i;
+ return err;
+ }
+ }
+ if (set)
+ h->params = params;
+ return 0;
+}
+
+static int saa6752hs_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls)
+{
+ return saa6752hs_do_ext_ctrls(sd, ctrls, 1);
+}
+
+static int saa6752hs_try_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls)
+{
+ return saa6752hs_do_ext_ctrls(sd, ctrls, 0);
+}
+
+static int saa6752hs_g_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls)
+{
+ struct saa6752hs_state *h = to_state(sd);
+ int i;
+
+ if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+ return -EINVAL;
+
+ for (i = 0; i < ctrls->count; i++) {
+ int err = get_ctrl(h->has_ac3, &h->params, ctrls->controls + i);
+
+ if (err) {
+ ctrls->error_idx = i;
+ return err;
+ }
+ }
+ return 0;
+}
+
+static int saa6752hs_g_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f)
+{
+ struct saa6752hs_state *h = to_state(sd);
+
+ if (h->video_format == SAA6752HS_VF_UNKNOWN)
+ h->video_format = SAA6752HS_VF_D1;
+ f->width = v4l2_format_table[h->video_format].fmt.pix.width;
+ f->height = v4l2_format_table[h->video_format].fmt.pix.height;
+ f->code = V4L2_MBUS_FMT_FIXED;
+ f->field = V4L2_FIELD_INTERLACED;
+ f->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ return 0;
+}
+
+static int saa6752hs_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f)
+{
+ struct saa6752hs_state *h = to_state(sd);
+ int dist_352, dist_480, dist_720;
+
+ if (f->code != V4L2_MBUS_FMT_FIXED)
+ return -EINVAL;
+
+ /*
+ FIXME: translate and round width/height into EMPRESS
+ subsample type:
+
+ type | PAL | NTSC
+ ---------------------------
+ SIF | 352x288 | 352x240
+ 1/2 D1 | 352x576 | 352x480
+ 2/3 D1 | 480x576 | 480x480
+ D1 | 720x576 | 720x480
+ */
+
+ dist_352 = abs(f->width - 352);
+ dist_480 = abs(f->width - 480);
+ dist_720 = abs(f->width - 720);
+ if (dist_720 < dist_480) {
+ f->width = 720;
+ f->height = 576;
+ h->video_format = SAA6752HS_VF_D1;
+ } else if (dist_480 < dist_352) {
+ f->width = 480;
+ f->height = 576;
+ h->video_format = SAA6752HS_VF_2_3_D1;
+ } else {
+ f->width = 352;
+ if (abs(f->height - 576) <
+ abs(f->height - 288)) {
+ f->height = 576;
+ h->video_format = SAA6752HS_VF_1_2_D1;
+ } else {
+ f->height = 288;
+ h->video_format = SAA6752HS_VF_SIF;
+ }
+ }
+ f->field = V4L2_FIELD_INTERLACED;
+ f->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ return 0;
+}
+
+static int saa6752hs_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct saa6752hs_state *h = to_state(sd);
+
+ h->standard = std;
+ return 0;
+}
+
+static int saa6752hs_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct saa6752hs_state *h = to_state(sd);
+
+ return v4l2_chip_ident_i2c_client(client,
+ chip, h->chip, h->revision);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops saa6752hs_core_ops = {
+ .g_chip_ident = saa6752hs_g_chip_ident,
+ .init = saa6752hs_init,
+ .queryctrl = saa6752hs_queryctrl,
+ .querymenu = saa6752hs_querymenu,
+ .g_ext_ctrls = saa6752hs_g_ext_ctrls,
+ .s_ext_ctrls = saa6752hs_s_ext_ctrls,
+ .try_ext_ctrls = saa6752hs_try_ext_ctrls,
+ .s_std = saa6752hs_s_std,
+};
+
+static const struct v4l2_subdev_video_ops saa6752hs_video_ops = {
+ .s_mbus_fmt = saa6752hs_s_mbus_fmt,
+ .g_mbus_fmt = saa6752hs_g_mbus_fmt,
+};
+
+static const struct v4l2_subdev_ops saa6752hs_ops = {
+ .core = &saa6752hs_core_ops,
+ .video = &saa6752hs_video_ops,
+};
+
+static int saa6752hs_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct saa6752hs_state *h = kzalloc(sizeof(*h), GFP_KERNEL);
+ struct v4l2_subdev *sd;
+ u8 addr = 0x13;
+ u8 data[12];
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+ if (h == NULL)
+ return -ENOMEM;
+ sd = &h->sd;
+ v4l2_i2c_subdev_init(sd, client, &saa6752hs_ops);
+
+ i2c_master_send(client, &addr, 1);
+ i2c_master_recv(client, data, sizeof(data));
+ h->chip = V4L2_IDENT_SAA6752HS;
+ h->revision = (data[8] << 8) | data[9];
+ h->has_ac3 = 0;
+ if (h->revision == 0x0206) {
+ h->chip = V4L2_IDENT_SAA6752HS_AC3;
+ h->has_ac3 = 1;
+ v4l_info(client, "support AC-3\n");
+ }
+ h->params = param_defaults;
+ h->standard = 0; /* Assume 625 input lines */
+ return 0;
+}
+
+static int saa6752hs_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
+ return 0;
+}
+
+static const struct i2c_device_id saa6752hs_id[] = {
+ { "saa6752hs", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, saa6752hs_id);
+
+static struct i2c_driver saa6752hs_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "saa6752hs",
+ },
+ .probe = saa6752hs_probe,
+ .remove = saa6752hs_remove,
+ .id_table = saa6752hs_id,
+};
+
+module_i2c_driver(saa6752hs_driver);
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c
new file mode 100644
index 00000000..10460fd3
--- /dev/null
+++ b/drivers/media/video/saa7134/saa7134-alsa.c
@@ -0,0 +1,1209 @@
+/*
+ * SAA713x ALSA support for V4L
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <linux/interrupt.h>
+
+#include "saa7134.h"
+#include "saa7134-reg.h"
+
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug,"enable debug messages [alsa]");
+
+/*
+ * Configuration macros
+ */
+
+/* defaults */
+#define MIXER_ADDR_UNSELECTED -1
+#define MIXER_ADDR_TVTUNER 0
+#define MIXER_ADDR_LINE1 1
+#define MIXER_ADDR_LINE2 2
+#define MIXER_ADDR_LAST 2
+
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1};
+
+module_param_array(index, int, NULL, 0444);
+module_param_array(enable, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for SAA7134 capture interface(s).");
+MODULE_PARM_DESC(enable, "Enable (or not) the SAA7134 capture interface(s).");
+
+#define dprintk(fmt, arg...) if (debug) \
+ printk(KERN_DEBUG "%s/alsa: " fmt, dev->name , ##arg)
+
+
+
+/*
+ * Main chip structure
+ */
+
+typedef struct snd_card_saa7134 {
+ struct snd_card *card;
+ spinlock_t mixer_lock;
+ int mixer_volume[MIXER_ADDR_LAST+1][2];
+ int capture_source_addr;
+ int capture_source[2];
+ struct snd_kcontrol *capture_ctl[MIXER_ADDR_LAST+1];
+ struct pci_dev *pci;
+ struct saa7134_dev *dev;
+
+ unsigned long iobase;
+ s16 irq;
+ u16 mute_was_on;
+
+ spinlock_t lock;
+} snd_card_saa7134_t;
+
+
+/*
+ * PCM structure
+ */
+
+typedef struct snd_card_saa7134_pcm {
+ struct saa7134_dev *dev;
+
+ spinlock_t lock;
+
+ struct snd_pcm_substream *substream;
+} snd_card_saa7134_pcm_t;
+
+static struct snd_card *snd_saa7134_cards[SNDRV_CARDS];
+
+
+/*
+ * saa7134 DMA audio stop
+ *
+ * Called when the capture device is released or the buffer overflows
+ *
+ * - Copied verbatim from saa7134-oss's dsp_dma_stop.
+ *
+ */
+
+static void saa7134_dma_stop(struct saa7134_dev *dev)
+{
+ dev->dmasound.dma_blk = -1;
+ dev->dmasound.dma_running = 0;
+ saa7134_set_dmabits(dev);
+}
+
+/*
+ * saa7134 DMA audio start
+ *
+ * Called when preparing the capture device for use
+ *
+ * - Copied verbatim from saa7134-oss's dsp_dma_start.
+ *
+ */
+
+static void saa7134_dma_start(struct saa7134_dev *dev)
+{
+ dev->dmasound.dma_blk = 0;
+ dev->dmasound.dma_running = 1;
+ saa7134_set_dmabits(dev);
+}
+
+/*
+ * saa7134 audio DMA IRQ handler
+ *
+ * Called whenever we get an SAA7134_IRQ_REPORT_DONE_RA3 interrupt
+ * Handles shifting between the 2 buffers, manages the read counters,
+ * and notifies ALSA when periods elapse
+ *
+ * - Mostly copied from saa7134-oss's saa7134_irq_oss_done.
+ *
+ */
+
+static void saa7134_irq_alsa_done(struct saa7134_dev *dev,
+ unsigned long status)
+{
+ int next_blk, reg = 0;
+
+ spin_lock(&dev->slock);
+ if (UNSET == dev->dmasound.dma_blk) {
+ dprintk("irq: recording stopped\n");
+ goto done;
+ }
+ if (0 != (status & 0x0f000000))
+ dprintk("irq: lost %ld\n", (status >> 24) & 0x0f);
+ if (0 == (status & 0x10000000)) {
+ /* odd */
+ if (0 == (dev->dmasound.dma_blk & 0x01))
+ reg = SAA7134_RS_BA1(6);
+ } else {
+ /* even */
+ if (1 == (dev->dmasound.dma_blk & 0x01))
+ reg = SAA7134_RS_BA2(6);
+ }
+ if (0 == reg) {
+ dprintk("irq: field oops [%s]\n",
+ (status & 0x10000000) ? "even" : "odd");
+ goto done;
+ }
+
+ if (dev->dmasound.read_count >= dev->dmasound.blksize * (dev->dmasound.blocks-2)) {
+ dprintk("irq: overrun [full=%d/%d] - Blocks in %d\n",dev->dmasound.read_count,
+ dev->dmasound.bufsize, dev->dmasound.blocks);
+ spin_unlock(&dev->slock);
+ snd_pcm_stop(dev->dmasound.substream,SNDRV_PCM_STATE_XRUN);
+ return;
+ }
+
+ /* next block addr */
+ next_blk = (dev->dmasound.dma_blk + 2) % dev->dmasound.blocks;
+ saa_writel(reg,next_blk * dev->dmasound.blksize);
+ if (debug > 2)
+ dprintk("irq: ok, %s, next_blk=%d, addr=%x, blocks=%u, size=%u, read=%u\n",
+ (status & 0x10000000) ? "even" : "odd ", next_blk,
+ next_blk * dev->dmasound.blksize, dev->dmasound.blocks, dev->dmasound.blksize, dev->dmasound.read_count);
+
+ /* update status & wake waiting readers */
+ dev->dmasound.dma_blk = (dev->dmasound.dma_blk + 1) % dev->dmasound.blocks;
+ dev->dmasound.read_count += dev->dmasound.blksize;
+
+ dev->dmasound.recording_on = reg;
+
+ if (dev->dmasound.read_count >= snd_pcm_lib_period_bytes(dev->dmasound.substream)) {
+ spin_unlock(&dev->slock);
+ snd_pcm_period_elapsed(dev->dmasound.substream);
+ spin_lock(&dev->slock);
+ }
+
+ done:
+ spin_unlock(&dev->slock);
+
+}
+
+/*
+ * IRQ request handler
+ *
+ * Runs along with saa7134's IRQ handler, discards anything that isn't
+ * DMA sound
+ *
+ */
+
+static irqreturn_t saa7134_alsa_irq(int irq, void *dev_id)
+{
+ struct saa7134_dmasound *dmasound = dev_id;
+ struct saa7134_dev *dev = dmasound->priv_data;
+
+ unsigned long report, status;
+ int loop, handled = 0;
+
+ for (loop = 0; loop < 10; loop++) {
+ report = saa_readl(SAA7134_IRQ_REPORT);
+ status = saa_readl(SAA7134_IRQ_STATUS);
+
+ if (report & SAA7134_IRQ_REPORT_DONE_RA3) {
+ handled = 1;
+ saa_writel(SAA7134_IRQ_REPORT,
+ SAA7134_IRQ_REPORT_DONE_RA3);
+ saa7134_irq_alsa_done(dev, status);
+ } else {
+ goto out;
+ }
+ }
+
+ if (loop == 10) {
+ dprintk("error! looping IRQ!");
+ }
+
+out:
+ return IRQ_RETVAL(handled);
+}
+
+/*
+ * ALSA capture trigger
+ *
+ * - One of the ALSA capture callbacks.
+ *
+ * Called whenever a capture is started or stopped. Must be defined,
+ * but there's nothing we want to do here
+ *
+ */
+
+static int snd_card_saa7134_capture_trigger(struct snd_pcm_substream * substream,
+ int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_card_saa7134_pcm_t *pcm = runtime->private_data;
+ struct saa7134_dev *dev=pcm->dev;
+ int err = 0;
+
+ spin_lock(&dev->slock);
+ if (cmd == SNDRV_PCM_TRIGGER_START) {
+ /* start dma */
+ saa7134_dma_start(dev);
+ } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+ /* stop dma */
+ saa7134_dma_stop(dev);
+ } else {
+ err = -EINVAL;
+ }
+ spin_unlock(&dev->slock);
+
+ return err;
+}
+
+/*
+ * DMA buffer initialization
+ *
+ * Uses V4L functions to initialize the DMA. Shouldn't be necessary in
+ * ALSA, but I was unable to use ALSA's own DMA, and had to force the
+ * usage of V4L's
+ *
+ * - Copied verbatim from saa7134-oss.
+ *
+ */
+
+static int dsp_buffer_init(struct saa7134_dev *dev)
+{
+ int err;
+
+ BUG_ON(!dev->dmasound.bufsize);
+
+ videobuf_dma_init(&dev->dmasound.dma);
+ err = videobuf_dma_init_kernel(&dev->dmasound.dma, PCI_DMA_FROMDEVICE,
+ (dev->dmasound.bufsize + PAGE_SIZE) >> PAGE_SHIFT);
+ if (0 != err)
+ return err;
+ return 0;
+}
+
+/*
+ * DMA buffer release
+ *
+ * Called after closing the device, during snd_card_saa7134_capture_close
+ *
+ */
+
+static int dsp_buffer_free(struct saa7134_dev *dev)
+{
+ BUG_ON(!dev->dmasound.blksize);
+
+ videobuf_dma_free(&dev->dmasound.dma);
+
+ dev->dmasound.blocks = 0;
+ dev->dmasound.blksize = 0;
+ dev->dmasound.bufsize = 0;
+
+ return 0;
+}
+
+/*
+ * Setting the capture source and updating the ALSA controls
+ */
+static int snd_saa7134_capsrc_set(struct snd_kcontrol *kcontrol,
+ int left, int right, bool force_notify)
+{
+ snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol);
+ int change = 0, addr = kcontrol->private_value;
+ int active, old_addr;
+ u32 anabar, xbarin;
+ int analog_io, rate;
+ struct saa7134_dev *dev;
+
+ dev = chip->dev;
+
+ spin_lock_irq(&chip->mixer_lock);
+
+ active = left != 0 || right != 0;
+ old_addr = chip->capture_source_addr;
+
+ /* The active capture source cannot be deactivated */
+ if (active) {
+ change = old_addr != addr ||
+ chip->capture_source[0] != left ||
+ chip->capture_source[1] != right;
+
+ chip->capture_source[0] = left;
+ chip->capture_source[1] = right;
+ chip->capture_source_addr = addr;
+ dev->dmasound.input = addr;
+ }
+ spin_unlock_irq(&chip->mixer_lock);
+
+ if (change) {
+ switch (dev->pci->device) {
+
+ case PCI_DEVICE_ID_PHILIPS_SAA7134:
+ switch (addr) {
+ case MIXER_ADDR_TVTUNER:
+ saa_andorb(SAA7134_AUDIO_FORMAT_CTRL,
+ 0xc0, 0xc0);
+ saa_andorb(SAA7134_SIF_SAMPLE_FREQ,
+ 0x03, 0x00);
+ break;
+ case MIXER_ADDR_LINE1:
+ case MIXER_ADDR_LINE2:
+ analog_io = (MIXER_ADDR_LINE1 == addr) ?
+ 0x00 : 0x08;
+ rate = (32000 == dev->dmasound.rate) ?
+ 0x01 : 0x03;
+ saa_andorb(SAA7134_ANALOG_IO_SELECT,
+ 0x08, analog_io);
+ saa_andorb(SAA7134_AUDIO_FORMAT_CTRL,
+ 0xc0, 0x80);
+ saa_andorb(SAA7134_SIF_SAMPLE_FREQ,
+ 0x03, rate);
+ break;
+ }
+
+ break;
+ case PCI_DEVICE_ID_PHILIPS_SAA7133:
+ case PCI_DEVICE_ID_PHILIPS_SAA7135:
+ xbarin = 0x03; /* adc */
+ anabar = 0;
+ switch (addr) {
+ case MIXER_ADDR_TVTUNER:
+ xbarin = 0; /* Demodulator */
+ anabar = 2; /* DACs */
+ break;
+ case MIXER_ADDR_LINE1:
+ anabar = 0; /* aux1, aux1 */
+ break;
+ case MIXER_ADDR_LINE2:
+ anabar = 9; /* aux2, aux2 */
+ break;
+ }
+
+ /* output xbar always main channel */
+ saa_dsp_writel(dev, SAA7133_DIGITAL_OUTPUT_SEL1,
+ 0xbbbb10);
+
+ if (left || right) {
+ /* We've got data, turn the input on */
+ saa_dsp_writel(dev, SAA7133_DIGITAL_INPUT_XBAR1,
+ xbarin);
+ saa_writel(SAA7133_ANALOG_IO_SELECT, anabar);
+ } else {
+ saa_dsp_writel(dev, SAA7133_DIGITAL_INPUT_XBAR1,
+ 0);
+ saa_writel(SAA7133_ANALOG_IO_SELECT, 0);
+ }
+ break;
+ }
+ }
+
+ if (change) {
+ if (force_notify)
+ snd_ctl_notify(chip->card,
+ SNDRV_CTL_EVENT_MASK_VALUE,
+ &chip->capture_ctl[addr]->id);
+
+ if (old_addr != MIXER_ADDR_UNSELECTED && old_addr != addr)
+ snd_ctl_notify(chip->card,
+ SNDRV_CTL_EVENT_MASK_VALUE,
+ &chip->capture_ctl[old_addr]->id);
+ }
+
+ return change;
+}
+
+/*
+ * ALSA PCM preparation
+ *
+ * - One of the ALSA capture callbacks.
+ *
+ * Called right after the capture device is opened, this function configures
+ * the buffer using the previously defined functions, allocates the memory,
+ * sets up the hardware registers, and then starts the DMA. When this function
+ * returns, the audio should be flowing.
+ *
+ */
+
+static int snd_card_saa7134_capture_prepare(struct snd_pcm_substream * substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int bswap, sign;
+ u32 fmt, control;
+ snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream);
+ struct saa7134_dev *dev;
+ snd_card_saa7134_pcm_t *pcm = runtime->private_data;
+
+ pcm->dev->dmasound.substream = substream;
+
+ dev = saa7134->dev;
+
+ if (snd_pcm_format_width(runtime->format) == 8)
+ fmt = 0x00;
+ else
+ fmt = 0x01;
+
+ if (snd_pcm_format_signed(runtime->format))
+ sign = 1;
+ else
+ sign = 0;
+
+ if (snd_pcm_format_big_endian(runtime->format))
+ bswap = 1;
+ else
+ bswap = 0;
+
+ switch (dev->pci->device) {
+ case PCI_DEVICE_ID_PHILIPS_SAA7134:
+ if (1 == runtime->channels)
+ fmt |= (1 << 3);
+ if (2 == runtime->channels)
+ fmt |= (3 << 3);
+ if (sign)
+ fmt |= 0x04;
+
+ fmt |= (MIXER_ADDR_TVTUNER == dev->dmasound.input) ? 0xc0 : 0x80;
+ saa_writeb(SAA7134_NUM_SAMPLES0, ((dev->dmasound.blksize - 1) & 0x0000ff));
+ saa_writeb(SAA7134_NUM_SAMPLES1, ((dev->dmasound.blksize - 1) & 0x00ff00) >> 8);
+ saa_writeb(SAA7134_NUM_SAMPLES2, ((dev->dmasound.blksize - 1) & 0xff0000) >> 16);
+ saa_writeb(SAA7134_AUDIO_FORMAT_CTRL, fmt);
+
+ break;
+ case PCI_DEVICE_ID_PHILIPS_SAA7133:
+ case PCI_DEVICE_ID_PHILIPS_SAA7135:
+ if (1 == runtime->channels)
+ fmt |= (1 << 4);
+ if (2 == runtime->channels)
+ fmt |= (2 << 4);
+ if (!sign)
+ fmt |= 0x04;
+ saa_writel(SAA7133_NUM_SAMPLES, dev->dmasound.blksize -1);
+ saa_writel(SAA7133_AUDIO_CHANNEL, 0x543210 | (fmt << 24));
+ break;
+ }
+
+ dprintk("rec_start: afmt=%d ch=%d => fmt=0x%x swap=%c\n",
+ runtime->format, runtime->channels, fmt,
+ bswap ? 'b' : '-');
+ /* dma: setup channel 6 (= AUDIO) */
+ control = SAA7134_RS_CONTROL_BURST_16 |
+ SAA7134_RS_CONTROL_ME |
+ (dev->dmasound.pt.dma >> 12);
+ if (bswap)
+ control |= SAA7134_RS_CONTROL_BSWAP;
+
+ saa_writel(SAA7134_RS_BA1(6),0);
+ saa_writel(SAA7134_RS_BA2(6),dev->dmasound.blksize);
+ saa_writel(SAA7134_RS_PITCH(6),0);
+ saa_writel(SAA7134_RS_CONTROL(6),control);
+
+ dev->dmasound.rate = runtime->rate;
+
+ /* Setup and update the card/ALSA controls */
+ snd_saa7134_capsrc_set(saa7134->capture_ctl[dev->dmasound.input], 1, 1,
+ true);
+
+ return 0;
+
+}
+
+/*
+ * ALSA pointer fetching
+ *
+ * - One of the ALSA capture callbacks.
+ *
+ * Called whenever a period elapses, it must return the current hardware
+ * position of the buffer.
+ * Also resets the read counter used to prevent overruns
+ *
+ */
+
+static snd_pcm_uframes_t
+snd_card_saa7134_capture_pointer(struct snd_pcm_substream * substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_card_saa7134_pcm_t *pcm = runtime->private_data;
+ struct saa7134_dev *dev=pcm->dev;
+
+ if (dev->dmasound.read_count) {
+ dev->dmasound.read_count -= snd_pcm_lib_period_bytes(substream);
+ dev->dmasound.read_offset += snd_pcm_lib_period_bytes(substream);
+ if (dev->dmasound.read_offset == dev->dmasound.bufsize)
+ dev->dmasound.read_offset = 0;
+ }
+
+ return bytes_to_frames(runtime, dev->dmasound.read_offset);
+}
+
+/*
+ * ALSA hardware capabilities definition
+ *
+ * Report only 32kHz for ALSA:
+ *
+ * - SAA7133/35 uses DDEP (DemDec Easy Programming mode), which works in 32kHz
+ * only
+ * - SAA7134 for TV mode uses DemDec mode (32kHz)
+ * - Radio works in 32kHz only
+ * - When recording 48kHz from Line1/Line2, switching of capture source to TV
+ * means
+ * switching to 32kHz without any frequency translation
+ */
+
+static struct snd_pcm_hardware snd_card_saa7134_capture =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S16_BE | \
+ SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_U8 | \
+ SNDRV_PCM_FMTBIT_U16_LE | \
+ SNDRV_PCM_FMTBIT_U16_BE,
+ .rates = SNDRV_PCM_RATE_32000,
+ .rate_min = 32000,
+ .rate_max = 32000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (256*1024),
+ .period_bytes_min = 64,
+ .period_bytes_max = (256*1024),
+ .periods_min = 4,
+ .periods_max = 1024,
+};
+
+static void snd_card_saa7134_runtime_free(struct snd_pcm_runtime *runtime)
+{
+ snd_card_saa7134_pcm_t *pcm = runtime->private_data;
+
+ kfree(pcm);
+}
+
+
+/*
+ * ALSA hardware params
+ *
+ * - One of the ALSA capture callbacks.
+ *
+ * Called on initialization, right before the PCM preparation
+ *
+ */
+
+static int snd_card_saa7134_hw_params(struct snd_pcm_substream * substream,
+ struct snd_pcm_hw_params * hw_params)
+{
+ snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream);
+ struct saa7134_dev *dev;
+ unsigned int period_size, periods;
+ int err;
+
+ period_size = params_period_bytes(hw_params);
+ periods = params_periods(hw_params);
+
+ if (period_size < 0x100 || period_size > 0x10000)
+ return -EINVAL;
+ if (periods < 4)
+ return -EINVAL;
+ if (period_size * periods > 1024 * 1024)
+ return -EINVAL;
+
+ dev = saa7134->dev;
+
+ if (dev->dmasound.blocks == periods &&
+ dev->dmasound.blksize == period_size)
+ return 0;
+
+ /* release the old buffer */
+ if (substream->runtime->dma_area) {
+ saa7134_pgtable_free(dev->pci, &dev->dmasound.pt);
+ videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma);
+ dsp_buffer_free(dev);
+ substream->runtime->dma_area = NULL;
+ }
+ dev->dmasound.blocks = periods;
+ dev->dmasound.blksize = period_size;
+ dev->dmasound.bufsize = period_size * periods;
+
+ err = dsp_buffer_init(dev);
+ if (0 != err) {
+ dev->dmasound.blocks = 0;
+ dev->dmasound.blksize = 0;
+ dev->dmasound.bufsize = 0;
+ return err;
+ }
+
+ if (0 != (err = videobuf_dma_map(&dev->pci->dev, &dev->dmasound.dma))) {
+ dsp_buffer_free(dev);
+ return err;
+ }
+ if (0 != (err = saa7134_pgtable_alloc(dev->pci,&dev->dmasound.pt))) {
+ videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma);
+ dsp_buffer_free(dev);
+ return err;
+ }
+ if (0 != (err = saa7134_pgtable_build(dev->pci,&dev->dmasound.pt,
+ dev->dmasound.dma.sglist,
+ dev->dmasound.dma.sglen,
+ 0))) {
+ saa7134_pgtable_free(dev->pci, &dev->dmasound.pt);
+ videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma);
+ dsp_buffer_free(dev);
+ return err;
+ }
+
+ /* I should be able to use runtime->dma_addr in the control
+ byte, but it doesn't work. So I allocate the DMA using the
+ V4L functions, and force ALSA to use that as the DMA area */
+
+ substream->runtime->dma_area = dev->dmasound.dma.vaddr;
+ substream->runtime->dma_bytes = dev->dmasound.bufsize;
+ substream->runtime->dma_addr = 0;
+
+ return 0;
+
+}
+
+/*
+ * ALSA hardware release
+ *
+ * - One of the ALSA capture callbacks.
+ *
+ * Called after closing the device, but before snd_card_saa7134_capture_close
+ * It stops the DMA audio and releases the buffers.
+ *
+ */
+
+static int snd_card_saa7134_hw_free(struct snd_pcm_substream * substream)
+{
+ snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream);
+ struct saa7134_dev *dev;
+
+ dev = saa7134->dev;
+
+ if (substream->runtime->dma_area) {
+ saa7134_pgtable_free(dev->pci, &dev->dmasound.pt);
+ videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma);
+ dsp_buffer_free(dev);
+ substream->runtime->dma_area = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * ALSA capture finish
+ *
+ * - One of the ALSA capture callbacks.
+ *
+ * Called after closing the device.
+ *
+ */
+
+static int snd_card_saa7134_capture_close(struct snd_pcm_substream * substream)
+{
+ snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream);
+ struct saa7134_dev *dev = saa7134->dev;
+
+ if (saa7134->mute_was_on) {
+ dev->ctl_mute = 1;
+ saa7134_tvaudio_setmute(dev);
+ }
+ return 0;
+}
+
+/*
+ * ALSA capture start
+ *
+ * - One of the ALSA capture callbacks.
+ *
+ * Called when opening the device. It creates and populates the PCM
+ * structure
+ *
+ */
+
+static int snd_card_saa7134_capture_open(struct snd_pcm_substream * substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_card_saa7134_pcm_t *pcm;
+ snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream);
+ struct saa7134_dev *dev;
+ int amux, err;
+
+ if (!saa7134) {
+ printk(KERN_ERR "BUG: saa7134 can't find device struct."
+ " Can't proceed with open\n");
+ return -ENODEV;
+ }
+ dev = saa7134->dev;
+ mutex_lock(&dev->dmasound.lock);
+
+ dev->dmasound.read_count = 0;
+ dev->dmasound.read_offset = 0;
+
+ amux = dev->input->amux;
+ if ((amux < 1) || (amux > 3))
+ amux = 1;
+ dev->dmasound.input = amux - 1;
+
+ mutex_unlock(&dev->dmasound.lock);
+
+ pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
+ if (pcm == NULL)
+ return -ENOMEM;
+
+ pcm->dev=saa7134->dev;
+
+ spin_lock_init(&pcm->lock);
+
+ pcm->substream = substream;
+ runtime->private_data = pcm;
+ runtime->private_free = snd_card_saa7134_runtime_free;
+ runtime->hw = snd_card_saa7134_capture;
+
+ if (dev->ctl_mute != 0) {
+ saa7134->mute_was_on = 1;
+ dev->ctl_mute = 0;
+ saa7134_tvaudio_setmute(dev);
+ }
+
+ err = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
+ return err;
+
+ err = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIODS, 2);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/*
+ * page callback (needed for mmap)
+ */
+
+static struct page *snd_card_saa7134_page(struct snd_pcm_substream *substream,
+ unsigned long offset)
+{
+ void *pageptr = substream->runtime->dma_area + offset;
+ return vmalloc_to_page(pageptr);
+}
+
+/*
+ * ALSA capture callbacks definition
+ */
+
+static struct snd_pcm_ops snd_card_saa7134_capture_ops = {
+ .open = snd_card_saa7134_capture_open,
+ .close = snd_card_saa7134_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_card_saa7134_hw_params,
+ .hw_free = snd_card_saa7134_hw_free,
+ .prepare = snd_card_saa7134_capture_prepare,
+ .trigger = snd_card_saa7134_capture_trigger,
+ .pointer = snd_card_saa7134_capture_pointer,
+ .page = snd_card_saa7134_page,
+};
+
+/*
+ * ALSA PCM setup
+ *
+ * Called when initializing the board. Sets up the name and hooks up
+ * the callbacks
+ *
+ */
+
+static int snd_card_saa7134_pcm(snd_card_saa7134_t *saa7134, int device)
+{
+ struct snd_pcm *pcm;
+ int err;
+
+ if ((err = snd_pcm_new(saa7134->card, "SAA7134 PCM", device, 0, 1, &pcm)) < 0)
+ return err;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_saa7134_capture_ops);
+ pcm->private_data = saa7134;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "SAA7134 PCM");
+ return 0;
+}
+
+#define SAA713x_VOLUME(xname, xindex, addr) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+ .info = snd_saa7134_volume_info, \
+ .get = snd_saa7134_volume_get, .put = snd_saa7134_volume_put, \
+ .private_value = addr }
+
+static int snd_saa7134_volume_info(struct snd_kcontrol * kcontrol,
+ struct snd_ctl_elem_info * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 20;
+ return 0;
+}
+
+static int snd_saa7134_volume_get(struct snd_kcontrol * kcontrol,
+ struct snd_ctl_elem_value * ucontrol)
+{
+ snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol);
+ int addr = kcontrol->private_value;
+
+ ucontrol->value.integer.value[0] = chip->mixer_volume[addr][0];
+ ucontrol->value.integer.value[1] = chip->mixer_volume[addr][1];
+ return 0;
+}
+
+static int snd_saa7134_volume_put(struct snd_kcontrol * kcontrol,
+ struct snd_ctl_elem_value * ucontrol)
+{
+ snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol);
+ struct saa7134_dev *dev = chip->dev;
+
+ int change, addr = kcontrol->private_value;
+ int left, right;
+
+ left = ucontrol->value.integer.value[0];
+ if (left < 0)
+ left = 0;
+ if (left > 20)
+ left = 20;
+ right = ucontrol->value.integer.value[1];
+ if (right < 0)
+ right = 0;
+ if (right > 20)
+ right = 20;
+ spin_lock_irq(&chip->mixer_lock);
+ change = 0;
+ if (chip->mixer_volume[addr][0] != left) {
+ change = 1;
+ right = left;
+ }
+ if (chip->mixer_volume[addr][1] != right) {
+ change = 1;
+ left = right;
+ }
+ if (change) {
+ switch (dev->pci->device) {
+ case PCI_DEVICE_ID_PHILIPS_SAA7134:
+ switch (addr) {
+ case MIXER_ADDR_TVTUNER:
+ left = 20;
+ break;
+ case MIXER_ADDR_LINE1:
+ saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x10,
+ (left > 10) ? 0x00 : 0x10);
+ break;
+ case MIXER_ADDR_LINE2:
+ saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x20,
+ (left > 10) ? 0x00 : 0x20);
+ break;
+ }
+ break;
+ case PCI_DEVICE_ID_PHILIPS_SAA7133:
+ case PCI_DEVICE_ID_PHILIPS_SAA7135:
+ switch (addr) {
+ case MIXER_ADDR_TVTUNER:
+ left = 20;
+ break;
+ case MIXER_ADDR_LINE1:
+ saa_andorb(0x0594, 0x10,
+ (left > 10) ? 0x00 : 0x10);
+ break;
+ case MIXER_ADDR_LINE2:
+ saa_andorb(0x0594, 0x20,
+ (left > 10) ? 0x00 : 0x20);
+ break;
+ }
+ break;
+ }
+ chip->mixer_volume[addr][0] = left;
+ chip->mixer_volume[addr][1] = right;
+ }
+ spin_unlock_irq(&chip->mixer_lock);
+ return change;
+}
+
+#define SAA713x_CAPSRC(xname, xindex, addr) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+ .info = snd_saa7134_capsrc_info, \
+ .get = snd_saa7134_capsrc_get, .put = snd_saa7134_capsrc_put, \
+ .private_value = addr }
+
+static int snd_saa7134_capsrc_info(struct snd_kcontrol * kcontrol,
+ struct snd_ctl_elem_info * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_saa7134_capsrc_get(struct snd_kcontrol * kcontrol,
+ struct snd_ctl_elem_value * ucontrol)
+{
+ snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol);
+ int addr = kcontrol->private_value;
+
+ spin_lock_irq(&chip->mixer_lock);
+ if (chip->capture_source_addr == addr) {
+ ucontrol->value.integer.value[0] = chip->capture_source[0];
+ ucontrol->value.integer.value[1] = chip->capture_source[1];
+ } else {
+ ucontrol->value.integer.value[0] = 0;
+ ucontrol->value.integer.value[1] = 0;
+ }
+ spin_unlock_irq(&chip->mixer_lock);
+
+ return 0;
+}
+
+static int snd_saa7134_capsrc_put(struct snd_kcontrol * kcontrol,
+ struct snd_ctl_elem_value * ucontrol)
+{
+ int left, right;
+ left = ucontrol->value.integer.value[0] & 1;
+ right = ucontrol->value.integer.value[1] & 1;
+
+ return snd_saa7134_capsrc_set(kcontrol, left, right, false);
+}
+
+static struct snd_kcontrol_new snd_saa7134_volume_controls[] = {
+SAA713x_VOLUME("Video Volume", 0, MIXER_ADDR_TVTUNER),
+SAA713x_VOLUME("Line Volume", 1, MIXER_ADDR_LINE1),
+SAA713x_VOLUME("Line Volume", 2, MIXER_ADDR_LINE2),
+};
+
+static struct snd_kcontrol_new snd_saa7134_capture_controls[] = {
+SAA713x_CAPSRC("Video Capture Switch", 0, MIXER_ADDR_TVTUNER),
+SAA713x_CAPSRC("Line Capture Switch", 1, MIXER_ADDR_LINE1),
+SAA713x_CAPSRC("Line Capture Switch", 2, MIXER_ADDR_LINE2),
+};
+
+/*
+ * ALSA mixer setup
+ *
+ * Called when initializing the board. Sets up the name and hooks up
+ * the callbacks
+ *
+ */
+
+static int snd_card_saa7134_new_mixer(snd_card_saa7134_t * chip)
+{
+ struct snd_card *card = chip->card;
+ struct snd_kcontrol *kcontrol;
+ unsigned int idx;
+ int err, addr;
+
+ strcpy(card->mixername, "SAA7134 Mixer");
+
+ for (idx = 0; idx < ARRAY_SIZE(snd_saa7134_volume_controls); idx++) {
+ kcontrol = snd_ctl_new1(&snd_saa7134_volume_controls[idx],
+ chip);
+ err = snd_ctl_add(card, kcontrol);
+ if (err < 0)
+ return err;
+ }
+
+ for (idx = 0; idx < ARRAY_SIZE(snd_saa7134_capture_controls); idx++) {
+ kcontrol = snd_ctl_new1(&snd_saa7134_capture_controls[idx],
+ chip);
+ addr = snd_saa7134_capture_controls[idx].private_value;
+ chip->capture_ctl[addr] = kcontrol;
+ err = snd_ctl_add(card, kcontrol);
+ if (err < 0)
+ return err;
+ }
+
+ chip->capture_source_addr = MIXER_ADDR_UNSELECTED;
+ return 0;
+}
+
+static void snd_saa7134_free(struct snd_card * card)
+{
+ snd_card_saa7134_t *chip = card->private_data;
+
+ if (chip->dev->dmasound.priv_data == NULL)
+ return;
+
+ if (chip->irq >= 0)
+ free_irq(chip->irq, &chip->dev->dmasound);
+
+ chip->dev->dmasound.priv_data = NULL;
+
+}
+
+/*
+ * ALSA initialization
+ *
+ * Called by the init routine, once for each saa7134 device present,
+ * it creates the basic structures and registers the ALSA devices
+ *
+ */
+
+static int alsa_card_saa7134_create(struct saa7134_dev *dev, int devnum)
+{
+
+ struct snd_card *card;
+ snd_card_saa7134_t *chip;
+ int err;
+
+
+ if (devnum >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[devnum])
+ return -ENODEV;
+
+ err = snd_card_create(index[devnum], id[devnum], THIS_MODULE,
+ sizeof(snd_card_saa7134_t), &card);
+ if (err < 0)
+ return err;
+
+ strcpy(card->driver, "SAA7134");
+
+ /* Card "creation" */
+
+ card->private_free = snd_saa7134_free;
+ chip = card->private_data;
+
+ spin_lock_init(&chip->lock);
+ spin_lock_init(&chip->mixer_lock);
+
+ chip->dev = dev;
+
+ chip->card = card;
+
+ chip->pci = dev->pci;
+ chip->iobase = pci_resource_start(dev->pci, 0);
+
+
+ err = request_irq(dev->pci->irq, saa7134_alsa_irq,
+ IRQF_SHARED | IRQF_DISABLED, dev->name,
+ (void*) &dev->dmasound);
+
+ if (err < 0) {
+ printk(KERN_ERR "%s: can't get IRQ %d for ALSA\n",
+ dev->name, dev->pci->irq);
+ goto __nodev;
+ }
+
+ chip->irq = dev->pci->irq;
+
+ mutex_init(&dev->dmasound.lock);
+
+ if ((err = snd_card_saa7134_new_mixer(chip)) < 0)
+ goto __nodev;
+
+ if ((err = snd_card_saa7134_pcm(chip, 0)) < 0)
+ goto __nodev;
+
+ snd_card_set_dev(card, &chip->pci->dev);
+
+ /* End of "creation" */
+
+ strcpy(card->shortname, "SAA7134");
+ sprintf(card->longname, "%s at 0x%lx irq %d",
+ chip->dev->name, chip->iobase, chip->irq);
+
+ printk(KERN_INFO "%s/alsa: %s registered as card %d\n",dev->name,card->longname,index[devnum]);
+
+ if ((err = snd_card_register(card)) == 0) {
+ snd_saa7134_cards[devnum] = card;
+ return 0;
+ }
+
+__nodev:
+ snd_card_free(card);
+ return err;
+}
+
+
+static int alsa_device_init(struct saa7134_dev *dev)
+{
+ dev->dmasound.priv_data = dev;
+ alsa_card_saa7134_create(dev,dev->nr);
+ return 1;
+}
+
+static int alsa_device_exit(struct saa7134_dev *dev)
+{
+
+ snd_card_free(snd_saa7134_cards[dev->nr]);
+ snd_saa7134_cards[dev->nr] = NULL;
+ return 1;
+}
+
+/*
+ * Module initializer
+ *
+ * Loops through present saa7134 cards, and assigns an ALSA device
+ * to each one
+ *
+ */
+
+static int saa7134_alsa_init(void)
+{
+ struct saa7134_dev *dev = NULL;
+ struct list_head *list;
+
+ saa7134_dmasound_init = alsa_device_init;
+ saa7134_dmasound_exit = alsa_device_exit;
+
+ printk(KERN_INFO "saa7134 ALSA driver for DMA sound loaded\n");
+
+ list_for_each(list,&saa7134_devlist) {
+ dev = list_entry(list, struct saa7134_dev, devlist);
+ if (dev->pci->device == PCI_DEVICE_ID_PHILIPS_SAA7130)
+ printk(KERN_INFO "%s/alsa: %s doesn't support digital audio\n",
+ dev->name, saa7134_boards[dev->board].name);
+ else
+ alsa_device_init(dev);
+ }
+
+ if (dev == NULL)
+ printk(KERN_INFO "saa7134 ALSA: no saa7134 cards found\n");
+
+ return 0;
+
+}
+
+/*
+ * Module destructor
+ */
+
+static void saa7134_alsa_exit(void)
+{
+ int idx;
+
+ for (idx = 0; idx < SNDRV_CARDS; idx++) {
+ snd_card_free(snd_saa7134_cards[idx]);
+ }
+
+ saa7134_dmasound_init = NULL;
+ saa7134_dmasound_exit = NULL;
+ printk(KERN_INFO "saa7134 ALSA driver for DMA sound unloaded\n");
+
+ return;
+}
+
+/* We initialize this late, to make sure the sound system is up and running */
+late_initcall(saa7134_alsa_init);
+module_exit(saa7134_alsa_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ricardo Cerqueira");
diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c
new file mode 100644
index 00000000..53aae596
--- /dev/null
+++ b/drivers/media/video/saa7134/saa7134-cards.c
@@ -0,0 +1,7981 @@
+/*
+ *
+ * device driver for philips saa7134 based TV cards
+ * card-specific stuff.
+ *
+ * (c) 2001-04 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+#include "tuner-xc2028.h"
+#include <media/v4l2-common.h>
+#include <media/tveeprom.h>
+#include "tea5767.h"
+#include "tda18271.h"
+#include "xc5000.h"
+#include "s5h1411.h"
+
+/* commly used strings */
+static char name_mute[] = "mute";
+static char name_radio[] = "Radio";
+static char name_tv[] = "Television";
+static char name_tv_mono[] = "TV (mono only)";
+static char name_comp[] = "Composite";
+static char name_comp1[] = "Composite1";
+static char name_comp2[] = "Composite2";
+static char name_comp3[] = "Composite3";
+static char name_comp4[] = "Composite4";
+static char name_svideo[] = "S-Video";
+
+/* ------------------------------------------------------------------ */
+/* board config info */
+
+/* If radio_type !=UNSET, radio_addr should be specified
+ */
+
+struct saa7134_board saa7134_boards[] = {
+ [SAA7134_BOARD_UNKNOWN] = {
+ .name = "UNKNOWN/GENERIC",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+
+ .inputs = {{
+ .name = "default",
+ .vmux = 0,
+ .amux = LINE1,
+ }},
+ },
+ [SAA7134_BOARD_PROTEUS_PRO] = {
+ /* /me */
+ .name = "Proteus Pro [philips reference design]",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+
+ .inputs = {{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_FLYVIDEO3000] = {
+ /* "Marco d'Itri" <md@Linux.IT> */
+ .name = "LifeView FlyVIDEO3000",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+
+ .gpiomask = 0xe000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .gpio = 0x8000,
+ .tv = 1,
+ },{
+ .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .gpio = 0x0000,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ .gpio = 0x4000,
+ },{
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE2,
+ .gpio = 0x4000,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ .gpio = 0x4000,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0x2000,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = TV,
+ .gpio = 0x8000,
+ },
+ },
+ [SAA7134_BOARD_FLYVIDEO2000] = {
+ /* "TC Wan" <tcwan@cs.usm.my> */
+ .name = "LifeView/Typhoon FlyVIDEO2000",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+
+ .gpiomask = 0xe000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .gpio = 0x0000,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ .gpio = 0x4000,
+ },{
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE2,
+ .gpio = 0x4000,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ .gpio = 0x4000,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0x2000,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = LINE2,
+ .gpio = 0x8000,
+ },
+ },
+ [SAA7134_BOARD_FLYTVPLATINUM_MINI] = {
+ /* "Arnaud Quette" <aquette@free.fr> */
+ .name = "LifeView FlyTV Platinum Mini",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1, /* Composite signal on S-Video input */
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_comp2, /* Composite input */
+ .vmux = 3,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ },
+ [SAA7134_BOARD_FLYTVPLATINUM_FM] = {
+ /* LifeView FlyTV Platinum FM (LR214WF) */
+ /* "Peter Missel <peter.missel@onlinehome.de> */
+ .name = "LifeView FlyTV Platinum FM / Gold",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+
+ .gpiomask = 0x1E000, /* Set GP16 and unused 15,14,13 to Output */
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .gpio = 0x10000, /* GP16=1 selects TV input */
+ .tv = 1,
+ },{
+/* .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .gpio = 0x0000,
+ .tv = 1,
+ },{
+*/ .name = name_comp1, /* Composite signal on S-Video input */
+ .vmux = 0,
+ .amux = LINE2,
+/* .gpio = 0x4000, */
+ },{
+ .name = name_comp2, /* Composite input */
+ .vmux = 3,
+ .amux = LINE2,
+/* .gpio = 0x4000, */
+ },{
+ .name = name_svideo, /* S-Video signal on S-Video input */
+ .vmux = 8,
+ .amux = LINE2,
+/* .gpio = 0x4000, */
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x00000, /* GP16=0 selects FM radio antenna */
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = TV,
+ .gpio = 0x10000,
+ },
+ },
+ [SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM] = {
+ /* RoverMedia TV Link Pro FM (LR138 REV:I) */
+ /* Eugene Yudin <Eugene.Yudin@gmail.com> */
+ .name = "RoverMedia TV Link Pro FM",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* TCL MFPE05 2 */
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0xe000,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .gpio = 0x8000,
+ .tv = 1,
+ }, {
+ .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .gpio = 0x0000,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ .gpio = 0x4000,
+ }, {
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE2,
+ .gpio = 0x4000,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ .gpio = 0x4000,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0x2000,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = TV,
+ .gpio = 0x8000,
+ },
+ },
+ [SAA7134_BOARD_EMPRESS] = {
+ /* "Gert Vervoort" <gert.vervoort@philips.com> */
+ .name = "EMPRESS",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .empress_addr = 0x20,
+
+ .inputs = {{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ .mpeg = SAA7134_MPEG_EMPRESS,
+ .video_out = CCIR656,
+ },
+ [SAA7134_BOARD_MONSTERTV] = {
+ /* "K.Ohta" <alpha292@bremen.or.jp> */
+ .name = "SKNet Monster TV",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_MD9717] = {
+ .name = "Tevion MD 9717",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ /* workaround for problems with normal TV sound */
+ .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE1,
+ },{
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = TV,
+ },
+ },
+ [SAA7134_BOARD_TVSTATION_RDS] = {
+ /* Typhoon TV Tuner RDS: Art.Nr. 50694 */
+ .name = "KNC One TV-Station RDS / Typhoon TV Tuner RDS",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+
+ .name = "CVid over SVid",
+ .vmux = 0,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_TVSTATION_DVR] = {
+ .name = "KNC One TV-Station DVR",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .empress_addr = 0x20,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0x820000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x20000,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ .gpio = 0x20000,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ .gpio = 0x20000,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0x20000,
+ },
+ .mpeg = SAA7134_MPEG_EMPRESS,
+ .video_out = CCIR656,
+ },
+ [SAA7134_BOARD_CINERGY400] = {
+ .name = "Terratec Cinergy 400 TV",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 4,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp2, /* CVideo over SVideo Connector */
+ .vmux = 0,
+ .amux = LINE1,
+ }}
+ },
+ [SAA7134_BOARD_MD5044] = {
+ .name = "Medion 5044",
+ .audio_clock = 0x00187de7, /* was: 0x00200000, */
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ /* workaround for problems with normal TV sound */
+ .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_KWORLD] = {
+ .name = "Kworld/KuroutoShikou SAA7130-TVPCI",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ },
+ [SAA7134_BOARD_CINERGY600] = {
+ .name = "Terratec Cinergy 600 TV",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 4,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp2, /* CVideo over SVideo Connector */
+ .vmux = 0,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_MD7134] = {
+ .name = "Medion 7134",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = TV,
+ },
+ },
+ [SAA7134_BOARD_TYPHOON_90031] = {
+ /* aka Typhoon "TV+Radio", Art.Nr 90031 */
+ /* Tom Zoerner <tomzo at users sourceforge net> */
+ .name = "Typhoon TV+Radio 90031",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_ELSA] = {
+ .name = "ELSA EX-VISION 300TV",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_HITACHI_NTSC,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 4,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ },
+ [SAA7134_BOARD_ELSA_500TV] = {
+ .name = "ELSA EX-VISION 500TV",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_HITACHI_NTSC,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 7,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 8,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_tv_mono,
+ .vmux = 8,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ },
+ [SAA7134_BOARD_ELSA_700TV] = {
+ .name = "ELSA EX-VISION 700TV",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_HITACHI_NTSC,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 4,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 6,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 7,
+ .amux = LINE1,
+ }},
+ .mute = {
+ .name = name_mute,
+ .amux = TV,
+ },
+ },
+ [SAA7134_BOARD_ASUSTeK_TVFM7134] = {
+ .name = "ASUS TV-FM 7134",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 4,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 6,
+ .amux = LINE2,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE1,
+ },
+ },
+ [SAA7134_BOARD_ASUSTeK_TVFM7135] = {
+ .name = "ASUS TV-FM 7135",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x200000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .gpio = 0x0000,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 4,
+ .amux = LINE2,
+ .gpio = 0x0000,
+ },{
+ .name = name_svideo,
+ .vmux = 6,
+ .amux = LINE2,
+ .gpio = 0x0000,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x200000,
+ },
+ .mute = {
+ .name = name_mute,
+ .gpio = 0x0000,
+ },
+
+ },
+ [SAA7134_BOARD_VA1000POWER] = {
+ .name = "AOPEN VA1000 POWER",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_NTSC,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ },
+ [SAA7134_BOARD_10MOONSTVMASTER] = {
+ /* "lilicheng" <llc@linuxfans.org> */
+ .name = "10MOONS PCI TV CAPTURE CARD",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0xe000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .gpio = 0x0000,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ .gpio = 0x4000,
+ },{
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE2,
+ .gpio = 0x4000,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ .gpio = 0x4000,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0x2000,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = LINE2,
+ .gpio = 0x8000,
+ },
+ },
+ [SAA7134_BOARD_BMK_MPEX_NOTUNER] = {
+ /* "Andrew de Quincey" <adq@lidskialf.net> */
+ .name = "BMK MPEX No Tuner",
+ .audio_clock = 0x200000,
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .empress_addr = 0x20,
+ .inputs = {{
+ .name = name_comp1,
+ .vmux = 4,
+ .amux = LINE1,
+ },{
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_comp3,
+ .vmux = 0,
+ .amux = LINE1,
+ },{
+ .name = name_comp4,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .mpeg = SAA7134_MPEG_EMPRESS,
+ .video_out = CCIR656,
+ },
+ [SAA7134_BOARD_VIDEOMATE_TV] = {
+ .name = "Compro VideoMate TV",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ },
+ [SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUS] = {
+ .name = "Compro VideoMate TV Gold+",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .gpiomask = 0x800c0000,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ .gpio = 0x06c00012,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ .gpio = 0x0ac20012,
+ },{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .gpio = 0x08c20012,
+ .tv = 1,
+ }}, /* radio and probably mute is missing */
+ },
+ [SAA7134_BOARD_CRONOS_PLUS] = {
+ /*
+ gpio pins:
+ 0 .. 3 BASE_ID
+ 4 .. 7 PROTECT_ID
+ 8 .. 11 USER_OUT
+ 12 .. 13 USER_IN
+ 14 .. 15 VIDIN_SEL
+ */
+ .name = "Matrox CronosPlus",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0xcf00,
+ .inputs = {{
+ .name = name_comp1,
+ .vmux = 0,
+ .gpio = 2 << 14,
+ },{
+ .name = name_comp2,
+ .vmux = 0,
+ .gpio = 1 << 14,
+ },{
+ .name = name_comp3,
+ .vmux = 0,
+ .gpio = 0 << 14,
+ },{
+ .name = name_comp4,
+ .vmux = 0,
+ .gpio = 3 << 14,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .gpio = 2 << 14,
+ }},
+ },
+ [SAA7134_BOARD_MD2819] = {
+ .name = "AverMedia M156 / Medion 2819",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0x03,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x00,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ .gpio = 0x02,
+ }, {
+ .name = name_comp2,
+ .vmux = 0,
+ .amux = LINE1,
+ .gpio = 0x02,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ .gpio = 0x02,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE1,
+ .gpio = 0x01,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = TV,
+ .gpio = 0x00,
+ },
+ },
+ [SAA7134_BOARD_BMK_MPEX_TUNER] = {
+ /* "Greg Wickham <greg.wickham@grangenet.net> */
+ .name = "BMK MPEX Tuner",
+ .audio_clock = 0x200000,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .empress_addr = 0x20,
+ .inputs = {{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ }},
+ .mpeg = SAA7134_MPEG_EMPRESS,
+ .video_out = CCIR656,
+ },
+ [SAA7134_BOARD_ASUSTEK_TVFM7133] = {
+ .name = "ASUS TV-FM 7133",
+ .audio_clock = 0x00187de7,
+ /* probably wrong, the 7133 one is the NTSC version ...
+ * .tuner_type = TUNER_PHILIPS_FM1236_MK3 */
+ .tuner_type = TUNER_LG_NTSC_NEW_TAPC,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+
+ },{
+ .name = name_comp1,
+ .vmux = 4,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 6,
+ .amux = LINE2,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE1,
+ },
+ },
+ [SAA7134_BOARD_PINNACLE_PCTV_STEREO] = {
+ .name = "Pinnacle PCTV Stereo (saa7134)",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_MT2032,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER | TDA9887_PORT2_INACTIVE,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_comp2,
+ .vmux = 1,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ },
+ [SAA7134_BOARD_MANLI_MTV002] = {
+ /* Ognjen Nastic <ognjen@logosoft.ba> */
+ .name = "Manli MuchTV M-TV002",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_MANLI_MTV001] = {
+ /* Ognjen Nastic <ognjen@logosoft.ba> UNTESTED */
+ .name = "Manli MuchTV M-TV001",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ .mute = {
+ .name = name_mute,
+ .amux = LINE1,
+ },
+ },
+ [SAA7134_BOARD_TG3000TV] = {
+ /* TransGear 3000TV */
+ .name = "Nagase Sangyo TransGear 3000TV",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ },
+ [SAA7134_BOARD_ECS_TVP3XP] = {
+ .name = "Elitegroup ECS TVP3XP FM1216 Tuner Card(PAL-BG,FM) ",
+ .audio_clock = 0x187de7, /* xtal 32.1 MHz */
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = "CVid over SVid",
+ .vmux = 0,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_ECS_TVP3XP_4CB5] = {
+ .name = "Elitegroup ECS TVP3XP FM1236 Tuner Card (NTSC,FM)",
+ .audio_clock = 0x187de7,
+ .tuner_type = TUNER_PHILIPS_NTSC,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = "CVid over SVid",
+ .vmux = 0,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_ECS_TVP3XP_4CB6] = {
+ /* Barry Scott <barry.scott@onelan.co.uk> */
+ .name = "Elitegroup ECS TVP3XP FM1246 Tuner Card (PAL,FM)",
+ .audio_clock = 0x187de7,
+ .tuner_type = TUNER_PHILIPS_PAL_I,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = "CVid over SVid",
+ .vmux = 0,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_AVACSSMARTTV] = {
+ /* Roman Pszonczenko <romka@kolos.math.uni.lodz.pl> */
+ .name = "AVACS SmartTV",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0x200000,
+ },
+ },
+ [SAA7134_BOARD_AVERMEDIA_DVD_EZMAKER] = {
+ /* Michael Smith <msmith@cbnco.com> */
+ .name = "AVerMedia DVD EZMaker",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_comp1,
+ .vmux = 3,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ }},
+ },
+ [SAA7134_BOARD_AVERMEDIA_M103] = {
+ /* Massimo Piccioni <dafastidio@libero.it> */
+ .name = "AVerMedia MiniPCI DVB-T Hybrid M103",
+ .audio_clock = 0x187de7,
+ .tuner_type = TUNER_XC2028,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ } },
+ },
+ [SAA7134_BOARD_NOVAC_PRIMETV7133] = {
+ /* toshii@netbsd.org */
+ .name = "Noval Prime TV 7133",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_ALPS_TSBH1_NTSC,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_comp1,
+ .vmux = 3,
+ },{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ }},
+ },
+ [SAA7134_BOARD_AVERMEDIA_STUDIO_305] = {
+ .name = "AverMedia AverTV Studio 305",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1256_IH3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = LINE1,
+ },
+ },
+ [SAA7134_BOARD_AVERMEDIA_STUDIO_505] = {
+ /* Vasiliy Temnikov <vaka@newmail.ru> */
+ .name = "AverMedia AverTV Studio 505",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ }, {
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = LINE1,
+ },
+ },
+ [SAA7134_BOARD_UPMOST_PURPLE_TV] = {
+ .name = "UPMOST PURPLE TV",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1236_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 7,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_svideo,
+ .vmux = 7,
+ .amux = LINE1,
+ }},
+ },
+ [SAA7134_BOARD_ITEMS_MTV005] = {
+ /* Norman Jonas <normanjonas@arcor.de> */
+ .name = "Items MuchTV Plus / IT-005",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_CINERGY200] = {
+ .name = "Terratec Cinergy 200 TV",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 4,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp2, /* CVideo over SVideo Connector */
+ .vmux = 0,
+ .amux = LINE1,
+ }},
+ .mute = {
+ .name = name_mute,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_VIDEOMATE_TV_PVR] = {
+ /* Alain St-Denis <alain@topaze.homeip.net> */
+ .name = "Compro VideoMate TV PVR/FM",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x808c0080,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ .gpio = 0x00080,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ .gpio = 0x00080,
+ },{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2_LEFT,
+ .tv = 1,
+ .gpio = 0x00080,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0x80000,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = LINE2,
+ .gpio = 0x40000,
+ },
+ },
+ [SAA7134_BOARD_SABRENT_SBTTVFM] = {
+ /* Michael Rodriguez-Torrent <mrtorrent@asu.edu> */
+ .name = "Sabrent SBT-TVFM (saa7130)",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_ZOLID_XPERT_TV7134] = {
+ /* Helge Jensen <helge.jensen@slog.dk> */
+ .name = ":Zolid Xpert TV7134",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_NTSC,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ },
+ [SAA7134_BOARD_EMPIRE_PCI_TV_RADIO_LE] = {
+ /* "Matteo Az" <matte.az@nospam.libero.it> ;-) */
+ .name = "Empire PCI TV-Radio LE",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x4000,
+ .inputs = {{
+ .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .gpio = 0x8000,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ .gpio = 0x8000,
+ },{
+ .name = name_svideo,
+ .vmux = 6,
+ .amux = LINE1,
+ .gpio = 0x8000,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE1,
+ .gpio = 0x8000,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = TV,
+ .gpio =0x8000,
+ }
+ },
+ [SAA7134_BOARD_AVERMEDIA_STUDIO_307] = {
+ /*
+ Nickolay V. Shmyrev <nshmyrev@yandex.ru>
+ Lots of thanks to Andrey Zolotarev <zolotarev_andrey@mail.ru>
+ */
+ .name = "Avermedia AVerTV Studio 307",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1256_IH3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0x03,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x00,
+ },{
+ .name = name_comp,
+ .vmux = 3,
+ .amux = LINE1,
+ .gpio = 0x02,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ .gpio = 0x02,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE1,
+ .gpio = 0x01,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = LINE1,
+ .gpio = 0x00,
+ },
+ },
+ [SAA7134_BOARD_AVERMEDIA_GO_007_FM] = {
+ .name = "Avermedia AVerTV GO 007 FM",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x00300003,
+ /* .gpiomask = 0x8c240003, */
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x01,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE1,
+ .gpio = 0x02,
+ },{
+ .name = name_svideo,
+ .vmux = 6,
+ .amux = LINE1,
+ .gpio = 0x02,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x00300001,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = TV,
+ .gpio = 0x01,
+ },
+ },
+ [SAA7134_BOARD_AVERMEDIA_CARDBUS] = {
+ /* Kees.Blom@cwi.nl */
+ .name = "AVerMedia Cardbus TV/Radio (E500)",
+ .audio_clock = 0x187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE1,
+ },
+ },
+ [SAA7134_BOARD_AVERMEDIA_CARDBUS_501] = {
+ /* Oldrich Jedlicka <oldium.pro@seznam.cz> */
+ .name = "AVerMedia Cardbus TV/Radio (E501R)",
+ .audio_clock = 0x187de7,
+ .tuner_type = TUNER_ALPS_TSBE5_PAL,
+ .radio_type = TUNER_TEA5767,
+ .tuner_addr = 0x61,
+ .radio_addr = 0x60,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0x08000000,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x08000000,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ .gpio = 0x08000000,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ .gpio = 0x08000000,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0x00000000,
+ },
+ },
+ [SAA7134_BOARD_CINERGY400_CARDBUS] = {
+ .name = "Terratec Cinergy 400 mobile",
+ .audio_clock = 0x187de7,
+ .tuner_type = TUNER_ALPS_TSBE5_PAL,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ },
+ [SAA7134_BOARD_CINERGY600_MK3] = {
+ .name = "Terratec Cinergy 600 TV MK3",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .rds_addr = 0x10,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 4,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp2, /* CVideo over SVideo Connector */
+ .vmux = 0,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_VIDEOMATE_GOLD_PLUS] = {
+ /* Dylan Walkden <dylan_walkden@hotmail.com> */
+ .name = "Compro VideoMate Gold+ Pal",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x1ce780,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 0, /* CVideo over SVideo Connector - ok? */
+ .amux = LINE1,
+ .gpio = 0x008080,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ .gpio = 0x008080,
+ },{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x008080,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0x80000,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = LINE2,
+ .gpio = 0x0c8000,
+ },
+ },
+ [SAA7134_BOARD_PINNACLE_300I_DVBT_PAL] = {
+ .name = "Pinnacle PCTV 300i DVB-T + PAL",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_MT2032,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER | TDA9887_PORT2_INACTIVE,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_comp2,
+ .vmux = 1,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ },
+ [SAA7134_BOARD_PROVIDEO_PV952] = {
+ /* andreas.kretschmer@web.de */
+ .name = "ProVideo PV952",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_AVERMEDIA_305] = {
+ /* much like the "studio" version but without radio
+ * and another tuner (sirspiritus@yandex.ru) */
+ .name = "AverMedia AverTV/305",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FQ1216ME,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ .mute = {
+ .name = name_mute,
+ .amux = LINE1,
+ },
+ },
+ [SAA7134_BOARD_FLYDVBTDUO] = {
+ /* LifeView FlyDVB-T DUO */
+ /* "Nico Sabbi <nsabbi@tiscali.it> Hartmut Hackmann hartmut.hackmann@t-online.de*/
+ .name = "LifeView FlyDVB-T DUO / MSI TV@nywhere Duo",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x00200000,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .gpio = 0x200000, /* GPIO21=High for TV input */
+ .tv = 1,
+ },{
+ .name = name_comp1, /* Composite signal on S-Video input */
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_comp2, /* Composite input */
+ .vmux = 3,
+ .amux = LINE2,
+ },{
+ .name = name_svideo, /* S-Video signal on S-Video input */
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x000000, /* GPIO21=Low for FM radio antenna */
+ },
+ },
+ [SAA7134_BOARD_PHILIPS_TOUGH] = {
+ .name = "Philips TOUGH DVB-T reference design",
+ .tuner_type = TUNER_ABSENT,
+ .audio_clock = 0x00187de7,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ },
+ [SAA7134_BOARD_AVERMEDIA_307] = {
+ /*
+ Davydov Vladimir <vladimir@iqmedia.com>
+ */
+ .name = "Avermedia AVerTV 307",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FQ1216ME,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE1,
+ },{
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ },
+ [SAA7134_BOARD_ADS_INSTANT_TV] = {
+ .name = "ADS Tech Instant TV (saa7135)",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ },
+ [SAA7134_BOARD_KWORLD_VSTREAM_XPERT] = {
+ .name = "Kworld/Tevion V-Stream Xpert TV PVR7134",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_PAL_I,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x0700,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x000,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ .gpio = 0x200, /* gpio by DScaler */
+ },{
+ .name = name_svideo,
+ .vmux = 0,
+ .amux = LINE1,
+ .gpio = 0x200,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE1,
+ .gpio = 0x100,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = TV,
+ .gpio = 0x000,
+ },
+ },
+ [SAA7134_BOARD_FLYDVBT_DUO_CARDBUS] = {
+ .name = "LifeView/Typhoon/Genius FlyDVB-T Duo Cardbus",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .gpiomask = 0x00200000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .gpio = 0x200000, /* GPIO21=High for TV input */
+ .tv = 1,
+ },{
+ .name = name_svideo, /* S-Video signal on S-Video input */
+ .vmux = 8,
+ .amux = LINE2,
+ },{
+ .name = name_comp1, /* Composite signal on S-Video input */
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_comp2, /* Composite input */
+ .vmux = 3,
+ .amux = LINE2,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x000000, /* GPIO21=Low for FM radio antenna */
+ },
+ },
+ [SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII] = {
+ .name = "Compro VideoMate TV Gold+II",
+ .audio_clock = 0x002187de7,
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .radio_type = TUNER_TEA5767,
+ .tuner_addr = 0x63,
+ .radio_addr = 0x60,
+ .gpiomask = 0x8c1880,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 0,
+ .amux = LINE1,
+ .gpio = 0x800800,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ .gpio = 0x801000,
+ },{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x800000,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x880000,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = LINE2,
+ .gpio = 0x840000,
+ },
+ },
+ [SAA7134_BOARD_KWORLD_XPERT] = {
+ /*
+ FIXME:
+ - Remote control doesn't initialize properly.
+ - Audio volume starts muted,
+ then gradually increases after channel change.
+ - Overlay scaling problems (application error?)
+ - Composite S-Video untested.
+ From: Konrad Rzepecki <hannibal@megapolis.pl>
+ */
+ .name = "Kworld Xpert TV PVR7134",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_TENA_9533_DI,
+ .radio_type = TUNER_TEA5767,
+ .tuner_addr = 0x61,
+ .radio_addr = 0x60,
+ .gpiomask = 0x0700,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x000,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ .gpio = 0x200, /* gpio by DScaler */
+ },{
+ .name = name_svideo,
+ .vmux = 0,
+ .amux = LINE1,
+ .gpio = 0x200,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE1,
+ .gpio = 0x100,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = TV,
+ .gpio = 0x000,
+ },
+ },
+ [SAA7134_BOARD_FLYTV_DIGIMATRIX] = {
+ .name = "FlyTV mini Asus Digimatrix",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_LG_TALN,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ .radio = {
+ .name = name_radio, /* radio unconfirmed */
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_KWORLD_TERMINATOR] = {
+ /* Kworld V-Stream Studio TV Terminator */
+ /* "James Webb <jrwebb@qwest.net> */
+ .name = "V-Stream Studio TV Terminator",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 1 << 21,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .gpio = 0x0000000,
+ .tv = 1,
+ },{
+ .name = name_comp1, /* Composite input */
+ .vmux = 3,
+ .amux = LINE2,
+ .gpio = 0x0000000,
+ },{
+ .name = name_svideo, /* S-Video input */
+ .vmux = 8,
+ .amux = LINE2,
+ .gpio = 0x0000000,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x0200000,
+ },
+ },
+ [SAA7134_BOARD_YUAN_TUN900] = {
+ /* FIXME:
+ * S-Video and composite sources untested.
+ * Radio not working.
+ * Remote control not yet implemented.
+ * From : codemaster@webgeeks.be */
+ .name = "Yuan TUN-900 (saa7135)",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr= ADDR_UNSET,
+ .radio_addr= ADDR_UNSET,
+ .gpiomask = 0x00010003,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x01,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ .gpio = 0x02,
+ },{
+ .name = name_svideo,
+ .vmux = 6,
+ .amux = LINE2,
+ .gpio = 0x02,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE1,
+ .gpio = 0x00010003,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = TV,
+ .gpio = 0x01,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_409FM] = {
+ /* <http://tuner.beholder.ru>, Sergey <skiv@orel.ru> */
+ /* Beholder Intl. Ltd. 2008 */
+ /*Dmitry Belimov <d.belimov@gmail.com> */
+ .name = "Beholder BeholdTV 409 FM",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0x00008000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_GOTVIEW_7135] = {
+ /* Mike Baikov <mike@baikov.com> */
+ /* Andrey Cvetcov <ays14@yandex.ru> */
+ .name = "GoTView 7135 PCI",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0x00200003,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x00200003,
+ },{
+ .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .gpio = 0x00200003,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ .gpio = 0x00200003,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ .gpio = 0x00200003,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0x00200003,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = TV,
+ .gpio = 0x00200003,
+ },
+ },
+ [SAA7134_BOARD_PHILIPS_EUROPA] = {
+ .name = "Philips EUROPA V3 reference design",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TD1316,
+ .radio_type = UNSET,
+ .tuner_addr = 0x61,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ },
+ [SAA7134_BOARD_VIDEOMATE_DVBT_300] = {
+ .name = "Compro Videomate DVB-T300",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TD1316,
+ .radio_type = UNSET,
+ .tuner_addr = 0x61,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ },
+ [SAA7134_BOARD_VIDEOMATE_DVBT_200] = {
+ .name = "Compro Videomate DVB-T200",
+ .tuner_type = TUNER_ABSENT,
+ .audio_clock = 0x00187de7,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ },
+ [SAA7134_BOARD_RTD_VFG7350] = {
+ .name = "RTD Embedded Technologies VFG7350",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .empress_addr = 0x21,
+ .inputs = {{
+ .name = "Composite 0",
+ .vmux = 0,
+ .amux = LINE1,
+ },{
+ .name = "Composite 1",
+ .vmux = 1,
+ .amux = LINE2,
+ },{
+ .name = "Composite 2",
+ .vmux = 2,
+ .amux = LINE1,
+ },{
+ .name = "Composite 3",
+ .vmux = 3,
+ .amux = LINE2,
+ },{
+ .name = "S-Video 0",
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = "S-Video 1",
+ .vmux = 9,
+ .amux = LINE2,
+ }},
+ .mpeg = SAA7134_MPEG_EMPRESS,
+ .video_out = CCIR656,
+ .vid_port_opts = ( SET_T_CODE_POLARITY_NON_INVERTED |
+ SET_CLOCK_NOT_DELAYED |
+ SET_CLOCK_INVERTED |
+ SET_VSYNC_OFF ),
+ },
+ [SAA7134_BOARD_RTD_VFG7330] = {
+ .name = "RTD Embedded Technologies VFG7330",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = "Composite 0",
+ .vmux = 0,
+ .amux = LINE1,
+ },{
+ .name = "Composite 1",
+ .vmux = 1,
+ .amux = LINE2,
+ },{
+ .name = "Composite 2",
+ .vmux = 2,
+ .amux = LINE1,
+ },{
+ .name = "Composite 3",
+ .vmux = 3,
+ .amux = LINE2,
+ },{
+ .name = "S-Video 0",
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = "S-Video 1",
+ .vmux = 9,
+ .amux = LINE2,
+ }},
+ },
+ [SAA7134_BOARD_FLYTVPLATINUM_MINI2] = {
+ .name = "LifeView FlyTV Platinum Mini2",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1, /* Composite signal on S-Video input */
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_comp2, /* Composite input */
+ .vmux = 3,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ },
+ [SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180] = {
+ /* Michael Krufky <mkrufky@m1k.net>
+ * Uses Alps Electric TDHU2, containing NXT2004 ATSC Decoder
+ * AFAIK, there is no analog demod, thus,
+ * no support for analog television.
+ */
+ .name = "AVerMedia AVerTVHD MCE A180",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ },
+ [SAA7134_BOARD_MONSTERTV_MOBILE] = {
+ .name = "SKNet MonsterTV Mobile",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 6,
+ .amux = LINE1,
+ }},
+ },
+ [SAA7134_BOARD_PINNACLE_PCTV_110i] = {
+ .name = "Pinnacle PCTV 40i/50i/110i (saa7133)",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x080200000,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 4,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE2,
+ }, {
+ .name = name_comp2,
+ .vmux = 0,
+ .amux = LINE2,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x0200000,
+ },
+ },
+ [SAA7134_BOARD_ASUSTeK_P7131_DUAL] = {
+ .name = "ASUSTeK P7131 Dual",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 1 << 21,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x0000000,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE2,
+ .gpio = 0x0200000,
+ },{
+ .name = name_comp2,
+ .vmux = 0,
+ .amux = LINE2,
+ .gpio = 0x0200000,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ .gpio = 0x0200000,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x0200000,
+ },
+ },
+ [SAA7134_BOARD_SEDNA_PC_TV_CARDBUS] = {
+ /* Paul Tom Zalac <pzalac@gmail.com> */
+ /* Pavel Mihaylov <bin@bash.info> */
+ .name = "Sedna/MuchTV PC TV Cardbus TV/Radio (ITO25 Rev:2B)",
+ /* Sedna/MuchTV (OEM) Cardbus TV Tuner */
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0xe880c0,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 6,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_ASUSTEK_DIGIMATRIX_TV] = {
+ /* "Cyril Lacoux (Yack)" <clacoux@ifeelgood.org> */
+ .name = "ASUS Digimatrix TV",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_FQ1216ME,
+ .tda9887_conf = TDA9887_PRESENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ },
+ [SAA7134_BOARD_PHILIPS_TIGER] = {
+ .name = "Philips Tiger reference design",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tuner_config = 0,
+ .mpeg = SAA7134_MPEG_DVB,
+ .gpiomask = 0x0200000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x0200000,
+ },
+ },
+ [SAA7134_BOARD_MSI_TVATANYWHERE_PLUS] = {
+ .name = "MSI TV@Anywhere plus",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 1 << 21,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE2, /* unconfirmed, taken from Philips driver */
+ },{
+ .name = name_comp2,
+ .vmux = 0, /* untested, Composite over S-Video */
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x0200000,
+ },
+ },
+ [SAA7134_BOARD_CINERGY250PCI] = {
+ /* remote-control does not work. The signal about a
+ key press comes in via gpio, but the key code
+ doesn't. Neither does it have an i2c remote control
+ interface. */
+ .name = "Terratec Cinergy 250 PCI TV",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x80200000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_svideo, /* NOT tested */
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x0200000,
+ },
+ },
+ [SAA7134_BOARD_FLYDVB_TRIO] = {
+ /* LifeView LR319 FlyDVB Trio */
+ /* Peter Missel <peter.missel@onlinehome.de> */
+ .name = "LifeView FlyDVB Trio",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x00200000,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv, /* Analog broadcast/cable TV */
+ .vmux = 1,
+ .amux = TV,
+ .gpio = 0x200000, /* GPIO21=High for TV input */
+ .tv = 1,
+ },{
+ .name = name_svideo, /* S-Video signal on S-Video input */
+ .vmux = 8,
+ .amux = LINE2,
+ },{
+ .name = name_comp1, /* Composite signal on S-Video input */
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_comp2, /* Composite input */
+ .vmux = 3,
+ .amux = LINE2,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x000000, /* GPIO21=Low for FM radio antenna */
+ },
+ },
+ [SAA7134_BOARD_AVERMEDIA_777] = {
+ .name = "AverTV DVB-T 777",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ },
+ [SAA7134_BOARD_FLYDVBT_LR301] = {
+ /* LifeView FlyDVB-T */
+ /* Giampiero Giancipoli <gianci@libero.it> */
+ .name = "LifeView FlyDVB-T / Genius VideoWonder DVB-T",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_comp1, /* Composite input */
+ .vmux = 3,
+ .amux = LINE2,
+ },{
+ .name = name_svideo, /* S-Video signal on S-Video input */
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ },
+ [SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331] = {
+ .name = "ADS Instant TV Duo Cardbus PTV331",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .gpiomask = 0x00600000, /* Bit 21 0=Radio, Bit 22 0=TV */
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x00200000,
+ }},
+ },
+ [SAA7134_BOARD_TEVION_DVBT_220RF] = {
+ .name = "Tevion/KWorld DVB-T 220RF",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .gpiomask = 1 << 21,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_comp2,
+ .vmux = 0,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x0200000,
+ },
+ },
+ [SAA7134_BOARD_KWORLD_DVBT_210] = {
+ .name = "KWorld DVB-T 210",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .gpiomask = 1 << 21,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x0200000,
+ },
+ },
+ [SAA7134_BOARD_KWORLD_ATSC110] = {
+ .name = "Kworld ATSC110/115",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TUV1236D,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ },
+ [SAA7134_BOARD_AVERMEDIA_A169_B] = {
+ /* AVerMedia A169 */
+ /* Rickard Osser <ricky@osser.se> */
+ /* This card has two saa7134 chips on it,
+ but only one of them is currently working. */
+ .name = "AVerMedia A169 B",
+ .audio_clock = 0x02187de7,
+ .tuner_type = TUNER_LG_TALN,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0x0a60000,
+ },
+ [SAA7134_BOARD_AVERMEDIA_A169_B1] = {
+ /* AVerMedia A169 */
+ /* Rickard Osser <ricky@osser.se> */
+ .name = "AVerMedia A169 B1",
+ .audio_clock = 0x02187de7,
+ .tuner_type = TUNER_LG_TALN,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0xca60000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 4,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x04a61000,
+ },{
+ .name = name_comp2, /* Composite SVIDEO (B/W if signal is carried with SVIDEO) */
+ .vmux = 1,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 9, /* 9 is correct as S-VIDEO1 according to a169.inf! */
+ .amux = LINE1,
+ }},
+ },
+ [SAA7134_BOARD_MD7134_BRIDGE_2] = {
+ /* The second saa7134 on this card only serves as DVB-S host bridge */
+ .name = "Medion 7134 Bridge #2",
+ .audio_clock = 0x00187de7,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ },
+ [SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS] = {
+ .name = "LifeView FlyDVB-T Hybrid Cardbus/MSI TV @nywhere A/D NB",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .gpiomask = 0x00600000, /* Bit 21 0=Radio, Bit 22 0=TV */
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .gpio = 0x200000, /* GPIO21=High for TV input */
+ .tv = 1,
+ },{
+ .name = name_svideo, /* S-Video signal on S-Video input */
+ .vmux = 8,
+ .amux = LINE2,
+ },{
+ .name = name_comp1, /* Composite signal on S-Video input */
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_comp2, /* Composite input */
+ .vmux = 3,
+ .amux = LINE2,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x000000, /* GPIO21=Low for FM radio antenna */
+ },
+ },
+ [SAA7134_BOARD_FLYVIDEO3000_NTSC] = {
+ /* "Zac Bowling" <zac@zacbowling.com> */
+ .name = "LifeView FlyVIDEO3000 (NTSC)",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_NTSC,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+
+ .gpiomask = 0xe000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .gpio = 0x8000,
+ .tv = 1,
+ },{
+ .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .gpio = 0x0000,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ .gpio = 0x4000,
+ },{
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE2,
+ .gpio = 0x4000,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ .gpio = 0x4000,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0x2000,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = TV,
+ .gpio = 0x8000,
+ },
+ },
+ [SAA7134_BOARD_MEDION_MD8800_QUADRO] = {
+ .name = "Medion Md8800 Quadro",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ },
+ [SAA7134_BOARD_FLYDVBS_LR300] = {
+ /* LifeView FlyDVB-s */
+ /* Igor M. Liplianin <liplianin@tut.by> */
+ .name = "LifeView FlyDVB-S /Acorp TV134DS",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_comp1, /* Composite input */
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_svideo, /* S-Video signal on S-Video input */
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ },
+ [SAA7134_BOARD_PROTEUS_2309] = {
+ .name = "Proteus Pro 2309",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ .mute = {
+ .name = name_mute,
+ .amux = LINE1,
+ },
+ },
+ [SAA7134_BOARD_AVERMEDIA_A16AR] = {
+ /* Petr Baudis <pasky@ucw.cz> */
+ .name = "AVerMedia TV Hybrid A16AR",
+ .audio_clock = 0x187de7,
+ .tuner_type = TUNER_PHILIPS_TD1316, /* untested */
+ .radio_type = TUNER_TEA5767, /* untested */
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = 0x60,
+ .tda9887_conf = TDA9887_PRESENT,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE1,
+ },
+ },
+ [SAA7134_BOARD_ASUS_EUROPA2_HYBRID] = {
+ .name = "Asus Europa2 OEM",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT| TDA9887_PORT1_ACTIVE | TDA9887_PORT2_ACTIVE,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 4,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE1,
+ },
+ },
+ [SAA7134_BOARD_PINNACLE_PCTV_310i] = {
+ .name = "Pinnacle PCTV 310i",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tuner_config = 1,
+ .mpeg = SAA7134_MPEG_DVB,
+ .gpiomask = 0x000200000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 4,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE2,
+ },{
+ .name = name_comp2,
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x0200000,
+ },
+ },
+ [SAA7134_BOARD_AVERMEDIA_STUDIO_507] = {
+ /* Mikhail Fedotov <mo_fedotov@mail.ru> */
+ .name = "Avermedia AVerTV Studio 507",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1256_IH3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0x03,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x00,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ .gpio = 0x00,
+ },{
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE2,
+ .gpio = 0x00,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ .gpio = 0x00,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0x01,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = LINE1,
+ .gpio = 0x00,
+ },
+ },
+ [SAA7134_BOARD_VIDEOMATE_DVBT_200A] = {
+ /* Francis Barber <fedora@barber-family.id.au> */
+ .name = "Compro Videomate DVB-T200A",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ },
+ [SAA7134_BOARD_HAUPPAUGE_HVR1110] = {
+ /* Thomas Genty <tomlohave@gmail.com> */
+ /* David Bentham <db260179@hotmail.com> */
+ .name = "Hauppauge WinTV-HVR1110 DVB-T/Hybrid",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tuner_config = 1,
+ .mpeg = SAA7134_MPEG_DVB,
+ .gpiomask = 0x0200100,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x0000100,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x0200100,
+ },
+ },
+ [SAA7134_BOARD_HAUPPAUGE_HVR1150] = {
+ .name = "Hauppauge WinTV-HVR1150 ATSC/QAM-Hybrid",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tuner_config = 3,
+ .mpeg = SAA7134_MPEG_DVB,
+ .ts_type = SAA7134_MPEG_TS_SERIAL,
+ .ts_force_val = 1,
+ .gpiomask = 0x0800100, /* GPIO 21 is an INPUT */
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x0000100,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x0800100, /* GPIO 23 HI for FM */
+ },
+ },
+ [SAA7134_BOARD_HAUPPAUGE_HVR1120] = {
+ .name = "Hauppauge WinTV-HVR1120 DVB-T/Hybrid",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tuner_config = 3,
+ .mpeg = SAA7134_MPEG_DVB,
+ .ts_type = SAA7134_MPEG_TS_SERIAL,
+ .gpiomask = 0x0800100, /* GPIO 21 is an INPUT */
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x0000100,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x0800100, /* GPIO 23 HI for FM */
+ },
+ },
+ [SAA7134_BOARD_CINERGY_HT_PCMCIA] = {
+ .name = "Terratec Cinergy HT PCMCIA",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 6,
+ .amux = LINE1,
+ }},
+ },
+ [SAA7134_BOARD_ENCORE_ENLTV] = {
+ /* Steven Walter <stevenrwalter@gmail.com>
+ Juan Pablo Sormani <sorman@gmail.com> */
+ .name = "Encore ENLTV",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_TNF_5335MF,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = 3,
+ .tv = 1,
+ },{
+ .name = name_tv_mono,
+ .vmux = 7,
+ .amux = 4,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = 2,
+ },{
+ .name = name_svideo,
+ .vmux = 0,
+ .amux = 2,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+/* .gpio = 0x00300001,*/
+ .gpio = 0x20000,
+
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = 0,
+ },
+ },
+ [SAA7134_BOARD_ENCORE_ENLTV_FM] = {
+ /* Juan Pablo Sormani <sorman@gmail.com> */
+ .name = "Encore ENLTV-FM",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_FCV1236D,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = 3,
+ .tv = 1,
+ },{
+ .name = name_tv_mono,
+ .vmux = 7,
+ .amux = 4,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = 2,
+ },{
+ .name = name_svideo,
+ .vmux = 0,
+ .amux = 2,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0x20000,
+
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = 0,
+ },
+ },
+ [SAA7134_BOARD_ENCORE_ENLTV_FM53] = {
+ .name = "Encore ENLTV-FM v5.3",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_TNF_5335MF,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x7000,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 1,
+ .amux = 1,
+ .tv = 1,
+ .gpio = 0x50000,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = 2,
+ .gpio = 0x2000,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = 2,
+ .gpio = 0x2000,
+ } },
+ .radio = {
+ .name = name_radio,
+ .vmux = 1,
+ .amux = 1,
+ },
+ .mute = {
+ .name = name_mute,
+ .gpio = 0xf000,
+ .amux = 0,
+ },
+ },
+ [SAA7134_BOARD_ENCORE_ENLTV_FM3] = {
+ .name = "Encore ENLTV-FM 3",
+ .audio_clock = 0x02187de7,
+ .tuner_type = TUNER_TENA_TNF_5337,
+ .radio_type = TUNER_TEA5767,
+ .tuner_addr = 0x61,
+ .radio_addr = 0x60,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .vmux = 1,
+ .amux = LINE1,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = LINE1,
+ .gpio = 0x43000,
+ },
+ },
+ [SAA7134_BOARD_CINERGY_HT_PCI] = {
+ .name = "Terratec Cinergy HT PCI",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 6,
+ .amux = LINE1,
+ }},
+ },
+ [SAA7134_BOARD_PHILIPS_TIGER_S] = {
+ .name = "Philips Tiger - S Reference design",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tuner_config = 2,
+ .mpeg = SAA7134_MPEG_DVB,
+ .gpiomask = 0x0200000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x0200000,
+ },
+ },
+ [SAA7134_BOARD_AVERMEDIA_M102] = {
+ .name = "Avermedia M102",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 1<<21,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 6,
+ .amux = LINE2,
+ }},
+ },
+ [SAA7134_BOARD_ASUS_P7131_4871] = {
+ .name = "ASUS P7131 4871",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tuner_config = 2,
+ .mpeg = SAA7134_MPEG_DVB,
+ .gpiomask = 0x0200000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x0200000,
+ }},
+ },
+ [SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA] = {
+ .name = "ASUSTeK P7131 Hybrid",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tuner_config = 2,
+ .gpiomask = 1 << 21,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x0000000,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE2,
+ .gpio = 0x0200000,
+ },{
+ .name = name_comp2,
+ .vmux = 0,
+ .amux = LINE2,
+ .gpio = 0x0200000,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ .gpio = 0x0200000,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x0200000,
+ },
+ },
+ [SAA7134_BOARD_ASUSTeK_P7131_ANALOG] = {
+ .name = "ASUSTeK P7131 Analog",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 1 << 21,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x0000000,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE2,
+ }, {
+ .name = name_comp2,
+ .vmux = 0,
+ .amux = LINE2,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x0200000,
+ },
+ },
+ [SAA7134_BOARD_SABRENT_TV_PCB05] = {
+ .name = "Sabrent PCMCIA TV-PCB05",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_comp2,
+ .vmux = 0,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .mute = {
+ .name = name_mute,
+ .amux = TV,
+ },
+ },
+ [SAA7134_BOARD_10MOONSTVMASTER3] = {
+ /* Tony Wan <aloha_cn@hotmail.com> */
+ .name = "10MOONS TM300 TV Card",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x7000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .gpio = 0x0000,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ .gpio = 0x2000,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ .gpio = 0x2000,
+ }},
+ .mute = {
+ .name = name_mute,
+ .amux = LINE2,
+ .gpio = 0x3000,
+ },
+ },
+ [SAA7134_BOARD_AVERMEDIA_SUPER_007] = {
+ .name = "Avermedia Super 007",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tuner_config = 0,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv, /* FIXME: analog tv untested */
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ }},
+ },
+ [SAA7134_BOARD_AVERMEDIA_M135A] = {
+ .name = "Avermedia PCI pure analog (M135A)",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tuner_config = 2,
+ .gpiomask = 0x020200000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x00200000,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = TV,
+ .gpio = 0x01,
+ },
+ },
+ [SAA7134_BOARD_AVERMEDIA_M733A] = {
+ .name = "Avermedia PCI M733A",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tuner_config = 0,
+ .gpiomask = 0x020200000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x00200000,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = TV,
+ .gpio = 0x01,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_401] = {
+ /* Beholder Intl. Ltd. 2008 */
+ /*Dmitry Belimov <d.belimov@gmail.com> */
+ .name = "Beholder BeholdTV 401",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FQ1216ME,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x00008000,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ .mute = {
+ .name = name_mute,
+ .amux = LINE1,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_403] = {
+ /* Beholder Intl. Ltd. 2008 */
+ /*Dmitry Belimov <d.belimov@gmail.com> */
+ .name = "Beholder BeholdTV 403",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FQ1216ME,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x00008000,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ },
+ [SAA7134_BOARD_BEHOLD_403FM] = {
+ /* Beholder Intl. Ltd. 2008 */
+ /*Dmitry Belimov <d.belimov@gmail.com> */
+ .name = "Beholder BeholdTV 403 FM",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FQ1216ME,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x00008000,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_405] = {
+ /* Beholder Intl. Ltd. 2008 */
+ /*Dmitry Belimov <d.belimov@gmail.com> */
+ .name = "Beholder BeholdTV 405",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0x00008000,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ },
+ [SAA7134_BOARD_BEHOLD_405FM] = {
+ /* Sergey <skiv@orel.ru> */
+ /* Beholder Intl. Ltd. 2008 */
+ /*Dmitry Belimov <d.belimov@gmail.com> */
+ .name = "Beholder BeholdTV 405 FM",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0x00008000,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_407] = {
+ /* Beholder Intl. Ltd. 2008 */
+ /*Dmitry Belimov <d.belimov@gmail.com> */
+ .name = "Beholder BeholdTV 407",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0x00008000,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ .gpio = 0xc0c000,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ .gpio = 0xc0c000,
+ },{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0xc0c000,
+ }},
+ },
+ [SAA7134_BOARD_BEHOLD_407FM] = {
+ /* Beholder Intl. Ltd. 2008 */
+ /*Dmitry Belimov <d.belimov@gmail.com> */
+ .name = "Beholder BeholdTV 407 FM",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0x00008000,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ .gpio = 0xc0c000,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ .gpio = 0xc0c000,
+ },{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0xc0c000,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0xc0c000,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_409] = {
+ /* Beholder Intl. Ltd. 2008 */
+ /*Dmitry Belimov <d.belimov@gmail.com> */
+ .name = "Beholder BeholdTV 409",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0x00008000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ },
+ [SAA7134_BOARD_BEHOLD_505FM] = {
+ /* Beholder Intl. Ltd. 2008 */
+ /*Dmitry Belimov <d.belimov@gmail.com> */
+ .name = "Beholder BeholdTV 505 FM",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0x00008000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = LINE2,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .mute = {
+ .name = name_mute,
+ .amux = LINE1,
+ },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_505RDS_MK5] = {
+ /* Beholder Intl. Ltd. 2008 */
+ /*Dmitry Belimov <d.belimov@gmail.com> */
+ .name = "Beholder BeholdTV 505 RDS",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_FM1216MK5,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .rds_addr = 0x10,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0x00008000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .mute = {
+ .name = name_mute,
+ .amux = LINE1,
+ },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_507_9FM] = {
+ /* Beholder Intl. Ltd. 2008 */
+ /*Dmitry Belimov <d.belimov@gmail.com> */
+ .name = "Beholder BeholdTV 507 FM / BeholdTV 509 FM",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0x00008000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_507RDS_MK5] = {
+ /* Beholder Intl. Ltd. 2008 */
+ /*Dmitry Belimov <d.belimov@gmail.com> */
+ .name = "Beholder BeholdTV 507 RDS",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216MK5,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .rds_addr = 0x10,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0x00008000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_507RDS_MK3] = {
+ /* Beholder Intl. Ltd. 2008 */
+ /*Dmitry Belimov <d.belimov@gmail.com> */
+ .name = "Beholder BeholdTV 507 RDS",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .rds_addr = 0x10,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0x00008000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM] = {
+ /* Beholder Intl. Ltd. 2008 */
+ /* Dmitry Belimov <d.belimov@gmail.com> */
+ .name = "Beholder BeholdTV Columbus TV/FM",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_ALPS_TSBE5_PAL,
+ .radio_type = TUNER_TEA5767,
+ .tuner_addr = 0xc2 >> 1,
+ .radio_addr = 0xc0 >> 1,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0x000A8004,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x000A8004,
+ }, {
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ .gpio = 0x000A8000,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ .gpio = 0x000A8000,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0x000A8000,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_607FM_MK3] = {
+ /* Andrey Melnikoff <temnota@kmv.ru> */
+ .name = "Beholder BeholdTV 607 FM",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_609FM_MK3] = {
+ /* Andrey Melnikoff <temnota@kmv.ru> */
+ .name = "Beholder BeholdTV 609 FM",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_607FM_MK5] = {
+ /* Andrey Melnikoff <temnota@kmv.ru> */
+ .name = "Beholder BeholdTV 607 FM",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216MK5,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_609FM_MK5] = {
+ /* Andrey Melnikoff <temnota@kmv.ru> */
+ .name = "Beholder BeholdTV 609 FM",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216MK5,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_607RDS_MK3] = {
+ /* Andrey Melnikoff <temnota@kmv.ru> */
+ .name = "Beholder BeholdTV 607 RDS",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .rds_addr = 0x10,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_609RDS_MK3] = {
+ /* Andrey Melnikoff <temnota@kmv.ru> */
+ .name = "Beholder BeholdTV 609 RDS",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .rds_addr = 0x10,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_607RDS_MK5] = {
+ /* Andrey Melnikoff <temnota@kmv.ru> */
+ .name = "Beholder BeholdTV 607 RDS",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216MK5,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .rds_addr = 0x10,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_609RDS_MK5] = {
+ /* Andrey Melnikoff <temnota@kmv.ru> */
+ .name = "Beholder BeholdTV 609 RDS",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216MK5,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .rds_addr = 0x10,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_M6] = {
+ /* Igor Kuznetsov <igk@igk.ru> */
+ /* Andrey Melnikoff <temnota@kmv.ru> */
+ /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */
+ /* Alexey Osipov <lion-simba@pridelands.ru> */
+ .name = "Beholder BeholdTV M6",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .empress_addr = 0x20,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ .mpeg = SAA7134_MPEG_EMPRESS,
+ .video_out = CCIR656,
+ .vid_port_opts = (SET_T_CODE_POLARITY_NON_INVERTED |
+ SET_CLOCK_NOT_DELAYED |
+ SET_CLOCK_INVERTED |
+ SET_VSYNC_OFF),
+ },
+ [SAA7134_BOARD_BEHOLD_M63] = {
+ /* Igor Kuznetsov <igk@igk.ru> */
+ /* Andrey Melnikoff <temnota@kmv.ru> */
+ /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */
+ .name = "Beholder BeholdTV M63",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .empress_addr = 0x20,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ .mpeg = SAA7134_MPEG_EMPRESS,
+ .video_out = CCIR656,
+ .vid_port_opts = (SET_T_CODE_POLARITY_NON_INVERTED |
+ SET_CLOCK_NOT_DELAYED |
+ SET_CLOCK_INVERTED |
+ SET_VSYNC_OFF),
+ },
+ [SAA7134_BOARD_BEHOLD_M6_EXTRA] = {
+ /* Igor Kuznetsov <igk@igk.ru> */
+ /* Andrey Melnikoff <temnota@kmv.ru> */
+ /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */
+ /* Alexey Osipov <lion-simba@pridelands.ru> */
+ .name = "Beholder BeholdTV M6 Extra",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216MK5,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .rds_addr = 0x10,
+ .empress_addr = 0x20,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ .mpeg = SAA7134_MPEG_EMPRESS,
+ .video_out = CCIR656,
+ .vid_port_opts = (SET_T_CODE_POLARITY_NON_INVERTED |
+ SET_CLOCK_NOT_DELAYED |
+ SET_CLOCK_INVERTED |
+ SET_VSYNC_OFF),
+ },
+ [SAA7134_BOARD_TWINHAN_DTV_DVB_3056] = {
+ .name = "Twinhan Hybrid DTV-DVB 3056 PCI",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tuner_config = 2,
+ .mpeg = SAA7134_MPEG_DVB,
+ .gpiomask = 0x0200000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8, /* untested */
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x0200000,
+ },
+ },
+ [SAA7134_BOARD_GENIUS_TVGO_A11MCE] = {
+ /* Adrian Pardini <pardo.bsso@gmail.com> */
+ .name = "Genius TVGO AM11MCE",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_TNF_5335MF,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0xf000,
+ .inputs = {{
+ .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .gpio = 0x0000,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ .gpio = 0x2000,
+ .tv = 1
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ .gpio = 0x2000,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0x1000,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = LINE2,
+ .gpio = 0x6000,
+ },
+ },
+ [SAA7134_BOARD_PHILIPS_SNAKE] = {
+ .name = "NXP Snake DVB-S reference design",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ },
+ [SAA7134_BOARD_CREATIX_CTX953] = {
+ .name = "Medion/Creatix CTX953 Hybrid",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tuner_config = 0,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ },
+ [SAA7134_BOARD_MSI_TVANYWHERE_AD11] = {
+ .name = "MSI TV@nywhere A/D v1.1",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tuner_config = 2,
+ .mpeg = SAA7134_MPEG_DVB,
+ .gpiomask = 0x0200000,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x0200000,
+ },
+ },
+ [SAA7134_BOARD_AVERMEDIA_CARDBUS_506] = {
+ .name = "AVerMedia Cardbus TV/Radio (E506R)",
+ .audio_clock = 0x187de7,
+ .tuner_type = TUNER_XC2028,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ },
+ },
+ [SAA7134_BOARD_AVERMEDIA_A16D] = {
+ .name = "AVerMedia Hybrid TV/Radio (A16D)",
+ .audio_clock = 0x187de7,
+ .tuner_type = TUNER_XC2028,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }, {
+ .name = name_comp,
+ .vmux = 0,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ },
+ },
+ [SAA7134_BOARD_AVERMEDIA_M115] = {
+ .name = "Avermedia M115",
+ .audio_clock = 0x187de7,
+ .tuner_type = TUNER_XC2028,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ } },
+ },
+ [SAA7134_BOARD_VIDEOMATE_T750] = {
+ /* John Newbigin <jn@it.swin.edu.au> */
+ .name = "Compro VideoMate T750",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_XC2028,
+ .radio_type = UNSET,
+ .tuner_addr = 0x61,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE2,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ }
+ },
+ [SAA7134_BOARD_AVERMEDIA_A700_PRO] = {
+ /* Matthias Schwarzott <zzam@gentoo.org> */
+ .name = "Avermedia DVB-S Pro A700",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = { {
+ .name = name_comp,
+ .vmux = 1,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 6,
+ .amux = LINE1,
+ } },
+ },
+ [SAA7134_BOARD_AVERMEDIA_A700_HYBRID] = {
+ /* Matthias Schwarzott <zzam@gentoo.org> */
+ .name = "Avermedia DVB-S Hybrid+FM A700",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_XC2028,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 4,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp,
+ .vmux = 1,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 6,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_H6] = {
+ /* Igor Kuznetsov <igk@igk.ru> */
+ .name = "Beholder BeholdTV H6",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FMD1216MEX_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_ASUSTeK_TIGER_3IN1] = {
+ .name = "Asus Tiger 3in1",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tuner_config = 2,
+ .gpiomask = 1 << 21,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp,
+ .vmux = 0,
+ .amux = LINE2,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x0200000,
+ },
+ },
+ [SAA7134_BOARD_REAL_ANGEL_220] = {
+ .name = "Zogis Real Angel 220",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_TNF_5335MF,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x801a8087,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 3,
+ .amux = LINE2,
+ .tv = 1,
+ .gpio = 0x624000,
+ }, {
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ .gpio = 0x624000,
+ }, {
+ .name = name_svideo,
+ .vmux = 1,
+ .amux = LINE1,
+ .gpio = 0x624000,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0x624001,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = TV,
+ },
+ },
+ [SAA7134_BOARD_ADS_INSTANT_HDTV_PCI] = {
+ .name = "ADS Tech Instant HDTV",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TUV1236D,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp,
+ .vmux = 4,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ },
+ [SAA7134_BOARD_ASUSTeK_TIGER] = {
+ .name = "Asus Tiger Rev:1.00",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tuner_config = 0,
+ .mpeg = SAA7134_MPEG_DVB,
+ .gpiomask = 0x0200000,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE2,
+ }, {
+ .name = name_comp2,
+ .vmux = 0,
+ .amux = LINE2,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x0200000,
+ },
+ },
+ [SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG] = {
+ .name = "Kworld Plus TV Analog Lite PCI",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_YMEC_TVF_5533MF,
+ .radio_type = TUNER_TEA5767,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = 0x60,
+ .gpiomask = 0x80000700,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ .gpio = 0x100,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ .gpio = 0x200,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ .gpio = 0x200,
+ } },
+ .radio = {
+ .name = name_radio,
+ .vmux = 1,
+ .amux = LINE1,
+ .gpio = 0x100,
+ },
+ .mute = {
+ .name = name_mute,
+ .vmux = 8,
+ .amux = 2,
+ },
+ },
+ [SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG] = {
+ .name = "Kworld PCI SBTVD/ISDB-T Full-Seg Hybrid",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .tuner_addr = ADDR_UNSET,
+ .radio_type = UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x8e054000,
+ .mpeg = SAA7134_MPEG_DVB,
+ .ts_type = SAA7134_MPEG_TS_PARALLEL,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+#if 0 /* FIXME */
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ .gpio = 0x200,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ .gpio = 0x200,
+#endif
+ } },
+#if 0
+ .radio = {
+ .name = name_radio,
+ .vmux = 1,
+ .amux = LINE1,
+ .gpio = 0x100,
+ },
+#endif
+ .mute = {
+ .name = name_mute,
+ .vmux = 0,
+ .amux = TV,
+ },
+ },
+ [SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS] = {
+ .name = "Avermedia AVerTV GO 007 FM Plus",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x00300003,
+ /* .gpiomask = 0x8c240003, */
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x01,
+ }, {
+ .name = name_svideo,
+ .vmux = 6,
+ .amux = LINE1,
+ .gpio = 0x02,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x00300001,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = TV,
+ .gpio = 0x01,
+ },
+ },
+ [SAA7134_BOARD_AVERMEDIA_STUDIO_507UA] = {
+ /* Andy Shevchenko <andy@smile.org.ua> */
+ .name = "Avermedia AVerTV Studio 507UA",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* Should be MK5 */
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0x03,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x00,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ .gpio = 0x00,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ .gpio = 0x00,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0x01,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = LINE1,
+ .gpio = 0x00,
+ },
+ },
+ [SAA7134_BOARD_VIDEOMATE_S350] = {
+ /* Jan D. Louw <jd.louw@mweb.co.za */
+ .name = "Compro VideoMate S350/S300",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = { {
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8, /* Not tested */
+ .amux = LINE1
+ } },
+ },
+ [SAA7134_BOARD_BEHOLD_X7] = {
+ /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */
+ .name = "Beholder BeholdTV X7",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_XC5000,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 2,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 9,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ },
+ },
+ [SAA7134_BOARD_ZOLID_HYBRID_PCI] = {
+ .name = "Zolid Hybrid TV Tuner PCI",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tuner_config = 0,
+ .mpeg = SAA7134_MPEG_DVB,
+ .ts_type = SAA7134_MPEG_TS_PARALLEL,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ } },
+ .radio = { /* untested */
+ .name = name_radio,
+ .amux = TV,
+ },
+ },
+ [SAA7134_BOARD_ASUS_EUROPA_HYBRID] = {
+ .name = "Asus Europa Hybrid OEM",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TD1316,
+ .radio_type = UNSET,
+ .tuner_addr = 0x61,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 4,
+ .amux = LINE2,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ } },
+ },
+ [SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S] = {
+ .name = "Leadtek Winfast DTV1000S",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = { {
+ .name = name_comp1,
+ .vmux = 3,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ } },
+ },
+ [SAA7134_BOARD_BEHOLD_505RDS_MK3] = {
+ /* Beholder Intl. Ltd. 2008 */
+ /*Dmitry Belimov <d.belimov@gmail.com> */
+ .name = "Beholder BeholdTV 505 RDS",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .rds_addr = 0x10,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0x00008000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = LINE2,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .mute = {
+ .name = name_mute,
+ .amux = LINE1,
+ },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_HAWELL_HW_404M7] = {
+ /* Hawell HW-404M7 & Hawell HW-808M7 */
+ /* Bogoslovskiy Viktor <bogovic@bk.ru> */
+ .name = "Hawell HW-404M7",
+ .audio_clock = 0x00200000,
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x389c00,
+ .inputs = {{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ .gpio = 0x01fc00,
+ } },
+ },
+ [SAA7134_BOARD_BEHOLD_H7] = {
+ /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */
+ .name = "Beholder BeholdTV H7",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_XC5000,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .ts_type = SAA7134_MPEG_TS_PARALLEL,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 2,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 9,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_A7] = {
+ /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */
+ .name = "Beholder BeholdTV A7",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_XC5000,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 2,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 9,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ },
+ },
+ [SAA7134_BOARD_TECHNOTREND_BUDGET_T3000] = {
+ .name = "TechoTrend TT-budget T-3000",
+ .tuner_type = TUNER_PHILIPS_TD1316,
+ .audio_clock = 0x00187de7,
+ .radio_type = UNSET,
+ .tuner_addr = 0x63,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ } },
+ },
+ [SAA7134_BOARD_VIDEOMATE_M1F] = {
+ /* Pavel Osnova <pvosnova@gmail.com> */
+ .name = "Compro VideoMate Vista M1F",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .radio_type = TUNER_TEA5767,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = 0x60,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE2,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE1,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = TV,
+ },
+ },
+ [SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2] = {
+ /* Timothy Lee <timothy.lee@siriushk.com> */
+ .name = "MagicPro ProHDTV Pro2 DMB-TH/Hybrid",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_config = 3,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x02050000,
+ .mpeg = SAA7134_MPEG_DVB,
+ .ts_type = SAA7134_MPEG_TS_PARALLEL,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x00050000,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ .gpio = 0x00050000,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ .gpio = 0x00050000,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x00050000,
+ },
+ .mute = {
+ .name = name_mute,
+ .vmux = 0,
+ .amux = TV,
+ .gpio = 0x00050000,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_501] = {
+ /* Beholder Intl. Ltd. 2010 */
+ /* Dmitry Belimov <d.belimov@gmail.com> */
+ .name = "Beholder BeholdTV 501",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x00008000,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 3,
+ .amux = LINE2,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .mute = {
+ .name = name_mute,
+ .amux = LINE1,
+ },
+ },
+ [SAA7134_BOARD_BEHOLD_503FM] = {
+ /* Beholder Intl. Ltd. 2010 */
+ /* Dmitry Belimov <d.belimov@gmail.com> */
+ .name = "Beholder BeholdTV 503 FM",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x00008000,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 3,
+ .amux = LINE2,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .mute = {
+ .name = name_mute,
+ .amux = LINE1,
+ },
+ },
+ [SAA7134_BOARD_SENSORAY811_911] = {
+ .name = "Sensoray 811/911",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE1,
+ }, {
+ .name = name_comp3,
+ .vmux = 2,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ },
+ [SAA7134_BOARD_KWORLD_PC150U] = {
+ .name = "Kworld PC150-U",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .gpiomask = 1 << 21,
+ .ts_type = SAA7134_MPEG_TS_PARALLEL,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp,
+ .vmux = 3,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x0000000,
+ },
+ },
+
+};
+
+const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards);
+
+/* ------------------------------------------------------------------ */
+/* PCI ids + subsystem IDs */
+
+struct pci_device_id saa7134_pci_tbl[] = {
+ {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = PCI_VENDOR_ID_PHILIPS,
+ .subdevice = 0x2001,
+ .driver_data = SAA7134_BOARD_PROTEUS_PRO,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = PCI_VENDOR_ID_PHILIPS,
+ .subdevice = 0x2001,
+ .driver_data = SAA7134_BOARD_PROTEUS_PRO,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = PCI_VENDOR_ID_PHILIPS,
+ .subdevice = 0x6752,
+ .driver_data = SAA7134_BOARD_EMPRESS,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1131,
+ .subdevice = 0x4e85,
+ .driver_data = SAA7134_BOARD_MONSTERTV,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x153b,
+ .subdevice = 0x1142,
+ .driver_data = SAA7134_BOARD_CINERGY400,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x153b,
+ .subdevice = 0x1143,
+ .driver_data = SAA7134_BOARD_CINERGY600,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x153b,
+ .subdevice = 0x1158,
+ .driver_data = SAA7134_BOARD_CINERGY600_MK3,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x153b,
+ .subdevice = 0x1162,
+ .driver_data = SAA7134_BOARD_CINERGY400_CARDBUS,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x5169,
+ .subdevice = 0x0138,
+ .driver_data = SAA7134_BOARD_FLYVIDEO3000_NTSC,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x5168,
+ .subdevice = 0x0138,
+ .driver_data = SAA7134_BOARD_FLYVIDEO3000,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x4e42, /* "Typhoon PCI Capture TV Card" Art.No. 50673 */
+ .subdevice = 0x0138,
+ .driver_data = SAA7134_BOARD_FLYVIDEO3000,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x5168,
+ .subdevice = 0x0138,
+ .driver_data = SAA7134_BOARD_FLYVIDEO2000,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x4e42, /* Typhoon */
+ .subdevice = 0x0138, /* LifeView FlyTV Prime30 OEM */
+ .driver_data = SAA7134_BOARD_FLYVIDEO2000,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5168,
+ .subdevice = 0x0212, /* minipci, LR212 */
+ .driver_data = SAA7134_BOARD_FLYTVPLATINUM_MINI,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x14c0,
+ .subdevice = 0x1212, /* minipci, LR1212 */
+ .driver_data = SAA7134_BOARD_FLYTVPLATINUM_MINI2,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x4e42,
+ .subdevice = 0x0212, /* OEM minipci, LR212 */
+ .driver_data = SAA7134_BOARD_FLYTVPLATINUM_MINI,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5168, /* Animation Technologies (LifeView) */
+ .subdevice = 0x0214, /* Standard PCI, LR214 Rev E and earlier (SAA7135) */
+ .driver_data = SAA7134_BOARD_FLYTVPLATINUM_FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5168, /* Animation Technologies (LifeView) */
+ .subdevice = 0x5214, /* Standard PCI, LR214 Rev F onwards (SAA7131) */
+ .driver_data = SAA7134_BOARD_FLYTVPLATINUM_FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1489, /* KYE */
+ .subdevice = 0x0214, /* Genius VideoWonder ProTV */
+ .driver_data = SAA7134_BOARD_FLYTVPLATINUM_FM, /* is an LR214WF actually */
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x16be,
+ .subdevice = 0x0003,
+ .driver_data = SAA7134_BOARD_MD7134,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x16be, /* CTX946 analog TV, HW mpeg, DVB-T */
+ .subdevice = 0x5000, /* only analog TV and DVB-T for now */
+ .driver_data = SAA7134_BOARD_MD7134,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x1048,
+ .subdevice = 0x226b,
+ .driver_data = SAA7134_BOARD_ELSA,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x1048,
+ .subdevice = 0x226a,
+ .driver_data = SAA7134_BOARD_ELSA_500TV,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x1048,
+ .subdevice = 0x226c,
+ .driver_data = SAA7134_BOARD_ELSA_700TV,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = PCI_VENDOR_ID_ASUSTEK,
+ .subdevice = 0x4842,
+ .driver_data = SAA7134_BOARD_ASUSTeK_TVFM7134,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = PCI_VENDOR_ID_ASUSTEK,
+ .subdevice = 0x4845,
+ .driver_data = SAA7134_BOARD_ASUSTeK_TVFM7135,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = PCI_VENDOR_ID_ASUSTEK,
+ .subdevice = 0x4830,
+ .driver_data = SAA7134_BOARD_ASUSTeK_TVFM7134,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = PCI_VENDOR_ID_ASUSTEK,
+ .subdevice = 0x4843,
+ .driver_data = SAA7134_BOARD_ASUSTEK_TVFM7133,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = PCI_VENDOR_ID_ASUSTEK,
+ .subdevice = 0x4840,
+ .driver_data = SAA7134_BOARD_ASUSTeK_TVFM7134,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = PCI_VENDOR_ID_PHILIPS,
+ .subdevice = 0xfe01,
+ .driver_data = SAA7134_BOARD_TVSTATION_RDS,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1894,
+ .subdevice = 0xfe01,
+ .driver_data = SAA7134_BOARD_TVSTATION_RDS,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1894,
+ .subdevice = 0xa006,
+ .driver_data = SAA7134_BOARD_TVSTATION_DVR,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1131,
+ .subdevice = 0x7133,
+ .driver_data = SAA7134_BOARD_VA1000POWER,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = PCI_VENDOR_ID_PHILIPS,
+ .subdevice = 0x2001,
+ .driver_data = SAA7134_BOARD_10MOONSTVMASTER,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x185b,
+ .subdevice = 0xc100,
+ .driver_data = SAA7134_BOARD_VIDEOMATE_TV,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x185b,
+ .subdevice = 0xc100,
+ .driver_data = SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUS,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = PCI_VENDOR_ID_MATROX,
+ .subdevice = 0x48d0,
+ .driver_data = SAA7134_BOARD_CRONOS_PLUS,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0xa70b,
+ .driver_data = SAA7134_BOARD_MD2819,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0xa7a1,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_A700_PRO,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0xa7a2,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_A700_HYBRID,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0x2115,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_305,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0xa115,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_505,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0x2108,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_305,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0x10ff,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_DVD_EZMAKER,
+ },{
+ /* AVerMedia CardBus */
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0xd6ee,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_CARDBUS,
+ },{
+ /* AVerMedia CardBus */
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0xb7e9,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_CARDBUS_501,
+ }, {
+ /* TransGear 3000TV */
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0x050c,
+ .driver_data = SAA7134_BOARD_TG3000TV,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x11bd,
+ .subdevice = 0x002b,
+ .driver_data = SAA7134_BOARD_PINNACLE_PCTV_STEREO,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x11bd,
+ .subdevice = 0x002d,
+ .driver_data = SAA7134_BOARD_PINNACLE_300I_DVBT_PAL,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1019,
+ .subdevice = 0x4cb4,
+ .driver_data = SAA7134_BOARD_ECS_TVP3XP,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1019,
+ .subdevice = 0x4cb5,
+ .driver_data = SAA7134_BOARD_ECS_TVP3XP_4CB5,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1019,
+ .subdevice = 0x4cb6,
+ .driver_data = SAA7134_BOARD_ECS_TVP3XP_4CB6,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x12ab,
+ .subdevice = 0x0800,
+ .driver_data = SAA7134_BOARD_UPMOST_PURPLE_TV,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x153b,
+ .subdevice = 0x1152,
+ .driver_data = SAA7134_BOARD_CINERGY200,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x185b,
+ .subdevice = 0xc100,
+ .driver_data = SAA7134_BOARD_VIDEOMATE_TV_PVR,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0x9715,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_307,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0xa70a,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_307,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x185b,
+ .subdevice = 0xc200,
+ .driver_data = SAA7134_BOARD_VIDEOMATE_GOLD_PLUS,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1540,
+ .subdevice = 0x9524,
+ .driver_data = SAA7134_BOARD_PROVIDEO_PV952,
+
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5168,
+ .subdevice = 0x0502, /* Cardbus version */
+ .driver_data = SAA7134_BOARD_FLYDVBT_DUO_CARDBUS,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5168,
+ .subdevice = 0x0306, /* PCI version */
+ .driver_data = SAA7134_BOARD_FLYDVBTDUO,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0xf31f,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_GO_007_FM,
+
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0xf11d,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_M135A,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0x4155,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_M733A,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0x4255,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_M733A,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = PCI_VENDOR_ID_PHILIPS,
+ .subdevice = 0x2004,
+ .driver_data = SAA7134_BOARD_PHILIPS_TOUGH,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1421,
+ .subdevice = 0x0350, /* PCI version */
+ .driver_data = SAA7134_BOARD_ADS_INSTANT_TV,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1421,
+ .subdevice = 0x0351, /* PCI version, new revision */
+ .driver_data = SAA7134_BOARD_ADS_INSTANT_TV,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1421,
+ .subdevice = 0x0370, /* cardbus version */
+ .driver_data = SAA7134_BOARD_ADS_INSTANT_TV,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1421,
+ .subdevice = 0x1370, /* cardbus version */
+ .driver_data = SAA7134_BOARD_ADS_INSTANT_TV,
+
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x4e42, /* Typhoon */
+ .subdevice = 0x0502, /* LifeView LR502 OEM */
+ .driver_data = SAA7134_BOARD_FLYDVBT_DUO_CARDBUS,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1043,
+ .subdevice = 0x0210, /* mini pci NTSC version */
+ .driver_data = SAA7134_BOARD_FLYTV_DIGIMATRIX,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1043,
+ .subdevice = 0x0210, /* mini pci PAL/SECAM version */
+ .driver_data = SAA7134_BOARD_ASUSTEK_DIGIMATRIX_TV,
+
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x0000, /* It shouldn't break anything, since subdevice id seems unique */
+ .subdevice = 0x4091,
+ .driver_data = SAA7134_BOARD_BEHOLD_409FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5456, /* GoTView */
+ .subdevice = 0x7135,
+ .driver_data = SAA7134_BOARD_GOTVIEW_7135,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = PCI_VENDOR_ID_PHILIPS,
+ .subdevice = 0x2004,
+ .driver_data = SAA7134_BOARD_PHILIPS_EUROPA,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x185b,
+ .subdevice = 0xc900,
+ .driver_data = SAA7134_BOARD_VIDEOMATE_DVBT_300,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x185b,
+ .subdevice = 0xc901,
+ .driver_data = SAA7134_BOARD_VIDEOMATE_DVBT_200,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1435,
+ .subdevice = 0x7350,
+ .driver_data = SAA7134_BOARD_RTD_VFG7350,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1435,
+ .subdevice = 0x7330,
+ .driver_data = SAA7134_BOARD_RTD_VFG7330,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1461,
+ .subdevice = 0x1044,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1131,
+ .subdevice = 0x4ee9,
+ .driver_data = SAA7134_BOARD_MONSTERTV_MOBILE,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x11bd,
+ .subdevice = 0x002e,
+ .driver_data = SAA7134_BOARD_PINNACLE_PCTV_110i,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1043,
+ .subdevice = 0x4862,
+ .driver_data = SAA7134_BOARD_ASUSTeK_P7131_DUAL,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = PCI_VENDOR_ID_PHILIPS,
+ .subdevice = 0x2018,
+ .driver_data = SAA7134_BOARD_PHILIPS_TIGER,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1462,
+ .subdevice = 0x6231, /* tda8275a, ks003 IR */
+ .driver_data = SAA7134_BOARD_MSI_TVATANYWHERE_PLUS,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1462,
+ .subdevice = 0x8624, /* tda8275, ks003 IR */
+ .driver_data = SAA7134_BOARD_MSI_TVATANYWHERE_PLUS,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x153b,
+ .subdevice = 0x1160,
+ .driver_data = SAA7134_BOARD_CINERGY250PCI,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA 7131E */
+ .subvendor = 0x5168,
+ .subdevice = 0x0319,
+ .driver_data = SAA7134_BOARD_FLYDVB_TRIO,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1461,
+ .subdevice = 0x2c05,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_777,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x5168,
+ .subdevice = 0x0301,
+ .driver_data = SAA7134_BOARD_FLYDVBT_LR301,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x0331,
+ .subdevice = 0x1421,
+ .driver_data = SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x17de,
+ .subdevice = 0x7201,
+ .driver_data = SAA7134_BOARD_TEVION_DVBT_220RF,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x17de,
+ .subdevice = 0x7250,
+ .driver_data = SAA7134_BOARD_KWORLD_DVBT_210,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA7135HL */
+ .subvendor = 0x17de,
+ .subdevice = 0x7350,
+ .driver_data = SAA7134_BOARD_KWORLD_ATSC110,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA7135HL */
+ .subvendor = 0x17de,
+ .subdevice = 0x7352,
+ .driver_data = SAA7134_BOARD_KWORLD_ATSC110, /* ATSC 115 */
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA7135HL */
+ .subvendor = 0x17de,
+ .subdevice = 0xa134,
+ .driver_data = SAA7134_BOARD_KWORLD_PC150U,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1461,
+ .subdevice = 0x7360,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_A169_B,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1461,
+ .subdevice = 0x6360,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_A169_B1,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x16be,
+ .subdevice = 0x0005,
+ .driver_data = SAA7134_BOARD_MD7134_BRIDGE_2,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x5168,
+ .subdevice = 0x0300,
+ .driver_data = SAA7134_BOARD_FLYDVBS_LR300,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x4e42,
+ .subdevice = 0x0300,/* LR300 */
+ .driver_data = SAA7134_BOARD_FLYDVBS_LR300,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1489,
+ .subdevice = 0x0301,
+ .driver_data = SAA7134_BOARD_FLYDVBT_LR301,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5168, /* Animation Technologies (LifeView) */
+ .subdevice = 0x0304,
+ .driver_data = SAA7134_BOARD_FLYTVPLATINUM_FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5168,
+ .subdevice = 0x3306,
+ .driver_data = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5168,
+ .subdevice = 0x3502, /* whats the difference to 0x3306 ?*/
+ .driver_data = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5168,
+ .subdevice = 0x3307, /* FlyDVB-T Hybrid Mini PCI */
+ .driver_data = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x16be,
+ .subdevice = 0x0007,
+ .driver_data = SAA7134_BOARD_MEDION_MD8800_QUADRO,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x16be,
+ .subdevice = 0x0008,
+ .driver_data = SAA7134_BOARD_MEDION_MD8800_QUADRO,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x16be,
+ .subdevice = 0x000d, /* triple CTX948_V1.1.1 */
+ .driver_data = SAA7134_BOARD_MEDION_MD8800_QUADRO,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1461,
+ .subdevice = 0x2c05,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_777,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1489,
+ .subdevice = 0x0502, /* Cardbus version */
+ .driver_data = SAA7134_BOARD_FLYDVBT_DUO_CARDBUS,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x0919, /* Philips Proteus PRO 2309 */
+ .subdevice = 0x2003,
+ .driver_data = SAA7134_BOARD_PROTEUS_2309,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1461,
+ .subdevice = 0x2c00,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_A16AR,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1043,
+ .subdevice = 0x4860,
+ .driver_data = SAA7134_BOARD_ASUS_EUROPA2_HYBRID,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x11bd,
+ .subdevice = 0x002f,
+ .driver_data = SAA7134_BOARD_PINNACLE_PCTV_310i,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0x9715,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_507,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0xa11b,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_507UA,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1043,
+ .subdevice = 0x4876,
+ .driver_data = SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x0070,
+ .subdevice = 0x6700,
+ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x0070,
+ .subdevice = 0x6701,
+ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x0070,
+ .subdevice = 0x6702,
+ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x0070,
+ .subdevice = 0x6703,
+ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x0070,
+ .subdevice = 0x6704,
+ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x0070,
+ .subdevice = 0x6705,
+ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x0070,
+ .subdevice = 0x6706,
+ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1150,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x0070,
+ .subdevice = 0x6707,
+ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x0070,
+ .subdevice = 0x6708,
+ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1150,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x0070,
+ .subdevice = 0x6709,
+ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x0070,
+ .subdevice = 0x670a,
+ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x153b,
+ .subdevice = 0x1172,
+ .driver_data = SAA7134_BOARD_CINERGY_HT_PCMCIA,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = PCI_VENDOR_ID_PHILIPS,
+ .subdevice = 0x2342,
+ .driver_data = SAA7134_BOARD_ENCORE_ENLTV,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x1131,
+ .subdevice = 0x2341,
+ .driver_data = SAA7134_BOARD_ENCORE_ENLTV,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x3016,
+ .subdevice = 0x2344,
+ .driver_data = SAA7134_BOARD_ENCORE_ENLTV,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x1131,
+ .subdevice = 0x230f,
+ .driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x1a7f,
+ .subdevice = 0x2008,
+ .driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM53,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1a7f,
+ .subdevice = 0x2108,
+ .driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM3,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x153b,
+ .subdevice = 0x1175,
+ .driver_data = SAA7134_BOARD_CINERGY_HT_PCI,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0xf31e,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_M102,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x4E42, /* MSI */
+ .subdevice = 0x0306, /* TV@nywhere DUO */
+ .driver_data = SAA7134_BOARD_FLYDVBTDUO,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1043,
+ .subdevice = 0x4871,
+ .driver_data = SAA7134_BOARD_ASUS_P7131_4871,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1043,
+ .subdevice = 0x4857, /* REV:1.00 */
+ .driver_data = SAA7134_BOARD_ASUSTeK_TIGER,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x0919, /* SinoVideo PCI 2309 Proteus (7134) */
+ .subdevice = 0x2003, /* OEM cardbus */
+ .driver_data = SAA7134_BOARD_SABRENT_TV_PCB05,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = PCI_VENDOR_ID_PHILIPS,
+ .subdevice = 0x2304,
+ .driver_data = SAA7134_BOARD_10MOONSTVMASTER3,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0xf01d, /* AVerTV DVB-T Super 007 */
+ .driver_data = SAA7134_BOARD_AVERMEDIA_SUPER_007,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x0000,
+ .subdevice = 0x4016,
+ .driver_data = SAA7134_BOARD_BEHOLD_401,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x0000,
+ .subdevice = 0x4036,
+ .driver_data = SAA7134_BOARD_BEHOLD_403,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x0000,
+ .subdevice = 0x4037,
+ .driver_data = SAA7134_BOARD_BEHOLD_403FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x0000,
+ .subdevice = 0x4050,
+ .driver_data = SAA7134_BOARD_BEHOLD_405,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x0000,
+ .subdevice = 0x4051,
+ .driver_data = SAA7134_BOARD_BEHOLD_405FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x0000,
+ .subdevice = 0x4070,
+ .driver_data = SAA7134_BOARD_BEHOLD_407,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x0000,
+ .subdevice = 0x4071,
+ .driver_data = SAA7134_BOARD_BEHOLD_407FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x0000,
+ .subdevice = 0x4090,
+ .driver_data = SAA7134_BOARD_BEHOLD_409,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x0000,
+ .subdevice = 0x505B,
+ .driver_data = SAA7134_BOARD_BEHOLD_505RDS_MK5,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x0000,
+ .subdevice = 0x5051,
+ .driver_data = SAA7134_BOARD_BEHOLD_505RDS_MK3,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x5ace,
+ .subdevice = 0x5050,
+ .driver_data = SAA7134_BOARD_BEHOLD_505FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x0000,
+ .subdevice = 0x5071,
+ .driver_data = SAA7134_BOARD_BEHOLD_507RDS_MK3,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x0000,
+ .subdevice = 0x507B,
+ .driver_data = SAA7134_BOARD_BEHOLD_507RDS_MK5,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x5ace,
+ .subdevice = 0x5070,
+ .driver_data = SAA7134_BOARD_BEHOLD_507_9FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5ace,
+ .subdevice = 0x5090,
+ .driver_data = SAA7134_BOARD_BEHOLD_507_9FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x0000,
+ .subdevice = 0x5201,
+ .driver_data = SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x5ace,
+ .subdevice = 0x6070,
+ .driver_data = SAA7134_BOARD_BEHOLD_607FM_MK3,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x5ace,
+ .subdevice = 0x6071,
+ .driver_data = SAA7134_BOARD_BEHOLD_607FM_MK5,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x5ace,
+ .subdevice = 0x6072,
+ .driver_data = SAA7134_BOARD_BEHOLD_607RDS_MK3,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x5ace,
+ .subdevice = 0x6073,
+ .driver_data = SAA7134_BOARD_BEHOLD_607RDS_MK5,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5ace,
+ .subdevice = 0x6090,
+ .driver_data = SAA7134_BOARD_BEHOLD_609FM_MK3,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5ace,
+ .subdevice = 0x6091,
+ .driver_data = SAA7134_BOARD_BEHOLD_609FM_MK5,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5ace,
+ .subdevice = 0x6092,
+ .driver_data = SAA7134_BOARD_BEHOLD_609RDS_MK3,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5ace,
+ .subdevice = 0x6093,
+ .driver_data = SAA7134_BOARD_BEHOLD_609RDS_MK5,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5ace,
+ .subdevice = 0x6190,
+ .driver_data = SAA7134_BOARD_BEHOLD_M6,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5ace,
+ .subdevice = 0x6193,
+ .driver_data = SAA7134_BOARD_BEHOLD_M6_EXTRA,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5ace,
+ .subdevice = 0x6191,
+ .driver_data = SAA7134_BOARD_BEHOLD_M63,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x4e42,
+ .subdevice = 0x3502,
+ .driver_data = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1822, /*Twinhan Technology Co. Ltd*/
+ .subdevice = 0x0022,
+ .driver_data = SAA7134_BOARD_TWINHAN_DTV_DVB_3056,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x16be,
+ .subdevice = 0x0010, /* Medion version CTX953_V.1.4.3 */
+ .driver_data = SAA7134_BOARD_CREATIX_CTX953,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1462, /* MSI */
+ .subdevice = 0x8625, /* TV@nywhere A/D v1.1 */
+ .driver_data = SAA7134_BOARD_MSI_TVANYWHERE_AD11,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0xf436,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_CARDBUS_506,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0xf936,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_A16D,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0xa836,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_M115,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x185b,
+ .subdevice = 0xc900,
+ .driver_data = SAA7134_BOARD_VIDEOMATE_T750,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA7135HL */
+ .subvendor = 0x1421,
+ .subdevice = 0x0380,
+ .driver_data = SAA7134_BOARD_ADS_INSTANT_HDTV_PCI,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5169,
+ .subdevice = 0x1502,
+ .driver_data = SAA7134_BOARD_FLYTVPLATINUM_MINI,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5ace,
+ .subdevice = 0x6290,
+ .driver_data = SAA7134_BOARD_BEHOLD_H6,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0xf636,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_M103,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0xf736,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_M103,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1043,
+ .subdevice = 0x4878, /* REV:1.02G */
+ .driver_data = SAA7134_BOARD_ASUSTeK_TIGER_3IN1,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x17de,
+ .subdevice = 0x7128,
+ .driver_data = SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x17de,
+ .subdevice = 0xb136,
+ .driver_data = SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0xf31d,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x185b,
+ .subdevice = 0xc900,
+ .driver_data = SAA7134_BOARD_VIDEOMATE_S350,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5ace, /* Beholder Intl. Ltd. */
+ .subdevice = 0x7595,
+ .driver_data = SAA7134_BOARD_BEHOLD_X7,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x19d1, /* RoverMedia */
+ .subdevice = 0x0138, /* LifeView FlyTV Prime30 OEM */
+ .driver_data = SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = PCI_VENDOR_ID_PHILIPS,
+ .subdevice = 0x2004,
+ .driver_data = SAA7134_BOARD_ZOLID_HYBRID_PCI,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1043,
+ .subdevice = 0x4847,
+ .driver_data = SAA7134_BOARD_ASUS_EUROPA_HYBRID,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x107d,
+ .subdevice = 0x6655,
+ .driver_data = SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x13c2,
+ .subdevice = 0x2804,
+ .driver_data = SAA7134_BOARD_TECHNOTREND_BUDGET_T3000,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5ace, /* Beholder Intl. Ltd. */
+ .subdevice = 0x7190,
+ .driver_data = SAA7134_BOARD_BEHOLD_H7,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5ace, /* Beholder Intl. Ltd. */
+ .subdevice = 0x7090,
+ .driver_data = SAA7134_BOARD_BEHOLD_A7,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7135,
+ .subvendor = 0x185b,
+ .subdevice = 0xc900,
+ .driver_data = SAA7134_BOARD_VIDEOMATE_M1F,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5ace,
+ .subdevice = 0x5030,
+ .driver_data = SAA7134_BOARD_BEHOLD_503FM,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x5ace,
+ .subdevice = 0x5010,
+ .driver_data = SAA7134_BOARD_BEHOLD_501,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x17de,
+ .subdevice = 0xd136,
+ .driver_data = SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x6000,
+ .subdevice = 0x0811,
+ .driver_data = SAA7134_BOARD_SENSORAY811_911,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x6000,
+ .subdevice = 0x0911,
+ .driver_data = SAA7134_BOARD_SENSORAY811_911,
+ }, {
+ /* --- boards without eeprom + subsystem ID --- */
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = PCI_VENDOR_ID_PHILIPS,
+ .subdevice = 0,
+ .driver_data = SAA7134_BOARD_NOAUTO,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = PCI_VENDOR_ID_PHILIPS,
+ .subdevice = 0,
+ .driver_data = SAA7134_BOARD_NOAUTO,
+ },{
+ /* --- default catch --- */
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = SAA7134_BOARD_UNKNOWN,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = SAA7134_BOARD_UNKNOWN,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = SAA7134_BOARD_UNKNOWN,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7135,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = SAA7134_BOARD_UNKNOWN,
+ },{
+ /* --- end of list --- */
+ }
+};
+MODULE_DEVICE_TABLE(pci, saa7134_pci_tbl);
+
+/* ----------------------------------------------------------- */
+/* flyvideo tweaks */
+
+
+static void board_flyvideo(struct saa7134_dev *dev)
+{
+ printk("%s: there are different flyvideo cards with different tuners\n"
+ "%s: out there, you might have to use the tuner=<nr> insmod\n"
+ "%s: option to override the default value.\n",
+ dev->name, dev->name, dev->name);
+}
+
+static int saa7134_xc2028_callback(struct saa7134_dev *dev,
+ int command, int arg)
+{
+ switch (command) {
+ case XC2028_TUNER_RESET:
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00008000, 0x00000000);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00008000, 0x00008000);
+ switch (dev->board) {
+ case SAA7134_BOARD_AVERMEDIA_CARDBUS_506:
+ case SAA7134_BOARD_AVERMEDIA_M103:
+ saa7134_set_gpio(dev, 23, 0);
+ msleep(10);
+ saa7134_set_gpio(dev, 23, 1);
+ break;
+ case SAA7134_BOARD_AVERMEDIA_A16D:
+ saa7134_set_gpio(dev, 21, 0);
+ msleep(10);
+ saa7134_set_gpio(dev, 21, 1);
+ break;
+ case SAA7134_BOARD_AVERMEDIA_A700_HYBRID:
+ saa7134_set_gpio(dev, 18, 0);
+ msleep(10);
+ saa7134_set_gpio(dev, 18, 1);
+ break;
+ case SAA7134_BOARD_VIDEOMATE_T750:
+ saa7134_set_gpio(dev, 20, 0);
+ msleep(10);
+ saa7134_set_gpio(dev, 20, 1);
+ break;
+ }
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int saa7134_xc5000_callback(struct saa7134_dev *dev,
+ int command, int arg)
+{
+ switch (dev->board) {
+ case SAA7134_BOARD_BEHOLD_X7:
+ case SAA7134_BOARD_BEHOLD_H7:
+ case SAA7134_BOARD_BEHOLD_A7:
+ if (command == XC5000_TUNER_RESET) {
+ /* Down and UP pheripherial RESET pin for reset all chips */
+ saa_writeb(SAA7134_SPECIAL_MODE, 0x00);
+ msleep(10);
+ saa_writeb(SAA7134_SPECIAL_MODE, 0x01);
+ msleep(10);
+ }
+ break;
+ default:
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x06e20000, 0x06e20000);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x06a20000, 0x06a20000);
+ saa_andorl(SAA7133_ANALOG_IO_SELECT >> 2, 0x02, 0x02);
+ saa_andorl(SAA7134_ANALOG_IN_CTRL1 >> 2, 0x81, 0x81);
+ saa_andorl(SAA7134_AUDIO_CLOCK0 >> 2, 0x03187de7, 0x03187de7);
+ saa_andorl(SAA7134_AUDIO_PLL_CTRL >> 2, 0x03, 0x03);
+ saa_andorl(SAA7134_AUDIO_CLOCKS_PER_FIELD0 >> 2,
+ 0x0001e000, 0x0001e000);
+ break;
+ }
+ return 0;
+}
+
+static int saa7134_tda8290_827x_callback(struct saa7134_dev *dev,
+ int command, int arg)
+{
+ u8 sync_control;
+
+ switch (command) {
+ case 0: /* switch LNA gain through GPIO 22*/
+ saa7134_set_gpio(dev, 22, arg) ;
+ break;
+ case 1: /* vsync output at GPIO22. 50 / 60Hz */
+ saa_andorb(SAA7134_VIDEO_PORT_CTRL3, 0x80, 0x80);
+ saa_andorb(SAA7134_VIDEO_PORT_CTRL6, 0x0f, 0x03);
+ if (arg == 1)
+ sync_control = 11;
+ else
+ sync_control = 17;
+ saa_writeb(SAA7134_VGATE_START, sync_control);
+ saa_writeb(SAA7134_VGATE_STOP, sync_control + 1);
+ saa_andorb(SAA7134_MISC_VGATE_MSB, 0x03, 0x00);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static inline int saa7134_tda18271_hvr11x0_toggle_agc(struct saa7134_dev *dev,
+ enum tda18271_mode mode)
+{
+ /* toggle AGC switch through GPIO 26 */
+ switch (mode) {
+ case TDA18271_ANALOG:
+ saa7134_set_gpio(dev, 26, 0);
+ break;
+ case TDA18271_DIGITAL:
+ saa7134_set_gpio(dev, 26, 1);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static inline int saa7134_kworld_sbtvd_toggle_agc(struct saa7134_dev *dev,
+ enum tda18271_mode mode)
+{
+ /* toggle AGC switch through GPIO 27 */
+ switch (mode) {
+ case TDA18271_ANALOG:
+ saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0x4000);
+ saa_writel(SAA7134_GPIO_GPSTATUS0 >> 2, 0x4000);
+ msleep(20);
+ break;
+ case TDA18271_DIGITAL:
+ saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0x14000);
+ saa_writel(SAA7134_GPIO_GPSTATUS0 >> 2, 0x14000);
+ msleep(20);
+ saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0x54000);
+ saa_writel(SAA7134_GPIO_GPSTATUS0 >> 2, 0x54000);
+ msleep(30);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int saa7134_kworld_pc150u_toggle_agc(struct saa7134_dev *dev,
+ enum tda18271_mode mode)
+{
+ switch (mode) {
+ case TDA18271_ANALOG:
+ saa7134_set_gpio(dev, 18, 0);
+ break;
+ case TDA18271_DIGITAL:
+ saa7134_set_gpio(dev, 18, 1);
+ msleep(30);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int saa7134_tda8290_18271_callback(struct saa7134_dev *dev,
+ int command, int arg)
+{
+ int ret = 0;
+
+ switch (command) {
+ case TDA18271_CALLBACK_CMD_AGC_ENABLE: /* 0 */
+ switch (dev->board) {
+ case SAA7134_BOARD_HAUPPAUGE_HVR1150:
+ case SAA7134_BOARD_HAUPPAUGE_HVR1120:
+ case SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2:
+ ret = saa7134_tda18271_hvr11x0_toggle_agc(dev, arg);
+ break;
+ case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG:
+ ret = saa7134_kworld_sbtvd_toggle_agc(dev, arg);
+ break;
+ case SAA7134_BOARD_KWORLD_PC150U:
+ ret = saa7134_kworld_pc150u_toggle_agc(dev, arg);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int saa7134_tda8290_callback(struct saa7134_dev *dev,
+ int command, int arg)
+{
+ int ret;
+
+ switch (dev->board) {
+ case SAA7134_BOARD_HAUPPAUGE_HVR1150:
+ case SAA7134_BOARD_HAUPPAUGE_HVR1120:
+ case SAA7134_BOARD_AVERMEDIA_M733A:
+ case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG:
+ case SAA7134_BOARD_KWORLD_PC150U:
+ case SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2:
+ /* tda8290 + tda18271 */
+ ret = saa7134_tda8290_18271_callback(dev, command, arg);
+ break;
+ default:
+ /* tda8290 + tda827x */
+ ret = saa7134_tda8290_827x_callback(dev, command, arg);
+ break;
+ }
+ return ret;
+}
+
+int saa7134_tuner_callback(void *priv, int component, int command, int arg)
+{
+ struct saa7134_dev *dev = priv;
+
+ if (dev != NULL) {
+ switch (dev->tuner_type) {
+ case TUNER_PHILIPS_TDA8290:
+ return saa7134_tda8290_callback(dev, command, arg);
+ case TUNER_XC2028:
+ return saa7134_xc2028_callback(dev, command, arg);
+ case TUNER_XC5000:
+ return saa7134_xc5000_callback(dev, command, arg);
+ }
+ } else {
+ printk(KERN_ERR "saa7134: Error - device struct undefined.\n");
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL(saa7134_tuner_callback);
+
+/* ----------------------------------------------------------- */
+
+static void hauppauge_eeprom(struct saa7134_dev *dev, u8 *eeprom_data)
+{
+ struct tveeprom tv;
+
+ tveeprom_hauppauge_analog(&dev->i2c_client, &tv, eeprom_data);
+
+ /* Make sure we support the board model */
+ switch (tv.model) {
+ case 67019: /* WinTV-HVR1110 (Retail, IR Blaster, hybrid, FM, SVid/Comp, 3.5mm audio in) */
+ case 67109: /* WinTV-HVR1000 (Retail, IR Receive, analog, no FM, SVid/Comp, 3.5mm audio in) */
+ case 67201: /* WinTV-HVR1150 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */
+ case 67301: /* WinTV-HVR1000 (Retail, IR Receive, analog, no FM, SVid/Comp, 3.5mm audio in) */
+ case 67209: /* WinTV-HVR1110 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */
+ case 67559: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */
+ case 67569: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM) */
+ case 67579: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM) */
+ case 67589: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM, SVid/Comp, RCA aud) */
+ case 67599: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM, SVid/Comp, RCA aud) */
+ case 67651: /* WinTV-HVR1150 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */
+ case 67659: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */
+ break;
+ default:
+ printk(KERN_WARNING "%s: warning: "
+ "unknown hauppauge model #%d\n", dev->name, tv.model);
+ break;
+ }
+
+ printk(KERN_INFO "%s: hauppauge eeprom: model=%d\n",
+ dev->name, tv.model);
+}
+
+/* ----------------------------------------------------------- */
+
+int saa7134_board_init1(struct saa7134_dev *dev)
+{
+ /* Always print gpio, often manufacturers encode tuner type and other info. */
+ saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0);
+ dev->gpio_value = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
+ printk(KERN_INFO "%s: board init: gpio is %x\n", dev->name, dev->gpio_value);
+
+ switch (dev->board) {
+ case SAA7134_BOARD_FLYVIDEO2000:
+ case SAA7134_BOARD_FLYVIDEO3000:
+ case SAA7134_BOARD_FLYVIDEO3000_NTSC:
+ dev->has_remote = SAA7134_REMOTE_GPIO;
+ board_flyvideo(dev);
+ break;
+ case SAA7134_BOARD_FLYTVPLATINUM_MINI2:
+ case SAA7134_BOARD_FLYTVPLATINUM_FM:
+ case SAA7134_BOARD_CINERGY400:
+ case SAA7134_BOARD_CINERGY600:
+ case SAA7134_BOARD_CINERGY600_MK3:
+ case SAA7134_BOARD_ECS_TVP3XP:
+ case SAA7134_BOARD_ECS_TVP3XP_4CB5:
+ case SAA7134_BOARD_ECS_TVP3XP_4CB6:
+ case SAA7134_BOARD_MD2819:
+ case SAA7134_BOARD_KWORLD_VSTREAM_XPERT:
+ case SAA7134_BOARD_KWORLD_XPERT:
+ case SAA7134_BOARD_AVERMEDIA_STUDIO_305:
+ case SAA7134_BOARD_AVERMEDIA_STUDIO_505:
+ case SAA7134_BOARD_AVERMEDIA_305:
+ case SAA7134_BOARD_AVERMEDIA_STUDIO_307:
+ case SAA7134_BOARD_AVERMEDIA_307:
+ case SAA7134_BOARD_AVERMEDIA_STUDIO_507:
+ case SAA7134_BOARD_AVERMEDIA_GO_007_FM:
+ case SAA7134_BOARD_AVERMEDIA_777:
+ case SAA7134_BOARD_AVERMEDIA_M135A:
+/* case SAA7134_BOARD_SABRENT_SBTTVFM: */ /* not finished yet */
+ case SAA7134_BOARD_VIDEOMATE_TV_PVR:
+ case SAA7134_BOARD_VIDEOMATE_GOLD_PLUS:
+ case SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII:
+ case SAA7134_BOARD_VIDEOMATE_M1F:
+ case SAA7134_BOARD_VIDEOMATE_DVBT_300:
+ case SAA7134_BOARD_VIDEOMATE_DVBT_200:
+ case SAA7134_BOARD_VIDEOMATE_DVBT_200A:
+ case SAA7134_BOARD_MANLI_MTV001:
+ case SAA7134_BOARD_MANLI_MTV002:
+ case SAA7134_BOARD_BEHOLD_409FM:
+ case SAA7134_BOARD_AVACSSMARTTV:
+ case SAA7134_BOARD_GOTVIEW_7135:
+ case SAA7134_BOARD_KWORLD_TERMINATOR:
+ case SAA7134_BOARD_SEDNA_PC_TV_CARDBUS:
+ case SAA7134_BOARD_FLYDVBT_LR301:
+ case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
+ case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
+ case SAA7134_BOARD_ASUSTeK_P7131_ANALOG:
+ case SAA7134_BOARD_FLYDVBTDUO:
+ case SAA7134_BOARD_PROTEUS_2309:
+ case SAA7134_BOARD_AVERMEDIA_A16AR:
+ case SAA7134_BOARD_ENCORE_ENLTV:
+ case SAA7134_BOARD_ENCORE_ENLTV_FM:
+ case SAA7134_BOARD_ENCORE_ENLTV_FM53:
+ case SAA7134_BOARD_ENCORE_ENLTV_FM3:
+ case SAA7134_BOARD_10MOONSTVMASTER3:
+ case SAA7134_BOARD_BEHOLD_401:
+ case SAA7134_BOARD_BEHOLD_403:
+ case SAA7134_BOARD_BEHOLD_403FM:
+ case SAA7134_BOARD_BEHOLD_405:
+ case SAA7134_BOARD_BEHOLD_405FM:
+ case SAA7134_BOARD_BEHOLD_407:
+ case SAA7134_BOARD_BEHOLD_407FM:
+ case SAA7134_BOARD_BEHOLD_409:
+ case SAA7134_BOARD_BEHOLD_505FM:
+ case SAA7134_BOARD_BEHOLD_505RDS_MK5:
+ case SAA7134_BOARD_BEHOLD_505RDS_MK3:
+ case SAA7134_BOARD_BEHOLD_507_9FM:
+ case SAA7134_BOARD_BEHOLD_507RDS_MK3:
+ case SAA7134_BOARD_BEHOLD_507RDS_MK5:
+ case SAA7134_BOARD_GENIUS_TVGO_A11MCE:
+ case SAA7134_BOARD_REAL_ANGEL_220:
+ case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG:
+ case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS:
+ case SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM:
+ case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S:
+ dev->has_remote = SAA7134_REMOTE_GPIO;
+ break;
+ case SAA7134_BOARD_FLYDVBS_LR300:
+ saa_writeb(SAA7134_GPIO_GPMODE3, 0x80);
+ saa_writeb(SAA7134_GPIO_GPSTATUS2, 0x40);
+ dev->has_remote = SAA7134_REMOTE_GPIO;
+ break;
+ case SAA7134_BOARD_MD5044:
+ printk("%s: seems there are two different versions of the MD5044\n"
+ "%s: (with the same ID) out there. If sound doesn't work for\n"
+ "%s: you try the audio_clock_override=0x200000 insmod option.\n",
+ dev->name,dev->name,dev->name);
+ break;
+ case SAA7134_BOARD_CINERGY400_CARDBUS:
+ /* power-up tuner chip */
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x00040000, 0x00040000);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00040000, 0x00000000);
+ break;
+ case SAA7134_BOARD_PINNACLE_300I_DVBT_PAL:
+ /* this turns the remote control chip off to work around a bug in it */
+ saa_writeb(SAA7134_GPIO_GPMODE1, 0x80);
+ saa_writeb(SAA7134_GPIO_GPSTATUS1, 0x80);
+ break;
+ case SAA7134_BOARD_MONSTERTV_MOBILE:
+ /* power-up tuner chip */
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x00040000, 0x00040000);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00040000, 0x00000004);
+ break;
+ case SAA7134_BOARD_FLYDVBT_DUO_CARDBUS:
+ /* turn the fan on */
+ saa_writeb(SAA7134_GPIO_GPMODE3, 0x08);
+ saa_writeb(SAA7134_GPIO_GPSTATUS3, 0x06);
+ break;
+ case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331:
+ case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS:
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x08000000, 0x08000000);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x08000000, 0x00000000);
+ break;
+ case SAA7134_BOARD_AVERMEDIA_CARDBUS:
+ case SAA7134_BOARD_AVERMEDIA_M115:
+ /* power-down tuner chip */
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0xffffffff, 0);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0xffffffff, 0);
+ msleep(10);
+ /* power-up tuner chip */
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0xffffffff, 0xffffffff);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0xffffffff, 0xffffffff);
+ msleep(10);
+ break;
+ case SAA7134_BOARD_AVERMEDIA_CARDBUS_501:
+ /* power-down tuner chip */
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x08400000, 0x08400000);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x08400000, 0);
+ msleep(10);
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x08400000, 0x08400000);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x08400000, 0x08400000);
+ msleep(10);
+ dev->has_remote = SAA7134_REMOTE_I2C;
+ break;
+ case SAA7134_BOARD_AVERMEDIA_CARDBUS_506:
+ saa7134_set_gpio(dev, 23, 0);
+ msleep(10);
+ saa7134_set_gpio(dev, 23, 1);
+ dev->has_remote = SAA7134_REMOTE_I2C;
+ break;
+ case SAA7134_BOARD_AVERMEDIA_M103:
+ saa7134_set_gpio(dev, 23, 0);
+ msleep(10);
+ saa7134_set_gpio(dev, 23, 1);
+ break;
+ case SAA7134_BOARD_AVERMEDIA_A16D:
+ saa7134_set_gpio(dev, 21, 0);
+ msleep(10);
+ saa7134_set_gpio(dev, 21, 1);
+ msleep(1);
+ dev->has_remote = SAA7134_REMOTE_GPIO;
+ break;
+ case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM:
+ /* power-down tuner chip */
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x000A8004, 0x000A8004);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x000A8004, 0);
+ msleep(10);
+ /* power-up tuner chip */
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x000A8004, 0x000A8004);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x000A8004, 0x000A8004);
+ msleep(10);
+ /* remote via GPIO */
+ dev->has_remote = SAA7134_REMOTE_GPIO;
+ break;
+ case SAA7134_BOARD_RTD_VFG7350:
+
+ /*
+ * Make sure Production Test Register at offset 0x1D1 is cleared
+ * to take chip out of test mode. Clearing bit 4 (TST_EN_AOUT)
+ * prevents pin 105 from remaining low; keeping pin 105 low
+ * continually resets the SAA6752 chip.
+ */
+
+ saa_writeb (SAA7134_PRODUCTION_TEST_MODE, 0x00);
+ break;
+ case SAA7134_BOARD_HAUPPAUGE_HVR1150:
+ case SAA7134_BOARD_HAUPPAUGE_HVR1120:
+ dev->has_remote = SAA7134_REMOTE_GPIO;
+ /* GPIO 26 high for digital, low for analog */
+ saa7134_set_gpio(dev, 26, 0);
+ msleep(1);
+
+ saa7134_set_gpio(dev, 22, 0);
+ msleep(10);
+ saa7134_set_gpio(dev, 22, 1);
+ break;
+ /* i2c remotes */
+ case SAA7134_BOARD_PINNACLE_PCTV_110i:
+ case SAA7134_BOARD_PINNACLE_PCTV_310i:
+ case SAA7134_BOARD_UPMOST_PURPLE_TV:
+ case SAA7134_BOARD_MSI_TVATANYWHERE_PLUS:
+ case SAA7134_BOARD_HAUPPAUGE_HVR1110:
+ case SAA7134_BOARD_BEHOLD_607FM_MK3:
+ case SAA7134_BOARD_BEHOLD_607FM_MK5:
+ case SAA7134_BOARD_BEHOLD_609FM_MK3:
+ case SAA7134_BOARD_BEHOLD_609FM_MK5:
+ case SAA7134_BOARD_BEHOLD_607RDS_MK3:
+ case SAA7134_BOARD_BEHOLD_607RDS_MK5:
+ case SAA7134_BOARD_BEHOLD_609RDS_MK3:
+ case SAA7134_BOARD_BEHOLD_609RDS_MK5:
+ case SAA7134_BOARD_BEHOLD_M6:
+ case SAA7134_BOARD_BEHOLD_M63:
+ case SAA7134_BOARD_BEHOLD_M6_EXTRA:
+ case SAA7134_BOARD_BEHOLD_H6:
+ case SAA7134_BOARD_BEHOLD_X7:
+ case SAA7134_BOARD_BEHOLD_H7:
+ case SAA7134_BOARD_BEHOLD_A7:
+ case SAA7134_BOARD_KWORLD_PC150U:
+ dev->has_remote = SAA7134_REMOTE_I2C;
+ break;
+ case SAA7134_BOARD_AVERMEDIA_A169_B:
+ printk("%s: %s: dual saa713x broadcast decoders\n"
+ "%s: Sorry, none of the inputs to this chip are supported yet.\n"
+ "%s: Dual decoder functionality is disabled for now, use the other chip.\n",
+ dev->name,card(dev).name,dev->name,dev->name);
+ break;
+ case SAA7134_BOARD_AVERMEDIA_M102:
+ /* enable tuner */
+ dev->has_remote = SAA7134_REMOTE_GPIO;
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x8c040007, 0x8c040007);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0c0007cd, 0x0c0007cd);
+ break;
+ case SAA7134_BOARD_AVERMEDIA_A700_HYBRID:
+ case SAA7134_BOARD_AVERMEDIA_A700_PRO:
+ /* write windows gpio values */
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x80040100, 0x80040100);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x80040100, 0x00040100);
+ break;
+ case SAA7134_BOARD_VIDEOMATE_S350:
+ dev->has_remote = SAA7134_REMOTE_GPIO;
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x0000C000, 0x0000C000);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0000C000, 0x0000C000);
+ break;
+ case SAA7134_BOARD_AVERMEDIA_M733A:
+ saa7134_set_gpio(dev, 1, 1);
+ msleep(10);
+ saa7134_set_gpio(dev, 1, 0);
+ msleep(10);
+ saa7134_set_gpio(dev, 1, 1);
+ dev->has_remote = SAA7134_REMOTE_GPIO;
+ break;
+ case SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2:
+ /* enable LGS-8G75 */
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x0e050000, 0x0c050000);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0e050000, 0x0c050000);
+ break;
+ case SAA7134_BOARD_VIDEOMATE_T750:
+ /* enable the analog tuner */
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x00008000, 0x00008000);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00008000, 0x00008000);
+ break;
+ }
+ return 0;
+}
+
+static void saa7134_tuner_setup(struct saa7134_dev *dev)
+{
+ struct tuner_setup tun_setup;
+ unsigned int mode_mask = T_RADIO | T_ANALOG_TV;
+
+ memset(&tun_setup, 0, sizeof(tun_setup));
+ tun_setup.tuner_callback = saa7134_tuner_callback;
+
+ if (saa7134_boards[dev->board].radio_type != UNSET) {
+ tun_setup.type = saa7134_boards[dev->board].radio_type;
+ tun_setup.addr = saa7134_boards[dev->board].radio_addr;
+
+ tun_setup.mode_mask = T_RADIO;
+
+ saa_call_all(dev, tuner, s_type_addr, &tun_setup);
+ mode_mask &= ~T_RADIO;
+ }
+
+ if ((dev->tuner_type != TUNER_ABSENT) && (dev->tuner_type != UNSET)) {
+ tun_setup.type = dev->tuner_type;
+ tun_setup.addr = dev->tuner_addr;
+ tun_setup.config = saa7134_boards[dev->board].tuner_config;
+ tun_setup.tuner_callback = saa7134_tuner_callback;
+
+ tun_setup.mode_mask = mode_mask;
+
+ saa_call_all(dev, tuner, s_type_addr, &tun_setup);
+ }
+
+ if (dev->tda9887_conf) {
+ struct v4l2_priv_tun_config tda9887_cfg;
+
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &dev->tda9887_conf;
+
+ saa_call_all(dev, tuner, s_config, &tda9887_cfg);
+ }
+
+ if (dev->tuner_type == TUNER_XC2028) {
+ struct v4l2_priv_tun_config xc2028_cfg;
+ struct xc2028_ctrl ctl;
+
+ memset(&xc2028_cfg, 0, sizeof(xc2028_cfg));
+ memset(&ctl, 0, sizeof(ctl));
+
+ ctl.fname = XC2028_DEFAULT_FIRMWARE;
+ ctl.max_len = 64;
+
+ switch (dev->board) {
+ case SAA7134_BOARD_AVERMEDIA_A16D:
+ case SAA7134_BOARD_AVERMEDIA_CARDBUS_506:
+ case SAA7134_BOARD_AVERMEDIA_M103:
+ case SAA7134_BOARD_AVERMEDIA_A700_HYBRID:
+ ctl.demod = XC3028_FE_ZARLINK456;
+ break;
+ default:
+ ctl.demod = XC3028_FE_OREN538;
+ ctl.mts = 1;
+ }
+
+ xc2028_cfg.tuner = TUNER_XC2028;
+ xc2028_cfg.priv = &ctl;
+
+ saa_call_all(dev, tuner, s_config, &xc2028_cfg);
+ }
+}
+
+/* stuff which needs working i2c */
+int saa7134_board_init2(struct saa7134_dev *dev)
+{
+ unsigned char buf;
+ int board;
+
+ /* Put here the code that enables the chips that are needed
+ for analog mode and doesn't depend on the tuner attachment.
+ It is also a good idea to get tuner type from eeprom, etc before
+ initializing tuner, since we can avoid loading tuner driver
+ on devices that has TUNER_ABSENT
+ */
+ switch (dev->board) {
+ case SAA7134_BOARD_BMK_MPEX_NOTUNER:
+ case SAA7134_BOARD_BMK_MPEX_TUNER:
+ /* Checks if the device has a tuner at 0x60 addr
+ If the device doesn't have a tuner, TUNER_ABSENT
+ will be used at tuner_type, avoiding loading tuner
+ without needing it
+ */
+ dev->i2c_client.addr = 0x60;
+ board = (i2c_master_recv(&dev->i2c_client, &buf, 0) < 0)
+ ? SAA7134_BOARD_BMK_MPEX_NOTUNER
+ : SAA7134_BOARD_BMK_MPEX_TUNER;
+ if (board == dev->board)
+ break;
+ dev->board = board;
+ printk("%s: board type fixup: %s\n", dev->name,
+ saa7134_boards[dev->board].name);
+ dev->tuner_type = saa7134_boards[dev->board].tuner_type;
+
+ break;
+ case SAA7134_BOARD_MD7134:
+ {
+ u8 subaddr;
+ u8 data[3];
+ int ret, tuner_t;
+ struct i2c_msg msg[] = {{.addr=0x50, .flags=0, .buf=&subaddr, .len = 1},
+ {.addr=0x50, .flags=I2C_M_RD, .buf=data, .len = 3}};
+
+ subaddr= 0x14;
+ tuner_t = 0;
+
+ /* Retrieve device data from eeprom, checking for the
+ proper tuner_type.
+ */
+ ret = i2c_transfer(&dev->i2c_adap, msg, 2);
+ if (ret != 2) {
+ printk(KERN_ERR "EEPROM read failure\n");
+ } else if ((data[0] != 0) && (data[0] != 0xff)) {
+ /* old config structure */
+ subaddr = data[0] + 2;
+ msg[1].len = 2;
+ i2c_transfer(&dev->i2c_adap, msg, 2);
+ tuner_t = (data[0] << 8) + data[1];
+ switch (tuner_t){
+ case 0x0103:
+ dev->tuner_type = TUNER_PHILIPS_PAL;
+ break;
+ case 0x010C:
+ dev->tuner_type = TUNER_PHILIPS_FM1216ME_MK3;
+ break;
+ default:
+ printk(KERN_ERR "%s Can't determine tuner type %x from EEPROM\n", dev->name, tuner_t);
+ }
+ } else if ((data[1] != 0) && (data[1] != 0xff)) {
+ /* new config structure */
+ subaddr = data[1] + 1;
+ msg[1].len = 1;
+ i2c_transfer(&dev->i2c_adap, msg, 2);
+ subaddr = data[0] + 1;
+ msg[1].len = 2;
+ i2c_transfer(&dev->i2c_adap, msg, 2);
+ tuner_t = (data[1] << 8) + data[0];
+ switch (tuner_t) {
+ case 0x0005:
+ dev->tuner_type = TUNER_PHILIPS_FM1216ME_MK3;
+ break;
+ case 0x001d:
+ dev->tuner_type = TUNER_PHILIPS_FMD1216ME_MK3;
+ printk(KERN_INFO "%s Board has DVB-T\n", dev->name);
+ break;
+ default:
+ printk(KERN_ERR "%s Can't determine tuner type %x from EEPROM\n", dev->name, tuner_t);
+ }
+ } else {
+ printk(KERN_ERR "%s unexpected config structure\n", dev->name);
+ }
+
+ printk(KERN_INFO "%s Tuner type is %d\n", dev->name, dev->tuner_type);
+ break;
+ }
+ case SAA7134_BOARD_PHILIPS_EUROPA:
+ if (dev->autodetected && (dev->eedata[0x41] == 0x1c)) {
+ /* Reconfigure board as Snake reference design */
+ dev->board = SAA7134_BOARD_PHILIPS_SNAKE;
+ dev->tuner_type = saa7134_boards[dev->board].tuner_type;
+ printk(KERN_INFO "%s: Reconfigured board as %s\n",
+ dev->name, saa7134_boards[dev->board].name);
+ break;
+ }
+ /* break intentionally omitted */
+ case SAA7134_BOARD_VIDEOMATE_DVBT_300:
+ case SAA7134_BOARD_ASUS_EUROPA2_HYBRID:
+ case SAA7134_BOARD_ASUS_EUROPA_HYBRID:
+ case SAA7134_BOARD_TECHNOTREND_BUDGET_T3000:
+ {
+
+ /* The Philips EUROPA based hybrid boards have the tuner
+ connected through the channel decoder. We have to make it
+ transparent to find it
+ */
+ u8 data[] = { 0x07, 0x02};
+ struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
+
+ break;
+ }
+ case SAA7134_BOARD_PHILIPS_TIGER:
+ case SAA7134_BOARD_PHILIPS_TIGER_S:
+ {
+ u8 data[] = { 0x3c, 0x33, 0x60};
+ struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
+ if (dev->autodetected && (dev->eedata[0x49] == 0x50)) {
+ dev->board = SAA7134_BOARD_PHILIPS_TIGER_S;
+ printk(KERN_INFO "%s: Reconfigured board as %s\n",
+ dev->name, saa7134_boards[dev->board].name);
+ }
+ if (dev->board == SAA7134_BOARD_PHILIPS_TIGER_S) {
+ dev->tuner_type = TUNER_PHILIPS_TDA8290;
+
+ data[2] = 0x68;
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
+ break;
+ }
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
+ break;
+ }
+ case SAA7134_BOARD_ASUSTeK_TVFM7135:
+ /* The card below is detected as card=53, but is different */
+ if (dev->autodetected && (dev->eedata[0x27] == 0x03)) {
+ dev->board = SAA7134_BOARD_ASUSTeK_P7131_ANALOG;
+ printk(KERN_INFO "%s: P7131 analog only, using "
+ "entry of %s\n",
+ dev->name, saa7134_boards[dev->board].name);
+
+ /* IR init has already happened for other cards, so
+ * we have to catch up. */
+ dev->has_remote = SAA7134_REMOTE_GPIO;
+ saa7134_input_init1(dev);
+ }
+ break;
+ case SAA7134_BOARD_HAUPPAUGE_HVR1150:
+ case SAA7134_BOARD_HAUPPAUGE_HVR1120:
+ hauppauge_eeprom(dev, dev->eedata+0x80);
+ break;
+ case SAA7134_BOARD_HAUPPAUGE_HVR1110:
+ hauppauge_eeprom(dev, dev->eedata+0x80);
+ /* break intentionally omitted */
+ case SAA7134_BOARD_PINNACLE_PCTV_310i:
+ case SAA7134_BOARD_KWORLD_DVBT_210:
+ case SAA7134_BOARD_TEVION_DVBT_220RF:
+ case SAA7134_BOARD_ASUSTeK_TIGER:
+ case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
+ case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
+ case SAA7134_BOARD_MEDION_MD8800_QUADRO:
+ case SAA7134_BOARD_AVERMEDIA_SUPER_007:
+ case SAA7134_BOARD_TWINHAN_DTV_DVB_3056:
+ case SAA7134_BOARD_CREATIX_CTX953:
+ {
+ /* this is a hybrid board, initialize to analog mode
+ * and configure firmware eeprom address
+ */
+ u8 data[] = { 0x3c, 0x33, 0x60};
+ struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
+ break;
+ }
+ case SAA7134_BOARD_ASUSTeK_TIGER_3IN1:
+ {
+ u8 data[] = { 0x3c, 0x33, 0x60};
+ struct i2c_msg msg = {.addr = 0x0b, .flags = 0, .buf = data,
+ .len = sizeof(data)};
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
+ break;
+ }
+ case SAA7134_BOARD_FLYDVB_TRIO:
+ {
+ u8 temp = 0;
+ int rc;
+ u8 data[] = { 0x3c, 0x33, 0x62};
+ struct i2c_msg msg = {.addr=0x09, .flags=0, .buf=data, .len = sizeof(data)};
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
+
+ /*
+ * send weak up message to pic16C505 chip
+ * @ LifeView FlyDVB Trio
+ */
+ msg.buf = &temp;
+ msg.addr = 0x0b;
+ msg.len = 1;
+ if (1 != i2c_transfer(&dev->i2c_adap, &msg, 1)) {
+ printk(KERN_WARNING "%s: send wake up byte to pic16C505"
+ "(IR chip) failed\n", dev->name);
+ } else {
+ msg.flags = I2C_M_RD;
+ rc = i2c_transfer(&dev->i2c_adap, &msg, 1);
+ printk(KERN_INFO "%s: probe IR chip @ i2c 0x%02x: %s\n",
+ dev->name, msg.addr,
+ (1 == rc) ? "yes" : "no");
+ if (rc == 1)
+ dev->has_remote = SAA7134_REMOTE_I2C;
+ }
+ break;
+ }
+ case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331:
+ case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS:
+ {
+ /* initialize analog mode */
+ u8 data[] = { 0x3c, 0x33, 0x6a};
+ struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
+ break;
+ }
+ case SAA7134_BOARD_CINERGY_HT_PCMCIA:
+ case SAA7134_BOARD_CINERGY_HT_PCI:
+ {
+ /* initialize analog mode */
+ u8 data[] = { 0x3c, 0x33, 0x68};
+ struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
+ break;
+ }
+ case SAA7134_BOARD_VIDEOMATE_DVBT_200:
+ case SAA7134_BOARD_VIDEOMATE_DVBT_200A:
+ /* The T200 and the T200A share the same pci id. Consequently,
+ * we are going to query eeprom to try to find out which one we
+ * are actually looking at. */
+
+ /* Don't do this if the board was specifically selected with an
+ * insmod option or if we have the default configuration T200*/
+ if (!dev->autodetected || (dev->eedata[0x41] == 0xd0))
+ break;
+ if (dev->eedata[0x41] == 0x02) {
+ /* Reconfigure board as T200A */
+ dev->board = SAA7134_BOARD_VIDEOMATE_DVBT_200A;
+ dev->tuner_type = saa7134_boards[dev->board].tuner_type;
+ dev->tda9887_conf = saa7134_boards[dev->board].tda9887_conf;
+ printk(KERN_INFO "%s: Reconfigured board as %s\n",
+ dev->name, saa7134_boards[dev->board].name);
+ } else {
+ printk(KERN_WARNING "%s: Unexpected tuner type info: %x in eeprom\n",
+ dev->name, dev->eedata[0x41]);
+ break;
+ }
+ break;
+ case SAA7134_BOARD_ADS_INSTANT_HDTV_PCI:
+ case SAA7134_BOARD_KWORLD_ATSC110:
+ {
+ struct i2c_msg msg = { .addr = 0x0a, .flags = 0 };
+ int i;
+ static u8 buffer[][2] = {
+ { 0x10, 0x12 },
+ { 0x13, 0x04 },
+ { 0x16, 0x00 },
+ { 0x14, 0x04 },
+ { 0x17, 0x00 },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(buffer); i++) {
+ msg.buf = &buffer[i][0];
+ msg.len = ARRAY_SIZE(buffer[0]);
+ if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1)
+ printk(KERN_WARNING
+ "%s: Unable to enable tuner(%i).\n",
+ dev->name, i);
+ }
+ break;
+ }
+ case SAA7134_BOARD_BEHOLD_H6:
+ {
+ u8 data[] = { 0x09, 0x9f, 0x86, 0x11};
+ struct i2c_msg msg = {.addr = 0x61, .flags = 0, .buf = data,
+ .len = sizeof(data)};
+
+ /* The tuner TUNER_PHILIPS_FMD1216MEX_MK3 after hardware */
+ /* start has disabled IF and enabled DVB-T. When saa7134 */
+ /* scan I2C devices it not detect IF tda9887 and can`t */
+ /* watch TV without software reboot. For solve this problem */
+ /* switch the tuner to analog TV mode manually. */
+ if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1)
+ printk(KERN_WARNING
+ "%s: Unable to enable IF of the tuner.\n",
+ dev->name);
+ break;
+ }
+ case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG:
+ saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0x4000);
+ saa_writel(SAA7134_GPIO_GPSTATUS0 >> 2, 0x4000);
+
+ saa7134_set_gpio(dev, 27, 0);
+ break;
+ } /* switch() */
+
+ /* initialize tuner */
+ if (TUNER_ABSENT != dev->tuner_type) {
+ int has_demod = (dev->tda9887_conf & TDA9887_PRESENT);
+
+ /* Note: radio tuner address is always filled in,
+ so we do not need to probe for a radio tuner device. */
+ if (dev->radio_type != UNSET)
+ v4l2_i2c_new_subdev(&dev->v4l2_dev,
+ &dev->i2c_adap, "tuner",
+ dev->radio_addr, NULL);
+ if (has_demod)
+ v4l2_i2c_new_subdev(&dev->v4l2_dev,
+ &dev->i2c_adap, "tuner",
+ 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
+ if (dev->tuner_addr == ADDR_UNSET) {
+ enum v4l2_i2c_tuner_type type =
+ has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV;
+
+ v4l2_i2c_new_subdev(&dev->v4l2_dev,
+ &dev->i2c_adap, "tuner",
+ 0, v4l2_i2c_tuner_addrs(type));
+ } else {
+ v4l2_i2c_new_subdev(&dev->v4l2_dev,
+ &dev->i2c_adap, "tuner",
+ dev->tuner_addr, NULL);
+ }
+ }
+
+ saa7134_tuner_setup(dev);
+
+ switch (dev->board) {
+ case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM:
+ case SAA7134_BOARD_AVERMEDIA_CARDBUS_501:
+ {
+ struct v4l2_priv_tun_config tea5767_cfg;
+ struct tea5767_ctrl ctl;
+
+ dev->i2c_client.addr = 0xC0;
+ /* set TEA5767(analog FM) defines */
+ memset(&ctl, 0, sizeof(ctl));
+ ctl.xtal_freq = TEA5767_HIGH_LO_13MHz;
+ tea5767_cfg.tuner = TUNER_TEA5767;
+ tea5767_cfg.priv = &ctl;
+ saa_call_all(dev, tuner, s_config, &tea5767_cfg);
+ break;
+ }
+ } /* switch() */
+
+ return 0;
+}
diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c
new file mode 100644
index 00000000..5fbb4e49
--- /dev/null
+++ b/drivers/media/video/saa7134/saa7134-core.c
@@ -0,0 +1,1368 @@
+/*
+ *
+ * device driver for philips saa7134 based TV cards
+ * driver core
+ *
+ * (c) 2001-03 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/kmod.h>
+#include <linux/sound.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/dma-mapping.h>
+#include <linux/pm.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+
+MODULE_DESCRIPTION("v4l2 driver module for saa7130/34 based TV cards");
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(SAA7134_VERSION);
+
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int irq_debug;
+module_param(irq_debug, int, 0644);
+MODULE_PARM_DESC(irq_debug,"enable debug messages [IRQ handler]");
+
+static unsigned int core_debug;
+module_param(core_debug, int, 0644);
+MODULE_PARM_DESC(core_debug,"enable debug messages [core]");
+
+static unsigned int gpio_tracking;
+module_param(gpio_tracking, int, 0644);
+MODULE_PARM_DESC(gpio_tracking,"enable debug messages [gpio]");
+
+static unsigned int alsa = 1;
+module_param(alsa, int, 0644);
+MODULE_PARM_DESC(alsa,"enable/disable ALSA DMA sound [dmasound]");
+
+static unsigned int latency = UNSET;
+module_param(latency, int, 0444);
+MODULE_PARM_DESC(latency,"pci latency timer");
+
+int saa7134_no_overlay=-1;
+module_param_named(no_overlay, saa7134_no_overlay, int, 0444);
+MODULE_PARM_DESC(no_overlay,"allow override overlay default (0 disables, 1 enables)"
+ " [some VIA/SIS chipsets are known to have problem with overlay]");
+
+static unsigned int video_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
+static unsigned int vbi_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
+static unsigned int radio_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
+static unsigned int tuner[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
+static unsigned int card[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
+
+
+module_param_array(video_nr, int, NULL, 0444);
+module_param_array(vbi_nr, int, NULL, 0444);
+module_param_array(radio_nr, int, NULL, 0444);
+module_param_array(tuner, int, NULL, 0444);
+module_param_array(card, int, NULL, 0444);
+
+MODULE_PARM_DESC(video_nr, "video device number");
+MODULE_PARM_DESC(vbi_nr, "vbi device number");
+MODULE_PARM_DESC(radio_nr, "radio device number");
+MODULE_PARM_DESC(tuner, "tuner type");
+MODULE_PARM_DESC(card, "card type");
+
+DEFINE_MUTEX(saa7134_devlist_lock);
+EXPORT_SYMBOL(saa7134_devlist_lock);
+LIST_HEAD(saa7134_devlist);
+EXPORT_SYMBOL(saa7134_devlist);
+static LIST_HEAD(mops_list);
+static unsigned int saa7134_devcount;
+
+int (*saa7134_dmasound_init)(struct saa7134_dev *dev);
+int (*saa7134_dmasound_exit)(struct saa7134_dev *dev);
+
+#define dprintk(fmt, arg...) if (core_debug) \
+ printk(KERN_DEBUG "%s/core: " fmt, dev->name , ## arg)
+
+void saa7134_track_gpio(struct saa7134_dev *dev, char *msg)
+{
+ unsigned long mode,status;
+
+ if (!gpio_tracking)
+ return;
+ /* rising SAA7134_GPIO_GPRESCAN reads the status */
+ saa_andorb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN,0);
+ saa_andorb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN,SAA7134_GPIO_GPRESCAN);
+ mode = saa_readl(SAA7134_GPIO_GPMODE0 >> 2) & 0xfffffff;
+ status = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2) & 0xfffffff;
+ printk(KERN_DEBUG
+ "%s: gpio: mode=0x%07lx in=0x%07lx out=0x%07lx [%s]\n",
+ dev->name, mode, (~mode) & status, mode & status, msg);
+}
+
+void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value)
+{
+ u32 index, bitval;
+
+ index = 1 << bit_no;
+ switch (value) {
+ case 0: /* static value */
+ case 1: dprintk("setting GPIO%d to static %d\n", bit_no, value);
+ /* turn sync mode off if necessary */
+ if (index & 0x00c00000)
+ saa_andorb(SAA7134_VIDEO_PORT_CTRL6, 0x0f, 0x00);
+ if (value)
+ bitval = index;
+ else
+ bitval = 0;
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, index, index);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, index, bitval);
+ break;
+ case 3: /* tristate */
+ dprintk("setting GPIO%d to tristate\n", bit_no);
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, index, 0);
+ break;
+ }
+}
+
+/* ------------------------------------------------------------------ */
+
+
+/* ----------------------------------------------------------- */
+/* delayed request_module */
+
+#if defined(CONFIG_MODULES) && defined(MODULE)
+
+static void request_module_async(struct work_struct *work){
+ struct saa7134_dev* dev = container_of(work, struct saa7134_dev, request_module_wk);
+ if (card_is_empress(dev))
+ request_module("saa7134-empress");
+ if (card_is_dvb(dev))
+ request_module("saa7134-dvb");
+ if (alsa) {
+ if (dev->pci->device != PCI_DEVICE_ID_PHILIPS_SAA7130)
+ request_module("saa7134-alsa");
+ }
+}
+
+static void request_submodules(struct saa7134_dev *dev)
+{
+ INIT_WORK(&dev->request_module_wk, request_module_async);
+ schedule_work(&dev->request_module_wk);
+}
+
+static void flush_request_submodules(struct saa7134_dev *dev)
+{
+ flush_work_sync(&dev->request_module_wk);
+}
+
+#else
+#define request_submodules(dev)
+#define flush_request_submodules(dev)
+#endif /* CONFIG_MODULES */
+
+/* ------------------------------------------------------------------ */
+
+/* nr of (saa7134-)pages for the given buffer size */
+static int saa7134_buffer_pages(int size)
+{
+ size = PAGE_ALIGN(size);
+ size += PAGE_SIZE; /* for non-page-aligned buffers */
+ size /= 4096;
+ return size;
+}
+
+/* calc max # of buffers from size (must not exceed the 4MB virtual
+ * address space per DMA channel) */
+int saa7134_buffer_count(unsigned int size, unsigned int count)
+{
+ unsigned int maxcount;
+
+ maxcount = 1024 / saa7134_buffer_pages(size);
+ if (count > maxcount)
+ count = maxcount;
+ return count;
+}
+
+int saa7134_buffer_startpage(struct saa7134_buf *buf)
+{
+ return saa7134_buffer_pages(buf->vb.bsize) * buf->vb.i;
+}
+
+unsigned long saa7134_buffer_base(struct saa7134_buf *buf)
+{
+ unsigned long base;
+ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+
+ base = saa7134_buffer_startpage(buf) * 4096;
+ base += dma->sglist[0].offset;
+ return base;
+}
+
+/* ------------------------------------------------------------------ */
+
+int saa7134_pgtable_alloc(struct pci_dev *pci, struct saa7134_pgtable *pt)
+{
+ __le32 *cpu;
+ dma_addr_t dma_addr = 0;
+
+ cpu = pci_alloc_consistent(pci, SAA7134_PGTABLE_SIZE, &dma_addr);
+ if (NULL == cpu)
+ return -ENOMEM;
+ pt->size = SAA7134_PGTABLE_SIZE;
+ pt->cpu = cpu;
+ pt->dma = dma_addr;
+ return 0;
+}
+
+int saa7134_pgtable_build(struct pci_dev *pci, struct saa7134_pgtable *pt,
+ struct scatterlist *list, unsigned int length,
+ unsigned int startpage)
+{
+ __le32 *ptr;
+ unsigned int i,p;
+
+ BUG_ON(NULL == pt || NULL == pt->cpu);
+
+ ptr = pt->cpu + startpage;
+ for (i = 0; i < length; i++, list++)
+ for (p = 0; p * 4096 < list->length; p++, ptr++)
+ *ptr = cpu_to_le32(sg_dma_address(list) - list->offset);
+ return 0;
+}
+
+void saa7134_pgtable_free(struct pci_dev *pci, struct saa7134_pgtable *pt)
+{
+ if (NULL == pt->cpu)
+ return;
+ pci_free_consistent(pci, pt->size, pt->cpu, pt->dma);
+ pt->cpu = NULL;
+}
+
+/* ------------------------------------------------------------------ */
+
+void saa7134_dma_free(struct videobuf_queue *q,struct saa7134_buf *buf)
+{
+ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+ BUG_ON(in_interrupt());
+
+ videobuf_waiton(q, &buf->vb, 0, 0);
+ videobuf_dma_unmap(q->dev, dma);
+ videobuf_dma_free(dma);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+/* ------------------------------------------------------------------ */
+
+int saa7134_buffer_queue(struct saa7134_dev *dev,
+ struct saa7134_dmaqueue *q,
+ struct saa7134_buf *buf)
+{
+ struct saa7134_buf *next = NULL;
+
+ assert_spin_locked(&dev->slock);
+ dprintk("buffer_queue %p\n",buf);
+ if (NULL == q->curr) {
+ if (!q->need_two) {
+ q->curr = buf;
+ buf->activate(dev,buf,NULL);
+ } else if (list_empty(&q->queue)) {
+ list_add_tail(&buf->vb.queue,&q->queue);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ } else {
+ next = list_entry(q->queue.next,struct saa7134_buf,
+ vb.queue);
+ q->curr = buf;
+ buf->activate(dev,buf,next);
+ }
+ } else {
+ list_add_tail(&buf->vb.queue,&q->queue);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ }
+ return 0;
+}
+
+void saa7134_buffer_finish(struct saa7134_dev *dev,
+ struct saa7134_dmaqueue *q,
+ unsigned int state)
+{
+ assert_spin_locked(&dev->slock);
+ dprintk("buffer_finish %p\n",q->curr);
+
+ /* finish current buffer */
+ q->curr->vb.state = state;
+ do_gettimeofday(&q->curr->vb.ts);
+ wake_up(&q->curr->vb.done);
+ q->curr = NULL;
+}
+
+void saa7134_buffer_next(struct saa7134_dev *dev,
+ struct saa7134_dmaqueue *q)
+{
+ struct saa7134_buf *buf,*next = NULL;
+
+ assert_spin_locked(&dev->slock);
+ BUG_ON(NULL != q->curr);
+
+ if (!list_empty(&q->queue)) {
+ /* activate next one from queue */
+ buf = list_entry(q->queue.next,struct saa7134_buf,vb.queue);
+ dprintk("buffer_next %p [prev=%p/next=%p]\n",
+ buf,q->queue.prev,q->queue.next);
+ list_del(&buf->vb.queue);
+ if (!list_empty(&q->queue))
+ next = list_entry(q->queue.next,struct saa7134_buf,
+ vb.queue);
+ q->curr = buf;
+ buf->activate(dev,buf,next);
+ dprintk("buffer_next #2 prev=%p/next=%p\n",
+ q->queue.prev,q->queue.next);
+ } else {
+ /* nothing to do -- just stop DMA */
+ dprintk("buffer_next %p\n",NULL);
+ saa7134_set_dmabits(dev);
+ del_timer(&q->timeout);
+
+ if (card_has_mpeg(dev))
+ if (dev->ts_started)
+ saa7134_ts_stop(dev);
+ }
+}
+
+void saa7134_buffer_timeout(unsigned long data)
+{
+ struct saa7134_dmaqueue *q = (struct saa7134_dmaqueue*)data;
+ struct saa7134_dev *dev = q->dev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->slock,flags);
+
+ /* try to reset the hardware (SWRST) */
+ saa_writeb(SAA7134_REGION_ENABLE, 0x00);
+ saa_writeb(SAA7134_REGION_ENABLE, 0x80);
+ saa_writeb(SAA7134_REGION_ENABLE, 0x00);
+
+ /* flag current buffer as failed,
+ try to start over with the next one. */
+ if (q->curr) {
+ dprintk("timeout on %p\n",q->curr);
+ saa7134_buffer_finish(dev,q,VIDEOBUF_ERROR);
+ }
+ saa7134_buffer_next(dev,q);
+ spin_unlock_irqrestore(&dev->slock,flags);
+}
+
+/* ------------------------------------------------------------------ */
+
+int saa7134_set_dmabits(struct saa7134_dev *dev)
+{
+ u32 split, task=0, ctrl=0, irq=0;
+ enum v4l2_field cap = V4L2_FIELD_ANY;
+ enum v4l2_field ov = V4L2_FIELD_ANY;
+
+ assert_spin_locked(&dev->slock);
+
+ if (dev->insuspend)
+ return 0;
+
+ /* video capture -- dma 0 + video task A */
+ if (dev->video_q.curr) {
+ task |= 0x01;
+ ctrl |= SAA7134_MAIN_CTRL_TE0;
+ irq |= SAA7134_IRQ1_INTE_RA0_1 |
+ SAA7134_IRQ1_INTE_RA0_0;
+ cap = dev->video_q.curr->vb.field;
+ }
+
+ /* video capture -- dma 1+2 (planar modes) */
+ if (dev->video_q.curr &&
+ dev->video_q.curr->fmt->planar) {
+ ctrl |= SAA7134_MAIN_CTRL_TE4 |
+ SAA7134_MAIN_CTRL_TE5;
+ }
+
+ /* screen overlay -- dma 0 + video task B */
+ if (dev->ovenable) {
+ task |= 0x10;
+ ctrl |= SAA7134_MAIN_CTRL_TE1;
+ ov = dev->ovfield;
+ }
+
+ /* vbi capture -- dma 0 + vbi task A+B */
+ if (dev->vbi_q.curr) {
+ task |= 0x22;
+ ctrl |= SAA7134_MAIN_CTRL_TE2 |
+ SAA7134_MAIN_CTRL_TE3;
+ irq |= SAA7134_IRQ1_INTE_RA0_7 |
+ SAA7134_IRQ1_INTE_RA0_6 |
+ SAA7134_IRQ1_INTE_RA0_5 |
+ SAA7134_IRQ1_INTE_RA0_4;
+ }
+
+ /* audio capture -- dma 3 */
+ if (dev->dmasound.dma_running) {
+ ctrl |= SAA7134_MAIN_CTRL_TE6;
+ irq |= SAA7134_IRQ1_INTE_RA3_1 |
+ SAA7134_IRQ1_INTE_RA3_0;
+ }
+
+ /* TS capture -- dma 5 */
+ if (dev->ts_q.curr) {
+ ctrl |= SAA7134_MAIN_CTRL_TE5;
+ irq |= SAA7134_IRQ1_INTE_RA2_1 |
+ SAA7134_IRQ1_INTE_RA2_0;
+ }
+
+ /* set task conditions + field handling */
+ if (V4L2_FIELD_HAS_BOTH(cap) || V4L2_FIELD_HAS_BOTH(ov) || cap == ov) {
+ /* default config -- use full frames */
+ saa_writeb(SAA7134_TASK_CONDITIONS(TASK_A), 0x0d);
+ saa_writeb(SAA7134_TASK_CONDITIONS(TASK_B), 0x0d);
+ saa_writeb(SAA7134_FIELD_HANDLING(TASK_A), 0x02);
+ saa_writeb(SAA7134_FIELD_HANDLING(TASK_B), 0x02);
+ split = 0;
+ } else {
+ /* split fields between tasks */
+ if (V4L2_FIELD_TOP == cap) {
+ /* odd A, even B, repeat */
+ saa_writeb(SAA7134_TASK_CONDITIONS(TASK_A), 0x0d);
+ saa_writeb(SAA7134_TASK_CONDITIONS(TASK_B), 0x0e);
+ } else {
+ /* odd B, even A, repeat */
+ saa_writeb(SAA7134_TASK_CONDITIONS(TASK_A), 0x0e);
+ saa_writeb(SAA7134_TASK_CONDITIONS(TASK_B), 0x0d);
+ }
+ saa_writeb(SAA7134_FIELD_HANDLING(TASK_A), 0x01);
+ saa_writeb(SAA7134_FIELD_HANDLING(TASK_B), 0x01);
+ split = 1;
+ }
+
+ /* irqs */
+ saa_writeb(SAA7134_REGION_ENABLE, task);
+ saa_writel(SAA7134_IRQ1, irq);
+ saa_andorl(SAA7134_MAIN_CTRL,
+ SAA7134_MAIN_CTRL_TE0 |
+ SAA7134_MAIN_CTRL_TE1 |
+ SAA7134_MAIN_CTRL_TE2 |
+ SAA7134_MAIN_CTRL_TE3 |
+ SAA7134_MAIN_CTRL_TE4 |
+ SAA7134_MAIN_CTRL_TE5 |
+ SAA7134_MAIN_CTRL_TE6,
+ ctrl);
+ dprintk("dmabits: task=0x%02x ctrl=0x%02x irq=0x%x split=%s\n",
+ task, ctrl, irq, split ? "no" : "yes");
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+/* IRQ handler + helpers */
+
+static char *irqbits[] = {
+ "DONE_RA0", "DONE_RA1", "DONE_RA2", "DONE_RA3",
+ "AR", "PE", "PWR_ON", "RDCAP", "INTL", "FIDT", "MMC",
+ "TRIG_ERR", "CONF_ERR", "LOAD_ERR",
+ "GPIO16", "GPIO18", "GPIO22", "GPIO23"
+};
+#define IRQBITS ARRAY_SIZE(irqbits)
+
+static void print_irqstatus(struct saa7134_dev *dev, int loop,
+ unsigned long report, unsigned long status)
+{
+ unsigned int i;
+
+ printk(KERN_DEBUG "%s/irq[%d,%ld]: r=0x%lx s=0x%02lx",
+ dev->name,loop,jiffies,report,status);
+ for (i = 0; i < IRQBITS; i++) {
+ if (!(report & (1 << i)))
+ continue;
+ printk(" %s",irqbits[i]);
+ }
+ if (report & SAA7134_IRQ_REPORT_DONE_RA0) {
+ printk(" | RA0=%s,%s,%s,%ld",
+ (status & 0x40) ? "vbi" : "video",
+ (status & 0x20) ? "b" : "a",
+ (status & 0x10) ? "odd" : "even",
+ (status & 0x0f));
+ }
+ printk("\n");
+}
+
+static irqreturn_t saa7134_irq(int irq, void *dev_id)
+{
+ struct saa7134_dev *dev = (struct saa7134_dev*) dev_id;
+ unsigned long report,status;
+ int loop, handled = 0;
+
+ if (dev->insuspend)
+ goto out;
+
+ for (loop = 0; loop < 10; loop++) {
+ report = saa_readl(SAA7134_IRQ_REPORT);
+ status = saa_readl(SAA7134_IRQ_STATUS);
+
+ /* If dmasound support is active and we get a sound report,
+ * mask out the report and let the saa7134-alsa module deal
+ * with it */
+ if ((report & SAA7134_IRQ_REPORT_DONE_RA3) &&
+ (dev->dmasound.priv_data != NULL) )
+ {
+ if (irq_debug > 1)
+ printk(KERN_DEBUG "%s/irq: preserving DMA sound interrupt\n",
+ dev->name);
+ report &= ~SAA7134_IRQ_REPORT_DONE_RA3;
+ }
+
+ if (0 == report) {
+ if (irq_debug > 1)
+ printk(KERN_DEBUG "%s/irq: no (more) work\n",
+ dev->name);
+ goto out;
+ }
+
+ handled = 1;
+ saa_writel(SAA7134_IRQ_REPORT,report);
+ if (irq_debug)
+ print_irqstatus(dev,loop,report,status);
+
+
+ if ((report & SAA7134_IRQ_REPORT_RDCAP) ||
+ (report & SAA7134_IRQ_REPORT_INTL))
+ saa7134_irq_video_signalchange(dev);
+
+
+ if ((report & SAA7134_IRQ_REPORT_DONE_RA0) &&
+ (status & 0x60) == 0)
+ saa7134_irq_video_done(dev,status);
+
+ if ((report & SAA7134_IRQ_REPORT_DONE_RA0) &&
+ (status & 0x40) == 0x40)
+ saa7134_irq_vbi_done(dev,status);
+
+ if ((report & SAA7134_IRQ_REPORT_DONE_RA2) &&
+ card_has_mpeg(dev))
+ saa7134_irq_ts_done(dev,status);
+
+ if (report & SAA7134_IRQ_REPORT_GPIO16) {
+ switch (dev->has_remote) {
+ case SAA7134_REMOTE_GPIO:
+ if (!dev->remote)
+ break;
+ if (dev->remote->mask_keydown & 0x10000) {
+ saa7134_input_irq(dev);
+ }
+ break;
+
+ case SAA7134_REMOTE_I2C:
+ break; /* FIXME: invoke I2C get_key() */
+
+ default: /* GPIO16 not used by IR remote */
+ break;
+ }
+ }
+
+ if (report & SAA7134_IRQ_REPORT_GPIO18) {
+ switch (dev->has_remote) {
+ case SAA7134_REMOTE_GPIO:
+ if (!dev->remote)
+ break;
+ if ((dev->remote->mask_keydown & 0x40000) ||
+ (dev->remote->mask_keyup & 0x40000)) {
+ saa7134_input_irq(dev);
+ }
+ break;
+
+ case SAA7134_REMOTE_I2C:
+ break; /* FIXME: invoke I2C get_key() */
+
+ default: /* GPIO18 not used by IR remote */
+ break;
+ }
+ }
+ }
+
+ if (10 == loop) {
+ print_irqstatus(dev,loop,report,status);
+ if (report & SAA7134_IRQ_REPORT_PE) {
+ /* disable all parity error */
+ printk(KERN_WARNING "%s/irq: looping -- "
+ "clearing PE (parity error!) enable bit\n",dev->name);
+ saa_clearl(SAA7134_IRQ2,SAA7134_IRQ2_INTE_PE);
+ } else if (report & SAA7134_IRQ_REPORT_GPIO16) {
+ /* disable gpio16 IRQ */
+ printk(KERN_WARNING "%s/irq: looping -- "
+ "clearing GPIO16 enable bit\n",dev->name);
+ saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO16_P);
+ saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO16_N);
+ } else if (report & SAA7134_IRQ_REPORT_GPIO18) {
+ /* disable gpio18 IRQs */
+ printk(KERN_WARNING "%s/irq: looping -- "
+ "clearing GPIO18 enable bit\n",dev->name);
+ saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18_P);
+ saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18_N);
+ } else {
+ /* disable all irqs */
+ printk(KERN_WARNING "%s/irq: looping -- "
+ "clearing all enable bits\n",dev->name);
+ saa_writel(SAA7134_IRQ1,0);
+ saa_writel(SAA7134_IRQ2,0);
+ }
+ }
+
+ out:
+ return IRQ_RETVAL(handled);
+}
+
+/* ------------------------------------------------------------------ */
+
+/* early init (no i2c, no irq) */
+
+static int saa7134_hw_enable1(struct saa7134_dev *dev)
+{
+ /* RAM FIFO config */
+ saa_writel(SAA7134_FIFO_SIZE, 0x08070503);
+ saa_writel(SAA7134_THRESHOULD, 0x02020202);
+
+ /* enable audio + video processing */
+ saa_writel(SAA7134_MAIN_CTRL,
+ SAA7134_MAIN_CTRL_VPLLE |
+ SAA7134_MAIN_CTRL_APLLE |
+ SAA7134_MAIN_CTRL_EXOSC |
+ SAA7134_MAIN_CTRL_EVFE1 |
+ SAA7134_MAIN_CTRL_EVFE2 |
+ SAA7134_MAIN_CTRL_ESFE |
+ SAA7134_MAIN_CTRL_EBDAC);
+
+ /*
+ * Initialize OSS _after_ enabling audio clock PLL and audio processing.
+ * OSS initialization writes to registers via the audio DSP; these
+ * writes will fail unless the audio clock has been started. At worst,
+ * audio will not work.
+ */
+
+ /* enable peripheral devices */
+ saa_writeb(SAA7134_SPECIAL_MODE, 0x01);
+
+ /* set vertical line numbering start (vbi needs this) */
+ saa_writeb(SAA7134_SOURCE_TIMING2, 0x20);
+
+ return 0;
+}
+
+static int saa7134_hwinit1(struct saa7134_dev *dev)
+{
+ dprintk("hwinit1\n");
+
+ saa_writel(SAA7134_IRQ1, 0);
+ saa_writel(SAA7134_IRQ2, 0);
+
+ /* Clear any stale IRQ reports */
+ saa_writel(SAA7134_IRQ_REPORT, saa_readl(SAA7134_IRQ_REPORT));
+
+ mutex_init(&dev->lock);
+ spin_lock_init(&dev->slock);
+
+ saa7134_track_gpio(dev,"pre-init");
+ saa7134_video_init1(dev);
+ saa7134_vbi_init1(dev);
+ if (card_has_mpeg(dev))
+ saa7134_ts_init1(dev);
+ saa7134_input_init1(dev);
+
+ saa7134_hw_enable1(dev);
+
+ return 0;
+}
+
+/* late init (with i2c + irq) */
+static int saa7134_hw_enable2(struct saa7134_dev *dev)
+{
+
+ unsigned int irq2_mask;
+
+ /* enable IRQ's */
+ irq2_mask =
+ SAA7134_IRQ2_INTE_DEC3 |
+ SAA7134_IRQ2_INTE_DEC2 |
+ SAA7134_IRQ2_INTE_DEC1 |
+ SAA7134_IRQ2_INTE_DEC0 |
+ SAA7134_IRQ2_INTE_PE |
+ SAA7134_IRQ2_INTE_AR;
+
+ if (dev->has_remote == SAA7134_REMOTE_GPIO && dev->remote) {
+ if (dev->remote->mask_keydown & 0x10000)
+ irq2_mask |= SAA7134_IRQ2_INTE_GPIO16_N;
+ else { /* Allow enabling both IRQ edge triggers */
+ if (dev->remote->mask_keydown & 0x40000)
+ irq2_mask |= SAA7134_IRQ2_INTE_GPIO18_P;
+ if (dev->remote->mask_keyup & 0x40000)
+ irq2_mask |= SAA7134_IRQ2_INTE_GPIO18_N;
+ }
+ }
+
+ if (dev->has_remote == SAA7134_REMOTE_I2C) {
+ request_module("ir-kbd-i2c");
+ }
+
+ saa_writel(SAA7134_IRQ1, 0);
+ saa_writel(SAA7134_IRQ2, irq2_mask);
+
+ return 0;
+}
+
+static int saa7134_hwinit2(struct saa7134_dev *dev)
+{
+
+ dprintk("hwinit2\n");
+
+ saa7134_video_init2(dev);
+ saa7134_tvaudio_init2(dev);
+
+ saa7134_hw_enable2(dev);
+
+ return 0;
+}
+
+
+/* shutdown */
+static int saa7134_hwfini(struct saa7134_dev *dev)
+{
+ dprintk("hwfini\n");
+
+ if (card_has_mpeg(dev))
+ saa7134_ts_fini(dev);
+ saa7134_input_fini(dev);
+ saa7134_vbi_fini(dev);
+ saa7134_tvaudio_fini(dev);
+ return 0;
+}
+
+static void __devinit must_configure_manually(int has_eeprom)
+{
+ unsigned int i,p;
+
+ if (!has_eeprom)
+ printk(KERN_WARNING
+ "saa7134: <rant>\n"
+ "saa7134: Congratulations! Your TV card vendor saved a few\n"
+ "saa7134: cents for a eeprom, thus your pci board has no\n"
+ "saa7134: subsystem ID and I can't identify it automatically\n"
+ "saa7134: </rant>\n"
+ "saa7134: I feel better now. Ok, here are the good news:\n"
+ "saa7134: You can use the card=<nr> insmod option to specify\n"
+ "saa7134: which board do you have. The list:\n");
+ else
+ printk(KERN_WARNING
+ "saa7134: Board is currently unknown. You might try to use the card=<nr>\n"
+ "saa7134: insmod option to specify which board do you have, but this is\n"
+ "saa7134: somewhat risky, as might damage your card. It is better to ask\n"
+ "saa7134: for support at linux-media@vger.kernel.org.\n"
+ "saa7134: The supported cards are:\n");
+
+ for (i = 0; i < saa7134_bcount; i++) {
+ printk(KERN_WARNING "saa7134: card=%d -> %-40.40s",
+ i,saa7134_boards[i].name);
+ for (p = 0; saa7134_pci_tbl[p].driver_data; p++) {
+ if (saa7134_pci_tbl[p].driver_data != i)
+ continue;
+ printk(" %04x:%04x",
+ saa7134_pci_tbl[p].subvendor,
+ saa7134_pci_tbl[p].subdevice);
+ }
+ printk("\n");
+ }
+}
+
+static struct video_device *vdev_init(struct saa7134_dev *dev,
+ struct video_device *template,
+ char *type)
+{
+ struct video_device *vfd;
+
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+ *vfd = *template;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+ vfd->release = video_device_release;
+ vfd->debug = video_debug;
+ snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
+ dev->name, type, saa7134_boards[dev->board].name);
+ video_set_drvdata(vfd, dev);
+ return vfd;
+}
+
+static void saa7134_unregister_video(struct saa7134_dev *dev)
+{
+ if (dev->video_dev) {
+ if (video_is_registered(dev->video_dev))
+ video_unregister_device(dev->video_dev);
+ else
+ video_device_release(dev->video_dev);
+ dev->video_dev = NULL;
+ }
+ if (dev->vbi_dev) {
+ if (video_is_registered(dev->vbi_dev))
+ video_unregister_device(dev->vbi_dev);
+ else
+ video_device_release(dev->vbi_dev);
+ dev->vbi_dev = NULL;
+ }
+ if (dev->radio_dev) {
+ if (video_is_registered(dev->radio_dev))
+ video_unregister_device(dev->radio_dev);
+ else
+ video_device_release(dev->radio_dev);
+ dev->radio_dev = NULL;
+ }
+}
+
+static void mpeg_ops_attach(struct saa7134_mpeg_ops *ops,
+ struct saa7134_dev *dev)
+{
+ int err;
+
+ if (NULL != dev->mops)
+ return;
+ if (saa7134_boards[dev->board].mpeg != ops->type)
+ return;
+ err = ops->init(dev);
+ if (0 != err)
+ return;
+ dev->mops = ops;
+}
+
+static void mpeg_ops_detach(struct saa7134_mpeg_ops *ops,
+ struct saa7134_dev *dev)
+{
+ if (NULL == dev->mops)
+ return;
+ if (dev->mops != ops)
+ return;
+ dev->mops->fini(dev);
+ dev->mops = NULL;
+}
+
+static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_id)
+{
+ struct saa7134_dev *dev;
+ struct saa7134_mpeg_ops *mops;
+ int err;
+
+ if (saa7134_devcount == SAA7134_MAXBOARDS)
+ return -ENOMEM;
+
+ dev = kzalloc(sizeof(*dev),GFP_KERNEL);
+ if (NULL == dev)
+ return -ENOMEM;
+
+ err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev);
+ if (err)
+ goto fail0;
+
+ /* pci init */
+ dev->pci = pci_dev;
+ if (pci_enable_device(pci_dev)) {
+ err = -EIO;
+ goto fail1;
+ }
+
+ dev->nr = saa7134_devcount;
+ sprintf(dev->name,"saa%x[%d]",pci_dev->device,dev->nr);
+
+ /* pci quirks */
+ if (pci_pci_problems) {
+ if (pci_pci_problems & PCIPCI_TRITON)
+ printk(KERN_INFO "%s: quirk: PCIPCI_TRITON\n", dev->name);
+ if (pci_pci_problems & PCIPCI_NATOMA)
+ printk(KERN_INFO "%s: quirk: PCIPCI_NATOMA\n", dev->name);
+ if (pci_pci_problems & PCIPCI_VIAETBF)
+ printk(KERN_INFO "%s: quirk: PCIPCI_VIAETBF\n", dev->name);
+ if (pci_pci_problems & PCIPCI_VSFX)
+ printk(KERN_INFO "%s: quirk: PCIPCI_VSFX\n",dev->name);
+#ifdef PCIPCI_ALIMAGIK
+ if (pci_pci_problems & PCIPCI_ALIMAGIK) {
+ printk(KERN_INFO "%s: quirk: PCIPCI_ALIMAGIK -- latency fixup\n",
+ dev->name);
+ latency = 0x0A;
+ }
+#endif
+ if (pci_pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL)) {
+ printk(KERN_INFO "%s: quirk: this driver and your "
+ "chipset may not work together"
+ " in overlay mode.\n",dev->name);
+ if (!saa7134_no_overlay) {
+ printk(KERN_INFO "%s: quirk: overlay "
+ "mode will be disabled.\n",
+ dev->name);
+ saa7134_no_overlay = 1;
+ } else {
+ printk(KERN_INFO "%s: quirk: overlay "
+ "mode will be forced. Use this"
+ " option at your own risk.\n",
+ dev->name);
+ }
+ }
+ }
+ if (UNSET != latency) {
+ printk(KERN_INFO "%s: setting pci latency timer to %d\n",
+ dev->name,latency);
+ pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, latency);
+ }
+
+ /* print pci info */
+ dev->pci_rev = pci_dev->revision;
+ pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat);
+ printk(KERN_INFO "%s: found at %s, rev: %d, irq: %d, "
+ "latency: %d, mmio: 0x%llx\n", dev->name,
+ pci_name(pci_dev), dev->pci_rev, pci_dev->irq,
+ dev->pci_lat,(unsigned long long)pci_resource_start(pci_dev,0));
+ pci_set_master(pci_dev);
+ if (!pci_dma_supported(pci_dev, DMA_BIT_MASK(32))) {
+ printk("%s: Oops: no 32bit PCI DMA ???\n",dev->name);
+ err = -EIO;
+ goto fail1;
+ }
+
+ /* board config */
+ dev->board = pci_id->driver_data;
+ if (card[dev->nr] >= 0 &&
+ card[dev->nr] < saa7134_bcount)
+ dev->board = card[dev->nr];
+ if (SAA7134_BOARD_UNKNOWN == dev->board)
+ must_configure_manually(0);
+ else if (SAA7134_BOARD_NOAUTO == dev->board) {
+ must_configure_manually(1);
+ dev->board = SAA7134_BOARD_UNKNOWN;
+ }
+ dev->autodetected = card[dev->nr] != dev->board;
+ dev->tuner_type = saa7134_boards[dev->board].tuner_type;
+ dev->tuner_addr = saa7134_boards[dev->board].tuner_addr;
+ dev->radio_type = saa7134_boards[dev->board].radio_type;
+ dev->radio_addr = saa7134_boards[dev->board].radio_addr;
+ dev->tda9887_conf = saa7134_boards[dev->board].tda9887_conf;
+ if (UNSET != tuner[dev->nr])
+ dev->tuner_type = tuner[dev->nr];
+ printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n",
+ dev->name,pci_dev->subsystem_vendor,
+ pci_dev->subsystem_device,saa7134_boards[dev->board].name,
+ dev->board, dev->autodetected ?
+ "autodetected" : "insmod option");
+
+ /* get mmio */
+ if (!request_mem_region(pci_resource_start(pci_dev,0),
+ pci_resource_len(pci_dev,0),
+ dev->name)) {
+ err = -EBUSY;
+ printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx\n",
+ dev->name,(unsigned long long)pci_resource_start(pci_dev,0));
+ goto fail1;
+ }
+ dev->lmmio = ioremap(pci_resource_start(pci_dev, 0),
+ pci_resource_len(pci_dev, 0));
+ dev->bmmio = (__u8 __iomem *)dev->lmmio;
+ if (NULL == dev->lmmio) {
+ err = -EIO;
+ printk(KERN_ERR "%s: can't ioremap() MMIO memory\n",
+ dev->name);
+ goto fail2;
+ }
+
+ /* initialize hardware #1 */
+ saa7134_board_init1(dev);
+ saa7134_hwinit1(dev);
+
+ /* get irq */
+ err = request_irq(pci_dev->irq, saa7134_irq,
+ IRQF_SHARED | IRQF_DISABLED, dev->name, dev);
+ if (err < 0) {
+ printk(KERN_ERR "%s: can't get IRQ %d\n",
+ dev->name,pci_dev->irq);
+ goto fail3;
+ }
+
+ /* wait a bit, register i2c bus */
+ msleep(100);
+ saa7134_i2c_register(dev);
+ saa7134_board_init2(dev);
+
+ saa7134_hwinit2(dev);
+
+ /* load i2c helpers */
+ if (card_is_empress(dev)) {
+ struct v4l2_subdev *sd =
+ v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+ "saa6752hs",
+ saa7134_boards[dev->board].empress_addr, NULL);
+
+ if (sd)
+ sd->grp_id = GRP_EMPRESS;
+ }
+
+ if (saa7134_boards[dev->board].rds_addr) {
+ struct v4l2_subdev *sd;
+
+ sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
+ &dev->i2c_adap, "saa6588",
+ 0, I2C_ADDRS(saa7134_boards[dev->board].rds_addr));
+ if (sd) {
+ printk(KERN_INFO "%s: found RDS decoder\n", dev->name);
+ dev->has_rds = 1;
+ }
+ }
+
+ v4l2_prio_init(&dev->prio);
+
+ mutex_lock(&saa7134_devlist_lock);
+ list_for_each_entry(mops, &mops_list, next)
+ mpeg_ops_attach(mops, dev);
+ list_add_tail(&dev->devlist, &saa7134_devlist);
+ mutex_unlock(&saa7134_devlist_lock);
+
+ /* check for signal */
+ saa7134_irq_video_signalchange(dev);
+
+ if (TUNER_ABSENT != dev->tuner_type)
+ saa_call_all(dev, core, s_power, 0);
+
+ /* register v4l devices */
+ if (saa7134_no_overlay > 0)
+ printk(KERN_INFO "%s: Overlay support disabled.\n", dev->name);
+
+ dev->video_dev = vdev_init(dev,&saa7134_video_template,"video");
+ err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER,
+ video_nr[dev->nr]);
+ if (err < 0) {
+ printk(KERN_INFO "%s: can't register video device\n",
+ dev->name);
+ goto fail4;
+ }
+ printk(KERN_INFO "%s: registered device %s [v4l2]\n",
+ dev->name, video_device_node_name(dev->video_dev));
+
+ dev->vbi_dev = vdev_init(dev, &saa7134_video_template, "vbi");
+
+ err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI,
+ vbi_nr[dev->nr]);
+ if (err < 0)
+ goto fail4;
+ printk(KERN_INFO "%s: registered device %s\n",
+ dev->name, video_device_node_name(dev->vbi_dev));
+
+ if (card_has_radio(dev)) {
+ dev->radio_dev = vdev_init(dev,&saa7134_radio_template,"radio");
+ err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO,
+ radio_nr[dev->nr]);
+ if (err < 0)
+ goto fail4;
+ printk(KERN_INFO "%s: registered device %s\n",
+ dev->name, video_device_node_name(dev->radio_dev));
+ }
+
+ /* everything worked */
+ saa7134_devcount++;
+
+ if (saa7134_dmasound_init && !dev->dmasound.priv_data)
+ saa7134_dmasound_init(dev);
+
+ request_submodules(dev);
+ return 0;
+
+ fail4:
+ saa7134_unregister_video(dev);
+ saa7134_i2c_unregister(dev);
+ free_irq(pci_dev->irq, dev);
+ fail3:
+ saa7134_hwfini(dev);
+ iounmap(dev->lmmio);
+ fail2:
+ release_mem_region(pci_resource_start(pci_dev,0),
+ pci_resource_len(pci_dev,0));
+ fail1:
+ v4l2_device_unregister(&dev->v4l2_dev);
+ fail0:
+ kfree(dev);
+ return err;
+}
+
+static void __devexit saa7134_finidev(struct pci_dev *pci_dev)
+{
+ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+ struct saa7134_dev *dev = container_of(v4l2_dev, struct saa7134_dev, v4l2_dev);
+ struct saa7134_mpeg_ops *mops;
+
+ flush_request_submodules(dev);
+
+ /* Release DMA sound modules if present */
+ if (saa7134_dmasound_exit && dev->dmasound.priv_data) {
+ saa7134_dmasound_exit(dev);
+ }
+
+ /* debugging ... */
+ if (irq_debug) {
+ u32 report = saa_readl(SAA7134_IRQ_REPORT);
+ u32 status = saa_readl(SAA7134_IRQ_STATUS);
+ print_irqstatus(dev,42,report,status);
+ }
+
+ /* disable peripheral devices */
+ saa_writeb(SAA7134_SPECIAL_MODE,0);
+
+ /* shutdown hardware */
+ saa_writel(SAA7134_IRQ1,0);
+ saa_writel(SAA7134_IRQ2,0);
+ saa_writel(SAA7134_MAIN_CTRL,0);
+
+ /* shutdown subsystems */
+ saa7134_hwfini(dev);
+
+ /* unregister */
+ mutex_lock(&saa7134_devlist_lock);
+ list_del(&dev->devlist);
+ list_for_each_entry(mops, &mops_list, next)
+ mpeg_ops_detach(mops, dev);
+ mutex_unlock(&saa7134_devlist_lock);
+ saa7134_devcount--;
+
+ saa7134_i2c_unregister(dev);
+ saa7134_unregister_video(dev);
+
+
+ /* the DMA sound modules should be unloaded before reaching
+ this, but just in case they are still present... */
+ if (dev->dmasound.priv_data != NULL) {
+ free_irq(pci_dev->irq, &dev->dmasound);
+ dev->dmasound.priv_data = NULL;
+ }
+
+
+ /* release resources */
+ free_irq(pci_dev->irq, dev);
+ iounmap(dev->lmmio);
+ release_mem_region(pci_resource_start(pci_dev,0),
+ pci_resource_len(pci_dev,0));
+
+
+ v4l2_device_unregister(&dev->v4l2_dev);
+
+ /* free memory */
+ kfree(dev);
+}
+
+#ifdef CONFIG_PM
+
+/* resends a current buffer in queue after resume */
+static int saa7134_buffer_requeue(struct saa7134_dev *dev,
+ struct saa7134_dmaqueue *q)
+{
+ struct saa7134_buf *buf, *next;
+
+ assert_spin_locked(&dev->slock);
+
+ buf = q->curr;
+ next = buf;
+ dprintk("buffer_requeue\n");
+
+ if (!buf)
+ return 0;
+
+ dprintk("buffer_requeue : resending active buffers \n");
+
+ if (!list_empty(&q->queue))
+ next = list_entry(q->queue.next, struct saa7134_buf,
+ vb.queue);
+ buf->activate(dev, buf, next);
+
+ return 0;
+}
+
+static int saa7134_suspend(struct pci_dev *pci_dev , pm_message_t state)
+{
+ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+ struct saa7134_dev *dev = container_of(v4l2_dev, struct saa7134_dev, v4l2_dev);
+
+ /* disable overlay - apps should enable it explicitly on resume*/
+ dev->ovenable = 0;
+
+ /* Disable interrupts, DMA, and rest of the chip*/
+ saa_writel(SAA7134_IRQ1, 0);
+ saa_writel(SAA7134_IRQ2, 0);
+ saa_writel(SAA7134_MAIN_CTRL, 0);
+
+ dev->insuspend = 1;
+ synchronize_irq(pci_dev->irq);
+
+ /* ACK interrupts once more, just in case,
+ since the IRQ handler won't ack them anymore*/
+
+ saa_writel(SAA7134_IRQ_REPORT, saa_readl(SAA7134_IRQ_REPORT));
+
+ /* Disable timeout timers - if we have active buffers, we will
+ fill them on resume*/
+
+ del_timer(&dev->video_q.timeout);
+ del_timer(&dev->vbi_q.timeout);
+ del_timer(&dev->ts_q.timeout);
+
+ if (dev->remote)
+ saa7134_ir_stop(dev);
+
+ pci_save_state(pci_dev);
+ pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state));
+
+ return 0;
+}
+
+static int saa7134_resume(struct pci_dev *pci_dev)
+{
+ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+ struct saa7134_dev *dev = container_of(v4l2_dev, struct saa7134_dev, v4l2_dev);
+ unsigned long flags;
+
+ pci_set_power_state(pci_dev, PCI_D0);
+ pci_restore_state(pci_dev);
+
+ /* Do things that are done in saa7134_initdev ,
+ except of initializing memory structures.*/
+
+ saa7134_board_init1(dev);
+
+ /* saa7134_hwinit1 */
+ if (saa7134_boards[dev->board].video_out)
+ saa7134_videoport_init(dev);
+ if (card_has_mpeg(dev))
+ saa7134_ts_init_hw(dev);
+ if (dev->remote)
+ saa7134_ir_start(dev);
+ saa7134_hw_enable1(dev);
+
+ msleep(100);
+
+ saa7134_board_init2(dev);
+
+ /*saa7134_hwinit2*/
+ saa7134_set_tvnorm_hw(dev);
+ saa7134_tvaudio_setmute(dev);
+ saa7134_tvaudio_setvolume(dev, dev->ctl_volume);
+ saa7134_tvaudio_init(dev);
+ saa7134_enable_i2s(dev);
+ saa7134_hw_enable2(dev);
+
+ saa7134_irq_video_signalchange(dev);
+
+ /*resume unfinished buffer(s)*/
+ spin_lock_irqsave(&dev->slock, flags);
+ saa7134_buffer_requeue(dev, &dev->video_q);
+ saa7134_buffer_requeue(dev, &dev->vbi_q);
+ saa7134_buffer_requeue(dev, &dev->ts_q);
+
+ /* FIXME: Disable DMA audio sound - temporary till proper support
+ is implemented*/
+
+ dev->dmasound.dma_running = 0;
+
+ /* start DMA now*/
+ dev->insuspend = 0;
+ smp_wmb();
+ saa7134_set_dmabits(dev);
+ spin_unlock_irqrestore(&dev->slock, flags);
+
+ return 0;
+}
+#endif
+
+/* ----------------------------------------------------------- */
+
+int saa7134_ts_register(struct saa7134_mpeg_ops *ops)
+{
+ struct saa7134_dev *dev;
+
+ mutex_lock(&saa7134_devlist_lock);
+ list_for_each_entry(dev, &saa7134_devlist, devlist)
+ mpeg_ops_attach(ops, dev);
+ list_add_tail(&ops->next,&mops_list);
+ mutex_unlock(&saa7134_devlist_lock);
+ return 0;
+}
+
+void saa7134_ts_unregister(struct saa7134_mpeg_ops *ops)
+{
+ struct saa7134_dev *dev;
+
+ mutex_lock(&saa7134_devlist_lock);
+ list_del(&ops->next);
+ list_for_each_entry(dev, &saa7134_devlist, devlist)
+ mpeg_ops_detach(ops, dev);
+ mutex_unlock(&saa7134_devlist_lock);
+}
+
+EXPORT_SYMBOL(saa7134_ts_register);
+EXPORT_SYMBOL(saa7134_ts_unregister);
+
+/* ----------------------------------------------------------- */
+
+static struct pci_driver saa7134_pci_driver = {
+ .name = "saa7134",
+ .id_table = saa7134_pci_tbl,
+ .probe = saa7134_initdev,
+ .remove = __devexit_p(saa7134_finidev),
+#ifdef CONFIG_PM
+ .suspend = saa7134_suspend,
+ .resume = saa7134_resume
+#endif
+};
+
+static int __init saa7134_init(void)
+{
+ INIT_LIST_HEAD(&saa7134_devlist);
+ printk(KERN_INFO "saa7130/34: v4l2 driver version %s loaded\n",
+ SAA7134_VERSION);
+ return pci_register_driver(&saa7134_pci_driver);
+}
+
+static void __exit saa7134_fini(void)
+{
+ pci_unregister_driver(&saa7134_pci_driver);
+}
+
+module_init(saa7134_init);
+module_exit(saa7134_fini);
+
+/* ----------------------------------------------------------- */
+
+EXPORT_SYMBOL(saa7134_set_gpio);
+EXPORT_SYMBOL(saa7134_boards);
+
+/* ----------------- for the DMA sound modules --------------- */
+
+EXPORT_SYMBOL(saa7134_dmasound_init);
+EXPORT_SYMBOL(saa7134_dmasound_exit);
+EXPORT_SYMBOL(saa7134_pgtable_free);
+EXPORT_SYMBOL(saa7134_pgtable_build);
+EXPORT_SYMBOL(saa7134_pgtable_alloc);
+EXPORT_SYMBOL(saa7134_set_dmabits);
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c
new file mode 100644
index 00000000..aaa5c97a
--- /dev/null
+++ b/drivers/media/video/saa7134/saa7134-dvb.c
@@ -0,0 +1,1897 @@
+/*
+ *
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * Extended 3 / 2005 by Hartmut Hackmann to support various
+ * cards with the tda10046 DVB-T channel decoder
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/suspend.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+#include <media/v4l2-common.h>
+#include "dvb-pll.h"
+#include <dvb_frontend.h>
+
+#include "mt352.h"
+#include "mt352_priv.h" /* FIXME */
+#include "tda1004x.h"
+#include "nxt200x.h"
+#include "tuner-xc2028.h"
+#include "xc5000.h"
+
+#include "tda10086.h"
+#include "tda826x.h"
+#include "tda827x.h"
+#include "isl6421.h"
+#include "isl6405.h"
+#include "lnbp21.h"
+#include "tuner-simple.h"
+#include "tda10048.h"
+#include "tda18271.h"
+#include "lgdt3305.h"
+#include "tda8290.h"
+#include "mb86a20s.h"
+#include "lgs8gxx.h"
+
+#include "zl10353.h"
+#include "qt1010.h"
+
+#include "zl10036.h"
+#include "zl10039.h"
+#include "mt312.h"
+#include "s5h1411.h"
+
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+
+static unsigned int antenna_pwr;
+
+module_param(antenna_pwr, int, 0444);
+MODULE_PARM_DESC(antenna_pwr,"enable antenna power (Pinnacle 300i)");
+
+static int use_frontend;
+module_param(use_frontend, int, 0644);
+MODULE_PARM_DESC(use_frontend,"for cards with multiple frontends (0: terrestrial, 1: satellite)");
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off module debugging (default:off).");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define dprintk(fmt, arg...) do { if (debug) \
+ printk(KERN_DEBUG "%s/dvb: " fmt, dev->name , ## arg); } while(0)
+
+/* Print a warning */
+#define wprintk(fmt, arg...) \
+ printk(KERN_WARNING "%s/dvb: " fmt, dev->name, ## arg)
+
+/* ------------------------------------------------------------------
+ * mt352 based DVB-T cards
+ */
+
+static int pinnacle_antenna_pwr(struct saa7134_dev *dev, int on)
+{
+ u32 ok;
+
+ if (!on) {
+ saa_setl(SAA7134_GPIO_GPMODE0 >> 2, (1 << 26));
+ saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 26));
+ return 0;
+ }
+
+ saa_setl(SAA7134_GPIO_GPMODE0 >> 2, (1 << 26));
+ saa_setl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 26));
+ udelay(10);
+
+ saa_setl(SAA7134_GPIO_GPMODE0 >> 2, (1 << 28));
+ saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 28));
+ udelay(10);
+ saa_setl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 28));
+ udelay(10);
+ ok = saa_readl(SAA7134_GPIO_GPSTATUS0) & (1 << 27);
+ dprintk("%s %s\n", __func__, ok ? "on" : "off");
+
+ if (!ok)
+ saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 26));
+ return ok;
+}
+
+static int mt352_pinnacle_init(struct dvb_frontend* fe)
+{
+ static u8 clock_config [] = { CLOCK_CTL, 0x3d, 0x28 };
+ static u8 reset [] = { RESET, 0x80 };
+ static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 };
+ static u8 agc_cfg [] = { AGC_TARGET, 0x28, 0xa0 };
+ static u8 capt_range_cfg[] = { CAPT_RANGE, 0x31 };
+ static u8 fsm_ctl_cfg[] = { 0x7b, 0x04 };
+ static u8 gpp_ctl_cfg [] = { GPP_CTL, 0x0f };
+ static u8 scan_ctl_cfg [] = { SCAN_CTL, 0x0d };
+ static u8 irq_cfg [] = { INTERRUPT_EN_0, 0x00, 0x00, 0x00, 0x00 };
+ struct saa7134_dev *dev= fe->dvb->priv;
+
+ dprintk("%s called\n", __func__);
+
+ mt352_write(fe, clock_config, sizeof(clock_config));
+ udelay(200);
+ mt352_write(fe, reset, sizeof(reset));
+ mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
+ mt352_write(fe, agc_cfg, sizeof(agc_cfg));
+ mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+ mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg));
+
+ mt352_write(fe, fsm_ctl_cfg, sizeof(fsm_ctl_cfg));
+ mt352_write(fe, scan_ctl_cfg, sizeof(scan_ctl_cfg));
+ mt352_write(fe, irq_cfg, sizeof(irq_cfg));
+
+ return 0;
+}
+
+static int mt352_aver777_init(struct dvb_frontend* fe)
+{
+ static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x2d };
+ static u8 reset [] = { RESET, 0x80 };
+ static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 };
+ static u8 agc_cfg [] = { AGC_TARGET, 0x28, 0xa0 };
+ static u8 capt_range_cfg[] = { CAPT_RANGE, 0x33 };
+
+ mt352_write(fe, clock_config, sizeof(clock_config));
+ udelay(200);
+ mt352_write(fe, reset, sizeof(reset));
+ mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
+ mt352_write(fe, agc_cfg, sizeof(agc_cfg));
+ mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+
+ return 0;
+}
+
+static int mt352_avermedia_xc3028_init(struct dvb_frontend *fe)
+{
+ static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x2d };
+ static u8 reset [] = { RESET, 0x80 };
+ static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 };
+ static u8 agc_cfg [] = { AGC_TARGET, 0xe };
+ static u8 capt_range_cfg[] = { CAPT_RANGE, 0x33 };
+
+ mt352_write(fe, clock_config, sizeof(clock_config));
+ udelay(200);
+ mt352_write(fe, reset, sizeof(reset));
+ mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
+ mt352_write(fe, agc_cfg, sizeof(agc_cfg));
+ mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+ return 0;
+}
+
+static int mt352_pinnacle_tuner_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u8 off[] = { 0x00, 0xf1};
+ u8 on[] = { 0x00, 0x71};
+ struct i2c_msg msg = {.addr=0x43, .flags=0, .buf=off, .len = sizeof(off)};
+
+ struct saa7134_dev *dev = fe->dvb->priv;
+ struct v4l2_frequency f;
+
+ /* set frequency (mt2050) */
+ f.tuner = 0;
+ f.type = V4L2_TUNER_DIGITAL_TV;
+ f.frequency = c->frequency / 1000 * 16 / 1000;
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
+ saa_call_all(dev, tuner, s_frequency, &f);
+ msg.buf = on;
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
+
+ pinnacle_antenna_pwr(dev, antenna_pwr);
+
+ /* mt352 setup */
+ return mt352_pinnacle_init(fe);
+}
+
+static struct mt352_config pinnacle_300i = {
+ .demod_address = 0x3c >> 1,
+ .adc_clock = 20333,
+ .if2 = 36150,
+ .no_tuner = 1,
+ .demod_init = mt352_pinnacle_init,
+};
+
+static struct mt352_config avermedia_777 = {
+ .demod_address = 0xf,
+ .demod_init = mt352_aver777_init,
+};
+
+static struct mt352_config avermedia_xc3028_mt352_dev = {
+ .demod_address = (0x1e >> 1),
+ .no_tuner = 1,
+ .demod_init = mt352_avermedia_xc3028_init,
+};
+
+static struct tda18271_std_map mb86a20s_tda18271_std_map = {
+ .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4,
+ .if_lvl = 7, .rfagc_top = 0x37, },
+};
+
+static struct tda18271_config kworld_tda18271_config = {
+ .std_map = &mb86a20s_tda18271_std_map,
+ .gate = TDA18271_GATE_DIGITAL,
+ .config = 3, /* Use tuner callback for AGC */
+
+};
+
+static const struct mb86a20s_config kworld_mb86a20s_config = {
+ .demod_address = 0x10,
+};
+
+static int kworld_sbtvd_gate_ctrl(struct dvb_frontend* fe, int enable)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+
+ unsigned char initmsg[] = {0x45, 0x97};
+ unsigned char msg_enable[] = {0x45, 0xc1};
+ unsigned char msg_disable[] = {0x45, 0x81};
+ struct i2c_msg msg = {.addr = 0x4b, .flags = 0, .buf = initmsg, .len = 2};
+
+ if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) {
+ wprintk("could not access the I2C gate\n");
+ return -EIO;
+ }
+ if (enable)
+ msg.buf = msg_enable;
+ else
+ msg.buf = msg_disable;
+ if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) {
+ wprintk("could not access the I2C gate\n");
+ return -EIO;
+ }
+ msleep(20);
+ return 0;
+}
+
+/* ==================================================================
+ * tda1004x based DVB-T cards, helper functions
+ */
+
+static int philips_tda1004x_request_firmware(struct dvb_frontend *fe,
+ const struct firmware **fw, char *name)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ return request_firmware(fw, name, &dev->pci->dev);
+}
+
+/* ------------------------------------------------------------------
+ * these tuners are tu1216, td1316(a)
+ */
+
+static int philips_tda6651_pll_set(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct saa7134_dev *dev = fe->dvb->priv;
+ struct tda1004x_state *state = fe->demodulator_priv;
+ u8 addr = state->config->tuner_address;
+ u8 tuner_buf[4];
+ struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tuner_buf,.len =
+ sizeof(tuner_buf) };
+ int tuner_frequency = 0;
+ u8 band, cp, filter;
+
+ /* determine charge pump */
+ tuner_frequency = c->frequency + 36166000;
+ if (tuner_frequency < 87000000)
+ return -EINVAL;
+ else if (tuner_frequency < 130000000)
+ cp = 3;
+ else if (tuner_frequency < 160000000)
+ cp = 5;
+ else if (tuner_frequency < 200000000)
+ cp = 6;
+ else if (tuner_frequency < 290000000)
+ cp = 3;
+ else if (tuner_frequency < 420000000)
+ cp = 5;
+ else if (tuner_frequency < 480000000)
+ cp = 6;
+ else if (tuner_frequency < 620000000)
+ cp = 3;
+ else if (tuner_frequency < 830000000)
+ cp = 5;
+ else if (tuner_frequency < 895000000)
+ cp = 7;
+ else
+ return -EINVAL;
+
+ /* determine band */
+ if (c->frequency < 49000000)
+ return -EINVAL;
+ else if (c->frequency < 161000000)
+ band = 1;
+ else if (c->frequency < 444000000)
+ band = 2;
+ else if (c->frequency < 861000000)
+ band = 4;
+ else
+ return -EINVAL;
+
+ /* setup PLL filter */
+ switch (c->bandwidth_hz) {
+ case 6000000:
+ filter = 0;
+ break;
+
+ case 7000000:
+ filter = 0;
+ break;
+
+ case 8000000:
+ filter = 1;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* calculate divisor
+ * ((36166000+((1000000/6)/2)) + Finput)/(1000000/6)
+ */
+ tuner_frequency = (((c->frequency / 1000) * 6) + 217496) / 1000;
+
+ /* setup tuner buffer */
+ tuner_buf[0] = (tuner_frequency >> 8) & 0x7f;
+ tuner_buf[1] = tuner_frequency & 0xff;
+ tuner_buf[2] = 0xca;
+ tuner_buf[3] = (cp << 5) | (filter << 3) | band;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) {
+ wprintk("could not write to tuner at addr: 0x%02x\n",
+ addr << 1);
+ return -EIO;
+ }
+ msleep(1);
+ return 0;
+}
+
+static int philips_tu1216_init(struct dvb_frontend *fe)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ struct tda1004x_state *state = fe->demodulator_priv;
+ u8 addr = state->config->tuner_address;
+ static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab };
+ struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) };
+
+ /* setup PLL configuration */
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1)
+ return -EIO;
+ msleep(1);
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static struct tda1004x_config philips_tu1216_60_config = {
+ .demod_address = 0x8,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_4M,
+ .agc_config = TDA10046_AGC_DEFAULT,
+ .if_freq = TDA10046_FREQ_3617,
+ .tuner_address = 0x60,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config philips_tu1216_61_config = {
+
+ .demod_address = 0x8,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_4M,
+ .agc_config = TDA10046_AGC_DEFAULT,
+ .if_freq = TDA10046_FREQ_3617,
+ .tuner_address = 0x61,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+/* ------------------------------------------------------------------ */
+
+static int philips_td1316_tuner_init(struct dvb_frontend *fe)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ struct tda1004x_state *state = fe->demodulator_priv;
+ u8 addr = state->config->tuner_address;
+ static u8 msg[] = { 0x0b, 0xf5, 0x86, 0xab };
+ struct i2c_msg init_msg = {.addr = addr,.flags = 0,.buf = msg,.len = sizeof(msg) };
+
+ /* setup PLL configuration */
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1)
+ return -EIO;
+ return 0;
+}
+
+static int philips_td1316_tuner_set_params(struct dvb_frontend *fe)
+{
+ return philips_tda6651_pll_set(fe);
+}
+
+static int philips_td1316_tuner_sleep(struct dvb_frontend *fe)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ struct tda1004x_state *state = fe->demodulator_priv;
+ u8 addr = state->config->tuner_address;
+ static u8 msg[] = { 0x0b, 0xdc, 0x86, 0xa4 };
+ struct i2c_msg analog_msg = {.addr = addr,.flags = 0,.buf = msg,.len = sizeof(msg) };
+
+ /* switch the tuner to analog mode */
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(&dev->i2c_adap, &analog_msg, 1) != 1)
+ return -EIO;
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int philips_europa_tuner_init(struct dvb_frontend *fe)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ static u8 msg[] = { 0x00, 0x40};
+ struct i2c_msg init_msg = {.addr = 0x43,.flags = 0,.buf = msg,.len = sizeof(msg) };
+
+
+ if (philips_td1316_tuner_init(fe))
+ return -EIO;
+ msleep(1);
+ if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1)
+ return -EIO;
+
+ return 0;
+}
+
+static int philips_europa_tuner_sleep(struct dvb_frontend *fe)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+
+ static u8 msg[] = { 0x00, 0x14 };
+ struct i2c_msg analog_msg = {.addr = 0x43,.flags = 0,.buf = msg,.len = sizeof(msg) };
+
+ if (philips_td1316_tuner_sleep(fe))
+ return -EIO;
+
+ /* switch the board to analog mode */
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ i2c_transfer(&dev->i2c_adap, &analog_msg, 1);
+ return 0;
+}
+
+static int philips_europa_demod_sleep(struct dvb_frontend *fe)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+
+ if (dev->original_demod_sleep)
+ dev->original_demod_sleep(fe);
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ return 0;
+}
+
+static struct tda1004x_config philips_europa_config = {
+
+ .demod_address = 0x8,
+ .invert = 0,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_4M,
+ .agc_config = TDA10046_AGC_IFO_AUTO_POS,
+ .if_freq = TDA10046_FREQ_052,
+ .tuner_address = 0x61,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config medion_cardbus = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_IFO_AUTO_NEG,
+ .if_freq = TDA10046_FREQ_3613,
+ .tuner_address = 0x61,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config technotrend_budget_t3000_config = {
+ .demod_address = 0x8,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_4M,
+ .agc_config = TDA10046_AGC_DEFAULT,
+ .if_freq = TDA10046_FREQ_3617,
+ .tuner_address = 0x63,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+/* ------------------------------------------------------------------
+ * tda 1004x based cards with philips silicon tuner
+ */
+
+static int tda8290_i2c_gate_ctrl( struct dvb_frontend* fe, int enable)
+{
+ struct tda1004x_state *state = fe->demodulator_priv;
+
+ u8 addr = state->config->i2c_gate;
+ static u8 tda8290_close[] = { 0x21, 0xc0};
+ static u8 tda8290_open[] = { 0x21, 0x80};
+ struct i2c_msg tda8290_msg = {.addr = addr,.flags = 0, .len = 2};
+ if (enable) {
+ tda8290_msg.buf = tda8290_close;
+ } else {
+ tda8290_msg.buf = tda8290_open;
+ }
+ if (i2c_transfer(state->i2c, &tda8290_msg, 1) != 1) {
+ struct saa7134_dev *dev = fe->dvb->priv;
+ wprintk("could not access tda8290 I2C gate\n");
+ return -EIO;
+ }
+ msleep(20);
+ return 0;
+}
+
+static int philips_tda827x_tuner_init(struct dvb_frontend *fe)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ struct tda1004x_state *state = fe->demodulator_priv;
+
+ switch (state->config->antenna_switch) {
+ case 0: break;
+ case 1: dprintk("setting GPIO21 to 0 (TV antenna?)\n");
+ saa7134_set_gpio(dev, 21, 0);
+ break;
+ case 2: dprintk("setting GPIO21 to 1 (Radio antenna?)\n");
+ saa7134_set_gpio(dev, 21, 1);
+ break;
+ }
+ return 0;
+}
+
+static int philips_tda827x_tuner_sleep(struct dvb_frontend *fe)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ struct tda1004x_state *state = fe->demodulator_priv;
+
+ switch (state->config->antenna_switch) {
+ case 0: break;
+ case 1: dprintk("setting GPIO21 to 1 (Radio antenna?)\n");
+ saa7134_set_gpio(dev, 21, 1);
+ break;
+ case 2: dprintk("setting GPIO21 to 0 (TV antenna?)\n");
+ saa7134_set_gpio(dev, 21, 0);
+ break;
+ }
+ return 0;
+}
+
+static int configure_tda827x_fe(struct saa7134_dev *dev,
+ struct tda1004x_config *cdec_conf,
+ struct tda827x_config *tuner_conf)
+{
+ struct videobuf_dvb_frontend *fe0;
+
+ /* Get the first frontend */
+ fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
+
+ fe0->dvb.frontend = dvb_attach(tda10046_attach, cdec_conf, &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ if (cdec_conf->i2c_gate)
+ fe0->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl;
+ if (dvb_attach(tda827x_attach, fe0->dvb.frontend,
+ cdec_conf->tuner_address,
+ &dev->i2c_adap, tuner_conf))
+ return 0;
+
+ wprintk("no tda827x tuner found at addr: %02x\n",
+ cdec_conf->tuner_address);
+ }
+ return -EINVAL;
+}
+
+/* ------------------------------------------------------------------ */
+
+static struct tda827x_config tda827x_cfg_0 = {
+ .init = philips_tda827x_tuner_init,
+ .sleep = philips_tda827x_tuner_sleep,
+ .config = 0,
+ .switch_addr = 0
+};
+
+static struct tda827x_config tda827x_cfg_1 = {
+ .init = philips_tda827x_tuner_init,
+ .sleep = philips_tda827x_tuner_sleep,
+ .config = 1,
+ .switch_addr = 0x4b
+};
+
+static struct tda827x_config tda827x_cfg_2 = {
+ .init = philips_tda827x_tuner_init,
+ .sleep = philips_tda827x_tuner_sleep,
+ .config = 2,
+ .switch_addr = 0x4b
+};
+
+static struct tda827x_config tda827x_cfg_2_sw42 = {
+ .init = philips_tda827x_tuner_init,
+ .sleep = philips_tda827x_tuner_sleep,
+ .config = 2,
+ .switch_addr = 0x42
+};
+
+/* ------------------------------------------------------------------ */
+
+static struct tda1004x_config tda827x_lifeview_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
+ .if_freq = TDA10046_FREQ_045,
+ .tuner_address = 0x60,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config philips_tiger_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .antenna_switch= 1,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config cinergy_ht_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP01_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config cinergy_ht_pci_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP01_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x60,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config philips_tiger_s_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP01_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .antenna_switch= 1,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config pinnacle_pctv_310i_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config hauppauge_hvr_1110_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config asus_p7131_dual_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .antenna_switch= 2,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config lifeview_trio_config = {
+ .demod_address = 0x09,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP00_I,
+ .if_freq = TDA10046_FREQ_045,
+ .tuner_address = 0x60,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config tevion_dvbt220rf_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
+ .if_freq = TDA10046_FREQ_045,
+ .tuner_address = 0x60,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config md8800_dvbt_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP01_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x60,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config asus_p7131_4871_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP01_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .antenna_switch= 2,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config asus_p7131_hybrid_lna_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .antenna_switch= 2,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config kworld_dvb_t_210_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .antenna_switch= 1,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config avermedia_super_007_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP01_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x60,
+ .antenna_switch= 1,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config twinhan_dtv_dvb_3056_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP01_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x42,
+ .tuner_address = 0x61,
+ .antenna_switch = 1,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config asus_tiger_3in1_config = {
+ .demod_address = 0x0b,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .antenna_switch = 1,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+/* ------------------------------------------------------------------
+ * special case: this card uses saa713x GPIO22 for the mode switch
+ */
+
+static int ads_duo_tuner_init(struct dvb_frontend *fe)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ philips_tda827x_tuner_init(fe);
+ /* route TDA8275a AGC input to the channel decoder */
+ saa7134_set_gpio(dev, 22, 1);
+ return 0;
+}
+
+static int ads_duo_tuner_sleep(struct dvb_frontend *fe)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ /* route TDA8275a AGC input to the analog IF chip*/
+ saa7134_set_gpio(dev, 22, 0);
+ philips_tda827x_tuner_sleep(fe);
+ return 0;
+}
+
+static struct tda827x_config ads_duo_cfg = {
+ .init = ads_duo_tuner_init,
+ .sleep = ads_duo_tuner_sleep,
+ .config = 0
+};
+
+static struct tda1004x_config ads_tech_duo_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP00_I,
+ .if_freq = TDA10046_FREQ_045,
+ .tuner_address = 0x61,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct zl10353_config behold_h6_config = {
+ .demod_address = 0x1e>>1,
+ .no_tuner = 1,
+ .parallel_ts = 1,
+ .disable_i2c_gate_ctrl = 1,
+};
+
+static struct xc5000_config behold_x7_tunerconfig = {
+ .i2c_address = 0xc2>>1,
+ .if_khz = 4560,
+ .radio_input = XC5000_RADIO_FM1,
+};
+
+static struct zl10353_config behold_x7_config = {
+ .demod_address = 0x1e>>1,
+ .if2 = 45600,
+ .no_tuner = 1,
+ .parallel_ts = 1,
+ .disable_i2c_gate_ctrl = 1,
+};
+
+static struct zl10353_config videomate_t750_zl10353_config = {
+ .demod_address = 0x0f,
+ .no_tuner = 1,
+ .parallel_ts = 1,
+ .disable_i2c_gate_ctrl = 1,
+};
+
+static struct qt1010_config videomate_t750_qt1010_config = {
+ .i2c_address = 0x62
+};
+
+
+/* ==================================================================
+ * tda10086 based DVB-S cards, helper functions
+ */
+
+static struct tda10086_config flydvbs = {
+ .demod_address = 0x0e,
+ .invert = 0,
+ .diseqc_tone = 0,
+ .xtal_freq = TDA10086_XTAL_16M,
+};
+
+static struct tda10086_config sd1878_4m = {
+ .demod_address = 0x0e,
+ .invert = 0,
+ .diseqc_tone = 0,
+ .xtal_freq = TDA10086_XTAL_4M,
+};
+
+/* ------------------------------------------------------------------
+ * special case: lnb supply is connected to the gated i2c
+ */
+
+static int md8800_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+ int res = -EIO;
+ struct saa7134_dev *dev = fe->dvb->priv;
+ if (fe->ops.i2c_gate_ctrl) {
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (dev->original_set_voltage)
+ res = dev->original_set_voltage(fe, voltage);
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+ return res;
+};
+
+static int md8800_set_high_voltage(struct dvb_frontend *fe, long arg)
+{
+ int res = -EIO;
+ struct saa7134_dev *dev = fe->dvb->priv;
+ if (fe->ops.i2c_gate_ctrl) {
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (dev->original_set_high_voltage)
+ res = dev->original_set_high_voltage(fe, arg);
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+ return res;
+};
+
+static int md8800_set_voltage2(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ u8 wbuf[2] = { 0x1f, 00 };
+ u8 rbuf;
+ struct i2c_msg msg[] = { { .addr = 0x08, .flags = 0, .buf = wbuf, .len = 1 },
+ { .addr = 0x08, .flags = I2C_M_RD, .buf = &rbuf, .len = 1 } };
+
+ if (i2c_transfer(&dev->i2c_adap, msg, 2) != 2)
+ return -EIO;
+ /* NOTE: this assumes that gpo1 is used, it might be bit 5 (gpo2) */
+ if (voltage == SEC_VOLTAGE_18)
+ wbuf[1] = rbuf | 0x10;
+ else
+ wbuf[1] = rbuf & 0xef;
+ msg[0].len = 2;
+ i2c_transfer(&dev->i2c_adap, msg, 1);
+ return 0;
+}
+
+static int md8800_set_high_voltage2(struct dvb_frontend *fe, long arg)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ wprintk("%s: sorry can't set high LNB supply voltage from here\n", __func__);
+ return -EIO;
+}
+
+/* ==================================================================
+ * nxt200x based ATSC cards, helper functions
+ */
+
+static struct nxt200x_config avertvhda180 = {
+ .demod_address = 0x0a,
+};
+
+static struct nxt200x_config kworldatsc110 = {
+ .demod_address = 0x0a,
+};
+
+/* ------------------------------------------------------------------ */
+
+static struct mt312_config avertv_a700_mt312 = {
+ .demod_address = 0x0e,
+ .voltage_inverted = 1,
+};
+
+static struct zl10036_config avertv_a700_tuner = {
+ .tuner_address = 0x60,
+};
+
+static struct mt312_config zl10313_compro_s350_config = {
+ .demod_address = 0x0e,
+};
+
+static struct lgdt3305_config hcw_lgdt3305_config = {
+ .i2c_addr = 0x0e,
+ .mpeg_mode = LGDT3305_MPEG_SERIAL,
+ .tpclk_edge = LGDT3305_TPCLK_RISING_EDGE,
+ .tpvalid_polarity = LGDT3305_TP_VALID_HIGH,
+ .deny_i2c_rptr = 1,
+ .spectral_inversion = 1,
+ .qam_if_khz = 4000,
+ .vsb_if_khz = 3250,
+};
+
+static struct tda10048_config hcw_tda10048_config = {
+ .demod_address = 0x10 >> 1,
+ .output_mode = TDA10048_SERIAL_OUTPUT,
+ .fwbulkwritelen = TDA10048_BULKWRITE_200,
+ .inversion = TDA10048_INVERSION_ON,
+ .dtv6_if_freq_khz = TDA10048_IF_3300,
+ .dtv7_if_freq_khz = TDA10048_IF_3500,
+ .dtv8_if_freq_khz = TDA10048_IF_4000,
+ .clk_freq_khz = TDA10048_CLK_16000,
+ .disable_gate_access = 1,
+};
+
+static struct tda18271_std_map hauppauge_tda18271_std_map = {
+ .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 4,
+ .if_lvl = 1, .rfagc_top = 0x58, },
+ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 5,
+ .if_lvl = 1, .rfagc_top = 0x58, },
+};
+
+static struct tda18271_config hcw_tda18271_config = {
+ .std_map = &hauppauge_tda18271_std_map,
+ .gate = TDA18271_GATE_ANALOG,
+ .config = 3,
+ .output_opt = TDA18271_OUTPUT_LT_OFF,
+};
+
+static struct tda829x_config tda829x_no_probe = {
+ .probe_tuner = TDA829X_DONT_PROBE,
+};
+
+static struct tda10048_config zolid_tda10048_config = {
+ .demod_address = 0x10 >> 1,
+ .output_mode = TDA10048_PARALLEL_OUTPUT,
+ .fwbulkwritelen = TDA10048_BULKWRITE_200,
+ .inversion = TDA10048_INVERSION_ON,
+ .dtv6_if_freq_khz = TDA10048_IF_3300,
+ .dtv7_if_freq_khz = TDA10048_IF_3500,
+ .dtv8_if_freq_khz = TDA10048_IF_4000,
+ .clk_freq_khz = TDA10048_CLK_16000,
+ .disable_gate_access = 1,
+};
+
+static struct tda18271_config zolid_tda18271_config = {
+ .gate = TDA18271_GATE_ANALOG,
+};
+
+static struct tda10048_config dtv1000s_tda10048_config = {
+ .demod_address = 0x10 >> 1,
+ .output_mode = TDA10048_PARALLEL_OUTPUT,
+ .fwbulkwritelen = TDA10048_BULKWRITE_200,
+ .inversion = TDA10048_INVERSION_ON,
+ .dtv6_if_freq_khz = TDA10048_IF_3300,
+ .dtv7_if_freq_khz = TDA10048_IF_3800,
+ .dtv8_if_freq_khz = TDA10048_IF_4300,
+ .clk_freq_khz = TDA10048_CLK_16000,
+ .disable_gate_access = 1,
+};
+
+static struct tda18271_std_map dtv1000s_tda18271_std_map = {
+ .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+ .dvbt_7 = { .if_freq = 3800, .agc_mode = 3, .std = 5,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+ .dvbt_8 = { .if_freq = 4300, .agc_mode = 3, .std = 6,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+};
+
+static struct tda18271_config dtv1000s_tda18271_config = {
+ .std_map = &dtv1000s_tda18271_std_map,
+ .gate = TDA18271_GATE_ANALOG,
+};
+
+static struct lgs8gxx_config prohdtv_pro2_lgs8g75_config = {
+ .prod = LGS8GXX_PROD_LGS8G75,
+ .demod_address = 0x1d,
+ .serial_ts = 0,
+ .ts_clk_pol = 1,
+ .ts_clk_gated = 0,
+ .if_clk_freq = 30400, /* 30.4 MHz */
+ .if_freq = 4000, /* 4.00 MHz */
+ .if_neg_center = 0,
+ .ext_adc = 0,
+ .adc_signed = 1,
+ .adc_vpp = 3, /* 2.0 Vpp */
+ .if_neg_edge = 1,
+};
+
+static struct tda18271_config prohdtv_pro2_tda18271_config = {
+ .gate = TDA18271_GATE_ANALOG,
+ .output_opt = TDA18271_OUTPUT_LT_OFF,
+};
+
+static struct tda18271_std_map kworld_tda18271_std_map = {
+ .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 3,
+ .if_lvl = 6, .rfagc_top = 0x37 },
+ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0,
+ .if_lvl = 6, .rfagc_top = 0x37 },
+};
+
+static struct tda18271_config kworld_pc150u_tda18271_config = {
+ .std_map = &kworld_tda18271_std_map,
+ .gate = TDA18271_GATE_ANALOG,
+ .output_opt = TDA18271_OUTPUT_LT_OFF,
+ .config = 3, /* Use tuner callback for AGC */
+ .rf_cal_on_startup = 1
+};
+
+static struct s5h1411_config kworld_s5h1411_config = {
+ .output_mode = S5H1411_PARALLEL_OUTPUT,
+ .gpio = S5H1411_GPIO_OFF,
+ .qam_if = S5H1411_IF_4000,
+ .vsb_if = S5H1411_IF_3250,
+ .inversion = S5H1411_INVERSION_ON,
+ .status_mode = S5H1411_DEMODLOCKING,
+ .mpeg_timing =
+ S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+
+/* ==================================================================
+ * Core code
+ */
+
+static int dvb_init(struct saa7134_dev *dev)
+{
+ int ret;
+ int attach_xc3028 = 0;
+ struct videobuf_dvb_frontend *fe0;
+
+ /* FIXME: add support for multi-frontend */
+ mutex_init(&dev->frontends.lock);
+ INIT_LIST_HEAD(&dev->frontends.felist);
+
+ printk(KERN_INFO "%s() allocating 1 frontend\n", __func__);
+ fe0 = videobuf_dvb_alloc_frontend(&dev->frontends, 1);
+ if (!fe0) {
+ printk(KERN_ERR "%s() failed to alloc\n", __func__);
+ return -ENOMEM;
+ }
+
+ /* init struct videobuf_dvb */
+ dev->ts.nr_bufs = 32;
+ dev->ts.nr_packets = 32*4;
+ fe0->dvb.name = dev->name;
+ videobuf_queue_sg_init(&fe0->dvb.dvbq, &saa7134_ts_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_ALTERNATE,
+ sizeof(struct saa7134_buf),
+ dev, NULL);
+
+ switch (dev->board) {
+ case SAA7134_BOARD_PINNACLE_300I_DVBT_PAL:
+ dprintk("pinnacle 300i dvb setup\n");
+ fe0->dvb.frontend = dvb_attach(mt352_attach, &pinnacle_300i,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ fe0->dvb.frontend->ops.tuner_ops.set_params = mt352_pinnacle_tuner_set_params;
+ }
+ break;
+ case SAA7134_BOARD_AVERMEDIA_777:
+ case SAA7134_BOARD_AVERMEDIA_A16AR:
+ dprintk("avertv 777 dvb setup\n");
+ fe0->dvb.frontend = dvb_attach(mt352_attach, &avermedia_777,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0x61,
+ TUNER_PHILIPS_TD1316);
+ }
+ break;
+ case SAA7134_BOARD_AVERMEDIA_A16D:
+ dprintk("AverMedia A16D dvb setup\n");
+ fe0->dvb.frontend = dvb_attach(mt352_attach,
+ &avermedia_xc3028_mt352_dev,
+ &dev->i2c_adap);
+ attach_xc3028 = 1;
+ break;
+ case SAA7134_BOARD_MD7134:
+ fe0->dvb.frontend = dvb_attach(tda10046_attach,
+ &medion_cardbus,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, medion_cardbus.tuner_address,
+ TUNER_PHILIPS_FMD1216ME_MK3);
+ }
+ break;
+ case SAA7134_BOARD_PHILIPS_TOUGH:
+ fe0->dvb.frontend = dvb_attach(tda10046_attach,
+ &philips_tu1216_60_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ fe0->dvb.frontend->ops.tuner_ops.init = philips_tu1216_init;
+ fe0->dvb.frontend->ops.tuner_ops.set_params = philips_tda6651_pll_set;
+ }
+ break;
+ case SAA7134_BOARD_FLYDVBTDUO:
+ case SAA7134_BOARD_FLYDVBT_DUO_CARDBUS:
+ if (configure_tda827x_fe(dev, &tda827x_lifeview_config,
+ &tda827x_cfg_0) < 0)
+ goto dettach_frontend;
+ break;
+ case SAA7134_BOARD_PHILIPS_EUROPA:
+ case SAA7134_BOARD_VIDEOMATE_DVBT_300:
+ case SAA7134_BOARD_ASUS_EUROPA_HYBRID:
+ fe0->dvb.frontend = dvb_attach(tda10046_attach,
+ &philips_europa_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ dev->original_demod_sleep = fe0->dvb.frontend->ops.sleep;
+ fe0->dvb.frontend->ops.sleep = philips_europa_demod_sleep;
+ fe0->dvb.frontend->ops.tuner_ops.init = philips_europa_tuner_init;
+ fe0->dvb.frontend->ops.tuner_ops.sleep = philips_europa_tuner_sleep;
+ fe0->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params;
+ }
+ break;
+ case SAA7134_BOARD_TECHNOTREND_BUDGET_T3000:
+ fe0->dvb.frontend = dvb_attach(tda10046_attach,
+ &technotrend_budget_t3000_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ dev->original_demod_sleep = fe0->dvb.frontend->ops.sleep;
+ fe0->dvb.frontend->ops.sleep = philips_europa_demod_sleep;
+ fe0->dvb.frontend->ops.tuner_ops.init = philips_europa_tuner_init;
+ fe0->dvb.frontend->ops.tuner_ops.sleep = philips_europa_tuner_sleep;
+ fe0->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params;
+ }
+ break;
+ case SAA7134_BOARD_VIDEOMATE_DVBT_200:
+ fe0->dvb.frontend = dvb_attach(tda10046_attach,
+ &philips_tu1216_61_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ fe0->dvb.frontend->ops.tuner_ops.init = philips_tu1216_init;
+ fe0->dvb.frontend->ops.tuner_ops.set_params = philips_tda6651_pll_set;
+ }
+ break;
+ case SAA7134_BOARD_KWORLD_DVBT_210:
+ if (configure_tda827x_fe(dev, &kworld_dvb_t_210_config,
+ &tda827x_cfg_2) < 0)
+ goto dettach_frontend;
+ break;
+ case SAA7134_BOARD_HAUPPAUGE_HVR1120:
+ fe0->dvb.frontend = dvb_attach(tda10048_attach,
+ &hcw_tda10048_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(tda829x_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0x4b,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_adap,
+ &hcw_tda18271_config);
+ }
+ break;
+ case SAA7134_BOARD_PHILIPS_TIGER:
+ if (configure_tda827x_fe(dev, &philips_tiger_config,
+ &tda827x_cfg_0) < 0)
+ goto dettach_frontend;
+ break;
+ case SAA7134_BOARD_PINNACLE_PCTV_310i:
+ if (configure_tda827x_fe(dev, &pinnacle_pctv_310i_config,
+ &tda827x_cfg_1) < 0)
+ goto dettach_frontend;
+ break;
+ case SAA7134_BOARD_HAUPPAUGE_HVR1110:
+ if (configure_tda827x_fe(dev, &hauppauge_hvr_1110_config,
+ &tda827x_cfg_1) < 0)
+ goto dettach_frontend;
+ break;
+ case SAA7134_BOARD_HAUPPAUGE_HVR1150:
+ fe0->dvb.frontend = dvb_attach(lgdt3305_attach,
+ &hcw_lgdt3305_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ dvb_attach(tda829x_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0x4b,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_adap,
+ &hcw_tda18271_config);
+ }
+ break;
+ case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
+ if (configure_tda827x_fe(dev, &asus_p7131_dual_config,
+ &tda827x_cfg_0) < 0)
+ goto dettach_frontend;
+ break;
+ case SAA7134_BOARD_FLYDVBT_LR301:
+ if (configure_tda827x_fe(dev, &tda827x_lifeview_config,
+ &tda827x_cfg_0) < 0)
+ goto dettach_frontend;
+ break;
+ case SAA7134_BOARD_FLYDVB_TRIO:
+ if (!use_frontend) { /* terrestrial */
+ if (configure_tda827x_fe(dev, &lifeview_trio_config,
+ &tda827x_cfg_0) < 0)
+ goto dettach_frontend;
+ } else { /* satellite */
+ fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x63,
+ &dev->i2c_adap, 0) == NULL) {
+ wprintk("%s: Lifeview Trio, No tda826x found!\n", __func__);
+ goto dettach_frontend;
+ }
+ if (dvb_attach(isl6421_attach, fe0->dvb.frontend, &dev->i2c_adap,
+ 0x08, 0, 0) == NULL) {
+ wprintk("%s: Lifeview Trio, No ISL6421 found!\n", __func__);
+ goto dettach_frontend;
+ }
+ }
+ }
+ break;
+ case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331:
+ case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS:
+ fe0->dvb.frontend = dvb_attach(tda10046_attach,
+ &ads_tech_duo_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ if (dvb_attach(tda827x_attach,fe0->dvb.frontend,
+ ads_tech_duo_config.tuner_address, &dev->i2c_adap,
+ &ads_duo_cfg) == NULL) {
+ wprintk("no tda827x tuner found at addr: %02x\n",
+ ads_tech_duo_config.tuner_address);
+ goto dettach_frontend;
+ }
+ } else
+ wprintk("failed to attach tda10046\n");
+ break;
+ case SAA7134_BOARD_TEVION_DVBT_220RF:
+ if (configure_tda827x_fe(dev, &tevion_dvbt220rf_config,
+ &tda827x_cfg_0) < 0)
+ goto dettach_frontend;
+ break;
+ case SAA7134_BOARD_MEDION_MD8800_QUADRO:
+ if (!use_frontend) { /* terrestrial */
+ if (configure_tda827x_fe(dev, &md8800_dvbt_config,
+ &tda827x_cfg_0) < 0)
+ goto dettach_frontend;
+ } else { /* satellite */
+ fe0->dvb.frontend = dvb_attach(tda10086_attach,
+ &flydvbs, &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ struct dvb_frontend *fe = fe0->dvb.frontend;
+ u8 dev_id = dev->eedata[2];
+ u8 data = 0xc4;
+ struct i2c_msg msg = {.addr = 0x08, .flags = 0, .len = 1};
+
+ if (dvb_attach(tda826x_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_adap, 0) == NULL) {
+ wprintk("%s: Medion Quadro, no tda826x "
+ "found !\n", __func__);
+ goto dettach_frontend;
+ }
+ if (dev_id != 0x08) {
+ /* we need to open the i2c gate (we know it exists) */
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (dvb_attach(isl6405_attach, fe,
+ &dev->i2c_adap, 0x08, 0, 0) == NULL) {
+ wprintk("%s: Medion Quadro, no ISL6405 "
+ "found !\n", __func__);
+ goto dettach_frontend;
+ }
+ if (dev_id == 0x07) {
+ /* fire up the 2nd section of the LNB supply since
+ we can't do this from the other section */
+ msg.buf = &data;
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
+ }
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ dev->original_set_voltage = fe->ops.set_voltage;
+ fe->ops.set_voltage = md8800_set_voltage;
+ dev->original_set_high_voltage = fe->ops.enable_high_lnb_voltage;
+ fe->ops.enable_high_lnb_voltage = md8800_set_high_voltage;
+ } else {
+ fe->ops.set_voltage = md8800_set_voltage2;
+ fe->ops.enable_high_lnb_voltage = md8800_set_high_voltage2;
+ }
+ }
+ }
+ break;
+ case SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180:
+ fe0->dvb.frontend = dvb_attach(nxt200x_attach, &avertvhda180,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend)
+ dvb_attach(dvb_pll_attach, fe0->dvb.frontend, 0x61,
+ NULL, DVB_PLL_TDHU2);
+ break;
+ case SAA7134_BOARD_ADS_INSTANT_HDTV_PCI:
+ case SAA7134_BOARD_KWORLD_ATSC110:
+ fe0->dvb.frontend = dvb_attach(nxt200x_attach, &kworldatsc110,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend)
+ dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0x61,
+ TUNER_PHILIPS_TUV1236D);
+ break;
+ case SAA7134_BOARD_KWORLD_PC150U:
+ saa7134_set_gpio(dev, 18, 1); /* Switch to digital mode */
+ saa7134_tuner_callback(dev, 0,
+ TDA18271_CALLBACK_CMD_AGC_ENABLE, 1);
+ fe0->dvb.frontend = dvb_attach(s5h1411_attach,
+ &kworld_s5h1411_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(tda829x_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0x4b,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_adap,
+ &kworld_pc150u_tda18271_config);
+ }
+ break;
+ case SAA7134_BOARD_FLYDVBS_LR300:
+ fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x60,
+ &dev->i2c_adap, 0) == NULL) {
+ wprintk("%s: No tda826x found!\n", __func__);
+ goto dettach_frontend;
+ }
+ if (dvb_attach(isl6421_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0x08, 0, 0) == NULL) {
+ wprintk("%s: No ISL6421 found!\n", __func__);
+ goto dettach_frontend;
+ }
+ }
+ break;
+ case SAA7134_BOARD_ASUS_EUROPA2_HYBRID:
+ fe0->dvb.frontend = dvb_attach(tda10046_attach,
+ &medion_cardbus,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ dev->original_demod_sleep = fe0->dvb.frontend->ops.sleep;
+ fe0->dvb.frontend->ops.sleep = philips_europa_demod_sleep;
+
+ dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, medion_cardbus.tuner_address,
+ TUNER_PHILIPS_FMD1216ME_MK3);
+ }
+ break;
+ case SAA7134_BOARD_VIDEOMATE_DVBT_200A:
+ fe0->dvb.frontend = dvb_attach(tda10046_attach,
+ &philips_europa_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ fe0->dvb.frontend->ops.tuner_ops.init = philips_td1316_tuner_init;
+ fe0->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params;
+ }
+ break;
+ case SAA7134_BOARD_CINERGY_HT_PCMCIA:
+ if (configure_tda827x_fe(dev, &cinergy_ht_config,
+ &tda827x_cfg_0) < 0)
+ goto dettach_frontend;
+ break;
+ case SAA7134_BOARD_CINERGY_HT_PCI:
+ if (configure_tda827x_fe(dev, &cinergy_ht_pci_config,
+ &tda827x_cfg_0) < 0)
+ goto dettach_frontend;
+ break;
+ case SAA7134_BOARD_PHILIPS_TIGER_S:
+ if (configure_tda827x_fe(dev, &philips_tiger_s_config,
+ &tda827x_cfg_2) < 0)
+ goto dettach_frontend;
+ break;
+ case SAA7134_BOARD_ASUS_P7131_4871:
+ if (configure_tda827x_fe(dev, &asus_p7131_4871_config,
+ &tda827x_cfg_2) < 0)
+ goto dettach_frontend;
+ break;
+ case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
+ if (configure_tda827x_fe(dev, &asus_p7131_hybrid_lna_config,
+ &tda827x_cfg_2) < 0)
+ goto dettach_frontend;
+ break;
+ case SAA7134_BOARD_AVERMEDIA_SUPER_007:
+ if (configure_tda827x_fe(dev, &avermedia_super_007_config,
+ &tda827x_cfg_0) < 0)
+ goto dettach_frontend;
+ break;
+ case SAA7134_BOARD_TWINHAN_DTV_DVB_3056:
+ if (configure_tda827x_fe(dev, &twinhan_dtv_dvb_3056_config,
+ &tda827x_cfg_2_sw42) < 0)
+ goto dettach_frontend;
+ break;
+ case SAA7134_BOARD_PHILIPS_SNAKE:
+ fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x60,
+ &dev->i2c_adap, 0) == NULL) {
+ wprintk("%s: No tda826x found!\n", __func__);
+ goto dettach_frontend;
+ }
+ if (dvb_attach(lnbp21_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0, 0) == NULL) {
+ wprintk("%s: No lnbp21 found!\n", __func__);
+ goto dettach_frontend;
+ }
+ }
+ break;
+ case SAA7134_BOARD_CREATIX_CTX953:
+ if (configure_tda827x_fe(dev, &md8800_dvbt_config,
+ &tda827x_cfg_0) < 0)
+ goto dettach_frontend;
+ break;
+ case SAA7134_BOARD_MSI_TVANYWHERE_AD11:
+ if (configure_tda827x_fe(dev, &philips_tiger_s_config,
+ &tda827x_cfg_2) < 0)
+ goto dettach_frontend;
+ break;
+ case SAA7134_BOARD_AVERMEDIA_CARDBUS_506:
+ dprintk("AverMedia E506R dvb setup\n");
+ saa7134_set_gpio(dev, 25, 0);
+ msleep(10);
+ saa7134_set_gpio(dev, 25, 1);
+ fe0->dvb.frontend = dvb_attach(mt352_attach,
+ &avermedia_xc3028_mt352_dev,
+ &dev->i2c_adap);
+ attach_xc3028 = 1;
+ break;
+ case SAA7134_BOARD_MD7134_BRIDGE_2:
+ fe0->dvb.frontend = dvb_attach(tda10086_attach,
+ &sd1878_4m, &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ struct dvb_frontend *fe;
+ if (dvb_attach(dvb_pll_attach, fe0->dvb.frontend, 0x60,
+ &dev->i2c_adap, DVB_PLL_PHILIPS_SD1878_TDA8261) == NULL) {
+ wprintk("%s: MD7134 DVB-S, no SD1878 "
+ "found !\n", __func__);
+ goto dettach_frontend;
+ }
+ /* we need to open the i2c gate (we know it exists) */
+ fe = fe0->dvb.frontend;
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (dvb_attach(isl6405_attach, fe,
+ &dev->i2c_adap, 0x08, 0, 0) == NULL) {
+ wprintk("%s: MD7134 DVB-S, no ISL6405 "
+ "found !\n", __func__);
+ goto dettach_frontend;
+ }
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ dev->original_set_voltage = fe->ops.set_voltage;
+ fe->ops.set_voltage = md8800_set_voltage;
+ dev->original_set_high_voltage = fe->ops.enable_high_lnb_voltage;
+ fe->ops.enable_high_lnb_voltage = md8800_set_high_voltage;
+ }
+ break;
+ case SAA7134_BOARD_AVERMEDIA_M103:
+ saa7134_set_gpio(dev, 25, 0);
+ msleep(10);
+ saa7134_set_gpio(dev, 25, 1);
+ fe0->dvb.frontend = dvb_attach(mt352_attach,
+ &avermedia_xc3028_mt352_dev,
+ &dev->i2c_adap);
+ attach_xc3028 = 1;
+ break;
+ case SAA7134_BOARD_ASUSTeK_TIGER_3IN1:
+ if (!use_frontend) { /* terrestrial */
+ if (configure_tda827x_fe(dev, &asus_tiger_3in1_config,
+ &tda827x_cfg_2) < 0)
+ goto dettach_frontend;
+ } else { /* satellite */
+ fe0->dvb.frontend = dvb_attach(tda10086_attach,
+ &flydvbs, &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ if (dvb_attach(tda826x_attach,
+ fe0->dvb.frontend, 0x60,
+ &dev->i2c_adap, 0) == NULL) {
+ wprintk("%s: Asus Tiger 3in1, no "
+ "tda826x found!\n", __func__);
+ goto dettach_frontend;
+ }
+ if (dvb_attach(lnbp21_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0, 0) == NULL) {
+ wprintk("%s: Asus Tiger 3in1, no lnbp21"
+ " found!\n", __func__);
+ goto dettach_frontend;
+ }
+ }
+ }
+ break;
+ case SAA7134_BOARD_ASUSTeK_TIGER:
+ if (configure_tda827x_fe(dev, &philips_tiger_config,
+ &tda827x_cfg_0) < 0)
+ goto dettach_frontend;
+ break;
+ case SAA7134_BOARD_BEHOLD_H6:
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &behold_h6_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0x61,
+ TUNER_PHILIPS_FMD1216MEX_MK3);
+ }
+ break;
+ case SAA7134_BOARD_BEHOLD_X7:
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &behold_x7_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ dvb_attach(xc5000_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, &behold_x7_tunerconfig);
+ }
+ break;
+ case SAA7134_BOARD_BEHOLD_H7:
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &behold_x7_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ dvb_attach(xc5000_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, &behold_x7_tunerconfig);
+ }
+ break;
+ case SAA7134_BOARD_AVERMEDIA_A700_PRO:
+ case SAA7134_BOARD_AVERMEDIA_A700_HYBRID:
+ /* Zarlink ZL10313 */
+ fe0->dvb.frontend = dvb_attach(mt312_attach,
+ &avertv_a700_mt312, &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ if (dvb_attach(zl10036_attach, fe0->dvb.frontend,
+ &avertv_a700_tuner, &dev->i2c_adap) == NULL) {
+ wprintk("%s: No zl10036 found!\n",
+ __func__);
+ }
+ }
+ break;
+ case SAA7134_BOARD_VIDEOMATE_S350:
+ fe0->dvb.frontend = dvb_attach(mt312_attach,
+ &zl10313_compro_s350_config, &dev->i2c_adap);
+ if (fe0->dvb.frontend)
+ if (dvb_attach(zl10039_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_adap) == NULL)
+ wprintk("%s: No zl10039 found!\n",
+ __func__);
+
+ break;
+ case SAA7134_BOARD_VIDEOMATE_T750:
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &videomate_t750_zl10353_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (dvb_attach(qt1010_attach,
+ fe0->dvb.frontend,
+ &dev->i2c_adap,
+ &videomate_t750_qt1010_config) == NULL)
+ wprintk("error attaching QT1010\n");
+ }
+ break;
+ case SAA7134_BOARD_ZOLID_HYBRID_PCI:
+ fe0->dvb.frontend = dvb_attach(tda10048_attach,
+ &zolid_tda10048_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(tda829x_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0x4b,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_adap,
+ &zolid_tda18271_config);
+ }
+ break;
+ case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S:
+ fe0->dvb.frontend = dvb_attach(tda10048_attach,
+ &dtv1000s_tda10048_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(tda829x_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0x4b,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_adap,
+ &dtv1000s_tda18271_config);
+ }
+ break;
+ case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG:
+ /* Switch to digital mode */
+ saa7134_tuner_callback(dev, 0,
+ TDA18271_CALLBACK_CMD_AGC_ENABLE, 1);
+ fe0->dvb.frontend = dvb_attach(mb86a20s_attach,
+ &kworld_mb86a20s_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(tda829x_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0x4b,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_adap,
+ &kworld_tda18271_config);
+ fe0->dvb.frontend->ops.i2c_gate_ctrl = kworld_sbtvd_gate_ctrl;
+ }
+
+ /* mb86a20s need to use the I2C gateway */
+ break;
+ case SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2:
+ fe0->dvb.frontend = dvb_attach(lgs8gxx_attach,
+ &prohdtv_pro2_lgs8g75_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(tda829x_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0x4b,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_adap,
+ &prohdtv_pro2_tda18271_config);
+ }
+ break;
+ default:
+ wprintk("Huh? unknown DVB card?\n");
+ break;
+ }
+
+ if (attach_xc3028) {
+ struct dvb_frontend *fe;
+ struct xc2028_config cfg = {
+ .i2c_adap = &dev->i2c_adap,
+ .i2c_addr = 0x61,
+ };
+
+ if (!fe0->dvb.frontend)
+ goto dettach_frontend;
+
+ fe = dvb_attach(xc2028_attach, fe0->dvb.frontend, &cfg);
+ if (!fe) {
+ printk(KERN_ERR "%s/2: xc3028 attach failed\n",
+ dev->name);
+ goto dettach_frontend;
+ }
+ }
+
+ if (NULL == fe0->dvb.frontend) {
+ printk(KERN_ERR "%s/dvb: frontend initialization failed\n", dev->name);
+ goto dettach_frontend;
+ }
+ /* define general-purpose callback pointer */
+ fe0->dvb.frontend->callback = saa7134_tuner_callback;
+
+ /* register everything else */
+ ret = videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev,
+ &dev->pci->dev, adapter_nr, 0, NULL);
+
+ /* this sequence is necessary to make the tda1004x load its firmware
+ * and to enter analog mode of hybrid boards
+ */
+ if (!ret) {
+ if (fe0->dvb.frontend->ops.init)
+ fe0->dvb.frontend->ops.init(fe0->dvb.frontend);
+ if (fe0->dvb.frontend->ops.sleep)
+ fe0->dvb.frontend->ops.sleep(fe0->dvb.frontend);
+ if (fe0->dvb.frontend->ops.tuner_ops.sleep)
+ fe0->dvb.frontend->ops.tuner_ops.sleep(fe0->dvb.frontend);
+ }
+ return ret;
+
+dettach_frontend:
+ videobuf_dvb_dealloc_frontends(&dev->frontends);
+ return -EINVAL;
+}
+
+static int dvb_fini(struct saa7134_dev *dev)
+{
+ struct videobuf_dvb_frontend *fe0;
+
+ /* Get the first frontend */
+ fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
+ if (!fe0)
+ return -EINVAL;
+
+ /* FIXME: I suspect that this code is bogus, since the entry for
+ Pinnacle 300I DVB-T PAL already defines the proper init to allow
+ the detection of mt2032 (TDA9887_PORT2_INACTIVE)
+ */
+ if (dev->board == SAA7134_BOARD_PINNACLE_300I_DVBT_PAL) {
+ struct v4l2_priv_tun_config tda9887_cfg;
+ static int on = TDA9887_PRESENT | TDA9887_PORT2_INACTIVE;
+
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &on;
+
+ /* otherwise we don't detect the tuner on next insmod */
+ saa_call_all(dev, tuner, s_config, &tda9887_cfg);
+ } else if (dev->board == SAA7134_BOARD_MEDION_MD8800_QUADRO) {
+ if ((dev->eedata[2] == 0x07) && use_frontend) {
+ /* turn off the 2nd lnb supply */
+ u8 data = 0x80;
+ struct i2c_msg msg = {.addr = 0x08, .buf = &data, .flags = 0, .len = 1};
+ struct dvb_frontend *fe;
+ fe = fe0->dvb.frontend;
+ if (fe->ops.i2c_gate_ctrl) {
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+ }
+ }
+ videobuf_dvb_unregister_bus(&dev->frontends);
+ return 0;
+}
+
+static struct saa7134_mpeg_ops dvb_ops = {
+ .type = SAA7134_MPEG_DVB,
+ .init = dvb_init,
+ .fini = dvb_fini,
+};
+
+static int __init dvb_register(void)
+{
+ return saa7134_ts_register(&dvb_ops);
+}
+
+static void __exit dvb_unregister(void)
+{
+ saa7134_ts_unregister(&dvb_ops);
+}
+
+module_init(dvb_register);
+module_exit(dvb_unregister);
+
+/* ------------------------------------------------------------------ */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c
new file mode 100644
index 00000000..dde361a9
--- /dev/null
+++ b/drivers/media/video/saa7134/saa7134-empress.c
@@ -0,0 +1,590 @@
+/*
+ *
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+
+#include <media/saa6752hs.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
+
+/* ------------------------------------------------------------------ */
+
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+
+static unsigned int empress_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
+
+module_param_array(empress_nr, int, NULL, 0444);
+MODULE_PARM_DESC(empress_nr,"ts device number");
+
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug,"enable debug messages");
+
+#define dprintk(fmt, arg...) if (debug) \
+ printk(KERN_DEBUG "%s/empress: " fmt, dev->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+
+static void ts_reset_encoder(struct saa7134_dev* dev)
+{
+ if (!dev->empress_started)
+ return;
+
+ saa_writeb(SAA7134_SPECIAL_MODE, 0x00);
+ msleep(10);
+ saa_writeb(SAA7134_SPECIAL_MODE, 0x01);
+ msleep(100);
+ dev->empress_started = 0;
+}
+
+static int ts_init_encoder(struct saa7134_dev* dev)
+{
+ u32 leading_null_bytes = 0;
+
+ /* If more cards start to need this, then this
+ should probably be added to the card definitions. */
+ switch (dev->board) {
+ case SAA7134_BOARD_BEHOLD_M6:
+ case SAA7134_BOARD_BEHOLD_M63:
+ case SAA7134_BOARD_BEHOLD_M6_EXTRA:
+ leading_null_bytes = 1;
+ break;
+ }
+ ts_reset_encoder(dev);
+ saa_call_all(dev, core, init, leading_null_bytes);
+ dev->empress_started = 1;
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int ts_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct saa7134_dev *dev = video_drvdata(file);
+ int err;
+
+ dprintk("open dev=%s\n", video_device_node_name(vdev));
+ err = -EBUSY;
+ if (!mutex_trylock(&dev->empress_tsq.vb_lock))
+ return err;
+ if (atomic_read(&dev->empress_users))
+ goto done;
+
+ /* Unmute audio */
+ saa_writeb(SAA7134_AUDIO_MUTE_CTRL,
+ saa_readb(SAA7134_AUDIO_MUTE_CTRL) & ~(1 << 6));
+
+ atomic_inc(&dev->empress_users);
+ file->private_data = dev;
+ err = 0;
+
+done:
+ mutex_unlock(&dev->empress_tsq.vb_lock);
+ return err;
+}
+
+static int ts_release(struct file *file)
+{
+ struct saa7134_dev *dev = file->private_data;
+
+ videobuf_stop(&dev->empress_tsq);
+ videobuf_mmap_free(&dev->empress_tsq);
+
+ /* stop the encoder */
+ ts_reset_encoder(dev);
+
+ /* Mute audio */
+ saa_writeb(SAA7134_AUDIO_MUTE_CTRL,
+ saa_readb(SAA7134_AUDIO_MUTE_CTRL) | (1 << 6));
+
+ atomic_dec(&dev->empress_users);
+
+ return 0;
+}
+
+static ssize_t
+ts_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+{
+ struct saa7134_dev *dev = file->private_data;
+
+ if (!dev->empress_started)
+ ts_init_encoder(dev);
+
+ return videobuf_read_stream(&dev->empress_tsq,
+ data, count, ppos, 0,
+ file->f_flags & O_NONBLOCK);
+}
+
+static unsigned int
+ts_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct saa7134_dev *dev = file->private_data;
+
+ return videobuf_poll_stream(file, &dev->empress_tsq, wait);
+}
+
+
+static int
+ts_mmap(struct file *file, struct vm_area_struct * vma)
+{
+ struct saa7134_dev *dev = file->private_data;
+
+ return videobuf_mmap_mapper(&dev->empress_tsq, vma);
+}
+
+/*
+ * This function is _not_ called directly, but from
+ * video_generic_ioctl (and maybe others). userspace
+ * copying is done already, arg is a kernel pointer.
+ */
+
+static int empress_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct saa7134_dev *dev = file->private_data;
+
+ strcpy(cap->driver, "saa7134");
+ strlcpy(cap->card, saa7134_boards[dev->board].name,
+ sizeof(cap->card));
+ sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+ return 0;
+}
+
+static int empress_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
+
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ strcpy(i->name, "CCIR656");
+
+ return 0;
+}
+
+static int empress_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int empress_s_input(struct file *file, void *priv, unsigned int i)
+{
+ if (i != 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int empress_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index != 0)
+ return -EINVAL;
+
+ strlcpy(f->description, "MPEG TS", sizeof(f->description));
+ f->pixelformat = V4L2_PIX_FMT_MPEG;
+
+ return 0;
+}
+
+static int empress_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct saa7134_dev *dev = file->private_data;
+ struct v4l2_mbus_framefmt mbus_fmt;
+
+ saa_call_all(dev, video, g_mbus_fmt, &mbus_fmt);
+
+ v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt);
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets;
+
+ return 0;
+}
+
+static int empress_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct saa7134_dev *dev = file->private_data;
+ struct v4l2_mbus_framefmt mbus_fmt;
+
+ v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED);
+ saa_call_all(dev, video, s_mbus_fmt, &mbus_fmt);
+ v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt);
+
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets;
+
+ return 0;
+}
+
+static int empress_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct saa7134_dev *dev = file->private_data;
+
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets;
+
+ return 0;
+}
+
+static int empress_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct saa7134_dev *dev = file->private_data;
+
+ return videobuf_reqbufs(&dev->empress_tsq, p);
+}
+
+static int empress_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct saa7134_dev *dev = file->private_data;
+
+ return videobuf_querybuf(&dev->empress_tsq, b);
+}
+
+static int empress_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct saa7134_dev *dev = file->private_data;
+
+ return videobuf_qbuf(&dev->empress_tsq, b);
+}
+
+static int empress_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct saa7134_dev *dev = file->private_data;
+
+ return videobuf_dqbuf(&dev->empress_tsq, b,
+ file->f_flags & O_NONBLOCK);
+}
+
+static int empress_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct saa7134_dev *dev = file->private_data;
+
+ return videobuf_streamon(&dev->empress_tsq);
+}
+
+static int empress_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct saa7134_dev *dev = file->private_data;
+
+ return videobuf_streamoff(&dev->empress_tsq);
+}
+
+static int empress_s_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *ctrls)
+{
+ struct saa7134_dev *dev = file->private_data;
+ int err;
+
+ /* count == 0 is abused in saa6752hs.c, so that special
+ case is handled here explicitly. */
+ if (ctrls->count == 0)
+ return 0;
+
+ if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+ return -EINVAL;
+
+ err = saa_call_empress(dev, core, s_ext_ctrls, ctrls);
+ ts_init_encoder(dev);
+
+ return err;
+}
+
+static int empress_g_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *ctrls)
+{
+ struct saa7134_dev *dev = file->private_data;
+
+ if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+ return -EINVAL;
+ return saa_call_empress(dev, core, g_ext_ctrls, ctrls);
+}
+
+static int empress_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *c)
+{
+ struct saa7134_dev *dev = file->private_data;
+
+ return saa7134_g_ctrl_internal(dev, NULL, c);
+}
+
+static int empress_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *c)
+{
+ struct saa7134_dev *dev = file->private_data;
+
+ return saa7134_s_ctrl_internal(dev, NULL, c);
+}
+
+static int empress_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ /* Must be sorted from low to high control ID! */
+ static const u32 user_ctrls[] = {
+ V4L2_CID_USER_CLASS,
+ V4L2_CID_BRIGHTNESS,
+ V4L2_CID_CONTRAST,
+ V4L2_CID_SATURATION,
+ V4L2_CID_HUE,
+ V4L2_CID_AUDIO_VOLUME,
+ V4L2_CID_AUDIO_MUTE,
+ V4L2_CID_HFLIP,
+ 0
+ };
+
+ /* Must be sorted from low to high control ID! */
+ static const u32 mpeg_ctrls[] = {
+ V4L2_CID_MPEG_CLASS,
+ V4L2_CID_MPEG_STREAM_TYPE,
+ V4L2_CID_MPEG_STREAM_PID_PMT,
+ V4L2_CID_MPEG_STREAM_PID_AUDIO,
+ V4L2_CID_MPEG_STREAM_PID_VIDEO,
+ V4L2_CID_MPEG_STREAM_PID_PCR,
+ V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ,
+ V4L2_CID_MPEG_AUDIO_ENCODING,
+ V4L2_CID_MPEG_AUDIO_L2_BITRATE,
+ V4L2_CID_MPEG_VIDEO_ENCODING,
+ V4L2_CID_MPEG_VIDEO_ASPECT,
+ V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+ V4L2_CID_MPEG_VIDEO_BITRATE,
+ V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
+ 0
+ };
+ static const u32 *ctrl_classes[] = {
+ user_ctrls,
+ mpeg_ctrls,
+ NULL
+ };
+ struct saa7134_dev *dev = file->private_data;
+
+ c->id = v4l2_ctrl_next(ctrl_classes, c->id);
+ if (c->id == 0)
+ return -EINVAL;
+ if (c->id == V4L2_CID_USER_CLASS || c->id == V4L2_CID_MPEG_CLASS)
+ return v4l2_ctrl_query_fill(c, 0, 0, 0, 0);
+ if (V4L2_CTRL_ID2CLASS(c->id) != V4L2_CTRL_CLASS_MPEG)
+ return saa7134_queryctrl(file, priv, c);
+ return saa_call_empress(dev, core, queryctrl, c);
+}
+
+static int empress_querymenu(struct file *file, void *priv,
+ struct v4l2_querymenu *c)
+{
+ struct saa7134_dev *dev = file->private_data;
+
+ if (V4L2_CTRL_ID2CLASS(c->id) != V4L2_CTRL_CLASS_MPEG)
+ return -EINVAL;
+ return saa_call_empress(dev, core, querymenu, c);
+}
+
+static int empress_g_chip_ident(struct file *file, void *fh,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct saa7134_dev *dev = file->private_data;
+
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+ if (chip->match.type == V4L2_CHIP_MATCH_I2C_DRIVER &&
+ !strcmp(chip->match.name, "saa6752hs"))
+ return saa_call_empress(dev, core, g_chip_ident, chip);
+ if (chip->match.type == V4L2_CHIP_MATCH_I2C_ADDR)
+ return saa_call_empress(dev, core, g_chip_ident, chip);
+ return -EINVAL;
+}
+
+static int empress_s_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct saa7134_dev *dev = file->private_data;
+
+ return saa7134_s_std_internal(dev, NULL, id);
+}
+
+static int empress_g_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct saa7134_dev *dev = file->private_data;
+
+ *id = dev->tvnorm->id;
+ return 0;
+}
+
+static const struct v4l2_file_operations ts_fops =
+{
+ .owner = THIS_MODULE,
+ .open = ts_open,
+ .release = ts_release,
+ .read = ts_read,
+ .poll = ts_poll,
+ .mmap = ts_mmap,
+ .ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops ts_ioctl_ops = {
+ .vidioc_querycap = empress_querycap,
+ .vidioc_enum_fmt_vid_cap = empress_enum_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = empress_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = empress_s_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = empress_g_fmt_vid_cap,
+ .vidioc_reqbufs = empress_reqbufs,
+ .vidioc_querybuf = empress_querybuf,
+ .vidioc_qbuf = empress_qbuf,
+ .vidioc_dqbuf = empress_dqbuf,
+ .vidioc_streamon = empress_streamon,
+ .vidioc_streamoff = empress_streamoff,
+ .vidioc_s_ext_ctrls = empress_s_ext_ctrls,
+ .vidioc_g_ext_ctrls = empress_g_ext_ctrls,
+ .vidioc_enum_input = empress_enum_input,
+ .vidioc_g_input = empress_g_input,
+ .vidioc_s_input = empress_s_input,
+ .vidioc_queryctrl = empress_queryctrl,
+ .vidioc_querymenu = empress_querymenu,
+ .vidioc_g_ctrl = empress_g_ctrl,
+ .vidioc_s_ctrl = empress_s_ctrl,
+ .vidioc_g_chip_ident = empress_g_chip_ident,
+ .vidioc_s_std = empress_s_std,
+ .vidioc_g_std = empress_g_std,
+};
+
+/* ----------------------------------------------------------- */
+
+static struct video_device saa7134_empress_template = {
+ .name = "saa7134-empress",
+ .fops = &ts_fops,
+ .ioctl_ops = &ts_ioctl_ops,
+
+ .tvnorms = SAA7134_NORMS,
+ .current_norm = V4L2_STD_PAL,
+};
+
+static void empress_signal_update(struct work_struct *work)
+{
+ struct saa7134_dev* dev =
+ container_of(work, struct saa7134_dev, empress_workqueue);
+
+ if (dev->nosignal) {
+ dprintk("no video signal\n");
+ } else {
+ dprintk("video signal acquired\n");
+ }
+}
+
+static void empress_signal_change(struct saa7134_dev *dev)
+{
+ schedule_work(&dev->empress_workqueue);
+}
+
+
+static int empress_init(struct saa7134_dev *dev)
+{
+ int err;
+
+ dprintk("%s: %s\n",dev->name,__func__);
+ dev->empress_dev = video_device_alloc();
+ if (NULL == dev->empress_dev)
+ return -ENOMEM;
+ *(dev->empress_dev) = saa7134_empress_template;
+ dev->empress_dev->parent = &dev->pci->dev;
+ dev->empress_dev->release = video_device_release;
+ snprintf(dev->empress_dev->name, sizeof(dev->empress_dev->name),
+ "%s empress (%s)", dev->name,
+ saa7134_boards[dev->board].name);
+
+ INIT_WORK(&dev->empress_workqueue, empress_signal_update);
+
+ video_set_drvdata(dev->empress_dev, dev);
+ err = video_register_device(dev->empress_dev,VFL_TYPE_GRABBER,
+ empress_nr[dev->nr]);
+ if (err < 0) {
+ printk(KERN_INFO "%s: can't register video device\n",
+ dev->name);
+ video_device_release(dev->empress_dev);
+ dev->empress_dev = NULL;
+ return err;
+ }
+ printk(KERN_INFO "%s: registered device %s [mpeg]\n",
+ dev->name, video_device_node_name(dev->empress_dev));
+
+ videobuf_queue_sg_init(&dev->empress_tsq, &saa7134_ts_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_ALTERNATE,
+ sizeof(struct saa7134_buf),
+ dev, NULL);
+
+ empress_signal_update(&dev->empress_workqueue);
+ return 0;
+}
+
+static int empress_fini(struct saa7134_dev *dev)
+{
+ dprintk("%s: %s\n",dev->name,__func__);
+
+ if (NULL == dev->empress_dev)
+ return 0;
+ flush_work_sync(&dev->empress_workqueue);
+ video_unregister_device(dev->empress_dev);
+ dev->empress_dev = NULL;
+ return 0;
+}
+
+static struct saa7134_mpeg_ops empress_ops = {
+ .type = SAA7134_MPEG_EMPRESS,
+ .init = empress_init,
+ .fini = empress_fini,
+ .signal_change = empress_signal_change,
+};
+
+static int __init empress_register(void)
+{
+ return saa7134_ts_register(&empress_ops);
+}
+
+static void __exit empress_unregister(void)
+{
+ saa7134_ts_unregister(&empress_ops);
+}
+
+module_init(empress_register);
+module_exit(empress_unregister);
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c
new file mode 100644
index 00000000..a176ec32
--- /dev/null
+++ b/drivers/media/video/saa7134/saa7134-i2c.c
@@ -0,0 +1,435 @@
+/*
+ *
+ * device driver for philips saa7134 based TV cards
+ * i2c interface support
+ *
+ * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+#include <media/v4l2-common.h>
+
+/* ----------------------------------------------------------- */
+
+static unsigned int i2c_debug;
+module_param(i2c_debug, int, 0644);
+MODULE_PARM_DESC(i2c_debug,"enable debug messages [i2c]");
+
+static unsigned int i2c_scan;
+module_param(i2c_scan, int, 0444);
+MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time");
+
+#define d1printk if (1 == i2c_debug) printk
+#define d2printk if (2 == i2c_debug) printk
+
+#define I2C_WAIT_DELAY 32
+#define I2C_WAIT_RETRY 16
+
+/* ----------------------------------------------------------- */
+
+static char *str_i2c_status[] = {
+ "IDLE", "DONE_STOP", "BUSY", "TO_SCL", "TO_ARB", "DONE_WRITE",
+ "DONE_READ", "DONE_WRITE_TO", "DONE_READ_TO", "NO_DEVICE",
+ "NO_ACKN", "BUS_ERR", "ARB_LOST", "SEQ_ERR", "ST_ERR", "SW_ERR"
+};
+
+enum i2c_status {
+ IDLE = 0, // no I2C command pending
+ DONE_STOP = 1, // I2C command done and STOP executed
+ BUSY = 2, // executing I2C command
+ TO_SCL = 3, // executing I2C command, time out on clock stretching
+ TO_ARB = 4, // time out on arbitration trial, still trying
+ DONE_WRITE = 5, // I2C command done and awaiting next write command
+ DONE_READ = 6, // I2C command done and awaiting next read command
+ DONE_WRITE_TO = 7, // see 5, and time out on status echo
+ DONE_READ_TO = 8, // see 6, and time out on status echo
+ NO_DEVICE = 9, // no acknowledge on device slave address
+ NO_ACKN = 10, // no acknowledge after data byte transfer
+ BUS_ERR = 11, // bus error
+ ARB_LOST = 12, // arbitration lost during transfer
+ SEQ_ERR = 13, // erroneous programming sequence
+ ST_ERR = 14, // wrong status echoing
+ SW_ERR = 15 // software error
+};
+
+static char *str_i2c_attr[] = {
+ "NOP", "STOP", "CONTINUE", "START"
+};
+
+enum i2c_attr {
+ NOP = 0, // no operation on I2C bus
+ STOP = 1, // stop condition, no associated byte transfer
+ CONTINUE = 2, // continue with byte transfer
+ START = 3 // start condition with byte transfer
+};
+
+static inline enum i2c_status i2c_get_status(struct saa7134_dev *dev)
+{
+ enum i2c_status status;
+
+ status = saa_readb(SAA7134_I2C_ATTR_STATUS) & 0x0f;
+ d2printk(KERN_DEBUG "%s: i2c stat <= %s\n",dev->name,
+ str_i2c_status[status]);
+ return status;
+}
+
+static inline void i2c_set_status(struct saa7134_dev *dev,
+ enum i2c_status status)
+{
+ d2printk(KERN_DEBUG "%s: i2c stat => %s\n",dev->name,
+ str_i2c_status[status]);
+ saa_andorb(SAA7134_I2C_ATTR_STATUS,0x0f,status);
+}
+
+static inline void i2c_set_attr(struct saa7134_dev *dev, enum i2c_attr attr)
+{
+ d2printk(KERN_DEBUG "%s: i2c attr => %s\n",dev->name,
+ str_i2c_attr[attr]);
+ saa_andorb(SAA7134_I2C_ATTR_STATUS,0xc0,attr << 6);
+}
+
+static inline int i2c_is_error(enum i2c_status status)
+{
+ switch (status) {
+ case NO_DEVICE:
+ case NO_ACKN:
+ case BUS_ERR:
+ case ARB_LOST:
+ case SEQ_ERR:
+ case ST_ERR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static inline int i2c_is_idle(enum i2c_status status)
+{
+ switch (status) {
+ case IDLE:
+ case DONE_STOP:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static inline int i2c_is_busy(enum i2c_status status)
+{
+ switch (status) {
+ case BUSY:
+ case TO_SCL:
+ case TO_ARB:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int i2c_is_busy_wait(struct saa7134_dev *dev)
+{
+ enum i2c_status status;
+ int count;
+
+ for (count = 0; count < I2C_WAIT_RETRY; count++) {
+ status = i2c_get_status(dev);
+ if (!i2c_is_busy(status))
+ break;
+ saa_wait(I2C_WAIT_DELAY);
+ }
+ if (I2C_WAIT_RETRY == count)
+ return false;
+ return true;
+}
+
+static int i2c_reset(struct saa7134_dev *dev)
+{
+ enum i2c_status status;
+ int count;
+
+ d2printk(KERN_DEBUG "%s: i2c reset\n",dev->name);
+ status = i2c_get_status(dev);
+ if (!i2c_is_error(status))
+ return true;
+ i2c_set_status(dev,status);
+
+ for (count = 0; count < I2C_WAIT_RETRY; count++) {
+ status = i2c_get_status(dev);
+ if (!i2c_is_error(status))
+ break;
+ udelay(I2C_WAIT_DELAY);
+ }
+ if (I2C_WAIT_RETRY == count)
+ return false;
+
+ if (!i2c_is_idle(status))
+ return false;
+
+ i2c_set_attr(dev,NOP);
+ return true;
+}
+
+static inline int i2c_send_byte(struct saa7134_dev *dev,
+ enum i2c_attr attr,
+ unsigned char data)
+{
+ enum i2c_status status;
+ __u32 dword;
+
+ /* have to write both attr + data in one 32bit word */
+ dword = saa_readl(SAA7134_I2C_ATTR_STATUS >> 2);
+ dword &= 0x0f;
+ dword |= (attr << 6);
+ dword |= ((__u32)data << 8);
+ dword |= 0x00 << 16; /* 100 kHz */
+// dword |= 0x40 << 16; /* 400 kHz */
+ dword |= 0xf0 << 24;
+ saa_writel(SAA7134_I2C_ATTR_STATUS >> 2, dword);
+ d2printk(KERN_DEBUG "%s: i2c data => 0x%x\n",dev->name,data);
+
+ if (!i2c_is_busy_wait(dev))
+ return -EIO;
+ status = i2c_get_status(dev);
+ if (i2c_is_error(status))
+ return -EIO;
+ return 0;
+}
+
+static inline int i2c_recv_byte(struct saa7134_dev *dev)
+{
+ enum i2c_status status;
+ unsigned char data;
+
+ i2c_set_attr(dev,CONTINUE);
+ if (!i2c_is_busy_wait(dev))
+ return -EIO;
+ status = i2c_get_status(dev);
+ if (i2c_is_error(status))
+ return -EIO;
+ data = saa_readb(SAA7134_I2C_DATA);
+ d2printk(KERN_DEBUG "%s: i2c data <= 0x%x\n",dev->name,data);
+ return data;
+}
+
+static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg *msgs, int num)
+{
+ struct saa7134_dev *dev = i2c_adap->algo_data;
+ enum i2c_status status;
+ unsigned char data;
+ int addr,rc,i,byte;
+
+ status = i2c_get_status(dev);
+ if (!i2c_is_idle(status))
+ if (!i2c_reset(dev))
+ return -EIO;
+
+ d2printk("start xfer\n");
+ d1printk(KERN_DEBUG "%s: i2c xfer:",dev->name);
+ for (i = 0; i < num; i++) {
+ if (!(msgs[i].flags & I2C_M_NOSTART) || 0 == i) {
+ /* send address */
+ d2printk("send address\n");
+ addr = msgs[i].addr << 1;
+ if (msgs[i].flags & I2C_M_RD)
+ addr |= 1;
+ if (i > 0 && msgs[i].flags &
+ I2C_M_RD && msgs[i].addr != 0x40 &&
+ msgs[i].addr != 0x19) {
+ /* workaround for a saa7134 i2c bug
+ * needed to talk to the mt352 demux
+ * thanks to pinnacle for the hint */
+ int quirk = 0xfe;
+ d1printk(" [%02x quirk]",quirk);
+ i2c_send_byte(dev,START,quirk);
+ i2c_recv_byte(dev);
+ }
+ d1printk(" < %02x", addr);
+ rc = i2c_send_byte(dev,START,addr);
+ if (rc < 0)
+ goto err;
+ }
+ if (msgs[i].flags & I2C_M_RD) {
+ /* read bytes */
+ d2printk("read bytes\n");
+ for (byte = 0; byte < msgs[i].len; byte++) {
+ d1printk(" =");
+ rc = i2c_recv_byte(dev);
+ if (rc < 0)
+ goto err;
+ d1printk("%02x", rc);
+ msgs[i].buf[byte] = rc;
+ }
+ /* discard mysterious extra byte when reading
+ from Samsung S5H1411. i2c bus gets error
+ if we do not. */
+ if (0x19 == msgs[i].addr) {
+ d1printk(" ?");
+ rc = i2c_recv_byte(dev);
+ if (rc < 0)
+ goto err;
+ d1printk("%02x", rc);
+ }
+ } else {
+ /* write bytes */
+ d2printk("write bytes\n");
+ for (byte = 0; byte < msgs[i].len; byte++) {
+ data = msgs[i].buf[byte];
+ d1printk(" %02x", data);
+ rc = i2c_send_byte(dev,CONTINUE,data);
+ if (rc < 0)
+ goto err;
+ }
+ }
+ }
+ d2printk("xfer done\n");
+ d1printk(" >");
+ i2c_set_attr(dev,STOP);
+ rc = -EIO;
+ if (!i2c_is_busy_wait(dev))
+ goto err;
+ status = i2c_get_status(dev);
+ if (i2c_is_error(status))
+ goto err;
+ /* ensure that the bus is idle for at least one bit slot */
+ msleep(1);
+
+ d1printk("\n");
+ return num;
+ err:
+ if (1 == i2c_debug) {
+ status = i2c_get_status(dev);
+ printk(" ERROR: %s\n",str_i2c_status[status]);
+ }
+ return rc;
+}
+
+/* ----------------------------------------------------------- */
+
+static u32 functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_EMUL;
+}
+
+static struct i2c_algorithm saa7134_algo = {
+ .master_xfer = saa7134_i2c_xfer,
+ .functionality = functionality,
+};
+
+static struct i2c_adapter saa7134_adap_template = {
+ .owner = THIS_MODULE,
+ .name = "saa7134",
+ .algo = &saa7134_algo,
+};
+
+static struct i2c_client saa7134_client_template = {
+ .name = "saa7134 internal",
+};
+
+/* ----------------------------------------------------------- */
+
+static int
+saa7134_i2c_eeprom(struct saa7134_dev *dev, unsigned char *eedata, int len)
+{
+ unsigned char buf;
+ int i,err;
+
+ dev->i2c_client.addr = 0xa0 >> 1;
+ buf = 0;
+ if (1 != (err = i2c_master_send(&dev->i2c_client,&buf,1))) {
+ printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n",
+ dev->name,err);
+ return -1;
+ }
+ if (len != (err = i2c_master_recv(&dev->i2c_client,eedata,len))) {
+ printk(KERN_WARNING "%s: i2c eeprom read error (err=%d)\n",
+ dev->name,err);
+ return -1;
+ }
+ for (i = 0; i < len; i++) {
+ if (0 == (i % 16))
+ printk(KERN_INFO "%s: i2c eeprom %02x:",dev->name,i);
+ printk(" %02x",eedata[i]);
+ if (15 == (i % 16))
+ printk("\n");
+ }
+ return 0;
+}
+
+static char *i2c_devs[128] = {
+ [ 0x20 ] = "mpeg encoder (saa6752hs)",
+ [ 0xa0 >> 1 ] = "eeprom",
+ [ 0xc0 >> 1 ] = "tuner (analog)",
+ [ 0x86 >> 1 ] = "tda9887",
+ [ 0x5a >> 1 ] = "remote control",
+};
+
+static void do_i2c_scan(char *name, struct i2c_client *c)
+{
+ unsigned char buf;
+ int i,rc;
+
+ for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) {
+ c->addr = i;
+ rc = i2c_master_recv(c,&buf,0);
+ if (rc < 0)
+ continue;
+ printk("%s: i2c scan: found device @ 0x%x [%s]\n",
+ name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
+ }
+}
+
+int saa7134_i2c_register(struct saa7134_dev *dev)
+{
+ dev->i2c_adap = saa7134_adap_template;
+ dev->i2c_adap.dev.parent = &dev->pci->dev;
+ strcpy(dev->i2c_adap.name,dev->name);
+ dev->i2c_adap.algo_data = dev;
+ i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev);
+ i2c_add_adapter(&dev->i2c_adap);
+
+ dev->i2c_client = saa7134_client_template;
+ dev->i2c_client.adapter = &dev->i2c_adap;
+
+ saa7134_i2c_eeprom(dev,dev->eedata,sizeof(dev->eedata));
+ if (i2c_scan)
+ do_i2c_scan(dev->name,&dev->i2c_client);
+
+ /* Instantiate the IR receiver device, if present */
+ saa7134_probe_i2c_ir(dev);
+ return 0;
+}
+
+int saa7134_i2c_unregister(struct saa7134_dev *dev)
+{
+ i2c_del_adapter(&dev->i2c_adap);
+ return 0;
+}
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c
new file mode 100644
index 00000000..48d28786
--- /dev/null
+++ b/drivers/media/video/saa7134/saa7134-input.c
@@ -0,0 +1,1038 @@
+/*
+ *
+ * handle saa7134 IR remotes via linux kernel input layer.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+
+#define MODULE_NAME "saa7134"
+
+static unsigned int disable_ir;
+module_param(disable_ir, int, 0444);
+MODULE_PARM_DESC(disable_ir,"disable infrared remote support");
+
+static unsigned int ir_debug;
+module_param(ir_debug, int, 0644);
+MODULE_PARM_DESC(ir_debug,"enable debug messages [IR]");
+
+static int pinnacle_remote;
+module_param(pinnacle_remote, int, 0644); /* Choose Pinnacle PCTV remote */
+MODULE_PARM_DESC(pinnacle_remote, "Specify Pinnacle PCTV remote: 0=coloured, 1=grey (defaults to 0)");
+
+#define dprintk(fmt, arg...) if (ir_debug) \
+ printk(KERN_DEBUG "%s/ir: " fmt, dev->name , ## arg)
+#define i2cdprintk(fmt, arg...) if (ir_debug) \
+ printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg)
+
+/* Helper function for raw decoding at GPIO16 or GPIO18 */
+static int saa7134_raw_decode_irq(struct saa7134_dev *dev);
+
+/* -------------------- GPIO generic keycode builder -------------------- */
+
+static int build_key(struct saa7134_dev *dev)
+{
+ struct saa7134_card_ir *ir = dev->remote;
+ u32 gpio, data;
+
+ /* here comes the additional handshake steps for some cards */
+ switch (dev->board) {
+ case SAA7134_BOARD_GOTVIEW_7135:
+ saa_setb(SAA7134_GPIO_GPSTATUS1, 0x80);
+ saa_clearb(SAA7134_GPIO_GPSTATUS1, 0x80);
+ break;
+ }
+ /* rising SAA7134_GPIO_GPRESCAN reads the status */
+ saa_clearb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN);
+ saa_setb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN);
+
+ gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
+ if (ir->polling) {
+ if (ir->last_gpio == gpio)
+ return 0;
+ ir->last_gpio = gpio;
+ }
+
+ data = ir_extract_bits(gpio, ir->mask_keycode);
+ dprintk("build_key gpio=0x%x mask=0x%x data=%d\n",
+ gpio, ir->mask_keycode, data);
+
+ switch (dev->board) {
+ case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG:
+ if (data == ir->mask_keycode)
+ rc_keyup(ir->dev);
+ else
+ rc_keydown_notimeout(ir->dev, data, 0);
+ return 0;
+ }
+
+ if (ir->polling) {
+ if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) ||
+ (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) {
+ rc_keydown_notimeout(ir->dev, data, 0);
+ } else {
+ rc_keyup(ir->dev);
+ }
+ }
+ else { /* IRQ driven mode - handle key press and release in one go */
+ if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) ||
+ (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) {
+ rc_keydown_notimeout(ir->dev, data, 0);
+ rc_keyup(ir->dev);
+ }
+ }
+
+ return 0;
+}
+
+/* --------------------- Chip specific I2C key builders ----------------- */
+
+static int get_key_flydvb_trio(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+ int gpio;
+ int attempt = 0;
+ unsigned char b;
+
+ /* We need this to access GPI Used by the saa_readl macro. */
+ struct saa7134_dev *dev = ir->c->adapter->algo_data;
+
+ if (dev == NULL) {
+ i2cdprintk("get_key_flydvb_trio: "
+ "ir->c->adapter->algo_data is NULL!\n");
+ return -EIO;
+ }
+
+ /* rising SAA7134_GPIGPRESCAN reads the status */
+ saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+ saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+
+ gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
+
+ if (0x40000 & ~gpio)
+ return 0; /* No button press */
+
+ /* No button press - only before first key pressed */
+ if (b == 0xFF)
+ return 0;
+
+ /* poll IR chip */
+ /* weak up the IR chip */
+ b = 0;
+
+ while (1 != i2c_master_send(ir->c, &b, 1)) {
+ if ((attempt++) < 10) {
+ /*
+ * wait a bit for next attempt -
+ * I don't know how make it better
+ */
+ msleep(10);
+ continue;
+ }
+ i2cdprintk("send wake up byte to pic16C505 (IR chip)"
+ "failed %dx\n", attempt);
+ return -EIO;
+ }
+ if (1 != i2c_master_recv(ir->c, &b, 1)) {
+ i2cdprintk("read error\n");
+ return -EIO;
+ }
+
+ *ir_key = b;
+ *ir_raw = b;
+ return 1;
+}
+
+static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir, u32 *ir_key,
+ u32 *ir_raw)
+{
+ unsigned char b;
+ int gpio;
+
+ /* <dev> is needed to access GPIO. Used by the saa_readl macro. */
+ struct saa7134_dev *dev = ir->c->adapter->algo_data;
+ if (dev == NULL) {
+ i2cdprintk("get_key_msi_tvanywhere_plus: "
+ "ir->c->adapter->algo_data is NULL!\n");
+ return -EIO;
+ }
+
+ /* rising SAA7134_GPIO_GPRESCAN reads the status */
+
+ saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+ saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+
+ gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
+
+ /* GPIO&0x40 is pulsed low when a button is pressed. Don't do
+ I2C receive if gpio&0x40 is not low. */
+
+ if (gpio & 0x40)
+ return 0; /* No button press */
+
+ /* GPIO says there is a button press. Get it. */
+
+ if (1 != i2c_master_recv(ir->c, &b, 1)) {
+ i2cdprintk("read error\n");
+ return -EIO;
+ }
+
+ /* No button press */
+
+ if (b == 0xff)
+ return 0;
+
+ /* Button pressed */
+
+ dprintk("get_key_msi_tvanywhere_plus: Key = 0x%02X\n", b);
+ *ir_key = b;
+ *ir_raw = b;
+ return 1;
+}
+
+/* copied and modified from get_key_msi_tvanywhere_plus() */
+static int get_key_kworld_pc150u(struct IR_i2c *ir, u32 *ir_key,
+ u32 *ir_raw)
+{
+ unsigned char b;
+ unsigned int gpio;
+
+ /* <dev> is needed to access GPIO. Used by the saa_readl macro. */
+ struct saa7134_dev *dev = ir->c->adapter->algo_data;
+ if (dev == NULL) {
+ i2cdprintk("get_key_kworld_pc150u: "
+ "ir->c->adapter->algo_data is NULL!\n");
+ return -EIO;
+ }
+
+ /* rising SAA7134_GPIO_GPRESCAN reads the status */
+
+ saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+ saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+
+ gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
+
+ /* GPIO&0x100 is pulsed low when a button is pressed. Don't do
+ I2C receive if gpio&0x100 is not low. */
+
+ if (gpio & 0x100)
+ return 0; /* No button press */
+
+ /* GPIO says there is a button press. Get it. */
+
+ if (1 != i2c_master_recv(ir->c, &b, 1)) {
+ i2cdprintk("read error\n");
+ return -EIO;
+ }
+
+ /* No button press */
+
+ if (b == 0xff)
+ return 0;
+
+ /* Button pressed */
+
+ dprintk("get_key_kworld_pc150u: Key = 0x%02X\n", b);
+ *ir_key = b;
+ *ir_raw = b;
+ return 1;
+}
+
+static int get_key_purpletv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+ unsigned char b;
+
+ /* poll IR chip */
+ if (1 != i2c_master_recv(ir->c, &b, 1)) {
+ i2cdprintk("read error\n");
+ return -EIO;
+ }
+
+ /* no button press */
+ if (b==0)
+ return 0;
+
+ /* repeating */
+ if (b & 0x80)
+ return 1;
+
+ *ir_key = b;
+ *ir_raw = b;
+ return 1;
+}
+
+static int get_key_hvr1110(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+ unsigned char buf[5];
+
+ /* poll IR chip */
+ if (5 != i2c_master_recv(ir->c, buf, 5))
+ return -EIO;
+
+ /* Check if some key were pressed */
+ if (!(buf[0] & 0x80))
+ return 0;
+
+ /*
+ * buf[3] & 0x80 is always high.
+ * buf[3] & 0x40 is a parity bit. A repeat event is marked
+ * by preserving it into two separate readings
+ * buf[4] bits 0 and 1, and buf[1] and buf[2] are always
+ * zero.
+ */
+ *ir_key = 0x1fff & ((buf[3] << 8) | (buf[4] >> 2));
+ *ir_raw = *ir_key;
+ return 1;
+}
+
+
+static int get_key_beholdm6xx(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+ unsigned char data[12];
+ u32 gpio;
+
+ struct saa7134_dev *dev = ir->c->adapter->algo_data;
+
+ /* rising SAA7134_GPIO_GPRESCAN reads the status */
+ saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+ saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+
+ gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
+
+ if (0x400000 & ~gpio)
+ return 0; /* No button press */
+
+ ir->c->addr = 0x5a >> 1;
+
+ if (12 != i2c_master_recv(ir->c, data, 12)) {
+ i2cdprintk("read error\n");
+ return -EIO;
+ }
+
+ if (data[9] != (unsigned char)(~data[8]))
+ return 0;
+
+ *ir_raw = ((data[10] << 16) | (data[11] << 8) | (data[9] << 0));
+ *ir_key = *ir_raw;
+
+ return 1;
+}
+
+/* Common (grey or coloured) pinnacle PCTV remote handling
+ *
+ */
+static int get_key_pinnacle(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw,
+ int parity_offset, int marker, int code_modulo)
+{
+ unsigned char b[4];
+ unsigned int start = 0,parity = 0,code = 0;
+
+ /* poll IR chip */
+ if (4 != i2c_master_recv(ir->c, b, 4)) {
+ i2cdprintk("read error\n");
+ return -EIO;
+ }
+
+ for (start = 0; start < ARRAY_SIZE(b); start++) {
+ if (b[start] == marker) {
+ code=b[(start+parity_offset + 1) % 4];
+ parity=b[(start+parity_offset) % 4];
+ }
+ }
+
+ /* Empty Request */
+ if (parity == 0)
+ return 0;
+
+ /* Repeating... */
+ if (ir->old == parity)
+ return 0;
+
+ ir->old = parity;
+
+ /* drop special codes when a key is held down a long time for the grey controller
+ In this case, the second bit of the code is asserted */
+ if (marker == 0xfe && (code & 0x40))
+ return 0;
+
+ code %= code_modulo;
+
+ *ir_raw = code;
+ *ir_key = code;
+
+ i2cdprintk("Pinnacle PCTV key %02x\n", code);
+
+ return 1;
+}
+
+/* The grey pinnacle PCTV remote
+ *
+ * There are one issue with this remote:
+ * - I2c packet does not change when the same key is pressed quickly. The workaround
+ * is to hold down each key for about half a second, so that another code is generated
+ * in the i2c packet, and the function can distinguish key presses.
+ *
+ * Sylvain Pasche <sylvain.pasche@gmail.com>
+ */
+static int get_key_pinnacle_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+
+ return get_key_pinnacle(ir, ir_key, ir_raw, 1, 0xfe, 0xff);
+}
+
+
+/* The new pinnacle PCTV remote (with the colored buttons)
+ *
+ * Ricardo Cerqueira <v4l@cerqueira.org>
+ */
+static int get_key_pinnacle_color(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+ /* code_modulo parameter (0x88) is used to reduce code value to fit inside IR_KEYTAB_SIZE
+ *
+ * this is the only value that results in 42 unique
+ * codes < 128
+ */
+
+ return get_key_pinnacle(ir, ir_key, ir_raw, 2, 0x80, 0x88);
+}
+
+void saa7134_input_irq(struct saa7134_dev *dev)
+{
+ struct saa7134_card_ir *ir;
+
+ if (!dev || !dev->remote)
+ return;
+
+ ir = dev->remote;
+ if (!ir->running)
+ return;
+
+ if (!ir->polling && !ir->raw_decode) {
+ build_key(dev);
+ } else if (ir->raw_decode) {
+ saa7134_raw_decode_irq(dev);
+ }
+}
+
+static void saa7134_input_timer(unsigned long data)
+{
+ struct saa7134_dev *dev = (struct saa7134_dev *)data;
+ struct saa7134_card_ir *ir = dev->remote;
+
+ build_key(dev);
+ mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling));
+}
+
+static void ir_raw_decode_timer_end(unsigned long data)
+{
+ struct saa7134_dev *dev = (struct saa7134_dev *)data;
+ struct saa7134_card_ir *ir = dev->remote;
+
+ ir_raw_event_handle(dev->remote->dev);
+
+ ir->active = false;
+}
+
+static int __saa7134_ir_start(void *priv)
+{
+ struct saa7134_dev *dev = priv;
+ struct saa7134_card_ir *ir;
+
+ if (!dev || !dev->remote)
+ return -EINVAL;
+
+ ir = dev->remote;
+ if (ir->running)
+ return 0;
+
+ /* Moved here from saa7134_input_init1() because the latter
+ * is not called on device resume */
+ switch (dev->board) {
+ case SAA7134_BOARD_MD2819:
+ case SAA7134_BOARD_KWORLD_VSTREAM_XPERT:
+ case SAA7134_BOARD_AVERMEDIA_305:
+ case SAA7134_BOARD_AVERMEDIA_307:
+ case SAA7134_BOARD_AVERMEDIA_STUDIO_305:
+ case SAA7134_BOARD_AVERMEDIA_STUDIO_505:
+ case SAA7134_BOARD_AVERMEDIA_STUDIO_307:
+ case SAA7134_BOARD_AVERMEDIA_STUDIO_507:
+ case SAA7134_BOARD_AVERMEDIA_STUDIO_507UA:
+ case SAA7134_BOARD_AVERMEDIA_GO_007_FM:
+ case SAA7134_BOARD_AVERMEDIA_M102:
+ case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS:
+ /* Without this we won't receive key up events */
+ saa_setb(SAA7134_GPIO_GPMODE0, 0x4);
+ saa_setb(SAA7134_GPIO_GPSTATUS0, 0x4);
+ break;
+ case SAA7134_BOARD_AVERMEDIA_777:
+ case SAA7134_BOARD_AVERMEDIA_A16AR:
+ /* Without this we won't receive key up events */
+ saa_setb(SAA7134_GPIO_GPMODE1, 0x1);
+ saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1);
+ break;
+ case SAA7134_BOARD_AVERMEDIA_A16D:
+ /* Without this we won't receive key up events */
+ saa_setb(SAA7134_GPIO_GPMODE1, 0x1);
+ saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1);
+ break;
+ case SAA7134_BOARD_GOTVIEW_7135:
+ saa_setb(SAA7134_GPIO_GPMODE1, 0x80);
+ break;
+ }
+
+ ir->running = true;
+ ir->active = false;
+
+ if (ir->polling) {
+ setup_timer(&ir->timer, saa7134_input_timer,
+ (unsigned long)dev);
+ ir->timer.expires = jiffies + HZ;
+ add_timer(&ir->timer);
+ } else if (ir->raw_decode) {
+ /* set timer_end for code completion */
+ setup_timer(&ir->timer, ir_raw_decode_timer_end,
+ (unsigned long)dev);
+ }
+
+ return 0;
+}
+
+static void __saa7134_ir_stop(void *priv)
+{
+ struct saa7134_dev *dev = priv;
+ struct saa7134_card_ir *ir;
+
+ if (!dev || !dev->remote)
+ return;
+
+ ir = dev->remote;
+ if (!ir->running)
+ return;
+
+ if (ir->polling || ir->raw_decode)
+ del_timer_sync(&ir->timer);
+
+ ir->active = false;
+ ir->running = false;
+
+ return;
+}
+
+int saa7134_ir_start(struct saa7134_dev *dev)
+{
+ if (dev->remote->users)
+ return __saa7134_ir_start(dev);
+
+ return 0;
+}
+
+void saa7134_ir_stop(struct saa7134_dev *dev)
+{
+ if (dev->remote->users)
+ __saa7134_ir_stop(dev);
+}
+
+static int saa7134_ir_open(struct rc_dev *rc)
+{
+ struct saa7134_dev *dev = rc->priv;
+
+ dev->remote->users++;
+ return __saa7134_ir_start(dev);
+}
+
+static void saa7134_ir_close(struct rc_dev *rc)
+{
+ struct saa7134_dev *dev = rc->priv;
+
+ dev->remote->users--;
+ if (!dev->remote->users)
+ __saa7134_ir_stop(dev);
+}
+
+int saa7134_input_init1(struct saa7134_dev *dev)
+{
+ struct saa7134_card_ir *ir;
+ struct rc_dev *rc;
+ char *ir_codes = NULL;
+ u32 mask_keycode = 0;
+ u32 mask_keydown = 0;
+ u32 mask_keyup = 0;
+ unsigned polling = 0;
+ bool raw_decode = false;
+ int err;
+
+ if (dev->has_remote != SAA7134_REMOTE_GPIO)
+ return -ENODEV;
+ if (disable_ir)
+ return -ENODEV;
+
+ /* detect & configure */
+ switch (dev->board) {
+ case SAA7134_BOARD_FLYVIDEO2000:
+ case SAA7134_BOARD_FLYVIDEO3000:
+ case SAA7134_BOARD_FLYTVPLATINUM_FM:
+ case SAA7134_BOARD_FLYTVPLATINUM_MINI2:
+ case SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM:
+ ir_codes = RC_MAP_FLYVIDEO;
+ mask_keycode = 0xEC00000;
+ mask_keydown = 0x0040000;
+ break;
+ case SAA7134_BOARD_CINERGY400:
+ case SAA7134_BOARD_CINERGY600:
+ case SAA7134_BOARD_CINERGY600_MK3:
+ ir_codes = RC_MAP_CINERGY;
+ mask_keycode = 0x00003f;
+ mask_keyup = 0x040000;
+ break;
+ case SAA7134_BOARD_ECS_TVP3XP:
+ case SAA7134_BOARD_ECS_TVP3XP_4CB5:
+ ir_codes = RC_MAP_EZTV;
+ mask_keycode = 0x00017c;
+ mask_keyup = 0x000002;
+ polling = 50; // ms
+ break;
+ case SAA7134_BOARD_KWORLD_XPERT:
+ case SAA7134_BOARD_AVACSSMARTTV:
+ ir_codes = RC_MAP_PIXELVIEW;
+ mask_keycode = 0x00001F;
+ mask_keyup = 0x000020;
+ polling = 50; // ms
+ break;
+ case SAA7134_BOARD_MD2819:
+ case SAA7134_BOARD_KWORLD_VSTREAM_XPERT:
+ case SAA7134_BOARD_AVERMEDIA_305:
+ case SAA7134_BOARD_AVERMEDIA_307:
+ case SAA7134_BOARD_AVERMEDIA_STUDIO_305:
+ case SAA7134_BOARD_AVERMEDIA_STUDIO_505:
+ case SAA7134_BOARD_AVERMEDIA_STUDIO_307:
+ case SAA7134_BOARD_AVERMEDIA_STUDIO_507:
+ case SAA7134_BOARD_AVERMEDIA_STUDIO_507UA:
+ case SAA7134_BOARD_AVERMEDIA_GO_007_FM:
+ case SAA7134_BOARD_AVERMEDIA_M102:
+ case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS:
+ ir_codes = RC_MAP_AVERMEDIA;
+ mask_keycode = 0x0007C8;
+ mask_keydown = 0x000010;
+ polling = 50; // ms
+ /* GPIO stuff moved to __saa7134_ir_start() */
+ break;
+ case SAA7134_BOARD_AVERMEDIA_M135A:
+ ir_codes = RC_MAP_AVERMEDIA_M135A;
+ mask_keydown = 0x0040000; /* Enable GPIO18 line on both edges */
+ mask_keyup = 0x0040000;
+ mask_keycode = 0xffff;
+ raw_decode = true;
+ break;
+ case SAA7134_BOARD_AVERMEDIA_M733A:
+ ir_codes = RC_MAP_AVERMEDIA_M733A_RM_K6;
+ mask_keydown = 0x0040000;
+ mask_keyup = 0x0040000;
+ mask_keycode = 0xffff;
+ raw_decode = true;
+ break;
+ case SAA7134_BOARD_AVERMEDIA_777:
+ case SAA7134_BOARD_AVERMEDIA_A16AR:
+ ir_codes = RC_MAP_AVERMEDIA;
+ mask_keycode = 0x02F200;
+ mask_keydown = 0x000400;
+ polling = 50; // ms
+ /* GPIO stuff moved to __saa7134_ir_start() */
+ break;
+ case SAA7134_BOARD_AVERMEDIA_A16D:
+ ir_codes = RC_MAP_AVERMEDIA_A16D;
+ mask_keycode = 0x02F200;
+ mask_keydown = 0x000400;
+ polling = 50; /* ms */
+ /* GPIO stuff moved to __saa7134_ir_start() */
+ break;
+ case SAA7134_BOARD_KWORLD_TERMINATOR:
+ ir_codes = RC_MAP_PIXELVIEW;
+ mask_keycode = 0x00001f;
+ mask_keyup = 0x000060;
+ polling = 50; // ms
+ break;
+ case SAA7134_BOARD_MANLI_MTV001:
+ case SAA7134_BOARD_MANLI_MTV002:
+ ir_codes = RC_MAP_MANLI;
+ mask_keycode = 0x001f00;
+ mask_keyup = 0x004000;
+ polling = 50; /* ms */
+ break;
+ case SAA7134_BOARD_BEHOLD_409FM:
+ case SAA7134_BOARD_BEHOLD_401:
+ case SAA7134_BOARD_BEHOLD_403:
+ case SAA7134_BOARD_BEHOLD_403FM:
+ case SAA7134_BOARD_BEHOLD_405:
+ case SAA7134_BOARD_BEHOLD_405FM:
+ case SAA7134_BOARD_BEHOLD_407:
+ case SAA7134_BOARD_BEHOLD_407FM:
+ case SAA7134_BOARD_BEHOLD_409:
+ case SAA7134_BOARD_BEHOLD_505FM:
+ case SAA7134_BOARD_BEHOLD_505RDS_MK5:
+ case SAA7134_BOARD_BEHOLD_505RDS_MK3:
+ case SAA7134_BOARD_BEHOLD_507_9FM:
+ case SAA7134_BOARD_BEHOLD_507RDS_MK3:
+ case SAA7134_BOARD_BEHOLD_507RDS_MK5:
+ ir_codes = RC_MAP_MANLI;
+ mask_keycode = 0x003f00;
+ mask_keyup = 0x004000;
+ polling = 50; /* ms */
+ break;
+ case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM:
+ ir_codes = RC_MAP_BEHOLD_COLUMBUS;
+ mask_keycode = 0x003f00;
+ mask_keyup = 0x004000;
+ polling = 50; // ms
+ break;
+ case SAA7134_BOARD_SEDNA_PC_TV_CARDBUS:
+ ir_codes = RC_MAP_PCTV_SEDNA;
+ mask_keycode = 0x001f00;
+ mask_keyup = 0x004000;
+ polling = 50; // ms
+ break;
+ case SAA7134_BOARD_GOTVIEW_7135:
+ ir_codes = RC_MAP_GOTVIEW7135;
+ mask_keycode = 0x0003CC;
+ mask_keydown = 0x000010;
+ polling = 5; /* ms */
+ /* GPIO stuff moved to __saa7134_ir_start() */
+ break;
+ case SAA7134_BOARD_VIDEOMATE_TV_PVR:
+ case SAA7134_BOARD_VIDEOMATE_GOLD_PLUS:
+ case SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII:
+ ir_codes = RC_MAP_VIDEOMATE_TV_PVR;
+ mask_keycode = 0x00003F;
+ mask_keyup = 0x400000;
+ polling = 50; // ms
+ break;
+ case SAA7134_BOARD_PROTEUS_2309:
+ ir_codes = RC_MAP_PROTEUS_2309;
+ mask_keycode = 0x00007F;
+ mask_keyup = 0x000080;
+ polling = 50; // ms
+ break;
+ case SAA7134_BOARD_VIDEOMATE_DVBT_300:
+ case SAA7134_BOARD_VIDEOMATE_DVBT_200:
+ ir_codes = RC_MAP_VIDEOMATE_TV_PVR;
+ mask_keycode = 0x003F00;
+ mask_keyup = 0x040000;
+ break;
+ case SAA7134_BOARD_FLYDVBS_LR300:
+ case SAA7134_BOARD_FLYDVBT_LR301:
+ case SAA7134_BOARD_FLYDVBTDUO:
+ ir_codes = RC_MAP_FLYDVB;
+ mask_keycode = 0x0001F00;
+ mask_keydown = 0x0040000;
+ break;
+ case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
+ case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
+ case SAA7134_BOARD_ASUSTeK_P7131_ANALOG:
+ ir_codes = RC_MAP_ASUS_PC39;
+ mask_keydown = 0x0040000; /* Enable GPIO18 line on both edges */
+ mask_keyup = 0x0040000;
+ mask_keycode = 0xffff;
+ raw_decode = true;
+ break;
+ case SAA7134_BOARD_ENCORE_ENLTV:
+ case SAA7134_BOARD_ENCORE_ENLTV_FM:
+ ir_codes = RC_MAP_ENCORE_ENLTV;
+ mask_keycode = 0x00007f;
+ mask_keyup = 0x040000;
+ polling = 50; // ms
+ break;
+ case SAA7134_BOARD_ENCORE_ENLTV_FM53:
+ case SAA7134_BOARD_ENCORE_ENLTV_FM3:
+ ir_codes = RC_MAP_ENCORE_ENLTV_FM53;
+ mask_keydown = 0x0040000; /* Enable GPIO18 line on both edges */
+ mask_keyup = 0x0040000;
+ mask_keycode = 0xffff;
+ raw_decode = true;
+ break;
+ case SAA7134_BOARD_10MOONSTVMASTER3:
+ ir_codes = RC_MAP_ENCORE_ENLTV;
+ mask_keycode = 0x5f80000;
+ mask_keyup = 0x8000000;
+ polling = 50; //ms
+ break;
+ case SAA7134_BOARD_GENIUS_TVGO_A11MCE:
+ ir_codes = RC_MAP_GENIUS_TVGO_A11MCE;
+ mask_keycode = 0xff;
+ mask_keydown = 0xf00000;
+ polling = 50; /* ms */
+ break;
+ case SAA7134_BOARD_REAL_ANGEL_220:
+ ir_codes = RC_MAP_REAL_AUDIO_220_32_KEYS;
+ mask_keycode = 0x3f00;
+ mask_keyup = 0x4000;
+ polling = 50; /* ms */
+ break;
+ case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG:
+ ir_codes = RC_MAP_KWORLD_PLUS_TV_ANALOG;
+ mask_keycode = 0x7f;
+ polling = 40; /* ms */
+ break;
+ case SAA7134_BOARD_VIDEOMATE_S350:
+ ir_codes = RC_MAP_VIDEOMATE_S350;
+ mask_keycode = 0x003f00;
+ mask_keydown = 0x040000;
+ break;
+ case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S:
+ ir_codes = RC_MAP_WINFAST;
+ mask_keycode = 0x5f00;
+ mask_keyup = 0x020000;
+ polling = 50; /* ms */
+ break;
+ case SAA7134_BOARD_VIDEOMATE_M1F:
+ ir_codes = RC_MAP_VIDEOMATE_K100;
+ mask_keycode = 0x0ff00;
+ mask_keyup = 0x040000;
+ break;
+ case SAA7134_BOARD_HAUPPAUGE_HVR1150:
+ case SAA7134_BOARD_HAUPPAUGE_HVR1120:
+ ir_codes = RC_MAP_HAUPPAUGE;
+ mask_keydown = 0x0040000; /* Enable GPIO18 line on both edges */
+ mask_keyup = 0x0040000;
+ mask_keycode = 0xffff;
+ raw_decode = true;
+ break;
+ }
+ if (NULL == ir_codes) {
+ printk("%s: Oops: IR config error [card=%d]\n",
+ dev->name, dev->board);
+ return -ENODEV;
+ }
+
+ ir = kzalloc(sizeof(*ir), GFP_KERNEL);
+ rc = rc_allocate_device();
+ if (!ir || !rc) {
+ err = -ENOMEM;
+ goto err_out_free;
+ }
+
+ ir->dev = rc;
+ dev->remote = ir;
+
+ /* init hardware-specific stuff */
+ ir->mask_keycode = mask_keycode;
+ ir->mask_keydown = mask_keydown;
+ ir->mask_keyup = mask_keyup;
+ ir->polling = polling;
+ ir->raw_decode = raw_decode;
+
+ /* init input device */
+ snprintf(ir->name, sizeof(ir->name), "saa7134 IR (%s)",
+ saa7134_boards[dev->board].name);
+ snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0",
+ pci_name(dev->pci));
+
+ rc->priv = dev;
+ rc->open = saa7134_ir_open;
+ rc->close = saa7134_ir_close;
+ if (raw_decode)
+ rc->driver_type = RC_DRIVER_IR_RAW;
+
+ rc->input_name = ir->name;
+ rc->input_phys = ir->phys;
+ rc->input_id.bustype = BUS_PCI;
+ rc->input_id.version = 1;
+ if (dev->pci->subsystem_vendor) {
+ rc->input_id.vendor = dev->pci->subsystem_vendor;
+ rc->input_id.product = dev->pci->subsystem_device;
+ } else {
+ rc->input_id.vendor = dev->pci->vendor;
+ rc->input_id.product = dev->pci->device;
+ }
+ rc->dev.parent = &dev->pci->dev;
+ rc->map_name = ir_codes;
+ rc->driver_name = MODULE_NAME;
+
+ err = rc_register_device(rc);
+ if (err)
+ goto err_out_free;
+
+ return 0;
+
+err_out_free:
+ rc_free_device(rc);
+ dev->remote = NULL;
+ kfree(ir);
+ return err;
+}
+
+void saa7134_input_fini(struct saa7134_dev *dev)
+{
+ if (NULL == dev->remote)
+ return;
+
+ saa7134_ir_stop(dev);
+ rc_unregister_device(dev->remote->dev);
+ kfree(dev->remote);
+ dev->remote = NULL;
+}
+
+void saa7134_probe_i2c_ir(struct saa7134_dev *dev)
+{
+ struct i2c_board_info info;
+ struct i2c_msg msg_msi = {
+ .addr = 0x50,
+ .flags = I2C_M_RD,
+ .len = 0,
+ .buf = NULL,
+ };
+ int rc;
+
+ if (disable_ir) {
+ dprintk("IR has been disabled, not probing for i2c remote\n");
+ return;
+ }
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ memset(&dev->init_data, 0, sizeof(dev->init_data));
+ strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
+
+ switch (dev->board) {
+ case SAA7134_BOARD_PINNACLE_PCTV_110i:
+ case SAA7134_BOARD_PINNACLE_PCTV_310i:
+ dev->init_data.name = "Pinnacle PCTV";
+ if (pinnacle_remote == 0) {
+ dev->init_data.get_key = get_key_pinnacle_color;
+ dev->init_data.ir_codes = RC_MAP_PINNACLE_COLOR;
+ info.addr = 0x47;
+ } else {
+ dev->init_data.get_key = get_key_pinnacle_grey;
+ dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY;
+ info.addr = 0x47;
+ }
+ break;
+ case SAA7134_BOARD_UPMOST_PURPLE_TV:
+ dev->init_data.name = "Purple TV";
+ dev->init_data.get_key = get_key_purpletv;
+ dev->init_data.ir_codes = RC_MAP_PURPLETV;
+ info.addr = 0x7a;
+ break;
+ case SAA7134_BOARD_MSI_TVATANYWHERE_PLUS:
+ dev->init_data.name = "MSI TV@nywhere Plus";
+ dev->init_data.get_key = get_key_msi_tvanywhere_plus;
+ dev->init_data.ir_codes = RC_MAP_MSI_TVANYWHERE_PLUS;
+ /*
+ * MSI TV@nyware Plus requires more frequent polling
+ * otherwise it will miss some keypresses
+ */
+ dev->init_data.polling_interval = 50;
+ info.addr = 0x30;
+ /* MSI TV@nywhere Plus controller doesn't seem to
+ respond to probes unless we read something from
+ an existing device. Weird...
+ REVISIT: might no longer be needed */
+ rc = i2c_transfer(&dev->i2c_adap, &msg_msi, 1);
+ dprintk("probe 0x%02x @ %s: %s\n",
+ msg_msi.addr, dev->i2c_adap.name,
+ (1 == rc) ? "yes" : "no");
+ break;
+ case SAA7134_BOARD_KWORLD_PC150U:
+ /* copied and modified from MSI TV@nywhere Plus */
+ dev->init_data.name = "Kworld PC150-U";
+ dev->init_data.get_key = get_key_kworld_pc150u;
+ dev->init_data.ir_codes = RC_MAP_KWORLD_PC150U;
+ info.addr = 0x30;
+ /* MSI TV@nywhere Plus controller doesn't seem to
+ respond to probes unless we read something from
+ an existing device. Weird...
+ REVISIT: might no longer be needed */
+ rc = i2c_transfer(&dev->i2c_adap, &msg_msi, 1);
+ dprintk("probe 0x%02x @ %s: %s\n",
+ msg_msi.addr, dev->i2c_adap.name,
+ (1 == rc) ? "yes" : "no");
+ break;
+ case SAA7134_BOARD_HAUPPAUGE_HVR1110:
+ dev->init_data.name = "HVR 1110";
+ dev->init_data.get_key = get_key_hvr1110;
+ dev->init_data.ir_codes = RC_MAP_HAUPPAUGE;
+ info.addr = 0x71;
+ break;
+ case SAA7134_BOARD_BEHOLD_607FM_MK3:
+ case SAA7134_BOARD_BEHOLD_607FM_MK5:
+ case SAA7134_BOARD_BEHOLD_609FM_MK3:
+ case SAA7134_BOARD_BEHOLD_609FM_MK5:
+ case SAA7134_BOARD_BEHOLD_607RDS_MK3:
+ case SAA7134_BOARD_BEHOLD_607RDS_MK5:
+ case SAA7134_BOARD_BEHOLD_609RDS_MK3:
+ case SAA7134_BOARD_BEHOLD_609RDS_MK5:
+ case SAA7134_BOARD_BEHOLD_M6:
+ case SAA7134_BOARD_BEHOLD_M63:
+ case SAA7134_BOARD_BEHOLD_M6_EXTRA:
+ case SAA7134_BOARD_BEHOLD_H6:
+ case SAA7134_BOARD_BEHOLD_X7:
+ case SAA7134_BOARD_BEHOLD_H7:
+ case SAA7134_BOARD_BEHOLD_A7:
+ dev->init_data.name = "BeholdTV";
+ dev->init_data.get_key = get_key_beholdm6xx;
+ dev->init_data.ir_codes = RC_MAP_BEHOLD;
+ dev->init_data.type = RC_TYPE_NEC;
+ info.addr = 0x2d;
+ break;
+ case SAA7134_BOARD_AVERMEDIA_CARDBUS_501:
+ case SAA7134_BOARD_AVERMEDIA_CARDBUS_506:
+ info.addr = 0x40;
+ break;
+ case SAA7134_BOARD_FLYDVB_TRIO:
+ dev->init_data.name = "FlyDVB Trio";
+ dev->init_data.get_key = get_key_flydvb_trio;
+ dev->init_data.ir_codes = RC_MAP_FLYDVB;
+ info.addr = 0x0b;
+ break;
+ default:
+ dprintk("No I2C IR support for board %x\n", dev->board);
+ return;
+ }
+
+ if (dev->init_data.name)
+ info.platform_data = &dev->init_data;
+ i2c_new_device(&dev->i2c_adap, &info);
+}
+
+static int saa7134_raw_decode_irq(struct saa7134_dev *dev)
+{
+ struct saa7134_card_ir *ir = dev->remote;
+ unsigned long timeout;
+ int space;
+
+ /* Generate initial event */
+ saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+ saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+ space = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2) & ir->mask_keydown;
+ ir_raw_event_store_edge(dev->remote->dev, space ? IR_SPACE : IR_PULSE);
+
+ /*
+ * Wait 15 ms from the start of the first IR event before processing
+ * the event. This time is enough for NEC protocol. May need adjustments
+ * to work with other protocols.
+ */
+ if (!ir->active) {
+ timeout = jiffies + msecs_to_jiffies(15);
+ mod_timer(&ir->timer, timeout);
+ ir->active = true;
+ }
+
+ return 1;
+}
diff --git a/drivers/media/video/saa7134/saa7134-reg.h b/drivers/media/video/saa7134/saa7134-reg.h
new file mode 100644
index 00000000..e7e0af10
--- /dev/null
+++ b/drivers/media/video/saa7134/saa7134-reg.h
@@ -0,0 +1,378 @@
+/*
+ *
+ * philips saa7134 registers
+ */
+
+/* ------------------------------------------------------------------ */
+/*
+ * PCI ID's
+ */
+#ifndef PCI_DEVICE_ID_PHILIPS_SAA7130
+# define PCI_DEVICE_ID_PHILIPS_SAA7130 0x7130
+#endif
+#ifndef PCI_DEVICE_ID_PHILIPS_SAA7133
+# define PCI_DEVICE_ID_PHILIPS_SAA7133 0x7133
+#endif
+#ifndef PCI_DEVICE_ID_PHILIPS_SAA7134
+# define PCI_DEVICE_ID_PHILIPS_SAA7134 0x7134
+#endif
+#ifndef PCI_DEVICE_ID_PHILIPS_SAA7135
+# define PCI_DEVICE_ID_PHILIPS_SAA7135 0x7135
+#endif
+
+/* ------------------------------------------------------------------ */
+/*
+ * registers -- 32 bit
+ */
+
+/* DMA channels, n = 0 ... 6 */
+#define SAA7134_RS_BA1(n) ((0x200 >> 2) + 4*n)
+#define SAA7134_RS_BA2(n) ((0x204 >> 2) + 4*n)
+#define SAA7134_RS_PITCH(n) ((0x208 >> 2) + 4*n)
+#define SAA7134_RS_CONTROL(n) ((0x20c >> 2) + 4*n)
+#define SAA7134_RS_CONTROL_WSWAP (0x01 << 25)
+#define SAA7134_RS_CONTROL_BSWAP (0x01 << 24)
+#define SAA7134_RS_CONTROL_BURST_2 (0x01 << 21)
+#define SAA7134_RS_CONTROL_BURST_4 (0x02 << 21)
+#define SAA7134_RS_CONTROL_BURST_8 (0x03 << 21)
+#define SAA7134_RS_CONTROL_BURST_16 (0x04 << 21)
+#define SAA7134_RS_CONTROL_BURST_32 (0x05 << 21)
+#define SAA7134_RS_CONTROL_BURST_64 (0x06 << 21)
+#define SAA7134_RS_CONTROL_BURST_MAX (0x07 << 21)
+#define SAA7134_RS_CONTROL_ME (0x01 << 20)
+#define SAA7134_FIFO_SIZE (0x2a0 >> 2)
+#define SAA7134_THRESHOULD (0x2a4 >> 2)
+
+#define SAA7133_NUM_SAMPLES (0x588 >> 2)
+#define SAA7133_AUDIO_CHANNEL (0x58c >> 2)
+#define SAA7133_AUDIO_FORMAT (0x58f >> 2)
+#define SAA7133_DIGITAL_OUTPUT_SEL1 (0x46c >> 2)
+#define SAA7133_DIGITAL_OUTPUT_SEL2 (0x470 >> 2)
+#define SAA7133_DIGITAL_INPUT_XBAR1 (0x464 >> 2)
+#define SAA7133_ANALOG_IO_SELECT (0x594 >> 2)
+
+/* main control */
+#define SAA7134_MAIN_CTRL (0x2a8 >> 2)
+#define SAA7134_MAIN_CTRL_VPLLE (1 << 15)
+#define SAA7134_MAIN_CTRL_APLLE (1 << 14)
+#define SAA7134_MAIN_CTRL_EXOSC (1 << 13)
+#define SAA7134_MAIN_CTRL_EVFE1 (1 << 12)
+#define SAA7134_MAIN_CTRL_EVFE2 (1 << 11)
+#define SAA7134_MAIN_CTRL_ESFE (1 << 10)
+#define SAA7134_MAIN_CTRL_EBADC (1 << 9)
+#define SAA7134_MAIN_CTRL_EBDAC (1 << 8)
+#define SAA7134_MAIN_CTRL_TE6 (1 << 6)
+#define SAA7134_MAIN_CTRL_TE5 (1 << 5)
+#define SAA7134_MAIN_CTRL_TE4 (1 << 4)
+#define SAA7134_MAIN_CTRL_TE3 (1 << 3)
+#define SAA7134_MAIN_CTRL_TE2 (1 << 2)
+#define SAA7134_MAIN_CTRL_TE1 (1 << 1)
+#define SAA7134_MAIN_CTRL_TE0 (1 << 0)
+
+/* DMA status */
+#define SAA7134_DMA_STATUS (0x2ac >> 2)
+
+/* audio / video status */
+#define SAA7134_AV_STATUS (0x2c0 >> 2)
+#define SAA7134_AV_STATUS_STEREO (1 << 17)
+#define SAA7134_AV_STATUS_DUAL (1 << 16)
+#define SAA7134_AV_STATUS_PILOT (1 << 15)
+#define SAA7134_AV_STATUS_SMB (1 << 14)
+#define SAA7134_AV_STATUS_DMB (1 << 13)
+#define SAA7134_AV_STATUS_VDSP (1 << 12)
+#define SAA7134_AV_STATUS_IIC_STATUS (3 << 10)
+#define SAA7134_AV_STATUS_MVM (7 << 7)
+#define SAA7134_AV_STATUS_FIDT (1 << 6)
+#define SAA7134_AV_STATUS_INTL (1 << 5)
+#define SAA7134_AV_STATUS_RDCAP (1 << 4)
+#define SAA7134_AV_STATUS_PWR_ON (1 << 3)
+#define SAA7134_AV_STATUS_LOAD_ERR (1 << 2)
+#define SAA7134_AV_STATUS_TRIG_ERR (1 << 1)
+#define SAA7134_AV_STATUS_CONF_ERR (1 << 0)
+
+/* interrupt */
+#define SAA7134_IRQ1 (0x2c4 >> 2)
+#define SAA7134_IRQ1_INTE_RA3_1 (1 << 25)
+#define SAA7134_IRQ1_INTE_RA3_0 (1 << 24)
+#define SAA7134_IRQ1_INTE_RA2_3 (1 << 19)
+#define SAA7134_IRQ1_INTE_RA2_2 (1 << 18)
+#define SAA7134_IRQ1_INTE_RA2_1 (1 << 17)
+#define SAA7134_IRQ1_INTE_RA2_0 (1 << 16)
+#define SAA7134_IRQ1_INTE_RA1_3 (1 << 11)
+#define SAA7134_IRQ1_INTE_RA1_2 (1 << 10)
+#define SAA7134_IRQ1_INTE_RA1_1 (1 << 9)
+#define SAA7134_IRQ1_INTE_RA1_0 (1 << 8)
+#define SAA7134_IRQ1_INTE_RA0_7 (1 << 7)
+#define SAA7134_IRQ1_INTE_RA0_6 (1 << 6)
+#define SAA7134_IRQ1_INTE_RA0_5 (1 << 5)
+#define SAA7134_IRQ1_INTE_RA0_4 (1 << 4)
+#define SAA7134_IRQ1_INTE_RA0_3 (1 << 3)
+#define SAA7134_IRQ1_INTE_RA0_2 (1 << 2)
+#define SAA7134_IRQ1_INTE_RA0_1 (1 << 1)
+#define SAA7134_IRQ1_INTE_RA0_0 (1 << 0)
+
+#define SAA7134_IRQ2 (0x2c8 >> 2)
+#define SAA7134_IRQ2_INTE_GPIO23_N (1 << 17) /* negative edge */
+#define SAA7134_IRQ2_INTE_GPIO23_P (1 << 16) /* positive edge */
+#define SAA7134_IRQ2_INTE_GPIO22_N (1 << 15) /* negative edge */
+#define SAA7134_IRQ2_INTE_GPIO22_P (1 << 14) /* positive edge */
+#define SAA7134_IRQ2_INTE_GPIO18_N (1 << 13) /* negative edge */
+#define SAA7134_IRQ2_INTE_GPIO18_P (1 << 12) /* positive edge */
+#define SAA7134_IRQ2_INTE_GPIO16_N (1 << 11) /* negative edge */
+#define SAA7134_IRQ2_INTE_GPIO16_P (1 << 10) /* positive edge */
+#define SAA7134_IRQ2_INTE_SC2 (1 << 9)
+#define SAA7134_IRQ2_INTE_SC1 (1 << 8)
+#define SAA7134_IRQ2_INTE_SC0 (1 << 7)
+#define SAA7134_IRQ2_INTE_DEC4 (1 << 6)
+#define SAA7134_IRQ2_INTE_DEC3 (1 << 5)
+#define SAA7134_IRQ2_INTE_DEC2 (1 << 4)
+#define SAA7134_IRQ2_INTE_DEC1 (1 << 3)
+#define SAA7134_IRQ2_INTE_DEC0 (1 << 2)
+#define SAA7134_IRQ2_INTE_PE (1 << 1)
+#define SAA7134_IRQ2_INTE_AR (1 << 0)
+
+#define SAA7134_IRQ_REPORT (0x2cc >> 2)
+#define SAA7134_IRQ_REPORT_GPIO23 (1 << 17)
+#define SAA7134_IRQ_REPORT_GPIO22 (1 << 16)
+#define SAA7134_IRQ_REPORT_GPIO18 (1 << 15)
+#define SAA7134_IRQ_REPORT_GPIO16 (1 << 14)
+#define SAA7134_IRQ_REPORT_LOAD_ERR (1 << 13)
+#define SAA7134_IRQ_REPORT_CONF_ERR (1 << 12)
+#define SAA7134_IRQ_REPORT_TRIG_ERR (1 << 11)
+#define SAA7134_IRQ_REPORT_MMC (1 << 10)
+#define SAA7134_IRQ_REPORT_FIDT (1 << 9)
+#define SAA7134_IRQ_REPORT_INTL (1 << 8)
+#define SAA7134_IRQ_REPORT_RDCAP (1 << 7)
+#define SAA7134_IRQ_REPORT_PWR_ON (1 << 6)
+#define SAA7134_IRQ_REPORT_PE (1 << 5)
+#define SAA7134_IRQ_REPORT_AR (1 << 4)
+#define SAA7134_IRQ_REPORT_DONE_RA3 (1 << 3)
+#define SAA7134_IRQ_REPORT_DONE_RA2 (1 << 2)
+#define SAA7134_IRQ_REPORT_DONE_RA1 (1 << 1)
+#define SAA7134_IRQ_REPORT_DONE_RA0 (1 << 0)
+#define SAA7134_IRQ_STATUS (0x2d0 >> 2)
+
+
+/* ------------------------------------------------------------------ */
+/*
+ * registers -- 8 bit
+ */
+
+/* video decoder */
+#define SAA7134_INCR_DELAY 0x101
+#define SAA7134_ANALOG_IN_CTRL1 0x102
+#define SAA7134_ANALOG_IN_CTRL2 0x103
+#define SAA7134_ANALOG_IN_CTRL3 0x104
+#define SAA7134_ANALOG_IN_CTRL4 0x105
+#define SAA7134_HSYNC_START 0x106
+#define SAA7134_HSYNC_STOP 0x107
+#define SAA7134_SYNC_CTRL 0x108
+#define SAA7134_LUMA_CTRL 0x109
+#define SAA7134_DEC_LUMA_BRIGHT 0x10a
+#define SAA7134_DEC_LUMA_CONTRAST 0x10b
+#define SAA7134_DEC_CHROMA_SATURATION 0x10c
+#define SAA7134_DEC_CHROMA_HUE 0x10d
+#define SAA7134_CHROMA_CTRL1 0x10e
+#define SAA7134_CHROMA_GAIN 0x10f
+#define SAA7134_CHROMA_CTRL2 0x110
+#define SAA7134_MODE_DELAY_CTRL 0x111
+
+#define SAA7134_ANALOG_ADC 0x114
+#define SAA7134_VGATE_START 0x115
+#define SAA7134_VGATE_STOP 0x116
+#define SAA7134_MISC_VGATE_MSB 0x117
+#define SAA7134_RAW_DATA_GAIN 0x118
+#define SAA7134_RAW_DATA_OFFSET 0x119
+#define SAA7134_STATUS_VIDEO1 0x11e
+#define SAA7134_STATUS_VIDEO2 0x11f
+
+/* video scaler */
+#define SAA7134_SOURCE_TIMING1 0x000
+#define SAA7134_SOURCE_TIMING2 0x001
+#define SAA7134_REGION_ENABLE 0x004
+#define SAA7134_SCALER_STATUS0 0x006
+#define SAA7134_SCALER_STATUS1 0x007
+#define SAA7134_START_GREEN 0x00c
+#define SAA7134_START_BLUE 0x00d
+#define SAA7134_START_RED 0x00e
+#define SAA7134_GREEN_PATH(x) (0x010 +x)
+#define SAA7134_BLUE_PATH(x) (0x020 +x)
+#define SAA7134_RED_PATH(x) (0x030 +x)
+
+#define TASK_A 0x040
+#define TASK_B 0x080
+#define SAA7134_TASK_CONDITIONS(t) (0x000 +t)
+#define SAA7134_FIELD_HANDLING(t) (0x001 +t)
+#define SAA7134_DATA_PATH(t) (0x002 +t)
+#define SAA7134_VBI_H_START1(t) (0x004 +t)
+#define SAA7134_VBI_H_START2(t) (0x005 +t)
+#define SAA7134_VBI_H_STOP1(t) (0x006 +t)
+#define SAA7134_VBI_H_STOP2(t) (0x007 +t)
+#define SAA7134_VBI_V_START1(t) (0x008 +t)
+#define SAA7134_VBI_V_START2(t) (0x009 +t)
+#define SAA7134_VBI_V_STOP1(t) (0x00a +t)
+#define SAA7134_VBI_V_STOP2(t) (0x00b +t)
+#define SAA7134_VBI_H_LEN1(t) (0x00c +t)
+#define SAA7134_VBI_H_LEN2(t) (0x00d +t)
+#define SAA7134_VBI_V_LEN1(t) (0x00e +t)
+#define SAA7134_VBI_V_LEN2(t) (0x00f +t)
+
+#define SAA7134_VIDEO_H_START1(t) (0x014 +t)
+#define SAA7134_VIDEO_H_START2(t) (0x015 +t)
+#define SAA7134_VIDEO_H_STOP1(t) (0x016 +t)
+#define SAA7134_VIDEO_H_STOP2(t) (0x017 +t)
+#define SAA7134_VIDEO_V_START1(t) (0x018 +t)
+#define SAA7134_VIDEO_V_START2(t) (0x019 +t)
+#define SAA7134_VIDEO_V_STOP1(t) (0x01a +t)
+#define SAA7134_VIDEO_V_STOP2(t) (0x01b +t)
+#define SAA7134_VIDEO_PIXELS1(t) (0x01c +t)
+#define SAA7134_VIDEO_PIXELS2(t) (0x01d +t)
+#define SAA7134_VIDEO_LINES1(t) (0x01e +t)
+#define SAA7134_VIDEO_LINES2(t) (0x01f +t)
+
+#define SAA7134_H_PRESCALE(t) (0x020 +t)
+#define SAA7134_ACC_LENGTH(t) (0x021 +t)
+#define SAA7134_LEVEL_CTRL(t) (0x022 +t)
+#define SAA7134_FIR_PREFILTER_CTRL(t) (0x023 +t)
+#define SAA7134_LUMA_BRIGHT(t) (0x024 +t)
+#define SAA7134_LUMA_CONTRAST(t) (0x025 +t)
+#define SAA7134_CHROMA_SATURATION(t) (0x026 +t)
+#define SAA7134_VBI_H_SCALE_INC1(t) (0x028 +t)
+#define SAA7134_VBI_H_SCALE_INC2(t) (0x029 +t)
+#define SAA7134_VBI_PHASE_OFFSET_LUMA(t) (0x02a +t)
+#define SAA7134_VBI_PHASE_OFFSET_CHROMA(t) (0x02b +t)
+#define SAA7134_H_SCALE_INC1(t) (0x02c +t)
+#define SAA7134_H_SCALE_INC2(t) (0x02d +t)
+#define SAA7134_H_PHASE_OFF_LUMA(t) (0x02e +t)
+#define SAA7134_H_PHASE_OFF_CHROMA(t) (0x02f +t)
+#define SAA7134_V_SCALE_RATIO1(t) (0x030 +t)
+#define SAA7134_V_SCALE_RATIO2(t) (0x031 +t)
+#define SAA7134_V_FILTER(t) (0x032 +t)
+#define SAA7134_V_PHASE_OFFSET0(t) (0x034 +t)
+#define SAA7134_V_PHASE_OFFSET1(t) (0x035 +t)
+#define SAA7134_V_PHASE_OFFSET2(t) (0x036 +t)
+#define SAA7134_V_PHASE_OFFSET3(t) (0x037 +t)
+
+/* clipping & dma */
+#define SAA7134_OFMT_VIDEO_A 0x300
+#define SAA7134_OFMT_DATA_A 0x301
+#define SAA7134_OFMT_VIDEO_B 0x302
+#define SAA7134_OFMT_DATA_B 0x303
+#define SAA7134_ALPHA_NOCLIP 0x304
+#define SAA7134_ALPHA_CLIP 0x305
+#define SAA7134_UV_PIXEL 0x308
+#define SAA7134_CLIP_RED 0x309
+#define SAA7134_CLIP_GREEN 0x30a
+#define SAA7134_CLIP_BLUE 0x30b
+
+/* i2c bus */
+#define SAA7134_I2C_ATTR_STATUS 0x180
+#define SAA7134_I2C_DATA 0x181
+#define SAA7134_I2C_CLOCK_SELECT 0x182
+#define SAA7134_I2C_TIMER 0x183
+
+/* audio */
+#define SAA7134_NICAM_ADD_DATA1 0x140
+#define SAA7134_NICAM_ADD_DATA2 0x141
+#define SAA7134_NICAM_STATUS 0x142
+#define SAA7134_AUDIO_STATUS 0x143
+#define SAA7134_NICAM_ERROR_COUNT 0x144
+#define SAA7134_IDENT_SIF 0x145
+#define SAA7134_LEVEL_READOUT1 0x146
+#define SAA7134_LEVEL_READOUT2 0x147
+#define SAA7134_NICAM_ERROR_LOW 0x148
+#define SAA7134_NICAM_ERROR_HIGH 0x149
+#define SAA7134_DCXO_IDENT_CTRL 0x14a
+#define SAA7134_DEMODULATOR 0x14b
+#define SAA7134_AGC_GAIN_SELECT 0x14c
+#define SAA7134_CARRIER1_FREQ0 0x150
+#define SAA7134_CARRIER1_FREQ1 0x151
+#define SAA7134_CARRIER1_FREQ2 0x152
+#define SAA7134_CARRIER2_FREQ0 0x154
+#define SAA7134_CARRIER2_FREQ1 0x155
+#define SAA7134_CARRIER2_FREQ2 0x156
+#define SAA7134_NUM_SAMPLES0 0x158
+#define SAA7134_NUM_SAMPLES1 0x159
+#define SAA7134_NUM_SAMPLES2 0x15a
+#define SAA7134_AUDIO_FORMAT_CTRL 0x15b
+#define SAA7134_MONITOR_SELECT 0x160
+#define SAA7134_FM_DEEMPHASIS 0x161
+#define SAA7134_FM_DEMATRIX 0x162
+#define SAA7134_CHANNEL1_LEVEL 0x163
+#define SAA7134_CHANNEL2_LEVEL 0x164
+#define SAA7134_NICAM_CONFIG 0x165
+#define SAA7134_NICAM_LEVEL_ADJUST 0x166
+#define SAA7134_STEREO_DAC_OUTPUT_SELECT 0x167
+#define SAA7134_I2S_OUTPUT_FORMAT 0x168
+#define SAA7134_I2S_OUTPUT_SELECT 0x169
+#define SAA7134_I2S_OUTPUT_LEVEL 0x16a
+#define SAA7134_DSP_OUTPUT_SELECT 0x16b
+#define SAA7134_AUDIO_MUTE_CTRL 0x16c
+#define SAA7134_SIF_SAMPLE_FREQ 0x16d
+#define SAA7134_ANALOG_IO_SELECT 0x16e
+#define SAA7134_AUDIO_CLOCK0 0x170
+#define SAA7134_AUDIO_CLOCK1 0x171
+#define SAA7134_AUDIO_CLOCK2 0x172
+#define SAA7134_AUDIO_PLL_CTRL 0x173
+#define SAA7134_AUDIO_CLOCKS_PER_FIELD0 0x174
+#define SAA7134_AUDIO_CLOCKS_PER_FIELD1 0x175
+#define SAA7134_AUDIO_CLOCKS_PER_FIELD2 0x176
+
+/* video port output */
+#define SAA7134_VIDEO_PORT_CTRL0 0x190
+#define SAA7134_VIDEO_PORT_CTRL1 0x191
+#define SAA7134_VIDEO_PORT_CTRL2 0x192
+#define SAA7134_VIDEO_PORT_CTRL3 0x193
+#define SAA7134_VIDEO_PORT_CTRL4 0x194
+#define SAA7134_VIDEO_PORT_CTRL5 0x195
+#define SAA7134_VIDEO_PORT_CTRL6 0x196
+#define SAA7134_VIDEO_PORT_CTRL7 0x197
+#define SAA7134_VIDEO_PORT_CTRL8 0x198
+
+/* transport stream interface */
+#define SAA7134_TS_PARALLEL 0x1a0
+#define SAA7134_TS_PARALLEL_SERIAL 0x1a1
+#define SAA7134_TS_SERIAL0 0x1a2
+#define SAA7134_TS_SERIAL1 0x1a3
+#define SAA7134_TS_DMA0 0x1a4
+#define SAA7134_TS_DMA1 0x1a5
+#define SAA7134_TS_DMA2 0x1a6
+
+/* GPIO Controls */
+#define SAA7134_GPIO_GPRESCAN 0x80
+#define SAA7134_GPIO_27_25 0x0E
+
+#define SAA7134_GPIO_GPMODE0 0x1B0
+#define SAA7134_GPIO_GPMODE1 0x1B1
+#define SAA7134_GPIO_GPMODE2 0x1B2
+#define SAA7134_GPIO_GPMODE3 0x1B3
+#define SAA7134_GPIO_GPSTATUS0 0x1B4
+#define SAA7134_GPIO_GPSTATUS1 0x1B5
+#define SAA7134_GPIO_GPSTATUS2 0x1B6
+#define SAA7134_GPIO_GPSTATUS3 0x1B7
+
+/* I2S output */
+#define SAA7134_I2S_AUDIO_OUTPUT 0x1c0
+
+/* test modes */
+#define SAA7134_SPECIAL_MODE 0x1d0
+#define SAA7134_PRODUCTION_TEST_MODE 0x1d1
+
+/* audio -- saa7133 + saa7135 only */
+#define SAA7135_DSP_RWSTATE 0x580
+#define SAA7135_DSP_RWSTATE_ERR (1 << 3)
+#define SAA7135_DSP_RWSTATE_IDA (1 << 2)
+#define SAA7135_DSP_RWSTATE_RDB (1 << 1)
+#define SAA7135_DSP_RWSTATE_WRR (1 << 0)
+
+#define SAA7135_DSP_RWCLEAR 0x586
+#define SAA7135_DSP_RWCLEAR_RERR 1
+
+#define SAA7133_I2S_AUDIO_CONTROL 0x591
+/* ------------------------------------------------------------------ */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
+
diff --git a/drivers/media/video/saa7134/saa7134-ts.c b/drivers/media/video/saa7134/saa7134-ts.c
new file mode 100644
index 00000000..2e3f4b41
--- /dev/null
+++ b/drivers/media/video/saa7134/saa7134-ts.c
@@ -0,0 +1,327 @@
+/*
+ *
+ * device driver for philips saa7134 based TV cards
+ * video4linux video interface
+ *
+ * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int ts_debug;
+module_param(ts_debug, int, 0644);
+MODULE_PARM_DESC(ts_debug,"enable debug messages [ts]");
+
+#define dprintk(fmt, arg...) if (ts_debug) \
+ printk(KERN_DEBUG "%s/ts: " fmt, dev->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+
+static int buffer_activate(struct saa7134_dev *dev,
+ struct saa7134_buf *buf,
+ struct saa7134_buf *next)
+{
+
+ dprintk("buffer_activate [%p]",buf);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->top_seen = 0;
+
+ if (NULL == next)
+ next = buf;
+ if (V4L2_FIELD_TOP == buf->vb.field) {
+ dprintk("- [top] buf=%p next=%p\n",buf,next);
+ saa_writel(SAA7134_RS_BA1(5),saa7134_buffer_base(buf));
+ saa_writel(SAA7134_RS_BA2(5),saa7134_buffer_base(next));
+ } else {
+ dprintk("- [bottom] buf=%p next=%p\n",buf,next);
+ saa_writel(SAA7134_RS_BA1(5),saa7134_buffer_base(next));
+ saa_writel(SAA7134_RS_BA2(5),saa7134_buffer_base(buf));
+ }
+
+ /* start DMA */
+ saa7134_set_dmabits(dev);
+
+ mod_timer(&dev->ts_q.timeout, jiffies+TS_BUFFER_TIMEOUT);
+
+ if (!dev->ts_started)
+ saa7134_ts_start(dev);
+
+ return 0;
+}
+
+static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct saa7134_dev *dev = q->priv_data;
+ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+ unsigned int lines, llength, size;
+ int err;
+
+ dprintk("buffer_prepare [%p,%s]\n",buf,v4l2_field_names[field]);
+
+ llength = TS_PACKET_SIZE;
+ lines = dev->ts.nr_packets;
+
+ size = lines * llength;
+ if (0 != buf->vb.baddr && buf->vb.bsize < size)
+ return -EINVAL;
+
+ if (buf->vb.size != size) {
+ saa7134_dma_free(q,buf);
+ }
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+
+ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+
+ dprintk("buffer_prepare: needs_init\n");
+
+ buf->vb.width = llength;
+ buf->vb.height = lines;
+ buf->vb.size = size;
+ buf->pt = &dev->ts.pt_ts;
+
+ err = videobuf_iolock(q,&buf->vb,NULL);
+ if (err)
+ goto oops;
+ err = saa7134_pgtable_build(dev->pci,buf->pt,
+ dma->sglist,
+ dma->sglen,
+ saa7134_buffer_startpage(buf));
+ if (err)
+ goto oops;
+ }
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ buf->activate = buffer_activate;
+ buf->vb.field = field;
+ return 0;
+
+ oops:
+ saa7134_dma_free(q,buf);
+ return err;
+}
+
+static int
+buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+ struct saa7134_dev *dev = q->priv_data;
+
+ *size = TS_PACKET_SIZE * dev->ts.nr_packets;
+ if (0 == *count)
+ *count = dev->ts.nr_bufs;
+ *count = saa7134_buffer_count(*size,*count);
+
+ return 0;
+}
+
+static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct saa7134_dev *dev = q->priv_data;
+ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+
+ saa7134_buffer_queue(dev,&dev->ts_q,buf);
+}
+
+static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+ struct saa7134_dev *dev = q->priv_data;
+
+ if (dev->ts_started)
+ saa7134_ts_stop(dev);
+
+ saa7134_dma_free(q,buf);
+}
+
+struct videobuf_queue_ops saa7134_ts_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+EXPORT_SYMBOL_GPL(saa7134_ts_qops);
+
+/* ----------------------------------------------------------- */
+/* exported stuff */
+
+static unsigned int tsbufs = 8;
+module_param(tsbufs, int, 0444);
+MODULE_PARM_DESC(tsbufs, "number of ts buffers for read/write IO, range 2-32");
+
+static unsigned int ts_nr_packets = 64;
+module_param(ts_nr_packets, int, 0444);
+MODULE_PARM_DESC(ts_nr_packets,"size of a ts buffers (in ts packets)");
+
+int saa7134_ts_init_hw(struct saa7134_dev *dev)
+{
+ /* deactivate TS softreset */
+ saa_writeb(SAA7134_TS_SERIAL1, 0x00);
+ /* TSSOP high active, TSVAL high active, TSLOCK ignored */
+ saa_writeb(SAA7134_TS_PARALLEL, 0x6c);
+ saa_writeb(SAA7134_TS_PARALLEL_SERIAL, (TS_PACKET_SIZE-1));
+ saa_writeb(SAA7134_TS_DMA0, ((dev->ts.nr_packets-1)&0xff));
+ saa_writeb(SAA7134_TS_DMA1, (((dev->ts.nr_packets-1)>>8)&0xff));
+ /* TSNOPIT=0, TSCOLAP=0 */
+ saa_writeb(SAA7134_TS_DMA2,
+ ((((dev->ts.nr_packets-1)>>16)&0x3f) | 0x00));
+
+ return 0;
+}
+
+int saa7134_ts_init1(struct saa7134_dev *dev)
+{
+ /* sanitycheck insmod options */
+ if (tsbufs < 2)
+ tsbufs = 2;
+ if (tsbufs > VIDEO_MAX_FRAME)
+ tsbufs = VIDEO_MAX_FRAME;
+ if (ts_nr_packets < 4)
+ ts_nr_packets = 4;
+ if (ts_nr_packets > 312)
+ ts_nr_packets = 312;
+ dev->ts.nr_bufs = tsbufs;
+ dev->ts.nr_packets = ts_nr_packets;
+
+ INIT_LIST_HEAD(&dev->ts_q.queue);
+ init_timer(&dev->ts_q.timeout);
+ dev->ts_q.timeout.function = saa7134_buffer_timeout;
+ dev->ts_q.timeout.data = (unsigned long)(&dev->ts_q);
+ dev->ts_q.dev = dev;
+ dev->ts_q.need_two = 1;
+ dev->ts_started = 0;
+ saa7134_pgtable_alloc(dev->pci,&dev->ts.pt_ts);
+
+ /* init TS hw */
+ saa7134_ts_init_hw(dev);
+
+ return 0;
+}
+
+/* Function for stop TS */
+int saa7134_ts_stop(struct saa7134_dev *dev)
+{
+ dprintk("TS stop\n");
+
+ BUG_ON(!dev->ts_started);
+
+ /* Stop TS stream */
+ switch (saa7134_boards[dev->board].ts_type) {
+ case SAA7134_MPEG_TS_PARALLEL:
+ saa_writeb(SAA7134_TS_PARALLEL, 0x6c);
+ dev->ts_started = 0;
+ break;
+ case SAA7134_MPEG_TS_SERIAL:
+ saa_writeb(SAA7134_TS_SERIAL0, 0x40);
+ dev->ts_started = 0;
+ break;
+ }
+ return 0;
+}
+
+/* Function for start TS */
+int saa7134_ts_start(struct saa7134_dev *dev)
+{
+ dprintk("TS start\n");
+
+ BUG_ON(dev->ts_started);
+
+ /* dma: setup channel 5 (= TS) */
+ saa_writeb(SAA7134_TS_DMA0, (dev->ts.nr_packets - 1) & 0xff);
+ saa_writeb(SAA7134_TS_DMA1,
+ ((dev->ts.nr_packets - 1) >> 8) & 0xff);
+ /* TSNOPIT=0, TSCOLAP=0 */
+ saa_writeb(SAA7134_TS_DMA2,
+ (((dev->ts.nr_packets - 1) >> 16) & 0x3f) | 0x00);
+ saa_writel(SAA7134_RS_PITCH(5), TS_PACKET_SIZE);
+ saa_writel(SAA7134_RS_CONTROL(5), SAA7134_RS_CONTROL_BURST_16 |
+ SAA7134_RS_CONTROL_ME |
+ (dev->ts.pt_ts.dma >> 12));
+
+ /* reset hardware TS buffers */
+ saa_writeb(SAA7134_TS_SERIAL1, 0x00);
+ saa_writeb(SAA7134_TS_SERIAL1, 0x03);
+ saa_writeb(SAA7134_TS_SERIAL1, 0x00);
+ saa_writeb(SAA7134_TS_SERIAL1, 0x01);
+
+ /* TS clock non-inverted */
+ saa_writeb(SAA7134_TS_SERIAL1, 0x00);
+
+ /* Start TS stream */
+ switch (saa7134_boards[dev->board].ts_type) {
+ case SAA7134_MPEG_TS_PARALLEL:
+ saa_writeb(SAA7134_TS_SERIAL0, 0x40);
+ saa_writeb(SAA7134_TS_PARALLEL, 0xec |
+ (saa7134_boards[dev->board].ts_force_val << 4));
+ break;
+ case SAA7134_MPEG_TS_SERIAL:
+ saa_writeb(SAA7134_TS_SERIAL0, 0xd8);
+ saa_writeb(SAA7134_TS_PARALLEL, 0x6c |
+ (saa7134_boards[dev->board].ts_force_val << 4));
+ saa_writeb(SAA7134_TS_PARALLEL_SERIAL, 0xbc);
+ saa_writeb(SAA7134_TS_SERIAL1, 0x02);
+ break;
+ }
+
+ dev->ts_started = 1;
+
+ return 0;
+}
+
+int saa7134_ts_fini(struct saa7134_dev *dev)
+{
+ saa7134_pgtable_free(dev->pci,&dev->ts.pt_ts);
+ return 0;
+}
+
+void saa7134_irq_ts_done(struct saa7134_dev *dev, unsigned long status)
+{
+ enum v4l2_field field;
+
+ spin_lock(&dev->slock);
+ if (dev->ts_q.curr) {
+ field = dev->ts_q.curr->vb.field;
+ if (field == V4L2_FIELD_TOP) {
+ if ((status & 0x100000) != 0x000000)
+ goto done;
+ } else {
+ if ((status & 0x100000) != 0x100000)
+ goto done;
+ }
+ saa7134_buffer_finish(dev,&dev->ts_q,VIDEOBUF_DONE);
+ }
+ saa7134_buffer_next(dev,&dev->ts_q);
+
+ done:
+ spin_unlock(&dev->slock);
+}
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/saa7134/saa7134-tvaudio.c b/drivers/media/video/saa7134/saa7134-tvaudio.c
new file mode 100644
index 00000000..b7a99bee
--- /dev/null
+++ b/drivers/media/video/saa7134/saa7134-tvaudio.c
@@ -0,0 +1,1087 @@
+/*
+ *
+ * device driver for philips saa7134 based TV cards
+ * tv audio decoder (fm stereo, nicam, ...)
+ *
+ * (c) 2001-03 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/delay.h>
+#include <linux/freezer.h>
+#include <asm/div64.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int audio_debug;
+module_param(audio_debug, int, 0644);
+MODULE_PARM_DESC(audio_debug,"enable debug messages [tv audio]");
+
+static unsigned int audio_ddep;
+module_param(audio_ddep, int, 0644);
+MODULE_PARM_DESC(audio_ddep,"audio ddep overwrite");
+
+static int audio_clock_override = UNSET;
+module_param(audio_clock_override, int, 0644);
+
+static int audio_clock_tweak;
+module_param(audio_clock_tweak, int, 0644);
+MODULE_PARM_DESC(audio_clock_tweak, "Audio clock tick fine tuning for cards with audio crystal that's slightly off (range [-1024 .. 1024])");
+
+#define dprintk(fmt, arg...) if (audio_debug) \
+ printk(KERN_DEBUG "%s/audio: " fmt, dev->name , ## arg)
+#define d2printk(fmt, arg...) if (audio_debug > 1) \
+ printk(KERN_DEBUG "%s/audio: " fmt, dev->name, ## arg)
+
+#define print_regb(reg) printk("%s: reg 0x%03x [%-16s]: 0x%02x\n", \
+ dev->name,(SAA7134_##reg),(#reg),saa_readb((SAA7134_##reg)))
+
+/* msecs */
+#define SCAN_INITIAL_DELAY 1000
+#define SCAN_SAMPLE_DELAY 200
+#define SCAN_SUBCARRIER_DELAY 2000
+
+/* ------------------------------------------------------------------ */
+/* saa7134 code */
+
+static struct mainscan {
+ char *name;
+ v4l2_std_id std;
+ int carr;
+} mainscan[] = {
+ {
+ .name = "MN",
+ .std = V4L2_STD_MN,
+ .carr = 4500,
+ },{
+ .name = "BGH",
+ .std = V4L2_STD_B | V4L2_STD_GH,
+ .carr = 5500,
+ },{
+ .name = "I",
+ .std = V4L2_STD_PAL_I,
+ .carr = 6000,
+ },{
+ .name = "DKL",
+ .std = V4L2_STD_DK | V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC,
+ .carr = 6500,
+ }
+};
+
+static struct saa7134_tvaudio tvaudio[] = {
+ {
+ .name = "PAL-B/G FM-stereo",
+ .std = V4L2_STD_PAL_BG,
+ .mode = TVAUDIO_FM_BG_STEREO,
+ .carr1 = 5500,
+ .carr2 = 5742,
+ },{
+ .name = "PAL-D/K1 FM-stereo",
+ .std = V4L2_STD_PAL_DK,
+ .carr1 = 6500,
+ .carr2 = 6258,
+ .mode = TVAUDIO_FM_BG_STEREO,
+ },{
+ .name = "PAL-D/K2 FM-stereo",
+ .std = V4L2_STD_PAL_DK,
+ .carr1 = 6500,
+ .carr2 = 6742,
+ .mode = TVAUDIO_FM_BG_STEREO,
+ },{
+ .name = "PAL-D/K3 FM-stereo",
+ .std = V4L2_STD_PAL_DK,
+ .carr1 = 6500,
+ .carr2 = 5742,
+ .mode = TVAUDIO_FM_BG_STEREO,
+ },{
+ .name = "PAL-B/G NICAM",
+ .std = V4L2_STD_PAL_BG,
+ .carr1 = 5500,
+ .carr2 = 5850,
+ .mode = TVAUDIO_NICAM_FM,
+ },{
+ .name = "PAL-I NICAM",
+ .std = V4L2_STD_PAL_I,
+ .carr1 = 6000,
+ .carr2 = 6552,
+ .mode = TVAUDIO_NICAM_FM,
+ },{
+ .name = "PAL-D/K NICAM",
+ .std = V4L2_STD_PAL_DK,
+ .carr1 = 6500,
+ .carr2 = 5850,
+ .mode = TVAUDIO_NICAM_FM,
+ },{
+ .name = "SECAM-L NICAM",
+ .std = V4L2_STD_SECAM_L,
+ .carr1 = 6500,
+ .carr2 = 5850,
+ .mode = TVAUDIO_NICAM_AM,
+ },{
+ .name = "SECAM-D/K NICAM",
+ .std = V4L2_STD_SECAM_DK,
+ .carr1 = 6500,
+ .carr2 = 5850,
+ .mode = TVAUDIO_NICAM_FM,
+ },{
+ .name = "NTSC-A2 FM-stereo",
+ .std = V4L2_STD_NTSC,
+ .carr1 = 4500,
+ .carr2 = 4724,
+ .mode = TVAUDIO_FM_K_STEREO,
+ },{
+ .name = "NTSC-M",
+ .std = V4L2_STD_NTSC,
+ .carr1 = 4500,
+ .carr2 = -1,
+ .mode = TVAUDIO_FM_MONO,
+ }
+};
+#define TVAUDIO ARRAY_SIZE(tvaudio)
+
+/* ------------------------------------------------------------------ */
+
+static u32 tvaudio_carr2reg(u32 carrier)
+{
+ u64 a = carrier;
+
+ a <<= 24;
+ do_div(a,12288);
+ return a;
+}
+
+static void tvaudio_setcarrier(struct saa7134_dev *dev,
+ int primary, int secondary)
+{
+ if (-1 == secondary)
+ secondary = primary;
+ saa_writel(SAA7134_CARRIER1_FREQ0 >> 2, tvaudio_carr2reg(primary));
+ saa_writel(SAA7134_CARRIER2_FREQ0 >> 2, tvaudio_carr2reg(secondary));
+}
+
+#define SAA7134_MUTE_MASK 0xbb
+#define SAA7134_MUTE_ANALOG 0x04
+#define SAA7134_MUTE_I2S 0x40
+
+static void mute_input_7134(struct saa7134_dev *dev)
+{
+ unsigned int mute;
+ struct saa7134_input *in;
+ int ausel=0, ics=0, ocs=0;
+ int mask;
+
+ /* look what is to do ... */
+ in = dev->input;
+ mute = (dev->ctl_mute ||
+ (dev->automute && (&card(dev).radio) != in));
+ if (card(dev).mute.name) {
+ /*
+ * 7130 - we'll mute using some unconnected audio input
+ * 7134 - we'll probably should switch external mux with gpio
+ */
+ if (mute)
+ in = &card(dev).mute;
+ }
+
+ if (dev->hw_mute == mute &&
+ dev->hw_input == in && !dev->insuspend) {
+ dprintk("mute/input: nothing to do [mute=%d,input=%s]\n",
+ mute,in->name);
+ return;
+ }
+
+ dprintk("ctl_mute=%d automute=%d input=%s => mute=%d input=%s\n",
+ dev->ctl_mute,dev->automute,dev->input->name,mute,in->name);
+ dev->hw_mute = mute;
+ dev->hw_input = in;
+
+ if (PCI_DEVICE_ID_PHILIPS_SAA7134 == dev->pci->device)
+ /* 7134 mute */
+ saa_writeb(SAA7134_AUDIO_MUTE_CTRL, mute ?
+ SAA7134_MUTE_MASK |
+ SAA7134_MUTE_ANALOG |
+ SAA7134_MUTE_I2S :
+ SAA7134_MUTE_MASK);
+
+ /* switch internal audio mux */
+ switch (in->amux) {
+ case TV: ausel=0xc0; ics=0x00; ocs=0x02; break;
+ case LINE1: ausel=0x80; ics=0x00; ocs=0x00; break;
+ case LINE2: ausel=0x80; ics=0x08; ocs=0x01; break;
+ case LINE2_LEFT: ausel=0x80; ics=0x08; ocs=0x05; break;
+ }
+ saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, 0xc0, ausel);
+ saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x08, ics);
+ saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x07, ocs);
+ // for oss, we need to change the clock configuration
+ if (in->amux == TV)
+ saa_andorb(SAA7134_SIF_SAMPLE_FREQ, 0x03, 0x00);
+ else
+ saa_andorb(SAA7134_SIF_SAMPLE_FREQ, 0x03, 0x01);
+
+ /* switch gpio-connected external audio mux */
+ if (0 == card(dev).gpiomask)
+ return;
+
+ mask = card(dev).gpiomask;
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, mask, mask);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, mask, in->gpio);
+ saa7134_track_gpio(dev,in->name);
+}
+
+static void tvaudio_setmode(struct saa7134_dev *dev,
+ struct saa7134_tvaudio *audio,
+ char *note)
+{
+ int acpf, tweak = 0;
+
+ if (dev->tvnorm->id == V4L2_STD_NTSC) {
+ acpf = 0x19066;
+ } else {
+ acpf = 0x1e000;
+ }
+ if (audio_clock_tweak > -1024 && audio_clock_tweak < 1024)
+ tweak = audio_clock_tweak;
+
+ if (note)
+ dprintk("tvaudio_setmode: %s %s [%d.%03d/%d.%03d MHz] acpf=%d%+d\n",
+ note,audio->name,
+ audio->carr1 / 1000, audio->carr1 % 1000,
+ audio->carr2 / 1000, audio->carr2 % 1000,
+ acpf, tweak);
+
+ acpf += tweak;
+ saa_writeb(SAA7134_AUDIO_CLOCKS_PER_FIELD0, (acpf & 0x0000ff) >> 0);
+ saa_writeb(SAA7134_AUDIO_CLOCKS_PER_FIELD1, (acpf & 0x00ff00) >> 8);
+ saa_writeb(SAA7134_AUDIO_CLOCKS_PER_FIELD2, (acpf & 0x030000) >> 16);
+ tvaudio_setcarrier(dev,audio->carr1,audio->carr2);
+
+ switch (audio->mode) {
+ case TVAUDIO_FM_MONO:
+ case TVAUDIO_FM_BG_STEREO:
+ saa_writeb(SAA7134_DEMODULATOR, 0x00);
+ saa_writeb(SAA7134_DCXO_IDENT_CTRL, 0x00);
+ saa_writeb(SAA7134_FM_DEEMPHASIS, 0x22);
+ saa_writeb(SAA7134_FM_DEMATRIX, 0x80);
+ saa_writeb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0xa0);
+ break;
+ case TVAUDIO_FM_K_STEREO:
+ saa_writeb(SAA7134_DEMODULATOR, 0x00);
+ saa_writeb(SAA7134_DCXO_IDENT_CTRL, 0x01);
+ saa_writeb(SAA7134_FM_DEEMPHASIS, 0x22);
+ saa_writeb(SAA7134_FM_DEMATRIX, 0x80);
+ saa_writeb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0xa0);
+ break;
+ case TVAUDIO_NICAM_FM:
+ saa_writeb(SAA7134_DEMODULATOR, 0x10);
+ saa_writeb(SAA7134_DCXO_IDENT_CTRL, 0x00);
+ saa_writeb(SAA7134_FM_DEEMPHASIS, 0x44);
+ saa_writeb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0xa1);
+ saa_writeb(SAA7134_NICAM_CONFIG, 0x00);
+ break;
+ case TVAUDIO_NICAM_AM:
+ saa_writeb(SAA7134_DEMODULATOR, 0x12);
+ saa_writeb(SAA7134_DCXO_IDENT_CTRL, 0x00);
+ saa_writeb(SAA7134_FM_DEEMPHASIS, 0x44);
+ saa_writeb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0xa1);
+ saa_writeb(SAA7134_NICAM_CONFIG, 0x00);
+ break;
+ case TVAUDIO_FM_SAT_STEREO:
+ /* not implemented (yet) */
+ break;
+ }
+}
+
+static int tvaudio_sleep(struct saa7134_dev *dev, int timeout)
+{
+ if (dev->thread.scan1 == dev->thread.scan2 &&
+ !kthread_should_stop()) {
+ if (timeout < 0) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule();
+ } else {
+ schedule_timeout_interruptible
+ (msecs_to_jiffies(timeout));
+ }
+ }
+ return dev->thread.scan1 != dev->thread.scan2;
+}
+
+static int tvaudio_checkcarrier(struct saa7134_dev *dev, struct mainscan *scan)
+{
+ __s32 left,right,value;
+
+ if (!(dev->tvnorm->id & scan->std)) {
+ value = 0;
+ dprintk("skipping %d.%03d MHz [%4s]\n",
+ scan->carr / 1000, scan->carr % 1000, scan->name);
+ return 0;
+ }
+
+ if (audio_debug > 1) {
+ int i;
+ dprintk("debug %d:",scan->carr);
+ for (i = -150; i <= 150; i += 30) {
+ tvaudio_setcarrier(dev,scan->carr+i,scan->carr+i);
+ saa_readl(SAA7134_LEVEL_READOUT1 >> 2);
+ if (tvaudio_sleep(dev,SCAN_SAMPLE_DELAY))
+ return -1;
+ value = saa_readl(SAA7134_LEVEL_READOUT1 >> 2);
+ if (0 == i)
+ printk(" # %6d # ",value >> 16);
+ else
+ printk(" %6d",value >> 16);
+ }
+ printk("\n");
+ }
+
+ tvaudio_setcarrier(dev,scan->carr-90,scan->carr-90);
+ saa_readl(SAA7134_LEVEL_READOUT1 >> 2);
+ if (tvaudio_sleep(dev,SCAN_SAMPLE_DELAY))
+ return -1;
+ left = saa_readl(SAA7134_LEVEL_READOUT1 >> 2);
+
+ tvaudio_setcarrier(dev,scan->carr+90,scan->carr+90);
+ saa_readl(SAA7134_LEVEL_READOUT1 >> 2);
+ if (tvaudio_sleep(dev,SCAN_SAMPLE_DELAY))
+ return -1;
+ right = saa_readl(SAA7134_LEVEL_READOUT1 >> 2);
+
+ left >>= 16;
+ right >>= 16;
+ value = left > right ? left - right : right - left;
+ dprintk("scanning %d.%03d MHz [%4s] => dc is %5d [%d/%d]\n",
+ scan->carr / 1000, scan->carr % 1000,
+ scan->name, value, left, right);
+ return value;
+}
+
+
+static int tvaudio_getstereo(struct saa7134_dev *dev, struct saa7134_tvaudio *audio)
+{
+ __u32 idp, nicam, nicam_status;
+ int retval = -1;
+
+ switch (audio->mode) {
+ case TVAUDIO_FM_MONO:
+ return V4L2_TUNER_SUB_MONO;
+ case TVAUDIO_FM_K_STEREO:
+ case TVAUDIO_FM_BG_STEREO:
+ idp = (saa_readb(SAA7134_IDENT_SIF) & 0xe0) >> 5;
+ dprintk("getstereo: fm/stereo: idp=0x%x\n",idp);
+ if (0x03 == (idp & 0x03))
+ retval = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ else if (0x05 == (idp & 0x05))
+ retval = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ else if (0x01 == (idp & 0x01))
+ retval = V4L2_TUNER_SUB_MONO;
+ break;
+ case TVAUDIO_FM_SAT_STEREO:
+ /* not implemented (yet) */
+ break;
+ case TVAUDIO_NICAM_FM:
+ case TVAUDIO_NICAM_AM:
+ nicam = saa_readb(SAA7134_AUDIO_STATUS);
+ dprintk("getstereo: nicam=0x%x\n",nicam);
+ if (nicam & 0x1) {
+ nicam_status = saa_readb(SAA7134_NICAM_STATUS);
+ dprintk("getstereo: nicam_status=0x%x\n", nicam_status);
+
+ switch (nicam_status & 0x03) {
+ case 0x01:
+ retval = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ break;
+ case 0x02:
+ retval = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ break;
+ default:
+ retval = V4L2_TUNER_SUB_MONO;
+ }
+ } else {
+ /* No nicam detected */
+ }
+ break;
+ }
+ if (retval != -1)
+ dprintk("found audio subchannels:%s%s%s%s\n",
+ (retval & V4L2_TUNER_SUB_MONO) ? " mono" : "",
+ (retval & V4L2_TUNER_SUB_STEREO) ? " stereo" : "",
+ (retval & V4L2_TUNER_SUB_LANG1) ? " lang1" : "",
+ (retval & V4L2_TUNER_SUB_LANG2) ? " lang2" : "");
+ return retval;
+}
+
+static int tvaudio_setstereo(struct saa7134_dev *dev, struct saa7134_tvaudio *audio,
+ u32 mode)
+{
+ static char *name[] = {
+ [ V4L2_TUNER_MODE_MONO ] = "mono",
+ [ V4L2_TUNER_MODE_STEREO ] = "stereo",
+ [ V4L2_TUNER_MODE_LANG1 ] = "lang1",
+ [ V4L2_TUNER_MODE_LANG2 ] = "lang2",
+ [ V4L2_TUNER_MODE_LANG1_LANG2 ] = "lang1+lang2",
+ };
+ static u32 fm[] = {
+ [ V4L2_TUNER_MODE_MONO ] = 0x00, /* ch1 */
+ [ V4L2_TUNER_MODE_STEREO ] = 0x80, /* auto */
+ [ V4L2_TUNER_MODE_LANG1 ] = 0x00, /* ch1 */
+ [ V4L2_TUNER_MODE_LANG2 ] = 0x01, /* ch2 */
+ [ V4L2_TUNER_MODE_LANG1_LANG2 ] = 0x80, /* auto */
+ };
+ u32 reg;
+
+ switch (audio->mode) {
+ case TVAUDIO_FM_MONO:
+ /* nothing to do ... */
+ break;
+ case TVAUDIO_FM_K_STEREO:
+ case TVAUDIO_FM_BG_STEREO:
+ case TVAUDIO_NICAM_AM:
+ case TVAUDIO_NICAM_FM:
+ dprintk("setstereo [fm] => %s\n",
+ name[ mode % ARRAY_SIZE(name) ]);
+ reg = fm[ mode % ARRAY_SIZE(fm) ];
+ saa_writeb(SAA7134_FM_DEMATRIX, reg);
+ break;
+ case TVAUDIO_FM_SAT_STEREO:
+ /* Not implemented */
+ break;
+ }
+ return 0;
+}
+
+static int tvaudio_thread(void *data)
+{
+ struct saa7134_dev *dev = data;
+ int carr_vals[ARRAY_SIZE(mainscan)];
+ unsigned int i, audio, nscan;
+ int max1,max2,carrier,rx,mode,lastmode,default_carrier;
+
+ set_freezable();
+
+ for (;;) {
+ tvaudio_sleep(dev,-1);
+ if (kthread_should_stop())
+ goto done;
+
+ restart:
+ try_to_freeze();
+
+ dev->thread.scan1 = dev->thread.scan2;
+ dprintk("tvaudio thread scan start [%d]\n",dev->thread.scan1);
+ dev->tvaudio = NULL;
+
+ saa_writeb(SAA7134_MONITOR_SELECT, 0xa0);
+ saa_writeb(SAA7134_FM_DEMATRIX, 0x80);
+
+ if (dev->ctl_automute)
+ dev->automute = 1;
+
+ mute_input_7134(dev);
+
+ /* give the tuner some time */
+ if (tvaudio_sleep(dev,SCAN_INITIAL_DELAY))
+ goto restart;
+
+ max1 = 0;
+ max2 = 0;
+ nscan = 0;
+ carrier = 0;
+ default_carrier = 0;
+ for (i = 0; i < ARRAY_SIZE(mainscan); i++) {
+ if (!(dev->tvnorm->id & mainscan[i].std))
+ continue;
+ if (!default_carrier)
+ default_carrier = mainscan[i].carr;
+ nscan++;
+ }
+
+ if (1 == nscan) {
+ /* only one candidate -- skip scan ;) */
+ dprintk("only one main carrier candidate - skipping scan\n");
+ max1 = 12345;
+ carrier = default_carrier;
+ } else {
+ /* scan for the main carrier */
+ saa_writeb(SAA7134_MONITOR_SELECT,0x00);
+ tvaudio_setmode(dev,&tvaudio[0],NULL);
+ for (i = 0; i < ARRAY_SIZE(mainscan); i++) {
+ carr_vals[i] = tvaudio_checkcarrier(dev, mainscan+i);
+ if (dev->thread.scan1 != dev->thread.scan2)
+ goto restart;
+ }
+ for (max1 = 0, max2 = 0, i = 0; i < ARRAY_SIZE(mainscan); i++) {
+ if (max1 < carr_vals[i]) {
+ max2 = max1;
+ max1 = carr_vals[i];
+ carrier = mainscan[i].carr;
+ } else if (max2 < carr_vals[i]) {
+ max2 = carr_vals[i];
+ }
+ }
+ }
+
+ if (0 != carrier && max1 > 2000 && max1 > max2*3) {
+ /* found good carrier */
+ dprintk("found %s main sound carrier @ %d.%03d MHz [%d/%d]\n",
+ dev->tvnorm->name, carrier/1000, carrier%1000,
+ max1, max2);
+ dev->last_carrier = carrier;
+ dev->automute = 0;
+
+ } else if (0 != dev->last_carrier) {
+ /* no carrier -- try last detected one as fallback */
+ carrier = dev->last_carrier;
+ dprintk("audio carrier scan failed, "
+ "using %d.%03d MHz [last detected]\n",
+ carrier/1000, carrier%1000);
+ dev->automute = 1;
+
+ } else {
+ /* no carrier + no fallback -- use default */
+ carrier = default_carrier;
+ dprintk("audio carrier scan failed, "
+ "using %d.%03d MHz [default]\n",
+ carrier/1000, carrier%1000);
+ dev->automute = 1;
+ }
+ tvaudio_setcarrier(dev,carrier,carrier);
+ saa_andorb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0x30, 0x00);
+ saa7134_tvaudio_setmute(dev);
+ /* find the exact tv audio norm */
+ for (audio = UNSET, i = 0; i < TVAUDIO; i++) {
+ if (dev->tvnorm->id != UNSET &&
+ !(dev->tvnorm->id & tvaudio[i].std))
+ continue;
+ if (tvaudio[i].carr1 != carrier)
+ continue;
+ /* Note: at least the primary carrier is right here */
+ if (UNSET == audio)
+ audio = i;
+ tvaudio_setmode(dev,&tvaudio[i],"trying");
+ if (tvaudio_sleep(dev,SCAN_SUBCARRIER_DELAY))
+ goto restart;
+ if (-1 != tvaudio_getstereo(dev,&tvaudio[i])) {
+ audio = i;
+ break;
+ }
+ }
+ saa_andorb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0x30, 0x30);
+ if (UNSET == audio)
+ continue;
+ tvaudio_setmode(dev,&tvaudio[audio],"using");
+
+ tvaudio_setstereo(dev,&tvaudio[audio],V4L2_TUNER_MODE_MONO);
+ dev->tvaudio = &tvaudio[audio];
+
+ lastmode = 42;
+ for (;;) {
+
+ try_to_freeze();
+
+ if (tvaudio_sleep(dev,5000))
+ goto restart;
+ if (kthread_should_stop())
+ break;
+ if (UNSET == dev->thread.mode) {
+ rx = tvaudio_getstereo(dev, &tvaudio[audio]);
+ mode = saa7134_tvaudio_rx2mode(rx);
+ } else {
+ mode = dev->thread.mode;
+ }
+ if (lastmode != mode) {
+ tvaudio_setstereo(dev,&tvaudio[audio],mode);
+ lastmode = mode;
+ }
+ }
+ }
+
+ done:
+ dev->thread.stopped = 1;
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+/* saa7133 / saa7135 code */
+
+static char *stdres[0x20] = {
+ [0x00] = "no standard detected",
+ [0x01] = "B/G (in progress)",
+ [0x02] = "D/K (in progress)",
+ [0x03] = "M (in progress)",
+
+ [0x04] = "B/G A2",
+ [0x05] = "B/G NICAM",
+ [0x06] = "D/K A2 (1)",
+ [0x07] = "D/K A2 (2)",
+ [0x08] = "D/K A2 (3)",
+ [0x09] = "D/K NICAM",
+ [0x0a] = "L NICAM",
+ [0x0b] = "I NICAM",
+
+ [0x0c] = "M Korea",
+ [0x0d] = "M BTSC ",
+ [0x0e] = "M EIAJ",
+
+ [0x0f] = "FM radio / IF 10.7 / 50 deemp",
+ [0x10] = "FM radio / IF 10.7 / 75 deemp",
+ [0x11] = "FM radio / IF sel / 50 deemp",
+ [0x12] = "FM radio / IF sel / 75 deemp",
+
+ [0x13 ... 0x1e ] = "unknown",
+ [0x1f] = "??? [in progress]",
+};
+
+#define DSP_RETRY 32
+#define DSP_DELAY 16
+#define SAA7135_DSP_RWCLEAR_RERR 1
+
+static inline int saa_dsp_reset_error_bit(struct saa7134_dev *dev)
+{
+ int state = saa_readb(SAA7135_DSP_RWSTATE);
+ if (unlikely(state & SAA7135_DSP_RWSTATE_ERR)) {
+ d2printk("%s: resetting error bit\n", dev->name);
+ saa_writeb(SAA7135_DSP_RWCLEAR, SAA7135_DSP_RWCLEAR_RERR);
+ }
+ return 0;
+}
+
+static inline int saa_dsp_wait_bit(struct saa7134_dev *dev, int bit)
+{
+ int state, count = DSP_RETRY;
+
+ state = saa_readb(SAA7135_DSP_RWSTATE);
+ if (unlikely(state & SAA7135_DSP_RWSTATE_ERR)) {
+ printk(KERN_WARNING "%s: dsp access error\n", dev->name);
+ saa_dsp_reset_error_bit(dev);
+ return -EIO;
+ }
+ while (0 == (state & bit)) {
+ if (unlikely(0 == count)) {
+ printk("%s: dsp access wait timeout [bit=%s]\n",
+ dev->name,
+ (bit & SAA7135_DSP_RWSTATE_WRR) ? "WRR" :
+ (bit & SAA7135_DSP_RWSTATE_RDB) ? "RDB" :
+ (bit & SAA7135_DSP_RWSTATE_IDA) ? "IDA" :
+ "???");
+ return -EIO;
+ }
+ saa_wait(DSP_DELAY);
+ state = saa_readb(SAA7135_DSP_RWSTATE);
+ count--;
+ }
+ return 0;
+}
+
+
+int saa_dsp_writel(struct saa7134_dev *dev, int reg, u32 value)
+{
+ int err;
+
+ d2printk("dsp write reg 0x%x = 0x%06x\n",reg<<2,value);
+ err = saa_dsp_wait_bit(dev,SAA7135_DSP_RWSTATE_WRR);
+ if (err < 0)
+ return err;
+ saa_writel(reg,value);
+ err = saa_dsp_wait_bit(dev,SAA7135_DSP_RWSTATE_WRR);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+static int getstereo_7133(struct saa7134_dev *dev)
+{
+ int retval = V4L2_TUNER_SUB_MONO;
+ u32 value;
+
+ value = saa_readl(0x528 >> 2);
+ if (value & 0x20)
+ retval = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ if (value & 0x40)
+ retval = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ return retval;
+}
+
+static int mute_input_7133(struct saa7134_dev *dev)
+{
+ u32 reg = 0;
+ u32 xbarin, xbarout;
+ int mask;
+ struct saa7134_input *in;
+
+ xbarin = 0x03;
+ switch (dev->input->amux) {
+ case TV:
+ reg = 0x02;
+ xbarin = 0;
+ break;
+ case LINE1:
+ reg = 0x00;
+ break;
+ case LINE2:
+ case LINE2_LEFT:
+ reg = 0x09;
+ break;
+ }
+ saa_dsp_writel(dev, 0x464 >> 2, xbarin);
+ if (dev->ctl_mute) {
+ reg = 0x07;
+ xbarout = 0xbbbbbb;
+ } else
+ xbarout = 0xbbbb10;
+ saa_dsp_writel(dev, 0x46c >> 2, xbarout);
+
+ saa_writel(0x594 >> 2, reg);
+
+
+ /* switch gpio-connected external audio mux */
+ if (0 != card(dev).gpiomask) {
+ mask = card(dev).gpiomask;
+
+ if (card(dev).mute.name && dev->ctl_mute)
+ in = &card(dev).mute;
+ else
+ in = dev->input;
+
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, mask, mask);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, mask, in->gpio);
+ saa7134_track_gpio(dev,in->name);
+ }
+
+ return 0;
+}
+
+static int tvaudio_thread_ddep(void *data)
+{
+ struct saa7134_dev *dev = data;
+ u32 value, norms;
+
+ set_freezable();
+ for (;;) {
+ tvaudio_sleep(dev,-1);
+ if (kthread_should_stop())
+ goto done;
+ restart:
+ try_to_freeze();
+
+ dev->thread.scan1 = dev->thread.scan2;
+ dprintk("tvaudio thread scan start [%d]\n",dev->thread.scan1);
+
+ if (audio_ddep >= 0x04 && audio_ddep <= 0x0e) {
+ /* insmod option override */
+ norms = (audio_ddep << 2) | 0x01;
+ dprintk("ddep override: %s\n",stdres[audio_ddep]);
+ } else if (&card(dev).radio == dev->input) {
+ dprintk("FM Radio\n");
+ if (dev->tuner_type == TUNER_PHILIPS_TDA8290) {
+ norms = (0x11 << 2) | 0x01;
+ saa_dsp_writel(dev, 0x42c >> 2, 0x729555);
+ } else {
+ norms = (0x0f << 2) | 0x01;
+ }
+ } else {
+ /* (let chip) scan for sound carrier */
+ norms = 0;
+ if (dev->tvnorm->id & (V4L2_STD_B | V4L2_STD_GH))
+ norms |= 0x04;
+ if (dev->tvnorm->id & V4L2_STD_PAL_I)
+ norms |= 0x20;
+ if (dev->tvnorm->id & V4L2_STD_DK)
+ norms |= 0x08;
+ if (dev->tvnorm->id & V4L2_STD_MN)
+ norms |= 0x40;
+ if (dev->tvnorm->id & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))
+ norms |= 0x10;
+ if (0 == norms)
+ norms = 0x7c; /* all */
+ dprintk("scanning:%s%s%s%s%s\n",
+ (norms & 0x04) ? " B/G" : "",
+ (norms & 0x08) ? " D/K" : "",
+ (norms & 0x10) ? " L/L'" : "",
+ (norms & 0x20) ? " I" : "",
+ (norms & 0x40) ? " M" : "");
+ }
+
+ /* kick automatic standard detection */
+ saa_dsp_writel(dev, 0x454 >> 2, 0);
+ saa_dsp_writel(dev, 0x454 >> 2, norms | 0x80);
+
+ /* setup crossbars */
+ saa_dsp_writel(dev, 0x464 >> 2, 0x000000);
+ saa_dsp_writel(dev, 0x470 >> 2, 0x101010);
+
+ if (tvaudio_sleep(dev,3000))
+ goto restart;
+ value = saa_readl(0x528 >> 2) & 0xffffff;
+
+ dprintk("tvaudio thread status: 0x%x [%s%s%s]\n",
+ value, stdres[value & 0x1f],
+ (value & 0x000020) ? ",stereo" : "",
+ (value & 0x000040) ? ",dual" : "");
+ dprintk("detailed status: "
+ "%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s\n",
+ (value & 0x000080) ? " A2/EIAJ pilot tone " : "",
+ (value & 0x000100) ? " A2/EIAJ dual " : "",
+ (value & 0x000200) ? " A2/EIAJ stereo " : "",
+ (value & 0x000400) ? " A2/EIAJ noise mute " : "",
+
+ (value & 0x000800) ? " BTSC/FM radio pilot " : "",
+ (value & 0x001000) ? " SAP carrier " : "",
+ (value & 0x002000) ? " BTSC stereo noise mute " : "",
+ (value & 0x004000) ? " SAP noise mute " : "",
+ (value & 0x008000) ? " VDSP " : "",
+
+ (value & 0x010000) ? " NICST " : "",
+ (value & 0x020000) ? " NICDU " : "",
+ (value & 0x040000) ? " NICAM muted " : "",
+ (value & 0x080000) ? " NICAM reserve sound " : "",
+
+ (value & 0x100000) ? " init done " : "");
+ }
+
+ done:
+ dev->thread.stopped = 1;
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+/* common stuff + external entry points */
+
+void saa7134_enable_i2s(struct saa7134_dev *dev)
+{
+ int i2s_format;
+
+ if (!card_is_empress(dev))
+ return;
+
+ if (dev->pci->device == PCI_DEVICE_ID_PHILIPS_SAA7130)
+ return;
+
+ /* configure GPIO for out */
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x0E000000, 0x00000000);
+
+ switch (dev->pci->device) {
+ case PCI_DEVICE_ID_PHILIPS_SAA7133:
+ case PCI_DEVICE_ID_PHILIPS_SAA7135:
+ /* Set I2S format (SONY)  */
+ saa_writeb(SAA7133_I2S_AUDIO_CONTROL, 0x00);
+ /* Start I2S */
+ saa_writeb(SAA7134_I2S_AUDIO_OUTPUT, 0x11);
+ break;
+
+ case PCI_DEVICE_ID_PHILIPS_SAA7134:
+ i2s_format = (dev->input->amux == TV) ? 0x00 : 0x01;
+
+ /* enable I2S audio output for the mpeg encoder */
+ saa_writeb(SAA7134_I2S_OUTPUT_SELECT, 0x80);
+ saa_writeb(SAA7134_I2S_OUTPUT_FORMAT, i2s_format);
+ saa_writeb(SAA7134_I2S_OUTPUT_LEVEL, 0x0F);
+ saa_writeb(SAA7134_I2S_AUDIO_OUTPUT, 0x01);
+
+ default:
+ break;
+ }
+}
+
+int saa7134_tvaudio_rx2mode(u32 rx)
+{
+ u32 mode;
+
+ mode = V4L2_TUNER_MODE_MONO;
+ if (rx & V4L2_TUNER_SUB_STEREO)
+ mode = V4L2_TUNER_MODE_STEREO;
+ else if (rx & V4L2_TUNER_SUB_LANG1)
+ mode = V4L2_TUNER_MODE_LANG1;
+ else if (rx & V4L2_TUNER_SUB_LANG2)
+ mode = V4L2_TUNER_MODE_LANG2;
+ return mode;
+}
+
+void saa7134_tvaudio_setmute(struct saa7134_dev *dev)
+{
+ switch (dev->pci->device) {
+ case PCI_DEVICE_ID_PHILIPS_SAA7130:
+ case PCI_DEVICE_ID_PHILIPS_SAA7134:
+ mute_input_7134(dev);
+ break;
+ case PCI_DEVICE_ID_PHILIPS_SAA7133:
+ case PCI_DEVICE_ID_PHILIPS_SAA7135:
+ mute_input_7133(dev);
+ break;
+ }
+}
+
+void saa7134_tvaudio_setinput(struct saa7134_dev *dev,
+ struct saa7134_input *in)
+{
+ dev->input = in;
+ switch (dev->pci->device) {
+ case PCI_DEVICE_ID_PHILIPS_SAA7130:
+ case PCI_DEVICE_ID_PHILIPS_SAA7134:
+ mute_input_7134(dev);
+ break;
+ case PCI_DEVICE_ID_PHILIPS_SAA7133:
+ case PCI_DEVICE_ID_PHILIPS_SAA7135:
+ mute_input_7133(dev);
+ break;
+ }
+ saa7134_enable_i2s(dev);
+}
+
+void saa7134_tvaudio_setvolume(struct saa7134_dev *dev, int level)
+{
+ switch (dev->pci->device) {
+ case PCI_DEVICE_ID_PHILIPS_SAA7134:
+ saa_writeb(SAA7134_CHANNEL1_LEVEL, level & 0x1f);
+ saa_writeb(SAA7134_CHANNEL2_LEVEL, level & 0x1f);
+ saa_writeb(SAA7134_NICAM_LEVEL_ADJUST, level & 0x1f);
+ break;
+ }
+}
+
+int saa7134_tvaudio_getstereo(struct saa7134_dev *dev)
+{
+ int retval = V4L2_TUNER_SUB_MONO;
+
+ switch (dev->pci->device) {
+ case PCI_DEVICE_ID_PHILIPS_SAA7134:
+ if (dev->tvaudio)
+ retval = tvaudio_getstereo(dev,dev->tvaudio);
+ break;
+ case PCI_DEVICE_ID_PHILIPS_SAA7133:
+ case PCI_DEVICE_ID_PHILIPS_SAA7135:
+ retval = getstereo_7133(dev);
+ break;
+ }
+ return retval;
+}
+
+void saa7134_tvaudio_init(struct saa7134_dev *dev)
+{
+ int clock = saa7134_boards[dev->board].audio_clock;
+
+ if (UNSET != audio_clock_override)
+ clock = audio_clock_override;
+
+ switch (dev->pci->device) {
+ case PCI_DEVICE_ID_PHILIPS_SAA7134:
+ /* init all audio registers */
+ saa_writeb(SAA7134_AUDIO_PLL_CTRL, 0x00);
+ if (need_resched())
+ schedule();
+ else
+ udelay(10);
+
+ saa_writeb(SAA7134_AUDIO_CLOCK0, clock & 0xff);
+ saa_writeb(SAA7134_AUDIO_CLOCK1, (clock >> 8) & 0xff);
+ saa_writeb(SAA7134_AUDIO_CLOCK2, (clock >> 16) & 0xff);
+ /* frame locked audio is mandatory for NICAM */
+ saa_writeb(SAA7134_AUDIO_PLL_CTRL, 0x01);
+ saa_writeb(SAA7134_NICAM_ERROR_LOW, 0x14);
+ saa_writeb(SAA7134_NICAM_ERROR_HIGH, 0x50);
+ break;
+ case PCI_DEVICE_ID_PHILIPS_SAA7133:
+ case PCI_DEVICE_ID_PHILIPS_SAA7135:
+ saa_writel(0x598 >> 2, clock);
+ saa_dsp_writel(dev, 0x474 >> 2, 0x00);
+ saa_dsp_writel(dev, 0x450 >> 2, 0x00);
+ }
+}
+
+int saa7134_tvaudio_init2(struct saa7134_dev *dev)
+{
+ int (*my_thread)(void *data) = NULL;
+
+ switch (dev->pci->device) {
+ case PCI_DEVICE_ID_PHILIPS_SAA7134:
+ my_thread = tvaudio_thread;
+ break;
+ case PCI_DEVICE_ID_PHILIPS_SAA7133:
+ case PCI_DEVICE_ID_PHILIPS_SAA7135:
+ my_thread = tvaudio_thread_ddep;
+ break;
+ }
+
+ dev->thread.thread = NULL;
+ dev->thread.scan1 = dev->thread.scan2 = 0;
+ if (my_thread) {
+ saa7134_tvaudio_init(dev);
+ /* start tvaudio thread */
+ dev->thread.thread = kthread_run(my_thread, dev, "%s", dev->name);
+ if (IS_ERR(dev->thread.thread)) {
+ printk(KERN_WARNING "%s: kernel_thread() failed\n",
+ dev->name);
+ /* XXX: missing error handling here */
+ }
+ }
+
+ saa7134_enable_i2s(dev);
+ return 0;
+}
+
+int saa7134_tvaudio_close(struct saa7134_dev *dev)
+{
+ dev->automute = 1;
+ /* anything else to undo? */
+ return 0;
+}
+
+int saa7134_tvaudio_fini(struct saa7134_dev *dev)
+{
+ /* shutdown tvaudio thread */
+ if (dev->thread.thread && !dev->thread.stopped)
+ kthread_stop(dev->thread.thread);
+
+ saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x07, 0x00); /* LINE1 */
+ return 0;
+}
+
+int saa7134_tvaudio_do_scan(struct saa7134_dev *dev)
+{
+ if (dev->input->amux != TV) {
+ dprintk("sound IF not in use, skipping scan\n");
+ dev->automute = 0;
+ saa7134_tvaudio_setmute(dev);
+ } else if (dev->thread.thread) {
+ dev->thread.mode = UNSET;
+ dev->thread.scan2++;
+
+ if (!dev->insuspend && !dev->thread.stopped)
+ wake_up_process(dev->thread.thread);
+ } else {
+ dev->automute = 0;
+ saa7134_tvaudio_setmute(dev);
+ }
+ return 0;
+}
+
+EXPORT_SYMBOL(saa_dsp_writel);
+EXPORT_SYMBOL(saa7134_tvaudio_setmute);
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/saa7134/saa7134-vbi.c b/drivers/media/video/saa7134/saa7134-vbi.c
new file mode 100644
index 00000000..e9aa94b8
--- /dev/null
+++ b/drivers/media/video/saa7134/saa7134-vbi.c
@@ -0,0 +1,255 @@
+/*
+ *
+ * device driver for philips saa7134 based TV cards
+ * video4linux video interface
+ *
+ * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int vbi_debug;
+module_param(vbi_debug, int, 0644);
+MODULE_PARM_DESC(vbi_debug,"enable debug messages [vbi]");
+
+static unsigned int vbibufs = 4;
+module_param(vbibufs, int, 0444);
+MODULE_PARM_DESC(vbibufs,"number of vbi buffers, range 2-32");
+
+#define dprintk(fmt, arg...) if (vbi_debug) \
+ printk(KERN_DEBUG "%s/vbi: " fmt, dev->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+
+#define VBI_LINE_COUNT 16
+#define VBI_LINE_LENGTH 2048
+#define VBI_SCALE 0x200
+
+static void task_init(struct saa7134_dev *dev, struct saa7134_buf *buf,
+ int task)
+{
+ struct saa7134_tvnorm *norm = dev->tvnorm;
+
+ /* setup video scaler */
+ saa_writeb(SAA7134_VBI_H_START1(task), norm->h_start & 0xff);
+ saa_writeb(SAA7134_VBI_H_START2(task), norm->h_start >> 8);
+ saa_writeb(SAA7134_VBI_H_STOP1(task), norm->h_stop & 0xff);
+ saa_writeb(SAA7134_VBI_H_STOP2(task), norm->h_stop >> 8);
+ saa_writeb(SAA7134_VBI_V_START1(task), norm->vbi_v_start_0 & 0xff);
+ saa_writeb(SAA7134_VBI_V_START2(task), norm->vbi_v_start_0 >> 8);
+ saa_writeb(SAA7134_VBI_V_STOP1(task), norm->vbi_v_stop_0 & 0xff);
+ saa_writeb(SAA7134_VBI_V_STOP2(task), norm->vbi_v_stop_0 >> 8);
+
+ saa_writeb(SAA7134_VBI_H_SCALE_INC1(task), VBI_SCALE & 0xff);
+ saa_writeb(SAA7134_VBI_H_SCALE_INC2(task), VBI_SCALE >> 8);
+ saa_writeb(SAA7134_VBI_PHASE_OFFSET_LUMA(task), 0x00);
+ saa_writeb(SAA7134_VBI_PHASE_OFFSET_CHROMA(task), 0x00);
+
+ saa_writeb(SAA7134_VBI_H_LEN1(task), buf->vb.width & 0xff);
+ saa_writeb(SAA7134_VBI_H_LEN2(task), buf->vb.width >> 8);
+ saa_writeb(SAA7134_VBI_V_LEN1(task), buf->vb.height & 0xff);
+ saa_writeb(SAA7134_VBI_V_LEN2(task), buf->vb.height >> 8);
+
+ saa_andorb(SAA7134_DATA_PATH(task), 0xc0, 0x00);
+}
+
+/* ------------------------------------------------------------------ */
+
+static int buffer_activate(struct saa7134_dev *dev,
+ struct saa7134_buf *buf,
+ struct saa7134_buf *next)
+{
+ unsigned long control,base;
+
+ dprintk("buffer_activate [%p]\n",buf);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->top_seen = 0;
+
+ task_init(dev,buf,TASK_A);
+ task_init(dev,buf,TASK_B);
+ saa_writeb(SAA7134_OFMT_DATA_A, 0x06);
+ saa_writeb(SAA7134_OFMT_DATA_B, 0x06);
+
+ /* DMA: setup channel 2+3 (= VBI Task A+B) */
+ base = saa7134_buffer_base(buf);
+ control = SAA7134_RS_CONTROL_BURST_16 |
+ SAA7134_RS_CONTROL_ME |
+ (buf->pt->dma >> 12);
+ saa_writel(SAA7134_RS_BA1(2),base);
+ saa_writel(SAA7134_RS_BA2(2),base + buf->vb.size/2);
+ saa_writel(SAA7134_RS_PITCH(2),buf->vb.width);
+ saa_writel(SAA7134_RS_CONTROL(2),control);
+ saa_writel(SAA7134_RS_BA1(3),base);
+ saa_writel(SAA7134_RS_BA2(3),base + buf->vb.size/2);
+ saa_writel(SAA7134_RS_PITCH(3),buf->vb.width);
+ saa_writel(SAA7134_RS_CONTROL(3),control);
+
+ /* start DMA */
+ saa7134_set_dmabits(dev);
+ mod_timer(&dev->vbi_q.timeout, jiffies+BUFFER_TIMEOUT);
+
+ return 0;
+}
+
+static int buffer_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct saa7134_fh *fh = q->priv_data;
+ struct saa7134_dev *dev = fh->dev;
+ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+ struct saa7134_tvnorm *norm = dev->tvnorm;
+ unsigned int lines, llength, size;
+ int err;
+
+ lines = norm->vbi_v_stop_0 - norm->vbi_v_start_0 +1;
+ if (lines > VBI_LINE_COUNT)
+ lines = VBI_LINE_COUNT;
+ llength = VBI_LINE_LENGTH;
+ size = lines * llength * 2;
+ if (0 != buf->vb.baddr && buf->vb.bsize < size)
+ return -EINVAL;
+
+ if (buf->vb.size != size)
+ saa7134_dma_free(q,buf);
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+
+ buf->vb.width = llength;
+ buf->vb.height = lines;
+ buf->vb.size = size;
+ buf->pt = &fh->pt_vbi;
+
+ err = videobuf_iolock(q,&buf->vb,NULL);
+ if (err)
+ goto oops;
+ err = saa7134_pgtable_build(dev->pci,buf->pt,
+ dma->sglist,
+ dma->sglen,
+ saa7134_buffer_startpage(buf));
+ if (err)
+ goto oops;
+ }
+ buf->vb.state = VIDEOBUF_PREPARED;
+ buf->activate = buffer_activate;
+ buf->vb.field = field;
+ return 0;
+
+ oops:
+ saa7134_dma_free(q,buf);
+ return err;
+}
+
+static int
+buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+ struct saa7134_fh *fh = q->priv_data;
+ struct saa7134_dev *dev = fh->dev;
+ int llength,lines;
+
+ lines = dev->tvnorm->vbi_v_stop_0 - dev->tvnorm->vbi_v_start_0 +1;
+ llength = VBI_LINE_LENGTH;
+ *size = lines * llength * 2;
+ if (0 == *count)
+ *count = vbibufs;
+ *count = saa7134_buffer_count(*size,*count);
+ return 0;
+}
+
+static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct saa7134_fh *fh = q->priv_data;
+ struct saa7134_dev *dev = fh->dev;
+ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+
+ saa7134_buffer_queue(dev,&dev->vbi_q,buf);
+}
+
+static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+
+ saa7134_dma_free(q,buf);
+}
+
+struct videobuf_queue_ops saa7134_vbi_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/* ------------------------------------------------------------------ */
+
+int saa7134_vbi_init1(struct saa7134_dev *dev)
+{
+ INIT_LIST_HEAD(&dev->vbi_q.queue);
+ init_timer(&dev->vbi_q.timeout);
+ dev->vbi_q.timeout.function = saa7134_buffer_timeout;
+ dev->vbi_q.timeout.data = (unsigned long)(&dev->vbi_q);
+ dev->vbi_q.dev = dev;
+
+ if (vbibufs < 2)
+ vbibufs = 2;
+ if (vbibufs > VIDEO_MAX_FRAME)
+ vbibufs = VIDEO_MAX_FRAME;
+ return 0;
+}
+
+int saa7134_vbi_fini(struct saa7134_dev *dev)
+{
+ /* nothing */
+ return 0;
+}
+
+void saa7134_irq_vbi_done(struct saa7134_dev *dev, unsigned long status)
+{
+ spin_lock(&dev->slock);
+ if (dev->vbi_q.curr) {
+ dev->vbi_fieldcount++;
+ /* make sure we have seen both fields */
+ if ((status & 0x10) == 0x00) {
+ dev->vbi_q.curr->top_seen = 1;
+ goto done;
+ }
+ if (!dev->vbi_q.curr->top_seen)
+ goto done;
+
+ dev->vbi_q.curr->vb.field_count = dev->vbi_fieldcount;
+ saa7134_buffer_finish(dev,&dev->vbi_q,VIDEOBUF_DONE);
+ }
+ saa7134_buffer_next(dev,&dev->vbi_q);
+
+ done:
+ spin_unlock(&dev->slock);
+}
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c
new file mode 100644
index 00000000..417034eb
--- /dev/null
+++ b/drivers/media/video/saa7134/saa7134-video.c
@@ -0,0 +1,2661 @@
+/*
+ *
+ * device driver for philips saa7134 based TV cards
+ * video4linux video interface
+ *
+ * (c) 2001-03 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sort.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+#include <media/v4l2-common.h>
+#include <media/saa6588.h>
+
+/* ------------------------------------------------------------------ */
+
+unsigned int video_debug;
+static unsigned int gbuffers = 8;
+static unsigned int noninterlaced; /* 0 */
+static unsigned int gbufsize = 720*576*4;
+static unsigned int gbufsize_max = 720*576*4;
+static char secam[] = "--";
+module_param(video_debug, int, 0644);
+MODULE_PARM_DESC(video_debug,"enable debug messages [video]");
+module_param(gbuffers, int, 0444);
+MODULE_PARM_DESC(gbuffers,"number of capture buffers, range 2-32");
+module_param(noninterlaced, int, 0644);
+MODULE_PARM_DESC(noninterlaced,"capture non interlaced video");
+module_param_string(secam, secam, sizeof(secam), 0644);
+MODULE_PARM_DESC(secam, "force SECAM variant, either DK,L or Lc");
+
+
+#define dprintk(fmt, arg...) if (video_debug&0x04) \
+ printk(KERN_DEBUG "%s/video: " fmt, dev->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+/* Defines for Video Output Port Register at address 0x191 */
+
+/* Bit 0: VIP code T bit polarity */
+
+#define VP_T_CODE_P_NON_INVERTED 0x00
+#define VP_T_CODE_P_INVERTED 0x01
+
+/* ------------------------------------------------------------------ */
+/* Defines for Video Output Port Register at address 0x195 */
+
+/* Bit 2: Video output clock delay control */
+
+#define VP_CLK_CTRL2_NOT_DELAYED 0x00
+#define VP_CLK_CTRL2_DELAYED 0x04
+
+/* Bit 1: Video output clock invert control */
+
+#define VP_CLK_CTRL1_NON_INVERTED 0x00
+#define VP_CLK_CTRL1_INVERTED 0x02
+
+/* ------------------------------------------------------------------ */
+/* Defines for Video Output Port Register at address 0x196 */
+
+/* Bits 2 to 0: VSYNC pin video vertical sync type */
+
+#define VP_VS_TYPE_MASK 0x07
+
+#define VP_VS_TYPE_OFF 0x00
+#define VP_VS_TYPE_V123 0x01
+#define VP_VS_TYPE_V_ITU 0x02
+#define VP_VS_TYPE_VGATE_L 0x03
+#define VP_VS_TYPE_RESERVED1 0x04
+#define VP_VS_TYPE_RESERVED2 0x05
+#define VP_VS_TYPE_F_ITU 0x06
+#define VP_VS_TYPE_SC_FID 0x07
+
+/* ------------------------------------------------------------------ */
+/* data structs for video */
+
+static int video_out[][9] = {
+ [CCIR656] = { 0x00, 0xb1, 0x00, 0xa1, 0x00, 0x04, 0x06, 0x00, 0x00 },
+};
+
+static struct saa7134_format formats[] = {
+ {
+ .name = "8 bpp gray",
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .depth = 8,
+ .pm = 0x06,
+ },{
+ .name = "15 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .depth = 16,
+ .pm = 0x13 | 0x80,
+ },{
+ .name = "15 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB555X,
+ .depth = 16,
+ .pm = 0x13 | 0x80,
+ .bswap = 1,
+ },{
+ .name = "16 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .depth = 16,
+ .pm = 0x10 | 0x80,
+ },{
+ .name = "16 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+ .depth = 16,
+ .pm = 0x10 | 0x80,
+ .bswap = 1,
+ },{
+ .name = "24 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .depth = 24,
+ .pm = 0x11,
+ },{
+ .name = "24 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .depth = 24,
+ .pm = 0x11,
+ .bswap = 1,
+ },{
+ .name = "32 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_BGR32,
+ .depth = 32,
+ .pm = 0x12,
+ },{
+ .name = "32 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .depth = 32,
+ .pm = 0x12,
+ .bswap = 1,
+ .wswap = 1,
+ },{
+ .name = "4:2:2 packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .pm = 0x00,
+ .bswap = 1,
+ .yuv = 1,
+ },{
+ .name = "4:2:2 packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = 16,
+ .pm = 0x00,
+ .yuv = 1,
+ },{
+ .name = "4:2:2 planar, Y-Cb-Cr",
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .depth = 16,
+ .pm = 0x09,
+ .yuv = 1,
+ .planar = 1,
+ .hshift = 1,
+ .vshift = 0,
+ },{
+ .name = "4:2:0 planar, Y-Cb-Cr",
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .depth = 12,
+ .pm = 0x0a,
+ .yuv = 1,
+ .planar = 1,
+ .hshift = 1,
+ .vshift = 1,
+ },{
+ .name = "4:2:0 planar, Y-Cb-Cr",
+ .fourcc = V4L2_PIX_FMT_YVU420,
+ .depth = 12,
+ .pm = 0x0a,
+ .yuv = 1,
+ .planar = 1,
+ .uvswap = 1,
+ .hshift = 1,
+ .vshift = 1,
+ }
+};
+#define FORMATS ARRAY_SIZE(formats)
+
+#define NORM_625_50 \
+ .h_start = 0, \
+ .h_stop = 719, \
+ .video_v_start = 24, \
+ .video_v_stop = 311, \
+ .vbi_v_start_0 = 7, \
+ .vbi_v_stop_0 = 22, \
+ .vbi_v_start_1 = 319, \
+ .src_timing = 4
+
+#define NORM_525_60 \
+ .h_start = 0, \
+ .h_stop = 719, \
+ .video_v_start = 23, \
+ .video_v_stop = 262, \
+ .vbi_v_start_0 = 10, \
+ .vbi_v_stop_0 = 21, \
+ .vbi_v_start_1 = 273, \
+ .src_timing = 7
+
+static struct saa7134_tvnorm tvnorms[] = {
+ {
+ .name = "PAL", /* autodetect */
+ .id = V4L2_STD_PAL,
+ NORM_625_50,
+
+ .sync_control = 0x18,
+ .luma_control = 0x40,
+ .chroma_ctrl1 = 0x81,
+ .chroma_gain = 0x2a,
+ .chroma_ctrl2 = 0x06,
+ .vgate_misc = 0x1c,
+
+ },{
+ .name = "PAL-BG",
+ .id = V4L2_STD_PAL_BG,
+ NORM_625_50,
+
+ .sync_control = 0x18,
+ .luma_control = 0x40,
+ .chroma_ctrl1 = 0x81,
+ .chroma_gain = 0x2a,
+ .chroma_ctrl2 = 0x06,
+ .vgate_misc = 0x1c,
+
+ },{
+ .name = "PAL-I",
+ .id = V4L2_STD_PAL_I,
+ NORM_625_50,
+
+ .sync_control = 0x18,
+ .luma_control = 0x40,
+ .chroma_ctrl1 = 0x81,
+ .chroma_gain = 0x2a,
+ .chroma_ctrl2 = 0x06,
+ .vgate_misc = 0x1c,
+
+ },{
+ .name = "PAL-DK",
+ .id = V4L2_STD_PAL_DK,
+ NORM_625_50,
+
+ .sync_control = 0x18,
+ .luma_control = 0x40,
+ .chroma_ctrl1 = 0x81,
+ .chroma_gain = 0x2a,
+ .chroma_ctrl2 = 0x06,
+ .vgate_misc = 0x1c,
+
+ },{
+ .name = "NTSC",
+ .id = V4L2_STD_NTSC,
+ NORM_525_60,
+
+ .sync_control = 0x59,
+ .luma_control = 0x40,
+ .chroma_ctrl1 = 0x89,
+ .chroma_gain = 0x2a,
+ .chroma_ctrl2 = 0x0e,
+ .vgate_misc = 0x18,
+
+ },{
+ .name = "SECAM",
+ .id = V4L2_STD_SECAM,
+ NORM_625_50,
+
+ .sync_control = 0x18,
+ .luma_control = 0x1b,
+ .chroma_ctrl1 = 0xd1,
+ .chroma_gain = 0x80,
+ .chroma_ctrl2 = 0x00,
+ .vgate_misc = 0x1c,
+
+ },{
+ .name = "SECAM-DK",
+ .id = V4L2_STD_SECAM_DK,
+ NORM_625_50,
+
+ .sync_control = 0x18,
+ .luma_control = 0x1b,
+ .chroma_ctrl1 = 0xd1,
+ .chroma_gain = 0x80,
+ .chroma_ctrl2 = 0x00,
+ .vgate_misc = 0x1c,
+
+ },{
+ .name = "SECAM-L",
+ .id = V4L2_STD_SECAM_L,
+ NORM_625_50,
+
+ .sync_control = 0x18,
+ .luma_control = 0x1b,
+ .chroma_ctrl1 = 0xd1,
+ .chroma_gain = 0x80,
+ .chroma_ctrl2 = 0x00,
+ .vgate_misc = 0x1c,
+
+ },{
+ .name = "SECAM-Lc",
+ .id = V4L2_STD_SECAM_LC,
+ NORM_625_50,
+
+ .sync_control = 0x18,
+ .luma_control = 0x1b,
+ .chroma_ctrl1 = 0xd1,
+ .chroma_gain = 0x80,
+ .chroma_ctrl2 = 0x00,
+ .vgate_misc = 0x1c,
+
+ },{
+ .name = "PAL-M",
+ .id = V4L2_STD_PAL_M,
+ NORM_525_60,
+
+ .sync_control = 0x59,
+ .luma_control = 0x40,
+ .chroma_ctrl1 = 0xb9,
+ .chroma_gain = 0x2a,
+ .chroma_ctrl2 = 0x0e,
+ .vgate_misc = 0x18,
+
+ },{
+ .name = "PAL-Nc",
+ .id = V4L2_STD_PAL_Nc,
+ NORM_625_50,
+
+ .sync_control = 0x18,
+ .luma_control = 0x40,
+ .chroma_ctrl1 = 0xa1,
+ .chroma_gain = 0x2a,
+ .chroma_ctrl2 = 0x06,
+ .vgate_misc = 0x1c,
+
+ },{
+ .name = "PAL-60",
+ .id = V4L2_STD_PAL_60,
+
+ .h_start = 0,
+ .h_stop = 719,
+ .video_v_start = 23,
+ .video_v_stop = 262,
+ .vbi_v_start_0 = 10,
+ .vbi_v_stop_0 = 21,
+ .vbi_v_start_1 = 273,
+ .src_timing = 7,
+
+ .sync_control = 0x18,
+ .luma_control = 0x40,
+ .chroma_ctrl1 = 0x81,
+ .chroma_gain = 0x2a,
+ .chroma_ctrl2 = 0x06,
+ .vgate_misc = 0x1c,
+ }
+};
+#define TVNORMS ARRAY_SIZE(tvnorms)
+
+#define V4L2_CID_PRIVATE_INVERT (V4L2_CID_PRIVATE_BASE + 0)
+#define V4L2_CID_PRIVATE_Y_ODD (V4L2_CID_PRIVATE_BASE + 1)
+#define V4L2_CID_PRIVATE_Y_EVEN (V4L2_CID_PRIVATE_BASE + 2)
+#define V4L2_CID_PRIVATE_AUTOMUTE (V4L2_CID_PRIVATE_BASE + 3)
+#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 4)
+
+static const struct v4l2_queryctrl no_ctrl = {
+ .name = "42",
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+};
+static const struct v4l2_queryctrl video_ctrls[] = {
+ /* --- video --- */
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 128,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_CONTRAST,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 68,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_SATURATION,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 64,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_HUE,
+ .name = "Hue",
+ .minimum = -128,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_HFLIP,
+ .name = "Mirror",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },
+ /* --- audio --- */
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .name = "Volume",
+ .minimum = -15,
+ .maximum = 15,
+ .step = 1,
+ .default_value = 0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ /* --- private --- */
+ {
+ .id = V4L2_CID_PRIVATE_INVERT,
+ .name = "Invert",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_PRIVATE_Y_ODD,
+ .name = "y offset odd field",
+ .minimum = 0,
+ .maximum = 128,
+ .step = 1,
+ .default_value = 0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_PRIVATE_Y_EVEN,
+ .name = "y offset even field",
+ .minimum = 0,
+ .maximum = 128,
+ .step = 1,
+ .default_value = 0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_PRIVATE_AUTOMUTE,
+ .name = "automute",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ }
+};
+static const unsigned int CTRLS = ARRAY_SIZE(video_ctrls);
+
+static const struct v4l2_queryctrl* ctrl_by_id(unsigned int id)
+{
+ unsigned int i;
+
+ for (i = 0; i < CTRLS; i++)
+ if (video_ctrls[i].id == id)
+ return video_ctrls+i;
+ return NULL;
+}
+
+static struct saa7134_format* format_by_fourcc(unsigned int fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < FORMATS; i++)
+ if (formats[i].fourcc == fourcc)
+ return formats+i;
+ return NULL;
+}
+
+/* ----------------------------------------------------------------------- */
+/* resource management */
+
+static int res_get(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int bit)
+{
+ if (fh->resources & bit)
+ /* have it already allocated */
+ return 1;
+
+ /* is it free? */
+ mutex_lock(&dev->lock);
+ if (dev->resources & bit) {
+ /* no, someone else uses it */
+ mutex_unlock(&dev->lock);
+ return 0;
+ }
+ /* it's free, grab it */
+ fh->resources |= bit;
+ dev->resources |= bit;
+ dprintk("res: get %d\n",bit);
+ mutex_unlock(&dev->lock);
+ return 1;
+}
+
+static int res_check(struct saa7134_fh *fh, unsigned int bit)
+{
+ return (fh->resources & bit);
+}
+
+static int res_locked(struct saa7134_dev *dev, unsigned int bit)
+{
+ return (dev->resources & bit);
+}
+
+static
+void res_free(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int bits)
+{
+ BUG_ON((fh->resources & bits) != bits);
+
+ mutex_lock(&dev->lock);
+ fh->resources &= ~bits;
+ dev->resources &= ~bits;
+ dprintk("res: put %d\n",bits);
+ mutex_unlock(&dev->lock);
+}
+
+/* ------------------------------------------------------------------ */
+
+static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm)
+{
+ dprintk("set tv norm = %s\n",norm->name);
+ dev->tvnorm = norm;
+
+ /* setup cropping */
+ dev->crop_bounds.left = norm->h_start;
+ dev->crop_defrect.left = norm->h_start;
+ dev->crop_bounds.width = norm->h_stop - norm->h_start +1;
+ dev->crop_defrect.width = norm->h_stop - norm->h_start +1;
+
+ dev->crop_bounds.top = (norm->vbi_v_stop_0+1)*2;
+ dev->crop_defrect.top = norm->video_v_start*2;
+ dev->crop_bounds.height = ((norm->id & V4L2_STD_525_60) ? 524 : 624)
+ - dev->crop_bounds.top;
+ dev->crop_defrect.height = (norm->video_v_stop - norm->video_v_start +1)*2;
+
+ dev->crop_current = dev->crop_defrect;
+
+ saa7134_set_tvnorm_hw(dev);
+}
+
+static void video_mux(struct saa7134_dev *dev, int input)
+{
+ dprintk("video input = %d [%s]\n", input, card_in(dev, input).name);
+ dev->ctl_input = input;
+ set_tvnorm(dev, dev->tvnorm);
+ saa7134_tvaudio_setinput(dev, &card_in(dev, input));
+}
+
+
+static void saa7134_set_decoder(struct saa7134_dev *dev)
+{
+ int luma_control, sync_control, mux;
+
+ struct saa7134_tvnorm *norm = dev->tvnorm;
+ mux = card_in(dev, dev->ctl_input).vmux;
+
+ luma_control = norm->luma_control;
+ sync_control = norm->sync_control;
+
+ if (mux > 5)
+ luma_control |= 0x80; /* svideo */
+ if (noninterlaced || dev->nosignal)
+ sync_control |= 0x20;
+
+ /* setup video decoder */
+ saa_writeb(SAA7134_INCR_DELAY, 0x08);
+ saa_writeb(SAA7134_ANALOG_IN_CTRL1, 0xc0 | mux);
+ saa_writeb(SAA7134_ANALOG_IN_CTRL2, 0x00);
+
+ saa_writeb(SAA7134_ANALOG_IN_CTRL3, 0x90);
+ saa_writeb(SAA7134_ANALOG_IN_CTRL4, 0x90);
+ saa_writeb(SAA7134_HSYNC_START, 0xeb);
+ saa_writeb(SAA7134_HSYNC_STOP, 0xe0);
+ saa_writeb(SAA7134_SOURCE_TIMING1, norm->src_timing);
+
+ saa_writeb(SAA7134_SYNC_CTRL, sync_control);
+ saa_writeb(SAA7134_LUMA_CTRL, luma_control);
+ saa_writeb(SAA7134_DEC_LUMA_BRIGHT, dev->ctl_bright);
+
+ saa_writeb(SAA7134_DEC_LUMA_CONTRAST,
+ dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast);
+
+ saa_writeb(SAA7134_DEC_CHROMA_SATURATION,
+ dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation);
+
+ saa_writeb(SAA7134_DEC_CHROMA_HUE, dev->ctl_hue);
+ saa_writeb(SAA7134_CHROMA_CTRL1, norm->chroma_ctrl1);
+ saa_writeb(SAA7134_CHROMA_GAIN, norm->chroma_gain);
+
+ saa_writeb(SAA7134_CHROMA_CTRL2, norm->chroma_ctrl2);
+ saa_writeb(SAA7134_MODE_DELAY_CTRL, 0x00);
+
+ saa_writeb(SAA7134_ANALOG_ADC, 0x01);
+ saa_writeb(SAA7134_VGATE_START, 0x11);
+ saa_writeb(SAA7134_VGATE_STOP, 0xfe);
+ saa_writeb(SAA7134_MISC_VGATE_MSB, norm->vgate_misc);
+ saa_writeb(SAA7134_RAW_DATA_GAIN, 0x40);
+ saa_writeb(SAA7134_RAW_DATA_OFFSET, 0x80);
+}
+
+void saa7134_set_tvnorm_hw(struct saa7134_dev *dev)
+{
+ saa7134_set_decoder(dev);
+
+ if (card_in(dev, dev->ctl_input).tv)
+ saa_call_all(dev, core, s_std, dev->tvnorm->id);
+ /* Set the correct norm for the saa6752hs. This function
+ does nothing if there is no saa6752hs. */
+ saa_call_empress(dev, core, s_std, dev->tvnorm->id);
+}
+
+static void set_h_prescale(struct saa7134_dev *dev, int task, int prescale)
+{
+ static const struct {
+ int xpsc;
+ int xacl;
+ int xc2_1;
+ int xdcg;
+ int vpfy;
+ } vals[] = {
+ /* XPSC XACL XC2_1 XDCG VPFY */
+ { 1, 0, 0, 0, 0 },
+ { 2, 2, 1, 2, 2 },
+ { 3, 4, 1, 3, 2 },
+ { 4, 8, 1, 4, 2 },
+ { 5, 8, 1, 4, 2 },
+ { 6, 8, 1, 4, 3 },
+ { 7, 8, 1, 4, 3 },
+ { 8, 15, 0, 4, 3 },
+ { 9, 15, 0, 4, 3 },
+ { 10, 16, 1, 5, 3 },
+ };
+ static const int count = ARRAY_SIZE(vals);
+ int i;
+
+ for (i = 0; i < count; i++)
+ if (vals[i].xpsc == prescale)
+ break;
+ if (i == count)
+ return;
+
+ saa_writeb(SAA7134_H_PRESCALE(task), vals[i].xpsc);
+ saa_writeb(SAA7134_ACC_LENGTH(task), vals[i].xacl);
+ saa_writeb(SAA7134_LEVEL_CTRL(task),
+ (vals[i].xc2_1 << 3) | (vals[i].xdcg));
+ saa_andorb(SAA7134_FIR_PREFILTER_CTRL(task), 0x0f,
+ (vals[i].vpfy << 2) | vals[i].vpfy);
+}
+
+static void set_v_scale(struct saa7134_dev *dev, int task, int yscale)
+{
+ int val,mirror;
+
+ saa_writeb(SAA7134_V_SCALE_RATIO1(task), yscale & 0xff);
+ saa_writeb(SAA7134_V_SCALE_RATIO2(task), yscale >> 8);
+
+ mirror = (dev->ctl_mirror) ? 0x02 : 0x00;
+ if (yscale < 2048) {
+ /* LPI */
+ dprintk("yscale LPI yscale=%d\n",yscale);
+ saa_writeb(SAA7134_V_FILTER(task), 0x00 | mirror);
+ saa_writeb(SAA7134_LUMA_CONTRAST(task), 0x40);
+ saa_writeb(SAA7134_CHROMA_SATURATION(task), 0x40);
+ } else {
+ /* ACM */
+ val = 0x40 * 1024 / yscale;
+ dprintk("yscale ACM yscale=%d val=0x%x\n",yscale,val);
+ saa_writeb(SAA7134_V_FILTER(task), 0x01 | mirror);
+ saa_writeb(SAA7134_LUMA_CONTRAST(task), val);
+ saa_writeb(SAA7134_CHROMA_SATURATION(task), val);
+ }
+ saa_writeb(SAA7134_LUMA_BRIGHT(task), 0x80);
+}
+
+static void set_size(struct saa7134_dev *dev, int task,
+ int width, int height, int interlace)
+{
+ int prescale,xscale,yscale,y_even,y_odd;
+ int h_start, h_stop, v_start, v_stop;
+ int div = interlace ? 2 : 1;
+
+ /* setup video scaler */
+ h_start = dev->crop_current.left;
+ v_start = dev->crop_current.top/2;
+ h_stop = (dev->crop_current.left + dev->crop_current.width -1);
+ v_stop = (dev->crop_current.top + dev->crop_current.height -1)/2;
+
+ saa_writeb(SAA7134_VIDEO_H_START1(task), h_start & 0xff);
+ saa_writeb(SAA7134_VIDEO_H_START2(task), h_start >> 8);
+ saa_writeb(SAA7134_VIDEO_H_STOP1(task), h_stop & 0xff);
+ saa_writeb(SAA7134_VIDEO_H_STOP2(task), h_stop >> 8);
+ saa_writeb(SAA7134_VIDEO_V_START1(task), v_start & 0xff);
+ saa_writeb(SAA7134_VIDEO_V_START2(task), v_start >> 8);
+ saa_writeb(SAA7134_VIDEO_V_STOP1(task), v_stop & 0xff);
+ saa_writeb(SAA7134_VIDEO_V_STOP2(task), v_stop >> 8);
+
+ prescale = dev->crop_current.width / width;
+ if (0 == prescale)
+ prescale = 1;
+ xscale = 1024 * dev->crop_current.width / prescale / width;
+ yscale = 512 * div * dev->crop_current.height / height;
+ dprintk("prescale=%d xscale=%d yscale=%d\n",prescale,xscale,yscale);
+ set_h_prescale(dev,task,prescale);
+ saa_writeb(SAA7134_H_SCALE_INC1(task), xscale & 0xff);
+ saa_writeb(SAA7134_H_SCALE_INC2(task), xscale >> 8);
+ set_v_scale(dev,task,yscale);
+
+ saa_writeb(SAA7134_VIDEO_PIXELS1(task), width & 0xff);
+ saa_writeb(SAA7134_VIDEO_PIXELS2(task), width >> 8);
+ saa_writeb(SAA7134_VIDEO_LINES1(task), height/div & 0xff);
+ saa_writeb(SAA7134_VIDEO_LINES2(task), height/div >> 8);
+
+ /* deinterlace y offsets */
+ y_odd = dev->ctl_y_odd;
+ y_even = dev->ctl_y_even;
+ saa_writeb(SAA7134_V_PHASE_OFFSET0(task), y_odd);
+ saa_writeb(SAA7134_V_PHASE_OFFSET1(task), y_even);
+ saa_writeb(SAA7134_V_PHASE_OFFSET2(task), y_odd);
+ saa_writeb(SAA7134_V_PHASE_OFFSET3(task), y_even);
+}
+
+/* ------------------------------------------------------------------ */
+
+struct cliplist {
+ __u16 position;
+ __u8 enable;
+ __u8 disable;
+};
+
+static void set_cliplist(struct saa7134_dev *dev, int reg,
+ struct cliplist *cl, int entries, char *name)
+{
+ __u8 winbits = 0;
+ int i;
+
+ for (i = 0; i < entries; i++) {
+ winbits |= cl[i].enable;
+ winbits &= ~cl[i].disable;
+ if (i < 15 && cl[i].position == cl[i+1].position)
+ continue;
+ saa_writeb(reg + 0, winbits);
+ saa_writeb(reg + 2, cl[i].position & 0xff);
+ saa_writeb(reg + 3, cl[i].position >> 8);
+ dprintk("clip: %s winbits=%02x pos=%d\n",
+ name,winbits,cl[i].position);
+ reg += 8;
+ }
+ for (; reg < 0x400; reg += 8) {
+ saa_writeb(reg+ 0, 0);
+ saa_writeb(reg + 1, 0);
+ saa_writeb(reg + 2, 0);
+ saa_writeb(reg + 3, 0);
+ }
+}
+
+static int clip_range(int val)
+{
+ if (val < 0)
+ val = 0;
+ return val;
+}
+
+/* Sort into smallest position first order */
+static int cliplist_cmp(const void *a, const void *b)
+{
+ const struct cliplist *cla = a;
+ const struct cliplist *clb = b;
+ if (cla->position < clb->position)
+ return -1;
+ if (cla->position > clb->position)
+ return 1;
+ return 0;
+}
+
+static int setup_clipping(struct saa7134_dev *dev, struct v4l2_clip *clips,
+ int nclips, int interlace)
+{
+ struct cliplist col[16], row[16];
+ int cols = 0, rows = 0, i;
+ int div = interlace ? 2 : 1;
+
+ memset(col, 0, sizeof(col));
+ memset(row, 0, sizeof(row));
+ for (i = 0; i < nclips && i < 8; i++) {
+ col[cols].position = clip_range(clips[i].c.left);
+ col[cols].enable = (1 << i);
+ cols++;
+ col[cols].position = clip_range(clips[i].c.left+clips[i].c.width);
+ col[cols].disable = (1 << i);
+ cols++;
+ row[rows].position = clip_range(clips[i].c.top / div);
+ row[rows].enable = (1 << i);
+ rows++;
+ row[rows].position = clip_range((clips[i].c.top + clips[i].c.height)
+ / div);
+ row[rows].disable = (1 << i);
+ rows++;
+ }
+ sort(col, cols, sizeof col[0], cliplist_cmp, NULL);
+ sort(row, rows, sizeof row[0], cliplist_cmp, NULL);
+ set_cliplist(dev,0x380,col,cols,"cols");
+ set_cliplist(dev,0x384,row,rows,"rows");
+ return 0;
+}
+
+static int verify_preview(struct saa7134_dev *dev, struct v4l2_window *win)
+{
+ enum v4l2_field field;
+ int maxw, maxh;
+
+ if (NULL == dev->ovbuf.base)
+ return -EINVAL;
+ if (NULL == dev->ovfmt)
+ return -EINVAL;
+ if (win->w.width < 48 || win->w.height < 32)
+ return -EINVAL;
+ if (win->clipcount > 2048)
+ return -EINVAL;
+
+ field = win->field;
+ maxw = dev->crop_current.width;
+ maxh = dev->crop_current.height;
+
+ if (V4L2_FIELD_ANY == field) {
+ field = (win->w.height > maxh/2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_TOP;
+ }
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ win->field = field;
+ if (win->w.width > maxw)
+ win->w.width = maxw;
+ if (win->w.height > maxh)
+ win->w.height = maxh;
+ return 0;
+}
+
+static int start_preview(struct saa7134_dev *dev, struct saa7134_fh *fh)
+{
+ unsigned long base,control,bpl;
+ int err;
+
+ err = verify_preview(dev,&fh->win);
+ if (0 != err)
+ return err;
+
+ dev->ovfield = fh->win.field;
+ dprintk("start_preview %dx%d+%d+%d %s field=%s\n",
+ fh->win.w.width,fh->win.w.height,
+ fh->win.w.left,fh->win.w.top,
+ dev->ovfmt->name,v4l2_field_names[dev->ovfield]);
+
+ /* setup window + clipping */
+ set_size(dev,TASK_B,fh->win.w.width,fh->win.w.height,
+ V4L2_FIELD_HAS_BOTH(dev->ovfield));
+ setup_clipping(dev,fh->clips,fh->nclips,
+ V4L2_FIELD_HAS_BOTH(dev->ovfield));
+ if (dev->ovfmt->yuv)
+ saa_andorb(SAA7134_DATA_PATH(TASK_B), 0x3f, 0x03);
+ else
+ saa_andorb(SAA7134_DATA_PATH(TASK_B), 0x3f, 0x01);
+ saa_writeb(SAA7134_OFMT_VIDEO_B, dev->ovfmt->pm | 0x20);
+
+ /* dma: setup channel 1 (= Video Task B) */
+ base = (unsigned long)dev->ovbuf.base;
+ base += dev->ovbuf.fmt.bytesperline * fh->win.w.top;
+ base += dev->ovfmt->depth/8 * fh->win.w.left;
+ bpl = dev->ovbuf.fmt.bytesperline;
+ control = SAA7134_RS_CONTROL_BURST_16;
+ if (dev->ovfmt->bswap)
+ control |= SAA7134_RS_CONTROL_BSWAP;
+ if (dev->ovfmt->wswap)
+ control |= SAA7134_RS_CONTROL_WSWAP;
+ if (V4L2_FIELD_HAS_BOTH(dev->ovfield)) {
+ saa_writel(SAA7134_RS_BA1(1),base);
+ saa_writel(SAA7134_RS_BA2(1),base+bpl);
+ saa_writel(SAA7134_RS_PITCH(1),bpl*2);
+ saa_writel(SAA7134_RS_CONTROL(1),control);
+ } else {
+ saa_writel(SAA7134_RS_BA1(1),base);
+ saa_writel(SAA7134_RS_BA2(1),base);
+ saa_writel(SAA7134_RS_PITCH(1),bpl);
+ saa_writel(SAA7134_RS_CONTROL(1),control);
+ }
+
+ /* start dma */
+ dev->ovenable = 1;
+ saa7134_set_dmabits(dev);
+
+ return 0;
+}
+
+static int stop_preview(struct saa7134_dev *dev, struct saa7134_fh *fh)
+{
+ dev->ovenable = 0;
+ saa7134_set_dmabits(dev);
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int buffer_activate(struct saa7134_dev *dev,
+ struct saa7134_buf *buf,
+ struct saa7134_buf *next)
+{
+ unsigned long base,control,bpl;
+ unsigned long bpl_uv,lines_uv,base2,base3,tmp; /* planar */
+
+ dprintk("buffer_activate buf=%p\n",buf);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->top_seen = 0;
+
+ set_size(dev,TASK_A,buf->vb.width,buf->vb.height,
+ V4L2_FIELD_HAS_BOTH(buf->vb.field));
+ if (buf->fmt->yuv)
+ saa_andorb(SAA7134_DATA_PATH(TASK_A), 0x3f, 0x03);
+ else
+ saa_andorb(SAA7134_DATA_PATH(TASK_A), 0x3f, 0x01);
+ saa_writeb(SAA7134_OFMT_VIDEO_A, buf->fmt->pm);
+
+ /* DMA: setup channel 0 (= Video Task A0) */
+ base = saa7134_buffer_base(buf);
+ if (buf->fmt->planar)
+ bpl = buf->vb.width;
+ else
+ bpl = (buf->vb.width * buf->fmt->depth) / 8;
+ control = SAA7134_RS_CONTROL_BURST_16 |
+ SAA7134_RS_CONTROL_ME |
+ (buf->pt->dma >> 12);
+ if (buf->fmt->bswap)
+ control |= SAA7134_RS_CONTROL_BSWAP;
+ if (buf->fmt->wswap)
+ control |= SAA7134_RS_CONTROL_WSWAP;
+ if (V4L2_FIELD_HAS_BOTH(buf->vb.field)) {
+ /* interlaced */
+ saa_writel(SAA7134_RS_BA1(0),base);
+ saa_writel(SAA7134_RS_BA2(0),base+bpl);
+ saa_writel(SAA7134_RS_PITCH(0),bpl*2);
+ } else {
+ /* non-interlaced */
+ saa_writel(SAA7134_RS_BA1(0),base);
+ saa_writel(SAA7134_RS_BA2(0),base);
+ saa_writel(SAA7134_RS_PITCH(0),bpl);
+ }
+ saa_writel(SAA7134_RS_CONTROL(0),control);
+
+ if (buf->fmt->planar) {
+ /* DMA: setup channel 4+5 (= planar task A) */
+ bpl_uv = bpl >> buf->fmt->hshift;
+ lines_uv = buf->vb.height >> buf->fmt->vshift;
+ base2 = base + bpl * buf->vb.height;
+ base3 = base2 + bpl_uv * lines_uv;
+ if (buf->fmt->uvswap)
+ tmp = base2, base2 = base3, base3 = tmp;
+ dprintk("uv: bpl=%ld lines=%ld base2/3=%ld/%ld\n",
+ bpl_uv,lines_uv,base2,base3);
+ if (V4L2_FIELD_HAS_BOTH(buf->vb.field)) {
+ /* interlaced */
+ saa_writel(SAA7134_RS_BA1(4),base2);
+ saa_writel(SAA7134_RS_BA2(4),base2+bpl_uv);
+ saa_writel(SAA7134_RS_PITCH(4),bpl_uv*2);
+ saa_writel(SAA7134_RS_BA1(5),base3);
+ saa_writel(SAA7134_RS_BA2(5),base3+bpl_uv);
+ saa_writel(SAA7134_RS_PITCH(5),bpl_uv*2);
+ } else {
+ /* non-interlaced */
+ saa_writel(SAA7134_RS_BA1(4),base2);
+ saa_writel(SAA7134_RS_BA2(4),base2);
+ saa_writel(SAA7134_RS_PITCH(4),bpl_uv);
+ saa_writel(SAA7134_RS_BA1(5),base3);
+ saa_writel(SAA7134_RS_BA2(5),base3);
+ saa_writel(SAA7134_RS_PITCH(5),bpl_uv);
+ }
+ saa_writel(SAA7134_RS_CONTROL(4),control);
+ saa_writel(SAA7134_RS_CONTROL(5),control);
+ }
+
+ /* start DMA */
+ saa7134_set_dmabits(dev);
+ mod_timer(&dev->video_q.timeout, jiffies+BUFFER_TIMEOUT);
+ return 0;
+}
+
+static int buffer_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct saa7134_fh *fh = q->priv_data;
+ struct saa7134_dev *dev = fh->dev;
+ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+ unsigned int size;
+ int err;
+
+ /* sanity checks */
+ if (NULL == fh->fmt)
+ return -EINVAL;
+ if (fh->width < 48 ||
+ fh->height < 32 ||
+ fh->width/4 > dev->crop_current.width ||
+ fh->height/4 > dev->crop_current.height ||
+ fh->width > dev->crop_bounds.width ||
+ fh->height > dev->crop_bounds.height)
+ return -EINVAL;
+ size = (fh->width * fh->height * fh->fmt->depth) >> 3;
+ if (0 != buf->vb.baddr && buf->vb.bsize < size)
+ return -EINVAL;
+
+ dprintk("buffer_prepare [%d,size=%dx%d,bytes=%d,fields=%s,%s]\n",
+ vb->i,fh->width,fh->height,size,v4l2_field_names[field],
+ fh->fmt->name);
+ if (buf->vb.width != fh->width ||
+ buf->vb.height != fh->height ||
+ buf->vb.size != size ||
+ buf->vb.field != field ||
+ buf->fmt != fh->fmt) {
+ saa7134_dma_free(q,buf);
+ }
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+
+ buf->vb.width = fh->width;
+ buf->vb.height = fh->height;
+ buf->vb.size = size;
+ buf->vb.field = field;
+ buf->fmt = fh->fmt;
+ buf->pt = &fh->pt_cap;
+ dev->video_q.curr = NULL;
+
+ err = videobuf_iolock(q,&buf->vb,&dev->ovbuf);
+ if (err)
+ goto oops;
+ err = saa7134_pgtable_build(dev->pci,buf->pt,
+ dma->sglist,
+ dma->sglen,
+ saa7134_buffer_startpage(buf));
+ if (err)
+ goto oops;
+ }
+ buf->vb.state = VIDEOBUF_PREPARED;
+ buf->activate = buffer_activate;
+ return 0;
+
+ oops:
+ saa7134_dma_free(q,buf);
+ return err;
+}
+
+static int
+buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+ struct saa7134_fh *fh = q->priv_data;
+
+ *size = fh->fmt->depth * fh->width * fh->height >> 3;
+ if (0 == *count)
+ *count = gbuffers;
+ *count = saa7134_buffer_count(*size,*count);
+ return 0;
+}
+
+static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct saa7134_fh *fh = q->priv_data;
+ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+
+ saa7134_buffer_queue(fh->dev,&fh->dev->video_q,buf);
+}
+
+static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+
+ saa7134_dma_free(q,buf);
+}
+
+static struct videobuf_queue_ops video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/* ------------------------------------------------------------------ */
+
+int saa7134_g_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c)
+{
+ const struct v4l2_queryctrl* ctrl;
+
+ ctrl = ctrl_by_id(c->id);
+ if (NULL == ctrl)
+ return -EINVAL;
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ c->value = dev->ctl_bright;
+ break;
+ case V4L2_CID_HUE:
+ c->value = dev->ctl_hue;
+ break;
+ case V4L2_CID_CONTRAST:
+ c->value = dev->ctl_contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ c->value = dev->ctl_saturation;
+ break;
+ case V4L2_CID_AUDIO_MUTE:
+ c->value = dev->ctl_mute;
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ c->value = dev->ctl_volume;
+ break;
+ case V4L2_CID_PRIVATE_INVERT:
+ c->value = dev->ctl_invert;
+ break;
+ case V4L2_CID_HFLIP:
+ c->value = dev->ctl_mirror;
+ break;
+ case V4L2_CID_PRIVATE_Y_EVEN:
+ c->value = dev->ctl_y_even;
+ break;
+ case V4L2_CID_PRIVATE_Y_ODD:
+ c->value = dev->ctl_y_odd;
+ break;
+ case V4L2_CID_PRIVATE_AUTOMUTE:
+ c->value = dev->ctl_automute;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7134_g_ctrl_internal);
+
+static int saa7134_g_ctrl(struct file *file, void *priv, struct v4l2_control *c)
+{
+ struct saa7134_fh *fh = priv;
+
+ return saa7134_g_ctrl_internal(fh->dev, fh, c);
+}
+
+int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c)
+{
+ const struct v4l2_queryctrl* ctrl;
+ unsigned long flags;
+ int restart_overlay = 0;
+ int err;
+
+ /* When called from the empress code fh == NULL.
+ That needs to be fixed somehow, but for now this is
+ good enough. */
+ if (fh) {
+ err = v4l2_prio_check(&dev->prio, fh->prio);
+ if (0 != err)
+ return err;
+ }
+ err = -EINVAL;
+
+ mutex_lock(&dev->lock);
+
+ ctrl = ctrl_by_id(c->id);
+ if (NULL == ctrl)
+ goto error;
+
+ dprintk("set_control name=%s val=%d\n",ctrl->name,c->value);
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ case V4L2_CTRL_TYPE_MENU:
+ case V4L2_CTRL_TYPE_INTEGER:
+ if (c->value < ctrl->minimum)
+ c->value = ctrl->minimum;
+ if (c->value > ctrl->maximum)
+ c->value = ctrl->maximum;
+ break;
+ default:
+ /* nothing */;
+ };
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ dev->ctl_bright = c->value;
+ saa_writeb(SAA7134_DEC_LUMA_BRIGHT, dev->ctl_bright);
+ break;
+ case V4L2_CID_HUE:
+ dev->ctl_hue = c->value;
+ saa_writeb(SAA7134_DEC_CHROMA_HUE, dev->ctl_hue);
+ break;
+ case V4L2_CID_CONTRAST:
+ dev->ctl_contrast = c->value;
+ saa_writeb(SAA7134_DEC_LUMA_CONTRAST,
+ dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast);
+ break;
+ case V4L2_CID_SATURATION:
+ dev->ctl_saturation = c->value;
+ saa_writeb(SAA7134_DEC_CHROMA_SATURATION,
+ dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation);
+ break;
+ case V4L2_CID_AUDIO_MUTE:
+ dev->ctl_mute = c->value;
+ saa7134_tvaudio_setmute(dev);
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ dev->ctl_volume = c->value;
+ saa7134_tvaudio_setvolume(dev,dev->ctl_volume);
+ break;
+ case V4L2_CID_PRIVATE_INVERT:
+ dev->ctl_invert = c->value;
+ saa_writeb(SAA7134_DEC_LUMA_CONTRAST,
+ dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast);
+ saa_writeb(SAA7134_DEC_CHROMA_SATURATION,
+ dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation);
+ break;
+ case V4L2_CID_HFLIP:
+ dev->ctl_mirror = c->value;
+ restart_overlay = 1;
+ break;
+ case V4L2_CID_PRIVATE_Y_EVEN:
+ dev->ctl_y_even = c->value;
+ restart_overlay = 1;
+ break;
+ case V4L2_CID_PRIVATE_Y_ODD:
+ dev->ctl_y_odd = c->value;
+ restart_overlay = 1;
+ break;
+ case V4L2_CID_PRIVATE_AUTOMUTE:
+ {
+ struct v4l2_priv_tun_config tda9887_cfg;
+
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &dev->tda9887_conf;
+
+ dev->ctl_automute = c->value;
+ if (dev->tda9887_conf) {
+ if (dev->ctl_automute)
+ dev->tda9887_conf |= TDA9887_AUTOMUTE;
+ else
+ dev->tda9887_conf &= ~TDA9887_AUTOMUTE;
+
+ saa_call_all(dev, tuner, s_config, &tda9887_cfg);
+ }
+ break;
+ }
+ default:
+ goto error;
+ }
+ if (restart_overlay && fh && res_check(fh, RESOURCE_OVERLAY)) {
+ spin_lock_irqsave(&dev->slock,flags);
+ stop_preview(dev,fh);
+ start_preview(dev,fh);
+ spin_unlock_irqrestore(&dev->slock,flags);
+ }
+ err = 0;
+
+error:
+ mutex_unlock(&dev->lock);
+ return err;
+}
+EXPORT_SYMBOL_GPL(saa7134_s_ctrl_internal);
+
+static int saa7134_s_ctrl(struct file *file, void *f, struct v4l2_control *c)
+{
+ struct saa7134_fh *fh = f;
+
+ return saa7134_s_ctrl_internal(fh->dev, fh, c);
+}
+
+/* ------------------------------------------------------------------ */
+
+static struct videobuf_queue* saa7134_queue(struct saa7134_fh *fh)
+{
+ struct videobuf_queue* q = NULL;
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ q = &fh->cap;
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ q = &fh->vbi;
+ break;
+ default:
+ BUG();
+ }
+ return q;
+}
+
+static int saa7134_resource(struct saa7134_fh *fh)
+{
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return RESOURCE_VIDEO;
+
+ if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ return RESOURCE_VBI;
+
+ BUG();
+ return 0;
+}
+
+static int video_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct saa7134_dev *dev = video_drvdata(file);
+ struct saa7134_fh *fh;
+ enum v4l2_buf_type type = 0;
+ int radio = 0;
+
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ break;
+ case VFL_TYPE_VBI:
+ type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ break;
+ case VFL_TYPE_RADIO:
+ radio = 1;
+ break;
+ }
+
+ dprintk("open dev=%s radio=%d type=%s\n", video_device_node_name(vdev),
+ radio, v4l2_type_names[type]);
+
+ /* allocate + initialize per filehandle data */
+ fh = kzalloc(sizeof(*fh),GFP_KERNEL);
+ if (NULL == fh)
+ return -ENOMEM;
+
+ file->private_data = fh;
+ fh->dev = dev;
+ fh->radio = radio;
+ fh->type = type;
+ fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
+ fh->width = 720;
+ fh->height = 576;
+ v4l2_prio_open(&dev->prio, &fh->prio);
+
+ videobuf_queue_sg_init(&fh->cap, &video_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct saa7134_buf),
+ fh, NULL);
+ videobuf_queue_sg_init(&fh->vbi, &saa7134_vbi_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_FIELD_SEQ_TB,
+ sizeof(struct saa7134_buf),
+ fh, NULL);
+ saa7134_pgtable_alloc(dev->pci,&fh->pt_cap);
+ saa7134_pgtable_alloc(dev->pci,&fh->pt_vbi);
+
+ if (fh->radio) {
+ /* switch to radio mode */
+ saa7134_tvaudio_setinput(dev,&card(dev).radio);
+ saa_call_all(dev, tuner, s_radio);
+ } else {
+ /* switch to video/vbi mode */
+ video_mux(dev,dev->ctl_input);
+ }
+ return 0;
+}
+
+static ssize_t
+video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+{
+ struct saa7134_fh *fh = file->private_data;
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (res_locked(fh->dev,RESOURCE_VIDEO))
+ return -EBUSY;
+ return videobuf_read_one(saa7134_queue(fh),
+ data, count, ppos,
+ file->f_flags & O_NONBLOCK);
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ if (!res_get(fh->dev,fh,RESOURCE_VBI))
+ return -EBUSY;
+ return videobuf_read_stream(saa7134_queue(fh),
+ data, count, ppos, 1,
+ file->f_flags & O_NONBLOCK);
+ break;
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+static unsigned int
+video_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct saa7134_fh *fh = file->private_data;
+ struct videobuf_buffer *buf = NULL;
+ unsigned int rc = 0;
+
+ if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type)
+ return videobuf_poll_stream(file, &fh->vbi, wait);
+
+ if (res_check(fh,RESOURCE_VIDEO)) {
+ mutex_lock(&fh->cap.vb_lock);
+ if (!list_empty(&fh->cap.stream))
+ buf = list_entry(fh->cap.stream.next, struct videobuf_buffer, stream);
+ } else {
+ mutex_lock(&fh->cap.vb_lock);
+ if (UNSET == fh->cap.read_off) {
+ /* need to capture a new frame */
+ if (res_locked(fh->dev,RESOURCE_VIDEO))
+ goto err;
+ if (0 != fh->cap.ops->buf_prepare(&fh->cap,fh->cap.read_buf,fh->cap.field))
+ goto err;
+ fh->cap.ops->buf_queue(&fh->cap,fh->cap.read_buf);
+ fh->cap.read_off = 0;
+ }
+ buf = fh->cap.read_buf;
+ }
+
+ if (!buf)
+ goto err;
+
+ poll_wait(file, &buf->done, wait);
+ if (buf->state == VIDEOBUF_DONE ||
+ buf->state == VIDEOBUF_ERROR)
+ rc = POLLIN|POLLRDNORM;
+ mutex_unlock(&fh->cap.vb_lock);
+ return rc;
+
+err:
+ mutex_unlock(&fh->cap.vb_lock);
+ return POLLERR;
+}
+
+static int video_release(struct file *file)
+{
+ struct saa7134_fh *fh = file->private_data;
+ struct saa7134_dev *dev = fh->dev;
+ struct saa6588_command cmd;
+ unsigned long flags;
+
+ saa7134_tvaudio_close(dev);
+
+ /* turn off overlay */
+ if (res_check(fh, RESOURCE_OVERLAY)) {
+ spin_lock_irqsave(&dev->slock,flags);
+ stop_preview(dev,fh);
+ spin_unlock_irqrestore(&dev->slock,flags);
+ res_free(dev,fh,RESOURCE_OVERLAY);
+ }
+
+ /* stop video capture */
+ if (res_check(fh, RESOURCE_VIDEO)) {
+ videobuf_streamoff(&fh->cap);
+ res_free(dev,fh,RESOURCE_VIDEO);
+ }
+ if (fh->cap.read_buf) {
+ buffer_release(&fh->cap,fh->cap.read_buf);
+ kfree(fh->cap.read_buf);
+ }
+
+ /* stop vbi capture */
+ if (res_check(fh, RESOURCE_VBI)) {
+ videobuf_stop(&fh->vbi);
+ res_free(dev,fh,RESOURCE_VBI);
+ }
+
+ /* ts-capture will not work in planar mode, so turn it off Hac: 04.05*/
+ saa_andorb(SAA7134_OFMT_VIDEO_A, 0x1f, 0);
+ saa_andorb(SAA7134_OFMT_VIDEO_B, 0x1f, 0);
+ saa_andorb(SAA7134_OFMT_DATA_A, 0x1f, 0);
+ saa_andorb(SAA7134_OFMT_DATA_B, 0x1f, 0);
+
+ saa_call_all(dev, core, s_power, 0);
+ if (fh->radio)
+ saa_call_all(dev, core, ioctl, SAA6588_CMD_CLOSE, &cmd);
+
+ /* free stuff */
+ videobuf_mmap_free(&fh->cap);
+ videobuf_mmap_free(&fh->vbi);
+ saa7134_pgtable_free(dev->pci,&fh->pt_cap);
+ saa7134_pgtable_free(dev->pci,&fh->pt_vbi);
+
+ v4l2_prio_close(&dev->prio, fh->prio);
+ file->private_data = NULL;
+ kfree(fh);
+ return 0;
+}
+
+static int video_mmap(struct file *file, struct vm_area_struct * vma)
+{
+ struct saa7134_fh *fh = file->private_data;
+
+ return videobuf_mmap_mapper(saa7134_queue(fh), vma);
+}
+
+static ssize_t radio_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct saa7134_fh *fh = file->private_data;
+ struct saa7134_dev *dev = fh->dev;
+ struct saa6588_command cmd;
+
+ cmd.block_count = count/3;
+ cmd.buffer = data;
+ cmd.instance = file;
+ cmd.result = -ENODEV;
+
+ saa_call_all(dev, core, ioctl, SAA6588_CMD_READ, &cmd);
+
+ return cmd.result;
+}
+
+static unsigned int radio_poll(struct file *file, poll_table *wait)
+{
+ struct saa7134_fh *fh = file->private_data;
+ struct saa7134_dev *dev = fh->dev;
+ struct saa6588_command cmd;
+
+ cmd.instance = file;
+ cmd.event_list = wait;
+ cmd.result = -ENODEV;
+ saa_call_all(dev, core, ioctl, SAA6588_CMD_POLL, &cmd);
+
+ return cmd.result;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int saa7134_try_get_set_fmt_vbi_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ struct saa7134_tvnorm *norm = dev->tvnorm;
+
+ f->fmt.vbi.sampling_rate = 6750000 * 4;
+ f->fmt.vbi.samples_per_line = 2048 /* VBI_LINE_LENGTH */;
+ f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+ f->fmt.vbi.offset = 64 * 4;
+ f->fmt.vbi.start[0] = norm->vbi_v_start_0;
+ f->fmt.vbi.count[0] = norm->vbi_v_stop_0 - norm->vbi_v_start_0 +1;
+ f->fmt.vbi.start[1] = norm->vbi_v_start_1;
+ f->fmt.vbi.count[1] = f->fmt.vbi.count[0];
+ f->fmt.vbi.flags = 0; /* VBI_UNSYNC VBI_INTERLACED */
+
+ return 0;
+}
+
+static int saa7134_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct saa7134_fh *fh = priv;
+
+ f->fmt.pix.width = fh->width;
+ f->fmt.pix.height = fh->height;
+ f->fmt.pix.field = fh->cap.field;
+ f->fmt.pix.pixelformat = fh->fmt->fourcc;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fh->fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+ return 0;
+}
+
+static int saa7134_g_fmt_vid_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct saa7134_fh *fh = priv;
+
+ if (saa7134_no_overlay > 0) {
+ printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+ return -EINVAL;
+ }
+ f->fmt.win = fh->win;
+
+ return 0;
+}
+
+static int saa7134_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ struct saa7134_format *fmt;
+ enum v4l2_field field;
+ unsigned int maxw, maxh;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+
+ field = f->fmt.pix.field;
+ maxw = min(dev->crop_current.width*4, dev->crop_bounds.width);
+ maxh = min(dev->crop_current.height*4, dev->crop_bounds.height);
+
+ if (V4L2_FIELD_ANY == field) {
+ field = (f->fmt.pix.height > maxh/2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_BOTTOM;
+ }
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ f->fmt.pix.field = field;
+ if (f->fmt.pix.width < 48)
+ f->fmt.pix.width = 48;
+ if (f->fmt.pix.height < 32)
+ f->fmt.pix.height = 32;
+ if (f->fmt.pix.width > maxw)
+ f->fmt.pix.width = maxw;
+ if (f->fmt.pix.height > maxh)
+ f->fmt.pix.height = maxh;
+ f->fmt.pix.width &= ~0x03;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+static int saa7134_try_fmt_vid_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+
+ if (saa7134_no_overlay > 0) {
+ printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+ return -EINVAL;
+ }
+
+ return verify_preview(dev, &f->fmt.win);
+}
+
+static int saa7134_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct saa7134_fh *fh = priv;
+ int err;
+
+ err = saa7134_try_fmt_vid_cap(file, priv, f);
+ if (0 != err)
+ return err;
+
+ fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ fh->width = f->fmt.pix.width;
+ fh->height = f->fmt.pix.height;
+ fh->cap.field = f->fmt.pix.field;
+ return 0;
+}
+
+static int saa7134_s_fmt_vid_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ int err;
+ unsigned long flags;
+
+ if (saa7134_no_overlay > 0) {
+ printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+ return -EINVAL;
+ }
+ err = verify_preview(dev, &f->fmt.win);
+ if (0 != err)
+ return err;
+
+ mutex_lock(&dev->lock);
+
+ fh->win = f->fmt.win;
+ fh->nclips = f->fmt.win.clipcount;
+
+ if (fh->nclips > 8)
+ fh->nclips = 8;
+
+ if (copy_from_user(fh->clips, f->fmt.win.clips,
+ sizeof(struct v4l2_clip)*fh->nclips)) {
+ mutex_unlock(&dev->lock);
+ return -EFAULT;
+ }
+
+ if (res_check(fh, RESOURCE_OVERLAY)) {
+ spin_lock_irqsave(&dev->slock, flags);
+ stop_preview(dev, fh);
+ start_preview(dev, fh);
+ spin_unlock_irqrestore(&dev->slock, flags);
+ }
+
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+int saa7134_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c)
+{
+ const struct v4l2_queryctrl *ctrl;
+
+ if ((c->id < V4L2_CID_BASE ||
+ c->id >= V4L2_CID_LASTP1) &&
+ (c->id < V4L2_CID_PRIVATE_BASE ||
+ c->id >= V4L2_CID_PRIVATE_LASTP1))
+ return -EINVAL;
+ ctrl = ctrl_by_id(c->id);
+ *c = (NULL != ctrl) ? *ctrl : no_ctrl;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7134_queryctrl);
+
+static int saa7134_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ unsigned int n;
+
+ n = i->index;
+ if (n >= SAA7134_INPUT_MAX)
+ return -EINVAL;
+ if (NULL == card_in(dev, i->index).name)
+ return -EINVAL;
+ i->index = n;
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ strcpy(i->name, card_in(dev, n).name);
+ if (card_in(dev, n).tv)
+ i->type = V4L2_INPUT_TYPE_TUNER;
+ i->audioset = 1;
+ if (n == dev->ctl_input) {
+ int v1 = saa_readb(SAA7134_STATUS_VIDEO1);
+ int v2 = saa_readb(SAA7134_STATUS_VIDEO2);
+
+ if (0 != (v1 & 0x40))
+ i->status |= V4L2_IN_ST_NO_H_LOCK;
+ if (0 != (v2 & 0x40))
+ i->status |= V4L2_IN_ST_NO_SYNC;
+ if (0 != (v2 & 0x0e))
+ i->status |= V4L2_IN_ST_MACROVISION;
+ }
+ i->std = SAA7134_NORMS;
+ return 0;
+}
+
+static int saa7134_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+
+ *i = dev->ctl_input;
+ return 0;
+}
+
+static int saa7134_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ int err;
+
+ err = v4l2_prio_check(&dev->prio, fh->prio);
+ if (0 != err)
+ return err;
+
+ if (i >= SAA7134_INPUT_MAX)
+ return -EINVAL;
+ if (NULL == card_in(dev, i).name)
+ return -EINVAL;
+ mutex_lock(&dev->lock);
+ video_mux(dev, i);
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int saa7134_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+
+ unsigned int tuner_type = dev->tuner_type;
+
+ strcpy(cap->driver, "saa7134");
+ strlcpy(cap->card, saa7134_boards[dev->board].name,
+ sizeof(cap->card));
+ sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VBI_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING |
+ V4L2_CAP_TUNER;
+ if (dev->has_rds)
+ cap->capabilities |= V4L2_CAP_RDS_CAPTURE;
+ if (saa7134_no_overlay <= 0)
+ cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY;
+
+ if ((tuner_type == TUNER_ABSENT) || (tuner_type == UNSET))
+ cap->capabilities &= ~V4L2_CAP_TUNER;
+ return 0;
+}
+
+int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_std_id *id)
+{
+ unsigned long flags;
+ unsigned int i;
+ v4l2_std_id fixup;
+ int err;
+
+ /* When called from the empress code fh == NULL.
+ That needs to be fixed somehow, but for now this is
+ good enough. */
+ if (fh) {
+ err = v4l2_prio_check(&dev->prio, fh->prio);
+ if (0 != err)
+ return err;
+ } else if (res_locked(dev, RESOURCE_OVERLAY)) {
+ /* Don't change the std from the mpeg device
+ if overlay is active. */
+ return -EBUSY;
+ }
+
+ for (i = 0; i < TVNORMS; i++)
+ if (*id == tvnorms[i].id)
+ break;
+
+ if (i == TVNORMS)
+ for (i = 0; i < TVNORMS; i++)
+ if (*id & tvnorms[i].id)
+ break;
+ if (i == TVNORMS)
+ return -EINVAL;
+
+ if ((*id & V4L2_STD_SECAM) && (secam[0] != '-')) {
+ if (secam[0] == 'L' || secam[0] == 'l') {
+ if (secam[1] == 'C' || secam[1] == 'c')
+ fixup = V4L2_STD_SECAM_LC;
+ else
+ fixup = V4L2_STD_SECAM_L;
+ } else {
+ if (secam[0] == 'D' || secam[0] == 'd')
+ fixup = V4L2_STD_SECAM_DK;
+ else
+ fixup = V4L2_STD_SECAM;
+ }
+ for (i = 0; i < TVNORMS; i++) {
+ if (fixup == tvnorms[i].id)
+ break;
+ }
+ if (i == TVNORMS)
+ return -EINVAL;
+ }
+
+ *id = tvnorms[i].id;
+
+ mutex_lock(&dev->lock);
+ if (fh && res_check(fh, RESOURCE_OVERLAY)) {
+ spin_lock_irqsave(&dev->slock, flags);
+ stop_preview(dev, fh);
+ spin_unlock_irqrestore(&dev->slock, flags);
+
+ set_tvnorm(dev, &tvnorms[i]);
+
+ spin_lock_irqsave(&dev->slock, flags);
+ start_preview(dev, fh);
+ spin_unlock_irqrestore(&dev->slock, flags);
+ } else
+ set_tvnorm(dev, &tvnorms[i]);
+
+ saa7134_tvaudio_do_scan(dev);
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7134_s_std_internal);
+
+static int saa7134_s_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct saa7134_fh *fh = priv;
+
+ return saa7134_s_std_internal(fh->dev, fh, id);
+}
+
+static int saa7134_g_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+
+ *id = dev->tvnorm->id;
+ return 0;
+}
+
+static int saa7134_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cap)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+
+ if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
+ cap->bounds = dev->crop_bounds;
+ cap->defrect = dev->crop_defrect;
+ cap->pixelaspect.numerator = 1;
+ cap->pixelaspect.denominator = 1;
+ if (dev->tvnorm->id & V4L2_STD_525_60) {
+ cap->pixelaspect.numerator = 11;
+ cap->pixelaspect.denominator = 10;
+ }
+ if (dev->tvnorm->id & V4L2_STD_625_50) {
+ cap->pixelaspect.numerator = 54;
+ cap->pixelaspect.denominator = 59;
+ }
+ return 0;
+}
+
+static int saa7134_g_crop(struct file *file, void *f, struct v4l2_crop *crop)
+{
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
+ crop->c = dev->crop_current;
+ return 0;
+}
+
+static int saa7134_s_crop(struct file *file, void *f, struct v4l2_crop *crop)
+{
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
+ struct v4l2_rect *b = &dev->crop_bounds;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
+ if (crop->c.height < 0)
+ return -EINVAL;
+ if (crop->c.width < 0)
+ return -EINVAL;
+
+ if (res_locked(fh->dev, RESOURCE_OVERLAY))
+ return -EBUSY;
+ if (res_locked(fh->dev, RESOURCE_VIDEO))
+ return -EBUSY;
+
+ if (crop->c.top < b->top)
+ crop->c.top = b->top;
+ if (crop->c.top > b->top + b->height)
+ crop->c.top = b->top + b->height;
+ if (crop->c.height > b->top - crop->c.top + b->height)
+ crop->c.height = b->top - crop->c.top + b->height;
+
+ if (crop->c.left < b->left)
+ crop->c.left = b->left;
+ if (crop->c.left > b->left + b->width)
+ crop->c.left = b->left + b->width;
+ if (crop->c.width > b->left - crop->c.left + b->width)
+ crop->c.width = b->left - crop->c.left + b->width;
+
+ dev->crop_current = crop->c;
+ return 0;
+}
+
+static int saa7134_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ int n;
+
+ if (0 != t->index)
+ return -EINVAL;
+ memset(t, 0, sizeof(*t));
+ for (n = 0; n < SAA7134_INPUT_MAX; n++) {
+ if (card_in(dev, n).tv)
+ break;
+ }
+ if (n == SAA7134_INPUT_MAX)
+ return -EINVAL;
+ if (NULL != card_in(dev, n).name) {
+ strcpy(t->name, "Television");
+ t->type = V4L2_TUNER_ANALOG_TV;
+ t->capability = V4L2_TUNER_CAP_NORM |
+ V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 |
+ V4L2_TUNER_CAP_LANG2;
+ t->rangehigh = 0xffffffffUL;
+ t->rxsubchans = saa7134_tvaudio_getstereo(dev);
+ t->audmode = saa7134_tvaudio_rx2mode(t->rxsubchans);
+ }
+ if (0 != (saa_readb(SAA7134_STATUS_VIDEO1) & 0x03))
+ t->signal = 0xffff;
+ return 0;
+}
+
+static int saa7134_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ int rx, mode, err;
+
+ err = v4l2_prio_check(&dev->prio, fh->prio);
+ if (0 != err)
+ return err;
+
+ mode = dev->thread.mode;
+ if (UNSET == mode) {
+ rx = saa7134_tvaudio_getstereo(dev);
+ mode = saa7134_tvaudio_rx2mode(t->rxsubchans);
+ }
+ if (mode != t->audmode)
+ dev->thread.mode = t->audmode;
+
+ return 0;
+}
+
+static int saa7134_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+
+ f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ f->frequency = dev->ctl_freq;
+
+ return 0;
+}
+
+static int saa7134_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ int err;
+
+ err = v4l2_prio_check(&dev->prio, fh->prio);
+ if (0 != err)
+ return err;
+
+ if (0 != f->tuner)
+ return -EINVAL;
+ if (0 == fh->radio && V4L2_TUNER_ANALOG_TV != f->type)
+ return -EINVAL;
+ if (1 == fh->radio && V4L2_TUNER_RADIO != f->type)
+ return -EINVAL;
+ mutex_lock(&dev->lock);
+ dev->ctl_freq = f->frequency;
+
+ saa_call_all(dev, tuner, s_frequency, f);
+
+ saa7134_tvaudio_do_scan(dev);
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int saa7134_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ strcpy(a->name, "audio");
+ return 0;
+}
+
+static int saa7134_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ return 0;
+}
+
+static int saa7134_g_priority(struct file *file, void *f, enum v4l2_priority *p)
+{
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
+
+ *p = v4l2_prio_max(&dev->prio);
+ return 0;
+}
+
+static int saa7134_s_priority(struct file *file, void *f,
+ enum v4l2_priority prio)
+{
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
+
+ return v4l2_prio_change(&dev->prio, &fh->prio, prio);
+}
+
+static int saa7134_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index >= FORMATS)
+ return -EINVAL;
+
+ strlcpy(f->description, formats[f->index].name,
+ sizeof(f->description));
+
+ f->pixelformat = formats[f->index].fourcc;
+
+ return 0;
+}
+
+static int saa7134_enum_fmt_vid_overlay(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (saa7134_no_overlay > 0) {
+ printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+ return -EINVAL;
+ }
+
+ if ((f->index >= FORMATS) || formats[f->index].planar)
+ return -EINVAL;
+
+ strlcpy(f->description, formats[f->index].name,
+ sizeof(f->description));
+
+ f->pixelformat = formats[f->index].fourcc;
+
+ return 0;
+}
+
+static int saa7134_g_fbuf(struct file *file, void *f,
+ struct v4l2_framebuffer *fb)
+{
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
+
+ *fb = dev->ovbuf;
+ fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+
+ return 0;
+}
+
+static int saa7134_s_fbuf(struct file *file, void *f,
+ struct v4l2_framebuffer *fb)
+{
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
+ struct saa7134_format *fmt;
+
+ if (!capable(CAP_SYS_ADMIN) &&
+ !capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ /* check args */
+ fmt = format_by_fourcc(fb->fmt.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+
+ /* ok, accept it */
+ dev->ovbuf = *fb;
+ dev->ovfmt = fmt;
+ if (0 == dev->ovbuf.fmt.bytesperline)
+ dev->ovbuf.fmt.bytesperline =
+ dev->ovbuf.fmt.width*fmt->depth/8;
+ return 0;
+}
+
+static int saa7134_overlay(struct file *file, void *f, unsigned int on)
+{
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
+ unsigned long flags;
+
+ if (on) {
+ if (saa7134_no_overlay > 0) {
+ dprintk("no_overlay\n");
+ return -EINVAL;
+ }
+
+ if (!res_get(dev, fh, RESOURCE_OVERLAY))
+ return -EBUSY;
+ spin_lock_irqsave(&dev->slock, flags);
+ start_preview(dev, fh);
+ spin_unlock_irqrestore(&dev->slock, flags);
+ }
+ if (!on) {
+ if (!res_check(fh, RESOURCE_OVERLAY))
+ return -EINVAL;
+ spin_lock_irqsave(&dev->slock, flags);
+ stop_preview(dev, fh);
+ spin_unlock_irqrestore(&dev->slock, flags);
+ res_free(dev, fh, RESOURCE_OVERLAY);
+ }
+ return 0;
+}
+
+static int saa7134_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct saa7134_fh *fh = priv;
+ return videobuf_reqbufs(saa7134_queue(fh), p);
+}
+
+static int saa7134_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct saa7134_fh *fh = priv;
+ return videobuf_querybuf(saa7134_queue(fh), b);
+}
+
+static int saa7134_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct saa7134_fh *fh = priv;
+ return videobuf_qbuf(saa7134_queue(fh), b);
+}
+
+static int saa7134_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct saa7134_fh *fh = priv;
+ return videobuf_dqbuf(saa7134_queue(fh), b,
+ file->f_flags & O_NONBLOCK);
+}
+
+static int saa7134_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ int res = saa7134_resource(fh);
+
+ if (!res_get(dev, fh, res))
+ return -EBUSY;
+
+ return videobuf_streamon(saa7134_queue(fh));
+}
+
+static int saa7134_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ int err;
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ int res = saa7134_resource(fh);
+
+ err = videobuf_streamoff(saa7134_queue(fh));
+ if (err < 0)
+ return err;
+ res_free(dev, fh, res);
+ return 0;
+}
+
+static int saa7134_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *parm)
+{
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vidioc_g_register (struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+ reg->val = saa_readb(reg->reg);
+ reg->size = 1;
+ return 0;
+}
+
+static int vidioc_s_register (struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+ saa_writeb(reg->reg&0xffffff, reg->val);
+ return 0;
+}
+#endif
+
+static int radio_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct saa7134_fh *fh = file->private_data;
+ struct saa7134_dev *dev = fh->dev;
+
+ strcpy(cap->driver, "saa7134");
+ strlcpy(cap->card, saa7134_boards[dev->board].name, sizeof(cap->card));
+ sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+ cap->capabilities = V4L2_CAP_TUNER;
+ return 0;
+}
+
+static int radio_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct saa7134_fh *fh = file->private_data;
+ struct saa7134_dev *dev = fh->dev;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ memset(t, 0, sizeof(*t));
+ strcpy(t->name, "Radio");
+ t->type = V4L2_TUNER_RADIO;
+
+ saa_call_all(dev, tuner, g_tuner, t);
+ if (dev->input->amux == TV) {
+ t->signal = 0xf800 - ((saa_readb(0x581) & 0x1f) << 11);
+ t->rxsubchans = (saa_readb(0x529) & 0x08) ?
+ V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
+ }
+ return 0;
+}
+static int radio_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct saa7134_fh *fh = file->private_data;
+ struct saa7134_dev *dev = fh->dev;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ saa_call_all(dev, tuner, s_tuner, t);
+ return 0;
+}
+
+static int radio_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
+
+ strcpy(i->name, "Radio");
+ i->type = V4L2_INPUT_TYPE_TUNER;
+
+ return 0;
+}
+
+static int radio_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int radio_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ memset(a, 0, sizeof(*a));
+ strcpy(a->name, "Radio");
+ return 0;
+}
+
+static int radio_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ return 0;
+}
+
+static int radio_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ return 0;
+}
+
+static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
+ return 0;
+}
+
+static int radio_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ const struct v4l2_queryctrl *ctrl;
+
+ if (c->id < V4L2_CID_BASE ||
+ c->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+ if (c->id == V4L2_CID_AUDIO_MUTE) {
+ ctrl = ctrl_by_id(c->id);
+ *c = *ctrl;
+ } else
+ *c = no_ctrl;
+ return 0;
+}
+
+static const struct v4l2_file_operations video_fops =
+{
+ .owner = THIS_MODULE,
+ .open = video_open,
+ .release = video_release,
+ .read = video_read,
+ .poll = video_poll,
+ .mmap = video_mmap,
+ .ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops video_ioctl_ops = {
+ .vidioc_querycap = saa7134_querycap,
+ .vidioc_enum_fmt_vid_cap = saa7134_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = saa7134_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = saa7134_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = saa7134_s_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_overlay = saa7134_enum_fmt_vid_overlay,
+ .vidioc_g_fmt_vid_overlay = saa7134_g_fmt_vid_overlay,
+ .vidioc_try_fmt_vid_overlay = saa7134_try_fmt_vid_overlay,
+ .vidioc_s_fmt_vid_overlay = saa7134_s_fmt_vid_overlay,
+ .vidioc_g_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap,
+ .vidioc_try_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap,
+ .vidioc_s_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap,
+ .vidioc_g_audio = saa7134_g_audio,
+ .vidioc_s_audio = saa7134_s_audio,
+ .vidioc_cropcap = saa7134_cropcap,
+ .vidioc_reqbufs = saa7134_reqbufs,
+ .vidioc_querybuf = saa7134_querybuf,
+ .vidioc_qbuf = saa7134_qbuf,
+ .vidioc_dqbuf = saa7134_dqbuf,
+ .vidioc_s_std = saa7134_s_std,
+ .vidioc_g_std = saa7134_g_std,
+ .vidioc_enum_input = saa7134_enum_input,
+ .vidioc_g_input = saa7134_g_input,
+ .vidioc_s_input = saa7134_s_input,
+ .vidioc_queryctrl = saa7134_queryctrl,
+ .vidioc_g_ctrl = saa7134_g_ctrl,
+ .vidioc_s_ctrl = saa7134_s_ctrl,
+ .vidioc_streamon = saa7134_streamon,
+ .vidioc_streamoff = saa7134_streamoff,
+ .vidioc_g_tuner = saa7134_g_tuner,
+ .vidioc_s_tuner = saa7134_s_tuner,
+ .vidioc_g_crop = saa7134_g_crop,
+ .vidioc_s_crop = saa7134_s_crop,
+ .vidioc_g_fbuf = saa7134_g_fbuf,
+ .vidioc_s_fbuf = saa7134_s_fbuf,
+ .vidioc_overlay = saa7134_overlay,
+ .vidioc_g_priority = saa7134_g_priority,
+ .vidioc_s_priority = saa7134_s_priority,
+ .vidioc_g_parm = saa7134_g_parm,
+ .vidioc_g_frequency = saa7134_g_frequency,
+ .vidioc_s_frequency = saa7134_s_frequency,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+#endif
+};
+
+static const struct v4l2_file_operations radio_fops = {
+ .owner = THIS_MODULE,
+ .open = video_open,
+ .read = radio_read,
+ .release = video_release,
+ .ioctl = video_ioctl2,
+ .poll = radio_poll,
+};
+
+static const struct v4l2_ioctl_ops radio_ioctl_ops = {
+ .vidioc_querycap = radio_querycap,
+ .vidioc_g_tuner = radio_g_tuner,
+ .vidioc_enum_input = radio_enum_input,
+ .vidioc_g_audio = radio_g_audio,
+ .vidioc_s_tuner = radio_s_tuner,
+ .vidioc_s_audio = radio_s_audio,
+ .vidioc_s_input = radio_s_input,
+ .vidioc_s_std = radio_s_std,
+ .vidioc_queryctrl = radio_queryctrl,
+ .vidioc_g_input = radio_g_input,
+ .vidioc_g_ctrl = saa7134_g_ctrl,
+ .vidioc_s_ctrl = saa7134_s_ctrl,
+ .vidioc_g_frequency = saa7134_g_frequency,
+ .vidioc_s_frequency = saa7134_s_frequency,
+};
+
+/* ----------------------------------------------------------- */
+/* exported stuff */
+
+struct video_device saa7134_video_template = {
+ .name = "saa7134-video",
+ .fops = &video_fops,
+ .ioctl_ops = &video_ioctl_ops,
+ .tvnorms = SAA7134_NORMS,
+ .current_norm = V4L2_STD_PAL,
+};
+
+struct video_device saa7134_radio_template = {
+ .name = "saa7134-radio",
+ .fops = &radio_fops,
+ .ioctl_ops = &radio_ioctl_ops,
+};
+
+int saa7134_video_init1(struct saa7134_dev *dev)
+{
+ /* sanitycheck insmod options */
+ if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME)
+ gbuffers = 2;
+ if (gbufsize < 0 || gbufsize > gbufsize_max)
+ gbufsize = gbufsize_max;
+ gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK;
+
+ /* put some sensible defaults into the data structures ... */
+ dev->ctl_bright = ctrl_by_id(V4L2_CID_BRIGHTNESS)->default_value;
+ dev->ctl_contrast = ctrl_by_id(V4L2_CID_CONTRAST)->default_value;
+ dev->ctl_hue = ctrl_by_id(V4L2_CID_HUE)->default_value;
+ dev->ctl_saturation = ctrl_by_id(V4L2_CID_SATURATION)->default_value;
+ dev->ctl_volume = ctrl_by_id(V4L2_CID_AUDIO_VOLUME)->default_value;
+ dev->ctl_mute = 1; // ctrl_by_id(V4L2_CID_AUDIO_MUTE)->default_value;
+ dev->ctl_invert = ctrl_by_id(V4L2_CID_PRIVATE_INVERT)->default_value;
+ dev->ctl_automute = ctrl_by_id(V4L2_CID_PRIVATE_AUTOMUTE)->default_value;
+
+ if (dev->tda9887_conf && dev->ctl_automute)
+ dev->tda9887_conf |= TDA9887_AUTOMUTE;
+ dev->automute = 0;
+
+ INIT_LIST_HEAD(&dev->video_q.queue);
+ init_timer(&dev->video_q.timeout);
+ dev->video_q.timeout.function = saa7134_buffer_timeout;
+ dev->video_q.timeout.data = (unsigned long)(&dev->video_q);
+ dev->video_q.dev = dev;
+
+ if (saa7134_boards[dev->board].video_out)
+ saa7134_videoport_init(dev);
+
+ return 0;
+}
+
+int saa7134_videoport_init(struct saa7134_dev *dev)
+{
+ /* enable video output */
+ int vo = saa7134_boards[dev->board].video_out;
+ int video_reg;
+ unsigned int vid_port_opts = saa7134_boards[dev->board].vid_port_opts;
+
+ /* Configure videoport */
+ saa_writeb(SAA7134_VIDEO_PORT_CTRL0, video_out[vo][0]);
+ video_reg = video_out[vo][1];
+ if (vid_port_opts & SET_T_CODE_POLARITY_NON_INVERTED)
+ video_reg &= ~VP_T_CODE_P_INVERTED;
+ saa_writeb(SAA7134_VIDEO_PORT_CTRL1, video_reg);
+ saa_writeb(SAA7134_VIDEO_PORT_CTRL2, video_out[vo][2]);
+ saa_writeb(SAA7134_VIDEO_PORT_CTRL4, video_out[vo][4]);
+ video_reg = video_out[vo][5];
+ if (vid_port_opts & SET_CLOCK_NOT_DELAYED)
+ video_reg &= ~VP_CLK_CTRL2_DELAYED;
+ if (vid_port_opts & SET_CLOCK_INVERTED)
+ video_reg |= VP_CLK_CTRL1_INVERTED;
+ saa_writeb(SAA7134_VIDEO_PORT_CTRL5, video_reg);
+ video_reg = video_out[vo][6];
+ if (vid_port_opts & SET_VSYNC_OFF) {
+ video_reg &= ~VP_VS_TYPE_MASK;
+ video_reg |= VP_VS_TYPE_OFF;
+ }
+ saa_writeb(SAA7134_VIDEO_PORT_CTRL6, video_reg);
+ saa_writeb(SAA7134_VIDEO_PORT_CTRL7, video_out[vo][7]);
+ saa_writeb(SAA7134_VIDEO_PORT_CTRL8, video_out[vo][8]);
+
+ /* Start videoport */
+ saa_writeb(SAA7134_VIDEO_PORT_CTRL3, video_out[vo][3]);
+
+ return 0;
+}
+
+int saa7134_video_init2(struct saa7134_dev *dev)
+{
+ /* init video hw */
+ set_tvnorm(dev,&tvnorms[0]);
+ video_mux(dev,0);
+ saa7134_tvaudio_setmute(dev);
+ saa7134_tvaudio_setvolume(dev,dev->ctl_volume);
+ return 0;
+}
+
+void saa7134_irq_video_signalchange(struct saa7134_dev *dev)
+{
+ static const char *st[] = {
+ "(no signal)", "NTSC", "PAL", "SECAM" };
+ u32 st1,st2;
+
+ st1 = saa_readb(SAA7134_STATUS_VIDEO1);
+ st2 = saa_readb(SAA7134_STATUS_VIDEO2);
+ dprintk("DCSDT: pll: %s, sync: %s, norm: %s\n",
+ (st1 & 0x40) ? "not locked" : "locked",
+ (st2 & 0x40) ? "no" : "yes",
+ st[st1 & 0x03]);
+ dev->nosignal = (st1 & 0x40) || (st2 & 0x40) || !(st2 & 0x1);
+
+ if (dev->nosignal) {
+ /* no video signal -> mute audio */
+ if (dev->ctl_automute)
+ dev->automute = 1;
+ saa7134_tvaudio_setmute(dev);
+ } else {
+ /* wake up tvaudio audio carrier scan thread */
+ saa7134_tvaudio_do_scan(dev);
+ }
+
+ if ((st2 & 0x80) && !noninterlaced && !dev->nosignal)
+ saa_clearb(SAA7134_SYNC_CTRL, 0x20);
+ else
+ saa_setb(SAA7134_SYNC_CTRL, 0x20);
+
+ if (dev->mops && dev->mops->signal_change)
+ dev->mops->signal_change(dev);
+}
+
+
+void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status)
+{
+ enum v4l2_field field;
+
+ spin_lock(&dev->slock);
+ if (dev->video_q.curr) {
+ dev->video_fieldcount++;
+ field = dev->video_q.curr->vb.field;
+ if (V4L2_FIELD_HAS_BOTH(field)) {
+ /* make sure we have seen both fields */
+ if ((status & 0x10) == 0x00) {
+ dev->video_q.curr->top_seen = 1;
+ goto done;
+ }
+ if (!dev->video_q.curr->top_seen)
+ goto done;
+ } else if (field == V4L2_FIELD_TOP) {
+ if ((status & 0x10) != 0x10)
+ goto done;
+ } else if (field == V4L2_FIELD_BOTTOM) {
+ if ((status & 0x10) != 0x00)
+ goto done;
+ }
+ dev->video_q.curr->vb.field_count = dev->video_fieldcount;
+ saa7134_buffer_finish(dev,&dev->video_q,VIDEOBUF_DONE);
+ }
+ saa7134_buffer_next(dev,&dev->video_q);
+
+ done:
+ spin_unlock(&dev->slock);
+}
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
new file mode 100644
index 00000000..f625060e
--- /dev/null
+++ b/drivers/media/video/saa7134/saa7134.h
@@ -0,0 +1,855 @@
+/*
+ *
+ * v4l2 device driver for philips saa7134 based TV cards
+ *
+ * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define SAA7134_VERSION "0, 2, 17"
+
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <linux/kdev_t.h>
+#include <linux/input.h>
+#include <linux/notifier.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+
+#include <asm/io.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/tuner.h>
+#include <media/rc-core.h>
+#include <media/ir-kbd-i2c.h>
+#include <media/videobuf-dma-sg.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#if defined(CONFIG_VIDEO_SAA7134_DVB) || defined(CONFIG_VIDEO_SAA7134_DVB_MODULE)
+#include <media/videobuf-dvb.h>
+#endif
+
+#define UNSET (-1U)
+
+/* ----------------------------------------------------------- */
+/* enums */
+
+enum saa7134_tvaudio_mode {
+ TVAUDIO_FM_MONO = 1,
+ TVAUDIO_FM_BG_STEREO = 2,
+ TVAUDIO_FM_SAT_STEREO = 3,
+ TVAUDIO_FM_K_STEREO = 4,
+ TVAUDIO_NICAM_AM = 5,
+ TVAUDIO_NICAM_FM = 6,
+};
+
+enum saa7134_audio_in {
+ TV = 1,
+ LINE1 = 2,
+ LINE2 = 3,
+ LINE2_LEFT,
+};
+
+enum saa7134_video_out {
+ CCIR656 = 1,
+};
+
+/* ----------------------------------------------------------- */
+/* static data */
+
+struct saa7134_tvnorm {
+ char *name;
+ v4l2_std_id id;
+
+ /* video decoder */
+ unsigned int sync_control;
+ unsigned int luma_control;
+ unsigned int chroma_ctrl1;
+ unsigned int chroma_gain;
+ unsigned int chroma_ctrl2;
+ unsigned int vgate_misc;
+
+ /* video scaler */
+ unsigned int h_start;
+ unsigned int h_stop;
+ unsigned int video_v_start;
+ unsigned int video_v_stop;
+ unsigned int vbi_v_start_0;
+ unsigned int vbi_v_stop_0;
+ unsigned int src_timing;
+ unsigned int vbi_v_start_1;
+};
+
+struct saa7134_tvaudio {
+ char *name;
+ v4l2_std_id std;
+ enum saa7134_tvaudio_mode mode;
+ int carr1;
+ int carr2;
+};
+
+struct saa7134_format {
+ char *name;
+ unsigned int fourcc;
+ unsigned int depth;
+ unsigned int pm;
+ unsigned int vshift; /* vertical downsampling (for planar yuv) */
+ unsigned int hshift; /* horizontal downsampling (for planar yuv) */
+ unsigned int bswap:1;
+ unsigned int wswap:1;
+ unsigned int yuv:1;
+ unsigned int planar:1;
+ unsigned int uvswap:1;
+};
+
+struct saa7134_card_ir {
+ struct rc_dev *dev;
+
+ char name[32];
+ char phys[32];
+ unsigned users;
+
+ u32 polling;
+ u32 last_gpio;
+ u32 mask_keycode, mask_keydown, mask_keyup;
+
+ bool running;
+ bool active;
+
+ struct timer_list timer;
+
+ /* IR core raw decoding */
+ u32 raw_decode;
+};
+
+/* ----------------------------------------------------------- */
+/* card configuration */
+
+#define SAA7134_BOARD_NOAUTO UNSET
+#define SAA7134_BOARD_UNKNOWN 0
+#define SAA7134_BOARD_PROTEUS_PRO 1
+#define SAA7134_BOARD_FLYVIDEO3000 2
+#define SAA7134_BOARD_FLYVIDEO2000 3
+#define SAA7134_BOARD_EMPRESS 4
+#define SAA7134_BOARD_MONSTERTV 5
+#define SAA7134_BOARD_MD9717 6
+#define SAA7134_BOARD_TVSTATION_RDS 7
+#define SAA7134_BOARD_CINERGY400 8
+#define SAA7134_BOARD_MD5044 9
+#define SAA7134_BOARD_KWORLD 10
+#define SAA7134_BOARD_CINERGY600 11
+#define SAA7134_BOARD_MD7134 12
+#define SAA7134_BOARD_TYPHOON_90031 13
+#define SAA7134_BOARD_ELSA 14
+#define SAA7134_BOARD_ELSA_500TV 15
+#define SAA7134_BOARD_ASUSTeK_TVFM7134 16
+#define SAA7134_BOARD_VA1000POWER 17
+#define SAA7134_BOARD_BMK_MPEX_NOTUNER 18
+#define SAA7134_BOARD_VIDEOMATE_TV 19
+#define SAA7134_BOARD_CRONOS_PLUS 20
+#define SAA7134_BOARD_10MOONSTVMASTER 21
+#define SAA7134_BOARD_MD2819 22
+#define SAA7134_BOARD_BMK_MPEX_TUNER 23
+#define SAA7134_BOARD_TVSTATION_DVR 24
+#define SAA7134_BOARD_ASUSTEK_TVFM7133 25
+#define SAA7134_BOARD_PINNACLE_PCTV_STEREO 26
+#define SAA7134_BOARD_MANLI_MTV002 27
+#define SAA7134_BOARD_MANLI_MTV001 28
+#define SAA7134_BOARD_TG3000TV 29
+#define SAA7134_BOARD_ECS_TVP3XP 30
+#define SAA7134_BOARD_ECS_TVP3XP_4CB5 31
+#define SAA7134_BOARD_AVACSSMARTTV 32
+#define SAA7134_BOARD_AVERMEDIA_DVD_EZMAKER 33
+#define SAA7134_BOARD_NOVAC_PRIMETV7133 34
+#define SAA7134_BOARD_AVERMEDIA_STUDIO_305 35
+#define SAA7134_BOARD_UPMOST_PURPLE_TV 36
+#define SAA7134_BOARD_ITEMS_MTV005 37
+#define SAA7134_BOARD_CINERGY200 38
+#define SAA7134_BOARD_FLYTVPLATINUM_MINI 39
+#define SAA7134_BOARD_VIDEOMATE_TV_PVR 40
+#define SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUS 41
+#define SAA7134_BOARD_SABRENT_SBTTVFM 42
+#define SAA7134_BOARD_ZOLID_XPERT_TV7134 43
+#define SAA7134_BOARD_EMPIRE_PCI_TV_RADIO_LE 44
+#define SAA7134_BOARD_AVERMEDIA_STUDIO_307 45
+#define SAA7134_BOARD_AVERMEDIA_CARDBUS 46
+#define SAA7134_BOARD_CINERGY400_CARDBUS 47
+#define SAA7134_BOARD_CINERGY600_MK3 48
+#define SAA7134_BOARD_VIDEOMATE_GOLD_PLUS 49
+#define SAA7134_BOARD_PINNACLE_300I_DVBT_PAL 50
+#define SAA7134_BOARD_PROVIDEO_PV952 51
+#define SAA7134_BOARD_AVERMEDIA_305 52
+#define SAA7134_BOARD_ASUSTeK_TVFM7135 53
+#define SAA7134_BOARD_FLYTVPLATINUM_FM 54
+#define SAA7134_BOARD_FLYDVBTDUO 55
+#define SAA7134_BOARD_AVERMEDIA_307 56
+#define SAA7134_BOARD_AVERMEDIA_GO_007_FM 57
+#define SAA7134_BOARD_ADS_INSTANT_TV 58
+#define SAA7134_BOARD_KWORLD_VSTREAM_XPERT 59
+#define SAA7134_BOARD_FLYDVBT_DUO_CARDBUS 60
+#define SAA7134_BOARD_PHILIPS_TOUGH 61
+#define SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII 62
+#define SAA7134_BOARD_KWORLD_XPERT 63
+#define SAA7134_BOARD_FLYTV_DIGIMATRIX 64
+#define SAA7134_BOARD_KWORLD_TERMINATOR 65
+#define SAA7134_BOARD_YUAN_TUN900 66
+#define SAA7134_BOARD_BEHOLD_409FM 67
+#define SAA7134_BOARD_GOTVIEW_7135 68
+#define SAA7134_BOARD_PHILIPS_EUROPA 69
+#define SAA7134_BOARD_VIDEOMATE_DVBT_300 70
+#define SAA7134_BOARD_VIDEOMATE_DVBT_200 71
+#define SAA7134_BOARD_RTD_VFG7350 72
+#define SAA7134_BOARD_RTD_VFG7330 73
+#define SAA7134_BOARD_FLYTVPLATINUM_MINI2 74
+#define SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180 75
+#define SAA7134_BOARD_MONSTERTV_MOBILE 76
+#define SAA7134_BOARD_PINNACLE_PCTV_110i 77
+#define SAA7134_BOARD_ASUSTeK_P7131_DUAL 78
+#define SAA7134_BOARD_SEDNA_PC_TV_CARDBUS 79
+#define SAA7134_BOARD_ASUSTEK_DIGIMATRIX_TV 80
+#define SAA7134_BOARD_PHILIPS_TIGER 81
+#define SAA7134_BOARD_MSI_TVATANYWHERE_PLUS 82
+#define SAA7134_BOARD_CINERGY250PCI 83
+#define SAA7134_BOARD_FLYDVB_TRIO 84
+#define SAA7134_BOARD_AVERMEDIA_777 85
+#define SAA7134_BOARD_FLYDVBT_LR301 86
+#define SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331 87
+#define SAA7134_BOARD_TEVION_DVBT_220RF 88
+#define SAA7134_BOARD_ELSA_700TV 89
+#define SAA7134_BOARD_KWORLD_ATSC110 90
+#define SAA7134_BOARD_AVERMEDIA_A169_B 91
+#define SAA7134_BOARD_AVERMEDIA_A169_B1 92
+#define SAA7134_BOARD_MD7134_BRIDGE_2 93
+#define SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS 94
+#define SAA7134_BOARD_FLYVIDEO3000_NTSC 95
+#define SAA7134_BOARD_MEDION_MD8800_QUADRO 96
+#define SAA7134_BOARD_FLYDVBS_LR300 97
+#define SAA7134_BOARD_PROTEUS_2309 98
+#define SAA7134_BOARD_AVERMEDIA_A16AR 99
+#define SAA7134_BOARD_ASUS_EUROPA2_HYBRID 100
+#define SAA7134_BOARD_PINNACLE_PCTV_310i 101
+#define SAA7134_BOARD_AVERMEDIA_STUDIO_507 102
+#define SAA7134_BOARD_VIDEOMATE_DVBT_200A 103
+#define SAA7134_BOARD_HAUPPAUGE_HVR1110 104
+#define SAA7134_BOARD_CINERGY_HT_PCMCIA 105
+#define SAA7134_BOARD_ENCORE_ENLTV 106
+#define SAA7134_BOARD_ENCORE_ENLTV_FM 107
+#define SAA7134_BOARD_CINERGY_HT_PCI 108
+#define SAA7134_BOARD_PHILIPS_TIGER_S 109
+#define SAA7134_BOARD_AVERMEDIA_M102 110
+#define SAA7134_BOARD_ASUS_P7131_4871 111
+#define SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA 112
+#define SAA7134_BOARD_ECS_TVP3XP_4CB6 113
+#define SAA7134_BOARD_KWORLD_DVBT_210 114
+#define SAA7134_BOARD_SABRENT_TV_PCB05 115
+#define SAA7134_BOARD_10MOONSTVMASTER3 116
+#define SAA7134_BOARD_AVERMEDIA_SUPER_007 117
+#define SAA7134_BOARD_BEHOLD_401 118
+#define SAA7134_BOARD_BEHOLD_403 119
+#define SAA7134_BOARD_BEHOLD_403FM 120
+#define SAA7134_BOARD_BEHOLD_405 121
+#define SAA7134_BOARD_BEHOLD_405FM 122
+#define SAA7134_BOARD_BEHOLD_407 123
+#define SAA7134_BOARD_BEHOLD_407FM 124
+#define SAA7134_BOARD_BEHOLD_409 125
+#define SAA7134_BOARD_BEHOLD_505FM 126
+#define SAA7134_BOARD_BEHOLD_507_9FM 127
+#define SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM 128
+#define SAA7134_BOARD_BEHOLD_607FM_MK3 129
+#define SAA7134_BOARD_BEHOLD_M6 130
+#define SAA7134_BOARD_TWINHAN_DTV_DVB_3056 131
+#define SAA7134_BOARD_GENIUS_TVGO_A11MCE 132
+#define SAA7134_BOARD_PHILIPS_SNAKE 133
+#define SAA7134_BOARD_CREATIX_CTX953 134
+#define SAA7134_BOARD_MSI_TVANYWHERE_AD11 135
+#define SAA7134_BOARD_AVERMEDIA_CARDBUS_506 136
+#define SAA7134_BOARD_AVERMEDIA_A16D 137
+#define SAA7134_BOARD_AVERMEDIA_M115 138
+#define SAA7134_BOARD_VIDEOMATE_T750 139
+#define SAA7134_BOARD_AVERMEDIA_A700_PRO 140
+#define SAA7134_BOARD_AVERMEDIA_A700_HYBRID 141
+#define SAA7134_BOARD_BEHOLD_H6 142
+#define SAA7134_BOARD_BEHOLD_M63 143
+#define SAA7134_BOARD_BEHOLD_M6_EXTRA 144
+#define SAA7134_BOARD_AVERMEDIA_M103 145
+#define SAA7134_BOARD_ASUSTeK_P7131_ANALOG 146
+#define SAA7134_BOARD_ASUSTeK_TIGER_3IN1 147
+#define SAA7134_BOARD_ENCORE_ENLTV_FM53 148
+#define SAA7134_BOARD_AVERMEDIA_M135A 149
+#define SAA7134_BOARD_REAL_ANGEL_220 150
+#define SAA7134_BOARD_ADS_INSTANT_HDTV_PCI 151
+#define SAA7134_BOARD_ASUSTeK_TIGER 152
+#define SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG 153
+#define SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS 154
+#define SAA7134_BOARD_HAUPPAUGE_HVR1150 155
+#define SAA7134_BOARD_HAUPPAUGE_HVR1120 156
+#define SAA7134_BOARD_AVERMEDIA_STUDIO_507UA 157
+#define SAA7134_BOARD_AVERMEDIA_CARDBUS_501 158
+#define SAA7134_BOARD_BEHOLD_505RDS_MK5 159
+#define SAA7134_BOARD_BEHOLD_507RDS_MK3 160
+#define SAA7134_BOARD_BEHOLD_507RDS_MK5 161
+#define SAA7134_BOARD_BEHOLD_607FM_MK5 162
+#define SAA7134_BOARD_BEHOLD_609FM_MK3 163
+#define SAA7134_BOARD_BEHOLD_609FM_MK5 164
+#define SAA7134_BOARD_BEHOLD_607RDS_MK3 165
+#define SAA7134_BOARD_BEHOLD_607RDS_MK5 166
+#define SAA7134_BOARD_BEHOLD_609RDS_MK3 167
+#define SAA7134_BOARD_BEHOLD_609RDS_MK5 168
+#define SAA7134_BOARD_VIDEOMATE_S350 169
+#define SAA7134_BOARD_AVERMEDIA_STUDIO_505 170
+#define SAA7134_BOARD_BEHOLD_X7 171
+#define SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM 172
+#define SAA7134_BOARD_ZOLID_HYBRID_PCI 173
+#define SAA7134_BOARD_ASUS_EUROPA_HYBRID 174
+#define SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S 175
+#define SAA7134_BOARD_BEHOLD_505RDS_MK3 176
+#define SAA7134_BOARD_HAWELL_HW_404M7 177
+#define SAA7134_BOARD_BEHOLD_H7 178
+#define SAA7134_BOARD_BEHOLD_A7 179
+#define SAA7134_BOARD_AVERMEDIA_M733A 180
+#define SAA7134_BOARD_TECHNOTREND_BUDGET_T3000 181
+#define SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG 182
+#define SAA7134_BOARD_VIDEOMATE_M1F 183
+#define SAA7134_BOARD_ENCORE_ENLTV_FM3 184
+#define SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2 185
+#define SAA7134_BOARD_BEHOLD_501 186
+#define SAA7134_BOARD_BEHOLD_503FM 187
+#define SAA7134_BOARD_SENSORAY811_911 188
+#define SAA7134_BOARD_KWORLD_PC150U 189
+
+#define SAA7134_MAXBOARDS 32
+#define SAA7134_INPUT_MAX 8
+
+/* ----------------------------------------------------------- */
+/* Since we support 2 remote types, lets tell them apart */
+
+#define SAA7134_REMOTE_GPIO 1
+#define SAA7134_REMOTE_I2C 2
+
+/* ----------------------------------------------------------- */
+/* Video Output Port Register Initialization Options */
+
+#define SET_T_CODE_POLARITY_NON_INVERTED (1 << 0)
+#define SET_CLOCK_NOT_DELAYED (1 << 1)
+#define SET_CLOCK_INVERTED (1 << 2)
+#define SET_VSYNC_OFF (1 << 3)
+
+struct saa7134_input {
+ char *name;
+ unsigned int vmux;
+ enum saa7134_audio_in amux;
+ unsigned int gpio;
+ unsigned int tv:1;
+};
+
+enum saa7134_mpeg_type {
+ SAA7134_MPEG_UNUSED,
+ SAA7134_MPEG_EMPRESS,
+ SAA7134_MPEG_DVB,
+};
+
+enum saa7134_mpeg_ts_type {
+ SAA7134_MPEG_TS_PARALLEL = 0,
+ SAA7134_MPEG_TS_SERIAL,
+};
+
+struct saa7134_board {
+ char *name;
+ unsigned int audio_clock;
+
+ /* input switching */
+ unsigned int gpiomask;
+ struct saa7134_input inputs[SAA7134_INPUT_MAX];
+ struct saa7134_input radio;
+ struct saa7134_input mute;
+
+ /* i2c chip info */
+ unsigned int tuner_type;
+ unsigned int radio_type;
+ unsigned char tuner_addr;
+ unsigned char radio_addr;
+ unsigned char empress_addr;
+ unsigned char rds_addr;
+
+ unsigned int tda9887_conf;
+ unsigned int tuner_config;
+
+ /* peripheral I/O */
+ enum saa7134_video_out video_out;
+ enum saa7134_mpeg_type mpeg;
+ enum saa7134_mpeg_ts_type ts_type;
+ unsigned int vid_port_opts;
+ unsigned int ts_force_val:1;
+};
+
+#define card_has_radio(dev) (NULL != saa7134_boards[dev->board].radio.name)
+#define card_is_empress(dev) (SAA7134_MPEG_EMPRESS == saa7134_boards[dev->board].mpeg)
+#define card_is_dvb(dev) (SAA7134_MPEG_DVB == saa7134_boards[dev->board].mpeg)
+#define card_has_mpeg(dev) (SAA7134_MPEG_UNUSED != saa7134_boards[dev->board].mpeg)
+#define card(dev) (saa7134_boards[dev->board])
+#define card_in(dev,n) (saa7134_boards[dev->board].inputs[n])
+
+/* ----------------------------------------------------------- */
+/* device / file handle status */
+
+#define RESOURCE_OVERLAY 1
+#define RESOURCE_VIDEO 2
+#define RESOURCE_VBI 4
+
+#define INTERLACE_AUTO 0
+#define INTERLACE_ON 1
+#define INTERLACE_OFF 2
+
+#define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */
+#define TS_BUFFER_TIMEOUT msecs_to_jiffies(1000) /* 1 second */
+
+struct saa7134_dev;
+struct saa7134_dma;
+
+/* saa7134 page table */
+struct saa7134_pgtable {
+ unsigned int size;
+ __le32 *cpu;
+ dma_addr_t dma;
+};
+
+/* tvaudio thread status */
+struct saa7134_thread {
+ struct task_struct *thread;
+ unsigned int scan1;
+ unsigned int scan2;
+ unsigned int mode;
+ unsigned int stopped;
+};
+
+/* buffer for one video/vbi/ts frame */
+struct saa7134_buf {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+
+ /* saa7134 specific */
+ struct saa7134_format *fmt;
+ unsigned int top_seen;
+ int (*activate)(struct saa7134_dev *dev,
+ struct saa7134_buf *buf,
+ struct saa7134_buf *next);
+
+ /* page tables */
+ struct saa7134_pgtable *pt;
+};
+
+struct saa7134_dmaqueue {
+ struct saa7134_dev *dev;
+ struct saa7134_buf *curr;
+ struct list_head queue;
+ struct timer_list timeout;
+ unsigned int need_two;
+};
+
+/* video filehandle status */
+struct saa7134_fh {
+ struct saa7134_dev *dev;
+ unsigned int radio;
+ enum v4l2_buf_type type;
+ unsigned int resources;
+ enum v4l2_priority prio;
+
+ /* video overlay */
+ struct v4l2_window win;
+ struct v4l2_clip clips[8];
+ unsigned int nclips;
+
+ /* video capture */
+ struct saa7134_format *fmt;
+ unsigned int width,height;
+ struct videobuf_queue cap;
+ struct saa7134_pgtable pt_cap;
+
+ /* vbi capture */
+ struct videobuf_queue vbi;
+ struct saa7134_pgtable pt_vbi;
+};
+
+/* dmasound dsp status */
+struct saa7134_dmasound {
+ struct mutex lock;
+ int minor_mixer;
+ int minor_dsp;
+ unsigned int users_dsp;
+
+ /* mixer */
+ enum saa7134_audio_in input;
+ unsigned int count;
+ unsigned int line1;
+ unsigned int line2;
+
+ /* dsp */
+ unsigned int afmt;
+ unsigned int rate;
+ unsigned int channels;
+ unsigned int recording_on;
+ unsigned int dma_running;
+ unsigned int blocks;
+ unsigned int blksize;
+ unsigned int bufsize;
+ struct saa7134_pgtable pt;
+ struct videobuf_dmabuf dma;
+ unsigned int dma_blk;
+ unsigned int read_offset;
+ unsigned int read_count;
+ void * priv_data;
+ struct snd_pcm_substream *substream;
+};
+
+/* ts/mpeg status */
+struct saa7134_ts {
+ /* TS capture */
+ struct saa7134_pgtable pt_ts;
+ int nr_packets;
+ int nr_bufs;
+};
+
+/* ts/mpeg ops */
+struct saa7134_mpeg_ops {
+ enum saa7134_mpeg_type type;
+ struct list_head next;
+ int (*init)(struct saa7134_dev *dev);
+ int (*fini)(struct saa7134_dev *dev);
+ void (*signal_change)(struct saa7134_dev *dev);
+};
+
+/* global device status */
+struct saa7134_dev {
+ struct list_head devlist;
+ struct mutex lock;
+ spinlock_t slock;
+ struct v4l2_prio_state prio;
+ struct v4l2_device v4l2_dev;
+ /* workstruct for loading modules */
+ struct work_struct request_module_wk;
+
+ /* insmod option/autodetected */
+ int autodetected;
+
+ /* various device info */
+ unsigned int resources;
+ struct video_device *video_dev;
+ struct video_device *radio_dev;
+ struct video_device *vbi_dev;
+ struct saa7134_dmasound dmasound;
+
+ /* infrared remote */
+ int has_remote;
+ struct saa7134_card_ir *remote;
+
+ /* pci i/o */
+ char name[32];
+ int nr;
+ struct pci_dev *pci;
+ unsigned char pci_rev,pci_lat;
+ __u32 __iomem *lmmio;
+ __u8 __iomem *bmmio;
+
+ /* config info */
+ unsigned int board;
+ unsigned int tuner_type;
+ unsigned int radio_type;
+ unsigned char tuner_addr;
+ unsigned char radio_addr;
+
+ unsigned int tda9887_conf;
+ unsigned int gpio_value;
+
+ /* i2c i/o */
+ struct i2c_adapter i2c_adap;
+ struct i2c_client i2c_client;
+ unsigned char eedata[256];
+ int has_rds;
+
+ /* video overlay */
+ struct v4l2_framebuffer ovbuf;
+ struct saa7134_format *ovfmt;
+ unsigned int ovenable;
+ enum v4l2_field ovfield;
+
+ /* video+ts+vbi capture */
+ struct saa7134_dmaqueue video_q;
+ struct saa7134_dmaqueue vbi_q;
+ unsigned int video_fieldcount;
+ unsigned int vbi_fieldcount;
+
+ /* various v4l controls */
+ struct saa7134_tvnorm *tvnorm; /* video */
+ struct saa7134_tvaudio *tvaudio;
+ unsigned int ctl_input;
+ int ctl_bright;
+ int ctl_contrast;
+ int ctl_hue;
+ int ctl_saturation;
+ int ctl_freq;
+ int ctl_mute; /* audio */
+ int ctl_volume;
+ int ctl_invert; /* private */
+ int ctl_mirror;
+ int ctl_y_odd;
+ int ctl_y_even;
+ int ctl_automute;
+
+ /* crop */
+ struct v4l2_rect crop_bounds;
+ struct v4l2_rect crop_defrect;
+ struct v4l2_rect crop_current;
+
+ /* other global state info */
+ unsigned int automute;
+ struct saa7134_thread thread;
+ struct saa7134_input *input;
+ struct saa7134_input *hw_input;
+ unsigned int hw_mute;
+ int last_carrier;
+ int nosignal;
+ unsigned int insuspend;
+
+ /* I2C keyboard data */
+ struct IR_i2c_init_data init_data;
+
+ /* SAA7134_MPEG_* */
+ struct saa7134_ts ts;
+ struct saa7134_dmaqueue ts_q;
+ int ts_started;
+ struct saa7134_mpeg_ops *mops;
+
+ /* SAA7134_MPEG_EMPRESS only */
+ struct video_device *empress_dev;
+ struct videobuf_queue empress_tsq;
+ atomic_t empress_users;
+ struct work_struct empress_workqueue;
+ int empress_started;
+
+#if defined(CONFIG_VIDEO_SAA7134_DVB) || defined(CONFIG_VIDEO_SAA7134_DVB_MODULE)
+ /* SAA7134_MPEG_DVB only */
+ struct videobuf_dvb_frontends frontends;
+ int (*original_demod_sleep)(struct dvb_frontend *fe);
+ int (*original_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage);
+ int (*original_set_high_voltage)(struct dvb_frontend *fe, long arg);
+#endif
+ void (*gate_ctrl)(struct saa7134_dev *dev, int open);
+};
+
+/* ----------------------------------------------------------- */
+
+#define saa_readl(reg) readl(dev->lmmio + (reg))
+#define saa_writel(reg,value) writel((value), dev->lmmio + (reg));
+#define saa_andorl(reg,mask,value) \
+ writel((readl(dev->lmmio+(reg)) & ~(mask)) |\
+ ((value) & (mask)), dev->lmmio+(reg))
+#define saa_setl(reg,bit) saa_andorl((reg),(bit),(bit))
+#define saa_clearl(reg,bit) saa_andorl((reg),(bit),0)
+
+#define saa_readb(reg) readb(dev->bmmio + (reg))
+#define saa_writeb(reg,value) writeb((value), dev->bmmio + (reg));
+#define saa_andorb(reg,mask,value) \
+ writeb((readb(dev->bmmio+(reg)) & ~(mask)) |\
+ ((value) & (mask)), dev->bmmio+(reg))
+#define saa_setb(reg,bit) saa_andorb((reg),(bit),(bit))
+#define saa_clearb(reg,bit) saa_andorb((reg),(bit),0)
+
+#define saa_wait(us) { udelay(us); }
+
+#define SAA7134_NORMS (\
+ V4L2_STD_PAL | V4L2_STD_PAL_N | \
+ V4L2_STD_PAL_Nc | V4L2_STD_SECAM | \
+ V4L2_STD_NTSC | V4L2_STD_PAL_M | \
+ V4L2_STD_PAL_60)
+
+#define GRP_EMPRESS (1)
+#define saa_call_all(dev, o, f, args...) do { \
+ if (dev->gate_ctrl) \
+ dev->gate_ctrl(dev, 1); \
+ v4l2_device_call_all(&(dev)->v4l2_dev, 0, o, f , ##args); \
+ if (dev->gate_ctrl) \
+ dev->gate_ctrl(dev, 0); \
+} while (0)
+
+#define saa_call_empress(dev, o, f, args...) ({ \
+ long _rc; \
+ if (dev->gate_ctrl) \
+ dev->gate_ctrl(dev, 1); \
+ _rc = v4l2_device_call_until_err(&(dev)->v4l2_dev, \
+ GRP_EMPRESS, o, f , ##args); \
+ if (dev->gate_ctrl) \
+ dev->gate_ctrl(dev, 0); \
+ _rc; \
+})
+
+/* ----------------------------------------------------------- */
+/* saa7134-core.c */
+
+extern struct list_head saa7134_devlist;
+extern struct mutex saa7134_devlist_lock;
+extern int saa7134_no_overlay;
+
+void saa7134_track_gpio(struct saa7134_dev *dev, char *msg);
+void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value);
+
+#define SAA7134_PGTABLE_SIZE 4096
+
+int saa7134_pgtable_alloc(struct pci_dev *pci, struct saa7134_pgtable *pt);
+int saa7134_pgtable_build(struct pci_dev *pci, struct saa7134_pgtable *pt,
+ struct scatterlist *list, unsigned int length,
+ unsigned int startpage);
+void saa7134_pgtable_free(struct pci_dev *pci, struct saa7134_pgtable *pt);
+
+int saa7134_buffer_count(unsigned int size, unsigned int count);
+int saa7134_buffer_startpage(struct saa7134_buf *buf);
+unsigned long saa7134_buffer_base(struct saa7134_buf *buf);
+
+int saa7134_buffer_queue(struct saa7134_dev *dev, struct saa7134_dmaqueue *q,
+ struct saa7134_buf *buf);
+void saa7134_buffer_finish(struct saa7134_dev *dev, struct saa7134_dmaqueue *q,
+ unsigned int state);
+void saa7134_buffer_next(struct saa7134_dev *dev, struct saa7134_dmaqueue *q);
+void saa7134_buffer_timeout(unsigned long data);
+void saa7134_dma_free(struct videobuf_queue *q,struct saa7134_buf *buf);
+
+int saa7134_set_dmabits(struct saa7134_dev *dev);
+
+extern int (*saa7134_dmasound_init)(struct saa7134_dev *dev);
+extern int (*saa7134_dmasound_exit)(struct saa7134_dev *dev);
+
+
+/* ----------------------------------------------------------- */
+/* saa7134-cards.c */
+
+extern struct saa7134_board saa7134_boards[];
+extern const unsigned int saa7134_bcount;
+extern struct pci_device_id __devinitdata saa7134_pci_tbl[];
+
+extern int saa7134_board_init1(struct saa7134_dev *dev);
+extern int saa7134_board_init2(struct saa7134_dev *dev);
+int saa7134_tuner_callback(void *priv, int component, int command, int arg);
+
+
+/* ----------------------------------------------------------- */
+/* saa7134-i2c.c */
+
+int saa7134_i2c_register(struct saa7134_dev *dev);
+int saa7134_i2c_unregister(struct saa7134_dev *dev);
+
+
+/* ----------------------------------------------------------- */
+/* saa7134-video.c */
+
+extern unsigned int video_debug;
+extern struct video_device saa7134_video_template;
+extern struct video_device saa7134_radio_template;
+
+int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c);
+int saa7134_g_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c);
+int saa7134_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c);
+int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_std_id *id);
+
+int saa7134_videoport_init(struct saa7134_dev *dev);
+void saa7134_set_tvnorm_hw(struct saa7134_dev *dev);
+
+int saa7134_video_init1(struct saa7134_dev *dev);
+int saa7134_video_init2(struct saa7134_dev *dev);
+void saa7134_irq_video_signalchange(struct saa7134_dev *dev);
+void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status);
+
+
+/* ----------------------------------------------------------- */
+/* saa7134-ts.c */
+
+#define TS_PACKET_SIZE 188 /* TS packets 188 bytes */
+
+extern struct videobuf_queue_ops saa7134_ts_qops;
+
+int saa7134_ts_init1(struct saa7134_dev *dev);
+int saa7134_ts_fini(struct saa7134_dev *dev);
+void saa7134_irq_ts_done(struct saa7134_dev *dev, unsigned long status);
+
+int saa7134_ts_register(struct saa7134_mpeg_ops *ops);
+void saa7134_ts_unregister(struct saa7134_mpeg_ops *ops);
+
+int saa7134_ts_init_hw(struct saa7134_dev *dev);
+
+int saa7134_ts_start(struct saa7134_dev *dev);
+int saa7134_ts_stop(struct saa7134_dev *dev);
+
+/* ----------------------------------------------------------- */
+/* saa7134-vbi.c */
+
+extern struct videobuf_queue_ops saa7134_vbi_qops;
+extern struct video_device saa7134_vbi_template;
+
+int saa7134_vbi_init1(struct saa7134_dev *dev);
+int saa7134_vbi_fini(struct saa7134_dev *dev);
+void saa7134_irq_vbi_done(struct saa7134_dev *dev, unsigned long status);
+
+
+/* ----------------------------------------------------------- */
+/* saa7134-tvaudio.c */
+
+int saa7134_tvaudio_rx2mode(u32 rx);
+
+void saa7134_tvaudio_setmute(struct saa7134_dev *dev);
+void saa7134_tvaudio_setinput(struct saa7134_dev *dev,
+ struct saa7134_input *in);
+void saa7134_tvaudio_setvolume(struct saa7134_dev *dev, int level);
+int saa7134_tvaudio_getstereo(struct saa7134_dev *dev);
+
+void saa7134_tvaudio_init(struct saa7134_dev *dev);
+int saa7134_tvaudio_init2(struct saa7134_dev *dev);
+int saa7134_tvaudio_fini(struct saa7134_dev *dev);
+int saa7134_tvaudio_do_scan(struct saa7134_dev *dev);
+int saa7134_tvaudio_close(struct saa7134_dev *dev);
+
+int saa_dsp_writel(struct saa7134_dev *dev, int reg, u32 value);
+
+void saa7134_enable_i2s(struct saa7134_dev *dev);
+
+/* ----------------------------------------------------------- */
+/* saa7134-oss.c */
+
+extern const struct file_operations saa7134_dsp_fops;
+extern const struct file_operations saa7134_mixer_fops;
+
+int saa7134_oss_init1(struct saa7134_dev *dev);
+int saa7134_oss_fini(struct saa7134_dev *dev);
+void saa7134_irq_oss_done(struct saa7134_dev *dev, unsigned long status);
+
+/* ----------------------------------------------------------- */
+/* saa7134-input.c */
+
+#if defined(CONFIG_VIDEO_SAA7134_RC)
+int saa7134_input_init1(struct saa7134_dev *dev);
+void saa7134_input_fini(struct saa7134_dev *dev);
+void saa7134_input_irq(struct saa7134_dev *dev);
+void saa7134_probe_i2c_ir(struct saa7134_dev *dev);
+int saa7134_ir_start(struct saa7134_dev *dev);
+void saa7134_ir_stop(struct saa7134_dev *dev);
+#else
+#define saa7134_input_init1(dev) ((void)0)
+#define saa7134_input_fini(dev) ((void)0)
+#define saa7134_input_irq(dev) ((void)0)
+#define saa7134_probe_i2c_ir(dev) ((void)0)
+#define saa7134_ir_start(dev) ((void)0)
+#define saa7134_ir_stop(dev) ((void)0)
+#endif
diff --git a/drivers/media/video/saa7146.h b/drivers/media/video/saa7146.h
new file mode 100644
index 00000000..9fadb331
--- /dev/null
+++ b/drivers/media/video/saa7146.h
@@ -0,0 +1,112 @@
+/*
+ saa7146.h - definitions philips saa7146 based cards
+ Copyright (C) 1999 Nathan Laredo (laredo@gnu.org)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __SAA7146__
+#define __SAA7146__
+
+#define SAA7146_VERSION_CODE 0x000101
+
+#include <linux/types.h>
+#include <linux/wait.h>
+
+#ifndef O_NONCAP
+#define O_NONCAP O_TRUNC
+#endif
+
+#define MAX_GBUFFERS 2
+#define FBUF_SIZE 0x190000
+
+#ifdef __KERNEL__
+
+struct saa7146_window
+{
+ int x, y;
+ ushort width, height;
+ ushort bpp, bpl;
+ ushort swidth, sheight;
+ short cropx, cropy;
+ ushort cropwidth, cropheight;
+ unsigned long vidadr;
+ int color_fmt;
+ ushort depth;
+};
+
+/* Per-open data for handling multiple opens on one device */
+struct device_open
+{
+ int isopen;
+ int noncapturing;
+ struct saa7146 *dev;
+};
+#define MAX_OPENS 3
+
+struct saa7146
+{
+ struct video_device video_dev;
+ struct video_picture picture;
+ struct video_audio audio_dev;
+ struct video_info vidinfo;
+ int user;
+ int cap;
+ int capuser;
+ int irqstate; /* irq routine is state driven */
+ int writemode;
+ int playmode;
+ unsigned int nr;
+ unsigned long irq; /* IRQ used by SAA7146 card */
+ unsigned short id;
+ unsigned char revision;
+ unsigned char boardcfg[64]; /* 64 bytes of config from eeprom */
+ unsigned long saa7146_adr; /* bus address of IO mem from PCI BIOS */
+ struct saa7146_window win;
+ unsigned char __iomem *saa7146_mem; /* pointer to mapped IO memory */
+ struct device_open open_data[MAX_OPENS];
+#define MAX_MARKS 16
+ /* for a/v sync */
+ int endmark[MAX_MARKS], endmarkhead, endmarktail;
+ u32 *dmaRPS1, *pageRPS1, *dmaRPS2, *pageRPS2, *dmavid1, *dmavid2,
+ *dmavid3, *dmaa1in, *dmaa1out, *dmaa2in, *dmaa2out,
+ *pagedebi, *pagevid1, *pagevid2, *pagevid3, *pagea1in,
+ *pagea1out, *pagea2in, *pagea2out;
+ wait_queue_head_t i2cq, debiq, audq, vidq;
+ u8 *vidbuf, *audbuf, *osdbuf, *dmadebi;
+ int audhead, vidhead, osdhead, audtail, vidtail, osdtail;
+ spinlock_t lock; /* the device lock */
+};
+#endif
+
+#ifdef _ALPHA_SAA7146
+#define saawrite(dat,adr) writel((dat), saa->saa7146_adr+(adr))
+#define saaread(adr) readl(saa->saa7146_adr+(adr))
+#else
+#define saawrite(dat,adr) writel((dat), saa->saa7146_mem+(adr))
+#define saaread(adr) readl(saa->saa7146_mem+(adr))
+#endif
+
+#define saaand(dat,adr) saawrite((dat) & saaread(adr), adr)
+#define saaor(dat,adr) saawrite((dat) | saaread(adr), adr)
+#define saaaor(dat,mask,adr) saawrite((dat) | ((mask) & saaread(adr)), adr)
+
+/* bitmask of attached hardware found */
+#define SAA7146_UNKNOWN 0x00000000
+#define SAA7146_SAA7111 0x00000001
+#define SAA7146_SAA7121 0x00000002
+#define SAA7146_IBMMPEG 0x00000004
+
+#endif
diff --git a/drivers/media/video/saa7146reg.h b/drivers/media/video/saa7146reg.h
new file mode 100644
index 00000000..80ec2c14
--- /dev/null
+++ b/drivers/media/video/saa7146reg.h
@@ -0,0 +1,283 @@
+/*
+ saa7146.h - definitions philips saa7146 based cards
+ Copyright (C) 1999 Nathan Laredo (laredo@gnu.org)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __SAA7146_REG__
+#define __SAA7146_REG__
+#define SAA7146_BASE_ODD1 0x00
+#define SAA7146_BASE_EVEN1 0x04
+#define SAA7146_PROT_ADDR1 0x08
+#define SAA7146_PITCH1 0x0c
+#define SAA7146_PAGE1 0x10
+#define SAA7146_NUM_LINE_BYTE1 0x14
+#define SAA7146_BASE_ODD2 0x18
+#define SAA7146_BASE_EVEN2 0x1c
+#define SAA7146_PROT_ADDR2 0x20
+#define SAA7146_PITCH2 0x24
+#define SAA7146_PAGE2 0x28
+#define SAA7146_NUM_LINE_BYTE2 0x2c
+#define SAA7146_BASE_ODD3 0x30
+#define SAA7146_BASE_EVEN3 0x34
+#define SAA7146_PROT_ADDR3 0x38
+#define SAA7146_PITCH3 0x3c
+#define SAA7146_PAGE3 0x40
+#define SAA7146_NUM_LINE_BYTE3 0x44
+#define SAA7146_PCI_BT_V1 0x48
+#define SAA7146_PCI_BT_V2 0x49
+#define SAA7146_PCI_BT_V3 0x4a
+#define SAA7146_PCI_BT_DEBI 0x4b
+#define SAA7146_PCI_BT_A 0x4c
+#define SAA7146_DD1_INIT 0x50
+#define SAA7146_DD1_STREAM_B 0x54
+#define SAA7146_DD1_STREAM_A 0x56
+#define SAA7146_BRS_CTRL 0x58
+#define SAA7146_HPS_CTRL 0x5c
+#define SAA7146_HPS_V_SCALE 0x60
+#define SAA7146_HPS_V_GAIN 0x64
+#define SAA7146_HPS_H_PRESCALE 0x68
+#define SAA7146_HPS_H_SCALE 0x6c
+#define SAA7146_BCS_CTRL 0x70
+#define SAA7146_CHROMA_KEY_RANGE 0x74
+#define SAA7146_CLIP_FORMAT_CTRL 0x78
+#define SAA7146_DEBI_CONFIG 0x7c
+#define SAA7146_DEBI_COMMAND 0x80
+#define SAA7146_DEBI_PAGE 0x84
+#define SAA7146_DEBI_AD 0x88
+#define SAA7146_I2C_TRANSFER 0x8c
+#define SAA7146_I2C_STATUS 0x90
+#define SAA7146_BASE_A1_IN 0x94
+#define SAA7146_PROT_A1_IN 0x98
+#define SAA7146_PAGE_A1_IN 0x9C
+#define SAA7146_BASE_A1_OUT 0xa0
+#define SAA7146_PROT_A1_OUT 0xa4
+#define SAA7146_PAGE_A1_OUT 0xa8
+#define SAA7146_BASE_A2_IN 0xac
+#define SAA7146_PROT_A2_IN 0xb0
+#define SAA7146_PAGE_A2_IN 0xb4
+#define SAA7146_BASE_A2_OUT 0xb8
+#define SAA7146_PROT_A2_OUT 0xbc
+#define SAA7146_PAGE_A2_OUT 0xc0
+#define SAA7146_RPS_PAGE0 0xc4
+#define SAA7146_RPS_PAGE1 0xc8
+#define SAA7146_RPS_THRESH0 0xcc
+#define SAA7146_RPS_THRESH1 0xd0
+#define SAA7146_RPS_TOV0 0xd4
+#define SAA7146_RPS_TOV1 0xd8
+#define SAA7146_IER 0xdc
+#define SAA7146_GPIO_CTRL 0xe0
+#define SAA7146_EC1SSR 0xe4
+#define SAA7146_EC2SSR 0xe8
+#define SAA7146_ECT1R 0xec
+#define SAA7146_ECT2R 0xf0
+#define SAA7146_ACON1 0xf4
+#define SAA7146_ACON2 0xf8
+#define SAA7146_MC1 0xfc
+#define SAA7146_MC2 0x100
+#define SAA7146_RPS_ADDR0 0x104
+#define SAA7146_RPS_ADDR1 0x108
+#define SAA7146_ISR 0x10c
+#define SAA7146_PSR 0x110
+#define SAA7146_SSR 0x114
+#define SAA7146_EC1R 0x118
+#define SAA7146_EC2R 0x11c
+#define SAA7146_VDP1 0x120
+#define SAA7146_VDP2 0x124
+#define SAA7146_VDP3 0x128
+#define SAA7146_ADP1 0x12c
+#define SAA7146_ADP2 0x130
+#define SAA7146_ADP3 0x134
+#define SAA7146_ADP4 0x138
+#define SAA7146_DDP 0x13c
+#define SAA7146_LEVEL_REP 0x140
+#define SAA7146_FB_BUFFER1 0x144
+#define SAA7146_FB_BUFFER2 0x148
+#define SAA7146_A_TIME_SLOT1 0x180
+#define SAA7146_A_TIME_SLOT2 0x1C0
+
+/* bitfield defines */
+#define MASK_31 0x80000000
+#define MASK_30 0x40000000
+#define MASK_29 0x20000000
+#define MASK_28 0x10000000
+#define MASK_27 0x08000000
+#define MASK_26 0x04000000
+#define MASK_25 0x02000000
+#define MASK_24 0x01000000
+#define MASK_23 0x00800000
+#define MASK_22 0x00400000
+#define MASK_21 0x00200000
+#define MASK_20 0x00100000
+#define MASK_19 0x00080000
+#define MASK_18 0x00040000
+#define MASK_17 0x00020000
+#define MASK_16 0x00010000
+#define MASK_15 0x00008000
+#define MASK_14 0x00004000
+#define MASK_13 0x00002000
+#define MASK_12 0x00001000
+#define MASK_11 0x00000800
+#define MASK_10 0x00000400
+#define MASK_09 0x00000200
+#define MASK_08 0x00000100
+#define MASK_07 0x00000080
+#define MASK_06 0x00000040
+#define MASK_05 0x00000020
+#define MASK_04 0x00000010
+#define MASK_03 0x00000008
+#define MASK_02 0x00000004
+#define MASK_01 0x00000002
+#define MASK_00 0x00000001
+#define MASK_B0 0x000000ff
+#define MASK_B1 0x0000ff00
+#define MASK_B2 0x00ff0000
+#define MASK_B3 0xff000000
+#define MASK_W0 0x0000ffff
+#define MASK_W1 0xffff0000
+#define MASK_PA 0xfffffffc
+#define MASK_PR 0xfffffffe
+#define MASK_ER 0xffffffff
+#define MASK_NONE 0x00000000
+
+#define SAA7146_PAGE_MAP_EN MASK_11
+/* main control register 1 */
+#define SAA7146_MC1_MRST_N MASK_15
+#define SAA7146_MC1_ERPS1 MASK_13
+#define SAA7146_MC1_ERPS0 MASK_12
+#define SAA7146_MC1_EDP MASK_11
+#define SAA7146_MC1_EVP MASK_10
+#define SAA7146_MC1_EAP MASK_09
+#define SAA7146_MC1_EI2C MASK_08
+#define SAA7146_MC1_TR_E_DEBI MASK_07
+#define SAA7146_MC1_TR_E_1 MASK_06
+#define SAA7146_MC1_TR_E_2 MASK_05
+#define SAA7146_MC1_TR_E_3 MASK_04
+#define SAA7146_MC1_TR_E_A2_OUT MASK_03
+#define SAA7146_MC1_TR_E_A2_IN MASK_02
+#define SAA7146_MC1_TR_E_A1_OUT MASK_01
+#define SAA7146_MC1_TR_E_A1_IN MASK_00
+/* main control register 2 */
+#define SAA7146_MC2_RPS_SIG4 MASK_15
+#define SAA7146_MC2_RPS_SIG3 MASK_14
+#define SAA7146_MC2_RPS_SIG2 MASK_13
+#define SAA7146_MC2_RPS_SIG1 MASK_12
+#define SAA7146_MC2_RPS_SIG0 MASK_11
+#define SAA7146_MC2_UPLD_D1_B MASK_10
+#define SAA7146_MC2_UPLD_D1_A MASK_09
+#define SAA7146_MC2_UPLD_BRS MASK_08
+#define SAA7146_MC2_UPLD_HPS_H MASK_06
+#define SAA7146_MC2_UPLD_HPS_V MASK_05
+#define SAA7146_MC2_UPLD_DMA3 MASK_04
+#define SAA7146_MC2_UPLD_DMA2 MASK_03
+#define SAA7146_MC2_UPLD_DMA1 MASK_02
+#define SAA7146_MC2_UPLD_DEBI MASK_01
+#define SAA7146_MC2_UPLD_I2C MASK_00
+/* Primary Status Register and Interrupt Enable/Status Registers */
+#define SAA7146_PSR_PPEF MASK_31
+#define SAA7146_PSR_PABO MASK_30
+#define SAA7146_PSR_PPED MASK_29
+#define SAA7146_PSR_RPS_I1 MASK_28
+#define SAA7146_PSR_RPS_I0 MASK_27
+#define SAA7146_PSR_RPS_LATE1 MASK_26
+#define SAA7146_PSR_RPS_LATE0 MASK_25
+#define SAA7146_PSR_RPS_E1 MASK_24
+#define SAA7146_PSR_RPS_E0 MASK_23
+#define SAA7146_PSR_RPS_TO1 MASK_22
+#define SAA7146_PSR_RPS_TO0 MASK_21
+#define SAA7146_PSR_UPLD MASK_20
+#define SAA7146_PSR_DEBI_S MASK_19
+#define SAA7146_PSR_DEBI_E MASK_18
+#define SAA7146_PSR_I2C_S MASK_17
+#define SAA7146_PSR_I2C_E MASK_16
+#define SAA7146_PSR_A2_IN MASK_15
+#define SAA7146_PSR_A2_OUT MASK_14
+#define SAA7146_PSR_A1_IN MASK_13
+#define SAA7146_PSR_A1_OUT MASK_12
+#define SAA7146_PSR_AFOU MASK_11
+#define SAA7146_PSR_V_PE MASK_10
+#define SAA7146_PSR_VFOU MASK_09
+#define SAA7146_PSR_FIDA MASK_08
+#define SAA7146_PSR_FIDB MASK_07
+#define SAA7146_PSR_PIN3 MASK_06
+#define SAA7146_PSR_PIN2 MASK_05
+#define SAA7146_PSR_PIN1 MASK_04
+#define SAA7146_PSR_PIN0 MASK_03
+#define SAA7146_PSR_ECS MASK_02
+#define SAA7146_PSR_EC3S MASK_01
+#define SAA7146_PSR_EC0S MASK_00
+/* Secondary Status Register */
+#define SAA7146_SSR_PRQ MASK_31
+#define SAA7146_SSR_PMA MASK_30
+#define SAA7146_SSR_RPS_RE1 MASK_29
+#define SAA7146_SSR_RPS_PE1 MASK_28
+#define SAA7146_SSR_RPS_A1 MASK_27
+#define SAA7146_SSR_RPS_RE0 MASK_26
+#define SAA7146_SSR_RPS_PE0 MASK_25
+#define SAA7146_SSR_RPS_A0 MASK_24
+#define SAA7146_SSR_DEBI_TO MASK_23
+#define SAA7146_SSR_DEBI_EF MASK_22
+#define SAA7146_SSR_I2C_EA MASK_21
+#define SAA7146_SSR_I2C_EW MASK_20
+#define SAA7146_SSR_I2C_ER MASK_19
+#define SAA7146_SSR_I2C_EL MASK_18
+#define SAA7146_SSR_I2C_EF MASK_17
+#define SAA7146_SSR_V3P MASK_16
+#define SAA7146_SSR_V2P MASK_15
+#define SAA7146_SSR_V1P MASK_14
+#define SAA7146_SSR_VF3 MASK_13
+#define SAA7146_SSR_VF2 MASK_12
+#define SAA7146_SSR_VF1 MASK_11
+#define SAA7146_SSR_AF2_IN MASK_10
+#define SAA7146_SSR_AF2_OUT MASK_09
+#define SAA7146_SSR_AF1_IN MASK_08
+#define SAA7146_SSR_AF1_OUT MASK_07
+#define SAA7146_SSR_VGT MASK_05
+#define SAA7146_SSR_LNQG MASK_04
+#define SAA7146_SSR_EC5S MASK_03
+#define SAA7146_SSR_EC4S MASK_02
+#define SAA7146_SSR_EC2S MASK_01
+#define SAA7146_SSR_EC1S MASK_00
+/* I2C status register */
+#define SAA7146_I2C_ABORT MASK_07
+#define SAA7146_I2C_SPERR MASK_06
+#define SAA7146_I2C_APERR MASK_05
+#define SAA7146_I2C_DTERR MASK_04
+#define SAA7146_I2C_DRERR MASK_03
+#define SAA7146_I2C_AL MASK_02
+#define SAA7146_I2C_ERR MASK_01
+#define SAA7146_I2C_BUSY MASK_00
+/* output formats */
+#define SAA7146_YUV422 0
+#define SAA7146_RGB16 0
+#define SAA7146_YUV444 1
+#define SAA7146_RGB24 1
+#define SAA7146_ARGB32 2
+#define SAA7146_YUV411 3
+#define SAA7146_ARGB15 3
+#define SAA7146_YUV2 4
+#define SAA7146_RGAB15 4
+#define SAA7146_Y8 6
+#define SAA7146_YUV8 7
+#define SAA7146_RGB8 7
+#define SAA7146_YUV444p 8
+#define SAA7146_YUV422p 9
+#define SAA7146_YUV420p 10
+#define SAA7146_YUV1620 11
+#define SAA7146_Y1 13
+#define SAA7146_Y2 14
+#define SAA7146_YUV1 15
+#endif
diff --git a/drivers/media/video/saa7164/Kconfig b/drivers/media/video/saa7164/Kconfig
new file mode 100644
index 00000000..35326372
--- /dev/null
+++ b/drivers/media/video/saa7164/Kconfig
@@ -0,0 +1,18 @@
+config VIDEO_SAA7164
+ tristate "NXP SAA7164 support"
+ depends on DVB_CORE && PCI && I2C
+ select I2C_ALGOBIT
+ select FW_LOADER
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ select VIDEOBUF_DVB
+ select DVB_TDA10048 if !DVB_FE_CUSTOMISE
+ select DVB_S5H1411 if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE
+ ---help---
+ This is a video4linux driver for NXP SAA7164 based
+ TV cards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7164
+
diff --git a/drivers/media/video/saa7164/Makefile b/drivers/media/video/saa7164/Makefile
new file mode 100644
index 00000000..068443af
--- /dev/null
+++ b/drivers/media/video/saa7164/Makefile
@@ -0,0 +1,12 @@
+saa7164-objs := saa7164-cards.o saa7164-core.o saa7164-i2c.o saa7164-dvb.o \
+ saa7164-fw.o saa7164-bus.o saa7164-cmd.o saa7164-api.o \
+ saa7164-buffer.o saa7164-encoder.o saa7164-vbi.o
+
+obj-$(CONFIG_VIDEO_SAA7164) += saa7164.o
+
+ccflags-y += -I$(srctree)/drivers/media/video
+ccflags-y += -I$(srctree)/drivers/media/common/tuners
+ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
+ccflags-y += -I$(srctree)/drivers/media/dvb/frontends
+
+ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/video/saa7164/saa7164-api.c b/drivers/media/video/saa7164/saa7164-api.c
new file mode 100644
index 00000000..8a98ab68
--- /dev/null
+++ b/drivers/media/video/saa7164/saa7164-api.c
@@ -0,0 +1,1533 @@
+/*
+ * Driver for the NXP SAA7164 PCIe bridge
+ *
+ * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/wait.h>
+#include <linux/slab.h>
+
+#include "saa7164.h"
+
+int saa7164_api_get_load_info(struct saa7164_dev *dev, struct tmFwInfoStruct *i)
+{
+ int ret;
+
+ if (!(saa_debug & DBGLVL_CPU))
+ return 0;
+
+ dprintk(DBGLVL_API, "%s()\n", __func__);
+
+ i->deviceinst = 0;
+ i->devicespec = 0;
+ i->mode = 0;
+ i->status = 0;
+
+ ret = saa7164_cmd_send(dev, 0, GET_CUR,
+ GET_FW_STATUS_CONTROL, sizeof(struct tmFwInfoStruct), i);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ printk(KERN_INFO "saa7164[%d]-CPU: %d percent", dev->nr, i->CPULoad);
+
+ return ret;
+}
+
+int saa7164_api_collect_debug(struct saa7164_dev *dev)
+{
+ struct tmComResDebugGetData d;
+ u8 more = 255;
+ int ret;
+
+ dprintk(DBGLVL_API, "%s()\n", __func__);
+
+ while (more--) {
+
+ memset(&d, 0, sizeof(d));
+
+ ret = saa7164_cmd_send(dev, 0, GET_CUR,
+ GET_DEBUG_DATA_CONTROL, sizeof(d), &d);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n",
+ __func__, ret);
+
+ if (d.dwResult != SAA_OK)
+ break;
+
+ printk(KERN_INFO "saa7164[%d]-FWMSG: %s", dev->nr,
+ d.ucDebugData);
+ }
+
+ return 0;
+}
+
+int saa7164_api_set_debug(struct saa7164_dev *dev, u8 level)
+{
+ struct tmComResDebugSetLevel lvl;
+ int ret;
+
+ dprintk(DBGLVL_API, "%s(level=%d)\n", __func__, level);
+
+ /* Retrieve current state */
+ ret = saa7164_cmd_send(dev, 0, GET_CUR,
+ SET_DEBUG_LEVEL_CONTROL, sizeof(lvl), &lvl);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ dprintk(DBGLVL_API, "%s() Was %d\n", __func__, lvl.dwDebugLevel);
+
+ lvl.dwDebugLevel = level;
+
+ /* set new state */
+ ret = saa7164_cmd_send(dev, 0, SET_CUR,
+ SET_DEBUG_LEVEL_CONTROL, sizeof(lvl), &lvl);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ return ret;
+}
+
+int saa7164_api_set_vbi_format(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ struct tmComResProbeCommit fmt, rsp;
+ int ret;
+
+ dprintk(DBGLVL_API, "%s(nr=%d, unitid=0x%x)\n", __func__,
+ port->nr, port->hwcfg.unitid);
+
+ fmt.bmHint = 0;
+ fmt.bFormatIndex = 1;
+ fmt.bFrameIndex = 1;
+
+ /* Probe, see if it can support this format */
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid,
+ SET_CUR, SAA_PROBE_CONTROL, sizeof(fmt), &fmt);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() set error, ret = 0x%x\n", __func__, ret);
+
+ /* See of the format change was successful */
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid,
+ GET_CUR, SAA_PROBE_CONTROL, sizeof(rsp), &rsp);
+ if (ret != SAA_OK) {
+ printk(KERN_ERR "%s() get error, ret = 0x%x\n", __func__, ret);
+ } else {
+ /* Compare requested vs received, should be same */
+ if (memcmp(&fmt, &rsp, sizeof(rsp)) == 0) {
+ dprintk(DBGLVL_API, "SET/PROBE Verified\n");
+
+ /* Ask the device to select the negotiated format */
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid,
+ SET_CUR, SAA_COMMIT_CONTROL, sizeof(fmt), &fmt);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() commit error, ret = 0x%x\n",
+ __func__, ret);
+
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid,
+ GET_CUR, SAA_COMMIT_CONTROL, sizeof(rsp), &rsp);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() GET commit error, ret = 0x%x\n",
+ __func__, ret);
+
+ if (memcmp(&fmt, &rsp, sizeof(rsp)) != 0) {
+ printk(KERN_ERR "%s() memcmp error, ret = 0x%x\n",
+ __func__, ret);
+ } else
+ dprintk(DBGLVL_API, "SET/COMMIT Verified\n");
+
+ dprintk(DBGLVL_API, "rsp.bmHint = 0x%x\n", rsp.bmHint);
+ dprintk(DBGLVL_API, "rsp.bFormatIndex = 0x%x\n",
+ rsp.bFormatIndex);
+ dprintk(DBGLVL_API, "rsp.bFrameIndex = 0x%x\n",
+ rsp.bFrameIndex);
+ } else
+ printk(KERN_ERR "%s() compare failed\n", __func__);
+ }
+
+ if (ret == SAA_OK)
+ dprintk(DBGLVL_API, "%s(nr=%d) Success\n", __func__, port->nr);
+
+ return ret;
+}
+
+int saa7164_api_set_gop_size(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ struct tmComResEncVideoGopStructure gs;
+ int ret;
+
+ dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+ gs.ucRefFrameDist = port->encoder_params.refdist;
+ gs.ucGOPSize = port->encoder_params.gop_size;
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
+ EU_VIDEO_GOP_STRUCTURE_CONTROL,
+ sizeof(gs), &gs);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ return ret;
+}
+
+int saa7164_api_set_encoder(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ struct tmComResEncVideoBitRate vb;
+ struct tmComResEncAudioBitRate ab;
+ int ret;
+
+ dprintk(DBGLVL_ENC, "%s() unitid=0x%x\n", __func__,
+ port->hwcfg.sourceid);
+
+ if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS)
+ port->encoder_profile = EU_PROFILE_PS_DVD;
+ else
+ port->encoder_profile = EU_PROFILE_TS_HQ;
+
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
+ EU_PROFILE_CONTROL, sizeof(u8), &port->encoder_profile);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ /* Resolution */
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
+ EU_PROFILE_CONTROL, sizeof(u8), &port->encoder_profile);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ /* Establish video bitrates */
+ if (port->encoder_params.bitrate_mode ==
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
+ vb.ucVideoBitRateMode = EU_VIDEO_BIT_RATE_MODE_CONSTANT;
+ else
+ vb.ucVideoBitRateMode = EU_VIDEO_BIT_RATE_MODE_VARIABLE_PEAK;
+ vb.dwVideoBitRate = port->encoder_params.bitrate;
+ vb.dwVideoBitRatePeak = port->encoder_params.bitrate_peak;
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
+ EU_VIDEO_BIT_RATE_CONTROL,
+ sizeof(struct tmComResEncVideoBitRate),
+ &vb);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ /* Establish audio bitrates */
+ ab.ucAudioBitRateMode = 0;
+ ab.dwAudioBitRate = 384000;
+ ab.dwAudioBitRatePeak = ab.dwAudioBitRate;
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
+ EU_AUDIO_BIT_RATE_CONTROL,
+ sizeof(struct tmComResEncAudioBitRate),
+ &ab);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__,
+ ret);
+
+ saa7164_api_set_aspect_ratio(port);
+ saa7164_api_set_gop_size(port);
+
+ return ret;
+}
+
+int saa7164_api_get_encoder(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ struct tmComResEncVideoBitRate v;
+ struct tmComResEncAudioBitRate a;
+ struct tmComResEncVideoInputAspectRatio ar;
+ int ret;
+
+ dprintk(DBGLVL_ENC, "%s() unitid=0x%x\n", __func__,
+ port->hwcfg.sourceid);
+
+ port->encoder_profile = 0;
+ port->video_format = 0;
+ port->video_resolution = 0;
+ port->audio_format = 0;
+
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
+ EU_PROFILE_CONTROL, sizeof(u8), &port->encoder_profile);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
+ EU_VIDEO_RESOLUTION_CONTROL, sizeof(u8),
+ &port->video_resolution);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
+ EU_VIDEO_FORMAT_CONTROL, sizeof(u8), &port->video_format);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
+ EU_VIDEO_BIT_RATE_CONTROL, sizeof(v), &v);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
+ EU_AUDIO_FORMAT_CONTROL, sizeof(u8), &port->audio_format);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
+ EU_AUDIO_BIT_RATE_CONTROL, sizeof(a), &a);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ /* Aspect Ratio */
+ ar.width = 0;
+ ar.height = 0;
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
+ EU_VIDEO_INPUT_ASPECT_CONTROL,
+ sizeof(struct tmComResEncVideoInputAspectRatio), &ar);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ dprintk(DBGLVL_ENC, "encoder_profile = %d\n", port->encoder_profile);
+ dprintk(DBGLVL_ENC, "video_format = %d\n", port->video_format);
+ dprintk(DBGLVL_ENC, "audio_format = %d\n", port->audio_format);
+ dprintk(DBGLVL_ENC, "video_resolution= %d\n", port->video_resolution);
+ dprintk(DBGLVL_ENC, "v.ucVideoBitRateMode = %d\n",
+ v.ucVideoBitRateMode);
+ dprintk(DBGLVL_ENC, "v.dwVideoBitRate = %d\n",
+ v.dwVideoBitRate);
+ dprintk(DBGLVL_ENC, "v.dwVideoBitRatePeak = %d\n",
+ v.dwVideoBitRatePeak);
+ dprintk(DBGLVL_ENC, "a.ucVideoBitRateMode = %d\n",
+ a.ucAudioBitRateMode);
+ dprintk(DBGLVL_ENC, "a.dwVideoBitRate = %d\n",
+ a.dwAudioBitRate);
+ dprintk(DBGLVL_ENC, "a.dwVideoBitRatePeak = %d\n",
+ a.dwAudioBitRatePeak);
+ dprintk(DBGLVL_ENC, "aspect.width / height = %d:%d\n",
+ ar.width, ar.height);
+
+ return ret;
+}
+
+int saa7164_api_set_aspect_ratio(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ struct tmComResEncVideoInputAspectRatio ar;
+ int ret;
+
+ dprintk(DBGLVL_ENC, "%s(%d)\n", __func__,
+ port->encoder_params.ctl_aspect);
+
+ switch (port->encoder_params.ctl_aspect) {
+ case V4L2_MPEG_VIDEO_ASPECT_1x1:
+ ar.width = 1;
+ ar.height = 1;
+ break;
+ case V4L2_MPEG_VIDEO_ASPECT_4x3:
+ ar.width = 4;
+ ar.height = 3;
+ break;
+ case V4L2_MPEG_VIDEO_ASPECT_16x9:
+ ar.width = 16;
+ ar.height = 9;
+ break;
+ case V4L2_MPEG_VIDEO_ASPECT_221x100:
+ ar.width = 221;
+ ar.height = 100;
+ break;
+ default:
+ BUG();
+ }
+
+ dprintk(DBGLVL_ENC, "%s(%d) now %d:%d\n", __func__,
+ port->encoder_params.ctl_aspect,
+ ar.width, ar.height);
+
+ /* Aspect Ratio */
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
+ EU_VIDEO_INPUT_ASPECT_CONTROL,
+ sizeof(struct tmComResEncVideoInputAspectRatio), &ar);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ return ret;
+}
+
+int saa7164_api_set_usercontrol(struct saa7164_port *port, u8 ctl)
+{
+ struct saa7164_dev *dev = port->dev;
+ int ret;
+ u16 val;
+
+ if (ctl == PU_BRIGHTNESS_CONTROL)
+ val = port->ctl_brightness;
+ else
+ if (ctl == PU_CONTRAST_CONTROL)
+ val = port->ctl_contrast;
+ else
+ if (ctl == PU_HUE_CONTROL)
+ val = port->ctl_hue;
+ else
+ if (ctl == PU_SATURATION_CONTROL)
+ val = port->ctl_saturation;
+ else
+ if (ctl == PU_SHARPNESS_CONTROL)
+ val = port->ctl_sharpness;
+ else
+ return -EINVAL;
+
+ dprintk(DBGLVL_ENC, "%s() unitid=0x%x ctl=%d, val=%d\n",
+ __func__, port->encunit.vsourceid, ctl, val);
+
+ ret = saa7164_cmd_send(port->dev, port->encunit.vsourceid, SET_CUR,
+ ctl, sizeof(u16), &val);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ return ret;
+}
+
+int saa7164_api_get_usercontrol(struct saa7164_port *port, u8 ctl)
+{
+ struct saa7164_dev *dev = port->dev;
+ int ret;
+ u16 val;
+
+ ret = saa7164_cmd_send(port->dev, port->encunit.vsourceid, GET_CUR,
+ ctl, sizeof(u16), &val);
+ if (ret != SAA_OK) {
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+ return ret;
+ }
+
+ dprintk(DBGLVL_ENC, "%s() ctl=%d, val=%d\n",
+ __func__, ctl, val);
+
+ if (ctl == PU_BRIGHTNESS_CONTROL)
+ port->ctl_brightness = val;
+ else
+ if (ctl == PU_CONTRAST_CONTROL)
+ port->ctl_contrast = val;
+ else
+ if (ctl == PU_HUE_CONTROL)
+ port->ctl_hue = val;
+ else
+ if (ctl == PU_SATURATION_CONTROL)
+ port->ctl_saturation = val;
+ else
+ if (ctl == PU_SHARPNESS_CONTROL)
+ port->ctl_sharpness = val;
+
+ return ret;
+}
+
+int saa7164_api_set_videomux(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ u8 inputs[] = { 1, 2, 2, 2, 5, 5, 5 };
+ int ret;
+
+ dprintk(DBGLVL_ENC, "%s() v_mux=%d a_mux=%d\n",
+ __func__, port->mux_input, inputs[port->mux_input - 1]);
+
+ /* Audio Mute */
+ ret = saa7164_api_audio_mute(port, 1);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ /* Video Mux */
+ ret = saa7164_cmd_send(port->dev, port->vidproc.sourceid, SET_CUR,
+ SU_INPUT_SELECT_CONTROL, sizeof(u8), &port->mux_input);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ /* Audio Mux */
+ ret = saa7164_cmd_send(port->dev, port->audfeat.sourceid, SET_CUR,
+ SU_INPUT_SELECT_CONTROL, sizeof(u8),
+ &inputs[port->mux_input - 1]);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ /* Audio UnMute */
+ ret = saa7164_api_audio_mute(port, 0);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ return ret;
+}
+
+int saa7164_api_audio_mute(struct saa7164_port *port, int mute)
+{
+ struct saa7164_dev *dev = port->dev;
+ u8 v = mute;
+ int ret;
+
+ dprintk(DBGLVL_API, "%s(%d)\n", __func__, mute);
+
+ ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, SET_CUR,
+ MUTE_CONTROL, sizeof(u8), &v);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ return ret;
+}
+
+/* 0 = silence, 0xff = full */
+int saa7164_api_set_audio_volume(struct saa7164_port *port, s8 level)
+{
+ struct saa7164_dev *dev = port->dev;
+ s16 v, min, max;
+ int ret;
+
+ dprintk(DBGLVL_API, "%s(%d)\n", __func__, level);
+
+ /* Obtain the min/max ranges */
+ ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, GET_MIN,
+ VOLUME_CONTROL, sizeof(u16), &min);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, GET_MAX,
+ VOLUME_CONTROL, sizeof(u16), &max);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, GET_CUR,
+ (0x01 << 8) | VOLUME_CONTROL, sizeof(u16), &v);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ dprintk(DBGLVL_API, "%s(%d) min=%d max=%d cur=%d\n", __func__,
+ level, min, max, v);
+
+ v = level;
+ if (v < min)
+ v = min;
+ if (v > max)
+ v = max;
+
+ /* Left */
+ ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, SET_CUR,
+ (0x01 << 8) | VOLUME_CONTROL, sizeof(s16), &v);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ /* Right */
+ ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, SET_CUR,
+ (0x02 << 8) | VOLUME_CONTROL, sizeof(s16), &v);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, GET_CUR,
+ (0x01 << 8) | VOLUME_CONTROL, sizeof(u16), &v);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ dprintk(DBGLVL_API, "%s(%d) min=%d max=%d cur=%d\n", __func__,
+ level, min, max, v);
+
+ return ret;
+}
+
+int saa7164_api_set_audio_std(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ struct tmComResAudioDefaults lvl;
+ struct tmComResTunerStandard tvaudio;
+ int ret;
+
+ dprintk(DBGLVL_API, "%s()\n", __func__);
+
+ /* Establish default levels */
+ lvl.ucDecoderLevel = TMHW_LEV_ADJ_DECLEV_DEFAULT;
+ lvl.ucDecoderFM_Level = TMHW_LEV_ADJ_DECLEV_DEFAULT;
+ lvl.ucMonoLevel = TMHW_LEV_ADJ_MONOLEV_DEFAULT;
+ lvl.ucNICAM_Level = TMHW_LEV_ADJ_NICLEV_DEFAULT;
+ lvl.ucSAP_Level = TMHW_LEV_ADJ_SAPLEV_DEFAULT;
+ lvl.ucADC_Level = TMHW_LEV_ADJ_ADCLEV_DEFAULT;
+ ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, SET_CUR,
+ AUDIO_DEFAULT_CONTROL, sizeof(struct tmComResAudioDefaults),
+ &lvl);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ /* Manually select the appropriate TV audio standard */
+ if (port->encodernorm.id & V4L2_STD_NTSC) {
+ tvaudio.std = TU_STANDARD_NTSC_M;
+ tvaudio.country = 1;
+ } else {
+ tvaudio.std = TU_STANDARD_PAL_I;
+ tvaudio.country = 44;
+ }
+
+ ret = saa7164_cmd_send(port->dev, port->tunerunit.unitid, SET_CUR,
+ TU_STANDARD_CONTROL, sizeof(tvaudio), &tvaudio);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() TU_STANDARD_CONTROL error, ret = 0x%x\n",
+ __func__, ret);
+ return ret;
+}
+
+int saa7164_api_set_audio_detection(struct saa7164_port *port, int autodetect)
+{
+ struct saa7164_dev *dev = port->dev;
+ struct tmComResTunerStandardAuto p;
+ int ret;
+
+ dprintk(DBGLVL_API, "%s(%d)\n", __func__, autodetect);
+
+ /* Disable TV Audio autodetect if not already set (buggy) */
+ if (autodetect)
+ p.mode = TU_STANDARD_AUTO;
+ else
+ p.mode = TU_STANDARD_MANUAL;
+ ret = saa7164_cmd_send(port->dev, port->tunerunit.unitid, SET_CUR,
+ TU_STANDARD_AUTO_CONTROL, sizeof(p), &p);
+ if (ret != SAA_OK)
+ printk(KERN_ERR
+ "%s() TU_STANDARD_AUTO_CONTROL error, ret = 0x%x\n",
+ __func__, ret);
+
+ return ret;
+}
+
+int saa7164_api_get_videomux(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ int ret;
+
+ ret = saa7164_cmd_send(port->dev, port->vidproc.sourceid, GET_CUR,
+ SU_INPUT_SELECT_CONTROL, sizeof(u8), &port->mux_input);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ dprintk(DBGLVL_ENC, "%s() v_mux=%d\n",
+ __func__, port->mux_input);
+
+ return ret;
+}
+
+int saa7164_api_set_dif(struct saa7164_port *port, u8 reg, u8 val)
+{
+ struct saa7164_dev *dev = port->dev;
+
+ u16 len = 0;
+ u8 buf[256];
+ int ret;
+ u8 mas;
+
+ dprintk(DBGLVL_API, "%s(nr=%d type=%d val=%x)\n", __func__,
+ port->nr, port->type, val);
+
+ if (port->nr == 0)
+ mas = 0xd0;
+ else
+ mas = 0xe0;
+
+ memset(buf, 0, sizeof(buf));
+
+ buf[0x00] = 0x04;
+ buf[0x01] = 0x00;
+ buf[0x02] = 0x00;
+ buf[0x03] = 0x00;
+
+ buf[0x04] = 0x04;
+ buf[0x05] = 0x00;
+ buf[0x06] = 0x00;
+ buf[0x07] = 0x00;
+
+ buf[0x08] = reg;
+ buf[0x09] = 0x26;
+ buf[0x0a] = mas;
+ buf[0x0b] = 0xb0;
+
+ buf[0x0c] = val;
+ buf[0x0d] = 0x00;
+ buf[0x0e] = 0x00;
+ buf[0x0f] = 0x00;
+
+ ret = saa7164_cmd_send(dev, port->ifunit.unitid, GET_LEN,
+ EXU_REGISTER_ACCESS_CONTROL, sizeof(len), &len);
+ if (ret != SAA_OK) {
+ printk(KERN_ERR "%s() error, ret(1) = 0x%x\n", __func__, ret);
+ return -EIO;
+ }
+
+ ret = saa7164_cmd_send(dev, port->ifunit.unitid, SET_CUR,
+ EXU_REGISTER_ACCESS_CONTROL, len, &buf);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret(2) = 0x%x\n", __func__, ret);
+#if 0
+ saa7164_dumphex16(dev, buf, 16);
+#endif
+ return ret == SAA_OK ? 0 : -EIO;
+}
+
+/* Disable the IF block AGC controls */
+int saa7164_api_configure_dif(struct saa7164_port *port, u32 std)
+{
+ struct saa7164_dev *dev = port->dev;
+ int ret = 0;
+ u8 agc_disable;
+
+ dprintk(DBGLVL_API, "%s(nr=%d, 0x%x)\n", __func__, port->nr, std);
+
+ if (std & V4L2_STD_NTSC) {
+ dprintk(DBGLVL_API, " NTSC\n");
+ saa7164_api_set_dif(port, 0x00, 0x01); /* Video Standard */
+ agc_disable = 0;
+ } else if (std & V4L2_STD_PAL_I) {
+ dprintk(DBGLVL_API, " PAL-I\n");
+ saa7164_api_set_dif(port, 0x00, 0x08); /* Video Standard */
+ agc_disable = 0;
+ } else if (std & V4L2_STD_PAL_M) {
+ dprintk(DBGLVL_API, " PAL-M\n");
+ saa7164_api_set_dif(port, 0x00, 0x01); /* Video Standard */
+ agc_disable = 0;
+ } else if (std & V4L2_STD_PAL_N) {
+ dprintk(DBGLVL_API, " PAL-N\n");
+ saa7164_api_set_dif(port, 0x00, 0x01); /* Video Standard */
+ agc_disable = 0;
+ } else if (std & V4L2_STD_PAL_Nc) {
+ dprintk(DBGLVL_API, " PAL-Nc\n");
+ saa7164_api_set_dif(port, 0x00, 0x01); /* Video Standard */
+ agc_disable = 0;
+ } else if (std & V4L2_STD_PAL_B) {
+ dprintk(DBGLVL_API, " PAL-B\n");
+ saa7164_api_set_dif(port, 0x00, 0x02); /* Video Standard */
+ agc_disable = 0;
+ } else if (std & V4L2_STD_PAL_DK) {
+ dprintk(DBGLVL_API, " PAL-DK\n");
+ saa7164_api_set_dif(port, 0x00, 0x10); /* Video Standard */
+ agc_disable = 0;
+ } else if (std & V4L2_STD_SECAM_L) {
+ dprintk(DBGLVL_API, " SECAM-L\n");
+ saa7164_api_set_dif(port, 0x00, 0x20); /* Video Standard */
+ agc_disable = 0;
+ } else {
+ /* Unknown standard, assume DTV */
+ dprintk(DBGLVL_API, " Unknown (assuming DTV)\n");
+ /* Undefinded Video Standard */
+ saa7164_api_set_dif(port, 0x00, 0x80);
+ agc_disable = 1;
+ }
+
+ saa7164_api_set_dif(port, 0x48, 0xa0); /* AGC Functions 1 */
+ saa7164_api_set_dif(port, 0xc0, agc_disable); /* AGC Output Disable */
+ saa7164_api_set_dif(port, 0x7c, 0x04); /* CVBS EQ */
+ saa7164_api_set_dif(port, 0x04, 0x01); /* Active */
+ msleep(100);
+ saa7164_api_set_dif(port, 0x04, 0x00); /* Active (again) */
+ msleep(100);
+
+ return ret;
+}
+
+/* Ensure the dif is in the correct state for the operating mode
+ * (analog / dtv). We only configure the diff through the analog encoder
+ * so when we're in digital mode we need to find the appropriate encoder
+ * and use it to configure the DIF.
+ */
+int saa7164_api_initialize_dif(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ struct saa7164_port *p = NULL;
+ int ret = -EINVAL;
+ u32 std = 0;
+
+ dprintk(DBGLVL_API, "%s(nr=%d type=%d)\n", __func__,
+ port->nr, port->type);
+
+ if (port->type == SAA7164_MPEG_ENCODER) {
+ /* Pick any analog standard to init the diff.
+ * we'll come back during encoder_init'
+ * and set the correct standard if requried.
+ */
+ std = V4L2_STD_NTSC;
+ } else
+ if (port->type == SAA7164_MPEG_DVB) {
+ if (port->nr == SAA7164_PORT_TS1)
+ p = &dev->ports[SAA7164_PORT_ENC1];
+ else
+ p = &dev->ports[SAA7164_PORT_ENC2];
+ } else
+ if (port->type == SAA7164_MPEG_VBI) {
+ std = V4L2_STD_NTSC;
+ if (port->nr == SAA7164_PORT_VBI1)
+ p = &dev->ports[SAA7164_PORT_ENC1];
+ else
+ p = &dev->ports[SAA7164_PORT_ENC2];
+ } else
+ BUG();
+
+ if (p)
+ ret = saa7164_api_configure_dif(p, std);
+
+ return ret;
+}
+
+int saa7164_api_transition_port(struct saa7164_port *port, u8 mode)
+{
+ struct saa7164_dev *dev = port->dev;
+
+ int ret;
+
+ dprintk(DBGLVL_API, "%s(nr=%d unitid=0x%x,%d)\n",
+ __func__, port->nr, port->hwcfg.unitid, mode);
+
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid, SET_CUR,
+ SAA_STATE_CONTROL, sizeof(mode), &mode);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s(portnr %d unitid 0x%x) error, ret = 0x%x\n",
+ __func__, port->nr, port->hwcfg.unitid, ret);
+
+ return ret;
+}
+
+int saa7164_api_get_fw_version(struct saa7164_dev *dev, u32 *version)
+{
+ int ret;
+
+ ret = saa7164_cmd_send(dev, 0, GET_CUR,
+ GET_FW_VERSION_CONTROL, sizeof(u32), version);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ return ret;
+}
+
+int saa7164_api_read_eeprom(struct saa7164_dev *dev, u8 *buf, int buflen)
+{
+ u8 reg[] = { 0x0f, 0x00 };
+
+ if (buflen < 128)
+ return -ENOMEM;
+
+ /* Assumption: Hauppauge eeprom is at 0xa0 on on bus 0 */
+ /* TODO: Pull the details from the boards struct */
+ return saa7164_api_i2c_read(&dev->i2c_bus[0], 0xa0 >> 1, sizeof(reg),
+ &reg[0], 128, buf);
+}
+
+int saa7164_api_configure_port_vbi(struct saa7164_dev *dev,
+ struct saa7164_port *port)
+{
+ struct tmComResVBIFormatDescrHeader *fmt = &port->vbi_fmt_ntsc;
+
+ dprintk(DBGLVL_API, " bFormatIndex = 0x%x\n", fmt->bFormatIndex);
+ dprintk(DBGLVL_API, " VideoStandard = 0x%x\n", fmt->VideoStandard);
+ dprintk(DBGLVL_API, " StartLine = %d\n", fmt->StartLine);
+ dprintk(DBGLVL_API, " EndLine = %d\n", fmt->EndLine);
+ dprintk(DBGLVL_API, " FieldRate = %d\n", fmt->FieldRate);
+ dprintk(DBGLVL_API, " bNumLines = %d\n", fmt->bNumLines);
+
+ /* Cache the hardware configuration in the port */
+
+ port->bufcounter = port->hwcfg.BARLocation;
+ port->pitch = port->hwcfg.BARLocation + (2 * sizeof(u32));
+ port->bufsize = port->hwcfg.BARLocation + (3 * sizeof(u32));
+ port->bufoffset = port->hwcfg.BARLocation + (4 * sizeof(u32));
+ port->bufptr32l = port->hwcfg.BARLocation +
+ (4 * sizeof(u32)) +
+ (sizeof(u32) * port->hwcfg.buffercount) + sizeof(u32);
+ port->bufptr32h = port->hwcfg.BARLocation +
+ (4 * sizeof(u32)) +
+ (sizeof(u32) * port->hwcfg.buffercount);
+ port->bufptr64 = port->hwcfg.BARLocation +
+ (4 * sizeof(u32)) +
+ (sizeof(u32) * port->hwcfg.buffercount);
+ dprintk(DBGLVL_API, " = port->hwcfg.BARLocation = 0x%x\n",
+ port->hwcfg.BARLocation);
+
+ dprintk(DBGLVL_API, " = VS_FORMAT_VBI (becomes dev->en[%d])\n",
+ port->nr);
+
+ return 0;
+}
+
+int saa7164_api_configure_port_mpeg2ts(struct saa7164_dev *dev,
+ struct saa7164_port *port,
+ struct tmComResTSFormatDescrHeader *tsfmt)
+{
+ dprintk(DBGLVL_API, " bFormatIndex = 0x%x\n", tsfmt->bFormatIndex);
+ dprintk(DBGLVL_API, " bDataOffset = 0x%x\n", tsfmt->bDataOffset);
+ dprintk(DBGLVL_API, " bPacketLength= 0x%x\n", tsfmt->bPacketLength);
+ dprintk(DBGLVL_API, " bStrideLength= 0x%x\n", tsfmt->bStrideLength);
+ dprintk(DBGLVL_API, " bguid = (....)\n");
+
+ /* Cache the hardware configuration in the port */
+
+ port->bufcounter = port->hwcfg.BARLocation;
+ port->pitch = port->hwcfg.BARLocation + (2 * sizeof(u32));
+ port->bufsize = port->hwcfg.BARLocation + (3 * sizeof(u32));
+ port->bufoffset = port->hwcfg.BARLocation + (4 * sizeof(u32));
+ port->bufptr32l = port->hwcfg.BARLocation +
+ (4 * sizeof(u32)) +
+ (sizeof(u32) * port->hwcfg.buffercount) + sizeof(u32);
+ port->bufptr32h = port->hwcfg.BARLocation +
+ (4 * sizeof(u32)) +
+ (sizeof(u32) * port->hwcfg.buffercount);
+ port->bufptr64 = port->hwcfg.BARLocation +
+ (4 * sizeof(u32)) +
+ (sizeof(u32) * port->hwcfg.buffercount);
+ dprintk(DBGLVL_API, " = port->hwcfg.BARLocation = 0x%x\n",
+ port->hwcfg.BARLocation);
+
+ dprintk(DBGLVL_API, " = VS_FORMAT_MPEGTS (becomes dev->ts[%d])\n",
+ port->nr);
+
+ return 0;
+}
+
+int saa7164_api_configure_port_mpeg2ps(struct saa7164_dev *dev,
+ struct saa7164_port *port,
+ struct tmComResPSFormatDescrHeader *fmt)
+{
+ dprintk(DBGLVL_API, " bFormatIndex = 0x%x\n", fmt->bFormatIndex);
+ dprintk(DBGLVL_API, " wPacketLength= 0x%x\n", fmt->wPacketLength);
+ dprintk(DBGLVL_API, " wPackLength= 0x%x\n", fmt->wPackLength);
+ dprintk(DBGLVL_API, " bPackDataType= 0x%x\n", fmt->bPackDataType);
+
+ /* Cache the hardware configuration in the port */
+ /* TODO: CHECK THIS in the port config */
+ port->bufcounter = port->hwcfg.BARLocation;
+ port->pitch = port->hwcfg.BARLocation + (2 * sizeof(u32));
+ port->bufsize = port->hwcfg.BARLocation + (3 * sizeof(u32));
+ port->bufoffset = port->hwcfg.BARLocation + (4 * sizeof(u32));
+ port->bufptr32l = port->hwcfg.BARLocation +
+ (4 * sizeof(u32)) +
+ (sizeof(u32) * port->hwcfg.buffercount) + sizeof(u32);
+ port->bufptr32h = port->hwcfg.BARLocation +
+ (4 * sizeof(u32)) +
+ (sizeof(u32) * port->hwcfg.buffercount);
+ port->bufptr64 = port->hwcfg.BARLocation +
+ (4 * sizeof(u32)) +
+ (sizeof(u32) * port->hwcfg.buffercount);
+ dprintk(DBGLVL_API, " = port->hwcfg.BARLocation = 0x%x\n",
+ port->hwcfg.BARLocation);
+
+ dprintk(DBGLVL_API, " = VS_FORMAT_MPEGPS (becomes dev->enc[%d])\n",
+ port->nr);
+
+ return 0;
+}
+
+int saa7164_api_dump_subdevs(struct saa7164_dev *dev, u8 *buf, int len)
+{
+ struct saa7164_port *tsport = NULL;
+ struct saa7164_port *encport = NULL;
+ struct saa7164_port *vbiport = NULL;
+ u32 idx, next_offset;
+ int i;
+ struct tmComResDescrHeader *hdr, *t;
+ struct tmComResExtDevDescrHeader *exthdr;
+ struct tmComResPathDescrHeader *pathhdr;
+ struct tmComResAntTermDescrHeader *anttermhdr;
+ struct tmComResTunerDescrHeader *tunerunithdr;
+ struct tmComResDMATermDescrHeader *vcoutputtermhdr;
+ struct tmComResTSFormatDescrHeader *tsfmt;
+ struct tmComResPSFormatDescrHeader *psfmt;
+ struct tmComResSelDescrHeader *psel;
+ struct tmComResProcDescrHeader *pdh;
+ struct tmComResAFeatureDescrHeader *afd;
+ struct tmComResEncoderDescrHeader *edh;
+ struct tmComResVBIFormatDescrHeader *vbifmt;
+ u32 currpath = 0;
+
+ dprintk(DBGLVL_API,
+ "%s(?,?,%d) sizeof(struct tmComResDescrHeader) = %d bytes\n",
+ __func__, len, (u32)sizeof(struct tmComResDescrHeader));
+
+ for (idx = 0; idx < (len - sizeof(struct tmComResDescrHeader));) {
+
+ hdr = (struct tmComResDescrHeader *)(buf + idx);
+
+ if (hdr->type != CS_INTERFACE)
+ return SAA_ERR_NOT_SUPPORTED;
+
+ dprintk(DBGLVL_API, "@ 0x%x =\n", idx);
+ switch (hdr->subtype) {
+ case GENERAL_REQUEST:
+ dprintk(DBGLVL_API, " GENERAL_REQUEST\n");
+ break;
+ case VC_TUNER_PATH:
+ dprintk(DBGLVL_API, " VC_TUNER_PATH\n");
+ pathhdr = (struct tmComResPathDescrHeader *)(buf + idx);
+ dprintk(DBGLVL_API, " pathid = 0x%x\n",
+ pathhdr->pathid);
+ currpath = pathhdr->pathid;
+ break;
+ case VC_INPUT_TERMINAL:
+ dprintk(DBGLVL_API, " VC_INPUT_TERMINAL\n");
+ anttermhdr =
+ (struct tmComResAntTermDescrHeader *)(buf + idx);
+ dprintk(DBGLVL_API, " terminalid = 0x%x\n",
+ anttermhdr->terminalid);
+ dprintk(DBGLVL_API, " terminaltype = 0x%x\n",
+ anttermhdr->terminaltype);
+ switch (anttermhdr->terminaltype) {
+ case ITT_ANTENNA:
+ dprintk(DBGLVL_API, " = ITT_ANTENNA\n");
+ break;
+ case LINE_CONNECTOR:
+ dprintk(DBGLVL_API, " = LINE_CONNECTOR\n");
+ break;
+ case SPDIF_CONNECTOR:
+ dprintk(DBGLVL_API, " = SPDIF_CONNECTOR\n");
+ break;
+ case COMPOSITE_CONNECTOR:
+ dprintk(DBGLVL_API,
+ " = COMPOSITE_CONNECTOR\n");
+ break;
+ case SVIDEO_CONNECTOR:
+ dprintk(DBGLVL_API, " = SVIDEO_CONNECTOR\n");
+ break;
+ case COMPONENT_CONNECTOR:
+ dprintk(DBGLVL_API,
+ " = COMPONENT_CONNECTOR\n");
+ break;
+ case STANDARD_DMA:
+ dprintk(DBGLVL_API, " = STANDARD_DMA\n");
+ break;
+ default:
+ dprintk(DBGLVL_API, " = undefined (0x%x)\n",
+ anttermhdr->terminaltype);
+ }
+ dprintk(DBGLVL_API, " assocterminal= 0x%x\n",
+ anttermhdr->assocterminal);
+ dprintk(DBGLVL_API, " iterminal = 0x%x\n",
+ anttermhdr->iterminal);
+ dprintk(DBGLVL_API, " controlsize = 0x%x\n",
+ anttermhdr->controlsize);
+ break;
+ case VC_OUTPUT_TERMINAL:
+ dprintk(DBGLVL_API, " VC_OUTPUT_TERMINAL\n");
+ vcoutputtermhdr =
+ (struct tmComResDMATermDescrHeader *)(buf + idx);
+ dprintk(DBGLVL_API, " unitid = 0x%x\n",
+ vcoutputtermhdr->unitid);
+ dprintk(DBGLVL_API, " terminaltype = 0x%x\n",
+ vcoutputtermhdr->terminaltype);
+ switch (vcoutputtermhdr->terminaltype) {
+ case ITT_ANTENNA:
+ dprintk(DBGLVL_API, " = ITT_ANTENNA\n");
+ break;
+ case LINE_CONNECTOR:
+ dprintk(DBGLVL_API, " = LINE_CONNECTOR\n");
+ break;
+ case SPDIF_CONNECTOR:
+ dprintk(DBGLVL_API, " = SPDIF_CONNECTOR\n");
+ break;
+ case COMPOSITE_CONNECTOR:
+ dprintk(DBGLVL_API,
+ " = COMPOSITE_CONNECTOR\n");
+ break;
+ case SVIDEO_CONNECTOR:
+ dprintk(DBGLVL_API, " = SVIDEO_CONNECTOR\n");
+ break;
+ case COMPONENT_CONNECTOR:
+ dprintk(DBGLVL_API,
+ " = COMPONENT_CONNECTOR\n");
+ break;
+ case STANDARD_DMA:
+ dprintk(DBGLVL_API, " = STANDARD_DMA\n");
+ break;
+ default:
+ dprintk(DBGLVL_API, " = undefined (0x%x)\n",
+ vcoutputtermhdr->terminaltype);
+ }
+ dprintk(DBGLVL_API, " assocterminal= 0x%x\n",
+ vcoutputtermhdr->assocterminal);
+ dprintk(DBGLVL_API, " sourceid = 0x%x\n",
+ vcoutputtermhdr->sourceid);
+ dprintk(DBGLVL_API, " iterminal = 0x%x\n",
+ vcoutputtermhdr->iterminal);
+ dprintk(DBGLVL_API, " BARLocation = 0x%x\n",
+ vcoutputtermhdr->BARLocation);
+ dprintk(DBGLVL_API, " flags = 0x%x\n",
+ vcoutputtermhdr->flags);
+ dprintk(DBGLVL_API, " interruptid = 0x%x\n",
+ vcoutputtermhdr->interruptid);
+ dprintk(DBGLVL_API, " buffercount = 0x%x\n",
+ vcoutputtermhdr->buffercount);
+ dprintk(DBGLVL_API, " metadatasize = 0x%x\n",
+ vcoutputtermhdr->metadatasize);
+ dprintk(DBGLVL_API, " controlsize = 0x%x\n",
+ vcoutputtermhdr->controlsize);
+ dprintk(DBGLVL_API, " numformats = 0x%x\n",
+ vcoutputtermhdr->numformats);
+
+ t = (struct tmComResDescrHeader *)
+ ((struct tmComResDMATermDescrHeader *)(buf + idx));
+ next_offset = idx + (vcoutputtermhdr->len);
+ for (i = 0; i < vcoutputtermhdr->numformats; i++) {
+ t = (struct tmComResDescrHeader *)
+ (buf + next_offset);
+ switch (t->subtype) {
+ case VS_FORMAT_MPEG2TS:
+ tsfmt =
+ (struct tmComResTSFormatDescrHeader *)t;
+ if (currpath == 1)
+ tsport = &dev->ports[SAA7164_PORT_TS1];
+ else
+ tsport = &dev->ports[SAA7164_PORT_TS2];
+ memcpy(&tsport->hwcfg, vcoutputtermhdr,
+ sizeof(*vcoutputtermhdr));
+ saa7164_api_configure_port_mpeg2ts(dev,
+ tsport, tsfmt);
+ break;
+ case VS_FORMAT_MPEG2PS:
+ psfmt =
+ (struct tmComResPSFormatDescrHeader *)t;
+ if (currpath == 1)
+ encport = &dev->ports[SAA7164_PORT_ENC1];
+ else
+ encport = &dev->ports[SAA7164_PORT_ENC2];
+ memcpy(&encport->hwcfg, vcoutputtermhdr,
+ sizeof(*vcoutputtermhdr));
+ saa7164_api_configure_port_mpeg2ps(dev,
+ encport, psfmt);
+ break;
+ case VS_FORMAT_VBI:
+ vbifmt =
+ (struct tmComResVBIFormatDescrHeader *)t;
+ if (currpath == 1)
+ vbiport = &dev->ports[SAA7164_PORT_VBI1];
+ else
+ vbiport = &dev->ports[SAA7164_PORT_VBI2];
+ memcpy(&vbiport->hwcfg, vcoutputtermhdr,
+ sizeof(*vcoutputtermhdr));
+ memcpy(&vbiport->vbi_fmt_ntsc, vbifmt,
+ sizeof(*vbifmt));
+ saa7164_api_configure_port_vbi(dev,
+ vbiport);
+ break;
+ case VS_FORMAT_RDS:
+ dprintk(DBGLVL_API,
+ " = VS_FORMAT_RDS\n");
+ break;
+ case VS_FORMAT_UNCOMPRESSED:
+ dprintk(DBGLVL_API,
+ " = VS_FORMAT_UNCOMPRESSED\n");
+ break;
+ case VS_FORMAT_TYPE:
+ dprintk(DBGLVL_API,
+ " = VS_FORMAT_TYPE\n");
+ break;
+ default:
+ dprintk(DBGLVL_API,
+ " = undefined (0x%x)\n",
+ t->subtype);
+ }
+ next_offset += t->len;
+ }
+
+ break;
+ case TUNER_UNIT:
+ dprintk(DBGLVL_API, " TUNER_UNIT\n");
+ tunerunithdr =
+ (struct tmComResTunerDescrHeader *)(buf + idx);
+ dprintk(DBGLVL_API, " unitid = 0x%x\n",
+ tunerunithdr->unitid);
+ dprintk(DBGLVL_API, " sourceid = 0x%x\n",
+ tunerunithdr->sourceid);
+ dprintk(DBGLVL_API, " iunit = 0x%x\n",
+ tunerunithdr->iunit);
+ dprintk(DBGLVL_API, " tuningstandards = 0x%x\n",
+ tunerunithdr->tuningstandards);
+ dprintk(DBGLVL_API, " controlsize = 0x%x\n",
+ tunerunithdr->controlsize);
+ dprintk(DBGLVL_API, " controls = 0x%x\n",
+ tunerunithdr->controls);
+
+ if (tunerunithdr->unitid == tunerunithdr->iunit) {
+ if (currpath == 1)
+ encport = &dev->ports[SAA7164_PORT_ENC1];
+ else
+ encport = &dev->ports[SAA7164_PORT_ENC2];
+ memcpy(&encport->tunerunit, tunerunithdr,
+ sizeof(struct tmComResTunerDescrHeader));
+ dprintk(DBGLVL_API,
+ " (becomes dev->enc[%d] tuner)\n",
+ encport->nr);
+ }
+ break;
+ case VC_SELECTOR_UNIT:
+ psel = (struct tmComResSelDescrHeader *)(buf + idx);
+ dprintk(DBGLVL_API, " VC_SELECTOR_UNIT\n");
+ dprintk(DBGLVL_API, " unitid = 0x%x\n",
+ psel->unitid);
+ dprintk(DBGLVL_API, " nrinpins = 0x%x\n",
+ psel->nrinpins);
+ dprintk(DBGLVL_API, " sourceid = 0x%x\n",
+ psel->sourceid);
+ break;
+ case VC_PROCESSING_UNIT:
+ pdh = (struct tmComResProcDescrHeader *)(buf + idx);
+ dprintk(DBGLVL_API, " VC_PROCESSING_UNIT\n");
+ dprintk(DBGLVL_API, " unitid = 0x%x\n",
+ pdh->unitid);
+ dprintk(DBGLVL_API, " sourceid = 0x%x\n",
+ pdh->sourceid);
+ dprintk(DBGLVL_API, " controlsize = 0x%x\n",
+ pdh->controlsize);
+ if (pdh->controlsize == 0x04) {
+ if (currpath == 1)
+ encport = &dev->ports[SAA7164_PORT_ENC1];
+ else
+ encport = &dev->ports[SAA7164_PORT_ENC2];
+ memcpy(&encport->vidproc, pdh,
+ sizeof(struct tmComResProcDescrHeader));
+ dprintk(DBGLVL_API, " (becomes dev->enc[%d])\n",
+ encport->nr);
+ }
+ break;
+ case FEATURE_UNIT:
+ afd = (struct tmComResAFeatureDescrHeader *)(buf + idx);
+ dprintk(DBGLVL_API, " FEATURE_UNIT\n");
+ dprintk(DBGLVL_API, " unitid = 0x%x\n",
+ afd->unitid);
+ dprintk(DBGLVL_API, " sourceid = 0x%x\n",
+ afd->sourceid);
+ dprintk(DBGLVL_API, " controlsize = 0x%x\n",
+ afd->controlsize);
+ if (currpath == 1)
+ encport = &dev->ports[SAA7164_PORT_ENC1];
+ else
+ encport = &dev->ports[SAA7164_PORT_ENC2];
+ memcpy(&encport->audfeat, afd,
+ sizeof(struct tmComResAFeatureDescrHeader));
+ dprintk(DBGLVL_API, " (becomes dev->enc[%d])\n",
+ encport->nr);
+ break;
+ case ENCODER_UNIT:
+ edh = (struct tmComResEncoderDescrHeader *)(buf + idx);
+ dprintk(DBGLVL_API, " ENCODER_UNIT\n");
+ dprintk(DBGLVL_API, " subtype = 0x%x\n", edh->subtype);
+ dprintk(DBGLVL_API, " unitid = 0x%x\n", edh->unitid);
+ dprintk(DBGLVL_API, " vsourceid = 0x%x\n",
+ edh->vsourceid);
+ dprintk(DBGLVL_API, " asourceid = 0x%x\n",
+ edh->asourceid);
+ dprintk(DBGLVL_API, " iunit = 0x%x\n", edh->iunit);
+ if (edh->iunit == edh->unitid) {
+ if (currpath == 1)
+ encport = &dev->ports[SAA7164_PORT_ENC1];
+ else
+ encport = &dev->ports[SAA7164_PORT_ENC2];
+ memcpy(&encport->encunit, edh,
+ sizeof(struct tmComResEncoderDescrHeader));
+ dprintk(DBGLVL_API,
+ " (becomes dev->enc[%d])\n",
+ encport->nr);
+ }
+ break;
+ case EXTENSION_UNIT:
+ dprintk(DBGLVL_API, " EXTENSION_UNIT\n");
+ exthdr = (struct tmComResExtDevDescrHeader *)(buf + idx);
+ dprintk(DBGLVL_API, " unitid = 0x%x\n",
+ exthdr->unitid);
+ dprintk(DBGLVL_API, " deviceid = 0x%x\n",
+ exthdr->deviceid);
+ dprintk(DBGLVL_API, " devicetype = 0x%x\n",
+ exthdr->devicetype);
+ if (exthdr->devicetype & 0x1)
+ dprintk(DBGLVL_API, " = Decoder Device\n");
+ if (exthdr->devicetype & 0x2)
+ dprintk(DBGLVL_API, " = GPIO Source\n");
+ if (exthdr->devicetype & 0x4)
+ dprintk(DBGLVL_API, " = Video Decoder\n");
+ if (exthdr->devicetype & 0x8)
+ dprintk(DBGLVL_API, " = Audio Decoder\n");
+ if (exthdr->devicetype & 0x20)
+ dprintk(DBGLVL_API, " = Crossbar\n");
+ if (exthdr->devicetype & 0x40)
+ dprintk(DBGLVL_API, " = Tuner\n");
+ if (exthdr->devicetype & 0x80)
+ dprintk(DBGLVL_API, " = IF PLL\n");
+ if (exthdr->devicetype & 0x100)
+ dprintk(DBGLVL_API, " = Demodulator\n");
+ if (exthdr->devicetype & 0x200)
+ dprintk(DBGLVL_API, " = RDS Decoder\n");
+ if (exthdr->devicetype & 0x400)
+ dprintk(DBGLVL_API, " = Encoder\n");
+ if (exthdr->devicetype & 0x800)
+ dprintk(DBGLVL_API, " = IR Decoder\n");
+ if (exthdr->devicetype & 0x1000)
+ dprintk(DBGLVL_API, " = EEPROM\n");
+ if (exthdr->devicetype & 0x2000)
+ dprintk(DBGLVL_API,
+ " = VBI Decoder\n");
+ if (exthdr->devicetype & 0x10000)
+ dprintk(DBGLVL_API,
+ " = Streaming Device\n");
+ if (exthdr->devicetype & 0x20000)
+ dprintk(DBGLVL_API,
+ " = DRM Device\n");
+ if (exthdr->devicetype & 0x40000000)
+ dprintk(DBGLVL_API,
+ " = Generic Device\n");
+ if (exthdr->devicetype & 0x80000000)
+ dprintk(DBGLVL_API,
+ " = Config Space Device\n");
+ dprintk(DBGLVL_API, " numgpiopins = 0x%x\n",
+ exthdr->numgpiopins);
+ dprintk(DBGLVL_API, " numgpiogroups = 0x%x\n",
+ exthdr->numgpiogroups);
+ dprintk(DBGLVL_API, " controlsize = 0x%x\n",
+ exthdr->controlsize);
+ if (exthdr->devicetype & 0x80) {
+ if (currpath == 1)
+ encport = &dev->ports[SAA7164_PORT_ENC1];
+ else
+ encport = &dev->ports[SAA7164_PORT_ENC2];
+ memcpy(&encport->ifunit, exthdr,
+ sizeof(struct tmComResExtDevDescrHeader));
+ dprintk(DBGLVL_API,
+ " (becomes dev->enc[%d])\n",
+ encport->nr);
+ }
+ break;
+ case PVC_INFRARED_UNIT:
+ dprintk(DBGLVL_API, " PVC_INFRARED_UNIT\n");
+ break;
+ case DRM_UNIT:
+ dprintk(DBGLVL_API, " DRM_UNIT\n");
+ break;
+ default:
+ dprintk(DBGLVL_API, "default %d\n", hdr->subtype);
+ }
+
+ dprintk(DBGLVL_API, " 1.%x\n", hdr->len);
+ dprintk(DBGLVL_API, " 2.%x\n", hdr->type);
+ dprintk(DBGLVL_API, " 3.%x\n", hdr->subtype);
+ dprintk(DBGLVL_API, " 4.%x\n", hdr->unitid);
+
+ idx += hdr->len;
+ }
+
+ return 0;
+}
+
+int saa7164_api_enum_subdevs(struct saa7164_dev *dev)
+{
+ int ret;
+ u32 buflen = 0;
+ u8 *buf;
+
+ dprintk(DBGLVL_API, "%s()\n", __func__);
+
+ /* Get the total descriptor length */
+ ret = saa7164_cmd_send(dev, 0, GET_LEN,
+ GET_DESCRIPTORS_CONTROL, sizeof(buflen), &buflen);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ dprintk(DBGLVL_API, "%s() total descriptor size = %d bytes.\n",
+ __func__, buflen);
+
+ /* Allocate enough storage for all of the descs */
+ buf = kzalloc(buflen, GFP_KERNEL);
+ if (!buf)
+ return SAA_ERR_NO_RESOURCES;
+
+ /* Retrieve them */
+ ret = saa7164_cmd_send(dev, 0, GET_CUR,
+ GET_DESCRIPTORS_CONTROL, buflen, buf);
+ if (ret != SAA_OK) {
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+ goto out;
+ }
+
+ if (saa_debug & DBGLVL_API)
+ saa7164_dumphex16(dev, buf, (buflen/16)*16);
+
+ saa7164_api_dump_subdevs(dev, buf, buflen);
+
+out:
+ kfree(buf);
+ return ret;
+}
+
+int saa7164_api_i2c_read(struct saa7164_i2c *bus, u8 addr, u32 reglen, u8 *reg,
+ u32 datalen, u8 *data)
+{
+ struct saa7164_dev *dev = bus->dev;
+ u16 len = 0;
+ int unitid;
+ u32 regval;
+ u8 buf[256];
+ int ret;
+
+ dprintk(DBGLVL_API, "%s()\n", __func__);
+
+ if (reglen > 4)
+ return -EIO;
+
+ if (reglen == 1)
+ regval = *(reg);
+ else
+ if (reglen == 2)
+ regval = ((*(reg) << 8) || *(reg+1));
+ else
+ if (reglen == 3)
+ regval = ((*(reg) << 16) | (*(reg+1) << 8) | *(reg+2));
+ else
+ if (reglen == 4)
+ regval = ((*(reg) << 24) | (*(reg+1) << 16) |
+ (*(reg+2) << 8) | *(reg+3));
+
+ /* Prepare the send buffer */
+ /* Bytes 00-03 source register length
+ * 04-07 source bytes to read
+ * 08... register address
+ */
+ memset(buf, 0, sizeof(buf));
+ memcpy((buf + 2 * sizeof(u32) + 0), reg, reglen);
+ *((u32 *)(buf + 0 * sizeof(u32))) = reglen;
+ *((u32 *)(buf + 1 * sizeof(u32))) = datalen;
+
+ unitid = saa7164_i2caddr_to_unitid(bus, addr);
+ if (unitid < 0) {
+ printk(KERN_ERR
+ "%s() error, cannot translate regaddr 0x%x to unitid\n",
+ __func__, addr);
+ return -EIO;
+ }
+
+ ret = saa7164_cmd_send(bus->dev, unitid, GET_LEN,
+ EXU_REGISTER_ACCESS_CONTROL, sizeof(len), &len);
+ if (ret != SAA_OK) {
+ printk(KERN_ERR "%s() error, ret(1) = 0x%x\n", __func__, ret);
+ return -EIO;
+ }
+
+ dprintk(DBGLVL_API, "%s() len = %d bytes\n", __func__, len);
+
+ if (saa_debug & DBGLVL_I2C)
+ saa7164_dumphex16(dev, buf, 2 * 16);
+
+ ret = saa7164_cmd_send(bus->dev, unitid, GET_CUR,
+ EXU_REGISTER_ACCESS_CONTROL, len, &buf);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret(2) = 0x%x\n", __func__, ret);
+ else {
+ if (saa_debug & DBGLVL_I2C)
+ saa7164_dumphex16(dev, buf, sizeof(buf));
+ memcpy(data, (buf + 2 * sizeof(u32) + reglen), datalen);
+ }
+
+ return ret == SAA_OK ? 0 : -EIO;
+}
+
+/* For a given 8 bit i2c address device, write the buffer */
+int saa7164_api_i2c_write(struct saa7164_i2c *bus, u8 addr, u32 datalen,
+ u8 *data)
+{
+ struct saa7164_dev *dev = bus->dev;
+ u16 len = 0;
+ int unitid;
+ int reglen;
+ u8 buf[256];
+ int ret;
+
+ dprintk(DBGLVL_API, "%s()\n", __func__);
+
+ if ((datalen == 0) || (datalen > 232))
+ return -EIO;
+
+ memset(buf, 0, sizeof(buf));
+
+ unitid = saa7164_i2caddr_to_unitid(bus, addr);
+ if (unitid < 0) {
+ printk(KERN_ERR
+ "%s() error, cannot translate regaddr 0x%x to unitid\n",
+ __func__, addr);
+ return -EIO;
+ }
+
+ reglen = saa7164_i2caddr_to_reglen(bus, addr);
+ if (reglen < 0) {
+ printk(KERN_ERR
+ "%s() error, cannot translate regaddr to reglen\n",
+ __func__);
+ return -EIO;
+ }
+
+ ret = saa7164_cmd_send(bus->dev, unitid, GET_LEN,
+ EXU_REGISTER_ACCESS_CONTROL, sizeof(len), &len);
+ if (ret != SAA_OK) {
+ printk(KERN_ERR "%s() error, ret(1) = 0x%x\n", __func__, ret);
+ return -EIO;
+ }
+
+ dprintk(DBGLVL_API, "%s() len = %d bytes\n", __func__, len);
+
+ /* Prepare the send buffer */
+ /* Bytes 00-03 dest register length
+ * 04-07 dest bytes to write
+ * 08... register address
+ */
+ *((u32 *)(buf + 0 * sizeof(u32))) = reglen;
+ *((u32 *)(buf + 1 * sizeof(u32))) = datalen - reglen;
+ memcpy((buf + 2 * sizeof(u32)), data, datalen);
+
+ if (saa_debug & DBGLVL_I2C)
+ saa7164_dumphex16(dev, buf, sizeof(buf));
+
+ ret = saa7164_cmd_send(bus->dev, unitid, SET_CUR,
+ EXU_REGISTER_ACCESS_CONTROL, len, &buf);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret(2) = 0x%x\n", __func__, ret);
+
+ return ret == SAA_OK ? 0 : -EIO;
+}
+
+int saa7164_api_modify_gpio(struct saa7164_dev *dev, u8 unitid,
+ u8 pin, u8 state)
+{
+ int ret;
+ struct tmComResGPIO t;
+
+ dprintk(DBGLVL_API, "%s(0x%x, %d, %d)\n",
+ __func__, unitid, pin, state);
+
+ if ((pin > 7) || (state > 2))
+ return SAA_ERR_BAD_PARAMETER;
+
+ t.pin = pin;
+ t.state = state;
+
+ ret = saa7164_cmd_send(dev, unitid, SET_CUR,
+ EXU_GPIO_CONTROL, sizeof(t), &t);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n",
+ __func__, ret);
+
+ return ret;
+}
+
+int saa7164_api_set_gpiobit(struct saa7164_dev *dev, u8 unitid,
+ u8 pin)
+{
+ return saa7164_api_modify_gpio(dev, unitid, pin, 1);
+}
+
+int saa7164_api_clear_gpiobit(struct saa7164_dev *dev, u8 unitid,
+ u8 pin)
+{
+ return saa7164_api_modify_gpio(dev, unitid, pin, 0);
+}
+
diff --git a/drivers/media/video/saa7164/saa7164-buffer.c b/drivers/media/video/saa7164/saa7164-buffer.c
new file mode 100644
index 00000000..66696fa8
--- /dev/null
+++ b/drivers/media/video/saa7164/saa7164-buffer.c
@@ -0,0 +1,322 @@
+/*
+ * Driver for the NXP SAA7164 PCIe bridge
+ *
+ * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/slab.h>
+
+#include "saa7164.h"
+
+/* The PCI address space for buffer handling looks like this:
+ *
+ * +-u32 wide-------------+
+ * | +
+ * +-u64 wide------------------------------------+
+ * + +
+ * +----------------------+
+ * | CurrentBufferPtr + Pointer to current PCI buffer >-+
+ * +----------------------+ |
+ * | Unused + |
+ * +----------------------+ |
+ * | Pitch + = 188 (bytes) |
+ * +----------------------+ |
+ * | PCI buffer size + = pitch * number of lines (312) |
+ * +----------------------+ |
+ * |0| Buf0 Write Offset + |
+ * +----------------------+ v
+ * |1| Buf1 Write Offset + |
+ * +----------------------+ |
+ * |2| Buf2 Write Offset + |
+ * +----------------------+ |
+ * |3| Buf3 Write Offset + |
+ * +----------------------+ |
+ * ... More write offsets |
+ * +---------------------------------------------+ |
+ * +0| set of ptrs to PCI pagetables + |
+ * +---------------------------------------------+ |
+ * +1| set of ptrs to PCI pagetables + <--------+
+ * +---------------------------------------------+
+ * +2| set of ptrs to PCI pagetables +
+ * +---------------------------------------------+
+ * +3| set of ptrs to PCI pagetables + >--+
+ * +---------------------------------------------+ |
+ * ... More buffer pointers | +----------------+
+ * +->| pt[0] TS data |
+ * | +----------------+
+ * |
+ * | +----------------+
+ * +->| pt[1] TS data |
+ * | +----------------+
+ * | etc
+ */
+
+void saa7164_buffer_display(struct saa7164_buffer *buf)
+{
+ struct saa7164_dev *dev = buf->port->dev;
+ int i;
+
+ dprintk(DBGLVL_BUF, "%s() buffer @ 0x%p nr=%d\n",
+ __func__, buf, buf->idx);
+ dprintk(DBGLVL_BUF, " pci_cpu @ 0x%p dma @ 0x%08llx len = 0x%x\n",
+ buf->cpu, (long long)buf->dma, buf->pci_size);
+ dprintk(DBGLVL_BUF, " pt_cpu @ 0x%p pt_dma @ 0x%08llx len = 0x%x\n",
+ buf->pt_cpu, (long long)buf->pt_dma, buf->pt_size);
+
+ /* Format the Page Table Entries to point into the data buffer */
+ for (i = 0 ; i < SAA7164_PT_ENTRIES; i++) {
+
+ dprintk(DBGLVL_BUF, " pt[%02d] = 0x%p -> 0x%llx\n",
+ i, buf->pt_cpu, (u64)*(buf->pt_cpu));
+
+ }
+}
+/* Allocate a new buffer structure and associated PCI space in bytes.
+ * len must be a multiple of sizeof(u64)
+ */
+struct saa7164_buffer *saa7164_buffer_alloc(struct saa7164_port *port,
+ u32 len)
+{
+ struct tmHWStreamParameters *params = &port->hw_streamingparams;
+ struct saa7164_buffer *buf = NULL;
+ struct saa7164_dev *dev = port->dev;
+ int i;
+
+ if ((len == 0) || (len >= 65536) || (len % sizeof(u64))) {
+ log_warn("%s() SAA_ERR_BAD_PARAMETER\n", __func__);
+ goto ret;
+ }
+
+ buf = kzalloc(sizeof(struct saa7164_buffer), GFP_KERNEL);
+ if (!buf) {
+ log_warn("%s() SAA_ERR_NO_RESOURCES\n", __func__);
+ goto ret;
+ }
+
+ buf->idx = -1;
+ buf->port = port;
+ buf->flags = SAA7164_BUFFER_FREE;
+ buf->pos = 0;
+ buf->actual_size = params->pitch * params->numberoflines;
+ buf->crc = 0;
+ /* TODO: arg len is being ignored */
+ buf->pci_size = SAA7164_PT_ENTRIES * 0x1000;
+ buf->pt_size = (SAA7164_PT_ENTRIES * sizeof(u64)) + 0x1000;
+
+ /* Allocate contiguous memory */
+ buf->cpu = pci_alloc_consistent(port->dev->pci, buf->pci_size,
+ &buf->dma);
+ if (!buf->cpu)
+ goto fail1;
+
+ buf->pt_cpu = pci_alloc_consistent(port->dev->pci, buf->pt_size,
+ &buf->pt_dma);
+ if (!buf->pt_cpu)
+ goto fail2;
+
+ /* init the buffers to a known pattern, easier during debugging */
+ memset_io(buf->cpu, 0xff, buf->pci_size);
+ buf->crc = crc32(0, buf->cpu, buf->actual_size);
+ memset_io(buf->pt_cpu, 0xff, buf->pt_size);
+
+ dprintk(DBGLVL_BUF, "%s() allocated buffer @ 0x%p (%d pageptrs)\n",
+ __func__, buf, params->numpagetables);
+ dprintk(DBGLVL_BUF, " pci_cpu @ 0x%p dma @ 0x%08lx len = 0x%x\n",
+ buf->cpu, (long)buf->dma, buf->pci_size);
+ dprintk(DBGLVL_BUF, " pt_cpu @ 0x%p pt_dma @ 0x%08lx len = 0x%x\n",
+ buf->pt_cpu, (long)buf->pt_dma, buf->pt_size);
+
+ /* Format the Page Table Entries to point into the data buffer */
+ for (i = 0 ; i < params->numpagetables; i++) {
+
+ *(buf->pt_cpu + i) = buf->dma + (i * 0x1000); /* TODO */
+ dprintk(DBGLVL_BUF, " pt[%02d] = 0x%p -> 0x%llx\n",
+ i, buf->pt_cpu, (u64)*(buf->pt_cpu));
+
+ }
+
+ goto ret;
+
+fail2:
+ pci_free_consistent(port->dev->pci, buf->pci_size, buf->cpu, buf->dma);
+fail1:
+ kfree(buf);
+
+ buf = NULL;
+ret:
+ return buf;
+}
+
+int saa7164_buffer_dealloc(struct saa7164_buffer *buf)
+{
+ struct saa7164_dev *dev;
+
+ if (!buf || !buf->port)
+ return SAA_ERR_BAD_PARAMETER;
+ dev = buf->port->dev;
+
+ dprintk(DBGLVL_BUF, "%s() deallocating buffer @ 0x%p\n",
+ __func__, buf);
+
+ if (buf->flags != SAA7164_BUFFER_FREE)
+ log_warn(" freeing a non-free buffer\n");
+
+ pci_free_consistent(dev->pci, buf->pci_size, buf->cpu, buf->dma);
+ pci_free_consistent(dev->pci, buf->pt_size, buf->pt_cpu, buf->pt_dma);
+
+ kfree(buf);
+
+ return SAA_OK;
+}
+
+int saa7164_buffer_zero_offsets(struct saa7164_port *port, int i)
+{
+ struct saa7164_dev *dev = port->dev;
+
+ if ((i < 0) || (i >= port->hwcfg.buffercount))
+ return -EINVAL;
+
+ dprintk(DBGLVL_BUF, "%s(idx = %d)\n", __func__, i);
+
+ saa7164_writel(port->bufoffset + (sizeof(u32) * i), 0);
+
+ return 0;
+}
+
+/* Write a buffer into the hardware */
+int saa7164_buffer_activate(struct saa7164_buffer *buf, int i)
+{
+ struct saa7164_port *port = buf->port;
+ struct saa7164_dev *dev = port->dev;
+
+ if ((i < 0) || (i >= port->hwcfg.buffercount))
+ return -EINVAL;
+
+ dprintk(DBGLVL_BUF, "%s(idx = %d)\n", __func__, i);
+
+ buf->idx = i; /* Note of which buffer list index position we occupy */
+ buf->flags = SAA7164_BUFFER_BUSY;
+ buf->pos = 0;
+
+ /* TODO: Review this in light of 32v64 assignments */
+ saa7164_writel(port->bufoffset + (sizeof(u32) * i), 0);
+ saa7164_writel(port->bufptr32h + ((sizeof(u32) * 2) * i), buf->pt_dma);
+ saa7164_writel(port->bufptr32l + ((sizeof(u32) * 2) * i), 0);
+
+ dprintk(DBGLVL_BUF, " buf[%d] offset 0x%llx (0x%x) "
+ "buf 0x%llx/%llx (0x%x/%x) nr=%d\n",
+ buf->idx,
+ (u64)port->bufoffset + (i * sizeof(u32)),
+ saa7164_readl(port->bufoffset + (sizeof(u32) * i)),
+ (u64)port->bufptr32h + ((sizeof(u32) * 2) * i),
+ (u64)port->bufptr32l + ((sizeof(u32) * 2) * i),
+ saa7164_readl(port->bufptr32h + ((sizeof(u32) * i) * 2)),
+ saa7164_readl(port->bufptr32l + ((sizeof(u32) * i) * 2)),
+ buf->idx);
+
+ return 0;
+}
+
+int saa7164_buffer_cfg_port(struct saa7164_port *port)
+{
+ struct tmHWStreamParameters *params = &port->hw_streamingparams;
+ struct saa7164_dev *dev = port->dev;
+ struct saa7164_buffer *buf;
+ struct list_head *c, *n;
+ int i = 0;
+
+ dprintk(DBGLVL_BUF, "%s(port=%d)\n", __func__, port->nr);
+
+ saa7164_writel(port->bufcounter, 0);
+ saa7164_writel(port->pitch, params->pitch);
+ saa7164_writel(port->bufsize, params->pitch * params->numberoflines);
+
+ dprintk(DBGLVL_BUF, " configured:\n");
+ dprintk(DBGLVL_BUF, " lmmio 0x%p\n", dev->lmmio);
+ dprintk(DBGLVL_BUF, " bufcounter 0x%x = 0x%x\n", port->bufcounter,
+ saa7164_readl(port->bufcounter));
+
+ dprintk(DBGLVL_BUF, " pitch 0x%x = %d\n", port->pitch,
+ saa7164_readl(port->pitch));
+
+ dprintk(DBGLVL_BUF, " bufsize 0x%x = %d\n", port->bufsize,
+ saa7164_readl(port->bufsize));
+
+ dprintk(DBGLVL_BUF, " buffercount = %d\n", port->hwcfg.buffercount);
+ dprintk(DBGLVL_BUF, " bufoffset = 0x%x\n", port->bufoffset);
+ dprintk(DBGLVL_BUF, " bufptr32h = 0x%x\n", port->bufptr32h);
+ dprintk(DBGLVL_BUF, " bufptr32l = 0x%x\n", port->bufptr32l);
+
+ /* Poke the buffers and offsets into PCI space */
+ mutex_lock(&port->dmaqueue_lock);
+ list_for_each_safe(c, n, &port->dmaqueue.list) {
+ buf = list_entry(c, struct saa7164_buffer, list);
+
+ if (buf->flags != SAA7164_BUFFER_FREE)
+ BUG();
+
+ /* Place the buffer in the h/w queue */
+ saa7164_buffer_activate(buf, i);
+
+ /* Don't exceed the device maximum # bufs */
+ if (i++ > port->hwcfg.buffercount)
+ BUG();
+
+ }
+ mutex_unlock(&port->dmaqueue_lock);
+
+ return 0;
+}
+
+struct saa7164_user_buffer *saa7164_buffer_alloc_user(struct saa7164_dev *dev,
+ u32 len)
+{
+ struct saa7164_user_buffer *buf;
+
+ buf = kzalloc(sizeof(struct saa7164_user_buffer), GFP_KERNEL);
+ if (!buf)
+ return NULL;
+
+ buf->data = kzalloc(len, GFP_KERNEL);
+
+ if (!buf->data) {
+ kfree(buf);
+ return NULL;
+ }
+
+ buf->actual_size = len;
+ buf->pos = 0;
+ buf->crc = 0;
+
+ dprintk(DBGLVL_BUF, "%s() allocated user buffer @ 0x%p\n",
+ __func__, buf);
+
+ return buf;
+}
+
+void saa7164_buffer_dealloc_user(struct saa7164_user_buffer *buf)
+{
+ if (!buf)
+ return;
+
+ kfree(buf->data);
+ buf->data = NULL;
+
+ kfree(buf);
+}
+
diff --git a/drivers/media/video/saa7164/saa7164-bus.c b/drivers/media/video/saa7164/saa7164-bus.c
new file mode 100644
index 00000000..a7f58a99
--- /dev/null
+++ b/drivers/media/video/saa7164/saa7164-bus.c
@@ -0,0 +1,475 @@
+/*
+ * Driver for the NXP SAA7164 PCIe bridge
+ *
+ * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "saa7164.h"
+
+/* The message bus to/from the firmware is a ring buffer in PCI address
+ * space. Establish the defaults.
+ */
+int saa7164_bus_setup(struct saa7164_dev *dev)
+{
+ struct tmComResBusInfo *b = &dev->bus;
+
+ mutex_init(&b->lock);
+
+ b->Type = TYPE_BUS_PCIe;
+ b->m_wMaxReqSize = SAA_DEVICE_MAXREQUESTSIZE;
+
+ b->m_pdwSetRing = (u8 *)(dev->bmmio +
+ ((u32)dev->busdesc.CommandRing));
+
+ b->m_dwSizeSetRing = SAA_DEVICE_BUFFERBLOCKSIZE;
+
+ b->m_pdwGetRing = (u8 *)(dev->bmmio +
+ ((u32)dev->busdesc.ResponseRing));
+
+ b->m_dwSizeGetRing = SAA_DEVICE_BUFFERBLOCKSIZE;
+
+ b->m_dwSetWritePos = ((u32)dev->intfdesc.BARLocation) +
+ (2 * sizeof(u64));
+ b->m_dwSetReadPos = b->m_dwSetWritePos + (1 * sizeof(u32));
+
+ b->m_dwGetWritePos = b->m_dwSetWritePos + (2 * sizeof(u32));
+ b->m_dwGetReadPos = b->m_dwSetWritePos + (3 * sizeof(u32));
+
+ return 0;
+}
+
+void saa7164_bus_dump(struct saa7164_dev *dev)
+{
+ struct tmComResBusInfo *b = &dev->bus;
+
+ dprintk(DBGLVL_BUS, "Dumping the bus structure:\n");
+ dprintk(DBGLVL_BUS, " .type = %d\n", b->Type);
+ dprintk(DBGLVL_BUS, " .dev->bmmio = 0x%p\n", dev->bmmio);
+ dprintk(DBGLVL_BUS, " .m_wMaxReqSize = 0x%x\n", b->m_wMaxReqSize);
+ dprintk(DBGLVL_BUS, " .m_pdwSetRing = 0x%p\n", b->m_pdwSetRing);
+ dprintk(DBGLVL_BUS, " .m_dwSizeSetRing = 0x%x\n", b->m_dwSizeSetRing);
+ dprintk(DBGLVL_BUS, " .m_pdwGetRing = 0x%p\n", b->m_pdwGetRing);
+ dprintk(DBGLVL_BUS, " .m_dwSizeGetRing = 0x%x\n", b->m_dwSizeGetRing);
+
+ dprintk(DBGLVL_BUS, " .m_dwSetReadPos = 0x%x (0x%08x)\n",
+ b->m_dwSetReadPos, saa7164_readl(b->m_dwSetReadPos));
+
+ dprintk(DBGLVL_BUS, " .m_dwSetWritePos = 0x%x (0x%08x)\n",
+ b->m_dwSetWritePos, saa7164_readl(b->m_dwSetWritePos));
+
+ dprintk(DBGLVL_BUS, " .m_dwGetReadPos = 0x%x (0x%08x)\n",
+ b->m_dwGetReadPos, saa7164_readl(b->m_dwGetReadPos));
+
+ dprintk(DBGLVL_BUS, " .m_dwGetWritePos = 0x%x (0x%08x)\n",
+ b->m_dwGetWritePos, saa7164_readl(b->m_dwGetWritePos));
+
+}
+
+/* Intensionally throw a BUG() if the state of the message bus looks corrupt */
+void saa7164_bus_verify(struct saa7164_dev *dev)
+{
+ struct tmComResBusInfo *b = &dev->bus;
+ int bug = 0;
+
+ if (saa7164_readl(b->m_dwSetReadPos) > b->m_dwSizeSetRing)
+ bug++;
+
+ if (saa7164_readl(b->m_dwSetWritePos) > b->m_dwSizeSetRing)
+ bug++;
+
+ if (saa7164_readl(b->m_dwGetReadPos) > b->m_dwSizeGetRing)
+ bug++;
+
+ if (saa7164_readl(b->m_dwGetWritePos) > b->m_dwSizeGetRing)
+ bug++;
+
+ if (bug) {
+ saa_debug = 0xffff; /* Ensure we get the bus dump */
+ saa7164_bus_dump(dev);
+ saa_debug = 1024; /* Ensure we get the bus dump */
+ BUG();
+ }
+}
+
+void saa7164_bus_dumpmsg(struct saa7164_dev *dev, struct tmComResInfo* m,
+ void *buf)
+{
+ dprintk(DBGLVL_BUS, "Dumping msg structure:\n");
+ dprintk(DBGLVL_BUS, " .id = %d\n", m->id);
+ dprintk(DBGLVL_BUS, " .flags = 0x%x\n", m->flags);
+ dprintk(DBGLVL_BUS, " .size = 0x%x\n", m->size);
+ dprintk(DBGLVL_BUS, " .command = 0x%x\n", m->command);
+ dprintk(DBGLVL_BUS, " .controlselector = 0x%x\n", m->controlselector);
+ dprintk(DBGLVL_BUS, " .seqno = %d\n", m->seqno);
+ if (buf)
+ dprintk(DBGLVL_BUS, " .buffer (ignored)\n");
+}
+
+/*
+ * Places a command or a response on the bus. The implementation does not
+ * know if it is a command or a response it just places the data on the
+ * bus depending on the bus information given in the struct tmComResBusInfo
+ * structure. If the command or response does not fit into the bus ring
+ * buffer it will be refused.
+ *
+ * Return Value:
+ * SAA_OK The function executed successfully.
+ * < 0 One or more members are not initialized.
+ */
+int saa7164_bus_set(struct saa7164_dev *dev, struct tmComResInfo* msg,
+ void *buf)
+{
+ struct tmComResBusInfo *bus = &dev->bus;
+ u32 bytes_to_write, free_write_space, timeout, curr_srp, curr_swp;
+ u32 new_swp, space_rem;
+ int ret = SAA_ERR_BAD_PARAMETER;
+
+ if (!msg) {
+ printk(KERN_ERR "%s() !msg\n", __func__);
+ return SAA_ERR_BAD_PARAMETER;
+ }
+
+ dprintk(DBGLVL_BUS, "%s()\n", __func__);
+
+ saa7164_bus_verify(dev);
+
+ msg->size = cpu_to_le16(msg->size);
+ msg->command = cpu_to_le32(msg->command);
+ msg->controlselector = cpu_to_le16(msg->controlselector);
+
+ if (msg->size > dev->bus.m_wMaxReqSize) {
+ printk(KERN_ERR "%s() Exceeded dev->bus.m_wMaxReqSize\n",
+ __func__);
+ return SAA_ERR_BAD_PARAMETER;
+ }
+
+ if ((msg->size > 0) && (buf == NULL)) {
+ printk(KERN_ERR "%s() Missing message buffer\n", __func__);
+ return SAA_ERR_BAD_PARAMETER;
+ }
+
+ /* Lock the bus from any other access */
+ mutex_lock(&bus->lock);
+
+ bytes_to_write = sizeof(*msg) + msg->size;
+ free_write_space = 0;
+ timeout = SAA_BUS_TIMEOUT;
+ curr_srp = le32_to_cpu(saa7164_readl(bus->m_dwSetReadPos));
+ curr_swp = le32_to_cpu(saa7164_readl(bus->m_dwSetWritePos));
+
+ /* Deal with ring wrapping issues */
+ if (curr_srp > curr_swp)
+ /* Deal with the wrapped ring */
+ free_write_space = curr_srp - curr_swp;
+ else
+ /* The ring has not wrapped yet */
+ free_write_space = (curr_srp + bus->m_dwSizeSetRing) - curr_swp;
+
+ dprintk(DBGLVL_BUS, "%s() bytes_to_write = %d\n", __func__,
+ bytes_to_write);
+
+ dprintk(DBGLVL_BUS, "%s() free_write_space = %d\n", __func__,
+ free_write_space);
+
+ dprintk(DBGLVL_BUS, "%s() curr_srp = %x\n", __func__, curr_srp);
+ dprintk(DBGLVL_BUS, "%s() curr_swp = %x\n", __func__, curr_swp);
+
+ /* Process the msg and write the content onto the bus */
+ while (bytes_to_write >= free_write_space) {
+
+ if (timeout-- == 0) {
+ printk(KERN_ERR "%s() bus timeout\n", __func__);
+ ret = SAA_ERR_NO_RESOURCES;
+ goto out;
+ }
+
+ /* TODO: Review this delay, efficient? */
+ /* Wait, allowing the hardware fetch time */
+ mdelay(1);
+
+ /* Check the space usage again */
+ curr_srp = le32_to_cpu(saa7164_readl(bus->m_dwSetReadPos));
+
+ /* Deal with ring wrapping issues */
+ if (curr_srp > curr_swp)
+ /* Deal with the wrapped ring */
+ free_write_space = curr_srp - curr_swp;
+ else
+ /* Read didn't wrap around the buffer */
+ free_write_space = (curr_srp + bus->m_dwSizeSetRing) -
+ curr_swp;
+
+ }
+
+ /* Calculate the new write position */
+ new_swp = curr_swp + bytes_to_write;
+
+ dprintk(DBGLVL_BUS, "%s() new_swp = %x\n", __func__, new_swp);
+ dprintk(DBGLVL_BUS, "%s() bus->m_dwSizeSetRing = %x\n", __func__,
+ bus->m_dwSizeSetRing);
+
+ /* Mental Note: line 462 tmmhComResBusPCIe.cpp */
+
+ /* Check if we're going to wrap again */
+ if (new_swp > bus->m_dwSizeSetRing) {
+
+ /* Ring wraps */
+ new_swp -= bus->m_dwSizeSetRing;
+
+ space_rem = bus->m_dwSizeSetRing - curr_swp;
+
+ dprintk(DBGLVL_BUS, "%s() space_rem = %x\n", __func__,
+ space_rem);
+
+ dprintk(DBGLVL_BUS, "%s() sizeof(*msg) = %d\n", __func__,
+ (u32)sizeof(*msg));
+
+ if (space_rem < sizeof(*msg)) {
+ dprintk(DBGLVL_BUS, "%s() tr4\n", __func__);
+
+ /* Split the msg into pieces as the ring wraps */
+ memcpy(bus->m_pdwSetRing + curr_swp, msg, space_rem);
+ memcpy(bus->m_pdwSetRing, (u8 *)msg + space_rem,
+ sizeof(*msg) - space_rem);
+
+ memcpy(bus->m_pdwSetRing + sizeof(*msg) - space_rem,
+ buf, msg->size);
+
+ } else if (space_rem == sizeof(*msg)) {
+ dprintk(DBGLVL_BUS, "%s() tr5\n", __func__);
+
+ /* Additional data at the beginning of the ring */
+ memcpy(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg));
+ memcpy(bus->m_pdwSetRing, buf, msg->size);
+
+ } else {
+ /* Additional data wraps around the ring */
+ memcpy(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg));
+ if (msg->size > 0) {
+ memcpy(bus->m_pdwSetRing + curr_swp +
+ sizeof(*msg), buf, space_rem -
+ sizeof(*msg));
+ memcpy(bus->m_pdwSetRing, (u8 *)buf +
+ space_rem - sizeof(*msg),
+ bytes_to_write - space_rem);
+ }
+
+ }
+
+ } /* (new_swp > bus->m_dwSizeSetRing) */
+ else {
+ dprintk(DBGLVL_BUS, "%s() tr6\n", __func__);
+
+ /* The ring buffer doesn't wrap, two simple copies */
+ memcpy(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg));
+ memcpy(bus->m_pdwSetRing + curr_swp + sizeof(*msg), buf,
+ msg->size);
+ }
+
+ dprintk(DBGLVL_BUS, "%s() new_swp = %x\n", __func__, new_swp);
+
+ /* Update the bus write position */
+ saa7164_writel(bus->m_dwSetWritePos, cpu_to_le32(new_swp));
+ ret = SAA_OK;
+
+out:
+ saa7164_bus_dump(dev);
+ mutex_unlock(&bus->lock);
+ saa7164_bus_verify(dev);
+ return ret;
+}
+
+/*
+ * Receive a command or a response from the bus. The implementation does not
+ * know if it is a command or a response it simply dequeues the data,
+ * depending on the bus information given in the struct tmComResBusInfo
+ * structure.
+ *
+ * Return Value:
+ * 0 The function executed successfully.
+ * < 0 One or more members are not initialized.
+ */
+int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg,
+ void *buf, int peekonly)
+{
+ struct tmComResBusInfo *bus = &dev->bus;
+ u32 bytes_to_read, write_distance, curr_grp, curr_gwp,
+ new_grp, buf_size, space_rem;
+ struct tmComResInfo msg_tmp;
+ int ret = SAA_ERR_BAD_PARAMETER;
+
+ saa7164_bus_verify(dev);
+
+ if (msg == NULL)
+ return ret;
+
+ if (msg->size > dev->bus.m_wMaxReqSize) {
+ printk(KERN_ERR "%s() Exceeded dev->bus.m_wMaxReqSize\n",
+ __func__);
+ return ret;
+ }
+
+ if ((peekonly == 0) && (msg->size > 0) && (buf == NULL)) {
+ printk(KERN_ERR
+ "%s() Missing msg buf, size should be %d bytes\n",
+ __func__, msg->size);
+ return ret;
+ }
+
+ mutex_lock(&bus->lock);
+
+ /* Peek the bus to see if a msg exists, if it's not what we're expecting
+ * then return cleanly else read the message from the bus.
+ */
+ curr_gwp = le32_to_cpu(saa7164_readl(bus->m_dwGetWritePos));
+ curr_grp = le32_to_cpu(saa7164_readl(bus->m_dwGetReadPos));
+
+ if (curr_gwp == curr_grp) {
+ ret = SAA_ERR_EMPTY;
+ goto out;
+ }
+
+ bytes_to_read = sizeof(*msg);
+
+ /* Calculate write distance to current read position */
+ write_distance = 0;
+ if (curr_gwp >= curr_grp)
+ /* Write doesn't wrap around the ring */
+ write_distance = curr_gwp - curr_grp;
+ else
+ /* Write wraps around the ring */
+ write_distance = curr_gwp + bus->m_dwSizeGetRing - curr_grp;
+
+ if (bytes_to_read > write_distance) {
+ printk(KERN_ERR "%s() No message/response found\n", __func__);
+ ret = SAA_ERR_INVALID_COMMAND;
+ goto out;
+ }
+
+ /* Calculate the new read position */
+ new_grp = curr_grp + bytes_to_read;
+ if (new_grp > bus->m_dwSizeGetRing) {
+
+ /* Ring wraps */
+ new_grp -= bus->m_dwSizeGetRing;
+ space_rem = bus->m_dwSizeGetRing - curr_grp;
+
+ memcpy(&msg_tmp, bus->m_pdwGetRing + curr_grp, space_rem);
+ memcpy((u8 *)&msg_tmp + space_rem, bus->m_pdwGetRing,
+ bytes_to_read - space_rem);
+
+ } else {
+ /* No wrapping */
+ memcpy(&msg_tmp, bus->m_pdwGetRing + curr_grp, bytes_to_read);
+ }
+
+ /* No need to update the read positions, because this was a peek */
+ /* If the caller specifically want to peek, return */
+ if (peekonly) {
+ memcpy(msg, &msg_tmp, sizeof(*msg));
+ goto peekout;
+ }
+
+ /* Check if the command/response matches what is expected */
+ if ((msg_tmp.id != msg->id) || (msg_tmp.command != msg->command) ||
+ (msg_tmp.controlselector != msg->controlselector) ||
+ (msg_tmp.seqno != msg->seqno) || (msg_tmp.size != msg->size)) {
+
+ printk(KERN_ERR "%s() Unexpected msg miss-match\n", __func__);
+ saa7164_bus_dumpmsg(dev, msg, buf);
+ saa7164_bus_dumpmsg(dev, &msg_tmp, NULL);
+ ret = SAA_ERR_INVALID_COMMAND;
+ goto out;
+ }
+
+ /* Get the actual command and response from the bus */
+ buf_size = msg->size;
+
+ bytes_to_read = sizeof(*msg) + msg->size;
+ /* Calculate write distance to current read position */
+ write_distance = 0;
+ if (curr_gwp >= curr_grp)
+ /* Write doesn't wrap around the ring */
+ write_distance = curr_gwp - curr_grp;
+ else
+ /* Write wraps around the ring */
+ write_distance = curr_gwp + bus->m_dwSizeGetRing - curr_grp;
+
+ if (bytes_to_read > write_distance) {
+ printk(KERN_ERR "%s() Invalid bus state, missing msg "
+ "or mangled ring, faulty H/W / bad code?\n", __func__);
+ ret = SAA_ERR_INVALID_COMMAND;
+ goto out;
+ }
+
+ /* Calculate the new read position */
+ new_grp = curr_grp + bytes_to_read;
+ if (new_grp > bus->m_dwSizeGetRing) {
+
+ /* Ring wraps */
+ new_grp -= bus->m_dwSizeGetRing;
+ space_rem = bus->m_dwSizeGetRing - curr_grp;
+
+ if (space_rem < sizeof(*msg)) {
+ /* msg wraps around the ring */
+ memcpy(msg, bus->m_pdwGetRing + curr_grp, space_rem);
+ memcpy((u8 *)msg + space_rem, bus->m_pdwGetRing,
+ sizeof(*msg) - space_rem);
+ if (buf)
+ memcpy(buf, bus->m_pdwGetRing + sizeof(*msg) -
+ space_rem, buf_size);
+
+ } else if (space_rem == sizeof(*msg)) {
+ memcpy(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg));
+ if (buf)
+ memcpy(buf, bus->m_pdwGetRing, buf_size);
+ } else {
+ /* Additional data wraps around the ring */
+ memcpy(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg));
+ if (buf) {
+ memcpy(buf, bus->m_pdwGetRing + curr_grp +
+ sizeof(*msg), space_rem - sizeof(*msg));
+ memcpy(buf + space_rem - sizeof(*msg),
+ bus->m_pdwGetRing, bytes_to_read -
+ space_rem);
+ }
+
+ }
+
+ } else {
+ /* No wrapping */
+ memcpy(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg));
+ if (buf)
+ memcpy(buf, bus->m_pdwGetRing + curr_grp + sizeof(*msg),
+ buf_size);
+ }
+
+ /* Update the read positions, adjusting the ring */
+ saa7164_writel(bus->m_dwGetReadPos, cpu_to_le32(new_grp));
+
+peekout:
+ msg->size = le16_to_cpu(msg->size);
+ msg->command = le32_to_cpu(msg->command);
+ msg->controlselector = le16_to_cpu(msg->controlselector);
+ ret = SAA_OK;
+out:
+ mutex_unlock(&bus->lock);
+ saa7164_bus_verify(dev);
+ return ret;
+}
+
diff --git a/drivers/media/video/saa7164/saa7164-cards.c b/drivers/media/video/saa7164/saa7164-cards.c
new file mode 100644
index 00000000..5b72da5c
--- /dev/null
+++ b/drivers/media/video/saa7164/saa7164-cards.c
@@ -0,0 +1,773 @@
+/*
+ * Driver for the NXP SAA7164 PCIe bridge
+ *
+ * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+#include "saa7164.h"
+
+/* The Bridge API needs to understand register widths (in bytes) for the
+ * attached I2C devices, so we can simplify the virtual i2c mechansms
+ * and keep the -i2c.c implementation clean.
+ */
+#define REGLEN_8bit 1
+#define REGLEN_16bit 2
+
+struct saa7164_board saa7164_boards[] = {
+ [SAA7164_BOARD_UNKNOWN] = {
+ /* Bridge will not load any firmware, without knowing
+ * the rev this would be fatal. */
+ .name = "Unknown",
+ },
+ [SAA7164_BOARD_UNKNOWN_REV2] = {
+ /* Bridge will load the v2 f/w and dump descriptors */
+ /* Required during new board bringup */
+ .name = "Generic Rev2",
+ .chiprev = SAA7164_CHIP_REV2,
+ },
+ [SAA7164_BOARD_UNKNOWN_REV3] = {
+ /* Bridge will load the v2 f/w and dump descriptors */
+ /* Required during new board bringup */
+ .name = "Generic Rev3",
+ .chiprev = SAA7164_CHIP_REV3,
+ },
+ [SAA7164_BOARD_HAUPPAUGE_HVR2200] = {
+ .name = "Hauppauge WinTV-HVR2200",
+ .porta = SAA7164_MPEG_DVB,
+ .portb = SAA7164_MPEG_DVB,
+ .portc = SAA7164_MPEG_ENCODER,
+ .portd = SAA7164_MPEG_ENCODER,
+ .porte = SAA7164_MPEG_VBI,
+ .portf = SAA7164_MPEG_VBI,
+ .chiprev = SAA7164_CHIP_REV3,
+ .unit = {{
+ .id = 0x1d,
+ .type = SAA7164_UNIT_EEPROM,
+ .name = "4K EEPROM",
+ .i2c_bus_nr = SAA7164_I2C_BUS_0,
+ .i2c_bus_addr = 0xa0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x04,
+ .type = SAA7164_UNIT_TUNER,
+ .name = "TDA18271-1",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0xc0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x1b,
+ .type = SAA7164_UNIT_TUNER,
+ .name = "TDA18271-2",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0xc0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x1e,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "TDA10048-1",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0x10 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x1f,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "TDA10048-2",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0x12 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ } },
+ },
+ [SAA7164_BOARD_HAUPPAUGE_HVR2200_2] = {
+ .name = "Hauppauge WinTV-HVR2200",
+ .porta = SAA7164_MPEG_DVB,
+ .portb = SAA7164_MPEG_DVB,
+ .portc = SAA7164_MPEG_ENCODER,
+ .portd = SAA7164_MPEG_ENCODER,
+ .porte = SAA7164_MPEG_VBI,
+ .portf = SAA7164_MPEG_VBI,
+ .chiprev = SAA7164_CHIP_REV2,
+ .unit = {{
+ .id = 0x06,
+ .type = SAA7164_UNIT_EEPROM,
+ .name = "4K EEPROM",
+ .i2c_bus_nr = SAA7164_I2C_BUS_0,
+ .i2c_bus_addr = 0xa0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x04,
+ .type = SAA7164_UNIT_TUNER,
+ .name = "TDA18271-1",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0xc0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x05,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "TDA10048-1",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0x10 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x1e,
+ .type = SAA7164_UNIT_TUNER,
+ .name = "TDA18271-2",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0xc0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x1f,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "TDA10048-2",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0x12 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ } },
+ },
+ [SAA7164_BOARD_HAUPPAUGE_HVR2200_3] = {
+ .name = "Hauppauge WinTV-HVR2200",
+ .porta = SAA7164_MPEG_DVB,
+ .portb = SAA7164_MPEG_DVB,
+ .portc = SAA7164_MPEG_ENCODER,
+ .portd = SAA7164_MPEG_ENCODER,
+ .porte = SAA7164_MPEG_VBI,
+ .portf = SAA7164_MPEG_VBI,
+ .chiprev = SAA7164_CHIP_REV2,
+ .unit = {{
+ .id = 0x1d,
+ .type = SAA7164_UNIT_EEPROM,
+ .name = "4K EEPROM",
+ .i2c_bus_nr = SAA7164_I2C_BUS_0,
+ .i2c_bus_addr = 0xa0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x04,
+ .type = SAA7164_UNIT_TUNER,
+ .name = "TDA18271-1",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0xc0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x05,
+ .type = SAA7164_UNIT_ANALOG_DEMODULATOR,
+ .name = "TDA8290-1",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0x84 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x1b,
+ .type = SAA7164_UNIT_TUNER,
+ .name = "TDA18271-2",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0xc0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x1c,
+ .type = SAA7164_UNIT_ANALOG_DEMODULATOR,
+ .name = "TDA8290-2",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0x84 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x1e,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "TDA10048-1",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0x10 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x1f,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "TDA10048-2",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0x12 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ } },
+ },
+ [SAA7164_BOARD_HAUPPAUGE_HVR2200_4] = {
+ .name = "Hauppauge WinTV-HVR2200",
+ .porta = SAA7164_MPEG_DVB,
+ .portb = SAA7164_MPEG_DVB,
+ .portc = SAA7164_MPEG_ENCODER,
+ .portd = SAA7164_MPEG_ENCODER,
+ .porte = SAA7164_MPEG_VBI,
+ .portf = SAA7164_MPEG_VBI,
+ .chiprev = SAA7164_CHIP_REV3,
+ .unit = {{
+ .id = 0x1d,
+ .type = SAA7164_UNIT_EEPROM,
+ .name = "4K EEPROM",
+ .i2c_bus_nr = SAA7164_I2C_BUS_0,
+ .i2c_bus_addr = 0xa0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x04,
+ .type = SAA7164_UNIT_TUNER,
+ .name = "TDA18271-1",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0xc0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x05,
+ .type = SAA7164_UNIT_ANALOG_DEMODULATOR,
+ .name = "TDA8290-1",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0x84 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x1b,
+ .type = SAA7164_UNIT_TUNER,
+ .name = "TDA18271-2",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0xc0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x1c,
+ .type = SAA7164_UNIT_ANALOG_DEMODULATOR,
+ .name = "TDA8290-2",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0x84 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x1e,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "TDA10048-1",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0x10 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x1f,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "TDA10048-2",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0x12 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ } },
+ },
+ [SAA7164_BOARD_HAUPPAUGE_HVR2250] = {
+ .name = "Hauppauge WinTV-HVR2250",
+ .porta = SAA7164_MPEG_DVB,
+ .portb = SAA7164_MPEG_DVB,
+ .portc = SAA7164_MPEG_ENCODER,
+ .portd = SAA7164_MPEG_ENCODER,
+ .porte = SAA7164_MPEG_VBI,
+ .portf = SAA7164_MPEG_VBI,
+ .chiprev = SAA7164_CHIP_REV3,
+ .unit = {{
+ .id = 0x22,
+ .type = SAA7164_UNIT_EEPROM,
+ .name = "4K EEPROM",
+ .i2c_bus_nr = SAA7164_I2C_BUS_0,
+ .i2c_bus_addr = 0xa0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x04,
+ .type = SAA7164_UNIT_TUNER,
+ .name = "TDA18271-1",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0xc0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x07,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "CX24228/S5H1411-1 (TOP)",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0x32 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x08,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "CX24228/S5H1411-1 (QAM)",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0x34 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x1e,
+ .type = SAA7164_UNIT_TUNER,
+ .name = "TDA18271-2",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0xc0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x20,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "CX24228/S5H1411-2 (TOP)",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0x32 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x23,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "CX24228/S5H1411-2 (QAM)",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0x34 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ } },
+ },
+ [SAA7164_BOARD_HAUPPAUGE_HVR2250_2] = {
+ .name = "Hauppauge WinTV-HVR2250",
+ .porta = SAA7164_MPEG_DVB,
+ .portb = SAA7164_MPEG_DVB,
+ .portc = SAA7164_MPEG_ENCODER,
+ .portd = SAA7164_MPEG_ENCODER,
+ .porte = SAA7164_MPEG_VBI,
+ .portf = SAA7164_MPEG_VBI,
+ .chiprev = SAA7164_CHIP_REV3,
+ .unit = {{
+ .id = 0x28,
+ .type = SAA7164_UNIT_EEPROM,
+ .name = "4K EEPROM",
+ .i2c_bus_nr = SAA7164_I2C_BUS_0,
+ .i2c_bus_addr = 0xa0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x04,
+ .type = SAA7164_UNIT_TUNER,
+ .name = "TDA18271-1",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0xc0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x07,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "CX24228/S5H1411-1 (TOP)",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0x32 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x08,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "CX24228/S5H1411-1 (QAM)",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0x34 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x24,
+ .type = SAA7164_UNIT_TUNER,
+ .name = "TDA18271-2",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0xc0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x26,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "CX24228/S5H1411-2 (TOP)",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0x32 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x29,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "CX24228/S5H1411-2 (QAM)",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0x34 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ } },
+ },
+ [SAA7164_BOARD_HAUPPAUGE_HVR2250_3] = {
+ .name = "Hauppauge WinTV-HVR2250",
+ .porta = SAA7164_MPEG_DVB,
+ .portb = SAA7164_MPEG_DVB,
+ .portc = SAA7164_MPEG_ENCODER,
+ .portd = SAA7164_MPEG_ENCODER,
+ .porte = SAA7164_MPEG_VBI,
+ .portf = SAA7164_MPEG_VBI,
+ .chiprev = SAA7164_CHIP_REV3,
+ .unit = {{
+ .id = 0x26,
+ .type = SAA7164_UNIT_EEPROM,
+ .name = "4K EEPROM",
+ .i2c_bus_nr = SAA7164_I2C_BUS_0,
+ .i2c_bus_addr = 0xa0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x04,
+ .type = SAA7164_UNIT_TUNER,
+ .name = "TDA18271-1",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0xc0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x07,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "CX24228/S5H1411-1 (TOP)",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0x32 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x08,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "CX24228/S5H1411-1 (QAM)",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0x34 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x22,
+ .type = SAA7164_UNIT_TUNER,
+ .name = "TDA18271-2",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0xc0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x24,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "CX24228/S5H1411-2 (TOP)",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0x32 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x27,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "CX24228/S5H1411-2 (QAM)",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0x34 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ } },
+ },
+ [SAA7164_BOARD_HAUPPAUGE_HVR2200_5] = {
+ .name = "Hauppauge WinTV-HVR2200",
+ .porta = SAA7164_MPEG_DVB,
+ .portb = SAA7164_MPEG_DVB,
+ .chiprev = SAA7164_CHIP_REV3,
+ .unit = {{
+ .id = 0x23,
+ .type = SAA7164_UNIT_EEPROM,
+ .name = "4K EEPROM",
+ .i2c_bus_nr = SAA7164_I2C_BUS_0,
+ .i2c_bus_addr = 0xa0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x04,
+ .type = SAA7164_UNIT_TUNER,
+ .name = "TDA18271-1",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0xc0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x05,
+ .type = SAA7164_UNIT_ANALOG_DEMODULATOR,
+ .name = "TDA8290-1",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0x84 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x21,
+ .type = SAA7164_UNIT_TUNER,
+ .name = "TDA18271-2",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0xc0 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x22,
+ .type = SAA7164_UNIT_ANALOG_DEMODULATOR,
+ .name = "TDA8290-2",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0x84 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x24,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "TDA10048-1",
+ .i2c_bus_nr = SAA7164_I2C_BUS_1,
+ .i2c_bus_addr = 0x10 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ }, {
+ .id = 0x25,
+ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ .name = "TDA10048-2",
+ .i2c_bus_nr = SAA7164_I2C_BUS_2,
+ .i2c_bus_addr = 0x12 >> 1,
+ .i2c_reg_len = REGLEN_8bit,
+ } },
+ },
+};
+const unsigned int saa7164_bcount = ARRAY_SIZE(saa7164_boards);
+
+/* ------------------------------------------------------------------ */
+/* PCI subsystem IDs */
+
+struct saa7164_subid saa7164_subids[] = {
+ {
+ .subvendor = 0x0070,
+ .subdevice = 0x8880,
+ .card = SAA7164_BOARD_HAUPPAUGE_HVR2250,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x8810,
+ .card = SAA7164_BOARD_HAUPPAUGE_HVR2250,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x8980,
+ .card = SAA7164_BOARD_HAUPPAUGE_HVR2200,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x8900,
+ .card = SAA7164_BOARD_HAUPPAUGE_HVR2200_2,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x8901,
+ .card = SAA7164_BOARD_HAUPPAUGE_HVR2200_3,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x88A1,
+ .card = SAA7164_BOARD_HAUPPAUGE_HVR2250_3,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x8891,
+ .card = SAA7164_BOARD_HAUPPAUGE_HVR2250_2,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x8851,
+ .card = SAA7164_BOARD_HAUPPAUGE_HVR2250_2,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x8940,
+ .card = SAA7164_BOARD_HAUPPAUGE_HVR2200_4,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x8953,
+ .card = SAA7164_BOARD_HAUPPAUGE_HVR2200_5,
+ },
+};
+const unsigned int saa7164_idcount = ARRAY_SIZE(saa7164_subids);
+
+void saa7164_card_list(struct saa7164_dev *dev)
+{
+ int i;
+
+ if (0 == dev->pci->subsystem_vendor &&
+ 0 == dev->pci->subsystem_device) {
+ printk(KERN_ERR
+ "%s: Board has no valid PCIe Subsystem ID and can't\n"
+ "%s: be autodetected. Pass card=<n> insmod option to\n"
+ "%s: workaround that. Send complaints to the vendor\n"
+ "%s: of the TV card. Best regards,\n"
+ "%s: -- tux\n",
+ dev->name, dev->name, dev->name, dev->name, dev->name);
+ } else {
+ printk(KERN_ERR
+ "%s: Your board isn't known (yet) to the driver.\n"
+ "%s: Try to pick one of the existing card configs via\n"
+ "%s: card=<n> insmod option. Updating to the latest\n"
+ "%s: version might help as well.\n",
+ dev->name, dev->name, dev->name, dev->name);
+ }
+
+ printk(KERN_ERR "%s: Here are valid choices for the card=<n> insmod "
+ "option:\n", dev->name);
+
+ for (i = 0; i < saa7164_bcount; i++)
+ printk(KERN_ERR "%s: card=%d -> %s\n",
+ dev->name, i, saa7164_boards[i].name);
+}
+
+/* TODO: clean this define up into the -cards.c structs */
+#define PCIEBRIDGE_UNITID 2
+
+void saa7164_gpio_setup(struct saa7164_dev *dev)
+{
+ switch (dev->board) {
+ case SAA7164_BOARD_HAUPPAUGE_HVR2200:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2200_2:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2200_3:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2200_4:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2200_5:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2250:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2250_2:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2250_3:
+ /*
+ GPIO 2: s5h1411 / tda10048-1 demod reset
+ GPIO 3: s5h1411 / tda10048-2 demod reset
+ GPIO 7: IRBlaster Zilog reset
+ */
+
+ /* Reset parts by going in and out of reset */
+ saa7164_api_clear_gpiobit(dev, PCIEBRIDGE_UNITID, 2);
+ saa7164_api_clear_gpiobit(dev, PCIEBRIDGE_UNITID, 3);
+
+ msleep(20);
+
+ saa7164_api_set_gpiobit(dev, PCIEBRIDGE_UNITID, 2);
+ saa7164_api_set_gpiobit(dev, PCIEBRIDGE_UNITID, 3);
+ break;
+ }
+}
+
+static void hauppauge_eeprom(struct saa7164_dev *dev, u8 *eeprom_data)
+{
+ struct tveeprom tv;
+
+ /* TODO: Assumption: eeprom on bus 0 */
+ tveeprom_hauppauge_analog(&dev->i2c_bus[0].i2c_client, &tv,
+ eeprom_data);
+
+ /* Make sure we support the board model */
+ switch (tv.model) {
+ case 88001:
+ /* Development board - Limit circulation */
+ /* WinTV-HVR2250 (PCIe, Retail, full-height bracket)
+ * ATSC/QAM (TDA18271/S5H1411) and basic analog, no IR, FM */
+ case 88021:
+ /* WinTV-HVR2250 (PCIe, Retail, full-height bracket)
+ * ATSC/QAM (TDA18271/S5H1411) and basic analog, MCE CIR, FM */
+ break;
+ case 88041:
+ /* WinTV-HVR2250 (PCIe, Retail, full-height bracket)
+ * ATSC/QAM (TDA18271/S5H1411) and basic analog, no IR, FM */
+ break;
+ case 88061:
+ /* WinTV-HVR2250 (PCIe, Retail, full-height bracket)
+ * ATSC/QAM (TDA18271/S5H1411) and basic analog, FM */
+ break;
+ case 89519:
+ case 89609:
+ /* WinTV-HVR2200 (PCIe, Retail, full-height)
+ * DVB-T (TDA18271/TDA10048) and basic analog, no IR */
+ break;
+ case 89619:
+ /* WinTV-HVR2200 (PCIe, Retail, half-height)
+ * DVB-T (TDA18271/TDA10048) and basic analog, no IR */
+ break;
+ default:
+ printk(KERN_ERR "%s: Warning: Unknown Hauppauge model #%d\n",
+ dev->name, tv.model);
+ break;
+ }
+
+ printk(KERN_INFO "%s: Hauppauge eeprom: model=%d\n", dev->name,
+ tv.model);
+}
+
+void saa7164_card_setup(struct saa7164_dev *dev)
+{
+ static u8 eeprom[256];
+
+ if (dev->i2c_bus[0].i2c_rc == 0) {
+ if (saa7164_api_read_eeprom(dev, &eeprom[0],
+ sizeof(eeprom)) < 0)
+ return;
+ }
+
+ switch (dev->board) {
+ case SAA7164_BOARD_HAUPPAUGE_HVR2200:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2200_2:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2200_3:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2200_4:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2200_5:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2250:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2250_2:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2250_3:
+ hauppauge_eeprom(dev, &eeprom[0]);
+ break;
+ }
+}
+
+/* With most other drivers, the kernel expects to communicate with subdrivers
+ * through i2c. This bridge does not allow that, it does not expose any direct
+ * access to I2C. Instead we have to communicate through the device f/w for
+ * register access to 'processing units'. Each unit has a unique
+ * id, regardless of how the physical implementation occurs across
+ * the three physical i2c busses. The being said if we want leverge of
+ * the existing kernel drivers for tuners and demods we have to 'speak i2c',
+ * to this bridge implements 3 virtual i2c buses. This is a helper function
+ * for those.
+ *
+ * Description: Translate the kernels notion of an i2c address and bus into
+ * the appropriate unitid.
+ */
+int saa7164_i2caddr_to_unitid(struct saa7164_i2c *bus, int addr)
+{
+ /* For a given bus and i2c device address, return the saa7164 unique
+ * unitid. < 0 on error */
+
+ struct saa7164_dev *dev = bus->dev;
+ struct saa7164_unit *unit;
+ int i;
+
+ for (i = 0; i < SAA7164_MAX_UNITS; i++) {
+ unit = &saa7164_boards[dev->board].unit[i];
+
+ if (unit->type == SAA7164_UNIT_UNDEFINED)
+ continue;
+ if ((bus->nr == unit->i2c_bus_nr) &&
+ (addr == unit->i2c_bus_addr))
+ return unit->id;
+ }
+
+ return -1;
+}
+
+/* The 7164 API needs to know the i2c register length in advance.
+ * this is a helper function. Based on a specific chip addr and bus return the
+ * reg length.
+ */
+int saa7164_i2caddr_to_reglen(struct saa7164_i2c *bus, int addr)
+{
+ /* For a given bus and i2c device address, return the
+ * saa7164 registry address width. < 0 on error
+ */
+
+ struct saa7164_dev *dev = bus->dev;
+ struct saa7164_unit *unit;
+ int i;
+
+ for (i = 0; i < SAA7164_MAX_UNITS; i++) {
+ unit = &saa7164_boards[dev->board].unit[i];
+
+ if (unit->type == SAA7164_UNIT_UNDEFINED)
+ continue;
+
+ if ((bus->nr == unit->i2c_bus_nr) &&
+ (addr == unit->i2c_bus_addr))
+ return unit->i2c_reg_len;
+ }
+
+ return -1;
+}
+/* TODO: implement a 'findeeprom' functio like the above and fix any other
+ * eeprom related todo's in -api.c.
+ */
+
+/* Translate a unitid into a x readable device name, for display purposes. */
+char *saa7164_unitid_name(struct saa7164_dev *dev, u8 unitid)
+{
+ char *undefed = "UNDEFINED";
+ char *bridge = "BRIDGE";
+ struct saa7164_unit *unit;
+ int i;
+
+ if (unitid == 0)
+ return bridge;
+
+ for (i = 0; i < SAA7164_MAX_UNITS; i++) {
+ unit = &saa7164_boards[dev->board].unit[i];
+
+ if (unit->type == SAA7164_UNIT_UNDEFINED)
+ continue;
+
+ if (unitid == unit->id)
+ return unit->name;
+ }
+
+ return undefed;
+}
+
diff --git a/drivers/media/video/saa7164/saa7164-cmd.c b/drivers/media/video/saa7164/saa7164-cmd.c
new file mode 100644
index 00000000..62fac7f9
--- /dev/null
+++ b/drivers/media/video/saa7164/saa7164-cmd.c
@@ -0,0 +1,589 @@
+/*
+ * Driver for the NXP SAA7164 PCIe bridge
+ *
+ * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/wait.h>
+
+#include "saa7164.h"
+
+int saa7164_cmd_alloc_seqno(struct saa7164_dev *dev)
+{
+ int i, ret = -1;
+
+ mutex_lock(&dev->lock);
+ for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) {
+ if (dev->cmds[i].inuse == 0) {
+ dev->cmds[i].inuse = 1;
+ dev->cmds[i].signalled = 0;
+ dev->cmds[i].timeout = 0;
+ ret = dev->cmds[i].seqno;
+ break;
+ }
+ }
+ mutex_unlock(&dev->lock);
+
+ return ret;
+}
+
+void saa7164_cmd_free_seqno(struct saa7164_dev *dev, u8 seqno)
+{
+ mutex_lock(&dev->lock);
+ if ((dev->cmds[seqno].inuse == 1) &&
+ (dev->cmds[seqno].seqno == seqno)) {
+ dev->cmds[seqno].inuse = 0;
+ dev->cmds[seqno].signalled = 0;
+ dev->cmds[seqno].timeout = 0;
+ }
+ mutex_unlock(&dev->lock);
+}
+
+void saa7164_cmd_timeout_seqno(struct saa7164_dev *dev, u8 seqno)
+{
+ mutex_lock(&dev->lock);
+ if ((dev->cmds[seqno].inuse == 1) &&
+ (dev->cmds[seqno].seqno == seqno)) {
+ dev->cmds[seqno].timeout = 1;
+ }
+ mutex_unlock(&dev->lock);
+}
+
+u32 saa7164_cmd_timeout_get(struct saa7164_dev *dev, u8 seqno)
+{
+ int ret = 0;
+
+ mutex_lock(&dev->lock);
+ if ((dev->cmds[seqno].inuse == 1) &&
+ (dev->cmds[seqno].seqno == seqno)) {
+ ret = dev->cmds[seqno].timeout;
+ }
+ mutex_unlock(&dev->lock);
+
+ return ret;
+}
+
+/* Commands to the f/w get marshelled to/from this code then onto the PCI
+ * -bus/c running buffer. */
+int saa7164_irq_dequeue(struct saa7164_dev *dev)
+{
+ int ret = SAA_OK, i = 0;
+ u32 timeout;
+ wait_queue_head_t *q = NULL;
+ u8 tmp[512];
+ dprintk(DBGLVL_CMD, "%s()\n", __func__);
+
+ /* While any outstand message on the bus exists... */
+ do {
+
+ /* Peek the msg bus */
+ struct tmComResInfo tRsp = { 0, 0, 0, 0, 0, 0 };
+ ret = saa7164_bus_get(dev, &tRsp, NULL, 1);
+ if (ret != SAA_OK)
+ break;
+
+ q = &dev->cmds[tRsp.seqno].wait;
+ timeout = saa7164_cmd_timeout_get(dev, tRsp.seqno);
+ dprintk(DBGLVL_CMD, "%s() timeout = %d\n", __func__, timeout);
+ if (!timeout) {
+ dprintk(DBGLVL_CMD,
+ "%s() signalled seqno(%d) (for dequeue)\n",
+ __func__, tRsp.seqno);
+ dev->cmds[tRsp.seqno].signalled = 1;
+ wake_up(q);
+ } else {
+ printk(KERN_ERR
+ "%s() found timed out command on the bus\n",
+ __func__);
+
+ /* Clean the bus */
+ ret = saa7164_bus_get(dev, &tRsp, &tmp, 0);
+ printk(KERN_ERR "%s() ret = %x\n", __func__, ret);
+ if (ret == SAA_ERR_EMPTY)
+ /* Someone else already fetched the response */
+ return SAA_OK;
+
+ if (ret != SAA_OK)
+ return ret;
+ }
+
+ /* It's unlikely to have more than 4 or 5 pending messages,
+ * ensure we exit at some point regardless.
+ */
+ } while (i++ < 32);
+
+ return ret;
+}
+
+/* Commands to the f/w get marshelled to/from this code then onto the PCI
+ * -bus/c running buffer. */
+int saa7164_cmd_dequeue(struct saa7164_dev *dev)
+{
+ int loop = 1;
+ int ret;
+ u32 timeout;
+ wait_queue_head_t *q = NULL;
+ u8 tmp[512];
+ dprintk(DBGLVL_CMD, "%s()\n", __func__);
+
+ while (loop) {
+
+ struct tmComResInfo tRsp = { 0, 0, 0, 0, 0, 0 };
+ ret = saa7164_bus_get(dev, &tRsp, NULL, 1);
+ if (ret == SAA_ERR_EMPTY)
+ return SAA_OK;
+
+ if (ret != SAA_OK)
+ return ret;
+
+ q = &dev->cmds[tRsp.seqno].wait;
+ timeout = saa7164_cmd_timeout_get(dev, tRsp.seqno);
+ dprintk(DBGLVL_CMD, "%s() timeout = %d\n", __func__, timeout);
+ if (timeout) {
+ printk(KERN_ERR "found timed out command on the bus\n");
+
+ /* Clean the bus */
+ ret = saa7164_bus_get(dev, &tRsp, &tmp, 0);
+ printk(KERN_ERR "ret = %x\n", ret);
+ if (ret == SAA_ERR_EMPTY)
+ /* Someone else already fetched the response */
+ return SAA_OK;
+
+ if (ret != SAA_OK)
+ return ret;
+
+ if (tRsp.flags & PVC_CMDFLAG_CONTINUE)
+ printk(KERN_ERR "split response\n");
+ else
+ saa7164_cmd_free_seqno(dev, tRsp.seqno);
+
+ printk(KERN_ERR " timeout continue\n");
+ continue;
+ }
+
+ dprintk(DBGLVL_CMD, "%s() signalled seqno(%d) (for dequeue)\n",
+ __func__, tRsp.seqno);
+ dev->cmds[tRsp.seqno].signalled = 1;
+ wake_up(q);
+ return SAA_OK;
+ }
+
+ return SAA_OK;
+}
+
+int saa7164_cmd_set(struct saa7164_dev *dev, struct tmComResInfo *msg,
+ void *buf)
+{
+ struct tmComResBusInfo *bus = &dev->bus;
+ u8 cmd_sent;
+ u16 size, idx;
+ u32 cmds;
+ void *tmp;
+ int ret = -1;
+
+ if (!msg) {
+ printk(KERN_ERR "%s() !msg\n", __func__);
+ return SAA_ERR_BAD_PARAMETER;
+ }
+
+ mutex_lock(&dev->cmds[msg->id].lock);
+
+ size = msg->size;
+ idx = 0;
+ cmds = size / bus->m_wMaxReqSize;
+ if (size % bus->m_wMaxReqSize == 0)
+ cmds -= 1;
+
+ cmd_sent = 0;
+
+ /* Split the request into smaller chunks */
+ for (idx = 0; idx < cmds; idx++) {
+
+ msg->flags |= SAA_CMDFLAG_CONTINUE;
+ msg->size = bus->m_wMaxReqSize;
+ tmp = buf + idx * bus->m_wMaxReqSize;
+
+ ret = saa7164_bus_set(dev, msg, tmp);
+ if (ret != SAA_OK) {
+ printk(KERN_ERR "%s() set failed %d\n", __func__, ret);
+
+ if (cmd_sent) {
+ ret = SAA_ERR_BUSY;
+ goto out;
+ }
+ ret = SAA_ERR_OVERFLOW;
+ goto out;
+ }
+ cmd_sent = 1;
+ }
+
+ /* If not the last command... */
+ if (idx != 0)
+ msg->flags &= ~SAA_CMDFLAG_CONTINUE;
+
+ msg->size = size - idx * bus->m_wMaxReqSize;
+
+ ret = saa7164_bus_set(dev, msg, buf + idx * bus->m_wMaxReqSize);
+ if (ret != SAA_OK) {
+ printk(KERN_ERR "%s() set last failed %d\n", __func__, ret);
+
+ if (cmd_sent) {
+ ret = SAA_ERR_BUSY;
+ goto out;
+ }
+ ret = SAA_ERR_OVERFLOW;
+ goto out;
+ }
+ ret = SAA_OK;
+
+out:
+ mutex_unlock(&dev->cmds[msg->id].lock);
+ return ret;
+}
+
+/* Wait for a signal event, without holding a mutex. Either return TIMEOUT if
+ * the event never occurred, or SAA_OK if it was signaled during the wait.
+ */
+int saa7164_cmd_wait(struct saa7164_dev *dev, u8 seqno)
+{
+ wait_queue_head_t *q = NULL;
+ int ret = SAA_BUS_TIMEOUT;
+ unsigned long stamp;
+ int r;
+
+ if (saa_debug >= 4)
+ saa7164_bus_dump(dev);
+
+ dprintk(DBGLVL_CMD, "%s(seqno=%d)\n", __func__, seqno);
+
+ mutex_lock(&dev->lock);
+ if ((dev->cmds[seqno].inuse == 1) &&
+ (dev->cmds[seqno].seqno == seqno)) {
+ q = &dev->cmds[seqno].wait;
+ }
+ mutex_unlock(&dev->lock);
+
+ if (q) {
+ /* If we haven't been signalled we need to wait */
+ if (dev->cmds[seqno].signalled == 0) {
+ stamp = jiffies;
+ dprintk(DBGLVL_CMD,
+ "%s(seqno=%d) Waiting (signalled=%d)\n",
+ __func__, seqno, dev->cmds[seqno].signalled);
+
+ /* Wait for signalled to be flagged or timeout */
+ /* In a highly stressed system this can easily extend
+ * into multiple seconds before the deferred worker
+ * is scheduled, and we're woken up via signal.
+ * We typically are signalled in < 50ms but it can
+ * take MUCH longer.
+ */
+ wait_event_timeout(*q, dev->cmds[seqno].signalled,
+ (HZ * waitsecs));
+ r = time_before(jiffies, stamp + (HZ * waitsecs));
+ if (r)
+ ret = SAA_OK;
+ else
+ saa7164_cmd_timeout_seqno(dev, seqno);
+
+ dprintk(DBGLVL_CMD, "%s(seqno=%d) Waiting res = %d "
+ "(signalled=%d)\n", __func__, seqno, r,
+ dev->cmds[seqno].signalled);
+ } else
+ ret = SAA_OK;
+ } else
+ printk(KERN_ERR "%s(seqno=%d) seqno is invalid\n",
+ __func__, seqno);
+
+ return ret;
+}
+
+void saa7164_cmd_signal(struct saa7164_dev *dev, u8 seqno)
+{
+ int i;
+ dprintk(DBGLVL_CMD, "%s()\n", __func__);
+
+ mutex_lock(&dev->lock);
+ for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) {
+ if (dev->cmds[i].inuse == 1) {
+ dprintk(DBGLVL_CMD,
+ "seqno %d inuse, sig = %d, t/out = %d\n",
+ dev->cmds[i].seqno,
+ dev->cmds[i].signalled,
+ dev->cmds[i].timeout);
+ }
+ }
+
+ for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) {
+ if ((dev->cmds[i].inuse == 1) && ((i == 0) ||
+ (dev->cmds[i].signalled) || (dev->cmds[i].timeout))) {
+ dprintk(DBGLVL_CMD, "%s(seqno=%d) calling wake_up\n",
+ __func__, i);
+ dev->cmds[i].signalled = 1;
+ wake_up(&dev->cmds[i].wait);
+ }
+ }
+ mutex_unlock(&dev->lock);
+}
+
+int saa7164_cmd_send(struct saa7164_dev *dev, u8 id, enum tmComResCmd command,
+ u16 controlselector, u16 size, void *buf)
+{
+ struct tmComResInfo command_t, *pcommand_t;
+ struct tmComResInfo response_t, *presponse_t;
+ u8 errdata[256];
+ u16 resp_dsize;
+ u16 data_recd;
+ u32 loop;
+ int ret;
+ int safety = 0;
+
+ dprintk(DBGLVL_CMD, "%s(unitid = %s (%d) , command = 0x%x, "
+ "sel = 0x%x)\n", __func__, saa7164_unitid_name(dev, id), id,
+ command, controlselector);
+
+ if ((size == 0) || (buf == NULL)) {
+ printk(KERN_ERR "%s() Invalid param\n", __func__);
+ return SAA_ERR_BAD_PARAMETER;
+ }
+
+ /* Prepare some basic command/response structures */
+ memset(&command_t, 0, sizeof(command_t));
+ memset(&response_t, 0, sizeof(response_t));
+ pcommand_t = &command_t;
+ presponse_t = &response_t;
+ command_t.id = id;
+ command_t.command = command;
+ command_t.controlselector = controlselector;
+ command_t.size = size;
+
+ /* Allocate a unique sequence number */
+ ret = saa7164_cmd_alloc_seqno(dev);
+ if (ret < 0) {
+ printk(KERN_ERR "%s() No free sequences\n", __func__);
+ ret = SAA_ERR_NO_RESOURCES;
+ goto out;
+ }
+
+ command_t.seqno = (u8)ret;
+
+ /* Send Command */
+ resp_dsize = size;
+ pcommand_t->size = size;
+
+ dprintk(DBGLVL_CMD, "%s() pcommand_t.seqno = %d\n",
+ __func__, pcommand_t->seqno);
+
+ dprintk(DBGLVL_CMD, "%s() pcommand_t.size = %d\n",
+ __func__, pcommand_t->size);
+
+ ret = saa7164_cmd_set(dev, pcommand_t, buf);
+ if (ret != SAA_OK) {
+ printk(KERN_ERR "%s() set command failed %d\n", __func__, ret);
+
+ if (ret != SAA_ERR_BUSY)
+ saa7164_cmd_free_seqno(dev, pcommand_t->seqno);
+ else
+ /* Flag a timeout, because at least one
+ * command was sent */
+ saa7164_cmd_timeout_seqno(dev, pcommand_t->seqno);
+
+ goto out;
+ }
+
+ /* With split responses we have to collect the msgs piece by piece */
+ data_recd = 0;
+ loop = 1;
+ while (loop) {
+ dprintk(DBGLVL_CMD, "%s() loop\n", __func__);
+
+ ret = saa7164_cmd_wait(dev, pcommand_t->seqno);
+ dprintk(DBGLVL_CMD, "%s() loop ret = %d\n", __func__, ret);
+
+ /* if power is down and this is not a power command ... */
+
+ if (ret == SAA_BUS_TIMEOUT) {
+ printk(KERN_ERR "Event timed out\n");
+ saa7164_cmd_timeout_seqno(dev, pcommand_t->seqno);
+ return ret;
+ }
+
+ if (ret != SAA_OK) {
+ printk(KERN_ERR "spurious error\n");
+ return ret;
+ }
+
+ /* Peek response */
+ ret = saa7164_bus_get(dev, presponse_t, NULL, 1);
+ if (ret == SAA_ERR_EMPTY) {
+ dprintk(4, "%s() SAA_ERR_EMPTY\n", __func__);
+ continue;
+ }
+ if (ret != SAA_OK) {
+ printk(KERN_ERR "peek failed\n");
+ return ret;
+ }
+
+ dprintk(DBGLVL_CMD, "%s() presponse_t->seqno = %d\n",
+ __func__, presponse_t->seqno);
+
+ dprintk(DBGLVL_CMD, "%s() presponse_t->flags = 0x%x\n",
+ __func__, presponse_t->flags);
+
+ dprintk(DBGLVL_CMD, "%s() presponse_t->size = %d\n",
+ __func__, presponse_t->size);
+
+ /* Check if the response was for our command */
+ if (presponse_t->seqno != pcommand_t->seqno) {
+
+ dprintk(DBGLVL_CMD,
+ "wrong event: seqno = %d, "
+ "expected seqno = %d, "
+ "will dequeue regardless\n",
+ presponse_t->seqno, pcommand_t->seqno);
+
+ ret = saa7164_cmd_dequeue(dev);
+ if (ret != SAA_OK) {
+ printk(KERN_ERR "dequeue failed, ret = %d\n",
+ ret);
+ if (safety++ > 16) {
+ printk(KERN_ERR
+ "dequeue exceeded, safety exit\n");
+ return SAA_ERR_BUSY;
+ }
+ }
+
+ continue;
+ }
+
+ if ((presponse_t->flags & PVC_RESPONSEFLAG_ERROR) != 0) {
+
+ memset(&errdata[0], 0, sizeof(errdata));
+
+ ret = saa7164_bus_get(dev, presponse_t, &errdata[0], 0);
+ if (ret != SAA_OK) {
+ printk(KERN_ERR "get error(2)\n");
+ return ret;
+ }
+
+ saa7164_cmd_free_seqno(dev, pcommand_t->seqno);
+
+ dprintk(DBGLVL_CMD, "%s() errdata %02x%02x%02x%02x\n",
+ __func__, errdata[0], errdata[1], errdata[2],
+ errdata[3]);
+
+ /* Map error codes */
+ dprintk(DBGLVL_CMD, "%s() cmd, error code = 0x%x\n",
+ __func__, errdata[0]);
+
+ switch (errdata[0]) {
+ case PVC_ERRORCODE_INVALID_COMMAND:
+ dprintk(DBGLVL_CMD, "%s() INVALID_COMMAND\n",
+ __func__);
+ ret = SAA_ERR_INVALID_COMMAND;
+ break;
+ case PVC_ERRORCODE_INVALID_DATA:
+ dprintk(DBGLVL_CMD, "%s() INVALID_DATA\n",
+ __func__);
+ ret = SAA_ERR_BAD_PARAMETER;
+ break;
+ case PVC_ERRORCODE_TIMEOUT:
+ dprintk(DBGLVL_CMD, "%s() TIMEOUT\n", __func__);
+ ret = SAA_ERR_TIMEOUT;
+ break;
+ case PVC_ERRORCODE_NAK:
+ dprintk(DBGLVL_CMD, "%s() NAK\n", __func__);
+ ret = SAA_ERR_NULL_PACKET;
+ break;
+ case PVC_ERRORCODE_UNKNOWN:
+ case PVC_ERRORCODE_INVALID_CONTROL:
+ dprintk(DBGLVL_CMD,
+ "%s() UNKNOWN OR INVALID CONTROL\n",
+ __func__);
+ default:
+ dprintk(DBGLVL_CMD, "%s() UNKNOWN\n", __func__);
+ ret = SAA_ERR_NOT_SUPPORTED;
+ }
+
+ /* See of other commands are on the bus */
+ if (saa7164_cmd_dequeue(dev) != SAA_OK)
+ printk(KERN_ERR "dequeue(2) failed\n");
+
+ return ret;
+ }
+
+ /* If response is invalid */
+ if ((presponse_t->id != pcommand_t->id) ||
+ (presponse_t->command != pcommand_t->command) ||
+ (presponse_t->controlselector !=
+ pcommand_t->controlselector) ||
+ (((resp_dsize - data_recd) != presponse_t->size) &&
+ !(presponse_t->flags & PVC_CMDFLAG_CONTINUE)) ||
+ ((resp_dsize - data_recd) < presponse_t->size)) {
+
+ /* Invalid */
+ dprintk(DBGLVL_CMD, "%s() Invalid\n", __func__);
+ ret = saa7164_bus_get(dev, presponse_t, NULL, 0);
+ if (ret != SAA_OK) {
+ printk(KERN_ERR "get failed\n");
+ return ret;
+ }
+
+ /* See of other commands are on the bus */
+ if (saa7164_cmd_dequeue(dev) != SAA_OK)
+ printk(KERN_ERR "dequeue(3) failed\n");
+ continue;
+ }
+
+ /* OK, now we're actually getting out correct response */
+ ret = saa7164_bus_get(dev, presponse_t, buf + data_recd, 0);
+ if (ret != SAA_OK) {
+ printk(KERN_ERR "get failed\n");
+ return ret;
+ }
+
+ data_recd = presponse_t->size + data_recd;
+ if (resp_dsize == data_recd) {
+ dprintk(DBGLVL_CMD, "%s() Resp recd\n", __func__);
+ break;
+ }
+
+ /* See of other commands are on the bus */
+ if (saa7164_cmd_dequeue(dev) != SAA_OK)
+ printk(KERN_ERR "dequeue(3) failed\n");
+
+ continue;
+
+ } /* (loop) */
+
+ /* Release the sequence number allocation */
+ saa7164_cmd_free_seqno(dev, pcommand_t->seqno);
+
+ /* if powerdown signal all pending commands */
+
+ dprintk(DBGLVL_CMD, "%s() Calling dequeue then exit\n", __func__);
+
+ /* See of other commands are on the bus */
+ if (saa7164_cmd_dequeue(dev) != SAA_OK)
+ printk(KERN_ERR "dequeue(4) failed\n");
+
+ ret = SAA_OK;
+out:
+ return ret;
+}
+
diff --git a/drivers/media/video/saa7164/saa7164-core.c b/drivers/media/video/saa7164/saa7164-core.c
new file mode 100644
index 00000000..3b7d7b4e
--- /dev/null
+++ b/drivers/media/video/saa7164/saa7164-core.c
@@ -0,0 +1,1526 @@
+/*
+ * Driver for the NXP SAA7164 PCIe bridge
+ *
+ * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kmod.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <asm/div64.h>
+
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif
+#include "saa7164.h"
+
+MODULE_DESCRIPTION("Driver for NXP SAA7164 based TV cards");
+MODULE_AUTHOR("Steven Toth <stoth@kernellabs.com>");
+MODULE_LICENSE("GPL");
+
+/*
+ * 1 Basic
+ * 2
+ * 4 i2c
+ * 8 api
+ * 16 cmd
+ * 32 bus
+ */
+
+unsigned int saa_debug;
+module_param_named(debug, saa_debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable debug messages");
+
+unsigned int fw_debug;
+module_param(fw_debug, int, 0644);
+MODULE_PARM_DESC(fw_debug, "Firware debug level def:2");
+
+unsigned int encoder_buffers = SAA7164_MAX_ENCODER_BUFFERS;
+module_param(encoder_buffers, int, 0644);
+MODULE_PARM_DESC(encoder_buffers, "Total buffers in read queue 16-512 def:64");
+
+unsigned int vbi_buffers = SAA7164_MAX_VBI_BUFFERS;
+module_param(vbi_buffers, int, 0644);
+MODULE_PARM_DESC(vbi_buffers, "Total buffers in read queue 16-512 def:64");
+
+unsigned int waitsecs = 10;
+module_param(waitsecs, int, 0644);
+MODULE_PARM_DESC(waitsecs, "timeout on firmware messages");
+
+static unsigned int card[] = {[0 ... (SAA7164_MAXBOARDS - 1)] = UNSET };
+module_param_array(card, int, NULL, 0444);
+MODULE_PARM_DESC(card, "card type");
+
+unsigned int print_histogram = 64;
+module_param(print_histogram, int, 0644);
+MODULE_PARM_DESC(print_histogram, "print histogram values once");
+
+unsigned int crc_checking = 1;
+module_param(crc_checking, int, 0644);
+MODULE_PARM_DESC(crc_checking, "enable crc sanity checking on buffers");
+
+unsigned int guard_checking = 1;
+module_param(guard_checking, int, 0644);
+MODULE_PARM_DESC(guard_checking,
+ "enable dma sanity checking for buffer overruns");
+
+static unsigned int saa7164_devcount;
+
+static DEFINE_MUTEX(devlist);
+LIST_HEAD(saa7164_devlist);
+
+#define INT_SIZE 16
+
+void saa7164_dumphex16FF(struct saa7164_dev *dev, u8 *buf, int len)
+{
+ int i;
+ u8 tmp[16];
+ memset(&tmp[0], 0xff, sizeof(tmp));
+
+ printk(KERN_INFO "--------------------> "
+ "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n");
+
+ for (i = 0; i < len; i += 16) {
+ if (memcmp(&tmp, buf + i, sizeof(tmp)) != 0) {
+ printk(KERN_INFO " [0x%08x] "
+ "%02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n", i,
+ *(buf+i+0), *(buf+i+1), *(buf+i+2), *(buf+i+3),
+ *(buf+i+4), *(buf+i+5), *(buf+i+6), *(buf+i+7),
+ *(buf+i+8), *(buf+i+9), *(buf+i+10), *(buf+i+11),
+ *(buf+i+12), *(buf+i+13), *(buf+i+14), *(buf+i+15));
+ }
+ }
+}
+
+static void saa7164_pack_verifier(struct saa7164_buffer *buf)
+{
+ u8 *p = (u8 *)buf->cpu;
+ int i;
+
+ for (i = 0; i < buf->actual_size; i += 2048) {
+
+ if ((*(p + i + 0) != 0x00) || (*(p + i + 1) != 0x00) ||
+ (*(p + i + 2) != 0x01) || (*(p + i + 3) != 0xBA)) {
+ printk(KERN_ERR "No pack at 0x%x\n", i);
+#if 0
+ saa7164_dumphex16FF(buf->port->dev, (p + i), 32);
+#endif
+ }
+ }
+}
+
+#define FIXED_VIDEO_PID 0xf1
+#define FIXED_AUDIO_PID 0xf2
+
+static void saa7164_ts_verifier(struct saa7164_buffer *buf)
+{
+ struct saa7164_port *port = buf->port;
+ u32 i;
+ u8 cc, a;
+ u16 pid;
+ u8 __iomem *bufcpu = (u8 *)buf->cpu;
+
+ port->sync_errors = 0;
+ port->v_cc_errors = 0;
+ port->a_cc_errors = 0;
+
+ for (i = 0; i < buf->actual_size; i += 188) {
+ if (*(bufcpu + i) != 0x47)
+ port->sync_errors++;
+
+ /* TODO: Query pid lower 8 bits, ignoring upper bits intensionally */
+ pid = ((*(bufcpu + i + 1) & 0x1f) << 8) | *(bufcpu + i + 2);
+ cc = *(bufcpu + i + 3) & 0x0f;
+
+ if (pid == FIXED_VIDEO_PID) {
+ a = ((port->last_v_cc + 1) & 0x0f);
+ if (a != cc) {
+ printk(KERN_ERR "video cc last = %x current = %x i = %d\n",
+ port->last_v_cc, cc, i);
+ port->v_cc_errors++;
+ }
+
+ port->last_v_cc = cc;
+ } else
+ if (pid == FIXED_AUDIO_PID) {
+ a = ((port->last_a_cc + 1) & 0x0f);
+ if (a != cc) {
+ printk(KERN_ERR "audio cc last = %x current = %x i = %d\n",
+ port->last_a_cc, cc, i);
+ port->a_cc_errors++;
+ }
+
+ port->last_a_cc = cc;
+ }
+
+ }
+
+ /* Only report errors if we've been through this function atleast
+ * once already and the cached cc values are primed. First time through
+ * always generates errors.
+ */
+ if (port->v_cc_errors && (port->done_first_interrupt > 1))
+ printk(KERN_ERR "video pid cc, %d errors\n", port->v_cc_errors);
+
+ if (port->a_cc_errors && (port->done_first_interrupt > 1))
+ printk(KERN_ERR "audio pid cc, %d errors\n", port->a_cc_errors);
+
+ if (port->sync_errors && (port->done_first_interrupt > 1))
+ printk(KERN_ERR "sync_errors = %d\n", port->sync_errors);
+
+ if (port->done_first_interrupt == 1)
+ port->done_first_interrupt++;
+}
+
+static void saa7164_histogram_reset(struct saa7164_histogram *hg, char *name)
+{
+ int i;
+
+ memset(hg, 0, sizeof(struct saa7164_histogram));
+ strcpy(hg->name, name);
+
+ /* First 30ms x 1ms */
+ for (i = 0; i < 30; i++)
+ hg->counter1[0 + i].val = i;
+
+ /* 30 - 200ms x 10ms */
+ for (i = 0; i < 18; i++)
+ hg->counter1[30 + i].val = 30 + (i * 10);
+
+ /* 200 - 2000ms x 100ms */
+ for (i = 0; i < 15; i++)
+ hg->counter1[48 + i].val = 200 + (i * 200);
+
+ /* Catch all massive value (2secs) */
+ hg->counter1[55].val = 2000;
+
+ /* Catch all massive value (4secs) */
+ hg->counter1[56].val = 4000;
+
+ /* Catch all massive value (8secs) */
+ hg->counter1[57].val = 8000;
+
+ /* Catch all massive value (15secs) */
+ hg->counter1[58].val = 15000;
+
+ /* Catch all massive value (30secs) */
+ hg->counter1[59].val = 30000;
+
+ /* Catch all massive value (60secs) */
+ hg->counter1[60].val = 60000;
+
+ /* Catch all massive value (5mins) */
+ hg->counter1[61].val = 300000;
+
+ /* Catch all massive value (15mins) */
+ hg->counter1[62].val = 900000;
+
+ /* Catch all massive values (1hr) */
+ hg->counter1[63].val = 3600000;
+}
+
+void saa7164_histogram_update(struct saa7164_histogram *hg, u32 val)
+{
+ int i;
+ for (i = 0; i < 64; i++) {
+ if (val <= hg->counter1[i].val) {
+ hg->counter1[i].count++;
+ hg->counter1[i].update_time = jiffies;
+ break;
+ }
+ }
+}
+
+static void saa7164_histogram_print(struct saa7164_port *port,
+ struct saa7164_histogram *hg)
+{
+ u32 entries = 0;
+ int i;
+
+ printk(KERN_ERR "Histogram named %s (ms, count, last_update_jiffy)\n", hg->name);
+ for (i = 0; i < 64; i++) {
+ if (hg->counter1[i].count == 0)
+ continue;
+
+ printk(KERN_ERR " %4d %12d %Ld\n",
+ hg->counter1[i].val,
+ hg->counter1[i].count,
+ hg->counter1[i].update_time);
+
+ entries++;
+ }
+ printk(KERN_ERR "Total: %d\n", entries);
+}
+
+static void saa7164_work_enchandler_helper(struct saa7164_port *port, int bufnr)
+{
+ struct saa7164_dev *dev = port->dev;
+ struct saa7164_buffer *buf = NULL;
+ struct saa7164_user_buffer *ubuf = NULL;
+ struct list_head *c, *n;
+ int i = 0;
+ u8 __iomem *p;
+
+ mutex_lock(&port->dmaqueue_lock);
+ list_for_each_safe(c, n, &port->dmaqueue.list) {
+
+ buf = list_entry(c, struct saa7164_buffer, list);
+ if (i++ > port->hwcfg.buffercount) {
+ printk(KERN_ERR "%s() illegal i count %d\n",
+ __func__, i);
+ break;
+ }
+
+ if (buf->idx == bufnr) {
+
+ /* Found the buffer, deal with it */
+ dprintk(DBGLVL_IRQ, "%s() bufnr: %d\n", __func__, bufnr);
+
+ if (crc_checking) {
+ /* Throw a new checksum on the dma buffer */
+ buf->crc = crc32(0, buf->cpu, buf->actual_size);
+ }
+
+ if (guard_checking) {
+ p = (u8 *)buf->cpu;
+ if ((*(p + buf->actual_size + 0) != 0xff) ||
+ (*(p + buf->actual_size + 1) != 0xff) ||
+ (*(p + buf->actual_size + 2) != 0xff) ||
+ (*(p + buf->actual_size + 3) != 0xff) ||
+ (*(p + buf->actual_size + 0x10) != 0xff) ||
+ (*(p + buf->actual_size + 0x11) != 0xff) ||
+ (*(p + buf->actual_size + 0x12) != 0xff) ||
+ (*(p + buf->actual_size + 0x13) != 0xff)) {
+ printk(KERN_ERR "%s() buf %p guard buffer breach\n",
+ __func__, buf);
+#if 0
+ saa7164_dumphex16FF(dev, (p + buf->actual_size) - 32 , 64);
+#endif
+ }
+ }
+
+ if ((port->nr != SAA7164_PORT_VBI1) && (port->nr != SAA7164_PORT_VBI2)) {
+ /* Validate the incoming buffer content */
+ if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_TS)
+ saa7164_ts_verifier(buf);
+ else if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS)
+ saa7164_pack_verifier(buf);
+ }
+
+ /* find a free user buffer and clone to it */
+ if (!list_empty(&port->list_buf_free.list)) {
+
+ /* Pull the first buffer from the used list */
+ ubuf = list_first_entry(&port->list_buf_free.list,
+ struct saa7164_user_buffer, list);
+
+ if (buf->actual_size <= ubuf->actual_size) {
+
+ memcpy_fromio(ubuf->data, buf->cpu,
+ ubuf->actual_size);
+
+ if (crc_checking) {
+ /* Throw a new checksum on the read buffer */
+ ubuf->crc = crc32(0, ubuf->data, ubuf->actual_size);
+ }
+
+ /* Requeue the buffer on the free list */
+ ubuf->pos = 0;
+
+ list_move_tail(&ubuf->list,
+ &port->list_buf_used.list);
+
+ /* Flag any userland waiters */
+ wake_up_interruptible(&port->wait_read);
+
+ } else {
+ printk(KERN_ERR "buf %p bufsize fails match\n", buf);
+ }
+
+ } else
+ printk(KERN_ERR "encirq no free buffers, increase param encoder_buffers\n");
+
+ /* Ensure offset into buffer remains 0, fill buffer
+ * with known bad data. We check for this data at a later point
+ * in time. */
+ saa7164_buffer_zero_offsets(port, bufnr);
+ memset_io(buf->cpu, 0xff, buf->pci_size);
+ if (crc_checking) {
+ /* Throw yet aanother new checksum on the dma buffer */
+ buf->crc = crc32(0, buf->cpu, buf->actual_size);
+ }
+
+ break;
+ }
+ }
+ mutex_unlock(&port->dmaqueue_lock);
+}
+
+static void saa7164_work_enchandler(struct work_struct *w)
+{
+ struct saa7164_port *port =
+ container_of(w, struct saa7164_port, workenc);
+ struct saa7164_dev *dev = port->dev;
+
+ u32 wp, mcb, rp, cnt = 0;
+
+ port->last_svc_msecs_diff = port->last_svc_msecs;
+ port->last_svc_msecs = jiffies_to_msecs(jiffies);
+
+ port->last_svc_msecs_diff = port->last_svc_msecs -
+ port->last_svc_msecs_diff;
+
+ saa7164_histogram_update(&port->svc_interval,
+ port->last_svc_msecs_diff);
+
+ port->last_irq_svc_msecs_diff = port->last_svc_msecs -
+ port->last_irq_msecs;
+
+ saa7164_histogram_update(&port->irq_svc_interval,
+ port->last_irq_svc_msecs_diff);
+
+ dprintk(DBGLVL_IRQ,
+ "%s() %Ldms elapsed irq->deferred %Ldms wp: %d rp: %d\n",
+ __func__,
+ port->last_svc_msecs_diff,
+ port->last_irq_svc_msecs_diff,
+ port->last_svc_wp,
+ port->last_svc_rp
+ );
+
+ /* Current write position */
+ wp = saa7164_readl(port->bufcounter);
+ if (wp > (port->hwcfg.buffercount - 1)) {
+ printk(KERN_ERR "%s() illegal buf count %d\n", __func__, wp);
+ return;
+ }
+
+ /* Most current complete buffer */
+ if (wp == 0)
+ mcb = (port->hwcfg.buffercount - 1);
+ else
+ mcb = wp - 1;
+
+ while (1) {
+ if (port->done_first_interrupt == 0) {
+ port->done_first_interrupt++;
+ rp = mcb;
+ } else
+ rp = (port->last_svc_rp + 1) % 8;
+
+ if ((rp < 0) || (rp > (port->hwcfg.buffercount - 1))) {
+ printk(KERN_ERR "%s() illegal rp count %d\n", __func__, rp);
+ break;
+ }
+
+ saa7164_work_enchandler_helper(port, rp);
+ port->last_svc_rp = rp;
+ cnt++;
+
+ if (rp == mcb)
+ break;
+ }
+
+ /* TODO: Convert this into a /proc/saa7164 style readable file */
+ if (print_histogram == port->nr) {
+ saa7164_histogram_print(port, &port->irq_interval);
+ saa7164_histogram_print(port, &port->svc_interval);
+ saa7164_histogram_print(port, &port->irq_svc_interval);
+ saa7164_histogram_print(port, &port->read_interval);
+ saa7164_histogram_print(port, &port->poll_interval);
+ /* TODO: fix this to preserve any previous state */
+ print_histogram = 64 + port->nr;
+ }
+}
+
+static void saa7164_work_vbihandler(struct work_struct *w)
+{
+ struct saa7164_port *port =
+ container_of(w, struct saa7164_port, workenc);
+ struct saa7164_dev *dev = port->dev;
+
+ u32 wp, mcb, rp, cnt = 0;
+
+ port->last_svc_msecs_diff = port->last_svc_msecs;
+ port->last_svc_msecs = jiffies_to_msecs(jiffies);
+ port->last_svc_msecs_diff = port->last_svc_msecs -
+ port->last_svc_msecs_diff;
+
+ saa7164_histogram_update(&port->svc_interval,
+ port->last_svc_msecs_diff);
+
+ port->last_irq_svc_msecs_diff = port->last_svc_msecs -
+ port->last_irq_msecs;
+
+ saa7164_histogram_update(&port->irq_svc_interval,
+ port->last_irq_svc_msecs_diff);
+
+ dprintk(DBGLVL_IRQ,
+ "%s() %Ldms elapsed irq->deferred %Ldms wp: %d rp: %d\n",
+ __func__,
+ port->last_svc_msecs_diff,
+ port->last_irq_svc_msecs_diff,
+ port->last_svc_wp,
+ port->last_svc_rp
+ );
+
+ /* Current write position */
+ wp = saa7164_readl(port->bufcounter);
+ if (wp > (port->hwcfg.buffercount - 1)) {
+ printk(KERN_ERR "%s() illegal buf count %d\n", __func__, wp);
+ return;
+ }
+
+ /* Most current complete buffer */
+ if (wp == 0)
+ mcb = (port->hwcfg.buffercount - 1);
+ else
+ mcb = wp - 1;
+
+ while (1) {
+ if (port->done_first_interrupt == 0) {
+ port->done_first_interrupt++;
+ rp = mcb;
+ } else
+ rp = (port->last_svc_rp + 1) % 8;
+
+ if ((rp < 0) || (rp > (port->hwcfg.buffercount - 1))) {
+ printk(KERN_ERR "%s() illegal rp count %d\n", __func__, rp);
+ break;
+ }
+
+ saa7164_work_enchandler_helper(port, rp);
+ port->last_svc_rp = rp;
+ cnt++;
+
+ if (rp == mcb)
+ break;
+ }
+
+ /* TODO: Convert this into a /proc/saa7164 style readable file */
+ if (print_histogram == port->nr) {
+ saa7164_histogram_print(port, &port->irq_interval);
+ saa7164_histogram_print(port, &port->svc_interval);
+ saa7164_histogram_print(port, &port->irq_svc_interval);
+ saa7164_histogram_print(port, &port->read_interval);
+ saa7164_histogram_print(port, &port->poll_interval);
+ /* TODO: fix this to preserve any previous state */
+ print_histogram = 64 + port->nr;
+ }
+}
+
+static void saa7164_work_cmdhandler(struct work_struct *w)
+{
+ struct saa7164_dev *dev = container_of(w, struct saa7164_dev, workcmd);
+
+ /* Wake up any complete commands */
+ saa7164_irq_dequeue(dev);
+}
+
+static void saa7164_buffer_deliver(struct saa7164_buffer *buf)
+{
+ struct saa7164_port *port = buf->port;
+
+ /* Feed the transport payload into the kernel demux */
+ dvb_dmx_swfilter_packets(&port->dvb.demux, (u8 *)buf->cpu,
+ SAA7164_TS_NUMBER_OF_LINES);
+
+}
+
+static irqreturn_t saa7164_irq_vbi(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+
+ /* Store old time */
+ port->last_irq_msecs_diff = port->last_irq_msecs;
+
+ /* Collect new stats */
+ port->last_irq_msecs = jiffies_to_msecs(jiffies);
+
+ /* Calculate stats */
+ port->last_irq_msecs_diff = port->last_irq_msecs -
+ port->last_irq_msecs_diff;
+
+ saa7164_histogram_update(&port->irq_interval,
+ port->last_irq_msecs_diff);
+
+ dprintk(DBGLVL_IRQ, "%s() %Ldms elapsed\n", __func__,
+ port->last_irq_msecs_diff);
+
+ /* Tis calls the vbi irq handler */
+ schedule_work(&port->workenc);
+ return 0;
+}
+
+static irqreturn_t saa7164_irq_encoder(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+
+ /* Store old time */
+ port->last_irq_msecs_diff = port->last_irq_msecs;
+
+ /* Collect new stats */
+ port->last_irq_msecs = jiffies_to_msecs(jiffies);
+
+ /* Calculate stats */
+ port->last_irq_msecs_diff = port->last_irq_msecs -
+ port->last_irq_msecs_diff;
+
+ saa7164_histogram_update(&port->irq_interval,
+ port->last_irq_msecs_diff);
+
+ dprintk(DBGLVL_IRQ, "%s() %Ldms elapsed\n", __func__,
+ port->last_irq_msecs_diff);
+
+ schedule_work(&port->workenc);
+ return 0;
+}
+
+static irqreturn_t saa7164_irq_ts(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ struct saa7164_buffer *buf;
+ struct list_head *c, *n;
+ int wp, i = 0, rp;
+
+ /* Find the current write point from the hardware */
+ wp = saa7164_readl(port->bufcounter);
+ if (wp > (port->hwcfg.buffercount - 1))
+ BUG();
+
+ /* Find the previous buffer to the current write point */
+ if (wp == 0)
+ rp = (port->hwcfg.buffercount - 1);
+ else
+ rp = wp - 1;
+
+ /* Lookup the WP in the buffer list */
+ /* TODO: turn this into a worker thread */
+ list_for_each_safe(c, n, &port->dmaqueue.list) {
+ buf = list_entry(c, struct saa7164_buffer, list);
+ if (i++ > port->hwcfg.buffercount)
+ BUG();
+
+ if (buf->idx == rp) {
+ /* Found the buffer, deal with it */
+ dprintk(DBGLVL_IRQ, "%s() wp: %d processing: %d\n",
+ __func__, wp, rp);
+ saa7164_buffer_deliver(buf);
+ break;
+ }
+
+ }
+ return 0;
+}
+
+/* Primary IRQ handler and dispatch mechanism */
+static irqreturn_t saa7164_irq(int irq, void *dev_id)
+{
+ struct saa7164_dev *dev = dev_id;
+ struct saa7164_port *porta = &dev->ports[SAA7164_PORT_TS1];
+ struct saa7164_port *portb = &dev->ports[SAA7164_PORT_TS2];
+ struct saa7164_port *portc = &dev->ports[SAA7164_PORT_ENC1];
+ struct saa7164_port *portd = &dev->ports[SAA7164_PORT_ENC2];
+ struct saa7164_port *porte = &dev->ports[SAA7164_PORT_VBI1];
+ struct saa7164_port *portf = &dev->ports[SAA7164_PORT_VBI2];
+
+ u32 intid, intstat[INT_SIZE/4];
+ int i, handled = 0, bit;
+
+ if (dev == NULL) {
+ printk(KERN_ERR "%s() No device specified\n", __func__);
+ handled = 0;
+ goto out;
+ }
+
+ /* Check that the hardware is accessible. If the status bytes are
+ * 0xFF then the device is not accessible, the the IRQ belongs
+ * to another driver.
+ * 4 x u32 interrupt registers.
+ */
+ for (i = 0; i < INT_SIZE/4; i++) {
+
+ /* TODO: Convert into saa7164_readl() */
+ /* Read the 4 hardware interrupt registers */
+ intstat[i] = saa7164_readl(dev->int_status + (i * 4));
+
+ if (intstat[i])
+ handled = 1;
+ }
+ if (handled == 0)
+ goto out;
+
+ /* For each of the HW interrupt registers */
+ for (i = 0; i < INT_SIZE/4; i++) {
+
+ if (intstat[i]) {
+ /* Each function of the board has it's own interruptid.
+ * Find the function that triggered then call
+ * it's handler.
+ */
+ for (bit = 0; bit < 32; bit++) {
+
+ if (((intstat[i] >> bit) & 0x00000001) == 0)
+ continue;
+
+ /* Calculate the interrupt id (0x00 to 0x7f) */
+
+ intid = (i * 32) + bit;
+ if (intid == dev->intfdesc.bInterruptId) {
+ /* A response to an cmd/api call */
+ schedule_work(&dev->workcmd);
+ } else if (intid == porta->hwcfg.interruptid) {
+
+ /* Transport path 1 */
+ saa7164_irq_ts(porta);
+
+ } else if (intid == portb->hwcfg.interruptid) {
+
+ /* Transport path 2 */
+ saa7164_irq_ts(portb);
+
+ } else if (intid == portc->hwcfg.interruptid) {
+
+ /* Encoder path 1 */
+ saa7164_irq_encoder(portc);
+
+ } else if (intid == portd->hwcfg.interruptid) {
+
+ /* Encoder path 2 */
+ saa7164_irq_encoder(portd);
+
+ } else if (intid == porte->hwcfg.interruptid) {
+
+ /* VBI path 1 */
+ saa7164_irq_vbi(porte);
+
+ } else if (intid == portf->hwcfg.interruptid) {
+
+ /* VBI path 2 */
+ saa7164_irq_vbi(portf);
+
+ } else {
+ /* Find the function */
+ dprintk(DBGLVL_IRQ,
+ "%s() unhandled interrupt "
+ "reg 0x%x bit 0x%x "
+ "intid = 0x%x\n",
+ __func__, i, bit, intid);
+ }
+ }
+
+ /* Ack it */
+ saa7164_writel(dev->int_ack + (i * 4), intstat[i]);
+
+ }
+ }
+out:
+ return IRQ_RETVAL(handled);
+}
+
+void saa7164_getfirmwarestatus(struct saa7164_dev *dev)
+{
+ struct saa7164_fw_status *s = &dev->fw_status;
+
+ dev->fw_status.status = saa7164_readl(SAA_DEVICE_SYSINIT_STATUS);
+ dev->fw_status.mode = saa7164_readl(SAA_DEVICE_SYSINIT_MODE);
+ dev->fw_status.spec = saa7164_readl(SAA_DEVICE_SYSINIT_SPEC);
+ dev->fw_status.inst = saa7164_readl(SAA_DEVICE_SYSINIT_INST);
+ dev->fw_status.cpuload = saa7164_readl(SAA_DEVICE_SYSINIT_CPULOAD);
+ dev->fw_status.remainheap =
+ saa7164_readl(SAA_DEVICE_SYSINIT_REMAINHEAP);
+
+ dprintk(1, "Firmware status:\n");
+ dprintk(1, " .status = 0x%08x\n", s->status);
+ dprintk(1, " .mode = 0x%08x\n", s->mode);
+ dprintk(1, " .spec = 0x%08x\n", s->spec);
+ dprintk(1, " .inst = 0x%08x\n", s->inst);
+ dprintk(1, " .cpuload = 0x%08x\n", s->cpuload);
+ dprintk(1, " .remainheap = 0x%08x\n", s->remainheap);
+}
+
+u32 saa7164_getcurrentfirmwareversion(struct saa7164_dev *dev)
+{
+ u32 reg;
+
+ reg = saa7164_readl(SAA_DEVICE_VERSION);
+ dprintk(1, "Device running firmware version %d.%d.%d.%d (0x%x)\n",
+ (reg & 0x0000fc00) >> 10,
+ (reg & 0x000003e0) >> 5,
+ (reg & 0x0000001f),
+ (reg & 0xffff0000) >> 16,
+ reg);
+
+ return reg;
+}
+
+/* TODO: Debugging func, remove */
+void saa7164_dumphex16(struct saa7164_dev *dev, u8 *buf, int len)
+{
+ int i;
+
+ printk(KERN_INFO "--------------------> "
+ "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n");
+
+ for (i = 0; i < len; i += 16)
+ printk(KERN_INFO " [0x%08x] "
+ "%02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n", i,
+ *(buf+i+0), *(buf+i+1), *(buf+i+2), *(buf+i+3),
+ *(buf+i+4), *(buf+i+5), *(buf+i+6), *(buf+i+7),
+ *(buf+i+8), *(buf+i+9), *(buf+i+10), *(buf+i+11),
+ *(buf+i+12), *(buf+i+13), *(buf+i+14), *(buf+i+15));
+}
+
+/* TODO: Debugging func, remove */
+void saa7164_dumpregs(struct saa7164_dev *dev, u32 addr)
+{
+ int i;
+
+ dprintk(1, "--------------------> "
+ "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n");
+
+ for (i = 0; i < 0x100; i += 16)
+ dprintk(1, "region0[0x%08x] = "
+ "%02x %02x %02x %02x %02x %02x %02x %02x"
+ " %02x %02x %02x %02x %02x %02x %02x %02x\n", i,
+ (u8)saa7164_readb(addr + i + 0),
+ (u8)saa7164_readb(addr + i + 1),
+ (u8)saa7164_readb(addr + i + 2),
+ (u8)saa7164_readb(addr + i + 3),
+ (u8)saa7164_readb(addr + i + 4),
+ (u8)saa7164_readb(addr + i + 5),
+ (u8)saa7164_readb(addr + i + 6),
+ (u8)saa7164_readb(addr + i + 7),
+ (u8)saa7164_readb(addr + i + 8),
+ (u8)saa7164_readb(addr + i + 9),
+ (u8)saa7164_readb(addr + i + 10),
+ (u8)saa7164_readb(addr + i + 11),
+ (u8)saa7164_readb(addr + i + 12),
+ (u8)saa7164_readb(addr + i + 13),
+ (u8)saa7164_readb(addr + i + 14),
+ (u8)saa7164_readb(addr + i + 15)
+ );
+}
+
+static void saa7164_dump_hwdesc(struct saa7164_dev *dev)
+{
+ dprintk(1, "@0x%p hwdesc sizeof(struct tmComResHWDescr) = %d bytes\n",
+ &dev->hwdesc, (u32)sizeof(struct tmComResHWDescr));
+
+ dprintk(1, " .bLength = 0x%x\n", dev->hwdesc.bLength);
+ dprintk(1, " .bDescriptorType = 0x%x\n", dev->hwdesc.bDescriptorType);
+ dprintk(1, " .bDescriptorSubtype = 0x%x\n",
+ dev->hwdesc.bDescriptorSubtype);
+
+ dprintk(1, " .bcdSpecVersion = 0x%x\n", dev->hwdesc.bcdSpecVersion);
+ dprintk(1, " .dwClockFrequency = 0x%x\n", dev->hwdesc.dwClockFrequency);
+ dprintk(1, " .dwClockUpdateRes = 0x%x\n", dev->hwdesc.dwClockUpdateRes);
+ dprintk(1, " .bCapabilities = 0x%x\n", dev->hwdesc.bCapabilities);
+ dprintk(1, " .dwDeviceRegistersLocation = 0x%x\n",
+ dev->hwdesc.dwDeviceRegistersLocation);
+
+ dprintk(1, " .dwHostMemoryRegion = 0x%x\n",
+ dev->hwdesc.dwHostMemoryRegion);
+
+ dprintk(1, " .dwHostMemoryRegionSize = 0x%x\n",
+ dev->hwdesc.dwHostMemoryRegionSize);
+
+ dprintk(1, " .dwHostHibernatMemRegion = 0x%x\n",
+ dev->hwdesc.dwHostHibernatMemRegion);
+
+ dprintk(1, " .dwHostHibernatMemRegionSize = 0x%x\n",
+ dev->hwdesc.dwHostHibernatMemRegionSize);
+}
+
+static void saa7164_dump_intfdesc(struct saa7164_dev *dev)
+{
+ dprintk(1, "@0x%p intfdesc "
+ "sizeof(struct tmComResInterfaceDescr) = %d bytes\n",
+ &dev->intfdesc, (u32)sizeof(struct tmComResInterfaceDescr));
+
+ dprintk(1, " .bLength = 0x%x\n", dev->intfdesc.bLength);
+ dprintk(1, " .bDescriptorType = 0x%x\n", dev->intfdesc.bDescriptorType);
+ dprintk(1, " .bDescriptorSubtype = 0x%x\n",
+ dev->intfdesc.bDescriptorSubtype);
+
+ dprintk(1, " .bFlags = 0x%x\n", dev->intfdesc.bFlags);
+ dprintk(1, " .bInterfaceType = 0x%x\n", dev->intfdesc.bInterfaceType);
+ dprintk(1, " .bInterfaceId = 0x%x\n", dev->intfdesc.bInterfaceId);
+ dprintk(1, " .bBaseInterface = 0x%x\n", dev->intfdesc.bBaseInterface);
+ dprintk(1, " .bInterruptId = 0x%x\n", dev->intfdesc.bInterruptId);
+ dprintk(1, " .bDebugInterruptId = 0x%x\n",
+ dev->intfdesc.bDebugInterruptId);
+
+ dprintk(1, " .BARLocation = 0x%x\n", dev->intfdesc.BARLocation);
+}
+
+static void saa7164_dump_busdesc(struct saa7164_dev *dev)
+{
+ dprintk(1, "@0x%p busdesc sizeof(struct tmComResBusDescr) = %d bytes\n",
+ &dev->busdesc, (u32)sizeof(struct tmComResBusDescr));
+
+ dprintk(1, " .CommandRing = 0x%016Lx\n", dev->busdesc.CommandRing);
+ dprintk(1, " .ResponseRing = 0x%016Lx\n", dev->busdesc.ResponseRing);
+ dprintk(1, " .CommandWrite = 0x%x\n", dev->busdesc.CommandWrite);
+ dprintk(1, " .CommandRead = 0x%x\n", dev->busdesc.CommandRead);
+ dprintk(1, " .ResponseWrite = 0x%x\n", dev->busdesc.ResponseWrite);
+ dprintk(1, " .ResponseRead = 0x%x\n", dev->busdesc.ResponseRead);
+}
+
+/* Much of the hardware configuration and PCI registers are configured
+ * dynamically depending on firmware. We have to cache some initial
+ * structures then use these to locate other important structures
+ * from PCI space.
+ */
+static void saa7164_get_descriptors(struct saa7164_dev *dev)
+{
+ memcpy_fromio(&dev->hwdesc, dev->bmmio, sizeof(struct tmComResHWDescr));
+ memcpy_fromio(&dev->intfdesc, dev->bmmio + sizeof(struct tmComResHWDescr),
+ sizeof(struct tmComResInterfaceDescr));
+ memcpy_fromio(&dev->busdesc, dev->bmmio + dev->intfdesc.BARLocation,
+ sizeof(struct tmComResBusDescr));
+
+ if (dev->hwdesc.bLength != sizeof(struct tmComResHWDescr)) {
+ printk(KERN_ERR "Structure struct tmComResHWDescr is mangled\n");
+ printk(KERN_ERR "Need %x got %d\n", dev->hwdesc.bLength,
+ (u32)sizeof(struct tmComResHWDescr));
+ } else
+ saa7164_dump_hwdesc(dev);
+
+ if (dev->intfdesc.bLength != sizeof(struct tmComResInterfaceDescr)) {
+ printk(KERN_ERR "struct struct tmComResInterfaceDescr is mangled\n");
+ printk(KERN_ERR "Need %x got %d\n", dev->intfdesc.bLength,
+ (u32)sizeof(struct tmComResInterfaceDescr));
+ } else
+ saa7164_dump_intfdesc(dev);
+
+ saa7164_dump_busdesc(dev);
+}
+
+static int saa7164_pci_quirks(struct saa7164_dev *dev)
+{
+ return 0;
+}
+
+static int get_resources(struct saa7164_dev *dev)
+{
+ if (request_mem_region(pci_resource_start(dev->pci, 0),
+ pci_resource_len(dev->pci, 0), dev->name)) {
+
+ if (request_mem_region(pci_resource_start(dev->pci, 2),
+ pci_resource_len(dev->pci, 2), dev->name))
+ return 0;
+ }
+
+ printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx or 0x%llx\n",
+ dev->name,
+ (u64)pci_resource_start(dev->pci, 0),
+ (u64)pci_resource_start(dev->pci, 2));
+
+ return -EBUSY;
+}
+
+static int saa7164_port_init(struct saa7164_dev *dev, int portnr)
+{
+ struct saa7164_port *port = NULL;
+
+ if ((portnr < 0) || (portnr >= SAA7164_MAX_PORTS))
+ BUG();
+
+ port = &dev->ports[portnr];
+
+ port->dev = dev;
+ port->nr = portnr;
+
+ if ((portnr == SAA7164_PORT_TS1) || (portnr == SAA7164_PORT_TS2))
+ port->type = SAA7164_MPEG_DVB;
+ else
+ if ((portnr == SAA7164_PORT_ENC1) || (portnr == SAA7164_PORT_ENC2)) {
+ port->type = SAA7164_MPEG_ENCODER;
+
+ /* We need a deferred interrupt handler for cmd handling */
+ INIT_WORK(&port->workenc, saa7164_work_enchandler);
+ } else if ((portnr == SAA7164_PORT_VBI1) || (portnr == SAA7164_PORT_VBI2)) {
+ port->type = SAA7164_MPEG_VBI;
+
+ /* We need a deferred interrupt handler for cmd handling */
+ INIT_WORK(&port->workenc, saa7164_work_vbihandler);
+ } else
+ BUG();
+
+ /* Init all the critical resources */
+ mutex_init(&port->dvb.lock);
+ INIT_LIST_HEAD(&port->dmaqueue.list);
+ mutex_init(&port->dmaqueue_lock);
+
+ INIT_LIST_HEAD(&port->list_buf_used.list);
+ INIT_LIST_HEAD(&port->list_buf_free.list);
+ init_waitqueue_head(&port->wait_read);
+
+
+ saa7164_histogram_reset(&port->irq_interval, "irq intervals");
+ saa7164_histogram_reset(&port->svc_interval, "deferred intervals");
+ saa7164_histogram_reset(&port->irq_svc_interval,
+ "irq to deferred intervals");
+ saa7164_histogram_reset(&port->read_interval,
+ "encoder/vbi read() intervals");
+ saa7164_histogram_reset(&port->poll_interval,
+ "encoder/vbi poll() intervals");
+
+ return 0;
+}
+
+static int saa7164_dev_setup(struct saa7164_dev *dev)
+{
+ int i;
+
+ mutex_init(&dev->lock);
+ atomic_inc(&dev->refcount);
+ dev->nr = saa7164_devcount++;
+
+ snprintf(dev->name, sizeof(dev->name), "saa7164[%d]", dev->nr);
+
+ mutex_lock(&devlist);
+ list_add_tail(&dev->devlist, &saa7164_devlist);
+ mutex_unlock(&devlist);
+
+ /* board config */
+ dev->board = UNSET;
+ if (card[dev->nr] < saa7164_bcount)
+ dev->board = card[dev->nr];
+
+ for (i = 0; UNSET == dev->board && i < saa7164_idcount; i++)
+ if (dev->pci->subsystem_vendor == saa7164_subids[i].subvendor &&
+ dev->pci->subsystem_device ==
+ saa7164_subids[i].subdevice)
+ dev->board = saa7164_subids[i].card;
+
+ if (UNSET == dev->board) {
+ dev->board = SAA7164_BOARD_UNKNOWN;
+ saa7164_card_list(dev);
+ }
+
+ dev->pci_bus = dev->pci->bus->number;
+ dev->pci_slot = PCI_SLOT(dev->pci->devfn);
+
+ /* I2C Defaults / setup */
+ dev->i2c_bus[0].dev = dev;
+ dev->i2c_bus[0].nr = 0;
+ dev->i2c_bus[1].dev = dev;
+ dev->i2c_bus[1].nr = 1;
+ dev->i2c_bus[2].dev = dev;
+ dev->i2c_bus[2].nr = 2;
+
+ /* Transport + Encoder ports 1, 2, 3, 4 - Defaults / setup */
+ saa7164_port_init(dev, SAA7164_PORT_TS1);
+ saa7164_port_init(dev, SAA7164_PORT_TS2);
+ saa7164_port_init(dev, SAA7164_PORT_ENC1);
+ saa7164_port_init(dev, SAA7164_PORT_ENC2);
+ saa7164_port_init(dev, SAA7164_PORT_VBI1);
+ saa7164_port_init(dev, SAA7164_PORT_VBI2);
+
+ if (get_resources(dev) < 0) {
+ printk(KERN_ERR "CORE %s No more PCIe resources for "
+ "subsystem: %04x:%04x\n",
+ dev->name, dev->pci->subsystem_vendor,
+ dev->pci->subsystem_device);
+
+ saa7164_devcount--;
+ return -ENODEV;
+ }
+
+ /* PCI/e allocations */
+ dev->lmmio = ioremap(pci_resource_start(dev->pci, 0),
+ pci_resource_len(dev->pci, 0));
+
+ dev->lmmio2 = ioremap(pci_resource_start(dev->pci, 2),
+ pci_resource_len(dev->pci, 2));
+
+ dev->bmmio = (u8 __iomem *)dev->lmmio;
+ dev->bmmio2 = (u8 __iomem *)dev->lmmio2;
+
+ /* Inerrupt and ack register locations offset of bmmio */
+ dev->int_status = 0x183000 + 0xf80;
+ dev->int_ack = 0x183000 + 0xf90;
+
+ printk(KERN_INFO
+ "CORE %s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n",
+ dev->name, dev->pci->subsystem_vendor,
+ dev->pci->subsystem_device, saa7164_boards[dev->board].name,
+ dev->board, card[dev->nr] == dev->board ?
+ "insmod option" : "autodetected");
+
+ saa7164_pci_quirks(dev);
+
+ return 0;
+}
+
+static void saa7164_dev_unregister(struct saa7164_dev *dev)
+{
+ dprintk(1, "%s()\n", __func__);
+
+ release_mem_region(pci_resource_start(dev->pci, 0),
+ pci_resource_len(dev->pci, 0));
+
+ release_mem_region(pci_resource_start(dev->pci, 2),
+ pci_resource_len(dev->pci, 2));
+
+ if (!atomic_dec_and_test(&dev->refcount))
+ return;
+
+ iounmap(dev->lmmio);
+ iounmap(dev->lmmio2);
+
+ return;
+}
+
+#ifdef CONFIG_PROC_FS
+static int saa7164_proc_show(struct seq_file *m, void *v)
+{
+ struct saa7164_dev *dev;
+ struct tmComResBusInfo *b;
+ struct list_head *list;
+ int i, c;
+
+ if (saa7164_devcount == 0)
+ return 0;
+
+ list_for_each(list, &saa7164_devlist) {
+ dev = list_entry(list, struct saa7164_dev, devlist);
+ seq_printf(m, "%s = %p\n", dev->name, dev);
+
+ /* Lock the bus from any other access */
+ b = &dev->bus;
+ mutex_lock(&b->lock);
+
+ seq_printf(m, " .m_pdwSetWritePos = 0x%x (0x%08x)\n",
+ b->m_dwSetReadPos, saa7164_readl(b->m_dwSetReadPos));
+
+ seq_printf(m, " .m_pdwSetReadPos = 0x%x (0x%08x)\n",
+ b->m_dwSetWritePos, saa7164_readl(b->m_dwSetWritePos));
+
+ seq_printf(m, " .m_pdwGetWritePos = 0x%x (0x%08x)\n",
+ b->m_dwGetReadPos, saa7164_readl(b->m_dwGetReadPos));
+
+ seq_printf(m, " .m_pdwGetReadPos = 0x%x (0x%08x)\n",
+ b->m_dwGetWritePos, saa7164_readl(b->m_dwGetWritePos));
+ c = 0;
+ seq_printf(m, "\n Set Ring:\n");
+ seq_printf(m, "\n addr 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n");
+ for (i = 0; i < b->m_dwSizeSetRing; i++) {
+ if (c == 0)
+ seq_printf(m, " %04x:", i);
+
+ seq_printf(m, " %02x", *(b->m_pdwSetRing + i));
+
+ if (++c == 16) {
+ seq_printf(m, "\n");
+ c = 0;
+ }
+ }
+
+ c = 0;
+ seq_printf(m, "\n Get Ring:\n");
+ seq_printf(m, "\n addr 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n");
+ for (i = 0; i < b->m_dwSizeGetRing; i++) {
+ if (c == 0)
+ seq_printf(m, " %04x:", i);
+
+ seq_printf(m, " %02x", *(b->m_pdwGetRing + i));
+
+ if (++c == 16) {
+ seq_printf(m, "\n");
+ c = 0;
+ }
+ }
+
+ mutex_unlock(&b->lock);
+
+ }
+
+ return 0;
+}
+
+static int saa7164_proc_open(struct inode *inode, struct file *filp)
+{
+ return single_open(filp, saa7164_proc_show, NULL);
+}
+
+static const struct file_operations saa7164_proc_fops = {
+ .open = saa7164_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int saa7164_proc_create(void)
+{
+ struct proc_dir_entry *pe;
+
+ pe = proc_create("saa7164", S_IRUGO, NULL, &saa7164_proc_fops);
+ if (!pe)
+ return -ENOMEM;
+
+ return 0;
+}
+#endif
+
+static int saa7164_thread_function(void *data)
+{
+ struct saa7164_dev *dev = data;
+ struct tmFwInfoStruct fwinfo;
+ u64 last_poll_time = 0;
+
+ dprintk(DBGLVL_THR, "thread started\n");
+
+ set_freezable();
+
+ while (1) {
+ msleep_interruptible(100);
+ if (kthread_should_stop())
+ break;
+ try_to_freeze();
+
+ dprintk(DBGLVL_THR, "thread running\n");
+
+ /* Dump the firmware debug message to console */
+ /* Polling this costs us 1-2% of the arm CPU */
+ /* convert this into a respnde to interrupt 0x7a */
+ saa7164_api_collect_debug(dev);
+
+ /* Monitor CPU load every 1 second */
+ if ((last_poll_time + 1000 /* ms */) < jiffies_to_msecs(jiffies)) {
+ saa7164_api_get_load_info(dev, &fwinfo);
+ last_poll_time = jiffies_to_msecs(jiffies);
+ }
+
+ }
+
+ dprintk(DBGLVL_THR, "thread exiting\n");
+ return 0;
+}
+
+static int __devinit saa7164_initdev(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_id)
+{
+ struct saa7164_dev *dev;
+ int err, i;
+ u32 version;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (NULL == dev)
+ return -ENOMEM;
+
+ /* pci init */
+ dev->pci = pci_dev;
+ if (pci_enable_device(pci_dev)) {
+ err = -EIO;
+ goto fail_free;
+ }
+
+ if (saa7164_dev_setup(dev) < 0) {
+ err = -EINVAL;
+ goto fail_free;
+ }
+
+ /* print pci info */
+ dev->pci_rev = pci_dev->revision;
+ pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat);
+ printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, "
+ "latency: %d, mmio: 0x%llx\n", dev->name,
+ pci_name(pci_dev), dev->pci_rev, pci_dev->irq,
+ dev->pci_lat,
+ (unsigned long long)pci_resource_start(pci_dev, 0));
+
+ pci_set_master(pci_dev);
+ /* TODO */
+ if (!pci_dma_supported(pci_dev, 0xffffffff)) {
+ printk("%s/0: Oops: no 32bit PCI DMA ???\n", dev->name);
+ err = -EIO;
+ goto fail_irq;
+ }
+
+ err = request_irq(pci_dev->irq, saa7164_irq,
+ IRQF_SHARED | IRQF_DISABLED, dev->name, dev);
+ if (err < 0) {
+ printk(KERN_ERR "%s: can't get IRQ %d\n", dev->name,
+ pci_dev->irq);
+ err = -EIO;
+ goto fail_irq;
+ }
+
+ pci_set_drvdata(pci_dev, dev);
+
+ /* Init the internal command list */
+ for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) {
+ dev->cmds[i].seqno = i;
+ dev->cmds[i].inuse = 0;
+ mutex_init(&dev->cmds[i].lock);
+ init_waitqueue_head(&dev->cmds[i].wait);
+ }
+
+ /* We need a deferred interrupt handler for cmd handling */
+ INIT_WORK(&dev->workcmd, saa7164_work_cmdhandler);
+
+ /* Only load the firmware if we know the board */
+ if (dev->board != SAA7164_BOARD_UNKNOWN) {
+
+ err = saa7164_downloadfirmware(dev);
+ if (err < 0) {
+ printk(KERN_ERR
+ "Failed to boot firmware, no features "
+ "registered\n");
+ goto fail_fw;
+ }
+
+ saa7164_get_descriptors(dev);
+ saa7164_dumpregs(dev, 0);
+ saa7164_getcurrentfirmwareversion(dev);
+ saa7164_getfirmwarestatus(dev);
+ err = saa7164_bus_setup(dev);
+ if (err < 0)
+ printk(KERN_ERR
+ "Failed to setup the bus, will continue\n");
+ saa7164_bus_dump(dev);
+
+ /* Ping the running firmware via the command bus and get the
+ * firmware version, this checks the bus is running OK.
+ */
+ version = 0;
+ if (saa7164_api_get_fw_version(dev, &version) == SAA_OK)
+ dprintk(1, "Bus is operating correctly using "
+ "version %d.%d.%d.%d (0x%x)\n",
+ (version & 0x0000fc00) >> 10,
+ (version & 0x000003e0) >> 5,
+ (version & 0x0000001f),
+ (version & 0xffff0000) >> 16,
+ version);
+ else
+ printk(KERN_ERR
+ "Failed to communicate with the firmware\n");
+
+ /* Bring up the I2C buses */
+ saa7164_i2c_register(&dev->i2c_bus[0]);
+ saa7164_i2c_register(&dev->i2c_bus[1]);
+ saa7164_i2c_register(&dev->i2c_bus[2]);
+ saa7164_gpio_setup(dev);
+ saa7164_card_setup(dev);
+
+ /* Parse the dynamic device configuration, find various
+ * media endpoints (MPEG, WMV, PS, TS) and cache their
+ * configuration details into the driver, so we can
+ * reference them later during simething_register() func,
+ * interrupt handlers, deferred work handlers etc.
+ */
+ saa7164_api_enum_subdevs(dev);
+
+ /* Begin to create the video sub-systems and register funcs */
+ if (saa7164_boards[dev->board].porta == SAA7164_MPEG_DVB) {
+ if (saa7164_dvb_register(&dev->ports[SAA7164_PORT_TS1]) < 0) {
+ printk(KERN_ERR "%s() Failed to register "
+ "dvb adapters on porta\n",
+ __func__);
+ }
+ }
+
+ if (saa7164_boards[dev->board].portb == SAA7164_MPEG_DVB) {
+ if (saa7164_dvb_register(&dev->ports[SAA7164_PORT_TS2]) < 0) {
+ printk(KERN_ERR"%s() Failed to register "
+ "dvb adapters on portb\n",
+ __func__);
+ }
+ }
+
+ if (saa7164_boards[dev->board].portc == SAA7164_MPEG_ENCODER) {
+ if (saa7164_encoder_register(&dev->ports[SAA7164_PORT_ENC1]) < 0) {
+ printk(KERN_ERR"%s() Failed to register "
+ "mpeg encoder\n", __func__);
+ }
+ }
+
+ if (saa7164_boards[dev->board].portd == SAA7164_MPEG_ENCODER) {
+ if (saa7164_encoder_register(&dev->ports[SAA7164_PORT_ENC2]) < 0) {
+ printk(KERN_ERR"%s() Failed to register "
+ "mpeg encoder\n", __func__);
+ }
+ }
+
+ if (saa7164_boards[dev->board].porte == SAA7164_MPEG_VBI) {
+ if (saa7164_vbi_register(&dev->ports[SAA7164_PORT_VBI1]) < 0) {
+ printk(KERN_ERR"%s() Failed to register "
+ "vbi device\n", __func__);
+ }
+ }
+
+ if (saa7164_boards[dev->board].portf == SAA7164_MPEG_VBI) {
+ if (saa7164_vbi_register(&dev->ports[SAA7164_PORT_VBI2]) < 0) {
+ printk(KERN_ERR"%s() Failed to register "
+ "vbi device\n", __func__);
+ }
+ }
+ saa7164_api_set_debug(dev, fw_debug);
+
+ if (fw_debug) {
+ dev->kthread = kthread_run(saa7164_thread_function, dev,
+ "saa7164 debug");
+ if (!dev->kthread)
+ printk(KERN_ERR "%s() Failed to create "
+ "debug kernel thread\n", __func__);
+ }
+
+ } /* != BOARD_UNKNOWN */
+ else
+ printk(KERN_ERR "%s() Unsupported board detected, "
+ "registering without firmware\n", __func__);
+
+ dprintk(1, "%s() parameter debug = %d\n", __func__, saa_debug);
+ dprintk(1, "%s() parameter waitsecs = %d\n", __func__, waitsecs);
+
+fail_fw:
+ return 0;
+
+fail_irq:
+ saa7164_dev_unregister(dev);
+fail_free:
+ kfree(dev);
+ return err;
+}
+
+static void saa7164_shutdown(struct saa7164_dev *dev)
+{
+ dprintk(1, "%s()\n", __func__);
+}
+
+static void __devexit saa7164_finidev(struct pci_dev *pci_dev)
+{
+ struct saa7164_dev *dev = pci_get_drvdata(pci_dev);
+
+ if (dev->board != SAA7164_BOARD_UNKNOWN) {
+ if (fw_debug && dev->kthread) {
+ kthread_stop(dev->kthread);
+ dev->kthread = NULL;
+ }
+ if (dev->firmwareloaded)
+ saa7164_api_set_debug(dev, 0x00);
+ }
+
+ saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1],
+ &dev->ports[SAA7164_PORT_ENC1].irq_interval);
+ saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1],
+ &dev->ports[SAA7164_PORT_ENC1].svc_interval);
+ saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1],
+ &dev->ports[SAA7164_PORT_ENC1].irq_svc_interval);
+ saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1],
+ &dev->ports[SAA7164_PORT_ENC1].read_interval);
+ saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1],
+ &dev->ports[SAA7164_PORT_ENC1].poll_interval);
+ saa7164_histogram_print(&dev->ports[SAA7164_PORT_VBI1],
+ &dev->ports[SAA7164_PORT_VBI1].read_interval);
+ saa7164_histogram_print(&dev->ports[SAA7164_PORT_VBI2],
+ &dev->ports[SAA7164_PORT_VBI2].poll_interval);
+
+ saa7164_shutdown(dev);
+
+ if (saa7164_boards[dev->board].porta == SAA7164_MPEG_DVB)
+ saa7164_dvb_unregister(&dev->ports[SAA7164_PORT_TS1]);
+
+ if (saa7164_boards[dev->board].portb == SAA7164_MPEG_DVB)
+ saa7164_dvb_unregister(&dev->ports[SAA7164_PORT_TS2]);
+
+ if (saa7164_boards[dev->board].portc == SAA7164_MPEG_ENCODER)
+ saa7164_encoder_unregister(&dev->ports[SAA7164_PORT_ENC1]);
+
+ if (saa7164_boards[dev->board].portd == SAA7164_MPEG_ENCODER)
+ saa7164_encoder_unregister(&dev->ports[SAA7164_PORT_ENC2]);
+
+ if (saa7164_boards[dev->board].porte == SAA7164_MPEG_VBI)
+ saa7164_vbi_unregister(&dev->ports[SAA7164_PORT_VBI1]);
+
+ if (saa7164_boards[dev->board].portf == SAA7164_MPEG_VBI)
+ saa7164_vbi_unregister(&dev->ports[SAA7164_PORT_VBI2]);
+
+ saa7164_i2c_unregister(&dev->i2c_bus[0]);
+ saa7164_i2c_unregister(&dev->i2c_bus[1]);
+ saa7164_i2c_unregister(&dev->i2c_bus[2]);
+
+ pci_disable_device(pci_dev);
+
+ /* unregister stuff */
+ free_irq(pci_dev->irq, dev);
+ pci_set_drvdata(pci_dev, NULL);
+
+ mutex_lock(&devlist);
+ list_del(&dev->devlist);
+ mutex_unlock(&devlist);
+
+ saa7164_dev_unregister(dev);
+ kfree(dev);
+}
+
+static struct pci_device_id saa7164_pci_tbl[] = {
+ {
+ /* SAA7164 */
+ .vendor = 0x1131,
+ .device = 0x7164,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ }, {
+ /* --- end of list --- */
+ }
+};
+MODULE_DEVICE_TABLE(pci, saa7164_pci_tbl);
+
+static struct pci_driver saa7164_pci_driver = {
+ .name = "saa7164",
+ .id_table = saa7164_pci_tbl,
+ .probe = saa7164_initdev,
+ .remove = __devexit_p(saa7164_finidev),
+ /* TODO */
+ .suspend = NULL,
+ .resume = NULL,
+};
+
+static int __init saa7164_init(void)
+{
+ printk(KERN_INFO "saa7164 driver loaded\n");
+
+#ifdef CONFIG_PROC_FS
+ saa7164_proc_create();
+#endif
+ return pci_register_driver(&saa7164_pci_driver);
+}
+
+static void __exit saa7164_fini(void)
+{
+#ifdef CONFIG_PROC_FS
+ remove_proc_entry("saa7164", NULL);
+#endif
+ pci_unregister_driver(&saa7164_pci_driver);
+}
+
+module_init(saa7164_init);
+module_exit(saa7164_fini);
+
diff --git a/drivers/media/video/saa7164/saa7164-dvb.c b/drivers/media/video/saa7164/saa7164-dvb.c
new file mode 100644
index 00000000..5c5cc3eb
--- /dev/null
+++ b/drivers/media/video/saa7164/saa7164-dvb.c
@@ -0,0 +1,556 @@
+/*
+ * Driver for the NXP SAA7164 PCIe bridge
+ *
+ * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "saa7164.h"
+
+#include "tda10048.h"
+#include "tda18271.h"
+#include "s5h1411.h"
+
+#define DRIVER_NAME "saa7164"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+/* addr is in the card struct, get it from there */
+static struct tda10048_config hauppauge_hvr2200_1_config = {
+ .demod_address = 0x10 >> 1,
+ .output_mode = TDA10048_SERIAL_OUTPUT,
+ .fwbulkwritelen = TDA10048_BULKWRITE_200,
+ .inversion = TDA10048_INVERSION_ON,
+ .dtv6_if_freq_khz = TDA10048_IF_3300,
+ .dtv7_if_freq_khz = TDA10048_IF_3500,
+ .dtv8_if_freq_khz = TDA10048_IF_4000,
+ .clk_freq_khz = TDA10048_CLK_16000,
+};
+static struct tda10048_config hauppauge_hvr2200_2_config = {
+ .demod_address = 0x12 >> 1,
+ .output_mode = TDA10048_SERIAL_OUTPUT,
+ .fwbulkwritelen = TDA10048_BULKWRITE_200,
+ .inversion = TDA10048_INVERSION_ON,
+ .dtv6_if_freq_khz = TDA10048_IF_3300,
+ .dtv7_if_freq_khz = TDA10048_IF_3500,
+ .dtv8_if_freq_khz = TDA10048_IF_4000,
+ .clk_freq_khz = TDA10048_CLK_16000,
+};
+
+static struct tda18271_std_map hauppauge_tda18271_std_map = {
+ .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 3,
+ .if_lvl = 6, .rfagc_top = 0x37 },
+ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0,
+ .if_lvl = 6, .rfagc_top = 0x37 },
+};
+
+static struct tda18271_config hauppauge_hvr22x0_tuner_config = {
+ .std_map = &hauppauge_tda18271_std_map,
+ .gate = TDA18271_GATE_ANALOG,
+ .role = TDA18271_MASTER,
+};
+
+static struct tda18271_config hauppauge_hvr22x0s_tuner_config = {
+ .std_map = &hauppauge_tda18271_std_map,
+ .gate = TDA18271_GATE_ANALOG,
+ .role = TDA18271_SLAVE,
+ .output_opt = TDA18271_OUTPUT_LT_OFF,
+ .rf_cal_on_startup = 1
+};
+
+static struct s5h1411_config hauppauge_s5h1411_config = {
+ .output_mode = S5H1411_SERIAL_OUTPUT,
+ .gpio = S5H1411_GPIO_ON,
+ .qam_if = S5H1411_IF_4000,
+ .vsb_if = S5H1411_IF_3250,
+ .inversion = S5H1411_INVERSION_ON,
+ .status_mode = S5H1411_DEMODLOCKING,
+ .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static int saa7164_dvb_stop_port(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ int ret;
+
+ ret = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
+ if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
+ printk(KERN_ERR "%s() stop transition failed, ret = 0x%x\n",
+ __func__, ret);
+ ret = -EIO;
+ } else {
+ dprintk(DBGLVL_DVB, "%s() Stopped\n", __func__);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int saa7164_dvb_acquire_port(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ int ret;
+
+ ret = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);
+ if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
+ printk(KERN_ERR "%s() acquire transition failed, ret = 0x%x\n",
+ __func__, ret);
+ ret = -EIO;
+ } else {
+ dprintk(DBGLVL_DVB, "%s() Acquired\n", __func__);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int saa7164_dvb_pause_port(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ int ret;
+
+ ret = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE);
+ if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
+ printk(KERN_ERR "%s() pause transition failed, ret = 0x%x\n",
+ __func__, ret);
+ ret = -EIO;
+ } else {
+ dprintk(DBGLVL_DVB, "%s() Paused\n", __func__);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+/* Firmware is very windows centric, meaning you have to transition
+ * the part through AVStream / KS Windows stages, forwards or backwards.
+ * States are: stopped, acquired (h/w), paused, started.
+ */
+static int saa7164_dvb_stop_streaming(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ struct saa7164_buffer *buf;
+ struct list_head *p, *q;
+ int ret;
+
+ dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr);
+
+ ret = saa7164_dvb_pause_port(port);
+ ret = saa7164_dvb_acquire_port(port);
+ ret = saa7164_dvb_stop_port(port);
+
+ /* Mark the hardware buffers as free */
+ mutex_lock(&port->dmaqueue_lock);
+ list_for_each_safe(p, q, &port->dmaqueue.list) {
+ buf = list_entry(p, struct saa7164_buffer, list);
+ buf->flags = SAA7164_BUFFER_FREE;
+ }
+ mutex_unlock(&port->dmaqueue_lock);
+
+ return ret;
+}
+
+static int saa7164_dvb_start_port(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ int ret = 0, result;
+
+ dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr);
+
+ saa7164_buffer_cfg_port(port);
+
+ /* Acquire the hardware */
+ result = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);
+ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+ printk(KERN_ERR "%s() acquire transition failed, res = 0x%x\n",
+ __func__, result);
+
+ /* Stop the hardware, regardless */
+ result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
+ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+ printk(KERN_ERR "%s() acquire/forced stop transition "
+ "failed, res = 0x%x\n", __func__, result);
+ }
+ ret = -EIO;
+ goto out;
+ } else
+ dprintk(DBGLVL_DVB, "%s() Acquired\n", __func__);
+
+ /* Pause the hardware */
+ result = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE);
+ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+ printk(KERN_ERR "%s() pause transition failed, res = 0x%x\n",
+ __func__, result);
+
+ /* Stop the hardware, regardless */
+ result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
+ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+ printk(KERN_ERR "%s() pause/forced stop transition "
+ "failed, res = 0x%x\n", __func__, result);
+ }
+
+ ret = -EIO;
+ goto out;
+ } else
+ dprintk(DBGLVL_DVB, "%s() Paused\n", __func__);
+
+ /* Start the hardware */
+ result = saa7164_api_transition_port(port, SAA_DMASTATE_RUN);
+ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+ printk(KERN_ERR "%s() run transition failed, result = 0x%x\n",
+ __func__, result);
+
+ /* Stop the hardware, regardless */
+ result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
+ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+ printk(KERN_ERR "%s() run/forced stop transition "
+ "failed, res = 0x%x\n", __func__, result);
+ }
+
+ ret = -EIO;
+ } else
+ dprintk(DBGLVL_DVB, "%s() Running\n", __func__);
+
+out:
+ return ret;
+}
+
+static int saa7164_dvb_start_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct saa7164_port *port = (struct saa7164_port *) demux->priv;
+ struct saa7164_dvb *dvb = &port->dvb;
+ struct saa7164_dev *dev = port->dev;
+ int ret = 0;
+
+ dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr);
+
+ if (!demux->dmx.frontend)
+ return -EINVAL;
+
+ if (dvb) {
+ mutex_lock(&dvb->lock);
+ if (dvb->feeding++ == 0) {
+ /* Start transport */
+ ret = saa7164_dvb_start_port(port);
+ }
+ mutex_unlock(&dvb->lock);
+ dprintk(DBGLVL_DVB, "%s(port=%d) now feeding = %d\n",
+ __func__, port->nr, dvb->feeding);
+ }
+
+ return ret;
+}
+
+static int saa7164_dvb_stop_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct saa7164_port *port = (struct saa7164_port *) demux->priv;
+ struct saa7164_dvb *dvb = &port->dvb;
+ struct saa7164_dev *dev = port->dev;
+ int ret = 0;
+
+ dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr);
+
+ if (dvb) {
+ mutex_lock(&dvb->lock);
+ if (--dvb->feeding == 0) {
+ /* Stop transport */
+ ret = saa7164_dvb_stop_streaming(port);
+ }
+ mutex_unlock(&dvb->lock);
+ dprintk(DBGLVL_DVB, "%s(port=%d) now feeding = %d\n",
+ __func__, port->nr, dvb->feeding);
+ }
+
+ return ret;
+}
+
+static int dvb_register(struct saa7164_port *port)
+{
+ struct saa7164_dvb *dvb = &port->dvb;
+ struct saa7164_dev *dev = port->dev;
+ struct saa7164_buffer *buf;
+ int result, i;
+
+ dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr);
+
+ if (port->type != SAA7164_MPEG_DVB)
+ BUG();
+
+ /* Sanity check that the PCI configuration space is active */
+ if (port->hwcfg.BARLocation == 0) {
+ result = -ENOMEM;
+ printk(KERN_ERR "%s: dvb_register_adapter failed "
+ "(errno = %d), NO PCI configuration\n",
+ DRIVER_NAME, result);
+ goto fail_adapter;
+ }
+
+ /* Init and establish defaults */
+ port->hw_streamingparams.bitspersample = 8;
+ port->hw_streamingparams.samplesperline = 188;
+ port->hw_streamingparams.numberoflines =
+ (SAA7164_TS_NUMBER_OF_LINES * 188) / 188;
+
+ port->hw_streamingparams.pitch = 188;
+ port->hw_streamingparams.linethreshold = 0;
+ port->hw_streamingparams.pagetablelistvirt = NULL;
+ port->hw_streamingparams.pagetablelistphys = NULL;
+ port->hw_streamingparams.numpagetables = 2 +
+ ((SAA7164_TS_NUMBER_OF_LINES * 188) / PAGE_SIZE);
+
+ port->hw_streamingparams.numpagetableentries = port->hwcfg.buffercount;
+
+ /* Allocate the PCI resources */
+ for (i = 0; i < port->hwcfg.buffercount; i++) {
+ buf = saa7164_buffer_alloc(port,
+ port->hw_streamingparams.numberoflines *
+ port->hw_streamingparams.pitch);
+
+ if (!buf) {
+ result = -ENOMEM;
+ printk(KERN_ERR "%s: dvb_register_adapter failed "
+ "(errno = %d), unable to allocate buffers\n",
+ DRIVER_NAME, result);
+ goto fail_adapter;
+ }
+
+ mutex_lock(&port->dmaqueue_lock);
+ list_add_tail(&buf->list, &port->dmaqueue.list);
+ mutex_unlock(&port->dmaqueue_lock);
+ }
+
+ /* register adapter */
+ result = dvb_register_adapter(&dvb->adapter, DRIVER_NAME, THIS_MODULE,
+ &dev->pci->dev, adapter_nr);
+ if (result < 0) {
+ printk(KERN_ERR "%s: dvb_register_adapter failed "
+ "(errno = %d)\n", DRIVER_NAME, result);
+ goto fail_adapter;
+ }
+ dvb->adapter.priv = port;
+
+ /* register frontend */
+ result = dvb_register_frontend(&dvb->adapter, dvb->frontend);
+ if (result < 0) {
+ printk(KERN_ERR "%s: dvb_register_frontend failed "
+ "(errno = %d)\n", DRIVER_NAME, result);
+ goto fail_frontend;
+ }
+
+ /* register demux stuff */
+ dvb->demux.dmx.capabilities =
+ DMX_TS_FILTERING | DMX_SECTION_FILTERING |
+ DMX_MEMORY_BASED_FILTERING;
+ dvb->demux.priv = port;
+ dvb->demux.filternum = 256;
+ dvb->demux.feednum = 256;
+ dvb->demux.start_feed = saa7164_dvb_start_feed;
+ dvb->demux.stop_feed = saa7164_dvb_stop_feed;
+ result = dvb_dmx_init(&dvb->demux);
+ if (result < 0) {
+ printk(KERN_ERR "%s: dvb_dmx_init failed (errno = %d)\n",
+ DRIVER_NAME, result);
+ goto fail_dmx;
+ }
+
+ dvb->dmxdev.filternum = 256;
+ dvb->dmxdev.demux = &dvb->demux.dmx;
+ dvb->dmxdev.capabilities = 0;
+ result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter);
+ if (result < 0) {
+ printk(KERN_ERR "%s: dvb_dmxdev_init failed (errno = %d)\n",
+ DRIVER_NAME, result);
+ goto fail_dmxdev;
+ }
+
+ dvb->fe_hw.source = DMX_FRONTEND_0;
+ result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+ if (result < 0) {
+ printk(KERN_ERR "%s: add_frontend failed "
+ "(DMX_FRONTEND_0, errno = %d)\n", DRIVER_NAME, result);
+ goto fail_fe_hw;
+ }
+
+ dvb->fe_mem.source = DMX_MEMORY_FE;
+ result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+ if (result < 0) {
+ printk(KERN_ERR "%s: add_frontend failed "
+ "(DMX_MEMORY_FE, errno = %d)\n", DRIVER_NAME, result);
+ goto fail_fe_mem;
+ }
+
+ result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+ if (result < 0) {
+ printk(KERN_ERR "%s: connect_frontend failed (errno = %d)\n",
+ DRIVER_NAME, result);
+ goto fail_fe_conn;
+ }
+
+ /* register network adapter */
+ dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx);
+ return 0;
+
+fail_fe_conn:
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+fail_fe_mem:
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+fail_fe_hw:
+ dvb_dmxdev_release(&dvb->dmxdev);
+fail_dmxdev:
+ dvb_dmx_release(&dvb->demux);
+fail_dmx:
+ dvb_unregister_frontend(dvb->frontend);
+fail_frontend:
+ dvb_frontend_detach(dvb->frontend);
+ dvb_unregister_adapter(&dvb->adapter);
+fail_adapter:
+ return result;
+}
+
+int saa7164_dvb_unregister(struct saa7164_port *port)
+{
+ struct saa7164_dvb *dvb = &port->dvb;
+ struct saa7164_dev *dev = port->dev;
+ struct saa7164_buffer *b;
+ struct list_head *c, *n;
+
+ dprintk(DBGLVL_DVB, "%s()\n", __func__);
+
+ if (port->type != SAA7164_MPEG_DVB)
+ BUG();
+
+ /* Remove any allocated buffers */
+ mutex_lock(&port->dmaqueue_lock);
+ list_for_each_safe(c, n, &port->dmaqueue.list) {
+ b = list_entry(c, struct saa7164_buffer, list);
+ list_del(c);
+ saa7164_buffer_dealloc(b);
+ }
+ mutex_unlock(&port->dmaqueue_lock);
+
+ if (dvb->frontend == NULL)
+ return 0;
+
+ dvb_net_release(&dvb->net);
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+ dvb_dmxdev_release(&dvb->dmxdev);
+ dvb_dmx_release(&dvb->demux);
+ dvb_unregister_frontend(dvb->frontend);
+ dvb_frontend_detach(dvb->frontend);
+ dvb_unregister_adapter(&dvb->adapter);
+ return 0;
+}
+
+/* All the DVB attach calls go here, this function get's modified
+ * for each new card.
+ */
+int saa7164_dvb_register(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ struct saa7164_dvb *dvb = &port->dvb;
+ struct saa7164_i2c *i2c_bus = NULL;
+ int ret;
+
+ dprintk(DBGLVL_DVB, "%s()\n", __func__);
+
+ /* init frontend */
+ switch (dev->board) {
+ case SAA7164_BOARD_HAUPPAUGE_HVR2200:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2200_2:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2200_3:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2200_4:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2200_5:
+ i2c_bus = &dev->i2c_bus[port->nr + 1];
+ switch (port->nr) {
+ case 0:
+ port->dvb.frontend = dvb_attach(tda10048_attach,
+ &hauppauge_hvr2200_1_config,
+ &i2c_bus->i2c_adap);
+
+ if (port->dvb.frontend != NULL) {
+ /* TODO: addr is in the card struct */
+ dvb_attach(tda18271_attach, port->dvb.frontend,
+ 0xc0 >> 1, &i2c_bus->i2c_adap,
+ &hauppauge_hvr22x0_tuner_config);
+ }
+
+ break;
+ case 1:
+ port->dvb.frontend = dvb_attach(tda10048_attach,
+ &hauppauge_hvr2200_2_config,
+ &i2c_bus->i2c_adap);
+
+ if (port->dvb.frontend != NULL) {
+ /* TODO: addr is in the card struct */
+ dvb_attach(tda18271_attach, port->dvb.frontend,
+ 0xc0 >> 1, &i2c_bus->i2c_adap,
+ &hauppauge_hvr22x0s_tuner_config);
+ }
+
+ break;
+ }
+ break;
+ case SAA7164_BOARD_HAUPPAUGE_HVR2250:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2250_2:
+ case SAA7164_BOARD_HAUPPAUGE_HVR2250_3:
+ i2c_bus = &dev->i2c_bus[port->nr + 1];
+
+ port->dvb.frontend = dvb_attach(s5h1411_attach,
+ &hauppauge_s5h1411_config,
+ &i2c_bus->i2c_adap);
+
+ if (port->dvb.frontend != NULL) {
+ if (port->nr == 0) {
+ /* Master TDA18271 */
+ /* TODO: addr is in the card struct */
+ dvb_attach(tda18271_attach, port->dvb.frontend,
+ 0xc0 >> 1, &i2c_bus->i2c_adap,
+ &hauppauge_hvr22x0_tuner_config);
+ } else {
+ /* Slave TDA18271 */
+ dvb_attach(tda18271_attach, port->dvb.frontend,
+ 0xc0 >> 1, &i2c_bus->i2c_adap,
+ &hauppauge_hvr22x0s_tuner_config);
+ }
+ }
+
+ break;
+ default:
+ printk(KERN_ERR "%s: The frontend isn't supported\n",
+ dev->name);
+ break;
+ }
+ if (NULL == dvb->frontend) {
+ printk(KERN_ERR "%s() Frontend initialization failed\n",
+ __func__);
+ return -1;
+ }
+
+ /* register everything */
+ ret = dvb_register(port);
+ if (ret < 0) {
+ if (dvb->frontend->ops.release)
+ dvb->frontend->ops.release(dvb->frontend);
+ return ret;
+ }
+
+ return 0;
+}
+
diff --git a/drivers/media/video/saa7164/saa7164-encoder.c b/drivers/media/video/saa7164/saa7164-encoder.c
new file mode 100644
index 00000000..a9ed686a
--- /dev/null
+++ b/drivers/media/video/saa7164/saa7164-encoder.c
@@ -0,0 +1,1500 @@
+/*
+ * Driver for the NXP SAA7164 PCIe bridge
+ *
+ * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "saa7164.h"
+
+#define ENCODER_MAX_BITRATE 6500000
+#define ENCODER_MIN_BITRATE 1000000
+#define ENCODER_DEF_BITRATE 5000000
+
+static struct saa7164_tvnorm saa7164_tvnorms[] = {
+ {
+ .name = "NTSC-M",
+ .id = V4L2_STD_NTSC_M,
+ }, {
+ .name = "NTSC-JP",
+ .id = V4L2_STD_NTSC_M_JP,
+ }
+};
+
+static const u32 saa7164_v4l2_ctrls[] = {
+ V4L2_CID_BRIGHTNESS,
+ V4L2_CID_CONTRAST,
+ V4L2_CID_SATURATION,
+ V4L2_CID_HUE,
+ V4L2_CID_AUDIO_VOLUME,
+ V4L2_CID_SHARPNESS,
+ V4L2_CID_MPEG_STREAM_TYPE,
+ V4L2_CID_MPEG_VIDEO_ASPECT,
+ V4L2_CID_MPEG_VIDEO_B_FRAMES,
+ V4L2_CID_MPEG_VIDEO_GOP_SIZE,
+ V4L2_CID_MPEG_AUDIO_MUTE,
+ V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+ V4L2_CID_MPEG_VIDEO_BITRATE,
+ V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
+ 0
+};
+
+/* Take the encoder configuration form the port struct and
+ * flush it to the hardware.
+ */
+static void saa7164_encoder_configure(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+ port->encoder_params.width = port->width;
+ port->encoder_params.height = port->height;
+ port->encoder_params.is_50hz =
+ (port->encodernorm.id & V4L2_STD_625_50) != 0;
+
+ /* Set up the DIF (enable it) for analog mode by default */
+ saa7164_api_initialize_dif(port);
+
+ /* Configure the correct video standard */
+ saa7164_api_configure_dif(port, port->encodernorm.id);
+
+ /* Ensure the audio decoder is correct configured */
+ saa7164_api_set_audio_std(port);
+}
+
+static int saa7164_encoder_buffers_dealloc(struct saa7164_port *port)
+{
+ struct list_head *c, *n, *p, *q, *l, *v;
+ struct saa7164_dev *dev = port->dev;
+ struct saa7164_buffer *buf;
+ struct saa7164_user_buffer *ubuf;
+
+ /* Remove any allocated buffers */
+ mutex_lock(&port->dmaqueue_lock);
+
+ dprintk(DBGLVL_ENC, "%s(port=%d) dmaqueue\n", __func__, port->nr);
+ list_for_each_safe(c, n, &port->dmaqueue.list) {
+ buf = list_entry(c, struct saa7164_buffer, list);
+ list_del(c);
+ saa7164_buffer_dealloc(buf);
+ }
+
+ dprintk(DBGLVL_ENC, "%s(port=%d) used\n", __func__, port->nr);
+ list_for_each_safe(p, q, &port->list_buf_used.list) {
+ ubuf = list_entry(p, struct saa7164_user_buffer, list);
+ list_del(p);
+ saa7164_buffer_dealloc_user(ubuf);
+ }
+
+ dprintk(DBGLVL_ENC, "%s(port=%d) free\n", __func__, port->nr);
+ list_for_each_safe(l, v, &port->list_buf_free.list) {
+ ubuf = list_entry(l, struct saa7164_user_buffer, list);
+ list_del(l);
+ saa7164_buffer_dealloc_user(ubuf);
+ }
+
+ mutex_unlock(&port->dmaqueue_lock);
+ dprintk(DBGLVL_ENC, "%s(port=%d) done\n", __func__, port->nr);
+
+ return 0;
+}
+
+/* Dynamic buffer switch at encoder start time */
+static int saa7164_encoder_buffers_alloc(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ struct saa7164_buffer *buf;
+ struct saa7164_user_buffer *ubuf;
+ struct tmHWStreamParameters *params = &port->hw_streamingparams;
+ int result = -ENODEV, i;
+ int len = 0;
+
+ dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+ if (port->encoder_params.stream_type ==
+ V4L2_MPEG_STREAM_TYPE_MPEG2_PS) {
+ dprintk(DBGLVL_ENC,
+ "%s() type=V4L2_MPEG_STREAM_TYPE_MPEG2_PS\n",
+ __func__);
+ params->samplesperline = 128;
+ params->numberoflines = 256;
+ params->pitch = 128;
+ params->numpagetables = 2 +
+ ((SAA7164_PS_NUMBER_OF_LINES * 128) / PAGE_SIZE);
+ } else
+ if (port->encoder_params.stream_type ==
+ V4L2_MPEG_STREAM_TYPE_MPEG2_TS) {
+ dprintk(DBGLVL_ENC,
+ "%s() type=V4L2_MPEG_STREAM_TYPE_MPEG2_TS\n",
+ __func__);
+ params->samplesperline = 188;
+ params->numberoflines = 312;
+ params->pitch = 188;
+ params->numpagetables = 2 +
+ ((SAA7164_TS_NUMBER_OF_LINES * 188) / PAGE_SIZE);
+ } else
+ BUG();
+
+ /* Init and establish defaults */
+ params->bitspersample = 8;
+ params->linethreshold = 0;
+ params->pagetablelistvirt = NULL;
+ params->pagetablelistphys = NULL;
+ params->numpagetableentries = port->hwcfg.buffercount;
+
+ /* Allocate the PCI resources, buffers (hard) */
+ for (i = 0; i < port->hwcfg.buffercount; i++) {
+ buf = saa7164_buffer_alloc(port,
+ params->numberoflines *
+ params->pitch);
+
+ if (!buf) {
+ printk(KERN_ERR "%s() failed "
+ "(errno = %d), unable to allocate buffer\n",
+ __func__, result);
+ result = -ENOMEM;
+ goto failed;
+ } else {
+
+ mutex_lock(&port->dmaqueue_lock);
+ list_add_tail(&buf->list, &port->dmaqueue.list);
+ mutex_unlock(&port->dmaqueue_lock);
+
+ }
+ }
+
+ /* Allocate some kernel buffers for copying
+ * to userpsace.
+ */
+ len = params->numberoflines * params->pitch;
+
+ if (encoder_buffers < 16)
+ encoder_buffers = 16;
+ if (encoder_buffers > 512)
+ encoder_buffers = 512;
+
+ for (i = 0; i < encoder_buffers; i++) {
+
+ ubuf = saa7164_buffer_alloc_user(dev, len);
+ if (ubuf) {
+ mutex_lock(&port->dmaqueue_lock);
+ list_add_tail(&ubuf->list, &port->list_buf_free.list);
+ mutex_unlock(&port->dmaqueue_lock);
+ }
+
+ }
+
+ result = 0;
+
+failed:
+ return result;
+}
+
+static int saa7164_encoder_initialize(struct saa7164_port *port)
+{
+ saa7164_encoder_configure(port);
+ return 0;
+}
+
+/* -- V4L2 --------------------------------------------------------- */
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct saa7164_encoder_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ struct saa7164_dev *dev = port->dev;
+ unsigned int i;
+
+ dprintk(DBGLVL_ENC, "%s(id=0x%x)\n", __func__, (u32)*id);
+
+ for (i = 0; i < ARRAY_SIZE(saa7164_tvnorms); i++) {
+ if (*id & saa7164_tvnorms[i].id)
+ break;
+ }
+ if (i == ARRAY_SIZE(saa7164_tvnorms))
+ return -EINVAL;
+
+ port->encodernorm = saa7164_tvnorms[i];
+
+ /* Update the audio decoder while is not running in
+ * auto detect mode.
+ */
+ saa7164_api_set_audio_std(port);
+
+ dprintk(DBGLVL_ENC, "%s(id=0x%x) OK\n", __func__, (u32)*id);
+
+ return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ int n;
+
+ char *inputs[] = { "tuner", "composite", "svideo", "aux",
+ "composite 2", "svideo 2", "aux 2" };
+
+ if (i->index >= 7)
+ return -EINVAL;
+
+ strcpy(i->name, inputs[i->index]);
+
+ if (i->index == 0)
+ i->type = V4L2_INPUT_TYPE_TUNER;
+ else
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+
+ for (n = 0; n < ARRAY_SIZE(saa7164_tvnorms); n++)
+ i->std |= saa7164_tvnorms[n].id;
+
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct saa7164_encoder_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ struct saa7164_dev *dev = port->dev;
+
+ if (saa7164_api_get_videomux(port) != SAA_OK)
+ return -EIO;
+
+ *i = (port->mux_input - 1);
+
+ dprintk(DBGLVL_ENC, "%s() input=%d\n", __func__, *i);
+
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct saa7164_encoder_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ struct saa7164_dev *dev = port->dev;
+
+ dprintk(DBGLVL_ENC, "%s() input=%d\n", __func__, i);
+
+ if (i >= 7)
+ return -EINVAL;
+
+ port->mux_input = i + 1;
+
+ if (saa7164_api_set_videomux(port) != SAA_OK)
+ return -EIO;
+
+ return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct saa7164_encoder_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ struct saa7164_dev *dev = port->dev;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ strcpy(t->name, "tuner");
+ t->type = V4L2_TUNER_ANALOG_TV;
+ t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO;
+
+ dprintk(DBGLVL_ENC, "VIDIOC_G_TUNER: tuner type %d\n", t->type);
+
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ /* Update the A/V core */
+ return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct saa7164_encoder_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+
+ f->type = V4L2_TUNER_ANALOG_TV;
+ f->frequency = port->freq;
+
+ return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct saa7164_encoder_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ struct saa7164_dev *dev = port->dev;
+ struct saa7164_port *tsport;
+ struct dvb_frontend *fe;
+
+ /* TODO: Pull this for the std */
+ struct analog_parameters params = {
+ .mode = V4L2_TUNER_ANALOG_TV,
+ .audmode = V4L2_TUNER_MODE_STEREO,
+ .std = port->encodernorm.id,
+ .frequency = f->frequency
+ };
+
+ /* Stop the encoder */
+ dprintk(DBGLVL_ENC, "%s() frequency=%d tuner=%d\n", __func__,
+ f->frequency, f->tuner);
+
+ if (f->tuner != 0)
+ return -EINVAL;
+
+ if (f->type != V4L2_TUNER_ANALOG_TV)
+ return -EINVAL;
+
+ port->freq = f->frequency;
+
+ /* Update the hardware */
+ if (port->nr == SAA7164_PORT_ENC1)
+ tsport = &dev->ports[SAA7164_PORT_TS1];
+ else
+ if (port->nr == SAA7164_PORT_ENC2)
+ tsport = &dev->ports[SAA7164_PORT_TS2];
+ else
+ BUG();
+
+ fe = tsport->dvb.frontend;
+
+ if (fe && fe->ops.tuner_ops.set_analog_params)
+ fe->ops.tuner_ops.set_analog_params(fe, &params);
+ else
+ printk(KERN_ERR "%s() No analog tuner, aborting\n", __func__);
+
+ saa7164_encoder_initialize(port);
+
+ return 0;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctl)
+{
+ struct saa7164_encoder_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ struct saa7164_dev *dev = port->dev;
+
+ dprintk(DBGLVL_ENC, "%s(id=%d, value=%d)\n", __func__,
+ ctl->id, ctl->value);
+
+ switch (ctl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctl->value = port->ctl_brightness;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctl->value = port->ctl_contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ ctl->value = port->ctl_saturation;
+ break;
+ case V4L2_CID_HUE:
+ ctl->value = port->ctl_hue;
+ break;
+ case V4L2_CID_SHARPNESS:
+ ctl->value = port->ctl_sharpness;
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ ctl->value = port->ctl_volume;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctl)
+{
+ struct saa7164_encoder_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ struct saa7164_dev *dev = port->dev;
+ int ret = 0;
+
+ dprintk(DBGLVL_ENC, "%s(id=%d, value=%d)\n", __func__,
+ ctl->id, ctl->value);
+
+ switch (ctl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ if ((ctl->value >= 0) && (ctl->value <= 255)) {
+ port->ctl_brightness = ctl->value;
+ saa7164_api_set_usercontrol(port,
+ PU_BRIGHTNESS_CONTROL);
+ } else
+ ret = -EINVAL;
+ break;
+ case V4L2_CID_CONTRAST:
+ if ((ctl->value >= 0) && (ctl->value <= 255)) {
+ port->ctl_contrast = ctl->value;
+ saa7164_api_set_usercontrol(port, PU_CONTRAST_CONTROL);
+ } else
+ ret = -EINVAL;
+ break;
+ case V4L2_CID_SATURATION:
+ if ((ctl->value >= 0) && (ctl->value <= 255)) {
+ port->ctl_saturation = ctl->value;
+ saa7164_api_set_usercontrol(port,
+ PU_SATURATION_CONTROL);
+ } else
+ ret = -EINVAL;
+ break;
+ case V4L2_CID_HUE:
+ if ((ctl->value >= 0) && (ctl->value <= 255)) {
+ port->ctl_hue = ctl->value;
+ saa7164_api_set_usercontrol(port, PU_HUE_CONTROL);
+ } else
+ ret = -EINVAL;
+ break;
+ case V4L2_CID_SHARPNESS:
+ if ((ctl->value >= 0) && (ctl->value <= 255)) {
+ port->ctl_sharpness = ctl->value;
+ saa7164_api_set_usercontrol(port, PU_SHARPNESS_CONTROL);
+ } else
+ ret = -EINVAL;
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ if ((ctl->value >= -83) && (ctl->value <= 24)) {
+ port->ctl_volume = ctl->value;
+ saa7164_api_set_audio_volume(port, port->ctl_volume);
+ } else
+ ret = -EINVAL;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int saa7164_get_ctrl(struct saa7164_port *port,
+ struct v4l2_ext_control *ctrl)
+{
+ struct saa7164_encoder_params *params = &port->encoder_params;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ ctrl->value = params->bitrate;
+ break;
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ ctrl->value = params->stream_type;
+ break;
+ case V4L2_CID_MPEG_AUDIO_MUTE:
+ ctrl->value = params->ctl_mute;
+ break;
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ ctrl->value = params->ctl_aspect;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ ctrl->value = params->bitrate_mode;
+ break;
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ ctrl->value = params->refdist;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+ ctrl->value = params->bitrate_peak;
+ break;
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ ctrl->value = params->gop_size;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int vidioc_g_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *ctrls)
+{
+ struct saa7164_encoder_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ int i, err = 0;
+
+ if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+ for (i = 0; i < ctrls->count; i++) {
+ struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+ err = saa7164_get_ctrl(port, ctrl);
+ if (err) {
+ ctrls->error_idx = i;
+ break;
+ }
+ }
+ return err;
+
+ }
+
+ return -EINVAL;
+}
+
+static int saa7164_try_ctrl(struct v4l2_ext_control *ctrl, int ac3)
+{
+ int ret = -EINVAL;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ if ((ctrl->value >= ENCODER_MIN_BITRATE) &&
+ (ctrl->value <= ENCODER_MAX_BITRATE))
+ ret = 0;
+ break;
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ if ((ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_PS) ||
+ (ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_TS))
+ ret = 0;
+ break;
+ case V4L2_CID_MPEG_AUDIO_MUTE:
+ if ((ctrl->value >= 0) &&
+ (ctrl->value <= 1))
+ ret = 0;
+ break;
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ if ((ctrl->value >= V4L2_MPEG_VIDEO_ASPECT_1x1) &&
+ (ctrl->value <= V4L2_MPEG_VIDEO_ASPECT_221x100))
+ ret = 0;
+ break;
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ if ((ctrl->value >= 0) &&
+ (ctrl->value <= 255))
+ ret = 0;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ if ((ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) ||
+ (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR))
+ ret = 0;
+ break;
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ if ((ctrl->value >= 1) &&
+ (ctrl->value <= 3))
+ ret = 0;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+ if ((ctrl->value >= ENCODER_MIN_BITRATE) &&
+ (ctrl->value <= ENCODER_MAX_BITRATE))
+ ret = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int vidioc_try_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *ctrls)
+{
+ int i, err = 0;
+
+ if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+ for (i = 0; i < ctrls->count; i++) {
+ struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+ err = saa7164_try_ctrl(ctrl, 0);
+ if (err) {
+ ctrls->error_idx = i;
+ break;
+ }
+ }
+ return err;
+ }
+
+ return -EINVAL;
+}
+
+static int saa7164_set_ctrl(struct saa7164_port *port,
+ struct v4l2_ext_control *ctrl)
+{
+ struct saa7164_encoder_params *params = &port->encoder_params;
+ int ret = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ params->bitrate = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ params->stream_type = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_AUDIO_MUTE:
+ params->ctl_mute = ctrl->value;
+ ret = saa7164_api_audio_mute(port, params->ctl_mute);
+ if (ret != SAA_OK) {
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__,
+ ret);
+ ret = -EIO;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ params->ctl_aspect = ctrl->value;
+ ret = saa7164_api_set_aspect_ratio(port);
+ if (ret != SAA_OK) {
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__,
+ ret);
+ ret = -EIO;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ params->bitrate_mode = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ params->refdist = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+ params->bitrate_peak = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ params->gop_size = ctrl->value;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* TODO: Update the hardware */
+
+ return ret;
+}
+
+static int vidioc_s_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *ctrls)
+{
+ struct saa7164_encoder_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ int i, err = 0;
+
+ if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+ for (i = 0; i < ctrls->count; i++) {
+ struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+ err = saa7164_try_ctrl(ctrl, 0);
+ if (err) {
+ ctrls->error_idx = i;
+ break;
+ }
+ err = saa7164_set_ctrl(port, ctrl);
+ if (err) {
+ ctrls->error_idx = i;
+ break;
+ }
+ }
+ return err;
+
+ }
+
+ return -EINVAL;
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct saa7164_encoder_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ struct saa7164_dev *dev = port->dev;
+
+ strcpy(cap->driver, dev->name);
+ strlcpy(cap->card, saa7164_boards[dev->board].name,
+ sizeof(cap->card));
+ sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE |
+ 0;
+
+ cap->capabilities |= V4L2_CAP_TUNER;
+ cap->version = 0;
+
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index != 0)
+ return -EINVAL;
+
+ strlcpy(f->description, "MPEG", sizeof(f->description));
+ f->pixelformat = V4L2_PIX_FMT_MPEG;
+
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct saa7164_encoder_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ struct saa7164_dev *dev = port->dev;
+
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage =
+ port->ts_packet_size * port->ts_packet_count;
+ f->fmt.pix.colorspace = 0;
+ f->fmt.pix.width = port->width;
+ f->fmt.pix.height = port->height;
+
+ dprintk(DBGLVL_ENC, "VIDIOC_G_FMT: w: %d, h: %d\n",
+ port->width, port->height);
+
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct saa7164_encoder_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ struct saa7164_dev *dev = port->dev;
+
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage =
+ port->ts_packet_size * port->ts_packet_count;
+ f->fmt.pix.colorspace = 0;
+ dprintk(DBGLVL_ENC, "VIDIOC_TRY_FMT: w: %d, h: %d\n",
+ port->width, port->height);
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct saa7164_encoder_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ struct saa7164_dev *dev = port->dev;
+
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage =
+ port->ts_packet_size * port->ts_packet_count;
+ f->fmt.pix.colorspace = 0;
+
+ dprintk(DBGLVL_ENC, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n",
+ f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
+
+ return 0;
+}
+
+static int fill_queryctrl(struct saa7164_encoder_params *params,
+ struct v4l2_queryctrl *c)
+{
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 127);
+ case V4L2_CID_CONTRAST:
+ return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 66);
+ case V4L2_CID_SATURATION:
+ return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 62);
+ case V4L2_CID_HUE:
+ return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 128);
+ case V4L2_CID_SHARPNESS:
+ return v4l2_ctrl_query_fill(c, 0x0, 0x0f, 1, 8);
+ case V4L2_CID_MPEG_AUDIO_MUTE:
+ return v4l2_ctrl_query_fill(c, 0x0, 0x01, 1, 0);
+ case V4L2_CID_AUDIO_VOLUME:
+ return v4l2_ctrl_query_fill(c, -83, 24, 1, 20);
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ return v4l2_ctrl_query_fill(c,
+ ENCODER_MIN_BITRATE, ENCODER_MAX_BITRATE,
+ 100000, ENCODER_DEF_BITRATE);
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ return v4l2_ctrl_query_fill(c,
+ V4L2_MPEG_STREAM_TYPE_MPEG2_PS,
+ V4L2_MPEG_STREAM_TYPE_MPEG2_TS,
+ 1, V4L2_MPEG_STREAM_TYPE_MPEG2_PS);
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ return v4l2_ctrl_query_fill(c,
+ V4L2_MPEG_VIDEO_ASPECT_1x1,
+ V4L2_MPEG_VIDEO_ASPECT_221x100,
+ 1, V4L2_MPEG_VIDEO_ASPECT_4x3);
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ return v4l2_ctrl_query_fill(c, 1, 255, 1, 15);
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ return v4l2_ctrl_query_fill(c,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR,
+ 1, V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ return v4l2_ctrl_query_fill(c,
+ 1, 3, 1, 1);
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+ return v4l2_ctrl_query_fill(c,
+ ENCODER_MIN_BITRATE, ENCODER_MAX_BITRATE,
+ 100000, ENCODER_DEF_BITRATE);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ struct saa7164_encoder_fh *fh = priv;
+ struct saa7164_port *port = fh->port;
+ int i, next;
+ u32 id = c->id;
+
+ memset(c, 0, sizeof(*c));
+
+ next = !!(id & V4L2_CTRL_FLAG_NEXT_CTRL);
+ c->id = id & ~V4L2_CTRL_FLAG_NEXT_CTRL;
+
+ for (i = 0; i < ARRAY_SIZE(saa7164_v4l2_ctrls); i++) {
+ if (next) {
+ if (c->id < saa7164_v4l2_ctrls[i])
+ c->id = saa7164_v4l2_ctrls[i];
+ else
+ continue;
+ }
+
+ if (c->id == saa7164_v4l2_ctrls[i])
+ return fill_queryctrl(&port->encoder_params, c);
+
+ if (c->id < saa7164_v4l2_ctrls[i])
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int saa7164_encoder_stop_port(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ int ret;
+
+ ret = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
+ if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
+ printk(KERN_ERR "%s() stop transition failed, ret = 0x%x\n",
+ __func__, ret);
+ ret = -EIO;
+ } else {
+ dprintk(DBGLVL_ENC, "%s() Stopped\n", __func__);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int saa7164_encoder_acquire_port(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ int ret;
+
+ ret = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);
+ if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
+ printk(KERN_ERR "%s() acquire transition failed, ret = 0x%x\n",
+ __func__, ret);
+ ret = -EIO;
+ } else {
+ dprintk(DBGLVL_ENC, "%s() Acquired\n", __func__);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int saa7164_encoder_pause_port(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ int ret;
+
+ ret = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE);
+ if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
+ printk(KERN_ERR "%s() pause transition failed, ret = 0x%x\n",
+ __func__, ret);
+ ret = -EIO;
+ } else {
+ dprintk(DBGLVL_ENC, "%s() Paused\n", __func__);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+/* Firmware is very windows centric, meaning you have to transition
+ * the part through AVStream / KS Windows stages, forwards or backwards.
+ * States are: stopped, acquired (h/w), paused, started.
+ * We have to leave here will all of the soft buffers on the free list,
+ * else the cfg_post() func won't have soft buffers to correctly configure.
+ */
+static int saa7164_encoder_stop_streaming(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ struct saa7164_buffer *buf;
+ struct saa7164_user_buffer *ubuf;
+ struct list_head *c, *n;
+ int ret;
+
+ dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr);
+
+ ret = saa7164_encoder_pause_port(port);
+ ret = saa7164_encoder_acquire_port(port);
+ ret = saa7164_encoder_stop_port(port);
+
+ dprintk(DBGLVL_ENC, "%s(port=%d) Hardware stopped\n", __func__,
+ port->nr);
+
+ /* Reset the state of any allocated buffer resources */
+ mutex_lock(&port->dmaqueue_lock);
+
+ /* Reset the hard and soft buffer state */
+ list_for_each_safe(c, n, &port->dmaqueue.list) {
+ buf = list_entry(c, struct saa7164_buffer, list);
+ buf->flags = SAA7164_BUFFER_FREE;
+ buf->pos = 0;
+ }
+
+ list_for_each_safe(c, n, &port->list_buf_used.list) {
+ ubuf = list_entry(c, struct saa7164_user_buffer, list);
+ ubuf->pos = 0;
+ list_move_tail(&ubuf->list, &port->list_buf_free.list);
+ }
+
+ mutex_unlock(&port->dmaqueue_lock);
+
+ /* Free any allocated resources */
+ saa7164_encoder_buffers_dealloc(port);
+
+ dprintk(DBGLVL_ENC, "%s(port=%d) Released\n", __func__, port->nr);
+
+ return ret;
+}
+
+static int saa7164_encoder_start_streaming(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ int result, ret = 0;
+
+ dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr);
+
+ port->done_first_interrupt = 0;
+
+ /* allocate all of the PCIe DMA buffer resources on the fly,
+ * allowing switching between TS and PS payloads without
+ * requiring a complete driver reload.
+ */
+ saa7164_encoder_buffers_alloc(port);
+
+ /* Configure the encoder with any cache values */
+ saa7164_api_set_encoder(port);
+ saa7164_api_get_encoder(port);
+
+ /* Place the empty buffers on the hardware */
+ saa7164_buffer_cfg_port(port);
+
+ /* Acquire the hardware */
+ result = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);
+ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+ printk(KERN_ERR "%s() acquire transition failed, res = 0x%x\n",
+ __func__, result);
+
+ /* Stop the hardware, regardless */
+ result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
+ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+ printk(KERN_ERR "%s() acquire/forced stop transition "
+ "failed, res = 0x%x\n", __func__, result);
+ }
+ ret = -EIO;
+ goto out;
+ } else
+ dprintk(DBGLVL_ENC, "%s() Acquired\n", __func__);
+
+ /* Pause the hardware */
+ result = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE);
+ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+ printk(KERN_ERR "%s() pause transition failed, res = 0x%x\n",
+ __func__, result);
+
+ /* Stop the hardware, regardless */
+ result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
+ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+ printk(KERN_ERR "%s() pause/forced stop transition "
+ "failed, res = 0x%x\n", __func__, result);
+ }
+
+ ret = -EIO;
+ goto out;
+ } else
+ dprintk(DBGLVL_ENC, "%s() Paused\n", __func__);
+
+ /* Start the hardware */
+ result = saa7164_api_transition_port(port, SAA_DMASTATE_RUN);
+ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+ printk(KERN_ERR "%s() run transition failed, result = 0x%x\n",
+ __func__, result);
+
+ /* Stop the hardware, regardless */
+ result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
+ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+ printk(KERN_ERR "%s() run/forced stop transition "
+ "failed, res = 0x%x\n", __func__, result);
+ }
+
+ ret = -EIO;
+ } else
+ dprintk(DBGLVL_ENC, "%s() Running\n", __func__);
+
+out:
+ return ret;
+}
+
+static int fops_open(struct file *file)
+{
+ struct saa7164_dev *dev;
+ struct saa7164_port *port;
+ struct saa7164_encoder_fh *fh;
+
+ port = (struct saa7164_port *)video_get_drvdata(video_devdata(file));
+ if (!port)
+ return -ENODEV;
+
+ dev = port->dev;
+
+ dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+ /* allocate + initialize per filehandle data */
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (NULL == fh)
+ return -ENOMEM;
+
+ file->private_data = fh;
+ fh->port = port;
+
+ return 0;
+}
+
+static int fops_release(struct file *file)
+{
+ struct saa7164_encoder_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ struct saa7164_dev *dev = port->dev;
+
+ dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+ /* Shut device down on last close */
+ if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) {
+ if (atomic_dec_return(&port->v4l_reader_count) == 0) {
+ /* stop mpeg capture then cancel buffers */
+ saa7164_encoder_stop_streaming(port);
+ }
+ }
+
+ file->private_data = NULL;
+ kfree(fh);
+
+ return 0;
+}
+
+struct saa7164_user_buffer *saa7164_enc_next_buf(struct saa7164_port *port)
+{
+ struct saa7164_user_buffer *ubuf = NULL;
+ struct saa7164_dev *dev = port->dev;
+ u32 crc;
+
+ mutex_lock(&port->dmaqueue_lock);
+ if (!list_empty(&port->list_buf_used.list)) {
+ ubuf = list_first_entry(&port->list_buf_used.list,
+ struct saa7164_user_buffer, list);
+
+ if (crc_checking) {
+ crc = crc32(0, ubuf->data, ubuf->actual_size);
+ if (crc != ubuf->crc) {
+ printk(KERN_ERR
+ "%s() ubuf %p crc became invalid, was 0x%x became 0x%x\n",
+ __func__,
+ ubuf, ubuf->crc, crc);
+ }
+ }
+
+ }
+ mutex_unlock(&port->dmaqueue_lock);
+
+ dprintk(DBGLVL_ENC, "%s() returns %p\n", __func__, ubuf);
+
+ return ubuf;
+}
+
+static ssize_t fops_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *pos)
+{
+ struct saa7164_encoder_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ struct saa7164_user_buffer *ubuf = NULL;
+ struct saa7164_dev *dev = port->dev;
+ int ret = 0;
+ int rem, cnt;
+ u8 *p;
+
+ port->last_read_msecs_diff = port->last_read_msecs;
+ port->last_read_msecs = jiffies_to_msecs(jiffies);
+ port->last_read_msecs_diff = port->last_read_msecs -
+ port->last_read_msecs_diff;
+
+ saa7164_histogram_update(&port->read_interval,
+ port->last_read_msecs_diff);
+
+ if (*pos) {
+ printk(KERN_ERR "%s() ESPIPE\n", __func__);
+ return -ESPIPE;
+ }
+
+ if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
+ if (atomic_inc_return(&port->v4l_reader_count) == 1) {
+
+ if (saa7164_encoder_initialize(port) < 0) {
+ printk(KERN_ERR "%s() EINVAL\n", __func__);
+ return -EINVAL;
+ }
+
+ saa7164_encoder_start_streaming(port);
+ msleep(200);
+ }
+ }
+
+ /* blocking wait for buffer */
+ if ((file->f_flags & O_NONBLOCK) == 0) {
+ if (wait_event_interruptible(port->wait_read,
+ saa7164_enc_next_buf(port))) {
+ printk(KERN_ERR "%s() ERESTARTSYS\n", __func__);
+ return -ERESTARTSYS;
+ }
+ }
+
+ /* Pull the first buffer from the used list */
+ ubuf = saa7164_enc_next_buf(port);
+
+ while ((count > 0) && ubuf) {
+
+ /* set remaining bytes to copy */
+ rem = ubuf->actual_size - ubuf->pos;
+ cnt = rem > count ? count : rem;
+
+ p = ubuf->data + ubuf->pos;
+
+ dprintk(DBGLVL_ENC,
+ "%s() count=%d cnt=%d rem=%d buf=%p buf->pos=%d\n",
+ __func__, (int)count, cnt, rem, ubuf, ubuf->pos);
+
+ if (copy_to_user(buffer, p, cnt)) {
+ printk(KERN_ERR "%s() copy_to_user failed\n", __func__);
+ if (!ret) {
+ printk(KERN_ERR "%s() EFAULT\n", __func__);
+ ret = -EFAULT;
+ }
+ goto err;
+ }
+
+ ubuf->pos += cnt;
+ count -= cnt;
+ buffer += cnt;
+ ret += cnt;
+
+ if (ubuf->pos > ubuf->actual_size)
+ printk(KERN_ERR "read() pos > actual, huh?\n");
+
+ if (ubuf->pos == ubuf->actual_size) {
+
+ /* finished with current buffer, take next buffer */
+
+ /* Requeue the buffer on the free list */
+ ubuf->pos = 0;
+
+ mutex_lock(&port->dmaqueue_lock);
+ list_move_tail(&ubuf->list, &port->list_buf_free.list);
+ mutex_unlock(&port->dmaqueue_lock);
+
+ /* Dequeue next */
+ if ((file->f_flags & O_NONBLOCK) == 0) {
+ if (wait_event_interruptible(port->wait_read,
+ saa7164_enc_next_buf(port))) {
+ break;
+ }
+ }
+ ubuf = saa7164_enc_next_buf(port);
+ }
+ }
+err:
+ if (!ret && !ubuf)
+ ret = -EAGAIN;
+
+ return ret;
+}
+
+static unsigned int fops_poll(struct file *file, poll_table *wait)
+{
+ struct saa7164_encoder_fh *fh =
+ (struct saa7164_encoder_fh *)file->private_data;
+ struct saa7164_port *port = fh->port;
+ unsigned int mask = 0;
+
+ port->last_poll_msecs_diff = port->last_poll_msecs;
+ port->last_poll_msecs = jiffies_to_msecs(jiffies);
+ port->last_poll_msecs_diff = port->last_poll_msecs -
+ port->last_poll_msecs_diff;
+
+ saa7164_histogram_update(&port->poll_interval,
+ port->last_poll_msecs_diff);
+
+ if (!video_is_registered(port->v4l_device))
+ return -EIO;
+
+ if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
+ if (atomic_inc_return(&port->v4l_reader_count) == 1) {
+ if (saa7164_encoder_initialize(port) < 0)
+ return -EINVAL;
+ saa7164_encoder_start_streaming(port);
+ msleep(200);
+ }
+ }
+
+ /* blocking wait for buffer */
+ if ((file->f_flags & O_NONBLOCK) == 0) {
+ if (wait_event_interruptible(port->wait_read,
+ saa7164_enc_next_buf(port))) {
+ return -ERESTARTSYS;
+ }
+ }
+
+ /* Pull the first buffer from the used list */
+ if (!list_empty(&port->list_buf_used.list))
+ mask |= POLLIN | POLLRDNORM;
+
+ return mask;
+}
+
+static const struct v4l2_file_operations mpeg_fops = {
+ .owner = THIS_MODULE,
+ .open = fops_open,
+ .release = fops_release,
+ .read = fops_read,
+ .poll = fops_poll,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+int saa7164_g_chip_ident(struct file *file, void *fh,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct saa7164_port *port = ((struct saa7164_encoder_fh *)fh)->port;
+ struct saa7164_dev *dev = port->dev;
+ dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+ return 0;
+}
+
+int saa7164_g_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg)
+{
+ struct saa7164_port *port = ((struct saa7164_encoder_fh *)fh)->port;
+ struct saa7164_dev *dev = port->dev;
+ dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ return 0;
+}
+
+int saa7164_s_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg)
+{
+ struct saa7164_port *port = ((struct saa7164_encoder_fh *)fh)->port;
+ struct saa7164_dev *dev = port->dev;
+ dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls,
+ .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls,
+ .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_chip_ident = saa7164_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = saa7164_g_register,
+ .vidioc_s_register = saa7164_s_register,
+#endif
+};
+
+static struct video_device saa7164_mpeg_template = {
+ .name = "saa7164",
+ .fops = &mpeg_fops,
+ .ioctl_ops = &mpeg_ioctl_ops,
+ .minor = -1,
+ .tvnorms = SAA7164_NORMS,
+ .current_norm = V4L2_STD_NTSC_M,
+};
+
+static struct video_device *saa7164_encoder_alloc(
+ struct saa7164_port *port,
+ struct pci_dev *pci,
+ struct video_device *template,
+ char *type)
+{
+ struct video_device *vfd;
+ struct saa7164_dev *dev = port->dev;
+
+ dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+
+ *vfd = *template;
+ snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name,
+ type, saa7164_boards[dev->board].name);
+
+ vfd->parent = &pci->dev;
+ vfd->release = video_device_release;
+ return vfd;
+}
+
+int saa7164_encoder_register(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ int result = -ENODEV;
+
+ dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+ if (port->type != SAA7164_MPEG_ENCODER)
+ BUG();
+
+ /* Sanity check that the PCI configuration space is active */
+ if (port->hwcfg.BARLocation == 0) {
+ printk(KERN_ERR "%s() failed "
+ "(errno = %d), NO PCI configuration\n",
+ __func__, result);
+ result = -ENOMEM;
+ goto failed;
+ }
+
+ /* Establish encoder defaults here */
+ /* Set default TV standard */
+ port->encodernorm = saa7164_tvnorms[0];
+ port->width = 720;
+ port->mux_input = 1; /* Composite */
+ port->video_format = EU_VIDEO_FORMAT_MPEG_2;
+ port->audio_format = 0;
+ port->video_resolution = 0;
+ port->ctl_brightness = 127;
+ port->ctl_contrast = 66;
+ port->ctl_hue = 128;
+ port->ctl_saturation = 62;
+ port->ctl_sharpness = 8;
+ port->encoder_params.bitrate = ENCODER_DEF_BITRATE;
+ port->encoder_params.bitrate_peak = ENCODER_DEF_BITRATE;
+ port->encoder_params.bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR;
+ port->encoder_params.stream_type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS;
+ port->encoder_params.ctl_mute = 0;
+ port->encoder_params.ctl_aspect = V4L2_MPEG_VIDEO_ASPECT_4x3;
+ port->encoder_params.refdist = 1;
+ port->encoder_params.gop_size = SAA7164_ENCODER_DEFAULT_GOP_SIZE;
+
+ if (port->encodernorm.id & V4L2_STD_525_60)
+ port->height = 480;
+ else
+ port->height = 576;
+
+ /* Allocate and register the video device node */
+ port->v4l_device = saa7164_encoder_alloc(port,
+ dev->pci, &saa7164_mpeg_template, "mpeg");
+
+ if (!port->v4l_device) {
+ printk(KERN_INFO "%s: can't allocate mpeg device\n",
+ dev->name);
+ result = -ENOMEM;
+ goto failed;
+ }
+
+ video_set_drvdata(port->v4l_device, port);
+ result = video_register_device(port->v4l_device,
+ VFL_TYPE_GRABBER, -1);
+ if (result < 0) {
+ printk(KERN_INFO "%s: can't register mpeg device\n",
+ dev->name);
+ /* TODO: We're going to leak here if we don't dealloc
+ The buffers above. The unreg function can't deal wit it.
+ */
+ goto failed;
+ }
+
+ printk(KERN_INFO "%s: registered device video%d [mpeg]\n",
+ dev->name, port->v4l_device->num);
+
+ /* Configure the hardware defaults */
+ saa7164_api_set_videomux(port);
+ saa7164_api_set_usercontrol(port, PU_BRIGHTNESS_CONTROL);
+ saa7164_api_set_usercontrol(port, PU_CONTRAST_CONTROL);
+ saa7164_api_set_usercontrol(port, PU_HUE_CONTROL);
+ saa7164_api_set_usercontrol(port, PU_SATURATION_CONTROL);
+ saa7164_api_set_usercontrol(port, PU_SHARPNESS_CONTROL);
+ saa7164_api_audio_mute(port, 0);
+ saa7164_api_set_audio_volume(port, 20);
+ saa7164_api_set_aspect_ratio(port);
+
+ /* Disable audio standard detection, it's buggy */
+ saa7164_api_set_audio_detection(port, 0);
+
+ saa7164_api_set_encoder(port);
+ saa7164_api_get_encoder(port);
+
+ result = 0;
+failed:
+ return result;
+}
+
+void saa7164_encoder_unregister(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+
+ dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr);
+
+ if (port->type != SAA7164_MPEG_ENCODER)
+ BUG();
+
+ if (port->v4l_device) {
+ if (port->v4l_device->minor != -1)
+ video_unregister_device(port->v4l_device);
+ else
+ video_device_release(port->v4l_device);
+
+ port->v4l_device = NULL;
+ }
+
+ dprintk(DBGLVL_ENC, "%s(port=%d) done\n", __func__, port->nr);
+}
+
diff --git a/drivers/media/video/saa7164/saa7164-fw.c b/drivers/media/video/saa7164/saa7164-fw.c
new file mode 100644
index 00000000..a266bf01
--- /dev/null
+++ b/drivers/media/video/saa7164/saa7164-fw.c
@@ -0,0 +1,613 @@
+/*
+ * Driver for the NXP SAA7164 PCIe bridge
+ *
+ * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/firmware.h>
+#include <linux/slab.h>
+
+#include "saa7164.h"
+
+#define SAA7164_REV2_FIRMWARE "NXP7164-2010-03-10.1.fw"
+#define SAA7164_REV2_FIRMWARE_SIZE 4019072
+
+#define SAA7164_REV3_FIRMWARE "NXP7164-2010-03-10.1.fw"
+#define SAA7164_REV3_FIRMWARE_SIZE 4019072
+
+struct fw_header {
+ u32 firmwaresize;
+ u32 bslsize;
+ u32 reserved;
+ u32 version;
+};
+
+int saa7164_dl_wait_ack(struct saa7164_dev *dev, u32 reg)
+{
+ u32 timeout = SAA_DEVICE_TIMEOUT;
+ while ((saa7164_readl(reg) & 0x01) == 0) {
+ timeout -= 10;
+ if (timeout == 0) {
+ printk(KERN_ERR "%s() timeout (no d/l ack)\n",
+ __func__);
+ return -EBUSY;
+ }
+ msleep(100);
+ }
+
+ return 0;
+}
+
+int saa7164_dl_wait_clr(struct saa7164_dev *dev, u32 reg)
+{
+ u32 timeout = SAA_DEVICE_TIMEOUT;
+ while (saa7164_readl(reg) & 0x01) {
+ timeout -= 10;
+ if (timeout == 0) {
+ printk(KERN_ERR "%s() timeout (no d/l clr)\n",
+ __func__);
+ return -EBUSY;
+ }
+ msleep(100);
+ }
+
+ return 0;
+}
+
+/* TODO: move dlflags into dev-> and change to write/readl/b */
+/* TODO: Excessive levels of debug */
+int saa7164_downloadimage(struct saa7164_dev *dev, u8 *src, u32 srcsize,
+ u32 dlflags, u8 *dst, u32 dstsize)
+{
+ u32 reg, timeout, offset;
+ u8 *srcbuf = NULL;
+ int ret;
+
+ u32 dlflag = dlflags;
+ u32 dlflag_ack = dlflag + 4;
+ u32 drflag = dlflag_ack + 4;
+ u32 drflag_ack = drflag + 4;
+ u32 bleflag = drflag_ack + 4;
+
+ dprintk(DBGLVL_FW,
+ "%s(image=%p, size=%d, flags=0x%x, dst=%p, dstsize=0x%x)\n",
+ __func__, src, srcsize, dlflags, dst, dstsize);
+
+ if ((src == NULL) || (dst == NULL)) {
+ ret = -EIO;
+ goto out;
+ }
+
+ srcbuf = kzalloc(4 * 1048576, GFP_KERNEL);
+ if (NULL == srcbuf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (srcsize > (4*1048576)) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(srcbuf, src, srcsize);
+
+ dprintk(DBGLVL_FW, "%s() dlflag = 0x%x\n", __func__, dlflag);
+ dprintk(DBGLVL_FW, "%s() dlflag_ack = 0x%x\n", __func__, dlflag_ack);
+ dprintk(DBGLVL_FW, "%s() drflag = 0x%x\n", __func__, drflag);
+ dprintk(DBGLVL_FW, "%s() drflag_ack = 0x%x\n", __func__, drflag_ack);
+ dprintk(DBGLVL_FW, "%s() bleflag = 0x%x\n", __func__, bleflag);
+
+ reg = saa7164_readl(dlflag);
+ dprintk(DBGLVL_FW, "%s() dlflag (0x%x)= 0x%x\n", __func__, dlflag, reg);
+ if (reg == 1)
+ dprintk(DBGLVL_FW,
+ "%s() Download flag already set, please reboot\n",
+ __func__);
+
+ /* Indicate download start */
+ saa7164_writel(dlflag, 1);
+ ret = saa7164_dl_wait_ack(dev, dlflag_ack);
+ if (ret < 0)
+ goto out;
+
+ /* Ack download start, then wait for wait */
+ saa7164_writel(dlflag, 0);
+ ret = saa7164_dl_wait_clr(dev, dlflag_ack);
+ if (ret < 0)
+ goto out;
+
+ /* Deal with the raw firmware, in the appropriate chunk size */
+ for (offset = 0; srcsize > dstsize;
+ srcsize -= dstsize, offset += dstsize) {
+
+ dprintk(DBGLVL_FW, "%s() memcpy %d\n", __func__, dstsize);
+ memcpy(dst, srcbuf + offset, dstsize);
+
+ /* Flag the data as ready */
+ saa7164_writel(drflag, 1);
+ ret = saa7164_dl_wait_ack(dev, drflag_ack);
+ if (ret < 0)
+ goto out;
+
+ /* Wait for indication data was received */
+ saa7164_writel(drflag, 0);
+ ret = saa7164_dl_wait_clr(dev, drflag_ack);
+ if (ret < 0)
+ goto out;
+
+ }
+
+ dprintk(DBGLVL_FW, "%s() memcpy(l) %d\n", __func__, dstsize);
+ /* Write last block to the device */
+ memcpy(dst, srcbuf+offset, srcsize);
+
+ /* Flag the data as ready */
+ saa7164_writel(drflag, 1);
+ ret = saa7164_dl_wait_ack(dev, drflag_ack);
+ if (ret < 0)
+ goto out;
+
+ saa7164_writel(drflag, 0);
+ timeout = 0;
+ while (saa7164_readl(bleflag) != SAA_DEVICE_IMAGE_BOOTING) {
+ if (saa7164_readl(bleflag) & SAA_DEVICE_IMAGE_CORRUPT) {
+ printk(KERN_ERR "%s() image corrupt\n", __func__);
+ ret = -EBUSY;
+ goto out;
+ }
+
+ if (saa7164_readl(bleflag) & SAA_DEVICE_MEMORY_CORRUPT) {
+ printk(KERN_ERR "%s() device memory corrupt\n",
+ __func__);
+ ret = -EBUSY;
+ goto out;
+ }
+
+ msleep(10); /* Checkpatch throws a < 20ms warning */
+ if (timeout++ > 60)
+ break;
+ }
+
+ printk(KERN_INFO "%s() Image downloaded, booting...\n", __func__);
+
+ ret = saa7164_dl_wait_clr(dev, drflag_ack);
+ if (ret < 0)
+ goto out;
+
+ printk(KERN_INFO "%s() Image booted successfully.\n", __func__);
+ ret = 0;
+
+out:
+ kfree(srcbuf);
+ return ret;
+}
+
+/* TODO: Excessive debug */
+/* Load the firmware. Optionally it can be in ROM or newer versions
+ * can be on disk, saving the expense of the ROM hardware. */
+int saa7164_downloadfirmware(struct saa7164_dev *dev)
+{
+ /* u32 second_timeout = 60 * SAA_DEVICE_TIMEOUT; */
+ u32 tmp, filesize, version, err_flags, first_timeout, fwlength;
+ u32 second_timeout, updatebootloader = 1, bootloadersize = 0;
+ const struct firmware *fw = NULL;
+ struct fw_header *hdr, *boothdr = NULL, *fwhdr;
+ u32 bootloaderversion = 0, fwloadersize;
+ u8 *bootloaderoffset = NULL, *fwloaderoffset;
+ char *fwname;
+ int ret;
+
+ dprintk(DBGLVL_FW, "%s()\n", __func__);
+
+ if (saa7164_boards[dev->board].chiprev == SAA7164_CHIP_REV2) {
+ fwname = SAA7164_REV2_FIRMWARE;
+ fwlength = SAA7164_REV2_FIRMWARE_SIZE;
+ } else {
+ fwname = SAA7164_REV3_FIRMWARE;
+ fwlength = SAA7164_REV3_FIRMWARE_SIZE;
+ }
+
+ version = saa7164_getcurrentfirmwareversion(dev);
+
+ if (version == 0x00) {
+
+ second_timeout = 100;
+ first_timeout = 100;
+ err_flags = saa7164_readl(SAA_BOOTLOADERERROR_FLAGS);
+ dprintk(DBGLVL_FW, "%s() err_flags = %x\n",
+ __func__, err_flags);
+
+ while (err_flags != SAA_DEVICE_IMAGE_BOOTING) {
+ dprintk(DBGLVL_FW, "%s() err_flags = %x\n",
+ __func__, err_flags);
+ msleep(10); /* Checkpatch throws a < 20ms warning */
+
+ if (err_flags & SAA_DEVICE_IMAGE_CORRUPT) {
+ printk(KERN_ERR "%s() firmware corrupt\n",
+ __func__);
+ break;
+ }
+ if (err_flags & SAA_DEVICE_MEMORY_CORRUPT) {
+ printk(KERN_ERR "%s() device memory corrupt\n",
+ __func__);
+ break;
+ }
+ if (err_flags & SAA_DEVICE_NO_IMAGE) {
+ printk(KERN_ERR "%s() no first image\n",
+ __func__);
+ break;
+ }
+ if (err_flags & SAA_DEVICE_IMAGE_SEARCHING) {
+ first_timeout -= 10;
+ if (first_timeout == 0) {
+ printk(KERN_ERR
+ "%s() no first image\n",
+ __func__);
+ break;
+ }
+ } else if (err_flags & SAA_DEVICE_IMAGE_LOADING) {
+ second_timeout -= 10;
+ if (second_timeout == 0) {
+ printk(KERN_ERR
+ "%s() FW load time exceeded\n",
+ __func__);
+ break;
+ }
+ } else {
+ second_timeout -= 10;
+ if (second_timeout == 0) {
+ printk(KERN_ERR
+ "%s() Unknown bootloader flags 0x%x\n",
+ __func__, err_flags);
+ break;
+ }
+ }
+
+ err_flags = saa7164_readl(SAA_BOOTLOADERERROR_FLAGS);
+ } /* While != Booting */
+
+ if (err_flags == SAA_DEVICE_IMAGE_BOOTING) {
+ dprintk(DBGLVL_FW, "%s() Loader 1 has loaded.\n",
+ __func__);
+ first_timeout = SAA_DEVICE_TIMEOUT;
+ second_timeout = 60 * SAA_DEVICE_TIMEOUT;
+ second_timeout = 100;
+
+ err_flags = saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS);
+ dprintk(DBGLVL_FW, "%s() err_flags2 = %x\n",
+ __func__, err_flags);
+ while (err_flags != SAA_DEVICE_IMAGE_BOOTING) {
+ dprintk(DBGLVL_FW, "%s() err_flags2 = %x\n",
+ __func__, err_flags);
+ msleep(10); /* Checkpatch throws a < 20ms warning */
+
+ if (err_flags & SAA_DEVICE_IMAGE_CORRUPT) {
+ printk(KERN_ERR
+ "%s() firmware corrupt\n",
+ __func__);
+ break;
+ }
+ if (err_flags & SAA_DEVICE_MEMORY_CORRUPT) {
+ printk(KERN_ERR
+ "%s() device memory corrupt\n",
+ __func__);
+ break;
+ }
+ if (err_flags & SAA_DEVICE_NO_IMAGE) {
+ printk(KERN_ERR "%s() no first image\n",
+ __func__);
+ break;
+ }
+ if (err_flags & SAA_DEVICE_IMAGE_SEARCHING) {
+ first_timeout -= 10;
+ if (first_timeout == 0) {
+ printk(KERN_ERR
+ "%s() no second image\n",
+ __func__);
+ break;
+ }
+ } else if (err_flags &
+ SAA_DEVICE_IMAGE_LOADING) {
+ second_timeout -= 10;
+ if (second_timeout == 0) {
+ printk(KERN_ERR
+ "%s() FW load time exceeded\n",
+ __func__);
+ break;
+ }
+ } else {
+ second_timeout -= 10;
+ if (second_timeout == 0) {
+ printk(KERN_ERR
+ "%s() Unknown bootloader flags 0x%x\n",
+ __func__, err_flags);
+ break;
+ }
+ }
+
+ err_flags =
+ saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS);
+ } /* err_flags != SAA_DEVICE_IMAGE_BOOTING */
+
+ dprintk(DBGLVL_FW, "%s() Loader flags 1:0x%x 2:0x%x.\n",
+ __func__,
+ saa7164_readl(SAA_BOOTLOADERERROR_FLAGS),
+ saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS));
+
+ } /* err_flags == SAA_DEVICE_IMAGE_BOOTING */
+
+ /* It's possible for both firmwares to have booted,
+ * but that doesn't mean they've finished booting yet.
+ */
+ if ((saa7164_readl(SAA_BOOTLOADERERROR_FLAGS) ==
+ SAA_DEVICE_IMAGE_BOOTING) &&
+ (saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS) ==
+ SAA_DEVICE_IMAGE_BOOTING)) {
+
+
+ dprintk(DBGLVL_FW, "%s() Loader 2 has loaded.\n",
+ __func__);
+
+ first_timeout = SAA_DEVICE_TIMEOUT;
+ while (first_timeout) {
+ msleep(10); /* Checkpatch throws a < 20ms warning */
+
+ version =
+ saa7164_getcurrentfirmwareversion(dev);
+ if (version) {
+ dprintk(DBGLVL_FW,
+ "%s() All f/w loaded successfully\n",
+ __func__);
+ break;
+ } else {
+ first_timeout -= 10;
+ if (first_timeout == 0) {
+ printk(KERN_ERR
+ "%s() FW did not boot\n",
+ __func__);
+ break;
+ }
+ }
+ }
+ }
+ version = saa7164_getcurrentfirmwareversion(dev);
+ } /* version == 0 */
+
+ /* Has the firmware really booted? */
+ if ((saa7164_readl(SAA_BOOTLOADERERROR_FLAGS) ==
+ SAA_DEVICE_IMAGE_BOOTING) &&
+ (saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS) ==
+ SAA_DEVICE_IMAGE_BOOTING) && (version == 0)) {
+
+ printk(KERN_ERR
+ "%s() The firmware hung, probably bad firmware\n",
+ __func__);
+
+ /* Tell the second stage loader we have a deadlock */
+ saa7164_writel(SAA_DEVICE_DEADLOCK_DETECTED_OFFSET,
+ SAA_DEVICE_DEADLOCK_DETECTED);
+
+ saa7164_getfirmwarestatus(dev);
+
+ return -ENOMEM;
+ }
+
+ dprintk(DBGLVL_FW, "Device has Firmware Version %d.%d.%d.%d\n",
+ (version & 0x0000fc00) >> 10,
+ (version & 0x000003e0) >> 5,
+ (version & 0x0000001f),
+ (version & 0xffff0000) >> 16);
+
+ /* Load the firmwware from the disk if required */
+ if (version == 0) {
+
+ printk(KERN_INFO "%s() Waiting for firmware upload (%s)\n",
+ __func__, fwname);
+
+ ret = request_firmware(&fw, fwname, &dev->pci->dev);
+ if (ret) {
+ printk(KERN_ERR "%s() Upload failed. "
+ "(file not found?)\n", __func__);
+ return -ENOMEM;
+ }
+
+ printk(KERN_INFO "%s() firmware read %Zu bytes.\n",
+ __func__, fw->size);
+
+ if (fw->size != fwlength) {
+ printk(KERN_ERR "xc5000: firmware incorrect size\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ printk(KERN_INFO "%s() firmware loaded.\n", __func__);
+
+ hdr = (struct fw_header *)fw->data;
+ printk(KERN_INFO "Firmware file header part 1:\n");
+ printk(KERN_INFO " .FirmwareSize = 0x%x\n", hdr->firmwaresize);
+ printk(KERN_INFO " .BSLSize = 0x%x\n", hdr->bslsize);
+ printk(KERN_INFO " .Reserved = 0x%x\n", hdr->reserved);
+ printk(KERN_INFO " .Version = 0x%x\n", hdr->version);
+
+ /* Retrieve bootloader if reqd */
+ if ((hdr->firmwaresize == 0) && (hdr->bslsize == 0))
+ /* Second bootloader in the firmware file */
+ filesize = hdr->reserved * 16;
+ else
+ filesize = (hdr->firmwaresize + hdr->bslsize) *
+ 16 + sizeof(struct fw_header);
+
+ printk(KERN_INFO "%s() SecBootLoader.FileSize = %d\n",
+ __func__, filesize);
+
+ /* Get bootloader (if reqd) and firmware header */
+ if ((hdr->firmwaresize == 0) && (hdr->bslsize == 0)) {
+ /* Second boot loader is required */
+
+ /* Get the loader header */
+ boothdr = (struct fw_header *)(fw->data +
+ sizeof(struct fw_header));
+
+ bootloaderversion =
+ saa7164_readl(SAA_DEVICE_2ND_VERSION);
+ dprintk(DBGLVL_FW, "Onboard BootLoader:\n");
+ dprintk(DBGLVL_FW, "->Flag 0x%x\n",
+ saa7164_readl(SAA_BOOTLOADERERROR_FLAGS));
+ dprintk(DBGLVL_FW, "->Ack 0x%x\n",
+ saa7164_readl(SAA_DATAREADY_FLAG_ACK));
+ dprintk(DBGLVL_FW, "->FW Version 0x%x\n", version);
+ dprintk(DBGLVL_FW, "->Loader Version 0x%x\n",
+ bootloaderversion);
+
+ if ((saa7164_readl(SAA_BOOTLOADERERROR_FLAGS) ==
+ 0x03) && (saa7164_readl(SAA_DATAREADY_FLAG_ACK)
+ == 0x00) && (version == 0x00)) {
+
+ dprintk(DBGLVL_FW, "BootLoader version in "
+ "rom %d.%d.%d.%d\n",
+ (bootloaderversion & 0x0000fc00) >> 10,
+ (bootloaderversion & 0x000003e0) >> 5,
+ (bootloaderversion & 0x0000001f),
+ (bootloaderversion & 0xffff0000) >> 16
+ );
+ dprintk(DBGLVL_FW, "BootLoader version "
+ "in file %d.%d.%d.%d\n",
+ (boothdr->version & 0x0000fc00) >> 10,
+ (boothdr->version & 0x000003e0) >> 5,
+ (boothdr->version & 0x0000001f),
+ (boothdr->version & 0xffff0000) >> 16
+ );
+
+ if (bootloaderversion == boothdr->version)
+ updatebootloader = 0;
+ }
+
+ /* Calculate offset to firmware header */
+ tmp = (boothdr->firmwaresize + boothdr->bslsize) * 16 +
+ (sizeof(struct fw_header) +
+ sizeof(struct fw_header));
+
+ fwhdr = (struct fw_header *)(fw->data+tmp);
+ } else {
+ /* No second boot loader */
+ fwhdr = hdr;
+ }
+
+ dprintk(DBGLVL_FW, "Firmware version in file %d.%d.%d.%d\n",
+ (fwhdr->version & 0x0000fc00) >> 10,
+ (fwhdr->version & 0x000003e0) >> 5,
+ (fwhdr->version & 0x0000001f),
+ (fwhdr->version & 0xffff0000) >> 16
+ );
+
+ if (version == fwhdr->version) {
+ /* No download, firmware already on board */
+ ret = 0;
+ goto out;
+ }
+
+ if ((hdr->firmwaresize == 0) && (hdr->bslsize == 0)) {
+ if (updatebootloader) {
+ /* Get ready to upload the bootloader */
+ bootloadersize = (boothdr->firmwaresize +
+ boothdr->bslsize) * 16 +
+ sizeof(struct fw_header);
+
+ bootloaderoffset = (u8 *)(fw->data +
+ sizeof(struct fw_header));
+
+ dprintk(DBGLVL_FW, "bootloader d/l starts.\n");
+ printk(KERN_INFO "%s() FirmwareSize = 0x%x\n",
+ __func__, boothdr->firmwaresize);
+ printk(KERN_INFO "%s() BSLSize = 0x%x\n",
+ __func__, boothdr->bslsize);
+ printk(KERN_INFO "%s() Reserved = 0x%x\n",
+ __func__, boothdr->reserved);
+ printk(KERN_INFO "%s() Version = 0x%x\n",
+ __func__, boothdr->version);
+ ret = saa7164_downloadimage(
+ dev,
+ bootloaderoffset,
+ bootloadersize,
+ SAA_DOWNLOAD_FLAGS,
+ dev->bmmio + SAA_DEVICE_DOWNLOAD_OFFSET,
+ SAA_DEVICE_BUFFERBLOCKSIZE);
+ if (ret < 0) {
+ printk(KERN_ERR
+ "bootloader d/l has failed\n");
+ goto out;
+ }
+ dprintk(DBGLVL_FW,
+ "bootloader download complete.\n");
+
+ }
+
+ printk(KERN_ERR "starting firmware download(2)\n");
+ bootloadersize = (boothdr->firmwaresize +
+ boothdr->bslsize) * 16 +
+ sizeof(struct fw_header);
+
+ bootloaderoffset =
+ (u8 *)(fw->data + sizeof(struct fw_header));
+
+ fwloaderoffset = bootloaderoffset + bootloadersize;
+
+ /* TODO: fix this bounds overrun here with old f/ws */
+ fwloadersize = (fwhdr->firmwaresize + fwhdr->bslsize) *
+ 16 + sizeof(struct fw_header);
+
+ ret = saa7164_downloadimage(
+ dev,
+ fwloaderoffset,
+ fwloadersize,
+ SAA_DEVICE_2ND_DOWNLOADFLAG_OFFSET,
+ dev->bmmio + SAA_DEVICE_2ND_DOWNLOAD_OFFSET,
+ SAA_DEVICE_2ND_BUFFERBLOCKSIZE);
+ if (ret < 0) {
+ printk(KERN_ERR "firmware download failed\n");
+ goto out;
+ }
+ printk(KERN_ERR "firmware download complete.\n");
+
+ } else {
+
+ /* No bootloader update reqd, download firmware only */
+ printk(KERN_ERR "starting firmware download(3)\n");
+
+ ret = saa7164_downloadimage(
+ dev,
+ (u8 *)fw->data,
+ fw->size,
+ SAA_DOWNLOAD_FLAGS,
+ dev->bmmio + SAA_DEVICE_DOWNLOAD_OFFSET,
+ SAA_DEVICE_BUFFERBLOCKSIZE);
+ if (ret < 0) {
+ printk(KERN_ERR "firmware download failed\n");
+ goto out;
+ }
+ printk(KERN_ERR "firmware download complete.\n");
+ }
+ }
+
+ dev->firmwareloaded = 1;
+ ret = 0;
+
+out:
+ release_firmware(fw);
+ return ret;
+}
diff --git a/drivers/media/video/saa7164/saa7164-i2c.c b/drivers/media/video/saa7164/saa7164-i2c.c
new file mode 100644
index 00000000..26148f76
--- /dev/null
+++ b/drivers/media/video/saa7164/saa7164-i2c.c
@@ -0,0 +1,141 @@
+/*
+ * Driver for the NXP SAA7164 PCIe bridge
+ *
+ * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include "saa7164.h"
+
+static int i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
+{
+ struct saa7164_i2c *bus = i2c_adap->algo_data;
+ struct saa7164_dev *dev = bus->dev;
+ int i, retval = 0;
+
+ dprintk(DBGLVL_I2C, "%s(num = %d)\n", __func__, num);
+
+ for (i = 0 ; i < num; i++) {
+ dprintk(DBGLVL_I2C, "%s(num = %d) addr = 0x%02x len = 0x%x\n",
+ __func__, num, msgs[i].addr, msgs[i].len);
+ if (msgs[i].flags & I2C_M_RD) {
+ /* Unsupported - Yet*/
+ printk(KERN_ERR "%s() Unsupported - Yet\n", __func__);
+ continue;
+ } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) &&
+ msgs[i].addr == msgs[i + 1].addr) {
+ /* write then read from same address */
+
+ retval = saa7164_api_i2c_read(bus, msgs[i].addr,
+ msgs[i].len, msgs[i].buf,
+ msgs[i+1].len, msgs[i+1].buf
+ );
+
+ i++;
+
+ if (retval < 0)
+ goto err;
+ } else {
+ /* write */
+ retval = saa7164_api_i2c_write(bus, msgs[i].addr,
+ msgs[i].len, msgs[i].buf);
+ }
+ if (retval < 0)
+ goto err;
+ }
+ return num;
+
+err:
+ return retval;
+}
+
+void saa7164_call_i2c_clients(struct saa7164_i2c *bus, unsigned int cmd,
+ void *arg)
+{
+ if (bus->i2c_rc != 0)
+ return;
+
+ i2c_clients_command(&bus->i2c_adap, cmd, arg);
+}
+
+static u32 saa7164_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm saa7164_i2c_algo_template = {
+ .master_xfer = i2c_xfer,
+ .functionality = saa7164_functionality,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static struct i2c_adapter saa7164_i2c_adap_template = {
+ .name = "saa7164",
+ .owner = THIS_MODULE,
+ .algo = &saa7164_i2c_algo_template,
+};
+
+static struct i2c_client saa7164_i2c_client_template = {
+ .name = "saa7164 internal",
+};
+
+int saa7164_i2c_register(struct saa7164_i2c *bus)
+{
+ struct saa7164_dev *dev = bus->dev;
+
+ dprintk(DBGLVL_I2C, "%s(bus = %d)\n", __func__, bus->nr);
+
+ memcpy(&bus->i2c_adap, &saa7164_i2c_adap_template,
+ sizeof(bus->i2c_adap));
+
+ memcpy(&bus->i2c_algo, &saa7164_i2c_algo_template,
+ sizeof(bus->i2c_algo));
+
+ memcpy(&bus->i2c_client, &saa7164_i2c_client_template,
+ sizeof(bus->i2c_client));
+
+ bus->i2c_adap.dev.parent = &dev->pci->dev;
+
+ strlcpy(bus->i2c_adap.name, bus->dev->name,
+ sizeof(bus->i2c_adap.name));
+
+ bus->i2c_algo.data = bus;
+ bus->i2c_adap.algo_data = bus;
+ i2c_set_adapdata(&bus->i2c_adap, bus);
+ i2c_add_adapter(&bus->i2c_adap);
+
+ bus->i2c_client.adapter = &bus->i2c_adap;
+
+ if (0 != bus->i2c_rc)
+ printk(KERN_ERR "%s: i2c bus %d register FAILED\n",
+ dev->name, bus->nr);
+
+ return bus->i2c_rc;
+}
+
+int saa7164_i2c_unregister(struct saa7164_i2c *bus)
+{
+ i2c_del_adapter(&bus->i2c_adap);
+ return 0;
+}
diff --git a/drivers/media/video/saa7164/saa7164-reg.h b/drivers/media/video/saa7164/saa7164-reg.h
new file mode 100644
index 00000000..2bbf8158
--- /dev/null
+++ b/drivers/media/video/saa7164/saa7164-reg.h
@@ -0,0 +1,219 @@
+/*
+ * Driver for the NXP SAA7164 PCIe bridge
+ *
+ * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* TODO: Retest the driver with errors expressed as negatives */
+
+/* Result codes */
+#define SAA_OK 0
+#define SAA_ERR_BAD_PARAMETER 0x09
+#define SAA_ERR_NO_RESOURCES 0x0c
+#define SAA_ERR_NOT_SUPPORTED 0x13
+#define SAA_ERR_BUSY 0x15
+#define SAA_ERR_READ 0x17
+#define SAA_ERR_TIMEOUT 0x1f
+#define SAA_ERR_OVERFLOW 0x20
+#define SAA_ERR_EMPTY 0x22
+#define SAA_ERR_NOT_STARTED 0x23
+#define SAA_ERR_ALREADY_STARTED 0x24
+#define SAA_ERR_NOT_STOPPED 0x25
+#define SAA_ERR_ALREADY_STOPPED 0x26
+#define SAA_ERR_INVALID_COMMAND 0x3e
+#define SAA_ERR_NULL_PACKET 0x59
+
+/* Errors and flags from the silicon */
+#define PVC_ERRORCODE_UNKNOWN 0x00
+#define PVC_ERRORCODE_INVALID_COMMAND 0x01
+#define PVC_ERRORCODE_INVALID_CONTROL 0x02
+#define PVC_ERRORCODE_INVALID_DATA 0x03
+#define PVC_ERRORCODE_TIMEOUT 0x04
+#define PVC_ERRORCODE_NAK 0x05
+#define PVC_RESPONSEFLAG_ERROR 0x01
+#define PVC_RESPONSEFLAG_OVERFLOW 0x02
+#define PVC_RESPONSEFLAG_RESET 0x04
+#define PVC_RESPONSEFLAG_INTERFACE 0x08
+#define PVC_RESPONSEFLAG_CONTINUED 0x10
+#define PVC_CMDFLAG_INTERRUPT 0x02
+#define PVC_CMDFLAG_INTERFACE 0x04
+#define PVC_CMDFLAG_SERIALIZE 0x08
+#define PVC_CMDFLAG_CONTINUE 0x10
+
+/* Silicon Commands */
+#define GET_DESCRIPTORS_CONTROL 0x01
+#define GET_STRING_CONTROL 0x03
+#define GET_LANGUAGE_CONTROL 0x05
+#define SET_POWER_CONTROL 0x07
+#define GET_FW_STATUS_CONTROL 0x08
+#define GET_FW_VERSION_CONTROL 0x09
+#define SET_DEBUG_LEVEL_CONTROL 0x0B
+#define GET_DEBUG_DATA_CONTROL 0x0C
+#define GET_PRODUCTION_INFO_CONTROL 0x0D
+
+/* cmd defines */
+#define SAA_CMDFLAG_CONTINUE 0x10
+#define SAA_CMD_MAX_MSG_UNITS 256
+
+/* Some defines */
+#define SAA_BUS_TIMEOUT 50
+#define SAA_DEVICE_TIMEOUT 5000
+#define SAA_DEVICE_MAXREQUESTSIZE 256
+
+/* Register addresses */
+#define SAA_DEVICE_VERSION 0x30
+#define SAA_DOWNLOAD_FLAGS 0x34
+#define SAA_DOWNLOAD_FLAG 0x34
+#define SAA_DOWNLOAD_FLAG_ACK 0x38
+#define SAA_DATAREADY_FLAG 0x3C
+#define SAA_DATAREADY_FLAG_ACK 0x40
+
+/* Boot loader register and bit definitions */
+#define SAA_BOOTLOADERERROR_FLAGS 0x44
+#define SAA_DEVICE_IMAGE_SEARCHING 0x01
+#define SAA_DEVICE_IMAGE_LOADING 0x02
+#define SAA_DEVICE_IMAGE_BOOTING 0x03
+#define SAA_DEVICE_IMAGE_CORRUPT 0x04
+#define SAA_DEVICE_MEMORY_CORRUPT 0x08
+#define SAA_DEVICE_NO_IMAGE 0x10
+
+/* Register addresses */
+#define SAA_DEVICE_2ND_VERSION 0x50
+#define SAA_DEVICE_2ND_DOWNLOADFLAG_OFFSET 0x54
+
+/* Register addresses */
+#define SAA_SECONDSTAGEERROR_FLAGS 0x64
+
+/* Bootloader regs and flags */
+#define SAA_DEVICE_DEADLOCK_DETECTED_OFFSET 0x6C
+#define SAA_DEVICE_DEADLOCK_DETECTED 0xDEADDEAD
+
+/* Basic firmware status registers */
+#define SAA_DEVICE_SYSINIT_STATUS_OFFSET 0x70
+#define SAA_DEVICE_SYSINIT_STATUS 0x70
+#define SAA_DEVICE_SYSINIT_MODE 0x74
+#define SAA_DEVICE_SYSINIT_SPEC 0x78
+#define SAA_DEVICE_SYSINIT_INST 0x7C
+#define SAA_DEVICE_SYSINIT_CPULOAD 0x80
+#define SAA_DEVICE_SYSINIT_REMAINHEAP 0x84
+
+#define SAA_DEVICE_DOWNLOAD_OFFSET 0x1000
+#define SAA_DEVICE_BUFFERBLOCKSIZE 0x1000
+
+#define SAA_DEVICE_2ND_BUFFERBLOCKSIZE 0x100000
+#define SAA_DEVICE_2ND_DOWNLOAD_OFFSET 0x200000
+
+/* Descriptors */
+#define CS_INTERFACE 0x24
+
+/* Descriptor subtypes */
+#define VC_INPUT_TERMINAL 0x02
+#define VC_OUTPUT_TERMINAL 0x03
+#define VC_SELECTOR_UNIT 0x04
+#define VC_PROCESSING_UNIT 0x05
+#define FEATURE_UNIT 0x06
+#define TUNER_UNIT 0x09
+#define ENCODER_UNIT 0x0A
+#define EXTENSION_UNIT 0x0B
+#define VC_TUNER_PATH 0xF0
+#define PVC_HARDWARE_DESCRIPTOR 0xF1
+#define PVC_INTERFACE_DESCRIPTOR 0xF2
+#define PVC_INFRARED_UNIT 0xF3
+#define DRM_UNIT 0xF4
+#define GENERAL_REQUEST 0xF5
+
+/* Format Types */
+#define VS_FORMAT_TYPE 0x02
+#define VS_FORMAT_TYPE_I 0x01
+#define VS_FORMAT_UNCOMPRESSED 0x04
+#define VS_FRAME_UNCOMPRESSED 0x05
+#define VS_FORMAT_MPEG2PS 0x09
+#define VS_FORMAT_MPEG2TS 0x0A
+#define VS_FORMAT_MPEG4SL 0x0B
+#define VS_FORMAT_WM9 0x0C
+#define VS_FORMAT_DIVX 0x0D
+#define VS_FORMAT_VBI 0x0E
+#define VS_FORMAT_RDS 0x0F
+
+/* Device extension commands */
+#define EXU_REGISTER_ACCESS_CONTROL 0x00
+#define EXU_GPIO_CONTROL 0x01
+#define EXU_GPIO_GROUP_CONTROL 0x02
+#define EXU_INTERRUPT_CONTROL 0x03
+
+/* State Transition and args */
+#define SAA_PROBE_CONTROL 0x01
+#define SAA_COMMIT_CONTROL 0x02
+#define SAA_STATE_CONTROL 0x03
+#define SAA_DMASTATE_STOP 0x00
+#define SAA_DMASTATE_ACQUIRE 0x01
+#define SAA_DMASTATE_PAUSE 0x02
+#define SAA_DMASTATE_RUN 0x03
+
+/* A/V Mux Input Selector */
+#define SU_INPUT_SELECT_CONTROL 0x01
+
+/* Encoder Profiles */
+#define EU_PROFILE_PS_DVD 0x06
+#define EU_PROFILE_TS_HQ 0x09
+#define EU_VIDEO_FORMAT_MPEG_2 0x02
+
+/* Tuner */
+#define TU_AUDIO_MODE_CONTROL 0x17
+
+/* Video Formats */
+#define TU_STANDARD_CONTROL 0x00
+#define TU_STANDARD_AUTO_CONTROL 0x01
+#define TU_STANDARD_NONE 0x00
+#define TU_STANDARD_NTSC_M 0x01
+#define TU_STANDARD_PAL_I 0x08
+#define TU_STANDARD_MANUAL 0x00
+#define TU_STANDARD_AUTO 0x01
+
+/* Video Controls */
+#define PU_BRIGHTNESS_CONTROL 0x02
+#define PU_CONTRAST_CONTROL 0x03
+#define PU_HUE_CONTROL 0x06
+#define PU_SATURATION_CONTROL 0x07
+#define PU_SHARPNESS_CONTROL 0x08
+
+/* Audio Controls */
+#define MUTE_CONTROL 0x01
+#define VOLUME_CONTROL 0x02
+#define AUDIO_DEFAULT_CONTROL 0x0D
+
+/* Default Volume Levels */
+#define TMHW_LEV_ADJ_DECLEV_DEFAULT 0x00
+#define TMHW_LEV_ADJ_MONOLEV_DEFAULT 0x00
+#define TMHW_LEV_ADJ_NICLEV_DEFAULT 0x00
+#define TMHW_LEV_ADJ_SAPLEV_DEFAULT 0x00
+#define TMHW_LEV_ADJ_ADCLEV_DEFAULT 0x00
+
+/* Encoder Related Commands */
+#define EU_PROFILE_CONTROL 0x00
+#define EU_VIDEO_FORMAT_CONTROL 0x01
+#define EU_VIDEO_BIT_RATE_CONTROL 0x02
+#define EU_VIDEO_RESOLUTION_CONTROL 0x03
+#define EU_VIDEO_GOP_STRUCTURE_CONTROL 0x04
+#define EU_VIDEO_INPUT_ASPECT_CONTROL 0x0A
+#define EU_AUDIO_FORMAT_CONTROL 0x0C
+#define EU_AUDIO_BIT_RATE_CONTROL 0x0D
+
+/* Firmware Debugging */
+#define SET_DEBUG_LEVEL_CONTROL 0x0B
+#define GET_DEBUG_DATA_CONTROL 0x0C
diff --git a/drivers/media/video/saa7164/saa7164-types.h b/drivers/media/video/saa7164/saa7164-types.h
new file mode 100644
index 00000000..1d2140a3
--- /dev/null
+++ b/drivers/media/video/saa7164/saa7164-types.h
@@ -0,0 +1,442 @@
+/*
+ * Driver for the NXP SAA7164 PCIe bridge
+ *
+ * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* TODO: Cleanup and shorten the namespace */
+
+/* Some structues are passed directly to/from the firmware and
+ * have strict alignment requirements. This is one of them.
+ */
+struct tmComResHWDescr {
+ u8 bLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype;
+ u16 bcdSpecVersion;
+ u32 dwClockFrequency;
+ u32 dwClockUpdateRes;
+ u8 bCapabilities;
+ u32 dwDeviceRegistersLocation;
+ u32 dwHostMemoryRegion;
+ u32 dwHostMemoryRegionSize;
+ u32 dwHostHibernatMemRegion;
+ u32 dwHostHibernatMemRegionSize;
+} __attribute__((packed));
+
+/* This is DWORD aligned on windows but I can't find the right
+ * gcc syntax to match the binary data from the device.
+ * I've manually padded with Reserved[3] bytes to match the hardware,
+ * but this could break if GCC decies to pack in a different way.
+ */
+struct tmComResInterfaceDescr {
+ u8 bLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype;
+ u8 bFlags;
+ u8 bInterfaceType;
+ u8 bInterfaceId;
+ u8 bBaseInterface;
+ u8 bInterruptId;
+ u8 bDebugInterruptId;
+ u8 BARLocation;
+ u8 Reserved[3];
+};
+
+struct tmComResBusDescr {
+ u64 CommandRing;
+ u64 ResponseRing;
+ u32 CommandWrite;
+ u32 CommandRead;
+ u32 ResponseWrite;
+ u32 ResponseRead;
+};
+
+enum tmBusType {
+ NONE = 0,
+ TYPE_BUS_PCI = 1,
+ TYPE_BUS_PCIe = 2,
+ TYPE_BUS_USB = 3,
+ TYPE_BUS_I2C = 4
+};
+
+struct tmComResBusInfo {
+ enum tmBusType Type;
+ u16 m_wMaxReqSize;
+ u8 *m_pdwSetRing;
+ u32 m_dwSizeSetRing;
+ u8 *m_pdwGetRing;
+ u32 m_dwSizeGetRing;
+ u32 m_dwSetWritePos;
+ u32 m_dwSetReadPos;
+ u32 m_dwGetWritePos;
+ u32 m_dwGetReadPos;
+
+ /* All access is protected */
+ struct mutex lock;
+
+};
+
+struct tmComResInfo {
+ u8 id;
+ u8 flags;
+ u16 size;
+ u32 command;
+ u16 controlselector;
+ u8 seqno;
+} __attribute__((packed));
+
+enum tmComResCmd {
+ SET_CUR = 0x01,
+ GET_CUR = 0x81,
+ GET_MIN = 0x82,
+ GET_MAX = 0x83,
+ GET_RES = 0x84,
+ GET_LEN = 0x85,
+ GET_INFO = 0x86,
+ GET_DEF = 0x87
+};
+
+struct cmd {
+ u8 seqno;
+ u32 inuse;
+ u32 timeout;
+ u32 signalled;
+ struct mutex lock;
+ wait_queue_head_t wait;
+};
+
+struct tmDescriptor {
+ u32 pathid;
+ u32 size;
+ void *descriptor;
+};
+
+struct tmComResDescrHeader {
+ u8 len;
+ u8 type;
+ u8 subtype;
+ u8 unitid;
+} __attribute__((packed));
+
+struct tmComResExtDevDescrHeader {
+ u8 len;
+ u8 type;
+ u8 subtype;
+ u8 unitid;
+ u32 devicetype;
+ u16 deviceid;
+ u32 numgpiopins;
+ u8 numgpiogroups;
+ u8 controlsize;
+} __attribute__((packed));
+
+struct tmComResGPIO {
+ u32 pin;
+ u8 state;
+} __attribute__((packed));
+
+struct tmComResPathDescrHeader {
+ u8 len;
+ u8 type;
+ u8 subtype;
+ u8 pathid;
+} __attribute__((packed));
+
+/* terminaltype */
+enum tmComResTermType {
+ ITT_ANTENNA = 0x0203,
+ LINE_CONNECTOR = 0x0603,
+ SPDIF_CONNECTOR = 0x0605,
+ COMPOSITE_CONNECTOR = 0x0401,
+ SVIDEO_CONNECTOR = 0x0402,
+ COMPONENT_CONNECTOR = 0x0403,
+ STANDARD_DMA = 0xF101
+};
+
+struct tmComResAntTermDescrHeader {
+ u8 len;
+ u8 type;
+ u8 subtype;
+ u8 terminalid;
+ u16 terminaltype;
+ u8 assocterminal;
+ u8 iterminal;
+ u8 controlsize;
+} __attribute__((packed));
+
+struct tmComResTunerDescrHeader {
+ u8 len;
+ u8 type;
+ u8 subtype;
+ u8 unitid;
+ u8 sourceid;
+ u8 iunit;
+ u32 tuningstandards;
+ u8 controlsize;
+ u32 controls;
+} __attribute__((packed));
+
+enum tmBufferFlag {
+ /* the buffer does not contain any valid data */
+ TM_BUFFER_FLAG_EMPTY,
+
+ /* the buffer is filled with valid data */
+ TM_BUFFER_FLAG_DONE,
+
+ /* the buffer is the dummy buffer - TODO??? */
+ TM_BUFFER_FLAG_DUMMY_BUFFER
+};
+
+struct tmBuffer {
+ u64 *pagetablevirt;
+ u64 pagetablephys;
+ u16 offset;
+ u8 *context;
+ u64 timestamp;
+ enum tmBufferFlag BufferFlag;
+ u32 lostbuffers;
+ u32 validbuffers;
+ u64 *dummypagevirt;
+ u64 dummypagephys;
+ u64 *addressvirt;
+};
+
+struct tmHWStreamParameters {
+ u32 bitspersample;
+ u32 samplesperline;
+ u32 numberoflines;
+ u32 pitch;
+ u32 linethreshold;
+ u64 **pagetablelistvirt;
+ u64 *pagetablelistphys;
+ u32 numpagetables;
+ u32 numpagetableentries;
+};
+
+struct tmStreamParameters {
+ struct tmHWStreamParameters HWStreamParameters;
+ u64 qwDummyPageTablePhys;
+ u64 *pDummyPageTableVirt;
+};
+
+struct tmComResDMATermDescrHeader {
+ u8 len;
+ u8 type;
+ u8 subtyle;
+ u8 unitid;
+ u16 terminaltype;
+ u8 assocterminal;
+ u8 sourceid;
+ u8 iterminal;
+ u32 BARLocation;
+ u8 flags;
+ u8 interruptid;
+ u8 buffercount;
+ u8 metadatasize;
+ u8 numformats;
+ u8 controlsize;
+} __attribute__((packed));
+
+/*
+ *
+ * Description:
+ * This is the transport stream format header.
+ *
+ * Settings:
+ * bLength - The size of this descriptor in bytes.
+ * bDescriptorType - CS_INTERFACE.
+ * bDescriptorSubtype - VS_FORMAT_MPEG2TS descriptor subtype.
+ * bFormatIndex - A non-zero constant that uniquely identifies the
+ * format.
+ * bDataOffset - Offset to TSP packet within MPEG-2 TS transport
+ * stride, in bytes.
+ * bPacketLength - Length of TSP packet, in bytes (typically 188).
+ * bStrideLength - Length of MPEG-2 TS transport stride.
+ * guidStrideFormat - A Globally Unique Identifier indicating the
+ * format of the stride data (if any). Set to zeros
+ * if there is no Stride Data, or if the Stride
+ * Data is to be ignored by the application.
+ *
+ */
+struct tmComResTSFormatDescrHeader {
+ u8 len;
+ u8 type;
+ u8 subtype;
+ u8 bFormatIndex;
+ u8 bDataOffset;
+ u8 bPacketLength;
+ u8 bStrideLength;
+ u8 guidStrideFormat[16];
+} __attribute__((packed));
+
+/* Encoder related structures */
+
+/* A/V Mux Selector */
+struct tmComResSelDescrHeader {
+ u8 len;
+ u8 type;
+ u8 subtype;
+ u8 unitid;
+ u8 nrinpins;
+ u8 sourceid;
+} __attribute__((packed));
+
+/* A/V Audio processor definitions */
+struct tmComResProcDescrHeader {
+ u8 len;
+ u8 type;
+ u8 subtype;
+ u8 unitid;
+ u8 sourceid;
+ u16 wreserved;
+ u8 controlsize;
+} __attribute__((packed));
+
+/* Video bitrate control message */
+#define EU_VIDEO_BIT_RATE_MODE_CONSTANT (0)
+#define EU_VIDEO_BIT_RATE_MODE_VARIABLE_AVERAGE (1)
+#define EU_VIDEO_BIT_RATE_MODE_VARIABLE_PEAK (2)
+struct tmComResEncVideoBitRate {
+ u8 ucVideoBitRateMode;
+ u32 dwVideoBitRate;
+ u32 dwVideoBitRatePeak;
+} __attribute__((packed));
+
+/* Video Encoder Aspect Ratio message */
+struct tmComResEncVideoInputAspectRatio {
+ u8 width;
+ u8 height;
+} __attribute__((packed));
+
+/* Video Encoder GOP IBP message */
+/* 1. IPPPPPPPPPPPPPP */
+/* 2. IBPBPBPBPBPBPBP */
+/* 3. IBBPBBPBBPBBP */
+#define SAA7164_ENCODER_DEFAULT_GOP_DIST (1)
+#define SAA7164_ENCODER_DEFAULT_GOP_SIZE (15)
+struct tmComResEncVideoGopStructure {
+ u8 ucGOPSize; /* GOP Size 12, 15 */
+ u8 ucRefFrameDist; /* Reference Frame Distance */
+} __attribute__((packed));
+
+/* Encoder processor definition */
+struct tmComResEncoderDescrHeader {
+ u8 len;
+ u8 type;
+ u8 subtype;
+ u8 unitid;
+ u8 vsourceid;
+ u8 asourceid;
+ u8 iunit;
+ u32 dwmControlCap;
+ u32 dwmProfileCap;
+ u32 dwmVidFormatCap;
+ u8 bmVidBitrateCap;
+ u16 wmVidResolutionsCap;
+ u16 wmVidFrmRateCap;
+ u32 dwmAudFormatCap;
+ u8 bmAudBitrateCap;
+} __attribute__((packed));
+
+/* Audio processor definition */
+struct tmComResAFeatureDescrHeader {
+ u8 len;
+ u8 type;
+ u8 subtype;
+ u8 unitid;
+ u8 sourceid;
+ u8 controlsize;
+} __attribute__((packed));
+
+/* Audio control messages */
+struct tmComResAudioDefaults {
+ u8 ucDecoderLevel;
+ u8 ucDecoderFM_Level;
+ u8 ucMonoLevel;
+ u8 ucNICAM_Level;
+ u8 ucSAP_Level;
+ u8 ucADC_Level;
+} __attribute__((packed));
+
+/* Audio bitrate control message */
+struct tmComResEncAudioBitRate {
+ u8 ucAudioBitRateMode;
+ u32 dwAudioBitRate;
+ u32 dwAudioBitRatePeak;
+} __attribute__((packed));
+
+/* Tuner / AV Decoder messages */
+struct tmComResTunerStandard {
+ u8 std;
+ u32 country;
+} __attribute__((packed));
+
+struct tmComResTunerStandardAuto {
+ u8 mode;
+} __attribute__((packed));
+
+/* EEPROM definition for PS stream types */
+struct tmComResPSFormatDescrHeader {
+ u8 len;
+ u8 type;
+ u8 subtype;
+ u8 bFormatIndex;
+ u16 wPacketLength;
+ u16 wPackLength;
+ u8 bPackDataType;
+} __attribute__((packed));
+
+/* VBI control structure */
+struct tmComResVBIFormatDescrHeader {
+ u8 len;
+ u8 type;
+ u8 subtype; /* VS_FORMAT_VBI */
+ u8 bFormatIndex;
+ u32 VideoStandard; /* See KS_AnalogVideoStandard, NTSC = 1 */
+ u8 StartLine; /* NTSC Start = 10 */
+ u8 EndLine; /* NTSC = 21 */
+ u8 FieldRate; /* 60 for NTSC */
+ u8 bNumLines; /* Unused - scheduled for removal */
+} __attribute__((packed));
+
+struct tmComResProbeCommit {
+ u16 bmHint;
+ u8 bFormatIndex;
+ u8 bFrameIndex;
+} __attribute__((packed));
+
+struct tmComResDebugSetLevel {
+ u32 dwDebugLevel;
+} __attribute__((packed));
+
+struct tmComResDebugGetData {
+ u32 dwResult;
+ u8 ucDebugData[256];
+} __attribute__((packed));
+
+struct tmFwInfoStruct {
+ u32 status;
+ u32 mode;
+ u32 devicespec;
+ u32 deviceinst;
+ u32 CPULoad;
+ u32 RemainHeap;
+ u32 CPUClock;
+ u32 RAMSpeed;
+} __attribute__((packed));
diff --git a/drivers/media/video/saa7164/saa7164-vbi.c b/drivers/media/video/saa7164/saa7164-vbi.c
new file mode 100644
index 00000000..273cf807
--- /dev/null
+++ b/drivers/media/video/saa7164/saa7164-vbi.c
@@ -0,0 +1,1374 @@
+/*
+ * Driver for the NXP SAA7164 PCIe bridge
+ *
+ * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "saa7164.h"
+
+static struct saa7164_tvnorm saa7164_tvnorms[] = {
+ {
+ .name = "NTSC-M",
+ .id = V4L2_STD_NTSC_M,
+ }, {
+ .name = "NTSC-JP",
+ .id = V4L2_STD_NTSC_M_JP,
+ }
+};
+
+static const u32 saa7164_v4l2_ctrls[] = {
+ 0
+};
+
+/* Take the encoder configuration from the port struct and
+ * flush it to the hardware.
+ */
+static void saa7164_vbi_configure(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ dprintk(DBGLVL_VBI, "%s()\n", __func__);
+
+ port->vbi_params.width = port->width;
+ port->vbi_params.height = port->height;
+ port->vbi_params.is_50hz =
+ (port->encodernorm.id & V4L2_STD_625_50) != 0;
+
+ /* Set up the DIF (enable it) for analog mode by default */
+ saa7164_api_initialize_dif(port);
+
+ /* Configure the correct video standard */
+#if 0
+ saa7164_api_configure_dif(port, port->encodernorm.id);
+#endif
+
+#if 0
+ /* Ensure the audio decoder is correct configured */
+ saa7164_api_set_audio_std(port);
+#endif
+ dprintk(DBGLVL_VBI, "%s() ends\n", __func__);
+}
+
+static int saa7164_vbi_buffers_dealloc(struct saa7164_port *port)
+{
+ struct list_head *c, *n, *p, *q, *l, *v;
+ struct saa7164_dev *dev = port->dev;
+ struct saa7164_buffer *buf;
+ struct saa7164_user_buffer *ubuf;
+
+ /* Remove any allocated buffers */
+ mutex_lock(&port->dmaqueue_lock);
+
+ dprintk(DBGLVL_VBI, "%s(port=%d) dmaqueue\n", __func__, port->nr);
+ list_for_each_safe(c, n, &port->dmaqueue.list) {
+ buf = list_entry(c, struct saa7164_buffer, list);
+ list_del(c);
+ saa7164_buffer_dealloc(buf);
+ }
+
+ dprintk(DBGLVL_VBI, "%s(port=%d) used\n", __func__, port->nr);
+ list_for_each_safe(p, q, &port->list_buf_used.list) {
+ ubuf = list_entry(p, struct saa7164_user_buffer, list);
+ list_del(p);
+ saa7164_buffer_dealloc_user(ubuf);
+ }
+
+ dprintk(DBGLVL_VBI, "%s(port=%d) free\n", __func__, port->nr);
+ list_for_each_safe(l, v, &port->list_buf_free.list) {
+ ubuf = list_entry(l, struct saa7164_user_buffer, list);
+ list_del(l);
+ saa7164_buffer_dealloc_user(ubuf);
+ }
+
+ mutex_unlock(&port->dmaqueue_lock);
+ dprintk(DBGLVL_VBI, "%s(port=%d) done\n", __func__, port->nr);
+
+ return 0;
+}
+
+/* Dynamic buffer switch at vbi start time */
+static int saa7164_vbi_buffers_alloc(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ struct saa7164_buffer *buf;
+ struct saa7164_user_buffer *ubuf;
+ struct tmHWStreamParameters *params = &port->hw_streamingparams;
+ int result = -ENODEV, i;
+ int len = 0;
+
+ dprintk(DBGLVL_VBI, "%s()\n", __func__);
+
+ /* TODO: NTSC SPECIFIC */
+ /* Init and establish defaults */
+ params->samplesperline = 1440;
+ params->numberoflines = 12;
+ params->numberoflines = 18;
+ params->pitch = 1600;
+ params->pitch = 1440;
+ params->numpagetables = 2 +
+ ((params->numberoflines * params->pitch) / PAGE_SIZE);
+ params->bitspersample = 8;
+ params->linethreshold = 0;
+ params->pagetablelistvirt = NULL;
+ params->pagetablelistphys = NULL;
+ params->numpagetableentries = port->hwcfg.buffercount;
+
+ /* Allocate the PCI resources, buffers (hard) */
+ for (i = 0; i < port->hwcfg.buffercount; i++) {
+ buf = saa7164_buffer_alloc(port,
+ params->numberoflines *
+ params->pitch);
+
+ if (!buf) {
+ printk(KERN_ERR "%s() failed "
+ "(errno = %d), unable to allocate buffer\n",
+ __func__, result);
+ result = -ENOMEM;
+ goto failed;
+ } else {
+
+ mutex_lock(&port->dmaqueue_lock);
+ list_add_tail(&buf->list, &port->dmaqueue.list);
+ mutex_unlock(&port->dmaqueue_lock);
+
+ }
+ }
+
+ /* Allocate some kernel buffers for copying
+ * to userpsace.
+ */
+ len = params->numberoflines * params->pitch;
+
+ if (vbi_buffers < 16)
+ vbi_buffers = 16;
+ if (vbi_buffers > 512)
+ vbi_buffers = 512;
+
+ for (i = 0; i < vbi_buffers; i++) {
+
+ ubuf = saa7164_buffer_alloc_user(dev, len);
+ if (ubuf) {
+ mutex_lock(&port->dmaqueue_lock);
+ list_add_tail(&ubuf->list, &port->list_buf_free.list);
+ mutex_unlock(&port->dmaqueue_lock);
+ }
+
+ }
+
+ result = 0;
+
+failed:
+ return result;
+}
+
+
+static int saa7164_vbi_initialize(struct saa7164_port *port)
+{
+ saa7164_vbi_configure(port);
+ return 0;
+}
+
+/* -- V4L2 --------------------------------------------------------- */
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct saa7164_vbi_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ struct saa7164_dev *dev = port->dev;
+ unsigned int i;
+
+ dprintk(DBGLVL_VBI, "%s(id=0x%x)\n", __func__, (u32)*id);
+
+ for (i = 0; i < ARRAY_SIZE(saa7164_tvnorms); i++) {
+ if (*id & saa7164_tvnorms[i].id)
+ break;
+ }
+ if (i == ARRAY_SIZE(saa7164_tvnorms))
+ return -EINVAL;
+
+ port->encodernorm = saa7164_tvnorms[i];
+
+ /* Update the audio decoder while is not running in
+ * auto detect mode.
+ */
+ saa7164_api_set_audio_std(port);
+
+ dprintk(DBGLVL_VBI, "%s(id=0x%x) OK\n", __func__, (u32)*id);
+
+ return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ int n;
+
+ char *inputs[] = { "tuner", "composite", "svideo", "aux",
+ "composite 2", "svideo 2", "aux 2" };
+
+ if (i->index >= 7)
+ return -EINVAL;
+
+ strcpy(i->name, inputs[i->index]);
+
+ if (i->index == 0)
+ i->type = V4L2_INPUT_TYPE_TUNER;
+ else
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+
+ for (n = 0; n < ARRAY_SIZE(saa7164_tvnorms); n++)
+ i->std |= saa7164_tvnorms[n].id;
+
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct saa7164_vbi_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ struct saa7164_dev *dev = port->dev;
+
+ if (saa7164_api_get_videomux(port) != SAA_OK)
+ return -EIO;
+
+ *i = (port->mux_input - 1);
+
+ dprintk(DBGLVL_VBI, "%s() input=%d\n", __func__, *i);
+
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct saa7164_vbi_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ struct saa7164_dev *dev = port->dev;
+
+ dprintk(DBGLVL_VBI, "%s() input=%d\n", __func__, i);
+
+ if (i >= 7)
+ return -EINVAL;
+
+ port->mux_input = i + 1;
+
+ if (saa7164_api_set_videomux(port) != SAA_OK)
+ return -EIO;
+
+ return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct saa7164_vbi_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ struct saa7164_dev *dev = port->dev;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ strcpy(t->name, "tuner");
+ t->type = V4L2_TUNER_ANALOG_TV;
+ t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO;
+
+ dprintk(DBGLVL_VBI, "VIDIOC_G_TUNER: tuner type %d\n", t->type);
+
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ /* Update the A/V core */
+ return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct saa7164_vbi_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+
+ f->type = V4L2_TUNER_ANALOG_TV;
+ f->frequency = port->freq;
+
+ return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct saa7164_vbi_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ struct saa7164_dev *dev = port->dev;
+ struct saa7164_port *tsport;
+ struct dvb_frontend *fe;
+
+ /* TODO: Pull this for the std */
+ struct analog_parameters params = {
+ .mode = V4L2_TUNER_ANALOG_TV,
+ .audmode = V4L2_TUNER_MODE_STEREO,
+ .std = port->encodernorm.id,
+ .frequency = f->frequency
+ };
+
+ /* Stop the encoder */
+ dprintk(DBGLVL_VBI, "%s() frequency=%d tuner=%d\n", __func__,
+ f->frequency, f->tuner);
+
+ if (f->tuner != 0)
+ return -EINVAL;
+
+ if (f->type != V4L2_TUNER_ANALOG_TV)
+ return -EINVAL;
+
+ port->freq = f->frequency;
+
+ /* Update the hardware */
+ if (port->nr == SAA7164_PORT_VBI1)
+ tsport = &dev->ports[SAA7164_PORT_TS1];
+ else
+ if (port->nr == SAA7164_PORT_VBI2)
+ tsport = &dev->ports[SAA7164_PORT_TS2];
+ else
+ BUG();
+
+ fe = tsport->dvb.frontend;
+
+ if (fe && fe->ops.tuner_ops.set_analog_params)
+ fe->ops.tuner_ops.set_analog_params(fe, &params);
+ else
+ printk(KERN_ERR "%s() No analog tuner, aborting\n", __func__);
+
+ saa7164_vbi_initialize(port);
+
+ return 0;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctl)
+{
+ struct saa7164_vbi_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ struct saa7164_dev *dev = port->dev;
+
+ dprintk(DBGLVL_VBI, "%s(id=%d, value=%d)\n", __func__,
+ ctl->id, ctl->value);
+
+ switch (ctl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctl->value = port->ctl_brightness;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctl->value = port->ctl_contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ ctl->value = port->ctl_saturation;
+ break;
+ case V4L2_CID_HUE:
+ ctl->value = port->ctl_hue;
+ break;
+ case V4L2_CID_SHARPNESS:
+ ctl->value = port->ctl_sharpness;
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ ctl->value = port->ctl_volume;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctl)
+{
+ struct saa7164_vbi_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ struct saa7164_dev *dev = port->dev;
+ int ret = 0;
+
+ dprintk(DBGLVL_VBI, "%s(id=%d, value=%d)\n", __func__,
+ ctl->id, ctl->value);
+
+ switch (ctl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ if ((ctl->value >= 0) && (ctl->value <= 255)) {
+ port->ctl_brightness = ctl->value;
+ saa7164_api_set_usercontrol(port,
+ PU_BRIGHTNESS_CONTROL);
+ } else
+ ret = -EINVAL;
+ break;
+ case V4L2_CID_CONTRAST:
+ if ((ctl->value >= 0) && (ctl->value <= 255)) {
+ port->ctl_contrast = ctl->value;
+ saa7164_api_set_usercontrol(port, PU_CONTRAST_CONTROL);
+ } else
+ ret = -EINVAL;
+ break;
+ case V4L2_CID_SATURATION:
+ if ((ctl->value >= 0) && (ctl->value <= 255)) {
+ port->ctl_saturation = ctl->value;
+ saa7164_api_set_usercontrol(port,
+ PU_SATURATION_CONTROL);
+ } else
+ ret = -EINVAL;
+ break;
+ case V4L2_CID_HUE:
+ if ((ctl->value >= 0) && (ctl->value <= 255)) {
+ port->ctl_hue = ctl->value;
+ saa7164_api_set_usercontrol(port, PU_HUE_CONTROL);
+ } else
+ ret = -EINVAL;
+ break;
+ case V4L2_CID_SHARPNESS:
+ if ((ctl->value >= 0) && (ctl->value <= 255)) {
+ port->ctl_sharpness = ctl->value;
+ saa7164_api_set_usercontrol(port, PU_SHARPNESS_CONTROL);
+ } else
+ ret = -EINVAL;
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ if ((ctl->value >= -83) && (ctl->value <= 24)) {
+ port->ctl_volume = ctl->value;
+ saa7164_api_set_audio_volume(port, port->ctl_volume);
+ } else
+ ret = -EINVAL;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int saa7164_get_ctrl(struct saa7164_port *port,
+ struct v4l2_ext_control *ctrl)
+{
+ struct saa7164_vbi_params *params = &port->vbi_params;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ ctrl->value = params->stream_type;
+ break;
+ case V4L2_CID_MPEG_AUDIO_MUTE:
+ ctrl->value = params->ctl_mute;
+ break;
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ ctrl->value = params->ctl_aspect;
+ break;
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ ctrl->value = params->refdist;
+ break;
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ ctrl->value = params->gop_size;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int vidioc_g_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *ctrls)
+{
+ struct saa7164_vbi_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ int i, err = 0;
+
+ if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+ for (i = 0; i < ctrls->count; i++) {
+ struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+ err = saa7164_get_ctrl(port, ctrl);
+ if (err) {
+ ctrls->error_idx = i;
+ break;
+ }
+ }
+ return err;
+
+ }
+
+ return -EINVAL;
+}
+
+static int saa7164_try_ctrl(struct v4l2_ext_control *ctrl, int ac3)
+{
+ int ret = -EINVAL;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ if ((ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_PS) ||
+ (ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_TS))
+ ret = 0;
+ break;
+ case V4L2_CID_MPEG_AUDIO_MUTE:
+ if ((ctrl->value >= 0) &&
+ (ctrl->value <= 1))
+ ret = 0;
+ break;
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ if ((ctrl->value >= V4L2_MPEG_VIDEO_ASPECT_1x1) &&
+ (ctrl->value <= V4L2_MPEG_VIDEO_ASPECT_221x100))
+ ret = 0;
+ break;
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ if ((ctrl->value >= 0) &&
+ (ctrl->value <= 255))
+ ret = 0;
+ break;
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ if ((ctrl->value >= 1) &&
+ (ctrl->value <= 3))
+ ret = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int vidioc_try_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *ctrls)
+{
+ int i, err = 0;
+
+ if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+ for (i = 0; i < ctrls->count; i++) {
+ struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+ err = saa7164_try_ctrl(ctrl, 0);
+ if (err) {
+ ctrls->error_idx = i;
+ break;
+ }
+ }
+ return err;
+ }
+
+ return -EINVAL;
+}
+
+static int saa7164_set_ctrl(struct saa7164_port *port,
+ struct v4l2_ext_control *ctrl)
+{
+ struct saa7164_vbi_params *params = &port->vbi_params;
+ int ret = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ params->stream_type = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_AUDIO_MUTE:
+ params->ctl_mute = ctrl->value;
+ ret = saa7164_api_audio_mute(port, params->ctl_mute);
+ if (ret != SAA_OK) {
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__,
+ ret);
+ ret = -EIO;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ params->ctl_aspect = ctrl->value;
+ ret = saa7164_api_set_aspect_ratio(port);
+ if (ret != SAA_OK) {
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__,
+ ret);
+ ret = -EIO;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ params->refdist = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ params->gop_size = ctrl->value;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* TODO: Update the hardware */
+
+ return ret;
+}
+
+static int vidioc_s_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *ctrls)
+{
+ struct saa7164_vbi_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ int i, err = 0;
+
+ if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+ for (i = 0; i < ctrls->count; i++) {
+ struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+ err = saa7164_try_ctrl(ctrl, 0);
+ if (err) {
+ ctrls->error_idx = i;
+ break;
+ }
+ err = saa7164_set_ctrl(port, ctrl);
+ if (err) {
+ ctrls->error_idx = i;
+ break;
+ }
+ }
+ return err;
+
+ }
+
+ return -EINVAL;
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct saa7164_vbi_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ struct saa7164_dev *dev = port->dev;
+
+ strcpy(cap->driver, dev->name);
+ strlcpy(cap->card, saa7164_boards[dev->board].name,
+ sizeof(cap->card));
+ sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+
+ cap->capabilities =
+ V4L2_CAP_VBI_CAPTURE |
+ V4L2_CAP_READWRITE |
+ 0;
+
+ cap->capabilities |= V4L2_CAP_TUNER;
+ cap->version = 0;
+
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index != 0)
+ return -EINVAL;
+
+ strlcpy(f->description, "VBI", sizeof(f->description));
+ f->pixelformat = V4L2_PIX_FMT_MPEG;
+
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct saa7164_vbi_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ struct saa7164_dev *dev = port->dev;
+
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage =
+ port->ts_packet_size * port->ts_packet_count;
+ f->fmt.pix.colorspace = 0;
+ f->fmt.pix.width = port->width;
+ f->fmt.pix.height = port->height;
+
+ dprintk(DBGLVL_VBI, "VIDIOC_G_FMT: w: %d, h: %d\n",
+ port->width, port->height);
+
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct saa7164_vbi_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ struct saa7164_dev *dev = port->dev;
+
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage =
+ port->ts_packet_size * port->ts_packet_count;
+ f->fmt.pix.colorspace = 0;
+ dprintk(DBGLVL_VBI, "VIDIOC_TRY_FMT: w: %d, h: %d\n",
+ port->width, port->height);
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct saa7164_vbi_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ struct saa7164_dev *dev = port->dev;
+
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage =
+ port->ts_packet_size * port->ts_packet_count;
+ f->fmt.pix.colorspace = 0;
+
+ dprintk(DBGLVL_VBI, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n",
+ f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
+
+ return 0;
+}
+
+static int fill_queryctrl(struct saa7164_vbi_params *params,
+ struct v4l2_queryctrl *c)
+{
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 127);
+ case V4L2_CID_CONTRAST:
+ return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 66);
+ case V4L2_CID_SATURATION:
+ return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 62);
+ case V4L2_CID_HUE:
+ return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 128);
+ case V4L2_CID_SHARPNESS:
+ return v4l2_ctrl_query_fill(c, 0x0, 0x0f, 1, 8);
+ case V4L2_CID_MPEG_AUDIO_MUTE:
+ return v4l2_ctrl_query_fill(c, 0x0, 0x01, 1, 0);
+ case V4L2_CID_AUDIO_VOLUME:
+ return v4l2_ctrl_query_fill(c, -83, 24, 1, 20);
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ return v4l2_ctrl_query_fill(c,
+ V4L2_MPEG_STREAM_TYPE_MPEG2_PS,
+ V4L2_MPEG_STREAM_TYPE_MPEG2_TS,
+ 1, V4L2_MPEG_STREAM_TYPE_MPEG2_PS);
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ return v4l2_ctrl_query_fill(c,
+ V4L2_MPEG_VIDEO_ASPECT_1x1,
+ V4L2_MPEG_VIDEO_ASPECT_221x100,
+ 1, V4L2_MPEG_VIDEO_ASPECT_4x3);
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ return v4l2_ctrl_query_fill(c, 1, 255, 1, 15);
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ return v4l2_ctrl_query_fill(c,
+ 1, 3, 1, 1);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ struct saa7164_vbi_fh *fh = priv;
+ struct saa7164_port *port = fh->port;
+ int i, next;
+ u32 id = c->id;
+
+ memset(c, 0, sizeof(*c));
+
+ next = !!(id & V4L2_CTRL_FLAG_NEXT_CTRL);
+ c->id = id & ~V4L2_CTRL_FLAG_NEXT_CTRL;
+
+ for (i = 0; i < ARRAY_SIZE(saa7164_v4l2_ctrls); i++) {
+ if (next) {
+ if (c->id < saa7164_v4l2_ctrls[i])
+ c->id = saa7164_v4l2_ctrls[i];
+ else
+ continue;
+ }
+
+ if (c->id == saa7164_v4l2_ctrls[i])
+ return fill_queryctrl(&port->vbi_params, c);
+
+ if (c->id < saa7164_v4l2_ctrls[i])
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int saa7164_vbi_stop_port(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ int ret;
+
+ ret = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
+ if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
+ printk(KERN_ERR "%s() stop transition failed, ret = 0x%x\n",
+ __func__, ret);
+ ret = -EIO;
+ } else {
+ dprintk(DBGLVL_VBI, "%s() Stopped\n", __func__);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int saa7164_vbi_acquire_port(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ int ret;
+
+ ret = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);
+ if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
+ printk(KERN_ERR "%s() acquire transition failed, ret = 0x%x\n",
+ __func__, ret);
+ ret = -EIO;
+ } else {
+ dprintk(DBGLVL_VBI, "%s() Acquired\n", __func__);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int saa7164_vbi_pause_port(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ int ret;
+
+ ret = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE);
+ if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
+ printk(KERN_ERR "%s() pause transition failed, ret = 0x%x\n",
+ __func__, ret);
+ ret = -EIO;
+ } else {
+ dprintk(DBGLVL_VBI, "%s() Paused\n", __func__);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+/* Firmware is very windows centric, meaning you have to transition
+ * the part through AVStream / KS Windows stages, forwards or backwards.
+ * States are: stopped, acquired (h/w), paused, started.
+ * We have to leave here will all of the soft buffers on the free list,
+ * else the cfg_post() func won't have soft buffers to correctly configure.
+ */
+static int saa7164_vbi_stop_streaming(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ struct saa7164_buffer *buf;
+ struct saa7164_user_buffer *ubuf;
+ struct list_head *c, *n;
+ int ret;
+
+ dprintk(DBGLVL_VBI, "%s(port=%d)\n", __func__, port->nr);
+
+ ret = saa7164_vbi_pause_port(port);
+ ret = saa7164_vbi_acquire_port(port);
+ ret = saa7164_vbi_stop_port(port);
+
+ dprintk(DBGLVL_VBI, "%s(port=%d) Hardware stopped\n", __func__,
+ port->nr);
+
+ /* Reset the state of any allocated buffer resources */
+ mutex_lock(&port->dmaqueue_lock);
+
+ /* Reset the hard and soft buffer state */
+ list_for_each_safe(c, n, &port->dmaqueue.list) {
+ buf = list_entry(c, struct saa7164_buffer, list);
+ buf->flags = SAA7164_BUFFER_FREE;
+ buf->pos = 0;
+ }
+
+ list_for_each_safe(c, n, &port->list_buf_used.list) {
+ ubuf = list_entry(c, struct saa7164_user_buffer, list);
+ ubuf->pos = 0;
+ list_move_tail(&ubuf->list, &port->list_buf_free.list);
+ }
+
+ mutex_unlock(&port->dmaqueue_lock);
+
+ /* Free any allocated resources */
+ saa7164_vbi_buffers_dealloc(port);
+
+ dprintk(DBGLVL_VBI, "%s(port=%d) Released\n", __func__, port->nr);
+
+ return ret;
+}
+
+static int saa7164_vbi_start_streaming(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ int result, ret = 0;
+
+ dprintk(DBGLVL_VBI, "%s(port=%d)\n", __func__, port->nr);
+
+ port->done_first_interrupt = 0;
+
+ /* allocate all of the PCIe DMA buffer resources on the fly,
+ * allowing switching between TS and PS payloads without
+ * requiring a complete driver reload.
+ */
+ saa7164_vbi_buffers_alloc(port);
+
+ /* Configure the encoder with any cache values */
+#if 0
+ saa7164_api_set_encoder(port);
+ saa7164_api_get_encoder(port);
+#endif
+
+ /* Place the empty buffers on the hardware */
+ saa7164_buffer_cfg_port(port);
+
+ /* Negotiate format */
+ if (saa7164_api_set_vbi_format(port) != SAA_OK) {
+ printk(KERN_ERR "%s() No supported VBI format\n", __func__);
+ ret = -EIO;
+ goto out;
+ }
+
+ /* Acquire the hardware */
+ result = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);
+ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+ printk(KERN_ERR "%s() acquire transition failed, res = 0x%x\n",
+ __func__, result);
+
+ ret = -EIO;
+ goto out;
+ } else
+ dprintk(DBGLVL_VBI, "%s() Acquired\n", __func__);
+
+ /* Pause the hardware */
+ result = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE);
+ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+ printk(KERN_ERR "%s() pause transition failed, res = 0x%x\n",
+ __func__, result);
+
+ /* Stop the hardware, regardless */
+ result = saa7164_vbi_stop_port(port);
+ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+ printk(KERN_ERR "%s() pause/forced stop transition "
+ "failed, res = 0x%x\n", __func__, result);
+ }
+
+ ret = -EIO;
+ goto out;
+ } else
+ dprintk(DBGLVL_VBI, "%s() Paused\n", __func__);
+
+ /* Start the hardware */
+ result = saa7164_api_transition_port(port, SAA_DMASTATE_RUN);
+ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+ printk(KERN_ERR "%s() run transition failed, result = 0x%x\n",
+ __func__, result);
+
+ /* Stop the hardware, regardless */
+ result = saa7164_vbi_acquire_port(port);
+ result = saa7164_vbi_stop_port(port);
+ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+ printk(KERN_ERR "%s() run/forced stop transition "
+ "failed, res = 0x%x\n", __func__, result);
+ }
+
+ ret = -EIO;
+ } else
+ dprintk(DBGLVL_VBI, "%s() Running\n", __func__);
+
+out:
+ return ret;
+}
+
+int saa7164_vbi_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ /* ntsc */
+ f->fmt.vbi.samples_per_line = 1600;
+ f->fmt.vbi.samples_per_line = 1440;
+ f->fmt.vbi.sampling_rate = 27000000;
+ f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+ f->fmt.vbi.offset = 0;
+ f->fmt.vbi.flags = 0;
+ f->fmt.vbi.start[0] = 10;
+ f->fmt.vbi.count[0] = 18;
+ f->fmt.vbi.start[1] = 263 + 10 + 1;
+ f->fmt.vbi.count[1] = 18;
+ return 0;
+}
+
+static int fops_open(struct file *file)
+{
+ struct saa7164_dev *dev;
+ struct saa7164_port *port;
+ struct saa7164_vbi_fh *fh;
+
+ port = (struct saa7164_port *)video_get_drvdata(video_devdata(file));
+ if (!port)
+ return -ENODEV;
+
+ dev = port->dev;
+
+ dprintk(DBGLVL_VBI, "%s()\n", __func__);
+
+ /* allocate + initialize per filehandle data */
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (NULL == fh)
+ return -ENOMEM;
+
+ file->private_data = fh;
+ fh->port = port;
+
+ return 0;
+}
+
+static int fops_release(struct file *file)
+{
+ struct saa7164_vbi_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ struct saa7164_dev *dev = port->dev;
+
+ dprintk(DBGLVL_VBI, "%s()\n", __func__);
+
+ /* Shut device down on last close */
+ if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) {
+ if (atomic_dec_return(&port->v4l_reader_count) == 0) {
+ /* stop vbi capture then cancel buffers */
+ saa7164_vbi_stop_streaming(port);
+ }
+ }
+
+ file->private_data = NULL;
+ kfree(fh);
+
+ return 0;
+}
+
+struct saa7164_user_buffer *saa7164_vbi_next_buf(struct saa7164_port *port)
+{
+ struct saa7164_user_buffer *ubuf = NULL;
+ struct saa7164_dev *dev = port->dev;
+ u32 crc;
+
+ mutex_lock(&port->dmaqueue_lock);
+ if (!list_empty(&port->list_buf_used.list)) {
+ ubuf = list_first_entry(&port->list_buf_used.list,
+ struct saa7164_user_buffer, list);
+
+ if (crc_checking) {
+ crc = crc32(0, ubuf->data, ubuf->actual_size);
+ if (crc != ubuf->crc) {
+ printk(KERN_ERR "%s() ubuf %p crc became invalid, was 0x%x became 0x%x\n",
+ __func__,
+ ubuf, ubuf->crc, crc);
+ }
+ }
+
+ }
+ mutex_unlock(&port->dmaqueue_lock);
+
+ dprintk(DBGLVL_VBI, "%s() returns %p\n", __func__, ubuf);
+
+ return ubuf;
+}
+
+static ssize_t fops_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *pos)
+{
+ struct saa7164_vbi_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+ struct saa7164_user_buffer *ubuf = NULL;
+ struct saa7164_dev *dev = port->dev;
+ int ret = 0;
+ int rem, cnt;
+ u8 *p;
+
+ port->last_read_msecs_diff = port->last_read_msecs;
+ port->last_read_msecs = jiffies_to_msecs(jiffies);
+ port->last_read_msecs_diff = port->last_read_msecs -
+ port->last_read_msecs_diff;
+
+ saa7164_histogram_update(&port->read_interval,
+ port->last_read_msecs_diff);
+
+ if (*pos) {
+ printk(KERN_ERR "%s() ESPIPE\n", __func__);
+ return -ESPIPE;
+ }
+
+ if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
+ if (atomic_inc_return(&port->v4l_reader_count) == 1) {
+
+ if (saa7164_vbi_initialize(port) < 0) {
+ printk(KERN_ERR "%s() EINVAL\n", __func__);
+ return -EINVAL;
+ }
+
+ saa7164_vbi_start_streaming(port);
+ msleep(200);
+ }
+ }
+
+ /* blocking wait for buffer */
+ if ((file->f_flags & O_NONBLOCK) == 0) {
+ if (wait_event_interruptible(port->wait_read,
+ saa7164_vbi_next_buf(port))) {
+ printk(KERN_ERR "%s() ERESTARTSYS\n", __func__);
+ return -ERESTARTSYS;
+ }
+ }
+
+ /* Pull the first buffer from the used list */
+ ubuf = saa7164_vbi_next_buf(port);
+
+ while ((count > 0) && ubuf) {
+
+ /* set remaining bytes to copy */
+ rem = ubuf->actual_size - ubuf->pos;
+ cnt = rem > count ? count : rem;
+
+ p = ubuf->data + ubuf->pos;
+
+ dprintk(DBGLVL_VBI,
+ "%s() count=%d cnt=%d rem=%d buf=%p buf->pos=%d\n",
+ __func__, (int)count, cnt, rem, ubuf, ubuf->pos);
+
+ if (copy_to_user(buffer, p, cnt)) {
+ printk(KERN_ERR "%s() copy_to_user failed\n", __func__);
+ if (!ret) {
+ printk(KERN_ERR "%s() EFAULT\n", __func__);
+ ret = -EFAULT;
+ }
+ goto err;
+ }
+
+ ubuf->pos += cnt;
+ count -= cnt;
+ buffer += cnt;
+ ret += cnt;
+
+ if (ubuf->pos > ubuf->actual_size)
+ printk(KERN_ERR "read() pos > actual, huh?\n");
+
+ if (ubuf->pos == ubuf->actual_size) {
+
+ /* finished with current buffer, take next buffer */
+
+ /* Requeue the buffer on the free list */
+ ubuf->pos = 0;
+
+ mutex_lock(&port->dmaqueue_lock);
+ list_move_tail(&ubuf->list, &port->list_buf_free.list);
+ mutex_unlock(&port->dmaqueue_lock);
+
+ /* Dequeue next */
+ if ((file->f_flags & O_NONBLOCK) == 0) {
+ if (wait_event_interruptible(port->wait_read,
+ saa7164_vbi_next_buf(port))) {
+ break;
+ }
+ }
+ ubuf = saa7164_vbi_next_buf(port);
+ }
+ }
+err:
+ if (!ret && !ubuf) {
+ printk(KERN_ERR "%s() EAGAIN\n", __func__);
+ ret = -EAGAIN;
+ }
+
+ return ret;
+}
+
+static unsigned int fops_poll(struct file *file, poll_table *wait)
+{
+ struct saa7164_vbi_fh *fh = (struct saa7164_vbi_fh *)file->private_data;
+ struct saa7164_port *port = fh->port;
+ unsigned int mask = 0;
+
+ port->last_poll_msecs_diff = port->last_poll_msecs;
+ port->last_poll_msecs = jiffies_to_msecs(jiffies);
+ port->last_poll_msecs_diff = port->last_poll_msecs -
+ port->last_poll_msecs_diff;
+
+ saa7164_histogram_update(&port->poll_interval,
+ port->last_poll_msecs_diff);
+
+ if (!video_is_registered(port->v4l_device))
+ return -EIO;
+
+ if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
+ if (atomic_inc_return(&port->v4l_reader_count) == 1) {
+ if (saa7164_vbi_initialize(port) < 0)
+ return -EINVAL;
+ saa7164_vbi_start_streaming(port);
+ msleep(200);
+ }
+ }
+
+ /* blocking wait for buffer */
+ if ((file->f_flags & O_NONBLOCK) == 0) {
+ if (wait_event_interruptible(port->wait_read,
+ saa7164_vbi_next_buf(port))) {
+ return -ERESTARTSYS;
+ }
+ }
+
+ /* Pull the first buffer from the used list */
+ if (!list_empty(&port->list_buf_used.list))
+ mask |= POLLIN | POLLRDNORM;
+
+ return mask;
+}
+static const struct v4l2_file_operations vbi_fops = {
+ .owner = THIS_MODULE,
+ .open = fops_open,
+ .release = fops_release,
+ .read = fops_read,
+ .poll = fops_poll,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops vbi_ioctl_ops = {
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls,
+ .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls,
+ .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls,
+ .vidioc_queryctrl = vidioc_queryctrl,
+#if 0
+ .vidioc_g_chip_ident = saa7164_g_chip_ident,
+#endif
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+#if 0
+ .vidioc_g_register = saa7164_g_register,
+ .vidioc_s_register = saa7164_s_register,
+#endif
+#endif
+ .vidioc_g_fmt_vbi_cap = saa7164_vbi_fmt,
+ .vidioc_try_fmt_vbi_cap = saa7164_vbi_fmt,
+ .vidioc_s_fmt_vbi_cap = saa7164_vbi_fmt,
+};
+
+static struct video_device saa7164_vbi_template = {
+ .name = "saa7164",
+ .fops = &vbi_fops,
+ .ioctl_ops = &vbi_ioctl_ops,
+ .minor = -1,
+ .tvnorms = SAA7164_NORMS,
+ .current_norm = V4L2_STD_NTSC_M,
+};
+
+static struct video_device *saa7164_vbi_alloc(
+ struct saa7164_port *port,
+ struct pci_dev *pci,
+ struct video_device *template,
+ char *type)
+{
+ struct video_device *vfd;
+ struct saa7164_dev *dev = port->dev;
+
+ dprintk(DBGLVL_VBI, "%s()\n", __func__);
+
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+
+ *vfd = *template;
+ snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name,
+ type, saa7164_boards[dev->board].name);
+
+ vfd->parent = &pci->dev;
+ vfd->release = video_device_release;
+ return vfd;
+}
+
+int saa7164_vbi_register(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ int result = -ENODEV;
+
+ dprintk(DBGLVL_VBI, "%s()\n", __func__);
+
+ if (port->type != SAA7164_MPEG_VBI)
+ BUG();
+
+ /* Sanity check that the PCI configuration space is active */
+ if (port->hwcfg.BARLocation == 0) {
+ printk(KERN_ERR "%s() failed "
+ "(errno = %d), NO PCI configuration\n",
+ __func__, result);
+ result = -ENOMEM;
+ goto failed;
+ }
+
+ /* Establish VBI defaults here */
+
+ /* Allocate and register the video device node */
+ port->v4l_device = saa7164_vbi_alloc(port,
+ dev->pci, &saa7164_vbi_template, "vbi");
+
+ if (!port->v4l_device) {
+ printk(KERN_INFO "%s: can't allocate vbi device\n",
+ dev->name);
+ result = -ENOMEM;
+ goto failed;
+ }
+
+ video_set_drvdata(port->v4l_device, port);
+ result = video_register_device(port->v4l_device,
+ VFL_TYPE_VBI, -1);
+ if (result < 0) {
+ printk(KERN_INFO "%s: can't register vbi device\n",
+ dev->name);
+ /* TODO: We're going to leak here if we don't dealloc
+ The buffers above. The unreg function can't deal wit it.
+ */
+ goto failed;
+ }
+
+ printk(KERN_INFO "%s: registered device vbi%d [vbi]\n",
+ dev->name, port->v4l_device->num);
+
+ /* Configure the hardware defaults */
+
+ result = 0;
+failed:
+ return result;
+}
+
+void saa7164_vbi_unregister(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+
+ dprintk(DBGLVL_VBI, "%s(port=%d)\n", __func__, port->nr);
+
+ if (port->type != SAA7164_MPEG_VBI)
+ BUG();
+
+ if (port->v4l_device) {
+ if (port->v4l_device->minor != -1)
+ video_unregister_device(port->v4l_device);
+ else
+ video_device_release(port->v4l_device);
+
+ port->v4l_device = NULL;
+ }
+
+}
diff --git a/drivers/media/video/saa7164/saa7164.h b/drivers/media/video/saa7164/saa7164.h
new file mode 100644
index 00000000..742b3410
--- /dev/null
+++ b/drivers/media/video/saa7164/saa7164.h
@@ -0,0 +1,624 @@
+/*
+ * Driver for the NXP SAA7164 PCIe bridge
+ *
+ * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ Driver architecture
+ *******************
+
+ saa7164_core.c/buffer.c/cards.c/i2c.c/dvb.c
+ | : Standard Linux driver framework for creating
+ | : exposing and managing interfaces to the rest
+ | : of the kernel or userland. Also uses _fw.c to load
+ | : firmware direct into the PCIe bus, bypassing layers.
+ V
+ saa7164_api..() : Translate kernel specific functions/features
+ | : into command buffers.
+ V
+ saa7164_cmd..() : Manages the flow of command packets on/off,
+ | : the bus. Deal with bus errors, timeouts etc.
+ V
+ saa7164_bus..() : Manage a read/write memory ring buffer in the
+ | : PCIe Address space.
+ |
+ | saa7164_fw...() : Load any frimware
+ | | : direct into the device
+ V V
+ <- ----------------- PCIe address space -------------------- ->
+*/
+
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/kdev_t.h>
+#include <linux/mutex.h>
+#include <linux/crc32.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+
+#include <media/tuner.h>
+#include <media/tveeprom.h>
+#include <media/videobuf-dma-sg.h>
+#include <media/videobuf-dvb.h>
+#include <dvb_demux.h>
+#include <dvb_frontend.h>
+#include <dvb_net.h>
+#include <dvbdev.h>
+#include <dmxdev.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-chip-ident.h>
+
+#include "saa7164-reg.h"
+#include "saa7164-types.h"
+
+#define SAA7164_MAXBOARDS 8
+
+#define UNSET (-1U)
+#define SAA7164_BOARD_NOAUTO UNSET
+#define SAA7164_BOARD_UNKNOWN 0
+#define SAA7164_BOARD_UNKNOWN_REV2 1
+#define SAA7164_BOARD_UNKNOWN_REV3 2
+#define SAA7164_BOARD_HAUPPAUGE_HVR2250 3
+#define SAA7164_BOARD_HAUPPAUGE_HVR2200 4
+#define SAA7164_BOARD_HAUPPAUGE_HVR2200_2 5
+#define SAA7164_BOARD_HAUPPAUGE_HVR2200_3 6
+#define SAA7164_BOARD_HAUPPAUGE_HVR2250_2 7
+#define SAA7164_BOARD_HAUPPAUGE_HVR2250_3 8
+#define SAA7164_BOARD_HAUPPAUGE_HVR2200_4 9
+#define SAA7164_BOARD_HAUPPAUGE_HVR2200_5 10
+
+#define SAA7164_MAX_UNITS 8
+#define SAA7164_TS_NUMBER_OF_LINES 312
+#define SAA7164_PS_NUMBER_OF_LINES 256
+#define SAA7164_PT_ENTRIES 16 /* (312 * 188) / 4096 */
+#define SAA7164_MAX_ENCODER_BUFFERS 64 /* max 5secs of latency at 6Mbps */
+#define SAA7164_MAX_VBI_BUFFERS 64
+
+/* Port related defines */
+#define SAA7164_PORT_TS1 (0)
+#define SAA7164_PORT_TS2 (SAA7164_PORT_TS1 + 1)
+#define SAA7164_PORT_ENC1 (SAA7164_PORT_TS2 + 1)
+#define SAA7164_PORT_ENC2 (SAA7164_PORT_ENC1 + 1)
+#define SAA7164_PORT_VBI1 (SAA7164_PORT_ENC2 + 1)
+#define SAA7164_PORT_VBI2 (SAA7164_PORT_VBI1 + 1)
+#define SAA7164_MAX_PORTS (SAA7164_PORT_VBI2 + 1)
+
+#define DBGLVL_FW 4
+#define DBGLVL_DVB 8
+#define DBGLVL_I2C 16
+#define DBGLVL_API 32
+#define DBGLVL_CMD 64
+#define DBGLVL_BUS 128
+#define DBGLVL_IRQ 256
+#define DBGLVL_BUF 512
+#define DBGLVL_ENC 1024
+#define DBGLVL_VBI 2048
+#define DBGLVL_THR 4096
+#define DBGLVL_CPU 8192
+
+#define SAA7164_NORMS \
+ (V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_JP | V4L2_STD_NTSC_443)
+
+enum port_t {
+ SAA7164_MPEG_UNDEFINED = 0,
+ SAA7164_MPEG_DVB,
+ SAA7164_MPEG_ENCODER,
+ SAA7164_MPEG_VBI,
+};
+
+enum saa7164_i2c_bus_nr {
+ SAA7164_I2C_BUS_0 = 0,
+ SAA7164_I2C_BUS_1,
+ SAA7164_I2C_BUS_2,
+};
+
+enum saa7164_buffer_flags {
+ SAA7164_BUFFER_UNDEFINED = 0,
+ SAA7164_BUFFER_FREE,
+ SAA7164_BUFFER_BUSY,
+ SAA7164_BUFFER_FULL
+};
+
+enum saa7164_unit_type {
+ SAA7164_UNIT_UNDEFINED = 0,
+ SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ SAA7164_UNIT_ANALOG_DEMODULATOR,
+ SAA7164_UNIT_TUNER,
+ SAA7164_UNIT_EEPROM,
+ SAA7164_UNIT_ZILOG_IRBLASTER,
+ SAA7164_UNIT_ENCODER,
+};
+
+/* The PCIe bridge doesn't grant direct access to i2c.
+ * Instead, you address i2c devices using a uniqely
+ * allocated 'unitid' value via a messaging API. This
+ * is a problem. The kernel and existing demod/tuner
+ * drivers expect to talk 'i2c', so we have to maintain
+ * a translation layer, and a series of functions to
+ * convert i2c bus + device address into a unit id.
+ */
+struct saa7164_unit {
+ enum saa7164_unit_type type;
+ u8 id;
+ char *name;
+ enum saa7164_i2c_bus_nr i2c_bus_nr;
+ u8 i2c_bus_addr;
+ u8 i2c_reg_len;
+};
+
+struct saa7164_board {
+ char *name;
+ enum port_t porta, portb, portc,
+ portd, porte, portf;
+ enum {
+ SAA7164_CHIP_UNDEFINED = 0,
+ SAA7164_CHIP_REV2,
+ SAA7164_CHIP_REV3,
+ } chiprev;
+ struct saa7164_unit unit[SAA7164_MAX_UNITS];
+};
+
+struct saa7164_subid {
+ u16 subvendor;
+ u16 subdevice;
+ u32 card;
+};
+
+struct saa7164_encoder_fh {
+ struct saa7164_port *port;
+ atomic_t v4l_reading;
+};
+
+struct saa7164_vbi_fh {
+ struct saa7164_port *port;
+ atomic_t v4l_reading;
+};
+
+struct saa7164_histogram_bucket {
+ u32 val;
+ u32 count;
+ u64 update_time;
+};
+
+struct saa7164_histogram {
+ char name[32];
+ struct saa7164_histogram_bucket counter1[64];
+};
+
+struct saa7164_user_buffer {
+ struct list_head list;
+
+ /* Attributes */
+ u8 *data;
+ u32 pos;
+ u32 actual_size;
+
+ u32 crc;
+};
+
+struct saa7164_fw_status {
+
+ /* RISC Core details */
+ u32 status;
+ u32 mode;
+ u32 spec;
+ u32 inst;
+ u32 cpuload;
+ u32 remainheap;
+
+ /* Firmware version */
+ u32 version;
+ u32 major;
+ u32 sub;
+ u32 rel;
+ u32 buildnr;
+};
+
+struct saa7164_dvb {
+ struct mutex lock;
+ struct dvb_adapter adapter;
+ struct dvb_frontend *frontend;
+ struct dvb_demux demux;
+ struct dmxdev dmxdev;
+ struct dmx_frontend fe_hw;
+ struct dmx_frontend fe_mem;
+ struct dvb_net net;
+ int feeding;
+};
+
+struct saa7164_i2c {
+ struct saa7164_dev *dev;
+
+ enum saa7164_i2c_bus_nr nr;
+
+ /* I2C I/O */
+ struct i2c_adapter i2c_adap;
+ struct i2c_algo_bit_data i2c_algo;
+ struct i2c_client i2c_client;
+ u32 i2c_rc;
+};
+
+struct saa7164_ctrl {
+ struct v4l2_queryctrl v;
+};
+
+struct saa7164_tvnorm {
+ char *name;
+ v4l2_std_id id;
+};
+
+struct saa7164_encoder_params {
+ struct saa7164_tvnorm encodernorm;
+ u32 height;
+ u32 width;
+ u32 is_50hz;
+ u32 bitrate; /* bps */
+ u32 bitrate_peak; /* bps */
+ u32 bitrate_mode;
+ u32 stream_type; /* V4L2_MPEG_STREAM_TYPE_MPEG2_TS */
+
+ u32 audio_sampling_freq;
+ u32 ctl_mute;
+ u32 ctl_aspect;
+ u32 refdist;
+ u32 gop_size;
+};
+
+struct saa7164_vbi_params {
+ struct saa7164_tvnorm encodernorm;
+ u32 height;
+ u32 width;
+ u32 is_50hz;
+ u32 bitrate; /* bps */
+ u32 bitrate_peak; /* bps */
+ u32 bitrate_mode;
+ u32 stream_type; /* V4L2_MPEG_STREAM_TYPE_MPEG2_TS */
+
+ u32 audio_sampling_freq;
+ u32 ctl_mute;
+ u32 ctl_aspect;
+ u32 refdist;
+ u32 gop_size;
+};
+
+struct saa7164_port;
+
+struct saa7164_buffer {
+ struct list_head list;
+
+ /* Note of which h/w buffer list index position we occupy */
+ int idx;
+
+ struct saa7164_port *port;
+
+ /* Hardware Specific */
+ /* PCI Memory allocations */
+ enum saa7164_buffer_flags flags; /* Free, Busy, Full */
+
+ /* A block of page align PCI memory */
+ u32 pci_size; /* PCI allocation size in bytes */
+ u64 __iomem *cpu; /* Virtual address */
+ dma_addr_t dma; /* Physical address */
+ u32 crc; /* Checksum for the entire buffer data */
+
+ /* A page table that splits the block into a number of entries */
+ u32 pt_size; /* PCI allocation size in bytes */
+ u64 __iomem *pt_cpu; /* Virtual address */
+ dma_addr_t pt_dma; /* Physical address */
+
+ /* Encoder fops */
+ u32 pos;
+ u32 actual_size;
+};
+
+struct saa7164_port {
+
+ struct saa7164_dev *dev;
+ enum port_t type;
+ int nr;
+
+ /* --- Generic port attributes --- */
+
+ /* HW stream parameters */
+ struct tmHWStreamParameters hw_streamingparams;
+
+ /* DMA configuration values, is seeded during initialization */
+ struct tmComResDMATermDescrHeader hwcfg;
+
+ /* hardware specific registers */
+ u32 bufcounter;
+ u32 pitch;
+ u32 bufsize;
+ u32 bufoffset;
+ u32 bufptr32l;
+ u32 bufptr32h;
+ u64 bufptr64;
+
+ u32 numpte; /* Number of entries in array, only valid in head */
+
+ struct mutex dmaqueue_lock;
+ struct saa7164_buffer dmaqueue;
+
+ u64 last_irq_msecs, last_svc_msecs;
+ u64 last_irq_msecs_diff, last_svc_msecs_diff;
+ u32 last_svc_wp;
+ u32 last_svc_rp;
+ u64 last_irq_svc_msecs_diff;
+ u64 last_read_msecs, last_read_msecs_diff;
+ u64 last_poll_msecs, last_poll_msecs_diff;
+
+ struct saa7164_histogram irq_interval;
+ struct saa7164_histogram svc_interval;
+ struct saa7164_histogram irq_svc_interval;
+ struct saa7164_histogram read_interval;
+ struct saa7164_histogram poll_interval;
+
+ /* --- DVB Transport Specific --- */
+ struct saa7164_dvb dvb;
+
+ /* --- Encoder/V4L related attributes --- */
+ /* Encoder */
+ /* Defaults established in saa7164-encoder.c */
+ struct saa7164_tvnorm encodernorm;
+ u32 height;
+ u32 width;
+ u32 freq;
+ u32 ts_packet_size;
+ u32 ts_packet_count;
+ u8 mux_input;
+ u8 encoder_profile;
+ u8 video_format;
+ u8 audio_format;
+ u8 video_resolution;
+ u16 ctl_brightness;
+ u16 ctl_contrast;
+ u16 ctl_hue;
+ u16 ctl_saturation;
+ u16 ctl_sharpness;
+ s8 ctl_volume;
+
+ struct tmComResAFeatureDescrHeader audfeat;
+ struct tmComResEncoderDescrHeader encunit;
+ struct tmComResProcDescrHeader vidproc;
+ struct tmComResExtDevDescrHeader ifunit;
+ struct tmComResTunerDescrHeader tunerunit;
+
+ struct work_struct workenc;
+
+ /* V4L Encoder Video */
+ struct saa7164_encoder_params encoder_params;
+ struct video_device *v4l_device;
+ atomic_t v4l_reader_count;
+
+ struct saa7164_buffer list_buf_used;
+ struct saa7164_buffer list_buf_free;
+ wait_queue_head_t wait_read;
+
+ /* V4L VBI */
+ struct tmComResVBIFormatDescrHeader vbi_fmt_ntsc;
+ struct saa7164_vbi_params vbi_params;
+
+ /* Debug */
+ u32 sync_errors;
+ u32 v_cc_errors;
+ u32 a_cc_errors;
+ u8 last_v_cc;
+ u8 last_a_cc;
+ u32 done_first_interrupt;
+};
+
+struct saa7164_dev {
+ struct list_head devlist;
+ atomic_t refcount;
+
+ /* pci stuff */
+ struct pci_dev *pci;
+ unsigned char pci_rev, pci_lat;
+ int pci_bus, pci_slot;
+ u32 __iomem *lmmio;
+ u8 __iomem *bmmio;
+ u32 __iomem *lmmio2;
+ u8 __iomem *bmmio2;
+ int pci_irqmask;
+
+ /* board details */
+ int nr;
+ int hwrevision;
+ u32 board;
+ char name[16];
+
+ /* firmware status */
+ struct saa7164_fw_status fw_status;
+ u32 firmwareloaded;
+
+ struct tmComResHWDescr hwdesc;
+ struct tmComResInterfaceDescr intfdesc;
+ struct tmComResBusDescr busdesc;
+
+ struct tmComResBusInfo bus;
+
+ /* Interrupt status and ack registers */
+ u32 int_status;
+ u32 int_ack;
+
+ struct cmd cmds[SAA_CMD_MAX_MSG_UNITS];
+ struct mutex lock;
+
+ /* I2c related */
+ struct saa7164_i2c i2c_bus[3];
+
+ /* Transport related */
+ struct saa7164_port ports[SAA7164_MAX_PORTS];
+
+ /* Deferred command/api interrupts handling */
+ struct work_struct workcmd;
+
+ /* A kernel thread to monitor the firmware log, used
+ * only in debug mode.
+ */
+ struct task_struct *kthread;
+
+};
+
+extern struct list_head saa7164_devlist;
+extern unsigned int waitsecs;
+extern unsigned int encoder_buffers;
+extern unsigned int vbi_buffers;
+
+/* ----------------------------------------------------------- */
+/* saa7164-core.c */
+void saa7164_dumpregs(struct saa7164_dev *dev, u32 addr);
+void saa7164_dumphex16(struct saa7164_dev *dev, u8 *buf, int len);
+void saa7164_getfirmwarestatus(struct saa7164_dev *dev);
+u32 saa7164_getcurrentfirmwareversion(struct saa7164_dev *dev);
+void saa7164_histogram_update(struct saa7164_histogram *hg, u32 val);
+
+/* ----------------------------------------------------------- */
+/* saa7164-fw.c */
+int saa7164_downloadfirmware(struct saa7164_dev *dev);
+
+/* ----------------------------------------------------------- */
+/* saa7164-i2c.c */
+extern int saa7164_i2c_register(struct saa7164_i2c *bus);
+extern int saa7164_i2c_unregister(struct saa7164_i2c *bus);
+extern void saa7164_call_i2c_clients(struct saa7164_i2c *bus,
+ unsigned int cmd, void *arg);
+
+/* ----------------------------------------------------------- */
+/* saa7164-bus.c */
+int saa7164_bus_setup(struct saa7164_dev *dev);
+void saa7164_bus_dump(struct saa7164_dev *dev);
+int saa7164_bus_set(struct saa7164_dev *dev, struct tmComResInfo* msg,
+ void *buf);
+int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg,
+ void *buf, int peekonly);
+
+/* ----------------------------------------------------------- */
+/* saa7164-cmd.c */
+int saa7164_cmd_send(struct saa7164_dev *dev,
+ u8 id, enum tmComResCmd command, u16 controlselector,
+ u16 size, void *buf);
+void saa7164_cmd_signal(struct saa7164_dev *dev, u8 seqno);
+int saa7164_irq_dequeue(struct saa7164_dev *dev);
+
+/* ----------------------------------------------------------- */
+/* saa7164-api.c */
+int saa7164_api_get_fw_version(struct saa7164_dev *dev, u32 *version);
+int saa7164_api_enum_subdevs(struct saa7164_dev *dev);
+int saa7164_api_i2c_read(struct saa7164_i2c *bus, u8 addr, u32 reglen, u8 *reg,
+ u32 datalen, u8 *data);
+int saa7164_api_i2c_write(struct saa7164_i2c *bus, u8 addr,
+ u32 datalen, u8 *data);
+int saa7164_api_dif_write(struct saa7164_i2c *bus, u8 addr,
+ u32 datalen, u8 *data);
+int saa7164_api_read_eeprom(struct saa7164_dev *dev, u8 *buf, int buflen);
+int saa7164_api_set_gpiobit(struct saa7164_dev *dev, u8 unitid, u8 pin);
+int saa7164_api_clear_gpiobit(struct saa7164_dev *dev, u8 unitid, u8 pin);
+int saa7164_api_transition_port(struct saa7164_port *port, u8 mode);
+int saa7164_api_initialize_dif(struct saa7164_port *port);
+int saa7164_api_configure_dif(struct saa7164_port *port, u32 std);
+int saa7164_api_set_encoder(struct saa7164_port *port);
+int saa7164_api_get_encoder(struct saa7164_port *port);
+int saa7164_api_set_aspect_ratio(struct saa7164_port *port);
+int saa7164_api_set_usercontrol(struct saa7164_port *port, u8 ctl);
+int saa7164_api_get_usercontrol(struct saa7164_port *port, u8 ctl);
+int saa7164_api_set_videomux(struct saa7164_port *port);
+int saa7164_api_audio_mute(struct saa7164_port *port, int mute);
+int saa7164_api_set_audio_volume(struct saa7164_port *port, s8 level);
+int saa7164_api_set_audio_std(struct saa7164_port *port);
+int saa7164_api_set_audio_detection(struct saa7164_port *port, int autodetect);
+int saa7164_api_get_videomux(struct saa7164_port *port);
+int saa7164_api_set_vbi_format(struct saa7164_port *port);
+int saa7164_api_set_debug(struct saa7164_dev *dev, u8 level);
+int saa7164_api_collect_debug(struct saa7164_dev *dev);
+int saa7164_api_get_load_info(struct saa7164_dev *dev,
+ struct tmFwInfoStruct *i);
+
+/* ----------------------------------------------------------- */
+/* saa7164-cards.c */
+extern struct saa7164_board saa7164_boards[];
+extern const unsigned int saa7164_bcount;
+
+extern struct saa7164_subid saa7164_subids[];
+extern const unsigned int saa7164_idcount;
+
+extern void saa7164_card_list(struct saa7164_dev *dev);
+extern void saa7164_gpio_setup(struct saa7164_dev *dev);
+extern void saa7164_card_setup(struct saa7164_dev *dev);
+
+extern int saa7164_i2caddr_to_reglen(struct saa7164_i2c *bus, int addr);
+extern int saa7164_i2caddr_to_unitid(struct saa7164_i2c *bus, int addr);
+extern char *saa7164_unitid_name(struct saa7164_dev *dev, u8 unitid);
+
+/* ----------------------------------------------------------- */
+/* saa7164-dvb.c */
+extern int saa7164_dvb_register(struct saa7164_port *port);
+extern int saa7164_dvb_unregister(struct saa7164_port *port);
+
+/* ----------------------------------------------------------- */
+/* saa7164-buffer.c */
+extern struct saa7164_buffer *saa7164_buffer_alloc(
+ struct saa7164_port *port, u32 len);
+extern int saa7164_buffer_dealloc(struct saa7164_buffer *buf);
+extern void saa7164_buffer_display(struct saa7164_buffer *buf);
+extern int saa7164_buffer_activate(struct saa7164_buffer *buf, int i);
+extern int saa7164_buffer_cfg_port(struct saa7164_port *port);
+extern struct saa7164_user_buffer *saa7164_buffer_alloc_user(
+ struct saa7164_dev *dev, u32 len);
+extern void saa7164_buffer_dealloc_user(struct saa7164_user_buffer *buf);
+extern int saa7164_buffer_zero_offsets(struct saa7164_port *port, int i);
+
+/* ----------------------------------------------------------- */
+/* saa7164-encoder.c */
+int saa7164_encoder_register(struct saa7164_port *port);
+void saa7164_encoder_unregister(struct saa7164_port *port);
+
+/* ----------------------------------------------------------- */
+/* saa7164-vbi.c */
+int saa7164_vbi_register(struct saa7164_port *port);
+void saa7164_vbi_unregister(struct saa7164_port *port);
+
+/* ----------------------------------------------------------- */
+
+extern unsigned int crc_checking;
+
+extern unsigned int saa_debug;
+#define dprintk(level, fmt, arg...)\
+ do { if (saa_debug & level)\
+ printk(KERN_DEBUG "%s: " fmt, dev->name, ## arg);\
+ } while (0)
+
+#define log_warn(fmt, arg...)\
+ do { \
+ printk(KERN_WARNING "%s: " fmt, dev->name, ## arg);\
+ } while (0)
+
+#define log_err(fmt, arg...)\
+ do { \
+ printk(KERN_ERROR "%s: " fmt, dev->name, ## arg);\
+ } while (0)
+
+#define saa7164_readl(reg) readl(dev->lmmio + ((reg) >> 2))
+#define saa7164_writel(reg, value) writel((value), dev->lmmio + ((reg) >> 2))
+
+#define saa7164_readb(reg) readl(dev->bmmio + (reg))
+#define saa7164_writeb(reg, value) writel((value), dev->bmmio + (reg))
+
diff --git a/drivers/media/video/saa717x.c b/drivers/media/video/saa717x.c
new file mode 100644
index 00000000..1e844665
--- /dev/null
+++ b/drivers/media/video/saa717x.c
@@ -0,0 +1,1378 @@
+/*
+ * saa717x - Philips SAA717xHL video decoder driver
+ *
+ * Based on the saa7115 driver
+ *
+ * Changes by Ohta Kyuma <alpha292@bremen.or.jp>
+ * - Apply to SAA717x,NEC uPD64031,uPD64083. (1/31/2004)
+ *
+ * Changes by T.Adachi (tadachi@tadachi-net.com)
+ * - support audio, video scaler etc, and checked the initialize sequence.
+ *
+ * Cleaned up by Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * Note: this is a reversed engineered driver based on captures from
+ * the I2C bus under Windows. This chip is very similar to the saa7134,
+ * though. Unfortunately, this driver is currently only working for NTSC.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+
+MODULE_DESCRIPTION("Philips SAA717x audio/video decoder driver");
+MODULE_AUTHOR("K. Ohta, T. Adachi, Hans Verkuil");
+MODULE_LICENSE("GPL");
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+/*
+ * Generic i2c probe
+ * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
+ */
+
+struct saa717x_state {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+ v4l2_std_id std;
+ int input;
+ int enable;
+ int radio;
+ int playback;
+ int audio;
+ int tuner_audio_mode;
+ int audio_main_mute;
+ int audio_main_vol_r;
+ int audio_main_vol_l;
+ u16 audio_main_bass;
+ u16 audio_main_treble;
+ u16 audio_main_volume;
+ u16 audio_main_balance;
+ int audio_input;
+};
+
+static inline struct saa717x_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct saa717x_state, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct saa717x_state, hdl)->sd;
+}
+
+/* ----------------------------------------------------------------------- */
+
+/* for audio mode */
+#define TUNER_AUDIO_MONO 0 /* LL */
+#define TUNER_AUDIO_STEREO 1 /* LR */
+#define TUNER_AUDIO_LANG1 2 /* LL */
+#define TUNER_AUDIO_LANG2 3 /* RR */
+
+#define SAA717X_NTSC_WIDTH (704)
+#define SAA717X_NTSC_HEIGHT (480)
+
+/* ----------------------------------------------------------------------- */
+
+static int saa717x_write(struct v4l2_subdev *sd, u32 reg, u32 value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct i2c_adapter *adap = client->adapter;
+ int fw_addr = reg == 0x454 || (reg >= 0x464 && reg <= 0x478) || reg == 0x480 || reg == 0x488;
+ unsigned char mm1[6];
+ struct i2c_msg msg;
+
+ msg.flags = 0;
+ msg.addr = client->addr;
+ mm1[0] = (reg >> 8) & 0xff;
+ mm1[1] = reg & 0xff;
+
+ if (fw_addr) {
+ mm1[4] = (value >> 16) & 0xff;
+ mm1[3] = (value >> 8) & 0xff;
+ mm1[2] = value & 0xff;
+ } else {
+ mm1[2] = value & 0xff;
+ }
+ msg.len = fw_addr ? 5 : 3; /* Long Registers have *only* three bytes! */
+ msg.buf = mm1;
+ v4l2_dbg(2, debug, sd, "wrote: reg 0x%03x=%08x\n", reg, value);
+ return i2c_transfer(adap, &msg, 1) == 1;
+}
+
+static void saa717x_write_regs(struct v4l2_subdev *sd, u32 *data)
+{
+ while (data[0] || data[1]) {
+ saa717x_write(sd, data[0], data[1]);
+ data += 2;
+ }
+}
+
+static u32 saa717x_read(struct v4l2_subdev *sd, u32 reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct i2c_adapter *adap = client->adapter;
+ int fw_addr = (reg >= 0x404 && reg <= 0x4b8) || reg == 0x528;
+ unsigned char mm1[2];
+ unsigned char mm2[4] = { 0, 0, 0, 0 };
+ struct i2c_msg msgs[2];
+ u32 value;
+
+ msgs[0].flags = 0;
+ msgs[1].flags = I2C_M_RD;
+ msgs[0].addr = msgs[1].addr = client->addr;
+ mm1[0] = (reg >> 8) & 0xff;
+ mm1[1] = reg & 0xff;
+ msgs[0].len = 2;
+ msgs[0].buf = mm1;
+ msgs[1].len = fw_addr ? 3 : 1; /* Multibyte Registers contains *only* 3 bytes */
+ msgs[1].buf = mm2;
+ i2c_transfer(adap, msgs, 2);
+
+ if (fw_addr)
+ value = (mm2[2] & 0xff) | ((mm2[1] & 0xff) >> 8) | ((mm2[0] & 0xff) >> 16);
+ else
+ value = mm2[0] & 0xff;
+
+ v4l2_dbg(2, debug, sd, "read: reg 0x%03x=0x%08x\n", reg, value);
+ return value;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static u32 reg_init_initialize[] =
+{
+ /* from linux driver */
+ 0x101, 0x008, /* Increment delay */
+
+ 0x103, 0x000, /* Analog input control 2 */
+ 0x104, 0x090, /* Analog input control 3 */
+ 0x105, 0x090, /* Analog input control 4 */
+ 0x106, 0x0eb, /* Horizontal sync start */
+ 0x107, 0x0e0, /* Horizontal sync stop */
+ 0x109, 0x055, /* Luminance control */
+
+ 0x10f, 0x02a, /* Chroma gain control */
+ 0x110, 0x000, /* Chroma control 2 */
+
+ 0x114, 0x045, /* analog/ADC */
+
+ 0x118, 0x040, /* RAW data gain */
+ 0x119, 0x080, /* RAW data offset */
+
+ 0x044, 0x000, /* VBI horizontal input window start (L) TASK A */
+ 0x045, 0x000, /* VBI horizontal input window start (H) TASK A */
+ 0x046, 0x0cf, /* VBI horizontal input window stop (L) TASK A */
+ 0x047, 0x002, /* VBI horizontal input window stop (H) TASK A */
+
+ 0x049, 0x000, /* VBI vertical input window start (H) TASK A */
+
+ 0x04c, 0x0d0, /* VBI horizontal output length (L) TASK A */
+ 0x04d, 0x002, /* VBI horizontal output length (H) TASK A */
+
+ 0x064, 0x080, /* Lumina brightness TASK A */
+ 0x065, 0x040, /* Luminance contrast TASK A */
+ 0x066, 0x040, /* Chroma saturation TASK A */
+ /* 067H: Reserved */
+ 0x068, 0x000, /* VBI horizontal scaling increment (L) TASK A */
+ 0x069, 0x004, /* VBI horizontal scaling increment (H) TASK A */
+ 0x06a, 0x000, /* VBI phase offset TASK A */
+
+ 0x06e, 0x000, /* Horizontal phase offset Luma TASK A */
+ 0x06f, 0x000, /* Horizontal phase offset Chroma TASK A */
+
+ 0x072, 0x000, /* Vertical filter mode TASK A */
+
+ 0x084, 0x000, /* VBI horizontal input window start (L) TAKS B */
+ 0x085, 0x000, /* VBI horizontal input window start (H) TAKS B */
+ 0x086, 0x0cf, /* VBI horizontal input window stop (L) TAKS B */
+ 0x087, 0x002, /* VBI horizontal input window stop (H) TAKS B */
+
+ 0x089, 0x000, /* VBI vertical input window start (H) TAKS B */
+
+ 0x08c, 0x0d0, /* VBI horizontal output length (L) TASK B */
+ 0x08d, 0x002, /* VBI horizontal output length (H) TASK B */
+
+ 0x0a4, 0x080, /* Lumina brightness TASK B */
+ 0x0a5, 0x040, /* Luminance contrast TASK B */
+ 0x0a6, 0x040, /* Chroma saturation TASK B */
+ /* 0A7H reserved */
+ 0x0a8, 0x000, /* VBI horizontal scaling increment (L) TASK B */
+ 0x0a9, 0x004, /* VBI horizontal scaling increment (H) TASK B */
+ 0x0aa, 0x000, /* VBI phase offset TASK B */
+
+ 0x0ae, 0x000, /* Horizontal phase offset Luma TASK B */
+ 0x0af, 0x000, /*Horizontal phase offset Chroma TASK B */
+
+ 0x0b2, 0x000, /* Vertical filter mode TASK B */
+
+ 0x00c, 0x000, /* Start point GREEN path */
+ 0x00d, 0x000, /* Start point BLUE path */
+ 0x00e, 0x000, /* Start point RED path */
+
+ 0x010, 0x010, /* GREEN path gamma curve --- */
+ 0x011, 0x020,
+ 0x012, 0x030,
+ 0x013, 0x040,
+ 0x014, 0x050,
+ 0x015, 0x060,
+ 0x016, 0x070,
+ 0x017, 0x080,
+ 0x018, 0x090,
+ 0x019, 0x0a0,
+ 0x01a, 0x0b0,
+ 0x01b, 0x0c0,
+ 0x01c, 0x0d0,
+ 0x01d, 0x0e0,
+ 0x01e, 0x0f0,
+ 0x01f, 0x0ff, /* --- GREEN path gamma curve */
+
+ 0x020, 0x010, /* BLUE path gamma curve --- */
+ 0x021, 0x020,
+ 0x022, 0x030,
+ 0x023, 0x040,
+ 0x024, 0x050,
+ 0x025, 0x060,
+ 0x026, 0x070,
+ 0x027, 0x080,
+ 0x028, 0x090,
+ 0x029, 0x0a0,
+ 0x02a, 0x0b0,
+ 0x02b, 0x0c0,
+ 0x02c, 0x0d0,
+ 0x02d, 0x0e0,
+ 0x02e, 0x0f0,
+ 0x02f, 0x0ff, /* --- BLUE path gamma curve */
+
+ 0x030, 0x010, /* RED path gamma curve --- */
+ 0x031, 0x020,
+ 0x032, 0x030,
+ 0x033, 0x040,
+ 0x034, 0x050,
+ 0x035, 0x060,
+ 0x036, 0x070,
+ 0x037, 0x080,
+ 0x038, 0x090,
+ 0x039, 0x0a0,
+ 0x03a, 0x0b0,
+ 0x03b, 0x0c0,
+ 0x03c, 0x0d0,
+ 0x03d, 0x0e0,
+ 0x03e, 0x0f0,
+ 0x03f, 0x0ff, /* --- RED path gamma curve */
+
+ 0x109, 0x085, /* Luminance control */
+
+ /**** from app start ****/
+ 0x584, 0x000, /* AGC gain control */
+ 0x585, 0x000, /* Program count */
+ 0x586, 0x003, /* Status reset */
+ 0x588, 0x0ff, /* Number of audio samples (L) */
+ 0x589, 0x00f, /* Number of audio samples (M) */
+ 0x58a, 0x000, /* Number of audio samples (H) */
+ 0x58b, 0x000, /* Audio select */
+ 0x58c, 0x010, /* Audio channel assign1 */
+ 0x58d, 0x032, /* Audio channel assign2 */
+ 0x58e, 0x054, /* Audio channel assign3 */
+ 0x58f, 0x023, /* Audio format */
+ 0x590, 0x000, /* SIF control */
+
+ 0x595, 0x000, /* ?? */
+ 0x596, 0x000, /* ?? */
+ 0x597, 0x000, /* ?? */
+
+ 0x464, 0x00, /* Digital input crossbar1 */
+
+ 0x46c, 0xbbbb10, /* Digital output selection1-3 */
+ 0x470, 0x101010, /* Digital output selection4-6 */
+
+ 0x478, 0x00, /* Sound feature control */
+
+ 0x474, 0x18, /* Softmute control */
+
+ 0x454, 0x0425b9, /* Sound Easy programming(reset) */
+ 0x454, 0x042539, /* Sound Easy programming(reset) */
+
+
+ /**** common setting( of DVD play, including scaler commands) ****/
+ 0x042, 0x003, /* Data path configuration for VBI (TASK A) */
+
+ 0x082, 0x003, /* Data path configuration for VBI (TASK B) */
+
+ 0x108, 0x0f8, /* Sync control */
+ 0x2a9, 0x0fd, /* ??? */
+ 0x102, 0x089, /* select video input "mode 9" */
+ 0x111, 0x000, /* Mode/delay control */
+
+ 0x10e, 0x00a, /* Chroma control 1 */
+
+ 0x594, 0x002, /* SIF, analog I/O select */
+
+ 0x454, 0x0425b9, /* Sound */
+ 0x454, 0x042539,
+
+ 0x111, 0x000,
+ 0x10e, 0x00a,
+ 0x464, 0x000,
+ 0x300, 0x000,
+ 0x301, 0x006,
+ 0x302, 0x000,
+ 0x303, 0x006,
+ 0x308, 0x040,
+ 0x309, 0x000,
+ 0x30a, 0x000,
+ 0x30b, 0x000,
+ 0x000, 0x002,
+ 0x001, 0x000,
+ 0x002, 0x000,
+ 0x003, 0x000,
+ 0x004, 0x033,
+ 0x040, 0x01d,
+ 0x041, 0x001,
+ 0x042, 0x004,
+ 0x043, 0x000,
+ 0x080, 0x01e,
+ 0x081, 0x001,
+ 0x082, 0x004,
+ 0x083, 0x000,
+ 0x190, 0x018,
+ 0x115, 0x000,
+ 0x116, 0x012,
+ 0x117, 0x018,
+ 0x04a, 0x011,
+ 0x08a, 0x011,
+ 0x04b, 0x000,
+ 0x08b, 0x000,
+ 0x048, 0x000,
+ 0x088, 0x000,
+ 0x04e, 0x012,
+ 0x08e, 0x012,
+ 0x058, 0x012,
+ 0x098, 0x012,
+ 0x059, 0x000,
+ 0x099, 0x000,
+ 0x05a, 0x003,
+ 0x09a, 0x003,
+ 0x05b, 0x001,
+ 0x09b, 0x001,
+ 0x054, 0x008,
+ 0x094, 0x008,
+ 0x055, 0x000,
+ 0x095, 0x000,
+ 0x056, 0x0c7,
+ 0x096, 0x0c7,
+ 0x057, 0x002,
+ 0x097, 0x002,
+ 0x0ff, 0x0ff,
+ 0x060, 0x001,
+ 0x0a0, 0x001,
+ 0x061, 0x000,
+ 0x0a1, 0x000,
+ 0x062, 0x000,
+ 0x0a2, 0x000,
+ 0x063, 0x000,
+ 0x0a3, 0x000,
+ 0x070, 0x000,
+ 0x0b0, 0x000,
+ 0x071, 0x004,
+ 0x0b1, 0x004,
+ 0x06c, 0x0e9,
+ 0x0ac, 0x0e9,
+ 0x06d, 0x003,
+ 0x0ad, 0x003,
+ 0x05c, 0x0d0,
+ 0x09c, 0x0d0,
+ 0x05d, 0x002,
+ 0x09d, 0x002,
+ 0x05e, 0x0f2,
+ 0x09e, 0x0f2,
+ 0x05f, 0x000,
+ 0x09f, 0x000,
+ 0x074, 0x000,
+ 0x0b4, 0x000,
+ 0x075, 0x000,
+ 0x0b5, 0x000,
+ 0x076, 0x000,
+ 0x0b6, 0x000,
+ 0x077, 0x000,
+ 0x0b7, 0x000,
+ 0x195, 0x008,
+ 0x0ff, 0x0ff,
+ 0x108, 0x0f8,
+ 0x111, 0x000,
+ 0x10e, 0x00a,
+ 0x2a9, 0x0fd,
+ 0x464, 0x001,
+ 0x454, 0x042135,
+ 0x598, 0x0e7,
+ 0x599, 0x07d,
+ 0x59a, 0x018,
+ 0x59c, 0x066,
+ 0x59d, 0x090,
+ 0x59e, 0x001,
+ 0x584, 0x000,
+ 0x585, 0x000,
+ 0x586, 0x003,
+ 0x588, 0x0ff,
+ 0x589, 0x00f,
+ 0x58a, 0x000,
+ 0x58b, 0x000,
+ 0x58c, 0x010,
+ 0x58d, 0x032,
+ 0x58e, 0x054,
+ 0x58f, 0x023,
+ 0x590, 0x000,
+ 0x595, 0x000,
+ 0x596, 0x000,
+ 0x597, 0x000,
+ 0x464, 0x000,
+ 0x46c, 0xbbbb10,
+ 0x470, 0x101010,
+
+
+ 0x478, 0x000,
+ 0x474, 0x018,
+ 0x454, 0x042135,
+ 0x598, 0x0e7,
+ 0x599, 0x07d,
+ 0x59a, 0x018,
+ 0x59c, 0x066,
+ 0x59d, 0x090,
+ 0x59e, 0x001,
+ 0x584, 0x000,
+ 0x585, 0x000,
+ 0x586, 0x003,
+ 0x588, 0x0ff,
+ 0x589, 0x00f,
+ 0x58a, 0x000,
+ 0x58b, 0x000,
+ 0x58c, 0x010,
+ 0x58d, 0x032,
+ 0x58e, 0x054,
+ 0x58f, 0x023,
+ 0x590, 0x000,
+ 0x595, 0x000,
+ 0x596, 0x000,
+ 0x597, 0x000,
+ 0x464, 0x000,
+ 0x46c, 0xbbbb10,
+ 0x470, 0x101010,
+
+ 0x478, 0x000,
+ 0x474, 0x018,
+ 0x454, 0x042135,
+ 0x598, 0x0e7,
+ 0x599, 0x07d,
+ 0x59a, 0x018,
+ 0x59c, 0x066,
+ 0x59d, 0x090,
+ 0x59e, 0x001,
+ 0x584, 0x000,
+ 0x585, 0x000,
+ 0x586, 0x003,
+ 0x588, 0x0ff,
+ 0x589, 0x00f,
+ 0x58a, 0x000,
+ 0x58b, 0x000,
+ 0x58c, 0x010,
+ 0x58d, 0x032,
+ 0x58e, 0x054,
+ 0x58f, 0x023,
+ 0x590, 0x000,
+ 0x595, 0x000,
+ 0x596, 0x000,
+ 0x597, 0x000,
+ 0x464, 0x000,
+ 0x46c, 0xbbbb10,
+ 0x470, 0x101010,
+ 0x478, 0x000,
+ 0x474, 0x018,
+ 0x454, 0x042135,
+ 0x193, 0x000,
+ 0x300, 0x000,
+ 0x301, 0x006,
+ 0x302, 0x000,
+ 0x303, 0x006,
+ 0x308, 0x040,
+ 0x309, 0x000,
+ 0x30a, 0x000,
+ 0x30b, 0x000,
+ 0x000, 0x002,
+ 0x001, 0x000,
+ 0x002, 0x000,
+ 0x003, 0x000,
+ 0x004, 0x033,
+ 0x040, 0x01d,
+ 0x041, 0x001,
+ 0x042, 0x004,
+ 0x043, 0x000,
+ 0x080, 0x01e,
+ 0x081, 0x001,
+ 0x082, 0x004,
+ 0x083, 0x000,
+ 0x190, 0x018,
+ 0x115, 0x000,
+ 0x116, 0x012,
+ 0x117, 0x018,
+ 0x04a, 0x011,
+ 0x08a, 0x011,
+ 0x04b, 0x000,
+ 0x08b, 0x000,
+ 0x048, 0x000,
+ 0x088, 0x000,
+ 0x04e, 0x012,
+ 0x08e, 0x012,
+ 0x058, 0x012,
+ 0x098, 0x012,
+ 0x059, 0x000,
+ 0x099, 0x000,
+ 0x05a, 0x003,
+ 0x09a, 0x003,
+ 0x05b, 0x001,
+ 0x09b, 0x001,
+ 0x054, 0x008,
+ 0x094, 0x008,
+ 0x055, 0x000,
+ 0x095, 0x000,
+ 0x056, 0x0c7,
+ 0x096, 0x0c7,
+ 0x057, 0x002,
+ 0x097, 0x002,
+ 0x060, 0x001,
+ 0x0a0, 0x001,
+ 0x061, 0x000,
+ 0x0a1, 0x000,
+ 0x062, 0x000,
+ 0x0a2, 0x000,
+ 0x063, 0x000,
+ 0x0a3, 0x000,
+ 0x070, 0x000,
+ 0x0b0, 0x000,
+ 0x071, 0x004,
+ 0x0b1, 0x004,
+ 0x06c, 0x0e9,
+ 0x0ac, 0x0e9,
+ 0x06d, 0x003,
+ 0x0ad, 0x003,
+ 0x05c, 0x0d0,
+ 0x09c, 0x0d0,
+ 0x05d, 0x002,
+ 0x09d, 0x002,
+ 0x05e, 0x0f2,
+ 0x09e, 0x0f2,
+ 0x05f, 0x000,
+ 0x09f, 0x000,
+ 0x074, 0x000,
+ 0x0b4, 0x000,
+ 0x075, 0x000,
+ 0x0b5, 0x000,
+ 0x076, 0x000,
+ 0x0b6, 0x000,
+ 0x077, 0x000,
+ 0x0b7, 0x000,
+ 0x195, 0x008,
+ 0x598, 0x0e7,
+ 0x599, 0x07d,
+ 0x59a, 0x018,
+ 0x59c, 0x066,
+ 0x59d, 0x090,
+ 0x59e, 0x001,
+ 0x584, 0x000,
+ 0x585, 0x000,
+ 0x586, 0x003,
+ 0x588, 0x0ff,
+ 0x589, 0x00f,
+ 0x58a, 0x000,
+ 0x58b, 0x000,
+ 0x58c, 0x010,
+ 0x58d, 0x032,
+ 0x58e, 0x054,
+ 0x58f, 0x023,
+ 0x590, 0x000,
+ 0x595, 0x000,
+ 0x596, 0x000,
+ 0x597, 0x000,
+ 0x464, 0x000,
+ 0x46c, 0xbbbb10,
+ 0x470, 0x101010,
+ 0x478, 0x000,
+ 0x474, 0x018,
+ 0x454, 0x042135,
+ 0x193, 0x0a6,
+ 0x108, 0x0f8,
+ 0x042, 0x003,
+ 0x082, 0x003,
+ 0x454, 0x0425b9,
+ 0x454, 0x042539,
+ 0x193, 0x000,
+ 0x193, 0x0a6,
+ 0x464, 0x000,
+
+ 0, 0
+};
+
+/* Tuner */
+static u32 reg_init_tuner_input[] = {
+ 0x108, 0x0f8, /* Sync control */
+ 0x111, 0x000, /* Mode/delay control */
+ 0x10e, 0x00a, /* Chroma control 1 */
+ 0, 0
+};
+
+/* Composite */
+static u32 reg_init_composite_input[] = {
+ 0x108, 0x0e8, /* Sync control */
+ 0x111, 0x000, /* Mode/delay control */
+ 0x10e, 0x04a, /* Chroma control 1 */
+ 0, 0
+};
+
+/* S-Video */
+static u32 reg_init_svideo_input[] = {
+ 0x108, 0x0e8, /* Sync control */
+ 0x111, 0x000, /* Mode/delay control */
+ 0x10e, 0x04a, /* Chroma control 1 */
+ 0, 0
+};
+
+static u32 reg_set_audio_template[4][2] =
+{
+ { /* for MONO
+ tadachi 6/29 DMA audio output select?
+ Register 0x46c
+ 7-4: DMA2, 3-0: DMA1 ch. DMA4, DMA3 DMA2, DMA1
+ 0: MAIN left, 1: MAIN right
+ 2: AUX1 left, 3: AUX1 right
+ 4: AUX2 left, 5: AUX2 right
+ 6: DPL left, 7: DPL right
+ 8: DPL center, 9: DPL surround
+ A: monitor output, B: digital sense */
+ 0xbbbb00,
+
+ /* tadachi 6/29 DAC and I2S output select?
+ Register 0x470
+ 7-4:DAC right ch. 3-0:DAC left ch.
+ I2S1 right,left I2S2 right,left */
+ 0x00,
+ },
+ { /* for STEREO */
+ 0xbbbb10, 0x101010,
+ },
+ { /* for LANG1 */
+ 0xbbbb00, 0x00,
+ },
+ { /* for LANG2/SAP */
+ 0xbbbb11, 0x111111,
+ }
+};
+
+
+/* Get detected audio flags (from saa7134 driver) */
+static void get_inf_dev_status(struct v4l2_subdev *sd,
+ int *dual_flag, int *stereo_flag)
+{
+ u32 reg_data3;
+
+ static char *stdres[0x20] = {
+ [0x00] = "no standard detected",
+ [0x01] = "B/G (in progress)",
+ [0x02] = "D/K (in progress)",
+ [0x03] = "M (in progress)",
+
+ [0x04] = "B/G A2",
+ [0x05] = "B/G NICAM",
+ [0x06] = "D/K A2 (1)",
+ [0x07] = "D/K A2 (2)",
+ [0x08] = "D/K A2 (3)",
+ [0x09] = "D/K NICAM",
+ [0x0a] = "L NICAM",
+ [0x0b] = "I NICAM",
+
+ [0x0c] = "M Korea",
+ [0x0d] = "M BTSC ",
+ [0x0e] = "M EIAJ",
+
+ [0x0f] = "FM radio / IF 10.7 / 50 deemp",
+ [0x10] = "FM radio / IF 10.7 / 75 deemp",
+ [0x11] = "FM radio / IF sel / 50 deemp",
+ [0x12] = "FM radio / IF sel / 75 deemp",
+
+ [0x13 ... 0x1e] = "unknown",
+ [0x1f] = "??? [in progress]",
+ };
+
+
+ *dual_flag = *stereo_flag = 0;
+
+ /* (demdec status: 0x528) */
+
+ /* read current status */
+ reg_data3 = saa717x_read(sd, 0x0528);
+
+ v4l2_dbg(1, debug, sd, "tvaudio thread status: 0x%x [%s%s%s]\n",
+ reg_data3, stdres[reg_data3 & 0x1f],
+ (reg_data3 & 0x000020) ? ",stereo" : "",
+ (reg_data3 & 0x000040) ? ",dual" : "");
+ v4l2_dbg(1, debug, sd, "detailed status: "
+ "%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s\n",
+ (reg_data3 & 0x000080) ? " A2/EIAJ pilot tone " : "",
+ (reg_data3 & 0x000100) ? " A2/EIAJ dual " : "",
+ (reg_data3 & 0x000200) ? " A2/EIAJ stereo " : "",
+ (reg_data3 & 0x000400) ? " A2/EIAJ noise mute " : "",
+
+ (reg_data3 & 0x000800) ? " BTSC/FM radio pilot " : "",
+ (reg_data3 & 0x001000) ? " SAP carrier " : "",
+ (reg_data3 & 0x002000) ? " BTSC stereo noise mute " : "",
+ (reg_data3 & 0x004000) ? " SAP noise mute " : "",
+ (reg_data3 & 0x008000) ? " VDSP " : "",
+
+ (reg_data3 & 0x010000) ? " NICST " : "",
+ (reg_data3 & 0x020000) ? " NICDU " : "",
+ (reg_data3 & 0x040000) ? " NICAM muted " : "",
+ (reg_data3 & 0x080000) ? " NICAM reserve sound " : "",
+
+ (reg_data3 & 0x100000) ? " init done " : "");
+
+ if (reg_data3 & 0x000220) {
+ v4l2_dbg(1, debug, sd, "ST!!!\n");
+ *stereo_flag = 1;
+ }
+
+ if (reg_data3 & 0x000140) {
+ v4l2_dbg(1, debug, sd, "DUAL!!!\n");
+ *dual_flag = 1;
+ }
+}
+
+/* regs write to set audio mode */
+static void set_audio_mode(struct v4l2_subdev *sd, int audio_mode)
+{
+ v4l2_dbg(1, debug, sd, "writing registers to set audio mode by set %d\n",
+ audio_mode);
+
+ saa717x_write(sd, 0x46c, reg_set_audio_template[audio_mode][0]);
+ saa717x_write(sd, 0x470, reg_set_audio_template[audio_mode][1]);
+}
+
+/* write regs to set audio volume, bass and treble */
+static int set_audio_regs(struct v4l2_subdev *sd,
+ struct saa717x_state *decoder)
+{
+ u8 mute = 0xac; /* -84 dB */
+ u32 val;
+ unsigned int work_l, work_r;
+
+ /* set SIF analog I/O select */
+ saa717x_write(sd, 0x0594, decoder->audio_input);
+ v4l2_dbg(1, debug, sd, "set audio input %d\n",
+ decoder->audio_input);
+
+ /* normalize ( 65535 to 0 -> 24 to -40 (not -84)) */
+ work_l = (min(65536 - decoder->audio_main_balance, 32768) * decoder->audio_main_volume) / 32768;
+ work_r = (min(decoder->audio_main_balance, (u16)32768) * decoder->audio_main_volume) / 32768;
+ decoder->audio_main_vol_l = (long)work_l * (24 - (-40)) / 65535 - 40;
+ decoder->audio_main_vol_r = (long)work_r * (24 - (-40)) / 65535 - 40;
+
+ /* set main volume */
+ /* main volume L[7-0],R[7-0],0x00 24=24dB,-83dB, -84(mute) */
+ /* def:0dB->6dB(MPG600GR) */
+ /* if mute is on, set mute */
+ if (decoder->audio_main_mute) {
+ val = mute | (mute << 8);
+ } else {
+ val = (u8)decoder->audio_main_vol_l |
+ ((u8)decoder->audio_main_vol_r << 8);
+ }
+
+ saa717x_write(sd, 0x480, val);
+
+ /* set bass and treble */
+ val = decoder->audio_main_bass & 0x1f;
+ val |= (decoder->audio_main_treble & 0x1f) << 5;
+ saa717x_write(sd, 0x488, val);
+ return 0;
+}
+
+/********** scaling staff ***********/
+static void set_h_prescale(struct v4l2_subdev *sd,
+ int task, int prescale)
+{
+ static const struct {
+ int xpsc;
+ int xacl;
+ int xc2_1;
+ int xdcg;
+ int vpfy;
+ } vals[] = {
+ /* XPSC XACL XC2_1 XDCG VPFY */
+ { 1, 0, 0, 0, 0 },
+ { 2, 2, 1, 2, 2 },
+ { 3, 4, 1, 3, 2 },
+ { 4, 8, 1, 4, 2 },
+ { 5, 8, 1, 4, 2 },
+ { 6, 8, 1, 4, 3 },
+ { 7, 8, 1, 4, 3 },
+ { 8, 15, 0, 4, 3 },
+ { 9, 15, 0, 4, 3 },
+ { 10, 16, 1, 5, 3 },
+ };
+ static const int count = ARRAY_SIZE(vals);
+ int i, task_shift;
+
+ task_shift = task * 0x40;
+ for (i = 0; i < count; i++)
+ if (vals[i].xpsc == prescale)
+ break;
+ if (i == count)
+ return;
+
+ /* horizonal prescaling */
+ saa717x_write(sd, 0x60 + task_shift, vals[i].xpsc);
+ /* accumulation length */
+ saa717x_write(sd, 0x61 + task_shift, vals[i].xacl);
+ /* level control */
+ saa717x_write(sd, 0x62 + task_shift,
+ (vals[i].xc2_1 << 3) | vals[i].xdcg);
+ /*FIR prefilter control */
+ saa717x_write(sd, 0x63 + task_shift,
+ (vals[i].vpfy << 2) | vals[i].vpfy);
+}
+
+/********** scaling staff ***********/
+static void set_v_scale(struct v4l2_subdev *sd, int task, int yscale)
+{
+ int task_shift;
+
+ task_shift = task * 0x40;
+ /* Vertical scaling ratio (LOW) */
+ saa717x_write(sd, 0x70 + task_shift, yscale & 0xff);
+ /* Vertical scaling ratio (HI) */
+ saa717x_write(sd, 0x71 + task_shift, yscale >> 8);
+}
+
+static int saa717x_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ struct saa717x_state *state = to_state(sd);
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ saa717x_write(sd, 0x10a, ctrl->val);
+ return 0;
+
+ case V4L2_CID_CONTRAST:
+ saa717x_write(sd, 0x10b, ctrl->val);
+ return 0;
+
+ case V4L2_CID_SATURATION:
+ saa717x_write(sd, 0x10c, ctrl->val);
+ return 0;
+
+ case V4L2_CID_HUE:
+ saa717x_write(sd, 0x10d, ctrl->val);
+ return 0;
+
+ case V4L2_CID_AUDIO_MUTE:
+ state->audio_main_mute = ctrl->val;
+ break;
+
+ case V4L2_CID_AUDIO_VOLUME:
+ state->audio_main_volume = ctrl->val;
+ break;
+
+ case V4L2_CID_AUDIO_BALANCE:
+ state->audio_main_balance = ctrl->val;
+ break;
+
+ case V4L2_CID_AUDIO_TREBLE:
+ state->audio_main_treble = ctrl->val;
+ break;
+
+ case V4L2_CID_AUDIO_BASS:
+ state->audio_main_bass = ctrl->val;
+ break;
+
+ default:
+ return 0;
+ }
+ set_audio_regs(sd, state);
+ return 0;
+}
+
+static int saa717x_s_video_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct saa717x_state *decoder = to_state(sd);
+ int is_tuner = input & 0x80; /* tuner input flag */
+
+ input &= 0x7f;
+
+ v4l2_dbg(1, debug, sd, "decoder set input (%d)\n", input);
+ /* inputs from 0-9 are available*/
+ /* saa717x have mode0-mode9 but mode5 is reserved. */
+ if (input > 9 || input == 5)
+ return -EINVAL;
+
+ if (decoder->input != input) {
+ int input_line = input;
+
+ decoder->input = input_line;
+ v4l2_dbg(1, debug, sd, "now setting %s input %d\n",
+ input_line >= 6 ? "S-Video" : "Composite",
+ input_line);
+
+ /* select mode */
+ saa717x_write(sd, 0x102,
+ (saa717x_read(sd, 0x102) & 0xf0) |
+ input_line);
+
+ /* bypass chrominance trap for modes 6..9 */
+ saa717x_write(sd, 0x109,
+ (saa717x_read(sd, 0x109) & 0x7f) |
+ (input_line < 6 ? 0x0 : 0x80));
+
+ /* change audio_mode */
+ if (is_tuner) {
+ /* tuner */
+ set_audio_mode(sd, decoder->tuner_audio_mode);
+ } else {
+ /* Force to STEREO mode if Composite or
+ * S-Video were chosen */
+ set_audio_mode(sd, TUNER_AUDIO_STEREO);
+ }
+ /* change initialize procedure (Composite/S-Video) */
+ if (is_tuner)
+ saa717x_write_regs(sd, reg_init_tuner_input);
+ else if (input_line >= 6)
+ saa717x_write_regs(sd, reg_init_svideo_input);
+ else
+ saa717x_write_regs(sd, reg_init_composite_input);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int saa717x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = saa717x_read(sd, reg->reg);
+ reg->size = 1;
+ return 0;
+}
+
+static int saa717x_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u16 addr = reg->reg & 0xffff;
+ u8 val = reg->val & 0xff;
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ saa717x_write(sd, addr, val);
+ return 0;
+}
+#endif
+
+static int saa717x_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt)
+{
+ int prescale, h_scale, v_scale;
+
+ v4l2_dbg(1, debug, sd, "decoder set size\n");
+
+ if (fmt->code != V4L2_MBUS_FMT_FIXED)
+ return -EINVAL;
+
+ /* FIXME need better bounds checking here */
+ if (fmt->width < 1 || fmt->width > 1440)
+ return -EINVAL;
+ if (fmt->height < 1 || fmt->height > 960)
+ return -EINVAL;
+
+ fmt->field = V4L2_FIELD_INTERLACED;
+ fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+ /* scaling setting */
+ /* NTSC and interlace only */
+ prescale = SAA717X_NTSC_WIDTH / fmt->width;
+ if (prescale == 0)
+ prescale = 1;
+ h_scale = 1024 * SAA717X_NTSC_WIDTH / prescale / fmt->width;
+ /* interlace */
+ v_scale = 512 * 2 * SAA717X_NTSC_HEIGHT / fmt->height;
+
+ /* Horizontal prescaling etc */
+ set_h_prescale(sd, 0, prescale);
+ set_h_prescale(sd, 1, prescale);
+
+ /* Horizontal scaling increment */
+ /* TASK A */
+ saa717x_write(sd, 0x6C, (u8)(h_scale & 0xFF));
+ saa717x_write(sd, 0x6D, (u8)((h_scale >> 8) & 0xFF));
+ /* TASK B */
+ saa717x_write(sd, 0xAC, (u8)(h_scale & 0xFF));
+ saa717x_write(sd, 0xAD, (u8)((h_scale >> 8) & 0xFF));
+
+ /* Vertical prescaling etc */
+ set_v_scale(sd, 0, v_scale);
+ set_v_scale(sd, 1, v_scale);
+
+ /* set video output size */
+ /* video number of pixels at output */
+ /* TASK A */
+ saa717x_write(sd, 0x5C, (u8)(fmt->width & 0xFF));
+ saa717x_write(sd, 0x5D, (u8)((fmt->width >> 8) & 0xFF));
+ /* TASK B */
+ saa717x_write(sd, 0x9C, (u8)(fmt->width & 0xFF));
+ saa717x_write(sd, 0x9D, (u8)((fmt->width >> 8) & 0xFF));
+
+ /* video number of lines at output */
+ /* TASK A */
+ saa717x_write(sd, 0x5E, (u8)(fmt->height & 0xFF));
+ saa717x_write(sd, 0x5F, (u8)((fmt->height >> 8) & 0xFF));
+ /* TASK B */
+ saa717x_write(sd, 0x9E, (u8)(fmt->height & 0xFF));
+ saa717x_write(sd, 0x9F, (u8)((fmt->height >> 8) & 0xFF));
+ return 0;
+}
+
+static int saa717x_s_radio(struct v4l2_subdev *sd)
+{
+ struct saa717x_state *decoder = to_state(sd);
+
+ decoder->radio = 1;
+ return 0;
+}
+
+static int saa717x_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct saa717x_state *decoder = to_state(sd);
+
+ v4l2_dbg(1, debug, sd, "decoder set norm ");
+ v4l2_dbg(1, debug, sd, "(not yet implementd)\n");
+
+ decoder->radio = 0;
+ decoder->std = std;
+ return 0;
+}
+
+static int saa717x_s_audio_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct saa717x_state *decoder = to_state(sd);
+
+ if (input < 3) { /* FIXME! --tadachi */
+ decoder->audio_input = input;
+ v4l2_dbg(1, debug, sd,
+ "set decoder audio input to %d\n",
+ decoder->audio_input);
+ set_audio_regs(sd, decoder);
+ return 0;
+ }
+ return -ERANGE;
+}
+
+static int saa717x_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct saa717x_state *decoder = to_state(sd);
+
+ v4l2_dbg(1, debug, sd, "decoder %s output\n",
+ enable ? "enable" : "disable");
+ decoder->enable = enable;
+ saa717x_write(sd, 0x193, enable ? 0xa6 : 0x26);
+ return 0;
+}
+
+/* change audio mode */
+static int saa717x_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct saa717x_state *decoder = to_state(sd);
+ int audio_mode;
+ char *mes[4] = {
+ "MONO", "STEREO", "LANG1", "LANG2/SAP"
+ };
+
+ audio_mode = TUNER_AUDIO_STEREO;
+
+ switch (vt->audmode) {
+ case V4L2_TUNER_MODE_MONO:
+ audio_mode = TUNER_AUDIO_MONO;
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ audio_mode = TUNER_AUDIO_STEREO;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ audio_mode = TUNER_AUDIO_LANG2;
+ break;
+ case V4L2_TUNER_MODE_LANG1:
+ audio_mode = TUNER_AUDIO_LANG1;
+ break;
+ }
+
+ v4l2_dbg(1, debug, sd, "change audio mode to %s\n",
+ mes[audio_mode]);
+ decoder->tuner_audio_mode = audio_mode;
+ /* The registers are not changed here. */
+ /* See DECODER_ENABLE_OUTPUT section. */
+ set_audio_mode(sd, decoder->tuner_audio_mode);
+ return 0;
+}
+
+static int saa717x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct saa717x_state *decoder = to_state(sd);
+ int dual_f, stereo_f;
+
+ if (decoder->radio)
+ return 0;
+ get_inf_dev_status(sd, &dual_f, &stereo_f);
+
+ v4l2_dbg(1, debug, sd, "DETECT==st:%d dual:%d\n",
+ stereo_f, dual_f);
+
+ /* mono */
+ if ((dual_f == 0) && (stereo_f == 0)) {
+ vt->rxsubchans = V4L2_TUNER_SUB_MONO;
+ v4l2_dbg(1, debug, sd, "DETECT==MONO\n");
+ }
+
+ /* stereo */
+ if (stereo_f == 1) {
+ if (vt->audmode == V4L2_TUNER_MODE_STEREO ||
+ vt->audmode == V4L2_TUNER_MODE_LANG1) {
+ vt->rxsubchans = V4L2_TUNER_SUB_STEREO;
+ v4l2_dbg(1, debug, sd, "DETECT==ST(ST)\n");
+ } else {
+ vt->rxsubchans = V4L2_TUNER_SUB_MONO;
+ v4l2_dbg(1, debug, sd, "DETECT==ST(MONO)\n");
+ }
+ }
+
+ /* dual */
+ if (dual_f == 1) {
+ if (vt->audmode == V4L2_TUNER_MODE_LANG2) {
+ vt->rxsubchans = V4L2_TUNER_SUB_LANG2 | V4L2_TUNER_SUB_MONO;
+ v4l2_dbg(1, debug, sd, "DETECT==DUAL1\n");
+ } else {
+ vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_MONO;
+ v4l2_dbg(1, debug, sd, "DETECT==DUAL2\n");
+ }
+ }
+ return 0;
+}
+
+static int saa717x_log_status(struct v4l2_subdev *sd)
+{
+ struct saa717x_state *state = to_state(sd);
+
+ v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_ctrl_ops saa717x_ctrl_ops = {
+ .s_ctrl = saa717x_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops saa717x_core_ops = {
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = saa717x_g_register,
+ .s_register = saa717x_s_register,
+#endif
+ .s_std = saa717x_s_std,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+ .log_status = saa717x_log_status,
+};
+
+static const struct v4l2_subdev_tuner_ops saa717x_tuner_ops = {
+ .g_tuner = saa717x_g_tuner,
+ .s_tuner = saa717x_s_tuner,
+ .s_radio = saa717x_s_radio,
+};
+
+static const struct v4l2_subdev_video_ops saa717x_video_ops = {
+ .s_routing = saa717x_s_video_routing,
+ .s_mbus_fmt = saa717x_s_mbus_fmt,
+ .s_stream = saa717x_s_stream,
+};
+
+static const struct v4l2_subdev_audio_ops saa717x_audio_ops = {
+ .s_routing = saa717x_s_audio_routing,
+};
+
+static const struct v4l2_subdev_ops saa717x_ops = {
+ .core = &saa717x_core_ops,
+ .tuner = &saa717x_tuner_ops,
+ .audio = &saa717x_audio_ops,
+ .video = &saa717x_video_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+
+/* i2c implementation */
+
+/* ----------------------------------------------------------------------- */
+static int saa717x_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct saa717x_state *decoder;
+ struct v4l2_ctrl_handler *hdl;
+ struct v4l2_subdev *sd;
+ u8 id = 0;
+ char *p = "";
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ decoder = kzalloc(sizeof(struct saa717x_state), GFP_KERNEL);
+ if (decoder == NULL)
+ return -ENOMEM;
+
+ sd = &decoder->sd;
+ v4l2_i2c_subdev_init(sd, client, &saa717x_ops);
+
+ if (saa717x_write(sd, 0x5a4, 0xfe) &&
+ saa717x_write(sd, 0x5a5, 0x0f) &&
+ saa717x_write(sd, 0x5a6, 0x00) &&
+ saa717x_write(sd, 0x5a7, 0x01))
+ id = saa717x_read(sd, 0x5a0);
+ if (id != 0xc2 && id != 0x32 && id != 0xf2 && id != 0x6c) {
+ v4l2_dbg(1, debug, sd, "saa717x not found (id=%02x)\n", id);
+ kfree(decoder);
+ return -ENODEV;
+ }
+ if (id == 0xc2)
+ p = "saa7173";
+ else if (id == 0x32)
+ p = "saa7174A";
+ else if (id == 0x6c)
+ p = "saa7174HL";
+ else
+ p = "saa7171";
+ v4l2_info(sd, "%s found @ 0x%x (%s)\n", p,
+ client->addr << 1, client->adapter->name);
+
+ hdl = &decoder->hdl;
+ v4l2_ctrl_handler_init(hdl, 9);
+ /* add in ascending ID order */
+ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 68);
+ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 255, 1, 64);
+ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+ V4L2_CID_HUE, -128, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+ V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 42000);
+ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+ V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768);
+ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+ V4L2_CID_AUDIO_BASS, -16, 15, 1, 0);
+ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+ V4L2_CID_AUDIO_TREBLE, -16, 15, 1, 0);
+ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+ sd->ctrl_handler = hdl;
+ if (hdl->error) {
+ int err = hdl->error;
+
+ v4l2_ctrl_handler_free(hdl);
+ kfree(decoder);
+ return err;
+ }
+
+ decoder->std = V4L2_STD_NTSC;
+ decoder->input = -1;
+ decoder->enable = 1;
+
+ /* FIXME!! */
+ decoder->playback = 0; /* initially capture mode used */
+ decoder->audio = 1; /* DECODER_AUDIO_48_KHZ */
+
+ decoder->audio_input = 2; /* FIXME!! */
+
+ decoder->tuner_audio_mode = TUNER_AUDIO_STEREO;
+ /* set volume, bass and treble */
+ decoder->audio_main_vol_l = 6;
+ decoder->audio_main_vol_r = 6;
+
+ v4l2_dbg(1, debug, sd, "writing init values\n");
+
+ /* FIXME!! */
+ saa717x_write_regs(sd, reg_init_initialize);
+
+ v4l2_ctrl_handler_setup(hdl);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(2*HZ);
+ return 0;
+}
+
+static int saa717x_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+ kfree(to_state(sd));
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct i2c_device_id saa717x_id[] = {
+ { "saa717x", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, saa717x_id);
+
+static struct i2c_driver saa717x_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "saa717x",
+ },
+ .probe = saa717x_probe,
+ .remove = saa717x_remove,
+ .id_table = saa717x_id,
+};
+
+module_i2c_driver(saa717x_driver);
diff --git a/drivers/media/video/saa7185.c b/drivers/media/video/saa7185.c
new file mode 100644
index 00000000..2c6b65c7
--- /dev/null
+++ b/drivers/media/video/saa7185.c
@@ -0,0 +1,377 @@
+/*
+ * saa7185 - Philips SAA7185B video encoder driver version 0.0.3
+ *
+ * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+ *
+ * Slight changes for video timing and attachment output by
+ * Wolfgang Scherr <scherr@net4you.net>
+ *
+ * Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * - moved over to linux>=2.4.x i2c protocol (1/1/2003)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/ioctl.h>
+#include <asm/uaccess.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+
+MODULE_DESCRIPTION("Philips SAA7185 video encoder driver");
+MODULE_AUTHOR("Dave Perks");
+MODULE_LICENSE("GPL");
+
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+
+/* ----------------------------------------------------------------------- */
+
+struct saa7185 {
+ struct v4l2_subdev sd;
+ unsigned char reg[128];
+
+ v4l2_std_id norm;
+};
+
+static inline struct saa7185 *to_saa7185(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct saa7185, sd);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static inline int saa7185_read(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_read_byte(client);
+}
+
+static int saa7185_write(struct v4l2_subdev *sd, u8 reg, u8 value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct saa7185 *encoder = to_saa7185(sd);
+
+ v4l2_dbg(1, debug, sd, "%02x set to %02x\n", reg, value);
+ encoder->reg[reg] = value;
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static int saa7185_write_block(struct v4l2_subdev *sd,
+ const u8 *data, unsigned int len)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct saa7185 *encoder = to_saa7185(sd);
+ int ret = -1;
+ u8 reg;
+
+ /* the adv7175 has an autoincrement function, use it if
+ * the adapter understands raw I2C */
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ /* do raw I2C, not smbus compatible */
+ u8 block_data[32];
+ int block_len;
+
+ while (len >= 2) {
+ block_len = 0;
+ block_data[block_len++] = reg = data[0];
+ do {
+ block_data[block_len++] =
+ encoder->reg[reg++] = data[1];
+ len -= 2;
+ data += 2;
+ } while (len >= 2 && data[0] == reg && block_len < 32);
+ ret = i2c_master_send(client, block_data, block_len);
+ if (ret < 0)
+ break;
+ }
+ } else {
+ /* do some slow I2C emulation kind of thing */
+ while (len >= 2) {
+ reg = *data++;
+ ret = saa7185_write(sd, reg, *data++);
+ if (ret < 0)
+ break;
+ len -= 2;
+ }
+ }
+
+ return ret;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const unsigned char init_common[] = {
+ 0x3a, 0x0f, /* CBENB=0, V656=0, VY2C=1,
+ * YUV2C=1, MY2C=1, MUV2C=1 */
+
+ 0x42, 0x6b, /* OVLY0=107 */
+ 0x43, 0x00, /* OVLU0=0 white */
+ 0x44, 0x00, /* OVLV0=0 */
+ 0x45, 0x22, /* OVLY1=34 */
+ 0x46, 0xac, /* OVLU1=172 yellow */
+ 0x47, 0x0e, /* OVLV1=14 */
+ 0x48, 0x03, /* OVLY2=3 */
+ 0x49, 0x1d, /* OVLU2=29 cyan */
+ 0x4a, 0xac, /* OVLV2=172 */
+ 0x4b, 0xf0, /* OVLY3=240 */
+ 0x4c, 0xc8, /* OVLU3=200 green */
+ 0x4d, 0xb9, /* OVLV3=185 */
+ 0x4e, 0xd4, /* OVLY4=212 */
+ 0x4f, 0x38, /* OVLU4=56 magenta */
+ 0x50, 0x47, /* OVLV4=71 */
+ 0x51, 0xc1, /* OVLY5=193 */
+ 0x52, 0xe3, /* OVLU5=227 red */
+ 0x53, 0x54, /* OVLV5=84 */
+ 0x54, 0xa3, /* OVLY6=163 */
+ 0x55, 0x54, /* OVLU6=84 blue */
+ 0x56, 0xf2, /* OVLV6=242 */
+ 0x57, 0x90, /* OVLY7=144 */
+ 0x58, 0x00, /* OVLU7=0 black */
+ 0x59, 0x00, /* OVLV7=0 */
+
+ 0x5a, 0x00, /* CHPS=0 */
+ 0x5b, 0x76, /* GAINU=118 */
+ 0x5c, 0xa5, /* GAINV=165 */
+ 0x5d, 0x3c, /* BLCKL=60 */
+ 0x5e, 0x3a, /* BLNNL=58 */
+ 0x5f, 0x3a, /* CCRS=0, BLNVB=58 */
+ 0x60, 0x00, /* NULL */
+
+ /* 0x61 - 0x66 set according to norm */
+
+ 0x67, 0x00, /* 0 : caption 1st byte odd field */
+ 0x68, 0x00, /* 0 : caption 2nd byte odd field */
+ 0x69, 0x00, /* 0 : caption 1st byte even field */
+ 0x6a, 0x00, /* 0 : caption 2nd byte even field */
+
+ 0x6b, 0x91, /* MODIN=2, PCREF=0, SCCLN=17 */
+ 0x6c, 0x20, /* SRCV1=0, TRCV2=1, ORCV1=0, PRCV1=0,
+ * CBLF=0, ORCV2=0, PRCV2=0 */
+ 0x6d, 0x00, /* SRCM1=0, CCEN=0 */
+
+ 0x6e, 0x0e, /* HTRIG=0x005, approx. centered, at
+ * least for PAL */
+ 0x6f, 0x00, /* HTRIG upper bits */
+ 0x70, 0x20, /* PHRES=0, SBLN=1, VTRIG=0 */
+
+ /* The following should not be needed */
+
+ 0x71, 0x15, /* BMRQ=0x115 */
+ 0x72, 0x90, /* EMRQ=0x690 */
+ 0x73, 0x61, /* EMRQ=0x690, BMRQ=0x115 */
+ 0x74, 0x00, /* NULL */
+ 0x75, 0x00, /* NULL */
+ 0x76, 0x00, /* NULL */
+ 0x77, 0x15, /* BRCV=0x115 */
+ 0x78, 0x90, /* ERCV=0x690 */
+ 0x79, 0x61, /* ERCV=0x690, BRCV=0x115 */
+
+ /* Field length controls */
+
+ 0x7a, 0x70, /* FLC=0 */
+
+ /* The following should not be needed if SBLN = 1 */
+
+ 0x7b, 0x16, /* FAL=22 */
+ 0x7c, 0x35, /* LAL=244 */
+ 0x7d, 0x20, /* LAL=244, FAL=22 */
+};
+
+static const unsigned char init_pal[] = {
+ 0x61, 0x1e, /* FISE=0, PAL=1, SCBW=1, RTCE=1,
+ * YGS=1, INPI=0, DOWN=0 */
+ 0x62, 0xc8, /* DECTYP=1, BSTA=72 */
+ 0x63, 0xcb, /* FSC0 */
+ 0x64, 0x8a, /* FSC1 */
+ 0x65, 0x09, /* FSC2 */
+ 0x66, 0x2a, /* FSC3 */
+};
+
+static const unsigned char init_ntsc[] = {
+ 0x61, 0x1d, /* FISE=1, PAL=0, SCBW=1, RTCE=1,
+ * YGS=1, INPI=0, DOWN=0 */
+ 0x62, 0xe6, /* DECTYP=1, BSTA=102 */
+ 0x63, 0x1f, /* FSC0 */
+ 0x64, 0x7c, /* FSC1 */
+ 0x65, 0xf0, /* FSC2 */
+ 0x66, 0x21, /* FSC3 */
+};
+
+
+static int saa7185_init(struct v4l2_subdev *sd, u32 val)
+{
+ struct saa7185 *encoder = to_saa7185(sd);
+
+ saa7185_write_block(sd, init_common, sizeof(init_common));
+ if (encoder->norm & V4L2_STD_NTSC)
+ saa7185_write_block(sd, init_ntsc, sizeof(init_ntsc));
+ else
+ saa7185_write_block(sd, init_pal, sizeof(init_pal));
+ return 0;
+}
+
+static int saa7185_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct saa7185 *encoder = to_saa7185(sd);
+
+ if (std & V4L2_STD_NTSC)
+ saa7185_write_block(sd, init_ntsc, sizeof(init_ntsc));
+ else if (std & V4L2_STD_PAL)
+ saa7185_write_block(sd, init_pal, sizeof(init_pal));
+ else
+ return -EINVAL;
+ encoder->norm = std;
+ return 0;
+}
+
+static int saa7185_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct saa7185 *encoder = to_saa7185(sd);
+
+ /* RJ: input = 0: input is from SA7111
+ input = 1: input is from ZR36060 */
+
+ switch (input) {
+ case 0:
+ /* turn off colorbar */
+ saa7185_write(sd, 0x3a, 0x0f);
+ /* Switch RTCE to 1 */
+ saa7185_write(sd, 0x61, (encoder->reg[0x61] & 0xf7) | 0x08);
+ saa7185_write(sd, 0x6e, 0x01);
+ break;
+
+ case 1:
+ /* turn off colorbar */
+ saa7185_write(sd, 0x3a, 0x0f);
+ /* Switch RTCE to 0 */
+ saa7185_write(sd, 0x61, (encoder->reg[0x61] & 0xf7) | 0x00);
+ /* SW: a slight sync problem... */
+ saa7185_write(sd, 0x6e, 0x00);
+ break;
+
+ case 2:
+ /* turn on colorbar */
+ saa7185_write(sd, 0x3a, 0x8f);
+ /* Switch RTCE to 0 */
+ saa7185_write(sd, 0x61, (encoder->reg[0x61] & 0xf7) | 0x08);
+ /* SW: a slight sync problem... */
+ saa7185_write(sd, 0x6e, 0x01);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int saa7185_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7185, 0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops saa7185_core_ops = {
+ .g_chip_ident = saa7185_g_chip_ident,
+ .init = saa7185_init,
+};
+
+static const struct v4l2_subdev_video_ops saa7185_video_ops = {
+ .s_std_output = saa7185_s_std_output,
+ .s_routing = saa7185_s_routing,
+};
+
+static const struct v4l2_subdev_ops saa7185_ops = {
+ .core = &saa7185_core_ops,
+ .video = &saa7185_video_ops,
+};
+
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7185_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int i;
+ struct saa7185 *encoder;
+ struct v4l2_subdev *sd;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ encoder = kzalloc(sizeof(struct saa7185), GFP_KERNEL);
+ if (encoder == NULL)
+ return -ENOMEM;
+ encoder->norm = V4L2_STD_NTSC;
+ sd = &encoder->sd;
+ v4l2_i2c_subdev_init(sd, client, &saa7185_ops);
+
+ i = saa7185_write_block(sd, init_common, sizeof(init_common));
+ if (i >= 0)
+ i = saa7185_write_block(sd, init_ntsc, sizeof(init_ntsc));
+ if (i < 0)
+ v4l2_dbg(1, debug, sd, "init error %d\n", i);
+ else
+ v4l2_dbg(1, debug, sd, "revision 0x%x\n",
+ saa7185_read(sd) >> 5);
+ return 0;
+}
+
+static int saa7185_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct saa7185 *encoder = to_saa7185(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ /* SW: output off is active */
+ saa7185_write(sd, 0x61, (encoder->reg[0x61]) | 0x40);
+ kfree(encoder);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct i2c_device_id saa7185_id[] = {
+ { "saa7185", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, saa7185_id);
+
+static struct i2c_driver saa7185_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "saa7185",
+ },
+ .probe = saa7185_probe,
+ .remove = saa7185_remove,
+ .id_table = saa7185_id,
+};
+
+module_i2c_driver(saa7185_driver);
diff --git a/drivers/media/video/saa7191.c b/drivers/media/video/saa7191.c
new file mode 100644
index 00000000..d7d1670e
--- /dev/null
+++ b/drivers/media/video/saa7191.c
@@ -0,0 +1,659 @@
+/*
+ * saa7191.c - Philips SAA7191 video decoder driver
+ *
+ * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
+ * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+
+#include "saa7191.h"
+
+#define SAA7191_MODULE_VERSION "0.0.5"
+
+MODULE_DESCRIPTION("Philips SAA7191 video decoder driver");
+MODULE_VERSION(SAA7191_MODULE_VERSION);
+MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>");
+MODULE_LICENSE("GPL");
+
+
+// #define SAA7191_DEBUG
+
+#ifdef SAA7191_DEBUG
+#define dprintk(x...) printk("SAA7191: " x);
+#else
+#define dprintk(x...)
+#endif
+
+#define SAA7191_SYNC_COUNT 30
+#define SAA7191_SYNC_DELAY 100 /* milliseconds */
+
+struct saa7191 {
+ struct v4l2_subdev sd;
+
+ /* the register values are stored here as the actual
+ * I2C-registers are write-only */
+ u8 reg[25];
+
+ int input;
+ v4l2_std_id norm;
+};
+
+static inline struct saa7191 *to_saa7191(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct saa7191, sd);
+}
+
+static const u8 initseq[] = {
+ 0, /* Subaddress */
+
+ 0x50, /* (0x50) SAA7191_REG_IDEL */
+
+ /* 50 Hz signal timing */
+ 0x30, /* (0x30) SAA7191_REG_HSYB */
+ 0x00, /* (0x00) SAA7191_REG_HSYS */
+ 0xe8, /* (0xe8) SAA7191_REG_HCLB */
+ 0xb6, /* (0xb6) SAA7191_REG_HCLS */
+ 0xf4, /* (0xf4) SAA7191_REG_HPHI */
+
+ /* control */
+ SAA7191_LUMA_APER_1, /* (0x01) SAA7191_REG_LUMA - CVBS mode */
+ 0x00, /* (0x00) SAA7191_REG_HUEC */
+ 0xf8, /* (0xf8) SAA7191_REG_CKTQ */
+ 0xf8, /* (0xf8) SAA7191_REG_CKTS */
+ 0x90, /* (0x90) SAA7191_REG_PLSE */
+ 0x90, /* (0x90) SAA7191_REG_SESE */
+ 0x00, /* (0x00) SAA7191_REG_GAIN */
+ SAA7191_STDC_NFEN | SAA7191_STDC_HRMV, /* (0x0c) SAA7191_REG_STDC
+ * - not SECAM,
+ * slow time constant */
+ SAA7191_IOCK_OEDC | SAA7191_IOCK_OEHS | SAA7191_IOCK_OEVS
+ | SAA7191_IOCK_OEDY, /* (0x78) SAA7191_REG_IOCK
+ * - chroma from CVBS, GPSW1 & 2 off */
+ SAA7191_CTL3_AUFD | SAA7191_CTL3_SCEN | SAA7191_CTL3_OFTS
+ | SAA7191_CTL3_YDEL0, /* (0x99) SAA7191_REG_CTL3
+ * - automatic field detection */
+ 0x00, /* (0x00) SAA7191_REG_CTL4 */
+ 0x2c, /* (0x2c) SAA7191_REG_CHCV - PAL nominal value */
+ 0x00, /* unused */
+ 0x00, /* unused */
+
+ /* 60 Hz signal timing */
+ 0x34, /* (0x34) SAA7191_REG_HS6B */
+ 0x0a, /* (0x0a) SAA7191_REG_HS6S */
+ 0xf4, /* (0xf4) SAA7191_REG_HC6B */
+ 0xce, /* (0xce) SAA7191_REG_HC6S */
+ 0xf4, /* (0xf4) SAA7191_REG_HP6I */
+};
+
+/* SAA7191 register handling */
+
+static u8 saa7191_read_reg(struct v4l2_subdev *sd, u8 reg)
+{
+ return to_saa7191(sd)->reg[reg];
+}
+
+static int saa7191_read_status(struct v4l2_subdev *sd, u8 *value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ ret = i2c_master_recv(client, value, 1);
+ if (ret < 0) {
+ printk(KERN_ERR "SAA7191: saa7191_read_status(): read failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+
+static int saa7191_write_reg(struct v4l2_subdev *sd, u8 reg, u8 value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ to_saa7191(sd)->reg[reg] = value;
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/* the first byte of data must be the first subaddress number (register) */
+static int saa7191_write_block(struct v4l2_subdev *sd,
+ u8 length, const u8 *data)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct saa7191 *decoder = to_saa7191(sd);
+ int i;
+ int ret;
+
+ for (i = 0; i < (length - 1); i++) {
+ decoder->reg[data[0] + i] = data[i + 1];
+ }
+
+ ret = i2c_master_send(client, data, length);
+ if (ret < 0) {
+ printk(KERN_ERR "SAA7191: saa7191_write_block(): "
+ "write failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Helper functions */
+
+static int saa7191_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct saa7191 *decoder = to_saa7191(sd);
+ u8 luma = saa7191_read_reg(sd, SAA7191_REG_LUMA);
+ u8 iock = saa7191_read_reg(sd, SAA7191_REG_IOCK);
+ int err;
+
+ switch (input) {
+ case SAA7191_INPUT_COMPOSITE: /* Set Composite input */
+ iock &= ~(SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW1
+ | SAA7191_IOCK_GPSW2);
+ /* Chrominance trap active */
+ luma &= ~SAA7191_LUMA_BYPS;
+ break;
+ case SAA7191_INPUT_SVIDEO: /* Set S-Video input */
+ iock |= SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW2;
+ /* Chrominance trap bypassed */
+ luma |= SAA7191_LUMA_BYPS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ err = saa7191_write_reg(sd, SAA7191_REG_LUMA, luma);
+ if (err)
+ return -EIO;
+ err = saa7191_write_reg(sd, SAA7191_REG_IOCK, iock);
+ if (err)
+ return -EIO;
+
+ decoder->input = input;
+
+ return 0;
+}
+
+static int saa7191_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
+{
+ struct saa7191 *decoder = to_saa7191(sd);
+ u8 stdc = saa7191_read_reg(sd, SAA7191_REG_STDC);
+ u8 ctl3 = saa7191_read_reg(sd, SAA7191_REG_CTL3);
+ u8 chcv = saa7191_read_reg(sd, SAA7191_REG_CHCV);
+ int err;
+
+ if (norm & V4L2_STD_PAL) {
+ stdc &= ~SAA7191_STDC_SECS;
+ ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL);
+ chcv = SAA7191_CHCV_PAL;
+ } else if (norm & V4L2_STD_NTSC) {
+ stdc &= ~SAA7191_STDC_SECS;
+ ctl3 &= ~SAA7191_CTL3_AUFD;
+ ctl3 |= SAA7191_CTL3_FSEL;
+ chcv = SAA7191_CHCV_NTSC;
+ } else if (norm & V4L2_STD_SECAM) {
+ stdc |= SAA7191_STDC_SECS;
+ ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL);
+ chcv = SAA7191_CHCV_PAL;
+ } else {
+ return -EINVAL;
+ }
+
+ err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3);
+ if (err)
+ return -EIO;
+ err = saa7191_write_reg(sd, SAA7191_REG_STDC, stdc);
+ if (err)
+ return -EIO;
+ err = saa7191_write_reg(sd, SAA7191_REG_CHCV, chcv);
+ if (err)
+ return -EIO;
+
+ decoder->norm = norm;
+
+ dprintk("ctl3: %02x stdc: %02x chcv: %02x\n", ctl3,
+ stdc, chcv);
+ dprintk("norm: %llx\n", norm);
+
+ return 0;
+}
+
+static int saa7191_wait_for_signal(struct v4l2_subdev *sd, u8 *status)
+{
+ int i = 0;
+
+ dprintk("Checking for signal...\n");
+
+ for (i = 0; i < SAA7191_SYNC_COUNT; i++) {
+ if (saa7191_read_status(sd, status))
+ return -EIO;
+
+ if (((*status) & SAA7191_STATUS_HLCK) == 0) {
+ dprintk("Signal found\n");
+ return 0;
+ }
+
+ msleep(SAA7191_SYNC_DELAY);
+ }
+
+ dprintk("No signal\n");
+
+ return -EBUSY;
+}
+
+static int saa7191_querystd(struct v4l2_subdev *sd, v4l2_std_id *norm)
+{
+ struct saa7191 *decoder = to_saa7191(sd);
+ u8 stdc = saa7191_read_reg(sd, SAA7191_REG_STDC);
+ u8 ctl3 = saa7191_read_reg(sd, SAA7191_REG_CTL3);
+ u8 status;
+ v4l2_std_id old_norm = decoder->norm;
+ int err = 0;
+
+ dprintk("SAA7191 extended signal auto-detection...\n");
+
+ *norm = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM;
+ stdc &= ~SAA7191_STDC_SECS;
+ ctl3 &= ~(SAA7191_CTL3_FSEL);
+
+ err = saa7191_write_reg(sd, SAA7191_REG_STDC, stdc);
+ if (err) {
+ err = -EIO;
+ goto out;
+ }
+ err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3);
+ if (err) {
+ err = -EIO;
+ goto out;
+ }
+
+ ctl3 |= SAA7191_CTL3_AUFD;
+ err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3);
+ if (err) {
+ err = -EIO;
+ goto out;
+ }
+
+ msleep(SAA7191_SYNC_DELAY);
+
+ err = saa7191_wait_for_signal(sd, &status);
+ if (err)
+ goto out;
+
+ if (status & SAA7191_STATUS_FIDT) {
+ /* 60Hz signal -> NTSC */
+ dprintk("60Hz signal: NTSC\n");
+ *norm = V4L2_STD_NTSC;
+ return 0;
+ }
+
+ /* 50Hz signal */
+ dprintk("50Hz signal: Trying PAL...\n");
+
+ /* try PAL first */
+ err = saa7191_s_std(sd, V4L2_STD_PAL);
+ if (err)
+ goto out;
+
+ msleep(SAA7191_SYNC_DELAY);
+
+ err = saa7191_wait_for_signal(sd, &status);
+ if (err)
+ goto out;
+
+ /* not 50Hz ? */
+ if (status & SAA7191_STATUS_FIDT) {
+ dprintk("No 50Hz signal\n");
+ saa7191_s_std(sd, old_norm);
+ return -EAGAIN;
+ }
+
+ if (status & SAA7191_STATUS_CODE) {
+ dprintk("PAL\n");
+ *norm = V4L2_STD_PAL;
+ return saa7191_s_std(sd, old_norm);
+ }
+
+ dprintk("No color detected with PAL - Trying SECAM...\n");
+
+ /* no color detected ? -> try SECAM */
+ err = saa7191_s_std(sd, V4L2_STD_SECAM);
+ if (err)
+ goto out;
+
+ msleep(SAA7191_SYNC_DELAY);
+
+ err = saa7191_wait_for_signal(sd, &status);
+ if (err)
+ goto out;
+
+ /* not 50Hz ? */
+ if (status & SAA7191_STATUS_FIDT) {
+ dprintk("No 50Hz signal\n");
+ err = -EAGAIN;
+ goto out;
+ }
+
+ if (status & SAA7191_STATUS_CODE) {
+ /* Color detected -> SECAM */
+ dprintk("SECAM\n");
+ *norm = V4L2_STD_SECAM;
+ return saa7191_s_std(sd, old_norm);
+ }
+
+ dprintk("No color detected with SECAM - Going back to PAL.\n");
+
+out:
+ return saa7191_s_std(sd, old_norm);
+}
+
+static int saa7191_autodetect_norm(struct v4l2_subdev *sd)
+{
+ u8 status;
+
+ dprintk("SAA7191 signal auto-detection...\n");
+
+ dprintk("Reading status...\n");
+
+ if (saa7191_read_status(sd, &status))
+ return -EIO;
+
+ dprintk("Checking for signal...\n");
+
+ /* no signal ? */
+ if (status & SAA7191_STATUS_HLCK) {
+ dprintk("No signal\n");
+ return -EBUSY;
+ }
+
+ dprintk("Signal found\n");
+
+ if (status & SAA7191_STATUS_FIDT) {
+ /* 60hz signal -> NTSC */
+ dprintk("NTSC\n");
+ return saa7191_s_std(sd, V4L2_STD_NTSC);
+ } else {
+ /* 50hz signal -> PAL */
+ dprintk("PAL\n");
+ return saa7191_s_std(sd, V4L2_STD_PAL);
+ }
+}
+
+static int saa7191_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ u8 reg;
+ int ret = 0;
+
+ switch (ctrl->id) {
+ case SAA7191_CONTROL_BANDPASS:
+ case SAA7191_CONTROL_BANDPASS_WEIGHT:
+ case SAA7191_CONTROL_CORING:
+ reg = saa7191_read_reg(sd, SAA7191_REG_LUMA);
+ switch (ctrl->id) {
+ case SAA7191_CONTROL_BANDPASS:
+ ctrl->value = ((s32)reg & SAA7191_LUMA_BPSS_MASK)
+ >> SAA7191_LUMA_BPSS_SHIFT;
+ break;
+ case SAA7191_CONTROL_BANDPASS_WEIGHT:
+ ctrl->value = ((s32)reg & SAA7191_LUMA_APER_MASK)
+ >> SAA7191_LUMA_APER_SHIFT;
+ break;
+ case SAA7191_CONTROL_CORING:
+ ctrl->value = ((s32)reg & SAA7191_LUMA_CORI_MASK)
+ >> SAA7191_LUMA_CORI_SHIFT;
+ break;
+ }
+ break;
+ case SAA7191_CONTROL_FORCE_COLOUR:
+ case SAA7191_CONTROL_CHROMA_GAIN:
+ reg = saa7191_read_reg(sd, SAA7191_REG_GAIN);
+ if (ctrl->id == SAA7191_CONTROL_FORCE_COLOUR)
+ ctrl->value = ((s32)reg & SAA7191_GAIN_COLO) ? 1 : 0;
+ else
+ ctrl->value = ((s32)reg & SAA7191_GAIN_LFIS_MASK)
+ >> SAA7191_GAIN_LFIS_SHIFT;
+ break;
+ case V4L2_CID_HUE:
+ reg = saa7191_read_reg(sd, SAA7191_REG_HUEC);
+ if (reg < 0x80)
+ reg += 0x80;
+ else
+ reg -= 0x80;
+ ctrl->value = (s32)reg;
+ break;
+ case SAA7191_CONTROL_VTRC:
+ reg = saa7191_read_reg(sd, SAA7191_REG_STDC);
+ ctrl->value = ((s32)reg & SAA7191_STDC_VTRC) ? 1 : 0;
+ break;
+ case SAA7191_CONTROL_LUMA_DELAY:
+ reg = saa7191_read_reg(sd, SAA7191_REG_CTL3);
+ ctrl->value = ((s32)reg & SAA7191_CTL3_YDEL_MASK)
+ >> SAA7191_CTL3_YDEL_SHIFT;
+ if (ctrl->value >= 4)
+ ctrl->value -= 8;
+ break;
+ case SAA7191_CONTROL_VNR:
+ reg = saa7191_read_reg(sd, SAA7191_REG_CTL4);
+ ctrl->value = ((s32)reg & SAA7191_CTL4_VNOI_MASK)
+ >> SAA7191_CTL4_VNOI_SHIFT;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int saa7191_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ u8 reg;
+ int ret = 0;
+
+ switch (ctrl->id) {
+ case SAA7191_CONTROL_BANDPASS:
+ case SAA7191_CONTROL_BANDPASS_WEIGHT:
+ case SAA7191_CONTROL_CORING:
+ reg = saa7191_read_reg(sd, SAA7191_REG_LUMA);
+ switch (ctrl->id) {
+ case SAA7191_CONTROL_BANDPASS:
+ reg &= ~SAA7191_LUMA_BPSS_MASK;
+ reg |= (ctrl->value << SAA7191_LUMA_BPSS_SHIFT)
+ & SAA7191_LUMA_BPSS_MASK;
+ break;
+ case SAA7191_CONTROL_BANDPASS_WEIGHT:
+ reg &= ~SAA7191_LUMA_APER_MASK;
+ reg |= (ctrl->value << SAA7191_LUMA_APER_SHIFT)
+ & SAA7191_LUMA_APER_MASK;
+ break;
+ case SAA7191_CONTROL_CORING:
+ reg &= ~SAA7191_LUMA_CORI_MASK;
+ reg |= (ctrl->value << SAA7191_LUMA_CORI_SHIFT)
+ & SAA7191_LUMA_CORI_MASK;
+ break;
+ }
+ ret = saa7191_write_reg(sd, SAA7191_REG_LUMA, reg);
+ break;
+ case SAA7191_CONTROL_FORCE_COLOUR:
+ case SAA7191_CONTROL_CHROMA_GAIN:
+ reg = saa7191_read_reg(sd, SAA7191_REG_GAIN);
+ if (ctrl->id == SAA7191_CONTROL_FORCE_COLOUR) {
+ if (ctrl->value)
+ reg |= SAA7191_GAIN_COLO;
+ else
+ reg &= ~SAA7191_GAIN_COLO;
+ } else {
+ reg &= ~SAA7191_GAIN_LFIS_MASK;
+ reg |= (ctrl->value << SAA7191_GAIN_LFIS_SHIFT)
+ & SAA7191_GAIN_LFIS_MASK;
+ }
+ ret = saa7191_write_reg(sd, SAA7191_REG_GAIN, reg);
+ break;
+ case V4L2_CID_HUE:
+ reg = ctrl->value & 0xff;
+ if (reg < 0x80)
+ reg += 0x80;
+ else
+ reg -= 0x80;
+ ret = saa7191_write_reg(sd, SAA7191_REG_HUEC, reg);
+ break;
+ case SAA7191_CONTROL_VTRC:
+ reg = saa7191_read_reg(sd, SAA7191_REG_STDC);
+ if (ctrl->value)
+ reg |= SAA7191_STDC_VTRC;
+ else
+ reg &= ~SAA7191_STDC_VTRC;
+ ret = saa7191_write_reg(sd, SAA7191_REG_STDC, reg);
+ break;
+ case SAA7191_CONTROL_LUMA_DELAY: {
+ s32 value = ctrl->value;
+ if (value < 0)
+ value += 8;
+ reg = saa7191_read_reg(sd, SAA7191_REG_CTL3);
+ reg &= ~SAA7191_CTL3_YDEL_MASK;
+ reg |= (value << SAA7191_CTL3_YDEL_SHIFT)
+ & SAA7191_CTL3_YDEL_MASK;
+ ret = saa7191_write_reg(sd, SAA7191_REG_CTL3, reg);
+ break;
+ }
+ case SAA7191_CONTROL_VNR:
+ reg = saa7191_read_reg(sd, SAA7191_REG_CTL4);
+ reg &= ~SAA7191_CTL4_VNOI_MASK;
+ reg |= (ctrl->value << SAA7191_CTL4_VNOI_SHIFT)
+ & SAA7191_CTL4_VNOI_MASK;
+ ret = saa7191_write_reg(sd, SAA7191_REG_CTL4, reg);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/* I2C-interface */
+
+static int saa7191_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+ u8 status_reg;
+ int res = V4L2_IN_ST_NO_SIGNAL;
+
+ if (saa7191_read_status(sd, &status_reg))
+ return -EIO;
+ if ((status_reg & SAA7191_STATUS_HLCK) == 0)
+ res = 0;
+ if (!(status_reg & SAA7191_STATUS_CODE))
+ res |= V4L2_IN_ST_NO_COLOR;
+ *status = res;
+ return 0;
+}
+
+
+static int saa7191_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7191, 0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops saa7191_core_ops = {
+ .g_chip_ident = saa7191_g_chip_ident,
+ .g_ctrl = saa7191_g_ctrl,
+ .s_ctrl = saa7191_s_ctrl,
+ .s_std = saa7191_s_std,
+};
+
+static const struct v4l2_subdev_video_ops saa7191_video_ops = {
+ .s_routing = saa7191_s_routing,
+ .querystd = saa7191_querystd,
+ .g_input_status = saa7191_g_input_status,
+};
+
+static const struct v4l2_subdev_ops saa7191_ops = {
+ .core = &saa7191_core_ops,
+ .video = &saa7191_video_ops,
+};
+
+static int saa7191_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int err = 0;
+ struct saa7191 *decoder;
+ struct v4l2_subdev *sd;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ decoder = kzalloc(sizeof(*decoder), GFP_KERNEL);
+ if (!decoder)
+ return -ENOMEM;
+
+ sd = &decoder->sd;
+ v4l2_i2c_subdev_init(sd, client, &saa7191_ops);
+
+ err = saa7191_write_block(sd, sizeof(initseq), initseq);
+ if (err) {
+ printk(KERN_ERR "SAA7191 initialization failed\n");
+ kfree(decoder);
+ return err;
+ }
+
+ printk(KERN_INFO "SAA7191 initialized\n");
+
+ decoder->input = SAA7191_INPUT_COMPOSITE;
+ decoder->norm = V4L2_STD_PAL;
+
+ err = saa7191_autodetect_norm(sd);
+ if (err && (err != -EBUSY))
+ printk(KERN_ERR "SAA7191: Signal auto-detection failed\n");
+
+ return 0;
+}
+
+static int saa7191_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_saa7191(sd));
+ return 0;
+}
+
+static const struct i2c_device_id saa7191_id[] = {
+ { "saa7191", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, saa7191_id);
+
+static struct i2c_driver saa7191_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "saa7191",
+ },
+ .probe = saa7191_probe,
+ .remove = saa7191_remove,
+ .id_table = saa7191_id,
+};
+
+module_i2c_driver(saa7191_driver);
diff --git a/drivers/media/video/saa7191.h b/drivers/media/video/saa7191.h
new file mode 100644
index 00000000..803c74d6
--- /dev/null
+++ b/drivers/media/video/saa7191.h
@@ -0,0 +1,245 @@
+/*
+ * saa7191.h - Philips SAA7191 video decoder driver
+ *
+ * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
+ * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _SAA7191_H_
+#define _SAA7191_H_
+
+/* Philips SAA7191 DMSD I2C bus address */
+#define SAA7191_ADDR 0x8a
+
+/* Register subaddresses. */
+#define SAA7191_REG_IDEL 0x00
+#define SAA7191_REG_HSYB 0x01
+#define SAA7191_REG_HSYS 0x02
+#define SAA7191_REG_HCLB 0x03
+#define SAA7191_REG_HCLS 0x04
+#define SAA7191_REG_HPHI 0x05
+#define SAA7191_REG_LUMA 0x06
+#define SAA7191_REG_HUEC 0x07
+#define SAA7191_REG_CKTQ 0x08 /* bits 3-7 */
+#define SAA7191_REG_CKTS 0x09 /* bits 3-7 */
+#define SAA7191_REG_PLSE 0x0a
+#define SAA7191_REG_SESE 0x0b
+#define SAA7191_REG_GAIN 0x0c
+#define SAA7191_REG_STDC 0x0d
+#define SAA7191_REG_IOCK 0x0e
+#define SAA7191_REG_CTL3 0x0f
+#define SAA7191_REG_CTL4 0x10
+#define SAA7191_REG_CHCV 0x11
+#define SAA7191_REG_HS6B 0x14
+#define SAA7191_REG_HS6S 0x15
+#define SAA7191_REG_HC6B 0x16
+#define SAA7191_REG_HC6S 0x17
+#define SAA7191_REG_HP6I 0x18
+#define SAA7191_REG_STATUS 0xff /* not really a subaddress */
+
+/* Status Register definitions */
+#define SAA7191_STATUS_CODE 0x01 /* color detected flag */
+#define SAA7191_STATUS_FIDT 0x20 /* signal type 50/60 Hz */
+#define SAA7191_STATUS_HLCK 0x40 /* PLL unlocked(1)/locked(0) */
+#define SAA7191_STATUS_STTC 0x80 /* tv/vtr time constant */
+
+/* Luminance Control Register definitions */
+/* input mode select bit:
+ * 0=CVBS (chrominance trap active), 1=S-Video (trap bypassed) */
+#define SAA7191_LUMA_BYPS 0x80
+/* pre-filter (only when chrominance trap is active) */
+#define SAA7191_LUMA_PREF 0x40
+/* aperture bandpass to select different characteristics with maximums
+ * (bits 4-5) */
+#define SAA7191_LUMA_BPSS_MASK 0x30
+#define SAA7191_LUMA_BPSS_SHIFT 4
+#define SAA7191_LUMA_BPSS_3 0x30
+#define SAA7191_LUMA_BPSS_2 0x20
+#define SAA7191_LUMA_BPSS_1 0x10
+#define SAA7191_LUMA_BPSS_0 0x00
+/* coring range for high frequency components according to 8-bit luminance
+ * (bits 2-3)
+ * 0=coring off, n= (+-)n LSB */
+#define SAA7191_LUMA_CORI_MASK 0x0c
+#define SAA7191_LUMA_CORI_SHIFT 2
+#define SAA7191_LUMA_CORI_3 0x0c
+#define SAA7191_LUMA_CORI_2 0x08
+#define SAA7191_LUMA_CORI_1 0x04
+#define SAA7191_LUMA_CORI_0 0x00
+/* aperture bandpass filter weights high frequency components of luminance
+ * signal (bits 0-1)
+ * 0=factor 0, 1=0.25, 2=0.5, 3=1 */
+#define SAA7191_LUMA_APER_MASK 0x03
+#define SAA7191_LUMA_APER_SHIFT 0
+#define SAA7191_LUMA_APER_3 0x03
+#define SAA7191_LUMA_APER_2 0x02
+#define SAA7191_LUMA_APER_1 0x01
+#define SAA7191_LUMA_APER_0 0x00
+
+/* Chrominance Gain Control Settings Register definitions */
+/* colour on: 0=automatic colour-killer enabled, 1=forced colour on */
+#define SAA7191_GAIN_COLO 0x80
+/* chrominance gain control (AGC filter)
+ * 0=loop filter time constant slow, 1=medium, 2=fast, 3=actual gain */
+#define SAA7191_GAIN_LFIS_MASK 0x60
+#define SAA7191_GAIN_LFIS_SHIFT 5
+#define SAA7191_GAIN_LFIS_3 0x60
+#define SAA7191_GAIN_LFIS_2 0x40
+#define SAA7191_GAIN_LFIS_1 0x20
+#define SAA7191_GAIN_LFIS_0 0x00
+
+/* Standard/Mode Control Register definitions */
+/* tv/vtr mode bit: 0=TV mode (slow time constant),
+ * 1=VTR mode (fast time constant) */
+#define SAA7191_STDC_VTRC 0x80
+/* SAA7191B-specific functions enable (RTCO, ODD and GPSW0 outputs)
+ * 0=outputs set to high-impedance (circuit equals SAA7191), 1=enabled */
+#define SAA7191_STDC_NFEN 0x08
+/* HREF generation: 0=like SAA7191, 1=HREF is 8xLLC2 clocks earlier */
+#define SAA7191_STDC_HRMV 0x04
+/* general purpose switch 0
+ * (not used with VINO afaik) */
+#define SAA7191_STDC_GPSW0 0x02
+/* SECAM mode bit: 0=other standards, 1=SECAM */
+#define SAA7191_STDC_SECS 0x01
+
+/* I/O and Clock Control Register definitions */
+/* horizontal clock PLL: 0=PLL closed,
+ * 1=PLL circuit open and horizontal freq fixed */
+#define SAA7191_IOCK_HPLL 0x80
+/* colour-difference output enable (outputs UV0-UV7) */
+#define SAA7191_IOCK_OEDC 0x40
+/* H-sync output enable */
+#define SAA7191_IOCK_OEHS 0x20
+/* V-sync output enable */
+#define SAA7191_IOCK_OEVS 0x10
+/* luminance output enable (outputs Y0-Y7) */
+#define SAA7191_IOCK_OEDY 0x08
+/* S-VHS bit (chrominance from CVBS or from chrominance input):
+ * 0=controlled by BYPS-bit, 1=from chrominance input */
+#define SAA7191_IOCK_CHRS 0x04
+/* general purpose switch 2
+ * VINO-specific: 0=used with CVBS, 1=used with S-Video */
+#define SAA7191_IOCK_GPSW2 0x02
+/* general purpose switch 1 */
+/* VINO-specific: 0=always, 1=not used!*/
+#define SAA7191_IOCK_GPSW1 0x01
+
+/* Miscellaneous Control #1 Register definitions */
+/* automatic field detection (50/60Hz standard) */
+#define SAA7191_CTL3_AUFD 0x80
+/* field select: (if AUFD=0)
+ * 0=50Hz (625 lines), 1=60Hz (525 lines) */
+#define SAA7191_CTL3_FSEL 0x40
+/* SECAM cross-colour reduction enable */
+#define SAA7191_CTL3_SXCR 0x20
+/* sync and clamping pulse enable (HCL and HSY outputs) */
+#define SAA7191_CTL3_SCEN 0x10
+/* output format: 0=4:1:1, 1=4:2:2 (4:2:2 for VINO) */
+#define SAA7191_CTL3_OFTS 0x08
+/* luminance delay compensation
+ * 0=0*2/LLC, 1=+1*2/LLC, 2=+2*2/LLC, 3=+3*2/LLC,
+ * 4=-4*2/LLC, 5=-3*2/LLC, 6=-2*2/LLC, 7=-1*2/LLC
+ * step size = 2/LLC = 67.8ns for 50Hz, 81.5ns for 60Hz */
+#define SAA7191_CTL3_YDEL_MASK 0x07
+#define SAA7191_CTL3_YDEL_SHIFT 0
+#define SAA7191_CTL3_YDEL2 0x04
+#define SAA7191_CTL3_YDEL1 0x02
+#define SAA7191_CTL3_YDEL0 0x01
+
+/* Miscellaneous Control #2 Register definitions */
+/* select HREF position
+ * 0=normal, HREF is matched to YUV output port,
+ * 1=HREF is matched to CVBS input port */
+#define SAA7191_CTL4_HRFS 0x04
+/* vertical noise reduction
+ * 0=normal, 1=searching window, 2=auto-deflection, 3=reduction bypassed */
+#define SAA7191_CTL4_VNOI_MASK 0x03
+#define SAA7191_CTL4_VNOI_SHIFT 0
+#define SAA7191_CTL4_VNOI_3 0x03
+#define SAA7191_CTL4_VNOI_2 0x02
+#define SAA7191_CTL4_VNOI_1 0x01
+#define SAA7191_CTL4_VNOI_0 0x00
+
+/* Chrominance Gain Control Register definitions
+ * - for QAM-modulated input signals, effects output amplitude
+ * (SECAM gain fixed)
+ * (nominal values for UV CCIR level) */
+#define SAA7191_CHCV_NTSC 0x2c
+#define SAA7191_CHCV_PAL 0x59
+
+/* Driver interface definitions */
+#define SAA7191_INPUT_COMPOSITE 0
+#define SAA7191_INPUT_SVIDEO 1
+
+#define SAA7191_NORM_PAL 1
+#define SAA7191_NORM_NTSC 2
+#define SAA7191_NORM_SECAM 3
+
+struct saa7191_status {
+ /* 0=no signal, 1=signal detected */
+ int signal;
+ /* 0=50hz (pal) signal, 1=60hz (ntsc) signal */
+ int signal_60hz;
+ /* 0=no color detected, 1=color detected */
+ int color;
+
+ /* current SAA7191_INPUT_ */
+ int input;
+ /* current SAA7191_NORM_ */
+ int norm;
+};
+
+#define SAA7191_BANDPASS_MIN 0x00
+#define SAA7191_BANDPASS_MAX 0x03
+#define SAA7191_BANDPASS_DEFAULT 0x00
+
+#define SAA7191_BANDPASS_WEIGHT_MIN 0x00
+#define SAA7191_BANDPASS_WEIGHT_MAX 0x03
+#define SAA7191_BANDPASS_WEIGHT_DEFAULT 0x01
+
+#define SAA7191_CORING_MIN 0x00
+#define SAA7191_CORING_MAX 0x03
+#define SAA7191_CORING_DEFAULT 0x00
+
+#define SAA7191_HUE_MIN 0x00
+#define SAA7191_HUE_MAX 0xff
+#define SAA7191_HUE_DEFAULT 0x80
+
+#define SAA7191_VTRC_MIN 0x00
+#define SAA7191_VTRC_MAX 0x01
+#define SAA7191_VTRC_DEFAULT 0x00
+
+#define SAA7191_FORCE_COLOUR_MIN 0x00
+#define SAA7191_FORCE_COLOUR_MAX 0x01
+#define SAA7191_FORCE_COLOUR_DEFAULT 0x00
+
+#define SAA7191_CHROMA_GAIN_MIN 0x00
+#define SAA7191_CHROMA_GAIN_MAX 0x03
+#define SAA7191_CHROMA_GAIN_DEFAULT 0x00
+
+#define SAA7191_LUMA_DELAY_MIN -0x04
+#define SAA7191_LUMA_DELAY_MAX 0x03
+#define SAA7191_LUMA_DELAY_DEFAULT 0x01
+
+#define SAA7191_VNR_MIN 0x00
+#define SAA7191_VNR_MAX 0x03
+#define SAA7191_VNR_DEFAULT 0x00
+
+#define SAA7191_CONTROL_BANDPASS (V4L2_CID_PRIVATE_BASE + 0)
+#define SAA7191_CONTROL_BANDPASS_WEIGHT (V4L2_CID_PRIVATE_BASE + 1)
+#define SAA7191_CONTROL_CORING (V4L2_CID_PRIVATE_BASE + 2)
+#define SAA7191_CONTROL_FORCE_COLOUR (V4L2_CID_PRIVATE_BASE + 3)
+#define SAA7191_CONTROL_CHROMA_GAIN (V4L2_CID_PRIVATE_BASE + 4)
+#define SAA7191_CONTROL_VTRC (V4L2_CID_PRIVATE_BASE + 5)
+#define SAA7191_CONTROL_LUMA_DELAY (V4L2_CID_PRIVATE_BASE + 6)
+#define SAA7191_CONTROL_VNR (V4L2_CID_PRIVATE_BASE + 7)
+
+#define DECODER_SAA7191_GET_STATUS _IOR('d', 195, struct saa7191_status)
+#define DECODER_SAA7191_SET_NORM _IOW('d', 196, int)
+
+#endif
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c
new file mode 100644
index 00000000..424dfacd
--- /dev/null
+++ b/drivers/media/video/sh_mobile_ceu_camera.c
@@ -0,0 +1,2319 @@
+/*
+ * V4L2 Driver for SuperH Mobile CEU interface
+ *
+ * Copyright (C) 2008 Magnus Damm
+ *
+ * Based on V4L2 Driver for PXA camera host - "pxa_camera.c",
+ *
+ * Copyright (C) 2006, Sascha Hauer, Pengutronix
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+#include <linux/pm_runtime.h>
+#include <linux/sched.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/soc_camera.h>
+#include <media/sh_mobile_ceu.h>
+#include <media/sh_mobile_csi2.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/v4l2-mediabus.h>
+#include <media/soc_mediabus.h>
+
+/* register offsets for sh7722 / sh7723 */
+
+#define CAPSR 0x00 /* Capture start register */
+#define CAPCR 0x04 /* Capture control register */
+#define CAMCR 0x08 /* Capture interface control register */
+#define CMCYR 0x0c /* Capture interface cycle register */
+#define CAMOR 0x10 /* Capture interface offset register */
+#define CAPWR 0x14 /* Capture interface width register */
+#define CAIFR 0x18 /* Capture interface input format register */
+#define CSTCR 0x20 /* Camera strobe control register (<= sh7722) */
+#define CSECR 0x24 /* Camera strobe emission count register (<= sh7722) */
+#define CRCNTR 0x28 /* CEU register control register */
+#define CRCMPR 0x2c /* CEU register forcible control register */
+#define CFLCR 0x30 /* Capture filter control register */
+#define CFSZR 0x34 /* Capture filter size clip register */
+#define CDWDR 0x38 /* Capture destination width register */
+#define CDAYR 0x3c /* Capture data address Y register */
+#define CDACR 0x40 /* Capture data address C register */
+#define CDBYR 0x44 /* Capture data bottom-field address Y register */
+#define CDBCR 0x48 /* Capture data bottom-field address C register */
+#define CBDSR 0x4c /* Capture bundle destination size register */
+#define CFWCR 0x5c /* Firewall operation control register */
+#define CLFCR 0x60 /* Capture low-pass filter control register */
+#define CDOCR 0x64 /* Capture data output control register */
+#define CDDCR 0x68 /* Capture data complexity level register */
+#define CDDAR 0x6c /* Capture data complexity level address register */
+#define CEIER 0x70 /* Capture event interrupt enable register */
+#define CETCR 0x74 /* Capture event flag clear register */
+#define CSTSR 0x7c /* Capture status register */
+#define CSRTR 0x80 /* Capture software reset register */
+#define CDSSR 0x84 /* Capture data size register */
+#define CDAYR2 0x90 /* Capture data address Y register 2 */
+#define CDACR2 0x94 /* Capture data address C register 2 */
+#define CDBYR2 0x98 /* Capture data bottom-field address Y register 2 */
+#define CDBCR2 0x9c /* Capture data bottom-field address C register 2 */
+
+#undef DEBUG_GEOMETRY
+#ifdef DEBUG_GEOMETRY
+#define dev_geo dev_info
+#else
+#define dev_geo dev_dbg
+#endif
+
+/* per video frame buffer */
+struct sh_mobile_ceu_buffer {
+ struct vb2_buffer vb; /* v4l buffer must be first */
+ struct list_head queue;
+};
+
+struct sh_mobile_ceu_dev {
+ struct soc_camera_host ici;
+ struct soc_camera_device *icd;
+ struct platform_device *csi2_pdev;
+
+ unsigned int irq;
+ void __iomem *base;
+ size_t video_limit;
+ size_t buf_total;
+
+ spinlock_t lock; /* Protects video buffer lists */
+ struct list_head capture;
+ struct vb2_buffer *active;
+ struct vb2_alloc_ctx *alloc_ctx;
+
+ struct sh_mobile_ceu_info *pdata;
+ struct completion complete;
+
+ u32 cflcr;
+
+ /* static max sizes either from platform data or default */
+ int max_width;
+ int max_height;
+
+ enum v4l2_field field;
+ int sequence;
+
+ unsigned int image_mode:1;
+ unsigned int is_16bit:1;
+ unsigned int frozen:1;
+};
+
+struct sh_mobile_ceu_cam {
+ /* CEU offsets within the camera output, before the CEU scaler */
+ unsigned int ceu_left;
+ unsigned int ceu_top;
+ /* Client output, as seen by the CEU */
+ unsigned int width;
+ unsigned int height;
+ /*
+ * User window from S_CROP / G_CROP, produced by client cropping and
+ * scaling, CEU scaling and CEU cropping, mapped back onto the client
+ * input window
+ */
+ struct v4l2_rect subrect;
+ /* Camera cropping rectangle */
+ struct v4l2_rect rect;
+ const struct soc_mbus_pixelfmt *extra_fmt;
+ enum v4l2_mbus_pixelcode code;
+};
+
+static struct sh_mobile_ceu_buffer *to_ceu_vb(struct vb2_buffer *vb)
+{
+ return container_of(vb, struct sh_mobile_ceu_buffer, vb);
+}
+
+static void ceu_write(struct sh_mobile_ceu_dev *priv,
+ unsigned long reg_offs, u32 data)
+{
+ iowrite32(data, priv->base + reg_offs);
+}
+
+static u32 ceu_read(struct sh_mobile_ceu_dev *priv, unsigned long reg_offs)
+{
+ return ioread32(priv->base + reg_offs);
+}
+
+static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev)
+{
+ int i, success = 0;
+ struct soc_camera_device *icd = pcdev->icd;
+
+ ceu_write(pcdev, CAPSR, 1 << 16); /* reset */
+
+ /* wait CSTSR.CPTON bit */
+ for (i = 0; i < 1000; i++) {
+ if (!(ceu_read(pcdev, CSTSR) & 1)) {
+ success++;
+ break;
+ }
+ udelay(1);
+ }
+
+ /* wait CAPSR.CPKIL bit */
+ for (i = 0; i < 1000; i++) {
+ if (!(ceu_read(pcdev, CAPSR) & (1 << 16))) {
+ success++;
+ break;
+ }
+ udelay(1);
+ }
+
+
+ if (2 != success) {
+ dev_warn(icd->pdev, "soft reset time out\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * Videobuf operations
+ */
+
+/*
+ * .queue_setup() is called to check, whether the driver can accept the
+ * requested number of buffers and to fill in plane sizes
+ * for the current frame format if required
+ */
+static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *count, unsigned int *num_planes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct soc_camera_device *icd = container_of(vq, struct soc_camera_device, vb2_vidq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ int bytes_per_line;
+ unsigned int height;
+
+ if (fmt) {
+ const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd,
+ fmt->fmt.pix.pixelformat);
+ if (!xlate)
+ return -EINVAL;
+ bytes_per_line = soc_mbus_bytes_per_line(fmt->fmt.pix.width,
+ xlate->host_fmt);
+ height = fmt->fmt.pix.height;
+ } else {
+ /* Called from VIDIOC_REQBUFS or in compatibility mode */
+ bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+ height = icd->user_height;
+ }
+ if (bytes_per_line < 0)
+ return bytes_per_line;
+
+ sizes[0] = bytes_per_line * height;
+
+ alloc_ctxs[0] = pcdev->alloc_ctx;
+
+ if (!vq->num_buffers)
+ pcdev->sequence = 0;
+
+ if (!*count)
+ *count = 2;
+
+ /* If *num_planes != 0, we have already verified *count. */
+ if (pcdev->video_limit && !*num_planes) {
+ size_t size = PAGE_ALIGN(sizes[0]) * *count;
+
+ if (size + pcdev->buf_total > pcdev->video_limit)
+ *count = (pcdev->video_limit - pcdev->buf_total) /
+ PAGE_ALIGN(sizes[0]);
+ }
+
+ *num_planes = 1;
+
+ dev_dbg(icd->parent, "count=%d, size=%u\n", *count, sizes[0]);
+
+ return 0;
+}
+
+#define CEU_CETCR_MAGIC 0x0317f313 /* acknowledge magical interrupt sources */
+#define CEU_CETCR_IGRW (1 << 4) /* prohibited register access interrupt bit */
+#define CEU_CEIER_CPEIE (1 << 0) /* one-frame capture end interrupt */
+#define CEU_CEIER_VBP (1 << 20) /* vbp error */
+#define CEU_CAPCR_CTNCP (1 << 16) /* continuous capture mode (if set) */
+#define CEU_CEIER_MASK (CEU_CEIER_CPEIE | CEU_CEIER_VBP)
+
+
+/*
+ * return value doesn't reflex the success/failure to queue the new buffer,
+ * but rather the status of the previous buffer.
+ */
+static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
+{
+ struct soc_camera_device *icd = pcdev->icd;
+ dma_addr_t phys_addr_top, phys_addr_bottom;
+ unsigned long top1, top2;
+ unsigned long bottom1, bottom2;
+ u32 status;
+ bool planar;
+ int ret = 0;
+
+ /*
+ * The hardware is _very_ picky about this sequence. Especially
+ * the CEU_CETCR_MAGIC value. It seems like we need to acknowledge
+ * several not-so-well documented interrupt sources in CETCR.
+ */
+ ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~CEU_CEIER_MASK);
+ status = ceu_read(pcdev, CETCR);
+ ceu_write(pcdev, CETCR, ~status & CEU_CETCR_MAGIC);
+ if (!pcdev->frozen)
+ ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | CEU_CEIER_MASK);
+ ceu_write(pcdev, CAPCR, ceu_read(pcdev, CAPCR) & ~CEU_CAPCR_CTNCP);
+ ceu_write(pcdev, CETCR, CEU_CETCR_MAGIC ^ CEU_CETCR_IGRW);
+
+ /*
+ * When a VBP interrupt occurs, a capture end interrupt does not occur
+ * and the image of that frame is not captured correctly. So, soft reset
+ * is needed here.
+ */
+ if (status & CEU_CEIER_VBP) {
+ sh_mobile_ceu_soft_reset(pcdev);
+ ret = -EIO;
+ }
+
+ if (pcdev->frozen) {
+ complete(&pcdev->complete);
+ return ret;
+ }
+
+ if (!pcdev->active)
+ return ret;
+
+ if (V4L2_FIELD_INTERLACED_BT == pcdev->field) {
+ top1 = CDBYR;
+ top2 = CDBCR;
+ bottom1 = CDAYR;
+ bottom2 = CDACR;
+ } else {
+ top1 = CDAYR;
+ top2 = CDACR;
+ bottom1 = CDBYR;
+ bottom2 = CDBCR;
+ }
+
+ phys_addr_top = vb2_dma_contig_plane_dma_addr(pcdev->active, 0);
+
+ switch (icd->current_fmt->host_fmt->fourcc) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ planar = true;
+ break;
+ default:
+ planar = false;
+ }
+
+ ceu_write(pcdev, top1, phys_addr_top);
+ if (V4L2_FIELD_NONE != pcdev->field) {
+ if (planar)
+ phys_addr_bottom = phys_addr_top + icd->user_width;
+ else
+ phys_addr_bottom = phys_addr_top +
+ soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+ ceu_write(pcdev, bottom1, phys_addr_bottom);
+ }
+
+ if (planar) {
+ phys_addr_top += icd->user_width *
+ icd->user_height;
+ ceu_write(pcdev, top2, phys_addr_top);
+ if (V4L2_FIELD_NONE != pcdev->field) {
+ phys_addr_bottom = phys_addr_top + icd->user_width;
+ ceu_write(pcdev, bottom2, phys_addr_bottom);
+ }
+ }
+
+ ceu_write(pcdev, CAPSR, 0x1); /* start capture */
+
+ return ret;
+}
+
+static int sh_mobile_ceu_videobuf_prepare(struct vb2_buffer *vb)
+{
+ struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb);
+
+ /* Added list head initialization on alloc */
+ WARN(!list_empty(&buf->queue), "Buffer %p on queue!\n", vb);
+
+ return 0;
+}
+
+static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb)
+{
+ struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb);
+ unsigned long size;
+ int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+
+ if (bytes_per_line < 0)
+ goto error;
+
+ size = icd->user_height * bytes_per_line;
+
+ if (vb2_plane_size(vb, 0) < size) {
+ dev_err(icd->parent, "Buffer #%d too small (%lu < %lu)\n",
+ vb->v4l2_buf.index, vb2_plane_size(vb, 0), size);
+ goto error;
+ }
+
+ vb2_set_plane_payload(vb, 0, size);
+
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+ vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
+
+#ifdef DEBUG
+ /*
+ * This can be useful if you want to see if we actually fill
+ * the buffer with something
+ */
+ if (vb2_plane_vaddr(vb, 0))
+ memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0));
+#endif
+
+ spin_lock_irq(&pcdev->lock);
+ list_add_tail(&buf->queue, &pcdev->capture);
+
+ if (!pcdev->active) {
+ /*
+ * Because there were no active buffer at this moment,
+ * we are not interested in the return value of
+ * sh_mobile_ceu_capture here.
+ */
+ pcdev->active = vb;
+ sh_mobile_ceu_capture(pcdev);
+ }
+ spin_unlock_irq(&pcdev->lock);
+
+ return;
+
+error:
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+}
+
+static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb)
+{
+ struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+
+ spin_lock_irq(&pcdev->lock);
+
+ if (pcdev->active == vb) {
+ /* disable capture (release DMA buffer), reset */
+ ceu_write(pcdev, CAPSR, 1 << 16);
+ pcdev->active = NULL;
+ }
+
+ /*
+ * Doesn't hurt also if the list is empty, but it hurts, if queuing the
+ * buffer failed, and .buf_init() hasn't been called
+ */
+ if (buf->queue.next)
+ list_del_init(&buf->queue);
+
+ pcdev->buf_total -= PAGE_ALIGN(vb2_plane_size(vb, 0));
+ dev_dbg(icd->parent, "%s() %zu bytes buffers\n", __func__,
+ pcdev->buf_total);
+
+ spin_unlock_irq(&pcdev->lock);
+}
+
+static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb)
+{
+ struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+
+ pcdev->buf_total += PAGE_ALIGN(vb2_plane_size(vb, 0));
+ dev_dbg(icd->parent, "%s() %zu bytes buffers\n", __func__,
+ pcdev->buf_total);
+
+ /* This is for locking debugging only */
+ INIT_LIST_HEAD(&to_ceu_vb(vb)->queue);
+ return 0;
+}
+
+static int sh_mobile_ceu_stop_streaming(struct vb2_queue *q)
+{
+ struct soc_camera_device *icd = container_of(q, struct soc_camera_device, vb2_vidq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ struct list_head *buf_head, *tmp;
+
+ spin_lock_irq(&pcdev->lock);
+
+ pcdev->active = NULL;
+
+ list_for_each_safe(buf_head, tmp, &pcdev->capture)
+ list_del_init(buf_head);
+
+ spin_unlock_irq(&pcdev->lock);
+
+ return sh_mobile_ceu_soft_reset(pcdev);
+}
+
+static struct vb2_ops sh_mobile_ceu_videobuf_ops = {
+ .queue_setup = sh_mobile_ceu_videobuf_setup,
+ .buf_prepare = sh_mobile_ceu_videobuf_prepare,
+ .buf_queue = sh_mobile_ceu_videobuf_queue,
+ .buf_cleanup = sh_mobile_ceu_videobuf_release,
+ .buf_init = sh_mobile_ceu_videobuf_init,
+ .wait_prepare = soc_camera_unlock,
+ .wait_finish = soc_camera_lock,
+ .stop_streaming = sh_mobile_ceu_stop_streaming,
+};
+
+static irqreturn_t sh_mobile_ceu_irq(int irq, void *data)
+{
+ struct sh_mobile_ceu_dev *pcdev = data;
+ struct vb2_buffer *vb;
+ int ret;
+
+ spin_lock(&pcdev->lock);
+
+ vb = pcdev->active;
+ if (!vb)
+ /* Stale interrupt from a released buffer */
+ goto out;
+
+ list_del_init(&to_ceu_vb(vb)->queue);
+
+ if (!list_empty(&pcdev->capture))
+ pcdev->active = &list_entry(pcdev->capture.next,
+ struct sh_mobile_ceu_buffer, queue)->vb;
+ else
+ pcdev->active = NULL;
+
+ ret = sh_mobile_ceu_capture(pcdev);
+ do_gettimeofday(&vb->v4l2_buf.timestamp);
+ if (!ret) {
+ vb->v4l2_buf.field = pcdev->field;
+ vb->v4l2_buf.sequence = pcdev->sequence++;
+ }
+ vb2_buffer_done(vb, ret < 0 ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+
+out:
+ spin_unlock(&pcdev->lock);
+
+ return IRQ_HANDLED;
+}
+
+static struct v4l2_subdev *find_csi2(struct sh_mobile_ceu_dev *pcdev)
+{
+ struct v4l2_subdev *sd;
+
+ if (!pcdev->csi2_pdev)
+ return NULL;
+
+ v4l2_device_for_each_subdev(sd, &pcdev->ici.v4l2_dev)
+ if (&pcdev->csi2_pdev->dev == v4l2_get_subdevdata(sd))
+ return sd;
+
+ return NULL;
+}
+
+/* Called with .video_lock held */
+static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ struct v4l2_subdev *csi2_sd;
+ int ret;
+
+ if (pcdev->icd)
+ return -EBUSY;
+
+ dev_info(icd->parent,
+ "SuperH Mobile CEU driver attached to camera %d\n",
+ icd->devnum);
+
+ pm_runtime_get_sync(ici->v4l2_dev.dev);
+
+ pcdev->buf_total = 0;
+
+ ret = sh_mobile_ceu_soft_reset(pcdev);
+
+ csi2_sd = find_csi2(pcdev);
+ if (csi2_sd) {
+ csi2_sd->grp_id = soc_camera_grp_id(icd);
+ v4l2_set_subdev_hostdata(csi2_sd, icd);
+ }
+
+ ret = v4l2_subdev_call(csi2_sd, core, s_power, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) {
+ pm_runtime_put_sync(ici->v4l2_dev.dev);
+ return ret;
+ }
+
+ /*
+ * -ENODEV is special: either csi2_sd == NULL or the CSI-2 driver
+ * has not found this soc-camera device among its clients
+ */
+ if (ret == -ENODEV && csi2_sd)
+ csi2_sd->grp_id = 0;
+ pcdev->icd = icd;
+
+ return 0;
+}
+
+/* Called with .video_lock held */
+static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
+
+ BUG_ON(icd != pcdev->icd);
+
+ v4l2_subdev_call(csi2_sd, core, s_power, 0);
+ if (csi2_sd)
+ csi2_sd->grp_id = 0;
+ /* disable capture, disable interrupts */
+ ceu_write(pcdev, CEIER, 0);
+ sh_mobile_ceu_soft_reset(pcdev);
+
+ /* make sure active buffer is canceled */
+ spin_lock_irq(&pcdev->lock);
+ if (pcdev->active) {
+ list_del_init(&to_ceu_vb(pcdev->active)->queue);
+ vb2_buffer_done(pcdev->active, VB2_BUF_STATE_ERROR);
+ pcdev->active = NULL;
+ }
+ spin_unlock_irq(&pcdev->lock);
+
+ pm_runtime_put_sync(ici->v4l2_dev.dev);
+
+ dev_info(icd->parent,
+ "SuperH Mobile CEU driver detached from camera %d\n",
+ icd->devnum);
+
+ pcdev->icd = NULL;
+}
+
+/*
+ * See chapter 29.4.12 "Capture Filter Control Register (CFLCR)"
+ * in SH7722 Hardware Manual
+ */
+static unsigned int size_dst(unsigned int src, unsigned int scale)
+{
+ unsigned int mant_pre = scale >> 12;
+ if (!src || !scale)
+ return src;
+ return ((mant_pre + 2 * (src - 1)) / (2 * mant_pre) - 1) *
+ mant_pre * 4096 / scale + 1;
+}
+
+static u16 calc_scale(unsigned int src, unsigned int *dst)
+{
+ u16 scale;
+
+ if (src == *dst)
+ return 0;
+
+ scale = (src * 4096 / *dst) & ~7;
+
+ while (scale > 4096 && size_dst(src, scale) < *dst)
+ scale -= 8;
+
+ *dst = size_dst(src, scale);
+
+ return scale;
+}
+
+/* rect is guaranteed to not exceed the scaled camera rectangle */
+static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_cam *cam = icd->host_priv;
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ unsigned int height, width, cdwdr_width, in_width, in_height;
+ unsigned int left_offset, top_offset;
+ u32 camor;
+
+ dev_geo(icd->parent, "Crop %ux%u@%u:%u\n",
+ icd->user_width, icd->user_height, cam->ceu_left, cam->ceu_top);
+
+ left_offset = cam->ceu_left;
+ top_offset = cam->ceu_top;
+
+ WARN_ON(icd->user_width & 3 || icd->user_height & 3);
+
+ width = icd->user_width;
+
+ if (pcdev->image_mode) {
+ in_width = cam->width;
+ if (!pcdev->is_16bit) {
+ in_width *= 2;
+ left_offset *= 2;
+ }
+ cdwdr_width = width;
+ } else {
+ int bytes_per_line = soc_mbus_bytes_per_line(width,
+ icd->current_fmt->host_fmt);
+ unsigned int w_factor;
+
+ switch (icd->current_fmt->host_fmt->packing) {
+ case SOC_MBUS_PACKING_2X8_PADHI:
+ w_factor = 2;
+ break;
+ default:
+ w_factor = 1;
+ }
+
+ in_width = cam->width * w_factor;
+ left_offset *= w_factor;
+
+ if (bytes_per_line < 0)
+ cdwdr_width = width;
+ else
+ cdwdr_width = bytes_per_line;
+ }
+
+ height = icd->user_height;
+ in_height = cam->height;
+ if (V4L2_FIELD_NONE != pcdev->field) {
+ height = (height / 2) & ~3;
+ in_height /= 2;
+ top_offset /= 2;
+ cdwdr_width *= 2;
+ }
+
+ /* CSI2 special configuration */
+ if (pcdev->pdata->csi2) {
+ in_width = ((in_width - 2) * 2);
+ left_offset *= 2;
+ }
+
+ /* Set CAMOR, CAPWR, CFSZR, take care of CDWDR */
+ camor = left_offset | (top_offset << 16);
+
+ dev_geo(icd->parent,
+ "CAMOR 0x%x, CAPWR 0x%x, CFSZR 0x%x, CDWDR 0x%x\n", camor,
+ (in_height << 16) | in_width, (height << 16) | width,
+ cdwdr_width);
+
+ ceu_write(pcdev, CAMOR, camor);
+ ceu_write(pcdev, CAPWR, (in_height << 16) | in_width);
+ /* CFSZR clipping is applied _after_ the scaling filter (CFLCR) */
+ ceu_write(pcdev, CFSZR, (height << 16) | width);
+ ceu_write(pcdev, CDWDR, cdwdr_width);
+}
+
+static u32 capture_save_reset(struct sh_mobile_ceu_dev *pcdev)
+{
+ u32 capsr = ceu_read(pcdev, CAPSR);
+ ceu_write(pcdev, CAPSR, 1 << 16); /* reset, stop capture */
+ return capsr;
+}
+
+static void capture_restore(struct sh_mobile_ceu_dev *pcdev, u32 capsr)
+{
+ unsigned long timeout = jiffies + 10 * HZ;
+
+ /*
+ * Wait until the end of the current frame. It can take a long time,
+ * but if it has been aborted by a CAPSR reset, it shoule exit sooner.
+ */
+ while ((ceu_read(pcdev, CSTSR) & 1) && time_before(jiffies, timeout))
+ msleep(1);
+
+ if (time_after(jiffies, timeout)) {
+ dev_err(pcdev->ici.v4l2_dev.dev,
+ "Timeout waiting for frame end! Interface problem?\n");
+ return;
+ }
+
+ /* Wait until reset clears, this shall not hang... */
+ while (ceu_read(pcdev, CAPSR) & (1 << 16))
+ udelay(10);
+
+ /* Anything to restore? */
+ if (capsr & ~(1 << 16))
+ ceu_write(pcdev, CAPSR, capsr);
+}
+
+/* Find the bus subdevice driver, e.g., CSI2 */
+static struct v4l2_subdev *find_bus_subdev(struct sh_mobile_ceu_dev *pcdev,
+ struct soc_camera_device *icd)
+{
+ if (pcdev->csi2_pdev) {
+ struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
+ if (csi2_sd && csi2_sd->grp_id == soc_camera_grp_id(icd))
+ return csi2_sd;
+ }
+
+ return soc_camera_to_subdev(icd);
+}
+
+#define CEU_BUS_FLAGS (V4L2_MBUS_MASTER | \
+ V4L2_MBUS_PCLK_SAMPLE_RISING | \
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_HSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_VSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_DATA_ACTIVE_HIGH)
+
+/* Capture is not running, no interrupts, no locking needed */
+static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ struct v4l2_subdev *sd = find_bus_subdev(pcdev, icd);
+ struct sh_mobile_ceu_cam *cam = icd->host_priv;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long value, common_flags = CEU_BUS_FLAGS;
+ u32 capsr = capture_save_reset(pcdev);
+ unsigned int yuv_lineskip;
+ int ret;
+
+ /*
+ * If the client doesn't implement g_mbus_config, we just use our
+ * platform data
+ */
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg,
+ common_flags);
+ if (!common_flags)
+ return -EINVAL;
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ }
+
+ /* Make choises, based on platform preferences */
+ if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
+ if (pcdev->pdata->flags & SH_CEU_FLAG_HSYNC_LOW)
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
+ else
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
+ }
+
+ if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
+ if (pcdev->pdata->flags & SH_CEU_FLAG_VSYNC_LOW)
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
+ else
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
+ }
+
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+
+ if (icd->current_fmt->host_fmt->bits_per_sample > 8)
+ pcdev->is_16bit = 1;
+ else
+ pcdev->is_16bit = 0;
+
+ ceu_write(pcdev, CRCNTR, 0);
+ ceu_write(pcdev, CRCMPR, 0);
+
+ value = 0x00000010; /* data fetch by default */
+ yuv_lineskip = 0x10;
+
+ switch (icd->current_fmt->host_fmt->fourcc) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ /* convert 4:2:2 -> 4:2:0 */
+ yuv_lineskip = 0; /* skip for NV12/21, no skip for NV16/61 */
+ /* fall-through */
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ switch (cam->code) {
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ value = 0x00000000; /* Cb0, Y0, Cr0, Y1 */
+ break;
+ case V4L2_MBUS_FMT_VYUY8_2X8:
+ value = 0x00000100; /* Cr0, Y0, Cb0, Y1 */
+ break;
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ value = 0x00000200; /* Y0, Cb0, Y1, Cr0 */
+ break;
+ case V4L2_MBUS_FMT_YVYU8_2X8:
+ value = 0x00000300; /* Y0, Cr0, Y1, Cb0 */
+ break;
+ default:
+ BUG();
+ }
+ }
+
+ if (icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV21 ||
+ icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV61)
+ value ^= 0x00000100; /* swap U, V to change from NV1x->NVx1 */
+
+ value |= common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
+ value |= common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
+ value |= pcdev->is_16bit ? 1 << 12 : 0;
+
+ /* CSI2 mode */
+ if (pcdev->pdata->csi2)
+ value |= 3 << 12;
+
+ ceu_write(pcdev, CAMCR, value);
+
+ ceu_write(pcdev, CAPCR, 0x00300000);
+
+ switch (pcdev->field) {
+ case V4L2_FIELD_INTERLACED_TB:
+ value = 0x101;
+ break;
+ case V4L2_FIELD_INTERLACED_BT:
+ value = 0x102;
+ break;
+ default:
+ value = 0;
+ break;
+ }
+ ceu_write(pcdev, CAIFR, value);
+
+ sh_mobile_ceu_set_rect(icd);
+ mdelay(1);
+
+ dev_geo(icd->parent, "CFLCR 0x%x\n", pcdev->cflcr);
+ ceu_write(pcdev, CFLCR, pcdev->cflcr);
+
+ /*
+ * A few words about byte order (observed in Big Endian mode)
+ *
+ * In data fetch mode bytes are received in chunks of 8 bytes.
+ * D0, D1, D2, D3, D4, D5, D6, D7 (D0 received first)
+ *
+ * The data is however by default written to memory in reverse order:
+ * D7, D6, D5, D4, D3, D2, D1, D0 (D7 written to lowest byte)
+ *
+ * The lowest three bits of CDOCR allows us to do swapping,
+ * using 7 we swap the data bytes to match the incoming order:
+ * D0, D1, D2, D3, D4, D5, D6, D7
+ */
+ value = 0x00000007 | yuv_lineskip;
+
+ ceu_write(pcdev, CDOCR, value);
+ ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */
+
+ capture_restore(pcdev, capsr);
+
+ /* not in bundle mode: skip CBDSR, CDAYR2, CDACR2, CDBYR2, CDBCR2 */
+ return 0;
+}
+
+static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd,
+ unsigned char buswidth)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ struct v4l2_subdev *sd = find_bus_subdev(pcdev, icd);
+ unsigned long common_flags = CEU_BUS_FLAGS;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ int ret;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret)
+ common_flags = soc_mbus_config_compatible(&cfg,
+ common_flags);
+ else if (ret != -ENOIOCTLCMD)
+ return ret;
+
+ if (!common_flags || buswidth > 16)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct soc_mbus_pixelfmt sh_mobile_ceu_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .name = "NV12",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_1_5X8,
+ .order = SOC_MBUS_ORDER_LE,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .name = "NV21",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_1_5X8,
+ .order = SOC_MBUS_ORDER_LE,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .name = "NV16",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV61,
+ .name = "NV61",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+};
+
+/* This will be corrected as we get more formats */
+static bool sh_mobile_ceu_packing_supported(const struct soc_mbus_pixelfmt *fmt)
+{
+ return fmt->packing == SOC_MBUS_PACKING_NONE ||
+ (fmt->bits_per_sample == 8 &&
+ fmt->packing == SOC_MBUS_PACKING_1_5X8) ||
+ (fmt->bits_per_sample == 8 &&
+ fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) ||
+ (fmt->bits_per_sample > 8 &&
+ fmt->packing == SOC_MBUS_PACKING_EXTEND16);
+}
+
+static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect);
+
+static struct soc_camera_device *ctrl_to_icd(struct v4l2_ctrl *ctrl)
+{
+ return container_of(ctrl->handler, struct soc_camera_device,
+ ctrl_handler);
+}
+
+static int sh_mobile_ceu_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct soc_camera_device *icd = ctrl_to_icd(ctrl);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+
+ switch (ctrl->id) {
+ case V4L2_CID_SHARPNESS:
+ switch (icd->current_fmt->host_fmt->fourcc) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ ceu_write(pcdev, CLFCR, !ctrl->val);
+ return 0;
+ }
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static const struct v4l2_ctrl_ops sh_mobile_ceu_ctrl_ops = {
+ .s_ctrl = sh_mobile_ceu_s_ctrl,
+};
+
+static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int idx,
+ struct soc_camera_format_xlate *xlate)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ int ret, k, n;
+ int formats = 0;
+ struct sh_mobile_ceu_cam *cam;
+ enum v4l2_mbus_pixelcode code;
+ const struct soc_mbus_pixelfmt *fmt;
+
+ ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
+ if (ret < 0)
+ /* No more formats */
+ return 0;
+
+ fmt = soc_mbus_get_fmtdesc(code);
+ if (!fmt) {
+ dev_warn(dev, "unsupported format code #%u: %d\n", idx, code);
+ return 0;
+ }
+
+ if (!pcdev->pdata->csi2) {
+ /* Are there any restrictions in the CSI-2 case? */
+ ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample);
+ if (ret < 0)
+ return 0;
+ }
+
+ if (!icd->host_priv) {
+ struct v4l2_mbus_framefmt mf;
+ struct v4l2_rect rect;
+ int shift = 0;
+
+ /* Add our control */
+ v4l2_ctrl_new_std(&icd->ctrl_handler, &sh_mobile_ceu_ctrl_ops,
+ V4L2_CID_SHARPNESS, 0, 1, 1, 0);
+ if (icd->ctrl_handler.error)
+ return icd->ctrl_handler.error;
+
+ /* FIXME: subwindow is lost between close / open */
+
+ /* Cache current client geometry */
+ ret = client_g_rect(sd, &rect);
+ if (ret < 0)
+ return ret;
+
+ /* First time */
+ ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * All currently existing CEU implementations support 2560x1920
+ * or larger frames. If the sensor is proposing too big a frame,
+ * don't bother with possibly supportred by the CEU larger
+ * sizes, just try VGA multiples. If needed, this can be
+ * adjusted in the future.
+ */
+ while ((mf.width > pcdev->max_width ||
+ mf.height > pcdev->max_height) && shift < 4) {
+ /* Try 2560x1920, 1280x960, 640x480, 320x240 */
+ mf.width = 2560 >> shift;
+ mf.height = 1920 >> shift;
+ ret = v4l2_device_call_until_err(sd->v4l2_dev,
+ soc_camera_grp_id(icd), video,
+ s_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+ shift++;
+ }
+
+ if (shift == 4) {
+ dev_err(dev, "Failed to configure the client below %ux%x\n",
+ mf.width, mf.height);
+ return -EIO;
+ }
+
+ dev_geo(dev, "camera fmt %ux%u\n", mf.width, mf.height);
+
+ cam = kzalloc(sizeof(*cam), GFP_KERNEL);
+ if (!cam)
+ return -ENOMEM;
+
+ /* We are called with current camera crop, initialise subrect with it */
+ cam->rect = rect;
+ cam->subrect = rect;
+
+ cam->width = mf.width;
+ cam->height = mf.height;
+
+ icd->host_priv = cam;
+ } else {
+ cam = icd->host_priv;
+ }
+
+ /* Beginning of a pass */
+ if (!idx)
+ cam->extra_fmt = NULL;
+
+ switch (code) {
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ case V4L2_MBUS_FMT_VYUY8_2X8:
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ case V4L2_MBUS_FMT_YVYU8_2X8:
+ if (cam->extra_fmt)
+ break;
+
+ /*
+ * Our case is simple so far: for any of the above four camera
+ * formats we add all our four synthesized NV* formats, so,
+ * just marking the device with a single flag suffices. If
+ * the format generation rules are more complex, you would have
+ * to actually hang your already added / counted formats onto
+ * the host_priv pointer and check whether the format you're
+ * going to add now is already there.
+ */
+ cam->extra_fmt = sh_mobile_ceu_formats;
+
+ n = ARRAY_SIZE(sh_mobile_ceu_formats);
+ formats += n;
+ for (k = 0; xlate && k < n; k++) {
+ xlate->host_fmt = &sh_mobile_ceu_formats[k];
+ xlate->code = code;
+ xlate++;
+ dev_dbg(dev, "Providing format %s using code %d\n",
+ sh_mobile_ceu_formats[k].name, code);
+ }
+ break;
+ default:
+ if (!sh_mobile_ceu_packing_supported(fmt))
+ return 0;
+ }
+
+ /* Generic pass-through */
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = fmt;
+ xlate->code = code;
+ xlate++;
+ dev_dbg(dev, "Providing format %s in pass-through mode\n",
+ fmt->name);
+ }
+
+ return formats;
+}
+
+static void sh_mobile_ceu_put_formats(struct soc_camera_device *icd)
+{
+ kfree(icd->host_priv);
+ icd->host_priv = NULL;
+}
+
+/* Check if any dimension of r1 is smaller than respective one of r2 */
+static bool is_smaller(struct v4l2_rect *r1, struct v4l2_rect *r2)
+{
+ return r1->width < r2->width || r1->height < r2->height;
+}
+
+/* Check if r1 fails to cover r2 */
+static bool is_inside(struct v4l2_rect *r1, struct v4l2_rect *r2)
+{
+ return r1->left > r2->left || r1->top > r2->top ||
+ r1->left + r1->width < r2->left + r2->width ||
+ r1->top + r1->height < r2->top + r2->height;
+}
+
+static unsigned int scale_down(unsigned int size, unsigned int scale)
+{
+ return (size * 4096 + scale / 2) / scale;
+}
+
+static unsigned int calc_generic_scale(unsigned int input, unsigned int output)
+{
+ return (input * 4096 + output / 2) / output;
+}
+
+/* Get and store current client crop */
+static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect)
+{
+ struct v4l2_crop crop;
+ struct v4l2_cropcap cap;
+ int ret;
+
+ crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ ret = v4l2_subdev_call(sd, video, g_crop, &crop);
+ if (!ret) {
+ *rect = crop.c;
+ return ret;
+ }
+
+ /* Camera driver doesn't support .g_crop(), assume default rectangle */
+ cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ ret = v4l2_subdev_call(sd, video, cropcap, &cap);
+ if (!ret)
+ *rect = cap.defrect;
+
+ return ret;
+}
+
+/* Client crop has changed, update our sub-rectangle to remain within the area */
+static void update_subrect(struct sh_mobile_ceu_cam *cam)
+{
+ struct v4l2_rect *rect = &cam->rect, *subrect = &cam->subrect;
+
+ if (rect->width < subrect->width)
+ subrect->width = rect->width;
+
+ if (rect->height < subrect->height)
+ subrect->height = rect->height;
+
+ if (rect->left > subrect->left)
+ subrect->left = rect->left;
+ else if (rect->left + rect->width >
+ subrect->left + subrect->width)
+ subrect->left = rect->left + rect->width -
+ subrect->width;
+
+ if (rect->top > subrect->top)
+ subrect->top = rect->top;
+ else if (rect->top + rect->height >
+ subrect->top + subrect->height)
+ subrect->top = rect->top + rect->height -
+ subrect->height;
+}
+
+/*
+ * The common for both scaling and cropping iterative approach is:
+ * 1. try if the client can produce exactly what requested by the user
+ * 2. if (1) failed, try to double the client image until we get one big enough
+ * 3. if (2) failed, try to request the maximum image
+ */
+static int client_s_crop(struct soc_camera_device *icd, struct v4l2_crop *crop,
+ struct v4l2_crop *cam_crop)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_rect *rect = &crop->c, *cam_rect = &cam_crop->c;
+ struct device *dev = sd->v4l2_dev->dev;
+ struct sh_mobile_ceu_cam *cam = icd->host_priv;
+ struct v4l2_cropcap cap;
+ int ret;
+ unsigned int width, height;
+
+ v4l2_subdev_call(sd, video, s_crop, crop);
+ ret = client_g_rect(sd, cam_rect);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Now cam_crop contains the current camera input rectangle, and it must
+ * be within camera cropcap bounds
+ */
+ if (!memcmp(rect, cam_rect, sizeof(*rect))) {
+ /* Even if camera S_CROP failed, but camera rectangle matches */
+ dev_dbg(dev, "Camera S_CROP successful for %dx%d@%d:%d\n",
+ rect->width, rect->height, rect->left, rect->top);
+ cam->rect = *cam_rect;
+ return 0;
+ }
+
+ /* Try to fix cropping, that camera hasn't managed to set */
+ dev_geo(dev, "Fix camera S_CROP for %dx%d@%d:%d to %dx%d@%d:%d\n",
+ cam_rect->width, cam_rect->height,
+ cam_rect->left, cam_rect->top,
+ rect->width, rect->height, rect->left, rect->top);
+
+ /* We need sensor maximum rectangle */
+ ret = v4l2_subdev_call(sd, video, cropcap, &cap);
+ if (ret < 0)
+ return ret;
+
+ /* Put user requested rectangle within sensor bounds */
+ soc_camera_limit_side(&rect->left, &rect->width, cap.bounds.left, 2,
+ cap.bounds.width);
+ soc_camera_limit_side(&rect->top, &rect->height, cap.bounds.top, 4,
+ cap.bounds.height);
+
+ /*
+ * Popular special case - some cameras can only handle fixed sizes like
+ * QVGA, VGA,... Take care to avoid infinite loop.
+ */
+ width = max(cam_rect->width, 2);
+ height = max(cam_rect->height, 2);
+
+ /*
+ * Loop as long as sensor is not covering the requested rectangle and
+ * is still within its bounds
+ */
+ while (!ret && (is_smaller(cam_rect, rect) ||
+ is_inside(cam_rect, rect)) &&
+ (cap.bounds.width > width || cap.bounds.height > height)) {
+
+ width *= 2;
+ height *= 2;
+
+ cam_rect->width = width;
+ cam_rect->height = height;
+
+ /*
+ * We do not know what capabilities the camera has to set up
+ * left and top borders. We could try to be smarter in iterating
+ * them, e.g., if camera current left is to the right of the
+ * target left, set it to the middle point between the current
+ * left and minimum left. But that would add too much
+ * complexity: we would have to iterate each border separately.
+ * Instead we just drop to the left and top bounds.
+ */
+ if (cam_rect->left > rect->left)
+ cam_rect->left = cap.bounds.left;
+
+ if (cam_rect->left + cam_rect->width < rect->left + rect->width)
+ cam_rect->width = rect->left + rect->width -
+ cam_rect->left;
+
+ if (cam_rect->top > rect->top)
+ cam_rect->top = cap.bounds.top;
+
+ if (cam_rect->top + cam_rect->height < rect->top + rect->height)
+ cam_rect->height = rect->top + rect->height -
+ cam_rect->top;
+
+ v4l2_subdev_call(sd, video, s_crop, cam_crop);
+ ret = client_g_rect(sd, cam_rect);
+ dev_geo(dev, "Camera S_CROP %d for %dx%d@%d:%d\n", ret,
+ cam_rect->width, cam_rect->height,
+ cam_rect->left, cam_rect->top);
+ }
+
+ /* S_CROP must not modify the rectangle */
+ if (is_smaller(cam_rect, rect) || is_inside(cam_rect, rect)) {
+ /*
+ * The camera failed to configure a suitable cropping,
+ * we cannot use the current rectangle, set to max
+ */
+ *cam_rect = cap.bounds;
+ v4l2_subdev_call(sd, video, s_crop, cam_crop);
+ ret = client_g_rect(sd, cam_rect);
+ dev_geo(dev, "Camera S_CROP %d for max %dx%d@%d:%d\n", ret,
+ cam_rect->width, cam_rect->height,
+ cam_rect->left, cam_rect->top);
+ }
+
+ if (!ret) {
+ cam->rect = *cam_rect;
+ update_subrect(cam);
+ }
+
+ return ret;
+}
+
+/* Iterative s_mbus_fmt, also updates cached client crop on success */
+static int client_s_fmt(struct soc_camera_device *icd,
+ struct v4l2_mbus_framefmt *mf, bool ceu_can_scale)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ struct sh_mobile_ceu_cam *cam = icd->host_priv;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h;
+ unsigned int max_width, max_height;
+ struct v4l2_cropcap cap;
+ bool ceu_1to1;
+ int ret;
+
+ ret = v4l2_device_call_until_err(sd->v4l2_dev,
+ soc_camera_grp_id(icd), video,
+ s_mbus_fmt, mf);
+ if (ret < 0)
+ return ret;
+
+ dev_geo(dev, "camera scaled to %ux%u\n", mf->width, mf->height);
+
+ if (width == mf->width && height == mf->height) {
+ /* Perfect! The client has done it all. */
+ ceu_1to1 = true;
+ goto update_cache;
+ }
+
+ ceu_1to1 = false;
+ if (!ceu_can_scale)
+ goto update_cache;
+
+ cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ ret = v4l2_subdev_call(sd, video, cropcap, &cap);
+ if (ret < 0)
+ return ret;
+
+ max_width = min(cap.bounds.width, pcdev->max_width);
+ max_height = min(cap.bounds.height, pcdev->max_height);
+
+ /* Camera set a format, but geometry is not precise, try to improve */
+ tmp_w = mf->width;
+ tmp_h = mf->height;
+
+ /* width <= max_width && height <= max_height - guaranteed by try_fmt */
+ while ((width > tmp_w || height > tmp_h) &&
+ tmp_w < max_width && tmp_h < max_height) {
+ tmp_w = min(2 * tmp_w, max_width);
+ tmp_h = min(2 * tmp_h, max_height);
+ mf->width = tmp_w;
+ mf->height = tmp_h;
+ ret = v4l2_device_call_until_err(sd->v4l2_dev,
+ soc_camera_grp_id(icd), video,
+ s_mbus_fmt, mf);
+ dev_geo(dev, "Camera scaled to %ux%u\n",
+ mf->width, mf->height);
+ if (ret < 0) {
+ /* This shouldn't happen */
+ dev_err(dev, "Client failed to set format: %d\n", ret);
+ return ret;
+ }
+ }
+
+update_cache:
+ /* Update cache */
+ ret = client_g_rect(sd, &cam->rect);
+ if (ret < 0)
+ return ret;
+
+ if (ceu_1to1)
+ cam->subrect = cam->rect;
+ else
+ update_subrect(cam);
+
+ return 0;
+}
+
+/**
+ * @width - on output: user width, mapped back to input
+ * @height - on output: user height, mapped back to input
+ * @mf - in- / output camera output window
+ */
+static int client_scale(struct soc_camera_device *icd,
+ struct v4l2_mbus_framefmt *mf,
+ unsigned int *width, unsigned int *height,
+ bool ceu_can_scale)
+{
+ struct sh_mobile_ceu_cam *cam = icd->host_priv;
+ struct device *dev = icd->parent;
+ struct v4l2_mbus_framefmt mf_tmp = *mf;
+ unsigned int scale_h, scale_v;
+ int ret;
+
+ /*
+ * 5. Apply iterative camera S_FMT for camera user window (also updates
+ * client crop cache and the imaginary sub-rectangle).
+ */
+ ret = client_s_fmt(icd, &mf_tmp, ceu_can_scale);
+ if (ret < 0)
+ return ret;
+
+ dev_geo(dev, "5: camera scaled to %ux%u\n",
+ mf_tmp.width, mf_tmp.height);
+
+ /* 6. Retrieve camera output window (g_fmt) */
+
+ /* unneeded - it is already in "mf_tmp" */
+
+ /* 7. Calculate new client scales. */
+ scale_h = calc_generic_scale(cam->rect.width, mf_tmp.width);
+ scale_v = calc_generic_scale(cam->rect.height, mf_tmp.height);
+
+ mf->width = mf_tmp.width;
+ mf->height = mf_tmp.height;
+ mf->colorspace = mf_tmp.colorspace;
+
+ /*
+ * 8. Calculate new CEU crop - apply camera scales to previously
+ * updated "effective" crop.
+ */
+ *width = scale_down(cam->subrect.width, scale_h);
+ *height = scale_down(cam->subrect.height, scale_v);
+
+ dev_geo(dev, "8: new client sub-window %ux%u\n", *width, *height);
+
+ return 0;
+}
+
+/*
+ * CEU can scale and crop, but we don't want to waste bandwidth and kill the
+ * framerate by always requesting the maximum image from the client. See
+ * Documentation/video4linux/sh_mobile_ceu_camera.txt for a description of
+ * scaling and cropping algorithms and for the meaning of referenced here steps.
+ */
+static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd,
+ struct v4l2_crop *a)
+{
+ struct v4l2_rect *rect = &a->c;
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ struct v4l2_crop cam_crop;
+ struct sh_mobile_ceu_cam *cam = icd->host_priv;
+ struct v4l2_rect *cam_rect = &cam_crop.c;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_mbus_framefmt mf;
+ unsigned int scale_cam_h, scale_cam_v, scale_ceu_h, scale_ceu_v,
+ out_width, out_height;
+ int interm_width, interm_height;
+ u32 capsr, cflcr;
+ int ret;
+
+ dev_geo(dev, "S_CROP(%ux%u@%u:%u)\n", rect->width, rect->height,
+ rect->left, rect->top);
+
+ /* During camera cropping its output window can change too, stop CEU */
+ capsr = capture_save_reset(pcdev);
+ dev_dbg(dev, "CAPSR 0x%x, CFLCR 0x%x\n", capsr, pcdev->cflcr);
+
+ /*
+ * 1. - 2. Apply iterative camera S_CROP for new input window, read back
+ * actual camera rectangle.
+ */
+ ret = client_s_crop(icd, a, &cam_crop);
+ if (ret < 0)
+ return ret;
+
+ dev_geo(dev, "1-2: camera cropped to %ux%u@%u:%u\n",
+ cam_rect->width, cam_rect->height,
+ cam_rect->left, cam_rect->top);
+
+ /* On success cam_crop contains current camera crop */
+
+ /* 3. Retrieve camera output window */
+ ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ if (mf.width > pcdev->max_width || mf.height > pcdev->max_height)
+ return -EINVAL;
+
+ /* 4. Calculate camera scales */
+ scale_cam_h = calc_generic_scale(cam_rect->width, mf.width);
+ scale_cam_v = calc_generic_scale(cam_rect->height, mf.height);
+
+ /* Calculate intermediate window */
+ interm_width = scale_down(rect->width, scale_cam_h);
+ interm_height = scale_down(rect->height, scale_cam_v);
+
+ if (interm_width < icd->user_width) {
+ u32 new_scale_h;
+
+ new_scale_h = calc_generic_scale(rect->width, icd->user_width);
+
+ mf.width = scale_down(cam_rect->width, new_scale_h);
+ }
+
+ if (interm_height < icd->user_height) {
+ u32 new_scale_v;
+
+ new_scale_v = calc_generic_scale(rect->height, icd->user_height);
+
+ mf.height = scale_down(cam_rect->height, new_scale_v);
+ }
+
+ if (interm_width < icd->user_width || interm_height < icd->user_height) {
+ ret = v4l2_device_call_until_err(sd->v4l2_dev,
+ soc_camera_grp_id(icd), video,
+ s_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ dev_geo(dev, "New camera output %ux%u\n", mf.width, mf.height);
+ scale_cam_h = calc_generic_scale(cam_rect->width, mf.width);
+ scale_cam_v = calc_generic_scale(cam_rect->height, mf.height);
+ interm_width = scale_down(rect->width, scale_cam_h);
+ interm_height = scale_down(rect->height, scale_cam_v);
+ }
+
+ /* Cache camera output window */
+ cam->width = mf.width;
+ cam->height = mf.height;
+
+ if (pcdev->image_mode) {
+ out_width = min(interm_width, icd->user_width);
+ out_height = min(interm_height, icd->user_height);
+ } else {
+ out_width = interm_width;
+ out_height = interm_height;
+ }
+
+ /*
+ * 5. Calculate CEU scales from camera scales from results of (5) and
+ * the user window
+ */
+ scale_ceu_h = calc_scale(interm_width, &out_width);
+ scale_ceu_v = calc_scale(interm_height, &out_height);
+
+ dev_geo(dev, "5: CEU scales %u:%u\n", scale_ceu_h, scale_ceu_v);
+
+ /* Apply CEU scales. */
+ cflcr = scale_ceu_h | (scale_ceu_v << 16);
+ if (cflcr != pcdev->cflcr) {
+ pcdev->cflcr = cflcr;
+ ceu_write(pcdev, CFLCR, cflcr);
+ }
+
+ icd->user_width = out_width & ~3;
+ icd->user_height = out_height & ~3;
+ /* Offsets are applied at the CEU scaling filter input */
+ cam->ceu_left = scale_down(rect->left - cam_rect->left, scale_cam_h) & ~1;
+ cam->ceu_top = scale_down(rect->top - cam_rect->top, scale_cam_v) & ~1;
+
+ /* 6. Use CEU cropping to crop to the new window. */
+ sh_mobile_ceu_set_rect(icd);
+
+ cam->subrect = *rect;
+
+ dev_geo(dev, "6: CEU cropped to %ux%u@%u:%u\n",
+ icd->user_width, icd->user_height,
+ cam->ceu_left, cam->ceu_top);
+
+ /* Restore capture. The CE bit can be cleared by the hardware */
+ if (pcdev->active)
+ capsr |= 1;
+ capture_restore(pcdev, capsr);
+
+ /* Even if only camera cropping succeeded */
+ return ret;
+}
+
+static int sh_mobile_ceu_get_crop(struct soc_camera_device *icd,
+ struct v4l2_crop *a)
+{
+ struct sh_mobile_ceu_cam *cam = icd->host_priv;
+
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->c = cam->subrect;
+
+ return 0;
+}
+
+/*
+ * Calculate real client output window by applying new scales to the current
+ * client crop. New scales are calculated from the requested output format and
+ * CEU crop, mapped backed onto the client input (subrect).
+ */
+static void calculate_client_output(struct soc_camera_device *icd,
+ const struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf)
+{
+ struct sh_mobile_ceu_cam *cam = icd->host_priv;
+ struct device *dev = icd->parent;
+ struct v4l2_rect *cam_subrect = &cam->subrect;
+ unsigned int scale_v, scale_h;
+
+ if (cam_subrect->width == cam->rect.width &&
+ cam_subrect->height == cam->rect.height) {
+ /* No sub-cropping */
+ mf->width = pix->width;
+ mf->height = pix->height;
+ return;
+ }
+
+ /* 1.-2. Current camera scales and subwin - cached. */
+
+ dev_geo(dev, "2: subwin %ux%u@%u:%u\n",
+ cam_subrect->width, cam_subrect->height,
+ cam_subrect->left, cam_subrect->top);
+
+ /*
+ * 3. Calculate new combined scales from input sub-window to requested
+ * user window.
+ */
+
+ /*
+ * TODO: CEU cannot scale images larger than VGA to smaller than SubQCIF
+ * (128x96) or larger than VGA
+ */
+ scale_h = calc_generic_scale(cam_subrect->width, pix->width);
+ scale_v = calc_generic_scale(cam_subrect->height, pix->height);
+
+ dev_geo(dev, "3: scales %u:%u\n", scale_h, scale_v);
+
+ /*
+ * 4. Calculate desired client output window by applying combined scales
+ * to client (real) input window.
+ */
+ mf->width = scale_down(cam->rect.width, scale_h);
+ mf->height = scale_down(cam->rect.height, scale_v);
+}
+
+/* Similar to set_crop multistage iterative algorithm */
+static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ struct sh_mobile_ceu_cam *cam = icd->host_priv;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_mbus_framefmt mf;
+ __u32 pixfmt = pix->pixelformat;
+ const struct soc_camera_format_xlate *xlate;
+ /* Keep Compiler Happy */
+ unsigned int ceu_sub_width = 0, ceu_sub_height = 0;
+ u16 scale_v, scale_h;
+ int ret;
+ bool image_mode;
+ enum v4l2_field field;
+
+ switch (pix->field) {
+ default:
+ pix->field = V4L2_FIELD_NONE;
+ /* fall-through */
+ case V4L2_FIELD_INTERLACED_TB:
+ case V4L2_FIELD_INTERLACED_BT:
+ case V4L2_FIELD_NONE:
+ field = pix->field;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ field = V4L2_FIELD_INTERLACED_TB;
+ break;
+ }
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (!xlate) {
+ dev_warn(dev, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+
+ /* 1.-4. Calculate desired client output geometry */
+ calculate_client_output(icd, pix, &mf);
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
+
+ switch (pixfmt) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ image_mode = true;
+ break;
+ default:
+ image_mode = false;
+ }
+
+ dev_geo(dev, "S_FMT(pix=0x%x, fld 0x%x, code 0x%x, %ux%u)\n", pixfmt, mf.field, mf.code,
+ pix->width, pix->height);
+
+ dev_geo(dev, "4: request camera output %ux%u\n", mf.width, mf.height);
+
+ /* 5. - 9. */
+ ret = client_scale(icd, &mf, &ceu_sub_width, &ceu_sub_height,
+ image_mode && V4L2_FIELD_NONE == field);
+
+ dev_geo(dev, "5-9: client scale return %d\n", ret);
+
+ /* Done with the camera. Now see if we can improve the result */
+
+ dev_geo(dev, "fmt %ux%u, requested %ux%u\n",
+ mf.width, mf.height, pix->width, pix->height);
+ if (ret < 0)
+ return ret;
+
+ if (mf.code != xlate->code)
+ return -EINVAL;
+
+ /* 9. Prepare CEU crop */
+ cam->width = mf.width;
+ cam->height = mf.height;
+
+ /* 10. Use CEU scaling to scale to the requested user window. */
+
+ /* We cannot scale up */
+ if (pix->width > ceu_sub_width)
+ ceu_sub_width = pix->width;
+
+ if (pix->height > ceu_sub_height)
+ ceu_sub_height = pix->height;
+
+ pix->colorspace = mf.colorspace;
+
+ if (image_mode) {
+ /* Scale pix->{width x height} down to width x height */
+ scale_h = calc_scale(ceu_sub_width, &pix->width);
+ scale_v = calc_scale(ceu_sub_height, &pix->height);
+ } else {
+ pix->width = ceu_sub_width;
+ pix->height = ceu_sub_height;
+ scale_h = 0;
+ scale_v = 0;
+ }
+
+ pcdev->cflcr = scale_h | (scale_v << 16);
+
+ /*
+ * We have calculated CFLCR, the actual configuration will be performed
+ * in sh_mobile_ceu_set_bus_param()
+ */
+
+ dev_geo(dev, "10: W: %u : 0x%x = %u, H: %u : 0x%x = %u\n",
+ ceu_sub_width, scale_h, pix->width,
+ ceu_sub_height, scale_v, pix->height);
+
+ cam->code = xlate->code;
+ icd->current_fmt = xlate;
+
+ pcdev->field = field;
+ pcdev->image_mode = image_mode;
+
+ /* CFSZR requirement */
+ pix->width &= ~3;
+ pix->height &= ~3;
+
+ return 0;
+}
+
+static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_mbus_framefmt mf;
+ __u32 pixfmt = pix->pixelformat;
+ int width, height;
+ int ret;
+
+ dev_geo(icd->parent, "TRY_FMT(pix=0x%x, %ux%u)\n",
+ pixfmt, pix->width, pix->height);
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (!xlate) {
+ dev_warn(icd->parent, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+
+ /* FIXME: calculate using depth and bus width */
+
+ /* CFSZR requires height and width to be 4-pixel aligned */
+ v4l_bound_align_image(&pix->width, 2, pcdev->max_width, 2,
+ &pix->height, 4, pcdev->max_height, 2, 0);
+
+ width = pix->width;
+ height = pix->height;
+
+ /* limit to sensor capabilities */
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.code = xlate->code;
+ mf.colorspace = pix->colorspace;
+
+ ret = v4l2_device_call_until_err(sd->v4l2_dev, soc_camera_grp_id(icd),
+ video, try_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->field = mf.field;
+ pix->colorspace = mf.colorspace;
+
+ switch (pixfmt) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ /* FIXME: check against rect_max after converting soc-camera */
+ /* We can scale precisely, need a bigger image from camera */
+ if (pix->width < width || pix->height < height) {
+ /*
+ * We presume, the sensor behaves sanely, i.e., if
+ * requested a bigger rectangle, it will not return a
+ * smaller one.
+ */
+ mf.width = pcdev->max_width;
+ mf.height = pcdev->max_height;
+ ret = v4l2_device_call_until_err(sd->v4l2_dev,
+ soc_camera_grp_id(icd), video,
+ try_mbus_fmt, &mf);
+ if (ret < 0) {
+ /* Shouldn't actually happen... */
+ dev_err(icd->parent,
+ "FIXME: client try_fmt() = %d\n", ret);
+ return ret;
+ }
+ }
+ /* We will scale exactly */
+ if (mf.width > width)
+ pix->width = width;
+ if (mf.height > height)
+ pix->height = height;
+ }
+
+ pix->width &= ~3;
+ pix->height &= ~3;
+
+ dev_geo(icd->parent, "%s(): return %d, fmt 0x%x, %ux%u\n",
+ __func__, ret, pix->pixelformat, pix->width, pix->height);
+
+ return ret;
+}
+
+static int sh_mobile_ceu_set_livecrop(struct soc_camera_device *icd,
+ struct v4l2_crop *a)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ u32 out_width = icd->user_width, out_height = icd->user_height;
+ int ret;
+
+ /* Freeze queue */
+ pcdev->frozen = 1;
+ /* Wait for frame */
+ ret = wait_for_completion_interruptible(&pcdev->complete);
+ /* Stop the client */
+ ret = v4l2_subdev_call(sd, video, s_stream, 0);
+ if (ret < 0)
+ dev_warn(icd->parent,
+ "Client failed to stop the stream: %d\n", ret);
+ else
+ /* Do the crop, if it fails, there's nothing more we can do */
+ sh_mobile_ceu_set_crop(icd, a);
+
+ dev_geo(icd->parent, "Output after crop: %ux%u\n", icd->user_width, icd->user_height);
+
+ if (icd->user_width != out_width || icd->user_height != out_height) {
+ struct v4l2_format f = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .fmt.pix = {
+ .width = out_width,
+ .height = out_height,
+ .pixelformat = icd->current_fmt->host_fmt->fourcc,
+ .field = pcdev->field,
+ .colorspace = icd->colorspace,
+ },
+ };
+ ret = sh_mobile_ceu_set_fmt(icd, &f);
+ if (!ret && (out_width != f.fmt.pix.width ||
+ out_height != f.fmt.pix.height))
+ ret = -EINVAL;
+ if (!ret) {
+ icd->user_width = out_width & ~3;
+ icd->user_height = out_height & ~3;
+ ret = sh_mobile_ceu_set_bus_param(icd);
+ }
+ }
+
+ /* Thaw the queue */
+ pcdev->frozen = 0;
+ spin_lock_irq(&pcdev->lock);
+ sh_mobile_ceu_capture(pcdev);
+ spin_unlock_irq(&pcdev->lock);
+ /* Start the client */
+ ret = v4l2_subdev_call(sd, video, s_stream, 1);
+ return ret;
+}
+
+static unsigned int sh_mobile_ceu_poll(struct file *file, poll_table *pt)
+{
+ struct soc_camera_device *icd = file->private_data;
+
+ return vb2_poll(&icd->vb2_vidq, file, pt);
+}
+
+static int sh_mobile_ceu_querycap(struct soc_camera_host *ici,
+ struct v4l2_capability *cap)
+{
+ strlcpy(cap->card, "SuperH_Mobile_CEU", sizeof(cap->card));
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ return 0;
+}
+
+static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q,
+ struct soc_camera_device *icd)
+{
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->drv_priv = icd;
+ q->ops = &sh_mobile_ceu_videobuf_ops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct sh_mobile_ceu_buffer);
+
+ return vb2_queue_init(q);
+}
+
+static struct soc_camera_host_ops sh_mobile_ceu_host_ops = {
+ .owner = THIS_MODULE,
+ .add = sh_mobile_ceu_add_device,
+ .remove = sh_mobile_ceu_remove_device,
+ .get_formats = sh_mobile_ceu_get_formats,
+ .put_formats = sh_mobile_ceu_put_formats,
+ .get_crop = sh_mobile_ceu_get_crop,
+ .set_crop = sh_mobile_ceu_set_crop,
+ .set_livecrop = sh_mobile_ceu_set_livecrop,
+ .set_fmt = sh_mobile_ceu_set_fmt,
+ .try_fmt = sh_mobile_ceu_try_fmt,
+ .poll = sh_mobile_ceu_poll,
+ .querycap = sh_mobile_ceu_querycap,
+ .set_bus_param = sh_mobile_ceu_set_bus_param,
+ .init_videobuf2 = sh_mobile_ceu_init_videobuf,
+};
+
+struct bus_wait {
+ struct notifier_block notifier;
+ struct completion completion;
+ struct device *dev;
+};
+
+static int bus_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct device *dev = data;
+ struct bus_wait *wait = container_of(nb, struct bus_wait, notifier);
+
+ if (wait->dev != dev)
+ return NOTIFY_DONE;
+
+ switch (action) {
+ case BUS_NOTIFY_UNBOUND_DRIVER:
+ /* Protect from module unloading */
+ wait_for_completion(&wait->completion);
+ return NOTIFY_OK;
+ }
+ return NOTIFY_DONE;
+}
+
+static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev)
+{
+ struct sh_mobile_ceu_dev *pcdev;
+ struct resource *res;
+ void __iomem *base;
+ unsigned int irq;
+ int err = 0;
+ struct bus_wait wait = {
+ .completion = COMPLETION_INITIALIZER_ONSTACK(wait.completion),
+ .notifier.notifier_call = bus_notify,
+ };
+ struct sh_mobile_ceu_companion *csi2;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (!res || (int)irq <= 0) {
+ dev_err(&pdev->dev, "Not enough CEU platform resources.\n");
+ err = -ENODEV;
+ goto exit;
+ }
+
+ pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL);
+ if (!pcdev) {
+ dev_err(&pdev->dev, "Could not allocate pcdev\n");
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ INIT_LIST_HEAD(&pcdev->capture);
+ spin_lock_init(&pcdev->lock);
+ init_completion(&pcdev->complete);
+
+ pcdev->pdata = pdev->dev.platform_data;
+ if (!pcdev->pdata) {
+ err = -EINVAL;
+ dev_err(&pdev->dev, "CEU platform data not set.\n");
+ goto exit_kfree;
+ }
+
+ pcdev->max_width = pcdev->pdata->max_width ? : 2560;
+ pcdev->max_height = pcdev->pdata->max_height ? : 1920;
+
+ base = ioremap_nocache(res->start, resource_size(res));
+ if (!base) {
+ err = -ENXIO;
+ dev_err(&pdev->dev, "Unable to ioremap CEU registers.\n");
+ goto exit_kfree;
+ }
+
+ pcdev->irq = irq;
+ pcdev->base = base;
+ pcdev->video_limit = 0; /* only enabled if second resource exists */
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res) {
+ err = dma_declare_coherent_memory(&pdev->dev, res->start,
+ res->start,
+ resource_size(res),
+ DMA_MEMORY_MAP |
+ DMA_MEMORY_EXCLUSIVE);
+ if (!err) {
+ dev_err(&pdev->dev, "Unable to declare CEU memory.\n");
+ err = -ENXIO;
+ goto exit_iounmap;
+ }
+
+ pcdev->video_limit = resource_size(res);
+ }
+
+ /* request irq */
+ err = request_irq(pcdev->irq, sh_mobile_ceu_irq, IRQF_DISABLED,
+ dev_name(&pdev->dev), pcdev);
+ if (err) {
+ dev_err(&pdev->dev, "Unable to register CEU interrupt.\n");
+ goto exit_release_mem;
+ }
+
+ pm_suspend_ignore_children(&pdev->dev, true);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_resume(&pdev->dev);
+
+ pcdev->ici.priv = pcdev;
+ pcdev->ici.v4l2_dev.dev = &pdev->dev;
+ pcdev->ici.nr = pdev->id;
+ pcdev->ici.drv_name = dev_name(&pdev->dev);
+ pcdev->ici.ops = &sh_mobile_ceu_host_ops;
+
+ pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(pcdev->alloc_ctx)) {
+ err = PTR_ERR(pcdev->alloc_ctx);
+ goto exit_free_clk;
+ }
+
+ err = soc_camera_host_register(&pcdev->ici);
+ if (err)
+ goto exit_free_ctx;
+
+ /* CSI2 interfacing */
+ csi2 = pcdev->pdata->csi2;
+ if (csi2) {
+ struct platform_device *csi2_pdev =
+ platform_device_alloc("sh-mobile-csi2", csi2->id);
+ struct sh_csi2_pdata *csi2_pdata = csi2->platform_data;
+
+ if (!csi2_pdev) {
+ err = -ENOMEM;
+ goto exit_host_unregister;
+ }
+
+ pcdev->csi2_pdev = csi2_pdev;
+
+ err = platform_device_add_data(csi2_pdev, csi2_pdata, sizeof(*csi2_pdata));
+ if (err < 0)
+ goto exit_pdev_put;
+
+ csi2_pdata = csi2_pdev->dev.platform_data;
+ csi2_pdata->v4l2_dev = &pcdev->ici.v4l2_dev;
+
+ csi2_pdev->resource = csi2->resource;
+ csi2_pdev->num_resources = csi2->num_resources;
+
+ err = platform_device_add(csi2_pdev);
+ if (err < 0)
+ goto exit_pdev_put;
+
+ wait.dev = &csi2_pdev->dev;
+
+ err = bus_register_notifier(&platform_bus_type, &wait.notifier);
+ if (err < 0)
+ goto exit_pdev_unregister;
+
+ /*
+ * From this point the driver module will not unload, until
+ * we complete the completion.
+ */
+
+ if (!csi2_pdev->dev.driver) {
+ complete(&wait.completion);
+ /* Either too late, or probing failed */
+ bus_unregister_notifier(&platform_bus_type, &wait.notifier);
+ err = -ENXIO;
+ goto exit_pdev_unregister;
+ }
+
+ /*
+ * The module is still loaded, in the worst case it is hanging
+ * in device release on our completion. So, _now_ dereferencing
+ * the "owner" is safe!
+ */
+
+ err = try_module_get(csi2_pdev->dev.driver->owner);
+
+ /* Let notifier complete, if it has been locked */
+ complete(&wait.completion);
+ bus_unregister_notifier(&platform_bus_type, &wait.notifier);
+ if (!err) {
+ err = -ENODEV;
+ goto exit_pdev_unregister;
+ }
+ }
+
+ return 0;
+
+exit_pdev_unregister:
+ platform_device_del(pcdev->csi2_pdev);
+exit_pdev_put:
+ pcdev->csi2_pdev->resource = NULL;
+ platform_device_put(pcdev->csi2_pdev);
+exit_host_unregister:
+ soc_camera_host_unregister(&pcdev->ici);
+exit_free_ctx:
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
+exit_free_clk:
+ pm_runtime_disable(&pdev->dev);
+ free_irq(pcdev->irq, pcdev);
+exit_release_mem:
+ if (platform_get_resource(pdev, IORESOURCE_MEM, 1))
+ dma_release_declared_memory(&pdev->dev);
+exit_iounmap:
+ iounmap(base);
+exit_kfree:
+ kfree(pcdev);
+exit:
+ return err;
+}
+
+static int __devexit sh_mobile_ceu_remove(struct platform_device *pdev)
+{
+ struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+ struct sh_mobile_ceu_dev *pcdev = container_of(soc_host,
+ struct sh_mobile_ceu_dev, ici);
+ struct platform_device *csi2_pdev = pcdev->csi2_pdev;
+
+ soc_camera_host_unregister(soc_host);
+ pm_runtime_disable(&pdev->dev);
+ free_irq(pcdev->irq, pcdev);
+ if (platform_get_resource(pdev, IORESOURCE_MEM, 1))
+ dma_release_declared_memory(&pdev->dev);
+ iounmap(pcdev->base);
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
+ if (csi2_pdev && csi2_pdev->dev.driver) {
+ struct module *csi2_drv = csi2_pdev->dev.driver->owner;
+ platform_device_del(csi2_pdev);
+ csi2_pdev->resource = NULL;
+ platform_device_put(csi2_pdev);
+ module_put(csi2_drv);
+ }
+ kfree(pcdev);
+
+ return 0;
+}
+
+static int sh_mobile_ceu_runtime_nop(struct device *dev)
+{
+ /* Runtime PM callback shared between ->runtime_suspend()
+ * and ->runtime_resume(). Simply returns success.
+ *
+ * This driver re-initializes all registers after
+ * pm_runtime_get_sync() anyway so there is no need
+ * to save and restore registers here.
+ */
+ return 0;
+}
+
+static const struct dev_pm_ops sh_mobile_ceu_dev_pm_ops = {
+ .runtime_suspend = sh_mobile_ceu_runtime_nop,
+ .runtime_resume = sh_mobile_ceu_runtime_nop,
+};
+
+static struct platform_driver sh_mobile_ceu_driver = {
+ .driver = {
+ .name = "sh_mobile_ceu",
+ .pm = &sh_mobile_ceu_dev_pm_ops,
+ },
+ .probe = sh_mobile_ceu_probe,
+ .remove = __devexit_p(sh_mobile_ceu_remove),
+};
+
+static int __init sh_mobile_ceu_init(void)
+{
+ /* Whatever return code */
+ request_module("sh_mobile_csi2");
+ return platform_driver_register(&sh_mobile_ceu_driver);
+}
+
+static void __exit sh_mobile_ceu_exit(void)
+{
+ platform_driver_unregister(&sh_mobile_ceu_driver);
+}
+
+module_init(sh_mobile_ceu_init);
+module_exit(sh_mobile_ceu_exit);
+
+MODULE_DESCRIPTION("SuperH Mobile CEU driver");
+MODULE_AUTHOR("Magnus Damm");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.6");
+MODULE_ALIAS("platform:sh_mobile_ceu");
diff --git a/drivers/media/video/sh_mobile_csi2.c b/drivers/media/video/sh_mobile_csi2.c
new file mode 100644
index 00000000..05286500
--- /dev/null
+++ b/drivers/media/video/sh_mobile_csi2.c
@@ -0,0 +1,398 @@
+/*
+ * Driver for the SH-Mobile MIPI CSI-2 unit
+ *
+ * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/module.h>
+
+#include <media/sh_mobile_ceu.h>
+#include <media/sh_mobile_csi2.h>
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mediabus.h>
+#include <media/v4l2-subdev.h>
+
+#define SH_CSI2_TREF 0x00
+#define SH_CSI2_SRST 0x04
+#define SH_CSI2_PHYCNT 0x08
+#define SH_CSI2_CHKSUM 0x0C
+#define SH_CSI2_VCDT 0x10
+
+struct sh_csi2 {
+ struct v4l2_subdev subdev;
+ struct list_head list;
+ unsigned int irq;
+ unsigned long mipi_flags;
+ void __iomem *base;
+ struct platform_device *pdev;
+ struct sh_csi2_client_config *client;
+};
+
+static int sh_csi2_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
+ struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
+
+ if (mf->width > 8188)
+ mf->width = 8188;
+ else if (mf->width & 1)
+ mf->width &= ~1;
+
+ switch (pdata->type) {
+ case SH_CSI2C:
+ switch (mf->code) {
+ case V4L2_MBUS_FMT_UYVY8_2X8: /* YUV422 */
+ case V4L2_MBUS_FMT_YUYV8_1_5X8: /* YUV420 */
+ case V4L2_MBUS_FMT_Y8_1X8: /* RAW8 */
+ case V4L2_MBUS_FMT_SBGGR8_1X8:
+ case V4L2_MBUS_FMT_SGRBG8_1X8:
+ break;
+ default:
+ /* All MIPI CSI-2 devices must support one of primary formats */
+ mf->code = V4L2_MBUS_FMT_YUYV8_2X8;
+ }
+ break;
+ case SH_CSI2I:
+ switch (mf->code) {
+ case V4L2_MBUS_FMT_Y8_1X8: /* RAW8 */
+ case V4L2_MBUS_FMT_SBGGR8_1X8:
+ case V4L2_MBUS_FMT_SGRBG8_1X8:
+ case V4L2_MBUS_FMT_SBGGR10_1X10: /* RAW10 */
+ case V4L2_MBUS_FMT_SBGGR12_1X12: /* RAW12 */
+ break;
+ default:
+ /* All MIPI CSI-2 devices must support one of primary formats */
+ mf->code = V4L2_MBUS_FMT_SBGGR8_1X8;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * We have done our best in try_fmt to try and tell the sensor, which formats
+ * we support. If now the configuration is unsuitable for us we can only
+ * error out.
+ */
+static int sh_csi2_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
+ u32 tmp = (priv->client->channel & 3) << 8;
+
+ dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code);
+ if (mf->width > 8188 || mf->width & 1)
+ return -EINVAL;
+
+ switch (mf->code) {
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ tmp |= 0x1e; /* YUV422 8 bit */
+ break;
+ case V4L2_MBUS_FMT_YUYV8_1_5X8:
+ tmp |= 0x18; /* YUV420 8 bit */
+ break;
+ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE:
+ tmp |= 0x21; /* RGB555 */
+ break;
+ case V4L2_MBUS_FMT_RGB565_2X8_BE:
+ tmp |= 0x22; /* RGB565 */
+ break;
+ case V4L2_MBUS_FMT_Y8_1X8:
+ case V4L2_MBUS_FMT_SBGGR8_1X8:
+ case V4L2_MBUS_FMT_SGRBG8_1X8:
+ tmp |= 0x2a; /* RAW8 */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ iowrite32(tmp, priv->base + SH_CSI2_VCDT);
+
+ return 0;
+}
+
+static int sh_csi2_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+
+ return 0;
+}
+
+static int sh_csi2_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
+ struct soc_camera_device *icd = v4l2_get_subdev_hostdata(sd);
+ struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
+ struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2,
+ .flags = priv->mipi_flags};
+
+ return v4l2_subdev_call(client_sd, video, s_mbus_config, &client_cfg);
+}
+
+static struct v4l2_subdev_video_ops sh_csi2_subdev_video_ops = {
+ .s_mbus_fmt = sh_csi2_s_fmt,
+ .try_mbus_fmt = sh_csi2_try_fmt,
+ .g_mbus_config = sh_csi2_g_mbus_config,
+ .s_mbus_config = sh_csi2_s_mbus_config,
+};
+
+static void sh_csi2_hwinit(struct sh_csi2 *priv)
+{
+ struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
+ __u32 tmp = 0x10; /* Enable MIPI CSI clock lane */
+
+ /* Reflect registers immediately */
+ iowrite32(0x00000001, priv->base + SH_CSI2_TREF);
+ /* reset CSI2 harware */
+ iowrite32(0x00000001, priv->base + SH_CSI2_SRST);
+ udelay(5);
+ iowrite32(0x00000000, priv->base + SH_CSI2_SRST);
+
+ switch (pdata->type) {
+ case SH_CSI2C:
+ if (priv->client->lanes == 1)
+ tmp |= 1;
+ else
+ /* Default - both lanes */
+ tmp |= 3;
+ break;
+ case SH_CSI2I:
+ if (!priv->client->lanes || priv->client->lanes > 4)
+ /* Default - all 4 lanes */
+ tmp |= 0xf;
+ else
+ tmp |= (1 << priv->client->lanes) - 1;
+ }
+
+ if (priv->client->phy == SH_CSI2_PHY_MAIN)
+ tmp |= 0x8000;
+
+ iowrite32(tmp, priv->base + SH_CSI2_PHYCNT);
+
+ tmp = 0;
+ if (pdata->flags & SH_CSI2_ECC)
+ tmp |= 2;
+ if (pdata->flags & SH_CSI2_CRC)
+ tmp |= 1;
+ iowrite32(tmp, priv->base + SH_CSI2_CHKSUM);
+}
+
+static int sh_csi2_client_connect(struct sh_csi2 *priv)
+{
+ struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
+ struct soc_camera_device *icd = v4l2_get_subdev_hostdata(&priv->subdev);
+ struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
+ struct device *dev = v4l2_get_subdevdata(&priv->subdev);
+ struct v4l2_mbus_config cfg;
+ unsigned long common_flags, csi2_flags;
+ int i, ret;
+
+ if (priv->client)
+ return -EBUSY;
+
+ for (i = 0; i < pdata->num_clients; i++)
+ if (&pdata->clients[i].pdev->dev == icd->pdev)
+ break;
+
+ dev_dbg(dev, "%s(%p): found #%d\n", __func__, dev, i);
+
+ if (i == pdata->num_clients)
+ return -ENODEV;
+
+ /* Check if we can support this camera */
+ csi2_flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK | V4L2_MBUS_CSI2_1_LANE;
+
+ switch (pdata->type) {
+ case SH_CSI2C:
+ if (pdata->clients[i].lanes != 1)
+ csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
+ break;
+ case SH_CSI2I:
+ switch (pdata->clients[i].lanes) {
+ default:
+ csi2_flags |= V4L2_MBUS_CSI2_4_LANE;
+ case 3:
+ csi2_flags |= V4L2_MBUS_CSI2_3_LANE;
+ case 2:
+ csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
+ }
+ }
+
+ cfg.type = V4L2_MBUS_CSI2;
+ ret = v4l2_subdev_call(client_sd, video, g_mbus_config, &cfg);
+ if (ret == -ENOIOCTLCMD)
+ common_flags = csi2_flags;
+ else if (!ret)
+ common_flags = soc_mbus_config_compatible(&cfg,
+ csi2_flags);
+ else
+ common_flags = 0;
+
+ if (!common_flags)
+ return -EINVAL;
+
+ /* All good: camera MIPI configuration supported */
+ priv->mipi_flags = common_flags;
+ priv->client = pdata->clients + i;
+
+ pm_runtime_get_sync(dev);
+
+ sh_csi2_hwinit(priv);
+
+ return 0;
+}
+
+static void sh_csi2_client_disconnect(struct sh_csi2 *priv)
+{
+ if (!priv->client)
+ return;
+
+ priv->client = NULL;
+
+ pm_runtime_put(v4l2_get_subdevdata(&priv->subdev));
+}
+
+static int sh_csi2_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
+
+ if (on)
+ return sh_csi2_client_connect(priv);
+
+ sh_csi2_client_disconnect(priv);
+ return 0;
+}
+
+static struct v4l2_subdev_core_ops sh_csi2_subdev_core_ops = {
+ .s_power = sh_csi2_s_power,
+};
+
+static struct v4l2_subdev_ops sh_csi2_subdev_ops = {
+ .core = &sh_csi2_subdev_core_ops,
+ .video = &sh_csi2_subdev_video_ops,
+};
+
+static __devinit int sh_csi2_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ unsigned int irq;
+ int ret;
+ struct sh_csi2 *priv;
+ /* Platform data specify the PHY, lanes, ECC, CRC */
+ struct sh_csi2_pdata *pdata = pdev->dev.platform_data;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ /* Interrupt unused so far */
+ irq = platform_get_irq(pdev, 0);
+
+ if (!res || (int)irq <= 0 || !pdata) {
+ dev_err(&pdev->dev, "Not enough CSI2 platform resources.\n");
+ return -ENODEV;
+ }
+
+ /* TODO: Add support for CSI2I. Careful: different register layout! */
+ if (pdata->type != SH_CSI2C) {
+ dev_err(&pdev->dev, "Only CSI2C supported ATM.\n");
+ return -EINVAL;
+ }
+
+ priv = kzalloc(sizeof(struct sh_csi2), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->irq = irq;
+
+ if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
+ dev_err(&pdev->dev, "CSI2 register region already claimed\n");
+ ret = -EBUSY;
+ goto ereqreg;
+ }
+
+ priv->base = ioremap(res->start, resource_size(res));
+ if (!priv->base) {
+ ret = -ENXIO;
+ dev_err(&pdev->dev, "Unable to ioremap CSI2 registers.\n");
+ goto eremap;
+ }
+
+ priv->pdev = pdev;
+ platform_set_drvdata(pdev, priv);
+
+ v4l2_subdev_init(&priv->subdev, &sh_csi2_subdev_ops);
+ v4l2_set_subdevdata(&priv->subdev, &pdev->dev);
+
+ snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s.mipi-csi",
+ dev_name(pdata->v4l2_dev->dev));
+ ret = v4l2_device_register_subdev(pdata->v4l2_dev, &priv->subdev);
+ dev_dbg(&pdev->dev, "%s(%p): ret(register_subdev) = %d\n", __func__, priv, ret);
+ if (ret < 0)
+ goto esdreg;
+
+ pm_runtime_enable(&pdev->dev);
+
+ dev_dbg(&pdev->dev, "CSI2 probed.\n");
+
+ return 0;
+
+esdreg:
+ iounmap(priv->base);
+eremap:
+ release_mem_region(res->start, resource_size(res));
+ereqreg:
+ kfree(priv);
+
+ return ret;
+}
+
+static __devexit int sh_csi2_remove(struct platform_device *pdev)
+{
+ struct sh_csi2 *priv = platform_get_drvdata(pdev);
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ v4l2_device_unregister_subdev(&priv->subdev);
+ pm_runtime_disable(&pdev->dev);
+ iounmap(priv->base);
+ release_mem_region(res->start, resource_size(res));
+ platform_set_drvdata(pdev, NULL);
+ kfree(priv);
+
+ return 0;
+}
+
+static struct platform_driver __refdata sh_csi2_pdrv = {
+ .remove = __devexit_p(sh_csi2_remove),
+ .probe = sh_csi2_probe,
+ .driver = {
+ .name = "sh-mobile-csi2",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(sh_csi2_pdrv);
+
+MODULE_DESCRIPTION("SH-Mobile MIPI CSI-2 driver");
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:sh-mobile-csi2");
diff --git a/drivers/media/video/sh_vou.c b/drivers/media/video/sh_vou.c
new file mode 100644
index 00000000..9644bd86
--- /dev/null
+++ b/drivers/media/video/sh_vou.c
@@ -0,0 +1,1493 @@
+/*
+ * SuperH Video Output Unit (VOU) driver
+ *
+ * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/module.h>
+
+#include <media/sh_vou.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mediabus.h>
+#include <media/videobuf-dma-contig.h>
+
+/* Mirror addresses are not available for all registers */
+#define VOUER 0
+#define VOUCR 4
+#define VOUSTR 8
+#define VOUVCR 0xc
+#define VOUISR 0x10
+#define VOUBCR 0x14
+#define VOUDPR 0x18
+#define VOUDSR 0x1c
+#define VOUVPR 0x20
+#define VOUIR 0x24
+#define VOUSRR 0x28
+#define VOUMSR 0x2c
+#define VOUHIR 0x30
+#define VOUDFR 0x34
+#define VOUAD1R 0x38
+#define VOUAD2R 0x3c
+#define VOUAIR 0x40
+#define VOUSWR 0x44
+#define VOURCR 0x48
+#define VOURPR 0x50
+
+enum sh_vou_status {
+ SH_VOU_IDLE,
+ SH_VOU_INITIALISING,
+ SH_VOU_RUNNING,
+};
+
+#define VOU_MAX_IMAGE_WIDTH 720
+#define VOU_MAX_IMAGE_HEIGHT 576
+
+struct sh_vou_device {
+ struct v4l2_device v4l2_dev;
+ struct video_device *vdev;
+ atomic_t use_count;
+ struct sh_vou_pdata *pdata;
+ spinlock_t lock;
+ void __iomem *base;
+ /* State information */
+ struct v4l2_pix_format pix;
+ struct v4l2_rect rect;
+ struct list_head queue;
+ v4l2_std_id std;
+ int pix_idx;
+ struct videobuf_buffer *active;
+ enum sh_vou_status status;
+ struct mutex fop_lock;
+};
+
+struct sh_vou_file {
+ struct videobuf_queue vbq;
+};
+
+/* Register access routines for sides A, B and mirror addresses */
+static void sh_vou_reg_a_write(struct sh_vou_device *vou_dev, unsigned int reg,
+ u32 value)
+{
+ __raw_writel(value, vou_dev->base + reg);
+}
+
+static void sh_vou_reg_ab_write(struct sh_vou_device *vou_dev, unsigned int reg,
+ u32 value)
+{
+ __raw_writel(value, vou_dev->base + reg);
+ __raw_writel(value, vou_dev->base + reg + 0x1000);
+}
+
+static void sh_vou_reg_m_write(struct sh_vou_device *vou_dev, unsigned int reg,
+ u32 value)
+{
+ __raw_writel(value, vou_dev->base + reg + 0x2000);
+}
+
+static u32 sh_vou_reg_a_read(struct sh_vou_device *vou_dev, unsigned int reg)
+{
+ return __raw_readl(vou_dev->base + reg);
+}
+
+static void sh_vou_reg_a_set(struct sh_vou_device *vou_dev, unsigned int reg,
+ u32 value, u32 mask)
+{
+ u32 old = __raw_readl(vou_dev->base + reg);
+
+ value = (value & mask) | (old & ~mask);
+ __raw_writel(value, vou_dev->base + reg);
+}
+
+static void sh_vou_reg_b_set(struct sh_vou_device *vou_dev, unsigned int reg,
+ u32 value, u32 mask)
+{
+ sh_vou_reg_a_set(vou_dev, reg + 0x1000, value, mask);
+}
+
+static void sh_vou_reg_ab_set(struct sh_vou_device *vou_dev, unsigned int reg,
+ u32 value, u32 mask)
+{
+ sh_vou_reg_a_set(vou_dev, reg, value, mask);
+ sh_vou_reg_b_set(vou_dev, reg, value, mask);
+}
+
+struct sh_vou_fmt {
+ u32 pfmt;
+ char *desc;
+ unsigned char bpp;
+ unsigned char rgb;
+ unsigned char yf;
+ unsigned char pkf;
+};
+
+/* Further pixel formats can be added */
+static struct sh_vou_fmt vou_fmt[] = {
+ {
+ .pfmt = V4L2_PIX_FMT_NV12,
+ .bpp = 12,
+ .desc = "YVU420 planar",
+ .yf = 0,
+ .rgb = 0,
+ },
+ {
+ .pfmt = V4L2_PIX_FMT_NV16,
+ .bpp = 16,
+ .desc = "YVYU planar",
+ .yf = 1,
+ .rgb = 0,
+ },
+ {
+ .pfmt = V4L2_PIX_FMT_RGB24,
+ .bpp = 24,
+ .desc = "RGB24",
+ .pkf = 2,
+ .rgb = 1,
+ },
+ {
+ .pfmt = V4L2_PIX_FMT_RGB565,
+ .bpp = 16,
+ .desc = "RGB565",
+ .pkf = 3,
+ .rgb = 1,
+ },
+ {
+ .pfmt = V4L2_PIX_FMT_RGB565X,
+ .bpp = 16,
+ .desc = "RGB565 byteswapped",
+ .pkf = 3,
+ .rgb = 1,
+ },
+};
+
+static void sh_vou_schedule_next(struct sh_vou_device *vou_dev,
+ struct videobuf_buffer *vb)
+{
+ dma_addr_t addr1, addr2;
+
+ addr1 = videobuf_to_dma_contig(vb);
+ switch (vou_dev->pix.pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV16:
+ addr2 = addr1 + vou_dev->pix.width * vou_dev->pix.height;
+ break;
+ default:
+ addr2 = 0;
+ }
+
+ sh_vou_reg_m_write(vou_dev, VOUAD1R, addr1);
+ sh_vou_reg_m_write(vou_dev, VOUAD2R, addr2);
+}
+
+static void sh_vou_stream_start(struct sh_vou_device *vou_dev,
+ struct videobuf_buffer *vb)
+{
+ unsigned int row_coeff;
+#ifdef __LITTLE_ENDIAN
+ u32 dataswap = 7;
+#else
+ u32 dataswap = 0;
+#endif
+
+ switch (vou_dev->pix.pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV16:
+ row_coeff = 1;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ dataswap ^= 1;
+ case V4L2_PIX_FMT_RGB565X:
+ row_coeff = 2;
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ row_coeff = 3;
+ break;
+ }
+
+ sh_vou_reg_a_write(vou_dev, VOUSWR, dataswap);
+ sh_vou_reg_ab_write(vou_dev, VOUAIR, vou_dev->pix.width * row_coeff);
+ sh_vou_schedule_next(vou_dev, vb);
+}
+
+static void free_buffer(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ BUG_ON(in_interrupt());
+
+ /* Wait until this buffer is no longer in STATE_QUEUED or STATE_ACTIVE */
+ videobuf_waiton(vq, vb, 0, 0);
+ videobuf_dma_contig_free(vq, vb);
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+/* Locking: caller holds fop_lock mutex */
+static int sh_vou_buf_setup(struct videobuf_queue *vq, unsigned int *count,
+ unsigned int *size)
+{
+ struct video_device *vdev = vq->priv_data;
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+
+ *size = vou_fmt[vou_dev->pix_idx].bpp * vou_dev->pix.width *
+ vou_dev->pix.height / 8;
+
+ if (*count < 2)
+ *count = 2;
+
+ /* Taking into account maximum frame size, *count will stay >= 2 */
+ if (PAGE_ALIGN(*size) * *count > 4 * 1024 * 1024)
+ *count = 4 * 1024 * 1024 / PAGE_ALIGN(*size);
+
+ dev_dbg(vq->dev, "%s(): count=%d, size=%d\n", __func__, *count, *size);
+
+ return 0;
+}
+
+/* Locking: caller holds fop_lock mutex */
+static int sh_vou_buf_prepare(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct video_device *vdev = vq->priv_data;
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct v4l2_pix_format *pix = &vou_dev->pix;
+ int bytes_per_line = vou_fmt[vou_dev->pix_idx].bpp * pix->width / 8;
+ int ret;
+
+ dev_dbg(vq->dev, "%s()\n", __func__);
+
+ if (vb->width != pix->width ||
+ vb->height != pix->height ||
+ vb->field != pix->field) {
+ vb->width = pix->width;
+ vb->height = pix->height;
+ vb->field = field;
+ if (vb->state != VIDEOBUF_NEEDS_INIT)
+ free_buffer(vq, vb);
+ }
+
+ vb->size = vb->height * bytes_per_line;
+ if (vb->baddr && vb->bsize < vb->size) {
+ /* User buffer too small */
+ dev_warn(vq->dev, "User buffer too small: [%u] @ %lx\n",
+ vb->bsize, vb->baddr);
+ return -EINVAL;
+ }
+
+ if (vb->state == VIDEOBUF_NEEDS_INIT) {
+ ret = videobuf_iolock(vq, vb, NULL);
+ if (ret < 0) {
+ dev_warn(vq->dev, "IOLOCK buf-type %d: %d\n",
+ vb->memory, ret);
+ return ret;
+ }
+ vb->state = VIDEOBUF_PREPARED;
+ }
+
+ dev_dbg(vq->dev,
+ "%s(): fmt #%d, %u bytes per line, phys 0x%x, type %d, state %d\n",
+ __func__, vou_dev->pix_idx, bytes_per_line,
+ videobuf_to_dma_contig(vb), vb->memory, vb->state);
+
+ return 0;
+}
+
+/* Locking: caller holds fop_lock mutex and vq->irqlock spinlock */
+static void sh_vou_buf_queue(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct video_device *vdev = vq->priv_data;
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+
+ dev_dbg(vq->dev, "%s()\n", __func__);
+
+ vb->state = VIDEOBUF_QUEUED;
+ list_add_tail(&vb->queue, &vou_dev->queue);
+
+ if (vou_dev->status == SH_VOU_RUNNING) {
+ return;
+ } else if (!vou_dev->active) {
+ vou_dev->active = vb;
+ /* Start from side A: we use mirror addresses, so, set B */
+ sh_vou_reg_a_write(vou_dev, VOURPR, 1);
+ dev_dbg(vq->dev, "%s: first buffer status 0x%x\n", __func__,
+ sh_vou_reg_a_read(vou_dev, VOUSTR));
+ sh_vou_schedule_next(vou_dev, vb);
+ /* Only activate VOU after the second buffer */
+ } else if (vou_dev->active->queue.next == &vb->queue) {
+ /* Second buffer - initialise register side B */
+ sh_vou_reg_a_write(vou_dev, VOURPR, 0);
+ sh_vou_stream_start(vou_dev, vb);
+
+ /* Register side switching with frame VSYNC */
+ sh_vou_reg_a_write(vou_dev, VOURCR, 5);
+ dev_dbg(vq->dev, "%s: second buffer status 0x%x\n", __func__,
+ sh_vou_reg_a_read(vou_dev, VOUSTR));
+
+ /* Enable End-of-Frame (VSYNC) interrupts */
+ sh_vou_reg_a_write(vou_dev, VOUIR, 0x10004);
+ /* Two buffers on the queue - activate the hardware */
+
+ vou_dev->status = SH_VOU_RUNNING;
+ sh_vou_reg_a_write(vou_dev, VOUER, 0x107);
+ }
+}
+
+static void sh_vou_buf_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct video_device *vdev = vq->priv_data;
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ unsigned long flags;
+
+ dev_dbg(vq->dev, "%s()\n", __func__);
+
+ spin_lock_irqsave(&vou_dev->lock, flags);
+
+ if (vou_dev->active == vb) {
+ /* disable output */
+ sh_vou_reg_a_set(vou_dev, VOUER, 0, 1);
+ /* ...but the current frame will complete */
+ sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x30000);
+ vou_dev->active = NULL;
+ }
+
+ if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED)) {
+ vb->state = VIDEOBUF_ERROR;
+ list_del(&vb->queue);
+ }
+
+ spin_unlock_irqrestore(&vou_dev->lock, flags);
+
+ free_buffer(vq, vb);
+}
+
+static struct videobuf_queue_ops sh_vou_video_qops = {
+ .buf_setup = sh_vou_buf_setup,
+ .buf_prepare = sh_vou_buf_prepare,
+ .buf_queue = sh_vou_buf_queue,
+ .buf_release = sh_vou_buf_release,
+};
+
+/* Video IOCTLs */
+static int sh_vou_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct sh_vou_file *vou_file = priv;
+
+ dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+
+ strlcpy(cap->card, "SuperH VOU", sizeof(cap->card));
+ cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+ return 0;
+}
+
+/* Enumerate formats, that the device can accept from the user */
+static int sh_vou_enum_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_fmtdesc *fmt)
+{
+ struct sh_vou_file *vou_file = priv;
+
+ if (fmt->index >= ARRAY_SIZE(vou_fmt))
+ return -EINVAL;
+
+ dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+
+ fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ strlcpy(fmt->description, vou_fmt[fmt->index].desc,
+ sizeof(fmt->description));
+ fmt->pixelformat = vou_fmt[fmt->index].pfmt;
+
+ return 0;
+}
+
+static int sh_vou_g_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
+
+ fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ fmt->fmt.pix = vou_dev->pix;
+
+ return 0;
+}
+
+static const unsigned char vou_scale_h_num[] = {1, 9, 2, 9, 4};
+static const unsigned char vou_scale_h_den[] = {1, 8, 1, 4, 1};
+static const unsigned char vou_scale_h_fld[] = {0, 2, 1, 3};
+static const unsigned char vou_scale_v_num[] = {1, 2, 4};
+static const unsigned char vou_scale_v_den[] = {1, 1, 1};
+static const unsigned char vou_scale_v_fld[] = {0, 1};
+
+static void sh_vou_configure_geometry(struct sh_vou_device *vou_dev,
+ int pix_idx, int w_idx, int h_idx)
+{
+ struct sh_vou_fmt *fmt = vou_fmt + pix_idx;
+ unsigned int black_left, black_top, width_max, height_max,
+ frame_in_height, frame_out_height, frame_out_top;
+ struct v4l2_rect *rect = &vou_dev->rect;
+ struct v4l2_pix_format *pix = &vou_dev->pix;
+ u32 vouvcr = 0, dsr_h, dsr_v;
+
+ if (vou_dev->std & V4L2_STD_525_60) {
+ width_max = 858;
+ height_max = 262;
+ } else {
+ width_max = 864;
+ height_max = 312;
+ }
+
+ frame_in_height = pix->height / 2;
+ frame_out_height = rect->height / 2;
+ frame_out_top = rect->top / 2;
+
+ /*
+ * Cropping scheme: max useful image is 720x480, and the total video
+ * area is 858x525 (NTSC) or 864x625 (PAL). AK8813 / 8814 starts
+ * sampling data beginning with fixed 276th (NTSC) / 288th (PAL) clock,
+ * of which the first 33 / 25 clocks HSYNC must be held active. This
+ * has to be configured in CR[HW]. 1 pixel equals 2 clock periods.
+ * This gives CR[HW] = 16 / 12, VPR[HVP] = 138 / 144, which gives
+ * exactly 858 - 138 = 864 - 144 = 720! We call the out-of-display area,
+ * beyond DSR, specified on the left and top by the VPR register "black
+ * pixels" and out-of-image area (DPR) "background pixels." We fix VPR
+ * at 138 / 144 : 20, because that's the HSYNC timing, that our first
+ * client requires, and that's exactly what leaves us 720 pixels for the
+ * image; we leave VPR[VVP] at default 20 for now, because the client
+ * doesn't seem to have any special requirements for it. Otherwise we
+ * could also set it to max - 240 = 22 / 72. Thus VPR depends only on
+ * the selected standard, and DPR and DSR are selected according to
+ * cropping. Q: how does the client detect the first valid line? Does
+ * HSYNC stay inactive during invalid (black) lines?
+ */
+ black_left = width_max - VOU_MAX_IMAGE_WIDTH;
+ black_top = 20;
+
+ dsr_h = rect->width + rect->left;
+ dsr_v = frame_out_height + frame_out_top;
+
+ dev_dbg(vou_dev->v4l2_dev.dev,
+ "image %ux%u, black %u:%u, offset %u:%u, display %ux%u\n",
+ pix->width, frame_in_height, black_left, black_top,
+ rect->left, frame_out_top, dsr_h, dsr_v);
+
+ /* VOUISR height - half of a frame height in frame mode */
+ sh_vou_reg_ab_write(vou_dev, VOUISR, (pix->width << 16) | frame_in_height);
+ sh_vou_reg_ab_write(vou_dev, VOUVPR, (black_left << 16) | black_top);
+ sh_vou_reg_ab_write(vou_dev, VOUDPR, (rect->left << 16) | frame_out_top);
+ sh_vou_reg_ab_write(vou_dev, VOUDSR, (dsr_h << 16) | dsr_v);
+
+ /*
+ * if necessary, we could set VOUHIR to
+ * max(black_left + dsr_h, width_max) here
+ */
+
+ if (w_idx)
+ vouvcr |= (1 << 15) | (vou_scale_h_fld[w_idx - 1] << 4);
+ if (h_idx)
+ vouvcr |= (1 << 14) | vou_scale_v_fld[h_idx - 1];
+
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s: scaling 0x%x\n", fmt->desc, vouvcr);
+
+ /* To produce a colour bar for testing set bit 23 of VOUVCR */
+ sh_vou_reg_ab_write(vou_dev, VOUVCR, vouvcr);
+ sh_vou_reg_ab_write(vou_dev, VOUDFR,
+ fmt->pkf | (fmt->yf << 8) | (fmt->rgb << 16));
+}
+
+struct sh_vou_geometry {
+ struct v4l2_rect output;
+ unsigned int in_width;
+ unsigned int in_height;
+ int scale_idx_h;
+ int scale_idx_v;
+};
+
+/*
+ * Find input geometry, that we can use to produce output, closest to the
+ * requested rectangle, using VOU scaling
+ */
+static void vou_adjust_input(struct sh_vou_geometry *geo, v4l2_std_id std)
+{
+ /* The compiler cannot know, that best and idx will indeed be set */
+ unsigned int best_err = UINT_MAX, best = 0, img_height_max;
+ int i, idx = 0;
+
+ if (std & V4L2_STD_525_60)
+ img_height_max = 480;
+ else
+ img_height_max = 576;
+
+ /* Image width must be a multiple of 4 */
+ v4l_bound_align_image(&geo->in_width, 0, VOU_MAX_IMAGE_WIDTH, 2,
+ &geo->in_height, 0, img_height_max, 1, 0);
+
+ /* Select scales to come as close as possible to the output image */
+ for (i = ARRAY_SIZE(vou_scale_h_num) - 1; i >= 0; i--) {
+ unsigned int err;
+ unsigned int found = geo->output.width * vou_scale_h_den[i] /
+ vou_scale_h_num[i];
+
+ if (found > VOU_MAX_IMAGE_WIDTH)
+ /* scales increase */
+ break;
+
+ err = abs(found - geo->in_width);
+ if (err < best_err) {
+ best_err = err;
+ idx = i;
+ best = found;
+ }
+ if (!err)
+ break;
+ }
+
+ geo->in_width = best;
+ geo->scale_idx_h = idx;
+
+ best_err = UINT_MAX;
+
+ /* This loop can be replaced with one division */
+ for (i = ARRAY_SIZE(vou_scale_v_num) - 1; i >= 0; i--) {
+ unsigned int err;
+ unsigned int found = geo->output.height * vou_scale_v_den[i] /
+ vou_scale_v_num[i];
+
+ if (found > img_height_max)
+ /* scales increase */
+ break;
+
+ err = abs(found - geo->in_height);
+ if (err < best_err) {
+ best_err = err;
+ idx = i;
+ best = found;
+ }
+ if (!err)
+ break;
+ }
+
+ geo->in_height = best;
+ geo->scale_idx_v = idx;
+}
+
+/*
+ * Find output geometry, that we can produce, using VOU scaling, closest to
+ * the requested rectangle
+ */
+static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std)
+{
+ unsigned int best_err = UINT_MAX, best, width_max, height_max,
+ img_height_max;
+ int i, idx;
+
+ if (std & V4L2_STD_525_60) {
+ width_max = 858;
+ height_max = 262 * 2;
+ img_height_max = 480;
+ } else {
+ width_max = 864;
+ height_max = 312 * 2;
+ img_height_max = 576;
+ }
+
+ /* Select scales to come as close as possible to the output image */
+ for (i = 0; i < ARRAY_SIZE(vou_scale_h_num); i++) {
+ unsigned int err;
+ unsigned int found = geo->in_width * vou_scale_h_num[i] /
+ vou_scale_h_den[i];
+
+ if (found > VOU_MAX_IMAGE_WIDTH)
+ /* scales increase */
+ break;
+
+ err = abs(found - geo->output.width);
+ if (err < best_err) {
+ best_err = err;
+ idx = i;
+ best = found;
+ }
+ if (!err)
+ break;
+ }
+
+ geo->output.width = best;
+ geo->scale_idx_h = idx;
+ if (geo->output.left + best > width_max)
+ geo->output.left = width_max - best;
+
+ pr_debug("%s(): W %u * %u/%u = %u\n", __func__, geo->in_width,
+ vou_scale_h_num[idx], vou_scale_h_den[idx], best);
+
+ best_err = UINT_MAX;
+
+ /* This loop can be replaced with one division */
+ for (i = 0; i < ARRAY_SIZE(vou_scale_v_num); i++) {
+ unsigned int err;
+ unsigned int found = geo->in_height * vou_scale_v_num[i] /
+ vou_scale_v_den[i];
+
+ if (found > img_height_max)
+ /* scales increase */
+ break;
+
+ err = abs(found - geo->output.height);
+ if (err < best_err) {
+ best_err = err;
+ idx = i;
+ best = found;
+ }
+ if (!err)
+ break;
+ }
+
+ geo->output.height = best;
+ geo->scale_idx_v = idx;
+ if (geo->output.top + best > height_max)
+ geo->output.top = height_max - best;
+
+ pr_debug("%s(): H %u * %u/%u = %u\n", __func__, geo->in_height,
+ vou_scale_v_num[idx], vou_scale_v_den[idx], best);
+}
+
+static int sh_vou_s_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct v4l2_pix_format *pix = &fmt->fmt.pix;
+ unsigned int img_height_max;
+ int pix_idx;
+ struct sh_vou_geometry geo;
+ struct v4l2_mbus_framefmt mbfmt = {
+ /* Revisit: is this the correct code? */
+ .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .field = V4L2_FIELD_INTERLACED,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ };
+ int ret;
+
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u -> %ux%u\n", __func__,
+ vou_dev->rect.width, vou_dev->rect.height,
+ pix->width, pix->height);
+
+ if (pix->field == V4L2_FIELD_ANY)
+ pix->field = V4L2_FIELD_NONE;
+
+ if (fmt->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
+ pix->field != V4L2_FIELD_NONE)
+ return -EINVAL;
+
+ for (pix_idx = 0; pix_idx < ARRAY_SIZE(vou_fmt); pix_idx++)
+ if (vou_fmt[pix_idx].pfmt == pix->pixelformat)
+ break;
+
+ if (pix_idx == ARRAY_SIZE(vou_fmt))
+ return -EINVAL;
+
+ if (vou_dev->std & V4L2_STD_525_60)
+ img_height_max = 480;
+ else
+ img_height_max = 576;
+
+ /* Image width must be a multiple of 4 */
+ v4l_bound_align_image(&pix->width, 0, VOU_MAX_IMAGE_WIDTH, 2,
+ &pix->height, 0, img_height_max, 1, 0);
+
+ geo.in_width = pix->width;
+ geo.in_height = pix->height;
+ geo.output = vou_dev->rect;
+
+ vou_adjust_output(&geo, vou_dev->std);
+
+ mbfmt.width = geo.output.width;
+ mbfmt.height = geo.output.height;
+ ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video,
+ s_mbus_fmt, &mbfmt);
+ /* Must be implemented, so, don't check for -ENOIOCTLCMD */
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u -> %ux%u\n", __func__,
+ geo.output.width, geo.output.height, mbfmt.width, mbfmt.height);
+
+ /* Sanity checks */
+ if ((unsigned)mbfmt.width > VOU_MAX_IMAGE_WIDTH ||
+ (unsigned)mbfmt.height > img_height_max ||
+ mbfmt.code != V4L2_MBUS_FMT_YUYV8_2X8)
+ return -EIO;
+
+ if (mbfmt.width != geo.output.width ||
+ mbfmt.height != geo.output.height) {
+ geo.output.width = mbfmt.width;
+ geo.output.height = mbfmt.height;
+
+ vou_adjust_input(&geo, vou_dev->std);
+ }
+
+ /* We tried to preserve output rectangle, but it could have changed */
+ vou_dev->rect = geo.output;
+ pix->width = geo.in_width;
+ pix->height = geo.in_height;
+
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u\n", __func__,
+ pix->width, pix->height);
+
+ vou_dev->pix_idx = pix_idx;
+
+ vou_dev->pix = *pix;
+
+ sh_vou_configure_geometry(vou_dev, pix_idx,
+ geo.scale_idx_h, geo.scale_idx_v);
+
+ return 0;
+}
+
+static int sh_vou_try_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct sh_vou_file *vou_file = priv;
+ struct v4l2_pix_format *pix = &fmt->fmt.pix;
+ int i;
+
+ dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+
+ fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ pix->field = V4L2_FIELD_NONE;
+
+ v4l_bound_align_image(&pix->width, 0, VOU_MAX_IMAGE_WIDTH, 1,
+ &pix->height, 0, VOU_MAX_IMAGE_HEIGHT, 1, 0);
+
+ for (i = 0; ARRAY_SIZE(vou_fmt); i++)
+ if (vou_fmt[i].pfmt == pix->pixelformat)
+ return 0;
+
+ pix->pixelformat = vou_fmt[0].pfmt;
+
+ return 0;
+}
+
+static int sh_vou_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *req)
+{
+ struct sh_vou_file *vou_file = priv;
+
+ dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+
+ if (req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+
+ return videobuf_reqbufs(&vou_file->vbq, req);
+}
+
+static int sh_vou_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct sh_vou_file *vou_file = priv;
+
+ dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+
+ return videobuf_querybuf(&vou_file->vbq, b);
+}
+
+static int sh_vou_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct sh_vou_file *vou_file = priv;
+
+ dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+
+ return videobuf_qbuf(&vou_file->vbq, b);
+}
+
+static int sh_vou_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct sh_vou_file *vou_file = priv;
+
+ dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+
+ return videobuf_dqbuf(&vou_file->vbq, b, file->f_flags & O_NONBLOCK);
+}
+
+static int sh_vou_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type buftype)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct sh_vou_file *vou_file = priv;
+ int ret;
+
+ dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+
+ ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0,
+ video, s_stream, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+
+ /* This calls our .buf_queue() (== sh_vou_buf_queue) */
+ return videobuf_streamon(&vou_file->vbq);
+}
+
+static int sh_vou_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type buftype)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct sh_vou_file *vou_file = priv;
+
+ dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+
+ /*
+ * This calls buf_release from host driver's videobuf_queue_ops for all
+ * remaining buffers. When the last buffer is freed, stop streaming
+ */
+ videobuf_streamoff(&vou_file->vbq);
+ v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, s_stream, 0);
+
+ return 0;
+}
+
+static u32 sh_vou_ntsc_mode(enum sh_vou_bus_fmt bus_fmt)
+{
+ switch (bus_fmt) {
+ default:
+ pr_warning("%s(): Invalid bus-format code %d, using default 8-bit\n",
+ __func__, bus_fmt);
+ case SH_VOU_BUS_8BIT:
+ return 1;
+ case SH_VOU_BUS_16BIT:
+ return 0;
+ case SH_VOU_BUS_BT656:
+ return 3;
+ }
+}
+
+static int sh_vou_s_std(struct file *file, void *priv, v4l2_std_id *std_id)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ int ret;
+
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s(): 0x%llx\n", __func__, *std_id);
+
+ if (*std_id & ~vdev->tvnorms)
+ return -EINVAL;
+
+ ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video,
+ s_std_output, *std_id);
+ /* Shall we continue, if the subdev doesn't support .s_std_output()? */
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+
+ if (*std_id & V4L2_STD_525_60)
+ sh_vou_reg_ab_set(vou_dev, VOUCR,
+ sh_vou_ntsc_mode(vou_dev->pdata->bus_fmt) << 29, 7 << 29);
+ else
+ sh_vou_reg_ab_set(vou_dev, VOUCR, 5 << 29, 7 << 29);
+
+ vou_dev->std = *std_id;
+
+ return 0;
+}
+
+static int sh_vou_g_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
+
+ *std = vou_dev->std;
+
+ return 0;
+}
+
+static int sh_vou_g_crop(struct file *file, void *fh, struct v4l2_crop *a)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
+
+ a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ a->c = vou_dev->rect;
+
+ return 0;
+}
+
+/* Assume a dull encoder, do all the work ourselves. */
+static int sh_vou_s_crop(struct file *file, void *fh, struct v4l2_crop *a)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct v4l2_rect *rect = &a->c;
+ struct v4l2_crop sd_crop = {.type = V4L2_BUF_TYPE_VIDEO_OUTPUT};
+ struct v4l2_pix_format *pix = &vou_dev->pix;
+ struct sh_vou_geometry geo;
+ struct v4l2_mbus_framefmt mbfmt = {
+ /* Revisit: is this the correct code? */
+ .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .field = V4L2_FIELD_INTERLACED,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ };
+ unsigned int img_height_max;
+ int ret;
+
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u@%u:%u\n", __func__,
+ rect->width, rect->height, rect->left, rect->top);
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+
+ if (vou_dev->std & V4L2_STD_525_60)
+ img_height_max = 480;
+ else
+ img_height_max = 576;
+
+ v4l_bound_align_image(&rect->width, 0, VOU_MAX_IMAGE_WIDTH, 1,
+ &rect->height, 0, img_height_max, 1, 0);
+
+ if (rect->width + rect->left > VOU_MAX_IMAGE_WIDTH)
+ rect->left = VOU_MAX_IMAGE_WIDTH - rect->width;
+
+ if (rect->height + rect->top > img_height_max)
+ rect->top = img_height_max - rect->height;
+
+ geo.output = *rect;
+ geo.in_width = pix->width;
+ geo.in_height = pix->height;
+
+ /* Configure the encoder one-to-one, position at 0, ignore errors */
+ sd_crop.c.width = geo.output.width;
+ sd_crop.c.height = geo.output.height;
+ /*
+ * We first issue a S_CROP, so that the subsequent S_FMT delivers the
+ * final encoder configuration.
+ */
+ v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video,
+ s_crop, &sd_crop);
+ mbfmt.width = geo.output.width;
+ mbfmt.height = geo.output.height;
+ ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video,
+ s_mbus_fmt, &mbfmt);
+ /* Must be implemented, so, don't check for -ENOIOCTLCMD */
+ if (ret < 0)
+ return ret;
+
+ /* Sanity checks */
+ if ((unsigned)mbfmt.width > VOU_MAX_IMAGE_WIDTH ||
+ (unsigned)mbfmt.height > img_height_max ||
+ mbfmt.code != V4L2_MBUS_FMT_YUYV8_2X8)
+ return -EIO;
+
+ geo.output.width = mbfmt.width;
+ geo.output.height = mbfmt.height;
+
+ /*
+ * No down-scaling. According to the API, current call has precedence:
+ * http://v4l2spec.bytesex.org/spec/x1904.htm#AEN1954 paragraph two.
+ */
+ vou_adjust_input(&geo, vou_dev->std);
+
+ /* We tried to preserve output rectangle, but it could have changed */
+ vou_dev->rect = geo.output;
+ pix->width = geo.in_width;
+ pix->height = geo.in_height;
+
+ sh_vou_configure_geometry(vou_dev, vou_dev->pix_idx,
+ geo.scale_idx_h, geo.scale_idx_v);
+
+ return 0;
+}
+
+/*
+ * Total field: NTSC 858 x 2 * 262/263, PAL 864 x 2 * 312/313, default rectangle
+ * is the initial register values, height takes the interlaced format into
+ * account. The actual image can only go up to 720 x 2 * 240, So, VOUVPR can
+ * actually only meaningfully contain values <= 720 and <= 240 respectively, and
+ * not <= 864 and <= 312.
+ */
+static int sh_vou_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *a)
+{
+ struct sh_vou_file *vou_file = priv;
+
+ dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+
+ a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ a->bounds.width = VOU_MAX_IMAGE_WIDTH;
+ a->bounds.height = VOU_MAX_IMAGE_HEIGHT;
+ /* Default = max, set VOUDPR = 0, which is not hardware default */
+ a->defrect.left = 0;
+ a->defrect.top = 0;
+ a->defrect.width = VOU_MAX_IMAGE_WIDTH;
+ a->defrect.height = VOU_MAX_IMAGE_HEIGHT;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static irqreturn_t sh_vou_isr(int irq, void *dev_id)
+{
+ struct sh_vou_device *vou_dev = dev_id;
+ static unsigned long j;
+ struct videobuf_buffer *vb;
+ static int cnt;
+ static int side;
+ u32 irq_status = sh_vou_reg_a_read(vou_dev, VOUIR), masked;
+ u32 vou_status = sh_vou_reg_a_read(vou_dev, VOUSTR);
+
+ if (!(irq_status & 0x300)) {
+ if (printk_timed_ratelimit(&j, 500))
+ dev_warn(vou_dev->v4l2_dev.dev, "IRQ status 0x%x!\n",
+ irq_status);
+ return IRQ_NONE;
+ }
+
+ spin_lock(&vou_dev->lock);
+ if (!vou_dev->active || list_empty(&vou_dev->queue)) {
+ if (printk_timed_ratelimit(&j, 500))
+ dev_warn(vou_dev->v4l2_dev.dev,
+ "IRQ without active buffer: %x!\n", irq_status);
+ /* Just ack: buf_release will disable further interrupts */
+ sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x300);
+ spin_unlock(&vou_dev->lock);
+ return IRQ_HANDLED;
+ }
+
+ masked = ~(0x300 & irq_status) & irq_status & 0x30304;
+ dev_dbg(vou_dev->v4l2_dev.dev,
+ "IRQ status 0x%x -> 0x%x, VOU status 0x%x, cnt %d\n",
+ irq_status, masked, vou_status, cnt);
+
+ cnt++;
+ side = vou_status & 0x10000;
+
+ /* Clear only set interrupts */
+ sh_vou_reg_a_write(vou_dev, VOUIR, masked);
+
+ vb = vou_dev->active;
+ list_del(&vb->queue);
+
+ vb->state = VIDEOBUF_DONE;
+ do_gettimeofday(&vb->ts);
+ vb->field_count++;
+ wake_up(&vb->done);
+
+ if (list_empty(&vou_dev->queue)) {
+ /* Stop VOU */
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s: queue empty after %d\n",
+ __func__, cnt);
+ sh_vou_reg_a_set(vou_dev, VOUER, 0, 1);
+ vou_dev->active = NULL;
+ vou_dev->status = SH_VOU_INITIALISING;
+ /* Disable End-of-Frame (VSYNC) interrupts */
+ sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x30000);
+ spin_unlock(&vou_dev->lock);
+ return IRQ_HANDLED;
+ }
+
+ vou_dev->active = list_entry(vou_dev->queue.next,
+ struct videobuf_buffer, queue);
+
+ if (vou_dev->active->queue.next != &vou_dev->queue) {
+ struct videobuf_buffer *new = list_entry(vou_dev->active->queue.next,
+ struct videobuf_buffer, queue);
+ sh_vou_schedule_next(vou_dev, new);
+ }
+
+ spin_unlock(&vou_dev->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int sh_vou_hw_init(struct sh_vou_device *vou_dev)
+{
+ struct sh_vou_pdata *pdata = vou_dev->pdata;
+ u32 voucr = sh_vou_ntsc_mode(pdata->bus_fmt) << 29;
+ int i = 100;
+
+ /* Disable all IRQs */
+ sh_vou_reg_a_write(vou_dev, VOUIR, 0);
+
+ /* Reset VOU interfaces - registers unaffected */
+ sh_vou_reg_a_write(vou_dev, VOUSRR, 0x101);
+ while (--i && (sh_vou_reg_a_read(vou_dev, VOUSRR) & 0x101))
+ udelay(1);
+
+ if (!i)
+ return -ETIMEDOUT;
+
+ dev_dbg(vou_dev->v4l2_dev.dev, "Reset took %dus\n", 100 - i);
+
+ if (pdata->flags & SH_VOU_PCLK_FALLING)
+ voucr |= 1 << 28;
+ if (pdata->flags & SH_VOU_HSYNC_LOW)
+ voucr |= 1 << 27;
+ if (pdata->flags & SH_VOU_VSYNC_LOW)
+ voucr |= 1 << 26;
+ sh_vou_reg_ab_set(vou_dev, VOUCR, voucr, 0xfc000000);
+
+ /* Manual register side switching at first */
+ sh_vou_reg_a_write(vou_dev, VOURCR, 4);
+ /* Default - fixed HSYNC length, can be made configurable is required */
+ sh_vou_reg_ab_write(vou_dev, VOUMSR, 0x800000);
+
+ return 0;
+}
+
+/* File operations */
+static int sh_vou_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct sh_vou_file *vou_file = kzalloc(sizeof(struct sh_vou_file),
+ GFP_KERNEL);
+
+ if (!vou_file)
+ return -ENOMEM;
+
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
+
+ file->private_data = vou_file;
+
+ if (atomic_inc_return(&vou_dev->use_count) == 1) {
+ int ret;
+ /* First open */
+ vou_dev->status = SH_VOU_INITIALISING;
+ pm_runtime_get_sync(vdev->v4l2_dev->dev);
+ ret = sh_vou_hw_init(vou_dev);
+ if (ret < 0) {
+ atomic_dec(&vou_dev->use_count);
+ pm_runtime_put(vdev->v4l2_dev->dev);
+ vou_dev->status = SH_VOU_IDLE;
+ return ret;
+ }
+ }
+
+ videobuf_queue_dma_contig_init(&vou_file->vbq, &sh_vou_video_qops,
+ vou_dev->v4l2_dev.dev, &vou_dev->lock,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT,
+ V4L2_FIELD_NONE,
+ sizeof(struct videobuf_buffer), vdev,
+ &vou_dev->fop_lock);
+
+ return 0;
+}
+
+static int sh_vou_release(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct sh_vou_file *vou_file = file->private_data;
+
+ dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+
+ if (!atomic_dec_return(&vou_dev->use_count)) {
+ /* Last close */
+ vou_dev->status = SH_VOU_IDLE;
+ sh_vou_reg_a_set(vou_dev, VOUER, 0, 0x101);
+ pm_runtime_put(vdev->v4l2_dev->dev);
+ }
+
+ file->private_data = NULL;
+ kfree(vou_file);
+
+ return 0;
+}
+
+static int sh_vou_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct sh_vou_file *vou_file = file->private_data;
+
+ dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+
+ return videobuf_mmap_mapper(&vou_file->vbq, vma);
+}
+
+static unsigned int sh_vou_poll(struct file *file, poll_table *wait)
+{
+ struct sh_vou_file *vou_file = file->private_data;
+
+ dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+
+ return videobuf_poll_stream(file, &vou_file->vbq, wait);
+}
+
+static int sh_vou_g_chip_ident(struct file *file, void *fh,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+
+ return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, g_chip_ident, id);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int sh_vou_g_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+
+ return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, g_register, reg);
+}
+
+static int sh_vou_s_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+
+ return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, s_register, reg);
+}
+#endif
+
+/* sh_vou display ioctl operations */
+static const struct v4l2_ioctl_ops sh_vou_ioctl_ops = {
+ .vidioc_querycap = sh_vou_querycap,
+ .vidioc_enum_fmt_vid_out = sh_vou_enum_fmt_vid_out,
+ .vidioc_g_fmt_vid_out = sh_vou_g_fmt_vid_out,
+ .vidioc_s_fmt_vid_out = sh_vou_s_fmt_vid_out,
+ .vidioc_try_fmt_vid_out = sh_vou_try_fmt_vid_out,
+ .vidioc_reqbufs = sh_vou_reqbufs,
+ .vidioc_querybuf = sh_vou_querybuf,
+ .vidioc_qbuf = sh_vou_qbuf,
+ .vidioc_dqbuf = sh_vou_dqbuf,
+ .vidioc_streamon = sh_vou_streamon,
+ .vidioc_streamoff = sh_vou_streamoff,
+ .vidioc_s_std = sh_vou_s_std,
+ .vidioc_g_std = sh_vou_g_std,
+ .vidioc_cropcap = sh_vou_cropcap,
+ .vidioc_g_crop = sh_vou_g_crop,
+ .vidioc_s_crop = sh_vou_s_crop,
+ .vidioc_g_chip_ident = sh_vou_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = sh_vou_g_register,
+ .vidioc_s_register = sh_vou_s_register,
+#endif
+};
+
+static const struct v4l2_file_operations sh_vou_fops = {
+ .owner = THIS_MODULE,
+ .open = sh_vou_open,
+ .release = sh_vou_release,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = sh_vou_mmap,
+ .poll = sh_vou_poll,
+};
+
+static const struct video_device sh_vou_video_template = {
+ .name = "sh_vou",
+ .fops = &sh_vou_fops,
+ .ioctl_ops = &sh_vou_ioctl_ops,
+ .tvnorms = V4L2_STD_525_60, /* PAL only supported in 8-bit non-bt656 mode */
+ .current_norm = V4L2_STD_NTSC_M,
+};
+
+static int __devinit sh_vou_probe(struct platform_device *pdev)
+{
+ struct sh_vou_pdata *vou_pdata = pdev->dev.platform_data;
+ struct v4l2_rect *rect;
+ struct v4l2_pix_format *pix;
+ struct i2c_adapter *i2c_adap;
+ struct video_device *vdev;
+ struct sh_vou_device *vou_dev;
+ struct resource *reg_res, *region;
+ struct v4l2_subdev *subdev;
+ int irq, ret;
+
+ reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+
+ if (!vou_pdata || !reg_res || irq <= 0) {
+ dev_err(&pdev->dev, "Insufficient VOU platform information.\n");
+ return -ENODEV;
+ }
+
+ vou_dev = kzalloc(sizeof(*vou_dev), GFP_KERNEL);
+ if (!vou_dev)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&vou_dev->queue);
+ spin_lock_init(&vou_dev->lock);
+ mutex_init(&vou_dev->fop_lock);
+ atomic_set(&vou_dev->use_count, 0);
+ vou_dev->pdata = vou_pdata;
+ vou_dev->status = SH_VOU_IDLE;
+
+ rect = &vou_dev->rect;
+ pix = &vou_dev->pix;
+
+ /* Fill in defaults */
+ vou_dev->std = sh_vou_video_template.current_norm;
+ rect->left = 0;
+ rect->top = 0;
+ rect->width = VOU_MAX_IMAGE_WIDTH;
+ rect->height = 480;
+ pix->width = VOU_MAX_IMAGE_WIDTH;
+ pix->height = 480;
+ pix->pixelformat = V4L2_PIX_FMT_YVYU;
+ pix->field = V4L2_FIELD_NONE;
+ pix->bytesperline = VOU_MAX_IMAGE_WIDTH * 2;
+ pix->sizeimage = VOU_MAX_IMAGE_WIDTH * 2 * 480;
+ pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+ region = request_mem_region(reg_res->start, resource_size(reg_res),
+ pdev->name);
+ if (!region) {
+ dev_err(&pdev->dev, "VOU region already claimed\n");
+ ret = -EBUSY;
+ goto ereqmemreg;
+ }
+
+ vou_dev->base = ioremap(reg_res->start, resource_size(reg_res));
+ if (!vou_dev->base) {
+ ret = -ENOMEM;
+ goto emap;
+ }
+
+ ret = request_irq(irq, sh_vou_isr, 0, "vou", vou_dev);
+ if (ret < 0)
+ goto ereqirq;
+
+ ret = v4l2_device_register(&pdev->dev, &vou_dev->v4l2_dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Error registering v4l2 device\n");
+ goto ev4l2devreg;
+ }
+
+ /* Allocate memory for video device */
+ vdev = video_device_alloc();
+ if (vdev == NULL) {
+ ret = -ENOMEM;
+ goto evdevalloc;
+ }
+
+ *vdev = sh_vou_video_template;
+ if (vou_pdata->bus_fmt == SH_VOU_BUS_8BIT)
+ vdev->tvnorms |= V4L2_STD_PAL;
+ vdev->v4l2_dev = &vou_dev->v4l2_dev;
+ vdev->release = video_device_release;
+ vdev->lock = &vou_dev->fop_lock;
+
+ vou_dev->vdev = vdev;
+ video_set_drvdata(vdev, vou_dev);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_resume(&pdev->dev);
+
+ i2c_adap = i2c_get_adapter(vou_pdata->i2c_adap);
+ if (!i2c_adap) {
+ ret = -ENODEV;
+ goto ei2cgadap;
+ }
+
+ ret = sh_vou_hw_init(vou_dev);
+ if (ret < 0)
+ goto ereset;
+
+ subdev = v4l2_i2c_new_subdev_board(&vou_dev->v4l2_dev, i2c_adap,
+ vou_pdata->board_info, NULL);
+ if (!subdev) {
+ ret = -ENOMEM;
+ goto ei2cnd;
+ }
+
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ if (ret < 0)
+ goto evregdev;
+
+ return 0;
+
+evregdev:
+ei2cnd:
+ereset:
+ i2c_put_adapter(i2c_adap);
+ei2cgadap:
+ video_device_release(vdev);
+ pm_runtime_disable(&pdev->dev);
+evdevalloc:
+ v4l2_device_unregister(&vou_dev->v4l2_dev);
+ev4l2devreg:
+ free_irq(irq, vou_dev);
+ereqirq:
+ iounmap(vou_dev->base);
+emap:
+ release_mem_region(reg_res->start, resource_size(reg_res));
+ereqmemreg:
+ kfree(vou_dev);
+ return ret;
+}
+
+static int __devexit sh_vou_remove(struct platform_device *pdev)
+{
+ int irq = platform_get_irq(pdev, 0);
+ struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
+ struct sh_vou_device *vou_dev = container_of(v4l2_dev,
+ struct sh_vou_device, v4l2_dev);
+ struct v4l2_subdev *sd = list_entry(v4l2_dev->subdevs.next,
+ struct v4l2_subdev, list);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct resource *reg_res;
+
+ if (irq > 0)
+ free_irq(irq, vou_dev);
+ pm_runtime_disable(&pdev->dev);
+ video_unregister_device(vou_dev->vdev);
+ i2c_put_adapter(client->adapter);
+ v4l2_device_unregister(&vou_dev->v4l2_dev);
+ iounmap(vou_dev->base);
+ reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (reg_res)
+ release_mem_region(reg_res->start, resource_size(reg_res));
+ kfree(vou_dev);
+ return 0;
+}
+
+static struct platform_driver __refdata sh_vou = {
+ .remove = __devexit_p(sh_vou_remove),
+ .driver = {
+ .name = "sh-vou",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init sh_vou_init(void)
+{
+ return platform_driver_probe(&sh_vou, sh_vou_probe);
+}
+
+static void __exit sh_vou_exit(void)
+{
+ platform_driver_unregister(&sh_vou);
+}
+
+module_init(sh_vou_init);
+module_exit(sh_vou_exit);
+
+MODULE_DESCRIPTION("SuperH VOU driver");
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1.0");
+MODULE_ALIAS("platform:sh-vou");
diff --git a/drivers/media/video/sn9c102/Kconfig b/drivers/media/video/sn9c102/Kconfig
new file mode 100644
index 00000000..6ebaf294
--- /dev/null
+++ b/drivers/media/video/sn9c102/Kconfig
@@ -0,0 +1,14 @@
+config USB_SN9C102
+ tristate "USB SN9C1xx PC Camera Controller support (DEPRECATED)"
+ depends on VIDEO_V4L2
+ ---help---
+ This driver is DEPRECATED please use the gspca sonixb and
+ sonixj modules instead.
+
+ Say Y here if you want support for cameras based on SONiX SN9C101,
+ SN9C102, SN9C103, SN9C105 and SN9C120 PC Camera Controllers.
+
+ See <file:Documentation/video4linux/sn9c102.txt> for more info.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sn9c102.
diff --git a/drivers/media/video/sn9c102/Makefile b/drivers/media/video/sn9c102/Makefile
new file mode 100644
index 00000000..7ecd5a90
--- /dev/null
+++ b/drivers/media/video/sn9c102/Makefile
@@ -0,0 +1,15 @@
+sn9c102-objs := sn9c102_core.o \
+ sn9c102_hv7131d.o \
+ sn9c102_hv7131r.o \
+ sn9c102_mi0343.o \
+ sn9c102_mi0360.o \
+ sn9c102_mt9v111.o \
+ sn9c102_ov7630.o \
+ sn9c102_ov7660.o \
+ sn9c102_pas106b.o \
+ sn9c102_pas202bcb.o \
+ sn9c102_tas5110c1b.o \
+ sn9c102_tas5110d.o \
+ sn9c102_tas5130d1b.o
+
+obj-$(CONFIG_USB_SN9C102) += sn9c102.o
diff --git a/drivers/media/video/sn9c102/sn9c102.h b/drivers/media/video/sn9c102/sn9c102.h
new file mode 100644
index 00000000..22ea211a
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102.h
@@ -0,0 +1,211 @@
+/***************************************************************************
+ * V4L2 driver for SN9C1xx PC Camera Controllers *
+ * *
+ * Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#ifndef _SN9C102_H_
+#define _SN9C102_H_
+
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/types.h>
+#include <linux/param.h>
+#include <linux/rwsem.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <linux/stddef.h>
+#include <linux/kref.h>
+
+#include "sn9c102_config.h"
+#include "sn9c102_sensor.h"
+#include "sn9c102_devtable.h"
+
+
+enum sn9c102_frame_state {
+ F_UNUSED,
+ F_QUEUED,
+ F_GRABBING,
+ F_DONE,
+ F_ERROR,
+};
+
+struct sn9c102_frame_t {
+ void* bufmem;
+ struct v4l2_buffer buf;
+ enum sn9c102_frame_state state;
+ struct list_head frame;
+ unsigned long vma_use_count;
+};
+
+enum sn9c102_dev_state {
+ DEV_INITIALIZED = 0x01,
+ DEV_DISCONNECTED = 0x02,
+ DEV_MISCONFIGURED = 0x04,
+};
+
+enum sn9c102_io_method {
+ IO_NONE,
+ IO_READ,
+ IO_MMAP,
+};
+
+enum sn9c102_stream_state {
+ STREAM_OFF,
+ STREAM_INTERRUPT,
+ STREAM_ON,
+};
+
+typedef char sn9c102_sof_header_t[62];
+
+struct sn9c102_sof_t {
+ sn9c102_sof_header_t header;
+ u16 bytesread;
+};
+
+struct sn9c102_sysfs_attr {
+ u16 reg, i2c_reg;
+ sn9c102_sof_header_t frame_header;
+};
+
+struct sn9c102_module_param {
+ u8 force_munmap;
+ u16 frame_timeout;
+};
+
+static DEFINE_MUTEX(sn9c102_sysfs_lock);
+static DECLARE_RWSEM(sn9c102_dev_lock);
+
+struct sn9c102_device {
+ struct video_device* v4ldev;
+
+ enum sn9c102_bridge bridge;
+ struct sn9c102_sensor sensor;
+
+ struct usb_device* usbdev;
+ struct urb* urb[SN9C102_URBS];
+ void* transfer_buffer[SN9C102_URBS];
+ u8* control_buffer;
+
+ struct sn9c102_frame_t *frame_current, frame[SN9C102_MAX_FRAMES];
+ struct list_head inqueue, outqueue;
+ u32 frame_count, nbuffers, nreadbuffers;
+
+ enum sn9c102_io_method io;
+ enum sn9c102_stream_state stream;
+
+ struct v4l2_jpegcompression compression;
+
+ struct sn9c102_sysfs_attr sysfs;
+ struct sn9c102_sof_t sof;
+ u16 reg[384];
+
+ struct sn9c102_module_param module_param;
+
+ struct kref kref;
+ enum sn9c102_dev_state state;
+ u8 users;
+
+ struct completion probe;
+ struct mutex open_mutex, fileop_mutex;
+ spinlock_t queue_lock;
+ wait_queue_head_t wait_open, wait_frame, wait_stream;
+};
+
+/*****************************************************************************/
+
+struct sn9c102_device*
+sn9c102_match_id(struct sn9c102_device* cam, const struct usb_device_id *id)
+{
+ return usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id) ? cam : NULL;
+}
+
+
+void
+sn9c102_attach_sensor(struct sn9c102_device* cam,
+ const struct sn9c102_sensor* sensor)
+{
+ memcpy(&cam->sensor, sensor, sizeof(struct sn9c102_sensor));
+}
+
+
+enum sn9c102_bridge
+sn9c102_get_bridge(struct sn9c102_device* cam)
+{
+ return cam->bridge;
+}
+
+
+struct sn9c102_sensor* sn9c102_get_sensor(struct sn9c102_device* cam)
+{
+ return &cam->sensor;
+}
+
+/*****************************************************************************/
+
+#undef DBG
+#undef KDBG
+#ifdef SN9C102_DEBUG
+# define DBG(level, fmt, args...) \
+do { \
+ if (debug >= (level)) { \
+ if ((level) == 1) \
+ dev_err(&cam->usbdev->dev, fmt "\n", ## args); \
+ else if ((level) == 2) \
+ dev_info(&cam->usbdev->dev, fmt "\n", ## args); \
+ else if ((level) >= 3) \
+ dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \
+ __func__, __LINE__ , ## args); \
+ } \
+} while (0)
+# define V4LDBG(level, name, cmd) \
+do { \
+ if (debug >= (level)) \
+ v4l_print_ioctl(name, cmd); \
+} while (0)
+# define KDBG(level, fmt, args...) \
+do { \
+ if (debug >= (level)) { \
+ if ((level) == 1 || (level) == 2) \
+ pr_info("sn9c102: " fmt "\n", ## args); \
+ else if ((level) == 3) \
+ pr_debug("sn9c102: [%s:%d] " fmt "\n", \
+ __func__, __LINE__ , ## args); \
+ } \
+} while (0)
+#else
+# define DBG(level, fmt, args...) do {;} while(0)
+# define V4LDBG(level, name, cmd) do {;} while(0)
+# define KDBG(level, fmt, args...) do {;} while(0)
+#endif
+
+#undef PDBG
+#define PDBG(fmt, args...) \
+dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", __FILE__, __func__, \
+ __LINE__ , ## args)
+
+#undef PDBGG
+#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */
+
+#endif /* _SN9C102_H_ */
diff --git a/drivers/media/video/sn9c102/sn9c102_config.h b/drivers/media/video/sn9c102/sn9c102_config.h
new file mode 100644
index 00000000..0f4e0378
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_config.h
@@ -0,0 +1,86 @@
+/***************************************************************************
+ * Global parameters for the V4L2 driver for SN9C1xx PC Camera Controllers *
+ * *
+ * Copyright (C) 2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#ifndef _SN9C102_CONFIG_H_
+#define _SN9C102_CONFIG_H_
+
+#include <linux/types.h>
+#include <linux/jiffies.h>
+
+#define SN9C102_DEBUG
+#define SN9C102_DEBUG_LEVEL 2
+#define SN9C102_MAX_DEVICES 64
+#define SN9C102_PRESERVE_IMGSCALE 0
+#define SN9C102_FORCE_MUNMAP 0
+#define SN9C102_MAX_FRAMES 32
+#define SN9C102_URBS 2
+#define SN9C102_ISO_PACKETS 7
+#define SN9C102_ALTERNATE_SETTING 8
+#define SN9C102_URB_TIMEOUT msecs_to_jiffies(2 * SN9C102_ISO_PACKETS)
+#define SN9C102_CTRL_TIMEOUT 300
+#define SN9C102_FRAME_TIMEOUT 0
+
+/*****************************************************************************/
+
+static const u8 SN9C102_Y_QTABLE0[64] = {
+ 8, 5, 5, 8, 12, 20, 25, 30,
+ 6, 6, 7, 9, 13, 29, 30, 27,
+ 7, 6, 8, 12, 20, 28, 34, 28,
+ 7, 8, 11, 14, 25, 43, 40, 31,
+ 9, 11, 18, 28, 34, 54, 51, 38,
+ 12, 17, 27, 32, 40, 52, 56, 46,
+ 24, 32, 39, 43, 51, 60, 60, 50,
+ 36, 46, 47, 49, 56, 50, 51, 49
+};
+
+static const u8 SN9C102_UV_QTABLE0[64] = {
+ 8, 9, 12, 23, 49, 49, 49, 49,
+ 9, 10, 13, 33, 49, 49, 49, 49,
+ 12, 13, 28, 49, 49, 49, 49, 49,
+ 23, 33, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49
+};
+
+static const u8 SN9C102_Y_QTABLE1[64] = {
+ 16, 11, 10, 16, 24, 40, 51, 61,
+ 12, 12, 14, 19, 26, 58, 60, 55,
+ 14, 13, 16, 24, 40, 57, 69, 56,
+ 14, 17, 22, 29, 51, 87, 80, 62,
+ 18, 22, 37, 56, 68, 109, 103, 77,
+ 24, 35, 55, 64, 81, 104, 113, 92,
+ 49, 64, 78, 87, 103, 121, 120, 101,
+ 72, 92, 95, 98, 112, 100, 103, 99
+};
+
+static const u8 SN9C102_UV_QTABLE1[64] = {
+ 17, 18, 24, 47, 99, 99, 99, 99,
+ 18, 21, 26, 66, 99, 99, 99, 99,
+ 24, 26, 56, 99, 99, 99, 99, 99,
+ 47, 66, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99
+};
+
+#endif /* _SN9C102_CONFIG_H_ */
diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c
new file mode 100644
index 00000000..c2882fa5
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_core.c
@@ -0,0 +1,3423 @@
+/***************************************************************************
+ * V4L2 driver for SN9C1xx PC Camera Controllers *
+ * *
+ * Copyright (C) 2004-2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/compiler.h>
+#include <linux/ioctl.h>
+#include <linux/poll.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/version.h>
+#include <linux/page-flags.h>
+#include <asm/byteorder.h>
+#include <asm/page.h>
+#include <asm/uaccess.h>
+
+#include "sn9c102.h"
+
+/*****************************************************************************/
+
+#define SN9C102_MODULE_NAME "V4L2 driver for SN9C1xx PC Camera Controllers"
+#define SN9C102_MODULE_ALIAS "sn9c1xx"
+#define SN9C102_MODULE_AUTHOR "(C) 2004-2007 Luca Risolia"
+#define SN9C102_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
+#define SN9C102_MODULE_LICENSE "GPL"
+#define SN9C102_MODULE_VERSION "1:1.48"
+
+/*****************************************************************************/
+
+MODULE_DEVICE_TABLE(usb, sn9c102_id_table);
+
+MODULE_AUTHOR(SN9C102_MODULE_AUTHOR " " SN9C102_AUTHOR_EMAIL);
+MODULE_DESCRIPTION(SN9C102_MODULE_NAME);
+MODULE_ALIAS(SN9C102_MODULE_ALIAS);
+MODULE_VERSION(SN9C102_MODULE_VERSION);
+MODULE_LICENSE(SN9C102_MODULE_LICENSE);
+
+static short video_nr[] = {[0 ... SN9C102_MAX_DEVICES-1] = -1};
+module_param_array(video_nr, short, NULL, 0444);
+MODULE_PARM_DESC(video_nr,
+ " <-1|n[,...]>"
+ "\nSpecify V4L2 minor mode number."
+ "\n-1 = use next available (default)"
+ "\n n = use minor number n (integer >= 0)"
+ "\nYou can specify up to "__MODULE_STRING(SN9C102_MAX_DEVICES)
+ " cameras this way."
+ "\nFor example:"
+ "\nvideo_nr=-1,2,-1 would assign minor number 2 to"
+ "\nthe second camera and use auto for the first"
+ "\none and for every other camera."
+ "\n");
+
+static bool force_munmap[] = {[0 ... SN9C102_MAX_DEVICES-1] =
+ SN9C102_FORCE_MUNMAP};
+module_param_array(force_munmap, bool, NULL, 0444);
+MODULE_PARM_DESC(force_munmap,
+ " <0|1[,...]>"
+ "\nForce the application to unmap previously"
+ "\nmapped buffer memory before calling any VIDIOC_S_CROP or"
+ "\nVIDIOC_S_FMT ioctl's. Not all the applications support"
+ "\nthis feature. This parameter is specific for each"
+ "\ndetected camera."
+ "\n0 = do not force memory unmapping"
+ "\n1 = force memory unmapping (save memory)"
+ "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"."
+ "\n");
+
+static unsigned int frame_timeout[] = {[0 ... SN9C102_MAX_DEVICES-1] =
+ SN9C102_FRAME_TIMEOUT};
+module_param_array(frame_timeout, uint, NULL, 0644);
+MODULE_PARM_DESC(frame_timeout,
+ " <0|n[,...]>"
+ "\nTimeout for a video frame in seconds before"
+ "\nreturning an I/O error; 0 for infinity."
+ "\nThis parameter is specific for each detected camera."
+ "\nDefault value is "__MODULE_STRING(SN9C102_FRAME_TIMEOUT)"."
+ "\n");
+
+#ifdef SN9C102_DEBUG
+static unsigned short debug = SN9C102_DEBUG_LEVEL;
+module_param(debug, ushort, 0644);
+MODULE_PARM_DESC(debug,
+ " <n>"
+ "\nDebugging information level, from 0 to 3:"
+ "\n0 = none (use carefully)"
+ "\n1 = critical errors"
+ "\n2 = significant informations"
+ "\n3 = more verbose messages"
+ "\nLevel 3 is useful for testing only."
+ "\nDefault value is "__MODULE_STRING(SN9C102_DEBUG_LEVEL)"."
+ "\n");
+#endif
+
+/*
+ Add the probe entries to this table. Be sure to add the entry in the right
+ place, since, on failure, the next probing routine is called according to
+ the order of the list below, from top to bottom.
+*/
+static int (*sn9c102_sensor_table[])(struct sn9c102_device *) = {
+ &sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */
+ &sn9c102_probe_hv7131r, /* strong detection based on SENSOR ids */
+ &sn9c102_probe_mi0343, /* strong detection based on SENSOR ids */
+ &sn9c102_probe_mi0360, /* strong detection based on SENSOR ids */
+ &sn9c102_probe_mt9v111, /* strong detection based on SENSOR ids */
+ &sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */
+ &sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */
+ &sn9c102_probe_ov7630, /* strong detection based on SENSOR ids */
+ &sn9c102_probe_ov7660, /* strong detection based on SENSOR ids */
+ &sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */
+ &sn9c102_probe_tas5110d, /* detection based on USB pid/vid */
+ &sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */
+};
+
+/*****************************************************************************/
+
+static u32
+sn9c102_request_buffers(struct sn9c102_device* cam, u32 count,
+ enum sn9c102_io_method io)
+{
+ struct v4l2_pix_format* p = &(cam->sensor.pix_format);
+ struct v4l2_rect* r = &(cam->sensor.cropcap.bounds);
+ size_t imagesize = cam->module_param.force_munmap || io == IO_READ ?
+ (p->width * p->height * p->priv) / 8 :
+ (r->width * r->height * p->priv) / 8;
+ void* buff = NULL;
+ u32 i;
+
+ if (count > SN9C102_MAX_FRAMES)
+ count = SN9C102_MAX_FRAMES;
+
+ if (cam->bridge == BRIDGE_SN9C105 || cam->bridge == BRIDGE_SN9C120)
+ imagesize += 589 + 2; /* length of JPEG header + EOI marker */
+
+ cam->nbuffers = count;
+ while (cam->nbuffers > 0) {
+ if ((buff = vmalloc_32_user(cam->nbuffers *
+ PAGE_ALIGN(imagesize))))
+ break;
+ cam->nbuffers--;
+ }
+
+ for (i = 0; i < cam->nbuffers; i++) {
+ cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize);
+ cam->frame[i].buf.index = i;
+ cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize);
+ cam->frame[i].buf.length = imagesize;
+ cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cam->frame[i].buf.sequence = 0;
+ cam->frame[i].buf.field = V4L2_FIELD_NONE;
+ cam->frame[i].buf.memory = V4L2_MEMORY_MMAP;
+ cam->frame[i].buf.flags = 0;
+ }
+
+ return cam->nbuffers;
+}
+
+
+static void sn9c102_release_buffers(struct sn9c102_device* cam)
+{
+ if (cam->nbuffers) {
+ vfree(cam->frame[0].bufmem);
+ cam->nbuffers = 0;
+ }
+ cam->frame_current = NULL;
+}
+
+
+static void sn9c102_empty_framequeues(struct sn9c102_device* cam)
+{
+ u32 i;
+
+ INIT_LIST_HEAD(&cam->inqueue);
+ INIT_LIST_HEAD(&cam->outqueue);
+
+ for (i = 0; i < SN9C102_MAX_FRAMES; i++) {
+ cam->frame[i].state = F_UNUSED;
+ cam->frame[i].buf.bytesused = 0;
+ }
+}
+
+
+static void sn9c102_requeue_outqueue(struct sn9c102_device* cam)
+{
+ struct sn9c102_frame_t *i;
+
+ list_for_each_entry(i, &cam->outqueue, frame) {
+ i->state = F_QUEUED;
+ list_add(&i->frame, &cam->inqueue);
+ }
+
+ INIT_LIST_HEAD(&cam->outqueue);
+}
+
+
+static void sn9c102_queue_unusedframes(struct sn9c102_device* cam)
+{
+ unsigned long lock_flags;
+ u32 i;
+
+ for (i = 0; i < cam->nbuffers; i++)
+ if (cam->frame[i].state == F_UNUSED) {
+ cam->frame[i].state = F_QUEUED;
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ list_add_tail(&cam->frame[i].frame, &cam->inqueue);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+ }
+}
+
+/*****************************************************************************/
+
+/*
+ Write a sequence of count value/register pairs. Returns -1 after the first
+ failed write, or 0 for no errors.
+*/
+int sn9c102_write_regs(struct sn9c102_device* cam, const u8 valreg[][2],
+ int count)
+{
+ struct usb_device* udev = cam->usbdev;
+ u8* buff = cam->control_buffer;
+ int i, res;
+
+ for (i = 0; i < count; i++) {
+ u8 index = valreg[i][1];
+
+ /*
+ index is a u8, so it must be <256 and can't be out of range.
+ If we put in a check anyway, gcc annoys us with a warning
+ hat our check is useless. People get all uppity when they
+ see warnings in the kernel compile.
+ */
+
+ *buff = valreg[i][0];
+
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08,
+ 0x41, index, 0, buff, 1,
+ SN9C102_CTRL_TIMEOUT);
+
+ if (res < 0) {
+ DBG(3, "Failed to write a register (value 0x%02X, "
+ "index 0x%02X, error %d)", *buff, index, res);
+ return -1;
+ }
+
+ cam->reg[index] = *buff;
+ }
+
+ return 0;
+}
+
+
+int sn9c102_write_reg(struct sn9c102_device* cam, u8 value, u16 index)
+{
+ struct usb_device* udev = cam->usbdev;
+ u8* buff = cam->control_buffer;
+ int res;
+
+ if (index >= ARRAY_SIZE(cam->reg))
+ return -1;
+
+ *buff = value;
+
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
+ index, 0, buff, 1, SN9C102_CTRL_TIMEOUT);
+ if (res < 0) {
+ DBG(3, "Failed to write a register (value 0x%02X, index "
+ "0x%02X, error %d)", value, index, res);
+ return -1;
+ }
+
+ cam->reg[index] = value;
+
+ return 0;
+}
+
+
+/* NOTE: with the SN9C10[123] reading some registers always returns 0 */
+int sn9c102_read_reg(struct sn9c102_device* cam, u16 index)
+{
+ struct usb_device* udev = cam->usbdev;
+ u8* buff = cam->control_buffer;
+ int res;
+
+ res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1,
+ index, 0, buff, 1, SN9C102_CTRL_TIMEOUT);
+ if (res < 0)
+ DBG(3, "Failed to read a register (index 0x%02X, error %d)",
+ index, res);
+
+ return (res >= 0) ? (int)(*buff) : -1;
+}
+
+
+int sn9c102_pread_reg(struct sn9c102_device* cam, u16 index)
+{
+ if (index >= ARRAY_SIZE(cam->reg))
+ return -1;
+
+ return cam->reg[index];
+}
+
+
+static int
+sn9c102_i2c_wait(struct sn9c102_device* cam,
+ const struct sn9c102_sensor* sensor)
+{
+ int i, r;
+
+ for (i = 1; i <= 5; i++) {
+ r = sn9c102_read_reg(cam, 0x08);
+ if (r < 0)
+ return -EIO;
+ if (r & 0x04)
+ return 0;
+ if (sensor->frequency & SN9C102_I2C_400KHZ)
+ udelay(5*16);
+ else
+ udelay(16*16);
+ }
+ return -EBUSY;
+}
+
+
+static int
+sn9c102_i2c_detect_read_error(struct sn9c102_device* cam,
+ const struct sn9c102_sensor* sensor)
+{
+ int r , err = 0;
+
+ r = sn9c102_read_reg(cam, 0x08);
+ if (r < 0)
+ err += r;
+
+ if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) {
+ if (!(r & 0x08))
+ err += -1;
+ } else {
+ if (r & 0x08)
+ err += -1;
+ }
+
+ return err ? -EIO : 0;
+}
+
+
+static int
+sn9c102_i2c_detect_write_error(struct sn9c102_device* cam,
+ const struct sn9c102_sensor* sensor)
+{
+ int r;
+ r = sn9c102_read_reg(cam, 0x08);
+ return (r < 0 || (r >= 0 && (r & 0x08))) ? -EIO : 0;
+}
+
+
+int
+sn9c102_i2c_try_raw_read(struct sn9c102_device* cam,
+ const struct sn9c102_sensor* sensor, u8 data0,
+ u8 data1, u8 n, u8 buffer[])
+{
+ struct usb_device* udev = cam->usbdev;
+ u8* data = cam->control_buffer;
+ int i = 0, err = 0, res;
+
+ /* Write cycle */
+ data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) |
+ ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) | 0x10;
+ data[1] = data0; /* I2C slave id */
+ data[2] = data1; /* address */
+ data[7] = 0x10;
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
+ 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ err += sn9c102_i2c_wait(cam, sensor);
+
+ /* Read cycle - n bytes */
+ data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) |
+ ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) |
+ (n << 4) | 0x02;
+ data[1] = data0;
+ data[7] = 0x10;
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
+ 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ err += sn9c102_i2c_wait(cam, sensor);
+
+ /* The first read byte will be placed in data[4] */
+ res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1,
+ 0x0a, 0, data, 5, SN9C102_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ err += sn9c102_i2c_detect_read_error(cam, sensor);
+
+ PDBGG("I2C read: address 0x%02X, first read byte: 0x%02X", data1,
+ data[4]);
+
+ if (err) {
+ DBG(3, "I2C read failed for %s image sensor", sensor->name);
+ return -1;
+ }
+
+ if (buffer)
+ for (i = 0; i < n && i < 5; i++)
+ buffer[n-i-1] = data[4-i];
+
+ return (int)data[4];
+}
+
+
+int
+sn9c102_i2c_try_raw_write(struct sn9c102_device* cam,
+ const struct sn9c102_sensor* sensor, u8 n, u8 data0,
+ u8 data1, u8 data2, u8 data3, u8 data4, u8 data5)
+{
+ struct usb_device* udev = cam->usbdev;
+ u8* data = cam->control_buffer;
+ int err = 0, res;
+
+ /* Write cycle. It usually is address + value */
+ data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) |
+ ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0)
+ | ((n - 1) << 4);
+ data[1] = data0;
+ data[2] = data1;
+ data[3] = data2;
+ data[4] = data3;
+ data[5] = data4;
+ data[6] = data5;
+ data[7] = 0x17;
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
+ 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ err += sn9c102_i2c_wait(cam, sensor);
+ err += sn9c102_i2c_detect_write_error(cam, sensor);
+
+ if (err)
+ DBG(3, "I2C write failed for %s image sensor", sensor->name);
+
+ PDBGG("I2C raw write: %u bytes, data0 = 0x%02X, data1 = 0x%02X, "
+ "data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X",
+ n, data0, data1, data2, data3, data4, data5);
+
+ return err ? -1 : 0;
+}
+
+
+int
+sn9c102_i2c_try_read(struct sn9c102_device* cam,
+ const struct sn9c102_sensor* sensor, u8 address)
+{
+ return sn9c102_i2c_try_raw_read(cam, sensor, sensor->i2c_slave_id,
+ address, 1, NULL);
+}
+
+
+static int sn9c102_i2c_try_write(struct sn9c102_device* cam,
+ const struct sn9c102_sensor* sensor,
+ u8 address, u8 value)
+{
+ return sn9c102_i2c_try_raw_write(cam, sensor, 3,
+ sensor->i2c_slave_id, address,
+ value, 0, 0, 0);
+}
+
+
+int sn9c102_i2c_read(struct sn9c102_device* cam, u8 address)
+{
+ return sn9c102_i2c_try_read(cam, &cam->sensor, address);
+}
+
+
+int sn9c102_i2c_write(struct sn9c102_device* cam, u8 address, u8 value)
+{
+ return sn9c102_i2c_try_write(cam, &cam->sensor, address, value);
+}
+
+/*****************************************************************************/
+
+static size_t sn9c102_sof_length(struct sn9c102_device* cam)
+{
+ switch (cam->bridge) {
+ case BRIDGE_SN9C101:
+ case BRIDGE_SN9C102:
+ return 12;
+ case BRIDGE_SN9C103:
+ return 18;
+ case BRIDGE_SN9C105:
+ case BRIDGE_SN9C120:
+ return 62;
+ }
+
+ return 0;
+}
+
+
+static void*
+sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len)
+{
+ static const char marker[6] = {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96};
+ const char *m = mem;
+ size_t soflen = 0, i, j;
+
+ soflen = sn9c102_sof_length(cam);
+
+ for (i = 0; i < len; i++) {
+ size_t b;
+
+ /* Read the variable part of the header */
+ if (unlikely(cam->sof.bytesread >= sizeof(marker))) {
+ cam->sof.header[cam->sof.bytesread] = *(m+i);
+ if (++cam->sof.bytesread == soflen) {
+ cam->sof.bytesread = 0;
+ return mem + i;
+ }
+ continue;
+ }
+
+ /* Search for the SOF marker (fixed part) in the header */
+ for (j = 0, b=cam->sof.bytesread; j+b < sizeof(marker); j++) {
+ if (unlikely(i+j == len))
+ return NULL;
+ if (*(m+i+j) == marker[cam->sof.bytesread]) {
+ cam->sof.header[cam->sof.bytesread] = *(m+i+j);
+ if (++cam->sof.bytesread == sizeof(marker)) {
+ PDBGG("Bytes to analyze: %zd. SOF "
+ "starts at byte #%zd", len, i);
+ i += j+1;
+ break;
+ }
+ } else {
+ cam->sof.bytesread = 0;
+ break;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+
+static void*
+sn9c102_find_eof_header(struct sn9c102_device* cam, void* mem, size_t len)
+{
+ static const u8 eof_header[4][4] = {
+ {0x00, 0x00, 0x00, 0x00},
+ {0x40, 0x00, 0x00, 0x00},
+ {0x80, 0x00, 0x00, 0x00},
+ {0xc0, 0x00, 0x00, 0x00},
+ };
+ size_t i, j;
+
+ /* The EOF header does not exist in compressed data */
+ if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X ||
+ cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_JPEG)
+ return NULL;
+
+ /*
+ The EOF header might cross the packet boundary, but this is not a
+ problem, since the end of a frame is determined by checking its size
+ in the first place.
+ */
+ for (i = 0; (len >= 4) && (i <= len - 4); i++)
+ for (j = 0; j < ARRAY_SIZE(eof_header); j++)
+ if (!memcmp(mem + i, eof_header[j], 4))
+ return mem + i;
+
+ return NULL;
+}
+
+
+static void
+sn9c102_write_jpegheader(struct sn9c102_device* cam, struct sn9c102_frame_t* f)
+{
+ static const u8 jpeg_header[589] = {
+ 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x06, 0x04, 0x05,
+ 0x06, 0x05, 0x04, 0x06, 0x06, 0x05, 0x06, 0x07, 0x07, 0x06,
+ 0x08, 0x0a, 0x10, 0x0a, 0x0a, 0x09, 0x09, 0x0a, 0x14, 0x0e,
+ 0x0f, 0x0c, 0x10, 0x17, 0x14, 0x18, 0x18, 0x17, 0x14, 0x16,
+ 0x16, 0x1a, 0x1d, 0x25, 0x1f, 0x1a, 0x1b, 0x23, 0x1c, 0x16,
+ 0x16, 0x20, 0x2c, 0x20, 0x23, 0x26, 0x27, 0x29, 0x2a, 0x29,
+ 0x19, 0x1f, 0x2d, 0x30, 0x2d, 0x28, 0x30, 0x25, 0x28, 0x29,
+ 0x28, 0x01, 0x07, 0x07, 0x07, 0x0a, 0x08, 0x0a, 0x13, 0x0a,
+ 0x0a, 0x13, 0x28, 0x1a, 0x16, 0x1a, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0xff, 0xc4, 0x01, 0xa2,
+ 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
+ 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x01,
+ 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03,
+ 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00,
+ 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04,
+ 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04,
+ 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61,
+ 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23,
+ 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62,
+ 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25,
+ 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38,
+ 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
+ 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64,
+ 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76,
+ 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+ 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
+ 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa,
+ 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2,
+ 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3,
+ 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3,
+ 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3,
+ 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x11, 0x00, 0x02,
+ 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04,
+ 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04,
+ 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
+ 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1,
+ 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1,
+ 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19,
+ 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
+ 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
+ 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64,
+ 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76,
+ 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
+ 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9,
+ 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
+ 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
+ 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3,
+ 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4,
+ 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc0, 0x00, 0x11,
+ 0x08, 0x01, 0xe0, 0x02, 0x80, 0x03, 0x01, 0x21, 0x00, 0x02,
+ 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xda, 0x00, 0x0c, 0x03,
+ 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00
+ };
+ u8 *pos = f->bufmem;
+
+ memcpy(pos, jpeg_header, sizeof(jpeg_header));
+ *(pos + 6) = 0x00;
+ *(pos + 7 + 64) = 0x01;
+ if (cam->compression.quality == 0) {
+ memcpy(pos + 7, SN9C102_Y_QTABLE0, 64);
+ memcpy(pos + 8 + 64, SN9C102_UV_QTABLE0, 64);
+ } else if (cam->compression.quality == 1) {
+ memcpy(pos + 7, SN9C102_Y_QTABLE1, 64);
+ memcpy(pos + 8 + 64, SN9C102_UV_QTABLE1, 64);
+ }
+ *(pos + 564) = cam->sensor.pix_format.width & 0xFF;
+ *(pos + 563) = (cam->sensor.pix_format.width >> 8) & 0xFF;
+ *(pos + 562) = cam->sensor.pix_format.height & 0xFF;
+ *(pos + 561) = (cam->sensor.pix_format.height >> 8) & 0xFF;
+ *(pos + 567) = 0x21;
+
+ f->buf.bytesused += sizeof(jpeg_header);
+}
+
+
+static void sn9c102_urb_complete(struct urb *urb)
+{
+ struct sn9c102_device* cam = urb->context;
+ struct sn9c102_frame_t** f;
+ size_t imagesize, soflen;
+ u8 i;
+ int err = 0;
+
+ if (urb->status == -ENOENT)
+ return;
+
+ f = &cam->frame_current;
+
+ if (cam->stream == STREAM_INTERRUPT) {
+ cam->stream = STREAM_OFF;
+ if ((*f))
+ (*f)->state = F_QUEUED;
+ cam->sof.bytesread = 0;
+ DBG(3, "Stream interrupted by application");
+ wake_up(&cam->wait_stream);
+ }
+
+ if (cam->state & DEV_DISCONNECTED)
+ return;
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ wake_up_interruptible(&cam->wait_frame);
+ return;
+ }
+
+ if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue))
+ goto resubmit_urb;
+
+ if (!(*f))
+ (*f) = list_entry(cam->inqueue.next, struct sn9c102_frame_t,
+ frame);
+
+ imagesize = (cam->sensor.pix_format.width *
+ cam->sensor.pix_format.height *
+ cam->sensor.pix_format.priv) / 8;
+ if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_JPEG)
+ imagesize += 589; /* length of jpeg header */
+ soflen = sn9c102_sof_length(cam);
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ unsigned int img, len, status;
+ void *pos, *sof, *eof;
+
+ len = urb->iso_frame_desc[i].actual_length;
+ status = urb->iso_frame_desc[i].status;
+ pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer;
+
+ if (status) {
+ DBG(3, "Error in isochronous frame");
+ (*f)->state = F_ERROR;
+ cam->sof.bytesread = 0;
+ continue;
+ }
+
+ PDBGG("Isochrnous frame: length %u, #%u i", len, i);
+
+redo:
+ sof = sn9c102_find_sof_header(cam, pos, len);
+ if (likely(!sof)) {
+ eof = sn9c102_find_eof_header(cam, pos, len);
+ if ((*f)->state == F_GRABBING) {
+end_of_frame:
+ img = len;
+
+ if (eof)
+ img = (eof > pos) ? eof - pos - 1 : 0;
+
+ if ((*f)->buf.bytesused + img > imagesize) {
+ u32 b;
+ b = (*f)->buf.bytesused + img -
+ imagesize;
+ img = imagesize - (*f)->buf.bytesused;
+ PDBGG("Expected EOF not found: video "
+ "frame cut");
+ if (eof)
+ DBG(3, "Exceeded limit: +%u "
+ "bytes", (unsigned)(b));
+ }
+
+ memcpy((*f)->bufmem + (*f)->buf.bytesused, pos,
+ img);
+
+ if ((*f)->buf.bytesused == 0)
+ do_gettimeofday(&(*f)->buf.timestamp);
+
+ (*f)->buf.bytesused += img;
+
+ if ((*f)->buf.bytesused == imagesize ||
+ ((cam->sensor.pix_format.pixelformat ==
+ V4L2_PIX_FMT_SN9C10X ||
+ cam->sensor.pix_format.pixelformat ==
+ V4L2_PIX_FMT_JPEG) && eof)) {
+ u32 b;
+
+ b = (*f)->buf.bytesused;
+ (*f)->state = F_DONE;
+ (*f)->buf.sequence= ++cam->frame_count;
+
+ spin_lock(&cam->queue_lock);
+ list_move_tail(&(*f)->frame,
+ &cam->outqueue);
+ if (!list_empty(&cam->inqueue))
+ (*f) = list_entry(
+ cam->inqueue.next,
+ struct sn9c102_frame_t,
+ frame );
+ else
+ (*f) = NULL;
+ spin_unlock(&cam->queue_lock);
+
+ memcpy(cam->sysfs.frame_header,
+ cam->sof.header, soflen);
+
+ DBG(3, "Video frame captured: %lu "
+ "bytes", (unsigned long)(b));
+
+ if (!(*f))
+ goto resubmit_urb;
+
+ } else if (eof) {
+ (*f)->state = F_ERROR;
+ DBG(3, "Not expected EOF after %lu "
+ "bytes of image data",
+ (unsigned long)
+ ((*f)->buf.bytesused));
+ }
+
+ if (sof) /* (1) */
+ goto start_of_frame;
+
+ } else if (eof) {
+ DBG(3, "EOF without SOF");
+ continue;
+
+ } else {
+ PDBGG("Ignoring pointless isochronous frame");
+ continue;
+ }
+
+ } else if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR) {
+start_of_frame:
+ (*f)->state = F_GRABBING;
+ (*f)->buf.bytesused = 0;
+ len -= (sof - pos);
+ pos = sof;
+ if (cam->sensor.pix_format.pixelformat ==
+ V4L2_PIX_FMT_JPEG)
+ sn9c102_write_jpegheader(cam, (*f));
+ DBG(3, "SOF detected: new video frame");
+ if (len)
+ goto redo;
+
+ } else if ((*f)->state == F_GRABBING) {
+ eof = sn9c102_find_eof_header(cam, pos, len);
+ if (eof && eof < sof)
+ goto end_of_frame; /* (1) */
+ else {
+ if (cam->sensor.pix_format.pixelformat ==
+ V4L2_PIX_FMT_SN9C10X ||
+ cam->sensor.pix_format.pixelformat ==
+ V4L2_PIX_FMT_JPEG) {
+ if (sof - pos >= soflen) {
+ eof = sof - soflen;
+ } else { /* remove header */
+ eof = pos;
+ (*f)->buf.bytesused -=
+ (soflen - (sof - pos));
+ }
+ goto end_of_frame;
+ } else {
+ DBG(3, "SOF before expected EOF after "
+ "%lu bytes of image data",
+ (unsigned long)
+ ((*f)->buf.bytesused));
+ goto start_of_frame;
+ }
+ }
+ }
+ }
+
+resubmit_urb:
+ urb->dev = cam->usbdev;
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0 && err != -EPERM) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "usb_submit_urb() failed");
+ }
+
+ wake_up_interruptible(&cam->wait_frame);
+}
+
+
+static int sn9c102_start_transfer(struct sn9c102_device* cam)
+{
+ struct usb_device *udev = cam->usbdev;
+ struct urb* urb;
+ struct usb_host_interface* altsetting = usb_altnum_to_altsetting(
+ usb_ifnum_to_if(udev, 0),
+ SN9C102_ALTERNATE_SETTING);
+ const unsigned int psz = le16_to_cpu(altsetting->
+ endpoint[0].desc.wMaxPacketSize);
+ s8 i, j;
+ int err = 0;
+
+ for (i = 0; i < SN9C102_URBS; i++) {
+ cam->transfer_buffer[i] = kzalloc(SN9C102_ISO_PACKETS * psz,
+ GFP_KERNEL);
+ if (!cam->transfer_buffer[i]) {
+ err = -ENOMEM;
+ DBG(1, "Not enough memory");
+ goto free_buffers;
+ }
+ }
+
+ for (i = 0; i < SN9C102_URBS; i++) {
+ urb = usb_alloc_urb(SN9C102_ISO_PACKETS, GFP_KERNEL);
+ cam->urb[i] = urb;
+ if (!urb) {
+ err = -ENOMEM;
+ DBG(1, "usb_alloc_urb() failed");
+ goto free_urbs;
+ }
+ urb->dev = udev;
+ urb->context = cam;
+ urb->pipe = usb_rcvisocpipe(udev, 1);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->number_of_packets = SN9C102_ISO_PACKETS;
+ urb->complete = sn9c102_urb_complete;
+ urb->transfer_buffer = cam->transfer_buffer[i];
+ urb->transfer_buffer_length = psz * SN9C102_ISO_PACKETS;
+ urb->interval = 1;
+ for (j = 0; j < SN9C102_ISO_PACKETS; j++) {
+ urb->iso_frame_desc[j].offset = psz * j;
+ urb->iso_frame_desc[j].length = psz;
+ }
+ }
+
+ /* Enable video */
+ if (!(cam->reg[0x01] & 0x04)) {
+ err = sn9c102_write_reg(cam, cam->reg[0x01] | 0x04, 0x01);
+ if (err) {
+ err = -EIO;
+ DBG(1, "I/O hardware error");
+ goto free_urbs;
+ }
+ }
+
+ err = usb_set_interface(udev, 0, SN9C102_ALTERNATE_SETTING);
+ if (err) {
+ DBG(1, "usb_set_interface() failed");
+ goto free_urbs;
+ }
+
+ cam->frame_current = NULL;
+ cam->sof.bytesread = 0;
+
+ for (i = 0; i < SN9C102_URBS; i++) {
+ err = usb_submit_urb(cam->urb[i], GFP_KERNEL);
+ if (err) {
+ for (j = i-1; j >= 0; j--)
+ usb_kill_urb(cam->urb[j]);
+ DBG(1, "usb_submit_urb() failed, error %d", err);
+ goto free_urbs;
+ }
+ }
+
+ return 0;
+
+free_urbs:
+ for (i = 0; (i < SN9C102_URBS) && cam->urb[i]; i++)
+ usb_free_urb(cam->urb[i]);
+
+free_buffers:
+ for (i = 0; (i < SN9C102_URBS) && cam->transfer_buffer[i]; i++)
+ kfree(cam->transfer_buffer[i]);
+
+ return err;
+}
+
+
+static int sn9c102_stop_transfer(struct sn9c102_device* cam)
+{
+ struct usb_device *udev = cam->usbdev;
+ s8 i;
+ int err = 0;
+
+ if (cam->state & DEV_DISCONNECTED)
+ return 0;
+
+ for (i = SN9C102_URBS-1; i >= 0; i--) {
+ usb_kill_urb(cam->urb[i]);
+ usb_free_urb(cam->urb[i]);
+ kfree(cam->transfer_buffer[i]);
+ }
+
+ err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */
+ if (err)
+ DBG(3, "usb_set_interface() failed");
+
+ return err;
+}
+
+
+static int sn9c102_stream_interrupt(struct sn9c102_device* cam)
+{
+ long timeout;
+
+ cam->stream = STREAM_INTERRUPT;
+ timeout = wait_event_timeout(cam->wait_stream,
+ (cam->stream == STREAM_OFF) ||
+ (cam->state & DEV_DISCONNECTED),
+ SN9C102_URB_TIMEOUT);
+ if (cam->state & DEV_DISCONNECTED)
+ return -ENODEV;
+ else if (cam->stream != STREAM_OFF) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "URB timeout reached. The camera is misconfigured. "
+ "To use it, close and open %s again.",
+ video_device_node_name(cam->v4ldev));
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static u16 sn9c102_strtou16(const char* buff, size_t len, ssize_t* count)
+{
+ char str[7];
+ char* endp;
+ unsigned long val;
+
+ if (len < 6) {
+ strncpy(str, buff, len);
+ str[len] = '\0';
+ } else {
+ strncpy(str, buff, 6);
+ str[6] = '\0';
+ }
+
+ val = simple_strtoul(str, &endp, 0);
+
+ *count = 0;
+ if (val <= 0xffff)
+ *count = (ssize_t)(endp - str);
+ if ((*count) && (len == *count+1) && (buff[*count] == '\n'))
+ *count += 1;
+
+ return (u16)val;
+}
+
+/*
+ NOTE 1: being inside one of the following methods implies that the v4l
+ device exists for sure (see kobjects and reference counters)
+ NOTE 2: buffers are PAGE_SIZE long
+*/
+
+static ssize_t sn9c102_show_reg(struct device* cd,
+ struct device_attribute *attr, char* buf)
+{
+ struct sn9c102_device* cam;
+ ssize_t count;
+
+ if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(container_of(cd, struct video_device, dev));
+ if (!cam) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ count = sprintf(buf, "%u\n", cam->sysfs.reg);
+
+ mutex_unlock(&sn9c102_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t
+sn9c102_store_reg(struct device* cd, struct device_attribute *attr,
+ const char* buf, size_t len)
+{
+ struct sn9c102_device* cam;
+ u16 index;
+ ssize_t count;
+
+ if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(container_of(cd, struct video_device, dev));
+ if (!cam) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ index = sn9c102_strtou16(buf, len, &count);
+ if (index >= ARRAY_SIZE(cam->reg) || !count) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -EINVAL;
+ }
+
+ cam->sysfs.reg = index;
+
+ DBG(2, "Moved SN9C1XX register index to 0x%02X", cam->sysfs.reg);
+ DBG(3, "Written bytes: %zd", count);
+
+ mutex_unlock(&sn9c102_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t sn9c102_show_val(struct device* cd,
+ struct device_attribute *attr, char* buf)
+{
+ struct sn9c102_device* cam;
+ ssize_t count;
+ int val;
+
+ if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(container_of(cd, struct video_device, dev));
+ if (!cam) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ if ((val = sn9c102_read_reg(cam, cam->sysfs.reg)) < 0) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -EIO;
+ }
+
+ count = sprintf(buf, "%d\n", val);
+
+ DBG(3, "Read bytes: %zd, value: %d", count, val);
+
+ mutex_unlock(&sn9c102_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t
+sn9c102_store_val(struct device* cd, struct device_attribute *attr,
+ const char* buf, size_t len)
+{
+ struct sn9c102_device* cam;
+ u16 value;
+ ssize_t count;
+ int err;
+
+ if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(container_of(cd, struct video_device, dev));
+ if (!cam) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ value = sn9c102_strtou16(buf, len, &count);
+ if (!count) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -EINVAL;
+ }
+
+ err = sn9c102_write_reg(cam, value, cam->sysfs.reg);
+ if (err) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -EIO;
+ }
+
+ DBG(2, "Written SN9C1XX reg. 0x%02X, val. 0x%02X",
+ cam->sysfs.reg, value);
+ DBG(3, "Written bytes: %zd", count);
+
+ mutex_unlock(&sn9c102_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t sn9c102_show_i2c_reg(struct device* cd,
+ struct device_attribute *attr, char* buf)
+{
+ struct sn9c102_device* cam;
+ ssize_t count;
+
+ if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(container_of(cd, struct video_device, dev));
+ if (!cam) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ count = sprintf(buf, "%u\n", cam->sysfs.i2c_reg);
+
+ DBG(3, "Read bytes: %zd", count);
+
+ mutex_unlock(&sn9c102_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t
+sn9c102_store_i2c_reg(struct device* cd, struct device_attribute *attr,
+ const char* buf, size_t len)
+{
+ struct sn9c102_device* cam;
+ u16 index;
+ ssize_t count;
+
+ if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(container_of(cd, struct video_device, dev));
+ if (!cam) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ index = sn9c102_strtou16(buf, len, &count);
+ if (!count) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -EINVAL;
+ }
+
+ cam->sysfs.i2c_reg = index;
+
+ DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg);
+ DBG(3, "Written bytes: %zd", count);
+
+ mutex_unlock(&sn9c102_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t sn9c102_show_i2c_val(struct device* cd,
+ struct device_attribute *attr, char* buf)
+{
+ struct sn9c102_device* cam;
+ ssize_t count;
+ int val;
+
+ if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(container_of(cd, struct video_device, dev));
+ if (!cam) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ if (!(cam->sensor.sysfs_ops & SN9C102_I2C_READ)) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -ENOSYS;
+ }
+
+ if ((val = sn9c102_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -EIO;
+ }
+
+ count = sprintf(buf, "%d\n", val);
+
+ DBG(3, "Read bytes: %zd, value: %d", count, val);
+
+ mutex_unlock(&sn9c102_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t
+sn9c102_store_i2c_val(struct device* cd, struct device_attribute *attr,
+ const char* buf, size_t len)
+{
+ struct sn9c102_device* cam;
+ u16 value;
+ ssize_t count;
+ int err;
+
+ if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(container_of(cd, struct video_device, dev));
+ if (!cam) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ if (!(cam->sensor.sysfs_ops & SN9C102_I2C_WRITE)) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -ENOSYS;
+ }
+
+ value = sn9c102_strtou16(buf, len, &count);
+ if (!count) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -EINVAL;
+ }
+
+ err = sn9c102_i2c_write(cam, cam->sysfs.i2c_reg, value);
+ if (err) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -EIO;
+ }
+
+ DBG(2, "Written sensor reg. 0x%02X, val. 0x%02X",
+ cam->sysfs.i2c_reg, value);
+ DBG(3, "Written bytes: %zd", count);
+
+ mutex_unlock(&sn9c102_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t
+sn9c102_store_green(struct device* cd, struct device_attribute *attr,
+ const char* buf, size_t len)
+{
+ struct sn9c102_device* cam;
+ enum sn9c102_bridge bridge;
+ ssize_t res = 0;
+ u16 value;
+ ssize_t count;
+
+ if (mutex_lock_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(container_of(cd, struct video_device, dev));
+ if (!cam) {
+ mutex_unlock(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ bridge = cam->bridge;
+
+ mutex_unlock(&sn9c102_sysfs_lock);
+
+ value = sn9c102_strtou16(buf, len, &count);
+ if (!count)
+ return -EINVAL;
+
+ switch (bridge) {
+ case BRIDGE_SN9C101:
+ case BRIDGE_SN9C102:
+ if (value > 0x0f)
+ return -EINVAL;
+ if ((res = sn9c102_store_reg(cd, attr, "0x11", 4)) >= 0)
+ res = sn9c102_store_val(cd, attr, buf, len);
+ break;
+ case BRIDGE_SN9C103:
+ case BRIDGE_SN9C105:
+ case BRIDGE_SN9C120:
+ if (value > 0x7f)
+ return -EINVAL;
+ if ((res = sn9c102_store_reg(cd, attr, "0x07", 4)) >= 0)
+ res = sn9c102_store_val(cd, attr, buf, len);
+ break;
+ }
+
+ return res;
+}
+
+
+static ssize_t
+sn9c102_store_blue(struct device* cd, struct device_attribute *attr,
+ const char* buf, size_t len)
+{
+ ssize_t res = 0;
+ u16 value;
+ ssize_t count;
+
+ value = sn9c102_strtou16(buf, len, &count);
+ if (!count || value > 0x7f)
+ return -EINVAL;
+
+ if ((res = sn9c102_store_reg(cd, attr, "0x06", 4)) >= 0)
+ res = sn9c102_store_val(cd, attr, buf, len);
+
+ return res;
+}
+
+
+static ssize_t
+sn9c102_store_red(struct device* cd, struct device_attribute *attr,
+ const char* buf, size_t len)
+{
+ ssize_t res = 0;
+ u16 value;
+ ssize_t count;
+
+ value = sn9c102_strtou16(buf, len, &count);
+ if (!count || value > 0x7f)
+ return -EINVAL;
+
+ if ((res = sn9c102_store_reg(cd, attr, "0x05", 4)) >= 0)
+ res = sn9c102_store_val(cd, attr, buf, len);
+
+ return res;
+}
+
+
+static ssize_t sn9c102_show_frame_header(struct device* cd,
+ struct device_attribute *attr,
+ char* buf)
+{
+ struct sn9c102_device* cam;
+ ssize_t count;
+
+ cam = video_get_drvdata(container_of(cd, struct video_device, dev));
+ if (!cam)
+ return -ENODEV;
+
+ count = sizeof(cam->sysfs.frame_header);
+ memcpy(buf, cam->sysfs.frame_header, count);
+
+ DBG(3, "Frame header, read bytes: %zd", count);
+
+ return count;
+}
+
+
+static DEVICE_ATTR(reg, S_IRUGO | S_IWUSR, sn9c102_show_reg, sn9c102_store_reg);
+static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, sn9c102_show_val, sn9c102_store_val);
+static DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR,
+ sn9c102_show_i2c_reg, sn9c102_store_i2c_reg);
+static DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR,
+ sn9c102_show_i2c_val, sn9c102_store_i2c_val);
+static DEVICE_ATTR(green, S_IWUSR, NULL, sn9c102_store_green);
+static DEVICE_ATTR(blue, S_IWUSR, NULL, sn9c102_store_blue);
+static DEVICE_ATTR(red, S_IWUSR, NULL, sn9c102_store_red);
+static DEVICE_ATTR(frame_header, S_IRUGO, sn9c102_show_frame_header, NULL);
+
+
+static int sn9c102_create_sysfs(struct sn9c102_device* cam)
+{
+ struct device *dev = &(cam->v4ldev->dev);
+ int err = 0;
+
+ if ((err = device_create_file(dev, &dev_attr_reg)))
+ goto err_out;
+ if ((err = device_create_file(dev, &dev_attr_val)))
+ goto err_reg;
+ if ((err = device_create_file(dev, &dev_attr_frame_header)))
+ goto err_val;
+
+ if (cam->sensor.sysfs_ops) {
+ if ((err = device_create_file(dev, &dev_attr_i2c_reg)))
+ goto err_frame_header;
+ if ((err = device_create_file(dev, &dev_attr_i2c_val)))
+ goto err_i2c_reg;
+ }
+
+ if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) {
+ if ((err = device_create_file(dev, &dev_attr_green)))
+ goto err_i2c_val;
+ } else {
+ if ((err = device_create_file(dev, &dev_attr_blue)))
+ goto err_i2c_val;
+ if ((err = device_create_file(dev, &dev_attr_red)))
+ goto err_blue;
+ }
+
+ return 0;
+
+err_blue:
+ device_remove_file(dev, &dev_attr_blue);
+err_i2c_val:
+ if (cam->sensor.sysfs_ops)
+ device_remove_file(dev, &dev_attr_i2c_val);
+err_i2c_reg:
+ if (cam->sensor.sysfs_ops)
+ device_remove_file(dev, &dev_attr_i2c_reg);
+err_frame_header:
+ device_remove_file(dev, &dev_attr_frame_header);
+err_val:
+ device_remove_file(dev, &dev_attr_val);
+err_reg:
+ device_remove_file(dev, &dev_attr_reg);
+err_out:
+ return err;
+}
+#endif /* CONFIG_VIDEO_ADV_DEBUG */
+
+/*****************************************************************************/
+
+static int
+sn9c102_set_pix_format(struct sn9c102_device* cam, struct v4l2_pix_format* pix)
+{
+ int err = 0;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X ||
+ pix->pixelformat == V4L2_PIX_FMT_JPEG) {
+ switch (cam->bridge) {
+ case BRIDGE_SN9C101:
+ case BRIDGE_SN9C102:
+ case BRIDGE_SN9C103:
+ err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x80,
+ 0x18);
+ break;
+ case BRIDGE_SN9C105:
+ case BRIDGE_SN9C120:
+ err += sn9c102_write_reg(cam, cam->reg[0x18] & 0x7f,
+ 0x18);
+ break;
+ }
+ } else {
+ switch (cam->bridge) {
+ case BRIDGE_SN9C101:
+ case BRIDGE_SN9C102:
+ case BRIDGE_SN9C103:
+ err += sn9c102_write_reg(cam, cam->reg[0x18] & 0x7f,
+ 0x18);
+ break;
+ case BRIDGE_SN9C105:
+ case BRIDGE_SN9C120:
+ err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x80,
+ 0x18);
+ break;
+ }
+ }
+
+ return err ? -EIO : 0;
+}
+
+
+static int
+sn9c102_set_compression(struct sn9c102_device* cam,
+ struct v4l2_jpegcompression* compression)
+{
+ int i, err = 0;
+
+ switch (cam->bridge) {
+ case BRIDGE_SN9C101:
+ case BRIDGE_SN9C102:
+ case BRIDGE_SN9C103:
+ if (compression->quality == 0)
+ err += sn9c102_write_reg(cam, cam->reg[0x17] | 0x01,
+ 0x17);
+ else if (compression->quality == 1)
+ err += sn9c102_write_reg(cam, cam->reg[0x17] & 0xfe,
+ 0x17);
+ break;
+ case BRIDGE_SN9C105:
+ case BRIDGE_SN9C120:
+ if (compression->quality == 0) {
+ for (i = 0; i <= 63; i++) {
+ err += sn9c102_write_reg(cam,
+ SN9C102_Y_QTABLE1[i],
+ 0x100 + i);
+ err += sn9c102_write_reg(cam,
+ SN9C102_UV_QTABLE1[i],
+ 0x140 + i);
+ }
+ err += sn9c102_write_reg(cam, cam->reg[0x18] & 0xbf,
+ 0x18);
+ } else if (compression->quality == 1) {
+ for (i = 0; i <= 63; i++) {
+ err += sn9c102_write_reg(cam,
+ SN9C102_Y_QTABLE1[i],
+ 0x100 + i);
+ err += sn9c102_write_reg(cam,
+ SN9C102_UV_QTABLE1[i],
+ 0x140 + i);
+ }
+ err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x40,
+ 0x18);
+ }
+ break;
+ }
+
+ return err ? -EIO : 0;
+}
+
+
+static int sn9c102_set_scale(struct sn9c102_device* cam, u8 scale)
+{
+ u8 r = 0;
+ int err = 0;
+
+ if (scale == 1)
+ r = cam->reg[0x18] & 0xcf;
+ else if (scale == 2) {
+ r = cam->reg[0x18] & 0xcf;
+ r |= 0x10;
+ } else if (scale == 4)
+ r = cam->reg[0x18] | 0x20;
+
+ err += sn9c102_write_reg(cam, r, 0x18);
+ if (err)
+ return -EIO;
+
+ PDBGG("Scaling factor: %u", scale);
+
+ return 0;
+}
+
+
+static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = &cam->sensor;
+ u8 h_start = (u8)(rect->left - s->cropcap.bounds.left),
+ v_start = (u8)(rect->top - s->cropcap.bounds.top),
+ h_size = (u8)(rect->width / 16),
+ v_size = (u8)(rect->height / 16);
+ int err = 0;
+
+ err += sn9c102_write_reg(cam, h_start, 0x12);
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+ err += sn9c102_write_reg(cam, h_size, 0x15);
+ err += sn9c102_write_reg(cam, v_size, 0x16);
+ if (err)
+ return -EIO;
+
+ PDBGG("h_start, v_start, h_size, v_size, ho_size, vo_size "
+ "%u %u %u %u", h_start, v_start, h_size, v_size);
+
+ return 0;
+}
+
+
+static int sn9c102_init(struct sn9c102_device* cam)
+{
+ struct sn9c102_sensor* s = &cam->sensor;
+ struct v4l2_control ctrl;
+ struct v4l2_queryctrl *qctrl;
+ struct v4l2_rect* rect;
+ u8 i = 0;
+ int err = 0;
+
+ if (!(cam->state & DEV_INITIALIZED)) {
+ mutex_init(&cam->open_mutex);
+ init_waitqueue_head(&cam->wait_open);
+ qctrl = s->qctrl;
+ rect = &(s->cropcap.defrect);
+ } else { /* use current values */
+ qctrl = s->_qctrl;
+ rect = &(s->_rect);
+ }
+
+ err += sn9c102_set_scale(cam, rect->width / s->pix_format.width);
+ err += sn9c102_set_crop(cam, rect);
+ if (err)
+ return err;
+
+ if (s->init) {
+ err = s->init(cam);
+ if (err) {
+ DBG(3, "Sensor initialization failed");
+ return err;
+ }
+ }
+
+ if (!(cam->state & DEV_INITIALIZED))
+ if (cam->bridge == BRIDGE_SN9C101 ||
+ cam->bridge == BRIDGE_SN9C102 ||
+ cam->bridge == BRIDGE_SN9C103) {
+ if (s->pix_format.pixelformat == V4L2_PIX_FMT_JPEG)
+ s->pix_format.pixelformat= V4L2_PIX_FMT_SBGGR8;
+ cam->compression.quality = cam->reg[0x17] & 0x01 ?
+ 0 : 1;
+ } else {
+ if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X)
+ s->pix_format.pixelformat = V4L2_PIX_FMT_JPEG;
+ cam->compression.quality = cam->reg[0x18] & 0x40 ?
+ 0 : 1;
+ err += sn9c102_set_compression(cam, &cam->compression);
+ }
+ else
+ err += sn9c102_set_compression(cam, &cam->compression);
+ err += sn9c102_set_pix_format(cam, &s->pix_format);
+ if (s->set_pix_format)
+ err += s->set_pix_format(cam, &s->pix_format);
+ if (err)
+ return err;
+
+ if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X ||
+ s->pix_format.pixelformat == V4L2_PIX_FMT_JPEG)
+ DBG(3, "Compressed video format is active, quality %d",
+ cam->compression.quality);
+ else
+ DBG(3, "Uncompressed video format is active");
+
+ if (s->set_crop)
+ if ((err = s->set_crop(cam, rect))) {
+ DBG(3, "set_crop() failed");
+ return err;
+ }
+
+ if (s->set_ctrl) {
+ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
+ if (s->qctrl[i].id != 0 &&
+ !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) {
+ ctrl.id = s->qctrl[i].id;
+ ctrl.value = qctrl[i].default_value;
+ err = s->set_ctrl(cam, &ctrl);
+ if (err) {
+ DBG(3, "Set %s control failed",
+ s->qctrl[i].name);
+ return err;
+ }
+ DBG(3, "Image sensor supports '%s' control",
+ s->qctrl[i].name);
+ }
+ }
+
+ if (!(cam->state & DEV_INITIALIZED)) {
+ mutex_init(&cam->fileop_mutex);
+ spin_lock_init(&cam->queue_lock);
+ init_waitqueue_head(&cam->wait_frame);
+ init_waitqueue_head(&cam->wait_stream);
+ cam->nreadbuffers = 2;
+ memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl));
+ memcpy(&(s->_rect), &(s->cropcap.defrect),
+ sizeof(struct v4l2_rect));
+ cam->state |= DEV_INITIALIZED;
+ }
+
+ DBG(2, "Initialization succeeded");
+ return 0;
+}
+
+/*****************************************************************************/
+
+static void sn9c102_release_resources(struct kref *kref)
+{
+ struct sn9c102_device *cam;
+
+ mutex_lock(&sn9c102_sysfs_lock);
+
+ cam = container_of(kref, struct sn9c102_device, kref);
+
+ DBG(2, "V4L2 device %s deregistered",
+ video_device_node_name(cam->v4ldev));
+ video_set_drvdata(cam->v4ldev, NULL);
+ video_unregister_device(cam->v4ldev);
+ usb_put_dev(cam->usbdev);
+ kfree(cam->control_buffer);
+ kfree(cam);
+
+ mutex_unlock(&sn9c102_sysfs_lock);
+
+}
+
+
+static int sn9c102_open(struct file *filp)
+{
+ struct sn9c102_device* cam;
+ int err = 0;
+
+ /*
+ A read_trylock() in open() is the only safe way to prevent race
+ conditions with disconnect(), one close() and multiple (not
+ necessarily simultaneous) attempts to open(). For example, it
+ prevents from waiting for a second access, while the device
+ structure is being deallocated, after a possible disconnect() and
+ during a following close() holding the write lock: given that, after
+ this deallocation, no access will be possible anymore, using the
+ non-trylock version would have let open() gain the access to the
+ device structure improperly.
+ For this reason the lock must also not be per-device.
+ */
+ if (!down_read_trylock(&sn9c102_dev_lock))
+ return -ERESTARTSYS;
+
+ cam = video_drvdata(filp);
+
+ if (wait_for_completion_interruptible(&cam->probe)) {
+ up_read(&sn9c102_dev_lock);
+ return -ERESTARTSYS;
+ }
+
+ kref_get(&cam->kref);
+
+ /*
+ Make sure to isolate all the simultaneous opens.
+ */
+ if (mutex_lock_interruptible(&cam->open_mutex)) {
+ kref_put(&cam->kref, sn9c102_release_resources);
+ up_read(&sn9c102_dev_lock);
+ return -ERESTARTSYS;
+ }
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present");
+ err = -ENODEV;
+ goto out;
+ }
+
+ if (cam->users) {
+ DBG(2, "Device %s is already in use",
+ video_device_node_name(cam->v4ldev));
+ DBG(3, "Simultaneous opens are not supported");
+ /*
+ open() must follow the open flags and should block
+ eventually while the device is in use.
+ */
+ if ((filp->f_flags & O_NONBLOCK) ||
+ (filp->f_flags & O_NDELAY)) {
+ err = -EWOULDBLOCK;
+ goto out;
+ }
+ DBG(2, "A blocking open() has been requested. Wait for the "
+ "device to be released...");
+ up_read(&sn9c102_dev_lock);
+ /*
+ We will not release the "open_mutex" lock, so that only one
+ process can be in the wait queue below. This way the process
+ will be sleeping while holding the lock, without losing its
+ priority after any wake_up().
+ */
+ err = wait_event_interruptible_exclusive(cam->wait_open,
+ (cam->state & DEV_DISCONNECTED)
+ || !cam->users);
+ down_read(&sn9c102_dev_lock);
+ if (err)
+ goto out;
+ if (cam->state & DEV_DISCONNECTED) {
+ err = -ENODEV;
+ goto out;
+ }
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ err = sn9c102_init(cam);
+ if (err) {
+ DBG(1, "Initialization failed again. "
+ "I will retry on next open().");
+ goto out;
+ }
+ cam->state &= ~DEV_MISCONFIGURED;
+ }
+
+ if ((err = sn9c102_start_transfer(cam)))
+ goto out;
+
+ filp->private_data = cam;
+ cam->users++;
+ cam->io = IO_NONE;
+ cam->stream = STREAM_OFF;
+ cam->nbuffers = 0;
+ cam->frame_count = 0;
+ sn9c102_empty_framequeues(cam);
+
+ DBG(3, "Video device %s is open", video_device_node_name(cam->v4ldev));
+
+out:
+ mutex_unlock(&cam->open_mutex);
+ if (err)
+ kref_put(&cam->kref, sn9c102_release_resources);
+
+ up_read(&sn9c102_dev_lock);
+ return err;
+}
+
+
+static int sn9c102_release(struct file *filp)
+{
+ struct sn9c102_device* cam;
+
+ down_write(&sn9c102_dev_lock);
+
+ cam = video_drvdata(filp);
+
+ sn9c102_stop_transfer(cam);
+ sn9c102_release_buffers(cam);
+ cam->users--;
+ wake_up_interruptible_nr(&cam->wait_open, 1);
+
+ DBG(3, "Video device %s closed", video_device_node_name(cam->v4ldev));
+
+ kref_put(&cam->kref, sn9c102_release_resources);
+
+ up_write(&sn9c102_dev_lock);
+
+ return 0;
+}
+
+
+static ssize_t
+sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
+{
+ struct sn9c102_device *cam = video_drvdata(filp);
+ struct sn9c102_frame_t* f, * i;
+ unsigned long lock_flags;
+ long timeout;
+ int err = 0;
+
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
+ return -ERESTARTSYS;
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present");
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ DBG(1, "The camera is misconfigured. Close and open it "
+ "again.");
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+
+ if (cam->io == IO_MMAP) {
+ DBG(3, "Close and open the device again to choose "
+ "the read method");
+ mutex_unlock(&cam->fileop_mutex);
+ return -EBUSY;
+ }
+
+ if (cam->io == IO_NONE) {
+ if (!sn9c102_request_buffers(cam,cam->nreadbuffers, IO_READ)) {
+ DBG(1, "read() failed, not enough memory");
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENOMEM;
+ }
+ cam->io = IO_READ;
+ cam->stream = STREAM_ON;
+ }
+
+ if (list_empty(&cam->inqueue)) {
+ if (!list_empty(&cam->outqueue))
+ sn9c102_empty_framequeues(cam);
+ sn9c102_queue_unusedframes(cam);
+ }
+
+ if (!count) {
+ mutex_unlock(&cam->fileop_mutex);
+ return 0;
+ }
+
+ if (list_empty(&cam->outqueue)) {
+ if (filp->f_flags & O_NONBLOCK) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EAGAIN;
+ }
+ if (!cam->module_param.frame_timeout) {
+ err = wait_event_interruptible
+ ( cam->wait_frame,
+ (!list_empty(&cam->outqueue)) ||
+ (cam->state & DEV_DISCONNECTED) ||
+ (cam->state & DEV_MISCONFIGURED) );
+ if (err) {
+ mutex_unlock(&cam->fileop_mutex);
+ return err;
+ }
+ } else {
+ timeout = wait_event_interruptible_timeout
+ ( cam->wait_frame,
+ (!list_empty(&cam->outqueue)) ||
+ (cam->state & DEV_DISCONNECTED) ||
+ (cam->state & DEV_MISCONFIGURED),
+ msecs_to_jiffies(
+ cam->module_param.frame_timeout * 1000
+ )
+ );
+ if (timeout < 0) {
+ mutex_unlock(&cam->fileop_mutex);
+ return timeout;
+ } else if (timeout == 0 &&
+ !(cam->state & DEV_DISCONNECTED)) {
+ DBG(1, "Video frame timeout elapsed");
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+ }
+ if (cam->state & DEV_DISCONNECTED) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+ if (cam->state & DEV_MISCONFIGURED) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+ }
+
+ f = list_entry(cam->outqueue.prev, struct sn9c102_frame_t, frame);
+
+ if (count > f->buf.bytesused)
+ count = f->buf.bytesused;
+
+ if (copy_to_user(buf, f->bufmem, count)) {
+ err = -EFAULT;
+ goto exit;
+ }
+ *f_pos += count;
+
+exit:
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ list_for_each_entry(i, &cam->outqueue, frame)
+ i->state = F_UNUSED;
+ INIT_LIST_HEAD(&cam->outqueue);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+ sn9c102_queue_unusedframes(cam);
+
+ PDBGG("Frame #%lu, bytes read: %zu",
+ (unsigned long)f->buf.index, count);
+
+ mutex_unlock(&cam->fileop_mutex);
+
+ return count;
+}
+
+
+static unsigned int sn9c102_poll(struct file *filp, poll_table *wait)
+{
+ struct sn9c102_device *cam = video_drvdata(filp);
+ struct sn9c102_frame_t* f;
+ unsigned long lock_flags;
+ unsigned int mask = 0;
+
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
+ return POLLERR;
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present");
+ goto error;
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ DBG(1, "The camera is misconfigured. Close and open it "
+ "again.");
+ goto error;
+ }
+
+ if (cam->io == IO_NONE) {
+ if (!sn9c102_request_buffers(cam, cam->nreadbuffers,
+ IO_READ)) {
+ DBG(1, "poll() failed, not enough memory");
+ goto error;
+ }
+ cam->io = IO_READ;
+ cam->stream = STREAM_ON;
+ }
+
+ if (cam->io == IO_READ) {
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ list_for_each_entry(f, &cam->outqueue, frame)
+ f->state = F_UNUSED;
+ INIT_LIST_HEAD(&cam->outqueue);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+ sn9c102_queue_unusedframes(cam);
+ }
+
+ poll_wait(filp, &cam->wait_frame, wait);
+
+ if (!list_empty(&cam->outqueue))
+ mask |= POLLIN | POLLRDNORM;
+
+ mutex_unlock(&cam->fileop_mutex);
+
+ return mask;
+
+error:
+ mutex_unlock(&cam->fileop_mutex);
+ return POLLERR;
+}
+
+
+static void sn9c102_vm_open(struct vm_area_struct* vma)
+{
+ struct sn9c102_frame_t* f = vma->vm_private_data;
+ f->vma_use_count++;
+}
+
+
+static void sn9c102_vm_close(struct vm_area_struct* vma)
+{
+ /* NOTE: buffers are not freed here */
+ struct sn9c102_frame_t* f = vma->vm_private_data;
+ f->vma_use_count--;
+}
+
+
+static const struct vm_operations_struct sn9c102_vm_ops = {
+ .open = sn9c102_vm_open,
+ .close = sn9c102_vm_close,
+};
+
+
+static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma)
+{
+ struct sn9c102_device *cam = video_drvdata(filp);
+ unsigned long size = vma->vm_end - vma->vm_start,
+ start = vma->vm_start;
+ void *pos;
+ u32 i;
+
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
+ return -ERESTARTSYS;
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present");
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ DBG(1, "The camera is misconfigured. Close and open it "
+ "again.");
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+
+ if (!(vma->vm_flags & (VM_WRITE | VM_READ))) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EACCES;
+ }
+
+ if (cam->io != IO_MMAP ||
+ size != PAGE_ALIGN(cam->frame[0].buf.length)) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < cam->nbuffers; i++) {
+ if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff)
+ break;
+ }
+ if (i == cam->nbuffers) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EINVAL;
+ }
+
+ vma->vm_flags |= VM_IO;
+ vma->vm_flags |= VM_RESERVED;
+
+ pos = cam->frame[i].bufmem;
+ while (size > 0) { /* size is page-aligned */
+ if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -EAGAIN;
+ }
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ vma->vm_ops = &sn9c102_vm_ops;
+ vma->vm_private_data = &cam->frame[i];
+ sn9c102_vm_open(vma);
+
+ mutex_unlock(&cam->fileop_mutex);
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+static int
+sn9c102_vidioc_querycap(struct sn9c102_device* cam, void __user * arg)
+{
+ struct v4l2_capability cap = {
+ .driver = "sn9c102",
+ .version = LINUX_VERSION_CODE,
+ .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING,
+ };
+
+ strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card));
+ if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0)
+ strlcpy(cap.bus_info, dev_name(&cam->usbdev->dev),
+ sizeof(cap.bus_info));
+
+ if (copy_to_user(arg, &cap, sizeof(cap)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_enuminput(struct sn9c102_device* cam, void __user * arg)
+{
+ struct v4l2_input i;
+
+ if (copy_from_user(&i, arg, sizeof(i)))
+ return -EFAULT;
+
+ if (i.index)
+ return -EINVAL;
+
+ memset(&i, 0, sizeof(i));
+ strcpy(i.name, "Camera");
+ i.type = V4L2_INPUT_TYPE_CAMERA;
+ i.capabilities = V4L2_IN_CAP_STD;
+
+ if (copy_to_user(arg, &i, sizeof(i)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_g_input(struct sn9c102_device* cam, void __user * arg)
+{
+ int index = 0;
+
+ if (copy_to_user(arg, &index, sizeof(index)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_s_input(struct sn9c102_device* cam, void __user * arg)
+{
+ int index;
+
+ if (copy_from_user(&index, arg, sizeof(index)))
+ return -EFAULT;
+
+ if (index != 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_query_ctrl(struct sn9c102_device* cam, void __user * arg)
+{
+ struct sn9c102_sensor* s = &cam->sensor;
+ struct v4l2_queryctrl qc;
+ u8 i;
+
+ if (copy_from_user(&qc, arg, sizeof(qc)))
+ return -EFAULT;
+
+ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
+ if (qc.id && qc.id == s->qctrl[i].id) {
+ memcpy(&qc, &(s->qctrl[i]), sizeof(qc));
+ if (copy_to_user(arg, &qc, sizeof(qc)))
+ return -EFAULT;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+
+static int
+sn9c102_vidioc_g_ctrl(struct sn9c102_device* cam, void __user * arg)
+{
+ struct sn9c102_sensor* s = &cam->sensor;
+ struct v4l2_control ctrl;
+ int err = 0;
+ u8 i;
+
+ if (!s->get_ctrl && !s->set_ctrl)
+ return -EINVAL;
+
+ if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
+ return -EFAULT;
+
+ if (!s->get_ctrl) {
+ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
+ if (ctrl.id && ctrl.id == s->qctrl[i].id) {
+ ctrl.value = s->_qctrl[i].default_value;
+ goto exit;
+ }
+ return -EINVAL;
+ } else
+ err = s->get_ctrl(cam, &ctrl);
+
+exit:
+ if (copy_to_user(arg, &ctrl, sizeof(ctrl)))
+ return -EFAULT;
+
+ PDBGG("VIDIOC_G_CTRL: id %lu, value %lu",
+ (unsigned long)ctrl.id, (unsigned long)ctrl.value);
+
+ return err;
+}
+
+
+static int
+sn9c102_vidioc_s_ctrl(struct sn9c102_device* cam, void __user * arg)
+{
+ struct sn9c102_sensor* s = &cam->sensor;
+ struct v4l2_control ctrl;
+ u8 i;
+ int err = 0;
+
+ if (!s->set_ctrl)
+ return -EINVAL;
+
+ if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
+ return -EFAULT;
+
+ for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) {
+ if (ctrl.id == s->qctrl[i].id) {
+ if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)
+ return -EINVAL;
+ if (ctrl.value < s->qctrl[i].minimum ||
+ ctrl.value > s->qctrl[i].maximum)
+ return -ERANGE;
+ ctrl.value -= ctrl.value % s->qctrl[i].step;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(s->qctrl))
+ return -EINVAL;
+ if ((err = s->set_ctrl(cam, &ctrl)))
+ return err;
+
+ s->_qctrl[i].default_value = ctrl.value;
+
+ PDBGG("VIDIOC_S_CTRL: id %lu, value %lu",
+ (unsigned long)ctrl.id, (unsigned long)ctrl.value);
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_cropcap(struct sn9c102_device* cam, void __user * arg)
+{
+ struct v4l2_cropcap* cc = &(cam->sensor.cropcap);
+
+ cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cc->pixelaspect.numerator = 1;
+ cc->pixelaspect.denominator = 1;
+
+ if (copy_to_user(arg, cc, sizeof(*cc)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_g_crop(struct sn9c102_device* cam, void __user * arg)
+{
+ struct sn9c102_sensor* s = &cam->sensor;
+ struct v4l2_crop crop = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ };
+
+ memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect));
+
+ if (copy_to_user(arg, &crop, sizeof(crop)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_s_crop(struct sn9c102_device* cam, void __user * arg)
+{
+ struct sn9c102_sensor* s = &cam->sensor;
+ struct v4l2_crop crop;
+ struct v4l2_rect* rect;
+ struct v4l2_rect* bounds = &(s->cropcap.bounds);
+ struct v4l2_pix_format* pix_format = &(s->pix_format);
+ u8 scale;
+ const enum sn9c102_stream_state stream = cam->stream;
+ const u32 nbuffers = cam->nbuffers;
+ u32 i;
+ int err = 0;
+
+ if (copy_from_user(&crop, arg, sizeof(crop)))
+ return -EFAULT;
+
+ rect = &(crop.c);
+
+ if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (cam->module_param.force_munmap)
+ for (i = 0; i < cam->nbuffers; i++)
+ if (cam->frame[i].vma_use_count) {
+ DBG(3, "VIDIOC_S_CROP failed. "
+ "Unmap the buffers first.");
+ return -EBUSY;
+ }
+
+ /* Preserve R,G or B origin */
+ rect->left = (s->_rect.left & 1L) ? rect->left | 1L : rect->left & ~1L;
+ rect->top = (s->_rect.top & 1L) ? rect->top | 1L : rect->top & ~1L;
+
+ if (rect->width < 16)
+ rect->width = 16;
+ if (rect->height < 16)
+ rect->height = 16;
+ if (rect->width > bounds->width)
+ rect->width = bounds->width;
+ if (rect->height > bounds->height)
+ rect->height = bounds->height;
+ if (rect->left < bounds->left)
+ rect->left = bounds->left;
+ if (rect->top < bounds->top)
+ rect->top = bounds->top;
+ if (rect->left + rect->width > bounds->left + bounds->width)
+ rect->left = bounds->left+bounds->width - rect->width;
+ if (rect->top + rect->height > bounds->top + bounds->height)
+ rect->top = bounds->top+bounds->height - rect->height;
+
+ rect->width &= ~15L;
+ rect->height &= ~15L;
+
+ if (SN9C102_PRESERVE_IMGSCALE) {
+ /* Calculate the actual scaling factor */
+ u32 a, b;
+ a = rect->width * rect->height;
+ b = pix_format->width * pix_format->height;
+ scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1;
+ } else
+ scale = 1;
+
+ if (cam->stream == STREAM_ON)
+ if ((err = sn9c102_stream_interrupt(cam)))
+ return err;
+
+ if (copy_to_user(arg, &crop, sizeof(crop))) {
+ cam->stream = stream;
+ return -EFAULT;
+ }
+
+ if (cam->module_param.force_munmap || cam->io == IO_READ)
+ sn9c102_release_buffers(cam);
+
+ err = sn9c102_set_crop(cam, rect);
+ if (s->set_crop)
+ err += s->set_crop(cam, rect);
+ err += sn9c102_set_scale(cam, scale);
+
+ if (err) { /* atomic, no rollback in ioctl() */
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To "
+ "use the camera, close and open %s again.",
+ video_device_node_name(cam->v4ldev));
+ return -EIO;
+ }
+
+ s->pix_format.width = rect->width/scale;
+ s->pix_format.height = rect->height/scale;
+ memcpy(&(s->_rect), rect, sizeof(*rect));
+
+ if ((cam->module_param.force_munmap || cam->io == IO_READ) &&
+ nbuffers != sn9c102_request_buffers(cam, nbuffers, cam->io)) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To "
+ "use the camera, close and open %s again.",
+ video_device_node_name(cam->v4ldev));
+ return -ENOMEM;
+ }
+
+ if (cam->io == IO_READ)
+ sn9c102_empty_framequeues(cam);
+ else if (cam->module_param.force_munmap)
+ sn9c102_requeue_outqueue(cam);
+
+ cam->stream = stream;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_enum_framesizes(struct sn9c102_device* cam, void __user * arg)
+{
+ struct v4l2_frmsizeenum frmsize;
+
+ if (copy_from_user(&frmsize, arg, sizeof(frmsize)))
+ return -EFAULT;
+
+ if (frmsize.index != 0)
+ return -EINVAL;
+
+ switch (cam->bridge) {
+ case BRIDGE_SN9C101:
+ case BRIDGE_SN9C102:
+ case BRIDGE_SN9C103:
+ if (frmsize.pixel_format != V4L2_PIX_FMT_SN9C10X &&
+ frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8)
+ return -EINVAL;
+ case BRIDGE_SN9C105:
+ case BRIDGE_SN9C120:
+ if (frmsize.pixel_format != V4L2_PIX_FMT_JPEG &&
+ frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8)
+ return -EINVAL;
+ }
+
+ frmsize.type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ frmsize.stepwise.min_width = frmsize.stepwise.step_width = 16;
+ frmsize.stepwise.min_height = frmsize.stepwise.step_height = 16;
+ frmsize.stepwise.max_width = cam->sensor.cropcap.bounds.width;
+ frmsize.stepwise.max_height = cam->sensor.cropcap.bounds.height;
+ memset(&frmsize.reserved, 0, sizeof(frmsize.reserved));
+
+ if (copy_to_user(arg, &frmsize, sizeof(frmsize)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_enum_fmt(struct sn9c102_device* cam, void __user * arg)
+{
+ struct v4l2_fmtdesc fmtd;
+
+ if (copy_from_user(&fmtd, arg, sizeof(fmtd)))
+ return -EFAULT;
+
+ if (fmtd.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (fmtd.index == 0) {
+ strcpy(fmtd.description, "bayer rgb");
+ fmtd.pixelformat = V4L2_PIX_FMT_SBGGR8;
+ } else if (fmtd.index == 1) {
+ switch (cam->bridge) {
+ case BRIDGE_SN9C101:
+ case BRIDGE_SN9C102:
+ case BRIDGE_SN9C103:
+ strcpy(fmtd.description, "compressed");
+ fmtd.pixelformat = V4L2_PIX_FMT_SN9C10X;
+ break;
+ case BRIDGE_SN9C105:
+ case BRIDGE_SN9C120:
+ strcpy(fmtd.description, "JPEG");
+ fmtd.pixelformat = V4L2_PIX_FMT_JPEG;
+ break;
+ }
+ fmtd.flags = V4L2_FMT_FLAG_COMPRESSED;
+ } else
+ return -EINVAL;
+
+ fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ memset(&fmtd.reserved, 0, sizeof(fmtd.reserved));
+
+ if (copy_to_user(arg, &fmtd, sizeof(fmtd)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_g_fmt(struct sn9c102_device* cam, void __user * arg)
+{
+ struct v4l2_format format;
+ struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format);
+
+ if (copy_from_user(&format, arg, sizeof(format)))
+ return -EFAULT;
+
+ if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ pfmt->colorspace = (pfmt->pixelformat == V4L2_PIX_FMT_JPEG) ?
+ V4L2_COLORSPACE_JPEG : V4L2_COLORSPACE_SRGB;
+ pfmt->bytesperline = (pfmt->pixelformat == V4L2_PIX_FMT_SN9C10X ||
+ pfmt->pixelformat == V4L2_PIX_FMT_JPEG)
+ ? 0 : (pfmt->width * pfmt->priv) / 8;
+ pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8);
+ pfmt->field = V4L2_FIELD_NONE;
+ memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt));
+
+ if (copy_to_user(arg, &format, sizeof(format)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd,
+ void __user * arg)
+{
+ struct sn9c102_sensor* s = &cam->sensor;
+ struct v4l2_format format;
+ struct v4l2_pix_format* pix;
+ struct v4l2_pix_format* pfmt = &(s->pix_format);
+ struct v4l2_rect* bounds = &(s->cropcap.bounds);
+ struct v4l2_rect rect;
+ u8 scale;
+ const enum sn9c102_stream_state stream = cam->stream;
+ const u32 nbuffers = cam->nbuffers;
+ u32 i;
+ int err = 0;
+
+ if (copy_from_user(&format, arg, sizeof(format)))
+ return -EFAULT;
+
+ pix = &(format.fmt.pix);
+
+ if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ memcpy(&rect, &(s->_rect), sizeof(rect));
+
+ { /* calculate the actual scaling factor */
+ u32 a, b;
+ a = rect.width * rect.height;
+ b = pix->width * pix->height;
+ scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1;
+ }
+
+ rect.width = scale * pix->width;
+ rect.height = scale * pix->height;
+
+ if (rect.width < 16)
+ rect.width = 16;
+ if (rect.height < 16)
+ rect.height = 16;
+ if (rect.width > bounds->left + bounds->width - rect.left)
+ rect.width = bounds->left + bounds->width - rect.left;
+ if (rect.height > bounds->top + bounds->height - rect.top)
+ rect.height = bounds->top + bounds->height - rect.top;
+
+ rect.width &= ~15L;
+ rect.height &= ~15L;
+
+ { /* adjust the scaling factor */
+ u32 a, b;
+ a = rect.width * rect.height;
+ b = pix->width * pix->height;
+ scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1;
+ }
+
+ pix->width = rect.width / scale;
+ pix->height = rect.height / scale;
+
+ switch (cam->bridge) {
+ case BRIDGE_SN9C101:
+ case BRIDGE_SN9C102:
+ case BRIDGE_SN9C103:
+ if (pix->pixelformat != V4L2_PIX_FMT_SN9C10X &&
+ pix->pixelformat != V4L2_PIX_FMT_SBGGR8)
+ pix->pixelformat = pfmt->pixelformat;
+ break;
+ case BRIDGE_SN9C105:
+ case BRIDGE_SN9C120:
+ if (pix->pixelformat != V4L2_PIX_FMT_JPEG &&
+ pix->pixelformat != V4L2_PIX_FMT_SBGGR8)
+ pix->pixelformat = pfmt->pixelformat;
+ break;
+ }
+ pix->priv = pfmt->priv; /* bpp */
+ pix->colorspace = (pix->pixelformat == V4L2_PIX_FMT_JPEG) ?
+ V4L2_COLORSPACE_JPEG : V4L2_COLORSPACE_SRGB;
+ pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_SN9C10X ||
+ pix->pixelformat == V4L2_PIX_FMT_JPEG)
+ ? 0 : (pix->width * pix->priv) / 8;
+ pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8);
+ pix->field = V4L2_FIELD_NONE;
+
+ if (cmd == VIDIOC_TRY_FMT) {
+ if (copy_to_user(arg, &format, sizeof(format)))
+ return -EFAULT;
+ return 0;
+ }
+
+ if (cam->module_param.force_munmap)
+ for (i = 0; i < cam->nbuffers; i++)
+ if (cam->frame[i].vma_use_count) {
+ DBG(3, "VIDIOC_S_FMT failed. Unmap the "
+ "buffers first.");
+ return -EBUSY;
+ }
+
+ if (cam->stream == STREAM_ON)
+ if ((err = sn9c102_stream_interrupt(cam)))
+ return err;
+
+ if (copy_to_user(arg, &format, sizeof(format))) {
+ cam->stream = stream;
+ return -EFAULT;
+ }
+
+ if (cam->module_param.force_munmap || cam->io == IO_READ)
+ sn9c102_release_buffers(cam);
+
+ err += sn9c102_set_pix_format(cam, pix);
+ err += sn9c102_set_crop(cam, &rect);
+ if (s->set_pix_format)
+ err += s->set_pix_format(cam, pix);
+ if (s->set_crop)
+ err += s->set_crop(cam, &rect);
+ err += sn9c102_set_scale(cam, scale);
+
+ if (err) { /* atomic, no rollback in ioctl() */
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To "
+ "use the camera, close and open %s again.",
+ video_device_node_name(cam->v4ldev));
+ return -EIO;
+ }
+
+ memcpy(pfmt, pix, sizeof(*pix));
+ memcpy(&(s->_rect), &rect, sizeof(rect));
+
+ if ((cam->module_param.force_munmap || cam->io == IO_READ) &&
+ nbuffers != sn9c102_request_buffers(cam, nbuffers, cam->io)) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To "
+ "use the camera, close and open %s again.",
+ video_device_node_name(cam->v4ldev));
+ return -ENOMEM;
+ }
+
+ if (cam->io == IO_READ)
+ sn9c102_empty_framequeues(cam);
+ else if (cam->module_param.force_munmap)
+ sn9c102_requeue_outqueue(cam);
+
+ cam->stream = stream;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_g_jpegcomp(struct sn9c102_device* cam, void __user * arg)
+{
+ if (copy_to_user(arg, &cam->compression, sizeof(cam->compression)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_s_jpegcomp(struct sn9c102_device* cam, void __user * arg)
+{
+ struct v4l2_jpegcompression jc;
+ const enum sn9c102_stream_state stream = cam->stream;
+ int err = 0;
+
+ if (copy_from_user(&jc, arg, sizeof(jc)))
+ return -EFAULT;
+
+ if (jc.quality != 0 && jc.quality != 1)
+ return -EINVAL;
+
+ if (cam->stream == STREAM_ON)
+ if ((err = sn9c102_stream_interrupt(cam)))
+ return err;
+
+ err += sn9c102_set_compression(cam, &jc);
+ if (err) { /* atomic, no rollback in ioctl() */
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware problems. "
+ "To use the camera, close and open %s again.",
+ video_device_node_name(cam->v4ldev));
+ return -EIO;
+ }
+
+ cam->compression.quality = jc.quality;
+
+ cam->stream = stream;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_reqbufs(struct sn9c102_device* cam, void __user * arg)
+{
+ struct v4l2_requestbuffers rb;
+ u32 i;
+ int err;
+
+ if (copy_from_user(&rb, arg, sizeof(rb)))
+ return -EFAULT;
+
+ if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ rb.memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+ if (cam->io == IO_READ) {
+ DBG(3, "Close and open the device again to choose the mmap "
+ "I/O method");
+ return -EBUSY;
+ }
+
+ for (i = 0; i < cam->nbuffers; i++)
+ if (cam->frame[i].vma_use_count) {
+ DBG(3, "VIDIOC_REQBUFS failed. Previous buffers are "
+ "still mapped.");
+ return -EBUSY;
+ }
+
+ if (cam->stream == STREAM_ON)
+ if ((err = sn9c102_stream_interrupt(cam)))
+ return err;
+
+ sn9c102_empty_framequeues(cam);
+
+ sn9c102_release_buffers(cam);
+ if (rb.count)
+ rb.count = sn9c102_request_buffers(cam, rb.count, IO_MMAP);
+
+ if (copy_to_user(arg, &rb, sizeof(rb))) {
+ sn9c102_release_buffers(cam);
+ cam->io = IO_NONE;
+ return -EFAULT;
+ }
+
+ cam->io = rb.count ? IO_MMAP : IO_NONE;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_querybuf(struct sn9c102_device* cam, void __user * arg)
+{
+ struct v4l2_buffer b;
+
+ if (copy_from_user(&b, arg, sizeof(b)))
+ return -EFAULT;
+
+ if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ b.index >= cam->nbuffers || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ memcpy(&b, &cam->frame[b.index].buf, sizeof(b));
+
+ if (cam->frame[b.index].vma_use_count)
+ b.flags |= V4L2_BUF_FLAG_MAPPED;
+
+ if (cam->frame[b.index].state == F_DONE)
+ b.flags |= V4L2_BUF_FLAG_DONE;
+ else if (cam->frame[b.index].state != F_UNUSED)
+ b.flags |= V4L2_BUF_FLAG_QUEUED;
+
+ if (copy_to_user(arg, &b, sizeof(b)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_qbuf(struct sn9c102_device* cam, void __user * arg)
+{
+ struct v4l2_buffer b;
+ unsigned long lock_flags;
+
+ if (copy_from_user(&b, arg, sizeof(b)))
+ return -EFAULT;
+
+ if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ b.index >= cam->nbuffers || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ if (cam->frame[b.index].state != F_UNUSED)
+ return -EINVAL;
+
+ cam->frame[b.index].state = F_QUEUED;
+
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ list_add_tail(&cam->frame[b.index].frame, &cam->inqueue);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+ PDBGG("Frame #%lu queued", (unsigned long)b.index);
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp,
+ void __user * arg)
+{
+ struct v4l2_buffer b;
+ struct sn9c102_frame_t *f;
+ unsigned long lock_flags;
+ long timeout;
+ int err = 0;
+
+ if (copy_from_user(&b, arg, sizeof(b)))
+ return -EFAULT;
+
+ if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ if (list_empty(&cam->outqueue)) {
+ if (cam->stream == STREAM_OFF)
+ return -EINVAL;
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ if (!cam->module_param.frame_timeout) {
+ err = wait_event_interruptible
+ ( cam->wait_frame,
+ (!list_empty(&cam->outqueue)) ||
+ (cam->state & DEV_DISCONNECTED) ||
+ (cam->state & DEV_MISCONFIGURED) );
+ if (err)
+ return err;
+ } else {
+ timeout = wait_event_interruptible_timeout
+ ( cam->wait_frame,
+ (!list_empty(&cam->outqueue)) ||
+ (cam->state & DEV_DISCONNECTED) ||
+ (cam->state & DEV_MISCONFIGURED),
+ cam->module_param.frame_timeout *
+ 1000 * msecs_to_jiffies(1) );
+ if (timeout < 0)
+ return timeout;
+ else if (timeout == 0 &&
+ !(cam->state & DEV_DISCONNECTED)) {
+ DBG(1, "Video frame timeout elapsed");
+ return -EIO;
+ }
+ }
+ if (cam->state & DEV_DISCONNECTED)
+ return -ENODEV;
+ if (cam->state & DEV_MISCONFIGURED)
+ return -EIO;
+ }
+
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ f = list_entry(cam->outqueue.next, struct sn9c102_frame_t, frame);
+ list_del(cam->outqueue.next);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+ f->state = F_UNUSED;
+
+ memcpy(&b, &f->buf, sizeof(b));
+ if (f->vma_use_count)
+ b.flags |= V4L2_BUF_FLAG_MAPPED;
+
+ if (copy_to_user(arg, &b, sizeof(b)))
+ return -EFAULT;
+
+ PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index);
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_streamon(struct sn9c102_device* cam, void __user * arg)
+{
+ int type;
+
+ if (copy_from_user(&type, arg, sizeof(type)))
+ return -EFAULT;
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ cam->stream = STREAM_ON;
+
+ DBG(3, "Stream on");
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_streamoff(struct sn9c102_device* cam, void __user * arg)
+{
+ int type, err;
+
+ if (copy_from_user(&type, arg, sizeof(type)))
+ return -EFAULT;
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ if (cam->stream == STREAM_ON)
+ if ((err = sn9c102_stream_interrupt(cam)))
+ return err;
+
+ sn9c102_empty_framequeues(cam);
+
+ DBG(3, "Stream off");
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_g_parm(struct sn9c102_device* cam, void __user * arg)
+{
+ struct v4l2_streamparm sp;
+
+ if (copy_from_user(&sp, arg, sizeof(sp)))
+ return -EFAULT;
+
+ if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ sp.parm.capture.extendedmode = 0;
+ sp.parm.capture.readbuffers = cam->nreadbuffers;
+
+ if (copy_to_user(arg, &sp, sizeof(sp)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_s_parm(struct sn9c102_device* cam, void __user * arg)
+{
+ struct v4l2_streamparm sp;
+
+ if (copy_from_user(&sp, arg, sizeof(sp)))
+ return -EFAULT;
+
+ if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ sp.parm.capture.extendedmode = 0;
+
+ if (sp.parm.capture.readbuffers == 0)
+ sp.parm.capture.readbuffers = cam->nreadbuffers;
+
+ if (sp.parm.capture.readbuffers > SN9C102_MAX_FRAMES)
+ sp.parm.capture.readbuffers = SN9C102_MAX_FRAMES;
+
+ if (copy_to_user(arg, &sp, sizeof(sp)))
+ return -EFAULT;
+
+ cam->nreadbuffers = sp.parm.capture.readbuffers;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_enumaudio(struct sn9c102_device* cam, void __user * arg)
+{
+ struct v4l2_audio audio;
+
+ if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102)
+ return -EINVAL;
+
+ if (copy_from_user(&audio, arg, sizeof(audio)))
+ return -EFAULT;
+
+ if (audio.index != 0)
+ return -EINVAL;
+
+ strcpy(audio.name, "Microphone");
+ audio.capability = 0;
+ audio.mode = 0;
+
+ if (copy_to_user(arg, &audio, sizeof(audio)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_g_audio(struct sn9c102_device* cam, void __user * arg)
+{
+ struct v4l2_audio audio;
+
+ if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102)
+ return -EINVAL;
+
+ if (copy_from_user(&audio, arg, sizeof(audio)))
+ return -EFAULT;
+
+ memset(&audio, 0, sizeof(audio));
+ strcpy(audio.name, "Microphone");
+
+ if (copy_to_user(arg, &audio, sizeof(audio)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int
+sn9c102_vidioc_s_audio(struct sn9c102_device* cam, void __user * arg)
+{
+ struct v4l2_audio audio;
+
+ if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102)
+ return -EINVAL;
+
+ if (copy_from_user(&audio, arg, sizeof(audio)))
+ return -EFAULT;
+
+ if (audio.index != 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+
+static long sn9c102_ioctl_v4l2(struct file *filp,
+ unsigned int cmd, void __user *arg)
+{
+ struct sn9c102_device *cam = video_drvdata(filp);
+
+ switch (cmd) {
+
+ case VIDIOC_QUERYCAP:
+ return sn9c102_vidioc_querycap(cam, arg);
+
+ case VIDIOC_ENUMINPUT:
+ return sn9c102_vidioc_enuminput(cam, arg);
+
+ case VIDIOC_G_INPUT:
+ return sn9c102_vidioc_g_input(cam, arg);
+
+ case VIDIOC_S_INPUT:
+ return sn9c102_vidioc_s_input(cam, arg);
+
+ case VIDIOC_QUERYCTRL:
+ return sn9c102_vidioc_query_ctrl(cam, arg);
+
+ case VIDIOC_G_CTRL:
+ return sn9c102_vidioc_g_ctrl(cam, arg);
+
+ case VIDIOC_S_CTRL:
+ return sn9c102_vidioc_s_ctrl(cam, arg);
+
+ case VIDIOC_CROPCAP:
+ return sn9c102_vidioc_cropcap(cam, arg);
+
+ case VIDIOC_G_CROP:
+ return sn9c102_vidioc_g_crop(cam, arg);
+
+ case VIDIOC_S_CROP:
+ return sn9c102_vidioc_s_crop(cam, arg);
+
+ case VIDIOC_ENUM_FRAMESIZES:
+ return sn9c102_vidioc_enum_framesizes(cam, arg);
+
+ case VIDIOC_ENUM_FMT:
+ return sn9c102_vidioc_enum_fmt(cam, arg);
+
+ case VIDIOC_G_FMT:
+ return sn9c102_vidioc_g_fmt(cam, arg);
+
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_S_FMT:
+ return sn9c102_vidioc_try_s_fmt(cam, cmd, arg);
+
+ case VIDIOC_G_JPEGCOMP:
+ return sn9c102_vidioc_g_jpegcomp(cam, arg);
+
+ case VIDIOC_S_JPEGCOMP:
+ return sn9c102_vidioc_s_jpegcomp(cam, arg);
+
+ case VIDIOC_REQBUFS:
+ return sn9c102_vidioc_reqbufs(cam, arg);
+
+ case VIDIOC_QUERYBUF:
+ return sn9c102_vidioc_querybuf(cam, arg);
+
+ case VIDIOC_QBUF:
+ return sn9c102_vidioc_qbuf(cam, arg);
+
+ case VIDIOC_DQBUF:
+ return sn9c102_vidioc_dqbuf(cam, filp, arg);
+
+ case VIDIOC_STREAMON:
+ return sn9c102_vidioc_streamon(cam, arg);
+
+ case VIDIOC_STREAMOFF:
+ return sn9c102_vidioc_streamoff(cam, arg);
+
+ case VIDIOC_G_PARM:
+ return sn9c102_vidioc_g_parm(cam, arg);
+
+ case VIDIOC_S_PARM:
+ return sn9c102_vidioc_s_parm(cam, arg);
+
+ case VIDIOC_ENUMAUDIO:
+ return sn9c102_vidioc_enumaudio(cam, arg);
+
+ case VIDIOC_G_AUDIO:
+ return sn9c102_vidioc_g_audio(cam, arg);
+
+ case VIDIOC_S_AUDIO:
+ return sn9c102_vidioc_s_audio(cam, arg);
+
+ default:
+ return -ENOTTY;
+
+ }
+}
+
+
+static long sn9c102_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct sn9c102_device *cam = video_drvdata(filp);
+ int err = 0;
+
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
+ return -ERESTARTSYS;
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present");
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ DBG(1, "The camera is misconfigured. Close and open it "
+ "again.");
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+
+ V4LDBG(3, "sn9c102", cmd);
+
+ err = sn9c102_ioctl_v4l2(filp, cmd, (void __user *)arg);
+
+ mutex_unlock(&cam->fileop_mutex);
+
+ return err;
+}
+
+/*****************************************************************************/
+
+static const struct v4l2_file_operations sn9c102_fops = {
+ .owner = THIS_MODULE,
+ .open = sn9c102_open,
+ .release = sn9c102_release,
+ .unlocked_ioctl = sn9c102_ioctl,
+ .read = sn9c102_read,
+ .poll = sn9c102_poll,
+ .mmap = sn9c102_mmap,
+};
+
+/*****************************************************************************/
+
+/* It exists a single interface only. We do not need to validate anything. */
+static int
+sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct sn9c102_device* cam;
+ static unsigned int dev_nr;
+ unsigned int i;
+ int err = 0, r;
+
+ if (!(cam = kzalloc(sizeof(struct sn9c102_device), GFP_KERNEL)))
+ return -ENOMEM;
+
+ cam->usbdev = udev;
+
+ if (!(cam->control_buffer = kzalloc(8, GFP_KERNEL))) {
+ DBG(1, "kzalloc() failed");
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ if (!(cam->v4ldev = video_device_alloc())) {
+ DBG(1, "video_device_alloc() failed");
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ r = sn9c102_read_reg(cam, 0x00);
+ if (r < 0 || (r != 0x10 && r != 0x11 && r != 0x12)) {
+ DBG(1, "Sorry, this is not a SN9C1xx-based camera "
+ "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
+ err = -ENODEV;
+ goto fail;
+ }
+
+ cam->bridge = id->driver_info;
+ switch (cam->bridge) {
+ case BRIDGE_SN9C101:
+ case BRIDGE_SN9C102:
+ DBG(2, "SN9C10[12] PC Camera Controller detected "
+ "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
+ break;
+ case BRIDGE_SN9C103:
+ DBG(2, "SN9C103 PC Camera Controller detected "
+ "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
+ break;
+ case BRIDGE_SN9C105:
+ DBG(2, "SN9C105 PC Camera Controller detected "
+ "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
+ break;
+ case BRIDGE_SN9C120:
+ DBG(2, "SN9C120 PC Camera Controller detected "
+ "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
+ break;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sn9c102_sensor_table); i++) {
+ err = sn9c102_sensor_table[i](cam);
+ if (!err)
+ break;
+ }
+
+ if (!err) {
+ DBG(2, "%s image sensor detected", cam->sensor.name);
+ DBG(3, "Support for %s maintained by %s",
+ cam->sensor.name, cam->sensor.maintainer);
+ } else {
+ DBG(1, "No supported image sensor detected for this bridge");
+ err = -ENODEV;
+ goto fail;
+ }
+
+ if (!(cam->bridge & cam->sensor.supported_bridge)) {
+ DBG(1, "Bridge not supported");
+ err = -ENODEV;
+ goto fail;
+ }
+
+ if (sn9c102_init(cam)) {
+ DBG(1, "Initialization failed. I will retry on open().");
+ cam->state |= DEV_MISCONFIGURED;
+ }
+
+ strcpy(cam->v4ldev->name, "SN9C1xx PC Camera");
+ cam->v4ldev->fops = &sn9c102_fops;
+ cam->v4ldev->release = video_device_release;
+ cam->v4ldev->parent = &udev->dev;
+
+ init_completion(&cam->probe);
+
+ err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER,
+ video_nr[dev_nr]);
+ if (err) {
+ DBG(1, "V4L2 device registration failed");
+ if (err == -ENFILE && video_nr[dev_nr] == -1)
+ DBG(1, "Free /dev/videoX node not found");
+ video_nr[dev_nr] = -1;
+ dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0;
+ complete_all(&cam->probe);
+ goto fail;
+ }
+
+ DBG(2, "V4L2 device registered as %s",
+ video_device_node_name(cam->v4ldev));
+
+ video_set_drvdata(cam->v4ldev, cam);
+ cam->module_param.force_munmap = force_munmap[dev_nr];
+ cam->module_param.frame_timeout = frame_timeout[dev_nr];
+
+ dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0;
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ err = sn9c102_create_sysfs(cam);
+ if (!err)
+ DBG(2, "Optional device control through 'sysfs' "
+ "interface ready");
+ else
+ DBG(2, "Failed to create optional 'sysfs' interface for "
+ "device controlling. Error #%d", err);
+#else
+ DBG(2, "Optional device control through 'sysfs' interface disabled");
+ DBG(3, "Compile the kernel with the 'CONFIG_VIDEO_ADV_DEBUG' "
+ "configuration option to enable it.");
+#endif
+
+ usb_set_intfdata(intf, cam);
+ kref_init(&cam->kref);
+ usb_get_dev(cam->usbdev);
+
+ complete_all(&cam->probe);
+
+ return 0;
+
+fail:
+ if (cam) {
+ kfree(cam->control_buffer);
+ if (cam->v4ldev)
+ video_device_release(cam->v4ldev);
+ kfree(cam);
+ }
+ return err;
+}
+
+
+static void sn9c102_usb_disconnect(struct usb_interface* intf)
+{
+ struct sn9c102_device* cam;
+
+ down_write(&sn9c102_dev_lock);
+
+ cam = usb_get_intfdata(intf);
+
+ DBG(2, "Disconnecting %s...", cam->v4ldev->name);
+
+ if (cam->users) {
+ DBG(2, "Device %s is open! Deregistration and memory "
+ "deallocation are deferred.",
+ video_device_node_name(cam->v4ldev));
+ cam->state |= DEV_MISCONFIGURED;
+ sn9c102_stop_transfer(cam);
+ cam->state |= DEV_DISCONNECTED;
+ wake_up_interruptible(&cam->wait_frame);
+ wake_up(&cam->wait_stream);
+ } else
+ cam->state |= DEV_DISCONNECTED;
+
+ wake_up_interruptible_all(&cam->wait_open);
+
+ kref_put(&cam->kref, sn9c102_release_resources);
+
+ up_write(&sn9c102_dev_lock);
+}
+
+
+static struct usb_driver sn9c102_usb_driver = {
+ .name = "sn9c102",
+ .id_table = sn9c102_id_table,
+ .probe = sn9c102_usb_probe,
+ .disconnect = sn9c102_usb_disconnect,
+};
+
+module_usb_driver(sn9c102_usb_driver);
diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h
new file mode 100644
index 00000000..b3d2cc72
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_devtable.h
@@ -0,0 +1,147 @@
+/***************************************************************************
+ * Table of device identifiers of the SN9C1xx PC Camera Controllers *
+ * *
+ * Copyright (C) 2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#ifndef _SN9C102_DEVTABLE_H_
+#define _SN9C102_DEVTABLE_H_
+
+#include <linux/usb.h>
+
+struct sn9c102_device;
+
+/*
+ Each SN9C1xx camera has proper PID/VID identifiers.
+ SN9C103, SN9C105, SN9C120 support multiple interfaces, but we only have to
+ handle the video class interface.
+*/
+#define SN9C102_USB_DEVICE(vend, prod, bridge) \
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
+ USB_DEVICE_ID_MATCH_INT_CLASS, \
+ .idVendor = (vend), \
+ .idProduct = (prod), \
+ .bInterfaceClass = 0xff, \
+ .driver_info = (bridge)
+
+static const struct usb_device_id sn9c102_id_table[] = {
+ /* SN9C101 and SN9C102 */
+#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE
+ { SN9C102_USB_DEVICE(0x0c45, 0x6001, BRIDGE_SN9C102), },
+ { SN9C102_USB_DEVICE(0x0c45, 0x6005, BRIDGE_SN9C102), },
+ { SN9C102_USB_DEVICE(0x0c45, 0x6007, BRIDGE_SN9C102), },
+ { SN9C102_USB_DEVICE(0x0c45, 0x6009, BRIDGE_SN9C102), },
+ { SN9C102_USB_DEVICE(0x0c45, 0x600d, BRIDGE_SN9C102), },
+/* { SN9C102_USB_DEVICE(0x0c45, 0x6011, BRIDGE_SN9C102), }, OV6650 */
+ { SN9C102_USB_DEVICE(0x0c45, 0x6019, BRIDGE_SN9C102), },
+#endif
+ { SN9C102_USB_DEVICE(0x0c45, 0x6024, BRIDGE_SN9C102), },
+ { SN9C102_USB_DEVICE(0x0c45, 0x6025, BRIDGE_SN9C102), },
+#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE
+ { SN9C102_USB_DEVICE(0x0c45, 0x6028, BRIDGE_SN9C102), },
+ { SN9C102_USB_DEVICE(0x0c45, 0x6029, BRIDGE_SN9C102), },
+ { SN9C102_USB_DEVICE(0x0c45, 0x602a, BRIDGE_SN9C102), },
+#endif
+ { SN9C102_USB_DEVICE(0x0c45, 0x602b, BRIDGE_SN9C102), }, /* not in sonixb */
+#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE
+ { SN9C102_USB_DEVICE(0x0c45, 0x602c, BRIDGE_SN9C102), },
+/* { SN9C102_USB_DEVICE(0x0c45, 0x602d, BRIDGE_SN9C102), }, HV7131R */
+ { SN9C102_USB_DEVICE(0x0c45, 0x602e, BRIDGE_SN9C102), },
+#endif
+ { SN9C102_USB_DEVICE(0x0c45, 0x6030, BRIDGE_SN9C102), }, /* not in sonixb */
+ /* SN9C103 */
+/* { SN9C102_USB_DEVICE(0x0c45, 0x6080, BRIDGE_SN9C103), }, non existent ? */
+ { SN9C102_USB_DEVICE(0x0c45, 0x6082, BRIDGE_SN9C103), }, /* not in sonixb */
+#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE
+/* { SN9C102_USB_DEVICE(0x0c45, 0x6083, BRIDGE_SN9C103), }, HY7131D/E */
+/* { SN9C102_USB_DEVICE(0x0c45, 0x6088, BRIDGE_SN9C103), }, non existent ? */
+/* { SN9C102_USB_DEVICE(0x0c45, 0x608a, BRIDGE_SN9C103), }, non existent ? */
+/* { SN9C102_USB_DEVICE(0x0c45, 0x608b, BRIDGE_SN9C103), }, non existent ? */
+ { SN9C102_USB_DEVICE(0x0c45, 0x608c, BRIDGE_SN9C103), },
+/* { SN9C102_USB_DEVICE(0x0c45, 0x608e, BRIDGE_SN9C103), }, CISVF10 */
+ { SN9C102_USB_DEVICE(0x0c45, 0x608f, BRIDGE_SN9C103), },
+/* { SN9C102_USB_DEVICE(0x0c45, 0x60a0, BRIDGE_SN9C103), }, non existent ? */
+/* { SN9C102_USB_DEVICE(0x0c45, 0x60a2, BRIDGE_SN9C103), }, non existent ? */
+/* { SN9C102_USB_DEVICE(0x0c45, 0x60a3, BRIDGE_SN9C103), }, non existent ? */
+/* { SN9C102_USB_DEVICE(0x0c45, 0x60a8, BRIDGE_SN9C103), }, PAS106 */
+/* { SN9C102_USB_DEVICE(0x0c45, 0x60aa, BRIDGE_SN9C103), }, TAS5130 */
+/* { SN9C102_USB_DEVICE(0x0c45, 0x60ab, BRIDGE_SN9C103), }, TAS5110, non existent */
+/* { SN9C102_USB_DEVICE(0x0c45, 0x60ac, BRIDGE_SN9C103), }, non existent ? */
+/* { SN9C102_USB_DEVICE(0x0c45, 0x60ae, BRIDGE_SN9C103), }, non existent ? */
+ { SN9C102_USB_DEVICE(0x0c45, 0x60af, BRIDGE_SN9C103), },
+ { SN9C102_USB_DEVICE(0x0c45, 0x60b0, BRIDGE_SN9C103), },
+/* { SN9C102_USB_DEVICE(0x0c45, 0x60b2, BRIDGE_SN9C103), }, non existent ? */
+/* { SN9C102_USB_DEVICE(0x0c45, 0x60b3, BRIDGE_SN9C103), }, non existent ? */
+/* { SN9C102_USB_DEVICE(0x0c45, 0x60b8, BRIDGE_SN9C103), }, non existent ? */
+/* { SN9C102_USB_DEVICE(0x0c45, 0x60ba, BRIDGE_SN9C103), }, non existent ? */
+/* { SN9C102_USB_DEVICE(0x0c45, 0x60bb, BRIDGE_SN9C103), }, non existent ? */
+/* { SN9C102_USB_DEVICE(0x0c45, 0x60bc, BRIDGE_SN9C103), }, non existent ? */
+/* { SN9C102_USB_DEVICE(0x0c45, 0x60be, BRIDGE_SN9C103), }, non existent ? */
+#endif
+ /* SN9C105 */
+#if !defined CONFIG_USB_GSPCA_SONIXJ && !defined CONFIG_USB_GSPCA_SONIXJ_MODULE
+ { SN9C102_USB_DEVICE(0x045e, 0x00f5, BRIDGE_SN9C105), },
+ { SN9C102_USB_DEVICE(0x045e, 0x00f7, BRIDGE_SN9C105), },
+ { SN9C102_USB_DEVICE(0x0471, 0x0327, BRIDGE_SN9C105), },
+ { SN9C102_USB_DEVICE(0x0471, 0x0328, BRIDGE_SN9C105), },
+ { SN9C102_USB_DEVICE(0x0c45, 0x60c0, BRIDGE_SN9C105), },
+/* { SN9C102_USB_DEVICE(0x0c45, 0x60c2, BRIDGE_SN9C105), }, PO1030 */
+/* { SN9C102_USB_DEVICE(0x0c45, 0x60c8, BRIDGE_SN9C105), }, OM6801 */
+/* { SN9C102_USB_DEVICE(0x0c45, 0x60cc, BRIDGE_SN9C105), }, HV7131GP */
+/* { SN9C102_USB_DEVICE(0x0c45, 0x60ea, BRIDGE_SN9C105), }, non existent ? */
+/* { SN9C102_USB_DEVICE(0x0c45, 0x60ec, BRIDGE_SN9C105), }, MO4000 */
+/* { SN9C102_USB_DEVICE(0x0c45, 0x60ef, BRIDGE_SN9C105), }, ICM105C */
+/* { SN9C102_USB_DEVICE(0x0c45, 0x60fa, BRIDGE_SN9C105), }, OV7648 */
+ { SN9C102_USB_DEVICE(0x0c45, 0x60fb, BRIDGE_SN9C105), },
+ { SN9C102_USB_DEVICE(0x0c45, 0x60fc, BRIDGE_SN9C105), },
+ { SN9C102_USB_DEVICE(0x0c45, 0x60fe, BRIDGE_SN9C105), },
+ /* SN9C120 */
+ { SN9C102_USB_DEVICE(0x0458, 0x7025, BRIDGE_SN9C120), },
+/* { SN9C102_USB_DEVICE(0x0c45, 0x6102, BRIDGE_SN9C120), }, po2030 */
+/* { SN9C102_USB_DEVICE(0x0c45, 0x6108, BRIDGE_SN9C120), }, om6801 */
+/* { SN9C102_USB_DEVICE(0x0c45, 0x610f, BRIDGE_SN9C120), }, S5K53BEB */
+ { SN9C102_USB_DEVICE(0x0c45, 0x6130, BRIDGE_SN9C120), },
+/* { SN9C102_USB_DEVICE(0x0c45, 0x6138, BRIDGE_SN9C120), }, MO8000 */
+ { SN9C102_USB_DEVICE(0x0c45, 0x613a, BRIDGE_SN9C120), },
+ { SN9C102_USB_DEVICE(0x0c45, 0x613b, BRIDGE_SN9C120), },
+ { SN9C102_USB_DEVICE(0x0c45, 0x613c, BRIDGE_SN9C120), },
+ { SN9C102_USB_DEVICE(0x0c45, 0x613e, BRIDGE_SN9C120), },
+#endif
+ { }
+};
+
+/*
+ Probing functions: on success, you must attach the sensor to the camera
+ by calling sn9c102_attach_sensor().
+ To enable the I2C communication, you might need to perform a really basic
+ initialization of the SN9C1XX chip.
+ Functions must return 0 on success, the appropriate error otherwise.
+*/
+extern int sn9c102_probe_hv7131d(struct sn9c102_device* cam);
+extern int sn9c102_probe_hv7131r(struct sn9c102_device* cam);
+extern int sn9c102_probe_mi0343(struct sn9c102_device* cam);
+extern int sn9c102_probe_mi0360(struct sn9c102_device* cam);
+extern int sn9c102_probe_mt9v111(struct sn9c102_device *cam);
+extern int sn9c102_probe_ov7630(struct sn9c102_device* cam);
+extern int sn9c102_probe_ov7660(struct sn9c102_device* cam);
+extern int sn9c102_probe_pas106b(struct sn9c102_device* cam);
+extern int sn9c102_probe_pas202bcb(struct sn9c102_device* cam);
+extern int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam);
+extern int sn9c102_probe_tas5110d(struct sn9c102_device* cam);
+extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam);
+
+#endif /* _SN9C102_DEVTABLE_H_ */
diff --git a/drivers/media/video/sn9c102/sn9c102_hv7131d.c b/drivers/media/video/sn9c102/sn9c102_hv7131d.c
new file mode 100644
index 00000000..2dce5c90
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_hv7131d.c
@@ -0,0 +1,264 @@
+/***************************************************************************
+ * Plug-in for HV7131D image sensor connected to the SN9C1xx PC Camera *
+ * Controllers *
+ * *
+ * Copyright (C) 2004-2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include "sn9c102_sensor.h"
+#include "sn9c102_devtable.h"
+
+
+static int hv7131d_init(struct sn9c102_device* cam)
+{
+ int err;
+
+ err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11},
+ {0x00, 0x14}, {0x60, 0x17},
+ {0x0e, 0x18}, {0xf2, 0x19});
+
+ err += sn9c102_i2c_write(cam, 0x01, 0x04);
+ err += sn9c102_i2c_write(cam, 0x02, 0x00);
+ err += sn9c102_i2c_write(cam, 0x28, 0x00);
+
+ return err;
+}
+
+
+static int hv7131d_get_ctrl(struct sn9c102_device* cam,
+ struct v4l2_control* ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ {
+ int r1 = sn9c102_i2c_read(cam, 0x26),
+ r2 = sn9c102_i2c_read(cam, 0x27);
+ if (r1 < 0 || r2 < 0)
+ return -EIO;
+ ctrl->value = (r1 << 8) | (r2 & 0xff);
+ }
+ return 0;
+ case V4L2_CID_RED_BALANCE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x31)) < 0)
+ return -EIO;
+ ctrl->value = 0x3f - (ctrl->value & 0x3f);
+ return 0;
+ case V4L2_CID_BLUE_BALANCE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x33)) < 0)
+ return -EIO;
+ ctrl->value = 0x3f - (ctrl->value & 0x3f);
+ return 0;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x32)) < 0)
+ return -EIO;
+ ctrl->value = 0x3f - (ctrl->value & 0x3f);
+ return 0;
+ case SN9C102_V4L2_CID_RESET_LEVEL:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x30)) < 0)
+ return -EIO;
+ ctrl->value &= 0x3f;
+ return 0;
+ case SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x34)) < 0)
+ return -EIO;
+ ctrl->value &= 0x07;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+
+static int hv7131d_set_ctrl(struct sn9c102_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ err += sn9c102_i2c_write(cam, 0x26, ctrl->value >> 8);
+ err += sn9c102_i2c_write(cam, 0x27, ctrl->value & 0xff);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x31, 0x3f - ctrl->value);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x33, 0x3f - ctrl->value);
+ break;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x32, 0x3f - ctrl->value);
+ break;
+ case SN9C102_V4L2_CID_RESET_LEVEL:
+ err += sn9c102_i2c_write(cam, 0x30, ctrl->value);
+ break;
+ case SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE:
+ err += sn9c102_i2c_write(cam, 0x34, ctrl->value);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return err ? -EIO : 0;
+}
+
+
+static int hv7131d_set_crop(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+ int err = 0;
+ u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 2,
+ v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2;
+
+ err += sn9c102_write_reg(cam, h_start, 0x12);
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+
+ return err;
+}
+
+
+static int hv7131d_set_pix_format(struct sn9c102_device* cam,
+ const struct v4l2_pix_format* pix)
+{
+ int err = 0;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
+ err += sn9c102_write_reg(cam, 0x42, 0x19);
+ else
+ err += sn9c102_write_reg(cam, 0xf2, 0x19);
+
+ return err;
+}
+
+
+static const struct sn9c102_sensor hv7131d = {
+ .name = "HV7131D",
+ .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+ .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102,
+ .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE,
+ .frequency = SN9C102_I2C_100KHZ,
+ .interface = SN9C102_I2C_2WIRES,
+ .i2c_slave_id = 0x11,
+ .init = &hv7131d_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x0250,
+ .maximum = 0xffff,
+ .step = 0x0001,
+ .default_value = 0x0250,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "red balance",
+ .minimum = 0x00,
+ .maximum = 0x3f,
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "blue balance",
+ .minimum = 0x00,
+ .maximum = 0x3f,
+ .step = 0x01,
+ .default_value = 0x20,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_GREEN_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "green balance",
+ .minimum = 0x00,
+ .maximum = 0x3f,
+ .step = 0x01,
+ .default_value = 0x1e,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_RESET_LEVEL,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "reset level",
+ .minimum = 0x19,
+ .maximum = 0x3f,
+ .step = 0x01,
+ .default_value = 0x30,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "pixel bias voltage",
+ .minimum = 0x00,
+ .maximum = 0x07,
+ .step = 0x01,
+ .default_value = 0x02,
+ .flags = 0,
+ },
+ },
+ .get_ctrl = &hv7131d_get_ctrl,
+ .set_ctrl = &hv7131d_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ .set_crop = &hv7131d_set_crop,
+ .pix_format = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8,
+ },
+ .set_pix_format = &hv7131d_set_pix_format
+};
+
+
+int sn9c102_probe_hv7131d(struct sn9c102_device* cam)
+{
+ int r0 = 0, r1 = 0, err;
+
+ err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01},
+ {0x28, 0x17});
+
+ r0 = sn9c102_i2c_try_read(cam, &hv7131d, 0x00);
+ r1 = sn9c102_i2c_try_read(cam, &hv7131d, 0x01);
+ if (err || r0 < 0 || r1 < 0)
+ return -EIO;
+
+ if ((r0 != 0x00 && r0 != 0x01) || r1 != 0x04)
+ return -ENODEV;
+
+ sn9c102_attach_sensor(cam, &hv7131d);
+
+ return 0;
+}
diff --git a/drivers/media/video/sn9c102/sn9c102_hv7131r.c b/drivers/media/video/sn9c102/sn9c102_hv7131r.c
new file mode 100644
index 00000000..4295887f
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_hv7131r.c
@@ -0,0 +1,363 @@
+/***************************************************************************
+ * Plug-in for HV7131R image sensor connected to the SN9C1xx PC Camera *
+ * Controllers *
+ * *
+ * Copyright (C) 2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include "sn9c102_sensor.h"
+#include "sn9c102_devtable.h"
+
+
+static int hv7131r_init(struct sn9c102_device* cam)
+{
+ int err = 0;
+
+ switch (sn9c102_get_bridge(cam)) {
+ case BRIDGE_SN9C103:
+ err = sn9c102_write_const_regs(cam, {0x00, 0x03}, {0x1a, 0x04},
+ {0x20, 0x05}, {0x20, 0x06},
+ {0x03, 0x10}, {0x00, 0x14},
+ {0x60, 0x17}, {0x0a, 0x18},
+ {0xf0, 0x19}, {0x1d, 0x1a},
+ {0x10, 0x1b}, {0x02, 0x1c},
+ {0x03, 0x1d}, {0x0f, 0x1e},
+ {0x0c, 0x1f}, {0x00, 0x20},
+ {0x10, 0x21}, {0x20, 0x22},
+ {0x30, 0x23}, {0x40, 0x24},
+ {0x50, 0x25}, {0x60, 0x26},
+ {0x70, 0x27}, {0x80, 0x28},
+ {0x90, 0x29}, {0xa0, 0x2a},
+ {0xb0, 0x2b}, {0xc0, 0x2c},
+ {0xd0, 0x2d}, {0xe0, 0x2e},
+ {0xf0, 0x2f}, {0xff, 0x30});
+ break;
+ case BRIDGE_SN9C105:
+ case BRIDGE_SN9C120:
+ err = sn9c102_write_const_regs(cam, {0x44, 0x01}, {0x40, 0x02},
+ {0x00, 0x03}, {0x1a, 0x04},
+ {0x44, 0x05}, {0x3e, 0x06},
+ {0x1a, 0x07}, {0x03, 0x10},
+ {0x08, 0x14}, {0xa3, 0x17},
+ {0x4b, 0x18}, {0x00, 0x19},
+ {0x1d, 0x1a}, {0x10, 0x1b},
+ {0x02, 0x1c}, {0x03, 0x1d},
+ {0x0f, 0x1e}, {0x0c, 0x1f},
+ {0x00, 0x20}, {0x29, 0x21},
+ {0x40, 0x22}, {0x54, 0x23},
+ {0x66, 0x24}, {0x76, 0x25},
+ {0x85, 0x26}, {0x94, 0x27},
+ {0xa1, 0x28}, {0xae, 0x29},
+ {0xbb, 0x2a}, {0xc7, 0x2b},
+ {0xd3, 0x2c}, {0xde, 0x2d},
+ {0xea, 0x2e}, {0xf4, 0x2f},
+ {0xff, 0x30}, {0x00, 0x3F},
+ {0xC7, 0x40}, {0x01, 0x41},
+ {0x44, 0x42}, {0x00, 0x43},
+ {0x44, 0x44}, {0x00, 0x45},
+ {0x44, 0x46}, {0x00, 0x47},
+ {0xC7, 0x48}, {0x01, 0x49},
+ {0xC7, 0x4A}, {0x01, 0x4B},
+ {0xC7, 0x4C}, {0x01, 0x4D},
+ {0x44, 0x4E}, {0x00, 0x4F},
+ {0x44, 0x50}, {0x00, 0x51},
+ {0x44, 0x52}, {0x00, 0x53},
+ {0xC7, 0x54}, {0x01, 0x55},
+ {0xC7, 0x56}, {0x01, 0x57},
+ {0xC7, 0x58}, {0x01, 0x59},
+ {0x44, 0x5A}, {0x00, 0x5B},
+ {0x44, 0x5C}, {0x00, 0x5D},
+ {0x44, 0x5E}, {0x00, 0x5F},
+ {0xC7, 0x60}, {0x01, 0x61},
+ {0xC7, 0x62}, {0x01, 0x63},
+ {0xC7, 0x64}, {0x01, 0x65},
+ {0x44, 0x66}, {0x00, 0x67},
+ {0x44, 0x68}, {0x00, 0x69},
+ {0x44, 0x6A}, {0x00, 0x6B},
+ {0xC7, 0x6C}, {0x01, 0x6D},
+ {0xC7, 0x6E}, {0x01, 0x6F},
+ {0xC7, 0x70}, {0x01, 0x71},
+ {0x44, 0x72}, {0x00, 0x73},
+ {0x44, 0x74}, {0x00, 0x75},
+ {0x44, 0x76}, {0x00, 0x77},
+ {0xC7, 0x78}, {0x01, 0x79},
+ {0xC7, 0x7A}, {0x01, 0x7B},
+ {0xC7, 0x7C}, {0x01, 0x7D},
+ {0x44, 0x7E}, {0x00, 0x7F},
+ {0x14, 0x84}, {0x00, 0x85},
+ {0x27, 0x86}, {0x00, 0x87},
+ {0x07, 0x88}, {0x00, 0x89},
+ {0xEC, 0x8A}, {0x0f, 0x8B},
+ {0xD8, 0x8C}, {0x0f, 0x8D},
+ {0x3D, 0x8E}, {0x00, 0x8F},
+ {0x3D, 0x90}, {0x00, 0x91},
+ {0xCD, 0x92}, {0x0f, 0x93},
+ {0xf7, 0x94}, {0x0f, 0x95},
+ {0x0C, 0x96}, {0x00, 0x97},
+ {0x00, 0x98}, {0x66, 0x99},
+ {0x05, 0x9A}, {0x00, 0x9B},
+ {0x04, 0x9C}, {0x00, 0x9D},
+ {0x08, 0x9E}, {0x00, 0x9F},
+ {0x2D, 0xC0}, {0x2D, 0xC1},
+ {0x3A, 0xC2}, {0x05, 0xC3},
+ {0x04, 0xC4}, {0x3F, 0xC5},
+ {0x00, 0xC6}, {0x00, 0xC7},
+ {0x50, 0xC8}, {0x3C, 0xC9},
+ {0x28, 0xCA}, {0xD8, 0xCB},
+ {0x14, 0xCC}, {0xEC, 0xCD},
+ {0x32, 0xCE}, {0xDD, 0xCF},
+ {0x32, 0xD0}, {0xDD, 0xD1},
+ {0x6A, 0xD2}, {0x50, 0xD3},
+ {0x00, 0xD4}, {0x00, 0xD5},
+ {0x00, 0xD6});
+ break;
+ default:
+ break;
+ }
+
+ err += sn9c102_i2c_write(cam, 0x20, 0x00);
+ err += sn9c102_i2c_write(cam, 0x21, 0xd6);
+ err += sn9c102_i2c_write(cam, 0x25, 0x06);
+
+ return err;
+}
+
+
+static int hv7131r_get_ctrl(struct sn9c102_device* cam,
+ struct v4l2_control* ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x30)) < 0)
+ return -EIO;
+ return 0;
+ case V4L2_CID_RED_BALANCE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x31)) < 0)
+ return -EIO;
+ ctrl->value = ctrl->value & 0x3f;
+ return 0;
+ case V4L2_CID_BLUE_BALANCE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x33)) < 0)
+ return -EIO;
+ ctrl->value = ctrl->value & 0x3f;
+ return 0;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x32)) < 0)
+ return -EIO;
+ ctrl->value = ctrl->value & 0x3f;
+ return 0;
+ case V4L2_CID_BLACK_LEVEL:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x01)) < 0)
+ return -EIO;
+ ctrl->value = (ctrl->value & 0x08) ? 1 : 0;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+
+static int hv7131r_set_ctrl(struct sn9c102_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ err += sn9c102_i2c_write(cam, 0x30, ctrl->value);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x31, ctrl->value);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x33, ctrl->value);
+ break;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x32, ctrl->value);
+ break;
+ case V4L2_CID_BLACK_LEVEL:
+ {
+ int r = sn9c102_i2c_read(cam, 0x01);
+ if (r < 0)
+ return -EIO;
+ err += sn9c102_i2c_write(cam, 0x01,
+ (ctrl->value<<3) | (r&0xf7));
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return err ? -EIO : 0;
+}
+
+
+static int hv7131r_set_crop(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+ int err = 0;
+ u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1,
+ v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1;
+
+ err += sn9c102_write_reg(cam, h_start, 0x12);
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+
+ return err;
+}
+
+
+static int hv7131r_set_pix_format(struct sn9c102_device* cam,
+ const struct v4l2_pix_format* pix)
+{
+ int err = 0;
+
+ switch (sn9c102_get_bridge(cam)) {
+ case BRIDGE_SN9C103:
+ if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) {
+ err += sn9c102_write_reg(cam, 0xa0, 0x19);
+ err += sn9c102_i2c_write(cam, 0x01, 0x04);
+ } else {
+ err += sn9c102_write_reg(cam, 0x30, 0x19);
+ err += sn9c102_i2c_write(cam, 0x01, 0x04);
+ }
+ break;
+ case BRIDGE_SN9C105:
+ case BRIDGE_SN9C120:
+ if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) {
+ err += sn9c102_write_reg(cam, 0xa5, 0x17);
+ err += sn9c102_i2c_write(cam, 0x01, 0x24);
+ } else {
+ err += sn9c102_write_reg(cam, 0xa3, 0x17);
+ err += sn9c102_i2c_write(cam, 0x01, 0x04);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return err;
+}
+
+
+static const struct sn9c102_sensor hv7131r = {
+ .name = "HV7131R",
+ .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+ .supported_bridge = BRIDGE_SN9C103 | BRIDGE_SN9C105 | BRIDGE_SN9C120,
+ .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE,
+ .frequency = SN9C102_I2C_100KHZ,
+ .interface = SN9C102_I2C_2WIRES,
+ .i2c_slave_id = 0x11,
+ .init = &hv7131r_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "global gain",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x01,
+ .default_value = 0x40,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "red balance",
+ .minimum = 0x00,
+ .maximum = 0x3f,
+ .step = 0x01,
+ .default_value = 0x08,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "blue balance",
+ .minimum = 0x00,
+ .maximum = 0x3f,
+ .step = 0x01,
+ .default_value = 0x1a,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_GREEN_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "green balance",
+ .minimum = 0x00,
+ .maximum = 0x3f,
+ .step = 0x01,
+ .default_value = 0x2f,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_BLACK_LEVEL,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "auto black level compensation",
+ .minimum = 0x00,
+ .maximum = 0x01,
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ },
+ .get_ctrl = &hv7131r_get_ctrl,
+ .set_ctrl = &hv7131r_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ .set_crop = &hv7131r_set_crop,
+ .pix_format = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8,
+ },
+ .set_pix_format = &hv7131r_set_pix_format
+};
+
+
+int sn9c102_probe_hv7131r(struct sn9c102_device* cam)
+{
+ int devid, err;
+
+ err = sn9c102_write_const_regs(cam, {0x09, 0x01}, {0x44, 0x02},
+ {0x34, 0x01}, {0x20, 0x17},
+ {0x34, 0x01}, {0x46, 0x01});
+
+ devid = sn9c102_i2c_try_read(cam, &hv7131r, 0x00);
+ if (err || devid < 0)
+ return -EIO;
+
+ if (devid != 0x02)
+ return -ENODEV;
+
+ sn9c102_attach_sensor(cam, &hv7131r);
+
+ return 0;
+}
diff --git a/drivers/media/video/sn9c102/sn9c102_mi0343.c b/drivers/media/video/sn9c102/sn9c102_mi0343.c
new file mode 100644
index 00000000..1f5b09be
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_mi0343.c
@@ -0,0 +1,352 @@
+/***************************************************************************
+ * Plug-in for MI-0343 image sensor connected to the SN9C1xx PC Camera *
+ * Controllers *
+ * *
+ * Copyright (C) 2004-2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include "sn9c102_sensor.h"
+#include "sn9c102_devtable.h"
+
+
+static int mi0343_init(struct sn9c102_device* cam)
+{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+ int err = 0;
+
+ err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11},
+ {0x0a, 0x14}, {0x40, 0x01},
+ {0x20, 0x17}, {0x07, 0x18},
+ {0xa0, 0x19});
+
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d,
+ 0x00, 0x01, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d,
+ 0x00, 0x00, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03,
+ 0x01, 0xe1, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04,
+ 0x02, 0x81, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05,
+ 0x00, 0x17, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06,
+ 0x00, 0x11, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x62,
+ 0x04, 0x9a, 0, 0);
+
+ return err;
+}
+
+
+static int mi0343_get_ctrl(struct sn9c102_device* cam,
+ struct v4l2_control* ctrl)
+{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+ u8 data[2];
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x09, 2,
+ data) < 0)
+ return -EIO;
+ ctrl->value = data[0];
+ return 0;
+ case V4L2_CID_GAIN:
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x35, 2,
+ data) < 0)
+ return -EIO;
+ break;
+ case V4L2_CID_HFLIP:
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2,
+ data) < 0)
+ return -EIO;
+ ctrl->value = data[1] & 0x20 ? 1 : 0;
+ return 0;
+ case V4L2_CID_VFLIP:
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2,
+ data) < 0)
+ return -EIO;
+ ctrl->value = data[1] & 0x80 ? 1 : 0;
+ return 0;
+ case V4L2_CID_RED_BALANCE:
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2d, 2,
+ data) < 0)
+ return -EIO;
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2c, 2,
+ data) < 0)
+ return -EIO;
+ break;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2e, 2,
+ data) < 0)
+ return -EIO;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ case V4L2_CID_RED_BALANCE:
+ case V4L2_CID_BLUE_BALANCE:
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ ctrl->value = data[1] | (data[0] << 8);
+ if (ctrl->value >= 0x10 && ctrl->value <= 0x3f)
+ ctrl->value -= 0x10;
+ else if (ctrl->value >= 0x60 && ctrl->value <= 0x7f)
+ ctrl->value -= 0x60;
+ else if (ctrl->value >= 0xe0 && ctrl->value <= 0xff)
+ ctrl->value -= 0xe0;
+ }
+
+ return 0;
+}
+
+
+static int mi0343_set_ctrl(struct sn9c102_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+ u16 reg = 0;
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ case V4L2_CID_RED_BALANCE:
+ case V4L2_CID_BLUE_BALANCE:
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ if (ctrl->value <= (0x3f-0x10))
+ reg = 0x10 + ctrl->value;
+ else if (ctrl->value <= ((0x3f-0x10) + (0x7f-0x60)))
+ reg = 0x60 + (ctrl->value - (0x3f-0x10));
+ else
+ reg = 0xe0 + (ctrl->value - (0x3f-0x10) - (0x7f-0x60));
+ break;
+ }
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x09, ctrl->value, 0x00,
+ 0, 0);
+ break;
+ case V4L2_CID_GAIN:
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x35, reg >> 8, reg & 0xff,
+ 0, 0);
+ break;
+ case V4L2_CID_HFLIP:
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x20, ctrl->value ? 0x40:0x00,
+ ctrl->value ? 0x20:0x00,
+ 0, 0);
+ break;
+ case V4L2_CID_VFLIP:
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x20, ctrl->value ? 0x80:0x00,
+ ctrl->value ? 0x80:0x00,
+ 0, 0);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x2d, reg >> 8, reg & 0xff,
+ 0, 0);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x2c, reg >> 8, reg & 0xff,
+ 0, 0);
+ break;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x2b, reg >> 8, reg & 0xff,
+ 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x2e, reg >> 8, reg & 0xff,
+ 0, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return err ? -EIO : 0;
+}
+
+
+static int mi0343_set_crop(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+ int err = 0;
+ u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0,
+ v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2;
+
+ err += sn9c102_write_reg(cam, h_start, 0x12);
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+
+ return err;
+}
+
+
+static int mi0343_set_pix_format(struct sn9c102_device* cam,
+ const struct v4l2_pix_format* pix)
+{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+ int err = 0;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) {
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x0a, 0x00, 0x03, 0, 0);
+ err += sn9c102_write_reg(cam, 0x20, 0x19);
+ } else {
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x0a, 0x00, 0x05, 0, 0);
+ err += sn9c102_write_reg(cam, 0xa0, 0x19);
+ }
+
+ return err;
+}
+
+
+static const struct sn9c102_sensor mi0343 = {
+ .name = "MI-0343",
+ .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+ .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102,
+ .frequency = SN9C102_I2C_100KHZ,
+ .interface = SN9C102_I2C_2WIRES,
+ .i2c_slave_id = 0x5d,
+ .init = &mi0343_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x00,
+ .maximum = 0x0f,
+ .step = 0x01,
+ .default_value = 0x06,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "global gain",
+ .minimum = 0x00,
+ .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0),/*0x6d*/
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "horizontal mirror",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "vertical mirror",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "red balance",
+ .minimum = 0x00,
+ .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0),
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "blue balance",
+ .minimum = 0x00,
+ .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0),
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_GREEN_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "green balance",
+ .minimum = 0x00,
+ .maximum = ((0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0)),
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ },
+ .get_ctrl = &mi0343_get_ctrl,
+ .set_ctrl = &mi0343_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ .set_crop = &mi0343_set_crop,
+ .pix_format = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8,
+ },
+ .set_pix_format = &mi0343_set_pix_format
+};
+
+
+int sn9c102_probe_mi0343(struct sn9c102_device* cam)
+{
+ u8 data[2];
+
+ if (sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01},
+ {0x28, 0x17}))
+ return -EIO;
+
+ if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, 0x00,
+ 2, data) < 0)
+ return -EIO;
+
+ if (data[1] != 0x42 || data[0] != 0xe3)
+ return -ENODEV;
+
+ sn9c102_attach_sensor(cam, &mi0343);
+
+ return 0;
+}
diff --git a/drivers/media/video/sn9c102/sn9c102_mi0360.c b/drivers/media/video/sn9c102/sn9c102_mi0360.c
new file mode 100644
index 00000000..d973fc19
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_mi0360.c
@@ -0,0 +1,453 @@
+/***************************************************************************
+ * Plug-in for MI-0360 image sensor connected to the SN9C1xx PC Camera *
+ * Controllers *
+ * *
+ * Copyright (C) 2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include "sn9c102_sensor.h"
+#include "sn9c102_devtable.h"
+
+
+static int mi0360_init(struct sn9c102_device* cam)
+{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+ int err = 0;
+
+ switch (sn9c102_get_bridge(cam)) {
+ case BRIDGE_SN9C103:
+ err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11},
+ {0x0a, 0x14}, {0x40, 0x01},
+ {0x20, 0x17}, {0x07, 0x18},
+ {0xa0, 0x19}, {0x02, 0x1c},
+ {0x03, 0x1d}, {0x0f, 0x1e},
+ {0x0c, 0x1f}, {0x00, 0x20},
+ {0x10, 0x21}, {0x20, 0x22},
+ {0x30, 0x23}, {0x40, 0x24},
+ {0x50, 0x25}, {0x60, 0x26},
+ {0x70, 0x27}, {0x80, 0x28},
+ {0x90, 0x29}, {0xa0, 0x2a},
+ {0xb0, 0x2b}, {0xc0, 0x2c},
+ {0xd0, 0x2d}, {0xe0, 0x2e},
+ {0xf0, 0x2f}, {0xff, 0x30});
+ break;
+ case BRIDGE_SN9C105:
+ case BRIDGE_SN9C120:
+ err = sn9c102_write_const_regs(cam, {0x44, 0x01}, {0x40, 0x02},
+ {0x00, 0x03}, {0x1a, 0x04},
+ {0x50, 0x05}, {0x20, 0x06},
+ {0x10, 0x07}, {0x03, 0x10},
+ {0x08, 0x14}, {0xa2, 0x17},
+ {0x47, 0x18}, {0x00, 0x19},
+ {0x1d, 0x1a}, {0x10, 0x1b},
+ {0x02, 0x1c}, {0x03, 0x1d},
+ {0x0f, 0x1e}, {0x0c, 0x1f},
+ {0x00, 0x20}, {0x29, 0x21},
+ {0x40, 0x22}, {0x54, 0x23},
+ {0x66, 0x24}, {0x76, 0x25},
+ {0x85, 0x26}, {0x94, 0x27},
+ {0xa1, 0x28}, {0xae, 0x29},
+ {0xbb, 0x2a}, {0xc7, 0x2b},
+ {0xd3, 0x2c}, {0xde, 0x2d},
+ {0xea, 0x2e}, {0xf4, 0x2f},
+ {0xff, 0x30}, {0x00, 0x3F},
+ {0xC7, 0x40}, {0x01, 0x41},
+ {0x44, 0x42}, {0x00, 0x43},
+ {0x44, 0x44}, {0x00, 0x45},
+ {0x44, 0x46}, {0x00, 0x47},
+ {0xC7, 0x48}, {0x01, 0x49},
+ {0xC7, 0x4A}, {0x01, 0x4B},
+ {0xC7, 0x4C}, {0x01, 0x4D},
+ {0x44, 0x4E}, {0x00, 0x4F},
+ {0x44, 0x50}, {0x00, 0x51},
+ {0x44, 0x52}, {0x00, 0x53},
+ {0xC7, 0x54}, {0x01, 0x55},
+ {0xC7, 0x56}, {0x01, 0x57},
+ {0xC7, 0x58}, {0x01, 0x59},
+ {0x44, 0x5A}, {0x00, 0x5B},
+ {0x44, 0x5C}, {0x00, 0x5D},
+ {0x44, 0x5E}, {0x00, 0x5F},
+ {0xC7, 0x60}, {0x01, 0x61},
+ {0xC7, 0x62}, {0x01, 0x63},
+ {0xC7, 0x64}, {0x01, 0x65},
+ {0x44, 0x66}, {0x00, 0x67},
+ {0x44, 0x68}, {0x00, 0x69},
+ {0x44, 0x6A}, {0x00, 0x6B},
+ {0xC7, 0x6C}, {0x01, 0x6D},
+ {0xC7, 0x6E}, {0x01, 0x6F},
+ {0xC7, 0x70}, {0x01, 0x71},
+ {0x44, 0x72}, {0x00, 0x73},
+ {0x44, 0x74}, {0x00, 0x75},
+ {0x44, 0x76}, {0x00, 0x77},
+ {0xC7, 0x78}, {0x01, 0x79},
+ {0xC7, 0x7A}, {0x01, 0x7B},
+ {0xC7, 0x7C}, {0x01, 0x7D},
+ {0x44, 0x7E}, {0x00, 0x7F},
+ {0x14, 0x84}, {0x00, 0x85},
+ {0x27, 0x86}, {0x00, 0x87},
+ {0x07, 0x88}, {0x00, 0x89},
+ {0xEC, 0x8A}, {0x0f, 0x8B},
+ {0xD8, 0x8C}, {0x0f, 0x8D},
+ {0x3D, 0x8E}, {0x00, 0x8F},
+ {0x3D, 0x90}, {0x00, 0x91},
+ {0xCD, 0x92}, {0x0f, 0x93},
+ {0xf7, 0x94}, {0x0f, 0x95},
+ {0x0C, 0x96}, {0x00, 0x97},
+ {0x00, 0x98}, {0x66, 0x99},
+ {0x05, 0x9A}, {0x00, 0x9B},
+ {0x04, 0x9C}, {0x00, 0x9D},
+ {0x08, 0x9E}, {0x00, 0x9F},
+ {0x2D, 0xC0}, {0x2D, 0xC1},
+ {0x3A, 0xC2}, {0x05, 0xC3},
+ {0x04, 0xC4}, {0x3F, 0xC5},
+ {0x00, 0xC6}, {0x00, 0xC7},
+ {0x50, 0xC8}, {0x3C, 0xC9},
+ {0x28, 0xCA}, {0xD8, 0xCB},
+ {0x14, 0xCC}, {0xEC, 0xCD},
+ {0x32, 0xCE}, {0xDD, 0xCF},
+ {0x32, 0xD0}, {0xDD, 0xD1},
+ {0x6A, 0xD2}, {0x50, 0xD3},
+ {0x00, 0xD4}, {0x00, 0xD5},
+ {0x00, 0xD6});
+ break;
+ default:
+ break;
+ }
+
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d,
+ 0x00, 0x01, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d,
+ 0x00, 0x00, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03,
+ 0x01, 0xe1, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04,
+ 0x02, 0x81, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05,
+ 0x00, 0x17, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06,
+ 0x00, 0x11, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x62,
+ 0x04, 0x9a, 0, 0);
+
+ return err;
+}
+
+
+static int mi0360_get_ctrl(struct sn9c102_device* cam,
+ struct v4l2_control* ctrl)
+{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+ u8 data[2];
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x09, 2,
+ data) < 0)
+ return -EIO;
+ ctrl->value = data[0];
+ return 0;
+ case V4L2_CID_GAIN:
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x35, 2,
+ data) < 0)
+ return -EIO;
+ ctrl->value = data[1];
+ return 0;
+ case V4L2_CID_RED_BALANCE:
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2c, 2,
+ data) < 0)
+ return -EIO;
+ ctrl->value = data[1];
+ return 0;
+ case V4L2_CID_BLUE_BALANCE:
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2d, 2,
+ data) < 0)
+ return -EIO;
+ ctrl->value = data[1];
+ return 0;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2e, 2,
+ data) < 0)
+ return -EIO;
+ ctrl->value = data[1];
+ return 0;
+ case V4L2_CID_HFLIP:
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2,
+ data) < 0)
+ return -EIO;
+ ctrl->value = data[1] & 0x20 ? 1 : 0;
+ return 0;
+ case V4L2_CID_VFLIP:
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2,
+ data) < 0)
+ return -EIO;
+ ctrl->value = data[1] & 0x80 ? 1 : 0;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+static int mi0360_set_ctrl(struct sn9c102_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x09, ctrl->value, 0x00,
+ 0, 0);
+ break;
+ case V4L2_CID_GAIN:
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x35, 0x03, ctrl->value,
+ 0, 0);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x2c, 0x03, ctrl->value,
+ 0, 0);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x2d, 0x03, ctrl->value,
+ 0, 0);
+ break;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x2b, 0x03, ctrl->value,
+ 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x2e, 0x03, ctrl->value,
+ 0, 0);
+ break;
+ case V4L2_CID_HFLIP:
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x20, ctrl->value ? 0x40:0x00,
+ ctrl->value ? 0x20:0x00,
+ 0, 0);
+ break;
+ case V4L2_CID_VFLIP:
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x20, ctrl->value ? 0x80:0x00,
+ ctrl->value ? 0x80:0x00,
+ 0, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return err ? -EIO : 0;
+}
+
+
+static int mi0360_set_crop(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+ int err = 0;
+ u8 h_start = 0, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1;
+
+ switch (sn9c102_get_bridge(cam)) {
+ case BRIDGE_SN9C103:
+ h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0;
+ break;
+ case BRIDGE_SN9C105:
+ case BRIDGE_SN9C120:
+ h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1;
+ break;
+ default:
+ break;
+ }
+
+ err += sn9c102_write_reg(cam, h_start, 0x12);
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+
+ return err;
+}
+
+
+static int mi0360_set_pix_format(struct sn9c102_device* cam,
+ const struct v4l2_pix_format* pix)
+{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+ int err = 0;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) {
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x0a, 0x00, 0x05, 0, 0);
+ err += sn9c102_write_reg(cam, 0x60, 0x19);
+ if (sn9c102_get_bridge(cam) == BRIDGE_SN9C105 ||
+ sn9c102_get_bridge(cam) == BRIDGE_SN9C120)
+ err += sn9c102_write_reg(cam, 0xa6, 0x17);
+ } else {
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x0a, 0x00, 0x02, 0, 0);
+ err += sn9c102_write_reg(cam, 0x20, 0x19);
+ if (sn9c102_get_bridge(cam) == BRIDGE_SN9C105 ||
+ sn9c102_get_bridge(cam) == BRIDGE_SN9C120)
+ err += sn9c102_write_reg(cam, 0xa2, 0x17);
+ }
+
+ return err;
+}
+
+
+static const struct sn9c102_sensor mi0360 = {
+ .name = "MI-0360",
+ .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+ .supported_bridge = BRIDGE_SN9C103 | BRIDGE_SN9C105 | BRIDGE_SN9C120,
+ .frequency = SN9C102_I2C_100KHZ,
+ .interface = SN9C102_I2C_2WIRES,
+ .i2c_slave_id = 0x5d,
+ .init = &mi0360_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x00,
+ .maximum = 0x0f,
+ .step = 0x01,
+ .default_value = 0x05,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "global gain",
+ .minimum = 0x00,
+ .maximum = 0x7f,
+ .step = 0x01,
+ .default_value = 0x25,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "horizontal mirror",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "vertical mirror",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "blue balance",
+ .minimum = 0x00,
+ .maximum = 0x7f,
+ .step = 0x01,
+ .default_value = 0x0f,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "red balance",
+ .minimum = 0x00,
+ .maximum = 0x7f,
+ .step = 0x01,
+ .default_value = 0x32,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_GREEN_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "green balance",
+ .minimum = 0x00,
+ .maximum = 0x7f,
+ .step = 0x01,
+ .default_value = 0x25,
+ .flags = 0,
+ },
+ },
+ .get_ctrl = &mi0360_get_ctrl,
+ .set_ctrl = &mi0360_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ .set_crop = &mi0360_set_crop,
+ .pix_format = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8,
+ },
+ .set_pix_format = &mi0360_set_pix_format
+};
+
+
+int sn9c102_probe_mi0360(struct sn9c102_device* cam)
+{
+
+ u8 data[2];
+
+ switch (sn9c102_get_bridge(cam)) {
+ case BRIDGE_SN9C103:
+ if (sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01},
+ {0x28, 0x17}))
+ return -EIO;
+ break;
+ case BRIDGE_SN9C105:
+ case BRIDGE_SN9C120:
+ if (sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1},
+ {0x01, 0x01}, {0x00, 0x01},
+ {0x28, 0x17}))
+ return -EIO;
+ break;
+ default:
+ break;
+ }
+
+ if (sn9c102_i2c_try_raw_read(cam, &mi0360, mi0360.i2c_slave_id, 0x00,
+ 2, data) < 0)
+ return -EIO;
+
+ if (data[0] != 0x82 || data[1] != 0x43)
+ return -ENODEV;
+
+ sn9c102_attach_sensor(cam, &mi0360);
+
+ return 0;
+}
diff --git a/drivers/media/video/sn9c102/sn9c102_mt9v111.c b/drivers/media/video/sn9c102/sn9c102_mt9v111.c
new file mode 100644
index 00000000..95986eb4
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_mt9v111.c
@@ -0,0 +1,260 @@
+/***************************************************************************
+ * Plug-in for MT9V111 image sensor connected to the SN9C1xx PC Camera *
+ * Controllers *
+ * *
+ * Copyright (C) 2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include "sn9c102_sensor.h"
+#include "sn9c102_devtable.h"
+
+
+static int mt9v111_init(struct sn9c102_device *cam)
+{
+ struct sn9c102_sensor *s = sn9c102_get_sensor(cam);
+ int err = 0;
+
+ err = sn9c102_write_const_regs(cam, {0x44, 0x01}, {0x40, 0x02},
+ {0x00, 0x03}, {0x1a, 0x04},
+ {0x1f, 0x05}, {0x20, 0x06},
+ {0x1f, 0x07}, {0x81, 0x08},
+ {0x5c, 0x09}, {0x00, 0x0a},
+ {0x00, 0x0b}, {0x00, 0x0c},
+ {0x00, 0x0d}, {0x00, 0x0e},
+ {0x00, 0x0f}, {0x03, 0x10},
+ {0x00, 0x11}, {0x00, 0x12},
+ {0x02, 0x13}, {0x14, 0x14},
+ {0x28, 0x15}, {0x1e, 0x16},
+ {0xe2, 0x17}, {0x06, 0x18},
+ {0x00, 0x19}, {0x00, 0x1a},
+ {0x00, 0x1b}, {0x08, 0x20},
+ {0x39, 0x21}, {0x51, 0x22},
+ {0x63, 0x23}, {0x73, 0x24},
+ {0x82, 0x25}, {0x8f, 0x26},
+ {0x9b, 0x27}, {0xa7, 0x28},
+ {0xb1, 0x29}, {0xbc, 0x2a},
+ {0xc6, 0x2b}, {0xcf, 0x2c},
+ {0xd8, 0x2d}, {0xe1, 0x2e},
+ {0xea, 0x2f}, {0xf2, 0x30},
+ {0x13, 0x84}, {0x00, 0x85},
+ {0x25, 0x86}, {0x00, 0x87},
+ {0x07, 0x88}, {0x00, 0x89},
+ {0xee, 0x8a}, {0x0f, 0x8b},
+ {0xe5, 0x8c}, {0x0f, 0x8d},
+ {0x2e, 0x8e}, {0x00, 0x8f},
+ {0x30, 0x90}, {0x00, 0x91},
+ {0xd4, 0x92}, {0x0f, 0x93},
+ {0xfc, 0x94}, {0x0f, 0x95},
+ {0x14, 0x96}, {0x00, 0x97},
+ {0x00, 0x98}, {0x60, 0x99},
+ {0x07, 0x9a}, {0x40, 0x9b},
+ {0x20, 0x9c}, {0x00, 0x9d},
+ {0x00, 0x9e}, {0x00, 0x9f},
+ {0x2d, 0xc0}, {0x2d, 0xc1},
+ {0x3a, 0xc2}, {0x05, 0xc3},
+ {0x04, 0xc4}, {0x3f, 0xc5},
+ {0x00, 0xc6}, {0x00, 0xc7},
+ {0x50, 0xc8}, {0x3c, 0xc9},
+ {0x28, 0xca}, {0xd8, 0xcb},
+ {0x14, 0xcc}, {0xec, 0xcd},
+ {0x32, 0xce}, {0xdd, 0xcf},
+ {0x2d, 0xd0}, {0xdd, 0xd1},
+ {0x6a, 0xd2}, {0x50, 0xd3},
+ {0x60, 0xd4}, {0x00, 0xd5},
+ {0x00, 0xd6});
+
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x01,
+ 0x00, 0x01, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d,
+ 0x00, 0x01, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d,
+ 0x00, 0x00, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x08,
+ 0x04, 0x80, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x01,
+ 0x00, 0x04, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x08,
+ 0x00, 0x08, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x02,
+ 0x00, 0x16, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03,
+ 0x01, 0xe7, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04,
+ 0x02, 0x87, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06,
+ 0x00, 0x40, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05,
+ 0x00, 0x09, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x07,
+ 0x30, 0x02, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0c,
+ 0x00, 0x00, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x12,
+ 0x00, 0xb0, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x13,
+ 0x00, 0x7c, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x1e,
+ 0x00, 0x00, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x20,
+ 0x00, 0x00, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x20,
+ 0x00, 0x00, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x01,
+ 0x00, 0x04, 0, 0);
+
+ return err;
+}
+
+static int mt9v111_get_ctrl(struct sn9c102_device *cam,
+ struct v4l2_control *ctrl)
+{
+ struct sn9c102_sensor *s = sn9c102_get_sensor(cam);
+ u8 data[2];
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, 2,
+ data) < 0)
+ return -EIO;
+ ctrl->value = data[1] & 0x80 ? 1 : 0;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ return err ? -EIO : 0;
+}
+
+static int mt9v111_set_ctrl(struct sn9c102_device *cam,
+ const struct v4l2_control *ctrl)
+{
+ struct sn9c102_sensor *s = sn9c102_get_sensor(cam);
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id,
+ 0x20,
+ ctrl->value ? 0x80 : 0x00,
+ ctrl->value ? 0x80 : 0x00, 0,
+ 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return err ? -EIO : 0;
+}
+
+static int mt9v111_set_crop(struct sn9c102_device *cam,
+ const struct v4l2_rect *rect)
+{
+ struct sn9c102_sensor *s = sn9c102_get_sensor(cam);
+ int err = 0;
+ u8 v_start = (u8) (rect->top - s->cropcap.bounds.top) + 2;
+
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+
+ return err;
+}
+
+static int mt9v111_set_pix_format(struct sn9c102_device *cam,
+ const struct v4l2_pix_format *pix)
+{
+ int err = 0;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) {
+ err += sn9c102_write_reg(cam, 0xb4, 0x17);
+ } else {
+ err += sn9c102_write_reg(cam, 0xe2, 0x17);
+ }
+
+ return err;
+}
+
+
+static const struct sn9c102_sensor mt9v111 = {
+ .name = "MT9V111",
+ .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+ .supported_bridge = BRIDGE_SN9C105 | BRIDGE_SN9C120,
+ .frequency = SN9C102_I2C_100KHZ,
+ .interface = SN9C102_I2C_2WIRES,
+ .i2c_slave_id = 0x5c,
+ .init = &mt9v111_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "vertical mirror",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ },
+ },
+ .get_ctrl = &mt9v111_get_ctrl,
+ .set_ctrl = &mt9v111_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ .set_crop = &mt9v111_set_crop,
+ .pix_format = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8,
+ },
+ .set_pix_format = &mt9v111_set_pix_format
+};
+
+
+int sn9c102_probe_mt9v111(struct sn9c102_device *cam)
+{
+ u8 data[2];
+ int err = 0;
+
+ err += sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1},
+ {0x29, 0x01}, {0x42, 0x17},
+ {0x62, 0x17}, {0x08, 0x01});
+ err += sn9c102_i2c_try_raw_write(cam, &mt9v111, 4,
+ mt9v111.i2c_slave_id, 0x01, 0x00,
+ 0x04, 0, 0);
+ if (err || sn9c102_i2c_try_raw_read(cam, &mt9v111,
+ mt9v111.i2c_slave_id, 0x36, 2,
+ data) < 0)
+ return -EIO;
+
+ if (data[0] != 0x82 || data[1] != 0x3a)
+ return -ENODEV;
+
+ sn9c102_attach_sensor(cam, &mt9v111);
+
+ return 0;
+}
diff --git a/drivers/media/video/sn9c102/sn9c102_ov7630.c b/drivers/media/video/sn9c102/sn9c102_ov7630.c
new file mode 100644
index 00000000..803712c2
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_ov7630.c
@@ -0,0 +1,626 @@
+/***************************************************************************
+ * Plug-in for OV7630 image sensor connected to the SN9C1xx PC Camera *
+ * Controllers *
+ * *
+ * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include "sn9c102_sensor.h"
+#include "sn9c102_devtable.h"
+
+
+static int ov7630_init(struct sn9c102_device* cam)
+{
+ int err = 0;
+
+ switch (sn9c102_get_bridge(cam)) {
+ case BRIDGE_SN9C101:
+ case BRIDGE_SN9C102:
+ err = sn9c102_write_const_regs(cam, {0x00, 0x14}, {0x60, 0x17},
+ {0x0f, 0x18}, {0x50, 0x19});
+
+ err += sn9c102_i2c_write(cam, 0x12, 0x8d);
+ err += sn9c102_i2c_write(cam, 0x12, 0x0d);
+ err += sn9c102_i2c_write(cam, 0x11, 0x00);
+ err += sn9c102_i2c_write(cam, 0x15, 0x35);
+ err += sn9c102_i2c_write(cam, 0x16, 0x03);
+ err += sn9c102_i2c_write(cam, 0x17, 0x1c);
+ err += sn9c102_i2c_write(cam, 0x18, 0xbd);
+ err += sn9c102_i2c_write(cam, 0x19, 0x06);
+ err += sn9c102_i2c_write(cam, 0x1a, 0xf6);
+ err += sn9c102_i2c_write(cam, 0x1b, 0x04);
+ err += sn9c102_i2c_write(cam, 0x20, 0x44);
+ err += sn9c102_i2c_write(cam, 0x23, 0xee);
+ err += sn9c102_i2c_write(cam, 0x26, 0xa0);
+ err += sn9c102_i2c_write(cam, 0x27, 0x9a);
+ err += sn9c102_i2c_write(cam, 0x28, 0x20);
+ err += sn9c102_i2c_write(cam, 0x29, 0x30);
+ err += sn9c102_i2c_write(cam, 0x2f, 0x3d);
+ err += sn9c102_i2c_write(cam, 0x30, 0x24);
+ err += sn9c102_i2c_write(cam, 0x32, 0x86);
+ err += sn9c102_i2c_write(cam, 0x60, 0xa9);
+ err += sn9c102_i2c_write(cam, 0x61, 0x42);
+ err += sn9c102_i2c_write(cam, 0x65, 0x00);
+ err += sn9c102_i2c_write(cam, 0x69, 0x38);
+ err += sn9c102_i2c_write(cam, 0x6f, 0x88);
+ err += sn9c102_i2c_write(cam, 0x70, 0x0b);
+ err += sn9c102_i2c_write(cam, 0x71, 0x00);
+ err += sn9c102_i2c_write(cam, 0x74, 0x21);
+ err += sn9c102_i2c_write(cam, 0x7d, 0xf7);
+ break;
+ case BRIDGE_SN9C103:
+ err = sn9c102_write_const_regs(cam, {0x00, 0x02}, {0x00, 0x03},
+ {0x1a, 0x04}, {0x20, 0x05},
+ {0x20, 0x06}, {0x20, 0x07},
+ {0x03, 0x10}, {0x0a, 0x14},
+ {0x60, 0x17}, {0x0f, 0x18},
+ {0x50, 0x19}, {0x1d, 0x1a},
+ {0x10, 0x1b}, {0x02, 0x1c},
+ {0x03, 0x1d}, {0x0f, 0x1e},
+ {0x0c, 0x1f}, {0x00, 0x20},
+ {0x10, 0x21}, {0x20, 0x22},
+ {0x30, 0x23}, {0x40, 0x24},
+ {0x50, 0x25}, {0x60, 0x26},
+ {0x70, 0x27}, {0x80, 0x28},
+ {0x90, 0x29}, {0xa0, 0x2a},
+ {0xb0, 0x2b}, {0xc0, 0x2c},
+ {0xd0, 0x2d}, {0xe0, 0x2e},
+ {0xf0, 0x2f}, {0xff, 0x30});
+
+ err += sn9c102_i2c_write(cam, 0x12, 0x8d);
+ err += sn9c102_i2c_write(cam, 0x12, 0x0d);
+ err += sn9c102_i2c_write(cam, 0x15, 0x34);
+ err += sn9c102_i2c_write(cam, 0x11, 0x01);
+ err += sn9c102_i2c_write(cam, 0x1b, 0x04);
+ err += sn9c102_i2c_write(cam, 0x20, 0x44);
+ err += sn9c102_i2c_write(cam, 0x23, 0xee);
+ err += sn9c102_i2c_write(cam, 0x26, 0xa0);
+ err += sn9c102_i2c_write(cam, 0x27, 0x9a);
+ err += sn9c102_i2c_write(cam, 0x28, 0x20);
+ err += sn9c102_i2c_write(cam, 0x29, 0x30);
+ err += sn9c102_i2c_write(cam, 0x2f, 0x3d);
+ err += sn9c102_i2c_write(cam, 0x30, 0x24);
+ err += sn9c102_i2c_write(cam, 0x32, 0x86);
+ err += sn9c102_i2c_write(cam, 0x60, 0xa9);
+ err += sn9c102_i2c_write(cam, 0x61, 0x42);
+ err += sn9c102_i2c_write(cam, 0x65, 0x00);
+ err += sn9c102_i2c_write(cam, 0x69, 0x38);
+ err += sn9c102_i2c_write(cam, 0x6f, 0x88);
+ err += sn9c102_i2c_write(cam, 0x70, 0x0b);
+ err += sn9c102_i2c_write(cam, 0x71, 0x00);
+ err += sn9c102_i2c_write(cam, 0x74, 0x21);
+ err += sn9c102_i2c_write(cam, 0x7d, 0xf7);
+ break;
+ case BRIDGE_SN9C105:
+ case BRIDGE_SN9C120:
+ err = sn9c102_write_const_regs(cam, {0x40, 0x02}, {0x00, 0x03},
+ {0x1a, 0x04}, {0x03, 0x10},
+ {0x0a, 0x14}, {0xe2, 0x17},
+ {0x0b, 0x18}, {0x00, 0x19},
+ {0x1d, 0x1a}, {0x10, 0x1b},
+ {0x02, 0x1c}, {0x03, 0x1d},
+ {0x0f, 0x1e}, {0x0c, 0x1f},
+ {0x00, 0x20}, {0x24, 0x21},
+ {0x3b, 0x22}, {0x47, 0x23},
+ {0x60, 0x24}, {0x71, 0x25},
+ {0x80, 0x26}, {0x8f, 0x27},
+ {0x9d, 0x28}, {0xaa, 0x29},
+ {0xb8, 0x2a}, {0xc4, 0x2b},
+ {0xd1, 0x2c}, {0xdd, 0x2d},
+ {0xe8, 0x2e}, {0xf4, 0x2f},
+ {0xff, 0x30}, {0x00, 0x3f},
+ {0xc7, 0x40}, {0x01, 0x41},
+ {0x44, 0x42}, {0x00, 0x43},
+ {0x44, 0x44}, {0x00, 0x45},
+ {0x44, 0x46}, {0x00, 0x47},
+ {0xc7, 0x48}, {0x01, 0x49},
+ {0xc7, 0x4a}, {0x01, 0x4b},
+ {0xc7, 0x4c}, {0x01, 0x4d},
+ {0x44, 0x4e}, {0x00, 0x4f},
+ {0x44, 0x50}, {0x00, 0x51},
+ {0x44, 0x52}, {0x00, 0x53},
+ {0xc7, 0x54}, {0x01, 0x55},
+ {0xc7, 0x56}, {0x01, 0x57},
+ {0xc7, 0x58}, {0x01, 0x59},
+ {0x44, 0x5a}, {0x00, 0x5b},
+ {0x44, 0x5c}, {0x00, 0x5d},
+ {0x44, 0x5e}, {0x00, 0x5f},
+ {0xc7, 0x60}, {0x01, 0x61},
+ {0xc7, 0x62}, {0x01, 0x63},
+ {0xc7, 0x64}, {0x01, 0x65},
+ {0x44, 0x66}, {0x00, 0x67},
+ {0x44, 0x68}, {0x00, 0x69},
+ {0x44, 0x6a}, {0x00, 0x6b},
+ {0xc7, 0x6c}, {0x01, 0x6d},
+ {0xc7, 0x6e}, {0x01, 0x6f},
+ {0xc7, 0x70}, {0x01, 0x71},
+ {0x44, 0x72}, {0x00, 0x73},
+ {0x44, 0x74}, {0x00, 0x75},
+ {0x44, 0x76}, {0x00, 0x77},
+ {0xc7, 0x78}, {0x01, 0x79},
+ {0xc7, 0x7a}, {0x01, 0x7b},
+ {0xc7, 0x7c}, {0x01, 0x7d},
+ {0x44, 0x7e}, {0x00, 0x7f},
+ {0x17, 0x84}, {0x00, 0x85},
+ {0x2e, 0x86}, {0x00, 0x87},
+ {0x09, 0x88}, {0x00, 0x89},
+ {0xe8, 0x8a}, {0x0f, 0x8b},
+ {0xda, 0x8c}, {0x0f, 0x8d},
+ {0x40, 0x8e}, {0x00, 0x8f},
+ {0x37, 0x90}, {0x00, 0x91},
+ {0xcf, 0x92}, {0x0f, 0x93},
+ {0xfa, 0x94}, {0x0f, 0x95},
+ {0x00, 0x96}, {0x00, 0x97},
+ {0x00, 0x98}, {0x66, 0x99},
+ {0x00, 0x9a}, {0x40, 0x9b},
+ {0x20, 0x9c}, {0x00, 0x9d},
+ {0x00, 0x9e}, {0x00, 0x9f},
+ {0x2d, 0xc0}, {0x2d, 0xc1},
+ {0x3a, 0xc2}, {0x00, 0xc3},
+ {0x04, 0xc4}, {0x3f, 0xc5},
+ {0x00, 0xc6}, {0x00, 0xc7},
+ {0x50, 0xc8}, {0x3c, 0xc9},
+ {0x28, 0xca}, {0xd8, 0xcb},
+ {0x14, 0xcc}, {0xec, 0xcd},
+ {0x32, 0xce}, {0xdd, 0xcf},
+ {0x32, 0xd0}, {0xdd, 0xd1},
+ {0x6a, 0xd2}, {0x50, 0xd3},
+ {0x60, 0xd4}, {0x00, 0xd5},
+ {0x00, 0xd6});
+
+ err += sn9c102_i2c_write(cam, 0x12, 0x80);
+ err += sn9c102_i2c_write(cam, 0x12, 0x48);
+ err += sn9c102_i2c_write(cam, 0x01, 0x80);
+ err += sn9c102_i2c_write(cam, 0x02, 0x80);
+ err += sn9c102_i2c_write(cam, 0x03, 0x80);
+ err += sn9c102_i2c_write(cam, 0x04, 0x10);
+ err += sn9c102_i2c_write(cam, 0x05, 0x20);
+ err += sn9c102_i2c_write(cam, 0x06, 0x80);
+ err += sn9c102_i2c_write(cam, 0x11, 0x00);
+ err += sn9c102_i2c_write(cam, 0x0c, 0x20);
+ err += sn9c102_i2c_write(cam, 0x0d, 0x20);
+ err += sn9c102_i2c_write(cam, 0x15, 0x80);
+ err += sn9c102_i2c_write(cam, 0x16, 0x03);
+ err += sn9c102_i2c_write(cam, 0x17, 0x1b);
+ err += sn9c102_i2c_write(cam, 0x18, 0xbd);
+ err += sn9c102_i2c_write(cam, 0x19, 0x05);
+ err += sn9c102_i2c_write(cam, 0x1a, 0xf6);
+ err += sn9c102_i2c_write(cam, 0x1b, 0x04);
+ err += sn9c102_i2c_write(cam, 0x21, 0x1b);
+ err += sn9c102_i2c_write(cam, 0x22, 0x00);
+ err += sn9c102_i2c_write(cam, 0x23, 0xde);
+ err += sn9c102_i2c_write(cam, 0x24, 0x10);
+ err += sn9c102_i2c_write(cam, 0x25, 0x8a);
+ err += sn9c102_i2c_write(cam, 0x26, 0xa0);
+ err += sn9c102_i2c_write(cam, 0x27, 0xca);
+ err += sn9c102_i2c_write(cam, 0x28, 0xa2);
+ err += sn9c102_i2c_write(cam, 0x29, 0x74);
+ err += sn9c102_i2c_write(cam, 0x2a, 0x88);
+ err += sn9c102_i2c_write(cam, 0x2b, 0x34);
+ err += sn9c102_i2c_write(cam, 0x2c, 0x88);
+ err += sn9c102_i2c_write(cam, 0x2e, 0x00);
+ err += sn9c102_i2c_write(cam, 0x2f, 0x00);
+ err += sn9c102_i2c_write(cam, 0x30, 0x00);
+ err += sn9c102_i2c_write(cam, 0x32, 0xc2);
+ err += sn9c102_i2c_write(cam, 0x33, 0x08);
+ err += sn9c102_i2c_write(cam, 0x4c, 0x40);
+ err += sn9c102_i2c_write(cam, 0x4d, 0xf3);
+ err += sn9c102_i2c_write(cam, 0x60, 0x05);
+ err += sn9c102_i2c_write(cam, 0x61, 0x40);
+ err += sn9c102_i2c_write(cam, 0x62, 0x12);
+ err += sn9c102_i2c_write(cam, 0x63, 0x57);
+ err += sn9c102_i2c_write(cam, 0x64, 0x73);
+ err += sn9c102_i2c_write(cam, 0x65, 0x00);
+ err += sn9c102_i2c_write(cam, 0x66, 0x55);
+ err += sn9c102_i2c_write(cam, 0x67, 0x01);
+ err += sn9c102_i2c_write(cam, 0x68, 0xac);
+ err += sn9c102_i2c_write(cam, 0x69, 0x38);
+ err += sn9c102_i2c_write(cam, 0x6f, 0x1f);
+ err += sn9c102_i2c_write(cam, 0x70, 0x01);
+ err += sn9c102_i2c_write(cam, 0x71, 0x00);
+ err += sn9c102_i2c_write(cam, 0x72, 0x10);
+ err += sn9c102_i2c_write(cam, 0x73, 0x50);
+ err += sn9c102_i2c_write(cam, 0x74, 0x20);
+ err += sn9c102_i2c_write(cam, 0x76, 0x01);
+ err += sn9c102_i2c_write(cam, 0x77, 0xf3);
+ err += sn9c102_i2c_write(cam, 0x78, 0x90);
+ err += sn9c102_i2c_write(cam, 0x79, 0x98);
+ err += sn9c102_i2c_write(cam, 0x7a, 0x98);
+ err += sn9c102_i2c_write(cam, 0x7b, 0x00);
+ err += sn9c102_i2c_write(cam, 0x7c, 0x38);
+ err += sn9c102_i2c_write(cam, 0x7d, 0xff);
+ break;
+ default:
+ break;
+ }
+
+ return err;
+}
+
+
+static int ov7630_get_ctrl(struct sn9c102_device* cam,
+ struct v4l2_control* ctrl)
+{
+ enum sn9c102_bridge bridge = sn9c102_get_bridge(cam);
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0)
+ return -EIO;
+ break;
+ case V4L2_CID_RED_BALANCE:
+ if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120)
+ ctrl->value = sn9c102_pread_reg(cam, 0x05);
+ else
+ ctrl->value = sn9c102_pread_reg(cam, 0x07);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ ctrl->value = sn9c102_pread_reg(cam, 0x06);
+ break;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120)
+ ctrl->value = sn9c102_pread_reg(cam, 0x07);
+ else
+ ctrl->value = sn9c102_pread_reg(cam, 0x05);
+ break;
+ break;
+ case V4L2_CID_GAIN:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x00)) < 0)
+ return -EIO;
+ ctrl->value &= 0x3f;
+ break;
+ case V4L2_CID_DO_WHITE_BALANCE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0)
+ return -EIO;
+ ctrl->value &= 0x3f;
+ break;
+ case V4L2_CID_WHITENESS:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x0d)) < 0)
+ return -EIO;
+ ctrl->value &= 0x3f;
+ break;
+ case V4L2_CID_AUTOGAIN:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x13)) < 0)
+ return -EIO;
+ ctrl->value &= 0x01;
+ break;
+ case V4L2_CID_VFLIP:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x75)) < 0)
+ return -EIO;
+ ctrl->value = (ctrl->value & 0x80) ? 1 : 0;
+ break;
+ case SN9C102_V4L2_CID_GAMMA:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x14)) < 0)
+ return -EIO;
+ ctrl->value = (ctrl->value & 0x02) ? 1 : 0;
+ break;
+ case SN9C102_V4L2_CID_BAND_FILTER:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x2d)) < 0)
+ return -EIO;
+ ctrl->value = (ctrl->value & 0x02) ? 1 : 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return err ? -EIO : 0;
+}
+
+
+static int ov7630_set_ctrl(struct sn9c102_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ enum sn9c102_bridge bridge = sn9c102_get_bridge(cam);
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ err += sn9c102_i2c_write(cam, 0x10, ctrl->value);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120)
+ err += sn9c102_write_reg(cam, ctrl->value, 0x05);
+ else
+ err += sn9c102_write_reg(cam, ctrl->value, 0x07);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ err += sn9c102_write_reg(cam, ctrl->value, 0x06);
+ break;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120)
+ err += sn9c102_write_reg(cam, ctrl->value, 0x07);
+ else
+ err += sn9c102_write_reg(cam, ctrl->value, 0x05);
+ break;
+ case V4L2_CID_GAIN:
+ err += sn9c102_i2c_write(cam, 0x00, ctrl->value);
+ break;
+ case V4L2_CID_DO_WHITE_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x0c, ctrl->value);
+ break;
+ case V4L2_CID_WHITENESS:
+ err += sn9c102_i2c_write(cam, 0x0d, ctrl->value);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ err += sn9c102_i2c_write(cam, 0x13, ctrl->value |
+ (ctrl->value << 1));
+ break;
+ case V4L2_CID_VFLIP:
+ err += sn9c102_i2c_write(cam, 0x75, 0x0e | (ctrl->value << 7));
+ break;
+ case SN9C102_V4L2_CID_GAMMA:
+ err += sn9c102_i2c_write(cam, 0x14, ctrl->value << 2);
+ break;
+ case SN9C102_V4L2_CID_BAND_FILTER:
+ err += sn9c102_i2c_write(cam, 0x2d, ctrl->value << 2);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return err ? -EIO : 0;
+}
+
+
+static int ov7630_set_crop(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+ int err = 0;
+ u8 h_start = 0, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1;
+
+ switch (sn9c102_get_bridge(cam)) {
+ case BRIDGE_SN9C101:
+ case BRIDGE_SN9C102:
+ case BRIDGE_SN9C103:
+ h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1;
+ break;
+ case BRIDGE_SN9C105:
+ case BRIDGE_SN9C120:
+ h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4;
+ break;
+ default:
+ break;
+ }
+
+ err += sn9c102_write_reg(cam, h_start, 0x12);
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+
+ return err;
+}
+
+
+static int ov7630_set_pix_format(struct sn9c102_device* cam,
+ const struct v4l2_pix_format* pix)
+{
+ int err = 0;
+
+ switch (sn9c102_get_bridge(cam)) {
+ case BRIDGE_SN9C101:
+ case BRIDGE_SN9C102:
+ case BRIDGE_SN9C103:
+ if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8)
+ err += sn9c102_write_reg(cam, 0x50, 0x19);
+ else
+ err += sn9c102_write_reg(cam, 0x20, 0x19);
+ break;
+ case BRIDGE_SN9C105:
+ case BRIDGE_SN9C120:
+ if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) {
+ err += sn9c102_write_reg(cam, 0xe5, 0x17);
+ err += sn9c102_i2c_write(cam, 0x11, 0x04);
+ } else {
+ err += sn9c102_write_reg(cam, 0xe2, 0x17);
+ err += sn9c102_i2c_write(cam, 0x11, 0x02);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return err;
+}
+
+
+static const struct sn9c102_sensor ov7630 = {
+ .name = "OV7630",
+ .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+ .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103 |
+ BRIDGE_SN9C105 | BRIDGE_SN9C120,
+ .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE,
+ .frequency = SN9C102_I2C_100KHZ,
+ .interface = SN9C102_I2C_2WIRES,
+ .i2c_slave_id = 0x21,
+ .init = &ov7630_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "global gain",
+ .minimum = 0x00,
+ .maximum = 0x3f,
+ .step = 0x01,
+ .default_value = 0x14,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x01,
+ .default_value = 0x60,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_WHITENESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "white balance background: red",
+ .minimum = 0x00,
+ .maximum = 0x3f,
+ .step = 0x01,
+ .default_value = 0x20,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_DO_WHITE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "white balance background: blue",
+ .minimum = 0x00,
+ .maximum = 0x3f,
+ .step = 0x01,
+ .default_value = 0x20,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "red balance",
+ .minimum = 0x00,
+ .maximum = 0x7f,
+ .step = 0x01,
+ .default_value = 0x20,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "blue balance",
+ .minimum = 0x00,
+ .maximum = 0x7f,
+ .step = 0x01,
+ .default_value = 0x20,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "auto adjust",
+ .minimum = 0x00,
+ .maximum = 0x01,
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "vertical flip",
+ .minimum = 0x00,
+ .maximum = 0x01,
+ .step = 0x01,
+ .default_value = 0x01,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_GREEN_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "green balance",
+ .minimum = 0x00,
+ .maximum = 0x7f,
+ .step = 0x01,
+ .default_value = 0x20,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_BAND_FILTER,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "band filter",
+ .minimum = 0x00,
+ .maximum = 0x01,
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_GAMMA,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "rgb gamma",
+ .minimum = 0x00,
+ .maximum = 0x01,
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ },
+ .get_ctrl = &ov7630_get_ctrl,
+ .set_ctrl = &ov7630_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ .set_crop = &ov7630_set_crop,
+ .pix_format = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_SN9C10X,
+ .priv = 8,
+ },
+ .set_pix_format = &ov7630_set_pix_format
+};
+
+
+int sn9c102_probe_ov7630(struct sn9c102_device* cam)
+{
+ int pid, ver, err = 0;
+
+ switch (sn9c102_get_bridge(cam)) {
+ case BRIDGE_SN9C101:
+ case BRIDGE_SN9C102:
+ err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01},
+ {0x28, 0x17});
+ break;
+ case BRIDGE_SN9C103: /* do _not_ change anything! */
+ err = sn9c102_write_const_regs(cam, {0x09, 0x01}, {0x42, 0x01},
+ {0x28, 0x17}, {0x44, 0x02});
+ pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a);
+ if (err || pid < 0) /* try a different initialization */
+ err += sn9c102_write_const_regs(cam, {0x01, 0x01},
+ {0x00, 0x01});
+ break;
+ case BRIDGE_SN9C105:
+ case BRIDGE_SN9C120:
+ err = sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1},
+ {0x29, 0x01}, {0x74, 0x02},
+ {0x0e, 0x01}, {0x44, 0x01});
+ break;
+ default:
+ break;
+ }
+
+ pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a);
+ ver = sn9c102_i2c_try_read(cam, &ov7630, 0x0b);
+ if (err || pid < 0 || ver < 0)
+ return -EIO;
+ if (pid != 0x76 || ver != 0x31)
+ return -ENODEV;
+ sn9c102_attach_sensor(cam, &ov7630);
+
+ return 0;
+}
diff --git a/drivers/media/video/sn9c102/sn9c102_ov7660.c b/drivers/media/video/sn9c102/sn9c102_ov7660.c
new file mode 100644
index 00000000..7977795d
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_ov7660.c
@@ -0,0 +1,538 @@
+/***************************************************************************
+ * Plug-in for OV7660 image sensor connected to the SN9C1xx PC Camera *
+ * Controllers *
+ * *
+ * Copyright (C) 2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include "sn9c102_sensor.h"
+#include "sn9c102_devtable.h"
+
+
+static int ov7660_init(struct sn9c102_device* cam)
+{
+ int err = 0;
+
+ err = sn9c102_write_const_regs(cam, {0x40, 0x02}, {0x00, 0x03},
+ {0x1a, 0x04}, {0x03, 0x10},
+ {0x08, 0x14}, {0x20, 0x17},
+ {0x8b, 0x18}, {0x00, 0x19},
+ {0x1d, 0x1a}, {0x10, 0x1b},
+ {0x02, 0x1c}, {0x03, 0x1d},
+ {0x0f, 0x1e}, {0x0c, 0x1f},
+ {0x00, 0x20}, {0x29, 0x21},
+ {0x40, 0x22}, {0x54, 0x23},
+ {0x66, 0x24}, {0x76, 0x25},
+ {0x85, 0x26}, {0x94, 0x27},
+ {0xa1, 0x28}, {0xae, 0x29},
+ {0xbb, 0x2a}, {0xc7, 0x2b},
+ {0xd3, 0x2c}, {0xde, 0x2d},
+ {0xea, 0x2e}, {0xf4, 0x2f},
+ {0xff, 0x30}, {0x00, 0x3f},
+ {0xc7, 0x40}, {0x01, 0x41},
+ {0x44, 0x42}, {0x00, 0x43},
+ {0x44, 0x44}, {0x00, 0x45},
+ {0x44, 0x46}, {0x00, 0x47},
+ {0xc7, 0x48}, {0x01, 0x49},
+ {0xc7, 0x4a}, {0x01, 0x4b},
+ {0xc7, 0x4c}, {0x01, 0x4d},
+ {0x44, 0x4e}, {0x00, 0x4f},
+ {0x44, 0x50}, {0x00, 0x51},
+ {0x44, 0x52}, {0x00, 0x53},
+ {0xc7, 0x54}, {0x01, 0x55},
+ {0xc7, 0x56}, {0x01, 0x57},
+ {0xc7, 0x58}, {0x01, 0x59},
+ {0x44, 0x5a}, {0x00, 0x5b},
+ {0x44, 0x5c}, {0x00, 0x5d},
+ {0x44, 0x5e}, {0x00, 0x5f},
+ {0xc7, 0x60}, {0x01, 0x61},
+ {0xc7, 0x62}, {0x01, 0x63},
+ {0xc7, 0x64}, {0x01, 0x65},
+ {0x44, 0x66}, {0x00, 0x67},
+ {0x44, 0x68}, {0x00, 0x69},
+ {0x44, 0x6a}, {0x00, 0x6b},
+ {0xc7, 0x6c}, {0x01, 0x6d},
+ {0xc7, 0x6e}, {0x01, 0x6f},
+ {0xc7, 0x70}, {0x01, 0x71},
+ {0x44, 0x72}, {0x00, 0x73},
+ {0x44, 0x74}, {0x00, 0x75},
+ {0x44, 0x76}, {0x00, 0x77},
+ {0xc7, 0x78}, {0x01, 0x79},
+ {0xc7, 0x7a}, {0x01, 0x7b},
+ {0xc7, 0x7c}, {0x01, 0x7d},
+ {0x44, 0x7e}, {0x00, 0x7f},
+ {0x14, 0x84}, {0x00, 0x85},
+ {0x27, 0x86}, {0x00, 0x87},
+ {0x07, 0x88}, {0x00, 0x89},
+ {0xec, 0x8a}, {0x0f, 0x8b},
+ {0xd8, 0x8c}, {0x0f, 0x8d},
+ {0x3d, 0x8e}, {0x00, 0x8f},
+ {0x3d, 0x90}, {0x00, 0x91},
+ {0xcd, 0x92}, {0x0f, 0x93},
+ {0xf7, 0x94}, {0x0f, 0x95},
+ {0x0c, 0x96}, {0x00, 0x97},
+ {0x00, 0x98}, {0x66, 0x99},
+ {0x05, 0x9a}, {0x00, 0x9b},
+ {0x04, 0x9c}, {0x00, 0x9d},
+ {0x08, 0x9e}, {0x00, 0x9f},
+ {0x2d, 0xc0}, {0x2d, 0xc1},
+ {0x3a, 0xc2}, {0x05, 0xc3},
+ {0x04, 0xc4}, {0x3f, 0xc5},
+ {0x00, 0xc6}, {0x00, 0xc7},
+ {0x50, 0xc8}, {0x3C, 0xc9},
+ {0x28, 0xca}, {0xd8, 0xcb},
+ {0x14, 0xcc}, {0xec, 0xcd},
+ {0x32, 0xce}, {0xdd, 0xcf},
+ {0x32, 0xd0}, {0xdd, 0xd1},
+ {0x6a, 0xd2}, {0x50, 0xd3},
+ {0x00, 0xd4}, {0x00, 0xd5},
+ {0x00, 0xd6});
+
+ err += sn9c102_i2c_write(cam, 0x12, 0x80);
+ err += sn9c102_i2c_write(cam, 0x11, 0x09);
+ err += sn9c102_i2c_write(cam, 0x00, 0x0A);
+ err += sn9c102_i2c_write(cam, 0x01, 0x80);
+ err += sn9c102_i2c_write(cam, 0x02, 0x80);
+ err += sn9c102_i2c_write(cam, 0x03, 0x00);
+ err += sn9c102_i2c_write(cam, 0x04, 0x00);
+ err += sn9c102_i2c_write(cam, 0x05, 0x08);
+ err += sn9c102_i2c_write(cam, 0x06, 0x0B);
+ err += sn9c102_i2c_write(cam, 0x07, 0x00);
+ err += sn9c102_i2c_write(cam, 0x08, 0x1C);
+ err += sn9c102_i2c_write(cam, 0x09, 0x01);
+ err += sn9c102_i2c_write(cam, 0x0A, 0x76);
+ err += sn9c102_i2c_write(cam, 0x0B, 0x60);
+ err += sn9c102_i2c_write(cam, 0x0C, 0x00);
+ err += sn9c102_i2c_write(cam, 0x0D, 0x08);
+ err += sn9c102_i2c_write(cam, 0x0E, 0x04);
+ err += sn9c102_i2c_write(cam, 0x0F, 0x6F);
+ err += sn9c102_i2c_write(cam, 0x10, 0x20);
+ err += sn9c102_i2c_write(cam, 0x11, 0x03);
+ err += sn9c102_i2c_write(cam, 0x12, 0x05);
+ err += sn9c102_i2c_write(cam, 0x13, 0xC7);
+ err += sn9c102_i2c_write(cam, 0x14, 0x2C);
+ err += sn9c102_i2c_write(cam, 0x15, 0x00);
+ err += sn9c102_i2c_write(cam, 0x16, 0x02);
+ err += sn9c102_i2c_write(cam, 0x17, 0x10);
+ err += sn9c102_i2c_write(cam, 0x18, 0x60);
+ err += sn9c102_i2c_write(cam, 0x19, 0x02);
+ err += sn9c102_i2c_write(cam, 0x1A, 0x7B);
+ err += sn9c102_i2c_write(cam, 0x1B, 0x02);
+ err += sn9c102_i2c_write(cam, 0x1C, 0x7F);
+ err += sn9c102_i2c_write(cam, 0x1D, 0xA2);
+ err += sn9c102_i2c_write(cam, 0x1E, 0x01);
+ err += sn9c102_i2c_write(cam, 0x1F, 0x0E);
+ err += sn9c102_i2c_write(cam, 0x20, 0x05);
+ err += sn9c102_i2c_write(cam, 0x21, 0x05);
+ err += sn9c102_i2c_write(cam, 0x22, 0x05);
+ err += sn9c102_i2c_write(cam, 0x23, 0x05);
+ err += sn9c102_i2c_write(cam, 0x24, 0x68);
+ err += sn9c102_i2c_write(cam, 0x25, 0x58);
+ err += sn9c102_i2c_write(cam, 0x26, 0xD4);
+ err += sn9c102_i2c_write(cam, 0x27, 0x80);
+ err += sn9c102_i2c_write(cam, 0x28, 0x80);
+ err += sn9c102_i2c_write(cam, 0x29, 0x30);
+ err += sn9c102_i2c_write(cam, 0x2A, 0x00);
+ err += sn9c102_i2c_write(cam, 0x2B, 0x00);
+ err += sn9c102_i2c_write(cam, 0x2C, 0x80);
+ err += sn9c102_i2c_write(cam, 0x2D, 0x00);
+ err += sn9c102_i2c_write(cam, 0x2E, 0x00);
+ err += sn9c102_i2c_write(cam, 0x2F, 0x0E);
+ err += sn9c102_i2c_write(cam, 0x30, 0x08);
+ err += sn9c102_i2c_write(cam, 0x31, 0x30);
+ err += sn9c102_i2c_write(cam, 0x32, 0xB4);
+ err += sn9c102_i2c_write(cam, 0x33, 0x00);
+ err += sn9c102_i2c_write(cam, 0x34, 0x07);
+ err += sn9c102_i2c_write(cam, 0x35, 0x84);
+ err += sn9c102_i2c_write(cam, 0x36, 0x00);
+ err += sn9c102_i2c_write(cam, 0x37, 0x0C);
+ err += sn9c102_i2c_write(cam, 0x38, 0x02);
+ err += sn9c102_i2c_write(cam, 0x39, 0x43);
+ err += sn9c102_i2c_write(cam, 0x3A, 0x00);
+ err += sn9c102_i2c_write(cam, 0x3B, 0x0A);
+ err += sn9c102_i2c_write(cam, 0x3C, 0x6C);
+ err += sn9c102_i2c_write(cam, 0x3D, 0x99);
+ err += sn9c102_i2c_write(cam, 0x3E, 0x0E);
+ err += sn9c102_i2c_write(cam, 0x3F, 0x41);
+ err += sn9c102_i2c_write(cam, 0x40, 0xC1);
+ err += sn9c102_i2c_write(cam, 0x41, 0x22);
+ err += sn9c102_i2c_write(cam, 0x42, 0x08);
+ err += sn9c102_i2c_write(cam, 0x43, 0xF0);
+ err += sn9c102_i2c_write(cam, 0x44, 0x10);
+ err += sn9c102_i2c_write(cam, 0x45, 0x78);
+ err += sn9c102_i2c_write(cam, 0x46, 0xA8);
+ err += sn9c102_i2c_write(cam, 0x47, 0x60);
+ err += sn9c102_i2c_write(cam, 0x48, 0x80);
+ err += sn9c102_i2c_write(cam, 0x49, 0x00);
+ err += sn9c102_i2c_write(cam, 0x4A, 0x00);
+ err += sn9c102_i2c_write(cam, 0x4B, 0x00);
+ err += sn9c102_i2c_write(cam, 0x4C, 0x00);
+ err += sn9c102_i2c_write(cam, 0x4D, 0x00);
+ err += sn9c102_i2c_write(cam, 0x4E, 0x00);
+ err += sn9c102_i2c_write(cam, 0x4F, 0x46);
+ err += sn9c102_i2c_write(cam, 0x50, 0x36);
+ err += sn9c102_i2c_write(cam, 0x51, 0x0F);
+ err += sn9c102_i2c_write(cam, 0x52, 0x17);
+ err += sn9c102_i2c_write(cam, 0x53, 0x7F);
+ err += sn9c102_i2c_write(cam, 0x54, 0x96);
+ err += sn9c102_i2c_write(cam, 0x55, 0x40);
+ err += sn9c102_i2c_write(cam, 0x56, 0x40);
+ err += sn9c102_i2c_write(cam, 0x57, 0x40);
+ err += sn9c102_i2c_write(cam, 0x58, 0x0F);
+ err += sn9c102_i2c_write(cam, 0x59, 0xBA);
+ err += sn9c102_i2c_write(cam, 0x5A, 0x9A);
+ err += sn9c102_i2c_write(cam, 0x5B, 0x22);
+ err += sn9c102_i2c_write(cam, 0x5C, 0xB9);
+ err += sn9c102_i2c_write(cam, 0x5D, 0x9B);
+ err += sn9c102_i2c_write(cam, 0x5E, 0x10);
+ err += sn9c102_i2c_write(cam, 0x5F, 0xF0);
+ err += sn9c102_i2c_write(cam, 0x60, 0x05);
+ err += sn9c102_i2c_write(cam, 0x61, 0x60);
+ err += sn9c102_i2c_write(cam, 0x62, 0x00);
+ err += sn9c102_i2c_write(cam, 0x63, 0x00);
+ err += sn9c102_i2c_write(cam, 0x64, 0x50);
+ err += sn9c102_i2c_write(cam, 0x65, 0x30);
+ err += sn9c102_i2c_write(cam, 0x66, 0x00);
+ err += sn9c102_i2c_write(cam, 0x67, 0x80);
+ err += sn9c102_i2c_write(cam, 0x68, 0x7A);
+ err += sn9c102_i2c_write(cam, 0x69, 0x90);
+ err += sn9c102_i2c_write(cam, 0x6A, 0x80);
+ err += sn9c102_i2c_write(cam, 0x6B, 0x0A);
+ err += sn9c102_i2c_write(cam, 0x6C, 0x30);
+ err += sn9c102_i2c_write(cam, 0x6D, 0x48);
+ err += sn9c102_i2c_write(cam, 0x6E, 0x80);
+ err += sn9c102_i2c_write(cam, 0x6F, 0x74);
+ err += sn9c102_i2c_write(cam, 0x70, 0x64);
+ err += sn9c102_i2c_write(cam, 0x71, 0x60);
+ err += sn9c102_i2c_write(cam, 0x72, 0x5C);
+ err += sn9c102_i2c_write(cam, 0x73, 0x58);
+ err += sn9c102_i2c_write(cam, 0x74, 0x54);
+ err += sn9c102_i2c_write(cam, 0x75, 0x4C);
+ err += sn9c102_i2c_write(cam, 0x76, 0x40);
+ err += sn9c102_i2c_write(cam, 0x77, 0x38);
+ err += sn9c102_i2c_write(cam, 0x78, 0x34);
+ err += sn9c102_i2c_write(cam, 0x79, 0x30);
+ err += sn9c102_i2c_write(cam, 0x7A, 0x2F);
+ err += sn9c102_i2c_write(cam, 0x7B, 0x2B);
+ err += sn9c102_i2c_write(cam, 0x7C, 0x03);
+ err += sn9c102_i2c_write(cam, 0x7D, 0x07);
+ err += sn9c102_i2c_write(cam, 0x7E, 0x17);
+ err += sn9c102_i2c_write(cam, 0x7F, 0x34);
+ err += sn9c102_i2c_write(cam, 0x80, 0x41);
+ err += sn9c102_i2c_write(cam, 0x81, 0x4D);
+ err += sn9c102_i2c_write(cam, 0x82, 0x58);
+ err += sn9c102_i2c_write(cam, 0x83, 0x63);
+ err += sn9c102_i2c_write(cam, 0x84, 0x6E);
+ err += sn9c102_i2c_write(cam, 0x85, 0x77);
+ err += sn9c102_i2c_write(cam, 0x86, 0x87);
+ err += sn9c102_i2c_write(cam, 0x87, 0x95);
+ err += sn9c102_i2c_write(cam, 0x88, 0xAF);
+ err += sn9c102_i2c_write(cam, 0x89, 0xC7);
+ err += sn9c102_i2c_write(cam, 0x8A, 0xDF);
+ err += sn9c102_i2c_write(cam, 0x8B, 0x99);
+ err += sn9c102_i2c_write(cam, 0x8C, 0x99);
+ err += sn9c102_i2c_write(cam, 0x8D, 0xCF);
+ err += sn9c102_i2c_write(cam, 0x8E, 0x20);
+ err += sn9c102_i2c_write(cam, 0x8F, 0x26);
+ err += sn9c102_i2c_write(cam, 0x90, 0x10);
+ err += sn9c102_i2c_write(cam, 0x91, 0x0C);
+ err += sn9c102_i2c_write(cam, 0x92, 0x25);
+ err += sn9c102_i2c_write(cam, 0x93, 0x00);
+ err += sn9c102_i2c_write(cam, 0x94, 0x50);
+ err += sn9c102_i2c_write(cam, 0x95, 0x50);
+ err += sn9c102_i2c_write(cam, 0x96, 0x00);
+ err += sn9c102_i2c_write(cam, 0x97, 0x01);
+ err += sn9c102_i2c_write(cam, 0x98, 0x10);
+ err += sn9c102_i2c_write(cam, 0x99, 0x40);
+ err += sn9c102_i2c_write(cam, 0x9A, 0x40);
+ err += sn9c102_i2c_write(cam, 0x9B, 0x20);
+ err += sn9c102_i2c_write(cam, 0x9C, 0x00);
+ err += sn9c102_i2c_write(cam, 0x9D, 0x99);
+ err += sn9c102_i2c_write(cam, 0x9E, 0x7F);
+ err += sn9c102_i2c_write(cam, 0x9F, 0x00);
+ err += sn9c102_i2c_write(cam, 0xA0, 0x00);
+ err += sn9c102_i2c_write(cam, 0xA1, 0x00);
+
+ return err;
+}
+
+
+static int ov7660_get_ctrl(struct sn9c102_device* cam,
+ struct v4l2_control* ctrl)
+{
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0)
+ return -EIO;
+ break;
+ case V4L2_CID_DO_WHITE_BALANCE:
+ if ((ctrl->value = sn9c102_read_reg(cam, 0x02)) < 0)
+ return -EIO;
+ ctrl->value = (ctrl->value & 0x04) ? 1 : 0;
+ break;
+ case V4L2_CID_RED_BALANCE:
+ if ((ctrl->value = sn9c102_read_reg(cam, 0x05)) < 0)
+ return -EIO;
+ ctrl->value &= 0x7f;
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ if ((ctrl->value = sn9c102_read_reg(cam, 0x06)) < 0)
+ return -EIO;
+ ctrl->value &= 0x7f;
+ break;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ if ((ctrl->value = sn9c102_read_reg(cam, 0x07)) < 0)
+ return -EIO;
+ ctrl->value &= 0x7f;
+ break;
+ case SN9C102_V4L2_CID_BAND_FILTER:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x3b)) < 0)
+ return -EIO;
+ ctrl->value &= 0x08;
+ break;
+ case V4L2_CID_GAIN:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x00)) < 0)
+ return -EIO;
+ ctrl->value &= 0x1f;
+ break;
+ case V4L2_CID_AUTOGAIN:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x13)) < 0)
+ return -EIO;
+ ctrl->value &= 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return err ? -EIO : 0;
+}
+
+
+static int ov7660_set_ctrl(struct sn9c102_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ err += sn9c102_i2c_write(cam, 0x10, ctrl->value);
+ break;
+ case V4L2_CID_DO_WHITE_BALANCE:
+ err += sn9c102_write_reg(cam, 0x43 | (ctrl->value << 2), 0x02);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ err += sn9c102_write_reg(cam, ctrl->value, 0x05);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ err += sn9c102_write_reg(cam, ctrl->value, 0x06);
+ break;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ err += sn9c102_write_reg(cam, ctrl->value, 0x07);
+ break;
+ case SN9C102_V4L2_CID_BAND_FILTER:
+ err += sn9c102_i2c_write(cam, ctrl->value << 3, 0x3b);
+ break;
+ case V4L2_CID_GAIN:
+ err += sn9c102_i2c_write(cam, 0x00, 0x60 + ctrl->value);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ err += sn9c102_i2c_write(cam, 0x13, 0xc0 |
+ (ctrl->value * 0x07));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return err ? -EIO : 0;
+}
+
+
+static int ov7660_set_crop(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+ int err = 0;
+ u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1,
+ v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1;
+
+ err += sn9c102_write_reg(cam, h_start, 0x12);
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+
+ return err;
+}
+
+
+static int ov7660_set_pix_format(struct sn9c102_device* cam,
+ const struct v4l2_pix_format* pix)
+{
+ int r0, err = 0;
+
+ r0 = sn9c102_pread_reg(cam, 0x01);
+
+ if (pix->pixelformat == V4L2_PIX_FMT_JPEG) {
+ err += sn9c102_write_reg(cam, r0 | 0x40, 0x01);
+ err += sn9c102_write_reg(cam, 0xa2, 0x17);
+ err += sn9c102_i2c_write(cam, 0x11, 0x00);
+ } else {
+ err += sn9c102_write_reg(cam, r0 | 0x40, 0x01);
+ err += sn9c102_write_reg(cam, 0xa2, 0x17);
+ err += sn9c102_i2c_write(cam, 0x11, 0x0d);
+ }
+
+ return err;
+}
+
+
+static const struct sn9c102_sensor ov7660 = {
+ .name = "OV7660",
+ .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+ .supported_bridge = BRIDGE_SN9C105 | BRIDGE_SN9C120,
+ .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE,
+ .frequency = SN9C102_I2C_100KHZ,
+ .interface = SN9C102_I2C_2WIRES,
+ .i2c_slave_id = 0x21,
+ .init = &ov7660_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "global gain",
+ .minimum = 0x00,
+ .maximum = 0x1f,
+ .step = 0x01,
+ .default_value = 0x09,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x01,
+ .default_value = 0x27,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_DO_WHITE_BALANCE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "night mode",
+ .minimum = 0x00,
+ .maximum = 0x01,
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "red balance",
+ .minimum = 0x00,
+ .maximum = 0x7f,
+ .step = 0x01,
+ .default_value = 0x14,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "blue balance",
+ .minimum = 0x00,
+ .maximum = 0x7f,
+ .step = 0x01,
+ .default_value = 0x14,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "auto adjust",
+ .minimum = 0x00,
+ .maximum = 0x01,
+ .step = 0x01,
+ .default_value = 0x01,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_GREEN_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "green balance",
+ .minimum = 0x00,
+ .maximum = 0x7f,
+ .step = 0x01,
+ .default_value = 0x14,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_BAND_FILTER,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "band filter",
+ .minimum = 0x00,
+ .maximum = 0x01,
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ },
+ .get_ctrl = &ov7660_get_ctrl,
+ .set_ctrl = &ov7660_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ .set_crop = &ov7660_set_crop,
+ .pix_format = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_JPEG,
+ .priv = 8,
+ },
+ .set_pix_format = &ov7660_set_pix_format
+};
+
+
+int sn9c102_probe_ov7660(struct sn9c102_device* cam)
+{
+ int pid, ver, err;
+
+ err = sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1},
+ {0x01, 0x01}, {0x00, 0x01},
+ {0x28, 0x17});
+
+ pid = sn9c102_i2c_try_read(cam, &ov7660, 0x0a);
+ ver = sn9c102_i2c_try_read(cam, &ov7660, 0x0b);
+ if (err || pid < 0 || ver < 0)
+ return -EIO;
+ if (pid != 0x76 || ver != 0x60)
+ return -ENODEV;
+
+ sn9c102_attach_sensor(cam, &ov7660);
+
+ return 0;
+}
diff --git a/drivers/media/video/sn9c102/sn9c102_pas106b.c b/drivers/media/video/sn9c102/sn9c102_pas106b.c
new file mode 100644
index 00000000..81cd969c
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_pas106b.c
@@ -0,0 +1,302 @@
+/***************************************************************************
+ * Plug-in for PAS106B image sensor connected to the SN9C1xx PC Camera *
+ * Controllers *
+ * *
+ * Copyright (C) 2004-2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include <linux/delay.h>
+#include "sn9c102_sensor.h"
+#include "sn9c102_devtable.h"
+
+
+static int pas106b_init(struct sn9c102_device* cam)
+{
+ int err = 0;
+
+ err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11},
+ {0x00, 0x14}, {0x20, 0x17},
+ {0x20, 0x19}, {0x09, 0x18});
+
+ err += sn9c102_i2c_write(cam, 0x02, 0x0c);
+ err += sn9c102_i2c_write(cam, 0x05, 0x5a);
+ err += sn9c102_i2c_write(cam, 0x06, 0x88);
+ err += sn9c102_i2c_write(cam, 0x07, 0x80);
+ err += sn9c102_i2c_write(cam, 0x10, 0x06);
+ err += sn9c102_i2c_write(cam, 0x11, 0x06);
+ err += sn9c102_i2c_write(cam, 0x12, 0x00);
+ err += sn9c102_i2c_write(cam, 0x14, 0x02);
+ err += sn9c102_i2c_write(cam, 0x13, 0x01);
+
+ msleep(400);
+
+ return err;
+}
+
+
+static int pas106b_get_ctrl(struct sn9c102_device* cam,
+ struct v4l2_control* ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ {
+ int r1 = sn9c102_i2c_read(cam, 0x03),
+ r2 = sn9c102_i2c_read(cam, 0x04);
+ if (r1 < 0 || r2 < 0)
+ return -EIO;
+ ctrl->value = (r1 << 4) | (r2 & 0x0f);
+ }
+ return 0;
+ case V4L2_CID_RED_BALANCE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0)
+ return -EIO;
+ ctrl->value &= 0x1f;
+ return 0;
+ case V4L2_CID_BLUE_BALANCE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x09)) < 0)
+ return -EIO;
+ ctrl->value &= 0x1f;
+ return 0;
+ case V4L2_CID_GAIN:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x0e)) < 0)
+ return -EIO;
+ ctrl->value &= 0x1f;
+ return 0;
+ case V4L2_CID_CONTRAST:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x0f)) < 0)
+ return -EIO;
+ ctrl->value &= 0x07;
+ return 0;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x0a)) < 0)
+ return -EIO;
+ ctrl->value = (ctrl->value & 0x1f) << 1;
+ return 0;
+ case SN9C102_V4L2_CID_DAC_MAGNITUDE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x08)) < 0)
+ return -EIO;
+ ctrl->value &= 0xf8;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+
+static int pas106b_set_ctrl(struct sn9c102_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ err += sn9c102_i2c_write(cam, 0x03, ctrl->value >> 4);
+ err += sn9c102_i2c_write(cam, 0x04, ctrl->value & 0x0f);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x0c, ctrl->value);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x09, ctrl->value);
+ break;
+ case V4L2_CID_GAIN:
+ err += sn9c102_i2c_write(cam, 0x0e, ctrl->value);
+ break;
+ case V4L2_CID_CONTRAST:
+ err += sn9c102_i2c_write(cam, 0x0f, ctrl->value);
+ break;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x0a, ctrl->value >> 1);
+ err += sn9c102_i2c_write(cam, 0x0b, ctrl->value >> 1);
+ break;
+ case SN9C102_V4L2_CID_DAC_MAGNITUDE:
+ err += sn9c102_i2c_write(cam, 0x08, ctrl->value << 3);
+ break;
+ default:
+ return -EINVAL;
+ }
+ err += sn9c102_i2c_write(cam, 0x13, 0x01);
+
+ return err ? -EIO : 0;
+}
+
+
+static int pas106b_set_crop(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+ int err = 0;
+ u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4,
+ v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3;
+
+ err += sn9c102_write_reg(cam, h_start, 0x12);
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+
+ return err;
+}
+
+
+static int pas106b_set_pix_format(struct sn9c102_device* cam,
+ const struct v4l2_pix_format* pix)
+{
+ int err = 0;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
+ err += sn9c102_write_reg(cam, 0x2c, 0x17);
+ else
+ err += sn9c102_write_reg(cam, 0x20, 0x17);
+
+ return err;
+}
+
+
+static const struct sn9c102_sensor pas106b = {
+ .name = "PAS106B",
+ .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+ .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102,
+ .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE,
+ .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ,
+ .interface = SN9C102_I2C_2WIRES,
+ .i2c_slave_id = 0x40,
+ .init = &pas106b_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x125,
+ .maximum = 0xfff,
+ .step = 0x001,
+ .default_value = 0x140,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "global gain",
+ .minimum = 0x00,
+ .maximum = 0x1f,
+ .step = 0x01,
+ .default_value = 0x0d,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "contrast",
+ .minimum = 0x00,
+ .maximum = 0x07,
+ .step = 0x01,
+ .default_value = 0x00, /* 0x00~0x03 have same effect */
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "red balance",
+ .minimum = 0x00,
+ .maximum = 0x1f,
+ .step = 0x01,
+ .default_value = 0x04,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "blue balance",
+ .minimum = 0x00,
+ .maximum = 0x1f,
+ .step = 0x01,
+ .default_value = 0x06,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_GREEN_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "green balance",
+ .minimum = 0x00,
+ .maximum = 0x3e,
+ .step = 0x02,
+ .default_value = 0x02,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_DAC_MAGNITUDE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "DAC magnitude",
+ .minimum = 0x00,
+ .maximum = 0x1f,
+ .step = 0x01,
+ .default_value = 0x01,
+ .flags = 0,
+ },
+ },
+ .get_ctrl = &pas106b_get_ctrl,
+ .set_ctrl = &pas106b_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 352,
+ .height = 288,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 352,
+ .height = 288,
+ },
+ },
+ .set_crop = &pas106b_set_crop,
+ .pix_format = {
+ .width = 352,
+ .height = 288,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8, /* we use this field as 'bits per pixel' */
+ },
+ .set_pix_format = &pas106b_set_pix_format
+};
+
+
+int sn9c102_probe_pas106b(struct sn9c102_device* cam)
+{
+ int r0 = 0, r1 = 0;
+ unsigned int pid = 0;
+
+ /*
+ Minimal initialization to enable the I2C communication
+ NOTE: do NOT change the values!
+ */
+ if (sn9c102_write_const_regs(cam,
+ {0x01, 0x01}, /* sensor power down */
+ {0x00, 0x01}, /* sensor power on */
+ {0x28, 0x17})) /* sensor clock at 24 MHz */
+ return -EIO;
+
+ r0 = sn9c102_i2c_try_read(cam, &pas106b, 0x00);
+ r1 = sn9c102_i2c_try_read(cam, &pas106b, 0x01);
+ if (r0 < 0 || r1 < 0)
+ return -EIO;
+
+ pid = (r0 << 11) | ((r1 & 0xf0) >> 4);
+ if (pid != 0x007)
+ return -ENODEV;
+
+ sn9c102_attach_sensor(cam, &pas106b);
+
+ return 0;
+}
diff --git a/drivers/media/video/sn9c102/sn9c102_pas202bcb.c b/drivers/media/video/sn9c102/sn9c102_pas202bcb.c
new file mode 100644
index 00000000..2e86fdc8
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_pas202bcb.c
@@ -0,0 +1,335 @@
+/***************************************************************************
+ * Plug-in for PAS202BCB image sensor connected to the SN9C1xx PC Camera *
+ * Controllers *
+ * *
+ * Copyright (C) 2004 by Carlos Eduardo Medaglia Dyonisio *
+ * <medaglia@undl.org.br> *
+ * *
+ * Support for SN9C103, DAC Magnitude, exposure and green gain controls *
+ * added by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include <linux/delay.h>
+#include "sn9c102_sensor.h"
+#include "sn9c102_devtable.h"
+
+
+static int pas202bcb_init(struct sn9c102_device* cam)
+{
+ int err = 0;
+
+ switch (sn9c102_get_bridge(cam)) {
+ case BRIDGE_SN9C101:
+ case BRIDGE_SN9C102:
+ err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11},
+ {0x00, 0x14}, {0x20, 0x17},
+ {0x30, 0x19}, {0x09, 0x18});
+ break;
+ case BRIDGE_SN9C103:
+ err = sn9c102_write_const_regs(cam, {0x00, 0x02}, {0x00, 0x03},
+ {0x1a, 0x04}, {0x20, 0x05},
+ {0x20, 0x06}, {0x20, 0x07},
+ {0x00, 0x10}, {0x00, 0x11},
+ {0x00, 0x14}, {0x20, 0x17},
+ {0x30, 0x19}, {0x09, 0x18},
+ {0x02, 0x1c}, {0x03, 0x1d},
+ {0x0f, 0x1e}, {0x0c, 0x1f},
+ {0x00, 0x20}, {0x10, 0x21},
+ {0x20, 0x22}, {0x30, 0x23},
+ {0x40, 0x24}, {0x50, 0x25},
+ {0x60, 0x26}, {0x70, 0x27},
+ {0x80, 0x28}, {0x90, 0x29},
+ {0xa0, 0x2a}, {0xb0, 0x2b},
+ {0xc0, 0x2c}, {0xd0, 0x2d},
+ {0xe0, 0x2e}, {0xf0, 0x2f},
+ {0xff, 0x30});
+ break;
+ default:
+ break;
+ }
+
+ err += sn9c102_i2c_write(cam, 0x02, 0x14);
+ err += sn9c102_i2c_write(cam, 0x03, 0x40);
+ err += sn9c102_i2c_write(cam, 0x0d, 0x2c);
+ err += sn9c102_i2c_write(cam, 0x0e, 0x01);
+ err += sn9c102_i2c_write(cam, 0x0f, 0xa9);
+ err += sn9c102_i2c_write(cam, 0x10, 0x08);
+ err += sn9c102_i2c_write(cam, 0x13, 0x63);
+ err += sn9c102_i2c_write(cam, 0x15, 0x70);
+ err += sn9c102_i2c_write(cam, 0x11, 0x01);
+
+ msleep(400);
+
+ return err;
+}
+
+
+static int pas202bcb_get_ctrl(struct sn9c102_device* cam,
+ struct v4l2_control* ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ {
+ int r1 = sn9c102_i2c_read(cam, 0x04),
+ r2 = sn9c102_i2c_read(cam, 0x05);
+ if (r1 < 0 || r2 < 0)
+ return -EIO;
+ ctrl->value = (r1 << 6) | (r2 & 0x3f);
+ }
+ return 0;
+ case V4L2_CID_RED_BALANCE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x09)) < 0)
+ return -EIO;
+ ctrl->value &= 0x0f;
+ return 0;
+ case V4L2_CID_BLUE_BALANCE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x07)) < 0)
+ return -EIO;
+ ctrl->value &= 0x0f;
+ return 0;
+ case V4L2_CID_GAIN:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0)
+ return -EIO;
+ ctrl->value &= 0x1f;
+ return 0;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x08)) < 0)
+ return -EIO;
+ ctrl->value &= 0x0f;
+ return 0;
+ case SN9C102_V4L2_CID_DAC_MAGNITUDE:
+ if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0)
+ return -EIO;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+
+static int pas202bcb_set_pix_format(struct sn9c102_device* cam,
+ const struct v4l2_pix_format* pix)
+{
+ int err = 0;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
+ err += sn9c102_write_reg(cam, 0x28, 0x17);
+ else
+ err += sn9c102_write_reg(cam, 0x20, 0x17);
+
+ return err;
+}
+
+
+static int pas202bcb_set_ctrl(struct sn9c102_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ err += sn9c102_i2c_write(cam, 0x04, ctrl->value >> 6);
+ err += sn9c102_i2c_write(cam, 0x05, ctrl->value & 0x3f);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x09, ctrl->value);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x07, ctrl->value);
+ break;
+ case V4L2_CID_GAIN:
+ err += sn9c102_i2c_write(cam, 0x10, ctrl->value);
+ break;
+ case SN9C102_V4L2_CID_GREEN_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x08, ctrl->value);
+ break;
+ case SN9C102_V4L2_CID_DAC_MAGNITUDE:
+ err += sn9c102_i2c_write(cam, 0x0c, ctrl->value);
+ break;
+ default:
+ return -EINVAL;
+ }
+ err += sn9c102_i2c_write(cam, 0x11, 0x01);
+
+ return err ? -EIO : 0;
+}
+
+
+static int pas202bcb_set_crop(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+ int err = 0;
+ u8 h_start = 0,
+ v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3;
+
+ switch (sn9c102_get_bridge(cam)) {
+ case BRIDGE_SN9C101:
+ case BRIDGE_SN9C102:
+ h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4;
+ break;
+ case BRIDGE_SN9C103:
+ h_start = (u8)(rect->left - s->cropcap.bounds.left) + 3;
+ break;
+ default:
+ break;
+ }
+
+ err += sn9c102_write_reg(cam, h_start, 0x12);
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+
+ return err;
+}
+
+
+static const struct sn9c102_sensor pas202bcb = {
+ .name = "PAS202BCB",
+ .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+ .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103,
+ .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE,
+ .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ,
+ .interface = SN9C102_I2C_2WIRES,
+ .i2c_slave_id = 0x40,
+ .init = &pas202bcb_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x01e5,
+ .maximum = 0x3fff,
+ .step = 0x0001,
+ .default_value = 0x01e5,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "global gain",
+ .minimum = 0x00,
+ .maximum = 0x1f,
+ .step = 0x01,
+ .default_value = 0x0b,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "red balance",
+ .minimum = 0x00,
+ .maximum = 0x0f,
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "blue balance",
+ .minimum = 0x00,
+ .maximum = 0x0f,
+ .step = 0x01,
+ .default_value = 0x05,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_GREEN_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "green balance",
+ .minimum = 0x00,
+ .maximum = 0x0f,
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ {
+ .id = SN9C102_V4L2_CID_DAC_MAGNITUDE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "DAC magnitude",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x01,
+ .default_value = 0x04,
+ .flags = 0,
+ },
+ },
+ .get_ctrl = &pas202bcb_get_ctrl,
+ .set_ctrl = &pas202bcb_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ .set_crop = &pas202bcb_set_crop,
+ .pix_format = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8,
+ },
+ .set_pix_format = &pas202bcb_set_pix_format
+};
+
+
+int sn9c102_probe_pas202bcb(struct sn9c102_device* cam)
+{
+ int r0 = 0, r1 = 0, err = 0;
+ unsigned int pid = 0;
+
+ /*
+ * Minimal initialization to enable the I2C communication
+ * NOTE: do NOT change the values!
+ */
+ switch (sn9c102_get_bridge(cam)) {
+ case BRIDGE_SN9C101:
+ case BRIDGE_SN9C102:
+ err = sn9c102_write_const_regs(cam,
+ {0x01, 0x01}, /* power down */
+ {0x40, 0x01}, /* power on */
+ {0x28, 0x17});/* clock 24 MHz */
+ break;
+ case BRIDGE_SN9C103: /* do _not_ change anything! */
+ err = sn9c102_write_const_regs(cam, {0x09, 0x01}, {0x44, 0x01},
+ {0x44, 0x02}, {0x29, 0x17});
+ break;
+ default:
+ break;
+ }
+
+ r0 = sn9c102_i2c_try_read(cam, &pas202bcb, 0x00);
+ r1 = sn9c102_i2c_try_read(cam, &pas202bcb, 0x01);
+
+ if (err || r0 < 0 || r1 < 0)
+ return -EIO;
+
+ pid = (r0 << 4) | ((r1 & 0xf0) >> 4);
+ if (pid != 0x017)
+ return -ENODEV;
+
+ sn9c102_attach_sensor(cam, &pas202bcb);
+
+ return 0;
+}
diff --git a/drivers/media/video/sn9c102/sn9c102_sensor.h b/drivers/media/video/sn9c102/sn9c102_sensor.h
new file mode 100644
index 00000000..3679970d
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_sensor.h
@@ -0,0 +1,307 @@
+/***************************************************************************
+ * API for image sensors connected to the SN9C1xx PC Camera Controllers *
+ * *
+ * Copyright (C) 2004-2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#ifndef _SN9C102_SENSOR_H_
+#define _SN9C102_SENSOR_H_
+
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <linux/device.h>
+#include <linux/stddef.h>
+#include <linux/errno.h>
+#include <asm/types.h>
+
+struct sn9c102_device;
+struct sn9c102_sensor;
+
+/*****************************************************************************/
+
+/*
+ OVERVIEW.
+ This is a small interface that allows you to add support for any CCD/CMOS
+ image sensors connected to the SN9C1XX bridges. The entire API is documented
+ below. In the most general case, to support a sensor there are three steps
+ you have to follow:
+ 1) define the main "sn9c102_sensor" structure by setting the basic fields;
+ 2) write a probing function to be called by the core module when the USB
+ camera is recognized, then add both the USB ids and the name of that
+ function to the two corresponding tables in sn9c102_devtable.h;
+ 3) implement the methods that you want/need (and fill the rest of the main
+ structure accordingly).
+ "sn9c102_pas106b.c" is an example of all this stuff. Remember that you do
+ NOT need to touch the source code of the core module for the things to work
+ properly, unless you find bugs or flaws in it. Finally, do not forget to
+ read the V4L2 API for completeness.
+*/
+
+/*****************************************************************************/
+
+enum sn9c102_bridge {
+ BRIDGE_SN9C101 = 0x01,
+ BRIDGE_SN9C102 = 0x02,
+ BRIDGE_SN9C103 = 0x04,
+ BRIDGE_SN9C105 = 0x08,
+ BRIDGE_SN9C120 = 0x10,
+};
+
+/* Return the bridge name */
+enum sn9c102_bridge sn9c102_get_bridge(struct sn9c102_device* cam);
+
+/* Return a pointer the sensor struct attached to the camera */
+struct sn9c102_sensor* sn9c102_get_sensor(struct sn9c102_device* cam);
+
+/* Identify a device */
+extern struct sn9c102_device*
+sn9c102_match_id(struct sn9c102_device* cam, const struct usb_device_id *id);
+
+/* Attach a probed sensor to the camera. */
+extern void
+sn9c102_attach_sensor(struct sn9c102_device* cam,
+ const struct sn9c102_sensor* sensor);
+
+/*
+ Read/write routines: they always return -1 on error, 0 or the read value
+ otherwise. NOTE that a real read operation is not supported by the SN9C1XX
+ chip for some of its registers. To work around this problem, a pseudo-read
+ call is provided instead: it returns the last successfully written value
+ on the register (0 if it has never been written), the usual -1 on error.
+*/
+
+/* The "try" I2C I/O versions are used when probing the sensor */
+extern int sn9c102_i2c_try_read(struct sn9c102_device*,
+ const struct sn9c102_sensor*, u8 address);
+
+/*
+ These must be used if and only if the sensor doesn't implement the standard
+ I2C protocol. There are a number of good reasons why you must use the
+ single-byte versions of these functions: do not abuse. The first function
+ writes n bytes, from data0 to datan, to registers 0x09 - 0x09+n of SN9C1XX
+ chip. The second one programs the registers 0x09 and 0x10 with data0 and
+ data1, and places the n bytes read from the sensor register table in the
+ buffer pointed by 'buffer'. Both the functions return -1 on error; the write
+ version returns 0 on success, while the read version returns the first read
+ byte.
+*/
+extern int sn9c102_i2c_try_raw_write(struct sn9c102_device* cam,
+ const struct sn9c102_sensor* sensor, u8 n,
+ u8 data0, u8 data1, u8 data2, u8 data3,
+ u8 data4, u8 data5);
+extern int sn9c102_i2c_try_raw_read(struct sn9c102_device* cam,
+ const struct sn9c102_sensor* sensor,
+ u8 data0, u8 data1, u8 n, u8 buffer[]);
+
+/* To be used after the sensor struct has been attached to the camera struct */
+extern int sn9c102_i2c_write(struct sn9c102_device*, u8 address, u8 value);
+extern int sn9c102_i2c_read(struct sn9c102_device*, u8 address);
+
+/* I/O on registers in the bridge. Could be used by the sensor methods too */
+extern int sn9c102_read_reg(struct sn9c102_device*, u16 index);
+extern int sn9c102_pread_reg(struct sn9c102_device*, u16 index);
+extern int sn9c102_write_reg(struct sn9c102_device*, u8 value, u16 index);
+extern int sn9c102_write_regs(struct sn9c102_device*, const u8 valreg[][2],
+ int count);
+/*
+ Write multiple registers with constant values. For example:
+ sn9c102_write_const_regs(cam, {0x00, 0x14}, {0x60, 0x17}, {0x0f, 0x18});
+ Register addresses must be < 256.
+*/
+#define sn9c102_write_const_regs(sn9c102_device, data...) \
+ ({ static const u8 _valreg[][2] = {data}; \
+ sn9c102_write_regs(sn9c102_device, _valreg, ARRAY_SIZE(_valreg)); })
+
+/*****************************************************************************/
+
+enum sn9c102_i2c_sysfs_ops {
+ SN9C102_I2C_READ = 0x01,
+ SN9C102_I2C_WRITE = 0x02,
+};
+
+enum sn9c102_i2c_frequency { /* sensors may support both the frequencies */
+ SN9C102_I2C_100KHZ = 0x01,
+ SN9C102_I2C_400KHZ = 0x02,
+};
+
+enum sn9c102_i2c_interface {
+ SN9C102_I2C_2WIRES,
+ SN9C102_I2C_3WIRES,
+};
+
+#define SN9C102_MAX_CTRLS (V4L2_CID_LASTP1-V4L2_CID_BASE+10)
+
+struct sn9c102_sensor {
+ char name[32], /* sensor name */
+ maintainer[64]; /* name of the maintainer <email> */
+
+ enum sn9c102_bridge supported_bridge; /* supported SN9C1xx bridges */
+
+ /* Supported operations through the 'sysfs' interface */
+ enum sn9c102_i2c_sysfs_ops sysfs_ops;
+
+ /*
+ These sensor capabilities must be provided if the SN9C1XX controller
+ needs to communicate through the sensor serial interface by using
+ at least one of the i2c functions available.
+ */
+ enum sn9c102_i2c_frequency frequency;
+ enum sn9c102_i2c_interface interface;
+
+ /*
+ This identifier must be provided if the image sensor implements
+ the standard I2C protocol.
+ */
+ u8 i2c_slave_id; /* reg. 0x09 */
+
+ /*
+ NOTE: Where not noted,most of the functions below are not mandatory.
+ Set to null if you do not implement them. If implemented,
+ they must return 0 on success, the proper error otherwise.
+ */
+
+ int (*init)(struct sn9c102_device* cam);
+ /*
+ This function will be called after the sensor has been attached.
+ It should be used to initialize the sensor only, but may also
+ configure part of the SN9C1XX chip if necessary. You don't need to
+ setup picture settings like brightness, contrast, etc.. here, if
+ the corresponding controls are implemented (see below), since
+ they are adjusted in the core driver by calling the set_ctrl()
+ method after init(), where the arguments are the default values
+ specified in the v4l2_queryctrl list of supported controls;
+ Same suggestions apply for other settings, _if_ the corresponding
+ methods are present; if not, the initialization must configure the
+ sensor according to the default configuration structures below.
+ */
+
+ struct v4l2_queryctrl qctrl[SN9C102_MAX_CTRLS];
+ /*
+ Optional list of default controls, defined as indicated in the
+ V4L2 API. Menu type controls are not handled by this interface.
+ */
+
+ int (*get_ctrl)(struct sn9c102_device* cam, struct v4l2_control* ctrl);
+ int (*set_ctrl)(struct sn9c102_device* cam,
+ const struct v4l2_control* ctrl);
+ /*
+ You must implement at least the set_ctrl method if you have defined
+ the list above. The returned value must follow the V4L2
+ specifications for the VIDIOC_G|C_CTRL ioctls. V4L2_CID_H|VCENTER
+ are not supported by this driver, so do not implement them. Also,
+ you don't have to check whether the passed values are out of bounds,
+ given that this is done by the core module.
+ */
+
+ struct v4l2_cropcap cropcap;
+ /*
+ Think the image sensor as a grid of R,G,B monochromatic pixels
+ disposed according to a particular Bayer pattern, which describes
+ the complete array of pixels, from (0,0) to (xmax, ymax). We will
+ use this coordinate system from now on. It is assumed the sensor
+ chip can be programmed to capture/transmit a subsection of that
+ array of pixels: we will call this subsection "active window".
+ It is not always true that the largest achievable active window can
+ cover the whole array of pixels. The V4L2 API defines another
+ area called "source rectangle", which, in turn, is a subrectangle of
+ the active window. The SN9C1XX chip is always programmed to read the
+ source rectangle.
+ The bounds of both the active window and the source rectangle are
+ specified in the cropcap substructures 'bounds' and 'defrect'.
+ By default, the source rectangle should cover the largest possible
+ area. Again, it is not always true that the largest source rectangle
+ can cover the entire active window, although it is a rare case for
+ the hardware we have. The bounds of the source rectangle _must_ be
+ multiple of 16 and must use the same coordinate system as indicated
+ before; their centers shall align initially.
+ If necessary, the sensor chip must be initialized during init() to
+ set the bounds of the active sensor window; however, by default, it
+ usually covers the largest achievable area (maxwidth x maxheight)
+ of pixels, so no particular initialization is needed, if you have
+ defined the correct default bounds in the structures.
+ See the V4L2 API for further details.
+ NOTE: once you have defined the bounds of the active window
+ (struct cropcap.bounds) you must not change them.anymore.
+ Only 'bounds' and 'defrect' fields are mandatory, other fields
+ will be ignored.
+ */
+
+ int (*set_crop)(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect);
+ /*
+ To be called on VIDIOC_C_SETCROP. The core module always calls a
+ default routine which configures the appropriate SN9C1XX regs (also
+ scaling), but you may need to override/adjust specific stuff.
+ 'rect' contains width and height values that are multiple of 16: in
+ case you override the default function, you always have to program
+ the chip to match those values; on error return the corresponding
+ error code without rolling back.
+ NOTE: in case, you must program the SN9C1XX chip to get rid of
+ blank pixels or blank lines at the _start_ of each line or
+ frame after each HSYNC or VSYNC, so that the image starts with
+ real RGB data (see regs 0x12, 0x13) (having set H_SIZE and,
+ V_SIZE you don't have to care about blank pixels or blank
+ lines at the end of each line or frame).
+ */
+
+ struct v4l2_pix_format pix_format;
+ /*
+ What you have to define here are: 1) initial 'width' and 'height' of
+ the target rectangle 2) the initial 'pixelformat', which can be
+ either V4L2_PIX_FMT_SN9C10X, V4L2_PIX_FMT_JPEG (for ompressed video)
+ or V4L2_PIX_FMT_SBGGR8 3) 'priv', which we'll be used to indicate
+ the number of bits per pixel for uncompressed video, 8 or 9 (despite
+ the current value of 'pixelformat').
+ NOTE 1: both 'width' and 'height' _must_ be either 1/1 or 1/2 or 1/4
+ of cropcap.defrect.width and cropcap.defrect.height. I
+ suggest 1/1.
+ NOTE 2: The initial compression quality is defined by the first bit
+ of reg 0x17 during the initialization of the image sensor.
+ NOTE 3: as said above, you have to program the SN9C1XX chip to get
+ rid of any blank pixels, so that the output of the sensor
+ matches the RGB bayer sequence (i.e. BGBGBG...GRGRGR).
+ */
+
+ int (*set_pix_format)(struct sn9c102_device* cam,
+ const struct v4l2_pix_format* pix);
+ /*
+ To be called on VIDIOC_S_FMT, when switching from the SBGGR8 to
+ SN9C10X pixel format or viceversa. On error return the corresponding
+ error code without rolling back.
+ */
+
+ /*
+ Do NOT write to the data below, it's READ ONLY. It is used by the
+ core module to store successfully updated values of the above
+ settings, for rollbacks..etc..in case of errors during atomic I/O
+ */
+ struct v4l2_queryctrl _qctrl[SN9C102_MAX_CTRLS];
+ struct v4l2_rect _rect;
+};
+
+/*****************************************************************************/
+
+/* Private ioctl's for control settings supported by some image sensors */
+#define SN9C102_V4L2_CID_DAC_MAGNITUDE (V4L2_CID_PRIVATE_BASE + 0)
+#define SN9C102_V4L2_CID_GREEN_BALANCE (V4L2_CID_PRIVATE_BASE + 1)
+#define SN9C102_V4L2_CID_RESET_LEVEL (V4L2_CID_PRIVATE_BASE + 2)
+#define SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE (V4L2_CID_PRIVATE_BASE + 3)
+#define SN9C102_V4L2_CID_GAMMA (V4L2_CID_PRIVATE_BASE + 4)
+#define SN9C102_V4L2_CID_BAND_FILTER (V4L2_CID_PRIVATE_BASE + 5)
+#define SN9C102_V4L2_CID_BRIGHT_LEVEL (V4L2_CID_PRIVATE_BASE + 6)
+
+#endif /* _SN9C102_SENSOR_H_ */
diff --git a/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c b/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c
new file mode 100644
index 00000000..04cdfdde
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c
@@ -0,0 +1,154 @@
+/***************************************************************************
+ * Plug-in for TAS5110C1B image sensor connected to the SN9C1xx PC Camera *
+ * Controllers *
+ * *
+ * Copyright (C) 2004-2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include "sn9c102_sensor.h"
+#include "sn9c102_devtable.h"
+
+
+static int tas5110c1b_init(struct sn9c102_device* cam)
+{
+ int err = 0;
+
+ err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x44, 0x01},
+ {0x00, 0x10}, {0x00, 0x11},
+ {0x0a, 0x14}, {0x60, 0x17},
+ {0x06, 0x18}, {0xfb, 0x19});
+
+ err += sn9c102_i2c_write(cam, 0xc0, 0x80);
+
+ return err;
+}
+
+
+static int tas5110c1b_set_ctrl(struct sn9c102_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return err ? -EIO : 0;
+}
+
+
+static int tas5110c1b_set_crop(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+ int err = 0;
+ u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69,
+ v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9;
+
+ err += sn9c102_write_reg(cam, h_start, 0x12);
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+
+ /* Don't change ! */
+ err += sn9c102_write_reg(cam, 0x14, 0x1a);
+ err += sn9c102_write_reg(cam, 0x0a, 0x1b);
+ err += sn9c102_write_reg(cam, sn9c102_pread_reg(cam, 0x19), 0x19);
+
+ return err;
+}
+
+
+static int tas5110c1b_set_pix_format(struct sn9c102_device* cam,
+ const struct v4l2_pix_format* pix)
+{
+ int err = 0;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
+ err += sn9c102_write_reg(cam, 0x2b, 0x19);
+ else
+ err += sn9c102_write_reg(cam, 0xfb, 0x19);
+
+ return err;
+}
+
+
+static const struct sn9c102_sensor tas5110c1b = {
+ .name = "TAS5110C1B",
+ .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+ .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102,
+ .sysfs_ops = SN9C102_I2C_WRITE,
+ .frequency = SN9C102_I2C_100KHZ,
+ .interface = SN9C102_I2C_3WIRES,
+ .init = &tas5110c1b_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "global gain",
+ .minimum = 0x00,
+ .maximum = 0xf6,
+ .step = 0x01,
+ .default_value = 0x40,
+ .flags = 0,
+ },
+ },
+ .set_ctrl = &tas5110c1b_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 352,
+ .height = 288,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 352,
+ .height = 288,
+ },
+ },
+ .set_crop = &tas5110c1b_set_crop,
+ .pix_format = {
+ .width = 352,
+ .height = 288,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8,
+ },
+ .set_pix_format = &tas5110c1b_set_pix_format
+};
+
+
+int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam)
+{
+ const struct usb_device_id tas5110c1b_id_table[] = {
+ { USB_DEVICE(0x0c45, 0x6001), },
+ { USB_DEVICE(0x0c45, 0x6005), },
+ { USB_DEVICE(0x0c45, 0x60ab), },
+ { }
+ };
+
+ /* Sensor detection is based on USB pid/vid */
+ if (!sn9c102_match_id(cam, tas5110c1b_id_table))
+ return -ENODEV;
+
+ sn9c102_attach_sensor(cam, &tas5110c1b);
+
+ return 0;
+}
diff --git a/drivers/media/video/sn9c102/sn9c102_tas5110d.c b/drivers/media/video/sn9c102/sn9c102_tas5110d.c
new file mode 100644
index 00000000..9372e6f9
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_tas5110d.c
@@ -0,0 +1,119 @@
+/***************************************************************************
+ * Plug-in for TAS5110D image sensor connected to the SN9C1xx PC Camera *
+ * Controllers *
+ * *
+ * Copyright (C) 2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include "sn9c102_sensor.h"
+#include "sn9c102_devtable.h"
+
+
+static int tas5110d_init(struct sn9c102_device* cam)
+{
+ int err;
+
+ err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x04, 0x01},
+ {0x0a, 0x14}, {0x60, 0x17},
+ {0x06, 0x18}, {0xfb, 0x19});
+
+ err += sn9c102_i2c_write(cam, 0x9a, 0xca);
+
+ return err;
+}
+
+
+static int tas5110d_set_crop(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+ int err = 0;
+ u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69,
+ v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9;
+
+ err += sn9c102_write_reg(cam, h_start, 0x12);
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+
+ err += sn9c102_write_reg(cam, 0x14, 0x1a);
+ err += sn9c102_write_reg(cam, 0x0a, 0x1b);
+
+ return err;
+}
+
+
+static int tas5110d_set_pix_format(struct sn9c102_device* cam,
+ const struct v4l2_pix_format* pix)
+{
+ int err = 0;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
+ err += sn9c102_write_reg(cam, 0x3b, 0x19);
+ else
+ err += sn9c102_write_reg(cam, 0xfb, 0x19);
+
+ return err;
+}
+
+
+static const struct sn9c102_sensor tas5110d = {
+ .name = "TAS5110D",
+ .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+ .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102,
+ .sysfs_ops = SN9C102_I2C_WRITE,
+ .frequency = SN9C102_I2C_100KHZ,
+ .interface = SN9C102_I2C_2WIRES,
+ .i2c_slave_id = 0x61,
+ .init = &tas5110d_init,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 352,
+ .height = 288,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 352,
+ .height = 288,
+ },
+ },
+ .set_crop = &tas5110d_set_crop,
+ .pix_format = {
+ .width = 352,
+ .height = 288,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8,
+ },
+ .set_pix_format = &tas5110d_set_pix_format
+};
+
+
+int sn9c102_probe_tas5110d(struct sn9c102_device* cam)
+{
+ const struct usb_device_id tas5110d_id_table[] = {
+ { USB_DEVICE(0x0c45, 0x6007), },
+ { }
+ };
+
+ if (!sn9c102_match_id(cam, tas5110d_id_table))
+ return -ENODEV;
+
+ sn9c102_attach_sensor(cam, &tas5110d);
+
+ return 0;
+}
diff --git a/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c b/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c
new file mode 100644
index 00000000..a30bbc43
--- /dev/null
+++ b/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c
@@ -0,0 +1,165 @@
+/***************************************************************************
+ * Plug-in for TAS5130D1B image sensor connected to the SN9C1xx PC Camera *
+ * Controllers *
+ * *
+ * Copyright (C) 2004-2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include "sn9c102_sensor.h"
+#include "sn9c102_devtable.h"
+
+
+static int tas5130d1b_init(struct sn9c102_device* cam)
+{
+ int err;
+
+ err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x20, 0x17},
+ {0x04, 0x01}, {0x01, 0x10},
+ {0x00, 0x11}, {0x00, 0x14},
+ {0x60, 0x17}, {0x07, 0x18});
+
+ return err;
+}
+
+
+static int tas5130d1b_set_ctrl(struct sn9c102_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value);
+ break;
+ case V4L2_CID_EXPOSURE:
+ err += sn9c102_i2c_write(cam, 0x40, 0x47 - ctrl->value);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return err ? -EIO : 0;
+}
+
+
+static int tas5130d1b_set_crop(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
+ u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 104,
+ v_start = (u8)(rect->top - s->cropcap.bounds.top) + 12;
+ int err = 0;
+
+ err += sn9c102_write_reg(cam, h_start, 0x12);
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+
+ /* Do NOT change! */
+ err += sn9c102_write_reg(cam, 0x1f, 0x1a);
+ err += sn9c102_write_reg(cam, 0x1a, 0x1b);
+ err += sn9c102_write_reg(cam, sn9c102_pread_reg(cam, 0x19), 0x19);
+
+ return err;
+}
+
+
+static int tas5130d1b_set_pix_format(struct sn9c102_device* cam,
+ const struct v4l2_pix_format* pix)
+{
+ int err = 0;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
+ err += sn9c102_write_reg(cam, 0x63, 0x19);
+ else
+ err += sn9c102_write_reg(cam, 0xf3, 0x19);
+
+ return err;
+}
+
+
+static const struct sn9c102_sensor tas5130d1b = {
+ .name = "TAS5130D1B",
+ .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+ .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102,
+ .sysfs_ops = SN9C102_I2C_WRITE,
+ .frequency = SN9C102_I2C_100KHZ,
+ .interface = SN9C102_I2C_3WIRES,
+ .init = &tas5130d1b_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "global gain",
+ .minimum = 0x00,
+ .maximum = 0xf6,
+ .step = 0x02,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x00,
+ .maximum = 0x47,
+ .step = 0x01,
+ .default_value = 0x00,
+ .flags = 0,
+ },
+ },
+ .set_ctrl = &tas5130d1b_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ .set_crop = &tas5130d1b_set_crop,
+ .pix_format = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8,
+ },
+ .set_pix_format = &tas5130d1b_set_pix_format
+};
+
+
+int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam)
+{
+ const struct usb_device_id tas5130d1b_id_table[] = {
+ { USB_DEVICE(0x0c45, 0x6024), },
+ { USB_DEVICE(0x0c45, 0x6025), },
+ { USB_DEVICE(0x0c45, 0x60aa), },
+ { }
+ };
+
+ /* Sensor detection is based on USB pid/vid */
+ if (!sn9c102_match_id(cam, tas5130d1b_id_table))
+ return -ENODEV;
+
+ sn9c102_attach_sensor(cam, &tas5130d1b);
+
+ return 0;
+}
diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c
new file mode 100644
index 00000000..aedb970d
--- /dev/null
+++ b/drivers/media/video/soc_camera.c
@@ -0,0 +1,1537 @@
+/*
+ * camera image capture (abstract) bus driver
+ *
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * This driver provides an interface between platform-specific camera
+ * busses and camera devices. It should be used if the camera is
+ * connected not over a "proper" bus like PCI or USB, but over a
+ * special bus, like, for example, the Quick Capture interface on PXA270
+ * SoCs. Later it should also be used for i.MX31 SoCs from Freescale.
+ * It can handle multiple cameras and / or multiple busses, which can
+ * be used, e.g., in stereo-vision applications.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/vmalloc.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-dev.h>
+#include <media/videobuf-core.h>
+#include <media/videobuf2-core.h>
+#include <media/soc_mediabus.h>
+
+/* Default to VGA resolution */
+#define DEFAULT_WIDTH 640
+#define DEFAULT_HEIGHT 480
+
+#define is_streaming(ici, icd) \
+ (((ici)->ops->init_videobuf) ? \
+ (icd)->vb_vidq.streaming : \
+ vb2_is_streaming(&(icd)->vb2_vidq))
+
+static LIST_HEAD(hosts);
+static LIST_HEAD(devices);
+static DEFINE_MUTEX(list_lock); /* Protects the list of hosts */
+
+static int soc_camera_power_on(struct soc_camera_device *icd,
+ struct soc_camera_link *icl)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ int ret = regulator_bulk_enable(icl->num_regulators,
+ icl->regulators);
+ if (ret < 0) {
+ dev_err(icd->pdev, "Cannot enable regulators\n");
+ return ret;
+ }
+
+ if (icl->power) {
+ ret = icl->power(icd->pdev, 1);
+ if (ret < 0) {
+ dev_err(icd->pdev,
+ "Platform failed to power-on the camera.\n");
+ goto elinkpwr;
+ }
+ }
+
+ ret = v4l2_subdev_call(sd, core, s_power, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+ goto esdpwr;
+
+ return 0;
+
+esdpwr:
+ if (icl->power)
+ icl->power(icd->pdev, 0);
+elinkpwr:
+ regulator_bulk_disable(icl->num_regulators,
+ icl->regulators);
+ return ret;
+}
+
+static int soc_camera_power_off(struct soc_camera_device *icd,
+ struct soc_camera_link *icl)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ int ret = v4l2_subdev_call(sd, core, s_power, 0);
+
+ if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+ return ret;
+
+ if (icl->power) {
+ ret = icl->power(icd->pdev, 0);
+ if (ret < 0) {
+ dev_err(icd->pdev,
+ "Platform failed to power-off the camera.\n");
+ return ret;
+ }
+ }
+
+ ret = regulator_bulk_disable(icl->num_regulators,
+ icl->regulators);
+ if (ret < 0)
+ dev_err(icd->pdev, "Cannot disable regulators\n");
+
+ return ret;
+}
+
+const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc(
+ struct soc_camera_device *icd, unsigned int fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < icd->num_user_formats; i++)
+ if (icd->user_formats[i].host_fmt->fourcc == fourcc)
+ return icd->user_formats + i;
+ return NULL;
+}
+EXPORT_SYMBOL(soc_camera_xlate_by_fourcc);
+
+/**
+ * soc_camera_apply_board_flags() - apply platform SOCAM_SENSOR_INVERT_* flags
+ * @icl: camera platform parameters
+ * @cfg: media bus configuration
+ * @return: resulting flags
+ */
+unsigned long soc_camera_apply_board_flags(struct soc_camera_link *icl,
+ const struct v4l2_mbus_config *cfg)
+{
+ unsigned long f, flags = cfg->flags;
+
+ /* If only one of the two polarities is supported, switch to the opposite */
+ if (icl->flags & SOCAM_SENSOR_INVERT_HSYNC) {
+ f = flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW);
+ if (f == V4L2_MBUS_HSYNC_ACTIVE_HIGH || f == V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ flags ^= V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW;
+ }
+
+ if (icl->flags & SOCAM_SENSOR_INVERT_VSYNC) {
+ f = flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW);
+ if (f == V4L2_MBUS_VSYNC_ACTIVE_HIGH || f == V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ flags ^= V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW;
+ }
+
+ if (icl->flags & SOCAM_SENSOR_INVERT_PCLK) {
+ f = flags & (V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING);
+ if (f == V4L2_MBUS_PCLK_SAMPLE_RISING || f == V4L2_MBUS_PCLK_SAMPLE_FALLING)
+ flags ^= V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING;
+ }
+
+ return flags;
+}
+EXPORT_SYMBOL(soc_camera_apply_board_flags);
+
+#define pixfmtstr(x) (x) & 0xff, ((x) >> 8) & 0xff, ((x) >> 16) & 0xff, \
+ ((x) >> 24) & 0xff
+
+static int soc_camera_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ int ret;
+
+ dev_dbg(icd->pdev, "TRY_FMT(%c%c%c%c, %ux%u)\n",
+ pixfmtstr(pix->pixelformat), pix->width, pix->height);
+
+ pix->bytesperline = 0;
+ pix->sizeimage = 0;
+
+ ret = ici->ops->try_fmt(icd, f);
+ if (ret < 0)
+ return ret;
+
+ if (!pix->sizeimage) {
+ if (!pix->bytesperline) {
+ const struct soc_camera_format_xlate *xlate;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+ if (!xlate)
+ return -EINVAL;
+
+ ret = soc_mbus_bytes_per_line(pix->width,
+ xlate->host_fmt);
+ if (ret > 0)
+ pix->bytesperline = ret;
+ }
+ if (pix->bytesperline)
+ pix->sizeimage = pix->bytesperline * pix->height;
+ }
+
+ return 0;
+}
+
+static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct soc_camera_device *icd = file->private_data;
+
+ WARN_ON(priv != file->private_data);
+
+ /* Only single-plane capture is supported so far */
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ /* limit format to hardware capabilities */
+ return soc_camera_try_fmt(icd, f);
+}
+
+static int soc_camera_enum_input(struct file *file, void *priv,
+ struct v4l2_input *inp)
+{
+ if (inp->index != 0)
+ return -EINVAL;
+
+ /* default is camera */
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ inp->std = V4L2_STD_UNKNOWN;
+ strcpy(inp->name, "Camera");
+
+ return 0;
+}
+
+static int soc_camera_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+
+ return 0;
+}
+
+static int soc_camera_s_input(struct file *file, void *priv, unsigned int i)
+{
+ if (i > 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id *a)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+
+ return v4l2_subdev_call(sd, core, s_std, *a);
+}
+
+static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+
+ return v4l2_subdev_call(sd, core, g_std, a);
+}
+
+static int soc_camera_enum_fsizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ return ici->ops->enum_fsizes(icd, fsize);
+}
+
+static int soc_camera_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ int ret;
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ WARN_ON(priv != file->private_data);
+
+ if (icd->streamer && icd->streamer != file)
+ return -EBUSY;
+
+ if (ici->ops->init_videobuf) {
+ ret = videobuf_reqbufs(&icd->vb_vidq, p);
+ if (ret < 0)
+ return ret;
+
+ ret = ici->ops->reqbufs(icd, p);
+ } else {
+ ret = vb2_reqbufs(&icd->vb2_vidq, p);
+ }
+
+ if (!ret && !icd->streamer)
+ icd->streamer = file;
+
+ return ret;
+}
+
+static int soc_camera_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ WARN_ON(priv != file->private_data);
+
+ if (ici->ops->init_videobuf)
+ return videobuf_querybuf(&icd->vb_vidq, p);
+ else
+ return vb2_querybuf(&icd->vb2_vidq, p);
+}
+
+static int soc_camera_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ WARN_ON(priv != file->private_data);
+
+ if (icd->streamer != file)
+ return -EBUSY;
+
+ if (ici->ops->init_videobuf)
+ return videobuf_qbuf(&icd->vb_vidq, p);
+ else
+ return vb2_qbuf(&icd->vb2_vidq, p);
+}
+
+static int soc_camera_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ WARN_ON(priv != file->private_data);
+
+ if (icd->streamer != file)
+ return -EBUSY;
+
+ if (ici->ops->init_videobuf)
+ return videobuf_dqbuf(&icd->vb_vidq, p, file->f_flags & O_NONBLOCK);
+ else
+ return vb2_dqbuf(&icd->vb2_vidq, p, file->f_flags & O_NONBLOCK);
+}
+
+static int soc_camera_create_bufs(struct file *file, void *priv,
+ struct v4l2_create_buffers *create)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ /* videobuf2 only */
+ if (ici->ops->init_videobuf)
+ return -EINVAL;
+ else
+ return vb2_create_bufs(&icd->vb2_vidq, create);
+}
+
+static int soc_camera_prepare_buf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ /* videobuf2 only */
+ if (ici->ops->init_videobuf)
+ return -EINVAL;
+ else
+ return vb2_prepare_buf(&icd->vb2_vidq, b);
+}
+
+/* Always entered with .video_lock held */
+static int soc_camera_init_user_formats(struct soc_camera_device *icd)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ unsigned int i, fmts = 0, raw_fmts = 0;
+ int ret;
+ enum v4l2_mbus_pixelcode code;
+
+ while (!v4l2_subdev_call(sd, video, enum_mbus_fmt, raw_fmts, &code))
+ raw_fmts++;
+
+ if (!ici->ops->get_formats)
+ /*
+ * Fallback mode - the host will have to serve all
+ * sensor-provided formats one-to-one to the user
+ */
+ fmts = raw_fmts;
+ else
+ /*
+ * First pass - only count formats this host-sensor
+ * configuration can provide
+ */
+ for (i = 0; i < raw_fmts; i++) {
+ ret = ici->ops->get_formats(icd, i, NULL);
+ if (ret < 0)
+ return ret;
+ fmts += ret;
+ }
+
+ if (!fmts)
+ return -ENXIO;
+
+ icd->user_formats =
+ vmalloc(fmts * sizeof(struct soc_camera_format_xlate));
+ if (!icd->user_formats)
+ return -ENOMEM;
+
+ dev_dbg(icd->pdev, "Found %d supported formats.\n", fmts);
+
+ /* Second pass - actually fill data formats */
+ fmts = 0;
+ for (i = 0; i < raw_fmts; i++)
+ if (!ici->ops->get_formats) {
+ v4l2_subdev_call(sd, video, enum_mbus_fmt, i, &code);
+ icd->user_formats[fmts].host_fmt =
+ soc_mbus_get_fmtdesc(code);
+ if (icd->user_formats[fmts].host_fmt)
+ icd->user_formats[fmts++].code = code;
+ } else {
+ ret = ici->ops->get_formats(icd, i,
+ &icd->user_formats[fmts]);
+ if (ret < 0)
+ goto egfmt;
+ fmts += ret;
+ }
+
+ icd->num_user_formats = fmts;
+ icd->current_fmt = &icd->user_formats[0];
+
+ return 0;
+
+egfmt:
+ vfree(icd->user_formats);
+ return ret;
+}
+
+/* Always entered with .video_lock held */
+static void soc_camera_free_user_formats(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ if (ici->ops->put_formats)
+ ici->ops->put_formats(icd);
+ icd->current_fmt = NULL;
+ icd->num_user_formats = 0;
+ vfree(icd->user_formats);
+ icd->user_formats = NULL;
+}
+
+/* Called with .vb_lock held, or from the first open(2), see comment there */
+static int soc_camera_set_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ int ret;
+
+ dev_dbg(icd->pdev, "S_FMT(%c%c%c%c, %ux%u)\n",
+ pixfmtstr(pix->pixelformat), pix->width, pix->height);
+
+ /* We always call try_fmt() before set_fmt() or set_crop() */
+ ret = soc_camera_try_fmt(icd, f);
+ if (ret < 0)
+ return ret;
+
+ ret = ici->ops->set_fmt(icd, f);
+ if (ret < 0) {
+ return ret;
+ } else if (!icd->current_fmt ||
+ icd->current_fmt->host_fmt->fourcc != pix->pixelformat) {
+ dev_err(icd->pdev,
+ "Host driver hasn't set up current format correctly!\n");
+ return -EINVAL;
+ }
+
+ icd->user_width = pix->width;
+ icd->user_height = pix->height;
+ icd->bytesperline = pix->bytesperline;
+ icd->sizeimage = pix->sizeimage;
+ icd->colorspace = pix->colorspace;
+ icd->field = pix->field;
+ if (ici->ops->init_videobuf)
+ icd->vb_vidq.field = pix->field;
+
+ dev_dbg(icd->pdev, "set width: %d height: %d\n",
+ icd->user_width, icd->user_height);
+
+ /* set physical bus parameters */
+ return ici->ops->set_bus_param(icd);
+}
+
+static int soc_camera_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct soc_camera_device *icd = dev_get_drvdata(vdev->parent);
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
+ struct soc_camera_host *ici;
+ int ret;
+
+ if (!to_soc_camera_control(icd))
+ /* No device driver attached */
+ return -ENODEV;
+
+ ici = to_soc_camera_host(icd->parent);
+
+ if (!try_module_get(ici->ops->owner)) {
+ dev_err(icd->pdev, "Couldn't lock capture bus driver.\n");
+ return -EINVAL;
+ }
+
+ icd->use_count++;
+
+ /* Now we really have to activate the camera */
+ if (icd->use_count == 1) {
+ /* Restore parameters before the last close() per V4L2 API */
+ struct v4l2_format f = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .fmt.pix = {
+ .width = icd->user_width,
+ .height = icd->user_height,
+ .field = icd->field,
+ .colorspace = icd->colorspace,
+ .pixelformat =
+ icd->current_fmt->host_fmt->fourcc,
+ },
+ };
+
+ /* The camera could have been already on, try to reset */
+ if (icl->reset)
+ icl->reset(icd->pdev);
+
+ /* Don't mess with the host during probe */
+ mutex_lock(&ici->host_lock);
+ ret = ici->ops->add(icd);
+ mutex_unlock(&ici->host_lock);
+ if (ret < 0) {
+ dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret);
+ goto eiciadd;
+ }
+
+ ret = soc_camera_power_on(icd, icl);
+ if (ret < 0)
+ goto epower;
+
+ pm_runtime_enable(&icd->vdev->dev);
+ ret = pm_runtime_resume(&icd->vdev->dev);
+ if (ret < 0 && ret != -ENOSYS)
+ goto eresume;
+
+ /*
+ * Try to configure with default parameters. Notice: this is the
+ * very first open, so, we cannot race against other calls,
+ * apart from someone else calling open() simultaneously, but
+ * .video_lock is protecting us against it.
+ */
+ ret = soc_camera_set_fmt(icd, &f);
+ if (ret < 0)
+ goto esfmt;
+
+ if (ici->ops->init_videobuf) {
+ ici->ops->init_videobuf(&icd->vb_vidq, icd);
+ } else {
+ ret = ici->ops->init_videobuf2(&icd->vb2_vidq, icd);
+ if (ret < 0)
+ goto einitvb;
+ }
+ v4l2_ctrl_handler_setup(&icd->ctrl_handler);
+ }
+
+ file->private_data = icd;
+ dev_dbg(icd->pdev, "camera device open\n");
+
+ return 0;
+
+ /*
+ * First four errors are entered with the .video_lock held
+ * and use_count == 1
+ */
+einitvb:
+esfmt:
+ pm_runtime_disable(&icd->vdev->dev);
+eresume:
+ soc_camera_power_off(icd, icl);
+epower:
+ ici->ops->remove(icd);
+eiciadd:
+ icd->use_count--;
+ module_put(ici->ops->owner);
+
+ return ret;
+}
+
+static int soc_camera_close(struct file *file)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ icd->use_count--;
+ if (!icd->use_count) {
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
+
+ pm_runtime_suspend(&icd->vdev->dev);
+ pm_runtime_disable(&icd->vdev->dev);
+
+ if (ici->ops->init_videobuf2)
+ vb2_queue_release(&icd->vb2_vidq);
+ ici->ops->remove(icd);
+
+ soc_camera_power_off(icd, icl);
+ }
+
+ if (icd->streamer == file)
+ icd->streamer = NULL;
+
+ module_put(ici->ops->owner);
+
+ dev_dbg(icd->pdev, "camera device close\n");
+
+ return 0;
+}
+
+static ssize_t soc_camera_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct soc_camera_device *icd = file->private_data;
+ int err = -EINVAL;
+
+ dev_err(icd->pdev, "camera device read not implemented\n");
+
+ return err;
+}
+
+static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ int err;
+
+ dev_dbg(icd->pdev, "mmap called, vma=0x%08lx\n", (unsigned long)vma);
+
+ if (icd->streamer != file)
+ return -EBUSY;
+
+ if (ici->ops->init_videobuf)
+ err = videobuf_mmap_mapper(&icd->vb_vidq, vma);
+ else
+ err = vb2_mmap(&icd->vb2_vidq, vma);
+
+ dev_dbg(icd->pdev, "vma start=0x%08lx, size=%ld, ret=%d\n",
+ (unsigned long)vma->vm_start,
+ (unsigned long)vma->vm_end - (unsigned long)vma->vm_start,
+ err);
+
+ return err;
+}
+
+static unsigned int soc_camera_poll(struct file *file, poll_table *pt)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ if (icd->streamer != file)
+ return -EBUSY;
+
+ if (ici->ops->init_videobuf && list_empty(&icd->vb_vidq.stream)) {
+ dev_err(icd->pdev, "Trying to poll with no queued buffers!\n");
+ return POLLERR;
+ }
+
+ return ici->ops->poll(file, pt);
+}
+
+void soc_camera_lock(struct vb2_queue *vq)
+{
+ struct soc_camera_device *icd = vb2_get_drv_priv(vq);
+ mutex_lock(&icd->video_lock);
+}
+EXPORT_SYMBOL(soc_camera_lock);
+
+void soc_camera_unlock(struct vb2_queue *vq)
+{
+ struct soc_camera_device *icd = vb2_get_drv_priv(vq);
+ mutex_unlock(&icd->video_lock);
+}
+EXPORT_SYMBOL(soc_camera_unlock);
+
+static struct v4l2_file_operations soc_camera_fops = {
+ .owner = THIS_MODULE,
+ .open = soc_camera_open,
+ .release = soc_camera_close,
+ .unlocked_ioctl = video_ioctl2,
+ .read = soc_camera_read,
+ .mmap = soc_camera_mmap,
+ .poll = soc_camera_poll,
+};
+
+static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct soc_camera_device *icd = file->private_data;
+ int ret;
+
+ WARN_ON(priv != file->private_data);
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ dev_warn(icd->pdev, "Wrong buf-type %d\n", f->type);
+ return -EINVAL;
+ }
+
+ if (icd->streamer && icd->streamer != file)
+ return -EBUSY;
+
+ if (is_streaming(to_soc_camera_host(icd->parent), icd)) {
+ dev_err(icd->pdev, "S_FMT denied: queue initialised\n");
+ return -EBUSY;
+ }
+
+ ret = soc_camera_set_fmt(icd, f);
+
+ if (!ret && !icd->streamer)
+ icd->streamer = file;
+
+ return ret;
+}
+
+static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct soc_camera_device *icd = file->private_data;
+ const struct soc_mbus_pixelfmt *format;
+
+ WARN_ON(priv != file->private_data);
+
+ if (f->index >= icd->num_user_formats)
+ return -EINVAL;
+
+ format = icd->user_formats[f->index].host_fmt;
+
+ if (format->name)
+ strlcpy(f->description, format->name, sizeof(f->description));
+ f->pixelformat = format->fourcc;
+ return 0;
+}
+
+static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ WARN_ON(priv != file->private_data);
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ pix->width = icd->user_width;
+ pix->height = icd->user_height;
+ pix->bytesperline = icd->bytesperline;
+ pix->sizeimage = icd->sizeimage;
+ pix->field = icd->field;
+ pix->pixelformat = icd->current_fmt->host_fmt->fourcc;
+ pix->colorspace = icd->colorspace;
+ dev_dbg(icd->pdev, "current_fmt->fourcc: 0x%08x\n",
+ icd->current_fmt->host_fmt->fourcc);
+ return 0;
+}
+
+static int soc_camera_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ WARN_ON(priv != file->private_data);
+
+ strlcpy(cap->driver, ici->drv_name, sizeof(cap->driver));
+ return ici->ops->querycap(ici, cap);
+}
+
+static int soc_camera_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type i)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ int ret;
+
+ WARN_ON(priv != file->private_data);
+
+ if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (icd->streamer != file)
+ return -EBUSY;
+
+ /* This calls buf_queue from host driver's videobuf_queue_ops */
+ if (ici->ops->init_videobuf)
+ ret = videobuf_streamon(&icd->vb_vidq);
+ else
+ ret = vb2_streamon(&icd->vb2_vidq, i);
+
+ if (!ret)
+ v4l2_subdev_call(sd, video, s_stream, 1);
+
+ return ret;
+}
+
+static int soc_camera_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type i)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ WARN_ON(priv != file->private_data);
+
+ if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (icd->streamer != file)
+ return -EBUSY;
+
+ /*
+ * This calls buf_release from host driver's videobuf_queue_ops for all
+ * remaining buffers. When the last buffer is freed, stop capture
+ */
+ if (ici->ops->init_videobuf)
+ videobuf_streamoff(&icd->vb_vidq);
+ else
+ vb2_streamoff(&icd->vb2_vidq, i);
+
+ v4l2_subdev_call(sd, video, s_stream, 0);
+
+ return 0;
+}
+
+static int soc_camera_cropcap(struct file *file, void *fh,
+ struct v4l2_cropcap *a)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ return ici->ops->cropcap(icd, a);
+}
+
+static int soc_camera_g_crop(struct file *file, void *fh,
+ struct v4l2_crop *a)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ int ret;
+
+ ret = ici->ops->get_crop(icd, a);
+
+ return ret;
+}
+
+/*
+ * According to the V4L2 API, drivers shall not update the struct v4l2_crop
+ * argument with the actual geometry, instead, the user shall use G_CROP to
+ * retrieve it.
+ */
+static int soc_camera_s_crop(struct file *file, void *fh,
+ struct v4l2_crop *a)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct v4l2_rect *rect = &a->c;
+ struct v4l2_crop current_crop;
+ int ret;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ dev_dbg(icd->pdev, "S_CROP(%ux%u@%u:%u)\n",
+ rect->width, rect->height, rect->left, rect->top);
+
+ /* If get_crop fails, we'll let host and / or client drivers decide */
+ ret = ici->ops->get_crop(icd, &current_crop);
+
+ /* Prohibit window size change with initialised buffers */
+ if (ret < 0) {
+ dev_err(icd->pdev,
+ "S_CROP denied: getting current crop failed\n");
+ } else if ((a->c.width == current_crop.c.width &&
+ a->c.height == current_crop.c.height) ||
+ !is_streaming(ici, icd)) {
+ /* same size or not streaming - use .set_crop() */
+ ret = ici->ops->set_crop(icd, a);
+ } else if (ici->ops->set_livecrop) {
+ ret = ici->ops->set_livecrop(icd, a);
+ } else {
+ dev_err(icd->pdev,
+ "S_CROP denied: queue initialised and sizes differ\n");
+ ret = -EBUSY;
+ }
+
+ return ret;
+}
+
+static int soc_camera_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ if (ici->ops->get_parm)
+ return ici->ops->get_parm(icd, a);
+
+ return -ENOIOCTLCMD;
+}
+
+static int soc_camera_s_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ if (ici->ops->set_parm)
+ return ici->ops->set_parm(icd, a);
+
+ return -ENOIOCTLCMD;
+}
+
+static int soc_camera_g_chip_ident(struct file *file, void *fh,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+
+ return v4l2_subdev_call(sd, core, g_chip_ident, id);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int soc_camera_g_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+
+ return v4l2_subdev_call(sd, core, g_register, reg);
+}
+
+static int soc_camera_s_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+
+ return v4l2_subdev_call(sd, core, s_register, reg);
+}
+#endif
+
+static int soc_camera_probe(struct soc_camera_device *icd);
+
+/* So far this function cannot fail */
+static void scan_add_host(struct soc_camera_host *ici)
+{
+ struct soc_camera_device *icd;
+
+ mutex_lock(&ici->host_lock);
+
+ list_for_each_entry(icd, &devices, list) {
+ if (icd->iface == ici->nr) {
+ int ret;
+
+ icd->parent = ici->v4l2_dev.dev;
+ ret = soc_camera_probe(icd);
+ }
+ }
+
+ mutex_unlock(&ici->host_lock);
+}
+
+#ifdef CONFIG_I2C_BOARDINFO
+static int soc_camera_init_i2c(struct soc_camera_device *icd,
+ struct soc_camera_link *icl)
+{
+ struct i2c_client *client;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct i2c_adapter *adap = i2c_get_adapter(icl->i2c_adapter_id);
+ struct v4l2_subdev *subdev;
+
+ if (!adap) {
+ dev_err(icd->pdev, "Cannot get I2C adapter #%d. No driver?\n",
+ icl->i2c_adapter_id);
+ goto ei2cga;
+ }
+
+ icl->board_info->platform_data = icl;
+
+ subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap,
+ icl->board_info, NULL);
+ if (!subdev)
+ goto ei2cnd;
+
+ client = v4l2_get_subdevdata(subdev);
+
+ /* Use to_i2c_client(dev) to recover the i2c client */
+ icd->control = &client->dev;
+
+ return 0;
+ei2cnd:
+ i2c_put_adapter(adap);
+ei2cga:
+ return -ENODEV;
+}
+
+static void soc_camera_free_i2c(struct soc_camera_device *icd)
+{
+ struct i2c_client *client =
+ to_i2c_client(to_soc_camera_control(icd));
+ struct i2c_adapter *adap = client->adapter;
+
+ icd->control = NULL;
+ v4l2_device_unregister_subdev(i2c_get_clientdata(client));
+ i2c_unregister_device(client);
+ i2c_put_adapter(adap);
+}
+#else
+#define soc_camera_init_i2c(icd, icl) (-ENODEV)
+#define soc_camera_free_i2c(icd) do {} while (0)
+#endif
+
+static int soc_camera_video_start(struct soc_camera_device *icd);
+static int video_dev_create(struct soc_camera_device *icd);
+/* Called during host-driver probe */
+static int soc_camera_probe(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
+ struct device *control = NULL;
+ struct v4l2_subdev *sd;
+ struct v4l2_mbus_framefmt mf;
+ int ret;
+
+ dev_info(icd->pdev, "Probing %s\n", dev_name(icd->pdev));
+
+ /*
+ * Currently the subdev with the largest number of controls (13) is
+ * ov6550. So let's pick 16 as a hint for the control handler. Note
+ * that this is a hint only: too large and you waste some memory, too
+ * small and there is a (very) small performance hit when looking up
+ * controls in the internal hash.
+ */
+ ret = v4l2_ctrl_handler_init(&icd->ctrl_handler, 16);
+ if (ret < 0)
+ return ret;
+
+ ret = regulator_bulk_get(icd->pdev, icl->num_regulators,
+ icl->regulators);
+ if (ret < 0)
+ goto ereg;
+
+ /* The camera could have been already on, try to reset */
+ if (icl->reset)
+ icl->reset(icd->pdev);
+
+ ret = ici->ops->add(icd);
+ if (ret < 0)
+ goto eadd;
+
+ /*
+ * This will not yet call v4l2_subdev_core_ops::s_power(1), because the
+ * subdevice has not been initialised yet. We'll have to call it once
+ * again after initialisation, even though it shouldn't be needed, we
+ * don't do any IO here.
+ */
+ ret = soc_camera_power_on(icd, icl);
+ if (ret < 0)
+ goto epower;
+
+ /* Must have icd->vdev before registering the device */
+ ret = video_dev_create(icd);
+ if (ret < 0)
+ goto evdc;
+
+ /* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */
+ if (icl->board_info) {
+ ret = soc_camera_init_i2c(icd, icl);
+ if (ret < 0)
+ goto eadddev;
+ } else if (!icl->add_device || !icl->del_device) {
+ ret = -EINVAL;
+ goto eadddev;
+ } else {
+ if (icl->module_name)
+ ret = request_module(icl->module_name);
+
+ ret = icl->add_device(icd);
+ if (ret < 0)
+ goto eadddev;
+
+ /*
+ * FIXME: this is racy, have to use driver-binding notification,
+ * when it is available
+ */
+ control = to_soc_camera_control(icd);
+ if (!control || !control->driver || !dev_get_drvdata(control) ||
+ !try_module_get(control->driver->owner)) {
+ icl->del_device(icd);
+ ret = -ENODEV;
+ goto enodrv;
+ }
+ }
+
+ sd = soc_camera_to_subdev(icd);
+ sd->grp_id = soc_camera_grp_id(icd);
+ v4l2_set_subdev_hostdata(sd, icd);
+
+ if (v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler))
+ goto ectrl;
+
+ /* At this point client .probe() should have run already */
+ ret = soc_camera_init_user_formats(icd);
+ if (ret < 0)
+ goto eiufmt;
+
+ icd->field = V4L2_FIELD_ANY;
+
+ /*
+ * ..._video_start() will create a device node, video_register_device()
+ * itself is protected against concurrent open() calls, but we also have
+ * to protect our data.
+ */
+ mutex_lock(&icd->video_lock);
+
+ ret = soc_camera_video_start(icd);
+ if (ret < 0)
+ goto evidstart;
+
+ ret = v4l2_subdev_call(sd, core, s_power, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ goto esdpwr;
+
+ /* Try to improve our guess of a reasonable window format */
+ if (!v4l2_subdev_call(sd, video, g_mbus_fmt, &mf)) {
+ icd->user_width = mf.width;
+ icd->user_height = mf.height;
+ icd->colorspace = mf.colorspace;
+ icd->field = mf.field;
+ }
+
+ ici->ops->remove(icd);
+
+ soc_camera_power_off(icd, icl);
+
+ mutex_unlock(&icd->video_lock);
+
+ return 0;
+
+esdpwr:
+ video_unregister_device(icd->vdev);
+evidstart:
+ mutex_unlock(&icd->video_lock);
+ soc_camera_free_user_formats(icd);
+eiufmt:
+ectrl:
+ if (icl->board_info) {
+ soc_camera_free_i2c(icd);
+ } else {
+ icl->del_device(icd);
+ module_put(control->driver->owner);
+ }
+enodrv:
+eadddev:
+ video_device_release(icd->vdev);
+ icd->vdev = NULL;
+evdc:
+ soc_camera_power_off(icd, icl);
+epower:
+ ici->ops->remove(icd);
+eadd:
+ regulator_bulk_free(icl->num_regulators, icl->regulators);
+ereg:
+ v4l2_ctrl_handler_free(&icd->ctrl_handler);
+ return ret;
+}
+
+/*
+ * This is called on device_unregister, which only means we have to disconnect
+ * from the host, but not remove ourselves from the device list
+ */
+static int soc_camera_remove(struct soc_camera_device *icd)
+{
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
+ struct video_device *vdev = icd->vdev;
+
+ BUG_ON(!icd->parent);
+
+ v4l2_ctrl_handler_free(&icd->ctrl_handler);
+ if (vdev) {
+ video_unregister_device(vdev);
+ icd->vdev = NULL;
+ }
+
+ if (icl->board_info) {
+ soc_camera_free_i2c(icd);
+ } else {
+ struct device_driver *drv = to_soc_camera_control(icd)->driver;
+ if (drv) {
+ icl->del_device(icd);
+ module_put(drv->owner);
+ }
+ }
+ soc_camera_free_user_formats(icd);
+
+ regulator_bulk_free(icl->num_regulators, icl->regulators);
+
+ return 0;
+}
+
+static int default_cropcap(struct soc_camera_device *icd,
+ struct v4l2_cropcap *a)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ return v4l2_subdev_call(sd, video, cropcap, a);
+}
+
+static int default_g_crop(struct soc_camera_device *icd, struct v4l2_crop *a)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ return v4l2_subdev_call(sd, video, g_crop, a);
+}
+
+static int default_s_crop(struct soc_camera_device *icd, struct v4l2_crop *a)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ return v4l2_subdev_call(sd, video, s_crop, a);
+}
+
+static int default_g_parm(struct soc_camera_device *icd,
+ struct v4l2_streamparm *parm)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ return v4l2_subdev_call(sd, video, g_parm, parm);
+}
+
+static int default_s_parm(struct soc_camera_device *icd,
+ struct v4l2_streamparm *parm)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ return v4l2_subdev_call(sd, video, s_parm, parm);
+}
+
+static int default_enum_fsizes(struct soc_camera_device *icd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int ret;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ __u32 pixfmt = fsize->pixel_format;
+ struct v4l2_frmsizeenum fsize_mbus = *fsize;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (!xlate)
+ return -EINVAL;
+ /* map xlate-code to pixel_format, sensor only handle xlate-code*/
+ fsize_mbus.pixel_format = xlate->code;
+
+ ret = v4l2_subdev_call(sd, video, enum_mbus_fsizes, &fsize_mbus);
+ if (ret < 0)
+ return ret;
+
+ *fsize = fsize_mbus;
+ fsize->pixel_format = pixfmt;
+
+ return 0;
+}
+
+int soc_camera_host_register(struct soc_camera_host *ici)
+{
+ struct soc_camera_host *ix;
+ int ret;
+
+ if (!ici || !ici->ops ||
+ !ici->ops->try_fmt ||
+ !ici->ops->set_fmt ||
+ !ici->ops->set_bus_param ||
+ !ici->ops->querycap ||
+ ((!ici->ops->init_videobuf ||
+ !ici->ops->reqbufs) &&
+ !ici->ops->init_videobuf2) ||
+ !ici->ops->add ||
+ !ici->ops->remove ||
+ !ici->ops->poll ||
+ !ici->v4l2_dev.dev)
+ return -EINVAL;
+
+ if (!ici->ops->set_crop)
+ ici->ops->set_crop = default_s_crop;
+ if (!ici->ops->get_crop)
+ ici->ops->get_crop = default_g_crop;
+ if (!ici->ops->cropcap)
+ ici->ops->cropcap = default_cropcap;
+ if (!ici->ops->set_parm)
+ ici->ops->set_parm = default_s_parm;
+ if (!ici->ops->get_parm)
+ ici->ops->get_parm = default_g_parm;
+ if (!ici->ops->enum_fsizes)
+ ici->ops->enum_fsizes = default_enum_fsizes;
+
+ mutex_lock(&list_lock);
+ list_for_each_entry(ix, &hosts, list) {
+ if (ix->nr == ici->nr) {
+ ret = -EBUSY;
+ goto edevreg;
+ }
+ }
+
+ ret = v4l2_device_register(ici->v4l2_dev.dev, &ici->v4l2_dev);
+ if (ret < 0)
+ goto edevreg;
+
+ list_add_tail(&ici->list, &hosts);
+ mutex_unlock(&list_lock);
+
+ mutex_init(&ici->host_lock);
+ scan_add_host(ici);
+
+ return 0;
+
+edevreg:
+ mutex_unlock(&list_lock);
+ return ret;
+}
+EXPORT_SYMBOL(soc_camera_host_register);
+
+/* Unregister all clients! */
+void soc_camera_host_unregister(struct soc_camera_host *ici)
+{
+ struct soc_camera_device *icd;
+
+ mutex_lock(&list_lock);
+
+ list_del(&ici->list);
+ list_for_each_entry(icd, &devices, list)
+ if (icd->iface == ici->nr && to_soc_camera_control(icd))
+ soc_camera_remove(icd);
+
+ mutex_unlock(&list_lock);
+
+ v4l2_device_unregister(&ici->v4l2_dev);
+}
+EXPORT_SYMBOL(soc_camera_host_unregister);
+
+/* Image capture device */
+static int soc_camera_device_register(struct soc_camera_device *icd)
+{
+ struct soc_camera_device *ix;
+ int num = -1, i;
+
+ for (i = 0; i < 256 && num < 0; i++) {
+ num = i;
+ /* Check if this index is available on this interface */
+ list_for_each_entry(ix, &devices, list) {
+ if (ix->iface == icd->iface && ix->devnum == i) {
+ num = -1;
+ break;
+ }
+ }
+ }
+
+ if (num < 0)
+ /*
+ * ok, we have 256 cameras on this host...
+ * man, stay reasonable...
+ */
+ return -ENOMEM;
+
+ icd->devnum = num;
+ icd->use_count = 0;
+ icd->host_priv = NULL;
+ mutex_init(&icd->video_lock);
+
+ list_add_tail(&icd->list, &devices);
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = {
+ .vidioc_querycap = soc_camera_querycap,
+ .vidioc_try_fmt_vid_cap = soc_camera_try_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = soc_camera_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = soc_camera_s_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap,
+ .vidioc_enum_input = soc_camera_enum_input,
+ .vidioc_g_input = soc_camera_g_input,
+ .vidioc_s_input = soc_camera_s_input,
+ .vidioc_s_std = soc_camera_s_std,
+ .vidioc_g_std = soc_camera_g_std,
+ .vidioc_enum_framesizes = soc_camera_enum_fsizes,
+ .vidioc_reqbufs = soc_camera_reqbufs,
+ .vidioc_querybuf = soc_camera_querybuf,
+ .vidioc_qbuf = soc_camera_qbuf,
+ .vidioc_dqbuf = soc_camera_dqbuf,
+ .vidioc_create_bufs = soc_camera_create_bufs,
+ .vidioc_prepare_buf = soc_camera_prepare_buf,
+ .vidioc_streamon = soc_camera_streamon,
+ .vidioc_streamoff = soc_camera_streamoff,
+ .vidioc_cropcap = soc_camera_cropcap,
+ .vidioc_g_crop = soc_camera_g_crop,
+ .vidioc_s_crop = soc_camera_s_crop,
+ .vidioc_g_parm = soc_camera_g_parm,
+ .vidioc_s_parm = soc_camera_s_parm,
+ .vidioc_g_chip_ident = soc_camera_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = soc_camera_g_register,
+ .vidioc_s_register = soc_camera_s_register,
+#endif
+};
+
+static int video_dev_create(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct video_device *vdev = video_device_alloc();
+
+ if (!vdev)
+ return -ENOMEM;
+
+ strlcpy(vdev->name, ici->drv_name, sizeof(vdev->name));
+
+ vdev->parent = icd->pdev;
+ vdev->current_norm = V4L2_STD_UNKNOWN;
+ vdev->fops = &soc_camera_fops;
+ vdev->ioctl_ops = &soc_camera_ioctl_ops;
+ vdev->release = video_device_release;
+ vdev->tvnorms = V4L2_STD_UNKNOWN;
+ vdev->ctrl_handler = &icd->ctrl_handler;
+ vdev->lock = &icd->video_lock;
+
+ icd->vdev = vdev;
+
+ return 0;
+}
+
+/*
+ * Called from soc_camera_probe() above (with .video_lock held???)
+ */
+static int soc_camera_video_start(struct soc_camera_device *icd)
+{
+ const struct device_type *type = icd->vdev->dev.type;
+ int ret;
+
+ if (!icd->parent)
+ return -ENODEV;
+
+ ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ dev_err(icd->pdev, "video_register_device failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Restore device type, possibly set by the subdevice driver */
+ icd->vdev->dev.type = type;
+
+ return 0;
+}
+
+static int __devinit soc_camera_pdrv_probe(struct platform_device *pdev)
+{
+ struct soc_camera_link *icl = pdev->dev.platform_data;
+ struct soc_camera_device *icd;
+ int ret;
+
+ if (!icl)
+ return -EINVAL;
+
+ icd = kzalloc(sizeof(*icd), GFP_KERNEL);
+ if (!icd)
+ return -ENOMEM;
+
+ icd->iface = icl->bus_id;
+ icd->link = icl;
+ icd->pdev = &pdev->dev;
+ platform_set_drvdata(pdev, icd);
+
+ ret = soc_camera_device_register(icd);
+ if (ret < 0)
+ goto escdevreg;
+
+ icd->user_width = DEFAULT_WIDTH;
+ icd->user_height = DEFAULT_HEIGHT;
+
+ return 0;
+
+escdevreg:
+ kfree(icd);
+
+ return ret;
+}
+
+/*
+ * Only called on rmmod for each platform device, since they are not
+ * hot-pluggable. Now we know, that all our users - hosts and devices have
+ * been unloaded already
+ */
+static int __devexit soc_camera_pdrv_remove(struct platform_device *pdev)
+{
+ struct soc_camera_device *icd = platform_get_drvdata(pdev);
+
+ if (!icd)
+ return -EINVAL;
+
+ list_del(&icd->list);
+
+ kfree(icd);
+
+ return 0;
+}
+
+static struct platform_driver __refdata soc_camera_pdrv = {
+ .remove = __devexit_p(soc_camera_pdrv_remove),
+ .driver = {
+ .name = "soc-camera-pdrv",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init soc_camera_init(void)
+{
+ return platform_driver_probe(&soc_camera_pdrv, soc_camera_pdrv_probe);
+}
+
+static void __exit soc_camera_exit(void)
+{
+ platform_driver_unregister(&soc_camera_pdrv);
+}
+
+module_init(soc_camera_init);
+module_exit(soc_camera_exit);
+
+MODULE_DESCRIPTION("Image capture bus driver");
+MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:soc-camera-pdrv");
diff --git a/drivers/media/video/soc_camera_platform.c b/drivers/media/video/soc_camera_platform.c
new file mode 100644
index 00000000..f59ccade
--- /dev/null
+++ b/drivers/media/video/soc_camera_platform.c
@@ -0,0 +1,197 @@
+/*
+ * Generic Platform Camera Driver
+ *
+ * Copyright (C) 2008 Magnus Damm
+ * Based on mt9m001 driver,
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-subdev.h>
+#include <media/soc_camera.h>
+#include <media/soc_camera_platform.h>
+
+struct soc_camera_platform_priv {
+ struct v4l2_subdev subdev;
+};
+
+static struct soc_camera_platform_priv *get_priv(struct platform_device *pdev)
+{
+ struct v4l2_subdev *subdev = platform_get_drvdata(pdev);
+ return container_of(subdev, struct soc_camera_platform_priv, subdev);
+}
+
+static int soc_camera_platform_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
+ return p->set_capture(p, enable);
+}
+
+static int soc_camera_platform_fill_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
+
+ mf->width = p->format.width;
+ mf->height = p->format.height;
+ mf->code = p->format.code;
+ mf->colorspace = p->format.colorspace;
+ mf->field = p->format.field;
+
+ return 0;
+}
+
+static struct v4l2_subdev_core_ops platform_subdev_core_ops;
+
+static int soc_camera_platform_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
+
+ if (index)
+ return -EINVAL;
+
+ *code = p->format.code;
+ return 0;
+}
+
+static int soc_camera_platform_g_crop(struct v4l2_subdev *sd,
+ struct v4l2_crop *a)
+{
+ struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
+
+ a->c.left = 0;
+ a->c.top = 0;
+ a->c.width = p->format.width;
+ a->c.height = p->format.height;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int soc_camera_platform_cropcap(struct v4l2_subdev *sd,
+ struct v4l2_cropcap *a)
+{
+ struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
+
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ a->bounds.width = p->format.width;
+ a->bounds.height = p->format.height;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int soc_camera_platform_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
+
+ cfg->flags = p->mbus_param;
+ cfg->type = p->mbus_type;
+
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops platform_subdev_video_ops = {
+ .s_stream = soc_camera_platform_s_stream,
+ .enum_mbus_fmt = soc_camera_platform_enum_fmt,
+ .cropcap = soc_camera_platform_cropcap,
+ .g_crop = soc_camera_platform_g_crop,
+ .try_mbus_fmt = soc_camera_platform_fill_fmt,
+ .g_mbus_fmt = soc_camera_platform_fill_fmt,
+ .s_mbus_fmt = soc_camera_platform_fill_fmt,
+ .g_mbus_config = soc_camera_platform_g_mbus_config,
+};
+
+static struct v4l2_subdev_ops platform_subdev_ops = {
+ .core = &platform_subdev_core_ops,
+ .video = &platform_subdev_video_ops,
+};
+
+static int soc_camera_platform_probe(struct platform_device *pdev)
+{
+ struct soc_camera_host *ici;
+ struct soc_camera_platform_priv *priv;
+ struct soc_camera_platform_info *p = pdev->dev.platform_data;
+ struct soc_camera_device *icd;
+ int ret;
+
+ if (!p)
+ return -EINVAL;
+
+ if (!p->icd) {
+ dev_err(&pdev->dev,
+ "Platform has not set soc_camera_device pointer!\n");
+ return -EINVAL;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ icd = p->icd;
+
+ /* soc-camera convention: control's drvdata points to the subdev */
+ platform_set_drvdata(pdev, &priv->subdev);
+ /* Set the control device reference */
+ icd->control = &pdev->dev;
+
+ ici = to_soc_camera_host(icd->parent);
+
+ v4l2_subdev_init(&priv->subdev, &platform_subdev_ops);
+ v4l2_set_subdevdata(&priv->subdev, p);
+ strncpy(priv->subdev.name, dev_name(&pdev->dev), V4L2_SUBDEV_NAME_SIZE);
+
+ ret = v4l2_device_register_subdev(&ici->v4l2_dev, &priv->subdev);
+ if (ret)
+ goto evdrs;
+
+ return ret;
+
+evdrs:
+ platform_set_drvdata(pdev, NULL);
+ kfree(priv);
+ return ret;
+}
+
+static int soc_camera_platform_remove(struct platform_device *pdev)
+{
+ struct soc_camera_platform_priv *priv = get_priv(pdev);
+ struct soc_camera_platform_info *p = v4l2_get_subdevdata(&priv->subdev);
+
+ p->icd->control = NULL;
+ v4l2_device_unregister_subdev(&priv->subdev);
+ platform_set_drvdata(pdev, NULL);
+ kfree(priv);
+ return 0;
+}
+
+static struct platform_driver soc_camera_platform_driver = {
+ .driver = {
+ .name = "soc_camera_platform",
+ .owner = THIS_MODULE,
+ },
+ .probe = soc_camera_platform_probe,
+ .remove = soc_camera_platform_remove,
+};
+
+module_platform_driver(soc_camera_platform_driver);
+
+MODULE_DESCRIPTION("SoC Camera Platform driver");
+MODULE_AUTHOR("Magnus Damm");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:soc_camera_platform");
diff --git a/drivers/media/video/soc_mediabus.c b/drivers/media/video/soc_mediabus.c
new file mode 100644
index 00000000..cf7f2194
--- /dev/null
+++ b/drivers/media/video/soc_mediabus.c
@@ -0,0 +1,433 @@
+/*
+ * soc-camera media bus helper routines
+ *
+ * Copyright (C) 2009, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-mediabus.h>
+#include <media/soc_mediabus.h>
+
+static const struct soc_mbus_lookup mbus_fmt[] = {
+{
+ .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .name = "YUYV",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_YVYU8_2X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .name = "YVYU",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .name = "UYVY",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_VYUY8_2X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .name = "VYUY",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .name = "RGB555",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_RGB555X,
+ .name = "RGB555X",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_RGB565_2X8_LE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .name = "RGB565",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_RGB565_2X8_BE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+ .name = "RGB565X",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_SBGGR8_1X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .name = "Bayer 8 BGGR",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_SBGGR10_1X10,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ .name = "Bayer 10 BGGR",
+ .bits_per_sample = 10,
+ .packing = SOC_MBUS_PACKING_EXTEND16,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_Y8_1X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .name = "Grey",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_Y10_1X10,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_Y10,
+ .name = "Grey 10bit",
+ .bits_per_sample = 10,
+ .packing = SOC_MBUS_PACKING_EXTEND16,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ .name = "Bayer 10 BGGR",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ .name = "Bayer 10 BGGR",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADLO,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ .name = "Bayer 10 BGGR",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ .name = "Bayer 10 BGGR",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADLO,
+ .order = SOC_MBUS_ORDER_BE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_JPEG_1X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_JPEG,
+ .name = "JPEG",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_VARIABLE,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_RGB444,
+ .name = "RGB444",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_YUYV8_1_5X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .name = "YUYV 4:2:0",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_1_5X8,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_YVYU8_1_5X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_YVU420,
+ .name = "YVYU 4:2:0",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_1_5X8,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_UYVY8_1X16,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .name = "UYVY 16bit",
+ .bits_per_sample = 16,
+ .packing = SOC_MBUS_PACKING_EXTEND16,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_VYUY8_1X16,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .name = "VYUY 16bit",
+ .bits_per_sample = 16,
+ .packing = SOC_MBUS_PACKING_EXTEND16,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_YUYV8_1X16,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .name = "YUYV 16bit",
+ .bits_per_sample = 16,
+ .packing = SOC_MBUS_PACKING_EXTEND16,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_YVYU8_1X16,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .name = "YVYU 16bit",
+ .bits_per_sample = 16,
+ .packing = SOC_MBUS_PACKING_EXTEND16,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_SGRBG8_1X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .name = "Bayer 8 GRBG",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_SGRBG10DPCM8,
+ .name = "Bayer 10 BGGR DPCM 8",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_SGBRG10_1X10,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_SGBRG10,
+ .name = "Bayer 10 GBRG",
+ .bits_per_sample = 10,
+ .packing = SOC_MBUS_PACKING_EXTEND16,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_SGRBG10_1X10,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_SGRBG10,
+ .name = "Bayer 10 GRBG",
+ .bits_per_sample = 10,
+ .packing = SOC_MBUS_PACKING_EXTEND16,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_SRGGB10_1X10,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_SRGGB10,
+ .name = "Bayer 10 RGGB",
+ .bits_per_sample = 10,
+ .packing = SOC_MBUS_PACKING_EXTEND16,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_SBGGR12_1X12,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_SBGGR12,
+ .name = "Bayer 12 BGGR",
+ .bits_per_sample = 12,
+ .packing = SOC_MBUS_PACKING_EXTEND16,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_SGBRG12_1X12,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_SGBRG12,
+ .name = "Bayer 12 GBRG",
+ .bits_per_sample = 12,
+ .packing = SOC_MBUS_PACKING_EXTEND16,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_SGRBG12_1X12,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_SGRBG12,
+ .name = "Bayer 12 GRBG",
+ .bits_per_sample = 12,
+ .packing = SOC_MBUS_PACKING_EXTEND16,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_SRGGB12_1X12,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_SRGGB12,
+ .name = "Bayer 12 RGGB",
+ .bits_per_sample = 12,
+ .packing = SOC_MBUS_PACKING_EXTEND16,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+},
+};
+
+int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf,
+ unsigned int *numerator, unsigned int *denominator)
+{
+ switch (mf->packing) {
+ case SOC_MBUS_PACKING_NONE:
+ case SOC_MBUS_PACKING_EXTEND16:
+ *numerator = 1;
+ *denominator = 1;
+ return 0;
+ case SOC_MBUS_PACKING_2X8_PADHI:
+ case SOC_MBUS_PACKING_2X8_PADLO:
+ *numerator = 2;
+ *denominator = 1;
+ return 0;
+ case SOC_MBUS_PACKING_1_5X8:
+ *numerator = 3;
+ *denominator = 2;
+ return 0;
+ case SOC_MBUS_PACKING_VARIABLE:
+ *numerator = 0;
+ *denominator = 1;
+ return 0;
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL(soc_mbus_samples_per_pixel);
+
+s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf)
+{
+ switch (mf->packing) {
+ case SOC_MBUS_PACKING_NONE:
+ return width * mf->bits_per_sample / 8;
+ case SOC_MBUS_PACKING_2X8_PADHI:
+ case SOC_MBUS_PACKING_2X8_PADLO:
+ case SOC_MBUS_PACKING_EXTEND16:
+ return width * 2;
+ case SOC_MBUS_PACKING_1_5X8:
+ return width * 3 / 2;
+ case SOC_MBUS_PACKING_VARIABLE:
+ return 0;
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL(soc_mbus_bytes_per_line);
+
+const struct soc_mbus_pixelfmt *soc_mbus_find_fmtdesc(
+ enum v4l2_mbus_pixelcode code,
+ const struct soc_mbus_lookup *lookup,
+ int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ if (lookup[i].code == code)
+ return &lookup[i].fmt;
+
+ return NULL;
+}
+EXPORT_SYMBOL(soc_mbus_find_fmtdesc);
+
+const struct soc_mbus_pixelfmt *soc_mbus_get_fmtdesc(
+ enum v4l2_mbus_pixelcode code)
+{
+ return soc_mbus_find_fmtdesc(code, mbus_fmt, ARRAY_SIZE(mbus_fmt));
+}
+EXPORT_SYMBOL(soc_mbus_get_fmtdesc);
+
+unsigned int soc_mbus_config_compatible(const struct v4l2_mbus_config *cfg,
+ unsigned int flags)
+{
+ unsigned long common_flags;
+ bool hsync = true, vsync = true, pclk, data, mode;
+ bool mipi_lanes, mipi_clock;
+
+ common_flags = cfg->flags & flags;
+
+ switch (cfg->type) {
+ case V4L2_MBUS_PARALLEL:
+ hsync = common_flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_HSYNC_ACTIVE_LOW);
+ vsync = common_flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_VSYNC_ACTIVE_LOW);
+ case V4L2_MBUS_BT656:
+ pclk = common_flags & (V4L2_MBUS_PCLK_SAMPLE_RISING |
+ V4L2_MBUS_PCLK_SAMPLE_FALLING);
+ data = common_flags & (V4L2_MBUS_DATA_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_LOW);
+ mode = common_flags & (V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE);
+ return (!hsync || !vsync || !pclk || !data || !mode) ?
+ 0 : common_flags;
+ case V4L2_MBUS_CSI2:
+ mipi_lanes = common_flags & V4L2_MBUS_CSI2_LANES;
+ mipi_clock = common_flags & (V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK |
+ V4L2_MBUS_CSI2_CONTINUOUS_CLOCK);
+ return (!mipi_lanes || !mipi_clock) ? 0 : common_flags;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(soc_mbus_config_compatible);
+
+static int __init soc_mbus_init(void)
+{
+ return 0;
+}
+
+static void __exit soc_mbus_exit(void)
+{
+}
+
+module_init(soc_mbus_init);
+module_exit(soc_mbus_exit);
+
+MODULE_DESCRIPTION("soc-camera media bus interface");
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/sr030pc30.c b/drivers/media/video/sr030pc30.c
new file mode 100644
index 00000000..e9d95bda
--- /dev/null
+++ b/drivers/media/video/sr030pc30.c
@@ -0,0 +1,871 @@
+/*
+ * Driver for SiliconFile SR030PC30 VGA (1/10-Inch) Image Sensor with ISP
+ *
+ * Copyright (C) 2010 Samsung Electronics Co., Ltd
+ * Author: Sylwester Nawrocki, s.nawrocki@samsung.com
+ *
+ * Based on original driver authored by Dongsoo Nathaniel Kim
+ * and HeungJun Kim <riverful.kim@samsung.com>.
+ *
+ * Based on mt9v011 Micron Digital Image Sensor driver
+ * Copyright (c) 2009 Mauro Carvalho Chehab (mchehab@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-mediabus.h>
+#include <media/sr030pc30.h>
+
+static int debug;
+module_param(debug, int, 0644);
+
+#define MODULE_NAME "SR030PC30"
+
+/*
+ * Register offsets within a page
+ * b15..b8 - page id, b7..b0 - register address
+ */
+#define POWER_CTRL_REG 0x0001
+#define PAGEMODE_REG 0x03
+#define DEVICE_ID_REG 0x0004
+#define NOON010PC30_ID 0x86
+#define SR030PC30_ID 0x8C
+#define VDO_CTL1_REG 0x0010
+#define SUBSAMPL_NONE_VGA 0
+#define SUBSAMPL_QVGA 0x10
+#define SUBSAMPL_QQVGA 0x20
+#define VDO_CTL2_REG 0x0011
+#define SYNC_CTL_REG 0x0012
+#define WIN_ROWH_REG 0x0020
+#define WIN_ROWL_REG 0x0021
+#define WIN_COLH_REG 0x0022
+#define WIN_COLL_REG 0x0023
+#define WIN_HEIGHTH_REG 0x0024
+#define WIN_HEIGHTL_REG 0x0025
+#define WIN_WIDTHH_REG 0x0026
+#define WIN_WIDTHL_REG 0x0027
+#define HBLANKH_REG 0x0040
+#define HBLANKL_REG 0x0041
+#define VSYNCH_REG 0x0042
+#define VSYNCL_REG 0x0043
+/* page 10 */
+#define ISP_CTL_REG(n) (0x1010 + (n))
+#define YOFS_REG 0x1040
+#define DARK_YOFS_REG 0x1041
+#define AG_ABRTH_REG 0x1050
+#define SAT_CTL_REG 0x1060
+#define BSAT_REG 0x1061
+#define RSAT_REG 0x1062
+#define AG_SAT_TH_REG 0x1063
+/* page 11 */
+#define ZLPF_CTRL_REG 0x1110
+#define ZLPF_CTRL2_REG 0x1112
+#define ZLPF_AGH_THR_REG 0x1121
+#define ZLPF_THR_REG 0x1160
+#define ZLPF_DYN_THR_REG 0x1160
+/* page 12 */
+#define YCLPF_CTL1_REG 0x1240
+#define YCLPF_CTL2_REG 0x1241
+#define YCLPF_THR_REG 0x1250
+#define BLPF_CTL_REG 0x1270
+#define BLPF_THR1_REG 0x1274
+#define BLPF_THR2_REG 0x1275
+/* page 14 - Lens Shading Compensation */
+#define LENS_CTRL_REG 0x1410
+#define LENS_XCEN_REG 0x1420
+#define LENS_YCEN_REG 0x1421
+#define LENS_R_COMP_REG 0x1422
+#define LENS_G_COMP_REG 0x1423
+#define LENS_B_COMP_REG 0x1424
+/* page 15 - Color correction */
+#define CMC_CTL_REG 0x1510
+#define CMC_OFSGH_REG 0x1514
+#define CMC_OFSGL_REG 0x1516
+#define CMC_SIGN_REG 0x1517
+/* Color correction coefficients */
+#define CMC_COEF_REG(n) (0x1530 + (n))
+/* Color correction offset coefficients */
+#define CMC_OFS_REG(n) (0x1540 + (n))
+/* page 16 - Gamma correction */
+#define GMA_CTL_REG 0x1610
+/* Gamma correction coefficients 0.14 */
+#define GMA_COEF_REG(n) (0x1630 + (n))
+/* page 20 - Auto Exposure */
+#define AE_CTL1_REG 0x2010
+#define AE_CTL2_REG 0x2011
+#define AE_FRM_CTL_REG 0x2020
+#define AE_FINE_CTL_REG(n) (0x2028 + (n))
+#define EXP_TIMEH_REG 0x2083
+#define EXP_TIMEM_REG 0x2084
+#define EXP_TIMEL_REG 0x2085
+#define EXP_MMINH_REG 0x2086
+#define EXP_MMINL_REG 0x2087
+#define EXP_MMAXH_REG 0x2088
+#define EXP_MMAXM_REG 0x2089
+#define EXP_MMAXL_REG 0x208A
+/* page 22 - Auto White Balance */
+#define AWB_CTL1_REG 0x2210
+#define AWB_ENABLE 0x80
+#define AWB_CTL2_REG 0x2211
+#define MWB_ENABLE 0x01
+/* RGB gain control (manual WB) when AWB_CTL1[7]=0 */
+#define AWB_RGAIN_REG 0x2280
+#define AWB_GGAIN_REG 0x2281
+#define AWB_BGAIN_REG 0x2282
+#define AWB_RMAX_REG 0x2283
+#define AWB_RMIN_REG 0x2284
+#define AWB_BMAX_REG 0x2285
+#define AWB_BMIN_REG 0x2286
+/* R, B gain range in bright light conditions */
+#define AWB_RMAXB_REG 0x2287
+#define AWB_RMINB_REG 0x2288
+#define AWB_BMAXB_REG 0x2289
+#define AWB_BMINB_REG 0x228A
+/* manual white balance, when AWB_CTL2[0]=1 */
+#define MWB_RGAIN_REG 0x22B2
+#define MWB_BGAIN_REG 0x22B3
+/* the token to mark an array end */
+#define REG_TERM 0xFFFF
+
+/* Minimum and maximum exposure time in ms */
+#define EXPOS_MIN_MS 1
+#define EXPOS_MAX_MS 125
+
+struct sr030pc30_info {
+ struct v4l2_subdev sd;
+ const struct sr030pc30_platform_data *pdata;
+ const struct sr030pc30_format *curr_fmt;
+ const struct sr030pc30_frmsize *curr_win;
+ unsigned int auto_wb:1;
+ unsigned int auto_exp:1;
+ unsigned int hflip:1;
+ unsigned int vflip:1;
+ unsigned int sleep:1;
+ unsigned int exposure;
+ u8 blue_balance;
+ u8 red_balance;
+ u8 i2c_reg_page;
+};
+
+struct sr030pc30_format {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+ u16 ispctl1_reg;
+};
+
+struct sr030pc30_frmsize {
+ u16 width;
+ u16 height;
+ int vid_ctl1;
+};
+
+struct i2c_regval {
+ u16 addr;
+ u16 val;
+};
+
+static const struct v4l2_queryctrl sr030pc30_ctrl[] = {
+ {
+ .id = V4L2_CID_AUTO_WHITE_BALANCE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto White Balance",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ }, {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Balance",
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 64,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Balance",
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 64,
+ }, {
+ .id = V4L2_CID_EXPOSURE_AUTO,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Auto Exposure",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ }, {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = EXPOS_MIN_MS,
+ .maximum = EXPOS_MAX_MS,
+ .step = 1,
+ .default_value = 1,
+ }, {
+ }
+};
+
+/* supported resolutions */
+static const struct sr030pc30_frmsize sr030pc30_sizes[] = {
+ {
+ .width = 640,
+ .height = 480,
+ .vid_ctl1 = SUBSAMPL_NONE_VGA,
+ }, {
+ .width = 320,
+ .height = 240,
+ .vid_ctl1 = SUBSAMPL_QVGA,
+ }, {
+ .width = 160,
+ .height = 120,
+ .vid_ctl1 = SUBSAMPL_QQVGA,
+ },
+};
+
+/* supported pixel formats */
+static const struct sr030pc30_format sr030pc30_formats[] = {
+ {
+ .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .ispctl1_reg = 0x03,
+ }, {
+ .code = V4L2_MBUS_FMT_YVYU8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .ispctl1_reg = 0x02,
+ }, {
+ .code = V4L2_MBUS_FMT_VYUY8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .ispctl1_reg = 0,
+ }, {
+ .code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .ispctl1_reg = 0x01,
+ }, {
+ .code = V4L2_MBUS_FMT_RGB565_2X8_BE,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .ispctl1_reg = 0x40,
+ },
+};
+
+static const struct i2c_regval sr030pc30_base_regs[] = {
+ /* Window size and position within pixel matrix */
+ { WIN_ROWH_REG, 0x00 }, { WIN_ROWL_REG, 0x06 },
+ { WIN_COLH_REG, 0x00 }, { WIN_COLL_REG, 0x06 },
+ { WIN_HEIGHTH_REG, 0x01 }, { WIN_HEIGHTL_REG, 0xE0 },
+ { WIN_WIDTHH_REG, 0x02 }, { WIN_WIDTHL_REG, 0x80 },
+ { HBLANKH_REG, 0x01 }, { HBLANKL_REG, 0x50 },
+ { VSYNCH_REG, 0x00 }, { VSYNCL_REG, 0x14 },
+ { SYNC_CTL_REG, 0 },
+ /* Color corection and saturation */
+ { ISP_CTL_REG(0), 0x30 }, { YOFS_REG, 0x80 },
+ { DARK_YOFS_REG, 0x04 }, { AG_ABRTH_REG, 0x78 },
+ { SAT_CTL_REG, 0x1F }, { BSAT_REG, 0x90 },
+ { AG_SAT_TH_REG, 0xF0 }, { 0x1064, 0x80 },
+ { CMC_CTL_REG, 0x03 }, { CMC_OFSGH_REG, 0x3C },
+ { CMC_OFSGL_REG, 0x2C }, { CMC_SIGN_REG, 0x2F },
+ { CMC_COEF_REG(0), 0xCB }, { CMC_OFS_REG(0), 0x87 },
+ { CMC_COEF_REG(1), 0x61 }, { CMC_OFS_REG(1), 0x18 },
+ { CMC_COEF_REG(2), 0x16 }, { CMC_OFS_REG(2), 0x91 },
+ { CMC_COEF_REG(3), 0x23 }, { CMC_OFS_REG(3), 0x94 },
+ { CMC_COEF_REG(4), 0xCE }, { CMC_OFS_REG(4), 0x9f },
+ { CMC_COEF_REG(5), 0x2B }, { CMC_OFS_REG(5), 0x33 },
+ { CMC_COEF_REG(6), 0x01 }, { CMC_OFS_REG(6), 0x00 },
+ { CMC_COEF_REG(7), 0x34 }, { CMC_OFS_REG(7), 0x94 },
+ { CMC_COEF_REG(8), 0x75 }, { CMC_OFS_REG(8), 0x14 },
+ /* Color corection coefficients */
+ { GMA_CTL_REG, 0x03 }, { GMA_COEF_REG(0), 0x00 },
+ { GMA_COEF_REG(1), 0x19 }, { GMA_COEF_REG(2), 0x26 },
+ { GMA_COEF_REG(3), 0x3B }, { GMA_COEF_REG(4), 0x5D },
+ { GMA_COEF_REG(5), 0x79 }, { GMA_COEF_REG(6), 0x8E },
+ { GMA_COEF_REG(7), 0x9F }, { GMA_COEF_REG(8), 0xAF },
+ { GMA_COEF_REG(9), 0xBD }, { GMA_COEF_REG(10), 0xCA },
+ { GMA_COEF_REG(11), 0xDD }, { GMA_COEF_REG(12), 0xEC },
+ { GMA_COEF_REG(13), 0xF7 }, { GMA_COEF_REG(14), 0xFF },
+ /* Noise reduction, Z-LPF, YC-LPF and BLPF filters setup */
+ { ZLPF_CTRL_REG, 0x99 }, { ZLPF_CTRL2_REG, 0x0E },
+ { ZLPF_AGH_THR_REG, 0x29 }, { ZLPF_THR_REG, 0x0F },
+ { ZLPF_DYN_THR_REG, 0x63 }, { YCLPF_CTL1_REG, 0x23 },
+ { YCLPF_CTL2_REG, 0x3B }, { YCLPF_THR_REG, 0x05 },
+ { BLPF_CTL_REG, 0x1D }, { BLPF_THR1_REG, 0x05 },
+ { BLPF_THR2_REG, 0x04 },
+ /* Automatic white balance */
+ { AWB_CTL1_REG, 0xFB }, { AWB_CTL2_REG, 0x26 },
+ { AWB_RMAX_REG, 0x54 }, { AWB_RMIN_REG, 0x2B },
+ { AWB_BMAX_REG, 0x57 }, { AWB_BMIN_REG, 0x29 },
+ { AWB_RMAXB_REG, 0x50 }, { AWB_RMINB_REG, 0x43 },
+ { AWB_BMAXB_REG, 0x30 }, { AWB_BMINB_REG, 0x22 },
+ /* Auto exposure */
+ { AE_CTL1_REG, 0x8C }, { AE_CTL2_REG, 0x04 },
+ { AE_FRM_CTL_REG, 0x01 }, { AE_FINE_CTL_REG(0), 0x3F },
+ { AE_FINE_CTL_REG(1), 0xA3 }, { AE_FINE_CTL_REG(3), 0x34 },
+ /* Lens shading compensation */
+ { LENS_CTRL_REG, 0x01 }, { LENS_XCEN_REG, 0x80 },
+ { LENS_YCEN_REG, 0x70 }, { LENS_R_COMP_REG, 0x53 },
+ { LENS_G_COMP_REG, 0x40 }, { LENS_B_COMP_REG, 0x3e },
+ { REG_TERM, 0 },
+};
+
+static inline struct sr030pc30_info *to_sr030pc30(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct sr030pc30_info, sd);
+}
+
+static inline int set_i2c_page(struct sr030pc30_info *info,
+ struct i2c_client *client, unsigned int reg)
+{
+ int ret = 0;
+ u32 page = reg >> 8 & 0xFF;
+
+ if (info->i2c_reg_page != page && (reg & 0xFF) != 0x03) {
+ ret = i2c_smbus_write_byte_data(client, PAGEMODE_REG, page);
+ if (!ret)
+ info->i2c_reg_page = page;
+ }
+ return ret;
+}
+
+static int cam_i2c_read(struct v4l2_subdev *sd, u32 reg_addr)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct sr030pc30_info *info = to_sr030pc30(sd);
+
+ int ret = set_i2c_page(info, client, reg_addr);
+ if (!ret)
+ ret = i2c_smbus_read_byte_data(client, reg_addr & 0xFF);
+ return ret;
+}
+
+static int cam_i2c_write(struct v4l2_subdev *sd, u32 reg_addr, u32 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct sr030pc30_info *info = to_sr030pc30(sd);
+
+ int ret = set_i2c_page(info, client, reg_addr);
+ if (!ret)
+ ret = i2c_smbus_write_byte_data(
+ client, reg_addr & 0xFF, val);
+ return ret;
+}
+
+static inline int sr030pc30_bulk_write_reg(struct v4l2_subdev *sd,
+ const struct i2c_regval *msg)
+{
+ while (msg->addr != REG_TERM) {
+ int ret = cam_i2c_write(sd, msg->addr, msg->val);
+ if (ret)
+ return ret;
+ msg++;
+ }
+ return 0;
+}
+
+/* Device reset and sleep mode control */
+static int sr030pc30_pwr_ctrl(struct v4l2_subdev *sd,
+ bool reset, bool sleep)
+{
+ struct sr030pc30_info *info = to_sr030pc30(sd);
+ u8 reg = sleep ? 0xF1 : 0xF0;
+ int ret = 0;
+
+ if (reset)
+ ret = cam_i2c_write(sd, POWER_CTRL_REG, reg | 0x02);
+ if (!ret) {
+ ret = cam_i2c_write(sd, POWER_CTRL_REG, reg);
+ if (!ret) {
+ info->sleep = sleep;
+ if (reset)
+ info->i2c_reg_page = -1;
+ }
+ }
+ return ret;
+}
+
+static inline int sr030pc30_enable_autoexposure(struct v4l2_subdev *sd, int on)
+{
+ struct sr030pc30_info *info = to_sr030pc30(sd);
+ /* auto anti-flicker is also enabled here */
+ int ret = cam_i2c_write(sd, AE_CTL1_REG, on ? 0xDC : 0x0C);
+ if (!ret)
+ info->auto_exp = on;
+ return ret;
+}
+
+static int sr030pc30_set_exposure(struct v4l2_subdev *sd, int value)
+{
+ struct sr030pc30_info *info = to_sr030pc30(sd);
+
+ unsigned long expos = value * info->pdata->clk_rate / (8 * 1000);
+
+ int ret = cam_i2c_write(sd, EXP_TIMEH_REG, expos >> 16 & 0xFF);
+ if (!ret)
+ ret = cam_i2c_write(sd, EXP_TIMEM_REG, expos >> 8 & 0xFF);
+ if (!ret)
+ ret = cam_i2c_write(sd, EXP_TIMEL_REG, expos & 0xFF);
+ if (!ret) { /* Turn off AE */
+ info->exposure = value;
+ ret = sr030pc30_enable_autoexposure(sd, 0);
+ }
+ return ret;
+}
+
+/* Automatic white balance control */
+static int sr030pc30_enable_autowhitebalance(struct v4l2_subdev *sd, int on)
+{
+ struct sr030pc30_info *info = to_sr030pc30(sd);
+
+ int ret = cam_i2c_write(sd, AWB_CTL2_REG, on ? 0x2E : 0x2F);
+ if (!ret)
+ ret = cam_i2c_write(sd, AWB_CTL1_REG, on ? 0xFB : 0x7B);
+ if (!ret)
+ info->auto_wb = on;
+
+ return ret;
+}
+
+static int sr030pc30_set_flip(struct v4l2_subdev *sd)
+{
+ struct sr030pc30_info *info = to_sr030pc30(sd);
+
+ s32 reg = cam_i2c_read(sd, VDO_CTL2_REG);
+ if (reg < 0)
+ return reg;
+
+ reg &= 0x7C;
+ if (info->hflip)
+ reg |= 0x01;
+ if (info->vflip)
+ reg |= 0x02;
+ return cam_i2c_write(sd, VDO_CTL2_REG, reg | 0x80);
+}
+
+/* Configure resolution, color format and image flip */
+static int sr030pc30_set_params(struct v4l2_subdev *sd)
+{
+ struct sr030pc30_info *info = to_sr030pc30(sd);
+ int ret;
+
+ if (!info->curr_win)
+ return -EINVAL;
+
+ /* Configure the resolution through subsampling */
+ ret = cam_i2c_write(sd, VDO_CTL1_REG,
+ info->curr_win->vid_ctl1);
+
+ if (!ret && info->curr_fmt)
+ ret = cam_i2c_write(sd, ISP_CTL_REG(0),
+ info->curr_fmt->ispctl1_reg);
+ if (!ret)
+ ret = sr030pc30_set_flip(sd);
+
+ return ret;
+}
+
+/* Find nearest matching image pixel size. */
+static int sr030pc30_try_frame_size(struct v4l2_mbus_framefmt *mf)
+{
+ unsigned int min_err = ~0;
+ int i = ARRAY_SIZE(sr030pc30_sizes);
+ const struct sr030pc30_frmsize *fsize = &sr030pc30_sizes[0],
+ *match = NULL;
+ while (i--) {
+ int err = abs(fsize->width - mf->width)
+ + abs(fsize->height - mf->height);
+ if (err < min_err) {
+ min_err = err;
+ match = fsize;
+ }
+ fsize++;
+ }
+ if (match) {
+ mf->width = match->width;
+ mf->height = match->height;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int sr030pc30_queryctrl(struct v4l2_subdev *sd,
+ struct v4l2_queryctrl *qc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sr030pc30_ctrl); i++)
+ if (qc->id == sr030pc30_ctrl[i].id) {
+ *qc = sr030pc30_ctrl[i];
+ v4l2_dbg(1, debug, sd, "%s id: %d\n",
+ __func__, qc->id);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static inline int sr030pc30_set_bluebalance(struct v4l2_subdev *sd, int value)
+{
+ int ret = cam_i2c_write(sd, MWB_BGAIN_REG, value);
+ if (!ret)
+ to_sr030pc30(sd)->blue_balance = value;
+ return ret;
+}
+
+static inline int sr030pc30_set_redbalance(struct v4l2_subdev *sd, int value)
+{
+ int ret = cam_i2c_write(sd, MWB_RGAIN_REG, value);
+ if (!ret)
+ to_sr030pc30(sd)->red_balance = value;
+ return ret;
+}
+
+static int sr030pc30_s_ctrl(struct v4l2_subdev *sd,
+ struct v4l2_control *ctrl)
+{
+ int i, ret = 0;
+
+ for (i = 0; i < ARRAY_SIZE(sr030pc30_ctrl); i++)
+ if (ctrl->id == sr030pc30_ctrl[i].id)
+ break;
+
+ if (i == ARRAY_SIZE(sr030pc30_ctrl))
+ return -EINVAL;
+
+ if (ctrl->value < sr030pc30_ctrl[i].minimum ||
+ ctrl->value > sr030pc30_ctrl[i].maximum)
+ return -ERANGE;
+
+ v4l2_dbg(1, debug, sd, "%s: ctrl_id: %d, value: %d\n",
+ __func__, ctrl->id, ctrl->value);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ sr030pc30_enable_autowhitebalance(sd, ctrl->value);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ ret = sr030pc30_set_bluebalance(sd, ctrl->value);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ ret = sr030pc30_set_redbalance(sd, ctrl->value);
+ break;
+ case V4L2_CID_EXPOSURE_AUTO:
+ sr030pc30_enable_autoexposure(sd,
+ ctrl->value == V4L2_EXPOSURE_AUTO);
+ break;
+ case V4L2_CID_EXPOSURE:
+ ret = sr030pc30_set_exposure(sd, ctrl->value);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int sr030pc30_g_ctrl(struct v4l2_subdev *sd,
+ struct v4l2_control *ctrl)
+{
+ struct sr030pc30_info *info = to_sr030pc30(sd);
+
+ v4l2_dbg(1, debug, sd, "%s: id: %d\n", __func__, ctrl->id);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ ctrl->value = info->auto_wb;
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ ctrl->value = info->blue_balance;
+ break;
+ case V4L2_CID_RED_BALANCE:
+ ctrl->value = info->red_balance;
+ break;
+ case V4L2_CID_EXPOSURE_AUTO:
+ ctrl->value = info->auto_exp;
+ break;
+ case V4L2_CID_EXPOSURE:
+ ctrl->value = info->exposure;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int sr030pc30_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (!code || index >= ARRAY_SIZE(sr030pc30_formats))
+ return -EINVAL;
+
+ *code = sr030pc30_formats[index].code;
+ return 0;
+}
+
+static int sr030pc30_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct sr030pc30_info *info = to_sr030pc30(sd);
+ int ret;
+
+ if (!mf)
+ return -EINVAL;
+
+ if (!info->curr_win || !info->curr_fmt) {
+ ret = sr030pc30_set_params(sd);
+ if (ret)
+ return ret;
+ }
+
+ mf->width = info->curr_win->width;
+ mf->height = info->curr_win->height;
+ mf->code = info->curr_fmt->code;
+ mf->colorspace = info->curr_fmt->colorspace;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+/* Return nearest media bus frame format. */
+static const struct sr030pc30_format *try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ int i = ARRAY_SIZE(sr030pc30_formats);
+
+ sr030pc30_try_frame_size(mf);
+
+ while (i--)
+ if (mf->code == sr030pc30_formats[i].code)
+ break;
+
+ mf->code = sr030pc30_formats[i].code;
+
+ return &sr030pc30_formats[i];
+}
+
+/* Return nearest media bus frame format. */
+static int sr030pc30_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ if (!sd || !mf)
+ return -EINVAL;
+
+ try_fmt(sd, mf);
+ return 0;
+}
+
+static int sr030pc30_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct sr030pc30_info *info = to_sr030pc30(sd);
+
+ if (!sd || !mf)
+ return -EINVAL;
+
+ info->curr_fmt = try_fmt(sd, mf);
+
+ return sr030pc30_set_params(sd);
+}
+
+static int sr030pc30_base_config(struct v4l2_subdev *sd)
+{
+ struct sr030pc30_info *info = to_sr030pc30(sd);
+ int ret;
+ unsigned long expmin, expmax;
+
+ ret = sr030pc30_bulk_write_reg(sd, sr030pc30_base_regs);
+ if (!ret) {
+ info->curr_fmt = &sr030pc30_formats[0];
+ info->curr_win = &sr030pc30_sizes[0];
+ ret = sr030pc30_set_params(sd);
+ }
+ if (!ret)
+ ret = sr030pc30_pwr_ctrl(sd, false, false);
+
+ if (!ret && !info->pdata)
+ return ret;
+
+ expmin = EXPOS_MIN_MS * info->pdata->clk_rate / (8 * 1000);
+ expmax = EXPOS_MAX_MS * info->pdata->clk_rate / (8 * 1000);
+
+ v4l2_dbg(1, debug, sd, "%s: expmin= %lx, expmax= %lx", __func__,
+ expmin, expmax);
+
+ /* Setting up manual exposure time range */
+ ret = cam_i2c_write(sd, EXP_MMINH_REG, expmin >> 8 & 0xFF);
+ if (!ret)
+ ret = cam_i2c_write(sd, EXP_MMINL_REG, expmin & 0xFF);
+ if (!ret)
+ ret = cam_i2c_write(sd, EXP_MMAXH_REG, expmax >> 16 & 0xFF);
+ if (!ret)
+ ret = cam_i2c_write(sd, EXP_MMAXM_REG, expmax >> 8 & 0xFF);
+ if (!ret)
+ ret = cam_i2c_write(sd, EXP_MMAXL_REG, expmax & 0xFF);
+
+ return ret;
+}
+
+static int sr030pc30_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct sr030pc30_info *info = to_sr030pc30(sd);
+ const struct sr030pc30_platform_data *pdata = info->pdata;
+ int ret;
+
+ if (pdata == NULL) {
+ WARN(1, "No platform data!\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Put sensor into power sleep mode before switching off
+ * power and disabling MCLK.
+ */
+ if (!on)
+ sr030pc30_pwr_ctrl(sd, false, true);
+
+ /* set_power controls sensor's power and clock */
+ if (pdata->set_power) {
+ ret = pdata->set_power(&client->dev, on);
+ if (ret)
+ return ret;
+ }
+
+ if (on) {
+ ret = sr030pc30_base_config(sd);
+ } else {
+ ret = 0;
+ info->curr_win = NULL;
+ info->curr_fmt = NULL;
+ }
+
+ return ret;
+}
+
+static const struct v4l2_subdev_core_ops sr030pc30_core_ops = {
+ .s_power = sr030pc30_s_power,
+ .queryctrl = sr030pc30_queryctrl,
+ .s_ctrl = sr030pc30_s_ctrl,
+ .g_ctrl = sr030pc30_g_ctrl,
+};
+
+static const struct v4l2_subdev_video_ops sr030pc30_video_ops = {
+ .g_mbus_fmt = sr030pc30_g_fmt,
+ .s_mbus_fmt = sr030pc30_s_fmt,
+ .try_mbus_fmt = sr030pc30_try_fmt,
+ .enum_mbus_fmt = sr030pc30_enum_fmt,
+};
+
+static const struct v4l2_subdev_ops sr030pc30_ops = {
+ .core = &sr030pc30_core_ops,
+ .video = &sr030pc30_video_ops,
+};
+
+/*
+ * Detect sensor type. Return 0 if SR030PC30 was detected
+ * or -ENODEV otherwise.
+ */
+static int sr030pc30_detect(struct i2c_client *client)
+{
+ const struct sr030pc30_platform_data *pdata
+ = client->dev.platform_data;
+ int ret;
+
+ /* Enable sensor's power and clock */
+ if (pdata->set_power) {
+ ret = pdata->set_power(&client->dev, 1);
+ if (ret)
+ return ret;
+ }
+
+ ret = i2c_smbus_read_byte_data(client, DEVICE_ID_REG);
+
+ if (pdata->set_power)
+ pdata->set_power(&client->dev, 0);
+
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: I2C read failed\n", __func__);
+ return ret;
+ }
+
+ return ret == SR030PC30_ID ? 0 : -ENODEV;
+}
+
+
+static int sr030pc30_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct sr030pc30_info *info;
+ struct v4l2_subdev *sd;
+ const struct sr030pc30_platform_data *pdata
+ = client->dev.platform_data;
+ int ret;
+
+ if (!pdata) {
+ dev_err(&client->dev, "No platform data!");
+ return -EIO;
+ }
+
+ ret = sr030pc30_detect(client);
+ if (ret)
+ return ret;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ sd = &info->sd;
+ strcpy(sd->name, MODULE_NAME);
+ info->pdata = client->dev.platform_data;
+
+ v4l2_i2c_subdev_init(sd, client, &sr030pc30_ops);
+
+ info->i2c_reg_page = -1;
+ info->hflip = 1;
+ info->auto_exp = 1;
+ info->exposure = 30;
+
+ return 0;
+}
+
+static int sr030pc30_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct sr030pc30_info *info = to_sr030pc30(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(info);
+ return 0;
+}
+
+static const struct i2c_device_id sr030pc30_id[] = {
+ { MODULE_NAME, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, sr030pc30_id);
+
+
+static struct i2c_driver sr030pc30_i2c_driver = {
+ .driver = {
+ .name = MODULE_NAME
+ },
+ .probe = sr030pc30_probe,
+ .remove = sr030pc30_remove,
+ .id_table = sr030pc30_id,
+};
+
+module_i2c_driver(sr030pc30_i2c_driver);
+
+MODULE_DESCRIPTION("Siliconfile SR030PC30 camera driver");
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/stk-sensor.c b/drivers/media/video/stk-sensor.c
new file mode 100644
index 00000000..e546b014
--- /dev/null
+++ b/drivers/media/video/stk-sensor.c
@@ -0,0 +1,595 @@
+/* stk-sensor.c: Driver for ov96xx sensor (used in some Syntek webcams)
+ *
+ * Copyright 2007-2008 Jaime Velasco Juan <jsagarribay@gmail.com>
+ *
+ * Some parts derived from ov7670.c:
+ * Copyright 2006 One Laptop Per Child Association, Inc. Written
+ * by Jonathan Corbet with substantial inspiration from Mark
+ * McClelland's ovcamchip code.
+ *
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
+ *
+ * This file may be distributed under the terms of the GNU General
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Controlling the sensor via the STK1125 vendor specific control interface:
+ * The camera uses an OmniVision sensor and the stk1125 provides an
+ * SCCB(i2c)-USB bridge which let us program the sensor.
+ * In my case the sensor id is 0x9652, it can be read from sensor's register
+ * 0x0A and 0x0B as follows:
+ * - read register #R:
+ * output #R to index 0x0208
+ * output 0x0070 to index 0x0200
+ * input 1 byte from index 0x0201 (some kind of status register)
+ * until its value is 0x01
+ * input 1 byte from index 0x0209. This is the value of #R
+ * - write value V to register #R
+ * output #R to index 0x0204
+ * output V to index 0x0205
+ * output 0x0005 to index 0x0200
+ * input 1 byte from index 0x0201 until its value becomes 0x04
+ */
+
+/* It seems the i2c bus is controlled with these registers */
+
+#include "stk-webcam.h"
+
+#define STK_IIC_BASE (0x0200)
+# define STK_IIC_OP (STK_IIC_BASE)
+# define STK_IIC_OP_TX (0x05)
+# define STK_IIC_OP_RX (0x70)
+# define STK_IIC_STAT (STK_IIC_BASE+1)
+# define STK_IIC_STAT_TX_OK (0x04)
+# define STK_IIC_STAT_RX_OK (0x01)
+/* I don't know what does this register.
+ * when it is 0x00 or 0x01, we cannot talk to the sensor,
+ * other values work */
+# define STK_IIC_ENABLE (STK_IIC_BASE+2)
+# define STK_IIC_ENABLE_NO (0x00)
+/* This is what the driver writes in windows */
+# define STK_IIC_ENABLE_YES (0x1e)
+/*
+ * Address of the slave. Seems like the binary driver look for the
+ * sensor in multiple places, attempting a reset sequence.
+ * We only know about the ov9650
+ */
+# define STK_IIC_ADDR (STK_IIC_BASE+3)
+# define STK_IIC_TX_INDEX (STK_IIC_BASE+4)
+# define STK_IIC_TX_VALUE (STK_IIC_BASE+5)
+# define STK_IIC_RX_INDEX (STK_IIC_BASE+8)
+# define STK_IIC_RX_VALUE (STK_IIC_BASE+9)
+
+#define MAX_RETRIES (50)
+
+#define SENSOR_ADDRESS (0x60)
+
+/* From ov7670.c (These registers aren't fully accurate) */
+
+/* Registers */
+#define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */
+#define REG_BLUE 0x01 /* blue gain */
+#define REG_RED 0x02 /* red gain */
+#define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */
+#define REG_COM1 0x04 /* Control 1 */
+#define COM1_CCIR656 0x40 /* CCIR656 enable */
+#define COM1_QFMT 0x20 /* QVGA/QCIF format */
+#define COM1_SKIP_0 0x00 /* Do not skip any row */
+#define COM1_SKIP_2 0x04 /* Skip 2 rows of 4 */
+#define COM1_SKIP_3 0x08 /* Skip 3 rows of 4 */
+#define REG_BAVE 0x05 /* U/B Average level */
+#define REG_GbAVE 0x06 /* Y/Gb Average level */
+#define REG_AECHH 0x07 /* AEC MS 5 bits */
+#define REG_RAVE 0x08 /* V/R Average level */
+#define REG_COM2 0x09 /* Control 2 */
+#define COM2_SSLEEP 0x10 /* Soft sleep mode */
+#define REG_PID 0x0a /* Product ID MSB */
+#define REG_VER 0x0b /* Product ID LSB */
+#define REG_COM3 0x0c /* Control 3 */
+#define COM3_SWAP 0x40 /* Byte swap */
+#define COM3_SCALEEN 0x08 /* Enable scaling */
+#define COM3_DCWEN 0x04 /* Enable downsamp/crop/window */
+#define REG_COM4 0x0d /* Control 4 */
+#define REG_COM5 0x0e /* All "reserved" */
+#define REG_COM6 0x0f /* Control 6 */
+#define REG_AECH 0x10 /* More bits of AEC value */
+#define REG_CLKRC 0x11 /* Clock control */
+#define CLK_PLL 0x80 /* Enable internal PLL */
+#define CLK_EXT 0x40 /* Use external clock directly */
+#define CLK_SCALE 0x3f /* Mask for internal clock scale */
+#define REG_COM7 0x12 /* Control 7 */
+#define COM7_RESET 0x80 /* Register reset */
+#define COM7_FMT_MASK 0x38
+#define COM7_FMT_SXGA 0x00
+#define COM7_FMT_VGA 0x40
+#define COM7_FMT_CIF 0x20 /* CIF format */
+#define COM7_FMT_QVGA 0x10 /* QVGA format */
+#define COM7_FMT_QCIF 0x08 /* QCIF format */
+#define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */
+#define COM7_YUV 0x00 /* YUV */
+#define COM7_BAYER 0x01 /* Bayer format */
+#define COM7_PBAYER 0x05 /* "Processed bayer" */
+#define REG_COM8 0x13 /* Control 8 */
+#define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */
+#define COM8_AECSTEP 0x40 /* Unlimited AEC step size */
+#define COM8_BFILT 0x20 /* Band filter enable */
+#define COM8_AGC 0x04 /* Auto gain enable */
+#define COM8_AWB 0x02 /* White balance enable */
+#define COM8_AEC 0x01 /* Auto exposure enable */
+#define REG_COM9 0x14 /* Control 9 - gain ceiling */
+#define REG_COM10 0x15 /* Control 10 */
+#define COM10_HSYNC 0x40 /* HSYNC instead of HREF */
+#define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */
+#define COM10_HREF_REV 0x08 /* Reverse HREF */
+#define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */
+#define COM10_VS_NEG 0x02 /* VSYNC negative */
+#define COM10_HS_NEG 0x01 /* HSYNC negative */
+#define REG_HSTART 0x17 /* Horiz start high bits */
+#define REG_HSTOP 0x18 /* Horiz stop high bits */
+#define REG_VSTART 0x19 /* Vert start high bits */
+#define REG_VSTOP 0x1a /* Vert stop high bits */
+#define REG_PSHFT 0x1b /* Pixel delay after HREF */
+#define REG_MIDH 0x1c /* Manuf. ID high */
+#define REG_MIDL 0x1d /* Manuf. ID low */
+#define REG_MVFP 0x1e /* Mirror / vflip */
+#define MVFP_MIRROR 0x20 /* Mirror image */
+#define MVFP_FLIP 0x10 /* Vertical flip */
+
+#define REG_AEW 0x24 /* AGC upper limit */
+#define REG_AEB 0x25 /* AGC lower limit */
+#define REG_VPT 0x26 /* AGC/AEC fast mode op region */
+#define REG_ADVFL 0x2d /* Insert dummy lines (LSB) */
+#define REG_ADVFH 0x2e /* Insert dummy lines (MSB) */
+#define REG_HSYST 0x30 /* HSYNC rising edge delay */
+#define REG_HSYEN 0x31 /* HSYNC falling edge delay */
+#define REG_HREF 0x32 /* HREF pieces */
+#define REG_TSLB 0x3a /* lots of stuff */
+#define TSLB_YLAST 0x04 /* UYVY or VYUY - see com13 */
+#define TSLB_BYTEORD 0x08 /* swap bytes in 16bit mode? */
+#define REG_COM11 0x3b /* Control 11 */
+#define COM11_NIGHT 0x80 /* NIght mode enable */
+#define COM11_NMFR 0x60 /* Two bit NM frame rate */
+#define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */
+#define COM11_50HZ 0x08 /* Manual 50Hz select */
+#define COM11_EXP 0x02
+#define REG_COM12 0x3c /* Control 12 */
+#define COM12_HREF 0x80 /* HREF always */
+#define REG_COM13 0x3d /* Control 13 */
+#define COM13_GAMMA 0x80 /* Gamma enable */
+#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */
+#define COM13_CMATRIX 0x10 /* Enable color matrix for RGB or YUV */
+#define COM13_UVSWAP 0x01 /* V before U - w/TSLB */
+#define REG_COM14 0x3e /* Control 14 */
+#define COM14_DCWEN 0x10 /* DCW/PCLK-scale enable */
+#define REG_EDGE 0x3f /* Edge enhancement factor */
+#define REG_COM15 0x40 /* Control 15 */
+#define COM15_R10F0 0x00 /* Data range 10 to F0 */
+#define COM15_R01FE 0x80 /* 01 to FE */
+#define COM15_R00FF 0xc0 /* 00 to FF */
+#define COM15_RGB565 0x10 /* RGB565 output */
+#define COM15_RGBFIXME 0x20 /* FIXME */
+#define COM15_RGB555 0x30 /* RGB555 output */
+#define REG_COM16 0x41 /* Control 16 */
+#define COM16_AWBGAIN 0x08 /* AWB gain enable */
+#define REG_COM17 0x42 /* Control 17 */
+#define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */
+#define COM17_CBAR 0x08 /* DSP Color bar */
+
+/*
+ * This matrix defines how the colors are generated, must be
+ * tweaked to adjust hue and saturation.
+ *
+ * Order: v-red, v-green, v-blue, u-red, u-green, u-blue
+ *
+ * They are nine-bit signed quantities, with the sign bit
+ * stored in 0x58. Sign for v-red is bit 0, and up from there.
+ */
+#define REG_CMATRIX_BASE 0x4f
+#define CMATRIX_LEN 6
+#define REG_CMATRIX_SIGN 0x58
+
+
+#define REG_BRIGHT 0x55 /* Brightness */
+#define REG_CONTRAS 0x56 /* Contrast control */
+
+#define REG_GFIX 0x69 /* Fix gain control */
+
+#define REG_RGB444 0x8c /* RGB 444 control */
+#define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */
+#define R444_RGBX 0x01 /* Empty nibble at end */
+
+#define REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */
+#define REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */
+
+#define REG_BD50MAX 0xa5 /* 50hz banding step limit */
+#define REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */
+#define REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */
+#define REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */
+#define REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */
+#define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */
+#define REG_BD60MAX 0xab /* 60hz banding step limit */
+
+
+
+
+/* Returns 0 if OK */
+static int stk_sensor_outb(struct stk_camera *dev, u8 reg, u8 val)
+{
+ int i = 0;
+ int tmpval = 0;
+
+ if (stk_camera_write_reg(dev, STK_IIC_TX_INDEX, reg))
+ return 1;
+ if (stk_camera_write_reg(dev, STK_IIC_TX_VALUE, val))
+ return 1;
+ if (stk_camera_write_reg(dev, STK_IIC_OP, STK_IIC_OP_TX))
+ return 1;
+ do {
+ if (stk_camera_read_reg(dev, STK_IIC_STAT, &tmpval))
+ return 1;
+ i++;
+ } while (tmpval == 0 && i < MAX_RETRIES);
+ if (tmpval != STK_IIC_STAT_TX_OK) {
+ if (tmpval)
+ STK_ERROR("stk_sensor_outb failed, status=0x%02x\n",
+ tmpval);
+ return 1;
+ } else
+ return 0;
+}
+
+static int stk_sensor_inb(struct stk_camera *dev, u8 reg, u8 *val)
+{
+ int i = 0;
+ int tmpval = 0;
+
+ if (stk_camera_write_reg(dev, STK_IIC_RX_INDEX, reg))
+ return 1;
+ if (stk_camera_write_reg(dev, STK_IIC_OP, STK_IIC_OP_RX))
+ return 1;
+ do {
+ if (stk_camera_read_reg(dev, STK_IIC_STAT, &tmpval))
+ return 1;
+ i++;
+ } while (tmpval == 0 && i < MAX_RETRIES);
+ if (tmpval != STK_IIC_STAT_RX_OK) {
+ if (tmpval)
+ STK_ERROR("stk_sensor_inb failed, status=0x%02x\n",
+ tmpval);
+ return 1;
+ }
+
+ if (stk_camera_read_reg(dev, STK_IIC_RX_VALUE, &tmpval))
+ return 1;
+
+ *val = (u8) tmpval;
+ return 0;
+}
+
+static int stk_sensor_write_regvals(struct stk_camera *dev,
+ struct regval *rv)
+{
+ int ret;
+ if (rv == NULL)
+ return 0;
+ while (rv->reg != 0xff || rv->val != 0xff) {
+ ret = stk_sensor_outb(dev, rv->reg, rv->val);
+ if (ret != 0)
+ return ret;
+ rv++;
+ }
+ return 0;
+}
+
+int stk_sensor_sleep(struct stk_camera *dev)
+{
+ u8 tmp;
+ return stk_sensor_inb(dev, REG_COM2, &tmp)
+ || stk_sensor_outb(dev, REG_COM2, tmp|COM2_SSLEEP);
+}
+
+int stk_sensor_wakeup(struct stk_camera *dev)
+{
+ u8 tmp;
+ return stk_sensor_inb(dev, REG_COM2, &tmp)
+ || stk_sensor_outb(dev, REG_COM2, tmp&~COM2_SSLEEP);
+}
+
+static struct regval ov_initvals[] = {
+ {REG_CLKRC, CLK_PLL},
+ {REG_COM11, 0x01},
+ {0x6a, 0x7d},
+ {REG_AECH, 0x40},
+ {REG_GAIN, 0x00},
+ {REG_BLUE, 0x80},
+ {REG_RED, 0x80},
+ /* Do not enable fast AEC for now */
+ /*{REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC},*/
+ {REG_COM8, COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC},
+ {0x39, 0x50}, {0x38, 0x93},
+ {0x37, 0x00}, {0x35, 0x81},
+ {REG_COM5, 0x20},
+ {REG_COM1, 0x00},
+ {REG_COM3, 0x00},
+ {REG_COM4, 0x00},
+ {REG_PSHFT, 0x00},
+ {0x16, 0x07},
+ {0x33, 0xe2}, {0x34, 0xbf},
+ {REG_COM16, 0x00},
+ {0x96, 0x04},
+ /* Gamma curve values */
+/* { 0x7a, 0x20 }, { 0x7b, 0x10 },
+ { 0x7c, 0x1e }, { 0x7d, 0x35 },
+ { 0x7e, 0x5a }, { 0x7f, 0x69 },
+ { 0x80, 0x76 }, { 0x81, 0x80 },
+ { 0x82, 0x88 }, { 0x83, 0x8f },
+ { 0x84, 0x96 }, { 0x85, 0xa3 },
+ { 0x86, 0xaf }, { 0x87, 0xc4 },
+ { 0x88, 0xd7 }, { 0x89, 0xe8 },
+*/
+ {REG_GFIX, 0x40},
+ {0x8e, 0x00},
+ {REG_COM12, 0x73},
+ {0x8f, 0xdf}, {0x8b, 0x06},
+ {0x8c, 0x20},
+ {0x94, 0x88}, {0x95, 0x88},
+/* {REG_COM15, 0xc1}, TODO */
+ {0x29, 0x3f},
+ {REG_COM6, 0x42},
+ {REG_BD50MAX, 0x80},
+ {REG_HAECC6, 0xb8}, {REG_HAECC7, 0x92},
+ {REG_BD60MAX, 0x0a},
+ {0x90, 0x00}, {0x91, 0x00},
+ {REG_HAECC1, 0x00}, {REG_HAECC2, 0x00},
+ {REG_AEW, 0x68}, {REG_AEB, 0x5c},
+ {REG_VPT, 0xc3},
+ {REG_COM9, 0x2e},
+ {0x2a, 0x00}, {0x2b, 0x00},
+
+ {0xff, 0xff}, /* END MARKER */
+};
+
+/* Probe the I2C bus and initialise the sensor chip */
+int stk_sensor_init(struct stk_camera *dev)
+{
+ u8 idl = 0;
+ u8 idh = 0;
+
+ if (stk_camera_write_reg(dev, STK_IIC_ENABLE, STK_IIC_ENABLE_YES)
+ || stk_camera_write_reg(dev, STK_IIC_ADDR, SENSOR_ADDRESS)
+ || stk_sensor_outb(dev, REG_COM7, COM7_RESET)) {
+ STK_ERROR("Sensor resetting failed\n");
+ return -ENODEV;
+ }
+ msleep(10);
+ /* Read the manufacturer ID: ov = 0x7FA2 */
+ if (stk_sensor_inb(dev, REG_MIDH, &idh)
+ || stk_sensor_inb(dev, REG_MIDL, &idl)) {
+ STK_ERROR("Strange error reading sensor ID\n");
+ return -ENODEV;
+ }
+ if (idh != 0x7f || idl != 0xa2) {
+ STK_ERROR("Huh? you don't have a sensor from ovt\n");
+ return -ENODEV;
+ }
+ if (stk_sensor_inb(dev, REG_PID, &idh)
+ || stk_sensor_inb(dev, REG_VER, &idl)) {
+ STK_ERROR("Could not read sensor model\n");
+ return -ENODEV;
+ }
+ stk_sensor_write_regvals(dev, ov_initvals);
+ msleep(10);
+ STK_INFO("OmniVision sensor detected, id %02X%02X"
+ " at address %x\n", idh, idl, SENSOR_ADDRESS);
+ return 0;
+}
+
+/* V4L2_PIX_FMT_UYVY */
+static struct regval ov_fmt_uyvy[] = {
+ {REG_TSLB, TSLB_YLAST|0x08 },
+ { 0x4f, 0x80 }, /* "matrix coefficient 1" */
+ { 0x50, 0x80 }, /* "matrix coefficient 2" */
+ { 0x51, 0 }, /* vb */
+ { 0x52, 0x22 }, /* "matrix coefficient 4" */
+ { 0x53, 0x5e }, /* "matrix coefficient 5" */
+ { 0x54, 0x80 }, /* "matrix coefficient 6" */
+ {REG_COM13, COM13_UVSAT|COM13_CMATRIX},
+ {REG_COM15, COM15_R00FF },
+ {0xff, 0xff}, /* END MARKER */
+};
+/* V4L2_PIX_FMT_YUYV */
+static struct regval ov_fmt_yuyv[] = {
+ {REG_TSLB, 0 },
+ { 0x4f, 0x80 }, /* "matrix coefficient 1" */
+ { 0x50, 0x80 }, /* "matrix coefficient 2" */
+ { 0x51, 0 }, /* vb */
+ { 0x52, 0x22 }, /* "matrix coefficient 4" */
+ { 0x53, 0x5e }, /* "matrix coefficient 5" */
+ { 0x54, 0x80 }, /* "matrix coefficient 6" */
+ {REG_COM13, COM13_UVSAT|COM13_CMATRIX},
+ {REG_COM15, COM15_R00FF },
+ {0xff, 0xff}, /* END MARKER */
+};
+
+/* V4L2_PIX_FMT_RGB565X rrrrrggg gggbbbbb */
+static struct regval ov_fmt_rgbr[] = {
+ { REG_RGB444, 0 }, /* No RGB444 please */
+ {REG_TSLB, 0x00},
+ { REG_COM1, 0x0 },
+ { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */
+ { 0x4f, 0xb3 }, /* "matrix coefficient 1" */
+ { 0x50, 0xb3 }, /* "matrix coefficient 2" */
+ { 0x51, 0 }, /* vb */
+ { 0x52, 0x3d }, /* "matrix coefficient 4" */
+ { 0x53, 0xa7 }, /* "matrix coefficient 5" */
+ { 0x54, 0xe4 }, /* "matrix coefficient 6" */
+ { REG_COM13, COM13_GAMMA },
+ { REG_COM15, COM15_RGB565|COM15_R00FF },
+ { 0xff, 0xff },
+};
+
+/* V4L2_PIX_FMT_RGB565 gggbbbbb rrrrrggg */
+static struct regval ov_fmt_rgbp[] = {
+ { REG_RGB444, 0 }, /* No RGB444 please */
+ {REG_TSLB, TSLB_BYTEORD },
+ { REG_COM1, 0x0 },
+ { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */
+ { 0x4f, 0xb3 }, /* "matrix coefficient 1" */
+ { 0x50, 0xb3 }, /* "matrix coefficient 2" */
+ { 0x51, 0 }, /* vb */
+ { 0x52, 0x3d }, /* "matrix coefficient 4" */
+ { 0x53, 0xa7 }, /* "matrix coefficient 5" */
+ { 0x54, 0xe4 }, /* "matrix coefficient 6" */
+ { REG_COM13, COM13_GAMMA },
+ { REG_COM15, COM15_RGB565|COM15_R00FF },
+ { 0xff, 0xff },
+};
+
+/* V4L2_PIX_FMT_SRGGB8 */
+static struct regval ov_fmt_bayer[] = {
+ /* This changes color order */
+ {REG_TSLB, 0x40}, /* BGGR */
+ /* {REG_TSLB, 0x08}, */ /* BGGR with vertical image flipping */
+ {REG_COM15, COM15_R00FF },
+ {0xff, 0xff}, /* END MARKER */
+};
+/*
+ * Store a set of start/stop values into the camera.
+ */
+static int stk_sensor_set_hw(struct stk_camera *dev,
+ int hstart, int hstop, int vstart, int vstop)
+{
+ int ret;
+ unsigned char v;
+/*
+ * Horizontal: 11 bits, top 8 live in hstart and hstop. Bottom 3 of
+ * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is
+ * a mystery "edge offset" value in the top two bits of href.
+ */
+ ret = stk_sensor_outb(dev, REG_HSTART, (hstart >> 3) & 0xff);
+ ret += stk_sensor_outb(dev, REG_HSTOP, (hstop >> 3) & 0xff);
+ ret += stk_sensor_inb(dev, REG_HREF, &v);
+ v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7);
+ msleep(10);
+ ret += stk_sensor_outb(dev, REG_HREF, v);
+/*
+ * Vertical: similar arrangement (note: this is different from ov7670.c)
+ */
+ ret += stk_sensor_outb(dev, REG_VSTART, (vstart >> 3) & 0xff);
+ ret += stk_sensor_outb(dev, REG_VSTOP, (vstop >> 3) & 0xff);
+ ret += stk_sensor_inb(dev, REG_VREF, &v);
+ v = (v & 0xc0) | ((vstop & 0x7) << 3) | (vstart & 0x7);
+ msleep(10);
+ ret += stk_sensor_outb(dev, REG_VREF, v);
+ return ret;
+}
+
+
+int stk_sensor_configure(struct stk_camera *dev)
+{
+ int com7;
+ /*
+ * We setup the sensor to output dummy lines in low-res modes,
+ * so we don't get absurdly hight framerates.
+ */
+ unsigned dummylines;
+ int flip;
+ struct regval *rv;
+
+ switch (dev->vsettings.mode) {
+ case MODE_QCIF: com7 = COM7_FMT_QCIF;
+ dummylines = 604;
+ break;
+ case MODE_QVGA: com7 = COM7_FMT_QVGA;
+ dummylines = 267;
+ break;
+ case MODE_CIF: com7 = COM7_FMT_CIF;
+ dummylines = 412;
+ break;
+ case MODE_VGA: com7 = COM7_FMT_VGA;
+ dummylines = 11;
+ break;
+ case MODE_SXGA: com7 = COM7_FMT_SXGA;
+ dummylines = 0;
+ break;
+ default: STK_ERROR("Unsupported mode %d\n", dev->vsettings.mode);
+ return -EFAULT;
+ }
+ switch (dev->vsettings.palette) {
+ case V4L2_PIX_FMT_UYVY:
+ com7 |= COM7_YUV;
+ rv = ov_fmt_uyvy;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ com7 |= COM7_YUV;
+ rv = ov_fmt_yuyv;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ com7 |= COM7_RGB;
+ rv = ov_fmt_rgbp;
+ break;
+ case V4L2_PIX_FMT_RGB565X:
+ com7 |= COM7_RGB;
+ rv = ov_fmt_rgbr;
+ break;
+ case V4L2_PIX_FMT_SBGGR8:
+ com7 |= COM7_PBAYER;
+ rv = ov_fmt_bayer;
+ break;
+ default: STK_ERROR("Unsupported colorspace\n");
+ return -EFAULT;
+ }
+ /*FIXME sometimes the sensor go to a bad state
+ stk_sensor_write_regvals(dev, ov_initvals); */
+ stk_sensor_outb(dev, REG_COM7, com7);
+ msleep(50);
+ stk_sensor_write_regvals(dev, rv);
+ flip = (dev->vsettings.vflip?MVFP_FLIP:0)
+ | (dev->vsettings.hflip?MVFP_MIRROR:0);
+ stk_sensor_outb(dev, REG_MVFP, flip);
+ if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8
+ && !dev->vsettings.vflip)
+ stk_sensor_outb(dev, REG_TSLB, 0x08);
+ stk_sensor_outb(dev, REG_ADVFH, dummylines >> 8);
+ stk_sensor_outb(dev, REG_ADVFL, dummylines & 0xff);
+ msleep(50);
+ switch (dev->vsettings.mode) {
+ case MODE_VGA:
+ if (stk_sensor_set_hw(dev, 302, 1582, 6, 486))
+ STK_ERROR("stk_sensor_set_hw failed (VGA)\n");
+ break;
+ case MODE_SXGA:
+ case MODE_CIF:
+ case MODE_QVGA:
+ case MODE_QCIF:
+ /*FIXME These settings seem ignored by the sensor
+ if (stk_sensor_set_hw(dev, 220, 1500, 10, 1034))
+ STK_ERROR("stk_sensor_set_hw failed (SXGA)\n");
+ */
+ break;
+ }
+ msleep(10);
+ return 0;
+}
+
+int stk_sensor_set_brightness(struct stk_camera *dev, int br)
+{
+ if (br < 0 || br > 0xff)
+ return -EINVAL;
+ stk_sensor_outb(dev, REG_AEB, max(0x00, br - 6));
+ stk_sensor_outb(dev, REG_AEW, min(0xff, br + 6));
+ return 0;
+}
+
diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c
new file mode 100644
index 00000000..d427f843
--- /dev/null
+++ b/drivers/media/video/stk-webcam.c
@@ -0,0 +1,1380 @@
+/*
+ * stk-webcam.c : Driver for Syntek 1125 USB webcam controller
+ *
+ * Copyright (C) 2006 Nicolas VIVIEN
+ * Copyright 2007-2008 Jaime Velasco Juan <jsagarribay@gmail.com>
+ *
+ * Some parts are inspired from cafe_ccic.c
+ * Copyright 2006-2007 Jonathan Corbet
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+
+#include <linux/usb.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+
+#include "stk-webcam.h"
+
+
+static bool hflip = 1;
+module_param(hflip, bool, 0444);
+MODULE_PARM_DESC(hflip, "Horizontal image flip (mirror). Defaults to 1");
+
+static bool vflip = 1;
+module_param(vflip, bool, 0444);
+MODULE_PARM_DESC(vflip, "Vertical image flip. Defaults to 1");
+
+static int debug;
+module_param(debug, int, 0444);
+MODULE_PARM_DESC(debug, "Debug v4l ioctls. Defaults to 0");
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jaime Velasco Juan <jsagarribay@gmail.com> and Nicolas VIVIEN");
+MODULE_DESCRIPTION("Syntek DC1125 webcam driver");
+
+
+/* bool for webcam LED management */
+int first_init = 1;
+
+/* Some cameras have audio interfaces, we aren't interested in those */
+static struct usb_device_id stkwebcam_table[] = {
+ { USB_DEVICE_AND_INTERFACE_INFO(0x174f, 0xa311, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x05e1, 0x0501, 0xff, 0xff, 0xff) },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, stkwebcam_table);
+
+/*
+ * Basic stuff
+ */
+int stk_camera_write_reg(struct stk_camera *dev, u16 index, u8 value)
+{
+ struct usb_device *udev = dev->udev;
+ int ret;
+
+ ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x01,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value,
+ index,
+ NULL,
+ 0,
+ 500);
+ if (ret < 0)
+ return ret;
+ else
+ return 0;
+}
+
+int stk_camera_read_reg(struct stk_camera *dev, u16 index, int *value)
+{
+ struct usb_device *udev = dev->udev;
+ int ret;
+
+ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ 0x00,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x00,
+ index,
+ (u8 *) value,
+ sizeof(u8),
+ 500);
+ if (ret < 0)
+ return ret;
+ else
+ return 0;
+}
+
+static int stk_start_stream(struct stk_camera *dev)
+{
+ int value;
+ int i, ret;
+ int value_116, value_117;
+
+ if (!is_present(dev))
+ return -ENODEV;
+ if (!is_memallocd(dev) || !is_initialised(dev)) {
+ STK_ERROR("FIXME: Buffers are not allocated\n");
+ return -EFAULT;
+ }
+ ret = usb_set_interface(dev->udev, 0, 5);
+
+ if (ret < 0)
+ STK_ERROR("usb_set_interface failed !\n");
+ if (stk_sensor_wakeup(dev))
+ STK_ERROR("error awaking the sensor\n");
+
+ stk_camera_read_reg(dev, 0x0116, &value_116);
+ stk_camera_read_reg(dev, 0x0117, &value_117);
+
+ stk_camera_write_reg(dev, 0x0116, 0x0000);
+ stk_camera_write_reg(dev, 0x0117, 0x0000);
+
+ stk_camera_read_reg(dev, 0x0100, &value);
+ stk_camera_write_reg(dev, 0x0100, value | 0x80);
+
+ stk_camera_write_reg(dev, 0x0116, value_116);
+ stk_camera_write_reg(dev, 0x0117, value_117);
+ for (i = 0; i < MAX_ISO_BUFS; i++) {
+ if (dev->isobufs[i].urb) {
+ ret = usb_submit_urb(dev->isobufs[i].urb, GFP_KERNEL);
+ atomic_inc(&dev->urbs_used);
+ if (ret)
+ return ret;
+ }
+ }
+ set_streaming(dev);
+ return 0;
+}
+
+static int stk_stop_stream(struct stk_camera *dev)
+{
+ int value;
+ int i;
+ if (is_present(dev)) {
+ stk_camera_read_reg(dev, 0x0100, &value);
+ stk_camera_write_reg(dev, 0x0100, value & ~0x80);
+ if (dev->isobufs != NULL) {
+ for (i = 0; i < MAX_ISO_BUFS; i++) {
+ if (dev->isobufs[i].urb)
+ usb_kill_urb(dev->isobufs[i].urb);
+ }
+ }
+ unset_streaming(dev);
+
+ if (usb_set_interface(dev->udev, 0, 0))
+ STK_ERROR("usb_set_interface failed !\n");
+ if (stk_sensor_sleep(dev))
+ STK_ERROR("error suspending the sensor\n");
+ }
+ return 0;
+}
+
+/*
+ * This seems to be the shortest init sequence we
+ * must do in order to find the sensor
+ * Bit 5 of reg. 0x0000 here is important, when reset to 0 the sensor
+ * is also reset. Maybe powers down it?
+ * Rest of values don't make a difference
+ */
+
+static struct regval stk1125_initvals[] = {
+ /*TODO: What means this sequence? */
+ {0x0000, 0x24},
+ {0x0100, 0x21},
+ {0x0002, 0x68},
+ {0x0003, 0x80},
+ {0x0005, 0x00},
+ {0x0007, 0x03},
+ {0x000d, 0x00},
+ {0x000f, 0x02},
+ {0x0300, 0x12},
+ {0x0350, 0x41},
+ {0x0351, 0x00},
+ {0x0352, 0x00},
+ {0x0353, 0x00},
+ {0x0018, 0x10},
+ {0x0019, 0x00},
+ {0x001b, 0x0e},
+ {0x001c, 0x46},
+ {0x0300, 0x80},
+ {0x001a, 0x04},
+ {0x0110, 0x00},
+ {0x0111, 0x00},
+ {0x0112, 0x00},
+ {0x0113, 0x00},
+
+ {0xffff, 0xff},
+};
+
+
+static int stk_initialise(struct stk_camera *dev)
+{
+ struct regval *rv;
+ int ret;
+ if (!is_present(dev))
+ return -ENODEV;
+ if (is_initialised(dev))
+ return 0;
+ rv = stk1125_initvals;
+ while (rv->reg != 0xffff) {
+ ret = stk_camera_write_reg(dev, rv->reg, rv->val);
+ if (ret)
+ return ret;
+ rv++;
+ }
+ if (stk_sensor_init(dev) == 0) {
+ set_initialised(dev);
+ return 0;
+ } else
+ return -1;
+}
+
+/* *********************************************** */
+/*
+ * This function is called as an URB transfert is complete (Isochronous pipe).
+ * So, the traitement is done in interrupt time, so it has be fast, not crash,
+ * and not stall. Neat.
+ */
+static void stk_isoc_handler(struct urb *urb)
+{
+ int i;
+ int ret;
+ int framelen;
+ unsigned long flags;
+
+ unsigned char *fill = NULL;
+ unsigned char *iso_buf = NULL;
+
+ struct stk_camera *dev;
+ struct stk_sio_buffer *fb;
+
+ dev = (struct stk_camera *) urb->context;
+
+ if (dev == NULL) {
+ STK_ERROR("isoc_handler called with NULL device !\n");
+ return;
+ }
+
+ if (urb->status == -ENOENT || urb->status == -ECONNRESET
+ || urb->status == -ESHUTDOWN) {
+ atomic_dec(&dev->urbs_used);
+ return;
+ }
+
+ spin_lock_irqsave(&dev->spinlock, flags);
+
+ if (urb->status != -EINPROGRESS && urb->status != 0) {
+ STK_ERROR("isoc_handler: urb->status == %d\n", urb->status);
+ goto resubmit;
+ }
+
+ if (list_empty(&dev->sio_avail)) {
+ /*FIXME Stop streaming after a while */
+ (void) (printk_ratelimit() &&
+ STK_ERROR("isoc_handler without available buffer!\n"));
+ goto resubmit;
+ }
+ fb = list_first_entry(&dev->sio_avail,
+ struct stk_sio_buffer, list);
+ fill = fb->buffer + fb->v4lbuf.bytesused;
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ if (urb->iso_frame_desc[i].status != 0) {
+ if (urb->iso_frame_desc[i].status != -EXDEV)
+ STK_ERROR("Frame %d has error %d\n", i,
+ urb->iso_frame_desc[i].status);
+ continue;
+ }
+ framelen = urb->iso_frame_desc[i].actual_length;
+ iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+
+ if (framelen <= 4)
+ continue; /* no data */
+
+ /*
+ * we found something informational from there
+ * the isoc frames have to type of headers
+ * type1: 00 xx 00 00 or 20 xx 00 00
+ * type2: 80 xx 00 00 00 00 00 00 or a0 xx 00 00 00 00 00 00
+ * xx is a sequencer which has never been seen over 0x3f
+ * imho data written down looks like bayer, i see similarities
+ * after every 640 bytes
+ */
+ if (*iso_buf & 0x80) {
+ framelen -= 8;
+ iso_buf += 8;
+ /* This marks a new frame */
+ if (fb->v4lbuf.bytesused != 0
+ && fb->v4lbuf.bytesused != dev->frame_size) {
+ (void) (printk_ratelimit() &&
+ STK_ERROR("frame %d, "
+ "bytesused=%d, skipping\n",
+ i, fb->v4lbuf.bytesused));
+ fb->v4lbuf.bytesused = 0;
+ fill = fb->buffer;
+ } else if (fb->v4lbuf.bytesused == dev->frame_size) {
+ if (list_is_singular(&dev->sio_avail)) {
+ /* Always reuse the last buffer */
+ fb->v4lbuf.bytesused = 0;
+ fill = fb->buffer;
+ } else {
+ list_move_tail(dev->sio_avail.next,
+ &dev->sio_full);
+ wake_up(&dev->wait_frame);
+ fb = list_first_entry(&dev->sio_avail,
+ struct stk_sio_buffer, list);
+ fb->v4lbuf.bytesused = 0;
+ fill = fb->buffer;
+ }
+ }
+ } else {
+ framelen -= 4;
+ iso_buf += 4;
+ }
+
+ /* Our buffer is full !!! */
+ if (framelen + fb->v4lbuf.bytesused > dev->frame_size) {
+ (void) (printk_ratelimit() &&
+ STK_ERROR("Frame buffer overflow, lost sync\n"));
+ /*FIXME Do something here? */
+ continue;
+ }
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ memcpy(fill, iso_buf, framelen);
+ spin_lock_irqsave(&dev->spinlock, flags);
+ fill += framelen;
+
+ /* New size of our buffer */
+ fb->v4lbuf.bytesused += framelen;
+ }
+
+resubmit:
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ urb->dev = dev->udev;
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret != 0) {
+ STK_ERROR("Error (%d) re-submitting urb in stk_isoc_handler.\n",
+ ret);
+ }
+}
+
+/* -------------------------------------------- */
+
+static int stk_prepare_iso(struct stk_camera *dev)
+{
+ void *kbuf;
+ int i, j;
+ struct urb *urb;
+ struct usb_device *udev;
+
+ if (dev == NULL)
+ return -ENXIO;
+ udev = dev->udev;
+
+ if (dev->isobufs)
+ STK_ERROR("isobufs already allocated. Bad\n");
+ else
+ dev->isobufs = kcalloc(MAX_ISO_BUFS, sizeof(*dev->isobufs),
+ GFP_KERNEL);
+ if (dev->isobufs == NULL) {
+ STK_ERROR("Unable to allocate iso buffers\n");
+ return -ENOMEM;
+ }
+ for (i = 0; i < MAX_ISO_BUFS; i++) {
+ if (dev->isobufs[i].data == NULL) {
+ kbuf = kzalloc(ISO_BUFFER_SIZE, GFP_KERNEL);
+ if (kbuf == NULL) {
+ STK_ERROR("Failed to allocate iso buffer %d\n",
+ i);
+ goto isobufs_out;
+ }
+ dev->isobufs[i].data = kbuf;
+ } else
+ STK_ERROR("isobuf data already allocated\n");
+ if (dev->isobufs[i].urb == NULL) {
+ urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL);
+ if (urb == NULL) {
+ STK_ERROR("Failed to allocate URB %d\n", i);
+ goto isobufs_out;
+ }
+ dev->isobufs[i].urb = urb;
+ } else {
+ STK_ERROR("Killing URB\n");
+ usb_kill_urb(dev->isobufs[i].urb);
+ urb = dev->isobufs[i].urb;
+ }
+ urb->interval = 1;
+ urb->dev = udev;
+ urb->pipe = usb_rcvisocpipe(udev, dev->isoc_ep);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->transfer_buffer = dev->isobufs[i].data;
+ urb->transfer_buffer_length = ISO_BUFFER_SIZE;
+ urb->complete = stk_isoc_handler;
+ urb->context = dev;
+ urb->start_frame = 0;
+ urb->number_of_packets = ISO_FRAMES_PER_DESC;
+
+ for (j = 0; j < ISO_FRAMES_PER_DESC; j++) {
+ urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE;
+ urb->iso_frame_desc[j].length = ISO_MAX_FRAME_SIZE;
+ }
+ }
+ set_memallocd(dev);
+ return 0;
+
+isobufs_out:
+ for (i = 0; i < MAX_ISO_BUFS && dev->isobufs[i].data; i++)
+ kfree(dev->isobufs[i].data);
+ for (i = 0; i < MAX_ISO_BUFS && dev->isobufs[i].urb; i++)
+ usb_free_urb(dev->isobufs[i].urb);
+ kfree(dev->isobufs);
+ dev->isobufs = NULL;
+ return -ENOMEM;
+}
+
+static void stk_clean_iso(struct stk_camera *dev)
+{
+ int i;
+
+ if (dev == NULL || dev->isobufs == NULL)
+ return;
+
+ for (i = 0; i < MAX_ISO_BUFS; i++) {
+ struct urb *urb;
+
+ urb = dev->isobufs[i].urb;
+ if (urb) {
+ if (atomic_read(&dev->urbs_used) && is_present(dev))
+ usb_kill_urb(urb);
+ usb_free_urb(urb);
+ }
+ kfree(dev->isobufs[i].data);
+ }
+ kfree(dev->isobufs);
+ dev->isobufs = NULL;
+ unset_memallocd(dev);
+}
+
+static int stk_setup_siobuf(struct stk_camera *dev, int index)
+{
+ struct stk_sio_buffer *buf = dev->sio_bufs + index;
+ INIT_LIST_HEAD(&buf->list);
+ buf->v4lbuf.length = PAGE_ALIGN(dev->frame_size);
+ buf->buffer = vmalloc_user(buf->v4lbuf.length);
+ if (buf->buffer == NULL)
+ return -ENOMEM;
+ buf->mapcount = 0;
+ buf->dev = dev;
+ buf->v4lbuf.index = index;
+ buf->v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf->v4lbuf.field = V4L2_FIELD_NONE;
+ buf->v4lbuf.memory = V4L2_MEMORY_MMAP;
+ buf->v4lbuf.m.offset = 2*index*buf->v4lbuf.length;
+ return 0;
+}
+
+static int stk_free_sio_buffers(struct stk_camera *dev)
+{
+ int i;
+ int nbufs;
+ unsigned long flags;
+ if (dev->n_sbufs == 0 || dev->sio_bufs == NULL)
+ return 0;
+ /*
+ * If any buffers are mapped, we cannot free them at all.
+ */
+ for (i = 0; i < dev->n_sbufs; i++) {
+ if (dev->sio_bufs[i].mapcount > 0)
+ return -EBUSY;
+ }
+ /*
+ * OK, let's do it.
+ */
+ spin_lock_irqsave(&dev->spinlock, flags);
+ INIT_LIST_HEAD(&dev->sio_avail);
+ INIT_LIST_HEAD(&dev->sio_full);
+ nbufs = dev->n_sbufs;
+ dev->n_sbufs = 0;
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ for (i = 0; i < nbufs; i++) {
+ if (dev->sio_bufs[i].buffer != NULL)
+ vfree(dev->sio_bufs[i].buffer);
+ }
+ kfree(dev->sio_bufs);
+ dev->sio_bufs = NULL;
+ return 0;
+}
+
+static int stk_prepare_sio_buffers(struct stk_camera *dev, unsigned n_sbufs)
+{
+ int i;
+ if (dev->sio_bufs != NULL)
+ STK_ERROR("sio_bufs already allocated\n");
+ else {
+ dev->sio_bufs = kzalloc(n_sbufs * sizeof(struct stk_sio_buffer),
+ GFP_KERNEL);
+ if (dev->sio_bufs == NULL)
+ return -ENOMEM;
+ for (i = 0; i < n_sbufs; i++) {
+ if (stk_setup_siobuf(dev, i))
+ return (dev->n_sbufs > 1 ? 0 : -ENOMEM);
+ dev->n_sbufs = i+1;
+ }
+ }
+ return 0;
+}
+
+static int stk_allocate_buffers(struct stk_camera *dev, unsigned n_sbufs)
+{
+ int err;
+ err = stk_prepare_iso(dev);
+ if (err) {
+ stk_clean_iso(dev);
+ return err;
+ }
+ err = stk_prepare_sio_buffers(dev, n_sbufs);
+ if (err) {
+ stk_free_sio_buffers(dev);
+ return err;
+ }
+ return 0;
+}
+
+static void stk_free_buffers(struct stk_camera *dev)
+{
+ stk_clean_iso(dev);
+ stk_free_sio_buffers(dev);
+}
+/* -------------------------------------------- */
+
+/* v4l file operations */
+
+static int v4l_stk_open(struct file *fp)
+{
+ struct stk_camera *dev;
+ struct video_device *vdev;
+
+ vdev = video_devdata(fp);
+ dev = vdev_to_camera(vdev);
+
+ if (dev == NULL || !is_present(dev))
+ return -ENXIO;
+
+ if (!first_init)
+ stk_camera_write_reg(dev, 0x0, 0x24);
+ else
+ first_init = 0;
+
+ fp->private_data = dev;
+ usb_autopm_get_interface(dev->interface);
+
+ return 0;
+}
+
+static int v4l_stk_release(struct file *fp)
+{
+ struct stk_camera *dev = fp->private_data;
+
+ if (dev->owner == fp) {
+ stk_stop_stream(dev);
+ stk_free_buffers(dev);
+ stk_camera_write_reg(dev, 0x0, 0x49); /* turn off the LED */
+ unset_initialised(dev);
+ dev->owner = NULL;
+ }
+
+ if (is_present(dev))
+ usb_autopm_put_interface(dev->interface);
+
+ return 0;
+}
+
+static ssize_t v4l_stk_read(struct file *fp, char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ int i;
+ int ret;
+ unsigned long flags;
+ struct stk_sio_buffer *sbuf;
+ struct stk_camera *dev = fp->private_data;
+
+ if (!is_present(dev))
+ return -EIO;
+ if (dev->owner && dev->owner != fp)
+ return -EBUSY;
+ dev->owner = fp;
+ if (!is_streaming(dev)) {
+ if (stk_initialise(dev)
+ || stk_allocate_buffers(dev, 3)
+ || stk_start_stream(dev))
+ return -ENOMEM;
+ spin_lock_irqsave(&dev->spinlock, flags);
+ for (i = 0; i < dev->n_sbufs; i++) {
+ list_add_tail(&dev->sio_bufs[i].list, &dev->sio_avail);
+ dev->sio_bufs[i].v4lbuf.flags = V4L2_BUF_FLAG_QUEUED;
+ }
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ }
+ if (*f_pos == 0) {
+ if (fp->f_flags & O_NONBLOCK && list_empty(&dev->sio_full))
+ return -EWOULDBLOCK;
+ ret = wait_event_interruptible(dev->wait_frame,
+ !list_empty(&dev->sio_full) || !is_present(dev));
+ if (ret)
+ return ret;
+ if (!is_present(dev))
+ return -EIO;
+ }
+ if (count + *f_pos > dev->frame_size)
+ count = dev->frame_size - *f_pos;
+ spin_lock_irqsave(&dev->spinlock, flags);
+ if (list_empty(&dev->sio_full)) {
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ STK_ERROR("BUG: No siobufs ready\n");
+ return 0;
+ }
+ sbuf = list_first_entry(&dev->sio_full, struct stk_sio_buffer, list);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ if (copy_to_user(buf, sbuf->buffer + *f_pos, count))
+ return -EFAULT;
+
+ *f_pos += count;
+
+ if (*f_pos >= dev->frame_size) {
+ *f_pos = 0;
+ spin_lock_irqsave(&dev->spinlock, flags);
+ list_move_tail(&sbuf->list, &dev->sio_avail);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ }
+ return count;
+}
+
+static unsigned int v4l_stk_poll(struct file *fp, poll_table *wait)
+{
+ struct stk_camera *dev = fp->private_data;
+
+ poll_wait(fp, &dev->wait_frame, wait);
+
+ if (!is_present(dev))
+ return POLLERR;
+
+ if (!list_empty(&dev->sio_full))
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+
+static void stk_v4l_vm_open(struct vm_area_struct *vma)
+{
+ struct stk_sio_buffer *sbuf = vma->vm_private_data;
+ sbuf->mapcount++;
+}
+static void stk_v4l_vm_close(struct vm_area_struct *vma)
+{
+ struct stk_sio_buffer *sbuf = vma->vm_private_data;
+ sbuf->mapcount--;
+ if (sbuf->mapcount == 0)
+ sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_MAPPED;
+}
+static const struct vm_operations_struct stk_v4l_vm_ops = {
+ .open = stk_v4l_vm_open,
+ .close = stk_v4l_vm_close
+};
+
+static int v4l_stk_mmap(struct file *fp, struct vm_area_struct *vma)
+{
+ unsigned int i;
+ int ret;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ struct stk_camera *dev = fp->private_data;
+ struct stk_sio_buffer *sbuf = NULL;
+
+ if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED))
+ return -EINVAL;
+
+ for (i = 0; i < dev->n_sbufs; i++) {
+ if (dev->sio_bufs[i].v4lbuf.m.offset == offset) {
+ sbuf = dev->sio_bufs + i;
+ break;
+ }
+ }
+ if (sbuf == NULL)
+ return -EINVAL;
+ ret = remap_vmalloc_range(vma, sbuf->buffer, 0);
+ if (ret)
+ return ret;
+ vma->vm_flags |= VM_DONTEXPAND;
+ vma->vm_private_data = sbuf;
+ vma->vm_ops = &stk_v4l_vm_ops;
+ sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_MAPPED;
+ stk_v4l_vm_open(vma);
+ return 0;
+}
+
+/* v4l ioctl handlers */
+
+static int stk_vidioc_querycap(struct file *filp,
+ void *priv, struct v4l2_capability *cap)
+{
+ strcpy(cap->driver, "stk");
+ strcpy(cap->card, "stk");
+ cap->version = DRIVER_VERSION_NUM;
+
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
+ | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+ return 0;
+}
+
+static int stk_vidioc_enum_input(struct file *filp,
+ void *priv, struct v4l2_input *input)
+{
+ if (input->index != 0)
+ return -EINVAL;
+
+ strcpy(input->name, "Syntek USB Camera");
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+ return 0;
+}
+
+
+static int stk_vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int stk_vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ if (i != 0)
+ return -EINVAL;
+ else
+ return 0;
+}
+
+/* from vivi.c */
+static int stk_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id *a)
+{
+ return 0;
+}
+
+/* List of all V4Lv2 controls supported by the driver */
+static struct v4l2_queryctrl stk_controls[] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 0xffff,
+ .step = 0x0100,
+ .default_value = 0x6000,
+ },
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Horizontal Flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Vertical Flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ },
+};
+
+static int stk_vidioc_queryctrl(struct file *filp,
+ void *priv, struct v4l2_queryctrl *c)
+{
+ int i;
+ int nbr;
+ nbr = ARRAY_SIZE(stk_controls);
+
+ for (i = 0; i < nbr; i++) {
+ if (stk_controls[i].id == c->id) {
+ memcpy(c, &stk_controls[i],
+ sizeof(struct v4l2_queryctrl));
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int stk_vidioc_g_ctrl(struct file *filp,
+ void *priv, struct v4l2_control *c)
+{
+ struct stk_camera *dev = priv;
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ c->value = dev->vsettings.brightness;
+ break;
+ case V4L2_CID_HFLIP:
+ c->value = dev->vsettings.hflip;
+ break;
+ case V4L2_CID_VFLIP:
+ c->value = dev->vsettings.vflip;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int stk_vidioc_s_ctrl(struct file *filp,
+ void *priv, struct v4l2_control *c)
+{
+ struct stk_camera *dev = priv;
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ dev->vsettings.brightness = c->value;
+ return stk_sensor_set_brightness(dev, c->value >> 8);
+ case V4L2_CID_HFLIP:
+ dev->vsettings.hflip = c->value;
+ return 0;
+ case V4L2_CID_VFLIP:
+ dev->vsettings.vflip = c->value;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+static int stk_vidioc_enum_fmt_vid_cap(struct file *filp,
+ void *priv, struct v4l2_fmtdesc *fmtd)
+{
+ switch (fmtd->index) {
+ case 0:
+ fmtd->pixelformat = V4L2_PIX_FMT_RGB565;
+ strcpy(fmtd->description, "r5g6b5");
+ break;
+ case 1:
+ fmtd->pixelformat = V4L2_PIX_FMT_RGB565X;
+ strcpy(fmtd->description, "r5g6b5BE");
+ break;
+ case 2:
+ fmtd->pixelformat = V4L2_PIX_FMT_UYVY;
+ strcpy(fmtd->description, "yuv4:2:2");
+ break;
+ case 3:
+ fmtd->pixelformat = V4L2_PIX_FMT_SBGGR8;
+ strcpy(fmtd->description, "Raw bayer");
+ break;
+ case 4:
+ fmtd->pixelformat = V4L2_PIX_FMT_YUYV;
+ strcpy(fmtd->description, "yuv4:2:2");
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static struct stk_size {
+ unsigned w;
+ unsigned h;
+ enum stk_mode m;
+} stk_sizes[] = {
+ { .w = 1280, .h = 1024, .m = MODE_SXGA, },
+ { .w = 640, .h = 480, .m = MODE_VGA, },
+ { .w = 352, .h = 288, .m = MODE_CIF, },
+ { .w = 320, .h = 240, .m = MODE_QVGA, },
+ { .w = 176, .h = 144, .m = MODE_QCIF, },
+};
+
+static int stk_vidioc_g_fmt_vid_cap(struct file *filp,
+ void *priv, struct v4l2_format *f)
+{
+ struct v4l2_pix_format *pix_format = &f->fmt.pix;
+ struct stk_camera *dev = priv;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(stk_sizes) &&
+ stk_sizes[i].m != dev->vsettings.mode; i++)
+ ;
+ if (i == ARRAY_SIZE(stk_sizes)) {
+ STK_ERROR("ERROR: mode invalid\n");
+ return -EINVAL;
+ }
+ pix_format->width = stk_sizes[i].w;
+ pix_format->height = stk_sizes[i].h;
+ pix_format->field = V4L2_FIELD_NONE;
+ pix_format->colorspace = V4L2_COLORSPACE_SRGB;
+ pix_format->pixelformat = dev->vsettings.palette;
+ if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8)
+ pix_format->bytesperline = pix_format->width;
+ else
+ pix_format->bytesperline = 2 * pix_format->width;
+ pix_format->sizeimage = pix_format->bytesperline
+ * pix_format->height;
+ return 0;
+}
+
+static int stk_vidioc_try_fmt_vid_cap(struct file *filp,
+ void *priv, struct v4l2_format *fmtd)
+{
+ int i;
+ switch (fmtd->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB565X:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_SBGGR8:
+ break;
+ default:
+ return -EINVAL;
+ }
+ for (i = 1; i < ARRAY_SIZE(stk_sizes); i++) {
+ if (fmtd->fmt.pix.width > stk_sizes[i].w)
+ break;
+ }
+ if (i == ARRAY_SIZE(stk_sizes)
+ || (abs(fmtd->fmt.pix.width - stk_sizes[i-1].w)
+ < abs(fmtd->fmt.pix.width - stk_sizes[i].w))) {
+ fmtd->fmt.pix.height = stk_sizes[i-1].h;
+ fmtd->fmt.pix.width = stk_sizes[i-1].w;
+ fmtd->fmt.pix.priv = i - 1;
+ } else {
+ fmtd->fmt.pix.height = stk_sizes[i].h;
+ fmtd->fmt.pix.width = stk_sizes[i].w;
+ fmtd->fmt.pix.priv = i;
+ }
+
+ fmtd->fmt.pix.field = V4L2_FIELD_NONE;
+ fmtd->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
+ if (fmtd->fmt.pix.pixelformat == V4L2_PIX_FMT_SBGGR8)
+ fmtd->fmt.pix.bytesperline = fmtd->fmt.pix.width;
+ else
+ fmtd->fmt.pix.bytesperline = 2 * fmtd->fmt.pix.width;
+ fmtd->fmt.pix.sizeimage = fmtd->fmt.pix.bytesperline
+ * fmtd->fmt.pix.height;
+ return 0;
+}
+
+static int stk_setup_format(struct stk_camera *dev)
+{
+ int i = 0;
+ int depth;
+ if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8)
+ depth = 1;
+ else
+ depth = 2;
+ while (i < ARRAY_SIZE(stk_sizes) &&
+ stk_sizes[i].m != dev->vsettings.mode)
+ i++;
+ if (i == ARRAY_SIZE(stk_sizes)) {
+ STK_ERROR("Something is broken in %s\n", __func__);
+ return -EFAULT;
+ }
+ /* This registers controls some timings, not sure of what. */
+ stk_camera_write_reg(dev, 0x001b, 0x0e);
+ if (dev->vsettings.mode == MODE_SXGA)
+ stk_camera_write_reg(dev, 0x001c, 0x0e);
+ else
+ stk_camera_write_reg(dev, 0x001c, 0x46);
+ /*
+ * Registers 0x0115 0x0114 are the size of each line (bytes),
+ * regs 0x0117 0x0116 are the heigth of the image.
+ */
+ stk_camera_write_reg(dev, 0x0115,
+ ((stk_sizes[i].w * depth) >> 8) & 0xff);
+ stk_camera_write_reg(dev, 0x0114,
+ (stk_sizes[i].w * depth) & 0xff);
+ stk_camera_write_reg(dev, 0x0117,
+ (stk_sizes[i].h >> 8) & 0xff);
+ stk_camera_write_reg(dev, 0x0116,
+ stk_sizes[i].h & 0xff);
+ return stk_sensor_configure(dev);
+}
+
+static int stk_vidioc_s_fmt_vid_cap(struct file *filp,
+ void *priv, struct v4l2_format *fmtd)
+{
+ int ret;
+ struct stk_camera *dev = priv;
+
+ if (dev == NULL)
+ return -ENODEV;
+ if (!is_present(dev))
+ return -ENODEV;
+ if (is_streaming(dev))
+ return -EBUSY;
+ if (dev->owner && dev->owner != filp)
+ return -EBUSY;
+ ret = stk_vidioc_try_fmt_vid_cap(filp, priv, fmtd);
+ if (ret)
+ return ret;
+ dev->owner = filp;
+
+ dev->vsettings.palette = fmtd->fmt.pix.pixelformat;
+ stk_free_buffers(dev);
+ dev->frame_size = fmtd->fmt.pix.sizeimage;
+ dev->vsettings.mode = stk_sizes[fmtd->fmt.pix.priv].m;
+
+ stk_initialise(dev);
+ return stk_setup_format(dev);
+}
+
+static int stk_vidioc_reqbufs(struct file *filp,
+ void *priv, struct v4l2_requestbuffers *rb)
+{
+ struct stk_camera *dev = priv;
+
+ if (dev == NULL)
+ return -ENODEV;
+ if (rb->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+ if (is_streaming(dev)
+ || (dev->owner && dev->owner != filp))
+ return -EBUSY;
+ dev->owner = filp;
+
+ /*FIXME If they ask for zero, we must stop streaming and free */
+ if (rb->count < 3)
+ rb->count = 3;
+ /* Arbitrary limit */
+ else if (rb->count > 5)
+ rb->count = 5;
+
+ stk_allocate_buffers(dev, rb->count);
+ rb->count = dev->n_sbufs;
+ return 0;
+}
+
+static int stk_vidioc_querybuf(struct file *filp,
+ void *priv, struct v4l2_buffer *buf)
+{
+ struct stk_camera *dev = priv;
+ struct stk_sio_buffer *sbuf;
+
+ if (buf->index >= dev->n_sbufs)
+ return -EINVAL;
+ sbuf = dev->sio_bufs + buf->index;
+ *buf = sbuf->v4lbuf;
+ return 0;
+}
+
+static int stk_vidioc_qbuf(struct file *filp,
+ void *priv, struct v4l2_buffer *buf)
+{
+ struct stk_camera *dev = priv;
+ struct stk_sio_buffer *sbuf;
+ unsigned long flags;
+
+ if (buf->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+ if (buf->index >= dev->n_sbufs)
+ return -EINVAL;
+ sbuf = dev->sio_bufs + buf->index;
+ if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_QUEUED)
+ return 0;
+ sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_QUEUED;
+ sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_DONE;
+ spin_lock_irqsave(&dev->spinlock, flags);
+ list_add_tail(&sbuf->list, &dev->sio_avail);
+ *buf = sbuf->v4lbuf;
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ return 0;
+}
+
+static int stk_vidioc_dqbuf(struct file *filp,
+ void *priv, struct v4l2_buffer *buf)
+{
+ struct stk_camera *dev = priv;
+ struct stk_sio_buffer *sbuf;
+ unsigned long flags;
+ int ret;
+
+ if (!is_streaming(dev))
+ return -EINVAL;
+
+ if (filp->f_flags & O_NONBLOCK && list_empty(&dev->sio_full))
+ return -EWOULDBLOCK;
+ ret = wait_event_interruptible(dev->wait_frame,
+ !list_empty(&dev->sio_full) || !is_present(dev));
+ if (ret)
+ return ret;
+ if (!is_present(dev))
+ return -EIO;
+
+ spin_lock_irqsave(&dev->spinlock, flags);
+ sbuf = list_first_entry(&dev->sio_full, struct stk_sio_buffer, list);
+ list_del_init(&sbuf->list);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED;
+ sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE;
+ sbuf->v4lbuf.sequence = ++dev->sequence;
+ do_gettimeofday(&sbuf->v4lbuf.timestamp);
+
+ *buf = sbuf->v4lbuf;
+ return 0;
+}
+
+static int stk_vidioc_streamon(struct file *filp,
+ void *priv, enum v4l2_buf_type type)
+{
+ struct stk_camera *dev = priv;
+ if (is_streaming(dev))
+ return 0;
+ if (dev->sio_bufs == NULL)
+ return -EINVAL;
+ dev->sequence = 0;
+ return stk_start_stream(dev);
+}
+
+static int stk_vidioc_streamoff(struct file *filp,
+ void *priv, enum v4l2_buf_type type)
+{
+ struct stk_camera *dev = priv;
+ unsigned long flags;
+ int i;
+ stk_stop_stream(dev);
+ spin_lock_irqsave(&dev->spinlock, flags);
+ INIT_LIST_HEAD(&dev->sio_avail);
+ INIT_LIST_HEAD(&dev->sio_full);
+ for (i = 0; i < dev->n_sbufs; i++) {
+ INIT_LIST_HEAD(&dev->sio_bufs[i].list);
+ dev->sio_bufs[i].v4lbuf.flags = 0;
+ }
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ return 0;
+}
+
+
+static int stk_vidioc_g_parm(struct file *filp,
+ void *priv, struct v4l2_streamparm *sp)
+{
+ /*FIXME This is not correct */
+ sp->parm.capture.timeperframe.numerator = 1;
+ sp->parm.capture.timeperframe.denominator = 30;
+ sp->parm.capture.readbuffers = 2;
+ return 0;
+}
+
+static int stk_vidioc_enum_framesizes(struct file *filp,
+ void *priv, struct v4l2_frmsizeenum *frms)
+{
+ if (frms->index >= ARRAY_SIZE(stk_sizes))
+ return -EINVAL;
+ switch (frms->pixel_format) {
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB565X:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_SBGGR8:
+ frms->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ frms->discrete.width = stk_sizes[frms->index].w;
+ frms->discrete.height = stk_sizes[frms->index].h;
+ return 0;
+ default: return -EINVAL;
+ }
+}
+
+static struct v4l2_file_operations v4l_stk_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l_stk_open,
+ .release = v4l_stk_release,
+ .read = v4l_stk_read,
+ .poll = v4l_stk_poll,
+ .mmap = v4l_stk_mmap,
+ .ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops v4l_stk_ioctl_ops = {
+ .vidioc_querycap = stk_vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = stk_vidioc_enum_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = stk_vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = stk_vidioc_s_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = stk_vidioc_g_fmt_vid_cap,
+ .vidioc_enum_input = stk_vidioc_enum_input,
+ .vidioc_s_input = stk_vidioc_s_input,
+ .vidioc_g_input = stk_vidioc_g_input,
+ .vidioc_s_std = stk_vidioc_s_std,
+ .vidioc_reqbufs = stk_vidioc_reqbufs,
+ .vidioc_querybuf = stk_vidioc_querybuf,
+ .vidioc_qbuf = stk_vidioc_qbuf,
+ .vidioc_dqbuf = stk_vidioc_dqbuf,
+ .vidioc_streamon = stk_vidioc_streamon,
+ .vidioc_streamoff = stk_vidioc_streamoff,
+ .vidioc_queryctrl = stk_vidioc_queryctrl,
+ .vidioc_g_ctrl = stk_vidioc_g_ctrl,
+ .vidioc_s_ctrl = stk_vidioc_s_ctrl,
+ .vidioc_g_parm = stk_vidioc_g_parm,
+ .vidioc_enum_framesizes = stk_vidioc_enum_framesizes,
+};
+
+static void stk_v4l_dev_release(struct video_device *vd)
+{
+ struct stk_camera *dev = vdev_to_camera(vd);
+
+ if (dev->sio_bufs != NULL || dev->isobufs != NULL)
+ STK_ERROR("We are leaking memory\n");
+ usb_put_intf(dev->interface);
+ kfree(dev);
+}
+
+static struct video_device stk_v4l_data = {
+ .name = "stkwebcam",
+ .tvnorms = V4L2_STD_UNKNOWN,
+ .current_norm = V4L2_STD_UNKNOWN,
+ .fops = &v4l_stk_fops,
+ .ioctl_ops = &v4l_stk_ioctl_ops,
+ .release = stk_v4l_dev_release,
+};
+
+
+static int stk_register_video_device(struct stk_camera *dev)
+{
+ int err;
+
+ dev->vdev = stk_v4l_data;
+ dev->vdev.debug = debug;
+ dev->vdev.parent = &dev->interface->dev;
+ err = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1);
+ if (err)
+ STK_ERROR("v4l registration failed\n");
+ else
+ STK_INFO("Syntek USB2.0 Camera is now controlling device %s\n",
+ video_device_node_name(&dev->vdev));
+ return err;
+}
+
+
+/* USB Stuff */
+
+static int stk_camera_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ int i;
+ int err = 0;
+
+ struct stk_camera *dev = NULL;
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+
+ dev = kzalloc(sizeof(struct stk_camera), GFP_KERNEL);
+ if (dev == NULL) {
+ STK_ERROR("Out of memory !\n");
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&dev->spinlock);
+ init_waitqueue_head(&dev->wait_frame);
+
+ dev->udev = udev;
+ dev->interface = interface;
+ usb_get_intf(interface);
+
+ dev->vsettings.vflip = vflip;
+ dev->vsettings.hflip = hflip;
+ dev->n_sbufs = 0;
+ set_present(dev);
+
+ /* Set up the endpoint information
+ * use only the first isoc-in endpoint
+ * for the current alternate setting */
+ iface_desc = interface->cur_altsetting;
+
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+
+ if (!dev->isoc_ep
+ && usb_endpoint_is_isoc_in(endpoint)) {
+ /* we found an isoc in endpoint */
+ dev->isoc_ep = usb_endpoint_num(endpoint);
+ break;
+ }
+ }
+ if (!dev->isoc_ep) {
+ STK_ERROR("Could not find isoc-in endpoint");
+ err = -ENODEV;
+ goto error;
+ }
+ dev->vsettings.brightness = 0x7fff;
+ dev->vsettings.palette = V4L2_PIX_FMT_RGB565;
+ dev->vsettings.mode = MODE_VGA;
+ dev->frame_size = 640 * 480 * 2;
+
+ INIT_LIST_HEAD(&dev->sio_avail);
+ INIT_LIST_HEAD(&dev->sio_full);
+
+ usb_set_intfdata(interface, dev);
+
+ err = stk_register_video_device(dev);
+ if (err)
+ goto error;
+
+ return 0;
+
+error:
+ kfree(dev);
+ return err;
+}
+
+static void stk_camera_disconnect(struct usb_interface *interface)
+{
+ struct stk_camera *dev = usb_get_intfdata(interface);
+
+ usb_set_intfdata(interface, NULL);
+ unset_present(dev);
+
+ wake_up_interruptible(&dev->wait_frame);
+
+ STK_INFO("Syntek USB2.0 Camera release resources device %s\n",
+ video_device_node_name(&dev->vdev));
+
+ video_unregister_device(&dev->vdev);
+}
+
+#ifdef CONFIG_PM
+static int stk_camera_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct stk_camera *dev = usb_get_intfdata(intf);
+ if (is_streaming(dev)) {
+ stk_stop_stream(dev);
+ /* yes, this is ugly */
+ set_streaming(dev);
+ }
+ return 0;
+}
+
+static int stk_camera_resume(struct usb_interface *intf)
+{
+ struct stk_camera *dev = usb_get_intfdata(intf);
+ if (!is_initialised(dev))
+ return 0;
+ unset_initialised(dev);
+ stk_initialise(dev);
+ stk_camera_write_reg(dev, 0x0, 0x49);
+ stk_setup_format(dev);
+ if (is_streaming(dev))
+ stk_start_stream(dev);
+ return 0;
+}
+#endif
+
+static struct usb_driver stk_camera_driver = {
+ .name = "stkwebcam",
+ .probe = stk_camera_probe,
+ .disconnect = stk_camera_disconnect,
+ .id_table = stkwebcam_table,
+#ifdef CONFIG_PM
+ .suspend = stk_camera_suspend,
+ .resume = stk_camera_resume,
+#endif
+};
+
+module_usb_driver(stk_camera_driver);
diff --git a/drivers/media/video/stk-webcam.h b/drivers/media/video/stk-webcam.h
new file mode 100644
index 00000000..9f673663
--- /dev/null
+++ b/drivers/media/video/stk-webcam.h
@@ -0,0 +1,134 @@
+/*
+ * stk-webcam.h : Driver for Syntek 1125 USB webcam controller
+ *
+ * Copyright (C) 2006 Nicolas VIVIEN
+ * Copyright 2007-2008 Jaime Velasco Juan <jsagarribay@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef STKWEBCAM_H
+#define STKWEBCAM_H
+
+#include <linux/usb.h>
+#include <media/v4l2-common.h>
+
+#define DRIVER_VERSION "v0.0.1"
+#define DRIVER_VERSION_NUM 0x000001
+
+#define MAX_ISO_BUFS 3
+#define ISO_FRAMES_PER_DESC 16
+#define ISO_MAX_FRAME_SIZE 3 * 1024
+#define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE)
+
+
+#define PREFIX "stkwebcam: "
+#define STK_INFO(str, args...) printk(KERN_INFO PREFIX str, ##args)
+#define STK_ERROR(str, args...) printk(KERN_ERR PREFIX str, ##args)
+#define STK_WARNING(str, args...) printk(KERN_WARNING PREFIX str, ##args)
+
+struct stk_iso_buf {
+ void *data;
+ int length;
+ int read;
+ struct urb *urb;
+};
+
+/* Streaming IO buffers */
+struct stk_sio_buffer {
+ struct v4l2_buffer v4lbuf;
+ char *buffer;
+ int mapcount;
+ struct stk_camera *dev;
+ struct list_head list;
+};
+
+enum stk_mode {MODE_VGA, MODE_SXGA, MODE_CIF, MODE_QVGA, MODE_QCIF};
+
+struct stk_video {
+ enum stk_mode mode;
+ int brightness;
+ __u32 palette;
+ int hflip;
+ int vflip;
+};
+
+enum stk_status {
+ S_PRESENT = 1,
+ S_INITIALISED = 2,
+ S_MEMALLOCD = 4,
+ S_STREAMING = 8,
+};
+#define is_present(dev) ((dev)->status & S_PRESENT)
+#define is_initialised(dev) ((dev)->status & S_INITIALISED)
+#define is_streaming(dev) ((dev)->status & S_STREAMING)
+#define is_memallocd(dev) ((dev)->status & S_MEMALLOCD)
+#define set_present(dev) ((dev)->status = S_PRESENT)
+#define unset_present(dev) ((dev)->status &= \
+ ~(S_PRESENT|S_INITIALISED|S_STREAMING))
+#define set_initialised(dev) ((dev)->status |= S_INITIALISED)
+#define unset_initialised(dev) ((dev)->status &= ~S_INITIALISED)
+#define set_memallocd(dev) ((dev)->status |= S_MEMALLOCD)
+#define unset_memallocd(dev) ((dev)->status &= ~S_MEMALLOCD)
+#define set_streaming(dev) ((dev)->status |= S_STREAMING)
+#define unset_streaming(dev) ((dev)->status &= ~S_STREAMING)
+
+struct regval {
+ unsigned reg;
+ unsigned val;
+};
+
+struct stk_camera {
+ struct video_device vdev;
+ struct usb_device *udev;
+ struct usb_interface *interface;
+ int webcam_model;
+ struct file *owner;
+
+ u8 isoc_ep;
+
+ /* Not sure if this is right */
+ atomic_t urbs_used;
+
+ struct stk_video vsettings;
+
+ enum stk_status status;
+
+ spinlock_t spinlock;
+ wait_queue_head_t wait_frame;
+
+ struct stk_iso_buf *isobufs;
+
+ int frame_size;
+ /* Streaming buffers */
+ unsigned int n_sbufs;
+ struct stk_sio_buffer *sio_bufs;
+ struct list_head sio_avail;
+ struct list_head sio_full;
+ unsigned sequence;
+};
+
+#define vdev_to_camera(d) container_of(d, struct stk_camera, vdev)
+
+int stk_camera_write_reg(struct stk_camera *, u16, u8);
+int stk_camera_read_reg(struct stk_camera *, u16, int *);
+
+int stk_sensor_init(struct stk_camera *);
+int stk_sensor_configure(struct stk_camera *);
+int stk_sensor_sleep(struct stk_camera *dev);
+int stk_sensor_wakeup(struct stk_camera *dev);
+int stk_sensor_set_brightness(struct stk_camera *dev, int br);
+
+#endif
diff --git a/drivers/media/video/tcm825x.c b/drivers/media/video/tcm825x.c
new file mode 100644
index 00000000..462caa44
--- /dev/null
+++ b/drivers/media/video/tcm825x.c
@@ -0,0 +1,937 @@
+/*
+ * drivers/media/video/tcm825x.c
+ *
+ * TCM825X camera sensor driver.
+ *
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+ *
+ * Based on code from David Cohen <david.cohen@indt.org.br>
+ *
+ * This driver was based on ov9640 sensor driver from MontaVista
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <media/v4l2-int-device.h>
+
+#include "tcm825x.h"
+
+/*
+ * The sensor has two fps modes: the lower one just gives half the fps
+ * at the same xclk than the high one.
+ */
+#define MAX_FPS 30
+#define MIN_FPS 8
+#define MAX_HALF_FPS (MAX_FPS / 2)
+#define HIGH_FPS_MODE_LOWER_LIMIT 14
+#define DEFAULT_FPS MAX_HALF_FPS
+
+struct tcm825x_sensor {
+ const struct tcm825x_platform_data *platform_data;
+ struct v4l2_int_device *v4l2_int_device;
+ struct i2c_client *i2c_client;
+ struct v4l2_pix_format pix;
+ struct v4l2_fract timeperframe;
+};
+
+/* list of image formats supported by TCM825X sensor */
+static const struct v4l2_fmtdesc tcm825x_formats[] = {
+ {
+ .description = "YUYV (YUV 4:2:2), packed",
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ }, {
+ /* Note: V4L2 defines RGB565 as:
+ *
+ * Byte 0 Byte 1
+ * g2 g1 g0 r4 r3 r2 r1 r0 b4 b3 b2 b1 b0 g5 g4 g3
+ *
+ * We interpret RGB565 as:
+ *
+ * Byte 0 Byte 1
+ * g2 g1 g0 b4 b3 b2 b1 b0 r4 r3 r2 r1 r0 g5 g4 g3
+ */
+ .description = "RGB565, le",
+ .pixelformat = V4L2_PIX_FMT_RGB565,
+ },
+};
+
+#define TCM825X_NUM_CAPTURE_FORMATS ARRAY_SIZE(tcm825x_formats)
+
+/*
+ * TCM825X register configuration for all combinations of pixel format and
+ * image size
+ */
+static const struct tcm825x_reg subqcif = { 0x20, TCM825X_PICSIZ };
+static const struct tcm825x_reg qcif = { 0x18, TCM825X_PICSIZ };
+static const struct tcm825x_reg cif = { 0x14, TCM825X_PICSIZ };
+static const struct tcm825x_reg qqvga = { 0x0c, TCM825X_PICSIZ };
+static const struct tcm825x_reg qvga = { 0x04, TCM825X_PICSIZ };
+static const struct tcm825x_reg vga = { 0x00, TCM825X_PICSIZ };
+
+static const struct tcm825x_reg yuv422 = { 0x00, TCM825X_PICFMT };
+static const struct tcm825x_reg rgb565 = { 0x02, TCM825X_PICFMT };
+
+/* Our own specific controls */
+#define V4L2_CID_ALC V4L2_CID_PRIVATE_BASE
+#define V4L2_CID_H_EDGE_EN V4L2_CID_PRIVATE_BASE + 1
+#define V4L2_CID_V_EDGE_EN V4L2_CID_PRIVATE_BASE + 2
+#define V4L2_CID_LENS V4L2_CID_PRIVATE_BASE + 3
+#define V4L2_CID_MAX_EXPOSURE_TIME V4L2_CID_PRIVATE_BASE + 4
+#define V4L2_CID_LAST_PRIV V4L2_CID_MAX_EXPOSURE_TIME
+
+/* Video controls */
+static struct vcontrol {
+ struct v4l2_queryctrl qc;
+ u16 reg;
+ u16 start_bit;
+} video_control[] = {
+ {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 63,
+ .step = 1,
+ },
+ .reg = TCM825X_AG,
+ .start_bit = 0,
+ },
+ {
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Balance",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ },
+ .reg = TCM825X_MRG,
+ .start_bit = 0,
+ },
+ {
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Balance",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ },
+ .reg = TCM825X_MBG,
+ .start_bit = 0,
+ },
+ {
+ {
+ .id = V4L2_CID_AUTO_WHITE_BALANCE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto White Balance",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 0,
+ },
+ .reg = TCM825X_AWBSW,
+ .start_bit = 7,
+ },
+ {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure Time",
+ .minimum = 0,
+ .maximum = 0x1fff,
+ .step = 1,
+ },
+ .reg = TCM825X_ESRSPD_U,
+ .start_bit = 0,
+ },
+ {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mirror Image",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 0,
+ },
+ .reg = TCM825X_H_INV,
+ .start_bit = 6,
+ },
+ {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Vertical Flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 0,
+ },
+ .reg = TCM825X_V_INV,
+ .start_bit = 7,
+ },
+ /* Private controls */
+ {
+ {
+ .id = V4L2_CID_ALC,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto Luminance Control",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 0,
+ },
+ .reg = TCM825X_ALCSW,
+ .start_bit = 7,
+ },
+ {
+ {
+ .id = V4L2_CID_H_EDGE_EN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Horizontal Edge Enhancement",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ },
+ .reg = TCM825X_HDTG,
+ .start_bit = 0,
+ },
+ {
+ {
+ .id = V4L2_CID_V_EDGE_EN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Vertical Edge Enhancement",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ },
+ .reg = TCM825X_VDTG,
+ .start_bit = 0,
+ },
+ {
+ {
+ .id = V4L2_CID_LENS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Lens Shading Compensation",
+ .minimum = 0,
+ .maximum = 0x3f,
+ .step = 1,
+ },
+ .reg = TCM825X_LENS,
+ .start_bit = 0,
+ },
+ {
+ {
+ .id = V4L2_CID_MAX_EXPOSURE_TIME,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Maximum Exposure Time",
+ .minimum = 0,
+ .maximum = 0x3,
+ .step = 1,
+ },
+ .reg = TCM825X_ESRLIM,
+ .start_bit = 5,
+ },
+};
+
+
+static const struct tcm825x_reg *tcm825x_siz_reg[NUM_IMAGE_SIZES] =
+{ &subqcif, &qqvga, &qcif, &qvga, &cif, &vga };
+
+static const struct tcm825x_reg *tcm825x_fmt_reg[NUM_PIXEL_FORMATS] =
+{ &yuv422, &rgb565 };
+
+/*
+ * Read a value from a register in an TCM825X sensor device. The value is
+ * returned in 'val'.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int tcm825x_read_reg(struct i2c_client *client, int reg)
+{
+ int err;
+ struct i2c_msg msg[2];
+ u8 reg_buf, data_buf = 0;
+
+ if (!client->adapter)
+ return -ENODEV;
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = &reg_buf;
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 1;
+ msg[1].buf = &data_buf;
+
+ reg_buf = reg;
+
+ err = i2c_transfer(client->adapter, msg, 2);
+ if (err < 0)
+ return err;
+ return data_buf;
+}
+
+/*
+ * Write a value to a register in an TCM825X sensor device.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int tcm825x_write_reg(struct i2c_client *client, u8 reg, u8 val)
+{
+ int err;
+ struct i2c_msg msg[1];
+ unsigned char data[2];
+
+ if (!client->adapter)
+ return -ENODEV;
+
+ msg->addr = client->addr;
+ msg->flags = 0;
+ msg->len = 2;
+ msg->buf = data;
+ data[0] = reg;
+ data[1] = val;
+ err = i2c_transfer(client->adapter, msg, 1);
+ if (err >= 0)
+ return 0;
+ return err;
+}
+
+static int __tcm825x_write_reg_mask(struct i2c_client *client,
+ u8 reg, u8 val, u8 mask)
+{
+ int rc;
+
+ /* need to do read - modify - write */
+ rc = tcm825x_read_reg(client, reg);
+ if (rc < 0)
+ return rc;
+
+ rc &= (~mask); /* Clear the masked bits */
+ val &= mask; /* Enforce mask on value */
+ val |= rc;
+
+ /* write the new value to the register */
+ rc = tcm825x_write_reg(client, reg, val);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+#define tcm825x_write_reg_mask(client, regmask, val) \
+ __tcm825x_write_reg_mask(client, TCM825X_ADDR((regmask)), val, \
+ TCM825X_MASK((regmask)))
+
+
+/*
+ * Initialize a list of TCM825X registers.
+ * The list of registers is terminated by the pair of values
+ * { TCM825X_REG_TERM, TCM825X_VAL_TERM }.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int tcm825x_write_default_regs(struct i2c_client *client,
+ const struct tcm825x_reg *reglist)
+{
+ int err;
+ const struct tcm825x_reg *next = reglist;
+
+ while (!((next->reg == TCM825X_REG_TERM)
+ && (next->val == TCM825X_VAL_TERM))) {
+ err = tcm825x_write_reg(client, next->reg, next->val);
+ if (err) {
+ dev_err(&client->dev, "register writing failed\n");
+ return err;
+ }
+ next++;
+ }
+
+ return 0;
+}
+
+static struct vcontrol *find_vctrl(int id)
+{
+ int i;
+
+ if (id < V4L2_CID_BASE)
+ return NULL;
+
+ for (i = 0; i < ARRAY_SIZE(video_control); i++)
+ if (video_control[i].qc.id == id)
+ return &video_control[i];
+
+ return NULL;
+}
+
+/*
+ * Find the best match for a requested image capture size. The best match
+ * is chosen as the nearest match that has the same number or fewer pixels
+ * as the requested size, or the smallest image size if the requested size
+ * has fewer pixels than the smallest image.
+ */
+static enum image_size tcm825x_find_size(struct v4l2_int_device *s,
+ unsigned int width,
+ unsigned int height)
+{
+ enum image_size isize;
+ unsigned long pixels = width * height;
+ struct tcm825x_sensor *sensor = s->priv;
+
+ for (isize = subQCIF; isize < VGA; isize++) {
+ if (tcm825x_sizes[isize + 1].height
+ * tcm825x_sizes[isize + 1].width > pixels) {
+ dev_dbg(&sensor->i2c_client->dev, "size %d\n", isize);
+
+ return isize;
+ }
+ }
+
+ dev_dbg(&sensor->i2c_client->dev, "format default VGA\n");
+
+ return VGA;
+}
+
+/*
+ * Configure the TCM825X for current image size, pixel format, and
+ * frame period. fper is the frame period (in seconds) expressed as a
+ * fraction. Returns zero if successful, or non-zero otherwise. The
+ * actual frame period is returned in fper.
+ */
+static int tcm825x_configure(struct v4l2_int_device *s)
+{
+ struct tcm825x_sensor *sensor = s->priv;
+ struct v4l2_pix_format *pix = &sensor->pix;
+ enum image_size isize = tcm825x_find_size(s, pix->width, pix->height);
+ struct v4l2_fract *fper = &sensor->timeperframe;
+ enum pixel_format pfmt;
+ int err;
+ u32 tgt_fps;
+ u8 val;
+
+ /* common register initialization */
+ err = tcm825x_write_default_regs(
+ sensor->i2c_client, sensor->platform_data->default_regs());
+ if (err)
+ return err;
+
+ /* configure image size */
+ val = tcm825x_siz_reg[isize]->val;
+ dev_dbg(&sensor->i2c_client->dev,
+ "configuring image size %d\n", isize);
+ err = tcm825x_write_reg_mask(sensor->i2c_client,
+ tcm825x_siz_reg[isize]->reg, val);
+ if (err)
+ return err;
+
+ /* configure pixel format */
+ switch (pix->pixelformat) {
+ default:
+ case V4L2_PIX_FMT_RGB565:
+ pfmt = RGB565;
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ pfmt = YUV422;
+ break;
+ }
+
+ dev_dbg(&sensor->i2c_client->dev,
+ "configuring pixel format %d\n", pfmt);
+ val = tcm825x_fmt_reg[pfmt]->val;
+
+ err = tcm825x_write_reg_mask(sensor->i2c_client,
+ tcm825x_fmt_reg[pfmt]->reg, val);
+ if (err)
+ return err;
+
+ /*
+ * For frame rate < 15, the FPS reg (addr 0x02, bit 7) must be
+ * set. Frame rate will be halved from the normal.
+ */
+ tgt_fps = fper->denominator / fper->numerator;
+ if (tgt_fps <= HIGH_FPS_MODE_LOWER_LIMIT) {
+ val = tcm825x_read_reg(sensor->i2c_client, 0x02);
+ val |= 0x80;
+ tcm825x_write_reg(sensor->i2c_client, 0x02, val);
+ }
+
+ return 0;
+}
+
+static int ioctl_queryctrl(struct v4l2_int_device *s,
+ struct v4l2_queryctrl *qc)
+{
+ struct vcontrol *control;
+
+ control = find_vctrl(qc->id);
+
+ if (control == NULL)
+ return -EINVAL;
+
+ *qc = control->qc;
+
+ return 0;
+}
+
+static int ioctl_g_ctrl(struct v4l2_int_device *s,
+ struct v4l2_control *vc)
+{
+ struct tcm825x_sensor *sensor = s->priv;
+ struct i2c_client *client = sensor->i2c_client;
+ int val, r;
+ struct vcontrol *lvc;
+
+ /* exposure time is special, spread across 2 registers */
+ if (vc->id == V4L2_CID_EXPOSURE) {
+ int val_lower, val_upper;
+
+ val_upper = tcm825x_read_reg(client,
+ TCM825X_ADDR(TCM825X_ESRSPD_U));
+ if (val_upper < 0)
+ return val_upper;
+ val_lower = tcm825x_read_reg(client,
+ TCM825X_ADDR(TCM825X_ESRSPD_L));
+ if (val_lower < 0)
+ return val_lower;
+
+ vc->value = ((val_upper & 0x1f) << 8) | (val_lower);
+ return 0;
+ }
+
+ lvc = find_vctrl(vc->id);
+ if (lvc == NULL)
+ return -EINVAL;
+
+ r = tcm825x_read_reg(client, TCM825X_ADDR(lvc->reg));
+ if (r < 0)
+ return r;
+ val = r & TCM825X_MASK(lvc->reg);
+ val >>= lvc->start_bit;
+
+ if (val < 0)
+ return val;
+
+ if (vc->id == V4L2_CID_HFLIP || vc->id == V4L2_CID_VFLIP)
+ val ^= sensor->platform_data->is_upside_down();
+
+ vc->value = val;
+ return 0;
+}
+
+static int ioctl_s_ctrl(struct v4l2_int_device *s,
+ struct v4l2_control *vc)
+{
+ struct tcm825x_sensor *sensor = s->priv;
+ struct i2c_client *client = sensor->i2c_client;
+ struct vcontrol *lvc;
+ int val = vc->value;
+
+ /* exposure time is special, spread across 2 registers */
+ if (vc->id == V4L2_CID_EXPOSURE) {
+ int val_lower, val_upper;
+ val_lower = val & TCM825X_MASK(TCM825X_ESRSPD_L);
+ val_upper = (val >> 8) & TCM825X_MASK(TCM825X_ESRSPD_U);
+
+ if (tcm825x_write_reg_mask(client,
+ TCM825X_ESRSPD_U, val_upper))
+ return -EIO;
+
+ if (tcm825x_write_reg_mask(client,
+ TCM825X_ESRSPD_L, val_lower))
+ return -EIO;
+
+ return 0;
+ }
+
+ lvc = find_vctrl(vc->id);
+ if (lvc == NULL)
+ return -EINVAL;
+
+ if (vc->id == V4L2_CID_HFLIP || vc->id == V4L2_CID_VFLIP)
+ val ^= sensor->platform_data->is_upside_down();
+
+ val = val << lvc->start_bit;
+ if (tcm825x_write_reg_mask(client, lvc->reg, val))
+ return -EIO;
+
+ return 0;
+}
+
+static int ioctl_enum_fmt_cap(struct v4l2_int_device *s,
+ struct v4l2_fmtdesc *fmt)
+{
+ int index = fmt->index;
+
+ switch (fmt->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (index >= TCM825X_NUM_CAPTURE_FORMATS)
+ return -EINVAL;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ fmt->flags = tcm825x_formats[index].flags;
+ strlcpy(fmt->description, tcm825x_formats[index].description,
+ sizeof(fmt->description));
+ fmt->pixelformat = tcm825x_formats[index].pixelformat;
+
+ return 0;
+}
+
+static int ioctl_try_fmt_cap(struct v4l2_int_device *s,
+ struct v4l2_format *f)
+{
+ struct tcm825x_sensor *sensor = s->priv;
+ enum image_size isize;
+ int ifmt;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ isize = tcm825x_find_size(s, pix->width, pix->height);
+ dev_dbg(&sensor->i2c_client->dev, "isize = %d num_capture = %lu\n",
+ isize, (unsigned long)TCM825X_NUM_CAPTURE_FORMATS);
+
+ pix->width = tcm825x_sizes[isize].width;
+ pix->height = tcm825x_sizes[isize].height;
+
+ for (ifmt = 0; ifmt < TCM825X_NUM_CAPTURE_FORMATS; ifmt++)
+ if (pix->pixelformat == tcm825x_formats[ifmt].pixelformat)
+ break;
+
+ if (ifmt == TCM825X_NUM_CAPTURE_FORMATS)
+ ifmt = 0; /* Default = YUV 4:2:2 */
+
+ pix->pixelformat = tcm825x_formats[ifmt].pixelformat;
+ pix->field = V4L2_FIELD_NONE;
+ pix->bytesperline = pix->width * TCM825X_BYTES_PER_PIXEL;
+ pix->sizeimage = pix->bytesperline * pix->height;
+ pix->priv = 0;
+ dev_dbg(&sensor->i2c_client->dev, "format = 0x%08x\n",
+ pix->pixelformat);
+
+ switch (pix->pixelformat) {
+ case V4L2_PIX_FMT_UYVY:
+ default:
+ pix->colorspace = V4L2_COLORSPACE_JPEG;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ pix->colorspace = V4L2_COLORSPACE_SRGB;
+ break;
+ }
+
+ return 0;
+}
+
+static int ioctl_s_fmt_cap(struct v4l2_int_device *s,
+ struct v4l2_format *f)
+{
+ struct tcm825x_sensor *sensor = s->priv;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ int rval;
+
+ rval = ioctl_try_fmt_cap(s, f);
+ if (rval)
+ return rval;
+
+ rval = tcm825x_configure(s);
+
+ sensor->pix = *pix;
+
+ return rval;
+}
+
+static int ioctl_g_fmt_cap(struct v4l2_int_device *s,
+ struct v4l2_format *f)
+{
+ struct tcm825x_sensor *sensor = s->priv;
+
+ f->fmt.pix = sensor->pix;
+
+ return 0;
+}
+
+static int ioctl_g_parm(struct v4l2_int_device *s,
+ struct v4l2_streamparm *a)
+{
+ struct tcm825x_sensor *sensor = s->priv;
+ struct v4l2_captureparm *cparm = &a->parm.capture;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ memset(a, 0, sizeof(*a));
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ cparm->capability = V4L2_CAP_TIMEPERFRAME;
+ cparm->timeperframe = sensor->timeperframe;
+
+ return 0;
+}
+
+static int ioctl_s_parm(struct v4l2_int_device *s,
+ struct v4l2_streamparm *a)
+{
+ struct tcm825x_sensor *sensor = s->priv;
+ struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
+ u32 tgt_fps; /* target frames per secound */
+ int rval;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if ((timeperframe->numerator == 0)
+ || (timeperframe->denominator == 0)) {
+ timeperframe->denominator = DEFAULT_FPS;
+ timeperframe->numerator = 1;
+ }
+
+ tgt_fps = timeperframe->denominator / timeperframe->numerator;
+
+ if (tgt_fps > MAX_FPS) {
+ timeperframe->denominator = MAX_FPS;
+ timeperframe->numerator = 1;
+ } else if (tgt_fps < MIN_FPS) {
+ timeperframe->denominator = MIN_FPS;
+ timeperframe->numerator = 1;
+ }
+
+ sensor->timeperframe = *timeperframe;
+
+ rval = tcm825x_configure(s);
+
+ return rval;
+}
+
+static int ioctl_s_power(struct v4l2_int_device *s, int on)
+{
+ struct tcm825x_sensor *sensor = s->priv;
+
+ return sensor->platform_data->power_set(on);
+}
+
+/*
+ * Given the image capture format in pix, the nominal frame period in
+ * timeperframe, calculate the required xclk frequency.
+ *
+ * TCM825X input frequency characteristics are:
+ * Minimum 11.9 MHz, Typical 24.57 MHz and maximum 25/27 MHz
+ */
+
+static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
+{
+ struct tcm825x_sensor *sensor = s->priv;
+ struct v4l2_fract *timeperframe = &sensor->timeperframe;
+ u32 tgt_xclk; /* target xclk */
+ u32 tgt_fps; /* target frames per secound */
+ int rval;
+
+ rval = sensor->platform_data->ifparm(p);
+ if (rval)
+ return rval;
+
+ tgt_fps = timeperframe->denominator / timeperframe->numerator;
+
+ tgt_xclk = (tgt_fps <= HIGH_FPS_MODE_LOWER_LIMIT) ?
+ (2457 * tgt_fps) / MAX_HALF_FPS :
+ (2457 * tgt_fps) / MAX_FPS;
+ tgt_xclk *= 10000;
+
+ tgt_xclk = min(tgt_xclk, (u32)TCM825X_XCLK_MAX);
+ tgt_xclk = max(tgt_xclk, (u32)TCM825X_XCLK_MIN);
+
+ p->u.bt656.clock_curr = tgt_xclk;
+
+ return 0;
+}
+
+static int ioctl_g_needs_reset(struct v4l2_int_device *s, void *buf)
+{
+ struct tcm825x_sensor *sensor = s->priv;
+
+ return sensor->platform_data->needs_reset(s, buf, &sensor->pix);
+}
+
+static int ioctl_reset(struct v4l2_int_device *s)
+{
+ return -EBUSY;
+}
+
+static int ioctl_init(struct v4l2_int_device *s)
+{
+ return tcm825x_configure(s);
+}
+
+static int ioctl_dev_exit(struct v4l2_int_device *s)
+{
+ return 0;
+}
+
+static int ioctl_dev_init(struct v4l2_int_device *s)
+{
+ struct tcm825x_sensor *sensor = s->priv;
+ int r;
+
+ r = tcm825x_read_reg(sensor->i2c_client, 0x01);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ dev_err(&sensor->i2c_client->dev, "device not detected\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+static struct v4l2_int_ioctl_desc tcm825x_ioctl_desc[] = {
+ { vidioc_int_dev_init_num,
+ (v4l2_int_ioctl_func *)ioctl_dev_init },
+ { vidioc_int_dev_exit_num,
+ (v4l2_int_ioctl_func *)ioctl_dev_exit },
+ { vidioc_int_s_power_num,
+ (v4l2_int_ioctl_func *)ioctl_s_power },
+ { vidioc_int_g_ifparm_num,
+ (v4l2_int_ioctl_func *)ioctl_g_ifparm },
+ { vidioc_int_g_needs_reset_num,
+ (v4l2_int_ioctl_func *)ioctl_g_needs_reset },
+ { vidioc_int_reset_num,
+ (v4l2_int_ioctl_func *)ioctl_reset },
+ { vidioc_int_init_num,
+ (v4l2_int_ioctl_func *)ioctl_init },
+ { vidioc_int_enum_fmt_cap_num,
+ (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap },
+ { vidioc_int_try_fmt_cap_num,
+ (v4l2_int_ioctl_func *)ioctl_try_fmt_cap },
+ { vidioc_int_g_fmt_cap_num,
+ (v4l2_int_ioctl_func *)ioctl_g_fmt_cap },
+ { vidioc_int_s_fmt_cap_num,
+ (v4l2_int_ioctl_func *)ioctl_s_fmt_cap },
+ { vidioc_int_g_parm_num,
+ (v4l2_int_ioctl_func *)ioctl_g_parm },
+ { vidioc_int_s_parm_num,
+ (v4l2_int_ioctl_func *)ioctl_s_parm },
+ { vidioc_int_queryctrl_num,
+ (v4l2_int_ioctl_func *)ioctl_queryctrl },
+ { vidioc_int_g_ctrl_num,
+ (v4l2_int_ioctl_func *)ioctl_g_ctrl },
+ { vidioc_int_s_ctrl_num,
+ (v4l2_int_ioctl_func *)ioctl_s_ctrl },
+};
+
+static struct v4l2_int_slave tcm825x_slave = {
+ .ioctls = tcm825x_ioctl_desc,
+ .num_ioctls = ARRAY_SIZE(tcm825x_ioctl_desc),
+};
+
+static struct tcm825x_sensor tcm825x;
+
+static struct v4l2_int_device tcm825x_int_device = {
+ .module = THIS_MODULE,
+ .name = TCM825X_NAME,
+ .priv = &tcm825x,
+ .type = v4l2_int_type_slave,
+ .u = {
+ .slave = &tcm825x_slave,
+ },
+};
+
+static int tcm825x_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct tcm825x_sensor *sensor = &tcm825x;
+
+ if (i2c_get_clientdata(client))
+ return -EBUSY;
+
+ sensor->platform_data = client->dev.platform_data;
+
+ if (sensor->platform_data == NULL
+ || !sensor->platform_data->is_okay())
+ return -ENODEV;
+
+ sensor->v4l2_int_device = &tcm825x_int_device;
+
+ sensor->i2c_client = client;
+ i2c_set_clientdata(client, sensor);
+
+ /* Make the default capture format QVGA RGB565 */
+ sensor->pix.width = tcm825x_sizes[QVGA].width;
+ sensor->pix.height = tcm825x_sizes[QVGA].height;
+ sensor->pix.pixelformat = V4L2_PIX_FMT_RGB565;
+
+ return v4l2_int_device_register(sensor->v4l2_int_device);
+}
+
+static int tcm825x_remove(struct i2c_client *client)
+{
+ struct tcm825x_sensor *sensor = i2c_get_clientdata(client);
+
+ if (!client->adapter)
+ return -ENODEV; /* our client isn't attached */
+
+ v4l2_int_device_unregister(sensor->v4l2_int_device);
+
+ return 0;
+}
+
+static const struct i2c_device_id tcm825x_id[] = {
+ { "tcm825x", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tcm825x_id);
+
+static struct i2c_driver tcm825x_i2c_driver = {
+ .driver = {
+ .name = TCM825X_NAME,
+ },
+ .probe = tcm825x_probe,
+ .remove = tcm825x_remove,
+ .id_table = tcm825x_id,
+};
+
+static struct tcm825x_sensor tcm825x = {
+ .timeperframe = {
+ .numerator = 1,
+ .denominator = DEFAULT_FPS,
+ },
+};
+
+static int __init tcm825x_init(void)
+{
+ int rval;
+
+ rval = i2c_add_driver(&tcm825x_i2c_driver);
+ if (rval)
+ printk(KERN_INFO "%s: failed registering " TCM825X_NAME "\n",
+ __func__);
+
+ return rval;
+}
+
+static void __exit tcm825x_exit(void)
+{
+ i2c_del_driver(&tcm825x_i2c_driver);
+}
+
+/*
+ * FIXME: Menelaus isn't ready (?) at module_init stage, so use
+ * late_initcall for now.
+ */
+late_initcall(tcm825x_init);
+module_exit(tcm825x_exit);
+
+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
+MODULE_DESCRIPTION("TCM825x camera sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/tcm825x.h b/drivers/media/video/tcm825x.h
new file mode 100644
index 00000000..5b7e6968
--- /dev/null
+++ b/drivers/media/video/tcm825x.h
@@ -0,0 +1,200 @@
+/*
+ * drivers/media/video/tcm825x.h
+ *
+ * Register definitions for the TCM825X CameraChip.
+ *
+ * Author: David Cohen (david.cohen@indt.org.br)
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ * This file was based on ov9640.h from MontaVista
+ */
+
+#ifndef TCM825X_H
+#define TCM825X_H
+
+#include <linux/videodev2.h>
+
+#include <media/v4l2-int-device.h>
+
+#define TCM825X_NAME "tcm825x"
+
+#define TCM825X_MASK(x) x & 0x00ff
+#define TCM825X_ADDR(x) (x & 0xff00) >> 8
+
+/* The TCM825X I2C sensor chip has a fixed slave address of 0x3d. */
+#define TCM825X_I2C_ADDR 0x3d
+
+/*
+ * define register offsets for the TCM825X sensor chip
+ * OFFSET(8 bits) + MASK(8 bits)
+ * MASK bit 4 and 3 are used when the register uses more than one address
+ */
+#define TCM825X_FPS 0x0280
+#define TCM825X_ACF 0x0240
+#define TCM825X_DOUTBUF 0x020C
+#define TCM825X_DCLKP 0x0202
+#define TCM825X_ACFDET 0x0201
+#define TCM825X_DOUTSW 0x0380
+#define TCM825X_DATAHZ 0x0340
+#define TCM825X_PICSIZ 0x033c
+#define TCM825X_PICFMT 0x0302
+#define TCM825X_V_INV 0x0480
+#define TCM825X_H_INV 0x0440
+#define TCM825X_ESRLSW 0x0430
+#define TCM825X_V_LENGTH 0x040F
+#define TCM825X_ALCSW 0x0580
+#define TCM825X_ESRLIM 0x0560
+#define TCM825X_ESRSPD_U 0x051F
+#define TCM825X_ESRSPD_L 0x06FF
+#define TCM825X_AG 0x07FF
+#define TCM825X_ESRSPD2 0x06FF
+#define TCM825X_ALCMODE 0x0830
+#define TCM825X_ALCH 0x080F
+#define TCM825X_ALCL 0x09FF
+#define TCM825X_AWBSW 0x0A80
+#define TCM825X_MRG 0x0BFF
+#define TCM825X_MBG 0x0CFF
+#define TCM825X_GAMSW 0x0D80
+#define TCM825X_HDTG 0x0EFF
+#define TCM825X_VDTG 0x0FFF
+#define TCM825X_HDTCORE 0x10F0
+#define TCM825X_VDTCORE 0x100F
+#define TCM825X_CONT 0x11FF
+#define TCM825X_BRIGHT 0x12FF
+#define TCM825X_VHUE 0x137F
+#define TCM825X_UHUE 0x147F
+#define TCM825X_VGAIN 0x153F
+#define TCM825X_UGAIN 0x163F
+#define TCM825X_UVCORE 0x170F
+#define TCM825X_SATU 0x187F
+#define TCM825X_MHMODE 0x1980
+#define TCM825X_MHLPFSEL 0x1940
+#define TCM825X_YMODE 0x1930
+#define TCM825X_MIXHG 0x1907
+#define TCM825X_LENS 0x1A3F
+#define TCM825X_AGLIM 0x1BE0
+#define TCM825X_LENSRPOL 0x1B10
+#define TCM825X_LENSRGAIN 0x1B0F
+#define TCM825X_ES100S 0x1CFF
+#define TCM825X_ES120S 0x1DFF
+#define TCM825X_DMASK 0x1EC0
+#define TCM825X_CODESW 0x1E20
+#define TCM825X_CODESEL 0x1E10
+#define TCM825X_TESPIC 0x1E04
+#define TCM825X_PICSEL 0x1E03
+#define TCM825X_HNUM 0x20FF
+#define TCM825X_VOUTPH 0x287F
+#define TCM825X_ESROUT 0x327F
+#define TCM825X_ESROUT2 0x33FF
+#define TCM825X_AGOUT 0x34FF
+#define TCM825X_DGOUT 0x353F
+#define TCM825X_AGSLOW1 0x39C0
+#define TCM825X_FLLSMODE 0x3930
+#define TCM825X_FLLSLIM 0x390F
+#define TCM825X_DETSEL 0x3AF0
+#define TCM825X_ACDETNC 0x3A0F
+#define TCM825X_AGSLOW2 0x3BC0
+#define TCM825X_DG 0x3B3F
+#define TCM825X_REJHLEV 0x3CFF
+#define TCM825X_ALCLOCK 0x3D80
+#define TCM825X_FPSLNKSW 0x3D40
+#define TCM825X_ALCSPD 0x3D30
+#define TCM825X_REJH 0x3D03
+#define TCM825X_SHESRSW 0x3E80
+#define TCM825X_ESLIMSEL 0x3E40
+#define TCM825X_SHESRSPD 0x3E30
+#define TCM825X_ELSTEP 0x3E0C
+#define TCM825X_ELSTART 0x3E03
+#define TCM825X_AGMIN 0x3FFF
+#define TCM825X_PREGRG 0x423F
+#define TCM825X_PREGBG 0x433F
+#define TCM825X_PRERG 0x443F
+#define TCM825X_PREBG 0x453F
+#define TCM825X_MSKBR 0x477F
+#define TCM825X_MSKGR 0x487F
+#define TCM825X_MSKRB 0x497F
+#define TCM825X_MSKGB 0x4A7F
+#define TCM825X_MSKRG 0x4B7F
+#define TCM825X_MSKBG 0x4C7F
+#define TCM825X_HDTCSW 0x4D80
+#define TCM825X_VDTCSW 0x4D40
+#define TCM825X_DTCYL 0x4D3F
+#define TCM825X_HDTPSW 0x4E80
+#define TCM825X_VDTPSW 0x4E40
+#define TCM825X_DTCGAIN 0x4E3F
+#define TCM825X_DTLLIMSW 0x4F10
+#define TCM825X_DTLYLIM 0x4F0F
+#define TCM825X_YLCUTLMSK 0x5080
+#define TCM825X_YLCUTL 0x503F
+#define TCM825X_YLCUTHMSK 0x5180
+#define TCM825X_YLCUTH 0x513F
+#define TCM825X_UVSKNC 0x527F
+#define TCM825X_UVLJ 0x537F
+#define TCM825X_WBGMIN 0x54FF
+#define TCM825X_WBGMAX 0x55FF
+#define TCM825X_WBSPDUP 0x5603
+#define TCM825X_ALLAREA 0x5820
+#define TCM825X_WBLOCK 0x5810
+#define TCM825X_WB2SP 0x580F
+#define TCM825X_KIZUSW 0x5920
+#define TCM825X_PBRSW 0x5910
+#define TCM825X_ABCSW 0x5903
+#define TCM825X_PBDLV 0x5AFF
+#define TCM825X_PBC1LV 0x5BFF
+
+#define TCM825X_NUM_REGS (TCM825X_ADDR(TCM825X_PBC1LV) + 1)
+
+#define TCM825X_BYTES_PER_PIXEL 2
+
+#define TCM825X_REG_TERM 0xff /* terminating list entry for reg */
+#define TCM825X_VAL_TERM 0xff /* terminating list entry for val */
+
+/* define a structure for tcm825x register initialization values */
+struct tcm825x_reg {
+ u8 val;
+ u16 reg;
+};
+
+enum image_size { subQCIF = 0, QQVGA, QCIF, QVGA, CIF, VGA };
+enum pixel_format { YUV422 = 0, RGB565 };
+#define NUM_IMAGE_SIZES 6
+#define NUM_PIXEL_FORMATS 2
+
+#define TCM825X_XCLK_MIN 11900000
+#define TCM825X_XCLK_MAX 25000000
+
+struct capture_size {
+ unsigned long width;
+ unsigned long height;
+};
+
+struct tcm825x_platform_data {
+ /* Is the sensor usable? Doesn't yet mean it's there, but you
+ * can try! */
+ int (*is_okay)(void);
+ /* Set power state, zero is off, non-zero is on. */
+ int (*power_set)(int power);
+ /* Default registers written after power-on or reset. */
+ const struct tcm825x_reg *(*default_regs)(void);
+ int (*needs_reset)(struct v4l2_int_device *s, void *buf,
+ struct v4l2_pix_format *fmt);
+ int (*ifparm)(struct v4l2_ifparm *p);
+ int (*is_upside_down)(void);
+};
+
+/* Array of image sizes supported by TCM825X. These must be ordered from
+ * smallest image size to largest.
+ */
+static const struct capture_size tcm825x_sizes[] = {
+ { 128, 96 }, /* subQCIF */
+ { 160, 120 }, /* QQVGA */
+ { 176, 144 }, /* QCIF */
+ { 320, 240 }, /* QVGA */
+ { 352, 288 }, /* CIF */
+ { 640, 480 }, /* VGA */
+};
+
+#endif /* ifndef TCM825X_H */
diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c
new file mode 100644
index 00000000..f7707e65
--- /dev/null
+++ b/drivers/media/video/tda7432.c
@@ -0,0 +1,485 @@
+/*
+ * For the STS-Thompson TDA7432 audio processor chip
+ *
+ * Handles audio functions: volume, balance, tone, loudness
+ * This driver will not complain if used with any
+ * other i2c device with the same address.
+ *
+ * Muting and tone control by Jonathan Isom <jisom@ematic.com>
+ *
+ * Copyright (c) 2000 Eric Sandeen <eric_sandeen@bigfoot.com>
+ * Copyright (c) 2006 Mauro Carvalho Chehab <mchehab@infradead.org>
+ * This code is placed under the terms of the GNU General Public License
+ * Based on tda9855.c by Steve VanDeBogart (vandebo@uclink.berkeley.edu)
+ * Which was based on tda8425.c by Greg Alexander (c) 1998
+ *
+ * OPTIONS:
+ * debug - set to 1 if you'd like to see debug messages
+ * set to 2 if you'd like to be inundated with debug messages
+ *
+ * loudness - set between 0 and 15 for varying degrees of loudness effect
+ *
+ * maxvol - set maximium volume to +20db (1), default is 0db(0)
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/i2c-addr.h>
+
+#ifndef VIDEO_AUDIO_BALANCE
+# define VIDEO_AUDIO_BALANCE 32
+#endif
+
+MODULE_AUTHOR("Eric Sandeen <eric_sandeen@bigfoot.com>");
+MODULE_DESCRIPTION("bttv driver for the tda7432 audio processor chip");
+MODULE_LICENSE("GPL");
+
+static int maxvol;
+static int loudness; /* disable loudness by default */
+static int debug; /* insmod parameter */
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Set debugging level from 0 to 3. Default is off(0).");
+module_param(loudness, int, S_IRUGO);
+MODULE_PARM_DESC(loudness, "Turn loudness on(1) else off(0). Default is off(0).");
+module_param(maxvol, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(maxvol, "Set maximium volume to +20dB(0) else +0dB(1). Default is +20dB(0).");
+
+
+/* Structure of address and subaddresses for the tda7432 */
+
+struct tda7432 {
+ struct v4l2_subdev sd;
+ int addr;
+ int input;
+ int volume;
+ int muted;
+ int bass, treble;
+ int lf, lr, rf, rr;
+ int loud;
+};
+
+static inline struct tda7432 *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct tda7432, sd);
+}
+
+/* The TDA7432 is made by STS-Thompson
+ * http://www.st.com
+ * http://us.st.com/stonline/books/pdf/docs/4056.pdf
+ *
+ * TDA7432: I2C-bus controlled basic audio processor
+ *
+ * The TDA7432 controls basic audio functions like volume, balance,
+ * and tone control (including loudness). It also has four channel
+ * output (for front and rear). Since most vidcap cards probably
+ * don't have 4 channel output, this driver will set front & rear
+ * together (no independent control).
+ */
+
+ /* Subaddresses for TDA7432 */
+
+#define TDA7432_IN 0x00 /* Input select */
+#define TDA7432_VL 0x01 /* Volume */
+#define TDA7432_TN 0x02 /* Bass, Treble (Tone) */
+#define TDA7432_LF 0x03 /* Attenuation LF (Left Front) */
+#define TDA7432_LR 0x04 /* Attenuation LR (Left Rear) */
+#define TDA7432_RF 0x05 /* Attenuation RF (Right Front) */
+#define TDA7432_RR 0x06 /* Attenuation RR (Right Rear) */
+#define TDA7432_LD 0x07 /* Loudness */
+
+
+ /* Masks for bits in TDA7432 subaddresses */
+
+/* Many of these not used - just for documentation */
+
+/* Subaddress 0x00 - Input selection and bass control */
+
+/* Bits 0,1,2 control input:
+ * 0x00 - Stereo input
+ * 0x02 - Mono input
+ * 0x03 - Mute (Using Attenuators Plays better with modules)
+ * Mono probably isn't used - I'm guessing only the stereo
+ * input is connected on most cards, so we'll set it to stereo.
+ *
+ * Bit 3 controls bass cut: 0/1 is non-symmetric/symmetric bass cut
+ * Bit 4 controls bass range: 0/1 is extended/standard bass range
+ *
+ * Highest 3 bits not used
+ */
+
+#define TDA7432_STEREO_IN 0
+#define TDA7432_MONO_IN 2 /* Probably won't be used */
+#define TDA7432_BASS_SYM 1 << 3
+#define TDA7432_BASS_NORM 1 << 4
+
+/* Subaddress 0x01 - Volume */
+
+/* Lower 7 bits control volume from -79dB to +32dB in 1dB steps
+ * Recommended maximum is +20 dB
+ *
+ * +32dB: 0x00
+ * +20dB: 0x0c
+ * 0dB: 0x20
+ * -79dB: 0x6f
+ *
+ * MSB (bit 7) controls loudness: 1/0 is loudness on/off
+ */
+
+#define TDA7432_VOL_0DB 0x20
+#define TDA7432_LD_ON 1 << 7
+
+
+/* Subaddress 0x02 - Tone control */
+
+/* Bits 0,1,2 control absolute treble gain from 0dB to 14dB
+ * 0x0 is 14dB, 0x7 is 0dB
+ *
+ * Bit 3 controls treble attenuation/gain (sign)
+ * 1 = gain (+)
+ * 0 = attenuation (-)
+ *
+ * Bits 4,5,6 control absolute bass gain from 0dB to 14dB
+ * (This is only true for normal base range, set in 0x00)
+ * 0x0 << 4 is 14dB, 0x7 is 0dB
+ *
+ * Bit 7 controls bass attenuation/gain (sign)
+ * 1 << 7 = gain (+)
+ * 0 << 7 = attenuation (-)
+ *
+ * Example:
+ * 1 1 0 1 0 1 0 1 is +4dB bass, -4dB treble
+ */
+
+#define TDA7432_TREBLE_0DB 0xf
+#define TDA7432_TREBLE 7
+#define TDA7432_TREBLE_GAIN 1 << 3
+#define TDA7432_BASS_0DB 0xf
+#define TDA7432_BASS 7 << 4
+#define TDA7432_BASS_GAIN 1 << 7
+
+
+/* Subaddress 0x03 - Left Front attenuation */
+/* Subaddress 0x04 - Left Rear attenuation */
+/* Subaddress 0x05 - Right Front attenuation */
+/* Subaddress 0x06 - Right Rear attenuation */
+
+/* Bits 0,1,2,3,4 control attenuation from 0dB to -37.5dB
+ * in 1.5dB steps.
+ *
+ * 0x00 is 0dB
+ * 0x1f is -37.5dB
+ *
+ * Bit 5 mutes that channel when set (1 = mute, 0 = unmute)
+ * We'll use the mute on the input, though (above)
+ * Bits 6,7 unused
+ */
+
+#define TDA7432_ATTEN_0DB 0x00
+#define TDA7432_MUTE 0x1 << 5
+
+
+/* Subaddress 0x07 - Loudness Control */
+
+/* Bits 0,1,2,3 control loudness from 0dB to -15dB in 1dB steps
+ * when bit 4 is NOT set
+ *
+ * 0x0 is 0dB
+ * 0xf is -15dB
+ *
+ * If bit 4 is set, then there is a flat attenuation according to
+ * the lower 4 bits, as above.
+ *
+ * Bits 5,6,7 unused
+ */
+
+
+
+/* Begin code */
+
+static int tda7432_write(struct v4l2_subdev *sd, int subaddr, int val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ unsigned char buffer[2];
+
+ v4l2_dbg(2, debug, sd, "In tda7432_write\n");
+ v4l2_dbg(1, debug, sd, "Writing %d 0x%x\n", subaddr, val);
+ buffer[0] = subaddr;
+ buffer[1] = val;
+ if (2 != i2c_master_send(client, buffer, 2)) {
+ v4l2_err(sd, "I/O error, trying (write %d 0x%x)\n",
+ subaddr, val);
+ return -1;
+ }
+ return 0;
+}
+
+static int tda7432_set(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tda7432 *t = to_state(sd);
+ unsigned char buf[16];
+
+ v4l2_dbg(1, debug, sd,
+ "tda7432: 7432_set(0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x)\n",
+ t->input, t->volume, t->bass, t->treble, t->lf, t->lr,
+ t->rf, t->rr, t->loud);
+ buf[0] = TDA7432_IN;
+ buf[1] = t->input;
+ buf[2] = t->volume;
+ buf[3] = t->bass;
+ buf[4] = t->treble;
+ buf[5] = t->lf;
+ buf[6] = t->lr;
+ buf[7] = t->rf;
+ buf[8] = t->rr;
+ buf[9] = t->loud;
+ if (10 != i2c_master_send(client, buf, 10)) {
+ v4l2_err(sd, "I/O error, trying tda7432_set\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void do_tda7432_init(struct v4l2_subdev *sd)
+{
+ struct tda7432 *t = to_state(sd);
+
+ v4l2_dbg(2, debug, sd, "In tda7432_init\n");
+
+ t->input = TDA7432_STEREO_IN | /* Main (stereo) input */
+ TDA7432_BASS_SYM | /* Symmetric bass cut */
+ TDA7432_BASS_NORM; /* Normal bass range */
+ t->volume = 0x3b ; /* -27dB Volume */
+ if (loudness) /* Turn loudness on? */
+ t->volume |= TDA7432_LD_ON;
+ t->muted = 1;
+ t->treble = TDA7432_TREBLE_0DB; /* 0dB Treble */
+ t->bass = TDA7432_BASS_0DB; /* 0dB Bass */
+ t->lf = TDA7432_ATTEN_0DB; /* 0dB attenuation */
+ t->lr = TDA7432_ATTEN_0DB; /* 0dB attenuation */
+ t->rf = TDA7432_ATTEN_0DB; /* 0dB attenuation */
+ t->rr = TDA7432_ATTEN_0DB; /* 0dB attenuation */
+ t->loud = loudness; /* insmod parameter */
+
+ tda7432_set(sd);
+}
+
+static int tda7432_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct tda7432 *t = to_state(sd);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value=t->muted;
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+ if (!maxvol){ /* max +20db */
+ ctrl->value = ( 0x6f - (t->volume & 0x7F) ) * 630;
+ } else { /* max 0db */
+ ctrl->value = ( 0x6f - (t->volume & 0x7F) ) * 829;
+ }
+ return 0;
+ case V4L2_CID_AUDIO_BALANCE:
+ {
+ if ( (t->lf) < (t->rf) )
+ /* right is attenuated, balance shifted left */
+ ctrl->value = (32768 - 1057*(t->rf));
+ else
+ /* left is attenuated, balance shifted right */
+ ctrl->value = (32768 + 1057*(t->lf));
+ return 0;
+ }
+ case V4L2_CID_AUDIO_BASS:
+ {
+ /* Bass/treble 4 bits each */
+ int bass=t->bass;
+ if(bass >= 0x8)
+ bass = ~(bass - 0x8) & 0xf;
+ ctrl->value = (bass << 12)+(bass << 8)+(bass << 4)+(bass);
+ return 0;
+ }
+ case V4L2_CID_AUDIO_TREBLE:
+ {
+ int treble=t->treble;
+ if(treble >= 0x8)
+ treble = ~(treble - 0x8) & 0xf;
+ ctrl->value = (treble << 12)+(treble << 8)+(treble << 4)+(treble);
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int tda7432_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct tda7432 *t = to_state(sd);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ t->muted=ctrl->value;
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ if(!maxvol){ /* max +20db */
+ t->volume = 0x6f - ((ctrl->value)/630);
+ } else { /* max 0db */
+ t->volume = 0x6f - ((ctrl->value)/829);
+ }
+ if (loudness) /* Turn on the loudness bit */
+ t->volume |= TDA7432_LD_ON;
+
+ tda7432_write(sd, TDA7432_VL, t->volume);
+ return 0;
+ case V4L2_CID_AUDIO_BALANCE:
+ if (ctrl->value < 32768) {
+ /* shifted to left, attenuate right */
+ t->rr = (32768 - ctrl->value)/1057;
+ t->rf = t->rr;
+ t->lr = TDA7432_ATTEN_0DB;
+ t->lf = TDA7432_ATTEN_0DB;
+ } else if(ctrl->value > 32769) {
+ /* shifted to right, attenuate left */
+ t->lf = (ctrl->value - 32768)/1057;
+ t->lr = t->lf;
+ t->rr = TDA7432_ATTEN_0DB;
+ t->rf = TDA7432_ATTEN_0DB;
+ } else {
+ /* centered */
+ t->rr = TDA7432_ATTEN_0DB;
+ t->rf = TDA7432_ATTEN_0DB;
+ t->lf = TDA7432_ATTEN_0DB;
+ t->lr = TDA7432_ATTEN_0DB;
+ }
+ break;
+ case V4L2_CID_AUDIO_BASS:
+ t->bass = ctrl->value >> 12;
+ if(t->bass>= 0x8)
+ t->bass = (~t->bass & 0xf) + 0x8 ;
+
+ tda7432_write(sd, TDA7432_TN, 0x10 | (t->bass << 4) | t->treble);
+ return 0;
+ case V4L2_CID_AUDIO_TREBLE:
+ t->treble= ctrl->value >> 12;
+ if(t->treble>= 0x8)
+ t->treble = (~t->treble & 0xf) + 0x8 ;
+
+ tda7432_write(sd, TDA7432_TN, 0x10 | (t->bass << 4) | t->treble);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ /* Used for both mute and balance changes */
+ if (t->muted)
+ {
+ /* Mute & update balance*/
+ tda7432_write(sd, TDA7432_LF, t->lf | TDA7432_MUTE);
+ tda7432_write(sd, TDA7432_LR, t->lr | TDA7432_MUTE);
+ tda7432_write(sd, TDA7432_RF, t->rf | TDA7432_MUTE);
+ tda7432_write(sd, TDA7432_RR, t->rr | TDA7432_MUTE);
+ } else {
+ tda7432_write(sd, TDA7432_LF, t->lf);
+ tda7432_write(sd, TDA7432_LR, t->lr);
+ tda7432_write(sd, TDA7432_RF, t->rf);
+ tda7432_write(sd, TDA7432_RR, t->rr);
+ }
+ return 0;
+}
+
+static int tda7432_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880);
+ case V4L2_CID_AUDIO_MUTE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768);
+ }
+ return -EINVAL;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tda7432_core_ops = {
+ .queryctrl = tda7432_queryctrl,
+ .g_ctrl = tda7432_g_ctrl,
+ .s_ctrl = tda7432_s_ctrl,
+};
+
+static const struct v4l2_subdev_ops tda7432_ops = {
+ .core = &tda7432_core_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+/* *********************** *
+ * i2c interface functions *
+ * *********************** */
+
+static int tda7432_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tda7432 *t;
+ struct v4l2_subdev *sd;
+
+ v4l_info(client, "chip found @ 0x%02x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ t = kzalloc(sizeof(*t), GFP_KERNEL);
+ if (!t)
+ return -ENOMEM;
+ sd = &t->sd;
+ v4l2_i2c_subdev_init(sd, client, &tda7432_ops);
+ if (loudness < 0 || loudness > 15) {
+ v4l2_warn(sd, "loudness parameter must be between 0 and 15\n");
+ if (loudness < 0)
+ loudness = 0;
+ if (loudness > 15)
+ loudness = 15;
+ }
+
+ do_tda7432_init(sd);
+ return 0;
+}
+
+static int tda7432_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ do_tda7432_init(sd);
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
+ return 0;
+}
+
+static const struct i2c_device_id tda7432_id[] = {
+ { "tda7432", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tda7432_id);
+
+static struct i2c_driver tda7432_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "tda7432",
+ },
+ .probe = tda7432_probe,
+ .remove = tda7432_remove,
+ .id_table = tda7432_id,
+};
+
+module_i2c_driver(tda7432_driver);
diff --git a/drivers/media/video/tda9840.c b/drivers/media/video/tda9840.c
new file mode 100644
index 00000000..465d7086
--- /dev/null
+++ b/drivers/media/video/tda9840.c
@@ -0,0 +1,211 @@
+ /*
+ tda9840 - i2c-driver for the tda9840 by SGS Thomson
+
+ Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de>
+ Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
+
+ The tda9840 is a stereo/dual sound processor with digital
+ identification. It can be found at address 0x84 on the i2c-bus.
+
+ For detailed informations download the specifications directly
+ from SGS Thomson at http://www.st.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/module.h>
+#include <linux/ioctl.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_DESCRIPTION("tda9840 driver");
+MODULE_LICENSE("GPL");
+
+static int debug;
+module_param(debug, int, 0644);
+
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+#define SWITCH 0x00
+#define LEVEL_ADJUST 0x02
+#define STEREO_ADJUST 0x03
+#define TEST 0x04
+
+#define TDA9840_SET_MUTE 0x00
+#define TDA9840_SET_MONO 0x10
+#define TDA9840_SET_STEREO 0x2a
+#define TDA9840_SET_LANG1 0x12
+#define TDA9840_SET_LANG2 0x1e
+#define TDA9840_SET_BOTH 0x1a
+#define TDA9840_SET_BOTH_R 0x16
+#define TDA9840_SET_EXTERNAL 0x7a
+
+
+static void tda9840_write(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (i2c_smbus_write_byte_data(client, reg, val))
+ v4l2_dbg(1, debug, sd, "error writing %02x to %02x\n",
+ val, reg);
+}
+
+static int tda9840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t)
+{
+ int byte;
+
+ if (t->index)
+ return -EINVAL;
+
+ switch (t->audmode) {
+ case V4L2_TUNER_MODE_STEREO:
+ byte = TDA9840_SET_STEREO;
+ break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ byte = TDA9840_SET_BOTH;
+ break;
+ case V4L2_TUNER_MODE_LANG1:
+ byte = TDA9840_SET_LANG1;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ byte = TDA9840_SET_LANG2;
+ break;
+ default:
+ byte = TDA9840_SET_MONO;
+ break;
+ }
+ v4l2_dbg(1, debug, sd, "TDA9840_SWITCH: 0x%02x\n", byte);
+ tda9840_write(sd, SWITCH, byte);
+ return 0;
+}
+
+static int tda9840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 byte;
+
+ t->rxsubchans = V4L2_TUNER_SUB_MONO;
+ if (1 != i2c_master_recv(client, &byte, 1)) {
+ v4l2_dbg(1, debug, sd,
+ "i2c_master_recv() failed\n");
+ return -EIO;
+ }
+
+ if (byte & 0x80) {
+ v4l2_dbg(1, debug, sd,
+ "TDA9840_DETECT: register contents invalid\n");
+ return -EINVAL;
+ }
+
+ v4l2_dbg(1, debug, sd, "TDA9840_DETECT: byte: 0x%02x\n", byte);
+
+ switch (byte & 0x60) {
+ case 0x00:
+ t->rxsubchans = V4L2_TUNER_SUB_MONO;
+ break;
+ case 0x20:
+ t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ break;
+ case 0x40:
+ t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO;
+ break;
+ default: /* Incorrect detect */
+ t->rxsubchans = V4L2_TUNER_MODE_MONO;
+ break;
+ }
+ return 0;
+}
+
+static int tda9840_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TDA9840, 0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tda9840_core_ops = {
+ .g_chip_ident = tda9840_g_chip_ident,
+};
+
+static const struct v4l2_subdev_tuner_ops tda9840_tuner_ops = {
+ .s_tuner = tda9840_s_tuner,
+ .g_tuner = tda9840_g_tuner,
+};
+
+static const struct v4l2_subdev_ops tda9840_ops = {
+ .core = &tda9840_core_ops,
+ .tuner = &tda9840_tuner_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int tda9840_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct v4l2_subdev *sd;
+
+ /* let's see whether this adapter can support what we need */
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_BYTE_DATA |
+ I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
+ return -EIO;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ if (sd == NULL)
+ return -ENOMEM;
+ v4l2_i2c_subdev_init(sd, client, &tda9840_ops);
+
+ /* set initial values for level & stereo - adjustment, mode */
+ tda9840_write(sd, LEVEL_ADJUST, 0);
+ tda9840_write(sd, STEREO_ADJUST, 0);
+ tda9840_write(sd, SWITCH, TDA9840_SET_STEREO);
+ return 0;
+}
+
+static int tda9840_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(sd);
+ return 0;
+}
+
+static const struct i2c_device_id tda9840_id[] = {
+ { "tda9840", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tda9840_id);
+
+static struct i2c_driver tda9840_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "tda9840",
+ },
+ .probe = tda9840_probe,
+ .remove = tda9840_remove,
+ .id_table = tda9840_id,
+};
+
+module_i2c_driver(tda9840_driver);
diff --git a/drivers/media/video/tea6415c.c b/drivers/media/video/tea6415c.c
new file mode 100644
index 00000000..d1d6ea1d
--- /dev/null
+++ b/drivers/media/video/tea6415c.c
@@ -0,0 +1,187 @@
+ /*
+ tea6415c - i2c-driver for the tea6415c by SGS Thomson
+
+ Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de>
+ Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
+
+ The tea6415c is a bus controlled video-matrix-switch
+ with 8 inputs and 6 outputs.
+ It is cascadable, i.e. it can be found at the addresses
+ 0x86 and 0x06 on the i2c-bus.
+
+ For detailed informations download the specifications directly
+ from SGS Thomson at http://www.st.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License vs published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mvss Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/module.h>
+#include <linux/ioctl.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include "tea6415c.h"
+
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_DESCRIPTION("tea6415c driver");
+MODULE_LICENSE("GPL");
+
+static int debug;
+module_param(debug, int, 0644);
+
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+
+/* makes a connection between the input-pin 'i' and the output-pin 'o' */
+static int tea6415c_s_routing(struct v4l2_subdev *sd,
+ u32 i, u32 o, u32 config)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 byte = 0;
+ int ret;
+
+ v4l2_dbg(1, debug, sd, "i=%d, o=%d\n", i, o);
+
+ /* check if the pins are valid */
+ if (0 == ((1 == i || 3 == i || 5 == i || 6 == i || 8 == i || 10 == i || 20 == i || 11 == i)
+ && (18 == o || 17 == o || 16 == o || 15 == o || 14 == o || 13 == o)))
+ return -EINVAL;
+
+ /* to understand this, have a look at the tea6415c-specs (p.5) */
+ switch (o) {
+ case 18:
+ byte = 0x00;
+ break;
+ case 14:
+ byte = 0x20;
+ break;
+ case 16:
+ byte = 0x10;
+ break;
+ case 17:
+ byte = 0x08;
+ break;
+ case 15:
+ byte = 0x18;
+ break;
+ case 13:
+ byte = 0x28;
+ break;
+ };
+
+ switch (i) {
+ case 5:
+ byte |= 0x00;
+ break;
+ case 8:
+ byte |= 0x04;
+ break;
+ case 3:
+ byte |= 0x02;
+ break;
+ case 20:
+ byte |= 0x06;
+ break;
+ case 6:
+ byte |= 0x01;
+ break;
+ case 10:
+ byte |= 0x05;
+ break;
+ case 1:
+ byte |= 0x03;
+ break;
+ case 11:
+ byte |= 0x07;
+ break;
+ };
+
+ ret = i2c_smbus_write_byte(client, byte);
+ if (ret) {
+ v4l2_dbg(1, debug, sd,
+ "i2c_smbus_write_byte() failed, ret:%d\n", ret);
+ return -EIO;
+ }
+ return ret;
+}
+
+static int tea6415c_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TEA6415C, 0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tea6415c_core_ops = {
+ .g_chip_ident = tea6415c_g_chip_ident,
+};
+
+static const struct v4l2_subdev_video_ops tea6415c_video_ops = {
+ .s_routing = tea6415c_s_routing,
+};
+
+static const struct v4l2_subdev_ops tea6415c_ops = {
+ .core = &tea6415c_core_ops,
+ .video = &tea6415c_video_ops,
+};
+
+static int tea6415c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct v4l2_subdev *sd;
+
+ /* let's see whether this adapter can support what we need */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE))
+ return -EIO;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+ sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ if (sd == NULL)
+ return -ENOMEM;
+ v4l2_i2c_subdev_init(sd, client, &tea6415c_ops);
+ return 0;
+}
+
+static int tea6415c_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(sd);
+ return 0;
+}
+
+static const struct i2c_device_id tea6415c_id[] = {
+ { "tea6415c", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tea6415c_id);
+
+static struct i2c_driver tea6415c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "tea6415c",
+ },
+ .probe = tea6415c_probe,
+ .remove = tea6415c_remove,
+ .id_table = tea6415c_id,
+};
+
+module_i2c_driver(tea6415c_driver);
diff --git a/drivers/media/video/tea6415c.h b/drivers/media/video/tea6415c.h
new file mode 100644
index 00000000..3a47d697
--- /dev/null
+++ b/drivers/media/video/tea6415c.h
@@ -0,0 +1,27 @@
+#ifndef __INCLUDED_TEA6415C__
+#define __INCLUDED_TEA6415C__
+
+/* the tea6415c's design is quite brain-dead. although there are
+ 8 inputs and 6 outputs, these aren't enumerated in any way. because
+ I don't want to say "connect input pin 20 to output pin 17", I define
+ a "virtual" pin-order. */
+
+/* input pins */
+#define TEA6415C_OUTPUT1 18
+#define TEA6415C_OUTPUT2 14
+#define TEA6415C_OUTPUT3 16
+#define TEA6415C_OUTPUT4 17
+#define TEA6415C_OUTPUT5 13
+#define TEA6415C_OUTPUT6 15
+
+/* output pins */
+#define TEA6415C_INPUT1 5
+#define TEA6415C_INPUT2 8
+#define TEA6415C_INPUT3 3
+#define TEA6415C_INPUT4 20
+#define TEA6415C_INPUT5 6
+#define TEA6415C_INPUT6 10
+#define TEA6415C_INPUT7 1
+#define TEA6415C_INPUT8 11
+
+#endif
diff --git a/drivers/media/video/tea6420.c b/drivers/media/video/tea6420.c
new file mode 100644
index 00000000..38757217
--- /dev/null
+++ b/drivers/media/video/tea6420.c
@@ -0,0 +1,169 @@
+ /*
+ tea6420 - i2c-driver for the tea6420 by SGS Thomson
+
+ Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de>
+ Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
+
+ The tea6420 is a bus controlled audio-matrix with 5 stereo inputs,
+ 4 stereo outputs and gain control for each output.
+ It is cascadable, i.e. it can be found at the addresses 0x98
+ and 0x9a on the i2c-bus.
+
+ For detailed informations download the specifications directly
+ from SGS Thomson at http://www.st.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/module.h>
+#include <linux/ioctl.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include "tea6420.h"
+
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_DESCRIPTION("tea6420 driver");
+MODULE_LICENSE("GPL");
+
+static int debug;
+module_param(debug, int, 0644);
+
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+
+/* make a connection between the input 'i' and the output 'o'
+ with gain 'g' (note: i = 6 means 'mute') */
+static int tea6420_s_routing(struct v4l2_subdev *sd,
+ u32 i, u32 o, u32 config)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int g = (o >> 4) & 0xf;
+ u8 byte;
+ int ret;
+
+ o &= 0xf;
+ v4l2_dbg(1, debug, sd, "i=%d, o=%d, g=%d\n", i, o, g);
+
+ /* check if the parameters are valid */
+ if (i < 1 || i > 6 || o < 1 || o > 4 || g < 0 || g > 6 || g % 2 != 0)
+ return -EINVAL;
+
+ byte = ((o - 1) << 5);
+ byte |= (i - 1);
+
+ /* to understand this, have a look at the tea6420-specs (p.5) */
+ switch (g) {
+ case 0:
+ byte |= (3 << 3);
+ break;
+ case 2:
+ byte |= (2 << 3);
+ break;
+ case 4:
+ byte |= (1 << 3);
+ break;
+ case 6:
+ break;
+ }
+
+ ret = i2c_smbus_write_byte(client, byte);
+ if (ret) {
+ v4l2_dbg(1, debug, sd,
+ "i2c_smbus_write_byte() failed, ret:%d\n", ret);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int tea6420_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TEA6420, 0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tea6420_core_ops = {
+ .g_chip_ident = tea6420_g_chip_ident,
+};
+
+static const struct v4l2_subdev_audio_ops tea6420_audio_ops = {
+ .s_routing = tea6420_s_routing,
+};
+
+static const struct v4l2_subdev_ops tea6420_ops = {
+ .core = &tea6420_core_ops,
+ .audio = &tea6420_audio_ops,
+};
+
+static int tea6420_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct v4l2_subdev *sd;
+ int err, i;
+
+ /* let's see whether this adapter can support what we need */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE))
+ return -EIO;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ if (sd == NULL)
+ return -ENOMEM;
+ v4l2_i2c_subdev_init(sd, client, &tea6420_ops);
+
+ /* set initial values: set "mute"-input to all outputs at gain 0 */
+ err = 0;
+ for (i = 1; i < 5; i++)
+ err += tea6420_s_routing(sd, 6, i, 0);
+ if (err) {
+ v4l_dbg(1, debug, client, "could not initialize tea6420\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int tea6420_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(sd);
+ return 0;
+}
+
+static const struct i2c_device_id tea6420_id[] = {
+ { "tea6420", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tea6420_id);
+
+static struct i2c_driver tea6420_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "tea6420",
+ },
+ .probe = tea6420_probe,
+ .remove = tea6420_remove,
+ .id_table = tea6420_id,
+};
+
+module_i2c_driver(tea6420_driver);
diff --git a/drivers/media/video/tea6420.h b/drivers/media/video/tea6420.h
new file mode 100644
index 00000000..4aa3edb3
--- /dev/null
+++ b/drivers/media/video/tea6420.h
@@ -0,0 +1,24 @@
+#ifndef __INCLUDED_TEA6420__
+#define __INCLUDED_TEA6420__
+
+/* input pins */
+#define TEA6420_OUTPUT1 1
+#define TEA6420_OUTPUT2 2
+#define TEA6420_OUTPUT3 3
+#define TEA6420_OUTPUT4 4
+
+/* output pins */
+#define TEA6420_INPUT1 1
+#define TEA6420_INPUT2 2
+#define TEA6420_INPUT3 3
+#define TEA6420_INPUT4 4
+#define TEA6420_INPUT5 5
+#define TEA6420_INPUT6 6
+
+/* gain on the output pins, ORed with the output pin */
+#define TEA6420_GAIN0 0x00
+#define TEA6420_GAIN2 0x20
+#define TEA6420_GAIN4 0x40
+#define TEA6420_GAIN6 0x60
+
+#endif
diff --git a/drivers/media/video/ths7303.c b/drivers/media/video/ths7303.c
new file mode 100644
index 00000000..e5c0eede
--- /dev/null
+++ b/drivers/media/video/ths7303.c
@@ -0,0 +1,140 @@
+/*
+ * ths7303- THS7303 Video Amplifier driver
+ *
+ * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed .as is. WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-chip-ident.h>
+
+MODULE_DESCRIPTION("TI THS7303 video amplifier driver");
+MODULE_AUTHOR("Chaithrika U S");
+MODULE_LICENSE("GPL");
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level 0-1");
+
+/* following function is used to set ths7303 */
+static int ths7303_setvalue(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ int err = 0;
+ u8 val;
+ struct i2c_client *client;
+
+ client = v4l2_get_subdevdata(sd);
+
+ if (std & (V4L2_STD_ALL & ~V4L2_STD_SECAM)) {
+ val = 0x02;
+ v4l2_dbg(1, debug, sd, "setting value for SDTV format\n");
+ } else {
+ val = 0x00;
+ v4l2_dbg(1, debug, sd, "disabling all channels\n");
+ }
+
+ err |= i2c_smbus_write_byte_data(client, 0x01, val);
+ err |= i2c_smbus_write_byte_data(client, 0x02, val);
+ err |= i2c_smbus_write_byte_data(client, 0x03, val);
+
+ if (err)
+ v4l2_err(sd, "write failed\n");
+
+ return err;
+}
+
+static int ths7303_s_std_output(struct v4l2_subdev *sd, v4l2_std_id norm)
+{
+ return ths7303_setvalue(sd, norm);
+}
+
+static int ths7303_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_THS7303, 0);
+}
+
+static const struct v4l2_subdev_video_ops ths7303_video_ops = {
+ .s_std_output = ths7303_s_std_output,
+};
+
+static const struct v4l2_subdev_core_ops ths7303_core_ops = {
+ .g_chip_ident = ths7303_g_chip_ident,
+};
+
+static const struct v4l2_subdev_ops ths7303_ops = {
+ .core = &ths7303_core_ops,
+ .video = &ths7303_video_ops,
+};
+
+static int ths7303_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct v4l2_subdev *sd;
+ v4l2_std_id std_id = V4L2_STD_NTSC;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ if (sd == NULL)
+ return -ENOMEM;
+
+ v4l2_i2c_subdev_init(sd, client, &ths7303_ops);
+
+ return ths7303_setvalue(sd, std_id);
+}
+
+static int ths7303_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(sd);
+
+ return 0;
+}
+
+static const struct i2c_device_id ths7303_id[] = {
+ {"ths7303", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, ths7303_id);
+
+static struct i2c_driver ths7303_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ths7303",
+ },
+ .probe = ths7303_probe,
+ .remove = ths7303_remove,
+ .id_table = ths7303_id,
+};
+
+module_i2c_driver(ths7303_driver);
diff --git a/drivers/media/video/timblogiw.c b/drivers/media/video/timblogiw.c
new file mode 100644
index 00000000..02194c05
--- /dev/null
+++ b/drivers/media/video/timblogiw.c
@@ -0,0 +1,880 @@
+/*
+ * timblogiw.c timberdale FPGA LogiWin Video In driver
+ * Copyright (c) 2009-2010 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Supports:
+ * Timberdale FPGA LogiWin Video In
+ */
+
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dmaengine.h>
+#include <linux/scatterlist.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf-dma-contig.h>
+#include <media/timb_video.h>
+
+#define DRIVER_NAME "timb-video"
+
+#define TIMBLOGIWIN_NAME "Timberdale Video-In"
+#define TIMBLOGIW_VERSION_CODE 0x04
+
+#define TIMBLOGIW_LINES_PER_DESC 44
+#define TIMBLOGIW_MAX_VIDEO_MEM 16
+
+#define TIMBLOGIW_HAS_DECODER(lw) (lw->pdata.encoder.module_name)
+
+
+struct timblogiw {
+ struct video_device video_dev;
+ struct v4l2_device v4l2_dev; /* mutual exclusion */
+ struct mutex lock;
+ struct device *dev;
+ struct timb_video_platform_data pdata;
+ struct v4l2_subdev *sd_enc; /* encoder */
+ bool opened;
+};
+
+struct timblogiw_tvnorm {
+ v4l2_std_id std;
+ u16 width;
+ u16 height;
+ u8 fps;
+};
+
+struct timblogiw_fh {
+ struct videobuf_queue vb_vidq;
+ struct timblogiw_tvnorm const *cur_norm;
+ struct list_head capture;
+ struct dma_chan *chan;
+ spinlock_t queue_lock; /* mutual exclusion */
+ unsigned int frame_count;
+};
+
+struct timblogiw_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+ struct scatterlist sg[16];
+ dma_cookie_t cookie;
+ struct timblogiw_fh *fh;
+};
+
+const struct timblogiw_tvnorm timblogiw_tvnorms[] = {
+ {
+ .std = V4L2_STD_PAL,
+ .width = 720,
+ .height = 576,
+ .fps = 25
+ },
+ {
+ .std = V4L2_STD_NTSC,
+ .width = 720,
+ .height = 480,
+ .fps = 30
+ }
+};
+
+static int timblogiw_bytes_per_line(const struct timblogiw_tvnorm *norm)
+{
+ return norm->width * 2;
+}
+
+
+static int timblogiw_frame_size(const struct timblogiw_tvnorm *norm)
+{
+ return norm->height * timblogiw_bytes_per_line(norm);
+}
+
+static const struct timblogiw_tvnorm *timblogiw_get_norm(const v4l2_std_id std)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(timblogiw_tvnorms); i++)
+ if (timblogiw_tvnorms[i].std & std)
+ return timblogiw_tvnorms + i;
+
+ /* default to first element */
+ return timblogiw_tvnorms;
+}
+
+static void timblogiw_dma_cb(void *data)
+{
+ struct timblogiw_buffer *buf = data;
+ struct timblogiw_fh *fh = buf->fh;
+ struct videobuf_buffer *vb = &buf->vb;
+
+ spin_lock(&fh->queue_lock);
+
+ /* mark the transfer done */
+ buf->cookie = -1;
+
+ fh->frame_count++;
+
+ if (vb->state != VIDEOBUF_ERROR) {
+ list_del(&vb->queue);
+ do_gettimeofday(&vb->ts);
+ vb->field_count = fh->frame_count * 2;
+ vb->state = VIDEOBUF_DONE;
+
+ wake_up(&vb->done);
+ }
+
+ if (!list_empty(&fh->capture)) {
+ vb = list_entry(fh->capture.next, struct videobuf_buffer,
+ queue);
+ vb->state = VIDEOBUF_ACTIVE;
+ }
+
+ spin_unlock(&fh->queue_lock);
+}
+
+static bool timblogiw_dma_filter_fn(struct dma_chan *chan, void *filter_param)
+{
+ return chan->chan_id == (uintptr_t)filter_param;
+}
+
+/* IOCTL functions */
+
+static int timblogiw_g_fmt(struct file *file, void *priv,
+ struct v4l2_format *format)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw *lw = video_get_drvdata(vdev);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s entry\n", __func__);
+
+ if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ mutex_lock(&lw->lock);
+
+ format->fmt.pix.width = fh->cur_norm->width;
+ format->fmt.pix.height = fh->cur_norm->height;
+ format->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
+ format->fmt.pix.bytesperline = timblogiw_bytes_per_line(fh->cur_norm);
+ format->fmt.pix.sizeimage = timblogiw_frame_size(fh->cur_norm);
+ format->fmt.pix.field = V4L2_FIELD_NONE;
+
+ mutex_unlock(&lw->lock);
+
+ return 0;
+}
+
+static int timblogiw_try_fmt(struct file *file, void *priv,
+ struct v4l2_format *format)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_pix_format *pix = &format->fmt.pix;
+
+ dev_dbg(&vdev->dev,
+ "%s - width=%d, height=%d, pixelformat=%d, field=%d\n"
+ "bytes per line %d, size image: %d, colorspace: %d\n",
+ __func__,
+ pix->width, pix->height, pix->pixelformat, pix->field,
+ pix->bytesperline, pix->sizeimage, pix->colorspace);
+
+ if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (pix->field != V4L2_FIELD_NONE)
+ return -EINVAL;
+
+ if (pix->pixelformat != V4L2_PIX_FMT_UYVY)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int timblogiw_s_fmt(struct file *file, void *priv,
+ struct v4l2_format *format)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw *lw = video_get_drvdata(vdev);
+ struct timblogiw_fh *fh = priv;
+ struct v4l2_pix_format *pix = &format->fmt.pix;
+ int err;
+
+ mutex_lock(&lw->lock);
+
+ err = timblogiw_try_fmt(file, priv, format);
+ if (err)
+ goto out;
+
+ if (videobuf_queue_is_busy(&fh->vb_vidq)) {
+ dev_err(&vdev->dev, "%s queue busy\n", __func__);
+ err = -EBUSY;
+ goto out;
+ }
+
+ pix->width = fh->cur_norm->width;
+ pix->height = fh->cur_norm->height;
+
+out:
+ mutex_unlock(&lw->lock);
+ return err;
+}
+
+static int timblogiw_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ dev_dbg(&vdev->dev, "%s: Entry\n", __func__);
+ memset(cap, 0, sizeof(*cap));
+ strncpy(cap->card, TIMBLOGIWIN_NAME, sizeof(cap->card)-1);
+ strncpy(cap->driver, DRIVER_NAME, sizeof(cap->driver) - 1);
+ strlcpy(cap->bus_info, vdev->name, sizeof(cap->bus_info));
+ cap->version = TIMBLOGIW_VERSION_CODE;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
+ V4L2_CAP_READWRITE;
+
+ return 0;
+}
+
+static int timblogiw_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *fmt)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ dev_dbg(&vdev->dev, "%s, index: %d\n", __func__, fmt->index);
+
+ if (fmt->index != 0)
+ return -EINVAL;
+ memset(fmt, 0, sizeof(*fmt));
+ fmt->index = 0;
+ fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ strncpy(fmt->description, "4:2:2, packed, YUYV",
+ sizeof(fmt->description)-1);
+ fmt->pixelformat = V4L2_PIX_FMT_UYVY;
+
+ return 0;
+}
+
+static int timblogiw_g_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *sp)
+{
+ struct timblogiw_fh *fh = priv;
+ struct v4l2_captureparm *cp = &sp->parm.capture;
+
+ cp->capability = V4L2_CAP_TIMEPERFRAME;
+ cp->timeperframe.numerator = 1;
+ cp->timeperframe.denominator = fh->cur_norm->fps;
+
+ return 0;
+}
+
+static int timblogiw_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *rb)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ return videobuf_reqbufs(&fh->vb_vidq, rb);
+}
+
+static int timblogiw_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ return videobuf_querybuf(&fh->vb_vidq, b);
+}
+
+static int timblogiw_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ return videobuf_qbuf(&fh->vb_vidq, b);
+}
+
+static int timblogiw_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK);
+}
+
+static int timblogiw_g_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ *std = fh->cur_norm->std;
+ return 0;
+}
+
+static int timblogiw_s_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw *lw = video_get_drvdata(vdev);
+ struct timblogiw_fh *fh = priv;
+ int err = 0;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ mutex_lock(&lw->lock);
+
+ if (TIMBLOGIW_HAS_DECODER(lw))
+ err = v4l2_subdev_call(lw->sd_enc, core, s_std, *std);
+
+ if (!err)
+ fh->cur_norm = timblogiw_get_norm(*std);
+
+ mutex_unlock(&lw->lock);
+
+ return err;
+}
+
+static int timblogiw_enuminput(struct file *file, void *priv,
+ struct v4l2_input *inp)
+{
+ struct video_device *vdev = video_devdata(file);
+ int i;
+
+ dev_dbg(&vdev->dev, "%s: Entry\n", __func__);
+
+ if (inp->index != 0)
+ return -EINVAL;
+
+ inp->index = 0;
+
+ strncpy(inp->name, "Timb input 1", sizeof(inp->name) - 1);
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+
+ inp->std = 0;
+ for (i = 0; i < ARRAY_SIZE(timblogiw_tvnorms); i++)
+ inp->std |= timblogiw_tvnorms[i].std;
+
+ return 0;
+}
+
+static int timblogiw_g_input(struct file *file, void *priv,
+ unsigned int *input)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ dev_dbg(&vdev->dev, "%s: Entry\n", __func__);
+
+ *input = 0;
+
+ return 0;
+}
+
+static int timblogiw_s_input(struct file *file, void *priv, unsigned int input)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ dev_dbg(&vdev->dev, "%s: Entry\n", __func__);
+
+ if (input != 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int timblogiw_streamon(struct file *file, void *priv, unsigned int type)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ dev_dbg(&vdev->dev, "%s - No capture device\n", __func__);
+ return -EINVAL;
+ }
+
+ fh->frame_count = 0;
+ return videobuf_streamon(&fh->vb_vidq);
+}
+
+static int timblogiw_streamoff(struct file *file, void *priv,
+ unsigned int type)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s entry\n", __func__);
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ return videobuf_streamoff(&fh->vb_vidq);
+}
+
+static int timblogiw_querystd(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw *lw = video_get_drvdata(vdev);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s entry\n", __func__);
+
+ if (TIMBLOGIW_HAS_DECODER(lw))
+ return v4l2_subdev_call(lw->sd_enc, video, querystd, std);
+ else {
+ *std = fh->cur_norm->std;
+ return 0;
+ }
+}
+
+static int timblogiw_enum_framesizes(struct file *file, void *priv,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = priv;
+
+ dev_dbg(&vdev->dev, "%s - index: %d, format: %d\n", __func__,
+ fsize->index, fsize->pixel_format);
+
+ if ((fsize->index != 0) ||
+ (fsize->pixel_format != V4L2_PIX_FMT_UYVY))
+ return -EINVAL;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = fh->cur_norm->width;
+ fsize->discrete.height = fh->cur_norm->height;
+
+ return 0;
+}
+
+/* Video buffer functions */
+
+static int buffer_setup(struct videobuf_queue *vq, unsigned int *count,
+ unsigned int *size)
+{
+ struct timblogiw_fh *fh = vq->priv_data;
+
+ *size = timblogiw_frame_size(fh->cur_norm);
+
+ if (!*count)
+ *count = 32;
+
+ while (*size * *count > TIMBLOGIW_MAX_VIDEO_MEM * 1024 * 1024)
+ (*count)--;
+
+ return 0;
+}
+
+static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct timblogiw_fh *fh = vq->priv_data;
+ struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer,
+ vb);
+ unsigned int data_size = timblogiw_frame_size(fh->cur_norm);
+ int err = 0;
+
+ if (vb->baddr && vb->bsize < data_size)
+ /* User provided buffer, but it is too small */
+ return -ENOMEM;
+
+ vb->size = data_size;
+ vb->width = fh->cur_norm->width;
+ vb->height = fh->cur_norm->height;
+ vb->field = field;
+
+ if (vb->state == VIDEOBUF_NEEDS_INIT) {
+ int i;
+ unsigned int size;
+ unsigned int bytes_per_desc = TIMBLOGIW_LINES_PER_DESC *
+ timblogiw_bytes_per_line(fh->cur_norm);
+ dma_addr_t addr;
+
+ sg_init_table(buf->sg, ARRAY_SIZE(buf->sg));
+
+ err = videobuf_iolock(vq, vb, NULL);
+ if (err)
+ goto err;
+
+ addr = videobuf_to_dma_contig(vb);
+ for (i = 0, size = 0; size < data_size; i++) {
+ sg_dma_address(buf->sg + i) = addr + size;
+ size += bytes_per_desc;
+ sg_dma_len(buf->sg + i) = (size > data_size) ?
+ (bytes_per_desc - (size - data_size)) :
+ bytes_per_desc;
+ }
+
+ vb->state = VIDEOBUF_PREPARED;
+ buf->cookie = -1;
+ buf->fh = fh;
+ }
+
+ return 0;
+
+err:
+ videobuf_dma_contig_free(vq, vb);
+ vb->state = VIDEOBUF_NEEDS_INIT;
+ return err;
+}
+
+static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct timblogiw_fh *fh = vq->priv_data;
+ struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer,
+ vb);
+ struct dma_async_tx_descriptor *desc;
+ int sg_elems;
+ int bytes_per_desc = TIMBLOGIW_LINES_PER_DESC *
+ timblogiw_bytes_per_line(fh->cur_norm);
+
+ sg_elems = timblogiw_frame_size(fh->cur_norm) / bytes_per_desc;
+ sg_elems +=
+ (timblogiw_frame_size(fh->cur_norm) % bytes_per_desc) ? 1 : 0;
+
+ if (list_empty(&fh->capture))
+ vb->state = VIDEOBUF_ACTIVE;
+ else
+ vb->state = VIDEOBUF_QUEUED;
+
+ list_add_tail(&vb->queue, &fh->capture);
+
+ spin_unlock_irq(&fh->queue_lock);
+
+ desc = dmaengine_prep_slave_sg(fh->chan,
+ buf->sg, sg_elems, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP);
+ if (!desc) {
+ spin_lock_irq(&fh->queue_lock);
+ list_del_init(&vb->queue);
+ vb->state = VIDEOBUF_PREPARED;
+ return;
+ }
+
+ desc->callback_param = buf;
+ desc->callback = timblogiw_dma_cb;
+
+ buf->cookie = desc->tx_submit(desc);
+
+ spin_lock_irq(&fh->queue_lock);
+}
+
+static void buffer_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct timblogiw_fh *fh = vq->priv_data;
+ struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer,
+ vb);
+
+ videobuf_waiton(vq, vb, 0, 0);
+ if (buf->cookie >= 0)
+ dma_sync_wait(fh->chan, buf->cookie);
+
+ videobuf_dma_contig_free(vq, vb);
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static struct videobuf_queue_ops timblogiw_video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/* Device Operations functions */
+
+static int timblogiw_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw *lw = video_get_drvdata(vdev);
+ struct timblogiw_fh *fh;
+ v4l2_std_id std;
+ dma_cap_mask_t mask;
+ int err = 0;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ mutex_lock(&lw->lock);
+ if (lw->opened) {
+ err = -EBUSY;
+ goto out;
+ }
+
+ if (TIMBLOGIW_HAS_DECODER(lw) && !lw->sd_enc) {
+ struct i2c_adapter *adapt;
+
+ /* find the video decoder */
+ adapt = i2c_get_adapter(lw->pdata.i2c_adapter);
+ if (!adapt) {
+ dev_err(&vdev->dev, "No I2C bus #%d\n",
+ lw->pdata.i2c_adapter);
+ err = -ENODEV;
+ goto out;
+ }
+
+ /* now find the encoder */
+ lw->sd_enc = v4l2_i2c_new_subdev_board(&lw->v4l2_dev, adapt,
+ lw->pdata.encoder.info, NULL);
+
+ i2c_put_adapter(adapt);
+
+ if (!lw->sd_enc) {
+ dev_err(&vdev->dev, "Failed to get encoder: %s\n",
+ lw->pdata.encoder.module_name);
+ err = -ENODEV;
+ goto out;
+ }
+ }
+
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (!fh) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ fh->cur_norm = timblogiw_tvnorms;
+ timblogiw_querystd(file, fh, &std);
+ fh->cur_norm = timblogiw_get_norm(std);
+
+ INIT_LIST_HEAD(&fh->capture);
+ spin_lock_init(&fh->queue_lock);
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ dma_cap_set(DMA_PRIVATE, mask);
+
+ /* find the DMA channel */
+ fh->chan = dma_request_channel(mask, timblogiw_dma_filter_fn,
+ (void *)(uintptr_t)lw->pdata.dma_channel);
+ if (!fh->chan) {
+ dev_err(&vdev->dev, "Failed to get DMA channel\n");
+ kfree(fh);
+ err = -ENODEV;
+ goto out;
+ }
+
+ file->private_data = fh;
+ videobuf_queue_dma_contig_init(&fh->vb_vidq,
+ &timblogiw_video_qops, lw->dev, &fh->queue_lock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
+ sizeof(struct timblogiw_buffer), fh, NULL);
+
+ lw->opened = true;
+out:
+ mutex_unlock(&lw->lock);
+
+ return err;
+}
+
+static int timblogiw_close(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw *lw = video_get_drvdata(vdev);
+ struct timblogiw_fh *fh = file->private_data;
+
+ dev_dbg(&vdev->dev, "%s: Entry\n", __func__);
+
+ videobuf_stop(&fh->vb_vidq);
+ videobuf_mmap_free(&fh->vb_vidq);
+
+ dma_release_channel(fh->chan);
+
+ kfree(fh);
+
+ mutex_lock(&lw->lock);
+ lw->opened = false;
+ mutex_unlock(&lw->lock);
+ return 0;
+}
+
+static ssize_t timblogiw_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = file->private_data;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ return videobuf_read_stream(&fh->vb_vidq, data, count, ppos, 0,
+ file->f_flags & O_NONBLOCK);
+}
+
+static unsigned int timblogiw_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = file->private_data;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ return videobuf_poll_stream(file, &fh->vb_vidq, wait);
+}
+
+static int timblogiw_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct timblogiw_fh *fh = file->private_data;
+
+ dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+ return videobuf_mmap_mapper(&fh->vb_vidq, vma);
+}
+
+/* Platform device functions */
+
+static __devinitconst struct v4l2_ioctl_ops timblogiw_ioctl_ops = {
+ .vidioc_querycap = timblogiw_querycap,
+ .vidioc_enum_fmt_vid_cap = timblogiw_enum_fmt,
+ .vidioc_g_fmt_vid_cap = timblogiw_g_fmt,
+ .vidioc_try_fmt_vid_cap = timblogiw_try_fmt,
+ .vidioc_s_fmt_vid_cap = timblogiw_s_fmt,
+ .vidioc_g_parm = timblogiw_g_parm,
+ .vidioc_reqbufs = timblogiw_reqbufs,
+ .vidioc_querybuf = timblogiw_querybuf,
+ .vidioc_qbuf = timblogiw_qbuf,
+ .vidioc_dqbuf = timblogiw_dqbuf,
+ .vidioc_g_std = timblogiw_g_std,
+ .vidioc_s_std = timblogiw_s_std,
+ .vidioc_enum_input = timblogiw_enuminput,
+ .vidioc_g_input = timblogiw_g_input,
+ .vidioc_s_input = timblogiw_s_input,
+ .vidioc_streamon = timblogiw_streamon,
+ .vidioc_streamoff = timblogiw_streamoff,
+ .vidioc_querystd = timblogiw_querystd,
+ .vidioc_enum_framesizes = timblogiw_enum_framesizes,
+};
+
+static __devinitconst struct v4l2_file_operations timblogiw_fops = {
+ .owner = THIS_MODULE,
+ .open = timblogiw_open,
+ .release = timblogiw_close,
+ .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
+ .mmap = timblogiw_mmap,
+ .read = timblogiw_read,
+ .poll = timblogiw_poll,
+};
+
+static __devinitconst struct video_device timblogiw_template = {
+ .name = TIMBLOGIWIN_NAME,
+ .fops = &timblogiw_fops,
+ .ioctl_ops = &timblogiw_ioctl_ops,
+ .release = video_device_release_empty,
+ .minor = -1,
+ .tvnorms = V4L2_STD_PAL | V4L2_STD_NTSC
+};
+
+static int __devinit timblogiw_probe(struct platform_device *pdev)
+{
+ int err;
+ struct timblogiw *lw = NULL;
+ struct timb_video_platform_data *pdata = pdev->dev.platform_data;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "No platform data\n");
+ err = -EINVAL;
+ goto err;
+ }
+
+ if (!pdata->encoder.module_name)
+ dev_info(&pdev->dev, "Running without decoder\n");
+
+ lw = kzalloc(sizeof(*lw), GFP_KERNEL);
+ if (!lw) {
+ err = -ENOMEM;
+ goto err;
+ }
+
+ if (pdev->dev.parent)
+ lw->dev = pdev->dev.parent;
+ else
+ lw->dev = &pdev->dev;
+
+ memcpy(&lw->pdata, pdata, sizeof(lw->pdata));
+
+ mutex_init(&lw->lock);
+
+ lw->video_dev = timblogiw_template;
+
+ strlcpy(lw->v4l2_dev.name, DRIVER_NAME, sizeof(lw->v4l2_dev.name));
+ err = v4l2_device_register(NULL, &lw->v4l2_dev);
+ if (err)
+ goto err_register;
+
+ lw->video_dev.v4l2_dev = &lw->v4l2_dev;
+
+ platform_set_drvdata(pdev, lw);
+ video_set_drvdata(&lw->video_dev, lw);
+
+ err = video_register_device(&lw->video_dev, VFL_TYPE_GRABBER, 0);
+ if (err) {
+ dev_err(&pdev->dev, "Error reg video: %d\n", err);
+ goto err_request;
+ }
+
+
+ return 0;
+
+err_request:
+ platform_set_drvdata(pdev, NULL);
+ v4l2_device_unregister(&lw->v4l2_dev);
+err_register:
+ kfree(lw);
+err:
+ dev_err(&pdev->dev, "Failed to register: %d\n", err);
+
+ return err;
+}
+
+static int __devexit timblogiw_remove(struct platform_device *pdev)
+{
+ struct timblogiw *lw = platform_get_drvdata(pdev);
+
+ video_unregister_device(&lw->video_dev);
+
+ v4l2_device_unregister(&lw->v4l2_dev);
+
+ kfree(lw);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver timblogiw_platform_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = timblogiw_probe,
+ .remove = __devexit_p(timblogiw_remove),
+};
+
+module_platform_driver(timblogiw_platform_driver);
+
+MODULE_DESCRIPTION(TIMBLOGIWIN_NAME);
+MODULE_AUTHOR("Pelagicore AB <info@pelagicore.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:"DRIVER_NAME);
diff --git a/drivers/media/video/tlg2300/Kconfig b/drivers/media/video/tlg2300/Kconfig
new file mode 100644
index 00000000..645d9152
--- /dev/null
+++ b/drivers/media/video/tlg2300/Kconfig
@@ -0,0 +1,16 @@
+config VIDEO_TLG2300
+ tristate "Telegent TLG2300 USB video capture support"
+ depends on VIDEO_DEV && I2C && SND && DVB_CORE
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ depends on RC_CORE
+ select VIDEOBUF_VMALLOC
+ select SND_PCM
+ select VIDEOBUF_DVB
+
+ ---help---
+ This is a video4linux driver for Telegent tlg2300 based TV cards.
+ The driver supports V4L2, DVB-T and radio.
+
+ To compile this driver as a module, choose M here: the
+ module will be called poseidon
diff --git a/drivers/media/video/tlg2300/Makefile b/drivers/media/video/tlg2300/Makefile
new file mode 100644
index 00000000..ea09b9af
--- /dev/null
+++ b/drivers/media/video/tlg2300/Makefile
@@ -0,0 +1,9 @@
+poseidon-objs := pd-video.o pd-alsa.o pd-dvb.o pd-radio.o pd-main.o
+
+obj-$(CONFIG_VIDEO_TLG2300) += poseidon.o
+
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
+
diff --git a/drivers/media/video/tlg2300/pd-alsa.c b/drivers/media/video/tlg2300/pd-alsa.c
new file mode 100644
index 00000000..9f8b7da5
--- /dev/null
+++ b/drivers/media/video/tlg2300/pd-alsa.c
@@ -0,0 +1,332 @@
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/init.h>
+#include <linux/sound.h>
+#include <linux/spinlock.h>
+#include <linux/soundcard.h>
+#include <linux/vmalloc.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <linux/gfp.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <media/v4l2-common.h>
+#include "pd-common.h"
+#include "vendorcmds.h"
+
+static void complete_handler_audio(struct urb *urb);
+#define AUDIO_EP (0x83)
+#define AUDIO_BUF_SIZE (512)
+#define PERIOD_SIZE (1024 * 8)
+#define PERIOD_MIN (4)
+#define PERIOD_MAX PERIOD_MIN
+
+static struct snd_pcm_hardware snd_pd_hw_capture = {
+ .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID,
+
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = PERIOD_SIZE * PERIOD_MIN,
+ .period_bytes_min = PERIOD_SIZE,
+ .period_bytes_max = PERIOD_SIZE,
+ .periods_min = PERIOD_MIN,
+ .periods_max = PERIOD_MAX,
+ /*
+ .buffer_bytes_max = 62720 * 8,
+ .period_bytes_min = 64,
+ .period_bytes_max = 12544,
+ .periods_min = 2,
+ .periods_max = 98
+ */
+};
+
+static int snd_pd_capture_open(struct snd_pcm_substream *substream)
+{
+ struct poseidon *p = snd_pcm_substream_chip(substream);
+ struct poseidon_audio *pa = &p->audio;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ if (!p)
+ return -ENODEV;
+ pa->users++;
+ pa->card_close = 0;
+ pa->capture_pcm_substream = substream;
+ runtime->private_data = p;
+
+ runtime->hw = snd_pd_hw_capture;
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ usb_autopm_get_interface(p->interface);
+ kref_get(&p->kref);
+ return 0;
+}
+
+static int snd_pd_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct poseidon *p = snd_pcm_substream_chip(substream);
+ struct poseidon_audio *pa = &p->audio;
+
+ pa->users--;
+ pa->card_close = 1;
+ usb_autopm_put_interface(p->interface);
+ kref_put(&p->kref, poseidon_delete);
+ return 0;
+}
+
+static int snd_pd_hw_capture_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int size;
+
+ size = params_buffer_bytes(hw_params);
+ if (runtime->dma_area) {
+ if (runtime->dma_bytes > size)
+ return 0;
+ vfree(runtime->dma_area);
+ }
+ runtime->dma_area = vmalloc(size);
+ if (!runtime->dma_area)
+ return -ENOMEM;
+ else
+ runtime->dma_bytes = size;
+ return 0;
+}
+
+static int audio_buf_free(struct poseidon *p)
+{
+ struct poseidon_audio *pa = &p->audio;
+ int i;
+
+ for (i = 0; i < AUDIO_BUFS; i++)
+ if (pa->urb_array[i])
+ usb_kill_urb(pa->urb_array[i]);
+ free_all_urb_generic(pa->urb_array, AUDIO_BUFS);
+ logpm();
+ return 0;
+}
+
+static int snd_pd_hw_capture_free(struct snd_pcm_substream *substream)
+{
+ struct poseidon *p = snd_pcm_substream_chip(substream);
+
+ logpm();
+ audio_buf_free(p);
+ return 0;
+}
+
+static int snd_pd_prepare(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+#define AUDIO_TRAILER_SIZE (16)
+static inline void handle_audio_data(struct urb *urb, int *period_elapsed)
+{
+ struct poseidon_audio *pa = urb->context;
+ struct snd_pcm_runtime *runtime = pa->capture_pcm_substream->runtime;
+
+ int stride = runtime->frame_bits >> 3;
+ int len = urb->actual_length / stride;
+ unsigned char *cp = urb->transfer_buffer;
+ unsigned int oldptr = pa->rcv_position;
+
+ if (urb->actual_length == AUDIO_BUF_SIZE - 4)
+ len -= (AUDIO_TRAILER_SIZE / stride);
+
+ /* do the copy */
+ if (oldptr + len >= runtime->buffer_size) {
+ unsigned int cnt = runtime->buffer_size - oldptr;
+
+ memcpy(runtime->dma_area + oldptr * stride, cp, cnt * stride);
+ memcpy(runtime->dma_area, (cp + cnt * stride),
+ (len * stride - cnt * stride));
+ } else
+ memcpy(runtime->dma_area + oldptr * stride, cp, len * stride);
+
+ /* update the statas */
+ snd_pcm_stream_lock(pa->capture_pcm_substream);
+ pa->rcv_position += len;
+ if (pa->rcv_position >= runtime->buffer_size)
+ pa->rcv_position -= runtime->buffer_size;
+
+ pa->copied_position += (len);
+ if (pa->copied_position >= runtime->period_size) {
+ pa->copied_position -= runtime->period_size;
+ *period_elapsed = 1;
+ }
+ snd_pcm_stream_unlock(pa->capture_pcm_substream);
+}
+
+static void complete_handler_audio(struct urb *urb)
+{
+ struct poseidon_audio *pa = urb->context;
+ struct snd_pcm_substream *substream = pa->capture_pcm_substream;
+ int period_elapsed = 0;
+ int ret;
+
+ if (1 == pa->card_close || pa->capture_stream != STREAM_ON)
+ return;
+
+ if (urb->status != 0) {
+ /*if (urb->status == -ESHUTDOWN)*/
+ return;
+ }
+
+ if (substream) {
+ if (urb->actual_length) {
+ handle_audio_data(urb, &period_elapsed);
+ if (period_elapsed)
+ snd_pcm_period_elapsed(substream);
+ }
+ }
+
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret < 0)
+ log("audio urb failed (errcod = %i)", ret);
+ return;
+}
+
+static int fire_audio_urb(struct poseidon *p)
+{
+ int i, ret = 0;
+ struct poseidon_audio *pa = &p->audio;
+
+ alloc_bulk_urbs_generic(pa->urb_array, AUDIO_BUFS,
+ p->udev, AUDIO_EP,
+ AUDIO_BUF_SIZE, GFP_ATOMIC,
+ complete_handler_audio, pa);
+
+ for (i = 0; i < AUDIO_BUFS; i++) {
+ ret = usb_submit_urb(pa->urb_array[i], GFP_KERNEL);
+ if (ret)
+ log("urb err : %d", ret);
+ }
+ log();
+ return ret;
+}
+
+static int snd_pd_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct poseidon *p = snd_pcm_substream_chip(substream);
+ struct poseidon_audio *pa = &p->audio;
+
+ if (debug_mode)
+ log("cmd %d, audio stat : %d\n", cmd, pa->capture_stream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_START:
+ if (pa->capture_stream == STREAM_ON)
+ return 0;
+
+ pa->rcv_position = pa->copied_position = 0;
+ pa->capture_stream = STREAM_ON;
+
+ if (in_hibernation(p))
+ return 0;
+ fire_audio_urb(p);
+ return 0;
+
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ pa->capture_stream = STREAM_SUSPEND;
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ pa->capture_stream = STREAM_OFF;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static snd_pcm_uframes_t
+snd_pd_capture_pointer(struct snd_pcm_substream *substream)
+{
+ struct poseidon *p = snd_pcm_substream_chip(substream);
+ struct poseidon_audio *pa = &p->audio;
+ return pa->rcv_position;
+}
+
+static struct page *snd_pcm_pd_get_page(struct snd_pcm_substream *subs,
+ unsigned long offset)
+{
+ void *pageptr = subs->runtime->dma_area + offset;
+ return vmalloc_to_page(pageptr);
+}
+
+static struct snd_pcm_ops pcm_capture_ops = {
+ .open = snd_pd_capture_open,
+ .close = snd_pd_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_pd_hw_capture_params,
+ .hw_free = snd_pd_hw_capture_free,
+ .prepare = snd_pd_prepare,
+ .trigger = snd_pd_capture_trigger,
+ .pointer = snd_pd_capture_pointer,
+ .page = snd_pcm_pd_get_page,
+};
+
+#ifdef CONFIG_PM
+int pm_alsa_suspend(struct poseidon *p)
+{
+ logpm(p);
+ audio_buf_free(p);
+ return 0;
+}
+
+int pm_alsa_resume(struct poseidon *p)
+{
+ logpm(p);
+ fire_audio_urb(p);
+ return 0;
+}
+#endif
+
+int poseidon_audio_init(struct poseidon *p)
+{
+ struct poseidon_audio *pa = &p->audio;
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+ int ret;
+
+ ret = snd_card_create(-1, "Telegent", THIS_MODULE, 0, &card);
+ if (ret != 0)
+ return ret;
+
+ ret = snd_pcm_new(card, "poseidon audio", 0, 0, 1, &pcm);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
+ pcm->info_flags = 0;
+ pcm->private_data = p;
+ strcpy(pcm->name, "poseidon audio capture");
+
+ strcpy(card->driver, "ALSA driver");
+ strcpy(card->shortname, "poseidon Audio");
+ strcpy(card->longname, "poseidon ALSA Audio");
+
+ if (snd_card_register(card)) {
+ snd_card_free(card);
+ return -ENOMEM;
+ }
+ pa->card = card;
+ return 0;
+}
+
+int poseidon_audio_free(struct poseidon *p)
+{
+ struct poseidon_audio *pa = &p->audio;
+
+ if (pa->card)
+ snd_card_free(pa->card);
+ return 0;
+}
diff --git a/drivers/media/video/tlg2300/pd-common.h b/drivers/media/video/tlg2300/pd-common.h
new file mode 100644
index 00000000..5dd73b78
--- /dev/null
+++ b/drivers/media/video/tlg2300/pd-common.h
@@ -0,0 +1,281 @@
+#ifndef PD_COMMON_H
+#define PD_COMMON_H
+
+#include <linux/fs.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/videodev2.h>
+#include <linux/semaphore.h>
+#include <linux/usb.h>
+#include <linux/poll.h>
+#include <media/videobuf-vmalloc.h>
+#include <media/v4l2-device.h>
+
+#include "dvb_frontend.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dmxdev.h"
+
+#define SBUF_NUM 8
+#define MAX_BUFFER_NUM 6
+#define PK_PER_URB 32
+#define ISO_PKT_SIZE 3072
+
+#define POSEIDON_STATE_NONE (0x0000)
+#define POSEIDON_STATE_ANALOG (0x0001)
+#define POSEIDON_STATE_FM (0x0002)
+#define POSEIDON_STATE_DVBT (0x0004)
+#define POSEIDON_STATE_VBI (0x0008)
+#define POSEIDON_STATE_DISCONNECT (0x0080)
+
+#define PM_SUSPEND_DELAY 3
+
+#define V4L_PAL_VBI_LINES 18
+#define V4L_NTSC_VBI_LINES 12
+#define V4L_PAL_VBI_FRAMESIZE (V4L_PAL_VBI_LINES * 1440 * 2)
+#define V4L_NTSC_VBI_FRAMESIZE (V4L_NTSC_VBI_LINES * 1440 * 2)
+
+#define TUNER_FREQ_MIN (45000000)
+#define TUNER_FREQ_MAX (862000000)
+
+struct vbi_data {
+ struct video_device *v_dev;
+ struct video_data *video;
+ struct front_face *front;
+
+ unsigned int copied;
+ unsigned int vbi_size; /* the whole size of two fields */
+ int users;
+};
+
+/*
+ * This is the running context of the video, it is useful for
+ * resume()
+ */
+struct running_context {
+ u32 freq; /* VIDIOC_S_FREQUENCY */
+ int audio_idx; /* VIDIOC_S_TUNER */
+ v4l2_std_id tvnormid; /* VIDIOC_S_STD */
+ int sig_index; /* VIDIOC_S_INPUT */
+ struct v4l2_pix_format pix; /* VIDIOC_S_FMT */
+};
+
+struct video_data {
+ /* v4l2 video device */
+ struct video_device *v_dev;
+
+ /* the working context */
+ struct running_context context;
+
+ /* for data copy */
+ int field_count;
+
+ char *dst;
+ int lines_copied;
+ int prev_left;
+
+ int lines_per_field;
+ int lines_size;
+
+ /* for communication */
+ u8 endpoint_addr;
+ struct urb *urb_array[SBUF_NUM];
+ struct vbi_data *vbi;
+ struct poseidon *pd;
+ struct front_face *front;
+
+ int is_streaming;
+ int users;
+
+ /* for bubble handler */
+ struct work_struct bubble_work;
+};
+
+enum pcm_stream_state {
+ STREAM_OFF,
+ STREAM_ON,
+ STREAM_SUSPEND,
+};
+
+#define AUDIO_BUFS (3)
+#define CAPTURE_STREAM_EN 1
+struct poseidon_audio {
+ struct urb *urb_array[AUDIO_BUFS];
+ unsigned int copied_position;
+ struct snd_pcm_substream *capture_pcm_substream;
+
+ unsigned int rcv_position;
+ struct snd_card *card;
+ int card_close;
+
+ int users;
+ int pm_state;
+ enum pcm_stream_state capture_stream;
+};
+
+struct radio_data {
+ __u32 fm_freq;
+ int users;
+ unsigned int is_radio_streaming;
+ int pre_emphasis;
+ struct video_device *fm_dev;
+};
+
+#define DVB_SBUF_NUM 4
+#define DVB_URB_BUF_SIZE 0x2000
+struct pd_dvb_adapter {
+ struct dvb_adapter dvb_adap;
+ struct dvb_frontend dvb_fe;
+ struct dmxdev dmxdev;
+ struct dvb_demux demux;
+
+ atomic_t users;
+ atomic_t active_feed;
+
+ /* data transfer */
+ s32 is_streaming;
+ struct urb *urb_array[DVB_SBUF_NUM];
+ struct poseidon *pd_device;
+ u8 ep_addr;
+ u8 reserved[3];
+
+ /* data for power resume*/
+ struct dtv_frontend_properties fe_param;
+
+ /* for channel scanning */
+ int prev_freq;
+ int bandwidth;
+ unsigned long last_jiffies;
+};
+
+struct front_face {
+ /* use this field to distinguish VIDEO and VBI */
+ enum v4l2_buf_type type;
+
+ /* for host */
+ struct videobuf_queue q;
+
+ /* the bridge for host and device */
+ struct videobuf_buffer *curr_frame;
+
+ /* for device */
+ spinlock_t queue_lock;
+ struct list_head active;
+ struct poseidon *pd;
+};
+
+struct poseidon {
+ struct list_head device_list;
+
+ struct mutex lock;
+ struct kref kref;
+
+ /* for V4L2 */
+ struct v4l2_device v4l2_dev;
+
+ /* hardware info */
+ struct usb_device *udev;
+ struct usb_interface *interface;
+ int cur_transfer_mode;
+
+ struct video_data video_data; /* video */
+ struct vbi_data vbi_data; /* vbi */
+ struct poseidon_audio audio; /* audio (alsa) */
+ struct radio_data radio_data; /* FM */
+ struct pd_dvb_adapter dvb_data; /* DVB */
+
+ u32 state;
+ struct file *file_for_stream; /* the active stream*/
+
+#ifdef CONFIG_PM
+ int (*pm_suspend)(struct poseidon *);
+ int (*pm_resume)(struct poseidon *);
+ pm_message_t msg;
+
+ struct work_struct pm_work;
+ u8 portnum;
+#endif
+};
+
+struct poseidon_format {
+ char *name;
+ int fourcc; /* video4linux 2 */
+ int depth; /* bit/pixel */
+ int flags;
+};
+
+struct poseidon_tvnorm {
+ v4l2_std_id v4l2_id;
+ char name[12];
+ u32 tlg_tvnorm;
+};
+
+/* video */
+int pd_video_init(struct poseidon *);
+void pd_video_exit(struct poseidon *);
+int stop_all_video_stream(struct poseidon *);
+
+/* alsa audio */
+int poseidon_audio_init(struct poseidon *);
+int poseidon_audio_free(struct poseidon *);
+#ifdef CONFIG_PM
+int pm_alsa_suspend(struct poseidon *);
+int pm_alsa_resume(struct poseidon *);
+#endif
+
+/* dvb */
+int pd_dvb_usb_device_init(struct poseidon *);
+void pd_dvb_usb_device_exit(struct poseidon *);
+void pd_dvb_usb_device_cleanup(struct poseidon *);
+int pd_dvb_get_adapter_num(struct pd_dvb_adapter *);
+void dvb_stop_streaming(struct pd_dvb_adapter *);
+
+/* FM */
+int poseidon_fm_init(struct poseidon *);
+int poseidon_fm_exit(struct poseidon *);
+struct video_device *vdev_init(struct poseidon *, struct video_device *);
+
+/* vendor command ops */
+int send_set_req(struct poseidon*, u8, s32, s32*);
+int send_get_req(struct poseidon*, u8, s32, void*, s32*, s32);
+s32 set_tuner_mode(struct poseidon*, unsigned char);
+
+/* bulk urb alloc/free */
+int alloc_bulk_urbs_generic(struct urb **urb_array, int num,
+ struct usb_device *udev, u8 ep_addr,
+ int buf_size, gfp_t gfp_flags,
+ usb_complete_t complete_fn, void *context);
+void free_all_urb_generic(struct urb **urb_array, int num);
+
+/* misc */
+void poseidon_delete(struct kref *kref);
+void destroy_video_device(struct video_device **v_dev);
+extern int debug_mode;
+void set_debug_mode(struct video_device *vfd, int debug_mode);
+
+#ifdef CONFIG_PM
+#define in_hibernation(pd) (pd->msg.event == PM_EVENT_FREEZE)
+#else
+#define in_hibernation(pd) (0)
+#endif
+#define get_pm_count(p) (atomic_read(&(p)->interface->pm_usage_cnt))
+
+#define log(a, ...) printk(KERN_DEBUG "\t[ %s : %.3d ] "a"\n", \
+ __func__, __LINE__, ## __VA_ARGS__)
+
+/* for power management */
+#define logpm(pd) do {\
+ if (debug_mode & 0x10)\
+ log();\
+ } while (0)
+
+#define logs(f) do { \
+ if ((debug_mode & 0x4) && \
+ (f)->type == V4L2_BUF_TYPE_VBI_CAPTURE) \
+ log("type : VBI");\
+ \
+ if ((debug_mode & 0x8) && \
+ (f)->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) \
+ log("type : VIDEO");\
+ } while (0)
+#endif
diff --git a/drivers/media/video/tlg2300/pd-dvb.c b/drivers/media/video/tlg2300/pd-dvb.c
new file mode 100644
index 00000000..30fcb117
--- /dev/null
+++ b/drivers/media/video/tlg2300/pd-dvb.c
@@ -0,0 +1,596 @@
+#include "pd-common.h"
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/dvb/dmx.h>
+#include <linux/delay.h>
+#include <linux/gfp.h>
+
+#include "vendorcmds.h"
+#include <linux/sched.h>
+#include <linux/atomic.h>
+
+static void dvb_urb_cleanup(struct pd_dvb_adapter *pd_dvb);
+
+static int dvb_bandwidth[][2] = {
+ { TLG_BW_8, 8000000 },
+ { TLG_BW_7, 7000000 },
+ { TLG_BW_6, 6000000 }
+};
+static int dvb_bandwidth_length = ARRAY_SIZE(dvb_bandwidth);
+
+static s32 dvb_start_streaming(struct pd_dvb_adapter *pd_dvb);
+static int poseidon_check_mode_dvbt(struct poseidon *pd)
+{
+ s32 ret = 0, cmd_status = 0;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/4);
+
+ ret = usb_set_interface(pd->udev, 0, BULK_ALTERNATE_IFACE);
+ if (ret != 0)
+ return ret;
+
+ ret = set_tuner_mode(pd, TLG_MODE_CAPS_DVB_T);
+ if (ret)
+ return ret;
+
+ /* signal source */
+ ret = send_set_req(pd, SGNL_SRC_SEL, TLG_SIG_SRC_ANTENNA, &cmd_status);
+ if (ret|cmd_status)
+ return ret;
+
+ return 0;
+}
+
+/* acquire :
+ * 1 == open
+ * 0 == release
+ */
+static int poseidon_ts_bus_ctrl(struct dvb_frontend *fe, int acquire)
+{
+ struct poseidon *pd = fe->demodulator_priv;
+ struct pd_dvb_adapter *pd_dvb;
+ int ret = 0;
+
+ if (!pd)
+ return -ENODEV;
+
+ pd_dvb = container_of(fe, struct pd_dvb_adapter, dvb_fe);
+ if (acquire) {
+ mutex_lock(&pd->lock);
+ if (pd->state & POSEIDON_STATE_DISCONNECT) {
+ ret = -ENODEV;
+ goto open_out;
+ }
+
+ if (pd->state && !(pd->state & POSEIDON_STATE_DVBT)) {
+ ret = -EBUSY;
+ goto open_out;
+ }
+
+ usb_autopm_get_interface(pd->interface);
+ if (0 == pd->state) {
+ ret = poseidon_check_mode_dvbt(pd);
+ if (ret < 0) {
+ usb_autopm_put_interface(pd->interface);
+ goto open_out;
+ }
+ pd->state |= POSEIDON_STATE_DVBT;
+ pd_dvb->bandwidth = 0;
+ pd_dvb->prev_freq = 0;
+ }
+ atomic_inc(&pd_dvb->users);
+ kref_get(&pd->kref);
+open_out:
+ mutex_unlock(&pd->lock);
+ } else {
+ dvb_stop_streaming(pd_dvb);
+
+ if (atomic_dec_and_test(&pd_dvb->users)) {
+ mutex_lock(&pd->lock);
+ pd->state &= ~POSEIDON_STATE_DVBT;
+ mutex_unlock(&pd->lock);
+ }
+ kref_put(&pd->kref, poseidon_delete);
+ usb_autopm_put_interface(pd->interface);
+ }
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static void poseidon_fe_release(struct dvb_frontend *fe)
+{
+ struct poseidon *pd = fe->demodulator_priv;
+
+ pd->pm_suspend = NULL;
+ pd->pm_resume = NULL;
+}
+#else
+#define poseidon_fe_release NULL
+#endif
+
+static s32 poseidon_fe_sleep(struct dvb_frontend *fe)
+{
+ return 0;
+}
+
+/*
+ * return true if we can satisfy the conditions, else return false.
+ */
+static bool check_scan_ok(__u32 freq, int bandwidth,
+ struct pd_dvb_adapter *adapter)
+{
+ if (bandwidth < 0)
+ return false;
+
+ if (adapter->prev_freq == freq
+ && adapter->bandwidth == bandwidth) {
+ long nl = jiffies - adapter->last_jiffies;
+ unsigned int msec ;
+
+ msec = jiffies_to_msecs(abs(nl));
+ return msec > 15000 ? true : false;
+ }
+ return true;
+}
+
+/*
+ * Check if the firmware delays too long for an invalid frequency.
+ */
+static int fw_delay_overflow(struct pd_dvb_adapter *adapter)
+{
+ long nl = jiffies - adapter->last_jiffies;
+ unsigned int msec ;
+
+ msec = jiffies_to_msecs(abs(nl));
+ return msec > 800 ? true : false;
+}
+
+static int poseidon_set_fe(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
+ s32 ret = 0, cmd_status = 0;
+ s32 i, bandwidth = -1;
+ struct poseidon *pd = fe->demodulator_priv;
+ struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
+
+ if (in_hibernation(pd))
+ return -EBUSY;
+
+ mutex_lock(&pd->lock);
+ for (i = 0; i < dvb_bandwidth_length; i++)
+ if (fep->bandwidth_hz == dvb_bandwidth[i][1])
+ bandwidth = dvb_bandwidth[i][0];
+
+ if (check_scan_ok(fep->frequency, bandwidth, pd_dvb)) {
+ ret = send_set_req(pd, TUNE_FREQ_SELECT,
+ fep->frequency / 1000, &cmd_status);
+ if (ret | cmd_status) {
+ log("error line");
+ goto front_out;
+ }
+
+ ret = send_set_req(pd, DVBT_BANDW_SEL,
+ bandwidth, &cmd_status);
+ if (ret | cmd_status) {
+ log("error line");
+ goto front_out;
+ }
+
+ ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
+ if (ret | cmd_status) {
+ log("error line");
+ goto front_out;
+ }
+
+ /* save the context for future */
+ memcpy(&pd_dvb->fe_param, fep, sizeof(*fep));
+ pd_dvb->bandwidth = bandwidth;
+ pd_dvb->prev_freq = fep->frequency;
+ pd_dvb->last_jiffies = jiffies;
+ }
+front_out:
+ mutex_unlock(&pd->lock);
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int pm_dvb_suspend(struct poseidon *pd)
+{
+ struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
+ dvb_stop_streaming(pd_dvb);
+ dvb_urb_cleanup(pd_dvb);
+ msleep(500);
+ return 0;
+}
+
+static int pm_dvb_resume(struct poseidon *pd)
+{
+ struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
+
+ poseidon_check_mode_dvbt(pd);
+ msleep(300);
+ poseidon_set_fe(&pd_dvb->dvb_fe);
+
+ dvb_start_streaming(pd_dvb);
+ return 0;
+}
+#endif
+
+static s32 poseidon_fe_init(struct dvb_frontend *fe)
+{
+ struct poseidon *pd = fe->demodulator_priv;
+ struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
+
+#ifdef CONFIG_PM
+ pd->pm_suspend = pm_dvb_suspend;
+ pd->pm_resume = pm_dvb_resume;
+#endif
+ memset(&pd_dvb->fe_param, 0,
+ sizeof(struct dtv_frontend_properties));
+ return 0;
+}
+
+static int poseidon_get_fe(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
+ struct poseidon *pd = fe->demodulator_priv;
+ struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
+
+ memcpy(fep, &pd_dvb->fe_param, sizeof(*fep));
+ return 0;
+}
+
+static int poseidon_fe_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *tune)
+{
+ tune->min_delay_ms = 1000;
+ return 0;
+}
+
+static int poseidon_read_status(struct dvb_frontend *fe, fe_status_t *stat)
+{
+ struct poseidon *pd = fe->demodulator_priv;
+ s32 ret = -1, cmd_status;
+ struct tuner_dtv_sig_stat_s status = {};
+
+ if (in_hibernation(pd))
+ return -EBUSY;
+ mutex_lock(&pd->lock);
+
+ ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_DVB_T,
+ &status, &cmd_status, sizeof(status));
+ if (ret | cmd_status) {
+ log("get tuner status error");
+ goto out;
+ }
+
+ if (debug_mode)
+ log("P : %d, L %d, LB :%d", status.sig_present,
+ status.sig_locked, status.sig_lock_busy);
+
+ if (status.sig_lock_busy) {
+ goto out;
+ } else if (status.sig_present || status.sig_locked) {
+ *stat |= FE_HAS_LOCK | FE_HAS_SIGNAL | FE_HAS_CARRIER
+ | FE_HAS_SYNC | FE_HAS_VITERBI;
+ } else {
+ if (fw_delay_overflow(&pd->dvb_data))
+ *stat |= FE_TIMEDOUT;
+ }
+out:
+ mutex_unlock(&pd->lock);
+ return ret;
+}
+
+static int poseidon_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct poseidon *pd = fe->demodulator_priv;
+ struct tuner_ber_rate_s tlg_ber = {};
+ s32 ret = -1, cmd_status;
+
+ mutex_lock(&pd->lock);
+ ret = send_get_req(pd, TUNER_BER_RATE, 0,
+ &tlg_ber, &cmd_status, sizeof(tlg_ber));
+ if (ret | cmd_status)
+ goto out;
+ *ber = tlg_ber.ber_rate;
+out:
+ mutex_unlock(&pd->lock);
+ return ret;
+}
+
+static s32 poseidon_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct poseidon *pd = fe->demodulator_priv;
+ struct tuner_dtv_sig_stat_s status = {};
+ s32 ret = 0, cmd_status;
+
+ mutex_lock(&pd->lock);
+ ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_DVB_T,
+ &status, &cmd_status, sizeof(status));
+ if (ret | cmd_status)
+ goto out;
+ if ((status.sig_present || status.sig_locked) && !status.sig_strength)
+ *strength = 0xFFFF;
+ else
+ *strength = status.sig_strength;
+out:
+ mutex_unlock(&pd->lock);
+ return ret;
+}
+
+static int poseidon_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ return 0;
+}
+
+static int poseidon_read_unc_blocks(struct dvb_frontend *fe, u32 *unc)
+{
+ *unc = 0;
+ return 0;
+}
+
+static struct dvb_frontend_ops poseidon_frontend_ops = {
+ .delsys = { SYS_DVBT },
+ .info = {
+ .name = "Poseidon DVB-T",
+ .frequency_min = 174000000,
+ .frequency_max = 862000000,
+ .frequency_stepsize = 62500,/* FIXME */
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
+ FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_RECOVER |
+ FE_CAN_HIERARCHY_AUTO,
+ },
+
+ .release = poseidon_fe_release,
+
+ .init = poseidon_fe_init,
+ .sleep = poseidon_fe_sleep,
+
+ .set_frontend = poseidon_set_fe,
+ .get_frontend = poseidon_get_fe,
+ .get_tune_settings = poseidon_fe_get_tune_settings,
+
+ .read_status = poseidon_read_status,
+ .read_ber = poseidon_read_ber,
+ .read_signal_strength = poseidon_read_signal_strength,
+ .read_snr = poseidon_read_snr,
+ .read_ucblocks = poseidon_read_unc_blocks,
+
+ .ts_bus_ctrl = poseidon_ts_bus_ctrl,
+};
+
+static void dvb_urb_irq(struct urb *urb)
+{
+ struct pd_dvb_adapter *pd_dvb = urb->context;
+ int len = urb->transfer_buffer_length;
+ struct dvb_demux *demux = &pd_dvb->demux;
+ s32 ret;
+
+ if (!pd_dvb->is_streaming || urb->status) {
+ if (urb->status == -EPROTO)
+ goto resend;
+ return;
+ }
+
+ if (urb->actual_length == len)
+ dvb_dmx_swfilter(demux, urb->transfer_buffer, len);
+ else if (urb->actual_length == len - 4) {
+ int offset;
+ u8 *buf = urb->transfer_buffer;
+
+ /*
+ * The packet size is 512,
+ * last packet contains 456 bytes tsp data
+ */
+ for (offset = 456; offset < len; offset += 512) {
+ if (!strncmp(buf + offset, "DVHS", 4)) {
+ dvb_dmx_swfilter(demux, buf, offset);
+ if (len > offset + 52 + 4) {
+ /*16 bytes trailer + 36 bytes padding */
+ buf += offset + 52;
+ len -= offset + 52 + 4;
+ dvb_dmx_swfilter(demux, buf, len);
+ }
+ break;
+ }
+ }
+ }
+
+resend:
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret)
+ log(" usb_submit_urb failed: error %d", ret);
+}
+
+static int dvb_urb_init(struct pd_dvb_adapter *pd_dvb)
+{
+ if (pd_dvb->urb_array[0])
+ return 0;
+
+ alloc_bulk_urbs_generic(pd_dvb->urb_array, DVB_SBUF_NUM,
+ pd_dvb->pd_device->udev, pd_dvb->ep_addr,
+ DVB_URB_BUF_SIZE, GFP_KERNEL,
+ dvb_urb_irq, pd_dvb);
+ return 0;
+}
+
+static void dvb_urb_cleanup(struct pd_dvb_adapter *pd_dvb)
+{
+ free_all_urb_generic(pd_dvb->urb_array, DVB_SBUF_NUM);
+}
+
+static s32 dvb_start_streaming(struct pd_dvb_adapter *pd_dvb)
+{
+ struct poseidon *pd = pd_dvb->pd_device;
+ int ret = 0;
+
+ if (pd->state & POSEIDON_STATE_DISCONNECT)
+ return -ENODEV;
+
+ mutex_lock(&pd->lock);
+ if (!pd_dvb->is_streaming) {
+ s32 i, cmd_status = 0;
+ /*
+ * Once upon a time, there was a difficult bug lying here.
+ * ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
+ */
+
+ ret = send_set_req(pd, PLAY_SERVICE, 1, &cmd_status);
+ if (ret | cmd_status)
+ goto out;
+
+ ret = dvb_urb_init(pd_dvb);
+ if (ret < 0)
+ goto out;
+
+ pd_dvb->is_streaming = 1;
+ for (i = 0; i < DVB_SBUF_NUM; i++) {
+ ret = usb_submit_urb(pd_dvb->urb_array[i],
+ GFP_KERNEL);
+ if (ret) {
+ log(" submit urb error %d", ret);
+ goto out;
+ }
+ }
+ }
+out:
+ mutex_unlock(&pd->lock);
+ return ret;
+}
+
+void dvb_stop_streaming(struct pd_dvb_adapter *pd_dvb)
+{
+ struct poseidon *pd = pd_dvb->pd_device;
+
+ mutex_lock(&pd->lock);
+ if (pd_dvb->is_streaming) {
+ s32 i, ret, cmd_status = 0;
+
+ pd_dvb->is_streaming = 0;
+
+ for (i = 0; i < DVB_SBUF_NUM; i++)
+ if (pd_dvb->urb_array[i])
+ usb_kill_urb(pd_dvb->urb_array[i]);
+
+ ret = send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP,
+ &cmd_status);
+ if (ret | cmd_status)
+ log("error");
+ }
+ mutex_unlock(&pd->lock);
+}
+
+static int pd_start_feed(struct dvb_demux_feed *feed)
+{
+ struct pd_dvb_adapter *pd_dvb = feed->demux->priv;
+ int ret = 0;
+
+ if (!pd_dvb)
+ return -1;
+ if (atomic_inc_return(&pd_dvb->active_feed) == 1)
+ ret = dvb_start_streaming(pd_dvb);
+ return ret;
+}
+
+static int pd_stop_feed(struct dvb_demux_feed *feed)
+{
+ struct pd_dvb_adapter *pd_dvb = feed->demux->priv;
+
+ if (!pd_dvb)
+ return -1;
+ if (atomic_dec_and_test(&pd_dvb->active_feed))
+ dvb_stop_streaming(pd_dvb);
+ return 0;
+}
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+int pd_dvb_usb_device_init(struct poseidon *pd)
+{
+ struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
+ struct dvb_demux *dvbdemux;
+ int ret = 0;
+
+ pd_dvb->ep_addr = 0x82;
+ atomic_set(&pd_dvb->users, 0);
+ atomic_set(&pd_dvb->active_feed, 0);
+ pd_dvb->pd_device = pd;
+
+ ret = dvb_register_adapter(&pd_dvb->dvb_adap,
+ "Poseidon dvbt adapter",
+ THIS_MODULE,
+ NULL /* for hibernation correctly*/,
+ adapter_nr);
+ if (ret < 0)
+ goto error1;
+
+ /* register frontend */
+ pd_dvb->dvb_fe.demodulator_priv = pd;
+ memcpy(&pd_dvb->dvb_fe.ops, &poseidon_frontend_ops,
+ sizeof(struct dvb_frontend_ops));
+ ret = dvb_register_frontend(&pd_dvb->dvb_adap, &pd_dvb->dvb_fe);
+ if (ret < 0)
+ goto error2;
+
+ /* register demux device */
+ dvbdemux = &pd_dvb->demux;
+ dvbdemux->dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING;
+ dvbdemux->priv = pd_dvb;
+ dvbdemux->feednum = dvbdemux->filternum = 64;
+ dvbdemux->start_feed = pd_start_feed;
+ dvbdemux->stop_feed = pd_stop_feed;
+ dvbdemux->write_to_decoder = NULL;
+
+ ret = dvb_dmx_init(dvbdemux);
+ if (ret < 0)
+ goto error3;
+
+ pd_dvb->dmxdev.filternum = pd_dvb->demux.filternum;
+ pd_dvb->dmxdev.demux = &pd_dvb->demux.dmx;
+ pd_dvb->dmxdev.capabilities = 0;
+
+ ret = dvb_dmxdev_init(&pd_dvb->dmxdev, &pd_dvb->dvb_adap);
+ if (ret < 0)
+ goto error3;
+ return 0;
+
+error3:
+ dvb_unregister_frontend(&pd_dvb->dvb_fe);
+error2:
+ dvb_unregister_adapter(&pd_dvb->dvb_adap);
+error1:
+ return ret;
+}
+
+void pd_dvb_usb_device_exit(struct poseidon *pd)
+{
+ struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
+
+ while (atomic_read(&pd_dvb->users) != 0
+ || atomic_read(&pd_dvb->active_feed) != 0) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ);
+ }
+ dvb_dmxdev_release(&pd_dvb->dmxdev);
+ dvb_unregister_frontend(&pd_dvb->dvb_fe);
+ dvb_unregister_adapter(&pd_dvb->dvb_adap);
+ pd_dvb_usb_device_cleanup(pd);
+}
+
+void pd_dvb_usb_device_cleanup(struct poseidon *pd)
+{
+ struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
+
+ dvb_urb_cleanup(pd_dvb);
+}
+
+int pd_dvb_get_adapter_num(struct pd_dvb_adapter *pd_dvb)
+{
+ return pd_dvb->dvb_adap.num;
+}
diff --git a/drivers/media/video/tlg2300/pd-main.c b/drivers/media/video/tlg2300/pd-main.c
new file mode 100644
index 00000000..c096b3f7
--- /dev/null
+++ b/drivers/media/video/tlg2300/pd-main.c
@@ -0,0 +1,534 @@
+/*
+ * device driver for Telegent tlg2300 based TV cards
+ *
+ * Author :
+ * Kang Yong <kangyong@telegent.com>
+ * Zhang Xiaobing <xbzhang@telegent.com>
+ * Huang Shijie <zyziii@telegent.com> or <shijie8@gmail.com>
+ *
+ * (c) 2009 Telegent Systems
+ * (c) 2010 Telegent Systems
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <linux/suspend.h>
+#include <linux/usb/quirks.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/firmware.h>
+
+#include "vendorcmds.h"
+#include "pd-common.h"
+
+#define VENDOR_ID 0x1B24
+#define PRODUCT_ID 0x4001
+static struct usb_device_id id_table[] = {
+ { USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID, PRODUCT_ID, 255, 1, 0) },
+ { USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID, PRODUCT_ID, 255, 1, 1) },
+ { },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+int debug_mode;
+module_param(debug_mode, int, 0644);
+MODULE_PARM_DESC(debug_mode, "0 = disable, 1 = enable, 2 = verbose");
+
+static const char *firmware_name = "tlg2300_firmware.bin";
+static struct usb_driver poseidon_driver;
+static LIST_HEAD(pd_device_list);
+
+/*
+ * send set request to USB firmware.
+ */
+s32 send_set_req(struct poseidon *pd, u8 cmdid, s32 param, s32 *cmd_status)
+{
+ s32 ret;
+ s8 data[32] = {};
+ u16 lower_16, upper_16;
+
+ if (pd->state & POSEIDON_STATE_DISCONNECT)
+ return -ENODEV;
+
+ mdelay(30);
+
+ if (param == 0) {
+ upper_16 = lower_16 = 0;
+ } else {
+ /* send 32 bit param as two 16 bit param,little endian */
+ lower_16 = (unsigned short)(param & 0xffff);
+ upper_16 = (unsigned short)((param >> 16) & 0xffff);
+ }
+ ret = usb_control_msg(pd->udev,
+ usb_rcvctrlpipe(pd->udev, 0),
+ REQ_SET_CMD | cmdid,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ lower_16,
+ upper_16,
+ &data,
+ sizeof(*cmd_status),
+ USB_CTRL_GET_TIMEOUT);
+
+ if (!ret) {
+ return -ENXIO;
+ } else {
+ /* 1st 4 bytes into cmd_status */
+ memcpy((char *)cmd_status, &(data[0]), sizeof(*cmd_status));
+ }
+ return 0;
+}
+
+/*
+ * send get request to Poseidon firmware.
+ */
+s32 send_get_req(struct poseidon *pd, u8 cmdid, s32 param,
+ void *buf, s32 *cmd_status, s32 datalen)
+{
+ s32 ret;
+ s8 data[128] = {};
+ u16 lower_16, upper_16;
+
+ if (pd->state & POSEIDON_STATE_DISCONNECT)
+ return -ENODEV;
+
+ mdelay(30);
+ if (param == 0) {
+ upper_16 = lower_16 = 0;
+ } else {
+ /*send 32 bit param as two 16 bit param, little endian */
+ lower_16 = (unsigned short)(param & 0xffff);
+ upper_16 = (unsigned short)((param >> 16) & 0xffff);
+ }
+ ret = usb_control_msg(pd->udev,
+ usb_rcvctrlpipe(pd->udev, 0),
+ REQ_GET_CMD | cmdid,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ lower_16,
+ upper_16,
+ &data,
+ (datalen + sizeof(*cmd_status)),
+ USB_CTRL_GET_TIMEOUT);
+
+ if (ret < 0) {
+ return -ENXIO;
+ } else {
+ /* 1st 4 bytes into cmd_status, remaining data into cmd_data */
+ memcpy((char *)cmd_status, &data[0], sizeof(*cmd_status));
+ memcpy((char *)buf, &data[sizeof(*cmd_status)], datalen);
+ }
+ return 0;
+}
+
+static int pm_notifier_block(struct notifier_block *nb,
+ unsigned long event, void *dummy)
+{
+ struct poseidon *pd = NULL;
+ struct list_head *node, *next;
+
+ switch (event) {
+ case PM_POST_HIBERNATION:
+ list_for_each_safe(node, next, &pd_device_list) {
+ struct usb_device *udev;
+ struct usb_interface *iface;
+ int rc = 0;
+
+ pd = container_of(node, struct poseidon, device_list);
+ udev = pd->udev;
+ iface = pd->interface;
+
+ /* It will cause the system to reload the firmware */
+ rc = usb_lock_device_for_reset(udev, iface);
+ if (rc >= 0) {
+ usb_reset_device(udev);
+ usb_unlock_device(udev);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ log("event :%ld\n", event);
+ return 0;
+}
+
+static struct notifier_block pm_notifer = {
+ .notifier_call = pm_notifier_block,
+};
+
+int set_tuner_mode(struct poseidon *pd, unsigned char mode)
+{
+ s32 ret, cmd_status;
+
+ if (pd->state & POSEIDON_STATE_DISCONNECT)
+ return -ENODEV;
+
+ ret = send_set_req(pd, TUNE_MODE_SELECT, mode, &cmd_status);
+ if (ret || cmd_status)
+ return -ENXIO;
+ return 0;
+}
+
+void poseidon_delete(struct kref *kref)
+{
+ struct poseidon *pd = container_of(kref, struct poseidon, kref);
+
+ if (!pd)
+ return;
+ list_del_init(&pd->device_list);
+
+ pd_dvb_usb_device_cleanup(pd);
+ /* clean_audio_data(&pd->audio_data);*/
+
+ if (pd->udev) {
+ usb_put_dev(pd->udev);
+ pd->udev = NULL;
+ }
+ if (pd->interface) {
+ usb_put_intf(pd->interface);
+ pd->interface = NULL;
+ }
+ kfree(pd);
+ log();
+}
+
+static int firmware_download(struct usb_device *udev)
+{
+ int ret = 0, actual_length;
+ const struct firmware *fw = NULL;
+ void *fwbuf = NULL;
+ size_t fwlength = 0, offset;
+ size_t max_packet_size;
+
+ ret = request_firmware(&fw, firmware_name, &udev->dev);
+ if (ret) {
+ log("download err : %d", ret);
+ return ret;
+ }
+
+ fwlength = fw->size;
+
+ fwbuf = kmemdup(fw->data, fwlength, GFP_KERNEL);
+ if (!fwbuf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ max_packet_size = udev->ep_out[0x1]->desc.wMaxPacketSize;
+ log("\t\t download size : %d", (int)max_packet_size);
+
+ for (offset = 0; offset < fwlength; offset += max_packet_size) {
+ actual_length = 0;
+ ret = usb_bulk_msg(udev,
+ usb_sndbulkpipe(udev, 0x01), /* ep 1 */
+ fwbuf + offset,
+ min(max_packet_size, fwlength - offset),
+ &actual_length,
+ HZ * 10);
+ if (ret)
+ break;
+ }
+ kfree(fwbuf);
+out:
+ release_firmware(fw);
+ return ret;
+}
+
+static inline struct poseidon *get_pd(struct usb_interface *intf)
+{
+ return usb_get_intfdata(intf);
+}
+
+#ifdef CONFIG_PM
+/* one-to-one map : poseidon{} <----> usb_device{}'s port */
+static inline void set_map_flags(struct poseidon *pd, struct usb_device *udev)
+{
+ pd->portnum = udev->portnum;
+}
+
+static inline int get_autopm_ref(struct poseidon *pd)
+{
+ return pd->video_data.users + pd->vbi_data.users + pd->audio.users
+ + atomic_read(&pd->dvb_data.users) + pd->radio_data.users;
+}
+
+/* fixup something for poseidon */
+static inline struct poseidon *fixup(struct poseidon *pd)
+{
+ int count;
+
+ /* old udev and interface have gone, so put back reference . */
+ count = get_autopm_ref(pd);
+ log("count : %d, ref count : %d", count, get_pm_count(pd));
+ while (count--)
+ usb_autopm_put_interface(pd->interface);
+ /*usb_autopm_set_interface(pd->interface); */
+
+ usb_put_dev(pd->udev);
+ usb_put_intf(pd->interface);
+ log("event : %d\n", pd->msg.event);
+ return pd;
+}
+
+static struct poseidon *find_old_poseidon(struct usb_device *udev)
+{
+ struct poseidon *pd;
+
+ list_for_each_entry(pd, &pd_device_list, device_list) {
+ if (pd->portnum == udev->portnum && in_hibernation(pd))
+ return fixup(pd);
+ }
+ return NULL;
+}
+
+/* Is the card working now ? */
+static inline int is_working(struct poseidon *pd)
+{
+ return get_pm_count(pd) > 0;
+}
+
+static int poseidon_suspend(struct usb_interface *intf, pm_message_t msg)
+{
+ struct poseidon *pd = get_pd(intf);
+
+ if (!pd)
+ return 0;
+ if (!is_working(pd)) {
+ if (get_pm_count(pd) <= 0 && !in_hibernation(pd)) {
+ pd->msg.event = PM_EVENT_AUTO_SUSPEND;
+ pd->pm_resume = NULL; /* a good guard */
+ printk(KERN_DEBUG "\n\t+ TLG2300 auto suspend +\n\n");
+ }
+ return 0;
+ }
+ pd->msg = msg; /* save it here */
+ logpm(pd);
+ return pd->pm_suspend ? pd->pm_suspend(pd) : 0;
+}
+
+static int poseidon_resume(struct usb_interface *intf)
+{
+ struct poseidon *pd = get_pd(intf);
+
+ if (!pd)
+ return 0;
+ printk(KERN_DEBUG "\n\t ++ TLG2300 resume ++\n\n");
+
+ if (!is_working(pd)) {
+ if (PM_EVENT_AUTO_SUSPEND == pd->msg.event)
+ pd->msg = PMSG_ON;
+ return 0;
+ }
+ if (in_hibernation(pd)) {
+ logpm(pd);
+ return 0;
+ }
+ logpm(pd);
+ return pd->pm_resume ? pd->pm_resume(pd) : 0;
+}
+
+static void hibernation_resume(struct work_struct *w)
+{
+ struct poseidon *pd = container_of(w, struct poseidon, pm_work);
+ int count;
+
+ pd->msg.event = 0; /* clear it here */
+ pd->state &= ~POSEIDON_STATE_DISCONNECT;
+
+ /* set the new interface's reference */
+ count = get_autopm_ref(pd);
+ while (count--)
+ usb_autopm_get_interface(pd->interface);
+
+ /* resume the context */
+ logpm(pd);
+ if (pd->pm_resume)
+ pd->pm_resume(pd);
+}
+#else /* CONFIG_PM is not enabled: */
+static inline struct poseidon *find_old_poseidon(struct usb_device *udev)
+{
+ return NULL;
+}
+
+static inline void set_map_flags(struct poseidon *pd, struct usb_device *udev)
+{
+}
+#endif
+
+static int check_firmware(struct usb_device *udev, int *down_firmware)
+{
+ void *buf;
+ int ret;
+ struct cmd_firmware_vers_s *cmd_firm;
+
+ buf = kzalloc(sizeof(*cmd_firm) + sizeof(u32), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ ret = usb_control_msg(udev,
+ usb_rcvctrlpipe(udev, 0),
+ REQ_GET_CMD | GET_FW_ID,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0,
+ 0,
+ buf,
+ sizeof(*cmd_firm) + sizeof(u32),
+ USB_CTRL_GET_TIMEOUT);
+ kfree(buf);
+
+ if (ret < 0) {
+ *down_firmware = 1;
+ return firmware_download(udev);
+ }
+ return 0;
+}
+
+static int poseidon_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct poseidon *pd = NULL;
+ int ret = 0;
+ int new_one = 0;
+
+ /* download firmware */
+ check_firmware(udev, &ret);
+ if (ret)
+ return 0;
+
+ /* Do I recovery from the hibernate ? */
+ pd = find_old_poseidon(udev);
+ if (!pd) {
+ pd = kzalloc(sizeof(*pd), GFP_KERNEL);
+ if (!pd)
+ return -ENOMEM;
+ kref_init(&pd->kref);
+ set_map_flags(pd, udev);
+ new_one = 1;
+ }
+
+ pd->udev = usb_get_dev(udev);
+ pd->interface = usb_get_intf(interface);
+ usb_set_intfdata(interface, pd);
+
+ if (new_one) {
+ struct device *dev = &interface->dev;
+
+ logpm(pd);
+ mutex_init(&pd->lock);
+
+ /* register v4l2 device */
+ snprintf(pd->v4l2_dev.name, sizeof(pd->v4l2_dev.name), "%s %s",
+ dev->driver->name, dev_name(dev));
+ ret = v4l2_device_register(NULL, &pd->v4l2_dev);
+
+ /* register devices in directory /dev */
+ ret = pd_video_init(pd);
+ poseidon_audio_init(pd);
+ poseidon_fm_init(pd);
+ pd_dvb_usb_device_init(pd);
+
+ INIT_LIST_HEAD(&pd->device_list);
+ list_add_tail(&pd->device_list, &pd_device_list);
+ }
+
+ device_init_wakeup(&udev->dev, 1);
+#ifdef CONFIG_PM
+ pm_runtime_set_autosuspend_delay(&pd->udev->dev,
+ 1000 * PM_SUSPEND_DELAY);
+ usb_enable_autosuspend(pd->udev);
+
+ if (in_hibernation(pd)) {
+ INIT_WORK(&pd->pm_work, hibernation_resume);
+ schedule_work(&pd->pm_work);
+ }
+#endif
+ return 0;
+}
+
+static void poseidon_disconnect(struct usb_interface *interface)
+{
+ struct poseidon *pd = get_pd(interface);
+
+ if (!pd)
+ return;
+ logpm(pd);
+ if (in_hibernation(pd))
+ return;
+
+ mutex_lock(&pd->lock);
+ pd->state |= POSEIDON_STATE_DISCONNECT;
+ mutex_unlock(&pd->lock);
+
+ /* stop urb transferring */
+ stop_all_video_stream(pd);
+ dvb_stop_streaming(&pd->dvb_data);
+
+ /*unregister v4l2 device */
+ v4l2_device_unregister(&pd->v4l2_dev);
+
+ pd_dvb_usb_device_exit(pd);
+ poseidon_fm_exit(pd);
+
+ poseidon_audio_free(pd);
+ pd_video_exit(pd);
+
+ usb_set_intfdata(interface, NULL);
+ kref_put(&pd->kref, poseidon_delete);
+}
+
+static struct usb_driver poseidon_driver = {
+ .name = "poseidon",
+ .probe = poseidon_probe,
+ .disconnect = poseidon_disconnect,
+ .id_table = id_table,
+#ifdef CONFIG_PM
+ .suspend = poseidon_suspend,
+ .resume = poseidon_resume,
+#endif
+ .supports_autosuspend = 1,
+};
+
+static int __init poseidon_init(void)
+{
+ int ret;
+
+ ret = usb_register(&poseidon_driver);
+ if (ret)
+ return ret;
+ register_pm_notifier(&pm_notifer);
+ return ret;
+}
+
+static void __exit poseidon_exit(void)
+{
+ log();
+ unregister_pm_notifier(&pm_notifer);
+ usb_deregister(&poseidon_driver);
+}
+
+module_init(poseidon_init);
+module_exit(poseidon_exit);
+
+MODULE_AUTHOR("Telegent Systems");
+MODULE_DESCRIPTION("For tlg2300-based USB device ");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.2");
diff --git a/drivers/media/video/tlg2300/pd-radio.c b/drivers/media/video/tlg2300/pd-radio.c
new file mode 100644
index 00000000..4fad1dfb
--- /dev/null
+++ b/drivers/media/video/tlg2300/pd-radio.c
@@ -0,0 +1,421 @@
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/bitmap.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <media/v4l2-dev.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <media/v4l2-ioctl.h>
+#include <linux/sched.h>
+
+#include "pd-common.h"
+#include "vendorcmds.h"
+
+static int set_frequency(struct poseidon *p, __u32 frequency);
+static int poseidon_fm_close(struct file *filp);
+static int poseidon_fm_open(struct file *filp);
+
+#define TUNER_FREQ_MIN_FM 76000000
+#define TUNER_FREQ_MAX_FM 108000000
+
+#define MAX_PREEMPHASIS (V4L2_PREEMPHASIS_75_uS + 1)
+static int preemphasis[MAX_PREEMPHASIS] = {
+ TLG_TUNE_ASTD_NONE, /* V4L2_PREEMPHASIS_DISABLED */
+ TLG_TUNE_ASTD_FM_EUR, /* V4L2_PREEMPHASIS_50_uS */
+ TLG_TUNE_ASTD_FM_US, /* V4L2_PREEMPHASIS_75_uS */
+};
+
+static int poseidon_check_mode_radio(struct poseidon *p)
+{
+ int ret;
+ u32 status;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/2);
+ ret = usb_set_interface(p->udev, 0, BULK_ALTERNATE_IFACE);
+ if (ret < 0)
+ goto out;
+
+ ret = set_tuner_mode(p, TLG_MODE_FM_RADIO);
+ if (ret != 0)
+ goto out;
+
+ ret = send_set_req(p, SGNL_SRC_SEL, TLG_SIG_SRC_ANTENNA, &status);
+ ret = send_set_req(p, TUNER_AUD_ANA_STD,
+ p->radio_data.pre_emphasis, &status);
+ ret |= send_set_req(p, TUNER_AUD_MODE,
+ TLG_TUNE_TVAUDIO_MODE_STEREO, &status);
+ ret |= send_set_req(p, AUDIO_SAMPLE_RATE_SEL,
+ ATV_AUDIO_RATE_48K, &status);
+ ret |= send_set_req(p, TUNE_FREQ_SELECT, TUNER_FREQ_MIN_FM, &status);
+out:
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int pm_fm_suspend(struct poseidon *p)
+{
+ logpm(p);
+ pm_alsa_suspend(p);
+ usb_set_interface(p->udev, 0, 0);
+ msleep(300);
+ return 0;
+}
+
+static int pm_fm_resume(struct poseidon *p)
+{
+ logpm(p);
+ poseidon_check_mode_radio(p);
+ set_frequency(p, p->radio_data.fm_freq);
+ pm_alsa_resume(p);
+ return 0;
+}
+#endif
+
+static int poseidon_fm_open(struct file *filp)
+{
+ struct video_device *vfd = video_devdata(filp);
+ struct poseidon *p = video_get_drvdata(vfd);
+ int ret = 0;
+
+ if (!p)
+ return -1;
+
+ mutex_lock(&p->lock);
+ if (p->state & POSEIDON_STATE_DISCONNECT) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ if (p->state && !(p->state & POSEIDON_STATE_FM)) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ usb_autopm_get_interface(p->interface);
+ if (0 == p->state) {
+ /* default pre-emphasis */
+ if (p->radio_data.pre_emphasis == 0)
+ p->radio_data.pre_emphasis = TLG_TUNE_ASTD_FM_EUR;
+ set_debug_mode(vfd, debug_mode);
+
+ ret = poseidon_check_mode_radio(p);
+ if (ret < 0) {
+ usb_autopm_put_interface(p->interface);
+ goto out;
+ }
+ p->state |= POSEIDON_STATE_FM;
+ }
+ p->radio_data.users++;
+ kref_get(&p->kref);
+ filp->private_data = p;
+out:
+ mutex_unlock(&p->lock);
+ return ret;
+}
+
+static int poseidon_fm_close(struct file *filp)
+{
+ struct poseidon *p = filp->private_data;
+ struct radio_data *fm = &p->radio_data;
+ uint32_t status;
+
+ mutex_lock(&p->lock);
+ fm->users--;
+ if (0 == fm->users)
+ p->state &= ~POSEIDON_STATE_FM;
+
+ if (fm->is_radio_streaming && filp == p->file_for_stream) {
+ fm->is_radio_streaming = 0;
+ send_set_req(p, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP, &status);
+ }
+ usb_autopm_put_interface(p->interface);
+ mutex_unlock(&p->lock);
+
+ kref_put(&p->kref, poseidon_delete);
+ filp->private_data = NULL;
+ return 0;
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+{
+ struct poseidon *p = file->private_data;
+
+ strlcpy(v->driver, "tele-radio", sizeof(v->driver));
+ strlcpy(v->card, "Telegent Poseidon", sizeof(v->card));
+ usb_make_path(p->udev, v->bus_info, sizeof(v->bus_info));
+ v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ return 0;
+}
+
+static const struct v4l2_file_operations poseidon_fm_fops = {
+ .owner = THIS_MODULE,
+ .open = poseidon_fm_open,
+ .release = poseidon_fm_close,
+ .ioctl = video_ioctl2,
+};
+
+static int tlg_fm_vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *vt)
+{
+ struct tuner_fm_sig_stat_s fm_stat = {};
+ int ret, status, count = 5;
+ struct poseidon *p = file->private_data;
+
+ if (vt->index != 0)
+ return -EINVAL;
+
+ vt->type = V4L2_TUNER_RADIO;
+ vt->capability = V4L2_TUNER_CAP_STEREO;
+ vt->rangelow = TUNER_FREQ_MIN_FM / 62500;
+ vt->rangehigh = TUNER_FREQ_MAX_FM / 62500;
+ vt->rxsubchans = V4L2_TUNER_SUB_STEREO;
+ vt->audmode = V4L2_TUNER_MODE_STEREO;
+ vt->signal = 0;
+ vt->afc = 0;
+
+ mutex_lock(&p->lock);
+ ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO,
+ &fm_stat, &status, sizeof(fm_stat));
+
+ while (fm_stat.sig_lock_busy && count-- && !ret) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ);
+
+ ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO,
+ &fm_stat, &status, sizeof(fm_stat));
+ }
+ mutex_unlock(&p->lock);
+
+ if (ret || status) {
+ vt->signal = 0;
+ } else if ((fm_stat.sig_present || fm_stat.sig_locked)
+ && fm_stat.sig_strength == 0) {
+ vt->signal = 0xffff;
+ } else
+ vt->signal = (fm_stat.sig_strength * 255 / 10) << 8;
+
+ return 0;
+}
+
+static int fm_get_freq(struct file *file, void *priv,
+ struct v4l2_frequency *argp)
+{
+ struct poseidon *p = file->private_data;
+
+ argp->frequency = p->radio_data.fm_freq;
+ return 0;
+}
+
+static int set_frequency(struct poseidon *p, __u32 frequency)
+{
+ __u32 freq ;
+ int ret, status;
+
+ mutex_lock(&p->lock);
+
+ ret = send_set_req(p, TUNER_AUD_ANA_STD,
+ p->radio_data.pre_emphasis, &status);
+
+ freq = (frequency * 125) * 500 / 1000;/* kHZ */
+ if (freq < TUNER_FREQ_MIN_FM/1000 || freq > TUNER_FREQ_MAX_FM/1000) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ ret = send_set_req(p, TUNE_FREQ_SELECT, freq, &status);
+ if (ret < 0)
+ goto error ;
+ ret = send_set_req(p, TAKE_REQUEST, 0, &status);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/4);
+ if (!p->radio_data.is_radio_streaming) {
+ ret = send_set_req(p, TAKE_REQUEST, 0, &status);
+ ret = send_set_req(p, PLAY_SERVICE,
+ TLG_TUNE_PLAY_SVC_START, &status);
+ p->radio_data.is_radio_streaming = 1;
+ }
+ p->radio_data.fm_freq = frequency;
+error:
+ mutex_unlock(&p->lock);
+ return ret;
+}
+
+static int fm_set_freq(struct file *file, void *priv,
+ struct v4l2_frequency *argp)
+{
+ struct poseidon *p = file->private_data;
+
+ p->file_for_stream = file;
+#ifdef CONFIG_PM
+ p->pm_suspend = pm_fm_suspend;
+ p->pm_resume = pm_fm_resume;
+#endif
+ return set_frequency(p, argp->frequency);
+}
+
+static int tlg_fm_vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *arg)
+{
+ return 0;
+}
+
+static int tlg_fm_vidioc_g_exts_ctrl(struct file *file, void *fh,
+ struct v4l2_ext_controls *ctrls)
+{
+ struct poseidon *p = file->private_data;
+ int i;
+
+ if (ctrls->ctrl_class != V4L2_CTRL_CLASS_FM_TX)
+ return -EINVAL;
+
+ for (i = 0; i < ctrls->count; i++) {
+ struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+ if (ctrl->id != V4L2_CID_TUNE_PREEMPHASIS)
+ continue;
+
+ if (i < MAX_PREEMPHASIS)
+ ctrl->value = p->radio_data.pre_emphasis;
+ }
+ return 0;
+}
+
+static int tlg_fm_vidioc_s_exts_ctrl(struct file *file, void *fh,
+ struct v4l2_ext_controls *ctrls)
+{
+ int i;
+
+ if (ctrls->ctrl_class != V4L2_CTRL_CLASS_FM_TX)
+ return -EINVAL;
+
+ for (i = 0; i < ctrls->count; i++) {
+ struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+ if (ctrl->id != V4L2_CID_TUNE_PREEMPHASIS)
+ continue;
+
+ if (ctrl->value >= 0 && ctrl->value < MAX_PREEMPHASIS) {
+ struct poseidon *p = file->private_data;
+ int pre_emphasis = preemphasis[ctrl->value];
+ u32 status;
+
+ send_set_req(p, TUNER_AUD_ANA_STD,
+ pre_emphasis, &status);
+ p->radio_data.pre_emphasis = pre_emphasis;
+ }
+ }
+ return 0;
+}
+
+static int tlg_fm_vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ return 0;
+}
+
+static int tlg_fm_vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *ctrl)
+{
+ if (!(ctrl->id & V4L2_CTRL_FLAG_NEXT_CTRL))
+ return -EINVAL;
+
+ ctrl->id &= ~V4L2_CTRL_FLAG_NEXT_CTRL;
+ if (ctrl->id != V4L2_CID_TUNE_PREEMPHASIS) {
+ /* return the next supported control */
+ ctrl->id = V4L2_CID_TUNE_PREEMPHASIS;
+ v4l2_ctrl_query_fill(ctrl, V4L2_PREEMPHASIS_DISABLED,
+ V4L2_PREEMPHASIS_75_uS, 1,
+ V4L2_PREEMPHASIS_50_uS);
+ ctrl->flags = V4L2_CTRL_FLAG_UPDATE;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int tlg_fm_vidioc_querymenu(struct file *file, void *fh,
+ struct v4l2_querymenu *qmenu)
+{
+ return v4l2_ctrl_query_menu(qmenu, NULL, NULL);
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *vt)
+{
+ return vt->index > 0 ? -EINVAL : 0;
+}
+static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *va)
+{
+ return (va->index != 0) ? -EINVAL : 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ a->index = 0;
+ a->mode = 0;
+ a->capability = V4L2_AUDCAP_STEREO;
+ strcpy(a->name, "Radio");
+ return 0;
+}
+
+static int vidioc_s_input(struct file *filp, void *priv, u32 i)
+{
+ return (i != 0) ? -EINVAL : 0;
+}
+
+static int vidioc_g_input(struct file *filp, void *priv, u32 *i)
+{
+ return (*i != 0) ? -EINVAL : 0;
+}
+
+static const struct v4l2_ioctl_ops poseidon_fm_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = tlg_fm_vidioc_queryctrl,
+ .vidioc_querymenu = tlg_fm_vidioc_querymenu,
+ .vidioc_g_ctrl = tlg_fm_vidioc_g_ctrl,
+ .vidioc_s_ctrl = tlg_fm_vidioc_s_ctrl,
+ .vidioc_s_ext_ctrls = tlg_fm_vidioc_s_exts_ctrl,
+ .vidioc_g_ext_ctrls = tlg_fm_vidioc_g_exts_ctrl,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_tuner = tlg_fm_vidioc_g_tuner,
+ .vidioc_g_frequency = fm_get_freq,
+ .vidioc_s_frequency = fm_set_freq,
+};
+
+static struct video_device poseidon_fm_template = {
+ .name = "Telegent-Radio",
+ .fops = &poseidon_fm_fops,
+ .minor = -1,
+ .release = video_device_release,
+ .ioctl_ops = &poseidon_fm_ioctl_ops,
+};
+
+int poseidon_fm_init(struct poseidon *p)
+{
+ struct video_device *fm_dev;
+
+ fm_dev = vdev_init(p, &poseidon_fm_template);
+ if (fm_dev == NULL)
+ return -1;
+
+ if (video_register_device(fm_dev, VFL_TYPE_RADIO, -1) < 0) {
+ video_device_release(fm_dev);
+ return -1;
+ }
+ p->radio_data.fm_dev = fm_dev;
+ return 0;
+}
+
+int poseidon_fm_exit(struct poseidon *p)
+{
+ destroy_video_device(&p->radio_data.fm_dev);
+ return 0;
+}
diff --git a/drivers/media/video/tlg2300/pd-video.c b/drivers/media/video/tlg2300/pd-video.c
new file mode 100644
index 00000000..a794ae62
--- /dev/null
+++ b/drivers/media/video/tlg2300/pd-video.c
@@ -0,0 +1,1669 @@
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/videodev2.h>
+#include <linux/usb.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-dev.h>
+
+#include "pd-common.h"
+#include "vendorcmds.h"
+
+#ifdef CONFIG_PM
+static int pm_video_suspend(struct poseidon *pd);
+static int pm_video_resume(struct poseidon *pd);
+#endif
+static void iso_bubble_handler(struct work_struct *w);
+
+static int usb_transfer_mode;
+module_param(usb_transfer_mode, int, 0644);
+MODULE_PARM_DESC(usb_transfer_mode, "0 = Bulk, 1 = Isochronous");
+
+static const struct poseidon_format poseidon_formats[] = {
+ { "YUV 422", V4L2_PIX_FMT_YUYV, 16, 0},
+ { "RGB565", V4L2_PIX_FMT_RGB565, 16, 0},
+};
+
+static const struct poseidon_tvnorm poseidon_tvnorms[] = {
+ { V4L2_STD_PAL_D, "PAL-D", TLG_TUNE_VSTD_PAL_D },
+ { V4L2_STD_PAL_B, "PAL-B", TLG_TUNE_VSTD_PAL_B },
+ { V4L2_STD_PAL_G, "PAL-G", TLG_TUNE_VSTD_PAL_G },
+ { V4L2_STD_PAL_H, "PAL-H", TLG_TUNE_VSTD_PAL_H },
+ { V4L2_STD_PAL_I, "PAL-I", TLG_TUNE_VSTD_PAL_I },
+ { V4L2_STD_PAL_M, "PAL-M", TLG_TUNE_VSTD_PAL_M },
+ { V4L2_STD_PAL_N, "PAL-N", TLG_TUNE_VSTD_PAL_N_COMBO },
+ { V4L2_STD_PAL_Nc, "PAL-Nc", TLG_TUNE_VSTD_PAL_N_COMBO },
+ { V4L2_STD_NTSC_M, "NTSC-M", TLG_TUNE_VSTD_NTSC_M },
+ { V4L2_STD_NTSC_M_JP, "NTSC-JP", TLG_TUNE_VSTD_NTSC_M_J },
+ { V4L2_STD_SECAM_B, "SECAM-B", TLG_TUNE_VSTD_SECAM_B },
+ { V4L2_STD_SECAM_D, "SECAM-D", TLG_TUNE_VSTD_SECAM_D },
+ { V4L2_STD_SECAM_G, "SECAM-G", TLG_TUNE_VSTD_SECAM_G },
+ { V4L2_STD_SECAM_H, "SECAM-H", TLG_TUNE_VSTD_SECAM_H },
+ { V4L2_STD_SECAM_K, "SECAM-K", TLG_TUNE_VSTD_SECAM_K },
+ { V4L2_STD_SECAM_K1, "SECAM-K1", TLG_TUNE_VSTD_SECAM_K1 },
+ { V4L2_STD_SECAM_L, "SECAM-L", TLG_TUNE_VSTD_SECAM_L },
+ { V4L2_STD_SECAM_LC, "SECAM-LC", TLG_TUNE_VSTD_SECAM_L1 },
+};
+static const unsigned int POSEIDON_TVNORMS = ARRAY_SIZE(poseidon_tvnorms);
+
+struct pd_audio_mode {
+ u32 tlg_audio_mode;
+ u32 v4l2_audio_sub;
+ u32 v4l2_audio_mode;
+};
+
+static const struct pd_audio_mode pd_audio_modes[] = {
+ { TLG_TUNE_TVAUDIO_MODE_MONO, V4L2_TUNER_SUB_MONO,
+ V4L2_TUNER_MODE_MONO },
+ { TLG_TUNE_TVAUDIO_MODE_STEREO, V4L2_TUNER_SUB_STEREO,
+ V4L2_TUNER_MODE_STEREO },
+ { TLG_TUNE_TVAUDIO_MODE_LANG_A, V4L2_TUNER_SUB_LANG1,
+ V4L2_TUNER_MODE_LANG1 },
+ { TLG_TUNE_TVAUDIO_MODE_LANG_B, V4L2_TUNER_SUB_LANG2,
+ V4L2_TUNER_MODE_LANG2 },
+ { TLG_TUNE_TVAUDIO_MODE_LANG_C, V4L2_TUNER_SUB_LANG1,
+ V4L2_TUNER_MODE_LANG1_LANG2 }
+};
+static const unsigned int POSEIDON_AUDIOMODS = ARRAY_SIZE(pd_audio_modes);
+
+struct pd_input {
+ char *name;
+ uint32_t tlg_src;
+};
+
+static const struct pd_input pd_inputs[] = {
+ { "TV Antenna", TLG_SIG_SRC_ANTENNA },
+ { "TV Cable", TLG_SIG_SRC_CABLE },
+ { "TV SVideo", TLG_SIG_SRC_SVIDEO },
+ { "TV Composite", TLG_SIG_SRC_COMPOSITE }
+};
+static const unsigned int POSEIDON_INPUTS = ARRAY_SIZE(pd_inputs);
+
+struct poseidon_control {
+ struct v4l2_queryctrl v4l2_ctrl;
+ enum cmd_custom_param_id vc_id;
+};
+
+static struct poseidon_control controls[] = {
+ {
+ { V4L2_CID_BRIGHTNESS, V4L2_CTRL_TYPE_INTEGER,
+ "brightness", 0, 10000, 1, 100, 0, },
+ CUST_PARM_ID_BRIGHTNESS_CTRL
+ }, {
+ { V4L2_CID_CONTRAST, V4L2_CTRL_TYPE_INTEGER,
+ "contrast", 0, 10000, 1, 100, 0, },
+ CUST_PARM_ID_CONTRAST_CTRL,
+ }, {
+ { V4L2_CID_HUE, V4L2_CTRL_TYPE_INTEGER,
+ "hue", 0, 10000, 1, 100, 0, },
+ CUST_PARM_ID_HUE_CTRL,
+ }, {
+ { V4L2_CID_SATURATION, V4L2_CTRL_TYPE_INTEGER,
+ "saturation", 0, 10000, 1, 100, 0, },
+ CUST_PARM_ID_SATURATION_CTRL,
+ },
+};
+
+struct video_std_to_audio_std {
+ v4l2_std_id video_std;
+ int audio_std;
+};
+
+static const struct video_std_to_audio_std video_to_audio_map[] = {
+ /* country : { 27, 32, 33, 34, 36, 44, 45, 46, 47, 48, 64,
+ 65, 86, 351, 352, 353, 354, 358, 372, 852, 972 } */
+ { (V4L2_STD_PAL_I | V4L2_STD_PAL_B | V4L2_STD_PAL_D |
+ V4L2_STD_SECAM_L | V4L2_STD_SECAM_D), TLG_TUNE_ASTD_NICAM },
+
+ /* country : { 1, 52, 54, 55, 886 } */
+ {V4L2_STD_NTSC_M | V4L2_STD_PAL_N | V4L2_STD_PAL_M, TLG_TUNE_ASTD_BTSC},
+
+ /* country : { 81 } */
+ { V4L2_STD_NTSC_M_JP, TLG_TUNE_ASTD_EIAJ },
+
+ /* other country : TLG_TUNE_ASTD_A2 */
+};
+static const unsigned int map_size = ARRAY_SIZE(video_to_audio_map);
+
+static int get_audio_std(v4l2_std_id v4l2_std)
+{
+ int i = 0;
+
+ for (; i < map_size; i++) {
+ if (v4l2_std & video_to_audio_map[i].video_std)
+ return video_to_audio_map[i].audio_std;
+ }
+ return TLG_TUNE_ASTD_A2;
+}
+
+static int vidioc_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct front_face *front = fh;
+ struct poseidon *p = front->pd;
+
+ logs(front);
+
+ strcpy(cap->driver, "tele-video");
+ strcpy(cap->card, "Telegent Poseidon");
+ usb_make_path(p->udev, cap->bus_info, sizeof(cap->bus_info));
+ cap->version = KERNEL_VERSION(0, 0, 1);
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER |
+ V4L2_CAP_AUDIO | V4L2_CAP_STREAMING |
+ V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE;
+ return 0;
+}
+
+/*====================================================================*/
+static void init_copy(struct video_data *video, bool index)
+{
+ struct front_face *front = video->front;
+
+ video->field_count = index;
+ video->lines_copied = 0;
+ video->prev_left = 0 ;
+ video->dst = (char *)videobuf_to_vmalloc(front->curr_frame)
+ + index * video->lines_size;
+ video->vbi->copied = 0; /* set it here */
+}
+
+static bool get_frame(struct front_face *front, int *need_init)
+{
+ struct videobuf_buffer *vb = front->curr_frame;
+
+ if (vb)
+ return true;
+
+ spin_lock(&front->queue_lock);
+ if (!list_empty(&front->active)) {
+ vb = list_entry(front->active.next,
+ struct videobuf_buffer, queue);
+ if (need_init)
+ *need_init = 1;
+ front->curr_frame = vb;
+ list_del_init(&vb->queue);
+ }
+ spin_unlock(&front->queue_lock);
+
+ return !!vb;
+}
+
+/* check if the video's buffer is ready */
+static bool get_video_frame(struct front_face *front, struct video_data *video)
+{
+ int need_init = 0;
+ bool ret = true;
+
+ ret = get_frame(front, &need_init);
+ if (ret && need_init)
+ init_copy(video, 0);
+ return ret;
+}
+
+static void submit_frame(struct front_face *front)
+{
+ struct videobuf_buffer *vb = front->curr_frame;
+
+ if (vb == NULL)
+ return;
+
+ front->curr_frame = NULL;
+ vb->state = VIDEOBUF_DONE;
+ vb->field_count++;
+ do_gettimeofday(&vb->ts);
+
+ wake_up(&vb->done);
+}
+
+/*
+ * A frame is composed of two fields. If we receive all the two fields,
+ * call the submit_frame() to submit the whole frame to applications.
+ */
+static void end_field(struct video_data *video)
+{
+ /* logs(video->front); */
+ if (1 == video->field_count)
+ submit_frame(video->front);
+ else
+ init_copy(video, 1);
+}
+
+static void copy_video_data(struct video_data *video, char *src,
+ unsigned int count)
+{
+#define copy_data(len) \
+ do { \
+ if (++video->lines_copied > video->lines_per_field) \
+ goto overflow; \
+ memcpy(video->dst, src, len);\
+ video->dst += len + video->lines_size; \
+ src += len; \
+ count -= len; \
+ } while (0)
+
+ while (count && count >= video->lines_size) {
+ if (video->prev_left) {
+ copy_data(video->prev_left);
+ video->prev_left = 0;
+ continue;
+ }
+ copy_data(video->lines_size);
+ }
+ if (count && count < video->lines_size) {
+ memcpy(video->dst, src, count);
+
+ video->prev_left = video->lines_size - count;
+ video->dst += count;
+ }
+ return;
+
+overflow:
+ end_field(video);
+}
+
+static void check_trailer(struct video_data *video, char *src, int count)
+{
+ struct vbi_data *vbi = video->vbi;
+ int offset; /* trailer's offset */
+ char *buf;
+
+ offset = (video->context.pix.sizeimage / 2 + vbi->vbi_size / 2)
+ - (vbi->copied + video->lines_size * video->lines_copied);
+ if (video->prev_left)
+ offset -= (video->lines_size - video->prev_left);
+
+ if (offset > count || offset <= 0)
+ goto short_package;
+
+ buf = src + offset;
+
+ /* trailer : (VFHS) + U32 + U32 + field_num */
+ if (!strncmp(buf, "VFHS", 4)) {
+ int field_num = *((u32 *)(buf + 12));
+
+ if ((field_num & 1) ^ video->field_count) {
+ init_copy(video, video->field_count);
+ return;
+ }
+ copy_video_data(video, src, offset);
+ }
+short_package:
+ end_field(video);
+}
+
+/* ========== Check this more carefully! =========== */
+static inline void copy_vbi_data(struct vbi_data *vbi,
+ char *src, unsigned int count)
+{
+ struct front_face *front = vbi->front;
+
+ if (front && get_frame(front, NULL)) {
+ char *buf = videobuf_to_vmalloc(front->curr_frame);
+
+ if (vbi->video->field_count)
+ buf += (vbi->vbi_size / 2);
+ memcpy(buf + vbi->copied, src, count);
+ }
+ vbi->copied += count;
+}
+
+/*
+ * Copy the normal data (VBI or VIDEO) without the trailer.
+ * VBI is not interlaced, while VIDEO is interlaced.
+ */
+static inline void copy_vbi_video_data(struct video_data *video,
+ char *src, unsigned int count)
+{
+ struct vbi_data *vbi = video->vbi;
+ unsigned int vbi_delta = (vbi->vbi_size / 2) - vbi->copied;
+
+ if (vbi_delta >= count) {
+ copy_vbi_data(vbi, src, count);
+ } else {
+ if (vbi_delta) {
+ copy_vbi_data(vbi, src, vbi_delta);
+
+ /* we receive the two fields of the VBI*/
+ if (vbi->front && video->field_count)
+ submit_frame(vbi->front);
+ }
+ copy_video_data(video, src + vbi_delta, count - vbi_delta);
+ }
+}
+
+static void urb_complete_bulk(struct urb *urb)
+{
+ struct front_face *front = urb->context;
+ struct video_data *video = &front->pd->video_data;
+ char *src = (char *)urb->transfer_buffer;
+ int count = urb->actual_length;
+ int ret = 0;
+
+ if (!video->is_streaming || urb->status) {
+ if (urb->status == -EPROTO)
+ goto resend_it;
+ return;
+ }
+ if (!get_video_frame(front, video))
+ goto resend_it;
+
+ if (count == urb->transfer_buffer_length)
+ copy_vbi_video_data(video, src, count);
+ else
+ check_trailer(video, src, count);
+
+resend_it:
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret)
+ log(" submit failed: error %d", ret);
+}
+
+/************************* for ISO *********************/
+#define GET_SUCCESS (0)
+#define GET_TRAILER (1)
+#define GET_TOO_MUCH_BUBBLE (2)
+#define GET_NONE (3)
+static int get_chunk(int start, struct urb *urb,
+ int *head, int *tail, int *bubble_err)
+{
+ struct usb_iso_packet_descriptor *pkt = NULL;
+ int ret = GET_SUCCESS;
+
+ for (*head = *tail = -1; start < urb->number_of_packets; start++) {
+ pkt = &urb->iso_frame_desc[start];
+
+ /* handle the bubble of the Hub */
+ if (-EOVERFLOW == pkt->status) {
+ if (++*bubble_err > urb->number_of_packets / 3)
+ return GET_TOO_MUCH_BUBBLE;
+ continue;
+ }
+
+ /* This is the gap */
+ if (pkt->status || pkt->actual_length <= 0
+ || pkt->actual_length > ISO_PKT_SIZE) {
+ if (*head != -1)
+ break;
+ continue;
+ }
+
+ /* a good isochronous packet */
+ if (pkt->actual_length == ISO_PKT_SIZE) {
+ if (*head == -1)
+ *head = start;
+ *tail = start;
+ continue;
+ }
+
+ /* trailer is here */
+ if (pkt->actual_length < ISO_PKT_SIZE) {
+ if (*head == -1) {
+ *head = start;
+ *tail = start;
+ return GET_TRAILER;
+ }
+ break;
+ }
+ }
+
+ if (*head == -1 && *tail == -1)
+ ret = GET_NONE;
+ return ret;
+}
+
+/*
+ * |__|------|___|-----|_______|
+ * ^ ^
+ * | |
+ * gap gap
+ */
+static void urb_complete_iso(struct urb *urb)
+{
+ struct front_face *front = urb->context;
+ struct video_data *video = &front->pd->video_data;
+ int bubble_err = 0, head = 0, tail = 0;
+ char *src = (char *)urb->transfer_buffer;
+ int ret = 0;
+
+ if (!video->is_streaming)
+ return;
+
+ do {
+ if (!get_video_frame(front, video))
+ goto out;
+
+ switch (get_chunk(head, urb, &head, &tail, &bubble_err)) {
+ case GET_SUCCESS:
+ copy_vbi_video_data(video, src + (head * ISO_PKT_SIZE),
+ (tail - head + 1) * ISO_PKT_SIZE);
+ break;
+ case GET_TRAILER:
+ check_trailer(video, src + (head * ISO_PKT_SIZE),
+ ISO_PKT_SIZE);
+ break;
+ case GET_NONE:
+ goto out;
+ case GET_TOO_MUCH_BUBBLE:
+ log("\t We got too much bubble");
+ schedule_work(&video->bubble_work);
+ return;
+ }
+ } while (head = tail + 1, head < urb->number_of_packets);
+
+out:
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret)
+ log("usb_submit_urb err : %d", ret);
+}
+/*============================= [ end ] =====================*/
+
+static int prepare_iso_urb(struct video_data *video)
+{
+ struct usb_device *udev = video->pd->udev;
+ int i;
+
+ if (video->urb_array[0])
+ return 0;
+
+ for (i = 0; i < SBUF_NUM; i++) {
+ struct urb *urb;
+ void *mem;
+ int j;
+
+ urb = usb_alloc_urb(PK_PER_URB, GFP_KERNEL);
+ if (urb == NULL)
+ goto out;
+
+ video->urb_array[i] = urb;
+ mem = usb_alloc_coherent(udev,
+ ISO_PKT_SIZE * PK_PER_URB,
+ GFP_KERNEL,
+ &urb->transfer_dma);
+
+ urb->complete = urb_complete_iso; /* handler */
+ urb->dev = udev;
+ urb->context = video->front;
+ urb->pipe = usb_rcvisocpipe(udev,
+ video->endpoint_addr);
+ urb->interval = 1;
+ urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+ urb->number_of_packets = PK_PER_URB;
+ urb->transfer_buffer = mem;
+ urb->transfer_buffer_length = PK_PER_URB * ISO_PKT_SIZE;
+
+ for (j = 0; j < PK_PER_URB; j++) {
+ urb->iso_frame_desc[j].offset = ISO_PKT_SIZE * j;
+ urb->iso_frame_desc[j].length = ISO_PKT_SIZE;
+ }
+ }
+ return 0;
+out:
+ for (; i > 0; i--)
+ ;
+ return -ENOMEM;
+}
+
+/* return the succeeded number of the allocation */
+int alloc_bulk_urbs_generic(struct urb **urb_array, int num,
+ struct usb_device *udev, u8 ep_addr,
+ int buf_size, gfp_t gfp_flags,
+ usb_complete_t complete_fn, void *context)
+{
+ int i = 0;
+
+ for (; i < num; i++) {
+ void *mem;
+ struct urb *urb = usb_alloc_urb(0, gfp_flags);
+ if (urb == NULL)
+ return i;
+
+ mem = usb_alloc_coherent(udev, buf_size, gfp_flags,
+ &urb->transfer_dma);
+ if (mem == NULL) {
+ usb_free_urb(urb);
+ return i;
+ }
+
+ usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, ep_addr),
+ mem, buf_size, complete_fn, context);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ urb_array[i] = urb;
+ }
+ return i;
+}
+
+void free_all_urb_generic(struct urb **urb_array, int num)
+{
+ int i;
+ struct urb *urb;
+
+ for (i = 0; i < num; i++) {
+ urb = urb_array[i];
+ if (urb) {
+ usb_free_coherent(urb->dev,
+ urb->transfer_buffer_length,
+ urb->transfer_buffer,
+ urb->transfer_dma);
+ usb_free_urb(urb);
+ urb_array[i] = NULL;
+ }
+ }
+}
+
+static int prepare_bulk_urb(struct video_data *video)
+{
+ if (video->urb_array[0])
+ return 0;
+
+ alloc_bulk_urbs_generic(video->urb_array, SBUF_NUM,
+ video->pd->udev, video->endpoint_addr,
+ 0x2000, GFP_KERNEL,
+ urb_complete_bulk, video->front);
+ return 0;
+}
+
+/* free the URBs */
+static void free_all_urb(struct video_data *video)
+{
+ free_all_urb_generic(video->urb_array, SBUF_NUM);
+}
+
+static void pd_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ videobuf_vmalloc_free(vb);
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static void pd_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct front_face *front = q->priv_data;
+ vb->state = VIDEOBUF_QUEUED;
+ list_add_tail(&vb->queue, &front->active);
+}
+
+static int pd_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct front_face *front = q->priv_data;
+ int rc;
+
+ switch (front->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (VIDEOBUF_NEEDS_INIT == vb->state) {
+ struct v4l2_pix_format *pix;
+
+ pix = &front->pd->video_data.context.pix;
+ vb->size = pix->sizeimage; /* real frame size */
+ vb->width = pix->width;
+ vb->height = pix->height;
+ rc = videobuf_iolock(q, vb, NULL);
+ if (rc < 0)
+ return rc;
+ }
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ if (VIDEOBUF_NEEDS_INIT == vb->state) {
+ vb->size = front->pd->vbi_data.vbi_size;
+ rc = videobuf_iolock(q, vb, NULL);
+ if (rc < 0)
+ return rc;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ vb->field = field;
+ vb->state = VIDEOBUF_PREPARED;
+ return 0;
+}
+
+static int fire_all_urb(struct video_data *video)
+{
+ int i, ret;
+
+ video->is_streaming = 1;
+
+ for (i = 0; i < SBUF_NUM; i++) {
+ ret = usb_submit_urb(video->urb_array[i], GFP_KERNEL);
+ if (ret)
+ log("(%d) failed: error %d", i, ret);
+ }
+ return ret;
+}
+
+static int start_video_stream(struct poseidon *pd)
+{
+ struct video_data *video = &pd->video_data;
+ s32 cmd_status;
+
+ send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
+ send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_START, &cmd_status);
+
+ if (pd->cur_transfer_mode) {
+ prepare_iso_urb(video);
+ INIT_WORK(&video->bubble_work, iso_bubble_handler);
+ } else {
+ /* The bulk mode does not need a bubble handler */
+ prepare_bulk_urb(video);
+ }
+ fire_all_urb(video);
+ return 0;
+}
+
+static int pd_buf_setup(struct videobuf_queue *q, unsigned int *count,
+ unsigned int *size)
+{
+ struct front_face *front = q->priv_data;
+ struct poseidon *pd = front->pd;
+
+ switch (front->type) {
+ default:
+ return -EINVAL;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+ struct video_data *video = &pd->video_data;
+ struct v4l2_pix_format *pix = &video->context.pix;
+
+ *size = PAGE_ALIGN(pix->sizeimage);/* page aligned frame size */
+ if (*count < 4)
+ *count = 4;
+ if (1) {
+ /* same in different altersetting */
+ video->endpoint_addr = 0x82;
+ video->vbi = &pd->vbi_data;
+ video->vbi->video = video;
+ video->pd = pd;
+ video->lines_per_field = pix->height / 2;
+ video->lines_size = pix->width * 2;
+ video->front = front;
+ }
+ return start_video_stream(pd);
+ }
+
+ case V4L2_BUF_TYPE_VBI_CAPTURE: {
+ struct vbi_data *vbi = &pd->vbi_data;
+
+ *size = PAGE_ALIGN(vbi->vbi_size);
+ log("size : %d", *size);
+ if (*count == 0)
+ *count = 4;
+ }
+ break;
+ }
+ return 0;
+}
+
+static struct videobuf_queue_ops pd_video_qops = {
+ .buf_setup = pd_buf_setup,
+ .buf_prepare = pd_buf_prepare,
+ .buf_queue = pd_buf_queue,
+ .buf_release = pd_buf_release,
+};
+
+static int vidioc_enum_fmt(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ if (ARRAY_SIZE(poseidon_formats) <= f->index)
+ return -EINVAL;
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ f->flags = 0;
+ f->pixelformat = poseidon_formats[f->index].fourcc;
+ strcpy(f->description, poseidon_formats[f->index].name);
+ return 0;
+}
+
+static int vidioc_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+
+ logs(front);
+ f->fmt.pix = pd->video_data.context.pix;
+ return 0;
+}
+
+static int vidioc_try_fmt(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ return 0;
+}
+
+/*
+ * VLC calls VIDIOC_S_STD before VIDIOC_S_FMT, while
+ * Mplayer calls them in the reverse order.
+ */
+static int pd_vidioc_s_fmt(struct poseidon *pd, struct v4l2_pix_format *pix)
+{
+ struct video_data *video = &pd->video_data;
+ struct running_context *context = &video->context;
+ struct v4l2_pix_format *pix_def = &context->pix;
+ s32 ret = 0, cmd_status = 0, vid_resol;
+
+ /* set the pixel format to firmware */
+ if (pix->pixelformat == V4L2_PIX_FMT_RGB565) {
+ vid_resol = TLG_TUNER_VID_FORMAT_RGB_565;
+ } else {
+ pix->pixelformat = V4L2_PIX_FMT_YUYV;
+ vid_resol = TLG_TUNER_VID_FORMAT_YUV;
+ }
+ ret = send_set_req(pd, VIDEO_STREAM_FMT_SEL,
+ vid_resol, &cmd_status);
+
+ /* set the resolution to firmware */
+ vid_resol = TLG_TUNE_VID_RES_720;
+ switch (pix->width) {
+ case 704:
+ vid_resol = TLG_TUNE_VID_RES_704;
+ break;
+ default:
+ pix->width = 720;
+ case 720:
+ break;
+ }
+ ret |= send_set_req(pd, VIDEO_ROSOLU_SEL,
+ vid_resol, &cmd_status);
+ if (ret || cmd_status)
+ return -EBUSY;
+
+ pix_def->pixelformat = pix->pixelformat; /* save it */
+ pix->height = (context->tvnormid & V4L2_STD_525_60) ? 480 : 576;
+
+ /* Compare with the default setting */
+ if ((pix_def->width != pix->width)
+ || (pix_def->height != pix->height)) {
+ pix_def->width = pix->width;
+ pix_def->height = pix->height;
+ pix_def->bytesperline = pix->width * 2;
+ pix_def->sizeimage = pix->width * pix->height * 2;
+ }
+ *pix = *pix_def;
+
+ return 0;
+}
+
+static int vidioc_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+
+ logs(front);
+ /* stop VBI here */
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != f->type)
+ return -EINVAL;
+
+ mutex_lock(&pd->lock);
+ if (pd->file_for_stream == NULL)
+ pd->file_for_stream = file;
+ else if (file != pd->file_for_stream) {
+ mutex_unlock(&pd->lock);
+ return -EINVAL;
+ }
+
+ pd_vidioc_s_fmt(pd, &f->fmt.pix);
+ mutex_unlock(&pd->lock);
+ return 0;
+}
+
+static int vidioc_g_fmt_vbi(struct file *file, void *fh,
+ struct v4l2_format *v4l2_f)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+ struct v4l2_vbi_format *vbi_fmt = &v4l2_f->fmt.vbi;
+
+ vbi_fmt->samples_per_line = 720 * 2;
+ vbi_fmt->sampling_rate = 6750000 * 4;
+ vbi_fmt->sample_format = V4L2_PIX_FMT_GREY;
+ vbi_fmt->offset = 64 * 4; /*FIXME: why offset */
+ if (pd->video_data.context.tvnormid & V4L2_STD_525_60) {
+ vbi_fmt->start[0] = 10;
+ vbi_fmt->start[1] = 264;
+ vbi_fmt->count[0] = V4L_NTSC_VBI_LINES;
+ vbi_fmt->count[1] = V4L_NTSC_VBI_LINES;
+ } else {
+ vbi_fmt->start[0] = 6;
+ vbi_fmt->start[1] = 314;
+ vbi_fmt->count[0] = V4L_PAL_VBI_LINES;
+ vbi_fmt->count[1] = V4L_PAL_VBI_LINES;
+ }
+ vbi_fmt->flags = V4L2_VBI_UNSYNC;
+ logs(front);
+ return 0;
+}
+
+static int set_std(struct poseidon *pd, v4l2_std_id *norm)
+{
+ struct video_data *video = &pd->video_data;
+ struct vbi_data *vbi = &pd->vbi_data;
+ struct running_context *context;
+ struct v4l2_pix_format *pix;
+ s32 i, ret = 0, cmd_status, param;
+ int height;
+
+ for (i = 0; i < POSEIDON_TVNORMS; i++) {
+ if (*norm & poseidon_tvnorms[i].v4l2_id) {
+ param = poseidon_tvnorms[i].tlg_tvnorm;
+ log("name : %s", poseidon_tvnorms[i].name);
+ goto found;
+ }
+ }
+ return -EINVAL;
+found:
+ mutex_lock(&pd->lock);
+ ret = send_set_req(pd, VIDEO_STD_SEL, param, &cmd_status);
+ if (ret || cmd_status)
+ goto out;
+
+ /* Set vbi size and check the height of the frame */
+ context = &video->context;
+ context->tvnormid = poseidon_tvnorms[i].v4l2_id;
+ if (context->tvnormid & V4L2_STD_525_60) {
+ vbi->vbi_size = V4L_NTSC_VBI_FRAMESIZE;
+ height = 480;
+ } else {
+ vbi->vbi_size = V4L_PAL_VBI_FRAMESIZE;
+ height = 576;
+ }
+
+ pix = &context->pix;
+ if (pix->height != height) {
+ pix->height = height;
+ pix->sizeimage = pix->width * pix->height * 2;
+ }
+
+out:
+ mutex_unlock(&pd->lock);
+ return ret;
+}
+
+static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
+ struct front_face *front = fh;
+ logs(front);
+ return set_std(front->pd, norm);
+}
+
+static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *in)
+{
+ struct front_face *front = fh;
+
+ if (in->index < 0 || in->index >= POSEIDON_INPUTS)
+ return -EINVAL;
+ strcpy(in->name, pd_inputs[in->index].name);
+ in->type = V4L2_INPUT_TYPE_TUNER;
+
+ /*
+ * the audio input index mixed with this video input,
+ * Poseidon only have one audio/video, set to "0"
+ */
+ in->audioset = 0;
+ in->tuner = 0;
+ in->std = V4L2_STD_ALL;
+ in->status = 0;
+ logs(front);
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+ struct running_context *context = &pd->video_data.context;
+
+ logs(front);
+ *i = context->sig_index;
+ return 0;
+}
+
+/* We can support several inputs */
+static int vidioc_s_input(struct file *file, void *fh, unsigned int i)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+ s32 ret, cmd_status;
+
+ if (i < 0 || i >= POSEIDON_INPUTS)
+ return -EINVAL;
+ ret = send_set_req(pd, SGNL_SRC_SEL,
+ pd_inputs[i].tlg_src, &cmd_status);
+ if (ret)
+ return ret;
+
+ pd->video_data.context.sig_index = i;
+ return 0;
+}
+
+static struct poseidon_control *check_control_id(__u32 id)
+{
+ struct poseidon_control *control = &controls[0];
+ int array_size = ARRAY_SIZE(controls);
+
+ for (; control < &controls[array_size]; control++)
+ if (control->v4l2_ctrl.id == id)
+ return control;
+ return NULL;
+}
+
+static int vidioc_queryctrl(struct file *file, void *fh,
+ struct v4l2_queryctrl *a)
+{
+ struct poseidon_control *control = NULL;
+
+ control = check_control_id(a->id);
+ if (!control)
+ return -EINVAL;
+
+ *a = control->v4l2_ctrl;
+ return 0;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *ctrl)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+ struct poseidon_control *control = NULL;
+ struct tuner_custom_parameter_s tuner_param;
+ s32 ret = 0, cmd_status;
+
+ control = check_control_id(ctrl->id);
+ if (!control)
+ return -EINVAL;
+
+ mutex_lock(&pd->lock);
+ ret = send_get_req(pd, TUNER_CUSTOM_PARAMETER, control->vc_id,
+ &tuner_param, &cmd_status, sizeof(tuner_param));
+ mutex_unlock(&pd->lock);
+
+ if (ret || cmd_status)
+ return -1;
+
+ ctrl->value = tuner_param.param_value;
+ return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a)
+{
+ struct tuner_custom_parameter_s param = {0};
+ struct poseidon_control *control = NULL;
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+ s32 ret = 0, cmd_status, params;
+
+ control = check_control_id(a->id);
+ if (!control)
+ return -EINVAL;
+
+ param.param_value = a->value;
+ param.param_id = control->vc_id;
+ params = *(s32 *)&param; /* temp code */
+
+ mutex_lock(&pd->lock);
+ ret = send_set_req(pd, TUNER_CUSTOM_PARAMETER, params, &cmd_status);
+ ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
+ mutex_unlock(&pd->lock);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/4);
+ return ret;
+}
+
+/* Audio ioctls */
+static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+ if (0 != a->index)
+ return -EINVAL;
+ a->capability = V4L2_AUDCAP_STEREO;
+ strcpy(a->name, "USB audio in");
+ /*Poseidon have no AVL function.*/
+ a->mode = 0;
+ return 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+ a->index = 0;
+ a->capability = V4L2_AUDCAP_STEREO;
+ strcpy(a->name, "USB audio in");
+ a->mode = 0;
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+ return (0 == a->index) ? 0 : -EINVAL;
+}
+
+/* Tuner ioctls */
+static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *tuner)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+ struct tuner_atv_sig_stat_s atv_stat;
+ s32 count = 5, ret, cmd_status;
+ int index;
+
+ if (0 != tuner->index)
+ return -EINVAL;
+
+ mutex_lock(&pd->lock);
+ ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_ANALOG_TV,
+ &atv_stat, &cmd_status, sizeof(atv_stat));
+
+ while (atv_stat.sig_lock_busy && count-- && !ret) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ);
+
+ ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_ANALOG_TV,
+ &atv_stat, &cmd_status, sizeof(atv_stat));
+ }
+ mutex_unlock(&pd->lock);
+
+ if (debug_mode)
+ log("P:%d,S:%d", atv_stat.sig_present, atv_stat.sig_strength);
+
+ if (ret || cmd_status)
+ tuner->signal = 0;
+ else if (atv_stat.sig_present && !atv_stat.sig_strength)
+ tuner->signal = 0xFFFF;
+ else
+ tuner->signal = (atv_stat.sig_strength * 255 / 10) << 8;
+
+ strcpy(tuner->name, "Telegent Systems");
+ tuner->type = V4L2_TUNER_ANALOG_TV;
+ tuner->rangelow = TUNER_FREQ_MIN / 62500;
+ tuner->rangehigh = TUNER_FREQ_MAX / 62500;
+ tuner->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
+ index = pd->video_data.context.audio_idx;
+ tuner->rxsubchans = pd_audio_modes[index].v4l2_audio_sub;
+ tuner->audmode = pd_audio_modes[index].v4l2_audio_mode;
+ tuner->afc = 0;
+ logs(front);
+ return 0;
+}
+
+static int pd_vidioc_s_tuner(struct poseidon *pd, int index)
+{
+ s32 ret = 0, cmd_status, param, audiomode;
+
+ mutex_lock(&pd->lock);
+ param = pd_audio_modes[index].tlg_audio_mode;
+ ret = send_set_req(pd, TUNER_AUD_MODE, param, &cmd_status);
+ audiomode = get_audio_std(pd->video_data.context.tvnormid);
+ ret |= send_set_req(pd, TUNER_AUD_ANA_STD, audiomode,
+ &cmd_status);
+ if (!ret)
+ pd->video_data.context.audio_idx = index;
+ mutex_unlock(&pd->lock);
+ return ret;
+}
+
+static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *a)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+ int index;
+
+ if (0 != a->index)
+ return -EINVAL;
+ logs(front);
+ for (index = 0; index < POSEIDON_AUDIOMODS; index++)
+ if (a->audmode == pd_audio_modes[index].v4l2_audio_mode)
+ return pd_vidioc_s_tuner(pd, index);
+ return -EINVAL;
+}
+
+static int vidioc_g_frequency(struct file *file, void *fh,
+ struct v4l2_frequency *freq)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+ struct running_context *context = &pd->video_data.context;
+
+ if (0 != freq->tuner)
+ return -EINVAL;
+ freq->frequency = context->freq;
+ freq->type = V4L2_TUNER_ANALOG_TV;
+ return 0;
+}
+
+static int set_frequency(struct poseidon *pd, __u32 frequency)
+{
+ s32 ret = 0, param, cmd_status;
+ struct running_context *context = &pd->video_data.context;
+
+ param = frequency * 62500 / 1000;
+ if (param < TUNER_FREQ_MIN/1000 || param > TUNER_FREQ_MAX / 1000)
+ return -EINVAL;
+
+ mutex_lock(&pd->lock);
+ ret = send_set_req(pd, TUNE_FREQ_SELECT, param, &cmd_status);
+ ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
+
+ msleep(250); /* wait for a while until the hardware is ready. */
+ context->freq = frequency;
+ mutex_unlock(&pd->lock);
+ return ret;
+}
+
+static int vidioc_s_frequency(struct file *file, void *fh,
+ struct v4l2_frequency *freq)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+
+ logs(front);
+#ifdef CONFIG_PM
+ pd->pm_suspend = pm_video_suspend;
+ pd->pm_resume = pm_video_resume;
+#endif
+ return set_frequency(pd, freq->frequency);
+}
+
+static int vidioc_reqbufs(struct file *file, void *fh,
+ struct v4l2_requestbuffers *b)
+{
+ struct front_face *front = file->private_data;
+ logs(front);
+ return videobuf_reqbufs(&front->q, b);
+}
+
+static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct front_face *front = file->private_data;
+ logs(front);
+ return videobuf_querybuf(&front->q, b);
+}
+
+static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct front_face *front = file->private_data;
+ return videobuf_qbuf(&front->q, b);
+}
+
+static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct front_face *front = file->private_data;
+ return videobuf_dqbuf(&front->q, b, file->f_flags & O_NONBLOCK);
+}
+
+/* Just stop the URBs, do not free the URBs */
+static int usb_transfer_stop(struct video_data *video)
+{
+ if (video->is_streaming) {
+ int i;
+ s32 cmd_status;
+ struct poseidon *pd = video->pd;
+
+ video->is_streaming = 0;
+ for (i = 0; i < SBUF_NUM; ++i) {
+ if (video->urb_array[i])
+ usb_kill_urb(video->urb_array[i]);
+ }
+
+ send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP,
+ &cmd_status);
+ }
+ return 0;
+}
+
+int stop_all_video_stream(struct poseidon *pd)
+{
+ struct video_data *video = &pd->video_data;
+ struct vbi_data *vbi = &pd->vbi_data;
+
+ mutex_lock(&pd->lock);
+ if (video->is_streaming) {
+ struct front_face *front = video->front;
+
+ /* stop the URBs */
+ usb_transfer_stop(video);
+ free_all_urb(video);
+
+ /* stop the host side of VIDEO */
+ videobuf_stop(&front->q);
+ videobuf_mmap_free(&front->q);
+
+ /* stop the host side of VBI */
+ front = vbi->front;
+ if (front) {
+ videobuf_stop(&front->q);
+ videobuf_mmap_free(&front->q);
+ }
+ }
+ mutex_unlock(&pd->lock);
+ return 0;
+}
+
+/*
+ * The bubbles can seriously damage the video's quality,
+ * though it occurs in very rare situation.
+ */
+static void iso_bubble_handler(struct work_struct *w)
+{
+ struct video_data *video;
+ struct poseidon *pd;
+
+ video = container_of(w, struct video_data, bubble_work);
+ pd = video->pd;
+
+ mutex_lock(&pd->lock);
+ usb_transfer_stop(video);
+ msleep(500);
+ start_video_stream(pd);
+ mutex_unlock(&pd->lock);
+}
+
+
+static int vidioc_streamon(struct file *file, void *fh,
+ enum v4l2_buf_type type)
+{
+ struct front_face *front = fh;
+
+ logs(front);
+ if (unlikely(type != front->type))
+ return -EINVAL;
+ return videobuf_streamon(&front->q);
+}
+
+static int vidioc_streamoff(struct file *file, void *fh,
+ enum v4l2_buf_type type)
+{
+ struct front_face *front = file->private_data;
+
+ logs(front);
+ if (unlikely(type != front->type))
+ return -EINVAL;
+ return videobuf_streamoff(&front->q);
+}
+
+/* Set the firmware's default values : need altersetting */
+static int pd_video_checkmode(struct poseidon *pd)
+{
+ s32 ret = 0, cmd_status, audiomode;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/2);
+
+ /* choose the altersetting */
+ ret = usb_set_interface(pd->udev, 0,
+ (pd->cur_transfer_mode ?
+ ISO_3K_BULK_ALTERNATE_IFACE :
+ BULK_ALTERNATE_IFACE));
+ if (ret < 0)
+ goto error;
+
+ /* set default parameters for PAL-D , with the VBI enabled*/
+ ret = set_tuner_mode(pd, TLG_MODE_ANALOG_TV);
+ ret |= send_set_req(pd, SGNL_SRC_SEL,
+ TLG_SIG_SRC_ANTENNA, &cmd_status);
+ ret |= send_set_req(pd, VIDEO_STD_SEL,
+ TLG_TUNE_VSTD_PAL_D, &cmd_status);
+ ret |= send_set_req(pd, VIDEO_STREAM_FMT_SEL,
+ TLG_TUNER_VID_FORMAT_YUV, &cmd_status);
+ ret |= send_set_req(pd, VIDEO_ROSOLU_SEL,
+ TLG_TUNE_VID_RES_720, &cmd_status);
+ ret |= send_set_req(pd, TUNE_FREQ_SELECT, TUNER_FREQ_MIN, &cmd_status);
+ ret |= send_set_req(pd, VBI_DATA_SEL, 1, &cmd_status);/* enable vbi */
+
+ /* set the audio */
+ audiomode = get_audio_std(pd->video_data.context.tvnormid);
+ ret |= send_set_req(pd, TUNER_AUD_ANA_STD, audiomode, &cmd_status);
+ ret |= send_set_req(pd, TUNER_AUD_MODE,
+ TLG_TUNE_TVAUDIO_MODE_STEREO, &cmd_status);
+ ret |= send_set_req(pd, AUDIO_SAMPLE_RATE_SEL,
+ ATV_AUDIO_RATE_48K, &cmd_status);
+error:
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int pm_video_suspend(struct poseidon *pd)
+{
+ /* stop audio */
+ pm_alsa_suspend(pd);
+
+ /* stop and free all the URBs */
+ usb_transfer_stop(&pd->video_data);
+ free_all_urb(&pd->video_data);
+
+ /* reset the interface */
+ usb_set_interface(pd->udev, 0, 0);
+ msleep(300);
+ return 0;
+}
+
+static int restore_v4l2_context(struct poseidon *pd,
+ struct running_context *context)
+{
+ struct front_face *front = pd->video_data.front;
+
+ pd_video_checkmode(pd);
+
+ set_std(pd, &context->tvnormid);
+ vidioc_s_input(NULL, front, context->sig_index);
+ pd_vidioc_s_tuner(pd, context->audio_idx);
+ pd_vidioc_s_fmt(pd, &context->pix);
+ set_frequency(pd, context->freq);
+ return 0;
+}
+
+static int pm_video_resume(struct poseidon *pd)
+{
+ struct video_data *video = &pd->video_data;
+
+ /* resume the video */
+ /* [1] restore the origin V4L2 parameters */
+ restore_v4l2_context(pd, &video->context);
+
+ /* [2] initiate video copy variables */
+ if (video->front->curr_frame)
+ init_copy(video, 0);
+
+ /* [3] fire urbs */
+ start_video_stream(pd);
+
+ /* resume the audio */
+ pm_alsa_resume(pd);
+ return 0;
+}
+#endif
+
+void set_debug_mode(struct video_device *vfd, int debug_mode)
+{
+ vfd->debug = 0;
+ if (debug_mode & 0x1)
+ vfd->debug = V4L2_DEBUG_IOCTL;
+ if (debug_mode & 0x2)
+ vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
+}
+
+static void init_video_context(struct running_context *context)
+{
+ context->sig_index = 0;
+ context->audio_idx = 1; /* stereo */
+ context->tvnormid = V4L2_STD_PAL_D;
+ context->pix = (struct v4l2_pix_format) {
+ .width = 720,
+ .height = 576,
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ .field = V4L2_FIELD_INTERLACED,
+ .bytesperline = 720 * 2,
+ .sizeimage = 720 * 576 * 2,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ .priv = 0
+ };
+}
+
+static int pd_video_open(struct file *file)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct poseidon *pd = video_get_drvdata(vfd);
+ struct front_face *front = NULL;
+ int ret = -ENOMEM;
+
+ mutex_lock(&pd->lock);
+ usb_autopm_get_interface(pd->interface);
+
+ if (vfd->vfl_type == VFL_TYPE_GRABBER
+ && !(pd->state & POSEIDON_STATE_ANALOG)) {
+ front = kzalloc(sizeof(struct front_face), GFP_KERNEL);
+ if (!front)
+ goto out;
+
+ pd->cur_transfer_mode = usb_transfer_mode;/* bulk or iso */
+ init_video_context(&pd->video_data.context);
+
+ ret = pd_video_checkmode(pd);
+ if (ret < 0) {
+ kfree(front);
+ ret = -1;
+ goto out;
+ }
+
+ pd->state |= POSEIDON_STATE_ANALOG;
+ front->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ pd->video_data.users++;
+ set_debug_mode(vfd, debug_mode);
+
+ videobuf_queue_vmalloc_init(&front->q, &pd_video_qops,
+ NULL, &front->queue_lock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,/* video is interlacd */
+ sizeof(struct videobuf_buffer),/*it's enough*/
+ front, NULL);
+ } else if (vfd->vfl_type == VFL_TYPE_VBI
+ && !(pd->state & POSEIDON_STATE_VBI)) {
+ front = kzalloc(sizeof(struct front_face), GFP_KERNEL);
+ if (!front)
+ goto out;
+
+ pd->state |= POSEIDON_STATE_VBI;
+ front->type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ pd->vbi_data.front = front;
+ pd->vbi_data.users++;
+
+ videobuf_queue_vmalloc_init(&front->q, &pd_video_qops,
+ NULL, &front->queue_lock,
+ V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_FIELD_NONE, /* vbi is NONE mode */
+ sizeof(struct videobuf_buffer),
+ front, NULL);
+ } else {
+ /* maybe add FM support here */
+ log("other ");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ front->pd = pd;
+ front->curr_frame = NULL;
+ INIT_LIST_HEAD(&front->active);
+ spin_lock_init(&front->queue_lock);
+
+ file->private_data = front;
+ kref_get(&pd->kref);
+
+ mutex_unlock(&pd->lock);
+ return 0;
+out:
+ usb_autopm_put_interface(pd->interface);
+ mutex_unlock(&pd->lock);
+ return ret;
+}
+
+static int pd_video_release(struct file *file)
+{
+ struct front_face *front = file->private_data;
+ struct poseidon *pd = front->pd;
+ s32 cmd_status = 0;
+
+ logs(front);
+ mutex_lock(&pd->lock);
+
+ if (front->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ pd->state &= ~POSEIDON_STATE_ANALOG;
+
+ /* stop the device, and free the URBs */
+ usb_transfer_stop(&pd->video_data);
+ free_all_urb(&pd->video_data);
+
+ /* stop the firmware */
+ send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP,
+ &cmd_status);
+
+ pd->file_for_stream = NULL;
+ pd->video_data.users--;
+ } else if (front->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ pd->state &= ~POSEIDON_STATE_VBI;
+ pd->vbi_data.front = NULL;
+ pd->vbi_data.users--;
+ }
+ videobuf_stop(&front->q);
+ videobuf_mmap_free(&front->q);
+
+ usb_autopm_put_interface(pd->interface);
+ mutex_unlock(&pd->lock);
+
+ kfree(front);
+ file->private_data = NULL;
+ kref_put(&pd->kref, poseidon_delete);
+ return 0;
+}
+
+static int pd_video_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct front_face *front = file->private_data;
+ return videobuf_mmap_mapper(&front->q, vma);
+}
+
+static unsigned int pd_video_poll(struct file *file, poll_table *table)
+{
+ struct front_face *front = file->private_data;
+ return videobuf_poll_stream(file, &front->q, table);
+}
+
+static ssize_t pd_video_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct front_face *front = file->private_data;
+ return videobuf_read_stream(&front->q, buffer, count, ppos,
+ 0, file->f_flags & O_NONBLOCK);
+}
+
+/* This struct works for both VIDEO and VBI */
+static const struct v4l2_file_operations pd_video_fops = {
+ .owner = THIS_MODULE,
+ .open = pd_video_open,
+ .release = pd_video_release,
+ .read = pd_video_read,
+ .poll = pd_video_poll,
+ .mmap = pd_video_mmap,
+ .ioctl = video_ioctl2, /* maybe changed in future */
+};
+
+static const struct v4l2_ioctl_ops pd_video_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+
+ /* Video format */
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt,
+ .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi, /* VBI */
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt,
+
+ /* Input */
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_enum_input = vidioc_enum_input,
+
+ /* Audio ioctls */
+ .vidioc_enumaudio = vidioc_enumaudio,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+
+ /* Tuner ioctls */
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+
+ /* Buffer handlers */
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+
+ /* Stream on/off */
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+
+ /* Control handling */
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+};
+
+static struct video_device pd_video_template = {
+ .name = "Telegent-Video",
+ .fops = &pd_video_fops,
+ .minor = -1,
+ .release = video_device_release,
+ .tvnorms = V4L2_STD_ALL,
+ .ioctl_ops = &pd_video_ioctl_ops,
+};
+
+struct video_device *vdev_init(struct poseidon *pd, struct video_device *tmp)
+{
+ struct video_device *vfd;
+
+ vfd = video_device_alloc();
+ if (vfd == NULL)
+ return NULL;
+ *vfd = *tmp;
+ vfd->minor = -1;
+ vfd->v4l2_dev = &pd->v4l2_dev;
+ /*vfd->parent = &(pd->udev->dev); */
+ vfd->release = video_device_release;
+ video_set_drvdata(vfd, pd);
+ return vfd;
+}
+
+void destroy_video_device(struct video_device **v_dev)
+{
+ struct video_device *dev = *v_dev;
+
+ if (dev == NULL)
+ return;
+
+ if (video_is_registered(dev))
+ video_unregister_device(dev);
+ else
+ video_device_release(dev);
+ *v_dev = NULL;
+}
+
+void pd_video_exit(struct poseidon *pd)
+{
+ struct video_data *video = &pd->video_data;
+ struct vbi_data *vbi = &pd->vbi_data;
+
+ destroy_video_device(&video->v_dev);
+ destroy_video_device(&vbi->v_dev);
+ log();
+}
+
+int pd_video_init(struct poseidon *pd)
+{
+ struct video_data *video = &pd->video_data;
+ struct vbi_data *vbi = &pd->vbi_data;
+ int ret = -ENOMEM;
+
+ video->v_dev = vdev_init(pd, &pd_video_template);
+ if (video->v_dev == NULL)
+ goto out;
+
+ ret = video_register_device(video->v_dev, VFL_TYPE_GRABBER, -1);
+ if (ret != 0)
+ goto out;
+
+ /* VBI uses the same template as video */
+ vbi->v_dev = vdev_init(pd, &pd_video_template);
+ if (vbi->v_dev == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ ret = video_register_device(vbi->v_dev, VFL_TYPE_VBI, -1);
+ if (ret != 0)
+ goto out;
+ log("register VIDEO/VBI devices");
+ return 0;
+out:
+ log("VIDEO/VBI devices register failed, : %d", ret);
+ pd_video_exit(pd);
+ return ret;
+}
+
diff --git a/drivers/media/video/tlg2300/vendorcmds.h b/drivers/media/video/tlg2300/vendorcmds.h
new file mode 100644
index 00000000..ba6f4ae3
--- /dev/null
+++ b/drivers/media/video/tlg2300/vendorcmds.h
@@ -0,0 +1,243 @@
+#ifndef VENDOR_CMD_H_
+#define VENDOR_CMD_H_
+
+#define BULK_ALTERNATE_IFACE (2)
+#define ISO_3K_BULK_ALTERNATE_IFACE (1)
+#define REQ_SET_CMD (0X00)
+#define REQ_GET_CMD (0X80)
+
+enum tlg__analog_audio_standard {
+ TLG_TUNE_ASTD_NONE = 0x00000000,
+ TLG_TUNE_ASTD_A2 = 0x00000001,
+ TLG_TUNE_ASTD_NICAM = 0x00000002,
+ TLG_TUNE_ASTD_EIAJ = 0x00000004,
+ TLG_TUNE_ASTD_BTSC = 0x00000008,
+ TLG_TUNE_ASTD_FM_US = 0x00000010,
+ TLG_TUNE_ASTD_FM_EUR = 0x00000020,
+ TLG_TUNE_ASTD_ALL = 0x0000003f
+};
+
+/*
+ * identifiers for Custom Parameter messages.
+ * @typedef cmd_custom_param_id_t
+ */
+enum cmd_custom_param_id {
+ CUST_PARM_ID_NONE = 0x00,
+ CUST_PARM_ID_BRIGHTNESS_CTRL = 0x01,
+ CUST_PARM_ID_CONTRAST_CTRL = 0x02,
+ CUST_PARM_ID_HUE_CTRL = 0x03,
+ CUST_PARM_ID_SATURATION_CTRL = 0x04,
+ CUST_PARM_ID_AUDIO_SNR_THRESHOLD = 0x10,
+ CUST_PARM_ID_AUDIO_AGC_THRESHOLD = 0x11,
+ CUST_PARM_ID_MAX
+};
+
+struct tuner_custom_parameter_s {
+ uint16_t param_id; /* Parameter identifier */
+ uint16_t param_value; /* Parameter value */
+};
+
+struct tuner_ber_rate_s {
+ uint32_t ber_rate; /* BER sample rate in seconds */
+};
+
+struct tuner_atv_sig_stat_s {
+ uint32_t sig_present;
+ uint32_t sig_locked;
+ uint32_t sig_lock_busy;
+ uint32_t sig_strength; /* milliDb */
+ uint32_t tv_audio_chan; /* mono/stereo/sap*/
+ uint32_t mvision_stat; /* macrovision status */
+};
+
+struct tuner_dtv_sig_stat_s {
+ uint32_t sig_present; /* Boolean*/
+ uint32_t sig_locked; /* Boolean */
+ uint32_t sig_lock_busy; /* Boolean (Can this time-out?) */
+ uint32_t sig_strength; /* milliDb*/
+};
+
+struct tuner_fm_sig_stat_s {
+ uint32_t sig_present; /* Boolean*/
+ uint32_t sig_locked; /* Boolean */
+ uint32_t sig_lock_busy; /* Boolean */
+ uint32_t sig_stereo_mono;/* TBD*/
+ uint32_t sig_strength; /* milliDb*/
+};
+
+enum _tag_tlg_tune_srv_cmd {
+ TLG_TUNE_PLAY_SVC_START = 1,
+ TLG_TUNE_PLAY_SVC_STOP
+};
+
+enum _tag_tune_atv_audio_mode_caps {
+ TLG_TUNE_TVAUDIO_MODE_MONO = 0x00000001,
+ TLG_TUNE_TVAUDIO_MODE_STEREO = 0x00000002,
+ TLG_TUNE_TVAUDIO_MODE_LANG_A = 0x00000010,/* Primary language*/
+ TLG_TUNE_TVAUDIO_MODE_LANG_B = 0x00000020,/* 2nd avail language*/
+ TLG_TUNE_TVAUDIO_MODE_LANG_C = 0x00000040
+};
+
+
+enum _tag_tuner_atv_audio_rates {
+ ATV_AUDIO_RATE_NONE = 0x00,/* Audio not supported*/
+ ATV_AUDIO_RATE_32K = 0x01,/* Audio rate = 32 KHz*/
+ ATV_AUDIO_RATE_48K = 0x02, /* Audio rate = 48 KHz*/
+ ATV_AUDIO_RATE_31_25K = 0x04 /* Audio rate = 31.25KHz */
+};
+
+enum _tag_tune_atv_vid_res_caps {
+ TLG_TUNE_VID_RES_NONE = 0x00000000,
+ TLG_TUNE_VID_RES_720 = 0x00000001,
+ TLG_TUNE_VID_RES_704 = 0x00000002,
+ TLG_TUNE_VID_RES_360 = 0x00000004
+};
+
+enum _tag_tuner_analog_video_format {
+ TLG_TUNER_VID_FORMAT_YUV = 0x00000001,
+ TLG_TUNER_VID_FORMAT_YCRCB = 0x00000002,
+ TLG_TUNER_VID_FORMAT_RGB_565 = 0x00000004,
+};
+
+enum tlg_ext_audio_support {
+ TLG_EXT_AUDIO_NONE = 0x00,/* No external audio input supported */
+ TLG_EXT_AUDIO_LR = 0x01/* LR external audio inputs supported*/
+};
+
+enum {
+ TLG_MODE_NONE = 0x00, /* No Mode specified*/
+ TLG_MODE_ANALOG_TV = 0x01, /* Analog Television mode*/
+ TLG_MODE_ANALOG_TV_UNCOMP = 0x01, /* Analog Television mode*/
+ TLG_MODE_ANALOG_TV_COMP = 0x02, /* Analog TV mode (compressed)*/
+ TLG_MODE_FM_RADIO = 0x04, /* FM Radio mode*/
+ TLG_MODE_DVB_T = 0x08, /* Digital TV (DVB-T)*/
+};
+
+enum tlg_signal_sources_t {
+ TLG_SIG_SRC_NONE = 0x00,/* Signal source not specified */
+ TLG_SIG_SRC_ANTENNA = 0x01,/* Signal src is: Antenna */
+ TLG_SIG_SRC_CABLE = 0x02,/* Signal src is: Coax Cable*/
+ TLG_SIG_SRC_SVIDEO = 0x04,/* Signal src is: S_VIDEO */
+ TLG_SIG_SRC_COMPOSITE = 0x08 /* Signal src is: Composite Video */
+};
+
+enum tuner_analog_video_standard {
+ TLG_TUNE_VSTD_NONE = 0x00000000,
+ TLG_TUNE_VSTD_NTSC_M = 0x00000001,
+ TLG_TUNE_VSTD_NTSC_M_J = 0x00000002,/* Japan */
+ TLG_TUNE_VSTD_PAL_B = 0x00000010,
+ TLG_TUNE_VSTD_PAL_D = 0x00000020,
+ TLG_TUNE_VSTD_PAL_G = 0x00000040,
+ TLG_TUNE_VSTD_PAL_H = 0x00000080,
+ TLG_TUNE_VSTD_PAL_I = 0x00000100,
+ TLG_TUNE_VSTD_PAL_M = 0x00000200,
+ TLG_TUNE_VSTD_PAL_N = 0x00000400,
+ TLG_TUNE_VSTD_SECAM_B = 0x00001000,
+ TLG_TUNE_VSTD_SECAM_D = 0x00002000,
+ TLG_TUNE_VSTD_SECAM_G = 0x00004000,
+ TLG_TUNE_VSTD_SECAM_H = 0x00008000,
+ TLG_TUNE_VSTD_SECAM_K = 0x00010000,
+ TLG_TUNE_VSTD_SECAM_K1 = 0x00020000,
+ TLG_TUNE_VSTD_SECAM_L = 0x00040000,
+ TLG_TUNE_VSTD_SECAM_L1 = 0x00080000,
+ TLG_TUNE_VSTD_PAL_N_COMBO = 0x00100000
+};
+
+enum tlg_mode_caps {
+ TLG_MODE_CAPS_NONE = 0x00, /* No Mode specified */
+ TLG_MODE_CAPS_ANALOG_TV_UNCOMP = 0x01, /* Analog TV mode */
+ TLG_MODE_CAPS_ANALOG_TV_COMP = 0x02, /* Analog TV (compressed)*/
+ TLG_MODE_CAPS_FM_RADIO = 0x04, /* FM Radio mode */
+ TLG_MODE_CAPS_DVB_T = 0x08, /* Digital TV (DVB-T) */
+};
+
+enum poseidon_vendor_cmds {
+ LAST_CMD_STAT = 0x00,
+ GET_CHIP_ID = 0x01,
+ GET_FW_ID = 0x02,
+ PRODUCT_CAPS = 0x03,
+
+ TUNE_MODE_CAP_ATV = 0x10,
+ TUNE_MODE_CAP_ATVCOMP = 0X10,
+ TUNE_MODE_CAP_DVBT = 0x10,
+ TUNE_MODE_CAP_FM = 0x10,
+ TUNE_MODE_SELECT = 0x11,
+ TUNE_FREQ_SELECT = 0x12,
+ SGNL_SRC_SEL = 0x13,
+
+ VIDEO_STD_SEL = 0x14,
+ VIDEO_STREAM_FMT_SEL = 0x15,
+ VIDEO_ROSOLU_AVAIL = 0x16,
+ VIDEO_ROSOLU_SEL = 0x17,
+ VIDEO_CONT_PROTECT = 0x20,
+
+ VCR_TIMING_MODSEL = 0x21,
+ EXT_AUDIO_CAP = 0x22,
+ EXT_AUDIO_SEL = 0x23,
+ TEST_PATTERN_SEL = 0x24,
+ VBI_DATA_SEL = 0x25,
+ AUDIO_SAMPLE_RATE_CAP = 0x28,
+ AUDIO_SAMPLE_RATE_SEL = 0x29,
+ TUNER_AUD_MODE = 0x2a,
+ TUNER_AUD_MODE_AVAIL = 0x2b,
+ TUNER_AUD_ANA_STD = 0x2c,
+ TUNER_CUSTOM_PARAMETER = 0x2f,
+
+ DVBT_TUNE_MODE_SEL = 0x30,
+ DVBT_BANDW_CAP = 0x31,
+ DVBT_BANDW_SEL = 0x32,
+ DVBT_GUARD_INTERV_CAP = 0x33,
+ DVBT_GUARD_INTERV_SEL = 0x34,
+ DVBT_MODULATION_CAP = 0x35,
+ DVBT_MODULATION_SEL = 0x36,
+ DVBT_INNER_FEC_RATE_CAP = 0x37,
+ DVBT_INNER_FEC_RATE_SEL = 0x38,
+ DVBT_TRANS_MODE_CAP = 0x39,
+ DVBT_TRANS_MODE_SEL = 0x3a,
+ DVBT_SEARCH_RANG = 0x3c,
+
+ TUNER_SETUP_ANALOG = 0x40,
+ TUNER_SETUP_DIGITAL = 0x41,
+ TUNER_SETUP_FM_RADIO = 0x42,
+ TAKE_REQUEST = 0x43, /* Take effect of the command */
+ PLAY_SERVICE = 0x44, /* Play start or Play stop */
+ TUNER_STATUS = 0x45,
+ TUNE_PROP_DVBT = 0x46,
+ ERR_RATE_STATS = 0x47,
+ TUNER_BER_RATE = 0x48,
+
+ SCAN_CAPS = 0x50,
+ SCAN_SETUP = 0x51,
+ SCAN_SERVICE = 0x52,
+ SCAN_STATS = 0x53,
+
+ PID_SET = 0x58,
+ PID_UNSET = 0x59,
+ PID_LIST = 0x5a,
+
+ IRD_CAP = 0x60,
+ IRD_MODE_SEL = 0x61,
+ IRD_SETUP = 0x62,
+
+ PTM_MODE_CAP = 0x70,
+ PTM_MODE_SEL = 0x71,
+ PTM_SERVICE = 0x72,
+ TUNER_REG_SCRIPT = 0x73,
+ CMD_CHIP_RST = 0x74,
+};
+
+enum tlg_bw {
+ TLG_BW_5 = 5,
+ TLG_BW_6 = 6,
+ TLG_BW_7 = 7,
+ TLG_BW_8 = 8,
+ TLG_BW_12 = 12,
+ TLG_BW_15 = 15
+};
+
+struct cmd_firmware_vers_s {
+ uint8_t fw_rev_major;
+ uint8_t fw_rev_minor;
+ uint16_t fw_patch;
+};
+#endif /* VENDOR_CMD_H_ */
diff --git a/drivers/media/video/tlv320aic23b.c b/drivers/media/video/tlv320aic23b.c
new file mode 100644
index 00000000..809a75a5
--- /dev/null
+++ b/drivers/media/video/tlv320aic23b.c
@@ -0,0 +1,230 @@
+/*
+ * tlv320aic23b - driver version 0.0.1
+ *
+ * Copyright (C) 2006 Scott Alfter <salfter@ssai.us>
+ *
+ * Based on wm8775 driver
+ *
+ * Copyright (C) 2004 Ulf Eklund <ivtv at eklund.to>
+ * Copyright (C) 2005 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/ioctl.h>
+#include <asm/uaccess.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+
+MODULE_DESCRIPTION("tlv320aic23b driver");
+MODULE_AUTHOR("Scott Alfter, Ulf Eklund, Hans Verkuil");
+MODULE_LICENSE("GPL");
+
+
+/* ----------------------------------------------------------------------- */
+
+struct tlv320aic23b_state {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+};
+
+static inline struct tlv320aic23b_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct tlv320aic23b_state, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct tlv320aic23b_state, hdl)->sd;
+}
+
+static int tlv320aic23b_write(struct v4l2_subdev *sd, int reg, u16 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int i;
+
+ if ((reg < 0 || reg > 9) && (reg != 15)) {
+ v4l2_err(sd, "Invalid register R%d\n", reg);
+ return -1;
+ }
+
+ for (i = 0; i < 3; i++)
+ if (i2c_smbus_write_byte_data(client,
+ (reg << 1) | (val >> 8), val & 0xff) == 0)
+ return 0;
+ v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg);
+ return -1;
+}
+
+static int tlv320aic23b_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
+{
+ switch (freq) {
+ case 32000: /* set sample rate to 32 kHz */
+ tlv320aic23b_write(sd, 8, 0x018);
+ break;
+ case 44100: /* set sample rate to 44.1 kHz */
+ tlv320aic23b_write(sd, 8, 0x022);
+ break;
+ case 48000: /* set sample rate to 48 kHz */
+ tlv320aic23b_write(sd, 8, 0x000);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int tlv320aic23b_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ tlv320aic23b_write(sd, 0, 0x180); /* mute both channels */
+ /* set gain on both channels to +3.0 dB */
+ if (!ctrl->val)
+ tlv320aic23b_write(sd, 0, 0x119);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int tlv320aic23b_log_status(struct v4l2_subdev *sd)
+{
+ struct tlv320aic23b_state *state = to_state(sd);
+
+ v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_ctrl_ops tlv320aic23b_ctrl_ops = {
+ .s_ctrl = tlv320aic23b_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops tlv320aic23b_core_ops = {
+ .log_status = tlv320aic23b_log_status,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+};
+
+static const struct v4l2_subdev_audio_ops tlv320aic23b_audio_ops = {
+ .s_clock_freq = tlv320aic23b_s_clock_freq,
+};
+
+static const struct v4l2_subdev_ops tlv320aic23b_ops = {
+ .core = &tlv320aic23b_core_ops,
+ .audio = &tlv320aic23b_audio_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+/* i2c implementation */
+
+/*
+ * Generic i2c probe
+ * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
+ */
+
+static int tlv320aic23b_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tlv320aic23b_state *state;
+ struct v4l2_subdev *sd;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ state = kzalloc(sizeof(struct tlv320aic23b_state), GFP_KERNEL);
+ if (state == NULL)
+ return -ENOMEM;
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &tlv320aic23b_ops);
+
+ /* Initialize tlv320aic23b */
+
+ /* RESET */
+ tlv320aic23b_write(sd, 15, 0x000);
+ /* turn off DAC & mic input */
+ tlv320aic23b_write(sd, 6, 0x00A);
+ /* left-justified, 24-bit, master mode */
+ tlv320aic23b_write(sd, 7, 0x049);
+ /* set gain on both channels to +3.0 dB */
+ tlv320aic23b_write(sd, 0, 0x119);
+ /* set sample rate to 48 kHz */
+ tlv320aic23b_write(sd, 8, 0x000);
+ /* activate digital interface */
+ tlv320aic23b_write(sd, 9, 0x001);
+
+ v4l2_ctrl_handler_init(&state->hdl, 1);
+ v4l2_ctrl_new_std(&state->hdl, &tlv320aic23b_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+ sd->ctrl_handler = &state->hdl;
+ if (state->hdl.error) {
+ int err = state->hdl.error;
+
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ return err;
+ }
+ v4l2_ctrl_handler_setup(&state->hdl);
+ return 0;
+}
+
+static int tlv320aic23b_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct tlv320aic23b_state *state = to_state(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct i2c_device_id tlv320aic23b_id[] = {
+ { "tlv320aic23b", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tlv320aic23b_id);
+
+static struct i2c_driver tlv320aic23b_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "tlv320aic23b",
+ },
+ .probe = tlv320aic23b_probe,
+ .remove = tlv320aic23b_remove,
+ .id_table = tlv320aic23b_id,
+};
+
+module_i2c_driver(tlv320aic23b_driver);
diff --git a/drivers/media/video/tm6000/Kconfig b/drivers/media/video/tm6000/Kconfig
new file mode 100644
index 00000000..a43b77ab
--- /dev/null
+++ b/drivers/media/video/tm6000/Kconfig
@@ -0,0 +1,33 @@
+config VIDEO_TM6000
+ tristate "TV Master TM5600/6000/6010 driver"
+ depends on VIDEO_DEV && I2C && INPUT && RC_CORE && USB
+ select VIDEO_TUNER
+ select MEDIA_TUNER_XC2028
+ select MEDIA_TUNER_XC5000
+ select VIDEOBUF_VMALLOC
+ help
+ Support for TM5600/TM6000/TM6010 USB Device
+
+ Since these cards have no MPEG decoder onboard, they transmit
+ only compressed MPEG data over the usb bus, so you need
+ an external software decoder to watch TV on your computer.
+
+ Say Y if you own such a device and want to use it.
+
+config VIDEO_TM6000_ALSA
+ tristate "TV Master TM5600/6000/6010 audio support"
+ depends on VIDEO_TM6000 && SND
+ select SND_PCM
+ ---help---
+ This is a video4linux driver for direct (DMA) audio for
+ TM5600/TM6000/TM6010 USB Devices.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tm6000-alsa.
+
+config VIDEO_TM6000_DVB
+ tristate "DVB Support for tm6000 based TV cards"
+ depends on VIDEO_TM6000 && DVB_CORE && USB
+ select DVB_ZL10353
+ ---help---
+ This adds support for DVB cards based on the tm5600/tm6000 chip.
diff --git a/drivers/media/video/tm6000/Makefile b/drivers/media/video/tm6000/Makefile
new file mode 100644
index 00000000..395515b4
--- /dev/null
+++ b/drivers/media/video/tm6000/Makefile
@@ -0,0 +1,15 @@
+tm6000-y := tm6000-cards.o \
+ tm6000-core.o \
+ tm6000-i2c.o \
+ tm6000-video.o \
+ tm6000-stds.o \
+ tm6000-input.o
+
+obj-$(CONFIG_VIDEO_TM6000) += tm6000.o
+obj-$(CONFIG_VIDEO_TM6000_ALSA) += tm6000-alsa.o
+obj-$(CONFIG_VIDEO_TM6000_DVB) += tm6000-dvb.o
+
+ccflags-y := -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/tm6000/tm6000-alsa.c b/drivers/media/video/tm6000/tm6000-alsa.c
new file mode 100644
index 00000000..bd07ec70
--- /dev/null
+++ b/drivers/media/video/tm6000/tm6000-alsa.c
@@ -0,0 +1,528 @@
+/*
+ *
+ * Support for audio capture for tm5600/6000/6010
+ * (c) 2007-2008 Mauro Carvalho Chehab <mchehab@redhat.com>
+ *
+ * Based on cx88-alsa.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+
+
+#include "tm6000.h"
+#include "tm6000-regs.h"
+
+#undef dprintk
+
+#define dprintk(level, fmt, arg...) do { \
+ if (debug >= level) \
+ printk(KERN_INFO "%s/1: " fmt, chip->core->name , ## arg); \
+ } while (0)
+
+/****************************************************************************
+ Module global static vars
+ ****************************************************************************/
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+
+static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1};
+
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable tm6000x soundcard. default enabled.");
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for tm6000x capture interface(s).");
+
+
+/****************************************************************************
+ Module macros
+ ****************************************************************************/
+
+MODULE_DESCRIPTION("ALSA driver module for tm5600/tm6000/tm6010 based TV cards");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Trident,tm5600},"
+ "{{Trident,tm6000},"
+ "{{Trident,tm6010}");
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable debug messages");
+
+/****************************************************************************
+ Module specific funtions
+ ****************************************************************************/
+
+/*
+ * BOARD Specific: Sets audio DMA
+ */
+
+static int _tm6000_start_audio_dma(struct snd_tm6000_card *chip)
+{
+ struct tm6000_core *core = chip->core;
+
+ dprintk(1, "Starting audio DMA\n");
+
+ /* Enables audio */
+ tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_IF, 0x40, 0x40);
+
+ tm6000_set_audio_bitrate(core, 48000);
+
+ return 0;
+}
+
+/*
+ * BOARD Specific: Resets audio DMA
+ */
+static int _tm6000_stop_audio_dma(struct snd_tm6000_card *chip)
+{
+ struct tm6000_core *core = chip->core;
+
+ dprintk(1, "Stopping audio DMA\n");
+
+ /* Disables audio */
+ tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_IF, 0x00, 0x40);
+
+ return 0;
+}
+
+static void dsp_buffer_free(struct snd_pcm_substream *substream)
+{
+ struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
+
+ dprintk(2, "Freeing buffer\n");
+
+ vfree(substream->runtime->dma_area);
+ substream->runtime->dma_area = NULL;
+ substream->runtime->dma_bytes = 0;
+}
+
+static int dsp_buffer_alloc(struct snd_pcm_substream *substream, int size)
+{
+ struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
+
+ dprintk(2, "Allocating buffer\n");
+
+ if (substream->runtime->dma_area) {
+ if (substream->runtime->dma_bytes > size)
+ return 0;
+
+ dsp_buffer_free(substream);
+ }
+
+ substream->runtime->dma_area = vmalloc(size);
+ if (!substream->runtime->dma_area)
+ return -ENOMEM;
+
+ substream->runtime->dma_bytes = size;
+
+ return 0;
+}
+
+
+/****************************************************************************
+ ALSA PCM Interface
+ ****************************************************************************/
+
+/*
+ * Digital hardware definition
+ */
+#define DEFAULT_FIFO_SIZE 4096
+
+static struct snd_pcm_hardware snd_tm6000_digital_hw = {
+ .info = SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .period_bytes_min = 64,
+ .period_bytes_max = 12544,
+ .periods_min = 2,
+ .periods_max = 98,
+ .buffer_bytes_max = 62720 * 8,
+};
+
+/*
+ * audio pcm capture open callback
+ */
+static int snd_tm6000_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ err = snd_pcm_hw_constraint_pow2(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
+ goto _error;
+
+ chip->substream = substream;
+
+ runtime->hw = snd_tm6000_digital_hw;
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+
+ return 0;
+_error:
+ dprintk(1, "Error opening PCM!\n");
+ return err;
+}
+
+/*
+ * audio close callback
+ */
+static int snd_tm6000_close(struct snd_pcm_substream *substream)
+{
+ struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
+ struct tm6000_core *core = chip->core;
+
+ if (atomic_read(&core->stream_started) > 0) {
+ atomic_set(&core->stream_started, 0);
+ schedule_work(&core->wq_trigger);
+ }
+
+ return 0;
+}
+
+static int tm6000_fillbuf(struct tm6000_core *core, char *buf, int size)
+{
+ struct snd_tm6000_card *chip = core->adev;
+ struct snd_pcm_substream *substream = chip->substream;
+ struct snd_pcm_runtime *runtime;
+ int period_elapsed = 0;
+ unsigned int stride, buf_pos;
+ int length;
+
+ if (atomic_read(&core->stream_started) == 0)
+ return 0;
+
+ if (!size || !substream) {
+ dprintk(1, "substream was NULL\n");
+ return -EINVAL;
+ }
+
+ runtime = substream->runtime;
+ if (!runtime || !runtime->dma_area) {
+ dprintk(1, "runtime was NULL\n");
+ return -EINVAL;
+ }
+
+ buf_pos = chip->buf_pos;
+ stride = runtime->frame_bits >> 3;
+
+ if (stride == 0) {
+ dprintk(1, "stride is zero\n");
+ return -EINVAL;
+ }
+
+ length = size / stride;
+ if (length == 0) {
+ dprintk(1, "%s: length was zero\n", __func__);
+ return -EINVAL;
+ }
+
+ dprintk(1, "Copying %d bytes at %p[%d] - buf size=%d x %d\n", size,
+ runtime->dma_area, buf_pos,
+ (unsigned int)runtime->buffer_size, stride);
+
+ if (buf_pos + length >= runtime->buffer_size) {
+ unsigned int cnt = runtime->buffer_size - buf_pos;
+ memcpy(runtime->dma_area + buf_pos * stride, buf, cnt * stride);
+ memcpy(runtime->dma_area, buf + cnt * stride,
+ length * stride - cnt * stride);
+ } else
+ memcpy(runtime->dma_area + buf_pos * stride, buf,
+ length * stride);
+
+ snd_pcm_stream_lock(substream);
+
+ chip->buf_pos += length;
+ if (chip->buf_pos >= runtime->buffer_size)
+ chip->buf_pos -= runtime->buffer_size;
+
+ chip->period_pos += length;
+ if (chip->period_pos >= runtime->period_size) {
+ chip->period_pos -= runtime->period_size;
+ period_elapsed = 1;
+ }
+
+ snd_pcm_stream_unlock(substream);
+
+ if (period_elapsed)
+ snd_pcm_period_elapsed(substream);
+
+ return 0;
+}
+
+/*
+ * hw_params callback
+ */
+static int snd_tm6000_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ int size, rc;
+
+ size = params_period_bytes(hw_params) * params_periods(hw_params);
+
+ rc = dsp_buffer_alloc(substream, size);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+/*
+ * hw free callback
+ */
+static int snd_tm6000_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
+ struct tm6000_core *core = chip->core;
+
+ if (atomic_read(&core->stream_started) > 0) {
+ atomic_set(&core->stream_started, 0);
+ schedule_work(&core->wq_trigger);
+ }
+
+ dsp_buffer_free(substream);
+ return 0;
+}
+
+/*
+ * prepare callback
+ */
+static int snd_tm6000_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
+
+ chip->buf_pos = 0;
+ chip->period_pos = 0;
+
+ return 0;
+}
+
+
+/*
+ * trigger callback
+ */
+static void audio_trigger(struct work_struct *work)
+{
+ struct tm6000_core *core = container_of(work, struct tm6000_core,
+ wq_trigger);
+ struct snd_tm6000_card *chip = core->adev;
+
+ if (atomic_read(&core->stream_started)) {
+ dprintk(1, "starting capture");
+ _tm6000_start_audio_dma(chip);
+ } else {
+ dprintk(1, "stopping capture");
+ _tm6000_stop_audio_dma(chip);
+ }
+}
+
+static int snd_tm6000_card_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
+ struct tm6000_core *core = chip->core;
+ int err = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
+ case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
+ case SNDRV_PCM_TRIGGER_START:
+ atomic_set(&core->stream_started, 1);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */
+ case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */
+ case SNDRV_PCM_TRIGGER_STOP:
+ atomic_set(&core->stream_started, 0);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ schedule_work(&core->wq_trigger);
+
+ return err;
+}
+/*
+ * pointer callback
+ */
+static snd_pcm_uframes_t snd_tm6000_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
+
+ return chip->buf_pos;
+}
+
+static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
+ unsigned long offset)
+{
+ void *pageptr = subs->runtime->dma_area + offset;
+
+ return vmalloc_to_page(pageptr);
+}
+
+/*
+ * operators
+ */
+static struct snd_pcm_ops snd_tm6000_pcm_ops = {
+ .open = snd_tm6000_pcm_open,
+ .close = snd_tm6000_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_tm6000_hw_params,
+ .hw_free = snd_tm6000_hw_free,
+ .prepare = snd_tm6000_prepare,
+ .trigger = snd_tm6000_card_trigger,
+ .pointer = snd_tm6000_pointer,
+ .page = snd_pcm_get_vmalloc_page,
+};
+
+/*
+ * create a PCM device
+ */
+
+/* FIXME: Control interface - How to control volume/mute? */
+
+/****************************************************************************
+ Basic Flow for Sound Devices
+ ****************************************************************************/
+
+/*
+ * Alsa Constructor - Component probe
+ */
+static int tm6000_audio_init(struct tm6000_core *dev)
+{
+ struct snd_card *card;
+ struct snd_tm6000_card *chip;
+ int rc;
+ static int devnr;
+ char component[14];
+ struct snd_pcm *pcm;
+
+ if (!dev)
+ return 0;
+
+ if (devnr >= SNDRV_CARDS)
+ return -ENODEV;
+
+ if (!enable[devnr])
+ return -ENOENT;
+
+ rc = snd_card_create(index[devnr], "tm6000", THIS_MODULE, 0, &card);
+ if (rc < 0) {
+ snd_printk(KERN_ERR "cannot create card instance %d\n", devnr);
+ return rc;
+ }
+ strcpy(card->driver, "tm6000-alsa");
+ strcpy(card->shortname, "TM5600/60x0");
+ sprintf(card->longname, "TM5600/60x0 Audio at bus %d device %d",
+ dev->udev->bus->busnum, dev->udev->devnum);
+
+ sprintf(component, "USB%04x:%04x",
+ le16_to_cpu(dev->udev->descriptor.idVendor),
+ le16_to_cpu(dev->udev->descriptor.idProduct));
+ snd_component_add(card, component);
+ snd_card_set_dev(card, &dev->udev->dev);
+
+ chip = kzalloc(sizeof(struct snd_tm6000_card), GFP_KERNEL);
+ if (!chip) {
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ chip->core = dev;
+ chip->card = card;
+ dev->adev = chip;
+ spin_lock_init(&chip->reg_lock);
+
+ rc = snd_pcm_new(card, "TM6000 Audio", 0, 0, 1, &pcm);
+ if (rc < 0)
+ goto error_chip;
+
+ pcm->info_flags = 0;
+ pcm->private_data = chip;
+ strcpy(pcm->name, "Trident TM5600/60x0");
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_tm6000_pcm_ops);
+
+ INIT_WORK(&dev->wq_trigger, audio_trigger);
+ rc = snd_card_register(card);
+ if (rc < 0)
+ goto error_chip;
+
+ dprintk(1, "Registered audio driver for %s\n", card->longname);
+
+ return 0;
+
+error_chip:
+ kfree(chip);
+ dev->adev = NULL;
+error:
+ snd_card_free(card);
+ return rc;
+}
+
+static int tm6000_audio_fini(struct tm6000_core *dev)
+{
+ struct snd_tm6000_card *chip = dev->adev;
+
+ if (!dev)
+ return 0;
+
+ if (!chip)
+ return 0;
+
+ if (!chip->card)
+ return 0;
+
+ snd_card_free(chip->card);
+ chip->card = NULL;
+ kfree(chip);
+ dev->adev = NULL;
+
+ return 0;
+}
+
+static struct tm6000_ops audio_ops = {
+ .type = TM6000_AUDIO,
+ .name = "TM6000 Audio Extension",
+ .init = tm6000_audio_init,
+ .fini = tm6000_audio_fini,
+ .fillbuf = tm6000_fillbuf,
+};
+
+static int __init tm6000_alsa_register(void)
+{
+ return tm6000_register_extension(&audio_ops);
+}
+
+static void __exit tm6000_alsa_unregister(void)
+{
+ tm6000_unregister_extension(&audio_ops);
+}
+
+module_init(tm6000_alsa_register);
+module_exit(tm6000_alsa_unregister);
diff --git a/drivers/media/video/tm6000/tm6000-cards.c b/drivers/media/video/tm6000/tm6000-cards.c
new file mode 100644
index 00000000..034659b1
--- /dev/null
+++ b/drivers/media/video/tm6000/tm6000-cards.c
@@ -0,0 +1,1413 @@
+/*
+ * tm6000-cards.c - driver for TM5600/TM6000/TM6010 USB video capture devices
+ *
+ * Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/usb.h>
+#include <linux/slab.h>
+#include <media/v4l2-common.h>
+#include <media/tuner.h>
+#include <media/tvaudio.h>
+#include <media/i2c-addr.h>
+#include <media/rc-map.h>
+
+#include "tm6000.h"
+#include "tm6000-regs.h"
+#include "tuner-xc2028.h"
+#include "xc5000.h"
+
+#define TM6000_BOARD_UNKNOWN 0
+#define TM5600_BOARD_GENERIC 1
+#define TM6000_BOARD_GENERIC 2
+#define TM6010_BOARD_GENERIC 3
+#define TM5600_BOARD_10MOONS_UT821 4
+#define TM5600_BOARD_10MOONS_UT330 5
+#define TM6000_BOARD_ADSTECH_DUAL_TV 6
+#define TM6000_BOARD_FREECOM_AND_SIMILAR 7
+#define TM6000_BOARD_ADSTECH_MINI_DUAL_TV 8
+#define TM6010_BOARD_HAUPPAUGE_900H 9
+#define TM6010_BOARD_BEHOLD_WANDER 10
+#define TM6010_BOARD_BEHOLD_VOYAGER 11
+#define TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE 12
+#define TM6010_BOARD_TWINHAN_TU501 13
+#define TM6010_BOARD_BEHOLD_WANDER_LITE 14
+#define TM6010_BOARD_BEHOLD_VOYAGER_LITE 15
+#define TM5600_BOARD_TERRATEC_GRABSTER 16
+
+#define is_generic(model) ((model == TM6000_BOARD_UNKNOWN) || \
+ (model == TM5600_BOARD_GENERIC) || \
+ (model == TM6000_BOARD_GENERIC) || \
+ (model == TM6010_BOARD_GENERIC))
+
+#define TM6000_MAXBOARDS 16
+static unsigned int card[] = {[0 ... (TM6000_MAXBOARDS - 1)] = UNSET };
+
+module_param_array(card, int, NULL, 0444);
+
+static unsigned long tm6000_devused;
+
+
+struct tm6000_board {
+ char *name;
+ char eename[16]; /* EEPROM name */
+ unsigned eename_size; /* size of EEPROM name */
+ unsigned eename_pos; /* Position where it appears at ROM */
+
+ struct tm6000_capabilities caps;
+
+ enum tm6000_devtype type; /* variant of the chipset */
+ int tuner_type; /* type of the tuner */
+ int tuner_addr; /* tuner address */
+ int demod_addr; /* demodulator address */
+
+ struct tm6000_gpio gpio;
+
+ struct tm6000_input vinput[3];
+ struct tm6000_input rinput;
+
+ char *ir_codes;
+};
+
+static struct tm6000_board tm6000_boards[] = {
+ [TM6000_BOARD_UNKNOWN] = {
+ .name = "Unknown tm6000 video grabber",
+ .caps = {
+ .has_tuner = 1,
+ .has_eeprom = 1,
+ },
+ .gpio = {
+ .tuner_reset = TM6000_GPIO_1,
+ },
+ .vinput = { {
+ .type = TM6000_INPUT_TV,
+ .vmux = TM6000_VMUX_VIDEO_B,
+ .amux = TM6000_AMUX_ADC1,
+ }, {
+ .type = TM6000_INPUT_COMPOSITE1,
+ .vmux = TM6000_VMUX_VIDEO_A,
+ .amux = TM6000_AMUX_ADC2,
+ }, {
+ .type = TM6000_INPUT_SVIDEO,
+ .vmux = TM6000_VMUX_VIDEO_AB,
+ .amux = TM6000_AMUX_ADC2,
+ },
+ },
+ },
+ [TM5600_BOARD_GENERIC] = {
+ .name = "Generic tm5600 board",
+ .type = TM5600,
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0xc2 >> 1,
+ .caps = {
+ .has_tuner = 1,
+ .has_eeprom = 1,
+ },
+ .gpio = {
+ .tuner_reset = TM6000_GPIO_1,
+ },
+ .vinput = { {
+ .type = TM6000_INPUT_TV,
+ .vmux = TM6000_VMUX_VIDEO_B,
+ .amux = TM6000_AMUX_ADC1,
+ }, {
+ .type = TM6000_INPUT_COMPOSITE1,
+ .vmux = TM6000_VMUX_VIDEO_A,
+ .amux = TM6000_AMUX_ADC2,
+ }, {
+ .type = TM6000_INPUT_SVIDEO,
+ .vmux = TM6000_VMUX_VIDEO_AB,
+ .amux = TM6000_AMUX_ADC2,
+ },
+ },
+ },
+ [TM6000_BOARD_GENERIC] = {
+ .name = "Generic tm6000 board",
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0xc2 >> 1,
+ .caps = {
+ .has_tuner = 1,
+ .has_eeprom = 1,
+ },
+ .gpio = {
+ .tuner_reset = TM6000_GPIO_1,
+ },
+ .vinput = { {
+ .type = TM6000_INPUT_TV,
+ .vmux = TM6000_VMUX_VIDEO_B,
+ .amux = TM6000_AMUX_ADC1,
+ }, {
+ .type = TM6000_INPUT_COMPOSITE1,
+ .vmux = TM6000_VMUX_VIDEO_A,
+ .amux = TM6000_AMUX_ADC2,
+ }, {
+ .type = TM6000_INPUT_SVIDEO,
+ .vmux = TM6000_VMUX_VIDEO_AB,
+ .amux = TM6000_AMUX_ADC2,
+ },
+ },
+ },
+ [TM6010_BOARD_GENERIC] = {
+ .name = "Generic tm6010 board",
+ .type = TM6010,
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0xc2 >> 1,
+ .demod_addr = 0x1e >> 1,
+ .caps = {
+ .has_tuner = 1,
+ .has_dvb = 1,
+ .has_zl10353 = 1,
+ .has_eeprom = 1,
+ .has_remote = 1,
+ },
+ .gpio = {
+ .tuner_reset = TM6010_GPIO_2,
+ .tuner_on = TM6010_GPIO_3,
+ .demod_reset = TM6010_GPIO_1,
+ .demod_on = TM6010_GPIO_4,
+ .power_led = TM6010_GPIO_7,
+ .dvb_led = TM6010_GPIO_5,
+ .ir = TM6010_GPIO_0,
+ },
+ .vinput = { {
+ .type = TM6000_INPUT_TV,
+ .vmux = TM6000_VMUX_VIDEO_B,
+ .amux = TM6000_AMUX_SIF1,
+ }, {
+ .type = TM6000_INPUT_COMPOSITE1,
+ .vmux = TM6000_VMUX_VIDEO_A,
+ .amux = TM6000_AMUX_ADC2,
+ }, {
+ .type = TM6000_INPUT_SVIDEO,
+ .vmux = TM6000_VMUX_VIDEO_AB,
+ .amux = TM6000_AMUX_ADC2,
+ },
+ },
+ },
+ [TM5600_BOARD_10MOONS_UT821] = {
+ .name = "10Moons UT 821",
+ .tuner_type = TUNER_XC2028,
+ .eename = { '1', '0', 'M', 'O', 'O', 'N', 'S', '5', '6', '0', '0', 0xff, 0x45, 0x5b},
+ .eename_size = 14,
+ .eename_pos = 0x14,
+ .type = TM5600,
+ .tuner_addr = 0xc2 >> 1,
+ .caps = {
+ .has_tuner = 1,
+ .has_eeprom = 1,
+ },
+ .gpio = {
+ .tuner_reset = TM6000_GPIO_1,
+ },
+ .vinput = { {
+ .type = TM6000_INPUT_TV,
+ .vmux = TM6000_VMUX_VIDEO_B,
+ .amux = TM6000_AMUX_ADC1,
+ }, {
+ .type = TM6000_INPUT_COMPOSITE1,
+ .vmux = TM6000_VMUX_VIDEO_A,
+ .amux = TM6000_AMUX_ADC2,
+ }, {
+ .type = TM6000_INPUT_SVIDEO,
+ .vmux = TM6000_VMUX_VIDEO_AB,
+ .amux = TM6000_AMUX_ADC2,
+ },
+ },
+ },
+ [TM5600_BOARD_10MOONS_UT330] = {
+ .name = "10Moons UT 330",
+ .tuner_type = TUNER_PHILIPS_FQ1216AME_MK4,
+ .tuner_addr = 0xc8 >> 1,
+ .caps = {
+ .has_tuner = 1,
+ .has_dvb = 0,
+ .has_zl10353 = 0,
+ .has_eeprom = 1,
+ },
+ .vinput = { {
+ .type = TM6000_INPUT_TV,
+ .vmux = TM6000_VMUX_VIDEO_B,
+ .amux = TM6000_AMUX_ADC1,
+ }, {
+ .type = TM6000_INPUT_COMPOSITE1,
+ .vmux = TM6000_VMUX_VIDEO_A,
+ .amux = TM6000_AMUX_ADC2,
+ }, {
+ .type = TM6000_INPUT_SVIDEO,
+ .vmux = TM6000_VMUX_VIDEO_AB,
+ .amux = TM6000_AMUX_ADC2,
+ },
+ },
+ },
+ [TM6000_BOARD_ADSTECH_DUAL_TV] = {
+ .name = "ADSTECH Dual TV USB",
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0xc8 >> 1,
+ .caps = {
+ .has_tuner = 1,
+ .has_tda9874 = 1,
+ .has_dvb = 1,
+ .has_zl10353 = 1,
+ .has_eeprom = 1,
+ },
+ .vinput = { {
+ .type = TM6000_INPUT_TV,
+ .vmux = TM6000_VMUX_VIDEO_B,
+ .amux = TM6000_AMUX_ADC1,
+ }, {
+ .type = TM6000_INPUT_COMPOSITE1,
+ .vmux = TM6000_VMUX_VIDEO_A,
+ .amux = TM6000_AMUX_ADC2,
+ }, {
+ .type = TM6000_INPUT_SVIDEO,
+ .vmux = TM6000_VMUX_VIDEO_AB,
+ .amux = TM6000_AMUX_ADC2,
+ },
+ },
+ },
+ [TM6000_BOARD_FREECOM_AND_SIMILAR] = {
+ .name = "Freecom Hybrid Stick / Moka DVB-T Receiver Dual",
+ .tuner_type = TUNER_XC2028, /* has a XC3028 */
+ .tuner_addr = 0xc2 >> 1,
+ .demod_addr = 0x1e >> 1,
+ .caps = {
+ .has_tuner = 1,
+ .has_dvb = 1,
+ .has_zl10353 = 1,
+ .has_eeprom = 0,
+ .has_remote = 1,
+ },
+ .gpio = {
+ .tuner_reset = TM6000_GPIO_4,
+ },
+ .vinput = { {
+ .type = TM6000_INPUT_TV,
+ .vmux = TM6000_VMUX_VIDEO_B,
+ .amux = TM6000_AMUX_ADC1,
+ }, {
+ .type = TM6000_INPUT_COMPOSITE1,
+ .vmux = TM6000_VMUX_VIDEO_A,
+ .amux = TM6000_AMUX_ADC2,
+ }, {
+ .type = TM6000_INPUT_SVIDEO,
+ .vmux = TM6000_VMUX_VIDEO_AB,
+ .amux = TM6000_AMUX_ADC2,
+ },
+ },
+ },
+ [TM6000_BOARD_ADSTECH_MINI_DUAL_TV] = {
+ .name = "ADSTECH Mini Dual TV USB",
+ .tuner_type = TUNER_XC2028, /* has a XC3028 */
+ .tuner_addr = 0xc8 >> 1,
+ .demod_addr = 0x1e >> 1,
+ .caps = {
+ .has_tuner = 1,
+ .has_dvb = 1,
+ .has_zl10353 = 1,
+ .has_eeprom = 0,
+ },
+ .gpio = {
+ .tuner_reset = TM6000_GPIO_4,
+ },
+ .vinput = { {
+ .type = TM6000_INPUT_TV,
+ .vmux = TM6000_VMUX_VIDEO_B,
+ .amux = TM6000_AMUX_ADC1,
+ }, {
+ .type = TM6000_INPUT_COMPOSITE1,
+ .vmux = TM6000_VMUX_VIDEO_A,
+ .amux = TM6000_AMUX_ADC2,
+ }, {
+ .type = TM6000_INPUT_SVIDEO,
+ .vmux = TM6000_VMUX_VIDEO_AB,
+ .amux = TM6000_AMUX_ADC2,
+ },
+ },
+ },
+ [TM6010_BOARD_HAUPPAUGE_900H] = {
+ .name = "Hauppauge WinTV HVR-900H / WinTV USB2-Stick",
+ .eename = { 'H', 0, 'V', 0, 'R', 0, '9', 0, '0', 0, '0', 0, 'H', 0 },
+ .eename_size = 14,
+ .eename_pos = 0x42,
+ .tuner_type = TUNER_XC2028, /* has a XC3028 */
+ .tuner_addr = 0xc2 >> 1,
+ .demod_addr = 0x1e >> 1,
+ .type = TM6010,
+ .ir_codes = RC_MAP_HAUPPAUGE,
+ .caps = {
+ .has_tuner = 1,
+ .has_dvb = 1,
+ .has_zl10353 = 1,
+ .has_eeprom = 1,
+ .has_remote = 1,
+ },
+ .gpio = {
+ .tuner_reset = TM6010_GPIO_2,
+ .tuner_on = TM6010_GPIO_3,
+ .demod_reset = TM6010_GPIO_1,
+ .demod_on = TM6010_GPIO_4,
+ .power_led = TM6010_GPIO_7,
+ .dvb_led = TM6010_GPIO_5,
+ .ir = TM6010_GPIO_0,
+ },
+ .vinput = { {
+ .type = TM6000_INPUT_TV,
+ .vmux = TM6000_VMUX_VIDEO_B,
+ .amux = TM6000_AMUX_SIF1,
+ }, {
+ .type = TM6000_INPUT_COMPOSITE1,
+ .vmux = TM6000_VMUX_VIDEO_A,
+ .amux = TM6000_AMUX_ADC2,
+ }, {
+ .type = TM6000_INPUT_SVIDEO,
+ .vmux = TM6000_VMUX_VIDEO_AB,
+ .amux = TM6000_AMUX_ADC2,
+ },
+ },
+ },
+ [TM6010_BOARD_BEHOLD_WANDER] = {
+ .name = "Beholder Wander DVB-T/TV/FM USB2.0",
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0xc2 >> 1,
+ .demod_addr = 0x1e >> 1,
+ .type = TM6010,
+ .caps = {
+ .has_tuner = 1,
+ .has_dvb = 1,
+ .has_zl10353 = 1,
+ .has_eeprom = 1,
+ .has_remote = 1,
+ .has_radio = 1,
+ },
+ .gpio = {
+ .tuner_reset = TM6010_GPIO_0,
+ .demod_reset = TM6010_GPIO_1,
+ .power_led = TM6010_GPIO_6,
+ },
+ .vinput = { {
+ .type = TM6000_INPUT_TV,
+ .vmux = TM6000_VMUX_VIDEO_B,
+ .amux = TM6000_AMUX_SIF1,
+ }, {
+ .type = TM6000_INPUT_COMPOSITE1,
+ .vmux = TM6000_VMUX_VIDEO_A,
+ .amux = TM6000_AMUX_ADC2,
+ }, {
+ .type = TM6000_INPUT_SVIDEO,
+ .vmux = TM6000_VMUX_VIDEO_AB,
+ .amux = TM6000_AMUX_ADC2,
+ },
+ },
+ .rinput = {
+ .type = TM6000_INPUT_RADIO,
+ .amux = TM6000_AMUX_ADC1,
+ },
+ },
+ [TM6010_BOARD_BEHOLD_VOYAGER] = {
+ .name = "Beholder Voyager TV/FM USB2.0",
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0xc2 >> 1,
+ .type = TM6010,
+ .caps = {
+ .has_tuner = 1,
+ .has_dvb = 0,
+ .has_zl10353 = 0,
+ .has_eeprom = 1,
+ .has_remote = 1,
+ .has_radio = 1,
+ },
+ .gpio = {
+ .tuner_reset = TM6010_GPIO_0,
+ .power_led = TM6010_GPIO_6,
+ },
+ .vinput = { {
+ .type = TM6000_INPUT_TV,
+ .vmux = TM6000_VMUX_VIDEO_B,
+ .amux = TM6000_AMUX_SIF1,
+ }, {
+ .type = TM6000_INPUT_COMPOSITE1,
+ .vmux = TM6000_VMUX_VIDEO_A,
+ .amux = TM6000_AMUX_ADC2,
+ }, {
+ .type = TM6000_INPUT_SVIDEO,
+ .vmux = TM6000_VMUX_VIDEO_AB,
+ .amux = TM6000_AMUX_ADC2,
+ },
+ },
+ .rinput = {
+ .type = TM6000_INPUT_RADIO,
+ .amux = TM6000_AMUX_ADC1,
+ },
+ },
+ [TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE] = {
+ .name = "Terratec Cinergy Hybrid XE / Cinergy Hybrid-Stick",
+ .tuner_type = TUNER_XC2028, /* has a XC3028 */
+ .tuner_addr = 0xc2 >> 1,
+ .demod_addr = 0x1e >> 1,
+ .type = TM6010,
+ .caps = {
+ .has_tuner = 1,
+ .has_dvb = 1,
+ .has_zl10353 = 1,
+ .has_eeprom = 1,
+ .has_remote = 1,
+ .has_radio = 1,
+ },
+ .gpio = {
+ .tuner_reset = TM6010_GPIO_2,
+ .tuner_on = TM6010_GPIO_3,
+ .demod_reset = TM6010_GPIO_1,
+ .demod_on = TM6010_GPIO_4,
+ .power_led = TM6010_GPIO_7,
+ .dvb_led = TM6010_GPIO_5,
+ .ir = TM6010_GPIO_0,
+ },
+ .ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS,
+ .vinput = { {
+ .type = TM6000_INPUT_TV,
+ .vmux = TM6000_VMUX_VIDEO_B,
+ .amux = TM6000_AMUX_SIF1,
+ }, {
+ .type = TM6000_INPUT_COMPOSITE1,
+ .vmux = TM6000_VMUX_VIDEO_A,
+ .amux = TM6000_AMUX_ADC2,
+ }, {
+ .type = TM6000_INPUT_SVIDEO,
+ .vmux = TM6000_VMUX_VIDEO_AB,
+ .amux = TM6000_AMUX_ADC2,
+ },
+ },
+ .rinput = {
+ .type = TM6000_INPUT_RADIO,
+ .amux = TM6000_AMUX_SIF1,
+ },
+ },
+ [TM5600_BOARD_TERRATEC_GRABSTER] = {
+ .name = "Terratec Grabster AV 150/250 MX",
+ .type = TM5600,
+ .tuner_type = TUNER_ABSENT,
+ .vinput = { {
+ .type = TM6000_INPUT_TV,
+ .vmux = TM6000_VMUX_VIDEO_B,
+ .amux = TM6000_AMUX_ADC1,
+ }, {
+ .type = TM6000_INPUT_COMPOSITE1,
+ .vmux = TM6000_VMUX_VIDEO_A,
+ .amux = TM6000_AMUX_ADC2,
+ }, {
+ .type = TM6000_INPUT_SVIDEO,
+ .vmux = TM6000_VMUX_VIDEO_AB,
+ .amux = TM6000_AMUX_ADC2,
+ },
+ },
+ },
+ [TM6010_BOARD_TWINHAN_TU501] = {
+ .name = "Twinhan TU501(704D1)",
+ .tuner_type = TUNER_XC2028, /* has a XC3028 */
+ .tuner_addr = 0xc2 >> 1,
+ .demod_addr = 0x1e >> 1,
+ .type = TM6010,
+ .caps = {
+ .has_tuner = 1,
+ .has_dvb = 1,
+ .has_zl10353 = 1,
+ .has_eeprom = 1,
+ .has_remote = 1,
+ },
+ .gpio = {
+ .tuner_reset = TM6010_GPIO_2,
+ .tuner_on = TM6010_GPIO_3,
+ .demod_reset = TM6010_GPIO_1,
+ .demod_on = TM6010_GPIO_4,
+ .power_led = TM6010_GPIO_7,
+ .dvb_led = TM6010_GPIO_5,
+ .ir = TM6010_GPIO_0,
+ },
+ .vinput = { {
+ .type = TM6000_INPUT_TV,
+ .vmux = TM6000_VMUX_VIDEO_B,
+ .amux = TM6000_AMUX_SIF1,
+ }, {
+ .type = TM6000_INPUT_COMPOSITE1,
+ .vmux = TM6000_VMUX_VIDEO_A,
+ .amux = TM6000_AMUX_ADC2,
+ }, {
+ .type = TM6000_INPUT_SVIDEO,
+ .vmux = TM6000_VMUX_VIDEO_AB,
+ .amux = TM6000_AMUX_ADC2,
+ },
+ },
+ },
+ [TM6010_BOARD_BEHOLD_WANDER_LITE] = {
+ .name = "Beholder Wander Lite DVB-T/TV/FM USB2.0",
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0xc2 >> 1,
+ .demod_addr = 0x1e >> 1,
+ .type = TM6010,
+ .caps = {
+ .has_tuner = 1,
+ .has_dvb = 1,
+ .has_zl10353 = 1,
+ .has_eeprom = 1,
+ .has_remote = 0,
+ .has_radio = 1,
+ },
+ .gpio = {
+ .tuner_reset = TM6010_GPIO_0,
+ .demod_reset = TM6010_GPIO_1,
+ .power_led = TM6010_GPIO_6,
+ },
+ .vinput = { {
+ .type = TM6000_INPUT_TV,
+ .vmux = TM6000_VMUX_VIDEO_B,
+ .amux = TM6000_AMUX_SIF1,
+ },
+ },
+ .rinput = {
+ .type = TM6000_INPUT_RADIO,
+ .amux = TM6000_AMUX_ADC1,
+ },
+ },
+ [TM6010_BOARD_BEHOLD_VOYAGER_LITE] = {
+ .name = "Beholder Voyager Lite TV/FM USB2.0",
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0xc2 >> 1,
+ .type = TM6010,
+ .caps = {
+ .has_tuner = 1,
+ .has_dvb = 0,
+ .has_zl10353 = 0,
+ .has_eeprom = 1,
+ .has_remote = 0,
+ .has_radio = 1,
+ },
+ .gpio = {
+ .tuner_reset = TM6010_GPIO_0,
+ .power_led = TM6010_GPIO_6,
+ },
+ .vinput = { {
+ .type = TM6000_INPUT_TV,
+ .vmux = TM6000_VMUX_VIDEO_B,
+ .amux = TM6000_AMUX_SIF1,
+ },
+ },
+ .rinput = {
+ .type = TM6000_INPUT_RADIO,
+ .amux = TM6000_AMUX_ADC1,
+ },
+ },
+};
+
+/* table of devices that work with this driver */
+static struct usb_device_id tm6000_id_table[] = {
+ { USB_DEVICE(0x6000, 0x0001), .driver_info = TM5600_BOARD_GENERIC },
+ { USB_DEVICE(0x6000, 0x0002), .driver_info = TM6010_BOARD_GENERIC },
+ { USB_DEVICE(0x06e1, 0xf332), .driver_info = TM6000_BOARD_ADSTECH_DUAL_TV },
+ { USB_DEVICE(0x14aa, 0x0620), .driver_info = TM6000_BOARD_FREECOM_AND_SIMILAR },
+ { USB_DEVICE(0x06e1, 0xb339), .driver_info = TM6000_BOARD_ADSTECH_MINI_DUAL_TV },
+ { USB_DEVICE(0x2040, 0x6600), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
+ { USB_DEVICE(0x2040, 0x6601), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
+ { USB_DEVICE(0x2040, 0x6610), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
+ { USB_DEVICE(0x2040, 0x6611), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
+ { USB_DEVICE(0x6000, 0xdec0), .driver_info = TM6010_BOARD_BEHOLD_WANDER },
+ { USB_DEVICE(0x6000, 0xdec1), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER },
+ { USB_DEVICE(0x0ccd, 0x0086), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE },
+ { USB_DEVICE(0x0ccd, 0x00A5), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE },
+ { USB_DEVICE(0x0ccd, 0x0079), .driver_info = TM5600_BOARD_TERRATEC_GRABSTER },
+ { USB_DEVICE(0x13d3, 0x3240), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
+ { USB_DEVICE(0x13d3, 0x3241), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
+ { USB_DEVICE(0x13d3, 0x3243), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
+ { USB_DEVICE(0x13d3, 0x3264), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
+ { USB_DEVICE(0x6000, 0xdec2), .driver_info = TM6010_BOARD_BEHOLD_WANDER_LITE },
+ { USB_DEVICE(0x6000, 0xdec3), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER_LITE },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, tm6000_id_table);
+
+/* Control power led for show some activity */
+void tm6000_flash_led(struct tm6000_core *dev, u8 state)
+{
+ /* Power LED unconfigured */
+ if (!dev->gpio.power_led)
+ return;
+
+ /* ON Power LED */
+ if (state) {
+ switch (dev->model) {
+ case TM6010_BOARD_HAUPPAUGE_900H:
+ case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
+ case TM6010_BOARD_TWINHAN_TU501:
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+ dev->gpio.power_led, 0x00);
+ break;
+ case TM6010_BOARD_BEHOLD_WANDER:
+ case TM6010_BOARD_BEHOLD_VOYAGER:
+ case TM6010_BOARD_BEHOLD_WANDER_LITE:
+ case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+ dev->gpio.power_led, 0x01);
+ break;
+ }
+ }
+ /* OFF Power LED */
+ else {
+ switch (dev->model) {
+ case TM6010_BOARD_HAUPPAUGE_900H:
+ case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
+ case TM6010_BOARD_TWINHAN_TU501:
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+ dev->gpio.power_led, 0x01);
+ break;
+ case TM6010_BOARD_BEHOLD_WANDER:
+ case TM6010_BOARD_BEHOLD_VOYAGER:
+ case TM6010_BOARD_BEHOLD_WANDER_LITE:
+ case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+ dev->gpio.power_led, 0x00);
+ break;
+ }
+ }
+}
+
+/* Tuner callback to provide the proper gpio changes needed for xc5000 */
+int tm6000_xc5000_callback(void *ptr, int component, int command, int arg)
+{
+ int rc = 0;
+ struct tm6000_core *dev = ptr;
+
+ if (dev->tuner_type != TUNER_XC5000)
+ return 0;
+
+ switch (command) {
+ case XC5000_TUNER_RESET:
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+ dev->gpio.tuner_reset, 0x01);
+ msleep(15);
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+ dev->gpio.tuner_reset, 0x00);
+ msleep(15);
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+ dev->gpio.tuner_reset, 0x01);
+ break;
+ }
+ return rc;
+}
+EXPORT_SYMBOL_GPL(tm6000_xc5000_callback);
+
+/* Tuner callback to provide the proper gpio changes needed for xc2028 */
+
+int tm6000_tuner_callback(void *ptr, int component, int command, int arg)
+{
+ int rc = 0;
+ struct tm6000_core *dev = ptr;
+
+ if (dev->tuner_type != TUNER_XC2028)
+ return 0;
+
+ switch (command) {
+ case XC2028_RESET_CLK:
+ tm6000_ir_wait(dev, 0);
+
+ tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT,
+ 0x02, arg);
+ msleep(10);
+ rc = tm6000_i2c_reset(dev, 10);
+ break;
+ case XC2028_TUNER_RESET:
+ /* Reset codes during load firmware */
+ switch (arg) {
+ case 0:
+ /* newer tuner can faster reset */
+ switch (dev->model) {
+ case TM5600_BOARD_10MOONS_UT821:
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+ dev->gpio.tuner_reset, 0x01);
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+ 0x300, 0x01);
+ msleep(10);
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+ dev->gpio.tuner_reset, 0x00);
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+ 0x300, 0x00);
+ msleep(10);
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+ dev->gpio.tuner_reset, 0x01);
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+ 0x300, 0x01);
+ break;
+ case TM6010_BOARD_HAUPPAUGE_900H:
+ case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
+ case TM6010_BOARD_TWINHAN_TU501:
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+ dev->gpio.tuner_reset, 0x01);
+ msleep(60);
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+ dev->gpio.tuner_reset, 0x00);
+ msleep(75);
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+ dev->gpio.tuner_reset, 0x01);
+ msleep(60);
+ break;
+ default:
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+ dev->gpio.tuner_reset, 0x00);
+ msleep(130);
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+ dev->gpio.tuner_reset, 0x01);
+ msleep(130);
+ break;
+ }
+
+ tm6000_ir_wait(dev, 1);
+ break;
+ case 1:
+ tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT,
+ 0x02, 0x01);
+ msleep(10);
+ break;
+ case 2:
+ rc = tm6000_i2c_reset(dev, 100);
+ break;
+ }
+ break;
+ case XC2028_I2C_FLUSH:
+ tm6000_set_reg(dev, REQ_50_SET_START, 0, 0);
+ tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0);
+ break;
+ }
+ return rc;
+}
+EXPORT_SYMBOL_GPL(tm6000_tuner_callback);
+
+int tm6000_cards_setup(struct tm6000_core *dev)
+{
+ /*
+ * Board-specific initialization sequence. Handles all GPIO
+ * initialization sequences that are board-specific.
+ * Up to now, all found devices use GPIO1 and GPIO4 at the same way.
+ * Probably, they're all based on some reference device. Due to that,
+ * there's a common routine at the end to handle those GPIO's. Devices
+ * that use different pinups or init sequences can just return at
+ * the board-specific session.
+ */
+ switch (dev->model) {
+ case TM6010_BOARD_HAUPPAUGE_900H:
+ case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
+ case TM6010_BOARD_TWINHAN_TU501:
+ case TM6010_BOARD_GENERIC:
+ /* Turn xceive 3028 on */
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.tuner_on, 0x01);
+ msleep(15);
+ /* Turn zarlink zl10353 on */
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00);
+ msleep(15);
+ /* Reset zarlink zl10353 */
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00);
+ msleep(50);
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01);
+ msleep(15);
+ /* Turn zarlink zl10353 off */
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x01);
+ msleep(15);
+ /* ir ? */
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.ir, 0x01);
+ msleep(15);
+ /* Power led on (blue) */
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x00);
+ msleep(15);
+ /* DVB led off (orange) */
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.dvb_led, 0x01);
+ msleep(15);
+ /* Turn zarlink zl10353 on */
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00);
+ msleep(15);
+ break;
+ case TM6010_BOARD_BEHOLD_WANDER:
+ case TM6010_BOARD_BEHOLD_WANDER_LITE:
+ /* Power led on (blue) */
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01);
+ msleep(15);
+ /* Reset zarlink zl10353 */
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00);
+ msleep(50);
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01);
+ msleep(15);
+ break;
+ case TM6010_BOARD_BEHOLD_VOYAGER:
+ case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
+ /* Power led on (blue) */
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01);
+ msleep(15);
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * Default initialization. Most of the devices seem to use GPIO1
+ * and GPIO4.on the same way, so, this handles the common sequence
+ * used by most devices.
+ * If a device uses a different sequence or different GPIO pins for
+ * reset, just add the code at the board-specific part
+ */
+
+ if (dev->gpio.tuner_reset) {
+ int rc;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+ dev->gpio.tuner_reset, 0x00);
+ if (rc < 0) {
+ printk(KERN_ERR "Error %i doing tuner reset\n", rc);
+ return rc;
+ }
+
+ msleep(10); /* Just to be conservative */
+ rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+ dev->gpio.tuner_reset, 0x01);
+ if (rc < 0) {
+ printk(KERN_ERR "Error %i doing tuner reset\n", rc);
+ return rc;
+ }
+ }
+ } else {
+ printk(KERN_ERR "Tuner reset is not configured\n");
+ return -1;
+ }
+
+ msleep(50);
+
+ return 0;
+};
+
+static void tm6000_config_tuner(struct tm6000_core *dev)
+{
+ struct tuner_setup tun_setup;
+
+ /* Load tuner module */
+ v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+ "tuner", dev->tuner_addr, NULL);
+
+ memset(&tun_setup, 0, sizeof(tun_setup));
+ tun_setup.type = dev->tuner_type;
+ tun_setup.addr = dev->tuner_addr;
+
+ tun_setup.mode_mask = 0;
+ if (dev->caps.has_tuner)
+ tun_setup.mode_mask |= (T_ANALOG_TV | T_RADIO);
+
+ switch (dev->tuner_type) {
+ case TUNER_XC2028:
+ tun_setup.tuner_callback = tm6000_tuner_callback;
+ break;
+ case TUNER_XC5000:
+ tun_setup.tuner_callback = tm6000_xc5000_callback;
+ break;
+ }
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup);
+
+ switch (dev->tuner_type) {
+ case TUNER_XC2028: {
+ struct v4l2_priv_tun_config xc2028_cfg;
+ struct xc2028_ctrl ctl;
+
+ memset(&xc2028_cfg, 0, sizeof(xc2028_cfg));
+ memset(&ctl, 0, sizeof(ctl));
+
+ ctl.demod = XC3028_FE_ZARLINK456;
+
+ xc2028_cfg.tuner = TUNER_XC2028;
+ xc2028_cfg.priv = &ctl;
+
+ switch (dev->model) {
+ case TM6010_BOARD_HAUPPAUGE_900H:
+ case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
+ case TM6010_BOARD_TWINHAN_TU501:
+ ctl.max_len = 80;
+ ctl.fname = "xc3028L-v36.fw";
+ break;
+ default:
+ if (dev->dev_type == TM6010)
+ ctl.fname = "xc3028-v27.fw";
+ else
+ ctl.fname = "xc3028-v24.fw";
+ }
+
+ printk(KERN_INFO "Setting firmware parameters for xc2028\n");
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config,
+ &xc2028_cfg);
+
+ }
+ break;
+ case TUNER_XC5000:
+ {
+ struct v4l2_priv_tun_config xc5000_cfg;
+ struct xc5000_config ctl = {
+ .i2c_address = dev->tuner_addr,
+ .if_khz = 4570,
+ .radio_input = XC5000_RADIO_FM1_MONO,
+ };
+
+ xc5000_cfg.tuner = TUNER_XC5000;
+ xc5000_cfg.priv = &ctl;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config,
+ &xc5000_cfg);
+ }
+ break;
+ default:
+ printk(KERN_INFO "Unknown tuner type. Tuner is not configured.\n");
+ break;
+ }
+}
+
+static int fill_board_specific_data(struct tm6000_core *dev)
+{
+ int rc;
+
+ dev->dev_type = tm6000_boards[dev->model].type;
+ dev->tuner_type = tm6000_boards[dev->model].tuner_type;
+ dev->tuner_addr = tm6000_boards[dev->model].tuner_addr;
+
+ dev->gpio = tm6000_boards[dev->model].gpio;
+
+ dev->ir_codes = tm6000_boards[dev->model].ir_codes;
+
+ dev->demod_addr = tm6000_boards[dev->model].demod_addr;
+
+ dev->caps = tm6000_boards[dev->model].caps;
+
+ dev->vinput[0] = tm6000_boards[dev->model].vinput[0];
+ dev->vinput[1] = tm6000_boards[dev->model].vinput[1];
+ dev->vinput[2] = tm6000_boards[dev->model].vinput[2];
+ dev->rinput = tm6000_boards[dev->model].rinput;
+
+ /* setup per-model quirks */
+ switch (dev->model) {
+ case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
+ case TM6010_BOARD_HAUPPAUGE_900H:
+ dev->quirks |= TM6000_QUIRK_NO_USB_DELAY;
+ break;
+
+ default:
+ break;
+ }
+
+ /* initialize hardware */
+ rc = tm6000_init(dev);
+ if (rc < 0)
+ return rc;
+
+ return v4l2_device_register(&dev->udev->dev, &dev->v4l2_dev);
+}
+
+
+static void use_alternative_detection_method(struct tm6000_core *dev)
+{
+ int i, model = -1;
+
+ if (!dev->eedata_size)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(tm6000_boards); i++) {
+ if (!tm6000_boards[i].eename_size)
+ continue;
+ if (dev->eedata_size < tm6000_boards[i].eename_pos +
+ tm6000_boards[i].eename_size)
+ continue;
+
+ if (!memcmp(&dev->eedata[tm6000_boards[i].eename_pos],
+ tm6000_boards[i].eename,
+ tm6000_boards[i].eename_size)) {
+ model = i;
+ break;
+ }
+ }
+ if (model < 0) {
+ printk(KERN_INFO "Device has eeprom but is currently unknown\n");
+ return;
+ }
+
+ dev->model = model;
+
+ printk(KERN_INFO "Device identified via eeprom as %s (type = %d)\n",
+ tm6000_boards[model].name, model);
+}
+
+#if defined(CONFIG_MODULES) && defined(MODULE)
+static void request_module_async(struct work_struct *work)
+{
+ struct tm6000_core *dev = container_of(work, struct tm6000_core,
+ request_module_wk);
+
+ request_module("tm6000-alsa");
+
+ if (dev->caps.has_dvb)
+ request_module("tm6000-dvb");
+}
+
+static void request_modules(struct tm6000_core *dev)
+{
+ INIT_WORK(&dev->request_module_wk, request_module_async);
+ schedule_work(&dev->request_module_wk);
+}
+
+static void flush_request_modules(struct tm6000_core *dev)
+{
+ flush_work_sync(&dev->request_module_wk);
+}
+#else
+#define request_modules(dev)
+#define flush_request_modules(dev)
+#endif /* CONFIG_MODULES */
+
+static int tm6000_init_dev(struct tm6000_core *dev)
+{
+ struct v4l2_frequency f;
+ int rc = 0;
+
+ mutex_init(&dev->lock);
+ mutex_lock(&dev->lock);
+
+ if (!is_generic(dev->model)) {
+ rc = fill_board_specific_data(dev);
+ if (rc < 0)
+ goto err;
+
+ /* register i2c bus */
+ rc = tm6000_i2c_register(dev);
+ if (rc < 0)
+ goto err;
+ } else {
+ /* register i2c bus */
+ rc = tm6000_i2c_register(dev);
+ if (rc < 0)
+ goto err;
+
+ use_alternative_detection_method(dev);
+
+ rc = fill_board_specific_data(dev);
+ if (rc < 0)
+ goto err;
+ }
+
+ /* Default values for STD and resolutions */
+ dev->width = 720;
+ dev->height = 480;
+ dev->norm = V4L2_STD_PAL_M;
+
+ /* Configure tuner */
+ tm6000_config_tuner(dev);
+
+ /* Set video standard */
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
+
+ /* Set tuner frequency - also loads firmware on xc2028/xc3028 */
+ f.tuner = 0;
+ f.type = V4L2_TUNER_ANALOG_TV;
+ f.frequency = 3092; /* 193.25 MHz */
+ dev->freq = f.frequency;
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
+
+ if (dev->caps.has_tda9874)
+ v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+ "tvaudio", I2C_ADDR_TDA9874, NULL);
+
+ /* register and initialize V4L2 */
+ rc = tm6000_v4l2_register(dev);
+ if (rc < 0)
+ goto err;
+
+ tm6000_add_into_devlist(dev);
+ tm6000_init_extension(dev);
+
+ tm6000_ir_init(dev);
+
+ request_modules(dev);
+
+ mutex_unlock(&dev->lock);
+ return 0;
+
+err:
+ mutex_unlock(&dev->lock);
+ return rc;
+}
+
+/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */
+#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
+
+static void get_max_endpoint(struct usb_device *udev,
+ struct usb_host_interface *alt,
+ char *msgtype,
+ struct usb_host_endpoint *curr_e,
+ struct tm6000_endpoint *tm_ep)
+{
+ u16 tmp = le16_to_cpu(curr_e->desc.wMaxPacketSize);
+ unsigned int size = tmp & 0x7ff;
+
+ if (udev->speed == USB_SPEED_HIGH)
+ size = size * hb_mult(tmp);
+
+ if (size > tm_ep->maxsize) {
+ tm_ep->endp = curr_e;
+ tm_ep->maxsize = size;
+ tm_ep->bInterfaceNumber = alt->desc.bInterfaceNumber;
+ tm_ep->bAlternateSetting = alt->desc.bAlternateSetting;
+
+ printk(KERN_INFO "tm6000: %s endpoint: 0x%02x (max size=%u bytes)\n",
+ msgtype, curr_e->desc.bEndpointAddress,
+ size);
+ }
+}
+
+/*
+ * tm6000_usb_probe()
+ * checks for supported devices
+ */
+static int tm6000_usb_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *usbdev;
+ struct tm6000_core *dev = NULL;
+ int i, rc = 0;
+ int nr = 0;
+ char *speed;
+
+ usbdev = usb_get_dev(interface_to_usbdev(interface));
+
+ /* Selects the proper interface */
+ rc = usb_set_interface(usbdev, 0, 1);
+ if (rc < 0)
+ goto err;
+
+ /* Check to see next free device and mark as used */
+ nr = find_first_zero_bit(&tm6000_devused, TM6000_MAXBOARDS);
+ if (nr >= TM6000_MAXBOARDS) {
+ printk(KERN_ERR "tm6000: Supports only %i tm60xx boards.\n", TM6000_MAXBOARDS);
+ usb_put_dev(usbdev);
+ return -ENOMEM;
+ }
+
+ /* Create and initialize dev struct */
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL) {
+ printk(KERN_ERR "tm6000" ": out of memory!\n");
+ usb_put_dev(usbdev);
+ return -ENOMEM;
+ }
+ spin_lock_init(&dev->slock);
+ mutex_init(&dev->usb_lock);
+
+ /* Increment usage count */
+ set_bit(nr, &tm6000_devused);
+ snprintf(dev->name, 29, "tm6000 #%d", nr);
+
+ dev->model = id->driver_info;
+ if (card[nr] < ARRAY_SIZE(tm6000_boards))
+ dev->model = card[nr];
+
+ dev->udev = usbdev;
+ dev->devno = nr;
+
+ switch (usbdev->speed) {
+ case USB_SPEED_LOW:
+ speed = "1.5";
+ break;
+ case USB_SPEED_UNKNOWN:
+ case USB_SPEED_FULL:
+ speed = "12";
+ break;
+ case USB_SPEED_HIGH:
+ speed = "480";
+ break;
+ default:
+ speed = "unknown";
+ }
+
+ /* Get endpoints */
+ for (i = 0; i < interface->num_altsetting; i++) {
+ int ep;
+
+ for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) {
+ struct usb_host_endpoint *e;
+ int dir_out;
+
+ e = &interface->altsetting[i].endpoint[ep];
+
+ dir_out = ((e->desc.bEndpointAddress &
+ USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT);
+
+ printk(KERN_INFO "tm6000: alt %d, interface %i, class %i\n",
+ i,
+ interface->altsetting[i].desc.bInterfaceNumber,
+ interface->altsetting[i].desc.bInterfaceClass);
+
+ switch (e->desc.bmAttributes) {
+ case USB_ENDPOINT_XFER_BULK:
+ if (!dir_out) {
+ get_max_endpoint(usbdev,
+ &interface->altsetting[i],
+ "Bulk IN", e,
+ &dev->bulk_in);
+ } else {
+ get_max_endpoint(usbdev,
+ &interface->altsetting[i],
+ "Bulk OUT", e,
+ &dev->bulk_out);
+ }
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ if (!dir_out) {
+ get_max_endpoint(usbdev,
+ &interface->altsetting[i],
+ "ISOC IN", e,
+ &dev->isoc_in);
+ } else {
+ get_max_endpoint(usbdev,
+ &interface->altsetting[i],
+ "ISOC OUT", e,
+ &dev->isoc_out);
+ }
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ if (!dir_out) {
+ get_max_endpoint(usbdev,
+ &interface->altsetting[i],
+ "INT IN", e,
+ &dev->int_in);
+ } else {
+ get_max_endpoint(usbdev,
+ &interface->altsetting[i],
+ "INT OUT", e,
+ &dev->int_out);
+ }
+ break;
+ }
+ }
+ }
+
+
+ printk(KERN_INFO "tm6000: New video device @ %s Mbps (%04x:%04x, ifnum %d)\n",
+ speed,
+ le16_to_cpu(dev->udev->descriptor.idVendor),
+ le16_to_cpu(dev->udev->descriptor.idProduct),
+ interface->altsetting->desc.bInterfaceNumber);
+
+/* check if the the device has the iso in endpoint at the correct place */
+ if (!dev->isoc_in.endp) {
+ printk(KERN_ERR "tm6000: probing error: no IN ISOC endpoint!\n");
+ rc = -ENODEV;
+
+ goto err;
+ }
+
+ /* save our data pointer in this interface device */
+ usb_set_intfdata(interface, dev);
+
+ printk(KERN_INFO "tm6000: Found %s\n", tm6000_boards[dev->model].name);
+
+ rc = tm6000_init_dev(dev);
+ if (rc < 0)
+ goto err;
+
+ return 0;
+
+err:
+ printk(KERN_ERR "tm6000: Error %d while registering\n", rc);
+
+ clear_bit(nr, &tm6000_devused);
+ usb_put_dev(usbdev);
+
+ kfree(dev);
+ return rc;
+}
+
+/*
+ * tm6000_usb_disconnect()
+ * called when the device gets diconencted
+ * video device will be unregistered on v4l2_close in case it is still open
+ */
+static void tm6000_usb_disconnect(struct usb_interface *interface)
+{
+ struct tm6000_core *dev = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+
+ if (!dev)
+ return;
+
+ printk(KERN_INFO "tm6000: disconnecting %s\n", dev->name);
+
+ flush_request_modules(dev);
+
+ tm6000_ir_fini(dev);
+
+ if (dev->gpio.power_led) {
+ switch (dev->model) {
+ case TM6010_BOARD_HAUPPAUGE_900H:
+ case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
+ case TM6010_BOARD_TWINHAN_TU501:
+ /* Power led off */
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+ dev->gpio.power_led, 0x01);
+ msleep(15);
+ break;
+ case TM6010_BOARD_BEHOLD_WANDER:
+ case TM6010_BOARD_BEHOLD_VOYAGER:
+ case TM6010_BOARD_BEHOLD_WANDER_LITE:
+ case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
+ /* Power led off */
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+ dev->gpio.power_led, 0x00);
+ msleep(15);
+ break;
+ }
+ }
+ tm6000_v4l2_unregister(dev);
+
+ tm6000_i2c_unregister(dev);
+
+ v4l2_device_unregister(&dev->v4l2_dev);
+
+ dev->state |= DEV_DISCONNECTED;
+
+ usb_put_dev(dev->udev);
+
+ tm6000_close_extension(dev);
+ tm6000_remove_from_devlist(dev);
+
+ clear_bit(dev->devno, &tm6000_devused);
+ kfree(dev);
+}
+
+static struct usb_driver tm6000_usb_driver = {
+ .name = "tm6000",
+ .probe = tm6000_usb_probe,
+ .disconnect = tm6000_usb_disconnect,
+ .id_table = tm6000_id_table,
+};
+
+module_usb_driver(tm6000_usb_driver);
+
+MODULE_DESCRIPTION("Trident TVMaster TM5600/TM6000/TM6010 USB2 adapter");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/tm6000/tm6000-core.c b/drivers/media/video/tm6000/tm6000-core.c
new file mode 100644
index 00000000..22cc0116
--- /dev/null
+++ b/drivers/media/video/tm6000/tm6000-core.c
@@ -0,0 +1,935 @@
+/*
+ * tm6000-core.c - driver for TM5600/TM6000/TM6010 USB video capture devices
+ *
+ * Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
+ *
+ * Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com>
+ * - DVB-T support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include "tm6000.h"
+#include "tm6000-regs.h"
+#include <media/v4l2-common.h>
+#include <media/tuner.h>
+
+#define USB_TIMEOUT (5 * HZ) /* ms */
+
+int tm6000_read_write_usb(struct tm6000_core *dev, u8 req_type, u8 req,
+ u16 value, u16 index, u8 *buf, u16 len)
+{
+ int ret, i;
+ unsigned int pipe;
+ u8 *data = NULL;
+ int delay = 5000;
+
+ mutex_lock(&dev->usb_lock);
+
+ if (len)
+ data = kzalloc(len, GFP_KERNEL);
+
+ if (req_type & USB_DIR_IN)
+ pipe = usb_rcvctrlpipe(dev->udev, 0);
+ else {
+ pipe = usb_sndctrlpipe(dev->udev, 0);
+ memcpy(data, buf, len);
+ }
+
+ if (tm6000_debug & V4L2_DEBUG_I2C) {
+ printk(KERN_DEBUG "(dev %p, pipe %08x): ", dev->udev, pipe);
+
+ printk(KERN_CONT "%s: %02x %02x %02x %02x %02x %02x %02x %02x ",
+ (req_type & USB_DIR_IN) ? " IN" : "OUT",
+ req_type, req, value&0xff, value>>8, index&0xff,
+ index>>8, len&0xff, len>>8);
+
+ if (!(req_type & USB_DIR_IN)) {
+ printk(KERN_CONT ">>> ");
+ for (i = 0; i < len; i++)
+ printk(KERN_CONT " %02x", buf[i]);
+ printk(KERN_CONT "\n");
+ }
+ }
+
+ ret = usb_control_msg(dev->udev, pipe, req, req_type, value, index,
+ data, len, USB_TIMEOUT);
+
+ if (req_type & USB_DIR_IN)
+ memcpy(buf, data, len);
+
+ if (tm6000_debug & V4L2_DEBUG_I2C) {
+ if (ret < 0) {
+ if (req_type & USB_DIR_IN)
+ printk(KERN_DEBUG "<<< (len=%d)\n", len);
+
+ printk(KERN_CONT "%s: Error #%d\n", __func__, ret);
+ } else if (req_type & USB_DIR_IN) {
+ printk(KERN_CONT "<<< ");
+ for (i = 0; i < len; i++)
+ printk(KERN_CONT " %02x", buf[i]);
+ printk(KERN_CONT "\n");
+ }
+ }
+
+ kfree(data);
+
+ if (dev->quirks & TM6000_QUIRK_NO_USB_DELAY)
+ delay = 0;
+
+ if (req == REQ_16_SET_GET_I2C_WR1_RDN && !(req_type & USB_DIR_IN)) {
+ unsigned int tsleep;
+ /* Calculate delay time, 14000us for 64 bytes */
+ tsleep = (len * 200) + 200;
+ if (tsleep < delay)
+ tsleep = delay;
+ usleep_range(tsleep, tsleep + 1000);
+ }
+ else if (delay)
+ usleep_range(delay, delay + 1000);
+
+ mutex_unlock(&dev->usb_lock);
+ return ret;
+}
+
+int tm6000_set_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index)
+{
+ return
+ tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR,
+ req, value, index, NULL, 0);
+}
+EXPORT_SYMBOL_GPL(tm6000_set_reg);
+
+int tm6000_get_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index)
+{
+ int rc;
+ u8 buf[1];
+
+ rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req,
+ value, index, buf, 1);
+
+ if (rc < 0)
+ return rc;
+
+ return *buf;
+}
+EXPORT_SYMBOL_GPL(tm6000_get_reg);
+
+int tm6000_set_reg_mask(struct tm6000_core *dev, u8 req, u16 value,
+ u16 index, u16 mask)
+{
+ int rc;
+ u8 buf[1];
+ u8 new_index;
+
+ rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req,
+ value, 0, buf, 1);
+
+ if (rc < 0)
+ return rc;
+
+ new_index = (buf[0] & ~mask) | (index & mask);
+
+ if (new_index == buf[0])
+ return 0;
+
+ return tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR,
+ req, value, new_index, NULL, 0);
+}
+EXPORT_SYMBOL_GPL(tm6000_set_reg_mask);
+
+int tm6000_get_reg16(struct tm6000_core *dev, u8 req, u16 value, u16 index)
+{
+ int rc;
+ u8 buf[2];
+
+ rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req,
+ value, index, buf, 2);
+
+ if (rc < 0)
+ return rc;
+
+ return buf[1]|buf[0]<<8;
+}
+
+int tm6000_get_reg32(struct tm6000_core *dev, u8 req, u16 value, u16 index)
+{
+ int rc;
+ u8 buf[4];
+
+ rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req,
+ value, index, buf, 4);
+
+ if (rc < 0)
+ return rc;
+
+ return buf[3] | buf[2] << 8 | buf[1] << 16 | buf[0] << 24;
+}
+
+int tm6000_i2c_reset(struct tm6000_core *dev, u16 tsleep)
+{
+ int rc;
+
+ rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_CLK, 0);
+ if (rc < 0)
+ return rc;
+
+ msleep(tsleep);
+
+ rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_CLK, 1);
+ msleep(tsleep);
+
+ return rc;
+}
+
+void tm6000_set_fourcc_format(struct tm6000_core *dev)
+{
+ if (dev->dev_type == TM6010) {
+ int val;
+
+ val = tm6000_get_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, 0) & 0xfc;
+ if (dev->fourcc == V4L2_PIX_FMT_UYVY)
+ tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, val);
+ else
+ tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, val | 1);
+ } else {
+ if (dev->fourcc == V4L2_PIX_FMT_UYVY)
+ tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0xd0);
+ else
+ tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0x90);
+ }
+}
+
+static void tm6000_set_vbi(struct tm6000_core *dev)
+{
+ /*
+ * FIXME:
+ * VBI lines and start/end are different between 60Hz and 50Hz
+ * So, it is very likely that we need to change the config to
+ * something that takes it into account, doing something different
+ * if (dev->norm & V4L2_STD_525_60)
+ */
+
+ if (dev->dev_type == TM6010) {
+ tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01);
+ tm6000_set_reg(dev, TM6010_REQ07_R41_TELETEXT_VBI_CODE1, 0x27);
+ tm6000_set_reg(dev, TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55);
+ tm6000_set_reg(dev, TM6010_REQ07_R43_VBI_DATA_TYPE_LINE7, 0x66);
+ tm6000_set_reg(dev, TM6010_REQ07_R44_VBI_DATA_TYPE_LINE8, 0x66);
+ tm6000_set_reg(dev, TM6010_REQ07_R45_VBI_DATA_TYPE_LINE9, 0x66);
+ tm6000_set_reg(dev,
+ TM6010_REQ07_R46_VBI_DATA_TYPE_LINE10, 0x66);
+ tm6000_set_reg(dev,
+ TM6010_REQ07_R47_VBI_DATA_TYPE_LINE11, 0x66);
+ tm6000_set_reg(dev,
+ TM6010_REQ07_R48_VBI_DATA_TYPE_LINE12, 0x66);
+ tm6000_set_reg(dev,
+ TM6010_REQ07_R49_VBI_DATA_TYPE_LINE13, 0x66);
+ tm6000_set_reg(dev,
+ TM6010_REQ07_R4A_VBI_DATA_TYPE_LINE14, 0x66);
+ tm6000_set_reg(dev,
+ TM6010_REQ07_R4B_VBI_DATA_TYPE_LINE15, 0x66);
+ tm6000_set_reg(dev,
+ TM6010_REQ07_R4C_VBI_DATA_TYPE_LINE16, 0x66);
+ tm6000_set_reg(dev,
+ TM6010_REQ07_R4D_VBI_DATA_TYPE_LINE17, 0x66);
+ tm6000_set_reg(dev,
+ TM6010_REQ07_R4E_VBI_DATA_TYPE_LINE18, 0x66);
+ tm6000_set_reg(dev,
+ TM6010_REQ07_R4F_VBI_DATA_TYPE_LINE19, 0x66);
+ tm6000_set_reg(dev,
+ TM6010_REQ07_R50_VBI_DATA_TYPE_LINE20, 0x66);
+ tm6000_set_reg(dev,
+ TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x66);
+ tm6000_set_reg(dev,
+ TM6010_REQ07_R52_VBI_DATA_TYPE_LINE22, 0x66);
+ tm6000_set_reg(dev,
+ TM6010_REQ07_R53_VBI_DATA_TYPE_LINE23, 0x00);
+ tm6000_set_reg(dev,
+ TM6010_REQ07_R54_VBI_DATA_TYPE_RLINES, 0x00);
+ tm6000_set_reg(dev,
+ TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01);
+ tm6000_set_reg(dev,
+ TM6010_REQ07_R56_VBI_LOOP_FILTER_I_GAIN, 0x00);
+ tm6000_set_reg(dev,
+ TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02);
+ tm6000_set_reg(dev, TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35);
+ tm6000_set_reg(dev, TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0);
+ tm6000_set_reg(dev, TM6010_REQ07_R5A_VBI_TELETEXT_DTO1, 0x11);
+ tm6000_set_reg(dev, TM6010_REQ07_R5B_VBI_TELETEXT_DTO0, 0x4c);
+ tm6000_set_reg(dev, TM6010_REQ07_R40_TELETEXT_VBI_CODE0, 0x01);
+ tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x00);
+ }
+}
+
+int tm6000_init_analog_mode(struct tm6000_core *dev)
+{
+ struct v4l2_frequency f;
+
+ if (dev->dev_type == TM6010) {
+ u8 active = TM6010_REQ07_RCC_ACTIVE_IF_AUDIO_ENABLE;
+
+ if (!dev->radio)
+ active |= TM6010_REQ07_RCC_ACTIVE_IF_VIDEO_ENABLE;
+
+ /* Enable video and audio */
+ tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_IF,
+ active, 0x60);
+ /* Disable TS input */
+ tm6000_set_reg_mask(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE,
+ 0x00, 0x40);
+ } else {
+ /* Enables soft reset */
+ tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01);
+
+ if (dev->scaler)
+ /* Disable Hfilter and Enable TS Drop err */
+ tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x20);
+ else /* Enable Hfilter and disable TS Drop err */
+ tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x80);
+
+ tm6000_set_reg(dev, TM6010_REQ07_RC3_HSTART1, 0x88);
+ tm6000_set_reg(dev, TM6000_REQ07_RDA_CLK_SEL, 0x23);
+ tm6000_set_reg(dev, TM6010_REQ07_RD1_ADDR_FOR_REQ1, 0xc0);
+ tm6000_set_reg(dev, TM6010_REQ07_RD2_ADDR_FOR_REQ2, 0xd8);
+ tm6000_set_reg(dev, TM6010_REQ07_RD6_ENDP_REQ1_REQ2, 0x06);
+ tm6000_set_reg(dev, TM6000_REQ07_RDF_PWDOWN_ACLK, 0x1f);
+
+ /* AP Software reset */
+ tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x08);
+ tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x00);
+
+ tm6000_set_fourcc_format(dev);
+
+ /* Disables soft reset */
+ tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x00);
+ }
+ msleep(20);
+
+ /* Tuner firmware can now be loaded */
+
+ /*
+ * FIXME: This is a hack! xc3028 "sleeps" when no channel is detected
+ * for more than a few seconds. Not sure why, as this behavior does
+ * not happen on other devices with xc3028. So, I suspect that it
+ * is yet another bug at tm6000. After start sleeping, decoding
+ * doesn't start automatically. Instead, it requires some
+ * I2C commands to wake it up. As we want to have image at the
+ * beginning, we needed to add this hack. The better would be to
+ * discover some way to make tm6000 to wake up without this hack.
+ */
+ f.frequency = dev->freq;
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
+
+ msleep(100);
+ tm6000_set_standard(dev);
+ tm6000_set_vbi(dev);
+ tm6000_set_audio_bitrate(dev, 48000);
+
+ /* switch dvb led off */
+ if (dev->gpio.dvb_led) {
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+ dev->gpio.dvb_led, 0x01);
+ }
+
+ return 0;
+}
+
+int tm6000_init_digital_mode(struct tm6000_core *dev)
+{
+ if (dev->dev_type == TM6010) {
+ /* Disable video and audio */
+ tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_IF,
+ 0x00, 0x60);
+ /* Enable TS input */
+ tm6000_set_reg_mask(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE,
+ 0x40, 0x40);
+ /* all power down, but not the digital data port */
+ tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, 0x28);
+ tm6000_set_reg(dev, TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xfc);
+ tm6000_set_reg(dev, TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0xff);
+ } else {
+ tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x08);
+ tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01);
+ tm6000_set_reg(dev, TM6000_REQ07_RDF_PWDOWN_ACLK, 0x08);
+ tm6000_set_reg(dev, TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x0c);
+ tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0xff);
+ tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0xd8);
+ tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x40);
+ tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0xd0);
+ tm6000_set_reg(dev, TM6010_REQ07_RC3_HSTART1, 0x09);
+ tm6000_set_reg(dev, TM6000_REQ07_RDA_CLK_SEL, 0x37);
+ tm6000_set_reg(dev, TM6010_REQ07_RD1_ADDR_FOR_REQ1, 0xd8);
+ tm6000_set_reg(dev, TM6010_REQ07_RD2_ADDR_FOR_REQ2, 0xc0);
+ tm6000_set_reg(dev, TM6010_REQ07_RD6_ENDP_REQ1_REQ2, 0x60);
+
+ tm6000_set_reg(dev, TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x0c);
+ tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0xff);
+ tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0x08);
+ msleep(50);
+
+ tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00);
+ msleep(50);
+ tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x01);
+ msleep(50);
+ tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00);
+ msleep(100);
+ }
+
+ /* switch dvb led on */
+ if (dev->gpio.dvb_led) {
+ tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+ dev->gpio.dvb_led, 0x00);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(tm6000_init_digital_mode);
+
+struct reg_init {
+ u8 req;
+ u8 reg;
+ u8 val;
+};
+
+/* The meaning of those initializations are unknown */
+static struct reg_init tm6000_init_tab[] = {
+ /* REG VALUE */
+ { TM6000_REQ07_RDF_PWDOWN_ACLK, 0x1f },
+ { TM6010_REQ07_RFF_SOFT_RESET, 0x08 },
+ { TM6010_REQ07_RFF_SOFT_RESET, 0x00 },
+ { TM6010_REQ07_RD5_POWERSAVE, 0x4f },
+ { TM6000_REQ07_RDA_CLK_SEL, 0x23 },
+ { TM6000_REQ07_RDB_OUT_SEL, 0x08 },
+ { TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x00 },
+ { TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10 },
+ { TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00 },
+ { TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x00 },
+ { TM6000_REQ07_REB_VADC_AADC_MODE, 0x64 }, /* 48000 bits/sample, external input */
+ { TM6000_REQ07_REE_VADC_CTRL_SEL_CONTROL, 0xc2 },
+
+ { TM6010_REQ07_R3F_RESET, 0x01 }, /* Start of soft reset */
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x07 },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 },
+ { TM6010_REQ07_R05_NOISE_THRESHOLD, 0x64 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01 },
+ { TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0x82 },
+ { TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0x36 },
+ { TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0x50 },
+ { TM6010_REQ07_R0C_CHROMA_AGC_CONTROL, 0x6a },
+ { TM6010_REQ07_R11_AGC_PEAK_CONTROL, 0xc9 },
+ { TM6010_REQ07_R12_AGC_GATE_STARTH, 0x07 },
+ { TM6010_REQ07_R13_AGC_GATE_STARTL, 0x3b },
+ { TM6010_REQ07_R14_AGC_GATE_WIDTH, 0x47 },
+ { TM6010_REQ07_R15_AGC_BP_DELAY, 0x6f },
+ { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0xcd },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R20_HSYNC_RISING_EDGE_TIME, 0x3c },
+ { TM6010_REQ07_R21_HSYNC_PHASE_OFFSET, 0x3c },
+ { TM6010_REQ07_R2D_CHROMA_BURST_END, 0x48 },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 },
+ { TM6010_REQ07_R32_VSYNC_HLOCK_MIN, 0x74 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c },
+ { TM6010_REQ07_R34_VSYNC_AGC_MIN, 0x74 },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c },
+ { TM6010_REQ07_R36_VSYNC_VBI_MIN, 0x7a },
+ { TM6010_REQ07_R37_VSYNC_VBI_MAX, 0x26 },
+ { TM6010_REQ07_R38_VSYNC_THRESHOLD, 0x40 },
+ { TM6010_REQ07_R39_VSYNC_TIME_CONSTANT, 0x0a },
+ { TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55 },
+ { TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x11 },
+ { TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01 },
+ { TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02 },
+ { TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35 },
+ { TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0 },
+ { TM6010_REQ07_R80_COMB_FILTER_TRESHOLD, 0x15 },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 },
+ { TM6010_REQ07_RC1_TRESHOLD, 0xd0 },
+ { TM6010_REQ07_RC3_HSTART1, 0x88 },
+ { TM6010_REQ07_R3F_RESET, 0x00 }, /* End of the soft reset */
+ { TM6010_REQ05_R18_IMASK7, 0x00 },
+};
+
+static struct reg_init tm6010_init_tab[] = {
+ { TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x00 },
+ { TM6010_REQ07_RC4_HSTART0, 0xa0 },
+ { TM6010_REQ07_RC6_HEND0, 0x40 },
+ { TM6010_REQ07_RCA_VEND0, 0x31 },
+ { TM6010_REQ07_RCC_ACTIVE_IF, 0xe1 },
+ { TM6010_REQ07_RE0_DVIDEO_SOURCE, 0x03 },
+ { TM6010_REQ07_RFE_POWER_DOWN, 0x7f },
+
+ { TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0 },
+ { TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4 },
+ { TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf8 },
+ { TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x00 },
+ { TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf2 },
+ { TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0 },
+ { TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2 },
+ { TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x60 },
+ { TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc },
+
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x07 },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 },
+ { TM6010_REQ07_R05_NOISE_THRESHOLD, 0x64 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01 },
+ { TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0x82 },
+ { TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0x36 },
+ { TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0x50 },
+ { TM6010_REQ07_R0C_CHROMA_AGC_CONTROL, 0x6a },
+ { TM6010_REQ07_R11_AGC_PEAK_CONTROL, 0xc9 },
+ { TM6010_REQ07_R12_AGC_GATE_STARTH, 0x07 },
+ { TM6010_REQ07_R13_AGC_GATE_STARTL, 0x3b },
+ { TM6010_REQ07_R14_AGC_GATE_WIDTH, 0x47 },
+ { TM6010_REQ07_R15_AGC_BP_DELAY, 0x6f },
+ { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0xcd },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R20_HSYNC_RISING_EDGE_TIME, 0x3c },
+ { TM6010_REQ07_R21_HSYNC_PHASE_OFFSET, 0x3c },
+ { TM6010_REQ07_R2D_CHROMA_BURST_END, 0x48 },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 },
+ { TM6010_REQ07_R32_VSYNC_HLOCK_MIN, 0x74 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c },
+ { TM6010_REQ07_R34_VSYNC_AGC_MIN, 0x74 },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c },
+ { TM6010_REQ07_R36_VSYNC_VBI_MIN, 0x7a },
+ { TM6010_REQ07_R37_VSYNC_VBI_MAX, 0x26 },
+ { TM6010_REQ07_R38_VSYNC_THRESHOLD, 0x40 },
+ { TM6010_REQ07_R39_VSYNC_TIME_CONSTANT, 0x0a },
+ { TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55 },
+ { TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x11 },
+ { TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01 },
+ { TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02 },
+ { TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35 },
+ { TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0 },
+ { TM6010_REQ07_R80_COMB_FILTER_TRESHOLD, 0x15 },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 },
+ { TM6010_REQ07_RC1_TRESHOLD, 0xd0 },
+ { TM6010_REQ07_RC3_HSTART1, 0x88 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+
+ { TM6010_REQ05_R18_IMASK7, 0x00 },
+
+ { TM6010_REQ07_RDC_IR_LEADER1, 0xaa },
+ { TM6010_REQ07_RDD_IR_LEADER0, 0x30 },
+ { TM6010_REQ07_RDE_IR_PULSE_CNT1, 0x20 },
+ { TM6010_REQ07_RDF_IR_PULSE_CNT0, 0xd0 },
+ { REQ_04_EN_DISABLE_MCU_INT, 0x02, 0x00 },
+ { TM6010_REQ07_RD8_IR, 0x0f },
+
+ /* set remote wakeup key:any key wakeup */
+ { TM6010_REQ07_RE5_REMOTE_WAKEUP, 0xfe },
+ { TM6010_REQ07_RDA_IR_WAKEUP_SEL, 0xff },
+};
+
+int tm6000_init(struct tm6000_core *dev)
+{
+ int board, rc = 0, i, size;
+ struct reg_init *tab;
+
+ /* Check board revision */
+ board = tm6000_get_reg32(dev, REQ_40_GET_VERSION, 0, 0);
+ if (board >= 0) {
+ switch (board & 0xff) {
+ case 0xf3:
+ printk(KERN_INFO "Found tm6000\n");
+ if (dev->dev_type != TM6000)
+ dev->dev_type = TM6000;
+ break;
+ case 0xf4:
+ printk(KERN_INFO "Found tm6010\n");
+ if (dev->dev_type != TM6010)
+ dev->dev_type = TM6010;
+ break;
+ default:
+ printk(KERN_INFO "Unknown board version = 0x%08x\n", board);
+ }
+ } else
+ printk(KERN_ERR "Error %i while retrieving board version\n", board);
+
+ if (dev->dev_type == TM6010) {
+ tab = tm6010_init_tab;
+ size = ARRAY_SIZE(tm6010_init_tab);
+ } else {
+ tab = tm6000_init_tab;
+ size = ARRAY_SIZE(tm6000_init_tab);
+ }
+
+ /* Load board's initialization table */
+ for (i = 0; i < size; i++) {
+ rc = tm6000_set_reg(dev, tab[i].req, tab[i].reg, tab[i].val);
+ if (rc < 0) {
+ printk(KERN_ERR "Error %i while setting req %d, "
+ "reg %d to value %d\n", rc,
+ tab[i].req, tab[i].reg, tab[i].val);
+ return rc;
+ }
+ }
+
+ msleep(5); /* Just to be conservative */
+
+ rc = tm6000_cards_setup(dev);
+
+ return rc;
+}
+
+
+int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate)
+{
+ int val = 0;
+ u8 areg_f0 = 0x60; /* ADC MCLK = 250 Fs */
+ u8 areg_0a = 0x91; /* SIF 48KHz */
+
+ switch (bitrate) {
+ case 48000:
+ areg_f0 = 0x60; /* ADC MCLK = 250 Fs */
+ areg_0a = 0x91; /* SIF 48KHz */
+ dev->audio_bitrate = bitrate;
+ break;
+ case 32000:
+ areg_f0 = 0x00; /* ADC MCLK = 375 Fs */
+ areg_0a = 0x90; /* SIF 32KHz */
+ dev->audio_bitrate = bitrate;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+
+ /* enable I2S, if we use sif or external I2S device */
+ if (dev->dev_type == TM6010) {
+ val = tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, areg_0a);
+ if (val < 0)
+ return val;
+
+ val = tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
+ areg_f0, 0xf0);
+ if (val < 0)
+ return val;
+ } else {
+ val = tm6000_set_reg_mask(dev, TM6000_REQ07_REB_VADC_AADC_MODE,
+ areg_f0, 0xf0);
+ if (val < 0)
+ return val;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tm6000_set_audio_bitrate);
+
+int tm6000_set_audio_rinput(struct tm6000_core *dev)
+{
+ if (dev->dev_type == TM6010) {
+ /* Audio crossbar setting, default SIF1 */
+ u8 areg_f0;
+ u8 areg_07 = 0x10;
+
+ switch (dev->rinput.amux) {
+ case TM6000_AMUX_SIF1:
+ case TM6000_AMUX_SIF2:
+ areg_f0 = 0x03;
+ areg_07 = 0x30;
+ break;
+ case TM6000_AMUX_ADC1:
+ areg_f0 = 0x00;
+ break;
+ case TM6000_AMUX_ADC2:
+ areg_f0 = 0x08;
+ break;
+ case TM6000_AMUX_I2S:
+ areg_f0 = 0x04;
+ break;
+ default:
+ printk(KERN_INFO "%s: audio input dosn't support\n",
+ dev->name);
+ return 0;
+ break;
+ }
+ /* Set audio input crossbar */
+ tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
+ areg_f0, 0x0f);
+ /* Mux overflow workaround */
+ tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL,
+ areg_07, 0xf0);
+ } else {
+ u8 areg_eb;
+ /* Audio setting, default LINE1 */
+ switch (dev->rinput.amux) {
+ case TM6000_AMUX_ADC1:
+ areg_eb = 0x00;
+ break;
+ case TM6000_AMUX_ADC2:
+ areg_eb = 0x04;
+ break;
+ default:
+ printk(KERN_INFO "%s: audio input dosn't support\n",
+ dev->name);
+ return 0;
+ break;
+ }
+ /* Set audio input */
+ tm6000_set_reg_mask(dev, TM6000_REQ07_REB_VADC_AADC_MODE,
+ areg_eb, 0x0f);
+ }
+ return 0;
+}
+
+static void tm6010_set_mute_sif(struct tm6000_core *dev, u8 mute)
+{
+ u8 mute_reg = 0;
+
+ if (mute)
+ mute_reg = 0x08;
+
+ tm6000_set_reg_mask(dev, TM6010_REQ08_R0A_A_I2S_MOD, mute_reg, 0x08);
+}
+
+static void tm6010_set_mute_adc(struct tm6000_core *dev, u8 mute)
+{
+ u8 mute_reg = 0;
+
+ if (mute)
+ mute_reg = 0x20;
+
+ if (dev->dev_type == TM6010) {
+ tm6000_set_reg_mask(dev, TM6010_REQ08_RF2_LEFT_CHANNEL_VOL,
+ mute_reg, 0x20);
+ tm6000_set_reg_mask(dev, TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL,
+ mute_reg, 0x20);
+ } else {
+ tm6000_set_reg_mask(dev, TM6000_REQ07_REC_VADC_AADC_LVOL,
+ mute_reg, 0x20);
+ tm6000_set_reg_mask(dev, TM6000_REQ07_RED_VADC_AADC_RVOL,
+ mute_reg, 0x20);
+ }
+}
+
+int tm6000_tvaudio_set_mute(struct tm6000_core *dev, u8 mute)
+{
+ enum tm6000_mux mux;
+
+ if (dev->radio)
+ mux = dev->rinput.amux;
+ else
+ mux = dev->vinput[dev->input].amux;
+
+ switch (mux) {
+ case TM6000_AMUX_SIF1:
+ case TM6000_AMUX_SIF2:
+ if (dev->dev_type == TM6010)
+ tm6010_set_mute_sif(dev, mute);
+ else {
+ printk(KERN_INFO "ERROR: TM5600 and TM6000 don't has"
+ " SIF audio inputs. Please check the %s"
+ " configuration.\n", dev->name);
+ return -EINVAL;
+ }
+ break;
+ case TM6000_AMUX_ADC1:
+ case TM6000_AMUX_ADC2:
+ tm6010_set_mute_adc(dev, mute);
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ return 0;
+}
+
+static void tm6010_set_volume_sif(struct tm6000_core *dev, int vol)
+{
+ u8 vol_reg;
+
+ vol_reg = vol & 0x0F;
+
+ if (vol < 0)
+ vol_reg |= 0x40;
+
+ tm6000_set_reg(dev, TM6010_REQ08_R07_A_LEFT_VOL, vol_reg);
+ tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, vol_reg);
+}
+
+static void tm6010_set_volume_adc(struct tm6000_core *dev, int vol)
+{
+ u8 vol_reg;
+
+ vol_reg = (vol + 0x10) & 0x1f;
+
+ if (dev->dev_type == TM6010) {
+ tm6000_set_reg(dev, TM6010_REQ08_RF2_LEFT_CHANNEL_VOL, vol_reg);
+ tm6000_set_reg(dev, TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL, vol_reg);
+ } else {
+ tm6000_set_reg(dev, TM6000_REQ07_REC_VADC_AADC_LVOL, vol_reg);
+ tm6000_set_reg(dev, TM6000_REQ07_RED_VADC_AADC_RVOL, vol_reg);
+ }
+}
+
+void tm6000_set_volume(struct tm6000_core *dev, int vol)
+{
+ enum tm6000_mux mux;
+
+ if (dev->radio) {
+ mux = dev->rinput.amux;
+ vol += 8; /* Offset to 0 dB */
+ } else
+ mux = dev->vinput[dev->input].amux;
+
+ switch (mux) {
+ case TM6000_AMUX_SIF1:
+ case TM6000_AMUX_SIF2:
+ if (dev->dev_type == TM6010)
+ tm6010_set_volume_sif(dev, vol);
+ else
+ printk(KERN_INFO "ERROR: TM5600 and TM6000 don't has"
+ " SIF audio inputs. Please check the %s"
+ " configuration.\n", dev->name);
+ break;
+ case TM6000_AMUX_ADC1:
+ case TM6000_AMUX_ADC2:
+ tm6010_set_volume_adc(dev, vol);
+ break;
+ default:
+ break;
+ }
+}
+
+static LIST_HEAD(tm6000_devlist);
+static DEFINE_MUTEX(tm6000_devlist_mutex);
+
+/*
+ * tm6000_realease_resource()
+ */
+
+void tm6000_remove_from_devlist(struct tm6000_core *dev)
+{
+ mutex_lock(&tm6000_devlist_mutex);
+ list_del(&dev->devlist);
+ mutex_unlock(&tm6000_devlist_mutex);
+};
+
+void tm6000_add_into_devlist(struct tm6000_core *dev)
+{
+ mutex_lock(&tm6000_devlist_mutex);
+ list_add_tail(&dev->devlist, &tm6000_devlist);
+ mutex_unlock(&tm6000_devlist_mutex);
+};
+
+/*
+ * Extension interface
+ */
+
+static LIST_HEAD(tm6000_extension_devlist);
+
+int tm6000_call_fillbuf(struct tm6000_core *dev, enum tm6000_ops_type type,
+ char *buf, int size)
+{
+ struct tm6000_ops *ops = NULL;
+
+ /* FIXME: tm6000_extension_devlist_lock should be a spinlock */
+
+ if (!list_empty(&tm6000_extension_devlist)) {
+ list_for_each_entry(ops, &tm6000_extension_devlist, next) {
+ if (ops->fillbuf && ops->type == type)
+ ops->fillbuf(dev, buf, size);
+ }
+ }
+
+ return 0;
+}
+
+int tm6000_register_extension(struct tm6000_ops *ops)
+{
+ struct tm6000_core *dev = NULL;
+
+ mutex_lock(&tm6000_devlist_mutex);
+ list_add_tail(&ops->next, &tm6000_extension_devlist);
+ list_for_each_entry(dev, &tm6000_devlist, devlist) {
+ ops->init(dev);
+ printk(KERN_INFO "%s: Initialized (%s) extension\n",
+ dev->name, ops->name);
+ }
+ mutex_unlock(&tm6000_devlist_mutex);
+ return 0;
+}
+EXPORT_SYMBOL(tm6000_register_extension);
+
+void tm6000_unregister_extension(struct tm6000_ops *ops)
+{
+ struct tm6000_core *dev = NULL;
+
+ mutex_lock(&tm6000_devlist_mutex);
+ list_for_each_entry(dev, &tm6000_devlist, devlist)
+ ops->fini(dev);
+
+ printk(KERN_INFO "tm6000: Remove (%s) extension\n", ops->name);
+ list_del(&ops->next);
+ mutex_unlock(&tm6000_devlist_mutex);
+}
+EXPORT_SYMBOL(tm6000_unregister_extension);
+
+void tm6000_init_extension(struct tm6000_core *dev)
+{
+ struct tm6000_ops *ops = NULL;
+
+ mutex_lock(&tm6000_devlist_mutex);
+ if (!list_empty(&tm6000_extension_devlist)) {
+ list_for_each_entry(ops, &tm6000_extension_devlist, next) {
+ if (ops->init)
+ ops->init(dev);
+ }
+ }
+ mutex_unlock(&tm6000_devlist_mutex);
+}
+
+void tm6000_close_extension(struct tm6000_core *dev)
+{
+ struct tm6000_ops *ops = NULL;
+
+ mutex_lock(&tm6000_devlist_mutex);
+ if (!list_empty(&tm6000_extension_devlist)) {
+ list_for_each_entry(ops, &tm6000_extension_devlist, next) {
+ if (ops->fini)
+ ops->fini(dev);
+ }
+ }
+ mutex_unlock(&tm6000_devlist_mutex);
+}
diff --git a/drivers/media/video/tm6000/tm6000-dvb.c b/drivers/media/video/tm6000/tm6000-dvb.c
new file mode 100644
index 00000000..e1f3f66e
--- /dev/null
+++ b/drivers/media/video/tm6000/tm6000-dvb.c
@@ -0,0 +1,467 @@
+/*
+ * tm6000-dvb.c - dvb-t support for TM5600/TM6000/TM6010 USB video capture devices
+ *
+ * Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#include "tm6000.h"
+#include "tm6000-regs.h"
+
+#include "zl10353.h"
+
+#include <media/tuner.h>
+
+#include "tuner-xc2028.h"
+#include "xc5000.h"
+
+MODULE_DESCRIPTION("DVB driver extension module for tm5600/6000/6010 based TV cards");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_LICENSE("GPL");
+
+MODULE_SUPPORTED_DEVICE("{{Trident, tm5600},"
+ "{{Trident, tm6000},"
+ "{{Trident, tm6010}");
+
+static int debug;
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable debug message");
+
+static inline void print_err_status(struct tm6000_core *dev,
+ int packet, int status)
+{
+ char *errmsg = "Unknown";
+
+ switch (status) {
+ case -ENOENT:
+ errmsg = "unlinked synchronuously";
+ break;
+ case -ECONNRESET:
+ errmsg = "unlinked asynchronuously";
+ break;
+ case -ENOSR:
+ errmsg = "Buffer error (overrun)";
+ break;
+ case -EPIPE:
+ errmsg = "Stalled (device not responding)";
+ break;
+ case -EOVERFLOW:
+ errmsg = "Babble (bad cable?)";
+ break;
+ case -EPROTO:
+ errmsg = "Bit-stuff error (bad cable?)";
+ break;
+ case -EILSEQ:
+ errmsg = "CRC/Timeout (could be anything)";
+ break;
+ case -ETIME:
+ errmsg = "Device does not respond";
+ break;
+ }
+ if (packet < 0) {
+ dprintk(dev, 1, "URB status %d [%s].\n",
+ status, errmsg);
+ } else {
+ dprintk(dev, 1, "URB packet %d, status %d [%s].\n",
+ packet, status, errmsg);
+ }
+}
+
+static void tm6000_urb_received(struct urb *urb)
+{
+ int ret;
+ struct tm6000_core *dev = urb->context;
+
+ switch (urb->status) {
+ case 0:
+ case -ETIMEDOUT:
+ break;
+ case -ENOENT:
+ case -ECONNRESET:
+ case -ESHUTDOWN:
+ return;
+ default:
+ print_err_status(dev, 0, urb->status);
+ }
+
+ if (urb->actual_length > 0)
+ dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer,
+ urb->actual_length);
+
+ if (dev->dvb->streams > 0) {
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret < 0) {
+ printk(KERN_ERR "tm6000: error %s\n", __func__);
+ kfree(urb->transfer_buffer);
+ usb_free_urb(urb);
+ }
+ }
+}
+
+static int tm6000_start_stream(struct tm6000_core *dev)
+{
+ int ret;
+ unsigned int pipe, size;
+ struct tm6000_dvb *dvb = dev->dvb;
+
+ printk(KERN_INFO "tm6000: got start stream request %s\n", __func__);
+
+ if (dev->mode != TM6000_MODE_DIGITAL) {
+ tm6000_init_digital_mode(dev);
+ dev->mode = TM6000_MODE_DIGITAL;
+ }
+
+ dvb->bulk_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (dvb->bulk_urb == NULL) {
+ printk(KERN_ERR "tm6000: couldn't allocate urb\n");
+ return -ENOMEM;
+ }
+
+ pipe = usb_rcvbulkpipe(dev->udev, dev->bulk_in.endp->desc.bEndpointAddress
+ & USB_ENDPOINT_NUMBER_MASK);
+
+ size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe));
+ size = size * 15; /* 512 x 8 or 12 or 15 */
+
+ dvb->bulk_urb->transfer_buffer = kzalloc(size, GFP_KERNEL);
+ if (dvb->bulk_urb->transfer_buffer == NULL) {
+ usb_free_urb(dvb->bulk_urb);
+ printk(KERN_ERR "tm6000: couldn't allocate transfer buffer!\n");
+ return -ENOMEM;
+ }
+
+ usb_fill_bulk_urb(dvb->bulk_urb, dev->udev, pipe,
+ dvb->bulk_urb->transfer_buffer,
+ size,
+ tm6000_urb_received, dev);
+
+ ret = usb_clear_halt(dev->udev, pipe);
+ if (ret < 0) {
+ printk(KERN_ERR "tm6000: error %i in %s during pipe reset\n",
+ ret, __func__);
+ return ret;
+ } else
+ printk(KERN_ERR "tm6000: pipe resetted\n");
+
+/* mutex_lock(&tm6000_driver.open_close_mutex); */
+ ret = usb_submit_urb(dvb->bulk_urb, GFP_ATOMIC);
+
+/* mutex_unlock(&tm6000_driver.open_close_mutex); */
+ if (ret) {
+ printk(KERN_ERR "tm6000: submit of urb failed (error=%i)\n",
+ ret);
+
+ kfree(dvb->bulk_urb->transfer_buffer);
+ usb_free_urb(dvb->bulk_urb);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void tm6000_stop_stream(struct tm6000_core *dev)
+{
+ struct tm6000_dvb *dvb = dev->dvb;
+
+ if (dvb->bulk_urb) {
+ printk(KERN_INFO "urb killing\n");
+ usb_kill_urb(dvb->bulk_urb);
+ printk(KERN_INFO "urb buffer free\n");
+ kfree(dvb->bulk_urb->transfer_buffer);
+ usb_free_urb(dvb->bulk_urb);
+ dvb->bulk_urb = NULL;
+ }
+}
+
+static int tm6000_start_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct tm6000_core *dev = demux->priv;
+ struct tm6000_dvb *dvb = dev->dvb;
+ printk(KERN_INFO "tm6000: got start feed request %s\n", __func__);
+
+ mutex_lock(&dvb->mutex);
+ if (dvb->streams == 0) {
+ dvb->streams = 1;
+/* mutex_init(&tm6000_dev->streming_mutex); */
+ tm6000_start_stream(dev);
+ } else
+ ++(dvb->streams);
+ mutex_unlock(&dvb->mutex);
+
+ return 0;
+}
+
+static int tm6000_stop_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct tm6000_core *dev = demux->priv;
+ struct tm6000_dvb *dvb = dev->dvb;
+
+ printk(KERN_INFO "tm6000: got stop feed request %s\n", __func__);
+
+ mutex_lock(&dvb->mutex);
+
+ printk(KERN_INFO "stream %#x\n", dvb->streams);
+ --(dvb->streams);
+ if (dvb->streams == 0) {
+ printk(KERN_INFO "stop stream\n");
+ tm6000_stop_stream(dev);
+/* mutex_destroy(&tm6000_dev->streaming_mutex); */
+ }
+ mutex_unlock(&dvb->mutex);
+/* mutex_destroy(&tm6000_dev->streaming_mutex); */
+
+ return 0;
+}
+
+static int tm6000_dvb_attach_frontend(struct tm6000_core *dev)
+{
+ struct tm6000_dvb *dvb = dev->dvb;
+
+ if (dev->caps.has_zl10353) {
+ struct zl10353_config config = {
+ .demod_address = dev->demod_addr,
+ .no_tuner = 1,
+ .parallel_ts = 1,
+ .if2 = 45700,
+ .disable_i2c_gate_ctrl = 1,
+ };
+
+ dvb->frontend = dvb_attach(zl10353_attach, &config,
+ &dev->i2c_adap);
+ } else {
+ printk(KERN_ERR "tm6000: no frontend defined for the device!\n");
+ return -1;
+ }
+
+ return (!dvb->frontend) ? -1 : 0;
+}
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static int register_dvb(struct tm6000_core *dev)
+{
+ int ret = -1;
+ struct tm6000_dvb *dvb = dev->dvb;
+
+ mutex_init(&dvb->mutex);
+
+ dvb->streams = 0;
+
+ /* attach the frontend */
+ ret = tm6000_dvb_attach_frontend(dev);
+ if (ret < 0) {
+ printk(KERN_ERR "tm6000: couldn't attach the frontend!\n");
+ goto err;
+ }
+
+ ret = dvb_register_adapter(&dvb->adapter, "Trident TVMaster 6000 DVB-T",
+ THIS_MODULE, &dev->udev->dev, adapter_nr);
+ dvb->adapter.priv = dev;
+
+ if (dvb->frontend) {
+ switch (dev->tuner_type) {
+ case TUNER_XC2028: {
+ struct xc2028_config cfg = {
+ .i2c_adap = &dev->i2c_adap,
+ .i2c_addr = dev->tuner_addr,
+ };
+
+ dvb->frontend->callback = tm6000_tuner_callback;
+ ret = dvb_register_frontend(&dvb->adapter, dvb->frontend);
+ if (ret < 0) {
+ printk(KERN_ERR
+ "tm6000: couldn't register frontend\n");
+ goto adapter_err;
+ }
+
+ if (!dvb_attach(xc2028_attach, dvb->frontend, &cfg)) {
+ printk(KERN_ERR "tm6000: couldn't register "
+ "frontend (xc3028)\n");
+ ret = -EINVAL;
+ goto frontend_err;
+ }
+ printk(KERN_INFO "tm6000: XC2028/3028 asked to be "
+ "attached to frontend!\n");
+ break;
+ }
+ case TUNER_XC5000: {
+ struct xc5000_config cfg = {
+ .i2c_address = dev->tuner_addr,
+ };
+
+ dvb->frontend->callback = tm6000_xc5000_callback;
+ ret = dvb_register_frontend(&dvb->adapter, dvb->frontend);
+ if (ret < 0) {
+ printk(KERN_ERR
+ "tm6000: couldn't register frontend\n");
+ goto adapter_err;
+ }
+
+ if (!dvb_attach(xc5000_attach, dvb->frontend, &dev->i2c_adap, &cfg)) {
+ printk(KERN_ERR "tm6000: couldn't register "
+ "frontend (xc5000)\n");
+ ret = -EINVAL;
+ goto frontend_err;
+ }
+ printk(KERN_INFO "tm6000: XC5000 asked to be "
+ "attached to frontend!\n");
+ break;
+ }
+ }
+ } else
+ printk(KERN_ERR "tm6000: no frontend found\n");
+
+ dvb->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING
+ | DMX_MEMORY_BASED_FILTERING;
+ dvb->demux.priv = dev;
+ dvb->demux.filternum = 8;
+ dvb->demux.feednum = 8;
+ dvb->demux.start_feed = tm6000_start_feed;
+ dvb->demux.stop_feed = tm6000_stop_feed;
+ dvb->demux.write_to_decoder = NULL;
+ ret = dvb_dmx_init(&dvb->demux);
+ if (ret < 0) {
+ printk(KERN_ERR "tm6000: dvb_dmx_init failed (errno = %d)\n", ret);
+ goto frontend_err;
+ }
+
+ dvb->dmxdev.filternum = dev->dvb->demux.filternum;
+ dvb->dmxdev.demux = &dev->dvb->demux.dmx;
+ dvb->dmxdev.capabilities = 0;
+
+ ret = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter);
+ if (ret < 0) {
+ printk(KERN_ERR "tm6000: dvb_dmxdev_init failed (errno = %d)\n", ret);
+ goto dvb_dmx_err;
+ }
+
+ return 0;
+
+dvb_dmx_err:
+ dvb_dmx_release(&dvb->demux);
+frontend_err:
+ if (dvb->frontend) {
+ dvb_frontend_detach(dvb->frontend);
+ dvb_unregister_frontend(dvb->frontend);
+ }
+adapter_err:
+ dvb_unregister_adapter(&dvb->adapter);
+err:
+ return ret;
+}
+
+static void unregister_dvb(struct tm6000_core *dev)
+{
+ struct tm6000_dvb *dvb = dev->dvb;
+
+ if (dvb->bulk_urb != NULL) {
+ struct urb *bulk_urb = dvb->bulk_urb;
+
+ kfree(bulk_urb->transfer_buffer);
+ bulk_urb->transfer_buffer = NULL;
+ usb_unlink_urb(bulk_urb);
+ usb_free_urb(bulk_urb);
+ }
+
+/* mutex_lock(&tm6000_driver.open_close_mutex); */
+ if (dvb->frontend) {
+ dvb_frontend_detach(dvb->frontend);
+ dvb_unregister_frontend(dvb->frontend);
+ }
+
+ dvb_dmxdev_release(&dvb->dmxdev);
+ dvb_dmx_release(&dvb->demux);
+ dvb_unregister_adapter(&dvb->adapter);
+ mutex_destroy(&dvb->mutex);
+/* mutex_unlock(&tm6000_driver.open_close_mutex); */
+}
+
+static int dvb_init(struct tm6000_core *dev)
+{
+ struct tm6000_dvb *dvb;
+ int rc;
+
+ if (!dev)
+ return 0;
+
+ if (!dev->caps.has_dvb)
+ return 0;
+
+ if (dev->udev->speed == USB_SPEED_FULL) {
+ printk(KERN_INFO "This USB2.0 device cannot be run on a USB1.1 port. (it lacks a hardware PID filter)\n");
+ return 0;
+ }
+
+ dvb = kzalloc(sizeof(struct tm6000_dvb), GFP_KERNEL);
+ if (!dvb) {
+ printk(KERN_INFO "Cannot allocate memory\n");
+ return -ENOMEM;
+ }
+
+ dev->dvb = dvb;
+
+ rc = register_dvb(dev);
+ if (rc < 0) {
+ kfree(dvb);
+ dev->dvb = NULL;
+ return 0;
+ }
+
+ return 0;
+}
+
+static int dvb_fini(struct tm6000_core *dev)
+{
+ if (!dev)
+ return 0;
+
+ if (!dev->caps.has_dvb)
+ return 0;
+
+ if (dev->dvb) {
+ unregister_dvb(dev);
+ kfree(dev->dvb);
+ dev->dvb = NULL;
+ }
+
+ return 0;
+}
+
+static struct tm6000_ops dvb_ops = {
+ .type = TM6000_DVB,
+ .name = "TM6000 dvb Extension",
+ .init = dvb_init,
+ .fini = dvb_fini,
+};
+
+static int __init tm6000_dvb_register(void)
+{
+ return tm6000_register_extension(&dvb_ops);
+}
+
+static void __exit tm6000_dvb_unregister(void)
+{
+ tm6000_unregister_extension(&dvb_ops);
+}
+
+module_init(tm6000_dvb_register);
+module_exit(tm6000_dvb_unregister);
diff --git a/drivers/media/video/tm6000/tm6000-i2c.c b/drivers/media/video/tm6000/tm6000-i2c.c
new file mode 100644
index 00000000..c7e23e3d
--- /dev/null
+++ b/drivers/media/video/tm6000/tm6000-i2c.c
@@ -0,0 +1,334 @@
+/*
+ * tm6000-i2c.c - driver for TM5600/TM6000/TM6010 USB video capture devices
+ *
+ * Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
+ *
+ * Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com>
+ * - Fix SMBus Read Byte command
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+
+#include "tm6000.h"
+#include "tm6000-regs.h"
+#include <media/v4l2-common.h>
+#include <media/tuner.h>
+#include "tuner-xc2028.h"
+
+
+/* ----------------------------------------------------------- */
+
+static unsigned int i2c_debug;
+module_param(i2c_debug, int, 0644);
+MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
+
+#define i2c_dprintk(lvl, fmt, args...) if (i2c_debug >= lvl) do { \
+ printk(KERN_DEBUG "%s at %s: " fmt, \
+ dev->name, __func__, ##args); } while (0)
+
+static int tm6000_i2c_send_regs(struct tm6000_core *dev, unsigned char addr,
+ __u8 reg, char *buf, int len)
+{
+ int rc;
+ unsigned int i2c_packet_limit = 16;
+
+ if (dev->dev_type == TM6010)
+ i2c_packet_limit = 80;
+
+ if (!buf)
+ return -1;
+
+ if (len < 1 || len > i2c_packet_limit) {
+ printk(KERN_ERR "Incorrect length of i2c packet = %d, limit set to %d\n",
+ len, i2c_packet_limit);
+ return -1;
+ }
+
+ /* capture mutex */
+ rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE, REQ_16_SET_GET_I2C_WR1_RDN,
+ addr | reg << 8, 0, buf, len);
+
+ if (rc < 0) {
+ /* release mutex */
+ return rc;
+ }
+
+ /* release mutex */
+ return rc;
+}
+
+/* Generic read - doesn't work fine with 16bit registers */
+static int tm6000_i2c_recv_regs(struct tm6000_core *dev, unsigned char addr,
+ __u8 reg, char *buf, int len)
+{
+ int rc;
+ u8 b[2];
+ unsigned int i2c_packet_limit = 16;
+
+ if (dev->dev_type == TM6010)
+ i2c_packet_limit = 64;
+
+ if (!buf)
+ return -1;
+
+ if (len < 1 || len > i2c_packet_limit) {
+ printk(KERN_ERR "Incorrect length of i2c packet = %d, limit set to %d\n",
+ len, i2c_packet_limit);
+ return -1;
+ }
+
+ /* capture mutex */
+ if ((dev->caps.has_zl10353) && (dev->demod_addr << 1 == addr) && (reg % 2 == 0)) {
+ /*
+ * Workaround an I2C bug when reading from zl10353
+ */
+ reg -= 1;
+ len += 1;
+
+ rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ REQ_16_SET_GET_I2C_WR1_RDN, addr | reg << 8, 0, b, len);
+
+ *buf = b[1];
+ } else {
+ rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ REQ_16_SET_GET_I2C_WR1_RDN, addr | reg << 8, 0, buf, len);
+ }
+
+ /* release mutex */
+ return rc;
+}
+
+/*
+ * read from a 16bit register
+ * for example xc2028, xc3028 or xc3028L
+ */
+static int tm6000_i2c_recv_regs16(struct tm6000_core *dev, unsigned char addr,
+ __u16 reg, char *buf, int len)
+{
+ int rc;
+ unsigned char ureg;
+
+ if (!buf || len != 2)
+ return -1;
+
+ /* capture mutex */
+ if (dev->dev_type == TM6010) {
+ ureg = reg & 0xFF;
+ rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE, REQ_16_SET_GET_I2C_WR1_RDN,
+ addr | (reg & 0xFF00), 0, &ureg, 1);
+
+ if (rc < 0) {
+ /* release mutex */
+ return rc;
+ }
+
+ rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE, REQ_35_AFTEK_TUNER_READ,
+ reg, 0, buf, len);
+ } else {
+ rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE, REQ_14_SET_GET_I2C_WR2_RDN,
+ addr, reg, buf, len);
+ }
+
+ /* release mutex */
+ return rc;
+}
+
+static int tm6000_i2c_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg msgs[], int num)
+{
+ struct tm6000_core *dev = i2c_adap->algo_data;
+ int addr, rc, i, byte;
+
+ if (num <= 0)
+ return 0;
+ for (i = 0; i < num; i++) {
+ addr = (msgs[i].addr << 1) & 0xff;
+ i2c_dprintk(2, "%s %s addr=0x%x len=%d:",
+ (msgs[i].flags & I2C_M_RD) ? "read" : "write",
+ i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len);
+ if (msgs[i].flags & I2C_M_RD) {
+ /* read request without preceding register selection */
+ /*
+ * The TM6000 only supports a read transaction
+ * immediately after a 1 or 2 byte write to select
+ * a register. We cannot fulfil this request.
+ */
+ i2c_dprintk(2, " read without preceding write not"
+ " supported");
+ rc = -EOPNOTSUPP;
+ goto err;
+ } else if (i + 1 < num && msgs[i].len <= 2 &&
+ (msgs[i + 1].flags & I2C_M_RD) &&
+ msgs[i].addr == msgs[i + 1].addr) {
+ /* 1 or 2 byte write followed by a read */
+ if (i2c_debug >= 2)
+ for (byte = 0; byte < msgs[i].len; byte++)
+ printk(KERN_CONT " %02x", msgs[i].buf[byte]);
+ i2c_dprintk(2, "; joined to read %s len=%d:",
+ i == num - 2 ? "stop" : "nonstop",
+ msgs[i + 1].len);
+
+ if (msgs[i].len == 2) {
+ rc = tm6000_i2c_recv_regs16(dev, addr,
+ msgs[i].buf[0] << 8 | msgs[i].buf[1],
+ msgs[i + 1].buf, msgs[i + 1].len);
+ } else {
+ rc = tm6000_i2c_recv_regs(dev, addr, msgs[i].buf[0],
+ msgs[i + 1].buf, msgs[i + 1].len);
+ }
+
+ i++;
+
+ if (addr == dev->tuner_addr << 1) {
+ tm6000_set_reg(dev, REQ_50_SET_START, 0, 0);
+ tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0);
+ }
+ if (i2c_debug >= 2)
+ for (byte = 0; byte < msgs[i].len; byte++)
+ printk(KERN_CONT " %02x", msgs[i].buf[byte]);
+ } else {
+ /* write bytes */
+ if (i2c_debug >= 2)
+ for (byte = 0; byte < msgs[i].len; byte++)
+ printk(KERN_CONT " %02x", msgs[i].buf[byte]);
+ rc = tm6000_i2c_send_regs(dev, addr, msgs[i].buf[0],
+ msgs[i].buf + 1, msgs[i].len - 1);
+ }
+ if (i2c_debug >= 2)
+ printk(KERN_CONT "\n");
+ if (rc < 0)
+ goto err;
+ }
+
+ return num;
+err:
+ i2c_dprintk(2, " ERROR: %i\n", rc);
+ return rc;
+}
+
+static int tm6000_i2c_eeprom(struct tm6000_core *dev)
+{
+ int i, rc;
+ unsigned char *p = dev->eedata;
+ unsigned char bytes[17];
+
+ dev->i2c_client.addr = 0xa0 >> 1;
+ dev->eedata_size = 0;
+
+ bytes[16] = '\0';
+ for (i = 0; i < sizeof(dev->eedata); ) {
+ *p = i;
+ rc = tm6000_i2c_recv_regs(dev, 0xa0, i, p, 1);
+ if (rc < 1) {
+ if (p == dev->eedata)
+ goto noeeprom;
+ else {
+ printk(KERN_WARNING
+ "%s: i2c eeprom read error (err=%d)\n",
+ dev->name, rc);
+ }
+ return -EINVAL;
+ }
+ dev->eedata_size++;
+ p++;
+ if (0 == (i % 16))
+ printk(KERN_INFO "%s: i2c eeprom %02x:", dev->name, i);
+ printk(KERN_CONT " %02x", dev->eedata[i]);
+ if ((dev->eedata[i] >= ' ') && (dev->eedata[i] <= 'z'))
+ bytes[i%16] = dev->eedata[i];
+ else
+ bytes[i%16] = '.';
+
+ i++;
+
+ if (0 == (i % 16)) {
+ bytes[16] = '\0';
+ printk(KERN_CONT " %s\n", bytes);
+ }
+ }
+ if (0 != (i%16)) {
+ bytes[i%16] = '\0';
+ for (i %= 16; i < 16; i++)
+ printk(KERN_CONT " ");
+ printk(KERN_CONT " %s\n", bytes);
+ }
+
+ return 0;
+
+noeeprom:
+ printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n",
+ dev->name, rc);
+ return -EINVAL;
+}
+
+/* ----------------------------------------------------------- */
+
+/*
+ * functionality()
+ */
+static u32 functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm tm6000_algo = {
+ .master_xfer = tm6000_i2c_xfer,
+ .functionality = functionality,
+};
+
+/* ----------------------------------------------------------- */
+
+/*
+ * tm6000_i2c_register()
+ * register i2c bus
+ */
+int tm6000_i2c_register(struct tm6000_core *dev)
+{
+ int rc;
+
+ dev->i2c_adap.owner = THIS_MODULE;
+ dev->i2c_adap.algo = &tm6000_algo;
+ dev->i2c_adap.dev.parent = &dev->udev->dev;
+ strlcpy(dev->i2c_adap.name, dev->name, sizeof(dev->i2c_adap.name));
+ dev->i2c_adap.algo_data = dev;
+ i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev);
+ rc = i2c_add_adapter(&dev->i2c_adap);
+ if (rc)
+ return rc;
+
+ dev->i2c_client.adapter = &dev->i2c_adap;
+ strlcpy(dev->i2c_client.name, "tm6000 internal", I2C_NAME_SIZE);
+ tm6000_i2c_eeprom(dev);
+
+ return 0;
+}
+
+/*
+ * tm6000_i2c_unregister()
+ * unregister i2c_bus
+ */
+int tm6000_i2c_unregister(struct tm6000_core *dev)
+{
+ i2c_del_adapter(&dev->i2c_adap);
+ return 0;
+}
diff --git a/drivers/media/video/tm6000/tm6000-input.c b/drivers/media/video/tm6000/tm6000-input.c
new file mode 100644
index 00000000..859eb90e
--- /dev/null
+++ b/drivers/media/video/tm6000/tm6000-input.c
@@ -0,0 +1,499 @@
+/*
+ * tm6000-input.c - driver for TM5600/TM6000/TM6010 USB video capture devices
+ *
+ * Copyright (C) 2010 Stefan Ringel <stefan.ringel@arcor.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include <linux/input.h>
+#include <linux/usb.h>
+
+#include <media/rc-core.h>
+
+#include "tm6000.h"
+#include "tm6000-regs.h"
+
+static unsigned int ir_debug;
+module_param(ir_debug, int, 0644);
+MODULE_PARM_DESC(ir_debug, "debug message level");
+
+static unsigned int enable_ir = 1;
+module_param(enable_ir, int, 0644);
+MODULE_PARM_DESC(enable_ir, "enable ir (default is enable)");
+
+static unsigned int ir_clock_mhz = 12;
+module_param(ir_clock_mhz, int, 0644);
+MODULE_PARM_DESC(enable_ir, "ir clock, in MHz");
+
+#define URB_SUBMIT_DELAY 100 /* ms - Delay to submit an URB request on retrial and init */
+#define URB_INT_LED_DELAY 100 /* ms - Delay to turn led on again on int mode */
+
+#undef dprintk
+
+#define dprintk(level, fmt, arg...) do {\
+ if (ir_debug >= level) \
+ printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \
+ } while (0)
+
+struct tm6000_ir_poll_result {
+ u16 rc_data;
+};
+
+struct tm6000_IR {
+ struct tm6000_core *dev;
+ struct rc_dev *rc;
+ char name[32];
+ char phys[32];
+
+ /* poll expernal decoder */
+ int polling;
+ struct delayed_work work;
+ u8 wait:1;
+ u8 pwled:2;
+ u8 submit_urb:1;
+ u16 key_addr;
+ struct urb *int_urb;
+
+ /* IR device properties */
+ u64 rc_type;
+};
+
+void tm6000_ir_wait(struct tm6000_core *dev, u8 state)
+{
+ struct tm6000_IR *ir = dev->ir;
+
+ if (!dev->ir)
+ return;
+
+ dprintk(2, "%s: %i\n",__func__, ir->wait);
+
+ if (state)
+ ir->wait = 1;
+ else
+ ir->wait = 0;
+}
+
+static int tm6000_ir_config(struct tm6000_IR *ir)
+{
+ struct tm6000_core *dev = ir->dev;
+ u32 pulse = 0, leader = 0;
+
+ dprintk(2, "%s\n",__func__);
+
+ /*
+ * The IR decoder supports RC-5 or NEC, with a configurable timing.
+ * The timing configuration there is not that accurate, as it uses
+ * approximate values. The NEC spec mentions a 562.5 unit period,
+ * and RC-5 uses a 888.8 period.
+ * Currently, driver assumes a clock provided by a 12 MHz XTAL, but
+ * a modprobe parameter can adjust it.
+ * Adjustments are required for other timings.
+ * It seems that the 900ms timing for NEC is used to detect a RC-5
+ * IR, in order to discard such decoding
+ */
+
+ switch (ir->rc_type) {
+ case RC_TYPE_NEC:
+ leader = 900; /* ms */
+ pulse = 700; /* ms - the actual value would be 562 */
+ break;
+ default:
+ case RC_TYPE_RC5:
+ leader = 900; /* ms - from the NEC decoding */
+ pulse = 1780; /* ms - The actual value would be 1776 */
+ break;
+ }
+
+ pulse = ir_clock_mhz * pulse;
+ leader = ir_clock_mhz * leader;
+ if (ir->rc_type == RC_TYPE_NEC)
+ leader = leader | 0x8000;
+
+ dprintk(2, "%s: %s, %d MHz, leader = 0x%04x, pulse = 0x%06x \n",
+ __func__,
+ (ir->rc_type == RC_TYPE_NEC) ? "NEC" : "RC-5",
+ ir_clock_mhz, leader, pulse);
+
+ /* Remote WAKEUP = enable, normal mode, from IR decoder output */
+ tm6000_set_reg(dev, TM6010_REQ07_RE5_REMOTE_WAKEUP, 0xfe);
+
+ /* Enable IR reception on non-busrt mode */
+ tm6000_set_reg(dev, TM6010_REQ07_RD8_IR, 0x2f);
+
+ /* IR_WKUP_SEL = Low byte in decoded IR data */
+ tm6000_set_reg(dev, TM6010_REQ07_RDA_IR_WAKEUP_SEL, 0xff);
+ /* IR_WKU_ADD code */
+ tm6000_set_reg(dev, TM6010_REQ07_RDB_IR_WAKEUP_ADD, 0xff);
+
+ tm6000_set_reg(dev, TM6010_REQ07_RDC_IR_LEADER1, leader >> 8);
+ tm6000_set_reg(dev, TM6010_REQ07_RDD_IR_LEADER0, leader);
+
+ tm6000_set_reg(dev, TM6010_REQ07_RDE_IR_PULSE_CNT1, pulse >> 8);
+ tm6000_set_reg(dev, TM6010_REQ07_RDF_IR_PULSE_CNT0, pulse);
+
+ if (!ir->polling)
+ tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 0);
+ else
+ tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 1);
+ msleep(10);
+
+ /* Shows that IR is working via the LED */
+ tm6000_flash_led(dev, 0);
+ msleep(100);
+ tm6000_flash_led(dev, 1);
+ ir->pwled = 1;
+
+ return 0;
+}
+
+static void tm6000_ir_urb_received(struct urb *urb)
+{
+ struct tm6000_core *dev = urb->context;
+ struct tm6000_IR *ir = dev->ir;
+ struct tm6000_ir_poll_result poll_result;
+ char *buf;
+ int rc;
+
+ dprintk(2, "%s\n",__func__);
+ if (urb->status < 0 || urb->actual_length <= 0) {
+ printk(KERN_INFO "tm6000: IR URB failure: status: %i, length %i\n",
+ urb->status, urb->actual_length);
+ ir->submit_urb = 1;
+ schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY));
+ return;
+ }
+ buf = urb->transfer_buffer;
+
+ if (ir_debug)
+ print_hex_dump(KERN_DEBUG, "tm6000: IR data: ",
+ DUMP_PREFIX_OFFSET,16, 1,
+ buf, urb->actual_length, false);
+
+ poll_result.rc_data = buf[0];
+ if (urb->actual_length > 1)
+ poll_result.rc_data |= buf[1] << 8;
+
+ dprintk(1, "%s, scancode: 0x%04x\n",__func__, poll_result.rc_data);
+ rc_keydown(ir->rc, poll_result.rc_data, 0);
+
+ rc = usb_submit_urb(urb, GFP_ATOMIC);
+ /*
+ * Flash the led. We can't do it here, as it is running on IRQ context.
+ * So, use the scheduler to do it, in a few ms.
+ */
+ ir->pwled = 2;
+ schedule_delayed_work(&ir->work, msecs_to_jiffies(10));
+}
+
+static void tm6000_ir_handle_key(struct work_struct *work)
+{
+ struct tm6000_IR *ir = container_of(work, struct tm6000_IR, work.work);
+ struct tm6000_core *dev = ir->dev;
+ struct tm6000_ir_poll_result poll_result;
+ int rc;
+ u8 buf[2];
+
+ if (ir->wait)
+ return;
+
+ dprintk(3, "%s\n",__func__);
+
+ rc = tm6000_read_write_usb(dev, USB_DIR_IN |
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ REQ_02_GET_IR_CODE, 0, 0, buf, 2);
+ if (rc < 0)
+ return;
+
+ if (rc > 1)
+ poll_result.rc_data = buf[0] | buf[1] << 8;
+ else
+ poll_result.rc_data = buf[0];
+
+ /* Check if something was read */
+ if ((poll_result.rc_data & 0xff) == 0xff) {
+ if (!ir->pwled) {
+ tm6000_flash_led(dev, 1);
+ ir->pwled = 1;
+ }
+ return;
+ }
+
+ dprintk(1, "%s, scancode: 0x%04x\n",__func__, poll_result.rc_data);
+ rc_keydown(ir->rc, poll_result.rc_data, 0);
+ tm6000_flash_led(dev, 0);
+ ir->pwled = 0;
+
+ /* Re-schedule polling */
+ schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling));
+}
+
+static void tm6000_ir_int_work(struct work_struct *work)
+{
+ struct tm6000_IR *ir = container_of(work, struct tm6000_IR, work.work);
+ struct tm6000_core *dev = ir->dev;
+ int rc;
+
+ dprintk(3, "%s, submit_urb = %d, pwled = %d\n",__func__, ir->submit_urb,
+ ir->pwled);
+
+ if (ir->submit_urb) {
+ dprintk(3, "Resubmit urb\n");
+ tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 0);
+
+ rc = usb_submit_urb(ir->int_urb, GFP_ATOMIC);
+ if (rc < 0) {
+ printk(KERN_ERR "tm6000: Can't submit an IR interrupt. Error %i\n",
+ rc);
+ /* Retry in 100 ms */
+ schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY));
+ return;
+ }
+ ir->submit_urb = 0;
+ }
+
+ /* Led is enabled only if USB submit doesn't fail */
+ if (ir->pwled == 2) {
+ tm6000_flash_led(dev, 0);
+ ir->pwled = 0;
+ schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_INT_LED_DELAY));
+ } else if (!ir->pwled) {
+ tm6000_flash_led(dev, 1);
+ ir->pwled = 1;
+ }
+}
+
+static int tm6000_ir_start(struct rc_dev *rc)
+{
+ struct tm6000_IR *ir = rc->priv;
+
+ dprintk(2, "%s\n",__func__);
+
+ schedule_delayed_work(&ir->work, 0);
+
+ return 0;
+}
+
+static void tm6000_ir_stop(struct rc_dev *rc)
+{
+ struct tm6000_IR *ir = rc->priv;
+
+ dprintk(2, "%s\n",__func__);
+
+ cancel_delayed_work_sync(&ir->work);
+}
+
+static int tm6000_ir_change_protocol(struct rc_dev *rc, u64 rc_type)
+{
+ struct tm6000_IR *ir = rc->priv;
+
+ if (!ir)
+ return 0;
+
+ dprintk(2, "%s\n",__func__);
+
+ if ((rc->rc_map.scan) && (rc_type == RC_TYPE_NEC))
+ ir->key_addr = ((rc->rc_map.scan[0].scancode >> 8) & 0xffff);
+
+ ir->rc_type = rc_type;
+
+ tm6000_ir_config(ir);
+ /* TODO */
+ return 0;
+}
+
+static int __tm6000_ir_int_start(struct rc_dev *rc)
+{
+ struct tm6000_IR *ir = rc->priv;
+ struct tm6000_core *dev = ir->dev;
+ int pipe, size;
+ int err = -ENOMEM;
+
+ if (!ir)
+ return -ENODEV;
+
+ dprintk(2, "%s\n",__func__);
+
+ ir->int_urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!ir->int_urb)
+ return -ENOMEM;
+
+ pipe = usb_rcvintpipe(dev->udev,
+ dev->int_in.endp->desc.bEndpointAddress
+ & USB_ENDPOINT_NUMBER_MASK);
+
+ size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe));
+ dprintk(1, "IR max size: %d\n", size);
+
+ ir->int_urb->transfer_buffer = kzalloc(size, GFP_ATOMIC);
+ if (ir->int_urb->transfer_buffer == NULL) {
+ usb_free_urb(ir->int_urb);
+ return err;
+ }
+ dprintk(1, "int interval: %d\n", dev->int_in.endp->desc.bInterval);
+
+ usb_fill_int_urb(ir->int_urb, dev->udev, pipe,
+ ir->int_urb->transfer_buffer, size,
+ tm6000_ir_urb_received, dev,
+ dev->int_in.endp->desc.bInterval);
+
+ ir->submit_urb = 1;
+ schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY));
+
+ return 0;
+}
+
+static void __tm6000_ir_int_stop(struct rc_dev *rc)
+{
+ struct tm6000_IR *ir = rc->priv;
+
+ if (!ir || !ir->int_urb)
+ return;
+
+ dprintk(2, "%s\n",__func__);
+
+ usb_kill_urb(ir->int_urb);
+ kfree(ir->int_urb->transfer_buffer);
+ usb_free_urb(ir->int_urb);
+ ir->int_urb = NULL;
+}
+
+int tm6000_ir_int_start(struct tm6000_core *dev)
+{
+ struct tm6000_IR *ir = dev->ir;
+
+ if (!ir)
+ return 0;
+
+ return __tm6000_ir_int_start(ir->rc);
+}
+
+void tm6000_ir_int_stop(struct tm6000_core *dev)
+{
+ struct tm6000_IR *ir = dev->ir;
+
+ if (!ir || !ir->rc)
+ return;
+
+ __tm6000_ir_int_stop(ir->rc);
+}
+
+int tm6000_ir_init(struct tm6000_core *dev)
+{
+ struct tm6000_IR *ir;
+ struct rc_dev *rc;
+ int err = -ENOMEM;
+
+ if (!enable_ir)
+ return -ENODEV;
+
+ if (!dev->caps.has_remote)
+ return 0;
+
+ if (!dev->ir_codes)
+ return 0;
+
+ ir = kzalloc(sizeof(*ir), GFP_ATOMIC);
+ rc = rc_allocate_device();
+ if (!ir || !rc)
+ goto out;
+
+ dprintk(2, "%s\n", __func__);
+
+ /* record handles to ourself */
+ ir->dev = dev;
+ dev->ir = ir;
+ ir->rc = rc;
+
+ /* input setup */
+ rc->allowed_protos = RC_TYPE_RC5 | RC_TYPE_NEC;
+ /* Neded, in order to support NEC remotes with 24 or 32 bits */
+ rc->scanmask = 0xffff;
+ rc->priv = ir;
+ rc->change_protocol = tm6000_ir_change_protocol;
+ if (dev->int_in.endp) {
+ rc->open = __tm6000_ir_int_start;
+ rc->close = __tm6000_ir_int_stop;
+ INIT_DELAYED_WORK(&ir->work, tm6000_ir_int_work);
+ } else {
+ rc->open = tm6000_ir_start;
+ rc->close = tm6000_ir_stop;
+ ir->polling = 50;
+ INIT_DELAYED_WORK(&ir->work, tm6000_ir_handle_key);
+ }
+ rc->driver_type = RC_DRIVER_SCANCODE;
+
+ snprintf(ir->name, sizeof(ir->name), "tm5600/60x0 IR (%s)",
+ dev->name);
+
+ usb_make_path(dev->udev, ir->phys, sizeof(ir->phys));
+ strlcat(ir->phys, "/input0", sizeof(ir->phys));
+
+ tm6000_ir_change_protocol(rc, RC_TYPE_UNKNOWN);
+
+ rc->input_name = ir->name;
+ rc->input_phys = ir->phys;
+ rc->input_id.bustype = BUS_USB;
+ rc->input_id.version = 1;
+ rc->input_id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
+ rc->input_id.product = le16_to_cpu(dev->udev->descriptor.idProduct);
+ rc->map_name = dev->ir_codes;
+ rc->driver_name = "tm6000";
+ rc->dev.parent = &dev->udev->dev;
+
+ /* ir register */
+ err = rc_register_device(rc);
+ if (err)
+ goto out;
+
+ return 0;
+
+out:
+ dev->ir = NULL;
+ rc_free_device(rc);
+ kfree(ir);
+ return err;
+}
+
+int tm6000_ir_fini(struct tm6000_core *dev)
+{
+ struct tm6000_IR *ir = dev->ir;
+
+ /* skip detach on non attached board */
+
+ if (!ir)
+ return 0;
+
+ dprintk(2, "%s\n",__func__);
+
+ if (!ir->polling)
+ __tm6000_ir_int_stop(ir->rc);
+
+ tm6000_ir_stop(ir->rc);
+
+ /* Turn off the led */
+ tm6000_flash_led(dev, 0);
+ ir->pwled = 0;
+
+ rc_unregister_device(ir->rc);
+
+ kfree(ir);
+ dev->ir = NULL;
+
+ return 0;
+}
diff --git a/drivers/media/video/tm6000/tm6000-regs.h b/drivers/media/video/tm6000/tm6000-regs.h
new file mode 100644
index 00000000..a38c251e
--- /dev/null
+++ b/drivers/media/video/tm6000/tm6000-regs.h
@@ -0,0 +1,600 @@
+/*
+ * tm6000-regs.h - driver for TM5600/TM6000/TM6010 USB video capture devices
+ *
+ * Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Define TV Master TM5600/TM6000/TM6010 Request codes
+ */
+#define REQ_00_SET_IR_VALUE 0
+#define REQ_01_SET_WAKEUP_IRCODE 1
+#define REQ_02_GET_IR_CODE 2
+#define REQ_03_SET_GET_MCU_PIN 3
+#define REQ_04_EN_DISABLE_MCU_INT 4
+#define REQ_05_SET_GET_USBREG 5
+ /* Write: RegNum, Value, 0 */
+ /* Read : RegNum, Value, 1, RegStatus */
+#define REQ_06_SET_GET_USBREG_BIT 6
+#define REQ_07_SET_GET_AVREG 7
+ /* Write: RegNum, Value, 0 */
+ /* Read : RegNum, Value, 1, RegStatus */
+#define REQ_08_SET_GET_AVREG_BIT 8
+#define REQ_09_SET_GET_TUNER_FQ 9
+#define REQ_10_SET_TUNER_SYSTEM 10
+#define REQ_11_SET_EEPROM_ADDR 11
+#define REQ_12_SET_GET_EEPROMBYTE 12
+#define REQ_13_GET_EEPROM_SEQREAD 13
+#define REQ_14_SET_GET_I2C_WR2_RDN 14
+#define REQ_15_SET_GET_I2CBYTE 15
+ /* Write: Subaddr, Slave Addr, value, 0 */
+ /* Read : Subaddr, Slave Addr, value, 1 */
+#define REQ_16_SET_GET_I2C_WR1_RDN 16
+ /* Subaddr, Slave Addr, 0, length */
+#define REQ_17_SET_GET_I2CFP 17
+ /* Write: Slave Addr, register, value */
+ /* Read : Slave Addr, register, 2, data */
+#define REQ_20_DATA_TRANSFER 20
+#define REQ_30_I2C_WRITE 30
+#define REQ_31_I2C_READ 31
+#define REQ_35_AFTEK_TUNER_READ 35
+#define REQ_40_GET_VERSION 40
+#define REQ_50_SET_START 50
+#define REQ_51_SET_STOP 51
+#define REQ_52_TRANSMIT_DATA 52
+#define REQ_53_SPI_INITIAL 53
+#define REQ_54_SPI_SETSTART 54
+#define REQ_55_SPI_INOUTDATA 55
+#define REQ_56_SPI_SETSTOP 56
+
+/*
+ * Define TV Master TM5600/TM6000/TM6010 GPIO lines
+ */
+
+#define TM6000_GPIO_CLK 0x101
+#define TM6000_GPIO_DATA 0x100
+
+#define TM6000_GPIO_1 0x102
+#define TM6000_GPIO_2 0x103
+#define TM6000_GPIO_3 0x104
+#define TM6000_GPIO_4 0x300
+#define TM6000_GPIO_5 0x301
+#define TM6000_GPIO_6 0x304
+#define TM6000_GPIO_7 0x305
+
+/* tm6010 defines GPIO with different values */
+#define TM6010_GPIO_0 0x0102
+#define TM6010_GPIO_1 0x0103
+#define TM6010_GPIO_2 0x0104
+#define TM6010_GPIO_3 0x0105
+#define TM6010_GPIO_4 0x0106
+#define TM6010_GPIO_5 0x0107
+#define TM6010_GPIO_6 0x0300
+#define TM6010_GPIO_7 0x0301
+#define TM6010_GPIO_9 0x0305
+/*
+ * Define TV Master TM5600/TM6000/TM6010 URB message codes and length
+ */
+
+enum {
+ TM6000_URB_MSG_VIDEO = 1,
+ TM6000_URB_MSG_AUDIO,
+ TM6000_URB_MSG_VBI,
+ TM6000_URB_MSG_PTS,
+ TM6000_URB_MSG_ERR,
+};
+
+/* Define specific TM6000 Video decoder registers */
+#define TM6000_REQ07_RD8_TEST_SEL 0x07, 0xd8
+#define TM6000_REQ07_RD9_A_SIM_SEL 0x07, 0xd9
+#define TM6000_REQ07_RDA_CLK_SEL 0x07, 0xda
+#define TM6000_REQ07_RDB_OUT_SEL 0x07, 0xdb
+#define TM6000_REQ07_RDC_NSEL_I2S 0x07, 0xdc
+#define TM6000_REQ07_RDD_GPIO2_MDRV 0x07, 0xdd
+#define TM6000_REQ07_RDE_GPIO1_MDRV 0x07, 0xde
+#define TM6000_REQ07_RDF_PWDOWN_ACLK 0x07, 0xdf
+#define TM6000_REQ07_RE0_VADC_REF_CTL 0x07, 0xe0
+#define TM6000_REQ07_RE1_VADC_DACLIMP 0x07, 0xe1
+#define TM6000_REQ07_RE2_VADC_STATUS_CTL 0x07, 0xe2
+#define TM6000_REQ07_RE3_VADC_INP_LPF_SEL1 0x07, 0xe3
+#define TM6000_REQ07_RE4_VADC_TARGET1 0x07, 0xe4
+#define TM6000_REQ07_RE5_VADC_INP_LPF_SEL2 0x07, 0xe5
+#define TM6000_REQ07_RE6_VADC_TARGET2 0x07, 0xe6
+#define TM6000_REQ07_RE7_VADC_AGAIN_CTL 0x07, 0xe7
+#define TM6000_REQ07_RE8_VADC_PWDOWN_CTL 0x07, 0xe8
+#define TM6000_REQ07_RE9_VADC_INPUT_CTL1 0x07, 0xe9
+#define TM6000_REQ07_REA_VADC_INPUT_CTL2 0x07, 0xea
+#define TM6000_REQ07_REB_VADC_AADC_MODE 0x07, 0xeb
+#define TM6000_REQ07_REC_VADC_AADC_LVOL 0x07, 0xec
+#define TM6000_REQ07_RED_VADC_AADC_RVOL 0x07, 0xed
+#define TM6000_REQ07_REE_VADC_CTRL_SEL_CONTROL 0x07, 0xee
+#define TM6000_REQ07_REF_VADC_GAIN_MAP_CTL 0x07, 0xef
+#define TM6000_REQ07_RFD_BIST_ERR_VST_LOW 0x07, 0xfd
+#define TM6000_REQ07_RFE_BIST_ERR_VST_HIGH 0x07, 0xfe
+
+/* Define TM6000/TM6010 Video decoder registers */
+#define TM6010_REQ07_R00_VIDEO_CONTROL0 0x07, 0x00
+#define TM6010_REQ07_R01_VIDEO_CONTROL1 0x07, 0x01
+#define TM6010_REQ07_R02_VIDEO_CONTROL2 0x07, 0x02
+#define TM6010_REQ07_R03_YC_SEP_CONTROL 0x07, 0x03
+#define TM6010_REQ07_R04_LUMA_HAGC_CONTROL 0x07, 0x04
+#define TM6010_REQ07_R05_NOISE_THRESHOLD 0x07, 0x05
+#define TM6010_REQ07_R06_AGC_GATE_THRESHOLD 0x07, 0x06
+#define TM6010_REQ07_R07_OUTPUT_CONTROL 0x07, 0x07
+#define TM6010_REQ07_R08_LUMA_CONTRAST_ADJ 0x07, 0x08
+#define TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ 0x07, 0x09
+#define TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ 0x07, 0x0a
+#define TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ 0x07, 0x0b
+#define TM6010_REQ07_R0C_CHROMA_AGC_CONTROL 0x07, 0x0c
+#define TM6010_REQ07_R0D_CHROMA_KILL_LEVEL 0x07, 0x0d
+#define TM6010_REQ07_R0F_CHROMA_AUTO_POSITION 0x07, 0x0f
+#define TM6010_REQ07_R10_AGC_PEAK_NOMINAL 0x07, 0x10
+#define TM6010_REQ07_R11_AGC_PEAK_CONTROL 0x07, 0x11
+#define TM6010_REQ07_R12_AGC_GATE_STARTH 0x07, 0x12
+#define TM6010_REQ07_R13_AGC_GATE_STARTL 0x07, 0x13
+#define TM6010_REQ07_R14_AGC_GATE_WIDTH 0x07, 0x14
+#define TM6010_REQ07_R15_AGC_BP_DELAY 0x07, 0x15
+#define TM6010_REQ07_R16_LOCK_COUNT 0x07, 0x16
+#define TM6010_REQ07_R17_HLOOP_MAXSTATE 0x07, 0x17
+#define TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3 0x07, 0x18
+#define TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2 0x07, 0x19
+#define TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1 0x07, 0x1a
+#define TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0 0x07, 0x1b
+#define TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3 0x07, 0x1c
+#define TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2 0x07, 0x1d
+#define TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1 0x07, 0x1e
+#define TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0 0x07, 0x1f
+#define TM6010_REQ07_R20_HSYNC_RISING_EDGE_TIME 0x07, 0x20
+#define TM6010_REQ07_R21_HSYNC_PHASE_OFFSET 0x07, 0x21
+#define TM6010_REQ07_R22_HSYNC_PLL_START_TIME 0x07, 0x22
+#define TM6010_REQ07_R23_HSYNC_PLL_END_TIME 0x07, 0x23
+#define TM6010_REQ07_R24_HSYNC_TIP_START_TIME 0x07, 0x24
+#define TM6010_REQ07_R25_HSYNC_TIP_END_TIME 0x07, 0x25
+#define TM6010_REQ07_R26_HSYNC_RISING_EDGE_START 0x07, 0x26
+#define TM6010_REQ07_R27_HSYNC_RISING_EDGE_END 0x07, 0x27
+#define TM6010_REQ07_R28_BACKPORCH_START 0x07, 0x28
+#define TM6010_REQ07_R29_BACKPORCH_END 0x07, 0x29
+#define TM6010_REQ07_R2A_HSYNC_FILTER_START 0x07, 0x2a
+#define TM6010_REQ07_R2B_HSYNC_FILTER_END 0x07, 0x2b
+#define TM6010_REQ07_R2C_CHROMA_BURST_START 0x07, 0x2c
+#define TM6010_REQ07_R2D_CHROMA_BURST_END 0x07, 0x2d
+#define TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART 0x07, 0x2e
+#define TM6010_REQ07_R2F_ACTIVE_VIDEO_HWIDTH 0x07, 0x2f
+#define TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART 0x07, 0x30
+#define TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT 0x07, 0x31
+#define TM6010_REQ07_R32_VSYNC_HLOCK_MIN 0x07, 0x32
+#define TM6010_REQ07_R33_VSYNC_HLOCK_MAX 0x07, 0x33
+#define TM6010_REQ07_R34_VSYNC_AGC_MIN 0x07, 0x34
+#define TM6010_REQ07_R35_VSYNC_AGC_MAX 0x07, 0x35
+#define TM6010_REQ07_R36_VSYNC_VBI_MIN 0x07, 0x36
+#define TM6010_REQ07_R37_VSYNC_VBI_MAX 0x07, 0x37
+#define TM6010_REQ07_R38_VSYNC_THRESHOLD 0x07, 0x38
+#define TM6010_REQ07_R39_VSYNC_TIME_CONSTANT 0x07, 0x39
+#define TM6010_REQ07_R3A_STATUS1 0x07, 0x3a
+#define TM6010_REQ07_R3B_STATUS2 0x07, 0x3b
+#define TM6010_REQ07_R3C_STATUS3 0x07, 0x3c
+#define TM6010_REQ07_R3F_RESET 0x07, 0x3f
+#define TM6010_REQ07_R40_TELETEXT_VBI_CODE0 0x07, 0x40
+#define TM6010_REQ07_R41_TELETEXT_VBI_CODE1 0x07, 0x41
+#define TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL 0x07, 0x42
+#define TM6010_REQ07_R43_VBI_DATA_TYPE_LINE7 0x07, 0x43
+#define TM6010_REQ07_R44_VBI_DATA_TYPE_LINE8 0x07, 0x44
+#define TM6010_REQ07_R45_VBI_DATA_TYPE_LINE9 0x07, 0x45
+#define TM6010_REQ07_R46_VBI_DATA_TYPE_LINE10 0x07, 0x46
+#define TM6010_REQ07_R47_VBI_DATA_TYPE_LINE11 0x07, 0x47
+#define TM6010_REQ07_R48_VBI_DATA_TYPE_LINE12 0x07, 0x48
+#define TM6010_REQ07_R49_VBI_DATA_TYPE_LINE13 0x07, 0x49
+#define TM6010_REQ07_R4A_VBI_DATA_TYPE_LINE14 0x07, 0x4a
+#define TM6010_REQ07_R4B_VBI_DATA_TYPE_LINE15 0x07, 0x4b
+#define TM6010_REQ07_R4C_VBI_DATA_TYPE_LINE16 0x07, 0x4c
+#define TM6010_REQ07_R4D_VBI_DATA_TYPE_LINE17 0x07, 0x4d
+#define TM6010_REQ07_R4E_VBI_DATA_TYPE_LINE18 0x07, 0x4e
+#define TM6010_REQ07_R4F_VBI_DATA_TYPE_LINE19 0x07, 0x4f
+#define TM6010_REQ07_R50_VBI_DATA_TYPE_LINE20 0x07, 0x50
+#define TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21 0x07, 0x51
+#define TM6010_REQ07_R52_VBI_DATA_TYPE_LINE22 0x07, 0x52
+#define TM6010_REQ07_R53_VBI_DATA_TYPE_LINE23 0x07, 0x53
+#define TM6010_REQ07_R54_VBI_DATA_TYPE_RLINES 0x07, 0x54
+#define TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN 0x07, 0x55
+#define TM6010_REQ07_R56_VBI_LOOP_FILTER_I_GAIN 0x07, 0x56
+#define TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN 0x07, 0x57
+#define TM6010_REQ07_R58_VBI_CAPTION_DTO1 0x07, 0x58
+#define TM6010_REQ07_R59_VBI_CAPTION_DTO0 0x07, 0x59
+#define TM6010_REQ07_R5A_VBI_TELETEXT_DTO1 0x07, 0x5a
+#define TM6010_REQ07_R5B_VBI_TELETEXT_DTO0 0x07, 0x5b
+#define TM6010_REQ07_R5C_VBI_WSS625_DTO1 0x07, 0x5c
+#define TM6010_REQ07_R5D_VBI_WSS625_DTO0 0x07, 0x5d
+#define TM6010_REQ07_R5E_VBI_CAPTION_FRAME_START 0x07, 0x5e
+#define TM6010_REQ07_R5F_VBI_WSS625_FRAME_START 0x07, 0x5f
+#define TM6010_REQ07_R60_TELETEXT_FRAME_START 0x07, 0x60
+#define TM6010_REQ07_R61_VBI_CCDATA1 0x07, 0x61
+#define TM6010_REQ07_R62_VBI_CCDATA2 0x07, 0x62
+#define TM6010_REQ07_R63_VBI_WSS625_DATA1 0x07, 0x63
+#define TM6010_REQ07_R64_VBI_WSS625_DATA2 0x07, 0x64
+#define TM6010_REQ07_R65_VBI_DATA_STATUS 0x07, 0x65
+#define TM6010_REQ07_R66_VBI_CAPTION_START 0x07, 0x66
+#define TM6010_REQ07_R67_VBI_WSS625_START 0x07, 0x67
+#define TM6010_REQ07_R68_VBI_TELETEXT_START 0x07, 0x68
+#define TM6010_REQ07_R70_HSYNC_DTO_INC_STATUS3 0x07, 0x70
+#define TM6010_REQ07_R71_HSYNC_DTO_INC_STATUS2 0x07, 0x71
+#define TM6010_REQ07_R72_HSYNC_DTO_INC_STATUS1 0x07, 0x72
+#define TM6010_REQ07_R73_HSYNC_DTO_INC_STATUS0 0x07, 0x73
+#define TM6010_REQ07_R74_CHROMA_DTO_INC_STATUS3 0x07, 0x74
+#define TM6010_REQ07_R75_CHROMA_DTO_INC_STATUS2 0x07, 0x75
+#define TM6010_REQ07_R76_CHROMA_DTO_INC_STATUS1 0x07, 0x76
+#define TM6010_REQ07_R77_CHROMA_DTO_INC_STATUS0 0x07, 0x77
+#define TM6010_REQ07_R78_AGC_AGAIN_STATUS 0x07, 0x78
+#define TM6010_REQ07_R79_AGC_DGAIN_STATUS 0x07, 0x79
+#define TM6010_REQ07_R7A_CHROMA_MAG_STATUS 0x07, 0x7a
+#define TM6010_REQ07_R7B_CHROMA_GAIN_STATUS1 0x07, 0x7b
+#define TM6010_REQ07_R7C_CHROMA_GAIN_STATUS0 0x07, 0x7c
+#define TM6010_REQ07_R7D_CORDIC_FREQ_STATUS 0x07, 0x7d
+#define TM6010_REQ07_R7F_STATUS_NOISE 0x07, 0x7f
+#define TM6010_REQ07_R80_COMB_FILTER_TRESHOLD 0x07, 0x80
+#define TM6010_REQ07_R82_COMB_FILTER_CONFIG 0x07, 0x82
+#define TM6010_REQ07_R83_CHROMA_LOCK_CONFIG 0x07, 0x83
+#define TM6010_REQ07_R84_NOISE_NTSC_C 0x07, 0x84
+#define TM6010_REQ07_R85_NOISE_PAL_C 0x07, 0x85
+#define TM6010_REQ07_R86_NOISE_PHASE_C 0x07, 0x86
+#define TM6010_REQ07_R87_NOISE_PHASE_Y 0x07, 0x87
+#define TM6010_REQ07_R8A_CHROMA_LOOPFILTER_STATE 0x07, 0x8a
+#define TM6010_REQ07_R8B_CHROMA_HRESAMPLER 0x07, 0x8b
+#define TM6010_REQ07_R8D_CPUMP_DELAY_ADJ 0x07, 0x8d
+#define TM6010_REQ07_R8E_CPUMP_ADJ 0x07, 0x8e
+#define TM6010_REQ07_R8F_CPUMP_DELAY 0x07, 0x8f
+
+/* Define TM6000/TM6010 Miscellaneous registers */
+#define TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE 0x07, 0xc0
+#define TM6010_REQ07_RC1_TRESHOLD 0x07, 0xc1
+#define TM6010_REQ07_RC2_HSYNC_WIDTH 0x07, 0xc2
+#define TM6010_REQ07_RC3_HSTART1 0x07, 0xc3
+#define TM6010_REQ07_RC4_HSTART0 0x07, 0xc4
+#define TM6010_REQ07_RC5_HEND1 0x07, 0xc5
+#define TM6010_REQ07_RC6_HEND0 0x07, 0xc6
+#define TM6010_REQ07_RC7_VSTART1 0x07, 0xc7
+#define TM6010_REQ07_RC8_VSTART0 0x07, 0xc8
+#define TM6010_REQ07_RC9_VEND1 0x07, 0xc9
+#define TM6010_REQ07_RCA_VEND0 0x07, 0xca
+#define TM6010_REQ07_RCB_DELAY 0x07, 0xcb
+/* ONLY for TM6010 */
+#define TM6010_REQ07_RCC_ACTIVE_IF 0x07, 0xcc
+#define TM6010_REQ07_RCC_ACTIVE_IF_VIDEO_ENABLE (1 << 5)
+#define TM6010_REQ07_RCC_ACTIVE_IF_AUDIO_ENABLE (1 << 6)
+#define TM6010_REQ07_RD0_USB_PERIPHERY_CONTROL 0x07, 0xd0
+#define TM6010_REQ07_RD1_ADDR_FOR_REQ1 0x07, 0xd1
+#define TM6010_REQ07_RD2_ADDR_FOR_REQ2 0x07, 0xd2
+#define TM6010_REQ07_RD3_ADDR_FOR_REQ3 0x07, 0xd3
+#define TM6010_REQ07_RD4_ADDR_FOR_REQ4 0x07, 0xd4
+#define TM6010_REQ07_RD5_POWERSAVE 0x07, 0xd5
+#define TM6010_REQ07_RD6_ENDP_REQ1_REQ2 0x07, 0xd6
+#define TM6010_REQ07_RD7_ENDP_REQ3_REQ4 0x07, 0xd7
+/* ONLY for TM6010 */
+#define TM6010_REQ07_RD8_IR 0x07, 0xd8
+/* ONLY for TM6010 */
+#define TM6010_REQ07_RD9_IR_BSIZE 0x07, 0xd9
+/* ONLY for TM6010 */
+#define TM6010_REQ07_RDA_IR_WAKEUP_SEL 0x07, 0xda
+/* ONLY for TM6010 */
+#define TM6010_REQ07_RDB_IR_WAKEUP_ADD 0x07, 0xdb
+/* ONLY for TM6010 */
+#define TM6010_REQ07_RDC_IR_LEADER1 0x07, 0xdc
+/* ONLY for TM6010 */
+#define TM6010_REQ07_RDD_IR_LEADER0 0x07, 0xdd
+/* ONLY for TM6010 */
+#define TM6010_REQ07_RDE_IR_PULSE_CNT1 0x07, 0xde
+/* ONLY for TM6010 */
+#define TM6010_REQ07_RDF_IR_PULSE_CNT0 0x07, 0xdf
+/* ONLY for TM6010 */
+#define TM6010_REQ07_RE0_DVIDEO_SOURCE 0x07, 0xe0
+/* ONLY for TM6010 */
+#define TM6010_REQ07_RE0_DVIDEO_SOURCE_IF 0x07, 0xe1
+/* ONLY for TM6010 */
+#define TM6010_REQ07_RE2_OUT_SEL2 0x07, 0xe2
+/* ONLY for TM6010 */
+#define TM6010_REQ07_RE3_OUT_SEL1 0x07, 0xe3
+/* ONLY for TM6010 */
+#define TM6010_REQ07_RE4_OUT_SEL0 0x07, 0xe4
+/* ONLY for TM6010 */
+#define TM6010_REQ07_RE5_REMOTE_WAKEUP 0x07, 0xe5
+/* ONLY for TM6010 */
+#define TM6010_REQ07_RE7_PUB_GPIO 0x07, 0xe7
+/* ONLY for TM6010 */
+#define TM6010_REQ07_RE8_TYPESEL_MOS_I2S 0x07, 0xe8
+/* ONLY for TM6010 */
+#define TM6010_REQ07_RE9_TYPESEL_MOS_TS 0x07, 0xe9
+/* ONLY for TM6010 */
+#define TM6010_REQ07_REA_TYPESEL_MOS_CCIR 0x07, 0xea
+/* ONLY for TM6010 */
+#define TM6010_REQ07_RF0_BIST_CRC_RESULT0 0x07, 0xf0
+/* ONLY for TM6010 */
+#define TM6010_REQ07_RF1_BIST_CRC_RESULT1 0x07, 0xf1
+/* ONLY for TM6010 */
+#define TM6010_REQ07_RF2_BIST_CRC_RESULT2 0x07, 0xf2
+/* ONLY for TM6010 */
+#define TM6010_REQ07_RF3_BIST_CRC_RESULT3 0x07, 0xf3
+/* ONLY for TM6010 */
+#define TM6010_REQ07_RF4_BIST_ERR_VST2 0x07, 0xf4
+/* ONLY for TM6010 */
+#define TM6010_REQ07_RF5_BIST_ERR_VST1 0x07, 0xf5
+/* ONLY for TM6010 */
+#define TM6010_REQ07_RF6_BIST_ERR_VST0 0x07, 0xf6
+/* ONLY for TM6010 */
+#define TM6010_REQ07_RF7_BIST 0x07, 0xf7
+/* ONLY for TM6010 */
+#define TM6010_REQ07_RFE_POWER_DOWN 0x07, 0xfe
+#define TM6010_REQ07_RFF_SOFT_RESET 0x07, 0xff
+
+/* Define TM6000/TM6010 USB registers */
+#define TM6010_REQ05_R00_MAIN_CTRL 0x05, 0x00
+#define TM6010_REQ05_R01_DEVADDR 0x05, 0x01
+#define TM6010_REQ05_R02_TEST 0x05, 0x02
+#define TM6010_REQ05_R04_SOFN0 0x05, 0x04
+#define TM6010_REQ05_R05_SOFN1 0x05, 0x05
+#define TM6010_REQ05_R06_SOFTM0 0x05, 0x06
+#define TM6010_REQ05_R07_SOFTM1 0x05, 0x07
+#define TM6010_REQ05_R08_PHY_TEST 0x05, 0x08
+#define TM6010_REQ05_R09_VCTL 0x05, 0x09
+#define TM6010_REQ05_R0A_VSTA 0x05, 0x0a
+#define TM6010_REQ05_R0B_CX_CFG 0x05, 0x0b
+#define TM6010_REQ05_R0C_ENDP0_REG0 0x05, 0x0c
+#define TM6010_REQ05_R10_GMASK 0x05, 0x10
+#define TM6010_REQ05_R11_IMASK0 0x05, 0x11
+#define TM6010_REQ05_R12_IMASK1 0x05, 0x12
+#define TM6010_REQ05_R13_IMASK2 0x05, 0x13
+#define TM6010_REQ05_R14_IMASK3 0x05, 0x14
+#define TM6010_REQ05_R15_IMASK4 0x05, 0x15
+#define TM6010_REQ05_R16_IMASK5 0x05, 0x16
+#define TM6010_REQ05_R17_IMASK6 0x05, 0x17
+#define TM6010_REQ05_R18_IMASK7 0x05, 0x18
+#define TM6010_REQ05_R19_ZEROP0 0x05, 0x19
+#define TM6010_REQ05_R1A_ZEROP1 0x05, 0x1a
+#define TM6010_REQ05_R1C_FIFO_EMP0 0x05, 0x1c
+#define TM6010_REQ05_R1D_FIFO_EMP1 0x05, 0x1d
+#define TM6010_REQ05_R20_IRQ_GROUP 0x05, 0x20
+#define TM6010_REQ05_R21_IRQ_SOURCE0 0x05, 0x21
+#define TM6010_REQ05_R22_IRQ_SOURCE1 0x05, 0x22
+#define TM6010_REQ05_R23_IRQ_SOURCE2 0x05, 0x23
+#define TM6010_REQ05_R24_IRQ_SOURCE3 0x05, 0x24
+#define TM6010_REQ05_R25_IRQ_SOURCE4 0x05, 0x25
+#define TM6010_REQ05_R26_IRQ_SOURCE5 0x05, 0x26
+#define TM6010_REQ05_R27_IRQ_SOURCE6 0x05, 0x27
+#define TM6010_REQ05_R28_IRQ_SOURCE7 0x05, 0x28
+#define TM6010_REQ05_R29_SEQ_ERR0 0x05, 0x29
+#define TM6010_REQ05_R2A_SEQ_ERR1 0x05, 0x2a
+#define TM6010_REQ05_R2B_SEQ_ABORT0 0x05, 0x2b
+#define TM6010_REQ05_R2C_SEQ_ABORT1 0x05, 0x2c
+#define TM6010_REQ05_R2D_TX_ZERO0 0x05, 0x2d
+#define TM6010_REQ05_R2E_TX_ZERO1 0x05, 0x2e
+#define TM6010_REQ05_R2F_IDLE_CNT 0x05, 0x2f
+#define TM6010_REQ05_R30_FNO_P1 0x05, 0x30
+#define TM6010_REQ05_R31_FNO_P2 0x05, 0x31
+#define TM6010_REQ05_R32_FNO_P3 0x05, 0x32
+#define TM6010_REQ05_R33_FNO_P4 0x05, 0x33
+#define TM6010_REQ05_R34_FNO_P5 0x05, 0x34
+#define TM6010_REQ05_R35_FNO_P6 0x05, 0x35
+#define TM6010_REQ05_R36_FNO_P7 0x05, 0x36
+#define TM6010_REQ05_R37_FNO_P8 0x05, 0x37
+#define TM6010_REQ05_R38_FNO_P9 0x05, 0x38
+#define TM6010_REQ05_R30_FNO_P10 0x05, 0x39
+#define TM6010_REQ05_R30_FNO_P11 0x05, 0x3a
+#define TM6010_REQ05_R30_FNO_P12 0x05, 0x3b
+#define TM6010_REQ05_R30_FNO_P13 0x05, 0x3c
+#define TM6010_REQ05_R30_FNO_P14 0x05, 0x3d
+#define TM6010_REQ05_R30_FNO_P15 0x05, 0x3e
+#define TM6010_REQ05_R40_IN_MAXPS_LOW1 0x05, 0x40
+#define TM6010_REQ05_R41_IN_MAXPS_HIGH1 0x05, 0x41
+#define TM6010_REQ05_R42_IN_MAXPS_LOW2 0x05, 0x42
+#define TM6010_REQ05_R43_IN_MAXPS_HIGH2 0x05, 0x43
+#define TM6010_REQ05_R44_IN_MAXPS_LOW3 0x05, 0x44
+#define TM6010_REQ05_R45_IN_MAXPS_HIGH3 0x05, 0x45
+#define TM6010_REQ05_R46_IN_MAXPS_LOW4 0x05, 0x46
+#define TM6010_REQ05_R47_IN_MAXPS_HIGH4 0x05, 0x47
+#define TM6010_REQ05_R48_IN_MAXPS_LOW5 0x05, 0x48
+#define TM6010_REQ05_R49_IN_MAXPS_HIGH5 0x05, 0x49
+#define TM6010_REQ05_R4A_IN_MAXPS_LOW6 0x05, 0x4a
+#define TM6010_REQ05_R4B_IN_MAXPS_HIGH6 0x05, 0x4b
+#define TM6010_REQ05_R4C_IN_MAXPS_LOW7 0x05, 0x4c
+#define TM6010_REQ05_R4D_IN_MAXPS_HIGH7 0x05, 0x4d
+#define TM6010_REQ05_R4E_IN_MAXPS_LOW8 0x05, 0x4e
+#define TM6010_REQ05_R4F_IN_MAXPS_HIGH8 0x05, 0x4f
+#define TM6010_REQ05_R50_IN_MAXPS_LOW9 0x05, 0x50
+#define TM6010_REQ05_R51_IN_MAXPS_HIGH9 0x05, 0x51
+#define TM6010_REQ05_R40_IN_MAXPS_LOW10 0x05, 0x52
+#define TM6010_REQ05_R41_IN_MAXPS_HIGH10 0x05, 0x53
+#define TM6010_REQ05_R40_IN_MAXPS_LOW11 0x05, 0x54
+#define TM6010_REQ05_R41_IN_MAXPS_HIGH11 0x05, 0x55
+#define TM6010_REQ05_R40_IN_MAXPS_LOW12 0x05, 0x56
+#define TM6010_REQ05_R41_IN_MAXPS_HIGH12 0x05, 0x57
+#define TM6010_REQ05_R40_IN_MAXPS_LOW13 0x05, 0x58
+#define TM6010_REQ05_R41_IN_MAXPS_HIGH13 0x05, 0x59
+#define TM6010_REQ05_R40_IN_MAXPS_LOW14 0x05, 0x5a
+#define TM6010_REQ05_R41_IN_MAXPS_HIGH14 0x05, 0x5b
+#define TM6010_REQ05_R40_IN_MAXPS_LOW15 0x05, 0x5c
+#define TM6010_REQ05_R41_IN_MAXPS_HIGH15 0x05, 0x5d
+#define TM6010_REQ05_R60_OUT_MAXPS_LOW1 0x05, 0x60
+#define TM6010_REQ05_R61_OUT_MAXPS_HIGH1 0x05, 0x61
+#define TM6010_REQ05_R62_OUT_MAXPS_LOW2 0x05, 0x62
+#define TM6010_REQ05_R63_OUT_MAXPS_HIGH2 0x05, 0x63
+#define TM6010_REQ05_R64_OUT_MAXPS_LOW3 0x05, 0x64
+#define TM6010_REQ05_R65_OUT_MAXPS_HIGH3 0x05, 0x65
+#define TM6010_REQ05_R66_OUT_MAXPS_LOW4 0x05, 0x66
+#define TM6010_REQ05_R67_OUT_MAXPS_HIGH4 0x05, 0x67
+#define TM6010_REQ05_R68_OUT_MAXPS_LOW5 0x05, 0x68
+#define TM6010_REQ05_R69_OUT_MAXPS_HIGH5 0x05, 0x69
+#define TM6010_REQ05_R6A_OUT_MAXPS_LOW6 0x05, 0x6a
+#define TM6010_REQ05_R6B_OUT_MAXPS_HIGH6 0x05, 0x6b
+#define TM6010_REQ05_R6C_OUT_MAXPS_LOW7 0x05, 0x6c
+#define TM6010_REQ05_R6D_OUT_MAXPS_HIGH7 0x05, 0x6d
+#define TM6010_REQ05_R6E_OUT_MAXPS_LOW8 0x05, 0x6e
+#define TM6010_REQ05_R6F_OUT_MAXPS_HIGH8 0x05, 0x6f
+#define TM6010_REQ05_R70_OUT_MAXPS_LOW9 0x05, 0x70
+#define TM6010_REQ05_R71_OUT_MAXPS_HIGH9 0x05, 0x71
+#define TM6010_REQ05_R60_OUT_MAXPS_LOW10 0x05, 0x72
+#define TM6010_REQ05_R61_OUT_MAXPS_HIGH10 0x05, 0x73
+#define TM6010_REQ05_R60_OUT_MAXPS_LOW11 0x05, 0x74
+#define TM6010_REQ05_R61_OUT_MAXPS_HIGH11 0x05, 0x75
+#define TM6010_REQ05_R60_OUT_MAXPS_LOW12 0x05, 0x76
+#define TM6010_REQ05_R61_OUT_MAXPS_HIGH12 0x05, 0x77
+#define TM6010_REQ05_R60_OUT_MAXPS_LOW13 0x05, 0x78
+#define TM6010_REQ05_R61_OUT_MAXPS_HIGH13 0x05, 0x79
+#define TM6010_REQ05_R60_OUT_MAXPS_LOW14 0x05, 0x7a
+#define TM6010_REQ05_R61_OUT_MAXPS_HIGH14 0x05, 0x7b
+#define TM6010_REQ05_R60_OUT_MAXPS_LOW15 0x05, 0x7c
+#define TM6010_REQ05_R61_OUT_MAXPS_HIGH15 0x05, 0x7d
+#define TM6010_REQ05_R80_FIFO0 0x05, 0x80
+#define TM6010_REQ05_R81_FIFO1 0x05, 0x81
+#define TM6010_REQ05_R82_FIFO2 0x05, 0x82
+#define TM6010_REQ05_R83_FIFO3 0x05, 0x83
+#define TM6010_REQ05_R84_FIFO4 0x05, 0x84
+#define TM6010_REQ05_R85_FIFO5 0x05, 0x85
+#define TM6010_REQ05_R86_FIFO6 0x05, 0x86
+#define TM6010_REQ05_R87_FIFO7 0x05, 0x87
+#define TM6010_REQ05_R88_FIFO8 0x05, 0x88
+#define TM6010_REQ05_R89_FIFO9 0x05, 0x89
+#define TM6010_REQ05_R81_FIFO10 0x05, 0x8a
+#define TM6010_REQ05_R81_FIFO11 0x05, 0x8b
+#define TM6010_REQ05_R81_FIFO12 0x05, 0x8c
+#define TM6010_REQ05_R81_FIFO13 0x05, 0x8d
+#define TM6010_REQ05_R81_FIFO14 0x05, 0x8e
+#define TM6010_REQ05_R81_FIFO15 0x05, 0x8f
+#define TM6010_REQ05_R90_CFG_FIFO0 0x05, 0x90
+#define TM6010_REQ05_R91_CFG_FIFO1 0x05, 0x91
+#define TM6010_REQ05_R92_CFG_FIFO2 0x05, 0x92
+#define TM6010_REQ05_R93_CFG_FIFO3 0x05, 0x93
+#define TM6010_REQ05_R94_CFG_FIFO4 0x05, 0x94
+#define TM6010_REQ05_R95_CFG_FIFO5 0x05, 0x95
+#define TM6010_REQ05_R96_CFG_FIFO6 0x05, 0x96
+#define TM6010_REQ05_R97_CFG_FIFO7 0x05, 0x97
+#define TM6010_REQ05_R98_CFG_FIFO8 0x05, 0x98
+#define TM6010_REQ05_R99_CFG_FIFO9 0x05, 0x99
+#define TM6010_REQ05_R91_CFG_FIFO10 0x05, 0x9a
+#define TM6010_REQ05_R91_CFG_FIFO11 0x05, 0x9b
+#define TM6010_REQ05_R91_CFG_FIFO12 0x05, 0x9c
+#define TM6010_REQ05_R91_CFG_FIFO13 0x05, 0x9d
+#define TM6010_REQ05_R91_CFG_FIFO14 0x05, 0x9e
+#define TM6010_REQ05_R91_CFG_FIFO15 0x05, 0x9f
+#define TM6010_REQ05_RA0_CTL_FIFO0 0x05, 0xa0
+#define TM6010_REQ05_RA1_CTL_FIFO1 0x05, 0xa1
+#define TM6010_REQ05_RA2_CTL_FIFO2 0x05, 0xa2
+#define TM6010_REQ05_RA3_CTL_FIFO3 0x05, 0xa3
+#define TM6010_REQ05_RA4_CTL_FIFO4 0x05, 0xa4
+#define TM6010_REQ05_RA5_CTL_FIFO5 0x05, 0xa5
+#define TM6010_REQ05_RA6_CTL_FIFO6 0x05, 0xa6
+#define TM6010_REQ05_RA7_CTL_FIFO7 0x05, 0xa7
+#define TM6010_REQ05_RA8_CTL_FIFO8 0x05, 0xa8
+#define TM6010_REQ05_RA9_CTL_FIFO9 0x05, 0xa9
+#define TM6010_REQ05_RA1_CTL_FIFO10 0x05, 0xaa
+#define TM6010_REQ05_RA1_CTL_FIFO11 0x05, 0xab
+#define TM6010_REQ05_RA1_CTL_FIFO12 0x05, 0xac
+#define TM6010_REQ05_RA1_CTL_FIFO13 0x05, 0xad
+#define TM6010_REQ05_RA1_CTL_FIFO14 0x05, 0xae
+#define TM6010_REQ05_RA1_CTL_FIFO15 0x05, 0xaf
+#define TM6010_REQ05_RB0_BC_LOW_FIFO0 0x05, 0xb0
+#define TM6010_REQ05_RB1_BC_LOW_FIFO1 0x05, 0xb1
+#define TM6010_REQ05_RB2_BC_LOW_FIFO2 0x05, 0xb2
+#define TM6010_REQ05_RB3_BC_LOW_FIFO3 0x05, 0xb3
+#define TM6010_REQ05_RB4_BC_LOW_FIFO4 0x05, 0xb4
+#define TM6010_REQ05_RB5_BC_LOW_FIFO5 0x05, 0xb5
+#define TM6010_REQ05_RB6_BC_LOW_FIFO6 0x05, 0xb6
+#define TM6010_REQ05_RB7_BC_LOW_FIFO7 0x05, 0xb7
+#define TM6010_REQ05_RB8_BC_LOW_FIFO8 0x05, 0xb8
+#define TM6010_REQ05_RB9_BC_LOW_FIFO9 0x05, 0xb9
+#define TM6010_REQ05_RB1_BC_LOW_FIFO10 0x05, 0xba
+#define TM6010_REQ05_RB1_BC_LOW_FIFO11 0x05, 0xbb
+#define TM6010_REQ05_RB1_BC_LOW_FIFO12 0x05, 0xbc
+#define TM6010_REQ05_RB1_BC_LOW_FIFO13 0x05, 0xbd
+#define TM6010_REQ05_RB1_BC_LOW_FIFO14 0x05, 0xbe
+#define TM6010_REQ05_RB1_BC_LOW_FIFO15 0x05, 0xbf
+#define TM6010_REQ05_RC0_DATA_FIFO0 0x05, 0xc0
+#define TM6010_REQ05_RC4_DATA_FIFO1 0x05, 0xc4
+#define TM6010_REQ05_RC8_DATA_FIFO2 0x05, 0xc8
+#define TM6010_REQ05_RCC_DATA_FIFO3 0x05, 0xcc
+#define TM6010_REQ05_RD0_DATA_FIFO4 0x05, 0xd0
+#define TM6010_REQ05_RD4_DATA_FIFO5 0x05, 0xd4
+#define TM6010_REQ05_RD8_DATA_FIFO6 0x05, 0xd8
+#define TM6010_REQ05_RDC_DATA_FIFO7 0x05, 0xdc
+#define TM6010_REQ05_RE0_DATA_FIFO8 0x05, 0xe0
+#define TM6010_REQ05_RE4_DATA_FIFO9 0x05, 0xe4
+#define TM6010_REQ05_RC4_DATA_FIFO10 0x05, 0xe8
+#define TM6010_REQ05_RC4_DATA_FIFO11 0x05, 0xec
+#define TM6010_REQ05_RC4_DATA_FIFO12 0x05, 0xf0
+#define TM6010_REQ05_RC4_DATA_FIFO13 0x05, 0xf4
+#define TM6010_REQ05_RC4_DATA_FIFO14 0x05, 0xf8
+#define TM6010_REQ05_RC4_DATA_FIFO15 0x05, 0xfc
+
+/* Define TM6010 Audio decoder registers */
+/* This core available only in TM6010 */
+#define TM6010_REQ08_R00_A_VERSION 0x08, 0x00
+#define TM6010_REQ08_R01_A_INIT 0x08, 0x01
+#define TM6010_REQ08_R02_A_FIX_GAIN_CTRL 0x08, 0x02
+#define TM6010_REQ08_R03_A_AUTO_GAIN_CTRL 0x08, 0x03
+#define TM6010_REQ08_R04_A_SIF_AMP_CTRL 0x08, 0x04
+#define TM6010_REQ08_R05_A_STANDARD_MOD 0x08, 0x05
+#define TM6010_REQ08_R06_A_SOUND_MOD 0x08, 0x06
+#define TM6010_REQ08_R07_A_LEFT_VOL 0x08, 0x07
+#define TM6010_REQ08_R08_A_RIGHT_VOL 0x08, 0x08
+#define TM6010_REQ08_R09_A_MAIN_VOL 0x08, 0x09
+#define TM6010_REQ08_R0A_A_I2S_MOD 0x08, 0x0a
+#define TM6010_REQ08_R0B_A_ASD_THRES1 0x08, 0x0b
+#define TM6010_REQ08_R0C_A_ASD_THRES2 0x08, 0x0c
+#define TM6010_REQ08_R0D_A_AMD_THRES 0x08, 0x0d
+#define TM6010_REQ08_R0E_A_MONO_THRES1 0x08, 0x0e
+#define TM6010_REQ08_R0F_A_MONO_THRES2 0x08, 0x0f
+#define TM6010_REQ08_R10_A_MUTE_THRES1 0x08, 0x10
+#define TM6010_REQ08_R11_A_MUTE_THRES2 0x08, 0x11
+#define TM6010_REQ08_R12_A_AGC_U 0x08, 0x12
+#define TM6010_REQ08_R13_A_AGC_ERR_T 0x08, 0x13
+#define TM6010_REQ08_R14_A_AGC_GAIN_INIT 0x08, 0x14
+#define TM6010_REQ08_R15_A_AGC_STEP_THR 0x08, 0x15
+#define TM6010_REQ08_R16_A_AGC_GAIN_MAX 0x08, 0x16
+#define TM6010_REQ08_R17_A_AGC_GAIN_MIN 0x08, 0x17
+#define TM6010_REQ08_R18_A_TR_CTRL 0x08, 0x18
+#define TM6010_REQ08_R19_A_FH_2FH_GAIN 0x08, 0x19
+#define TM6010_REQ08_R1A_A_NICAM_SER_MAX 0x08, 0x1a
+#define TM6010_REQ08_R1B_A_NICAM_SER_MIN 0x08, 0x1b
+#define TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT 0x08, 0x1e
+#define TM6010_REQ08_R1F_A_TEST_INTF_SEL 0x08, 0x1f
+#define TM6010_REQ08_R20_A_TEST_PIN_SEL 0x08, 0x20
+#define TM6010_REQ08_R21_A_AGC_ERR 0x08, 0x21
+#define TM6010_REQ08_R22_A_AGC_GAIN 0x08, 0x22
+#define TM6010_REQ08_R23_A_NICAM_INFO 0x08, 0x23
+#define TM6010_REQ08_R24_A_SER 0x08, 0x24
+#define TM6010_REQ08_R25_A_C1_AMP 0x08, 0x25
+#define TM6010_REQ08_R26_A_C2_AMP 0x08, 0x26
+#define TM6010_REQ08_R27_A_NOISE_AMP 0x08, 0x27
+#define TM6010_REQ08_R28_A_AUDIO_MODE_RES 0x08, 0x28
+
+/* Define TM6010 Video ADC registers */
+#define TM6010_REQ08_RE0_ADC_REF 0x08, 0xe0
+#define TM6010_REQ08_RE1_DAC_CLMP 0x08, 0xe1
+#define TM6010_REQ08_RE2_POWER_DOWN_CTRL1 0x08, 0xe2
+#define TM6010_REQ08_RE3_ADC_IN1_SEL 0x08, 0xe3
+#define TM6010_REQ08_RE4_ADC_IN2_SEL 0x08, 0xe4
+#define TM6010_REQ08_RE5_GAIN_PARAM 0x08, 0xe5
+#define TM6010_REQ08_RE6_POWER_DOWN_CTRL2 0x08, 0xe6
+#define TM6010_REQ08_RE7_REG_GAIN_Y 0x08, 0xe7
+#define TM6010_REQ08_RE8_REG_GAIN_C 0x08, 0xe8
+#define TM6010_REQ08_RE9_BIAS_CTRL 0x08, 0xe9
+#define TM6010_REQ08_REA_BUFF_DRV_CTRL 0x08, 0xea
+#define TM6010_REQ08_REB_SIF_GAIN_CTRL 0x08, 0xeb
+#define TM6010_REQ08_REC_REVERSE_YC_CTRL 0x08, 0xec
+#define TM6010_REQ08_RED_GAIN_SEL 0x08, 0xed
+
+/* Define TM6010 Audio ADC registers */
+#define TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG 0x08, 0xf0
+#define TM6010_REQ08_RF1_AADC_POWER_DOWN 0x08, 0xf1
+#define TM6010_REQ08_RF2_LEFT_CHANNEL_VOL 0x08, 0xf2
+#define TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL 0x08, 0xf3
diff --git a/drivers/media/video/tm6000/tm6000-stds.c b/drivers/media/video/tm6000/tm6000-stds.c
new file mode 100644
index 00000000..9dc0831d
--- /dev/null
+++ b/drivers/media/video/tm6000/tm6000-stds.c
@@ -0,0 +1,640 @@
+/*
+ * tm6000-stds.c - driver for TM5600/TM6000/TM6010 USB video capture devices
+ *
+ * Copyright (C) 2007 Mauro Carvalho Chehab <mchehab@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include "tm6000.h"
+#include "tm6000-regs.h"
+
+static unsigned int tm6010_a_mode;
+module_param(tm6010_a_mode, int, 0644);
+MODULE_PARM_DESC(tm6010_a_mode, "set tm6010 sif audio mode");
+
+struct tm6000_reg_settings {
+ unsigned char req;
+ unsigned char reg;
+ unsigned char value;
+};
+
+
+struct tm6000_std_settings {
+ v4l2_std_id id;
+ struct tm6000_reg_settings *common;
+};
+
+static struct tm6000_reg_settings composite_pal_m[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x04 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x83 },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x0a },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe0 },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x20 },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f },
+ { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_reg_settings composite_pal_nc[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x36 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x91 },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x1f },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x0c },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f },
+ { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_reg_settings composite_pal[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x32 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x25 },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0xd5 },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x63 },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x50 },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f },
+ { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_reg_settings composite_secam[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x38 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24 },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92 },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8 },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xed },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x2c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x18 },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0xff },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_reg_settings composite_ntsc[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0f },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f },
+ { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdd },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_std_settings composite_stds[] = {
+ { .id = V4L2_STD_PAL_M, .common = composite_pal_m, },
+ { .id = V4L2_STD_PAL_Nc, .common = composite_pal_nc, },
+ { .id = V4L2_STD_PAL, .common = composite_pal, },
+ { .id = V4L2_STD_SECAM, .common = composite_secam, },
+ { .id = V4L2_STD_NTSC, .common = composite_ntsc, },
+};
+
+static struct tm6000_reg_settings svideo_pal_m[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x05 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x83 },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x0a },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe0 },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f },
+ { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_reg_settings svideo_pal_nc[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x37 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x91 },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x1f },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x0c },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f },
+ { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_reg_settings svideo_pal[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x33 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x30 },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x25 },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0xd5 },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x63 },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x50 },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2a },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f },
+ { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_reg_settings svideo_secam[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x39 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x03 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24 },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92 },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8 },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xed },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2a },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x2c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x18 },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0xff },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_reg_settings svideo_ntsc[] = {
+ { TM6010_REQ07_R3F_RESET, 0x01 },
+ { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x01 },
+ { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0f },
+ { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f },
+ { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x03 },
+ { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x30 },
+ { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0x8b },
+ { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e },
+ { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b },
+ { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 },
+ { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 },
+ { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c },
+ { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc },
+ { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc },
+ { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd },
+ { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 },
+ { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 },
+ { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 },
+ { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c },
+ { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c },
+ { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 },
+ { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f },
+ { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdd },
+ { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 },
+ { TM6010_REQ07_R3F_RESET, 0x00 },
+ { 0, 0, 0 }
+};
+
+static struct tm6000_std_settings svideo_stds[] = {
+ { .id = V4L2_STD_PAL_M, .common = svideo_pal_m, },
+ { .id = V4L2_STD_PAL_Nc, .common = svideo_pal_nc, },
+ { .id = V4L2_STD_PAL, .common = svideo_pal, },
+ { .id = V4L2_STD_SECAM, .common = svideo_secam, },
+ { .id = V4L2_STD_NTSC, .common = svideo_ntsc, },
+};
+
+static int tm6000_set_audio_std(struct tm6000_core *dev)
+{
+ uint8_t areg_02 = 0x04; /* GC1 Fixed gain 0dB */
+ uint8_t areg_05 = 0x01; /* Auto 4.5 = M Japan, Auto 6.5 = DK */
+ uint8_t areg_06 = 0x02; /* Auto de-emphasis, mannual channel mode */
+ uint8_t nicam_flag = 0; /* No NICAM */
+
+ if (dev->radio) {
+ tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04);
+ tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0x80);
+ tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, 0x0c);
+ /* set mono or stereo */
+ if (dev->amode == V4L2_TUNER_MODE_MONO)
+ tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x00);
+ else if (dev->amode == V4L2_TUNER_MODE_STEREO)
+ tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x02);
+ tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x18);
+ tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2, 0x0a);
+ tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES, 0x40);
+ tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfe);
+ tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13);
+ tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80);
+ tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, 0xff);
+ return 0;
+ }
+
+ /*
+ * STD/MN shouldn't be affected by tm6010_a_mode, as there's just one
+ * audio standard for each V4L2_STD type.
+ */
+ if ((dev->norm & V4L2_STD_NTSC) == V4L2_STD_NTSC_M_KR) {
+ areg_05 |= 0x04;
+ } else if ((dev->norm & V4L2_STD_NTSC) == V4L2_STD_NTSC_M_JP) {
+ areg_05 |= 0x43;
+ } else if (dev->norm & V4L2_STD_MN) {
+ areg_05 |= 0x22;
+ } else switch (tm6010_a_mode) {
+ /* auto */
+ case 0:
+ if ((dev->norm & V4L2_STD_SECAM) == V4L2_STD_SECAM_L)
+ areg_05 |= 0x00;
+ else /* Other PAL/SECAM standards */
+ areg_05 |= 0x10;
+ break;
+ /* A2 */
+ case 1:
+ if (dev->norm & V4L2_STD_DK)
+ areg_05 = 0x09;
+ else
+ areg_05 = 0x05;
+ break;
+ /* NICAM */
+ case 2:
+ if (dev->norm & V4L2_STD_DK) {
+ areg_05 = 0x06;
+ } else if (dev->norm & V4L2_STD_PAL_I) {
+ areg_05 = 0x08;
+ } else if (dev->norm & V4L2_STD_SECAM_L) {
+ areg_05 = 0x0a;
+ areg_02 = 0x02;
+ } else {
+ areg_05 = 0x07;
+ }
+ nicam_flag = 1;
+ break;
+ /* other */
+ case 3:
+ if (dev->norm & V4L2_STD_DK) {
+ areg_05 = 0x0b;
+ } else {
+ areg_05 = 0x02;
+ }
+ break;
+ }
+
+ tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, areg_02);
+ tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0xa0);
+ tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, areg_05);
+ tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, areg_06);
+ tm6000_set_reg(dev, TM6010_REQ08_R07_A_LEFT_VOL, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x08);
+ tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, 0x91);
+ tm6000_set_reg(dev, TM6010_REQ08_R0B_A_ASD_THRES1, 0x20);
+ tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2, 0x12);
+ tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES, 0x20);
+ tm6000_set_reg(dev, TM6010_REQ08_R0E_A_MONO_THRES1, 0xf0);
+ tm6000_set_reg(dev, TM6010_REQ08_R0F_A_MONO_THRES2, 0x80);
+ tm6000_set_reg(dev, TM6010_REQ08_R10_A_MUTE_THRES1, 0xc0);
+ tm6000_set_reg(dev, TM6010_REQ08_R11_A_MUTE_THRES2, 0x80);
+ tm6000_set_reg(dev, TM6010_REQ08_R12_A_AGC_U, 0x12);
+ tm6000_set_reg(dev, TM6010_REQ08_R13_A_AGC_ERR_T, 0xfe);
+ tm6000_set_reg(dev, TM6010_REQ08_R14_A_AGC_GAIN_INIT, 0x20);
+ tm6000_set_reg(dev, TM6010_REQ08_R15_A_AGC_STEP_THR, 0x14);
+ tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX, 0xfe);
+ tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN, 0x01);
+ tm6000_set_reg(dev, TM6010_REQ08_R18_A_TR_CTRL, 0xa0);
+ tm6000_set_reg(dev, TM6010_REQ08_R19_A_FH_2FH_GAIN, 0x32);
+ tm6000_set_reg(dev, TM6010_REQ08_R1A_A_NICAM_SER_MAX, 0x64);
+ tm6000_set_reg(dev, TM6010_REQ08_R1B_A_NICAM_SER_MIN, 0x20);
+ tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1c, 0x00);
+ tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1d, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13);
+ tm6000_set_reg(dev, TM6010_REQ08_R1F_A_TEST_INTF_SEL, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R20_A_TEST_PIN_SEL, 0x00);
+ tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80);
+
+ return 0;
+}
+
+void tm6000_get_std_res(struct tm6000_core *dev)
+{
+ /* Currently, those are the only supported resoltions */
+ if (dev->norm & V4L2_STD_525_60)
+ dev->height = 480;
+ else
+ dev->height = 576;
+
+ dev->width = 720;
+}
+
+static int tm6000_load_std(struct tm6000_core *dev, struct tm6000_reg_settings *set)
+{
+ int i, rc;
+
+ /* Load board's initialization table */
+ for (i = 0; set[i].req; i++) {
+ rc = tm6000_set_reg(dev, set[i].req, set[i].reg, set[i].value);
+ if (rc < 0) {
+ printk(KERN_ERR "Error %i while setting "
+ "req %d, reg %d to value %d\n",
+ rc, set[i].req, set[i].reg, set[i].value);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+int tm6000_set_standard(struct tm6000_core *dev)
+{
+ struct tm6000_input *input;
+ int i, rc = 0;
+ u8 reg_07_fe = 0x8a;
+ u8 reg_08_f1 = 0xfc;
+ u8 reg_08_e2 = 0xf0;
+ u8 reg_08_e6 = 0x0f;
+
+ tm6000_get_std_res(dev);
+
+ if (!dev->radio)
+ input = &dev->vinput[dev->input];
+ else
+ input = &dev->rinput;
+
+ if (dev->dev_type == TM6010) {
+ switch (input->vmux) {
+ case TM6000_VMUX_VIDEO_A:
+ tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4);
+ tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1);
+ tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0);
+ tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2);
+ tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe8);
+ reg_07_fe |= 0x01;
+ break;
+ case TM6000_VMUX_VIDEO_B:
+ tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8);
+ tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1);
+ tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0);
+ tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2);
+ tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe8);
+ reg_07_fe |= 0x01;
+ break;
+ case TM6000_VMUX_VIDEO_AB:
+ tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xfc);
+ tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf8);
+ reg_08_e6 = 0x00;
+ tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf2);
+ tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0);
+ tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2);
+ tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe0);
+ break;
+ default:
+ break;
+ }
+ switch (input->amux) {
+ case TM6000_AMUX_ADC1:
+ tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
+ 0x00, 0x0f);
+ /* Mux overflow workaround */
+ tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL,
+ 0x10, 0xf0);
+ break;
+ case TM6000_AMUX_ADC2:
+ tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
+ 0x08, 0x0f);
+ /* Mux overflow workaround */
+ tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL,
+ 0x10, 0xf0);
+ break;
+ case TM6000_AMUX_SIF1:
+ reg_08_e2 |= 0x02;
+ reg_08_e6 = 0x08;
+ reg_07_fe |= 0x40;
+ reg_08_f1 |= 0x02;
+ tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3);
+ tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
+ 0x02, 0x0f);
+ /* Mux overflow workaround */
+ tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL,
+ 0x30, 0xf0);
+ break;
+ case TM6000_AMUX_SIF2:
+ reg_08_e2 |= 0x02;
+ reg_08_e6 = 0x08;
+ reg_07_fe |= 0x40;
+ reg_08_f1 |= 0x02;
+ tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf7);
+ tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
+ 0x02, 0x0f);
+ /* Mux overflow workaround */
+ tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL,
+ 0x30, 0xf0);
+ break;
+ default:
+ break;
+ }
+ tm6000_set_reg(dev, TM6010_REQ08_RE2_POWER_DOWN_CTRL1, reg_08_e2);
+ tm6000_set_reg(dev, TM6010_REQ08_RE6_POWER_DOWN_CTRL2, reg_08_e6);
+ tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, reg_08_f1);
+ tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, reg_07_fe);
+ } else {
+ switch (input->vmux) {
+ case TM6000_VMUX_VIDEO_A:
+ tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10);
+ tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00);
+ tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x0f);
+ tm6000_set_reg(dev,
+ REQ_03_SET_GET_MCU_PIN, input->v_gpio, 0);
+ break;
+ case TM6000_VMUX_VIDEO_B:
+ tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x00);
+ tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00);
+ tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x0f);
+ tm6000_set_reg(dev,
+ REQ_03_SET_GET_MCU_PIN, input->v_gpio, 0);
+ break;
+ case TM6000_VMUX_VIDEO_AB:
+ tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10);
+ tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x10);
+ tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x00);
+ tm6000_set_reg(dev,
+ REQ_03_SET_GET_MCU_PIN, input->v_gpio, 1);
+ break;
+ default:
+ break;
+ }
+ switch (input->amux) {
+ case TM6000_AMUX_ADC1:
+ tm6000_set_reg_mask(dev,
+ TM6000_REQ07_REB_VADC_AADC_MODE, 0x00, 0x0f);
+ break;
+ case TM6000_AMUX_ADC2:
+ tm6000_set_reg_mask(dev,
+ TM6000_REQ07_REB_VADC_AADC_MODE, 0x04, 0x0f);
+ break;
+ default:
+ break;
+ }
+ }
+ if (input->type == TM6000_INPUT_SVIDEO) {
+ for (i = 0; i < ARRAY_SIZE(svideo_stds); i++) {
+ if (dev->norm & svideo_stds[i].id) {
+ rc = tm6000_load_std(dev, svideo_stds[i].common);
+ goto ret;
+ }
+ }
+ return -EINVAL;
+ } else {
+ for (i = 0; i < ARRAY_SIZE(composite_stds); i++) {
+ if (dev->norm & composite_stds[i].id) {
+ rc = tm6000_load_std(dev, composite_stds[i].common);
+ goto ret;
+ }
+ }
+ return -EINVAL;
+ }
+
+ret:
+ if (rc < 0)
+ return rc;
+
+ if ((dev->dev_type == TM6010) &&
+ ((input->amux == TM6000_AMUX_SIF1) ||
+ (input->amux == TM6000_AMUX_SIF2)))
+ tm6000_set_audio_std(dev);
+
+ msleep(40);
+
+ return 0;
+}
diff --git a/drivers/media/video/tm6000/tm6000-usb-isoc.h b/drivers/media/video/tm6000/tm6000-usb-isoc.h
new file mode 100644
index 00000000..99d15a55
--- /dev/null
+++ b/drivers/media/video/tm6000/tm6000-usb-isoc.h
@@ -0,0 +1,50 @@
+/*
+ * tm6000-buf.c - driver for TM5600/TM6000/TM6010 USB video capture devices
+ *
+ * Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/videodev2.h>
+
+#define TM6000_URB_MSG_LEN 180
+
+struct usb_isoc_ctl {
+ /* max packet size of isoc transaction */
+ int max_pkt_size;
+
+ /* number of allocated urbs */
+ int num_bufs;
+
+ /* urb for isoc transfers */
+ struct urb **urb;
+
+ /* transfer buffers for isoc transfer */
+ char **transfer_buffer;
+
+ /* Last buffer command and region */
+ u8 cmd;
+ int pos, size, pktsize;
+
+ /* Last field: ODD or EVEN? */
+ int vfield, field;
+
+ /* Stores incomplete commands */
+ u32 tmp_buf;
+ int tmp_buf_len;
+
+ /* Stores already requested buffers */
+ struct tm6000_buffer *buf;
+};
diff --git a/drivers/media/video/tm6000/tm6000-video.c b/drivers/media/video/tm6000/tm6000-video.c
new file mode 100644
index 00000000..bc13db73
--- /dev/null
+++ b/drivers/media/video/tm6000/tm6000-video.c
@@ -0,0 +1,1822 @@
+/*
+ * tm6000-video.c - driver for TM5600/TM6000/TM6010 USB video capture devices
+ *
+ * Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
+ *
+ * Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com>
+ * - Fixed module load/unload
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/random.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ioctl.h>
+#include <media/tuner.h>
+#include <linux/interrupt.h>
+#include <linux/kthread.h>
+#include <linux/highmem.h>
+#include <linux/freezer.h>
+
+#include "tm6000-regs.h"
+#include "tm6000.h"
+
+#define BUFFER_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */
+
+/* Limits minimum and default number of buffers */
+#define TM6000_MIN_BUF 4
+#define TM6000_DEF_BUF 8
+
+#define TM6000_MAX_ISO_PACKETS 46 /* Max number of ISO packets */
+
+/* Declare static vars that will be used as parameters */
+static unsigned int vid_limit = 16; /* Video memory limit, in Mb */
+static int video_nr = -1; /* /dev/videoN, -1 for autodetect */
+static int radio_nr = -1; /* /dev/radioN, -1 for autodetect */
+
+/* Debug level */
+int tm6000_debug;
+EXPORT_SYMBOL_GPL(tm6000_debug);
+
+static const struct v4l2_queryctrl no_ctrl = {
+ .name = "42",
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+};
+
+/* supported controls */
+static struct v4l2_queryctrl tm6000_qctrl[] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 54,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 0x1,
+ .default_value = 119,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 0x1,
+ .default_value = 112,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_HUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Hue",
+ .minimum = -128,
+ .maximum = 127,
+ .step = 0x1,
+ .default_value = 0,
+ .flags = 0,
+ },
+ /* --- audio --- */
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ }, {
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .name = "Volume",
+ .minimum = -15,
+ .maximum = 15,
+ .step = 1,
+ .default_value = 0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ }
+};
+
+static const unsigned int CTRLS = ARRAY_SIZE(tm6000_qctrl);
+static int qctl_regs[ARRAY_SIZE(tm6000_qctrl)];
+
+static struct tm6000_fmt format[] = {
+ {
+ .name = "4:2:2, packed, YVY2",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ }, {
+ .name = "4:2:2, packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = 16,
+ }, {
+ .name = "A/V + VBI mux packet",
+ .fourcc = V4L2_PIX_FMT_TM6000,
+ .depth = 16,
+ }
+};
+
+static const struct v4l2_queryctrl *ctrl_by_id(unsigned int id)
+{
+ unsigned int i;
+
+ for (i = 0; i < CTRLS; i++)
+ if (tm6000_qctrl[i].id == id)
+ return tm6000_qctrl+i;
+ return NULL;
+}
+
+/* ------------------------------------------------------------------
+ * DMA and thread functions
+ * ------------------------------------------------------------------
+ */
+
+#define norm_maxw(a) 720
+#define norm_maxh(a) 576
+
+#define norm_minw(a) norm_maxw(a)
+#define norm_minh(a) norm_maxh(a)
+
+/*
+ * video-buf generic routine to get the next available buffer
+ */
+static inline void get_next_buf(struct tm6000_dmaqueue *dma_q,
+ struct tm6000_buffer **buf)
+{
+ struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);
+ char *outp;
+
+ if (list_empty(&dma_q->active)) {
+ dprintk(dev, V4L2_DEBUG_QUEUE, "No active queue to serve\n");
+ *buf = NULL;
+ return;
+ }
+
+ *buf = list_entry(dma_q->active.next,
+ struct tm6000_buffer, vb.queue);
+
+ /* Cleans up buffer - Useful for testing for frame/URB loss */
+ outp = videobuf_to_vmalloc(&(*buf)->vb);
+
+ return;
+}
+
+/*
+ * Announces that a buffer were filled and request the next
+ */
+static inline void buffer_filled(struct tm6000_core *dev,
+ struct tm6000_dmaqueue *dma_q,
+ struct tm6000_buffer *buf)
+{
+ /* Advice that buffer was filled */
+ dprintk(dev, V4L2_DEBUG_ISOC, "[%p/%d] wakeup\n", buf, buf->vb.i);
+ buf->vb.state = VIDEOBUF_DONE;
+ buf->vb.field_count++;
+ do_gettimeofday(&buf->vb.ts);
+
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+}
+
+/*
+ * Identify the tm5600/6000 buffer header type and properly handles
+ */
+static int copy_streams(u8 *data, unsigned long len,
+ struct urb *urb)
+{
+ struct tm6000_dmaqueue *dma_q = urb->context;
+ struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);
+ u8 *ptr = data, *endp = data+len, c;
+ unsigned long header = 0;
+ int rc = 0;
+ unsigned int cmd, cpysize, pktsize, size, field, block, line, pos = 0;
+ struct tm6000_buffer *vbuf = NULL;
+ char *voutp = NULL;
+ unsigned int linewidth;
+
+ if (!dev->radio) {
+ /* get video buffer */
+ get_next_buf(dma_q, &vbuf);
+
+ if (!vbuf)
+ return rc;
+ voutp = videobuf_to_vmalloc(&vbuf->vb);
+
+ if (!voutp)
+ return 0;
+ }
+
+ for (ptr = data; ptr < endp;) {
+ if (!dev->isoc_ctl.cmd) {
+ /* Header */
+ if (dev->isoc_ctl.tmp_buf_len > 0) {
+ /* from last urb or packet */
+ header = dev->isoc_ctl.tmp_buf;
+ if (4 - dev->isoc_ctl.tmp_buf_len > 0) {
+ memcpy((u8 *)&header +
+ dev->isoc_ctl.tmp_buf_len,
+ ptr,
+ 4 - dev->isoc_ctl.tmp_buf_len);
+ ptr += 4 - dev->isoc_ctl.tmp_buf_len;
+ }
+ dev->isoc_ctl.tmp_buf_len = 0;
+ } else {
+ if (ptr + 3 >= endp) {
+ /* have incomplete header */
+ dev->isoc_ctl.tmp_buf_len = endp - ptr;
+ memcpy(&dev->isoc_ctl.tmp_buf, ptr,
+ dev->isoc_ctl.tmp_buf_len);
+ return rc;
+ }
+ /* Seek for sync */
+ for (; ptr < endp - 3; ptr++) {
+ if (*(ptr + 3) == 0x47)
+ break;
+ }
+ /* Get message header */
+ header = *(unsigned long *)ptr;
+ ptr += 4;
+ }
+
+ /* split the header fields */
+ c = (header >> 24) & 0xff;
+ size = ((header & 0x7e) << 1);
+ if (size > 0)
+ size -= 4;
+ block = (header >> 7) & 0xf;
+ field = (header >> 11) & 0x1;
+ line = (header >> 12) & 0x1ff;
+ cmd = (header >> 21) & 0x7;
+ /* Validates haeder fields */
+ if (size > TM6000_URB_MSG_LEN)
+ size = TM6000_URB_MSG_LEN;
+ pktsize = TM6000_URB_MSG_LEN;
+ /*
+ * calculate position in buffer and change the buffer
+ */
+ switch (cmd) {
+ case TM6000_URB_MSG_VIDEO:
+ if (!dev->radio) {
+ if ((dev->isoc_ctl.vfield != field) &&
+ (field == 1)) {
+ /*
+ * Announces that a new buffer
+ * were filled
+ */
+ buffer_filled(dev, dma_q, vbuf);
+ dprintk(dev, V4L2_DEBUG_ISOC,
+ "new buffer filled\n");
+ get_next_buf(dma_q, &vbuf);
+ if (!vbuf)
+ return rc;
+ voutp = videobuf_to_vmalloc(&vbuf->vb);
+ if (!voutp)
+ return rc;
+ memset(voutp, 0, vbuf->vb.size);
+ }
+ linewidth = vbuf->vb.width << 1;
+ pos = ((line << 1) - field - 1) *
+ linewidth + block * TM6000_URB_MSG_LEN;
+ /* Don't allow to write out of the buffer */
+ if (pos + size > vbuf->vb.size)
+ cmd = TM6000_URB_MSG_ERR;
+ dev->isoc_ctl.vfield = field;
+ }
+ break;
+ case TM6000_URB_MSG_VBI:
+ break;
+ case TM6000_URB_MSG_AUDIO:
+ case TM6000_URB_MSG_PTS:
+ size = pktsize; /* Size is always 180 bytes */
+ break;
+ }
+ } else {
+ /* Continue the last copy */
+ cmd = dev->isoc_ctl.cmd;
+ size = dev->isoc_ctl.size;
+ pos = dev->isoc_ctl.pos;
+ pktsize = dev->isoc_ctl.pktsize;
+ field = dev->isoc_ctl.field;
+ }
+ cpysize = (endp - ptr > size) ? size : endp - ptr;
+ if (cpysize) {
+ /* copy data in different buffers */
+ switch (cmd) {
+ case TM6000_URB_MSG_VIDEO:
+ /* Fills video buffer */
+ if (vbuf)
+ memcpy(&voutp[pos], ptr, cpysize);
+ break;
+ case TM6000_URB_MSG_AUDIO: {
+ int i;
+ for (i = 0; i < cpysize; i += 2)
+ swab16s((u16 *)(ptr + i));
+
+ tm6000_call_fillbuf(dev, TM6000_AUDIO, ptr, cpysize);
+ break;
+ }
+ case TM6000_URB_MSG_VBI:
+ /* Need some code to copy vbi buffer */
+ break;
+ case TM6000_URB_MSG_PTS: {
+ /* Need some code to copy pts */
+ u32 pts;
+ pts = *(u32 *)ptr;
+ dprintk(dev, V4L2_DEBUG_ISOC, "field %d, PTS %x",
+ field, pts);
+ break;
+ }
+ }
+ }
+ if (ptr + pktsize > endp) {
+ /*
+ * End of URB packet, but cmd processing is not
+ * complete. Preserve the state for a next packet
+ */
+ dev->isoc_ctl.pos = pos + cpysize;
+ dev->isoc_ctl.size = size - cpysize;
+ dev->isoc_ctl.cmd = cmd;
+ dev->isoc_ctl.field = field;
+ dev->isoc_ctl.pktsize = pktsize - (endp - ptr);
+ ptr += endp - ptr;
+ } else {
+ dev->isoc_ctl.cmd = 0;
+ ptr += pktsize;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Identify the tm5600/6000 buffer header type and properly handles
+ */
+static int copy_multiplexed(u8 *ptr, unsigned long len,
+ struct urb *urb)
+{
+ struct tm6000_dmaqueue *dma_q = urb->context;
+ struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);
+ unsigned int pos = dev->isoc_ctl.pos, cpysize;
+ int rc = 1;
+ struct tm6000_buffer *buf;
+ char *outp = NULL;
+
+ get_next_buf(dma_q, &buf);
+ if (buf)
+ outp = videobuf_to_vmalloc(&buf->vb);
+
+ if (!outp)
+ return 0;
+
+ while (len > 0) {
+ cpysize = min(len, buf->vb.size-pos);
+ memcpy(&outp[pos], ptr, cpysize);
+ pos += cpysize;
+ ptr += cpysize;
+ len -= cpysize;
+ if (pos >= buf->vb.size) {
+ pos = 0;
+ /* Announces that a new buffer were filled */
+ buffer_filled(dev, dma_q, buf);
+ dprintk(dev, V4L2_DEBUG_ISOC, "new buffer filled\n");
+ get_next_buf(dma_q, &buf);
+ if (!buf)
+ break;
+ outp = videobuf_to_vmalloc(&(buf->vb));
+ if (!outp)
+ return rc;
+ pos = 0;
+ }
+ }
+
+ dev->isoc_ctl.pos = pos;
+ return rc;
+}
+
+static inline void print_err_status(struct tm6000_core *dev,
+ int packet, int status)
+{
+ char *errmsg = "Unknown";
+
+ switch (status) {
+ case -ENOENT:
+ errmsg = "unlinked synchronuously";
+ break;
+ case -ECONNRESET:
+ errmsg = "unlinked asynchronuously";
+ break;
+ case -ENOSR:
+ errmsg = "Buffer error (overrun)";
+ break;
+ case -EPIPE:
+ errmsg = "Stalled (device not responding)";
+ break;
+ case -EOVERFLOW:
+ errmsg = "Babble (bad cable?)";
+ break;
+ case -EPROTO:
+ errmsg = "Bit-stuff error (bad cable?)";
+ break;
+ case -EILSEQ:
+ errmsg = "CRC/Timeout (could be anything)";
+ break;
+ case -ETIME:
+ errmsg = "Device does not respond";
+ break;
+ }
+ if (packet < 0) {
+ dprintk(dev, V4L2_DEBUG_QUEUE, "URB status %d [%s].\n",
+ status, errmsg);
+ } else {
+ dprintk(dev, V4L2_DEBUG_QUEUE, "URB packet %d, status %d [%s].\n",
+ packet, status, errmsg);
+ }
+}
+
+
+/*
+ * Controls the isoc copy of each urb packet
+ */
+static inline int tm6000_isoc_copy(struct urb *urb)
+{
+ struct tm6000_dmaqueue *dma_q = urb->context;
+ struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);
+ int i, len = 0, rc = 1, status;
+ char *p;
+
+ if (urb->status < 0) {
+ print_err_status(dev, -1, urb->status);
+ return 0;
+ }
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ status = urb->iso_frame_desc[i].status;
+
+ if (status < 0) {
+ print_err_status(dev, i, status);
+ continue;
+ }
+
+ len = urb->iso_frame_desc[i].actual_length;
+
+ if (len > 0) {
+ p = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+ if (!urb->iso_frame_desc[i].status) {
+ if ((dev->fourcc) == V4L2_PIX_FMT_TM6000) {
+ rc = copy_multiplexed(p, len, urb);
+ if (rc <= 0)
+ return rc;
+ } else {
+ copy_streams(p, len, urb);
+ }
+ }
+ }
+ }
+ return rc;
+}
+
+/* ------------------------------------------------------------------
+ * URB control
+ * ------------------------------------------------------------------
+ */
+
+/*
+ * IRQ callback, called by URB callback
+ */
+static void tm6000_irq_callback(struct urb *urb)
+{
+ struct tm6000_dmaqueue *dma_q = urb->context;
+ struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);
+ int i;
+
+ switch (urb->status) {
+ case 0:
+ case -ETIMEDOUT:
+ break;
+
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+
+ default:
+ tm6000_err("urb completion error %d.\n", urb->status);
+ break;
+ }
+
+ spin_lock(&dev->slock);
+ tm6000_isoc_copy(urb);
+ spin_unlock(&dev->slock);
+
+ /* Reset urb buffers */
+ for (i = 0; i < urb->number_of_packets; i++) {
+ urb->iso_frame_desc[i].status = 0;
+ urb->iso_frame_desc[i].actual_length = 0;
+ }
+
+ urb->status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (urb->status)
+ tm6000_err("urb resubmit failed (error=%i)\n",
+ urb->status);
+}
+
+/*
+ * Stop and Deallocate URBs
+ */
+static void tm6000_uninit_isoc(struct tm6000_core *dev)
+{
+ struct urb *urb;
+ int i;
+
+ dev->isoc_ctl.buf = NULL;
+ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
+ urb = dev->isoc_ctl.urb[i];
+ if (urb) {
+ usb_kill_urb(urb);
+ usb_unlink_urb(urb);
+ if (dev->isoc_ctl.transfer_buffer[i]) {
+ usb_free_coherent(dev->udev,
+ urb->transfer_buffer_length,
+ dev->isoc_ctl.transfer_buffer[i],
+ urb->transfer_dma);
+ }
+ usb_free_urb(urb);
+ dev->isoc_ctl.urb[i] = NULL;
+ }
+ dev->isoc_ctl.transfer_buffer[i] = NULL;
+ }
+
+ kfree(dev->isoc_ctl.urb);
+ kfree(dev->isoc_ctl.transfer_buffer);
+
+ dev->isoc_ctl.urb = NULL;
+ dev->isoc_ctl.transfer_buffer = NULL;
+ dev->isoc_ctl.num_bufs = 0;
+}
+
+/*
+ * Allocate URBs and start IRQ
+ */
+static int tm6000_prepare_isoc(struct tm6000_core *dev)
+{
+ struct tm6000_dmaqueue *dma_q = &dev->vidq;
+ int i, j, sb_size, pipe, size, max_packets, num_bufs = 8;
+ struct urb *urb;
+
+ /* De-allocates all pending stuff */
+ tm6000_uninit_isoc(dev);
+ /* Stop interrupt USB pipe */
+ tm6000_ir_int_stop(dev);
+
+ usb_set_interface(dev->udev,
+ dev->isoc_in.bInterfaceNumber,
+ dev->isoc_in.bAlternateSetting);
+
+ /* Start interrupt USB pipe */
+ tm6000_ir_int_start(dev);
+
+ pipe = usb_rcvisocpipe(dev->udev,
+ dev->isoc_in.endp->desc.bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK);
+
+ size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe));
+
+ if (size > dev->isoc_in.maxsize)
+ size = dev->isoc_in.maxsize;
+
+ dev->isoc_ctl.max_pkt_size = size;
+
+ max_packets = TM6000_MAX_ISO_PACKETS;
+ sb_size = max_packets * size;
+
+ dev->isoc_ctl.num_bufs = num_bufs;
+
+ dev->isoc_ctl.urb = kmalloc(sizeof(void *)*num_bufs, GFP_KERNEL);
+ if (!dev->isoc_ctl.urb) {
+ tm6000_err("cannot alloc memory for usb buffers\n");
+ return -ENOMEM;
+ }
+
+ dev->isoc_ctl.transfer_buffer = kmalloc(sizeof(void *)*num_bufs,
+ GFP_KERNEL);
+ if (!dev->isoc_ctl.transfer_buffer) {
+ tm6000_err("cannot allocate memory for usbtransfer\n");
+ kfree(dev->isoc_ctl.urb);
+ return -ENOMEM;
+ }
+
+ dprintk(dev, V4L2_DEBUG_QUEUE, "Allocating %d x %d packets"
+ " (%d bytes) of %d bytes each to handle %u size\n",
+ max_packets, num_bufs, sb_size,
+ dev->isoc_in.maxsize, size);
+
+ /* allocate urbs and transfer buffers */
+ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
+ urb = usb_alloc_urb(max_packets, GFP_KERNEL);
+ if (!urb) {
+ tm6000_err("cannot alloc isoc_ctl.urb %i\n", i);
+ tm6000_uninit_isoc(dev);
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+ dev->isoc_ctl.urb[i] = urb;
+
+ dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->udev,
+ sb_size, GFP_KERNEL, &urb->transfer_dma);
+ if (!dev->isoc_ctl.transfer_buffer[i]) {
+ tm6000_err("unable to allocate %i bytes for transfer"
+ " buffer %i%s\n",
+ sb_size, i,
+ in_interrupt() ? " while in int" : "");
+ tm6000_uninit_isoc(dev);
+ return -ENOMEM;
+ }
+ memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size);
+
+ usb_fill_bulk_urb(urb, dev->udev, pipe,
+ dev->isoc_ctl.transfer_buffer[i], sb_size,
+ tm6000_irq_callback, dma_q);
+ urb->interval = dev->isoc_in.endp->desc.bInterval;
+ urb->number_of_packets = max_packets;
+ urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+
+ for (j = 0; j < max_packets; j++) {
+ urb->iso_frame_desc[j].offset = size * j;
+ urb->iso_frame_desc[j].length = size;
+ }
+ }
+
+ return 0;
+}
+
+static int tm6000_start_thread(struct tm6000_core *dev)
+{
+ struct tm6000_dmaqueue *dma_q = &dev->vidq;
+ int i;
+
+ dma_q->frame = 0;
+ dma_q->ini_jiffies = jiffies;
+
+ init_waitqueue_head(&dma_q->wq);
+
+ /* submit urbs and enables IRQ */
+ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
+ int rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC);
+ if (rc) {
+ tm6000_err("submit of urb %i failed (error=%i)\n", i,
+ rc);
+ tm6000_uninit_isoc(dev);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------
+ * Videobuf operations
+ * ------------------------------------------------------------------
+ */
+
+static int
+buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
+{
+ struct tm6000_fh *fh = vq->priv_data;
+
+ *size = fh->fmt->depth * fh->width * fh->height >> 3;
+ if (0 == *count)
+ *count = TM6000_DEF_BUF;
+
+ if (*count < TM6000_MIN_BUF)
+ *count = TM6000_MIN_BUF;
+
+ while (*size * *count > vid_limit * 1024 * 1024)
+ (*count)--;
+
+ return 0;
+}
+
+static void free_buffer(struct videobuf_queue *vq, struct tm6000_buffer *buf)
+{
+ struct tm6000_fh *fh = vq->priv_data;
+ struct tm6000_core *dev = fh->dev;
+ unsigned long flags;
+
+ if (in_interrupt())
+ BUG();
+
+ /* We used to wait for the buffer to finish here, but this didn't work
+ because, as we were keeping the state as VIDEOBUF_QUEUED,
+ videobuf_queue_cancel marked it as finished for us.
+ (Also, it could wedge forever if the hardware was misconfigured.)
+
+ This should be safe; by the time we get here, the buffer isn't
+ queued anymore. If we ever start marking the buffers as
+ VIDEOBUF_ACTIVE, it won't be, though.
+ */
+ spin_lock_irqsave(&dev->slock, flags);
+ if (dev->isoc_ctl.buf == buf)
+ dev->isoc_ctl.buf = NULL;
+ spin_unlock_irqrestore(&dev->slock, flags);
+
+ videobuf_vmalloc_free(&buf->vb);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int
+buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct tm6000_fh *fh = vq->priv_data;
+ struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb);
+ struct tm6000_core *dev = fh->dev;
+ int rc = 0;
+
+ BUG_ON(NULL == fh->fmt);
+
+
+ /* FIXME: It assumes depth=2 */
+ /* The only currently supported format is 16 bits/pixel */
+ buf->vb.size = fh->fmt->depth*fh->width*fh->height >> 3;
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+
+ if (buf->fmt != fh->fmt ||
+ buf->vb.width != fh->width ||
+ buf->vb.height != fh->height ||
+ buf->vb.field != field) {
+ buf->fmt = fh->fmt;
+ buf->vb.width = fh->width;
+ buf->vb.height = fh->height;
+ buf->vb.field = field;
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+ }
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ rc = videobuf_iolock(vq, &buf->vb, NULL);
+ if (rc != 0)
+ goto fail;
+ }
+
+ if (!dev->isoc_ctl.num_bufs) {
+ rc = tm6000_prepare_isoc(dev);
+ if (rc < 0)
+ goto fail;
+
+ rc = tm6000_start_thread(dev);
+ if (rc < 0)
+ goto fail;
+
+ }
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+fail:
+ free_buffer(vq, buf);
+ return rc;
+}
+
+static void
+buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb);
+ struct tm6000_fh *fh = vq->priv_data;
+ struct tm6000_core *dev = fh->dev;
+ struct tm6000_dmaqueue *vidq = &dev->vidq;
+
+ buf->vb.state = VIDEOBUF_QUEUED;
+ list_add_tail(&buf->vb.queue, &vidq->active);
+}
+
+static void buffer_release(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb);
+
+ free_buffer(vq, buf);
+}
+
+static struct videobuf_queue_ops tm6000_video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/* ------------------------------------------------------------------
+ * IOCTL handling
+ * ------------------------------------------------------------------
+ */
+
+static bool is_res_read(struct tm6000_core *dev, struct tm6000_fh *fh)
+{
+ /* Is the current fh handling it? if so, that's OK */
+ if (dev->resources == fh && dev->is_res_read)
+ return true;
+
+ return false;
+}
+
+static bool is_res_streaming(struct tm6000_core *dev, struct tm6000_fh *fh)
+{
+ /* Is the current fh handling it? if so, that's OK */
+ if (dev->resources == fh)
+ return true;
+
+ return false;
+}
+
+static bool res_get(struct tm6000_core *dev, struct tm6000_fh *fh,
+ bool is_res_read)
+{
+ /* Is the current fh handling it? if so, that's OK */
+ if (dev->resources == fh && dev->is_res_read == is_res_read)
+ return true;
+
+ /* is it free? */
+ if (dev->resources)
+ return false;
+
+ /* grab it */
+ dev->resources = fh;
+ dev->is_res_read = is_res_read;
+ dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: get\n");
+ return true;
+}
+
+static void res_free(struct tm6000_core *dev, struct tm6000_fh *fh)
+{
+ /* Is the current fh handling it? if so, that's OK */
+ if (dev->resources != fh)
+ return;
+
+ dev->resources = NULL;
+ dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: put\n");
+}
+
+/* ------------------------------------------------------------------
+ * IOCTL vidioc handling
+ * ------------------------------------------------------------------
+ */
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev;
+
+ strlcpy(cap->driver, "tm6000", sizeof(cap->driver));
+ strlcpy(cap->card, "Trident TVMaster TM5600/6000/6010", sizeof(cap->card));
+ cap->version = TM6000_VERSION;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_STREAMING |
+ V4L2_CAP_AUDIO |
+ V4L2_CAP_READWRITE;
+
+ if (dev->tuner_type != TUNER_ABSENT)
+ cap->capabilities |= V4L2_CAP_TUNER;
+
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (unlikely(f->index >= ARRAY_SIZE(format)))
+ return -EINVAL;
+
+ strlcpy(f->description, format[f->index].name, sizeof(f->description));
+ f->pixelformat = format[f->index].fourcc;
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct tm6000_fh *fh = priv;
+
+ f->fmt.pix.width = fh->width;
+ f->fmt.pix.height = fh->height;
+ f->fmt.pix.field = fh->vb_vidq.field;
+ f->fmt.pix.pixelformat = fh->fmt->fourcc;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fh->fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+static struct tm6000_fmt *format_by_fourcc(unsigned int fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(format); i++)
+ if (format[i].fourcc == fourcc)
+ return format+i;
+ return NULL;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev;
+ struct tm6000_fmt *fmt;
+ enum v4l2_field field;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (NULL == fmt) {
+ dprintk(dev, V4L2_DEBUG_IOCTL_ARG, "Fourcc format (0x%08x)"
+ " invalid.\n", f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ field = f->fmt.pix.field;
+
+ if (field == V4L2_FIELD_ANY)
+ field = V4L2_FIELD_SEQ_TB;
+ else if (V4L2_FIELD_INTERLACED != field) {
+ dprintk(dev, V4L2_DEBUG_IOCTL_ARG, "Field type invalid.\n");
+ return -EINVAL;
+ }
+
+ tm6000_get_std_res(dev);
+
+ f->fmt.pix.width = dev->width;
+ f->fmt.pix.height = dev->height;
+
+ f->fmt.pix.width &= ~0x01;
+
+ f->fmt.pix.field = field;
+
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+/*FIXME: This seems to be generic enough to be at videodev2 */
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+ int ret = vidioc_try_fmt_vid_cap(file, fh, f);
+ if (ret < 0)
+ return ret;
+
+ fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ fh->width = f->fmt.pix.width;
+ fh->height = f->fmt.pix.height;
+ fh->vb_vidq.field = f->fmt.pix.field;
+ fh->type = f->type;
+
+ dev->fourcc = f->fmt.pix.pixelformat;
+
+ tm6000_set_fourcc_format(dev);
+
+ return 0;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct tm6000_fh *fh = priv;
+
+ return videobuf_reqbufs(&fh->vb_vidq, p);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct tm6000_fh *fh = priv;
+
+ return videobuf_querybuf(&fh->vb_vidq, p);
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct tm6000_fh *fh = priv;
+
+ return videobuf_qbuf(&fh->vb_vidq, p);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct tm6000_fh *fh = priv;
+
+ return videobuf_dqbuf(&fh->vb_vidq, p,
+ file->f_flags & O_NONBLOCK);
+}
+
+static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+
+ if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (i != fh->type)
+ return -EINVAL;
+
+ if (!res_get(dev, fh, false))
+ return -EBUSY;
+ return videobuf_streamon(&fh->vb_vidq);
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+
+ if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (i != fh->type)
+ return -EINVAL;
+
+ videobuf_streamoff(&fh->vb_vidq);
+ res_free(dev, fh);
+
+ return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
+{
+ int rc = 0;
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+
+ dev->norm = *norm;
+ rc = tm6000_init_analog_mode(dev);
+
+ fh->width = dev->width;
+ fh->height = dev->height;
+
+ if (rc < 0)
+ return rc;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
+
+ return 0;
+}
+
+static const char *iname[] = {
+ [TM6000_INPUT_TV] = "Television",
+ [TM6000_INPUT_COMPOSITE1] = "Composite 1",
+ [TM6000_INPUT_COMPOSITE2] = "Composite 2",
+ [TM6000_INPUT_SVIDEO] = "S-Video",
+};
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+ unsigned int n;
+
+ n = i->index;
+ if (n >= 3)
+ return -EINVAL;
+
+ if (!dev->vinput[n].type)
+ return -EINVAL;
+
+ i->index = n;
+
+ if (dev->vinput[n].type == TM6000_INPUT_TV)
+ i->type = V4L2_INPUT_TYPE_TUNER;
+ else
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+
+ strcpy(i->name, iname[dev->vinput[n].type]);
+
+ i->std = TM6000_STD;
+
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+
+ *i = dev->input;
+
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+ int rc = 0;
+
+ if (i >= 3)
+ return -EINVAL;
+ if (!dev->vinput[i].type)
+ return -EINVAL;
+
+ dev->input = i;
+
+ rc = vidioc_s_std(file, priv, &dev->vfd->current_norm);
+
+ return rc;
+}
+
+/* --- controls ---------------------------------------------- */
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tm6000_qctrl); i++)
+ if (qc->id && qc->id == tm6000_qctrl[i].id) {
+ memcpy(qc, &(tm6000_qctrl[i]),
+ sizeof(*qc));
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+ int val;
+
+ /* FIXME: Probably, those won't work! Maybe we need shadow regs */
+ switch (ctrl->id) {
+ case V4L2_CID_CONTRAST:
+ val = tm6000_get_reg(dev, TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0);
+ break;
+ case V4L2_CID_BRIGHTNESS:
+ val = tm6000_get_reg(dev, TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0);
+ return 0;
+ case V4L2_CID_SATURATION:
+ val = tm6000_get_reg(dev, TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0);
+ return 0;
+ case V4L2_CID_HUE:
+ val = tm6000_get_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, 0);
+ return 0;
+ case V4L2_CID_AUDIO_MUTE:
+ val = dev->ctl_mute;
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+ val = dev->ctl_volume;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ if (val < 0)
+ return val;
+
+ ctrl->value = val;
+
+ return 0;
+}
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+ u8 val = ctrl->value;
+
+ switch (ctrl->id) {
+ case V4L2_CID_CONTRAST:
+ tm6000_set_reg(dev, TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, val);
+ return 0;
+ case V4L2_CID_BRIGHTNESS:
+ tm6000_set_reg(dev, TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, val);
+ return 0;
+ case V4L2_CID_SATURATION:
+ tm6000_set_reg(dev, TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, val);
+ return 0;
+ case V4L2_CID_HUE:
+ tm6000_set_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, val);
+ return 0;
+ case V4L2_CID_AUDIO_MUTE:
+ dev->ctl_mute = val;
+ tm6000_tvaudio_set_mute(dev, val);
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+ dev->ctl_volume = val;
+ tm6000_set_volume(dev, val);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+
+ if (unlikely(UNSET == dev->tuner_type))
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+
+ strcpy(t->name, "Television");
+ t->type = V4L2_TUNER_ANALOG_TV;
+ t->capability = V4L2_TUNER_CAP_NORM;
+ t->rangehigh = 0xffffffffUL;
+ t->rxsubchans = V4L2_TUNER_SUB_STEREO;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t);
+
+ t->audmode = dev->amode;
+
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+
+ if (UNSET == dev->tuner_type)
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+
+ dev->amode = t->audmode;
+ dprintk(dev, 3, "audio mode: %x\n", t->audmode);
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t);
+
+ return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+
+ if (unlikely(UNSET == dev->tuner_type))
+ return -EINVAL;
+
+ f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ f->frequency = dev->freq;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, f);
+
+ return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+
+ if (unlikely(UNSET == dev->tuner_type))
+ return -EINVAL;
+ if (unlikely(f->tuner != 0))
+ return -EINVAL;
+ if (0 == fh->radio && V4L2_TUNER_ANALOG_TV != f->type)
+ return -EINVAL;
+ if (1 == fh->radio && V4L2_TUNER_RADIO != f->type)
+ return -EINVAL;
+
+ dev->freq = f->frequency;
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f);
+
+ return 0;
+}
+
+static int radio_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct tm6000_fh *fh = file->private_data;
+ struct tm6000_core *dev = fh->dev;
+
+ strcpy(cap->driver, "tm6000");
+ strlcpy(cap->card, dev->name, sizeof(dev->name));
+ sprintf(cap->bus_info, "USB%04x:%04x",
+ le16_to_cpu(dev->udev->descriptor.idVendor),
+ le16_to_cpu(dev->udev->descriptor.idProduct));
+ cap->version = dev->dev_type;
+ cap->capabilities = V4L2_CAP_TUNER |
+ V4L2_CAP_AUDIO |
+ V4L2_CAP_RADIO |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+static int radio_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct tm6000_fh *fh = file->private_data;
+ struct tm6000_core *dev = fh->dev;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ memset(t, 0, sizeof(*t));
+ strcpy(t->name, "Radio");
+ t->type = V4L2_TUNER_RADIO;
+ t->rxsubchans = V4L2_TUNER_SUB_STEREO;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t);
+
+ return 0;
+}
+
+static int radio_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct tm6000_fh *fh = file->private_data;
+ struct tm6000_core *dev = fh->dev;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t);
+
+ return 0;
+}
+
+static int radio_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+
+ if (i->index != 0)
+ return -EINVAL;
+
+ if (!dev->rinput.type)
+ return -EINVAL;
+
+ strcpy(i->name, "Radio");
+ i->type = V4L2_INPUT_TYPE_TUNER;
+
+ return 0;
+}
+
+static int radio_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+
+ if (dev->input != 5)
+ return -EINVAL;
+
+ *i = dev->input - 5;
+
+ return 0;
+}
+
+static int radio_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ memset(a, 0, sizeof(*a));
+ strcpy(a->name, "Radio");
+ return 0;
+}
+
+static int radio_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ return 0;
+}
+
+static int radio_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+
+ if (i)
+ return -EINVAL;
+
+ if (!dev->rinput.type)
+ return -EINVAL;
+
+ dev->input = i + 5;
+
+ return 0;
+}
+
+static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
+ return 0;
+}
+
+static int radio_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ const struct v4l2_queryctrl *ctrl;
+
+ if (c->id < V4L2_CID_BASE ||
+ c->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+ if (c->id == V4L2_CID_AUDIO_MUTE) {
+ ctrl = ctrl_by_id(c->id);
+ *c = *ctrl;
+ } else
+ *c = no_ctrl;
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------
+ File operations for the device
+ ------------------------------------------------------------------*/
+
+static int tm6000_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct tm6000_core *dev = video_drvdata(file);
+ struct tm6000_fh *fh;
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ int i, rc;
+ int radio = 0;
+
+ dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: open called (dev=%s)\n",
+ video_device_node_name(vdev));
+
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ break;
+ case VFL_TYPE_VBI:
+ type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ break;
+ case VFL_TYPE_RADIO:
+ radio = 1;
+ break;
+ }
+
+ /* If more than one user, mutex should be added */
+ dev->users++;
+
+ dprintk(dev, V4L2_DEBUG_OPEN, "open dev=%s type=%s users=%d\n",
+ video_device_node_name(vdev), v4l2_type_names[type],
+ dev->users);
+
+ /* allocate + initialize per filehandle data */
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (NULL == fh) {
+ dev->users--;
+ return -ENOMEM;
+ }
+
+ file->private_data = fh;
+ fh->dev = dev;
+ fh->radio = radio;
+ dev->radio = radio;
+ fh->type = type;
+ dev->fourcc = format[0].fourcc;
+
+ fh->fmt = format_by_fourcc(dev->fourcc);
+
+ tm6000_get_std_res(dev);
+
+ fh->width = dev->width;
+ fh->height = dev->height;
+
+ dprintk(dev, V4L2_DEBUG_OPEN, "Open: fh=0x%08lx, dev=0x%08lx, "
+ "dev->vidq=0x%08lx\n",
+ (unsigned long)fh, (unsigned long)dev,
+ (unsigned long)&dev->vidq);
+ dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty "
+ "queued=%d\n", list_empty(&dev->vidq.queued));
+ dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty "
+ "active=%d\n", list_empty(&dev->vidq.active));
+
+ /* initialize hardware on analog mode */
+ rc = tm6000_init_analog_mode(dev);
+ if (rc < 0)
+ return rc;
+
+ if (dev->mode != TM6000_MODE_ANALOG) {
+ /* Put all controls at a sane state */
+ for (i = 0; i < ARRAY_SIZE(tm6000_qctrl); i++)
+ qctl_regs[i] = tm6000_qctrl[i].default_value;
+
+ dev->mode = TM6000_MODE_ANALOG;
+ }
+
+ if (!fh->radio) {
+ videobuf_queue_vmalloc_init(&fh->vb_vidq, &tm6000_video_qops,
+ NULL, &dev->slock,
+ fh->type,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct tm6000_buffer), fh, &dev->lock);
+ } else {
+ dprintk(dev, V4L2_DEBUG_OPEN, "video_open: setting radio device\n");
+ dev->input = 5;
+ tm6000_set_audio_rinput(dev);
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio);
+ tm6000_prepare_isoc(dev);
+ tm6000_start_thread(dev);
+ }
+
+ return 0;
+}
+
+static ssize_t
+tm6000_read(struct file *file, char __user *data, size_t count, loff_t *pos)
+{
+ struct tm6000_fh *fh = file->private_data;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (!res_get(fh->dev, fh, true))
+ return -EBUSY;
+
+ return videobuf_read_stream(&fh->vb_vidq, data, count, pos, 0,
+ file->f_flags & O_NONBLOCK);
+ }
+ return 0;
+}
+
+static unsigned int
+tm6000_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct tm6000_fh *fh = file->private_data;
+ struct tm6000_buffer *buf;
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type)
+ return POLLERR;
+
+ if (!!is_res_streaming(fh->dev, fh))
+ return POLLERR;
+
+ if (!is_res_read(fh->dev, fh)) {
+ /* streaming capture */
+ if (list_empty(&fh->vb_vidq.stream))
+ return POLLERR;
+ buf = list_entry(fh->vb_vidq.stream.next, struct tm6000_buffer, vb.stream);
+ } else {
+ /* read() capture */
+ return videobuf_poll_stream(file, &fh->vb_vidq, wait);
+ }
+ poll_wait(file, &buf->vb.done, wait);
+ if (buf->vb.state == VIDEOBUF_DONE ||
+ buf->vb.state == VIDEOBUF_ERROR)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+static int tm6000_release(struct file *file)
+{
+ struct tm6000_fh *fh = file->private_data;
+ struct tm6000_core *dev = fh->dev;
+ struct video_device *vdev = video_devdata(file);
+
+ dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: close called (dev=%s, users=%d)\n",
+ video_device_node_name(vdev), dev->users);
+
+ dev->users--;
+
+ res_free(dev, fh);
+
+ if (!dev->users) {
+ tm6000_uninit_isoc(dev);
+
+ /* Stop interrupt USB pipe */
+ tm6000_ir_int_stop(dev);
+
+ usb_reset_configuration(dev->udev);
+
+ if (dev->int_in.endp)
+ usb_set_interface(dev->udev,
+ dev->isoc_in.bInterfaceNumber, 2);
+ else
+ usb_set_interface(dev->udev,
+ dev->isoc_in.bInterfaceNumber, 0);
+
+ /* Start interrupt USB pipe */
+ tm6000_ir_int_start(dev);
+
+ if (!fh->radio)
+ videobuf_mmap_free(&fh->vb_vidq);
+ }
+
+ kfree(fh);
+
+ return 0;
+}
+
+static int tm6000_mmap(struct file *file, struct vm_area_struct * vma)
+{
+ struct tm6000_fh *fh = file->private_data;
+
+ return videobuf_mmap_mapper(&fh->vb_vidq, vma);
+}
+
+static struct v4l2_file_operations tm6000_fops = {
+ .owner = THIS_MODULE,
+ .open = tm6000_open,
+ .release = tm6000_release,
+ .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
+ .read = tm6000_read,
+ .poll = tm6000_poll,
+ .mmap = tm6000_mmap,
+};
+
+static const struct v4l2_ioctl_ops video_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+};
+
+static struct video_device tm6000_template = {
+ .name = "tm6000",
+ .fops = &tm6000_fops,
+ .ioctl_ops = &video_ioctl_ops,
+ .release = video_device_release,
+ .tvnorms = TM6000_STD,
+ .current_norm = V4L2_STD_NTSC_M,
+};
+
+static const struct v4l2_file_operations radio_fops = {
+ .owner = THIS_MODULE,
+ .open = tm6000_open,
+ .release = tm6000_release,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops radio_ioctl_ops = {
+ .vidioc_querycap = radio_querycap,
+ .vidioc_g_tuner = radio_g_tuner,
+ .vidioc_enum_input = radio_enum_input,
+ .vidioc_g_audio = radio_g_audio,
+ .vidioc_s_tuner = radio_s_tuner,
+ .vidioc_s_audio = radio_s_audio,
+ .vidioc_s_input = radio_s_input,
+ .vidioc_s_std = radio_s_std,
+ .vidioc_queryctrl = radio_queryctrl,
+ .vidioc_g_input = radio_g_input,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+};
+
+static struct video_device tm6000_radio_template = {
+ .name = "tm6000",
+ .fops = &radio_fops,
+ .ioctl_ops = &radio_ioctl_ops,
+};
+
+/* -----------------------------------------------------------------
+ * Initialization and module stuff
+ * ------------------------------------------------------------------
+ */
+
+static struct video_device *vdev_init(struct tm6000_core *dev,
+ const struct video_device
+ *template, const char *type_name)
+{
+ struct video_device *vfd;
+
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+
+ *vfd = *template;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+ vfd->release = video_device_release;
+ vfd->debug = tm6000_debug;
+ vfd->lock = &dev->lock;
+
+ snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name);
+
+ video_set_drvdata(vfd, dev);
+ return vfd;
+}
+
+int tm6000_v4l2_register(struct tm6000_core *dev)
+{
+ int ret = -1;
+
+ dev->vfd = vdev_init(dev, &tm6000_template, "video");
+
+ if (!dev->vfd) {
+ printk(KERN_INFO "%s: can't register video device\n",
+ dev->name);
+ return -ENOMEM;
+ }
+
+ /* init video dma queues */
+ INIT_LIST_HEAD(&dev->vidq.active);
+ INIT_LIST_HEAD(&dev->vidq.queued);
+
+ ret = video_register_device(dev->vfd, VFL_TYPE_GRABBER, video_nr);
+
+ if (ret < 0) {
+ printk(KERN_INFO "%s: can't register video device\n",
+ dev->name);
+ return ret;
+ }
+
+ printk(KERN_INFO "%s: registered device %s\n",
+ dev->name, video_device_node_name(dev->vfd));
+
+ if (dev->caps.has_radio) {
+ dev->radio_dev = vdev_init(dev, &tm6000_radio_template,
+ "radio");
+ if (!dev->radio_dev) {
+ printk(KERN_INFO "%s: can't register radio device\n",
+ dev->name);
+ return ret; /* FIXME release resource */
+ }
+
+ ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO,
+ radio_nr);
+ if (ret < 0) {
+ printk(KERN_INFO "%s: can't register radio device\n",
+ dev->name);
+ return ret; /* FIXME release resource */
+ }
+
+ printk(KERN_INFO "%s: registered device %s\n",
+ dev->name, video_device_node_name(dev->radio_dev));
+ }
+
+ printk(KERN_INFO "Trident TVMaster TM5600/TM6000/TM6010 USB2 board (Load status: %d)\n", ret);
+ return ret;
+}
+
+int tm6000_v4l2_unregister(struct tm6000_core *dev)
+{
+ video_unregister_device(dev->vfd);
+
+ if (dev->radio_dev) {
+ if (video_is_registered(dev->radio_dev))
+ video_unregister_device(dev->radio_dev);
+ else
+ video_device_release(dev->radio_dev);
+ dev->radio_dev = NULL;
+ }
+
+ return 0;
+}
+
+int tm6000_v4l2_exit(void)
+{
+ return 0;
+}
+
+module_param(video_nr, int, 0);
+MODULE_PARM_DESC(video_nr, "Allow changing video device number");
+
+module_param_named(debug, tm6000_debug, int, 0444);
+MODULE_PARM_DESC(debug, "activates debug info");
+
+module_param(vid_limit, int, 0644);
+MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
+
diff --git a/drivers/media/video/tm6000/tm6000.h b/drivers/media/video/tm6000/tm6000.h
new file mode 100644
index 00000000..27ba659c
--- /dev/null
+++ b/drivers/media/video/tm6000/tm6000.h
@@ -0,0 +1,401 @@
+/*
+ * tm6000.h - driver for TM5600/TM6000/TM6010 USB video capture devices
+ *
+ * Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
+ *
+ * Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com>
+ * - DVB-T support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/videobuf-vmalloc.h>
+#include "tm6000-usb-isoc.h"
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <media/v4l2-device.h>
+
+#include <linux/dvb/frontend.h>
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dmxdev.h"
+
+#define TM6000_VERSION KERNEL_VERSION(0, 0, 2)
+
+/* Inputs */
+enum tm6000_itype {
+ TM6000_INPUT_TV = 1,
+ TM6000_INPUT_COMPOSITE1,
+ TM6000_INPUT_COMPOSITE2,
+ TM6000_INPUT_SVIDEO,
+ TM6000_INPUT_DVB,
+ TM6000_INPUT_RADIO,
+};
+
+enum tm6000_mux {
+ TM6000_VMUX_VIDEO_A = 1,
+ TM6000_VMUX_VIDEO_B,
+ TM6000_VMUX_VIDEO_AB,
+ TM6000_AMUX_ADC1,
+ TM6000_AMUX_ADC2,
+ TM6000_AMUX_SIF1,
+ TM6000_AMUX_SIF2,
+ TM6000_AMUX_I2S,
+};
+
+enum tm6000_devtype {
+ TM6000 = 0,
+ TM5600,
+ TM6010,
+};
+
+struct tm6000_input {
+ enum tm6000_itype type;
+ enum tm6000_mux vmux;
+ enum tm6000_mux amux;
+ unsigned int v_gpio;
+ unsigned int a_gpio;
+};
+
+/* ------------------------------------------------------------------
+ * Basic structures
+ * ------------------------------------------------------------------
+ */
+
+struct tm6000_fmt {
+ char *name;
+ u32 fourcc; /* v4l2 format id */
+ int depth;
+};
+
+/* buffer for one video frame */
+struct tm6000_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+
+ struct tm6000_fmt *fmt;
+};
+
+struct tm6000_dmaqueue {
+ struct list_head active;
+ struct list_head queued;
+
+ /* thread for generating video stream*/
+ struct task_struct *kthread;
+ wait_queue_head_t wq;
+ /* Counters to control fps rate */
+ int frame;
+ int ini_jiffies;
+};
+
+/* device states */
+enum tm6000_core_state {
+ DEV_INITIALIZED = 0x01,
+ DEV_DISCONNECTED = 0x02,
+ DEV_MISCONFIGURED = 0x04,
+};
+
+/* io methods */
+enum tm6000_io_method {
+ IO_NONE,
+ IO_READ,
+ IO_MMAP,
+};
+
+enum tm6000_mode {
+ TM6000_MODE_UNKNOWN = 0,
+ TM6000_MODE_ANALOG,
+ TM6000_MODE_DIGITAL,
+};
+
+struct tm6000_gpio {
+ int tuner_reset;
+ int tuner_on;
+ int demod_reset;
+ int demod_on;
+ int power_led;
+ int dvb_led;
+ int ir;
+};
+
+struct tm6000_capabilities {
+ unsigned int has_tuner:1;
+ unsigned int has_tda9874:1;
+ unsigned int has_dvb:1;
+ unsigned int has_zl10353:1;
+ unsigned int has_eeprom:1;
+ unsigned int has_remote:1;
+ unsigned int has_radio:1;
+};
+
+struct tm6000_dvb {
+ struct dvb_adapter adapter;
+ struct dvb_demux demux;
+ struct dvb_frontend *frontend;
+ struct dmxdev dmxdev;
+ unsigned int streams;
+ struct urb *bulk_urb;
+ struct mutex mutex;
+};
+
+struct snd_tm6000_card {
+ struct snd_card *card;
+ spinlock_t reg_lock;
+ struct tm6000_core *core;
+ struct snd_pcm_substream *substream;
+
+ /* temporary data for buffer fill processing */
+ unsigned buf_pos;
+ unsigned period_pos;
+};
+
+struct tm6000_endpoint {
+ struct usb_host_endpoint *endp;
+ __u8 bInterfaceNumber;
+ __u8 bAlternateSetting;
+ unsigned maxsize;
+};
+
+#define TM6000_QUIRK_NO_USB_DELAY (1 << 0)
+
+struct tm6000_core {
+ /* generic device properties */
+ char name[30]; /* name (including minor) of the device */
+ int model; /* index in the device_data struct */
+ int devno; /* marks the number of this device */
+ enum tm6000_devtype dev_type; /* type of device */
+ unsigned char eedata[256]; /* Eeprom data */
+ unsigned eedata_size; /* Size of the eeprom info */
+
+ v4l2_std_id norm; /* Current norm */
+ int width, height; /* Selected resolution */
+
+ enum tm6000_core_state state;
+
+ /* Device Capabilities*/
+ struct tm6000_capabilities caps;
+
+ /* Used to load alsa/dvb */
+ struct work_struct request_module_wk;
+
+ /* Tuner configuration */
+ int tuner_type; /* type of the tuner */
+ int tuner_addr; /* tuner address */
+
+ struct tm6000_gpio gpio;
+
+ char *ir_codes;
+
+ __u8 radio;
+
+ /* Demodulator configuration */
+ int demod_addr; /* demodulator address */
+
+ int audio_bitrate;
+ /* i2c i/o */
+ struct i2c_adapter i2c_adap;
+ struct i2c_client i2c_client;
+
+
+ /* extension */
+ struct list_head devlist;
+
+ /* video for linux */
+ int users;
+
+ /* various device info */
+ struct tm6000_fh *resources; /* Points to fh that is streaming */
+ bool is_res_read;
+
+ struct video_device *vfd;
+ struct video_device *radio_dev;
+ struct tm6000_dmaqueue vidq;
+ struct v4l2_device v4l2_dev;
+
+ int input;
+ struct tm6000_input vinput[3]; /* video input */
+ struct tm6000_input rinput; /* radio input */
+
+ int freq;
+ unsigned int fourcc;
+
+ enum tm6000_mode mode;
+
+ int ctl_mute; /* audio */
+ int ctl_volume;
+ int amode;
+
+ /* DVB-T support */
+ struct tm6000_dvb *dvb;
+
+ /* audio support */
+ struct snd_tm6000_card *adev;
+ struct work_struct wq_trigger; /* Trigger to start/stop audio for alsa module */
+ atomic_t stream_started; /* stream should be running if true */
+
+ struct tm6000_IR *ir;
+
+ /* locks */
+ struct mutex lock;
+ struct mutex usb_lock;
+
+ /* usb transfer */
+ struct usb_device *udev; /* the usb device */
+
+ struct tm6000_endpoint bulk_in, bulk_out, isoc_in, isoc_out;
+ struct tm6000_endpoint int_in, int_out;
+
+ /* scaler!=0 if scaler is active*/
+ int scaler;
+
+ /* Isoc control struct */
+ struct usb_isoc_ctl isoc_ctl;
+
+ spinlock_t slock;
+
+ unsigned long quirks;
+};
+
+enum tm6000_ops_type {
+ TM6000_AUDIO = 0x10,
+ TM6000_DVB = 0x20,
+};
+
+struct tm6000_ops {
+ struct list_head next;
+ char *name;
+ enum tm6000_ops_type type;
+ int (*init)(struct tm6000_core *);
+ int (*fini)(struct tm6000_core *);
+ int (*fillbuf)(struct tm6000_core *, char *buf, int size);
+};
+
+struct tm6000_fh {
+ struct tm6000_core *dev;
+ unsigned int radio;
+
+ /* video capture */
+ struct tm6000_fmt *fmt;
+ unsigned int width, height;
+ struct videobuf_queue vb_vidq;
+
+ enum v4l2_buf_type type;
+};
+
+#define TM6000_STD (V4L2_STD_PAL|V4L2_STD_PAL_N|V4L2_STD_PAL_Nc| \
+ V4L2_STD_PAL_M|V4L2_STD_PAL_60|V4L2_STD_NTSC_M| \
+ V4L2_STD_NTSC_M_JP|V4L2_STD_SECAM)
+
+/* In tm6000-cards.c */
+
+int tm6000_tuner_callback(void *ptr, int component, int command, int arg);
+int tm6000_xc5000_callback(void *ptr, int component, int command, int arg);
+int tm6000_cards_setup(struct tm6000_core *dev);
+void tm6000_flash_led(struct tm6000_core *dev, u8 state);
+
+/* In tm6000-core.c */
+
+int tm6000_read_write_usb(struct tm6000_core *dev, u8 reqtype, u8 req,
+ u16 value, u16 index, u8 *buf, u16 len);
+int tm6000_get_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index);
+int tm6000_get_reg16(struct tm6000_core *dev, u8 req, u16 value, u16 index);
+int tm6000_get_reg32(struct tm6000_core *dev, u8 req, u16 value, u16 index);
+int tm6000_set_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index);
+int tm6000_set_reg_mask(struct tm6000_core *dev, u8 req, u16 value,
+ u16 index, u16 mask);
+int tm6000_i2c_reset(struct tm6000_core *dev, u16 tsleep);
+int tm6000_init(struct tm6000_core *dev);
+int tm6000_reset(struct tm6000_core *dev);
+
+int tm6000_init_analog_mode(struct tm6000_core *dev);
+int tm6000_init_digital_mode(struct tm6000_core *dev);
+int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate);
+int tm6000_set_audio_rinput(struct tm6000_core *dev);
+int tm6000_tvaudio_set_mute(struct tm6000_core *dev, u8 mute);
+void tm6000_set_volume(struct tm6000_core *dev, int vol);
+
+int tm6000_v4l2_register(struct tm6000_core *dev);
+int tm6000_v4l2_unregister(struct tm6000_core *dev);
+int tm6000_v4l2_exit(void);
+void tm6000_set_fourcc_format(struct tm6000_core *dev);
+
+void tm6000_remove_from_devlist(struct tm6000_core *dev);
+void tm6000_add_into_devlist(struct tm6000_core *dev);
+int tm6000_register_extension(struct tm6000_ops *ops);
+void tm6000_unregister_extension(struct tm6000_ops *ops);
+void tm6000_init_extension(struct tm6000_core *dev);
+void tm6000_close_extension(struct tm6000_core *dev);
+int tm6000_call_fillbuf(struct tm6000_core *dev, enum tm6000_ops_type type,
+ char *buf, int size);
+
+
+/* In tm6000-stds.c */
+void tm6000_get_std_res(struct tm6000_core *dev);
+int tm6000_set_standard(struct tm6000_core *dev);
+
+/* In tm6000-i2c.c */
+int tm6000_i2c_register(struct tm6000_core *dev);
+int tm6000_i2c_unregister(struct tm6000_core *dev);
+
+/* In tm6000-queue.c */
+
+int tm6000_v4l2_mmap(struct file *filp, struct vm_area_struct *vma);
+
+int tm6000_vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type i);
+int tm6000_vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type i);
+int tm6000_vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *rb);
+int tm6000_vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *b);
+int tm6000_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b);
+int tm6000_vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b);
+ssize_t tm6000_v4l2_read(struct file *filp, char __user * buf, size_t count,
+ loff_t *f_pos);
+unsigned int tm6000_v4l2_poll(struct file *file,
+ struct poll_table_struct *wait);
+int tm6000_queue_init(struct tm6000_core *dev);
+
+/* In tm6000-alsa.c */
+/*int tm6000_audio_init(struct tm6000_core *dev, int idx);*/
+
+/* In tm6000-input.c */
+int tm6000_ir_init(struct tm6000_core *dev);
+int tm6000_ir_fini(struct tm6000_core *dev);
+void tm6000_ir_wait(struct tm6000_core *dev, u8 state);
+int tm6000_ir_int_start(struct tm6000_core *dev);
+void tm6000_ir_int_stop(struct tm6000_core *dev);
+
+/* Debug stuff */
+
+extern int tm6000_debug;
+
+#define dprintk(dev, level, fmt, arg...) do {\
+ if (tm6000_debug & level) \
+ printk(KERN_INFO "(%lu) %s %s :"fmt, jiffies, \
+ dev->name, __func__ , ##arg); } while (0)
+
+#define V4L2_DEBUG_REG 0x0004
+#define V4L2_DEBUG_I2C 0x0008
+#define V4L2_DEBUG_QUEUE 0x0010
+#define V4L2_DEBUG_ISOC 0x0020
+#define V4L2_DEBUG_RES_LOCK 0x0040 /* Resource locking */
+#define V4L2_DEBUG_OPEN 0x0080 /* video open/close debug */
+
+#define tm6000_err(fmt, arg...) do {\
+ printk(KERN_ERR "tm6000 %s :"fmt, \
+ __func__ , ##arg); } while (0)
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c
new file mode 100644
index 00000000..a5c6397a
--- /dev/null
+++ b/drivers/media/video/tuner-core.c
@@ -0,0 +1,1336 @@
+/*
+ * i2c tv tuner chip device driver
+ * core core, i.e. kernel interfaces, registering and so on
+ *
+ * Copyright(c) by Ralph Metzler, Gerd Knorr, Gunther Mayer
+ *
+ * Copyright(c) 2005-2011 by Mauro Carvalho Chehab
+ * - Added support for a separate Radio tuner
+ * - Major rework and cleanups at the code
+ *
+ * This driver supports many devices and the idea is to let the driver
+ * detect which device is present. So rather than listing all supported
+ * devices here, we pretend to support a single, fake device type that will
+ * handle both radio and analog TV tuning.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/i2c.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/videodev2.h>
+#include <media/tuner.h>
+#include <media/tuner-types.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include "mt20xx.h"
+#include "tda8290.h"
+#include "tea5761.h"
+#include "tea5767.h"
+#include "tuner-xc2028.h"
+#include "tuner-simple.h"
+#include "tda9887.h"
+#include "xc5000.h"
+#include "tda18271.h"
+#include "xc4000.h"
+
+#define UNSET (-1U)
+
+#define PREFIX (t->i2c->driver->driver.name)
+
+/*
+ * Driver modprobe parameters
+ */
+
+/* insmod options used at init time => read/only */
+static unsigned int addr;
+static unsigned int no_autodetect;
+static unsigned int show_i2c;
+
+module_param(addr, int, 0444);
+module_param(no_autodetect, int, 0444);
+module_param(show_i2c, int, 0444);
+
+/* insmod options used at runtime => read/write */
+static int tuner_debug;
+static unsigned int tv_range[2] = { 44, 958 };
+static unsigned int radio_range[2] = { 65, 108 };
+static char pal[] = "--";
+static char secam[] = "--";
+static char ntsc[] = "-";
+
+module_param_named(debug, tuner_debug, int, 0644);
+module_param_array(tv_range, int, NULL, 0644);
+module_param_array(radio_range, int, NULL, 0644);
+module_param_string(pal, pal, sizeof(pal), 0644);
+module_param_string(secam, secam, sizeof(secam), 0644);
+module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
+
+/*
+ * Static vars
+ */
+
+static LIST_HEAD(tuner_list);
+static const struct v4l2_subdev_ops tuner_ops;
+
+/*
+ * Debug macros
+ */
+
+#define tuner_warn(fmt, arg...) do { \
+ printk(KERN_WARNING "%s %d-%04x: " fmt, PREFIX, \
+ i2c_adapter_id(t->i2c->adapter), \
+ t->i2c->addr, ##arg); \
+ } while (0)
+
+#define tuner_info(fmt, arg...) do { \
+ printk(KERN_INFO "%s %d-%04x: " fmt, PREFIX, \
+ i2c_adapter_id(t->i2c->adapter), \
+ t->i2c->addr, ##arg); \
+ } while (0)
+
+#define tuner_err(fmt, arg...) do { \
+ printk(KERN_ERR "%s %d-%04x: " fmt, PREFIX, \
+ i2c_adapter_id(t->i2c->adapter), \
+ t->i2c->addr, ##arg); \
+ } while (0)
+
+#define tuner_dbg(fmt, arg...) do { \
+ if (tuner_debug) \
+ printk(KERN_DEBUG "%s %d-%04x: " fmt, PREFIX, \
+ i2c_adapter_id(t->i2c->adapter), \
+ t->i2c->addr, ##arg); \
+ } while (0)
+
+/*
+ * Internal struct used inside the driver
+ */
+
+struct tuner {
+ /* device */
+ struct dvb_frontend fe;
+ struct i2c_client *i2c;
+ struct v4l2_subdev sd;
+ struct list_head list;
+
+ /* keep track of the current settings */
+ v4l2_std_id std;
+ unsigned int tv_freq;
+ unsigned int radio_freq;
+ unsigned int audmode;
+
+ enum v4l2_tuner_type mode;
+ unsigned int mode_mask; /* Combination of allowable modes */
+
+ bool standby; /* Standby mode */
+
+ unsigned int type; /* chip type id */
+ unsigned int config;
+ const char *name;
+};
+
+/*
+ * Function prototypes
+ */
+
+static void set_tv_freq(struct i2c_client *c, unsigned int freq);
+static void set_radio_freq(struct i2c_client *c, unsigned int freq);
+
+/*
+ * tuner attach/detach logic
+ */
+
+/* This macro allows us to probe dynamically, avoiding static links */
+#ifdef CONFIG_MEDIA_ATTACH
+#define tuner_symbol_probe(FUNCTION, ARGS...) ({ \
+ int __r = -EINVAL; \
+ typeof(&FUNCTION) __a = symbol_request(FUNCTION); \
+ if (__a) { \
+ __r = (int) __a(ARGS); \
+ symbol_put(FUNCTION); \
+ } else { \
+ printk(KERN_ERR "TUNER: Unable to find " \
+ "symbol "#FUNCTION"()\n"); \
+ } \
+ __r; \
+})
+
+static void tuner_detach(struct dvb_frontend *fe)
+{
+ if (fe->ops.tuner_ops.release) {
+ fe->ops.tuner_ops.release(fe);
+ symbol_put_addr(fe->ops.tuner_ops.release);
+ }
+ if (fe->ops.analog_ops.release) {
+ fe->ops.analog_ops.release(fe);
+ symbol_put_addr(fe->ops.analog_ops.release);
+ }
+}
+#else
+#define tuner_symbol_probe(FUNCTION, ARGS...) ({ \
+ FUNCTION(ARGS); \
+})
+
+static void tuner_detach(struct dvb_frontend *fe)
+{
+ if (fe->ops.tuner_ops.release)
+ fe->ops.tuner_ops.release(fe);
+ if (fe->ops.analog_ops.release)
+ fe->ops.analog_ops.release(fe);
+}
+#endif
+
+
+static inline struct tuner *to_tuner(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct tuner, sd);
+}
+
+/*
+ * struct analog_demod_ops callbacks
+ */
+
+static void fe_set_params(struct dvb_frontend *fe,
+ struct analog_parameters *params)
+{
+ struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
+ struct tuner *t = fe->analog_demod_priv;
+
+ if (NULL == fe_tuner_ops->set_analog_params) {
+ tuner_warn("Tuner frontend module has no way to set freq\n");
+ return;
+ }
+ fe_tuner_ops->set_analog_params(fe, params);
+}
+
+static void fe_standby(struct dvb_frontend *fe)
+{
+ struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
+
+ if (fe_tuner_ops->sleep)
+ fe_tuner_ops->sleep(fe);
+}
+
+static int fe_has_signal(struct dvb_frontend *fe)
+{
+ u16 strength = 0;
+
+ if (fe->ops.tuner_ops.get_rf_strength)
+ fe->ops.tuner_ops.get_rf_strength(fe, &strength);
+
+ return strength;
+}
+
+static int fe_set_config(struct dvb_frontend *fe, void *priv_cfg)
+{
+ struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
+ struct tuner *t = fe->analog_demod_priv;
+
+ if (fe_tuner_ops->set_config)
+ return fe_tuner_ops->set_config(fe, priv_cfg);
+
+ tuner_warn("Tuner frontend module has no way to set config\n");
+
+ return 0;
+}
+
+static void tuner_status(struct dvb_frontend *fe);
+
+static struct analog_demod_ops tuner_analog_ops = {
+ .set_params = fe_set_params,
+ .standby = fe_standby,
+ .has_signal = fe_has_signal,
+ .set_config = fe_set_config,
+ .tuner_status = tuner_status
+};
+
+/*
+ * Functions to select between radio and TV and tuner probe/remove functions
+ */
+
+/**
+ * set_type - Sets the tuner type for a given device
+ *
+ * @c: i2c_client descriptoy
+ * @type: type of the tuner (e. g. tuner number)
+ * @new_mode_mask: Indicates if tuner supports TV and/or Radio
+ * @new_config: an optional parameter ranging from 0-255 used by
+ a few tuners to adjust an internal parameter,
+ like LNA mode
+ * @tuner_callback: an optional function to be called when switching
+ * to analog mode
+ *
+ * This function applys the tuner config to tuner specified
+ * by tun_setup structure. It contains several per-tuner initialization "magic"
+ */
+static void set_type(struct i2c_client *c, unsigned int type,
+ unsigned int new_mode_mask, unsigned int new_config,
+ int (*tuner_callback) (void *dev, int component, int cmd, int arg))
+{
+ struct tuner *t = to_tuner(i2c_get_clientdata(c));
+ struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+ unsigned char buffer[4];
+ int tune_now = 1;
+
+ if (type == UNSET || type == TUNER_ABSENT) {
+ tuner_dbg("tuner 0x%02x: Tuner type absent\n", c->addr);
+ return;
+ }
+
+ t->type = type;
+ /* prevent invalid config values */
+ t->config = new_config < 256 ? new_config : 0;
+ if (tuner_callback != NULL) {
+ tuner_dbg("defining GPIO callback\n");
+ t->fe.callback = tuner_callback;
+ }
+
+ /* discard private data, in case set_type() was previously called */
+ tuner_detach(&t->fe);
+ t->fe.analog_demod_priv = NULL;
+
+ switch (t->type) {
+ case TUNER_MT2032:
+ if (!dvb_attach(microtune_attach,
+ &t->fe, t->i2c->adapter, t->i2c->addr))
+ goto attach_failed;
+ break;
+ case TUNER_PHILIPS_TDA8290:
+ {
+ struct tda829x_config cfg = {
+ .lna_cfg = t->config,
+ };
+ if (!dvb_attach(tda829x_attach, &t->fe, t->i2c->adapter,
+ t->i2c->addr, &cfg))
+ goto attach_failed;
+ break;
+ }
+ case TUNER_TEA5767:
+ if (!dvb_attach(tea5767_attach, &t->fe,
+ t->i2c->adapter, t->i2c->addr))
+ goto attach_failed;
+ t->mode_mask = T_RADIO;
+ break;
+ case TUNER_TEA5761:
+ if (!dvb_attach(tea5761_attach, &t->fe,
+ t->i2c->adapter, t->i2c->addr))
+ goto attach_failed;
+ t->mode_mask = T_RADIO;
+ break;
+ case TUNER_PHILIPS_FMD1216ME_MK3:
+ case TUNER_PHILIPS_FMD1216MEX_MK3:
+ buffer[0] = 0x0b;
+ buffer[1] = 0xdc;
+ buffer[2] = 0x9c;
+ buffer[3] = 0x60;
+ i2c_master_send(c, buffer, 4);
+ mdelay(1);
+ buffer[2] = 0x86;
+ buffer[3] = 0x54;
+ i2c_master_send(c, buffer, 4);
+ if (!dvb_attach(simple_tuner_attach, &t->fe,
+ t->i2c->adapter, t->i2c->addr, t->type))
+ goto attach_failed;
+ break;
+ case TUNER_PHILIPS_TD1316:
+ buffer[0] = 0x0b;
+ buffer[1] = 0xdc;
+ buffer[2] = 0x86;
+ buffer[3] = 0xa4;
+ i2c_master_send(c, buffer, 4);
+ if (!dvb_attach(simple_tuner_attach, &t->fe,
+ t->i2c->adapter, t->i2c->addr, t->type))
+ goto attach_failed;
+ break;
+ case TUNER_XC2028:
+ {
+ struct xc2028_config cfg = {
+ .i2c_adap = t->i2c->adapter,
+ .i2c_addr = t->i2c->addr,
+ };
+ if (!dvb_attach(xc2028_attach, &t->fe, &cfg))
+ goto attach_failed;
+ tune_now = 0;
+ break;
+ }
+ case TUNER_TDA9887:
+ if (!dvb_attach(tda9887_attach,
+ &t->fe, t->i2c->adapter, t->i2c->addr))
+ goto attach_failed;
+ break;
+ case TUNER_XC5000:
+ {
+ struct xc5000_config xc5000_cfg = {
+ .i2c_address = t->i2c->addr,
+ /* if_khz will be set at dvb_attach() */
+ .if_khz = 0,
+ };
+
+ if (!dvb_attach(xc5000_attach,
+ &t->fe, t->i2c->adapter, &xc5000_cfg))
+ goto attach_failed;
+ tune_now = 0;
+ break;
+ }
+ case TUNER_XC5000C:
+ {
+ struct xc5000_config xc5000c_cfg = {
+ .i2c_address = t->i2c->addr,
+ /* if_khz will be set at dvb_attach() */
+ .if_khz = 0,
+ .chip_id = XC5000C,
+ };
+
+ if (!dvb_attach(xc5000_attach,
+ &t->fe, t->i2c->adapter, &xc5000c_cfg))
+ goto attach_failed;
+ tune_now = 0;
+ break;
+ }
+ case TUNER_NXP_TDA18271:
+ {
+ struct tda18271_config cfg = {
+ .config = t->config,
+ .small_i2c = TDA18271_03_BYTE_CHUNK_INIT,
+ };
+
+ if (!dvb_attach(tda18271_attach, &t->fe, t->i2c->addr,
+ t->i2c->adapter, &cfg))
+ goto attach_failed;
+ tune_now = 0;
+ break;
+ }
+ case TUNER_XC4000:
+ {
+ struct xc4000_config xc4000_cfg = {
+ .i2c_address = t->i2c->addr,
+ /* FIXME: the correct parameters will be set */
+ /* only when the digital dvb_attach() occurs */
+ .default_pm = 0,
+ .dvb_amplitude = 0,
+ .set_smoothedcvbs = 0,
+ .if_khz = 0
+ };
+ if (!dvb_attach(xc4000_attach,
+ &t->fe, t->i2c->adapter, &xc4000_cfg))
+ goto attach_failed;
+ tune_now = 0;
+ break;
+ }
+ default:
+ if (!dvb_attach(simple_tuner_attach, &t->fe,
+ t->i2c->adapter, t->i2c->addr, t->type))
+ goto attach_failed;
+
+ break;
+ }
+
+ if ((NULL == analog_ops->set_params) &&
+ (fe_tuner_ops->set_analog_params)) {
+
+ t->name = fe_tuner_ops->info.name;
+
+ t->fe.analog_demod_priv = t;
+ memcpy(analog_ops, &tuner_analog_ops,
+ sizeof(struct analog_demod_ops));
+
+ } else {
+ t->name = analog_ops->info.name;
+ }
+
+ tuner_dbg("type set to %s\n", t->name);
+
+ t->mode_mask = new_mode_mask;
+
+ /* Some tuners require more initialization setup before use,
+ such as firmware download or device calibration.
+ trying to set a frequency here will just fail
+ FIXME: better to move set_freq to the tuner code. This is needed
+ on analog tuners for PLL to properly work
+ */
+ if (tune_now) {
+ if (V4L2_TUNER_RADIO == t->mode)
+ set_radio_freq(c, t->radio_freq);
+ else
+ set_tv_freq(c, t->tv_freq);
+ }
+
+ tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
+ c->adapter->name, c->driver->driver.name, c->addr << 1, type,
+ t->mode_mask);
+ return;
+
+attach_failed:
+ tuner_dbg("Tuner attach for type = %d failed.\n", t->type);
+ t->type = TUNER_ABSENT;
+
+ return;
+}
+
+/**
+ * tuner_s_type_addr - Sets the tuner type for a device
+ *
+ * @sd: subdev descriptor
+ * @tun_setup: type to be associated to a given tuner i2c address
+ *
+ * This function applys the tuner config to tuner specified
+ * by tun_setup structure.
+ * If tuner I2C address is UNSET, then it will only set the device
+ * if the tuner supports the mode specified in the call.
+ * If the address is specified, the change will be applied only if
+ * tuner I2C address matches.
+ * The call can change the tuner number and the tuner mode.
+ */
+static int tuner_s_type_addr(struct v4l2_subdev *sd,
+ struct tuner_setup *tun_setup)
+{
+ struct tuner *t = to_tuner(sd);
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
+
+ tuner_dbg("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n",
+ tun_setup->type,
+ tun_setup->addr,
+ tun_setup->mode_mask,
+ tun_setup->config);
+
+ if ((t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
+ (t->mode_mask & tun_setup->mode_mask))) ||
+ (tun_setup->addr == c->addr)) {
+ set_type(c, tun_setup->type, tun_setup->mode_mask,
+ tun_setup->config, tun_setup->tuner_callback);
+ } else
+ tuner_dbg("set addr discarded for type %i, mask %x. "
+ "Asked to change tuner at addr 0x%02x, with mask %x\n",
+ t->type, t->mode_mask,
+ tun_setup->addr, tun_setup->mode_mask);
+
+ return 0;
+}
+
+/**
+ * tuner_s_config - Sets tuner configuration
+ *
+ * @sd: subdev descriptor
+ * @cfg: tuner configuration
+ *
+ * Calls tuner set_config() private function to set some tuner-internal
+ * parameters
+ */
+static int tuner_s_config(struct v4l2_subdev *sd,
+ const struct v4l2_priv_tun_config *cfg)
+{
+ struct tuner *t = to_tuner(sd);
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+
+ if (t->type != cfg->tuner)
+ return 0;
+
+ if (analog_ops->set_config) {
+ analog_ops->set_config(&t->fe, cfg->priv);
+ return 0;
+ }
+
+ tuner_dbg("Tuner frontend module has no way to set config\n");
+ return 0;
+}
+
+/**
+ * tuner_lookup - Seek for tuner adapters
+ *
+ * @adap: i2c_adapter struct
+ * @radio: pointer to be filled if the adapter is radio
+ * @tv: pointer to be filled if the adapter is TV
+ *
+ * Search for existing radio and/or TV tuners on the given I2C adapter,
+ * discarding demod-only adapters (tda9887).
+ *
+ * Note that when this function is called from tuner_probe you can be
+ * certain no other devices will be added/deleted at the same time, I2C
+ * core protects against that.
+ */
+static void tuner_lookup(struct i2c_adapter *adap,
+ struct tuner **radio, struct tuner **tv)
+{
+ struct tuner *pos;
+
+ *radio = NULL;
+ *tv = NULL;
+
+ list_for_each_entry(pos, &tuner_list, list) {
+ int mode_mask;
+
+ if (pos->i2c->adapter != adap ||
+ strcmp(pos->i2c->driver->driver.name, "tuner"))
+ continue;
+
+ mode_mask = pos->mode_mask;
+ if (*radio == NULL && mode_mask == T_RADIO)
+ *radio = pos;
+ /* Note: currently TDA9887 is the only demod-only
+ device. If other devices appear then we need to
+ make this test more general. */
+ else if (*tv == NULL && pos->type != TUNER_TDA9887 &&
+ (pos->mode_mask & T_ANALOG_TV))
+ *tv = pos;
+ }
+}
+
+/**
+ *tuner_probe - Probes the existing tuners on an I2C bus
+ *
+ * @client: i2c_client descriptor
+ * @id: not used
+ *
+ * This routine probes for tuners at the expected I2C addresses. On most
+ * cases, if a device answers to a given I2C address, it assumes that the
+ * device is a tuner. On a few cases, however, an additional logic is needed
+ * to double check if the device is really a tuner, or to identify the tuner
+ * type, like on tea5767/5761 devices.
+ *
+ * During client attach, set_type is called by adapter's attach_inform callback.
+ * set_type must then be completed by tuner_probe.
+ */
+static int tuner_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tuner *t;
+ struct tuner *radio;
+ struct tuner *tv;
+
+ t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
+ if (NULL == t)
+ return -ENOMEM;
+ v4l2_i2c_subdev_init(&t->sd, client, &tuner_ops);
+ t->i2c = client;
+ t->name = "(tuner unset)";
+ t->type = UNSET;
+ t->audmode = V4L2_TUNER_MODE_STEREO;
+ t->standby = 1;
+ t->radio_freq = 87.5 * 16000; /* Initial freq range */
+ t->tv_freq = 400 * 16; /* Sets freq to VHF High - needed for some PLL's to properly start */
+
+ if (show_i2c) {
+ unsigned char buffer[16];
+ int i, rc;
+
+ memset(buffer, 0, sizeof(buffer));
+ rc = i2c_master_recv(client, buffer, sizeof(buffer));
+ tuner_info("I2C RECV = ");
+ for (i = 0; i < rc; i++)
+ printk(KERN_CONT "%02x ", buffer[i]);
+ printk("\n");
+ }
+
+ /* autodetection code based on the i2c addr */
+ if (!no_autodetect) {
+ switch (client->addr) {
+ case 0x10:
+ if (tuner_symbol_probe(tea5761_autodetection,
+ t->i2c->adapter,
+ t->i2c->addr) >= 0) {
+ t->type = TUNER_TEA5761;
+ t->mode_mask = T_RADIO;
+ tuner_lookup(t->i2c->adapter, &radio, &tv);
+ if (tv)
+ tv->mode_mask &= ~T_RADIO;
+
+ goto register_client;
+ }
+ kfree(t);
+ return -ENODEV;
+ case 0x42:
+ case 0x43:
+ case 0x4a:
+ case 0x4b:
+ /* If chip is not tda8290, don't register.
+ since it can be tda9887*/
+ if (tuner_symbol_probe(tda829x_probe, t->i2c->adapter,
+ t->i2c->addr) >= 0) {
+ tuner_dbg("tda829x detected\n");
+ } else {
+ /* Default is being tda9887 */
+ t->type = TUNER_TDA9887;
+ t->mode_mask = T_RADIO | T_ANALOG_TV;
+ goto register_client;
+ }
+ break;
+ case 0x60:
+ if (tuner_symbol_probe(tea5767_autodetection,
+ t->i2c->adapter, t->i2c->addr)
+ >= 0) {
+ t->type = TUNER_TEA5767;
+ t->mode_mask = T_RADIO;
+ /* Sets freq to FM range */
+ tuner_lookup(t->i2c->adapter, &radio, &tv);
+ if (tv)
+ tv->mode_mask &= ~T_RADIO;
+
+ goto register_client;
+ }
+ break;
+ }
+ }
+
+ /* Initializes only the first TV tuner on this adapter. Why only the
+ first? Because there are some devices (notably the ones with TI
+ tuners) that have more than one i2c address for the *same* device.
+ Experience shows that, except for just one case, the first
+ address is the right one. The exception is a Russian tuner
+ (ACORP_Y878F). So, the desired behavior is just to enable the
+ first found TV tuner. */
+ tuner_lookup(t->i2c->adapter, &radio, &tv);
+ if (tv == NULL) {
+ t->mode_mask = T_ANALOG_TV;
+ if (radio == NULL)
+ t->mode_mask |= T_RADIO;
+ tuner_dbg("Setting mode_mask to 0x%02x\n", t->mode_mask);
+ }
+
+ /* Should be just before return */
+register_client:
+ /* Sets a default mode */
+ if (t->mode_mask & T_ANALOG_TV)
+ t->mode = V4L2_TUNER_ANALOG_TV;
+ else
+ t->mode = V4L2_TUNER_RADIO;
+ set_type(client, t->type, t->mode_mask, t->config, t->fe.callback);
+ list_add_tail(&t->list, &tuner_list);
+
+ tuner_info("Tuner %d found with type(s)%s%s.\n",
+ t->type,
+ t->mode_mask & T_RADIO ? " Radio" : "",
+ t->mode_mask & T_ANALOG_TV ? " TV" : "");
+ return 0;
+}
+
+/**
+ * tuner_remove - detaches a tuner
+ *
+ * @client: i2c_client descriptor
+ */
+
+static int tuner_remove(struct i2c_client *client)
+{
+ struct tuner *t = to_tuner(i2c_get_clientdata(client));
+
+ v4l2_device_unregister_subdev(&t->sd);
+ tuner_detach(&t->fe);
+ t->fe.analog_demod_priv = NULL;
+
+ list_del(&t->list);
+ kfree(t);
+ return 0;
+}
+
+/*
+ * Functions to switch between Radio and TV
+ *
+ * A few cards have a separate I2C tuner for radio. Those routines
+ * take care of switching between TV/Radio mode, filtering only the
+ * commands that apply to the Radio or TV tuner.
+ */
+
+/**
+ * check_mode - Verify if tuner supports the requested mode
+ * @t: a pointer to the module's internal struct_tuner
+ *
+ * This function checks if the tuner is capable of tuning analog TV,
+ * digital TV or radio, depending on what the caller wants. If the
+ * tuner can't support that mode, it returns -EINVAL. Otherwise, it
+ * returns 0.
+ * This function is needed for boards that have a separate tuner for
+ * radio (like devices with tea5767).
+ * NOTE: mt20xx uses V4L2_TUNER_DIGITAL_TV and calls set_tv_freq to
+ * select a TV frequency. So, t_mode = T_ANALOG_TV could actually
+ * be used to represent a Digital TV too.
+ */
+static inline int check_mode(struct tuner *t, enum v4l2_tuner_type mode)
+{
+ int t_mode;
+ if (mode == V4L2_TUNER_RADIO)
+ t_mode = T_RADIO;
+ else
+ t_mode = T_ANALOG_TV;
+
+ if ((t_mode & t->mode_mask) == 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * set_mode - Switch tuner to other mode.
+ * @t: a pointer to the module's internal struct_tuner
+ * @mode: enum v4l2_type (radio or TV)
+ *
+ * If tuner doesn't support the needed mode (radio or TV), prints a
+ * debug message and returns -EINVAL, changing its state to standby.
+ * Otherwise, changes the mode and returns 0.
+ */
+static int set_mode(struct tuner *t, enum v4l2_tuner_type mode)
+{
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+
+ if (mode != t->mode) {
+ if (check_mode(t, mode) == -EINVAL) {
+ tuner_dbg("Tuner doesn't support mode %d. "
+ "Putting tuner to sleep\n", mode);
+ t->standby = true;
+ if (analog_ops->standby)
+ analog_ops->standby(&t->fe);
+ return -EINVAL;
+ }
+ t->mode = mode;
+ tuner_dbg("Changing to mode %d\n", mode);
+ }
+ return 0;
+}
+
+/**
+ * set_freq - Set the tuner to the desired frequency.
+ * @t: a pointer to the module's internal struct_tuner
+ * @freq: frequency to set (0 means to use the current frequency)
+ */
+static void set_freq(struct tuner *t, unsigned int freq)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&t->sd);
+
+ if (t->mode == V4L2_TUNER_RADIO) {
+ if (!freq)
+ freq = t->radio_freq;
+ set_radio_freq(client, freq);
+ } else {
+ if (!freq)
+ freq = t->tv_freq;
+ set_tv_freq(client, freq);
+ }
+}
+
+/*
+ * Functions that are specific for TV mode
+ */
+
+/**
+ * set_tv_freq - Set tuner frequency, freq in Units of 62.5 kHz = 1/16MHz
+ *
+ * @c: i2c_client descriptor
+ * @freq: frequency
+ */
+static void set_tv_freq(struct i2c_client *c, unsigned int freq)
+{
+ struct tuner *t = to_tuner(i2c_get_clientdata(c));
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+
+ struct analog_parameters params = {
+ .mode = t->mode,
+ .audmode = t->audmode,
+ .std = t->std
+ };
+
+ if (t->type == UNSET) {
+ tuner_warn("tuner type not set\n");
+ return;
+ }
+ if (NULL == analog_ops->set_params) {
+ tuner_warn("Tuner has no way to set tv freq\n");
+ return;
+ }
+ if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) {
+ tuner_dbg("TV freq (%d.%02d) out of range (%d-%d)\n",
+ freq / 16, freq % 16 * 100 / 16, tv_range[0],
+ tv_range[1]);
+ /* V4L2 spec: if the freq is not possible then the closest
+ possible value should be selected */
+ if (freq < tv_range[0] * 16)
+ freq = tv_range[0] * 16;
+ else
+ freq = tv_range[1] * 16;
+ }
+ params.frequency = freq;
+ tuner_dbg("tv freq set to %d.%02d\n",
+ freq / 16, freq % 16 * 100 / 16);
+ t->tv_freq = freq;
+ t->standby = false;
+
+ analog_ops->set_params(&t->fe, &params);
+}
+
+/**
+ * tuner_fixup_std - force a given video standard variant
+ *
+ * @t: tuner internal struct
+ * @std: TV standard
+ *
+ * A few devices or drivers have problem to detect some standard variations.
+ * On other operational systems, the drivers generally have a per-country
+ * code, and some logic to apply per-country hacks. V4L2 API doesn't provide
+ * such hacks. Instead, it relies on a proper video standard selection from
+ * the userspace application. However, as some apps are buggy, not allowing
+ * to distinguish all video standard variations, a modprobe parameter can
+ * be used to force a video standard match.
+ */
+static v4l2_std_id tuner_fixup_std(struct tuner *t, v4l2_std_id std)
+{
+ if (pal[0] != '-' && (std & V4L2_STD_PAL) == V4L2_STD_PAL) {
+ switch (pal[0]) {
+ case '6':
+ return V4L2_STD_PAL_60;
+ case 'b':
+ case 'B':
+ case 'g':
+ case 'G':
+ return V4L2_STD_PAL_BG;
+ case 'i':
+ case 'I':
+ return V4L2_STD_PAL_I;
+ case 'd':
+ case 'D':
+ case 'k':
+ case 'K':
+ return V4L2_STD_PAL_DK;
+ case 'M':
+ case 'm':
+ return V4L2_STD_PAL_M;
+ case 'N':
+ case 'n':
+ if (pal[1] == 'c' || pal[1] == 'C')
+ return V4L2_STD_PAL_Nc;
+ return V4L2_STD_PAL_N;
+ default:
+ tuner_warn("pal= argument not recognised\n");
+ break;
+ }
+ }
+ if (secam[0] != '-' && (std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
+ switch (secam[0]) {
+ case 'b':
+ case 'B':
+ case 'g':
+ case 'G':
+ case 'h':
+ case 'H':
+ return V4L2_STD_SECAM_B |
+ V4L2_STD_SECAM_G |
+ V4L2_STD_SECAM_H;
+ case 'd':
+ case 'D':
+ case 'k':
+ case 'K':
+ return V4L2_STD_SECAM_DK;
+ case 'l':
+ case 'L':
+ if ((secam[1] == 'C') || (secam[1] == 'c'))
+ return V4L2_STD_SECAM_LC;
+ return V4L2_STD_SECAM_L;
+ default:
+ tuner_warn("secam= argument not recognised\n");
+ break;
+ }
+ }
+
+ if (ntsc[0] != '-' && (std & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
+ switch (ntsc[0]) {
+ case 'm':
+ case 'M':
+ return V4L2_STD_NTSC_M;
+ case 'j':
+ case 'J':
+ return V4L2_STD_NTSC_M_JP;
+ case 'k':
+ case 'K':
+ return V4L2_STD_NTSC_M_KR;
+ default:
+ tuner_info("ntsc= argument not recognised\n");
+ break;
+ }
+ }
+ return std;
+}
+
+/*
+ * Functions that are specific for Radio mode
+ */
+
+/**
+ * set_radio_freq - Set tuner frequency, freq in Units of 62.5 Hz = 1/16kHz
+ *
+ * @c: i2c_client descriptor
+ * @freq: frequency
+ */
+static void set_radio_freq(struct i2c_client *c, unsigned int freq)
+{
+ struct tuner *t = to_tuner(i2c_get_clientdata(c));
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+
+ struct analog_parameters params = {
+ .mode = t->mode,
+ .audmode = t->audmode,
+ .std = t->std
+ };
+
+ if (t->type == UNSET) {
+ tuner_warn("tuner type not set\n");
+ return;
+ }
+ if (NULL == analog_ops->set_params) {
+ tuner_warn("tuner has no way to set radio frequency\n");
+ return;
+ }
+ if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) {
+ tuner_dbg("radio freq (%d.%02d) out of range (%d-%d)\n",
+ freq / 16000, freq % 16000 * 100 / 16000,
+ radio_range[0], radio_range[1]);
+ /* V4L2 spec: if the freq is not possible then the closest
+ possible value should be selected */
+ if (freq < radio_range[0] * 16000)
+ freq = radio_range[0] * 16000;
+ else
+ freq = radio_range[1] * 16000;
+ }
+ params.frequency = freq;
+ tuner_dbg("radio freq set to %d.%02d\n",
+ freq / 16000, freq % 16000 * 100 / 16000);
+ t->radio_freq = freq;
+ t->standby = false;
+
+ analog_ops->set_params(&t->fe, &params);
+}
+
+/*
+ * Debug function for reporting tuner status to userspace
+ */
+
+/**
+ * tuner_status - Dumps the current tuner status at dmesg
+ * @fe: pointer to struct dvb_frontend
+ *
+ * This callback is used only for driver debug purposes, answering to
+ * VIDIOC_LOG_STATUS. No changes should happen on this call.
+ */
+static void tuner_status(struct dvb_frontend *fe)
+{
+ struct tuner *t = fe->analog_demod_priv;
+ unsigned long freq, freq_fraction;
+ struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
+ struct analog_demod_ops *analog_ops = &fe->ops.analog_ops;
+ const char *p;
+
+ switch (t->mode) {
+ case V4L2_TUNER_RADIO:
+ p = "radio";
+ break;
+ case V4L2_TUNER_DIGITAL_TV: /* Used by mt20xx */
+ p = "digital TV";
+ break;
+ case V4L2_TUNER_ANALOG_TV:
+ default:
+ p = "analog TV";
+ break;
+ }
+ if (t->mode == V4L2_TUNER_RADIO) {
+ freq = t->radio_freq / 16000;
+ freq_fraction = (t->radio_freq % 16000) * 100 / 16000;
+ } else {
+ freq = t->tv_freq / 16;
+ freq_fraction = (t->tv_freq % 16) * 100 / 16;
+ }
+ tuner_info("Tuner mode: %s%s\n", p,
+ t->standby ? " on standby mode" : "");
+ tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction);
+ tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std);
+ if (t->mode != V4L2_TUNER_RADIO)
+ return;
+ if (fe_tuner_ops->get_status) {
+ u32 tuner_status;
+
+ fe_tuner_ops->get_status(&t->fe, &tuner_status);
+ if (tuner_status & TUNER_STATUS_LOCKED)
+ tuner_info("Tuner is locked.\n");
+ if (tuner_status & TUNER_STATUS_STEREO)
+ tuner_info("Stereo: yes\n");
+ }
+ if (analog_ops->has_signal)
+ tuner_info("Signal strength: %d\n",
+ analog_ops->has_signal(fe));
+}
+
+/*
+ * Function to splicitly change mode to radio. Probably not needed anymore
+ */
+
+static int tuner_s_radio(struct v4l2_subdev *sd)
+{
+ struct tuner *t = to_tuner(sd);
+
+ if (set_mode(t, V4L2_TUNER_RADIO) == 0)
+ set_freq(t, 0);
+ return 0;
+}
+
+/*
+ * Tuner callbacks to handle userspace ioctl's
+ */
+
+/**
+ * tuner_s_power - controls the power state of the tuner
+ * @sd: pointer to struct v4l2_subdev
+ * @on: a zero value puts the tuner to sleep, non-zero wakes it up
+ */
+static int tuner_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct tuner *t = to_tuner(sd);
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+
+ if (on) {
+ if (t->standby && set_mode(t, t->mode) == 0) {
+ tuner_dbg("Waking up tuner\n");
+ set_freq(t, 0);
+ }
+ return 0;
+ }
+
+ tuner_dbg("Putting tuner to sleep\n");
+ t->standby = true;
+ if (analog_ops->standby)
+ analog_ops->standby(&t->fe);
+ return 0;
+}
+
+static int tuner_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct tuner *t = to_tuner(sd);
+
+ if (set_mode(t, V4L2_TUNER_ANALOG_TV))
+ return 0;
+
+ t->std = tuner_fixup_std(t, std);
+ if (t->std != std)
+ tuner_dbg("Fixup standard %llx to %llx\n", std, t->std);
+ set_freq(t, 0);
+ return 0;
+}
+
+static int tuner_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
+{
+ struct tuner *t = to_tuner(sd);
+
+ if (set_mode(t, f->type) == 0)
+ set_freq(t, f->frequency);
+ return 0;
+}
+
+/**
+ * tuner_g_frequency - Get the tuned frequency for the tuner
+ * @sd: pointer to struct v4l2_subdev
+ * @f: pointer to struct v4l2_frequency
+ *
+ * At return, the structure f will be filled with tuner frequency
+ * if the tuner matches the f->type.
+ * Note: f->type should be initialized before calling it.
+ * This is done by either video_ioctl2 or by the bridge driver.
+ */
+static int tuner_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
+{
+ struct tuner *t = to_tuner(sd);
+ struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
+
+ if (check_mode(t, f->type) == -EINVAL)
+ return 0;
+ if (f->type == t->mode && fe_tuner_ops->get_frequency && !t->standby) {
+ u32 abs_freq;
+
+ fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
+ f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
+ DIV_ROUND_CLOSEST(abs_freq * 2, 125) :
+ DIV_ROUND_CLOSEST(abs_freq, 62500);
+ } else {
+ f->frequency = (V4L2_TUNER_RADIO == f->type) ?
+ t->radio_freq : t->tv_freq;
+ }
+ return 0;
+}
+
+/**
+ * tuner_g_tuner - Fill in tuner information
+ * @sd: pointer to struct v4l2_subdev
+ * @vt: pointer to struct v4l2_tuner
+ *
+ * At return, the structure vt will be filled with tuner information
+ * if the tuner matches vt->type.
+ * Note: vt->type should be initialized before calling it.
+ * This is done by either video_ioctl2 or by the bridge driver.
+ */
+static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct tuner *t = to_tuner(sd);
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+ struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
+
+ if (check_mode(t, vt->type) == -EINVAL)
+ return 0;
+ if (vt->type == t->mode && analog_ops->get_afc)
+ vt->afc = analog_ops->get_afc(&t->fe);
+ if (t->mode != V4L2_TUNER_RADIO) {
+ vt->capability |= V4L2_TUNER_CAP_NORM;
+ vt->rangelow = tv_range[0] * 16;
+ vt->rangehigh = tv_range[1] * 16;
+ return 0;
+ }
+
+ /* radio mode */
+ if (vt->type == t->mode) {
+ vt->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ if (fe_tuner_ops->get_status) {
+ u32 tuner_status;
+
+ fe_tuner_ops->get_status(&t->fe, &tuner_status);
+ vt->rxsubchans =
+ (tuner_status & TUNER_STATUS_STEREO) ?
+ V4L2_TUNER_SUB_STEREO :
+ V4L2_TUNER_SUB_MONO;
+ }
+ if (analog_ops->has_signal)
+ vt->signal = analog_ops->has_signal(&t->fe);
+ vt->audmode = t->audmode;
+ }
+ vt->capability |= V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
+ vt->rangelow = radio_range[0] * 16000;
+ vt->rangehigh = radio_range[1] * 16000;
+
+ return 0;
+}
+
+/**
+ * tuner_s_tuner - Set the tuner's audio mode
+ * @sd: pointer to struct v4l2_subdev
+ * @vt: pointer to struct v4l2_tuner
+ *
+ * Sets the audio mode if the tuner matches vt->type.
+ * Note: vt->type should be initialized before calling it.
+ * This is done by either video_ioctl2 or by the bridge driver.
+ */
+static int tuner_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct tuner *t = to_tuner(sd);
+
+ if (set_mode(t, vt->type))
+ return 0;
+
+ if (t->mode == V4L2_TUNER_RADIO)
+ t->audmode = vt->audmode;
+ set_freq(t, 0);
+
+ return 0;
+}
+
+static int tuner_log_status(struct v4l2_subdev *sd)
+{
+ struct tuner *t = to_tuner(sd);
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+
+ if (analog_ops->tuner_status)
+ analog_ops->tuner_status(&t->fe);
+ return 0;
+}
+
+static int tuner_suspend(struct i2c_client *c, pm_message_t state)
+{
+ struct tuner *t = to_tuner(i2c_get_clientdata(c));
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+
+ tuner_dbg("suspend\n");
+
+ if (!t->standby && analog_ops->standby)
+ analog_ops->standby(&t->fe);
+
+ return 0;
+}
+
+static int tuner_resume(struct i2c_client *c)
+{
+ struct tuner *t = to_tuner(i2c_get_clientdata(c));
+
+ tuner_dbg("resume\n");
+
+ if (!t->standby)
+ if (set_mode(t, t->mode) == 0)
+ set_freq(t, 0);
+
+ return 0;
+}
+
+static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ /* TUNER_SET_CONFIG is still called by tuner-simple.c, so we have
+ to handle it here.
+ There must be a better way of doing this... */
+ switch (cmd) {
+ case TUNER_SET_CONFIG:
+ return tuner_s_config(sd, arg);
+ }
+ return -ENOIOCTLCMD;
+}
+
+/*
+ * Callback structs
+ */
+
+static const struct v4l2_subdev_core_ops tuner_core_ops = {
+ .log_status = tuner_log_status,
+ .s_std = tuner_s_std,
+ .s_power = tuner_s_power,
+};
+
+static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = {
+ .s_radio = tuner_s_radio,
+ .g_tuner = tuner_g_tuner,
+ .s_tuner = tuner_s_tuner,
+ .s_frequency = tuner_s_frequency,
+ .g_frequency = tuner_g_frequency,
+ .s_type_addr = tuner_s_type_addr,
+ .s_config = tuner_s_config,
+};
+
+static const struct v4l2_subdev_ops tuner_ops = {
+ .core = &tuner_core_ops,
+ .tuner = &tuner_tuner_ops,
+};
+
+/*
+ * I2C structs and module init functions
+ */
+
+static const struct i2c_device_id tuner_id[] = {
+ { "tuner", }, /* autodetect */
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tuner_id);
+
+static struct i2c_driver tuner_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "tuner",
+ },
+ .probe = tuner_probe,
+ .remove = tuner_remove,
+ .command = tuner_command,
+ .suspend = tuner_suspend,
+ .resume = tuner_resume,
+ .id_table = tuner_id,
+};
+
+module_i2c_driver(tuner_driver);
+
+MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
+MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c
new file mode 100644
index 00000000..c5b1a736
--- /dev/null
+++ b/drivers/media/video/tvaudio.c
@@ -0,0 +1,2081 @@
+/*
+ * Driver for simple i2c audio chips.
+ *
+ * Copyright (c) 2000 Gerd Knorr
+ * based on code by:
+ * Eric Sandeen (eric_sandeen@bigfoot.com)
+ * Steve VanDeBogart (vandebo@uclink.berkeley.edu)
+ * Greg Alexander (galexand@acm.org)
+ *
+ * Copyright(c) 2005-2008 Mauro Carvalho Chehab
+ * - Some cleanups, code fixes, etc
+ * - Convert it to V4L2 API
+ *
+ * This code is placed under the terms of the GNU General Public License
+ *
+ * OPTIONS:
+ * debug - set to 1 if you'd like to see debug messages
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+
+#include <media/tvaudio.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+
+#include <media/i2c-addr.h>
+
+/* ---------------------------------------------------------------------- */
+/* insmod args */
+
+static int debug; /* insmod parameter */
+module_param(debug, int, 0644);
+
+MODULE_DESCRIPTION("device driver for various i2c TV sound decoder / audiomux chips");
+MODULE_AUTHOR("Eric Sandeen, Steve VanDeBogart, Greg Alexander, Gerd Knorr");
+MODULE_LICENSE("GPL");
+
+#define UNSET (-1U)
+
+/* ---------------------------------------------------------------------- */
+/* our structs */
+
+#define MAXREGS 256
+
+struct CHIPSTATE;
+typedef int (*getvalue)(int);
+typedef int (*checkit)(struct CHIPSTATE*);
+typedef int (*initialize)(struct CHIPSTATE*);
+typedef int (*getmode)(struct CHIPSTATE*);
+typedef void (*setmode)(struct CHIPSTATE*, int mode);
+
+/* i2c command */
+typedef struct AUDIOCMD {
+ int count; /* # of bytes to send */
+ unsigned char bytes[MAXREGS+1]; /* addr, data, data, ... */
+} audiocmd;
+
+/* chip description */
+struct CHIPDESC {
+ char *name; /* chip name */
+ int addr_lo, addr_hi; /* i2c address range */
+ int registers; /* # of registers */
+
+ int *insmodopt;
+ checkit checkit;
+ initialize initialize;
+ int flags;
+#define CHIP_HAS_VOLUME 1
+#define CHIP_HAS_BASSTREBLE 2
+#define CHIP_HAS_INPUTSEL 4
+#define CHIP_NEED_CHECKMODE 8
+
+ /* various i2c command sequences */
+ audiocmd init;
+
+ /* which register has which value */
+ int leftreg,rightreg,treblereg,bassreg;
+
+ /* initialize with (defaults to 65535/65535/32768/32768 */
+ int leftinit,rightinit,trebleinit,bassinit;
+
+ /* functions to convert the values (v4l -> chip) */
+ getvalue volfunc,treblefunc,bassfunc;
+
+ /* get/set mode */
+ getmode getmode;
+ setmode setmode;
+
+ /* input switch register + values for v4l inputs */
+ int inputreg;
+ int inputmap[4];
+ int inputmute;
+ int inputmask;
+};
+
+/* current state of the chip */
+struct CHIPSTATE {
+ struct v4l2_subdev sd;
+
+ /* chip-specific description - should point to
+ an entry at CHIPDESC table */
+ struct CHIPDESC *desc;
+
+ /* shadow register set */
+ audiocmd shadow;
+
+ /* current settings */
+ __u16 left,right,treble,bass,muted,mode;
+ int prevmode;
+ int radio;
+ int input;
+
+ /* thread */
+ struct task_struct *thread;
+ struct timer_list wt;
+ int watch_stereo;
+ int audmode;
+};
+
+static inline struct CHIPSTATE *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct CHIPSTATE, sd);
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* i2c I/O functions */
+
+static int chip_write(struct CHIPSTATE *chip, int subaddr, int val)
+{
+ struct v4l2_subdev *sd = &chip->sd;
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
+ unsigned char buffer[2];
+
+ if (subaddr < 0) {
+ v4l2_dbg(1, debug, sd, "chip_write: 0x%x\n", val);
+ chip->shadow.bytes[1] = val;
+ buffer[0] = val;
+ if (1 != i2c_master_send(c, buffer, 1)) {
+ v4l2_warn(sd, "I/O error (write 0x%x)\n", val);
+ return -1;
+ }
+ } else {
+ if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) {
+ v4l2_info(sd,
+ "Tried to access a non-existent register: %d\n",
+ subaddr);
+ return -EINVAL;
+ }
+
+ v4l2_dbg(1, debug, sd, "chip_write: reg%d=0x%x\n",
+ subaddr, val);
+ chip->shadow.bytes[subaddr+1] = val;
+ buffer[0] = subaddr;
+ buffer[1] = val;
+ if (2 != i2c_master_send(c, buffer, 2)) {
+ v4l2_warn(sd, "I/O error (write reg%d=0x%x)\n",
+ subaddr, val);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int chip_write_masked(struct CHIPSTATE *chip,
+ int subaddr, int val, int mask)
+{
+ struct v4l2_subdev *sd = &chip->sd;
+
+ if (mask != 0) {
+ if (subaddr < 0) {
+ val = (chip->shadow.bytes[1] & ~mask) | (val & mask);
+ } else {
+ if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) {
+ v4l2_info(sd,
+ "Tried to access a non-existent register: %d\n",
+ subaddr);
+ return -EINVAL;
+ }
+
+ val = (chip->shadow.bytes[subaddr+1] & ~mask) | (val & mask);
+ }
+ }
+ return chip_write(chip, subaddr, val);
+}
+
+static int chip_read(struct CHIPSTATE *chip)
+{
+ struct v4l2_subdev *sd = &chip->sd;
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
+ unsigned char buffer;
+
+ if (1 != i2c_master_recv(c, &buffer, 1)) {
+ v4l2_warn(sd, "I/O error (read)\n");
+ return -1;
+ }
+ v4l2_dbg(1, debug, sd, "chip_read: 0x%x\n", buffer);
+ return buffer;
+}
+
+static int chip_read2(struct CHIPSTATE *chip, int subaddr)
+{
+ struct v4l2_subdev *sd = &chip->sd;
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
+ unsigned char write[1];
+ unsigned char read[1];
+ struct i2c_msg msgs[2] = {
+ { c->addr, 0, 1, write },
+ { c->addr, I2C_M_RD, 1, read }
+ };
+
+ write[0] = subaddr;
+
+ if (2 != i2c_transfer(c->adapter, msgs, 2)) {
+ v4l2_warn(sd, "I/O error (read2)\n");
+ return -1;
+ }
+ v4l2_dbg(1, debug, sd, "chip_read2: reg%d=0x%x\n",
+ subaddr, read[0]);
+ return read[0];
+}
+
+static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd)
+{
+ struct v4l2_subdev *sd = &chip->sd;
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
+ int i;
+
+ if (0 == cmd->count)
+ return 0;
+
+ if (cmd->count + cmd->bytes[0] - 1 >= ARRAY_SIZE(chip->shadow.bytes)) {
+ v4l2_info(sd,
+ "Tried to access a non-existent register range: %d to %d\n",
+ cmd->bytes[0] + 1, cmd->bytes[0] + cmd->count - 1);
+ return -EINVAL;
+ }
+
+ /* FIXME: it seems that the shadow bytes are wrong bellow !*/
+
+ /* update our shadow register set; print bytes if (debug > 0) */
+ v4l2_dbg(1, debug, sd, "chip_cmd(%s): reg=%d, data:",
+ name, cmd->bytes[0]);
+ for (i = 1; i < cmd->count; i++) {
+ if (debug)
+ printk(KERN_CONT " 0x%x", cmd->bytes[i]);
+ chip->shadow.bytes[i+cmd->bytes[0]] = cmd->bytes[i];
+ }
+ if (debug)
+ printk(KERN_CONT "\n");
+
+ /* send data to the chip */
+ if (cmd->count != i2c_master_send(c, cmd->bytes, cmd->count)) {
+ v4l2_warn(sd, "I/O error (%s)\n", name);
+ return -1;
+ }
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+/* kernel thread for doing i2c stuff asyncronly
+ * right now it is used only to check the audio mode (mono/stereo/whatever)
+ * some time after switching to another TV channel, then turn on stereo
+ * if available, ...
+ */
+
+static void chip_thread_wake(unsigned long data)
+{
+ struct CHIPSTATE *chip = (struct CHIPSTATE*)data;
+ wake_up_process(chip->thread);
+}
+
+static int chip_thread(void *data)
+{
+ struct CHIPSTATE *chip = data;
+ struct CHIPDESC *desc = chip->desc;
+ struct v4l2_subdev *sd = &chip->sd;
+ int mode;
+
+ v4l2_dbg(1, debug, sd, "thread started\n");
+ set_freezable();
+ for (;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (!kthread_should_stop())
+ schedule();
+ set_current_state(TASK_RUNNING);
+ try_to_freeze();
+ if (kthread_should_stop())
+ break;
+ v4l2_dbg(1, debug, sd, "thread wakeup\n");
+
+ /* don't do anything for radio or if mode != auto */
+ if (chip->radio || chip->mode != 0)
+ continue;
+
+ /* have a look what's going on */
+ mode = desc->getmode(chip);
+ if (mode == chip->prevmode)
+ continue;
+
+ /* chip detected a new audio mode - set it */
+ v4l2_dbg(1, debug, sd, "thread checkmode\n");
+
+ chip->prevmode = mode;
+
+ if (mode & V4L2_TUNER_MODE_STEREO)
+ desc->setmode(chip, V4L2_TUNER_MODE_STEREO);
+ if (mode & V4L2_TUNER_MODE_LANG1_LANG2)
+ desc->setmode(chip, V4L2_TUNER_MODE_STEREO);
+ else if (mode & V4L2_TUNER_MODE_LANG1)
+ desc->setmode(chip, V4L2_TUNER_MODE_LANG1);
+ else if (mode & V4L2_TUNER_MODE_LANG2)
+ desc->setmode(chip, V4L2_TUNER_MODE_LANG2);
+ else
+ desc->setmode(chip, V4L2_TUNER_MODE_MONO);
+
+ /* schedule next check */
+ mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000));
+ }
+
+ v4l2_dbg(1, debug, sd, "thread exiting\n");
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+/* audio chip descriptions - defines+functions for tda9840 */
+
+#define TDA9840_SW 0x00
+#define TDA9840_LVADJ 0x02
+#define TDA9840_STADJ 0x03
+#define TDA9840_TEST 0x04
+
+#define TDA9840_MONO 0x10
+#define TDA9840_STEREO 0x2a
+#define TDA9840_DUALA 0x12
+#define TDA9840_DUALB 0x1e
+#define TDA9840_DUALAB 0x1a
+#define TDA9840_DUALBA 0x16
+#define TDA9840_EXTERNAL 0x7a
+
+#define TDA9840_DS_DUAL 0x20 /* Dual sound identified */
+#define TDA9840_ST_STEREO 0x40 /* Stereo sound identified */
+#define TDA9840_PONRES 0x80 /* Power-on reset detected if = 1 */
+
+#define TDA9840_TEST_INT1SN 0x1 /* Integration time 0.5s when set */
+#define TDA9840_TEST_INTFU 0x02 /* Disables integrator function */
+
+static int tda9840_getmode(struct CHIPSTATE *chip)
+{
+ struct v4l2_subdev *sd = &chip->sd;
+ int val, mode;
+
+ val = chip_read(chip);
+ mode = V4L2_TUNER_MODE_MONO;
+ if (val & TDA9840_DS_DUAL)
+ mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ if (val & TDA9840_ST_STEREO)
+ mode |= V4L2_TUNER_MODE_STEREO;
+
+ v4l2_dbg(1, debug, sd, "tda9840_getmode(): raw chip read: %d, return: %d\n",
+ val, mode);
+ return mode;
+}
+
+static void tda9840_setmode(struct CHIPSTATE *chip, int mode)
+{
+ int update = 1;
+ int t = chip->shadow.bytes[TDA9840_SW + 1] & ~0x7e;
+
+ switch (mode) {
+ case V4L2_TUNER_MODE_MONO:
+ t |= TDA9840_MONO;
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ t |= TDA9840_STEREO;
+ break;
+ case V4L2_TUNER_MODE_LANG1:
+ t |= TDA9840_DUALA;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ t |= TDA9840_DUALB;
+ break;
+ default:
+ update = 0;
+ }
+
+ if (update)
+ chip_write(chip, TDA9840_SW, t);
+}
+
+static int tda9840_checkit(struct CHIPSTATE *chip)
+{
+ int rc;
+ rc = chip_read(chip);
+ /* lower 5 bits should be 0 */
+ return ((rc & 0x1f) == 0) ? 1 : 0;
+}
+
+/* ---------------------------------------------------------------------- */
+/* audio chip descriptions - defines+functions for tda985x */
+
+/* subaddresses for TDA9855 */
+#define TDA9855_VR 0x00 /* Volume, right */
+#define TDA9855_VL 0x01 /* Volume, left */
+#define TDA9855_BA 0x02 /* Bass */
+#define TDA9855_TR 0x03 /* Treble */
+#define TDA9855_SW 0x04 /* Subwoofer - not connected on DTV2000 */
+
+/* subaddresses for TDA9850 */
+#define TDA9850_C4 0x04 /* Control 1 for TDA9850 */
+
+/* subaddesses for both chips */
+#define TDA985x_C5 0x05 /* Control 2 for TDA9850, Control 1 for TDA9855 */
+#define TDA985x_C6 0x06 /* Control 3 for TDA9850, Control 2 for TDA9855 */
+#define TDA985x_C7 0x07 /* Control 4 for TDA9850, Control 3 for TDA9855 */
+#define TDA985x_A1 0x08 /* Alignment 1 for both chips */
+#define TDA985x_A2 0x09 /* Alignment 2 for both chips */
+#define TDA985x_A3 0x0a /* Alignment 3 for both chips */
+
+/* Masks for bits in TDA9855 subaddresses */
+/* 0x00 - VR in TDA9855 */
+/* 0x01 - VL in TDA9855 */
+/* lower 7 bits control gain from -71dB (0x28) to 16dB (0x7f)
+ * in 1dB steps - mute is 0x27 */
+
+
+/* 0x02 - BA in TDA9855 */
+/* lower 5 bits control bass gain from -12dB (0x06) to 16.5dB (0x19)
+ * in .5dB steps - 0 is 0x0E */
+
+
+/* 0x03 - TR in TDA9855 */
+/* 4 bits << 1 control treble gain from -12dB (0x3) to 12dB (0xb)
+ * in 3dB steps - 0 is 0x7 */
+
+/* Masks for bits in both chips' subaddresses */
+/* 0x04 - SW in TDA9855, C4/Control 1 in TDA9850 */
+/* Unique to TDA9855: */
+/* 4 bits << 2 control subwoofer/surround gain from -14db (0x1) to 14db (0xf)
+ * in 3dB steps - mute is 0x0 */
+
+/* Unique to TDA9850: */
+/* lower 4 bits control stereo noise threshold, over which stereo turns off
+ * set to values of 0x00 through 0x0f for Ster1 through Ster16 */
+
+
+/* 0x05 - C5 - Control 1 in TDA9855 , Control 2 in TDA9850*/
+/* Unique to TDA9855: */
+#define TDA9855_MUTE 1<<7 /* GMU, Mute at outputs */
+#define TDA9855_AVL 1<<6 /* AVL, Automatic Volume Level */
+#define TDA9855_LOUD 1<<5 /* Loudness, 1==off */
+#define TDA9855_SUR 1<<3 /* Surround / Subwoofer 1==.5(L-R) 0==.5(L+R) */
+ /* Bits 0 to 3 select various combinations
+ * of line in and line out, only the
+ * interesting ones are defined */
+#define TDA9855_EXT 1<<2 /* Selects inputs LIR and LIL. Pins 41 & 12 */
+#define TDA9855_INT 0 /* Selects inputs LOR and LOL. (internal) */
+
+/* Unique to TDA9850: */
+/* lower 4 bits contol SAP noise threshold, over which SAP turns off
+ * set to values of 0x00 through 0x0f for SAP1 through SAP16 */
+
+
+/* 0x06 - C6 - Control 2 in TDA9855, Control 3 in TDA9850 */
+/* Common to TDA9855 and TDA9850: */
+#define TDA985x_SAP 3<<6 /* Selects SAP output, mute if not received */
+#define TDA985x_STEREO 1<<6 /* Selects Stereo ouput, mono if not received */
+#define TDA985x_MONO 0 /* Forces Mono output */
+#define TDA985x_LMU 1<<3 /* Mute (LOR/LOL for 9855, OUTL/OUTR for 9850) */
+
+/* Unique to TDA9855: */
+#define TDA9855_TZCM 1<<5 /* If set, don't mute till zero crossing */
+#define TDA9855_VZCM 1<<4 /* If set, don't change volume till zero crossing*/
+#define TDA9855_LINEAR 0 /* Linear Stereo */
+#define TDA9855_PSEUDO 1 /* Pseudo Stereo */
+#define TDA9855_SPAT_30 2 /* Spatial Stereo, 30% anti-phase crosstalk */
+#define TDA9855_SPAT_50 3 /* Spatial Stereo, 52% anti-phase crosstalk */
+#define TDA9855_E_MONO 7 /* Forced mono - mono select elseware, so useless*/
+
+/* 0x07 - C7 - Control 3 in TDA9855, Control 4 in TDA9850 */
+/* Common to both TDA9855 and TDA9850: */
+/* lower 4 bits control input gain from -3.5dB (0x0) to 4dB (0xF)
+ * in .5dB steps - 0dB is 0x7 */
+
+/* 0x08, 0x09 - A1 and A2 (read/write) */
+/* Common to both TDA9855 and TDA9850: */
+/* lower 5 bites are wideband and spectral expander alignment
+ * from 0x00 to 0x1f - nominal at 0x0f and 0x10 (read/write) */
+#define TDA985x_STP 1<<5 /* Stereo Pilot/detect (read-only) */
+#define TDA985x_SAPP 1<<6 /* SAP Pilot/detect (read-only) */
+#define TDA985x_STS 1<<7 /* Stereo trigger 1= <35mV 0= <30mV (write-only)*/
+
+/* 0x0a - A3 */
+/* Common to both TDA9855 and TDA9850: */
+/* lower 3 bits control timing current for alignment: -30% (0x0), -20% (0x1),
+ * -10% (0x2), nominal (0x3), +10% (0x6), +20% (0x5), +30% (0x4) */
+#define TDA985x_ADJ 1<<7 /* Stereo adjust on/off (wideband and spectral */
+
+static int tda9855_volume(int val) { return val/0x2e8+0x27; }
+static int tda9855_bass(int val) { return val/0xccc+0x06; }
+static int tda9855_treble(int val) { return (val/0x1c71+0x3)<<1; }
+
+static int tda985x_getmode(struct CHIPSTATE *chip)
+{
+ int mode;
+
+ mode = ((TDA985x_STP | TDA985x_SAPP) &
+ chip_read(chip)) >> 4;
+ /* Add mono mode regardless of SAP and stereo */
+ /* Allows forced mono */
+ return mode | V4L2_TUNER_MODE_MONO;
+}
+
+static void tda985x_setmode(struct CHIPSTATE *chip, int mode)
+{
+ int update = 1;
+ int c6 = chip->shadow.bytes[TDA985x_C6+1] & 0x3f;
+
+ switch (mode) {
+ case V4L2_TUNER_MODE_MONO:
+ c6 |= TDA985x_MONO;
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ c6 |= TDA985x_STEREO;
+ break;
+ case V4L2_TUNER_MODE_LANG1:
+ c6 |= TDA985x_SAP;
+ break;
+ default:
+ update = 0;
+ }
+ if (update)
+ chip_write(chip,TDA985x_C6,c6);
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* audio chip descriptions - defines+functions for tda9873h */
+
+/* Subaddresses for TDA9873H */
+
+#define TDA9873_SW 0x00 /* Switching */
+#define TDA9873_AD 0x01 /* Adjust */
+#define TDA9873_PT 0x02 /* Port */
+
+/* Subaddress 0x00: Switching Data
+ * B7..B0:
+ *
+ * B1, B0: Input source selection
+ * 0, 0 internal
+ * 1, 0 external stereo
+ * 0, 1 external mono
+ */
+#define TDA9873_INP_MASK 3
+#define TDA9873_INTERNAL 0
+#define TDA9873_EXT_STEREO 2
+#define TDA9873_EXT_MONO 1
+
+/* B3, B2: output signal select
+ * B4 : transmission mode
+ * 0, 0, 1 Mono
+ * 1, 0, 0 Stereo
+ * 1, 1, 1 Stereo (reversed channel)
+ * 0, 0, 0 Dual AB
+ * 0, 0, 1 Dual AA
+ * 0, 1, 0 Dual BB
+ * 0, 1, 1 Dual BA
+ */
+
+#define TDA9873_TR_MASK (7 << 2)
+#define TDA9873_TR_MONO 4
+#define TDA9873_TR_STEREO 1 << 4
+#define TDA9873_TR_REVERSE (1 << 3) & (1 << 2)
+#define TDA9873_TR_DUALA 1 << 2
+#define TDA9873_TR_DUALB 1 << 3
+
+/* output level controls
+ * B5: output level switch (0 = reduced gain, 1 = normal gain)
+ * B6: mute (1 = muted)
+ * B7: auto-mute (1 = auto-mute enabled)
+ */
+
+#define TDA9873_GAIN_NORMAL 1 << 5
+#define TDA9873_MUTE 1 << 6
+#define TDA9873_AUTOMUTE 1 << 7
+
+/* Subaddress 0x01: Adjust/standard */
+
+/* Lower 4 bits (C3..C0) control stereo adjustment on R channel (-0.6 - +0.7 dB)
+ * Recommended value is +0 dB
+ */
+
+#define TDA9873_STEREO_ADJ 0x06 /* 0dB gain */
+
+/* Bits C6..C4 control FM stantard
+ * C6, C5, C4
+ * 0, 0, 0 B/G (PAL FM)
+ * 0, 0, 1 M
+ * 0, 1, 0 D/K(1)
+ * 0, 1, 1 D/K(2)
+ * 1, 0, 0 D/K(3)
+ * 1, 0, 1 I
+ */
+#define TDA9873_BG 0
+#define TDA9873_M 1
+#define TDA9873_DK1 2
+#define TDA9873_DK2 3
+#define TDA9873_DK3 4
+#define TDA9873_I 5
+
+/* C7 controls identification response time (1=fast/0=normal)
+ */
+#define TDA9873_IDR_NORM 0
+#define TDA9873_IDR_FAST 1 << 7
+
+
+/* Subaddress 0x02: Port data */
+
+/* E1, E0 free programmable ports P1/P2
+ 0, 0 both ports low
+ 0, 1 P1 high
+ 1, 0 P2 high
+ 1, 1 both ports high
+*/
+
+#define TDA9873_PORTS 3
+
+/* E2: test port */
+#define TDA9873_TST_PORT 1 << 2
+
+/* E5..E3 control mono output channel (together with transmission mode bit B4)
+ *
+ * E5 E4 E3 B4 OUTM
+ * 0 0 0 0 mono
+ * 0 0 1 0 DUAL B
+ * 0 1 0 1 mono (from stereo decoder)
+ */
+#define TDA9873_MOUT_MONO 0
+#define TDA9873_MOUT_FMONO 0
+#define TDA9873_MOUT_DUALA 0
+#define TDA9873_MOUT_DUALB 1 << 3
+#define TDA9873_MOUT_ST 1 << 4
+#define TDA9873_MOUT_EXTM (1 << 4 ) & (1 << 3)
+#define TDA9873_MOUT_EXTL 1 << 5
+#define TDA9873_MOUT_EXTR (1 << 5 ) & (1 << 3)
+#define TDA9873_MOUT_EXTLR (1 << 5 ) & (1 << 4)
+#define TDA9873_MOUT_MUTE (1 << 5 ) & (1 << 4) & (1 << 3)
+
+/* Status bits: (chip read) */
+#define TDA9873_PONR 0 /* Power-on reset detected if = 1 */
+#define TDA9873_STEREO 2 /* Stereo sound is identified */
+#define TDA9873_DUAL 4 /* Dual sound is identified */
+
+static int tda9873_getmode(struct CHIPSTATE *chip)
+{
+ struct v4l2_subdev *sd = &chip->sd;
+ int val,mode;
+
+ val = chip_read(chip);
+ mode = V4L2_TUNER_MODE_MONO;
+ if (val & TDA9873_STEREO)
+ mode |= V4L2_TUNER_MODE_STEREO;
+ if (val & TDA9873_DUAL)
+ mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ v4l2_dbg(1, debug, sd, "tda9873_getmode(): raw chip read: %d, return: %d\n",
+ val, mode);
+ return mode;
+}
+
+static void tda9873_setmode(struct CHIPSTATE *chip, int mode)
+{
+ struct v4l2_subdev *sd = &chip->sd;
+ int sw_data = chip->shadow.bytes[TDA9873_SW+1] & ~ TDA9873_TR_MASK;
+ /* int adj_data = chip->shadow.bytes[TDA9873_AD+1] ; */
+
+ if ((sw_data & TDA9873_INP_MASK) != TDA9873_INTERNAL) {
+ v4l2_dbg(1, debug, sd, "tda9873_setmode(): external input\n");
+ return;
+ }
+
+ v4l2_dbg(1, debug, sd, "tda9873_setmode(): chip->shadow.bytes[%d] = %d\n", TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]);
+ v4l2_dbg(1, debug, sd, "tda9873_setmode(): sw_data = %d\n", sw_data);
+
+ switch (mode) {
+ case V4L2_TUNER_MODE_MONO:
+ sw_data |= TDA9873_TR_MONO;
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ sw_data |= TDA9873_TR_STEREO;
+ break;
+ case V4L2_TUNER_MODE_LANG1:
+ sw_data |= TDA9873_TR_DUALA;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ sw_data |= TDA9873_TR_DUALB;
+ break;
+ default:
+ chip->mode = 0;
+ return;
+ }
+
+ chip_write(chip, TDA9873_SW, sw_data);
+ v4l2_dbg(1, debug, sd, "tda9873_setmode(): req. mode %d; chip_write: %d\n",
+ mode, sw_data);
+}
+
+static int tda9873_checkit(struct CHIPSTATE *chip)
+{
+ int rc;
+
+ if (-1 == (rc = chip_read2(chip,254)))
+ return 0;
+ return (rc & ~0x1f) == 0x80;
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* audio chip description - defines+functions for tda9874h and tda9874a */
+/* Dariusz Kowalewski <darekk@automex.pl> */
+
+/* Subaddresses for TDA9874H and TDA9874A (slave rx) */
+#define TDA9874A_AGCGR 0x00 /* AGC gain */
+#define TDA9874A_GCONR 0x01 /* general config */
+#define TDA9874A_MSR 0x02 /* monitor select */
+#define TDA9874A_C1FRA 0x03 /* carrier 1 freq. */
+#define TDA9874A_C1FRB 0x04 /* carrier 1 freq. */
+#define TDA9874A_C1FRC 0x05 /* carrier 1 freq. */
+#define TDA9874A_C2FRA 0x06 /* carrier 2 freq. */
+#define TDA9874A_C2FRB 0x07 /* carrier 2 freq. */
+#define TDA9874A_C2FRC 0x08 /* carrier 2 freq. */
+#define TDA9874A_DCR 0x09 /* demodulator config */
+#define TDA9874A_FMER 0x0a /* FM de-emphasis */
+#define TDA9874A_FMMR 0x0b /* FM dematrix */
+#define TDA9874A_C1OLAR 0x0c /* ch.1 output level adj. */
+#define TDA9874A_C2OLAR 0x0d /* ch.2 output level adj. */
+#define TDA9874A_NCONR 0x0e /* NICAM config */
+#define TDA9874A_NOLAR 0x0f /* NICAM output level adj. */
+#define TDA9874A_NLELR 0x10 /* NICAM lower error limit */
+#define TDA9874A_NUELR 0x11 /* NICAM upper error limit */
+#define TDA9874A_AMCONR 0x12 /* audio mute control */
+#define TDA9874A_SDACOSR 0x13 /* stereo DAC output select */
+#define TDA9874A_AOSR 0x14 /* analog output select */
+#define TDA9874A_DAICONR 0x15 /* digital audio interface config */
+#define TDA9874A_I2SOSR 0x16 /* I2S-bus output select */
+#define TDA9874A_I2SOLAR 0x17 /* I2S-bus output level adj. */
+#define TDA9874A_MDACOSR 0x18 /* mono DAC output select (tda9874a) */
+#define TDA9874A_ESP 0xFF /* easy standard progr. (tda9874a) */
+
+/* Subaddresses for TDA9874H and TDA9874A (slave tx) */
+#define TDA9874A_DSR 0x00 /* device status */
+#define TDA9874A_NSR 0x01 /* NICAM status */
+#define TDA9874A_NECR 0x02 /* NICAM error count */
+#define TDA9874A_DR1 0x03 /* add. data LSB */
+#define TDA9874A_DR2 0x04 /* add. data MSB */
+#define TDA9874A_LLRA 0x05 /* monitor level read-out LSB */
+#define TDA9874A_LLRB 0x06 /* monitor level read-out MSB */
+#define TDA9874A_SIFLR 0x07 /* SIF level */
+#define TDA9874A_TR2 252 /* test reg. 2 */
+#define TDA9874A_TR1 253 /* test reg. 1 */
+#define TDA9874A_DIC 254 /* device id. code */
+#define TDA9874A_SIC 255 /* software id. code */
+
+
+static int tda9874a_mode = 1; /* 0: A2, 1: NICAM */
+static int tda9874a_GCONR = 0xc0; /* default config. input pin: SIFSEL=0 */
+static int tda9874a_NCONR = 0x01; /* default NICAM config.: AMSEL=0,AMUTE=1 */
+static int tda9874a_ESP = 0x07; /* default standard: NICAM D/K */
+static int tda9874a_dic = -1; /* device id. code */
+
+/* insmod options for tda9874a */
+static unsigned int tda9874a_SIF = UNSET;
+static unsigned int tda9874a_AMSEL = UNSET;
+static unsigned int tda9874a_STD = UNSET;
+module_param(tda9874a_SIF, int, 0444);
+module_param(tda9874a_AMSEL, int, 0444);
+module_param(tda9874a_STD, int, 0444);
+
+/*
+ * initialization table for tda9874 decoder:
+ * - carrier 1 freq. registers (3 bytes)
+ * - carrier 2 freq. registers (3 bytes)
+ * - demudulator config register
+ * - FM de-emphasis register (slow identification mode)
+ * Note: frequency registers must be written in single i2c transfer.
+ */
+static struct tda9874a_MODES {
+ char *name;
+ audiocmd cmd;
+} tda9874a_modelist[9] = {
+ { "A2, B/G", /* default */
+ { 9, { TDA9874A_C1FRA, 0x72,0x95,0x55, 0x77,0xA0,0x00, 0x00,0x00 }} },
+ { "A2, M (Korea)",
+ { 9, { TDA9874A_C1FRA, 0x5D,0xC0,0x00, 0x62,0x6A,0xAA, 0x20,0x22 }} },
+ { "A2, D/K (1)",
+ { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x82,0x60,0x00, 0x00,0x00 }} },
+ { "A2, D/K (2)",
+ { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x8C,0x75,0x55, 0x00,0x00 }} },
+ { "A2, D/K (3)",
+ { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x77,0xA0,0x00, 0x00,0x00 }} },
+ { "NICAM, I",
+ { 9, { TDA9874A_C1FRA, 0x7D,0x00,0x00, 0x88,0x8A,0xAA, 0x08,0x33 }} },
+ { "NICAM, B/G",
+ { 9, { TDA9874A_C1FRA, 0x72,0x95,0x55, 0x79,0xEA,0xAA, 0x08,0x33 }} },
+ { "NICAM, D/K",
+ { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x79,0xEA,0xAA, 0x08,0x33 }} },
+ { "NICAM, L",
+ { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x79,0xEA,0xAA, 0x09,0x33 }} }
+};
+
+static int tda9874a_setup(struct CHIPSTATE *chip)
+{
+ struct v4l2_subdev *sd = &chip->sd;
+
+ chip_write(chip, TDA9874A_AGCGR, 0x00); /* 0 dB */
+ chip_write(chip, TDA9874A_GCONR, tda9874a_GCONR);
+ chip_write(chip, TDA9874A_MSR, (tda9874a_mode) ? 0x03:0x02);
+ if(tda9874a_dic == 0x11) {
+ chip_write(chip, TDA9874A_FMMR, 0x80);
+ } else { /* dic == 0x07 */
+ chip_cmd(chip,"tda9874_modelist",&tda9874a_modelist[tda9874a_STD].cmd);
+ chip_write(chip, TDA9874A_FMMR, 0x00);
+ }
+ chip_write(chip, TDA9874A_C1OLAR, 0x00); /* 0 dB */
+ chip_write(chip, TDA9874A_C2OLAR, 0x00); /* 0 dB */
+ chip_write(chip, TDA9874A_NCONR, tda9874a_NCONR);
+ chip_write(chip, TDA9874A_NOLAR, 0x00); /* 0 dB */
+ /* Note: If signal quality is poor you may want to change NICAM */
+ /* error limit registers (NLELR and NUELR) to some greater values. */
+ /* Then the sound would remain stereo, but won't be so clear. */
+ chip_write(chip, TDA9874A_NLELR, 0x14); /* default */
+ chip_write(chip, TDA9874A_NUELR, 0x50); /* default */
+
+ if(tda9874a_dic == 0x11) {
+ chip_write(chip, TDA9874A_AMCONR, 0xf9);
+ chip_write(chip, TDA9874A_SDACOSR, (tda9874a_mode) ? 0x81:0x80);
+ chip_write(chip, TDA9874A_AOSR, 0x80);
+ chip_write(chip, TDA9874A_MDACOSR, (tda9874a_mode) ? 0x82:0x80);
+ chip_write(chip, TDA9874A_ESP, tda9874a_ESP);
+ } else { /* dic == 0x07 */
+ chip_write(chip, TDA9874A_AMCONR, 0xfb);
+ chip_write(chip, TDA9874A_SDACOSR, (tda9874a_mode) ? 0x81:0x80);
+ chip_write(chip, TDA9874A_AOSR, 0x00); /* or 0x10 */
+ }
+ v4l2_dbg(1, debug, sd, "tda9874a_setup(): %s [0x%02X].\n",
+ tda9874a_modelist[tda9874a_STD].name,tda9874a_STD);
+ return 1;
+}
+
+static int tda9874a_getmode(struct CHIPSTATE *chip)
+{
+ struct v4l2_subdev *sd = &chip->sd;
+ int dsr,nsr,mode;
+ int necr; /* just for debugging */
+
+ mode = V4L2_TUNER_MODE_MONO;
+
+ if(-1 == (dsr = chip_read2(chip,TDA9874A_DSR)))
+ return mode;
+ if(-1 == (nsr = chip_read2(chip,TDA9874A_NSR)))
+ return mode;
+ if(-1 == (necr = chip_read2(chip,TDA9874A_NECR)))
+ return mode;
+
+ /* need to store dsr/nsr somewhere */
+ chip->shadow.bytes[MAXREGS-2] = dsr;
+ chip->shadow.bytes[MAXREGS-1] = nsr;
+
+ if(tda9874a_mode) {
+ /* Note: DSR.RSSF and DSR.AMSTAT bits are also checked.
+ * If NICAM auto-muting is enabled, DSR.AMSTAT=1 indicates
+ * that sound has (temporarily) switched from NICAM to
+ * mono FM (or AM) on 1st sound carrier due to high NICAM bit
+ * error count. So in fact there is no stereo in this case :-(
+ * But changing the mode to V4L2_TUNER_MODE_MONO would switch
+ * external 4052 multiplexer in audio_hook().
+ */
+ if(nsr & 0x02) /* NSR.S/MB=1 */
+ mode |= V4L2_TUNER_MODE_STEREO;
+ if(nsr & 0x01) /* NSR.D/SB=1 */
+ mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ } else {
+ if(dsr & 0x02) /* DSR.IDSTE=1 */
+ mode |= V4L2_TUNER_MODE_STEREO;
+ if(dsr & 0x04) /* DSR.IDDUA=1 */
+ mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+
+ v4l2_dbg(1, debug, sd, "tda9874a_getmode(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n",
+ dsr, nsr, necr, mode);
+ return mode;
+}
+
+static void tda9874a_setmode(struct CHIPSTATE *chip, int mode)
+{
+ struct v4l2_subdev *sd = &chip->sd;
+
+ /* Disable/enable NICAM auto-muting (based on DSR.RSSF status bit). */
+ /* If auto-muting is disabled, we can hear a signal of degrading quality. */
+ if (tda9874a_mode) {
+ if(chip->shadow.bytes[MAXREGS-2] & 0x20) /* DSR.RSSF=1 */
+ tda9874a_NCONR &= 0xfe; /* enable */
+ else
+ tda9874a_NCONR |= 0x01; /* disable */
+ chip_write(chip, TDA9874A_NCONR, tda9874a_NCONR);
+ }
+
+ /* Note: TDA9874A supports automatic FM dematrixing (FMMR register)
+ * and has auto-select function for audio output (AOSR register).
+ * Old TDA9874H doesn't support these features.
+ * TDA9874A also has additional mono output pin (OUTM), which
+ * on same (all?) tv-cards is not used, anyway (as well as MONOIN).
+ */
+ if(tda9874a_dic == 0x11) {
+ int aosr = 0x80;
+ int mdacosr = (tda9874a_mode) ? 0x82:0x80;
+
+ switch(mode) {
+ case V4L2_TUNER_MODE_MONO:
+ case V4L2_TUNER_MODE_STEREO:
+ break;
+ case V4L2_TUNER_MODE_LANG1:
+ aosr = 0x80; /* auto-select, dual A/A */
+ mdacosr = (tda9874a_mode) ? 0x82:0x80;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ aosr = 0xa0; /* auto-select, dual B/B */
+ mdacosr = (tda9874a_mode) ? 0x83:0x81;
+ break;
+ default:
+ chip->mode = 0;
+ return;
+ }
+ chip_write(chip, TDA9874A_AOSR, aosr);
+ chip_write(chip, TDA9874A_MDACOSR, mdacosr);
+
+ v4l2_dbg(1, debug, sd, "tda9874a_setmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n",
+ mode, aosr, mdacosr);
+
+ } else { /* dic == 0x07 */
+ int fmmr,aosr;
+
+ switch(mode) {
+ case V4L2_TUNER_MODE_MONO:
+ fmmr = 0x00; /* mono */
+ aosr = 0x10; /* A/A */
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ if(tda9874a_mode) {
+ fmmr = 0x00;
+ aosr = 0x00; /* handled by NICAM auto-mute */
+ } else {
+ fmmr = (tda9874a_ESP == 1) ? 0x05 : 0x04; /* stereo */
+ aosr = 0x00;
+ }
+ break;
+ case V4L2_TUNER_MODE_LANG1:
+ fmmr = 0x02; /* dual */
+ aosr = 0x10; /* dual A/A */
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ fmmr = 0x02; /* dual */
+ aosr = 0x20; /* dual B/B */
+ break;
+ default:
+ chip->mode = 0;
+ return;
+ }
+ chip_write(chip, TDA9874A_FMMR, fmmr);
+ chip_write(chip, TDA9874A_AOSR, aosr);
+
+ v4l2_dbg(1, debug, sd, "tda9874a_setmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n",
+ mode, fmmr, aosr);
+ }
+}
+
+static int tda9874a_checkit(struct CHIPSTATE *chip)
+{
+ struct v4l2_subdev *sd = &chip->sd;
+ int dic,sic; /* device id. and software id. codes */
+
+ if(-1 == (dic = chip_read2(chip,TDA9874A_DIC)))
+ return 0;
+ if(-1 == (sic = chip_read2(chip,TDA9874A_SIC)))
+ return 0;
+
+ v4l2_dbg(1, debug, sd, "tda9874a_checkit(): DIC=0x%X, SIC=0x%X.\n", dic, sic);
+
+ if((dic == 0x11)||(dic == 0x07)) {
+ v4l2_info(sd, "found tda9874%s.\n", (dic == 0x11) ? "a" : "h");
+ tda9874a_dic = dic; /* remember device id. */
+ return 1;
+ }
+ return 0; /* not found */
+}
+
+static int tda9874a_initialize(struct CHIPSTATE *chip)
+{
+ if (tda9874a_SIF > 2)
+ tda9874a_SIF = 1;
+ if (tda9874a_STD >= ARRAY_SIZE(tda9874a_modelist))
+ tda9874a_STD = 0;
+ if(tda9874a_AMSEL > 1)
+ tda9874a_AMSEL = 0;
+
+ if(tda9874a_SIF == 1)
+ tda9874a_GCONR = 0xc0; /* sound IF input 1 */
+ else
+ tda9874a_GCONR = 0xc1; /* sound IF input 2 */
+
+ tda9874a_ESP = tda9874a_STD;
+ tda9874a_mode = (tda9874a_STD < 5) ? 0 : 1;
+
+ if(tda9874a_AMSEL == 0)
+ tda9874a_NCONR = 0x01; /* auto-mute: analog mono input */
+ else
+ tda9874a_NCONR = 0x05; /* auto-mute: 1st carrier FM or AM */
+
+ tda9874a_setup(chip);
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+/* audio chip description - defines+functions for tda9875 */
+/* The TDA9875 is made by Philips Semiconductor
+ * http://www.semiconductors.philips.com
+ * TDA9875: I2C-bus controlled DSP audio processor, FM demodulator
+ *
+ */
+
+/* subaddresses for TDA9875 */
+#define TDA9875_MUT 0x12 /*General mute (value --> 0b11001100*/
+#define TDA9875_CFG 0x01 /* Config register (value --> 0b00000000 */
+#define TDA9875_DACOS 0x13 /*DAC i/o select (ADC) 0b0000100*/
+#define TDA9875_LOSR 0x16 /*Line output select regirter 0b0100 0001*/
+
+#define TDA9875_CH1V 0x0c /*Channel 1 volume (mute)*/
+#define TDA9875_CH2V 0x0d /*Channel 2 volume (mute)*/
+#define TDA9875_SC1 0x14 /*SCART 1 in (mono)*/
+#define TDA9875_SC2 0x15 /*SCART 2 in (mono)*/
+
+#define TDA9875_ADCIS 0x17 /*ADC input select (mono) 0b0110 000*/
+#define TDA9875_AER 0x19 /*Audio effect (AVL+Pseudo) 0b0000 0110*/
+#define TDA9875_MCS 0x18 /*Main channel select (DAC) 0b0000100*/
+#define TDA9875_MVL 0x1a /* Main volume gauche */
+#define TDA9875_MVR 0x1b /* Main volume droite */
+#define TDA9875_MBA 0x1d /* Main Basse */
+#define TDA9875_MTR 0x1e /* Main treble */
+#define TDA9875_ACS 0x1f /* Auxiliary channel select (FM) 0b0000000*/
+#define TDA9875_AVL 0x20 /* Auxiliary volume gauche */
+#define TDA9875_AVR 0x21 /* Auxiliary volume droite */
+#define TDA9875_ABA 0x22 /* Auxiliary Basse */
+#define TDA9875_ATR 0x23 /* Auxiliary treble */
+
+#define TDA9875_MSR 0x02 /* Monitor select register */
+#define TDA9875_C1MSB 0x03 /* Carrier 1 (FM) frequency register MSB */
+#define TDA9875_C1MIB 0x04 /* Carrier 1 (FM) frequency register (16-8]b */
+#define TDA9875_C1LSB 0x05 /* Carrier 1 (FM) frequency register LSB */
+#define TDA9875_C2MSB 0x06 /* Carrier 2 (nicam) frequency register MSB */
+#define TDA9875_C2MIB 0x07 /* Carrier 2 (nicam) frequency register (16-8]b */
+#define TDA9875_C2LSB 0x08 /* Carrier 2 (nicam) frequency register LSB */
+#define TDA9875_DCR 0x09 /* Demodulateur configuration regirter*/
+#define TDA9875_DEEM 0x0a /* FM de-emphasis regirter*/
+#define TDA9875_FMAT 0x0b /* FM Matrix regirter*/
+
+/* values */
+#define TDA9875_MUTE_ON 0xff /* general mute */
+#define TDA9875_MUTE_OFF 0xcc /* general no mute */
+
+static int tda9875_initialize(struct CHIPSTATE *chip)
+{
+ chip_write(chip, TDA9875_CFG, 0xd0); /*reg de config 0 (reset)*/
+ chip_write(chip, TDA9875_MSR, 0x03); /* Monitor 0b00000XXX*/
+ chip_write(chip, TDA9875_C1MSB, 0x00); /*Car1(FM) MSB XMHz*/
+ chip_write(chip, TDA9875_C1MIB, 0x00); /*Car1(FM) MIB XMHz*/
+ chip_write(chip, TDA9875_C1LSB, 0x00); /*Car1(FM) LSB XMHz*/
+ chip_write(chip, TDA9875_C2MSB, 0x00); /*Car2(NICAM) MSB XMHz*/
+ chip_write(chip, TDA9875_C2MIB, 0x00); /*Car2(NICAM) MIB XMHz*/
+ chip_write(chip, TDA9875_C2LSB, 0x00); /*Car2(NICAM) LSB XMHz*/
+ chip_write(chip, TDA9875_DCR, 0x00); /*Demod config 0x00*/
+ chip_write(chip, TDA9875_DEEM, 0x44); /*DE-Emph 0b0100 0100*/
+ chip_write(chip, TDA9875_FMAT, 0x00); /*FM Matrix reg 0x00*/
+ chip_write(chip, TDA9875_SC1, 0x00); /* SCART 1 (SC1)*/
+ chip_write(chip, TDA9875_SC2, 0x01); /* SCART 2 (sc2)*/
+
+ chip_write(chip, TDA9875_CH1V, 0x10); /* Channel volume 1 mute*/
+ chip_write(chip, TDA9875_CH2V, 0x10); /* Channel volume 2 mute */
+ chip_write(chip, TDA9875_DACOS, 0x02); /* sig DAC i/o(in:nicam)*/
+ chip_write(chip, TDA9875_ADCIS, 0x6f); /* sig ADC input(in:mono)*/
+ chip_write(chip, TDA9875_LOSR, 0x00); /* line out (in:mono)*/
+ chip_write(chip, TDA9875_AER, 0x00); /*06 Effect (AVL+PSEUDO) */
+ chip_write(chip, TDA9875_MCS, 0x44); /* Main ch select (DAC) */
+ chip_write(chip, TDA9875_MVL, 0x03); /* Vol Main left 10dB */
+ chip_write(chip, TDA9875_MVR, 0x03); /* Vol Main right 10dB*/
+ chip_write(chip, TDA9875_MBA, 0x00); /* Main Bass Main 0dB*/
+ chip_write(chip, TDA9875_MTR, 0x00); /* Main Treble Main 0dB*/
+ chip_write(chip, TDA9875_ACS, 0x44); /* Aux chan select (dac)*/
+ chip_write(chip, TDA9875_AVL, 0x00); /* Vol Aux left 0dB*/
+ chip_write(chip, TDA9875_AVR, 0x00); /* Vol Aux right 0dB*/
+ chip_write(chip, TDA9875_ABA, 0x00); /* Aux Bass Main 0dB*/
+ chip_write(chip, TDA9875_ATR, 0x00); /* Aux Aigus Main 0dB*/
+
+ chip_write(chip, TDA9875_MUT, 0xcc); /* General mute */
+ return 0;
+}
+
+static int tda9875_volume(int val) { return (unsigned char)(val / 602 - 84); }
+static int tda9875_bass(int val) { return (unsigned char)(max(-12, val / 2115 - 15)); }
+static int tda9875_treble(int val) { return (unsigned char)(val / 2622 - 12); }
+
+/* ----------------------------------------------------------------------- */
+
+
+/* *********************** *
+ * i2c interface functions *
+ * *********************** */
+
+static int tda9875_checkit(struct CHIPSTATE *chip)
+{
+ struct v4l2_subdev *sd = &chip->sd;
+ int dic, rev;
+
+ dic = chip_read2(chip, 254);
+ rev = chip_read2(chip, 255);
+
+ if (dic == 0 || dic == 2) { /* tda9875 and tda9875A */
+ v4l2_info(sd, "found tda9875%s rev. %d.\n",
+ dic == 0 ? "" : "A", rev);
+ return 1;
+ }
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+/* audio chip descriptions - defines+functions for tea6420 */
+
+#define TEA6300_VL 0x00 /* volume left */
+#define TEA6300_VR 0x01 /* volume right */
+#define TEA6300_BA 0x02 /* bass */
+#define TEA6300_TR 0x03 /* treble */
+#define TEA6300_FA 0x04 /* fader control */
+#define TEA6300_S 0x05 /* switch register */
+ /* values for those registers: */
+#define TEA6300_S_SA 0x01 /* stereo A input */
+#define TEA6300_S_SB 0x02 /* stereo B */
+#define TEA6300_S_SC 0x04 /* stereo C */
+#define TEA6300_S_GMU 0x80 /* general mute */
+
+#define TEA6320_V 0x00 /* volume (0-5)/loudness off (6)/zero crossing mute(7) */
+#define TEA6320_FFR 0x01 /* fader front right (0-5) */
+#define TEA6320_FFL 0x02 /* fader front left (0-5) */
+#define TEA6320_FRR 0x03 /* fader rear right (0-5) */
+#define TEA6320_FRL 0x04 /* fader rear left (0-5) */
+#define TEA6320_BA 0x05 /* bass (0-4) */
+#define TEA6320_TR 0x06 /* treble (0-4) */
+#define TEA6320_S 0x07 /* switch register */
+ /* values for those registers: */
+#define TEA6320_S_SA 0x07 /* stereo A input */
+#define TEA6320_S_SB 0x06 /* stereo B */
+#define TEA6320_S_SC 0x05 /* stereo C */
+#define TEA6320_S_SD 0x04 /* stereo D */
+#define TEA6320_S_GMU 0x80 /* general mute */
+
+#define TEA6420_S_SA 0x00 /* stereo A input */
+#define TEA6420_S_SB 0x01 /* stereo B */
+#define TEA6420_S_SC 0x02 /* stereo C */
+#define TEA6420_S_SD 0x03 /* stereo D */
+#define TEA6420_S_SE 0x04 /* stereo E */
+#define TEA6420_S_GMU 0x05 /* general mute */
+
+static int tea6300_shift10(int val) { return val >> 10; }
+static int tea6300_shift12(int val) { return val >> 12; }
+
+/* Assumes 16bit input (values 0x3f to 0x0c are unique, values less than */
+/* 0x0c mirror those immediately higher) */
+static int tea6320_volume(int val) { return (val / (65535/(63-12)) + 12) & 0x3f; }
+static int tea6320_shift11(int val) { return val >> 11; }
+static int tea6320_initialize(struct CHIPSTATE * chip)
+{
+ chip_write(chip, TEA6320_FFR, 0x3f);
+ chip_write(chip, TEA6320_FFL, 0x3f);
+ chip_write(chip, TEA6320_FRR, 0x3f);
+ chip_write(chip, TEA6320_FRL, 0x3f);
+
+ return 0;
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* audio chip descriptions - defines+functions for tda8425 */
+
+#define TDA8425_VL 0x00 /* volume left */
+#define TDA8425_VR 0x01 /* volume right */
+#define TDA8425_BA 0x02 /* bass */
+#define TDA8425_TR 0x03 /* treble */
+#define TDA8425_S1 0x08 /* switch functions */
+ /* values for those registers: */
+#define TDA8425_S1_OFF 0xEE /* audio off (mute on) */
+#define TDA8425_S1_CH1 0xCE /* audio channel 1 (mute off) - "linear stereo" mode */
+#define TDA8425_S1_CH2 0xCF /* audio channel 2 (mute off) - "linear stereo" mode */
+#define TDA8425_S1_MU 0x20 /* mute bit */
+#define TDA8425_S1_STEREO 0x18 /* stereo bits */
+#define TDA8425_S1_STEREO_SPATIAL 0x18 /* spatial stereo */
+#define TDA8425_S1_STEREO_LINEAR 0x08 /* linear stereo */
+#define TDA8425_S1_STEREO_PSEUDO 0x10 /* pseudo stereo */
+#define TDA8425_S1_STEREO_MONO 0x00 /* forced mono */
+#define TDA8425_S1_ML 0x06 /* language selector */
+#define TDA8425_S1_ML_SOUND_A 0x02 /* sound a */
+#define TDA8425_S1_ML_SOUND_B 0x04 /* sound b */
+#define TDA8425_S1_ML_STEREO 0x06 /* stereo */
+#define TDA8425_S1_IS 0x01 /* channel selector */
+
+
+static int tda8425_shift10(int val) { return (val >> 10) | 0xc0; }
+static int tda8425_shift12(int val) { return (val >> 12) | 0xf0; }
+
+static void tda8425_setmode(struct CHIPSTATE *chip, int mode)
+{
+ int s1 = chip->shadow.bytes[TDA8425_S1+1] & 0xe1;
+
+ if (mode & V4L2_TUNER_MODE_LANG1) {
+ s1 |= TDA8425_S1_ML_SOUND_A;
+ s1 |= TDA8425_S1_STEREO_PSEUDO;
+
+ } else if (mode & V4L2_TUNER_MODE_LANG2) {
+ s1 |= TDA8425_S1_ML_SOUND_B;
+ s1 |= TDA8425_S1_STEREO_PSEUDO;
+
+ } else {
+ s1 |= TDA8425_S1_ML_STEREO;
+
+ if (mode & V4L2_TUNER_MODE_MONO)
+ s1 |= TDA8425_S1_STEREO_MONO;
+ if (mode & V4L2_TUNER_MODE_STEREO)
+ s1 |= TDA8425_S1_STEREO_SPATIAL;
+ }
+ chip_write(chip,TDA8425_S1,s1);
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* audio chip descriptions - defines+functions for pic16c54 (PV951) */
+
+/* the registers of 16C54, I2C sub address. */
+#define PIC16C54_REG_KEY_CODE 0x01 /* Not use. */
+#define PIC16C54_REG_MISC 0x02
+
+/* bit definition of the RESET register, I2C data. */
+#define PIC16C54_MISC_RESET_REMOTE_CTL 0x01 /* bit 0, Reset to receive the key */
+ /* code of remote controller */
+#define PIC16C54_MISC_MTS_MAIN 0x02 /* bit 1 */
+#define PIC16C54_MISC_MTS_SAP 0x04 /* bit 2 */
+#define PIC16C54_MISC_MTS_BOTH 0x08 /* bit 3 */
+#define PIC16C54_MISC_SND_MUTE 0x10 /* bit 4, Mute Audio(Line-in and Tuner) */
+#define PIC16C54_MISC_SND_NOTMUTE 0x20 /* bit 5 */
+#define PIC16C54_MISC_SWITCH_TUNER 0x40 /* bit 6 , Switch to Line-in */
+#define PIC16C54_MISC_SWITCH_LINE 0x80 /* bit 7 , Switch to Tuner */
+
+/* ---------------------------------------------------------------------- */
+/* audio chip descriptions - defines+functions for TA8874Z */
+
+/* write 1st byte */
+#define TA8874Z_LED_STE 0x80
+#define TA8874Z_LED_BIL 0x40
+#define TA8874Z_LED_EXT 0x20
+#define TA8874Z_MONO_SET 0x10
+#define TA8874Z_MUTE 0x08
+#define TA8874Z_F_MONO 0x04
+#define TA8874Z_MODE_SUB 0x02
+#define TA8874Z_MODE_MAIN 0x01
+
+/* write 2nd byte */
+/*#define TA8874Z_TI 0x80 */ /* test mode */
+#define TA8874Z_SEPARATION 0x3f
+#define TA8874Z_SEPARATION_DEFAULT 0x10
+
+/* read */
+#define TA8874Z_B1 0x80
+#define TA8874Z_B0 0x40
+#define TA8874Z_CHAG_FLAG 0x20
+
+/*
+ * B1 B0
+ * mono L H
+ * stereo L L
+ * BIL H L
+ */
+static int ta8874z_getmode(struct CHIPSTATE *chip)
+{
+ int val, mode;
+
+ val = chip_read(chip);
+ mode = V4L2_TUNER_MODE_MONO;
+ if (val & TA8874Z_B1){
+ mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }else if (!(val & TA8874Z_B0)){
+ mode |= V4L2_TUNER_MODE_STEREO;
+ }
+ /* v4l_dbg(1, debug, chip->c, "ta8874z_getmode(): raw chip read: 0x%02x, return: 0x%02x\n", val, mode); */
+ return mode;
+}
+
+static audiocmd ta8874z_stereo = { 2, {0, TA8874Z_SEPARATION_DEFAULT}};
+static audiocmd ta8874z_mono = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}};
+static audiocmd ta8874z_main = {2, { 0, TA8874Z_SEPARATION_DEFAULT}};
+static audiocmd ta8874z_sub = {2, { TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT}};
+
+static void ta8874z_setmode(struct CHIPSTATE *chip, int mode)
+{
+ struct v4l2_subdev *sd = &chip->sd;
+ int update = 1;
+ audiocmd *t = NULL;
+
+ v4l2_dbg(1, debug, sd, "ta8874z_setmode(): mode: 0x%02x\n", mode);
+
+ switch(mode){
+ case V4L2_TUNER_MODE_MONO:
+ t = &ta8874z_mono;
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ t = &ta8874z_stereo;
+ break;
+ case V4L2_TUNER_MODE_LANG1:
+ t = &ta8874z_main;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ t = &ta8874z_sub;
+ break;
+ default:
+ update = 0;
+ }
+
+ if(update)
+ chip_cmd(chip, "TA8874Z", t);
+}
+
+static int ta8874z_checkit(struct CHIPSTATE *chip)
+{
+ int rc;
+ rc = chip_read(chip);
+ return ((rc & 0x1f) == 0x1f) ? 1 : 0;
+}
+
+/* ---------------------------------------------------------------------- */
+/* audio chip descriptions - struct CHIPDESC */
+
+/* insmod options to enable/disable individual audio chips */
+static int tda8425 = 1;
+static int tda9840 = 1;
+static int tda9850 = 1;
+static int tda9855 = 1;
+static int tda9873 = 1;
+static int tda9874a = 1;
+static int tda9875 = 1;
+static int tea6300; /* default 0 - address clash with msp34xx */
+static int tea6320; /* default 0 - address clash with msp34xx */
+static int tea6420 = 1;
+static int pic16c54 = 1;
+static int ta8874z; /* default 0 - address clash with tda9840 */
+
+module_param(tda8425, int, 0444);
+module_param(tda9840, int, 0444);
+module_param(tda9850, int, 0444);
+module_param(tda9855, int, 0444);
+module_param(tda9873, int, 0444);
+module_param(tda9874a, int, 0444);
+module_param(tda9875, int, 0444);
+module_param(tea6300, int, 0444);
+module_param(tea6320, int, 0444);
+module_param(tea6420, int, 0444);
+module_param(pic16c54, int, 0444);
+module_param(ta8874z, int, 0444);
+
+static struct CHIPDESC chiplist[] = {
+ {
+ .name = "tda9840",
+ .insmodopt = &tda9840,
+ .addr_lo = I2C_ADDR_TDA9840 >> 1,
+ .addr_hi = I2C_ADDR_TDA9840 >> 1,
+ .registers = 5,
+ .flags = CHIP_NEED_CHECKMODE,
+
+ /* callbacks */
+ .checkit = tda9840_checkit,
+ .getmode = tda9840_getmode,
+ .setmode = tda9840_setmode,
+
+ .init = { 2, { TDA9840_TEST, TDA9840_TEST_INT1SN
+ /* ,TDA9840_SW, TDA9840_MONO */} }
+ },
+ {
+ .name = "tda9873h",
+ .insmodopt = &tda9873,
+ .addr_lo = I2C_ADDR_TDA985x_L >> 1,
+ .addr_hi = I2C_ADDR_TDA985x_H >> 1,
+ .registers = 3,
+ .flags = CHIP_HAS_INPUTSEL | CHIP_NEED_CHECKMODE,
+
+ /* callbacks */
+ .checkit = tda9873_checkit,
+ .getmode = tda9873_getmode,
+ .setmode = tda9873_setmode,
+
+ .init = { 4, { TDA9873_SW, 0xa4, 0x06, 0x03 } },
+ .inputreg = TDA9873_SW,
+ .inputmute = TDA9873_MUTE | TDA9873_AUTOMUTE,
+ .inputmap = {0xa0, 0xa2, 0xa0, 0xa0},
+ .inputmask = TDA9873_INP_MASK|TDA9873_MUTE|TDA9873_AUTOMUTE,
+
+ },
+ {
+ .name = "tda9874h/a",
+ .insmodopt = &tda9874a,
+ .addr_lo = I2C_ADDR_TDA9874 >> 1,
+ .addr_hi = I2C_ADDR_TDA9874 >> 1,
+ .flags = CHIP_NEED_CHECKMODE,
+
+ /* callbacks */
+ .initialize = tda9874a_initialize,
+ .checkit = tda9874a_checkit,
+ .getmode = tda9874a_getmode,
+ .setmode = tda9874a_setmode,
+ },
+ {
+ .name = "tda9875",
+ .insmodopt = &tda9875,
+ .addr_lo = I2C_ADDR_TDA9875 >> 1,
+ .addr_hi = I2C_ADDR_TDA9875 >> 1,
+ .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE,
+
+ /* callbacks */
+ .initialize = tda9875_initialize,
+ .checkit = tda9875_checkit,
+ .volfunc = tda9875_volume,
+ .bassfunc = tda9875_bass,
+ .treblefunc = tda9875_treble,
+ .leftreg = TDA9875_MVL,
+ .rightreg = TDA9875_MVR,
+ .bassreg = TDA9875_MBA,
+ .treblereg = TDA9875_MTR,
+ .leftinit = 58880,
+ .rightinit = 58880,
+ },
+ {
+ .name = "tda9850",
+ .insmodopt = &tda9850,
+ .addr_lo = I2C_ADDR_TDA985x_L >> 1,
+ .addr_hi = I2C_ADDR_TDA985x_H >> 1,
+ .registers = 11,
+
+ .getmode = tda985x_getmode,
+ .setmode = tda985x_setmode,
+
+ .init = { 8, { TDA9850_C4, 0x08, 0x08, TDA985x_STEREO, 0x07, 0x10, 0x10, 0x03 } }
+ },
+ {
+ .name = "tda9855",
+ .insmodopt = &tda9855,
+ .addr_lo = I2C_ADDR_TDA985x_L >> 1,
+ .addr_hi = I2C_ADDR_TDA985x_H >> 1,
+ .registers = 11,
+ .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE,
+
+ .leftreg = TDA9855_VL,
+ .rightreg = TDA9855_VR,
+ .bassreg = TDA9855_BA,
+ .treblereg = TDA9855_TR,
+
+ /* callbacks */
+ .volfunc = tda9855_volume,
+ .bassfunc = tda9855_bass,
+ .treblefunc = tda9855_treble,
+ .getmode = tda985x_getmode,
+ .setmode = tda985x_setmode,
+
+ .init = { 12, { 0, 0x6f, 0x6f, 0x0e, 0x07<<1, 0x8<<2,
+ TDA9855_MUTE | TDA9855_AVL | TDA9855_LOUD | TDA9855_INT,
+ TDA985x_STEREO | TDA9855_LINEAR | TDA9855_TZCM | TDA9855_VZCM,
+ 0x07, 0x10, 0x10, 0x03 }}
+ },
+ {
+ .name = "tea6300",
+ .insmodopt = &tea6300,
+ .addr_lo = I2C_ADDR_TEA6300 >> 1,
+ .addr_hi = I2C_ADDR_TEA6300 >> 1,
+ .registers = 6,
+ .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL,
+
+ .leftreg = TEA6300_VR,
+ .rightreg = TEA6300_VL,
+ .bassreg = TEA6300_BA,
+ .treblereg = TEA6300_TR,
+
+ /* callbacks */
+ .volfunc = tea6300_shift10,
+ .bassfunc = tea6300_shift12,
+ .treblefunc = tea6300_shift12,
+
+ .inputreg = TEA6300_S,
+ .inputmap = { TEA6300_S_SA, TEA6300_S_SB, TEA6300_S_SC },
+ .inputmute = TEA6300_S_GMU,
+ },
+ {
+ .name = "tea6320",
+ .insmodopt = &tea6320,
+ .addr_lo = I2C_ADDR_TEA6300 >> 1,
+ .addr_hi = I2C_ADDR_TEA6300 >> 1,
+ .registers = 8,
+ .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL,
+
+ .leftreg = TEA6320_V,
+ .rightreg = TEA6320_V,
+ .bassreg = TEA6320_BA,
+ .treblereg = TEA6320_TR,
+
+ /* callbacks */
+ .initialize = tea6320_initialize,
+ .volfunc = tea6320_volume,
+ .bassfunc = tea6320_shift11,
+ .treblefunc = tea6320_shift11,
+
+ .inputreg = TEA6320_S,
+ .inputmap = { TEA6320_S_SA, TEA6420_S_SB, TEA6300_S_SC, TEA6320_S_SD },
+ .inputmute = TEA6300_S_GMU,
+ },
+ {
+ .name = "tea6420",
+ .insmodopt = &tea6420,
+ .addr_lo = I2C_ADDR_TEA6420 >> 1,
+ .addr_hi = I2C_ADDR_TEA6420 >> 1,
+ .registers = 1,
+ .flags = CHIP_HAS_INPUTSEL,
+
+ .inputreg = -1,
+ .inputmap = { TEA6420_S_SA, TEA6420_S_SB, TEA6420_S_SC },
+ .inputmute = TEA6300_S_GMU,
+ },
+ {
+ .name = "tda8425",
+ .insmodopt = &tda8425,
+ .addr_lo = I2C_ADDR_TDA8425 >> 1,
+ .addr_hi = I2C_ADDR_TDA8425 >> 1,
+ .registers = 9,
+ .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL,
+
+ .leftreg = TDA8425_VL,
+ .rightreg = TDA8425_VR,
+ .bassreg = TDA8425_BA,
+ .treblereg = TDA8425_TR,
+
+ /* callbacks */
+ .volfunc = tda8425_shift10,
+ .bassfunc = tda8425_shift12,
+ .treblefunc = tda8425_shift12,
+ .setmode = tda8425_setmode,
+
+ .inputreg = TDA8425_S1,
+ .inputmap = { TDA8425_S1_CH1, TDA8425_S1_CH1, TDA8425_S1_CH1 },
+ .inputmute = TDA8425_S1_OFF,
+
+ },
+ {
+ .name = "pic16c54 (PV951)",
+ .insmodopt = &pic16c54,
+ .addr_lo = I2C_ADDR_PIC16C54 >> 1,
+ .addr_hi = I2C_ADDR_PIC16C54>> 1,
+ .registers = 2,
+ .flags = CHIP_HAS_INPUTSEL,
+
+ .inputreg = PIC16C54_REG_MISC,
+ .inputmap = {PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_TUNER,
+ PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_LINE,
+ PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_LINE,
+ PIC16C54_MISC_SND_MUTE},
+ .inputmute = PIC16C54_MISC_SND_MUTE,
+ },
+ {
+ .name = "ta8874z",
+ .checkit = ta8874z_checkit,
+ .insmodopt = &ta8874z,
+ .addr_lo = I2C_ADDR_TDA9840 >> 1,
+ .addr_hi = I2C_ADDR_TDA9840 >> 1,
+ .registers = 2,
+ .flags = CHIP_NEED_CHECKMODE,
+
+ /* callbacks */
+ .getmode = ta8874z_getmode,
+ .setmode = ta8874z_setmode,
+
+ .init = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}},
+ },
+ { .name = NULL } /* EOF */
+};
+
+
+/* ---------------------------------------------------------------------- */
+
+static int tvaudio_g_ctrl(struct v4l2_subdev *sd,
+ struct v4l2_control *ctrl)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (!(desc->flags & CHIP_HAS_INPUTSEL))
+ break;
+ ctrl->value=chip->muted;
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+ if (!(desc->flags & CHIP_HAS_VOLUME))
+ break;
+ ctrl->value = max(chip->left,chip->right);
+ return 0;
+ case V4L2_CID_AUDIO_BALANCE:
+ {
+ int volume;
+ if (!(desc->flags & CHIP_HAS_VOLUME))
+ break;
+ volume = max(chip->left,chip->right);
+ if (volume)
+ ctrl->value=(32768*min(chip->left,chip->right))/volume;
+ else
+ ctrl->value=32768;
+ return 0;
+ }
+ case V4L2_CID_AUDIO_BASS:
+ if (!(desc->flags & CHIP_HAS_BASSTREBLE))
+ break;
+ ctrl->value = chip->bass;
+ return 0;
+ case V4L2_CID_AUDIO_TREBLE:
+ if (!(desc->flags & CHIP_HAS_BASSTREBLE))
+ break;
+ ctrl->value = chip->treble;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int tvaudio_s_ctrl(struct v4l2_subdev *sd,
+ struct v4l2_control *ctrl)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (!(desc->flags & CHIP_HAS_INPUTSEL))
+ break;
+
+ if (ctrl->value < 0 || ctrl->value >= 2)
+ return -ERANGE;
+ chip->muted = ctrl->value;
+ if (chip->muted)
+ chip_write_masked(chip,desc->inputreg,desc->inputmute,desc->inputmask);
+ else
+ chip_write_masked(chip,desc->inputreg,
+ desc->inputmap[chip->input],desc->inputmask);
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+ {
+ int volume,balance;
+
+ if (!(desc->flags & CHIP_HAS_VOLUME))
+ break;
+
+ volume = max(chip->left,chip->right);
+ if (volume)
+ balance=(32768*min(chip->left,chip->right))/volume;
+ else
+ balance=32768;
+
+ volume=ctrl->value;
+ chip->left = (min(65536 - balance,32768) * volume) / 32768;
+ chip->right = (min(balance,volume *(__u16)32768)) / 32768;
+
+ chip_write(chip,desc->leftreg,desc->volfunc(chip->left));
+ chip_write(chip,desc->rightreg,desc->volfunc(chip->right));
+
+ return 0;
+ }
+ case V4L2_CID_AUDIO_BALANCE:
+ {
+ int volume, balance;
+
+ if (!(desc->flags & CHIP_HAS_VOLUME))
+ break;
+
+ volume = max(chip->left, chip->right);
+ balance = ctrl->value;
+ chip->left = (min(65536 - balance, 32768) * volume) / 32768;
+ chip->right = (min(balance, volume * (__u16)32768)) / 32768;
+
+ chip_write(chip, desc->leftreg, desc->volfunc(chip->left));
+ chip_write(chip, desc->rightreg, desc->volfunc(chip->right));
+
+ return 0;
+ }
+ case V4L2_CID_AUDIO_BASS:
+ if (!(desc->flags & CHIP_HAS_BASSTREBLE))
+ break;
+ chip->bass = ctrl->value;
+ chip_write(chip,desc->bassreg,desc->bassfunc(chip->bass));
+
+ return 0;
+ case V4L2_CID_AUDIO_TREBLE:
+ if (!(desc->flags & CHIP_HAS_BASSTREBLE))
+ break;
+ chip->treble = ctrl->value;
+ chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble));
+
+ return 0;
+ }
+ return -EINVAL;
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* video4linux interface */
+
+static int tvaudio_s_radio(struct v4l2_subdev *sd)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+
+ chip->radio = 1;
+ chip->watch_stereo = 0;
+ /* del_timer(&chip->wt); */
+ return 0;
+}
+
+static int tvaudio_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
+
+ switch (qc->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (desc->flags & CHIP_HAS_INPUTSEL)
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ if (desc->flags & CHIP_HAS_VOLUME)
+ return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880);
+ break;
+ case V4L2_CID_AUDIO_BALANCE:
+ if (desc->flags & CHIP_HAS_VOLUME)
+ return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768);
+ break;
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ if (desc->flags & CHIP_HAS_BASSTREBLE)
+ return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768);
+ break;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static int tvaudio_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
+
+ if (!(desc->flags & CHIP_HAS_INPUTSEL))
+ return 0;
+ if (input >= 4)
+ return -EINVAL;
+ /* There are four inputs: tuner, radio, extern and intern. */
+ chip->input = input;
+ if (chip->muted)
+ return 0;
+ chip_write_masked(chip, desc->inputreg,
+ desc->inputmap[chip->input], desc->inputmask);
+ return 0;
+}
+
+static int tvaudio_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
+ int mode = 0;
+
+ if (!desc->setmode)
+ return 0;
+ if (chip->radio)
+ return 0;
+
+ switch (vt->audmode) {
+ case V4L2_TUNER_MODE_MONO:
+ case V4L2_TUNER_MODE_STEREO:
+ case V4L2_TUNER_MODE_LANG1:
+ case V4L2_TUNER_MODE_LANG2:
+ mode = vt->audmode;
+ break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ mode = V4L2_TUNER_MODE_STEREO;
+ break;
+ default:
+ return -EINVAL;
+ }
+ chip->audmode = vt->audmode;
+
+ if (mode) {
+ chip->watch_stereo = 0;
+ /* del_timer(&chip->wt); */
+ chip->mode = mode;
+ desc->setmode(chip, mode);
+ }
+ return 0;
+}
+
+static int tvaudio_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
+ int mode = V4L2_TUNER_MODE_MONO;
+
+ if (!desc->getmode)
+ return 0;
+ if (chip->radio)
+ return 0;
+
+ vt->audmode = chip->audmode;
+ vt->rxsubchans = 0;
+ vt->capability = V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
+
+ mode = desc->getmode(chip);
+
+ if (mode & V4L2_TUNER_MODE_MONO)
+ vt->rxsubchans |= V4L2_TUNER_SUB_MONO;
+ if (mode & V4L2_TUNER_MODE_STEREO)
+ vt->rxsubchans |= V4L2_TUNER_SUB_STEREO;
+ /* Note: for SAP it should be mono/lang2 or stereo/lang2.
+ When this module is converted fully to v4l2, then this
+ should change for those chips that can detect SAP. */
+ if (mode & V4L2_TUNER_MODE_LANG1)
+ vt->rxsubchans = V4L2_TUNER_SUB_LANG1 |
+ V4L2_TUNER_SUB_LANG2;
+ return 0;
+}
+
+static int tvaudio_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+
+ chip->radio = 0;
+ return 0;
+}
+
+static int tvaudio_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
+
+ chip->mode = 0; /* automatic */
+
+ /* For chips that provide getmode and setmode, and doesn't
+ automatically follows the stereo carrier, a kthread is
+ created to set the audio standard. In this case, when then
+ the video channel is changed, tvaudio starts on MONO mode.
+ After waiting for 2 seconds, the kernel thread is called,
+ to follow whatever audio standard is pointed by the
+ audio carrier.
+ */
+ if (chip->thread) {
+ desc->setmode(chip, V4L2_TUNER_MODE_MONO);
+ if (chip->prevmode != V4L2_TUNER_MODE_MONO)
+ chip->prevmode = -1; /* reset previous mode */
+ mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000));
+ }
+ return 0;
+}
+
+static int tvaudio_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVAUDIO, 0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tvaudio_core_ops = {
+ .g_chip_ident = tvaudio_g_chip_ident,
+ .queryctrl = tvaudio_queryctrl,
+ .g_ctrl = tvaudio_g_ctrl,
+ .s_ctrl = tvaudio_s_ctrl,
+ .s_std = tvaudio_s_std,
+};
+
+static const struct v4l2_subdev_tuner_ops tvaudio_tuner_ops = {
+ .s_radio = tvaudio_s_radio,
+ .s_frequency = tvaudio_s_frequency,
+ .s_tuner = tvaudio_s_tuner,
+ .g_tuner = tvaudio_g_tuner,
+};
+
+static const struct v4l2_subdev_audio_ops tvaudio_audio_ops = {
+ .s_routing = tvaudio_s_routing,
+};
+
+static const struct v4l2_subdev_ops tvaudio_ops = {
+ .core = &tvaudio_core_ops,
+ .tuner = &tvaudio_tuner_ops,
+ .audio = &tvaudio_audio_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+
+/* i2c registration */
+
+static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct CHIPSTATE *chip;
+ struct CHIPDESC *desc;
+ struct v4l2_subdev *sd;
+
+ if (debug) {
+ printk(KERN_INFO "tvaudio: TV audio decoder + audio/video mux driver\n");
+ printk(KERN_INFO "tvaudio: known chips: ");
+ for (desc = chiplist; desc->name != NULL; desc++)
+ printk("%s%s", (desc == chiplist) ? "" : ", ", desc->name);
+ printk("\n");
+ }
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+ sd = &chip->sd;
+ v4l2_i2c_subdev_init(sd, client, &tvaudio_ops);
+
+ /* find description for the chip */
+ v4l2_dbg(1, debug, sd, "chip found @ 0x%x\n", client->addr<<1);
+ for (desc = chiplist; desc->name != NULL; desc++) {
+ if (0 == *(desc->insmodopt))
+ continue;
+ if (client->addr < desc->addr_lo ||
+ client->addr > desc->addr_hi)
+ continue;
+ if (desc->checkit && !desc->checkit(chip))
+ continue;
+ break;
+ }
+ if (desc->name == NULL) {
+ v4l2_dbg(1, debug, sd, "no matching chip description found\n");
+ kfree(chip);
+ return -EIO;
+ }
+ v4l2_info(sd, "%s found @ 0x%x (%s)\n", desc->name, client->addr<<1, client->adapter->name);
+ if (desc->flags) {
+ v4l2_dbg(1, debug, sd, "matches:%s%s%s.\n",
+ (desc->flags & CHIP_HAS_VOLUME) ? " volume" : "",
+ (desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "",
+ (desc->flags & CHIP_HAS_INPUTSEL) ? " audiomux" : "");
+ }
+
+ /* fill required data structures */
+ if (!id)
+ strlcpy(client->name, desc->name, I2C_NAME_SIZE);
+ chip->desc = desc;
+ chip->shadow.count = desc->registers+1;
+ chip->prevmode = -1;
+ chip->audmode = V4L2_TUNER_MODE_LANG1;
+
+ /* initialization */
+ if (desc->initialize != NULL)
+ desc->initialize(chip);
+ else
+ chip_cmd(chip, "init", &desc->init);
+
+ if (desc->flags & CHIP_HAS_VOLUME) {
+ if (!desc->volfunc) {
+ /* This shouldn't be happen. Warn user, but keep working
+ without volume controls
+ */
+ v4l2_info(sd, "volume callback undefined!\n");
+ desc->flags &= ~CHIP_HAS_VOLUME;
+ } else {
+ chip->left = desc->leftinit ? desc->leftinit : 65535;
+ chip->right = desc->rightinit ? desc->rightinit : 65535;
+ chip_write(chip, desc->leftreg,
+ desc->volfunc(chip->left));
+ chip_write(chip, desc->rightreg,
+ desc->volfunc(chip->right));
+ }
+ }
+ if (desc->flags & CHIP_HAS_BASSTREBLE) {
+ if (!desc->bassfunc || !desc->treblefunc) {
+ /* This shouldn't be happen. Warn user, but keep working
+ without bass/treble controls
+ */
+ v4l2_info(sd, "bass/treble callbacks undefined!\n");
+ desc->flags &= ~CHIP_HAS_BASSTREBLE;
+ } else {
+ chip->treble = desc->trebleinit ?
+ desc->trebleinit : 32768;
+ chip->bass = desc->bassinit ?
+ desc->bassinit : 32768;
+ chip_write(chip, desc->bassreg,
+ desc->bassfunc(chip->bass));
+ chip_write(chip, desc->treblereg,
+ desc->treblefunc(chip->treble));
+ }
+ }
+
+ chip->thread = NULL;
+ init_timer(&chip->wt);
+ if (desc->flags & CHIP_NEED_CHECKMODE) {
+ if (!desc->getmode || !desc->setmode) {
+ /* This shouldn't be happen. Warn user, but keep working
+ without kthread
+ */
+ v4l2_info(sd, "set/get mode callbacks undefined!\n");
+ return 0;
+ }
+ /* start async thread */
+ chip->wt.function = chip_thread_wake;
+ chip->wt.data = (unsigned long)chip;
+ chip->thread = kthread_run(chip_thread, chip, client->name);
+ if (IS_ERR(chip->thread)) {
+ v4l2_warn(sd, "failed to create kthread\n");
+ chip->thread = NULL;
+ }
+ }
+ return 0;
+}
+
+static int tvaudio_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct CHIPSTATE *chip = to_state(sd);
+
+ del_timer_sync(&chip->wt);
+ if (chip->thread) {
+ /* shutdown async thread */
+ kthread_stop(chip->thread);
+ chip->thread = NULL;
+ }
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(chip);
+ return 0;
+}
+
+/* This driver supports many devices and the idea is to let the driver
+ detect which device is present. So rather than listing all supported
+ devices here, we pretend to support a single, fake device type. */
+static const struct i2c_device_id tvaudio_id[] = {
+ { "tvaudio", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tvaudio_id);
+
+static struct i2c_driver tvaudio_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "tvaudio",
+ },
+ .probe = tvaudio_probe,
+ .remove = tvaudio_remove,
+ .id_table = tvaudio_id,
+};
+
+module_i2c_driver(tvaudio_driver);
diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c
new file mode 100644
index 00000000..3b6cf034
--- /dev/null
+++ b/drivers/media/video/tveeprom.c
@@ -0,0 +1,792 @@
+/*
+ * tveeprom - eeprom decoder for tvcard configuration eeproms
+ *
+ * Data and decoding routines shamelessly borrowed from bttv-cards.c
+ * eeprom access routine shamelessly borrowed from bttv-if.c
+ * which are:
+
+ Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
+ & Marcus Metzler (mocm@thp.uni-koeln.de)
+ (c) 1999-2001 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+
+ * Adjustments to fit a more general model and all bugs:
+
+ Copyright (C) 2003 John Klar <linpvr at projectplasma.com>
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+
+#include <media/tuner.h>
+#include <media/tveeprom.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
+
+MODULE_DESCRIPTION("i2c Hauppauge eeprom decoder driver");
+MODULE_AUTHOR("John Klar");
+MODULE_LICENSE("GPL");
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+#define STRM(array, i) \
+ (i < sizeof(array) / sizeof(char *) ? array[i] : "unknown")
+
+#define tveeprom_info(fmt, arg...) \
+ v4l_printk(KERN_INFO, "tveeprom", c->adapter, c->addr, fmt , ## arg)
+#define tveeprom_warn(fmt, arg...) \
+ v4l_printk(KERN_WARNING, "tveeprom", c->adapter, c->addr, fmt , ## arg)
+#define tveeprom_dbg(fmt, arg...) do { \
+ if (debug) \
+ v4l_printk(KERN_DEBUG, "tveeprom", \
+ c->adapter, c->addr, fmt , ## arg); \
+ } while (0)
+
+/*
+ * The Hauppauge eeprom uses an 8bit field to determine which
+ * tuner formats the tuner supports.
+ */
+static struct HAUPPAUGE_TUNER_FMT
+{
+ int id;
+ char *name;
+}
+hauppauge_tuner_fmt[] =
+{
+ { V4L2_STD_UNKNOWN, " UNKNOWN" },
+ { V4L2_STD_UNKNOWN, " FM" },
+ { V4L2_STD_B|V4L2_STD_GH, " PAL(B/G)" },
+ { V4L2_STD_MN, " NTSC(M)" },
+ { V4L2_STD_PAL_I, " PAL(I)" },
+ { V4L2_STD_SECAM_L|V4L2_STD_SECAM_LC, " SECAM(L/L')" },
+ { V4L2_STD_DK, " PAL(D/D1/K)" },
+ { V4L2_STD_ATSC, " ATSC/DVB Digital" },
+};
+
+/* This is the full list of possible tuners. Many thanks to Hauppauge for
+ supplying this information. Note that many tuners where only used for
+ testing and never made it to the outside world. So you will only see
+ a subset in actual produced cards. */
+static struct HAUPPAUGE_TUNER
+{
+ int id;
+ char *name;
+}
+hauppauge_tuner[] =
+{
+ /* 0-9 */
+ { TUNER_ABSENT, "None" },
+ { TUNER_ABSENT, "External" },
+ { TUNER_ABSENT, "Unspecified" },
+ { TUNER_PHILIPS_PAL, "Philips FI1216" },
+ { TUNER_PHILIPS_SECAM, "Philips FI1216MF" },
+ { TUNER_PHILIPS_NTSC, "Philips FI1236" },
+ { TUNER_PHILIPS_PAL_I, "Philips FI1246" },
+ { TUNER_PHILIPS_PAL_DK, "Philips FI1256" },
+ { TUNER_PHILIPS_PAL, "Philips FI1216 MK2" },
+ { TUNER_PHILIPS_SECAM, "Philips FI1216MF MK2" },
+ /* 10-19 */
+ { TUNER_PHILIPS_NTSC, "Philips FI1236 MK2" },
+ { TUNER_PHILIPS_PAL_I, "Philips FI1246 MK2" },
+ { TUNER_PHILIPS_PAL_DK, "Philips FI1256 MK2" },
+ { TUNER_TEMIC_NTSC, "Temic 4032FY5" },
+ { TUNER_TEMIC_PAL, "Temic 4002FH5" },
+ { TUNER_TEMIC_PAL_I, "Temic 4062FY5" },
+ { TUNER_PHILIPS_PAL, "Philips FR1216 MK2" },
+ { TUNER_PHILIPS_SECAM, "Philips FR1216MF MK2" },
+ { TUNER_PHILIPS_NTSC, "Philips FR1236 MK2" },
+ { TUNER_PHILIPS_PAL_I, "Philips FR1246 MK2" },
+ /* 20-29 */
+ { TUNER_PHILIPS_PAL_DK, "Philips FR1256 MK2" },
+ { TUNER_PHILIPS_PAL, "Philips FM1216" },
+ { TUNER_PHILIPS_SECAM, "Philips FM1216MF" },
+ { TUNER_PHILIPS_NTSC, "Philips FM1236" },
+ { TUNER_PHILIPS_PAL_I, "Philips FM1246" },
+ { TUNER_PHILIPS_PAL_DK, "Philips FM1256" },
+ { TUNER_TEMIC_4036FY5_NTSC, "Temic 4036FY5" },
+ { TUNER_ABSENT, "Samsung TCPN9082D" },
+ { TUNER_ABSENT, "Samsung TCPM9092P" },
+ { TUNER_TEMIC_4006FH5_PAL, "Temic 4006FH5" },
+ /* 30-39 */
+ { TUNER_ABSENT, "Samsung TCPN9085D" },
+ { TUNER_ABSENT, "Samsung TCPB9085P" },
+ { TUNER_ABSENT, "Samsung TCPL9091P" },
+ { TUNER_TEMIC_4039FR5_NTSC, "Temic 4039FR5" },
+ { TUNER_PHILIPS_FQ1216ME, "Philips FQ1216 ME" },
+ { TUNER_TEMIC_4066FY5_PAL_I, "Temic 4066FY5" },
+ { TUNER_PHILIPS_NTSC, "Philips TD1536" },
+ { TUNER_PHILIPS_NTSC, "Philips TD1536D" },
+ { TUNER_PHILIPS_NTSC, "Philips FMR1236" }, /* mono radio */
+ { TUNER_ABSENT, "Philips FI1256MP" },
+ /* 40-49 */
+ { TUNER_ABSENT, "Samsung TCPQ9091P" },
+ { TUNER_TEMIC_4006FN5_MULTI_PAL, "Temic 4006FN5" },
+ { TUNER_TEMIC_4009FR5_PAL, "Temic 4009FR5" },
+ { TUNER_TEMIC_4046FM5, "Temic 4046FM5" },
+ { TUNER_TEMIC_4009FN5_MULTI_PAL_FM, "Temic 4009FN5" },
+ { TUNER_ABSENT, "Philips TD1536D FH 44"},
+ { TUNER_LG_NTSC_FM, "LG TP18NSR01F"},
+ { TUNER_LG_PAL_FM, "LG TP18PSB01D"},
+ { TUNER_LG_PAL, "LG TP18PSB11D"},
+ { TUNER_LG_PAL_I_FM, "LG TAPC-I001D"},
+ /* 50-59 */
+ { TUNER_LG_PAL_I, "LG TAPC-I701D"},
+ { TUNER_ABSENT, "Temic 4042FI5"},
+ { TUNER_MICROTUNE_4049FM5, "Microtune 4049 FM5"},
+ { TUNER_ABSENT, "LG TPI8NSR11F"},
+ { TUNER_ABSENT, "Microtune 4049 FM5 Alt I2C"},
+ { TUNER_PHILIPS_FM1216ME_MK3, "Philips FQ1216ME MK3"},
+ { TUNER_ABSENT, "Philips FI1236 MK3"},
+ { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216 ME MK3"},
+ { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK3"},
+ { TUNER_ABSENT, "Philips FM1216MP MK3"},
+ /* 60-69 */
+ { TUNER_PHILIPS_FM1216ME_MK3, "LG S001D MK3"},
+ { TUNER_ABSENT, "LG M001D MK3"},
+ { TUNER_PHILIPS_FM1216ME_MK3, "LG S701D MK3"},
+ { TUNER_ABSENT, "LG M701D MK3"},
+ { TUNER_ABSENT, "Temic 4146FM5"},
+ { TUNER_ABSENT, "Temic 4136FY5"},
+ { TUNER_ABSENT, "Temic 4106FH5"},
+ { TUNER_ABSENT, "Philips FQ1216LMP MK3"},
+ { TUNER_LG_NTSC_TAPE, "LG TAPE H001F MK3"},
+ { TUNER_LG_NTSC_TAPE, "LG TAPE H701F MK3"},
+ /* 70-79 */
+ { TUNER_ABSENT, "LG TALN H200T"},
+ { TUNER_ABSENT, "LG TALN H250T"},
+ { TUNER_ABSENT, "LG TALN M200T"},
+ { TUNER_ABSENT, "LG TALN Z200T"},
+ { TUNER_ABSENT, "LG TALN S200T"},
+ { TUNER_ABSENT, "Thompson DTT7595"},
+ { TUNER_ABSENT, "Thompson DTT7592"},
+ { TUNER_ABSENT, "Silicon TDA8275C1 8290"},
+ { TUNER_ABSENT, "Silicon TDA8275C1 8290 FM"},
+ { TUNER_ABSENT, "Thompson DTT757"},
+ /* 80-89 */
+ { TUNER_PHILIPS_FQ1216LME_MK3, "Philips FQ1216LME MK3"},
+ { TUNER_LG_PAL_NEW_TAPC, "LG TAPC G701D"},
+ { TUNER_LG_NTSC_NEW_TAPC, "LG TAPC H791F"},
+ { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MB 3"},
+ { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MI 3"},
+ { TUNER_TCL_2002N, "TCL 2002N 6A"},
+ { TUNER_PHILIPS_FM1236_MK3, "Philips FQ1236 MK3"},
+ { TUNER_SAMSUNG_TCPN_2121P30A, "Samsung TCPN 2121P30A"},
+ { TUNER_ABSENT, "Samsung TCPE 4121P30A"},
+ { TUNER_PHILIPS_FM1216ME_MK3, "TCL MFPE05 2"},
+ /* 90-99 */
+ { TUNER_ABSENT, "LG TALN H202T"},
+ { TUNER_PHILIPS_FQ1216AME_MK4, "Philips FQ1216AME MK4"},
+ { TUNER_PHILIPS_FQ1236A_MK4, "Philips FQ1236A MK4"},
+ { TUNER_ABSENT, "Philips FQ1286A MK4"},
+ { TUNER_ABSENT, "Philips FQ1216ME MK5"},
+ { TUNER_ABSENT, "Philips FQ1236 MK5"},
+ { TUNER_SAMSUNG_TCPG_6121P30A, "Samsung TCPG 6121P30A"},
+ { TUNER_TCL_2002MB, "TCL 2002MB_3H"},
+ { TUNER_ABSENT, "TCL 2002MI_3H"},
+ { TUNER_TCL_2002N, "TCL 2002N 5H"},
+ /* 100-109 */
+ { TUNER_PHILIPS_FMD1216ME_MK3, "Philips FMD1216ME"},
+ { TUNER_TEA5767, "Philips TEA5768HL FM Radio"},
+ { TUNER_ABSENT, "Panasonic ENV57H12D5"},
+ { TUNER_PHILIPS_FM1236_MK3, "TCL MFNM05-4"},
+ { TUNER_PHILIPS_FM1236_MK3, "TCL MNM05-4"},
+ { TUNER_PHILIPS_FM1216ME_MK3, "TCL MPE05-2"},
+ { TUNER_ABSENT, "TCL MQNM05-4"},
+ { TUNER_ABSENT, "LG TAPC-W701D"},
+ { TUNER_ABSENT, "TCL 9886P-WM"},
+ { TUNER_ABSENT, "TCL 1676NM-WM"},
+ /* 110-119 */
+ { TUNER_ABSENT, "Thompson DTT75105"},
+ { TUNER_ABSENT, "Conexant_CX24109"},
+ { TUNER_TCL_2002N, "TCL M2523_5N_E"},
+ { TUNER_TCL_2002MB, "TCL M2523_3DB_E"},
+ { TUNER_ABSENT, "Philips 8275A"},
+ { TUNER_ABSENT, "Microtune MT2060"},
+ { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK5"},
+ { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216ME MK5"},
+ { TUNER_ABSENT, "TCL M2523_3DI_E"},
+ { TUNER_ABSENT, "Samsung THPD5222FG30A"},
+ /* 120-129 */
+ { TUNER_XC2028, "Xceive XC3028"},
+ { TUNER_PHILIPS_FQ1216LME_MK3, "Philips FQ1216LME MK5"},
+ { TUNER_ABSENT, "Philips FQD1216LME"},
+ { TUNER_ABSENT, "Conexant CX24118A"},
+ { TUNER_ABSENT, "TCL DMF11WIP"},
+ { TUNER_ABSENT, "TCL MFNM05_4H_E"},
+ { TUNER_ABSENT, "TCL MNM05_4H_E"},
+ { TUNER_ABSENT, "TCL MPE05_2H_E"},
+ { TUNER_ABSENT, "TCL MQNM05_4_U"},
+ { TUNER_ABSENT, "TCL M2523_5NH_E"},
+ /* 130-139 */
+ { TUNER_ABSENT, "TCL M2523_3DBH_E"},
+ { TUNER_ABSENT, "TCL M2523_3DIH_E"},
+ { TUNER_ABSENT, "TCL MFPE05_2_U"},
+ { TUNER_PHILIPS_FMD1216MEX_MK3, "Philips FMD1216MEX"},
+ { TUNER_ABSENT, "Philips FRH2036B"},
+ { TUNER_ABSENT, "Panasonic ENGF75_01GF"},
+ { TUNER_ABSENT, "MaxLinear MXL5005"},
+ { TUNER_ABSENT, "MaxLinear MXL5003"},
+ { TUNER_ABSENT, "Xceive XC2028"},
+ { TUNER_ABSENT, "Microtune MT2131"},
+ /* 140-149 */
+ { TUNER_ABSENT, "Philips 8275A_8295"},
+ { TUNER_ABSENT, "TCL MF02GIP_5N_E"},
+ { TUNER_ABSENT, "TCL MF02GIP_3DB_E"},
+ { TUNER_ABSENT, "TCL MF02GIP_3DI_E"},
+ { TUNER_ABSENT, "Microtune MT2266"},
+ { TUNER_ABSENT, "TCL MF10WPP_4N_E"},
+ { TUNER_ABSENT, "LG TAPQ_H702F"},
+ { TUNER_ABSENT, "TCL M09WPP_4N_E"},
+ { TUNER_ABSENT, "MaxLinear MXL5005_v2"},
+ { TUNER_PHILIPS_TDA8290, "Philips 18271_8295"},
+ /* 150-159 */
+ { TUNER_XC5000, "Xceive XC5000"},
+ { TUNER_ABSENT, "Xceive XC3028L"},
+ { TUNER_ABSENT, "NXP 18271C2_716x"},
+ { TUNER_ABSENT, "Xceive XC4000"},
+ { TUNER_ABSENT, "Dibcom 7070"},
+ { TUNER_PHILIPS_TDA8290, "NXP 18271C2"},
+ { TUNER_ABSENT, "Siano SMS1010"},
+ { TUNER_ABSENT, "Siano SMS1150"},
+ { TUNER_ABSENT, "MaxLinear 5007"},
+ { TUNER_ABSENT, "TCL M09WPP_2P_E"},
+ /* 160-169 */
+ { TUNER_ABSENT, "Siano SMS1180"},
+ { TUNER_ABSENT, "Maxim_MAX2165"},
+ { TUNER_ABSENT, "Siano SMS1140"},
+ { TUNER_ABSENT, "Siano SMS1150 B1"},
+ { TUNER_ABSENT, "MaxLinear 111"},
+ { TUNER_ABSENT, "Dibcom 7770"},
+ { TUNER_ABSENT, "Siano SMS1180VNS"},
+ { TUNER_ABSENT, "Siano SMS1184"},
+ { TUNER_PHILIPS_FQ1236_MK5, "TCL M30WTP-4N-E"},
+ { TUNER_ABSENT, "TCL_M11WPP_2PN_E"},
+ /* 170-179 */
+ { TUNER_ABSENT, "MaxLinear 301"},
+ { TUNER_ABSENT, "Mirics MSi001"},
+ { TUNER_ABSENT, "MaxLinear MxL241SF"},
+ { TUNER_XC5000C, "Xceive XC5000C"},
+ { TUNER_ABSENT, "Montage M68TS2020"},
+ { TUNER_ABSENT, "Siano SMS1530"},
+ { TUNER_ABSENT, "Dibcom 7090"},
+ { TUNER_ABSENT, "Xceive XC5200C"},
+ { TUNER_ABSENT, "NXP 18273"},
+ { TUNER_ABSENT, "Montage M88TS2022"},
+ /* 180-189 */
+ { TUNER_ABSENT, "NXP 18272M"},
+ { TUNER_ABSENT, "NXP 18272S"},
+};
+
+/* Use V4L2_IDENT_AMBIGUOUS for those audio 'chips' that are
+ * internal to a video chip, i.e. not a separate audio chip. */
+static struct HAUPPAUGE_AUDIOIC
+{
+ u32 id;
+ char *name;
+}
+audioIC[] =
+{
+ /* 0-4 */
+ { V4L2_IDENT_NONE, "None" },
+ { V4L2_IDENT_UNKNOWN, "TEA6300" },
+ { V4L2_IDENT_UNKNOWN, "TEA6320" },
+ { V4L2_IDENT_UNKNOWN, "TDA9850" },
+ { V4L2_IDENT_MSPX4XX, "MSP3400C" },
+ /* 5-9 */
+ { V4L2_IDENT_MSPX4XX, "MSP3410D" },
+ { V4L2_IDENT_MSPX4XX, "MSP3415" },
+ { V4L2_IDENT_MSPX4XX, "MSP3430" },
+ { V4L2_IDENT_MSPX4XX, "MSP3438" },
+ { V4L2_IDENT_UNKNOWN, "CS5331" },
+ /* 10-14 */
+ { V4L2_IDENT_MSPX4XX, "MSP3435" },
+ { V4L2_IDENT_MSPX4XX, "MSP3440" },
+ { V4L2_IDENT_MSPX4XX, "MSP3445" },
+ { V4L2_IDENT_MSPX4XX, "MSP3411" },
+ { V4L2_IDENT_MSPX4XX, "MSP3416" },
+ /* 15-19 */
+ { V4L2_IDENT_MSPX4XX, "MSP3425" },
+ { V4L2_IDENT_MSPX4XX, "MSP3451" },
+ { V4L2_IDENT_MSPX4XX, "MSP3418" },
+ { V4L2_IDENT_UNKNOWN, "Type 0x12" },
+ { V4L2_IDENT_UNKNOWN, "OKI7716" },
+ /* 20-24 */
+ { V4L2_IDENT_MSPX4XX, "MSP4410" },
+ { V4L2_IDENT_MSPX4XX, "MSP4420" },
+ { V4L2_IDENT_MSPX4XX, "MSP4440" },
+ { V4L2_IDENT_MSPX4XX, "MSP4450" },
+ { V4L2_IDENT_MSPX4XX, "MSP4408" },
+ /* 25-29 */
+ { V4L2_IDENT_MSPX4XX, "MSP4418" },
+ { V4L2_IDENT_MSPX4XX, "MSP4428" },
+ { V4L2_IDENT_MSPX4XX, "MSP4448" },
+ { V4L2_IDENT_MSPX4XX, "MSP4458" },
+ { V4L2_IDENT_MSPX4XX, "Type 0x1d" },
+ /* 30-34 */
+ { V4L2_IDENT_AMBIGUOUS, "CX880" },
+ { V4L2_IDENT_AMBIGUOUS, "CX881" },
+ { V4L2_IDENT_AMBIGUOUS, "CX883" },
+ { V4L2_IDENT_AMBIGUOUS, "CX882" },
+ { V4L2_IDENT_AMBIGUOUS, "CX25840" },
+ /* 35-39 */
+ { V4L2_IDENT_AMBIGUOUS, "CX25841" },
+ { V4L2_IDENT_AMBIGUOUS, "CX25842" },
+ { V4L2_IDENT_AMBIGUOUS, "CX25843" },
+ { V4L2_IDENT_AMBIGUOUS, "CX23418" },
+ { V4L2_IDENT_AMBIGUOUS, "CX23885" },
+ /* 40-44 */
+ { V4L2_IDENT_AMBIGUOUS, "CX23888" },
+ { V4L2_IDENT_AMBIGUOUS, "SAA7131" },
+ { V4L2_IDENT_AMBIGUOUS, "CX23887" },
+ { V4L2_IDENT_AMBIGUOUS, "SAA7164" },
+ { V4L2_IDENT_AMBIGUOUS, "AU8522" },
+};
+
+/* This list is supplied by Hauppauge. Thanks! */
+static const char *decoderIC[] = {
+ /* 0-4 */
+ "None", "BT815", "BT817", "BT819", "BT815A",
+ /* 5-9 */
+ "BT817A", "BT819A", "BT827", "BT829", "BT848",
+ /* 10-14 */
+ "BT848A", "BT849A", "BT829A", "BT827A", "BT878",
+ /* 15-19 */
+ "BT879", "BT880", "VPX3226E", "SAA7114", "SAA7115",
+ /* 20-24 */
+ "CX880", "CX881", "CX883", "SAA7111", "SAA7113",
+ /* 25-29 */
+ "CX882", "TVP5150A", "CX25840", "CX25841", "CX25842",
+ /* 30-34 */
+ "CX25843", "CX23418", "NEC61153", "CX23885", "CX23888",
+ /* 35-39 */
+ "SAA7131", "CX25837", "CX23887", "CX23885A", "CX23887A",
+ /* 40-42 */
+ "SAA7164", "CX23885B", "AU8522"
+};
+
+static int hasRadioTuner(int tunerType)
+{
+ switch (tunerType) {
+ case 18: /* PNPEnv_TUNER_FR1236_MK2 */
+ case 23: /* PNPEnv_TUNER_FM1236 */
+ case 38: /* PNPEnv_TUNER_FMR1236 */
+ case 16: /* PNPEnv_TUNER_FR1216_MK2 */
+ case 19: /* PNPEnv_TUNER_FR1246_MK2 */
+ case 21: /* PNPEnv_TUNER_FM1216 */
+ case 24: /* PNPEnv_TUNER_FM1246 */
+ case 17: /* PNPEnv_TUNER_FR1216MF_MK2 */
+ case 22: /* PNPEnv_TUNER_FM1216MF */
+ case 20: /* PNPEnv_TUNER_FR1256_MK2 */
+ case 25: /* PNPEnv_TUNER_FM1256 */
+ case 33: /* PNPEnv_TUNER_4039FR5 */
+ case 42: /* PNPEnv_TUNER_4009FR5 */
+ case 52: /* PNPEnv_TUNER_4049FM5 */
+ case 54: /* PNPEnv_TUNER_4049FM5_AltI2C */
+ case 44: /* PNPEnv_TUNER_4009FN5 */
+ case 31: /* PNPEnv_TUNER_TCPB9085P */
+ case 30: /* PNPEnv_TUNER_TCPN9085D */
+ case 46: /* PNPEnv_TUNER_TP18NSR01F */
+ case 47: /* PNPEnv_TUNER_TP18PSB01D */
+ case 49: /* PNPEnv_TUNER_TAPC_I001D */
+ case 60: /* PNPEnv_TUNER_TAPE_S001D_MK3 */
+ case 57: /* PNPEnv_TUNER_FM1216ME_MK3 */
+ case 59: /* PNPEnv_TUNER_FM1216MP_MK3 */
+ case 58: /* PNPEnv_TUNER_FM1236_MK3 */
+ case 68: /* PNPEnv_TUNER_TAPE_H001F_MK3 */
+ case 61: /* PNPEnv_TUNER_TAPE_M001D_MK3 */
+ case 78: /* PNPEnv_TUNER_TDA8275C1_8290_FM */
+ case 89: /* PNPEnv_TUNER_TCL_MFPE05_2 */
+ case 92: /* PNPEnv_TUNER_PHILIPS_FQ1236A_MK4 */
+ case 105:
+ return 1;
+ }
+ return 0;
+}
+
+void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee,
+ unsigned char *eeprom_data)
+{
+ /* ----------------------------------------------
+ ** The hauppauge eeprom format is tagged
+ **
+ ** if packet[0] == 0x84, then packet[0..1] == length
+ ** else length = packet[0] & 3f;
+ ** if packet[0] & f8 == f8, then EOD and packet[1] == checksum
+ **
+ ** In our (ivtv) case we're interested in the following:
+ ** tuner type: tag [00].05 or [0a].01 (index into hauppauge_tuner)
+ ** tuner fmts: tag [00].04 or [0a].00 (bitmask index into
+ ** hauppauge_tuner_fmt)
+ ** radio: tag [00].{last} or [0e].00 (bitmask. bit2=FM)
+ ** audio proc: tag [02].01 or [05].00 (mask with 0x7f)
+ ** decoder proc: tag [09].01)
+
+ ** Fun info:
+ ** model: tag [00].07-08 or [06].00-01
+ ** revision: tag [00].09-0b or [06].04-06
+ ** serial#: tag [01].05-07 or [04].04-06
+
+ ** # of inputs/outputs ???
+ */
+
+ int i, j, len, done, beenhere, tag, start;
+
+ int tuner1 = 0, t_format1 = 0, audioic = -1;
+ char *t_name1 = NULL;
+ const char *t_fmt_name1[8] = { " none", "", "", "", "", "", "", "" };
+
+ int tuner2 = 0, t_format2 = 0;
+ char *t_name2 = NULL;
+ const char *t_fmt_name2[8] = { " none", "", "", "", "", "", "", "" };
+
+ memset(tvee, 0, sizeof(*tvee));
+ tvee->tuner_type = TUNER_ABSENT;
+ tvee->tuner2_type = TUNER_ABSENT;
+
+ done = len = beenhere = 0;
+
+ /* Different eeprom start offsets for em28xx, cx2388x and cx23418 */
+ if (eeprom_data[0] == 0x1a &&
+ eeprom_data[1] == 0xeb &&
+ eeprom_data[2] == 0x67 &&
+ eeprom_data[3] == 0x95)
+ start = 0xa0; /* Generic em28xx offset */
+ else if ((eeprom_data[0] & 0xe1) == 0x01 &&
+ eeprom_data[1] == 0x00 &&
+ eeprom_data[2] == 0x00 &&
+ eeprom_data[8] == 0x84)
+ start = 8; /* Generic cx2388x offset */
+ else if (eeprom_data[1] == 0x70 &&
+ eeprom_data[2] == 0x00 &&
+ eeprom_data[4] == 0x74 &&
+ eeprom_data[8] == 0x84)
+ start = 8; /* Generic cx23418 offset (models 74xxx) */
+ else
+ start = 0;
+
+ for (i = start; !done && i < 256; i += len) {
+ if (eeprom_data[i] == 0x84) {
+ len = eeprom_data[i + 1] + (eeprom_data[i + 2] << 8);
+ i += 3;
+ } else if ((eeprom_data[i] & 0xf0) == 0x70) {
+ if (eeprom_data[i] & 0x08) {
+ /* verify checksum! */
+ done = 1;
+ break;
+ }
+ len = eeprom_data[i] & 0x07;
+ ++i;
+ } else {
+ tveeprom_warn("Encountered bad packet header [%02x]. "
+ "Corrupt or not a Hauppauge eeprom.\n",
+ eeprom_data[i]);
+ return;
+ }
+
+ if (debug) {
+ tveeprom_info("Tag [%02x] + %d bytes:",
+ eeprom_data[i], len - 1);
+ for (j = 1; j < len; j++)
+ printk(KERN_CONT " %02x", eeprom_data[i + j]);
+ printk(KERN_CONT "\n");
+ }
+
+ /* process by tag */
+ tag = eeprom_data[i];
+ switch (tag) {
+ case 0x00:
+ /* tag: 'Comprehensive' */
+ tuner1 = eeprom_data[i+6];
+ t_format1 = eeprom_data[i+5];
+ tvee->has_radio = eeprom_data[i+len-1];
+ /* old style tag, don't know how to detect
+ IR presence, mark as unknown. */
+ tvee->has_ir = 0;
+ tvee->model =
+ eeprom_data[i+8] +
+ (eeprom_data[i+9] << 8);
+ tvee->revision = eeprom_data[i+10] +
+ (eeprom_data[i+11] << 8) +
+ (eeprom_data[i+12] << 16);
+ break;
+
+ case 0x01:
+ /* tag: 'SerialID' */
+ tvee->serial_number =
+ eeprom_data[i+6] +
+ (eeprom_data[i+7] << 8) +
+ (eeprom_data[i+8] << 16);
+ break;
+
+ case 0x02:
+ /* tag 'AudioInfo'
+ Note mask with 0x7F, high bit used on some older models
+ to indicate 4052 mux was removed in favor of using MSP
+ inputs directly. */
+ audioic = eeprom_data[i+2] & 0x7f;
+ if (audioic < ARRAY_SIZE(audioIC))
+ tvee->audio_processor = audioIC[audioic].id;
+ else
+ tvee->audio_processor = V4L2_IDENT_UNKNOWN;
+ break;
+
+ /* case 0x03: tag 'EEInfo' */
+
+ case 0x04:
+ /* tag 'SerialID2' */
+ tvee->serial_number =
+ eeprom_data[i+5] +
+ (eeprom_data[i+6] << 8) +
+ (eeprom_data[i+7] << 16);
+
+ if ((eeprom_data[i + 8] & 0xf0) &&
+ (tvee->serial_number < 0xffffff)) {
+ tvee->MAC_address[0] = 0x00;
+ tvee->MAC_address[1] = 0x0D;
+ tvee->MAC_address[2] = 0xFE;
+ tvee->MAC_address[3] = eeprom_data[i + 7];
+ tvee->MAC_address[4] = eeprom_data[i + 6];
+ tvee->MAC_address[5] = eeprom_data[i + 5];
+ tvee->has_MAC_address = 1;
+ }
+ break;
+
+ case 0x05:
+ /* tag 'Audio2'
+ Note mask with 0x7F, high bit used on some older models
+ to indicate 4052 mux was removed in favor of using MSP
+ inputs directly. */
+ audioic = eeprom_data[i+1] & 0x7f;
+ if (audioic < ARRAY_SIZE(audioIC))
+ tvee->audio_processor = audioIC[audioic].id;
+ else
+ tvee->audio_processor = V4L2_IDENT_UNKNOWN;
+
+ break;
+
+ case 0x06:
+ /* tag 'ModelRev' */
+ tvee->model =
+ eeprom_data[i + 1] +
+ (eeprom_data[i + 2] << 8) +
+ (eeprom_data[i + 3] << 16) +
+ (eeprom_data[i + 4] << 24);
+ tvee->revision =
+ eeprom_data[i + 5] +
+ (eeprom_data[i + 6] << 8) +
+ (eeprom_data[i + 7] << 16);
+ break;
+
+ case 0x07:
+ /* tag 'Details': according to Hauppauge not interesting
+ on any PCI-era or later boards. */
+ break;
+
+ /* there is no tag 0x08 defined */
+
+ case 0x09:
+ /* tag 'Video' */
+ tvee->decoder_processor = eeprom_data[i + 1];
+ break;
+
+ case 0x0a:
+ /* tag 'Tuner' */
+ if (beenhere == 0) {
+ tuner1 = eeprom_data[i + 2];
+ t_format1 = eeprom_data[i + 1];
+ beenhere = 1;
+ } else {
+ /* a second (radio) tuner may be present */
+ tuner2 = eeprom_data[i + 2];
+ t_format2 = eeprom_data[i + 1];
+ /* not a TV tuner? */
+ if (t_format2 == 0)
+ tvee->has_radio = 1; /* must be radio */
+ }
+ break;
+
+ case 0x0b:
+ /* tag 'Inputs': according to Hauppauge this is specific
+ to each driver family, so no good assumptions can be
+ made. */
+ break;
+
+ /* case 0x0c: tag 'Balun' */
+ /* case 0x0d: tag 'Teletext' */
+
+ case 0x0e:
+ /* tag: 'Radio' */
+ tvee->has_radio = eeprom_data[i+1];
+ break;
+
+ case 0x0f:
+ /* tag 'IRInfo' */
+ tvee->has_ir = 1 | (eeprom_data[i+1] << 1);
+ break;
+
+ /* case 0x10: tag 'VBIInfo' */
+ /* case 0x11: tag 'QCInfo' */
+ /* case 0x12: tag 'InfoBits' */
+
+ default:
+ tveeprom_dbg("Not sure what to do with tag [%02x]\n",
+ tag);
+ /* dump the rest of the packet? */
+ }
+ }
+
+ if (!done) {
+ tveeprom_warn("Ran out of data!\n");
+ return;
+ }
+
+ if (tvee->revision != 0) {
+ tvee->rev_str[0] = 32 + ((tvee->revision >> 18) & 0x3f);
+ tvee->rev_str[1] = 32 + ((tvee->revision >> 12) & 0x3f);
+ tvee->rev_str[2] = 32 + ((tvee->revision >> 6) & 0x3f);
+ tvee->rev_str[3] = 32 + (tvee->revision & 0x3f);
+ tvee->rev_str[4] = 0;
+ }
+
+ if (hasRadioTuner(tuner1) && !tvee->has_radio) {
+ tveeprom_info("The eeprom says no radio is present, but the tuner type\n");
+ tveeprom_info("indicates otherwise. I will assume that radio is present.\n");
+ tvee->has_radio = 1;
+ }
+
+ if (tuner1 < ARRAY_SIZE(hauppauge_tuner)) {
+ tvee->tuner_type = hauppauge_tuner[tuner1].id;
+ t_name1 = hauppauge_tuner[tuner1].name;
+ } else {
+ t_name1 = "unknown";
+ }
+
+ if (tuner2 < ARRAY_SIZE(hauppauge_tuner)) {
+ tvee->tuner2_type = hauppauge_tuner[tuner2].id;
+ t_name2 = hauppauge_tuner[tuner2].name;
+ } else {
+ t_name2 = "unknown";
+ }
+
+ tvee->tuner_hauppauge_model = tuner1;
+ tvee->tuner2_hauppauge_model = tuner2;
+ tvee->tuner_formats = 0;
+ tvee->tuner2_formats = 0;
+ for (i = j = 0; i < 8; i++) {
+ if (t_format1 & (1 << i)) {
+ tvee->tuner_formats |= hauppauge_tuner_fmt[i].id;
+ t_fmt_name1[j++] = hauppauge_tuner_fmt[i].name;
+ }
+ }
+ for (i = j = 0; i < 8; i++) {
+ if (t_format2 & (1 << i)) {
+ tvee->tuner2_formats |= hauppauge_tuner_fmt[i].id;
+ t_fmt_name2[j++] = hauppauge_tuner_fmt[i].name;
+ }
+ }
+
+ tveeprom_info("Hauppauge model %d, rev %s, serial# %d\n",
+ tvee->model, tvee->rev_str, tvee->serial_number);
+ if (tvee->has_MAC_address == 1)
+ tveeprom_info("MAC address is %pM\n", tvee->MAC_address);
+ tveeprom_info("tuner model is %s (idx %d, type %d)\n",
+ t_name1, tuner1, tvee->tuner_type);
+ tveeprom_info("TV standards%s%s%s%s%s%s%s%s (eeprom 0x%02x)\n",
+ t_fmt_name1[0], t_fmt_name1[1], t_fmt_name1[2],
+ t_fmt_name1[3], t_fmt_name1[4], t_fmt_name1[5],
+ t_fmt_name1[6], t_fmt_name1[7], t_format1);
+ if (tuner2)
+ tveeprom_info("second tuner model is %s (idx %d, type %d)\n",
+ t_name2, tuner2, tvee->tuner2_type);
+ if (t_format2)
+ tveeprom_info("TV standards%s%s%s%s%s%s%s%s (eeprom 0x%02x)\n",
+ t_fmt_name2[0], t_fmt_name2[1], t_fmt_name2[2],
+ t_fmt_name2[3], t_fmt_name2[4], t_fmt_name2[5],
+ t_fmt_name2[6], t_fmt_name2[7], t_format2);
+ if (audioic < 0) {
+ tveeprom_info("audio processor is unknown (no idx)\n");
+ tvee->audio_processor = V4L2_IDENT_UNKNOWN;
+ } else {
+ if (audioic < ARRAY_SIZE(audioIC))
+ tveeprom_info("audio processor is %s (idx %d)\n",
+ audioIC[audioic].name, audioic);
+ else
+ tveeprom_info("audio processor is unknown (idx %d)\n",
+ audioic);
+ }
+ if (tvee->decoder_processor)
+ tveeprom_info("decoder processor is %s (idx %d)\n",
+ STRM(decoderIC, tvee->decoder_processor),
+ tvee->decoder_processor);
+ if (tvee->has_ir)
+ tveeprom_info("has %sradio, has %sIR receiver, has %sIR transmitter\n",
+ tvee->has_radio ? "" : "no ",
+ (tvee->has_ir & 2) ? "" : "no ",
+ (tvee->has_ir & 4) ? "" : "no ");
+ else
+ tveeprom_info("has %sradio\n",
+ tvee->has_radio ? "" : "no ");
+}
+EXPORT_SYMBOL(tveeprom_hauppauge_analog);
+
+/* ----------------------------------------------------------------------- */
+/* generic helper functions */
+
+int tveeprom_read(struct i2c_client *c, unsigned char *eedata, int len)
+{
+ unsigned char buf;
+ int err;
+
+ buf = 0;
+ err = i2c_master_send(c, &buf, 1);
+ if (err != 1) {
+ tveeprom_info("Huh, no eeprom present (err=%d)?\n", err);
+ return -1;
+ }
+ err = i2c_master_recv(c, eedata, len);
+ if (err != len) {
+ tveeprom_warn("i2c eeprom read error (err=%d)\n", err);
+ return -1;
+ }
+ if (debug) {
+ int i;
+
+ tveeprom_info("full 256-byte eeprom dump:\n");
+ for (i = 0; i < len; i++) {
+ if (0 == (i % 16))
+ tveeprom_info("%02x:", i);
+ printk(KERN_CONT " %02x", eedata[i]);
+ if (15 == (i % 16))
+ printk(KERN_CONT "\n");
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL(tveeprom_read);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/tvp514x.c b/drivers/media/video/tvp514x.c
new file mode 100644
index 00000000..cd615c1d
--- /dev/null
+++ b/drivers/media/video/tvp514x.c
@@ -0,0 +1,1166 @@
+/*
+ * drivers/media/video/tvp514x.c
+ *
+ * TI TVP5146/47 decoder driver
+ *
+ * Copyright (C) 2008 Texas Instruments Inc
+ * Author: Vaibhav Hiremath <hvaibhav@ti.com>
+ *
+ * Contributors:
+ * Sivaraj R <sivaraj@ti.com>
+ * Brijesh R Jadav <brijesh.j@ti.com>
+ * Hardik Shah <hardik.shah@ti.com>
+ * Manjunath Hadli <mrh@ti.com>
+ * Karicheri Muralidharan <m-karicheri2@ti.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <linux/module.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-mediabus.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+#include <media/tvp514x.h>
+
+#include "tvp514x_regs.h"
+
+/* Module Name */
+#define TVP514X_MODULE_NAME "tvp514x"
+
+/* Private macros for TVP */
+#define I2C_RETRY_COUNT (5)
+#define LOCK_RETRY_COUNT (5)
+#define LOCK_RETRY_DELAY (200)
+
+/* Debug functions */
+static bool debug;
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("TVP514X linux decoder driver");
+MODULE_LICENSE("GPL");
+
+/* enum tvp514x_std - enum for supported standards */
+enum tvp514x_std {
+ STD_NTSC_MJ = 0,
+ STD_PAL_BDGHIN,
+ STD_INVALID
+};
+
+/**
+ * struct tvp514x_std_info - Structure to store standard informations
+ * @width: Line width in pixels
+ * @height:Number of active lines
+ * @video_std: Value to write in REG_VIDEO_STD register
+ * @standard: v4l2 standard structure information
+ */
+struct tvp514x_std_info {
+ unsigned long width;
+ unsigned long height;
+ u8 video_std;
+ struct v4l2_standard standard;
+};
+
+static struct tvp514x_reg tvp514x_reg_list_default[0x40];
+
+static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable);
+/**
+ * struct tvp514x_decoder - TVP5146/47 decoder object
+ * @sd: Subdevice Slave handle
+ * @tvp514x_regs: copy of hw's regs with preset values.
+ * @pdata: Board specific
+ * @ver: Chip version
+ * @streaming: TVP5146/47 decoder streaming - enabled or disabled.
+ * @current_std: Current standard
+ * @num_stds: Number of standards
+ * @std_list: Standards list
+ * @input: Input routing at chip level
+ * @output: Output routing at chip level
+ */
+struct tvp514x_decoder {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+ struct tvp514x_reg tvp514x_regs[ARRAY_SIZE(tvp514x_reg_list_default)];
+ const struct tvp514x_platform_data *pdata;
+
+ int ver;
+ int streaming;
+
+ enum tvp514x_std current_std;
+ int num_stds;
+ const struct tvp514x_std_info *std_list;
+ /* Input and Output Routing parameters */
+ u32 input;
+ u32 output;
+};
+
+/* TVP514x default register values */
+static struct tvp514x_reg tvp514x_reg_list_default[] = {
+ /* Composite selected */
+ {TOK_WRITE, REG_INPUT_SEL, 0x05},
+ {TOK_WRITE, REG_AFE_GAIN_CTRL, 0x0F},
+ /* Auto mode */
+ {TOK_WRITE, REG_VIDEO_STD, 0x00},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x00},
+ {TOK_SKIP, REG_AUTOSWITCH_MASK, 0x3F},
+ {TOK_WRITE, REG_COLOR_KILLER, 0x10},
+ {TOK_WRITE, REG_LUMA_CONTROL1, 0x00},
+ {TOK_WRITE, REG_LUMA_CONTROL2, 0x00},
+ {TOK_WRITE, REG_LUMA_CONTROL3, 0x02},
+ {TOK_WRITE, REG_BRIGHTNESS, 0x80},
+ {TOK_WRITE, REG_CONTRAST, 0x80},
+ {TOK_WRITE, REG_SATURATION, 0x80},
+ {TOK_WRITE, REG_HUE, 0x00},
+ {TOK_WRITE, REG_CHROMA_CONTROL1, 0x00},
+ {TOK_WRITE, REG_CHROMA_CONTROL2, 0x0E},
+ /* Reserved */
+ {TOK_SKIP, 0x0F, 0x00},
+ {TOK_WRITE, REG_COMP_PR_SATURATION, 0x80},
+ {TOK_WRITE, REG_COMP_Y_CONTRAST, 0x80},
+ {TOK_WRITE, REG_COMP_PB_SATURATION, 0x80},
+ /* Reserved */
+ {TOK_SKIP, 0x13, 0x00},
+ {TOK_WRITE, REG_COMP_Y_BRIGHTNESS, 0x80},
+ /* Reserved */
+ {TOK_SKIP, 0x15, 0x00},
+ /* NTSC timing */
+ {TOK_SKIP, REG_AVID_START_PIXEL_LSB, 0x55},
+ {TOK_SKIP, REG_AVID_START_PIXEL_MSB, 0x00},
+ {TOK_SKIP, REG_AVID_STOP_PIXEL_LSB, 0x25},
+ {TOK_SKIP, REG_AVID_STOP_PIXEL_MSB, 0x03},
+ /* NTSC timing */
+ {TOK_SKIP, REG_HSYNC_START_PIXEL_LSB, 0x00},
+ {TOK_SKIP, REG_HSYNC_START_PIXEL_MSB, 0x00},
+ {TOK_SKIP, REG_HSYNC_STOP_PIXEL_LSB, 0x40},
+ {TOK_SKIP, REG_HSYNC_STOP_PIXEL_MSB, 0x00},
+ /* NTSC timing */
+ {TOK_SKIP, REG_VSYNC_START_LINE_LSB, 0x04},
+ {TOK_SKIP, REG_VSYNC_START_LINE_MSB, 0x00},
+ {TOK_SKIP, REG_VSYNC_STOP_LINE_LSB, 0x07},
+ {TOK_SKIP, REG_VSYNC_STOP_LINE_MSB, 0x00},
+ /* NTSC timing */
+ {TOK_SKIP, REG_VBLK_START_LINE_LSB, 0x01},
+ {TOK_SKIP, REG_VBLK_START_LINE_MSB, 0x00},
+ {TOK_SKIP, REG_VBLK_STOP_LINE_LSB, 0x15},
+ {TOK_SKIP, REG_VBLK_STOP_LINE_MSB, 0x00},
+ /* Reserved */
+ {TOK_SKIP, 0x26, 0x00},
+ /* Reserved */
+ {TOK_SKIP, 0x27, 0x00},
+ {TOK_SKIP, REG_FAST_SWTICH_CONTROL, 0xCC},
+ /* Reserved */
+ {TOK_SKIP, 0x29, 0x00},
+ {TOK_SKIP, REG_FAST_SWTICH_SCART_DELAY, 0x00},
+ /* Reserved */
+ {TOK_SKIP, 0x2B, 0x00},
+ {TOK_SKIP, REG_SCART_DELAY, 0x00},
+ {TOK_SKIP, REG_CTI_DELAY, 0x00},
+ {TOK_SKIP, REG_CTI_CONTROL, 0x00},
+ /* Reserved */
+ {TOK_SKIP, 0x2F, 0x00},
+ /* Reserved */
+ {TOK_SKIP, 0x30, 0x00},
+ /* Reserved */
+ {TOK_SKIP, 0x31, 0x00},
+ /* HS, VS active high */
+ {TOK_WRITE, REG_SYNC_CONTROL, 0x00},
+ /* 10-bit BT.656 */
+ {TOK_WRITE, REG_OUTPUT_FORMATTER1, 0x00},
+ /* Enable clk & data */
+ {TOK_WRITE, REG_OUTPUT_FORMATTER2, 0x11},
+ /* Enable AVID & FLD */
+ {TOK_WRITE, REG_OUTPUT_FORMATTER3, 0xEE},
+ /* Enable VS & HS */
+ {TOK_WRITE, REG_OUTPUT_FORMATTER4, 0xAF},
+ {TOK_WRITE, REG_OUTPUT_FORMATTER5, 0xFF},
+ {TOK_WRITE, REG_OUTPUT_FORMATTER6, 0xFF},
+ /* Clear status */
+ {TOK_WRITE, REG_CLEAR_LOST_LOCK, 0x01},
+ {TOK_TERM, 0, 0},
+};
+
+/**
+ * Supported standards -
+ *
+ * Currently supports two standards only, need to add support for rest of the
+ * modes, like SECAM, etc...
+ */
+static const struct tvp514x_std_info tvp514x_std_list[] = {
+ /* Standard: STD_NTSC_MJ */
+ [STD_NTSC_MJ] = {
+ .width = NTSC_NUM_ACTIVE_PIXELS,
+ .height = NTSC_NUM_ACTIVE_LINES,
+ .video_std = VIDEO_STD_NTSC_MJ_BIT,
+ .standard = {
+ .index = 0,
+ .id = V4L2_STD_NTSC,
+ .name = "NTSC",
+ .frameperiod = {1001, 30000},
+ .framelines = 525
+ },
+ /* Standard: STD_PAL_BDGHIN */
+ },
+ [STD_PAL_BDGHIN] = {
+ .width = PAL_NUM_ACTIVE_PIXELS,
+ .height = PAL_NUM_ACTIVE_LINES,
+ .video_std = VIDEO_STD_PAL_BDGHIN_BIT,
+ .standard = {
+ .index = 1,
+ .id = V4L2_STD_PAL,
+ .name = "PAL",
+ .frameperiod = {1, 25},
+ .framelines = 625
+ },
+ },
+ /* Standard: need to add for additional standard */
+};
+
+
+static inline struct tvp514x_decoder *to_decoder(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct tvp514x_decoder, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct tvp514x_decoder, hdl)->sd;
+}
+
+
+/**
+ * tvp514x_read_reg() - Read a value from a register in an TVP5146/47.
+ * @sd: ptr to v4l2_subdev struct
+ * @reg: TVP5146/47 register address
+ *
+ * Returns value read if successful, or non-zero (-1) otherwise.
+ */
+static int tvp514x_read_reg(struct v4l2_subdev *sd, u8 reg)
+{
+ int err, retry = 0;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+read_again:
+
+ err = i2c_smbus_read_byte_data(client, reg);
+ if (err < 0) {
+ if (retry <= I2C_RETRY_COUNT) {
+ v4l2_warn(sd, "Read: retry ... %d\n", retry);
+ retry++;
+ msleep_interruptible(10);
+ goto read_again;
+ }
+ }
+
+ return err;
+}
+
+/**
+ * dump_reg() - dump the register content of TVP5146/47.
+ * @sd: ptr to v4l2_subdev struct
+ * @reg: TVP5146/47 register address
+ */
+static void dump_reg(struct v4l2_subdev *sd, u8 reg)
+{
+ u32 val;
+
+ val = tvp514x_read_reg(sd, reg);
+ v4l2_info(sd, "Reg(0x%.2X): 0x%.2X\n", reg, val);
+}
+
+/**
+ * tvp514x_write_reg() - Write a value to a register in TVP5146/47
+ * @sd: ptr to v4l2_subdev struct
+ * @reg: TVP5146/47 register address
+ * @val: value to be written to the register
+ *
+ * Write a value to a register in an TVP5146/47 decoder device.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int tvp514x_write_reg(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+ int err, retry = 0;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+write_again:
+
+ err = i2c_smbus_write_byte_data(client, reg, val);
+ if (err) {
+ if (retry <= I2C_RETRY_COUNT) {
+ v4l2_warn(sd, "Write: retry ... %d\n", retry);
+ retry++;
+ msleep_interruptible(10);
+ goto write_again;
+ }
+ }
+
+ return err;
+}
+
+/**
+ * tvp514x_write_regs() : Initializes a list of TVP5146/47 registers
+ * @sd: ptr to v4l2_subdev struct
+ * @reglist: list of TVP5146/47 registers and values
+ *
+ * Initializes a list of TVP5146/47 registers:-
+ * if token is TOK_TERM, then entire write operation terminates
+ * if token is TOK_DELAY, then a delay of 'val' msec is introduced
+ * if token is TOK_SKIP, then the register write is skipped
+ * if token is TOK_WRITE, then the register write is performed
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int tvp514x_write_regs(struct v4l2_subdev *sd,
+ const struct tvp514x_reg reglist[])
+{
+ int err;
+ const struct tvp514x_reg *next = reglist;
+
+ for (; next->token != TOK_TERM; next++) {
+ if (next->token == TOK_DELAY) {
+ msleep(next->val);
+ continue;
+ }
+
+ if (next->token == TOK_SKIP)
+ continue;
+
+ err = tvp514x_write_reg(sd, next->reg, (u8) next->val);
+ if (err) {
+ v4l2_err(sd, "Write failed. Err[%d]\n", err);
+ return err;
+ }
+ }
+ return 0;
+}
+
+/**
+ * tvp514x_query_current_std() : Query the current standard detected by TVP5146/47
+ * @sd: ptr to v4l2_subdev struct
+ *
+ * Returns the current standard detected by TVP5146/47, STD_INVALID if there is no
+ * standard detected.
+ */
+static enum tvp514x_std tvp514x_query_current_std(struct v4l2_subdev *sd)
+{
+ u8 std, std_status;
+
+ std = tvp514x_read_reg(sd, REG_VIDEO_STD);
+ if ((std & VIDEO_STD_MASK) == VIDEO_STD_AUTO_SWITCH_BIT)
+ /* use the standard status register */
+ std_status = tvp514x_read_reg(sd, REG_VIDEO_STD_STATUS);
+ else
+ /* use the standard register itself */
+ std_status = std;
+
+ switch (std_status & VIDEO_STD_MASK) {
+ case VIDEO_STD_NTSC_MJ_BIT:
+ return STD_NTSC_MJ;
+
+ case VIDEO_STD_PAL_BDGHIN_BIT:
+ return STD_PAL_BDGHIN;
+
+ default:
+ return STD_INVALID;
+ }
+
+ return STD_INVALID;
+}
+
+/* TVP5146/47 register dump function */
+static void tvp514x_reg_dump(struct v4l2_subdev *sd)
+{
+ dump_reg(sd, REG_INPUT_SEL);
+ dump_reg(sd, REG_AFE_GAIN_CTRL);
+ dump_reg(sd, REG_VIDEO_STD);
+ dump_reg(sd, REG_OPERATION_MODE);
+ dump_reg(sd, REG_COLOR_KILLER);
+ dump_reg(sd, REG_LUMA_CONTROL1);
+ dump_reg(sd, REG_LUMA_CONTROL2);
+ dump_reg(sd, REG_LUMA_CONTROL3);
+ dump_reg(sd, REG_BRIGHTNESS);
+ dump_reg(sd, REG_CONTRAST);
+ dump_reg(sd, REG_SATURATION);
+ dump_reg(sd, REG_HUE);
+ dump_reg(sd, REG_CHROMA_CONTROL1);
+ dump_reg(sd, REG_CHROMA_CONTROL2);
+ dump_reg(sd, REG_COMP_PR_SATURATION);
+ dump_reg(sd, REG_COMP_Y_CONTRAST);
+ dump_reg(sd, REG_COMP_PB_SATURATION);
+ dump_reg(sd, REG_COMP_Y_BRIGHTNESS);
+ dump_reg(sd, REG_AVID_START_PIXEL_LSB);
+ dump_reg(sd, REG_AVID_START_PIXEL_MSB);
+ dump_reg(sd, REG_AVID_STOP_PIXEL_LSB);
+ dump_reg(sd, REG_AVID_STOP_PIXEL_MSB);
+ dump_reg(sd, REG_HSYNC_START_PIXEL_LSB);
+ dump_reg(sd, REG_HSYNC_START_PIXEL_MSB);
+ dump_reg(sd, REG_HSYNC_STOP_PIXEL_LSB);
+ dump_reg(sd, REG_HSYNC_STOP_PIXEL_MSB);
+ dump_reg(sd, REG_VSYNC_START_LINE_LSB);
+ dump_reg(sd, REG_VSYNC_START_LINE_MSB);
+ dump_reg(sd, REG_VSYNC_STOP_LINE_LSB);
+ dump_reg(sd, REG_VSYNC_STOP_LINE_MSB);
+ dump_reg(sd, REG_VBLK_START_LINE_LSB);
+ dump_reg(sd, REG_VBLK_START_LINE_MSB);
+ dump_reg(sd, REG_VBLK_STOP_LINE_LSB);
+ dump_reg(sd, REG_VBLK_STOP_LINE_MSB);
+ dump_reg(sd, REG_SYNC_CONTROL);
+ dump_reg(sd, REG_OUTPUT_FORMATTER1);
+ dump_reg(sd, REG_OUTPUT_FORMATTER2);
+ dump_reg(sd, REG_OUTPUT_FORMATTER3);
+ dump_reg(sd, REG_OUTPUT_FORMATTER4);
+ dump_reg(sd, REG_OUTPUT_FORMATTER5);
+ dump_reg(sd, REG_OUTPUT_FORMATTER6);
+ dump_reg(sd, REG_CLEAR_LOST_LOCK);
+}
+
+/**
+ * tvp514x_configure() - Configure the TVP5146/47 registers
+ * @sd: ptr to v4l2_subdev struct
+ * @decoder: ptr to tvp514x_decoder structure
+ *
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int tvp514x_configure(struct v4l2_subdev *sd,
+ struct tvp514x_decoder *decoder)
+{
+ int err;
+
+ /* common register initialization */
+ err =
+ tvp514x_write_regs(sd, decoder->tvp514x_regs);
+ if (err)
+ return err;
+
+ if (debug)
+ tvp514x_reg_dump(sd);
+
+ return 0;
+}
+
+/**
+ * tvp514x_detect() - Detect if an tvp514x is present, and if so which revision.
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @decoder: pointer to tvp514x_decoder structure
+ *
+ * A device is considered to be detected if the chip ID (LSB and MSB)
+ * registers match the expected values.
+ * Any value of the rom version register is accepted.
+ * Returns ENODEV error number if no device is detected, or zero
+ * if a device is detected.
+ */
+static int tvp514x_detect(struct v4l2_subdev *sd,
+ struct tvp514x_decoder *decoder)
+{
+ u8 chip_id_msb, chip_id_lsb, rom_ver;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ chip_id_msb = tvp514x_read_reg(sd, REG_CHIP_ID_MSB);
+ chip_id_lsb = tvp514x_read_reg(sd, REG_CHIP_ID_LSB);
+ rom_ver = tvp514x_read_reg(sd, REG_ROM_VERSION);
+
+ v4l2_dbg(1, debug, sd,
+ "chip id detected msb:0x%x lsb:0x%x rom version:0x%x\n",
+ chip_id_msb, chip_id_lsb, rom_ver);
+ if ((chip_id_msb != TVP514X_CHIP_ID_MSB)
+ || ((chip_id_lsb != TVP5146_CHIP_ID_LSB)
+ && (chip_id_lsb != TVP5147_CHIP_ID_LSB))) {
+ /* We didn't read the values we expected, so this must not be
+ * an TVP5146/47.
+ */
+ v4l2_err(sd, "chip id mismatch msb:0x%x lsb:0x%x\n",
+ chip_id_msb, chip_id_lsb);
+ return -ENODEV;
+ }
+
+ decoder->ver = rom_ver;
+
+ v4l2_info(sd, "%s (Version - 0x%.2x) found at 0x%x (%s)\n",
+ client->name, decoder->ver,
+ client->addr << 1, client->adapter->name);
+ return 0;
+}
+
+/**
+ * tvp514x_querystd() - V4L2 decoder interface handler for querystd
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @std_id: standard V4L2 std_id ioctl enum
+ *
+ * Returns the current standard detected by TVP5146/47. If no active input is
+ * detected then *std_id is set to 0 and the function returns 0.
+ */
+static int tvp514x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std_id)
+{
+ struct tvp514x_decoder *decoder = to_decoder(sd);
+ enum tvp514x_std current_std;
+ enum tvp514x_input input_sel;
+ u8 sync_lock_status, lock_mask;
+
+ if (std_id == NULL)
+ return -EINVAL;
+
+ *std_id = V4L2_STD_UNKNOWN;
+
+ /* query the current standard */
+ current_std = tvp514x_query_current_std(sd);
+ if (current_std == STD_INVALID)
+ return 0;
+
+ input_sel = decoder->input;
+
+ switch (input_sel) {
+ case INPUT_CVBS_VI1A:
+ case INPUT_CVBS_VI1B:
+ case INPUT_CVBS_VI1C:
+ case INPUT_CVBS_VI2A:
+ case INPUT_CVBS_VI2B:
+ case INPUT_CVBS_VI2C:
+ case INPUT_CVBS_VI3A:
+ case INPUT_CVBS_VI3B:
+ case INPUT_CVBS_VI3C:
+ case INPUT_CVBS_VI4A:
+ lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT |
+ STATUS_HORZ_SYNC_LOCK_BIT |
+ STATUS_VIRT_SYNC_LOCK_BIT;
+ break;
+
+ case INPUT_SVIDEO_VI2A_VI1A:
+ case INPUT_SVIDEO_VI2B_VI1B:
+ case INPUT_SVIDEO_VI2C_VI1C:
+ case INPUT_SVIDEO_VI2A_VI3A:
+ case INPUT_SVIDEO_VI2B_VI3B:
+ case INPUT_SVIDEO_VI2C_VI3C:
+ case INPUT_SVIDEO_VI4A_VI1A:
+ case INPUT_SVIDEO_VI4A_VI1B:
+ case INPUT_SVIDEO_VI4A_VI1C:
+ case INPUT_SVIDEO_VI4A_VI3A:
+ case INPUT_SVIDEO_VI4A_VI3B:
+ case INPUT_SVIDEO_VI4A_VI3C:
+ lock_mask = STATUS_HORZ_SYNC_LOCK_BIT |
+ STATUS_VIRT_SYNC_LOCK_BIT;
+ break;
+ /*Need to add other interfaces*/
+ default:
+ return -EINVAL;
+ }
+ /* check whether signal is locked */
+ sync_lock_status = tvp514x_read_reg(sd, REG_STATUS1);
+ if (lock_mask != (sync_lock_status & lock_mask))
+ return 0; /* No input detected */
+
+ *std_id = decoder->std_list[current_std].standard.id;
+
+ v4l2_dbg(1, debug, sd, "Current STD: %s\n",
+ decoder->std_list[current_std].standard.name);
+ return 0;
+}
+
+/**
+ * tvp514x_s_std() - V4L2 decoder interface handler for s_std
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @std_id: standard V4L2 v4l2_std_id ioctl enum
+ *
+ * If std_id is supported, sets the requested standard. Otherwise, returns
+ * -EINVAL
+ */
+static int tvp514x_s_std(struct v4l2_subdev *sd, v4l2_std_id std_id)
+{
+ struct tvp514x_decoder *decoder = to_decoder(sd);
+ int err, i;
+
+ for (i = 0; i < decoder->num_stds; i++)
+ if (std_id & decoder->std_list[i].standard.id)
+ break;
+
+ if ((i == decoder->num_stds) || (i == STD_INVALID))
+ return -EINVAL;
+
+ err = tvp514x_write_reg(sd, REG_VIDEO_STD,
+ decoder->std_list[i].video_std);
+ if (err)
+ return err;
+
+ decoder->current_std = i;
+ decoder->tvp514x_regs[REG_VIDEO_STD].val =
+ decoder->std_list[i].video_std;
+
+ v4l2_dbg(1, debug, sd, "Standard set to: %s\n",
+ decoder->std_list[i].standard.name);
+ return 0;
+}
+
+/**
+ * tvp514x_s_routing() - V4L2 decoder interface handler for s_routing
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @input: input selector for routing the signal
+ * @output: output selector for routing the signal
+ * @config: config value. Not used
+ *
+ * If index is valid, selects the requested input. Otherwise, returns -EINVAL if
+ * the input is not supported or there is no active signal present in the
+ * selected input.
+ */
+static int tvp514x_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct tvp514x_decoder *decoder = to_decoder(sd);
+ int err;
+ enum tvp514x_input input_sel;
+ enum tvp514x_output output_sel;
+ u8 sync_lock_status, lock_mask;
+ int try_count = LOCK_RETRY_COUNT;
+
+ if ((input >= INPUT_INVALID) ||
+ (output >= OUTPUT_INVALID))
+ /* Index out of bound */
+ return -EINVAL;
+
+ /*
+ * For the sequence streamon -> streamoff and again s_input
+ * it fails to lock the signal, since streamoff puts TVP514x
+ * into power off state which leads to failure in sub-sequent s_input.
+ *
+ * So power up the TVP514x device here, since it is important to lock
+ * the signal at this stage.
+ */
+ if (!decoder->streaming)
+ tvp514x_s_stream(sd, 1);
+
+ input_sel = input;
+ output_sel = output;
+
+ err = tvp514x_write_reg(sd, REG_INPUT_SEL, input_sel);
+ if (err)
+ return err;
+
+ output_sel |= tvp514x_read_reg(sd,
+ REG_OUTPUT_FORMATTER1) & 0x7;
+ err = tvp514x_write_reg(sd, REG_OUTPUT_FORMATTER1,
+ output_sel);
+ if (err)
+ return err;
+
+ decoder->tvp514x_regs[REG_INPUT_SEL].val = input_sel;
+ decoder->tvp514x_regs[REG_OUTPUT_FORMATTER1].val = output_sel;
+
+ /* Clear status */
+ msleep(LOCK_RETRY_DELAY);
+ err =
+ tvp514x_write_reg(sd, REG_CLEAR_LOST_LOCK, 0x01);
+ if (err)
+ return err;
+
+ switch (input_sel) {
+ case INPUT_CVBS_VI1A:
+ case INPUT_CVBS_VI1B:
+ case INPUT_CVBS_VI1C:
+ case INPUT_CVBS_VI2A:
+ case INPUT_CVBS_VI2B:
+ case INPUT_CVBS_VI2C:
+ case INPUT_CVBS_VI3A:
+ case INPUT_CVBS_VI3B:
+ case INPUT_CVBS_VI3C:
+ case INPUT_CVBS_VI4A:
+ lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT |
+ STATUS_HORZ_SYNC_LOCK_BIT |
+ STATUS_VIRT_SYNC_LOCK_BIT;
+ break;
+
+ case INPUT_SVIDEO_VI2A_VI1A:
+ case INPUT_SVIDEO_VI2B_VI1B:
+ case INPUT_SVIDEO_VI2C_VI1C:
+ case INPUT_SVIDEO_VI2A_VI3A:
+ case INPUT_SVIDEO_VI2B_VI3B:
+ case INPUT_SVIDEO_VI2C_VI3C:
+ case INPUT_SVIDEO_VI4A_VI1A:
+ case INPUT_SVIDEO_VI4A_VI1B:
+ case INPUT_SVIDEO_VI4A_VI1C:
+ case INPUT_SVIDEO_VI4A_VI3A:
+ case INPUT_SVIDEO_VI4A_VI3B:
+ case INPUT_SVIDEO_VI4A_VI3C:
+ lock_mask = STATUS_HORZ_SYNC_LOCK_BIT |
+ STATUS_VIRT_SYNC_LOCK_BIT;
+ break;
+ /* Need to add other interfaces*/
+ default:
+ return -EINVAL;
+ }
+
+ while (try_count-- > 0) {
+ /* Allow decoder to sync up with new input */
+ msleep(LOCK_RETRY_DELAY);
+
+ sync_lock_status = tvp514x_read_reg(sd,
+ REG_STATUS1);
+ if (lock_mask == (sync_lock_status & lock_mask))
+ /* Input detected */
+ break;
+ }
+
+ if (try_count < 0)
+ return -EINVAL;
+
+ decoder->input = input;
+ decoder->output = output;
+
+ v4l2_dbg(1, debug, sd, "Input set to: %d\n", input_sel);
+
+ return 0;
+}
+
+/**
+ * tvp514x_s_ctrl() - V4L2 decoder interface handler for s_ctrl
+ * @ctrl: pointer to v4l2_ctrl structure
+ *
+ * If the requested control is supported, sets the control's current
+ * value in HW. Otherwise, returns -EINVAL if the control is not supported.
+ */
+static int tvp514x_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ struct tvp514x_decoder *decoder = to_decoder(sd);
+ int err = -EINVAL, value;
+
+ value = ctrl->val;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ err = tvp514x_write_reg(sd, REG_BRIGHTNESS, value);
+ if (!err)
+ decoder->tvp514x_regs[REG_BRIGHTNESS].val = value;
+ break;
+ case V4L2_CID_CONTRAST:
+ err = tvp514x_write_reg(sd, REG_CONTRAST, value);
+ if (!err)
+ decoder->tvp514x_regs[REG_CONTRAST].val = value;
+ break;
+ case V4L2_CID_SATURATION:
+ err = tvp514x_write_reg(sd, REG_SATURATION, value);
+ if (!err)
+ decoder->tvp514x_regs[REG_SATURATION].val = value;
+ break;
+ case V4L2_CID_HUE:
+ if (value == 180)
+ value = 0x7F;
+ else if (value == -180)
+ value = 0x80;
+ err = tvp514x_write_reg(sd, REG_HUE, value);
+ if (!err)
+ decoder->tvp514x_regs[REG_HUE].val = value;
+ break;
+ case V4L2_CID_AUTOGAIN:
+ err = tvp514x_write_reg(sd, REG_AFE_GAIN_CTRL, value ? 0x0f : 0x0c);
+ if (!err)
+ decoder->tvp514x_regs[REG_AFE_GAIN_CTRL].val = value;
+ break;
+ }
+
+ v4l2_dbg(1, debug, sd, "Set Control: ID - %d - %d\n",
+ ctrl->id, ctrl->val);
+ return err;
+}
+
+/**
+ * tvp514x_enum_mbus_fmt() - V4L2 decoder interface handler for enum_mbus_fmt
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @index: index of pixelcode to retrieve
+ * @code: receives the pixelcode
+ *
+ * Enumerates supported mediabus formats
+ */
+static int
+tvp514x_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index)
+ return -EINVAL;
+
+ *code = V4L2_MBUS_FMT_YUYV10_2X10;
+ return 0;
+}
+
+/**
+ * tvp514x_mbus_fmt_cap() - V4L2 decoder interface handler for try/s/g_mbus_fmt
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @f: pointer to the mediabus format structure
+ *
+ * Negotiates the image capture size and mediabus format.
+ */
+static int
+tvp514x_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f)
+{
+ struct tvp514x_decoder *decoder = to_decoder(sd);
+ enum tvp514x_std current_std;
+
+ if (f == NULL)
+ return -EINVAL;
+
+ /* Calculate height and width based on current standard */
+ current_std = decoder->current_std;
+
+ f->code = V4L2_MBUS_FMT_YUYV10_2X10;
+ f->width = decoder->std_list[current_std].width;
+ f->height = decoder->std_list[current_std].height;
+ f->field = V4L2_FIELD_INTERLACED;
+ f->colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+ v4l2_dbg(1, debug, sd, "MBUS_FMT: Width - %d, Height - %d\n",
+ f->width, f->height);
+ return 0;
+}
+
+/**
+ * tvp514x_g_parm() - V4L2 decoder interface handler for g_parm
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
+ *
+ * Returns the decoder's video CAPTURE parameters.
+ */
+static int
+tvp514x_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+ struct tvp514x_decoder *decoder = to_decoder(sd);
+ struct v4l2_captureparm *cparm;
+ enum tvp514x_std current_std;
+
+ if (a == NULL)
+ return -EINVAL;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ /* only capture is supported */
+ return -EINVAL;
+
+ /* get the current standard */
+ current_std = decoder->current_std;
+
+ cparm = &a->parm.capture;
+ cparm->capability = V4L2_CAP_TIMEPERFRAME;
+ cparm->timeperframe =
+ decoder->std_list[current_std].standard.frameperiod;
+
+ return 0;
+}
+
+/**
+ * tvp514x_s_parm() - V4L2 decoder interface handler for s_parm
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure
+ *
+ * Configures the decoder to use the input parameters, if possible. If
+ * not possible, returns the appropriate error code.
+ */
+static int
+tvp514x_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+ struct tvp514x_decoder *decoder = to_decoder(sd);
+ struct v4l2_fract *timeperframe;
+ enum tvp514x_std current_std;
+
+ if (a == NULL)
+ return -EINVAL;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ /* only capture is supported */
+ return -EINVAL;
+
+ timeperframe = &a->parm.capture.timeperframe;
+
+ /* get the current standard */
+ current_std = decoder->current_std;
+
+ *timeperframe =
+ decoder->std_list[current_std].standard.frameperiod;
+
+ return 0;
+}
+
+/**
+ * tvp514x_s_stream() - V4L2 decoder i/f handler for s_stream
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @enable: streaming enable or disable
+ *
+ * Sets streaming to enable or disable, if possible.
+ */
+static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ int err = 0;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tvp514x_decoder *decoder = to_decoder(sd);
+
+ if (decoder->streaming == enable)
+ return 0;
+
+ switch (enable) {
+ case 0:
+ {
+ /* Power Down Sequence */
+ err = tvp514x_write_reg(sd, REG_OPERATION_MODE, 0x01);
+ if (err) {
+ v4l2_err(sd, "Unable to turn off decoder\n");
+ return err;
+ }
+ decoder->streaming = enable;
+ break;
+ }
+ case 1:
+ {
+ struct tvp514x_reg *int_seq = (struct tvp514x_reg *)
+ client->driver->id_table->driver_data;
+
+ /* Power Up Sequence */
+ err = tvp514x_write_regs(sd, int_seq);
+ if (err) {
+ v4l2_err(sd, "Unable to turn on decoder\n");
+ return err;
+ }
+ /* Detect if not already detected */
+ err = tvp514x_detect(sd, decoder);
+ if (err) {
+ v4l2_err(sd, "Unable to detect decoder\n");
+ return err;
+ }
+ err = tvp514x_configure(sd, decoder);
+ if (err) {
+ v4l2_err(sd, "Unable to configure decoder\n");
+ return err;
+ }
+ decoder->streaming = enable;
+ break;
+ }
+ default:
+ err = -ENODEV;
+ break;
+ }
+
+ return err;
+}
+
+static const struct v4l2_ctrl_ops tvp514x_ctrl_ops = {
+ .s_ctrl = tvp514x_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops tvp514x_core_ops = {
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+ .s_std = tvp514x_s_std,
+};
+
+static const struct v4l2_subdev_video_ops tvp514x_video_ops = {
+ .s_routing = tvp514x_s_routing,
+ .querystd = tvp514x_querystd,
+ .enum_mbus_fmt = tvp514x_enum_mbus_fmt,
+ .g_mbus_fmt = tvp514x_mbus_fmt,
+ .try_mbus_fmt = tvp514x_mbus_fmt,
+ .s_mbus_fmt = tvp514x_mbus_fmt,
+ .g_parm = tvp514x_g_parm,
+ .s_parm = tvp514x_s_parm,
+ .s_stream = tvp514x_s_stream,
+};
+
+static const struct v4l2_subdev_ops tvp514x_ops = {
+ .core = &tvp514x_core_ops,
+ .video = &tvp514x_video_ops,
+};
+
+static struct tvp514x_decoder tvp514x_dev = {
+ .streaming = 0,
+ .current_std = STD_NTSC_MJ,
+ .std_list = tvp514x_std_list,
+ .num_stds = ARRAY_SIZE(tvp514x_std_list),
+
+};
+
+/**
+ * tvp514x_probe() - decoder driver i2c probe handler
+ * @client: i2c driver client device structure
+ * @id: i2c driver id table
+ *
+ * Register decoder as an i2c client device and V4L2
+ * device.
+ */
+static int
+tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct tvp514x_decoder *decoder;
+ struct v4l2_subdev *sd;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ if (!client->dev.platform_data) {
+ v4l2_err(client, "No platform data!!\n");
+ return -ENODEV;
+ }
+
+ decoder = kzalloc(sizeof(*decoder), GFP_KERNEL);
+ if (!decoder)
+ return -ENOMEM;
+
+ /* Initialize the tvp514x_decoder with default configuration */
+ *decoder = tvp514x_dev;
+ /* Copy default register configuration */
+ memcpy(decoder->tvp514x_regs, tvp514x_reg_list_default,
+ sizeof(tvp514x_reg_list_default));
+
+ /* Copy board specific information here */
+ decoder->pdata = client->dev.platform_data;
+
+ /**
+ * Fetch platform specific data, and configure the
+ * tvp514x_reg_list[] accordingly. Since this is one
+ * time configuration, no need to preserve.
+ */
+ decoder->tvp514x_regs[REG_OUTPUT_FORMATTER2].val |=
+ (decoder->pdata->clk_polarity << 1);
+ decoder->tvp514x_regs[REG_SYNC_CONTROL].val |=
+ ((decoder->pdata->hs_polarity << 2) |
+ (decoder->pdata->vs_polarity << 3));
+ /* Set default standard to auto */
+ decoder->tvp514x_regs[REG_VIDEO_STD].val =
+ VIDEO_STD_AUTO_SWITCH_BIT;
+
+ /* Register with V4L2 layer as slave device */
+ sd = &decoder->sd;
+ v4l2_i2c_subdev_init(sd, client, &tvp514x_ops);
+
+ v4l2_ctrl_handler_init(&decoder->hdl, 5);
+ v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops,
+ V4L2_CID_HUE, -180, 180, 180, 0);
+ v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ sd->ctrl_handler = &decoder->hdl;
+ if (decoder->hdl.error) {
+ int err = decoder->hdl.error;
+
+ v4l2_ctrl_handler_free(&decoder->hdl);
+ kfree(decoder);
+ return err;
+ }
+ v4l2_ctrl_handler_setup(&decoder->hdl);
+
+ v4l2_info(sd, "%s decoder driver registered !!\n", sd->name);
+
+ return 0;
+
+}
+
+/**
+ * tvp514x_remove() - decoder driver i2c remove handler
+ * @client: i2c driver client device structure
+ *
+ * Unregister decoder as an i2c client device and V4L2
+ * device. Complement of tvp514x_probe().
+ */
+static int tvp514x_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct tvp514x_decoder *decoder = to_decoder(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&decoder->hdl);
+ kfree(decoder);
+ return 0;
+}
+/* TVP5146 Init/Power on Sequence */
+static const struct tvp514x_reg tvp5146_init_reg_seq[] = {
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x01},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x00},
+ {TOK_TERM, 0, 0},
+};
+
+/* TVP5147 Init/Power on Sequence */
+static const struct tvp514x_reg tvp5147_init_reg_seq[] = {
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x16},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xA0},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x16},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x01},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x00},
+ {TOK_TERM, 0, 0},
+};
+
+/* TVP5146M2/TVP5147M1 Init/Power on Sequence */
+static const struct tvp514x_reg tvp514xm_init_reg_seq[] = {
+ {TOK_WRITE, REG_OPERATION_MODE, 0x01},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x00},
+ {TOK_TERM, 0, 0},
+};
+
+/**
+ * I2C Device Table -
+ *
+ * name - Name of the actual device/chip.
+ * driver_data - Driver data
+ */
+static const struct i2c_device_id tvp514x_id[] = {
+ {"tvp5146", (unsigned long)tvp5146_init_reg_seq},
+ {"tvp5146m2", (unsigned long)tvp514xm_init_reg_seq},
+ {"tvp5147", (unsigned long)tvp5147_init_reg_seq},
+ {"tvp5147m1", (unsigned long)tvp514xm_init_reg_seq},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, tvp514x_id);
+
+static struct i2c_driver tvp514x_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = TVP514X_MODULE_NAME,
+ },
+ .probe = tvp514x_probe,
+ .remove = tvp514x_remove,
+ .id_table = tvp514x_id,
+};
+
+module_i2c_driver(tvp514x_driver);
diff --git a/drivers/media/video/tvp514x_regs.h b/drivers/media/video/tvp514x_regs.h
new file mode 100644
index 00000000..18f29ad0
--- /dev/null
+++ b/drivers/media/video/tvp514x_regs.h
@@ -0,0 +1,287 @@
+/*
+ * drivers/media/video/tvp514x_regs.h
+ *
+ * Copyright (C) 2008 Texas Instruments Inc
+ * Author: Vaibhav Hiremath <hvaibhav@ti.com>
+ *
+ * Contributors:
+ * Sivaraj R <sivaraj@ti.com>
+ * Brijesh R Jadav <brijesh.j@ti.com>
+ * Hardik Shah <hardik.shah@ti.com>
+ * Manjunath Hadli <mrh@ti.com>
+ * Karicheri Muralidharan <m-karicheri2@ti.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef _TVP514X_REGS_H
+#define _TVP514X_REGS_H
+
+/*
+ * TVP5146/47 registers
+ */
+#define REG_INPUT_SEL (0x00)
+#define REG_AFE_GAIN_CTRL (0x01)
+#define REG_VIDEO_STD (0x02)
+#define REG_OPERATION_MODE (0x03)
+#define REG_AUTOSWITCH_MASK (0x04)
+
+#define REG_COLOR_KILLER (0x05)
+#define REG_LUMA_CONTROL1 (0x06)
+#define REG_LUMA_CONTROL2 (0x07)
+#define REG_LUMA_CONTROL3 (0x08)
+
+#define REG_BRIGHTNESS (0x09)
+#define REG_CONTRAST (0x0A)
+#define REG_SATURATION (0x0B)
+#define REG_HUE (0x0C)
+
+#define REG_CHROMA_CONTROL1 (0x0D)
+#define REG_CHROMA_CONTROL2 (0x0E)
+
+/* 0x0F Reserved */
+
+#define REG_COMP_PR_SATURATION (0x10)
+#define REG_COMP_Y_CONTRAST (0x11)
+#define REG_COMP_PB_SATURATION (0x12)
+
+/* 0x13 Reserved */
+
+#define REG_COMP_Y_BRIGHTNESS (0x14)
+
+/* 0x15 Reserved */
+
+#define REG_AVID_START_PIXEL_LSB (0x16)
+#define REG_AVID_START_PIXEL_MSB (0x17)
+#define REG_AVID_STOP_PIXEL_LSB (0x18)
+#define REG_AVID_STOP_PIXEL_MSB (0x19)
+
+#define REG_HSYNC_START_PIXEL_LSB (0x1A)
+#define REG_HSYNC_START_PIXEL_MSB (0x1B)
+#define REG_HSYNC_STOP_PIXEL_LSB (0x1C)
+#define REG_HSYNC_STOP_PIXEL_MSB (0x1D)
+
+#define REG_VSYNC_START_LINE_LSB (0x1E)
+#define REG_VSYNC_START_LINE_MSB (0x1F)
+#define REG_VSYNC_STOP_LINE_LSB (0x20)
+#define REG_VSYNC_STOP_LINE_MSB (0x21)
+
+#define REG_VBLK_START_LINE_LSB (0x22)
+#define REG_VBLK_START_LINE_MSB (0x23)
+#define REG_VBLK_STOP_LINE_LSB (0x24)
+#define REG_VBLK_STOP_LINE_MSB (0x25)
+
+/* 0x26 - 0x27 Reserved */
+
+#define REG_FAST_SWTICH_CONTROL (0x28)
+
+/* 0x29 Reserved */
+
+#define REG_FAST_SWTICH_SCART_DELAY (0x2A)
+
+/* 0x2B Reserved */
+
+#define REG_SCART_DELAY (0x2C)
+#define REG_CTI_DELAY (0x2D)
+#define REG_CTI_CONTROL (0x2E)
+
+/* 0x2F - 0x31 Reserved */
+
+#define REG_SYNC_CONTROL (0x32)
+#define REG_OUTPUT_FORMATTER1 (0x33)
+#define REG_OUTPUT_FORMATTER2 (0x34)
+#define REG_OUTPUT_FORMATTER3 (0x35)
+#define REG_OUTPUT_FORMATTER4 (0x36)
+#define REG_OUTPUT_FORMATTER5 (0x37)
+#define REG_OUTPUT_FORMATTER6 (0x38)
+#define REG_CLEAR_LOST_LOCK (0x39)
+
+#define REG_STATUS1 (0x3A)
+#define REG_STATUS2 (0x3B)
+
+#define REG_AGC_GAIN_STATUS_LSB (0x3C)
+#define REG_AGC_GAIN_STATUS_MSB (0x3D)
+
+/* 0x3E Reserved */
+
+#define REG_VIDEO_STD_STATUS (0x3F)
+#define REG_GPIO_INPUT1 (0x40)
+#define REG_GPIO_INPUT2 (0x41)
+
+/* 0x42 - 0x45 Reserved */
+
+#define REG_AFE_COARSE_GAIN_CH1 (0x46)
+#define REG_AFE_COARSE_GAIN_CH2 (0x47)
+#define REG_AFE_COARSE_GAIN_CH3 (0x48)
+#define REG_AFE_COARSE_GAIN_CH4 (0x49)
+
+#define REG_AFE_FINE_GAIN_PB_B_LSB (0x4A)
+#define REG_AFE_FINE_GAIN_PB_B_MSB (0x4B)
+#define REG_AFE_FINE_GAIN_Y_G_CHROMA_LSB (0x4C)
+#define REG_AFE_FINE_GAIN_Y_G_CHROMA_MSB (0x4D)
+#define REG_AFE_FINE_GAIN_PR_R_LSB (0x4E)
+#define REG_AFE_FINE_GAIN_PR_R_MSB (0x4F)
+#define REG_AFE_FINE_GAIN_CVBS_LUMA_LSB (0x50)
+#define REG_AFE_FINE_GAIN_CVBS_LUMA_MSB (0x51)
+
+/* 0x52 - 0x68 Reserved */
+
+#define REG_FBIT_VBIT_CONTROL1 (0x69)
+
+/* 0x6A - 0x6B Reserved */
+
+#define REG_BACKEND_AGC_CONTROL (0x6C)
+
+/* 0x6D - 0x6E Reserved */
+
+#define REG_AGC_DECREMENT_SPEED_CONTROL (0x6F)
+#define REG_ROM_VERSION (0x70)
+
+/* 0x71 - 0x73 Reserved */
+
+#define REG_AGC_WHITE_PEAK_PROCESSING (0x74)
+#define REG_FBIT_VBIT_CONTROL2 (0x75)
+#define REG_VCR_TRICK_MODE_CONTROL (0x76)
+#define REG_HORIZONTAL_SHAKE_INCREMENT (0x77)
+#define REG_AGC_INCREMENT_SPEED (0x78)
+#define REG_AGC_INCREMENT_DELAY (0x79)
+
+/* 0x7A - 0x7F Reserved */
+
+#define REG_CHIP_ID_MSB (0x80)
+#define REG_CHIP_ID_LSB (0x81)
+
+/* 0x82 Reserved */
+
+#define REG_CPLL_SPEED_CONTROL (0x83)
+
+/* 0x84 - 0x96 Reserved */
+
+#define REG_STATUS_REQUEST (0x97)
+
+/* 0x98 - 0x99 Reserved */
+
+#define REG_VERTICAL_LINE_COUNT_LSB (0x9A)
+#define REG_VERTICAL_LINE_COUNT_MSB (0x9B)
+
+/* 0x9C - 0x9D Reserved */
+
+#define REG_AGC_DECREMENT_DELAY (0x9E)
+
+/* 0x9F - 0xB0 Reserved */
+
+#define REG_VDP_TTX_FILTER_1_MASK1 (0xB1)
+#define REG_VDP_TTX_FILTER_1_MASK2 (0xB2)
+#define REG_VDP_TTX_FILTER_1_MASK3 (0xB3)
+#define REG_VDP_TTX_FILTER_1_MASK4 (0xB4)
+#define REG_VDP_TTX_FILTER_1_MASK5 (0xB5)
+#define REG_VDP_TTX_FILTER_2_MASK1 (0xB6)
+#define REG_VDP_TTX_FILTER_2_MASK2 (0xB7)
+#define REG_VDP_TTX_FILTER_2_MASK3 (0xB8)
+#define REG_VDP_TTX_FILTER_2_MASK4 (0xB9)
+#define REG_VDP_TTX_FILTER_2_MASK5 (0xBA)
+#define REG_VDP_TTX_FILTER_CONTROL (0xBB)
+#define REG_VDP_FIFO_WORD_COUNT (0xBC)
+#define REG_VDP_FIFO_INTERRUPT_THRLD (0xBD)
+
+/* 0xBE Reserved */
+
+#define REG_VDP_FIFO_RESET (0xBF)
+#define REG_VDP_FIFO_OUTPUT_CONTROL (0xC0)
+#define REG_VDP_LINE_NUMBER_INTERRUPT (0xC1)
+#define REG_VDP_PIXEL_ALIGNMENT_LSB (0xC2)
+#define REG_VDP_PIXEL_ALIGNMENT_MSB (0xC3)
+
+/* 0xC4 - 0xD5 Reserved */
+
+#define REG_VDP_LINE_START (0xD6)
+#define REG_VDP_LINE_STOP (0xD7)
+#define REG_VDP_GLOBAL_LINE_MODE (0xD8)
+#define REG_VDP_FULL_FIELD_ENABLE (0xD9)
+#define REG_VDP_FULL_FIELD_MODE (0xDA)
+
+/* 0xDB - 0xDF Reserved */
+
+#define REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR (0xE0)
+#define REG_VBUS_DATA_ACCESS_VBUS_ADDR_INCR (0xE1)
+#define REG_FIFO_READ_DATA (0xE2)
+
+/* 0xE3 - 0xE7 Reserved */
+
+#define REG_VBUS_ADDRESS_ACCESS1 (0xE8)
+#define REG_VBUS_ADDRESS_ACCESS2 (0xE9)
+#define REG_VBUS_ADDRESS_ACCESS3 (0xEA)
+
+/* 0xEB - 0xEF Reserved */
+
+#define REG_INTERRUPT_RAW_STATUS0 (0xF0)
+#define REG_INTERRUPT_RAW_STATUS1 (0xF1)
+#define REG_INTERRUPT_STATUS0 (0xF2)
+#define REG_INTERRUPT_STATUS1 (0xF3)
+#define REG_INTERRUPT_MASK0 (0xF4)
+#define REG_INTERRUPT_MASK1 (0xF5)
+#define REG_INTERRUPT_CLEAR0 (0xF6)
+#define REG_INTERRUPT_CLEAR1 (0xF7)
+
+/* 0xF8 - 0xFF Reserved */
+
+/*
+ * Mask and bit definitions of TVP5146/47 registers
+ */
+/* The ID values we are looking for */
+#define TVP514X_CHIP_ID_MSB (0x51)
+#define TVP5146_CHIP_ID_LSB (0x46)
+#define TVP5147_CHIP_ID_LSB (0x47)
+
+#define VIDEO_STD_MASK (0x07)
+#define VIDEO_STD_AUTO_SWITCH_BIT (0x00)
+#define VIDEO_STD_NTSC_MJ_BIT (0x01)
+#define VIDEO_STD_PAL_BDGHIN_BIT (0x02)
+#define VIDEO_STD_PAL_M_BIT (0x03)
+#define VIDEO_STD_PAL_COMBINATION_N_BIT (0x04)
+#define VIDEO_STD_NTSC_4_43_BIT (0x05)
+#define VIDEO_STD_SECAM_BIT (0x06)
+#define VIDEO_STD_PAL_60_BIT (0x07)
+
+/*
+ * Status bit
+ */
+#define STATUS_TV_VCR_BIT (1<<0)
+#define STATUS_HORZ_SYNC_LOCK_BIT (1<<1)
+#define STATUS_VIRT_SYNC_LOCK_BIT (1<<2)
+#define STATUS_CLR_SUBCAR_LOCK_BIT (1<<3)
+#define STATUS_LOST_LOCK_DETECT_BIT (1<<4)
+#define STATUS_FEILD_RATE_BIT (1<<5)
+#define STATUS_LINE_ALTERNATING_BIT (1<<6)
+#define STATUS_PEAK_WHITE_DETECT_BIT (1<<7)
+
+/* Tokens for register write */
+#define TOK_WRITE (0) /* token for write operation */
+#define TOK_TERM (1) /* terminating token */
+#define TOK_DELAY (2) /* delay token for reg list */
+#define TOK_SKIP (3) /* token to skip a register */
+/**
+ * struct tvp514x_reg - Structure for TVP5146/47 register initialization values
+ * @token - Token: TOK_WRITE, TOK_TERM etc..
+ * @reg - Register offset
+ * @val - Register Value for TOK_WRITE or delay in ms for TOK_DELAY
+ */
+struct tvp514x_reg {
+ u8 token;
+ u8 reg;
+ u32 val;
+};
+
+#endif /* ifndef _TVP514X_REGS_H */
diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c
new file mode 100644
index 00000000..1326e11c
--- /dev/null
+++ b/drivers/media/video/tvp5150.c
@@ -0,0 +1,1242 @@
+/*
+ * tvp5150 - Texas Instruments TVP5150A/AM1 video decoder driver
+ *
+ * Copyright (c) 2005,2006 Mauro Carvalho Chehab (mchehab@infradead.org)
+ * This code is placed under the terms of the GNU General Public License v2
+ */
+
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <media/v4l2-device.h>
+#include <media/tvp5150.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+
+#include "tvp5150_reg.h"
+
+#define TVP5150_H_MAX 720
+#define TVP5150_V_MAX_525_60 480
+#define TVP5150_V_MAX_OTHERS 576
+#define TVP5150_MAX_CROP_LEFT 511
+#define TVP5150_MAX_CROP_TOP 127
+#define TVP5150_CROP_SHIFT 2
+
+MODULE_DESCRIPTION("Texas Instruments TVP5150A video decoder driver");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
+MODULE_LICENSE("GPL");
+
+
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-2)");
+
+struct tvp5150 {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+ struct v4l2_rect rect;
+
+ v4l2_std_id norm; /* Current set standard */
+ u32 input;
+ u32 output;
+ int enable;
+};
+
+static inline struct tvp5150 *to_tvp5150(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct tvp5150, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct tvp5150, hdl)->sd;
+}
+
+static int tvp5150_read(struct v4l2_subdev *sd, unsigned char addr)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
+ unsigned char buffer[1];
+ int rc;
+
+ buffer[0] = addr;
+ if (1 != (rc = i2c_master_send(c, buffer, 1)))
+ v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 1)\n", rc);
+
+ msleep(10);
+
+ if (1 != (rc = i2c_master_recv(c, buffer, 1)))
+ v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 1)\n", rc);
+
+ v4l2_dbg(2, debug, sd, "tvp5150: read 0x%02x = 0x%02x\n", addr, buffer[0]);
+
+ return (buffer[0]);
+}
+
+static inline void tvp5150_write(struct v4l2_subdev *sd, unsigned char addr,
+ unsigned char value)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
+ unsigned char buffer[2];
+ int rc;
+
+ buffer[0] = addr;
+ buffer[1] = value;
+ v4l2_dbg(2, debug, sd, "tvp5150: writing 0x%02x 0x%02x\n", buffer[0], buffer[1]);
+ if (2 != (rc = i2c_master_send(c, buffer, 2)))
+ v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 2)\n", rc);
+}
+
+static void dump_reg_range(struct v4l2_subdev *sd, char *s, u8 init,
+ const u8 end, int max_line)
+{
+ int i = 0;
+
+ while (init != (u8)(end + 1)) {
+ if ((i % max_line) == 0) {
+ if (i > 0)
+ printk("\n");
+ printk("tvp5150: %s reg 0x%02x = ", s, init);
+ }
+ printk("%02x ", tvp5150_read(sd, init));
+
+ init++;
+ i++;
+ }
+ printk("\n");
+}
+
+static int tvp5150_log_status(struct v4l2_subdev *sd)
+{
+ printk("tvp5150: Video input source selection #1 = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_VD_IN_SRC_SEL_1));
+ printk("tvp5150: Analog channel controls = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_ANAL_CHL_CTL));
+ printk("tvp5150: Operation mode controls = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_OP_MODE_CTL));
+ printk("tvp5150: Miscellaneous controls = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_MISC_CTL));
+ printk("tvp5150: Autoswitch mask= 0x%02x\n",
+ tvp5150_read(sd, TVP5150_AUTOSW_MSK));
+ printk("tvp5150: Color killer threshold control = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_COLOR_KIL_THSH_CTL));
+ printk("tvp5150: Luminance processing controls #1 #2 and #3 = %02x %02x %02x\n",
+ tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_1),
+ tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_2),
+ tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_3));
+ printk("tvp5150: Brightness control = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_BRIGHT_CTL));
+ printk("tvp5150: Color saturation control = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_SATURATION_CTL));
+ printk("tvp5150: Hue control = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_HUE_CTL));
+ printk("tvp5150: Contrast control = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_CONTRAST_CTL));
+ printk("tvp5150: Outputs and data rates select = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_DATA_RATE_SEL));
+ printk("tvp5150: Configuration shared pins = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_CONF_SHARED_PIN));
+ printk("tvp5150: Active video cropping start = 0x%02x%02x\n",
+ tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_MSB),
+ tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_LSB));
+ printk("tvp5150: Active video cropping stop = 0x%02x%02x\n",
+ tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_MSB),
+ tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_LSB));
+ printk("tvp5150: Genlock/RTC = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_GENLOCK));
+ printk("tvp5150: Horizontal sync start = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_HORIZ_SYNC_START));
+ printk("tvp5150: Vertical blanking start = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_VERT_BLANKING_START));
+ printk("tvp5150: Vertical blanking stop = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_VERT_BLANKING_STOP));
+ printk("tvp5150: Chrominance processing control #1 and #2 = %02x %02x\n",
+ tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_1),
+ tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_2));
+ printk("tvp5150: Interrupt reset register B = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_INT_RESET_REG_B));
+ printk("tvp5150: Interrupt enable register B = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_INT_ENABLE_REG_B));
+ printk("tvp5150: Interrupt configuration register B = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_INTT_CONFIG_REG_B));
+ printk("tvp5150: Video standard = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_VIDEO_STD));
+ printk("tvp5150: Chroma gain factor: Cb=0x%02x Cr=0x%02x\n",
+ tvp5150_read(sd, TVP5150_CB_GAIN_FACT),
+ tvp5150_read(sd, TVP5150_CR_GAIN_FACTOR));
+ printk("tvp5150: Macrovision on counter = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_MACROVISION_ON_CTR));
+ printk("tvp5150: Macrovision off counter = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_MACROVISION_OFF_CTR));
+ printk("tvp5150: ITU-R BT.656.%d timing(TVP5150AM1 only)\n",
+ (tvp5150_read(sd, TVP5150_REV_SELECT) & 1) ? 3 : 4);
+ printk("tvp5150: Device ID = %02x%02x\n",
+ tvp5150_read(sd, TVP5150_MSB_DEV_ID),
+ tvp5150_read(sd, TVP5150_LSB_DEV_ID));
+ printk("tvp5150: ROM version = (hex) %02x.%02x\n",
+ tvp5150_read(sd, TVP5150_ROM_MAJOR_VER),
+ tvp5150_read(sd, TVP5150_ROM_MINOR_VER));
+ printk("tvp5150: Vertical line count = 0x%02x%02x\n",
+ tvp5150_read(sd, TVP5150_VERT_LN_COUNT_MSB),
+ tvp5150_read(sd, TVP5150_VERT_LN_COUNT_LSB));
+ printk("tvp5150: Interrupt status register B = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_INT_STATUS_REG_B));
+ printk("tvp5150: Interrupt active register B = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_INT_ACTIVE_REG_B));
+ printk("tvp5150: Status regs #1 to #5 = %02x %02x %02x %02x %02x\n",
+ tvp5150_read(sd, TVP5150_STATUS_REG_1),
+ tvp5150_read(sd, TVP5150_STATUS_REG_2),
+ tvp5150_read(sd, TVP5150_STATUS_REG_3),
+ tvp5150_read(sd, TVP5150_STATUS_REG_4),
+ tvp5150_read(sd, TVP5150_STATUS_REG_5));
+
+ dump_reg_range(sd, "Teletext filter 1", TVP5150_TELETEXT_FIL1_INI,
+ TVP5150_TELETEXT_FIL1_END, 8);
+ dump_reg_range(sd, "Teletext filter 2", TVP5150_TELETEXT_FIL2_INI,
+ TVP5150_TELETEXT_FIL2_END, 8);
+
+ printk("tvp5150: Teletext filter enable = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_TELETEXT_FIL_ENA));
+ printk("tvp5150: Interrupt status register A = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_INT_STATUS_REG_A));
+ printk("tvp5150: Interrupt enable register A = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_INT_ENABLE_REG_A));
+ printk("tvp5150: Interrupt configuration = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_INT_CONF));
+ printk("tvp5150: VDP status register = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_VDP_STATUS_REG));
+ printk("tvp5150: FIFO word count = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_FIFO_WORD_COUNT));
+ printk("tvp5150: FIFO interrupt threshold = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_FIFO_INT_THRESHOLD));
+ printk("tvp5150: FIFO reset = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_FIFO_RESET));
+ printk("tvp5150: Line number interrupt = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_LINE_NUMBER_INT));
+ printk("tvp5150: Pixel alignment register = 0x%02x%02x\n",
+ tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_HIGH),
+ tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_LOW));
+ printk("tvp5150: FIFO output control = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_FIFO_OUT_CTRL));
+ printk("tvp5150: Full field enable = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_FULL_FIELD_ENA));
+ printk("tvp5150: Full field mode register = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_FULL_FIELD_MODE_REG));
+
+ dump_reg_range(sd, "CC data", TVP5150_CC_DATA_INI,
+ TVP5150_CC_DATA_END, 8);
+
+ dump_reg_range(sd, "WSS data", TVP5150_WSS_DATA_INI,
+ TVP5150_WSS_DATA_END, 8);
+
+ dump_reg_range(sd, "VPS data", TVP5150_VPS_DATA_INI,
+ TVP5150_VPS_DATA_END, 8);
+
+ dump_reg_range(sd, "VITC data", TVP5150_VITC_DATA_INI,
+ TVP5150_VITC_DATA_END, 10);
+
+ dump_reg_range(sd, "Line mode", TVP5150_LINE_MODE_INI,
+ TVP5150_LINE_MODE_END, 8);
+ return 0;
+}
+
+/****************************************************************************
+ Basic functions
+ ****************************************************************************/
+
+static inline void tvp5150_selmux(struct v4l2_subdev *sd)
+{
+ int opmode = 0;
+ struct tvp5150 *decoder = to_tvp5150(sd);
+ int input = 0;
+ unsigned char val;
+
+ if ((decoder->output & TVP5150_BLACK_SCREEN) || !decoder->enable)
+ input = 8;
+
+ switch (decoder->input) {
+ case TVP5150_COMPOSITE1:
+ input |= 2;
+ /* fall through */
+ case TVP5150_COMPOSITE0:
+ break;
+ case TVP5150_SVIDEO:
+ default:
+ input |= 1;
+ break;
+ }
+
+ v4l2_dbg(1, debug, sd, "Selecting video route: route input=%i, output=%i "
+ "=> tvp5150 input=%i, opmode=%i\n",
+ decoder->input, decoder->output,
+ input, opmode);
+
+ tvp5150_write(sd, TVP5150_OP_MODE_CTL, opmode);
+ tvp5150_write(sd, TVP5150_VD_IN_SRC_SEL_1, input);
+
+ /* Svideo should enable YCrCb output and disable GPCL output
+ * For Composite and TV, it should be the reverse
+ */
+ val = tvp5150_read(sd, TVP5150_MISC_CTL);
+ if (decoder->input == TVP5150_SVIDEO)
+ val = (val & ~0x40) | 0x10;
+ else
+ val = (val & ~0x10) | 0x40;
+ tvp5150_write(sd, TVP5150_MISC_CTL, val);
+};
+
+struct i2c_reg_value {
+ unsigned char reg;
+ unsigned char value;
+};
+
+/* Default values as sugested at TVP5150AM1 datasheet */
+static const struct i2c_reg_value tvp5150_init_default[] = {
+ { /* 0x00 */
+ TVP5150_VD_IN_SRC_SEL_1,0x00
+ },
+ { /* 0x01 */
+ TVP5150_ANAL_CHL_CTL,0x15
+ },
+ { /* 0x02 */
+ TVP5150_OP_MODE_CTL,0x00
+ },
+ { /* 0x03 */
+ TVP5150_MISC_CTL,0x01
+ },
+ { /* 0x06 */
+ TVP5150_COLOR_KIL_THSH_CTL,0x10
+ },
+ { /* 0x07 */
+ TVP5150_LUMA_PROC_CTL_1,0x60
+ },
+ { /* 0x08 */
+ TVP5150_LUMA_PROC_CTL_2,0x00
+ },
+ { /* 0x09 */
+ TVP5150_BRIGHT_CTL,0x80
+ },
+ { /* 0x0a */
+ TVP5150_SATURATION_CTL,0x80
+ },
+ { /* 0x0b */
+ TVP5150_HUE_CTL,0x00
+ },
+ { /* 0x0c */
+ TVP5150_CONTRAST_CTL,0x80
+ },
+ { /* 0x0d */
+ TVP5150_DATA_RATE_SEL,0x47
+ },
+ { /* 0x0e */
+ TVP5150_LUMA_PROC_CTL_3,0x00
+ },
+ { /* 0x0f */
+ TVP5150_CONF_SHARED_PIN,0x08
+ },
+ { /* 0x11 */
+ TVP5150_ACT_VD_CROP_ST_MSB,0x00
+ },
+ { /* 0x12 */
+ TVP5150_ACT_VD_CROP_ST_LSB,0x00
+ },
+ { /* 0x13 */
+ TVP5150_ACT_VD_CROP_STP_MSB,0x00
+ },
+ { /* 0x14 */
+ TVP5150_ACT_VD_CROP_STP_LSB,0x00
+ },
+ { /* 0x15 */
+ TVP5150_GENLOCK,0x01
+ },
+ { /* 0x16 */
+ TVP5150_HORIZ_SYNC_START,0x80
+ },
+ { /* 0x18 */
+ TVP5150_VERT_BLANKING_START,0x00
+ },
+ { /* 0x19 */
+ TVP5150_VERT_BLANKING_STOP,0x00
+ },
+ { /* 0x1a */
+ TVP5150_CHROMA_PROC_CTL_1,0x0c
+ },
+ { /* 0x1b */
+ TVP5150_CHROMA_PROC_CTL_2,0x14
+ },
+ { /* 0x1c */
+ TVP5150_INT_RESET_REG_B,0x00
+ },
+ { /* 0x1d */
+ TVP5150_INT_ENABLE_REG_B,0x00
+ },
+ { /* 0x1e */
+ TVP5150_INTT_CONFIG_REG_B,0x00
+ },
+ { /* 0x28 */
+ TVP5150_VIDEO_STD,0x00
+ },
+ { /* 0x2e */
+ TVP5150_MACROVISION_ON_CTR,0x0f
+ },
+ { /* 0x2f */
+ TVP5150_MACROVISION_OFF_CTR,0x01
+ },
+ { /* 0xbb */
+ TVP5150_TELETEXT_FIL_ENA,0x00
+ },
+ { /* 0xc0 */
+ TVP5150_INT_STATUS_REG_A,0x00
+ },
+ { /* 0xc1 */
+ TVP5150_INT_ENABLE_REG_A,0x00
+ },
+ { /* 0xc2 */
+ TVP5150_INT_CONF,0x04
+ },
+ { /* 0xc8 */
+ TVP5150_FIFO_INT_THRESHOLD,0x80
+ },
+ { /* 0xc9 */
+ TVP5150_FIFO_RESET,0x00
+ },
+ { /* 0xca */
+ TVP5150_LINE_NUMBER_INT,0x00
+ },
+ { /* 0xcb */
+ TVP5150_PIX_ALIGN_REG_LOW,0x4e
+ },
+ { /* 0xcc */
+ TVP5150_PIX_ALIGN_REG_HIGH,0x00
+ },
+ { /* 0xcd */
+ TVP5150_FIFO_OUT_CTRL,0x01
+ },
+ { /* 0xcf */
+ TVP5150_FULL_FIELD_ENA,0x00
+ },
+ { /* 0xd0 */
+ TVP5150_LINE_MODE_INI,0x00
+ },
+ { /* 0xfc */
+ TVP5150_FULL_FIELD_MODE_REG,0x7f
+ },
+ { /* end of data */
+ 0xff,0xff
+ }
+};
+
+/* Default values as sugested at TVP5150AM1 datasheet */
+static const struct i2c_reg_value tvp5150_init_enable[] = {
+ {
+ TVP5150_CONF_SHARED_PIN, 2
+ },{ /* Automatic offset and AGC enabled */
+ TVP5150_ANAL_CHL_CTL, 0x15
+ },{ /* Activate YCrCb output 0x9 or 0xd ? */
+ TVP5150_MISC_CTL, 0x6f
+ },{ /* Activates video std autodetection for all standards */
+ TVP5150_AUTOSW_MSK, 0x0
+ },{ /* Default format: 0x47. For 4:2:2: 0x40 */
+ TVP5150_DATA_RATE_SEL, 0x47
+ },{
+ TVP5150_CHROMA_PROC_CTL_1, 0x0c
+ },{
+ TVP5150_CHROMA_PROC_CTL_2, 0x54
+ },{ /* Non documented, but initialized on WinTV USB2 */
+ 0x27, 0x20
+ },{
+ 0xff,0xff
+ }
+};
+
+struct tvp5150_vbi_type {
+ unsigned int vbi_type;
+ unsigned int ini_line;
+ unsigned int end_line;
+ unsigned int by_field :1;
+};
+
+struct i2c_vbi_ram_value {
+ u16 reg;
+ struct tvp5150_vbi_type type;
+ unsigned char values[16];
+};
+
+/* This struct have the values for each supported VBI Standard
+ * by
+ tvp5150_vbi_types should follow the same order as vbi_ram_default
+ * value 0 means rom position 0x10, value 1 means rom position 0x30
+ * and so on. There are 16 possible locations from 0 to 15.
+ */
+
+static struct i2c_vbi_ram_value vbi_ram_default[] =
+{
+ /* FIXME: Current api doesn't handle all VBI types, those not
+ yet supported are placed under #if 0 */
+#if 0
+ {0x010, /* Teletext, SECAM, WST System A */
+ {V4L2_SLICED_TELETEXT_SECAM,6,23,1},
+ { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x26,
+ 0xe6, 0xb4, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00 }
+ },
+#endif
+ {0x030, /* Teletext, PAL, WST System B */
+ {V4L2_SLICED_TELETEXT_B,6,22,1},
+ { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x2b,
+ 0xa6, 0x72, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00 }
+ },
+#if 0
+ {0x050, /* Teletext, PAL, WST System C */
+ {V4L2_SLICED_TELETEXT_PAL_C,6,22,1},
+ { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22,
+ 0xa6, 0x98, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 }
+ },
+ {0x070, /* Teletext, NTSC, WST System B */
+ {V4L2_SLICED_TELETEXT_NTSC_B,10,21,1},
+ { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x23,
+ 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 }
+ },
+ {0x090, /* Tetetext, NTSC NABTS System C */
+ {V4L2_SLICED_TELETEXT_NTSC_C,10,21,1},
+ { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22,
+ 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x15, 0x00 }
+ },
+ {0x0b0, /* Teletext, NTSC-J, NABTS System D */
+ {V4L2_SLICED_TELETEXT_NTSC_D,10,21,1},
+ { 0xaa, 0xaa, 0xff, 0xff, 0xa7, 0x2e, 0x20, 0x23,
+ 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 }
+ },
+ {0x0d0, /* Closed Caption, PAL/SECAM */
+ {V4L2_SLICED_CAPTION_625,22,22,1},
+ { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02,
+ 0xa6, 0x7b, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 }
+ },
+#endif
+ {0x0f0, /* Closed Caption, NTSC */
+ {V4L2_SLICED_CAPTION_525,21,21,1},
+ { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02,
+ 0x69, 0x8c, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 }
+ },
+ {0x110, /* Wide Screen Signal, PAL/SECAM */
+ {V4L2_SLICED_WSS_625,23,23,1},
+ { 0x5b, 0x55, 0xc5, 0xff, 0x00, 0x71, 0x6e, 0x42,
+ 0xa6, 0xcd, 0x0f, 0x00, 0x00, 0x00, 0x3a, 0x00 }
+ },
+#if 0
+ {0x130, /* Wide Screen Signal, NTSC C */
+ {V4L2_SLICED_WSS_525,20,20,1},
+ { 0x38, 0x00, 0x3f, 0x00, 0x00, 0x71, 0x6e, 0x43,
+ 0x69, 0x7c, 0x08, 0x00, 0x00, 0x00, 0x39, 0x00 }
+ },
+ {0x150, /* Vertical Interval Timecode (VITC), PAL/SECAM */
+ {V4l2_SLICED_VITC_625,6,22,0},
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49,
+ 0xa6, 0x85, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 }
+ },
+ {0x170, /* Vertical Interval Timecode (VITC), NTSC */
+ {V4l2_SLICED_VITC_525,10,20,0},
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49,
+ 0x69, 0x94, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 }
+ },
+#endif
+ {0x190, /* Video Program System (VPS), PAL */
+ {V4L2_SLICED_VPS,16,16,0},
+ { 0xaa, 0xaa, 0xff, 0xff, 0xba, 0xce, 0x2b, 0x0d,
+ 0xa6, 0xda, 0x0b, 0x00, 0x00, 0x00, 0x60, 0x00 }
+ },
+ /* 0x1d0 User programmable */
+
+ /* End of struct */
+ { (u16)-1 }
+};
+
+static int tvp5150_write_inittab(struct v4l2_subdev *sd,
+ const struct i2c_reg_value *regs)
+{
+ while (regs->reg != 0xff) {
+ tvp5150_write(sd, regs->reg, regs->value);
+ regs++;
+ }
+ return 0;
+}
+
+static int tvp5150_vdp_init(struct v4l2_subdev *sd,
+ const struct i2c_vbi_ram_value *regs)
+{
+ unsigned int i;
+
+ /* Disable Full Field */
+ tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0);
+
+ /* Before programming, Line mode should be at 0xff */
+ for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++)
+ tvp5150_write(sd, i, 0xff);
+
+ /* Load Ram Table */
+ while (regs->reg != (u16)-1) {
+ tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_HIGH, regs->reg >> 8);
+ tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_LOW, regs->reg);
+
+ for (i = 0; i < 16; i++)
+ tvp5150_write(sd, TVP5150_VDP_CONF_RAM_DATA, regs->values[i]);
+
+ regs++;
+ }
+ return 0;
+}
+
+/* Fills VBI capabilities based on i2c_vbi_ram_value struct */
+static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd,
+ struct v4l2_sliced_vbi_cap *cap)
+{
+ const struct i2c_vbi_ram_value *regs = vbi_ram_default;
+ int line;
+
+ v4l2_dbg(1, debug, sd, "g_sliced_vbi_cap\n");
+ memset(cap, 0, sizeof *cap);
+
+ while (regs->reg != (u16)-1 ) {
+ for (line=regs->type.ini_line;line<=regs->type.end_line;line++) {
+ cap->service_lines[0][line] |= regs->type.vbi_type;
+ }
+ cap->service_set |= regs->type.vbi_type;
+
+ regs++;
+ }
+ return 0;
+}
+
+/* Set vbi processing
+ * type - one of tvp5150_vbi_types
+ * line - line to gather data
+ * fields: bit 0 field1, bit 1, field2
+ * flags (default=0xf0) is a bitmask, were set means:
+ * bit 7: enable filtering null bytes on CC
+ * bit 6: send data also to FIFO
+ * bit 5: don't allow data with errors on FIFO
+ * bit 4: enable ECC when possible
+ * pix_align = pix alignment:
+ * LSB = field1
+ * MSB = field2
+ */
+static int tvp5150_set_vbi(struct v4l2_subdev *sd,
+ const struct i2c_vbi_ram_value *regs,
+ unsigned int type,u8 flags, int line,
+ const int fields)
+{
+ struct tvp5150 *decoder = to_tvp5150(sd);
+ v4l2_std_id std = decoder->norm;
+ u8 reg;
+ int pos=0;
+
+ if (std == V4L2_STD_ALL) {
+ v4l2_err(sd, "VBI can't be configured without knowing number of lines\n");
+ return 0;
+ } else if (std & V4L2_STD_625_50) {
+ /* Don't follow NTSC Line number convension */
+ line += 3;
+ }
+
+ if (line<6||line>27)
+ return 0;
+
+ while (regs->reg != (u16)-1 ) {
+ if ((type & regs->type.vbi_type) &&
+ (line>=regs->type.ini_line) &&
+ (line<=regs->type.end_line)) {
+ type=regs->type.vbi_type;
+ break;
+ }
+
+ regs++;
+ pos++;
+ }
+ if (regs->reg == (u16)-1)
+ return 0;
+
+ type=pos | (flags & 0xf0);
+ reg=((line-6)<<1)+TVP5150_LINE_MODE_INI;
+
+ if (fields&1) {
+ tvp5150_write(sd, reg, type);
+ }
+
+ if (fields&2) {
+ tvp5150_write(sd, reg+1, type);
+ }
+
+ return type;
+}
+
+static int tvp5150_get_vbi(struct v4l2_subdev *sd,
+ const struct i2c_vbi_ram_value *regs, int line)
+{
+ struct tvp5150 *decoder = to_tvp5150(sd);
+ v4l2_std_id std = decoder->norm;
+ u8 reg;
+ int pos, type = 0;
+
+ if (std == V4L2_STD_ALL) {
+ v4l2_err(sd, "VBI can't be configured without knowing number of lines\n");
+ return 0;
+ } else if (std & V4L2_STD_625_50) {
+ /* Don't follow NTSC Line number convension */
+ line += 3;
+ }
+
+ if (line < 6 || line > 27)
+ return 0;
+
+ reg = ((line - 6) << 1) + TVP5150_LINE_MODE_INI;
+
+ pos = tvp5150_read(sd, reg) & 0x0f;
+ if (pos < 0x0f)
+ type = regs[pos].type.vbi_type;
+
+ pos = tvp5150_read(sd, reg + 1) & 0x0f;
+ if (pos < 0x0f)
+ type |= regs[pos].type.vbi_type;
+
+ return type;
+}
+
+static int tvp5150_set_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct tvp5150 *decoder = to_tvp5150(sd);
+ int fmt = 0;
+
+ decoder->norm = std;
+
+ /* First tests should be against specific std */
+
+ if (std == V4L2_STD_ALL) {
+ fmt = VIDEO_STD_AUTO_SWITCH_BIT; /* Autodetect mode */
+ } else if (std & V4L2_STD_NTSC_443) {
+ fmt = VIDEO_STD_NTSC_4_43_BIT;
+ } else if (std & V4L2_STD_PAL_M) {
+ fmt = VIDEO_STD_PAL_M_BIT;
+ } else if (std & (V4L2_STD_PAL_N | V4L2_STD_PAL_Nc)) {
+ fmt = VIDEO_STD_PAL_COMBINATION_N_BIT;
+ } else {
+ /* Then, test against generic ones */
+ if (std & V4L2_STD_NTSC)
+ fmt = VIDEO_STD_NTSC_MJ_BIT;
+ else if (std & V4L2_STD_PAL)
+ fmt = VIDEO_STD_PAL_BDGHIN_BIT;
+ else if (std & V4L2_STD_SECAM)
+ fmt = VIDEO_STD_SECAM_BIT;
+ }
+
+ v4l2_dbg(1, debug, sd, "Set video std register to %d.\n", fmt);
+ tvp5150_write(sd, TVP5150_VIDEO_STD, fmt);
+ return 0;
+}
+
+static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct tvp5150 *decoder = to_tvp5150(sd);
+
+ if (decoder->norm == std)
+ return 0;
+
+ /* Change cropping height limits */
+ if (std & V4L2_STD_525_60)
+ decoder->rect.height = TVP5150_V_MAX_525_60;
+ else
+ decoder->rect.height = TVP5150_V_MAX_OTHERS;
+
+
+ return tvp5150_set_std(sd, std);
+}
+
+static int tvp5150_reset(struct v4l2_subdev *sd, u32 val)
+{
+ struct tvp5150 *decoder = to_tvp5150(sd);
+
+ /* Initializes TVP5150 to its default values */
+ tvp5150_write_inittab(sd, tvp5150_init_default);
+
+ /* Initializes VDP registers */
+ tvp5150_vdp_init(sd, vbi_ram_default);
+
+ /* Selects decoder input */
+ tvp5150_selmux(sd);
+
+ /* Initializes TVP5150 to stream enabled values */
+ tvp5150_write_inittab(sd, tvp5150_init_enable);
+
+ /* Initialize image preferences */
+ v4l2_ctrl_handler_setup(&decoder->hdl);
+
+ tvp5150_set_std(sd, decoder->norm);
+ return 0;
+};
+
+static int tvp5150_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ tvp5150_write(sd, TVP5150_BRIGHT_CTL, ctrl->val);
+ return 0;
+ case V4L2_CID_CONTRAST:
+ tvp5150_write(sd, TVP5150_CONTRAST_CTL, ctrl->val);
+ return 0;
+ case V4L2_CID_SATURATION:
+ tvp5150_write(sd, TVP5150_SATURATION_CTL, ctrl->val);
+ return 0;
+ case V4L2_CID_HUE:
+ tvp5150_write(sd, TVP5150_HUE_CTL, ctrl->val);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static v4l2_std_id tvp5150_read_std(struct v4l2_subdev *sd)
+{
+ int val = tvp5150_read(sd, TVP5150_STATUS_REG_5);
+
+ switch (val & 0x0F) {
+ case 0x01:
+ return V4L2_STD_NTSC;
+ case 0x03:
+ return V4L2_STD_PAL;
+ case 0x05:
+ return V4L2_STD_PAL_M;
+ case 0x07:
+ return V4L2_STD_PAL_N | V4L2_STD_PAL_Nc;
+ case 0x09:
+ return V4L2_STD_NTSC_443;
+ case 0xb:
+ return V4L2_STD_SECAM;
+ default:
+ return V4L2_STD_UNKNOWN;
+ }
+}
+
+static int tvp5150_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index)
+ return -EINVAL;
+
+ *code = V4L2_MBUS_FMT_YUYV8_2X8;
+ return 0;
+}
+
+static int tvp5150_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *f)
+{
+ struct tvp5150 *decoder = to_tvp5150(sd);
+ v4l2_std_id std;
+
+ if (f == NULL)
+ return -EINVAL;
+
+ tvp5150_reset(sd, 0);
+
+ /* Calculate height and width based on current standard */
+ if (decoder->norm == V4L2_STD_ALL)
+ std = tvp5150_read_std(sd);
+ else
+ std = decoder->norm;
+
+ f->width = decoder->rect.width;
+ f->height = decoder->rect.height;
+
+ f->code = V4L2_MBUS_FMT_YUYV8_2X8;
+ f->field = V4L2_FIELD_SEQ_TB;
+ f->colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+ v4l2_dbg(1, debug, sd, "width = %d, height = %d\n", f->width,
+ f->height);
+ return 0;
+}
+
+static int tvp5150_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct v4l2_rect rect = a->c;
+ struct tvp5150 *decoder = to_tvp5150(sd);
+ v4l2_std_id std;
+ int hmax;
+
+ v4l2_dbg(1, debug, sd, "%s left=%d, top=%d, width=%d, height=%d\n",
+ __func__, rect.left, rect.top, rect.width, rect.height);
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ /* tvp5150 has some special limits */
+ rect.left = clamp(rect.left, 0, TVP5150_MAX_CROP_LEFT);
+ rect.width = clamp(rect.width,
+ TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left,
+ TVP5150_H_MAX - rect.left);
+ rect.top = clamp(rect.top, 0, TVP5150_MAX_CROP_TOP);
+
+ /* Calculate height based on current standard */
+ if (decoder->norm == V4L2_STD_ALL)
+ std = tvp5150_read_std(sd);
+ else
+ std = decoder->norm;
+
+ if (std & V4L2_STD_525_60)
+ hmax = TVP5150_V_MAX_525_60;
+ else
+ hmax = TVP5150_V_MAX_OTHERS;
+
+ rect.height = clamp(rect.height,
+ hmax - TVP5150_MAX_CROP_TOP - rect.top,
+ hmax - rect.top);
+
+ tvp5150_write(sd, TVP5150_VERT_BLANKING_START, rect.top);
+ tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP,
+ rect.top + rect.height - hmax);
+ tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_MSB,
+ rect.left >> TVP5150_CROP_SHIFT);
+ tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_LSB,
+ rect.left | (1 << TVP5150_CROP_SHIFT));
+ tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_MSB,
+ (rect.left + rect.width - TVP5150_MAX_CROP_LEFT) >>
+ TVP5150_CROP_SHIFT);
+ tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_LSB,
+ rect.left + rect.width - TVP5150_MAX_CROP_LEFT);
+
+ decoder->rect = rect;
+
+ return 0;
+}
+
+static int tvp5150_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd);
+
+ a->c = decoder->rect;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int tvp5150_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd);
+ v4l2_std_id std;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ a->bounds.width = TVP5150_H_MAX;
+
+ /* Calculate height based on current standard */
+ if (decoder->norm == V4L2_STD_ALL)
+ std = tvp5150_read_std(sd);
+ else
+ std = decoder->norm;
+
+ if (std & V4L2_STD_525_60)
+ a->bounds.height = TVP5150_V_MAX_525_60;
+ else
+ a->bounds.height = TVP5150_V_MAX_OTHERS;
+
+ a->defrect = a->bounds;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+/****************************************************************************
+ I2C Command
+ ****************************************************************************/
+
+static int tvp5150_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct tvp5150 *decoder = to_tvp5150(sd);
+
+ decoder->input = input;
+ decoder->output = output;
+ tvp5150_selmux(sd);
+ return 0;
+}
+
+static int tvp5150_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt)
+{
+ /* this is for capturing 36 raw vbi lines
+ if there's a way to cut off the beginning 2 vbi lines
+ with the tvp5150 then the vbi line count could be lowered
+ to 17 lines/field again, although I couldn't find a register
+ which could do that cropping */
+ if (fmt->sample_format == V4L2_PIX_FMT_GREY)
+ tvp5150_write(sd, TVP5150_LUMA_PROC_CTL_1, 0x70);
+ if (fmt->count[0] == 18 && fmt->count[1] == 18) {
+ tvp5150_write(sd, TVP5150_VERT_BLANKING_START, 0x00);
+ tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, 0x01);
+ }
+ return 0;
+}
+
+static int tvp5150_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
+{
+ int i;
+
+ if (svbi->service_set != 0) {
+ for (i = 0; i <= 23; i++) {
+ svbi->service_lines[1][i] = 0;
+ svbi->service_lines[0][i] =
+ tvp5150_set_vbi(sd, vbi_ram_default,
+ svbi->service_lines[0][i], 0xf0, i, 3);
+ }
+ /* Enables FIFO */
+ tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 1);
+ } else {
+ /* Disables FIFO*/
+ tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 0);
+
+ /* Disable Full Field */
+ tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0);
+
+ /* Disable Line modes */
+ for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++)
+ tvp5150_write(sd, i, 0xff);
+ }
+ return 0;
+}
+
+static int tvp5150_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
+{
+ int i, mask = 0;
+
+ memset(svbi, 0, sizeof(*svbi));
+
+ for (i = 0; i <= 23; i++) {
+ svbi->service_lines[0][i] =
+ tvp5150_get_vbi(sd, vbi_ram_default, i);
+ mask |= svbi->service_lines[0][i];
+ }
+ svbi->service_set = mask;
+ return 0;
+}
+
+static int tvp5150_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ int rev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ rev = tvp5150_read(sd, TVP5150_ROM_MAJOR_VER) << 8 |
+ tvp5150_read(sd, TVP5150_ROM_MINOR_VER);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVP5150,
+ rev);
+}
+
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = tvp5150_read(sd, reg->reg & 0xff);
+ reg->size = 1;
+ return 0;
+}
+
+static int tvp5150_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ tvp5150_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ return 0;
+}
+#endif
+
+static int tvp5150_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ int status = tvp5150_read(sd, 0x88);
+
+ vt->signal = ((status & 0x04) && (status & 0x02)) ? 0xffff : 0x0;
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = {
+ .s_ctrl = tvp5150_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops tvp5150_core_ops = {
+ .log_status = tvp5150_log_status,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+ .s_std = tvp5150_s_std,
+ .reset = tvp5150_reset,
+ .g_chip_ident = tvp5150_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = tvp5150_g_register,
+ .s_register = tvp5150_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = {
+ .g_tuner = tvp5150_g_tuner,
+};
+
+static const struct v4l2_subdev_video_ops tvp5150_video_ops = {
+ .s_routing = tvp5150_s_routing,
+ .enum_mbus_fmt = tvp5150_enum_mbus_fmt,
+ .s_mbus_fmt = tvp5150_mbus_fmt,
+ .try_mbus_fmt = tvp5150_mbus_fmt,
+ .g_mbus_fmt = tvp5150_mbus_fmt,
+ .s_crop = tvp5150_s_crop,
+ .g_crop = tvp5150_g_crop,
+ .cropcap = tvp5150_cropcap,
+};
+
+static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = {
+ .g_sliced_vbi_cap = tvp5150_g_sliced_vbi_cap,
+ .g_sliced_fmt = tvp5150_g_sliced_fmt,
+ .s_sliced_fmt = tvp5150_s_sliced_fmt,
+ .s_raw_fmt = tvp5150_s_raw_fmt,
+};
+
+static const struct v4l2_subdev_ops tvp5150_ops = {
+ .core = &tvp5150_core_ops,
+ .tuner = &tvp5150_tuner_ops,
+ .video = &tvp5150_video_ops,
+ .vbi = &tvp5150_vbi_ops,
+};
+
+
+/****************************************************************************
+ I2C Client & Driver
+ ****************************************************************************/
+
+static int tvp5150_probe(struct i2c_client *c,
+ const struct i2c_device_id *id)
+{
+ struct tvp5150 *core;
+ struct v4l2_subdev *sd;
+ u8 msb_id, lsb_id, msb_rom, lsb_rom;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(c->adapter,
+ I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
+ return -EIO;
+
+ core = kzalloc(sizeof(struct tvp5150), GFP_KERNEL);
+ if (!core) {
+ return -ENOMEM;
+ }
+ sd = &core->sd;
+ v4l2_i2c_subdev_init(sd, c, &tvp5150_ops);
+ v4l_info(c, "chip found @ 0x%02x (%s)\n",
+ c->addr << 1, c->adapter->name);
+
+ msb_id = tvp5150_read(sd, TVP5150_MSB_DEV_ID);
+ lsb_id = tvp5150_read(sd, TVP5150_LSB_DEV_ID);
+ msb_rom = tvp5150_read(sd, TVP5150_ROM_MAJOR_VER);
+ lsb_rom = tvp5150_read(sd, TVP5150_ROM_MINOR_VER);
+
+ if (msb_rom == 4 && lsb_rom == 0) { /* Is TVP5150AM1 */
+ v4l2_info(sd, "tvp%02x%02xam1 detected.\n", msb_id, lsb_id);
+
+ /* ITU-T BT.656.4 timing */
+ tvp5150_write(sd, TVP5150_REV_SELECT, 0);
+ } else {
+ if (msb_rom == 3 || lsb_rom == 0x21) { /* Is TVP5150A */
+ v4l2_info(sd, "tvp%02x%02xa detected.\n", msb_id, lsb_id);
+ } else {
+ v4l2_info(sd, "*** unknown tvp%02x%02x chip detected.\n",
+ msb_id, lsb_id);
+ v4l2_info(sd, "*** Rom ver is %d.%d\n", msb_rom, lsb_rom);
+ }
+ }
+
+ core->norm = V4L2_STD_ALL; /* Default is autodetect */
+ core->input = TVP5150_COMPOSITE1;
+ core->enable = 1;
+
+ v4l2_ctrl_handler_init(&core->hdl, 4);
+ v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops,
+ V4L2_CID_HUE, -128, 127, 1, 0);
+ sd->ctrl_handler = &core->hdl;
+ if (core->hdl.error) {
+ int err = core->hdl.error;
+
+ v4l2_ctrl_handler_free(&core->hdl);
+ kfree(core);
+ return err;
+ }
+ v4l2_ctrl_handler_setup(&core->hdl);
+
+ /* Default is no cropping */
+ core->rect.top = 0;
+ if (tvp5150_read_std(sd) & V4L2_STD_525_60)
+ core->rect.height = TVP5150_V_MAX_525_60;
+ else
+ core->rect.height = TVP5150_V_MAX_OTHERS;
+ core->rect.left = 0;
+ core->rect.width = TVP5150_H_MAX;
+
+ if (debug > 1)
+ tvp5150_log_status(sd);
+ return 0;
+}
+
+static int tvp5150_remove(struct i2c_client *c)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(c);
+ struct tvp5150 *decoder = to_tvp5150(sd);
+
+ v4l2_dbg(1, debug, sd,
+ "tvp5150.c: removing tvp5150 adapter on address 0x%x\n",
+ c->addr << 1);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&decoder->hdl);
+ kfree(to_tvp5150(sd));
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct i2c_device_id tvp5150_id[] = {
+ { "tvp5150", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tvp5150_id);
+
+static struct i2c_driver tvp5150_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "tvp5150",
+ },
+ .probe = tvp5150_probe,
+ .remove = tvp5150_remove,
+ .id_table = tvp5150_id,
+};
+
+module_i2c_driver(tvp5150_driver);
diff --git a/drivers/media/video/tvp5150_reg.h b/drivers/media/video/tvp5150_reg.h
new file mode 100644
index 00000000..25a99494
--- /dev/null
+++ b/drivers/media/video/tvp5150_reg.h
@@ -0,0 +1,139 @@
+/*
+ * tvp5150 - Texas Instruments TVP5150A/AM1 video decoder registers
+ *
+ * Copyright (c) 2005,2006 Mauro Carvalho Chehab (mchehab@infradead.org)
+ * This code is placed under the terms of the GNU General Public License v2
+ */
+
+#define TVP5150_VD_IN_SRC_SEL_1 0x00 /* Video input source selection #1 */
+#define TVP5150_ANAL_CHL_CTL 0x01 /* Analog channel controls */
+#define TVP5150_OP_MODE_CTL 0x02 /* Operation mode controls */
+#define TVP5150_MISC_CTL 0x03 /* Miscellaneous controls */
+#define TVP5150_AUTOSW_MSK 0x04 /* Autoswitch mask: TVP5150A / TVP5150AM */
+
+/* Reserved 05h */
+
+#define TVP5150_COLOR_KIL_THSH_CTL 0x06 /* Color killer threshold control */
+#define TVP5150_LUMA_PROC_CTL_1 0x07 /* Luminance processing control #1 */
+#define TVP5150_LUMA_PROC_CTL_2 0x08 /* Luminance processing control #2 */
+#define TVP5150_BRIGHT_CTL 0x09 /* Brightness control */
+#define TVP5150_SATURATION_CTL 0x0a /* Color saturation control */
+#define TVP5150_HUE_CTL 0x0b /* Hue control */
+#define TVP5150_CONTRAST_CTL 0x0c /* Contrast control */
+#define TVP5150_DATA_RATE_SEL 0x0d /* Outputs and data rates select */
+#define TVP5150_LUMA_PROC_CTL_3 0x0e /* Luminance processing control #3 */
+#define TVP5150_CONF_SHARED_PIN 0x0f /* Configuration shared pins */
+
+/* Reserved 10h */
+
+#define TVP5150_ACT_VD_CROP_ST_MSB 0x11 /* Active video cropping start MSB */
+#define TVP5150_ACT_VD_CROP_ST_LSB 0x12 /* Active video cropping start LSB */
+#define TVP5150_ACT_VD_CROP_STP_MSB 0x13 /* Active video cropping stop MSB */
+#define TVP5150_ACT_VD_CROP_STP_LSB 0x14 /* Active video cropping stop LSB */
+#define TVP5150_GENLOCK 0x15 /* Genlock/RTC */
+#define TVP5150_HORIZ_SYNC_START 0x16 /* Horizontal sync start */
+
+/* Reserved 17h */
+
+#define TVP5150_VERT_BLANKING_START 0x18 /* Vertical blanking start */
+#define TVP5150_VERT_BLANKING_STOP 0x19 /* Vertical blanking stop */
+#define TVP5150_CHROMA_PROC_CTL_1 0x1a /* Chrominance processing control #1 */
+#define TVP5150_CHROMA_PROC_CTL_2 0x1b /* Chrominance processing control #2 */
+#define TVP5150_INT_RESET_REG_B 0x1c /* Interrupt reset register B */
+#define TVP5150_INT_ENABLE_REG_B 0x1d /* Interrupt enable register B */
+#define TVP5150_INTT_CONFIG_REG_B 0x1e /* Interrupt configuration register B */
+
+/* Reserved 1Fh-27h */
+
+#define VIDEO_STD_MASK (0x07 >> 1)
+#define TVP5150_VIDEO_STD 0x28 /* Video standard */
+#define VIDEO_STD_AUTO_SWITCH_BIT 0x00
+#define VIDEO_STD_NTSC_MJ_BIT 0x02
+#define VIDEO_STD_PAL_BDGHIN_BIT 0x04
+#define VIDEO_STD_PAL_M_BIT 0x06
+#define VIDEO_STD_PAL_COMBINATION_N_BIT 0x08
+#define VIDEO_STD_NTSC_4_43_BIT 0x0a
+#define VIDEO_STD_SECAM_BIT 0x0c
+
+#define VIDEO_STD_NTSC_MJ_BIT_AS 0x01
+#define VIDEO_STD_PAL_BDGHIN_BIT_AS 0x03
+#define VIDEO_STD_PAL_M_BIT_AS 0x05
+#define VIDEO_STD_PAL_COMBINATION_N_BIT_AS 0x07
+#define VIDEO_STD_NTSC_4_43_BIT_AS 0x09
+#define VIDEO_STD_SECAM_BIT_AS 0x0b
+
+/* Reserved 29h-2bh */
+
+#define TVP5150_CB_GAIN_FACT 0x2c /* Cb gain factor */
+#define TVP5150_CR_GAIN_FACTOR 0x2d /* Cr gain factor */
+#define TVP5150_MACROVISION_ON_CTR 0x2e /* Macrovision on counter */
+#define TVP5150_MACROVISION_OFF_CTR 0x2f /* Macrovision off counter */
+#define TVP5150_REV_SELECT 0x30 /* revision select (TVP5150AM1 only) */
+
+/* Reserved 31h-7Fh */
+
+#define TVP5150_MSB_DEV_ID 0x80 /* MSB of device ID */
+#define TVP5150_LSB_DEV_ID 0x81 /* LSB of device ID */
+#define TVP5150_ROM_MAJOR_VER 0x82 /* ROM major version */
+#define TVP5150_ROM_MINOR_VER 0x83 /* ROM minor version */
+#define TVP5150_VERT_LN_COUNT_MSB 0x84 /* Vertical line count MSB */
+#define TVP5150_VERT_LN_COUNT_LSB 0x85 /* Vertical line count LSB */
+#define TVP5150_INT_STATUS_REG_B 0x86 /* Interrupt status register B */
+#define TVP5150_INT_ACTIVE_REG_B 0x87 /* Interrupt active register B */
+#define TVP5150_STATUS_REG_1 0x88 /* Status register #1 */
+#define TVP5150_STATUS_REG_2 0x89 /* Status register #2 */
+#define TVP5150_STATUS_REG_3 0x8a /* Status register #3 */
+#define TVP5150_STATUS_REG_4 0x8b /* Status register #4 */
+#define TVP5150_STATUS_REG_5 0x8c /* Status register #5 */
+/* Reserved 8Dh-8Fh */
+ /* Closed caption data registers */
+#define TVP5150_CC_DATA_INI 0x90
+#define TVP5150_CC_DATA_END 0x93
+
+ /* WSS data registers */
+#define TVP5150_WSS_DATA_INI 0x94
+#define TVP5150_WSS_DATA_END 0x99
+
+/* VPS data registers */
+#define TVP5150_VPS_DATA_INI 0x9a
+#define TVP5150_VPS_DATA_END 0xa6
+
+/* VITC data registers */
+#define TVP5150_VITC_DATA_INI 0xa7
+#define TVP5150_VITC_DATA_END 0xaf
+
+#define TVP5150_VBI_FIFO_READ_DATA 0xb0 /* VBI FIFO read data */
+
+/* Teletext filter 1 */
+#define TVP5150_TELETEXT_FIL1_INI 0xb1
+#define TVP5150_TELETEXT_FIL1_END 0xb5
+
+/* Teletext filter 2 */
+#define TVP5150_TELETEXT_FIL2_INI 0xb6
+#define TVP5150_TELETEXT_FIL2_END 0xba
+
+#define TVP5150_TELETEXT_FIL_ENA 0xbb /* Teletext filter enable */
+/* Reserved BCh-BFh */
+#define TVP5150_INT_STATUS_REG_A 0xc0 /* Interrupt status register A */
+#define TVP5150_INT_ENABLE_REG_A 0xc1 /* Interrupt enable register A */
+#define TVP5150_INT_CONF 0xc2 /* Interrupt configuration */
+#define TVP5150_VDP_CONF_RAM_DATA 0xc3 /* VDP configuration RAM data */
+#define TVP5150_CONF_RAM_ADDR_LOW 0xc4 /* Configuration RAM address low byte */
+#define TVP5150_CONF_RAM_ADDR_HIGH 0xc5 /* Configuration RAM address high byte */
+#define TVP5150_VDP_STATUS_REG 0xc6 /* VDP status register */
+#define TVP5150_FIFO_WORD_COUNT 0xc7 /* FIFO word count */
+#define TVP5150_FIFO_INT_THRESHOLD 0xc8 /* FIFO interrupt threshold */
+#define TVP5150_FIFO_RESET 0xc9 /* FIFO reset */
+#define TVP5150_LINE_NUMBER_INT 0xca /* Line number interrupt */
+#define TVP5150_PIX_ALIGN_REG_LOW 0xcb /* Pixel alignment register low byte */
+#define TVP5150_PIX_ALIGN_REG_HIGH 0xcc /* Pixel alignment register high byte */
+#define TVP5150_FIFO_OUT_CTRL 0xcd /* FIFO output control */
+/* Reserved CEh */
+#define TVP5150_FULL_FIELD_ENA 0xcf /* Full field enable 1 */
+
+/* Line mode registers */
+#define TVP5150_LINE_MODE_INI 0xd0
+#define TVP5150_LINE_MODE_END 0xfb
+
+#define TVP5150_FULL_FIELD_MODE_REG 0xfc /* Full field mode register */
+/* Reserved FDh-FFh */
diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c
new file mode 100644
index 00000000..d7676d85
--- /dev/null
+++ b/drivers/media/video/tvp7002.c
@@ -0,0 +1,1072 @@
+/* Texas Instruments Triple 8-/10-BIT 165-/110-MSPS Video and Graphics
+ * Digitizer with Horizontal PLL registers
+ *
+ * Copyright (C) 2009 Texas Instruments Inc
+ * Author: Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>
+ *
+ * This code is partially based upon the TVP5150 driver
+ * written by Mauro Carvalho Chehab (mchehab@infradead.org),
+ * the TVP514x driver written by Vaibhav Hiremath <hvaibhav@ti.com>
+ * and the TVP7002 driver in the TI LSP 2.10.00.14. Revisions by
+ * Muralidharan Karicheri and Snehaprabha Narnakaje (TI).
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/module.h>
+#include <media/tvp7002.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include "tvp7002_reg.h"
+
+MODULE_DESCRIPTION("TI TVP7002 Video and Graphics Digitizer driver");
+MODULE_AUTHOR("Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>");
+MODULE_LICENSE("GPL");
+
+/* Module Name */
+#define TVP7002_MODULE_NAME "tvp7002"
+
+/* I2C retry attempts */
+#define I2C_RETRY_COUNT (5)
+
+/* End of registers */
+#define TVP7002_EOR 0x5c
+
+/* Read write definition for registers */
+#define TVP7002_READ 0
+#define TVP7002_WRITE 1
+#define TVP7002_RESERVED 2
+
+/* Interlaced vs progressive mask and shift */
+#define TVP7002_IP_SHIFT 5
+#define TVP7002_INPR_MASK (0x01 << TVP7002_IP_SHIFT)
+
+/* Shift for CPL and LPF registers */
+#define TVP7002_CL_SHIFT 8
+#define TVP7002_CL_MASK 0x0f
+
+/* Debug functions */
+static bool debug;
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-2)");
+
+/* Structure for register values */
+struct i2c_reg_value {
+ u8 reg;
+ u8 value;
+ u8 type;
+};
+
+/*
+ * Register default values (according to tvp7002 datasheet)
+ * In the case of read-only registers, the value (0xff) is
+ * never written. R/W functionality is controlled by the
+ * writable bit in the register struct definition.
+ */
+static const struct i2c_reg_value tvp7002_init_default[] = {
+ { TVP7002_CHIP_REV, 0xff, TVP7002_READ },
+ { TVP7002_HPLL_FDBK_DIV_MSBS, 0x67, TVP7002_WRITE },
+ { TVP7002_HPLL_FDBK_DIV_LSBS, 0x20, TVP7002_WRITE },
+ { TVP7002_HPLL_CRTL, 0xa0, TVP7002_WRITE },
+ { TVP7002_HPLL_PHASE_SEL, 0x80, TVP7002_WRITE },
+ { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE },
+ { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE },
+ { TVP7002_HSYNC_OUT_W, 0x60, TVP7002_WRITE },
+ { TVP7002_B_FINE_GAIN, 0x00, TVP7002_WRITE },
+ { TVP7002_G_FINE_GAIN, 0x00, TVP7002_WRITE },
+ { TVP7002_R_FINE_GAIN, 0x00, TVP7002_WRITE },
+ { TVP7002_B_FINE_OFF_MSBS, 0x80, TVP7002_WRITE },
+ { TVP7002_G_FINE_OFF_MSBS, 0x80, TVP7002_WRITE },
+ { TVP7002_R_FINE_OFF_MSBS, 0x80, TVP7002_WRITE },
+ { TVP7002_SYNC_CTL_1, 0x20, TVP7002_WRITE },
+ { TVP7002_HPLL_AND_CLAMP_CTL, 0x2e, TVP7002_WRITE },
+ { TVP7002_SYNC_ON_G_THRS, 0x5d, TVP7002_WRITE },
+ { TVP7002_SYNC_SEPARATOR_THRS, 0x47, TVP7002_WRITE },
+ { TVP7002_HPLL_PRE_COAST, 0x00, TVP7002_WRITE },
+ { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE },
+ { TVP7002_SYNC_DETECT_STAT, 0xff, TVP7002_READ },
+ { TVP7002_OUT_FORMATTER, 0x47, TVP7002_WRITE },
+ { TVP7002_MISC_CTL_1, 0x01, TVP7002_WRITE },
+ { TVP7002_MISC_CTL_2, 0x00, TVP7002_WRITE },
+ { TVP7002_MISC_CTL_3, 0x01, TVP7002_WRITE },
+ { TVP7002_IN_MUX_SEL_1, 0x00, TVP7002_WRITE },
+ { TVP7002_IN_MUX_SEL_2, 0x67, TVP7002_WRITE },
+ { TVP7002_B_AND_G_COARSE_GAIN, 0x77, TVP7002_WRITE },
+ { TVP7002_R_COARSE_GAIN, 0x07, TVP7002_WRITE },
+ { TVP7002_FINE_OFF_LSBS, 0x00, TVP7002_WRITE },
+ { TVP7002_B_COARSE_OFF, 0x10, TVP7002_WRITE },
+ { TVP7002_G_COARSE_OFF, 0x10, TVP7002_WRITE },
+ { TVP7002_R_COARSE_OFF, 0x10, TVP7002_WRITE },
+ { TVP7002_HSOUT_OUT_START, 0x08, TVP7002_WRITE },
+ { TVP7002_MISC_CTL_4, 0x00, TVP7002_WRITE },
+ { TVP7002_B_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ },
+ { TVP7002_G_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ },
+ { TVP7002_R_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ },
+ { TVP7002_AUTO_LVL_CTL_ENABLE, 0x80, TVP7002_WRITE },
+ { TVP7002_DGTL_ALC_OUT_MSBS, 0xff, TVP7002_READ },
+ { TVP7002_AUTO_LVL_CTL_FILTER, 0x53, TVP7002_WRITE },
+ { 0x29, 0x08, TVP7002_RESERVED },
+ { TVP7002_FINE_CLAMP_CTL, 0x07, TVP7002_WRITE },
+ /* PWR_CTL is controlled only by the probe and reset functions */
+ { TVP7002_PWR_CTL, 0x00, TVP7002_RESERVED },
+ { TVP7002_ADC_SETUP, 0x50, TVP7002_WRITE },
+ { TVP7002_COARSE_CLAMP_CTL, 0x00, TVP7002_WRITE },
+ { TVP7002_SOG_CLAMP, 0x80, TVP7002_WRITE },
+ { TVP7002_RGB_COARSE_CLAMP_CTL, 0x8c, TVP7002_WRITE },
+ { TVP7002_SOG_COARSE_CLAMP_CTL, 0x04, TVP7002_WRITE },
+ { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE },
+ { 0x32, 0x18, TVP7002_RESERVED },
+ { 0x33, 0x60, TVP7002_RESERVED },
+ { TVP7002_MVIS_STRIPPER_W, 0xff, TVP7002_RESERVED },
+ { TVP7002_VSYNC_ALGN, 0x10, TVP7002_WRITE },
+ { TVP7002_SYNC_BYPASS, 0x00, TVP7002_WRITE },
+ { TVP7002_L_FRAME_STAT_LSBS, 0xff, TVP7002_READ },
+ { TVP7002_L_FRAME_STAT_MSBS, 0xff, TVP7002_READ },
+ { TVP7002_CLK_L_STAT_LSBS, 0xff, TVP7002_READ },
+ { TVP7002_CLK_L_STAT_MSBS, 0xff, TVP7002_READ },
+ { TVP7002_HSYNC_W, 0xff, TVP7002_READ },
+ { TVP7002_VSYNC_W, 0xff, TVP7002_READ },
+ { TVP7002_L_LENGTH_TOL, 0x03, TVP7002_WRITE },
+ { 0x3e, 0x60, TVP7002_RESERVED },
+ { TVP7002_VIDEO_BWTH_CTL, 0x01, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_LSBS, 0x01, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_MSBS, 0x2c, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_LSBS, 0x06, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_MSBS, 0x2c, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_DURATION, 0x1e, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE },
+ { TVP7002_FBIT_F_0_START_L_OFF, 0x00, TVP7002_WRITE },
+ { TVP7002_FBIT_F_1_START_L_OFF, 0x00, TVP7002_WRITE },
+ { TVP7002_YUV_Y_G_COEF_LSBS, 0xe3, TVP7002_WRITE },
+ { TVP7002_YUV_Y_G_COEF_MSBS, 0x16, TVP7002_WRITE },
+ { TVP7002_YUV_Y_B_COEF_LSBS, 0x4f, TVP7002_WRITE },
+ { TVP7002_YUV_Y_B_COEF_MSBS, 0x02, TVP7002_WRITE },
+ { TVP7002_YUV_Y_R_COEF_LSBS, 0xce, TVP7002_WRITE },
+ { TVP7002_YUV_Y_R_COEF_MSBS, 0x06, TVP7002_WRITE },
+ { TVP7002_YUV_U_G_COEF_LSBS, 0xab, TVP7002_WRITE },
+ { TVP7002_YUV_U_G_COEF_MSBS, 0xf3, TVP7002_WRITE },
+ { TVP7002_YUV_U_B_COEF_LSBS, 0x00, TVP7002_WRITE },
+ { TVP7002_YUV_U_B_COEF_MSBS, 0x10, TVP7002_WRITE },
+ { TVP7002_YUV_U_R_COEF_LSBS, 0x55, TVP7002_WRITE },
+ { TVP7002_YUV_U_R_COEF_MSBS, 0xfc, TVP7002_WRITE },
+ { TVP7002_YUV_V_G_COEF_LSBS, 0x78, TVP7002_WRITE },
+ { TVP7002_YUV_V_G_COEF_MSBS, 0xf1, TVP7002_WRITE },
+ { TVP7002_YUV_V_B_COEF_LSBS, 0x88, TVP7002_WRITE },
+ { TVP7002_YUV_V_B_COEF_MSBS, 0xfe, TVP7002_WRITE },
+ { TVP7002_YUV_V_R_COEF_LSBS, 0x00, TVP7002_WRITE },
+ { TVP7002_YUV_V_R_COEF_MSBS, 0x10, TVP7002_WRITE },
+ /* This signals end of register values */
+ { TVP7002_EOR, 0xff, TVP7002_RESERVED }
+};
+
+/* Register parameters for 480P */
+static const struct i2c_reg_value tvp7002_parms_480P[] = {
+ { TVP7002_HPLL_FDBK_DIV_MSBS, 0x35, TVP7002_WRITE },
+ { TVP7002_HPLL_FDBK_DIV_LSBS, 0xa0, TVP7002_WRITE },
+ { TVP7002_HPLL_CRTL, 0x02, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_LSBS, 0x91, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_MSBS, 0x00, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_LSBS, 0x0B, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_MSBS, 0x00, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_START_L_OFF, 0x03, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_START_L_OFF, 0x01, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_DURATION, 0x13, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_DURATION, 0x13, TVP7002_WRITE },
+ { TVP7002_ALC_PLACEMENT, 0x18, TVP7002_WRITE },
+ { TVP7002_CLAMP_START, 0x06, TVP7002_WRITE },
+ { TVP7002_CLAMP_W, 0x10, TVP7002_WRITE },
+ { TVP7002_HPLL_PRE_COAST, 0x03, TVP7002_WRITE },
+ { TVP7002_HPLL_POST_COAST, 0x03, TVP7002_WRITE },
+ { TVP7002_EOR, 0xff, TVP7002_RESERVED }
+};
+
+/* Register parameters for 576P */
+static const struct i2c_reg_value tvp7002_parms_576P[] = {
+ { TVP7002_HPLL_FDBK_DIV_MSBS, 0x36, TVP7002_WRITE },
+ { TVP7002_HPLL_FDBK_DIV_LSBS, 0x00, TVP7002_WRITE },
+ { TVP7002_HPLL_CRTL, 0x18, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_LSBS, 0x9B, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_MSBS, 0x00, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_LSBS, 0x0F, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_MSBS, 0x00, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_START_L_OFF, 0x00, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE },
+ { TVP7002_ALC_PLACEMENT, 0x18, TVP7002_WRITE },
+ { TVP7002_CLAMP_START, 0x06, TVP7002_WRITE },
+ { TVP7002_CLAMP_W, 0x10, TVP7002_WRITE },
+ { TVP7002_HPLL_PRE_COAST, 0x03, TVP7002_WRITE },
+ { TVP7002_HPLL_POST_COAST, 0x03, TVP7002_WRITE },
+ { TVP7002_EOR, 0xff, TVP7002_RESERVED }
+};
+
+/* Register parameters for 1080I60 */
+static const struct i2c_reg_value tvp7002_parms_1080I60[] = {
+ { TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE },
+ { TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE },
+ { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE },
+ { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE },
+ { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE },
+ { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE },
+ { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE },
+ { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE },
+ { TVP7002_EOR, 0xff, TVP7002_RESERVED }
+};
+
+/* Register parameters for 1080P60 */
+static const struct i2c_reg_value tvp7002_parms_1080P60[] = {
+ { TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE },
+ { TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE },
+ { TVP7002_HPLL_CRTL, 0xE0, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE },
+ { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE },
+ { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE },
+ { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE },
+ { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE },
+ { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE },
+ { TVP7002_EOR, 0xff, TVP7002_RESERVED }
+};
+
+/* Register parameters for 1080I50 */
+static const struct i2c_reg_value tvp7002_parms_1080I50[] = {
+ { TVP7002_HPLL_FDBK_DIV_MSBS, 0xa5, TVP7002_WRITE },
+ { TVP7002_HPLL_FDBK_DIV_LSBS, 0x00, TVP7002_WRITE },
+ { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE },
+ { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE },
+ { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE },
+ { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE },
+ { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE },
+ { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE },
+ { TVP7002_EOR, 0xff, TVP7002_RESERVED }
+};
+
+/* Register parameters for 720P60 */
+static const struct i2c_reg_value tvp7002_parms_720P60[] = {
+ { TVP7002_HPLL_FDBK_DIV_MSBS, 0x67, TVP7002_WRITE },
+ { TVP7002_HPLL_FDBK_DIV_LSBS, 0x20, TVP7002_WRITE },
+ { TVP7002_HPLL_CRTL, 0xa0, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_LSBS, 0x4B, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_MSBS, 0x06, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE },
+ { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE },
+ { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE },
+ { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE },
+ { TVP7002_HPLL_PRE_COAST, 0x00, TVP7002_WRITE },
+ { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE },
+ { TVP7002_EOR, 0xff, TVP7002_RESERVED }
+};
+
+/* Register parameters for 720P50 */
+static const struct i2c_reg_value tvp7002_parms_720P50[] = {
+ { TVP7002_HPLL_FDBK_DIV_MSBS, 0x7b, TVP7002_WRITE },
+ { TVP7002_HPLL_FDBK_DIV_LSBS, 0xc0, TVP7002_WRITE },
+ { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE },
+ { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_LSBS, 0x4B, TVP7002_WRITE },
+ { TVP7002_AVID_STOP_PIXEL_MSBS, 0x06, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE },
+ { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE },
+ { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE },
+ { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE },
+ { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE },
+ { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE },
+ { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE },
+ { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE },
+ { TVP7002_EOR, 0xff, TVP7002_RESERVED }
+};
+
+/* Preset definition for handling device operation */
+struct tvp7002_preset_definition {
+ u32 preset;
+ const struct i2c_reg_value *p_settings;
+ enum v4l2_colorspace color_space;
+ enum v4l2_field scanmode;
+ u16 progressive;
+ u16 lines_per_frame;
+ u16 cpl_min;
+ u16 cpl_max;
+};
+
+/* Struct list for digital video presets */
+static const struct tvp7002_preset_definition tvp7002_presets[] = {
+ {
+ V4L2_DV_720P60,
+ tvp7002_parms_720P60,
+ V4L2_COLORSPACE_REC709,
+ V4L2_FIELD_NONE,
+ 1,
+ 0x2EE,
+ 135,
+ 153
+ },
+ {
+ V4L2_DV_1080I60,
+ tvp7002_parms_1080I60,
+ V4L2_COLORSPACE_REC709,
+ V4L2_FIELD_INTERLACED,
+ 0,
+ 0x465,
+ 181,
+ 205
+ },
+ {
+ V4L2_DV_1080I50,
+ tvp7002_parms_1080I50,
+ V4L2_COLORSPACE_REC709,
+ V4L2_FIELD_INTERLACED,
+ 0,
+ 0x465,
+ 217,
+ 245
+ },
+ {
+ V4L2_DV_720P50,
+ tvp7002_parms_720P50,
+ V4L2_COLORSPACE_REC709,
+ V4L2_FIELD_NONE,
+ 1,
+ 0x2EE,
+ 163,
+ 183
+ },
+ {
+ V4L2_DV_1080P60,
+ tvp7002_parms_1080P60,
+ V4L2_COLORSPACE_REC709,
+ V4L2_FIELD_NONE,
+ 1,
+ 0x465,
+ 90,
+ 102
+ },
+ {
+ V4L2_DV_480P59_94,
+ tvp7002_parms_480P,
+ V4L2_COLORSPACE_SMPTE170M,
+ V4L2_FIELD_NONE,
+ 1,
+ 0x20D,
+ 0xffff,
+ 0xffff
+ },
+ {
+ V4L2_DV_576P50,
+ tvp7002_parms_576P,
+ V4L2_COLORSPACE_SMPTE170M,
+ V4L2_FIELD_NONE,
+ 1,
+ 0x271,
+ 0xffff,
+ 0xffff
+ }
+};
+
+#define NUM_PRESETS ARRAY_SIZE(tvp7002_presets)
+
+/* Device definition */
+struct tvp7002 {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+ const struct tvp7002_config *pdata;
+
+ int ver;
+ int streaming;
+
+ const struct tvp7002_preset_definition *current_preset;
+};
+
+/*
+ * to_tvp7002 - Obtain device handler TVP7002
+ * @sd: ptr to v4l2_subdev struct
+ *
+ * Returns device handler tvp7002.
+ */
+static inline struct tvp7002 *to_tvp7002(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct tvp7002, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct tvp7002, hdl)->sd;
+}
+
+/*
+ * tvp7002_read - Read a value from a register in an TVP7002
+ * @sd: ptr to v4l2_subdev struct
+ * @addr: TVP7002 register address
+ * @dst: pointer to 8-bit destination
+ *
+ * Returns value read if successful, or non-zero (-1) otherwise.
+ */
+static int tvp7002_read(struct v4l2_subdev *sd, u8 addr, u8 *dst)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
+ int retry;
+ int error;
+
+ for (retry = 0; retry < I2C_RETRY_COUNT; retry++) {
+ error = i2c_smbus_read_byte_data(c, addr);
+
+ if (error >= 0) {
+ *dst = (u8)error;
+ return 0;
+ }
+
+ msleep_interruptible(10);
+ }
+ v4l2_err(sd, "TVP7002 read error %d\n", error);
+ return error;
+}
+
+/*
+ * tvp7002_read_err() - Read a register value with error code
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @reg: destination register
+ * @val: value to be read
+ * @err: pointer to error value
+ *
+ * Read a value in a register and save error value in pointer.
+ * Also update the register table if successful
+ */
+static inline void tvp7002_read_err(struct v4l2_subdev *sd, u8 reg,
+ u8 *dst, int *err)
+{
+ if (!*err)
+ *err = tvp7002_read(sd, reg, dst);
+}
+
+/*
+ * tvp7002_write() - Write a value to a register in TVP7002
+ * @sd: ptr to v4l2_subdev struct
+ * @addr: TVP7002 register address
+ * @value: value to be written to the register
+ *
+ * Write a value to a register in an TVP7002 decoder device.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int tvp7002_write(struct v4l2_subdev *sd, u8 addr, u8 value)
+{
+ struct i2c_client *c;
+ int retry;
+ int error;
+
+ c = v4l2_get_subdevdata(sd);
+
+ for (retry = 0; retry < I2C_RETRY_COUNT; retry++) {
+ error = i2c_smbus_write_byte_data(c, addr, value);
+
+ if (error >= 0)
+ return 0;
+
+ v4l2_warn(sd, "Write: retry ... %d\n", retry);
+ msleep_interruptible(10);
+ }
+ v4l2_err(sd, "TVP7002 write error %d\n", error);
+ return error;
+}
+
+/*
+ * tvp7002_write_err() - Write a register value with error code
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @reg: destination register
+ * @val: value to be written
+ * @err: pointer to error value
+ *
+ * Write a value in a register and save error value in pointer.
+ * Also update the register table if successful
+ */
+static inline void tvp7002_write_err(struct v4l2_subdev *sd, u8 reg,
+ u8 val, int *err)
+{
+ if (!*err)
+ *err = tvp7002_write(sd, reg, val);
+}
+
+/*
+ * tvp7002_g_chip_ident() - Get chip identification number
+ * @sd: ptr to v4l2_subdev struct
+ * @chip: ptr to v4l2_dbg_chip_ident struct
+ *
+ * Obtains the chip's identification number.
+ * Returns zero or -EINVAL if read operation fails.
+ */
+static int tvp7002_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ u8 rev;
+ int error;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ error = tvp7002_read(sd, TVP7002_CHIP_REV, &rev);
+
+ if (error < 0)
+ return error;
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVP7002, rev);
+}
+
+/*
+ * tvp7002_write_inittab() - Write initialization values
+ * @sd: ptr to v4l2_subdev struct
+ * @regs: ptr to i2c_reg_value struct
+ *
+ * Write initialization values.
+ * Returns zero or -EINVAL if read operation fails.
+ */
+static int tvp7002_write_inittab(struct v4l2_subdev *sd,
+ const struct i2c_reg_value *regs)
+{
+ int error = 0;
+
+ /* Initialize the first (defined) registers */
+ while (TVP7002_EOR != regs->reg) {
+ if (TVP7002_WRITE == regs->type)
+ tvp7002_write_err(sd, regs->reg, regs->value, &error);
+ regs++;
+ }
+
+ return error;
+}
+
+/*
+ * tvp7002_s_dv_preset() - Set digital video preset
+ * @sd: ptr to v4l2_subdev struct
+ * @dv_preset: ptr to v4l2_dv_preset struct
+ *
+ * Set the digital video preset for a TVP7002 decoder device.
+ * Returns zero when successful or -EINVAL if register access fails.
+ */
+static int tvp7002_s_dv_preset(struct v4l2_subdev *sd,
+ struct v4l2_dv_preset *dv_preset)
+{
+ struct tvp7002 *device = to_tvp7002(sd);
+ u32 preset;
+ int i;
+
+ for (i = 0; i < NUM_PRESETS; i++) {
+ preset = tvp7002_presets[i].preset;
+ if (preset == dv_preset->preset) {
+ device->current_preset = &tvp7002_presets[i];
+ return tvp7002_write_inittab(sd, tvp7002_presets[i].p_settings);
+ }
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * tvp7002_s_ctrl() - Set a control
+ * @ctrl: ptr to v4l2_ctrl struct
+ *
+ * Set a control in TVP7002 decoder device.
+ * Returns zero when successful or -EINVAL if register access fails.
+ */
+static int tvp7002_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ int error = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ tvp7002_write_err(sd, TVP7002_R_FINE_GAIN, ctrl->val, &error);
+ tvp7002_write_err(sd, TVP7002_G_FINE_GAIN, ctrl->val, &error);
+ tvp7002_write_err(sd, TVP7002_B_FINE_GAIN, ctrl->val, &error);
+ return error;
+ }
+ return -EINVAL;
+}
+
+/*
+ * tvp7002_mbus_fmt() - V4L2 decoder interface handler for try/s/g_mbus_fmt
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @f: pointer to mediabus format structure
+ *
+ * Negotiate the image capture size and mediabus format.
+ * There is only one possible format, so this single function works for
+ * get, set and try.
+ */
+static int tvp7002_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f)
+{
+ struct tvp7002 *device = to_tvp7002(sd);
+ struct v4l2_dv_enum_preset e_preset;
+ int error;
+
+ /* Calculate height and width based on current standard */
+ error = v4l_fill_dv_preset_info(device->current_preset->preset, &e_preset);
+ if (error)
+ return error;
+
+ f->width = e_preset.width;
+ f->height = e_preset.height;
+ f->code = V4L2_MBUS_FMT_YUYV10_1X20;
+ f->field = device->current_preset->scanmode;
+ f->colorspace = device->current_preset->color_space;
+
+ v4l2_dbg(1, debug, sd, "MBUS_FMT: Width - %d, Height - %d",
+ f->width, f->height);
+ return 0;
+}
+
+/*
+ * tvp7002_query_dv_preset() - query DV preset
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @qpreset: standard V4L2 v4l2_dv_preset structure
+ *
+ * Returns the current DV preset by TVP7002. If no active input is
+ * detected, returns -EINVAL
+ */
+static int tvp7002_query_dv_preset(struct v4l2_subdev *sd,
+ struct v4l2_dv_preset *qpreset)
+{
+ const struct tvp7002_preset_definition *presets = tvp7002_presets;
+ struct tvp7002 *device;
+ u8 progressive;
+ u32 lpfr;
+ u32 cpln;
+ int error = 0;
+ u8 lpf_lsb;
+ u8 lpf_msb;
+ u8 cpl_lsb;
+ u8 cpl_msb;
+ int index;
+
+ /* Return invalid preset if no active input is detected */
+ qpreset->preset = V4L2_DV_INVALID;
+
+ device = to_tvp7002(sd);
+
+ /* Read standards from device registers */
+ tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_LSBS, &lpf_lsb, &error);
+ tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_MSBS, &lpf_msb, &error);
+
+ if (error < 0)
+ return error;
+
+ tvp7002_read_err(sd, TVP7002_CLK_L_STAT_LSBS, &cpl_lsb, &error);
+ tvp7002_read_err(sd, TVP7002_CLK_L_STAT_MSBS, &cpl_msb, &error);
+
+ if (error < 0)
+ return error;
+
+ /* Get lines per frame, clocks per line and interlaced/progresive */
+ lpfr = lpf_lsb | ((TVP7002_CL_MASK & lpf_msb) << TVP7002_CL_SHIFT);
+ cpln = cpl_lsb | ((TVP7002_CL_MASK & cpl_msb) << TVP7002_CL_SHIFT);
+ progressive = (lpf_msb & TVP7002_INPR_MASK) >> TVP7002_IP_SHIFT;
+
+ /* Do checking of video modes */
+ for (index = 0; index < NUM_PRESETS; index++, presets++)
+ if (lpfr == presets->lines_per_frame &&
+ progressive == presets->progressive) {
+ if (presets->cpl_min == 0xffff)
+ break;
+ if (cpln >= presets->cpl_min && cpln <= presets->cpl_max)
+ break;
+ }
+
+ if (index == NUM_PRESETS) {
+ v4l2_dbg(1, debug, sd, "detection failed: lpf = %x, cpl = %x\n",
+ lpfr, cpln);
+ return 0;
+ }
+
+ /* Set values in found preset */
+ qpreset->preset = presets->preset;
+
+ /* Update lines per frame and clocks per line info */
+ v4l2_dbg(1, debug, sd, "detected preset: %d\n", presets->preset);
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+/*
+ * tvp7002_g_register() - Get the value of a register
+ * @sd: ptr to v4l2_subdev struct
+ * @reg: ptr to v4l2_dbg_register struct
+ *
+ * Get the value of a TVP7002 decoder device register.
+ * Returns zero when successful, -EINVAL if register read fails or
+ * access to I2C client fails, -EPERM if the call is not allowed
+ * by disabled CAP_SYS_ADMIN.
+ */
+static int tvp7002_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 val;
+ int ret;
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ ret = tvp7002_read(sd, reg->reg & 0xff, &val);
+ reg->val = val;
+ return ret;
+}
+
+/*
+ * tvp7002_s_register() - set a control
+ * @sd: ptr to v4l2_subdev struct
+ * @reg: ptr to v4l2_dbg_register struct
+ *
+ * Get the value of a TVP7002 decoder device register.
+ * Returns zero when successful, -EINVAL if register read fails or
+ * -EPERM if call not allowed.
+ */
+static int tvp7002_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ return tvp7002_write(sd, reg->reg & 0xff, reg->val & 0xff);
+}
+#endif
+
+/*
+ * tvp7002_enum_mbus_fmt() - Enum supported mediabus formats
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @index: format index
+ * @code: pointer to mediabus format
+ *
+ * Enumerate supported mediabus formats.
+ */
+
+static int tvp7002_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ /* Check requested format index is within range */
+ if (index)
+ return -EINVAL;
+ *code = V4L2_MBUS_FMT_YUYV10_1X20;
+ return 0;
+}
+
+/*
+ * tvp7002_s_stream() - V4L2 decoder i/f handler for s_stream
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @enable: streaming enable or disable
+ *
+ * Sets streaming to enable or disable, if possible.
+ */
+static int tvp7002_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct tvp7002 *device = to_tvp7002(sd);
+ int error = 0;
+
+ if (device->streaming == enable)
+ return 0;
+
+ if (enable) {
+ /* Set output state on (low impedance means stream on) */
+ error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x00);
+ device->streaming = enable;
+ } else {
+ /* Set output state off (high impedance means stream off) */
+ error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x03);
+ if (error)
+ v4l2_dbg(1, debug, sd, "Unable to stop streaming\n");
+
+ device->streaming = enable;
+ }
+
+ return error;
+}
+
+/*
+ * tvp7002_log_status() - Print information about register settings
+ * @sd: ptr to v4l2_subdev struct
+ *
+ * Log register values of a TVP7002 decoder device.
+ * Returns zero or -EINVAL if read operation fails.
+ */
+static int tvp7002_log_status(struct v4l2_subdev *sd)
+{
+ const struct tvp7002_preset_definition *presets = tvp7002_presets;
+ struct tvp7002 *device = to_tvp7002(sd);
+ struct v4l2_dv_enum_preset e_preset;
+ struct v4l2_dv_preset detected;
+ int i;
+
+ detected.preset = V4L2_DV_INVALID;
+ /* Find my current standard*/
+ tvp7002_query_dv_preset(sd, &detected);
+
+ /* Print standard related code values */
+ for (i = 0; i < NUM_PRESETS; i++, presets++)
+ if (presets->preset == detected.preset)
+ break;
+
+ if (v4l_fill_dv_preset_info(device->current_preset->preset, &e_preset))
+ return -EINVAL;
+
+ v4l2_info(sd, "Selected DV Preset: %s\n", e_preset.name);
+ v4l2_info(sd, " Pixels per line: %u\n", e_preset.width);
+ v4l2_info(sd, " Lines per frame: %u\n\n", e_preset.height);
+ if (i == NUM_PRESETS) {
+ v4l2_info(sd, "Detected DV Preset: None\n");
+ } else {
+ if (v4l_fill_dv_preset_info(presets->preset, &e_preset))
+ return -EINVAL;
+ v4l2_info(sd, "Detected DV Preset: %s\n", e_preset.name);
+ v4l2_info(sd, " Pixels per line: %u\n", e_preset.width);
+ v4l2_info(sd, " Lines per frame: %u\n\n", e_preset.height);
+ }
+ v4l2_info(sd, "Streaming enabled: %s\n",
+ device->streaming ? "yes" : "no");
+
+ /* Print the current value of the gain control */
+ v4l2_ctrl_handler_log_status(&device->hdl, sd->name);
+
+ return 0;
+}
+
+/*
+ * tvp7002_enum_dv_presets() - Enum supported digital video formats
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @preset: pointer to format struct
+ *
+ * Enumerate supported digital video formats.
+ */
+static int tvp7002_enum_dv_presets(struct v4l2_subdev *sd,
+ struct v4l2_dv_enum_preset *preset)
+{
+ /* Check requested format index is within range */
+ if (preset->index >= NUM_PRESETS)
+ return -EINVAL;
+
+ return v4l_fill_dv_preset_info(tvp7002_presets[preset->index].preset, preset);
+}
+
+static const struct v4l2_ctrl_ops tvp7002_ctrl_ops = {
+ .s_ctrl = tvp7002_s_ctrl,
+};
+
+/* V4L2 core operation handlers */
+static const struct v4l2_subdev_core_ops tvp7002_core_ops = {
+ .g_chip_ident = tvp7002_g_chip_ident,
+ .log_status = tvp7002_log_status,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = tvp7002_g_register,
+ .s_register = tvp7002_s_register,
+#endif
+};
+
+/* Specific video subsystem operation handlers */
+static const struct v4l2_subdev_video_ops tvp7002_video_ops = {
+ .enum_dv_presets = tvp7002_enum_dv_presets,
+ .s_dv_preset = tvp7002_s_dv_preset,
+ .query_dv_preset = tvp7002_query_dv_preset,
+ .s_stream = tvp7002_s_stream,
+ .g_mbus_fmt = tvp7002_mbus_fmt,
+ .try_mbus_fmt = tvp7002_mbus_fmt,
+ .s_mbus_fmt = tvp7002_mbus_fmt,
+ .enum_mbus_fmt = tvp7002_enum_mbus_fmt,
+};
+
+/* V4L2 top level operation handlers */
+static const struct v4l2_subdev_ops tvp7002_ops = {
+ .core = &tvp7002_core_ops,
+ .video = &tvp7002_video_ops,
+};
+
+/*
+ * tvp7002_probe - Probe a TVP7002 device
+ * @c: ptr to i2c_client struct
+ * @id: ptr to i2c_device_id struct
+ *
+ * Initialize the TVP7002 device
+ * Returns zero when successful, -EINVAL if register read fails or
+ * -EIO if i2c access is not available.
+ */
+static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id)
+{
+ struct v4l2_subdev *sd;
+ struct tvp7002 *device;
+ struct v4l2_dv_preset preset;
+ int polarity_a;
+ int polarity_b;
+ u8 revision;
+
+ int error;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(c->adapter,
+ I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
+ return -EIO;
+
+ if (!c->dev.platform_data) {
+ v4l_err(c, "No platform data!!\n");
+ return -ENODEV;
+ }
+
+ device = kzalloc(sizeof(struct tvp7002), GFP_KERNEL);
+
+ if (!device)
+ return -ENOMEM;
+
+ sd = &device->sd;
+ device->pdata = c->dev.platform_data;
+ device->current_preset = tvp7002_presets;
+
+ /* Tell v4l2 the device is ready */
+ v4l2_i2c_subdev_init(sd, c, &tvp7002_ops);
+ v4l_info(c, "tvp7002 found @ 0x%02x (%s)\n",
+ c->addr, c->adapter->name);
+
+ error = tvp7002_read(sd, TVP7002_CHIP_REV, &revision);
+ if (error < 0)
+ goto found_error;
+
+ /* Get revision number */
+ v4l2_info(sd, "Rev. %02x detected.\n", revision);
+ if (revision != 0x02)
+ v4l2_info(sd, "Unknown revision detected.\n");
+
+ /* Initializes TVP7002 to its default values */
+ error = tvp7002_write_inittab(sd, tvp7002_init_default);
+
+ if (error < 0)
+ goto found_error;
+
+ /* Set polarity information after registers have been set */
+ polarity_a = 0x20 | device->pdata->hs_polarity << 5
+ | device->pdata->vs_polarity << 2;
+ error = tvp7002_write(sd, TVP7002_SYNC_CTL_1, polarity_a);
+ if (error < 0)
+ goto found_error;
+
+ polarity_b = 0x01 | device->pdata->fid_polarity << 2
+ | device->pdata->sog_polarity << 1
+ | device->pdata->clk_polarity;
+ error = tvp7002_write(sd, TVP7002_MISC_CTL_3, polarity_b);
+ if (error < 0)
+ goto found_error;
+
+ /* Set registers according to default video mode */
+ preset.preset = device->current_preset->preset;
+ error = tvp7002_s_dv_preset(sd, &preset);
+
+ v4l2_ctrl_handler_init(&device->hdl, 1);
+ v4l2_ctrl_new_std(&device->hdl, &tvp7002_ctrl_ops,
+ V4L2_CID_GAIN, 0, 255, 1, 0);
+ sd->ctrl_handler = &device->hdl;
+ if (device->hdl.error) {
+ int err = device->hdl.error;
+
+ v4l2_ctrl_handler_free(&device->hdl);
+ kfree(device);
+ return err;
+ }
+ v4l2_ctrl_handler_setup(&device->hdl);
+
+found_error:
+ if (error < 0)
+ kfree(device);
+
+ return error;
+}
+
+/*
+ * tvp7002_remove - Remove TVP7002 device support
+ * @c: ptr to i2c_client struct
+ *
+ * Reset the TVP7002 device
+ * Returns zero.
+ */
+static int tvp7002_remove(struct i2c_client *c)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(c);
+ struct tvp7002 *device = to_tvp7002(sd);
+
+ v4l2_dbg(1, debug, sd, "Removing tvp7002 adapter"
+ "on address 0x%x\n", c->addr);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&device->hdl);
+ kfree(device);
+ return 0;
+}
+
+/* I2C Device ID table */
+static const struct i2c_device_id tvp7002_id[] = {
+ { "tvp7002", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tvp7002_id);
+
+/* I2C driver data */
+static struct i2c_driver tvp7002_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = TVP7002_MODULE_NAME,
+ },
+ .probe = tvp7002_probe,
+ .remove = tvp7002_remove,
+ .id_table = tvp7002_id,
+};
+
+module_i2c_driver(tvp7002_driver);
diff --git a/drivers/media/video/tvp7002_reg.h b/drivers/media/video/tvp7002_reg.h
new file mode 100644
index 00000000..0e34ca9b
--- /dev/null
+++ b/drivers/media/video/tvp7002_reg.h
@@ -0,0 +1,150 @@
+/* Texas Instruments Triple 8-/10-BIT 165-/110-MSPS Video and Graphics
+ * Digitizer with Horizontal PLL registers
+ *
+ * Copyright (C) 2009 Texas Instruments Inc
+ * Author: Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>
+ *
+ * This code is partially based upon the TVP5150 driver
+ * written by Mauro Carvalho Chehab (mchehab@infradead.org),
+ * the TVP514x driver written by Vaibhav Hiremath <hvaibhav@ti.com>
+ * and the TVP7002 driver in the TI LSP 2.10.00.14
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Naming conventions
+ * ------------------
+ *
+ * FDBK: Feedback
+ * DIV: Divider
+ * CTL: Control
+ * SEL: Select
+ * IN: Input
+ * OUT: Output
+ * R: Red
+ * G: Green
+ * B: Blue
+ * OFF: Offset
+ * THRS: Threshold
+ * DGTL: Digital
+ * LVL: Level
+ * PWR: Power
+ * MVIS: Macrovision
+ * W: Width
+ * H: Height
+ * ALGN: Alignment
+ * CLK: Clocks
+ * TOL: Tolerance
+ * BWTH: Bandwidth
+ * COEF: Coefficient
+ * STAT: Status
+ * AUTO: Automatic
+ * FLD: Field
+ * L: Line
+ */
+
+#define TVP7002_CHIP_REV 0x00
+#define TVP7002_HPLL_FDBK_DIV_MSBS 0x01
+#define TVP7002_HPLL_FDBK_DIV_LSBS 0x02
+#define TVP7002_HPLL_CRTL 0x03
+#define TVP7002_HPLL_PHASE_SEL 0x04
+#define TVP7002_CLAMP_START 0x05
+#define TVP7002_CLAMP_W 0x06
+#define TVP7002_HSYNC_OUT_W 0x07
+#define TVP7002_B_FINE_GAIN 0x08
+#define TVP7002_G_FINE_GAIN 0x09
+#define TVP7002_R_FINE_GAIN 0x0a
+#define TVP7002_B_FINE_OFF_MSBS 0x0b
+#define TVP7002_G_FINE_OFF_MSBS 0x0c
+#define TVP7002_R_FINE_OFF_MSBS 0x0d
+#define TVP7002_SYNC_CTL_1 0x0e
+#define TVP7002_HPLL_AND_CLAMP_CTL 0x0f
+#define TVP7002_SYNC_ON_G_THRS 0x10
+#define TVP7002_SYNC_SEPARATOR_THRS 0x11
+#define TVP7002_HPLL_PRE_COAST 0x12
+#define TVP7002_HPLL_POST_COAST 0x13
+#define TVP7002_SYNC_DETECT_STAT 0x14
+#define TVP7002_OUT_FORMATTER 0x15
+#define TVP7002_MISC_CTL_1 0x16
+#define TVP7002_MISC_CTL_2 0x17
+#define TVP7002_MISC_CTL_3 0x18
+#define TVP7002_IN_MUX_SEL_1 0x19
+#define TVP7002_IN_MUX_SEL_2 0x1a
+#define TVP7002_B_AND_G_COARSE_GAIN 0x1b
+#define TVP7002_R_COARSE_GAIN 0x1c
+#define TVP7002_FINE_OFF_LSBS 0x1d
+#define TVP7002_B_COARSE_OFF 0x1e
+#define TVP7002_G_COARSE_OFF 0x1f
+#define TVP7002_R_COARSE_OFF 0x20
+#define TVP7002_HSOUT_OUT_START 0x21
+#define TVP7002_MISC_CTL_4 0x22
+#define TVP7002_B_DGTL_ALC_OUT_LSBS 0x23
+#define TVP7002_G_DGTL_ALC_OUT_LSBS 0x24
+#define TVP7002_R_DGTL_ALC_OUT_LSBS 0x25
+#define TVP7002_AUTO_LVL_CTL_ENABLE 0x26
+#define TVP7002_DGTL_ALC_OUT_MSBS 0x27
+#define TVP7002_AUTO_LVL_CTL_FILTER 0x28
+/* Reserved 0x29*/
+#define TVP7002_FINE_CLAMP_CTL 0x2a
+#define TVP7002_PWR_CTL 0x2b
+#define TVP7002_ADC_SETUP 0x2c
+#define TVP7002_COARSE_CLAMP_CTL 0x2d
+#define TVP7002_SOG_CLAMP 0x2e
+#define TVP7002_RGB_COARSE_CLAMP_CTL 0x2f
+#define TVP7002_SOG_COARSE_CLAMP_CTL 0x30
+#define TVP7002_ALC_PLACEMENT 0x31
+/* Reserved 0x32 */
+/* Reserved 0x33 */
+#define TVP7002_MVIS_STRIPPER_W 0x34
+#define TVP7002_VSYNC_ALGN 0x35
+#define TVP7002_SYNC_BYPASS 0x36
+#define TVP7002_L_FRAME_STAT_LSBS 0x37
+#define TVP7002_L_FRAME_STAT_MSBS 0x38
+#define TVP7002_CLK_L_STAT_LSBS 0x39
+#define TVP7002_CLK_L_STAT_MSBS 0x3a
+#define TVP7002_HSYNC_W 0x3b
+#define TVP7002_VSYNC_W 0x3c
+#define TVP7002_L_LENGTH_TOL 0x3d
+/* Reserved 0x3e */
+#define TVP7002_VIDEO_BWTH_CTL 0x3f
+#define TVP7002_AVID_START_PIXEL_LSBS 0x40
+#define TVP7002_AVID_START_PIXEL_MSBS 0x41
+#define TVP7002_AVID_STOP_PIXEL_LSBS 0x42
+#define TVP7002_AVID_STOP_PIXEL_MSBS 0x43
+#define TVP7002_VBLK_F_0_START_L_OFF 0x44
+#define TVP7002_VBLK_F_1_START_L_OFF 0x45
+#define TVP7002_VBLK_F_0_DURATION 0x46
+#define TVP7002_VBLK_F_1_DURATION 0x47
+#define TVP7002_FBIT_F_0_START_L_OFF 0x48
+#define TVP7002_FBIT_F_1_START_L_OFF 0x49
+#define TVP7002_YUV_Y_G_COEF_LSBS 0x4a
+#define TVP7002_YUV_Y_G_COEF_MSBS 0x4b
+#define TVP7002_YUV_Y_B_COEF_LSBS 0x4c
+#define TVP7002_YUV_Y_B_COEF_MSBS 0x4d
+#define TVP7002_YUV_Y_R_COEF_LSBS 0x4e
+#define TVP7002_YUV_Y_R_COEF_MSBS 0x4f
+#define TVP7002_YUV_U_G_COEF_LSBS 0x50
+#define TVP7002_YUV_U_G_COEF_MSBS 0x51
+#define TVP7002_YUV_U_B_COEF_LSBS 0x52
+#define TVP7002_YUV_U_B_COEF_MSBS 0x53
+#define TVP7002_YUV_U_R_COEF_LSBS 0x54
+#define TVP7002_YUV_U_R_COEF_MSBS 0x55
+#define TVP7002_YUV_V_G_COEF_LSBS 0x56
+#define TVP7002_YUV_V_G_COEF_MSBS 0x57
+#define TVP7002_YUV_V_B_COEF_LSBS 0x58
+#define TVP7002_YUV_V_B_COEF_MSBS 0x59
+#define TVP7002_YUV_V_R_COEF_LSBS 0x5a
+#define TVP7002_YUV_V_R_COEF_MSBS 0x5b
+
diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c
new file mode 100644
index 00000000..8768efb8
--- /dev/null
+++ b/drivers/media/video/tw9910.c
@@ -0,0 +1,958 @@
+/*
+ * tw9910 Video Driver
+ *
+ * Copyright (C) 2008 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on ov772x driver,
+ *
+ * Copyright (C) 2008 Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
+ * Copyright (C) 2008 Magnus Damm
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/videodev2.h>
+
+#include <media/soc_camera.h>
+#include <media/tw9910.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-subdev.h>
+
+#define GET_ID(val) ((val & 0xF8) >> 3)
+#define GET_REV(val) (val & 0x07)
+
+/*
+ * register offset
+ */
+#define ID 0x00 /* Product ID Code Register */
+#define STATUS1 0x01 /* Chip Status Register I */
+#define INFORM 0x02 /* Input Format */
+#define OPFORM 0x03 /* Output Format Control Register */
+#define DLYCTR 0x04 /* Hysteresis and HSYNC Delay Control */
+#define OUTCTR1 0x05 /* Output Control I */
+#define ACNTL1 0x06 /* Analog Control Register 1 */
+#define CROP_HI 0x07 /* Cropping Register, High */
+#define VDELAY_LO 0x08 /* Vertical Delay Register, Low */
+#define VACTIVE_LO 0x09 /* Vertical Active Register, Low */
+#define HDELAY_LO 0x0A /* Horizontal Delay Register, Low */
+#define HACTIVE_LO 0x0B /* Horizontal Active Register, Low */
+#define CNTRL1 0x0C /* Control Register I */
+#define VSCALE_LO 0x0D /* Vertical Scaling Register, Low */
+#define SCALE_HI 0x0E /* Scaling Register, High */
+#define HSCALE_LO 0x0F /* Horizontal Scaling Register, Low */
+#define BRIGHT 0x10 /* BRIGHTNESS Control Register */
+#define CONTRAST 0x11 /* CONTRAST Control Register */
+#define SHARPNESS 0x12 /* SHARPNESS Control Register I */
+#define SAT_U 0x13 /* Chroma (U) Gain Register */
+#define SAT_V 0x14 /* Chroma (V) Gain Register */
+#define HUE 0x15 /* Hue Control Register */
+#define CORING1 0x17
+#define CORING2 0x18 /* Coring and IF compensation */
+#define VBICNTL 0x19 /* VBI Control Register */
+#define ACNTL2 0x1A /* Analog Control 2 */
+#define OUTCTR2 0x1B /* Output Control 2 */
+#define SDT 0x1C /* Standard Selection */
+#define SDTR 0x1D /* Standard Recognition */
+#define TEST 0x1F /* Test Control Register */
+#define CLMPG 0x20 /* Clamping Gain */
+#define IAGC 0x21 /* Individual AGC Gain */
+#define AGCGAIN 0x22 /* AGC Gain */
+#define PEAKWT 0x23 /* White Peak Threshold */
+#define CLMPL 0x24 /* Clamp level */
+#define SYNCT 0x25 /* Sync Amplitude */
+#define MISSCNT 0x26 /* Sync Miss Count Register */
+#define PCLAMP 0x27 /* Clamp Position Register */
+#define VCNTL1 0x28 /* Vertical Control I */
+#define VCNTL2 0x29 /* Vertical Control II */
+#define CKILL 0x2A /* Color Killer Level Control */
+#define COMB 0x2B /* Comb Filter Control */
+#define LDLY 0x2C /* Luma Delay and H Filter Control */
+#define MISC1 0x2D /* Miscellaneous Control I */
+#define LOOP 0x2E /* LOOP Control Register */
+#define MISC2 0x2F /* Miscellaneous Control II */
+#define MVSN 0x30 /* Macrovision Detection */
+#define STATUS2 0x31 /* Chip STATUS II */
+#define HFREF 0x32 /* H monitor */
+#define CLMD 0x33 /* CLAMP MODE */
+#define IDCNTL 0x34 /* ID Detection Control */
+#define CLCNTL1 0x35 /* Clamp Control I */
+#define ANAPLLCTL 0x4C
+#define VBIMIN 0x4D
+#define HSLOWCTL 0x4E
+#define WSS3 0x4F
+#define FILLDATA 0x50
+#define SDID 0x51
+#define DID 0x52
+#define WSS1 0x53
+#define WSS2 0x54
+#define VVBI 0x55
+#define LCTL6 0x56
+#define LCTL7 0x57
+#define LCTL8 0x58
+#define LCTL9 0x59
+#define LCTL10 0x5A
+#define LCTL11 0x5B
+#define LCTL12 0x5C
+#define LCTL13 0x5D
+#define LCTL14 0x5E
+#define LCTL15 0x5F
+#define LCTL16 0x60
+#define LCTL17 0x61
+#define LCTL18 0x62
+#define LCTL19 0x63
+#define LCTL20 0x64
+#define LCTL21 0x65
+#define LCTL22 0x66
+#define LCTL23 0x67
+#define LCTL24 0x68
+#define LCTL25 0x69
+#define LCTL26 0x6A
+#define HSBEGIN 0x6B
+#define HSEND 0x6C
+#define OVSDLY 0x6D
+#define OVSEND 0x6E
+#define VBIDELAY 0x6F
+
+/*
+ * register detail
+ */
+
+/* INFORM */
+#define FC27_ON 0x40 /* 1 : Input crystal clock frequency is 27MHz */
+#define FC27_FF 0x00 /* 0 : Square pixel mode. */
+ /* Must use 24.54MHz for 60Hz field rate */
+ /* source or 29.5MHz for 50Hz field rate */
+#define IFSEL_S 0x10 /* 01 : S-video decoding */
+#define IFSEL_C 0x00 /* 00 : Composite video decoding */
+ /* Y input video selection */
+#define YSEL_M0 0x00 /* 00 : Mux0 selected */
+#define YSEL_M1 0x04 /* 01 : Mux1 selected */
+#define YSEL_M2 0x08 /* 10 : Mux2 selected */
+#define YSEL_M3 0x10 /* 11 : Mux3 selected */
+
+/* OPFORM */
+#define MODE 0x80 /* 0 : CCIR601 compatible YCrCb 4:2:2 format */
+ /* 1 : ITU-R-656 compatible data sequence format */
+#define LEN 0x40 /* 0 : 8-bit YCrCb 4:2:2 output format */
+ /* 1 : 16-bit YCrCb 4:2:2 output format.*/
+#define LLCMODE 0x20 /* 1 : LLC output mode. */
+ /* 0 : free-run output mode */
+#define AINC 0x10 /* Serial interface auto-indexing control */
+ /* 0 : auto-increment */
+ /* 1 : non-auto */
+#define VSCTL 0x08 /* 1 : Vertical out ctrl by DVALID */
+ /* 0 : Vertical out ctrl by HACTIVE and DVALID */
+#define OEN_TRI_SEL_MASK 0x07
+#define OEN_TRI_SEL_ALL_ON 0x00 /* Enable output for Rev0/Rev1 */
+#define OEN_TRI_SEL_ALL_OFF_r0 0x06 /* All tri-stated for Rev0 */
+#define OEN_TRI_SEL_ALL_OFF_r1 0x07 /* All tri-stated for Rev1 */
+
+/* OUTCTR1 */
+#define VSP_LO 0x00 /* 0 : VS pin output polarity is active low */
+#define VSP_HI 0x80 /* 1 : VS pin output polarity is active high. */
+ /* VS pin output control */
+#define VSSL_VSYNC 0x00 /* 0 : VSYNC */
+#define VSSL_VACT 0x10 /* 1 : VACT */
+#define VSSL_FIELD 0x20 /* 2 : FIELD */
+#define VSSL_VVALID 0x30 /* 3 : VVALID */
+#define VSSL_ZERO 0x70 /* 7 : 0 */
+#define HSP_LOW 0x00 /* 0 : HS pin output polarity is active low */
+#define HSP_HI 0x08 /* 1 : HS pin output polarity is active high.*/
+ /* HS pin output control */
+#define HSSL_HACT 0x00 /* 0 : HACT */
+#define HSSL_HSYNC 0x01 /* 1 : HSYNC */
+#define HSSL_DVALID 0x02 /* 2 : DVALID */
+#define HSSL_HLOCK 0x03 /* 3 : HLOCK */
+#define HSSL_ASYNCW 0x04 /* 4 : ASYNCW */
+#define HSSL_ZERO 0x07 /* 7 : 0 */
+
+/* ACNTL1 */
+#define SRESET 0x80 /* resets the device to its default state
+ * but all register content remain unchanged.
+ * This bit is self-resetting.
+ */
+#define ACNTL1_PDN_MASK 0x0e
+#define CLK_PDN 0x08 /* system clock power down */
+#define Y_PDN 0x04 /* Luma ADC power down */
+#define C_PDN 0x02 /* Chroma ADC power down */
+
+/* ACNTL2 */
+#define ACNTL2_PDN_MASK 0x40
+#define PLL_PDN 0x40 /* PLL power down */
+
+/* VBICNTL */
+
+/* RTSEL : control the real time signal output from the MPOUT pin */
+#define RTSEL_MASK 0x07
+#define RTSEL_VLOSS 0x00 /* 0000 = Video loss */
+#define RTSEL_HLOCK 0x01 /* 0001 = H-lock */
+#define RTSEL_SLOCK 0x02 /* 0010 = S-lock */
+#define RTSEL_VLOCK 0x03 /* 0011 = V-lock */
+#define RTSEL_MONO 0x04 /* 0100 = MONO */
+#define RTSEL_DET50 0x05 /* 0101 = DET50 */
+#define RTSEL_FIELD 0x06 /* 0110 = FIELD */
+#define RTSEL_RTCO 0x07 /* 0111 = RTCO ( Real Time Control ) */
+
+/* HSYNC start and end are constant for now */
+#define HSYNC_START 0x0260
+#define HSYNC_END 0x0300
+
+/*
+ * structure
+ */
+
+struct regval_list {
+ unsigned char reg_num;
+ unsigned char value;
+};
+
+struct tw9910_scale_ctrl {
+ char *name;
+ unsigned short width;
+ unsigned short height;
+ u16 hscale;
+ u16 vscale;
+};
+
+struct tw9910_priv {
+ struct v4l2_subdev subdev;
+ struct tw9910_video_info *info;
+ const struct tw9910_scale_ctrl *scale;
+ v4l2_std_id norm;
+ u32 revision;
+};
+
+static const struct tw9910_scale_ctrl tw9910_ntsc_scales[] = {
+ {
+ .name = "NTSC SQ",
+ .width = 640,
+ .height = 480,
+ .hscale = 0x0100,
+ .vscale = 0x0100,
+ },
+ {
+ .name = "NTSC CCIR601",
+ .width = 720,
+ .height = 480,
+ .hscale = 0x0100,
+ .vscale = 0x0100,
+ },
+ {
+ .name = "NTSC SQ (CIF)",
+ .width = 320,
+ .height = 240,
+ .hscale = 0x0200,
+ .vscale = 0x0200,
+ },
+ {
+ .name = "NTSC CCIR601 (CIF)",
+ .width = 360,
+ .height = 240,
+ .hscale = 0x0200,
+ .vscale = 0x0200,
+ },
+ {
+ .name = "NTSC SQ (QCIF)",
+ .width = 160,
+ .height = 120,
+ .hscale = 0x0400,
+ .vscale = 0x0400,
+ },
+ {
+ .name = "NTSC CCIR601 (QCIF)",
+ .width = 180,
+ .height = 120,
+ .hscale = 0x0400,
+ .vscale = 0x0400,
+ },
+};
+
+static const struct tw9910_scale_ctrl tw9910_pal_scales[] = {
+ {
+ .name = "PAL SQ",
+ .width = 768,
+ .height = 576,
+ .hscale = 0x0100,
+ .vscale = 0x0100,
+ },
+ {
+ .name = "PAL CCIR601",
+ .width = 720,
+ .height = 576,
+ .hscale = 0x0100,
+ .vscale = 0x0100,
+ },
+ {
+ .name = "PAL SQ (CIF)",
+ .width = 384,
+ .height = 288,
+ .hscale = 0x0200,
+ .vscale = 0x0200,
+ },
+ {
+ .name = "PAL CCIR601 (CIF)",
+ .width = 360,
+ .height = 288,
+ .hscale = 0x0200,
+ .vscale = 0x0200,
+ },
+ {
+ .name = "PAL SQ (QCIF)",
+ .width = 192,
+ .height = 144,
+ .hscale = 0x0400,
+ .vscale = 0x0400,
+ },
+ {
+ .name = "PAL CCIR601 (QCIF)",
+ .width = 180,
+ .height = 144,
+ .hscale = 0x0400,
+ .vscale = 0x0400,
+ },
+};
+
+/*
+ * general function
+ */
+static struct tw9910_priv *to_tw9910(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct tw9910_priv,
+ subdev);
+}
+
+static int tw9910_mask_set(struct i2c_client *client, u8 command,
+ u8 mask, u8 set)
+{
+ s32 val = i2c_smbus_read_byte_data(client, command);
+ if (val < 0)
+ return val;
+
+ val &= ~mask;
+ val |= set & mask;
+
+ return i2c_smbus_write_byte_data(client, command, val);
+}
+
+static int tw9910_set_scale(struct i2c_client *client,
+ const struct tw9910_scale_ctrl *scale)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, SCALE_HI,
+ (scale->vscale & 0x0F00) >> 4 |
+ (scale->hscale & 0x0F00) >> 8);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, HSCALE_LO,
+ scale->hscale & 0x00FF);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, VSCALE_LO,
+ scale->vscale & 0x00FF);
+
+ return ret;
+}
+
+static int tw9910_set_hsync(struct i2c_client *client)
+{
+ struct tw9910_priv *priv = to_tw9910(client);
+ int ret;
+
+ /* bit 10 - 3 */
+ ret = i2c_smbus_write_byte_data(client, HSBEGIN,
+ (HSYNC_START & 0x07F8) >> 3);
+ if (ret < 0)
+ return ret;
+
+ /* bit 10 - 3 */
+ ret = i2c_smbus_write_byte_data(client, HSEND,
+ (HSYNC_END & 0x07F8) >> 3);
+ if (ret < 0)
+ return ret;
+
+ /* So far only revisions 0 and 1 have been seen */
+ /* bit 2 - 0 */
+ if (1 == priv->revision)
+ ret = tw9910_mask_set(client, HSLOWCTL, 0x77,
+ (HSYNC_START & 0x0007) << 4 |
+ (HSYNC_END & 0x0007));
+
+ return ret;
+}
+
+static void tw9910_reset(struct i2c_client *client)
+{
+ tw9910_mask_set(client, ACNTL1, SRESET, SRESET);
+ msleep(1);
+}
+
+static int tw9910_power(struct i2c_client *client, int enable)
+{
+ int ret;
+ u8 acntl1;
+ u8 acntl2;
+
+ if (enable) {
+ acntl1 = 0;
+ acntl2 = 0;
+ } else {
+ acntl1 = CLK_PDN | Y_PDN | C_PDN;
+ acntl2 = PLL_PDN;
+ }
+
+ ret = tw9910_mask_set(client, ACNTL1, ACNTL1_PDN_MASK, acntl1);
+ if (ret < 0)
+ return ret;
+
+ return tw9910_mask_set(client, ACNTL2, ACNTL2_PDN_MASK, acntl2);
+}
+
+static const struct tw9910_scale_ctrl *tw9910_select_norm(v4l2_std_id norm,
+ u32 width, u32 height)
+{
+ const struct tw9910_scale_ctrl *scale;
+ const struct tw9910_scale_ctrl *ret = NULL;
+ __u32 diff = 0xffffffff, tmp;
+ int size, i;
+
+ if (norm & V4L2_STD_NTSC) {
+ scale = tw9910_ntsc_scales;
+ size = ARRAY_SIZE(tw9910_ntsc_scales);
+ } else if (norm & V4L2_STD_PAL) {
+ scale = tw9910_pal_scales;
+ size = ARRAY_SIZE(tw9910_pal_scales);
+ } else {
+ return NULL;
+ }
+
+ for (i = 0; i < size; i++) {
+ tmp = abs(width - scale[i].width) +
+ abs(height - scale[i].height);
+ if (tmp < diff) {
+ diff = tmp;
+ ret = scale + i;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * subdevice operations
+ */
+static int tw9910_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
+ u8 val;
+ int ret;
+
+ if (!enable) {
+ switch (priv->revision) {
+ case 0:
+ val = OEN_TRI_SEL_ALL_OFF_r0;
+ break;
+ case 1:
+ val = OEN_TRI_SEL_ALL_OFF_r1;
+ break;
+ default:
+ dev_err(&client->dev, "un-supported revision\n");
+ return -EINVAL;
+ }
+ } else {
+ val = OEN_TRI_SEL_ALL_ON;
+
+ if (!priv->scale) {
+ dev_err(&client->dev, "norm select error\n");
+ return -EPERM;
+ }
+
+ dev_dbg(&client->dev, "%s %dx%d\n",
+ priv->scale->name,
+ priv->scale->width,
+ priv->scale->height);
+ }
+
+ ret = tw9910_mask_set(client, OPFORM, OEN_TRI_SEL_MASK, val);
+ if (ret < 0)
+ return ret;
+
+ return tw9910_power(client, enable);
+}
+
+static int tw9910_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
+
+ *norm = priv->norm;
+
+ return 0;
+}
+
+static int tw9910_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
+
+ if (!(norm & (V4L2_STD_NTSC | V4L2_STD_PAL)))
+ return -EINVAL;
+
+ priv->norm = norm;
+
+ return 0;
+}
+
+static int tw9910_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
+
+ id->ident = V4L2_IDENT_TW9910;
+ id->revision = priv->revision;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int tw9910_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ if (reg->reg > 0xff)
+ return -EINVAL;
+
+ ret = i2c_smbus_read_byte_data(client, reg->reg);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * ret = int
+ * reg->val = __u64
+ */
+ reg->val = (__u64)ret;
+
+ return 0;
+}
+
+static int tw9910_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->reg > 0xff ||
+ reg->val > 0xff)
+ return -EINVAL;
+
+ return i2c_smbus_write_byte_data(client, reg->reg, reg->val);
+}
+#endif
+
+static int tw9910_set_frame(struct v4l2_subdev *sd, u32 *width, u32 *height)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
+ int ret = -EINVAL;
+ u8 val;
+
+ /*
+ * select suitable norm
+ */
+ priv->scale = tw9910_select_norm(priv->norm, *width, *height);
+ if (!priv->scale)
+ goto tw9910_set_fmt_error;
+
+ /*
+ * reset hardware
+ */
+ tw9910_reset(client);
+
+ /*
+ * set bus width
+ */
+ val = 0x00;
+ if (SOCAM_DATAWIDTH_16 == priv->info->buswidth)
+ val = LEN;
+
+ ret = tw9910_mask_set(client, OPFORM, LEN, val);
+ if (ret < 0)
+ goto tw9910_set_fmt_error;
+
+ /*
+ * select MPOUT behavior
+ */
+ switch (priv->info->mpout) {
+ case TW9910_MPO_VLOSS:
+ val = RTSEL_VLOSS; break;
+ case TW9910_MPO_HLOCK:
+ val = RTSEL_HLOCK; break;
+ case TW9910_MPO_SLOCK:
+ val = RTSEL_SLOCK; break;
+ case TW9910_MPO_VLOCK:
+ val = RTSEL_VLOCK; break;
+ case TW9910_MPO_MONO:
+ val = RTSEL_MONO; break;
+ case TW9910_MPO_DET50:
+ val = RTSEL_DET50; break;
+ case TW9910_MPO_FIELD:
+ val = RTSEL_FIELD; break;
+ case TW9910_MPO_RTCO:
+ val = RTSEL_RTCO; break;
+ default:
+ val = 0;
+ }
+
+ ret = tw9910_mask_set(client, VBICNTL, RTSEL_MASK, val);
+ if (ret < 0)
+ goto tw9910_set_fmt_error;
+
+ /*
+ * set scale
+ */
+ ret = tw9910_set_scale(client, priv->scale);
+ if (ret < 0)
+ goto tw9910_set_fmt_error;
+
+ /*
+ * set hsync
+ */
+ ret = tw9910_set_hsync(client);
+ if (ret < 0)
+ goto tw9910_set_fmt_error;
+
+ *width = priv->scale->width;
+ *height = priv->scale->height;
+
+ return ret;
+
+tw9910_set_fmt_error:
+
+ tw9910_reset(client);
+ priv->scale = NULL;
+
+ return ret;
+}
+
+static int tw9910_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
+
+ a->c.left = 0;
+ a->c.top = 0;
+ if (priv->norm & V4L2_STD_NTSC) {
+ a->c.width = 640;
+ a->c.height = 480;
+ } else {
+ a->c.width = 768;
+ a->c.height = 576;
+ }
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int tw9910_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
+
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ if (priv->norm & V4L2_STD_NTSC) {
+ a->bounds.width = 640;
+ a->bounds.height = 480;
+ } else {
+ a->bounds.width = 768;
+ a->bounds.height = 576;
+ }
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int tw9910_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
+
+ if (!priv->scale) {
+ int ret;
+ u32 width = 640, height = 480;
+ ret = tw9910_set_frame(sd, &width, &height);
+ if (ret < 0)
+ return ret;
+ }
+
+ mf->width = priv->scale->width;
+ mf->height = priv->scale->height;
+ mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ mf->field = V4L2_FIELD_INTERLACED_BT;
+
+ return 0;
+}
+
+static int tw9910_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ u32 width = mf->width, height = mf->height;
+ int ret;
+
+ WARN_ON(mf->field != V4L2_FIELD_ANY &&
+ mf->field != V4L2_FIELD_INTERLACED_BT);
+
+ /*
+ * check color format
+ */
+ if (mf->code != V4L2_MBUS_FMT_UYVY8_2X8)
+ return -EINVAL;
+
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+
+ ret = tw9910_set_frame(sd, &width, &height);
+ if (!ret) {
+ mf->width = width;
+ mf->height = height;
+ }
+ return ret;
+}
+
+static int tw9910_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
+ const struct tw9910_scale_ctrl *scale;
+
+ if (V4L2_FIELD_ANY == mf->field) {
+ mf->field = V4L2_FIELD_INTERLACED_BT;
+ } else if (V4L2_FIELD_INTERLACED_BT != mf->field) {
+ dev_err(&client->dev, "Field type %d invalid.\n", mf->field);
+ return -EINVAL;
+ }
+
+ mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+
+ /*
+ * select suitable norm
+ */
+ scale = tw9910_select_norm(priv->norm, mf->width, mf->height);
+ if (!scale)
+ return -EINVAL;
+
+ mf->width = scale->width;
+ mf->height = scale->height;
+
+ return 0;
+}
+
+static int tw9910_video_probe(struct i2c_client *client)
+{
+ struct tw9910_priv *priv = to_tw9910(client);
+ s32 id;
+
+ /*
+ * tw9910 only use 8 or 16 bit bus width
+ */
+ if (SOCAM_DATAWIDTH_16 != priv->info->buswidth &&
+ SOCAM_DATAWIDTH_8 != priv->info->buswidth) {
+ dev_err(&client->dev, "bus width error\n");
+ return -ENODEV;
+ }
+
+ /*
+ * check and show Product ID
+ * So far only revisions 0 and 1 have been seen
+ */
+ id = i2c_smbus_read_byte_data(client, ID);
+ priv->revision = GET_REV(id);
+ id = GET_ID(id);
+
+ if (0x0B != id ||
+ 0x01 < priv->revision) {
+ dev_err(&client->dev,
+ "Product ID error %x:%x\n",
+ id, priv->revision);
+ return -ENODEV;
+ }
+
+ dev_info(&client->dev,
+ "tw9910 Product ID %0x:%0x\n", id, priv->revision);
+
+ priv->norm = V4L2_STD_NTSC;
+
+ return 0;
+}
+
+static struct v4l2_subdev_core_ops tw9910_subdev_core_ops = {
+ .g_chip_ident = tw9910_g_chip_ident,
+ .s_std = tw9910_s_std,
+ .g_std = tw9910_g_std,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = tw9910_g_register,
+ .s_register = tw9910_s_register,
+#endif
+};
+
+static int tw9910_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index)
+ return -EINVAL;
+
+ *code = V4L2_MBUS_FMT_UYVY8_2X8;
+ return 0;
+}
+
+static int tw9910_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static int tw9910_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ u8 val = VSSL_VVALID | HSSL_DVALID;
+ unsigned long flags = soc_camera_apply_board_flags(icl, cfg);
+
+ /*
+ * set OUTCTR1
+ *
+ * We use VVALID and DVALID signals to control VSYNC and HSYNC
+ * outputs, in this mode their polarity is inverted.
+ */
+ if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ val |= HSP_HI;
+
+ if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ val |= VSP_HI;
+
+ return i2c_smbus_write_byte_data(client, OUTCTR1, val);
+}
+
+static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = {
+ .s_stream = tw9910_s_stream,
+ .g_mbus_fmt = tw9910_g_fmt,
+ .s_mbus_fmt = tw9910_s_fmt,
+ .try_mbus_fmt = tw9910_try_fmt,
+ .cropcap = tw9910_cropcap,
+ .g_crop = tw9910_g_crop,
+ .enum_mbus_fmt = tw9910_enum_fmt,
+ .g_mbus_config = tw9910_g_mbus_config,
+ .s_mbus_config = tw9910_s_mbus_config,
+};
+
+static struct v4l2_subdev_ops tw9910_subdev_ops = {
+ .core = &tw9910_subdev_core_ops,
+ .video = &tw9910_subdev_video_ops,
+};
+
+/*
+ * i2c_driver function
+ */
+
+static int tw9910_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+
+{
+ struct tw9910_priv *priv;
+ struct tw9910_video_info *info;
+ struct i2c_adapter *adapter =
+ to_i2c_adapter(client->dev.parent);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ int ret;
+
+ if (!icl || !icl->priv) {
+ dev_err(&client->dev, "TW9910: missing platform data!\n");
+ return -EINVAL;
+ }
+
+ info = icl->priv;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&client->dev,
+ "I2C-Adapter doesn't support "
+ "I2C_FUNC_SMBUS_BYTE_DATA\n");
+ return -EIO;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->info = info;
+
+ v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops);
+
+ ret = tw9910_video_probe(client);
+ if (ret)
+ kfree(priv);
+
+ return ret;
+}
+
+static int tw9910_remove(struct i2c_client *client)
+{
+ struct tw9910_priv *priv = to_tw9910(client);
+
+ kfree(priv);
+ return 0;
+}
+
+static const struct i2c_device_id tw9910_id[] = {
+ { "tw9910", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tw9910_id);
+
+static struct i2c_driver tw9910_i2c_driver = {
+ .driver = {
+ .name = "tw9910",
+ },
+ .probe = tw9910_probe,
+ .remove = tw9910_remove,
+ .id_table = tw9910_id,
+};
+
+module_i2c_driver(tw9910_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for tw9910");
+MODULE_AUTHOR("Kuninori Morimoto");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/upd64031a.c b/drivers/media/video/upd64031a.c
new file mode 100644
index 00000000..1e744654
--- /dev/null
+++ b/drivers/media/video/upd64031a.c
@@ -0,0 +1,274 @@
+/*
+ * upd64031A - NEC Electronics Ghost Reduction for NTSC in Japan
+ *
+ * 2003 by T.Adachi <tadachi@tadachi-net.com>
+ * 2003 by Takeru KOMORIYA <komoriya@paken.org>
+ * 2006 by Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/upd64031a.h>
+
+/* --------------------- read registers functions define -------------------- */
+
+/* bit masks */
+#define GR_MODE_MASK 0xc0
+#define DIRECT_3DYCS_CONNECT_MASK 0xc0
+#define SYNC_CIRCUIT_MASK 0xa0
+
+/* -------------------------------------------------------------------------- */
+
+MODULE_DESCRIPTION("uPD64031A driver");
+MODULE_AUTHOR("T. Adachi, Takeru KOMORIYA, Hans Verkuil");
+MODULE_LICENSE("GPL");
+
+static int debug;
+module_param(debug, int, 0644);
+
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+
+enum {
+ R00 = 0, R01, R02, R03, R04,
+ R05, R06, R07, R08, R09,
+ R0A, R0B, R0C, R0D, R0E, R0F,
+ /* unused registers
+ R10, R11, R12, R13, R14,
+ R15, R16, R17,
+ */
+ TOT_REGS
+};
+
+struct upd64031a_state {
+ struct v4l2_subdev sd;
+ u8 regs[TOT_REGS];
+ u8 gr_mode;
+ u8 direct_3dycs_connect;
+ u8 ext_comp_sync;
+ u8 ext_vert_sync;
+};
+
+static inline struct upd64031a_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct upd64031a_state, sd);
+}
+
+static u8 upd64031a_init[] = {
+ 0x00, 0xb8, 0x48, 0xd2, 0xe6,
+ 0x03, 0x10, 0x0b, 0xaf, 0x7f,
+ 0x00, 0x00, 0x1d, 0x5e, 0x00,
+ 0xd0
+};
+
+/* ------------------------------------------------------------------------ */
+
+static u8 upd64031a_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 buf[2];
+
+ if (reg >= sizeof(buf))
+ return 0xff;
+ i2c_master_recv(client, buf, 2);
+ return buf[reg];
+}
+
+/* ------------------------------------------------------------------------ */
+
+static void upd64031a_write(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 buf[2];
+
+ buf[0] = reg;
+ buf[1] = val;
+ v4l2_dbg(1, debug, sd, "write reg: %02X val: %02X\n", reg, val);
+ if (i2c_master_send(client, buf, 2) != 2)
+ v4l2_err(sd, "I/O error write 0x%02x/0x%02x\n", reg, val);
+}
+
+/* ------------------------------------------------------------------------ */
+
+/* The input changed due to new input or channel changed */
+static int upd64031a_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
+{
+ struct upd64031a_state *state = to_state(sd);
+ u8 reg = state->regs[R00];
+
+ v4l2_dbg(1, debug, sd, "changed input or channel\n");
+ upd64031a_write(sd, R00, reg | 0x10);
+ upd64031a_write(sd, R00, reg & ~0x10);
+ return 0;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int upd64031a_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct upd64031a_state *state = to_state(sd);
+ u8 r00, r05, r08;
+
+ state->gr_mode = (input & 3) << 6;
+ state->direct_3dycs_connect = (input & 0xc) << 4;
+ state->ext_comp_sync =
+ (input & UPD64031A_COMPOSITE_EXTERNAL) << 1;
+ state->ext_vert_sync =
+ (input & UPD64031A_VERTICAL_EXTERNAL) << 2;
+ r00 = (state->regs[R00] & ~GR_MODE_MASK) | state->gr_mode;
+ r05 = (state->regs[R00] & ~SYNC_CIRCUIT_MASK) |
+ state->ext_comp_sync | state->ext_vert_sync;
+ r08 = (state->regs[R08] & ~DIRECT_3DYCS_CONNECT_MASK) |
+ state->direct_3dycs_connect;
+ upd64031a_write(sd, R00, r00);
+ upd64031a_write(sd, R05, r05);
+ upd64031a_write(sd, R08, r08);
+ return upd64031a_s_frequency(sd, NULL);
+}
+
+static int upd64031a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_UPD64031A, 0);
+}
+
+static int upd64031a_log_status(struct v4l2_subdev *sd)
+{
+ v4l2_info(sd, "Status: SA00=0x%02x SA01=0x%02x\n",
+ upd64031a_read(sd, 0), upd64031a_read(sd, 1));
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int upd64031a_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = upd64031a_read(sd, reg->reg & 0xff);
+ reg->size = 1;
+ return 0;
+}
+
+static int upd64031a_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ upd64031a_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ return 0;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops upd64031a_core_ops = {
+ .log_status = upd64031a_log_status,
+ .g_chip_ident = upd64031a_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = upd64031a_g_register,
+ .s_register = upd64031a_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_tuner_ops upd64031a_tuner_ops = {
+ .s_frequency = upd64031a_s_frequency,
+};
+
+static const struct v4l2_subdev_video_ops upd64031a_video_ops = {
+ .s_routing = upd64031a_s_routing,
+};
+
+static const struct v4l2_subdev_ops upd64031a_ops = {
+ .core = &upd64031a_core_ops,
+ .tuner = &upd64031a_tuner_ops,
+ .video = &upd64031a_video_ops,
+};
+
+/* ------------------------------------------------------------------------ */
+
+/* i2c implementation */
+
+static int upd64031a_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct upd64031a_state *state;
+ struct v4l2_subdev *sd;
+ int i;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ state = kzalloc(sizeof(struct upd64031a_state), GFP_KERNEL);
+ if (state == NULL)
+ return -ENOMEM;
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &upd64031a_ops);
+ memcpy(state->regs, upd64031a_init, sizeof(state->regs));
+ state->gr_mode = UPD64031A_GR_ON << 6;
+ state->direct_3dycs_connect = UPD64031A_3DYCS_COMPOSITE << 4;
+ state->ext_comp_sync = state->ext_vert_sync = 0;
+ for (i = 0; i < TOT_REGS; i++)
+ upd64031a_write(sd, i, state->regs[i]);
+ return 0;
+}
+
+static int upd64031a_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct i2c_device_id upd64031a_id[] = {
+ { "upd64031a", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, upd64031a_id);
+
+static struct i2c_driver upd64031a_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "upd64031a",
+ },
+ .probe = upd64031a_probe,
+ .remove = upd64031a_remove,
+ .id_table = upd64031a_id,
+};
+
+module_i2c_driver(upd64031a_driver);
diff --git a/drivers/media/video/upd64083.c b/drivers/media/video/upd64083.c
new file mode 100644
index 00000000..75d6acc6
--- /dev/null
+++ b/drivers/media/video/upd64083.c
@@ -0,0 +1,246 @@
+/*
+ * upd6408x - NEC Electronics 3-Dimensional Y/C separation driver
+ *
+ * 2003 by T.Adachi (tadachi@tadachi-net.com)
+ * 2003 by Takeru KOMORIYA <komoriya@paken.org>
+ * 2006 by Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/upd64083.h>
+
+MODULE_DESCRIPTION("uPD64083 driver");
+MODULE_AUTHOR("T. Adachi, Takeru KOMORIYA, Hans Verkuil");
+MODULE_LICENSE("GPL");
+
+static bool debug;
+module_param(debug, bool, 0644);
+
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+
+enum {
+ R00 = 0, R01, R02, R03, R04,
+ R05, R06, R07, R08, R09,
+ R0A, R0B, R0C, R0D, R0E, R0F,
+ R10, R11, R12, R13, R14,
+ R15, R16,
+ TOT_REGS
+};
+
+struct upd64083_state {
+ struct v4l2_subdev sd;
+ u8 mode;
+ u8 ext_y_adc;
+ u8 regs[TOT_REGS];
+};
+
+static inline struct upd64083_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct upd64083_state, sd);
+}
+
+/* Initial values when used in combination with the
+ NEC upd64031a ghost reduction chip. */
+static u8 upd64083_init[] = {
+ 0x1f, 0x01, 0xa0, 0x2d, 0x29, /* we use EXCSS=0 */
+ 0x36, 0xdd, 0x05, 0x56, 0x48,
+ 0x00, 0x3a, 0xa0, 0x05, 0x08,
+ 0x44, 0x60, 0x08, 0x52, 0xf8,
+ 0x53, 0x60, 0x10
+};
+
+/* ------------------------------------------------------------------------ */
+
+static void upd64083_write(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 buf[2];
+
+ buf[0] = reg;
+ buf[1] = val;
+ v4l2_dbg(1, debug, sd, "write reg: %02x val: %02x\n", reg, val);
+ if (i2c_master_send(client, buf, 2) != 2)
+ v4l2_err(sd, "I/O error write 0x%02x/0x%02x\n", reg, val);
+}
+
+/* ------------------------------------------------------------------------ */
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static u8 upd64083_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 buf[7];
+
+ if (reg >= sizeof(buf))
+ return 0xff;
+ i2c_master_recv(client, buf, sizeof(buf));
+ return buf[reg];
+}
+#endif
+
+/* ------------------------------------------------------------------------ */
+
+static int upd64083_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct upd64083_state *state = to_state(sd);
+ u8 r00, r02;
+
+ if (input > 7 || (input & 6) == 6)
+ return -EINVAL;
+ state->mode = (input & 3) << 6;
+ state->ext_y_adc = (input & UPD64083_EXT_Y_ADC) << 3;
+ r00 = (state->regs[R00] & ~(3 << 6)) | state->mode;
+ r02 = (state->regs[R02] & ~(1 << 5)) | state->ext_y_adc;
+ upd64083_write(sd, R00, r00);
+ upd64083_write(sd, R02, r02);
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int upd64083_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = upd64083_read(sd, reg->reg & 0xff);
+ reg->size = 1;
+ return 0;
+}
+
+static int upd64083_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ upd64083_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ return 0;
+}
+#endif
+
+static int upd64083_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_UPD64083, 0);
+}
+
+static int upd64083_log_status(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 buf[7];
+
+ i2c_master_recv(client, buf, 7);
+ v4l2_info(sd, "Status: SA00=%02x SA01=%02x SA02=%02x SA03=%02x "
+ "SA04=%02x SA05=%02x SA06=%02x\n",
+ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops upd64083_core_ops = {
+ .log_status = upd64083_log_status,
+ .g_chip_ident = upd64083_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = upd64083_g_register,
+ .s_register = upd64083_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops upd64083_video_ops = {
+ .s_routing = upd64083_s_routing,
+};
+
+static const struct v4l2_subdev_ops upd64083_ops = {
+ .core = &upd64083_core_ops,
+ .video = &upd64083_video_ops,
+};
+
+/* ------------------------------------------------------------------------ */
+
+/* i2c implementation */
+
+static int upd64083_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct upd64083_state *state;
+ struct v4l2_subdev *sd;
+ int i;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ state = kzalloc(sizeof(struct upd64083_state), GFP_KERNEL);
+ if (state == NULL)
+ return -ENOMEM;
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &upd64083_ops);
+ /* Initially assume that a ghost reduction chip is present */
+ state->mode = 0; /* YCS mode */
+ state->ext_y_adc = (1 << 5);
+ memcpy(state->regs, upd64083_init, TOT_REGS);
+ for (i = 0; i < TOT_REGS; i++)
+ upd64083_write(sd, i, state->regs[i]);
+ return 0;
+}
+
+static int upd64083_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct i2c_device_id upd64083_id[] = {
+ { "upd64083", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, upd64083_id);
+
+static struct i2c_driver upd64083_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "upd64083",
+ },
+ .probe = upd64083_probe,
+ .remove = upd64083_remove,
+ .id_table = upd64083_id,
+};
+
+module_i2c_driver(upd64083_driver);
diff --git a/drivers/media/video/usbvision/Kconfig b/drivers/media/video/usbvision/Kconfig
new file mode 100644
index 00000000..fc24ef05
--- /dev/null
+++ b/drivers/media/video/usbvision/Kconfig
@@ -0,0 +1,12 @@
+config VIDEO_USBVISION
+ tristate "USB video devices based on Nogatech NT1003/1004/1005"
+ depends on I2C && VIDEO_V4L2
+ select VIDEO_TUNER
+ select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO
+ ---help---
+ There are more than 50 different USB video devices based on
+ NT1003/1004/1005 USB Bridges. This driver enables using those
+ devices.
+
+ To compile this driver as a module, choose M here: the
+ module will be called usbvision.
diff --git a/drivers/media/video/usbvision/Makefile b/drivers/media/video/usbvision/Makefile
new file mode 100644
index 00000000..aea1e3b5
--- /dev/null
+++ b/drivers/media/video/usbvision/Makefile
@@ -0,0 +1,6 @@
+usbvision-objs := usbvision-core.o usbvision-video.o usbvision-i2c.o usbvision-cards.o
+
+obj-$(CONFIG_VIDEO_USBVISION) += usbvision.o
+
+ccflags-y += -Idrivers/media/video
+ccflags-y += -Idrivers/media/common/tuners
diff --git a/drivers/media/video/usbvision/usbvision-cards.c b/drivers/media/video/usbvision/usbvision-cards.c
new file mode 100644
index 00000000..3103d0d0
--- /dev/null
+++ b/drivers/media/video/usbvision/usbvision-cards.c
@@ -0,0 +1,1133 @@
+/*
+ * usbvision-cards.c
+ * usbvision cards definition file
+ *
+ * Copyright (c) 1999-2005 Joerg Heckenbach <joerg@heckenbach-aw.de>
+ *
+ * This module is part of usbvision driver project.
+ * Updates to driver completed by Dwaine P. Garden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/list.h>
+#include <linux/module.h>
+#include <media/v4l2-dev.h>
+#include <media/tuner.h>
+#include "usbvision.h"
+#include "usbvision-cards.h"
+
+/* Supported Devices: A table for usbvision.c*/
+struct usbvision_device_data_st usbvision_device_data[] = {
+ [XANBOO] = {
+ .interface = -1,
+ .codec = CODEC_SAA7113,
+ .video_channels = 4,
+ .video_norm = V4L2_STD_NTSC,
+ .audio_channels = 1,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 0,
+ .tuner_type = 0,
+ .x_offset = -1,
+ .y_offset = -1,
+ .model_string = "Xanboo",
+ },
+ [BELKIN_VIDEOBUS_II] = {
+ .interface = -1,
+ .codec = CODEC_SAA7113,
+ .video_channels = 2,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 0,
+ .tuner_type = 0,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Belkin USB VideoBus II Adapter",
+ },
+ [BELKIN_VIDEOBUS] = {
+ .interface = -1,
+ .codec = CODEC_SAA7111,
+ .video_channels = 2,
+ .video_norm = V4L2_STD_NTSC,
+ .audio_channels = 1,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 0,
+ .tuner_type = 0,
+ .x_offset = -1,
+ .y_offset = -1,
+ .model_string = "Belkin Components USB VideoBus",
+ },
+ [BELKIN_USB_VIDEOBUS_II] = {
+ .interface = -1,
+ .codec = CODEC_SAA7113,
+ .video_channels = 2,
+ .video_norm = V4L2_STD_NTSC,
+ .audio_channels = 1,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 0,
+ .tuner_type = 0,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Belkin USB VideoBus II",
+ },
+ [ECHOFX_INTERVIEW_LITE] = {
+ .interface = 0,
+ .codec = CODEC_SAA7111,
+ .video_channels = 2,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 0,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 0,
+ .tuner_type = 0,
+ .x_offset = -1,
+ .y_offset = -1,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "echoFX InterView Lite",
+ },
+ [USBGEAR_USBG_V1] = {
+ .interface = -1,
+ .codec = CODEC_SAA7111,
+ .video_channels = 2,
+ .video_norm = V4L2_STD_NTSC,
+ .audio_channels = 1,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 0,
+ .tuner_type = 0,
+ .x_offset = -1,
+ .y_offset = -1,
+ .model_string = "USBGear USBG-V1 resp. HAMA USB",
+ },
+ [D_LINK_V100] = {
+ .interface = -1,
+ .codec = CODEC_SAA7113,
+ .video_channels = 4,
+ .video_norm = V4L2_STD_NTSC,
+ .audio_channels = 0,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 0,
+ .tuner_type = 0,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "D-Link V100",
+ },
+ [X10_USB_CAMERA] = {
+ .interface = -1,
+ .codec = CODEC_SAA7111,
+ .video_channels = 2,
+ .video_norm = V4L2_STD_NTSC,
+ .audio_channels = 1,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 0,
+ .tuner_type = 0,
+ .x_offset = -1,
+ .y_offset = -1,
+ .model_string = "X10 USB Camera",
+ },
+ [HPG_WINTV_LIVE_PAL_BG] = {
+ .interface = -1,
+ .codec = CODEC_SAA7111,
+ .video_channels = 2,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 0,
+ .tuner_type = 0,
+ .x_offset = -1,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Hauppauge WinTV USB Live (PAL B/G)",
+ },
+ [HPG_WINTV_LIVE_PRO_NTSC_MN] = {
+ .interface = -1,
+ .codec = CODEC_SAA7113,
+ .video_channels = 2,
+ .video_norm = V4L2_STD_NTSC,
+ .audio_channels = 0,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 0,
+ .tuner_type = 0,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Hauppauge WinTV USB Live Pro (NTSC M/N)",
+ },
+ [ZORAN_PMD_NOGATECH] = {
+ .interface = -1,
+ .codec = CODEC_SAA7113,
+ .video_channels = 2,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 2,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 0,
+ .tuner_type = 0,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Zoran Co. PMD (Nogatech) AV-grabber Manhattan",
+ },
+ [NOGATECH_USB_TV_NTSC_FM] = {
+ .interface = -1,
+ .codec = CODEC_SAA7111,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_NTSC,
+ .audio_channels = 1,
+ .radio = 1,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .x_offset = -1,
+ .y_offset = 20,
+ .model_string = "Nogatech USB-TV (NTSC) FM",
+ },
+ [PNY_USB_TV_NTSC_FM] = {
+ .interface = -1,
+ .codec = CODEC_SAA7111,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_NTSC,
+ .audio_channels = 1,
+ .radio = 1,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .x_offset = -1,
+ .y_offset = 20,
+ .model_string = "PNY USB-TV (NTSC) FM",
+ },
+ [PV_PLAYTV_USB_PRO_PAL_FM] = {
+ .interface = 0,
+ .codec = CODEC_SAA7113,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 1,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "PixelView PlayTv-USB PRO (PAL) FM",
+ },
+ [ZT_721] = {
+ .interface = 0,
+ .codec = CODEC_SAA7113,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 1,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "ZTV ZT-721 2.4GHz USB A/V Receiver",
+ },
+ [HPG_WINTV_NTSC_MN] = {
+ .interface = -1,
+ .codec = CODEC_SAA7111,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_NTSC,
+ .audio_channels = 1,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .x_offset = -1,
+ .y_offset = 20,
+ .model_string = "Hauppauge WinTV USB (NTSC M/N)",
+ },
+ [HPG_WINTV_PAL_BG] = {
+ .interface = -1,
+ .codec = CODEC_SAA7111,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .x_offset = -1,
+ .y_offset = -1,
+ .model_string = "Hauppauge WinTV USB (PAL B/G)",
+ },
+ [HPG_WINTV_PAL_I] = {
+ .interface = -1,
+ .codec = CODEC_SAA7111,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .x_offset = -1,
+ .y_offset = -1,
+ .model_string = "Hauppauge WinTV USB (PAL I)",
+ },
+ [HPG_WINTV_PAL_SECAM_L] = {
+ .interface = -1,
+ .codec = CODEC_SAA7111,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_SECAM,
+ .audio_channels = 1,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_SECAM,
+ .x_offset = 0x80,
+ .y_offset = 0x16,
+ .model_string = "Hauppauge WinTV USB (PAL/SECAM L)",
+ },
+ [HPG_WINTV_PAL_D_K] = {
+ .interface = -1,
+ .codec = CODEC_SAA7111,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .x_offset = -1,
+ .y_offset = -1,
+ .model_string = "Hauppauge WinTV USB (PAL D/K)",
+ },
+ [HPG_WINTV_NTSC_FM] = {
+ .interface = -1,
+ .codec = CODEC_SAA7111,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_NTSC,
+ .audio_channels = 1,
+ .radio = 1,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .x_offset = -1,
+ .y_offset = -1,
+ .model_string = "Hauppauge WinTV USB (NTSC FM)",
+ },
+ [HPG_WINTV_PAL_BG_FM] = {
+ .interface = -1,
+ .codec = CODEC_SAA7111,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 1,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .x_offset = -1,
+ .y_offset = -1,
+ .model_string = "Hauppauge WinTV USB (PAL B/G FM)",
+ },
+ [HPG_WINTV_PAL_I_FM] = {
+ .interface = -1,
+ .codec = CODEC_SAA7111,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 1,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .x_offset = -1,
+ .y_offset = -1,
+ .model_string = "Hauppauge WinTV USB (PAL I FM)",
+ },
+ [HPG_WINTV_PAL_D_K_FM] = {
+ .interface = -1,
+ .codec = CODEC_SAA7111,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 1,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .x_offset = -1,
+ .y_offset = -1,
+ .model_string = "Hauppauge WinTV USB (PAL D/K FM)",
+ },
+ [HPG_WINTV_PRO_NTSC_MN] = {
+ .interface = 0,
+ .codec = CODEC_SAA7113,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_NTSC,
+ .audio_channels = 1,
+ .radio = 1,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_MICROTUNE_4049FM5,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Hauppauge WinTV USB Pro (NTSC M/N)",
+ },
+ [HPG_WINTV_PRO_NTSC_MN_V2] = {
+ .interface = 0,
+ .codec = CODEC_SAA7113,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_NTSC,
+ .audio_channels = 1,
+ .radio = 1,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_MICROTUNE_4049FM5,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Hauppauge WinTV USB Pro (NTSC M/N) V2",
+ },
+ [HPG_WINTV_PRO_PAL] = {
+ .interface = 0,
+ .codec = CODEC_SAA7113,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L)",
+ },
+ [HPG_WINTV_PRO_NTSC_MN_V3] = {
+ .interface = 0,
+ .codec = CODEC_SAA7113,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_NTSC,
+ .audio_channels = 1,
+ .radio = 1,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Hauppauge WinTV USB Pro (NTSC M/N) V3",
+ },
+ [HPG_WINTV_PRO_PAL_BG] = {
+ .interface = 0,
+ .codec = CODEC_SAA7113,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Hauppauge WinTV USB Pro (PAL B/G)",
+ },
+ [HPG_WINTV_PRO_PAL_I] = {
+ .interface = 0,
+ .codec = CODEC_SAA7113,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Hauppauge WinTV USB Pro (PAL I)",
+ },
+ [HPG_WINTV_PRO_PAL_SECAM_L] = {
+ .interface = -1,
+ .codec = CODEC_SAA7113,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_SECAM,
+ .audio_channels = 1,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_SECAM,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Hauppauge WinTV USB Pro (PAL/SECAM L)",
+ },
+ [HPG_WINTV_PRO_PAL_D_K] = {
+ .interface = -1,
+ .codec = CODEC_SAA7113,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Hauppauge WinTV USB Pro (PAL D/K)",
+ },
+ [HPG_WINTV_PRO_PAL_SECAM] = {
+ .interface = -1,
+ .codec = CODEC_SAA7113,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_SECAM,
+ .audio_channels = 1,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_SECAM,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L)",
+ },
+ [HPG_WINTV_PRO_PAL_SECAM_V2] = {
+ .interface = -1,
+ .codec = CODEC_SAA7113,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_SECAM,
+ .audio_channels = 1,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_SECAM,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L) V2",
+ },
+ [HPG_WINTV_PRO_PAL_BG_V2] = {
+ .interface = -1,
+ .codec = CODEC_SAA7113,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_ALPS_TSBE1_PAL,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Hauppauge WinTV USB Pro (PAL B/G) V2",
+ },
+ [HPG_WINTV_PRO_PAL_BG_D_K] = {
+ .interface = -1,
+ .codec = CODEC_SAA7113,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_ALPS_TSBE1_PAL,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Hauppauge WinTV USB Pro (PAL B/G,D/K)",
+ },
+ [HPG_WINTV_PRO_PAL_I_D_K] = {
+ .interface = -1,
+ .codec = CODEC_SAA7113,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Hauppauge WinTV USB Pro (PAL I,D/K)",
+ },
+ [HPG_WINTV_PRO_NTSC_MN_FM] = {
+ .interface = -1,
+ .codec = CODEC_SAA7113,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_NTSC,
+ .audio_channels = 1,
+ .radio = 1,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Hauppauge WinTV USB Pro (NTSC M/N FM)",
+ },
+ [HPG_WINTV_PRO_PAL_BG_FM] = {
+ .interface = 0,
+ .codec = CODEC_SAA7113,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 1,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Hauppauge WinTV USB Pro (PAL B/G FM)",
+ },
+ [HPG_WINTV_PRO_PAL_I_FM] = {
+ .interface = 0,
+ .codec = CODEC_SAA7113,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 1,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Hauppauge WinTV USB Pro (PAL I FM)",
+ },
+ [HPG_WINTV_PRO_PAL_D_K_FM] = {
+ .interface = 0,
+ .codec = CODEC_SAA7113,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 1,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Hauppauge WinTV USB Pro (PAL D/K FM)",
+ },
+ [HPG_WINTV_PRO_TEMIC_PAL_FM] = {
+ .interface = 0,
+ .codec = CODEC_SAA7113,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 1,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_MICROTUNE_4049FM5,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Hauppauge WinTV USB Pro (Temic PAL/SECAM B/G/I/D/K/L FM)",
+ },
+ [HPG_WINTV_PRO_TEMIC_PAL_BG_FM] = {
+ .interface = 0,
+ .codec = CODEC_SAA7113,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 1,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_MICROTUNE_4049FM5,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Hauppauge WinTV USB Pro (Temic PAL B/G FM)",
+ },
+ [HPG_WINTV_PRO_PAL_FM] = {
+ .interface = 0,
+ .codec = CODEC_SAA7113,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 1,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L FM)",
+ },
+ [HPG_WINTV_PRO_NTSC_MN_FM_V2] = {
+ .interface = 0,
+ .codec = CODEC_SAA7113,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_NTSC,
+ .audio_channels = 1,
+ .radio = 1,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Hauppauge WinTV USB Pro (NTSC M/N FM) V2",
+ },
+ [CAMTEL_TVB330] = {
+ .interface = -1,
+ .codec = CODEC_SAA7113,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_NTSC,
+ .audio_channels = 1,
+ .radio = 1,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .x_offset = 5,
+ .y_offset = 5,
+ .model_string = "Camtel Technology USB TV Genie Pro FM Model TVB330",
+ },
+ [DIGITAL_VIDEO_CREATOR_I] = {
+ .interface = -1,
+ .codec = CODEC_SAA7113,
+ .video_channels = 2,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 0,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 0,
+ .tuner_type = 0,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Digital Video Creator I",
+ },
+ [GLOBAL_VILLAGE_GV_007_NTSC] = {
+ .interface = -1,
+ .codec = CODEC_SAA7111,
+ .video_channels = 2,
+ .video_norm = V4L2_STD_NTSC,
+ .audio_channels = 0,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 0,
+ .tuner_type = 0,
+ .x_offset = 82,
+ .y_offset = 20,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Global Village GV-007 (NTSC)",
+ },
+ [DAZZLE_DVC_50_REV_1_NTSC] = {
+ .interface = 0,
+ .codec = CODEC_SAA7113,
+ .video_channels = 2,
+ .video_norm = V4L2_STD_NTSC,
+ .audio_channels = 0,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 0,
+ .tuner_type = 0,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Dazzle Fusion Model DVC-50 Rev 1 (NTSC)",
+ },
+ [DAZZLE_DVC_80_REV_1_PAL] = {
+ .interface = 0,
+ .codec = CODEC_SAA7113,
+ .video_channels = 2,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 0,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 0,
+ .tuner_type = 0,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Dazzle Fusion Model DVC-80 Rev 1 (PAL)",
+ },
+ [DAZZLE_DVC_90_REV_1_SECAM] = {
+ .interface = 0,
+ .codec = CODEC_SAA7113,
+ .video_channels = 2,
+ .video_norm = V4L2_STD_SECAM,
+ .audio_channels = 0,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 0,
+ .tuner_type = 0,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)",
+ },
+ [ESKAPE_LABS_MYTV2GO] = {
+ .interface = 0,
+ .codec = CODEC_SAA7113,
+ .video_channels = 2,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 1,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Eskape Labs MyTV2Go",
+ },
+ [PINNA_PCTV_USB_PAL] = {
+ .interface = -1,
+ .codec = CODEC_SAA7111,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 0,
+ .vbi = 0,
+ .tuner = 1,
+ .tuner_type = TUNER_TEMIC_4066FY5_PAL_I,
+ .x_offset = -1,
+ .y_offset = -1,
+ .model_string = "Pinnacle Studio PCTV USB (PAL)",
+ },
+ [PINNA_PCTV_USB_SECAM] = {
+ .interface = -1,
+ .codec = CODEC_SAA7111,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_SECAM,
+ .audio_channels = 1,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_SECAM,
+ .x_offset = -1,
+ .y_offset = -1,
+ .model_string = "Pinnacle Studio PCTV USB (SECAM)",
+ },
+ [PINNA_PCTV_USB_PAL_FM] = {
+ .interface = -1,
+ .codec = CODEC_SAA7111,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 1,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .x_offset = 128,
+ .y_offset = 23,
+ .model_string = "Pinnacle Studio PCTV USB (PAL) FM",
+ },
+ [MIRO_PCTV_USB] = {
+ .interface = -1,
+ .codec = CODEC_SAA7111,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .x_offset = -1,
+ .y_offset = -1,
+ .model_string = "Miro PCTV USB",
+ },
+ [PINNA_PCTV_USB_NTSC_FM] = {
+ .interface = -1,
+ .codec = CODEC_SAA7111,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_NTSC,
+ .audio_channels = 1,
+ .radio = 1,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .x_offset = -1,
+ .y_offset = -1,
+ .model_string = "Pinnacle Studio PCTV USB (NTSC) FM",
+ },
+ [PINNA_PCTV_USB_NTSC_FM_V3] = {
+ .interface = -1,
+ .codec = CODEC_SAA7111,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_NTSC,
+ .audio_channels = 1,
+ .radio = 1,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .x_offset = -1,
+ .y_offset = -1,
+ .model_string = "Pinnacle Studio PCTV USB (NTSC) FM V3",
+ },
+ [PINNA_PCTV_USB_PAL_FM_V2] = {
+ .interface = -1,
+ .codec = CODEC_SAA7113,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 1,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_TEMIC_4009FR5_PAL,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Pinnacle Studio PCTV USB (PAL) FM V2",
+ },
+ [PINNA_PCTV_USB_NTSC_FM_V2] = {
+ .interface = -1,
+ .codec = CODEC_SAA7111,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_NTSC,
+ .audio_channels = 1,
+ .radio = 1,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_TEMIC_4039FR5_NTSC,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Pinnacle Studio PCTV USB (NTSC) FM V2",
+ },
+ [PINNA_PCTV_USB_PAL_FM_V3] = {
+ .interface = -1,
+ .codec = CODEC_SAA7113,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 1,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_TEMIC_4009FR5_PAL,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Pinnacle Studio PCTV USB (PAL) FM V3",
+ },
+ [PINNA_LINX_VD_IN_CAB_NTSC] = {
+ .interface = -1,
+ .codec = CODEC_SAA7113,
+ .video_channels = 2,
+ .video_norm = V4L2_STD_NTSC,
+ .audio_channels = 1,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 0,
+ .tuner_type = 0,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Pinnacle Studio Linx Video input cable (NTSC)",
+ },
+ [PINNA_LINX_VD_IN_CAB_PAL] = {
+ .interface = -1,
+ .codec = CODEC_SAA7113,
+ .video_channels = 2,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 0,
+ .tuner_type = 0,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Pinnacle Studio Linx Video input cable (PAL)",
+ },
+ [PINNA_PCTV_BUNGEE_PAL_FM] = {
+ .interface = -1,
+ .codec = CODEC_SAA7113,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 1,
+ .radio = 1,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_TEMIC_4009FR5_PAL,
+ .x_offset = 0,
+ .y_offset = 3,
+ .dvi_yuv_override = 1,
+ .dvi_yuv = 7,
+ .model_string = "Pinnacle PCTV Bungee USB (PAL) FM",
+ },
+ [HPG_WINTV] = {
+ .interface = -1,
+ .codec = CODEC_SAA7111,
+ .video_channels = 3,
+ .video_norm = V4L2_STD_NTSC,
+ .audio_channels = 1,
+ .radio = 0,
+ .vbi = 1,
+ .tuner = 1,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .x_offset = -1,
+ .y_offset = -1,
+ .model_string = "Hauppauge WinTv-USB",
+ },
+ [MICROCAM_NTSC] = {
+ .interface = -1,
+ .codec = CODEC_WEBCAM,
+ .video_channels = 1,
+ .video_norm = V4L2_STD_NTSC,
+ .audio_channels = 0,
+ .radio = 0,
+ .vbi = 0,
+ .tuner = 0,
+ .tuner_type = 0,
+ .x_offset = 71,
+ .y_offset = 15,
+ .model_string = "Nogatech USB MicroCam NTSC (NV3000N)",
+ },
+ [MICROCAM_PAL] = {
+ .interface = -1,
+ .codec = CODEC_WEBCAM,
+ .video_channels = 1,
+ .video_norm = V4L2_STD_PAL,
+ .audio_channels = 0,
+ .radio = 0,
+ .vbi = 0,
+ .tuner = 0,
+ .tuner_type = 0,
+ .x_offset = 71,
+ .y_offset = 18,
+ .model_string = "Nogatech USB MicroCam PAL (NV3001P)",
+ },
+};
+const int usbvision_device_data_size = ARRAY_SIZE(usbvision_device_data);
+
+/* Supported Devices */
+
+struct usb_device_id usbvision_table[] = {
+ { USB_DEVICE(0x0a6f, 0x0400), .driver_info = XANBOO },
+ { USB_DEVICE(0x050d, 0x0106), .driver_info = BELKIN_VIDEOBUS_II },
+ { USB_DEVICE(0x050d, 0x0207), .driver_info = BELKIN_VIDEOBUS },
+ { USB_DEVICE(0x050d, 0x0208), .driver_info = BELKIN_USB_VIDEOBUS_II },
+ { USB_DEVICE(0x0571, 0x0002), .driver_info = ECHOFX_INTERVIEW_LITE },
+ { USB_DEVICE(0x0573, 0x0003), .driver_info = USBGEAR_USBG_V1 },
+ { USB_DEVICE(0x0573, 0x0400), .driver_info = D_LINK_V100 },
+ { USB_DEVICE(0x0573, 0x2000), .driver_info = X10_USB_CAMERA },
+ { USB_DEVICE(0x0573, 0x2d00), .driver_info = HPG_WINTV_LIVE_PAL_BG },
+ { USB_DEVICE(0x0573, 0x2d01), .driver_info = HPG_WINTV_LIVE_PRO_NTSC_MN },
+ { USB_DEVICE(0x0573, 0x2101), .driver_info = ZORAN_PMD_NOGATECH },
+ { USB_DEVICE(0x0573, 0x3000), .driver_info = MICROCAM_NTSC },
+ { USB_DEVICE(0x0573, 0x3001), .driver_info = MICROCAM_PAL },
+ { USB_DEVICE(0x0573, 0x4100), .driver_info = NOGATECH_USB_TV_NTSC_FM },
+ { USB_DEVICE(0x0573, 0x4110), .driver_info = PNY_USB_TV_NTSC_FM },
+ { USB_DEVICE(0x0573, 0x4450), .driver_info = PV_PLAYTV_USB_PRO_PAL_FM },
+ { USB_DEVICE(0x0573, 0x4550), .driver_info = ZT_721 },
+ { USB_DEVICE(0x0573, 0x4d00), .driver_info = HPG_WINTV_NTSC_MN },
+ { USB_DEVICE(0x0573, 0x4d01), .driver_info = HPG_WINTV_PAL_BG },
+ { USB_DEVICE(0x0573, 0x4d02), .driver_info = HPG_WINTV_PAL_I },
+ { USB_DEVICE(0x0573, 0x4d03), .driver_info = HPG_WINTV_PAL_SECAM_L },
+ { USB_DEVICE(0x0573, 0x4d04), .driver_info = HPG_WINTV_PAL_D_K },
+ { USB_DEVICE(0x0573, 0x4d10), .driver_info = HPG_WINTV_NTSC_FM },
+ { USB_DEVICE(0x0573, 0x4d11), .driver_info = HPG_WINTV_PAL_BG_FM },
+ { USB_DEVICE(0x0573, 0x4d12), .driver_info = HPG_WINTV_PAL_I_FM },
+ { USB_DEVICE(0x0573, 0x4d14), .driver_info = HPG_WINTV_PAL_D_K_FM },
+ { USB_DEVICE(0x0573, 0x4d2a), .driver_info = HPG_WINTV_PRO_NTSC_MN },
+ { USB_DEVICE(0x0573, 0x4d2b), .driver_info = HPG_WINTV_PRO_NTSC_MN_V2 },
+ { USB_DEVICE(0x0573, 0x4d2c), .driver_info = HPG_WINTV_PRO_PAL },
+ { USB_DEVICE(0x0573, 0x4d20), .driver_info = HPG_WINTV_PRO_NTSC_MN_V3 },
+ { USB_DEVICE(0x0573, 0x4d21), .driver_info = HPG_WINTV_PRO_PAL_BG },
+ { USB_DEVICE(0x0573, 0x4d22), .driver_info = HPG_WINTV_PRO_PAL_I },
+ { USB_DEVICE(0x0573, 0x4d23), .driver_info = HPG_WINTV_PRO_PAL_SECAM_L },
+ { USB_DEVICE(0x0573, 0x4d24), .driver_info = HPG_WINTV_PRO_PAL_D_K },
+ { USB_DEVICE(0x0573, 0x4d25), .driver_info = HPG_WINTV_PRO_PAL_SECAM },
+ { USB_DEVICE(0x0573, 0x4d26), .driver_info = HPG_WINTV_PRO_PAL_SECAM_V2 },
+ { USB_DEVICE(0x0573, 0x4d27), .driver_info = HPG_WINTV_PRO_PAL_BG_V2 },
+ { USB_DEVICE(0x0573, 0x4d28), .driver_info = HPG_WINTV_PRO_PAL_BG_D_K },
+ { USB_DEVICE(0x0573, 0x4d29), .driver_info = HPG_WINTV_PRO_PAL_I_D_K },
+ { USB_DEVICE(0x0573, 0x4d30), .driver_info = HPG_WINTV_PRO_NTSC_MN_FM },
+ { USB_DEVICE(0x0573, 0x4d31), .driver_info = HPG_WINTV_PRO_PAL_BG_FM },
+ { USB_DEVICE(0x0573, 0x4d32), .driver_info = HPG_WINTV_PRO_PAL_I_FM },
+ { USB_DEVICE(0x0573, 0x4d34), .driver_info = HPG_WINTV_PRO_PAL_D_K_FM },
+ { USB_DEVICE(0x0573, 0x4d35), .driver_info = HPG_WINTV_PRO_TEMIC_PAL_FM },
+ { USB_DEVICE(0x0573, 0x4d36), .driver_info = HPG_WINTV_PRO_TEMIC_PAL_BG_FM },
+ { USB_DEVICE(0x0573, 0x4d37), .driver_info = HPG_WINTV_PRO_PAL_FM },
+ { USB_DEVICE(0x0573, 0x4d38), .driver_info = HPG_WINTV_PRO_NTSC_MN_FM_V2 },
+ { USB_DEVICE(0x0768, 0x0006), .driver_info = CAMTEL_TVB330 },
+ { USB_DEVICE(0x07d0, 0x0001), .driver_info = DIGITAL_VIDEO_CREATOR_I },
+ { USB_DEVICE(0x07d0, 0x0002), .driver_info = GLOBAL_VILLAGE_GV_007_NTSC },
+ { USB_DEVICE(0x07d0, 0x0003), .driver_info = DAZZLE_DVC_50_REV_1_NTSC },
+ { USB_DEVICE(0x07d0, 0x0004), .driver_info = DAZZLE_DVC_80_REV_1_PAL },
+ { USB_DEVICE(0x07d0, 0x0005), .driver_info = DAZZLE_DVC_90_REV_1_SECAM },
+ { USB_DEVICE(0x07f8, 0x9104), .driver_info = ESKAPE_LABS_MYTV2GO },
+ { USB_DEVICE(0x2304, 0x010d), .driver_info = PINNA_PCTV_USB_PAL },
+ { USB_DEVICE(0x2304, 0x0109), .driver_info = PINNA_PCTV_USB_SECAM },
+ { USB_DEVICE(0x2304, 0x0110), .driver_info = PINNA_PCTV_USB_PAL_FM },
+ { USB_DEVICE(0x2304, 0x0111), .driver_info = MIRO_PCTV_USB },
+ { USB_DEVICE(0x2304, 0x0112), .driver_info = PINNA_PCTV_USB_NTSC_FM },
+ { USB_DEVICE(0x2304, 0x0113), .driver_info = PINNA_PCTV_USB_NTSC_FM_V3 },
+ { USB_DEVICE(0x2304, 0x0210), .driver_info = PINNA_PCTV_USB_PAL_FM_V2 },
+ { USB_DEVICE(0x2304, 0x0212), .driver_info = PINNA_PCTV_USB_NTSC_FM_V2 },
+ { USB_DEVICE(0x2304, 0x0214), .driver_info = PINNA_PCTV_USB_PAL_FM_V3 },
+ { USB_DEVICE(0x2304, 0x0300), .driver_info = PINNA_LINX_VD_IN_CAB_NTSC },
+ { USB_DEVICE(0x2304, 0x0301), .driver_info = PINNA_LINX_VD_IN_CAB_PAL },
+ { USB_DEVICE(0x2304, 0x0419), .driver_info = PINNA_PCTV_BUNGEE_PAL_FM },
+ { USB_DEVICE(0x2400, 0x4200), .driver_info = HPG_WINTV },
+ { }, /* terminate list */
+};
+
+MODULE_DEVICE_TABLE(usb, usbvision_table);
diff --git a/drivers/media/video/usbvision/usbvision-cards.h b/drivers/media/video/usbvision/usbvision-cards.h
new file mode 100644
index 00000000..a51cc118
--- /dev/null
+++ b/drivers/media/video/usbvision/usbvision-cards.h
@@ -0,0 +1,69 @@
+#define XANBOO 0
+#define BELKIN_VIDEOBUS_II 1
+#define BELKIN_VIDEOBUS 2
+#define BELKIN_USB_VIDEOBUS_II 3
+#define ECHOFX_INTERVIEW_LITE 4
+#define USBGEAR_USBG_V1 5
+#define D_LINK_V100 6
+#define X10_USB_CAMERA 7
+#define HPG_WINTV_LIVE_PAL_BG 8
+#define HPG_WINTV_LIVE_PRO_NTSC_MN 9
+#define ZORAN_PMD_NOGATECH 10
+#define NOGATECH_USB_TV_NTSC_FM 11
+#define PNY_USB_TV_NTSC_FM 12
+#define PV_PLAYTV_USB_PRO_PAL_FM 13
+#define ZT_721 14
+#define HPG_WINTV_NTSC_MN 15
+#define HPG_WINTV_PAL_BG 16
+#define HPG_WINTV_PAL_I 17
+#define HPG_WINTV_PAL_SECAM_L 18
+#define HPG_WINTV_PAL_D_K 19
+#define HPG_WINTV_NTSC_FM 20
+#define HPG_WINTV_PAL_BG_FM 21
+#define HPG_WINTV_PAL_I_FM 22
+#define HPG_WINTV_PAL_D_K_FM 23
+#define HPG_WINTV_PRO_NTSC_MN 24
+#define HPG_WINTV_PRO_NTSC_MN_V2 25
+#define HPG_WINTV_PRO_PAL 26
+#define HPG_WINTV_PRO_NTSC_MN_V3 27
+#define HPG_WINTV_PRO_PAL_BG 28
+#define HPG_WINTV_PRO_PAL_I 29
+#define HPG_WINTV_PRO_PAL_SECAM_L 30
+#define HPG_WINTV_PRO_PAL_D_K 31
+#define HPG_WINTV_PRO_PAL_SECAM 32
+#define HPG_WINTV_PRO_PAL_SECAM_V2 33
+#define HPG_WINTV_PRO_PAL_BG_V2 34
+#define HPG_WINTV_PRO_PAL_BG_D_K 35
+#define HPG_WINTV_PRO_PAL_I_D_K 36
+#define HPG_WINTV_PRO_NTSC_MN_FM 37
+#define HPG_WINTV_PRO_PAL_BG_FM 38
+#define HPG_WINTV_PRO_PAL_I_FM 39
+#define HPG_WINTV_PRO_PAL_D_K_FM 40
+#define HPG_WINTV_PRO_TEMIC_PAL_FM 41
+#define HPG_WINTV_PRO_TEMIC_PAL_BG_FM 42
+#define HPG_WINTV_PRO_PAL_FM 43
+#define HPG_WINTV_PRO_NTSC_MN_FM_V2 44
+#define CAMTEL_TVB330 45
+#define DIGITAL_VIDEO_CREATOR_I 46
+#define GLOBAL_VILLAGE_GV_007_NTSC 47
+#define DAZZLE_DVC_50_REV_1_NTSC 48
+#define DAZZLE_DVC_80_REV_1_PAL 49
+#define DAZZLE_DVC_90_REV_1_SECAM 50
+#define ESKAPE_LABS_MYTV2GO 51
+#define PINNA_PCTV_USB_PAL 52
+#define PINNA_PCTV_USB_SECAM 53
+#define PINNA_PCTV_USB_PAL_FM 54
+#define MIRO_PCTV_USB 55
+#define PINNA_PCTV_USB_NTSC_FM 56
+#define PINNA_PCTV_USB_PAL_FM_V2 57
+#define PINNA_PCTV_USB_NTSC_FM_V2 58
+#define PINNA_PCTV_USB_PAL_FM_V3 59
+#define PINNA_LINX_VD_IN_CAB_NTSC 60
+#define PINNA_LINX_VD_IN_CAB_PAL 61
+#define PINNA_PCTV_BUNGEE_PAL_FM 62
+#define HPG_WINTV 63
+#define PINNA_PCTV_USB_NTSC_FM_V3 64
+#define MICROCAM_NTSC 65
+#define MICROCAM_PAL 66
+
+extern const int usbvision_device_data_size;
diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c
new file mode 100644
index 00000000..f344411a
--- /dev/null
+++ b/drivers/media/video/usbvision/usbvision-core.c
@@ -0,0 +1,2520 @@
+/*
+ * usbvision-core.c - driver for NT100x USB video capture devices
+ *
+ *
+ * Copyright (c) 1999-2005 Joerg Heckenbach <joerg@heckenbach-aw.de>
+ * Dwaine Garden <dwainegarden@rogers.com>
+ *
+ * This module is part of usbvision driver project.
+ * Updates to driver completed by Dwaine P. Garden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/timer.h>
+#include <linux/gfp.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+
+#include <media/saa7115.h>
+#include <media/v4l2-common.h>
+#include <media/tuner.h>
+
+#include <linux/workqueue.h>
+
+#include "usbvision.h"
+
+static unsigned int core_debug;
+module_param(core_debug, int, 0644);
+MODULE_PARM_DESC(core_debug, "enable debug messages [core]");
+
+static int adjust_compression = 1; /* Set the compression to be adaptive */
+module_param(adjust_compression, int, 0444);
+MODULE_PARM_DESC(adjust_compression, " Set the ADPCM compression for the device. Default: 1 (On)");
+
+/* To help people with Black and White output with using s-video input.
+ * Some cables and input device are wired differently. */
+static int switch_svideo_input;
+module_param(switch_svideo_input, int, 0444);
+MODULE_PARM_DESC(switch_svideo_input, " Set the S-Video input. Some cables and input device are wired differently. Default: 0 (Off)");
+
+static unsigned int adjust_x_offset = -1;
+module_param(adjust_x_offset, int, 0644);
+MODULE_PARM_DESC(adjust_x_offset, "adjust X offset display [core]");
+
+static unsigned int adjust_y_offset = -1;
+module_param(adjust_y_offset, int, 0644);
+MODULE_PARM_DESC(adjust_y_offset, "adjust Y offset display [core]");
+
+
+#define ENABLE_HEXDUMP 0 /* Enable if you need it */
+
+
+#ifdef USBVISION_DEBUG
+ #define PDEBUG(level, fmt, args...) { \
+ if (core_debug & (level)) \
+ printk(KERN_INFO KBUILD_MODNAME ":[%s:%d] " fmt, \
+ __func__, __LINE__ , ## args); \
+ }
+#else
+ #define PDEBUG(level, fmt, args...) do {} while (0)
+#endif
+
+#define DBG_HEADER (1 << 0)
+#define DBG_IRQ (1 << 1)
+#define DBG_ISOC (1 << 2)
+#define DBG_PARSE (1 << 3)
+#define DBG_SCRATCH (1 << 4)
+#define DBG_FUNC (1 << 5)
+
+static const int max_imgwidth = MAX_FRAME_WIDTH;
+static const int max_imgheight = MAX_FRAME_HEIGHT;
+static const int min_imgwidth = MIN_FRAME_WIDTH;
+static const int min_imgheight = MIN_FRAME_HEIGHT;
+
+/* The value of 'scratch_buf_size' affects quality of the picture
+ * in many ways. Shorter buffers may cause loss of data when client
+ * is too slow. Larger buffers are memory-consuming and take longer
+ * to work with. This setting can be adjusted, but the default value
+ * should be OK for most desktop users.
+ */
+#define DEFAULT_SCRATCH_BUF_SIZE (0x20000) /* 128kB memory scratch buffer */
+static const int scratch_buf_size = DEFAULT_SCRATCH_BUF_SIZE;
+
+/* Function prototypes */
+static int usbvision_request_intra(struct usb_usbvision *usbvision);
+static int usbvision_unrequest_intra(struct usb_usbvision *usbvision);
+static int usbvision_adjust_compression(struct usb_usbvision *usbvision);
+static int usbvision_measure_bandwidth(struct usb_usbvision *usbvision);
+
+/*******************************/
+/* Memory management functions */
+/*******************************/
+
+/*
+ * Here we want the physical address of the memory.
+ * This is used when initializing the contents of the area.
+ */
+
+static void *usbvision_rvmalloc(unsigned long size)
+{
+ void *mem;
+ unsigned long adr;
+
+ size = PAGE_ALIGN(size);
+ mem = vmalloc_32(size);
+ if (!mem)
+ return NULL;
+
+ memset(mem, 0, size); /* Clear the ram out, no junk to the user */
+ adr = (unsigned long) mem;
+ while (size > 0) {
+ SetPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ return mem;
+}
+
+static void usbvision_rvfree(void *mem, unsigned long size)
+{
+ unsigned long adr;
+
+ if (!mem)
+ return;
+
+ size = PAGE_ALIGN(size);
+
+ adr = (unsigned long) mem;
+ while ((long) size > 0) {
+ ClearPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ vfree(mem);
+}
+
+
+#if ENABLE_HEXDUMP
+static void usbvision_hexdump(const unsigned char *data, int len)
+{
+ char tmp[80];
+ int i, k;
+
+ for (i = k = 0; len > 0; i++, len--) {
+ if (i > 0 && (i % 16 == 0)) {
+ printk("%s\n", tmp);
+ k = 0;
+ }
+ k += sprintf(&tmp[k], "%02x ", data[i]);
+ }
+ if (k > 0)
+ printk(KERN_CONT "%s\n", tmp);
+}
+#endif
+
+/********************************
+ * scratch ring buffer handling
+ ********************************/
+static int scratch_len(struct usb_usbvision *usbvision) /* This returns the amount of data actually in the buffer */
+{
+ int len = usbvision->scratch_write_ptr - usbvision->scratch_read_ptr;
+
+ if (len < 0)
+ len += scratch_buf_size;
+ PDEBUG(DBG_SCRATCH, "scratch_len() = %d\n", len);
+
+ return len;
+}
+
+
+/* This returns the free space left in the buffer */
+static int scratch_free(struct usb_usbvision *usbvision)
+{
+ int free = usbvision->scratch_read_ptr - usbvision->scratch_write_ptr;
+ if (free <= 0)
+ free += scratch_buf_size;
+ if (free) {
+ free -= 1; /* at least one byte in the buffer must */
+ /* left blank, otherwise there is no chance to differ between full and empty */
+ }
+ PDEBUG(DBG_SCRATCH, "return %d\n", free);
+
+ return free;
+}
+
+
+/* This puts data into the buffer */
+static int scratch_put(struct usb_usbvision *usbvision, unsigned char *data,
+ int len)
+{
+ int len_part;
+
+ if (usbvision->scratch_write_ptr + len < scratch_buf_size) {
+ memcpy(usbvision->scratch + usbvision->scratch_write_ptr, data, len);
+ usbvision->scratch_write_ptr += len;
+ } else {
+ len_part = scratch_buf_size - usbvision->scratch_write_ptr;
+ memcpy(usbvision->scratch + usbvision->scratch_write_ptr, data, len_part);
+ if (len == len_part) {
+ usbvision->scratch_write_ptr = 0; /* just set write_ptr to zero */
+ } else {
+ memcpy(usbvision->scratch, data + len_part, len - len_part);
+ usbvision->scratch_write_ptr = len - len_part;
+ }
+ }
+
+ PDEBUG(DBG_SCRATCH, "len=%d, new write_ptr=%d\n", len, usbvision->scratch_write_ptr);
+
+ return len;
+}
+
+/* This marks the write_ptr as position of new frame header */
+static void scratch_mark_header(struct usb_usbvision *usbvision)
+{
+ PDEBUG(DBG_SCRATCH, "header at write_ptr=%d\n", usbvision->scratch_headermarker_write_ptr);
+
+ usbvision->scratch_headermarker[usbvision->scratch_headermarker_write_ptr] =
+ usbvision->scratch_write_ptr;
+ usbvision->scratch_headermarker_write_ptr += 1;
+ usbvision->scratch_headermarker_write_ptr %= USBVISION_NUM_HEADERMARKER;
+}
+
+/* This gets data from the buffer at the given "ptr" position */
+static int scratch_get_extra(struct usb_usbvision *usbvision,
+ unsigned char *data, int *ptr, int len)
+{
+ int len_part;
+
+ if (*ptr + len < scratch_buf_size) {
+ memcpy(data, usbvision->scratch + *ptr, len);
+ *ptr += len;
+ } else {
+ len_part = scratch_buf_size - *ptr;
+ memcpy(data, usbvision->scratch + *ptr, len_part);
+ if (len == len_part) {
+ *ptr = 0; /* just set the y_ptr to zero */
+ } else {
+ memcpy(data + len_part, usbvision->scratch, len - len_part);
+ *ptr = len - len_part;
+ }
+ }
+
+ PDEBUG(DBG_SCRATCH, "len=%d, new ptr=%d\n", len, *ptr);
+
+ return len;
+}
+
+
+/* This sets the scratch extra read pointer */
+static void scratch_set_extra_ptr(struct usb_usbvision *usbvision, int *ptr,
+ int len)
+{
+ *ptr = (usbvision->scratch_read_ptr + len) % scratch_buf_size;
+
+ PDEBUG(DBG_SCRATCH, "ptr=%d\n", *ptr);
+}
+
+
+/* This increments the scratch extra read pointer */
+static void scratch_inc_extra_ptr(int *ptr, int len)
+{
+ *ptr = (*ptr + len) % scratch_buf_size;
+
+ PDEBUG(DBG_SCRATCH, "ptr=%d\n", *ptr);
+}
+
+
+/* This gets data from the buffer */
+static int scratch_get(struct usb_usbvision *usbvision, unsigned char *data,
+ int len)
+{
+ int len_part;
+
+ if (usbvision->scratch_read_ptr + len < scratch_buf_size) {
+ memcpy(data, usbvision->scratch + usbvision->scratch_read_ptr, len);
+ usbvision->scratch_read_ptr += len;
+ } else {
+ len_part = scratch_buf_size - usbvision->scratch_read_ptr;
+ memcpy(data, usbvision->scratch + usbvision->scratch_read_ptr, len_part);
+ if (len == len_part) {
+ usbvision->scratch_read_ptr = 0; /* just set the read_ptr to zero */
+ } else {
+ memcpy(data + len_part, usbvision->scratch, len - len_part);
+ usbvision->scratch_read_ptr = len - len_part;
+ }
+ }
+
+ PDEBUG(DBG_SCRATCH, "len=%d, new read_ptr=%d\n", len, usbvision->scratch_read_ptr);
+
+ return len;
+}
+
+
+/* This sets read pointer to next header and returns it */
+static int scratch_get_header(struct usb_usbvision *usbvision,
+ struct usbvision_frame_header *header)
+{
+ int err_code = 0;
+
+ PDEBUG(DBG_SCRATCH, "from read_ptr=%d", usbvision->scratch_headermarker_read_ptr);
+
+ while (usbvision->scratch_headermarker_write_ptr -
+ usbvision->scratch_headermarker_read_ptr != 0) {
+ usbvision->scratch_read_ptr =
+ usbvision->scratch_headermarker[usbvision->scratch_headermarker_read_ptr];
+ usbvision->scratch_headermarker_read_ptr += 1;
+ usbvision->scratch_headermarker_read_ptr %= USBVISION_NUM_HEADERMARKER;
+ scratch_get(usbvision, (unsigned char *)header, USBVISION_HEADER_LENGTH);
+ if ((header->magic_1 == USBVISION_MAGIC_1)
+ && (header->magic_2 == USBVISION_MAGIC_2)
+ && (header->header_length == USBVISION_HEADER_LENGTH)) {
+ err_code = USBVISION_HEADER_LENGTH;
+ header->frame_width = header->frame_width_lo + (header->frame_width_hi << 8);
+ header->frame_height = header->frame_height_lo + (header->frame_height_hi << 8);
+ break;
+ }
+ }
+
+ return err_code;
+}
+
+
+/* This removes len bytes of old data from the buffer */
+static void scratch_rm_old(struct usb_usbvision *usbvision, int len)
+{
+ usbvision->scratch_read_ptr += len;
+ usbvision->scratch_read_ptr %= scratch_buf_size;
+ PDEBUG(DBG_SCRATCH, "read_ptr is now %d\n", usbvision->scratch_read_ptr);
+}
+
+
+/* This resets the buffer - kills all data in it too */
+static void scratch_reset(struct usb_usbvision *usbvision)
+{
+ PDEBUG(DBG_SCRATCH, "\n");
+
+ usbvision->scratch_read_ptr = 0;
+ usbvision->scratch_write_ptr = 0;
+ usbvision->scratch_headermarker_read_ptr = 0;
+ usbvision->scratch_headermarker_write_ptr = 0;
+ usbvision->isocstate = isoc_state_no_frame;
+}
+
+int usbvision_scratch_alloc(struct usb_usbvision *usbvision)
+{
+ usbvision->scratch = vmalloc_32(scratch_buf_size);
+ scratch_reset(usbvision);
+ if (usbvision->scratch == NULL) {
+ dev_err(&usbvision->dev->dev,
+ "%s: unable to allocate %d bytes for scratch\n",
+ __func__, scratch_buf_size);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+void usbvision_scratch_free(struct usb_usbvision *usbvision)
+{
+ vfree(usbvision->scratch);
+ usbvision->scratch = NULL;
+}
+
+/*
+ * usbvision_decompress_alloc()
+ *
+ * allocates intermediate buffer for decompression
+ */
+int usbvision_decompress_alloc(struct usb_usbvision *usbvision)
+{
+ int IFB_size = MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT * 3 / 2;
+
+ usbvision->intra_frame_buffer = vmalloc_32(IFB_size);
+ if (usbvision->intra_frame_buffer == NULL) {
+ dev_err(&usbvision->dev->dev,
+ "%s: unable to allocate %d for compr. frame buffer\n",
+ __func__, IFB_size);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+/*
+ * usbvision_decompress_free()
+ *
+ * frees intermediate buffer for decompression
+ */
+void usbvision_decompress_free(struct usb_usbvision *usbvision)
+{
+ vfree(usbvision->intra_frame_buffer);
+ usbvision->intra_frame_buffer = NULL;
+
+}
+
+/************************************************************
+ * Here comes the data parsing stuff that is run as interrupt
+ ************************************************************/
+/*
+ * usbvision_find_header()
+ *
+ * Locate one of supported header markers in the scratch buffer.
+ */
+static enum parse_state usbvision_find_header(struct usb_usbvision *usbvision)
+{
+ struct usbvision_frame *frame;
+ int found_header = 0;
+
+ frame = usbvision->cur_frame;
+
+ while (scratch_get_header(usbvision, &frame->isoc_header) == USBVISION_HEADER_LENGTH) {
+ /* found header in scratch */
+ PDEBUG(DBG_HEADER, "found header: 0x%02x%02x %d %d %d %d %#x 0x%02x %u %u",
+ frame->isoc_header.magic_2,
+ frame->isoc_header.magic_1,
+ frame->isoc_header.header_length,
+ frame->isoc_header.frame_num,
+ frame->isoc_header.frame_phase,
+ frame->isoc_header.frame_latency,
+ frame->isoc_header.data_format,
+ frame->isoc_header.format_param,
+ frame->isoc_header.frame_width,
+ frame->isoc_header.frame_height);
+
+ if (usbvision->request_intra) {
+ if (frame->isoc_header.format_param & 0x80) {
+ found_header = 1;
+ usbvision->last_isoc_frame_num = -1; /* do not check for lost frames this time */
+ usbvision_unrequest_intra(usbvision);
+ break;
+ }
+ } else {
+ found_header = 1;
+ break;
+ }
+ }
+
+ if (found_header) {
+ frame->frmwidth = frame->isoc_header.frame_width * usbvision->stretch_width;
+ frame->frmheight = frame->isoc_header.frame_height * usbvision->stretch_height;
+ frame->v4l2_linesize = (frame->frmwidth * frame->v4l2_format.depth) >> 3;
+ } else { /* no header found */
+ PDEBUG(DBG_HEADER, "skipping scratch data, no header");
+ scratch_reset(usbvision);
+ return parse_state_end_parse;
+ }
+
+ /* found header */
+ if (frame->isoc_header.data_format == ISOC_MODE_COMPRESS) {
+ /* check isoc_header.frame_num for lost frames */
+ if (usbvision->last_isoc_frame_num >= 0) {
+ if (((usbvision->last_isoc_frame_num + 1) % 32) != frame->isoc_header.frame_num) {
+ /* unexpected frame drop: need to request new intra frame */
+ PDEBUG(DBG_HEADER, "Lost frame before %d on USB", frame->isoc_header.frame_num);
+ usbvision_request_intra(usbvision);
+ return parse_state_next_frame;
+ }
+ }
+ usbvision->last_isoc_frame_num = frame->isoc_header.frame_num;
+ }
+ usbvision->header_count++;
+ frame->scanstate = scan_state_lines;
+ frame->curline = 0;
+
+ return parse_state_continue;
+}
+
+static enum parse_state usbvision_parse_lines_422(struct usb_usbvision *usbvision,
+ long *pcopylen)
+{
+ volatile struct usbvision_frame *frame;
+ unsigned char *f;
+ int len;
+ int i;
+ unsigned char yuyv[4] = { 180, 128, 10, 128 }; /* YUV components */
+ unsigned char rv, gv, bv; /* RGB components */
+ int clipmask_index, bytes_per_pixel;
+ int stretch_bytes, clipmask_add;
+
+ frame = usbvision->cur_frame;
+ f = frame->data + (frame->v4l2_linesize * frame->curline);
+
+ /* Make sure there's enough data for the entire line */
+ len = (frame->isoc_header.frame_width * 2) + 5;
+ if (scratch_len(usbvision) < len) {
+ PDEBUG(DBG_PARSE, "out of data in line %d, need %u.\n", frame->curline, len);
+ return parse_state_out;
+ }
+
+ if ((frame->curline + 1) >= frame->frmheight)
+ return parse_state_next_frame;
+
+ bytes_per_pixel = frame->v4l2_format.bytes_per_pixel;
+ stretch_bytes = (usbvision->stretch_width - 1) * bytes_per_pixel;
+ clipmask_index = frame->curline * MAX_FRAME_WIDTH;
+ clipmask_add = usbvision->stretch_width;
+
+ for (i = 0; i < frame->frmwidth; i += (2 * usbvision->stretch_width)) {
+ scratch_get(usbvision, &yuyv[0], 4);
+
+ if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
+ *f++ = yuyv[0]; /* Y */
+ *f++ = yuyv[3]; /* U */
+ } else {
+ YUV_TO_RGB_BY_THE_BOOK(yuyv[0], yuyv[1], yuyv[3], rv, gv, bv);
+ switch (frame->v4l2_format.format) {
+ case V4L2_PIX_FMT_RGB565:
+ *f++ = (0x1F & rv) |
+ (0xE0 & (gv << 5));
+ *f++ = (0x07 & (gv >> 3)) |
+ (0xF8 & bv);
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ *f++ = rv;
+ *f++ = gv;
+ *f++ = bv;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ *f++ = rv;
+ *f++ = gv;
+ *f++ = bv;
+ f++;
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ *f++ = (0x1F & rv) |
+ (0xE0 & (gv << 5));
+ *f++ = (0x03 & (gv >> 3)) |
+ (0x7C & (bv << 2));
+ break;
+ }
+ }
+ clipmask_index += clipmask_add;
+ f += stretch_bytes;
+
+ if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
+ *f++ = yuyv[2]; /* Y */
+ *f++ = yuyv[1]; /* V */
+ } else {
+ YUV_TO_RGB_BY_THE_BOOK(yuyv[2], yuyv[1], yuyv[3], rv, gv, bv);
+ switch (frame->v4l2_format.format) {
+ case V4L2_PIX_FMT_RGB565:
+ *f++ = (0x1F & rv) |
+ (0xE0 & (gv << 5));
+ *f++ = (0x07 & (gv >> 3)) |
+ (0xF8 & bv);
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ *f++ = rv;
+ *f++ = gv;
+ *f++ = bv;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ *f++ = rv;
+ *f++ = gv;
+ *f++ = bv;
+ f++;
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ *f++ = (0x1F & rv) |
+ (0xE0 & (gv << 5));
+ *f++ = (0x03 & (gv >> 3)) |
+ (0x7C & (bv << 2));
+ break;
+ }
+ }
+ clipmask_index += clipmask_add;
+ f += stretch_bytes;
+ }
+
+ frame->curline += usbvision->stretch_height;
+ *pcopylen += frame->v4l2_linesize * usbvision->stretch_height;
+
+ if (frame->curline >= frame->frmheight)
+ return parse_state_next_frame;
+ return parse_state_continue;
+}
+
+/* The decompression routine */
+static int usbvision_decompress(struct usb_usbvision *usbvision, unsigned char *compressed,
+ unsigned char *decompressed, int *start_pos,
+ int *block_typestart_pos, int len)
+{
+ int rest_pixel, idx, max_pos, pos, extra_pos, block_len, block_type_pos, block_type_len;
+ unsigned char block_byte, block_code, block_type, block_type_byte, integrator;
+
+ integrator = 0;
+ pos = *start_pos;
+ block_type_pos = *block_typestart_pos;
+ max_pos = 396; /* pos + len; */
+ extra_pos = pos;
+ block_len = 0;
+ block_byte = 0;
+ block_code = 0;
+ block_type = 0;
+ block_type_byte = 0;
+ block_type_len = 0;
+ rest_pixel = len;
+
+ for (idx = 0; idx < len; idx++) {
+ if (block_len == 0) {
+ if (block_type_len == 0) {
+ block_type_byte = compressed[block_type_pos];
+ block_type_pos++;
+ block_type_len = 4;
+ }
+ block_type = (block_type_byte & 0xC0) >> 6;
+
+ /* statistic: */
+ usbvision->compr_block_types[block_type]++;
+
+ pos = extra_pos;
+ if (block_type == 0) {
+ if (rest_pixel >= 24) {
+ idx += 23;
+ rest_pixel -= 24;
+ integrator = decompressed[idx];
+ } else {
+ idx += rest_pixel - 1;
+ rest_pixel = 0;
+ }
+ } else {
+ block_code = compressed[pos];
+ pos++;
+ if (rest_pixel >= 24)
+ block_len = 24;
+ else
+ block_len = rest_pixel;
+ rest_pixel -= block_len;
+ extra_pos = pos + (block_len / 4);
+ }
+ block_type_byte <<= 2;
+ block_type_len -= 1;
+ }
+ if (block_len > 0) {
+ if ((block_len % 4) == 0) {
+ block_byte = compressed[pos];
+ pos++;
+ }
+ if (block_type == 1) /* inter Block */
+ integrator = decompressed[idx];
+ switch (block_byte & 0xC0) {
+ case 0x03 << 6:
+ integrator += compressed[extra_pos];
+ extra_pos++;
+ break;
+ case 0x02 << 6:
+ integrator += block_code;
+ break;
+ case 0x00:
+ integrator -= block_code;
+ break;
+ }
+ decompressed[idx] = integrator;
+ block_byte <<= 2;
+ block_len -= 1;
+ }
+ }
+ *start_pos = extra_pos;
+ *block_typestart_pos = block_type_pos;
+ return idx;
+}
+
+
+/*
+ * usbvision_parse_compress()
+ *
+ * Parse compressed frame from the scratch buffer, put
+ * decoded RGB value into the current frame buffer and add the written
+ * number of bytes (RGB) to the *pcopylen.
+ *
+ */
+static enum parse_state usbvision_parse_compress(struct usb_usbvision *usbvision,
+ long *pcopylen)
+{
+#define USBVISION_STRIP_MAGIC 0x5A
+#define USBVISION_STRIP_LEN_MAX 400
+#define USBVISION_STRIP_HEADER_LEN 3
+
+ struct usbvision_frame *frame;
+ unsigned char *f, *u = NULL, *v = NULL;
+ unsigned char strip_data[USBVISION_STRIP_LEN_MAX];
+ unsigned char strip_header[USBVISION_STRIP_HEADER_LEN];
+ int idx, idx_end, strip_len, strip_ptr, startblock_pos, block_pos, block_type_pos;
+ int clipmask_index, bytes_per_pixel, rc;
+ int image_size;
+ unsigned char rv, gv, bv;
+ static unsigned char *Y, *U, *V;
+
+ frame = usbvision->cur_frame;
+ image_size = frame->frmwidth * frame->frmheight;
+ if ((frame->v4l2_format.format == V4L2_PIX_FMT_YUV422P) ||
+ (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420)) { /* this is a planar format */
+ /* ... v4l2_linesize not used here. */
+ f = frame->data + (frame->width * frame->curline);
+ } else
+ f = frame->data + (frame->v4l2_linesize * frame->curline);
+
+ if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { /* initialise u and v pointers */
+ /* get base of u and b planes add halfoffset */
+ u = frame->data
+ + image_size
+ + (frame->frmwidth >> 1) * frame->curline;
+ v = u + (image_size >> 1);
+ } else if (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420) {
+ v = frame->data + image_size + ((frame->curline * (frame->width)) >> 2);
+ u = v + (image_size >> 2);
+ }
+
+ if (frame->curline == 0)
+ usbvision_adjust_compression(usbvision);
+
+ if (scratch_len(usbvision) < USBVISION_STRIP_HEADER_LEN)
+ return parse_state_out;
+
+ /* get strip header without changing the scratch_read_ptr */
+ scratch_set_extra_ptr(usbvision, &strip_ptr, 0);
+ scratch_get_extra(usbvision, &strip_header[0], &strip_ptr,
+ USBVISION_STRIP_HEADER_LEN);
+
+ if (strip_header[0] != USBVISION_STRIP_MAGIC) {
+ /* wrong strip magic */
+ usbvision->strip_magic_errors++;
+ return parse_state_next_frame;
+ }
+
+ if (frame->curline != (int)strip_header[2]) {
+ /* line number mismatch error */
+ usbvision->strip_line_number_errors++;
+ }
+
+ strip_len = 2 * (unsigned int)strip_header[1];
+ if (strip_len > USBVISION_STRIP_LEN_MAX) {
+ /* strip overrun */
+ /* I think this never happens */
+ usbvision_request_intra(usbvision);
+ }
+
+ if (scratch_len(usbvision) < strip_len) {
+ /* there is not enough data for the strip */
+ return parse_state_out;
+ }
+
+ if (usbvision->intra_frame_buffer) {
+ Y = usbvision->intra_frame_buffer + frame->frmwidth * frame->curline;
+ U = usbvision->intra_frame_buffer + image_size + (frame->frmwidth / 2) * (frame->curline / 2);
+ V = usbvision->intra_frame_buffer + image_size / 4 * 5 + (frame->frmwidth / 2) * (frame->curline / 2);
+ } else {
+ return parse_state_next_frame;
+ }
+
+ bytes_per_pixel = frame->v4l2_format.bytes_per_pixel;
+ clipmask_index = frame->curline * MAX_FRAME_WIDTH;
+
+ scratch_get(usbvision, strip_data, strip_len);
+
+ idx_end = frame->frmwidth;
+ block_type_pos = USBVISION_STRIP_HEADER_LEN;
+ startblock_pos = block_type_pos + (idx_end - 1) / 96 + (idx_end / 2 - 1) / 96 + 2;
+ block_pos = startblock_pos;
+
+ usbvision->block_pos = block_pos;
+
+ rc = usbvision_decompress(usbvision, strip_data, Y, &block_pos, &block_type_pos, idx_end);
+ if (strip_len > usbvision->max_strip_len)
+ usbvision->max_strip_len = strip_len;
+
+ if (frame->curline % 2)
+ rc = usbvision_decompress(usbvision, strip_data, V, &block_pos, &block_type_pos, idx_end / 2);
+ else
+ rc = usbvision_decompress(usbvision, strip_data, U, &block_pos, &block_type_pos, idx_end / 2);
+
+ if (block_pos > usbvision->comprblock_pos)
+ usbvision->comprblock_pos = block_pos;
+ if (block_pos > strip_len)
+ usbvision->strip_len_errors++;
+
+ for (idx = 0; idx < idx_end; idx++) {
+ if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
+ *f++ = Y[idx];
+ *f++ = idx & 0x01 ? U[idx / 2] : V[idx / 2];
+ } else if (frame->v4l2_format.format == V4L2_PIX_FMT_YUV422P) {
+ *f++ = Y[idx];
+ if (idx & 0x01)
+ *u++ = U[idx >> 1];
+ else
+ *v++ = V[idx >> 1];
+ } else if (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420) {
+ *f++ = Y[idx];
+ if (!((idx & 0x01) | (frame->curline & 0x01))) {
+ /* only need do this for 1 in 4 pixels */
+ /* intraframe buffer is YUV420 format */
+ *u++ = U[idx >> 1];
+ *v++ = V[idx >> 1];
+ }
+ } else {
+ YUV_TO_RGB_BY_THE_BOOK(Y[idx], U[idx / 2], V[idx / 2], rv, gv, bv);
+ switch (frame->v4l2_format.format) {
+ case V4L2_PIX_FMT_GREY:
+ *f++ = Y[idx];
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ *f++ = (0x1F & rv) |
+ (0xE0 & (gv << 5));
+ *f++ = (0x03 & (gv >> 3)) |
+ (0x7C & (bv << 2));
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ *f++ = (0x1F & rv) |
+ (0xE0 & (gv << 5));
+ *f++ = (0x07 & (gv >> 3)) |
+ (0xF8 & bv);
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ *f++ = rv;
+ *f++ = gv;
+ *f++ = bv;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ *f++ = rv;
+ *f++ = gv;
+ *f++ = bv;
+ f++;
+ break;
+ }
+ }
+ clipmask_index++;
+ }
+ /* Deal with non-integer no. of bytes for YUV420P */
+ if (frame->v4l2_format.format != V4L2_PIX_FMT_YVU420)
+ *pcopylen += frame->v4l2_linesize;
+ else
+ *pcopylen += frame->curline & 0x01 ? frame->v4l2_linesize : frame->v4l2_linesize << 1;
+
+ frame->curline += 1;
+
+ if (frame->curline >= frame->frmheight)
+ return parse_state_next_frame;
+ return parse_state_continue;
+
+}
+
+
+/*
+ * usbvision_parse_lines_420()
+ *
+ * Parse two lines from the scratch buffer, put
+ * decoded RGB value into the current frame buffer and add the written
+ * number of bytes (RGB) to the *pcopylen.
+ *
+ */
+static enum parse_state usbvision_parse_lines_420(struct usb_usbvision *usbvision,
+ long *pcopylen)
+{
+ struct usbvision_frame *frame;
+ unsigned char *f_even = NULL, *f_odd = NULL;
+ unsigned int pixel_per_line, block;
+ int pixel, block_split;
+ int y_ptr, u_ptr, v_ptr, y_odd_offset;
+ const int y_block_size = 128;
+ const int uv_block_size = 64;
+ const int sub_block_size = 32;
+ const int y_step[] = { 0, 0, 0, 2 }, y_step_size = 4;
+ const int uv_step[] = { 0, 0, 0, 4 }, uv_step_size = 4;
+ unsigned char y[2], u, v; /* YUV components */
+ int y_, u_, v_, vb, uvg, ur;
+ int r_, g_, b_; /* RGB components */
+ unsigned char g;
+ int clipmask_even_index, clipmask_odd_index, bytes_per_pixel;
+ int clipmask_add, stretch_bytes;
+
+ frame = usbvision->cur_frame;
+ f_even = frame->data + (frame->v4l2_linesize * frame->curline);
+ f_odd = f_even + frame->v4l2_linesize * usbvision->stretch_height;
+
+ /* Make sure there's enough data for the entire line */
+ /* In this mode usbvision transfer 3 bytes for every 2 pixels */
+ /* I need two lines to decode the color */
+ bytes_per_pixel = frame->v4l2_format.bytes_per_pixel;
+ stretch_bytes = (usbvision->stretch_width - 1) * bytes_per_pixel;
+ clipmask_even_index = frame->curline * MAX_FRAME_WIDTH;
+ clipmask_odd_index = clipmask_even_index + MAX_FRAME_WIDTH;
+ clipmask_add = usbvision->stretch_width;
+ pixel_per_line = frame->isoc_header.frame_width;
+
+ if (scratch_len(usbvision) < (int)pixel_per_line * 3) {
+ /* printk(KERN_DEBUG "out of data, need %d\n", len); */
+ return parse_state_out;
+ }
+
+ if ((frame->curline + 1) >= frame->frmheight)
+ return parse_state_next_frame;
+
+ block_split = (pixel_per_line%y_block_size) ? 1 : 0; /* are some blocks splitted into different lines? */
+
+ y_odd_offset = (pixel_per_line / y_block_size) * (y_block_size + uv_block_size)
+ + block_split * uv_block_size;
+
+ scratch_set_extra_ptr(usbvision, &y_ptr, y_odd_offset);
+ scratch_set_extra_ptr(usbvision, &u_ptr, y_block_size);
+ scratch_set_extra_ptr(usbvision, &v_ptr, y_odd_offset
+ + (4 - block_split) * sub_block_size);
+
+ for (block = 0; block < (pixel_per_line / sub_block_size); block++) {
+ for (pixel = 0; pixel < sub_block_size; pixel += 2) {
+ scratch_get(usbvision, &y[0], 2);
+ scratch_get_extra(usbvision, &u, &u_ptr, 1);
+ scratch_get_extra(usbvision, &v, &v_ptr, 1);
+
+ /* I don't use the YUV_TO_RGB macro for better performance */
+ v_ = v - 128;
+ u_ = u - 128;
+ vb = 132252 * v_;
+ uvg = -53281 * u_ - 25625 * v_;
+ ur = 104595 * u_;
+
+ if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
+ *f_even++ = y[0];
+ *f_even++ = v;
+ } else {
+ y_ = 76284 * (y[0] - 16);
+
+ b_ = (y_ + vb) >> 16;
+ g_ = (y_ + uvg) >> 16;
+ r_ = (y_ + ur) >> 16;
+
+ switch (frame->v4l2_format.format) {
+ case V4L2_PIX_FMT_RGB565:
+ g = LIMIT_RGB(g_);
+ *f_even++ =
+ (0x1F & LIMIT_RGB(r_)) |
+ (0xE0 & (g << 5));
+ *f_even++ =
+ (0x07 & (g >> 3)) |
+ (0xF8 & LIMIT_RGB(b_));
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ *f_even++ = LIMIT_RGB(r_);
+ *f_even++ = LIMIT_RGB(g_);
+ *f_even++ = LIMIT_RGB(b_);
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ *f_even++ = LIMIT_RGB(r_);
+ *f_even++ = LIMIT_RGB(g_);
+ *f_even++ = LIMIT_RGB(b_);
+ f_even++;
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ g = LIMIT_RGB(g_);
+ *f_even++ = (0x1F & LIMIT_RGB(r_)) |
+ (0xE0 & (g << 5));
+ *f_even++ = (0x03 & (g >> 3)) |
+ (0x7C & (LIMIT_RGB(b_) << 2));
+ break;
+ }
+ }
+ clipmask_even_index += clipmask_add;
+ f_even += stretch_bytes;
+
+ if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
+ *f_even++ = y[1];
+ *f_even++ = u;
+ } else {
+ y_ = 76284 * (y[1] - 16);
+
+ b_ = (y_ + vb) >> 16;
+ g_ = (y_ + uvg) >> 16;
+ r_ = (y_ + ur) >> 16;
+
+ switch (frame->v4l2_format.format) {
+ case V4L2_PIX_FMT_RGB565:
+ g = LIMIT_RGB(g_);
+ *f_even++ =
+ (0x1F & LIMIT_RGB(r_)) |
+ (0xE0 & (g << 5));
+ *f_even++ =
+ (0x07 & (g >> 3)) |
+ (0xF8 & LIMIT_RGB(b_));
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ *f_even++ = LIMIT_RGB(r_);
+ *f_even++ = LIMIT_RGB(g_);
+ *f_even++ = LIMIT_RGB(b_);
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ *f_even++ = LIMIT_RGB(r_);
+ *f_even++ = LIMIT_RGB(g_);
+ *f_even++ = LIMIT_RGB(b_);
+ f_even++;
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ g = LIMIT_RGB(g_);
+ *f_even++ = (0x1F & LIMIT_RGB(r_)) |
+ (0xE0 & (g << 5));
+ *f_even++ = (0x03 & (g >> 3)) |
+ (0x7C & (LIMIT_RGB(b_) << 2));
+ break;
+ }
+ }
+ clipmask_even_index += clipmask_add;
+ f_even += stretch_bytes;
+
+ scratch_get_extra(usbvision, &y[0], &y_ptr, 2);
+
+ if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
+ *f_odd++ = y[0];
+ *f_odd++ = v;
+ } else {
+ y_ = 76284 * (y[0] - 16);
+
+ b_ = (y_ + vb) >> 16;
+ g_ = (y_ + uvg) >> 16;
+ r_ = (y_ + ur) >> 16;
+
+ switch (frame->v4l2_format.format) {
+ case V4L2_PIX_FMT_RGB565:
+ g = LIMIT_RGB(g_);
+ *f_odd++ =
+ (0x1F & LIMIT_RGB(r_)) |
+ (0xE0 & (g << 5));
+ *f_odd++ =
+ (0x07 & (g >> 3)) |
+ (0xF8 & LIMIT_RGB(b_));
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ *f_odd++ = LIMIT_RGB(r_);
+ *f_odd++ = LIMIT_RGB(g_);
+ *f_odd++ = LIMIT_RGB(b_);
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ *f_odd++ = LIMIT_RGB(r_);
+ *f_odd++ = LIMIT_RGB(g_);
+ *f_odd++ = LIMIT_RGB(b_);
+ f_odd++;
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ g = LIMIT_RGB(g_);
+ *f_odd++ = (0x1F & LIMIT_RGB(r_)) |
+ (0xE0 & (g << 5));
+ *f_odd++ = (0x03 & (g >> 3)) |
+ (0x7C & (LIMIT_RGB(b_) << 2));
+ break;
+ }
+ }
+ clipmask_odd_index += clipmask_add;
+ f_odd += stretch_bytes;
+
+ if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
+ *f_odd++ = y[1];
+ *f_odd++ = u;
+ } else {
+ y_ = 76284 * (y[1] - 16);
+
+ b_ = (y_ + vb) >> 16;
+ g_ = (y_ + uvg) >> 16;
+ r_ = (y_ + ur) >> 16;
+
+ switch (frame->v4l2_format.format) {
+ case V4L2_PIX_FMT_RGB565:
+ g = LIMIT_RGB(g_);
+ *f_odd++ =
+ (0x1F & LIMIT_RGB(r_)) |
+ (0xE0 & (g << 5));
+ *f_odd++ =
+ (0x07 & (g >> 3)) |
+ (0xF8 & LIMIT_RGB(b_));
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ *f_odd++ = LIMIT_RGB(r_);
+ *f_odd++ = LIMIT_RGB(g_);
+ *f_odd++ = LIMIT_RGB(b_);
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ *f_odd++ = LIMIT_RGB(r_);
+ *f_odd++ = LIMIT_RGB(g_);
+ *f_odd++ = LIMIT_RGB(b_);
+ f_odd++;
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ g = LIMIT_RGB(g_);
+ *f_odd++ = (0x1F & LIMIT_RGB(r_)) |
+ (0xE0 & (g << 5));
+ *f_odd++ = (0x03 & (g >> 3)) |
+ (0x7C & (LIMIT_RGB(b_) << 2));
+ break;
+ }
+ }
+ clipmask_odd_index += clipmask_add;
+ f_odd += stretch_bytes;
+ }
+
+ scratch_rm_old(usbvision, y_step[block % y_step_size] * sub_block_size);
+ scratch_inc_extra_ptr(&y_ptr, y_step[(block + 2 * block_split) % y_step_size]
+ * sub_block_size);
+ scratch_inc_extra_ptr(&u_ptr, uv_step[block % uv_step_size]
+ * sub_block_size);
+ scratch_inc_extra_ptr(&v_ptr, uv_step[(block + 2 * block_split) % uv_step_size]
+ * sub_block_size);
+ }
+
+ scratch_rm_old(usbvision, pixel_per_line * 3 / 2
+ + block_split * sub_block_size);
+
+ frame->curline += 2 * usbvision->stretch_height;
+ *pcopylen += frame->v4l2_linesize * 2 * usbvision->stretch_height;
+
+ if (frame->curline >= frame->frmheight)
+ return parse_state_next_frame;
+ return parse_state_continue;
+}
+
+/*
+ * usbvision_parse_data()
+ *
+ * Generic routine to parse the scratch buffer. It employs either
+ * usbvision_find_header() or usbvision_parse_lines() to do most
+ * of work.
+ *
+ */
+static void usbvision_parse_data(struct usb_usbvision *usbvision)
+{
+ struct usbvision_frame *frame;
+ enum parse_state newstate;
+ long copylen = 0;
+ unsigned long lock_flags;
+
+ frame = usbvision->cur_frame;
+
+ PDEBUG(DBG_PARSE, "parsing len=%d\n", scratch_len(usbvision));
+
+ while (1) {
+ newstate = parse_state_out;
+ if (scratch_len(usbvision)) {
+ if (frame->scanstate == scan_state_scanning) {
+ newstate = usbvision_find_header(usbvision);
+ } else if (frame->scanstate == scan_state_lines) {
+ if (usbvision->isoc_mode == ISOC_MODE_YUV420)
+ newstate = usbvision_parse_lines_420(usbvision, &copylen);
+ else if (usbvision->isoc_mode == ISOC_MODE_YUV422)
+ newstate = usbvision_parse_lines_422(usbvision, &copylen);
+ else if (usbvision->isoc_mode == ISOC_MODE_COMPRESS)
+ newstate = usbvision_parse_compress(usbvision, &copylen);
+ }
+ }
+ if (newstate == parse_state_continue)
+ continue;
+ if ((newstate == parse_state_next_frame) || (newstate == parse_state_out))
+ break;
+ return; /* parse_state_end_parse */
+ }
+
+ if (newstate == parse_state_next_frame) {
+ frame->grabstate = frame_state_done;
+ do_gettimeofday(&(frame->timestamp));
+ frame->sequence = usbvision->frame_num;
+
+ spin_lock_irqsave(&usbvision->queue_lock, lock_flags);
+ list_move_tail(&(frame->frame), &usbvision->outqueue);
+ usbvision->cur_frame = NULL;
+ spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags);
+
+ usbvision->frame_num++;
+
+ /* This will cause the process to request another frame. */
+ if (waitqueue_active(&usbvision->wait_frame)) {
+ PDEBUG(DBG_PARSE, "Wake up !");
+ wake_up_interruptible(&usbvision->wait_frame);
+ }
+ } else {
+ frame->grabstate = frame_state_grabbing;
+ }
+
+ /* Update the frame's uncompressed length. */
+ frame->scanlength += copylen;
+}
+
+
+/*
+ * Make all of the blocks of data contiguous
+ */
+static int usbvision_compress_isochronous(struct usb_usbvision *usbvision,
+ struct urb *urb)
+{
+ unsigned char *packet_data;
+ int i, totlen = 0;
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ int packet_len = urb->iso_frame_desc[i].actual_length;
+ int packet_stat = urb->iso_frame_desc[i].status;
+
+ packet_data = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+
+ /* Detect and ignore errored packets */
+ if (packet_stat) { /* packet_stat != 0 ????????????? */
+ PDEBUG(DBG_ISOC, "data error: [%d] len=%d, status=%X", i, packet_len, packet_stat);
+ usbvision->isoc_err_count++;
+ continue;
+ }
+
+ /* Detect and ignore empty packets */
+ if (packet_len < 0) {
+ PDEBUG(DBG_ISOC, "error packet [%d]", i);
+ usbvision->isoc_skip_count++;
+ continue;
+ } else if (packet_len == 0) { /* Frame end ????? */
+ PDEBUG(DBG_ISOC, "null packet [%d]", i);
+ usbvision->isocstate = isoc_state_no_frame;
+ usbvision->isoc_skip_count++;
+ continue;
+ } else if (packet_len > usbvision->isoc_packet_size) {
+ PDEBUG(DBG_ISOC, "packet[%d] > isoc_packet_size", i);
+ usbvision->isoc_skip_count++;
+ continue;
+ }
+
+ PDEBUG(DBG_ISOC, "packet ok [%d] len=%d", i, packet_len);
+
+ if (usbvision->isocstate == isoc_state_no_frame) { /* new frame begins */
+ usbvision->isocstate = isoc_state_in_frame;
+ scratch_mark_header(usbvision);
+ usbvision_measure_bandwidth(usbvision);
+ PDEBUG(DBG_ISOC, "packet with header");
+ }
+
+ /*
+ * If usbvision continues to feed us with data but there is no
+ * consumption (if, for example, V4L client fell asleep) we
+ * may overflow the buffer. We have to move old data over to
+ * free room for new data. This is bad for old data. If we
+ * just drop new data then it's bad for new data... choose
+ * your favorite evil here.
+ */
+ if (scratch_free(usbvision) < packet_len) {
+ usbvision->scratch_ovf_count++;
+ PDEBUG(DBG_ISOC, "scratch buf overflow! scr_len: %d, n: %d",
+ scratch_len(usbvision), packet_len);
+ scratch_rm_old(usbvision, packet_len - scratch_free(usbvision));
+ }
+
+ /* Now we know that there is enough room in scratch buffer */
+ scratch_put(usbvision, packet_data, packet_len);
+ totlen += packet_len;
+ usbvision->isoc_data_count += packet_len;
+ usbvision->isoc_packet_count++;
+ }
+#if ENABLE_HEXDUMP
+ if (totlen > 0) {
+ static int foo;
+
+ if (foo < 1) {
+ printk(KERN_DEBUG "+%d.\n", usbvision->scratchlen);
+ usbvision_hexdump(data0, (totlen > 64) ? 64 : totlen);
+ ++foo;
+ }
+ }
+#endif
+ return totlen;
+}
+
+static void usbvision_isoc_irq(struct urb *urb)
+{
+ int err_code = 0;
+ int len;
+ struct usb_usbvision *usbvision = urb->context;
+ int i;
+ unsigned long start_time = jiffies;
+ struct usbvision_frame **f;
+
+ /* We don't want to do anything if we are about to be removed! */
+ if (!USBVISION_IS_OPERATIONAL(usbvision))
+ return;
+
+ /* any urb with wrong status is ignored without acknowledgement */
+ if (urb->status == -ENOENT)
+ return;
+
+ f = &usbvision->cur_frame;
+
+ /* Manage streaming interruption */
+ if (usbvision->streaming == stream_interrupt) {
+ usbvision->streaming = stream_idle;
+ if ((*f)) {
+ (*f)->grabstate = frame_state_ready;
+ (*f)->scanstate = scan_state_scanning;
+ }
+ PDEBUG(DBG_IRQ, "stream interrupted");
+ wake_up_interruptible(&usbvision->wait_stream);
+ }
+
+ /* Copy the data received into our scratch buffer */
+ len = usbvision_compress_isochronous(usbvision, urb);
+
+ usbvision->isoc_urb_count++;
+ usbvision->urb_length = len;
+
+ if (usbvision->streaming == stream_on) {
+ /* If we collected enough data let's parse! */
+ if (scratch_len(usbvision) > USBVISION_HEADER_LENGTH &&
+ !list_empty(&(usbvision->inqueue))) {
+ if (!(*f)) {
+ (*f) = list_entry(usbvision->inqueue.next,
+ struct usbvision_frame,
+ frame);
+ }
+ usbvision_parse_data(usbvision);
+ } else {
+ /* If we don't have a frame
+ we're current working on, complain */
+ PDEBUG(DBG_IRQ,
+ "received data, but no one needs it");
+ scratch_reset(usbvision);
+ }
+ } else {
+ PDEBUG(DBG_IRQ, "received data, but no one needs it");
+ scratch_reset(usbvision);
+ }
+
+ usbvision->time_in_irq += jiffies - start_time;
+
+ for (i = 0; i < USBVISION_URB_FRAMES; i++) {
+ urb->iso_frame_desc[i].status = 0;
+ urb->iso_frame_desc[i].actual_length = 0;
+ }
+
+ urb->status = 0;
+ urb->dev = usbvision->dev;
+ err_code = usb_submit_urb(urb, GFP_ATOMIC);
+
+ if (err_code) {
+ dev_err(&usbvision->dev->dev,
+ "%s: usb_submit_urb failed: error %d\n",
+ __func__, err_code);
+ }
+
+ return;
+}
+
+/*************************************/
+/* Low level usbvision access functions */
+/*************************************/
+
+/*
+ * usbvision_read_reg()
+ *
+ * return < 0 -> Error
+ * >= 0 -> Data
+ */
+
+int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg)
+{
+ int err_code = 0;
+ unsigned char buffer[1];
+
+ if (!USBVISION_IS_OPERATIONAL(usbvision))
+ return -1;
+
+ err_code = usb_control_msg(usbvision->dev, usb_rcvctrlpipe(usbvision->dev, 1),
+ USBVISION_OP_CODE,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
+ 0, (__u16) reg, buffer, 1, HZ);
+
+ if (err_code < 0) {
+ dev_err(&usbvision->dev->dev,
+ "%s: failed: error %d\n", __func__, err_code);
+ return err_code;
+ }
+ return buffer[0];
+}
+
+/*
+ * usbvision_write_reg()
+ *
+ * return 1 -> Reg written
+ * 0 -> usbvision is not yet ready
+ * -1 -> Something went wrong
+ */
+
+int usbvision_write_reg(struct usb_usbvision *usbvision, unsigned char reg,
+ unsigned char value)
+{
+ int err_code = 0;
+
+ if (!USBVISION_IS_OPERATIONAL(usbvision))
+ return 0;
+
+ err_code = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
+ USBVISION_OP_CODE,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_ENDPOINT, 0, (__u16) reg, &value, 1, HZ);
+
+ if (err_code < 0) {
+ dev_err(&usbvision->dev->dev,
+ "%s: failed: error %d\n", __func__, err_code);
+ }
+ return err_code;
+}
+
+
+static void usbvision_ctrl_urb_complete(struct urb *urb)
+{
+ struct usb_usbvision *usbvision = (struct usb_usbvision *)urb->context;
+
+ PDEBUG(DBG_IRQ, "");
+ usbvision->ctrl_urb_busy = 0;
+ if (waitqueue_active(&usbvision->ctrl_urb_wq))
+ wake_up_interruptible(&usbvision->ctrl_urb_wq);
+}
+
+
+static int usbvision_write_reg_irq(struct usb_usbvision *usbvision, int address,
+ unsigned char *data, int len)
+{
+ int err_code = 0;
+
+ PDEBUG(DBG_IRQ, "");
+ if (len > 8)
+ return -EFAULT;
+ if (usbvision->ctrl_urb_busy)
+ return -EBUSY;
+ usbvision->ctrl_urb_busy = 1;
+
+ usbvision->ctrl_urb_setup.bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT;
+ usbvision->ctrl_urb_setup.bRequest = USBVISION_OP_CODE;
+ usbvision->ctrl_urb_setup.wValue = 0;
+ usbvision->ctrl_urb_setup.wIndex = cpu_to_le16(address);
+ usbvision->ctrl_urb_setup.wLength = cpu_to_le16(len);
+ usb_fill_control_urb(usbvision->ctrl_urb, usbvision->dev,
+ usb_sndctrlpipe(usbvision->dev, 1),
+ (unsigned char *)&usbvision->ctrl_urb_setup,
+ (void *)usbvision->ctrl_urb_buffer, len,
+ usbvision_ctrl_urb_complete,
+ (void *)usbvision);
+
+ memcpy(usbvision->ctrl_urb_buffer, data, len);
+
+ err_code = usb_submit_urb(usbvision->ctrl_urb, GFP_ATOMIC);
+ if (err_code < 0) {
+ /* error in usb_submit_urb() */
+ usbvision->ctrl_urb_busy = 0;
+ }
+ PDEBUG(DBG_IRQ, "submit %d byte: error %d", len, err_code);
+ return err_code;
+}
+
+
+static int usbvision_init_compression(struct usb_usbvision *usbvision)
+{
+ int err_code = 0;
+
+ usbvision->last_isoc_frame_num = -1;
+ usbvision->isoc_data_count = 0;
+ usbvision->isoc_packet_count = 0;
+ usbvision->isoc_skip_count = 0;
+ usbvision->compr_level = 50;
+ usbvision->last_compr_level = -1;
+ usbvision->isoc_urb_count = 0;
+ usbvision->request_intra = 1;
+ usbvision->isoc_measure_bandwidth_count = 0;
+
+ return err_code;
+}
+
+/* this function measures the used bandwidth since last call
+ * return: 0 : no error
+ * sets used_bandwidth to 1-100 : 1-100% of full bandwidth resp. to isoc_packet_size
+ */
+static int usbvision_measure_bandwidth(struct usb_usbvision *usbvision)
+{
+ int err_code = 0;
+
+ if (usbvision->isoc_measure_bandwidth_count < 2) { /* this gives an average bandwidth of 3 frames */
+ usbvision->isoc_measure_bandwidth_count++;
+ return err_code;
+ }
+ if ((usbvision->isoc_packet_size > 0) && (usbvision->isoc_packet_count > 0)) {
+ usbvision->used_bandwidth = usbvision->isoc_data_count /
+ (usbvision->isoc_packet_count + usbvision->isoc_skip_count) *
+ 100 / usbvision->isoc_packet_size;
+ }
+ usbvision->isoc_measure_bandwidth_count = 0;
+ usbvision->isoc_data_count = 0;
+ usbvision->isoc_packet_count = 0;
+ usbvision->isoc_skip_count = 0;
+ return err_code;
+}
+
+static int usbvision_adjust_compression(struct usb_usbvision *usbvision)
+{
+ int err_code = 0;
+ unsigned char buffer[6];
+
+ PDEBUG(DBG_IRQ, "");
+ if ((adjust_compression) && (usbvision->used_bandwidth > 0)) {
+ usbvision->compr_level += (usbvision->used_bandwidth - 90) / 2;
+ RESTRICT_TO_RANGE(usbvision->compr_level, 0, 100);
+ if (usbvision->compr_level != usbvision->last_compr_level) {
+ int distortion;
+
+ if (usbvision->bridge_type == BRIDGE_NT1004 || usbvision->bridge_type == BRIDGE_NT1005) {
+ buffer[0] = (unsigned char)(4 + 16 * usbvision->compr_level / 100); /* PCM Threshold 1 */
+ buffer[1] = (unsigned char)(4 + 8 * usbvision->compr_level / 100); /* PCM Threshold 2 */
+ distortion = 7 + 248 * usbvision->compr_level / 100;
+ buffer[2] = (unsigned char)(distortion & 0xFF); /* Average distortion Threshold (inter) */
+ buffer[3] = (unsigned char)(distortion & 0xFF); /* Average distortion Threshold (intra) */
+ distortion = 1 + 42 * usbvision->compr_level / 100;
+ buffer[4] = (unsigned char)(distortion & 0xFF); /* Maximum distortion Threshold (inter) */
+ buffer[5] = (unsigned char)(distortion & 0xFF); /* Maximum distortion Threshold (intra) */
+ } else { /* BRIDGE_NT1003 */
+ buffer[0] = (unsigned char)(4 + 16 * usbvision->compr_level / 100); /* PCM threshold 1 */
+ buffer[1] = (unsigned char)(4 + 8 * usbvision->compr_level / 100); /* PCM threshold 2 */
+ distortion = 2 + 253 * usbvision->compr_level / 100;
+ buffer[2] = (unsigned char)(distortion & 0xFF); /* distortion threshold bit0-7 */
+ buffer[3] = 0; /* (unsigned char)((distortion >> 8) & 0x0F); distortion threshold bit 8-11 */
+ distortion = 0 + 43 * usbvision->compr_level / 100;
+ buffer[4] = (unsigned char)(distortion & 0xFF); /* maximum distortion bit0-7 */
+ buffer[5] = 0; /* (unsigned char)((distortion >> 8) & 0x01); maximum distortion bit 8 */
+ }
+ err_code = usbvision_write_reg_irq(usbvision, USBVISION_PCM_THR1, buffer, 6);
+ if (err_code == 0) {
+ PDEBUG(DBG_IRQ, "new compr params %#02x %#02x %#02x %#02x %#02x %#02x", buffer[0],
+ buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]);
+ usbvision->last_compr_level = usbvision->compr_level;
+ }
+ }
+ }
+ return err_code;
+}
+
+static int usbvision_request_intra(struct usb_usbvision *usbvision)
+{
+ int err_code = 0;
+ unsigned char buffer[1];
+
+ PDEBUG(DBG_IRQ, "");
+ usbvision->request_intra = 1;
+ buffer[0] = 1;
+ usbvision_write_reg_irq(usbvision, USBVISION_FORCE_INTRA, buffer, 1);
+ return err_code;
+}
+
+static int usbvision_unrequest_intra(struct usb_usbvision *usbvision)
+{
+ int err_code = 0;
+ unsigned char buffer[1];
+
+ PDEBUG(DBG_IRQ, "");
+ usbvision->request_intra = 0;
+ buffer[0] = 0;
+ usbvision_write_reg_irq(usbvision, USBVISION_FORCE_INTRA, buffer, 1);
+ return err_code;
+}
+
+/*******************************
+ * usbvision utility functions
+ *******************************/
+
+int usbvision_power_off(struct usb_usbvision *usbvision)
+{
+ int err_code = 0;
+
+ PDEBUG(DBG_FUNC, "");
+
+ err_code = usbvision_write_reg(usbvision, USBVISION_PWR_REG, USBVISION_SSPND_EN);
+ if (err_code == 1)
+ usbvision->power = 0;
+ PDEBUG(DBG_FUNC, "%s: err_code %d", (err_code != 1) ? "ERROR" : "power is off", err_code);
+ return err_code;
+}
+
+/* configure webcam image sensor using the serial port */
+static int usbvision_init_webcam(struct usb_usbvision *usbvision)
+{
+ int rc;
+ int i;
+ static char init_values[38][3] = {
+ { 0x04, 0x12, 0x08 }, { 0x05, 0xff, 0xc8 }, { 0x06, 0x18, 0x07 }, { 0x07, 0x90, 0x00 },
+ { 0x09, 0x00, 0x00 }, { 0x0a, 0x00, 0x00 }, { 0x0b, 0x08, 0x00 }, { 0x0d, 0xcc, 0xcc },
+ { 0x0e, 0x13, 0x14 }, { 0x10, 0x9b, 0x83 }, { 0x11, 0x5a, 0x3f }, { 0x12, 0xe4, 0x73 },
+ { 0x13, 0x88, 0x84 }, { 0x14, 0x89, 0x80 }, { 0x15, 0x00, 0x20 }, { 0x16, 0x00, 0x00 },
+ { 0x17, 0xff, 0xa0 }, { 0x18, 0x6b, 0x20 }, { 0x19, 0x22, 0x40 }, { 0x1a, 0x10, 0x07 },
+ { 0x1b, 0x00, 0x47 }, { 0x1c, 0x03, 0xe0 }, { 0x1d, 0x00, 0x00 }, { 0x1e, 0x00, 0x00 },
+ { 0x1f, 0x00, 0x00 }, { 0x20, 0x00, 0x00 }, { 0x21, 0x00, 0x00 }, { 0x22, 0x00, 0x00 },
+ { 0x23, 0x00, 0x00 }, { 0x24, 0x00, 0x00 }, { 0x25, 0x00, 0x00 }, { 0x26, 0x00, 0x00 },
+ { 0x27, 0x00, 0x00 }, { 0x28, 0x00, 0x00 }, { 0x29, 0x00, 0x00 }, { 0x08, 0x80, 0x60 },
+ { 0x0f, 0x2d, 0x24 }, { 0x0c, 0x80, 0x80 }
+ };
+ char value[3];
+
+ /* the only difference between PAL and NTSC init_values */
+ if (usbvision_device_data[usbvision->dev_model].video_norm == V4L2_STD_NTSC)
+ init_values[4][1] = 0x34;
+
+ for (i = 0; i < sizeof(init_values) / 3; i++) {
+ usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SOFT);
+ memcpy(value, init_values[i], 3);
+ rc = usb_control_msg(usbvision->dev,
+ usb_sndctrlpipe(usbvision->dev, 1),
+ USBVISION_OP_CODE,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_ENDPOINT, 0,
+ (__u16) USBVISION_SER_DAT1, value,
+ 3, HZ);
+ if (rc < 0)
+ return rc;
+ usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SIO);
+ /* write 3 bytes to the serial port using SIO mode */
+ usbvision_write_reg(usbvision, USBVISION_SER_CONT, 3 | 0x10);
+ usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, 0);
+ usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SOFT);
+ usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, USBVISION_IO_2);
+ usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SOFT | USBVISION_CLK_OUT);
+ usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SOFT | USBVISION_DAT_IO);
+ usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_SER_MODE_SOFT | USBVISION_CLK_OUT | USBVISION_DAT_IO);
+ }
+
+ return 0;
+}
+
+/*
+ * usbvision_set_video_format()
+ *
+ */
+static int usbvision_set_video_format(struct usb_usbvision *usbvision, int format)
+{
+ static const char proc[] = "usbvision_set_video_format";
+ int rc;
+ unsigned char value[2];
+
+ if (!USBVISION_IS_OPERATIONAL(usbvision))
+ return 0;
+
+ PDEBUG(DBG_FUNC, "isoc_mode %#02x", format);
+
+ if ((format != ISOC_MODE_YUV422)
+ && (format != ISOC_MODE_YUV420)
+ && (format != ISOC_MODE_COMPRESS)) {
+ printk(KERN_ERR "usbvision: unknown video format %02x, using default YUV420",
+ format);
+ format = ISOC_MODE_YUV420;
+ }
+ value[0] = 0x0A; /* TODO: See the effect of the filter */
+ value[1] = format; /* Sets the VO_MODE register which follows FILT_CONT */
+ rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
+ USBVISION_OP_CODE,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_ENDPOINT, 0,
+ (__u16) USBVISION_FILT_CONT, value, 2, HZ);
+
+ if (rc < 0) {
+ printk(KERN_ERR "%s: ERROR=%d. USBVISION stopped - "
+ "reconnect or reload driver.\n", proc, rc);
+ }
+ usbvision->isoc_mode = format;
+ return rc;
+}
+
+/*
+ * usbvision_set_output()
+ *
+ */
+
+int usbvision_set_output(struct usb_usbvision *usbvision, int width,
+ int height)
+{
+ int err_code = 0;
+ int usb_width, usb_height;
+ unsigned int frame_rate = 0, frame_drop = 0;
+ unsigned char value[4];
+
+ if (!USBVISION_IS_OPERATIONAL(usbvision))
+ return 0;
+
+ if (width > MAX_USB_WIDTH) {
+ usb_width = width / 2;
+ usbvision->stretch_width = 2;
+ } else {
+ usb_width = width;
+ usbvision->stretch_width = 1;
+ }
+
+ if (height > MAX_USB_HEIGHT) {
+ usb_height = height / 2;
+ usbvision->stretch_height = 2;
+ } else {
+ usb_height = height;
+ usbvision->stretch_height = 1;
+ }
+
+ RESTRICT_TO_RANGE(usb_width, MIN_FRAME_WIDTH, MAX_USB_WIDTH);
+ usb_width &= ~(MIN_FRAME_WIDTH-1);
+ RESTRICT_TO_RANGE(usb_height, MIN_FRAME_HEIGHT, MAX_USB_HEIGHT);
+ usb_height &= ~(1);
+
+ PDEBUG(DBG_FUNC, "usb %dx%d; screen %dx%d; stretch %dx%d",
+ usb_width, usb_height, width, height,
+ usbvision->stretch_width, usbvision->stretch_height);
+
+ /* I'll not rewrite the same values */
+ if ((usb_width != usbvision->curwidth) || (usb_height != usbvision->curheight)) {
+ value[0] = usb_width & 0xff; /* LSB */
+ value[1] = (usb_width >> 8) & 0x03; /* MSB */
+ value[2] = usb_height & 0xff; /* LSB */
+ value[3] = (usb_height >> 8) & 0x03; /* MSB */
+
+ err_code = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
+ USBVISION_OP_CODE,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
+ 0, (__u16) USBVISION_LXSIZE_O, value, 4, HZ);
+
+ if (err_code < 0) {
+ dev_err(&usbvision->dev->dev,
+ "%s failed: error %d\n", __func__, err_code);
+ return err_code;
+ }
+ usbvision->curwidth = usbvision->stretch_width * usb_width;
+ usbvision->curheight = usbvision->stretch_height * usb_height;
+ }
+
+ if (usbvision->isoc_mode == ISOC_MODE_YUV422)
+ frame_rate = (usbvision->isoc_packet_size * 1000) / (usb_width * usb_height * 2);
+ else if (usbvision->isoc_mode == ISOC_MODE_YUV420)
+ frame_rate = (usbvision->isoc_packet_size * 1000) / ((usb_width * usb_height * 12) / 8);
+ else
+ frame_rate = FRAMERATE_MAX;
+
+ if (usbvision->tvnorm_id & V4L2_STD_625_50)
+ frame_drop = frame_rate * 32 / 25 - 1;
+ else if (usbvision->tvnorm_id & V4L2_STD_525_60)
+ frame_drop = frame_rate * 32 / 30 - 1;
+
+ RESTRICT_TO_RANGE(frame_drop, FRAMERATE_MIN, FRAMERATE_MAX);
+
+ PDEBUG(DBG_FUNC, "frame_rate %d fps, frame_drop %d", frame_rate, frame_drop);
+
+ frame_drop = FRAMERATE_MAX; /* We can allow the maximum here, because dropping is controlled */
+
+ if (usbvision_device_data[usbvision->dev_model].codec == CODEC_WEBCAM) {
+ if (usbvision_device_data[usbvision->dev_model].video_norm == V4L2_STD_PAL)
+ frame_drop = 25;
+ else
+ frame_drop = 30;
+ }
+
+ /* frame_drop = 7; => frame_phase = 1, 5, 9, 13, 17, 21, 25, 0, 4, 8, ...
+ => frame_skip = 4;
+ => frame_rate = (7 + 1) * 25 / 32 = 200 / 32 = 6.25;
+
+ frame_drop = 9; => frame_phase = 1, 5, 8, 11, 14, 17, 21, 24, 27, 1, 4, 8, ...
+ => frame_skip = 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 4, ...
+ => frame_rate = (9 + 1) * 25 / 32 = 250 / 32 = 7.8125;
+ */
+ err_code = usbvision_write_reg(usbvision, USBVISION_FRM_RATE, frame_drop);
+ return err_code;
+}
+
+
+/*
+ * usbvision_frames_alloc
+ * allocate the required frames
+ */
+int usbvision_frames_alloc(struct usb_usbvision *usbvision, int number_of_frames)
+{
+ int i;
+
+ /* needs to be page aligned cause the buffers can be mapped individually! */
+ usbvision->max_frame_size = PAGE_ALIGN(usbvision->curwidth *
+ usbvision->curheight *
+ usbvision->palette.bytes_per_pixel);
+
+ /* Try to do my best to allocate the frames the user want in the remaining memory */
+ usbvision->num_frames = number_of_frames;
+ while (usbvision->num_frames > 0) {
+ usbvision->fbuf_size = usbvision->num_frames * usbvision->max_frame_size;
+ usbvision->fbuf = usbvision_rvmalloc(usbvision->fbuf_size);
+ if (usbvision->fbuf)
+ break;
+ usbvision->num_frames--;
+ }
+
+ spin_lock_init(&usbvision->queue_lock);
+ init_waitqueue_head(&usbvision->wait_frame);
+ init_waitqueue_head(&usbvision->wait_stream);
+
+ /* Allocate all buffers */
+ for (i = 0; i < usbvision->num_frames; i++) {
+ usbvision->frame[i].index = i;
+ usbvision->frame[i].grabstate = frame_state_unused;
+ usbvision->frame[i].data = usbvision->fbuf +
+ i * usbvision->max_frame_size;
+ /*
+ * Set default sizes for read operation.
+ */
+ usbvision->stretch_width = 1;
+ usbvision->stretch_height = 1;
+ usbvision->frame[i].width = usbvision->curwidth;
+ usbvision->frame[i].height = usbvision->curheight;
+ usbvision->frame[i].bytes_read = 0;
+ }
+ PDEBUG(DBG_FUNC, "allocated %d frames (%d bytes per frame)",
+ usbvision->num_frames, usbvision->max_frame_size);
+ return usbvision->num_frames;
+}
+
+/*
+ * usbvision_frames_free
+ * frees memory allocated for the frames
+ */
+void usbvision_frames_free(struct usb_usbvision *usbvision)
+{
+ /* Have to free all that memory */
+ PDEBUG(DBG_FUNC, "free %d frames", usbvision->num_frames);
+
+ if (usbvision->fbuf != NULL) {
+ usbvision_rvfree(usbvision->fbuf, usbvision->fbuf_size);
+ usbvision->fbuf = NULL;
+
+ usbvision->num_frames = 0;
+ }
+}
+/*
+ * usbvision_empty_framequeues()
+ * prepare queues for incoming and outgoing frames
+ */
+void usbvision_empty_framequeues(struct usb_usbvision *usbvision)
+{
+ u32 i;
+
+ INIT_LIST_HEAD(&(usbvision->inqueue));
+ INIT_LIST_HEAD(&(usbvision->outqueue));
+
+ for (i = 0; i < USBVISION_NUMFRAMES; i++) {
+ usbvision->frame[i].grabstate = frame_state_unused;
+ usbvision->frame[i].bytes_read = 0;
+ }
+}
+
+/*
+ * usbvision_stream_interrupt()
+ * stops streaming
+ */
+int usbvision_stream_interrupt(struct usb_usbvision *usbvision)
+{
+ int ret = 0;
+
+ /* stop reading from the device */
+
+ usbvision->streaming = stream_interrupt;
+ ret = wait_event_timeout(usbvision->wait_stream,
+ (usbvision->streaming == stream_idle),
+ msecs_to_jiffies(USBVISION_NUMSBUF*USBVISION_URB_FRAMES));
+ return ret;
+}
+
+/*
+ * usbvision_set_compress_params()
+ *
+ */
+
+static int usbvision_set_compress_params(struct usb_usbvision *usbvision)
+{
+ static const char proc[] = "usbvision_set_compresion_params: ";
+ int rc;
+ unsigned char value[6];
+
+ value[0] = 0x0F; /* Intra-Compression cycle */
+ value[1] = 0x01; /* Reg.45 one line per strip */
+ value[2] = 0x00; /* Reg.46 Force intra mode on all new frames */
+ value[3] = 0x00; /* Reg.47 FORCE_UP <- 0 normal operation (not force) */
+ value[4] = 0xA2; /* Reg.48 BUF_THR I'm not sure if this does something in not compressed mode. */
+ value[5] = 0x00; /* Reg.49 DVI_YUV This has nothing to do with compression */
+
+ /* catched values for NT1004 */
+ /* value[0] = 0xFF; Never apply intra mode automatically */
+ /* value[1] = 0xF1; Use full frame height for virtual strip width; One line per strip */
+ /* value[2] = 0x01; Force intra mode on all new frames */
+ /* value[3] = 0x00; Strip size 400 Bytes; do not force up */
+ /* value[4] = 0xA2; */
+ if (!USBVISION_IS_OPERATIONAL(usbvision))
+ return 0;
+
+ rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
+ USBVISION_OP_CODE,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_ENDPOINT, 0,
+ (__u16) USBVISION_INTRA_CYC, value, 5, HZ);
+
+ if (rc < 0) {
+ printk(KERN_ERR "%sERROR=%d. USBVISION stopped - "
+ "reconnect or reload driver.\n", proc, rc);
+ return rc;
+ }
+
+ if (usbvision->bridge_type == BRIDGE_NT1004) {
+ value[0] = 20; /* PCM Threshold 1 */
+ value[1] = 12; /* PCM Threshold 2 */
+ value[2] = 255; /* Distortion Threshold inter */
+ value[3] = 255; /* Distortion Threshold intra */
+ value[4] = 43; /* Max Distortion inter */
+ value[5] = 43; /* Max Distortion intra */
+ } else {
+ value[0] = 20; /* PCM Threshold 1 */
+ value[1] = 12; /* PCM Threshold 2 */
+ value[2] = 255; /* Distortion Threshold d7-d0 */
+ value[3] = 0; /* Distortion Threshold d11-d8 */
+ value[4] = 43; /* Max Distortion d7-d0 */
+ value[5] = 0; /* Max Distortion d8 */
+ }
+
+ if (!USBVISION_IS_OPERATIONAL(usbvision))
+ return 0;
+
+ rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
+ USBVISION_OP_CODE,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_ENDPOINT, 0,
+ (__u16) USBVISION_PCM_THR1, value, 6, HZ);
+
+ if (rc < 0) {
+ printk(KERN_ERR "%sERROR=%d. USBVISION stopped - "
+ "reconnect or reload driver.\n", proc, rc);
+ }
+ return rc;
+}
+
+
+/*
+ * usbvision_set_input()
+ *
+ * Set the input (saa711x, ...) size x y and other misc input params
+ * I've no idea if this parameters are right
+ *
+ */
+int usbvision_set_input(struct usb_usbvision *usbvision)
+{
+ static const char proc[] = "usbvision_set_input: ";
+ int rc;
+ unsigned char value[8];
+ unsigned char dvi_yuv_value;
+
+ if (!USBVISION_IS_OPERATIONAL(usbvision))
+ return 0;
+
+ /* Set input format expected from decoder*/
+ if (usbvision_device_data[usbvision->dev_model].vin_reg1_override) {
+ value[0] = usbvision_device_data[usbvision->dev_model].vin_reg1;
+ } else if (usbvision_device_data[usbvision->dev_model].codec == CODEC_SAA7113) {
+ /* SAA7113 uses 8 bit output */
+ value[0] = USBVISION_8_422_SYNC;
+ } else {
+ /* I'm sure only about d2-d0 [010] 16 bit 4:2:2 usin sync pulses
+ * as that is how saa7111 is configured */
+ value[0] = USBVISION_16_422_SYNC;
+ /* | USBVISION_VSNC_POL | USBVISION_VCLK_POL);*/
+ }
+
+ rc = usbvision_write_reg(usbvision, USBVISION_VIN_REG1, value[0]);
+ if (rc < 0) {
+ printk(KERN_ERR "%sERROR=%d. USBVISION stopped - "
+ "reconnect or reload driver.\n", proc, rc);
+ return rc;
+ }
+
+
+ if (usbvision->tvnorm_id & V4L2_STD_PAL) {
+ value[0] = 0xC0;
+ value[1] = 0x02; /* 0x02C0 -> 704 Input video line length */
+ value[2] = 0x20;
+ value[3] = 0x01; /* 0x0120 -> 288 Input video n. of lines */
+ value[4] = 0x60;
+ value[5] = 0x00; /* 0x0060 -> 96 Input video h offset */
+ value[6] = 0x16;
+ value[7] = 0x00; /* 0x0016 -> 22 Input video v offset */
+ } else if (usbvision->tvnorm_id & V4L2_STD_SECAM) {
+ value[0] = 0xC0;
+ value[1] = 0x02; /* 0x02C0 -> 704 Input video line length */
+ value[2] = 0x20;
+ value[3] = 0x01; /* 0x0120 -> 288 Input video n. of lines */
+ value[4] = 0x01;
+ value[5] = 0x00; /* 0x0001 -> 01 Input video h offset */
+ value[6] = 0x01;
+ value[7] = 0x00; /* 0x0001 -> 01 Input video v offset */
+ } else { /* V4L2_STD_NTSC */
+ value[0] = 0xD0;
+ value[1] = 0x02; /* 0x02D0 -> 720 Input video line length */
+ value[2] = 0xF0;
+ value[3] = 0x00; /* 0x00F0 -> 240 Input video number of lines */
+ value[4] = 0x50;
+ value[5] = 0x00; /* 0x0050 -> 80 Input video h offset */
+ value[6] = 0x10;
+ value[7] = 0x00; /* 0x0010 -> 16 Input video v offset */
+ }
+
+ /* webcam is only 480 pixels wide, both PAL and NTSC version */
+ if (usbvision_device_data[usbvision->dev_model].codec == CODEC_WEBCAM) {
+ value[0] = 0xe0;
+ value[1] = 0x01; /* 0x01E0 -> 480 Input video line length */
+ }
+
+ if (usbvision_device_data[usbvision->dev_model].x_offset >= 0) {
+ value[4] = usbvision_device_data[usbvision->dev_model].x_offset & 0xff;
+ value[5] = (usbvision_device_data[usbvision->dev_model].x_offset & 0x0300) >> 8;
+ }
+
+ if (adjust_x_offset != -1) {
+ value[4] = adjust_x_offset & 0xff;
+ value[5] = (adjust_x_offset & 0x0300) >> 8;
+ }
+
+ if (usbvision_device_data[usbvision->dev_model].y_offset >= 0) {
+ value[6] = usbvision_device_data[usbvision->dev_model].y_offset & 0xff;
+ value[7] = (usbvision_device_data[usbvision->dev_model].y_offset & 0x0300) >> 8;
+ }
+
+ if (adjust_y_offset != -1) {
+ value[6] = adjust_y_offset & 0xff;
+ value[7] = (adjust_y_offset & 0x0300) >> 8;
+ }
+
+ rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
+ USBVISION_OP_CODE, /* USBVISION specific code */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, 0,
+ (__u16) USBVISION_LXSIZE_I, value, 8, HZ);
+ if (rc < 0) {
+ printk(KERN_ERR "%sERROR=%d. USBVISION stopped - "
+ "reconnect or reload driver.\n", proc, rc);
+ return rc;
+ }
+
+
+ dvi_yuv_value = 0x00; /* U comes after V, Ya comes after U/V, Yb comes after Yb */
+
+ if (usbvision_device_data[usbvision->dev_model].dvi_yuv_override) {
+ dvi_yuv_value = usbvision_device_data[usbvision->dev_model].dvi_yuv;
+ } else if (usbvision_device_data[usbvision->dev_model].codec == CODEC_SAA7113) {
+ /* This changes as the fine sync control changes. Further investigation necessary */
+ dvi_yuv_value = 0x06;
+ }
+
+ return usbvision_write_reg(usbvision, USBVISION_DVI_YUV, dvi_yuv_value);
+}
+
+
+/*
+ * usbvision_set_dram_settings()
+ *
+ * Set the buffer address needed by the usbvision dram to operate
+ * This values has been taken with usbsnoop.
+ *
+ */
+
+static int usbvision_set_dram_settings(struct usb_usbvision *usbvision)
+{
+ int rc;
+ unsigned char value[8];
+
+ if (usbvision->isoc_mode == ISOC_MODE_COMPRESS) {
+ value[0] = 0x42;
+ value[1] = 0x71;
+ value[2] = 0xff;
+ value[3] = 0x00;
+ value[4] = 0x98;
+ value[5] = 0xe0;
+ value[6] = 0x71;
+ value[7] = 0xff;
+ /* UR: 0x0E200-0x3FFFF = 204288 Words (1 Word = 2 Byte) */
+ /* FDL: 0x00000-0x0E099 = 57498 Words */
+ /* VDW: 0x0E3FF-0x3FFFF */
+ } else {
+ value[0] = 0x42;
+ value[1] = 0x00;
+ value[2] = 0xff;
+ value[3] = 0x00;
+ value[4] = 0x00;
+ value[5] = 0x00;
+ value[6] = 0x00;
+ value[7] = 0xff;
+ }
+ /* These are the values of the address of the video buffer,
+ * they have to be loaded into the USBVISION_DRM_PRM1-8
+ *
+ * Start address of video output buffer for read: drm_prm1-2 -> 0x00000
+ * End address of video output buffer for read: drm_prm1-3 -> 0x1ffff
+ * Start address of video frame delay buffer: drm_prm1-4 -> 0x20000
+ * Only used in compressed mode
+ * End address of video frame delay buffer: drm_prm1-5-6 -> 0x3ffff
+ * Only used in compressed mode
+ * Start address of video output buffer for write: drm_prm1-7 -> 0x00000
+ * End address of video output buffer for write: drm_prm1-8 -> 0x1ffff
+ */
+
+ if (!USBVISION_IS_OPERATIONAL(usbvision))
+ return 0;
+
+ rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
+ USBVISION_OP_CODE, /* USBVISION specific code */
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_ENDPOINT, 0,
+ (__u16) USBVISION_DRM_PRM1, value, 8, HZ);
+
+ if (rc < 0) {
+ dev_err(&usbvision->dev->dev, "%s: ERROR=%d\n", __func__, rc);
+ return rc;
+ }
+
+ /* Restart the video buffer logic */
+ rc = usbvision_write_reg(usbvision, USBVISION_DRM_CONT, USBVISION_RES_UR |
+ USBVISION_RES_FDL | USBVISION_RES_VDW);
+ if (rc < 0)
+ return rc;
+ rc = usbvision_write_reg(usbvision, USBVISION_DRM_CONT, 0x00);
+
+ return rc;
+}
+
+/*
+ * ()
+ *
+ * Power on the device, enables suspend-resume logic
+ * & reset the isoc End-Point
+ *
+ */
+
+int usbvision_power_on(struct usb_usbvision *usbvision)
+{
+ int err_code = 0;
+
+ PDEBUG(DBG_FUNC, "");
+
+ usbvision_write_reg(usbvision, USBVISION_PWR_REG, USBVISION_SSPND_EN);
+ usbvision_write_reg(usbvision, USBVISION_PWR_REG,
+ USBVISION_SSPND_EN | USBVISION_RES2);
+
+ if (usbvision_device_data[usbvision->dev_model].codec == CODEC_WEBCAM) {
+ usbvision_write_reg(usbvision, USBVISION_VIN_REG1,
+ USBVISION_16_422_SYNC | USBVISION_HVALID_PO);
+ usbvision_write_reg(usbvision, USBVISION_VIN_REG2,
+ USBVISION_NOHVALID | USBVISION_KEEP_BLANK);
+ }
+ usbvision_write_reg(usbvision, USBVISION_PWR_REG,
+ USBVISION_SSPND_EN | USBVISION_PWR_VID);
+ mdelay(10);
+ err_code = usbvision_write_reg(usbvision, USBVISION_PWR_REG,
+ USBVISION_SSPND_EN | USBVISION_PWR_VID | USBVISION_RES2);
+ if (err_code == 1)
+ usbvision->power = 1;
+ PDEBUG(DBG_FUNC, "%s: err_code %d", (err_code < 0) ? "ERROR" : "power is on", err_code);
+ return err_code;
+}
+
+
+/*
+ * usbvision timer stuff
+ */
+
+/* to call usbvision_power_off from task queue */
+static void call_usbvision_power_off(struct work_struct *work)
+{
+ struct usb_usbvision *usbvision = container_of(work, struct usb_usbvision, power_off_work);
+
+ PDEBUG(DBG_FUNC, "");
+ if (mutex_lock_interruptible(&usbvision->v4l2_lock))
+ return;
+
+ if (usbvision->user == 0) {
+ usbvision_i2c_unregister(usbvision);
+
+ usbvision_power_off(usbvision);
+ usbvision->initialized = 0;
+ }
+ mutex_unlock(&usbvision->v4l2_lock);
+}
+
+static void usbvision_power_off_timer(unsigned long data)
+{
+ struct usb_usbvision *usbvision = (void *)data;
+
+ PDEBUG(DBG_FUNC, "");
+ del_timer(&usbvision->power_off_timer);
+ INIT_WORK(&usbvision->power_off_work, call_usbvision_power_off);
+ (void) schedule_work(&usbvision->power_off_work);
+}
+
+void usbvision_init_power_off_timer(struct usb_usbvision *usbvision)
+{
+ init_timer(&usbvision->power_off_timer);
+ usbvision->power_off_timer.data = (long)usbvision;
+ usbvision->power_off_timer.function = usbvision_power_off_timer;
+}
+
+void usbvision_set_power_off_timer(struct usb_usbvision *usbvision)
+{
+ mod_timer(&usbvision->power_off_timer, jiffies + USBVISION_POWEROFF_TIME);
+}
+
+void usbvision_reset_power_off_timer(struct usb_usbvision *usbvision)
+{
+ if (timer_pending(&usbvision->power_off_timer))
+ del_timer(&usbvision->power_off_timer);
+}
+
+/*
+ * usbvision_begin_streaming()
+ * Sure you have to put bit 7 to 0, if not incoming frames are droped, but no
+ * idea about the rest
+ */
+int usbvision_begin_streaming(struct usb_usbvision *usbvision)
+{
+ if (usbvision->isoc_mode == ISOC_MODE_COMPRESS)
+ usbvision_init_compression(usbvision);
+ return usbvision_write_reg(usbvision, USBVISION_VIN_REG2,
+ USBVISION_NOHVALID | usbvision->vin_reg2_preset);
+}
+
+/*
+ * usbvision_restart_isoc()
+ * Not sure yet if touching here PWR_REG make loose the config
+ */
+
+int usbvision_restart_isoc(struct usb_usbvision *usbvision)
+{
+ int ret;
+
+ ret = usbvision_write_reg(usbvision, USBVISION_PWR_REG,
+ USBVISION_SSPND_EN | USBVISION_PWR_VID);
+ if (ret < 0)
+ return ret;
+ ret = usbvision_write_reg(usbvision, USBVISION_PWR_REG,
+ USBVISION_SSPND_EN | USBVISION_PWR_VID |
+ USBVISION_RES2);
+ if (ret < 0)
+ return ret;
+ ret = usbvision_write_reg(usbvision, USBVISION_VIN_REG2,
+ USBVISION_KEEP_BLANK | USBVISION_NOHVALID |
+ usbvision->vin_reg2_preset);
+ if (ret < 0)
+ return ret;
+
+ /* TODO: schedule timeout */
+ while ((usbvision_read_reg(usbvision, USBVISION_STATUS_REG) & 0x01) != 1)
+ ;
+
+ return 0;
+}
+
+int usbvision_audio_off(struct usb_usbvision *usbvision)
+{
+ if (usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, USBVISION_AUDIO_MUTE) < 0) {
+ printk(KERN_ERR "usbvision_audio_off: can't write reg\n");
+ return -1;
+ }
+ usbvision->audio_mute = 0;
+ usbvision->audio_channel = USBVISION_AUDIO_MUTE;
+ return 0;
+}
+
+int usbvision_set_audio(struct usb_usbvision *usbvision, int audio_channel)
+{
+ if (!usbvision->audio_mute) {
+ if (usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, audio_channel) < 0) {
+ printk(KERN_ERR "usbvision_set_audio: can't write iopin register for audio switching\n");
+ return -1;
+ }
+ }
+ usbvision->audio_channel = audio_channel;
+ return 0;
+}
+
+int usbvision_setup(struct usb_usbvision *usbvision, int format)
+{
+ if (usbvision_device_data[usbvision->dev_model].codec == CODEC_WEBCAM)
+ usbvision_init_webcam(usbvision);
+ usbvision_set_video_format(usbvision, format);
+ usbvision_set_dram_settings(usbvision);
+ usbvision_set_compress_params(usbvision);
+ usbvision_set_input(usbvision);
+ usbvision_set_output(usbvision, MAX_USB_WIDTH, MAX_USB_HEIGHT);
+ usbvision_restart_isoc(usbvision);
+
+ /* cosas del PCM */
+ return USBVISION_IS_OPERATIONAL(usbvision);
+}
+
+int usbvision_set_alternate(struct usb_usbvision *dev)
+{
+ int err_code, prev_alt = dev->iface_alt;
+ int i;
+
+ dev->iface_alt = 0;
+ for (i = 0; i < dev->num_alt; i++)
+ if (dev->alt_max_pkt_size[i] > dev->alt_max_pkt_size[dev->iface_alt])
+ dev->iface_alt = i;
+
+ if (dev->iface_alt != prev_alt) {
+ dev->isoc_packet_size = dev->alt_max_pkt_size[dev->iface_alt];
+ PDEBUG(DBG_FUNC, "setting alternate %d with max_packet_size=%u",
+ dev->iface_alt, dev->isoc_packet_size);
+ err_code = usb_set_interface(dev->dev, dev->iface, dev->iface_alt);
+ if (err_code < 0) {
+ dev_err(&dev->dev->dev,
+ "cannot change alternate number to %d (error=%i)\n",
+ dev->iface_alt, err_code);
+ return err_code;
+ }
+ }
+
+ PDEBUG(DBG_ISOC, "ISO Packet Length:%d", dev->isoc_packet_size);
+
+ return 0;
+}
+
+/*
+ * usbvision_init_isoc()
+ *
+ */
+int usbvision_init_isoc(struct usb_usbvision *usbvision)
+{
+ struct usb_device *dev = usbvision->dev;
+ int buf_idx, err_code, reg_value;
+ int sb_size;
+
+ if (!USBVISION_IS_OPERATIONAL(usbvision))
+ return -EFAULT;
+
+ usbvision->cur_frame = NULL;
+ scratch_reset(usbvision);
+
+ /* Alternate interface 1 is is the biggest frame size */
+ err_code = usbvision_set_alternate(usbvision);
+ if (err_code < 0) {
+ usbvision->last_error = err_code;
+ return -EBUSY;
+ }
+ sb_size = USBVISION_URB_FRAMES * usbvision->isoc_packet_size;
+
+ reg_value = (16 - usbvision_read_reg(usbvision,
+ USBVISION_ALTER_REG)) & 0x0F;
+
+ usbvision->usb_bandwidth = reg_value >> 1;
+ PDEBUG(DBG_ISOC, "USB Bandwidth Usage: %dMbit/Sec",
+ usbvision->usb_bandwidth);
+
+
+
+ /* We double buffer the Iso lists */
+
+ for (buf_idx = 0; buf_idx < USBVISION_NUMSBUF; buf_idx++) {
+ int j, k;
+ struct urb *urb;
+
+ urb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL);
+ if (urb == NULL) {
+ dev_err(&usbvision->dev->dev,
+ "%s: usb_alloc_urb() failed\n", __func__);
+ return -ENOMEM;
+ }
+ usbvision->sbuf[buf_idx].urb = urb;
+ usbvision->sbuf[buf_idx].data =
+ usb_alloc_coherent(usbvision->dev,
+ sb_size,
+ GFP_KERNEL,
+ &urb->transfer_dma);
+ urb->dev = dev;
+ urb->context = usbvision;
+ urb->pipe = usb_rcvisocpipe(dev, usbvision->video_endp);
+ urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+ urb->interval = 1;
+ urb->transfer_buffer = usbvision->sbuf[buf_idx].data;
+ urb->complete = usbvision_isoc_irq;
+ urb->number_of_packets = USBVISION_URB_FRAMES;
+ urb->transfer_buffer_length =
+ usbvision->isoc_packet_size * USBVISION_URB_FRAMES;
+ for (j = k = 0; j < USBVISION_URB_FRAMES; j++,
+ k += usbvision->isoc_packet_size) {
+ urb->iso_frame_desc[j].offset = k;
+ urb->iso_frame_desc[j].length =
+ usbvision->isoc_packet_size;
+ }
+ }
+
+ /* Submit all URBs */
+ for (buf_idx = 0; buf_idx < USBVISION_NUMSBUF; buf_idx++) {
+ err_code = usb_submit_urb(usbvision->sbuf[buf_idx].urb,
+ GFP_KERNEL);
+ if (err_code) {
+ dev_err(&usbvision->dev->dev,
+ "%s: usb_submit_urb(%d) failed: error %d\n",
+ __func__, buf_idx, err_code);
+ }
+ }
+
+ usbvision->streaming = stream_idle;
+ PDEBUG(DBG_ISOC, "%s: streaming=1 usbvision->video_endp=$%02x",
+ __func__,
+ usbvision->video_endp);
+ return 0;
+}
+
+/*
+ * usbvision_stop_isoc()
+ *
+ * This procedure stops streaming and deallocates URBs. Then it
+ * activates zero-bandwidth alt. setting of the video interface.
+ *
+ */
+void usbvision_stop_isoc(struct usb_usbvision *usbvision)
+{
+ int buf_idx, err_code, reg_value;
+ int sb_size = USBVISION_URB_FRAMES * usbvision->isoc_packet_size;
+
+ if ((usbvision->streaming == stream_off) || (usbvision->dev == NULL))
+ return;
+
+ /* Unschedule all of the iso td's */
+ for (buf_idx = 0; buf_idx < USBVISION_NUMSBUF; buf_idx++) {
+ usb_kill_urb(usbvision->sbuf[buf_idx].urb);
+ if (usbvision->sbuf[buf_idx].data) {
+ usb_free_coherent(usbvision->dev,
+ sb_size,
+ usbvision->sbuf[buf_idx].data,
+ usbvision->sbuf[buf_idx].urb->transfer_dma);
+ }
+ usb_free_urb(usbvision->sbuf[buf_idx].urb);
+ usbvision->sbuf[buf_idx].urb = NULL;
+ }
+
+ PDEBUG(DBG_ISOC, "%s: streaming=stream_off\n", __func__);
+ usbvision->streaming = stream_off;
+
+ if (!usbvision->remove_pending) {
+ /* Set packet size to 0 */
+ usbvision->iface_alt = 0;
+ err_code = usb_set_interface(usbvision->dev, usbvision->iface,
+ usbvision->iface_alt);
+ if (err_code < 0) {
+ dev_err(&usbvision->dev->dev,
+ "%s: usb_set_interface() failed: error %d\n",
+ __func__, err_code);
+ usbvision->last_error = err_code;
+ }
+ reg_value = (16-usbvision_read_reg(usbvision, USBVISION_ALTER_REG)) & 0x0F;
+ usbvision->isoc_packet_size =
+ (reg_value == 0) ? 0 : (reg_value * 64) - 1;
+ PDEBUG(DBG_ISOC, "ISO Packet Length:%d",
+ usbvision->isoc_packet_size);
+
+ usbvision->usb_bandwidth = reg_value >> 1;
+ PDEBUG(DBG_ISOC, "USB Bandwidth Usage: %dMbit/Sec",
+ usbvision->usb_bandwidth);
+ }
+}
+
+int usbvision_muxsel(struct usb_usbvision *usbvision, int channel)
+{
+ /* inputs #0 and #3 are constant for every SAA711x. */
+ /* inputs #1 and #2 are variable for SAA7111 and SAA7113 */
+ int mode[4] = { SAA7115_COMPOSITE0, 0, 0, SAA7115_COMPOSITE3 };
+ int audio[] = { 1, 0, 0, 0 };
+ /* channel 0 is TV with audiochannel 1 (tuner mono) */
+ /* channel 1 is Composite with audio channel 0 (line in) */
+ /* channel 2 is S-Video with audio channel 0 (line in) */
+ /* channel 3 is additional video inputs to the device with audio channel 0 (line in) */
+
+ RESTRICT_TO_RANGE(channel, 0, usbvision->video_inputs);
+ usbvision->ctl_input = channel;
+
+ /* set the new channel */
+ /* Regular USB TV Tuners -> channel: 0 = Television, 1 = Composite, 2 = S-Video */
+ /* Four video input devices -> channel: 0 = Chan White, 1 = Chan Green, 2 = Chan Yellow, 3 = Chan Red */
+
+ switch (usbvision_device_data[usbvision->dev_model].codec) {
+ case CODEC_SAA7113:
+ mode[1] = SAA7115_COMPOSITE2;
+ if (switch_svideo_input) {
+ /* To handle problems with S-Video Input for
+ * some devices. Use switch_svideo_input
+ * parameter when loading the module.*/
+ mode[2] = SAA7115_COMPOSITE1;
+ } else {
+ mode[2] = SAA7115_SVIDEO1;
+ }
+ break;
+ case CODEC_SAA7111:
+ default:
+ /* modes for saa7111 */
+ mode[1] = SAA7115_COMPOSITE1;
+ mode[2] = SAA7115_SVIDEO1;
+ break;
+ }
+ call_all(usbvision, video, s_routing, mode[channel], 0, 0);
+ usbvision_set_audio(usbvision, audio[channel]);
+ return 0;
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/usbvision/usbvision-i2c.c b/drivers/media/video/usbvision/usbvision-i2c.c
new file mode 100644
index 00000000..89fec029
--- /dev/null
+++ b/drivers/media/video/usbvision/usbvision-i2c.c
@@ -0,0 +1,456 @@
+/*
+ * usbvision_i2c.c
+ * i2c algorithm for USB-I2C Bridges
+ *
+ * Copyright (c) 1999-2007 Joerg Heckenbach <joerg@heckenbach-aw.de>
+ * Dwaine Garden <dwainegarden@rogers.com>
+ *
+ * This module is part of usbvision driver project.
+ * Updates to driver completed by Dwaine P. Garden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/uaccess.h>
+#include <linux/ioport.h>
+#include <linux/errno.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include "usbvision.h"
+
+#define DBG_I2C (1 << 0)
+
+static int i2c_debug;
+
+module_param(i2c_debug, int, 0644); /* debug_i2c_usb mode of the device driver */
+MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
+
+#define PDEBUG(level, fmt, args...) { \
+ if (i2c_debug & (level)) \
+ printk(KERN_INFO KBUILD_MODNAME ":[%s:%d] " fmt, \
+ __func__, __LINE__ , ## args); \
+ }
+
+static int usbvision_i2c_write(struct usb_usbvision *usbvision, unsigned char addr, char *buf,
+ short len);
+static int usbvision_i2c_read(struct usb_usbvision *usbvision, unsigned char addr, char *buf,
+ short len);
+
+static inline int try_write_address(struct i2c_adapter *i2c_adap,
+ unsigned char addr, int retries)
+{
+ struct usb_usbvision *usbvision;
+ int i, ret = -1;
+ char buf[4];
+
+ usbvision = (struct usb_usbvision *)i2c_get_adapdata(i2c_adap);
+ buf[0] = 0x00;
+ for (i = 0; i <= retries; i++) {
+ ret = (usbvision_i2c_write(usbvision, addr, buf, 1));
+ if (ret == 1)
+ break; /* success! */
+ udelay(5);
+ if (i == retries) /* no success */
+ break;
+ udelay(10);
+ }
+ if (i) {
+ PDEBUG(DBG_I2C, "Needed %d retries for address %#2x", i, addr);
+ PDEBUG(DBG_I2C, "Maybe there's no device at this address");
+ }
+ return ret;
+}
+
+static inline int try_read_address(struct i2c_adapter *i2c_adap,
+ unsigned char addr, int retries)
+{
+ struct usb_usbvision *usbvision;
+ int i, ret = -1;
+ char buf[4];
+
+ usbvision = (struct usb_usbvision *)i2c_get_adapdata(i2c_adap);
+ for (i = 0; i <= retries; i++) {
+ ret = (usbvision_i2c_read(usbvision, addr, buf, 1));
+ if (ret == 1)
+ break; /* success! */
+ udelay(5);
+ if (i == retries) /* no success */
+ break;
+ udelay(10);
+ }
+ if (i) {
+ PDEBUG(DBG_I2C, "Needed %d retries for address %#2x", i, addr);
+ PDEBUG(DBG_I2C, "Maybe there's no device at this address");
+ }
+ return ret;
+}
+
+static inline int usb_find_address(struct i2c_adapter *i2c_adap,
+ struct i2c_msg *msg, int retries,
+ unsigned char *add)
+{
+ unsigned short flags = msg->flags;
+
+ unsigned char addr;
+ int ret;
+
+ addr = (msg->addr << 1);
+ if (flags & I2C_M_RD)
+ addr |= 1;
+
+ add[0] = addr;
+ if (flags & I2C_M_RD)
+ ret = try_read_address(i2c_adap, addr, retries);
+ else
+ ret = try_write_address(i2c_adap, addr, retries);
+
+ if (ret != 1)
+ return -EREMOTEIO;
+
+ return 0;
+}
+
+static int
+usbvision_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num)
+{
+ struct i2c_msg *pmsg;
+ struct usb_usbvision *usbvision;
+ int i, ret;
+ unsigned char addr = 0;
+
+ usbvision = (struct usb_usbvision *)i2c_get_adapdata(i2c_adap);
+
+ for (i = 0; i < num; i++) {
+ pmsg = &msgs[i];
+ ret = usb_find_address(i2c_adap, pmsg, i2c_adap->retries, &addr);
+ if (ret != 0) {
+ PDEBUG(DBG_I2C, "got NAK from device, message #%d", i);
+ return (ret < 0) ? ret : -EREMOTEIO;
+ }
+
+ if (pmsg->flags & I2C_M_RD) {
+ /* read bytes into buffer */
+ ret = (usbvision_i2c_read(usbvision, addr, pmsg->buf, pmsg->len));
+ if (ret < pmsg->len)
+ return (ret < 0) ? ret : -EREMOTEIO;
+ } else {
+ /* write bytes from buffer */
+ ret = (usbvision_i2c_write(usbvision, addr, pmsg->buf, pmsg->len));
+ if (ret < pmsg->len)
+ return (ret < 0) ? ret : -EREMOTEIO;
+ }
+ }
+ return num;
+}
+
+static u32 functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+/* -----exported algorithm data: ------------------------------------- */
+
+static struct i2c_algorithm usbvision_algo = {
+ .master_xfer = usbvision_i2c_xfer,
+ .smbus_xfer = NULL,
+ .functionality = functionality,
+};
+
+
+/* ----------------------------------------------------------------------- */
+/* usbvision specific I2C functions */
+/* ----------------------------------------------------------------------- */
+static struct i2c_adapter i2c_adap_template;
+
+int usbvision_i2c_register(struct usb_usbvision *usbvision)
+{
+ static unsigned short saa711x_addrs[] = {
+ 0x4a >> 1, 0x48 >> 1, /* SAA7111, SAA7111A and SAA7113 */
+ 0x42 >> 1, 0x40 >> 1, /* SAA7114, SAA7115 and SAA7118 */
+ I2C_CLIENT_END };
+
+ if (usbvision->registered_i2c)
+ return 0;
+
+ memcpy(&usbvision->i2c_adap, &i2c_adap_template,
+ sizeof(struct i2c_adapter));
+
+ sprintf(usbvision->i2c_adap.name, "%s-%d-%s", i2c_adap_template.name,
+ usbvision->dev->bus->busnum, usbvision->dev->devpath);
+ PDEBUG(DBG_I2C, "Adaptername: %s", usbvision->i2c_adap.name);
+ usbvision->i2c_adap.dev.parent = &usbvision->dev->dev;
+
+ i2c_set_adapdata(&usbvision->i2c_adap, &usbvision->v4l2_dev);
+
+ if (usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_IIC_LRNACK) < 0) {
+ printk(KERN_ERR "usbvision_i2c_register: can't write reg\n");
+ return -EBUSY;
+ }
+
+ PDEBUG(DBG_I2C, "I2C debugging is enabled [i2c]");
+ PDEBUG(DBG_I2C, "ALGO debugging is enabled [i2c]");
+
+ /* register new adapter to i2c module... */
+
+ usbvision->i2c_adap.algo = &usbvision_algo;
+
+ usbvision->i2c_adap.timeout = 100; /* default values, should */
+ usbvision->i2c_adap.retries = 3; /* be replaced by defines */
+
+ i2c_add_adapter(&usbvision->i2c_adap);
+
+ PDEBUG(DBG_I2C, "i2c bus for %s registered", usbvision->i2c_adap.name);
+
+ /* Request the load of the i2c modules we need */
+ switch (usbvision_device_data[usbvision->dev_model].codec) {
+ case CODEC_SAA7113:
+ case CODEC_SAA7111:
+ /* Without this delay the detection of the saa711x is
+ hit-and-miss. */
+ mdelay(10);
+ v4l2_i2c_new_subdev(&usbvision->v4l2_dev,
+ &usbvision->i2c_adap,
+ "saa7115_auto", 0, saa711x_addrs);
+ break;
+ }
+ if (usbvision_device_data[usbvision->dev_model].tuner == 1) {
+ struct v4l2_subdev *sd;
+ enum v4l2_i2c_tuner_type type;
+ struct tuner_setup tun_setup;
+
+ sd = v4l2_i2c_new_subdev(&usbvision->v4l2_dev,
+ &usbvision->i2c_adap,
+ "tuner", 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
+ /* depending on whether we found a demod or not, select
+ the tuner type. */
+ type = sd ? ADDRS_TV_WITH_DEMOD : ADDRS_TV;
+
+ sd = v4l2_i2c_new_subdev(&usbvision->v4l2_dev,
+ &usbvision->i2c_adap,
+ "tuner", 0, v4l2_i2c_tuner_addrs(type));
+
+ if (sd == NULL)
+ return -ENODEV;
+ if (usbvision->tuner_type != -1) {
+ tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
+ tun_setup.type = usbvision->tuner_type;
+ tun_setup.addr = v4l2_i2c_subdev_addr(sd);
+ call_all(usbvision, tuner, s_type_addr, &tun_setup);
+ }
+ }
+ usbvision->registered_i2c = 1;
+
+ return 0;
+}
+
+int usbvision_i2c_unregister(struct usb_usbvision *usbvision)
+{
+ if (!usbvision->registered_i2c)
+ return 0;
+
+ i2c_del_adapter(&(usbvision->i2c_adap));
+ usbvision->registered_i2c = 0;
+
+ PDEBUG(DBG_I2C, "i2c bus for %s unregistered", usbvision->i2c_adap.name);
+
+ return 0;
+}
+
+static int
+usbvision_i2c_read_max4(struct usb_usbvision *usbvision, unsigned char addr,
+ char *buf, short len)
+{
+ int rc, retries;
+
+ for (retries = 5;;) {
+ rc = usbvision_write_reg(usbvision, USBVISION_SER_ADRS, addr);
+ if (rc < 0)
+ return rc;
+
+ /* Initiate byte read cycle */
+ /* USBVISION_SER_CONT <- d0-d2 n. of bytes to r/w */
+ /* d3 0=Wr 1=Rd */
+ rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT,
+ (len & 0x07) | 0x18);
+ if (rc < 0)
+ return rc;
+
+ /* Test for Busy and ACK */
+ do {
+ /* USBVISION_SER_CONT -> d4 == 0 busy */
+ rc = usbvision_read_reg(usbvision, USBVISION_SER_CONT);
+ } while (rc > 0 && ((rc & 0x10) != 0)); /* Retry while busy */
+ if (rc < 0)
+ return rc;
+
+ /* USBVISION_SER_CONT -> d5 == 1 Not ack */
+ if ((rc & 0x20) == 0) /* Ack? */
+ break;
+
+ /* I2C abort */
+ rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT, 0x00);
+ if (rc < 0)
+ return rc;
+
+ if (--retries < 0)
+ return -1;
+ }
+
+ switch (len) {
+ case 4:
+ buf[3] = usbvision_read_reg(usbvision, USBVISION_SER_DAT4);
+ case 3:
+ buf[2] = usbvision_read_reg(usbvision, USBVISION_SER_DAT3);
+ case 2:
+ buf[1] = usbvision_read_reg(usbvision, USBVISION_SER_DAT2);
+ case 1:
+ buf[0] = usbvision_read_reg(usbvision, USBVISION_SER_DAT1);
+ break;
+ default:
+ printk(KERN_ERR
+ "usbvision_i2c_read_max4: buffer length > 4\n");
+ }
+
+ if (i2c_debug & DBG_I2C) {
+ int idx;
+
+ for (idx = 0; idx < len; idx++)
+ PDEBUG(DBG_I2C, "read %x from address %x", (unsigned char)buf[idx], addr);
+ }
+ return len;
+}
+
+
+static int usbvision_i2c_write_max4(struct usb_usbvision *usbvision,
+ unsigned char addr, const char *buf,
+ short len)
+{
+ int rc, retries;
+ int i;
+ unsigned char value[6];
+ unsigned char ser_cont;
+
+ ser_cont = (len & 0x07) | 0x10;
+
+ value[0] = addr;
+ value[1] = ser_cont;
+ for (i = 0; i < len; i++)
+ value[i + 2] = buf[i];
+
+ for (retries = 5;;) {
+ rc = usb_control_msg(usbvision->dev,
+ usb_sndctrlpipe(usbvision->dev, 1),
+ USBVISION_OP_CODE,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_ENDPOINT, 0,
+ (__u16) USBVISION_SER_ADRS, value,
+ len + 2, HZ);
+
+ if (rc < 0)
+ return rc;
+
+ rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT,
+ (len & 0x07) | 0x10);
+ if (rc < 0)
+ return rc;
+
+ /* Test for Busy and ACK */
+ do {
+ rc = usbvision_read_reg(usbvision, USBVISION_SER_CONT);
+ } while (rc > 0 && ((rc & 0x10) != 0)); /* Retry while busy */
+ if (rc < 0)
+ return rc;
+
+ if ((rc & 0x20) == 0) /* Ack? */
+ break;
+
+ /* I2C abort */
+ usbvision_write_reg(usbvision, USBVISION_SER_CONT, 0x00);
+
+ if (--retries < 0)
+ return -1;
+
+ }
+
+ if (i2c_debug & DBG_I2C) {
+ int idx;
+
+ for (idx = 0; idx < len; idx++)
+ PDEBUG(DBG_I2C, "wrote %x at address %x", (unsigned char)buf[idx], addr);
+ }
+ return len;
+}
+
+static int usbvision_i2c_write(struct usb_usbvision *usbvision, unsigned char addr, char *buf,
+ short len)
+{
+ char *buf_ptr = buf;
+ int retval;
+ int wrcount = 0;
+ int count;
+ int max_len = 4;
+
+ while (len > 0) {
+ count = (len > max_len) ? max_len : len;
+ retval = usbvision_i2c_write_max4(usbvision, addr, buf_ptr, count);
+ if (retval > 0) {
+ len -= count;
+ buf_ptr += count;
+ wrcount += count;
+ } else
+ return (retval < 0) ? retval : -EFAULT;
+ }
+ return wrcount;
+}
+
+static int usbvision_i2c_read(struct usb_usbvision *usbvision, unsigned char addr, char *buf,
+ short len)
+{
+ char temp[4];
+ int retval, i;
+ int rdcount = 0;
+ int count;
+
+ while (len > 0) {
+ count = (len > 3) ? 4 : len;
+ retval = usbvision_i2c_read_max4(usbvision, addr, temp, count);
+ if (retval > 0) {
+ for (i = 0; i < len; i++)
+ buf[rdcount + i] = temp[i];
+ len -= count;
+ rdcount += count;
+ } else
+ return (retval < 0) ? retval : -EFAULT;
+ }
+ return rdcount;
+}
+
+static struct i2c_adapter i2c_adap_template = {
+ .owner = THIS_MODULE,
+ .name = "usbvision",
+};
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c
new file mode 100644
index 00000000..5a74f5e0
--- /dev/null
+++ b/drivers/media/video/usbvision/usbvision-video.c
@@ -0,0 +1,1686 @@
+/*
+ * USB USBVISION Video device driver 0.9.10
+ *
+ *
+ *
+ * Copyright (c) 1999-2005 Joerg Heckenbach <joerg@heckenbach-aw.de>
+ *
+ * This module is part of usbvision driver project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Let's call the version 0.... until compression decoding is completely
+ * implemented.
+ *
+ * This driver is written by Jose Ignacio Gijon and Joerg Heckenbach.
+ * It was based on USB CPiA driver written by Peter Pregler,
+ * Scott J. Bertin and Johannes Erdfelt
+ * Ideas are taken from bttv driver by Ralph Metzler, Marcus Metzler &
+ * Gerd Knorr and zoran 36120/36125 driver by Pauline Middelink
+ * Updates to driver completed by Dwaine P. Garden
+ *
+ *
+ * TODO:
+ * - use submit_urb for all setup packets
+ * - Fix memory settings for nt1004. It is 4 times as big as the
+ * nt1003 memory.
+ * - Add audio on endpoint 3 for nt1004 chip.
+ * Seems impossible, needs a codec interface. Which one?
+ * - Clean up the driver.
+ * - optimization for performance.
+ * - Add Videotext capability (VBI). Working on it.....
+ * - Check audio for other devices
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/timer.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+
+#include <media/saa7115.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/tuner.h>
+
+#include <linux/workqueue.h>
+
+#include "usbvision.h"
+#include "usbvision-cards.h"
+
+#define DRIVER_AUTHOR \
+ "Joerg Heckenbach <joerg@heckenbach-aw.de>, " \
+ "Dwaine Garden <DwaineGarden@rogers.com>"
+#define DRIVER_NAME "usbvision"
+#define DRIVER_ALIAS "USBVision"
+#define DRIVER_DESC "USBVision USB Video Device Driver for Linux"
+#define DRIVER_LICENSE "GPL"
+#define USBVISION_VERSION_STRING "0.9.11"
+
+#define ENABLE_HEXDUMP 0 /* Enable if you need it */
+
+
+#ifdef USBVISION_DEBUG
+ #define PDEBUG(level, fmt, args...) { \
+ if (video_debug & (level)) \
+ printk(KERN_INFO KBUILD_MODNAME ":[%s:%d] " fmt, \
+ __func__, __LINE__ , ## args); \
+ }
+#else
+ #define PDEBUG(level, fmt, args...) do {} while (0)
+#endif
+
+#define DBG_IO (1 << 1)
+#define DBG_PROBE (1 << 2)
+#define DBG_MMAP (1 << 3)
+
+/* String operations */
+#define rmspace(str) while (*str == ' ') str++;
+#define goto2next(str) while (*str != ' ') str++; while (*str == ' ') str++;
+
+
+/* sequential number of usbvision device */
+static int usbvision_nr;
+
+static struct usbvision_v4l2_format_st usbvision_v4l2_format[] = {
+ { 1, 1, 8, V4L2_PIX_FMT_GREY , "GREY" },
+ { 1, 2, 16, V4L2_PIX_FMT_RGB565 , "RGB565" },
+ { 1, 3, 24, V4L2_PIX_FMT_RGB24 , "RGB24" },
+ { 1, 4, 32, V4L2_PIX_FMT_RGB32 , "RGB32" },
+ { 1, 2, 16, V4L2_PIX_FMT_RGB555 , "RGB555" },
+ { 1, 2, 16, V4L2_PIX_FMT_YUYV , "YUV422" },
+ { 1, 2, 12, V4L2_PIX_FMT_YVU420 , "YUV420P" }, /* 1.5 ! */
+ { 1, 2, 16, V4L2_PIX_FMT_YUV422P , "YUV422P" }
+};
+
+/* Function prototypes */
+static void usbvision_release(struct usb_usbvision *usbvision);
+
+/* Default initialization of device driver parameters */
+/* Set the default format for ISOC endpoint */
+static int isoc_mode = ISOC_MODE_COMPRESS;
+/* Set the default Debug Mode of the device driver */
+static int video_debug;
+/* Set the default device to power on at startup */
+static int power_on_at_open = 1;
+/* Sequential Number of Video Device */
+static int video_nr = -1;
+/* Sequential Number of Radio Device */
+static int radio_nr = -1;
+
+/* Grab parameters for the device driver */
+
+/* Showing parameters under SYSFS */
+module_param(isoc_mode, int, 0444);
+module_param(video_debug, int, 0444);
+module_param(power_on_at_open, int, 0444);
+module_param(video_nr, int, 0444);
+module_param(radio_nr, int, 0444);
+
+MODULE_PARM_DESC(isoc_mode, " Set the default format for ISOC endpoint. Default: 0x60 (Compression On)");
+MODULE_PARM_DESC(video_debug, " Set the default Debug Mode of the device driver. Default: 0 (Off)");
+MODULE_PARM_DESC(power_on_at_open, " Set the default device to power on when device is opened. Default: 1 (On)");
+MODULE_PARM_DESC(video_nr, "Set video device number (/dev/videoX). Default: -1 (autodetect)");
+MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX). Default: -1 (autodetect)");
+
+
+/* Misc stuff */
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+MODULE_VERSION(USBVISION_VERSION_STRING);
+MODULE_ALIAS(DRIVER_ALIAS);
+
+
+/*****************************************************************************/
+/* SYSFS Code - Copied from the stv680.c usb module. */
+/* Device information is located at /sys/class/video4linux/video0 */
+/* Device parameters information is located at /sys/module/usbvision */
+/* Device USB Information is located at */
+/* /sys/bus/usb/drivers/USBVision Video Grabber */
+/*****************************************************************************/
+
+#define YES_NO(x) ((x) ? "Yes" : "No")
+
+static inline struct usb_usbvision *cd_to_usbvision(struct device *cd)
+{
+ struct video_device *vdev =
+ container_of(cd, struct video_device, dev);
+ return video_get_drvdata(vdev);
+}
+
+static ssize_t show_version(struct device *cd,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", USBVISION_VERSION_STRING);
+}
+static DEVICE_ATTR(version, S_IRUGO, show_version, NULL);
+
+static ssize_t show_model(struct device *cd,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vdev =
+ container_of(cd, struct video_device, dev);
+ struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+ return sprintf(buf, "%s\n",
+ usbvision_device_data[usbvision->dev_model].model_string);
+}
+static DEVICE_ATTR(model, S_IRUGO, show_model, NULL);
+
+static ssize_t show_hue(struct device *cd,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vdev =
+ container_of(cd, struct video_device, dev);
+ struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+ struct v4l2_control ctrl;
+ ctrl.id = V4L2_CID_HUE;
+ ctrl.value = 0;
+ if (usbvision->user)
+ call_all(usbvision, core, g_ctrl, &ctrl);
+ return sprintf(buf, "%d\n", ctrl.value);
+}
+static DEVICE_ATTR(hue, S_IRUGO, show_hue, NULL);
+
+static ssize_t show_contrast(struct device *cd,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vdev =
+ container_of(cd, struct video_device, dev);
+ struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+ struct v4l2_control ctrl;
+ ctrl.id = V4L2_CID_CONTRAST;
+ ctrl.value = 0;
+ if (usbvision->user)
+ call_all(usbvision, core, g_ctrl, &ctrl);
+ return sprintf(buf, "%d\n", ctrl.value);
+}
+static DEVICE_ATTR(contrast, S_IRUGO, show_contrast, NULL);
+
+static ssize_t show_brightness(struct device *cd,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vdev =
+ container_of(cd, struct video_device, dev);
+ struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+ struct v4l2_control ctrl;
+ ctrl.id = V4L2_CID_BRIGHTNESS;
+ ctrl.value = 0;
+ if (usbvision->user)
+ call_all(usbvision, core, g_ctrl, &ctrl);
+ return sprintf(buf, "%d\n", ctrl.value);
+}
+static DEVICE_ATTR(brightness, S_IRUGO, show_brightness, NULL);
+
+static ssize_t show_saturation(struct device *cd,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vdev =
+ container_of(cd, struct video_device, dev);
+ struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+ struct v4l2_control ctrl;
+ ctrl.id = V4L2_CID_SATURATION;
+ ctrl.value = 0;
+ if (usbvision->user)
+ call_all(usbvision, core, g_ctrl, &ctrl);
+ return sprintf(buf, "%d\n", ctrl.value);
+}
+static DEVICE_ATTR(saturation, S_IRUGO, show_saturation, NULL);
+
+static ssize_t show_streaming(struct device *cd,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vdev =
+ container_of(cd, struct video_device, dev);
+ struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+ return sprintf(buf, "%s\n",
+ YES_NO(usbvision->streaming == stream_on ? 1 : 0));
+}
+static DEVICE_ATTR(streaming, S_IRUGO, show_streaming, NULL);
+
+static ssize_t show_compression(struct device *cd,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vdev =
+ container_of(cd, struct video_device, dev);
+ struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+ return sprintf(buf, "%s\n",
+ YES_NO(usbvision->isoc_mode == ISOC_MODE_COMPRESS));
+}
+static DEVICE_ATTR(compression, S_IRUGO, show_compression, NULL);
+
+static ssize_t show_device_bridge(struct device *cd,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vdev =
+ container_of(cd, struct video_device, dev);
+ struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+ return sprintf(buf, "%d\n", usbvision->bridge_type);
+}
+static DEVICE_ATTR(bridge, S_IRUGO, show_device_bridge, NULL);
+
+static void usbvision_create_sysfs(struct video_device *vdev)
+{
+ int res;
+
+ if (!vdev)
+ return;
+ do {
+ res = device_create_file(&vdev->dev, &dev_attr_version);
+ if (res < 0)
+ break;
+ res = device_create_file(&vdev->dev, &dev_attr_model);
+ if (res < 0)
+ break;
+ res = device_create_file(&vdev->dev, &dev_attr_hue);
+ if (res < 0)
+ break;
+ res = device_create_file(&vdev->dev, &dev_attr_contrast);
+ if (res < 0)
+ break;
+ res = device_create_file(&vdev->dev, &dev_attr_brightness);
+ if (res < 0)
+ break;
+ res = device_create_file(&vdev->dev, &dev_attr_saturation);
+ if (res < 0)
+ break;
+ res = device_create_file(&vdev->dev, &dev_attr_streaming);
+ if (res < 0)
+ break;
+ res = device_create_file(&vdev->dev, &dev_attr_compression);
+ if (res < 0)
+ break;
+ res = device_create_file(&vdev->dev, &dev_attr_bridge);
+ if (res >= 0)
+ return;
+ } while (0);
+
+ dev_err(&vdev->dev, "%s error: %d\n", __func__, res);
+}
+
+static void usbvision_remove_sysfs(struct video_device *vdev)
+{
+ if (vdev) {
+ device_remove_file(&vdev->dev, &dev_attr_version);
+ device_remove_file(&vdev->dev, &dev_attr_model);
+ device_remove_file(&vdev->dev, &dev_attr_hue);
+ device_remove_file(&vdev->dev, &dev_attr_contrast);
+ device_remove_file(&vdev->dev, &dev_attr_brightness);
+ device_remove_file(&vdev->dev, &dev_attr_saturation);
+ device_remove_file(&vdev->dev, &dev_attr_streaming);
+ device_remove_file(&vdev->dev, &dev_attr_compression);
+ device_remove_file(&vdev->dev, &dev_attr_bridge);
+ }
+}
+
+/*
+ * usbvision_open()
+ *
+ * This is part of Video 4 Linux API. The driver can be opened by one
+ * client only (checks internal counter 'usbvision->user'). The procedure
+ * then allocates buffers needed for video processing.
+ *
+ */
+static int usbvision_v4l2_open(struct file *file)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ int err_code = 0;
+
+ PDEBUG(DBG_IO, "open");
+
+ usbvision_reset_power_off_timer(usbvision);
+
+ if (usbvision->user)
+ err_code = -EBUSY;
+ else {
+ /* Allocate memory for the scratch ring buffer */
+ err_code = usbvision_scratch_alloc(usbvision);
+ if (isoc_mode == ISOC_MODE_COMPRESS) {
+ /* Allocate intermediate decompression buffers
+ only if needed */
+ err_code = usbvision_decompress_alloc(usbvision);
+ }
+ if (err_code) {
+ /* Deallocate all buffers if trouble */
+ usbvision_scratch_free(usbvision);
+ usbvision_decompress_free(usbvision);
+ }
+ }
+
+ /* If so far no errors then we shall start the camera */
+ if (!err_code) {
+ if (usbvision->power == 0) {
+ usbvision_power_on(usbvision);
+ usbvision_i2c_register(usbvision);
+ }
+
+ /* Send init sequence only once, it's large! */
+ if (!usbvision->initialized) {
+ int setup_ok = 0;
+ setup_ok = usbvision_setup(usbvision, isoc_mode);
+ if (setup_ok)
+ usbvision->initialized = 1;
+ else
+ err_code = -EBUSY;
+ }
+
+ if (!err_code) {
+ usbvision_begin_streaming(usbvision);
+ err_code = usbvision_init_isoc(usbvision);
+ /* device must be initialized before isoc transfer */
+ usbvision_muxsel(usbvision, 0);
+ usbvision->user++;
+ } else {
+ if (power_on_at_open) {
+ usbvision_i2c_unregister(usbvision);
+ usbvision_power_off(usbvision);
+ usbvision->initialized = 0;
+ }
+ }
+ }
+
+ /* prepare queues */
+ usbvision_empty_framequeues(usbvision);
+
+ PDEBUG(DBG_IO, "success");
+ return err_code;
+}
+
+/*
+ * usbvision_v4l2_close()
+ *
+ * This is part of Video 4 Linux API. The procedure
+ * stops streaming and deallocates all buffers that were earlier
+ * allocated in usbvision_v4l2_open().
+ *
+ */
+static int usbvision_v4l2_close(struct file *file)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ PDEBUG(DBG_IO, "close");
+
+ usbvision_audio_off(usbvision);
+ usbvision_restart_isoc(usbvision);
+ usbvision_stop_isoc(usbvision);
+
+ usbvision_decompress_free(usbvision);
+ usbvision_frames_free(usbvision);
+ usbvision_empty_framequeues(usbvision);
+ usbvision_scratch_free(usbvision);
+
+ usbvision->user--;
+
+ if (power_on_at_open) {
+ /* power off in a little while
+ to avoid off/on every close/open short sequences */
+ usbvision_set_power_off_timer(usbvision);
+ usbvision->initialized = 0;
+ }
+
+ if (usbvision->remove_pending) {
+ printk(KERN_INFO "%s: Final disconnect\n", __func__);
+ usbvision_release(usbvision);
+ }
+
+ PDEBUG(DBG_IO, "success");
+ return 0;
+}
+
+
+/*
+ * usbvision_ioctl()
+ *
+ * This is part of Video 4 Linux API. The procedure handles ioctl() calls.
+ *
+ */
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vidioc_g_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ int err_code;
+
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+ /* NT100x has a 8-bit register space */
+ err_code = usbvision_read_reg(usbvision, reg->reg&0xff);
+ if (err_code < 0) {
+ dev_err(&usbvision->vdev->dev,
+ "%s: VIDIOC_DBG_G_REGISTER failed: error %d\n",
+ __func__, err_code);
+ return err_code;
+ }
+ reg->val = err_code;
+ reg->size = 1;
+ return 0;
+}
+
+static int vidioc_s_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ int err_code;
+
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+ /* NT100x has a 8-bit register space */
+ err_code = usbvision_write_reg(usbvision, reg->reg & 0xff, reg->val);
+ if (err_code < 0) {
+ dev_err(&usbvision->vdev->dev,
+ "%s: VIDIOC_DBG_S_REGISTER failed: error %d\n",
+ __func__, err_code);
+ return err_code;
+ }
+ return 0;
+}
+#endif
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *vc)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ strlcpy(vc->driver, "USBVision", sizeof(vc->driver));
+ strlcpy(vc->card,
+ usbvision_device_data[usbvision->dev_model].model_string,
+ sizeof(vc->card));
+ usb_make_path(usbvision->dev, vc->bus_info, sizeof(vc->bus_info));
+ vc->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_AUDIO |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING |
+ (usbvision->have_tuner ? V4L2_CAP_TUNER : 0);
+ return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *vi)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ int chan;
+
+ if (vi->index >= usbvision->video_inputs)
+ return -EINVAL;
+ if (usbvision->have_tuner)
+ chan = vi->index;
+ else
+ chan = vi->index + 1; /* skip Television string*/
+
+ /* Determine the requested input characteristics
+ specific for each usbvision card model */
+ switch (chan) {
+ case 0:
+ if (usbvision_device_data[usbvision->dev_model].video_channels == 4) {
+ strcpy(vi->name, "White Video Input");
+ } else {
+ strcpy(vi->name, "Television");
+ vi->type = V4L2_INPUT_TYPE_TUNER;
+ vi->audioset = 1;
+ vi->tuner = chan;
+ vi->std = USBVISION_NORMS;
+ }
+ break;
+ case 1:
+ vi->type = V4L2_INPUT_TYPE_CAMERA;
+ if (usbvision_device_data[usbvision->dev_model].video_channels == 4)
+ strcpy(vi->name, "Green Video Input");
+ else
+ strcpy(vi->name, "Composite Video Input");
+ vi->std = V4L2_STD_PAL;
+ break;
+ case 2:
+ vi->type = V4L2_INPUT_TYPE_CAMERA;
+ if (usbvision_device_data[usbvision->dev_model].video_channels == 4)
+ strcpy(vi->name, "Yellow Video Input");
+ else
+ strcpy(vi->name, "S-Video Input");
+ vi->std = V4L2_STD_PAL;
+ break;
+ case 3:
+ vi->type = V4L2_INPUT_TYPE_CAMERA;
+ strcpy(vi->name, "Red Video Input");
+ vi->std = V4L2_STD_PAL;
+ break;
+ }
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *input)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ *input = usbvision->ctl_input;
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int input)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ if (input >= usbvision->video_inputs)
+ return -EINVAL;
+
+ usbvision_muxsel(usbvision, input);
+ usbvision_set_input(usbvision);
+ usbvision_set_output(usbvision,
+ usbvision->curwidth,
+ usbvision->curheight);
+ return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ usbvision->tvnorm_id = *id;
+
+ call_all(usbvision, core, s_std, usbvision->tvnorm_id);
+ /* propagate the change to the decoder */
+ usbvision_muxsel(usbvision, usbvision->ctl_input);
+
+ return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *vt)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ if (!usbvision->have_tuner || vt->index) /* Only tuner 0 */
+ return -EINVAL;
+ if (usbvision->radio) {
+ strcpy(vt->name, "Radio");
+ vt->type = V4L2_TUNER_RADIO;
+ } else {
+ strcpy(vt->name, "Television");
+ }
+ /* Let clients fill in the remainder of this struct */
+ call_all(usbvision, tuner, g_tuner, vt);
+
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *vt)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ /* Only no or one tuner for now */
+ if (!usbvision->have_tuner || vt->index)
+ return -EINVAL;
+ /* let clients handle this */
+ call_all(usbvision, tuner, s_tuner, vt);
+
+ return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *freq)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ freq->tuner = 0; /* Only one tuner */
+ if (usbvision->radio)
+ freq->type = V4L2_TUNER_RADIO;
+ else
+ freq->type = V4L2_TUNER_ANALOG_TV;
+ freq->frequency = usbvision->freq;
+
+ return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *freq)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ /* Only no or one tuner for now */
+ if (!usbvision->have_tuner || freq->tuner)
+ return -EINVAL;
+
+ usbvision->freq = freq->frequency;
+ call_all(usbvision, tuner, s_frequency, freq);
+
+ return 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ if (usbvision->radio)
+ strcpy(a->name, "Radio");
+ else
+ strcpy(a->name, "TV");
+
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *fh,
+ struct v4l2_audio *a)
+{
+ if (a->index)
+ return -EINVAL;
+ return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *ctrl)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ call_all(usbvision, core, queryctrl, ctrl);
+
+ if (!ctrl->type)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ call_all(usbvision, core, g_ctrl, ctrl);
+ return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ call_all(usbvision, core, s_ctrl, ctrl);
+ return 0;
+}
+
+static int vidioc_reqbufs(struct file *file,
+ void *priv, struct v4l2_requestbuffers *vr)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ int ret;
+
+ RESTRICT_TO_RANGE(vr->count, 1, USBVISION_NUMFRAMES);
+
+ /* Check input validity:
+ the user must do a VIDEO CAPTURE and MMAP method. */
+ if (vr->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+ if (usbvision->streaming == stream_on) {
+ ret = usbvision_stream_interrupt(usbvision);
+ if (ret)
+ return ret;
+ }
+
+ usbvision_frames_free(usbvision);
+ usbvision_empty_framequeues(usbvision);
+ vr->count = usbvision_frames_alloc(usbvision, vr->count);
+
+ usbvision->cur_frame = NULL;
+
+ return 0;
+}
+
+static int vidioc_querybuf(struct file *file,
+ void *priv, struct v4l2_buffer *vb)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ struct usbvision_frame *frame;
+
+ /* FIXME : must control
+ that buffers are mapped (VIDIOC_REQBUFS has been called) */
+ if (vb->index >= usbvision->num_frames)
+ return -EINVAL;
+ /* Updating the corresponding frame state */
+ vb->flags = 0;
+ frame = &usbvision->frame[vb->index];
+ if (frame->grabstate >= frame_state_ready)
+ vb->flags |= V4L2_BUF_FLAG_QUEUED;
+ if (frame->grabstate >= frame_state_done)
+ vb->flags |= V4L2_BUF_FLAG_DONE;
+ if (frame->grabstate == frame_state_unused)
+ vb->flags |= V4L2_BUF_FLAG_MAPPED;
+ vb->memory = V4L2_MEMORY_MMAP;
+
+ vb->m.offset = vb->index * PAGE_ALIGN(usbvision->max_frame_size);
+
+ vb->memory = V4L2_MEMORY_MMAP;
+ vb->field = V4L2_FIELD_NONE;
+ vb->length = usbvision->curwidth *
+ usbvision->curheight *
+ usbvision->palette.bytes_per_pixel;
+ vb->timestamp = usbvision->frame[vb->index].timestamp;
+ vb->sequence = usbvision->frame[vb->index].sequence;
+ return 0;
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *vb)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ struct usbvision_frame *frame;
+ unsigned long lock_flags;
+
+ /* FIXME : works only on VIDEO_CAPTURE MODE, MMAP. */
+ if (vb->index >= usbvision->num_frames)
+ return -EINVAL;
+
+ frame = &usbvision->frame[vb->index];
+
+ if (frame->grabstate != frame_state_unused)
+ return -EAGAIN;
+
+ /* Mark it as ready and enqueue frame */
+ frame->grabstate = frame_state_ready;
+ frame->scanstate = scan_state_scanning;
+ frame->scanlength = 0; /* Accumulated in usbvision_parse_data() */
+
+ vb->flags &= ~V4L2_BUF_FLAG_DONE;
+
+ /* set v4l2_format index */
+ frame->v4l2_format = usbvision->palette;
+
+ spin_lock_irqsave(&usbvision->queue_lock, lock_flags);
+ list_add_tail(&usbvision->frame[vb->index].frame, &usbvision->inqueue);
+ spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags);
+
+ return 0;
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *vb)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ int ret;
+ struct usbvision_frame *f;
+ unsigned long lock_flags;
+
+ if (list_empty(&(usbvision->outqueue))) {
+ if (usbvision->streaming == stream_idle)
+ return -EINVAL;
+ ret = wait_event_interruptible
+ (usbvision->wait_frame,
+ !list_empty(&(usbvision->outqueue)));
+ if (ret)
+ return ret;
+ }
+
+ spin_lock_irqsave(&usbvision->queue_lock, lock_flags);
+ f = list_entry(usbvision->outqueue.next,
+ struct usbvision_frame, frame);
+ list_del(usbvision->outqueue.next);
+ spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags);
+
+ f->grabstate = frame_state_unused;
+
+ vb->memory = V4L2_MEMORY_MMAP;
+ vb->flags = V4L2_BUF_FLAG_MAPPED |
+ V4L2_BUF_FLAG_QUEUED |
+ V4L2_BUF_FLAG_DONE;
+ vb->index = f->index;
+ vb->sequence = f->sequence;
+ vb->timestamp = f->timestamp;
+ vb->field = V4L2_FIELD_NONE;
+ vb->bytesused = f->scanlength;
+
+ return 0;
+}
+
+static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ usbvision->streaming = stream_on;
+ call_all(usbvision, video, s_stream, 1);
+
+ return 0;
+}
+
+static int vidioc_streamoff(struct file *file,
+ void *priv, enum v4l2_buf_type type)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (usbvision->streaming == stream_on) {
+ usbvision_stream_interrupt(usbvision);
+ /* Stop all video streamings */
+ call_all(usbvision, video, s_stream, 0);
+ }
+ usbvision_empty_framequeues(usbvision);
+
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *vfd)
+{
+ if (vfd->index >= USBVISION_SUPPORTED_PALETTES - 1)
+ return -EINVAL;
+ strcpy(vfd->description, usbvision_v4l2_format[vfd->index].desc);
+ vfd->pixelformat = usbvision_v4l2_format[vfd->index].format;
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *vf)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ vf->fmt.pix.width = usbvision->curwidth;
+ vf->fmt.pix.height = usbvision->curheight;
+ vf->fmt.pix.pixelformat = usbvision->palette.format;
+ vf->fmt.pix.bytesperline =
+ usbvision->curwidth * usbvision->palette.bytes_per_pixel;
+ vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline * usbvision->curheight;
+ vf->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ vf->fmt.pix.field = V4L2_FIELD_NONE; /* Always progressive image */
+
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *vf)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ int format_idx;
+
+ /* Find requested format in available ones */
+ for (format_idx = 0; format_idx < USBVISION_SUPPORTED_PALETTES; format_idx++) {
+ if (vf->fmt.pix.pixelformat ==
+ usbvision_v4l2_format[format_idx].format) {
+ usbvision->palette = usbvision_v4l2_format[format_idx];
+ break;
+ }
+ }
+ /* robustness */
+ if (format_idx == USBVISION_SUPPORTED_PALETTES)
+ return -EINVAL;
+ RESTRICT_TO_RANGE(vf->fmt.pix.width, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH);
+ RESTRICT_TO_RANGE(vf->fmt.pix.height, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT);
+
+ vf->fmt.pix.bytesperline = vf->fmt.pix.width*
+ usbvision->palette.bytes_per_pixel;
+ vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline*vf->fmt.pix.height;
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *vf)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ int ret;
+
+ ret = vidioc_try_fmt_vid_cap(file, priv, vf);
+ if (ret)
+ return ret;
+
+ /* stop io in case it is already in progress */
+ if (usbvision->streaming == stream_on) {
+ ret = usbvision_stream_interrupt(usbvision);
+ if (ret)
+ return ret;
+ }
+ usbvision_frames_free(usbvision);
+ usbvision_empty_framequeues(usbvision);
+
+ usbvision->cur_frame = NULL;
+
+ /* by now we are committed to the new data... */
+ usbvision_set_output(usbvision, vf->fmt.pix.width, vf->fmt.pix.height);
+
+ return 0;
+}
+
+static ssize_t usbvision_v4l2_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ int noblock = file->f_flags & O_NONBLOCK;
+ unsigned long lock_flags;
+ int ret, i;
+ struct usbvision_frame *frame;
+
+ PDEBUG(DBG_IO, "%s: %ld bytes, noblock=%d", __func__,
+ (unsigned long)count, noblock);
+
+ if (!USBVISION_IS_OPERATIONAL(usbvision) || (buf == NULL))
+ return -EFAULT;
+
+ /* This entry point is compatible with the mmap routines
+ so that a user can do either VIDIOC_QBUF/VIDIOC_DQBUF
+ to get frames or call read on the device. */
+ if (!usbvision->num_frames) {
+ /* First, allocate some frames to work with
+ if this has not been done with VIDIOC_REQBUF */
+ usbvision_frames_free(usbvision);
+ usbvision_empty_framequeues(usbvision);
+ usbvision_frames_alloc(usbvision, USBVISION_NUMFRAMES);
+ }
+
+ if (usbvision->streaming != stream_on) {
+ /* no stream is running, make it running ! */
+ usbvision->streaming = stream_on;
+ call_all(usbvision, video, s_stream, 1);
+ }
+
+ /* Then, enqueue as many frames as possible
+ (like a user of VIDIOC_QBUF would do) */
+ for (i = 0; i < usbvision->num_frames; i++) {
+ frame = &usbvision->frame[i];
+ if (frame->grabstate == frame_state_unused) {
+ /* Mark it as ready and enqueue frame */
+ frame->grabstate = frame_state_ready;
+ frame->scanstate = scan_state_scanning;
+ /* Accumulated in usbvision_parse_data() */
+ frame->scanlength = 0;
+
+ /* set v4l2_format index */
+ frame->v4l2_format = usbvision->palette;
+
+ spin_lock_irqsave(&usbvision->queue_lock, lock_flags);
+ list_add_tail(&frame->frame, &usbvision->inqueue);
+ spin_unlock_irqrestore(&usbvision->queue_lock,
+ lock_flags);
+ }
+ }
+
+ /* Then try to steal a frame (like a VIDIOC_DQBUF would do) */
+ if (list_empty(&(usbvision->outqueue))) {
+ if (noblock)
+ return -EAGAIN;
+
+ ret = wait_event_interruptible
+ (usbvision->wait_frame,
+ !list_empty(&(usbvision->outqueue)));
+ if (ret)
+ return ret;
+ }
+
+ spin_lock_irqsave(&usbvision->queue_lock, lock_flags);
+ frame = list_entry(usbvision->outqueue.next,
+ struct usbvision_frame, frame);
+ list_del(usbvision->outqueue.next);
+ spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags);
+
+ /* An error returns an empty frame */
+ if (frame->grabstate == frame_state_error) {
+ frame->bytes_read = 0;
+ return 0;
+ }
+
+ PDEBUG(DBG_IO, "%s: frmx=%d, bytes_read=%ld, scanlength=%ld",
+ __func__,
+ frame->index, frame->bytes_read, frame->scanlength);
+
+ /* copy bytes to user space; we allow for partials reads */
+ if ((count + frame->bytes_read) > (unsigned long)frame->scanlength)
+ count = frame->scanlength - frame->bytes_read;
+
+ if (copy_to_user(buf, frame->data + frame->bytes_read, count))
+ return -EFAULT;
+
+ frame->bytes_read += count;
+ PDEBUG(DBG_IO, "%s: {copy} count used=%ld, new bytes_read=%ld",
+ __func__,
+ (unsigned long)count, frame->bytes_read);
+
+ /* For now, forget the frame if it has not been read in one shot. */
+/* if (frame->bytes_read >= frame->scanlength) {*/ /* All data has been read */
+ frame->bytes_read = 0;
+
+ /* Mark it as available to be used again. */
+ frame->grabstate = frame_state_unused;
+/* } */
+
+ return count;
+}
+
+static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ unsigned long size = vma->vm_end - vma->vm_start,
+ start = vma->vm_start;
+ void *pos;
+ u32 i;
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ PDEBUG(DBG_MMAP, "mmap");
+
+ if (!USBVISION_IS_OPERATIONAL(usbvision))
+ return -EFAULT;
+
+ if (!(vma->vm_flags & VM_WRITE) ||
+ size != PAGE_ALIGN(usbvision->max_frame_size)) {
+ return -EINVAL;
+ }
+
+ for (i = 0; i < usbvision->num_frames; i++) {
+ if (((PAGE_ALIGN(usbvision->max_frame_size)*i) >> PAGE_SHIFT) ==
+ vma->vm_pgoff)
+ break;
+ }
+ if (i == usbvision->num_frames) {
+ PDEBUG(DBG_MMAP,
+ "mmap: user supplied mapping address is out of range");
+ return -EINVAL;
+ }
+
+ /* VM_IO is eventually going to replace PageReserved altogether */
+ vma->vm_flags |= VM_IO;
+ vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
+
+ pos = usbvision->frame[i].data;
+ while (size > 0) {
+ if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
+ PDEBUG(DBG_MMAP, "mmap: vm_insert_page failed");
+ return -EAGAIN;
+ }
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Here comes the stuff for radio on usbvision based devices
+ *
+ */
+static int usbvision_radio_open(struct file *file)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ int err_code = 0;
+
+ PDEBUG(DBG_IO, "%s:", __func__);
+
+ if (usbvision->user) {
+ dev_err(&usbvision->rdev->dev,
+ "%s: Someone tried to open an already opened USBVision Radio!\n",
+ __func__);
+ err_code = -EBUSY;
+ } else {
+ if (power_on_at_open) {
+ usbvision_reset_power_off_timer(usbvision);
+ if (usbvision->power == 0) {
+ usbvision_power_on(usbvision);
+ usbvision_i2c_register(usbvision);
+ }
+ }
+
+ /* Alternate interface 1 is is the biggest frame size */
+ err_code = usbvision_set_alternate(usbvision);
+ if (err_code < 0) {
+ usbvision->last_error = err_code;
+ err_code = -EBUSY;
+ goto out;
+ }
+
+ /* If so far no errors then we shall start the radio */
+ usbvision->radio = 1;
+ call_all(usbvision, tuner, s_radio);
+ usbvision_set_audio(usbvision, USBVISION_AUDIO_RADIO);
+ usbvision->user++;
+ }
+
+ if (err_code) {
+ if (power_on_at_open) {
+ usbvision_i2c_unregister(usbvision);
+ usbvision_power_off(usbvision);
+ usbvision->initialized = 0;
+ }
+ }
+out:
+ return err_code;
+}
+
+
+static int usbvision_radio_close(struct file *file)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ int err_code = 0;
+
+ PDEBUG(DBG_IO, "");
+
+ /* Set packet size to 0 */
+ usbvision->iface_alt = 0;
+ err_code = usb_set_interface(usbvision->dev, usbvision->iface,
+ usbvision->iface_alt);
+
+ usbvision_audio_off(usbvision);
+ usbvision->radio = 0;
+ usbvision->user--;
+
+ if (power_on_at_open) {
+ usbvision_set_power_off_timer(usbvision);
+ usbvision->initialized = 0;
+ }
+
+ if (usbvision->remove_pending) {
+ printk(KERN_INFO "%s: Final disconnect\n", __func__);
+ usbvision_release(usbvision);
+ }
+
+ PDEBUG(DBG_IO, "success");
+ return err_code;
+}
+
+/* Video registration stuff */
+
+/* Video template */
+static const struct v4l2_file_operations usbvision_fops = {
+ .owner = THIS_MODULE,
+ .open = usbvision_v4l2_open,
+ .release = usbvision_v4l2_close,
+ .read = usbvision_v4l2_read,
+ .mmap = usbvision_v4l2_mmap,
+ .unlocked_ioctl = video_ioctl2,
+/* .poll = video_poll, */
+};
+
+static const struct v4l2_ioctl_ops usbvision_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+#endif
+};
+
+static struct video_device usbvision_video_template = {
+ .fops = &usbvision_fops,
+ .ioctl_ops = &usbvision_ioctl_ops,
+ .name = "usbvision-video",
+ .release = video_device_release,
+ .tvnorms = USBVISION_NORMS,
+ .current_norm = V4L2_STD_PAL
+};
+
+
+/* Radio template */
+static const struct v4l2_file_operations usbvision_radio_fops = {
+ .owner = THIS_MODULE,
+ .open = usbvision_radio_open,
+ .release = usbvision_radio_close,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops usbvision_radio_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+};
+
+static struct video_device usbvision_radio_template = {
+ .fops = &usbvision_radio_fops,
+ .name = "usbvision-radio",
+ .release = video_device_release,
+ .ioctl_ops = &usbvision_radio_ioctl_ops,
+
+ .tvnorms = USBVISION_NORMS,
+ .current_norm = V4L2_STD_PAL
+};
+
+
+static struct video_device *usbvision_vdev_init(struct usb_usbvision *usbvision,
+ struct video_device *vdev_template,
+ char *name)
+{
+ struct usb_device *usb_dev = usbvision->dev;
+ struct video_device *vdev;
+
+ if (usb_dev == NULL) {
+ dev_err(&usbvision->dev->dev,
+ "%s: usbvision->dev is not set\n", __func__);
+ return NULL;
+ }
+
+ vdev = video_device_alloc();
+ if (NULL == vdev)
+ return NULL;
+ *vdev = *vdev_template;
+ vdev->lock = &usbvision->v4l2_lock;
+ vdev->v4l2_dev = &usbvision->v4l2_dev;
+ snprintf(vdev->name, sizeof(vdev->name), "%s", name);
+ video_set_drvdata(vdev, usbvision);
+ return vdev;
+}
+
+/* unregister video4linux devices */
+static void usbvision_unregister_video(struct usb_usbvision *usbvision)
+{
+ /* Radio Device: */
+ if (usbvision->rdev) {
+ PDEBUG(DBG_PROBE, "unregister %s [v4l2]",
+ video_device_node_name(usbvision->rdev));
+ if (video_is_registered(usbvision->rdev))
+ video_unregister_device(usbvision->rdev);
+ else
+ video_device_release(usbvision->rdev);
+ usbvision->rdev = NULL;
+ }
+
+ /* Video Device: */
+ if (usbvision->vdev) {
+ PDEBUG(DBG_PROBE, "unregister %s [v4l2]",
+ video_device_node_name(usbvision->vdev));
+ if (video_is_registered(usbvision->vdev))
+ video_unregister_device(usbvision->vdev);
+ else
+ video_device_release(usbvision->vdev);
+ usbvision->vdev = NULL;
+ }
+}
+
+/* register video4linux devices */
+static int __devinit usbvision_register_video(struct usb_usbvision *usbvision)
+{
+ /* Video Device: */
+ usbvision->vdev = usbvision_vdev_init(usbvision,
+ &usbvision_video_template,
+ "USBVision Video");
+ if (usbvision->vdev == NULL)
+ goto err_exit;
+ if (video_register_device(usbvision->vdev, VFL_TYPE_GRABBER, video_nr) < 0)
+ goto err_exit;
+ printk(KERN_INFO "USBVision[%d]: registered USBVision Video device %s [v4l2]\n",
+ usbvision->nr, video_device_node_name(usbvision->vdev));
+
+ /* Radio Device: */
+ if (usbvision_device_data[usbvision->dev_model].radio) {
+ /* usbvision has radio */
+ usbvision->rdev = usbvision_vdev_init(usbvision,
+ &usbvision_radio_template,
+ "USBVision Radio");
+ if (usbvision->rdev == NULL)
+ goto err_exit;
+ if (video_register_device(usbvision->rdev, VFL_TYPE_RADIO, radio_nr) < 0)
+ goto err_exit;
+ printk(KERN_INFO "USBVision[%d]: registered USBVision Radio device %s [v4l2]\n",
+ usbvision->nr, video_device_node_name(usbvision->rdev));
+ }
+ /* all done */
+ return 0;
+
+ err_exit:
+ dev_err(&usbvision->dev->dev,
+ "USBVision[%d]: video_register_device() failed\n",
+ usbvision->nr);
+ usbvision_unregister_video(usbvision);
+ return -1;
+}
+
+/*
+ * usbvision_alloc()
+ *
+ * This code allocates the struct usb_usbvision.
+ * It is filled with default values.
+ *
+ * Returns NULL on error, a pointer to usb_usbvision else.
+ *
+ */
+static struct usb_usbvision *usbvision_alloc(struct usb_device *dev,
+ struct usb_interface *intf)
+{
+ struct usb_usbvision *usbvision;
+
+ usbvision = kzalloc(sizeof(struct usb_usbvision), GFP_KERNEL);
+ if (usbvision == NULL)
+ return NULL;
+
+ usbvision->dev = dev;
+ if (v4l2_device_register(&intf->dev, &usbvision->v4l2_dev))
+ goto err_free;
+
+ mutex_init(&usbvision->v4l2_lock);
+
+ /* prepare control urb for control messages during interrupts */
+ usbvision->ctrl_urb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL);
+ if (usbvision->ctrl_urb == NULL)
+ goto err_unreg;
+ init_waitqueue_head(&usbvision->ctrl_urb_wq);
+
+ usbvision_init_power_off_timer(usbvision);
+
+ return usbvision;
+
+err_unreg:
+ v4l2_device_unregister(&usbvision->v4l2_dev);
+err_free:
+ kfree(usbvision);
+ return NULL;
+}
+
+/*
+ * usbvision_release()
+ *
+ * This code does final release of struct usb_usbvision. This happens
+ * after the device is disconnected -and- all clients closed their files.
+ *
+ */
+static void usbvision_release(struct usb_usbvision *usbvision)
+{
+ PDEBUG(DBG_PROBE, "");
+
+ usbvision_reset_power_off_timer(usbvision);
+
+ usbvision->initialized = 0;
+
+ usbvision_remove_sysfs(usbvision->vdev);
+ usbvision_unregister_video(usbvision);
+
+ usb_free_urb(usbvision->ctrl_urb);
+
+ v4l2_device_unregister(&usbvision->v4l2_dev);
+ kfree(usbvision);
+
+ PDEBUG(DBG_PROBE, "success");
+}
+
+
+/*********************** usb interface **********************************/
+
+static void usbvision_configure_video(struct usb_usbvision *usbvision)
+{
+ int model;
+
+ if (usbvision == NULL)
+ return;
+
+ model = usbvision->dev_model;
+ usbvision->palette = usbvision_v4l2_format[2]; /* V4L2_PIX_FMT_RGB24; */
+
+ if (usbvision_device_data[usbvision->dev_model].vin_reg2_override) {
+ usbvision->vin_reg2_preset =
+ usbvision_device_data[usbvision->dev_model].vin_reg2;
+ } else {
+ usbvision->vin_reg2_preset = 0;
+ }
+
+ usbvision->tvnorm_id = usbvision_device_data[model].video_norm;
+
+ usbvision->video_inputs = usbvision_device_data[model].video_channels;
+ usbvision->ctl_input = 0;
+
+ /* This should be here to make i2c clients to be able to register */
+ /* first switch off audio */
+ if (usbvision_device_data[model].audio_channels > 0)
+ usbvision_audio_off(usbvision);
+ if (!power_on_at_open) {
+ /* and then power up the noisy tuner */
+ usbvision_power_on(usbvision);
+ usbvision_i2c_register(usbvision);
+ }
+}
+
+/*
+ * usbvision_probe()
+ *
+ * This procedure queries device descriptor and accepts the interface
+ * if it looks like USBVISION video device
+ *
+ */
+static int __devinit usbvision_probe(struct usb_interface *intf,
+ const struct usb_device_id *devid)
+{
+ struct usb_device *dev = usb_get_dev(interface_to_usbdev(intf));
+ struct usb_interface *uif;
+ __u8 ifnum = intf->altsetting->desc.bInterfaceNumber;
+ const struct usb_host_interface *interface;
+ struct usb_usbvision *usbvision = NULL;
+ const struct usb_endpoint_descriptor *endpoint;
+ int model, i;
+
+ PDEBUG(DBG_PROBE, "VID=%#04x, PID=%#04x, ifnum=%u",
+ dev->descriptor.idVendor,
+ dev->descriptor.idProduct, ifnum);
+
+ model = devid->driver_info;
+ if (model < 0 || model >= usbvision_device_data_size) {
+ PDEBUG(DBG_PROBE, "model out of bounds %d", model);
+ return -ENODEV;
+ }
+ printk(KERN_INFO "%s: %s found\n", __func__,
+ usbvision_device_data[model].model_string);
+
+ if (usbvision_device_data[model].interface >= 0)
+ interface = &dev->actconfig->interface[usbvision_device_data[model].interface]->altsetting[0];
+ else
+ interface = &dev->actconfig->interface[ifnum]->altsetting[0];
+ endpoint = &interface->endpoint[1].desc;
+ if (!usb_endpoint_xfer_isoc(endpoint)) {
+ dev_err(&intf->dev, "%s: interface %d. has non-ISO endpoint!\n",
+ __func__, ifnum);
+ dev_err(&intf->dev, "%s: Endpoint attributes %d",
+ __func__, endpoint->bmAttributes);
+ return -ENODEV;
+ }
+ if (usb_endpoint_dir_out(endpoint)) {
+ dev_err(&intf->dev, "%s: interface %d. has ISO OUT endpoint!\n",
+ __func__, ifnum);
+ return -ENODEV;
+ }
+
+ usbvision = usbvision_alloc(dev, intf);
+ if (usbvision == NULL) {
+ dev_err(&intf->dev, "%s: couldn't allocate USBVision struct\n", __func__);
+ return -ENOMEM;
+ }
+
+ if (dev->descriptor.bNumConfigurations > 1)
+ usbvision->bridge_type = BRIDGE_NT1004;
+ else if (model == DAZZLE_DVC_90_REV_1_SECAM)
+ usbvision->bridge_type = BRIDGE_NT1005;
+ else
+ usbvision->bridge_type = BRIDGE_NT1003;
+ PDEBUG(DBG_PROBE, "bridge_type %d", usbvision->bridge_type);
+
+ /* compute alternate max packet sizes */
+ uif = dev->actconfig->interface[0];
+
+ usbvision->num_alt = uif->num_altsetting;
+ PDEBUG(DBG_PROBE, "Alternate settings: %i", usbvision->num_alt);
+ usbvision->alt_max_pkt_size = kmalloc(32 * usbvision->num_alt, GFP_KERNEL);
+ if (usbvision->alt_max_pkt_size == NULL) {
+ dev_err(&intf->dev, "usbvision: out of memory!\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < usbvision->num_alt; i++) {
+ u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[1].desc.
+ wMaxPacketSize);
+ usbvision->alt_max_pkt_size[i] =
+ (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
+ PDEBUG(DBG_PROBE, "Alternate setting %i, max size= %i", i,
+ usbvision->alt_max_pkt_size[i]);
+ }
+
+
+ usbvision->nr = usbvision_nr++;
+
+ usbvision->have_tuner = usbvision_device_data[model].tuner;
+ if (usbvision->have_tuner)
+ usbvision->tuner_type = usbvision_device_data[model].tuner_type;
+
+ usbvision->dev_model = model;
+ usbvision->remove_pending = 0;
+ usbvision->iface = ifnum;
+ usbvision->iface_alt = 0;
+ usbvision->video_endp = endpoint->bEndpointAddress;
+ usbvision->isoc_packet_size = 0;
+ usbvision->usb_bandwidth = 0;
+ usbvision->user = 0;
+ usbvision->streaming = stream_off;
+ usbvision_configure_video(usbvision);
+ usbvision_register_video(usbvision);
+
+ usbvision_create_sysfs(usbvision->vdev);
+
+ PDEBUG(DBG_PROBE, "success");
+ return 0;
+}
+
+
+/*
+ * usbvision_disconnect()
+ *
+ * This procedure stops all driver activity, deallocates interface-private
+ * structure (pointed by 'ptr') and after that driver should be removable
+ * with no ill consequences.
+ *
+ */
+static void __devexit usbvision_disconnect(struct usb_interface *intf)
+{
+ struct usb_usbvision *usbvision = to_usbvision(usb_get_intfdata(intf));
+
+ PDEBUG(DBG_PROBE, "");
+
+ if (usbvision == NULL) {
+ pr_err("%s: usb_get_intfdata() failed\n", __func__);
+ return;
+ }
+
+ mutex_lock(&usbvision->v4l2_lock);
+
+ /* At this time we ask to cancel outstanding URBs */
+ usbvision_stop_isoc(usbvision);
+
+ v4l2_device_disconnect(&usbvision->v4l2_dev);
+
+ if (usbvision->power) {
+ usbvision_i2c_unregister(usbvision);
+ usbvision_power_off(usbvision);
+ }
+ usbvision->remove_pending = 1; /* Now all ISO data will be ignored */
+
+ usb_put_dev(usbvision->dev);
+ usbvision->dev = NULL; /* USB device is no more */
+
+ mutex_unlock(&usbvision->v4l2_lock);
+
+ if (usbvision->user) {
+ printk(KERN_INFO "%s: In use, disconnect pending\n",
+ __func__);
+ wake_up_interruptible(&usbvision->wait_frame);
+ wake_up_interruptible(&usbvision->wait_stream);
+ } else {
+ usbvision_release(usbvision);
+ }
+
+ PDEBUG(DBG_PROBE, "success");
+}
+
+static struct usb_driver usbvision_driver = {
+ .name = "usbvision",
+ .id_table = usbvision_table,
+ .probe = usbvision_probe,
+ .disconnect = __devexit_p(usbvision_disconnect),
+};
+
+/*
+ * usbvision_init()
+ *
+ * This code is run to initialize the driver.
+ *
+ */
+static int __init usbvision_init(void)
+{
+ int err_code;
+
+ PDEBUG(DBG_PROBE, "");
+
+ PDEBUG(DBG_IO, "IO debugging is enabled [video]");
+ PDEBUG(DBG_PROBE, "PROBE debugging is enabled [video]");
+ PDEBUG(DBG_MMAP, "MMAP debugging is enabled [video]");
+
+ /* disable planar mode support unless compression enabled */
+ if (isoc_mode != ISOC_MODE_COMPRESS) {
+ /* FIXME : not the right way to set supported flag */
+ usbvision_v4l2_format[6].supported = 0; /* V4L2_PIX_FMT_YVU420 */
+ usbvision_v4l2_format[7].supported = 0; /* V4L2_PIX_FMT_YUV422P */
+ }
+
+ err_code = usb_register(&usbvision_driver);
+
+ if (err_code == 0) {
+ printk(KERN_INFO DRIVER_DESC " : " USBVISION_VERSION_STRING "\n");
+ PDEBUG(DBG_PROBE, "success");
+ }
+ return err_code;
+}
+
+static void __exit usbvision_exit(void)
+{
+ PDEBUG(DBG_PROBE, "");
+
+ usb_deregister(&usbvision_driver);
+ PDEBUG(DBG_PROBE, "success");
+}
+
+module_init(usbvision_init);
+module_exit(usbvision_exit);
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/video/usbvision/usbvision.h
new file mode 100644
index 00000000..43cf61fe
--- /dev/null
+++ b/drivers/media/video/usbvision/usbvision.h
@@ -0,0 +1,535 @@
+/*
+ * USBVISION.H
+ * usbvision header file
+ *
+ * Copyright (c) 1999-2005 Joerg Heckenbach <joerg@heckenbach-aw.de>
+ * Dwaine Garden <dwainegarden@rogers.com>
+ *
+ *
+ * Report problems to v4l MailingList: linux-media@vger.kernel.org
+ *
+ * This module is part of usbvision driver project.
+ * Updates to driver completed by Dwaine P. Garden
+ * v4l2 conversion by Thierry Merle <thierry.merle@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#ifndef __LINUX_USBVISION_H
+#define __LINUX_USBVISION_H
+
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <media/v4l2-device.h>
+#include <media/tuner.h>
+#include <linux/videodev2.h>
+
+#define USBVISION_DEBUG /* Turn on debug messages */
+
+#define USBVISION_PWR_REG 0x00
+ #define USBVISION_SSPND_EN (1 << 1)
+ #define USBVISION_RES2 (1 << 2)
+ #define USBVISION_PWR_VID (1 << 5)
+ #define USBVISION_E2_EN (1 << 7)
+#define USBVISION_CONFIG_REG 0x01
+#define USBVISION_ADRS_REG 0x02
+#define USBVISION_ALTER_REG 0x03
+#define USBVISION_FORCE_ALTER_REG 0x04
+#define USBVISION_STATUS_REG 0x05
+#define USBVISION_IOPIN_REG 0x06
+ #define USBVISION_IO_1 (1 << 0)
+ #define USBVISION_IO_2 (1 << 1)
+ #define USBVISION_AUDIO_IN 0
+ #define USBVISION_AUDIO_TV 1
+ #define USBVISION_AUDIO_RADIO 2
+ #define USBVISION_AUDIO_MUTE 3
+#define USBVISION_SER_MODE 0x07
+ #define USBVISION_CLK_OUT (1 << 0)
+ #define USBVISION_DAT_IO (1 << 1)
+ #define USBVISION_SENS_OUT (1 << 2)
+ #define USBVISION_SER_MODE_SOFT (0 << 4)
+ #define USBVISION_SER_MODE_SIO (1 << 4)
+#define USBVISION_SER_ADRS 0x08
+#define USBVISION_SER_CONT 0x09
+#define USBVISION_SER_DAT1 0x0A
+#define USBVISION_SER_DAT2 0x0B
+#define USBVISION_SER_DAT3 0x0C
+#define USBVISION_SER_DAT4 0x0D
+#define USBVISION_EE_DATA 0x0E
+#define USBVISION_EE_LSBAD 0x0F
+#define USBVISION_EE_CONT 0x10
+#define USBVISION_DRM_CONT 0x12
+ #define USBVISION_REF (1 << 0)
+ #define USBVISION_RES_UR (1 << 2)
+ #define USBVISION_RES_FDL (1 << 3)
+ #define USBVISION_RES_VDW (1 << 4)
+#define USBVISION_DRM_PRM1 0x13
+#define USBVISION_DRM_PRM2 0x14
+#define USBVISION_DRM_PRM3 0x15
+#define USBVISION_DRM_PRM4 0x16
+#define USBVISION_DRM_PRM5 0x17
+#define USBVISION_DRM_PRM6 0x18
+#define USBVISION_DRM_PRM7 0x19
+#define USBVISION_DRM_PRM8 0x1A
+#define USBVISION_VIN_REG1 0x1B
+ #define USBVISION_8_422_SYNC 0x01
+ #define USBVISION_16_422_SYNC 0x02
+ #define USBVISION_VSNC_POL (1 << 3)
+ #define USBVISION_HSNC_POL (1 << 4)
+ #define USBVISION_FID_POL (1 << 5)
+ #define USBVISION_HVALID_PO (1 << 6)
+ #define USBVISION_VCLK_POL (1 << 7)
+#define USBVISION_VIN_REG2 0x1C
+ #define USBVISION_AUTO_FID (1 << 0)
+ #define USBVISION_NONE_INTER (1 << 1)
+ #define USBVISION_NOHVALID (1 << 2)
+ #define USBVISION_UV_ID (1 << 3)
+ #define USBVISION_FIX_2C (1 << 4)
+ #define USBVISION_SEND_FID (1 << 5)
+ #define USBVISION_KEEP_BLANK (1 << 7)
+#define USBVISION_LXSIZE_I 0x1D
+#define USBVISION_MXSIZE_I 0x1E
+#define USBVISION_LYSIZE_I 0x1F
+#define USBVISION_MYSIZE_I 0x20
+#define USBVISION_LX_OFFST 0x21
+#define USBVISION_MX_OFFST 0x22
+#define USBVISION_LY_OFFST 0x23
+#define USBVISION_MY_OFFST 0x24
+#define USBVISION_FRM_RATE 0x25
+#define USBVISION_LXSIZE_O 0x26
+#define USBVISION_MXSIZE_O 0x27
+#define USBVISION_LYSIZE_O 0x28
+#define USBVISION_MYSIZE_O 0x29
+#define USBVISION_FILT_CONT 0x2A
+#define USBVISION_VO_MODE 0x2B
+#define USBVISION_INTRA_CYC 0x2C
+#define USBVISION_STRIP_SZ 0x2D
+#define USBVISION_FORCE_INTRA 0x2E
+#define USBVISION_FORCE_UP 0x2F
+#define USBVISION_BUF_THR 0x30
+#define USBVISION_DVI_YUV 0x31
+#define USBVISION_AUDIO_CONT 0x32
+#define USBVISION_AUD_PK_LEN 0x33
+#define USBVISION_BLK_PK_LEN 0x34
+#define USBVISION_PCM_THR1 0x38
+#define USBVISION_PCM_THR2 0x39
+#define USBVISION_DIST_THR_L 0x3A
+#define USBVISION_DIST_THR_H 0x3B
+#define USBVISION_MAX_DIST_L 0x3C
+#define USBVISION_MAX_DIST_H 0x3D
+#define USBVISION_OP_CODE 0x33
+
+#define MAX_BYTES_PER_PIXEL 4
+
+#define MIN_FRAME_WIDTH 64
+#define MAX_USB_WIDTH 320 /* 384 */
+#define MAX_FRAME_WIDTH 320 /* 384 */ /* streching sometimes causes crashes*/
+
+#define MIN_FRAME_HEIGHT 48
+#define MAX_USB_HEIGHT 240 /* 288 */
+#define MAX_FRAME_HEIGHT 240 /* 288 */ /* Streching sometimes causes crashes*/
+
+#define MAX_FRAME_SIZE (MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT * MAX_BYTES_PER_PIXEL)
+#define USBVISION_CLIPMASK_SIZE (MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT / 8) /* bytesize of clipmask */
+
+#define USBVISION_URB_FRAMES 32
+
+#define USBVISION_NUM_HEADERMARKER 20
+#define USBVISION_NUMFRAMES 3 /* Maximum number of frames an application can get */
+#define USBVISION_NUMSBUF 2 /* Dimensioning the USB S buffering */
+
+#define USBVISION_POWEROFF_TIME (3 * HZ) /* 3 seconds */
+
+
+#define FRAMERATE_MIN 0
+#define FRAMERATE_MAX 31
+
+enum {
+ ISOC_MODE_YUV422 = 0x03,
+ ISOC_MODE_YUV420 = 0x14,
+ ISOC_MODE_COMPRESS = 0x60,
+};
+
+/* This macro restricts an int variable to an inclusive range */
+#define RESTRICT_TO_RANGE(v, mi, ma) \
+ { if ((v) < (mi)) (v) = (mi); else if ((v) > (ma)) (v) = (ma); }
+
+/*
+ * We use macros to do YUV -> RGB conversion because this is
+ * very important for speed and totally unimportant for size.
+ *
+ * YUV -> RGB Conversion
+ * ---------------------
+ *
+ * B = 1.164*(Y-16) + 2.018*(V-128)
+ * G = 1.164*(Y-16) - 0.813*(U-128) - 0.391*(V-128)
+ * R = 1.164*(Y-16) + 1.596*(U-128)
+ *
+ * If you fancy integer arithmetics (as you should), hear this:
+ *
+ * 65536*B = 76284*(Y-16) + 132252*(V-128)
+ * 65536*G = 76284*(Y-16) - 53281*(U-128) - 25625*(V-128)
+ * 65536*R = 76284*(Y-16) + 104595*(U-128)
+ *
+ * Make sure the output values are within [0..255] range.
+ */
+#define LIMIT_RGB(x) (((x) < 0) ? 0 : (((x) > 255) ? 255 : (x)))
+#define YUV_TO_RGB_BY_THE_BOOK(my, mu, mv, mr, mg, mb) { \
+ int mm_y, mm_yc, mm_u, mm_v, mm_r, mm_g, mm_b; \
+ mm_y = (my) - 16; \
+ mm_u = (mu) - 128; \
+ mm_v = (mv) - 128; \
+ mm_yc = mm_y * 76284; \
+ mm_b = (mm_yc + 132252 * mm_v) >> 16; \
+ mm_g = (mm_yc - 53281 * mm_u - 25625 * mm_v) >> 16; \
+ mm_r = (mm_yc + 104595 * mm_u) >> 16; \
+ mb = LIMIT_RGB(mm_b); \
+ mg = LIMIT_RGB(mm_g); \
+ mr = LIMIT_RGB(mm_r); \
+}
+
+/* Debugging aid */
+#define USBVISION_SAY_AND_WAIT(what) { \
+ wait_queue_head_t wq; \
+ init_waitqueue_head(&wq); \
+ printk(KERN_INFO "Say: %s\n", what); \
+ interruptible_sleep_on_timeout(&wq, HZ * 3); \
+}
+
+/*
+ * This macro checks if usbvision is still operational. The 'usbvision'
+ * pointer must be valid, usbvision->dev must be valid, we are not
+ * removing the device and the device has not erred on us.
+ */
+#define USBVISION_IS_OPERATIONAL(udevice) (\
+ (udevice != NULL) && \
+ ((udevice)->dev != NULL) && \
+ ((udevice)->last_error == 0) && \
+ (!(udevice)->remove_pending))
+
+#define I2C_USB_ADAP_MAX 16
+
+#define USBVISION_NORMS (V4L2_STD_PAL | V4L2_STD_NTSC | V4L2_STD_SECAM | V4L2_STD_PAL_M)
+
+/* ----------------------------------------------------------------- */
+/* usbvision video structures */
+/* ----------------------------------------------------------------- */
+enum scan_state {
+ scan_state_scanning, /* Scanning for header */
+ scan_state_lines /* Parsing lines */
+};
+
+/* Completion states of the data parser */
+enum parse_state {
+ parse_state_continue, /* Just parse next item */
+ parse_state_next_frame, /* Frame done, send it to V4L */
+ parse_state_out, /* Not enough data for frame */
+ parse_state_end_parse /* End parsing */
+};
+
+enum frame_state {
+ frame_state_unused, /* Unused (no MCAPTURE) */
+ frame_state_ready, /* Ready to start grabbing */
+ frame_state_grabbing, /* In the process of being grabbed into */
+ frame_state_done, /* Finished grabbing, but not been synced yet */
+ frame_state_done_hold, /* Are syncing or reading */
+ frame_state_error, /* Something bad happened while processing */
+};
+
+/* stream states */
+enum stream_state {
+ stream_off, /* Driver streaming is completely OFF */
+ stream_idle, /* Driver streaming is ready to be put ON by the application */
+ stream_interrupt, /* Driver streaming must be interrupted */
+ stream_on, /* Driver streaming is put ON by the application */
+};
+
+enum isoc_state {
+ isoc_state_in_frame, /* Isoc packet is member of frame */
+ isoc_state_no_frame, /* Isoc packet is not member of any frame */
+};
+
+struct usb_device;
+
+struct usbvision_sbuf {
+ char *data;
+ struct urb *urb;
+};
+
+#define USBVISION_MAGIC_1 0x55
+#define USBVISION_MAGIC_2 0xAA
+#define USBVISION_HEADER_LENGTH 0x0c
+#define USBVISION_SAA7111_ADDR 0x48
+#define USBVISION_SAA7113_ADDR 0x4a
+#define USBVISION_IIC_LRACK 0x20
+#define USBVISION_IIC_LRNACK 0x30
+#define USBVISION_FRAME_FORMAT_PARAM_INTRA (1<<7)
+
+struct usbvision_v4l2_format_st {
+ int supported;
+ int bytes_per_pixel;
+ int depth;
+ int format;
+ char *desc;
+};
+#define USBVISION_SUPPORTED_PALETTES ARRAY_SIZE(usbvision_v4l2_format)
+
+struct usbvision_frame_header {
+ unsigned char magic_1; /* 0 magic */
+ unsigned char magic_2; /* 1 magic */
+ unsigned char header_length; /* 2 */
+ unsigned char frame_num; /* 3 */
+ unsigned char frame_phase; /* 4 */
+ unsigned char frame_latency; /* 5 */
+ unsigned char data_format; /* 6 */
+ unsigned char format_param; /* 7 */
+ unsigned char frame_width_lo; /* 8 */
+ unsigned char frame_width_hi; /* 9 */
+ unsigned char frame_height_lo; /* 10 */
+ unsigned char frame_height_hi; /* 11 */
+ __u16 frame_width; /* 8 - 9 after endian correction*/
+ __u16 frame_height; /* 10 - 11 after endian correction*/
+};
+
+struct usbvision_frame {
+ char *data; /* Frame buffer */
+ struct usbvision_frame_header isoc_header; /* Header from stream */
+
+ int width; /* Width application is expecting */
+ int height; /* Height */
+ int index; /* Frame index */
+ int frmwidth; /* Width the frame actually is */
+ int frmheight; /* Height */
+
+ volatile int grabstate; /* State of grabbing */
+ int scanstate; /* State of scanning */
+
+ struct list_head frame;
+
+ int curline; /* Line of frame we're working on */
+
+ long scanlength; /* uncompressed, raw data length of frame */
+ long bytes_read; /* amount of scanlength that has been read from data */
+ struct usbvision_v4l2_format_st v4l2_format; /* format the user needs*/
+ int v4l2_linesize; /* bytes for one videoline*/
+ struct timeval timestamp;
+ int sequence; /* How many video frames we send to user */
+};
+
+#define CODEC_SAA7113 7113
+#define CODEC_SAA7111 7111
+#define CODEC_WEBCAM 3000
+#define BRIDGE_NT1003 1003
+#define BRIDGE_NT1004 1004
+#define BRIDGE_NT1005 1005
+
+struct usbvision_device_data_st {
+ __u64 video_norm;
+ const char *model_string;
+ int interface; /* to handle special interface number like BELKIN and Hauppauge WinTV-USB II */
+ __u16 codec;
+ unsigned video_channels:3;
+ unsigned audio_channels:2;
+ unsigned radio:1;
+ unsigned vbi:1;
+ unsigned tuner:1;
+ unsigned vin_reg1_override:1; /* Override default value with */
+ unsigned vin_reg2_override:1; /* vin_reg1, vin_reg2, etc. */
+ unsigned dvi_yuv_override:1;
+ __u8 vin_reg1;
+ __u8 vin_reg2;
+ __u8 dvi_yuv;
+ __u8 tuner_type;
+ __s16 x_offset;
+ __s16 y_offset;
+};
+
+/* Declared on usbvision-cards.c */
+extern struct usbvision_device_data_st usbvision_device_data[];
+extern struct usb_device_id usbvision_table[];
+
+struct usb_usbvision {
+ struct v4l2_device v4l2_dev;
+ struct video_device *vdev; /* Video Device */
+ struct video_device *rdev; /* Radio Device */
+
+ /* i2c Declaration Section*/
+ struct i2c_adapter i2c_adap;
+ int registered_i2c;
+
+ struct urb *ctrl_urb;
+ unsigned char ctrl_urb_buffer[8];
+ int ctrl_urb_busy;
+ struct usb_ctrlrequest ctrl_urb_setup;
+ wait_queue_head_t ctrl_urb_wq; /* Processes waiting */
+
+ /* configuration part */
+ int have_tuner;
+ int tuner_type;
+ int bridge_type; /* NT1003, NT1004, NT1005 */
+ int radio;
+ int video_inputs; /* # of inputs */
+ unsigned long freq;
+ int audio_mute;
+ int audio_channel;
+ int isoc_mode; /* format of video data for the usb isoc-transfer */
+ unsigned int nr; /* Number of the device */
+
+ /* Device structure */
+ struct usb_device *dev;
+ /* usb transfer */
+ int num_alt; /* Number of alternative settings */
+ unsigned int *alt_max_pkt_size; /* array of max_packet_size */
+ unsigned char iface; /* Video interface number */
+ unsigned char iface_alt; /* Alt settings */
+ unsigned char vin_reg2_preset;
+ struct mutex v4l2_lock;
+ struct timer_list power_off_timer;
+ struct work_struct power_off_work;
+ int power; /* is the device powered on? */
+ int user; /* user count for exclusive use */
+ int initialized; /* Had we already sent init sequence? */
+ int dev_model; /* What type of USBVISION device we got? */
+ enum stream_state streaming; /* Are we streaming Isochronous? */
+ int last_error; /* What calamity struck us? */
+ int curwidth; /* width of the frame the device is currently set to*/
+ int curheight; /* height of the frame the device is currently set to*/
+ int stretch_width; /* stretch-factor for frame width (from usb to screen)*/
+ int stretch_height; /* stretch-factor for frame height (from usb to screen)*/
+ char *fbuf; /* Videodev buffer area for mmap*/
+ int max_frame_size; /* Bytes in one video frame */
+ int fbuf_size; /* Videodev buffer size */
+ spinlock_t queue_lock; /* spinlock for protecting mods on inqueue and outqueue */
+ struct list_head inqueue, outqueue; /* queued frame list and ready to dequeue frame list */
+ wait_queue_head_t wait_frame; /* Processes waiting */
+ wait_queue_head_t wait_stream; /* Processes waiting */
+ struct usbvision_frame *cur_frame; /* pointer to current frame, set by usbvision_find_header */
+ struct usbvision_frame frame[USBVISION_NUMFRAMES]; /* frame buffer */
+ int num_frames; /* number of frames allocated */
+ struct usbvision_sbuf sbuf[USBVISION_NUMSBUF]; /* S buffering */
+ volatile int remove_pending; /* If set then about to exit */
+
+ /* Scratch space from the Isochronous Pipe.*/
+ unsigned char *scratch;
+ int scratch_read_ptr;
+ int scratch_write_ptr;
+ int scratch_headermarker[USBVISION_NUM_HEADERMARKER];
+ int scratch_headermarker_read_ptr;
+ int scratch_headermarker_write_ptr;
+ enum isoc_state isocstate;
+ struct usbvision_v4l2_format_st palette;
+
+ struct v4l2_capability vcap; /* Video capabilities */
+ unsigned int ctl_input; /* selected input */
+ v4l2_std_id tvnorm_id; /* selected tv norm */
+ unsigned char video_endp; /* 0x82 for USBVISION devices based */
+
+ /* Decompression stuff: */
+ unsigned char *intra_frame_buffer; /* Buffer for reference frame */
+ int block_pos; /* for test only */
+ int request_intra; /* 0 = normal; 1 = intra frame is requested; */
+ int last_isoc_frame_num; /* check for lost isoc frames */
+ int isoc_packet_size; /* need to calculate used_bandwidth */
+ int used_bandwidth; /* used bandwidth 0-100%, need to set compr_level */
+ int compr_level; /* How strong (100) or weak (0) is compression */
+ int last_compr_level; /* How strong (100) or weak (0) was compression */
+ int usb_bandwidth; /* Mbit/s */
+
+ /* Statistics that can be overlayed on the screen */
+ unsigned long isoc_urb_count; /* How many URBs we received so far */
+ unsigned long urb_length; /* Length of last URB */
+ unsigned long isoc_data_count; /* How many bytes we received */
+ unsigned long header_count; /* How many frame headers we found */
+ unsigned long scratch_ovf_count; /* How many times we overflowed scratch */
+ unsigned long isoc_skip_count; /* How many empty ISO packets received */
+ unsigned long isoc_err_count; /* How many bad ISO packets received */
+ unsigned long isoc_packet_count; /* How many packets we totally got */
+ unsigned long time_in_irq; /* How long do we need for interrupt */
+ int isoc_measure_bandwidth_count;
+ int frame_num; /* How many video frames we send to user */
+ int max_strip_len; /* How big is the biggest strip */
+ int comprblock_pos;
+ int strip_len_errors; /* How many times was block_pos greater than strip_len */
+ int strip_magic_errors;
+ int strip_line_number_errors;
+ int compr_block_types[4];
+};
+
+static inline struct usb_usbvision *to_usbvision(struct v4l2_device *v4l2_dev)
+{
+ return container_of(v4l2_dev, struct usb_usbvision, v4l2_dev);
+}
+
+#define call_all(usbvision, o, f, args...) \
+ v4l2_device_call_all(&usbvision->v4l2_dev, 0, o, f, ##args)
+
+/* --------------------------------------------------------------- */
+/* defined in usbvision-i2c.c */
+/* i2c-algo-usb declaration */
+/* --------------------------------------------------------------- */
+
+/* ----------------------------------------------------------------------- */
+/* usbvision specific I2C functions */
+/* ----------------------------------------------------------------------- */
+int usbvision_i2c_register(struct usb_usbvision *usbvision);
+int usbvision_i2c_unregister(struct usb_usbvision *usbvision);
+
+/* defined in usbvision-core.c */
+int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg);
+int usbvision_write_reg(struct usb_usbvision *usbvision, unsigned char reg,
+ unsigned char value);
+
+int usbvision_frames_alloc(struct usb_usbvision *usbvision, int number_of_frames);
+void usbvision_frames_free(struct usb_usbvision *usbvision);
+int usbvision_scratch_alloc(struct usb_usbvision *usbvision);
+void usbvision_scratch_free(struct usb_usbvision *usbvision);
+int usbvision_decompress_alloc(struct usb_usbvision *usbvision);
+void usbvision_decompress_free(struct usb_usbvision *usbvision);
+
+int usbvision_setup(struct usb_usbvision *usbvision, int format);
+int usbvision_init_isoc(struct usb_usbvision *usbvision);
+int usbvision_restart_isoc(struct usb_usbvision *usbvision);
+void usbvision_stop_isoc(struct usb_usbvision *usbvision);
+int usbvision_set_alternate(struct usb_usbvision *dev);
+
+int usbvision_set_audio(struct usb_usbvision *usbvision, int audio_channel);
+int usbvision_audio_off(struct usb_usbvision *usbvision);
+
+int usbvision_begin_streaming(struct usb_usbvision *usbvision);
+void usbvision_empty_framequeues(struct usb_usbvision *dev);
+int usbvision_stream_interrupt(struct usb_usbvision *dev);
+
+int usbvision_muxsel(struct usb_usbvision *usbvision, int channel);
+int usbvision_set_input(struct usb_usbvision *usbvision);
+int usbvision_set_output(struct usb_usbvision *usbvision, int width, int height);
+
+void usbvision_init_power_off_timer(struct usb_usbvision *usbvision);
+void usbvision_set_power_off_timer(struct usb_usbvision *usbvision);
+void usbvision_reset_power_off_timer(struct usb_usbvision *usbvision);
+int usbvision_power_off(struct usb_usbvision *usbvision);
+int usbvision_power_on(struct usb_usbvision *usbvision);
+
+#endif /* __LINUX_USBVISION_H */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/uvc/Kconfig b/drivers/media/video/uvc/Kconfig
new file mode 100644
index 00000000..6c197da5
--- /dev/null
+++ b/drivers/media/video/uvc/Kconfig
@@ -0,0 +1,18 @@
+config USB_VIDEO_CLASS
+ tristate "USB Video Class (UVC)"
+ select VIDEOBUF2_VMALLOC
+ ---help---
+ Support for the USB Video Class (UVC). Currently only video
+ input devices, such as webcams, are supported.
+
+ For more information see: <http://linux-uvc.berlios.de/>
+
+config USB_VIDEO_CLASS_INPUT_EVDEV
+ bool "UVC input events device support"
+ default y
+ depends on USB_VIDEO_CLASS=INPUT || INPUT=y
+ ---help---
+ This option makes USB Video Class devices register an input device
+ to report button events.
+
+ If you are in doubt, say Y.
diff --git a/drivers/media/video/uvc/Makefile b/drivers/media/video/uvc/Makefile
new file mode 100644
index 00000000..c26d12fd
--- /dev/null
+++ b/drivers/media/video/uvc/Makefile
@@ -0,0 +1,6 @@
+uvcvideo-objs := uvc_driver.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_ctrl.o \
+ uvc_status.o uvc_isight.o uvc_debugfs.o
+ifeq ($(CONFIG_MEDIA_CONTROLLER),y)
+uvcvideo-objs += uvc_entity.o
+endif
+obj-$(CONFIG_USB_VIDEO_CLASS) += uvcvideo.o
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c
new file mode 100644
index 00000000..0efd3b10
--- /dev/null
+++ b/drivers/media/video/uvc/uvc_ctrl.c
@@ -0,0 +1,1934 @@
+/*
+ * uvc_ctrl.c -- USB Video Class driver - Controls
+ *
+ * Copyright (C) 2005-2010
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+#include <linux/atomic.h>
+
+#include "uvcvideo.h"
+
+#define UVC_CTRL_DATA_CURRENT 0
+#define UVC_CTRL_DATA_BACKUP 1
+#define UVC_CTRL_DATA_MIN 2
+#define UVC_CTRL_DATA_MAX 3
+#define UVC_CTRL_DATA_RES 4
+#define UVC_CTRL_DATA_DEF 5
+#define UVC_CTRL_DATA_LAST 6
+
+/* ------------------------------------------------------------------------
+ * Controls
+ */
+
+static struct uvc_control_info uvc_ctrls[] = {
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_BRIGHTNESS_CONTROL,
+ .index = 0,
+ .size = 2,
+ .flags = UVC_CTRL_FLAG_SET_CUR
+ | UVC_CTRL_FLAG_GET_RANGE
+ | UVC_CTRL_FLAG_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_CONTRAST_CONTROL,
+ .index = 1,
+ .size = 2,
+ .flags = UVC_CTRL_FLAG_SET_CUR
+ | UVC_CTRL_FLAG_GET_RANGE
+ | UVC_CTRL_FLAG_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_HUE_CONTROL,
+ .index = 2,
+ .size = 2,
+ .flags = UVC_CTRL_FLAG_SET_CUR
+ | UVC_CTRL_FLAG_GET_RANGE
+ | UVC_CTRL_FLAG_RESTORE
+ | UVC_CTRL_FLAG_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_SATURATION_CONTROL,
+ .index = 3,
+ .size = 2,
+ .flags = UVC_CTRL_FLAG_SET_CUR
+ | UVC_CTRL_FLAG_GET_RANGE
+ | UVC_CTRL_FLAG_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_SHARPNESS_CONTROL,
+ .index = 4,
+ .size = 2,
+ .flags = UVC_CTRL_FLAG_SET_CUR
+ | UVC_CTRL_FLAG_GET_RANGE
+ | UVC_CTRL_FLAG_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_GAMMA_CONTROL,
+ .index = 5,
+ .size = 2,
+ .flags = UVC_CTRL_FLAG_SET_CUR
+ | UVC_CTRL_FLAG_GET_RANGE
+ | UVC_CTRL_FLAG_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL,
+ .index = 6,
+ .size = 2,
+ .flags = UVC_CTRL_FLAG_SET_CUR
+ | UVC_CTRL_FLAG_GET_RANGE
+ | UVC_CTRL_FLAG_RESTORE
+ | UVC_CTRL_FLAG_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL,
+ .index = 7,
+ .size = 4,
+ .flags = UVC_CTRL_FLAG_SET_CUR
+ | UVC_CTRL_FLAG_GET_RANGE
+ | UVC_CTRL_FLAG_RESTORE
+ | UVC_CTRL_FLAG_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_BACKLIGHT_COMPENSATION_CONTROL,
+ .index = 8,
+ .size = 2,
+ .flags = UVC_CTRL_FLAG_SET_CUR
+ | UVC_CTRL_FLAG_GET_RANGE
+ | UVC_CTRL_FLAG_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_GAIN_CONTROL,
+ .index = 9,
+ .size = 2,
+ .flags = UVC_CTRL_FLAG_SET_CUR
+ | UVC_CTRL_FLAG_GET_RANGE
+ | UVC_CTRL_FLAG_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
+ .index = 10,
+ .size = 1,
+ .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
+ | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_HUE_AUTO_CONTROL,
+ .index = 11,
+ .size = 1,
+ .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
+ | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL,
+ .index = 12,
+ .size = 1,
+ .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
+ | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL,
+ .index = 13,
+ .size = 1,
+ .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
+ | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_DIGITAL_MULTIPLIER_CONTROL,
+ .index = 14,
+ .size = 2,
+ .flags = UVC_CTRL_FLAG_SET_CUR
+ | UVC_CTRL_FLAG_GET_RANGE
+ | UVC_CTRL_FLAG_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL,
+ .index = 15,
+ .size = 2,
+ .flags = UVC_CTRL_FLAG_SET_CUR
+ | UVC_CTRL_FLAG_GET_RANGE
+ | UVC_CTRL_FLAG_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_ANALOG_VIDEO_STANDARD_CONTROL,
+ .index = 16,
+ .size = 1,
+ .flags = UVC_CTRL_FLAG_GET_CUR,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_ANALOG_LOCK_STATUS_CONTROL,
+ .index = 17,
+ .size = 1,
+ .flags = UVC_CTRL_FLAG_GET_CUR,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_SCANNING_MODE_CONTROL,
+ .index = 0,
+ .size = 1,
+ .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
+ | UVC_CTRL_FLAG_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_AE_MODE_CONTROL,
+ .index = 1,
+ .size = 1,
+ .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
+ | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_GET_RES
+ | UVC_CTRL_FLAG_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_AE_PRIORITY_CONTROL,
+ .index = 2,
+ .size = 1,
+ .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
+ | UVC_CTRL_FLAG_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL,
+ .index = 3,
+ .size = 4,
+ .flags = UVC_CTRL_FLAG_SET_CUR
+ | UVC_CTRL_FLAG_GET_RANGE
+ | UVC_CTRL_FLAG_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_EXPOSURE_TIME_RELATIVE_CONTROL,
+ .index = 4,
+ .size = 1,
+ .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_FOCUS_ABSOLUTE_CONTROL,
+ .index = 5,
+ .size = 2,
+ .flags = UVC_CTRL_FLAG_SET_CUR
+ | UVC_CTRL_FLAG_GET_RANGE
+ | UVC_CTRL_FLAG_RESTORE
+ | UVC_CTRL_FLAG_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_FOCUS_RELATIVE_CONTROL,
+ .index = 6,
+ .size = 2,
+ .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_MIN
+ | UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES
+ | UVC_CTRL_FLAG_GET_DEF
+ | UVC_CTRL_FLAG_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_IRIS_ABSOLUTE_CONTROL,
+ .index = 7,
+ .size = 2,
+ .flags = UVC_CTRL_FLAG_SET_CUR
+ | UVC_CTRL_FLAG_GET_RANGE
+ | UVC_CTRL_FLAG_RESTORE
+ | UVC_CTRL_FLAG_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_IRIS_RELATIVE_CONTROL,
+ .index = 8,
+ .size = 1,
+ .flags = UVC_CTRL_FLAG_SET_CUR
+ | UVC_CTRL_FLAG_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_ZOOM_ABSOLUTE_CONTROL,
+ .index = 9,
+ .size = 2,
+ .flags = UVC_CTRL_FLAG_SET_CUR
+ | UVC_CTRL_FLAG_GET_RANGE
+ | UVC_CTRL_FLAG_RESTORE
+ | UVC_CTRL_FLAG_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_ZOOM_RELATIVE_CONTROL,
+ .index = 10,
+ .size = 3,
+ .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_MIN
+ | UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES
+ | UVC_CTRL_FLAG_GET_DEF
+ | UVC_CTRL_FLAG_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
+ .index = 11,
+ .size = 8,
+ .flags = UVC_CTRL_FLAG_SET_CUR
+ | UVC_CTRL_FLAG_GET_RANGE
+ | UVC_CTRL_FLAG_RESTORE
+ | UVC_CTRL_FLAG_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_PANTILT_RELATIVE_CONTROL,
+ .index = 12,
+ .size = 4,
+ .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_MIN
+ | UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES
+ | UVC_CTRL_FLAG_GET_DEF
+ | UVC_CTRL_FLAG_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_ROLL_ABSOLUTE_CONTROL,
+ .index = 13,
+ .size = 2,
+ .flags = UVC_CTRL_FLAG_SET_CUR
+ | UVC_CTRL_FLAG_GET_RANGE
+ | UVC_CTRL_FLAG_RESTORE
+ | UVC_CTRL_FLAG_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_ROLL_RELATIVE_CONTROL,
+ .index = 14,
+ .size = 2,
+ .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_MIN
+ | UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES
+ | UVC_CTRL_FLAG_GET_DEF
+ | UVC_CTRL_FLAG_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_FOCUS_AUTO_CONTROL,
+ .index = 17,
+ .size = 1,
+ .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
+ | UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_PRIVACY_CONTROL,
+ .index = 18,
+ .size = 1,
+ .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
+ | UVC_CTRL_FLAG_RESTORE
+ | UVC_CTRL_FLAG_AUTO_UPDATE,
+ },
+};
+
+static struct uvc_menu_info power_line_frequency_controls[] = {
+ { 0, "Disabled" },
+ { 1, "50 Hz" },
+ { 2, "60 Hz" },
+};
+
+static struct uvc_menu_info exposure_auto_controls[] = {
+ { 2, "Auto Mode" },
+ { 1, "Manual Mode" },
+ { 4, "Shutter Priority Mode" },
+ { 8, "Aperture Priority Mode" },
+};
+
+static __s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping,
+ __u8 query, const __u8 *data)
+{
+ __s8 zoom = (__s8)data[0];
+
+ switch (query) {
+ case UVC_GET_CUR:
+ return (zoom == 0) ? 0 : (zoom > 0 ? data[2] : -data[2]);
+
+ case UVC_GET_MIN:
+ case UVC_GET_MAX:
+ case UVC_GET_RES:
+ case UVC_GET_DEF:
+ default:
+ return data[2];
+ }
+}
+
+static void uvc_ctrl_set_zoom(struct uvc_control_mapping *mapping,
+ __s32 value, __u8 *data)
+{
+ data[0] = value == 0 ? 0 : (value > 0) ? 1 : 0xff;
+ data[2] = min((int)abs(value), 0xff);
+}
+
+static struct uvc_control_mapping uvc_ctrl_mappings[] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .name = "Brightness",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_BRIGHTNESS_CONTROL,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_SIGNED,
+ },
+ {
+ .id = V4L2_CID_CONTRAST,
+ .name = "Contrast",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_CONTRAST_CONTROL,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_HUE,
+ .name = "Hue",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_HUE_CONTROL,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_SIGNED,
+ },
+ {
+ .id = V4L2_CID_SATURATION,
+ .name = "Saturation",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_SATURATION_CONTROL,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_SHARPNESS,
+ .name = "Sharpness",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_SHARPNESS_CONTROL,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_GAMMA,
+ .name = "Gamma",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_GAMMA_CONTROL,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_BACKLIGHT_COMPENSATION,
+ .name = "Backlight Compensation",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_BACKLIGHT_COMPENSATION_CONTROL,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_GAIN,
+ .name = "Gain",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_GAIN_CONTROL,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_POWER_LINE_FREQUENCY,
+ .name = "Power Line Frequency",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
+ .size = 2,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_MENU,
+ .data_type = UVC_CTRL_DATA_TYPE_ENUM,
+ .menu_info = power_line_frequency_controls,
+ .menu_count = ARRAY_SIZE(power_line_frequency_controls),
+ },
+ {
+ .id = V4L2_CID_HUE_AUTO,
+ .name = "Hue, Auto",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_HUE_AUTO_CONTROL,
+ .size = 1,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
+ .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
+ },
+ {
+ .id = V4L2_CID_EXPOSURE_AUTO,
+ .name = "Exposure, Auto",
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_AE_MODE_CONTROL,
+ .size = 4,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_MENU,
+ .data_type = UVC_CTRL_DATA_TYPE_BITMASK,
+ .menu_info = exposure_auto_controls,
+ .menu_count = ARRAY_SIZE(exposure_auto_controls),
+ },
+ {
+ .id = V4L2_CID_EXPOSURE_AUTO_PRIORITY,
+ .name = "Exposure, Auto Priority",
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_AE_PRIORITY_CONTROL,
+ .size = 1,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
+ .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
+ },
+ {
+ .id = V4L2_CID_EXPOSURE_ABSOLUTE,
+ .name = "Exposure (Absolute)",
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL,
+ .size = 32,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_AUTO_WHITE_BALANCE,
+ .name = "White Balance Temperature, Auto",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL,
+ .size = 1,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
+ .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
+ },
+ {
+ .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE,
+ .name = "White Balance Temperature",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_AUTO_WHITE_BALANCE,
+ .name = "White Balance Component, Auto",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL,
+ .size = 1,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
+ .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
+ },
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .name = "White Balance Blue Component",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_SIGNED,
+ },
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .name = "White Balance Red Component",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL,
+ .size = 16,
+ .offset = 16,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_SIGNED,
+ },
+ {
+ .id = V4L2_CID_FOCUS_ABSOLUTE,
+ .name = "Focus (absolute)",
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_FOCUS_ABSOLUTE_CONTROL,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_FOCUS_AUTO,
+ .name = "Focus, Auto",
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_FOCUS_AUTO_CONTROL,
+ .size = 1,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
+ .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
+ },
+ {
+ .id = V4L2_CID_IRIS_ABSOLUTE,
+ .name = "Iris, Absolute",
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_IRIS_ABSOLUTE_CONTROL,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_IRIS_RELATIVE,
+ .name = "Iris, Relative",
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_IRIS_RELATIVE_CONTROL,
+ .size = 8,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_SIGNED,
+ },
+ {
+ .id = V4L2_CID_ZOOM_ABSOLUTE,
+ .name = "Zoom, Absolute",
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_ZOOM_ABSOLUTE_CONTROL,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_ZOOM_CONTINUOUS,
+ .name = "Zoom, Continuous",
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_ZOOM_RELATIVE_CONTROL,
+ .size = 0,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_SIGNED,
+ .get = uvc_ctrl_get_zoom,
+ .set = uvc_ctrl_set_zoom,
+ },
+ {
+ .id = V4L2_CID_PAN_ABSOLUTE,
+ .name = "Pan (Absolute)",
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
+ .size = 32,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_TILT_ABSOLUTE,
+ .name = "Tilt (Absolute)",
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
+ .size = 32,
+ .offset = 32,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_PRIVACY,
+ .name = "Privacy",
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_PRIVACY_CONTROL,
+ .size = 1,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
+ .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
+ },
+};
+
+/* ------------------------------------------------------------------------
+ * Utility functions
+ */
+
+static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id)
+{
+ return ctrl->uvc_data + id * ctrl->info.size;
+}
+
+static inline int uvc_test_bit(const __u8 *data, int bit)
+{
+ return (data[bit >> 3] >> (bit & 7)) & 1;
+}
+
+static inline void uvc_clear_bit(__u8 *data, int bit)
+{
+ data[bit >> 3] &= ~(1 << (bit & 7));
+}
+
+/* Extract the bit string specified by mapping->offset and mapping->size
+ * from the little-endian data stored at 'data' and return the result as
+ * a signed 32bit integer. Sign extension will be performed if the mapping
+ * references a signed data type.
+ */
+static __s32 uvc_get_le_value(struct uvc_control_mapping *mapping,
+ __u8 query, const __u8 *data)
+{
+ int bits = mapping->size;
+ int offset = mapping->offset;
+ __s32 value = 0;
+ __u8 mask;
+
+ data += offset / 8;
+ offset &= 7;
+ mask = ((1LL << bits) - 1) << offset;
+
+ for (; bits > 0; data++) {
+ __u8 byte = *data & mask;
+ value |= offset > 0 ? (byte >> offset) : (byte << (-offset));
+ bits -= 8 - (offset > 0 ? offset : 0);
+ offset -= 8;
+ mask = (1 << bits) - 1;
+ }
+
+ /* Sign-extend the value if needed. */
+ if (mapping->data_type == UVC_CTRL_DATA_TYPE_SIGNED)
+ value |= -(value & (1 << (mapping->size - 1)));
+
+ return value;
+}
+
+/* Set the bit string specified by mapping->offset and mapping->size
+ * in the little-endian data stored at 'data' to the value 'value'.
+ */
+static void uvc_set_le_value(struct uvc_control_mapping *mapping,
+ __s32 value, __u8 *data)
+{
+ int bits = mapping->size;
+ int offset = mapping->offset;
+ __u8 mask;
+
+ /* According to the v4l2 spec, writing any value to a button control
+ * should result in the action belonging to the button control being
+ * triggered. UVC devices however want to see a 1 written -> override
+ * value.
+ */
+ if (mapping->v4l2_type == V4L2_CTRL_TYPE_BUTTON)
+ value = -1;
+
+ data += offset / 8;
+ offset &= 7;
+
+ for (; bits > 0; data++) {
+ mask = ((1LL << bits) - 1) << offset;
+ *data = (*data & ~mask) | ((value << offset) & mask);
+ value >>= offset ? offset : 8;
+ bits -= 8 - offset;
+ offset = 0;
+ }
+}
+
+/* ------------------------------------------------------------------------
+ * Terminal and unit management
+ */
+
+static const __u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING;
+static const __u8 uvc_camera_guid[16] = UVC_GUID_UVC_CAMERA;
+static const __u8 uvc_media_transport_input_guid[16] =
+ UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT;
+
+static int uvc_entity_match_guid(const struct uvc_entity *entity,
+ const __u8 guid[16])
+{
+ switch (UVC_ENTITY_TYPE(entity)) {
+ case UVC_ITT_CAMERA:
+ return memcmp(uvc_camera_guid, guid, 16) == 0;
+
+ case UVC_ITT_MEDIA_TRANSPORT_INPUT:
+ return memcmp(uvc_media_transport_input_guid, guid, 16) == 0;
+
+ case UVC_VC_PROCESSING_UNIT:
+ return memcmp(uvc_processing_guid, guid, 16) == 0;
+
+ case UVC_VC_EXTENSION_UNIT:
+ return memcmp(entity->extension.guidExtensionCode,
+ guid, 16) == 0;
+
+ default:
+ return 0;
+ }
+}
+
+/* ------------------------------------------------------------------------
+ * UVC Controls
+ */
+
+static void __uvc_find_control(struct uvc_entity *entity, __u32 v4l2_id,
+ struct uvc_control_mapping **mapping, struct uvc_control **control,
+ int next)
+{
+ struct uvc_control *ctrl;
+ struct uvc_control_mapping *map;
+ unsigned int i;
+
+ if (entity == NULL)
+ return;
+
+ for (i = 0; i < entity->ncontrols; ++i) {
+ ctrl = &entity->controls[i];
+ if (!ctrl->initialized)
+ continue;
+
+ list_for_each_entry(map, &ctrl->info.mappings, list) {
+ if ((map->id == v4l2_id) && !next) {
+ *control = ctrl;
+ *mapping = map;
+ return;
+ }
+
+ if ((*mapping == NULL || (*mapping)->id > map->id) &&
+ (map->id > v4l2_id) && next) {
+ *control = ctrl;
+ *mapping = map;
+ }
+ }
+ }
+}
+
+static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
+ __u32 v4l2_id, struct uvc_control_mapping **mapping)
+{
+ struct uvc_control *ctrl = NULL;
+ struct uvc_entity *entity;
+ int next = v4l2_id & V4L2_CTRL_FLAG_NEXT_CTRL;
+
+ *mapping = NULL;
+
+ /* Mask the query flags. */
+ v4l2_id &= V4L2_CTRL_ID_MASK;
+
+ /* Find the control. */
+ list_for_each_entry(entity, &chain->entities, chain) {
+ __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
+ if (ctrl && !next)
+ return ctrl;
+ }
+
+ if (ctrl == NULL && !next)
+ uvc_trace(UVC_TRACE_CONTROL, "Control 0x%08x not found.\n",
+ v4l2_id);
+
+ return ctrl;
+}
+
+static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain,
+ struct uvc_control *ctrl)
+{
+ int ret;
+
+ if (ctrl->info.flags & UVC_CTRL_FLAG_GET_DEF) {
+ ret = uvc_query_ctrl(chain->dev, UVC_GET_DEF, ctrl->entity->id,
+ chain->dev->intfnum, ctrl->info.selector,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF),
+ ctrl->info.size);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN) {
+ ret = uvc_query_ctrl(chain->dev, UVC_GET_MIN, ctrl->entity->id,
+ chain->dev->intfnum, ctrl->info.selector,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN),
+ ctrl->info.size);
+ if (ret < 0)
+ return ret;
+ }
+ if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX) {
+ ret = uvc_query_ctrl(chain->dev, UVC_GET_MAX, ctrl->entity->id,
+ chain->dev->intfnum, ctrl->info.selector,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX),
+ ctrl->info.size);
+ if (ret < 0)
+ return ret;
+ }
+ if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES) {
+ ret = uvc_query_ctrl(chain->dev, UVC_GET_RES, ctrl->entity->id,
+ chain->dev->intfnum, ctrl->info.selector,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES),
+ ctrl->info.size);
+ if (ret < 0) {
+ if (UVC_ENTITY_TYPE(ctrl->entity) !=
+ UVC_VC_EXTENSION_UNIT)
+ return ret;
+
+ /* GET_RES is mandatory for XU controls, but some
+ * cameras still choke on it. Ignore errors and set the
+ * resolution value to zero.
+ */
+ uvc_warn_once(chain->dev, UVC_WARN_XU_GET_RES,
+ "UVC non compliance - GET_RES failed on "
+ "an XU control. Enabling workaround.\n");
+ memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES), 0,
+ ctrl->info.size);
+ }
+ }
+
+ ctrl->cached = 1;
+ return 0;
+}
+
+int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
+ struct v4l2_queryctrl *v4l2_ctrl)
+{
+ struct uvc_control *ctrl;
+ struct uvc_control_mapping *mapping;
+ struct uvc_menu_info *menu;
+ unsigned int i;
+ int ret;
+
+ ret = mutex_lock_interruptible(&chain->ctrl_mutex);
+ if (ret < 0)
+ return -ERESTARTSYS;
+
+ ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping);
+ if (ctrl == NULL) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ memset(v4l2_ctrl, 0, sizeof *v4l2_ctrl);
+ v4l2_ctrl->id = mapping->id;
+ v4l2_ctrl->type = mapping->v4l2_type;
+ strlcpy(v4l2_ctrl->name, mapping->name, sizeof v4l2_ctrl->name);
+ v4l2_ctrl->flags = 0;
+
+ if (!(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
+ v4l2_ctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
+ if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR))
+ v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ if (!ctrl->cached) {
+ ret = uvc_ctrl_populate_cache(chain, ctrl);
+ if (ret < 0)
+ goto done;
+ }
+
+ if (ctrl->info.flags & UVC_CTRL_FLAG_GET_DEF) {
+ v4l2_ctrl->default_value = mapping->get(mapping, UVC_GET_DEF,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF));
+ }
+
+ switch (mapping->v4l2_type) {
+ case V4L2_CTRL_TYPE_MENU:
+ v4l2_ctrl->minimum = 0;
+ v4l2_ctrl->maximum = mapping->menu_count - 1;
+ v4l2_ctrl->step = 1;
+
+ menu = mapping->menu_info;
+ for (i = 0; i < mapping->menu_count; ++i, ++menu) {
+ if (menu->value == v4l2_ctrl->default_value) {
+ v4l2_ctrl->default_value = i;
+ break;
+ }
+ }
+
+ goto done;
+
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ v4l2_ctrl->minimum = 0;
+ v4l2_ctrl->maximum = 1;
+ v4l2_ctrl->step = 1;
+ goto done;
+
+ case V4L2_CTRL_TYPE_BUTTON:
+ v4l2_ctrl->minimum = 0;
+ v4l2_ctrl->maximum = 0;
+ v4l2_ctrl->step = 0;
+ goto done;
+
+ default:
+ break;
+ }
+
+ if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN)
+ v4l2_ctrl->minimum = mapping->get(mapping, UVC_GET_MIN,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
+
+ if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX)
+ v4l2_ctrl->maximum = mapping->get(mapping, UVC_GET_MAX,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
+
+ if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)
+ v4l2_ctrl->step = mapping->get(mapping, UVC_GET_RES,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
+
+done:
+ mutex_unlock(&chain->ctrl_mutex);
+ return ret;
+}
+
+/*
+ * Mapping V4L2 controls to UVC controls can be straighforward if done well.
+ * Most of the UVC controls exist in V4L2, and can be mapped directly. Some
+ * must be grouped (for instance the Red Balance, Blue Balance and Do White
+ * Balance V4L2 controls use the White Balance Component UVC control) or
+ * otherwise translated. The approach we take here is to use a translation
+ * table for the controls that can be mapped directly, and handle the others
+ * manually.
+ */
+int uvc_query_v4l2_menu(struct uvc_video_chain *chain,
+ struct v4l2_querymenu *query_menu)
+{
+ struct uvc_menu_info *menu_info;
+ struct uvc_control_mapping *mapping;
+ struct uvc_control *ctrl;
+ u32 index = query_menu->index;
+ u32 id = query_menu->id;
+ int ret;
+
+ memset(query_menu, 0, sizeof(*query_menu));
+ query_menu->id = id;
+ query_menu->index = index;
+
+ ret = mutex_lock_interruptible(&chain->ctrl_mutex);
+ if (ret < 0)
+ return -ERESTARTSYS;
+
+ ctrl = uvc_find_control(chain, query_menu->id, &mapping);
+ if (ctrl == NULL || mapping->v4l2_type != V4L2_CTRL_TYPE_MENU) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (query_menu->index >= mapping->menu_count) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ menu_info = &mapping->menu_info[query_menu->index];
+
+ if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK &&
+ (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)) {
+ s32 bitmap;
+
+ if (!ctrl->cached) {
+ ret = uvc_ctrl_populate_cache(chain, ctrl);
+ if (ret < 0)
+ goto done;
+ }
+
+ bitmap = mapping->get(mapping, UVC_GET_RES,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
+ if (!(bitmap & menu_info->value)) {
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+ strlcpy(query_menu->name, menu_info->name, sizeof query_menu->name);
+
+done:
+ mutex_unlock(&chain->ctrl_mutex);
+ return ret;
+}
+
+
+/* --------------------------------------------------------------------------
+ * Control transactions
+ *
+ * To make extended set operations as atomic as the hardware allows, controls
+ * are handled using begin/commit/rollback operations.
+ *
+ * At the beginning of a set request, uvc_ctrl_begin should be called to
+ * initialize the request. This function acquires the control lock.
+ *
+ * When setting a control, the new value is stored in the control data field
+ * at position UVC_CTRL_DATA_CURRENT. The control is then marked as dirty for
+ * later processing. If the UVC and V4L2 control sizes differ, the current
+ * value is loaded from the hardware before storing the new value in the data
+ * field.
+ *
+ * After processing all controls in the transaction, uvc_ctrl_commit or
+ * uvc_ctrl_rollback must be called to apply the pending changes to the
+ * hardware or revert them. When applying changes, all controls marked as
+ * dirty will be modified in the UVC device, and the dirty flag will be
+ * cleared. When reverting controls, the control data field
+ * UVC_CTRL_DATA_CURRENT is reverted to its previous value
+ * (UVC_CTRL_DATA_BACKUP) for all dirty controls. Both functions release the
+ * control lock.
+ */
+int uvc_ctrl_begin(struct uvc_video_chain *chain)
+{
+ return mutex_lock_interruptible(&chain->ctrl_mutex) ? -ERESTARTSYS : 0;
+}
+
+static int uvc_ctrl_commit_entity(struct uvc_device *dev,
+ struct uvc_entity *entity, int rollback)
+{
+ struct uvc_control *ctrl;
+ unsigned int i;
+ int ret;
+
+ if (entity == NULL)
+ return 0;
+
+ for (i = 0; i < entity->ncontrols; ++i) {
+ ctrl = &entity->controls[i];
+ if (!ctrl->initialized)
+ continue;
+
+ /* Reset the loaded flag for auto-update controls that were
+ * marked as loaded in uvc_ctrl_get/uvc_ctrl_set to prevent
+ * uvc_ctrl_get from using the cached value.
+ */
+ if (ctrl->info.flags & UVC_CTRL_FLAG_AUTO_UPDATE)
+ ctrl->loaded = 0;
+
+ if (!ctrl->dirty)
+ continue;
+
+ if (!rollback)
+ ret = uvc_query_ctrl(dev, UVC_SET_CUR, ctrl->entity->id,
+ dev->intfnum, ctrl->info.selector,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+ ctrl->info.size);
+ else
+ ret = 0;
+
+ if (rollback || ret < 0)
+ memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
+ ctrl->info.size);
+
+ ctrl->dirty = 0;
+
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback)
+{
+ struct uvc_entity *entity;
+ int ret = 0;
+
+ /* Find the control. */
+ list_for_each_entry(entity, &chain->entities, chain) {
+ ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback);
+ if (ret < 0)
+ goto done;
+ }
+
+done:
+ mutex_unlock(&chain->ctrl_mutex);
+ return ret;
+}
+
+int uvc_ctrl_get(struct uvc_video_chain *chain,
+ struct v4l2_ext_control *xctrl)
+{
+ struct uvc_control *ctrl;
+ struct uvc_control_mapping *mapping;
+ struct uvc_menu_info *menu;
+ unsigned int i;
+ int ret;
+
+ ctrl = uvc_find_control(chain, xctrl->id, &mapping);
+ if (ctrl == NULL || (ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0)
+ return -EINVAL;
+
+ if (!ctrl->loaded) {
+ ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, ctrl->entity->id,
+ chain->dev->intfnum, ctrl->info.selector,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+ ctrl->info.size);
+ if (ret < 0)
+ return ret;
+
+ ctrl->loaded = 1;
+ }
+
+ xctrl->value = mapping->get(mapping, UVC_GET_CUR,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
+
+ if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) {
+ menu = mapping->menu_info;
+ for (i = 0; i < mapping->menu_count; ++i, ++menu) {
+ if (menu->value == xctrl->value) {
+ xctrl->value = i;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int uvc_ctrl_set(struct uvc_video_chain *chain,
+ struct v4l2_ext_control *xctrl)
+{
+ struct uvc_control *ctrl;
+ struct uvc_control_mapping *mapping;
+ s32 value;
+ u32 step;
+ s32 min;
+ s32 max;
+ int ret;
+
+ ctrl = uvc_find_control(chain, xctrl->id, &mapping);
+ if (ctrl == NULL || (ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR) == 0)
+ return -EINVAL;
+
+ /* Clamp out of range values. */
+ switch (mapping->v4l2_type) {
+ case V4L2_CTRL_TYPE_INTEGER:
+ if (!ctrl->cached) {
+ ret = uvc_ctrl_populate_cache(chain, ctrl);
+ if (ret < 0)
+ return ret;
+ }
+
+ min = mapping->get(mapping, UVC_GET_MIN,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN));
+ max = mapping->get(mapping, UVC_GET_MAX,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX));
+ step = mapping->get(mapping, UVC_GET_RES,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
+ if (step == 0)
+ step = 1;
+
+ xctrl->value = min + (xctrl->value - min + step/2) / step * step;
+ xctrl->value = clamp(xctrl->value, min, max);
+ value = xctrl->value;
+ break;
+
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ xctrl->value = clamp(xctrl->value, 0, 1);
+ value = xctrl->value;
+ break;
+
+ case V4L2_CTRL_TYPE_MENU:
+ if (xctrl->value < 0 || xctrl->value >= mapping->menu_count)
+ return -ERANGE;
+ value = mapping->menu_info[xctrl->value].value;
+
+ /* Valid menu indices are reported by the GET_RES request for
+ * UVC controls that support it.
+ */
+ if (mapping->data_type == UVC_CTRL_DATA_TYPE_BITMASK &&
+ (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES)) {
+ if (!ctrl->cached) {
+ ret = uvc_ctrl_populate_cache(chain, ctrl);
+ if (ret < 0)
+ return ret;
+ }
+
+ step = mapping->get(mapping, UVC_GET_RES,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
+ if (!(step & value))
+ return -ERANGE;
+ }
+
+ break;
+
+ default:
+ value = xctrl->value;
+ break;
+ }
+
+ /* If the mapping doesn't span the whole UVC control, the current value
+ * needs to be loaded from the device to perform the read-modify-write
+ * operation.
+ */
+ if (!ctrl->loaded && (ctrl->info.size * 8) != mapping->size) {
+ if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0) {
+ memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+ 0, ctrl->info.size);
+ } else {
+ ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
+ ctrl->entity->id, chain->dev->intfnum,
+ ctrl->info.selector,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+ ctrl->info.size);
+ if (ret < 0)
+ return ret;
+ }
+
+ ctrl->loaded = 1;
+ }
+
+ /* Backup the current value in case we need to rollback later. */
+ if (!ctrl->dirty) {
+ memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+ ctrl->info.size);
+ }
+
+ mapping->set(mapping, value,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
+
+ ctrl->dirty = 1;
+ ctrl->modified = 1;
+ return 0;
+}
+
+/* --------------------------------------------------------------------------
+ * Dynamic controls
+ */
+
+static void uvc_ctrl_fixup_xu_info(struct uvc_device *dev,
+ const struct uvc_control *ctrl, struct uvc_control_info *info)
+{
+ struct uvc_ctrl_fixup {
+ struct usb_device_id id;
+ u8 entity;
+ u8 selector;
+ u8 flags;
+ };
+
+ static const struct uvc_ctrl_fixup fixups[] = {
+ { { USB_DEVICE(0x046d, 0x08c2) }, 9, 1,
+ UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX |
+ UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_SET_CUR |
+ UVC_CTRL_FLAG_AUTO_UPDATE },
+ { { USB_DEVICE(0x046d, 0x08cc) }, 9, 1,
+ UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX |
+ UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_SET_CUR |
+ UVC_CTRL_FLAG_AUTO_UPDATE },
+ { { USB_DEVICE(0x046d, 0x0994) }, 9, 1,
+ UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX |
+ UVC_CTRL_FLAG_GET_DEF | UVC_CTRL_FLAG_SET_CUR |
+ UVC_CTRL_FLAG_AUTO_UPDATE },
+ };
+
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(fixups); ++i) {
+ if (!usb_match_one_id(dev->intf, &fixups[i].id))
+ continue;
+
+ if (fixups[i].entity == ctrl->entity->id &&
+ fixups[i].selector == info->selector) {
+ info->flags = fixups[i].flags;
+ return;
+ }
+ }
+}
+
+/*
+ * Query control information (size and flags) for XU controls.
+ */
+static int uvc_ctrl_fill_xu_info(struct uvc_device *dev,
+ const struct uvc_control *ctrl, struct uvc_control_info *info)
+{
+ u8 *data;
+ int ret;
+
+ data = kmalloc(2, GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ memcpy(info->entity, ctrl->entity->extension.guidExtensionCode,
+ sizeof(info->entity));
+ info->index = ctrl->index;
+ info->selector = ctrl->index + 1;
+
+ /* Query and verify the control length (GET_LEN) */
+ ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id, dev->intfnum,
+ info->selector, data, 2);
+ if (ret < 0) {
+ uvc_trace(UVC_TRACE_CONTROL,
+ "GET_LEN failed on control %pUl/%u (%d).\n",
+ info->entity, info->selector, ret);
+ goto done;
+ }
+
+ info->size = le16_to_cpup((__le16 *)data);
+
+ /* Query the control information (GET_INFO) */
+ ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, dev->intfnum,
+ info->selector, data, 1);
+ if (ret < 0) {
+ uvc_trace(UVC_TRACE_CONTROL,
+ "GET_INFO failed on control %pUl/%u (%d).\n",
+ info->entity, info->selector, ret);
+ goto done;
+ }
+
+ info->flags = UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX
+ | UVC_CTRL_FLAG_GET_RES | UVC_CTRL_FLAG_GET_DEF
+ | (data[0] & UVC_CONTROL_CAP_GET ?
+ UVC_CTRL_FLAG_GET_CUR : 0)
+ | (data[0] & UVC_CONTROL_CAP_SET ?
+ UVC_CTRL_FLAG_SET_CUR : 0)
+ | (data[0] & UVC_CONTROL_CAP_AUTOUPDATE ?
+ UVC_CTRL_FLAG_AUTO_UPDATE : 0);
+
+ uvc_ctrl_fixup_xu_info(dev, ctrl, info);
+
+ uvc_trace(UVC_TRACE_CONTROL, "XU control %pUl/%u queried: len %u, "
+ "flags { get %u set %u auto %u }.\n",
+ info->entity, info->selector, info->size,
+ (info->flags & UVC_CTRL_FLAG_GET_CUR) ? 1 : 0,
+ (info->flags & UVC_CTRL_FLAG_SET_CUR) ? 1 : 0,
+ (info->flags & UVC_CTRL_FLAG_AUTO_UPDATE) ? 1 : 0);
+
+done:
+ kfree(data);
+ return ret;
+}
+
+static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl,
+ const struct uvc_control_info *info);
+
+static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev,
+ struct uvc_control *ctrl)
+{
+ struct uvc_control_info info;
+ int ret;
+
+ if (ctrl->initialized)
+ return 0;
+
+ ret = uvc_ctrl_fill_xu_info(dev, ctrl, &info);
+ if (ret < 0)
+ return ret;
+
+ ret = uvc_ctrl_add_info(dev, ctrl, &info);
+ if (ret < 0)
+ uvc_trace(UVC_TRACE_CONTROL, "Failed to initialize control "
+ "%pUl/%u on device %s entity %u\n", info.entity,
+ info.selector, dev->udev->devpath, ctrl->entity->id);
+
+ return ret;
+}
+
+int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
+ struct uvc_xu_control_query *xqry)
+{
+ struct uvc_entity *entity;
+ struct uvc_control *ctrl;
+ unsigned int i, found = 0;
+ __u32 reqflags;
+ __u16 size;
+ __u8 *data = NULL;
+ int ret;
+
+ /* Find the extension unit. */
+ list_for_each_entry(entity, &chain->entities, chain) {
+ if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT &&
+ entity->id == xqry->unit)
+ break;
+ }
+
+ if (entity->id != xqry->unit) {
+ uvc_trace(UVC_TRACE_CONTROL, "Extension unit %u not found.\n",
+ xqry->unit);
+ return -ENOENT;
+ }
+
+ /* Find the control and perform delayed initialization if needed. */
+ for (i = 0; i < entity->ncontrols; ++i) {
+ ctrl = &entity->controls[i];
+ if (ctrl->index == xqry->selector - 1) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u not found.\n",
+ entity->extension.guidExtensionCode, xqry->selector);
+ return -ENOENT;
+ }
+
+ if (mutex_lock_interruptible(&chain->ctrl_mutex))
+ return -ERESTARTSYS;
+
+ ret = uvc_ctrl_init_xu_ctrl(chain->dev, ctrl);
+ if (ret < 0) {
+ ret = -ENOENT;
+ goto done;
+ }
+
+ /* Validate the required buffer size and flags for the request */
+ reqflags = 0;
+ size = ctrl->info.size;
+
+ switch (xqry->query) {
+ case UVC_GET_CUR:
+ reqflags = UVC_CTRL_FLAG_GET_CUR;
+ break;
+ case UVC_GET_MIN:
+ reqflags = UVC_CTRL_FLAG_GET_MIN;
+ break;
+ case UVC_GET_MAX:
+ reqflags = UVC_CTRL_FLAG_GET_MAX;
+ break;
+ case UVC_GET_DEF:
+ reqflags = UVC_CTRL_FLAG_GET_DEF;
+ break;
+ case UVC_GET_RES:
+ reqflags = UVC_CTRL_FLAG_GET_RES;
+ break;
+ case UVC_SET_CUR:
+ reqflags = UVC_CTRL_FLAG_SET_CUR;
+ break;
+ case UVC_GET_LEN:
+ size = 2;
+ break;
+ case UVC_GET_INFO:
+ size = 1;
+ break;
+ default:
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (size != xqry->size) {
+ ret = -ENOBUFS;
+ goto done;
+ }
+
+ if (reqflags && !(ctrl->info.flags & reqflags)) {
+ ret = -EBADRQC;
+ goto done;
+ }
+
+ data = kmalloc(size, GFP_KERNEL);
+ if (data == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ if (xqry->query == UVC_SET_CUR &&
+ copy_from_user(data, xqry->data, size)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ ret = uvc_query_ctrl(chain->dev, xqry->query, xqry->unit,
+ chain->dev->intfnum, xqry->selector, data, size);
+ if (ret < 0)
+ goto done;
+
+ if (xqry->query != UVC_SET_CUR &&
+ copy_to_user(xqry->data, data, size))
+ ret = -EFAULT;
+done:
+ kfree(data);
+ mutex_unlock(&chain->ctrl_mutex);
+ return ret;
+}
+
+/* --------------------------------------------------------------------------
+ * Suspend/resume
+ */
+
+/*
+ * Restore control values after resume, skipping controls that haven't been
+ * changed.
+ *
+ * TODO
+ * - Don't restore modified controls that are back to their default value.
+ * - Handle restore order (Auto-Exposure Mode should be restored before
+ * Exposure Time).
+ */
+int uvc_ctrl_resume_device(struct uvc_device *dev)
+{
+ struct uvc_control *ctrl;
+ struct uvc_entity *entity;
+ unsigned int i;
+ int ret;
+
+ /* Walk the entities list and restore controls when possible. */
+ list_for_each_entry(entity, &dev->entities, list) {
+
+ for (i = 0; i < entity->ncontrols; ++i) {
+ ctrl = &entity->controls[i];
+
+ if (!ctrl->initialized || !ctrl->modified ||
+ (ctrl->info.flags & UVC_CTRL_FLAG_RESTORE) == 0)
+ continue;
+
+ printk(KERN_INFO "restoring control %pUl/%u/%u\n",
+ ctrl->info.entity, ctrl->info.index,
+ ctrl->info.selector);
+ ctrl->dirty = 1;
+ }
+
+ ret = uvc_ctrl_commit_entity(dev, entity, 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/* --------------------------------------------------------------------------
+ * Control and mapping handling
+ */
+
+/*
+ * Add control information to a given control.
+ */
+static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl,
+ const struct uvc_control_info *info)
+{
+ int ret = 0;
+
+ memcpy(&ctrl->info, info, sizeof(*info));
+ INIT_LIST_HEAD(&ctrl->info.mappings);
+
+ /* Allocate an array to save control values (cur, def, max, etc.) */
+ ctrl->uvc_data = kzalloc(ctrl->info.size * UVC_CTRL_DATA_LAST + 1,
+ GFP_KERNEL);
+ if (ctrl->uvc_data == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ ctrl->initialized = 1;
+
+ uvc_trace(UVC_TRACE_CONTROL, "Added control %pUl/%u to device %s "
+ "entity %u\n", ctrl->info.entity, ctrl->info.selector,
+ dev->udev->devpath, ctrl->entity->id);
+
+done:
+ if (ret < 0)
+ kfree(ctrl->uvc_data);
+ return ret;
+}
+
+/*
+ * Add a control mapping to a given control.
+ */
+static int __uvc_ctrl_add_mapping(struct uvc_device *dev,
+ struct uvc_control *ctrl, const struct uvc_control_mapping *mapping)
+{
+ struct uvc_control_mapping *map;
+ unsigned int size;
+
+ /* Most mappings come from static kernel data and need to be duplicated.
+ * Mappings that come from userspace will be unnecessarily duplicated,
+ * this could be optimized.
+ */
+ map = kmemdup(mapping, sizeof(*mapping), GFP_KERNEL);
+ if (map == NULL)
+ return -ENOMEM;
+
+ size = sizeof(*mapping->menu_info) * mapping->menu_count;
+ map->menu_info = kmemdup(mapping->menu_info, size, GFP_KERNEL);
+ if (map->menu_info == NULL) {
+ kfree(map);
+ return -ENOMEM;
+ }
+
+ if (map->get == NULL)
+ map->get = uvc_get_le_value;
+ if (map->set == NULL)
+ map->set = uvc_set_le_value;
+
+ map->ctrl = &ctrl->info;
+ list_add_tail(&map->list, &ctrl->info.mappings);
+ uvc_trace(UVC_TRACE_CONTROL,
+ "Adding mapping '%s' to control %pUl/%u.\n",
+ map->name, ctrl->info.entity, ctrl->info.selector);
+
+ return 0;
+}
+
+int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
+ const struct uvc_control_mapping *mapping)
+{
+ struct uvc_device *dev = chain->dev;
+ struct uvc_control_mapping *map;
+ struct uvc_entity *entity;
+ struct uvc_control *ctrl;
+ int found = 0;
+ int ret;
+
+ if (mapping->id & ~V4L2_CTRL_ID_MASK) {
+ uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', control "
+ "id 0x%08x is invalid.\n", mapping->name,
+ mapping->id);
+ return -EINVAL;
+ }
+
+ /* Search for the matching (GUID/CS) control on the current chain */
+ list_for_each_entry(entity, &chain->entities, chain) {
+ unsigned int i;
+
+ if (UVC_ENTITY_TYPE(entity) != UVC_VC_EXTENSION_UNIT ||
+ !uvc_entity_match_guid(entity, mapping->entity))
+ continue;
+
+ for (i = 0; i < entity->ncontrols; ++i) {
+ ctrl = &entity->controls[i];
+ if (ctrl->index == mapping->selector - 1) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found)
+ break;
+ }
+ if (!found)
+ return -ENOENT;
+
+ if (mutex_lock_interruptible(&chain->ctrl_mutex))
+ return -ERESTARTSYS;
+
+ /* Perform delayed initialization of XU controls */
+ ret = uvc_ctrl_init_xu_ctrl(dev, ctrl);
+ if (ret < 0) {
+ ret = -ENOENT;
+ goto done;
+ }
+
+ list_for_each_entry(map, &ctrl->info.mappings, list) {
+ if (mapping->id == map->id) {
+ uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', "
+ "control id 0x%08x already exists.\n",
+ mapping->name, mapping->id);
+ ret = -EEXIST;
+ goto done;
+ }
+ }
+
+ /* Prevent excess memory consumption */
+ if (atomic_inc_return(&dev->nmappings) > UVC_MAX_CONTROL_MAPPINGS) {
+ atomic_dec(&dev->nmappings);
+ uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', maximum "
+ "mappings count (%u) exceeded.\n", mapping->name,
+ UVC_MAX_CONTROL_MAPPINGS);
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ ret = __uvc_ctrl_add_mapping(dev, ctrl, mapping);
+ if (ret < 0)
+ atomic_dec(&dev->nmappings);
+
+done:
+ mutex_unlock(&chain->ctrl_mutex);
+ return ret;
+}
+
+/*
+ * Prune an entity of its bogus controls using a blacklist. Bogus controls
+ * are currently the ones that crash the camera or unconditionally return an
+ * error when queried.
+ */
+static void uvc_ctrl_prune_entity(struct uvc_device *dev,
+ struct uvc_entity *entity)
+{
+ struct uvc_ctrl_blacklist {
+ struct usb_device_id id;
+ u8 index;
+ };
+
+ static const struct uvc_ctrl_blacklist processing_blacklist[] = {
+ { { USB_DEVICE(0x13d3, 0x509b) }, 9 }, /* Gain */
+ { { USB_DEVICE(0x1c4f, 0x3000) }, 6 }, /* WB Temperature */
+ { { USB_DEVICE(0x5986, 0x0241) }, 2 }, /* Hue */
+ };
+ static const struct uvc_ctrl_blacklist camera_blacklist[] = {
+ { { USB_DEVICE(0x06f8, 0x3005) }, 9 }, /* Zoom, Absolute */
+ };
+
+ const struct uvc_ctrl_blacklist *blacklist;
+ unsigned int size;
+ unsigned int count;
+ unsigned int i;
+ u8 *controls;
+
+ switch (UVC_ENTITY_TYPE(entity)) {
+ case UVC_VC_PROCESSING_UNIT:
+ blacklist = processing_blacklist;
+ count = ARRAY_SIZE(processing_blacklist);
+ controls = entity->processing.bmControls;
+ size = entity->processing.bControlSize;
+ break;
+
+ case UVC_ITT_CAMERA:
+ blacklist = camera_blacklist;
+ count = ARRAY_SIZE(camera_blacklist);
+ controls = entity->camera.bmControls;
+ size = entity->camera.bControlSize;
+ break;
+
+ default:
+ return;
+ }
+
+ for (i = 0; i < count; ++i) {
+ if (!usb_match_one_id(dev->intf, &blacklist[i].id))
+ continue;
+
+ if (blacklist[i].index >= 8 * size ||
+ !uvc_test_bit(controls, blacklist[i].index))
+ continue;
+
+ uvc_trace(UVC_TRACE_CONTROL, "%u/%u control is black listed, "
+ "removing it.\n", entity->id, blacklist[i].index);
+
+ uvc_clear_bit(controls, blacklist[i].index);
+ }
+}
+
+/*
+ * Add control information and hardcoded stock control mappings to the given
+ * device.
+ */
+static void uvc_ctrl_init_ctrl(struct uvc_device *dev, struct uvc_control *ctrl)
+{
+ const struct uvc_control_info *info = uvc_ctrls;
+ const struct uvc_control_info *iend = info + ARRAY_SIZE(uvc_ctrls);
+ const struct uvc_control_mapping *mapping = uvc_ctrl_mappings;
+ const struct uvc_control_mapping *mend =
+ mapping + ARRAY_SIZE(uvc_ctrl_mappings);
+
+ /* XU controls initialization requires querying the device for control
+ * information. As some buggy UVC devices will crash when queried
+ * repeatedly in a tight loop, delay XU controls initialization until
+ * first use.
+ */
+ if (UVC_ENTITY_TYPE(ctrl->entity) == UVC_VC_EXTENSION_UNIT)
+ return;
+
+ for (; info < iend; ++info) {
+ if (uvc_entity_match_guid(ctrl->entity, info->entity) &&
+ ctrl->index == info->index) {
+ uvc_ctrl_add_info(dev, ctrl, info);
+ break;
+ }
+ }
+
+ if (!ctrl->initialized)
+ return;
+
+ for (; mapping < mend; ++mapping) {
+ if (uvc_entity_match_guid(ctrl->entity, mapping->entity) &&
+ ctrl->info.selector == mapping->selector)
+ __uvc_ctrl_add_mapping(dev, ctrl, mapping);
+ }
+}
+
+/*
+ * Initialize device controls.
+ */
+int uvc_ctrl_init_device(struct uvc_device *dev)
+{
+ struct uvc_entity *entity;
+ unsigned int i;
+
+ /* Walk the entities list and instantiate controls */
+ list_for_each_entry(entity, &dev->entities, list) {
+ struct uvc_control *ctrl;
+ unsigned int bControlSize = 0, ncontrols = 0;
+ __u8 *bmControls = NULL;
+
+ if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) {
+ bmControls = entity->extension.bmControls;
+ bControlSize = entity->extension.bControlSize;
+ } else if (UVC_ENTITY_TYPE(entity) == UVC_VC_PROCESSING_UNIT) {
+ bmControls = entity->processing.bmControls;
+ bControlSize = entity->processing.bControlSize;
+ } else if (UVC_ENTITY_TYPE(entity) == UVC_ITT_CAMERA) {
+ bmControls = entity->camera.bmControls;
+ bControlSize = entity->camera.bControlSize;
+ }
+
+ /* Remove bogus/blacklisted controls */
+ uvc_ctrl_prune_entity(dev, entity);
+
+ /* Count supported controls and allocate the controls array */
+ for (i = 0; i < bControlSize; ++i)
+ ncontrols += hweight8(bmControls[i]);
+ if (ncontrols == 0)
+ continue;
+
+ entity->controls = kcalloc(ncontrols, sizeof(*ctrl),
+ GFP_KERNEL);
+ if (entity->controls == NULL)
+ return -ENOMEM;
+ entity->ncontrols = ncontrols;
+
+ /* Initialize all supported controls */
+ ctrl = entity->controls;
+ for (i = 0; i < bControlSize * 8; ++i) {
+ if (uvc_test_bit(bmControls, i) == 0)
+ continue;
+
+ ctrl->entity = entity;
+ ctrl->index = i;
+
+ uvc_ctrl_init_ctrl(dev, ctrl);
+ ctrl++;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Cleanup device controls.
+ */
+static void uvc_ctrl_cleanup_mappings(struct uvc_device *dev,
+ struct uvc_control *ctrl)
+{
+ struct uvc_control_mapping *mapping, *nm;
+
+ list_for_each_entry_safe(mapping, nm, &ctrl->info.mappings, list) {
+ list_del(&mapping->list);
+ kfree(mapping->menu_info);
+ kfree(mapping);
+ }
+}
+
+void uvc_ctrl_cleanup_device(struct uvc_device *dev)
+{
+ struct uvc_entity *entity;
+ unsigned int i;
+
+ /* Free controls and control mappings for all entities. */
+ list_for_each_entry(entity, &dev->entities, list) {
+ for (i = 0; i < entity->ncontrols; ++i) {
+ struct uvc_control *ctrl = &entity->controls[i];
+
+ if (!ctrl->initialized)
+ continue;
+
+ uvc_ctrl_cleanup_mappings(dev, ctrl);
+ kfree(ctrl->uvc_data);
+ }
+
+ kfree(entity->controls);
+ }
+}
diff --git a/drivers/media/video/uvc/uvc_debugfs.c b/drivers/media/video/uvc/uvc_debugfs.c
new file mode 100644
index 00000000..14561a5a
--- /dev/null
+++ b/drivers/media/video/uvc/uvc_debugfs.c
@@ -0,0 +1,136 @@
+/*
+ * uvc_debugfs.c -- USB Video Class driver - Debugging support
+ *
+ * Copyright (C) 2011
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#include "uvcvideo.h"
+
+/* -----------------------------------------------------------------------------
+ * Statistics
+ */
+
+#define UVC_DEBUGFS_BUF_SIZE 1024
+
+struct uvc_debugfs_buffer {
+ size_t count;
+ char data[UVC_DEBUGFS_BUF_SIZE];
+};
+
+static int uvc_debugfs_stats_open(struct inode *inode, struct file *file)
+{
+ struct uvc_streaming *stream = inode->i_private;
+ struct uvc_debugfs_buffer *buf;
+
+ buf = kmalloc(sizeof(*buf), GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ buf->count = uvc_video_stats_dump(stream, buf->data, sizeof(buf->data));
+
+ file->private_data = buf;
+ return 0;
+}
+
+static ssize_t uvc_debugfs_stats_read(struct file *file, char __user *user_buf,
+ size_t nbytes, loff_t *ppos)
+{
+ struct uvc_debugfs_buffer *buf = file->private_data;
+
+ return simple_read_from_buffer(user_buf, nbytes, ppos, buf->data,
+ buf->count);
+}
+
+static int uvc_debugfs_stats_release(struct inode *inode, struct file *file)
+{
+ kfree(file->private_data);
+ file->private_data = NULL;
+
+ return 0;
+}
+
+static const struct file_operations uvc_debugfs_stats_fops = {
+ .owner = THIS_MODULE,
+ .open = uvc_debugfs_stats_open,
+ .llseek = no_llseek,
+ .read = uvc_debugfs_stats_read,
+ .release = uvc_debugfs_stats_release,
+};
+
+/* -----------------------------------------------------------------------------
+ * Global and stream initialization/cleanup
+ */
+
+static struct dentry *uvc_debugfs_root_dir;
+
+int uvc_debugfs_init_stream(struct uvc_streaming *stream)
+{
+ struct usb_device *udev = stream->dev->udev;
+ struct dentry *dent;
+ char dir_name[32];
+
+ if (uvc_debugfs_root_dir == NULL)
+ return -ENODEV;
+
+ sprintf(dir_name, "%u-%u", udev->bus->busnum, udev->devnum);
+
+ dent = debugfs_create_dir(dir_name, uvc_debugfs_root_dir);
+ if (IS_ERR_OR_NULL(dent)) {
+ uvc_printk(KERN_INFO, "Unable to create debugfs %s "
+ "directory.\n", dir_name);
+ return -ENODEV;
+ }
+
+ stream->debugfs_dir = dent;
+
+ dent = debugfs_create_file("stats", 0444, stream->debugfs_dir,
+ stream, &uvc_debugfs_stats_fops);
+ if (IS_ERR_OR_NULL(dent)) {
+ uvc_printk(KERN_INFO, "Unable to create debugfs stats file.\n");
+ uvc_debugfs_cleanup_stream(stream);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+void uvc_debugfs_cleanup_stream(struct uvc_streaming *stream)
+{
+ if (stream->debugfs_dir == NULL)
+ return;
+
+ debugfs_remove_recursive(stream->debugfs_dir);
+ stream->debugfs_dir = NULL;
+}
+
+int uvc_debugfs_init(void)
+{
+ struct dentry *dir;
+
+ dir = debugfs_create_dir("uvcvideo", usb_debug_root);
+ if (IS_ERR_OR_NULL(dir)) {
+ uvc_printk(KERN_INFO, "Unable to create debugfs directory\n");
+ return -ENODATA;
+ }
+
+ uvc_debugfs_root_dir = dir;
+ return 0;
+}
+
+void uvc_debugfs_cleanup(void)
+{
+ if (uvc_debugfs_root_dir != NULL)
+ debugfs_remove_recursive(uvc_debugfs_root_dir);
+}
diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c
new file mode 100644
index 00000000..1d131720
--- /dev/null
+++ b/drivers/media/video/uvc/uvc_driver.c
@@ -0,0 +1,2448 @@
+/*
+ * uvc_driver.c -- USB Video Class driver
+ *
+ * Copyright (C) 2005-2010
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+/*
+ * This driver aims to support video input and ouput devices compliant with the
+ * 'USB Video Class' specification.
+ *
+ * The driver doesn't support the deprecated v4l1 interface. It implements the
+ * mmap capture method only, and doesn't do any image format conversion in
+ * software. If your user-space application doesn't support YUYV or MJPEG, fix
+ * it :-). Please note that the MJPEG data have been stripped from their
+ * Huffman tables (DHT marker), you will need to add it back if your JPEG
+ * codec can't handle MJPEG data.
+ */
+
+#include <linux/atomic.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+#include <linux/version.h>
+#include <asm/unaligned.h>
+
+#include <media/v4l2-common.h>
+
+#include "uvcvideo.h"
+
+#define DRIVER_AUTHOR "Laurent Pinchart " \
+ "<laurent.pinchart@ideasonboard.com>"
+#define DRIVER_DESC "USB Video Class driver"
+
+unsigned int uvc_clock_param = CLOCK_MONOTONIC;
+unsigned int uvc_no_drop_param;
+static unsigned int uvc_quirks_param = -1;
+unsigned int uvc_trace_param;
+unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT;
+
+/* ------------------------------------------------------------------------
+ * Video formats
+ */
+
+static struct uvc_format_desc uvc_fmts[] = {
+ {
+ .name = "YUV 4:2:2 (YUYV)",
+ .guid = UVC_GUID_FORMAT_YUY2,
+ .fcc = V4L2_PIX_FMT_YUYV,
+ },
+ {
+ .name = "YUV 4:2:2 (YUYV)",
+ .guid = UVC_GUID_FORMAT_YUY2_ISIGHT,
+ .fcc = V4L2_PIX_FMT_YUYV,
+ },
+ {
+ .name = "YUV 4:2:0 (NV12)",
+ .guid = UVC_GUID_FORMAT_NV12,
+ .fcc = V4L2_PIX_FMT_NV12,
+ },
+ {
+ .name = "MJPEG",
+ .guid = UVC_GUID_FORMAT_MJPEG,
+ .fcc = V4L2_PIX_FMT_MJPEG,
+ },
+ {
+ .name = "YVU 4:2:0 (YV12)",
+ .guid = UVC_GUID_FORMAT_YV12,
+ .fcc = V4L2_PIX_FMT_YVU420,
+ },
+ {
+ .name = "YUV 4:2:0 (I420)",
+ .guid = UVC_GUID_FORMAT_I420,
+ .fcc = V4L2_PIX_FMT_YUV420,
+ },
+ {
+ .name = "YUV 4:2:0 (M420)",
+ .guid = UVC_GUID_FORMAT_M420,
+ .fcc = V4L2_PIX_FMT_M420,
+ },
+ {
+ .name = "YUV 4:2:2 (UYVY)",
+ .guid = UVC_GUID_FORMAT_UYVY,
+ .fcc = V4L2_PIX_FMT_UYVY,
+ },
+ {
+ .name = "Greyscale (8-bit)",
+ .guid = UVC_GUID_FORMAT_Y800,
+ .fcc = V4L2_PIX_FMT_GREY,
+ },
+ {
+ .name = "Greyscale (16-bit)",
+ .guid = UVC_GUID_FORMAT_Y16,
+ .fcc = V4L2_PIX_FMT_Y16,
+ },
+ {
+ .name = "RGB Bayer",
+ .guid = UVC_GUID_FORMAT_BY8,
+ .fcc = V4L2_PIX_FMT_SBGGR8,
+ },
+ {
+ .name = "RGB565",
+ .guid = UVC_GUID_FORMAT_RGBP,
+ .fcc = V4L2_PIX_FMT_RGB565,
+ },
+ {
+ .name = "H.264",
+ .guid = UVC_GUID_FORMAT_H264,
+ .fcc = V4L2_PIX_FMT_H264,
+ },
+};
+
+/* ------------------------------------------------------------------------
+ * Utility functions
+ */
+
+struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
+ __u8 epaddr)
+{
+ struct usb_host_endpoint *ep;
+ unsigned int i;
+
+ for (i = 0; i < alts->desc.bNumEndpoints; ++i) {
+ ep = &alts->endpoint[i];
+ if (ep->desc.bEndpointAddress == epaddr)
+ return ep;
+ }
+
+ return NULL;
+}
+
+static struct uvc_format_desc *uvc_format_by_guid(const __u8 guid[16])
+{
+ unsigned int len = ARRAY_SIZE(uvc_fmts);
+ unsigned int i;
+
+ for (i = 0; i < len; ++i) {
+ if (memcmp(guid, uvc_fmts[i].guid, 16) == 0)
+ return &uvc_fmts[i];
+ }
+
+ return NULL;
+}
+
+static __u32 uvc_colorspace(const __u8 primaries)
+{
+ static const __u8 colorprimaries[] = {
+ 0,
+ V4L2_COLORSPACE_SRGB,
+ V4L2_COLORSPACE_470_SYSTEM_M,
+ V4L2_COLORSPACE_470_SYSTEM_BG,
+ V4L2_COLORSPACE_SMPTE170M,
+ V4L2_COLORSPACE_SMPTE240M,
+ };
+
+ if (primaries < ARRAY_SIZE(colorprimaries))
+ return colorprimaries[primaries];
+
+ return 0;
+}
+
+/* Simplify a fraction using a simple continued fraction decomposition. The
+ * idea here is to convert fractions such as 333333/10000000 to 1/30 using
+ * 32 bit arithmetic only. The algorithm is not perfect and relies upon two
+ * arbitrary parameters to remove non-significative terms from the simple
+ * continued fraction decomposition. Using 8 and 333 for n_terms and threshold
+ * respectively seems to give nice results.
+ */
+void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator,
+ unsigned int n_terms, unsigned int threshold)
+{
+ uint32_t *an;
+ uint32_t x, y, r;
+ unsigned int i, n;
+
+ an = kmalloc(n_terms * sizeof *an, GFP_KERNEL);
+ if (an == NULL)
+ return;
+
+ /* Convert the fraction to a simple continued fraction. See
+ * http://mathforum.org/dr.math/faq/faq.fractions.html
+ * Stop if the current term is bigger than or equal to the given
+ * threshold.
+ */
+ x = *numerator;
+ y = *denominator;
+
+ for (n = 0; n < n_terms && y != 0; ++n) {
+ an[n] = x / y;
+ if (an[n] >= threshold) {
+ if (n < 2)
+ n++;
+ break;
+ }
+
+ r = x - an[n] * y;
+ x = y;
+ y = r;
+ }
+
+ /* Expand the simple continued fraction back to an integer fraction. */
+ x = 0;
+ y = 1;
+
+ for (i = n; i > 0; --i) {
+ r = y;
+ y = an[i-1] * y + x;
+ x = r;
+ }
+
+ *numerator = y;
+ *denominator = x;
+ kfree(an);
+}
+
+/* Convert a fraction to a frame interval in 100ns multiples. The idea here is
+ * to compute numerator / denominator * 10000000 using 32 bit fixed point
+ * arithmetic only.
+ */
+uint32_t uvc_fraction_to_interval(uint32_t numerator, uint32_t denominator)
+{
+ uint32_t multiplier;
+
+ /* Saturate the result if the operation would overflow. */
+ if (denominator == 0 ||
+ numerator/denominator >= ((uint32_t)-1)/10000000)
+ return (uint32_t)-1;
+
+ /* Divide both the denominator and the multiplier by two until
+ * numerator * multiplier doesn't overflow. If anyone knows a better
+ * algorithm please let me know.
+ */
+ multiplier = 10000000;
+ while (numerator > ((uint32_t)-1)/multiplier) {
+ multiplier /= 2;
+ denominator /= 2;
+ }
+
+ return denominator ? numerator * multiplier / denominator : 0;
+}
+
+/* ------------------------------------------------------------------------
+ * Terminal and unit management
+ */
+
+struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id)
+{
+ struct uvc_entity *entity;
+
+ list_for_each_entry(entity, &dev->entities, list) {
+ if (entity->id == id)
+ return entity;
+ }
+
+ return NULL;
+}
+
+static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev,
+ int id, struct uvc_entity *entity)
+{
+ unsigned int i;
+
+ if (entity == NULL)
+ entity = list_entry(&dev->entities, struct uvc_entity, list);
+
+ list_for_each_entry_continue(entity, &dev->entities, list) {
+ for (i = 0; i < entity->bNrInPins; ++i)
+ if (entity->baSourceID[i] == id)
+ return entity;
+ }
+
+ return NULL;
+}
+
+static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id)
+{
+ struct uvc_streaming *stream;
+
+ list_for_each_entry(stream, &dev->streams, list) {
+ if (stream->header.bTerminalLink == id)
+ return stream;
+ }
+
+ return NULL;
+}
+
+/* ------------------------------------------------------------------------
+ * Descriptors parsing
+ */
+
+static int uvc_parse_format(struct uvc_device *dev,
+ struct uvc_streaming *streaming, struct uvc_format *format,
+ __u32 **intervals, unsigned char *buffer, int buflen)
+{
+ struct usb_interface *intf = streaming->intf;
+ struct usb_host_interface *alts = intf->cur_altsetting;
+ struct uvc_format_desc *fmtdesc;
+ struct uvc_frame *frame;
+ const unsigned char *start = buffer;
+ unsigned int interval;
+ unsigned int i, n;
+ __u8 ftype;
+
+ format->type = buffer[2];
+ format->index = buffer[3];
+
+ switch (buffer[2]) {
+ case UVC_VS_FORMAT_UNCOMPRESSED:
+ case UVC_VS_FORMAT_FRAME_BASED:
+ n = buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED ? 27 : 28;
+ if (buflen < n) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
+ "interface %d FORMAT error\n",
+ dev->udev->devnum,
+ alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ /* Find the format descriptor from its GUID. */
+ fmtdesc = uvc_format_by_guid(&buffer[5]);
+
+ if (fmtdesc != NULL) {
+ strlcpy(format->name, fmtdesc->name,
+ sizeof format->name);
+ format->fcc = fmtdesc->fcc;
+ } else {
+ uvc_printk(KERN_INFO, "Unknown video format %pUl\n",
+ &buffer[5]);
+ snprintf(format->name, sizeof(format->name), "%pUl\n",
+ &buffer[5]);
+ format->fcc = 0;
+ }
+
+ format->bpp = buffer[21];
+ if (buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED) {
+ ftype = UVC_VS_FRAME_UNCOMPRESSED;
+ } else {
+ ftype = UVC_VS_FRAME_FRAME_BASED;
+ if (buffer[27])
+ format->flags = UVC_FMT_FLAG_COMPRESSED;
+ }
+ break;
+
+ case UVC_VS_FORMAT_MJPEG:
+ if (buflen < 11) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
+ "interface %d FORMAT error\n",
+ dev->udev->devnum,
+ alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ strlcpy(format->name, "MJPEG", sizeof format->name);
+ format->fcc = V4L2_PIX_FMT_MJPEG;
+ format->flags = UVC_FMT_FLAG_COMPRESSED;
+ format->bpp = 0;
+ ftype = UVC_VS_FRAME_MJPEG;
+ break;
+
+ case UVC_VS_FORMAT_DV:
+ if (buflen < 9) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
+ "interface %d FORMAT error\n",
+ dev->udev->devnum,
+ alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ switch (buffer[8] & 0x7f) {
+ case 0:
+ strlcpy(format->name, "SD-DV", sizeof format->name);
+ break;
+ case 1:
+ strlcpy(format->name, "SDL-DV", sizeof format->name);
+ break;
+ case 2:
+ strlcpy(format->name, "HD-DV", sizeof format->name);
+ break;
+ default:
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
+ "interface %d: unknown DV format %u\n",
+ dev->udev->devnum,
+ alts->desc.bInterfaceNumber, buffer[8]);
+ return -EINVAL;
+ }
+
+ strlcat(format->name, buffer[8] & (1 << 7) ? " 60Hz" : " 50Hz",
+ sizeof format->name);
+
+ format->fcc = V4L2_PIX_FMT_DV;
+ format->flags = UVC_FMT_FLAG_COMPRESSED | UVC_FMT_FLAG_STREAM;
+ format->bpp = 0;
+ ftype = 0;
+
+ /* Create a dummy frame descriptor. */
+ frame = &format->frame[0];
+ memset(&format->frame[0], 0, sizeof format->frame[0]);
+ frame->bFrameIntervalType = 1;
+ frame->dwDefaultFrameInterval = 1;
+ frame->dwFrameInterval = *intervals;
+ *(*intervals)++ = 1;
+ format->nframes = 1;
+ break;
+
+ case UVC_VS_FORMAT_MPEG2TS:
+ case UVC_VS_FORMAT_STREAM_BASED:
+ /* Not supported yet. */
+ default:
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
+ "interface %d unsupported format %u\n",
+ dev->udev->devnum, alts->desc.bInterfaceNumber,
+ buffer[2]);
+ return -EINVAL;
+ }
+
+ uvc_trace(UVC_TRACE_DESCR, "Found format %s.\n", format->name);
+
+ buflen -= buffer[0];
+ buffer += buffer[0];
+
+ /* Parse the frame descriptors. Only uncompressed, MJPEG and frame
+ * based formats have frame descriptors.
+ */
+ while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
+ buffer[2] == ftype) {
+ frame = &format->frame[format->nframes];
+ if (ftype != UVC_VS_FRAME_FRAME_BASED)
+ n = buflen > 25 ? buffer[25] : 0;
+ else
+ n = buflen > 21 ? buffer[21] : 0;
+
+ n = n ? n : 3;
+
+ if (buflen < 26 + 4*n) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
+ "interface %d FRAME error\n", dev->udev->devnum,
+ alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ frame->bFrameIndex = buffer[3];
+ frame->bmCapabilities = buffer[4];
+ frame->wWidth = get_unaligned_le16(&buffer[5]);
+ frame->wHeight = get_unaligned_le16(&buffer[7]);
+ frame->dwMinBitRate = get_unaligned_le32(&buffer[9]);
+ frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]);
+ if (ftype != UVC_VS_FRAME_FRAME_BASED) {
+ frame->dwMaxVideoFrameBufferSize =
+ get_unaligned_le32(&buffer[17]);
+ frame->dwDefaultFrameInterval =
+ get_unaligned_le32(&buffer[21]);
+ frame->bFrameIntervalType = buffer[25];
+ } else {
+ frame->dwMaxVideoFrameBufferSize = 0;
+ frame->dwDefaultFrameInterval =
+ get_unaligned_le32(&buffer[17]);
+ frame->bFrameIntervalType = buffer[21];
+ }
+ frame->dwFrameInterval = *intervals;
+
+ /* Several UVC chipsets screw up dwMaxVideoFrameBufferSize
+ * completely. Observed behaviours range from setting the
+ * value to 1.1x the actual frame size to hardwiring the
+ * 16 low bits to 0. This results in a higher than necessary
+ * memory usage as well as a wrong image size information. For
+ * uncompressed formats this can be fixed by computing the
+ * value from the frame size.
+ */
+ if (!(format->flags & UVC_FMT_FLAG_COMPRESSED))
+ frame->dwMaxVideoFrameBufferSize = format->bpp
+ * frame->wWidth * frame->wHeight / 8;
+
+ /* Some bogus devices report dwMinFrameInterval equal to
+ * dwMaxFrameInterval and have dwFrameIntervalStep set to
+ * zero. Setting all null intervals to 1 fixes the problem and
+ * some other divisions by zero that could happen.
+ */
+ for (i = 0; i < n; ++i) {
+ interval = get_unaligned_le32(&buffer[26+4*i]);
+ *(*intervals)++ = interval ? interval : 1;
+ }
+
+ /* Make sure that the default frame interval stays between
+ * the boundaries.
+ */
+ n -= frame->bFrameIntervalType ? 1 : 2;
+ frame->dwDefaultFrameInterval =
+ min(frame->dwFrameInterval[n],
+ max(frame->dwFrameInterval[0],
+ frame->dwDefaultFrameInterval));
+
+ if (dev->quirks & UVC_QUIRK_RESTRICT_FRAME_RATE) {
+ frame->bFrameIntervalType = 1;
+ frame->dwFrameInterval[0] =
+ frame->dwDefaultFrameInterval;
+ }
+
+ uvc_trace(UVC_TRACE_DESCR, "- %ux%u (%u.%u fps)\n",
+ frame->wWidth, frame->wHeight,
+ 10000000/frame->dwDefaultFrameInterval,
+ (100000000/frame->dwDefaultFrameInterval)%10);
+
+ format->nframes++;
+ buflen -= buffer[0];
+ buffer += buffer[0];
+ }
+
+ if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
+ buffer[2] == UVC_VS_STILL_IMAGE_FRAME) {
+ buflen -= buffer[0];
+ buffer += buffer[0];
+ }
+
+ if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
+ buffer[2] == UVC_VS_COLORFORMAT) {
+ if (buflen < 6) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
+ "interface %d COLORFORMAT error\n",
+ dev->udev->devnum,
+ alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ format->colorspace = uvc_colorspace(buffer[3]);
+
+ buflen -= buffer[0];
+ buffer += buffer[0];
+ }
+
+ return buffer - start;
+}
+
+static int uvc_parse_streaming(struct uvc_device *dev,
+ struct usb_interface *intf)
+{
+ struct uvc_streaming *streaming = NULL;
+ struct uvc_format *format;
+ struct uvc_frame *frame;
+ struct usb_host_interface *alts = &intf->altsetting[0];
+ unsigned char *_buffer, *buffer = alts->extra;
+ int _buflen, buflen = alts->extralen;
+ unsigned int nformats = 0, nframes = 0, nintervals = 0;
+ unsigned int size, i, n, p;
+ __u32 *interval;
+ __u16 psize;
+ int ret = -EINVAL;
+
+ if (intf->cur_altsetting->desc.bInterfaceSubClass
+ != UVC_SC_VIDEOSTREAMING) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d interface %d isn't a "
+ "video streaming interface\n", dev->udev->devnum,
+ intf->altsetting[0].desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ if (usb_driver_claim_interface(&uvc_driver.driver, intf, dev)) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d interface %d is already "
+ "claimed\n", dev->udev->devnum,
+ intf->altsetting[0].desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ streaming = kzalloc(sizeof *streaming, GFP_KERNEL);
+ if (streaming == NULL) {
+ usb_driver_release_interface(&uvc_driver.driver, intf);
+ return -EINVAL;
+ }
+
+ mutex_init(&streaming->mutex);
+ streaming->dev = dev;
+ streaming->intf = usb_get_intf(intf);
+ streaming->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
+
+ /* The Pico iMage webcam has its class-specific interface descriptors
+ * after the endpoint descriptors.
+ */
+ if (buflen == 0) {
+ for (i = 0; i < alts->desc.bNumEndpoints; ++i) {
+ struct usb_host_endpoint *ep = &alts->endpoint[i];
+
+ if (ep->extralen == 0)
+ continue;
+
+ if (ep->extralen > 2 &&
+ ep->extra[1] == USB_DT_CS_INTERFACE) {
+ uvc_trace(UVC_TRACE_DESCR, "trying extra data "
+ "from endpoint %u.\n", i);
+ buffer = alts->endpoint[i].extra;
+ buflen = alts->endpoint[i].extralen;
+ break;
+ }
+ }
+ }
+
+ /* Skip the standard interface descriptors. */
+ while (buflen > 2 && buffer[1] != USB_DT_CS_INTERFACE) {
+ buflen -= buffer[0];
+ buffer += buffer[0];
+ }
+
+ if (buflen <= 2) {
+ uvc_trace(UVC_TRACE_DESCR, "no class-specific streaming "
+ "interface descriptors found.\n");
+ goto error;
+ }
+
+ /* Parse the header descriptor. */
+ switch (buffer[2]) {
+ case UVC_VS_OUTPUT_HEADER:
+ streaming->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ size = 9;
+ break;
+
+ case UVC_VS_INPUT_HEADER:
+ streaming->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ size = 13;
+ break;
+
+ default:
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
+ "%d HEADER descriptor not found.\n", dev->udev->devnum,
+ alts->desc.bInterfaceNumber);
+ goto error;
+ }
+
+ p = buflen >= 4 ? buffer[3] : 0;
+ n = buflen >= size ? buffer[size-1] : 0;
+
+ if (buflen < size + p*n) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
+ "interface %d HEADER descriptor is invalid.\n",
+ dev->udev->devnum, alts->desc.bInterfaceNumber);
+ goto error;
+ }
+
+ streaming->header.bNumFormats = p;
+ streaming->header.bEndpointAddress = buffer[6];
+ if (buffer[2] == UVC_VS_INPUT_HEADER) {
+ streaming->header.bmInfo = buffer[7];
+ streaming->header.bTerminalLink = buffer[8];
+ streaming->header.bStillCaptureMethod = buffer[9];
+ streaming->header.bTriggerSupport = buffer[10];
+ streaming->header.bTriggerUsage = buffer[11];
+ } else {
+ streaming->header.bTerminalLink = buffer[7];
+ }
+ streaming->header.bControlSize = n;
+
+ streaming->header.bmaControls = kmemdup(&buffer[size], p * n,
+ GFP_KERNEL);
+ if (streaming->header.bmaControls == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ buflen -= buffer[0];
+ buffer += buffer[0];
+
+ _buffer = buffer;
+ _buflen = buflen;
+
+ /* Count the format and frame descriptors. */
+ while (_buflen > 2 && _buffer[1] == USB_DT_CS_INTERFACE) {
+ switch (_buffer[2]) {
+ case UVC_VS_FORMAT_UNCOMPRESSED:
+ case UVC_VS_FORMAT_MJPEG:
+ case UVC_VS_FORMAT_FRAME_BASED:
+ nformats++;
+ break;
+
+ case UVC_VS_FORMAT_DV:
+ /* DV format has no frame descriptor. We will create a
+ * dummy frame descriptor with a dummy frame interval.
+ */
+ nformats++;
+ nframes++;
+ nintervals++;
+ break;
+
+ case UVC_VS_FORMAT_MPEG2TS:
+ case UVC_VS_FORMAT_STREAM_BASED:
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
+ "interface %d FORMAT %u is not supported.\n",
+ dev->udev->devnum,
+ alts->desc.bInterfaceNumber, _buffer[2]);
+ break;
+
+ case UVC_VS_FRAME_UNCOMPRESSED:
+ case UVC_VS_FRAME_MJPEG:
+ nframes++;
+ if (_buflen > 25)
+ nintervals += _buffer[25] ? _buffer[25] : 3;
+ break;
+
+ case UVC_VS_FRAME_FRAME_BASED:
+ nframes++;
+ if (_buflen > 21)
+ nintervals += _buffer[21] ? _buffer[21] : 3;
+ break;
+ }
+
+ _buflen -= _buffer[0];
+ _buffer += _buffer[0];
+ }
+
+ if (nformats == 0) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
+ "%d has no supported formats defined.\n",
+ dev->udev->devnum, alts->desc.bInterfaceNumber);
+ goto error;
+ }
+
+ size = nformats * sizeof *format + nframes * sizeof *frame
+ + nintervals * sizeof *interval;
+ format = kzalloc(size, GFP_KERNEL);
+ if (format == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ frame = (struct uvc_frame *)&format[nformats];
+ interval = (__u32 *)&frame[nframes];
+
+ streaming->format = format;
+ streaming->nformats = nformats;
+
+ /* Parse the format descriptors. */
+ while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE) {
+ switch (buffer[2]) {
+ case UVC_VS_FORMAT_UNCOMPRESSED:
+ case UVC_VS_FORMAT_MJPEG:
+ case UVC_VS_FORMAT_DV:
+ case UVC_VS_FORMAT_FRAME_BASED:
+ format->frame = frame;
+ ret = uvc_parse_format(dev, streaming, format,
+ &interval, buffer, buflen);
+ if (ret < 0)
+ goto error;
+
+ frame += format->nframes;
+ format++;
+
+ buflen -= ret;
+ buffer += ret;
+ continue;
+
+ default:
+ break;
+ }
+
+ buflen -= buffer[0];
+ buffer += buffer[0];
+ }
+
+ if (buflen)
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
+ "%d has %u bytes of trailing descriptor garbage.\n",
+ dev->udev->devnum, alts->desc.bInterfaceNumber, buflen);
+
+ /* Parse the alternate settings to find the maximum bandwidth. */
+ for (i = 0; i < intf->num_altsetting; ++i) {
+ struct usb_host_endpoint *ep;
+ alts = &intf->altsetting[i];
+ ep = uvc_find_endpoint(alts,
+ streaming->header.bEndpointAddress);
+ if (ep == NULL)
+ continue;
+
+ psize = le16_to_cpu(ep->desc.wMaxPacketSize);
+ psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
+ if (psize > streaming->maxpsize)
+ streaming->maxpsize = psize;
+ }
+
+ list_add_tail(&streaming->list, &dev->streams);
+ return 0;
+
+error:
+ usb_driver_release_interface(&uvc_driver.driver, intf);
+ usb_put_intf(intf);
+ kfree(streaming->format);
+ kfree(streaming->header.bmaControls);
+ kfree(streaming);
+ return ret;
+}
+
+static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id,
+ unsigned int num_pads, unsigned int extra_size)
+{
+ struct uvc_entity *entity;
+ unsigned int num_inputs;
+ unsigned int size;
+ unsigned int i;
+
+ extra_size = ALIGN(extra_size, sizeof(*entity->pads));
+ num_inputs = (type & UVC_TERM_OUTPUT) ? num_pads : num_pads - 1;
+ size = sizeof(*entity) + extra_size + sizeof(*entity->pads) * num_pads
+ + num_inputs;
+ entity = kzalloc(size, GFP_KERNEL);
+ if (entity == NULL)
+ return NULL;
+
+ entity->id = id;
+ entity->type = type;
+
+ entity->num_links = 0;
+ entity->num_pads = num_pads;
+ entity->pads = ((void *)(entity + 1)) + extra_size;
+
+ for (i = 0; i < num_inputs; ++i)
+ entity->pads[i].flags = MEDIA_PAD_FL_SINK;
+ if (!UVC_ENTITY_IS_OTERM(entity))
+ entity->pads[num_pads-1].flags = MEDIA_PAD_FL_SOURCE;
+
+ entity->bNrInPins = num_inputs;
+ entity->baSourceID = (__u8 *)(&entity->pads[num_pads]);
+
+ return entity;
+}
+
+/* Parse vendor-specific extensions. */
+static int uvc_parse_vendor_control(struct uvc_device *dev,
+ const unsigned char *buffer, int buflen)
+{
+ struct usb_device *udev = dev->udev;
+ struct usb_host_interface *alts = dev->intf->cur_altsetting;
+ struct uvc_entity *unit;
+ unsigned int n, p;
+ int handled = 0;
+
+ switch (le16_to_cpu(dev->udev->descriptor.idVendor)) {
+ case 0x046d: /* Logitech */
+ if (buffer[1] != 0x41 || buffer[2] != 0x01)
+ break;
+
+ /* Logitech implements several vendor specific functions
+ * through vendor specific extension units (LXU).
+ *
+ * The LXU descriptors are similar to XU descriptors
+ * (see "USB Device Video Class for Video Devices", section
+ * 3.7.2.6 "Extension Unit Descriptor") with the following
+ * differences:
+ *
+ * ----------------------------------------------------------
+ * 0 bLength 1 Number
+ * Size of this descriptor, in bytes: 24+p+n*2
+ * ----------------------------------------------------------
+ * 23+p+n bmControlsType N Bitmap
+ * Individual bits in the set are defined:
+ * 0: Absolute
+ * 1: Relative
+ *
+ * This bitset is mapped exactly the same as bmControls.
+ * ----------------------------------------------------------
+ * 23+p+n*2 bReserved 1 Boolean
+ * ----------------------------------------------------------
+ * 24+p+n*2 iExtension 1 Index
+ * Index of a string descriptor that describes this
+ * extension unit.
+ * ----------------------------------------------------------
+ */
+ p = buflen >= 22 ? buffer[21] : 0;
+ n = buflen >= 25 + p ? buffer[22+p] : 0;
+
+ if (buflen < 25 + p + 2*n) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d EXTENSION_UNIT error\n",
+ udev->devnum, alts->desc.bInterfaceNumber);
+ break;
+ }
+
+ unit = uvc_alloc_entity(UVC_VC_EXTENSION_UNIT, buffer[3],
+ p + 1, 2*n);
+ if (unit == NULL)
+ return -ENOMEM;
+
+ memcpy(unit->extension.guidExtensionCode, &buffer[4], 16);
+ unit->extension.bNumControls = buffer[20];
+ memcpy(unit->baSourceID, &buffer[22], p);
+ unit->extension.bControlSize = buffer[22+p];
+ unit->extension.bmControls = (__u8 *)unit + sizeof(*unit);
+ unit->extension.bmControlsType = (__u8 *)unit + sizeof(*unit)
+ + n;
+ memcpy(unit->extension.bmControls, &buffer[23+p], 2*n);
+
+ if (buffer[24+p+2*n] != 0)
+ usb_string(udev, buffer[24+p+2*n], unit->name,
+ sizeof unit->name);
+ else
+ sprintf(unit->name, "Extension %u", buffer[3]);
+
+ list_add_tail(&unit->list, &dev->entities);
+ handled = 1;
+ break;
+ }
+
+ return handled;
+}
+
+static int uvc_parse_standard_control(struct uvc_device *dev,
+ const unsigned char *buffer, int buflen)
+{
+ struct usb_device *udev = dev->udev;
+ struct uvc_entity *unit, *term;
+ struct usb_interface *intf;
+ struct usb_host_interface *alts = dev->intf->cur_altsetting;
+ unsigned int i, n, p, len;
+ __u16 type;
+
+ switch (buffer[2]) {
+ case UVC_VC_HEADER:
+ n = buflen >= 12 ? buffer[11] : 0;
+
+ if (buflen < 12 || buflen < 12 + n) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d HEADER error\n", udev->devnum,
+ alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ dev->uvc_version = get_unaligned_le16(&buffer[3]);
+ dev->clock_frequency = get_unaligned_le32(&buffer[7]);
+
+ /* Parse all USB Video Streaming interfaces. */
+ for (i = 0; i < n; ++i) {
+ intf = usb_ifnum_to_if(udev, buffer[12+i]);
+ if (intf == NULL) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d "
+ "interface %d doesn't exists\n",
+ udev->devnum, i);
+ continue;
+ }
+
+ uvc_parse_streaming(dev, intf);
+ }
+ break;
+
+ case UVC_VC_INPUT_TERMINAL:
+ if (buflen < 8) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d INPUT_TERMINAL error\n",
+ udev->devnum, alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ /* Make sure the terminal type MSB is not null, otherwise it
+ * could be confused with a unit.
+ */
+ type = get_unaligned_le16(&buffer[4]);
+ if ((type & 0xff00) == 0) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d INPUT_TERMINAL %d has invalid "
+ "type 0x%04x, skipping\n", udev->devnum,
+ alts->desc.bInterfaceNumber,
+ buffer[3], type);
+ return 0;
+ }
+
+ n = 0;
+ p = 0;
+ len = 8;
+
+ if (type == UVC_ITT_CAMERA) {
+ n = buflen >= 15 ? buffer[14] : 0;
+ len = 15;
+
+ } else if (type == UVC_ITT_MEDIA_TRANSPORT_INPUT) {
+ n = buflen >= 9 ? buffer[8] : 0;
+ p = buflen >= 10 + n ? buffer[9+n] : 0;
+ len = 10;
+ }
+
+ if (buflen < len + n + p) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d INPUT_TERMINAL error\n",
+ udev->devnum, alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ term = uvc_alloc_entity(type | UVC_TERM_INPUT, buffer[3],
+ 1, n + p);
+ if (term == NULL)
+ return -ENOMEM;
+
+ if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) {
+ term->camera.bControlSize = n;
+ term->camera.bmControls = (__u8 *)term + sizeof *term;
+ term->camera.wObjectiveFocalLengthMin =
+ get_unaligned_le16(&buffer[8]);
+ term->camera.wObjectiveFocalLengthMax =
+ get_unaligned_le16(&buffer[10]);
+ term->camera.wOcularFocalLength =
+ get_unaligned_le16(&buffer[12]);
+ memcpy(term->camera.bmControls, &buffer[15], n);
+ } else if (UVC_ENTITY_TYPE(term) ==
+ UVC_ITT_MEDIA_TRANSPORT_INPUT) {
+ term->media.bControlSize = n;
+ term->media.bmControls = (__u8 *)term + sizeof *term;
+ term->media.bTransportModeSize = p;
+ term->media.bmTransportModes = (__u8 *)term
+ + sizeof *term + n;
+ memcpy(term->media.bmControls, &buffer[9], n);
+ memcpy(term->media.bmTransportModes, &buffer[10+n], p);
+ }
+
+ if (buffer[7] != 0)
+ usb_string(udev, buffer[7], term->name,
+ sizeof term->name);
+ else if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA)
+ sprintf(term->name, "Camera %u", buffer[3]);
+ else if (UVC_ENTITY_TYPE(term) == UVC_ITT_MEDIA_TRANSPORT_INPUT)
+ sprintf(term->name, "Media %u", buffer[3]);
+ else
+ sprintf(term->name, "Input %u", buffer[3]);
+
+ list_add_tail(&term->list, &dev->entities);
+ break;
+
+ case UVC_VC_OUTPUT_TERMINAL:
+ if (buflen < 9) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d OUTPUT_TERMINAL error\n",
+ udev->devnum, alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ /* Make sure the terminal type MSB is not null, otherwise it
+ * could be confused with a unit.
+ */
+ type = get_unaligned_le16(&buffer[4]);
+ if ((type & 0xff00) == 0) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d OUTPUT_TERMINAL %d has invalid "
+ "type 0x%04x, skipping\n", udev->devnum,
+ alts->desc.bInterfaceNumber, buffer[3], type);
+ return 0;
+ }
+
+ term = uvc_alloc_entity(type | UVC_TERM_OUTPUT, buffer[3],
+ 1, 0);
+ if (term == NULL)
+ return -ENOMEM;
+
+ memcpy(term->baSourceID, &buffer[7], 1);
+
+ if (buffer[8] != 0)
+ usb_string(udev, buffer[8], term->name,
+ sizeof term->name);
+ else
+ sprintf(term->name, "Output %u", buffer[3]);
+
+ list_add_tail(&term->list, &dev->entities);
+ break;
+
+ case UVC_VC_SELECTOR_UNIT:
+ p = buflen >= 5 ? buffer[4] : 0;
+
+ if (buflen < 5 || buflen < 6 + p) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d SELECTOR_UNIT error\n",
+ udev->devnum, alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, 0);
+ if (unit == NULL)
+ return -ENOMEM;
+
+ memcpy(unit->baSourceID, &buffer[5], p);
+
+ if (buffer[5+p] != 0)
+ usb_string(udev, buffer[5+p], unit->name,
+ sizeof unit->name);
+ else
+ sprintf(unit->name, "Selector %u", buffer[3]);
+
+ list_add_tail(&unit->list, &dev->entities);
+ break;
+
+ case UVC_VC_PROCESSING_UNIT:
+ n = buflen >= 8 ? buffer[7] : 0;
+ p = dev->uvc_version >= 0x0110 ? 10 : 9;
+
+ if (buflen < p + n) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d PROCESSING_UNIT error\n",
+ udev->devnum, alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ unit = uvc_alloc_entity(buffer[2], buffer[3], 2, n);
+ if (unit == NULL)
+ return -ENOMEM;
+
+ memcpy(unit->baSourceID, &buffer[4], 1);
+ unit->processing.wMaxMultiplier =
+ get_unaligned_le16(&buffer[5]);
+ unit->processing.bControlSize = buffer[7];
+ unit->processing.bmControls = (__u8 *)unit + sizeof *unit;
+ memcpy(unit->processing.bmControls, &buffer[8], n);
+ if (dev->uvc_version >= 0x0110)
+ unit->processing.bmVideoStandards = buffer[9+n];
+
+ if (buffer[8+n] != 0)
+ usb_string(udev, buffer[8+n], unit->name,
+ sizeof unit->name);
+ else
+ sprintf(unit->name, "Processing %u", buffer[3]);
+
+ list_add_tail(&unit->list, &dev->entities);
+ break;
+
+ case UVC_VC_EXTENSION_UNIT:
+ p = buflen >= 22 ? buffer[21] : 0;
+ n = buflen >= 24 + p ? buffer[22+p] : 0;
+
+ if (buflen < 24 + p + n) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d EXTENSION_UNIT error\n",
+ udev->devnum, alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, n);
+ if (unit == NULL)
+ return -ENOMEM;
+
+ memcpy(unit->extension.guidExtensionCode, &buffer[4], 16);
+ unit->extension.bNumControls = buffer[20];
+ memcpy(unit->baSourceID, &buffer[22], p);
+ unit->extension.bControlSize = buffer[22+p];
+ unit->extension.bmControls = (__u8 *)unit + sizeof *unit;
+ memcpy(unit->extension.bmControls, &buffer[23+p], n);
+
+ if (buffer[23+p+n] != 0)
+ usb_string(udev, buffer[23+p+n], unit->name,
+ sizeof unit->name);
+ else
+ sprintf(unit->name, "Extension %u", buffer[3]);
+
+ list_add_tail(&unit->list, &dev->entities);
+ break;
+
+ default:
+ uvc_trace(UVC_TRACE_DESCR, "Found an unknown CS_INTERFACE "
+ "descriptor (%u)\n", buffer[2]);
+ break;
+ }
+
+ return 0;
+}
+
+static int uvc_parse_control(struct uvc_device *dev)
+{
+ struct usb_host_interface *alts = dev->intf->cur_altsetting;
+ unsigned char *buffer = alts->extra;
+ int buflen = alts->extralen;
+ int ret;
+
+ /* Parse the default alternate setting only, as the UVC specification
+ * defines a single alternate setting, the default alternate setting
+ * zero.
+ */
+
+ while (buflen > 2) {
+ if (uvc_parse_vendor_control(dev, buffer, buflen) ||
+ buffer[1] != USB_DT_CS_INTERFACE)
+ goto next_descriptor;
+
+ if ((ret = uvc_parse_standard_control(dev, buffer, buflen)) < 0)
+ return ret;
+
+next_descriptor:
+ buflen -= buffer[0];
+ buffer += buffer[0];
+ }
+
+ /* Check if the optional status endpoint is present. Built-in iSight
+ * webcams have an interrupt endpoint but spit proprietary data that
+ * don't conform to the UVC status endpoint messages. Don't try to
+ * handle the interrupt endpoint for those cameras.
+ */
+ if (alts->desc.bNumEndpoints == 1 &&
+ !(dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)) {
+ struct usb_host_endpoint *ep = &alts->endpoint[0];
+ struct usb_endpoint_descriptor *desc = &ep->desc;
+
+ if (usb_endpoint_is_int_in(desc) &&
+ le16_to_cpu(desc->wMaxPacketSize) >= 8 &&
+ desc->bInterval != 0) {
+ uvc_trace(UVC_TRACE_DESCR, "Found a Status endpoint "
+ "(addr %02x).\n", desc->bEndpointAddress);
+ dev->int_ep = ep;
+ }
+ }
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------------
+ * UVC device scan
+ */
+
+/*
+ * Scan the UVC descriptors to locate a chain starting at an Output Terminal
+ * and containing the following units:
+ *
+ * - one or more Output Terminals (USB Streaming or Display)
+ * - zero or one Processing Unit
+ * - zero, one or more single-input Selector Units
+ * - zero or one multiple-input Selector Units, provided all inputs are
+ * connected to input terminals
+ * - zero, one or mode single-input Extension Units
+ * - one or more Input Terminals (Camera, External or USB Streaming)
+ *
+ * The terminal and units must match on of the following structures:
+ *
+ * ITT_*(0) -> +---------+ +---------+ +---------+ -> TT_STREAMING(0)
+ * ... | SU{0,1} | -> | PU{0,1} | -> | XU{0,n} | ...
+ * ITT_*(n) -> +---------+ +---------+ +---------+ -> TT_STREAMING(n)
+ *
+ * +---------+ +---------+ -> OTT_*(0)
+ * TT_STREAMING -> | PU{0,1} | -> | XU{0,n} | ...
+ * +---------+ +---------+ -> OTT_*(n)
+ *
+ * The Processing Unit and Extension Units can be in any order. Additional
+ * Extension Units connected to the main chain as single-unit branches are
+ * also supported. Single-input Selector Units are ignored.
+ */
+static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
+ struct uvc_entity *entity)
+{
+ switch (UVC_ENTITY_TYPE(entity)) {
+ case UVC_VC_EXTENSION_UNIT:
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk(" <- XU %d", entity->id);
+
+ if (entity->bNrInPins != 1) {
+ uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more "
+ "than 1 input pin.\n", entity->id);
+ return -1;
+ }
+
+ break;
+
+ case UVC_VC_PROCESSING_UNIT:
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk(" <- PU %d", entity->id);
+
+ if (chain->processing != NULL) {
+ uvc_trace(UVC_TRACE_DESCR, "Found multiple "
+ "Processing Units in chain.\n");
+ return -1;
+ }
+
+ chain->processing = entity;
+ break;
+
+ case UVC_VC_SELECTOR_UNIT:
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk(" <- SU %d", entity->id);
+
+ /* Single-input selector units are ignored. */
+ if (entity->bNrInPins == 1)
+ break;
+
+ if (chain->selector != NULL) {
+ uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector "
+ "Units in chain.\n");
+ return -1;
+ }
+
+ chain->selector = entity;
+ break;
+
+ case UVC_ITT_VENDOR_SPECIFIC:
+ case UVC_ITT_CAMERA:
+ case UVC_ITT_MEDIA_TRANSPORT_INPUT:
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk(" <- IT %d\n", entity->id);
+
+ break;
+
+ case UVC_OTT_VENDOR_SPECIFIC:
+ case UVC_OTT_DISPLAY:
+ case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk(" OT %d", entity->id);
+
+ break;
+
+ case UVC_TT_STREAMING:
+ if (UVC_ENTITY_IS_ITERM(entity)) {
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk(" <- IT %d\n", entity->id);
+ } else {
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk(" OT %d", entity->id);
+ }
+
+ break;
+
+ default:
+ uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type "
+ "0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity));
+ return -1;
+ }
+
+ list_add_tail(&entity->chain, &chain->entities);
+ return 0;
+}
+
+static int uvc_scan_chain_forward(struct uvc_video_chain *chain,
+ struct uvc_entity *entity, struct uvc_entity *prev)
+{
+ struct uvc_entity *forward;
+ int found;
+
+ /* Forward scan */
+ forward = NULL;
+ found = 0;
+
+ while (1) {
+ forward = uvc_entity_by_reference(chain->dev, entity->id,
+ forward);
+ if (forward == NULL)
+ break;
+ if (forward == prev)
+ continue;
+
+ switch (UVC_ENTITY_TYPE(forward)) {
+ case UVC_VC_EXTENSION_UNIT:
+ if (forward->bNrInPins != 1) {
+ uvc_trace(UVC_TRACE_DESCR, "Extension unit %d "
+ "has more than 1 input pin.\n",
+ entity->id);
+ return -EINVAL;
+ }
+
+ list_add_tail(&forward->chain, &chain->entities);
+ if (uvc_trace_param & UVC_TRACE_PROBE) {
+ if (!found)
+ printk(" (->");
+
+ printk(" XU %d", forward->id);
+ found = 1;
+ }
+ break;
+
+ case UVC_OTT_VENDOR_SPECIFIC:
+ case UVC_OTT_DISPLAY:
+ case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
+ case UVC_TT_STREAMING:
+ if (UVC_ENTITY_IS_ITERM(forward)) {
+ uvc_trace(UVC_TRACE_DESCR, "Unsupported input "
+ "terminal %u.\n", forward->id);
+ return -EINVAL;
+ }
+
+ list_add_tail(&forward->chain, &chain->entities);
+ if (uvc_trace_param & UVC_TRACE_PROBE) {
+ if (!found)
+ printk(" (->");
+
+ printk(" OT %d", forward->id);
+ found = 1;
+ }
+ break;
+ }
+ }
+ if (found)
+ printk(")");
+
+ return 0;
+}
+
+static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
+ struct uvc_entity **_entity)
+{
+ struct uvc_entity *entity = *_entity;
+ struct uvc_entity *term;
+ int id = -EINVAL, i;
+
+ switch (UVC_ENTITY_TYPE(entity)) {
+ case UVC_VC_EXTENSION_UNIT:
+ case UVC_VC_PROCESSING_UNIT:
+ id = entity->baSourceID[0];
+ break;
+
+ case UVC_VC_SELECTOR_UNIT:
+ /* Single-input selector units are ignored. */
+ if (entity->bNrInPins == 1) {
+ id = entity->baSourceID[0];
+ break;
+ }
+
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk(" <- IT");
+
+ chain->selector = entity;
+ for (i = 0; i < entity->bNrInPins; ++i) {
+ id = entity->baSourceID[i];
+ term = uvc_entity_by_id(chain->dev, id);
+ if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) {
+ uvc_trace(UVC_TRACE_DESCR, "Selector unit %d "
+ "input %d isn't connected to an "
+ "input terminal\n", entity->id, i);
+ return -1;
+ }
+
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk(" %d", term->id);
+
+ list_add_tail(&term->chain, &chain->entities);
+ uvc_scan_chain_forward(chain, term, entity);
+ }
+
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk("\n");
+
+ id = 0;
+ break;
+
+ case UVC_ITT_VENDOR_SPECIFIC:
+ case UVC_ITT_CAMERA:
+ case UVC_ITT_MEDIA_TRANSPORT_INPUT:
+ case UVC_OTT_VENDOR_SPECIFIC:
+ case UVC_OTT_DISPLAY:
+ case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
+ case UVC_TT_STREAMING:
+ id = UVC_ENTITY_IS_OTERM(entity) ? entity->baSourceID[0] : 0;
+ break;
+ }
+
+ if (id <= 0) {
+ *_entity = NULL;
+ return id;
+ }
+
+ entity = uvc_entity_by_id(chain->dev, id);
+ if (entity == NULL) {
+ uvc_trace(UVC_TRACE_DESCR, "Found reference to "
+ "unknown entity %d.\n", id);
+ return -EINVAL;
+ }
+
+ *_entity = entity;
+ return 0;
+}
+
+static int uvc_scan_chain(struct uvc_video_chain *chain,
+ struct uvc_entity *term)
+{
+ struct uvc_entity *entity, *prev;
+
+ uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain:");
+
+ entity = term;
+ prev = NULL;
+
+ while (entity != NULL) {
+ /* Entity must not be part of an existing chain */
+ if (entity->chain.next || entity->chain.prev) {
+ uvc_trace(UVC_TRACE_DESCR, "Found reference to "
+ "entity %d already in chain.\n", entity->id);
+ return -EINVAL;
+ }
+
+ /* Process entity */
+ if (uvc_scan_chain_entity(chain, entity) < 0)
+ return -EINVAL;
+
+ /* Forward scan */
+ if (uvc_scan_chain_forward(chain, entity, prev) < 0)
+ return -EINVAL;
+
+ /* Backward scan */
+ prev = entity;
+ if (uvc_scan_chain_backward(chain, &entity) < 0)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static unsigned int uvc_print_terms(struct list_head *terms, u16 dir,
+ char *buffer)
+{
+ struct uvc_entity *term;
+ unsigned int nterms = 0;
+ char *p = buffer;
+
+ list_for_each_entry(term, terms, chain) {
+ if (!UVC_ENTITY_IS_TERM(term) ||
+ UVC_TERM_DIRECTION(term) != dir)
+ continue;
+
+ if (nterms)
+ p += sprintf(p, ",");
+ if (++nterms >= 4) {
+ p += sprintf(p, "...");
+ break;
+ }
+ p += sprintf(p, "%u", term->id);
+ }
+
+ return p - buffer;
+}
+
+static const char *uvc_print_chain(struct uvc_video_chain *chain)
+{
+ static char buffer[43];
+ char *p = buffer;
+
+ p += uvc_print_terms(&chain->entities, UVC_TERM_INPUT, p);
+ p += sprintf(p, " -> ");
+ uvc_print_terms(&chain->entities, UVC_TERM_OUTPUT, p);
+
+ return buffer;
+}
+
+/*
+ * Scan the device for video chains and register video devices.
+ *
+ * Chains are scanned starting at their output terminals and walked backwards.
+ */
+static int uvc_scan_device(struct uvc_device *dev)
+{
+ struct uvc_video_chain *chain;
+ struct uvc_entity *term;
+
+ list_for_each_entry(term, &dev->entities, list) {
+ if (!UVC_ENTITY_IS_OTERM(term))
+ continue;
+
+ /* If the terminal is already included in a chain, skip it.
+ * This can happen for chains that have multiple output
+ * terminals, where all output terminals beside the first one
+ * will be inserted in the chain in forward scans.
+ */
+ if (term->chain.next || term->chain.prev)
+ continue;
+
+ chain = kzalloc(sizeof(*chain), GFP_KERNEL);
+ if (chain == NULL)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&chain->entities);
+ mutex_init(&chain->ctrl_mutex);
+ chain->dev = dev;
+
+ if (uvc_scan_chain(chain, term) < 0) {
+ kfree(chain);
+ continue;
+ }
+
+ uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%s).\n",
+ uvc_print_chain(chain));
+
+ list_add_tail(&chain->list, &dev->chains);
+ }
+
+ if (list_empty(&dev->chains)) {
+ uvc_printk(KERN_INFO, "No valid video chain found.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------------
+ * Video device registration and unregistration
+ */
+
+/*
+ * Delete the UVC device.
+ *
+ * Called by the kernel when the last reference to the uvc_device structure
+ * is released.
+ *
+ * As this function is called after or during disconnect(), all URBs have
+ * already been canceled by the USB core. There is no need to kill the
+ * interrupt URB manually.
+ */
+static void uvc_delete(struct uvc_device *dev)
+{
+ struct list_head *p, *n;
+
+ usb_put_intf(dev->intf);
+ usb_put_dev(dev->udev);
+
+ uvc_status_cleanup(dev);
+ uvc_ctrl_cleanup_device(dev);
+
+ if (dev->vdev.dev)
+ v4l2_device_unregister(&dev->vdev);
+#ifdef CONFIG_MEDIA_CONTROLLER
+ if (media_devnode_is_registered(&dev->mdev.devnode))
+ media_device_unregister(&dev->mdev);
+#endif
+
+ list_for_each_safe(p, n, &dev->chains) {
+ struct uvc_video_chain *chain;
+ chain = list_entry(p, struct uvc_video_chain, list);
+ kfree(chain);
+ }
+
+ list_for_each_safe(p, n, &dev->entities) {
+ struct uvc_entity *entity;
+ entity = list_entry(p, struct uvc_entity, list);
+#ifdef CONFIG_MEDIA_CONTROLLER
+ uvc_mc_cleanup_entity(entity);
+#endif
+ if (entity->vdev) {
+ video_device_release(entity->vdev);
+ entity->vdev = NULL;
+ }
+ kfree(entity);
+ }
+
+ list_for_each_safe(p, n, &dev->streams) {
+ struct uvc_streaming *streaming;
+ streaming = list_entry(p, struct uvc_streaming, list);
+ usb_driver_release_interface(&uvc_driver.driver,
+ streaming->intf);
+ usb_put_intf(streaming->intf);
+ kfree(streaming->format);
+ kfree(streaming->header.bmaControls);
+ kfree(streaming);
+ }
+
+ kfree(dev);
+}
+
+static void uvc_release(struct video_device *vdev)
+{
+ struct uvc_streaming *stream = video_get_drvdata(vdev);
+ struct uvc_device *dev = stream->dev;
+
+ /* Decrement the registered streams count and delete the device when it
+ * reaches zero.
+ */
+ if (atomic_dec_and_test(&dev->nstreams))
+ uvc_delete(dev);
+}
+
+/*
+ * Unregister the video devices.
+ */
+static void uvc_unregister_video(struct uvc_device *dev)
+{
+ struct uvc_streaming *stream;
+
+ /* Unregistering all video devices might result in uvc_delete() being
+ * called from inside the loop if there's no open file handle. To avoid
+ * that, increment the stream count before iterating over the streams
+ * and decrement it when done.
+ */
+ atomic_inc(&dev->nstreams);
+
+ list_for_each_entry(stream, &dev->streams, list) {
+ if (stream->vdev == NULL)
+ continue;
+
+ video_unregister_device(stream->vdev);
+ stream->vdev = NULL;
+
+ uvc_debugfs_cleanup_stream(stream);
+ }
+
+ /* Decrement the stream count and call uvc_delete explicitly if there
+ * are no stream left.
+ */
+ if (atomic_dec_and_test(&dev->nstreams))
+ uvc_delete(dev);
+}
+
+static int uvc_register_video(struct uvc_device *dev,
+ struct uvc_streaming *stream)
+{
+ struct video_device *vdev;
+ int ret;
+
+ /* Initialize the streaming interface with default streaming
+ * parameters.
+ */
+ ret = uvc_video_init(stream);
+ if (ret < 0) {
+ uvc_printk(KERN_ERR, "Failed to initialize the device "
+ "(%d).\n", ret);
+ return ret;
+ }
+
+ uvc_debugfs_init_stream(stream);
+
+ /* Register the device with V4L. */
+ vdev = video_device_alloc();
+ if (vdev == NULL) {
+ uvc_printk(KERN_ERR, "Failed to allocate video device (%d).\n",
+ ret);
+ return -ENOMEM;
+ }
+
+ /* We already hold a reference to dev->udev. The video device will be
+ * unregistered before the reference is released, so we don't need to
+ * get another one.
+ */
+ vdev->v4l2_dev = &dev->vdev;
+ vdev->fops = &uvc_fops;
+ vdev->release = uvc_release;
+ strlcpy(vdev->name, dev->name, sizeof vdev->name);
+
+ /* Set the driver data before calling video_register_device, otherwise
+ * uvc_v4l2_open might race us.
+ */
+ stream->vdev = vdev;
+ video_set_drvdata(vdev, stream);
+
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ uvc_printk(KERN_ERR, "Failed to register video device (%d).\n",
+ ret);
+ stream->vdev = NULL;
+ video_device_release(vdev);
+ return ret;
+ }
+
+ atomic_inc(&dev->nstreams);
+ return 0;
+}
+
+/*
+ * Register all video devices in all chains.
+ */
+static int uvc_register_terms(struct uvc_device *dev,
+ struct uvc_video_chain *chain)
+{
+ struct uvc_streaming *stream;
+ struct uvc_entity *term;
+ int ret;
+
+ list_for_each_entry(term, &chain->entities, chain) {
+ if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING)
+ continue;
+
+ stream = uvc_stream_by_id(dev, term->id);
+ if (stream == NULL) {
+ uvc_printk(KERN_INFO, "No streaming interface found "
+ "for terminal %u.", term->id);
+ continue;
+ }
+
+ stream->chain = chain;
+ ret = uvc_register_video(dev, stream);
+ if (ret < 0)
+ return ret;
+
+ term->vdev = stream->vdev;
+ }
+
+ return 0;
+}
+
+static int uvc_register_chains(struct uvc_device *dev)
+{
+ struct uvc_video_chain *chain;
+ int ret;
+
+ list_for_each_entry(chain, &dev->chains, list) {
+ ret = uvc_register_terms(dev, chain);
+ if (ret < 0)
+ return ret;
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+ ret = uvc_mc_register_entities(chain);
+ if (ret < 0) {
+ uvc_printk(KERN_INFO, "Failed to register entites "
+ "(%d).\n", ret);
+ }
+#endif
+ }
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------------
+ * USB probe, disconnect, suspend and resume
+ */
+
+static int uvc_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct uvc_device *dev;
+ int ret;
+
+ if (id->idVendor && id->idProduct)
+ uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s "
+ "(%04x:%04x)\n", udev->devpath, id->idVendor,
+ id->idProduct);
+ else
+ uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n",
+ udev->devpath);
+
+ /* Allocate memory for the device and initialize it. */
+ if ((dev = kzalloc(sizeof *dev, GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&dev->entities);
+ INIT_LIST_HEAD(&dev->chains);
+ INIT_LIST_HEAD(&dev->streams);
+ atomic_set(&dev->nstreams, 0);
+ atomic_set(&dev->users, 0);
+ atomic_set(&dev->nmappings, 0);
+
+ dev->udev = usb_get_dev(udev);
+ dev->intf = usb_get_intf(intf);
+ dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
+ dev->quirks = (uvc_quirks_param == -1)
+ ? id->driver_info : uvc_quirks_param;
+
+ if (udev->product != NULL)
+ strlcpy(dev->name, udev->product, sizeof dev->name);
+ else
+ snprintf(dev->name, sizeof dev->name,
+ "UVC Camera (%04x:%04x)",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct));
+
+ /* Parse the Video Class control descriptor. */
+ if (uvc_parse_control(dev) < 0) {
+ uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC "
+ "descriptors.\n");
+ goto error;
+ }
+
+ uvc_printk(KERN_INFO, "Found UVC %u.%02x device %s (%04x:%04x)\n",
+ dev->uvc_version >> 8, dev->uvc_version & 0xff,
+ udev->product ? udev->product : "<unnamed>",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct));
+
+ if (dev->quirks != id->driver_info) {
+ uvc_printk(KERN_INFO, "Forcing device quirks to 0x%x by module "
+ "parameter for testing purpose.\n", dev->quirks);
+ uvc_printk(KERN_INFO, "Please report required quirks to the "
+ "linux-uvc-devel mailing list.\n");
+ }
+
+ /* Register the media and V4L2 devices. */
+#ifdef CONFIG_MEDIA_CONTROLLER
+ dev->mdev.dev = &intf->dev;
+ strlcpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model));
+ if (udev->serial)
+ strlcpy(dev->mdev.serial, udev->serial,
+ sizeof(dev->mdev.serial));
+ strcpy(dev->mdev.bus_info, udev->devpath);
+ dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
+ dev->mdev.driver_version = LINUX_VERSION_CODE;
+ if (media_device_register(&dev->mdev) < 0)
+ goto error;
+
+ dev->vdev.mdev = &dev->mdev;
+#endif
+ if (v4l2_device_register(&intf->dev, &dev->vdev) < 0)
+ goto error;
+
+ /* Initialize controls. */
+ if (uvc_ctrl_init_device(dev) < 0)
+ goto error;
+
+ /* Scan the device for video chains. */
+ if (uvc_scan_device(dev) < 0)
+ goto error;
+
+ /* Register video device nodes. */
+ if (uvc_register_chains(dev) < 0)
+ goto error;
+
+ /* Save our data pointer in the interface data. */
+ usb_set_intfdata(intf, dev);
+
+ /* Initialize the interrupt URB. */
+ if ((ret = uvc_status_init(dev)) < 0) {
+ uvc_printk(KERN_INFO, "Unable to initialize the status "
+ "endpoint (%d), status interrupt will not be "
+ "supported.\n", ret);
+ }
+
+ uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n");
+ usb_enable_autosuspend(udev);
+ return 0;
+
+error:
+ uvc_unregister_video(dev);
+ return -ENODEV;
+}
+
+static void uvc_disconnect(struct usb_interface *intf)
+{
+ struct uvc_device *dev = usb_get_intfdata(intf);
+
+ /* Set the USB interface data to NULL. This can be done outside the
+ * lock, as there's no other reader.
+ */
+ usb_set_intfdata(intf, NULL);
+
+ if (intf->cur_altsetting->desc.bInterfaceSubClass ==
+ UVC_SC_VIDEOSTREAMING)
+ return;
+
+ dev->state |= UVC_DEV_DISCONNECTED;
+
+ uvc_unregister_video(dev);
+}
+
+static int uvc_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct uvc_device *dev = usb_get_intfdata(intf);
+ struct uvc_streaming *stream;
+
+ uvc_trace(UVC_TRACE_SUSPEND, "Suspending interface %u\n",
+ intf->cur_altsetting->desc.bInterfaceNumber);
+
+ /* Controls are cached on the fly so they don't need to be saved. */
+ if (intf->cur_altsetting->desc.bInterfaceSubClass ==
+ UVC_SC_VIDEOCONTROL)
+ return uvc_status_suspend(dev);
+
+ list_for_each_entry(stream, &dev->streams, list) {
+ if (stream->intf == intf)
+ return uvc_video_suspend(stream);
+ }
+
+ uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB interface "
+ "mismatch.\n");
+ return -EINVAL;
+}
+
+static int __uvc_resume(struct usb_interface *intf, int reset)
+{
+ struct uvc_device *dev = usb_get_intfdata(intf);
+ struct uvc_streaming *stream;
+
+ uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n",
+ intf->cur_altsetting->desc.bInterfaceNumber);
+
+ if (intf->cur_altsetting->desc.bInterfaceSubClass ==
+ UVC_SC_VIDEOCONTROL) {
+ if (reset) {
+ int ret = uvc_ctrl_resume_device(dev);
+
+ if (ret < 0)
+ return ret;
+ }
+
+ return uvc_status_resume(dev);
+ }
+
+ list_for_each_entry(stream, &dev->streams, list) {
+ if (stream->intf == intf)
+ return uvc_video_resume(stream, reset);
+ }
+
+ uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB interface "
+ "mismatch.\n");
+ return -EINVAL;
+}
+
+static int uvc_resume(struct usb_interface *intf)
+{
+ return __uvc_resume(intf, 0);
+}
+
+static int uvc_reset_resume(struct usb_interface *intf)
+{
+ return __uvc_resume(intf, 1);
+}
+
+/* ------------------------------------------------------------------------
+ * Module parameters
+ */
+
+static int uvc_clock_param_get(char *buffer, struct kernel_param *kp)
+{
+ if (uvc_clock_param == CLOCK_MONOTONIC)
+ return sprintf(buffer, "CLOCK_MONOTONIC");
+ else
+ return sprintf(buffer, "CLOCK_REALTIME");
+}
+
+static int uvc_clock_param_set(const char *val, struct kernel_param *kp)
+{
+ if (strncasecmp(val, "clock_", strlen("clock_")) == 0)
+ val += strlen("clock_");
+
+ if (strcasecmp(val, "monotonic") == 0)
+ uvc_clock_param = CLOCK_MONOTONIC;
+ else if (strcasecmp(val, "realtime") == 0)
+ uvc_clock_param = CLOCK_REALTIME;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+module_param_call(clock, uvc_clock_param_set, uvc_clock_param_get,
+ &uvc_clock_param, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(clock, "Video buffers timestamp clock");
+module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames");
+module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(quirks, "Forced device quirks");
+module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(trace, "Trace level bitmask");
+module_param_named(timeout, uvc_timeout_param, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(timeout, "Streaming control requests timeout");
+
+/* ------------------------------------------------------------------------
+ * Driver initialization and cleanup
+ */
+
+/*
+ * The Logitech cameras listed below have their interface class set to
+ * VENDOR_SPEC because they don't announce themselves as UVC devices, even
+ * though they are compliant.
+ */
+static struct usb_device_id uvc_ids[] = {
+ /* LogiLink Wireless Webcam */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x0416,
+ .idProduct = 0xa91a,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Genius eFace 2025 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x0458,
+ .idProduct = 0x706e,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Microsoft Lifecam NX-6000 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x045e,
+ .idProduct = 0x00f8,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Microsoft Lifecam VX-7000 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x045e,
+ .idProduct = 0x0723,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Logitech Quickcam Fusion */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x046d,
+ .idProduct = 0x08c1,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0 },
+ /* Logitech Quickcam Orbit MP */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x046d,
+ .idProduct = 0x08c2,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0 },
+ /* Logitech Quickcam Pro for Notebook */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x046d,
+ .idProduct = 0x08c3,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0 },
+ /* Logitech Quickcam Pro 5000 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x046d,
+ .idProduct = 0x08c5,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0 },
+ /* Logitech Quickcam OEM Dell Notebook */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x046d,
+ .idProduct = 0x08c6,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0 },
+ /* Logitech Quickcam OEM Cisco VT Camera II */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x046d,
+ .idProduct = 0x08c7,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0 },
+ /* Chicony CNF7129 (Asus EEE 100HE) */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x04f2,
+ .idProduct = 0xb071,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_RESTRICT_FRAME_RATE },
+ /* Alcor Micro AU3820 (Future Boy PC USB Webcam) */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x058f,
+ .idProduct = 0x3820,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Dell XPS m1530 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x05a9,
+ .idProduct = 0x2640,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_DEF },
+ /* Apple Built-In iSight */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x05ac,
+ .idProduct = 0x8501,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX
+ | UVC_QUIRK_BUILTIN_ISIGHT },
+ /* Foxlink ("HP Webcam" on HP Mini 5103) */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x05c8,
+ .idProduct = 0x0403,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_FIX_BANDWIDTH },
+ /* Genesys Logic USB 2.0 PC Camera */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x05e3,
+ .idProduct = 0x0505,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* Hercules Classic Silver */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x06f8,
+ .idProduct = 0x300c,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_FIX_BANDWIDTH },
+ /* ViMicro Vega */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x0ac8,
+ .idProduct = 0x332d,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_FIX_BANDWIDTH },
+ /* ViMicro - Minoru3D */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x0ac8,
+ .idProduct = 0x3410,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_FIX_BANDWIDTH },
+ /* ViMicro Venus - Minoru3D */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x0ac8,
+ .idProduct = 0x3420,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_FIX_BANDWIDTH },
+ /* MT6227 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x0e8d,
+ .idProduct = 0x0004,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX
+ | UVC_QUIRK_PROBE_DEF },
+ /* IMC Networks (Medion Akoya) */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x13d3,
+ .idProduct = 0x5103,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* JMicron USB2.0 XGA WebCam */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x152d,
+ .idProduct = 0x0310,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Syntek (HP Spartan) */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x174f,
+ .idProduct = 0x5212,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* Syntek (Samsung Q310) */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x174f,
+ .idProduct = 0x5931,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* Syntek (Packard Bell EasyNote MX52 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x174f,
+ .idProduct = 0x8a12,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* Syntek (Asus F9SG) */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x174f,
+ .idProduct = 0x8a31,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* Syntek (Asus U3S) */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x174f,
+ .idProduct = 0x8a33,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* Syntek (JAOtech Smart Terminal) */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x174f,
+ .idProduct = 0x8a34,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* Miricle 307K */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x17dc,
+ .idProduct = 0x0202,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* Lenovo Thinkpad SL400/SL500 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x17ef,
+ .idProduct = 0x480b,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* Aveo Technology USB 2.0 Camera */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x1871,
+ .idProduct = 0x0306,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX
+ | UVC_QUIRK_PROBE_EXTRAFIELDS },
+ /* Ecamm Pico iMage */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x18cd,
+ .idProduct = 0xcafe,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_EXTRAFIELDS },
+ /* Manta MM-353 Plako */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x18ec,
+ .idProduct = 0x3188,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* FSC WebCam V30S */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x18ec,
+ .idProduct = 0x3288,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Arkmicro unbranded */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x18ec,
+ .idProduct = 0x3290,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_DEF },
+ /* The Imaging Source USB CCD cameras */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x199e,
+ .idProduct = 0x8102,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0 },
+ /* Bodelin ProScopeHR */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_DEV_HI
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x19ab,
+ .idProduct = 0x1000,
+ .bcdDevice_hi = 0x0126,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STATUS_INTERVAL },
+ /* MSI StarCam 370i */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x1b3b,
+ .idProduct = 0x2951,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* SiGma Micro USB Web Camera */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x1c4f,
+ .idProduct = 0x3000,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX
+ | UVC_QUIRK_IGNORE_SELECTOR_UNIT },
+ /* Generic USB Video Class */
+ { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, uvc_ids);
+
+struct uvc_driver uvc_driver = {
+ .driver = {
+ .name = "uvcvideo",
+ .probe = uvc_probe,
+ .disconnect = uvc_disconnect,
+ .suspend = uvc_suspend,
+ .resume = uvc_resume,
+ .reset_resume = uvc_reset_resume,
+ .id_table = uvc_ids,
+ .supports_autosuspend = 1,
+ },
+};
+
+static int __init uvc_init(void)
+{
+ int ret;
+
+ uvc_debugfs_init();
+
+ ret = usb_register(&uvc_driver.driver);
+ if (ret < 0) {
+ uvc_debugfs_cleanup();
+ return ret;
+ }
+
+ printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n");
+ return 0;
+}
+
+static void __exit uvc_cleanup(void)
+{
+ usb_deregister(&uvc_driver.driver);
+ uvc_debugfs_cleanup();
+}
+
+module_init(uvc_init);
+module_exit(uvc_cleanup);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
+
diff --git a/drivers/media/video/uvc/uvc_entity.c b/drivers/media/video/uvc/uvc_entity.c
new file mode 100644
index 00000000..29e23991
--- /dev/null
+++ b/drivers/media/video/uvc/uvc_entity.c
@@ -0,0 +1,126 @@
+/*
+ * uvc_entity.c -- USB Video Class driver
+ *
+ * Copyright (C) 2005-2011
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-common.h>
+
+#include "uvcvideo.h"
+
+/* ------------------------------------------------------------------------
+ * Video subdevices registration and unregistration
+ */
+
+static int uvc_mc_register_entity(struct uvc_video_chain *chain,
+ struct uvc_entity *entity)
+{
+ const u32 flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE;
+ struct media_entity *sink;
+ unsigned int i;
+ int ret;
+
+ sink = (UVC_ENTITY_TYPE(entity) == UVC_TT_STREAMING)
+ ? (entity->vdev ? &entity->vdev->entity : NULL)
+ : &entity->subdev.entity;
+ if (sink == NULL)
+ return 0;
+
+ for (i = 0; i < entity->num_pads; ++i) {
+ struct media_entity *source;
+ struct uvc_entity *remote;
+ u8 remote_pad;
+
+ if (!(entity->pads[i].flags & MEDIA_PAD_FL_SINK))
+ continue;
+
+ remote = uvc_entity_by_id(chain->dev, entity->baSourceID[i]);
+ if (remote == NULL)
+ return -EINVAL;
+
+ source = (UVC_ENTITY_TYPE(remote) == UVC_TT_STREAMING)
+ ? (remote->vdev ? &remote->vdev->entity : NULL)
+ : &remote->subdev.entity;
+ if (source == NULL)
+ continue;
+
+ remote_pad = remote->num_pads - 1;
+ ret = media_entity_create_link(source, remote_pad,
+ sink, i, flags);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (UVC_ENTITY_TYPE(entity) == UVC_TT_STREAMING)
+ return 0;
+
+ return v4l2_device_register_subdev(&chain->dev->vdev, &entity->subdev);
+}
+
+static struct v4l2_subdev_ops uvc_subdev_ops = {
+};
+
+void uvc_mc_cleanup_entity(struct uvc_entity *entity)
+{
+ if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING)
+ media_entity_cleanup(&entity->subdev.entity);
+ else if (entity->vdev != NULL)
+ media_entity_cleanup(&entity->vdev->entity);
+}
+
+static int uvc_mc_init_entity(struct uvc_entity *entity)
+{
+ int ret;
+
+ if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING) {
+ v4l2_subdev_init(&entity->subdev, &uvc_subdev_ops);
+ strlcpy(entity->subdev.name, entity->name,
+ sizeof(entity->subdev.name));
+
+ ret = media_entity_init(&entity->subdev.entity,
+ entity->num_pads, entity->pads, 0);
+ } else if (entity->vdev != NULL) {
+ ret = media_entity_init(&entity->vdev->entity,
+ entity->num_pads, entity->pads, 0);
+ } else
+ ret = 0;
+
+ return ret;
+}
+
+int uvc_mc_register_entities(struct uvc_video_chain *chain)
+{
+ struct uvc_entity *entity;
+ int ret;
+
+ list_for_each_entry(entity, &chain->entities, chain) {
+ ret = uvc_mc_init_entity(entity);
+ if (ret < 0) {
+ uvc_printk(KERN_INFO, "Failed to initialize entity for "
+ "entity %u\n", entity->id);
+ return ret;
+ }
+ }
+
+ list_for_each_entry(entity, &chain->entities, chain) {
+ ret = uvc_mc_register_entity(chain, entity);
+ if (ret < 0) {
+ uvc_printk(KERN_INFO, "Failed to register entity for "
+ "entity %u\n", entity->id);
+ return ret;
+ }
+ }
+
+ return 0;
+}
diff --git a/drivers/media/video/uvc/uvc_isight.c b/drivers/media/video/uvc/uvc_isight.c
new file mode 100644
index 00000000..8510e725
--- /dev/null
+++ b/drivers/media/video/uvc/uvc_isight.c
@@ -0,0 +1,137 @@
+/*
+ * uvc_isight.c -- USB Video Class driver - iSight support
+ *
+ * Copyright (C) 2006-2007
+ * Ivan N. Zlatev <contact@i-nz.net>
+ * Copyright (C) 2008-2009
+ * Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/usb.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+
+#include "uvcvideo.h"
+
+/* Built-in iSight webcams implements most of UVC 1.0 except a
+ * different packet format. Instead of sending a header at the
+ * beginning of each isochronous transfer payload, the webcam sends a
+ * single header per image (on its own in a packet), followed by
+ * packets containing data only.
+ *
+ * Offset Size (bytes) Description
+ * ------------------------------------------------------------------
+ * 0x00 1 Header length
+ * 0x01 1 Flags (UVC-compliant)
+ * 0x02 4 Always equal to '11223344'
+ * 0x06 8 Always equal to 'deadbeefdeadface'
+ * 0x0e 16 Unknown
+ *
+ * The header can be prefixed by an optional, unknown-purpose byte.
+ */
+
+static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf,
+ const __u8 *data, unsigned int len)
+{
+ static const __u8 hdr[] = {
+ 0x11, 0x22, 0x33, 0x44,
+ 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xfa, 0xce
+ };
+
+ unsigned int maxlen, nbytes;
+ __u8 *mem;
+ int is_header = 0;
+
+ if (buf == NULL)
+ return 0;
+
+ if ((len >= 14 && memcmp(&data[2], hdr, 12) == 0) ||
+ (len >= 15 && memcmp(&data[3], hdr, 12) == 0)) {
+ uvc_trace(UVC_TRACE_FRAME, "iSight header found\n");
+ is_header = 1;
+ }
+
+ /* Synchronize to the input stream by waiting for a header packet. */
+ if (buf->state != UVC_BUF_STATE_ACTIVE) {
+ if (!is_header) {
+ uvc_trace(UVC_TRACE_FRAME, "Dropping packet (out of "
+ "sync).\n");
+ return 0;
+ }
+
+ buf->state = UVC_BUF_STATE_ACTIVE;
+ }
+
+ /* Mark the buffer as done if we're at the beginning of a new frame.
+ *
+ * Empty buffers (bytesused == 0) don't trigger end of frame detection
+ * as it doesn't make sense to return an empty buffer.
+ */
+ if (is_header && buf->bytesused != 0) {
+ buf->state = UVC_BUF_STATE_DONE;
+ return -EAGAIN;
+ }
+
+ /* Copy the video data to the buffer. Skip header packets, as they
+ * contain no data.
+ */
+ if (!is_header) {
+ maxlen = buf->length - buf->bytesused;
+ mem = buf->mem + buf->bytesused;
+ nbytes = min(len, maxlen);
+ memcpy(mem, data, nbytes);
+ buf->bytesused += nbytes;
+
+ if (len > maxlen || buf->bytesused == buf->length) {
+ uvc_trace(UVC_TRACE_FRAME, "Frame complete "
+ "(overflow).\n");
+ buf->state = UVC_BUF_STATE_DONE;
+ }
+ }
+
+ return 0;
+}
+
+void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream,
+ struct uvc_buffer *buf)
+{
+ int ret, i;
+
+ for (i = 0; i < urb->number_of_packets; ++i) {
+ if (urb->iso_frame_desc[i].status < 0) {
+ uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame "
+ "lost (%d).\n",
+ urb->iso_frame_desc[i].status);
+ }
+
+ /* Decode the payload packet.
+ * uvc_video_decode is entered twice when a frame transition
+ * has been detected because the end of frame can only be
+ * reliably detected when the first packet of the new frame
+ * is processed. The first pass detects the transition and
+ * closes the previous frame's buffer, the second pass
+ * processes the data of the first payload of the new frame.
+ */
+ do {
+ ret = isight_decode(&stream->queue, buf,
+ urb->transfer_buffer +
+ urb->iso_frame_desc[i].offset,
+ urb->iso_frame_desc[i].actual_length);
+
+ if (buf == NULL)
+ break;
+
+ if (buf->state == UVC_BUF_STATE_DONE ||
+ buf->state == UVC_BUF_STATE_ERROR)
+ buf = uvc_queue_next_buffer(&stream->queue,
+ buf);
+ } while (ret == -EAGAIN);
+ }
+}
diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c
new file mode 100644
index 00000000..8f54e24e
--- /dev/null
+++ b/drivers/media/video/uvc/uvc_queue.c
@@ -0,0 +1,376 @@
+/*
+ * uvc_queue.c -- USB Video Class driver - Buffers management
+ *
+ * Copyright (C) 2005-2010
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/atomic.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+#include <media/videobuf2-vmalloc.h>
+
+#include "uvcvideo.h"
+
+/* ------------------------------------------------------------------------
+ * Video buffers queue management.
+ *
+ * Video queues is initialized by uvc_queue_init(). The function performs
+ * basic initialization of the uvc_video_queue struct and never fails.
+ *
+ * Video buffers are managed by videobuf2. The driver uses a mutex to protect
+ * the videobuf2 queue operations by serializing calls to videobuf2 and a
+ * spinlock to protect the IRQ queue that holds the buffers to be processed by
+ * the driver.
+ */
+
+/* -----------------------------------------------------------------------------
+ * videobuf2 queue operations
+ */
+
+static int uvc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
+ struct uvc_streaming *stream =
+ container_of(queue, struct uvc_streaming, queue);
+
+ if (*nbuffers > UVC_MAX_VIDEO_BUFFERS)
+ *nbuffers = UVC_MAX_VIDEO_BUFFERS;
+
+ *nplanes = 1;
+
+ sizes[0] = stream->ctrl.dwMaxVideoFrameSize;
+
+ return 0;
+}
+
+static int uvc_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
+ struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf);
+
+ if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) {
+ uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n");
+ return -EINVAL;
+ }
+
+ if (unlikely(queue->flags & UVC_QUEUE_DISCONNECTED))
+ return -ENODEV;
+
+ buf->state = UVC_BUF_STATE_QUEUED;
+ buf->error = 0;
+ buf->mem = vb2_plane_vaddr(vb, 0);
+ buf->length = vb2_plane_size(vb, 0);
+ if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ buf->bytesused = 0;
+ else
+ buf->bytesused = vb2_get_plane_payload(vb, 0);
+
+ return 0;
+}
+
+static void uvc_buffer_queue(struct vb2_buffer *vb)
+{
+ struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
+ struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf);
+ unsigned long flags;
+
+ spin_lock_irqsave(&queue->irqlock, flags);
+ if (likely(!(queue->flags & UVC_QUEUE_DISCONNECTED))) {
+ list_add_tail(&buf->queue, &queue->irqqueue);
+ } else {
+ /* If the device is disconnected return the buffer to userspace
+ * directly. The next QBUF call will fail with -ENODEV.
+ */
+ buf->state = UVC_BUF_STATE_ERROR;
+ vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR);
+ }
+
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+}
+
+static int uvc_buffer_finish(struct vb2_buffer *vb)
+{
+ struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
+ struct uvc_streaming *stream =
+ container_of(queue, struct uvc_streaming, queue);
+ struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf);
+
+ uvc_video_clock_update(stream, &vb->v4l2_buf, buf);
+ return 0;
+}
+
+static struct vb2_ops uvc_queue_qops = {
+ .queue_setup = uvc_queue_setup,
+ .buf_prepare = uvc_buffer_prepare,
+ .buf_queue = uvc_buffer_queue,
+ .buf_finish = uvc_buffer_finish,
+};
+
+void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,
+ int drop_corrupted)
+{
+ queue->queue.type = type;
+ queue->queue.io_modes = VB2_MMAP | VB2_USERPTR;
+ queue->queue.drv_priv = queue;
+ queue->queue.buf_struct_size = sizeof(struct uvc_buffer);
+ queue->queue.ops = &uvc_queue_qops;
+ queue->queue.mem_ops = &vb2_vmalloc_memops;
+ vb2_queue_init(&queue->queue);
+
+ mutex_init(&queue->mutex);
+ spin_lock_init(&queue->irqlock);
+ INIT_LIST_HEAD(&queue->irqqueue);
+ queue->flags = drop_corrupted ? UVC_QUEUE_DROP_CORRUPTED : 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 queue operations
+ */
+
+int uvc_alloc_buffers(struct uvc_video_queue *queue,
+ struct v4l2_requestbuffers *rb)
+{
+ int ret;
+
+ mutex_lock(&queue->mutex);
+ ret = vb2_reqbufs(&queue->queue, rb);
+ mutex_unlock(&queue->mutex);
+
+ return ret ? ret : rb->count;
+}
+
+void uvc_free_buffers(struct uvc_video_queue *queue)
+{
+ mutex_lock(&queue->mutex);
+ vb2_queue_release(&queue->queue);
+ mutex_unlock(&queue->mutex);
+}
+
+int uvc_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
+{
+ int ret;
+
+ mutex_lock(&queue->mutex);
+ ret = vb2_querybuf(&queue->queue, buf);
+ mutex_unlock(&queue->mutex);
+
+ return ret;
+}
+
+int uvc_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
+{
+ int ret;
+
+ mutex_lock(&queue->mutex);
+ ret = vb2_qbuf(&queue->queue, buf);
+ mutex_unlock(&queue->mutex);
+
+ return ret;
+}
+
+int uvc_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf,
+ int nonblocking)
+{
+ int ret;
+
+ mutex_lock(&queue->mutex);
+ ret = vb2_dqbuf(&queue->queue, buf, nonblocking);
+ mutex_unlock(&queue->mutex);
+
+ return ret;
+}
+
+int uvc_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma)
+{
+ int ret;
+
+ mutex_lock(&queue->mutex);
+ ret = vb2_mmap(&queue->queue, vma);
+ mutex_unlock(&queue->mutex);
+
+ return ret;
+}
+
+unsigned int uvc_queue_poll(struct uvc_video_queue *queue, struct file *file,
+ poll_table *wait)
+{
+ unsigned int ret;
+
+ mutex_lock(&queue->mutex);
+ ret = vb2_poll(&queue->queue, file, wait);
+ mutex_unlock(&queue->mutex);
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ *
+ */
+
+/*
+ * Check if buffers have been allocated.
+ */
+int uvc_queue_allocated(struct uvc_video_queue *queue)
+{
+ int allocated;
+
+ mutex_lock(&queue->mutex);
+ allocated = vb2_is_busy(&queue->queue);
+ mutex_unlock(&queue->mutex);
+
+ return allocated;
+}
+
+#ifndef CONFIG_MMU
+/*
+ * Get unmapped area.
+ *
+ * NO-MMU arch need this function to make mmap() work correctly.
+ */
+unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue,
+ unsigned long pgoff)
+{
+ struct uvc_buffer *buffer;
+ unsigned int i;
+ unsigned long ret;
+
+ mutex_lock(&queue->mutex);
+ for (i = 0; i < queue->count; ++i) {
+ buffer = &queue->buffer[i];
+ if ((buffer->buf.m.offset >> PAGE_SHIFT) == pgoff)
+ break;
+ }
+ if (i == queue->count) {
+ ret = -EINVAL;
+ goto done;
+ }
+ ret = (unsigned long)buf->mem;
+done:
+ mutex_unlock(&queue->mutex);
+ return ret;
+}
+#endif
+
+/*
+ * Enable or disable the video buffers queue.
+ *
+ * The queue must be enabled before starting video acquisition and must be
+ * disabled after stopping it. This ensures that the video buffers queue
+ * state can be properly initialized before buffers are accessed from the
+ * interrupt handler.
+ *
+ * Enabling the video queue returns -EBUSY if the queue is already enabled.
+ *
+ * Disabling the video queue cancels the queue and removes all buffers from
+ * the main queue.
+ *
+ * This function can't be called from interrupt context. Use
+ * uvc_queue_cancel() instead.
+ */
+int uvc_queue_enable(struct uvc_video_queue *queue, int enable)
+{
+ unsigned long flags;
+ int ret;
+
+ mutex_lock(&queue->mutex);
+ if (enable) {
+ ret = vb2_streamon(&queue->queue, queue->queue.type);
+ if (ret < 0)
+ goto done;
+
+ queue->buf_used = 0;
+ } else {
+ ret = vb2_streamoff(&queue->queue, queue->queue.type);
+ if (ret < 0)
+ goto done;
+
+ spin_lock_irqsave(&queue->irqlock, flags);
+ INIT_LIST_HEAD(&queue->irqqueue);
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+ }
+
+done:
+ mutex_unlock(&queue->mutex);
+ return ret;
+}
+
+/*
+ * Cancel the video buffers queue.
+ *
+ * Cancelling the queue marks all buffers on the irq queue as erroneous,
+ * wakes them up and removes them from the queue.
+ *
+ * If the disconnect parameter is set, further calls to uvc_queue_buffer will
+ * fail with -ENODEV.
+ *
+ * This function acquires the irq spinlock and can be called from interrupt
+ * context.
+ */
+void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect)
+{
+ struct uvc_buffer *buf;
+ unsigned long flags;
+
+ spin_lock_irqsave(&queue->irqlock, flags);
+ while (!list_empty(&queue->irqqueue)) {
+ buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
+ queue);
+ list_del(&buf->queue);
+ buf->state = UVC_BUF_STATE_ERROR;
+ vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR);
+ }
+ /* This must be protected by the irqlock spinlock to avoid race
+ * conditions between uvc_buffer_queue and the disconnection event that
+ * could result in an interruptible wait in uvc_dequeue_buffer. Do not
+ * blindly replace this logic by checking for the UVC_QUEUE_DISCONNECTED
+ * state outside the queue code.
+ */
+ if (disconnect)
+ queue->flags |= UVC_QUEUE_DISCONNECTED;
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+}
+
+struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
+ struct uvc_buffer *buf)
+{
+ struct uvc_buffer *nextbuf;
+ unsigned long flags;
+
+ if ((queue->flags & UVC_QUEUE_DROP_CORRUPTED) && buf->error) {
+ buf->error = 0;
+ buf->state = UVC_BUF_STATE_QUEUED;
+ vb2_set_plane_payload(&buf->buf, 0, 0);
+ return buf;
+ }
+
+ spin_lock_irqsave(&queue->irqlock, flags);
+ list_del(&buf->queue);
+ if (!list_empty(&queue->irqqueue))
+ nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
+ queue);
+ else
+ nextbuf = NULL;
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+
+ buf->state = buf->error ? VB2_BUF_STATE_ERROR : UVC_BUF_STATE_DONE;
+ vb2_set_plane_payload(&buf->buf, 0, buf->bytesused);
+ vb2_buffer_done(&buf->buf, VB2_BUF_STATE_DONE);
+
+ return nextbuf;
+}
diff --git a/drivers/media/video/uvc/uvc_status.c b/drivers/media/video/uvc/uvc_status.c
new file mode 100644
index 00000000..b7492775
--- /dev/null
+++ b/drivers/media/video/uvc/uvc_status.c
@@ -0,0 +1,237 @@
+/*
+ * uvc_status.c -- USB Video Class driver - Status endpoint
+ *
+ * Copyright (C) 2005-2009
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+
+#include "uvcvideo.h"
+
+/* --------------------------------------------------------------------------
+ * Input device
+ */
+#ifdef CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV
+static int uvc_input_init(struct uvc_device *dev)
+{
+ struct input_dev *input;
+ int ret;
+
+ input = input_allocate_device();
+ if (input == NULL)
+ return -ENOMEM;
+
+ usb_make_path(dev->udev, dev->input_phys, sizeof(dev->input_phys));
+ strlcat(dev->input_phys, "/button", sizeof(dev->input_phys));
+
+ input->name = dev->name;
+ input->phys = dev->input_phys;
+ usb_to_input_id(dev->udev, &input->id);
+ input->dev.parent = &dev->intf->dev;
+
+ __set_bit(EV_KEY, input->evbit);
+ __set_bit(KEY_CAMERA, input->keybit);
+
+ if ((ret = input_register_device(input)) < 0)
+ goto error;
+
+ dev->input = input;
+ return 0;
+
+error:
+ input_free_device(input);
+ return ret;
+}
+
+static void uvc_input_cleanup(struct uvc_device *dev)
+{
+ if (dev->input)
+ input_unregister_device(dev->input);
+}
+
+static void uvc_input_report_key(struct uvc_device *dev, unsigned int code,
+ int value)
+{
+ if (dev->input) {
+ input_report_key(dev->input, code, value);
+ input_sync(dev->input);
+ }
+}
+
+#else
+#define uvc_input_init(dev)
+#define uvc_input_cleanup(dev)
+#define uvc_input_report_key(dev, code, value)
+#endif /* CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV */
+
+/* --------------------------------------------------------------------------
+ * Status interrupt endpoint
+ */
+static void uvc_event_streaming(struct uvc_device *dev, __u8 *data, int len)
+{
+ if (len < 3) {
+ uvc_trace(UVC_TRACE_STATUS, "Invalid streaming status event "
+ "received.\n");
+ return;
+ }
+
+ if (data[2] == 0) {
+ if (len < 4)
+ return;
+ uvc_trace(UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n",
+ data[1], data[3] ? "pressed" : "released", len);
+ uvc_input_report_key(dev, KEY_CAMERA, data[3]);
+ } else {
+ uvc_trace(UVC_TRACE_STATUS, "Stream %u error event %02x %02x "
+ "len %d.\n", data[1], data[2], data[3], len);
+ }
+}
+
+static void uvc_event_control(struct uvc_device *dev, __u8 *data, int len)
+{
+ char *attrs[3] = { "value", "info", "failure" };
+
+ if (len < 6 || data[2] != 0 || data[4] > 2) {
+ uvc_trace(UVC_TRACE_STATUS, "Invalid control status event "
+ "received.\n");
+ return;
+ }
+
+ uvc_trace(UVC_TRACE_STATUS, "Control %u/%u %s change len %d.\n",
+ data[1], data[3], attrs[data[4]], len);
+}
+
+static void uvc_status_complete(struct urb *urb)
+{
+ struct uvc_device *dev = urb->context;
+ int len, ret;
+
+ switch (urb->status) {
+ case 0:
+ break;
+
+ case -ENOENT: /* usb_kill_urb() called. */
+ case -ECONNRESET: /* usb_unlink_urb() called. */
+ case -ESHUTDOWN: /* The endpoint is being disabled. */
+ case -EPROTO: /* Device is disconnected (reported by some
+ * host controller). */
+ return;
+
+ default:
+ uvc_printk(KERN_WARNING, "Non-zero status (%d) in status "
+ "completion handler.\n", urb->status);
+ return;
+ }
+
+ len = urb->actual_length;
+ if (len > 0) {
+ switch (dev->status[0] & 0x0f) {
+ case UVC_STATUS_TYPE_CONTROL:
+ uvc_event_control(dev, dev->status, len);
+ break;
+
+ case UVC_STATUS_TYPE_STREAMING:
+ uvc_event_streaming(dev, dev->status, len);
+ break;
+
+ default:
+ uvc_trace(UVC_TRACE_STATUS, "Unknown status event "
+ "type %u.\n", dev->status[0]);
+ break;
+ }
+ }
+
+ /* Resubmit the URB. */
+ urb->interval = dev->int_ep->desc.bInterval;
+ if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
+ uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).\n",
+ ret);
+ }
+}
+
+int uvc_status_init(struct uvc_device *dev)
+{
+ struct usb_host_endpoint *ep = dev->int_ep;
+ unsigned int pipe;
+ int interval;
+
+ if (ep == NULL)
+ return 0;
+
+ uvc_input_init(dev);
+
+ dev->status = kzalloc(UVC_MAX_STATUS_SIZE, GFP_KERNEL);
+ if (dev->status == NULL)
+ return -ENOMEM;
+
+ dev->int_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (dev->int_urb == NULL) {
+ kfree(dev->status);
+ return -ENOMEM;
+ }
+
+ pipe = usb_rcvintpipe(dev->udev, ep->desc.bEndpointAddress);
+
+ /* For high-speed interrupt endpoints, the bInterval value is used as
+ * an exponent of two. Some developers forgot about it.
+ */
+ interval = ep->desc.bInterval;
+ if (interval > 16 && dev->udev->speed == USB_SPEED_HIGH &&
+ (dev->quirks & UVC_QUIRK_STATUS_INTERVAL))
+ interval = fls(interval) - 1;
+
+ usb_fill_int_urb(dev->int_urb, dev->udev, pipe,
+ dev->status, UVC_MAX_STATUS_SIZE, uvc_status_complete,
+ dev, interval);
+
+ return 0;
+}
+
+void uvc_status_cleanup(struct uvc_device *dev)
+{
+ usb_kill_urb(dev->int_urb);
+ usb_free_urb(dev->int_urb);
+ kfree(dev->status);
+ uvc_input_cleanup(dev);
+}
+
+int uvc_status_start(struct uvc_device *dev)
+{
+ if (dev->int_urb == NULL)
+ return 0;
+
+ return usb_submit_urb(dev->int_urb, GFP_KERNEL);
+}
+
+void uvc_status_stop(struct uvc_device *dev)
+{
+ usb_kill_urb(dev->int_urb);
+}
+
+int uvc_status_suspend(struct uvc_device *dev)
+{
+ if (atomic_read(&dev->users))
+ usb_kill_urb(dev->int_urb);
+
+ return 0;
+}
+
+int uvc_status_resume(struct uvc_device *dev)
+{
+ if (dev->int_urb == NULL || atomic_read(&dev->users) == 0)
+ return 0;
+
+ return usb_submit_urb(dev->int_urb, GFP_NOIO);
+}
+
diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c
new file mode 100644
index 00000000..53ab9729
--- /dev/null
+++ b/drivers/media/video/uvc/uvc_v4l2.c
@@ -0,0 +1,1291 @@
+/*
+ * uvc_v4l2.c -- USB Video Class driver - V4L2 API
+ *
+ * Copyright (C) 2005-2010
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/compat.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/wait.h>
+#include <linux/atomic.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+
+#include "uvcvideo.h"
+
+/* ------------------------------------------------------------------------
+ * UVC ioctls
+ */
+static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain,
+ struct uvc_xu_control_mapping *xmap)
+{
+ struct uvc_control_mapping *map;
+ unsigned int size;
+ int ret;
+
+ map = kzalloc(sizeof *map, GFP_KERNEL);
+ if (map == NULL)
+ return -ENOMEM;
+
+ map->id = xmap->id;
+ memcpy(map->name, xmap->name, sizeof map->name);
+ memcpy(map->entity, xmap->entity, sizeof map->entity);
+ map->selector = xmap->selector;
+ map->size = xmap->size;
+ map->offset = xmap->offset;
+ map->v4l2_type = xmap->v4l2_type;
+ map->data_type = xmap->data_type;
+
+ switch (xmap->v4l2_type) {
+ case V4L2_CTRL_TYPE_INTEGER:
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ case V4L2_CTRL_TYPE_BUTTON:
+ break;
+
+ case V4L2_CTRL_TYPE_MENU:
+ /* Prevent excessive memory consumption, as well as integer
+ * overflows.
+ */
+ if (xmap->menu_count == 0 ||
+ xmap->menu_count > UVC_MAX_CONTROL_MENU_ENTRIES) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ size = xmap->menu_count * sizeof(*map->menu_info);
+ map->menu_info = kmalloc(size, GFP_KERNEL);
+ if (map->menu_info == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ if (copy_from_user(map->menu_info, xmap->menu_info, size)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ map->menu_count = xmap->menu_count;
+ break;
+
+ default:
+ uvc_trace(UVC_TRACE_CONTROL, "Unsupported V4L2 control type "
+ "%u.\n", xmap->v4l2_type);
+ ret = -ENOTTY;
+ goto done;
+ }
+
+ ret = uvc_ctrl_add_mapping(chain, map);
+
+done:
+ kfree(map->menu_info);
+ kfree(map);
+
+ return ret;
+}
+
+/* ------------------------------------------------------------------------
+ * V4L2 interface
+ */
+
+/*
+ * Find the frame interval closest to the requested frame interval for the
+ * given frame format and size. This should be done by the device as part of
+ * the Video Probe and Commit negotiation, but some hardware don't implement
+ * that feature.
+ */
+static __u32 uvc_try_frame_interval(struct uvc_frame *frame, __u32 interval)
+{
+ unsigned int i;
+
+ if (frame->bFrameIntervalType) {
+ __u32 best = -1, dist;
+
+ for (i = 0; i < frame->bFrameIntervalType; ++i) {
+ dist = interval > frame->dwFrameInterval[i]
+ ? interval - frame->dwFrameInterval[i]
+ : frame->dwFrameInterval[i] - interval;
+
+ if (dist > best)
+ break;
+
+ best = dist;
+ }
+
+ interval = frame->dwFrameInterval[i-1];
+ } else {
+ const __u32 min = frame->dwFrameInterval[0];
+ const __u32 max = frame->dwFrameInterval[1];
+ const __u32 step = frame->dwFrameInterval[2];
+
+ interval = min + (interval - min + step/2) / step * step;
+ if (interval > max)
+ interval = max;
+ }
+
+ return interval;
+}
+
+static int uvc_v4l2_try_format(struct uvc_streaming *stream,
+ struct v4l2_format *fmt, struct uvc_streaming_control *probe,
+ struct uvc_format **uvc_format, struct uvc_frame **uvc_frame)
+{
+ struct uvc_format *format = NULL;
+ struct uvc_frame *frame = NULL;
+ __u16 rw, rh;
+ unsigned int d, maxd;
+ unsigned int i;
+ __u32 interval;
+ int ret = 0;
+ __u8 *fcc;
+
+ if (fmt->type != stream->type)
+ return -EINVAL;
+
+ fcc = (__u8 *)&fmt->fmt.pix.pixelformat;
+ uvc_trace(UVC_TRACE_FORMAT, "Trying format 0x%08x (%c%c%c%c): %ux%u.\n",
+ fmt->fmt.pix.pixelformat,
+ fcc[0], fcc[1], fcc[2], fcc[3],
+ fmt->fmt.pix.width, fmt->fmt.pix.height);
+
+ /* Check if the hardware supports the requested format. */
+ for (i = 0; i < stream->nformats; ++i) {
+ format = &stream->format[i];
+ if (format->fcc == fmt->fmt.pix.pixelformat)
+ break;
+ }
+
+ if (format == NULL || format->fcc != fmt->fmt.pix.pixelformat) {
+ uvc_trace(UVC_TRACE_FORMAT, "Unsupported format 0x%08x.\n",
+ fmt->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ /* Find the closest image size. The distance between image sizes is
+ * the size in pixels of the non-overlapping regions between the
+ * requested size and the frame-specified size.
+ */
+ rw = fmt->fmt.pix.width;
+ rh = fmt->fmt.pix.height;
+ maxd = (unsigned int)-1;
+
+ for (i = 0; i < format->nframes; ++i) {
+ __u16 w = format->frame[i].wWidth;
+ __u16 h = format->frame[i].wHeight;
+
+ d = min(w, rw) * min(h, rh);
+ d = w*h + rw*rh - 2*d;
+ if (d < maxd) {
+ maxd = d;
+ frame = &format->frame[i];
+ }
+
+ if (maxd == 0)
+ break;
+ }
+
+ if (frame == NULL) {
+ uvc_trace(UVC_TRACE_FORMAT, "Unsupported size %ux%u.\n",
+ fmt->fmt.pix.width, fmt->fmt.pix.height);
+ return -EINVAL;
+ }
+
+ /* Use the default frame interval. */
+ interval = frame->dwDefaultFrameInterval;
+ uvc_trace(UVC_TRACE_FORMAT, "Using default frame interval %u.%u us "
+ "(%u.%u fps).\n", interval/10, interval%10, 10000000/interval,
+ (100000000/interval)%10);
+
+ /* Set the format index, frame index and frame interval. */
+ memset(probe, 0, sizeof *probe);
+ probe->bmHint = 1; /* dwFrameInterval */
+ probe->bFormatIndex = format->index;
+ probe->bFrameIndex = frame->bFrameIndex;
+ probe->dwFrameInterval = uvc_try_frame_interval(frame, interval);
+ /* Some webcams stall the probe control set request when the
+ * dwMaxVideoFrameSize field is set to zero. The UVC specification
+ * clearly states that the field is read-only from the host, so this
+ * is a webcam bug. Set dwMaxVideoFrameSize to the value reported by
+ * the webcam to work around the problem.
+ *
+ * The workaround could probably be enabled for all webcams, so the
+ * quirk can be removed if needed. It's currently useful to detect
+ * webcam bugs and fix them before they hit the market (providing
+ * developers test their webcams with the Linux driver as well as with
+ * the Windows driver).
+ */
+ mutex_lock(&stream->mutex);
+ if (stream->dev->quirks & UVC_QUIRK_PROBE_EXTRAFIELDS)
+ probe->dwMaxVideoFrameSize =
+ stream->ctrl.dwMaxVideoFrameSize;
+
+ /* Probe the device. */
+ ret = uvc_probe_video(stream, probe);
+ mutex_unlock(&stream->mutex);
+ if (ret < 0)
+ goto done;
+
+ fmt->fmt.pix.width = frame->wWidth;
+ fmt->fmt.pix.height = frame->wHeight;
+ fmt->fmt.pix.field = V4L2_FIELD_NONE;
+ fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8;
+ fmt->fmt.pix.sizeimage = probe->dwMaxVideoFrameSize;
+ fmt->fmt.pix.colorspace = format->colorspace;
+ fmt->fmt.pix.priv = 0;
+
+ if (uvc_format != NULL)
+ *uvc_format = format;
+ if (uvc_frame != NULL)
+ *uvc_frame = frame;
+
+done:
+ return ret;
+}
+
+static int uvc_v4l2_get_format(struct uvc_streaming *stream,
+ struct v4l2_format *fmt)
+{
+ struct uvc_format *format;
+ struct uvc_frame *frame;
+ int ret = 0;
+
+ if (fmt->type != stream->type)
+ return -EINVAL;
+
+ mutex_lock(&stream->mutex);
+ format = stream->cur_format;
+ frame = stream->cur_frame;
+
+ if (format == NULL || frame == NULL) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ fmt->fmt.pix.pixelformat = format->fcc;
+ fmt->fmt.pix.width = frame->wWidth;
+ fmt->fmt.pix.height = frame->wHeight;
+ fmt->fmt.pix.field = V4L2_FIELD_NONE;
+ fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8;
+ fmt->fmt.pix.sizeimage = stream->ctrl.dwMaxVideoFrameSize;
+ fmt->fmt.pix.colorspace = format->colorspace;
+ fmt->fmt.pix.priv = 0;
+
+done:
+ mutex_unlock(&stream->mutex);
+ return ret;
+}
+
+static int uvc_v4l2_set_format(struct uvc_streaming *stream,
+ struct v4l2_format *fmt)
+{
+ struct uvc_streaming_control probe;
+ struct uvc_format *format;
+ struct uvc_frame *frame;
+ int ret;
+
+ if (fmt->type != stream->type)
+ return -EINVAL;
+
+ ret = uvc_v4l2_try_format(stream, fmt, &probe, &format, &frame);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&stream->mutex);
+
+ if (uvc_queue_allocated(&stream->queue)) {
+ ret = -EBUSY;
+ goto done;
+ }
+
+ memcpy(&stream->ctrl, &probe, sizeof probe);
+ stream->cur_format = format;
+ stream->cur_frame = frame;
+
+done:
+ mutex_unlock(&stream->mutex);
+ return ret;
+}
+
+static int uvc_v4l2_get_streamparm(struct uvc_streaming *stream,
+ struct v4l2_streamparm *parm)
+{
+ uint32_t numerator, denominator;
+
+ if (parm->type != stream->type)
+ return -EINVAL;
+
+ mutex_lock(&stream->mutex);
+ numerator = stream->ctrl.dwFrameInterval;
+ mutex_unlock(&stream->mutex);
+
+ denominator = 10000000;
+ uvc_simplify_fraction(&numerator, &denominator, 8, 333);
+
+ memset(parm, 0, sizeof *parm);
+ parm->type = stream->type;
+
+ if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ parm->parm.capture.capturemode = 0;
+ parm->parm.capture.timeperframe.numerator = numerator;
+ parm->parm.capture.timeperframe.denominator = denominator;
+ parm->parm.capture.extendedmode = 0;
+ parm->parm.capture.readbuffers = 0;
+ } else {
+ parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
+ parm->parm.output.outputmode = 0;
+ parm->parm.output.timeperframe.numerator = numerator;
+ parm->parm.output.timeperframe.denominator = denominator;
+ }
+
+ return 0;
+}
+
+static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream,
+ struct v4l2_streamparm *parm)
+{
+ struct uvc_streaming_control probe;
+ struct v4l2_fract timeperframe;
+ uint32_t interval;
+ int ret;
+
+ if (parm->type != stream->type)
+ return -EINVAL;
+
+ if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ timeperframe = parm->parm.capture.timeperframe;
+ else
+ timeperframe = parm->parm.output.timeperframe;
+
+ interval = uvc_fraction_to_interval(timeperframe.numerator,
+ timeperframe.denominator);
+ uvc_trace(UVC_TRACE_FORMAT, "Setting frame interval to %u/%u (%u).\n",
+ timeperframe.numerator, timeperframe.denominator, interval);
+
+ mutex_lock(&stream->mutex);
+
+ if (uvc_queue_streaming(&stream->queue)) {
+ mutex_unlock(&stream->mutex);
+ return -EBUSY;
+ }
+
+ memcpy(&probe, &stream->ctrl, sizeof probe);
+ probe.dwFrameInterval =
+ uvc_try_frame_interval(stream->cur_frame, interval);
+
+ /* Probe the device with the new settings. */
+ ret = uvc_probe_video(stream, &probe);
+ if (ret < 0) {
+ mutex_unlock(&stream->mutex);
+ return ret;
+ }
+
+ memcpy(&stream->ctrl, &probe, sizeof probe);
+ mutex_unlock(&stream->mutex);
+
+ /* Return the actual frame period. */
+ timeperframe.numerator = probe.dwFrameInterval;
+ timeperframe.denominator = 10000000;
+ uvc_simplify_fraction(&timeperframe.numerator,
+ &timeperframe.denominator, 8, 333);
+
+ if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ parm->parm.capture.timeperframe = timeperframe;
+ else
+ parm->parm.output.timeperframe = timeperframe;
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------------
+ * Privilege management
+ */
+
+/*
+ * Privilege management is the multiple-open implementation basis. The current
+ * implementation is completely transparent for the end-user and doesn't
+ * require explicit use of the VIDIOC_G_PRIORITY and VIDIOC_S_PRIORITY ioctls.
+ * Those ioctls enable finer control on the device (by making possible for a
+ * user to request exclusive access to a device), but are not mature yet.
+ * Switching to the V4L2 priority mechanism might be considered in the future
+ * if this situation changes.
+ *
+ * Each open instance of a UVC device can either be in a privileged or
+ * unprivileged state. Only a single instance can be in a privileged state at
+ * a given time. Trying to perform an operation that requires privileges will
+ * automatically acquire the required privileges if possible, or return -EBUSY
+ * otherwise. Privileges are dismissed when closing the instance or when
+ * freeing the video buffers using VIDIOC_REQBUFS.
+ *
+ * Operations that require privileges are:
+ *
+ * - VIDIOC_S_INPUT
+ * - VIDIOC_S_PARM
+ * - VIDIOC_S_FMT
+ * - VIDIOC_REQBUFS
+ */
+static int uvc_acquire_privileges(struct uvc_fh *handle)
+{
+ /* Always succeed if the handle is already privileged. */
+ if (handle->state == UVC_HANDLE_ACTIVE)
+ return 0;
+
+ /* Check if the device already has a privileged handle. */
+ if (atomic_inc_return(&handle->stream->active) != 1) {
+ atomic_dec(&handle->stream->active);
+ return -EBUSY;
+ }
+
+ handle->state = UVC_HANDLE_ACTIVE;
+ return 0;
+}
+
+static void uvc_dismiss_privileges(struct uvc_fh *handle)
+{
+ if (handle->state == UVC_HANDLE_ACTIVE)
+ atomic_dec(&handle->stream->active);
+
+ handle->state = UVC_HANDLE_PASSIVE;
+}
+
+static int uvc_has_privileges(struct uvc_fh *handle)
+{
+ return handle->state == UVC_HANDLE_ACTIVE;
+}
+
+/* ------------------------------------------------------------------------
+ * V4L2 file operations
+ */
+
+static int uvc_v4l2_open(struct file *file)
+{
+ struct uvc_streaming *stream;
+ struct uvc_fh *handle;
+ int ret = 0;
+
+ uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n");
+ stream = video_drvdata(file);
+
+ if (stream->dev->state & UVC_DEV_DISCONNECTED)
+ return -ENODEV;
+
+ ret = usb_autopm_get_interface(stream->dev->intf);
+ if (ret < 0)
+ return ret;
+
+ /* Create the device handle. */
+ handle = kzalloc(sizeof *handle, GFP_KERNEL);
+ if (handle == NULL) {
+ usb_autopm_put_interface(stream->dev->intf);
+ return -ENOMEM;
+ }
+
+ if (atomic_inc_return(&stream->dev->users) == 1) {
+ ret = uvc_status_start(stream->dev);
+ if (ret < 0) {
+ usb_autopm_put_interface(stream->dev->intf);
+ atomic_dec(&stream->dev->users);
+ kfree(handle);
+ return ret;
+ }
+ }
+
+ handle->chain = stream->chain;
+ handle->stream = stream;
+ handle->state = UVC_HANDLE_PASSIVE;
+ file->private_data = handle;
+
+ return 0;
+}
+
+static int uvc_v4l2_release(struct file *file)
+{
+ struct uvc_fh *handle = file->private_data;
+ struct uvc_streaming *stream = handle->stream;
+
+ uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n");
+
+ /* Only free resources if this is a privileged handle. */
+ if (uvc_has_privileges(handle)) {
+ uvc_video_enable(stream, 0);
+ uvc_free_buffers(&stream->queue);
+ }
+
+ /* Release the file handle. */
+ uvc_dismiss_privileges(handle);
+ kfree(handle);
+ file->private_data = NULL;
+
+ if (atomic_dec_return(&stream->dev->users) == 0)
+ uvc_status_stop(stream->dev);
+
+ usb_autopm_put_interface(stream->dev->intf);
+ return 0;
+}
+
+static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct uvc_fh *handle = file->private_data;
+ struct uvc_video_chain *chain = handle->chain;
+ struct uvc_streaming *stream = handle->stream;
+ long ret = 0;
+
+ switch (cmd) {
+ /* Query capabilities */
+ case VIDIOC_QUERYCAP:
+ {
+ struct v4l2_capability *cap = arg;
+
+ memset(cap, 0, sizeof *cap);
+ strlcpy(cap->driver, "uvcvideo", sizeof cap->driver);
+ strlcpy(cap->card, vdev->name, sizeof cap->card);
+ usb_make_path(stream->dev->udev,
+ cap->bus_info, sizeof(cap->bus_info));
+ cap->version = LINUX_VERSION_CODE;
+ if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
+ | V4L2_CAP_STREAMING;
+ else
+ cap->capabilities = V4L2_CAP_VIDEO_OUTPUT
+ | V4L2_CAP_STREAMING;
+ break;
+ }
+
+ /* Get, Set & Query control */
+ case VIDIOC_QUERYCTRL:
+ return uvc_query_v4l2_ctrl(chain, arg);
+
+ case VIDIOC_G_CTRL:
+ {
+ struct v4l2_control *ctrl = arg;
+ struct v4l2_ext_control xctrl;
+
+ memset(&xctrl, 0, sizeof xctrl);
+ xctrl.id = ctrl->id;
+
+ ret = uvc_ctrl_begin(chain);
+ if (ret < 0)
+ return ret;
+
+ ret = uvc_ctrl_get(chain, &xctrl);
+ uvc_ctrl_rollback(chain);
+ if (ret >= 0)
+ ctrl->value = xctrl.value;
+ break;
+ }
+
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *ctrl = arg;
+ struct v4l2_ext_control xctrl;
+
+ memset(&xctrl, 0, sizeof xctrl);
+ xctrl.id = ctrl->id;
+ xctrl.value = ctrl->value;
+
+ ret = uvc_ctrl_begin(chain);
+ if (ret < 0)
+ return ret;
+
+ ret = uvc_ctrl_set(chain, &xctrl);
+ if (ret < 0) {
+ uvc_ctrl_rollback(chain);
+ return ret;
+ }
+ ret = uvc_ctrl_commit(chain);
+ if (ret == 0)
+ ctrl->value = xctrl.value;
+ break;
+ }
+
+ case VIDIOC_QUERYMENU:
+ return uvc_query_v4l2_menu(chain, arg);
+
+ case VIDIOC_G_EXT_CTRLS:
+ {
+ struct v4l2_ext_controls *ctrls = arg;
+ struct v4l2_ext_control *ctrl = ctrls->controls;
+ unsigned int i;
+
+ ret = uvc_ctrl_begin(chain);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ctrls->count; ++ctrl, ++i) {
+ ret = uvc_ctrl_get(chain, ctrl);
+ if (ret < 0) {
+ uvc_ctrl_rollback(chain);
+ ctrls->error_idx = i;
+ return ret;
+ }
+ }
+ ctrls->error_idx = 0;
+ ret = uvc_ctrl_rollback(chain);
+ break;
+ }
+
+ case VIDIOC_S_EXT_CTRLS:
+ case VIDIOC_TRY_EXT_CTRLS:
+ {
+ struct v4l2_ext_controls *ctrls = arg;
+ struct v4l2_ext_control *ctrl = ctrls->controls;
+ unsigned int i;
+
+ ret = uvc_ctrl_begin(chain);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ctrls->count; ++ctrl, ++i) {
+ ret = uvc_ctrl_set(chain, ctrl);
+ if (ret < 0) {
+ uvc_ctrl_rollback(chain);
+ ctrls->error_idx = i;
+ return ret;
+ }
+ }
+
+ ctrls->error_idx = 0;
+
+ if (cmd == VIDIOC_S_EXT_CTRLS)
+ ret = uvc_ctrl_commit(chain);
+ else
+ ret = uvc_ctrl_rollback(chain);
+ break;
+ }
+
+ /* Get, Set & Enum input */
+ case VIDIOC_ENUMINPUT:
+ {
+ const struct uvc_entity *selector = chain->selector;
+ struct v4l2_input *input = arg;
+ struct uvc_entity *iterm = NULL;
+ u32 index = input->index;
+ int pin = 0;
+
+ if (selector == NULL ||
+ (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
+ if (index != 0)
+ return -EINVAL;
+ list_for_each_entry(iterm, &chain->entities, chain) {
+ if (UVC_ENTITY_IS_ITERM(iterm))
+ break;
+ }
+ pin = iterm->id;
+ } else if (index < selector->bNrInPins) {
+ pin = selector->baSourceID[index];
+ list_for_each_entry(iterm, &chain->entities, chain) {
+ if (!UVC_ENTITY_IS_ITERM(iterm))
+ continue;
+ if (iterm->id == pin)
+ break;
+ }
+ }
+
+ if (iterm == NULL || iterm->id != pin)
+ return -EINVAL;
+
+ memset(input, 0, sizeof *input);
+ input->index = index;
+ strlcpy(input->name, iterm->name, sizeof input->name);
+ if (UVC_ENTITY_TYPE(iterm) == UVC_ITT_CAMERA)
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+ break;
+ }
+
+ case VIDIOC_G_INPUT:
+ {
+ u8 input;
+
+ if (chain->selector == NULL ||
+ (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
+ *(int *)arg = 0;
+ break;
+ }
+
+ ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
+ chain->selector->id, chain->dev->intfnum,
+ UVC_SU_INPUT_SELECT_CONTROL, &input, 1);
+ if (ret < 0)
+ return ret;
+
+ *(int *)arg = input - 1;
+ break;
+ }
+
+ case VIDIOC_S_INPUT:
+ {
+ u32 input = *(u32 *)arg + 1;
+
+ if ((ret = uvc_acquire_privileges(handle)) < 0)
+ return ret;
+
+ if (chain->selector == NULL ||
+ (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
+ if (input != 1)
+ return -EINVAL;
+ break;
+ }
+
+ if (input == 0 || input > chain->selector->bNrInPins)
+ return -EINVAL;
+
+ return uvc_query_ctrl(chain->dev, UVC_SET_CUR,
+ chain->selector->id, chain->dev->intfnum,
+ UVC_SU_INPUT_SELECT_CONTROL, &input, 1);
+ }
+
+ /* Try, Get, Set & Enum format */
+ case VIDIOC_ENUM_FMT:
+ {
+ struct v4l2_fmtdesc *fmt = arg;
+ struct uvc_format *format;
+ enum v4l2_buf_type type = fmt->type;
+ __u32 index = fmt->index;
+
+ if (fmt->type != stream->type ||
+ fmt->index >= stream->nformats)
+ return -EINVAL;
+
+ memset(fmt, 0, sizeof(*fmt));
+ fmt->index = index;
+ fmt->type = type;
+
+ format = &stream->format[fmt->index];
+ fmt->flags = 0;
+ if (format->flags & UVC_FMT_FLAG_COMPRESSED)
+ fmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
+ strlcpy(fmt->description, format->name,
+ sizeof fmt->description);
+ fmt->description[sizeof fmt->description - 1] = 0;
+ fmt->pixelformat = format->fcc;
+ break;
+ }
+
+ case VIDIOC_TRY_FMT:
+ {
+ struct uvc_streaming_control probe;
+
+ return uvc_v4l2_try_format(stream, arg, &probe, NULL, NULL);
+ }
+
+ case VIDIOC_S_FMT:
+ if ((ret = uvc_acquire_privileges(handle)) < 0)
+ return ret;
+
+ return uvc_v4l2_set_format(stream, arg);
+
+ case VIDIOC_G_FMT:
+ return uvc_v4l2_get_format(stream, arg);
+
+ /* Frame size enumeration */
+ case VIDIOC_ENUM_FRAMESIZES:
+ {
+ struct v4l2_frmsizeenum *fsize = arg;
+ struct uvc_format *format = NULL;
+ struct uvc_frame *frame;
+ int i;
+
+ /* Look for the given pixel format */
+ for (i = 0; i < stream->nformats; i++) {
+ if (stream->format[i].fcc ==
+ fsize->pixel_format) {
+ format = &stream->format[i];
+ break;
+ }
+ }
+ if (format == NULL)
+ return -EINVAL;
+
+ if (fsize->index >= format->nframes)
+ return -EINVAL;
+
+ frame = &format->frame[fsize->index];
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = frame->wWidth;
+ fsize->discrete.height = frame->wHeight;
+ break;
+ }
+
+ /* Frame interval enumeration */
+ case VIDIOC_ENUM_FRAMEINTERVALS:
+ {
+ struct v4l2_frmivalenum *fival = arg;
+ struct uvc_format *format = NULL;
+ struct uvc_frame *frame = NULL;
+ int i;
+
+ /* Look for the given pixel format and frame size */
+ for (i = 0; i < stream->nformats; i++) {
+ if (stream->format[i].fcc ==
+ fival->pixel_format) {
+ format = &stream->format[i];
+ break;
+ }
+ }
+ if (format == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < format->nframes; i++) {
+ if (format->frame[i].wWidth == fival->width &&
+ format->frame[i].wHeight == fival->height) {
+ frame = &format->frame[i];
+ break;
+ }
+ }
+ if (frame == NULL)
+ return -EINVAL;
+
+ if (frame->bFrameIntervalType) {
+ if (fival->index >= frame->bFrameIntervalType)
+ return -EINVAL;
+
+ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ fival->discrete.numerator =
+ frame->dwFrameInterval[fival->index];
+ fival->discrete.denominator = 10000000;
+ uvc_simplify_fraction(&fival->discrete.numerator,
+ &fival->discrete.denominator, 8, 333);
+ } else {
+ fival->type = V4L2_FRMIVAL_TYPE_STEPWISE;
+ fival->stepwise.min.numerator =
+ frame->dwFrameInterval[0];
+ fival->stepwise.min.denominator = 10000000;
+ fival->stepwise.max.numerator =
+ frame->dwFrameInterval[1];
+ fival->stepwise.max.denominator = 10000000;
+ fival->stepwise.step.numerator =
+ frame->dwFrameInterval[2];
+ fival->stepwise.step.denominator = 10000000;
+ uvc_simplify_fraction(&fival->stepwise.min.numerator,
+ &fival->stepwise.min.denominator, 8, 333);
+ uvc_simplify_fraction(&fival->stepwise.max.numerator,
+ &fival->stepwise.max.denominator, 8, 333);
+ uvc_simplify_fraction(&fival->stepwise.step.numerator,
+ &fival->stepwise.step.denominator, 8, 333);
+ }
+ break;
+ }
+
+ /* Get & Set streaming parameters */
+ case VIDIOC_G_PARM:
+ return uvc_v4l2_get_streamparm(stream, arg);
+
+ case VIDIOC_S_PARM:
+ if ((ret = uvc_acquire_privileges(handle)) < 0)
+ return ret;
+
+ return uvc_v4l2_set_streamparm(stream, arg);
+
+ /* Cropping and scaling */
+ case VIDIOC_CROPCAP:
+ {
+ struct v4l2_cropcap *ccap = arg;
+
+ if (ccap->type != stream->type)
+ return -EINVAL;
+
+ ccap->bounds.left = 0;
+ ccap->bounds.top = 0;
+
+ mutex_lock(&stream->mutex);
+ ccap->bounds.width = stream->cur_frame->wWidth;
+ ccap->bounds.height = stream->cur_frame->wHeight;
+ mutex_unlock(&stream->mutex);
+
+ ccap->defrect = ccap->bounds;
+
+ ccap->pixelaspect.numerator = 1;
+ ccap->pixelaspect.denominator = 1;
+ break;
+ }
+
+ case VIDIOC_G_CROP:
+ case VIDIOC_S_CROP:
+ return -EINVAL;
+
+ /* Buffers & streaming */
+ case VIDIOC_REQBUFS:
+ if ((ret = uvc_acquire_privileges(handle)) < 0)
+ return ret;
+
+ mutex_lock(&stream->mutex);
+ ret = uvc_alloc_buffers(&stream->queue, arg);
+ mutex_unlock(&stream->mutex);
+ if (ret < 0)
+ return ret;
+
+ if (ret == 0)
+ uvc_dismiss_privileges(handle);
+
+ ret = 0;
+ break;
+
+ case VIDIOC_QUERYBUF:
+ {
+ struct v4l2_buffer *buf = arg;
+
+ if (!uvc_has_privileges(handle))
+ return -EBUSY;
+
+ return uvc_query_buffer(&stream->queue, buf);
+ }
+
+ case VIDIOC_QBUF:
+ if (!uvc_has_privileges(handle))
+ return -EBUSY;
+
+ return uvc_queue_buffer(&stream->queue, arg);
+
+ case VIDIOC_DQBUF:
+ if (!uvc_has_privileges(handle))
+ return -EBUSY;
+
+ return uvc_dequeue_buffer(&stream->queue, arg,
+ file->f_flags & O_NONBLOCK);
+
+ case VIDIOC_STREAMON:
+ {
+ int *type = arg;
+
+ if (*type != stream->type)
+ return -EINVAL;
+
+ if (!uvc_has_privileges(handle))
+ return -EBUSY;
+
+ mutex_lock(&stream->mutex);
+ ret = uvc_video_enable(stream, 1);
+ mutex_unlock(&stream->mutex);
+ if (ret < 0)
+ return ret;
+ break;
+ }
+
+ case VIDIOC_STREAMOFF:
+ {
+ int *type = arg;
+
+ if (*type != stream->type)
+ return -EINVAL;
+
+ if (!uvc_has_privileges(handle))
+ return -EBUSY;
+
+ return uvc_video_enable(stream, 0);
+ }
+
+ /* Analog video standards make no sense for digital cameras. */
+ case VIDIOC_ENUMSTD:
+ case VIDIOC_QUERYSTD:
+ case VIDIOC_G_STD:
+ case VIDIOC_S_STD:
+
+ case VIDIOC_OVERLAY:
+
+ case VIDIOC_ENUMAUDIO:
+ case VIDIOC_ENUMAUDOUT:
+
+ case VIDIOC_ENUMOUTPUT:
+ uvc_trace(UVC_TRACE_IOCTL, "Unsupported ioctl 0x%08x\n", cmd);
+ return -EINVAL;
+
+ case UVCIOC_CTRL_MAP:
+ return uvc_ioctl_ctrl_map(chain, arg);
+
+ case UVCIOC_CTRL_QUERY:
+ return uvc_xu_ctrl_query(chain, arg);
+
+ default:
+ uvc_trace(UVC_TRACE_IOCTL, "Unknown ioctl 0x%08x\n", cmd);
+ return -ENOTTY;
+ }
+
+ return ret;
+}
+
+static long uvc_v4l2_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ if (uvc_trace_param & UVC_TRACE_IOCTL) {
+ uvc_printk(KERN_DEBUG, "uvc_v4l2_ioctl(");
+ v4l_printk_ioctl(cmd);
+ printk(")\n");
+ }
+
+ return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl);
+}
+
+#ifdef CONFIG_COMPAT
+struct uvc_xu_control_mapping32 {
+ __u32 id;
+ __u8 name[32];
+ __u8 entity[16];
+ __u8 selector;
+
+ __u8 size;
+ __u8 offset;
+ __u32 v4l2_type;
+ __u32 data_type;
+
+ compat_caddr_t menu_info;
+ __u32 menu_count;
+
+ __u32 reserved[4];
+};
+
+static int uvc_v4l2_get_xu_mapping(struct uvc_xu_control_mapping *kp,
+ const struct uvc_xu_control_mapping32 __user *up)
+{
+ struct uvc_menu_info __user *umenus;
+ struct uvc_menu_info __user *kmenus;
+ compat_caddr_t p;
+
+ if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
+ __copy_from_user(kp, up, offsetof(typeof(*up), menu_info)) ||
+ __get_user(kp->menu_count, &up->menu_count))
+ return -EFAULT;
+
+ memset(kp->reserved, 0, sizeof(kp->reserved));
+
+ if (kp->menu_count == 0) {
+ kp->menu_info = NULL;
+ return 0;
+ }
+
+ if (__get_user(p, &up->menu_info))
+ return -EFAULT;
+ umenus = compat_ptr(p);
+ if (!access_ok(VERIFY_READ, umenus, kp->menu_count * sizeof(*umenus)))
+ return -EFAULT;
+
+ kmenus = compat_alloc_user_space(kp->menu_count * sizeof(*kmenus));
+ if (kmenus == NULL)
+ return -EFAULT;
+ kp->menu_info = kmenus;
+
+ if (copy_in_user(kmenus, umenus, kp->menu_count * sizeof(*umenus)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int uvc_v4l2_put_xu_mapping(const struct uvc_xu_control_mapping *kp,
+ struct uvc_xu_control_mapping32 __user *up)
+{
+ struct uvc_menu_info __user *umenus;
+ struct uvc_menu_info __user *kmenus = kp->menu_info;
+ compat_caddr_t p;
+
+ if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
+ __copy_to_user(up, kp, offsetof(typeof(*up), menu_info)) ||
+ __put_user(kp->menu_count, &up->menu_count))
+ return -EFAULT;
+
+ __clear_user(up->reserved, sizeof(up->reserved));
+
+ if (kp->menu_count == 0)
+ return 0;
+
+ if (get_user(p, &up->menu_info))
+ return -EFAULT;
+ umenus = compat_ptr(p);
+ if (!access_ok(VERIFY_WRITE, umenus, kp->menu_count * sizeof(*umenus)))
+ return -EFAULT;
+
+ if (copy_in_user(umenus, kmenus, kp->menu_count * sizeof(*umenus)))
+ return -EFAULT;
+
+ return 0;
+}
+
+struct uvc_xu_control_query32 {
+ __u8 unit;
+ __u8 selector;
+ __u8 query;
+ __u16 size;
+ compat_caddr_t data;
+};
+
+static int uvc_v4l2_get_xu_query(struct uvc_xu_control_query *kp,
+ const struct uvc_xu_control_query32 __user *up)
+{
+ u8 __user *udata;
+ u8 __user *kdata;
+ compat_caddr_t p;
+
+ if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
+ __copy_from_user(kp, up, offsetof(typeof(*up), data)))
+ return -EFAULT;
+
+ if (kp->size == 0) {
+ kp->data = NULL;
+ return 0;
+ }
+
+ if (__get_user(p, &up->data))
+ return -EFAULT;
+ udata = compat_ptr(p);
+ if (!access_ok(VERIFY_READ, udata, kp->size))
+ return -EFAULT;
+
+ kdata = compat_alloc_user_space(kp->size);
+ if (kdata == NULL)
+ return -EFAULT;
+ kp->data = kdata;
+
+ if (copy_in_user(kdata, udata, kp->size))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int uvc_v4l2_put_xu_query(const struct uvc_xu_control_query *kp,
+ struct uvc_xu_control_query32 __user *up)
+{
+ u8 __user *udata;
+ u8 __user *kdata = kp->data;
+ compat_caddr_t p;
+
+ if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
+ __copy_to_user(up, kp, offsetof(typeof(*up), data)))
+ return -EFAULT;
+
+ if (kp->size == 0)
+ return 0;
+
+ if (get_user(p, &up->data))
+ return -EFAULT;
+ udata = compat_ptr(p);
+ if (!access_ok(VERIFY_READ, udata, kp->size))
+ return -EFAULT;
+
+ if (copy_in_user(udata, kdata, kp->size))
+ return -EFAULT;
+
+ return 0;
+}
+
+#define UVCIOC_CTRL_MAP32 _IOWR('u', 0x20, struct uvc_xu_control_mapping32)
+#define UVCIOC_CTRL_QUERY32 _IOWR('u', 0x21, struct uvc_xu_control_query32)
+
+static long uvc_v4l2_compat_ioctl32(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ union {
+ struct uvc_xu_control_mapping xmap;
+ struct uvc_xu_control_query xqry;
+ } karg;
+ void __user *up = compat_ptr(arg);
+ mm_segment_t old_fs;
+ long ret;
+
+ switch (cmd) {
+ case UVCIOC_CTRL_MAP32:
+ cmd = UVCIOC_CTRL_MAP;
+ ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up);
+ break;
+
+ case UVCIOC_CTRL_QUERY32:
+ cmd = UVCIOC_CTRL_QUERY;
+ ret = uvc_v4l2_get_xu_query(&karg.xqry, up);
+ break;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ ret = uvc_v4l2_ioctl(file, cmd, (unsigned long)&karg);
+ set_fs(old_fs);
+
+ if (ret < 0)
+ return ret;
+
+ switch (cmd) {
+ case UVCIOC_CTRL_MAP:
+ ret = uvc_v4l2_put_xu_mapping(&karg.xmap, up);
+ break;
+
+ case UVCIOC_CTRL_QUERY:
+ ret = uvc_v4l2_put_xu_query(&karg.xqry, up);
+ break;
+ }
+
+ return ret;
+}
+#endif
+
+static ssize_t uvc_v4l2_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_read: not implemented.\n");
+ return -EINVAL;
+}
+
+static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct uvc_fh *handle = file->private_data;
+ struct uvc_streaming *stream = handle->stream;
+
+ uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_mmap\n");
+
+ return uvc_queue_mmap(&stream->queue, vma);
+}
+
+static unsigned int uvc_v4l2_poll(struct file *file, poll_table *wait)
+{
+ struct uvc_fh *handle = file->private_data;
+ struct uvc_streaming *stream = handle->stream;
+
+ uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n");
+
+ return uvc_queue_poll(&stream->queue, file, wait);
+}
+
+#ifndef CONFIG_MMU
+static unsigned long uvc_v4l2_get_unmapped_area(struct file *file,
+ unsigned long addr, unsigned long len, unsigned long pgoff,
+ unsigned long flags)
+{
+ struct uvc_fh *handle = file->private_data;
+ struct uvc_streaming *stream = handle->stream;
+
+ uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_get_unmapped_area\n");
+
+ return uvc_queue_get_unmapped_area(&stream->queue, pgoff);
+}
+#endif
+
+const struct v4l2_file_operations uvc_fops = {
+ .owner = THIS_MODULE,
+ .open = uvc_v4l2_open,
+ .release = uvc_v4l2_release,
+ .unlocked_ioctl = uvc_v4l2_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl32 = uvc_v4l2_compat_ioctl32,
+#endif
+ .read = uvc_v4l2_read,
+ .mmap = uvc_v4l2_mmap,
+ .poll = uvc_v4l2_poll,
+#ifndef CONFIG_MMU
+ .get_unmapped_area = uvc_v4l2_get_unmapped_area,
+#endif
+};
+
diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c
new file mode 100644
index 00000000..b76b0ac0
--- /dev/null
+++ b/drivers/media/video/uvc/uvc_video.c
@@ -0,0 +1,1857 @@
+/*
+ * uvc_video.c -- USB Video Class driver - Video handling
+ *
+ * Copyright (C) 2005-2010
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+#include <linux/atomic.h>
+#include <asm/unaligned.h>
+
+#include <media/v4l2-common.h>
+
+#include "uvcvideo.h"
+
+/* ------------------------------------------------------------------------
+ * UVC Controls
+ */
+
+static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
+ __u8 intfnum, __u8 cs, void *data, __u16 size,
+ int timeout)
+{
+ __u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+ unsigned int pipe;
+
+ pipe = (query & 0x80) ? usb_rcvctrlpipe(dev->udev, 0)
+ : usb_sndctrlpipe(dev->udev, 0);
+ type |= (query & 0x80) ? USB_DIR_IN : USB_DIR_OUT;
+
+ return usb_control_msg(dev->udev, pipe, query, type, cs << 8,
+ unit << 8 | intfnum, data, size, timeout);
+}
+
+static const char *uvc_query_name(__u8 query)
+{
+ switch (query) {
+ case UVC_SET_CUR:
+ return "SET_CUR";
+ case UVC_GET_CUR:
+ return "GET_CUR";
+ case UVC_GET_MIN:
+ return "GET_MIN";
+ case UVC_GET_MAX:
+ return "GET_MAX";
+ case UVC_GET_RES:
+ return "GET_RES";
+ case UVC_GET_LEN:
+ return "GET_LEN";
+ case UVC_GET_INFO:
+ return "GET_INFO";
+ case UVC_GET_DEF:
+ return "GET_DEF";
+ default:
+ return "<invalid>";
+ }
+}
+
+int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
+ __u8 intfnum, __u8 cs, void *data, __u16 size)
+{
+ int ret;
+
+ ret = __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size,
+ UVC_CTRL_CONTROL_TIMEOUT);
+ if (ret != size) {
+ uvc_printk(KERN_ERR, "Failed to query (%s) UVC control %u on "
+ "unit %u: %d (exp. %u).\n", uvc_query_name(query), cs,
+ unit, ret, size);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void uvc_fixup_video_ctrl(struct uvc_streaming *stream,
+ struct uvc_streaming_control *ctrl)
+{
+ struct uvc_format *format = NULL;
+ struct uvc_frame *frame = NULL;
+ unsigned int i;
+
+ for (i = 0; i < stream->nformats; ++i) {
+ if (stream->format[i].index == ctrl->bFormatIndex) {
+ format = &stream->format[i];
+ break;
+ }
+ }
+
+ if (format == NULL)
+ return;
+
+ for (i = 0; i < format->nframes; ++i) {
+ if (format->frame[i].bFrameIndex == ctrl->bFrameIndex) {
+ frame = &format->frame[i];
+ break;
+ }
+ }
+
+ if (frame == NULL)
+ return;
+
+ if (!(format->flags & UVC_FMT_FLAG_COMPRESSED) ||
+ (ctrl->dwMaxVideoFrameSize == 0 &&
+ stream->dev->uvc_version < 0x0110))
+ ctrl->dwMaxVideoFrameSize =
+ frame->dwMaxVideoFrameBufferSize;
+
+ if (!(format->flags & UVC_FMT_FLAG_COMPRESSED) &&
+ stream->dev->quirks & UVC_QUIRK_FIX_BANDWIDTH &&
+ stream->intf->num_altsetting > 1) {
+ u32 interval;
+ u32 bandwidth;
+
+ interval = (ctrl->dwFrameInterval > 100000)
+ ? ctrl->dwFrameInterval
+ : frame->dwFrameInterval[0];
+
+ /* Compute a bandwidth estimation by multiplying the frame
+ * size by the number of video frames per second, divide the
+ * result by the number of USB frames (or micro-frames for
+ * high-speed devices) per second and add the UVC header size
+ * (assumed to be 12 bytes long).
+ */
+ bandwidth = frame->wWidth * frame->wHeight / 8 * format->bpp;
+ bandwidth *= 10000000 / interval + 1;
+ bandwidth /= 1000;
+ if (stream->dev->udev->speed == USB_SPEED_HIGH)
+ bandwidth /= 8;
+ bandwidth += 12;
+
+ /* The bandwidth estimate is too low for many cameras. Don't use
+ * maximum packet sizes lower than 1024 bytes to try and work
+ * around the problem. According to measurements done on two
+ * different camera models, the value is high enough to get most
+ * resolutions working while not preventing two simultaneous
+ * VGA streams at 15 fps.
+ */
+ bandwidth = max_t(u32, bandwidth, 1024);
+
+ ctrl->dwMaxPayloadTransferSize = bandwidth;
+ }
+}
+
+static int uvc_get_video_ctrl(struct uvc_streaming *stream,
+ struct uvc_streaming_control *ctrl, int probe, __u8 query)
+{
+ __u8 *data;
+ __u16 size;
+ int ret;
+
+ size = stream->dev->uvc_version >= 0x0110 ? 34 : 26;
+ if ((stream->dev->quirks & UVC_QUIRK_PROBE_DEF) &&
+ query == UVC_GET_DEF)
+ return -EIO;
+
+ data = kmalloc(size, GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ ret = __uvc_query_ctrl(stream->dev, query, 0, stream->intfnum,
+ probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data,
+ size, uvc_timeout_param);
+
+ if ((query == UVC_GET_MIN || query == UVC_GET_MAX) && ret == 2) {
+ /* Some cameras, mostly based on Bison Electronics chipsets,
+ * answer a GET_MIN or GET_MAX request with the wCompQuality
+ * field only.
+ */
+ uvc_warn_once(stream->dev, UVC_WARN_MINMAX, "UVC non "
+ "compliance - GET_MIN/MAX(PROBE) incorrectly "
+ "supported. Enabling workaround.\n");
+ memset(ctrl, 0, sizeof *ctrl);
+ ctrl->wCompQuality = le16_to_cpup((__le16 *)data);
+ ret = 0;
+ goto out;
+ } else if (query == UVC_GET_DEF && probe == 1 && ret != size) {
+ /* Many cameras don't support the GET_DEF request on their
+ * video probe control. Warn once and return, the caller will
+ * fall back to GET_CUR.
+ */
+ uvc_warn_once(stream->dev, UVC_WARN_PROBE_DEF, "UVC non "
+ "compliance - GET_DEF(PROBE) not supported. "
+ "Enabling workaround.\n");
+ ret = -EIO;
+ goto out;
+ } else if (ret != size) {
+ uvc_printk(KERN_ERR, "Failed to query (%u) UVC %s control : "
+ "%d (exp. %u).\n", query, probe ? "probe" : "commit",
+ ret, size);
+ ret = -EIO;
+ goto out;
+ }
+
+ ctrl->bmHint = le16_to_cpup((__le16 *)&data[0]);
+ ctrl->bFormatIndex = data[2];
+ ctrl->bFrameIndex = data[3];
+ ctrl->dwFrameInterval = le32_to_cpup((__le32 *)&data[4]);
+ ctrl->wKeyFrameRate = le16_to_cpup((__le16 *)&data[8]);
+ ctrl->wPFrameRate = le16_to_cpup((__le16 *)&data[10]);
+ ctrl->wCompQuality = le16_to_cpup((__le16 *)&data[12]);
+ ctrl->wCompWindowSize = le16_to_cpup((__le16 *)&data[14]);
+ ctrl->wDelay = le16_to_cpup((__le16 *)&data[16]);
+ ctrl->dwMaxVideoFrameSize = get_unaligned_le32(&data[18]);
+ ctrl->dwMaxPayloadTransferSize = get_unaligned_le32(&data[22]);
+
+ if (size == 34) {
+ ctrl->dwClockFrequency = get_unaligned_le32(&data[26]);
+ ctrl->bmFramingInfo = data[30];
+ ctrl->bPreferedVersion = data[31];
+ ctrl->bMinVersion = data[32];
+ ctrl->bMaxVersion = data[33];
+ } else {
+ ctrl->dwClockFrequency = stream->dev->clock_frequency;
+ ctrl->bmFramingInfo = 0;
+ ctrl->bPreferedVersion = 0;
+ ctrl->bMinVersion = 0;
+ ctrl->bMaxVersion = 0;
+ }
+
+ /* Some broken devices return null or wrong dwMaxVideoFrameSize and
+ * dwMaxPayloadTransferSize fields. Try to get the value from the
+ * format and frame descriptors.
+ */
+ uvc_fixup_video_ctrl(stream, ctrl);
+ ret = 0;
+
+out:
+ kfree(data);
+ return ret;
+}
+
+static int uvc_set_video_ctrl(struct uvc_streaming *stream,
+ struct uvc_streaming_control *ctrl, int probe)
+{
+ __u8 *data;
+ __u16 size;
+ int ret;
+
+ size = stream->dev->uvc_version >= 0x0110 ? 34 : 26;
+ data = kzalloc(size, GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ *(__le16 *)&data[0] = cpu_to_le16(ctrl->bmHint);
+ data[2] = ctrl->bFormatIndex;
+ data[3] = ctrl->bFrameIndex;
+ *(__le32 *)&data[4] = cpu_to_le32(ctrl->dwFrameInterval);
+ *(__le16 *)&data[8] = cpu_to_le16(ctrl->wKeyFrameRate);
+ *(__le16 *)&data[10] = cpu_to_le16(ctrl->wPFrameRate);
+ *(__le16 *)&data[12] = cpu_to_le16(ctrl->wCompQuality);
+ *(__le16 *)&data[14] = cpu_to_le16(ctrl->wCompWindowSize);
+ *(__le16 *)&data[16] = cpu_to_le16(ctrl->wDelay);
+ put_unaligned_le32(ctrl->dwMaxVideoFrameSize, &data[18]);
+ put_unaligned_le32(ctrl->dwMaxPayloadTransferSize, &data[22]);
+
+ if (size == 34) {
+ put_unaligned_le32(ctrl->dwClockFrequency, &data[26]);
+ data[30] = ctrl->bmFramingInfo;
+ data[31] = ctrl->bPreferedVersion;
+ data[32] = ctrl->bMinVersion;
+ data[33] = ctrl->bMaxVersion;
+ }
+
+ ret = __uvc_query_ctrl(stream->dev, UVC_SET_CUR, 0, stream->intfnum,
+ probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data,
+ size, uvc_timeout_param);
+ if (ret != size) {
+ uvc_printk(KERN_ERR, "Failed to set UVC %s control : "
+ "%d (exp. %u).\n", probe ? "probe" : "commit",
+ ret, size);
+ ret = -EIO;
+ }
+
+ kfree(data);
+ return ret;
+}
+
+int uvc_probe_video(struct uvc_streaming *stream,
+ struct uvc_streaming_control *probe)
+{
+ struct uvc_streaming_control probe_min, probe_max;
+ __u16 bandwidth;
+ unsigned int i;
+ int ret;
+
+ /* Perform probing. The device should adjust the requested values
+ * according to its capabilities. However, some devices, namely the
+ * first generation UVC Logitech webcams, don't implement the Video
+ * Probe control properly, and just return the needed bandwidth. For
+ * that reason, if the needed bandwidth exceeds the maximum available
+ * bandwidth, try to lower the quality.
+ */
+ ret = uvc_set_video_ctrl(stream, probe, 1);
+ if (ret < 0)
+ goto done;
+
+ /* Get the minimum and maximum values for compression settings. */
+ if (!(stream->dev->quirks & UVC_QUIRK_PROBE_MINMAX)) {
+ ret = uvc_get_video_ctrl(stream, &probe_min, 1, UVC_GET_MIN);
+ if (ret < 0)
+ goto done;
+ ret = uvc_get_video_ctrl(stream, &probe_max, 1, UVC_GET_MAX);
+ if (ret < 0)
+ goto done;
+
+ probe->wCompQuality = probe_max.wCompQuality;
+ }
+
+ for (i = 0; i < 2; ++i) {
+ ret = uvc_set_video_ctrl(stream, probe, 1);
+ if (ret < 0)
+ goto done;
+ ret = uvc_get_video_ctrl(stream, probe, 1, UVC_GET_CUR);
+ if (ret < 0)
+ goto done;
+
+ if (stream->intf->num_altsetting == 1)
+ break;
+
+ bandwidth = probe->dwMaxPayloadTransferSize;
+ if (bandwidth <= stream->maxpsize)
+ break;
+
+ if (stream->dev->quirks & UVC_QUIRK_PROBE_MINMAX) {
+ ret = -ENOSPC;
+ goto done;
+ }
+
+ /* TODO: negotiate compression parameters */
+ probe->wKeyFrameRate = probe_min.wKeyFrameRate;
+ probe->wPFrameRate = probe_min.wPFrameRate;
+ probe->wCompQuality = probe_max.wCompQuality;
+ probe->wCompWindowSize = probe_min.wCompWindowSize;
+ }
+
+done:
+ return ret;
+}
+
+static int uvc_commit_video(struct uvc_streaming *stream,
+ struct uvc_streaming_control *probe)
+{
+ return uvc_set_video_ctrl(stream, probe, 0);
+}
+
+/* -----------------------------------------------------------------------------
+ * Clocks and timestamps
+ */
+
+static void
+uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf,
+ const __u8 *data, int len)
+{
+ struct uvc_clock_sample *sample;
+ unsigned int header_size;
+ bool has_pts = false;
+ bool has_scr = false;
+ unsigned long flags;
+ struct timespec ts;
+ u16 host_sof;
+ u16 dev_sof;
+
+ switch (data[1] & (UVC_STREAM_PTS | UVC_STREAM_SCR)) {
+ case UVC_STREAM_PTS | UVC_STREAM_SCR:
+ header_size = 12;
+ has_pts = true;
+ has_scr = true;
+ break;
+ case UVC_STREAM_PTS:
+ header_size = 6;
+ has_pts = true;
+ break;
+ case UVC_STREAM_SCR:
+ header_size = 8;
+ has_scr = true;
+ break;
+ default:
+ header_size = 2;
+ break;
+ }
+
+ /* Check for invalid headers. */
+ if (len < header_size)
+ return;
+
+ /* Extract the timestamps:
+ *
+ * - store the frame PTS in the buffer structure
+ * - if the SCR field is present, retrieve the host SOF counter and
+ * kernel timestamps and store them with the SCR STC and SOF fields
+ * in the ring buffer
+ */
+ if (has_pts && buf != NULL)
+ buf->pts = get_unaligned_le32(&data[2]);
+
+ if (!has_scr)
+ return;
+
+ /* To limit the amount of data, drop SCRs with an SOF identical to the
+ * previous one.
+ */
+ dev_sof = get_unaligned_le16(&data[header_size - 2]);
+ if (dev_sof == stream->clock.last_sof)
+ return;
+
+ stream->clock.last_sof = dev_sof;
+
+ host_sof = usb_get_current_frame_number(stream->dev->udev);
+ ktime_get_ts(&ts);
+
+ /* The UVC specification allows device implementations that can't obtain
+ * the USB frame number to keep their own frame counters as long as they
+ * match the size and frequency of the frame number associated with USB
+ * SOF tokens. The SOF values sent by such devices differ from the USB
+ * SOF tokens by a fixed offset that needs to be estimated and accounted
+ * for to make timestamp recovery as accurate as possible.
+ *
+ * The offset is estimated the first time a device SOF value is received
+ * as the difference between the host and device SOF values. As the two
+ * SOF values can differ slightly due to transmission delays, consider
+ * that the offset is null if the difference is not higher than 10 ms
+ * (negative differences can not happen and are thus considered as an
+ * offset). The video commit control wDelay field should be used to
+ * compute a dynamic threshold instead of using a fixed 10 ms value, but
+ * devices don't report reliable wDelay values.
+ *
+ * See uvc_video_clock_host_sof() for an explanation regarding why only
+ * the 8 LSBs of the delta are kept.
+ */
+ if (stream->clock.sof_offset == (u16)-1) {
+ u16 delta_sof = (host_sof - dev_sof) & 255;
+ if (delta_sof >= 10)
+ stream->clock.sof_offset = delta_sof;
+ else
+ stream->clock.sof_offset = 0;
+ }
+
+ dev_sof = (dev_sof + stream->clock.sof_offset) & 2047;
+
+ spin_lock_irqsave(&stream->clock.lock, flags);
+
+ sample = &stream->clock.samples[stream->clock.head];
+ sample->dev_stc = get_unaligned_le32(&data[header_size - 6]);
+ sample->dev_sof = dev_sof;
+ sample->host_sof = host_sof;
+ sample->host_ts = ts;
+
+ /* Update the sliding window head and count. */
+ stream->clock.head = (stream->clock.head + 1) % stream->clock.size;
+
+ if (stream->clock.count < stream->clock.size)
+ stream->clock.count++;
+
+ spin_unlock_irqrestore(&stream->clock.lock, flags);
+}
+
+static void uvc_video_clock_reset(struct uvc_streaming *stream)
+{
+ struct uvc_clock *clock = &stream->clock;
+
+ clock->head = 0;
+ clock->count = 0;
+ clock->last_sof = -1;
+ clock->sof_offset = -1;
+}
+
+static int uvc_video_clock_init(struct uvc_streaming *stream)
+{
+ struct uvc_clock *clock = &stream->clock;
+
+ spin_lock_init(&clock->lock);
+ clock->size = 32;
+
+ clock->samples = kmalloc(clock->size * sizeof(*clock->samples),
+ GFP_KERNEL);
+ if (clock->samples == NULL)
+ return -ENOMEM;
+
+ uvc_video_clock_reset(stream);
+
+ return 0;
+}
+
+static void uvc_video_clock_cleanup(struct uvc_streaming *stream)
+{
+ kfree(stream->clock.samples);
+ stream->clock.samples = NULL;
+}
+
+/*
+ * uvc_video_clock_host_sof - Return the host SOF value for a clock sample
+ *
+ * Host SOF counters reported by usb_get_current_frame_number() usually don't
+ * cover the whole 11-bits SOF range (0-2047) but are limited to the HCI frame
+ * schedule window. They can be limited to 8, 9 or 10 bits depending on the host
+ * controller and its configuration.
+ *
+ * We thus need to recover the SOF value corresponding to the host frame number.
+ * As the device and host frame numbers are sampled in a short interval, the
+ * difference between their values should be equal to a small delta plus an
+ * integer multiple of 256 caused by the host frame number limited precision.
+ *
+ * To obtain the recovered host SOF value, compute the small delta by masking
+ * the high bits of the host frame counter and device SOF difference and add it
+ * to the device SOF value.
+ */
+static u16 uvc_video_clock_host_sof(const struct uvc_clock_sample *sample)
+{
+ /* The delta value can be negative. */
+ s8 delta_sof;
+
+ delta_sof = (sample->host_sof - sample->dev_sof) & 255;
+
+ return (sample->dev_sof + delta_sof) & 2047;
+}
+
+/*
+ * uvc_video_clock_update - Update the buffer timestamp
+ *
+ * This function converts the buffer PTS timestamp to the host clock domain by
+ * going through the USB SOF clock domain and stores the result in the V4L2
+ * buffer timestamp field.
+ *
+ * The relationship between the device clock and the host clock isn't known.
+ * However, the device and the host share the common USB SOF clock which can be
+ * used to recover that relationship.
+ *
+ * The relationship between the device clock and the USB SOF clock is considered
+ * to be linear over the clock samples sliding window and is given by
+ *
+ * SOF = m * PTS + p
+ *
+ * Several methods to compute the slope (m) and intercept (p) can be used. As
+ * the clock drift should be small compared to the sliding window size, we
+ * assume that the line that goes through the points at both ends of the window
+ * is a good approximation. Naming those points P1 and P2, we get
+ *
+ * SOF = (SOF2 - SOF1) / (STC2 - STC1) * PTS
+ * + (SOF1 * STC2 - SOF2 * STC1) / (STC2 - STC1)
+ *
+ * or
+ *
+ * SOF = ((SOF2 - SOF1) * PTS + SOF1 * STC2 - SOF2 * STC1) / (STC2 - STC1) (1)
+ *
+ * to avoid loosing precision in the division. Similarly, the host timestamp is
+ * computed with
+ *
+ * TS = ((TS2 - TS1) * PTS + TS1 * SOF2 - TS2 * SOF1) / (SOF2 - SOF1) (2)
+ *
+ * SOF values are coded on 11 bits by USB. We extend their precision with 16
+ * decimal bits, leading to a 11.16 coding.
+ *
+ * TODO: To avoid surprises with device clock values, PTS/STC timestamps should
+ * be normalized using the nominal device clock frequency reported through the
+ * UVC descriptors.
+ *
+ * Both the PTS/STC and SOF counters roll over, after a fixed but device
+ * specific amount of time for PTS/STC and after 2048ms for SOF. As long as the
+ * sliding window size is smaller than the rollover period, differences computed
+ * on unsigned integers will produce the correct result. However, the p term in
+ * the linear relations will be miscomputed.
+ *
+ * To fix the issue, we subtract a constant from the PTS and STC values to bring
+ * PTS to half the 32 bit STC range. The sliding window STC values then fit into
+ * the 32 bit range without any rollover.
+ *
+ * Similarly, we add 2048 to the device SOF values to make sure that the SOF
+ * computed by (1) will never be smaller than 0. This offset is then compensated
+ * by adding 2048 to the SOF values used in (2). However, this doesn't prevent
+ * rollovers between (1) and (2): the SOF value computed by (1) can be slightly
+ * lower than 4096, and the host SOF counters can have rolled over to 2048. This
+ * case is handled by subtracting 2048 from the SOF value if it exceeds the host
+ * SOF value at the end of the sliding window.
+ *
+ * Finally we subtract a constant from the host timestamps to bring the first
+ * timestamp of the sliding window to 1s.
+ */
+void uvc_video_clock_update(struct uvc_streaming *stream,
+ struct v4l2_buffer *v4l2_buf,
+ struct uvc_buffer *buf)
+{
+ struct uvc_clock *clock = &stream->clock;
+ struct uvc_clock_sample *first;
+ struct uvc_clock_sample *last;
+ unsigned long flags;
+ struct timespec ts;
+ u32 delta_stc;
+ u32 y1, y2;
+ u32 x1, x2;
+ u32 mean;
+ u32 sof;
+ u32 div;
+ u32 rem;
+ u64 y;
+
+ spin_lock_irqsave(&clock->lock, flags);
+
+ if (clock->count < clock->size)
+ goto done;
+
+ first = &clock->samples[clock->head];
+ last = &clock->samples[(clock->head - 1) % clock->size];
+
+ /* First step, PTS to SOF conversion. */
+ delta_stc = buf->pts - (1UL << 31);
+ x1 = first->dev_stc - delta_stc;
+ x2 = last->dev_stc - delta_stc;
+ if (x1 == x2)
+ goto done;
+
+ y1 = (first->dev_sof + 2048) << 16;
+ y2 = (last->dev_sof + 2048) << 16;
+ if (y2 < y1)
+ y2 += 2048 << 16;
+
+ y = (u64)(y2 - y1) * (1ULL << 31) + (u64)y1 * (u64)x2
+ - (u64)y2 * (u64)x1;
+ y = div_u64(y, x2 - x1);
+
+ sof = y;
+
+ uvc_trace(UVC_TRACE_CLOCK, "%s: PTS %u y %llu.%06llu SOF %u.%06llu "
+ "(x1 %u x2 %u y1 %u y2 %u SOF offset %u)\n",
+ stream->dev->name, buf->pts,
+ y >> 16, div_u64((y & 0xffff) * 1000000, 65536),
+ sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536),
+ x1, x2, y1, y2, clock->sof_offset);
+
+ /* Second step, SOF to host clock conversion. */
+ x1 = (uvc_video_clock_host_sof(first) + 2048) << 16;
+ x2 = (uvc_video_clock_host_sof(last) + 2048) << 16;
+ if (x2 < x1)
+ x2 += 2048 << 16;
+ if (x1 == x2)
+ goto done;
+
+ ts = timespec_sub(last->host_ts, first->host_ts);
+ y1 = NSEC_PER_SEC;
+ y2 = (ts.tv_sec + 1) * NSEC_PER_SEC + ts.tv_nsec;
+
+ /* Interpolated and host SOF timestamps can wrap around at slightly
+ * different times. Handle this by adding or removing 2048 to or from
+ * the computed SOF value to keep it close to the SOF samples mean
+ * value.
+ */
+ mean = (x1 + x2) / 2;
+ if (mean - (1024 << 16) > sof)
+ sof += 2048 << 16;
+ else if (sof > mean + (1024 << 16))
+ sof -= 2048 << 16;
+
+ y = (u64)(y2 - y1) * (u64)sof + (u64)y1 * (u64)x2
+ - (u64)y2 * (u64)x1;
+ y = div_u64(y, x2 - x1);
+
+ div = div_u64_rem(y, NSEC_PER_SEC, &rem);
+ ts.tv_sec = first->host_ts.tv_sec - 1 + div;
+ ts.tv_nsec = first->host_ts.tv_nsec + rem;
+ if (ts.tv_nsec >= NSEC_PER_SEC) {
+ ts.tv_sec++;
+ ts.tv_nsec -= NSEC_PER_SEC;
+ }
+
+ uvc_trace(UVC_TRACE_CLOCK, "%s: SOF %u.%06llu y %llu ts %lu.%06lu "
+ "buf ts %lu.%06lu (x1 %u/%u/%u x2 %u/%u/%u y1 %u y2 %u)\n",
+ stream->dev->name,
+ sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536),
+ y, ts.tv_sec, ts.tv_nsec / NSEC_PER_USEC,
+ v4l2_buf->timestamp.tv_sec, v4l2_buf->timestamp.tv_usec,
+ x1, first->host_sof, first->dev_sof,
+ x2, last->host_sof, last->dev_sof, y1, y2);
+
+ /* Update the V4L2 buffer. */
+ v4l2_buf->timestamp.tv_sec = ts.tv_sec;
+ v4l2_buf->timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
+
+done:
+ spin_unlock_irqrestore(&stream->clock.lock, flags);
+}
+
+/* ------------------------------------------------------------------------
+ * Stream statistics
+ */
+
+static void uvc_video_stats_decode(struct uvc_streaming *stream,
+ const __u8 *data, int len)
+{
+ unsigned int header_size;
+ bool has_pts = false;
+ bool has_scr = false;
+ u16 uninitialized_var(scr_sof);
+ u32 uninitialized_var(scr_stc);
+ u32 uninitialized_var(pts);
+
+ if (stream->stats.stream.nb_frames == 0 &&
+ stream->stats.frame.nb_packets == 0)
+ ktime_get_ts(&stream->stats.stream.start_ts);
+
+ switch (data[1] & (UVC_STREAM_PTS | UVC_STREAM_SCR)) {
+ case UVC_STREAM_PTS | UVC_STREAM_SCR:
+ header_size = 12;
+ has_pts = true;
+ has_scr = true;
+ break;
+ case UVC_STREAM_PTS:
+ header_size = 6;
+ has_pts = true;
+ break;
+ case UVC_STREAM_SCR:
+ header_size = 8;
+ has_scr = true;
+ break;
+ default:
+ header_size = 2;
+ break;
+ }
+
+ /* Check for invalid headers. */
+ if (len < header_size || data[0] < header_size) {
+ stream->stats.frame.nb_invalid++;
+ return;
+ }
+
+ /* Extract the timestamps. */
+ if (has_pts)
+ pts = get_unaligned_le32(&data[2]);
+
+ if (has_scr) {
+ scr_stc = get_unaligned_le32(&data[header_size - 6]);
+ scr_sof = get_unaligned_le16(&data[header_size - 2]);
+ }
+
+ /* Is PTS constant through the whole frame ? */
+ if (has_pts && stream->stats.frame.nb_pts) {
+ if (stream->stats.frame.pts != pts) {
+ stream->stats.frame.nb_pts_diffs++;
+ stream->stats.frame.last_pts_diff =
+ stream->stats.frame.nb_packets;
+ }
+ }
+
+ if (has_pts) {
+ stream->stats.frame.nb_pts++;
+ stream->stats.frame.pts = pts;
+ }
+
+ /* Do all frames have a PTS in their first non-empty packet, or before
+ * their first empty packet ?
+ */
+ if (stream->stats.frame.size == 0) {
+ if (len > header_size)
+ stream->stats.frame.has_initial_pts = has_pts;
+ if (len == header_size && has_pts)
+ stream->stats.frame.has_early_pts = true;
+ }
+
+ /* Do the SCR.STC and SCR.SOF fields vary through the frame ? */
+ if (has_scr && stream->stats.frame.nb_scr) {
+ if (stream->stats.frame.scr_stc != scr_stc)
+ stream->stats.frame.nb_scr_diffs++;
+ }
+
+ if (has_scr) {
+ /* Expand the SOF counter to 32 bits and store its value. */
+ if (stream->stats.stream.nb_frames > 0 ||
+ stream->stats.frame.nb_scr > 0)
+ stream->stats.stream.scr_sof_count +=
+ (scr_sof - stream->stats.stream.scr_sof) % 2048;
+ stream->stats.stream.scr_sof = scr_sof;
+
+ stream->stats.frame.nb_scr++;
+ stream->stats.frame.scr_stc = scr_stc;
+ stream->stats.frame.scr_sof = scr_sof;
+
+ if (scr_sof < stream->stats.stream.min_sof)
+ stream->stats.stream.min_sof = scr_sof;
+ if (scr_sof > stream->stats.stream.max_sof)
+ stream->stats.stream.max_sof = scr_sof;
+ }
+
+ /* Record the first non-empty packet number. */
+ if (stream->stats.frame.size == 0 && len > header_size)
+ stream->stats.frame.first_data = stream->stats.frame.nb_packets;
+
+ /* Update the frame size. */
+ stream->stats.frame.size += len - header_size;
+
+ /* Update the packets counters. */
+ stream->stats.frame.nb_packets++;
+ if (len > header_size)
+ stream->stats.frame.nb_empty++;
+
+ if (data[1] & UVC_STREAM_ERR)
+ stream->stats.frame.nb_errors++;
+}
+
+static void uvc_video_stats_update(struct uvc_streaming *stream)
+{
+ struct uvc_stats_frame *frame = &stream->stats.frame;
+
+ uvc_trace(UVC_TRACE_STATS, "frame %u stats: %u/%u/%u packets, "
+ "%u/%u/%u pts (%searly %sinitial), %u/%u scr, "
+ "last pts/stc/sof %u/%u/%u\n",
+ stream->sequence, frame->first_data,
+ frame->nb_packets - frame->nb_empty, frame->nb_packets,
+ frame->nb_pts_diffs, frame->last_pts_diff, frame->nb_pts,
+ frame->has_early_pts ? "" : "!",
+ frame->has_initial_pts ? "" : "!",
+ frame->nb_scr_diffs, frame->nb_scr,
+ frame->pts, frame->scr_stc, frame->scr_sof);
+
+ stream->stats.stream.nb_frames++;
+ stream->stats.stream.nb_packets += stream->stats.frame.nb_packets;
+ stream->stats.stream.nb_empty += stream->stats.frame.nb_empty;
+ stream->stats.stream.nb_errors += stream->stats.frame.nb_errors;
+ stream->stats.stream.nb_invalid += stream->stats.frame.nb_invalid;
+
+ if (frame->has_early_pts)
+ stream->stats.stream.nb_pts_early++;
+ if (frame->has_initial_pts)
+ stream->stats.stream.nb_pts_initial++;
+ if (frame->last_pts_diff <= frame->first_data)
+ stream->stats.stream.nb_pts_constant++;
+ if (frame->nb_scr >= frame->nb_packets - frame->nb_empty)
+ stream->stats.stream.nb_scr_count_ok++;
+ if (frame->nb_scr_diffs + 1 == frame->nb_scr)
+ stream->stats.stream.nb_scr_diffs_ok++;
+
+ memset(&stream->stats.frame, 0, sizeof(stream->stats.frame));
+}
+
+size_t uvc_video_stats_dump(struct uvc_streaming *stream, char *buf,
+ size_t size)
+{
+ unsigned int scr_sof_freq;
+ unsigned int duration;
+ struct timespec ts;
+ size_t count = 0;
+
+ ts.tv_sec = stream->stats.stream.stop_ts.tv_sec
+ - stream->stats.stream.start_ts.tv_sec;
+ ts.tv_nsec = stream->stats.stream.stop_ts.tv_nsec
+ - stream->stats.stream.start_ts.tv_nsec;
+ if (ts.tv_nsec < 0) {
+ ts.tv_sec--;
+ ts.tv_nsec += 1000000000;
+ }
+
+ /* Compute the SCR.SOF frequency estimate. At the nominal 1kHz SOF
+ * frequency this will not overflow before more than 1h.
+ */
+ duration = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
+ if (duration != 0)
+ scr_sof_freq = stream->stats.stream.scr_sof_count * 1000
+ / duration;
+ else
+ scr_sof_freq = 0;
+
+ count += scnprintf(buf + count, size - count,
+ "frames: %u\npackets: %u\nempty: %u\n"
+ "errors: %u\ninvalid: %u\n",
+ stream->stats.stream.nb_frames,
+ stream->stats.stream.nb_packets,
+ stream->stats.stream.nb_empty,
+ stream->stats.stream.nb_errors,
+ stream->stats.stream.nb_invalid);
+ count += scnprintf(buf + count, size - count,
+ "pts: %u early, %u initial, %u ok\n",
+ stream->stats.stream.nb_pts_early,
+ stream->stats.stream.nb_pts_initial,
+ stream->stats.stream.nb_pts_constant);
+ count += scnprintf(buf + count, size - count,
+ "scr: %u count ok, %u diff ok\n",
+ stream->stats.stream.nb_scr_count_ok,
+ stream->stats.stream.nb_scr_diffs_ok);
+ count += scnprintf(buf + count, size - count,
+ "sof: %u <= sof <= %u, freq %u.%03u kHz\n",
+ stream->stats.stream.min_sof,
+ stream->stats.stream.max_sof,
+ scr_sof_freq / 1000, scr_sof_freq % 1000);
+
+ return count;
+}
+
+static void uvc_video_stats_start(struct uvc_streaming *stream)
+{
+ memset(&stream->stats, 0, sizeof(stream->stats));
+ stream->stats.stream.min_sof = 2048;
+}
+
+static void uvc_video_stats_stop(struct uvc_streaming *stream)
+{
+ ktime_get_ts(&stream->stats.stream.stop_ts);
+}
+
+/* ------------------------------------------------------------------------
+ * Video codecs
+ */
+
+/* Video payload decoding is handled by uvc_video_decode_start(),
+ * uvc_video_decode_data() and uvc_video_decode_end().
+ *
+ * uvc_video_decode_start is called with URB data at the start of a bulk or
+ * isochronous payload. It processes header data and returns the header size
+ * in bytes if successful. If an error occurs, it returns a negative error
+ * code. The following error codes have special meanings.
+ *
+ * - EAGAIN informs the caller that the current video buffer should be marked
+ * as done, and that the function should be called again with the same data
+ * and a new video buffer. This is used when end of frame conditions can be
+ * reliably detected at the beginning of the next frame only.
+ *
+ * If an error other than -EAGAIN is returned, the caller will drop the current
+ * payload. No call to uvc_video_decode_data and uvc_video_decode_end will be
+ * made until the next payload. -ENODATA can be used to drop the current
+ * payload if no other error code is appropriate.
+ *
+ * uvc_video_decode_data is called for every URB with URB data. It copies the
+ * data to the video buffer.
+ *
+ * uvc_video_decode_end is called with header data at the end of a bulk or
+ * isochronous payload. It performs any additional header data processing and
+ * returns 0 or a negative error code if an error occurred. As header data have
+ * already been processed by uvc_video_decode_start, this functions isn't
+ * required to perform sanity checks a second time.
+ *
+ * For isochronous transfers where a payload is always transferred in a single
+ * URB, the three functions will be called in a row.
+ *
+ * To let the decoder process header data and update its internal state even
+ * when no video buffer is available, uvc_video_decode_start must be prepared
+ * to be called with a NULL buf parameter. uvc_video_decode_data and
+ * uvc_video_decode_end will never be called with a NULL buffer.
+ */
+static int uvc_video_decode_start(struct uvc_streaming *stream,
+ struct uvc_buffer *buf, const __u8 *data, int len)
+{
+ __u8 fid;
+
+ /* Sanity checks:
+ * - packet must be at least 2 bytes long
+ * - bHeaderLength value must be at least 2 bytes (see above)
+ * - bHeaderLength value can't be larger than the packet size.
+ */
+ if (len < 2 || data[0] < 2 || data[0] > len) {
+ stream->stats.frame.nb_invalid++;
+ return -EINVAL;
+ }
+
+ fid = data[1] & UVC_STREAM_FID;
+
+ /* Increase the sequence number regardless of any buffer states, so
+ * that discontinuous sequence numbers always indicate lost frames.
+ */
+ if (stream->last_fid != fid) {
+ stream->sequence++;
+ if (stream->sequence)
+ uvc_video_stats_update(stream);
+ }
+
+ uvc_video_clock_decode(stream, buf, data, len);
+ uvc_video_stats_decode(stream, data, len);
+
+ /* Store the payload FID bit and return immediately when the buffer is
+ * NULL.
+ */
+ if (buf == NULL) {
+ stream->last_fid = fid;
+ return -ENODATA;
+ }
+
+ /* Mark the buffer as bad if the error bit is set. */
+ if (data[1] & UVC_STREAM_ERR) {
+ uvc_trace(UVC_TRACE_FRAME, "Marking buffer as bad (error bit "
+ "set).\n");
+ buf->error = 1;
+ }
+
+ /* Synchronize to the input stream by waiting for the FID bit to be
+ * toggled when the the buffer state is not UVC_BUF_STATE_ACTIVE.
+ * stream->last_fid is initialized to -1, so the first isochronous
+ * frame will always be in sync.
+ *
+ * If the device doesn't toggle the FID bit, invert stream->last_fid
+ * when the EOF bit is set to force synchronisation on the next packet.
+ */
+ if (buf->state != UVC_BUF_STATE_ACTIVE) {
+ struct timespec ts;
+
+ if (fid == stream->last_fid) {
+ uvc_trace(UVC_TRACE_FRAME, "Dropping payload (out of "
+ "sync).\n");
+ if ((stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID) &&
+ (data[1] & UVC_STREAM_EOF))
+ stream->last_fid ^= UVC_STREAM_FID;
+ return -ENODATA;
+ }
+
+ if (uvc_clock_param == CLOCK_MONOTONIC)
+ ktime_get_ts(&ts);
+ else
+ ktime_get_real_ts(&ts);
+
+ buf->buf.v4l2_buf.sequence = stream->sequence;
+ buf->buf.v4l2_buf.timestamp.tv_sec = ts.tv_sec;
+ buf->buf.v4l2_buf.timestamp.tv_usec =
+ ts.tv_nsec / NSEC_PER_USEC;
+
+ /* TODO: Handle PTS and SCR. */
+ buf->state = UVC_BUF_STATE_ACTIVE;
+ }
+
+ /* Mark the buffer as done if we're at the beginning of a new frame.
+ * End of frame detection is better implemented by checking the EOF
+ * bit (FID bit toggling is delayed by one frame compared to the EOF
+ * bit), but some devices don't set the bit at end of frame (and the
+ * last payload can be lost anyway). We thus must check if the FID has
+ * been toggled.
+ *
+ * stream->last_fid is initialized to -1, so the first isochronous
+ * frame will never trigger an end of frame detection.
+ *
+ * Empty buffers (bytesused == 0) don't trigger end of frame detection
+ * as it doesn't make sense to return an empty buffer. This also
+ * avoids detecting end of frame conditions at FID toggling if the
+ * previous payload had the EOF bit set.
+ */
+ if (fid != stream->last_fid && buf->bytesused != 0) {
+ uvc_trace(UVC_TRACE_FRAME, "Frame complete (FID bit "
+ "toggled).\n");
+ buf->state = UVC_BUF_STATE_READY;
+ return -EAGAIN;
+ }
+
+ stream->last_fid = fid;
+
+ return data[0];
+}
+
+static void uvc_video_decode_data(struct uvc_streaming *stream,
+ struct uvc_buffer *buf, const __u8 *data, int len)
+{
+ unsigned int maxlen, nbytes;
+ void *mem;
+
+ if (len <= 0)
+ return;
+
+ /* Copy the video data to the buffer. */
+ maxlen = buf->length - buf->bytesused;
+ mem = buf->mem + buf->bytesused;
+ nbytes = min((unsigned int)len, maxlen);
+ memcpy(mem, data, nbytes);
+ buf->bytesused += nbytes;
+
+ /* Complete the current frame if the buffer size was exceeded. */
+ if (len > maxlen) {
+ uvc_trace(UVC_TRACE_FRAME, "Frame complete (overflow).\n");
+ buf->state = UVC_BUF_STATE_READY;
+ }
+}
+
+static void uvc_video_decode_end(struct uvc_streaming *stream,
+ struct uvc_buffer *buf, const __u8 *data, int len)
+{
+ /* Mark the buffer as done if the EOF marker is set. */
+ if (data[1] & UVC_STREAM_EOF && buf->bytesused != 0) {
+ uvc_trace(UVC_TRACE_FRAME, "Frame complete (EOF found).\n");
+ if (data[0] == len)
+ uvc_trace(UVC_TRACE_FRAME, "EOF in empty payload.\n");
+ buf->state = UVC_BUF_STATE_READY;
+ if (stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID)
+ stream->last_fid ^= UVC_STREAM_FID;
+ }
+}
+
+/* Video payload encoding is handled by uvc_video_encode_header() and
+ * uvc_video_encode_data(). Only bulk transfers are currently supported.
+ *
+ * uvc_video_encode_header is called at the start of a payload. It adds header
+ * data to the transfer buffer and returns the header size. As the only known
+ * UVC output device transfers a whole frame in a single payload, the EOF bit
+ * is always set in the header.
+ *
+ * uvc_video_encode_data is called for every URB and copies the data from the
+ * video buffer to the transfer buffer.
+ */
+static int uvc_video_encode_header(struct uvc_streaming *stream,
+ struct uvc_buffer *buf, __u8 *data, int len)
+{
+ data[0] = 2; /* Header length */
+ data[1] = UVC_STREAM_EOH | UVC_STREAM_EOF
+ | (stream->last_fid & UVC_STREAM_FID);
+ return 2;
+}
+
+static int uvc_video_encode_data(struct uvc_streaming *stream,
+ struct uvc_buffer *buf, __u8 *data, int len)
+{
+ struct uvc_video_queue *queue = &stream->queue;
+ unsigned int nbytes;
+ void *mem;
+
+ /* Copy video data to the URB buffer. */
+ mem = buf->mem + queue->buf_used;
+ nbytes = min((unsigned int)len, buf->bytesused - queue->buf_used);
+ nbytes = min(stream->bulk.max_payload_size - stream->bulk.payload_size,
+ nbytes);
+ memcpy(data, mem, nbytes);
+
+ queue->buf_used += nbytes;
+
+ return nbytes;
+}
+
+/* ------------------------------------------------------------------------
+ * URB handling
+ */
+
+/*
+ * Completion handler for video URBs.
+ */
+static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream,
+ struct uvc_buffer *buf)
+{
+ u8 *mem;
+ int ret, i;
+
+ for (i = 0; i < urb->number_of_packets; ++i) {
+ if (urb->iso_frame_desc[i].status < 0) {
+ uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame "
+ "lost (%d).\n", urb->iso_frame_desc[i].status);
+ /* Mark the buffer as faulty. */
+ if (buf != NULL)
+ buf->error = 1;
+ continue;
+ }
+
+ /* Decode the payload header. */
+ mem = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+ do {
+ ret = uvc_video_decode_start(stream, buf, mem,
+ urb->iso_frame_desc[i].actual_length);
+ if (ret == -EAGAIN)
+ buf = uvc_queue_next_buffer(&stream->queue,
+ buf);
+ } while (ret == -EAGAIN);
+
+ if (ret < 0)
+ continue;
+
+ /* Decode the payload data. */
+ uvc_video_decode_data(stream, buf, mem + ret,
+ urb->iso_frame_desc[i].actual_length - ret);
+
+ /* Process the header again. */
+ uvc_video_decode_end(stream, buf, mem,
+ urb->iso_frame_desc[i].actual_length);
+
+ if (buf->state == UVC_BUF_STATE_READY) {
+ if (buf->length != buf->bytesused &&
+ !(stream->cur_format->flags &
+ UVC_FMT_FLAG_COMPRESSED))
+ buf->error = 1;
+
+ buf = uvc_queue_next_buffer(&stream->queue, buf);
+ }
+ }
+}
+
+static void uvc_video_decode_bulk(struct urb *urb, struct uvc_streaming *stream,
+ struct uvc_buffer *buf)
+{
+ u8 *mem;
+ int len, ret;
+
+ if (urb->actual_length == 0)
+ return;
+
+ mem = urb->transfer_buffer;
+ len = urb->actual_length;
+ stream->bulk.payload_size += len;
+
+ /* If the URB is the first of its payload, decode and save the
+ * header.
+ */
+ if (stream->bulk.header_size == 0 && !stream->bulk.skip_payload) {
+ do {
+ ret = uvc_video_decode_start(stream, buf, mem, len);
+ if (ret == -EAGAIN)
+ buf = uvc_queue_next_buffer(&stream->queue,
+ buf);
+ } while (ret == -EAGAIN);
+
+ /* If an error occurred skip the rest of the payload. */
+ if (ret < 0 || buf == NULL) {
+ stream->bulk.skip_payload = 1;
+ } else {
+ memcpy(stream->bulk.header, mem, ret);
+ stream->bulk.header_size = ret;
+
+ mem += ret;
+ len -= ret;
+ }
+ }
+
+ /* The buffer queue might have been cancelled while a bulk transfer
+ * was in progress, so we can reach here with buf equal to NULL. Make
+ * sure buf is never dereferenced if NULL.
+ */
+
+ /* Process video data. */
+ if (!stream->bulk.skip_payload && buf != NULL)
+ uvc_video_decode_data(stream, buf, mem, len);
+
+ /* Detect the payload end by a URB smaller than the maximum size (or
+ * a payload size equal to the maximum) and process the header again.
+ */
+ if (urb->actual_length < urb->transfer_buffer_length ||
+ stream->bulk.payload_size >= stream->bulk.max_payload_size) {
+ if (!stream->bulk.skip_payload && buf != NULL) {
+ uvc_video_decode_end(stream, buf, stream->bulk.header,
+ stream->bulk.payload_size);
+ if (buf->state == UVC_BUF_STATE_READY)
+ buf = uvc_queue_next_buffer(&stream->queue,
+ buf);
+ }
+
+ stream->bulk.header_size = 0;
+ stream->bulk.skip_payload = 0;
+ stream->bulk.payload_size = 0;
+ }
+}
+
+static void uvc_video_encode_bulk(struct urb *urb, struct uvc_streaming *stream,
+ struct uvc_buffer *buf)
+{
+ u8 *mem = urb->transfer_buffer;
+ int len = stream->urb_size, ret;
+
+ if (buf == NULL) {
+ urb->transfer_buffer_length = 0;
+ return;
+ }
+
+ /* If the URB is the first of its payload, add the header. */
+ if (stream->bulk.header_size == 0) {
+ ret = uvc_video_encode_header(stream, buf, mem, len);
+ stream->bulk.header_size = ret;
+ stream->bulk.payload_size += ret;
+ mem += ret;
+ len -= ret;
+ }
+
+ /* Process video data. */
+ ret = uvc_video_encode_data(stream, buf, mem, len);
+
+ stream->bulk.payload_size += ret;
+ len -= ret;
+
+ if (buf->bytesused == stream->queue.buf_used ||
+ stream->bulk.payload_size == stream->bulk.max_payload_size) {
+ if (buf->bytesused == stream->queue.buf_used) {
+ stream->queue.buf_used = 0;
+ buf->state = UVC_BUF_STATE_READY;
+ buf->buf.v4l2_buf.sequence = ++stream->sequence;
+ uvc_queue_next_buffer(&stream->queue, buf);
+ stream->last_fid ^= UVC_STREAM_FID;
+ }
+
+ stream->bulk.header_size = 0;
+ stream->bulk.payload_size = 0;
+ }
+
+ urb->transfer_buffer_length = stream->urb_size - len;
+}
+
+static void uvc_video_complete(struct urb *urb)
+{
+ struct uvc_streaming *stream = urb->context;
+ struct uvc_video_queue *queue = &stream->queue;
+ struct uvc_buffer *buf = NULL;
+ unsigned long flags;
+ int ret;
+
+ switch (urb->status) {
+ case 0:
+ break;
+
+ default:
+ uvc_printk(KERN_WARNING, "Non-zero status (%d) in video "
+ "completion handler.\n", urb->status);
+
+ case -ENOENT: /* usb_kill_urb() called. */
+ if (stream->frozen)
+ return;
+
+ case -ECONNRESET: /* usb_unlink_urb() called. */
+ case -ESHUTDOWN: /* The endpoint is being disabled. */
+ uvc_queue_cancel(queue, urb->status == -ESHUTDOWN);
+ return;
+ }
+
+ spin_lock_irqsave(&queue->irqlock, flags);
+ if (!list_empty(&queue->irqqueue))
+ buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
+ queue);
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+
+ stream->decode(urb, stream, buf);
+
+ if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
+ uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n",
+ ret);
+ }
+}
+
+/*
+ * Free transfer buffers.
+ */
+static void uvc_free_urb_buffers(struct uvc_streaming *stream)
+{
+ unsigned int i;
+
+ for (i = 0; i < UVC_URBS; ++i) {
+ if (stream->urb_buffer[i]) {
+#ifndef CONFIG_DMA_NONCOHERENT
+ usb_free_coherent(stream->dev->udev, stream->urb_size,
+ stream->urb_buffer[i], stream->urb_dma[i]);
+#else
+ kfree(stream->urb_buffer[i]);
+#endif
+ stream->urb_buffer[i] = NULL;
+ }
+ }
+
+ stream->urb_size = 0;
+}
+
+/*
+ * Allocate transfer buffers. This function can be called with buffers
+ * already allocated when resuming from suspend, in which case it will
+ * return without touching the buffers.
+ *
+ * Limit the buffer size to UVC_MAX_PACKETS bulk/isochronous packets. If the
+ * system is too low on memory try successively smaller numbers of packets
+ * until allocation succeeds.
+ *
+ * Return the number of allocated packets on success or 0 when out of memory.
+ */
+static int uvc_alloc_urb_buffers(struct uvc_streaming *stream,
+ unsigned int size, unsigned int psize, gfp_t gfp_flags)
+{
+ unsigned int npackets;
+ unsigned int i;
+
+ /* Buffers are already allocated, bail out. */
+ if (stream->urb_size)
+ return stream->urb_size / psize;
+
+ /* Compute the number of packets. Bulk endpoints might transfer UVC
+ * payloads across multiple URBs.
+ */
+ npackets = DIV_ROUND_UP(size, psize);
+ if (npackets > UVC_MAX_PACKETS)
+ npackets = UVC_MAX_PACKETS;
+
+ /* Retry allocations until one succeed. */
+ for (; npackets > 1; npackets /= 2) {
+ for (i = 0; i < UVC_URBS; ++i) {
+ stream->urb_size = psize * npackets;
+#ifndef CONFIG_DMA_NONCOHERENT
+ stream->urb_buffer[i] = usb_alloc_coherent(
+ stream->dev->udev, stream->urb_size,
+ gfp_flags | __GFP_NOWARN, &stream->urb_dma[i]);
+#else
+ stream->urb_buffer[i] =
+ kmalloc(stream->urb_size, gfp_flags | __GFP_NOWARN);
+#endif
+ if (!stream->urb_buffer[i]) {
+ uvc_free_urb_buffers(stream);
+ break;
+ }
+ }
+
+ if (i == UVC_URBS) {
+ uvc_trace(UVC_TRACE_VIDEO, "Allocated %u URB buffers "
+ "of %ux%u bytes each.\n", UVC_URBS, npackets,
+ psize);
+ return npackets;
+ }
+ }
+
+ uvc_trace(UVC_TRACE_VIDEO, "Failed to allocate URB buffers (%u bytes "
+ "per packet).\n", psize);
+ return 0;
+}
+
+/*
+ * Uninitialize isochronous/bulk URBs and free transfer buffers.
+ */
+static void uvc_uninit_video(struct uvc_streaming *stream, int free_buffers)
+{
+ struct urb *urb;
+ unsigned int i;
+
+ uvc_video_stats_stop(stream);
+
+ for (i = 0; i < UVC_URBS; ++i) {
+ urb = stream->urb[i];
+ if (urb == NULL)
+ continue;
+
+ usb_kill_urb(urb);
+ usb_free_urb(urb);
+ stream->urb[i] = NULL;
+ }
+
+ if (free_buffers)
+ uvc_free_urb_buffers(stream);
+}
+
+/*
+ * Initialize isochronous URBs and allocate transfer buffers. The packet size
+ * is given by the endpoint.
+ */
+static int uvc_init_video_isoc(struct uvc_streaming *stream,
+ struct usb_host_endpoint *ep, gfp_t gfp_flags)
+{
+ struct urb *urb;
+ unsigned int npackets, i, j;
+ u16 psize;
+ u32 size;
+
+ psize = le16_to_cpu(ep->desc.wMaxPacketSize);
+ psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
+ size = stream->ctrl.dwMaxVideoFrameSize;
+
+ npackets = uvc_alloc_urb_buffers(stream, size, psize, gfp_flags);
+ if (npackets == 0)
+ return -ENOMEM;
+
+ size = npackets * psize;
+
+ for (i = 0; i < UVC_URBS; ++i) {
+ urb = usb_alloc_urb(npackets, gfp_flags);
+ if (urb == NULL) {
+ uvc_uninit_video(stream, 1);
+ return -ENOMEM;
+ }
+
+ urb->dev = stream->dev->udev;
+ urb->context = stream;
+ urb->pipe = usb_rcvisocpipe(stream->dev->udev,
+ ep->desc.bEndpointAddress);
+#ifndef CONFIG_DMA_NONCOHERENT
+ urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+ urb->transfer_dma = stream->urb_dma[i];
+#else
+ urb->transfer_flags = URB_ISO_ASAP;
+#endif
+ urb->interval = ep->desc.bInterval;
+ urb->transfer_buffer = stream->urb_buffer[i];
+ urb->complete = uvc_video_complete;
+ urb->number_of_packets = npackets;
+ urb->transfer_buffer_length = size;
+
+ for (j = 0; j < npackets; ++j) {
+ urb->iso_frame_desc[j].offset = j * psize;
+ urb->iso_frame_desc[j].length = psize;
+ }
+
+ stream->urb[i] = urb;
+ }
+
+ return 0;
+}
+
+/*
+ * Initialize bulk URBs and allocate transfer buffers. The packet size is
+ * given by the endpoint.
+ */
+static int uvc_init_video_bulk(struct uvc_streaming *stream,
+ struct usb_host_endpoint *ep, gfp_t gfp_flags)
+{
+ struct urb *urb;
+ unsigned int npackets, pipe, i;
+ u16 psize;
+ u32 size;
+
+ psize = le16_to_cpu(ep->desc.wMaxPacketSize) & 0x07ff;
+ size = stream->ctrl.dwMaxPayloadTransferSize;
+ stream->bulk.max_payload_size = size;
+
+ npackets = uvc_alloc_urb_buffers(stream, size, psize, gfp_flags);
+ if (npackets == 0)
+ return -ENOMEM;
+
+ size = npackets * psize;
+
+ if (usb_endpoint_dir_in(&ep->desc))
+ pipe = usb_rcvbulkpipe(stream->dev->udev,
+ ep->desc.bEndpointAddress);
+ else
+ pipe = usb_sndbulkpipe(stream->dev->udev,
+ ep->desc.bEndpointAddress);
+
+ if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ size = 0;
+
+ for (i = 0; i < UVC_URBS; ++i) {
+ urb = usb_alloc_urb(0, gfp_flags);
+ if (urb == NULL) {
+ uvc_uninit_video(stream, 1);
+ return -ENOMEM;
+ }
+
+ usb_fill_bulk_urb(urb, stream->dev->udev, pipe,
+ stream->urb_buffer[i], size, uvc_video_complete,
+ stream);
+#ifndef CONFIG_DMA_NONCOHERENT
+ urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+ urb->transfer_dma = stream->urb_dma[i];
+#endif
+
+ stream->urb[i] = urb;
+ }
+
+ return 0;
+}
+
+/*
+ * Initialize isochronous/bulk URBs and allocate transfer buffers.
+ */
+static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
+{
+ struct usb_interface *intf = stream->intf;
+ struct usb_host_endpoint *ep;
+ unsigned int i;
+ int ret;
+
+ stream->sequence = -1;
+ stream->last_fid = -1;
+ stream->bulk.header_size = 0;
+ stream->bulk.skip_payload = 0;
+ stream->bulk.payload_size = 0;
+
+ uvc_video_stats_start(stream);
+
+ if (intf->num_altsetting > 1) {
+ struct usb_host_endpoint *best_ep = NULL;
+ unsigned int best_psize = 3 * 1024;
+ unsigned int bandwidth;
+ unsigned int uninitialized_var(altsetting);
+ int intfnum = stream->intfnum;
+
+ /* Isochronous endpoint, select the alternate setting. */
+ bandwidth = stream->ctrl.dwMaxPayloadTransferSize;
+
+ if (bandwidth == 0) {
+ uvc_trace(UVC_TRACE_VIDEO, "Device requested null "
+ "bandwidth, defaulting to lowest.\n");
+ bandwidth = 1;
+ } else {
+ uvc_trace(UVC_TRACE_VIDEO, "Device requested %u "
+ "B/frame bandwidth.\n", bandwidth);
+ }
+
+ for (i = 0; i < intf->num_altsetting; ++i) {
+ struct usb_host_interface *alts;
+ unsigned int psize;
+
+ alts = &intf->altsetting[i];
+ ep = uvc_find_endpoint(alts,
+ stream->header.bEndpointAddress);
+ if (ep == NULL)
+ continue;
+
+ /* Check if the bandwidth is high enough. */
+ psize = le16_to_cpu(ep->desc.wMaxPacketSize);
+ psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
+ if (psize >= bandwidth && psize <= best_psize) {
+ altsetting = i;
+ best_psize = psize;
+ best_ep = ep;
+ }
+ }
+
+ if (best_ep == NULL) {
+ uvc_trace(UVC_TRACE_VIDEO, "No fast enough alt setting "
+ "for requested bandwidth.\n");
+ return -EIO;
+ }
+
+ uvc_trace(UVC_TRACE_VIDEO, "Selecting alternate setting %u "
+ "(%u B/frame bandwidth).\n", altsetting, best_psize);
+
+ ret = usb_set_interface(stream->dev->udev, intfnum, altsetting);
+ if (ret < 0)
+ return ret;
+
+ ret = uvc_init_video_isoc(stream, best_ep, gfp_flags);
+ } else {
+ /* Bulk endpoint, proceed to URB initialization. */
+ ep = uvc_find_endpoint(&intf->altsetting[0],
+ stream->header.bEndpointAddress);
+ if (ep == NULL)
+ return -EIO;
+
+ ret = uvc_init_video_bulk(stream, ep, gfp_flags);
+ }
+
+ if (ret < 0)
+ return ret;
+
+ /* Submit the URBs. */
+ for (i = 0; i < UVC_URBS; ++i) {
+ ret = usb_submit_urb(stream->urb[i], gfp_flags);
+ if (ret < 0) {
+ uvc_printk(KERN_ERR, "Failed to submit URB %u "
+ "(%d).\n", i, ret);
+ uvc_uninit_video(stream, 1);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/* --------------------------------------------------------------------------
+ * Suspend/resume
+ */
+
+/*
+ * Stop streaming without disabling the video queue.
+ *
+ * To let userspace applications resume without trouble, we must not touch the
+ * video buffers in any way. We mark the device as frozen to make sure the URB
+ * completion handler won't try to cancel the queue when we kill the URBs.
+ */
+int uvc_video_suspend(struct uvc_streaming *stream)
+{
+ if (!uvc_queue_streaming(&stream->queue))
+ return 0;
+
+ stream->frozen = 1;
+ uvc_uninit_video(stream, 0);
+ usb_set_interface(stream->dev->udev, stream->intfnum, 0);
+ return 0;
+}
+
+/*
+ * Reconfigure the video interface and restart streaming if it was enabled
+ * before suspend.
+ *
+ * If an error occurs, disable the video queue. This will wake all pending
+ * buffers, making sure userspace applications are notified of the problem
+ * instead of waiting forever.
+ */
+int uvc_video_resume(struct uvc_streaming *stream, int reset)
+{
+ int ret;
+
+ /* If the bus has been reset on resume, set the alternate setting to 0.
+ * This should be the default value, but some devices crash or otherwise
+ * misbehave if they don't receive a SET_INTERFACE request before any
+ * other video control request.
+ */
+ if (reset)
+ usb_set_interface(stream->dev->udev, stream->intfnum, 0);
+
+ stream->frozen = 0;
+
+ uvc_video_clock_reset(stream);
+
+ ret = uvc_commit_video(stream, &stream->ctrl);
+ if (ret < 0) {
+ uvc_queue_enable(&stream->queue, 0);
+ return ret;
+ }
+
+ if (!uvc_queue_streaming(&stream->queue))
+ return 0;
+
+ ret = uvc_init_video(stream, GFP_NOIO);
+ if (ret < 0)
+ uvc_queue_enable(&stream->queue, 0);
+
+ return ret;
+}
+
+/* ------------------------------------------------------------------------
+ * Video device
+ */
+
+/*
+ * Initialize the UVC video device by switching to alternate setting 0 and
+ * retrieve the default format.
+ *
+ * Some cameras (namely the Fuji Finepix) set the format and frame
+ * indexes to zero. The UVC standard doesn't clearly make this a spec
+ * violation, so try to silently fix the values if possible.
+ *
+ * This function is called before registering the device with V4L.
+ */
+int uvc_video_init(struct uvc_streaming *stream)
+{
+ struct uvc_streaming_control *probe = &stream->ctrl;
+ struct uvc_format *format = NULL;
+ struct uvc_frame *frame = NULL;
+ unsigned int i;
+ int ret;
+
+ if (stream->nformats == 0) {
+ uvc_printk(KERN_INFO, "No supported video formats found.\n");
+ return -EINVAL;
+ }
+
+ atomic_set(&stream->active, 0);
+
+ /* Initialize the video buffers queue. */
+ uvc_queue_init(&stream->queue, stream->type, !uvc_no_drop_param);
+
+ /* Alternate setting 0 should be the default, yet the XBox Live Vision
+ * Cam (and possibly other devices) crash or otherwise misbehave if
+ * they don't receive a SET_INTERFACE request before any other video
+ * control request.
+ */
+ usb_set_interface(stream->dev->udev, stream->intfnum, 0);
+
+ /* Set the streaming probe control with default streaming parameters
+ * retrieved from the device. Webcams that don't suport GET_DEF
+ * requests on the probe control will just keep their current streaming
+ * parameters.
+ */
+ if (uvc_get_video_ctrl(stream, probe, 1, UVC_GET_DEF) == 0)
+ uvc_set_video_ctrl(stream, probe, 1);
+
+ /* Initialize the streaming parameters with the probe control current
+ * value. This makes sure SET_CUR requests on the streaming commit
+ * control will always use values retrieved from a successful GET_CUR
+ * request on the probe control, as required by the UVC specification.
+ */
+ ret = uvc_get_video_ctrl(stream, probe, 1, UVC_GET_CUR);
+ if (ret < 0)
+ return ret;
+
+ /* Check if the default format descriptor exists. Use the first
+ * available format otherwise.
+ */
+ for (i = stream->nformats; i > 0; --i) {
+ format = &stream->format[i-1];
+ if (format->index == probe->bFormatIndex)
+ break;
+ }
+
+ if (format->nframes == 0) {
+ uvc_printk(KERN_INFO, "No frame descriptor found for the "
+ "default format.\n");
+ return -EINVAL;
+ }
+
+ /* Zero bFrameIndex might be correct. Stream-based formats (including
+ * MPEG-2 TS and DV) do not support frames but have a dummy frame
+ * descriptor with bFrameIndex set to zero. If the default frame
+ * descriptor is not found, use the first available frame.
+ */
+ for (i = format->nframes; i > 0; --i) {
+ frame = &format->frame[i-1];
+ if (frame->bFrameIndex == probe->bFrameIndex)
+ break;
+ }
+
+ probe->bFormatIndex = format->index;
+ probe->bFrameIndex = frame->bFrameIndex;
+
+ stream->cur_format = format;
+ stream->cur_frame = frame;
+
+ /* Select the video decoding function */
+ if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (stream->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)
+ stream->decode = uvc_video_decode_isight;
+ else if (stream->intf->num_altsetting > 1)
+ stream->decode = uvc_video_decode_isoc;
+ else
+ stream->decode = uvc_video_decode_bulk;
+ } else {
+ if (stream->intf->num_altsetting == 1)
+ stream->decode = uvc_video_encode_bulk;
+ else {
+ uvc_printk(KERN_INFO, "Isochronous endpoints are not "
+ "supported for video output devices.\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Enable or disable the video stream.
+ */
+int uvc_video_enable(struct uvc_streaming *stream, int enable)
+{
+ int ret;
+
+ if (!enable) {
+ uvc_uninit_video(stream, 1);
+ usb_set_interface(stream->dev->udev, stream->intfnum, 0);
+ uvc_queue_enable(&stream->queue, 0);
+ uvc_video_clock_cleanup(stream);
+ return 0;
+ }
+
+ ret = uvc_video_clock_init(stream);
+ if (ret < 0)
+ return ret;
+
+ ret = uvc_queue_enable(&stream->queue, 1);
+ if (ret < 0)
+ goto error_queue;
+
+ /* Commit the streaming parameters. */
+ ret = uvc_commit_video(stream, &stream->ctrl);
+ if (ret < 0)
+ goto error_commit;
+
+ ret = uvc_init_video(stream, GFP_KERNEL);
+ if (ret < 0)
+ goto error_video;
+
+ return 0;
+
+error_video:
+ usb_set_interface(stream->dev->udev, stream->intfnum, 0);
+error_commit:
+ uvc_queue_enable(&stream->queue, 0);
+error_queue:
+ uvc_video_clock_cleanup(stream);
+
+ return ret;
+}
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h
new file mode 100644
index 00000000..67f88d85
--- /dev/null
+++ b/drivers/media/video/uvc/uvcvideo.h
@@ -0,0 +1,697 @@
+#ifndef _USB_VIDEO_H_
+#define _USB_VIDEO_H_
+
+#ifndef __KERNEL__
+#error "The uvcvideo.h header is deprecated, use linux/uvcvideo.h instead."
+#endif /* __KERNEL__ */
+
+#include <linux/kernel.h>
+#include <linux/poll.h>
+#include <linux/usb.h>
+#include <linux/usb/video.h>
+#include <linux/uvcvideo.h>
+#include <linux/videodev2.h>
+#include <media/media-device.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-core.h>
+
+/* --------------------------------------------------------------------------
+ * UVC constants
+ */
+
+#define UVC_TERM_INPUT 0x0000
+#define UVC_TERM_OUTPUT 0x8000
+#define UVC_TERM_DIRECTION(term) ((term)->type & 0x8000)
+
+#define UVC_ENTITY_TYPE(entity) ((entity)->type & 0x7fff)
+#define UVC_ENTITY_IS_UNIT(entity) (((entity)->type & 0xff00) == 0)
+#define UVC_ENTITY_IS_TERM(entity) (((entity)->type & 0xff00) != 0)
+#define UVC_ENTITY_IS_ITERM(entity) \
+ (UVC_ENTITY_IS_TERM(entity) && \
+ ((entity)->type & 0x8000) == UVC_TERM_INPUT)
+#define UVC_ENTITY_IS_OTERM(entity) \
+ (UVC_ENTITY_IS_TERM(entity) && \
+ ((entity)->type & 0x8000) == UVC_TERM_OUTPUT)
+
+
+/* ------------------------------------------------------------------------
+ * GUIDs
+ */
+#define UVC_GUID_UVC_CAMERA \
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}
+#define UVC_GUID_UVC_OUTPUT \
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}
+#define UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT \
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03}
+#define UVC_GUID_UVC_PROCESSING \
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01}
+#define UVC_GUID_UVC_SELECTOR \
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02}
+
+#define UVC_GUID_FORMAT_MJPEG \
+ { 'M', 'J', 'P', 'G', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_YUY2 \
+ { 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_YUY2_ISIGHT \
+ { 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_NV12 \
+ { 'N', 'V', '1', '2', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_YV12 \
+ { 'Y', 'V', '1', '2', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_I420 \
+ { 'I', '4', '2', '0', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_UYVY \
+ { 'U', 'Y', 'V', 'Y', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_Y800 \
+ { 'Y', '8', '0', '0', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_Y16 \
+ { 'Y', '1', '6', ' ', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_BY8 \
+ { 'B', 'Y', '8', ' ', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_RGBP \
+ { 'R', 'G', 'B', 'P', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_M420 \
+ { 'M', '4', '2', '0', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+
+#define UVC_GUID_FORMAT_H264 \
+ { 'H', '2', '6', '4', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+
+/* ------------------------------------------------------------------------
+ * Driver specific constants.
+ */
+
+#define DRIVER_VERSION "1.1.1"
+
+/* Number of isochronous URBs. */
+#define UVC_URBS 5
+/* Maximum number of packets per URB. */
+#define UVC_MAX_PACKETS 32
+/* Maximum number of video buffers. */
+#define UVC_MAX_VIDEO_BUFFERS 32
+/* Maximum status buffer size in bytes of interrupt URB. */
+#define UVC_MAX_STATUS_SIZE 16
+
+#define UVC_CTRL_CONTROL_TIMEOUT 300
+#define UVC_CTRL_STREAMING_TIMEOUT 5000
+
+/* Maximum allowed number of control mappings per device */
+#define UVC_MAX_CONTROL_MAPPINGS 1024
+#define UVC_MAX_CONTROL_MENU_ENTRIES 32
+
+/* Devices quirks */
+#define UVC_QUIRK_STATUS_INTERVAL 0x00000001
+#define UVC_QUIRK_PROBE_MINMAX 0x00000002
+#define UVC_QUIRK_PROBE_EXTRAFIELDS 0x00000004
+#define UVC_QUIRK_BUILTIN_ISIGHT 0x00000008
+#define UVC_QUIRK_STREAM_NO_FID 0x00000010
+#define UVC_QUIRK_IGNORE_SELECTOR_UNIT 0x00000020
+#define UVC_QUIRK_FIX_BANDWIDTH 0x00000080
+#define UVC_QUIRK_PROBE_DEF 0x00000100
+#define UVC_QUIRK_RESTRICT_FRAME_RATE 0x00000200
+
+/* Format flags */
+#define UVC_FMT_FLAG_COMPRESSED 0x00000001
+#define UVC_FMT_FLAG_STREAM 0x00000002
+
+/* ------------------------------------------------------------------------
+ * Structures.
+ */
+
+struct uvc_device;
+
+/* TODO: Put the most frequently accessed fields at the beginning of
+ * structures to maximize cache efficiency.
+ */
+struct uvc_control_info {
+ struct list_head mappings;
+
+ __u8 entity[16];
+ __u8 index; /* Bit index in bmControls */
+ __u8 selector;
+
+ __u16 size;
+ __u32 flags;
+};
+
+struct uvc_control_mapping {
+ struct list_head list;
+
+ struct uvc_control_info *ctrl;
+
+ __u32 id;
+ __u8 name[32];
+ __u8 entity[16];
+ __u8 selector;
+
+ __u8 size;
+ __u8 offset;
+ enum v4l2_ctrl_type v4l2_type;
+ __u32 data_type;
+
+ struct uvc_menu_info *menu_info;
+ __u32 menu_count;
+
+ __s32 (*get) (struct uvc_control_mapping *mapping, __u8 query,
+ const __u8 *data);
+ void (*set) (struct uvc_control_mapping *mapping, __s32 value,
+ __u8 *data);
+};
+
+struct uvc_control {
+ struct uvc_entity *entity;
+ struct uvc_control_info info;
+
+ __u8 index; /* Used to match the uvc_control entry with a
+ uvc_control_info. */
+ __u8 dirty:1,
+ loaded:1,
+ modified:1,
+ cached:1,
+ initialized:1;
+
+ __u8 *uvc_data;
+};
+
+struct uvc_format_desc {
+ char *name;
+ __u8 guid[16];
+ __u32 fcc;
+};
+
+/* The term 'entity' refers to both UVC units and UVC terminals.
+ *
+ * The type field is either the terminal type (wTerminalType in the terminal
+ * descriptor), or the unit type (bDescriptorSubtype in the unit descriptor).
+ * As the bDescriptorSubtype field is one byte long, the type value will
+ * always have a null MSB for units. All terminal types defined by the UVC
+ * specification have a non-null MSB, so it is safe to use the MSB to
+ * differentiate between units and terminals as long as the descriptor parsing
+ * code makes sure terminal types have a non-null MSB.
+ *
+ * For terminals, the type's most significant bit stores the terminal
+ * direction (either UVC_TERM_INPUT or UVC_TERM_OUTPUT). The type field should
+ * always be accessed with the UVC_ENTITY_* macros and never directly.
+ */
+
+struct uvc_entity {
+ struct list_head list; /* Entity as part of a UVC device. */
+ struct list_head chain; /* Entity as part of a video device
+ * chain. */
+ __u8 id;
+ __u16 type;
+ char name[64];
+
+ /* Media controller-related fields. */
+ struct video_device *vdev;
+ struct v4l2_subdev subdev;
+ unsigned int num_pads;
+ unsigned int num_links;
+ struct media_pad *pads;
+
+ union {
+ struct {
+ __u16 wObjectiveFocalLengthMin;
+ __u16 wObjectiveFocalLengthMax;
+ __u16 wOcularFocalLength;
+ __u8 bControlSize;
+ __u8 *bmControls;
+ } camera;
+
+ struct {
+ __u8 bControlSize;
+ __u8 *bmControls;
+ __u8 bTransportModeSize;
+ __u8 *bmTransportModes;
+ } media;
+
+ struct {
+ } output;
+
+ struct {
+ __u16 wMaxMultiplier;
+ __u8 bControlSize;
+ __u8 *bmControls;
+ __u8 bmVideoStandards;
+ } processing;
+
+ struct {
+ } selector;
+
+ struct {
+ __u8 guidExtensionCode[16];
+ __u8 bNumControls;
+ __u8 bControlSize;
+ __u8 *bmControls;
+ __u8 *bmControlsType;
+ } extension;
+ };
+
+ __u8 bNrInPins;
+ __u8 *baSourceID;
+
+ unsigned int ncontrols;
+ struct uvc_control *controls;
+};
+
+struct uvc_frame {
+ __u8 bFrameIndex;
+ __u8 bmCapabilities;
+ __u16 wWidth;
+ __u16 wHeight;
+ __u32 dwMinBitRate;
+ __u32 dwMaxBitRate;
+ __u32 dwMaxVideoFrameBufferSize;
+ __u8 bFrameIntervalType;
+ __u32 dwDefaultFrameInterval;
+ __u32 *dwFrameInterval;
+};
+
+struct uvc_format {
+ __u8 type;
+ __u8 index;
+ __u8 bpp;
+ __u8 colorspace;
+ __u32 fcc;
+ __u32 flags;
+
+ char name[32];
+
+ unsigned int nframes;
+ struct uvc_frame *frame;
+};
+
+struct uvc_streaming_header {
+ __u8 bNumFormats;
+ __u8 bEndpointAddress;
+ __u8 bTerminalLink;
+ __u8 bControlSize;
+ __u8 *bmaControls;
+ /* The following fields are used by input headers only. */
+ __u8 bmInfo;
+ __u8 bStillCaptureMethod;
+ __u8 bTriggerSupport;
+ __u8 bTriggerUsage;
+};
+
+enum uvc_buffer_state {
+ UVC_BUF_STATE_IDLE = 0,
+ UVC_BUF_STATE_QUEUED = 1,
+ UVC_BUF_STATE_ACTIVE = 2,
+ UVC_BUF_STATE_READY = 3,
+ UVC_BUF_STATE_DONE = 4,
+ UVC_BUF_STATE_ERROR = 5,
+};
+
+struct uvc_buffer {
+ struct vb2_buffer buf;
+ struct list_head queue;
+
+ enum uvc_buffer_state state;
+ unsigned int error;
+
+ void *mem;
+ unsigned int length;
+ unsigned int bytesused;
+
+ u32 pts;
+};
+
+#define UVC_QUEUE_DISCONNECTED (1 << 0)
+#define UVC_QUEUE_DROP_CORRUPTED (1 << 1)
+
+struct uvc_video_queue {
+ struct vb2_queue queue;
+ struct mutex mutex; /* Protects queue */
+
+ unsigned int flags;
+ unsigned int buf_used;
+
+ spinlock_t irqlock; /* Protects irqqueue */
+ struct list_head irqqueue;
+};
+
+struct uvc_video_chain {
+ struct uvc_device *dev;
+ struct list_head list;
+
+ struct list_head entities; /* All entities */
+ struct uvc_entity *processing; /* Processing unit */
+ struct uvc_entity *selector; /* Selector unit */
+
+ struct mutex ctrl_mutex; /* Protects ctrl.info */
+};
+
+struct uvc_stats_frame {
+ unsigned int size; /* Number of bytes captured */
+ unsigned int first_data; /* Index of the first non-empty packet */
+
+ unsigned int nb_packets; /* Number of packets */
+ unsigned int nb_empty; /* Number of empty packets */
+ unsigned int nb_invalid; /* Number of packets with an invalid header */
+ unsigned int nb_errors; /* Number of packets with the error bit set */
+
+ unsigned int nb_pts; /* Number of packets with a PTS timestamp */
+ unsigned int nb_pts_diffs; /* Number of PTS differences inside a frame */
+ unsigned int last_pts_diff; /* Index of the last PTS difference */
+ bool has_initial_pts; /* Whether the first non-empty packet has a PTS */
+ bool has_early_pts; /* Whether a PTS is present before the first non-empty packet */
+ u32 pts; /* PTS of the last packet */
+
+ unsigned int nb_scr; /* Number of packets with a SCR timestamp */
+ unsigned int nb_scr_diffs; /* Number of SCR.STC differences inside a frame */
+ u16 scr_sof; /* SCR.SOF of the last packet */
+ u32 scr_stc; /* SCR.STC of the last packet */
+};
+
+struct uvc_stats_stream {
+ struct timespec start_ts; /* Stream start timestamp */
+ struct timespec stop_ts; /* Stream stop timestamp */
+
+ unsigned int nb_frames; /* Number of frames */
+
+ unsigned int nb_packets; /* Number of packets */
+ unsigned int nb_empty; /* Number of empty packets */
+ unsigned int nb_invalid; /* Number of packets with an invalid header */
+ unsigned int nb_errors; /* Number of packets with the error bit set */
+
+ unsigned int nb_pts_constant; /* Number of frames with constant PTS */
+ unsigned int nb_pts_early; /* Number of frames with early PTS */
+ unsigned int nb_pts_initial; /* Number of frames with initial PTS */
+
+ unsigned int nb_scr_count_ok; /* Number of frames with at least one SCR per non empty packet */
+ unsigned int nb_scr_diffs_ok; /* Number of frames with varying SCR.STC */
+ unsigned int scr_sof_count; /* STC.SOF counter accumulated since stream start */
+ unsigned int scr_sof; /* STC.SOF of the last packet */
+ unsigned int min_sof; /* Minimum STC.SOF value */
+ unsigned int max_sof; /* Maximum STC.SOF value */
+};
+
+struct uvc_streaming {
+ struct list_head list;
+ struct uvc_device *dev;
+ struct video_device *vdev;
+ struct uvc_video_chain *chain;
+ atomic_t active;
+
+ struct usb_interface *intf;
+ int intfnum;
+ __u16 maxpsize;
+
+ struct uvc_streaming_header header;
+ enum v4l2_buf_type type;
+
+ unsigned int nformats;
+ struct uvc_format *format;
+
+ struct uvc_streaming_control ctrl;
+ struct uvc_format *cur_format;
+ struct uvc_frame *cur_frame;
+ /* Protect access to ctrl, cur_format, cur_frame and hardware video
+ * probe control.
+ */
+ struct mutex mutex;
+
+ /* Buffers queue. */
+ unsigned int frozen : 1;
+ struct uvc_video_queue queue;
+ void (*decode) (struct urb *urb, struct uvc_streaming *video,
+ struct uvc_buffer *buf);
+
+ /* Context data used by the bulk completion handler. */
+ struct {
+ __u8 header[256];
+ unsigned int header_size;
+ int skip_payload;
+ __u32 payload_size;
+ __u32 max_payload_size;
+ } bulk;
+
+ struct urb *urb[UVC_URBS];
+ char *urb_buffer[UVC_URBS];
+ dma_addr_t urb_dma[UVC_URBS];
+ unsigned int urb_size;
+
+ __u32 sequence;
+ __u8 last_fid;
+
+ /* debugfs */
+ struct dentry *debugfs_dir;
+ struct {
+ struct uvc_stats_frame frame;
+ struct uvc_stats_stream stream;
+ } stats;
+
+ /* Timestamps support. */
+ struct uvc_clock {
+ struct uvc_clock_sample {
+ u32 dev_stc;
+ u16 dev_sof;
+ struct timespec host_ts;
+ u16 host_sof;
+ } *samples;
+
+ unsigned int head;
+ unsigned int count;
+ unsigned int size;
+
+ u16 last_sof;
+ u16 sof_offset;
+
+ spinlock_t lock;
+ } clock;
+};
+
+enum uvc_device_state {
+ UVC_DEV_DISCONNECTED = 1,
+};
+
+struct uvc_device {
+ struct usb_device *udev;
+ struct usb_interface *intf;
+ unsigned long warnings;
+ __u32 quirks;
+ int intfnum;
+ char name[32];
+
+ enum uvc_device_state state;
+ atomic_t users;
+ atomic_t nmappings;
+
+ /* Video control interface */
+#ifdef CONFIG_MEDIA_CONTROLLER
+ struct media_device mdev;
+#endif
+ struct v4l2_device vdev;
+ __u16 uvc_version;
+ __u32 clock_frequency;
+
+ struct list_head entities;
+ struct list_head chains;
+
+ /* Video Streaming interfaces */
+ struct list_head streams;
+ atomic_t nstreams;
+
+ /* Status Interrupt Endpoint */
+ struct usb_host_endpoint *int_ep;
+ struct urb *int_urb;
+ __u8 *status;
+ struct input_dev *input;
+ char input_phys[64];
+};
+
+enum uvc_handle_state {
+ UVC_HANDLE_PASSIVE = 0,
+ UVC_HANDLE_ACTIVE = 1,
+};
+
+struct uvc_fh {
+ struct uvc_video_chain *chain;
+ struct uvc_streaming *stream;
+ enum uvc_handle_state state;
+};
+
+struct uvc_driver {
+ struct usb_driver driver;
+};
+
+/* ------------------------------------------------------------------------
+ * Debugging, printing and logging
+ */
+
+#define UVC_TRACE_PROBE (1 << 0)
+#define UVC_TRACE_DESCR (1 << 1)
+#define UVC_TRACE_CONTROL (1 << 2)
+#define UVC_TRACE_FORMAT (1 << 3)
+#define UVC_TRACE_CAPTURE (1 << 4)
+#define UVC_TRACE_CALLS (1 << 5)
+#define UVC_TRACE_IOCTL (1 << 6)
+#define UVC_TRACE_FRAME (1 << 7)
+#define UVC_TRACE_SUSPEND (1 << 8)
+#define UVC_TRACE_STATUS (1 << 9)
+#define UVC_TRACE_VIDEO (1 << 10)
+#define UVC_TRACE_STATS (1 << 11)
+#define UVC_TRACE_CLOCK (1 << 12)
+
+#define UVC_WARN_MINMAX 0
+#define UVC_WARN_PROBE_DEF 1
+#define UVC_WARN_XU_GET_RES 2
+
+extern unsigned int uvc_clock_param;
+extern unsigned int uvc_no_drop_param;
+extern unsigned int uvc_trace_param;
+extern unsigned int uvc_timeout_param;
+
+#define uvc_trace(flag, msg...) \
+ do { \
+ if (uvc_trace_param & flag) \
+ printk(KERN_DEBUG "uvcvideo: " msg); \
+ } while (0)
+
+#define uvc_warn_once(dev, warn, msg...) \
+ do { \
+ if (!test_and_set_bit(warn, &dev->warnings)) \
+ printk(KERN_INFO "uvcvideo: " msg); \
+ } while (0)
+
+#define uvc_printk(level, msg...) \
+ printk(level "uvcvideo: " msg)
+
+/* --------------------------------------------------------------------------
+ * Internal functions.
+ */
+
+/* Core driver */
+extern struct uvc_driver uvc_driver;
+
+extern struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id);
+
+/* Video buffers queue management. */
+extern void uvc_queue_init(struct uvc_video_queue *queue,
+ enum v4l2_buf_type type, int drop_corrupted);
+extern int uvc_alloc_buffers(struct uvc_video_queue *queue,
+ struct v4l2_requestbuffers *rb);
+extern void uvc_free_buffers(struct uvc_video_queue *queue);
+extern int uvc_query_buffer(struct uvc_video_queue *queue,
+ struct v4l2_buffer *v4l2_buf);
+extern int uvc_queue_buffer(struct uvc_video_queue *queue,
+ struct v4l2_buffer *v4l2_buf);
+extern int uvc_dequeue_buffer(struct uvc_video_queue *queue,
+ struct v4l2_buffer *v4l2_buf, int nonblocking);
+extern int uvc_queue_enable(struct uvc_video_queue *queue, int enable);
+extern void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect);
+extern struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
+ struct uvc_buffer *buf);
+extern int uvc_queue_mmap(struct uvc_video_queue *queue,
+ struct vm_area_struct *vma);
+extern unsigned int uvc_queue_poll(struct uvc_video_queue *queue,
+ struct file *file, poll_table *wait);
+#ifndef CONFIG_MMU
+extern unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue,
+ unsigned long pgoff);
+#endif
+extern int uvc_queue_allocated(struct uvc_video_queue *queue);
+static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
+{
+ return vb2_is_streaming(&queue->queue);
+}
+
+/* V4L2 interface */
+extern const struct v4l2_file_operations uvc_fops;
+
+/* Media controller */
+extern int uvc_mc_register_entities(struct uvc_video_chain *chain);
+extern void uvc_mc_cleanup_entity(struct uvc_entity *entity);
+
+/* Video */
+extern int uvc_video_init(struct uvc_streaming *stream);
+extern int uvc_video_suspend(struct uvc_streaming *stream);
+extern int uvc_video_resume(struct uvc_streaming *stream, int reset);
+extern int uvc_video_enable(struct uvc_streaming *stream, int enable);
+extern int uvc_probe_video(struct uvc_streaming *stream,
+ struct uvc_streaming_control *probe);
+extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
+ __u8 intfnum, __u8 cs, void *data, __u16 size);
+void uvc_video_clock_update(struct uvc_streaming *stream,
+ struct v4l2_buffer *v4l2_buf,
+ struct uvc_buffer *buf);
+
+/* Status */
+extern int uvc_status_init(struct uvc_device *dev);
+extern void uvc_status_cleanup(struct uvc_device *dev);
+extern int uvc_status_start(struct uvc_device *dev);
+extern void uvc_status_stop(struct uvc_device *dev);
+extern int uvc_status_suspend(struct uvc_device *dev);
+extern int uvc_status_resume(struct uvc_device *dev);
+
+/* Controls */
+extern int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
+ struct v4l2_queryctrl *v4l2_ctrl);
+extern int uvc_query_v4l2_menu(struct uvc_video_chain *chain,
+ struct v4l2_querymenu *query_menu);
+
+extern int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
+ const struct uvc_control_mapping *mapping);
+extern int uvc_ctrl_init_device(struct uvc_device *dev);
+extern void uvc_ctrl_cleanup_device(struct uvc_device *dev);
+extern int uvc_ctrl_resume_device(struct uvc_device *dev);
+
+extern int uvc_ctrl_begin(struct uvc_video_chain *chain);
+extern int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback);
+static inline int uvc_ctrl_commit(struct uvc_video_chain *chain)
+{
+ return __uvc_ctrl_commit(chain, 0);
+}
+static inline int uvc_ctrl_rollback(struct uvc_video_chain *chain)
+{
+ return __uvc_ctrl_commit(chain, 1);
+}
+
+extern int uvc_ctrl_get(struct uvc_video_chain *chain,
+ struct v4l2_ext_control *xctrl);
+extern int uvc_ctrl_set(struct uvc_video_chain *chain,
+ struct v4l2_ext_control *xctrl);
+
+extern int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
+ struct uvc_xu_control_query *xqry);
+
+/* Utility functions */
+extern void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator,
+ unsigned int n_terms, unsigned int threshold);
+extern uint32_t uvc_fraction_to_interval(uint32_t numerator,
+ uint32_t denominator);
+extern struct usb_host_endpoint *uvc_find_endpoint(
+ struct usb_host_interface *alts, __u8 epaddr);
+
+/* Quirks support */
+void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream,
+ struct uvc_buffer *buf);
+
+/* debugfs and statistics */
+int uvc_debugfs_init(void);
+void uvc_debugfs_cleanup(void);
+int uvc_debugfs_init_stream(struct uvc_streaming *stream);
+void uvc_debugfs_cleanup_stream(struct uvc_streaming *stream);
+
+size_t uvc_video_stats_dump(struct uvc_streaming *stream, char *buf,
+ size_t size);
+
+#endif
diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c
new file mode 100644
index 00000000..1baec839
--- /dev/null
+++ b/drivers/media/video/v4l2-common.c
@@ -0,0 +1,623 @@
+/*
+ * Video for Linux Two
+ *
+ * A generic video device interface for the LINUX operating system
+ * using a set of device structures/vectors for low level operations.
+ *
+ * This file replaces the videodev.c file that comes with the
+ * regular kernel distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Author: Bill Dirks <bill@thedirks.org>
+ * based on code by Alan Cox, <alan@cymru.net>
+ *
+ */
+
+/*
+ * Video capture interface for Linux
+ *
+ * A generic video device interface for the LINUX operating system
+ * using a set of device structures/vectors for low level operations.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Author: Alan Cox, <alan@lxorguk.ukuu.org.uk>
+ *
+ * Fixes:
+ */
+
+/*
+ * Video4linux 1/2 integration by Justin Schoeman
+ * <justin@suntiger.ee.up.ac.za>
+ * 2.4 PROCFS support ported from 2.4 kernels by
+ * Iñaki García Etxebarria <garetxe@euskalnet.net>
+ * Makefile fix by "W. Michael Petullo" <mike@flyn.org>
+ * 2.4 devfs support ported from 2.4 kernels by
+ * Dan Merillat <dan@merillat.org>
+ * Added Gerd Knorrs v4l1 enhancements (Justin Schoeman)
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#if defined(CONFIG_SPI)
+#include <linux/spi/spi.h>
+#endif
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+#include <asm/div64.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-chip-ident.h>
+
+#include <linux/videodev2.h>
+
+MODULE_AUTHOR("Bill Dirks, Justin Schoeman, Gerd Knorr");
+MODULE_DESCRIPTION("misc helper functions for v4l2 device drivers");
+MODULE_LICENSE("GPL");
+
+/*
+ *
+ * V 4 L 2 D R I V E R H E L P E R A P I
+ *
+ */
+
+/*
+ * Video Standard Operations (contributed by Michael Schimek)
+ */
+
+/* Helper functions for control handling */
+
+/* Check for correctness of the ctrl's value based on the data from
+ struct v4l2_queryctrl and the available menu items. Note that
+ menu_items may be NULL, in that case it is ignored. */
+int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl,
+ const char * const *menu_items)
+{
+ if (qctrl->flags & V4L2_CTRL_FLAG_DISABLED)
+ return -EINVAL;
+ if (qctrl->flags & V4L2_CTRL_FLAG_GRABBED)
+ return -EBUSY;
+ if (qctrl->type == V4L2_CTRL_TYPE_STRING)
+ return 0;
+ if (qctrl->type == V4L2_CTRL_TYPE_BUTTON ||
+ qctrl->type == V4L2_CTRL_TYPE_INTEGER64 ||
+ qctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
+ return 0;
+ if (ctrl->value < qctrl->minimum || ctrl->value > qctrl->maximum)
+ return -ERANGE;
+ if (qctrl->type == V4L2_CTRL_TYPE_MENU && menu_items != NULL) {
+ if (menu_items[ctrl->value] == NULL ||
+ menu_items[ctrl->value][0] == '\0')
+ return -EINVAL;
+ }
+ if (qctrl->type == V4L2_CTRL_TYPE_BITMASK &&
+ (ctrl->value & ~qctrl->maximum))
+ return -ERANGE;
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_ctrl_check);
+
+/* Fill in a struct v4l2_queryctrl */
+int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def)
+{
+ const char *name;
+
+ v4l2_ctrl_fill(qctrl->id, &name, &qctrl->type,
+ &min, &max, &step, &def, &qctrl->flags);
+
+ if (name == NULL)
+ return -EINVAL;
+
+ qctrl->minimum = min;
+ qctrl->maximum = max;
+ qctrl->step = step;
+ qctrl->default_value = def;
+ qctrl->reserved[0] = qctrl->reserved[1] = 0;
+ strlcpy(qctrl->name, name, sizeof(qctrl->name));
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_ctrl_query_fill);
+
+/* Fill in a struct v4l2_querymenu based on the struct v4l2_queryctrl and
+ the menu. The qctrl pointer may be NULL, in which case it is ignored.
+ If menu_items is NULL, then the menu items are retrieved using
+ v4l2_ctrl_get_menu. */
+int v4l2_ctrl_query_menu(struct v4l2_querymenu *qmenu, struct v4l2_queryctrl *qctrl,
+ const char * const *menu_items)
+{
+ int i;
+
+ qmenu->reserved = 0;
+ if (menu_items == NULL)
+ menu_items = v4l2_ctrl_get_menu(qmenu->id);
+ if (menu_items == NULL ||
+ (qctrl && (qmenu->index < qctrl->minimum || qmenu->index > qctrl->maximum)))
+ return -EINVAL;
+ for (i = 0; i < qmenu->index && menu_items[i]; i++) ;
+ if (menu_items[i] == NULL || menu_items[i][0] == '\0')
+ return -EINVAL;
+ strlcpy(qmenu->name, menu_items[qmenu->index], sizeof(qmenu->name));
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_ctrl_query_menu);
+
+/* Fill in a struct v4l2_querymenu based on the specified array of valid
+ menu items (terminated by V4L2_CTRL_MENU_IDS_END).
+ Use this if there are 'holes' in the list of valid menu items. */
+int v4l2_ctrl_query_menu_valid_items(struct v4l2_querymenu *qmenu, const u32 *ids)
+{
+ const char * const *menu_items = v4l2_ctrl_get_menu(qmenu->id);
+
+ qmenu->reserved = 0;
+ if (menu_items == NULL || ids == NULL)
+ return -EINVAL;
+ while (*ids != V4L2_CTRL_MENU_IDS_END) {
+ if (*ids++ == qmenu->index) {
+ strlcpy(qmenu->name, menu_items[qmenu->index],
+ sizeof(qmenu->name));
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL(v4l2_ctrl_query_menu_valid_items);
+
+/* ctrl_classes points to an array of u32 pointers, the last element is
+ a NULL pointer. Each u32 array is a 0-terminated array of control IDs.
+ Each array must be sorted low to high and belong to the same control
+ class. The array of u32 pointers must also be sorted, from low class IDs
+ to high class IDs.
+
+ This function returns the first ID that follows after the given ID.
+ When no more controls are available 0 is returned. */
+u32 v4l2_ctrl_next(const u32 * const * ctrl_classes, u32 id)
+{
+ u32 ctrl_class = V4L2_CTRL_ID2CLASS(id);
+ const u32 *pctrl;
+
+ if (ctrl_classes == NULL)
+ return 0;
+
+ /* if no query is desired, then check if the ID is part of ctrl_classes */
+ if ((id & V4L2_CTRL_FLAG_NEXT_CTRL) == 0) {
+ /* find class */
+ while (*ctrl_classes && V4L2_CTRL_ID2CLASS(**ctrl_classes) != ctrl_class)
+ ctrl_classes++;
+ if (*ctrl_classes == NULL)
+ return 0;
+ pctrl = *ctrl_classes;
+ /* find control ID */
+ while (*pctrl && *pctrl != id) pctrl++;
+ return *pctrl ? id : 0;
+ }
+ id &= V4L2_CTRL_ID_MASK;
+ id++; /* select next control */
+ /* find first class that matches (or is greater than) the class of
+ the ID */
+ while (*ctrl_classes && V4L2_CTRL_ID2CLASS(**ctrl_classes) < ctrl_class)
+ ctrl_classes++;
+ /* no more classes */
+ if (*ctrl_classes == NULL)
+ return 0;
+ pctrl = *ctrl_classes;
+ /* find first ctrl within the class that is >= ID */
+ while (*pctrl && *pctrl < id) pctrl++;
+ if (*pctrl)
+ return *pctrl;
+ /* we are at the end of the controls of the current class. */
+ /* continue with next class if available */
+ ctrl_classes++;
+ if (*ctrl_classes == NULL)
+ return 0;
+ return **ctrl_classes;
+}
+EXPORT_SYMBOL(v4l2_ctrl_next);
+
+int v4l2_chip_match_host(const struct v4l2_dbg_match *match)
+{
+ switch (match->type) {
+ case V4L2_CHIP_MATCH_HOST:
+ return match->addr == 0;
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL(v4l2_chip_match_host);
+
+#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
+int v4l2_chip_match_i2c_client(struct i2c_client *c, const struct v4l2_dbg_match *match)
+{
+ int len;
+
+ if (c == NULL || match == NULL)
+ return 0;
+
+ switch (match->type) {
+ case V4L2_CHIP_MATCH_I2C_DRIVER:
+ if (c->driver == NULL || c->driver->driver.name == NULL)
+ return 0;
+ len = strlen(c->driver->driver.name);
+ /* legacy drivers have a ' suffix, don't try to match that */
+ if (len && c->driver->driver.name[len - 1] == '\'')
+ len--;
+ return len && !strncmp(c->driver->driver.name, match->name, len);
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ return c->addr == match->addr;
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL(v4l2_chip_match_i2c_client);
+
+int v4l2_chip_ident_i2c_client(struct i2c_client *c, struct v4l2_dbg_chip_ident *chip,
+ u32 ident, u32 revision)
+{
+ if (!v4l2_chip_match_i2c_client(c, &chip->match))
+ return 0;
+ if (chip->ident == V4L2_IDENT_NONE) {
+ chip->ident = ident;
+ chip->revision = revision;
+ }
+ else {
+ chip->ident = V4L2_IDENT_AMBIGUOUS;
+ chip->revision = 0;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_chip_ident_i2c_client);
+
+/* ----------------------------------------------------------------- */
+
+/* I2C Helper functions */
+
+
+void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
+ const struct v4l2_subdev_ops *ops)
+{
+ v4l2_subdev_init(sd, ops);
+ sd->flags |= V4L2_SUBDEV_FL_IS_I2C;
+ /* the owner is the same as the i2c_client's driver owner */
+ sd->owner = client->driver->driver.owner;
+ /* i2c_client and v4l2_subdev point to one another */
+ v4l2_set_subdevdata(sd, client);
+ i2c_set_clientdata(client, sd);
+ /* initialize name */
+ snprintf(sd->name, sizeof(sd->name), "%s %d-%04x",
+ client->driver->driver.name, i2c_adapter_id(client->adapter),
+ client->addr);
+}
+EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_init);
+
+
+
+/* Load an i2c sub-device. */
+struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev,
+ struct i2c_adapter *adapter, struct i2c_board_info *info,
+ const unsigned short *probe_addrs)
+{
+ struct v4l2_subdev *sd = NULL;
+ struct i2c_client *client;
+
+ BUG_ON(!v4l2_dev);
+
+ request_module(I2C_MODULE_PREFIX "%s", info->type);
+
+ /* Create the i2c client */
+ if (info->addr == 0 && probe_addrs)
+ client = i2c_new_probed_device(adapter, info, probe_addrs,
+ NULL);
+ else
+ client = i2c_new_device(adapter, info);
+
+ /* Note: by loading the module first we are certain that c->driver
+ will be set if the driver was found. If the module was not loaded
+ first, then the i2c core tries to delay-load the module for us,
+ and then c->driver is still NULL until the module is finally
+ loaded. This delay-load mechanism doesn't work if other drivers
+ want to use the i2c device, so explicitly loading the module
+ is the best alternative. */
+ if (client == NULL || client->driver == NULL)
+ goto error;
+
+ /* Lock the module so we can safely get the v4l2_subdev pointer */
+ if (!try_module_get(client->driver->driver.owner))
+ goto error;
+ sd = i2c_get_clientdata(client);
+
+ /* Register with the v4l2_device which increases the module's
+ use count as well. */
+ if (v4l2_device_register_subdev(v4l2_dev, sd))
+ sd = NULL;
+ /* Decrease the module use count to match the first try_module_get. */
+ module_put(client->driver->driver.owner);
+
+error:
+ /* If we have a client but no subdev, then something went wrong and
+ we must unregister the client. */
+ if (client && sd == NULL)
+ i2c_unregister_device(client);
+ return sd;
+}
+EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev_board);
+
+struct v4l2_subdev *v4l2_i2c_new_subdev(struct v4l2_device *v4l2_dev,
+ struct i2c_adapter *adapter, const char *client_type,
+ u8 addr, const unsigned short *probe_addrs)
+{
+ struct i2c_board_info info;
+
+ /* Setup the i2c board info with the device type and
+ the device address. */
+ memset(&info, 0, sizeof(info));
+ strlcpy(info.type, client_type, sizeof(info.type));
+ info.addr = addr;
+
+ return v4l2_i2c_new_subdev_board(v4l2_dev, adapter, &info, probe_addrs);
+}
+EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev);
+
+/* Return i2c client address of v4l2_subdev. */
+unsigned short v4l2_i2c_subdev_addr(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return client ? client->addr : I2C_CLIENT_END;
+}
+EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_addr);
+
+/* Return a list of I2C tuner addresses to probe. Use only if the tuner
+ addresses are unknown. */
+const unsigned short *v4l2_i2c_tuner_addrs(enum v4l2_i2c_tuner_type type)
+{
+ static const unsigned short radio_addrs[] = {
+#if defined(CONFIG_MEDIA_TUNER_TEA5761) || defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE)
+ 0x10,
+#endif
+ 0x60,
+ I2C_CLIENT_END
+ };
+ static const unsigned short demod_addrs[] = {
+ 0x42, 0x43, 0x4a, 0x4b,
+ I2C_CLIENT_END
+ };
+ static const unsigned short tv_addrs[] = {
+ 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
+ 0x60, 0x61, 0x62, 0x63, 0x64,
+ I2C_CLIENT_END
+ };
+
+ switch (type) {
+ case ADDRS_RADIO:
+ return radio_addrs;
+ case ADDRS_DEMOD:
+ return demod_addrs;
+ case ADDRS_TV:
+ return tv_addrs;
+ case ADDRS_TV_WITH_DEMOD:
+ return tv_addrs + 4;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(v4l2_i2c_tuner_addrs);
+
+#endif /* defined(CONFIG_I2C) */
+
+#if defined(CONFIG_SPI)
+
+/* Load a spi sub-device. */
+
+void v4l2_spi_subdev_init(struct v4l2_subdev *sd, struct spi_device *spi,
+ const struct v4l2_subdev_ops *ops)
+{
+ v4l2_subdev_init(sd, ops);
+ sd->flags |= V4L2_SUBDEV_FL_IS_SPI;
+ /* the owner is the same as the spi_device's driver owner */
+ sd->owner = spi->dev.driver->owner;
+ /* spi_device and v4l2_subdev point to one another */
+ v4l2_set_subdevdata(sd, spi);
+ spi_set_drvdata(spi, sd);
+ /* initialize name */
+ strlcpy(sd->name, spi->dev.driver->name, sizeof(sd->name));
+}
+EXPORT_SYMBOL_GPL(v4l2_spi_subdev_init);
+
+struct v4l2_subdev *v4l2_spi_new_subdev(struct v4l2_device *v4l2_dev,
+ struct spi_master *master, struct spi_board_info *info)
+{
+ struct v4l2_subdev *sd = NULL;
+ struct spi_device *spi = NULL;
+
+ BUG_ON(!v4l2_dev);
+
+ if (info->modalias)
+ request_module(info->modalias);
+
+ spi = spi_new_device(master, info);
+
+ if (spi == NULL || spi->dev.driver == NULL)
+ goto error;
+
+ if (!try_module_get(spi->dev.driver->owner))
+ goto error;
+
+ sd = spi_get_drvdata(spi);
+
+ /* Register with the v4l2_device which increases the module's
+ use count as well. */
+ if (v4l2_device_register_subdev(v4l2_dev, sd))
+ sd = NULL;
+
+ /* Decrease the module use count to match the first try_module_get. */
+ module_put(spi->dev.driver->owner);
+
+error:
+ /* If we have a client but no subdev, then something went wrong and
+ we must unregister the client. */
+ if (spi && sd == NULL)
+ spi_unregister_device(spi);
+
+ return sd;
+}
+EXPORT_SYMBOL_GPL(v4l2_spi_new_subdev);
+
+#endif /* defined(CONFIG_SPI) */
+
+/* Clamp x to be between min and max, aligned to a multiple of 2^align. min
+ * and max don't have to be aligned, but there must be at least one valid
+ * value. E.g., min=17,max=31,align=4 is not allowed as there are no multiples
+ * of 16 between 17 and 31. */
+static unsigned int clamp_align(unsigned int x, unsigned int min,
+ unsigned int max, unsigned int align)
+{
+ /* Bits that must be zero to be aligned */
+ unsigned int mask = ~((1 << align) - 1);
+
+ /* Round to nearest aligned value */
+ if (align)
+ x = (x + (1 << (align - 1))) & mask;
+
+ /* Clamp to aligned value of min and max */
+ if (x < min)
+ x = (min + ~mask) & mask;
+ else if (x > max)
+ x = max & mask;
+
+ return x;
+}
+
+/* Bound an image to have a width between wmin and wmax, and height between
+ * hmin and hmax, inclusive. Additionally, the width will be a multiple of
+ * 2^walign, the height will be a multiple of 2^halign, and the overall size
+ * (width*height) will be a multiple of 2^salign. The image may be shrunk
+ * or enlarged to fit the alignment constraints.
+ *
+ * The width or height maximum must not be smaller than the corresponding
+ * minimum. The alignments must not be so high there are no possible image
+ * sizes within the allowed bounds. wmin and hmin must be at least 1
+ * (don't use 0). If you don't care about a certain alignment, specify 0,
+ * as 2^0 is 1 and one byte alignment is equivalent to no alignment. If
+ * you only want to adjust downward, specify a maximum that's the same as
+ * the initial value.
+ */
+void v4l_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax,
+ unsigned int walign,
+ u32 *h, unsigned int hmin, unsigned int hmax,
+ unsigned int halign, unsigned int salign)
+{
+ *w = clamp_align(*w, wmin, wmax, walign);
+ *h = clamp_align(*h, hmin, hmax, halign);
+
+ /* Usually we don't need to align the size and are done now. */
+ if (!salign)
+ return;
+
+ /* How much alignment do we have? */
+ walign = __ffs(*w);
+ halign = __ffs(*h);
+ /* Enough to satisfy the image alignment? */
+ if (walign + halign < salign) {
+ /* Max walign where there is still a valid width */
+ unsigned int wmaxa = __fls(wmax ^ (wmin - 1));
+ /* Max halign where there is still a valid height */
+ unsigned int hmaxa = __fls(hmax ^ (hmin - 1));
+
+ /* up the smaller alignment until we have enough */
+ do {
+ if (halign >= hmaxa ||
+ (walign <= halign && walign < wmaxa)) {
+ *w = clamp_align(*w, wmin, wmax, walign + 1);
+ walign = __ffs(*w);
+ } else {
+ *h = clamp_align(*h, hmin, hmax, halign + 1);
+ halign = __ffs(*h);
+ }
+ } while (halign + walign < salign);
+ }
+}
+EXPORT_SYMBOL_GPL(v4l_bound_align_image);
+
+/**
+ * v4l_fill_dv_preset_info - fill description of a digital video preset
+ * @preset - preset value
+ * @info - pointer to struct v4l2_dv_enum_preset
+ *
+ * drivers can use this helper function to fill description of dv preset
+ * in info.
+ */
+int v4l_fill_dv_preset_info(u32 preset, struct v4l2_dv_enum_preset *info)
+{
+ static const struct v4l2_dv_preset_info {
+ u16 width;
+ u16 height;
+ const char *name;
+ } dv_presets[] = {
+ { 0, 0, "Invalid" }, /* V4L2_DV_INVALID */
+ { 720, 480, "480p@59.94" }, /* V4L2_DV_480P59_94 */
+ { 720, 576, "576p@50" }, /* V4L2_DV_576P50 */
+ { 1280, 720, "720p@24" }, /* V4L2_DV_720P24 */
+ { 1280, 720, "720p@25" }, /* V4L2_DV_720P25 */
+ { 1280, 720, "720p@30" }, /* V4L2_DV_720P30 */
+ { 1280, 720, "720p@50" }, /* V4L2_DV_720P50 */
+ { 1280, 720, "720p@59.94" }, /* V4L2_DV_720P59_94 */
+ { 1280, 720, "720p@60" }, /* V4L2_DV_720P60 */
+ { 1920, 1080, "1080i@29.97" }, /* V4L2_DV_1080I29_97 */
+ { 1920, 1080, "1080i@30" }, /* V4L2_DV_1080I30 */
+ { 1920, 1080, "1080i@25" }, /* V4L2_DV_1080I25 */
+ { 1920, 1080, "1080i@50" }, /* V4L2_DV_1080I50 */
+ { 1920, 1080, "1080i@60" }, /* V4L2_DV_1080I60 */
+ { 1920, 1080, "1080p@24" }, /* V4L2_DV_1080P24 */
+ { 1920, 1080, "1080p@25" }, /* V4L2_DV_1080P25 */
+ { 1920, 1080, "1080p@30" }, /* V4L2_DV_1080P30 */
+ { 1920, 1080, "1080p@50" }, /* V4L2_DV_1080P50 */
+ { 1920, 1080, "1080p@60" }, /* V4L2_DV_1080P60 */
+ };
+
+ if (info == NULL || preset >= ARRAY_SIZE(dv_presets))
+ return -EINVAL;
+
+ info->preset = preset;
+ info->width = dv_presets[preset].width;
+ info->height = dv_presets[preset].height;
+ strlcpy(info->name, dv_presets[preset].name, sizeof(info->name));
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l_fill_dv_preset_info);
+
+const struct v4l2_frmsize_discrete *v4l2_find_nearest_format(
+ const struct v4l2_discrete_probe *probe,
+ s32 width, s32 height)
+{
+ int i;
+ u32 error, min_error = UINT_MAX;
+ const struct v4l2_frmsize_discrete *size, *best = NULL;
+
+ if (!probe)
+ return best;
+
+ for (i = 0, size = probe->sizes; i < probe->num_sizes; i++, size++) {
+ error = abs(size->width - width) + abs(size->height - height);
+ if (error < min_error) {
+ min_error = error;
+ best = size;
+ }
+ if (!error)
+ break;
+ }
+
+ return best;
+}
+EXPORT_SYMBOL_GPL(v4l2_find_nearest_format);
diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c
new file mode 100644
index 00000000..2829d256
--- /dev/null
+++ b/drivers/media/video/v4l2-compat-ioctl32.c
@@ -0,0 +1,1042 @@
+/*
+ * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
+ * Separated from fs stuff by Arnd Bergmann <arnd@arndb.de>
+ *
+ * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com)
+ * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 2001,2002 Andi Kleen, SuSE Labs
+ * Copyright (C) 2003 Pavel Machek (pavel@ucw.cz)
+ * Copyright (C) 2005 Philippe De Muyter (phdm@macqel.be)
+ * Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * These routines maintain argument size conversion between 32bit and 64bit
+ * ioctls.
+ */
+
+#include <linux/compat.h>
+#include <linux/module.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+
+static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ long ret = -ENOIOCTLCMD;
+
+ if (file->f_op->unlocked_ioctl)
+ ret = file->f_op->unlocked_ioctl(file, cmd, arg);
+
+ return ret;
+}
+
+
+struct v4l2_clip32 {
+ struct v4l2_rect c;
+ compat_caddr_t next;
+};
+
+struct v4l2_window32 {
+ struct v4l2_rect w;
+ enum v4l2_field field;
+ __u32 chromakey;
+ compat_caddr_t clips; /* actually struct v4l2_clip32 * */
+ __u32 clipcount;
+ compat_caddr_t bitmap;
+};
+
+static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
+{
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_window32)) ||
+ copy_from_user(&kp->w, &up->w, sizeof(up->w)) ||
+ get_user(kp->field, &up->field) ||
+ get_user(kp->chromakey, &up->chromakey) ||
+ get_user(kp->clipcount, &up->clipcount))
+ return -EFAULT;
+ if (kp->clipcount > 2048)
+ return -EINVAL;
+ if (kp->clipcount) {
+ struct v4l2_clip32 __user *uclips;
+ struct v4l2_clip __user *kclips;
+ int n = kp->clipcount;
+ compat_caddr_t p;
+
+ if (get_user(p, &up->clips))
+ return -EFAULT;
+ uclips = compat_ptr(p);
+ kclips = compat_alloc_user_space(n * sizeof(struct v4l2_clip));
+ kp->clips = kclips;
+ while (--n >= 0) {
+ if (copy_in_user(&kclips->c, &uclips->c, sizeof(uclips->c)))
+ return -EFAULT;
+ if (put_user(n ? kclips + 1 : NULL, &kclips->next))
+ return -EFAULT;
+ uclips += 1;
+ kclips += 1;
+ }
+ } else
+ kp->clips = NULL;
+ return 0;
+}
+
+static int put_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
+{
+ if (copy_to_user(&up->w, &kp->w, sizeof(kp->w)) ||
+ put_user(kp->field, &up->field) ||
+ put_user(kp->chromakey, &up->chromakey) ||
+ put_user(kp->clipcount, &up->clipcount))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int get_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up)
+{
+ if (copy_from_user(kp, up, sizeof(struct v4l2_pix_format)))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int get_v4l2_pix_format_mplane(struct v4l2_pix_format_mplane *kp,
+ struct v4l2_pix_format_mplane __user *up)
+{
+ if (copy_from_user(kp, up, sizeof(struct v4l2_pix_format_mplane)))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int put_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up)
+{
+ if (copy_to_user(up, kp, sizeof(struct v4l2_pix_format)))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int put_v4l2_pix_format_mplane(struct v4l2_pix_format_mplane *kp,
+ struct v4l2_pix_format_mplane __user *up)
+{
+ if (copy_to_user(up, kp, sizeof(struct v4l2_pix_format_mplane)))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int get_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vbi_format __user *up)
+{
+ if (copy_from_user(kp, up, sizeof(struct v4l2_vbi_format)))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int put_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vbi_format __user *up)
+{
+ if (copy_to_user(up, kp, sizeof(struct v4l2_vbi_format)))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int get_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, struct v4l2_sliced_vbi_format __user *up)
+{
+ if (copy_from_user(kp, up, sizeof(struct v4l2_sliced_vbi_format)))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int put_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, struct v4l2_sliced_vbi_format __user *up)
+{
+ if (copy_to_user(up, kp, sizeof(struct v4l2_sliced_vbi_format)))
+ return -EFAULT;
+ return 0;
+}
+
+struct v4l2_format32 {
+ enum v4l2_buf_type type;
+ union {
+ struct v4l2_pix_format pix;
+ struct v4l2_pix_format_mplane pix_mp;
+ struct v4l2_window32 win;
+ struct v4l2_vbi_format vbi;
+ struct v4l2_sliced_vbi_format sliced;
+ __u8 raw_data[200]; /* user-defined */
+ } fmt;
+};
+
+/**
+ * struct v4l2_create_buffers32 - VIDIOC_CREATE_BUFS32 argument
+ * @index: on return, index of the first created buffer
+ * @count: entry: number of requested buffers,
+ * return: number of created buffers
+ * @memory: buffer memory type
+ * @format: frame format, for which buffers are requested
+ * @reserved: future extensions
+ */
+struct v4l2_create_buffers32 {
+ __u32 index;
+ __u32 count;
+ enum v4l2_memory memory;
+ struct v4l2_format32 format;
+ __u32 reserved[8];
+};
+
+static int __get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+{
+ switch (kp->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ return get_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix);
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ return get_v4l2_pix_format_mplane(&kp->fmt.pix_mp,
+ &up->fmt.pix_mp);
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ return get_v4l2_window32(&kp->fmt.win, &up->fmt.win);
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ return get_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi);
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ return get_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced);
+ case V4L2_BUF_TYPE_PRIVATE:
+ if (copy_from_user(kp, up, sizeof(kp->fmt.raw_data)))
+ return -EFAULT;
+ return 0;
+ default:
+ printk(KERN_INFO "compat_ioctl32: unexpected VIDIOC_FMT type %d\n",
+ kp->type);
+ return -EINVAL;
+ }
+}
+
+static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+{
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32)) ||
+ get_user(kp->type, &up->type))
+ return -EFAULT;
+ return __get_v4l2_format32(kp, up);
+}
+
+static int get_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
+{
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_create_buffers32)) ||
+ copy_from_user(kp, up, offsetof(struct v4l2_create_buffers32, format.fmt)))
+ return -EFAULT;
+ return __get_v4l2_format32(&kp->format, &up->format);
+}
+
+static int __put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+{
+ switch (kp->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ return put_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix);
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ return put_v4l2_pix_format_mplane(&kp->fmt.pix_mp,
+ &up->fmt.pix_mp);
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ return put_v4l2_window32(&kp->fmt.win, &up->fmt.win);
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ return put_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi);
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ return put_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced);
+ case V4L2_BUF_TYPE_PRIVATE:
+ if (copy_to_user(up, kp, sizeof(up->fmt.raw_data)))
+ return -EFAULT;
+ return 0;
+ default:
+ printk(KERN_INFO "compat_ioctl32: unexpected VIDIOC_FMT type %d\n",
+ kp->type);
+ return -EINVAL;
+ }
+}
+
+static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+{
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)) ||
+ put_user(kp->type, &up->type))
+ return -EFAULT;
+ return __put_v4l2_format32(kp, up);
+}
+
+static int put_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
+{
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_create_buffers32)) ||
+ copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format.fmt)))
+ return -EFAULT;
+ return __put_v4l2_format32(&kp->format, &up->format);
+}
+
+struct v4l2_standard32 {
+ __u32 index;
+ __u32 id[2]; /* __u64 would get the alignment wrong */
+ __u8 name[24];
+ struct v4l2_fract frameperiod; /* Frames, not fields */
+ __u32 framelines;
+ __u32 reserved[4];
+};
+
+static int get_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up)
+{
+ /* other fields are not set by the user, nor used by the driver */
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_standard32)) ||
+ get_user(kp->index, &up->index))
+ return -EFAULT;
+ return 0;
+}
+
+static int put_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up)
+{
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_standard32)) ||
+ put_user(kp->index, &up->index) ||
+ copy_to_user(up->id, &kp->id, sizeof(__u64)) ||
+ copy_to_user(up->name, kp->name, 24) ||
+ copy_to_user(&up->frameperiod, &kp->frameperiod, sizeof(kp->frameperiod)) ||
+ put_user(kp->framelines, &up->framelines) ||
+ copy_to_user(up->reserved, kp->reserved, 4 * sizeof(__u32)))
+ return -EFAULT;
+ return 0;
+}
+
+struct v4l2_plane32 {
+ __u32 bytesused;
+ __u32 length;
+ union {
+ __u32 mem_offset;
+ compat_long_t userptr;
+ } m;
+ __u32 data_offset;
+ __u32 reserved[11];
+};
+
+struct v4l2_buffer32 {
+ __u32 index;
+ enum v4l2_buf_type type;
+ __u32 bytesused;
+ __u32 flags;
+ enum v4l2_field field;
+ struct compat_timeval timestamp;
+ struct v4l2_timecode timecode;
+ __u32 sequence;
+
+ /* memory location */
+ enum v4l2_memory memory;
+ union {
+ __u32 offset;
+ compat_long_t userptr;
+ compat_caddr_t planes;
+ } m;
+ __u32 length;
+ __u32 input;
+ __u32 reserved;
+};
+
+static int get_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32,
+ enum v4l2_memory memory)
+{
+ void __user *up_pln;
+ compat_long_t p;
+
+ if (copy_in_user(up, up32, 2 * sizeof(__u32)) ||
+ copy_in_user(&up->data_offset, &up32->data_offset,
+ sizeof(__u32)))
+ return -EFAULT;
+
+ if (memory == V4L2_MEMORY_USERPTR) {
+ if (get_user(p, &up32->m.userptr))
+ return -EFAULT;
+ up_pln = compat_ptr(p);
+ if (put_user((unsigned long)up_pln, &up->m.userptr))
+ return -EFAULT;
+ } else {
+ if (copy_in_user(&up->m.mem_offset, &up32->m.mem_offset,
+ sizeof(__u32)))
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int put_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32,
+ enum v4l2_memory memory)
+{
+ if (copy_in_user(up32, up, 2 * sizeof(__u32)) ||
+ copy_in_user(&up32->data_offset, &up->data_offset,
+ sizeof(__u32)))
+ return -EFAULT;
+
+ /* For MMAP, driver might've set up the offset, so copy it back.
+ * USERPTR stays the same (was userspace-provided), so no copying. */
+ if (memory == V4L2_MEMORY_MMAP)
+ if (copy_in_user(&up32->m.mem_offset, &up->m.mem_offset,
+ sizeof(__u32)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user *up)
+{
+ struct v4l2_plane32 __user *uplane32;
+ struct v4l2_plane __user *uplane;
+ compat_caddr_t p;
+ int num_planes;
+ int ret;
+
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_buffer32)) ||
+ get_user(kp->index, &up->index) ||
+ get_user(kp->type, &up->type) ||
+ get_user(kp->flags, &up->flags) ||
+ get_user(kp->memory, &up->memory) ||
+ get_user(kp->input, &up->input))
+ return -EFAULT;
+
+ if (V4L2_TYPE_IS_OUTPUT(kp->type))
+ if (get_user(kp->bytesused, &up->bytesused) ||
+ get_user(kp->field, &up->field) ||
+ get_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
+ get_user(kp->timestamp.tv_usec,
+ &up->timestamp.tv_usec))
+ return -EFAULT;
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
+ if (get_user(kp->length, &up->length))
+ return -EFAULT;
+
+ num_planes = kp->length;
+ if (num_planes == 0) {
+ kp->m.planes = NULL;
+ /* num_planes == 0 is legal, e.g. when userspace doesn't
+ * need planes array on DQBUF*/
+ return 0;
+ }
+
+ if (get_user(p, &up->m.planes))
+ return -EFAULT;
+
+ uplane32 = compat_ptr(p);
+ if (!access_ok(VERIFY_READ, uplane32,
+ num_planes * sizeof(struct v4l2_plane32)))
+ return -EFAULT;
+
+ /* We don't really care if userspace decides to kill itself
+ * by passing a very big num_planes value */
+ uplane = compat_alloc_user_space(num_planes *
+ sizeof(struct v4l2_plane));
+ kp->m.planes = uplane;
+
+ while (--num_planes >= 0) {
+ ret = get_v4l2_plane32(uplane, uplane32, kp->memory);
+ if (ret)
+ return ret;
+ ++uplane;
+ ++uplane32;
+ }
+ } else {
+ switch (kp->memory) {
+ case V4L2_MEMORY_MMAP:
+ if (get_user(kp->length, &up->length) ||
+ get_user(kp->m.offset, &up->m.offset))
+ return -EFAULT;
+ break;
+ case V4L2_MEMORY_USERPTR:
+ {
+ compat_long_t tmp;
+
+ if (get_user(kp->length, &up->length) ||
+ get_user(tmp, &up->m.userptr))
+ return -EFAULT;
+
+ kp->m.userptr = (unsigned long)compat_ptr(tmp);
+ }
+ break;
+ case V4L2_MEMORY_OVERLAY:
+ if (get_user(kp->m.offset, &up->m.offset))
+ return -EFAULT;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user *up)
+{
+ struct v4l2_plane32 __user *uplane32;
+ struct v4l2_plane __user *uplane;
+ compat_caddr_t p;
+ int num_planes;
+ int ret;
+
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_buffer32)) ||
+ put_user(kp->index, &up->index) ||
+ put_user(kp->type, &up->type) ||
+ put_user(kp->flags, &up->flags) ||
+ put_user(kp->memory, &up->memory) ||
+ put_user(kp->input, &up->input))
+ return -EFAULT;
+
+ if (put_user(kp->bytesused, &up->bytesused) ||
+ put_user(kp->field, &up->field) ||
+ put_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
+ put_user(kp->timestamp.tv_usec, &up->timestamp.tv_usec) ||
+ copy_to_user(&up->timecode, &kp->timecode, sizeof(struct v4l2_timecode)) ||
+ put_user(kp->sequence, &up->sequence) ||
+ put_user(kp->reserved, &up->reserved))
+ return -EFAULT;
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
+ num_planes = kp->length;
+ if (num_planes == 0)
+ return 0;
+
+ uplane = kp->m.planes;
+ if (get_user(p, &up->m.planes))
+ return -EFAULT;
+ uplane32 = compat_ptr(p);
+
+ while (--num_planes >= 0) {
+ ret = put_v4l2_plane32(uplane, uplane32, kp->memory);
+ if (ret)
+ return ret;
+ ++uplane;
+ ++uplane32;
+ }
+ } else {
+ switch (kp->memory) {
+ case V4L2_MEMORY_MMAP:
+ if (put_user(kp->length, &up->length) ||
+ put_user(kp->m.offset, &up->m.offset))
+ return -EFAULT;
+ break;
+ case V4L2_MEMORY_USERPTR:
+ if (put_user(kp->length, &up->length) ||
+ put_user(kp->m.userptr, &up->m.userptr))
+ return -EFAULT;
+ break;
+ case V4L2_MEMORY_OVERLAY:
+ if (put_user(kp->m.offset, &up->m.offset))
+ return -EFAULT;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+struct v4l2_framebuffer32 {
+ __u32 capability;
+ __u32 flags;
+ compat_caddr_t base;
+ struct v4l2_pix_format fmt;
+};
+
+static int get_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_framebuffer32 __user *up)
+{
+ u32 tmp;
+
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_framebuffer32)) ||
+ get_user(tmp, &up->base) ||
+ get_user(kp->capability, &up->capability) ||
+ get_user(kp->flags, &up->flags))
+ return -EFAULT;
+ kp->base = compat_ptr(tmp);
+ get_v4l2_pix_format(&kp->fmt, &up->fmt);
+ return 0;
+}
+
+static int put_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_framebuffer32 __user *up)
+{
+ u32 tmp = (u32)((unsigned long)kp->base);
+
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_framebuffer32)) ||
+ put_user(tmp, &up->base) ||
+ put_user(kp->capability, &up->capability) ||
+ put_user(kp->flags, &up->flags))
+ return -EFAULT;
+ put_v4l2_pix_format(&kp->fmt, &up->fmt);
+ return 0;
+}
+
+struct v4l2_input32 {
+ __u32 index; /* Which input */
+ __u8 name[32]; /* Label */
+ __u32 type; /* Type of input */
+ __u32 audioset; /* Associated audios (bitfield) */
+ __u32 tuner; /* Associated tuner */
+ v4l2_std_id std;
+ __u32 status;
+ __u32 reserved[4];
+} __attribute__ ((packed));
+
+/* The 64-bit v4l2_input struct has extra padding at the end of the struct.
+ Otherwise it is identical to the 32-bit version. */
+static inline int get_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up)
+{
+ if (copy_from_user(kp, up, sizeof(struct v4l2_input32)))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int put_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up)
+{
+ if (copy_to_user(up, kp, sizeof(struct v4l2_input32)))
+ return -EFAULT;
+ return 0;
+}
+
+struct v4l2_ext_controls32 {
+ __u32 ctrl_class;
+ __u32 count;
+ __u32 error_idx;
+ __u32 reserved[2];
+ compat_caddr_t controls; /* actually struct v4l2_ext_control32 * */
+};
+
+struct v4l2_ext_control32 {
+ __u32 id;
+ __u32 size;
+ __u32 reserved2[1];
+ union {
+ __s32 value;
+ __s64 value64;
+ compat_caddr_t string; /* actually char * */
+ };
+} __attribute__ ((packed));
+
+/* The following function really belong in v4l2-common, but that causes
+ a circular dependency between modules. We need to think about this, but
+ for now this will do. */
+
+/* Return non-zero if this control is a pointer type. Currently only
+ type STRING is a pointer type. */
+static inline int ctrl_is_pointer(u32 id)
+{
+ switch (id) {
+ case V4L2_CID_RDS_TX_PS_NAME:
+ case V4L2_CID_RDS_TX_RADIO_TEXT:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up)
+{
+ struct v4l2_ext_control32 __user *ucontrols;
+ struct v4l2_ext_control __user *kcontrols;
+ int n;
+ compat_caddr_t p;
+
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_ext_controls32)) ||
+ get_user(kp->ctrl_class, &up->ctrl_class) ||
+ get_user(kp->count, &up->count) ||
+ get_user(kp->error_idx, &up->error_idx) ||
+ copy_from_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
+ return -EFAULT;
+ n = kp->count;
+ if (n == 0) {
+ kp->controls = NULL;
+ return 0;
+ }
+ if (get_user(p, &up->controls))
+ return -EFAULT;
+ ucontrols = compat_ptr(p);
+ if (!access_ok(VERIFY_READ, ucontrols,
+ n * sizeof(struct v4l2_ext_control32)))
+ return -EFAULT;
+ kcontrols = compat_alloc_user_space(n * sizeof(struct v4l2_ext_control));
+ kp->controls = kcontrols;
+ while (--n >= 0) {
+ if (copy_in_user(kcontrols, ucontrols, sizeof(*ucontrols)))
+ return -EFAULT;
+ if (ctrl_is_pointer(kcontrols->id)) {
+ void __user *s;
+
+ if (get_user(p, &ucontrols->string))
+ return -EFAULT;
+ s = compat_ptr(p);
+ if (put_user(s, &kcontrols->string))
+ return -EFAULT;
+ }
+ ucontrols++;
+ kcontrols++;
+ }
+ return 0;
+}
+
+static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up)
+{
+ struct v4l2_ext_control32 __user *ucontrols;
+ struct v4l2_ext_control __user *kcontrols = kp->controls;
+ int n = kp->count;
+ compat_caddr_t p;
+
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_ext_controls32)) ||
+ put_user(kp->ctrl_class, &up->ctrl_class) ||
+ put_user(kp->count, &up->count) ||
+ put_user(kp->error_idx, &up->error_idx) ||
+ copy_to_user(up->reserved, kp->reserved, sizeof(up->reserved)))
+ return -EFAULT;
+ if (!kp->count)
+ return 0;
+
+ if (get_user(p, &up->controls))
+ return -EFAULT;
+ ucontrols = compat_ptr(p);
+ if (!access_ok(VERIFY_WRITE, ucontrols,
+ n * sizeof(struct v4l2_ext_control32)))
+ return -EFAULT;
+
+ while (--n >= 0) {
+ unsigned size = sizeof(*ucontrols);
+
+ /* Do not modify the pointer when copying a pointer control.
+ The contents of the pointer was changed, not the pointer
+ itself. */
+ if (ctrl_is_pointer(kcontrols->id))
+ size -= sizeof(ucontrols->value64);
+ if (copy_in_user(ucontrols, kcontrols, size))
+ return -EFAULT;
+ ucontrols++;
+ kcontrols++;
+ }
+ return 0;
+}
+
+struct v4l2_event32 {
+ __u32 type;
+ union {
+ __u8 data[64];
+ } u;
+ __u32 pending;
+ __u32 sequence;
+ struct compat_timespec timestamp;
+ __u32 id;
+ __u32 reserved[8];
+};
+
+static int put_v4l2_event32(struct v4l2_event *kp, struct v4l2_event32 __user *up)
+{
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_event32)) ||
+ put_user(kp->type, &up->type) ||
+ copy_to_user(&up->u, &kp->u, sizeof(kp->u)) ||
+ put_user(kp->pending, &up->pending) ||
+ put_user(kp->sequence, &up->sequence) ||
+ put_compat_timespec(&kp->timestamp, &up->timestamp) ||
+ put_user(kp->id, &up->id) ||
+ copy_to_user(up->reserved, kp->reserved, 8 * sizeof(__u32)))
+ return -EFAULT;
+ return 0;
+}
+
+#define VIDIOC_G_FMT32 _IOWR('V', 4, struct v4l2_format32)
+#define VIDIOC_S_FMT32 _IOWR('V', 5, struct v4l2_format32)
+#define VIDIOC_QUERYBUF32 _IOWR('V', 9, struct v4l2_buffer32)
+#define VIDIOC_G_FBUF32 _IOR ('V', 10, struct v4l2_framebuffer32)
+#define VIDIOC_S_FBUF32 _IOW ('V', 11, struct v4l2_framebuffer32)
+#define VIDIOC_QBUF32 _IOWR('V', 15, struct v4l2_buffer32)
+#define VIDIOC_DQBUF32 _IOWR('V', 17, struct v4l2_buffer32)
+#define VIDIOC_ENUMSTD32 _IOWR('V', 25, struct v4l2_standard32)
+#define VIDIOC_ENUMINPUT32 _IOWR('V', 26, struct v4l2_input32)
+#define VIDIOC_TRY_FMT32 _IOWR('V', 64, struct v4l2_format32)
+#define VIDIOC_G_EXT_CTRLS32 _IOWR('V', 71, struct v4l2_ext_controls32)
+#define VIDIOC_S_EXT_CTRLS32 _IOWR('V', 72, struct v4l2_ext_controls32)
+#define VIDIOC_TRY_EXT_CTRLS32 _IOWR('V', 73, struct v4l2_ext_controls32)
+#define VIDIOC_DQEVENT32 _IOR ('V', 89, struct v4l2_event32)
+#define VIDIOC_CREATE_BUFS32 _IOWR('V', 92, struct v4l2_create_buffers32)
+#define VIDIOC_PREPARE_BUF32 _IOWR('V', 93, struct v4l2_buffer32)
+
+#define VIDIOC_OVERLAY32 _IOW ('V', 14, s32)
+#define VIDIOC_STREAMON32 _IOW ('V', 18, s32)
+#define VIDIOC_STREAMOFF32 _IOW ('V', 19, s32)
+#define VIDIOC_G_INPUT32 _IOR ('V', 38, s32)
+#define VIDIOC_S_INPUT32 _IOWR('V', 39, s32)
+#define VIDIOC_G_OUTPUT32 _IOR ('V', 46, s32)
+#define VIDIOC_S_OUTPUT32 _IOWR('V', 47, s32)
+
+static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ union {
+ struct v4l2_format v2f;
+ struct v4l2_buffer v2b;
+ struct v4l2_framebuffer v2fb;
+ struct v4l2_input v2i;
+ struct v4l2_standard v2s;
+ struct v4l2_ext_controls v2ecs;
+ struct v4l2_event v2ev;
+ struct v4l2_create_buffers v2crt;
+ unsigned long vx;
+ int vi;
+ } karg;
+ void __user *up = compat_ptr(arg);
+ int compatible_arg = 1;
+ long err = 0;
+
+ /* First, convert the command. */
+ switch (cmd) {
+ case VIDIOC_G_FMT32: cmd = VIDIOC_G_FMT; break;
+ case VIDIOC_S_FMT32: cmd = VIDIOC_S_FMT; break;
+ case VIDIOC_QUERYBUF32: cmd = VIDIOC_QUERYBUF; break;
+ case VIDIOC_G_FBUF32: cmd = VIDIOC_G_FBUF; break;
+ case VIDIOC_S_FBUF32: cmd = VIDIOC_S_FBUF; break;
+ case VIDIOC_QBUF32: cmd = VIDIOC_QBUF; break;
+ case VIDIOC_DQBUF32: cmd = VIDIOC_DQBUF; break;
+ case VIDIOC_ENUMSTD32: cmd = VIDIOC_ENUMSTD; break;
+ case VIDIOC_ENUMINPUT32: cmd = VIDIOC_ENUMINPUT; break;
+ case VIDIOC_TRY_FMT32: cmd = VIDIOC_TRY_FMT; break;
+ case VIDIOC_G_EXT_CTRLS32: cmd = VIDIOC_G_EXT_CTRLS; break;
+ case VIDIOC_S_EXT_CTRLS32: cmd = VIDIOC_S_EXT_CTRLS; break;
+ case VIDIOC_TRY_EXT_CTRLS32: cmd = VIDIOC_TRY_EXT_CTRLS; break;
+ case VIDIOC_DQEVENT32: cmd = VIDIOC_DQEVENT; break;
+ case VIDIOC_OVERLAY32: cmd = VIDIOC_OVERLAY; break;
+ case VIDIOC_STREAMON32: cmd = VIDIOC_STREAMON; break;
+ case VIDIOC_STREAMOFF32: cmd = VIDIOC_STREAMOFF; break;
+ case VIDIOC_G_INPUT32: cmd = VIDIOC_G_INPUT; break;
+ case VIDIOC_S_INPUT32: cmd = VIDIOC_S_INPUT; break;
+ case VIDIOC_G_OUTPUT32: cmd = VIDIOC_G_OUTPUT; break;
+ case VIDIOC_S_OUTPUT32: cmd = VIDIOC_S_OUTPUT; break;
+ case VIDIOC_CREATE_BUFS32: cmd = VIDIOC_CREATE_BUFS; break;
+ case VIDIOC_PREPARE_BUF32: cmd = VIDIOC_PREPARE_BUF; break;
+ }
+
+ switch (cmd) {
+ case VIDIOC_OVERLAY:
+ case VIDIOC_STREAMON:
+ case VIDIOC_STREAMOFF:
+ case VIDIOC_S_INPUT:
+ case VIDIOC_S_OUTPUT:
+ err = get_user(karg.vi, (s32 __user *)up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_G_INPUT:
+ case VIDIOC_G_OUTPUT:
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_G_FMT:
+ case VIDIOC_S_FMT:
+ case VIDIOC_TRY_FMT:
+ err = get_v4l2_format32(&karg.v2f, up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_CREATE_BUFS:
+ err = get_v4l2_create32(&karg.v2crt, up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_PREPARE_BUF:
+ case VIDIOC_QUERYBUF:
+ case VIDIOC_QBUF:
+ case VIDIOC_DQBUF:
+ err = get_v4l2_buffer32(&karg.v2b, up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_S_FBUF:
+ err = get_v4l2_framebuffer32(&karg.v2fb, up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_G_FBUF:
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_ENUMSTD:
+ err = get_v4l2_standard32(&karg.v2s, up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_ENUMINPUT:
+ err = get_v4l2_input32(&karg.v2i, up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_G_EXT_CTRLS:
+ case VIDIOC_S_EXT_CTRLS:
+ case VIDIOC_TRY_EXT_CTRLS:
+ err = get_v4l2_ext_controls32(&karg.v2ecs, up);
+ compatible_arg = 0;
+ break;
+ case VIDIOC_DQEVENT:
+ compatible_arg = 0;
+ break;
+ }
+ if (err)
+ return err;
+
+ if (compatible_arg)
+ err = native_ioctl(file, cmd, (unsigned long)up);
+ else {
+ mm_segment_t old_fs = get_fs();
+
+ set_fs(KERNEL_DS);
+ err = native_ioctl(file, cmd, (unsigned long)&karg);
+ set_fs(old_fs);
+ }
+
+ /* Special case: even after an error we need to put the
+ results back for these ioctls since the error_idx will
+ contain information on which control failed. */
+ switch (cmd) {
+ case VIDIOC_G_EXT_CTRLS:
+ case VIDIOC_S_EXT_CTRLS:
+ case VIDIOC_TRY_EXT_CTRLS:
+ if (put_v4l2_ext_controls32(&karg.v2ecs, up))
+ err = -EFAULT;
+ break;
+ }
+ if (err)
+ return err;
+
+ switch (cmd) {
+ case VIDIOC_S_INPUT:
+ case VIDIOC_S_OUTPUT:
+ case VIDIOC_G_INPUT:
+ case VIDIOC_G_OUTPUT:
+ err = put_user(((s32)karg.vi), (s32 __user *)up);
+ break;
+
+ case VIDIOC_G_FBUF:
+ err = put_v4l2_framebuffer32(&karg.v2fb, up);
+ break;
+
+ case VIDIOC_DQEVENT:
+ err = put_v4l2_event32(&karg.v2ev, up);
+ break;
+
+ case VIDIOC_G_FMT:
+ case VIDIOC_S_FMT:
+ case VIDIOC_TRY_FMT:
+ err = put_v4l2_format32(&karg.v2f, up);
+ break;
+
+ case VIDIOC_CREATE_BUFS:
+ err = put_v4l2_create32(&karg.v2crt, up);
+ break;
+
+ case VIDIOC_QUERYBUF:
+ case VIDIOC_QBUF:
+ case VIDIOC_DQBUF:
+ err = put_v4l2_buffer32(&karg.v2b, up);
+ break;
+
+ case VIDIOC_ENUMSTD:
+ err = put_v4l2_standard32(&karg.v2s, up);
+ break;
+
+ case VIDIOC_ENUMINPUT:
+ err = put_v4l2_input32(&karg.v2i, up);
+ break;
+ }
+ return err;
+}
+
+long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ long ret = -ENOIOCTLCMD;
+
+ if (!file->f_op->unlocked_ioctl)
+ return ret;
+
+ switch (cmd) {
+ case VIDIOC_QUERYCAP:
+ case VIDIOC_RESERVED:
+ case VIDIOC_ENUM_FMT:
+ case VIDIOC_G_FMT32:
+ case VIDIOC_S_FMT32:
+ case VIDIOC_REQBUFS:
+ case VIDIOC_QUERYBUF32:
+ case VIDIOC_G_FBUF32:
+ case VIDIOC_S_FBUF32:
+ case VIDIOC_OVERLAY32:
+ case VIDIOC_QBUF32:
+ case VIDIOC_DQBUF32:
+ case VIDIOC_STREAMON32:
+ case VIDIOC_STREAMOFF32:
+ case VIDIOC_G_PARM:
+ case VIDIOC_S_PARM:
+ case VIDIOC_G_STD:
+ case VIDIOC_S_STD:
+ case VIDIOC_ENUMSTD32:
+ case VIDIOC_ENUMINPUT32:
+ case VIDIOC_G_CTRL:
+ case VIDIOC_S_CTRL:
+ case VIDIOC_G_TUNER:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_G_AUDIO:
+ case VIDIOC_S_AUDIO:
+ case VIDIOC_QUERYCTRL:
+ case VIDIOC_QUERYMENU:
+ case VIDIOC_G_INPUT32:
+ case VIDIOC_S_INPUT32:
+ case VIDIOC_G_OUTPUT32:
+ case VIDIOC_S_OUTPUT32:
+ case VIDIOC_ENUMOUTPUT:
+ case VIDIOC_G_AUDOUT:
+ case VIDIOC_S_AUDOUT:
+ case VIDIOC_G_MODULATOR:
+ case VIDIOC_S_MODULATOR:
+ case VIDIOC_S_FREQUENCY:
+ case VIDIOC_G_FREQUENCY:
+ case VIDIOC_CROPCAP:
+ case VIDIOC_G_CROP:
+ case VIDIOC_S_CROP:
+ case VIDIOC_G_SELECTION:
+ case VIDIOC_S_SELECTION:
+ case VIDIOC_G_JPEGCOMP:
+ case VIDIOC_S_JPEGCOMP:
+ case VIDIOC_QUERYSTD:
+ case VIDIOC_TRY_FMT32:
+ case VIDIOC_ENUMAUDIO:
+ case VIDIOC_ENUMAUDOUT:
+ case VIDIOC_G_PRIORITY:
+ case VIDIOC_S_PRIORITY:
+ case VIDIOC_G_SLICED_VBI_CAP:
+ case VIDIOC_LOG_STATUS:
+ case VIDIOC_G_EXT_CTRLS32:
+ case VIDIOC_S_EXT_CTRLS32:
+ case VIDIOC_TRY_EXT_CTRLS32:
+ case VIDIOC_ENUM_FRAMESIZES:
+ case VIDIOC_ENUM_FRAMEINTERVALS:
+ case VIDIOC_G_ENC_INDEX:
+ case VIDIOC_ENCODER_CMD:
+ case VIDIOC_TRY_ENCODER_CMD:
+ case VIDIOC_DECODER_CMD:
+ case VIDIOC_TRY_DECODER_CMD:
+ case VIDIOC_DBG_S_REGISTER:
+ case VIDIOC_DBG_G_REGISTER:
+ case VIDIOC_DBG_G_CHIP_IDENT:
+ case VIDIOC_S_HW_FREQ_SEEK:
+ case VIDIOC_ENUM_DV_PRESETS:
+ case VIDIOC_S_DV_PRESET:
+ case VIDIOC_G_DV_PRESET:
+ case VIDIOC_QUERY_DV_PRESET:
+ case VIDIOC_S_DV_TIMINGS:
+ case VIDIOC_G_DV_TIMINGS:
+ case VIDIOC_DQEVENT:
+ case VIDIOC_DQEVENT32:
+ case VIDIOC_SUBSCRIBE_EVENT:
+ case VIDIOC_UNSUBSCRIBE_EVENT:
+ case VIDIOC_CREATE_BUFS32:
+ case VIDIOC_PREPARE_BUF32:
+ ret = do_video_ioctl(file, cmd, arg);
+ break;
+
+ default:
+ if (vdev->fops->compat_ioctl32)
+ ret = vdev->fops->compat_ioctl32(file, cmd, arg);
+
+ if (ret == -ENOIOCTLCMD)
+ printk(KERN_WARNING "compat_ioctl32: "
+ "unknown ioctl '%c', dir=%d, #%d (0x%08x)\n",
+ _IOC_TYPE(cmd), _IOC_DIR(cmd), _IOC_NR(cmd),
+ cmd);
+ break;
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_compat_ioctl32);
diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
new file mode 100644
index 00000000..18015c0a
--- /dev/null
+++ b/drivers/media/video/v4l2-ctrls.c
@@ -0,0 +1,2442 @@
+/*
+ V4L2 controls framework implementation.
+
+ Copyright (C) 2010 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-dev.h>
+
+#define has_op(master, op) \
+ (master->ops && master->ops->op)
+#define call_op(master, op) \
+ (has_op(master, op) ? master->ops->op(master) : 0)
+
+/* Internal temporary helper struct, one for each v4l2_ext_control */
+struct v4l2_ctrl_helper {
+ /* Pointer to the control reference of the master control */
+ struct v4l2_ctrl_ref *mref;
+ /* The control corresponding to the v4l2_ext_control ID field. */
+ struct v4l2_ctrl *ctrl;
+ /* v4l2_ext_control index of the next control belonging to the
+ same cluster, or 0 if there isn't any. */
+ u32 next;
+};
+
+/* Small helper function to determine if the autocluster is set to manual
+ mode. */
+static bool is_cur_manual(const struct v4l2_ctrl *master)
+{
+ return master->is_auto && master->cur.val == master->manual_mode_value;
+}
+
+/* Same as above, but this checks the against the new value instead of the
+ current value. */
+static bool is_new_manual(const struct v4l2_ctrl *master)
+{
+ return master->is_auto && master->val == master->manual_mode_value;
+}
+
+/* Returns NULL or a character pointer array containing the menu for
+ the given control ID. The pointer array ends with a NULL pointer.
+ An empty string signifies a menu entry that is invalid. This allows
+ drivers to disable certain options if it is not supported. */
+const char * const *v4l2_ctrl_get_menu(u32 id)
+{
+ static const char * const mpeg_audio_sampling_freq[] = {
+ "44.1 kHz",
+ "48 kHz",
+ "32 kHz",
+ NULL
+ };
+ static const char * const mpeg_audio_encoding[] = {
+ "MPEG-1/2 Layer I",
+ "MPEG-1/2 Layer II",
+ "MPEG-1/2 Layer III",
+ "MPEG-2/4 AAC",
+ "AC-3",
+ NULL
+ };
+ static const char * const mpeg_audio_l1_bitrate[] = {
+ "32 kbps",
+ "64 kbps",
+ "96 kbps",
+ "128 kbps",
+ "160 kbps",
+ "192 kbps",
+ "224 kbps",
+ "256 kbps",
+ "288 kbps",
+ "320 kbps",
+ "352 kbps",
+ "384 kbps",
+ "416 kbps",
+ "448 kbps",
+ NULL
+ };
+ static const char * const mpeg_audio_l2_bitrate[] = {
+ "32 kbps",
+ "48 kbps",
+ "56 kbps",
+ "64 kbps",
+ "80 kbps",
+ "96 kbps",
+ "112 kbps",
+ "128 kbps",
+ "160 kbps",
+ "192 kbps",
+ "224 kbps",
+ "256 kbps",
+ "320 kbps",
+ "384 kbps",
+ NULL
+ };
+ static const char * const mpeg_audio_l3_bitrate[] = {
+ "32 kbps",
+ "40 kbps",
+ "48 kbps",
+ "56 kbps",
+ "64 kbps",
+ "80 kbps",
+ "96 kbps",
+ "112 kbps",
+ "128 kbps",
+ "160 kbps",
+ "192 kbps",
+ "224 kbps",
+ "256 kbps",
+ "320 kbps",
+ NULL
+ };
+ static const char * const mpeg_audio_ac3_bitrate[] = {
+ "32 kbps",
+ "40 kbps",
+ "48 kbps",
+ "56 kbps",
+ "64 kbps",
+ "80 kbps",
+ "96 kbps",
+ "112 kbps",
+ "128 kbps",
+ "160 kbps",
+ "192 kbps",
+ "224 kbps",
+ "256 kbps",
+ "320 kbps",
+ "384 kbps",
+ "448 kbps",
+ "512 kbps",
+ "576 kbps",
+ "640 kbps",
+ NULL
+ };
+ static const char * const mpeg_audio_mode[] = {
+ "Stereo",
+ "Joint Stereo",
+ "Dual",
+ "Mono",
+ NULL
+ };
+ static const char * const mpeg_audio_mode_extension[] = {
+ "Bound 4",
+ "Bound 8",
+ "Bound 12",
+ "Bound 16",
+ NULL
+ };
+ static const char * const mpeg_audio_emphasis[] = {
+ "No Emphasis",
+ "50/15 us",
+ "CCITT J17",
+ NULL
+ };
+ static const char * const mpeg_audio_crc[] = {
+ "No CRC",
+ "16-bit CRC",
+ NULL
+ };
+ static const char * const mpeg_audio_dec_playback[] = {
+ "Auto",
+ "Stereo",
+ "Left",
+ "Right",
+ "Mono",
+ "Swapped Stereo",
+ NULL
+ };
+ static const char * const mpeg_video_encoding[] = {
+ "MPEG-1",
+ "MPEG-2",
+ "MPEG-4 AVC",
+ NULL
+ };
+ static const char * const mpeg_video_aspect[] = {
+ "1x1",
+ "4x3",
+ "16x9",
+ "2.21x1",
+ NULL
+ };
+ static const char * const mpeg_video_bitrate_mode[] = {
+ "Variable Bitrate",
+ "Constant Bitrate",
+ NULL
+ };
+ static const char * const mpeg_stream_type[] = {
+ "MPEG-2 Program Stream",
+ "MPEG-2 Transport Stream",
+ "MPEG-1 System Stream",
+ "MPEG-2 DVD-compatible Stream",
+ "MPEG-1 VCD-compatible Stream",
+ "MPEG-2 SVCD-compatible Stream",
+ NULL
+ };
+ static const char * const mpeg_stream_vbi_fmt[] = {
+ "No VBI",
+ "Private Packet, IVTV Format",
+ NULL
+ };
+ static const char * const camera_power_line_frequency[] = {
+ "Disabled",
+ "50 Hz",
+ "60 Hz",
+ "Auto",
+ NULL
+ };
+ static const char * const camera_exposure_auto[] = {
+ "Auto Mode",
+ "Manual Mode",
+ "Shutter Priority Mode",
+ "Aperture Priority Mode",
+ NULL
+ };
+ static const char * const colorfx[] = {
+ "None",
+ "Black & White",
+ "Sepia",
+ "Negative",
+ "Emboss",
+ "Sketch",
+ "Sky Blue",
+ "Grass Green",
+ "Skin Whiten",
+ "Vivid",
+ NULL
+ };
+ static const char * const tune_preemphasis[] = {
+ "No Preemphasis",
+ "50 Microseconds",
+ "75 Microseconds",
+ NULL,
+ };
+ static const char * const header_mode[] = {
+ "Separate Buffer",
+ "Joined With 1st Frame",
+ NULL,
+ };
+ static const char * const multi_slice[] = {
+ "Single",
+ "Max Macroblocks",
+ "Max Bytes",
+ NULL,
+ };
+ static const char * const entropy_mode[] = {
+ "CAVLC",
+ "CABAC",
+ NULL,
+ };
+ static const char * const mpeg_h264_level[] = {
+ "1",
+ "1b",
+ "1.1",
+ "1.2",
+ "1.3",
+ "2",
+ "2.1",
+ "2.2",
+ "3",
+ "3.1",
+ "3.2",
+ "4",
+ "4.1",
+ "4.2",
+ "5",
+ "5.1",
+ NULL,
+ };
+ static const char * const h264_loop_filter[] = {
+ "Enabled",
+ "Disabled",
+ "Disabled at Slice Boundary",
+ NULL,
+ };
+ static const char * const h264_profile[] = {
+ "Baseline",
+ "Constrained Baseline",
+ "Main",
+ "Extended",
+ "High",
+ "High 10",
+ "High 422",
+ "High 444 Predictive",
+ "High 10 Intra",
+ "High 422 Intra",
+ "High 444 Intra",
+ "CAVLC 444 Intra",
+ "Scalable Baseline",
+ "Scalable High",
+ "Scalable High Intra",
+ "Multiview High",
+ NULL,
+ };
+ static const char * const vui_sar_idc[] = {
+ "Unspecified",
+ "1:1",
+ "12:11",
+ "10:11",
+ "16:11",
+ "40:33",
+ "24:11",
+ "20:11",
+ "32:11",
+ "80:33",
+ "18:11",
+ "15:11",
+ "64:33",
+ "160:99",
+ "4:3",
+ "3:2",
+ "2:1",
+ "Extended SAR",
+ NULL,
+ };
+ static const char * const mpeg_mpeg4_level[] = {
+ "0",
+ "0b",
+ "1",
+ "2",
+ "3",
+ "3b",
+ "4",
+ "5",
+ NULL,
+ };
+ static const char * const mpeg4_profile[] = {
+ "Simple",
+ "Advanced Simple",
+ "Core",
+ "Simple Scalable",
+ "Advanced Coding Efficency",
+ NULL,
+ };
+
+ static const char * const flash_led_mode[] = {
+ "Off",
+ "Flash",
+ "Torch",
+ NULL,
+ };
+ static const char * const flash_strobe_source[] = {
+ "Software",
+ "External",
+ NULL,
+ };
+
+ static const char * const jpeg_chroma_subsampling[] = {
+ "4:4:4",
+ "4:2:2",
+ "4:2:0",
+ "4:1:1",
+ "4:1:0",
+ "Gray",
+ NULL,
+ };
+
+ switch (id) {
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+ return mpeg_audio_sampling_freq;
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ return mpeg_audio_encoding;
+ case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
+ return mpeg_audio_l1_bitrate;
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+ return mpeg_audio_l2_bitrate;
+ case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
+ return mpeg_audio_l3_bitrate;
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+ return mpeg_audio_ac3_bitrate;
+ case V4L2_CID_MPEG_AUDIO_MODE:
+ return mpeg_audio_mode;
+ case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
+ return mpeg_audio_mode_extension;
+ case V4L2_CID_MPEG_AUDIO_EMPHASIS:
+ return mpeg_audio_emphasis;
+ case V4L2_CID_MPEG_AUDIO_CRC:
+ return mpeg_audio_crc;
+ case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK:
+ case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK:
+ return mpeg_audio_dec_playback;
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ return mpeg_video_encoding;
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ return mpeg_video_aspect;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ return mpeg_video_bitrate_mode;
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ return mpeg_stream_type;
+ case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ return mpeg_stream_vbi_fmt;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ return camera_power_line_frequency;
+ case V4L2_CID_EXPOSURE_AUTO:
+ return camera_exposure_auto;
+ case V4L2_CID_COLORFX:
+ return colorfx;
+ case V4L2_CID_TUNE_PREEMPHASIS:
+ return tune_preemphasis;
+ case V4L2_CID_FLASH_LED_MODE:
+ return flash_led_mode;
+ case V4L2_CID_FLASH_STROBE_SOURCE:
+ return flash_strobe_source;
+ case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
+ return header_mode;
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
+ return multi_slice;
+ case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
+ return entropy_mode;
+ case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+ return mpeg_h264_level;
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
+ return h264_loop_filter;
+ case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+ return h264_profile;
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:
+ return vui_sar_idc;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
+ return mpeg_mpeg4_level;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
+ return mpeg4_profile;
+ case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
+ return jpeg_chroma_subsampling;
+
+ default:
+ return NULL;
+ }
+}
+EXPORT_SYMBOL(v4l2_ctrl_get_menu);
+
+/* Return the control name. */
+const char *v4l2_ctrl_get_name(u32 id)
+{
+ switch (id) {
+ /* USER controls */
+ /* Keep the order of the 'case's the same as in videodev2.h! */
+ case V4L2_CID_USER_CLASS: return "User Controls";
+ case V4L2_CID_BRIGHTNESS: return "Brightness";
+ case V4L2_CID_CONTRAST: return "Contrast";
+ case V4L2_CID_SATURATION: return "Saturation";
+ case V4L2_CID_HUE: return "Hue";
+ case V4L2_CID_AUDIO_VOLUME: return "Volume";
+ case V4L2_CID_AUDIO_BALANCE: return "Balance";
+ case V4L2_CID_AUDIO_BASS: return "Bass";
+ case V4L2_CID_AUDIO_TREBLE: return "Treble";
+ case V4L2_CID_AUDIO_MUTE: return "Mute";
+ case V4L2_CID_AUDIO_LOUDNESS: return "Loudness";
+ case V4L2_CID_BLACK_LEVEL: return "Black Level";
+ case V4L2_CID_AUTO_WHITE_BALANCE: return "White Balance, Automatic";
+ case V4L2_CID_DO_WHITE_BALANCE: return "Do White Balance";
+ case V4L2_CID_RED_BALANCE: return "Red Balance";
+ case V4L2_CID_BLUE_BALANCE: return "Blue Balance";
+ case V4L2_CID_GAMMA: return "Gamma";
+ case V4L2_CID_EXPOSURE: return "Exposure";
+ case V4L2_CID_AUTOGAIN: return "Gain, Automatic";
+ case V4L2_CID_GAIN: return "Gain";
+ case V4L2_CID_HFLIP: return "Horizontal Flip";
+ case V4L2_CID_VFLIP: return "Vertical Flip";
+ case V4L2_CID_HCENTER: return "Horizontal Center";
+ case V4L2_CID_VCENTER: return "Vertical Center";
+ case V4L2_CID_POWER_LINE_FREQUENCY: return "Power Line Frequency";
+ case V4L2_CID_HUE_AUTO: return "Hue, Automatic";
+ case V4L2_CID_WHITE_BALANCE_TEMPERATURE: return "White Balance Temperature";
+ case V4L2_CID_SHARPNESS: return "Sharpness";
+ case V4L2_CID_BACKLIGHT_COMPENSATION: return "Backlight Compensation";
+ case V4L2_CID_CHROMA_AGC: return "Chroma AGC";
+ case V4L2_CID_COLOR_KILLER: return "Color Killer";
+ case V4L2_CID_COLORFX: return "Color Effects";
+ case V4L2_CID_AUTOBRIGHTNESS: return "Brightness, Automatic";
+ case V4L2_CID_BAND_STOP_FILTER: return "Band-Stop Filter";
+ case V4L2_CID_ROTATE: return "Rotate";
+ case V4L2_CID_BG_COLOR: return "Background Color";
+ case V4L2_CID_CHROMA_GAIN: return "Chroma Gain";
+ case V4L2_CID_ILLUMINATORS_1: return "Illuminator 1";
+ case V4L2_CID_ILLUMINATORS_2: return "Illuminator 2";
+ case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: return "Min Number of Capture Buffers";
+ case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT: return "Min Number of Output Buffers";
+ case V4L2_CID_ALPHA_COMPONENT: return "Alpha Component";
+
+ /* MPEG controls */
+ /* Keep the order of the 'case's the same as in videodev2.h! */
+ case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls";
+ case V4L2_CID_MPEG_STREAM_TYPE: return "Stream Type";
+ case V4L2_CID_MPEG_STREAM_PID_PMT: return "Stream PMT Program ID";
+ case V4L2_CID_MPEG_STREAM_PID_AUDIO: return "Stream Audio Program ID";
+ case V4L2_CID_MPEG_STREAM_PID_VIDEO: return "Stream Video Program ID";
+ case V4L2_CID_MPEG_STREAM_PID_PCR: return "Stream PCR Program ID";
+ case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO: return "Stream PES Audio ID";
+ case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: return "Stream PES Video ID";
+ case V4L2_CID_MPEG_STREAM_VBI_FMT: return "Stream VBI Format";
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: return "Audio Sampling Frequency";
+ case V4L2_CID_MPEG_AUDIO_ENCODING: return "Audio Encoding";
+ case V4L2_CID_MPEG_AUDIO_L1_BITRATE: return "Audio Layer I Bitrate";
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE: return "Audio Layer II Bitrate";
+ case V4L2_CID_MPEG_AUDIO_L3_BITRATE: return "Audio Layer III Bitrate";
+ case V4L2_CID_MPEG_AUDIO_MODE: return "Audio Stereo Mode";
+ case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: return "Audio Stereo Mode Extension";
+ case V4L2_CID_MPEG_AUDIO_EMPHASIS: return "Audio Emphasis";
+ case V4L2_CID_MPEG_AUDIO_CRC: return "Audio CRC";
+ case V4L2_CID_MPEG_AUDIO_MUTE: return "Audio Mute";
+ case V4L2_CID_MPEG_AUDIO_AAC_BITRATE: return "Audio AAC Bitrate";
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: return "Audio AC-3 Bitrate";
+ case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK: return "Audio Playback";
+ case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK: return "Audio Multilingual Playback";
+ case V4L2_CID_MPEG_VIDEO_ENCODING: return "Video Encoding";
+ case V4L2_CID_MPEG_VIDEO_ASPECT: return "Video Aspect";
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES: return "Video B Frames";
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE: return "Video GOP Size";
+ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: return "Video GOP Closure";
+ case V4L2_CID_MPEG_VIDEO_PULLDOWN: return "Video Pulldown";
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: return "Video Bitrate Mode";
+ case V4L2_CID_MPEG_VIDEO_BITRATE: return "Video Bitrate";
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: return "Video Peak Bitrate";
+ case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: return "Video Temporal Decimation";
+ case V4L2_CID_MPEG_VIDEO_MUTE: return "Video Mute";
+ case V4L2_CID_MPEG_VIDEO_MUTE_YUV: return "Video Mute YUV";
+ case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE: return "Decoder Slice Interface";
+ case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER: return "MPEG4 Loop Filter Enable";
+ case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB: return "Number of Intra Refresh MBs";
+ case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: return "Frame Level Rate Control Enable";
+ case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: return "H264 MB Level Rate Control";
+ case V4L2_CID_MPEG_VIDEO_HEADER_MODE: return "Sequence Header Mode";
+ case V4L2_CID_MPEG_VIDEO_MAX_REF_PIC: return "Max Number of Reference Pics";
+ case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP: return "H263 I-Frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP: return "H263 P-Frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP: return "H263 B-Frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_H263_MIN_QP: return "H263 Minimum QP Value";
+ case V4L2_CID_MPEG_VIDEO_H263_MAX_QP: return "H263 Maximum QP Value";
+ case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: return "H264 I-Frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: return "H264 P-Frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP: return "H264 B-Frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: return "H264 Maximum QP Value";
+ case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: return "H264 Minimum QP Value";
+ case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM: return "H264 8x8 Transform Enable";
+ case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE: return "H264 CPB Buffer Size";
+ case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: return "H264 Entropy Mode";
+ case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD: return "H264 I-Frame Period";
+ case V4L2_CID_MPEG_VIDEO_H264_LEVEL: return "H264 Level";
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA: return "H264 Loop Filter Alpha Offset";
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA: return "H264 Loop Filter Beta Offset";
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: return "H264 Loop Filter Mode";
+ case V4L2_CID_MPEG_VIDEO_H264_PROFILE: return "H264 Profile";
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT: return "Vertical Size of SAR";
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH: return "Horizontal Size of SAR";
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE: return "Aspect Ratio VUI Enable";
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC: return "VUI Aspect Ratio IDC";
+ case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP: return "MPEG4 I-Frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP: return "MPEG4 P-Frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP: return "MPEG4 B-Frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP: return "MPEG4 Minimum QP Value";
+ case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP: return "MPEG4 Maximum QP Value";
+ case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: return "MPEG4 Level";
+ case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: return "MPEG4 Profile";
+ case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL: return "Quarter Pixel Search Enable";
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES: return "Maximum Bytes in a Slice";
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: return "Number of MBs in a Slice";
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: return "Slice Partitioning Method";
+ case V4L2_CID_MPEG_VIDEO_VBV_SIZE: return "VBV Buffer Size";
+ case V4L2_CID_MPEG_VIDEO_DEC_PTS: return "Video Decoder PTS";
+ case V4L2_CID_MPEG_VIDEO_DEC_FRAME: return "Video Decoder Frame Count";
+
+ /* CAMERA controls */
+ /* Keep the order of the 'case's the same as in videodev2.h! */
+ case V4L2_CID_CAMERA_CLASS: return "Camera Controls";
+ case V4L2_CID_EXPOSURE_AUTO: return "Auto Exposure";
+ case V4L2_CID_EXPOSURE_ABSOLUTE: return "Exposure Time, Absolute";
+ case V4L2_CID_EXPOSURE_AUTO_PRIORITY: return "Exposure, Dynamic Framerate";
+ case V4L2_CID_PAN_RELATIVE: return "Pan, Relative";
+ case V4L2_CID_TILT_RELATIVE: return "Tilt, Relative";
+ case V4L2_CID_PAN_RESET: return "Pan, Reset";
+ case V4L2_CID_TILT_RESET: return "Tilt, Reset";
+ case V4L2_CID_PAN_ABSOLUTE: return "Pan, Absolute";
+ case V4L2_CID_TILT_ABSOLUTE: return "Tilt, Absolute";
+ case V4L2_CID_FOCUS_ABSOLUTE: return "Focus, Absolute";
+ case V4L2_CID_FOCUS_RELATIVE: return "Focus, Relative";
+ case V4L2_CID_FOCUS_AUTO: return "Focus, Automatic";
+ case V4L2_CID_ZOOM_ABSOLUTE: return "Zoom, Absolute";
+ case V4L2_CID_ZOOM_RELATIVE: return "Zoom, Relative";
+ case V4L2_CID_ZOOM_CONTINUOUS: return "Zoom, Continuous";
+ case V4L2_CID_PRIVACY: return "Privacy";
+ case V4L2_CID_IRIS_ABSOLUTE: return "Iris, Absolute";
+ case V4L2_CID_IRIS_RELATIVE: return "Iris, Relative";
+
+ /* FM Radio Modulator control */
+ /* Keep the order of the 'case's the same as in videodev2.h! */
+ case V4L2_CID_FM_TX_CLASS: return "FM Radio Modulator Controls";
+ case V4L2_CID_RDS_TX_DEVIATION: return "RDS Signal Deviation";
+ case V4L2_CID_RDS_TX_PI: return "RDS Program ID";
+ case V4L2_CID_RDS_TX_PTY: return "RDS Program Type";
+ case V4L2_CID_RDS_TX_PS_NAME: return "RDS PS Name";
+ case V4L2_CID_RDS_TX_RADIO_TEXT: return "RDS Radio Text";
+ case V4L2_CID_AUDIO_LIMITER_ENABLED: return "Audio Limiter Feature Enabled";
+ case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: return "Audio Limiter Release Time";
+ case V4L2_CID_AUDIO_LIMITER_DEVIATION: return "Audio Limiter Deviation";
+ case V4L2_CID_AUDIO_COMPRESSION_ENABLED: return "Audio Compression Enabled";
+ case V4L2_CID_AUDIO_COMPRESSION_GAIN: return "Audio Compression Gain";
+ case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: return "Audio Compression Threshold";
+ case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: return "Audio Compression Attack Time";
+ case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: return "Audio Compression Release Time";
+ case V4L2_CID_PILOT_TONE_ENABLED: return "Pilot Tone Feature Enabled";
+ case V4L2_CID_PILOT_TONE_DEVIATION: return "Pilot Tone Deviation";
+ case V4L2_CID_PILOT_TONE_FREQUENCY: return "Pilot Tone Frequency";
+ case V4L2_CID_TUNE_PREEMPHASIS: return "Pre-Emphasis";
+ case V4L2_CID_TUNE_POWER_LEVEL: return "Tune Power Level";
+ case V4L2_CID_TUNE_ANTENNA_CAPACITOR: return "Tune Antenna Capacitor";
+
+ /* Flash controls */
+ case V4L2_CID_FLASH_CLASS: return "Flash Controls";
+ case V4L2_CID_FLASH_LED_MODE: return "LED Mode";
+ case V4L2_CID_FLASH_STROBE_SOURCE: return "Strobe Source";
+ case V4L2_CID_FLASH_STROBE: return "Strobe";
+ case V4L2_CID_FLASH_STROBE_STOP: return "Stop Strobe";
+ case V4L2_CID_FLASH_STROBE_STATUS: return "Strobe Status";
+ case V4L2_CID_FLASH_TIMEOUT: return "Strobe Timeout";
+ case V4L2_CID_FLASH_INTENSITY: return "Intensity, Flash Mode";
+ case V4L2_CID_FLASH_TORCH_INTENSITY: return "Intensity, Torch Mode";
+ case V4L2_CID_FLASH_INDICATOR_INTENSITY: return "Intensity, Indicator";
+ case V4L2_CID_FLASH_FAULT: return "Faults";
+ case V4L2_CID_FLASH_CHARGE: return "Charge";
+ case V4L2_CID_FLASH_READY: return "Ready to Strobe";
+
+ /* JPEG encoder controls */
+ /* Keep the order of the 'case's the same as in videodev2.h! */
+ case V4L2_CID_JPEG_CLASS: return "JPEG Compression Controls";
+ case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: return "Chroma Subsampling";
+ case V4L2_CID_JPEG_RESTART_INTERVAL: return "Restart Interval";
+ case V4L2_CID_JPEG_COMPRESSION_QUALITY: return "Compression Quality";
+ case V4L2_CID_JPEG_ACTIVE_MARKER: return "Active Markers";
+
+ default:
+ return NULL;
+ }
+}
+EXPORT_SYMBOL(v4l2_ctrl_get_name);
+
+void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
+ s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags)
+{
+ *name = v4l2_ctrl_get_name(id);
+ *flags = 0;
+
+ switch (id) {
+ case V4L2_CID_AUDIO_MUTE:
+ case V4L2_CID_AUDIO_LOUDNESS:
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ case V4L2_CID_AUTOGAIN:
+ case V4L2_CID_HFLIP:
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HUE_AUTO:
+ case V4L2_CID_CHROMA_AGC:
+ case V4L2_CID_COLOR_KILLER:
+ case V4L2_CID_MPEG_AUDIO_MUTE:
+ case V4L2_CID_MPEG_VIDEO_MUTE:
+ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+ case V4L2_CID_MPEG_VIDEO_PULLDOWN:
+ case V4L2_CID_EXPOSURE_AUTO_PRIORITY:
+ case V4L2_CID_FOCUS_AUTO:
+ case V4L2_CID_PRIVACY:
+ case V4L2_CID_AUDIO_LIMITER_ENABLED:
+ case V4L2_CID_AUDIO_COMPRESSION_ENABLED:
+ case V4L2_CID_PILOT_TONE_ENABLED:
+ case V4L2_CID_ILLUMINATORS_1:
+ case V4L2_CID_ILLUMINATORS_2:
+ case V4L2_CID_FLASH_STROBE_STATUS:
+ case V4L2_CID_FLASH_CHARGE:
+ case V4L2_CID_FLASH_READY:
+ case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER:
+ case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE:
+ case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:
+ case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE:
+ case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM:
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE:
+ case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL:
+ *type = V4L2_CTRL_TYPE_BOOLEAN;
+ *min = 0;
+ *max = *step = 1;
+ break;
+ case V4L2_CID_PAN_RESET:
+ case V4L2_CID_TILT_RESET:
+ case V4L2_CID_FLASH_STROBE:
+ case V4L2_CID_FLASH_STROBE_STOP:
+ *type = V4L2_CTRL_TYPE_BUTTON;
+ *flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
+ *min = *max = *step = *def = 0;
+ break;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+ case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+ case V4L2_CID_MPEG_AUDIO_MODE:
+ case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
+ case V4L2_CID_MPEG_AUDIO_EMPHASIS:
+ case V4L2_CID_MPEG_AUDIO_CRC:
+ case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK:
+ case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK:
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ case V4L2_CID_EXPOSURE_AUTO:
+ case V4L2_CID_COLORFX:
+ case V4L2_CID_TUNE_PREEMPHASIS:
+ case V4L2_CID_FLASH_LED_MODE:
+ case V4L2_CID_FLASH_STROBE_SOURCE:
+ case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
+ case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
+ case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
+ case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:
+ case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
+ case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
+ case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
+ *type = V4L2_CTRL_TYPE_MENU;
+ break;
+ case V4L2_CID_RDS_TX_PS_NAME:
+ case V4L2_CID_RDS_TX_RADIO_TEXT:
+ *type = V4L2_CTRL_TYPE_STRING;
+ break;
+ case V4L2_CID_USER_CLASS:
+ case V4L2_CID_CAMERA_CLASS:
+ case V4L2_CID_MPEG_CLASS:
+ case V4L2_CID_FM_TX_CLASS:
+ case V4L2_CID_FLASH_CLASS:
+ case V4L2_CID_JPEG_CLASS:
+ *type = V4L2_CTRL_TYPE_CTRL_CLASS;
+ /* You can neither read not write these */
+ *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY;
+ *min = *max = *step = *def = 0;
+ break;
+ case V4L2_CID_BG_COLOR:
+ *type = V4L2_CTRL_TYPE_INTEGER;
+ *step = 1;
+ *min = 0;
+ /* Max is calculated as RGB888 that is 2^24 */
+ *max = 0xFFFFFF;
+ break;
+ case V4L2_CID_FLASH_FAULT:
+ case V4L2_CID_JPEG_ACTIVE_MARKER:
+ *type = V4L2_CTRL_TYPE_BITMASK;
+ break;
+ case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
+ case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT:
+ *type = V4L2_CTRL_TYPE_INTEGER;
+ *flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ break;
+ case V4L2_CID_MPEG_VIDEO_DEC_FRAME:
+ case V4L2_CID_MPEG_VIDEO_DEC_PTS:
+ *type = V4L2_CTRL_TYPE_INTEGER64;
+ *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE;
+ break;
+ default:
+ *type = V4L2_CTRL_TYPE_INTEGER;
+ break;
+ }
+ switch (id) {
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ case V4L2_CID_MPEG_AUDIO_MODE:
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ *flags |= V4L2_CTRL_FLAG_UPDATE;
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ case V4L2_CID_BRIGHTNESS:
+ case V4L2_CID_CONTRAST:
+ case V4L2_CID_SATURATION:
+ case V4L2_CID_HUE:
+ case V4L2_CID_RED_BALANCE:
+ case V4L2_CID_BLUE_BALANCE:
+ case V4L2_CID_GAMMA:
+ case V4L2_CID_SHARPNESS:
+ case V4L2_CID_CHROMA_GAIN:
+ case V4L2_CID_RDS_TX_DEVIATION:
+ case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME:
+ case V4L2_CID_AUDIO_LIMITER_DEVIATION:
+ case V4L2_CID_AUDIO_COMPRESSION_GAIN:
+ case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD:
+ case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME:
+ case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME:
+ case V4L2_CID_PILOT_TONE_DEVIATION:
+ case V4L2_CID_PILOT_TONE_FREQUENCY:
+ case V4L2_CID_TUNE_POWER_LEVEL:
+ case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
+ *flags |= V4L2_CTRL_FLAG_SLIDER;
+ break;
+ case V4L2_CID_PAN_RELATIVE:
+ case V4L2_CID_TILT_RELATIVE:
+ case V4L2_CID_FOCUS_RELATIVE:
+ case V4L2_CID_IRIS_RELATIVE:
+ case V4L2_CID_ZOOM_RELATIVE:
+ *flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
+ break;
+ case V4L2_CID_FLASH_STROBE_STATUS:
+ case V4L2_CID_FLASH_READY:
+ *flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ break;
+ }
+}
+EXPORT_SYMBOL(v4l2_ctrl_fill);
+
+/* Helper function to determine whether the control type is compatible with
+ VIDIOC_G/S_CTRL. */
+static bool type_is_int(const struct v4l2_ctrl *ctrl)
+{
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_INTEGER64:
+ case V4L2_CTRL_TYPE_STRING:
+ /* Nope, these need v4l2_ext_control */
+ return false;
+ default:
+ return true;
+ }
+}
+
+static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 changes)
+{
+ memset(ev->reserved, 0, sizeof(ev->reserved));
+ ev->type = V4L2_EVENT_CTRL;
+ ev->id = ctrl->id;
+ ev->u.ctrl.changes = changes;
+ ev->u.ctrl.type = ctrl->type;
+ ev->u.ctrl.flags = ctrl->flags;
+ if (ctrl->type == V4L2_CTRL_TYPE_STRING)
+ ev->u.ctrl.value64 = 0;
+ else
+ ev->u.ctrl.value64 = ctrl->cur.val64;
+ ev->u.ctrl.minimum = ctrl->minimum;
+ ev->u.ctrl.maximum = ctrl->maximum;
+ if (ctrl->type == V4L2_CTRL_TYPE_MENU)
+ ev->u.ctrl.step = 1;
+ else
+ ev->u.ctrl.step = ctrl->step;
+ ev->u.ctrl.default_value = ctrl->default_value;
+}
+
+static void send_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 changes)
+{
+ struct v4l2_event ev;
+ struct v4l2_subscribed_event *sev;
+
+ if (list_empty(&ctrl->ev_subs))
+ return;
+ fill_event(&ev, ctrl, changes);
+
+ list_for_each_entry(sev, &ctrl->ev_subs, node)
+ if (sev->fh != fh ||
+ (sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK))
+ v4l2_event_queue_fh(sev->fh, &ev);
+}
+
+/* Helper function: copy the current control value back to the caller */
+static int cur_to_user(struct v4l2_ext_control *c,
+ struct v4l2_ctrl *ctrl)
+{
+ u32 len;
+
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_STRING:
+ len = strlen(ctrl->cur.string);
+ if (c->size < len + 1) {
+ c->size = len + 1;
+ return -ENOSPC;
+ }
+ return copy_to_user(c->string, ctrl->cur.string,
+ len + 1) ? -EFAULT : 0;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ c->value64 = ctrl->cur.val64;
+ break;
+ default:
+ c->value = ctrl->cur.val;
+ break;
+ }
+ return 0;
+}
+
+/* Helper function: copy the caller-provider value as the new control value */
+static int user_to_new(struct v4l2_ext_control *c,
+ struct v4l2_ctrl *ctrl)
+{
+ int ret;
+ u32 size;
+
+ ctrl->is_new = 1;
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_INTEGER64:
+ ctrl->val64 = c->value64;
+ break;
+ case V4L2_CTRL_TYPE_STRING:
+ size = c->size;
+ if (size == 0)
+ return -ERANGE;
+ if (size > ctrl->maximum + 1)
+ size = ctrl->maximum + 1;
+ ret = copy_from_user(ctrl->string, c->string, size);
+ if (!ret) {
+ char last = ctrl->string[size - 1];
+
+ ctrl->string[size - 1] = 0;
+ /* If the string was longer than ctrl->maximum,
+ then return an error. */
+ if (strlen(ctrl->string) == ctrl->maximum && last)
+ return -ERANGE;
+ }
+ return ret ? -EFAULT : 0;
+ default:
+ ctrl->val = c->value;
+ break;
+ }
+ return 0;
+}
+
+/* Helper function: copy the new control value back to the caller */
+static int new_to_user(struct v4l2_ext_control *c,
+ struct v4l2_ctrl *ctrl)
+{
+ u32 len;
+
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_STRING:
+ len = strlen(ctrl->string);
+ if (c->size < len + 1) {
+ c->size = ctrl->maximum + 1;
+ return -ENOSPC;
+ }
+ return copy_to_user(c->string, ctrl->string,
+ len + 1) ? -EFAULT : 0;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ c->value64 = ctrl->val64;
+ break;
+ default:
+ c->value = ctrl->val;
+ break;
+ }
+ return 0;
+}
+
+/* Copy the new value to the current value. */
+static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl,
+ bool update_inactive)
+{
+ bool changed = false;
+
+ if (ctrl == NULL)
+ return;
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_BUTTON:
+ changed = true;
+ break;
+ case V4L2_CTRL_TYPE_STRING:
+ /* strings are always 0-terminated */
+ changed = strcmp(ctrl->string, ctrl->cur.string);
+ strcpy(ctrl->cur.string, ctrl->string);
+ break;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ changed = ctrl->val64 != ctrl->cur.val64;
+ ctrl->cur.val64 = ctrl->val64;
+ break;
+ default:
+ changed = ctrl->val != ctrl->cur.val;
+ ctrl->cur.val = ctrl->val;
+ break;
+ }
+ if (update_inactive) {
+ /* Note: update_inactive can only be true for auto clusters. */
+ ctrl->flags &=
+ ~(V4L2_CTRL_FLAG_INACTIVE | V4L2_CTRL_FLAG_VOLATILE);
+ if (!is_cur_manual(ctrl->cluster[0])) {
+ ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ if (ctrl->cluster[0]->has_volatiles)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ }
+ fh = NULL;
+ }
+ if (changed || update_inactive) {
+ /* If a control was changed that was not one of the controls
+ modified by the application, then send the event to all. */
+ if (!ctrl->is_new)
+ fh = NULL;
+ send_event(fh, ctrl,
+ (changed ? V4L2_EVENT_CTRL_CH_VALUE : 0) |
+ (update_inactive ? V4L2_EVENT_CTRL_CH_FLAGS : 0));
+ }
+}
+
+/* Copy the current value to the new value */
+static void cur_to_new(struct v4l2_ctrl *ctrl)
+{
+ if (ctrl == NULL)
+ return;
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_STRING:
+ /* strings are always 0-terminated */
+ strcpy(ctrl->string, ctrl->cur.string);
+ break;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ ctrl->val64 = ctrl->cur.val64;
+ break;
+ default:
+ ctrl->val = ctrl->cur.val;
+ break;
+ }
+}
+
+/* Return non-zero if one or more of the controls in the cluster has a new
+ value that differs from the current value. */
+static int cluster_changed(struct v4l2_ctrl *master)
+{
+ int diff = 0;
+ int i;
+
+ for (i = 0; !diff && i < master->ncontrols; i++) {
+ struct v4l2_ctrl *ctrl = master->cluster[i];
+
+ if (ctrl == NULL)
+ continue;
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_BUTTON:
+ /* Button controls are always 'different' */
+ return 1;
+ case V4L2_CTRL_TYPE_STRING:
+ /* strings are always 0-terminated */
+ diff = strcmp(ctrl->string, ctrl->cur.string);
+ break;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ diff = ctrl->val64 != ctrl->cur.val64;
+ break;
+ default:
+ diff = ctrl->val != ctrl->cur.val;
+ break;
+ }
+ }
+ return diff;
+}
+
+/* Validate integer-type control */
+static int validate_new_int(const struct v4l2_ctrl *ctrl, s32 *pval)
+{
+ s32 val = *pval;
+ u32 offset;
+
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_INTEGER:
+ /* Round towards the closest legal value */
+ val += ctrl->step / 2;
+ if (val < ctrl->minimum)
+ val = ctrl->minimum;
+ if (val > ctrl->maximum)
+ val = ctrl->maximum;
+ offset = val - ctrl->minimum;
+ offset = ctrl->step * (offset / ctrl->step);
+ val = ctrl->minimum + offset;
+ *pval = val;
+ return 0;
+
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ *pval = !!val;
+ return 0;
+
+ case V4L2_CTRL_TYPE_MENU:
+ if (val < ctrl->minimum || val > ctrl->maximum)
+ return -ERANGE;
+ if (ctrl->qmenu[val][0] == '\0' ||
+ (ctrl->menu_skip_mask & (1 << val)))
+ return -EINVAL;
+ return 0;
+
+ case V4L2_CTRL_TYPE_BITMASK:
+ *pval &= ctrl->maximum;
+ return 0;
+
+ case V4L2_CTRL_TYPE_BUTTON:
+ case V4L2_CTRL_TYPE_CTRL_CLASS:
+ *pval = 0;
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+/* Validate a new control */
+static int validate_new(const struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c)
+{
+ char *s = c->string;
+ size_t len;
+
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_INTEGER:
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ case V4L2_CTRL_TYPE_MENU:
+ case V4L2_CTRL_TYPE_BITMASK:
+ case V4L2_CTRL_TYPE_BUTTON:
+ case V4L2_CTRL_TYPE_CTRL_CLASS:
+ return validate_new_int(ctrl, &c->value);
+
+ case V4L2_CTRL_TYPE_INTEGER64:
+ return 0;
+
+ case V4L2_CTRL_TYPE_STRING:
+ len = strlen(s);
+ if (len < ctrl->minimum)
+ return -ERANGE;
+ if ((len - ctrl->minimum) % ctrl->step)
+ return -ERANGE;
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static inline u32 node2id(struct list_head *node)
+{
+ return list_entry(node, struct v4l2_ctrl_ref, node)->ctrl->id;
+}
+
+/* Set the handler's error code if it wasn't set earlier already */
+static inline int handler_set_err(struct v4l2_ctrl_handler *hdl, int err)
+{
+ if (hdl->error == 0)
+ hdl->error = err;
+ return err;
+}
+
+/* Initialize the handler */
+int v4l2_ctrl_handler_init(struct v4l2_ctrl_handler *hdl,
+ unsigned nr_of_controls_hint)
+{
+ mutex_init(&hdl->lock);
+ INIT_LIST_HEAD(&hdl->ctrls);
+ INIT_LIST_HEAD(&hdl->ctrl_refs);
+ hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8;
+ hdl->buckets = kcalloc(hdl->nr_of_buckets, sizeof(hdl->buckets[0]),
+ GFP_KERNEL);
+ hdl->error = hdl->buckets ? 0 : -ENOMEM;
+ return hdl->error;
+}
+EXPORT_SYMBOL(v4l2_ctrl_handler_init);
+
+/* Free all controls and control refs */
+void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
+{
+ struct v4l2_ctrl_ref *ref, *next_ref;
+ struct v4l2_ctrl *ctrl, *next_ctrl;
+ struct v4l2_subscribed_event *sev, *next_sev;
+
+ if (hdl == NULL || hdl->buckets == NULL)
+ return;
+
+ mutex_lock(&hdl->lock);
+ /* Free all nodes */
+ list_for_each_entry_safe(ref, next_ref, &hdl->ctrl_refs, node) {
+ list_del(&ref->node);
+ kfree(ref);
+ }
+ /* Free all controls owned by the handler */
+ list_for_each_entry_safe(ctrl, next_ctrl, &hdl->ctrls, node) {
+ list_del(&ctrl->node);
+ list_for_each_entry_safe(sev, next_sev, &ctrl->ev_subs, node)
+ list_del(&sev->node);
+ kfree(ctrl);
+ }
+ kfree(hdl->buckets);
+ hdl->buckets = NULL;
+ hdl->cached = NULL;
+ hdl->error = 0;
+ mutex_unlock(&hdl->lock);
+}
+EXPORT_SYMBOL(v4l2_ctrl_handler_free);
+
+/* For backwards compatibility: V4L2_CID_PRIVATE_BASE should no longer
+ be used except in G_CTRL, S_CTRL, QUERYCTRL and QUERYMENU when dealing
+ with applications that do not use the NEXT_CTRL flag.
+
+ We just find the n-th private user control. It's O(N), but that should not
+ be an issue in this particular case. */
+static struct v4l2_ctrl_ref *find_private_ref(
+ struct v4l2_ctrl_handler *hdl, u32 id)
+{
+ struct v4l2_ctrl_ref *ref;
+
+ id -= V4L2_CID_PRIVATE_BASE;
+ list_for_each_entry(ref, &hdl->ctrl_refs, node) {
+ /* Search for private user controls that are compatible with
+ VIDIOC_G/S_CTRL. */
+ if (V4L2_CTRL_ID2CLASS(ref->ctrl->id) == V4L2_CTRL_CLASS_USER &&
+ V4L2_CTRL_DRIVER_PRIV(ref->ctrl->id)) {
+ if (!type_is_int(ref->ctrl))
+ continue;
+ if (id == 0)
+ return ref;
+ id--;
+ }
+ }
+ return NULL;
+}
+
+/* Find a control with the given ID. */
+static struct v4l2_ctrl_ref *find_ref(struct v4l2_ctrl_handler *hdl, u32 id)
+{
+ struct v4l2_ctrl_ref *ref;
+ int bucket;
+
+ id &= V4L2_CTRL_ID_MASK;
+
+ /* Old-style private controls need special handling */
+ if (id >= V4L2_CID_PRIVATE_BASE)
+ return find_private_ref(hdl, id);
+ bucket = id % hdl->nr_of_buckets;
+
+ /* Simple optimization: cache the last control found */
+ if (hdl->cached && hdl->cached->ctrl->id == id)
+ return hdl->cached;
+
+ /* Not in cache, search the hash */
+ ref = hdl->buckets ? hdl->buckets[bucket] : NULL;
+ while (ref && ref->ctrl->id != id)
+ ref = ref->next;
+
+ if (ref)
+ hdl->cached = ref; /* cache it! */
+ return ref;
+}
+
+/* Find a control with the given ID. Take the handler's lock first. */
+static struct v4l2_ctrl_ref *find_ref_lock(
+ struct v4l2_ctrl_handler *hdl, u32 id)
+{
+ struct v4l2_ctrl_ref *ref = NULL;
+
+ if (hdl) {
+ mutex_lock(&hdl->lock);
+ ref = find_ref(hdl, id);
+ mutex_unlock(&hdl->lock);
+ }
+ return ref;
+}
+
+/* Find a control with the given ID. */
+struct v4l2_ctrl *v4l2_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id)
+{
+ struct v4l2_ctrl_ref *ref = find_ref_lock(hdl, id);
+
+ return ref ? ref->ctrl : NULL;
+}
+EXPORT_SYMBOL(v4l2_ctrl_find);
+
+/* Allocate a new v4l2_ctrl_ref and hook it into the handler. */
+static int handler_new_ref(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_ctrl_ref *ref;
+ struct v4l2_ctrl_ref *new_ref;
+ u32 id = ctrl->id;
+ u32 class_ctrl = V4L2_CTRL_ID2CLASS(id) | 1;
+ int bucket = id % hdl->nr_of_buckets; /* which bucket to use */
+
+ /* Automatically add the control class if it is not yet present. */
+ if (id != class_ctrl && find_ref_lock(hdl, class_ctrl) == NULL)
+ if (!v4l2_ctrl_new_std(hdl, NULL, class_ctrl, 0, 0, 0, 0))
+ return hdl->error;
+
+ if (hdl->error)
+ return hdl->error;
+
+ new_ref = kzalloc(sizeof(*new_ref), GFP_KERNEL);
+ if (!new_ref)
+ return handler_set_err(hdl, -ENOMEM);
+ new_ref->ctrl = ctrl;
+ if (ctrl->handler == hdl) {
+ /* By default each control starts in a cluster of its own.
+ new_ref->ctrl is basically a cluster array with one
+ element, so that's perfect to use as the cluster pointer.
+ But only do this for the handler that owns the control. */
+ ctrl->cluster = &new_ref->ctrl;
+ ctrl->ncontrols = 1;
+ }
+
+ INIT_LIST_HEAD(&new_ref->node);
+
+ mutex_lock(&hdl->lock);
+
+ /* Add immediately at the end of the list if the list is empty, or if
+ the last element in the list has a lower ID.
+ This ensures that when elements are added in ascending order the
+ insertion is an O(1) operation. */
+ if (list_empty(&hdl->ctrl_refs) || id > node2id(hdl->ctrl_refs.prev)) {
+ list_add_tail(&new_ref->node, &hdl->ctrl_refs);
+ goto insert_in_hash;
+ }
+
+ /* Find insert position in sorted list */
+ list_for_each_entry(ref, &hdl->ctrl_refs, node) {
+ if (ref->ctrl->id < id)
+ continue;
+ /* Don't add duplicates */
+ if (ref->ctrl->id == id) {
+ kfree(new_ref);
+ goto unlock;
+ }
+ list_add(&new_ref->node, ref->node.prev);
+ break;
+ }
+
+insert_in_hash:
+ /* Insert the control node in the hash */
+ new_ref->next = hdl->buckets[bucket];
+ hdl->buckets[bucket] = new_ref;
+
+unlock:
+ mutex_unlock(&hdl->lock);
+ return 0;
+}
+
+/* Add a new control */
+static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_ops *ops,
+ u32 id, const char *name, enum v4l2_ctrl_type type,
+ s32 min, s32 max, u32 step, s32 def,
+ u32 flags, const char * const *qmenu, void *priv)
+{
+ struct v4l2_ctrl *ctrl;
+ unsigned sz_extra = 0;
+
+ if (hdl->error)
+ return NULL;
+
+ /* Sanity checks */
+ if (id == 0 || name == NULL || id >= V4L2_CID_PRIVATE_BASE ||
+ (type == V4L2_CTRL_TYPE_INTEGER && step == 0) ||
+ (type == V4L2_CTRL_TYPE_BITMASK && max == 0) ||
+ (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) ||
+ (type == V4L2_CTRL_TYPE_STRING && max == 0)) {
+ handler_set_err(hdl, -ERANGE);
+ return NULL;
+ }
+ if (type != V4L2_CTRL_TYPE_BITMASK && max < min) {
+ handler_set_err(hdl, -ERANGE);
+ return NULL;
+ }
+ if ((type == V4L2_CTRL_TYPE_INTEGER ||
+ type == V4L2_CTRL_TYPE_MENU ||
+ type == V4L2_CTRL_TYPE_BOOLEAN) &&
+ (def < min || def > max)) {
+ handler_set_err(hdl, -ERANGE);
+ return NULL;
+ }
+ if (type == V4L2_CTRL_TYPE_BITMASK && ((def & ~max) || min || step)) {
+ handler_set_err(hdl, -ERANGE);
+ return NULL;
+ }
+
+ if (type == V4L2_CTRL_TYPE_BUTTON)
+ flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
+ else if (type == V4L2_CTRL_TYPE_CTRL_CLASS)
+ flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ else if (type == V4L2_CTRL_TYPE_STRING)
+ sz_extra += 2 * (max + 1);
+
+ ctrl = kzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
+ if (ctrl == NULL) {
+ handler_set_err(hdl, -ENOMEM);
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&ctrl->node);
+ INIT_LIST_HEAD(&ctrl->ev_subs);
+ ctrl->handler = hdl;
+ ctrl->ops = ops;
+ ctrl->id = id;
+ ctrl->name = name;
+ ctrl->type = type;
+ ctrl->flags = flags;
+ ctrl->minimum = min;
+ ctrl->maximum = max;
+ ctrl->step = step;
+ ctrl->qmenu = qmenu;
+ ctrl->priv = priv;
+ ctrl->cur.val = ctrl->val = ctrl->default_value = def;
+
+ if (ctrl->type == V4L2_CTRL_TYPE_STRING) {
+ ctrl->cur.string = (char *)&ctrl[1] + sz_extra - (max + 1);
+ ctrl->string = (char *)&ctrl[1] + sz_extra - 2 * (max + 1);
+ if (ctrl->minimum)
+ memset(ctrl->cur.string, ' ', ctrl->minimum);
+ }
+ if (handler_new_ref(hdl, ctrl)) {
+ kfree(ctrl);
+ return NULL;
+ }
+ mutex_lock(&hdl->lock);
+ list_add_tail(&ctrl->node, &hdl->ctrls);
+ mutex_unlock(&hdl->lock);
+ return ctrl;
+}
+
+struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_config *cfg, void *priv)
+{
+ bool is_menu;
+ struct v4l2_ctrl *ctrl;
+ const char *name = cfg->name;
+ const char * const *qmenu = cfg->qmenu;
+ enum v4l2_ctrl_type type = cfg->type;
+ u32 flags = cfg->flags;
+ s32 min = cfg->min;
+ s32 max = cfg->max;
+ u32 step = cfg->step;
+ s32 def = cfg->def;
+
+ if (name == NULL)
+ v4l2_ctrl_fill(cfg->id, &name, &type, &min, &max, &step,
+ &def, &flags);
+
+ is_menu = (cfg->type == V4L2_CTRL_TYPE_MENU);
+ if (is_menu)
+ WARN_ON(step);
+ else
+ WARN_ON(cfg->menu_skip_mask);
+ if (is_menu && qmenu == NULL)
+ qmenu = v4l2_ctrl_get_menu(cfg->id);
+
+ ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->id, name,
+ type, min, max,
+ is_menu ? cfg->menu_skip_mask : step,
+ def, flags, qmenu, priv);
+ if (ctrl)
+ ctrl->is_private = cfg->is_private;
+ return ctrl;
+}
+EXPORT_SYMBOL(v4l2_ctrl_new_custom);
+
+/* Helper function for standard non-menu controls */
+struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_ops *ops,
+ u32 id, s32 min, s32 max, u32 step, s32 def)
+{
+ const char *name;
+ enum v4l2_ctrl_type type;
+ u32 flags;
+
+ v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
+ if (type == V4L2_CTRL_TYPE_MENU) {
+ handler_set_err(hdl, -EINVAL);
+ return NULL;
+ }
+ return v4l2_ctrl_new(hdl, ops, id, name, type,
+ min, max, step, def, flags, NULL, NULL);
+}
+EXPORT_SYMBOL(v4l2_ctrl_new_std);
+
+/* Helper function for standard menu controls */
+struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_ops *ops,
+ u32 id, s32 max, s32 mask, s32 def)
+{
+ const char * const *qmenu = v4l2_ctrl_get_menu(id);
+ const char *name;
+ enum v4l2_ctrl_type type;
+ s32 min;
+ s32 step;
+ u32 flags;
+
+ v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
+ if (type != V4L2_CTRL_TYPE_MENU) {
+ handler_set_err(hdl, -EINVAL);
+ return NULL;
+ }
+ return v4l2_ctrl_new(hdl, ops, id, name, type,
+ 0, max, mask, def, flags, qmenu, NULL);
+}
+EXPORT_SYMBOL(v4l2_ctrl_new_std_menu);
+
+/* Add a control from another handler to this handler */
+struct v4l2_ctrl *v4l2_ctrl_add_ctrl(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ctrl *ctrl)
+{
+ if (hdl == NULL || hdl->error)
+ return NULL;
+ if (ctrl == NULL) {
+ handler_set_err(hdl, -EINVAL);
+ return NULL;
+ }
+ if (ctrl->handler == hdl)
+ return ctrl;
+ return handler_new_ref(hdl, ctrl) ? NULL : ctrl;
+}
+EXPORT_SYMBOL(v4l2_ctrl_add_ctrl);
+
+/* Add the controls from another handler to our own. */
+int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ctrl_handler *add)
+{
+ struct v4l2_ctrl_ref *ref;
+ int ret = 0;
+
+ /* Do nothing if either handler is NULL or if they are the same */
+ if (!hdl || !add || hdl == add)
+ return 0;
+ if (hdl->error)
+ return hdl->error;
+ mutex_lock(&add->lock);
+ list_for_each_entry(ref, &add->ctrl_refs, node) {
+ struct v4l2_ctrl *ctrl = ref->ctrl;
+
+ /* Skip handler-private controls. */
+ if (ctrl->is_private)
+ continue;
+ /* And control classes */
+ if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
+ continue;
+ ret = handler_new_ref(hdl, ctrl);
+ if (ret)
+ break;
+ }
+ mutex_unlock(&add->lock);
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_ctrl_add_handler);
+
+/* Cluster controls */
+void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls)
+{
+ bool has_volatiles = false;
+ int i;
+
+ /* The first control is the master control and it must not be NULL */
+ BUG_ON(ncontrols == 0 || controls[0] == NULL);
+
+ for (i = 0; i < ncontrols; i++) {
+ if (controls[i]) {
+ controls[i]->cluster = controls;
+ controls[i]->ncontrols = ncontrols;
+ if (controls[i]->flags & V4L2_CTRL_FLAG_VOLATILE)
+ has_volatiles = true;
+ }
+ }
+ controls[0]->has_volatiles = has_volatiles;
+}
+EXPORT_SYMBOL(v4l2_ctrl_cluster);
+
+void v4l2_ctrl_auto_cluster(unsigned ncontrols, struct v4l2_ctrl **controls,
+ u8 manual_val, bool set_volatile)
+{
+ struct v4l2_ctrl *master = controls[0];
+ u32 flag = 0;
+ int i;
+
+ v4l2_ctrl_cluster(ncontrols, controls);
+ WARN_ON(ncontrols <= 1);
+ WARN_ON(manual_val < master->minimum || manual_val > master->maximum);
+ WARN_ON(set_volatile && !has_op(master, g_volatile_ctrl));
+ master->is_auto = true;
+ master->has_volatiles = set_volatile;
+ master->manual_mode_value = manual_val;
+ master->flags |= V4L2_CTRL_FLAG_UPDATE;
+
+ if (!is_cur_manual(master))
+ flag = V4L2_CTRL_FLAG_INACTIVE |
+ (set_volatile ? V4L2_CTRL_FLAG_VOLATILE : 0);
+
+ for (i = 1; i < ncontrols; i++)
+ if (controls[i])
+ controls[i]->flags |= flag;
+}
+EXPORT_SYMBOL(v4l2_ctrl_auto_cluster);
+
+/* Activate/deactivate a control. */
+void v4l2_ctrl_activate(struct v4l2_ctrl *ctrl, bool active)
+{
+ /* invert since the actual flag is called 'inactive' */
+ bool inactive = !active;
+ bool old;
+
+ if (ctrl == NULL)
+ return;
+
+ if (inactive)
+ /* set V4L2_CTRL_FLAG_INACTIVE */
+ old = test_and_set_bit(4, &ctrl->flags);
+ else
+ /* clear V4L2_CTRL_FLAG_INACTIVE */
+ old = test_and_clear_bit(4, &ctrl->flags);
+ if (old != inactive)
+ send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_FLAGS);
+}
+EXPORT_SYMBOL(v4l2_ctrl_activate);
+
+/* Grab/ungrab a control.
+ Typically used when streaming starts and you want to grab controls,
+ preventing the user from changing them.
+
+ Just call this and the framework will block any attempts to change
+ these controls. */
+void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed)
+{
+ bool old;
+
+ if (ctrl == NULL)
+ return;
+
+ v4l2_ctrl_lock(ctrl);
+ if (grabbed)
+ /* set V4L2_CTRL_FLAG_GRABBED */
+ old = test_and_set_bit(1, &ctrl->flags);
+ else
+ /* clear V4L2_CTRL_FLAG_GRABBED */
+ old = test_and_clear_bit(1, &ctrl->flags);
+ if (old != grabbed)
+ send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_FLAGS);
+ v4l2_ctrl_unlock(ctrl);
+}
+EXPORT_SYMBOL(v4l2_ctrl_grab);
+
+/* Log the control name and value */
+static void log_ctrl(const struct v4l2_ctrl *ctrl,
+ const char *prefix, const char *colon)
+{
+ if (ctrl->flags & (V4L2_CTRL_FLAG_DISABLED | V4L2_CTRL_FLAG_WRITE_ONLY))
+ return;
+ if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
+ return;
+
+ printk(KERN_INFO "%s%s%s: ", prefix, colon, ctrl->name);
+
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_INTEGER:
+ printk(KERN_CONT "%d", ctrl->cur.val);
+ break;
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ printk(KERN_CONT "%s", ctrl->cur.val ? "true" : "false");
+ break;
+ case V4L2_CTRL_TYPE_MENU:
+ printk(KERN_CONT "%s", ctrl->qmenu[ctrl->cur.val]);
+ break;
+ case V4L2_CTRL_TYPE_BITMASK:
+ printk(KERN_CONT "0x%08x", ctrl->cur.val);
+ break;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ printk(KERN_CONT "%lld", ctrl->cur.val64);
+ break;
+ case V4L2_CTRL_TYPE_STRING:
+ printk(KERN_CONT "%s", ctrl->cur.string);
+ break;
+ default:
+ printk(KERN_CONT "unknown type %d", ctrl->type);
+ break;
+ }
+ if (ctrl->flags & (V4L2_CTRL_FLAG_INACTIVE |
+ V4L2_CTRL_FLAG_GRABBED |
+ V4L2_CTRL_FLAG_VOLATILE)) {
+ if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
+ printk(KERN_CONT " inactive");
+ if (ctrl->flags & V4L2_CTRL_FLAG_GRABBED)
+ printk(KERN_CONT " grabbed");
+ if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE)
+ printk(KERN_CONT " volatile");
+ }
+ printk(KERN_CONT "\n");
+}
+
+/* Log all controls owned by the handler */
+void v4l2_ctrl_handler_log_status(struct v4l2_ctrl_handler *hdl,
+ const char *prefix)
+{
+ struct v4l2_ctrl *ctrl;
+ const char *colon = "";
+ int len;
+
+ if (hdl == NULL)
+ return;
+ if (prefix == NULL)
+ prefix = "";
+ len = strlen(prefix);
+ if (len && prefix[len - 1] != ' ')
+ colon = ": ";
+ mutex_lock(&hdl->lock);
+ list_for_each_entry(ctrl, &hdl->ctrls, node)
+ if (!(ctrl->flags & V4L2_CTRL_FLAG_DISABLED))
+ log_ctrl(ctrl, prefix, colon);
+ mutex_unlock(&hdl->lock);
+}
+EXPORT_SYMBOL(v4l2_ctrl_handler_log_status);
+
+/* Call s_ctrl for all controls owned by the handler */
+int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl)
+{
+ struct v4l2_ctrl *ctrl;
+ int ret = 0;
+
+ if (hdl == NULL)
+ return 0;
+ mutex_lock(&hdl->lock);
+ list_for_each_entry(ctrl, &hdl->ctrls, node)
+ ctrl->done = false;
+
+ list_for_each_entry(ctrl, &hdl->ctrls, node) {
+ struct v4l2_ctrl *master = ctrl->cluster[0];
+ int i;
+
+ /* Skip if this control was already handled by a cluster. */
+ /* Skip button controls and read-only controls. */
+ if (ctrl->done || ctrl->type == V4L2_CTRL_TYPE_BUTTON ||
+ (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY))
+ continue;
+
+ for (i = 0; i < master->ncontrols; i++) {
+ if (master->cluster[i]) {
+ cur_to_new(master->cluster[i]);
+ master->cluster[i]->is_new = 1;
+ master->cluster[i]->done = true;
+ }
+ }
+ ret = call_op(master, s_ctrl);
+ if (ret)
+ break;
+ }
+ mutex_unlock(&hdl->lock);
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_ctrl_handler_setup);
+
+/* Implement VIDIOC_QUERYCTRL */
+int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc)
+{
+ u32 id = qc->id & V4L2_CTRL_ID_MASK;
+ struct v4l2_ctrl_ref *ref;
+ struct v4l2_ctrl *ctrl;
+
+ if (hdl == NULL)
+ return -EINVAL;
+
+ mutex_lock(&hdl->lock);
+
+ /* Try to find it */
+ ref = find_ref(hdl, id);
+
+ if ((qc->id & V4L2_CTRL_FLAG_NEXT_CTRL) && !list_empty(&hdl->ctrl_refs)) {
+ /* Find the next control with ID > qc->id */
+
+ /* Did we reach the end of the control list? */
+ if (id >= node2id(hdl->ctrl_refs.prev)) {
+ ref = NULL; /* Yes, so there is no next control */
+ } else if (ref) {
+ /* We found a control with the given ID, so just get
+ the next one in the list. */
+ ref = list_entry(ref->node.next, typeof(*ref), node);
+ } else {
+ /* No control with the given ID exists, so start
+ searching for the next largest ID. We know there
+ is one, otherwise the first 'if' above would have
+ been true. */
+ list_for_each_entry(ref, &hdl->ctrl_refs, node)
+ if (id < ref->ctrl->id)
+ break;
+ }
+ }
+ mutex_unlock(&hdl->lock);
+ if (!ref)
+ return -EINVAL;
+
+ ctrl = ref->ctrl;
+ memset(qc, 0, sizeof(*qc));
+ if (id >= V4L2_CID_PRIVATE_BASE)
+ qc->id = id;
+ else
+ qc->id = ctrl->id;
+ strlcpy(qc->name, ctrl->name, sizeof(qc->name));
+ qc->minimum = ctrl->minimum;
+ qc->maximum = ctrl->maximum;
+ qc->default_value = ctrl->default_value;
+ if (ctrl->type == V4L2_CTRL_TYPE_MENU)
+ qc->step = 1;
+ else
+ qc->step = ctrl->step;
+ qc->flags = ctrl->flags;
+ qc->type = ctrl->type;
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_queryctrl);
+
+int v4l2_subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ if (qc->id & V4L2_CTRL_FLAG_NEXT_CTRL)
+ return -EINVAL;
+ return v4l2_queryctrl(sd->ctrl_handler, qc);
+}
+EXPORT_SYMBOL(v4l2_subdev_queryctrl);
+
+/* Implement VIDIOC_QUERYMENU */
+int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm)
+{
+ struct v4l2_ctrl *ctrl;
+ u32 i = qm->index;
+
+ ctrl = v4l2_ctrl_find(hdl, qm->id);
+ if (!ctrl)
+ return -EINVAL;
+
+ qm->reserved = 0;
+ /* Sanity checks */
+ if (ctrl->qmenu == NULL ||
+ i < ctrl->minimum || i > ctrl->maximum)
+ return -EINVAL;
+ /* Use mask to see if this menu item should be skipped */
+ if (ctrl->menu_skip_mask & (1 << i))
+ return -EINVAL;
+ /* Empty menu items should also be skipped */
+ if (ctrl->qmenu[i] == NULL || ctrl->qmenu[i][0] == '\0')
+ return -EINVAL;
+ strlcpy(qm->name, ctrl->qmenu[i], sizeof(qm->name));
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_querymenu);
+
+int v4l2_subdev_querymenu(struct v4l2_subdev *sd, struct v4l2_querymenu *qm)
+{
+ return v4l2_querymenu(sd->ctrl_handler, qm);
+}
+EXPORT_SYMBOL(v4l2_subdev_querymenu);
+
+
+
+/* Some general notes on the atomic requirements of VIDIOC_G/TRY/S_EXT_CTRLS:
+
+ It is not a fully atomic operation, just best-effort only. After all, if
+ multiple controls have to be set through multiple i2c writes (for example)
+ then some initial writes may succeed while others fail. Thus leaving the
+ system in an inconsistent state. The question is how much effort you are
+ willing to spend on trying to make something atomic that really isn't.
+
+ From the point of view of an application the main requirement is that
+ when you call VIDIOC_S_EXT_CTRLS and some values are invalid then an
+ error should be returned without actually affecting any controls.
+
+ If all the values are correct, then it is acceptable to just give up
+ in case of low-level errors.
+
+ It is important though that the application can tell when only a partial
+ configuration was done. The way we do that is through the error_idx field
+ of struct v4l2_ext_controls: if that is equal to the count field then no
+ controls were affected. Otherwise all controls before that index were
+ successful in performing their 'get' or 'set' operation, the control at
+ the given index failed, and you don't know what happened with the controls
+ after the failed one. Since if they were part of a control cluster they
+ could have been successfully processed (if a cluster member was encountered
+ at index < error_idx), they could have failed (if a cluster member was at
+ error_idx), or they may not have been processed yet (if the first cluster
+ member appeared after error_idx).
+
+ It is all fairly theoretical, though. In practice all you can do is to
+ bail out. If error_idx == count, then it is an application bug. If
+ error_idx < count then it is only an application bug if the error code was
+ EBUSY. That usually means that something started streaming just when you
+ tried to set the controls. In all other cases it is a driver/hardware
+ problem and all you can do is to retry or bail out.
+
+ Note that these rules do not apply to VIDIOC_TRY_EXT_CTRLS: since that
+ never modifies controls the error_idx is just set to whatever control
+ has an invalid value.
+ */
+
+/* Prepare for the extended g/s/try functions.
+ Find the controls in the control array and do some basic checks. */
+static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ext_controls *cs,
+ struct v4l2_ctrl_helper *helpers)
+{
+ struct v4l2_ctrl_helper *h;
+ bool have_clusters = false;
+ u32 i;
+
+ for (i = 0, h = helpers; i < cs->count; i++, h++) {
+ struct v4l2_ext_control *c = &cs->controls[i];
+ struct v4l2_ctrl_ref *ref;
+ struct v4l2_ctrl *ctrl;
+ u32 id = c->id & V4L2_CTRL_ID_MASK;
+
+ cs->error_idx = i;
+
+ if (cs->ctrl_class && V4L2_CTRL_ID2CLASS(id) != cs->ctrl_class)
+ return -EINVAL;
+
+ /* Old-style private controls are not allowed for
+ extended controls */
+ if (id >= V4L2_CID_PRIVATE_BASE)
+ return -EINVAL;
+ ref = find_ref_lock(hdl, id);
+ if (ref == NULL)
+ return -EINVAL;
+ ctrl = ref->ctrl;
+ if (ctrl->flags & V4L2_CTRL_FLAG_DISABLED)
+ return -EINVAL;
+
+ if (ctrl->cluster[0]->ncontrols > 1)
+ have_clusters = true;
+ if (ctrl->cluster[0] != ctrl)
+ ref = find_ref_lock(hdl, ctrl->cluster[0]->id);
+ /* Store the ref to the master control of the cluster */
+ h->mref = ref;
+ h->ctrl = ctrl;
+ /* Initially set next to 0, meaning that there is no other
+ control in this helper array belonging to the same
+ cluster */
+ h->next = 0;
+ }
+
+ /* We are done if there were no controls that belong to a multi-
+ control cluster. */
+ if (!have_clusters)
+ return 0;
+
+ /* The code below figures out in O(n) time which controls in the list
+ belong to the same cluster. */
+
+ /* This has to be done with the handler lock taken. */
+ mutex_lock(&hdl->lock);
+
+ /* First zero the helper field in the master control references */
+ for (i = 0; i < cs->count; i++)
+ helpers[i].mref->helper = 0;
+ for (i = 0, h = helpers; i < cs->count; i++, h++) {
+ struct v4l2_ctrl_ref *mref = h->mref;
+
+ /* If the mref->helper is set, then it points to an earlier
+ helper that belongs to the same cluster. */
+ if (mref->helper) {
+ /* Set the next field of mref->helper to the current
+ index: this means that that earlier helper now
+ points to the next helper in the same cluster. */
+ mref->helper->next = i;
+ /* mref should be set only for the first helper in the
+ cluster, clear the others. */
+ h->mref = NULL;
+ }
+ /* Point the mref helper to the current helper struct. */
+ mref->helper = h;
+ }
+ mutex_unlock(&hdl->lock);
+ return 0;
+}
+
+/* Handles the corner case where cs->count == 0. It checks whether the
+ specified control class exists. If that class ID is 0, then it checks
+ whether there are any controls at all. */
+static int class_check(struct v4l2_ctrl_handler *hdl, u32 ctrl_class)
+{
+ if (ctrl_class == 0)
+ return list_empty(&hdl->ctrl_refs) ? -EINVAL : 0;
+ return find_ref_lock(hdl, ctrl_class | 1) ? 0 : -EINVAL;
+}
+
+
+
+/* Get extended controls. Allocates the helpers array if needed. */
+int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
+{
+ struct v4l2_ctrl_helper helper[4];
+ struct v4l2_ctrl_helper *helpers = helper;
+ int ret;
+ int i, j;
+
+ cs->error_idx = cs->count;
+ cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class);
+
+ if (hdl == NULL)
+ return -EINVAL;
+
+ if (cs->count == 0)
+ return class_check(hdl, cs->ctrl_class);
+
+ if (cs->count > ARRAY_SIZE(helper)) {
+ helpers = kmalloc(sizeof(helper[0]) * cs->count, GFP_KERNEL);
+ if (helpers == NULL)
+ return -ENOMEM;
+ }
+
+ ret = prepare_ext_ctrls(hdl, cs, helpers);
+ cs->error_idx = cs->count;
+
+ for (i = 0; !ret && i < cs->count; i++)
+ if (helpers[i].ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
+ ret = -EACCES;
+
+ for (i = 0; !ret && i < cs->count; i++) {
+ int (*ctrl_to_user)(struct v4l2_ext_control *c,
+ struct v4l2_ctrl *ctrl) = cur_to_user;
+ struct v4l2_ctrl *master;
+
+ if (helpers[i].mref == NULL)
+ continue;
+
+ master = helpers[i].mref->ctrl;
+ cs->error_idx = i;
+
+ v4l2_ctrl_lock(master);
+
+ /* g_volatile_ctrl will update the new control values */
+ if ((master->flags & V4L2_CTRL_FLAG_VOLATILE) ||
+ (master->has_volatiles && !is_cur_manual(master))) {
+ for (j = 0; j < master->ncontrols; j++)
+ cur_to_new(master->cluster[j]);
+ ret = call_op(master, g_volatile_ctrl);
+ ctrl_to_user = new_to_user;
+ }
+ /* If OK, then copy the current (for non-volatile controls)
+ or the new (for volatile controls) control values to the
+ caller */
+ if (!ret) {
+ u32 idx = i;
+
+ do {
+ ret = ctrl_to_user(cs->controls + idx,
+ helpers[idx].ctrl);
+ idx = helpers[idx].next;
+ } while (!ret && idx);
+ }
+ v4l2_ctrl_unlock(master);
+ }
+
+ if (cs->count > ARRAY_SIZE(helper))
+ kfree(helpers);
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_g_ext_ctrls);
+
+int v4l2_subdev_g_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
+{
+ return v4l2_g_ext_ctrls(sd->ctrl_handler, cs);
+}
+EXPORT_SYMBOL(v4l2_subdev_g_ext_ctrls);
+
+/* Helper function to get a single control */
+static int get_ctrl(struct v4l2_ctrl *ctrl, s32 *val)
+{
+ struct v4l2_ctrl *master = ctrl->cluster[0];
+ int ret = 0;
+ int i;
+
+ if (ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
+ return -EACCES;
+
+ v4l2_ctrl_lock(master);
+ /* g_volatile_ctrl will update the current control values */
+ if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE) {
+ for (i = 0; i < master->ncontrols; i++)
+ cur_to_new(master->cluster[i]);
+ ret = call_op(master, g_volatile_ctrl);
+ *val = ctrl->val;
+ } else {
+ *val = ctrl->cur.val;
+ }
+ v4l2_ctrl_unlock(master);
+ return ret;
+}
+
+int v4l2_g_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *control)
+{
+ struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, control->id);
+
+ if (ctrl == NULL || !type_is_int(ctrl))
+ return -EINVAL;
+ return get_ctrl(ctrl, &control->value);
+}
+EXPORT_SYMBOL(v4l2_g_ctrl);
+
+int v4l2_subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control)
+{
+ return v4l2_g_ctrl(sd->ctrl_handler, control);
+}
+EXPORT_SYMBOL(v4l2_subdev_g_ctrl);
+
+s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl)
+{
+ s32 val = 0;
+
+ /* It's a driver bug if this happens. */
+ WARN_ON(!type_is_int(ctrl));
+ get_ctrl(ctrl, &val);
+ return val;
+}
+EXPORT_SYMBOL(v4l2_ctrl_g_ctrl);
+
+
+/* Core function that calls try/s_ctrl and ensures that the new value is
+ copied to the current value on a set.
+ Must be called with ctrl->handler->lock held. */
+static int try_or_set_cluster(struct v4l2_fh *fh,
+ struct v4l2_ctrl *master, bool set)
+{
+ bool update_flag;
+ int ret;
+ int i;
+
+ /* Go through the cluster and either validate the new value or
+ (if no new value was set), copy the current value to the new
+ value, ensuring a consistent view for the control ops when
+ called. */
+ for (i = 0; i < master->ncontrols; i++) {
+ struct v4l2_ctrl *ctrl = master->cluster[i];
+
+ if (ctrl == NULL)
+ continue;
+
+ if (!ctrl->is_new) {
+ cur_to_new(ctrl);
+ continue;
+ }
+ /* Check again: it may have changed since the
+ previous check in try_or_set_ext_ctrls(). */
+ if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED))
+ return -EBUSY;
+ }
+
+ ret = call_op(master, try_ctrl);
+
+ /* Don't set if there is no change */
+ if (ret || !set || !cluster_changed(master))
+ return ret;
+ ret = call_op(master, s_ctrl);
+ if (ret)
+ return ret;
+
+ /* If OK, then make the new values permanent. */
+ update_flag = is_cur_manual(master) != is_new_manual(master);
+ for (i = 0; i < master->ncontrols; i++)
+ new_to_cur(fh, master->cluster[i], update_flag && i > 0);
+ return 0;
+}
+
+/* Validate controls. */
+static int validate_ctrls(struct v4l2_ext_controls *cs,
+ struct v4l2_ctrl_helper *helpers, bool set)
+{
+ unsigned i;
+ int ret = 0;
+
+ cs->error_idx = cs->count;
+ for (i = 0; i < cs->count; i++) {
+ struct v4l2_ctrl *ctrl = helpers[i].ctrl;
+
+ cs->error_idx = i;
+
+ if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
+ return -EACCES;
+ /* This test is also done in try_set_control_cluster() which
+ is called in atomic context, so that has the final say,
+ but it makes sense to do an up-front check as well. Once
+ an error occurs in try_set_control_cluster() some other
+ controls may have been set already and we want to do a
+ best-effort to avoid that. */
+ if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED))
+ return -EBUSY;
+ ret = validate_new(ctrl, &cs->controls[i]);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+/* Obtain the current volatile values of an autocluster and mark them
+ as new. */
+static void update_from_auto_cluster(struct v4l2_ctrl *master)
+{
+ int i;
+
+ for (i = 0; i < master->ncontrols; i++)
+ cur_to_new(master->cluster[i]);
+ if (!call_op(master, g_volatile_ctrl))
+ for (i = 1; i < master->ncontrols; i++)
+ if (master->cluster[i])
+ master->cluster[i]->is_new = 1;
+}
+
+/* Try or try-and-set controls */
+static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ext_controls *cs,
+ bool set)
+{
+ struct v4l2_ctrl_helper helper[4];
+ struct v4l2_ctrl_helper *helpers = helper;
+ unsigned i, j;
+ int ret;
+
+ cs->error_idx = cs->count;
+ cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class);
+
+ if (hdl == NULL)
+ return -EINVAL;
+
+ if (cs->count == 0)
+ return class_check(hdl, cs->ctrl_class);
+
+ if (cs->count > ARRAY_SIZE(helper)) {
+ helpers = kmalloc(sizeof(helper[0]) * cs->count, GFP_KERNEL);
+ if (!helpers)
+ return -ENOMEM;
+ }
+ ret = prepare_ext_ctrls(hdl, cs, helpers);
+ if (!ret)
+ ret = validate_ctrls(cs, helpers, set);
+ if (ret && set)
+ cs->error_idx = cs->count;
+ for (i = 0; !ret && i < cs->count; i++) {
+ struct v4l2_ctrl *master;
+ u32 idx = i;
+
+ if (helpers[i].mref == NULL)
+ continue;
+
+ cs->error_idx = i;
+ master = helpers[i].mref->ctrl;
+ v4l2_ctrl_lock(master);
+
+ /* Reset the 'is_new' flags of the cluster */
+ for (j = 0; j < master->ncontrols; j++)
+ if (master->cluster[j])
+ master->cluster[j]->is_new = 0;
+
+ /* For volatile autoclusters that are currently in auto mode
+ we need to discover if it will be set to manual mode.
+ If so, then we have to copy the current volatile values
+ first since those will become the new manual values (which
+ may be overwritten by explicit new values from this set
+ of controls). */
+ if (master->is_auto && master->has_volatiles &&
+ !is_cur_manual(master)) {
+ /* Pick an initial non-manual value */
+ s32 new_auto_val = master->manual_mode_value + 1;
+ u32 tmp_idx = idx;
+
+ do {
+ /* Check if the auto control is part of the
+ list, and remember the new value. */
+ if (helpers[tmp_idx].ctrl == master)
+ new_auto_val = cs->controls[tmp_idx].value;
+ tmp_idx = helpers[tmp_idx].next;
+ } while (tmp_idx);
+ /* If the new value == the manual value, then copy
+ the current volatile values. */
+ if (new_auto_val == master->manual_mode_value)
+ update_from_auto_cluster(master);
+ }
+
+ /* Copy the new caller-supplied control values.
+ user_to_new() sets 'is_new' to 1. */
+ do {
+ ret = user_to_new(cs->controls + idx, helpers[idx].ctrl);
+ idx = helpers[idx].next;
+ } while (!ret && idx);
+
+ if (!ret)
+ ret = try_or_set_cluster(fh, master, set);
+
+ /* Copy the new values back to userspace. */
+ if (!ret) {
+ idx = i;
+ do {
+ ret = new_to_user(cs->controls + idx,
+ helpers[idx].ctrl);
+ idx = helpers[idx].next;
+ } while (!ret && idx);
+ }
+ v4l2_ctrl_unlock(master);
+ }
+
+ if (cs->count > ARRAY_SIZE(helper))
+ kfree(helpers);
+ return ret;
+}
+
+int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
+{
+ return try_set_ext_ctrls(NULL, hdl, cs, false);
+}
+EXPORT_SYMBOL(v4l2_try_ext_ctrls);
+
+int v4l2_s_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ext_controls *cs)
+{
+ return try_set_ext_ctrls(fh, hdl, cs, true);
+}
+EXPORT_SYMBOL(v4l2_s_ext_ctrls);
+
+int v4l2_subdev_try_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
+{
+ return try_set_ext_ctrls(NULL, sd->ctrl_handler, cs, false);
+}
+EXPORT_SYMBOL(v4l2_subdev_try_ext_ctrls);
+
+int v4l2_subdev_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
+{
+ return try_set_ext_ctrls(NULL, sd->ctrl_handler, cs, true);
+}
+EXPORT_SYMBOL(v4l2_subdev_s_ext_ctrls);
+
+/* Helper function for VIDIOC_S_CTRL compatibility */
+static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, s32 *val)
+{
+ struct v4l2_ctrl *master = ctrl->cluster[0];
+ int ret;
+ int i;
+
+ ret = validate_new_int(ctrl, val);
+ if (ret)
+ return ret;
+
+ v4l2_ctrl_lock(ctrl);
+
+ /* Reset the 'is_new' flags of the cluster */
+ for (i = 0; i < master->ncontrols; i++)
+ if (master->cluster[i])
+ master->cluster[i]->is_new = 0;
+
+ /* For autoclusters with volatiles that are switched from auto to
+ manual mode we have to update the current volatile values since
+ those will become the initial manual values after such a switch. */
+ if (master->is_auto && master->has_volatiles && ctrl == master &&
+ !is_cur_manual(master) && *val == master->manual_mode_value)
+ update_from_auto_cluster(master);
+ ctrl->val = *val;
+ ctrl->is_new = 1;
+ ret = try_or_set_cluster(fh, master, true);
+ *val = ctrl->cur.val;
+ v4l2_ctrl_unlock(ctrl);
+ return ret;
+}
+
+int v4l2_s_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
+ struct v4l2_control *control)
+{
+ struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, control->id);
+
+ if (ctrl == NULL || !type_is_int(ctrl))
+ return -EINVAL;
+
+ if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
+ return -EACCES;
+
+ return set_ctrl(fh, ctrl, &control->value);
+}
+EXPORT_SYMBOL(v4l2_s_ctrl);
+
+int v4l2_subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control)
+{
+ return v4l2_s_ctrl(NULL, sd->ctrl_handler, control);
+}
+EXPORT_SYMBOL(v4l2_subdev_s_ctrl);
+
+int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val)
+{
+ /* It's a driver bug if this happens. */
+ WARN_ON(!type_is_int(ctrl));
+ return set_ctrl(NULL, ctrl, &val);
+}
+EXPORT_SYMBOL(v4l2_ctrl_s_ctrl);
+
+void v4l2_ctrl_add_event(struct v4l2_ctrl *ctrl,
+ struct v4l2_subscribed_event *sev)
+{
+ v4l2_ctrl_lock(ctrl);
+ list_add_tail(&sev->node, &ctrl->ev_subs);
+ if (ctrl->type != V4L2_CTRL_TYPE_CTRL_CLASS &&
+ (sev->flags & V4L2_EVENT_SUB_FL_SEND_INITIAL)) {
+ struct v4l2_event ev;
+ u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
+
+ if (!(ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY))
+ changes |= V4L2_EVENT_CTRL_CH_VALUE;
+ fill_event(&ev, ctrl, changes);
+ v4l2_event_queue_fh(sev->fh, &ev);
+ }
+ v4l2_ctrl_unlock(ctrl);
+}
+EXPORT_SYMBOL(v4l2_ctrl_add_event);
+
+void v4l2_ctrl_del_event(struct v4l2_ctrl *ctrl,
+ struct v4l2_subscribed_event *sev)
+{
+ v4l2_ctrl_lock(ctrl);
+ list_del(&sev->node);
+ v4l2_ctrl_unlock(ctrl);
+}
+EXPORT_SYMBOL(v4l2_ctrl_del_event);
+
+int v4l2_ctrl_log_status(struct file *file, void *fh)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_fh *vfh = file->private_data;
+
+ if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) && vfd->v4l2_dev)
+ v4l2_ctrl_handler_log_status(vfh->ctrl_handler,
+ vfd->v4l2_dev->name);
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_ctrl_log_status);
+
+int v4l2_ctrl_subscribe_event(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ if (sub->type == V4L2_EVENT_CTRL)
+ return v4l2_event_subscribe(fh, sub, 0);
+ return -EINVAL;
+}
+EXPORT_SYMBOL(v4l2_ctrl_subscribe_event);
+
+unsigned int v4l2_ctrl_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct v4l2_fh *fh = file->private_data;
+
+ if (v4l2_event_pending(fh))
+ return POLLPRI;
+ poll_wait(file, &fh->wait, wait);
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_ctrl_poll);
diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c
new file mode 100644
index 00000000..70bec548
--- /dev/null
+++ b/drivers/media/video/v4l2-dev.c
@@ -0,0 +1,803 @@
+/*
+ * Video capture interface for Linux version 2
+ *
+ * A generic video device interface for the LINUX operating system
+ * using a set of device structures/vectors for low level operations.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alan Cox, <alan@lxorguk.ukuu.org.uk> (version 1)
+ * Mauro Carvalho Chehab <mchehab@infradead.org> (version 2)
+ *
+ * Fixes: 20000516 Claudio Matsuoka <claudio@conectiva.com>
+ * - Added procfs support
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+
+#define VIDEO_NUM_DEVICES 256
+#define VIDEO_NAME "video4linux"
+
+/*
+ * sysfs stuff
+ */
+
+static ssize_t show_index(struct device *cd,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vdev = to_video_device(cd);
+
+ return sprintf(buf, "%i\n", vdev->index);
+}
+
+static ssize_t show_name(struct device *cd,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vdev = to_video_device(cd);
+
+ return sprintf(buf, "%.*s\n", (int)sizeof(vdev->name), vdev->name);
+}
+
+static struct device_attribute video_device_attrs[] = {
+ __ATTR(name, S_IRUGO, show_name, NULL),
+ __ATTR(index, S_IRUGO, show_index, NULL),
+ __ATTR_NULL
+};
+
+/*
+ * Active devices
+ */
+static struct video_device *video_device[VIDEO_NUM_DEVICES];
+static DEFINE_MUTEX(videodev_lock);
+static DECLARE_BITMAP(devnode_nums[VFL_TYPE_MAX], VIDEO_NUM_DEVICES);
+
+/* Device node utility functions */
+
+/* Note: these utility functions all assume that vfl_type is in the range
+ [0, VFL_TYPE_MAX-1]. */
+
+#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
+/* Return the bitmap corresponding to vfl_type. */
+static inline unsigned long *devnode_bits(int vfl_type)
+{
+ /* Any types not assigned to fixed minor ranges must be mapped to
+ one single bitmap for the purposes of finding a free node number
+ since all those unassigned types use the same minor range. */
+ int idx = (vfl_type > VFL_TYPE_RADIO) ? VFL_TYPE_MAX - 1 : vfl_type;
+
+ return devnode_nums[idx];
+}
+#else
+/* Return the bitmap corresponding to vfl_type. */
+static inline unsigned long *devnode_bits(int vfl_type)
+{
+ return devnode_nums[vfl_type];
+}
+#endif
+
+/* Mark device node number vdev->num as used */
+static inline void devnode_set(struct video_device *vdev)
+{
+ set_bit(vdev->num, devnode_bits(vdev->vfl_type));
+}
+
+/* Mark device node number vdev->num as unused */
+static inline void devnode_clear(struct video_device *vdev)
+{
+ clear_bit(vdev->num, devnode_bits(vdev->vfl_type));
+}
+
+/* Try to find a free device node number in the range [from, to> */
+static inline int devnode_find(struct video_device *vdev, int from, int to)
+{
+ return find_next_zero_bit(devnode_bits(vdev->vfl_type), to, from);
+}
+
+struct video_device *video_device_alloc(void)
+{
+ return kzalloc(sizeof(struct video_device), GFP_KERNEL);
+}
+EXPORT_SYMBOL(video_device_alloc);
+
+void video_device_release(struct video_device *vdev)
+{
+ kfree(vdev);
+}
+EXPORT_SYMBOL(video_device_release);
+
+void video_device_release_empty(struct video_device *vdev)
+{
+ /* Do nothing */
+ /* Only valid when the video_device struct is a static. */
+}
+EXPORT_SYMBOL(video_device_release_empty);
+
+static inline void video_get(struct video_device *vdev)
+{
+ get_device(&vdev->dev);
+}
+
+static inline void video_put(struct video_device *vdev)
+{
+ put_device(&vdev->dev);
+}
+
+/* Called when the last user of the video device exits. */
+static void v4l2_device_release(struct device *cd)
+{
+ struct video_device *vdev = to_video_device(cd);
+ struct v4l2_device *v4l2_dev = vdev->v4l2_dev;
+
+ mutex_lock(&videodev_lock);
+ if (WARN_ON(video_device[vdev->minor] != vdev)) {
+ /* should not happen */
+ mutex_unlock(&videodev_lock);
+ return;
+ }
+
+ /* Free up this device for reuse */
+ video_device[vdev->minor] = NULL;
+
+ /* Delete the cdev on this minor as well */
+ cdev_del(vdev->cdev);
+ /* Just in case some driver tries to access this from
+ the release() callback. */
+ vdev->cdev = NULL;
+
+ /* Mark device node number as free */
+ devnode_clear(vdev);
+
+ mutex_unlock(&videodev_lock);
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ if (v4l2_dev && v4l2_dev->mdev &&
+ vdev->vfl_type != VFL_TYPE_SUBDEV)
+ media_device_unregister_entity(&vdev->entity);
+#endif
+
+ /* Do not call v4l2_device_put if there is no release callback set.
+ * Drivers that have no v4l2_device release callback might free the
+ * v4l2_dev instance in the video_device release callback below, so we
+ * must perform this check here.
+ *
+ * TODO: In the long run all drivers that use v4l2_device should use the
+ * v4l2_device release callback. This check will then be unnecessary.
+ */
+ if (v4l2_dev && v4l2_dev->release == NULL)
+ v4l2_dev = NULL;
+
+ /* Release video_device and perform other
+ cleanups as needed. */
+ vdev->release(vdev);
+
+ /* Decrease v4l2_device refcount */
+ if (v4l2_dev)
+ v4l2_device_put(v4l2_dev);
+}
+
+static struct class video_class = {
+ .name = VIDEO_NAME,
+ .dev_attrs = video_device_attrs,
+};
+
+struct video_device *video_devdata(struct file *file)
+{
+ return video_device[iminor(file->f_path.dentry->d_inode)];
+}
+EXPORT_SYMBOL(video_devdata);
+
+
+/* Priority handling */
+
+static inline bool prio_is_valid(enum v4l2_priority prio)
+{
+ return prio == V4L2_PRIORITY_BACKGROUND ||
+ prio == V4L2_PRIORITY_INTERACTIVE ||
+ prio == V4L2_PRIORITY_RECORD;
+}
+
+void v4l2_prio_init(struct v4l2_prio_state *global)
+{
+ memset(global, 0, sizeof(*global));
+}
+EXPORT_SYMBOL(v4l2_prio_init);
+
+int v4l2_prio_change(struct v4l2_prio_state *global, enum v4l2_priority *local,
+ enum v4l2_priority new)
+{
+ if (!prio_is_valid(new))
+ return -EINVAL;
+ if (*local == new)
+ return 0;
+
+ atomic_inc(&global->prios[new]);
+ if (prio_is_valid(*local))
+ atomic_dec(&global->prios[*local]);
+ *local = new;
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_prio_change);
+
+void v4l2_prio_open(struct v4l2_prio_state *global, enum v4l2_priority *local)
+{
+ v4l2_prio_change(global, local, V4L2_PRIORITY_DEFAULT);
+}
+EXPORT_SYMBOL(v4l2_prio_open);
+
+void v4l2_prio_close(struct v4l2_prio_state *global, enum v4l2_priority local)
+{
+ if (prio_is_valid(local))
+ atomic_dec(&global->prios[local]);
+}
+EXPORT_SYMBOL(v4l2_prio_close);
+
+enum v4l2_priority v4l2_prio_max(struct v4l2_prio_state *global)
+{
+ if (atomic_read(&global->prios[V4L2_PRIORITY_RECORD]) > 0)
+ return V4L2_PRIORITY_RECORD;
+ if (atomic_read(&global->prios[V4L2_PRIORITY_INTERACTIVE]) > 0)
+ return V4L2_PRIORITY_INTERACTIVE;
+ if (atomic_read(&global->prios[V4L2_PRIORITY_BACKGROUND]) > 0)
+ return V4L2_PRIORITY_BACKGROUND;
+ return V4L2_PRIORITY_UNSET;
+}
+EXPORT_SYMBOL(v4l2_prio_max);
+
+int v4l2_prio_check(struct v4l2_prio_state *global, enum v4l2_priority local)
+{
+ return (local < v4l2_prio_max(global)) ? -EBUSY : 0;
+}
+EXPORT_SYMBOL(v4l2_prio_check);
+
+
+static ssize_t v4l2_read(struct file *filp, char __user *buf,
+ size_t sz, loff_t *off)
+{
+ struct video_device *vdev = video_devdata(filp);
+ int ret = -ENODEV;
+
+ if (!vdev->fops->read)
+ return -EINVAL;
+ if (vdev->lock && mutex_lock_interruptible(vdev->lock))
+ return -ERESTARTSYS;
+ if (video_is_registered(vdev))
+ ret = vdev->fops->read(filp, buf, sz, off);
+ if (vdev->lock)
+ mutex_unlock(vdev->lock);
+ return ret;
+}
+
+static ssize_t v4l2_write(struct file *filp, const char __user *buf,
+ size_t sz, loff_t *off)
+{
+ struct video_device *vdev = video_devdata(filp);
+ int ret = -ENODEV;
+
+ if (!vdev->fops->write)
+ return -EINVAL;
+ if (vdev->lock && mutex_lock_interruptible(vdev->lock))
+ return -ERESTARTSYS;
+ if (video_is_registered(vdev))
+ ret = vdev->fops->write(filp, buf, sz, off);
+ if (vdev->lock)
+ mutex_unlock(vdev->lock);
+ return ret;
+}
+
+static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll)
+{
+ struct video_device *vdev = video_devdata(filp);
+ int ret = POLLERR | POLLHUP;
+
+ if (!vdev->fops->poll)
+ return DEFAULT_POLLMASK;
+ if (vdev->lock)
+ mutex_lock(vdev->lock);
+ if (video_is_registered(vdev))
+ ret = vdev->fops->poll(filp, poll);
+ if (vdev->lock)
+ mutex_unlock(vdev->lock);
+ return ret;
+}
+
+static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct video_device *vdev = video_devdata(filp);
+ int ret = -ENODEV;
+
+ if (vdev->fops->unlocked_ioctl) {
+ if (vdev->lock && mutex_lock_interruptible(vdev->lock))
+ return -ERESTARTSYS;
+ if (video_is_registered(vdev))
+ ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
+ if (vdev->lock)
+ mutex_unlock(vdev->lock);
+ } else if (vdev->fops->ioctl) {
+ /* This code path is a replacement for the BKL. It is a major
+ * hack but it will have to do for those drivers that are not
+ * yet converted to use unlocked_ioctl.
+ *
+ * There are two options: if the driver implements struct
+ * v4l2_device, then the lock defined there is used to
+ * serialize the ioctls. Otherwise the v4l2 core lock defined
+ * below is used. This lock is really bad since it serializes
+ * completely independent devices.
+ *
+ * Both variants suffer from the same problem: if the driver
+ * sleeps, then it blocks all ioctls since the lock is still
+ * held. This is very common for VIDIOC_DQBUF since that
+ * normally waits for a frame to arrive. As a result any other
+ * ioctl calls will proceed very, very slowly since each call
+ * will have to wait for the VIDIOC_QBUF to finish. Things that
+ * should take 0.01s may now take 10-20 seconds.
+ *
+ * The workaround is to *not* take the lock for VIDIOC_DQBUF.
+ * This actually works OK for videobuf-based drivers, since
+ * videobuf will take its own internal lock.
+ */
+ static DEFINE_MUTEX(v4l2_ioctl_mutex);
+ struct mutex *m = vdev->v4l2_dev ?
+ &vdev->v4l2_dev->ioctl_lock : &v4l2_ioctl_mutex;
+
+ if (cmd != VIDIOC_DQBUF && mutex_lock_interruptible(m))
+ return -ERESTARTSYS;
+ if (video_is_registered(vdev))
+ ret = vdev->fops->ioctl(filp, cmd, arg);
+ if (cmd != VIDIOC_DQBUF)
+ mutex_unlock(m);
+ } else
+ ret = -ENOTTY;
+
+ return ret;
+}
+
+#ifdef CONFIG_MMU
+#define v4l2_get_unmapped_area NULL
+#else
+static unsigned long v4l2_get_unmapped_area(struct file *filp,
+ unsigned long addr, unsigned long len, unsigned long pgoff,
+ unsigned long flags)
+{
+ struct video_device *vdev = video_devdata(filp);
+
+ if (!vdev->fops->get_unmapped_area)
+ return -ENOSYS;
+ if (!video_is_registered(vdev))
+ return -ENODEV;
+ return vdev->fops->get_unmapped_area(filp, addr, len, pgoff, flags);
+}
+#endif
+
+static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
+{
+ struct video_device *vdev = video_devdata(filp);
+ int ret = -ENODEV;
+
+ if (!vdev->fops->mmap)
+ return ret;
+ if (vdev->lock && mutex_lock_interruptible(vdev->lock))
+ return -ERESTARTSYS;
+ if (video_is_registered(vdev))
+ ret = vdev->fops->mmap(filp, vm);
+ if (vdev->lock)
+ mutex_unlock(vdev->lock);
+ return ret;
+}
+
+/* Override for the open function */
+static int v4l2_open(struct inode *inode, struct file *filp)
+{
+ struct video_device *vdev;
+ int ret = 0;
+
+ /* Check if the video device is available */
+ mutex_lock(&videodev_lock);
+ vdev = video_devdata(filp);
+ /* return ENODEV if the video device has already been removed. */
+ if (vdev == NULL || !video_is_registered(vdev)) {
+ mutex_unlock(&videodev_lock);
+ return -ENODEV;
+ }
+ /* and increase the device refcount */
+ video_get(vdev);
+ mutex_unlock(&videodev_lock);
+ if (vdev->fops->open) {
+ if (vdev->lock && mutex_lock_interruptible(vdev->lock)) {
+ ret = -ERESTARTSYS;
+ goto err;
+ }
+ if (video_is_registered(vdev))
+ ret = vdev->fops->open(filp);
+ else
+ ret = -ENODEV;
+ if (vdev->lock)
+ mutex_unlock(vdev->lock);
+ }
+
+err:
+ /* decrease the refcount in case of an error */
+ if (ret)
+ video_put(vdev);
+ return ret;
+}
+
+/* Override for the release function */
+static int v4l2_release(struct inode *inode, struct file *filp)
+{
+ struct video_device *vdev = video_devdata(filp);
+ int ret = 0;
+
+ if (vdev->fops->release) {
+ if (vdev->lock)
+ mutex_lock(vdev->lock);
+ vdev->fops->release(filp);
+ if (vdev->lock)
+ mutex_unlock(vdev->lock);
+ }
+ /* decrease the refcount unconditionally since the release()
+ return value is ignored. */
+ video_put(vdev);
+ return ret;
+}
+
+static const struct file_operations v4l2_fops = {
+ .owner = THIS_MODULE,
+ .read = v4l2_read,
+ .write = v4l2_write,
+ .open = v4l2_open,
+ .get_unmapped_area = v4l2_get_unmapped_area,
+ .mmap = v4l2_mmap,
+ .unlocked_ioctl = v4l2_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = v4l2_compat_ioctl32,
+#endif
+ .release = v4l2_release,
+ .poll = v4l2_poll,
+ .llseek = no_llseek,
+};
+
+/**
+ * get_index - assign stream index number based on parent device
+ * @vdev: video_device to assign index number to, vdev->parent should be assigned
+ *
+ * Note that when this is called the new device has not yet been registered
+ * in the video_device array, but it was able to obtain a minor number.
+ *
+ * This means that we can always obtain a free stream index number since
+ * the worst case scenario is that there are VIDEO_NUM_DEVICES - 1 slots in
+ * use of the video_device array.
+ *
+ * Returns a free index number.
+ */
+static int get_index(struct video_device *vdev)
+{
+ /* This can be static since this function is called with the global
+ videodev_lock held. */
+ static DECLARE_BITMAP(used, VIDEO_NUM_DEVICES);
+ int i;
+
+ /* Some drivers do not set the parent. In that case always return 0. */
+ if (vdev->parent == NULL)
+ return 0;
+
+ bitmap_zero(used, VIDEO_NUM_DEVICES);
+
+ for (i = 0; i < VIDEO_NUM_DEVICES; i++) {
+ if (video_device[i] != NULL &&
+ video_device[i]->parent == vdev->parent) {
+ set_bit(video_device[i]->index, used);
+ }
+ }
+
+ return find_first_zero_bit(used, VIDEO_NUM_DEVICES);
+}
+
+/**
+ * __video_register_device - register video4linux devices
+ * @vdev: video device structure we want to register
+ * @type: type of device to register
+ * @nr: which device node number (0 == /dev/video0, 1 == /dev/video1, ...
+ * -1 == first free)
+ * @warn_if_nr_in_use: warn if the desired device node number
+ * was already in use and another number was chosen instead.
+ * @owner: module that owns the video device node
+ *
+ * The registration code assigns minor numbers and device node numbers
+ * based on the requested type and registers the new device node with
+ * the kernel.
+ *
+ * This function assumes that struct video_device was zeroed when it
+ * was allocated and does not contain any stale date.
+ *
+ * An error is returned if no free minor or device node number could be
+ * found, or if the registration of the device node failed.
+ *
+ * Zero is returned on success.
+ *
+ * Valid types are
+ *
+ * %VFL_TYPE_GRABBER - A frame grabber
+ *
+ * %VFL_TYPE_VBI - Vertical blank data (undecoded)
+ *
+ * %VFL_TYPE_RADIO - A radio card
+ *
+ * %VFL_TYPE_SUBDEV - A subdevice
+ */
+int __video_register_device(struct video_device *vdev, int type, int nr,
+ int warn_if_nr_in_use, struct module *owner)
+{
+ int i = 0;
+ int ret;
+ int minor_offset = 0;
+ int minor_cnt = VIDEO_NUM_DEVICES;
+ const char *name_base;
+
+ /* A minor value of -1 marks this video device as never
+ having been registered */
+ vdev->minor = -1;
+
+ /* the release callback MUST be present */
+ if (WARN_ON(!vdev->release))
+ return -EINVAL;
+
+ /* v4l2_fh support */
+ spin_lock_init(&vdev->fh_lock);
+ INIT_LIST_HEAD(&vdev->fh_list);
+
+ /* Part 1: check device type */
+ switch (type) {
+ case VFL_TYPE_GRABBER:
+ name_base = "video";
+ break;
+ case VFL_TYPE_VBI:
+ name_base = "vbi";
+ break;
+ case VFL_TYPE_RADIO:
+ name_base = "radio";
+ break;
+ case VFL_TYPE_SUBDEV:
+ name_base = "v4l-subdev";
+ break;
+ default:
+ printk(KERN_ERR "%s called with unknown type: %d\n",
+ __func__, type);
+ return -EINVAL;
+ }
+
+ vdev->vfl_type = type;
+ vdev->cdev = NULL;
+ if (vdev->v4l2_dev) {
+ if (vdev->v4l2_dev->dev)
+ vdev->parent = vdev->v4l2_dev->dev;
+ if (vdev->ctrl_handler == NULL)
+ vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
+ /* If the prio state pointer is NULL, then use the v4l2_device
+ prio state. */
+ if (vdev->prio == NULL)
+ vdev->prio = &vdev->v4l2_dev->prio;
+ }
+
+ /* Part 2: find a free minor, device node number and device index. */
+#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
+ /* Keep the ranges for the first four types for historical
+ * reasons.
+ * Newer devices (not yet in place) should use the range
+ * of 128-191 and just pick the first free minor there
+ * (new style). */
+ switch (type) {
+ case VFL_TYPE_GRABBER:
+ minor_offset = 0;
+ minor_cnt = 64;
+ break;
+ case VFL_TYPE_RADIO:
+ minor_offset = 64;
+ minor_cnt = 64;
+ break;
+ case VFL_TYPE_VBI:
+ minor_offset = 224;
+ minor_cnt = 32;
+ break;
+ default:
+ minor_offset = 128;
+ minor_cnt = 64;
+ break;
+ }
+#endif
+
+ /* Pick a device node number */
+ mutex_lock(&videodev_lock);
+ nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt);
+ if (nr == minor_cnt)
+ nr = devnode_find(vdev, 0, minor_cnt);
+ if (nr == minor_cnt) {
+ printk(KERN_ERR "could not get a free device node number\n");
+ mutex_unlock(&videodev_lock);
+ return -ENFILE;
+ }
+#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
+ /* 1-on-1 mapping of device node number to minor number */
+ i = nr;
+#else
+ /* The device node number and minor numbers are independent, so
+ we just find the first free minor number. */
+ for (i = 0; i < VIDEO_NUM_DEVICES; i++)
+ if (video_device[i] == NULL)
+ break;
+ if (i == VIDEO_NUM_DEVICES) {
+ mutex_unlock(&videodev_lock);
+ printk(KERN_ERR "could not get a free minor\n");
+ return -ENFILE;
+ }
+#endif
+ vdev->minor = i + minor_offset;
+ vdev->num = nr;
+ devnode_set(vdev);
+
+ /* Should not happen since we thought this minor was free */
+ WARN_ON(video_device[vdev->minor] != NULL);
+ vdev->index = get_index(vdev);
+ mutex_unlock(&videodev_lock);
+
+ /* Part 3: Initialize the character device */
+ vdev->cdev = cdev_alloc();
+ if (vdev->cdev == NULL) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+ vdev->cdev->ops = &v4l2_fops;
+ vdev->cdev->owner = owner;
+ ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
+ if (ret < 0) {
+ printk(KERN_ERR "%s: cdev_add failed\n", __func__);
+ kfree(vdev->cdev);
+ vdev->cdev = NULL;
+ goto cleanup;
+ }
+
+ /* Part 4: register the device with sysfs */
+ vdev->dev.class = &video_class;
+ vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
+ if (vdev->parent)
+ vdev->dev.parent = vdev->parent;
+ dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
+ ret = device_register(&vdev->dev);
+ if (ret < 0) {
+ printk(KERN_ERR "%s: device_register failed\n", __func__);
+ goto cleanup;
+ }
+ /* Register the release callback that will be called when the last
+ reference to the device goes away. */
+ vdev->dev.release = v4l2_device_release;
+
+ if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)
+ printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__,
+ name_base, nr, video_device_node_name(vdev));
+
+ /* Increase v4l2_device refcount */
+ if (vdev->v4l2_dev)
+ v4l2_device_get(vdev->v4l2_dev);
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ /* Part 5: Register the entity. */
+ if (vdev->v4l2_dev && vdev->v4l2_dev->mdev &&
+ vdev->vfl_type != VFL_TYPE_SUBDEV) {
+ vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
+ vdev->entity.name = vdev->name;
+ vdev->entity.info.v4l.major = VIDEO_MAJOR;
+ vdev->entity.info.v4l.minor = vdev->minor;
+ ret = media_device_register_entity(vdev->v4l2_dev->mdev,
+ &vdev->entity);
+ if (ret < 0)
+ printk(KERN_WARNING
+ "%s: media_device_register_entity failed\n",
+ __func__);
+ }
+#endif
+ /* Part 6: Activate this minor. The char device can now be used. */
+ set_bit(V4L2_FL_REGISTERED, &vdev->flags);
+ mutex_lock(&videodev_lock);
+ video_device[vdev->minor] = vdev;
+ mutex_unlock(&videodev_lock);
+
+ return 0;
+
+cleanup:
+ mutex_lock(&videodev_lock);
+ if (vdev->cdev)
+ cdev_del(vdev->cdev);
+ devnode_clear(vdev);
+ mutex_unlock(&videodev_lock);
+ /* Mark this video device as never having been registered. */
+ vdev->minor = -1;
+ return ret;
+}
+EXPORT_SYMBOL(__video_register_device);
+
+/**
+ * video_unregister_device - unregister a video4linux device
+ * @vdev: the device to unregister
+ *
+ * This unregisters the passed device. Future open calls will
+ * be met with errors.
+ */
+void video_unregister_device(struct video_device *vdev)
+{
+ /* Check if vdev was ever registered at all */
+ if (!vdev || !video_is_registered(vdev))
+ return;
+
+ mutex_lock(&videodev_lock);
+ /* This must be in a critical section to prevent a race with v4l2_open.
+ * Once this bit has been cleared video_get may never be called again.
+ */
+ clear_bit(V4L2_FL_REGISTERED, &vdev->flags);
+ mutex_unlock(&videodev_lock);
+ device_unregister(&vdev->dev);
+}
+EXPORT_SYMBOL(video_unregister_device);
+
+/*
+ * Initialise video for linux
+ */
+static int __init videodev_init(void)
+{
+ dev_t dev = MKDEV(VIDEO_MAJOR, 0);
+ int ret;
+
+ printk(KERN_INFO "Linux video capture interface: v2.00\n");
+ ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);
+ if (ret < 0) {
+ printk(KERN_WARNING "videodev: unable to get major %d\n",
+ VIDEO_MAJOR);
+ return ret;
+ }
+
+ ret = class_register(&video_class);
+ if (ret < 0) {
+ unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
+ printk(KERN_WARNING "video_dev: class_register failed\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void __exit videodev_exit(void)
+{
+ dev_t dev = MKDEV(VIDEO_MAJOR, 0);
+
+ class_unregister(&video_class);
+ unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
+}
+
+subsys_initcall(videodev_init);
+module_exit(videodev_exit)
+
+MODULE_AUTHOR("Alan Cox, Mauro Carvalho Chehab <mchehab@infradead.org>");
+MODULE_DESCRIPTION("Device registrar for Video4Linux drivers v2");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV_MAJOR(VIDEO_MAJOR);
+
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c
new file mode 100644
index 00000000..1f203b85
--- /dev/null
+++ b/drivers/media/video/v4l2-device.c
@@ -0,0 +1,280 @@
+/*
+ V4L2 device support.
+
+ Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#if defined(CONFIG_SPI)
+#include <linux/spi/spi.h>
+#endif
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+
+int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
+{
+ if (v4l2_dev == NULL)
+ return -EINVAL;
+
+ INIT_LIST_HEAD(&v4l2_dev->subdevs);
+ spin_lock_init(&v4l2_dev->lock);
+ mutex_init(&v4l2_dev->ioctl_lock);
+ v4l2_prio_init(&v4l2_dev->prio);
+ kref_init(&v4l2_dev->ref);
+ get_device(dev);
+ v4l2_dev->dev = dev;
+ if (dev == NULL) {
+ /* If dev == NULL, then name must be filled in by the caller */
+ WARN_ON(!v4l2_dev->name[0]);
+ return 0;
+ }
+
+ /* Set name to driver name + device name if it is empty. */
+ if (!v4l2_dev->name[0])
+ snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s",
+ dev->driver->name, dev_name(dev));
+ if (!dev_get_drvdata(dev))
+ dev_set_drvdata(dev, v4l2_dev);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_device_register);
+
+static void v4l2_device_release(struct kref *ref)
+{
+ struct v4l2_device *v4l2_dev =
+ container_of(ref, struct v4l2_device, ref);
+
+ if (v4l2_dev->release)
+ v4l2_dev->release(v4l2_dev);
+}
+
+int v4l2_device_put(struct v4l2_device *v4l2_dev)
+{
+ return kref_put(&v4l2_dev->ref, v4l2_device_release);
+}
+EXPORT_SYMBOL_GPL(v4l2_device_put);
+
+int v4l2_device_set_name(struct v4l2_device *v4l2_dev, const char *basename,
+ atomic_t *instance)
+{
+ int num = atomic_inc_return(instance) - 1;
+ int len = strlen(basename);
+
+ if (basename[len - 1] >= '0' && basename[len - 1] <= '9')
+ snprintf(v4l2_dev->name, sizeof(v4l2_dev->name),
+ "%s-%d", basename, num);
+ else
+ snprintf(v4l2_dev->name, sizeof(v4l2_dev->name),
+ "%s%d", basename, num);
+ return num;
+}
+EXPORT_SYMBOL_GPL(v4l2_device_set_name);
+
+void v4l2_device_disconnect(struct v4l2_device *v4l2_dev)
+{
+ if (v4l2_dev->dev == NULL)
+ return;
+
+ if (dev_get_drvdata(v4l2_dev->dev) == v4l2_dev)
+ dev_set_drvdata(v4l2_dev->dev, NULL);
+ put_device(v4l2_dev->dev);
+ v4l2_dev->dev = NULL;
+}
+EXPORT_SYMBOL_GPL(v4l2_device_disconnect);
+
+void v4l2_device_unregister(struct v4l2_device *v4l2_dev)
+{
+ struct v4l2_subdev *sd, *next;
+
+ if (v4l2_dev == NULL)
+ return;
+ v4l2_device_disconnect(v4l2_dev);
+
+ /* Unregister subdevs */
+ list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list) {
+ v4l2_device_unregister_subdev(sd);
+#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
+ if (sd->flags & V4L2_SUBDEV_FL_IS_I2C) {
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ /* We need to unregister the i2c client explicitly.
+ We cannot rely on i2c_del_adapter to always
+ unregister clients for us, since if the i2c bus
+ is a platform bus, then it is never deleted. */
+ if (client)
+ i2c_unregister_device(client);
+ continue;
+ }
+#endif
+#if defined(CONFIG_SPI)
+ if (sd->flags & V4L2_SUBDEV_FL_IS_SPI) {
+ struct spi_device *spi = v4l2_get_subdevdata(sd);
+
+ if (spi)
+ spi_unregister_device(spi);
+ continue;
+ }
+#endif
+ }
+}
+EXPORT_SYMBOL_GPL(v4l2_device_unregister);
+
+int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
+ struct v4l2_subdev *sd)
+{
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ struct media_entity *entity = &sd->entity;
+#endif
+ int err;
+
+ /* Check for valid input */
+ if (v4l2_dev == NULL || sd == NULL || !sd->name[0])
+ return -EINVAL;
+
+ /* Warn if we apparently re-register a subdev */
+ WARN_ON(sd->v4l2_dev != NULL);
+
+ if (!try_module_get(sd->owner))
+ return -ENODEV;
+
+ sd->v4l2_dev = v4l2_dev;
+ if (sd->internal_ops && sd->internal_ops->registered) {
+ err = sd->internal_ops->registered(sd);
+ if (err) {
+ module_put(sd->owner);
+ return err;
+ }
+ }
+
+ /* This just returns 0 if either of the two args is NULL */
+ err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler);
+ if (err) {
+ if (sd->internal_ops && sd->internal_ops->unregistered)
+ sd->internal_ops->unregistered(sd);
+ module_put(sd->owner);
+ return err;
+ }
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ /* Register the entity. */
+ if (v4l2_dev->mdev) {
+ err = media_device_register_entity(v4l2_dev->mdev, entity);
+ if (err < 0) {
+ if (sd->internal_ops && sd->internal_ops->unregistered)
+ sd->internal_ops->unregistered(sd);
+ module_put(sd->owner);
+ return err;
+ }
+ }
+#endif
+
+ spin_lock(&v4l2_dev->lock);
+ list_add_tail(&sd->list, &v4l2_dev->subdevs);
+ spin_unlock(&v4l2_dev->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_device_register_subdev);
+
+static void v4l2_device_release_subdev_node(struct video_device *vdev)
+{
+ struct v4l2_subdev *sd = video_get_drvdata(vdev);
+ sd->devnode = NULL;
+ kfree(vdev);
+}
+
+int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
+{
+ struct video_device *vdev;
+ struct v4l2_subdev *sd;
+ int err;
+
+ /* Register a device node for every subdev marked with the
+ * V4L2_SUBDEV_FL_HAS_DEVNODE flag.
+ */
+ list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
+ if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
+ continue;
+
+ vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
+ if (!vdev) {
+ err = -ENOMEM;
+ goto clean_up;
+ }
+
+ video_set_drvdata(vdev, sd);
+ strlcpy(vdev->name, sd->name, sizeof(vdev->name));
+ vdev->v4l2_dev = v4l2_dev;
+ vdev->fops = &v4l2_subdev_fops;
+ vdev->release = v4l2_device_release_subdev_node;
+ vdev->ctrl_handler = sd->ctrl_handler;
+ err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
+ sd->owner);
+ if (err < 0) {
+ kfree(vdev);
+ goto clean_up;
+ }
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ sd->entity.info.v4l.major = VIDEO_MAJOR;
+ sd->entity.info.v4l.minor = vdev->minor;
+#endif
+ sd->devnode = vdev;
+ }
+ return 0;
+
+clean_up:
+ list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
+ if (!sd->devnode)
+ break;
+ video_unregister_device(sd->devnode);
+ }
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(v4l2_device_register_subdev_nodes);
+
+void v4l2_device_unregister_subdev(struct v4l2_subdev *sd)
+{
+ struct v4l2_device *v4l2_dev;
+
+ /* return if it isn't registered */
+ if (sd == NULL || sd->v4l2_dev == NULL)
+ return;
+
+ v4l2_dev = sd->v4l2_dev;
+
+ spin_lock(&v4l2_dev->lock);
+ list_del(&sd->list);
+ spin_unlock(&v4l2_dev->lock);
+
+ if (sd->internal_ops && sd->internal_ops->unregistered)
+ sd->internal_ops->unregistered(sd);
+ sd->v4l2_dev = NULL;
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ if (v4l2_dev->mdev)
+ media_device_unregister_entity(&sd->entity);
+#endif
+ video_unregister_device(sd->devnode);
+ module_put(sd->owner);
+}
+EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev);
diff --git a/drivers/media/video/v4l2-event.c b/drivers/media/video/v4l2-event.c
new file mode 100644
index 00000000..c26ad963
--- /dev/null
+++ b/drivers/media/video/v4l2-event.c
@@ -0,0 +1,320 @@
+/*
+ * v4l2-event.c
+ *
+ * V4L2 events.
+ *
+ * Copyright (C) 2009--2010 Nokia Corporation.
+ *
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ctrls.h>
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+
+static unsigned sev_pos(const struct v4l2_subscribed_event *sev, unsigned idx)
+{
+ idx += sev->first;
+ return idx >= sev->elems ? idx - sev->elems : idx;
+}
+
+static int __v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event)
+{
+ struct v4l2_kevent *kev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+
+ if (list_empty(&fh->available)) {
+ spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+ return -ENOENT;
+ }
+
+ WARN_ON(fh->navailable == 0);
+
+ kev = list_first_entry(&fh->available, struct v4l2_kevent, list);
+ list_del(&kev->list);
+ fh->navailable--;
+
+ kev->event.pending = fh->navailable;
+ *event = kev->event;
+ kev->sev->first = sev_pos(kev->sev, 1);
+ kev->sev->in_use--;
+
+ spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+
+ return 0;
+}
+
+int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event,
+ int nonblocking)
+{
+ int ret;
+
+ if (nonblocking)
+ return __v4l2_event_dequeue(fh, event);
+
+ /* Release the vdev lock while waiting */
+ if (fh->vdev->lock)
+ mutex_unlock(fh->vdev->lock);
+
+ do {
+ ret = wait_event_interruptible(fh->wait,
+ fh->navailable != 0);
+ if (ret < 0)
+ break;
+
+ ret = __v4l2_event_dequeue(fh, event);
+ } while (ret == -ENOENT);
+
+ if (fh->vdev->lock)
+ mutex_lock(fh->vdev->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_event_dequeue);
+
+/* Caller must hold fh->vdev->fh_lock! */
+static struct v4l2_subscribed_event *v4l2_event_subscribed(
+ struct v4l2_fh *fh, u32 type, u32 id)
+{
+ struct v4l2_subscribed_event *sev;
+
+ assert_spin_locked(&fh->vdev->fh_lock);
+
+ list_for_each_entry(sev, &fh->subscribed, list)
+ if (sev->type == type && sev->id == id)
+ return sev;
+
+ return NULL;
+}
+
+static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev,
+ const struct timespec *ts)
+{
+ struct v4l2_subscribed_event *sev;
+ struct v4l2_kevent *kev;
+ bool copy_payload = true;
+
+ /* Are we subscribed? */
+ sev = v4l2_event_subscribed(fh, ev->type, ev->id);
+ if (sev == NULL)
+ return;
+
+ /* Increase event sequence number on fh. */
+ fh->sequence++;
+
+ /* Do we have any free events? */
+ if (sev->in_use == sev->elems) {
+ /* no, remove the oldest one */
+ kev = sev->events + sev_pos(sev, 0);
+ list_del(&kev->list);
+ sev->in_use--;
+ sev->first = sev_pos(sev, 1);
+ fh->navailable--;
+ if (sev->elems == 1) {
+ if (sev->replace) {
+ sev->replace(&kev->event, ev);
+ copy_payload = false;
+ }
+ } else if (sev->merge) {
+ struct v4l2_kevent *second_oldest =
+ sev->events + sev_pos(sev, 0);
+ sev->merge(&kev->event, &second_oldest->event);
+ }
+ }
+
+ /* Take one and fill it. */
+ kev = sev->events + sev_pos(sev, sev->in_use);
+ kev->event.type = ev->type;
+ if (copy_payload)
+ kev->event.u = ev->u;
+ kev->event.id = ev->id;
+ kev->event.timestamp = *ts;
+ kev->event.sequence = fh->sequence;
+ sev->in_use++;
+ list_add_tail(&kev->list, &fh->available);
+
+ fh->navailable++;
+
+ wake_up_all(&fh->wait);
+}
+
+void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)
+{
+ struct v4l2_fh *fh;
+ unsigned long flags;
+ struct timespec timestamp;
+
+ ktime_get_ts(&timestamp);
+
+ spin_lock_irqsave(&vdev->fh_lock, flags);
+
+ list_for_each_entry(fh, &vdev->fh_list, list)
+ __v4l2_event_queue_fh(fh, ev, &timestamp);
+
+ spin_unlock_irqrestore(&vdev->fh_lock, flags);
+}
+EXPORT_SYMBOL_GPL(v4l2_event_queue);
+
+void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev)
+{
+ unsigned long flags;
+ struct timespec timestamp;
+
+ ktime_get_ts(&timestamp);
+
+ spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+ __v4l2_event_queue_fh(fh, ev, &timestamp);
+ spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+}
+EXPORT_SYMBOL_GPL(v4l2_event_queue_fh);
+
+int v4l2_event_pending(struct v4l2_fh *fh)
+{
+ return fh->navailable;
+}
+EXPORT_SYMBOL_GPL(v4l2_event_pending);
+
+static void ctrls_replace(struct v4l2_event *old, const struct v4l2_event *new)
+{
+ u32 old_changes = old->u.ctrl.changes;
+
+ old->u.ctrl = new->u.ctrl;
+ old->u.ctrl.changes |= old_changes;
+}
+
+static void ctrls_merge(const struct v4l2_event *old, struct v4l2_event *new)
+{
+ new->u.ctrl.changes |= old->u.ctrl.changes;
+}
+
+int v4l2_event_subscribe(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub, unsigned elems)
+{
+ struct v4l2_subscribed_event *sev, *found_ev;
+ struct v4l2_ctrl *ctrl = NULL;
+ unsigned long flags;
+ unsigned i;
+
+ if (sub->type == V4L2_EVENT_ALL)
+ return -EINVAL;
+
+ if (elems < 1)
+ elems = 1;
+ if (sub->type == V4L2_EVENT_CTRL) {
+ ctrl = v4l2_ctrl_find(fh->ctrl_handler, sub->id);
+ if (ctrl == NULL)
+ return -EINVAL;
+ }
+
+ sev = kzalloc(sizeof(*sev) + sizeof(struct v4l2_kevent) * elems, GFP_KERNEL);
+ if (!sev)
+ return -ENOMEM;
+ for (i = 0; i < elems; i++)
+ sev->events[i].sev = sev;
+ sev->type = sub->type;
+ sev->id = sub->id;
+ sev->flags = sub->flags;
+ sev->fh = fh;
+ sev->elems = elems;
+ if (ctrl) {
+ sev->replace = ctrls_replace;
+ sev->merge = ctrls_merge;
+ }
+
+ spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+ found_ev = v4l2_event_subscribed(fh, sub->type, sub->id);
+ if (!found_ev)
+ list_add(&sev->list, &fh->subscribed);
+ spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+
+ /* v4l2_ctrl_add_event uses a mutex, so do this outside the spin lock */
+ if (found_ev)
+ kfree(sev);
+ else if (ctrl)
+ v4l2_ctrl_add_event(ctrl, sev);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_event_subscribe);
+
+void v4l2_event_unsubscribe_all(struct v4l2_fh *fh)
+{
+ struct v4l2_event_subscription sub;
+ struct v4l2_subscribed_event *sev;
+ unsigned long flags;
+
+ do {
+ sev = NULL;
+
+ spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+ if (!list_empty(&fh->subscribed)) {
+ sev = list_first_entry(&fh->subscribed,
+ struct v4l2_subscribed_event, list);
+ sub.type = sev->type;
+ sub.id = sev->id;
+ }
+ spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+ if (sev)
+ v4l2_event_unsubscribe(fh, &sub);
+ } while (sev);
+}
+EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe_all);
+
+int v4l2_event_unsubscribe(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ struct v4l2_subscribed_event *sev;
+ unsigned long flags;
+ int i;
+
+ if (sub->type == V4L2_EVENT_ALL) {
+ v4l2_event_unsubscribe_all(fh);
+ return 0;
+ }
+
+ spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+
+ sev = v4l2_event_subscribed(fh, sub->type, sub->id);
+ if (sev != NULL) {
+ /* Remove any pending events for this subscription */
+ for (i = 0; i < sev->in_use; i++) {
+ list_del(&sev->events[sev_pos(sev, i)].list);
+ fh->navailable--;
+ }
+ list_del(&sev->list);
+ }
+
+ spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+ if (sev && sev->type == V4L2_EVENT_CTRL) {
+ struct v4l2_ctrl *ctrl = v4l2_ctrl_find(fh->ctrl_handler, sev->id);
+
+ if (ctrl)
+ v4l2_ctrl_del_event(ctrl, sev);
+ }
+
+ kfree(sev);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe);
diff --git a/drivers/media/video/v4l2-fh.c b/drivers/media/video/v4l2-fh.c
new file mode 100644
index 00000000..9e3fc040
--- /dev/null
+++ b/drivers/media/video/v4l2-fh.c
@@ -0,0 +1,120 @@
+/*
+ * v4l2-fh.c
+ *
+ * V4L2 file handles.
+ *
+ * Copyright (C) 2009--2010 Nokia Corporation.
+ *
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+
+void v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev)
+{
+ fh->vdev = vdev;
+ /* Inherit from video_device. May be overridden by the driver. */
+ fh->ctrl_handler = vdev->ctrl_handler;
+ INIT_LIST_HEAD(&fh->list);
+ set_bit(V4L2_FL_USES_V4L2_FH, &fh->vdev->flags);
+ fh->prio = V4L2_PRIORITY_UNSET;
+ init_waitqueue_head(&fh->wait);
+ INIT_LIST_HEAD(&fh->available);
+ INIT_LIST_HEAD(&fh->subscribed);
+ fh->sequence = -1;
+}
+EXPORT_SYMBOL_GPL(v4l2_fh_init);
+
+void v4l2_fh_add(struct v4l2_fh *fh)
+{
+ unsigned long flags;
+
+ if (test_bit(V4L2_FL_USE_FH_PRIO, &fh->vdev->flags))
+ v4l2_prio_open(fh->vdev->prio, &fh->prio);
+ spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+ list_add(&fh->list, &fh->vdev->fh_list);
+ spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+}
+EXPORT_SYMBOL_GPL(v4l2_fh_add);
+
+int v4l2_fh_open(struct file *filp)
+{
+ struct video_device *vdev = video_devdata(filp);
+ struct v4l2_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+
+ filp->private_data = fh;
+ if (fh == NULL)
+ return -ENOMEM;
+ v4l2_fh_init(fh, vdev);
+ v4l2_fh_add(fh);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_fh_open);
+
+void v4l2_fh_del(struct v4l2_fh *fh)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+ list_del_init(&fh->list);
+ spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+ if (test_bit(V4L2_FL_USE_FH_PRIO, &fh->vdev->flags))
+ v4l2_prio_close(fh->vdev->prio, fh->prio);
+}
+EXPORT_SYMBOL_GPL(v4l2_fh_del);
+
+void v4l2_fh_exit(struct v4l2_fh *fh)
+{
+ if (fh->vdev == NULL)
+ return;
+ v4l2_event_unsubscribe_all(fh);
+ fh->vdev = NULL;
+}
+EXPORT_SYMBOL_GPL(v4l2_fh_exit);
+
+int v4l2_fh_release(struct file *filp)
+{
+ struct v4l2_fh *fh = filp->private_data;
+
+ if (fh) {
+ v4l2_fh_del(fh);
+ v4l2_fh_exit(fh);
+ kfree(fh);
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_fh_release);
+
+int v4l2_fh_is_singular(struct v4l2_fh *fh)
+{
+ unsigned long flags;
+ int is_singular;
+
+ if (fh == NULL || fh->vdev == NULL)
+ return 0;
+ spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+ is_singular = list_is_singular(&fh->list);
+ spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+ return is_singular;
+}
+EXPORT_SYMBOL_GPL(v4l2_fh_is_singular);
diff --git a/drivers/media/video/v4l2-int-device.c b/drivers/media/video/v4l2-int-device.c
new file mode 100644
index 00000000..f4473494
--- /dev/null
+++ b/drivers/media/video/v4l2-int-device.c
@@ -0,0 +1,164 @@
+/*
+ * drivers/media/video/v4l2-int-device.c
+ *
+ * V4L2 internal ioctl interface.
+ *
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/sort.h>
+#include <linux/string.h>
+#include <linux/module.h>
+
+#include <media/v4l2-int-device.h>
+
+static DEFINE_MUTEX(mutex);
+static LIST_HEAD(int_list);
+
+void v4l2_int_device_try_attach_all(void)
+{
+ struct v4l2_int_device *m, *s;
+
+ list_for_each_entry(m, &int_list, head) {
+ if (m->type != v4l2_int_type_master)
+ continue;
+
+ list_for_each_entry(s, &int_list, head) {
+ if (s->type != v4l2_int_type_slave)
+ continue;
+
+ /* Slave is connected? */
+ if (s->u.slave->master)
+ continue;
+
+ /* Slave wants to attach to master? */
+ if (s->u.slave->attach_to[0] != 0
+ && strncmp(m->name, s->u.slave->attach_to,
+ V4L2NAMESIZE))
+ continue;
+
+ if (!try_module_get(m->module))
+ continue;
+
+ s->u.slave->master = m;
+ if (m->u.master->attach(s)) {
+ s->u.slave->master = NULL;
+ module_put(m->module);
+ continue;
+ }
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(v4l2_int_device_try_attach_all);
+
+static int ioctl_sort_cmp(const void *a, const void *b)
+{
+ const struct v4l2_int_ioctl_desc *d1 = a, *d2 = b;
+
+ if (d1->num > d2->num)
+ return 1;
+
+ if (d1->num < d2->num)
+ return -1;
+
+ return 0;
+}
+
+int v4l2_int_device_register(struct v4l2_int_device *d)
+{
+ if (d->type == v4l2_int_type_slave)
+ sort(d->u.slave->ioctls, d->u.slave->num_ioctls,
+ sizeof(struct v4l2_int_ioctl_desc),
+ &ioctl_sort_cmp, NULL);
+ mutex_lock(&mutex);
+ list_add(&d->head, &int_list);
+ v4l2_int_device_try_attach_all();
+ mutex_unlock(&mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_int_device_register);
+
+void v4l2_int_device_unregister(struct v4l2_int_device *d)
+{
+ mutex_lock(&mutex);
+ list_del(&d->head);
+ if (d->type == v4l2_int_type_slave
+ && d->u.slave->master != NULL) {
+ d->u.slave->master->u.master->detach(d);
+ module_put(d->u.slave->master->module);
+ d->u.slave->master = NULL;
+ }
+ mutex_unlock(&mutex);
+}
+EXPORT_SYMBOL_GPL(v4l2_int_device_unregister);
+
+/* Adapted from search_extable in extable.c. */
+static v4l2_int_ioctl_func *find_ioctl(struct v4l2_int_slave *slave, int cmd,
+ v4l2_int_ioctl_func *no_such_ioctl)
+{
+ const struct v4l2_int_ioctl_desc *first = slave->ioctls;
+ const struct v4l2_int_ioctl_desc *last =
+ first + slave->num_ioctls - 1;
+
+ while (first <= last) {
+ const struct v4l2_int_ioctl_desc *mid;
+
+ mid = (last - first) / 2 + first;
+
+ if (mid->num < cmd)
+ first = mid + 1;
+ else if (mid->num > cmd)
+ last = mid - 1;
+ else
+ return mid->func;
+ }
+
+ return no_such_ioctl;
+}
+
+static int no_such_ioctl_0(struct v4l2_int_device *d)
+{
+ return -ENOIOCTLCMD;
+}
+
+int v4l2_int_ioctl_0(struct v4l2_int_device *d, int cmd)
+{
+ return ((v4l2_int_ioctl_func_0 *)
+ find_ioctl(d->u.slave, cmd,
+ (v4l2_int_ioctl_func *)no_such_ioctl_0))(d);
+}
+EXPORT_SYMBOL_GPL(v4l2_int_ioctl_0);
+
+static int no_such_ioctl_1(struct v4l2_int_device *d, void *arg)
+{
+ return -ENOIOCTLCMD;
+}
+
+int v4l2_int_ioctl_1(struct v4l2_int_device *d, int cmd, void *arg)
+{
+ return ((v4l2_int_ioctl_func_1 *)
+ find_ioctl(d->u.slave, cmd,
+ (v4l2_int_ioctl_func *)no_such_ioctl_1))(d, arg);
+}
+EXPORT_SYMBOL_GPL(v4l2_int_ioctl_1);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c
new file mode 100644
index 00000000..5b2ec1fd
--- /dev/null
+++ b/drivers/media/video/v4l2-ioctl.c
@@ -0,0 +1,2490 @@
+/*
+ * Video capture interface for Linux version 2
+ *
+ * A generic framework to process V4L2 ioctl commands.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alan Cox, <alan@lxorguk.ukuu.org.uk> (version 1)
+ * Mauro Carvalho Chehab <mchehab@infradead.org> (version 2)
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+
+#include <linux/videodev2.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+
+#define dbgarg(cmd, fmt, arg...) \
+ do { \
+ if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) { \
+ printk(KERN_DEBUG "%s: ", vfd->name); \
+ v4l_printk_ioctl(cmd); \
+ printk(" " fmt, ## arg); \
+ } \
+ } while (0)
+
+#define dbgarg2(fmt, arg...) \
+ do { \
+ if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) \
+ printk(KERN_DEBUG "%s: " fmt, vfd->name, ## arg);\
+ } while (0)
+
+#define dbgarg3(fmt, arg...) \
+ do { \
+ if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) \
+ printk(KERN_CONT "%s: " fmt, vfd->name, ## arg);\
+ } while (0)
+
+/* Zero out the end of the struct pointed to by p. Everything after, but
+ * not including, the specified field is cleared. */
+#define CLEAR_AFTER_FIELD(p, field) \
+ memset((u8 *)(p) + offsetof(typeof(*(p)), field) + sizeof((p)->field), \
+ 0, sizeof(*(p)) - offsetof(typeof(*(p)), field) - sizeof((p)->field))
+
+#define have_fmt_ops(foo) ( \
+ ops->vidioc_##foo##_fmt_vid_cap || \
+ ops->vidioc_##foo##_fmt_vid_out || \
+ ops->vidioc_##foo##_fmt_vid_cap_mplane || \
+ ops->vidioc_##foo##_fmt_vid_out_mplane || \
+ ops->vidioc_##foo##_fmt_vid_overlay || \
+ ops->vidioc_##foo##_fmt_vbi_cap || \
+ ops->vidioc_##foo##_fmt_vid_out_overlay || \
+ ops->vidioc_##foo##_fmt_vbi_out || \
+ ops->vidioc_##foo##_fmt_sliced_vbi_cap || \
+ ops->vidioc_##foo##_fmt_sliced_vbi_out || \
+ ops->vidioc_##foo##_fmt_type_private)
+
+struct std_descr {
+ v4l2_std_id std;
+ const char *descr;
+};
+
+static const struct std_descr standards[] = {
+ { V4L2_STD_NTSC, "NTSC" },
+ { V4L2_STD_NTSC_M, "NTSC-M" },
+ { V4L2_STD_NTSC_M_JP, "NTSC-M-JP" },
+ { V4L2_STD_NTSC_M_KR, "NTSC-M-KR" },
+ { V4L2_STD_NTSC_443, "NTSC-443" },
+ { V4L2_STD_PAL, "PAL" },
+ { V4L2_STD_PAL_BG, "PAL-BG" },
+ { V4L2_STD_PAL_B, "PAL-B" },
+ { V4L2_STD_PAL_B1, "PAL-B1" },
+ { V4L2_STD_PAL_G, "PAL-G" },
+ { V4L2_STD_PAL_H, "PAL-H" },
+ { V4L2_STD_PAL_I, "PAL-I" },
+ { V4L2_STD_PAL_DK, "PAL-DK" },
+ { V4L2_STD_PAL_D, "PAL-D" },
+ { V4L2_STD_PAL_D1, "PAL-D1" },
+ { V4L2_STD_PAL_K, "PAL-K" },
+ { V4L2_STD_PAL_M, "PAL-M" },
+ { V4L2_STD_PAL_N, "PAL-N" },
+ { V4L2_STD_PAL_Nc, "PAL-Nc" },
+ { V4L2_STD_PAL_60, "PAL-60" },
+ { V4L2_STD_SECAM, "SECAM" },
+ { V4L2_STD_SECAM_B, "SECAM-B" },
+ { V4L2_STD_SECAM_G, "SECAM-G" },
+ { V4L2_STD_SECAM_H, "SECAM-H" },
+ { V4L2_STD_SECAM_DK, "SECAM-DK" },
+ { V4L2_STD_SECAM_D, "SECAM-D" },
+ { V4L2_STD_SECAM_K, "SECAM-K" },
+ { V4L2_STD_SECAM_K1, "SECAM-K1" },
+ { V4L2_STD_SECAM_L, "SECAM-L" },
+ { V4L2_STD_SECAM_LC, "SECAM-Lc" },
+ { 0, "Unknown" }
+};
+
+/* video4linux standard ID conversion to standard name
+ */
+const char *v4l2_norm_to_name(v4l2_std_id id)
+{
+ u32 myid = id;
+ int i;
+
+ /* HACK: ppc32 architecture doesn't have __ucmpdi2 function to handle
+ 64 bit comparations. So, on that architecture, with some gcc
+ variants, compilation fails. Currently, the max value is 30bit wide.
+ */
+ BUG_ON(myid != id);
+
+ for (i = 0; standards[i].std; i++)
+ if (myid == standards[i].std)
+ break;
+ return standards[i].descr;
+}
+EXPORT_SYMBOL(v4l2_norm_to_name);
+
+/* Returns frame period for the given standard */
+void v4l2_video_std_frame_period(int id, struct v4l2_fract *frameperiod)
+{
+ if (id & V4L2_STD_525_60) {
+ frameperiod->numerator = 1001;
+ frameperiod->denominator = 30000;
+ } else {
+ frameperiod->numerator = 1;
+ frameperiod->denominator = 25;
+ }
+}
+EXPORT_SYMBOL(v4l2_video_std_frame_period);
+
+/* Fill in the fields of a v4l2_standard structure according to the
+ 'id' and 'transmission' parameters. Returns negative on error. */
+int v4l2_video_std_construct(struct v4l2_standard *vs,
+ int id, const char *name)
+{
+ vs->id = id;
+ v4l2_video_std_frame_period(id, &vs->frameperiod);
+ vs->framelines = (id & V4L2_STD_525_60) ? 525 : 625;
+ strlcpy(vs->name, name, sizeof(vs->name));
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_video_std_construct);
+
+/* ----------------------------------------------------------------- */
+/* some arrays for pretty-printing debug messages of enum types */
+
+const char *v4l2_field_names[] = {
+ [V4L2_FIELD_ANY] = "any",
+ [V4L2_FIELD_NONE] = "none",
+ [V4L2_FIELD_TOP] = "top",
+ [V4L2_FIELD_BOTTOM] = "bottom",
+ [V4L2_FIELD_INTERLACED] = "interlaced",
+ [V4L2_FIELD_SEQ_TB] = "seq-tb",
+ [V4L2_FIELD_SEQ_BT] = "seq-bt",
+ [V4L2_FIELD_ALTERNATE] = "alternate",
+ [V4L2_FIELD_INTERLACED_TB] = "interlaced-tb",
+ [V4L2_FIELD_INTERLACED_BT] = "interlaced-bt",
+};
+EXPORT_SYMBOL(v4l2_field_names);
+
+const char *v4l2_type_names[] = {
+ [V4L2_BUF_TYPE_VIDEO_CAPTURE] = "vid-cap",
+ [V4L2_BUF_TYPE_VIDEO_OVERLAY] = "vid-overlay",
+ [V4L2_BUF_TYPE_VIDEO_OUTPUT] = "vid-out",
+ [V4L2_BUF_TYPE_VBI_CAPTURE] = "vbi-cap",
+ [V4L2_BUF_TYPE_VBI_OUTPUT] = "vbi-out",
+ [V4L2_BUF_TYPE_SLICED_VBI_CAPTURE] = "sliced-vbi-cap",
+ [V4L2_BUF_TYPE_SLICED_VBI_OUTPUT] = "sliced-vbi-out",
+ [V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY] = "vid-out-overlay",
+ [V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE] = "vid-cap-mplane",
+ [V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE] = "vid-out-mplane",
+};
+EXPORT_SYMBOL(v4l2_type_names);
+
+static const char *v4l2_memory_names[] = {
+ [V4L2_MEMORY_MMAP] = "mmap",
+ [V4L2_MEMORY_USERPTR] = "userptr",
+ [V4L2_MEMORY_OVERLAY] = "overlay",
+};
+
+#define prt_names(a, arr) ((((a) >= 0) && ((a) < ARRAY_SIZE(arr))) ? \
+ arr[a] : "unknown")
+
+/* ------------------------------------------------------------------ */
+/* debug help functions */
+static const char *v4l2_ioctls[] = {
+ [_IOC_NR(VIDIOC_QUERYCAP)] = "VIDIOC_QUERYCAP",
+ [_IOC_NR(VIDIOC_RESERVED)] = "VIDIOC_RESERVED",
+ [_IOC_NR(VIDIOC_ENUM_FMT)] = "VIDIOC_ENUM_FMT",
+ [_IOC_NR(VIDIOC_G_FMT)] = "VIDIOC_G_FMT",
+ [_IOC_NR(VIDIOC_S_FMT)] = "VIDIOC_S_FMT",
+ [_IOC_NR(VIDIOC_REQBUFS)] = "VIDIOC_REQBUFS",
+ [_IOC_NR(VIDIOC_QUERYBUF)] = "VIDIOC_QUERYBUF",
+ [_IOC_NR(VIDIOC_G_FBUF)] = "VIDIOC_G_FBUF",
+ [_IOC_NR(VIDIOC_S_FBUF)] = "VIDIOC_S_FBUF",
+ [_IOC_NR(VIDIOC_OVERLAY)] = "VIDIOC_OVERLAY",
+ [_IOC_NR(VIDIOC_QBUF)] = "VIDIOC_QBUF",
+ [_IOC_NR(VIDIOC_DQBUF)] = "VIDIOC_DQBUF",
+ [_IOC_NR(VIDIOC_STREAMON)] = "VIDIOC_STREAMON",
+ [_IOC_NR(VIDIOC_STREAMOFF)] = "VIDIOC_STREAMOFF",
+ [_IOC_NR(VIDIOC_G_PARM)] = "VIDIOC_G_PARM",
+ [_IOC_NR(VIDIOC_S_PARM)] = "VIDIOC_S_PARM",
+ [_IOC_NR(VIDIOC_G_STD)] = "VIDIOC_G_STD",
+ [_IOC_NR(VIDIOC_S_STD)] = "VIDIOC_S_STD",
+ [_IOC_NR(VIDIOC_ENUMSTD)] = "VIDIOC_ENUMSTD",
+ [_IOC_NR(VIDIOC_ENUMINPUT)] = "VIDIOC_ENUMINPUT",
+ [_IOC_NR(VIDIOC_G_CTRL)] = "VIDIOC_G_CTRL",
+ [_IOC_NR(VIDIOC_S_CTRL)] = "VIDIOC_S_CTRL",
+ [_IOC_NR(VIDIOC_G_TUNER)] = "VIDIOC_G_TUNER",
+ [_IOC_NR(VIDIOC_S_TUNER)] = "VIDIOC_S_TUNER",
+ [_IOC_NR(VIDIOC_G_AUDIO)] = "VIDIOC_G_AUDIO",
+ [_IOC_NR(VIDIOC_S_AUDIO)] = "VIDIOC_S_AUDIO",
+ [_IOC_NR(VIDIOC_QUERYCTRL)] = "VIDIOC_QUERYCTRL",
+ [_IOC_NR(VIDIOC_QUERYMENU)] = "VIDIOC_QUERYMENU",
+ [_IOC_NR(VIDIOC_G_INPUT)] = "VIDIOC_G_INPUT",
+ [_IOC_NR(VIDIOC_S_INPUT)] = "VIDIOC_S_INPUT",
+ [_IOC_NR(VIDIOC_G_OUTPUT)] = "VIDIOC_G_OUTPUT",
+ [_IOC_NR(VIDIOC_S_OUTPUT)] = "VIDIOC_S_OUTPUT",
+ [_IOC_NR(VIDIOC_ENUMOUTPUT)] = "VIDIOC_ENUMOUTPUT",
+ [_IOC_NR(VIDIOC_G_AUDOUT)] = "VIDIOC_G_AUDOUT",
+ [_IOC_NR(VIDIOC_S_AUDOUT)] = "VIDIOC_S_AUDOUT",
+ [_IOC_NR(VIDIOC_G_MODULATOR)] = "VIDIOC_G_MODULATOR",
+ [_IOC_NR(VIDIOC_S_MODULATOR)] = "VIDIOC_S_MODULATOR",
+ [_IOC_NR(VIDIOC_G_FREQUENCY)] = "VIDIOC_G_FREQUENCY",
+ [_IOC_NR(VIDIOC_S_FREQUENCY)] = "VIDIOC_S_FREQUENCY",
+ [_IOC_NR(VIDIOC_CROPCAP)] = "VIDIOC_CROPCAP",
+ [_IOC_NR(VIDIOC_G_CROP)] = "VIDIOC_G_CROP",
+ [_IOC_NR(VIDIOC_S_CROP)] = "VIDIOC_S_CROP",
+ [_IOC_NR(VIDIOC_G_SELECTION)] = "VIDIOC_G_SELECTION",
+ [_IOC_NR(VIDIOC_S_SELECTION)] = "VIDIOC_S_SELECTION",
+ [_IOC_NR(VIDIOC_G_JPEGCOMP)] = "VIDIOC_G_JPEGCOMP",
+ [_IOC_NR(VIDIOC_S_JPEGCOMP)] = "VIDIOC_S_JPEGCOMP",
+ [_IOC_NR(VIDIOC_QUERYSTD)] = "VIDIOC_QUERYSTD",
+ [_IOC_NR(VIDIOC_TRY_FMT)] = "VIDIOC_TRY_FMT",
+ [_IOC_NR(VIDIOC_ENUMAUDIO)] = "VIDIOC_ENUMAUDIO",
+ [_IOC_NR(VIDIOC_ENUMAUDOUT)] = "VIDIOC_ENUMAUDOUT",
+ [_IOC_NR(VIDIOC_G_PRIORITY)] = "VIDIOC_G_PRIORITY",
+ [_IOC_NR(VIDIOC_S_PRIORITY)] = "VIDIOC_S_PRIORITY",
+ [_IOC_NR(VIDIOC_G_SLICED_VBI_CAP)] = "VIDIOC_G_SLICED_VBI_CAP",
+ [_IOC_NR(VIDIOC_LOG_STATUS)] = "VIDIOC_LOG_STATUS",
+ [_IOC_NR(VIDIOC_G_EXT_CTRLS)] = "VIDIOC_G_EXT_CTRLS",
+ [_IOC_NR(VIDIOC_S_EXT_CTRLS)] = "VIDIOC_S_EXT_CTRLS",
+ [_IOC_NR(VIDIOC_TRY_EXT_CTRLS)] = "VIDIOC_TRY_EXT_CTRLS",
+#if 1
+ [_IOC_NR(VIDIOC_ENUM_FRAMESIZES)] = "VIDIOC_ENUM_FRAMESIZES",
+ [_IOC_NR(VIDIOC_ENUM_FRAMEINTERVALS)] = "VIDIOC_ENUM_FRAMEINTERVALS",
+ [_IOC_NR(VIDIOC_G_ENC_INDEX)] = "VIDIOC_G_ENC_INDEX",
+ [_IOC_NR(VIDIOC_ENCODER_CMD)] = "VIDIOC_ENCODER_CMD",
+ [_IOC_NR(VIDIOC_TRY_ENCODER_CMD)] = "VIDIOC_TRY_ENCODER_CMD",
+
+ [_IOC_NR(VIDIOC_DECODER_CMD)] = "VIDIOC_DECODER_CMD",
+ [_IOC_NR(VIDIOC_TRY_DECODER_CMD)] = "VIDIOC_TRY_DECODER_CMD",
+ [_IOC_NR(VIDIOC_DBG_S_REGISTER)] = "VIDIOC_DBG_S_REGISTER",
+ [_IOC_NR(VIDIOC_DBG_G_REGISTER)] = "VIDIOC_DBG_G_REGISTER",
+
+ [_IOC_NR(VIDIOC_DBG_G_CHIP_IDENT)] = "VIDIOC_DBG_G_CHIP_IDENT",
+ [_IOC_NR(VIDIOC_S_HW_FREQ_SEEK)] = "VIDIOC_S_HW_FREQ_SEEK",
+#endif
+ [_IOC_NR(VIDIOC_ENUM_DV_PRESETS)] = "VIDIOC_ENUM_DV_PRESETS",
+ [_IOC_NR(VIDIOC_S_DV_PRESET)] = "VIDIOC_S_DV_PRESET",
+ [_IOC_NR(VIDIOC_G_DV_PRESET)] = "VIDIOC_G_DV_PRESET",
+ [_IOC_NR(VIDIOC_QUERY_DV_PRESET)] = "VIDIOC_QUERY_DV_PRESET",
+ [_IOC_NR(VIDIOC_S_DV_TIMINGS)] = "VIDIOC_S_DV_TIMINGS",
+ [_IOC_NR(VIDIOC_G_DV_TIMINGS)] = "VIDIOC_G_DV_TIMINGS",
+ [_IOC_NR(VIDIOC_DQEVENT)] = "VIDIOC_DQEVENT",
+ [_IOC_NR(VIDIOC_SUBSCRIBE_EVENT)] = "VIDIOC_SUBSCRIBE_EVENT",
+ [_IOC_NR(VIDIOC_UNSUBSCRIBE_EVENT)] = "VIDIOC_UNSUBSCRIBE_EVENT",
+ [_IOC_NR(VIDIOC_CREATE_BUFS)] = "VIDIOC_CREATE_BUFS",
+ [_IOC_NR(VIDIOC_PREPARE_BUF)] = "VIDIOC_PREPARE_BUF",
+};
+#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
+
+/* Common ioctl debug function. This function can be used by
+ external ioctl messages as well as internal V4L ioctl */
+void v4l_printk_ioctl(unsigned int cmd)
+{
+ char *dir, *type;
+
+ switch (_IOC_TYPE(cmd)) {
+ case 'd':
+ type = "v4l2_int";
+ break;
+ case 'V':
+ if (_IOC_NR(cmd) >= V4L2_IOCTLS) {
+ type = "v4l2";
+ break;
+ }
+ printk("%s", v4l2_ioctls[_IOC_NR(cmd)]);
+ return;
+ default:
+ type = "unknown";
+ }
+
+ switch (_IOC_DIR(cmd)) {
+ case _IOC_NONE: dir = "--"; break;
+ case _IOC_READ: dir = "r-"; break;
+ case _IOC_WRITE: dir = "-w"; break;
+ case _IOC_READ | _IOC_WRITE: dir = "rw"; break;
+ default: dir = "*ERR*"; break;
+ }
+ printk("%s ioctl '%c', dir=%s, #%d (0x%08x)",
+ type, _IOC_TYPE(cmd), dir, _IOC_NR(cmd), cmd);
+}
+EXPORT_SYMBOL(v4l_printk_ioctl);
+
+static void dbgbuf(unsigned int cmd, struct video_device *vfd,
+ struct v4l2_buffer *p)
+{
+ struct v4l2_timecode *tc = &p->timecode;
+ struct v4l2_plane *plane;
+ int i;
+
+ dbgarg(cmd, "%02ld:%02d:%02d.%08ld index=%d, type=%s, "
+ "flags=0x%08d, field=%0d, sequence=%d, memory=%s\n",
+ p->timestamp.tv_sec / 3600,
+ (int)(p->timestamp.tv_sec / 60) % 60,
+ (int)(p->timestamp.tv_sec % 60),
+ (long)p->timestamp.tv_usec,
+ p->index,
+ prt_names(p->type, v4l2_type_names),
+ p->flags, p->field, p->sequence,
+ prt_names(p->memory, v4l2_memory_names));
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(p->type) && p->m.planes) {
+ for (i = 0; i < p->length; ++i) {
+ plane = &p->m.planes[i];
+ dbgarg2("plane %d: bytesused=%d, data_offset=0x%08x "
+ "offset/userptr=0x%08lx, length=%d\n",
+ i, plane->bytesused, plane->data_offset,
+ plane->m.userptr, plane->length);
+ }
+ } else {
+ dbgarg2("bytesused=%d, offset/userptr=0x%08lx, length=%d\n",
+ p->bytesused, p->m.userptr, p->length);
+ }
+
+ dbgarg2("timecode=%02d:%02d:%02d type=%d, "
+ "flags=0x%08d, frames=%d, userbits=0x%08x\n",
+ tc->hours, tc->minutes, tc->seconds,
+ tc->type, tc->flags, tc->frames, *(__u32 *)tc->userbits);
+}
+
+static inline void dbgrect(struct video_device *vfd, char *s,
+ struct v4l2_rect *r)
+{
+ dbgarg2("%sRect start at %dx%d, size=%dx%d\n", s, r->left, r->top,
+ r->width, r->height);
+};
+
+static inline void v4l_print_pix_fmt(struct video_device *vfd,
+ struct v4l2_pix_format *fmt)
+{
+ dbgarg2("width=%d, height=%d, format=%c%c%c%c, field=%s, "
+ "bytesperline=%d sizeimage=%d, colorspace=%d\n",
+ fmt->width, fmt->height,
+ (fmt->pixelformat & 0xff),
+ (fmt->pixelformat >> 8) & 0xff,
+ (fmt->pixelformat >> 16) & 0xff,
+ (fmt->pixelformat >> 24) & 0xff,
+ prt_names(fmt->field, v4l2_field_names),
+ fmt->bytesperline, fmt->sizeimage, fmt->colorspace);
+};
+
+static inline void v4l_print_pix_fmt_mplane(struct video_device *vfd,
+ struct v4l2_pix_format_mplane *fmt)
+{
+ int i;
+
+ dbgarg2("width=%d, height=%d, format=%c%c%c%c, field=%s, "
+ "colorspace=%d, num_planes=%d\n",
+ fmt->width, fmt->height,
+ (fmt->pixelformat & 0xff),
+ (fmt->pixelformat >> 8) & 0xff,
+ (fmt->pixelformat >> 16) & 0xff,
+ (fmt->pixelformat >> 24) & 0xff,
+ prt_names(fmt->field, v4l2_field_names),
+ fmt->colorspace, fmt->num_planes);
+
+ for (i = 0; i < fmt->num_planes; ++i)
+ dbgarg2("plane %d: bytesperline=%d sizeimage=%d\n", i,
+ fmt->plane_fmt[i].bytesperline,
+ fmt->plane_fmt[i].sizeimage);
+}
+
+static inline void v4l_print_ext_ctrls(unsigned int cmd,
+ struct video_device *vfd, struct v4l2_ext_controls *c, int show_vals)
+{
+ __u32 i;
+
+ if (!(vfd->debug & V4L2_DEBUG_IOCTL_ARG))
+ return;
+ dbgarg(cmd, "");
+ printk(KERN_CONT "class=0x%x", c->ctrl_class);
+ for (i = 0; i < c->count; i++) {
+ if (show_vals && !c->controls[i].size)
+ printk(KERN_CONT " id/val=0x%x/0x%x",
+ c->controls[i].id, c->controls[i].value);
+ else
+ printk(KERN_CONT " id=0x%x,size=%u",
+ c->controls[i].id, c->controls[i].size);
+ }
+ printk(KERN_CONT "\n");
+};
+
+static inline int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv)
+{
+ __u32 i;
+
+ /* zero the reserved fields */
+ c->reserved[0] = c->reserved[1] = 0;
+ for (i = 0; i < c->count; i++)
+ c->controls[i].reserved2[0] = 0;
+
+ /* V4L2_CID_PRIVATE_BASE cannot be used as control class
+ when using extended controls.
+ Only when passed in through VIDIOC_G_CTRL and VIDIOC_S_CTRL
+ is it allowed for backwards compatibility.
+ */
+ if (!allow_priv && c->ctrl_class == V4L2_CID_PRIVATE_BASE)
+ return 0;
+ /* Check that all controls are from the same control class. */
+ for (i = 0; i < c->count; i++) {
+ if (V4L2_CTRL_ID2CLASS(c->controls[i].id) != c->ctrl_class) {
+ c->error_idx = i;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int check_fmt(const struct v4l2_ioctl_ops *ops, enum v4l2_buf_type type)
+{
+ if (ops == NULL)
+ return -EINVAL;
+
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (ops->vidioc_g_fmt_vid_cap ||
+ ops->vidioc_g_fmt_vid_cap_mplane)
+ return 0;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ if (ops->vidioc_g_fmt_vid_cap_mplane)
+ return 0;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ if (ops->vidioc_g_fmt_vid_overlay)
+ return 0;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ if (ops->vidioc_g_fmt_vid_out ||
+ ops->vidioc_g_fmt_vid_out_mplane)
+ return 0;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ if (ops->vidioc_g_fmt_vid_out_mplane)
+ return 0;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ if (ops->vidioc_g_fmt_vid_out_overlay)
+ return 0;
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ if (ops->vidioc_g_fmt_vbi_cap)
+ return 0;
+ break;
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ if (ops->vidioc_g_fmt_vbi_out)
+ return 0;
+ break;
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ if (ops->vidioc_g_fmt_sliced_vbi_cap)
+ return 0;
+ break;
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ if (ops->vidioc_g_fmt_sliced_vbi_out)
+ return 0;
+ break;
+ case V4L2_BUF_TYPE_PRIVATE:
+ if (ops->vidioc_g_fmt_type_private)
+ return 0;
+ break;
+ }
+ return -EINVAL;
+}
+
+static long __video_do_ioctl(struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
+ void *fh = file->private_data;
+ struct v4l2_fh *vfh = NULL;
+ int use_fh_prio = 0;
+ long ret_prio = 0;
+ long ret = -ENOTTY;
+
+ if (ops == NULL) {
+ printk(KERN_WARNING "videodev: \"%s\" has no ioctl_ops.\n",
+ vfd->name);
+ return ret;
+ }
+
+ if ((vfd->debug & V4L2_DEBUG_IOCTL) &&
+ !(vfd->debug & V4L2_DEBUG_IOCTL_ARG)) {
+ v4l_print_ioctl(vfd->name, cmd);
+ printk(KERN_CONT "\n");
+ }
+
+ if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
+ vfh = file->private_data;
+ use_fh_prio = test_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
+ }
+
+ if (use_fh_prio)
+ ret_prio = v4l2_prio_check(vfd->prio, vfh->prio);
+
+ switch (cmd) {
+
+ /* --- capabilities ------------------------------------------ */
+ case VIDIOC_QUERYCAP:
+ {
+ struct v4l2_capability *cap = (struct v4l2_capability *)arg;
+
+ if (!ops->vidioc_querycap)
+ break;
+
+ cap->version = LINUX_VERSION_CODE;
+ ret = ops->vidioc_querycap(file, fh, cap);
+ if (!ret)
+ dbgarg(cmd, "driver=%s, card=%s, bus=%s, "
+ "version=0x%08x, "
+ "capabilities=0x%08x, "
+ "device_caps=0x%08x\n",
+ cap->driver, cap->card, cap->bus_info,
+ cap->version,
+ cap->capabilities,
+ cap->device_caps);
+ break;
+ }
+
+ /* --- priority ------------------------------------------ */
+ case VIDIOC_G_PRIORITY:
+ {
+ enum v4l2_priority *p = arg;
+
+ if (ops->vidioc_g_priority) {
+ ret = ops->vidioc_g_priority(file, fh, p);
+ } else if (use_fh_prio) {
+ *p = v4l2_prio_max(&vfd->v4l2_dev->prio);
+ ret = 0;
+ }
+ if (!ret)
+ dbgarg(cmd, "priority is %d\n", *p);
+ break;
+ }
+ case VIDIOC_S_PRIORITY:
+ {
+ enum v4l2_priority *p = arg;
+
+ if (!ops->vidioc_s_priority && !use_fh_prio)
+ break;
+ dbgarg(cmd, "setting priority to %d\n", *p);
+ if (ops->vidioc_s_priority)
+ ret = ops->vidioc_s_priority(file, fh, *p);
+ else
+ ret = ret_prio ? ret_prio :
+ v4l2_prio_change(&vfd->v4l2_dev->prio,
+ &vfh->prio, *p);
+ break;
+ }
+
+ /* --- capture ioctls ---------------------------------------- */
+ case VIDIOC_ENUM_FMT:
+ {
+ struct v4l2_fmtdesc *f = arg;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (likely(ops->vidioc_enum_fmt_vid_cap))
+ ret = ops->vidioc_enum_fmt_vid_cap(file, fh, f);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ if (likely(ops->vidioc_enum_fmt_vid_cap_mplane))
+ ret = ops->vidioc_enum_fmt_vid_cap_mplane(file,
+ fh, f);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ if (likely(ops->vidioc_enum_fmt_vid_overlay))
+ ret = ops->vidioc_enum_fmt_vid_overlay(file,
+ fh, f);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ if (likely(ops->vidioc_enum_fmt_vid_out))
+ ret = ops->vidioc_enum_fmt_vid_out(file, fh, f);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ if (likely(ops->vidioc_enum_fmt_vid_out_mplane))
+ ret = ops->vidioc_enum_fmt_vid_out_mplane(file,
+ fh, f);
+ break;
+ case V4L2_BUF_TYPE_PRIVATE:
+ if (likely(ops->vidioc_enum_fmt_type_private))
+ ret = ops->vidioc_enum_fmt_type_private(file,
+ fh, f);
+ break;
+ default:
+ break;
+ }
+ if (likely (!ret))
+ dbgarg(cmd, "index=%d, type=%d, flags=%d, "
+ "pixelformat=%c%c%c%c, description='%s'\n",
+ f->index, f->type, f->flags,
+ (f->pixelformat & 0xff),
+ (f->pixelformat >> 8) & 0xff,
+ (f->pixelformat >> 16) & 0xff,
+ (f->pixelformat >> 24) & 0xff,
+ f->description);
+ else if (ret == -ENOTTY &&
+ (ops->vidioc_enum_fmt_vid_cap ||
+ ops->vidioc_enum_fmt_vid_out ||
+ ops->vidioc_enum_fmt_vid_cap_mplane ||
+ ops->vidioc_enum_fmt_vid_out_mplane ||
+ ops->vidioc_enum_fmt_vid_overlay ||
+ ops->vidioc_enum_fmt_type_private))
+ ret = -EINVAL;
+ break;
+ }
+ case VIDIOC_G_FMT:
+ {
+ struct v4l2_format *f = (struct v4l2_format *)arg;
+
+ /* FIXME: Should be one dump per type */
+ dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names));
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (ops->vidioc_g_fmt_vid_cap)
+ ret = ops->vidioc_g_fmt_vid_cap(file, fh, f);
+ if (!ret)
+ v4l_print_pix_fmt(vfd, &f->fmt.pix);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ if (ops->vidioc_g_fmt_vid_cap_mplane)
+ ret = ops->vidioc_g_fmt_vid_cap_mplane(file,
+ fh, f);
+ if (!ret)
+ v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ if (likely(ops->vidioc_g_fmt_vid_overlay))
+ ret = ops->vidioc_g_fmt_vid_overlay(file,
+ fh, f);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ if (ops->vidioc_g_fmt_vid_out)
+ ret = ops->vidioc_g_fmt_vid_out(file, fh, f);
+ if (!ret)
+ v4l_print_pix_fmt(vfd, &f->fmt.pix);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ if (ops->vidioc_g_fmt_vid_out_mplane)
+ ret = ops->vidioc_g_fmt_vid_out_mplane(file,
+ fh, f);
+ if (!ret)
+ v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ if (likely(ops->vidioc_g_fmt_vid_out_overlay))
+ ret = ops->vidioc_g_fmt_vid_out_overlay(file,
+ fh, f);
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ if (likely(ops->vidioc_g_fmt_vbi_cap))
+ ret = ops->vidioc_g_fmt_vbi_cap(file, fh, f);
+ break;
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ if (likely(ops->vidioc_g_fmt_vbi_out))
+ ret = ops->vidioc_g_fmt_vbi_out(file, fh, f);
+ break;
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ if (likely(ops->vidioc_g_fmt_sliced_vbi_cap))
+ ret = ops->vidioc_g_fmt_sliced_vbi_cap(file,
+ fh, f);
+ break;
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ if (likely(ops->vidioc_g_fmt_sliced_vbi_out))
+ ret = ops->vidioc_g_fmt_sliced_vbi_out(file,
+ fh, f);
+ break;
+ case V4L2_BUF_TYPE_PRIVATE:
+ if (likely(ops->vidioc_g_fmt_type_private))
+ ret = ops->vidioc_g_fmt_type_private(file,
+ fh, f);
+ break;
+ }
+ if (unlikely(ret == -ENOTTY && have_fmt_ops(g)))
+ ret = -EINVAL;
+
+ break;
+ }
+ case VIDIOC_S_FMT:
+ {
+ struct v4l2_format *f = (struct v4l2_format *)arg;
+
+ if (!have_fmt_ops(s))
+ break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ ret = -EINVAL;
+
+ /* FIXME: Should be one dump per type */
+ dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names));
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ CLEAR_AFTER_FIELD(f, fmt.pix);
+ v4l_print_pix_fmt(vfd, &f->fmt.pix);
+ if (ops->vidioc_s_fmt_vid_cap)
+ ret = ops->vidioc_s_fmt_vid_cap(file, fh, f);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ CLEAR_AFTER_FIELD(f, fmt.pix_mp);
+ v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
+ if (ops->vidioc_s_fmt_vid_cap_mplane)
+ ret = ops->vidioc_s_fmt_vid_cap_mplane(file,
+ fh, f);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ CLEAR_AFTER_FIELD(f, fmt.win);
+ if (ops->vidioc_s_fmt_vid_overlay)
+ ret = ops->vidioc_s_fmt_vid_overlay(file,
+ fh, f);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ CLEAR_AFTER_FIELD(f, fmt.pix);
+ v4l_print_pix_fmt(vfd, &f->fmt.pix);
+ if (ops->vidioc_s_fmt_vid_out)
+ ret = ops->vidioc_s_fmt_vid_out(file, fh, f);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ CLEAR_AFTER_FIELD(f, fmt.pix_mp);
+ v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
+ if (ops->vidioc_s_fmt_vid_out_mplane)
+ ret = ops->vidioc_s_fmt_vid_out_mplane(file,
+ fh, f);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ CLEAR_AFTER_FIELD(f, fmt.win);
+ if (ops->vidioc_s_fmt_vid_out_overlay)
+ ret = ops->vidioc_s_fmt_vid_out_overlay(file,
+ fh, f);
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ CLEAR_AFTER_FIELD(f, fmt.vbi);
+ if (likely(ops->vidioc_s_fmt_vbi_cap))
+ ret = ops->vidioc_s_fmt_vbi_cap(file, fh, f);
+ break;
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ CLEAR_AFTER_FIELD(f, fmt.vbi);
+ if (likely(ops->vidioc_s_fmt_vbi_out))
+ ret = ops->vidioc_s_fmt_vbi_out(file, fh, f);
+ break;
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ CLEAR_AFTER_FIELD(f, fmt.sliced);
+ if (likely(ops->vidioc_s_fmt_sliced_vbi_cap))
+ ret = ops->vidioc_s_fmt_sliced_vbi_cap(file,
+ fh, f);
+ break;
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ CLEAR_AFTER_FIELD(f, fmt.sliced);
+ if (likely(ops->vidioc_s_fmt_sliced_vbi_out))
+ ret = ops->vidioc_s_fmt_sliced_vbi_out(file,
+ fh, f);
+
+ break;
+ case V4L2_BUF_TYPE_PRIVATE:
+ /* CLEAR_AFTER_FIELD(f, fmt.raw_data); <- does nothing */
+ if (likely(ops->vidioc_s_fmt_type_private))
+ ret = ops->vidioc_s_fmt_type_private(file,
+ fh, f);
+ break;
+ }
+ break;
+ }
+ case VIDIOC_TRY_FMT:
+ {
+ struct v4l2_format *f = (struct v4l2_format *)arg;
+
+ /* FIXME: Should be one dump per type */
+ dbgarg(cmd, "type=%s\n", prt_names(f->type,
+ v4l2_type_names));
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ CLEAR_AFTER_FIELD(f, fmt.pix);
+ if (ops->vidioc_try_fmt_vid_cap)
+ ret = ops->vidioc_try_fmt_vid_cap(file, fh, f);
+ if (!ret)
+ v4l_print_pix_fmt(vfd, &f->fmt.pix);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ CLEAR_AFTER_FIELD(f, fmt.pix_mp);
+ if (ops->vidioc_try_fmt_vid_cap_mplane)
+ ret = ops->vidioc_try_fmt_vid_cap_mplane(file,
+ fh, f);
+ if (!ret)
+ v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ CLEAR_AFTER_FIELD(f, fmt.win);
+ if (likely(ops->vidioc_try_fmt_vid_overlay))
+ ret = ops->vidioc_try_fmt_vid_overlay(file,
+ fh, f);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ CLEAR_AFTER_FIELD(f, fmt.pix);
+ if (ops->vidioc_try_fmt_vid_out)
+ ret = ops->vidioc_try_fmt_vid_out(file, fh, f);
+ if (!ret)
+ v4l_print_pix_fmt(vfd, &f->fmt.pix);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ CLEAR_AFTER_FIELD(f, fmt.pix_mp);
+ if (ops->vidioc_try_fmt_vid_out_mplane)
+ ret = ops->vidioc_try_fmt_vid_out_mplane(file,
+ fh, f);
+ if (!ret)
+ v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ CLEAR_AFTER_FIELD(f, fmt.win);
+ if (likely(ops->vidioc_try_fmt_vid_out_overlay))
+ ret = ops->vidioc_try_fmt_vid_out_overlay(file,
+ fh, f);
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ CLEAR_AFTER_FIELD(f, fmt.vbi);
+ if (likely(ops->vidioc_try_fmt_vbi_cap))
+ ret = ops->vidioc_try_fmt_vbi_cap(file, fh, f);
+ break;
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ CLEAR_AFTER_FIELD(f, fmt.vbi);
+ if (likely(ops->vidioc_try_fmt_vbi_out))
+ ret = ops->vidioc_try_fmt_vbi_out(file, fh, f);
+ break;
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ CLEAR_AFTER_FIELD(f, fmt.sliced);
+ if (likely(ops->vidioc_try_fmt_sliced_vbi_cap))
+ ret = ops->vidioc_try_fmt_sliced_vbi_cap(file,
+ fh, f);
+ break;
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ CLEAR_AFTER_FIELD(f, fmt.sliced);
+ if (likely(ops->vidioc_try_fmt_sliced_vbi_out))
+ ret = ops->vidioc_try_fmt_sliced_vbi_out(file,
+ fh, f);
+ break;
+ case V4L2_BUF_TYPE_PRIVATE:
+ /* CLEAR_AFTER_FIELD(f, fmt.raw_data); <- does nothing */
+ if (likely(ops->vidioc_try_fmt_type_private))
+ ret = ops->vidioc_try_fmt_type_private(file,
+ fh, f);
+ break;
+ }
+ if (unlikely(ret == -ENOTTY && have_fmt_ops(try)))
+ ret = -EINVAL;
+ break;
+ }
+ /* FIXME: Those buf reqs could be handled here,
+ with some changes on videobuf to allow its header to be included at
+ videodev2.h or being merged at videodev2.
+ */
+ case VIDIOC_REQBUFS:
+ {
+ struct v4l2_requestbuffers *p = arg;
+
+ if (!ops->vidioc_reqbufs)
+ break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ ret = check_fmt(ops, p->type);
+ if (ret)
+ break;
+
+ if (p->type < V4L2_BUF_TYPE_PRIVATE)
+ CLEAR_AFTER_FIELD(p, memory);
+
+ ret = ops->vidioc_reqbufs(file, fh, p);
+ dbgarg(cmd, "count=%d, type=%s, memory=%s\n",
+ p->count,
+ prt_names(p->type, v4l2_type_names),
+ prt_names(p->memory, v4l2_memory_names));
+ break;
+ }
+ case VIDIOC_QUERYBUF:
+ {
+ struct v4l2_buffer *p = arg;
+
+ if (!ops->vidioc_querybuf)
+ break;
+ ret = check_fmt(ops, p->type);
+ if (ret)
+ break;
+
+ ret = ops->vidioc_querybuf(file, fh, p);
+ if (!ret)
+ dbgbuf(cmd, vfd, p);
+ break;
+ }
+ case VIDIOC_QBUF:
+ {
+ struct v4l2_buffer *p = arg;
+
+ if (!ops->vidioc_qbuf)
+ break;
+ ret = check_fmt(ops, p->type);
+ if (ret)
+ break;
+
+ ret = ops->vidioc_qbuf(file, fh, p);
+ if (!ret)
+ dbgbuf(cmd, vfd, p);
+ break;
+ }
+ case VIDIOC_DQBUF:
+ {
+ struct v4l2_buffer *p = arg;
+
+ if (!ops->vidioc_dqbuf)
+ break;
+ ret = check_fmt(ops, p->type);
+ if (ret)
+ break;
+
+ ret = ops->vidioc_dqbuf(file, fh, p);
+ if (!ret)
+ dbgbuf(cmd, vfd, p);
+ break;
+ }
+ case VIDIOC_OVERLAY:
+ {
+ int *i = arg;
+
+ if (!ops->vidioc_overlay)
+ break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ dbgarg(cmd, "value=%d\n", *i);
+ ret = ops->vidioc_overlay(file, fh, *i);
+ break;
+ }
+ case VIDIOC_G_FBUF:
+ {
+ struct v4l2_framebuffer *p = arg;
+
+ if (!ops->vidioc_g_fbuf)
+ break;
+ ret = ops->vidioc_g_fbuf(file, fh, arg);
+ if (!ret) {
+ dbgarg(cmd, "capability=0x%x, flags=%d, base=0x%08lx\n",
+ p->capability, p->flags,
+ (unsigned long)p->base);
+ v4l_print_pix_fmt(vfd, &p->fmt);
+ }
+ break;
+ }
+ case VIDIOC_S_FBUF:
+ {
+ struct v4l2_framebuffer *p = arg;
+
+ if (!ops->vidioc_s_fbuf)
+ break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ dbgarg(cmd, "capability=0x%x, flags=%d, base=0x%08lx\n",
+ p->capability, p->flags, (unsigned long)p->base);
+ v4l_print_pix_fmt(vfd, &p->fmt);
+ ret = ops->vidioc_s_fbuf(file, fh, arg);
+ break;
+ }
+ case VIDIOC_STREAMON:
+ {
+ enum v4l2_buf_type i = *(int *)arg;
+
+ if (!ops->vidioc_streamon)
+ break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names));
+ ret = ops->vidioc_streamon(file, fh, i);
+ break;
+ }
+ case VIDIOC_STREAMOFF:
+ {
+ enum v4l2_buf_type i = *(int *)arg;
+
+ if (!ops->vidioc_streamoff)
+ break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names));
+ ret = ops->vidioc_streamoff(file, fh, i);
+ break;
+ }
+ /* ---------- tv norms ---------- */
+ case VIDIOC_ENUMSTD:
+ {
+ struct v4l2_standard *p = arg;
+ v4l2_std_id id = vfd->tvnorms, curr_id = 0;
+ unsigned int index = p->index, i, j = 0;
+ const char *descr = "";
+
+ if (id == 0)
+ break;
+ ret = -EINVAL;
+
+ /* Return norm array in a canonical way */
+ for (i = 0; i <= index && id; i++) {
+ /* last std value in the standards array is 0, so this
+ while always ends there since (id & 0) == 0. */
+ while ((id & standards[j].std) != standards[j].std)
+ j++;
+ curr_id = standards[j].std;
+ descr = standards[j].descr;
+ j++;
+ if (curr_id == 0)
+ break;
+ if (curr_id != V4L2_STD_PAL &&
+ curr_id != V4L2_STD_SECAM &&
+ curr_id != V4L2_STD_NTSC)
+ id &= ~curr_id;
+ }
+ if (i <= index)
+ break;
+
+ v4l2_video_std_construct(p, curr_id, descr);
+
+ dbgarg(cmd, "index=%d, id=0x%Lx, name=%s, fps=%d/%d, "
+ "framelines=%d\n", p->index,
+ (unsigned long long)p->id, p->name,
+ p->frameperiod.numerator,
+ p->frameperiod.denominator,
+ p->framelines);
+
+ ret = 0;
+ break;
+ }
+ case VIDIOC_G_STD:
+ {
+ v4l2_std_id *id = arg;
+
+ /* Calls the specific handler */
+ if (ops->vidioc_g_std)
+ ret = ops->vidioc_g_std(file, fh, id);
+ else if (vfd->current_norm) {
+ ret = 0;
+ *id = vfd->current_norm;
+ }
+
+ if (likely(!ret))
+ dbgarg(cmd, "std=0x%08Lx\n", (long long unsigned)*id);
+ break;
+ }
+ case VIDIOC_S_STD:
+ {
+ v4l2_std_id *id = arg, norm;
+
+ dbgarg(cmd, "std=%08Lx\n", (long long unsigned)*id);
+
+ if (!ops->vidioc_s_std)
+ break;
+
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ ret = -EINVAL;
+ norm = (*id) & vfd->tvnorms;
+ if (vfd->tvnorms && !norm) /* Check if std is supported */
+ break;
+
+ /* Calls the specific handler */
+ ret = ops->vidioc_s_std(file, fh, &norm);
+
+ /* Updates standard information */
+ if (ret >= 0)
+ vfd->current_norm = norm;
+ break;
+ }
+ case VIDIOC_QUERYSTD:
+ {
+ v4l2_std_id *p = arg;
+
+ if (!ops->vidioc_querystd)
+ break;
+ /*
+ * If nothing detected, it should return all supported
+ * Drivers just need to mask the std argument, in order
+ * to remove the standards that don't apply from the mask.
+ * This means that tuners, audio and video decoders can join
+ * their efforts to improve the standards detection
+ */
+ *p = vfd->tvnorms;
+ ret = ops->vidioc_querystd(file, fh, arg);
+ if (!ret)
+ dbgarg(cmd, "detected std=%08Lx\n",
+ (unsigned long long)*p);
+ break;
+ }
+ /* ------ input switching ---------- */
+ /* FIXME: Inputs can be handled inside videodev2 */
+ case VIDIOC_ENUMINPUT:
+ {
+ struct v4l2_input *p = arg;
+
+ /*
+ * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS &
+ * CAP_STD here based on ioctl handler provided by the
+ * driver. If the driver doesn't support these
+ * for a specific input, it must override these flags.
+ */
+ if (ops->vidioc_s_std)
+ p->capabilities |= V4L2_IN_CAP_STD;
+ if (ops->vidioc_s_dv_preset)
+ p->capabilities |= V4L2_IN_CAP_PRESETS;
+ if (ops->vidioc_s_dv_timings)
+ p->capabilities |= V4L2_IN_CAP_CUSTOM_TIMINGS;
+
+ if (!ops->vidioc_enum_input)
+ break;
+
+ ret = ops->vidioc_enum_input(file, fh, p);
+ if (!ret)
+ dbgarg(cmd, "index=%d, name=%s, type=%d, "
+ "audioset=%d, "
+ "tuner=%d, std=%08Lx, status=%d\n",
+ p->index, p->name, p->type, p->audioset,
+ p->tuner,
+ (unsigned long long)p->std,
+ p->status);
+ break;
+ }
+ case VIDIOC_G_INPUT:
+ {
+ unsigned int *i = arg;
+
+ if (!ops->vidioc_g_input)
+ break;
+ ret = ops->vidioc_g_input(file, fh, i);
+ if (!ret)
+ dbgarg(cmd, "value=%d\n", *i);
+ break;
+ }
+ case VIDIOC_S_INPUT:
+ {
+ unsigned int *i = arg;
+
+ if (!ops->vidioc_s_input)
+ break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ dbgarg(cmd, "value=%d\n", *i);
+ ret = ops->vidioc_s_input(file, fh, *i);
+ break;
+ }
+
+ /* ------ output switching ---------- */
+ case VIDIOC_ENUMOUTPUT:
+ {
+ struct v4l2_output *p = arg;
+
+ if (!ops->vidioc_enum_output)
+ break;
+
+ /*
+ * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS &
+ * CAP_STD here based on ioctl handler provided by the
+ * driver. If the driver doesn't support these
+ * for a specific output, it must override these flags.
+ */
+ if (ops->vidioc_s_std)
+ p->capabilities |= V4L2_OUT_CAP_STD;
+ if (ops->vidioc_s_dv_preset)
+ p->capabilities |= V4L2_OUT_CAP_PRESETS;
+ if (ops->vidioc_s_dv_timings)
+ p->capabilities |= V4L2_OUT_CAP_CUSTOM_TIMINGS;
+
+ ret = ops->vidioc_enum_output(file, fh, p);
+ if (!ret)
+ dbgarg(cmd, "index=%d, name=%s, type=%d, "
+ "audioset=0x%x, "
+ "modulator=%d, std=0x%08Lx\n",
+ p->index, p->name, p->type, p->audioset,
+ p->modulator, (unsigned long long)p->std);
+ break;
+ }
+ case VIDIOC_G_OUTPUT:
+ {
+ unsigned int *i = arg;
+
+ if (!ops->vidioc_g_output)
+ break;
+ ret = ops->vidioc_g_output(file, fh, i);
+ if (!ret)
+ dbgarg(cmd, "value=%d\n", *i);
+ break;
+ }
+ case VIDIOC_S_OUTPUT:
+ {
+ unsigned int *i = arg;
+
+ if (!ops->vidioc_s_output)
+ break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ dbgarg(cmd, "value=%d\n", *i);
+ ret = ops->vidioc_s_output(file, fh, *i);
+ break;
+ }
+
+ /* --- controls ---------------------------------------------- */
+ case VIDIOC_QUERYCTRL:
+ {
+ struct v4l2_queryctrl *p = arg;
+
+ if (vfh && vfh->ctrl_handler)
+ ret = v4l2_queryctrl(vfh->ctrl_handler, p);
+ else if (vfd->ctrl_handler)
+ ret = v4l2_queryctrl(vfd->ctrl_handler, p);
+ else if (ops->vidioc_queryctrl)
+ ret = ops->vidioc_queryctrl(file, fh, p);
+ else
+ break;
+ if (!ret)
+ dbgarg(cmd, "id=0x%x, type=%d, name=%s, min/max=%d/%d, "
+ "step=%d, default=%d, flags=0x%08x\n",
+ p->id, p->type, p->name,
+ p->minimum, p->maximum,
+ p->step, p->default_value, p->flags);
+ else
+ dbgarg(cmd, "id=0x%x\n", p->id);
+ break;
+ }
+ case VIDIOC_G_CTRL:
+ {
+ struct v4l2_control *p = arg;
+
+ if (vfh && vfh->ctrl_handler)
+ ret = v4l2_g_ctrl(vfh->ctrl_handler, p);
+ else if (vfd->ctrl_handler)
+ ret = v4l2_g_ctrl(vfd->ctrl_handler, p);
+ else if (ops->vidioc_g_ctrl)
+ ret = ops->vidioc_g_ctrl(file, fh, p);
+ else if (ops->vidioc_g_ext_ctrls) {
+ struct v4l2_ext_controls ctrls;
+ struct v4l2_ext_control ctrl;
+
+ ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id);
+ ctrls.count = 1;
+ ctrls.controls = &ctrl;
+ ctrl.id = p->id;
+ ctrl.value = p->value;
+ if (check_ext_ctrls(&ctrls, 1)) {
+ ret = ops->vidioc_g_ext_ctrls(file, fh, &ctrls);
+ if (ret == 0)
+ p->value = ctrl.value;
+ }
+ } else
+ break;
+ if (!ret)
+ dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value);
+ else
+ dbgarg(cmd, "id=0x%x\n", p->id);
+ break;
+ }
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *p = arg;
+ struct v4l2_ext_controls ctrls;
+ struct v4l2_ext_control ctrl;
+
+ if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler &&
+ !ops->vidioc_s_ctrl && !ops->vidioc_s_ext_ctrls)
+ break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+
+ dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value);
+
+ if (vfh && vfh->ctrl_handler) {
+ ret = v4l2_s_ctrl(vfh, vfh->ctrl_handler, p);
+ break;
+ }
+ if (vfd->ctrl_handler) {
+ ret = v4l2_s_ctrl(NULL, vfd->ctrl_handler, p);
+ break;
+ }
+ if (ops->vidioc_s_ctrl) {
+ ret = ops->vidioc_s_ctrl(file, fh, p);
+ break;
+ }
+ if (!ops->vidioc_s_ext_ctrls)
+ break;
+
+ ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id);
+ ctrls.count = 1;
+ ctrls.controls = &ctrl;
+ ctrl.id = p->id;
+ ctrl.value = p->value;
+ if (check_ext_ctrls(&ctrls, 1))
+ ret = ops->vidioc_s_ext_ctrls(file, fh, &ctrls);
+ else
+ ret = -EINVAL;
+ break;
+ }
+ case VIDIOC_G_EXT_CTRLS:
+ {
+ struct v4l2_ext_controls *p = arg;
+
+ p->error_idx = p->count;
+ if (vfh && vfh->ctrl_handler)
+ ret = v4l2_g_ext_ctrls(vfh->ctrl_handler, p);
+ else if (vfd->ctrl_handler)
+ ret = v4l2_g_ext_ctrls(vfd->ctrl_handler, p);
+ else if (ops->vidioc_g_ext_ctrls)
+ ret = check_ext_ctrls(p, 0) ?
+ ops->vidioc_g_ext_ctrls(file, fh, p) :
+ -EINVAL;
+ else
+ break;
+ v4l_print_ext_ctrls(cmd, vfd, p, !ret);
+ break;
+ }
+ case VIDIOC_S_EXT_CTRLS:
+ {
+ struct v4l2_ext_controls *p = arg;
+
+ p->error_idx = p->count;
+ if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler &&
+ !ops->vidioc_s_ext_ctrls)
+ break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ v4l_print_ext_ctrls(cmd, vfd, p, 1);
+ if (vfh && vfh->ctrl_handler)
+ ret = v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, p);
+ else if (vfd->ctrl_handler)
+ ret = v4l2_s_ext_ctrls(NULL, vfd->ctrl_handler, p);
+ else if (check_ext_ctrls(p, 0))
+ ret = ops->vidioc_s_ext_ctrls(file, fh, p);
+ else
+ ret = -EINVAL;
+ break;
+ }
+ case VIDIOC_TRY_EXT_CTRLS:
+ {
+ struct v4l2_ext_controls *p = arg;
+
+ p->error_idx = p->count;
+ if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler &&
+ !ops->vidioc_try_ext_ctrls)
+ break;
+ v4l_print_ext_ctrls(cmd, vfd, p, 1);
+ if (vfh && vfh->ctrl_handler)
+ ret = v4l2_try_ext_ctrls(vfh->ctrl_handler, p);
+ else if (vfd->ctrl_handler)
+ ret = v4l2_try_ext_ctrls(vfd->ctrl_handler, p);
+ else if (check_ext_ctrls(p, 0))
+ ret = ops->vidioc_try_ext_ctrls(file, fh, p);
+ else
+ ret = -EINVAL;
+ break;
+ }
+ case VIDIOC_QUERYMENU:
+ {
+ struct v4l2_querymenu *p = arg;
+
+ if (vfh && vfh->ctrl_handler)
+ ret = v4l2_querymenu(vfh->ctrl_handler, p);
+ else if (vfd->ctrl_handler)
+ ret = v4l2_querymenu(vfd->ctrl_handler, p);
+ else if (ops->vidioc_querymenu)
+ ret = ops->vidioc_querymenu(file, fh, p);
+ else
+ break;
+ if (!ret)
+ dbgarg(cmd, "id=0x%x, index=%d, name=%s\n",
+ p->id, p->index, p->name);
+ else
+ dbgarg(cmd, "id=0x%x, index=%d\n",
+ p->id, p->index);
+ break;
+ }
+ /* --- audio ---------------------------------------------- */
+ case VIDIOC_ENUMAUDIO:
+ {
+ struct v4l2_audio *p = arg;
+
+ if (!ops->vidioc_enumaudio)
+ break;
+ ret = ops->vidioc_enumaudio(file, fh, p);
+ if (!ret)
+ dbgarg(cmd, "index=%d, name=%s, capability=0x%x, "
+ "mode=0x%x\n", p->index, p->name,
+ p->capability, p->mode);
+ else
+ dbgarg(cmd, "index=%d\n", p->index);
+ break;
+ }
+ case VIDIOC_G_AUDIO:
+ {
+ struct v4l2_audio *p = arg;
+
+ if (!ops->vidioc_g_audio)
+ break;
+
+ ret = ops->vidioc_g_audio(file, fh, p);
+ if (!ret)
+ dbgarg(cmd, "index=%d, name=%s, capability=0x%x, "
+ "mode=0x%x\n", p->index,
+ p->name, p->capability, p->mode);
+ else
+ dbgarg(cmd, "index=%d\n", p->index);
+ break;
+ }
+ case VIDIOC_S_AUDIO:
+ {
+ struct v4l2_audio *p = arg;
+
+ if (!ops->vidioc_s_audio)
+ break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ dbgarg(cmd, "index=%d, name=%s, capability=0x%x, "
+ "mode=0x%x\n", p->index, p->name,
+ p->capability, p->mode);
+ ret = ops->vidioc_s_audio(file, fh, p);
+ break;
+ }
+ case VIDIOC_ENUMAUDOUT:
+ {
+ struct v4l2_audioout *p = arg;
+
+ if (!ops->vidioc_enumaudout)
+ break;
+ dbgarg(cmd, "Enum for index=%d\n", p->index);
+ ret = ops->vidioc_enumaudout(file, fh, p);
+ if (!ret)
+ dbgarg2("index=%d, name=%s, capability=%d, "
+ "mode=%d\n", p->index, p->name,
+ p->capability, p->mode);
+ break;
+ }
+ case VIDIOC_G_AUDOUT:
+ {
+ struct v4l2_audioout *p = arg;
+
+ if (!ops->vidioc_g_audout)
+ break;
+
+ ret = ops->vidioc_g_audout(file, fh, p);
+ if (!ret)
+ dbgarg2("index=%d, name=%s, capability=%d, "
+ "mode=%d\n", p->index, p->name,
+ p->capability, p->mode);
+ break;
+ }
+ case VIDIOC_S_AUDOUT:
+ {
+ struct v4l2_audioout *p = arg;
+
+ if (!ops->vidioc_s_audout)
+ break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ dbgarg(cmd, "index=%d, name=%s, capability=%d, "
+ "mode=%d\n", p->index, p->name,
+ p->capability, p->mode);
+
+ ret = ops->vidioc_s_audout(file, fh, p);
+ break;
+ }
+ case VIDIOC_G_MODULATOR:
+ {
+ struct v4l2_modulator *p = arg;
+
+ if (!ops->vidioc_g_modulator)
+ break;
+ ret = ops->vidioc_g_modulator(file, fh, p);
+ if (!ret)
+ dbgarg(cmd, "index=%d, name=%s, "
+ "capability=%d, rangelow=%d,"
+ " rangehigh=%d, txsubchans=%d\n",
+ p->index, p->name, p->capability,
+ p->rangelow, p->rangehigh,
+ p->txsubchans);
+ break;
+ }
+ case VIDIOC_S_MODULATOR:
+ {
+ struct v4l2_modulator *p = arg;
+
+ if (!ops->vidioc_s_modulator)
+ break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ dbgarg(cmd, "index=%d, name=%s, capability=%d, "
+ "rangelow=%d, rangehigh=%d, txsubchans=%d\n",
+ p->index, p->name, p->capability, p->rangelow,
+ p->rangehigh, p->txsubchans);
+ ret = ops->vidioc_s_modulator(file, fh, p);
+ break;
+ }
+ case VIDIOC_G_CROP:
+ {
+ struct v4l2_crop *p = arg;
+
+ if (!ops->vidioc_g_crop && !ops->vidioc_g_selection)
+ break;
+
+ dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
+
+ if (ops->vidioc_g_crop) {
+ ret = ops->vidioc_g_crop(file, fh, p);
+ } else {
+ /* simulate capture crop using selection api */
+ struct v4l2_selection s = {
+ .type = p->type,
+ };
+
+ /* crop means compose for output devices */
+ if (V4L2_TYPE_IS_OUTPUT(p->type))
+ s.target = V4L2_SEL_TGT_COMPOSE_ACTIVE;
+ else
+ s.target = V4L2_SEL_TGT_CROP_ACTIVE;
+
+ ret = ops->vidioc_g_selection(file, fh, &s);
+
+ /* copying results to old structure on success */
+ if (!ret)
+ p->c = s.r;
+ }
+
+ if (!ret)
+ dbgrect(vfd, "", &p->c);
+ break;
+ }
+ case VIDIOC_S_CROP:
+ {
+ struct v4l2_crop *p = arg;
+
+ if (!ops->vidioc_s_crop && !ops->vidioc_s_selection)
+ break;
+
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
+ dbgrect(vfd, "", &p->c);
+
+ if (ops->vidioc_s_crop) {
+ ret = ops->vidioc_s_crop(file, fh, p);
+ } else {
+ /* simulate capture crop using selection api */
+ struct v4l2_selection s = {
+ .type = p->type,
+ .r = p->c,
+ };
+
+ /* crop means compose for output devices */
+ if (V4L2_TYPE_IS_OUTPUT(p->type))
+ s.target = V4L2_SEL_TGT_COMPOSE_ACTIVE;
+ else
+ s.target = V4L2_SEL_TGT_CROP_ACTIVE;
+
+ ret = ops->vidioc_s_selection(file, fh, &s);
+ }
+ break;
+ }
+ case VIDIOC_G_SELECTION:
+ {
+ struct v4l2_selection *p = arg;
+
+ if (!ops->vidioc_g_selection)
+ break;
+
+ dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
+
+ ret = ops->vidioc_g_selection(file, fh, p);
+ if (!ret)
+ dbgrect(vfd, "", &p->r);
+ break;
+ }
+ case VIDIOC_S_SELECTION:
+ {
+ struct v4l2_selection *p = arg;
+
+ if (!ops->vidioc_s_selection)
+ break;
+
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+
+ dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
+ dbgrect(vfd, "", &p->r);
+
+ ret = ops->vidioc_s_selection(file, fh, p);
+ break;
+ }
+ case VIDIOC_CROPCAP:
+ {
+ struct v4l2_cropcap *p = arg;
+
+ /*FIXME: Should also show v4l2_fract pixelaspect */
+ if (!ops->vidioc_cropcap && !ops->vidioc_g_selection)
+ break;
+
+ dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
+ if (ops->vidioc_cropcap) {
+ ret = ops->vidioc_cropcap(file, fh, p);
+ } else {
+ struct v4l2_selection s = { .type = p->type };
+
+ /* obtaining bounds */
+ if (V4L2_TYPE_IS_OUTPUT(p->type))
+ s.target = V4L2_SEL_TGT_COMPOSE_BOUNDS;
+ else
+ s.target = V4L2_SEL_TGT_CROP_BOUNDS;
+
+ ret = ops->vidioc_g_selection(file, fh, &s);
+ if (ret)
+ break;
+ p->bounds = s.r;
+
+ /* obtaining defrect */
+ if (V4L2_TYPE_IS_OUTPUT(p->type))
+ s.target = V4L2_SEL_TGT_COMPOSE_DEFAULT;
+ else
+ s.target = V4L2_SEL_TGT_CROP_DEFAULT;
+
+ ret = ops->vidioc_g_selection(file, fh, &s);
+ if (ret)
+ break;
+ p->defrect = s.r;
+
+ /* setting trivial pixelaspect */
+ p->pixelaspect.numerator = 1;
+ p->pixelaspect.denominator = 1;
+ }
+
+ if (!ret) {
+ dbgrect(vfd, "bounds ", &p->bounds);
+ dbgrect(vfd, "defrect ", &p->defrect);
+ }
+ break;
+ }
+ case VIDIOC_G_JPEGCOMP:
+ {
+ struct v4l2_jpegcompression *p = arg;
+
+ if (!ops->vidioc_g_jpegcomp)
+ break;
+
+ ret = ops->vidioc_g_jpegcomp(file, fh, p);
+ if (!ret)
+ dbgarg(cmd, "quality=%d, APPn=%d, "
+ "APP_len=%d, COM_len=%d, "
+ "jpeg_markers=%d\n",
+ p->quality, p->APPn, p->APP_len,
+ p->COM_len, p->jpeg_markers);
+ break;
+ }
+ case VIDIOC_S_JPEGCOMP:
+ {
+ struct v4l2_jpegcompression *p = arg;
+
+ if (!ops->vidioc_g_jpegcomp)
+ break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ dbgarg(cmd, "quality=%d, APPn=%d, APP_len=%d, "
+ "COM_len=%d, jpeg_markers=%d\n",
+ p->quality, p->APPn, p->APP_len,
+ p->COM_len, p->jpeg_markers);
+ ret = ops->vidioc_s_jpegcomp(file, fh, p);
+ break;
+ }
+ case VIDIOC_G_ENC_INDEX:
+ {
+ struct v4l2_enc_idx *p = arg;
+
+ if (!ops->vidioc_g_enc_index)
+ break;
+ ret = ops->vidioc_g_enc_index(file, fh, p);
+ if (!ret)
+ dbgarg(cmd, "entries=%d, entries_cap=%d\n",
+ p->entries, p->entries_cap);
+ break;
+ }
+ case VIDIOC_ENCODER_CMD:
+ {
+ struct v4l2_encoder_cmd *p = arg;
+
+ if (!ops->vidioc_encoder_cmd)
+ break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ ret = ops->vidioc_encoder_cmd(file, fh, p);
+ if (!ret)
+ dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags);
+ break;
+ }
+ case VIDIOC_TRY_ENCODER_CMD:
+ {
+ struct v4l2_encoder_cmd *p = arg;
+
+ if (!ops->vidioc_try_encoder_cmd)
+ break;
+ ret = ops->vidioc_try_encoder_cmd(file, fh, p);
+ if (!ret)
+ dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags);
+ break;
+ }
+ case VIDIOC_DECODER_CMD:
+ {
+ struct v4l2_decoder_cmd *p = arg;
+
+ if (!ops->vidioc_decoder_cmd)
+ break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ ret = ops->vidioc_decoder_cmd(file, fh, p);
+ if (!ret)
+ dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags);
+ break;
+ }
+ case VIDIOC_TRY_DECODER_CMD:
+ {
+ struct v4l2_decoder_cmd *p = arg;
+
+ if (!ops->vidioc_try_decoder_cmd)
+ break;
+ ret = ops->vidioc_try_decoder_cmd(file, fh, p);
+ if (!ret)
+ dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags);
+ break;
+ }
+ case VIDIOC_G_PARM:
+ {
+ struct v4l2_streamparm *p = arg;
+
+ if (!ops->vidioc_g_parm && !vfd->current_norm)
+ break;
+ if (ops->vidioc_g_parm) {
+ ret = check_fmt(ops, p->type);
+ if (ret)
+ break;
+ ret = ops->vidioc_g_parm(file, fh, p);
+ } else {
+ v4l2_std_id std = vfd->current_norm;
+
+ ret = -EINVAL;
+ if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ break;
+
+ ret = 0;
+ if (ops->vidioc_g_std)
+ ret = ops->vidioc_g_std(file, fh, &std);
+ if (ret == 0)
+ v4l2_video_std_frame_period(std,
+ &p->parm.capture.timeperframe);
+ }
+
+ dbgarg(cmd, "type=%d\n", p->type);
+ break;
+ }
+ case VIDIOC_S_PARM:
+ {
+ struct v4l2_streamparm *p = arg;
+
+ if (!ops->vidioc_s_parm)
+ break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ ret = check_fmt(ops, p->type);
+ if (ret)
+ break;
+
+ dbgarg(cmd, "type=%d\n", p->type);
+ ret = ops->vidioc_s_parm(file, fh, p);
+ break;
+ }
+ case VIDIOC_G_TUNER:
+ {
+ struct v4l2_tuner *p = arg;
+
+ if (!ops->vidioc_g_tuner)
+ break;
+
+ p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+ V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ ret = ops->vidioc_g_tuner(file, fh, p);
+ if (!ret)
+ dbgarg(cmd, "index=%d, name=%s, type=%d, "
+ "capability=0x%x, rangelow=%d, "
+ "rangehigh=%d, signal=%d, afc=%d, "
+ "rxsubchans=0x%x, audmode=%d\n",
+ p->index, p->name, p->type,
+ p->capability, p->rangelow,
+ p->rangehigh, p->signal, p->afc,
+ p->rxsubchans, p->audmode);
+ break;
+ }
+ case VIDIOC_S_TUNER:
+ {
+ struct v4l2_tuner *p = arg;
+
+ if (!ops->vidioc_s_tuner)
+ break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+ V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ dbgarg(cmd, "index=%d, name=%s, type=%d, "
+ "capability=0x%x, rangelow=%d, "
+ "rangehigh=%d, signal=%d, afc=%d, "
+ "rxsubchans=0x%x, audmode=%d\n",
+ p->index, p->name, p->type,
+ p->capability, p->rangelow,
+ p->rangehigh, p->signal, p->afc,
+ p->rxsubchans, p->audmode);
+ ret = ops->vidioc_s_tuner(file, fh, p);
+ break;
+ }
+ case VIDIOC_G_FREQUENCY:
+ {
+ struct v4l2_frequency *p = arg;
+
+ if (!ops->vidioc_g_frequency)
+ break;
+
+ p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+ V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ ret = ops->vidioc_g_frequency(file, fh, p);
+ if (!ret)
+ dbgarg(cmd, "tuner=%d, type=%d, frequency=%d\n",
+ p->tuner, p->type, p->frequency);
+ break;
+ }
+ case VIDIOC_S_FREQUENCY:
+ {
+ struct v4l2_frequency *p = arg;
+ enum v4l2_tuner_type type;
+
+ if (!ops->vidioc_s_frequency)
+ break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+ V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ dbgarg(cmd, "tuner=%d, type=%d, frequency=%d\n",
+ p->tuner, p->type, p->frequency);
+ if (p->type != type)
+ ret = -EINVAL;
+ else
+ ret = ops->vidioc_s_frequency(file, fh, p);
+ break;
+ }
+ case VIDIOC_G_SLICED_VBI_CAP:
+ {
+ struct v4l2_sliced_vbi_cap *p = arg;
+
+ if (!ops->vidioc_g_sliced_vbi_cap)
+ break;
+
+ /* Clear up to type, everything after type is zerod already */
+ memset(p, 0, offsetof(struct v4l2_sliced_vbi_cap, type));
+
+ dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
+ ret = ops->vidioc_g_sliced_vbi_cap(file, fh, p);
+ if (!ret)
+ dbgarg2("service_set=%d\n", p->service_set);
+ break;
+ }
+ case VIDIOC_LOG_STATUS:
+ {
+ if (!ops->vidioc_log_status)
+ break;
+ if (vfd->v4l2_dev)
+ pr_info("%s: ================= START STATUS =================\n",
+ vfd->v4l2_dev->name);
+ ret = ops->vidioc_log_status(file, fh);
+ if (vfd->v4l2_dev)
+ pr_info("%s: ================== END STATUS ==================\n",
+ vfd->v4l2_dev->name);
+ break;
+ }
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ case VIDIOC_DBG_G_REGISTER:
+ {
+ struct v4l2_dbg_register *p = arg;
+
+ if (ops->vidioc_g_register) {
+ if (!capable(CAP_SYS_ADMIN))
+ ret = -EPERM;
+ else
+ ret = ops->vidioc_g_register(file, fh, p);
+ }
+ break;
+ }
+ case VIDIOC_DBG_S_REGISTER:
+ {
+ struct v4l2_dbg_register *p = arg;
+
+ if (ops->vidioc_s_register) {
+ if (!capable(CAP_SYS_ADMIN))
+ ret = -EPERM;
+ else
+ ret = ops->vidioc_s_register(file, fh, p);
+ }
+ break;
+ }
+#endif
+ case VIDIOC_DBG_G_CHIP_IDENT:
+ {
+ struct v4l2_dbg_chip_ident *p = arg;
+
+ if (!ops->vidioc_g_chip_ident)
+ break;
+ p->ident = V4L2_IDENT_NONE;
+ p->revision = 0;
+ ret = ops->vidioc_g_chip_ident(file, fh, p);
+ if (!ret)
+ dbgarg(cmd, "chip_ident=%u, revision=0x%x\n", p->ident, p->revision);
+ break;
+ }
+ case VIDIOC_S_HW_FREQ_SEEK:
+ {
+ struct v4l2_hw_freq_seek *p = arg;
+ enum v4l2_tuner_type type;
+
+ if (!ops->vidioc_s_hw_freq_seek)
+ break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+ V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ dbgarg(cmd,
+ "tuner=%u, type=%u, seek_upward=%u, wrap_around=%u, spacing=%u\n",
+ p->tuner, p->type, p->seek_upward, p->wrap_around, p->spacing);
+ if (p->type != type)
+ ret = -EINVAL;
+ else
+ ret = ops->vidioc_s_hw_freq_seek(file, fh, p);
+ break;
+ }
+ case VIDIOC_ENUM_FRAMESIZES:
+ {
+ struct v4l2_frmsizeenum *p = arg;
+
+ if (!ops->vidioc_enum_framesizes)
+ break;
+
+ ret = ops->vidioc_enum_framesizes(file, fh, p);
+ dbgarg(cmd,
+ "index=%d, pixelformat=%c%c%c%c, type=%d ",
+ p->index,
+ (p->pixel_format & 0xff),
+ (p->pixel_format >> 8) & 0xff,
+ (p->pixel_format >> 16) & 0xff,
+ (p->pixel_format >> 24) & 0xff,
+ p->type);
+ switch (p->type) {
+ case V4L2_FRMSIZE_TYPE_DISCRETE:
+ dbgarg3("width = %d, height=%d\n",
+ p->discrete.width, p->discrete.height);
+ break;
+ case V4L2_FRMSIZE_TYPE_STEPWISE:
+ dbgarg3("min %dx%d, max %dx%d, step %dx%d\n",
+ p->stepwise.min_width, p->stepwise.min_height,
+ p->stepwise.step_width, p->stepwise.step_height,
+ p->stepwise.max_width, p->stepwise.max_height);
+ break;
+ case V4L2_FRMSIZE_TYPE_CONTINUOUS:
+ dbgarg3("continuous\n");
+ break;
+ default:
+ dbgarg3("- Unknown type!\n");
+ }
+
+ break;
+ }
+ case VIDIOC_ENUM_FRAMEINTERVALS:
+ {
+ struct v4l2_frmivalenum *p = arg;
+
+ if (!ops->vidioc_enum_frameintervals)
+ break;
+
+ ret = ops->vidioc_enum_frameintervals(file, fh, p);
+ dbgarg(cmd,
+ "index=%d, pixelformat=%d, width=%d, height=%d, type=%d ",
+ p->index, p->pixel_format,
+ p->width, p->height, p->type);
+ switch (p->type) {
+ case V4L2_FRMIVAL_TYPE_DISCRETE:
+ dbgarg2("fps=%d/%d\n",
+ p->discrete.numerator,
+ p->discrete.denominator);
+ break;
+ case V4L2_FRMIVAL_TYPE_STEPWISE:
+ dbgarg2("min=%d/%d, max=%d/%d, step=%d/%d\n",
+ p->stepwise.min.numerator,
+ p->stepwise.min.denominator,
+ p->stepwise.max.numerator,
+ p->stepwise.max.denominator,
+ p->stepwise.step.numerator,
+ p->stepwise.step.denominator);
+ break;
+ case V4L2_FRMIVAL_TYPE_CONTINUOUS:
+ dbgarg2("continuous\n");
+ break;
+ default:
+ dbgarg2("- Unknown type!\n");
+ }
+ break;
+ }
+ case VIDIOC_ENUM_DV_PRESETS:
+ {
+ struct v4l2_dv_enum_preset *p = arg;
+
+ if (!ops->vidioc_enum_dv_presets)
+ break;
+
+ ret = ops->vidioc_enum_dv_presets(file, fh, p);
+ if (!ret)
+ dbgarg(cmd,
+ "index=%d, preset=%d, name=%s, width=%d,"
+ " height=%d ",
+ p->index, p->preset, p->name, p->width,
+ p->height);
+ break;
+ }
+ case VIDIOC_S_DV_PRESET:
+ {
+ struct v4l2_dv_preset *p = arg;
+
+ if (!ops->vidioc_s_dv_preset)
+ break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+
+ dbgarg(cmd, "preset=%d\n", p->preset);
+ ret = ops->vidioc_s_dv_preset(file, fh, p);
+ break;
+ }
+ case VIDIOC_G_DV_PRESET:
+ {
+ struct v4l2_dv_preset *p = arg;
+
+ if (!ops->vidioc_g_dv_preset)
+ break;
+
+ ret = ops->vidioc_g_dv_preset(file, fh, p);
+ if (!ret)
+ dbgarg(cmd, "preset=%d\n", p->preset);
+ break;
+ }
+ case VIDIOC_QUERY_DV_PRESET:
+ {
+ struct v4l2_dv_preset *p = arg;
+
+ if (!ops->vidioc_query_dv_preset)
+ break;
+
+ ret = ops->vidioc_query_dv_preset(file, fh, p);
+ if (!ret)
+ dbgarg(cmd, "preset=%d\n", p->preset);
+ break;
+ }
+ case VIDIOC_S_DV_TIMINGS:
+ {
+ struct v4l2_dv_timings *p = arg;
+
+ if (!ops->vidioc_s_dv_timings)
+ break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+
+ switch (p->type) {
+ case V4L2_DV_BT_656_1120:
+ dbgarg2("bt-656/1120:interlaced=%d, pixelclock=%lld,"
+ " width=%d, height=%d, polarities=%x,"
+ " hfrontporch=%d, hsync=%d, hbackporch=%d,"
+ " vfrontporch=%d, vsync=%d, vbackporch=%d,"
+ " il_vfrontporch=%d, il_vsync=%d,"
+ " il_vbackporch=%d\n",
+ p->bt.interlaced, p->bt.pixelclock,
+ p->bt.width, p->bt.height, p->bt.polarities,
+ p->bt.hfrontporch, p->bt.hsync,
+ p->bt.hbackporch, p->bt.vfrontporch,
+ p->bt.vsync, p->bt.vbackporch,
+ p->bt.il_vfrontporch, p->bt.il_vsync,
+ p->bt.il_vbackporch);
+ ret = ops->vidioc_s_dv_timings(file, fh, p);
+ break;
+ default:
+ dbgarg2("Unknown type %d!\n", p->type);
+ break;
+ }
+ break;
+ }
+ case VIDIOC_G_DV_TIMINGS:
+ {
+ struct v4l2_dv_timings *p = arg;
+
+ if (!ops->vidioc_g_dv_timings)
+ break;
+
+ ret = ops->vidioc_g_dv_timings(file, fh, p);
+ if (!ret) {
+ switch (p->type) {
+ case V4L2_DV_BT_656_1120:
+ dbgarg2("bt-656/1120:interlaced=%d,"
+ " pixelclock=%lld,"
+ " width=%d, height=%d, polarities=%x,"
+ " hfrontporch=%d, hsync=%d,"
+ " hbackporch=%d, vfrontporch=%d,"
+ " vsync=%d, vbackporch=%d,"
+ " il_vfrontporch=%d, il_vsync=%d,"
+ " il_vbackporch=%d\n",
+ p->bt.interlaced, p->bt.pixelclock,
+ p->bt.width, p->bt.height,
+ p->bt.polarities, p->bt.hfrontporch,
+ p->bt.hsync, p->bt.hbackporch,
+ p->bt.vfrontporch, p->bt.vsync,
+ p->bt.vbackporch, p->bt.il_vfrontporch,
+ p->bt.il_vsync, p->bt.il_vbackporch);
+ break;
+ default:
+ dbgarg2("Unknown type %d!\n", p->type);
+ break;
+ }
+ }
+ break;
+ }
+ case VIDIOC_DQEVENT:
+ {
+ struct v4l2_event *ev = arg;
+
+ if (!ops->vidioc_subscribe_event)
+ break;
+
+ ret = v4l2_event_dequeue(fh, ev, file->f_flags & O_NONBLOCK);
+ if (ret < 0) {
+ dbgarg(cmd, "no pending events?");
+ break;
+ }
+ dbgarg(cmd,
+ "pending=%d, type=0x%8.8x, sequence=%d, "
+ "timestamp=%lu.%9.9lu ",
+ ev->pending, ev->type, ev->sequence,
+ ev->timestamp.tv_sec, ev->timestamp.tv_nsec);
+ break;
+ }
+ case VIDIOC_SUBSCRIBE_EVENT:
+ {
+ struct v4l2_event_subscription *sub = arg;
+
+ if (!ops->vidioc_subscribe_event)
+ break;
+
+ ret = ops->vidioc_subscribe_event(fh, sub);
+ if (ret < 0) {
+ dbgarg(cmd, "failed, ret=%ld", ret);
+ break;
+ }
+ dbgarg(cmd, "type=0x%8.8x", sub->type);
+ break;
+ }
+ case VIDIOC_UNSUBSCRIBE_EVENT:
+ {
+ struct v4l2_event_subscription *sub = arg;
+
+ if (!ops->vidioc_unsubscribe_event)
+ break;
+
+ ret = ops->vidioc_unsubscribe_event(fh, sub);
+ if (ret < 0) {
+ dbgarg(cmd, "failed, ret=%ld", ret);
+ break;
+ }
+ dbgarg(cmd, "type=0x%8.8x", sub->type);
+ break;
+ }
+ case VIDIOC_CREATE_BUFS:
+ {
+ struct v4l2_create_buffers *create = arg;
+
+ if (!ops->vidioc_create_bufs)
+ break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ ret = check_fmt(ops, create->format.type);
+ if (ret)
+ break;
+
+ ret = ops->vidioc_create_bufs(file, fh, create);
+
+ dbgarg(cmd, "count=%d @ %d\n", create->count, create->index);
+ break;
+ }
+ case VIDIOC_PREPARE_BUF:
+ {
+ struct v4l2_buffer *b = arg;
+
+ if (!ops->vidioc_prepare_buf)
+ break;
+ ret = check_fmt(ops, b->type);
+ if (ret)
+ break;
+
+ ret = ops->vidioc_prepare_buf(file, fh, b);
+
+ dbgarg(cmd, "index=%d", b->index);
+ break;
+ }
+ default:
+ if (!ops->vidioc_default)
+ break;
+ ret = ops->vidioc_default(file, fh, ret_prio >= 0, cmd, arg);
+ break;
+ } /* switch */
+
+ if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) {
+ if (ret < 0) {
+ v4l_print_ioctl(vfd->name, cmd);
+ printk(KERN_CONT " error %ld\n", ret);
+ }
+ }
+
+ return ret;
+}
+
+/* In some cases, only a few fields are used as input, i.e. when the app sets
+ * "index" and then the driver fills in the rest of the structure for the thing
+ * with that index. We only need to copy up the first non-input field. */
+static unsigned long cmd_input_size(unsigned int cmd)
+{
+ /* Size of structure up to and including 'field' */
+#define CMDINSIZE(cmd, type, field) \
+ case VIDIOC_##cmd: \
+ return offsetof(struct v4l2_##type, field) + \
+ sizeof(((struct v4l2_##type *)0)->field);
+
+ switch (cmd) {
+ CMDINSIZE(ENUM_FMT, fmtdesc, type);
+ CMDINSIZE(G_FMT, format, type);
+ CMDINSIZE(QUERYBUF, buffer, length);
+ CMDINSIZE(G_PARM, streamparm, type);
+ CMDINSIZE(ENUMSTD, standard, index);
+ CMDINSIZE(ENUMINPUT, input, index);
+ CMDINSIZE(G_CTRL, control, id);
+ CMDINSIZE(G_TUNER, tuner, index);
+ CMDINSIZE(QUERYCTRL, queryctrl, id);
+ CMDINSIZE(QUERYMENU, querymenu, index);
+ CMDINSIZE(ENUMOUTPUT, output, index);
+ CMDINSIZE(G_MODULATOR, modulator, index);
+ CMDINSIZE(G_FREQUENCY, frequency, tuner);
+ CMDINSIZE(CROPCAP, cropcap, type);
+ CMDINSIZE(G_CROP, crop, type);
+ CMDINSIZE(ENUMAUDIO, audio, index);
+ CMDINSIZE(ENUMAUDOUT, audioout, index);
+ CMDINSIZE(ENCODER_CMD, encoder_cmd, flags);
+ CMDINSIZE(TRY_ENCODER_CMD, encoder_cmd, flags);
+ CMDINSIZE(G_SLICED_VBI_CAP, sliced_vbi_cap, type);
+ CMDINSIZE(ENUM_FRAMESIZES, frmsizeenum, pixel_format);
+ CMDINSIZE(ENUM_FRAMEINTERVALS, frmivalenum, height);
+ default:
+ return _IOC_SIZE(cmd);
+ }
+}
+
+static int check_array_args(unsigned int cmd, void *parg, size_t *array_size,
+ void * __user *user_ptr, void ***kernel_ptr)
+{
+ int ret = 0;
+
+ switch (cmd) {
+ case VIDIOC_QUERYBUF:
+ case VIDIOC_QBUF:
+ case VIDIOC_DQBUF: {
+ struct v4l2_buffer *buf = parg;
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(buf->type) && buf->length > 0) {
+ if (buf->length > VIDEO_MAX_PLANES) {
+ ret = -EINVAL;
+ break;
+ }
+ *user_ptr = (void __user *)buf->m.planes;
+ *kernel_ptr = (void *)&buf->m.planes;
+ *array_size = sizeof(struct v4l2_plane) * buf->length;
+ ret = 1;
+ }
+ break;
+ }
+
+ case VIDIOC_S_EXT_CTRLS:
+ case VIDIOC_G_EXT_CTRLS:
+ case VIDIOC_TRY_EXT_CTRLS: {
+ struct v4l2_ext_controls *ctrls = parg;
+
+ if (ctrls->count != 0) {
+ if (ctrls->count > V4L2_CID_MAX_CTRLS) {
+ ret = -EINVAL;
+ break;
+ }
+ *user_ptr = (void __user *)ctrls->controls;
+ *kernel_ptr = (void *)&ctrls->controls;
+ *array_size = sizeof(struct v4l2_ext_control)
+ * ctrls->count;
+ ret = 1;
+ }
+ break;
+ }
+ }
+
+ return ret;
+}
+
+long
+video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
+ v4l2_kioctl func)
+{
+ char sbuf[128];
+ void *mbuf = NULL;
+ void *parg = (void *)arg;
+ long err = -EINVAL;
+ bool has_array_args;
+ size_t array_size = 0;
+ void __user *user_ptr = NULL;
+ void **kernel_ptr = NULL;
+
+ /* Copy arguments into temp kernel buffer */
+ if (_IOC_DIR(cmd) != _IOC_NONE) {
+ if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
+ parg = sbuf;
+ } else {
+ /* too big to allocate from stack */
+ mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL);
+ if (NULL == mbuf)
+ return -ENOMEM;
+ parg = mbuf;
+ }
+
+ err = -EFAULT;
+ if (_IOC_DIR(cmd) & _IOC_WRITE) {
+ unsigned long n = cmd_input_size(cmd);
+
+ if (copy_from_user(parg, (void __user *)arg, n))
+ goto out;
+
+ /* zero out anything we don't copy from userspace */
+ if (n < _IOC_SIZE(cmd))
+ memset((u8 *)parg + n, 0, _IOC_SIZE(cmd) - n);
+ } else {
+ /* read-only ioctl */
+ memset(parg, 0, _IOC_SIZE(cmd));
+ }
+ }
+
+ err = check_array_args(cmd, parg, &array_size, &user_ptr, &kernel_ptr);
+ if (err < 0)
+ goto out;
+ has_array_args = err;
+
+ if (has_array_args) {
+ /*
+ * When adding new types of array args, make sure that the
+ * parent argument to ioctl (which contains the pointer to the
+ * array) fits into sbuf (so that mbuf will still remain
+ * unused up to here).
+ */
+ mbuf = kmalloc(array_size, GFP_KERNEL);
+ err = -ENOMEM;
+ if (NULL == mbuf)
+ goto out_array_args;
+ err = -EFAULT;
+ if (copy_from_user(mbuf, user_ptr, array_size))
+ goto out_array_args;
+ *kernel_ptr = mbuf;
+ }
+
+ /* Handles IOCTL */
+ err = func(file, cmd, parg);
+ if (err == -ENOIOCTLCMD)
+ err = -ENOTTY;
+
+ if (has_array_args) {
+ *kernel_ptr = user_ptr;
+ if (copy_to_user(user_ptr, mbuf, array_size))
+ err = -EFAULT;
+ goto out_array_args;
+ }
+ if (err < 0)
+ goto out;
+
+out_array_args:
+ /* Copy results into user buffer */
+ switch (_IOC_DIR(cmd)) {
+ case _IOC_READ:
+ case (_IOC_WRITE | _IOC_READ):
+ if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
+ err = -EFAULT;
+ break;
+ }
+
+out:
+ kfree(mbuf);
+ return err;
+}
+EXPORT_SYMBOL(video_usercopy);
+
+long video_ioctl2(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, __video_do_ioctl);
+}
+EXPORT_SYMBOL(video_ioctl2);
diff --git a/drivers/media/video/v4l2-mem2mem.c b/drivers/media/video/v4l2-mem2mem.c
new file mode 100644
index 00000000..975d0fa9
--- /dev/null
+++ b/drivers/media/video/v4l2-mem2mem.c
@@ -0,0 +1,631 @@
+/*
+ * Memory-to-memory device framework for Video for Linux 2 and videobuf.
+ *
+ * Helper functions for devices that use videobuf buffers for both their
+ * source and destination.
+ *
+ * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
+ * Pawel Osciak, <pawel@osciak.com>
+ * Marek Szyprowski, <m.szyprowski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <media/videobuf2-core.h>
+#include <media/v4l2-mem2mem.h>
+
+MODULE_DESCRIPTION("Mem to mem device framework for videobuf");
+MODULE_AUTHOR("Pawel Osciak, <pawel@osciak.com>");
+MODULE_LICENSE("GPL");
+
+static bool debug;
+module_param(debug, bool, 0644);
+
+#define dprintk(fmt, arg...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG "%s: " fmt, __func__, ## arg);\
+ } while (0)
+
+
+/* Instance is already queued on the job_queue */
+#define TRANS_QUEUED (1 << 0)
+/* Instance is currently running in hardware */
+#define TRANS_RUNNING (1 << 1)
+
+
+/* Offset base for buffers on the destination queue - used to distinguish
+ * between source and destination buffers when mmapping - they receive the same
+ * offsets but for different queues */
+#define DST_QUEUE_OFF_BASE (1 << 30)
+
+
+/**
+ * struct v4l2_m2m_dev - per-device context
+ * @curr_ctx: currently running instance
+ * @job_queue: instances queued to run
+ * @job_spinlock: protects job_queue
+ * @m2m_ops: driver callbacks
+ */
+struct v4l2_m2m_dev {
+ struct v4l2_m2m_ctx *curr_ctx;
+
+ struct list_head job_queue;
+ spinlock_t job_spinlock;
+
+ struct v4l2_m2m_ops *m2m_ops;
+};
+
+static struct v4l2_m2m_queue_ctx *get_queue_ctx(struct v4l2_m2m_ctx *m2m_ctx,
+ enum v4l2_buf_type type)
+{
+ if (V4L2_TYPE_IS_OUTPUT(type))
+ return &m2m_ctx->out_q_ctx;
+ else
+ return &m2m_ctx->cap_q_ctx;
+}
+
+/**
+ * v4l2_m2m_get_vq() - return vb2_queue for the given type
+ */
+struct vb2_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx,
+ enum v4l2_buf_type type)
+{
+ struct v4l2_m2m_queue_ctx *q_ctx;
+
+ q_ctx = get_queue_ctx(m2m_ctx, type);
+ if (!q_ctx)
+ return NULL;
+
+ return &q_ctx->q;
+}
+EXPORT_SYMBOL(v4l2_m2m_get_vq);
+
+/**
+ * v4l2_m2m_next_buf() - return next buffer from the list of ready buffers
+ */
+void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx)
+{
+ struct v4l2_m2m_buffer *b = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
+
+ if (list_empty(&q_ctx->rdy_queue)) {
+ spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
+ return NULL;
+ }
+
+ b = list_entry(q_ctx->rdy_queue.next, struct v4l2_m2m_buffer, list);
+ spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
+ return &b->vb;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_next_buf);
+
+/**
+ * v4l2_m2m_buf_remove() - take off a buffer from the list of ready buffers and
+ * return it
+ */
+void *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx)
+{
+ struct v4l2_m2m_buffer *b = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
+ if (list_empty(&q_ctx->rdy_queue)) {
+ spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
+ return NULL;
+ }
+ b = list_entry(q_ctx->rdy_queue.next, struct v4l2_m2m_buffer, list);
+ list_del(&b->list);
+ q_ctx->num_rdy--;
+ spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
+
+ return &b->vb;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_buf_remove);
+
+/*
+ * Scheduling handlers
+ */
+
+/**
+ * v4l2_m2m_get_curr_priv() - return driver private data for the currently
+ * running instance or NULL if no instance is running
+ */
+void *v4l2_m2m_get_curr_priv(struct v4l2_m2m_dev *m2m_dev)
+{
+ unsigned long flags;
+ void *ret = NULL;
+
+ spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
+ if (m2m_dev->curr_ctx)
+ ret = m2m_dev->curr_ctx->priv;
+ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_m2m_get_curr_priv);
+
+/**
+ * v4l2_m2m_try_run() - select next job to perform and run it if possible
+ *
+ * Get next transaction (if present) from the waiting jobs list and run it.
+ */
+static void v4l2_m2m_try_run(struct v4l2_m2m_dev *m2m_dev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
+ if (NULL != m2m_dev->curr_ctx) {
+ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+ dprintk("Another instance is running, won't run now\n");
+ return;
+ }
+
+ if (list_empty(&m2m_dev->job_queue)) {
+ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+ dprintk("No job pending\n");
+ return;
+ }
+
+ m2m_dev->curr_ctx = list_entry(m2m_dev->job_queue.next,
+ struct v4l2_m2m_ctx, queue);
+ m2m_dev->curr_ctx->job_flags |= TRANS_RUNNING;
+ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+
+ m2m_dev->m2m_ops->device_run(m2m_dev->curr_ctx->priv);
+}
+
+/**
+ * v4l2_m2m_try_schedule() - check whether an instance is ready to be added to
+ * the pending job queue and add it if so.
+ * @m2m_ctx: m2m context assigned to the instance to be checked
+ *
+ * There are three basic requirements an instance has to meet to be able to run:
+ * 1) at least one source buffer has to be queued,
+ * 2) at least one destination buffer has to be queued,
+ * 3) streaming has to be on.
+ *
+ * There may also be additional, custom requirements. In such case the driver
+ * should supply a custom callback (job_ready in v4l2_m2m_ops) that should
+ * return 1 if the instance is ready.
+ * An example of the above could be an instance that requires more than one
+ * src/dst buffer per transaction.
+ */
+static void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx)
+{
+ struct v4l2_m2m_dev *m2m_dev;
+ unsigned long flags_job, flags;
+
+ m2m_dev = m2m_ctx->m2m_dev;
+ dprintk("Trying to schedule a job for m2m_ctx: %p\n", m2m_ctx);
+
+ if (!m2m_ctx->out_q_ctx.q.streaming
+ || !m2m_ctx->cap_q_ctx.q.streaming) {
+ dprintk("Streaming needs to be on for both queues\n");
+ return;
+ }
+
+ spin_lock_irqsave(&m2m_dev->job_spinlock, flags_job);
+ if (m2m_ctx->job_flags & TRANS_QUEUED) {
+ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
+ dprintk("On job queue already\n");
+ return;
+ }
+
+ spin_lock_irqsave(&m2m_ctx->out_q_ctx.rdy_spinlock, flags);
+ if (list_empty(&m2m_ctx->out_q_ctx.rdy_queue)) {
+ spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags);
+ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
+ dprintk("No input buffers available\n");
+ return;
+ }
+ if (list_empty(&m2m_ctx->cap_q_ctx.rdy_queue)) {
+ spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags);
+ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
+ dprintk("No output buffers available\n");
+ return;
+ }
+ spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags);
+
+ if (m2m_dev->m2m_ops->job_ready
+ && (!m2m_dev->m2m_ops->job_ready(m2m_ctx->priv))) {
+ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
+ dprintk("Driver not ready\n");
+ return;
+ }
+
+ list_add_tail(&m2m_ctx->queue, &m2m_dev->job_queue);
+ m2m_ctx->job_flags |= TRANS_QUEUED;
+
+ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
+
+ v4l2_m2m_try_run(m2m_dev);
+}
+
+/**
+ * v4l2_m2m_job_finish() - inform the framework that a job has been finished
+ * and have it clean up
+ *
+ * Called by a driver to yield back the device after it has finished with it.
+ * Should be called as soon as possible after reaching a state which allows
+ * other instances to take control of the device.
+ *
+ * This function has to be called only after device_run() callback has been
+ * called on the driver. To prevent recursion, it should not be called directly
+ * from the device_run() callback though.
+ */
+void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev,
+ struct v4l2_m2m_ctx *m2m_ctx)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
+ if (!m2m_dev->curr_ctx || m2m_dev->curr_ctx != m2m_ctx) {
+ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+ dprintk("Called by an instance not currently running\n");
+ return;
+ }
+
+ list_del(&m2m_dev->curr_ctx->queue);
+ m2m_dev->curr_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING);
+ wake_up(&m2m_dev->curr_ctx->finished);
+ m2m_dev->curr_ctx = NULL;
+
+ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+
+ /* This instance might have more buffers ready, but since we do not
+ * allow more than one job on the job_queue per instance, each has
+ * to be scheduled separately after the previous one finishes. */
+ v4l2_m2m_try_schedule(m2m_ctx);
+ v4l2_m2m_try_run(m2m_dev);
+}
+EXPORT_SYMBOL(v4l2_m2m_job_finish);
+
+/**
+ * v4l2_m2m_reqbufs() - multi-queue-aware REQBUFS multiplexer
+ */
+int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct vb2_queue *vq;
+
+ vq = v4l2_m2m_get_vq(m2m_ctx, reqbufs->type);
+ return vb2_reqbufs(vq, reqbufs);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_reqbufs);
+
+/**
+ * v4l2_m2m_querybuf() - multi-queue-aware QUERYBUF multiplexer
+ *
+ * See v4l2_m2m_mmap() documentation for details.
+ */
+int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
+ struct v4l2_buffer *buf)
+{
+ struct vb2_queue *vq;
+ int ret = 0;
+ unsigned int i;
+
+ vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
+ ret = vb2_querybuf(vq, buf);
+
+ /* Adjust MMAP memory offsets for the CAPTURE queue */
+ if (buf->memory == V4L2_MEMORY_MMAP && !V4L2_TYPE_IS_OUTPUT(vq->type)) {
+ if (V4L2_TYPE_IS_MULTIPLANAR(vq->type)) {
+ for (i = 0; i < buf->length; ++i)
+ buf->m.planes[i].m.mem_offset
+ += DST_QUEUE_OFF_BASE;
+ } else {
+ buf->m.offset += DST_QUEUE_OFF_BASE;
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_querybuf);
+
+/**
+ * v4l2_m2m_qbuf() - enqueue a source or destination buffer, depending on
+ * the type
+ */
+int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
+ struct v4l2_buffer *buf)
+{
+ struct vb2_queue *vq;
+ int ret;
+
+ vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
+ ret = vb2_qbuf(vq, buf);
+ if (!ret)
+ v4l2_m2m_try_schedule(m2m_ctx);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_qbuf);
+
+/**
+ * v4l2_m2m_dqbuf() - dequeue a source or destination buffer, depending on
+ * the type
+ */
+int v4l2_m2m_dqbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
+ struct v4l2_buffer *buf)
+{
+ struct vb2_queue *vq;
+
+ vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
+ return vb2_dqbuf(vq, buf, file->f_flags & O_NONBLOCK);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_dqbuf);
+
+/**
+ * v4l2_m2m_streamon() - turn on streaming for a video queue
+ */
+int v4l2_m2m_streamon(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
+ enum v4l2_buf_type type)
+{
+ struct vb2_queue *vq;
+ int ret;
+
+ vq = v4l2_m2m_get_vq(m2m_ctx, type);
+ ret = vb2_streamon(vq, type);
+ if (!ret)
+ v4l2_m2m_try_schedule(m2m_ctx);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_streamon);
+
+/**
+ * v4l2_m2m_streamoff() - turn off streaming for a video queue
+ */
+int v4l2_m2m_streamoff(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
+ enum v4l2_buf_type type)
+{
+ struct vb2_queue *vq;
+
+ vq = v4l2_m2m_get_vq(m2m_ctx, type);
+ return vb2_streamoff(vq, type);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_streamoff);
+
+/**
+ * v4l2_m2m_poll() - poll replacement, for destination buffers only
+ *
+ * Call from the driver's poll() function. Will poll both queues. If a buffer
+ * is available to dequeue (with dqbuf) from the source queue, this will
+ * indicate that a non-blocking write can be performed, while read will be
+ * returned in case of the destination queue.
+ */
+unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
+ struct poll_table_struct *wait)
+{
+ struct vb2_queue *src_q, *dst_q;
+ struct vb2_buffer *src_vb = NULL, *dst_vb = NULL;
+ unsigned int rc = 0;
+ unsigned long flags;
+
+ src_q = v4l2_m2m_get_src_vq(m2m_ctx);
+ dst_q = v4l2_m2m_get_dst_vq(m2m_ctx);
+
+ /*
+ * There has to be at least one buffer queued on each queued_list, which
+ * means either in driver already or waiting for driver to claim it
+ * and start processing.
+ */
+ if ((!src_q->streaming || list_empty(&src_q->queued_list))
+ && (!dst_q->streaming || list_empty(&dst_q->queued_list))) {
+ rc = POLLERR;
+ goto end;
+ }
+
+ if (m2m_ctx->m2m_dev->m2m_ops->unlock)
+ m2m_ctx->m2m_dev->m2m_ops->unlock(m2m_ctx->priv);
+
+ poll_wait(file, &src_q->done_wq, wait);
+ poll_wait(file, &dst_q->done_wq, wait);
+
+ if (m2m_ctx->m2m_dev->m2m_ops->lock)
+ m2m_ctx->m2m_dev->m2m_ops->lock(m2m_ctx->priv);
+
+ spin_lock_irqsave(&src_q->done_lock, flags);
+ if (!list_empty(&src_q->done_list))
+ src_vb = list_first_entry(&src_q->done_list, struct vb2_buffer,
+ done_entry);
+ if (src_vb && (src_vb->state == VB2_BUF_STATE_DONE
+ || src_vb->state == VB2_BUF_STATE_ERROR))
+ rc |= POLLOUT | POLLWRNORM;
+ spin_unlock_irqrestore(&src_q->done_lock, flags);
+
+ spin_lock_irqsave(&dst_q->done_lock, flags);
+ if (!list_empty(&dst_q->done_list))
+ dst_vb = list_first_entry(&dst_q->done_list, struct vb2_buffer,
+ done_entry);
+ if (dst_vb && (dst_vb->state == VB2_BUF_STATE_DONE
+ || dst_vb->state == VB2_BUF_STATE_ERROR))
+ rc |= POLLIN | POLLRDNORM;
+ spin_unlock_irqrestore(&dst_q->done_lock, flags);
+
+end:
+ return rc;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_poll);
+
+/**
+ * v4l2_m2m_mmap() - source and destination queues-aware mmap multiplexer
+ *
+ * Call from driver's mmap() function. Will handle mmap() for both queues
+ * seamlessly for videobuffer, which will receive normal per-queue offsets and
+ * proper videobuf queue pointers. The differentiation is made outside videobuf
+ * by adding a predefined offset to buffers from one of the queues and
+ * subtracting it before passing it back to videobuf. Only drivers (and
+ * thus applications) receive modified offsets.
+ */
+int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
+ struct vm_area_struct *vma)
+{
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ struct vb2_queue *vq;
+
+ if (offset < DST_QUEUE_OFF_BASE) {
+ vq = v4l2_m2m_get_src_vq(m2m_ctx);
+ } else {
+ vq = v4l2_m2m_get_dst_vq(m2m_ctx);
+ vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT);
+ }
+
+ return vb2_mmap(vq, vma);
+}
+EXPORT_SYMBOL(v4l2_m2m_mmap);
+
+/**
+ * v4l2_m2m_init() - initialize per-driver m2m data
+ *
+ * Usually called from driver's probe() function.
+ */
+struct v4l2_m2m_dev *v4l2_m2m_init(struct v4l2_m2m_ops *m2m_ops)
+{
+ struct v4l2_m2m_dev *m2m_dev;
+
+ if (!m2m_ops)
+ return ERR_PTR(-EINVAL);
+
+ BUG_ON(!m2m_ops->device_run);
+ BUG_ON(!m2m_ops->job_abort);
+
+ m2m_dev = kzalloc(sizeof *m2m_dev, GFP_KERNEL);
+ if (!m2m_dev)
+ return ERR_PTR(-ENOMEM);
+
+ m2m_dev->curr_ctx = NULL;
+ m2m_dev->m2m_ops = m2m_ops;
+ INIT_LIST_HEAD(&m2m_dev->job_queue);
+ spin_lock_init(&m2m_dev->job_spinlock);
+
+ return m2m_dev;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_init);
+
+/**
+ * v4l2_m2m_release() - cleans up and frees a m2m_dev structure
+ *
+ * Usually called from driver's remove() function.
+ */
+void v4l2_m2m_release(struct v4l2_m2m_dev *m2m_dev)
+{
+ kfree(m2m_dev);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_release);
+
+/**
+ * v4l2_m2m_ctx_init() - allocate and initialize a m2m context
+ * @priv - driver's instance private data
+ * @m2m_dev - a previously initialized m2m_dev struct
+ * @vq_init - a callback for queue type-specific initialization function to be
+ * used for initializing videobuf_queues
+ *
+ * Usually called from driver's open() function.
+ */
+struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(struct v4l2_m2m_dev *m2m_dev,
+ void *drv_priv,
+ int (*queue_init)(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq))
+{
+ struct v4l2_m2m_ctx *m2m_ctx;
+ struct v4l2_m2m_queue_ctx *out_q_ctx, *cap_q_ctx;
+ int ret;
+
+ m2m_ctx = kzalloc(sizeof *m2m_ctx, GFP_KERNEL);
+ if (!m2m_ctx)
+ return ERR_PTR(-ENOMEM);
+
+ m2m_ctx->priv = drv_priv;
+ m2m_ctx->m2m_dev = m2m_dev;
+ init_waitqueue_head(&m2m_ctx->finished);
+
+ out_q_ctx = &m2m_ctx->out_q_ctx;
+ cap_q_ctx = &m2m_ctx->cap_q_ctx;
+
+ INIT_LIST_HEAD(&out_q_ctx->rdy_queue);
+ INIT_LIST_HEAD(&cap_q_ctx->rdy_queue);
+ spin_lock_init(&out_q_ctx->rdy_spinlock);
+ spin_lock_init(&cap_q_ctx->rdy_spinlock);
+
+ INIT_LIST_HEAD(&m2m_ctx->queue);
+
+ ret = queue_init(drv_priv, &out_q_ctx->q, &cap_q_ctx->q);
+
+ if (ret)
+ goto err;
+
+ return m2m_ctx;
+err:
+ kfree(m2m_ctx);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_ctx_init);
+
+/**
+ * v4l2_m2m_ctx_release() - release m2m context
+ *
+ * Usually called from driver's release() function.
+ */
+void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx)
+{
+ struct v4l2_m2m_dev *m2m_dev;
+ unsigned long flags;
+
+ m2m_dev = m2m_ctx->m2m_dev;
+
+ spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
+ if (m2m_ctx->job_flags & TRANS_RUNNING) {
+ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+ m2m_dev->m2m_ops->job_abort(m2m_ctx->priv);
+ dprintk("m2m_ctx %p running, will wait to complete", m2m_ctx);
+ wait_event(m2m_ctx->finished, !(m2m_ctx->job_flags & TRANS_RUNNING));
+ } else if (m2m_ctx->job_flags & TRANS_QUEUED) {
+ list_del(&m2m_ctx->queue);
+ m2m_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING);
+ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+ dprintk("m2m_ctx: %p had been on queue and was removed\n",
+ m2m_ctx);
+ } else {
+ /* Do nothing, was not on queue/running */
+ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+ }
+
+ vb2_queue_release(&m2m_ctx->cap_q_ctx.q);
+ vb2_queue_release(&m2m_ctx->out_q_ctx.q);
+
+ kfree(m2m_ctx);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_ctx_release);
+
+/**
+ * v4l2_m2m_buf_queue() - add a buffer to the proper ready buffers list.
+ *
+ * Call from buf_queue(), videobuf_queue_ops callback.
+ */
+void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, struct vb2_buffer *vb)
+{
+ struct v4l2_m2m_buffer *b = container_of(vb, struct v4l2_m2m_buffer, vb);
+ struct v4l2_m2m_queue_ctx *q_ctx;
+ unsigned long flags;
+
+ q_ctx = get_queue_ctx(m2m_ctx, vb->vb2_queue->type);
+ if (!q_ctx)
+ return;
+
+ spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
+ list_add_tail(&b->list, &q_ctx->rdy_queue);
+ q_ctx->num_rdy++;
+ spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_buf_queue);
+
diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c
new file mode 100644
index 00000000..6fe88e96
--- /dev/null
+++ b/drivers/media/video/v4l2-subdev.c
@@ -0,0 +1,351 @@
+/*
+ * V4L2 sub-device
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ *
+ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/ioctl.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <linux/export.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+
+static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd)
+{
+#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
+ /* Allocate try format and crop in the same memory block */
+ fh->try_fmt = kzalloc((sizeof(*fh->try_fmt) + sizeof(*fh->try_crop))
+ * sd->entity.num_pads, GFP_KERNEL);
+ if (fh->try_fmt == NULL)
+ return -ENOMEM;
+
+ fh->try_crop = (struct v4l2_rect *)
+ (fh->try_fmt + sd->entity.num_pads);
+#endif
+ return 0;
+}
+
+static void subdev_fh_free(struct v4l2_subdev_fh *fh)
+{
+#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
+ kfree(fh->try_fmt);
+ fh->try_fmt = NULL;
+ fh->try_crop = NULL;
+#endif
+}
+
+static int subdev_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+ struct v4l2_subdev_fh *subdev_fh;
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ struct media_entity *entity = NULL;
+#endif
+ int ret;
+
+ subdev_fh = kzalloc(sizeof(*subdev_fh), GFP_KERNEL);
+ if (subdev_fh == NULL)
+ return -ENOMEM;
+
+ ret = subdev_fh_init(subdev_fh, sd);
+ if (ret) {
+ kfree(subdev_fh);
+ return ret;
+ }
+
+ v4l2_fh_init(&subdev_fh->vfh, vdev);
+ v4l2_fh_add(&subdev_fh->vfh);
+ file->private_data = &subdev_fh->vfh;
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ if (sd->v4l2_dev->mdev) {
+ entity = media_entity_get(&sd->entity);
+ if (!entity) {
+ ret = -EBUSY;
+ goto err;
+ }
+ }
+#endif
+
+ if (sd->internal_ops && sd->internal_ops->open) {
+ ret = sd->internal_ops->open(sd, subdev_fh);
+ if (ret < 0)
+ goto err;
+ }
+
+ return 0;
+
+err:
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ if (entity)
+ media_entity_put(entity);
+#endif
+ v4l2_fh_del(&subdev_fh->vfh);
+ v4l2_fh_exit(&subdev_fh->vfh);
+ subdev_fh_free(subdev_fh);
+ kfree(subdev_fh);
+
+ return ret;
+}
+
+static int subdev_close(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+ struct v4l2_fh *vfh = file->private_data;
+ struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh);
+
+ if (sd->internal_ops && sd->internal_ops->close)
+ sd->internal_ops->close(sd, subdev_fh);
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ if (sd->v4l2_dev->mdev)
+ media_entity_put(&sd->entity);
+#endif
+ v4l2_fh_del(vfh);
+ v4l2_fh_exit(vfh);
+ subdev_fh_free(subdev_fh);
+ kfree(subdev_fh);
+ file->private_data = NULL;
+
+ return 0;
+}
+
+static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+ struct v4l2_fh *vfh = file->private_data;
+#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
+ struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh);
+#endif
+
+ switch (cmd) {
+ case VIDIOC_QUERYCTRL:
+ return v4l2_queryctrl(vfh->ctrl_handler, arg);
+
+ case VIDIOC_QUERYMENU:
+ return v4l2_querymenu(vfh->ctrl_handler, arg);
+
+ case VIDIOC_G_CTRL:
+ return v4l2_g_ctrl(vfh->ctrl_handler, arg);
+
+ case VIDIOC_S_CTRL:
+ return v4l2_s_ctrl(vfh, vfh->ctrl_handler, arg);
+
+ case VIDIOC_G_EXT_CTRLS:
+ return v4l2_g_ext_ctrls(vfh->ctrl_handler, arg);
+
+ case VIDIOC_S_EXT_CTRLS:
+ return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, arg);
+
+ case VIDIOC_TRY_EXT_CTRLS:
+ return v4l2_try_ext_ctrls(vfh->ctrl_handler, arg);
+
+ case VIDIOC_DQEVENT:
+ if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS))
+ return -ENOIOCTLCMD;
+
+ return v4l2_event_dequeue(vfh, arg, file->f_flags & O_NONBLOCK);
+
+ case VIDIOC_SUBSCRIBE_EVENT:
+ return v4l2_subdev_call(sd, core, subscribe_event, vfh, arg);
+
+ case VIDIOC_UNSUBSCRIBE_EVENT:
+ return v4l2_subdev_call(sd, core, unsubscribe_event, vfh, arg);
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ case VIDIOC_DBG_G_REGISTER:
+ {
+ struct v4l2_dbg_register *p = arg;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ return v4l2_subdev_call(sd, core, g_register, p);
+ }
+ case VIDIOC_DBG_S_REGISTER:
+ {
+ struct v4l2_dbg_register *p = arg;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ return v4l2_subdev_call(sd, core, s_register, p);
+ }
+#endif
+
+ case VIDIOC_LOG_STATUS: {
+ int ret;
+
+ pr_info("%s: ================= START STATUS =================\n",
+ sd->name);
+ ret = v4l2_subdev_call(sd, core, log_status);
+ pr_info("%s: ================== END STATUS ==================\n",
+ sd->name);
+ return ret;
+ }
+
+#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
+ case VIDIOC_SUBDEV_G_FMT: {
+ struct v4l2_subdev_format *format = arg;
+
+ if (format->which != V4L2_SUBDEV_FORMAT_TRY &&
+ format->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
+ if (format->pad >= sd->entity.num_pads)
+ return -EINVAL;
+
+ return v4l2_subdev_call(sd, pad, get_fmt, subdev_fh, format);
+ }
+
+ case VIDIOC_SUBDEV_S_FMT: {
+ struct v4l2_subdev_format *format = arg;
+
+ if (format->which != V4L2_SUBDEV_FORMAT_TRY &&
+ format->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
+ if (format->pad >= sd->entity.num_pads)
+ return -EINVAL;
+
+ return v4l2_subdev_call(sd, pad, set_fmt, subdev_fh, format);
+ }
+
+ case VIDIOC_SUBDEV_G_CROP: {
+ struct v4l2_subdev_crop *crop = arg;
+
+ if (crop->which != V4L2_SUBDEV_FORMAT_TRY &&
+ crop->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
+ if (crop->pad >= sd->entity.num_pads)
+ return -EINVAL;
+
+ return v4l2_subdev_call(sd, pad, get_crop, subdev_fh, crop);
+ }
+
+ case VIDIOC_SUBDEV_S_CROP: {
+ struct v4l2_subdev_crop *crop = arg;
+
+ if (crop->which != V4L2_SUBDEV_FORMAT_TRY &&
+ crop->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
+ if (crop->pad >= sd->entity.num_pads)
+ return -EINVAL;
+
+ return v4l2_subdev_call(sd, pad, set_crop, subdev_fh, crop);
+ }
+
+ case VIDIOC_SUBDEV_ENUM_MBUS_CODE: {
+ struct v4l2_subdev_mbus_code_enum *code = arg;
+
+ if (code->pad >= sd->entity.num_pads)
+ return -EINVAL;
+
+ return v4l2_subdev_call(sd, pad, enum_mbus_code, subdev_fh,
+ code);
+ }
+
+ case VIDIOC_SUBDEV_ENUM_FRAME_SIZE: {
+ struct v4l2_subdev_frame_size_enum *fse = arg;
+
+ if (fse->pad >= sd->entity.num_pads)
+ return -EINVAL;
+
+ return v4l2_subdev_call(sd, pad, enum_frame_size, subdev_fh,
+ fse);
+ }
+
+ case VIDIOC_SUBDEV_G_FRAME_INTERVAL:
+ return v4l2_subdev_call(sd, video, g_frame_interval, arg);
+
+ case VIDIOC_SUBDEV_S_FRAME_INTERVAL:
+ return v4l2_subdev_call(sd, video, s_frame_interval, arg);
+
+ case VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL: {
+ struct v4l2_subdev_frame_interval_enum *fie = arg;
+
+ if (fie->pad >= sd->entity.num_pads)
+ return -EINVAL;
+
+ return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh,
+ fie);
+ }
+#endif
+ default:
+ return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
+ }
+
+ return 0;
+}
+
+static long subdev_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, subdev_do_ioctl);
+}
+
+static unsigned int subdev_poll(struct file *file, poll_table *wait)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+ struct v4l2_fh *fh = file->private_data;
+
+ if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS))
+ return POLLERR;
+
+ poll_wait(file, &fh->wait, wait);
+
+ if (v4l2_event_pending(fh))
+ return POLLPRI;
+
+ return 0;
+}
+
+const struct v4l2_file_operations v4l2_subdev_fops = {
+ .owner = THIS_MODULE,
+ .open = subdev_open,
+ .unlocked_ioctl = subdev_ioctl,
+ .release = subdev_close,
+ .poll = subdev_poll,
+};
+
+void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops)
+{
+ INIT_LIST_HEAD(&sd->list);
+ BUG_ON(!ops);
+ sd->ops = ops;
+ sd->v4l2_dev = NULL;
+ sd->flags = 0;
+ sd->name[0] = '\0';
+ sd->grp_id = 0;
+ sd->dev_priv = NULL;
+ sd->host_priv = NULL;
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ sd->entity.name = sd->name;
+ sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+#endif
+}
+EXPORT_SYMBOL(v4l2_subdev_init);
diff --git a/drivers/media/video/via-camera.c b/drivers/media/video/via-camera.c
new file mode 100644
index 00000000..20f7237b
--- /dev/null
+++ b/drivers/media/video/via-camera.c
@@ -0,0 +1,1503 @@
+/*
+ * Driver for the VIA Chrome integrated camera controller.
+ *
+ * Copyright 2009,2010 Jonathan Corbet <corbet@lwn.net>
+ * Distributable under the terms of the GNU General Public License, version 2
+ *
+ * This work was supported by the One Laptop Per Child project
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/pci.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/videobuf-dma-sg.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/pm_qos.h>
+#include <linux/via-core.h>
+#include <linux/via-gpio.h>
+#include <linux/via_i2c.h>
+#include <asm/olpc.h>
+
+#include "via-camera.h"
+
+MODULE_ALIAS("platform:viafb-camera");
+MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
+MODULE_DESCRIPTION("VIA framebuffer-based camera controller driver");
+MODULE_LICENSE("GPL");
+
+static bool flip_image;
+module_param(flip_image, bool, 0444);
+MODULE_PARM_DESC(flip_image,
+ "If set, the sensor will be instructed to flip the image "
+ "vertically.");
+
+static bool override_serial;
+module_param(override_serial, bool, 0444);
+MODULE_PARM_DESC(override_serial,
+ "The camera driver will normally refuse to load if "
+ "the XO 1.5 serial port is enabled. Set this option "
+ "to force-enable the camera.");
+
+/*
+ * Basic window sizes.
+ */
+#define VGA_WIDTH 640
+#define VGA_HEIGHT 480
+#define QCIF_WIDTH 176
+#define QCIF_HEIGHT 144
+
+/*
+ * The structure describing our camera.
+ */
+enum viacam_opstate { S_IDLE = 0, S_RUNNING = 1 };
+
+struct via_camera {
+ struct v4l2_device v4l2_dev;
+ struct video_device vdev;
+ struct v4l2_subdev *sensor;
+ struct platform_device *platdev;
+ struct viafb_dev *viadev;
+ struct mutex lock;
+ enum viacam_opstate opstate;
+ unsigned long flags;
+ struct pm_qos_request qos_request;
+ /*
+ * GPIO info for power/reset management
+ */
+ int power_gpio;
+ int reset_gpio;
+ /*
+ * I/O memory stuff.
+ */
+ void __iomem *mmio; /* Where the registers live */
+ void __iomem *fbmem; /* Frame buffer memory */
+ u32 fb_offset; /* Reserved memory offset (FB) */
+ /*
+ * Capture buffers and related. The controller supports
+ * up to three, so that's what we have here. These buffers
+ * live in frame buffer memory, so we don't call them "DMA".
+ */
+ unsigned int cb_offsets[3]; /* offsets into fb mem */
+ u8 *cb_addrs[3]; /* Kernel-space addresses */
+ int n_cap_bufs; /* How many are we using? */
+ int next_buf;
+ struct videobuf_queue vb_queue;
+ struct list_head buffer_queue; /* prot. by reg_lock */
+ /*
+ * User tracking.
+ */
+ int users;
+ struct file *owner;
+ /*
+ * Video format information. sensor_format is kept in a form
+ * that we can use to pass to the sensor. We always run the
+ * sensor in VGA resolution, though, and let the controller
+ * downscale things if need be. So we keep the "real*
+ * dimensions separately.
+ */
+ struct v4l2_pix_format sensor_format;
+ struct v4l2_pix_format user_format;
+ enum v4l2_mbus_pixelcode mbus_code;
+};
+
+/*
+ * Yes, this is a hack, but there's only going to be one of these
+ * on any system we know of.
+ */
+static struct via_camera *via_cam_info;
+
+/*
+ * Flag values, manipulated with bitops
+ */
+#define CF_DMA_ACTIVE 0 /* A frame is incoming */
+#define CF_CONFIG_NEEDED 1 /* Must configure hardware */
+
+
+/*
+ * Nasty ugly v4l2 boilerplate.
+ */
+#define sensor_call(cam, optype, func, args...) \
+ v4l2_subdev_call(cam->sensor, optype, func, ##args)
+
+/*
+ * Debugging and related.
+ */
+#define cam_err(cam, fmt, arg...) \
+ dev_err(&(cam)->platdev->dev, fmt, ##arg);
+#define cam_warn(cam, fmt, arg...) \
+ dev_warn(&(cam)->platdev->dev, fmt, ##arg);
+#define cam_dbg(cam, fmt, arg...) \
+ dev_dbg(&(cam)->platdev->dev, fmt, ##arg);
+
+/*
+ * Format handling. This is ripped almost directly from Hans's changes
+ * to cafe_ccic.c. It's a little unfortunate; until this change, we
+ * didn't need to know anything about the format except its byte depth;
+ * now this information must be managed at this level too.
+ */
+static struct via_format {
+ __u8 *desc;
+ __u32 pixelformat;
+ int bpp; /* Bytes per pixel */
+ enum v4l2_mbus_pixelcode mbus_code;
+} via_formats[] = {
+ {
+ .desc = "YUYV 4:2:2",
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .bpp = 2,
+ },
+ /* RGB444 and Bayer should be doable, but have never been
+ tested with this driver. RGB565 seems to work at the default
+ resolution, but results in color corruption when being scaled by
+ viacam_set_scaled(), and is disabled as a result. */
+};
+#define N_VIA_FMTS ARRAY_SIZE(via_formats)
+
+static struct via_format *via_find_format(u32 pixelformat)
+{
+ unsigned i;
+
+ for (i = 0; i < N_VIA_FMTS; i++)
+ if (via_formats[i].pixelformat == pixelformat)
+ return via_formats + i;
+ /* Not found? Then return the first format. */
+ return via_formats;
+}
+
+
+/*--------------------------------------------------------------------------*/
+/*
+ * Sensor power/reset management. This piece is OLPC-specific for
+ * sure; other configurations will have things connected differently.
+ */
+static int via_sensor_power_setup(struct via_camera *cam)
+{
+ int ret;
+
+ cam->power_gpio = viafb_gpio_lookup("VGPIO3");
+ cam->reset_gpio = viafb_gpio_lookup("VGPIO2");
+ if (cam->power_gpio < 0 || cam->reset_gpio < 0) {
+ dev_err(&cam->platdev->dev, "Unable to find GPIO lines\n");
+ return -EINVAL;
+ }
+ ret = gpio_request(cam->power_gpio, "viafb-camera");
+ if (ret) {
+ dev_err(&cam->platdev->dev, "Unable to request power GPIO\n");
+ return ret;
+ }
+ ret = gpio_request(cam->reset_gpio, "viafb-camera");
+ if (ret) {
+ dev_err(&cam->platdev->dev, "Unable to request reset GPIO\n");
+ gpio_free(cam->power_gpio);
+ return ret;
+ }
+ gpio_direction_output(cam->power_gpio, 0);
+ gpio_direction_output(cam->reset_gpio, 0);
+ return 0;
+}
+
+/*
+ * Power up the sensor and perform the reset dance.
+ */
+static void via_sensor_power_up(struct via_camera *cam)
+{
+ gpio_set_value(cam->power_gpio, 1);
+ gpio_set_value(cam->reset_gpio, 0);
+ msleep(20); /* Probably excessive */
+ gpio_set_value(cam->reset_gpio, 1);
+ msleep(20);
+}
+
+static void via_sensor_power_down(struct via_camera *cam)
+{
+ gpio_set_value(cam->power_gpio, 0);
+ gpio_set_value(cam->reset_gpio, 0);
+}
+
+
+static void via_sensor_power_release(struct via_camera *cam)
+{
+ via_sensor_power_down(cam);
+ gpio_free(cam->power_gpio);
+ gpio_free(cam->reset_gpio);
+}
+
+/* --------------------------------------------------------------------------*/
+/* Sensor ops */
+
+/*
+ * Manage the ov7670 "flip" bit, which needs special help.
+ */
+static int viacam_set_flip(struct via_camera *cam)
+{
+ struct v4l2_control ctrl;
+
+ memset(&ctrl, 0, sizeof(ctrl));
+ ctrl.id = V4L2_CID_VFLIP;
+ ctrl.value = flip_image;
+ return sensor_call(cam, core, s_ctrl, &ctrl);
+}
+
+/*
+ * Configure the sensor. It's up to the caller to ensure
+ * that the camera is in the correct operating state.
+ */
+static int viacam_configure_sensor(struct via_camera *cam)
+{
+ struct v4l2_mbus_framefmt mbus_fmt;
+ int ret;
+
+ v4l2_fill_mbus_format(&mbus_fmt, &cam->sensor_format, cam->mbus_code);
+ ret = sensor_call(cam, core, init, 0);
+ if (ret == 0)
+ ret = sensor_call(cam, video, s_mbus_fmt, &mbus_fmt);
+ /*
+ * OV7670 does weird things if flip is set *before* format...
+ */
+ if (ret == 0)
+ ret = viacam_set_flip(cam);
+ return ret;
+}
+
+
+
+/* --------------------------------------------------------------------------*/
+/*
+ * Some simple register accessors; they assume that the lock is held.
+ *
+ * Should we want to support the second capture engine, we could
+ * hide the register difference by adding 0x1000 to registers in the
+ * 0x300-350 range.
+ */
+static inline void viacam_write_reg(struct via_camera *cam,
+ int reg, int value)
+{
+ iowrite32(value, cam->mmio + reg);
+}
+
+static inline int viacam_read_reg(struct via_camera *cam, int reg)
+{
+ return ioread32(cam->mmio + reg);
+}
+
+static inline void viacam_write_reg_mask(struct via_camera *cam,
+ int reg, int value, int mask)
+{
+ int tmp = viacam_read_reg(cam, reg);
+
+ tmp = (tmp & ~mask) | (value & mask);
+ viacam_write_reg(cam, reg, tmp);
+}
+
+
+/* --------------------------------------------------------------------------*/
+/* Interrupt management and handling */
+
+static irqreturn_t viacam_quick_irq(int irq, void *data)
+{
+ struct via_camera *cam = data;
+ irqreturn_t ret = IRQ_NONE;
+ int icv;
+
+ /*
+ * All we do here is to clear the interrupts and tell
+ * the handler thread to wake up.
+ */
+ spin_lock(&cam->viadev->reg_lock);
+ icv = viacam_read_reg(cam, VCR_INTCTRL);
+ if (icv & VCR_IC_EAV) {
+ icv |= VCR_IC_EAV|VCR_IC_EVBI|VCR_IC_FFULL;
+ viacam_write_reg(cam, VCR_INTCTRL, icv);
+ ret = IRQ_WAKE_THREAD;
+ }
+ spin_unlock(&cam->viadev->reg_lock);
+ return ret;
+}
+
+/*
+ * Find the next videobuf buffer which has somebody waiting on it.
+ */
+static struct videobuf_buffer *viacam_next_buffer(struct via_camera *cam)
+{
+ unsigned long flags;
+ struct videobuf_buffer *buf = NULL;
+
+ spin_lock_irqsave(&cam->viadev->reg_lock, flags);
+ if (cam->opstate != S_RUNNING)
+ goto out;
+ if (list_empty(&cam->buffer_queue))
+ goto out;
+ buf = list_entry(cam->buffer_queue.next, struct videobuf_buffer, queue);
+ if (!waitqueue_active(&buf->done)) {/* Nobody waiting */
+ buf = NULL;
+ goto out;
+ }
+ list_del(&buf->queue);
+ buf->state = VIDEOBUF_ACTIVE;
+out:
+ spin_unlock_irqrestore(&cam->viadev->reg_lock, flags);
+ return buf;
+}
+
+/*
+ * The threaded IRQ handler.
+ */
+static irqreturn_t viacam_irq(int irq, void *data)
+{
+ int bufn;
+ struct videobuf_buffer *vb;
+ struct via_camera *cam = data;
+ struct videobuf_dmabuf *vdma;
+
+ /*
+ * If there is no place to put the data frame, don't bother
+ * with anything else.
+ */
+ vb = viacam_next_buffer(cam);
+ if (vb == NULL)
+ goto done;
+ /*
+ * Figure out which buffer we just completed.
+ */
+ bufn = (viacam_read_reg(cam, VCR_INTCTRL) & VCR_IC_ACTBUF) >> 3;
+ bufn -= 1;
+ if (bufn < 0)
+ bufn = cam->n_cap_bufs - 1;
+ /*
+ * Copy over the data and let any waiters know.
+ */
+ vdma = videobuf_to_dma(vb);
+ viafb_dma_copy_out_sg(cam->cb_offsets[bufn], vdma->sglist, vdma->sglen);
+ vb->state = VIDEOBUF_DONE;
+ vb->size = cam->user_format.sizeimage;
+ wake_up(&vb->done);
+done:
+ return IRQ_HANDLED;
+}
+
+
+/*
+ * These functions must mess around with the general interrupt
+ * control register, which is relevant to much more than just the
+ * camera. Nothing else uses interrupts, though, as of this writing.
+ * Should that situation change, we'll have to improve support at
+ * the via-core level.
+ */
+static void viacam_int_enable(struct via_camera *cam)
+{
+ viacam_write_reg(cam, VCR_INTCTRL,
+ VCR_IC_INTEN|VCR_IC_EAV|VCR_IC_EVBI|VCR_IC_FFULL);
+ viafb_irq_enable(VDE_I_C0AVEN);
+}
+
+static void viacam_int_disable(struct via_camera *cam)
+{
+ viafb_irq_disable(VDE_I_C0AVEN);
+ viacam_write_reg(cam, VCR_INTCTRL, 0);
+}
+
+
+
+/* --------------------------------------------------------------------------*/
+/* Controller operations */
+
+/*
+ * Set up our capture buffers in framebuffer memory.
+ */
+static int viacam_ctlr_cbufs(struct via_camera *cam)
+{
+ int nbuf = cam->viadev->camera_fbmem_size/cam->sensor_format.sizeimage;
+ int i;
+ unsigned int offset;
+
+ /*
+ * See how many buffers we can work with.
+ */
+ if (nbuf >= 3) {
+ cam->n_cap_bufs = 3;
+ viacam_write_reg_mask(cam, VCR_CAPINTC, VCR_CI_3BUFS,
+ VCR_CI_3BUFS);
+ } else if (nbuf == 2) {
+ cam->n_cap_bufs = 2;
+ viacam_write_reg_mask(cam, VCR_CAPINTC, 0, VCR_CI_3BUFS);
+ } else {
+ cam_warn(cam, "Insufficient frame buffer memory\n");
+ return -ENOMEM;
+ }
+ /*
+ * Set them up.
+ */
+ offset = cam->fb_offset;
+ for (i = 0; i < cam->n_cap_bufs; i++) {
+ cam->cb_offsets[i] = offset;
+ cam->cb_addrs[i] = cam->fbmem + offset;
+ viacam_write_reg(cam, VCR_VBUF1 + i*4, offset & VCR_VBUF_MASK);
+ offset += cam->sensor_format.sizeimage;
+ }
+ return 0;
+}
+
+/*
+ * Set the scaling register for downscaling the image.
+ *
+ * This register works like this... Vertical scaling is enabled
+ * by bit 26; if that bit is set, downscaling is controlled by the
+ * value in bits 16:25. Those bits are divided by 1024 to get
+ * the scaling factor; setting just bit 25 thus cuts the height
+ * in half.
+ *
+ * Horizontal scaling works about the same, but it's enabled by
+ * bit 11, with bits 0:10 giving the numerator of a fraction
+ * (over 2048) for the scaling value.
+ *
+ * This function is naive in that, if the user departs from
+ * the 3x4 VGA scaling factor, the image will distort. We
+ * could work around that if it really seemed important.
+ */
+static void viacam_set_scale(struct via_camera *cam)
+{
+ unsigned int avscale;
+ int sf;
+
+ if (cam->user_format.width == VGA_WIDTH)
+ avscale = 0;
+ else {
+ sf = (cam->user_format.width*2048)/VGA_WIDTH;
+ avscale = VCR_AVS_HEN | sf;
+ }
+ if (cam->user_format.height < VGA_HEIGHT) {
+ sf = (1024*cam->user_format.height)/VGA_HEIGHT;
+ avscale |= VCR_AVS_VEN | (sf << 16);
+ }
+ viacam_write_reg(cam, VCR_AVSCALE, avscale);
+}
+
+
+/*
+ * Configure image-related information into the capture engine.
+ */
+static void viacam_ctlr_image(struct via_camera *cam)
+{
+ int cicreg;
+
+ /*
+ * Disable clock before messing with stuff - from the via
+ * sample driver.
+ */
+ viacam_write_reg(cam, VCR_CAPINTC, ~(VCR_CI_ENABLE|VCR_CI_CLKEN));
+ /*
+ * Set up the controller for VGA resolution, modulo magic
+ * offsets from the via sample driver.
+ */
+ viacam_write_reg(cam, VCR_HORRANGE, 0x06200120);
+ viacam_write_reg(cam, VCR_VERTRANGE, 0x01de0000);
+ viacam_set_scale(cam);
+ /*
+ * Image size info.
+ */
+ viacam_write_reg(cam, VCR_MAXDATA,
+ (cam->sensor_format.height << 16) |
+ (cam->sensor_format.bytesperline >> 3));
+ viacam_write_reg(cam, VCR_MAXVBI, 0);
+ viacam_write_reg(cam, VCR_VSTRIDE,
+ cam->user_format.bytesperline & VCR_VS_STRIDE);
+ /*
+ * Set up the capture interface control register,
+ * everything but the "go" bit.
+ *
+ * The FIFO threshold is a bit of a magic number; 8 is what
+ * VIA's sample code uses.
+ */
+ cicreg = VCR_CI_CLKEN |
+ 0x08000000 | /* FIFO threshold */
+ VCR_CI_FLDINV | /* OLPC-specific? */
+ VCR_CI_VREFINV | /* OLPC-specific? */
+ VCR_CI_DIBOTH | /* Capture both fields */
+ VCR_CI_CCIR601_8;
+ if (cam->n_cap_bufs == 3)
+ cicreg |= VCR_CI_3BUFS;
+ /*
+ * YUV formats need different byte swapping than RGB.
+ */
+ if (cam->user_format.pixelformat == V4L2_PIX_FMT_YUYV)
+ cicreg |= VCR_CI_YUYV;
+ else
+ cicreg |= VCR_CI_UYVY;
+ viacam_write_reg(cam, VCR_CAPINTC, cicreg);
+}
+
+
+static int viacam_config_controller(struct via_camera *cam)
+{
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->viadev->reg_lock, flags);
+ ret = viacam_ctlr_cbufs(cam);
+ if (!ret)
+ viacam_ctlr_image(cam);
+ spin_unlock_irqrestore(&cam->viadev->reg_lock, flags);
+ clear_bit(CF_CONFIG_NEEDED, &cam->flags);
+ return ret;
+}
+
+/*
+ * Make it start grabbing data.
+ */
+static void viacam_start_engine(struct via_camera *cam)
+{
+ spin_lock_irq(&cam->viadev->reg_lock);
+ cam->next_buf = 0;
+ viacam_write_reg_mask(cam, VCR_CAPINTC, VCR_CI_ENABLE, VCR_CI_ENABLE);
+ viacam_int_enable(cam);
+ (void) viacam_read_reg(cam, VCR_CAPINTC); /* Force post */
+ cam->opstate = S_RUNNING;
+ spin_unlock_irq(&cam->viadev->reg_lock);
+}
+
+
+static void viacam_stop_engine(struct via_camera *cam)
+{
+ spin_lock_irq(&cam->viadev->reg_lock);
+ viacam_int_disable(cam);
+ viacam_write_reg_mask(cam, VCR_CAPINTC, 0, VCR_CI_ENABLE);
+ (void) viacam_read_reg(cam, VCR_CAPINTC); /* Force post */
+ cam->opstate = S_IDLE;
+ spin_unlock_irq(&cam->viadev->reg_lock);
+}
+
+
+/* --------------------------------------------------------------------------*/
+/* Videobuf callback ops */
+
+/*
+ * buffer_setup. The purpose of this one would appear to be to tell
+ * videobuf how big a single image is. It's also evidently up to us
+ * to put some sort of limit on the maximum number of buffers allowed.
+ */
+static int viacam_vb_buf_setup(struct videobuf_queue *q,
+ unsigned int *count, unsigned int *size)
+{
+ struct via_camera *cam = q->priv_data;
+
+ *size = cam->user_format.sizeimage;
+ if (*count == 0 || *count > 6) /* Arbitrary number */
+ *count = 6;
+ return 0;
+}
+
+/*
+ * Prepare a buffer.
+ */
+static int viacam_vb_buf_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb, enum v4l2_field field)
+{
+ struct via_camera *cam = q->priv_data;
+
+ vb->size = cam->user_format.sizeimage;
+ vb->width = cam->user_format.width; /* bytesperline???? */
+ vb->height = cam->user_format.height;
+ vb->field = field;
+ if (vb->state == VIDEOBUF_NEEDS_INIT) {
+ int ret = videobuf_iolock(q, vb, NULL);
+ if (ret)
+ return ret;
+ }
+ vb->state = VIDEOBUF_PREPARED;
+ return 0;
+}
+
+/*
+ * We've got a buffer to put data into.
+ *
+ * FIXME: check for a running engine and valid buffers?
+ */
+static void viacam_vb_buf_queue(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ struct via_camera *cam = q->priv_data;
+
+ /*
+ * Note that videobuf holds the lock when it calls
+ * us, so we need not (indeed, cannot) take it here.
+ */
+ vb->state = VIDEOBUF_QUEUED;
+ list_add_tail(&vb->queue, &cam->buffer_queue);
+}
+
+/*
+ * Free a buffer.
+ */
+static void viacam_vb_buf_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ struct via_camera *cam = q->priv_data;
+
+ videobuf_dma_unmap(&cam->platdev->dev, videobuf_to_dma(vb));
+ videobuf_dma_free(videobuf_to_dma(vb));
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static const struct videobuf_queue_ops viacam_vb_ops = {
+ .buf_setup = viacam_vb_buf_setup,
+ .buf_prepare = viacam_vb_buf_prepare,
+ .buf_queue = viacam_vb_buf_queue,
+ .buf_release = viacam_vb_buf_release,
+};
+
+/* --------------------------------------------------------------------------*/
+/* File operations */
+
+static int viacam_open(struct file *filp)
+{
+ struct via_camera *cam = video_drvdata(filp);
+
+ filp->private_data = cam;
+ /*
+ * Note the new user. If this is the first one, we'll also
+ * need to power up the sensor.
+ */
+ mutex_lock(&cam->lock);
+ if (cam->users == 0) {
+ int ret = viafb_request_dma();
+
+ if (ret) {
+ mutex_unlock(&cam->lock);
+ return ret;
+ }
+ via_sensor_power_up(cam);
+ set_bit(CF_CONFIG_NEEDED, &cam->flags);
+ /*
+ * Hook into videobuf. Evidently this cannot fail.
+ */
+ videobuf_queue_sg_init(&cam->vb_queue, &viacam_vb_ops,
+ &cam->platdev->dev, &cam->viadev->reg_lock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
+ sizeof(struct videobuf_buffer), cam, NULL);
+ }
+ (cam->users)++;
+ mutex_unlock(&cam->lock);
+ return 0;
+}
+
+static int viacam_release(struct file *filp)
+{
+ struct via_camera *cam = video_drvdata(filp);
+
+ mutex_lock(&cam->lock);
+ (cam->users)--;
+ /*
+ * If the "owner" is closing, shut down any ongoing
+ * operations.
+ */
+ if (filp == cam->owner) {
+ videobuf_stop(&cam->vb_queue);
+ /*
+ * We don't hold the spinlock here, but, if release()
+ * is being called by the owner, nobody else will
+ * be changing the state. And an extra stop would
+ * not hurt anyway.
+ */
+ if (cam->opstate != S_IDLE)
+ viacam_stop_engine(cam);
+ cam->owner = NULL;
+ }
+ /*
+ * Last one out needs to turn out the lights.
+ */
+ if (cam->users == 0) {
+ videobuf_mmap_free(&cam->vb_queue);
+ via_sensor_power_down(cam);
+ viafb_release_dma();
+ }
+ mutex_unlock(&cam->lock);
+ return 0;
+}
+
+/*
+ * Read a frame from the device.
+ */
+static ssize_t viacam_read(struct file *filp, char __user *buffer,
+ size_t len, loff_t *pos)
+{
+ struct via_camera *cam = video_drvdata(filp);
+ int ret;
+
+ mutex_lock(&cam->lock);
+ /*
+ * Enforce the V4l2 "only one owner gets to read data" rule.
+ */
+ if (cam->owner && cam->owner != filp) {
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ cam->owner = filp;
+ /*
+ * Do we need to configure the hardware?
+ */
+ if (test_bit(CF_CONFIG_NEEDED, &cam->flags)) {
+ ret = viacam_configure_sensor(cam);
+ if (!ret)
+ ret = viacam_config_controller(cam);
+ if (ret)
+ goto out_unlock;
+ }
+ /*
+ * Fire up the capture engine, then have videobuf do
+ * the heavy lifting. Someday it would be good to avoid
+ * stopping and restarting the engine each time.
+ */
+ INIT_LIST_HEAD(&cam->buffer_queue);
+ viacam_start_engine(cam);
+ ret = videobuf_read_stream(&cam->vb_queue, buffer, len, pos, 0,
+ filp->f_flags & O_NONBLOCK);
+ viacam_stop_engine(cam);
+ /* videobuf_stop() ?? */
+
+out_unlock:
+ mutex_unlock(&cam->lock);
+ return ret;
+}
+
+
+static unsigned int viacam_poll(struct file *filp, struct poll_table_struct *pt)
+{
+ struct via_camera *cam = video_drvdata(filp);
+
+ return videobuf_poll_stream(filp, &cam->vb_queue, pt);
+}
+
+
+static int viacam_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct via_camera *cam = video_drvdata(filp);
+
+ return videobuf_mmap_mapper(&cam->vb_queue, vma);
+}
+
+
+
+static const struct v4l2_file_operations viacam_fops = {
+ .owner = THIS_MODULE,
+ .open = viacam_open,
+ .release = viacam_release,
+ .read = viacam_read,
+ .poll = viacam_poll,
+ .mmap = viacam_mmap,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+/*----------------------------------------------------------------------------*/
+/*
+ * The long list of v4l2 ioctl ops
+ */
+
+static int viacam_g_chip_ident(struct file *file, void *priv,
+ struct v4l2_dbg_chip_ident *ident)
+{
+ struct via_camera *cam = priv;
+
+ ident->ident = V4L2_IDENT_NONE;
+ ident->revision = 0;
+ if (v4l2_chip_match_host(&ident->match)) {
+ ident->ident = V4L2_IDENT_VIA_VX855;
+ return 0;
+ }
+ return sensor_call(cam, core, g_chip_ident, ident);
+}
+
+/*
+ * Control ops are passed through to the sensor.
+ */
+static int viacam_queryctrl(struct file *filp, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ struct via_camera *cam = priv;
+ int ret;
+
+ mutex_lock(&cam->lock);
+ ret = sensor_call(cam, core, queryctrl, qc);
+ mutex_unlock(&cam->lock);
+ return ret;
+}
+
+
+static int viacam_g_ctrl(struct file *filp, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct via_camera *cam = priv;
+ int ret;
+
+ mutex_lock(&cam->lock);
+ ret = sensor_call(cam, core, g_ctrl, ctrl);
+ mutex_unlock(&cam->lock);
+ return ret;
+}
+
+
+static int viacam_s_ctrl(struct file *filp, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct via_camera *cam = priv;
+ int ret;
+
+ mutex_lock(&cam->lock);
+ ret = sensor_call(cam, core, s_ctrl, ctrl);
+ mutex_unlock(&cam->lock);
+ return ret;
+}
+
+/*
+ * Only one input.
+ */
+static int viacam_enum_input(struct file *filp, void *priv,
+ struct v4l2_input *input)
+{
+ if (input->index != 0)
+ return -EINVAL;
+
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+ input->std = V4L2_STD_ALL; /* Not sure what should go here */
+ strcpy(input->name, "Camera");
+ return 0;
+}
+
+static int viacam_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int viacam_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ if (i != 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int viacam_s_std(struct file *filp, void *priv, v4l2_std_id *std)
+{
+ return 0;
+}
+
+/*
+ * Video format stuff. Here is our default format until
+ * user space messes with things.
+ */
+static const struct v4l2_pix_format viacam_def_pix_format = {
+ .width = VGA_WIDTH,
+ .height = VGA_HEIGHT,
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ .field = V4L2_FIELD_NONE,
+ .bytesperline = VGA_WIDTH * 2,
+ .sizeimage = VGA_WIDTH * VGA_HEIGHT * 2,
+};
+
+static const enum v4l2_mbus_pixelcode via_def_mbus_code = V4L2_MBUS_FMT_YUYV8_2X8;
+
+static int viacam_enum_fmt_vid_cap(struct file *filp, void *priv,
+ struct v4l2_fmtdesc *fmt)
+{
+ if (fmt->index >= N_VIA_FMTS)
+ return -EINVAL;
+ strlcpy(fmt->description, via_formats[fmt->index].desc,
+ sizeof(fmt->description));
+ fmt->pixelformat = via_formats[fmt->index].pixelformat;
+ return 0;
+}
+
+/*
+ * Figure out proper image dimensions, but always force the
+ * sensor to VGA.
+ */
+static void viacam_fmt_pre(struct v4l2_pix_format *userfmt,
+ struct v4l2_pix_format *sensorfmt)
+{
+ *sensorfmt = *userfmt;
+ if (userfmt->width < QCIF_WIDTH || userfmt->height < QCIF_HEIGHT) {
+ userfmt->width = QCIF_WIDTH;
+ userfmt->height = QCIF_HEIGHT;
+ }
+ if (userfmt->width > VGA_WIDTH || userfmt->height > VGA_HEIGHT) {
+ userfmt->width = VGA_WIDTH;
+ userfmt->height = VGA_HEIGHT;
+ }
+ sensorfmt->width = VGA_WIDTH;
+ sensorfmt->height = VGA_HEIGHT;
+}
+
+static void viacam_fmt_post(struct v4l2_pix_format *userfmt,
+ struct v4l2_pix_format *sensorfmt)
+{
+ struct via_format *f = via_find_format(userfmt->pixelformat);
+
+ sensorfmt->bytesperline = sensorfmt->width * f->bpp;
+ sensorfmt->sizeimage = sensorfmt->height * sensorfmt->bytesperline;
+ userfmt->pixelformat = sensorfmt->pixelformat;
+ userfmt->field = sensorfmt->field;
+ userfmt->bytesperline = 2 * userfmt->width;
+ userfmt->sizeimage = userfmt->bytesperline * userfmt->height;
+}
+
+
+/*
+ * The real work of figuring out a workable format.
+ */
+static int viacam_do_try_fmt(struct via_camera *cam,
+ struct v4l2_pix_format *upix, struct v4l2_pix_format *spix)
+{
+ int ret;
+ struct v4l2_mbus_framefmt mbus_fmt;
+ struct via_format *f = via_find_format(upix->pixelformat);
+
+ upix->pixelformat = f->pixelformat;
+ viacam_fmt_pre(upix, spix);
+ v4l2_fill_mbus_format(&mbus_fmt, upix, f->mbus_code);
+ ret = sensor_call(cam, video, try_mbus_fmt, &mbus_fmt);
+ v4l2_fill_pix_format(spix, &mbus_fmt);
+ viacam_fmt_post(upix, spix);
+ return ret;
+}
+
+
+
+static int viacam_try_fmt_vid_cap(struct file *filp, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct via_camera *cam = priv;
+ struct v4l2_format sfmt;
+ int ret;
+
+ mutex_lock(&cam->lock);
+ ret = viacam_do_try_fmt(cam, &fmt->fmt.pix, &sfmt.fmt.pix);
+ mutex_unlock(&cam->lock);
+ return ret;
+}
+
+
+static int viacam_g_fmt_vid_cap(struct file *filp, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct via_camera *cam = priv;
+
+ mutex_lock(&cam->lock);
+ fmt->fmt.pix = cam->user_format;
+ mutex_unlock(&cam->lock);
+ return 0;
+}
+
+static int viacam_s_fmt_vid_cap(struct file *filp, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct via_camera *cam = priv;
+ int ret;
+ struct v4l2_format sfmt;
+ struct via_format *f = via_find_format(fmt->fmt.pix.pixelformat);
+
+ /*
+ * Camera must be idle or we can't mess with the
+ * video setup.
+ */
+ mutex_lock(&cam->lock);
+ if (cam->opstate != S_IDLE) {
+ ret = -EBUSY;
+ goto out;
+ }
+ /*
+ * Let the sensor code look over and tweak the
+ * requested formatting.
+ */
+ ret = viacam_do_try_fmt(cam, &fmt->fmt.pix, &sfmt.fmt.pix);
+ if (ret)
+ goto out;
+ /*
+ * OK, let's commit to the new format.
+ */
+ cam->user_format = fmt->fmt.pix;
+ cam->sensor_format = sfmt.fmt.pix;
+ cam->mbus_code = f->mbus_code;
+ ret = viacam_configure_sensor(cam);
+ if (!ret)
+ ret = viacam_config_controller(cam);
+out:
+ mutex_unlock(&cam->lock);
+ return ret;
+}
+
+static int viacam_querycap(struct file *filp, void *priv,
+ struct v4l2_capability *cap)
+{
+ strcpy(cap->driver, "via-camera");
+ strcpy(cap->card, "via-camera");
+ cap->version = 1;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+ return 0;
+}
+
+/*
+ * Streaming operations - pure videobuf stuff.
+ */
+static int viacam_reqbufs(struct file *filp, void *priv,
+ struct v4l2_requestbuffers *rb)
+{
+ struct via_camera *cam = priv;
+
+ return videobuf_reqbufs(&cam->vb_queue, rb);
+}
+
+static int viacam_querybuf(struct file *filp, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct via_camera *cam = priv;
+
+ return videobuf_querybuf(&cam->vb_queue, buf);
+}
+
+static int viacam_qbuf(struct file *filp, void *priv, struct v4l2_buffer *buf)
+{
+ struct via_camera *cam = priv;
+
+ return videobuf_qbuf(&cam->vb_queue, buf);
+}
+
+static int viacam_dqbuf(struct file *filp, void *priv, struct v4l2_buffer *buf)
+{
+ struct via_camera *cam = priv;
+
+ return videobuf_dqbuf(&cam->vb_queue, buf, filp->f_flags & O_NONBLOCK);
+}
+
+static int viacam_streamon(struct file *filp, void *priv, enum v4l2_buf_type t)
+{
+ struct via_camera *cam = priv;
+ int ret = 0;
+
+ if (t != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ mutex_lock(&cam->lock);
+ if (cam->opstate != S_IDLE) {
+ ret = -EBUSY;
+ goto out;
+ }
+ /*
+ * Enforce the V4l2 "only one owner gets to read data" rule.
+ */
+ if (cam->owner && cam->owner != filp) {
+ ret = -EBUSY;
+ goto out;
+ }
+ cam->owner = filp;
+ /*
+ * Configure things if need be.
+ */
+ if (test_bit(CF_CONFIG_NEEDED, &cam->flags)) {
+ ret = viacam_configure_sensor(cam);
+ if (ret)
+ goto out;
+ ret = viacam_config_controller(cam);
+ if (ret)
+ goto out;
+ }
+ /*
+ * If the CPU goes into C3, the DMA transfer gets corrupted and
+ * users start filing unsightly bug reports. Put in a "latency"
+ * requirement which will keep the CPU out of the deeper sleep
+ * states.
+ */
+ pm_qos_add_request(&cam->qos_request, PM_QOS_CPU_DMA_LATENCY, 50);
+ /*
+ * Fire things up.
+ */
+ INIT_LIST_HEAD(&cam->buffer_queue);
+ ret = videobuf_streamon(&cam->vb_queue);
+ if (!ret)
+ viacam_start_engine(cam);
+out:
+ mutex_unlock(&cam->lock);
+ return ret;
+}
+
+static int viacam_streamoff(struct file *filp, void *priv, enum v4l2_buf_type t)
+{
+ struct via_camera *cam = priv;
+ int ret;
+
+ if (t != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ mutex_lock(&cam->lock);
+ if (cam->opstate != S_RUNNING) {
+ ret = -EINVAL;
+ goto out;
+ }
+ pm_qos_remove_request(&cam->qos_request);
+ viacam_stop_engine(cam);
+ /*
+ * Videobuf will recycle all of the outstanding buffers, but
+ * we should be sure we don't retain any references to
+ * any of them.
+ */
+ ret = videobuf_streamoff(&cam->vb_queue);
+ INIT_LIST_HEAD(&cam->buffer_queue);
+out:
+ mutex_unlock(&cam->lock);
+ return ret;
+}
+
+/* G/S_PARM */
+
+static int viacam_g_parm(struct file *filp, void *priv,
+ struct v4l2_streamparm *parm)
+{
+ struct via_camera *cam = priv;
+ int ret;
+
+ mutex_lock(&cam->lock);
+ ret = sensor_call(cam, video, g_parm, parm);
+ mutex_unlock(&cam->lock);
+ parm->parm.capture.readbuffers = cam->n_cap_bufs;
+ return ret;
+}
+
+static int viacam_s_parm(struct file *filp, void *priv,
+ struct v4l2_streamparm *parm)
+{
+ struct via_camera *cam = priv;
+ int ret;
+
+ mutex_lock(&cam->lock);
+ ret = sensor_call(cam, video, s_parm, parm);
+ mutex_unlock(&cam->lock);
+ parm->parm.capture.readbuffers = cam->n_cap_bufs;
+ return ret;
+}
+
+static int viacam_enum_framesizes(struct file *filp, void *priv,
+ struct v4l2_frmsizeenum *sizes)
+{
+ if (sizes->index != 0)
+ return -EINVAL;
+ sizes->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
+ sizes->stepwise.min_width = QCIF_WIDTH;
+ sizes->stepwise.min_height = QCIF_HEIGHT;
+ sizes->stepwise.max_width = VGA_WIDTH;
+ sizes->stepwise.max_height = VGA_HEIGHT;
+ sizes->stepwise.step_width = sizes->stepwise.step_height = 1;
+ return 0;
+}
+
+static int viacam_enum_frameintervals(struct file *filp, void *priv,
+ struct v4l2_frmivalenum *interval)
+{
+ struct via_camera *cam = priv;
+ int ret;
+
+ mutex_lock(&cam->lock);
+ ret = sensor_call(cam, video, enum_frameintervals, interval);
+ mutex_unlock(&cam->lock);
+ return ret;
+}
+
+
+
+static const struct v4l2_ioctl_ops viacam_ioctl_ops = {
+ .vidioc_g_chip_ident = viacam_g_chip_ident,
+ .vidioc_queryctrl = viacam_queryctrl,
+ .vidioc_g_ctrl = viacam_g_ctrl,
+ .vidioc_s_ctrl = viacam_s_ctrl,
+ .vidioc_enum_input = viacam_enum_input,
+ .vidioc_g_input = viacam_g_input,
+ .vidioc_s_input = viacam_s_input,
+ .vidioc_s_std = viacam_s_std,
+ .vidioc_enum_fmt_vid_cap = viacam_enum_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = viacam_try_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = viacam_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = viacam_s_fmt_vid_cap,
+ .vidioc_querycap = viacam_querycap,
+ .vidioc_reqbufs = viacam_reqbufs,
+ .vidioc_querybuf = viacam_querybuf,
+ .vidioc_qbuf = viacam_qbuf,
+ .vidioc_dqbuf = viacam_dqbuf,
+ .vidioc_streamon = viacam_streamon,
+ .vidioc_streamoff = viacam_streamoff,
+ .vidioc_g_parm = viacam_g_parm,
+ .vidioc_s_parm = viacam_s_parm,
+ .vidioc_enum_framesizes = viacam_enum_framesizes,
+ .vidioc_enum_frameintervals = viacam_enum_frameintervals,
+};
+
+/*----------------------------------------------------------------------------*/
+
+/*
+ * Power management.
+ */
+#ifdef CONFIG_PM
+
+static int viacam_suspend(void *priv)
+{
+ struct via_camera *cam = priv;
+ enum viacam_opstate state = cam->opstate;
+
+ if (cam->opstate != S_IDLE) {
+ viacam_stop_engine(cam);
+ cam->opstate = state; /* So resume restarts */
+ }
+
+ return 0;
+}
+
+static int viacam_resume(void *priv)
+{
+ struct via_camera *cam = priv;
+ int ret = 0;
+
+ /*
+ * Get back to a reasonable operating state.
+ */
+ via_write_reg_mask(VIASR, 0x78, 0, 0x80);
+ via_write_reg_mask(VIASR, 0x1e, 0xc0, 0xc0);
+ viacam_int_disable(cam);
+ set_bit(CF_CONFIG_NEEDED, &cam->flags);
+ /*
+ * Make sure the sensor's power state is correct
+ */
+ if (cam->users > 0)
+ via_sensor_power_up(cam);
+ else
+ via_sensor_power_down(cam);
+ /*
+ * If it was operating, try to restart it.
+ */
+ if (cam->opstate != S_IDLE) {
+ mutex_lock(&cam->lock);
+ ret = viacam_configure_sensor(cam);
+ if (!ret)
+ ret = viacam_config_controller(cam);
+ mutex_unlock(&cam->lock);
+ if (!ret)
+ viacam_start_engine(cam);
+ }
+
+ return ret;
+}
+
+static struct viafb_pm_hooks viacam_pm_hooks = {
+ .suspend = viacam_suspend,
+ .resume = viacam_resume
+};
+
+#endif /* CONFIG_PM */
+
+/*
+ * Setup stuff.
+ */
+
+static struct video_device viacam_v4l_template = {
+ .name = "via-camera",
+ .minor = -1,
+ .tvnorms = V4L2_STD_NTSC_M,
+ .current_norm = V4L2_STD_NTSC_M,
+ .fops = &viacam_fops,
+ .ioctl_ops = &viacam_ioctl_ops,
+ .release = video_device_release_empty, /* Check this */
+};
+
+/*
+ * The OLPC folks put the serial port on the same pin as
+ * the camera. They also get grumpy if we break the
+ * serial port and keep them from using it. So we have
+ * to check the serial enable bit and not step on it.
+ */
+#define VIACAM_SERIAL_DEVFN 0x88
+#define VIACAM_SERIAL_CREG 0x46
+#define VIACAM_SERIAL_BIT 0x40
+
+static __devinit bool viacam_serial_is_enabled(void)
+{
+ struct pci_bus *pbus = pci_find_bus(0, 0);
+ u8 cbyte;
+
+ if (!pbus)
+ return false;
+ pci_bus_read_config_byte(pbus, VIACAM_SERIAL_DEVFN,
+ VIACAM_SERIAL_CREG, &cbyte);
+ if ((cbyte & VIACAM_SERIAL_BIT) == 0)
+ return false; /* Not enabled */
+ if (override_serial == 0) {
+ printk(KERN_NOTICE "Via camera: serial port is enabled, " \
+ "refusing to load.\n");
+ printk(KERN_NOTICE "Specify override_serial=1 to force " \
+ "module loading.\n");
+ return true;
+ }
+ printk(KERN_NOTICE "Via camera: overriding serial port\n");
+ pci_bus_write_config_byte(pbus, VIACAM_SERIAL_DEVFN,
+ VIACAM_SERIAL_CREG, cbyte & ~VIACAM_SERIAL_BIT);
+ return false;
+}
+
+static __devinit int viacam_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct i2c_adapter *sensor_adapter;
+ struct viafb_dev *viadev = pdev->dev.platform_data;
+
+ /*
+ * Note that there are actually two capture channels on
+ * the device. We only deal with one for now. That
+ * is encoded here; nothing else assumes it's dealing with
+ * a unique capture device.
+ */
+ struct via_camera *cam;
+
+ /*
+ * Ensure that frame buffer memory has been set aside for
+ * this purpose. As an arbitrary limit, refuse to work
+ * with less than two frames of VGA 16-bit data.
+ *
+ * If we ever support the second port, we'll need to set
+ * aside more memory.
+ */
+ if (viadev->camera_fbmem_size < (VGA_HEIGHT*VGA_WIDTH*4)) {
+ printk(KERN_ERR "viacam: insufficient FB memory reserved\n");
+ return -ENOMEM;
+ }
+ if (viadev->engine_mmio == NULL) {
+ printk(KERN_ERR "viacam: No I/O memory, so no pictures\n");
+ return -ENOMEM;
+ }
+
+ if (machine_is_olpc() && viacam_serial_is_enabled())
+ return -EBUSY;
+
+ /*
+ * Basic structure initialization.
+ */
+ cam = kzalloc (sizeof(struct via_camera), GFP_KERNEL);
+ if (cam == NULL)
+ return -ENOMEM;
+ via_cam_info = cam;
+ cam->platdev = pdev;
+ cam->viadev = viadev;
+ cam->users = 0;
+ cam->owner = NULL;
+ cam->opstate = S_IDLE;
+ cam->user_format = cam->sensor_format = viacam_def_pix_format;
+ mutex_init(&cam->lock);
+ INIT_LIST_HEAD(&cam->buffer_queue);
+ cam->mmio = viadev->engine_mmio;
+ cam->fbmem = viadev->fbmem;
+ cam->fb_offset = viadev->camera_fbmem_offset;
+ cam->flags = 1 << CF_CONFIG_NEEDED;
+ cam->mbus_code = via_def_mbus_code;
+ /*
+ * Tell V4L that we exist.
+ */
+ ret = v4l2_device_register(&pdev->dev, &cam->v4l2_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to register v4l2 device\n");
+ return ret;
+ }
+ /*
+ * Convince the system that we can do DMA.
+ */
+ pdev->dev.dma_mask = &viadev->pdev->dma_mask;
+ dma_set_mask(&pdev->dev, 0xffffffff);
+ /*
+ * Fire up the capture port. The write to 0x78 looks purely
+ * OLPCish; any system will need to tweak 0x1e.
+ */
+ via_write_reg_mask(VIASR, 0x78, 0, 0x80);
+ via_write_reg_mask(VIASR, 0x1e, 0xc0, 0xc0);
+ /*
+ * Get the sensor powered up.
+ */
+ ret = via_sensor_power_setup(cam);
+ if (ret)
+ goto out_unregister;
+ via_sensor_power_up(cam);
+
+ /*
+ * See if we can't find it on the bus. The VIA_PORT_31 assumption
+ * is OLPC-specific. 0x42 assumption is ov7670-specific.
+ */
+ sensor_adapter = viafb_find_i2c_adapter(VIA_PORT_31);
+ cam->sensor = v4l2_i2c_new_subdev(&cam->v4l2_dev, sensor_adapter,
+ "ov7670", 0x42 >> 1, NULL);
+ if (cam->sensor == NULL) {
+ dev_err(&pdev->dev, "Unable to find the sensor!\n");
+ ret = -ENODEV;
+ goto out_power_down;
+ }
+ /*
+ * Get the IRQ.
+ */
+ viacam_int_disable(cam);
+ ret = request_threaded_irq(viadev->pdev->irq, viacam_quick_irq,
+ viacam_irq, IRQF_SHARED, "via-camera", cam);
+ if (ret)
+ goto out_power_down;
+ /*
+ * Tell V4l2 that we exist.
+ */
+ cam->vdev = viacam_v4l_template;
+ cam->vdev.v4l2_dev = &cam->v4l2_dev;
+ ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1);
+ if (ret)
+ goto out_irq;
+ video_set_drvdata(&cam->vdev, cam);
+
+#ifdef CONFIG_PM
+ /*
+ * Hook into PM events
+ */
+ viacam_pm_hooks.private = cam;
+ viafb_pm_register(&viacam_pm_hooks);
+#endif
+
+ /* Power the sensor down until somebody opens the device */
+ via_sensor_power_down(cam);
+ return 0;
+
+out_irq:
+ free_irq(viadev->pdev->irq, cam);
+out_power_down:
+ via_sensor_power_release(cam);
+out_unregister:
+ v4l2_device_unregister(&cam->v4l2_dev);
+ return ret;
+}
+
+static __devexit int viacam_remove(struct platform_device *pdev)
+{
+ struct via_camera *cam = via_cam_info;
+ struct viafb_dev *viadev = pdev->dev.platform_data;
+
+ video_unregister_device(&cam->vdev);
+ v4l2_device_unregister(&cam->v4l2_dev);
+ free_irq(viadev->pdev->irq, cam);
+ via_sensor_power_release(cam);
+ via_cam_info = NULL;
+ return 0;
+}
+
+static struct platform_driver viacam_driver = {
+ .driver = {
+ .name = "viafb-camera",
+ },
+ .probe = viacam_probe,
+ .remove = viacam_remove,
+};
+
+module_platform_driver(viacam_driver);
diff --git a/drivers/media/video/via-camera.h b/drivers/media/video/via-camera.h
new file mode 100644
index 00000000..b12a4b3d
--- /dev/null
+++ b/drivers/media/video/via-camera.h
@@ -0,0 +1,93 @@
+/*
+ * VIA Camera register definitions.
+ */
+#define VCR_INTCTRL 0x300 /* Capture interrupt control */
+#define VCR_IC_EAV 0x0001 /* End of active video status */
+#define VCR_IC_EVBI 0x0002 /* End of VBI status */
+#define VCR_IC_FBOTFLD 0x0004 /* "flipping" Bottom field is active */
+#define VCR_IC_ACTBUF 0x0018 /* Active video buffer */
+#define VCR_IC_VSYNC 0x0020 /* 0 = VB, 1 = active video */
+#define VCR_IC_BOTFLD 0x0040 /* Bottom field is active */
+#define VCR_IC_FFULL 0x0080 /* FIFO full */
+#define VCR_IC_INTEN 0x0100 /* End of active video int. enable */
+#define VCR_IC_VBIINT 0x0200 /* End of VBI int enable */
+#define VCR_IC_VBIBUF 0x0400 /* Current VBI buffer */
+
+#define VCR_TSC 0x308 /* Transport stream control */
+#define VCR_TSC_ENABLE 0x000001 /* Transport stream input enable */
+#define VCR_TSC_DROPERR 0x000002 /* Drop error packets */
+#define VCR_TSC_METHOD 0x00000c /* DMA method (non-functional) */
+#define VCR_TSC_COUNT 0x07fff0 /* KByte or packet count */
+#define VCR_TSC_CBMODE 0x080000 /* Change buffer by byte count */
+#define VCR_TSC_PSSIG 0x100000 /* Packet starting signal disable */
+#define VCR_TSC_BE 0x200000 /* MSB first (serial mode) */
+#define VCR_TSC_SERIAL 0x400000 /* Serial input (0 = parallel) */
+
+#define VCR_CAPINTC 0x310 /* Capture interface control */
+#define VCR_CI_ENABLE 0x00000001 /* Capture enable */
+#define VCR_CI_BSS 0x00000002 /* WTF "bit stream selection" */
+#define VCR_CI_3BUFS 0x00000004 /* 1 = 3 buffers, 0 = 2 buffers */
+#define VCR_CI_VIPEN 0x00000008 /* VIP enable */
+#define VCR_CI_CCIR601_8 0 /* CCIR601 input stream, 8 bit */
+#define VCR_CI_CCIR656_8 0x00000010 /* ... CCIR656, 8 bit */
+#define VCR_CI_CCIR601_16 0x00000020 /* ... CCIR601, 16 bit */
+#define VCR_CI_CCIR656_16 0x00000030 /* ... CCIR656, 16 bit */
+#define VCR_CI_HDMODE 0x00000040 /* CCIR656-16 hdr decode mode; 1=16b */
+#define VCR_CI_BSWAP 0x00000080 /* Swap bytes (16-bit) */
+#define VCR_CI_YUYV 0 /* Byte order 0123 */
+#define VCR_CI_UYVY 0x00000100 /* Byte order 1032 */
+#define VCR_CI_YVYU 0x00000200 /* Byte order 0321 */
+#define VCR_CI_VYUY 0x00000300 /* Byte order 3012 */
+#define VCR_CI_VIPTYPE 0x00000400 /* VIP type */
+#define VCR_CI_IFSEN 0x00000800 /* Input field signal enable */
+#define VCR_CI_DIODD 0 /* De-interlace odd, 30fps */
+#define VCR_CI_DIEVEN 0x00001000 /* ...even field, 30fps */
+#define VCR_CI_DIBOTH 0x00002000 /* ...both fields, 60fps */
+#define VCR_CI_DIBOTH30 0x00003000 /* ...both fields, 30fps interlace */
+#define VCR_CI_CONVTYPE 0x00004000 /* 4:2:2 to 4:4:4; 1 = interpolate */
+#define VCR_CI_CFC 0x00008000 /* Capture flipping control */
+#define VCR_CI_FILTER 0x00070000 /* Horiz filter mode select
+ 000 = none
+ 001 = 2 tap
+ 010 = 3 tap
+ 011 = 4 tap
+ 100 = 5 tap */
+#define VCR_CI_CLKINV 0x00080000 /* Input CLK inverted */
+#define VCR_CI_VREFINV 0x00100000 /* VREF inverted */
+#define VCR_CI_HREFINV 0x00200000 /* HREF inverted */
+#define VCR_CI_FLDINV 0x00400000 /* Field inverted */
+#define VCR_CI_CLKPIN 0x00800000 /* Capture clock pin */
+#define VCR_CI_THRESH 0x0f000000 /* Capture fifo threshold */
+#define VCR_CI_HRLE 0x10000000 /* Positive edge of HREF */
+#define VCR_CI_VRLE 0x20000000 /* Positive edge of VREF */
+#define VCR_CI_OFLDINV 0x40000000 /* Field output inverted */
+#define VCR_CI_CLKEN 0x80000000 /* Capture clock enable */
+
+#define VCR_HORRANGE 0x314 /* Active video horizontal range */
+#define VCR_VERTRANGE 0x318 /* Active video vertical range */
+#define VCR_AVSCALE 0x31c /* Active video scaling control */
+#define VCR_AVS_HEN 0x00000800 /* Horizontal scale enable */
+#define VCR_AVS_VEN 0x04000000 /* Vertical enable */
+#define VCR_VBIHOR 0x320 /* VBI Data horizontal range */
+#define VCR_VBIVERT 0x324 /* VBI data vertical range */
+#define VCR_VBIBUF1 0x328 /* First VBI buffer */
+#define VCR_VBISTRIDE 0x32c /* VBI stride */
+#define VCR_ANCDATACNT 0x330 /* Ancillary data count setting */
+#define VCR_MAXDATA 0x334 /* Active data count of active video */
+#define VCR_MAXVBI 0x338 /* Maximum data count of VBI */
+#define VCR_CAPDATA 0x33c /* Capture data count */
+#define VCR_VBUF1 0x340 /* First video buffer */
+#define VCR_VBUF2 0x344 /* Second video buffer */
+#define VCR_VBUF3 0x348 /* Third video buffer */
+#define VCR_VBUF_MASK 0x1ffffff0 /* Bits 28:4 */
+#define VCR_VBIBUF2 0x34c /* Second VBI buffer */
+#define VCR_VSTRIDE 0x350 /* Stride of video + coring control */
+#define VCR_VS_STRIDE_SHIFT 4
+#define VCR_VS_STRIDE 0x00001ff0 /* Stride (8-byte units) */
+#define VCR_VS_CCD 0x007f0000 /* Coring compare data */
+#define VCR_VS_COREEN 0x00800000 /* Coring enable */
+#define VCR_TS0ERR 0x354 /* TS buffer 0 error indicator */
+#define VCR_TS1ERR 0x358 /* TS buffer 0 error indicator */
+#define VCR_TS2ERR 0x35c /* TS buffer 0 error indicator */
+
+/* Add 0x1000 for the second capture engine registers */
diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c
new file mode 100644
index 00000000..de4fa4eb
--- /dev/null
+++ b/drivers/media/video/videobuf-core.c
@@ -0,0 +1,1204 @@
+/*
+ * generic helper functions for handling video4linux capture buffers
+ *
+ * (c) 2007 Mauro Carvalho Chehab, <mchehab@infradead.org>
+ *
+ * Highly based on video-buf written originally by:
+ * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org>
+ * (c) 2006 Mauro Carvalho Chehab, <mchehab@infradead.org>
+ * (c) 2006 Ted Walther and John Sokol
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+
+#include <media/videobuf-core.h>
+
+#define MAGIC_BUFFER 0x20070728
+#define MAGIC_CHECK(is, should) \
+ do { \
+ if (unlikely((is) != (should))) { \
+ printk(KERN_ERR \
+ "magic mismatch: %x (expected %x)\n", \
+ is, should); \
+ BUG(); \
+ } \
+ } while (0)
+
+static int debug;
+module_param(debug, int, 0644);
+
+MODULE_DESCRIPTION("helper module to manage video4linux buffers");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
+MODULE_LICENSE("GPL");
+
+#define dprintk(level, fmt, arg...) \
+ do { \
+ if (debug >= level) \
+ printk(KERN_DEBUG "vbuf: " fmt, ## arg); \
+ } while (0)
+
+/* --------------------------------------------------------------------- */
+
+#define CALL(q, f, arg...) \
+ ((q->int_ops->f) ? q->int_ops->f(arg) : 0)
+
+struct videobuf_buffer *videobuf_alloc_vb(struct videobuf_queue *q)
+{
+ struct videobuf_buffer *vb;
+
+ BUG_ON(q->msize < sizeof(*vb));
+
+ if (!q->int_ops || !q->int_ops->alloc_vb) {
+ printk(KERN_ERR "No specific ops defined!\n");
+ BUG();
+ }
+
+ vb = q->int_ops->alloc_vb(q->msize);
+ if (NULL != vb) {
+ init_waitqueue_head(&vb->done);
+ vb->magic = MAGIC_BUFFER;
+ }
+
+ return vb;
+}
+EXPORT_SYMBOL_GPL(videobuf_alloc_vb);
+
+static int is_state_active_or_queued(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ unsigned long flags;
+ bool rc;
+
+ spin_lock_irqsave(q->irqlock, flags);
+ rc = vb->state != VIDEOBUF_ACTIVE && vb->state != VIDEOBUF_QUEUED;
+ spin_unlock_irqrestore(q->irqlock, flags);
+ return rc;
+};
+
+int videobuf_waiton(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ int non_blocking, int intr)
+{
+ bool is_ext_locked;
+ int ret = 0;
+
+ MAGIC_CHECK(vb->magic, MAGIC_BUFFER);
+
+ if (non_blocking) {
+ if (is_state_active_or_queued(q, vb))
+ return 0;
+ return -EAGAIN;
+ }
+
+ is_ext_locked = q->ext_lock && mutex_is_locked(q->ext_lock);
+
+ /* Release vdev lock to prevent this wait from blocking outside access to
+ the device. */
+ if (is_ext_locked)
+ mutex_unlock(q->ext_lock);
+ if (intr)
+ ret = wait_event_interruptible(vb->done, is_state_active_or_queued(q, vb));
+ else
+ wait_event(vb->done, is_state_active_or_queued(q, vb));
+ /* Relock */
+ if (is_ext_locked)
+ mutex_lock(q->ext_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(videobuf_waiton);
+
+int videobuf_iolock(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ struct v4l2_framebuffer *fbuf)
+{
+ MAGIC_CHECK(vb->magic, MAGIC_BUFFER);
+ MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
+
+ return CALL(q, iolock, q, vb, fbuf);
+}
+EXPORT_SYMBOL_GPL(videobuf_iolock);
+
+void *videobuf_queue_to_vaddr(struct videobuf_queue *q,
+ struct videobuf_buffer *buf)
+{
+ if (q->int_ops->vaddr)
+ return q->int_ops->vaddr(buf);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(videobuf_queue_to_vaddr);
+
+/* --------------------------------------------------------------------- */
+
+
+void videobuf_queue_core_init(struct videobuf_queue *q,
+ const struct videobuf_queue_ops *ops,
+ struct device *dev,
+ spinlock_t *irqlock,
+ enum v4l2_buf_type type,
+ enum v4l2_field field,
+ unsigned int msize,
+ void *priv,
+ struct videobuf_qtype_ops *int_ops,
+ struct mutex *ext_lock)
+{
+ BUG_ON(!q);
+ memset(q, 0, sizeof(*q));
+ q->irqlock = irqlock;
+ q->ext_lock = ext_lock;
+ q->dev = dev;
+ q->type = type;
+ q->field = field;
+ q->msize = msize;
+ q->ops = ops;
+ q->priv_data = priv;
+ q->int_ops = int_ops;
+
+ /* All buffer operations are mandatory */
+ BUG_ON(!q->ops->buf_setup);
+ BUG_ON(!q->ops->buf_prepare);
+ BUG_ON(!q->ops->buf_queue);
+ BUG_ON(!q->ops->buf_release);
+
+ /* Lock is mandatory for queue_cancel to work */
+ BUG_ON(!irqlock);
+
+ /* Having implementations for abstract methods are mandatory */
+ BUG_ON(!q->int_ops);
+
+ mutex_init(&q->vb_lock);
+ init_waitqueue_head(&q->wait);
+ INIT_LIST_HEAD(&q->stream);
+}
+EXPORT_SYMBOL_GPL(videobuf_queue_core_init);
+
+/* Locking: Only usage in bttv unsafe find way to remove */
+int videobuf_queue_is_busy(struct videobuf_queue *q)
+{
+ int i;
+
+ MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
+
+ if (q->streaming) {
+ dprintk(1, "busy: streaming active\n");
+ return 1;
+ }
+ if (q->reading) {
+ dprintk(1, "busy: pending read #1\n");
+ return 1;
+ }
+ if (q->read_buf) {
+ dprintk(1, "busy: pending read #2\n");
+ return 1;
+ }
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ if (NULL == q->bufs[i])
+ continue;
+ if (q->bufs[i]->map) {
+ dprintk(1, "busy: buffer #%d mapped\n", i);
+ return 1;
+ }
+ if (q->bufs[i]->state == VIDEOBUF_QUEUED) {
+ dprintk(1, "busy: buffer #%d queued\n", i);
+ return 1;
+ }
+ if (q->bufs[i]->state == VIDEOBUF_ACTIVE) {
+ dprintk(1, "busy: buffer #%d avtive\n", i);
+ return 1;
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(videobuf_queue_is_busy);
+
+/**
+ * __videobuf_free() - free all the buffers and their control structures
+ *
+ * This function can only be called if streaming/reading is off, i.e. no buffers
+ * are under control of the driver.
+ */
+/* Locking: Caller holds q->vb_lock */
+static int __videobuf_free(struct videobuf_queue *q)
+{
+ int i;
+
+ dprintk(1, "%s\n", __func__);
+ if (!q)
+ return 0;
+
+ if (q->streaming || q->reading) {
+ dprintk(1, "Cannot free buffers when streaming or reading\n");
+ return -EBUSY;
+ }
+
+ MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
+
+ for (i = 0; i < VIDEO_MAX_FRAME; i++)
+ if (q->bufs[i] && q->bufs[i]->map) {
+ dprintk(1, "Cannot free mmapped buffers\n");
+ return -EBUSY;
+ }
+
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ if (NULL == q->bufs[i])
+ continue;
+ q->ops->buf_release(q, q->bufs[i]);
+ kfree(q->bufs[i]);
+ q->bufs[i] = NULL;
+ }
+
+ return 0;
+}
+
+/* Locking: Caller holds q->vb_lock */
+void videobuf_queue_cancel(struct videobuf_queue *q)
+{
+ unsigned long flags = 0;
+ int i;
+
+ q->streaming = 0;
+ q->reading = 0;
+ wake_up_interruptible_sync(&q->wait);
+
+ /* remove queued buffers from list */
+ spin_lock_irqsave(q->irqlock, flags);
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ if (NULL == q->bufs[i])
+ continue;
+ if (q->bufs[i]->state == VIDEOBUF_QUEUED) {
+ list_del(&q->bufs[i]->queue);
+ q->bufs[i]->state = VIDEOBUF_ERROR;
+ wake_up_all(&q->bufs[i]->done);
+ }
+ }
+ spin_unlock_irqrestore(q->irqlock, flags);
+
+ /* free all buffers + clear queue */
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ if (NULL == q->bufs[i])
+ continue;
+ q->ops->buf_release(q, q->bufs[i]);
+ }
+ INIT_LIST_HEAD(&q->stream);
+}
+EXPORT_SYMBOL_GPL(videobuf_queue_cancel);
+
+/* --------------------------------------------------------------------- */
+
+/* Locking: Caller holds q->vb_lock */
+enum v4l2_field videobuf_next_field(struct videobuf_queue *q)
+{
+ enum v4l2_field field = q->field;
+
+ BUG_ON(V4L2_FIELD_ANY == field);
+
+ if (V4L2_FIELD_ALTERNATE == field) {
+ if (V4L2_FIELD_TOP == q->last) {
+ field = V4L2_FIELD_BOTTOM;
+ q->last = V4L2_FIELD_BOTTOM;
+ } else {
+ field = V4L2_FIELD_TOP;
+ q->last = V4L2_FIELD_TOP;
+ }
+ }
+ return field;
+}
+EXPORT_SYMBOL_GPL(videobuf_next_field);
+
+/* Locking: Caller holds q->vb_lock */
+static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b,
+ struct videobuf_buffer *vb, enum v4l2_buf_type type)
+{
+ MAGIC_CHECK(vb->magic, MAGIC_BUFFER);
+ MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
+
+ b->index = vb->i;
+ b->type = type;
+
+ b->memory = vb->memory;
+ switch (b->memory) {
+ case V4L2_MEMORY_MMAP:
+ b->m.offset = vb->boff;
+ b->length = vb->bsize;
+ break;
+ case V4L2_MEMORY_USERPTR:
+ b->m.userptr = vb->baddr;
+ b->length = vb->bsize;
+ break;
+ case V4L2_MEMORY_OVERLAY:
+ b->m.offset = vb->boff;
+ break;
+ }
+
+ b->flags = 0;
+ if (vb->map)
+ b->flags |= V4L2_BUF_FLAG_MAPPED;
+
+ switch (vb->state) {
+ case VIDEOBUF_PREPARED:
+ case VIDEOBUF_QUEUED:
+ case VIDEOBUF_ACTIVE:
+ b->flags |= V4L2_BUF_FLAG_QUEUED;
+ break;
+ case VIDEOBUF_ERROR:
+ b->flags |= V4L2_BUF_FLAG_ERROR;
+ /* fall through */
+ case VIDEOBUF_DONE:
+ b->flags |= V4L2_BUF_FLAG_DONE;
+ break;
+ case VIDEOBUF_NEEDS_INIT:
+ case VIDEOBUF_IDLE:
+ /* nothing */
+ break;
+ }
+
+ if (vb->input != UNSET) {
+ b->flags |= V4L2_BUF_FLAG_INPUT;
+ b->input = vb->input;
+ }
+
+ b->field = vb->field;
+ b->timestamp = vb->ts;
+ b->bytesused = vb->size;
+ b->sequence = vb->field_count >> 1;
+}
+
+int videobuf_mmap_free(struct videobuf_queue *q)
+{
+ int ret;
+ videobuf_queue_lock(q);
+ ret = __videobuf_free(q);
+ videobuf_queue_unlock(q);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(videobuf_mmap_free);
+
+/* Locking: Caller holds q->vb_lock */
+int __videobuf_mmap_setup(struct videobuf_queue *q,
+ unsigned int bcount, unsigned int bsize,
+ enum v4l2_memory memory)
+{
+ unsigned int i;
+ int err;
+
+ MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
+
+ err = __videobuf_free(q);
+ if (0 != err)
+ return err;
+
+ /* Allocate and initialize buffers */
+ for (i = 0; i < bcount; i++) {
+ q->bufs[i] = videobuf_alloc_vb(q);
+
+ if (NULL == q->bufs[i])
+ break;
+
+ q->bufs[i]->i = i;
+ q->bufs[i]->input = UNSET;
+ q->bufs[i]->memory = memory;
+ q->bufs[i]->bsize = bsize;
+ switch (memory) {
+ case V4L2_MEMORY_MMAP:
+ q->bufs[i]->boff = PAGE_ALIGN(bsize) * i;
+ break;
+ case V4L2_MEMORY_USERPTR:
+ case V4L2_MEMORY_OVERLAY:
+ /* nothing */
+ break;
+ }
+ }
+
+ if (!i)
+ return -ENOMEM;
+
+ dprintk(1, "mmap setup: %d buffers, %d bytes each\n", i, bsize);
+
+ return i;
+}
+EXPORT_SYMBOL_GPL(__videobuf_mmap_setup);
+
+int videobuf_mmap_setup(struct videobuf_queue *q,
+ unsigned int bcount, unsigned int bsize,
+ enum v4l2_memory memory)
+{
+ int ret;
+ videobuf_queue_lock(q);
+ ret = __videobuf_mmap_setup(q, bcount, bsize, memory);
+ videobuf_queue_unlock(q);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(videobuf_mmap_setup);
+
+int videobuf_reqbufs(struct videobuf_queue *q,
+ struct v4l2_requestbuffers *req)
+{
+ unsigned int size, count;
+ int retval;
+
+ if (req->count < 1) {
+ dprintk(1, "reqbufs: count invalid (%d)\n", req->count);
+ return -EINVAL;
+ }
+
+ if (req->memory != V4L2_MEMORY_MMAP &&
+ req->memory != V4L2_MEMORY_USERPTR &&
+ req->memory != V4L2_MEMORY_OVERLAY) {
+ dprintk(1, "reqbufs: memory type invalid\n");
+ return -EINVAL;
+ }
+
+ videobuf_queue_lock(q);
+ if (req->type != q->type) {
+ dprintk(1, "reqbufs: queue type invalid\n");
+ retval = -EINVAL;
+ goto done;
+ }
+
+ if (q->streaming) {
+ dprintk(1, "reqbufs: streaming already exists\n");
+ retval = -EBUSY;
+ goto done;
+ }
+ if (!list_empty(&q->stream)) {
+ dprintk(1, "reqbufs: stream running\n");
+ retval = -EBUSY;
+ goto done;
+ }
+
+ count = req->count;
+ if (count > VIDEO_MAX_FRAME)
+ count = VIDEO_MAX_FRAME;
+ size = 0;
+ q->ops->buf_setup(q, &count, &size);
+ dprintk(1, "reqbufs: bufs=%d, size=0x%x [%u pages total]\n",
+ count, size,
+ (unsigned int)((count * PAGE_ALIGN(size)) >> PAGE_SHIFT));
+
+ retval = __videobuf_mmap_setup(q, count, size, req->memory);
+ if (retval < 0) {
+ dprintk(1, "reqbufs: mmap setup returned %d\n", retval);
+ goto done;
+ }
+
+ req->count = retval;
+ retval = 0;
+
+ done:
+ videobuf_queue_unlock(q);
+ return retval;
+}
+EXPORT_SYMBOL_GPL(videobuf_reqbufs);
+
+int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b)
+{
+ int ret = -EINVAL;
+
+ videobuf_queue_lock(q);
+ if (unlikely(b->type != q->type)) {
+ dprintk(1, "querybuf: Wrong type.\n");
+ goto done;
+ }
+ if (unlikely(b->index >= VIDEO_MAX_FRAME)) {
+ dprintk(1, "querybuf: index out of range.\n");
+ goto done;
+ }
+ if (unlikely(NULL == q->bufs[b->index])) {
+ dprintk(1, "querybuf: buffer is null.\n");
+ goto done;
+ }
+
+ videobuf_status(q, b, q->bufs[b->index], q->type);
+
+ ret = 0;
+done:
+ videobuf_queue_unlock(q);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(videobuf_querybuf);
+
+int videobuf_qbuf(struct videobuf_queue *q, struct v4l2_buffer *b)
+{
+ struct videobuf_buffer *buf;
+ enum v4l2_field field;
+ unsigned long flags = 0;
+ int retval;
+
+ MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
+
+ if (b->memory == V4L2_MEMORY_MMAP)
+ down_read(&current->mm->mmap_sem);
+
+ videobuf_queue_lock(q);
+ retval = -EBUSY;
+ if (q->reading) {
+ dprintk(1, "qbuf: Reading running...\n");
+ goto done;
+ }
+ retval = -EINVAL;
+ if (b->type != q->type) {
+ dprintk(1, "qbuf: Wrong type.\n");
+ goto done;
+ }
+ if (b->index >= VIDEO_MAX_FRAME) {
+ dprintk(1, "qbuf: index out of range.\n");
+ goto done;
+ }
+ buf = q->bufs[b->index];
+ if (NULL == buf) {
+ dprintk(1, "qbuf: buffer is null.\n");
+ goto done;
+ }
+ MAGIC_CHECK(buf->magic, MAGIC_BUFFER);
+ if (buf->memory != b->memory) {
+ dprintk(1, "qbuf: memory type is wrong.\n");
+ goto done;
+ }
+ if (buf->state != VIDEOBUF_NEEDS_INIT && buf->state != VIDEOBUF_IDLE) {
+ dprintk(1, "qbuf: buffer is already queued or active.\n");
+ goto done;
+ }
+
+ if (b->flags & V4L2_BUF_FLAG_INPUT) {
+ if (b->input >= q->inputs) {
+ dprintk(1, "qbuf: wrong input.\n");
+ goto done;
+ }
+ buf->input = b->input;
+ } else {
+ buf->input = UNSET;
+ }
+
+ switch (b->memory) {
+ case V4L2_MEMORY_MMAP:
+ if (0 == buf->baddr) {
+ dprintk(1, "qbuf: mmap requested "
+ "but buffer addr is zero!\n");
+ goto done;
+ }
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT
+ || q->type == V4L2_BUF_TYPE_VBI_OUTPUT
+ || q->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) {
+ buf->size = b->bytesused;
+ buf->field = b->field;
+ buf->ts = b->timestamp;
+ }
+ break;
+ case V4L2_MEMORY_USERPTR:
+ if (b->length < buf->bsize) {
+ dprintk(1, "qbuf: buffer length is not enough\n");
+ goto done;
+ }
+ if (VIDEOBUF_NEEDS_INIT != buf->state &&
+ buf->baddr != b->m.userptr)
+ q->ops->buf_release(q, buf);
+ buf->baddr = b->m.userptr;
+ break;
+ case V4L2_MEMORY_OVERLAY:
+ buf->boff = b->m.offset;
+ break;
+ default:
+ dprintk(1, "qbuf: wrong memory type\n");
+ goto done;
+ }
+
+ dprintk(1, "qbuf: requesting next field\n");
+ field = videobuf_next_field(q);
+ retval = q->ops->buf_prepare(q, buf, field);
+ if (0 != retval) {
+ dprintk(1, "qbuf: buffer_prepare returned %d\n", retval);
+ goto done;
+ }
+
+ list_add_tail(&buf->stream, &q->stream);
+ if (q->streaming) {
+ spin_lock_irqsave(q->irqlock, flags);
+ q->ops->buf_queue(q, buf);
+ spin_unlock_irqrestore(q->irqlock, flags);
+ }
+ dprintk(1, "qbuf: succeeded\n");
+ retval = 0;
+ wake_up_interruptible_sync(&q->wait);
+
+done:
+ videobuf_queue_unlock(q);
+
+ if (b->memory == V4L2_MEMORY_MMAP)
+ up_read(&current->mm->mmap_sem);
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(videobuf_qbuf);
+
+/* Locking: Caller holds q->vb_lock */
+static int stream_next_buffer_check_queue(struct videobuf_queue *q, int noblock)
+{
+ int retval;
+
+checks:
+ if (!q->streaming) {
+ dprintk(1, "next_buffer: Not streaming\n");
+ retval = -EINVAL;
+ goto done;
+ }
+
+ if (list_empty(&q->stream)) {
+ if (noblock) {
+ retval = -EAGAIN;
+ dprintk(2, "next_buffer: no buffers to dequeue\n");
+ goto done;
+ } else {
+ dprintk(2, "next_buffer: waiting on buffer\n");
+
+ /* Drop lock to avoid deadlock with qbuf */
+ videobuf_queue_unlock(q);
+
+ /* Checking list_empty and streaming is safe without
+ * locks because we goto checks to validate while
+ * holding locks before proceeding */
+ retval = wait_event_interruptible(q->wait,
+ !list_empty(&q->stream) || !q->streaming);
+ videobuf_queue_lock(q);
+
+ if (retval)
+ goto done;
+
+ goto checks;
+ }
+ }
+
+ retval = 0;
+
+done:
+ return retval;
+}
+
+/* Locking: Caller holds q->vb_lock */
+static int stream_next_buffer(struct videobuf_queue *q,
+ struct videobuf_buffer **vb, int nonblocking)
+{
+ int retval;
+ struct videobuf_buffer *buf = NULL;
+
+ retval = stream_next_buffer_check_queue(q, nonblocking);
+ if (retval)
+ goto done;
+
+ buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
+ retval = videobuf_waiton(q, buf, nonblocking, 1);
+ if (retval < 0)
+ goto done;
+
+ *vb = buf;
+done:
+ return retval;
+}
+
+int videobuf_dqbuf(struct videobuf_queue *q,
+ struct v4l2_buffer *b, int nonblocking)
+{
+ struct videobuf_buffer *buf = NULL;
+ int retval;
+
+ MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
+
+ memset(b, 0, sizeof(*b));
+ videobuf_queue_lock(q);
+
+ retval = stream_next_buffer(q, &buf, nonblocking);
+ if (retval < 0) {
+ dprintk(1, "dqbuf: next_buffer error: %i\n", retval);
+ goto done;
+ }
+
+ switch (buf->state) {
+ case VIDEOBUF_ERROR:
+ dprintk(1, "dqbuf: state is error\n");
+ break;
+ case VIDEOBUF_DONE:
+ dprintk(1, "dqbuf: state is done\n");
+ break;
+ default:
+ dprintk(1, "dqbuf: state invalid\n");
+ retval = -EINVAL;
+ goto done;
+ }
+ CALL(q, sync, q, buf);
+ videobuf_status(q, b, buf, q->type);
+ list_del(&buf->stream);
+ buf->state = VIDEOBUF_IDLE;
+ b->flags &= ~V4L2_BUF_FLAG_DONE;
+done:
+ videobuf_queue_unlock(q);
+ return retval;
+}
+EXPORT_SYMBOL_GPL(videobuf_dqbuf);
+
+int videobuf_streamon(struct videobuf_queue *q)
+{
+ struct videobuf_buffer *buf;
+ unsigned long flags = 0;
+ int retval;
+
+ videobuf_queue_lock(q);
+ retval = -EBUSY;
+ if (q->reading)
+ goto done;
+ retval = 0;
+ if (q->streaming)
+ goto done;
+ q->streaming = 1;
+ spin_lock_irqsave(q->irqlock, flags);
+ list_for_each_entry(buf, &q->stream, stream)
+ if (buf->state == VIDEOBUF_PREPARED)
+ q->ops->buf_queue(q, buf);
+ spin_unlock_irqrestore(q->irqlock, flags);
+
+ wake_up_interruptible_sync(&q->wait);
+done:
+ videobuf_queue_unlock(q);
+ return retval;
+}
+EXPORT_SYMBOL_GPL(videobuf_streamon);
+
+/* Locking: Caller holds q->vb_lock */
+static int __videobuf_streamoff(struct videobuf_queue *q)
+{
+ if (!q->streaming)
+ return -EINVAL;
+
+ videobuf_queue_cancel(q);
+
+ return 0;
+}
+
+int videobuf_streamoff(struct videobuf_queue *q)
+{
+ int retval;
+
+ videobuf_queue_lock(q);
+ retval = __videobuf_streamoff(q);
+ videobuf_queue_unlock(q);
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(videobuf_streamoff);
+
+/* Locking: Caller holds q->vb_lock */
+static ssize_t videobuf_read_zerocopy(struct videobuf_queue *q,
+ char __user *data,
+ size_t count, loff_t *ppos)
+{
+ enum v4l2_field field;
+ unsigned long flags = 0;
+ int retval;
+
+ MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
+
+ /* setup stuff */
+ q->read_buf = videobuf_alloc_vb(q);
+ if (NULL == q->read_buf)
+ return -ENOMEM;
+
+ q->read_buf->memory = V4L2_MEMORY_USERPTR;
+ q->read_buf->baddr = (unsigned long)data;
+ q->read_buf->bsize = count;
+
+ field = videobuf_next_field(q);
+ retval = q->ops->buf_prepare(q, q->read_buf, field);
+ if (0 != retval)
+ goto done;
+
+ /* start capture & wait */
+ spin_lock_irqsave(q->irqlock, flags);
+ q->ops->buf_queue(q, q->read_buf);
+ spin_unlock_irqrestore(q->irqlock, flags);
+ retval = videobuf_waiton(q, q->read_buf, 0, 0);
+ if (0 == retval) {
+ CALL(q, sync, q, q->read_buf);
+ if (VIDEOBUF_ERROR == q->read_buf->state)
+ retval = -EIO;
+ else
+ retval = q->read_buf->size;
+ }
+
+done:
+ /* cleanup */
+ q->ops->buf_release(q, q->read_buf);
+ kfree(q->read_buf);
+ q->read_buf = NULL;
+ return retval;
+}
+
+static int __videobuf_copy_to_user(struct videobuf_queue *q,
+ struct videobuf_buffer *buf,
+ char __user *data, size_t count,
+ int nonblocking)
+{
+ void *vaddr = CALL(q, vaddr, buf);
+
+ /* copy to userspace */
+ if (count > buf->size - q->read_off)
+ count = buf->size - q->read_off;
+
+ if (copy_to_user(data, vaddr + q->read_off, count))
+ return -EFAULT;
+
+ return count;
+}
+
+static int __videobuf_copy_stream(struct videobuf_queue *q,
+ struct videobuf_buffer *buf,
+ char __user *data, size_t count, size_t pos,
+ int vbihack, int nonblocking)
+{
+ unsigned int *fc = CALL(q, vaddr, buf);
+
+ if (vbihack) {
+ /* dirty, undocumented hack -- pass the frame counter
+ * within the last four bytes of each vbi data block.
+ * We need that one to maintain backward compatibility
+ * to all vbi decoding software out there ... */
+ fc += (buf->size >> 2) - 1;
+ *fc = buf->field_count >> 1;
+ dprintk(1, "vbihack: %d\n", *fc);
+ }
+
+ /* copy stuff using the common method */
+ count = __videobuf_copy_to_user(q, buf, data, count, nonblocking);
+
+ if ((count == -EFAULT) && (pos == 0))
+ return -EFAULT;
+
+ return count;
+}
+
+ssize_t videobuf_read_one(struct videobuf_queue *q,
+ char __user *data, size_t count, loff_t *ppos,
+ int nonblocking)
+{
+ enum v4l2_field field;
+ unsigned long flags = 0;
+ unsigned size = 0, nbufs = 1;
+ int retval;
+
+ MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
+
+ videobuf_queue_lock(q);
+
+ q->ops->buf_setup(q, &nbufs, &size);
+
+ if (NULL == q->read_buf &&
+ count >= size &&
+ !nonblocking) {
+ retval = videobuf_read_zerocopy(q, data, count, ppos);
+ if (retval >= 0 || retval == -EIO)
+ /* ok, all done */
+ goto done;
+ /* fallback to kernel bounce buffer on failures */
+ }
+
+ if (NULL == q->read_buf) {
+ /* need to capture a new frame */
+ retval = -ENOMEM;
+ q->read_buf = videobuf_alloc_vb(q);
+
+ dprintk(1, "video alloc=0x%p\n", q->read_buf);
+ if (NULL == q->read_buf)
+ goto done;
+ q->read_buf->memory = V4L2_MEMORY_USERPTR;
+ q->read_buf->bsize = count; /* preferred size */
+ field = videobuf_next_field(q);
+ retval = q->ops->buf_prepare(q, q->read_buf, field);
+
+ if (0 != retval) {
+ kfree(q->read_buf);
+ q->read_buf = NULL;
+ goto done;
+ }
+
+ spin_lock_irqsave(q->irqlock, flags);
+ q->ops->buf_queue(q, q->read_buf);
+ spin_unlock_irqrestore(q->irqlock, flags);
+
+ q->read_off = 0;
+ }
+
+ /* wait until capture is done */
+ retval = videobuf_waiton(q, q->read_buf, nonblocking, 1);
+ if (0 != retval)
+ goto done;
+
+ CALL(q, sync, q, q->read_buf);
+
+ if (VIDEOBUF_ERROR == q->read_buf->state) {
+ /* catch I/O errors */
+ q->ops->buf_release(q, q->read_buf);
+ kfree(q->read_buf);
+ q->read_buf = NULL;
+ retval = -EIO;
+ goto done;
+ }
+
+ /* Copy to userspace */
+ retval = __videobuf_copy_to_user(q, q->read_buf, data, count, nonblocking);
+ if (retval < 0)
+ goto done;
+
+ q->read_off += retval;
+ if (q->read_off == q->read_buf->size) {
+ /* all data copied, cleanup */
+ q->ops->buf_release(q, q->read_buf);
+ kfree(q->read_buf);
+ q->read_buf = NULL;
+ }
+
+done:
+ videobuf_queue_unlock(q);
+ return retval;
+}
+EXPORT_SYMBOL_GPL(videobuf_read_one);
+
+/* Locking: Caller holds q->vb_lock */
+static int __videobuf_read_start(struct videobuf_queue *q)
+{
+ enum v4l2_field field;
+ unsigned long flags = 0;
+ unsigned int count = 0, size = 0;
+ int err, i;
+
+ q->ops->buf_setup(q, &count, &size);
+ if (count < 2)
+ count = 2;
+ if (count > VIDEO_MAX_FRAME)
+ count = VIDEO_MAX_FRAME;
+ size = PAGE_ALIGN(size);
+
+ err = __videobuf_mmap_setup(q, count, size, V4L2_MEMORY_USERPTR);
+ if (err < 0)
+ return err;
+
+ count = err;
+
+ for (i = 0; i < count; i++) {
+ field = videobuf_next_field(q);
+ err = q->ops->buf_prepare(q, q->bufs[i], field);
+ if (err)
+ return err;
+ list_add_tail(&q->bufs[i]->stream, &q->stream);
+ }
+ spin_lock_irqsave(q->irqlock, flags);
+ for (i = 0; i < count; i++)
+ q->ops->buf_queue(q, q->bufs[i]);
+ spin_unlock_irqrestore(q->irqlock, flags);
+ q->reading = 1;
+ return 0;
+}
+
+static void __videobuf_read_stop(struct videobuf_queue *q)
+{
+ int i;
+
+ videobuf_queue_cancel(q);
+ __videobuf_free(q);
+ INIT_LIST_HEAD(&q->stream);
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ if (NULL == q->bufs[i])
+ continue;
+ kfree(q->bufs[i]);
+ q->bufs[i] = NULL;
+ }
+ q->read_buf = NULL;
+}
+
+int videobuf_read_start(struct videobuf_queue *q)
+{
+ int rc;
+
+ videobuf_queue_lock(q);
+ rc = __videobuf_read_start(q);
+ videobuf_queue_unlock(q);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(videobuf_read_start);
+
+void videobuf_read_stop(struct videobuf_queue *q)
+{
+ videobuf_queue_lock(q);
+ __videobuf_read_stop(q);
+ videobuf_queue_unlock(q);
+}
+EXPORT_SYMBOL_GPL(videobuf_read_stop);
+
+void videobuf_stop(struct videobuf_queue *q)
+{
+ videobuf_queue_lock(q);
+
+ if (q->streaming)
+ __videobuf_streamoff(q);
+
+ if (q->reading)
+ __videobuf_read_stop(q);
+
+ videobuf_queue_unlock(q);
+}
+EXPORT_SYMBOL_GPL(videobuf_stop);
+
+ssize_t videobuf_read_stream(struct videobuf_queue *q,
+ char __user *data, size_t count, loff_t *ppos,
+ int vbihack, int nonblocking)
+{
+ int rc, retval;
+ unsigned long flags = 0;
+
+ MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
+
+ dprintk(2, "%s\n", __func__);
+ videobuf_queue_lock(q);
+ retval = -EBUSY;
+ if (q->streaming)
+ goto done;
+ if (!q->reading) {
+ retval = __videobuf_read_start(q);
+ if (retval < 0)
+ goto done;
+ }
+
+ retval = 0;
+ while (count > 0) {
+ /* get / wait for data */
+ if (NULL == q->read_buf) {
+ q->read_buf = list_entry(q->stream.next,
+ struct videobuf_buffer,
+ stream);
+ list_del(&q->read_buf->stream);
+ q->read_off = 0;
+ }
+ rc = videobuf_waiton(q, q->read_buf, nonblocking, 1);
+ if (rc < 0) {
+ if (0 == retval)
+ retval = rc;
+ break;
+ }
+
+ if (q->read_buf->state == VIDEOBUF_DONE) {
+ rc = __videobuf_copy_stream(q, q->read_buf, data + retval, count,
+ retval, vbihack, nonblocking);
+ if (rc < 0) {
+ retval = rc;
+ break;
+ }
+ retval += rc;
+ count -= rc;
+ q->read_off += rc;
+ } else {
+ /* some error */
+ q->read_off = q->read_buf->size;
+ if (0 == retval)
+ retval = -EIO;
+ }
+
+ /* requeue buffer when done with copying */
+ if (q->read_off == q->read_buf->size) {
+ list_add_tail(&q->read_buf->stream,
+ &q->stream);
+ spin_lock_irqsave(q->irqlock, flags);
+ q->ops->buf_queue(q, q->read_buf);
+ spin_unlock_irqrestore(q->irqlock, flags);
+ q->read_buf = NULL;
+ }
+ if (retval < 0)
+ break;
+ }
+
+done:
+ videobuf_queue_unlock(q);
+ return retval;
+}
+EXPORT_SYMBOL_GPL(videobuf_read_stream);
+
+unsigned int videobuf_poll_stream(struct file *file,
+ struct videobuf_queue *q,
+ poll_table *wait)
+{
+ struct videobuf_buffer *buf = NULL;
+ unsigned int rc = 0;
+
+ videobuf_queue_lock(q);
+ if (q->streaming) {
+ if (!list_empty(&q->stream))
+ buf = list_entry(q->stream.next,
+ struct videobuf_buffer, stream);
+ } else {
+ if (!q->reading)
+ __videobuf_read_start(q);
+ if (!q->reading) {
+ rc = POLLERR;
+ } else if (NULL == q->read_buf) {
+ q->read_buf = list_entry(q->stream.next,
+ struct videobuf_buffer,
+ stream);
+ list_del(&q->read_buf->stream);
+ q->read_off = 0;
+ }
+ buf = q->read_buf;
+ }
+ if (!buf)
+ rc = POLLERR;
+
+ if (0 == rc) {
+ poll_wait(file, &buf->done, wait);
+ if (buf->state == VIDEOBUF_DONE ||
+ buf->state == VIDEOBUF_ERROR) {
+ switch (q->type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ rc = POLLOUT | POLLWRNORM;
+ break;
+ default:
+ rc = POLLIN | POLLRDNORM;
+ break;
+ }
+ }
+ }
+ videobuf_queue_unlock(q);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(videobuf_poll_stream);
+
+int videobuf_mmap_mapper(struct videobuf_queue *q, struct vm_area_struct *vma)
+{
+ int rc = -EINVAL;
+ int i;
+
+ MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS);
+
+ if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) {
+ dprintk(1, "mmap appl bug: PROT_WRITE and MAP_SHARED are required\n");
+ return -EINVAL;
+ }
+
+ videobuf_queue_lock(q);
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ struct videobuf_buffer *buf = q->bufs[i];
+
+ if (buf && buf->memory == V4L2_MEMORY_MMAP &&
+ buf->boff == (vma->vm_pgoff << PAGE_SHIFT)) {
+ rc = CALL(q, mmap_mapper, q, buf, vma);
+ break;
+ }
+ }
+ videobuf_queue_unlock(q);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(videobuf_mmap_mapper);
diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c
new file mode 100644
index 00000000..c9691115
--- /dev/null
+++ b/drivers/media/video/videobuf-dma-contig.c
@@ -0,0 +1,400 @@
+/*
+ * helper functions for physically contiguous capture buffers
+ *
+ * The functions support hardware lacking scatter gather support
+ * (i.e. the buffers must be linear in physical memory)
+ *
+ * Copyright (c) 2008 Magnus Damm
+ *
+ * Based on videobuf-vmalloc.c,
+ * (c) 2007 Mauro Carvalho Chehab, <mchehab@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/dma-mapping.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <media/videobuf-dma-contig.h>
+
+struct videobuf_dma_contig_memory {
+ u32 magic;
+ void *vaddr;
+ dma_addr_t dma_handle;
+ unsigned long size;
+};
+
+#define MAGIC_DC_MEM 0x0733ac61
+#define MAGIC_CHECK(is, should) \
+ if (unlikely((is) != (should))) { \
+ pr_err("magic mismatch: %x expected %x\n", (is), (should)); \
+ BUG(); \
+ }
+
+static void
+videobuf_vm_open(struct vm_area_struct *vma)
+{
+ struct videobuf_mapping *map = vma->vm_private_data;
+
+ dev_dbg(map->q->dev, "vm_open %p [count=%u,vma=%08lx-%08lx]\n",
+ map, map->count, vma->vm_start, vma->vm_end);
+
+ map->count++;
+}
+
+static void videobuf_vm_close(struct vm_area_struct *vma)
+{
+ struct videobuf_mapping *map = vma->vm_private_data;
+ struct videobuf_queue *q = map->q;
+ int i;
+
+ dev_dbg(q->dev, "vm_close %p [count=%u,vma=%08lx-%08lx]\n",
+ map, map->count, vma->vm_start, vma->vm_end);
+
+ map->count--;
+ if (0 == map->count) {
+ struct videobuf_dma_contig_memory *mem;
+
+ dev_dbg(q->dev, "munmap %p q=%p\n", map, q);
+ videobuf_queue_lock(q);
+
+ /* We need first to cancel streams, before unmapping */
+ if (q->streaming)
+ videobuf_queue_cancel(q);
+
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ if (NULL == q->bufs[i])
+ continue;
+
+ if (q->bufs[i]->map != map)
+ continue;
+
+ mem = q->bufs[i]->priv;
+ if (mem) {
+ /* This callback is called only if kernel has
+ allocated memory and this memory is mmapped.
+ In this case, memory should be freed,
+ in order to do memory unmap.
+ */
+
+ MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+ /* vfree is not atomic - can't be
+ called with IRQ's disabled
+ */
+ dev_dbg(q->dev, "buf[%d] freeing %p\n",
+ i, mem->vaddr);
+
+ dma_free_coherent(q->dev, mem->size,
+ mem->vaddr, mem->dma_handle);
+ mem->vaddr = NULL;
+ }
+
+ q->bufs[i]->map = NULL;
+ q->bufs[i]->baddr = 0;
+ }
+
+ kfree(map);
+
+ videobuf_queue_unlock(q);
+ }
+}
+
+static const struct vm_operations_struct videobuf_vm_ops = {
+ .open = videobuf_vm_open,
+ .close = videobuf_vm_close,
+};
+
+/**
+ * videobuf_dma_contig_user_put() - reset pointer to user space buffer
+ * @mem: per-buffer private videobuf-dma-contig data
+ *
+ * This function resets the user space pointer
+ */
+static void videobuf_dma_contig_user_put(struct videobuf_dma_contig_memory *mem)
+{
+ mem->dma_handle = 0;
+ mem->size = 0;
+}
+
+/**
+ * videobuf_dma_contig_user_get() - setup user space memory pointer
+ * @mem: per-buffer private videobuf-dma-contig data
+ * @vb: video buffer to map
+ *
+ * This function validates and sets up a pointer to user space memory.
+ * Only physically contiguous pfn-mapped memory is accepted.
+ *
+ * Returns 0 if successful.
+ */
+static int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem,
+ struct videobuf_buffer *vb)
+{
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *vma;
+ unsigned long prev_pfn, this_pfn;
+ unsigned long pages_done, user_address;
+ unsigned int offset;
+ int ret;
+
+ offset = vb->baddr & ~PAGE_MASK;
+ mem->size = PAGE_ALIGN(vb->size + offset);
+ ret = -EINVAL;
+
+ down_read(&mm->mmap_sem);
+
+ vma = find_vma(mm, vb->baddr);
+ if (!vma)
+ goto out_up;
+
+ if ((vb->baddr + mem->size) > vma->vm_end)
+ goto out_up;
+
+ pages_done = 0;
+ prev_pfn = 0; /* kill warning */
+ user_address = vb->baddr;
+
+ while (pages_done < (mem->size >> PAGE_SHIFT)) {
+ ret = follow_pfn(vma, user_address, &this_pfn);
+ if (ret)
+ break;
+
+ if (pages_done == 0)
+ mem->dma_handle = (this_pfn << PAGE_SHIFT) + offset;
+ else if (this_pfn != (prev_pfn + 1))
+ ret = -EFAULT;
+
+ if (ret)
+ break;
+
+ prev_pfn = this_pfn;
+ user_address += PAGE_SIZE;
+ pages_done++;
+ }
+
+ out_up:
+ up_read(&current->mm->mmap_sem);
+
+ return ret;
+}
+
+static struct videobuf_buffer *__videobuf_alloc_vb(size_t size)
+{
+ struct videobuf_dma_contig_memory *mem;
+ struct videobuf_buffer *vb;
+
+ vb = kzalloc(size + sizeof(*mem), GFP_KERNEL);
+ if (vb) {
+ mem = vb->priv = ((char *)vb) + size;
+ mem->magic = MAGIC_DC_MEM;
+ }
+
+ return vb;
+}
+
+static void *__videobuf_to_vaddr(struct videobuf_buffer *buf)
+{
+ struct videobuf_dma_contig_memory *mem = buf->priv;
+
+ BUG_ON(!mem);
+ MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+ return mem->vaddr;
+}
+
+static int __videobuf_iolock(struct videobuf_queue *q,
+ struct videobuf_buffer *vb,
+ struct v4l2_framebuffer *fbuf)
+{
+ struct videobuf_dma_contig_memory *mem = vb->priv;
+
+ BUG_ON(!mem);
+ MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+ switch (vb->memory) {
+ case V4L2_MEMORY_MMAP:
+ dev_dbg(q->dev, "%s memory method MMAP\n", __func__);
+
+ /* All handling should be done by __videobuf_mmap_mapper() */
+ if (!mem->vaddr) {
+ dev_err(q->dev, "memory is not alloced/mmapped.\n");
+ return -EINVAL;
+ }
+ break;
+ case V4L2_MEMORY_USERPTR:
+ dev_dbg(q->dev, "%s memory method USERPTR\n", __func__);
+
+ /* handle pointer from user space */
+ if (vb->baddr)
+ return videobuf_dma_contig_user_get(mem, vb);
+
+ /* allocate memory for the read() method */
+ mem->size = PAGE_ALIGN(vb->size);
+ mem->vaddr = dma_alloc_coherent(q->dev, mem->size,
+ &mem->dma_handle, GFP_KERNEL);
+ if (!mem->vaddr) {
+ dev_err(q->dev, "dma_alloc_coherent %ld failed\n",
+ mem->size);
+ return -ENOMEM;
+ }
+
+ dev_dbg(q->dev, "dma_alloc_coherent data is at %p (%ld)\n",
+ mem->vaddr, mem->size);
+ break;
+ case V4L2_MEMORY_OVERLAY:
+ default:
+ dev_dbg(q->dev, "%s memory method OVERLAY/unknown\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int __videobuf_mmap_mapper(struct videobuf_queue *q,
+ struct videobuf_buffer *buf,
+ struct vm_area_struct *vma)
+{
+ struct videobuf_dma_contig_memory *mem;
+ struct videobuf_mapping *map;
+ int retval;
+ unsigned long size;
+
+ dev_dbg(q->dev, "%s\n", __func__);
+
+ /* create mapping + update buffer list */
+ map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
+ if (!map)
+ return -ENOMEM;
+
+ buf->map = map;
+ map->q = q;
+
+ buf->baddr = vma->vm_start;
+
+ mem = buf->priv;
+ BUG_ON(!mem);
+ MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+ mem->size = PAGE_ALIGN(buf->bsize);
+ mem->vaddr = dma_alloc_coherent(q->dev, mem->size,
+ &mem->dma_handle, GFP_KERNEL);
+ if (!mem->vaddr) {
+ dev_err(q->dev, "dma_alloc_coherent size %ld failed\n",
+ mem->size);
+ goto error;
+ }
+ dev_dbg(q->dev, "dma_alloc_coherent data is at addr %p (size %ld)\n",
+ mem->vaddr, mem->size);
+
+ /* Try to remap memory */
+
+ size = vma->vm_end - vma->vm_start;
+ size = (size < mem->size) ? size : mem->size;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ retval = remap_pfn_range(vma, vma->vm_start,
+ mem->dma_handle >> PAGE_SHIFT,
+ size, vma->vm_page_prot);
+ if (retval) {
+ dev_err(q->dev, "mmap: remap failed with error %d. ", retval);
+ dma_free_coherent(q->dev, mem->size,
+ mem->vaddr, mem->dma_handle);
+ goto error;
+ }
+
+ vma->vm_ops = &videobuf_vm_ops;
+ vma->vm_flags |= VM_DONTEXPAND;
+ vma->vm_private_data = map;
+
+ dev_dbg(q->dev, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n",
+ map, q, vma->vm_start, vma->vm_end,
+ (long int)buf->bsize,
+ vma->vm_pgoff, buf->i);
+
+ videobuf_vm_open(vma);
+
+ return 0;
+
+error:
+ kfree(map);
+ return -ENOMEM;
+}
+
+static struct videobuf_qtype_ops qops = {
+ .magic = MAGIC_QTYPE_OPS,
+
+ .alloc_vb = __videobuf_alloc_vb,
+ .iolock = __videobuf_iolock,
+ .mmap_mapper = __videobuf_mmap_mapper,
+ .vaddr = __videobuf_to_vaddr,
+};
+
+void videobuf_queue_dma_contig_init(struct videobuf_queue *q,
+ const struct videobuf_queue_ops *ops,
+ struct device *dev,
+ spinlock_t *irqlock,
+ enum v4l2_buf_type type,
+ enum v4l2_field field,
+ unsigned int msize,
+ void *priv,
+ struct mutex *ext_lock)
+{
+ videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
+ priv, &qops, ext_lock);
+}
+EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init);
+
+dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf)
+{
+ struct videobuf_dma_contig_memory *mem = buf->priv;
+
+ BUG_ON(!mem);
+ MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+ return mem->dma_handle;
+}
+EXPORT_SYMBOL_GPL(videobuf_to_dma_contig);
+
+void videobuf_dma_contig_free(struct videobuf_queue *q,
+ struct videobuf_buffer *buf)
+{
+ struct videobuf_dma_contig_memory *mem = buf->priv;
+
+ /* mmapped memory can't be freed here, otherwise mmapped region
+ would be released, while still needed. In this case, the memory
+ release should happen inside videobuf_vm_close().
+ So, it should free memory only if the memory were allocated for
+ read() operation.
+ */
+ if (buf->memory != V4L2_MEMORY_USERPTR)
+ return;
+
+ if (!mem)
+ return;
+
+ MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+ /* handle user space pointer case */
+ if (buf->baddr) {
+ videobuf_dma_contig_user_put(mem);
+ return;
+ }
+
+ /* read() method */
+ if (mem->vaddr) {
+ dma_free_coherent(q->dev, mem->size, mem->vaddr, mem->dma_handle);
+ mem->vaddr = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(videobuf_dma_contig_free);
+
+MODULE_DESCRIPTION("helper module to manage video4linux dma contig buffers");
+MODULE_AUTHOR("Magnus Damm");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/videobuf-dma-sg.c b/drivers/media/video/videobuf-dma-sg.c
new file mode 100644
index 00000000..f300deaf
--- /dev/null
+++ b/drivers/media/video/videobuf-dma-sg.c
@@ -0,0 +1,633 @@
+/*
+ * helper functions for SG DMA video4linux capture buffers
+ *
+ * The functions expect the hardware being able to scatter gather
+ * (i.e. the buffers are not linear in physical memory, but fragmented
+ * into PAGE_SIZE chunks). They also assume the driver does not need
+ * to touch the video data.
+ *
+ * (c) 2007 Mauro Carvalho Chehab, <mchehab@infradead.org>
+ *
+ * Highly based on video-buf written originally by:
+ * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org>
+ * (c) 2006 Mauro Carvalho Chehab, <mchehab@infradead.org>
+ * (c) 2006 Ted Walther and John Sokol
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+#include <linux/pagemap.h>
+#include <linux/scatterlist.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+
+#include <media/videobuf-dma-sg.h>
+
+#define MAGIC_DMABUF 0x19721112
+#define MAGIC_SG_MEM 0x17890714
+
+#define MAGIC_CHECK(is, should) \
+ if (unlikely((is) != (should))) { \
+ printk(KERN_ERR "magic mismatch: %x (expected %x)\n", \
+ is, should); \
+ BUG(); \
+ }
+
+static int debug;
+module_param(debug, int, 0644);
+
+MODULE_DESCRIPTION("helper module to manage video4linux dma sg buffers");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
+MODULE_LICENSE("GPL");
+
+#define dprintk(level, fmt, arg...) \
+ if (debug >= level) \
+ printk(KERN_DEBUG "vbuf-sg: " fmt , ## arg)
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Return a scatterlist for some page-aligned vmalloc()'ed memory
+ * block (NULL on errors). Memory for the scatterlist is allocated
+ * using kmalloc. The caller must free the memory.
+ */
+static struct scatterlist *videobuf_vmalloc_to_sg(unsigned char *virt,
+ int nr_pages)
+{
+ struct scatterlist *sglist;
+ struct page *pg;
+ int i;
+
+ sglist = vzalloc(nr_pages * sizeof(*sglist));
+ if (NULL == sglist)
+ return NULL;
+ sg_init_table(sglist, nr_pages);
+ for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) {
+ pg = vmalloc_to_page(virt);
+ if (NULL == pg)
+ goto err;
+ BUG_ON(PageHighMem(pg));
+ sg_set_page(&sglist[i], pg, PAGE_SIZE, 0);
+ }
+ return sglist;
+
+err:
+ vfree(sglist);
+ return NULL;
+}
+
+/*
+ * Return a scatterlist for a an array of userpages (NULL on errors).
+ * Memory for the scatterlist is allocated using kmalloc. The caller
+ * must free the memory.
+ */
+static struct scatterlist *videobuf_pages_to_sg(struct page **pages,
+ int nr_pages, int offset, size_t size)
+{
+ struct scatterlist *sglist;
+ int i;
+
+ if (NULL == pages[0])
+ return NULL;
+ sglist = vmalloc(nr_pages * sizeof(*sglist));
+ if (NULL == sglist)
+ return NULL;
+ sg_init_table(sglist, nr_pages);
+
+ if (PageHighMem(pages[0]))
+ /* DMA to highmem pages might not work */
+ goto highmem;
+ sg_set_page(&sglist[0], pages[0],
+ min_t(size_t, PAGE_SIZE - offset, size), offset);
+ size -= min_t(size_t, PAGE_SIZE - offset, size);
+ for (i = 1; i < nr_pages; i++) {
+ if (NULL == pages[i])
+ goto nopage;
+ if (PageHighMem(pages[i]))
+ goto highmem;
+ sg_set_page(&sglist[i], pages[i], min_t(size_t, PAGE_SIZE, size), 0);
+ size -= min_t(size_t, PAGE_SIZE, size);
+ }
+ return sglist;
+
+nopage:
+ dprintk(2, "sgl: oops - no page\n");
+ vfree(sglist);
+ return NULL;
+
+highmem:
+ dprintk(2, "sgl: oops - highmem page\n");
+ vfree(sglist);
+ return NULL;
+}
+
+/* --------------------------------------------------------------------- */
+
+struct videobuf_dmabuf *videobuf_to_dma(struct videobuf_buffer *buf)
+{
+ struct videobuf_dma_sg_memory *mem = buf->priv;
+ BUG_ON(!mem);
+
+ MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
+
+ return &mem->dma;
+}
+EXPORT_SYMBOL_GPL(videobuf_to_dma);
+
+void videobuf_dma_init(struct videobuf_dmabuf *dma)
+{
+ memset(dma, 0, sizeof(*dma));
+ dma->magic = MAGIC_DMABUF;
+}
+EXPORT_SYMBOL_GPL(videobuf_dma_init);
+
+static int videobuf_dma_init_user_locked(struct videobuf_dmabuf *dma,
+ int direction, unsigned long data, unsigned long size)
+{
+ unsigned long first, last;
+ int err, rw = 0;
+
+ dma->direction = direction;
+ switch (dma->direction) {
+ case DMA_FROM_DEVICE:
+ rw = READ;
+ break;
+ case DMA_TO_DEVICE:
+ rw = WRITE;
+ break;
+ default:
+ BUG();
+ }
+
+ first = (data & PAGE_MASK) >> PAGE_SHIFT;
+ last = ((data+size-1) & PAGE_MASK) >> PAGE_SHIFT;
+ dma->offset = data & ~PAGE_MASK;
+ dma->size = size;
+ dma->nr_pages = last-first+1;
+ dma->pages = kmalloc(dma->nr_pages * sizeof(struct page *), GFP_KERNEL);
+ if (NULL == dma->pages)
+ return -ENOMEM;
+
+ dprintk(1, "init user [0x%lx+0x%lx => %d pages]\n",
+ data, size, dma->nr_pages);
+
+ err = get_user_pages(current, current->mm,
+ data & PAGE_MASK, dma->nr_pages,
+ rw == READ, 1, /* force */
+ dma->pages, NULL);
+
+ if (err != dma->nr_pages) {
+ dma->nr_pages = (err >= 0) ? err : 0;
+ dprintk(1, "get_user_pages: err=%d [%d]\n", err, dma->nr_pages);
+ return err < 0 ? err : -EINVAL;
+ }
+ return 0;
+}
+
+int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction,
+ unsigned long data, unsigned long size)
+{
+ int ret;
+
+ down_read(&current->mm->mmap_sem);
+ ret = videobuf_dma_init_user_locked(dma, direction, data, size);
+ up_read(&current->mm->mmap_sem);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(videobuf_dma_init_user);
+
+int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction,
+ int nr_pages)
+{
+ dprintk(1, "init kernel [%d pages]\n", nr_pages);
+
+ dma->direction = direction;
+ dma->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT);
+ if (NULL == dma->vaddr) {
+ dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages);
+ return -ENOMEM;
+ }
+
+ dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n",
+ (unsigned long)dma->vaddr,
+ nr_pages << PAGE_SHIFT);
+
+ memset(dma->vaddr, 0, nr_pages << PAGE_SHIFT);
+ dma->nr_pages = nr_pages;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel);
+
+int videobuf_dma_init_overlay(struct videobuf_dmabuf *dma, int direction,
+ dma_addr_t addr, int nr_pages)
+{
+ dprintk(1, "init overlay [%d pages @ bus 0x%lx]\n",
+ nr_pages, (unsigned long)addr);
+ dma->direction = direction;
+
+ if (0 == addr)
+ return -EINVAL;
+
+ dma->bus_addr = addr;
+ dma->nr_pages = nr_pages;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(videobuf_dma_init_overlay);
+
+int videobuf_dma_map(struct device *dev, struct videobuf_dmabuf *dma)
+{
+ MAGIC_CHECK(dma->magic, MAGIC_DMABUF);
+ BUG_ON(0 == dma->nr_pages);
+
+ if (dma->pages) {
+ dma->sglist = videobuf_pages_to_sg(dma->pages, dma->nr_pages,
+ dma->offset, dma->size);
+ }
+ if (dma->vaddr) {
+ dma->sglist = videobuf_vmalloc_to_sg(dma->vaddr,
+ dma->nr_pages);
+ }
+ if (dma->bus_addr) {
+ dma->sglist = vmalloc(sizeof(*dma->sglist));
+ if (NULL != dma->sglist) {
+ dma->sglen = 1;
+ sg_dma_address(&dma->sglist[0]) = dma->bus_addr
+ & PAGE_MASK;
+ dma->sglist[0].offset = dma->bus_addr & ~PAGE_MASK;
+ sg_dma_len(&dma->sglist[0]) = dma->nr_pages * PAGE_SIZE;
+ }
+ }
+ if (NULL == dma->sglist) {
+ dprintk(1, "scatterlist is NULL\n");
+ return -ENOMEM;
+ }
+ if (!dma->bus_addr) {
+ dma->sglen = dma_map_sg(dev, dma->sglist,
+ dma->nr_pages, dma->direction);
+ if (0 == dma->sglen) {
+ printk(KERN_WARNING
+ "%s: videobuf_map_sg failed\n", __func__);
+ vfree(dma->sglist);
+ dma->sglist = NULL;
+ dma->sglen = 0;
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(videobuf_dma_map);
+
+int videobuf_dma_unmap(struct device *dev, struct videobuf_dmabuf *dma)
+{
+ MAGIC_CHECK(dma->magic, MAGIC_DMABUF);
+
+ if (!dma->sglen)
+ return 0;
+
+ dma_unmap_sg(dev, dma->sglist, dma->sglen, dma->direction);
+
+ vfree(dma->sglist);
+ dma->sglist = NULL;
+ dma->sglen = 0;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(videobuf_dma_unmap);
+
+int videobuf_dma_free(struct videobuf_dmabuf *dma)
+{
+ int i;
+ MAGIC_CHECK(dma->magic, MAGIC_DMABUF);
+ BUG_ON(dma->sglen);
+
+ if (dma->pages) {
+ for (i = 0; i < dma->nr_pages; i++)
+ page_cache_release(dma->pages[i]);
+ kfree(dma->pages);
+ dma->pages = NULL;
+ }
+
+ vfree(dma->vaddr);
+ dma->vaddr = NULL;
+
+ if (dma->bus_addr)
+ dma->bus_addr = 0;
+ dma->direction = DMA_NONE;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(videobuf_dma_free);
+
+/* --------------------------------------------------------------------- */
+
+static void videobuf_vm_open(struct vm_area_struct *vma)
+{
+ struct videobuf_mapping *map = vma->vm_private_data;
+
+ dprintk(2, "vm_open %p [count=%d,vma=%08lx-%08lx]\n", map,
+ map->count, vma->vm_start, vma->vm_end);
+
+ map->count++;
+}
+
+static void videobuf_vm_close(struct vm_area_struct *vma)
+{
+ struct videobuf_mapping *map = vma->vm_private_data;
+ struct videobuf_queue *q = map->q;
+ struct videobuf_dma_sg_memory *mem;
+ int i;
+
+ dprintk(2, "vm_close %p [count=%d,vma=%08lx-%08lx]\n", map,
+ map->count, vma->vm_start, vma->vm_end);
+
+ map->count--;
+ if (0 == map->count) {
+ dprintk(1, "munmap %p q=%p\n", map, q);
+ videobuf_queue_lock(q);
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ if (NULL == q->bufs[i])
+ continue;
+ mem = q->bufs[i]->priv;
+ if (!mem)
+ continue;
+
+ MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
+
+ if (q->bufs[i]->map != map)
+ continue;
+ q->bufs[i]->map = NULL;
+ q->bufs[i]->baddr = 0;
+ q->ops->buf_release(q, q->bufs[i]);
+ }
+ videobuf_queue_unlock(q);
+ kfree(map);
+ }
+ return;
+}
+
+/*
+ * Get a anonymous page for the mapping. Make sure we can DMA to that
+ * memory location with 32bit PCI devices (i.e. don't use highmem for
+ * now ...). Bounce buffers don't work very well for the data rates
+ * video capture has.
+ */
+static int videobuf_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+ struct page *page;
+
+ dprintk(3, "fault: fault @ %08lx [vma %08lx-%08lx]\n",
+ (unsigned long)vmf->virtual_address,
+ vma->vm_start, vma->vm_end);
+
+ page = alloc_page(GFP_USER | __GFP_DMA32);
+ if (!page)
+ return VM_FAULT_OOM;
+ clear_user_highpage(page, (unsigned long)vmf->virtual_address);
+ vmf->page = page;
+
+ return 0;
+}
+
+static const struct vm_operations_struct videobuf_vm_ops = {
+ .open = videobuf_vm_open,
+ .close = videobuf_vm_close,
+ .fault = videobuf_vm_fault,
+};
+
+/* ---------------------------------------------------------------------
+ * SG handlers for the generic methods
+ */
+
+/* Allocated area consists on 3 parts:
+ struct video_buffer
+ struct <driver>_buffer (cx88_buffer, saa7134_buf, ...)
+ struct videobuf_dma_sg_memory
+ */
+
+static struct videobuf_buffer *__videobuf_alloc_vb(size_t size)
+{
+ struct videobuf_dma_sg_memory *mem;
+ struct videobuf_buffer *vb;
+
+ vb = kzalloc(size + sizeof(*mem), GFP_KERNEL);
+ if (!vb)
+ return vb;
+
+ mem = vb->priv = ((char *)vb) + size;
+ mem->magic = MAGIC_SG_MEM;
+
+ videobuf_dma_init(&mem->dma);
+
+ dprintk(1, "%s: allocated at %p(%ld+%ld) & %p(%ld)\n",
+ __func__, vb, (long)sizeof(*vb), (long)size - sizeof(*vb),
+ mem, (long)sizeof(*mem));
+
+ return vb;
+}
+
+static void *__videobuf_to_vaddr(struct videobuf_buffer *buf)
+{
+ struct videobuf_dma_sg_memory *mem = buf->priv;
+ BUG_ON(!mem);
+
+ MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
+
+ return mem->dma.vaddr;
+}
+
+static int __videobuf_iolock(struct videobuf_queue *q,
+ struct videobuf_buffer *vb,
+ struct v4l2_framebuffer *fbuf)
+{
+ int err, pages;
+ dma_addr_t bus;
+ struct videobuf_dma_sg_memory *mem = vb->priv;
+ BUG_ON(!mem);
+
+ MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
+
+ switch (vb->memory) {
+ case V4L2_MEMORY_MMAP:
+ case V4L2_MEMORY_USERPTR:
+ if (0 == vb->baddr) {
+ /* no userspace addr -- kernel bounce buffer */
+ pages = PAGE_ALIGN(vb->size) >> PAGE_SHIFT;
+ err = videobuf_dma_init_kernel(&mem->dma,
+ DMA_FROM_DEVICE,
+ pages);
+ if (0 != err)
+ return err;
+ } else if (vb->memory == V4L2_MEMORY_USERPTR) {
+ /* dma directly to userspace */
+ err = videobuf_dma_init_user(&mem->dma,
+ DMA_FROM_DEVICE,
+ vb->baddr, vb->bsize);
+ if (0 != err)
+ return err;
+ } else {
+ /* NOTE: HACK: videobuf_iolock on V4L2_MEMORY_MMAP
+ buffers can only be called from videobuf_qbuf
+ we take current->mm->mmap_sem there, to prevent
+ locking inversion, so don't take it here */
+
+ err = videobuf_dma_init_user_locked(&mem->dma,
+ DMA_FROM_DEVICE,
+ vb->baddr, vb->bsize);
+ if (0 != err)
+ return err;
+ }
+ break;
+ case V4L2_MEMORY_OVERLAY:
+ if (NULL == fbuf)
+ return -EINVAL;
+ /* FIXME: need sanity checks for vb->boff */
+ /*
+ * Using a double cast to avoid compiler warnings when
+ * building for PAE. Compiler doesn't like direct casting
+ * of a 32 bit ptr to 64 bit integer.
+ */
+ bus = (dma_addr_t)(unsigned long)fbuf->base + vb->boff;
+ pages = PAGE_ALIGN(vb->size) >> PAGE_SHIFT;
+ err = videobuf_dma_init_overlay(&mem->dma, DMA_FROM_DEVICE,
+ bus, pages);
+ if (0 != err)
+ return err;
+ break;
+ default:
+ BUG();
+ }
+ err = videobuf_dma_map(q->dev, &mem->dma);
+ if (0 != err)
+ return err;
+
+ return 0;
+}
+
+static int __videobuf_sync(struct videobuf_queue *q,
+ struct videobuf_buffer *buf)
+{
+ struct videobuf_dma_sg_memory *mem = buf->priv;
+ BUG_ON(!mem || !mem->dma.sglen);
+
+ MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
+ MAGIC_CHECK(mem->dma.magic, MAGIC_DMABUF);
+
+ dma_sync_sg_for_cpu(q->dev, mem->dma.sglist,
+ mem->dma.sglen, mem->dma.direction);
+
+ return 0;
+}
+
+static int __videobuf_mmap_mapper(struct videobuf_queue *q,
+ struct videobuf_buffer *buf,
+ struct vm_area_struct *vma)
+{
+ struct videobuf_dma_sg_memory *mem = buf->priv;
+ struct videobuf_mapping *map;
+ unsigned int first, last, size = 0, i;
+ int retval;
+
+ retval = -EINVAL;
+
+ BUG_ON(!mem);
+ MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
+
+ /* look for first buffer to map */
+ for (first = 0; first < VIDEO_MAX_FRAME; first++) {
+ if (buf == q->bufs[first]) {
+ size = PAGE_ALIGN(q->bufs[first]->bsize);
+ break;
+ }
+ }
+
+ /* paranoia, should never happen since buf is always valid. */
+ if (!size) {
+ dprintk(1, "mmap app bug: offset invalid [offset=0x%lx]\n",
+ (vma->vm_pgoff << PAGE_SHIFT));
+ goto done;
+ }
+
+ last = first;
+
+ /* create mapping + update buffer list */
+ retval = -ENOMEM;
+ map = kmalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
+ if (NULL == map)
+ goto done;
+
+ size = 0;
+ for (i = first; i <= last; i++) {
+ if (NULL == q->bufs[i])
+ continue;
+ q->bufs[i]->map = map;
+ q->bufs[i]->baddr = vma->vm_start + size;
+ size += PAGE_ALIGN(q->bufs[i]->bsize);
+ }
+
+ map->count = 1;
+ map->q = q;
+ vma->vm_ops = &videobuf_vm_ops;
+ vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED;
+ vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */
+ vma->vm_private_data = map;
+ dprintk(1, "mmap %p: q=%p %08lx-%08lx pgoff %08lx bufs %d-%d\n",
+ map, q, vma->vm_start, vma->vm_end, vma->vm_pgoff, first, last);
+ retval = 0;
+
+done:
+ return retval;
+}
+
+static struct videobuf_qtype_ops sg_ops = {
+ .magic = MAGIC_QTYPE_OPS,
+
+ .alloc_vb = __videobuf_alloc_vb,
+ .iolock = __videobuf_iolock,
+ .sync = __videobuf_sync,
+ .mmap_mapper = __videobuf_mmap_mapper,
+ .vaddr = __videobuf_to_vaddr,
+};
+
+void *videobuf_sg_alloc(size_t size)
+{
+ struct videobuf_queue q;
+
+ /* Required to make generic handler to call __videobuf_alloc */
+ q.int_ops = &sg_ops;
+
+ q.msize = size;
+
+ return videobuf_alloc_vb(&q);
+}
+EXPORT_SYMBOL_GPL(videobuf_sg_alloc);
+
+void videobuf_queue_sg_init(struct videobuf_queue *q,
+ const struct videobuf_queue_ops *ops,
+ struct device *dev,
+ spinlock_t *irqlock,
+ enum v4l2_buf_type type,
+ enum v4l2_field field,
+ unsigned int msize,
+ void *priv,
+ struct mutex *ext_lock)
+{
+ videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
+ priv, &sg_ops, ext_lock);
+}
+EXPORT_SYMBOL_GPL(videobuf_queue_sg_init);
+
diff --git a/drivers/media/video/videobuf-dvb.c b/drivers/media/video/videobuf-dvb.c
new file mode 100644
index 00000000..59cb54aa
--- /dev/null
+++ b/drivers/media/video/videobuf-dvb.c
@@ -0,0 +1,404 @@
+/*
+ *
+ * some helper function for simple DVB cards which simply DMA the
+ * complete transport stream and let the computer sort everything else
+ * (i.e. we are using the software demux, ...). Also uses the
+ * video-buf to manage DMA buffers.
+ *
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/kthread.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+
+#include <linux/freezer.h>
+
+#include <media/videobuf-core.h>
+#include <media/videobuf-dvb.h>
+
+/* ------------------------------------------------------------------ */
+
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug,"enable debug messages");
+
+#define dprintk(fmt, arg...) if (debug) \
+ printk(KERN_DEBUG "%s/dvb: " fmt, dvb->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+
+static int videobuf_dvb_thread(void *data)
+{
+ struct videobuf_dvb *dvb = data;
+ struct videobuf_buffer *buf;
+ unsigned long flags;
+ int err;
+ void *outp;
+
+ dprintk("dvb thread started\n");
+ set_freezable();
+ videobuf_read_start(&dvb->dvbq);
+
+ for (;;) {
+ /* fetch next buffer */
+ buf = list_entry(dvb->dvbq.stream.next,
+ struct videobuf_buffer, stream);
+ list_del(&buf->stream);
+ err = videobuf_waiton(&dvb->dvbq, buf, 0, 1);
+
+ /* no more feeds left or stop_feed() asked us to quit */
+ if (0 == dvb->nfeeds)
+ break;
+ if (kthread_should_stop())
+ break;
+ try_to_freeze();
+
+ /* feed buffer data to demux */
+ outp = videobuf_queue_to_vaddr(&dvb->dvbq, buf);
+
+ if (buf->state == VIDEOBUF_DONE)
+ dvb_dmx_swfilter(&dvb->demux, outp,
+ buf->size);
+
+ /* requeue buffer */
+ list_add_tail(&buf->stream,&dvb->dvbq.stream);
+ spin_lock_irqsave(dvb->dvbq.irqlock,flags);
+ dvb->dvbq.ops->buf_queue(&dvb->dvbq,buf);
+ spin_unlock_irqrestore(dvb->dvbq.irqlock,flags);
+ }
+
+ videobuf_read_stop(&dvb->dvbq);
+ dprintk("dvb thread stopped\n");
+
+ /* Hmm, linux becomes *very* unhappy without this ... */
+ while (!kthread_should_stop()) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule();
+ }
+ return 0;
+}
+
+static int videobuf_dvb_start_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct videobuf_dvb *dvb = demux->priv;
+ int rc;
+
+ if (!demux->dmx.frontend)
+ return -EINVAL;
+
+ mutex_lock(&dvb->lock);
+ dvb->nfeeds++;
+ rc = dvb->nfeeds;
+
+ if (NULL != dvb->thread)
+ goto out;
+ dvb->thread = kthread_run(videobuf_dvb_thread,
+ dvb, "%s dvb", dvb->name);
+ if (IS_ERR(dvb->thread)) {
+ rc = PTR_ERR(dvb->thread);
+ dvb->thread = NULL;
+ }
+
+out:
+ mutex_unlock(&dvb->lock);
+ return rc;
+}
+
+static int videobuf_dvb_stop_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct videobuf_dvb *dvb = demux->priv;
+ int err = 0;
+
+ mutex_lock(&dvb->lock);
+ dvb->nfeeds--;
+ if (0 == dvb->nfeeds && NULL != dvb->thread) {
+ err = kthread_stop(dvb->thread);
+ dvb->thread = NULL;
+ }
+ mutex_unlock(&dvb->lock);
+ return err;
+}
+
+static int videobuf_dvb_register_adapter(struct videobuf_dvb_frontends *fe,
+ struct module *module,
+ void *adapter_priv,
+ struct device *device,
+ char *adapter_name,
+ short *adapter_nr,
+ int mfe_shared,
+ int (*fe_ioctl_override)(struct dvb_frontend *,
+ unsigned int, void *, unsigned int))
+{
+ int result;
+
+ mutex_init(&fe->lock);
+
+ /* register adapter */
+ result = dvb_register_adapter(&fe->adapter, adapter_name, module,
+ device, adapter_nr);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: dvb_register_adapter failed (errno = %d)\n",
+ adapter_name, result);
+ }
+ fe->adapter.priv = adapter_priv;
+ fe->adapter.mfe_shared = mfe_shared;
+ fe->adapter.fe_ioctl_override = fe_ioctl_override;
+
+ return result;
+}
+
+static int videobuf_dvb_register_frontend(struct dvb_adapter *adapter,
+ struct videobuf_dvb *dvb)
+{
+ int result;
+
+ /* register frontend */
+ result = dvb_register_frontend(adapter, dvb->frontend);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: dvb_register_frontend failed (errno = %d)\n",
+ dvb->name, result);
+ goto fail_frontend;
+ }
+
+ /* register demux stuff */
+ dvb->demux.dmx.capabilities =
+ DMX_TS_FILTERING | DMX_SECTION_FILTERING |
+ DMX_MEMORY_BASED_FILTERING;
+ dvb->demux.priv = dvb;
+ dvb->demux.filternum = 256;
+ dvb->demux.feednum = 256;
+ dvb->demux.start_feed = videobuf_dvb_start_feed;
+ dvb->demux.stop_feed = videobuf_dvb_stop_feed;
+ result = dvb_dmx_init(&dvb->demux);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n",
+ dvb->name, result);
+ goto fail_dmx;
+ }
+
+ dvb->dmxdev.filternum = 256;
+ dvb->dmxdev.demux = &dvb->demux.dmx;
+ dvb->dmxdev.capabilities = 0;
+ result = dvb_dmxdev_init(&dvb->dmxdev, adapter);
+
+ if (result < 0) {
+ printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n",
+ dvb->name, result);
+ goto fail_dmxdev;
+ }
+
+ dvb->fe_hw.source = DMX_FRONTEND_0;
+ result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n",
+ dvb->name, result);
+ goto fail_fe_hw;
+ }
+
+ dvb->fe_mem.source = DMX_MEMORY_FE;
+ result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
+ dvb->name, result);
+ goto fail_fe_mem;
+ }
+
+ result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: connect_frontend failed (errno = %d)\n",
+ dvb->name, result);
+ goto fail_fe_conn;
+ }
+
+ /* register network adapter */
+ result = dvb_net_init(adapter, &dvb->net, &dvb->demux.dmx);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: dvb_net_init failed (errno = %d)\n",
+ dvb->name, result);
+ goto fail_fe_conn;
+ }
+ return 0;
+
+fail_fe_conn:
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+fail_fe_mem:
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+fail_fe_hw:
+ dvb_dmxdev_release(&dvb->dmxdev);
+fail_dmxdev:
+ dvb_dmx_release(&dvb->demux);
+fail_dmx:
+ dvb_unregister_frontend(dvb->frontend);
+fail_frontend:
+ dvb_frontend_detach(dvb->frontend);
+ dvb->frontend = NULL;
+
+ return result;
+}
+
+/* ------------------------------------------------------------------ */
+/* Register a single adapter and one or more frontends */
+int videobuf_dvb_register_bus(struct videobuf_dvb_frontends *f,
+ struct module *module,
+ void *adapter_priv,
+ struct device *device,
+ short *adapter_nr,
+ int mfe_shared,
+ int (*fe_ioctl_override)(struct dvb_frontend *,
+ unsigned int, void *, unsigned int))
+{
+ struct list_head *list, *q;
+ struct videobuf_dvb_frontend *fe;
+ int res;
+
+ fe = videobuf_dvb_get_frontend(f, 1);
+ if (!fe) {
+ printk(KERN_WARNING "Unable to register the adapter which has no frontends\n");
+ return -EINVAL;
+ }
+
+ /* Bring up the adapter */
+ res = videobuf_dvb_register_adapter(f, module, adapter_priv, device,
+ fe->dvb.name, adapter_nr, mfe_shared, fe_ioctl_override);
+ if (res < 0) {
+ printk(KERN_WARNING "videobuf_dvb_register_adapter failed (errno = %d)\n", res);
+ return res;
+ }
+
+ /* Attach all of the frontends to the adapter */
+ mutex_lock(&f->lock);
+ list_for_each_safe(list, q, &f->felist) {
+ fe = list_entry(list, struct videobuf_dvb_frontend, felist);
+ res = videobuf_dvb_register_frontend(&f->adapter, &fe->dvb);
+ if (res < 0) {
+ printk(KERN_WARNING "%s: videobuf_dvb_register_frontend failed (errno = %d)\n",
+ fe->dvb.name, res);
+ goto err;
+ }
+ }
+ mutex_unlock(&f->lock);
+ return 0;
+
+err:
+ mutex_unlock(&f->lock);
+ videobuf_dvb_unregister_bus(f);
+ return res;
+}
+EXPORT_SYMBOL(videobuf_dvb_register_bus);
+
+void videobuf_dvb_unregister_bus(struct videobuf_dvb_frontends *f)
+{
+ videobuf_dvb_dealloc_frontends(f);
+
+ dvb_unregister_adapter(&f->adapter);
+}
+EXPORT_SYMBOL(videobuf_dvb_unregister_bus);
+
+struct videobuf_dvb_frontend *videobuf_dvb_get_frontend(
+ struct videobuf_dvb_frontends *f, int id)
+{
+ struct list_head *list, *q;
+ struct videobuf_dvb_frontend *fe, *ret = NULL;
+
+ mutex_lock(&f->lock);
+
+ list_for_each_safe(list, q, &f->felist) {
+ fe = list_entry(list, struct videobuf_dvb_frontend, felist);
+ if (fe->id == id) {
+ ret = fe;
+ break;
+ }
+ }
+
+ mutex_unlock(&f->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(videobuf_dvb_get_frontend);
+
+int videobuf_dvb_find_frontend(struct videobuf_dvb_frontends *f,
+ struct dvb_frontend *p)
+{
+ struct list_head *list, *q;
+ struct videobuf_dvb_frontend *fe = NULL;
+ int ret = 0;
+
+ mutex_lock(&f->lock);
+
+ list_for_each_safe(list, q, &f->felist) {
+ fe = list_entry(list, struct videobuf_dvb_frontend, felist);
+ if (fe->dvb.frontend == p) {
+ ret = fe->id;
+ break;
+ }
+ }
+
+ mutex_unlock(&f->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(videobuf_dvb_find_frontend);
+
+struct videobuf_dvb_frontend *videobuf_dvb_alloc_frontend(
+ struct videobuf_dvb_frontends *f, int id)
+{
+ struct videobuf_dvb_frontend *fe;
+
+ fe = kzalloc(sizeof(struct videobuf_dvb_frontend), GFP_KERNEL);
+ if (fe == NULL)
+ goto fail_alloc;
+
+ fe->id = id;
+ mutex_init(&fe->dvb.lock);
+
+ mutex_lock(&f->lock);
+ list_add_tail(&fe->felist, &f->felist);
+ mutex_unlock(&f->lock);
+
+fail_alloc:
+ return fe;
+}
+EXPORT_SYMBOL(videobuf_dvb_alloc_frontend);
+
+void videobuf_dvb_dealloc_frontends(struct videobuf_dvb_frontends *f)
+{
+ struct list_head *list, *q;
+ struct videobuf_dvb_frontend *fe;
+
+ mutex_lock(&f->lock);
+ list_for_each_safe(list, q, &f->felist) {
+ fe = list_entry(list, struct videobuf_dvb_frontend, felist);
+ if (fe->dvb.net.dvbdev) {
+ dvb_net_release(&fe->dvb.net);
+ fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx,
+ &fe->dvb.fe_mem);
+ fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx,
+ &fe->dvb.fe_hw);
+ dvb_dmxdev_release(&fe->dvb.dmxdev);
+ dvb_dmx_release(&fe->dvb.demux);
+ dvb_unregister_frontend(fe->dvb.frontend);
+ }
+ if (fe->dvb.frontend)
+ /* always allocated, may have been reset */
+ dvb_frontend_detach(fe->dvb.frontend);
+ list_del(list); /* remove list entry */
+ kfree(fe); /* free frontend allocation */
+ }
+ mutex_unlock(&f->lock);
+}
+EXPORT_SYMBOL(videobuf_dvb_dealloc_frontends);
diff --git a/drivers/media/video/videobuf-vmalloc.c b/drivers/media/video/videobuf-vmalloc.c
new file mode 100644
index 00000000..df142580
--- /dev/null
+++ b/drivers/media/video/videobuf-vmalloc.c
@@ -0,0 +1,349 @@
+/*
+ * helper functions for vmalloc video4linux capture buffers
+ *
+ * The functions expect the hardware being able to scatter gather
+ * (i.e. the buffers are not linear in physical memory, but fragmented
+ * into PAGE_SIZE chunks). They also assume the driver does not need
+ * to touch the video data.
+ *
+ * (c) 2007 Mauro Carvalho Chehab, <mchehab@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/pagemap.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+
+#include <media/videobuf-vmalloc.h>
+
+#define MAGIC_DMABUF 0x17760309
+#define MAGIC_VMAL_MEM 0x18221223
+
+#define MAGIC_CHECK(is, should) \
+ if (unlikely((is) != (should))) { \
+ printk(KERN_ERR "magic mismatch: %x (expected %x)\n", \
+ is, should); \
+ BUG(); \
+ }
+
+static int debug;
+module_param(debug, int, 0644);
+
+MODULE_DESCRIPTION("helper module to manage video4linux vmalloc buffers");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
+MODULE_LICENSE("GPL");
+
+#define dprintk(level, fmt, arg...) \
+ if (debug >= level) \
+ printk(KERN_DEBUG "vbuf-vmalloc: " fmt , ## arg)
+
+
+/***************************************************************************/
+
+static void videobuf_vm_open(struct vm_area_struct *vma)
+{
+ struct videobuf_mapping *map = vma->vm_private_data;
+
+ dprintk(2, "vm_open %p [count=%u,vma=%08lx-%08lx]\n", map,
+ map->count, vma->vm_start, vma->vm_end);
+
+ map->count++;
+}
+
+static void videobuf_vm_close(struct vm_area_struct *vma)
+{
+ struct videobuf_mapping *map = vma->vm_private_data;
+ struct videobuf_queue *q = map->q;
+ int i;
+
+ dprintk(2, "vm_close %p [count=%u,vma=%08lx-%08lx]\n", map,
+ map->count, vma->vm_start, vma->vm_end);
+
+ map->count--;
+ if (0 == map->count) {
+ struct videobuf_vmalloc_memory *mem;
+
+ dprintk(1, "munmap %p q=%p\n", map, q);
+ videobuf_queue_lock(q);
+
+ /* We need first to cancel streams, before unmapping */
+ if (q->streaming)
+ videobuf_queue_cancel(q);
+
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ if (NULL == q->bufs[i])
+ continue;
+
+ if (q->bufs[i]->map != map)
+ continue;
+
+ mem = q->bufs[i]->priv;
+ if (mem) {
+ /* This callback is called only if kernel has
+ allocated memory and this memory is mmapped.
+ In this case, memory should be freed,
+ in order to do memory unmap.
+ */
+
+ MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
+
+ /* vfree is not atomic - can't be
+ called with IRQ's disabled
+ */
+ dprintk(1, "%s: buf[%d] freeing (%p)\n",
+ __func__, i, mem->vaddr);
+
+ vfree(mem->vaddr);
+ mem->vaddr = NULL;
+ }
+
+ q->bufs[i]->map = NULL;
+ q->bufs[i]->baddr = 0;
+ }
+
+ kfree(map);
+
+ videobuf_queue_unlock(q);
+ }
+
+ return;
+}
+
+static const struct vm_operations_struct videobuf_vm_ops = {
+ .open = videobuf_vm_open,
+ .close = videobuf_vm_close,
+};
+
+/* ---------------------------------------------------------------------
+ * vmalloc handlers for the generic methods
+ */
+
+/* Allocated area consists on 3 parts:
+ struct video_buffer
+ struct <driver>_buffer (cx88_buffer, saa7134_buf, ...)
+ struct videobuf_dma_sg_memory
+ */
+
+static struct videobuf_buffer *__videobuf_alloc_vb(size_t size)
+{
+ struct videobuf_vmalloc_memory *mem;
+ struct videobuf_buffer *vb;
+
+ vb = kzalloc(size + sizeof(*mem), GFP_KERNEL);
+ if (!vb)
+ return vb;
+
+ mem = vb->priv = ((char *)vb) + size;
+ mem->magic = MAGIC_VMAL_MEM;
+
+ dprintk(1, "%s: allocated at %p(%ld+%ld) & %p(%ld)\n",
+ __func__, vb, (long)sizeof(*vb), (long)size - sizeof(*vb),
+ mem, (long)sizeof(*mem));
+
+ return vb;
+}
+
+static int __videobuf_iolock(struct videobuf_queue *q,
+ struct videobuf_buffer *vb,
+ struct v4l2_framebuffer *fbuf)
+{
+ struct videobuf_vmalloc_memory *mem = vb->priv;
+ int pages;
+
+ BUG_ON(!mem);
+
+ MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
+
+ switch (vb->memory) {
+ case V4L2_MEMORY_MMAP:
+ dprintk(1, "%s memory method MMAP\n", __func__);
+
+ /* All handling should be done by __videobuf_mmap_mapper() */
+ if (!mem->vaddr) {
+ printk(KERN_ERR "memory is not alloced/mmapped.\n");
+ return -EINVAL;
+ }
+ break;
+ case V4L2_MEMORY_USERPTR:
+ pages = PAGE_ALIGN(vb->size);
+
+ dprintk(1, "%s memory method USERPTR\n", __func__);
+
+ if (vb->baddr) {
+ printk(KERN_ERR "USERPTR is currently not supported\n");
+ return -EINVAL;
+ }
+
+ /* The only USERPTR currently supported is the one needed for
+ * read() method.
+ */
+
+ mem->vaddr = vmalloc_user(pages);
+ if (!mem->vaddr) {
+ printk(KERN_ERR "vmalloc (%d pages) failed\n", pages);
+ return -ENOMEM;
+ }
+ dprintk(1, "vmalloc is at addr %p (%d pages)\n",
+ mem->vaddr, pages);
+
+#if 0
+ int rc;
+ /* Kernel userptr is used also by read() method. In this case,
+ there's no need to remap, since data will be copied to user
+ */
+ if (!vb->baddr)
+ return 0;
+
+ /* FIXME: to properly support USERPTR, remap should occur.
+ The code below won't work, since mem->vma = NULL
+ */
+ /* Try to remap memory */
+ rc = remap_vmalloc_range(mem->vma, (void *)vb->baddr, 0);
+ if (rc < 0) {
+ printk(KERN_ERR "mmap: remap failed with error %d", rc);
+ return -ENOMEM;
+ }
+#endif
+
+ break;
+ case V4L2_MEMORY_OVERLAY:
+ default:
+ dprintk(1, "%s memory method OVERLAY/unknown\n", __func__);
+
+ /* Currently, doesn't support V4L2_MEMORY_OVERLAY */
+ printk(KERN_ERR "Memory method currently unsupported.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int __videobuf_mmap_mapper(struct videobuf_queue *q,
+ struct videobuf_buffer *buf,
+ struct vm_area_struct *vma)
+{
+ struct videobuf_vmalloc_memory *mem;
+ struct videobuf_mapping *map;
+ int retval, pages;
+
+ dprintk(1, "%s\n", __func__);
+
+ /* create mapping + update buffer list */
+ map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
+ if (NULL == map)
+ return -ENOMEM;
+
+ buf->map = map;
+ map->q = q;
+
+ buf->baddr = vma->vm_start;
+
+ mem = buf->priv;
+ BUG_ON(!mem);
+ MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
+
+ pages = PAGE_ALIGN(vma->vm_end - vma->vm_start);
+ mem->vaddr = vmalloc_user(pages);
+ if (!mem->vaddr) {
+ printk(KERN_ERR "vmalloc (%d pages) failed\n", pages);
+ goto error;
+ }
+ dprintk(1, "vmalloc is at addr %p (%d pages)\n", mem->vaddr, pages);
+
+ /* Try to remap memory */
+ retval = remap_vmalloc_range(vma, mem->vaddr, 0);
+ if (retval < 0) {
+ printk(KERN_ERR "mmap: remap failed with error %d. ", retval);
+ vfree(mem->vaddr);
+ goto error;
+ }
+
+ vma->vm_ops = &videobuf_vm_ops;
+ vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED;
+ vma->vm_private_data = map;
+
+ dprintk(1, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n",
+ map, q, vma->vm_start, vma->vm_end,
+ (long int)buf->bsize,
+ vma->vm_pgoff, buf->i);
+
+ videobuf_vm_open(vma);
+
+ return 0;
+
+error:
+ mem = NULL;
+ kfree(map);
+ return -ENOMEM;
+}
+
+static struct videobuf_qtype_ops qops = {
+ .magic = MAGIC_QTYPE_OPS,
+
+ .alloc_vb = __videobuf_alloc_vb,
+ .iolock = __videobuf_iolock,
+ .mmap_mapper = __videobuf_mmap_mapper,
+ .vaddr = videobuf_to_vmalloc,
+};
+
+void videobuf_queue_vmalloc_init(struct videobuf_queue *q,
+ const struct videobuf_queue_ops *ops,
+ struct device *dev,
+ spinlock_t *irqlock,
+ enum v4l2_buf_type type,
+ enum v4l2_field field,
+ unsigned int msize,
+ void *priv,
+ struct mutex *ext_lock)
+{
+ videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
+ priv, &qops, ext_lock);
+}
+EXPORT_SYMBOL_GPL(videobuf_queue_vmalloc_init);
+
+void *videobuf_to_vmalloc(struct videobuf_buffer *buf)
+{
+ struct videobuf_vmalloc_memory *mem = buf->priv;
+ BUG_ON(!mem);
+ MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
+
+ return mem->vaddr;
+}
+EXPORT_SYMBOL_GPL(videobuf_to_vmalloc);
+
+void videobuf_vmalloc_free(struct videobuf_buffer *buf)
+{
+ struct videobuf_vmalloc_memory *mem = buf->priv;
+
+ /* mmapped memory can't be freed here, otherwise mmapped region
+ would be released, while still needed. In this case, the memory
+ release should happen inside videobuf_vm_close().
+ So, it should free memory only if the memory were allocated for
+ read() operation.
+ */
+ if ((buf->memory != V4L2_MEMORY_USERPTR) || buf->baddr)
+ return;
+
+ if (!mem)
+ return;
+
+ MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
+
+ vfree(mem->vaddr);
+ mem->vaddr = NULL;
+
+ return;
+}
+EXPORT_SYMBOL_GPL(videobuf_vmalloc_free);
+
diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c
new file mode 100644
index 00000000..2e8f1df7
--- /dev/null
+++ b/drivers/media/video/videobuf2-core.c
@@ -0,0 +1,2102 @@
+/*
+ * videobuf2-core.c - V4L2 driver helper framework
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <pawel@osciak.com>
+ * Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+
+#include <media/videobuf2-core.h>
+
+static int debug;
+module_param(debug, int, 0644);
+
+#define dprintk(level, fmt, arg...) \
+ do { \
+ if (debug >= level) \
+ printk(KERN_DEBUG "vb2: " fmt, ## arg); \
+ } while (0)
+
+#define call_memop(q, op, args...) \
+ (((q)->mem_ops->op) ? \
+ ((q)->mem_ops->op(args)) : 0)
+
+#define call_qop(q, op, args...) \
+ (((q)->ops->op) ? ((q)->ops->op(args)) : 0)
+
+#define V4L2_BUFFER_STATE_FLAGS (V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \
+ V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR | \
+ V4L2_BUF_FLAG_PREPARED)
+
+/**
+ * __vb2_buf_mem_alloc() - allocate video memory for the given buffer
+ */
+static int __vb2_buf_mem_alloc(struct vb2_buffer *vb)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+ void *mem_priv;
+ int plane;
+
+ /* Allocate memory for all planes in this buffer */
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ mem_priv = call_memop(q, alloc, q->alloc_ctx[plane],
+ q->plane_sizes[plane]);
+ if (IS_ERR_OR_NULL(mem_priv))
+ goto free;
+
+ /* Associate allocator private data with this plane */
+ vb->planes[plane].mem_priv = mem_priv;
+ vb->v4l2_planes[plane].length = q->plane_sizes[plane];
+ }
+
+ return 0;
+free:
+ /* Free already allocated memory if one of the allocations failed */
+ for (; plane > 0; --plane) {
+ call_memop(q, put, vb->planes[plane - 1].mem_priv);
+ vb->planes[plane - 1].mem_priv = NULL;
+ }
+
+ return -ENOMEM;
+}
+
+/**
+ * __vb2_buf_mem_free() - free memory of the given buffer
+ */
+static void __vb2_buf_mem_free(struct vb2_buffer *vb)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+ unsigned int plane;
+
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ call_memop(q, put, vb->planes[plane].mem_priv);
+ vb->planes[plane].mem_priv = NULL;
+ dprintk(3, "Freed plane %d of buffer %d\n", plane,
+ vb->v4l2_buf.index);
+ }
+}
+
+/**
+ * __vb2_buf_userptr_put() - release userspace memory associated with
+ * a USERPTR buffer
+ */
+static void __vb2_buf_userptr_put(struct vb2_buffer *vb)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+ unsigned int plane;
+
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ if (vb->planes[plane].mem_priv)
+ call_memop(q, put_userptr, vb->planes[plane].mem_priv);
+ vb->planes[plane].mem_priv = NULL;
+ }
+}
+
+/**
+ * __setup_offsets() - setup unique offsets ("cookies") for every plane in
+ * every buffer on the queue
+ */
+static void __setup_offsets(struct vb2_queue *q, unsigned int n)
+{
+ unsigned int buffer, plane;
+ struct vb2_buffer *vb;
+ unsigned long off;
+
+ if (q->num_buffers) {
+ struct v4l2_plane *p;
+ vb = q->bufs[q->num_buffers - 1];
+ p = &vb->v4l2_planes[vb->num_planes - 1];
+ off = PAGE_ALIGN(p->m.mem_offset + p->length);
+ } else {
+ off = 0;
+ }
+
+ for (buffer = q->num_buffers; buffer < q->num_buffers + n; ++buffer) {
+ vb = q->bufs[buffer];
+ if (!vb)
+ continue;
+
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ vb->v4l2_planes[plane].length = q->plane_sizes[plane];
+ vb->v4l2_planes[plane].m.mem_offset = off;
+
+ dprintk(3, "Buffer %d, plane %d offset 0x%08lx\n",
+ buffer, plane, off);
+
+ off += vb->v4l2_planes[plane].length;
+ off = PAGE_ALIGN(off);
+ }
+ }
+}
+
+/**
+ * __vb2_queue_alloc() - allocate videobuf buffer structures and (for MMAP type)
+ * video buffer memory for all buffers/planes on the queue and initializes the
+ * queue
+ *
+ * Returns the number of buffers successfully allocated.
+ */
+static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
+ unsigned int num_buffers, unsigned int num_planes)
+{
+ unsigned int buffer;
+ struct vb2_buffer *vb;
+ int ret;
+
+ for (buffer = 0; buffer < num_buffers; ++buffer) {
+ /* Allocate videobuf buffer structures */
+ vb = kzalloc(q->buf_struct_size, GFP_KERNEL);
+ if (!vb) {
+ dprintk(1, "Memory alloc for buffer struct failed\n");
+ break;
+ }
+
+ /* Length stores number of planes for multiplanar buffers */
+ if (V4L2_TYPE_IS_MULTIPLANAR(q->type))
+ vb->v4l2_buf.length = num_planes;
+
+ vb->state = VB2_BUF_STATE_DEQUEUED;
+ vb->vb2_queue = q;
+ vb->num_planes = num_planes;
+ vb->v4l2_buf.index = q->num_buffers + buffer;
+ vb->v4l2_buf.type = q->type;
+ vb->v4l2_buf.memory = memory;
+
+ /* Allocate video buffer memory for the MMAP type */
+ if (memory == V4L2_MEMORY_MMAP) {
+ ret = __vb2_buf_mem_alloc(vb);
+ if (ret) {
+ dprintk(1, "Failed allocating memory for "
+ "buffer %d\n", buffer);
+ kfree(vb);
+ break;
+ }
+ /*
+ * Call the driver-provided buffer initialization
+ * callback, if given. An error in initialization
+ * results in queue setup failure.
+ */
+ ret = call_qop(q, buf_init, vb);
+ if (ret) {
+ dprintk(1, "Buffer %d %p initialization"
+ " failed\n", buffer, vb);
+ __vb2_buf_mem_free(vb);
+ kfree(vb);
+ break;
+ }
+ }
+
+ q->bufs[q->num_buffers + buffer] = vb;
+ }
+
+ __setup_offsets(q, buffer);
+
+ dprintk(1, "Allocated %d buffers, %d plane(s) each\n",
+ buffer, num_planes);
+
+ return buffer;
+}
+
+/**
+ * __vb2_free_mem() - release all video buffer memory for a given queue
+ */
+static void __vb2_free_mem(struct vb2_queue *q, unsigned int buffers)
+{
+ unsigned int buffer;
+ struct vb2_buffer *vb;
+
+ for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
+ ++buffer) {
+ vb = q->bufs[buffer];
+ if (!vb)
+ continue;
+
+ /* Free MMAP buffers or release USERPTR buffers */
+ if (q->memory == V4L2_MEMORY_MMAP)
+ __vb2_buf_mem_free(vb);
+ else
+ __vb2_buf_userptr_put(vb);
+ }
+}
+
+/**
+ * __vb2_queue_free() - free buffers at the end of the queue - video memory and
+ * related information, if no buffers are left return the queue to an
+ * uninitialized state. Might be called even if the queue has already been freed.
+ */
+static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
+{
+ unsigned int buffer;
+
+ /* Call driver-provided cleanup function for each buffer, if provided */
+ if (q->ops->buf_cleanup) {
+ for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
+ ++buffer) {
+ if (NULL == q->bufs[buffer])
+ continue;
+ q->ops->buf_cleanup(q->bufs[buffer]);
+ }
+ }
+
+ /* Release video buffer memory */
+ __vb2_free_mem(q, buffers);
+
+ /* Free videobuf buffers */
+ for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
+ ++buffer) {
+ kfree(q->bufs[buffer]);
+ q->bufs[buffer] = NULL;
+ }
+
+ q->num_buffers -= buffers;
+ if (!q->num_buffers)
+ q->memory = 0;
+ INIT_LIST_HEAD(&q->queued_list);
+}
+
+/**
+ * __verify_planes_array() - verify that the planes array passed in struct
+ * v4l2_buffer from userspace can be safely used
+ */
+static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer *b)
+{
+ /* Is memory for copying plane information present? */
+ if (NULL == b->m.planes) {
+ dprintk(1, "Multi-planar buffer passed but "
+ "planes array not provided\n");
+ return -EINVAL;
+ }
+
+ if (b->length < vb->num_planes || b->length > VIDEO_MAX_PLANES) {
+ dprintk(1, "Incorrect planes array length, "
+ "expected %d, got %d\n", vb->num_planes, b->length);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * __buffer_in_use() - return true if the buffer is in use and
+ * the queue cannot be freed (by the means of REQBUFS(0)) call
+ */
+static bool __buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb)
+{
+ unsigned int plane;
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ void *mem_priv = vb->planes[plane].mem_priv;
+ /*
+ * If num_users() has not been provided, call_memop
+ * will return 0, apparently nobody cares about this
+ * case anyway. If num_users() returns more than 1,
+ * we are not the only user of the plane's memory.
+ */
+ if (mem_priv && call_memop(q, num_users, mem_priv) > 1)
+ return true;
+ }
+ return false;
+}
+
+/**
+ * __buffers_in_use() - return true if any buffers on the queue are in use and
+ * the queue cannot be freed (by the means of REQBUFS(0)) call
+ */
+static bool __buffers_in_use(struct vb2_queue *q)
+{
+ unsigned int buffer;
+ for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+ if (__buffer_in_use(q, q->bufs[buffer]))
+ return true;
+ }
+ return false;
+}
+
+/**
+ * __fill_v4l2_buffer() - fill in a struct v4l2_buffer with information to be
+ * returned to userspace
+ */
+static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+ int ret;
+
+ /* Copy back data such as timestamp, flags, input, etc. */
+ memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m));
+ b->input = vb->v4l2_buf.input;
+ b->reserved = vb->v4l2_buf.reserved;
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(q->type)) {
+ ret = __verify_planes_array(vb, b);
+ if (ret)
+ return ret;
+
+ /*
+ * Fill in plane-related data if userspace provided an array
+ * for it. The memory and size is verified above.
+ */
+ memcpy(b->m.planes, vb->v4l2_planes,
+ b->length * sizeof(struct v4l2_plane));
+ } else {
+ /*
+ * We use length and offset in v4l2_planes array even for
+ * single-planar buffers, but userspace does not.
+ */
+ b->length = vb->v4l2_planes[0].length;
+ b->bytesused = vb->v4l2_planes[0].bytesused;
+ if (q->memory == V4L2_MEMORY_MMAP)
+ b->m.offset = vb->v4l2_planes[0].m.mem_offset;
+ else if (q->memory == V4L2_MEMORY_USERPTR)
+ b->m.userptr = vb->v4l2_planes[0].m.userptr;
+ }
+
+ /*
+ * Clear any buffer state related flags.
+ */
+ b->flags &= ~V4L2_BUFFER_STATE_FLAGS;
+
+ switch (vb->state) {
+ case VB2_BUF_STATE_QUEUED:
+ case VB2_BUF_STATE_ACTIVE:
+ b->flags |= V4L2_BUF_FLAG_QUEUED;
+ break;
+ case VB2_BUF_STATE_ERROR:
+ b->flags |= V4L2_BUF_FLAG_ERROR;
+ /* fall through */
+ case VB2_BUF_STATE_DONE:
+ b->flags |= V4L2_BUF_FLAG_DONE;
+ break;
+ case VB2_BUF_STATE_PREPARED:
+ b->flags |= V4L2_BUF_FLAG_PREPARED;
+ break;
+ case VB2_BUF_STATE_DEQUEUED:
+ /* nothing */
+ break;
+ }
+
+ if (__buffer_in_use(q, vb))
+ b->flags |= V4L2_BUF_FLAG_MAPPED;
+
+ return 0;
+}
+
+/**
+ * vb2_querybuf() - query video buffer information
+ * @q: videobuf queue
+ * @b: buffer struct passed from userspace to vidioc_querybuf handler
+ * in driver
+ *
+ * Should be called from vidioc_querybuf ioctl handler in driver.
+ * This function will verify the passed v4l2_buffer structure and fill the
+ * relevant information for the userspace.
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_querybuf handler in driver.
+ */
+int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b)
+{
+ struct vb2_buffer *vb;
+
+ if (b->type != q->type) {
+ dprintk(1, "querybuf: wrong buffer type\n");
+ return -EINVAL;
+ }
+
+ if (b->index >= q->num_buffers) {
+ dprintk(1, "querybuf: buffer index out of range\n");
+ return -EINVAL;
+ }
+ vb = q->bufs[b->index];
+
+ return __fill_v4l2_buffer(vb, b);
+}
+EXPORT_SYMBOL(vb2_querybuf);
+
+/**
+ * __verify_userptr_ops() - verify that all memory operations required for
+ * USERPTR queue type have been provided
+ */
+static int __verify_userptr_ops(struct vb2_queue *q)
+{
+ if (!(q->io_modes & VB2_USERPTR) || !q->mem_ops->get_userptr ||
+ !q->mem_ops->put_userptr)
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * __verify_mmap_ops() - verify that all memory operations required for
+ * MMAP queue type have been provided
+ */
+static int __verify_mmap_ops(struct vb2_queue *q)
+{
+ if (!(q->io_modes & VB2_MMAP) || !q->mem_ops->alloc ||
+ !q->mem_ops->put || !q->mem_ops->mmap)
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * vb2_reqbufs() - Initiate streaming
+ * @q: videobuf2 queue
+ * @req: struct passed from userspace to vidioc_reqbufs handler in driver
+ *
+ * Should be called from vidioc_reqbufs ioctl handler of a driver.
+ * This function:
+ * 1) verifies streaming parameters passed from the userspace,
+ * 2) sets up the queue,
+ * 3) negotiates number of buffers and planes per buffer with the driver
+ * to be used during streaming,
+ * 4) allocates internal buffer structures (struct vb2_buffer), according to
+ * the agreed parameters,
+ * 5) for MMAP memory type, allocates actual video memory, using the
+ * memory handling/allocation routines provided during queue initialization
+ *
+ * If req->count is 0, all the memory will be freed instead.
+ * If the queue has been allocated previously (by a previous vb2_reqbufs) call
+ * and the queue is not busy, memory will be reallocated.
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_reqbufs handler in driver.
+ */
+int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
+{
+ unsigned int num_buffers, allocated_buffers, num_planes = 0;
+ int ret = 0;
+
+ if (q->fileio) {
+ dprintk(1, "reqbufs: file io in progress\n");
+ return -EBUSY;
+ }
+
+ if (req->memory != V4L2_MEMORY_MMAP
+ && req->memory != V4L2_MEMORY_USERPTR) {
+ dprintk(1, "reqbufs: unsupported memory type\n");
+ return -EINVAL;
+ }
+
+ if (req->type != q->type) {
+ dprintk(1, "reqbufs: requested type is incorrect\n");
+ return -EINVAL;
+ }
+
+ if (q->streaming) {
+ dprintk(1, "reqbufs: streaming active\n");
+ return -EBUSY;
+ }
+
+ /*
+ * Make sure all the required memory ops for given memory type
+ * are available.
+ */
+ if (req->memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) {
+ dprintk(1, "reqbufs: MMAP for current setup unsupported\n");
+ return -EINVAL;
+ }
+
+ if (req->memory == V4L2_MEMORY_USERPTR && __verify_userptr_ops(q)) {
+ dprintk(1, "reqbufs: USERPTR for current setup unsupported\n");
+ return -EINVAL;
+ }
+
+ if (req->count == 0 || q->num_buffers != 0 || q->memory != req->memory) {
+ /*
+ * We already have buffers allocated, so first check if they
+ * are not in use and can be freed.
+ */
+ if (q->memory == V4L2_MEMORY_MMAP && __buffers_in_use(q)) {
+ dprintk(1, "reqbufs: memory in use, cannot free\n");
+ return -EBUSY;
+ }
+
+ __vb2_queue_free(q, q->num_buffers);
+
+ /*
+ * In case of REQBUFS(0) return immediately without calling
+ * driver's queue_setup() callback and allocating resources.
+ */
+ if (req->count == 0)
+ return 0;
+ }
+
+ /*
+ * Make sure the requested values and current defaults are sane.
+ */
+ num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME);
+ memset(q->plane_sizes, 0, sizeof(q->plane_sizes));
+ memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
+ q->memory = req->memory;
+
+ /*
+ * Ask the driver how many buffers and planes per buffer it requires.
+ * Driver also sets the size and allocator context for each plane.
+ */
+ ret = call_qop(q, queue_setup, q, NULL, &num_buffers, &num_planes,
+ q->plane_sizes, q->alloc_ctx);
+ if (ret)
+ return ret;
+
+ /* Finally, allocate buffers and video memory */
+ ret = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes);
+ if (ret == 0) {
+ dprintk(1, "Memory allocation failed\n");
+ return -ENOMEM;
+ }
+
+ allocated_buffers = ret;
+
+ /*
+ * Check if driver can handle the allocated number of buffers.
+ */
+ if (allocated_buffers < num_buffers) {
+ num_buffers = allocated_buffers;
+
+ ret = call_qop(q, queue_setup, q, NULL, &num_buffers,
+ &num_planes, q->plane_sizes, q->alloc_ctx);
+
+ if (!ret && allocated_buffers < num_buffers)
+ ret = -ENOMEM;
+
+ /*
+ * Either the driver has accepted a smaller number of buffers,
+ * or .queue_setup() returned an error
+ */
+ }
+
+ q->num_buffers = allocated_buffers;
+
+ if (ret < 0) {
+ __vb2_queue_free(q, allocated_buffers);
+ return ret;
+ }
+
+ /*
+ * Return the number of successfully allocated buffers
+ * to the userspace.
+ */
+ req->count = allocated_buffers;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_reqbufs);
+
+/**
+ * vb2_create_bufs() - Allocate buffers and any required auxiliary structs
+ * @q: videobuf2 queue
+ * @create: creation parameters, passed from userspace to vidioc_create_bufs
+ * handler in driver
+ *
+ * Should be called from vidioc_create_bufs ioctl handler of a driver.
+ * This function:
+ * 1) verifies parameter sanity
+ * 2) calls the .queue_setup() queue operation
+ * 3) performs any necessary memory allocations
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_create_bufs handler in driver.
+ */
+int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
+{
+ unsigned int num_planes = 0, num_buffers, allocated_buffers;
+ int ret = 0;
+
+ if (q->fileio) {
+ dprintk(1, "%s(): file io in progress\n", __func__);
+ return -EBUSY;
+ }
+
+ if (create->memory != V4L2_MEMORY_MMAP
+ && create->memory != V4L2_MEMORY_USERPTR) {
+ dprintk(1, "%s(): unsupported memory type\n", __func__);
+ return -EINVAL;
+ }
+
+ if (create->format.type != q->type) {
+ dprintk(1, "%s(): requested type is incorrect\n", __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * Make sure all the required memory ops for given memory type
+ * are available.
+ */
+ if (create->memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) {
+ dprintk(1, "%s(): MMAP for current setup unsupported\n", __func__);
+ return -EINVAL;
+ }
+
+ if (create->memory == V4L2_MEMORY_USERPTR && __verify_userptr_ops(q)) {
+ dprintk(1, "%s(): USERPTR for current setup unsupported\n", __func__);
+ return -EINVAL;
+ }
+
+ if (q->num_buffers == VIDEO_MAX_FRAME) {
+ dprintk(1, "%s(): maximum number of buffers already allocated\n",
+ __func__);
+ return -ENOBUFS;
+ }
+
+ create->index = q->num_buffers;
+
+ if (!q->num_buffers) {
+ memset(q->plane_sizes, 0, sizeof(q->plane_sizes));
+ memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
+ q->memory = create->memory;
+ }
+
+ num_buffers = min(create->count, VIDEO_MAX_FRAME - q->num_buffers);
+
+ /*
+ * Ask the driver, whether the requested number of buffers, planes per
+ * buffer and their sizes are acceptable
+ */
+ ret = call_qop(q, queue_setup, q, &create->format, &num_buffers,
+ &num_planes, q->plane_sizes, q->alloc_ctx);
+ if (ret)
+ return ret;
+
+ /* Finally, allocate buffers and video memory */
+ ret = __vb2_queue_alloc(q, create->memory, num_buffers,
+ num_planes);
+ if (ret < 0) {
+ dprintk(1, "Memory allocation failed with error: %d\n", ret);
+ return ret;
+ }
+
+ allocated_buffers = ret;
+
+ /*
+ * Check if driver can handle the so far allocated number of buffers.
+ */
+ if (ret < num_buffers) {
+ num_buffers = ret;
+
+ /*
+ * q->num_buffers contains the total number of buffers, that the
+ * queue driver has set up
+ */
+ ret = call_qop(q, queue_setup, q, &create->format, &num_buffers,
+ &num_planes, q->plane_sizes, q->alloc_ctx);
+
+ if (!ret && allocated_buffers < num_buffers)
+ ret = -ENOMEM;
+
+ /*
+ * Either the driver has accepted a smaller number of buffers,
+ * or .queue_setup() returned an error
+ */
+ }
+
+ q->num_buffers += allocated_buffers;
+
+ if (ret < 0) {
+ __vb2_queue_free(q, allocated_buffers);
+ return ret;
+ }
+
+ /*
+ * Return the number of successfully allocated buffers
+ * to the userspace.
+ */
+ create->count = allocated_buffers;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_create_bufs);
+
+/**
+ * vb2_plane_vaddr() - Return a kernel virtual address of a given plane
+ * @vb: vb2_buffer to which the plane in question belongs to
+ * @plane_no: plane number for which the address is to be returned
+ *
+ * This function returns a kernel virtual address of a given plane if
+ * such a mapping exist, NULL otherwise.
+ */
+void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+
+ if (plane_no > vb->num_planes || !vb->planes[plane_no].mem_priv)
+ return NULL;
+
+ return call_memop(q, vaddr, vb->planes[plane_no].mem_priv);
+
+}
+EXPORT_SYMBOL_GPL(vb2_plane_vaddr);
+
+/**
+ * vb2_plane_cookie() - Return allocator specific cookie for the given plane
+ * @vb: vb2_buffer to which the plane in question belongs to
+ * @plane_no: plane number for which the cookie is to be returned
+ *
+ * This function returns an allocator specific cookie for a given plane if
+ * available, NULL otherwise. The allocator should provide some simple static
+ * inline function, which would convert this cookie to the allocator specific
+ * type that can be used directly by the driver to access the buffer. This can
+ * be for example physical address, pointer to scatter list or IOMMU mapping.
+ */
+void *vb2_plane_cookie(struct vb2_buffer *vb, unsigned int plane_no)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+
+ if (plane_no > vb->num_planes || !vb->planes[plane_no].mem_priv)
+ return NULL;
+
+ return call_memop(q, cookie, vb->planes[plane_no].mem_priv);
+}
+EXPORT_SYMBOL_GPL(vb2_plane_cookie);
+
+/**
+ * vb2_buffer_done() - inform videobuf that an operation on a buffer is finished
+ * @vb: vb2_buffer returned from the driver
+ * @state: either VB2_BUF_STATE_DONE if the operation finished successfully
+ * or VB2_BUF_STATE_ERROR if the operation finished with an error
+ *
+ * This function should be called by the driver after a hardware operation on
+ * a buffer is finished and the buffer may be returned to userspace. The driver
+ * cannot use this buffer anymore until it is queued back to it by videobuf
+ * by the means of buf_queue callback. Only buffers previously queued to the
+ * driver by buf_queue can be passed to this function.
+ */
+void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+ unsigned long flags;
+
+ if (vb->state != VB2_BUF_STATE_ACTIVE)
+ return;
+
+ if (state != VB2_BUF_STATE_DONE && state != VB2_BUF_STATE_ERROR)
+ return;
+
+ dprintk(4, "Done processing on buffer %d, state: %d\n",
+ vb->v4l2_buf.index, vb->state);
+
+ /* Add the buffer to the done buffers list */
+ spin_lock_irqsave(&q->done_lock, flags);
+ vb->state = state;
+ list_add_tail(&vb->done_entry, &q->done_list);
+ atomic_dec(&q->queued_count);
+ spin_unlock_irqrestore(&q->done_lock, flags);
+
+ /* Inform any processes that may be waiting for buffers */
+ wake_up(&q->done_wq);
+}
+EXPORT_SYMBOL_GPL(vb2_buffer_done);
+
+/**
+ * __fill_vb2_buffer() - fill a vb2_buffer with information provided in
+ * a v4l2_buffer by the userspace
+ */
+static int __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b,
+ struct v4l2_plane *v4l2_planes)
+{
+ unsigned int plane;
+ int ret;
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
+ /*
+ * Verify that the userspace gave us a valid array for
+ * plane information.
+ */
+ ret = __verify_planes_array(vb, b);
+ if (ret)
+ return ret;
+
+ /* Fill in driver-provided information for OUTPUT types */
+ if (V4L2_TYPE_IS_OUTPUT(b->type)) {
+ /*
+ * Will have to go up to b->length when API starts
+ * accepting variable number of planes.
+ */
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ v4l2_planes[plane].bytesused =
+ b->m.planes[plane].bytesused;
+ v4l2_planes[plane].data_offset =
+ b->m.planes[plane].data_offset;
+ }
+ }
+
+ if (b->memory == V4L2_MEMORY_USERPTR) {
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ v4l2_planes[plane].m.userptr =
+ b->m.planes[plane].m.userptr;
+ v4l2_planes[plane].length =
+ b->m.planes[plane].length;
+ }
+ }
+ } else {
+ /*
+ * Single-planar buffers do not use planes array,
+ * so fill in relevant v4l2_buffer struct fields instead.
+ * In videobuf we use our internal V4l2_planes struct for
+ * single-planar buffers as well, for simplicity.
+ */
+ if (V4L2_TYPE_IS_OUTPUT(b->type))
+ v4l2_planes[0].bytesused = b->bytesused;
+
+ if (b->memory == V4L2_MEMORY_USERPTR) {
+ v4l2_planes[0].m.userptr = b->m.userptr;
+ v4l2_planes[0].length = b->length;
+ }
+ }
+
+ vb->v4l2_buf.field = b->field;
+ vb->v4l2_buf.timestamp = b->timestamp;
+ vb->v4l2_buf.input = b->input;
+ vb->v4l2_buf.flags = b->flags & ~V4L2_BUFFER_STATE_FLAGS;
+
+ return 0;
+}
+
+/**
+ * __qbuf_userptr() - handle qbuf of a USERPTR buffer
+ */
+static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
+{
+ struct v4l2_plane planes[VIDEO_MAX_PLANES];
+ struct vb2_queue *q = vb->vb2_queue;
+ void *mem_priv;
+ unsigned int plane;
+ int ret;
+ int write = !V4L2_TYPE_IS_OUTPUT(q->type);
+
+ /* Verify and copy relevant information provided by the userspace */
+ ret = __fill_vb2_buffer(vb, b, planes);
+ if (ret)
+ return ret;
+
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ /* Skip the plane if already verified */
+ if (vb->v4l2_planes[plane].m.userptr &&
+ vb->v4l2_planes[plane].m.userptr == planes[plane].m.userptr
+ && vb->v4l2_planes[plane].length == planes[plane].length)
+ continue;
+
+ dprintk(3, "qbuf: userspace address for plane %d changed, "
+ "reacquiring memory\n", plane);
+
+ /* Check if the provided plane buffer is large enough */
+ if (planes[plane].length < q->plane_sizes[plane]) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Release previously acquired memory if present */
+ if (vb->planes[plane].mem_priv)
+ call_memop(q, put_userptr, vb->planes[plane].mem_priv);
+
+ vb->planes[plane].mem_priv = NULL;
+ vb->v4l2_planes[plane].m.userptr = 0;
+ vb->v4l2_planes[plane].length = 0;
+
+ /* Acquire each plane's memory */
+ mem_priv = call_memop(q, get_userptr, q->alloc_ctx[plane],
+ planes[plane].m.userptr,
+ planes[plane].length, write);
+ if (IS_ERR_OR_NULL(mem_priv)) {
+ dprintk(1, "qbuf: failed acquiring userspace "
+ "memory for plane %d\n", plane);
+ ret = mem_priv ? PTR_ERR(mem_priv) : -EINVAL;
+ goto err;
+ }
+ vb->planes[plane].mem_priv = mem_priv;
+ }
+
+ /*
+ * Call driver-specific initialization on the newly acquired buffer,
+ * if provided.
+ */
+ ret = call_qop(q, buf_init, vb);
+ if (ret) {
+ dprintk(1, "qbuf: buffer initialization failed\n");
+ goto err;
+ }
+
+ /*
+ * Now that everything is in order, copy relevant information
+ * provided by userspace.
+ */
+ for (plane = 0; plane < vb->num_planes; ++plane)
+ vb->v4l2_planes[plane] = planes[plane];
+
+ return 0;
+err:
+ /* In case of errors, release planes that were already acquired */
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ if (vb->planes[plane].mem_priv)
+ call_memop(q, put_userptr, vb->planes[plane].mem_priv);
+ vb->planes[plane].mem_priv = NULL;
+ vb->v4l2_planes[plane].m.userptr = 0;
+ vb->v4l2_planes[plane].length = 0;
+ }
+
+ return ret;
+}
+
+/**
+ * __qbuf_mmap() - handle qbuf of an MMAP buffer
+ */
+static int __qbuf_mmap(struct vb2_buffer *vb, const struct v4l2_buffer *b)
+{
+ return __fill_vb2_buffer(vb, b, vb->v4l2_planes);
+}
+
+/**
+ * __enqueue_in_driver() - enqueue a vb2_buffer in driver for processing
+ */
+static void __enqueue_in_driver(struct vb2_buffer *vb)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+
+ vb->state = VB2_BUF_STATE_ACTIVE;
+ atomic_inc(&q->queued_count);
+ q->ops->buf_queue(vb);
+}
+
+static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+ int ret;
+
+ switch (q->memory) {
+ case V4L2_MEMORY_MMAP:
+ ret = __qbuf_mmap(vb, b);
+ break;
+ case V4L2_MEMORY_USERPTR:
+ ret = __qbuf_userptr(vb, b);
+ break;
+ default:
+ WARN(1, "Invalid queue type\n");
+ ret = -EINVAL;
+ }
+
+ if (!ret)
+ ret = call_qop(q, buf_prepare, vb);
+ if (ret)
+ dprintk(1, "qbuf: buffer preparation failed: %d\n", ret);
+ else
+ vb->state = VB2_BUF_STATE_PREPARED;
+
+ return ret;
+}
+
+/**
+ * vb2_prepare_buf() - Pass ownership of a buffer from userspace to the kernel
+ * @q: videobuf2 queue
+ * @b: buffer structure passed from userspace to vidioc_prepare_buf
+ * handler in driver
+ *
+ * Should be called from vidioc_prepare_buf ioctl handler of a driver.
+ * This function:
+ * 1) verifies the passed buffer,
+ * 2) calls buf_prepare callback in the driver (if provided), in which
+ * driver-specific buffer initialization can be performed,
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_prepare_buf handler in driver.
+ */
+int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b)
+{
+ struct vb2_buffer *vb;
+ int ret;
+
+ if (q->fileio) {
+ dprintk(1, "%s(): file io in progress\n", __func__);
+ return -EBUSY;
+ }
+
+ if (b->type != q->type) {
+ dprintk(1, "%s(): invalid buffer type\n", __func__);
+ return -EINVAL;
+ }
+
+ if (b->index >= q->num_buffers) {
+ dprintk(1, "%s(): buffer index out of range\n", __func__);
+ return -EINVAL;
+ }
+
+ vb = q->bufs[b->index];
+ if (NULL == vb) {
+ /* Should never happen */
+ dprintk(1, "%s(): buffer is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (b->memory != q->memory) {
+ dprintk(1, "%s(): invalid memory type\n", __func__);
+ return -EINVAL;
+ }
+
+ if (vb->state != VB2_BUF_STATE_DEQUEUED) {
+ dprintk(1, "%s(): invalid buffer state %d\n", __func__, vb->state);
+ return -EINVAL;
+ }
+
+ ret = __buf_prepare(vb, b);
+ if (ret < 0)
+ return ret;
+
+ __fill_v4l2_buffer(vb, b);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_prepare_buf);
+
+/**
+ * vb2_qbuf() - Queue a buffer from userspace
+ * @q: videobuf2 queue
+ * @b: buffer structure passed from userspace to vidioc_qbuf handler
+ * in driver
+ *
+ * Should be called from vidioc_qbuf ioctl handler of a driver.
+ * This function:
+ * 1) verifies the passed buffer,
+ * 2) if necessary, calls buf_prepare callback in the driver (if provided), in
+ * which driver-specific buffer initialization can be performed,
+ * 3) if streaming is on, queues the buffer in driver by the means of buf_queue
+ * callback for processing.
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_qbuf handler in driver.
+ */
+int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
+{
+ struct rw_semaphore *mmap_sem = NULL;
+ struct vb2_buffer *vb;
+ int ret = 0;
+
+ /*
+ * In case of user pointer buffers vb2 allocator needs to get direct
+ * access to userspace pages. This requires getting read access on
+ * mmap semaphore in the current process structure. The same
+ * semaphore is taken before calling mmap operation, while both mmap
+ * and qbuf are called by the driver or v4l2 core with driver's lock
+ * held. To avoid a AB-BA deadlock (mmap_sem then driver's lock in
+ * mmap and driver's lock then mmap_sem in qbuf) the videobuf2 core
+ * release driver's lock, takes mmap_sem and then takes again driver's
+ * lock.
+ *
+ * To avoid race with other vb2 calls, which might be called after
+ * releasing driver's lock, this operation is performed at the
+ * beggining of qbuf processing. This way the queue status is
+ * consistent after getting driver's lock back.
+ */
+ if (q->memory == V4L2_MEMORY_USERPTR) {
+ mmap_sem = &current->mm->mmap_sem;
+ call_qop(q, wait_prepare, q);
+ down_read(mmap_sem);
+ call_qop(q, wait_finish, q);
+ }
+
+ if (q->fileio) {
+ dprintk(1, "qbuf: file io in progress\n");
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ if (b->type != q->type) {
+ dprintk(1, "qbuf: invalid buffer type\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ if (b->index >= q->num_buffers) {
+ dprintk(1, "qbuf: buffer index out of range\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ vb = q->bufs[b->index];
+ if (NULL == vb) {
+ /* Should never happen */
+ dprintk(1, "qbuf: buffer is NULL\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ if (b->memory != q->memory) {
+ dprintk(1, "qbuf: invalid memory type\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ switch (vb->state) {
+ case VB2_BUF_STATE_DEQUEUED:
+ ret = __buf_prepare(vb, b);
+ if (ret)
+ goto unlock;
+ case VB2_BUF_STATE_PREPARED:
+ break;
+ default:
+ dprintk(1, "qbuf: buffer already in use\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ /*
+ * Add to the queued buffers list, a buffer will stay on it until
+ * dequeued in dqbuf.
+ */
+ list_add_tail(&vb->queued_entry, &q->queued_list);
+ vb->state = VB2_BUF_STATE_QUEUED;
+
+ /*
+ * If already streaming, give the buffer to driver for processing.
+ * If not, the buffer will be given to driver on next streamon.
+ */
+ if (q->streaming)
+ __enqueue_in_driver(vb);
+
+ /* Fill buffer information for the userspace */
+ __fill_v4l2_buffer(vb, b);
+
+ dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index);
+unlock:
+ if (mmap_sem)
+ up_read(mmap_sem);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(vb2_qbuf);
+
+/**
+ * __vb2_wait_for_done_vb() - wait for a buffer to become available
+ * for dequeuing
+ *
+ * Will sleep if required for nonblocking == false.
+ */
+static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking)
+{
+ /*
+ * All operations on vb_done_list are performed under done_lock
+ * spinlock protection. However, buffers may be removed from
+ * it and returned to userspace only while holding both driver's
+ * lock and the done_lock spinlock. Thus we can be sure that as
+ * long as we hold the driver's lock, the list will remain not
+ * empty if list_empty() check succeeds.
+ */
+
+ for (;;) {
+ int ret;
+
+ if (!q->streaming) {
+ dprintk(1, "Streaming off, will not wait for buffers\n");
+ return -EINVAL;
+ }
+
+ if (!list_empty(&q->done_list)) {
+ /*
+ * Found a buffer that we were waiting for.
+ */
+ break;
+ }
+
+ if (nonblocking) {
+ dprintk(1, "Nonblocking and no buffers to dequeue, "
+ "will not wait\n");
+ return -EAGAIN;
+ }
+
+ /*
+ * We are streaming and blocking, wait for another buffer to
+ * become ready or for streamoff. Driver's lock is released to
+ * allow streamoff or qbuf to be called while waiting.
+ */
+ call_qop(q, wait_prepare, q);
+
+ /*
+ * All locks have been released, it is safe to sleep now.
+ */
+ dprintk(3, "Will sleep waiting for buffers\n");
+ ret = wait_event_interruptible(q->done_wq,
+ !list_empty(&q->done_list) || !q->streaming);
+
+ /*
+ * We need to reevaluate both conditions again after reacquiring
+ * the locks or return an error if one occurred.
+ */
+ call_qop(q, wait_finish, q);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+/**
+ * __vb2_get_done_vb() - get a buffer ready for dequeuing
+ *
+ * Will sleep if required for nonblocking == false.
+ */
+static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb,
+ int nonblocking)
+{
+ unsigned long flags;
+ int ret;
+
+ /*
+ * Wait for at least one buffer to become available on the done_list.
+ */
+ ret = __vb2_wait_for_done_vb(q, nonblocking);
+ if (ret)
+ return ret;
+
+ /*
+ * Driver's lock has been held since we last verified that done_list
+ * is not empty, so no need for another list_empty(done_list) check.
+ */
+ spin_lock_irqsave(&q->done_lock, flags);
+ *vb = list_first_entry(&q->done_list, struct vb2_buffer, done_entry);
+ list_del(&(*vb)->done_entry);
+ spin_unlock_irqrestore(&q->done_lock, flags);
+
+ return 0;
+}
+
+/**
+ * vb2_wait_for_all_buffers() - wait until all buffers are given back to vb2
+ * @q: videobuf2 queue
+ *
+ * This function will wait until all buffers that have been given to the driver
+ * by buf_queue() are given back to vb2 with vb2_buffer_done(). It doesn't call
+ * wait_prepare, wait_finish pair. It is intended to be called with all locks
+ * taken, for example from stop_streaming() callback.
+ */
+int vb2_wait_for_all_buffers(struct vb2_queue *q)
+{
+ if (!q->streaming) {
+ dprintk(1, "Streaming off, will not wait for buffers\n");
+ return -EINVAL;
+ }
+
+ wait_event(q->done_wq, !atomic_read(&q->queued_count));
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_wait_for_all_buffers);
+
+/**
+ * vb2_dqbuf() - Dequeue a buffer to the userspace
+ * @q: videobuf2 queue
+ * @b: buffer structure passed from userspace to vidioc_dqbuf handler
+ * in driver
+ * @nonblocking: if true, this call will not sleep waiting for a buffer if no
+ * buffers ready for dequeuing are present. Normally the driver
+ * would be passing (file->f_flags & O_NONBLOCK) here
+ *
+ * Should be called from vidioc_dqbuf ioctl handler of a driver.
+ * This function:
+ * 1) verifies the passed buffer,
+ * 2) calls buf_finish callback in the driver (if provided), in which
+ * driver can perform any additional operations that may be required before
+ * returning the buffer to userspace, such as cache sync,
+ * 3) the buffer struct members are filled with relevant information for
+ * the userspace.
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_dqbuf handler in driver.
+ */
+int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)
+{
+ struct vb2_buffer *vb = NULL;
+ int ret;
+
+ if (q->fileio) {
+ dprintk(1, "dqbuf: file io in progress\n");
+ return -EBUSY;
+ }
+
+ if (b->type != q->type) {
+ dprintk(1, "dqbuf: invalid buffer type\n");
+ return -EINVAL;
+ }
+
+ ret = __vb2_get_done_vb(q, &vb, nonblocking);
+ if (ret < 0) {
+ dprintk(1, "dqbuf: error getting next done buffer\n");
+ return ret;
+ }
+
+ ret = call_qop(q, buf_finish, vb);
+ if (ret) {
+ dprintk(1, "dqbuf: buffer finish failed\n");
+ return ret;
+ }
+
+ switch (vb->state) {
+ case VB2_BUF_STATE_DONE:
+ dprintk(3, "dqbuf: Returning done buffer\n");
+ break;
+ case VB2_BUF_STATE_ERROR:
+ dprintk(3, "dqbuf: Returning done buffer with errors\n");
+ break;
+ default:
+ dprintk(1, "dqbuf: Invalid buffer state\n");
+ return -EINVAL;
+ }
+
+ /* Fill buffer information for the userspace */
+ __fill_v4l2_buffer(vb, b);
+ /* Remove from videobuf queue */
+ list_del(&vb->queued_entry);
+
+ dprintk(1, "dqbuf of buffer %d, with state %d\n",
+ vb->v4l2_buf.index, vb->state);
+
+ vb->state = VB2_BUF_STATE_DEQUEUED;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_dqbuf);
+
+/**
+ * __vb2_queue_cancel() - cancel and stop (pause) streaming
+ *
+ * Removes all queued buffers from driver's queue and all buffers queued by
+ * userspace from videobuf's queue. Returns to state after reqbufs.
+ */
+static void __vb2_queue_cancel(struct vb2_queue *q)
+{
+ unsigned int i;
+
+ /*
+ * Tell driver to stop all transactions and release all queued
+ * buffers.
+ */
+ if (q->streaming)
+ call_qop(q, stop_streaming, q);
+ q->streaming = 0;
+
+ /*
+ * Remove all buffers from videobuf's list...
+ */
+ INIT_LIST_HEAD(&q->queued_list);
+ /*
+ * ...and done list; userspace will not receive any buffers it
+ * has not already dequeued before initiating cancel.
+ */
+ INIT_LIST_HEAD(&q->done_list);
+ atomic_set(&q->queued_count, 0);
+ wake_up_all(&q->done_wq);
+
+ /*
+ * Reinitialize all buffers for next use.
+ */
+ for (i = 0; i < q->num_buffers; ++i)
+ q->bufs[i]->state = VB2_BUF_STATE_DEQUEUED;
+}
+
+/**
+ * vb2_streamon - start streaming
+ * @q: videobuf2 queue
+ * @type: type argument passed from userspace to vidioc_streamon handler
+ *
+ * Should be called from vidioc_streamon handler of a driver.
+ * This function:
+ * 1) verifies current state
+ * 2) passes any previously queued buffers to the driver and starts streaming
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_streamon handler in the driver.
+ */
+int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
+{
+ struct vb2_buffer *vb;
+ int ret;
+
+ if (q->fileio) {
+ dprintk(1, "streamon: file io in progress\n");
+ return -EBUSY;
+ }
+
+ if (type != q->type) {
+ dprintk(1, "streamon: invalid stream type\n");
+ return -EINVAL;
+ }
+
+ if (q->streaming) {
+ dprintk(1, "streamon: already streaming\n");
+ return -EBUSY;
+ }
+
+ /*
+ * If any buffers were queued before streamon,
+ * we can now pass them to driver for processing.
+ */
+ list_for_each_entry(vb, &q->queued_list, queued_entry)
+ __enqueue_in_driver(vb);
+
+ /*
+ * Let driver notice that streaming state has been enabled.
+ */
+ ret = call_qop(q, start_streaming, q, atomic_read(&q->queued_count));
+ if (ret) {
+ dprintk(1, "streamon: driver refused to start streaming\n");
+ __vb2_queue_cancel(q);
+ return ret;
+ }
+
+ q->streaming = 1;
+
+ dprintk(3, "Streamon successful\n");
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_streamon);
+
+
+/**
+ * vb2_streamoff - stop streaming
+ * @q: videobuf2 queue
+ * @type: type argument passed from userspace to vidioc_streamoff handler
+ *
+ * Should be called from vidioc_streamoff handler of a driver.
+ * This function:
+ * 1) verifies current state,
+ * 2) stop streaming and dequeues any queued buffers, including those previously
+ * passed to the driver (after waiting for the driver to finish).
+ *
+ * This call can be used for pausing playback.
+ * The return values from this function are intended to be directly returned
+ * from vidioc_streamoff handler in the driver
+ */
+int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type)
+{
+ if (q->fileio) {
+ dprintk(1, "streamoff: file io in progress\n");
+ return -EBUSY;
+ }
+
+ if (type != q->type) {
+ dprintk(1, "streamoff: invalid stream type\n");
+ return -EINVAL;
+ }
+
+ if (!q->streaming) {
+ dprintk(1, "streamoff: not streaming\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Cancel will pause streaming and remove all buffers from the driver
+ * and videobuf, effectively returning control over them to userspace.
+ */
+ __vb2_queue_cancel(q);
+
+ dprintk(3, "Streamoff successful\n");
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_streamoff);
+
+/**
+ * __find_plane_by_offset() - find plane associated with the given offset off
+ */
+static int __find_plane_by_offset(struct vb2_queue *q, unsigned long off,
+ unsigned int *_buffer, unsigned int *_plane)
+{
+ struct vb2_buffer *vb;
+ unsigned int buffer, plane;
+
+ /*
+ * Go over all buffers and their planes, comparing the given offset
+ * with an offset assigned to each plane. If a match is found,
+ * return its buffer and plane numbers.
+ */
+ for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+ vb = q->bufs[buffer];
+
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ if (vb->v4l2_planes[plane].m.mem_offset == off) {
+ *_buffer = buffer;
+ *_plane = plane;
+ return 0;
+ }
+ }
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * vb2_mmap() - map video buffers into application address space
+ * @q: videobuf2 queue
+ * @vma: vma passed to the mmap file operation handler in the driver
+ *
+ * Should be called from mmap file operation handler of a driver.
+ * This function maps one plane of one of the available video buffers to
+ * userspace. To map whole video memory allocated on reqbufs, this function
+ * has to be called once per each plane per each buffer previously allocated.
+ *
+ * When the userspace application calls mmap, it passes to it an offset returned
+ * to it earlier by the means of vidioc_querybuf handler. That offset acts as
+ * a "cookie", which is then used to identify the plane to be mapped.
+ * This function finds a plane with a matching offset and a mapping is performed
+ * by the means of a provided memory operation.
+ *
+ * The return values from this function are intended to be directly returned
+ * from the mmap handler in driver.
+ */
+int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma)
+{
+ unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
+ struct vb2_buffer *vb;
+ unsigned int buffer, plane;
+ int ret;
+
+ if (q->memory != V4L2_MEMORY_MMAP) {
+ dprintk(1, "Queue is not currently set up for mmap\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Check memory area access mode.
+ */
+ if (!(vma->vm_flags & VM_SHARED)) {
+ dprintk(1, "Invalid vma flags, VM_SHARED needed\n");
+ return -EINVAL;
+ }
+ if (V4L2_TYPE_IS_OUTPUT(q->type)) {
+ if (!(vma->vm_flags & VM_WRITE)) {
+ dprintk(1, "Invalid vma flags, VM_WRITE needed\n");
+ return -EINVAL;
+ }
+ } else {
+ if (!(vma->vm_flags & VM_READ)) {
+ dprintk(1, "Invalid vma flags, VM_READ needed\n");
+ return -EINVAL;
+ }
+ }
+
+ /*
+ * Find the plane corresponding to the offset passed by userspace.
+ */
+ ret = __find_plane_by_offset(q, off, &buffer, &plane);
+ if (ret)
+ return ret;
+
+ vb = q->bufs[buffer];
+
+ ret = call_memop(q, mmap, vb->planes[plane].mem_priv, vma);
+ if (ret)
+ return ret;
+
+ dprintk(3, "Buffer %d, plane %d successfully mapped\n", buffer, plane);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_mmap);
+
+#ifndef CONFIG_MMU
+unsigned long vb2_get_unmapped_area(struct vb2_queue *q,
+ unsigned long addr,
+ unsigned long len,
+ unsigned long pgoff,
+ unsigned long flags)
+{
+ unsigned long off = pgoff << PAGE_SHIFT;
+ struct vb2_buffer *vb;
+ unsigned int buffer, plane;
+ int ret;
+
+ if (q->memory != V4L2_MEMORY_MMAP) {
+ dprintk(1, "Queue is not currently set up for mmap\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Find the plane corresponding to the offset passed by userspace.
+ */
+ ret = __find_plane_by_offset(q, off, &buffer, &plane);
+ if (ret)
+ return ret;
+
+ vb = q->bufs[buffer];
+
+ return (unsigned long)vb2_plane_vaddr(vb, plane);
+}
+EXPORT_SYMBOL_GPL(vb2_get_unmapped_area);
+#endif
+
+static int __vb2_init_fileio(struct vb2_queue *q, int read);
+static int __vb2_cleanup_fileio(struct vb2_queue *q);
+
+/**
+ * vb2_poll() - implements poll userspace operation
+ * @q: videobuf2 queue
+ * @file: file argument passed to the poll file operation handler
+ * @wait: wait argument passed to the poll file operation handler
+ *
+ * This function implements poll file operation handler for a driver.
+ * For CAPTURE queues, if a buffer is ready to be dequeued, the userspace will
+ * be informed that the file descriptor of a video device is available for
+ * reading.
+ * For OUTPUT queues, if a buffer is ready to be dequeued, the file descriptor
+ * will be reported as available for writing.
+ *
+ * The return values from this function are intended to be directly returned
+ * from poll handler in driver.
+ */
+unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait)
+{
+ unsigned long flags;
+ unsigned int ret;
+ struct vb2_buffer *vb = NULL;
+
+ /*
+ * Start file I/O emulator only if streaming API has not been used yet.
+ */
+ if (q->num_buffers == 0 && q->fileio == NULL) {
+ if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ)) {
+ ret = __vb2_init_fileio(q, 1);
+ if (ret)
+ return POLLERR;
+ }
+ if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE)) {
+ ret = __vb2_init_fileio(q, 0);
+ if (ret)
+ return POLLERR;
+ /*
+ * Write to OUTPUT queue can be done immediately.
+ */
+ return POLLOUT | POLLWRNORM;
+ }
+ }
+
+ /*
+ * There is nothing to wait for if no buffers have already been queued.
+ */
+ if (list_empty(&q->queued_list))
+ return POLLERR;
+
+ poll_wait(file, &q->done_wq, wait);
+
+ /*
+ * Take first buffer available for dequeuing.
+ */
+ spin_lock_irqsave(&q->done_lock, flags);
+ if (!list_empty(&q->done_list))
+ vb = list_first_entry(&q->done_list, struct vb2_buffer,
+ done_entry);
+ spin_unlock_irqrestore(&q->done_lock, flags);
+
+ if (vb && (vb->state == VB2_BUF_STATE_DONE
+ || vb->state == VB2_BUF_STATE_ERROR)) {
+ return (V4L2_TYPE_IS_OUTPUT(q->type)) ? POLLOUT | POLLWRNORM :
+ POLLIN | POLLRDNORM;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_poll);
+
+/**
+ * vb2_queue_init() - initialize a videobuf2 queue
+ * @q: videobuf2 queue; this structure should be allocated in driver
+ *
+ * The vb2_queue structure should be allocated by the driver. The driver is
+ * responsible of clearing it's content and setting initial values for some
+ * required entries before calling this function.
+ * q->ops, q->mem_ops, q->type and q->io_modes are mandatory. Please refer
+ * to the struct vb2_queue description in include/media/videobuf2-core.h
+ * for more information.
+ */
+int vb2_queue_init(struct vb2_queue *q)
+{
+ BUG_ON(!q);
+ BUG_ON(!q->ops);
+ BUG_ON(!q->mem_ops);
+ BUG_ON(!q->type);
+ BUG_ON(!q->io_modes);
+
+ BUG_ON(!q->ops->queue_setup);
+ BUG_ON(!q->ops->buf_queue);
+
+ INIT_LIST_HEAD(&q->queued_list);
+ INIT_LIST_HEAD(&q->done_list);
+ spin_lock_init(&q->done_lock);
+ init_waitqueue_head(&q->done_wq);
+
+ if (q->buf_struct_size == 0)
+ q->buf_struct_size = sizeof(struct vb2_buffer);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_queue_init);
+
+/**
+ * vb2_queue_release() - stop streaming, release the queue and free memory
+ * @q: videobuf2 queue
+ *
+ * This function stops streaming and performs necessary clean ups, including
+ * freeing video buffer memory. The driver is responsible for freeing
+ * the vb2_queue structure itself.
+ */
+void vb2_queue_release(struct vb2_queue *q)
+{
+ __vb2_cleanup_fileio(q);
+ __vb2_queue_cancel(q);
+ __vb2_queue_free(q, q->num_buffers);
+}
+EXPORT_SYMBOL_GPL(vb2_queue_release);
+
+/**
+ * struct vb2_fileio_buf - buffer context used by file io emulator
+ *
+ * vb2 provides a compatibility layer and emulator of file io (read and
+ * write) calls on top of streaming API. This structure is used for
+ * tracking context related to the buffers.
+ */
+struct vb2_fileio_buf {
+ void *vaddr;
+ unsigned int size;
+ unsigned int pos;
+ unsigned int queued:1;
+};
+
+/**
+ * struct vb2_fileio_data - queue context used by file io emulator
+ *
+ * vb2 provides a compatibility layer and emulator of file io (read and
+ * write) calls on top of streaming API. For proper operation it required
+ * this structure to save the driver state between each call of the read
+ * or write function.
+ */
+struct vb2_fileio_data {
+ struct v4l2_requestbuffers req;
+ struct v4l2_buffer b;
+ struct vb2_fileio_buf bufs[VIDEO_MAX_FRAME];
+ unsigned int index;
+ unsigned int q_count;
+ unsigned int dq_count;
+ unsigned int flags;
+};
+
+/**
+ * __vb2_init_fileio() - initialize file io emulator
+ * @q: videobuf2 queue
+ * @read: mode selector (1 means read, 0 means write)
+ */
+static int __vb2_init_fileio(struct vb2_queue *q, int read)
+{
+ struct vb2_fileio_data *fileio;
+ int i, ret;
+ unsigned int count = 0;
+
+ /*
+ * Sanity check
+ */
+ if ((read && !(q->io_modes & VB2_READ)) ||
+ (!read && !(q->io_modes & VB2_WRITE)))
+ BUG();
+
+ /*
+ * Check if device supports mapping buffers to kernel virtual space.
+ */
+ if (!q->mem_ops->vaddr)
+ return -EBUSY;
+
+ /*
+ * Check if streaming api has not been already activated.
+ */
+ if (q->streaming || q->num_buffers > 0)
+ return -EBUSY;
+
+ /*
+ * Start with count 1, driver can increase it in queue_setup()
+ */
+ count = 1;
+
+ dprintk(3, "setting up file io: mode %s, count %d, flags %08x\n",
+ (read) ? "read" : "write", count, q->io_flags);
+
+ fileio = kzalloc(sizeof(struct vb2_fileio_data), GFP_KERNEL);
+ if (fileio == NULL)
+ return -ENOMEM;
+
+ fileio->flags = q->io_flags;
+
+ /*
+ * Request buffers and use MMAP type to force driver
+ * to allocate buffers by itself.
+ */
+ fileio->req.count = count;
+ fileio->req.memory = V4L2_MEMORY_MMAP;
+ fileio->req.type = q->type;
+ ret = vb2_reqbufs(q, &fileio->req);
+ if (ret)
+ goto err_kfree;
+
+ /*
+ * Check if plane_count is correct
+ * (multiplane buffers are not supported).
+ */
+ if (q->bufs[0]->num_planes != 1) {
+ fileio->req.count = 0;
+ ret = -EBUSY;
+ goto err_reqbufs;
+ }
+
+ /*
+ * Get kernel address of each buffer.
+ */
+ for (i = 0; i < q->num_buffers; i++) {
+ fileio->bufs[i].vaddr = vb2_plane_vaddr(q->bufs[i], 0);
+ if (fileio->bufs[i].vaddr == NULL)
+ goto err_reqbufs;
+ fileio->bufs[i].size = vb2_plane_size(q->bufs[i], 0);
+ }
+
+ /*
+ * Read mode requires pre queuing of all buffers.
+ */
+ if (read) {
+ /*
+ * Queue all buffers.
+ */
+ for (i = 0; i < q->num_buffers; i++) {
+ struct v4l2_buffer *b = &fileio->b;
+ memset(b, 0, sizeof(*b));
+ b->type = q->type;
+ b->memory = q->memory;
+ b->index = i;
+ ret = vb2_qbuf(q, b);
+ if (ret)
+ goto err_reqbufs;
+ fileio->bufs[i].queued = 1;
+ }
+
+ /*
+ * Start streaming.
+ */
+ ret = vb2_streamon(q, q->type);
+ if (ret)
+ goto err_reqbufs;
+ }
+
+ q->fileio = fileio;
+
+ return ret;
+
+err_reqbufs:
+ vb2_reqbufs(q, &fileio->req);
+
+err_kfree:
+ kfree(fileio);
+ return ret;
+}
+
+/**
+ * __vb2_cleanup_fileio() - free resourced used by file io emulator
+ * @q: videobuf2 queue
+ */
+static int __vb2_cleanup_fileio(struct vb2_queue *q)
+{
+ struct vb2_fileio_data *fileio = q->fileio;
+
+ if (fileio) {
+ /*
+ * Hack fileio context to enable direct calls to vb2 ioctl
+ * interface.
+ */
+ q->fileio = NULL;
+
+ vb2_streamoff(q, q->type);
+ fileio->req.count = 0;
+ vb2_reqbufs(q, &fileio->req);
+ kfree(fileio);
+ dprintk(3, "file io emulator closed\n");
+ }
+ return 0;
+}
+
+/**
+ * __vb2_perform_fileio() - perform a single file io (read or write) operation
+ * @q: videobuf2 queue
+ * @data: pointed to target userspace buffer
+ * @count: number of bytes to read or write
+ * @ppos: file handle position tracking pointer
+ * @nonblock: mode selector (1 means blocking calls, 0 means nonblocking)
+ * @read: access mode selector (1 means read, 0 means write)
+ */
+static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_t count,
+ loff_t *ppos, int nonblock, int read)
+{
+ struct vb2_fileio_data *fileio;
+ struct vb2_fileio_buf *buf;
+ int ret, index;
+
+ dprintk(3, "file io: mode %s, offset %ld, count %zd, %sblocking\n",
+ read ? "read" : "write", (long)*ppos, count,
+ nonblock ? "non" : "");
+
+ if (!data)
+ return -EINVAL;
+
+ /*
+ * Initialize emulator on first call.
+ */
+ if (!q->fileio) {
+ ret = __vb2_init_fileio(q, read);
+ dprintk(3, "file io: vb2_init_fileio result: %d\n", ret);
+ if (ret)
+ return ret;
+ }
+ fileio = q->fileio;
+
+ /*
+ * Hack fileio context to enable direct calls to vb2 ioctl interface.
+ * The pointer will be restored before returning from this function.
+ */
+ q->fileio = NULL;
+
+ index = fileio->index;
+ buf = &fileio->bufs[index];
+
+ /*
+ * Check if we need to dequeue the buffer.
+ */
+ if (buf->queued) {
+ struct vb2_buffer *vb;
+
+ /*
+ * Call vb2_dqbuf to get buffer back.
+ */
+ memset(&fileio->b, 0, sizeof(fileio->b));
+ fileio->b.type = q->type;
+ fileio->b.memory = q->memory;
+ fileio->b.index = index;
+ ret = vb2_dqbuf(q, &fileio->b, nonblock);
+ dprintk(5, "file io: vb2_dqbuf result: %d\n", ret);
+ if (ret)
+ goto end;
+ fileio->dq_count += 1;
+
+ /*
+ * Get number of bytes filled by the driver
+ */
+ vb = q->bufs[index];
+ buf->size = vb2_get_plane_payload(vb, 0);
+ buf->queued = 0;
+ }
+
+ /*
+ * Limit count on last few bytes of the buffer.
+ */
+ if (buf->pos + count > buf->size) {
+ count = buf->size - buf->pos;
+ dprintk(5, "reducing read count: %zd\n", count);
+ }
+
+ /*
+ * Transfer data to userspace.
+ */
+ dprintk(3, "file io: copying %zd bytes - buffer %d, offset %u\n",
+ count, index, buf->pos);
+ if (read)
+ ret = copy_to_user(data, buf->vaddr + buf->pos, count);
+ else
+ ret = copy_from_user(buf->vaddr + buf->pos, data, count);
+ if (ret) {
+ dprintk(3, "file io: error copying data\n");
+ ret = -EFAULT;
+ goto end;
+ }
+
+ /*
+ * Update counters.
+ */
+ buf->pos += count;
+ *ppos += count;
+
+ /*
+ * Queue next buffer if required.
+ */
+ if (buf->pos == buf->size ||
+ (!read && (fileio->flags & VB2_FILEIO_WRITE_IMMEDIATELY))) {
+ /*
+ * Check if this is the last buffer to read.
+ */
+ if (read && (fileio->flags & VB2_FILEIO_READ_ONCE) &&
+ fileio->dq_count == 1) {
+ dprintk(3, "file io: read limit reached\n");
+ /*
+ * Restore fileio pointer and release the context.
+ */
+ q->fileio = fileio;
+ return __vb2_cleanup_fileio(q);
+ }
+
+ /*
+ * Call vb2_qbuf and give buffer to the driver.
+ */
+ memset(&fileio->b, 0, sizeof(fileio->b));
+ fileio->b.type = q->type;
+ fileio->b.memory = q->memory;
+ fileio->b.index = index;
+ fileio->b.bytesused = buf->pos;
+ ret = vb2_qbuf(q, &fileio->b);
+ dprintk(5, "file io: vb2_dbuf result: %d\n", ret);
+ if (ret)
+ goto end;
+
+ /*
+ * Buffer has been queued, update the status
+ */
+ buf->pos = 0;
+ buf->queued = 1;
+ buf->size = q->bufs[0]->v4l2_planes[0].length;
+ fileio->q_count += 1;
+
+ /*
+ * Switch to the next buffer
+ */
+ fileio->index = (index + 1) % q->num_buffers;
+
+ /*
+ * Start streaming if required.
+ */
+ if (!read && !q->streaming) {
+ ret = vb2_streamon(q, q->type);
+ if (ret)
+ goto end;
+ }
+ }
+
+ /*
+ * Return proper number of bytes processed.
+ */
+ if (ret == 0)
+ ret = count;
+end:
+ /*
+ * Restore the fileio context and block vb2 ioctl interface.
+ */
+ q->fileio = fileio;
+ return ret;
+}
+
+size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
+ loff_t *ppos, int nonblocking)
+{
+ return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 1);
+}
+EXPORT_SYMBOL_GPL(vb2_read);
+
+size_t vb2_write(struct vb2_queue *q, char __user *data, size_t count,
+ loff_t *ppos, int nonblocking)
+{
+ return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 0);
+}
+EXPORT_SYMBOL_GPL(vb2_write);
+
+MODULE_DESCRIPTION("Driver helper framework for Video for Linux 2");
+MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>, Marek Szyprowski");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/videobuf2-dma-contig.c b/drivers/media/video/videobuf2-dma-contig.c
new file mode 100644
index 00000000..4b713266
--- /dev/null
+++ b/drivers/media/video/videobuf2-dma-contig.c
@@ -0,0 +1,186 @@
+/*
+ * videobuf2-dma-contig.c - DMA contig memory allocator for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <pawel@osciak.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-memops.h>
+
+struct vb2_dc_conf {
+ struct device *dev;
+};
+
+struct vb2_dc_buf {
+ struct vb2_dc_conf *conf;
+ void *vaddr;
+ dma_addr_t dma_addr;
+ unsigned long size;
+ struct vm_area_struct *vma;
+ atomic_t refcount;
+ struct vb2_vmarea_handler handler;
+};
+
+static void vb2_dma_contig_put(void *buf_priv);
+
+static void *vb2_dma_contig_alloc(void *alloc_ctx, unsigned long size)
+{
+ struct vb2_dc_conf *conf = alloc_ctx;
+ struct vb2_dc_buf *buf;
+
+ buf = kzalloc(sizeof *buf, GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ buf->vaddr = dma_alloc_coherent(conf->dev, size, &buf->dma_addr,
+ GFP_KERNEL);
+ if (!buf->vaddr) {
+ dev_err(conf->dev, "dma_alloc_coherent of size %ld failed\n",
+ size);
+ kfree(buf);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ buf->conf = conf;
+ buf->size = size;
+
+ buf->handler.refcount = &buf->refcount;
+ buf->handler.put = vb2_dma_contig_put;
+ buf->handler.arg = buf;
+
+ atomic_inc(&buf->refcount);
+
+ return buf;
+}
+
+static void vb2_dma_contig_put(void *buf_priv)
+{
+ struct vb2_dc_buf *buf = buf_priv;
+
+ if (atomic_dec_and_test(&buf->refcount)) {
+ dma_free_coherent(buf->conf->dev, buf->size, buf->vaddr,
+ buf->dma_addr);
+ kfree(buf);
+ }
+}
+
+static void *vb2_dma_contig_cookie(void *buf_priv)
+{
+ struct vb2_dc_buf *buf = buf_priv;
+
+ return &buf->dma_addr;
+}
+
+static void *vb2_dma_contig_vaddr(void *buf_priv)
+{
+ struct vb2_dc_buf *buf = buf_priv;
+ if (!buf)
+ return NULL;
+
+ return buf->vaddr;
+}
+
+static unsigned int vb2_dma_contig_num_users(void *buf_priv)
+{
+ struct vb2_dc_buf *buf = buf_priv;
+
+ return atomic_read(&buf->refcount);
+}
+
+static int vb2_dma_contig_mmap(void *buf_priv, struct vm_area_struct *vma)
+{
+ struct vb2_dc_buf *buf = buf_priv;
+
+ if (!buf) {
+ printk(KERN_ERR "No buffer to map\n");
+ return -EINVAL;
+ }
+
+ return vb2_mmap_pfn_range(vma, buf->dma_addr, buf->size,
+ &vb2_common_vm_ops, &buf->handler);
+}
+
+static void *vb2_dma_contig_get_userptr(void *alloc_ctx, unsigned long vaddr,
+ unsigned long size, int write)
+{
+ struct vb2_dc_buf *buf;
+ struct vm_area_struct *vma;
+ dma_addr_t dma_addr = 0;
+ int ret;
+
+ buf = kzalloc(sizeof *buf, GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ ret = vb2_get_contig_userptr(vaddr, size, &vma, &dma_addr);
+ if (ret) {
+ printk(KERN_ERR "Failed acquiring VMA for vaddr 0x%08lx\n",
+ vaddr);
+ kfree(buf);
+ return ERR_PTR(ret);
+ }
+
+ buf->size = size;
+ buf->dma_addr = dma_addr;
+ buf->vma = vma;
+
+ return buf;
+}
+
+static void vb2_dma_contig_put_userptr(void *mem_priv)
+{
+ struct vb2_dc_buf *buf = mem_priv;
+
+ if (!buf)
+ return;
+
+ vb2_put_vma(buf->vma);
+ kfree(buf);
+}
+
+const struct vb2_mem_ops vb2_dma_contig_memops = {
+ .alloc = vb2_dma_contig_alloc,
+ .put = vb2_dma_contig_put,
+ .cookie = vb2_dma_contig_cookie,
+ .vaddr = vb2_dma_contig_vaddr,
+ .mmap = vb2_dma_contig_mmap,
+ .get_userptr = vb2_dma_contig_get_userptr,
+ .put_userptr = vb2_dma_contig_put_userptr,
+ .num_users = vb2_dma_contig_num_users,
+};
+EXPORT_SYMBOL_GPL(vb2_dma_contig_memops);
+
+void *vb2_dma_contig_init_ctx(struct device *dev)
+{
+ struct vb2_dc_conf *conf;
+
+ conf = kzalloc(sizeof *conf, GFP_KERNEL);
+ if (!conf)
+ return ERR_PTR(-ENOMEM);
+
+ conf->dev = dev;
+
+ return conf;
+}
+EXPORT_SYMBOL_GPL(vb2_dma_contig_init_ctx);
+
+void vb2_dma_contig_cleanup_ctx(void *alloc_ctx)
+{
+ kfree(alloc_ctx);
+}
+EXPORT_SYMBOL_GPL(vb2_dma_contig_cleanup_ctx);
+
+MODULE_DESCRIPTION("DMA-contig memory handling routines for videobuf2");
+MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/videobuf2-dma-sg.c b/drivers/media/video/videobuf2-dma-sg.c
new file mode 100644
index 00000000..25c3b360
--- /dev/null
+++ b/drivers/media/video/videobuf2-dma-sg.c
@@ -0,0 +1,283 @@
+/*
+ * videobuf2-dma-sg.c - dma scatter/gather memory allocator for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/scatterlist.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-memops.h>
+#include <media/videobuf2-dma-sg.h>
+
+struct vb2_dma_sg_buf {
+ void *vaddr;
+ struct page **pages;
+ int write;
+ int offset;
+ struct vb2_dma_sg_desc sg_desc;
+ atomic_t refcount;
+ struct vb2_vmarea_handler handler;
+};
+
+static void vb2_dma_sg_put(void *buf_priv);
+
+static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size)
+{
+ struct vb2_dma_sg_buf *buf;
+ int i;
+
+ buf = kzalloc(sizeof *buf, GFP_KERNEL);
+ if (!buf)
+ return NULL;
+
+ buf->vaddr = NULL;
+ buf->write = 0;
+ buf->offset = 0;
+ buf->sg_desc.size = size;
+ buf->sg_desc.num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+
+ buf->sg_desc.sglist = vzalloc(buf->sg_desc.num_pages *
+ sizeof(*buf->sg_desc.sglist));
+ if (!buf->sg_desc.sglist)
+ goto fail_sglist_alloc;
+ sg_init_table(buf->sg_desc.sglist, buf->sg_desc.num_pages);
+
+ buf->pages = kzalloc(buf->sg_desc.num_pages * sizeof(struct page *),
+ GFP_KERNEL);
+ if (!buf->pages)
+ goto fail_pages_array_alloc;
+
+ for (i = 0; i < buf->sg_desc.num_pages; ++i) {
+ buf->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO | __GFP_NOWARN);
+ if (NULL == buf->pages[i])
+ goto fail_pages_alloc;
+ sg_set_page(&buf->sg_desc.sglist[i],
+ buf->pages[i], PAGE_SIZE, 0);
+ }
+
+ buf->handler.refcount = &buf->refcount;
+ buf->handler.put = vb2_dma_sg_put;
+ buf->handler.arg = buf;
+
+ atomic_inc(&buf->refcount);
+
+ printk(KERN_DEBUG "%s: Allocated buffer of %d pages\n",
+ __func__, buf->sg_desc.num_pages);
+ return buf;
+
+fail_pages_alloc:
+ while (--i >= 0)
+ __free_page(buf->pages[i]);
+ kfree(buf->pages);
+
+fail_pages_array_alloc:
+ vfree(buf->sg_desc.sglist);
+
+fail_sglist_alloc:
+ kfree(buf);
+ return NULL;
+}
+
+static void vb2_dma_sg_put(void *buf_priv)
+{
+ struct vb2_dma_sg_buf *buf = buf_priv;
+ int i = buf->sg_desc.num_pages;
+
+ if (atomic_dec_and_test(&buf->refcount)) {
+ printk(KERN_DEBUG "%s: Freeing buffer of %d pages\n", __func__,
+ buf->sg_desc.num_pages);
+ if (buf->vaddr)
+ vm_unmap_ram(buf->vaddr, buf->sg_desc.num_pages);
+ vfree(buf->sg_desc.sglist);
+ while (--i >= 0)
+ __free_page(buf->pages[i]);
+ kfree(buf->pages);
+ kfree(buf);
+ }
+}
+
+static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr,
+ unsigned long size, int write)
+{
+ struct vb2_dma_sg_buf *buf;
+ unsigned long first, last;
+ int num_pages_from_user, i;
+
+ buf = kzalloc(sizeof *buf, GFP_KERNEL);
+ if (!buf)
+ return NULL;
+
+ buf->vaddr = NULL;
+ buf->write = write;
+ buf->offset = vaddr & ~PAGE_MASK;
+ buf->sg_desc.size = size;
+
+ first = (vaddr & PAGE_MASK) >> PAGE_SHIFT;
+ last = ((vaddr + size - 1) & PAGE_MASK) >> PAGE_SHIFT;
+ buf->sg_desc.num_pages = last - first + 1;
+
+ buf->sg_desc.sglist = vzalloc(
+ buf->sg_desc.num_pages * sizeof(*buf->sg_desc.sglist));
+ if (!buf->sg_desc.sglist)
+ goto userptr_fail_sglist_alloc;
+
+ sg_init_table(buf->sg_desc.sglist, buf->sg_desc.num_pages);
+
+ buf->pages = kzalloc(buf->sg_desc.num_pages * sizeof(struct page *),
+ GFP_KERNEL);
+ if (!buf->pages)
+ goto userptr_fail_pages_array_alloc;
+
+ num_pages_from_user = get_user_pages(current, current->mm,
+ vaddr & PAGE_MASK,
+ buf->sg_desc.num_pages,
+ write,
+ 1, /* force */
+ buf->pages,
+ NULL);
+
+ if (num_pages_from_user != buf->sg_desc.num_pages)
+ goto userptr_fail_get_user_pages;
+
+ sg_set_page(&buf->sg_desc.sglist[0], buf->pages[0],
+ PAGE_SIZE - buf->offset, buf->offset);
+ size -= PAGE_SIZE - buf->offset;
+ for (i = 1; i < buf->sg_desc.num_pages; ++i) {
+ sg_set_page(&buf->sg_desc.sglist[i], buf->pages[i],
+ min_t(size_t, PAGE_SIZE, size), 0);
+ size -= min_t(size_t, PAGE_SIZE, size);
+ }
+ return buf;
+
+userptr_fail_get_user_pages:
+ printk(KERN_DEBUG "get_user_pages requested/got: %d/%d]\n",
+ num_pages_from_user, buf->sg_desc.num_pages);
+ while (--num_pages_from_user >= 0)
+ put_page(buf->pages[num_pages_from_user]);
+ kfree(buf->pages);
+
+userptr_fail_pages_array_alloc:
+ vfree(buf->sg_desc.sglist);
+
+userptr_fail_sglist_alloc:
+ kfree(buf);
+ return NULL;
+}
+
+/*
+ * @put_userptr: inform the allocator that a USERPTR buffer will no longer
+ * be used
+ */
+static void vb2_dma_sg_put_userptr(void *buf_priv)
+{
+ struct vb2_dma_sg_buf *buf = buf_priv;
+ int i = buf->sg_desc.num_pages;
+
+ printk(KERN_DEBUG "%s: Releasing userspace buffer of %d pages\n",
+ __func__, buf->sg_desc.num_pages);
+ if (buf->vaddr)
+ vm_unmap_ram(buf->vaddr, buf->sg_desc.num_pages);
+ while (--i >= 0) {
+ if (buf->write)
+ set_page_dirty_lock(buf->pages[i]);
+ put_page(buf->pages[i]);
+ }
+ vfree(buf->sg_desc.sglist);
+ kfree(buf->pages);
+ kfree(buf);
+}
+
+static void *vb2_dma_sg_vaddr(void *buf_priv)
+{
+ struct vb2_dma_sg_buf *buf = buf_priv;
+
+ BUG_ON(!buf);
+
+ if (!buf->vaddr)
+ buf->vaddr = vm_map_ram(buf->pages,
+ buf->sg_desc.num_pages,
+ -1,
+ PAGE_KERNEL);
+
+ /* add offset in case userptr is not page-aligned */
+ return buf->vaddr + buf->offset;
+}
+
+static unsigned int vb2_dma_sg_num_users(void *buf_priv)
+{
+ struct vb2_dma_sg_buf *buf = buf_priv;
+
+ return atomic_read(&buf->refcount);
+}
+
+static int vb2_dma_sg_mmap(void *buf_priv, struct vm_area_struct *vma)
+{
+ struct vb2_dma_sg_buf *buf = buf_priv;
+ unsigned long uaddr = vma->vm_start;
+ unsigned long usize = vma->vm_end - vma->vm_start;
+ int i = 0;
+
+ if (!buf) {
+ printk(KERN_ERR "No memory to map\n");
+ return -EINVAL;
+ }
+
+ do {
+ int ret;
+
+ ret = vm_insert_page(vma, uaddr, buf->pages[i++]);
+ if (ret) {
+ printk(KERN_ERR "Remapping memory, error: %d\n", ret);
+ return ret;
+ }
+
+ uaddr += PAGE_SIZE;
+ usize -= PAGE_SIZE;
+ } while (usize > 0);
+
+
+ /*
+ * Use common vm_area operations to track buffer refcount.
+ */
+ vma->vm_private_data = &buf->handler;
+ vma->vm_ops = &vb2_common_vm_ops;
+
+ vma->vm_ops->open(vma);
+
+ return 0;
+}
+
+static void *vb2_dma_sg_cookie(void *buf_priv)
+{
+ struct vb2_dma_sg_buf *buf = buf_priv;
+
+ return &buf->sg_desc;
+}
+
+const struct vb2_mem_ops vb2_dma_sg_memops = {
+ .alloc = vb2_dma_sg_alloc,
+ .put = vb2_dma_sg_put,
+ .get_userptr = vb2_dma_sg_get_userptr,
+ .put_userptr = vb2_dma_sg_put_userptr,
+ .vaddr = vb2_dma_sg_vaddr,
+ .mmap = vb2_dma_sg_mmap,
+ .num_users = vb2_dma_sg_num_users,
+ .cookie = vb2_dma_sg_cookie,
+};
+EXPORT_SYMBOL_GPL(vb2_dma_sg_memops);
+
+MODULE_DESCRIPTION("dma scatter/gather memory handling routines for videobuf2");
+MODULE_AUTHOR("Andrzej Pietrasiewicz");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/videobuf2-memops.c b/drivers/media/video/videobuf2-memops.c
new file mode 100644
index 00000000..504cd4cb
--- /dev/null
+++ b/drivers/media/video/videobuf2-memops.c
@@ -0,0 +1,227 @@
+/*
+ * videobuf2-memops.c - generic memory handling routines for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <pawel@osciak.com>
+ * Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/file.h>
+
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-memops.h>
+
+/**
+ * vb2_get_vma() - acquire and lock the virtual memory area
+ * @vma: given virtual memory area
+ *
+ * This function attempts to acquire an area mapped in the userspace for
+ * the duration of a hardware operation. The area is "locked" by performing
+ * the same set of operation that are done when process calls fork() and
+ * memory areas are duplicated.
+ *
+ * Returns a copy of a virtual memory region on success or NULL.
+ */
+struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma)
+{
+ struct vm_area_struct *vma_copy;
+
+ vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL);
+ if (vma_copy == NULL)
+ return NULL;
+
+ if (vma->vm_ops && vma->vm_ops->open)
+ vma->vm_ops->open(vma);
+
+ if (vma->vm_file)
+ get_file(vma->vm_file);
+
+ memcpy(vma_copy, vma, sizeof(*vma));
+
+ vma_copy->vm_mm = NULL;
+ vma_copy->vm_next = NULL;
+ vma_copy->vm_prev = NULL;
+
+ return vma_copy;
+}
+EXPORT_SYMBOL_GPL(vb2_get_vma);
+
+/**
+ * vb2_put_userptr() - release a userspace virtual memory area
+ * @vma: virtual memory region associated with the area to be released
+ *
+ * This function releases the previously acquired memory area after a hardware
+ * operation.
+ */
+void vb2_put_vma(struct vm_area_struct *vma)
+{
+ if (!vma)
+ return;
+
+ if (vma->vm_ops && vma->vm_ops->close)
+ vma->vm_ops->close(vma);
+
+ if (vma->vm_file)
+ fput(vma->vm_file);
+
+ kfree(vma);
+}
+EXPORT_SYMBOL_GPL(vb2_put_vma);
+
+/**
+ * vb2_get_contig_userptr() - lock physically contiguous userspace mapped memory
+ * @vaddr: starting virtual address of the area to be verified
+ * @size: size of the area
+ * @res_paddr: will return physical address for the given vaddr
+ * @res_vma: will return locked copy of struct vm_area for the given area
+ *
+ * This function will go through memory area of size @size mapped at @vaddr and
+ * verify that the underlying physical pages are contiguous. If they are
+ * contiguous the virtual memory area is locked and a @res_vma is filled with
+ * the copy and @res_pa set to the physical address of the buffer.
+ *
+ * Returns 0 on success.
+ */
+int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size,
+ struct vm_area_struct **res_vma, dma_addr_t *res_pa)
+{
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *vma;
+ unsigned long offset, start, end;
+ unsigned long this_pfn, prev_pfn;
+ dma_addr_t pa = 0;
+
+ start = vaddr;
+ offset = start & ~PAGE_MASK;
+ end = start + size;
+
+ vma = find_vma(mm, start);
+
+ if (vma == NULL || vma->vm_end < end)
+ return -EFAULT;
+
+ for (prev_pfn = 0; start < end; start += PAGE_SIZE) {
+ int ret = follow_pfn(vma, start, &this_pfn);
+ if (ret)
+ return ret;
+
+ if (prev_pfn == 0)
+ pa = this_pfn << PAGE_SHIFT;
+ else if (this_pfn != prev_pfn + 1)
+ return -EFAULT;
+
+ prev_pfn = this_pfn;
+ }
+
+ /*
+ * Memory is contigous, lock vma and return to the caller
+ */
+ *res_vma = vb2_get_vma(vma);
+ if (*res_vma == NULL)
+ return -ENOMEM;
+
+ *res_pa = pa + offset;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_get_contig_userptr);
+
+/**
+ * vb2_mmap_pfn_range() - map physical pages to userspace
+ * @vma: virtual memory region for the mapping
+ * @paddr: starting physical address of the memory to be mapped
+ * @size: size of the memory to be mapped
+ * @vm_ops: vm operations to be assigned to the created area
+ * @priv: private data to be associated with the area
+ *
+ * Returns 0 on success.
+ */
+int vb2_mmap_pfn_range(struct vm_area_struct *vma, unsigned long paddr,
+ unsigned long size,
+ const struct vm_operations_struct *vm_ops,
+ void *priv)
+{
+ int ret;
+
+ size = min_t(unsigned long, vma->vm_end - vma->vm_start, size);
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ ret = remap_pfn_range(vma, vma->vm_start, paddr >> PAGE_SHIFT,
+ size, vma->vm_page_prot);
+ if (ret) {
+ printk(KERN_ERR "Remapping memory failed, error: %d\n", ret);
+ return ret;
+ }
+
+ vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED;
+ vma->vm_private_data = priv;
+ vma->vm_ops = vm_ops;
+
+ vma->vm_ops->open(vma);
+
+ pr_debug("%s: mapped paddr 0x%08lx at 0x%08lx, size %ld\n",
+ __func__, paddr, vma->vm_start, size);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_mmap_pfn_range);
+
+/**
+ * vb2_common_vm_open() - increase refcount of the vma
+ * @vma: virtual memory region for the mapping
+ *
+ * This function adds another user to the provided vma. It expects
+ * struct vb2_vmarea_handler pointer in vma->vm_private_data.
+ */
+static void vb2_common_vm_open(struct vm_area_struct *vma)
+{
+ struct vb2_vmarea_handler *h = vma->vm_private_data;
+
+ pr_debug("%s: %p, refcount: %d, vma: %08lx-%08lx\n",
+ __func__, h, atomic_read(h->refcount), vma->vm_start,
+ vma->vm_end);
+
+ atomic_inc(h->refcount);
+}
+
+/**
+ * vb2_common_vm_close() - decrease refcount of the vma
+ * @vma: virtual memory region for the mapping
+ *
+ * This function releases the user from the provided vma. It expects
+ * struct vb2_vmarea_handler pointer in vma->vm_private_data.
+ */
+static void vb2_common_vm_close(struct vm_area_struct *vma)
+{
+ struct vb2_vmarea_handler *h = vma->vm_private_data;
+
+ pr_debug("%s: %p, refcount: %d, vma: %08lx-%08lx\n",
+ __func__, h, atomic_read(h->refcount), vma->vm_start,
+ vma->vm_end);
+
+ h->put(h->arg);
+}
+
+/**
+ * vb2_common_vm_ops - common vm_ops used for tracking refcount of mmaped
+ * video buffers
+ */
+const struct vm_operations_struct vb2_common_vm_ops = {
+ .open = vb2_common_vm_open,
+ .close = vb2_common_vm_close,
+};
+EXPORT_SYMBOL_GPL(vb2_common_vm_ops);
+
+MODULE_DESCRIPTION("common memory handling routines for videobuf2");
+MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/videobuf2-vmalloc.c b/drivers/media/video/videobuf2-vmalloc.c
new file mode 100644
index 00000000..6b5ca6c7
--- /dev/null
+++ b/drivers/media/video/videobuf2-vmalloc.c
@@ -0,0 +1,222 @@
+/*
+ * videobuf2-vmalloc.c - vmalloc memory allocator for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <pawel@osciak.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-memops.h>
+
+struct vb2_vmalloc_buf {
+ void *vaddr;
+ struct page **pages;
+ struct vm_area_struct *vma;
+ int write;
+ unsigned long size;
+ unsigned int n_pages;
+ atomic_t refcount;
+ struct vb2_vmarea_handler handler;
+};
+
+static void vb2_vmalloc_put(void *buf_priv);
+
+static void *vb2_vmalloc_alloc(void *alloc_ctx, unsigned long size)
+{
+ struct vb2_vmalloc_buf *buf;
+
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return NULL;
+
+ buf->size = size;
+ buf->vaddr = vmalloc_user(buf->size);
+ buf->handler.refcount = &buf->refcount;
+ buf->handler.put = vb2_vmalloc_put;
+ buf->handler.arg = buf;
+
+ if (!buf->vaddr) {
+ pr_debug("vmalloc of size %ld failed\n", buf->size);
+ kfree(buf);
+ return NULL;
+ }
+
+ atomic_inc(&buf->refcount);
+ return buf;
+}
+
+static void vb2_vmalloc_put(void *buf_priv)
+{
+ struct vb2_vmalloc_buf *buf = buf_priv;
+
+ if (atomic_dec_and_test(&buf->refcount)) {
+ vfree(buf->vaddr);
+ kfree(buf);
+ }
+}
+
+static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr,
+ unsigned long size, int write)
+{
+ struct vb2_vmalloc_buf *buf;
+ unsigned long first, last;
+ int n_pages, offset;
+ struct vm_area_struct *vma;
+ dma_addr_t physp;
+
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return NULL;
+
+ buf->write = write;
+ offset = vaddr & ~PAGE_MASK;
+ buf->size = size;
+
+
+ vma = find_vma(current->mm, vaddr);
+ if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) {
+ if (vb2_get_contig_userptr(vaddr, size, &vma, &physp))
+ goto fail_pages_array_alloc;
+ buf->vma = vma;
+ buf->vaddr = ioremap_nocache(physp, size);
+ if (!buf->vaddr)
+ goto fail_pages_array_alloc;
+ } else {
+ first = vaddr >> PAGE_SHIFT;
+ last = (vaddr + size - 1) >> PAGE_SHIFT;
+ buf->n_pages = last - first + 1;
+ buf->pages = kzalloc(buf->n_pages * sizeof(struct page *),
+ GFP_KERNEL);
+ if (!buf->pages)
+ goto fail_pages_array_alloc;
+
+ /* current->mm->mmap_sem is taken by videobuf2 core */
+ n_pages = get_user_pages(current, current->mm,
+ vaddr & PAGE_MASK, buf->n_pages,
+ write, 1, /* force */
+ buf->pages, NULL);
+ if (n_pages != buf->n_pages)
+ goto fail_get_user_pages;
+
+ buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1,
+ PAGE_KERNEL);
+ if (!buf->vaddr)
+ goto fail_get_user_pages;
+ }
+
+ buf->vaddr += offset;
+ return buf;
+
+fail_get_user_pages:
+ pr_debug("get_user_pages requested/got: %d/%d]\n", n_pages,
+ buf->n_pages);
+ while (--n_pages >= 0)
+ put_page(buf->pages[n_pages]);
+ kfree(buf->pages);
+
+fail_pages_array_alloc:
+ kfree(buf);
+
+ return NULL;
+}
+
+static void vb2_vmalloc_put_userptr(void *buf_priv)
+{
+ struct vb2_vmalloc_buf *buf = buf_priv;
+ unsigned long vaddr = (unsigned long)buf->vaddr & PAGE_MASK;
+ unsigned int i;
+
+ if (buf->pages) {
+ if (vaddr)
+ vm_unmap_ram((void *)vaddr, buf->n_pages);
+ for (i = 0; i < buf->n_pages; ++i) {
+ if (buf->write)
+ set_page_dirty_lock(buf->pages[i]);
+ put_page(buf->pages[i]);
+ }
+ kfree(buf->pages);
+ } else {
+ if (buf->vma)
+ vb2_put_vma(buf->vma);
+ iounmap(buf->vaddr);
+ }
+ kfree(buf);
+}
+
+static void *vb2_vmalloc_vaddr(void *buf_priv)
+{
+ struct vb2_vmalloc_buf *buf = buf_priv;
+
+ if (!buf->vaddr) {
+ pr_err("Address of an unallocated plane requested "
+ "or cannot map user pointer\n");
+ return NULL;
+ }
+
+ return buf->vaddr;
+}
+
+static unsigned int vb2_vmalloc_num_users(void *buf_priv)
+{
+ struct vb2_vmalloc_buf *buf = buf_priv;
+ return atomic_read(&buf->refcount);
+}
+
+static int vb2_vmalloc_mmap(void *buf_priv, struct vm_area_struct *vma)
+{
+ struct vb2_vmalloc_buf *buf = buf_priv;
+ int ret;
+
+ if (!buf) {
+ pr_err("No memory to map\n");
+ return -EINVAL;
+ }
+
+ ret = remap_vmalloc_range(vma, buf->vaddr, 0);
+ if (ret) {
+ pr_err("Remapping vmalloc memory, error: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Make sure that vm_areas for 2 buffers won't be merged together
+ */
+ vma->vm_flags |= VM_DONTEXPAND;
+
+ /*
+ * Use common vm_area operations to track buffer refcount.
+ */
+ vma->vm_private_data = &buf->handler;
+ vma->vm_ops = &vb2_common_vm_ops;
+
+ vma->vm_ops->open(vma);
+
+ return 0;
+}
+
+const struct vb2_mem_ops vb2_vmalloc_memops = {
+ .alloc = vb2_vmalloc_alloc,
+ .put = vb2_vmalloc_put,
+ .get_userptr = vb2_vmalloc_get_userptr,
+ .put_userptr = vb2_vmalloc_put_userptr,
+ .vaddr = vb2_vmalloc_vaddr,
+ .mmap = vb2_vmalloc_mmap,
+ .num_users = vb2_vmalloc_num_users,
+};
+EXPORT_SYMBOL_GPL(vb2_vmalloc_memops);
+
+MODULE_DESCRIPTION("vmalloc memory handling routines for videobuf2");
+MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/vino.c b/drivers/media/video/vino.c
new file mode 100644
index 00000000..4d7391ec
--- /dev/null
+++ b/drivers/media/video/vino.c
@@ -0,0 +1,4349 @@
+/*
+ * Driver for the VINO (Video In No Out) system found in SGI Indys.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
+ *
+ * Based on the previous version of the driver for 2.4 kernels by:
+ * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
+ *
+ * v4l2_device/v4l2_subdev conversion by:
+ * Copyright (C) 2009 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * Note: this conversion is untested! Please contact the linux-media
+ * mailinglist if you can test this, together with the test results.
+ */
+
+/*
+ * TODO:
+ * - remove "mark pages reserved-hacks" from memory allocation code
+ * and implement fault()
+ * - check decimation, calculating and reporting image size when
+ * using decimation
+ * - implement read(), user mode buffers and overlay (?)
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/time.h>
+#include <linux/kmod.h>
+
+#include <linux/i2c.h>
+
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <linux/mutex.h>
+
+#include <asm/paccess.h>
+#include <asm/io.h>
+#include <asm/sgi/ip22.h>
+#include <asm/sgi/mc.h>
+
+#include "vino.h"
+#include "saa7191.h"
+#include "indycam.h"
+
+/* Uncomment the following line to get lots and lots of (mostly useless)
+ * debug info.
+ * Note that the debug output also slows down the driver significantly */
+// #define VINO_DEBUG
+// #define VINO_DEBUG_INT
+
+#define VINO_MODULE_VERSION "0.0.7"
+
+MODULE_DESCRIPTION("SGI VINO Video4Linux2 driver");
+MODULE_VERSION(VINO_MODULE_VERSION);
+MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>");
+MODULE_LICENSE("GPL");
+
+#ifdef VINO_DEBUG
+#define dprintk(x...) printk("VINO: " x);
+#else
+#define dprintk(x...)
+#endif
+
+#define VINO_NO_CHANNEL 0
+#define VINO_CHANNEL_A 1
+#define VINO_CHANNEL_B 2
+
+#define VINO_PAL_WIDTH 768
+#define VINO_PAL_HEIGHT 576
+#define VINO_NTSC_WIDTH 640
+#define VINO_NTSC_HEIGHT 480
+
+#define VINO_MIN_WIDTH 32
+#define VINO_MIN_HEIGHT 32
+
+#define VINO_CLIPPING_START_ODD_D1 1
+#define VINO_CLIPPING_START_ODD_PAL 15
+#define VINO_CLIPPING_START_ODD_NTSC 12
+
+#define VINO_CLIPPING_START_EVEN_D1 2
+#define VINO_CLIPPING_START_EVEN_PAL 15
+#define VINO_CLIPPING_START_EVEN_NTSC 12
+
+#define VINO_INPUT_CHANNEL_COUNT 3
+
+/* the number is the index for vino_inputs */
+#define VINO_INPUT_NONE -1
+#define VINO_INPUT_COMPOSITE 0
+#define VINO_INPUT_SVIDEO 1
+#define VINO_INPUT_D1 2
+
+#define VINO_PAGE_RATIO (PAGE_SIZE / VINO_PAGE_SIZE)
+
+#define VINO_FIFO_THRESHOLD_DEFAULT 16
+
+#define VINO_FRAMEBUFFER_SIZE ((VINO_PAL_WIDTH \
+ * VINO_PAL_HEIGHT * 4 \
+ + 3 * PAGE_SIZE) & ~(PAGE_SIZE - 1))
+
+#define VINO_FRAMEBUFFER_COUNT_MAX 8
+
+#define VINO_FRAMEBUFFER_UNUSED 0
+#define VINO_FRAMEBUFFER_IN_USE 1
+#define VINO_FRAMEBUFFER_READY 2
+
+#define VINO_QUEUE_ERROR -1
+#define VINO_QUEUE_MAGIC 0x20050125
+
+#define VINO_MEMORY_NONE 0
+#define VINO_MEMORY_MMAP 1
+#define VINO_MEMORY_USERPTR 2
+
+#define VINO_DUMMY_DESC_COUNT 4
+#define VINO_DESC_FETCH_DELAY 5 /* microseconds */
+
+#define VINO_MAX_FRAME_SKIP_COUNT 128
+
+/* the number is the index for vino_data_formats */
+#define VINO_DATA_FMT_NONE -1
+#define VINO_DATA_FMT_GREY 0
+#define VINO_DATA_FMT_RGB332 1
+#define VINO_DATA_FMT_RGB32 2
+#define VINO_DATA_FMT_YUV 3
+
+#define VINO_DATA_FMT_COUNT 4
+
+/* the number is the index for vino_data_norms */
+#define VINO_DATA_NORM_NONE -1
+#define VINO_DATA_NORM_NTSC 0
+#define VINO_DATA_NORM_PAL 1
+#define VINO_DATA_NORM_SECAM 2
+#define VINO_DATA_NORM_D1 3
+
+#define VINO_DATA_NORM_COUNT 4
+
+/* I2C controller flags */
+#define SGI_I2C_FORCE_IDLE (0 << 0)
+#define SGI_I2C_NOT_IDLE (1 << 0)
+#define SGI_I2C_WRITE (0 << 1)
+#define SGI_I2C_READ (1 << 1)
+#define SGI_I2C_RELEASE_BUS (0 << 2)
+#define SGI_I2C_HOLD_BUS (1 << 2)
+#define SGI_I2C_XFER_DONE (0 << 4)
+#define SGI_I2C_XFER_BUSY (1 << 4)
+#define SGI_I2C_ACK (0 << 5)
+#define SGI_I2C_NACK (1 << 5)
+#define SGI_I2C_BUS_OK (0 << 7)
+#define SGI_I2C_BUS_ERR (1 << 7)
+
+/* Internal data structure definitions */
+
+struct vino_input {
+ char *name;
+ v4l2_std_id std;
+};
+
+struct vino_clipping {
+ unsigned int left, right, top, bottom;
+};
+
+struct vino_data_format {
+ /* the description */
+ char *description;
+ /* bytes per pixel */
+ unsigned int bpp;
+ /* V4L2 fourcc code */
+ __u32 pixelformat;
+ /* V4L2 colorspace (duh!) */
+ enum v4l2_colorspace colorspace;
+};
+
+struct vino_data_norm {
+ char *description;
+ unsigned int width, height;
+ struct vino_clipping odd;
+ struct vino_clipping even;
+
+ v4l2_std_id std;
+ unsigned int fps_min, fps_max;
+ __u32 framelines;
+};
+
+struct vino_descriptor_table {
+ /* the number of PAGE_SIZE sized pages in the buffer */
+ unsigned int page_count;
+ /* virtual (kmalloc'd) pointers to the actual data
+ * (in PAGE_SIZE chunks, used with mmap streaming) */
+ unsigned long *virtual;
+
+ /* cpu address for the VINO descriptor table
+ * (contains DMA addresses, VINO_PAGE_SIZE chunks) */
+ unsigned long *dma_cpu;
+ /* dma address for the VINO descriptor table
+ * (contains DMA addresses, VINO_PAGE_SIZE chunks) */
+ dma_addr_t dma;
+};
+
+struct vino_framebuffer {
+ /* identifier nubmer */
+ unsigned int id;
+ /* the length of the whole buffer */
+ unsigned int size;
+ /* the length of actual data in buffer */
+ unsigned int data_size;
+ /* the data format */
+ unsigned int data_format;
+ /* the state of buffer data */
+ unsigned int state;
+ /* is the buffer mapped in user space? */
+ unsigned int map_count;
+ /* memory offset for mmap() */
+ unsigned int offset;
+ /* frame counter */
+ unsigned int frame_counter;
+ /* timestamp (written when image capture finishes) */
+ struct timeval timestamp;
+
+ struct vino_descriptor_table desc_table;
+
+ spinlock_t state_lock;
+};
+
+struct vino_framebuffer_fifo {
+ unsigned int length;
+
+ unsigned int used;
+ unsigned int head;
+ unsigned int tail;
+
+ unsigned int data[VINO_FRAMEBUFFER_COUNT_MAX];
+};
+
+struct vino_framebuffer_queue {
+ unsigned int magic;
+
+ /* VINO_MEMORY_NONE, VINO_MEMORY_MMAP or VINO_MEMORY_USERPTR */
+ unsigned int type;
+ unsigned int length;
+
+ /* data field of in and out contain index numbers for buffer */
+ struct vino_framebuffer_fifo in;
+ struct vino_framebuffer_fifo out;
+
+ struct vino_framebuffer *buffer[VINO_FRAMEBUFFER_COUNT_MAX];
+
+ spinlock_t queue_lock;
+ struct mutex queue_mutex;
+ wait_queue_head_t frame_wait_queue;
+};
+
+struct vino_interrupt_data {
+ struct timeval timestamp;
+ unsigned int frame_counter;
+ unsigned int skip_count;
+ unsigned int skip;
+};
+
+struct vino_channel_settings {
+ unsigned int channel;
+
+ int input;
+ unsigned int data_format;
+ unsigned int data_norm;
+ struct vino_clipping clipping;
+ unsigned int decimation;
+ unsigned int line_size;
+ unsigned int alpha;
+ unsigned int fps;
+ unsigned int framert_reg;
+
+ unsigned int fifo_threshold;
+
+ struct vino_framebuffer_queue fb_queue;
+
+ /* number of the current field */
+ unsigned int field;
+
+ /* read in progress */
+ int reading;
+ /* streaming is active */
+ int streaming;
+ /* the driver is currently processing the queue */
+ int capturing;
+
+ struct mutex mutex;
+ spinlock_t capture_lock;
+
+ unsigned int users;
+
+ struct vino_interrupt_data int_data;
+
+ /* V4L support */
+ struct video_device *vdev;
+};
+
+struct vino_settings {
+ struct v4l2_device v4l2_dev;
+ struct vino_channel_settings a;
+ struct vino_channel_settings b;
+
+ /* the channel which owns this client:
+ * VINO_NO_CHANNEL, VINO_CHANNEL_A or VINO_CHANNEL_B */
+ unsigned int decoder_owner;
+ struct v4l2_subdev *decoder;
+ unsigned int camera_owner;
+ struct v4l2_subdev *camera;
+
+ /* a lock for vino register access */
+ spinlock_t vino_lock;
+ /* a lock for channel input changes */
+ spinlock_t input_lock;
+
+ unsigned long dummy_page;
+ struct vino_descriptor_table dummy_desc_table;
+};
+
+/* Module parameters */
+
+/*
+ * Using vino_pixel_conversion the ABGR32-format pixels supplied
+ * by the VINO chip can be converted to more common formats
+ * like RGBA32 (or probably RGB24 in the future). This way we
+ * can give out data that can be specified correctly with
+ * the V4L2-definitions.
+ *
+ * The pixel format is specified as RGBA32 when no conversion
+ * is used.
+ *
+ * Note that this only affects the 32-bit bit depth.
+ *
+ * Use non-zero value to enable conversion.
+ */
+static int vino_pixel_conversion;
+
+module_param_named(pixelconv, vino_pixel_conversion, int, 0);
+
+MODULE_PARM_DESC(pixelconv,
+ "enable pixel conversion (non-zero value enables)");
+
+/* Internal data structures */
+
+static struct sgi_vino *vino;
+
+static struct vino_settings *vino_drvdata;
+
+#define camera_call(o, f, args...) \
+ v4l2_subdev_call(vino_drvdata->camera, o, f, ##args)
+#define decoder_call(o, f, args...) \
+ v4l2_subdev_call(vino_drvdata->decoder, o, f, ##args)
+
+static const char *vino_driver_name = "vino";
+static const char *vino_driver_description = "SGI VINO";
+static const char *vino_bus_name = "GIO64 bus";
+static const char *vino_vdev_name_a = "SGI VINO Channel A";
+static const char *vino_vdev_name_b = "SGI VINO Channel B";
+
+static void vino_capture_tasklet(unsigned long channel);
+
+DECLARE_TASKLET(vino_tasklet_a, vino_capture_tasklet, VINO_CHANNEL_A);
+DECLARE_TASKLET(vino_tasklet_b, vino_capture_tasklet, VINO_CHANNEL_B);
+
+static const struct vino_input vino_inputs[] = {
+ {
+ .name = "Composite",
+ .std = V4L2_STD_NTSC | V4L2_STD_PAL
+ | V4L2_STD_SECAM,
+ }, {
+ .name = "S-Video",
+ .std = V4L2_STD_NTSC | V4L2_STD_PAL
+ | V4L2_STD_SECAM,
+ }, {
+ .name = "D1/IndyCam",
+ .std = V4L2_STD_NTSC,
+ }
+};
+
+static const struct vino_data_format vino_data_formats[] = {
+ {
+ .description = "8-bit greyscale",
+ .bpp = 1,
+ .pixelformat = V4L2_PIX_FMT_GREY,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ }, {
+ .description = "8-bit dithered RGB 3-3-2",
+ .bpp = 1,
+ .pixelformat = V4L2_PIX_FMT_RGB332,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ }, {
+ .description = "32-bit RGB",
+ .bpp = 4,
+ .pixelformat = V4L2_PIX_FMT_RGB32,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ }, {
+ .description = "YUV 4:2:2",
+ .bpp = 2,
+ .pixelformat = V4L2_PIX_FMT_YUYV, // XXX: swapped?
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ }
+};
+
+static const struct vino_data_norm vino_data_norms[] = {
+ {
+ .description = "NTSC",
+ .std = V4L2_STD_NTSC,
+ .fps_min = 6,
+ .fps_max = 30,
+ .framelines = 525,
+ .width = VINO_NTSC_WIDTH,
+ .height = VINO_NTSC_HEIGHT,
+ .odd = {
+ .top = VINO_CLIPPING_START_ODD_NTSC,
+ .left = 0,
+ .bottom = VINO_CLIPPING_START_ODD_NTSC
+ + VINO_NTSC_HEIGHT / 2 - 1,
+ .right = VINO_NTSC_WIDTH,
+ },
+ .even = {
+ .top = VINO_CLIPPING_START_EVEN_NTSC,
+ .left = 0,
+ .bottom = VINO_CLIPPING_START_EVEN_NTSC
+ + VINO_NTSC_HEIGHT / 2 - 1,
+ .right = VINO_NTSC_WIDTH,
+ },
+ }, {
+ .description = "PAL",
+ .std = V4L2_STD_PAL,
+ .fps_min = 5,
+ .fps_max = 25,
+ .framelines = 625,
+ .width = VINO_PAL_WIDTH,
+ .height = VINO_PAL_HEIGHT,
+ .odd = {
+ .top = VINO_CLIPPING_START_ODD_PAL,
+ .left = 0,
+ .bottom = VINO_CLIPPING_START_ODD_PAL
+ + VINO_PAL_HEIGHT / 2 - 1,
+ .right = VINO_PAL_WIDTH,
+ },
+ .even = {
+ .top = VINO_CLIPPING_START_EVEN_PAL,
+ .left = 0,
+ .bottom = VINO_CLIPPING_START_EVEN_PAL
+ + VINO_PAL_HEIGHT / 2 - 1,
+ .right = VINO_PAL_WIDTH,
+ },
+ }, {
+ .description = "SECAM",
+ .std = V4L2_STD_SECAM,
+ .fps_min = 5,
+ .fps_max = 25,
+ .framelines = 625,
+ .width = VINO_PAL_WIDTH,
+ .height = VINO_PAL_HEIGHT,
+ .odd = {
+ .top = VINO_CLIPPING_START_ODD_PAL,
+ .left = 0,
+ .bottom = VINO_CLIPPING_START_ODD_PAL
+ + VINO_PAL_HEIGHT / 2 - 1,
+ .right = VINO_PAL_WIDTH,
+ },
+ .even = {
+ .top = VINO_CLIPPING_START_EVEN_PAL,
+ .left = 0,
+ .bottom = VINO_CLIPPING_START_EVEN_PAL
+ + VINO_PAL_HEIGHT / 2 - 1,
+ .right = VINO_PAL_WIDTH,
+ },
+ }, {
+ .description = "NTSC/D1",
+ .std = V4L2_STD_NTSC,
+ .fps_min = 6,
+ .fps_max = 30,
+ .framelines = 525,
+ .width = VINO_NTSC_WIDTH,
+ .height = VINO_NTSC_HEIGHT,
+ .odd = {
+ .top = VINO_CLIPPING_START_ODD_D1,
+ .left = 0,
+ .bottom = VINO_CLIPPING_START_ODD_D1
+ + VINO_NTSC_HEIGHT / 2 - 1,
+ .right = VINO_NTSC_WIDTH,
+ },
+ .even = {
+ .top = VINO_CLIPPING_START_EVEN_D1,
+ .left = 0,
+ .bottom = VINO_CLIPPING_START_EVEN_D1
+ + VINO_NTSC_HEIGHT / 2 - 1,
+ .right = VINO_NTSC_WIDTH,
+ },
+ }
+};
+
+#define VINO_INDYCAM_V4L2_CONTROL_COUNT 9
+
+struct v4l2_queryctrl vino_indycam_v4l2_controls[] = {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Automatic Gain Control",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = INDYCAM_AGC_DEFAULT,
+ }, {
+ .id = V4L2_CID_AUTO_WHITE_BALANCE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Automatic White Balance",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = INDYCAM_AWB_DEFAULT,
+ }, {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = INDYCAM_GAIN_MIN,
+ .maximum = INDYCAM_GAIN_MAX,
+ .step = 1,
+ .default_value = INDYCAM_GAIN_DEFAULT,
+ }, {
+ .id = INDYCAM_CONTROL_RED_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Saturation",
+ .minimum = INDYCAM_RED_SATURATION_MIN,
+ .maximum = INDYCAM_RED_SATURATION_MAX,
+ .step = 1,
+ .default_value = INDYCAM_RED_SATURATION_DEFAULT,
+ }, {
+ .id = INDYCAM_CONTROL_BLUE_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Saturation",
+ .minimum = INDYCAM_BLUE_SATURATION_MIN,
+ .maximum = INDYCAM_BLUE_SATURATION_MAX,
+ .step = 1,
+ .default_value = INDYCAM_BLUE_SATURATION_DEFAULT,
+ }, {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Balance",
+ .minimum = INDYCAM_RED_BALANCE_MIN,
+ .maximum = INDYCAM_RED_BALANCE_MAX,
+ .step = 1,
+ .default_value = INDYCAM_RED_BALANCE_DEFAULT,
+ }, {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Balance",
+ .minimum = INDYCAM_BLUE_BALANCE_MIN,
+ .maximum = INDYCAM_BLUE_BALANCE_MAX,
+ .step = 1,
+ .default_value = INDYCAM_BLUE_BALANCE_DEFAULT,
+ }, {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Shutter Control",
+ .minimum = INDYCAM_SHUTTER_MIN,
+ .maximum = INDYCAM_SHUTTER_MAX,
+ .step = 1,
+ .default_value = INDYCAM_SHUTTER_DEFAULT,
+ }, {
+ .id = V4L2_CID_GAMMA,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gamma",
+ .minimum = INDYCAM_GAMMA_MIN,
+ .maximum = INDYCAM_GAMMA_MAX,
+ .step = 1,
+ .default_value = INDYCAM_GAMMA_DEFAULT,
+ }
+};
+
+#define VINO_SAA7191_V4L2_CONTROL_COUNT 9
+
+struct v4l2_queryctrl vino_saa7191_v4l2_controls[] = {
+ {
+ .id = V4L2_CID_HUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Hue",
+ .minimum = SAA7191_HUE_MIN,
+ .maximum = SAA7191_HUE_MAX,
+ .step = 1,
+ .default_value = SAA7191_HUE_DEFAULT,
+ }, {
+ .id = SAA7191_CONTROL_BANDPASS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Luminance Bandpass",
+ .minimum = SAA7191_BANDPASS_MIN,
+ .maximum = SAA7191_BANDPASS_MAX,
+ .step = 1,
+ .default_value = SAA7191_BANDPASS_DEFAULT,
+ }, {
+ .id = SAA7191_CONTROL_BANDPASS_WEIGHT,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Luminance Bandpass Weight",
+ .minimum = SAA7191_BANDPASS_WEIGHT_MIN,
+ .maximum = SAA7191_BANDPASS_WEIGHT_MAX,
+ .step = 1,
+ .default_value = SAA7191_BANDPASS_WEIGHT_DEFAULT,
+ }, {
+ .id = SAA7191_CONTROL_CORING,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "HF Luminance Coring",
+ .minimum = SAA7191_CORING_MIN,
+ .maximum = SAA7191_CORING_MAX,
+ .step = 1,
+ .default_value = SAA7191_CORING_DEFAULT,
+ }, {
+ .id = SAA7191_CONTROL_FORCE_COLOUR,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Force Colour",
+ .minimum = SAA7191_FORCE_COLOUR_MIN,
+ .maximum = SAA7191_FORCE_COLOUR_MAX,
+ .step = 1,
+ .default_value = SAA7191_FORCE_COLOUR_DEFAULT,
+ }, {
+ .id = SAA7191_CONTROL_CHROMA_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Chrominance Gain Control",
+ .minimum = SAA7191_CHROMA_GAIN_MIN,
+ .maximum = SAA7191_CHROMA_GAIN_MAX,
+ .step = 1,
+ .default_value = SAA7191_CHROMA_GAIN_DEFAULT,
+ }, {
+ .id = SAA7191_CONTROL_VTRC,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "VTR Time Constant",
+ .minimum = SAA7191_VTRC_MIN,
+ .maximum = SAA7191_VTRC_MAX,
+ .step = 1,
+ .default_value = SAA7191_VTRC_DEFAULT,
+ }, {
+ .id = SAA7191_CONTROL_LUMA_DELAY,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Luminance Delay Compensation",
+ .minimum = SAA7191_LUMA_DELAY_MIN,
+ .maximum = SAA7191_LUMA_DELAY_MAX,
+ .step = 1,
+ .default_value = SAA7191_LUMA_DELAY_DEFAULT,
+ }, {
+ .id = SAA7191_CONTROL_VNR,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Vertical Noise Reduction",
+ .minimum = SAA7191_VNR_MIN,
+ .maximum = SAA7191_VNR_MAX,
+ .step = 1,
+ .default_value = SAA7191_VNR_DEFAULT,
+ }
+};
+
+/* VINO framebuffer/DMA descriptor management */
+
+static void vino_free_buffer_with_count(struct vino_framebuffer *fb,
+ unsigned int count)
+{
+ unsigned int i;
+
+ dprintk("vino_free_buffer_with_count(): count = %d\n", count);
+
+ for (i = 0; i < count; i++) {
+ ClearPageReserved(virt_to_page((void *)fb->desc_table.virtual[i]));
+ dma_unmap_single(NULL,
+ fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i],
+ PAGE_SIZE, DMA_FROM_DEVICE);
+ free_page(fb->desc_table.virtual[i]);
+ }
+
+ dma_free_coherent(NULL,
+ VINO_PAGE_RATIO * (fb->desc_table.page_count + 4) *
+ sizeof(dma_addr_t), (void *)fb->desc_table.dma_cpu,
+ fb->desc_table.dma);
+ kfree(fb->desc_table.virtual);
+
+ memset(fb, 0, sizeof(struct vino_framebuffer));
+}
+
+static void vino_free_buffer(struct vino_framebuffer *fb)
+{
+ vino_free_buffer_with_count(fb, fb->desc_table.page_count);
+}
+
+static int vino_allocate_buffer(struct vino_framebuffer *fb,
+ unsigned int size)
+{
+ unsigned int count, i, j;
+ int ret = 0;
+
+ dprintk("vino_allocate_buffer():\n");
+
+ if (size < 1)
+ return -EINVAL;
+
+ memset(fb, 0, sizeof(struct vino_framebuffer));
+
+ count = ((size / PAGE_SIZE) + 4) & ~3;
+
+ dprintk("vino_allocate_buffer(): size = %d, count = %d\n",
+ size, count);
+
+ /* allocate memory for table with virtual (page) addresses */
+ fb->desc_table.virtual =
+ kmalloc(count * sizeof(unsigned long), GFP_KERNEL);
+ if (!fb->desc_table.virtual)
+ return -ENOMEM;
+
+ /* allocate memory for table with dma addresses
+ * (has space for four extra descriptors) */
+ fb->desc_table.dma_cpu =
+ dma_alloc_coherent(NULL, VINO_PAGE_RATIO * (count + 4) *
+ sizeof(dma_addr_t), &fb->desc_table.dma,
+ GFP_KERNEL | GFP_DMA);
+ if (!fb->desc_table.dma_cpu) {
+ ret = -ENOMEM;
+ goto out_free_virtual;
+ }
+
+ /* allocate pages for the buffer and acquire the according
+ * dma addresses */
+ for (i = 0; i < count; i++) {
+ dma_addr_t dma_data_addr;
+
+ fb->desc_table.virtual[i] =
+ get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!fb->desc_table.virtual[i]) {
+ ret = -ENOBUFS;
+ break;
+ }
+
+ dma_data_addr =
+ dma_map_single(NULL,
+ (void *)fb->desc_table.virtual[i],
+ PAGE_SIZE, DMA_FROM_DEVICE);
+
+ for (j = 0; j < VINO_PAGE_RATIO; j++) {
+ fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i + j] =
+ dma_data_addr + VINO_PAGE_SIZE * j;
+ }
+
+ SetPageReserved(virt_to_page((void *)fb->desc_table.virtual[i]));
+ }
+
+ /* page_count needs to be set anyway, because the descriptor table has
+ * been allocated according to this number */
+ fb->desc_table.page_count = count;
+
+ if (ret) {
+ /* the descriptor with index i doesn't contain
+ * a valid address yet */
+ vino_free_buffer_with_count(fb, i);
+ return ret;
+ }
+
+ //fb->size = size;
+ fb->size = count * PAGE_SIZE;
+ fb->data_format = VINO_DATA_FMT_NONE;
+
+ /* set the dma stop-bit for the last (count+1)th descriptor */
+ fb->desc_table.dma_cpu[VINO_PAGE_RATIO * count] = VINO_DESC_STOP;
+ return 0;
+
+ out_free_virtual:
+ kfree(fb->desc_table.virtual);
+ return ret;
+}
+
+#if 0
+/* user buffers not fully implemented yet */
+static int vino_prepare_user_buffer(struct vino_framebuffer *fb,
+ void *user,
+ unsigned int size)
+{
+ unsigned int count, i, j;
+ int ret = 0;
+
+ dprintk("vino_prepare_user_buffer():\n");
+
+ if (size < 1)
+ return -EINVAL;
+
+ memset(fb, 0, sizeof(struct vino_framebuffer));
+
+ count = ((size / PAGE_SIZE)) & ~3;
+
+ dprintk("vino_prepare_user_buffer(): size = %d, count = %d\n",
+ size, count);
+
+ /* allocate memory for table with virtual (page) addresses */
+ fb->desc_table.virtual = (unsigned long *)
+ kmalloc(count * sizeof(unsigned long), GFP_KERNEL);
+ if (!fb->desc_table.virtual)
+ return -ENOMEM;
+
+ /* allocate memory for table with dma addresses
+ * (has space for four extra descriptors) */
+ fb->desc_table.dma_cpu =
+ dma_alloc_coherent(NULL, VINO_PAGE_RATIO * (count + 4) *
+ sizeof(dma_addr_t), &fb->desc_table.dma,
+ GFP_KERNEL | GFP_DMA);
+ if (!fb->desc_table.dma_cpu) {
+ ret = -ENOMEM;
+ goto out_free_virtual;
+ }
+
+ /* allocate pages for the buffer and acquire the according
+ * dma addresses */
+ for (i = 0; i < count; i++) {
+ dma_addr_t dma_data_addr;
+
+ fb->desc_table.virtual[i] =
+ get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!fb->desc_table.virtual[i]) {
+ ret = -ENOBUFS;
+ break;
+ }
+
+ dma_data_addr =
+ dma_map_single(NULL,
+ (void *)fb->desc_table.virtual[i],
+ PAGE_SIZE, DMA_FROM_DEVICE);
+
+ for (j = 0; j < VINO_PAGE_RATIO; j++) {
+ fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i + j] =
+ dma_data_addr + VINO_PAGE_SIZE * j;
+ }
+
+ SetPageReserved(virt_to_page((void *)fb->desc_table.virtual[i]));
+ }
+
+ /* page_count needs to be set anyway, because the descriptor table has
+ * been allocated according to this number */
+ fb->desc_table.page_count = count;
+
+ if (ret) {
+ /* the descriptor with index i doesn't contain
+ * a valid address yet */
+ vino_free_buffer_with_count(fb, i);
+ return ret;
+ }
+
+ //fb->size = size;
+ fb->size = count * PAGE_SIZE;
+
+ /* set the dma stop-bit for the last (count+1)th descriptor */
+ fb->desc_table.dma_cpu[VINO_PAGE_RATIO * count] = VINO_DESC_STOP;
+ return 0;
+
+ out_free_virtual:
+ kfree(fb->desc_table.virtual);
+ return ret;
+}
+#endif
+
+static void vino_sync_buffer(struct vino_framebuffer *fb)
+{
+ int i;
+
+ dprintk("vino_sync_buffer():\n");
+
+ for (i = 0; i < fb->desc_table.page_count; i++)
+ dma_sync_single_for_cpu(NULL,
+ fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i],
+ PAGE_SIZE, DMA_FROM_DEVICE);
+}
+
+/* Framebuffer fifo functions (need to be locked externally) */
+
+static inline void vino_fifo_init(struct vino_framebuffer_fifo *f,
+ unsigned int length)
+{
+ f->length = 0;
+ f->used = 0;
+ f->head = 0;
+ f->tail = 0;
+
+ if (length > VINO_FRAMEBUFFER_COUNT_MAX)
+ length = VINO_FRAMEBUFFER_COUNT_MAX;
+
+ f->length = length;
+}
+
+/* returns true/false */
+static inline int vino_fifo_has_id(struct vino_framebuffer_fifo *f,
+ unsigned int id)
+{
+ unsigned int i;
+
+ for (i = f->head; i == (f->tail - 1); i = (i + 1) % f->length) {
+ if (f->data[i] == id)
+ return 1;
+ }
+
+ return 0;
+}
+
+#if 0
+/* returns true/false */
+static inline int vino_fifo_full(struct vino_framebuffer_fifo *f)
+{
+ return (f->used == f->length);
+}
+#endif
+
+static inline unsigned int vino_fifo_get_used(struct vino_framebuffer_fifo *f)
+{
+ return f->used;
+}
+
+static int vino_fifo_enqueue(struct vino_framebuffer_fifo *f, unsigned int id)
+{
+ if (id >= f->length) {
+ return VINO_QUEUE_ERROR;
+ }
+
+ if (vino_fifo_has_id(f, id)) {
+ return VINO_QUEUE_ERROR;
+ }
+
+ if (f->used < f->length) {
+ f->data[f->tail] = id;
+ f->tail = (f->tail + 1) % f->length;
+ f->used++;
+ } else {
+ return VINO_QUEUE_ERROR;
+ }
+
+ return 0;
+}
+
+static int vino_fifo_peek(struct vino_framebuffer_fifo *f, unsigned int *id)
+{
+ if (f->used > 0) {
+ *id = f->data[f->head];
+ } else {
+ return VINO_QUEUE_ERROR;
+ }
+
+ return 0;
+}
+
+static int vino_fifo_dequeue(struct vino_framebuffer_fifo *f, unsigned int *id)
+{
+ if (f->used > 0) {
+ *id = f->data[f->head];
+ f->head = (f->head + 1) % f->length;
+ f->used--;
+ } else {
+ return VINO_QUEUE_ERROR;
+ }
+
+ return 0;
+}
+
+/* Framebuffer queue functions */
+
+/* execute with queue_lock locked */
+static void vino_queue_free_with_count(struct vino_framebuffer_queue *q,
+ unsigned int length)
+{
+ unsigned int i;
+
+ q->length = 0;
+ memset(&q->in, 0, sizeof(struct vino_framebuffer_fifo));
+ memset(&q->out, 0, sizeof(struct vino_framebuffer_fifo));
+ for (i = 0; i < length; i++) {
+ dprintk("vino_queue_free_with_count(): freeing buffer %d\n",
+ i);
+ vino_free_buffer(q->buffer[i]);
+ kfree(q->buffer[i]);
+ }
+
+ q->type = VINO_MEMORY_NONE;
+ q->magic = 0;
+}
+
+static void vino_queue_free(struct vino_framebuffer_queue *q)
+{
+ dprintk("vino_queue_free():\n");
+
+ if (q->magic != VINO_QUEUE_MAGIC)
+ return;
+ if (q->type != VINO_MEMORY_MMAP)
+ return;
+
+ mutex_lock(&q->queue_mutex);
+
+ vino_queue_free_with_count(q, q->length);
+
+ mutex_unlock(&q->queue_mutex);
+}
+
+static int vino_queue_init(struct vino_framebuffer_queue *q,
+ unsigned int *length)
+{
+ unsigned int i;
+ int ret = 0;
+
+ dprintk("vino_queue_init(): length = %d\n", *length);
+
+ if (q->magic == VINO_QUEUE_MAGIC) {
+ dprintk("vino_queue_init(): queue already initialized!\n");
+ return -EINVAL;
+ }
+
+ if (q->type != VINO_MEMORY_NONE) {
+ dprintk("vino_queue_init(): queue already initialized!\n");
+ return -EINVAL;
+ }
+
+ if (*length < 1)
+ return -EINVAL;
+
+ mutex_lock(&q->queue_mutex);
+
+ if (*length > VINO_FRAMEBUFFER_COUNT_MAX)
+ *length = VINO_FRAMEBUFFER_COUNT_MAX;
+
+ q->length = 0;
+
+ for (i = 0; i < *length; i++) {
+ dprintk("vino_queue_init(): allocating buffer %d\n", i);
+ q->buffer[i] = kmalloc(sizeof(struct vino_framebuffer),
+ GFP_KERNEL);
+ if (!q->buffer[i]) {
+ dprintk("vino_queue_init(): kmalloc() failed\n");
+ ret = -ENOMEM;
+ break;
+ }
+
+ ret = vino_allocate_buffer(q->buffer[i],
+ VINO_FRAMEBUFFER_SIZE);
+ if (ret) {
+ kfree(q->buffer[i]);
+ dprintk("vino_queue_init(): "
+ "vino_allocate_buffer() failed\n");
+ break;
+ }
+
+ q->buffer[i]->id = i;
+ if (i > 0) {
+ q->buffer[i]->offset = q->buffer[i - 1]->offset +
+ q->buffer[i - 1]->size;
+ } else {
+ q->buffer[i]->offset = 0;
+ }
+
+ spin_lock_init(&q->buffer[i]->state_lock);
+
+ dprintk("vino_queue_init(): buffer = %d, offset = %d, "
+ "size = %d\n", i, q->buffer[i]->offset,
+ q->buffer[i]->size);
+ }
+
+ if (ret) {
+ vino_queue_free_with_count(q, i);
+ *length = 0;
+ } else {
+ q->length = *length;
+ vino_fifo_init(&q->in, q->length);
+ vino_fifo_init(&q->out, q->length);
+ q->type = VINO_MEMORY_MMAP;
+ q->magic = VINO_QUEUE_MAGIC;
+ }
+
+ mutex_unlock(&q->queue_mutex);
+
+ return ret;
+}
+
+static struct vino_framebuffer *vino_queue_add(struct
+ vino_framebuffer_queue *q,
+ unsigned int id)
+{
+ struct vino_framebuffer *ret = NULL;
+ unsigned int total;
+ unsigned long flags;
+
+ dprintk("vino_queue_add(): id = %d\n", id);
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return ret;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0)
+ goto out;
+
+ if (id >= q->length)
+ goto out;
+
+ /* not needed?: if (vino_fifo_full(&q->out)) {
+ goto out;
+ }*/
+ /* check that outgoing queue isn't already full
+ * (or that it won't become full) */
+ total = vino_fifo_get_used(&q->in) +
+ vino_fifo_get_used(&q->out);
+ if (total >= q->length)
+ goto out;
+
+ if (vino_fifo_enqueue(&q->in, id))
+ goto out;
+
+ ret = q->buffer[id];
+
+out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+static struct vino_framebuffer *vino_queue_transfer(struct
+ vino_framebuffer_queue *q)
+{
+ struct vino_framebuffer *ret = NULL;
+ struct vino_framebuffer *fb;
+ int id;
+ unsigned long flags;
+
+ dprintk("vino_queue_transfer():\n");
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return ret;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0)
+ goto out;
+
+ // now this actually removes an entry from the incoming queue
+ if (vino_fifo_dequeue(&q->in, &id)) {
+ goto out;
+ }
+
+ dprintk("vino_queue_transfer(): id = %d\n", id);
+ fb = q->buffer[id];
+
+ // we have already checked that the outgoing queue is not full, but...
+ if (vino_fifo_enqueue(&q->out, id)) {
+ printk(KERN_ERR "vino_queue_transfer(): "
+ "outgoing queue is full, this shouldn't happen!\n");
+ goto out;
+ }
+
+ ret = fb;
+out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+/* returns true/false */
+static int vino_queue_incoming_contains(struct vino_framebuffer_queue *q,
+ unsigned int id)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return ret;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0)
+ goto out;
+
+ ret = vino_fifo_has_id(&q->in, id);
+
+out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+/* returns true/false */
+static int vino_queue_outgoing_contains(struct vino_framebuffer_queue *q,
+ unsigned int id)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return ret;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0)
+ goto out;
+
+ ret = vino_fifo_has_id(&q->out, id);
+
+out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+static int vino_queue_get_incoming(struct vino_framebuffer_queue *q,
+ unsigned int *used)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return VINO_QUEUE_ERROR;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0) {
+ ret = VINO_QUEUE_ERROR;
+ goto out;
+ }
+
+ *used = vino_fifo_get_used(&q->in);
+
+out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+static int vino_queue_get_outgoing(struct vino_framebuffer_queue *q,
+ unsigned int *used)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return VINO_QUEUE_ERROR;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0) {
+ ret = VINO_QUEUE_ERROR;
+ goto out;
+ }
+
+ *used = vino_fifo_get_used(&q->out);
+
+out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+#if 0
+static int vino_queue_get_total(struct vino_framebuffer_queue *q,
+ unsigned int *total)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return VINO_QUEUE_ERROR;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0) {
+ ret = VINO_QUEUE_ERROR;
+ goto out;
+ }
+
+ *total = vino_fifo_get_used(&q->in) +
+ vino_fifo_get_used(&q->out);
+
+out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+#endif
+
+static struct vino_framebuffer *vino_queue_peek(struct
+ vino_framebuffer_queue *q,
+ unsigned int *id)
+{
+ struct vino_framebuffer *ret = NULL;
+ unsigned long flags;
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return ret;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0)
+ goto out;
+
+ if (vino_fifo_peek(&q->in, id)) {
+ goto out;
+ }
+
+ ret = q->buffer[*id];
+out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+static struct vino_framebuffer *vino_queue_remove(struct
+ vino_framebuffer_queue *q,
+ unsigned int *id)
+{
+ struct vino_framebuffer *ret = NULL;
+ unsigned long flags;
+ dprintk("vino_queue_remove():\n");
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return ret;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0)
+ goto out;
+
+ if (vino_fifo_dequeue(&q->out, id)) {
+ goto out;
+ }
+
+ dprintk("vino_queue_remove(): id = %d\n", *id);
+ ret = q->buffer[*id];
+out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+static struct
+vino_framebuffer *vino_queue_get_buffer(struct vino_framebuffer_queue *q,
+ unsigned int id)
+{
+ struct vino_framebuffer *ret = NULL;
+ unsigned long flags;
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return ret;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0)
+ goto out;
+
+ if (id >= q->length)
+ goto out;
+
+ ret = q->buffer[id];
+ out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+static unsigned int vino_queue_get_length(struct vino_framebuffer_queue *q)
+{
+ unsigned int length = 0;
+ unsigned long flags;
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return length;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+ length = q->length;
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return length;
+}
+
+static int vino_queue_has_mapped_buffers(struct vino_framebuffer_queue *q)
+{
+ unsigned int i;
+ int ret = 0;
+ unsigned long flags;
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return ret;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+ for (i = 0; i < q->length; i++) {
+ if (q->buffer[i]->map_count > 0) {
+ ret = 1;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+/* VINO functions */
+
+/* execute with input_lock locked */
+static void vino_update_line_size(struct vino_channel_settings *vcs)
+{
+ unsigned int w = vcs->clipping.right - vcs->clipping.left;
+ unsigned int d = vcs->decimation;
+ unsigned int bpp = vino_data_formats[vcs->data_format].bpp;
+ unsigned int lsize;
+
+ dprintk("update_line_size(): before: w = %d, d = %d, "
+ "line_size = %d\n", w, d, vcs->line_size);
+
+ /* line size must be multiple of 8 bytes */
+ lsize = (bpp * (w / d)) & ~7;
+ w = (lsize / bpp) * d;
+
+ vcs->clipping.right = vcs->clipping.left + w;
+ vcs->line_size = lsize;
+
+ dprintk("update_line_size(): after: w = %d, d = %d, "
+ "line_size = %d\n", w, d, vcs->line_size);
+}
+
+/* execute with input_lock locked */
+static void vino_set_clipping(struct vino_channel_settings *vcs,
+ unsigned int x, unsigned int y,
+ unsigned int w, unsigned int h)
+{
+ unsigned int maxwidth, maxheight;
+ unsigned int d;
+
+ maxwidth = vino_data_norms[vcs->data_norm].width;
+ maxheight = vino_data_norms[vcs->data_norm].height;
+ d = vcs->decimation;
+
+ y &= ~1; /* odd/even fields */
+
+ if (x > maxwidth) {
+ x = 0;
+ }
+ if (y > maxheight) {
+ y = 0;
+ }
+
+ if (((w / d) < VINO_MIN_WIDTH)
+ || ((h / d) < VINO_MIN_HEIGHT)) {
+ w = VINO_MIN_WIDTH * d;
+ h = VINO_MIN_HEIGHT * d;
+ }
+
+ if ((x + w) > maxwidth) {
+ w = maxwidth - x;
+ if ((w / d) < VINO_MIN_WIDTH)
+ x = maxwidth - VINO_MIN_WIDTH * d;
+ }
+ if ((y + h) > maxheight) {
+ h = maxheight - y;
+ if ((h / d) < VINO_MIN_HEIGHT)
+ y = maxheight - VINO_MIN_HEIGHT * d;
+ }
+
+ vcs->clipping.left = x;
+ vcs->clipping.top = y;
+ vcs->clipping.right = x + w;
+ vcs->clipping.bottom = y + h;
+
+ vino_update_line_size(vcs);
+
+ dprintk("clipping %d, %d, %d, %d / %d - %d\n",
+ vcs->clipping.left, vcs->clipping.top, vcs->clipping.right,
+ vcs->clipping.bottom, vcs->decimation, vcs->line_size);
+}
+
+/* execute with input_lock locked */
+static inline void vino_set_default_clipping(struct vino_channel_settings *vcs)
+{
+ vino_set_clipping(vcs, 0, 0, vino_data_norms[vcs->data_norm].width,
+ vino_data_norms[vcs->data_norm].height);
+}
+
+/* execute with input_lock locked */
+static void vino_set_scaling(struct vino_channel_settings *vcs,
+ unsigned int w, unsigned int h)
+{
+ unsigned int x, y, curw, curh, d;
+
+ x = vcs->clipping.left;
+ y = vcs->clipping.top;
+ curw = vcs->clipping.right - vcs->clipping.left;
+ curh = vcs->clipping.bottom - vcs->clipping.top;
+
+ d = max(curw / w, curh / h);
+
+ dprintk("scaling w: %d, h: %d, curw: %d, curh: %d, d: %d\n",
+ w, h, curw, curh, d);
+
+ if (d < 1) {
+ d = 1;
+ } else if (d > 8) {
+ d = 8;
+ }
+
+ vcs->decimation = d;
+ vino_set_clipping(vcs, x, y, w * d, h * d);
+
+ dprintk("scaling %d, %d, %d, %d / %d - %d\n", vcs->clipping.left,
+ vcs->clipping.top, vcs->clipping.right, vcs->clipping.bottom,
+ vcs->decimation, vcs->line_size);
+}
+
+/* execute with input_lock locked */
+static inline void vino_set_default_scaling(struct vino_channel_settings *vcs)
+{
+ vino_set_scaling(vcs, vcs->clipping.right - vcs->clipping.left,
+ vcs->clipping.bottom - vcs->clipping.top);
+}
+
+/* execute with input_lock locked */
+static void vino_set_framerate(struct vino_channel_settings *vcs,
+ unsigned int fps)
+{
+ unsigned int mask;
+
+ switch (vcs->data_norm) {
+ case VINO_DATA_NORM_NTSC:
+ case VINO_DATA_NORM_D1:
+ fps = (unsigned int)(fps / 6) * 6; // FIXME: round!
+
+ if (fps < vino_data_norms[vcs->data_norm].fps_min)
+ fps = vino_data_norms[vcs->data_norm].fps_min;
+ if (fps > vino_data_norms[vcs->data_norm].fps_max)
+ fps = vino_data_norms[vcs->data_norm].fps_max;
+
+ switch (fps) {
+ case 6:
+ mask = 0x003;
+ break;
+ case 12:
+ mask = 0x0c3;
+ break;
+ case 18:
+ mask = 0x333;
+ break;
+ case 24:
+ mask = 0x3ff;
+ break;
+ case 30:
+ mask = 0xfff;
+ break;
+ default:
+ mask = VINO_FRAMERT_FULL;
+ }
+ vcs->framert_reg = VINO_FRAMERT_RT(mask);
+ break;
+ case VINO_DATA_NORM_PAL:
+ case VINO_DATA_NORM_SECAM:
+ fps = (unsigned int)(fps / 5) * 5; // FIXME: round!
+
+ if (fps < vino_data_norms[vcs->data_norm].fps_min)
+ fps = vino_data_norms[vcs->data_norm].fps_min;
+ if (fps > vino_data_norms[vcs->data_norm].fps_max)
+ fps = vino_data_norms[vcs->data_norm].fps_max;
+
+ switch (fps) {
+ case 5:
+ mask = 0x003;
+ break;
+ case 10:
+ mask = 0x0c3;
+ break;
+ case 15:
+ mask = 0x333;
+ break;
+ case 20:
+ mask = 0x0ff;
+ break;
+ case 25:
+ mask = 0x3ff;
+ break;
+ default:
+ mask = VINO_FRAMERT_FULL;
+ }
+ vcs->framert_reg = VINO_FRAMERT_RT(mask) | VINO_FRAMERT_PAL;
+ break;
+ }
+
+ vcs->fps = fps;
+}
+
+/* execute with input_lock locked */
+static inline void vino_set_default_framerate(struct
+ vino_channel_settings *vcs)
+{
+ vino_set_framerate(vcs, vino_data_norms[vcs->data_norm].fps_max);
+}
+
+/* VINO I2C bus functions */
+
+struct i2c_algo_sgi_data {
+ void *data; /* private data for lowlevel routines */
+ unsigned (*getctrl)(void *data);
+ void (*setctrl)(void *data, unsigned val);
+ unsigned (*rdata)(void *data);
+ void (*wdata)(void *data, unsigned val);
+
+ int xfer_timeout;
+ int ack_timeout;
+};
+
+static int wait_xfer_done(struct i2c_algo_sgi_data *adap)
+{
+ int i;
+
+ for (i = 0; i < adap->xfer_timeout; i++) {
+ if ((adap->getctrl(adap->data) & SGI_I2C_XFER_BUSY) == 0)
+ return 0;
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int wait_ack(struct i2c_algo_sgi_data *adap)
+{
+ int i;
+
+ if (wait_xfer_done(adap))
+ return -ETIMEDOUT;
+ for (i = 0; i < adap->ack_timeout; i++) {
+ if ((adap->getctrl(adap->data) & SGI_I2C_NACK) == 0)
+ return 0;
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int force_idle(struct i2c_algo_sgi_data *adap)
+{
+ int i;
+
+ adap->setctrl(adap->data, SGI_I2C_FORCE_IDLE);
+ for (i = 0; i < adap->xfer_timeout; i++) {
+ if ((adap->getctrl(adap->data) & SGI_I2C_NOT_IDLE) == 0)
+ goto out;
+ udelay(1);
+ }
+ return -ETIMEDOUT;
+out:
+ if (adap->getctrl(adap->data) & SGI_I2C_BUS_ERR)
+ return -EIO;
+ return 0;
+}
+
+static int do_address(struct i2c_algo_sgi_data *adap, unsigned int addr,
+ int rd)
+{
+ if (rd)
+ adap->setctrl(adap->data, SGI_I2C_NOT_IDLE);
+ /* Check if bus is idle, eventually force it to do so */
+ if (adap->getctrl(adap->data) & SGI_I2C_NOT_IDLE)
+ if (force_idle(adap))
+ return -EIO;
+ /* Write out the i2c chip address and specify operation */
+ adap->setctrl(adap->data,
+ SGI_I2C_HOLD_BUS | SGI_I2C_WRITE | SGI_I2C_NOT_IDLE);
+ if (rd)
+ addr |= 1;
+ adap->wdata(adap->data, addr);
+ if (wait_ack(adap))
+ return -EIO;
+ return 0;
+}
+
+static int i2c_read(struct i2c_algo_sgi_data *adap, unsigned char *buf,
+ unsigned int len)
+{
+ int i;
+
+ adap->setctrl(adap->data,
+ SGI_I2C_HOLD_BUS | SGI_I2C_READ | SGI_I2C_NOT_IDLE);
+ for (i = 0; i < len; i++) {
+ if (wait_xfer_done(adap))
+ return -EIO;
+ buf[i] = adap->rdata(adap->data);
+ }
+ adap->setctrl(adap->data, SGI_I2C_RELEASE_BUS | SGI_I2C_FORCE_IDLE);
+
+ return 0;
+
+}
+
+static int i2c_write(struct i2c_algo_sgi_data *adap, unsigned char *buf,
+ unsigned int len)
+{
+ int i;
+
+ /* We are already in write state */
+ for (i = 0; i < len; i++) {
+ adap->wdata(adap->data, buf[i]);
+ if (wait_ack(adap))
+ return -EIO;
+ }
+ return 0;
+}
+
+static int sgi_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
+ int num)
+{
+ struct i2c_algo_sgi_data *adap = i2c_adap->algo_data;
+ struct i2c_msg *p;
+ int i, err = 0;
+
+ for (i = 0; !err && i < num; i++) {
+ p = &msgs[i];
+ err = do_address(adap, p->addr, p->flags & I2C_M_RD);
+ if (err || !p->len)
+ continue;
+ if (p->flags & I2C_M_RD)
+ err = i2c_read(adap, p->buf, p->len);
+ else
+ err = i2c_write(adap, p->buf, p->len);
+ }
+
+ return (err < 0) ? err : i;
+}
+
+static u32 sgi_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm sgi_algo = {
+ .master_xfer = sgi_xfer,
+ .functionality = sgi_func,
+};
+
+static unsigned i2c_vino_getctrl(void *data)
+{
+ return vino->i2c_control;
+}
+
+static void i2c_vino_setctrl(void *data, unsigned val)
+{
+ vino->i2c_control = val;
+}
+
+static unsigned i2c_vino_rdata(void *data)
+{
+ return vino->i2c_data;
+}
+
+static void i2c_vino_wdata(void *data, unsigned val)
+{
+ vino->i2c_data = val;
+}
+
+static struct i2c_algo_sgi_data i2c_sgi_vino_data = {
+ .getctrl = &i2c_vino_getctrl,
+ .setctrl = &i2c_vino_setctrl,
+ .rdata = &i2c_vino_rdata,
+ .wdata = &i2c_vino_wdata,
+ .xfer_timeout = 200,
+ .ack_timeout = 1000,
+};
+
+static struct i2c_adapter vino_i2c_adapter = {
+ .name = "VINO I2C bus",
+ .algo = &sgi_algo,
+ .algo_data = &i2c_sgi_vino_data,
+ .owner = THIS_MODULE,
+};
+
+/*
+ * Prepare VINO for DMA transfer...
+ * (execute only with vino_lock and input_lock locked)
+ */
+static int vino_dma_setup(struct vino_channel_settings *vcs,
+ struct vino_framebuffer *fb)
+{
+ u32 ctrl, intr;
+ struct sgi_vino_channel *ch;
+ const struct vino_data_norm *norm;
+
+ dprintk("vino_dma_setup():\n");
+
+ vcs->field = 0;
+ fb->frame_counter = 0;
+
+ ch = (vcs->channel == VINO_CHANNEL_A) ? &vino->a : &vino->b;
+ norm = &vino_data_norms[vcs->data_norm];
+
+ ch->page_index = 0;
+ ch->line_count = 0;
+
+ /* VINO line size register is set 8 bytes less than actual */
+ ch->line_size = vcs->line_size - 8;
+
+ /* let VINO know where to transfer data */
+ ch->start_desc_tbl = fb->desc_table.dma;
+ ch->next_4_desc = fb->desc_table.dma;
+
+ /* give vino time to fetch the first four descriptors, 5 usec
+ * should be more than enough time */
+ udelay(VINO_DESC_FETCH_DELAY);
+
+ dprintk("vino_dma_setup(): start desc = %08x, next 4 desc = %08x\n",
+ ch->start_desc_tbl, ch->next_4_desc);
+
+ /* set the alpha register */
+ ch->alpha = vcs->alpha;
+
+ /* set clipping registers */
+ ch->clip_start = VINO_CLIP_ODD(norm->odd.top + vcs->clipping.top / 2) |
+ VINO_CLIP_EVEN(norm->even.top +
+ vcs->clipping.top / 2) |
+ VINO_CLIP_X(vcs->clipping.left);
+ ch->clip_end = VINO_CLIP_ODD(norm->odd.top +
+ vcs->clipping.bottom / 2 - 1) |
+ VINO_CLIP_EVEN(norm->even.top +
+ vcs->clipping.bottom / 2 - 1) |
+ VINO_CLIP_X(vcs->clipping.right);
+
+ /* set the size of actual content in the buffer (DECIMATION !) */
+ fb->data_size = ((vcs->clipping.right - vcs->clipping.left) /
+ vcs->decimation) *
+ ((vcs->clipping.bottom - vcs->clipping.top) /
+ vcs->decimation) *
+ vino_data_formats[vcs->data_format].bpp;
+
+ ch->frame_rate = vcs->framert_reg;
+
+ ctrl = vino->control;
+ intr = vino->intr_status;
+
+ if (vcs->channel == VINO_CHANNEL_A) {
+ /* All interrupt conditions for this channel was cleared
+ * so clear the interrupt status register and enable
+ * interrupts */
+ intr &= ~VINO_INTSTAT_A;
+ ctrl |= VINO_CTRL_A_INT;
+
+ /* enable synchronization */
+ ctrl |= VINO_CTRL_A_SYNC_ENBL;
+
+ /* enable frame assembly */
+ ctrl |= VINO_CTRL_A_INTERLEAVE_ENBL;
+
+ /* set decimation used */
+ if (vcs->decimation < 2)
+ ctrl &= ~VINO_CTRL_A_DEC_ENBL;
+ else {
+ ctrl |= VINO_CTRL_A_DEC_ENBL;
+ ctrl &= ~VINO_CTRL_A_DEC_SCALE_MASK;
+ ctrl |= (vcs->decimation - 1) <<
+ VINO_CTRL_A_DEC_SCALE_SHIFT;
+ }
+
+ /* select input interface */
+ if (vcs->input == VINO_INPUT_D1)
+ ctrl |= VINO_CTRL_A_SELECT;
+ else
+ ctrl &= ~VINO_CTRL_A_SELECT;
+
+ /* palette */
+ ctrl &= ~(VINO_CTRL_A_LUMA_ONLY | VINO_CTRL_A_RGB |
+ VINO_CTRL_A_DITHER);
+ } else {
+ intr &= ~VINO_INTSTAT_B;
+ ctrl |= VINO_CTRL_B_INT;
+
+ ctrl |= VINO_CTRL_B_SYNC_ENBL;
+ ctrl |= VINO_CTRL_B_INTERLEAVE_ENBL;
+
+ if (vcs->decimation < 2)
+ ctrl &= ~VINO_CTRL_B_DEC_ENBL;
+ else {
+ ctrl |= VINO_CTRL_B_DEC_ENBL;
+ ctrl &= ~VINO_CTRL_B_DEC_SCALE_MASK;
+ ctrl |= (vcs->decimation - 1) <<
+ VINO_CTRL_B_DEC_SCALE_SHIFT;
+
+ }
+ if (vcs->input == VINO_INPUT_D1)
+ ctrl |= VINO_CTRL_B_SELECT;
+ else
+ ctrl &= ~VINO_CTRL_B_SELECT;
+
+ ctrl &= ~(VINO_CTRL_B_LUMA_ONLY | VINO_CTRL_B_RGB |
+ VINO_CTRL_B_DITHER);
+ }
+
+ /* set palette */
+ fb->data_format = vcs->data_format;
+
+ switch (vcs->data_format) {
+ case VINO_DATA_FMT_GREY:
+ ctrl |= (vcs->channel == VINO_CHANNEL_A) ?
+ VINO_CTRL_A_LUMA_ONLY : VINO_CTRL_B_LUMA_ONLY;
+ break;
+ case VINO_DATA_FMT_RGB32:
+ ctrl |= (vcs->channel == VINO_CHANNEL_A) ?
+ VINO_CTRL_A_RGB : VINO_CTRL_B_RGB;
+ break;
+ case VINO_DATA_FMT_YUV:
+ /* nothing needs to be done */
+ break;
+ case VINO_DATA_FMT_RGB332:
+ ctrl |= (vcs->channel == VINO_CHANNEL_A) ?
+ VINO_CTRL_A_RGB | VINO_CTRL_A_DITHER :
+ VINO_CTRL_B_RGB | VINO_CTRL_B_DITHER;
+ break;
+ }
+
+ vino->intr_status = intr;
+ vino->control = ctrl;
+
+ return 0;
+}
+
+/* (execute only with vino_lock locked) */
+static inline void vino_dma_start(struct vino_channel_settings *vcs)
+{
+ u32 ctrl = vino->control;
+
+ dprintk("vino_dma_start():\n");
+ ctrl |= (vcs->channel == VINO_CHANNEL_A) ?
+ VINO_CTRL_A_DMA_ENBL : VINO_CTRL_B_DMA_ENBL;
+ vino->control = ctrl;
+}
+
+/* (execute only with vino_lock locked) */
+static inline void vino_dma_stop(struct vino_channel_settings *vcs)
+{
+ u32 ctrl = vino->control;
+
+ ctrl &= (vcs->channel == VINO_CHANNEL_A) ?
+ ~VINO_CTRL_A_DMA_ENBL : ~VINO_CTRL_B_DMA_ENBL;
+ ctrl &= (vcs->channel == VINO_CHANNEL_A) ?
+ ~VINO_CTRL_A_INT : ~VINO_CTRL_B_INT;
+ vino->control = ctrl;
+ dprintk("vino_dma_stop():\n");
+}
+
+/*
+ * Load dummy page to descriptor registers. This prevents generating of
+ * spurious interrupts. (execute only with vino_lock locked)
+ */
+static void vino_clear_interrupt(struct vino_channel_settings *vcs)
+{
+ struct sgi_vino_channel *ch;
+
+ ch = (vcs->channel == VINO_CHANNEL_A) ? &vino->a : &vino->b;
+
+ ch->page_index = 0;
+ ch->line_count = 0;
+
+ ch->start_desc_tbl = vino_drvdata->dummy_desc_table.dma;
+ ch->next_4_desc = vino_drvdata->dummy_desc_table.dma;
+
+ udelay(VINO_DESC_FETCH_DELAY);
+ dprintk("channel %c clear interrupt condition\n",
+ (vcs->channel == VINO_CHANNEL_A) ? 'A':'B');
+}
+
+static int vino_capture(struct vino_channel_settings *vcs,
+ struct vino_framebuffer *fb)
+{
+ int err = 0;
+ unsigned long flags, flags2;
+
+ spin_lock_irqsave(&fb->state_lock, flags);
+
+ if (fb->state == VINO_FRAMEBUFFER_IN_USE)
+ err = -EBUSY;
+ fb->state = VINO_FRAMEBUFFER_IN_USE;
+
+ spin_unlock_irqrestore(&fb->state_lock, flags);
+
+ if (err)
+ return err;
+
+ spin_lock_irqsave(&vino_drvdata->vino_lock, flags);
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags2);
+
+ vino_dma_setup(vcs, fb);
+ vino_dma_start(vcs);
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags2);
+ spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags);
+
+ return err;
+}
+
+static
+struct vino_framebuffer *vino_capture_enqueue(struct
+ vino_channel_settings *vcs,
+ unsigned int index)
+{
+ struct vino_framebuffer *fb;
+ unsigned long flags;
+
+ dprintk("vino_capture_enqueue():\n");
+
+ spin_lock_irqsave(&vcs->capture_lock, flags);
+
+ fb = vino_queue_add(&vcs->fb_queue, index);
+ if (fb == NULL) {
+ dprintk("vino_capture_enqueue(): vino_queue_add() failed, "
+ "queue full?\n");
+ goto out;
+ }
+out:
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+
+ return fb;
+}
+
+static int vino_capture_next(struct vino_channel_settings *vcs, int start)
+{
+ struct vino_framebuffer *fb;
+ unsigned int incoming, id;
+ int err = 0;
+ unsigned long flags;
+
+ dprintk("vino_capture_next():\n");
+
+ spin_lock_irqsave(&vcs->capture_lock, flags);
+
+ if (start) {
+ /* start capture only if capture isn't in progress already */
+ if (vcs->capturing) {
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+ return 0;
+ }
+
+ } else {
+ /* capture next frame:
+ * stop capture if capturing is not set */
+ if (!vcs->capturing) {
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+ return 0;
+ }
+ }
+
+ err = vino_queue_get_incoming(&vcs->fb_queue, &incoming);
+ if (err) {
+ dprintk("vino_capture_next(): vino_queue_get_incoming() "
+ "failed\n");
+ err = -EINVAL;
+ goto out;
+ }
+ if (incoming == 0) {
+ dprintk("vino_capture_next(): no buffers available\n");
+ goto out;
+ }
+
+ fb = vino_queue_peek(&vcs->fb_queue, &id);
+ if (fb == NULL) {
+ dprintk("vino_capture_next(): vino_queue_peek() failed\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (start) {
+ vcs->capturing = 1;
+ }
+
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+
+ err = vino_capture(vcs, fb);
+
+ return err;
+
+out:
+ vcs->capturing = 0;
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+
+ return err;
+}
+
+static inline int vino_is_capturing(struct vino_channel_settings *vcs)
+{
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vcs->capture_lock, flags);
+
+ ret = vcs->capturing;
+
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+
+ return ret;
+}
+
+/* waits until a frame is captured */
+static int vino_wait_for_frame(struct vino_channel_settings *vcs)
+{
+ wait_queue_t wait;
+ int err = 0;
+
+ dprintk("vino_wait_for_frame():\n");
+
+ init_waitqueue_entry(&wait, current);
+ /* add ourselves into wait queue */
+ add_wait_queue(&vcs->fb_queue.frame_wait_queue, &wait);
+
+ /* to ensure that schedule_timeout will return immediately
+ * if VINO interrupt was triggered meanwhile */
+ schedule_timeout_interruptible(msecs_to_jiffies(100));
+
+ if (signal_pending(current))
+ err = -EINTR;
+
+ remove_wait_queue(&vcs->fb_queue.frame_wait_queue, &wait);
+
+ dprintk("vino_wait_for_frame(): waiting for frame %s\n",
+ err ? "failed" : "ok");
+
+ return err;
+}
+
+/* the function assumes that PAGE_SIZE % 4 == 0 */
+static void vino_convert_to_rgba(struct vino_framebuffer *fb) {
+ unsigned char *pageptr;
+ unsigned int page, i;
+ unsigned char a;
+
+ for (page = 0; page < fb->desc_table.page_count; page++) {
+ pageptr = (unsigned char *)fb->desc_table.virtual[page];
+
+ for (i = 0; i < PAGE_SIZE; i += 4) {
+ a = pageptr[0];
+ pageptr[0] = pageptr[3];
+ pageptr[1] = pageptr[2];
+ pageptr[2] = pageptr[1];
+ pageptr[3] = a;
+ pageptr += 4;
+ }
+ }
+}
+
+/* checks if the buffer is in correct state and syncs data */
+static int vino_check_buffer(struct vino_channel_settings *vcs,
+ struct vino_framebuffer *fb)
+{
+ int err = 0;
+ unsigned long flags;
+
+ dprintk("vino_check_buffer():\n");
+
+ spin_lock_irqsave(&fb->state_lock, flags);
+ switch (fb->state) {
+ case VINO_FRAMEBUFFER_IN_USE:
+ err = -EIO;
+ break;
+ case VINO_FRAMEBUFFER_READY:
+ vino_sync_buffer(fb);
+ fb->state = VINO_FRAMEBUFFER_UNUSED;
+ break;
+ default:
+ err = -EINVAL;
+ }
+ spin_unlock_irqrestore(&fb->state_lock, flags);
+
+ if (!err) {
+ if (vino_pixel_conversion
+ && (fb->data_format == VINO_DATA_FMT_RGB32)) {
+ vino_convert_to_rgba(fb);
+ }
+ } else if (err && (err != -EINVAL)) {
+ dprintk("vino_check_buffer(): buffer not ready\n");
+
+ spin_lock_irqsave(&vino_drvdata->vino_lock, flags);
+ vino_dma_stop(vcs);
+ vino_clear_interrupt(vcs);
+ spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags);
+ }
+
+ return err;
+}
+
+/* forcefully terminates capture */
+static void vino_capture_stop(struct vino_channel_settings *vcs)
+{
+ unsigned int incoming = 0, outgoing = 0, id;
+ unsigned long flags, flags2;
+
+ dprintk("vino_capture_stop():\n");
+
+ spin_lock_irqsave(&vcs->capture_lock, flags);
+
+ /* unset capturing to stop queue processing */
+ vcs->capturing = 0;
+
+ spin_lock_irqsave(&vino_drvdata->vino_lock, flags2);
+
+ vino_dma_stop(vcs);
+ vino_clear_interrupt(vcs);
+
+ spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags2);
+
+ /* remove all items from the queue */
+ if (vino_queue_get_incoming(&vcs->fb_queue, &incoming)) {
+ dprintk("vino_capture_stop(): "
+ "vino_queue_get_incoming() failed\n");
+ goto out;
+ }
+ while (incoming > 0) {
+ vino_queue_transfer(&vcs->fb_queue);
+
+ if (vino_queue_get_incoming(&vcs->fb_queue, &incoming)) {
+ dprintk("vino_capture_stop(): "
+ "vino_queue_get_incoming() failed\n");
+ goto out;
+ }
+ }
+
+ if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) {
+ dprintk("vino_capture_stop(): "
+ "vino_queue_get_outgoing() failed\n");
+ goto out;
+ }
+ while (outgoing > 0) {
+ vino_queue_remove(&vcs->fb_queue, &id);
+
+ if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) {
+ dprintk("vino_capture_stop(): "
+ "vino_queue_get_outgoing() failed\n");
+ goto out;
+ }
+ }
+
+out:
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+}
+
+#if 0
+static int vino_capture_failed(struct vino_channel_settings *vcs)
+{
+ struct vino_framebuffer *fb;
+ unsigned long flags;
+ unsigned int i;
+ int ret;
+
+ dprintk("vino_capture_failed():\n");
+
+ spin_lock_irqsave(&vino_drvdata->vino_lock, flags);
+
+ vino_dma_stop(vcs);
+ vino_clear_interrupt(vcs);
+
+ spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags);
+
+ ret = vino_queue_get_incoming(&vcs->fb_queue, &i);
+ if (ret == VINO_QUEUE_ERROR) {
+ dprintk("vino_queue_get_incoming() failed\n");
+ return -EINVAL;
+ }
+ if (i == 0) {
+ /* no buffers to process */
+ return 0;
+ }
+
+ fb = vino_queue_peek(&vcs->fb_queue, &i);
+ if (fb == NULL) {
+ dprintk("vino_queue_peek() failed\n");
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&fb->state_lock, flags);
+ if (fb->state == VINO_FRAMEBUFFER_IN_USE) {
+ fb->state = VINO_FRAMEBUFFER_UNUSED;
+ vino_queue_transfer(&vcs->fb_queue);
+ vino_queue_remove(&vcs->fb_queue, &i);
+ /* we should actually discard the newest frame,
+ * but who cares ... */
+ }
+ spin_unlock_irqrestore(&fb->state_lock, flags);
+
+ return 0;
+}
+#endif
+
+static void vino_skip_frame(struct vino_channel_settings *vcs)
+{
+ struct vino_framebuffer *fb;
+ unsigned long flags;
+ unsigned int id;
+
+ spin_lock_irqsave(&vcs->capture_lock, flags);
+ fb = vino_queue_peek(&vcs->fb_queue, &id);
+ if (!fb) {
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+ dprintk("vino_skip_frame(): vino_queue_peek() failed!\n");
+ return;
+ }
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+
+ spin_lock_irqsave(&fb->state_lock, flags);
+ fb->state = VINO_FRAMEBUFFER_UNUSED;
+ spin_unlock_irqrestore(&fb->state_lock, flags);
+
+ vino_capture_next(vcs, 0);
+}
+
+static void vino_frame_done(struct vino_channel_settings *vcs)
+{
+ struct vino_framebuffer *fb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vcs->capture_lock, flags);
+ fb = vino_queue_transfer(&vcs->fb_queue);
+ if (!fb) {
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+ dprintk("vino_frame_done(): vino_queue_transfer() failed!\n");
+ return;
+ }
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+
+ fb->frame_counter = vcs->int_data.frame_counter;
+ memcpy(&fb->timestamp, &vcs->int_data.timestamp,
+ sizeof(struct timeval));
+
+ spin_lock_irqsave(&fb->state_lock, flags);
+ if (fb->state == VINO_FRAMEBUFFER_IN_USE)
+ fb->state = VINO_FRAMEBUFFER_READY;
+ spin_unlock_irqrestore(&fb->state_lock, flags);
+
+ wake_up(&vcs->fb_queue.frame_wait_queue);
+
+ vino_capture_next(vcs, 0);
+}
+
+static void vino_capture_tasklet(unsigned long channel) {
+ struct vino_channel_settings *vcs;
+
+ vcs = (channel == VINO_CHANNEL_A)
+ ? &vino_drvdata->a : &vino_drvdata->b;
+
+ if (vcs->int_data.skip)
+ vcs->int_data.skip_count++;
+
+ if (vcs->int_data.skip && (vcs->int_data.skip_count
+ <= VINO_MAX_FRAME_SKIP_COUNT)) {
+ vino_skip_frame(vcs);
+ } else {
+ vcs->int_data.skip_count = 0;
+ vino_frame_done(vcs);
+ }
+}
+
+static irqreturn_t vino_interrupt(int irq, void *dev_id)
+{
+ u32 ctrl, intr;
+ unsigned int fc_a, fc_b;
+ int handled_a = 0, skip_a = 0, done_a = 0;
+ int handled_b = 0, skip_b = 0, done_b = 0;
+
+#ifdef VINO_DEBUG_INT
+ int loop = 0;
+ unsigned int line_count = vino->a.line_count,
+ page_index = vino->a.page_index,
+ field_counter = vino->a.field_counter,
+ start_desc_tbl = vino->a.start_desc_tbl,
+ next_4_desc = vino->a.next_4_desc;
+ unsigned int line_count_2,
+ page_index_2,
+ field_counter_2,
+ start_desc_tbl_2,
+ next_4_desc_2;
+#endif
+
+ spin_lock(&vino_drvdata->vino_lock);
+
+ while ((intr = vino->intr_status)) {
+ fc_a = vino->a.field_counter >> 1;
+ fc_b = vino->b.field_counter >> 1;
+
+ /* handle error-interrupts in some special way ?
+ * --> skips frames */
+ if (intr & VINO_INTSTAT_A) {
+ if (intr & VINO_INTSTAT_A_EOF) {
+ vino_drvdata->a.field++;
+ if (vino_drvdata->a.field > 1) {
+ vino_dma_stop(&vino_drvdata->a);
+ vino_clear_interrupt(&vino_drvdata->a);
+ vino_drvdata->a.field = 0;
+ done_a = 1;
+ } else {
+ if (vino->a.page_index
+ != vino_drvdata->a.line_size) {
+ vino->a.line_count = 0;
+ vino->a.page_index =
+ vino_drvdata->
+ a.line_size;
+ vino->a.next_4_desc =
+ vino->a.start_desc_tbl;
+ }
+ }
+ dprintk("channel A end-of-field "
+ "interrupt: %04x\n", intr);
+ } else {
+ vino_dma_stop(&vino_drvdata->a);
+ vino_clear_interrupt(&vino_drvdata->a);
+ vino_drvdata->a.field = 0;
+ skip_a = 1;
+ dprintk("channel A error interrupt: %04x\n",
+ intr);
+ }
+
+#ifdef VINO_DEBUG_INT
+ line_count_2 = vino->a.line_count;
+ page_index_2 = vino->a.page_index;
+ field_counter_2 = vino->a.field_counter;
+ start_desc_tbl_2 = vino->a.start_desc_tbl;
+ next_4_desc_2 = vino->a.next_4_desc;
+
+ printk("intr = %04x, loop = %d, field = %d\n",
+ intr, loop, vino_drvdata->a.field);
+ printk("1- line count = %04d, page index = %04d, "
+ "start = %08x, next = %08x\n"
+ " fieldc = %d, framec = %d\n",
+ line_count, page_index, start_desc_tbl,
+ next_4_desc, field_counter, fc_a);
+ printk("12-line count = %04d, page index = %04d, "
+ " start = %08x, next = %08x\n",
+ line_count_2, page_index_2, start_desc_tbl_2,
+ next_4_desc_2);
+
+ if (done_a)
+ printk("\n");
+#endif
+ }
+
+ if (intr & VINO_INTSTAT_B) {
+ if (intr & VINO_INTSTAT_B_EOF) {
+ vino_drvdata->b.field++;
+ if (vino_drvdata->b.field > 1) {
+ vino_dma_stop(&vino_drvdata->b);
+ vino_clear_interrupt(&vino_drvdata->b);
+ vino_drvdata->b.field = 0;
+ done_b = 1;
+ }
+ dprintk("channel B end-of-field "
+ "interrupt: %04x\n", intr);
+ } else {
+ vino_dma_stop(&vino_drvdata->b);
+ vino_clear_interrupt(&vino_drvdata->b);
+ vino_drvdata->b.field = 0;
+ skip_b = 1;
+ dprintk("channel B error interrupt: %04x\n",
+ intr);
+ }
+ }
+
+ /* Always remember to clear interrupt status.
+ * Disable VINO interrupts while we do this. */
+ ctrl = vino->control;
+ vino->control = ctrl & ~(VINO_CTRL_A_INT | VINO_CTRL_B_INT);
+ vino->intr_status = ~intr;
+ vino->control = ctrl;
+
+ spin_unlock(&vino_drvdata->vino_lock);
+
+ if ((!handled_a) && (done_a || skip_a)) {
+ if (!skip_a) {
+ do_gettimeofday(&vino_drvdata->
+ a.int_data.timestamp);
+ vino_drvdata->a.int_data.frame_counter = fc_a;
+ }
+ vino_drvdata->a.int_data.skip = skip_a;
+
+ dprintk("channel A %s, interrupt: %d\n",
+ skip_a ? "skipping frame" : "frame done",
+ intr);
+ tasklet_hi_schedule(&vino_tasklet_a);
+ handled_a = 1;
+ }
+
+ if ((!handled_b) && (done_b || skip_b)) {
+ if (!skip_b) {
+ do_gettimeofday(&vino_drvdata->
+ b.int_data.timestamp);
+ vino_drvdata->b.int_data.frame_counter = fc_b;
+ }
+ vino_drvdata->b.int_data.skip = skip_b;
+
+ dprintk("channel B %s, interrupt: %d\n",
+ skip_b ? "skipping frame" : "frame done",
+ intr);
+ tasklet_hi_schedule(&vino_tasklet_b);
+ handled_b = 1;
+ }
+
+#ifdef VINO_DEBUG_INT
+ loop++;
+#endif
+ spin_lock(&vino_drvdata->vino_lock);
+ }
+
+ spin_unlock(&vino_drvdata->vino_lock);
+
+ return IRQ_HANDLED;
+}
+
+/* VINO video input management */
+
+static int vino_get_saa7191_input(int input)
+{
+ switch (input) {
+ case VINO_INPUT_COMPOSITE:
+ return SAA7191_INPUT_COMPOSITE;
+ case VINO_INPUT_SVIDEO:
+ return SAA7191_INPUT_SVIDEO;
+ default:
+ printk(KERN_ERR "VINO: vino_get_saa7191_input(): "
+ "invalid input!\n");
+ return -1;
+ }
+}
+
+/* execute with input_lock locked */
+static int vino_is_input_owner(struct vino_channel_settings *vcs)
+{
+ switch(vcs->input) {
+ case VINO_INPUT_COMPOSITE:
+ case VINO_INPUT_SVIDEO:
+ return vino_drvdata->decoder_owner == vcs->channel;
+ case VINO_INPUT_D1:
+ return vino_drvdata->camera_owner == vcs->channel;
+ default:
+ return 0;
+ }
+}
+
+static int vino_acquire_input(struct vino_channel_settings *vcs)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ dprintk("vino_acquire_input():\n");
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ /* First try D1 and then SAA7191 */
+ if (vino_drvdata->camera
+ && (vino_drvdata->camera_owner == VINO_NO_CHANNEL)) {
+ vino_drvdata->camera_owner = vcs->channel;
+ vcs->input = VINO_INPUT_D1;
+ vcs->data_norm = VINO_DATA_NORM_D1;
+ } else if (vino_drvdata->decoder
+ && (vino_drvdata->decoder_owner == VINO_NO_CHANNEL)) {
+ int input;
+ int data_norm;
+ v4l2_std_id norm;
+
+ input = VINO_INPUT_COMPOSITE;
+
+ ret = decoder_call(video, s_routing,
+ vino_get_saa7191_input(input), 0, 0);
+ if (ret) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ /* Don't hold spinlocks while auto-detecting norm
+ * as it may take a while... */
+
+ ret = decoder_call(video, querystd, &norm);
+ if (!ret) {
+ for (data_norm = 0; data_norm < 3; data_norm++) {
+ if (vino_data_norms[data_norm].std & norm)
+ break;
+ }
+ if (data_norm == 3)
+ data_norm = VINO_DATA_NORM_PAL;
+ ret = decoder_call(core, s_std, norm);
+ }
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ if (ret) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ vino_drvdata->decoder_owner = vcs->channel;
+
+ vcs->input = input;
+ vcs->data_norm = data_norm;
+ } else {
+ vcs->input = (vcs->channel == VINO_CHANNEL_A) ?
+ vino_drvdata->b.input : vino_drvdata->a.input;
+ vcs->data_norm = (vcs->channel == VINO_CHANNEL_A) ?
+ vino_drvdata->b.data_norm : vino_drvdata->a.data_norm;
+ }
+
+ if (vcs->input == VINO_INPUT_NONE) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ vino_set_default_clipping(vcs);
+ vino_set_default_scaling(vcs);
+ vino_set_default_framerate(vcs);
+
+ dprintk("vino_acquire_input(): %s\n", vino_inputs[vcs->input].name);
+
+out:
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return ret;
+}
+
+static int vino_set_input(struct vino_channel_settings *vcs, int input)
+{
+ struct vino_channel_settings *vcs2 = (vcs->channel == VINO_CHANNEL_A) ?
+ &vino_drvdata->b : &vino_drvdata->a;
+ unsigned long flags;
+ int ret = 0;
+
+ dprintk("vino_set_input():\n");
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ if (vcs->input == input)
+ goto out;
+
+ switch (input) {
+ case VINO_INPUT_COMPOSITE:
+ case VINO_INPUT_SVIDEO:
+ if (!vino_drvdata->decoder) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (vino_drvdata->decoder_owner == VINO_NO_CHANNEL) {
+ vino_drvdata->decoder_owner = vcs->channel;
+ }
+
+ if (vino_drvdata->decoder_owner == vcs->channel) {
+ int data_norm;
+ v4l2_std_id norm;
+
+ ret = decoder_call(video, s_routing,
+ vino_get_saa7191_input(input), 0, 0);
+ if (ret) {
+ vino_drvdata->decoder_owner = VINO_NO_CHANNEL;
+ ret = -EINVAL;
+ goto out;
+ }
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ /* Don't hold spinlocks while auto-detecting norm
+ * as it may take a while... */
+
+ ret = decoder_call(video, querystd, &norm);
+ if (!ret) {
+ for (data_norm = 0; data_norm < 3; data_norm++) {
+ if (vino_data_norms[data_norm].std & norm)
+ break;
+ }
+ if (data_norm == 3)
+ data_norm = VINO_DATA_NORM_PAL;
+ ret = decoder_call(core, s_std, norm);
+ }
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ if (ret) {
+ vino_drvdata->decoder_owner = VINO_NO_CHANNEL;
+ ret = -EINVAL;
+ goto out;
+ }
+
+ vcs->input = input;
+ vcs->data_norm = data_norm;
+ } else {
+ if (input != vcs2->input) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ vcs->input = input;
+ vcs->data_norm = vcs2->data_norm;
+ }
+
+ if (vino_drvdata->camera_owner == vcs->channel) {
+ /* Transfer the ownership or release the input */
+ if (vcs2->input == VINO_INPUT_D1) {
+ vino_drvdata->camera_owner = vcs2->channel;
+ } else {
+ vino_drvdata->camera_owner = VINO_NO_CHANNEL;
+ }
+ }
+ break;
+ case VINO_INPUT_D1:
+ if (!vino_drvdata->camera) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (vino_drvdata->camera_owner == VINO_NO_CHANNEL)
+ vino_drvdata->camera_owner = vcs->channel;
+
+ if (vino_drvdata->decoder_owner == vcs->channel) {
+ /* Transfer the ownership or release the input */
+ if ((vcs2->input == VINO_INPUT_COMPOSITE) ||
+ (vcs2->input == VINO_INPUT_SVIDEO)) {
+ vino_drvdata->decoder_owner = vcs2->channel;
+ } else {
+ vino_drvdata->decoder_owner = VINO_NO_CHANNEL;
+ }
+ }
+
+ vcs->input = input;
+ vcs->data_norm = VINO_DATA_NORM_D1;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+
+ vino_set_default_clipping(vcs);
+ vino_set_default_scaling(vcs);
+ vino_set_default_framerate(vcs);
+
+ dprintk("vino_set_input(): %s\n", vino_inputs[vcs->input].name);
+
+out:
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return ret;
+}
+
+static void vino_release_input(struct vino_channel_settings *vcs)
+{
+ struct vino_channel_settings *vcs2 = (vcs->channel == VINO_CHANNEL_A) ?
+ &vino_drvdata->b : &vino_drvdata->a;
+ unsigned long flags;
+
+ dprintk("vino_release_input():\n");
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ /* Release ownership of the channel
+ * and if the other channel takes input from
+ * the same source, transfer the ownership */
+ if (vino_drvdata->camera_owner == vcs->channel) {
+ if (vcs2->input == VINO_INPUT_D1) {
+ vino_drvdata->camera_owner = vcs2->channel;
+ } else {
+ vino_drvdata->camera_owner = VINO_NO_CHANNEL;
+ }
+ } else if (vino_drvdata->decoder_owner == vcs->channel) {
+ if ((vcs2->input == VINO_INPUT_COMPOSITE) ||
+ (vcs2->input == VINO_INPUT_SVIDEO)) {
+ vino_drvdata->decoder_owner = vcs2->channel;
+ } else {
+ vino_drvdata->decoder_owner = VINO_NO_CHANNEL;
+ }
+ }
+ vcs->input = VINO_INPUT_NONE;
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+}
+
+/* execute with input_lock locked */
+static int vino_set_data_norm(struct vino_channel_settings *vcs,
+ unsigned int data_norm,
+ unsigned long *flags)
+{
+ int err = 0;
+
+ if (data_norm == vcs->data_norm)
+ return 0;
+
+ switch (vcs->input) {
+ case VINO_INPUT_D1:
+ /* only one "norm" supported */
+ if (data_norm != VINO_DATA_NORM_D1)
+ return -EINVAL;
+ break;
+ case VINO_INPUT_COMPOSITE:
+ case VINO_INPUT_SVIDEO: {
+ v4l2_std_id norm;
+
+ if ((data_norm != VINO_DATA_NORM_PAL)
+ && (data_norm != VINO_DATA_NORM_NTSC)
+ && (data_norm != VINO_DATA_NORM_SECAM))
+ return -EINVAL;
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, *flags);
+
+ /* Don't hold spinlocks while setting norm
+ * as it may take a while... */
+
+ norm = vino_data_norms[data_norm].std;
+ err = decoder_call(core, s_std, norm);
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, *flags);
+
+ if (err)
+ goto out;
+
+ vcs->data_norm = data_norm;
+
+ vino_set_default_clipping(vcs);
+ vino_set_default_scaling(vcs);
+ vino_set_default_framerate(vcs);
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+out:
+ return err;
+}
+
+/* V4L2 helper functions */
+
+static int vino_find_data_format(__u32 pixelformat)
+{
+ int i;
+
+ for (i = 0; i < VINO_DATA_FMT_COUNT; i++) {
+ if (vino_data_formats[i].pixelformat == pixelformat)
+ return i;
+ }
+
+ return VINO_DATA_FMT_NONE;
+}
+
+static int vino_int_enum_input(struct vino_channel_settings *vcs, __u32 index)
+{
+ int input = VINO_INPUT_NONE;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+ if (vino_drvdata->decoder && vino_drvdata->camera) {
+ switch (index) {
+ case 0:
+ input = VINO_INPUT_COMPOSITE;
+ break;
+ case 1:
+ input = VINO_INPUT_SVIDEO;
+ break;
+ case 2:
+ input = VINO_INPUT_D1;
+ break;
+ }
+ } else if (vino_drvdata->decoder) {
+ switch (index) {
+ case 0:
+ input = VINO_INPUT_COMPOSITE;
+ break;
+ case 1:
+ input = VINO_INPUT_SVIDEO;
+ break;
+ }
+ } else if (vino_drvdata->camera) {
+ switch (index) {
+ case 0:
+ input = VINO_INPUT_D1;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return input;
+}
+
+/* execute with input_lock locked */
+static __u32 vino_find_input_index(struct vino_channel_settings *vcs)
+{
+ __u32 index = 0;
+ // FIXME: detect when no inputs available
+
+ if (vino_drvdata->decoder && vino_drvdata->camera) {
+ switch (vcs->input) {
+ case VINO_INPUT_COMPOSITE:
+ index = 0;
+ break;
+ case VINO_INPUT_SVIDEO:
+ index = 1;
+ break;
+ case VINO_INPUT_D1:
+ index = 2;
+ break;
+ }
+ } else if (vino_drvdata->decoder) {
+ switch (vcs->input) {
+ case VINO_INPUT_COMPOSITE:
+ index = 0;
+ break;
+ case VINO_INPUT_SVIDEO:
+ index = 1;
+ break;
+ }
+ } else if (vino_drvdata->camera) {
+ switch (vcs->input) {
+ case VINO_INPUT_D1:
+ index = 0;
+ break;
+ }
+ }
+
+ return index;
+}
+
+/* V4L2 ioctls */
+
+static int vino_querycap(struct file *file, void *__fh,
+ struct v4l2_capability *cap)
+{
+ memset(cap, 0, sizeof(struct v4l2_capability));
+
+ strcpy(cap->driver, vino_driver_name);
+ strcpy(cap->card, vino_driver_description);
+ strcpy(cap->bus_info, vino_bus_name);
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_STREAMING;
+ // V4L2_CAP_OVERLAY, V4L2_CAP_READWRITE
+ return 0;
+}
+
+static int vino_enum_input(struct file *file, void *__fh,
+ struct v4l2_input *i)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ __u32 index = i->index;
+ int input;
+ dprintk("requested index = %d\n", index);
+
+ input = vino_int_enum_input(vcs, index);
+ if (input == VINO_INPUT_NONE)
+ return -EINVAL;
+
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ i->std = vino_inputs[input].std;
+ strcpy(i->name, vino_inputs[input].name);
+
+ if (input == VINO_INPUT_COMPOSITE || input == VINO_INPUT_SVIDEO)
+ decoder_call(video, g_input_status, &i->status);
+ return 0;
+}
+
+static int vino_g_input(struct file *file, void *__fh,
+ unsigned int *i)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ __u32 index;
+ int input;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+ input = vcs->input;
+ index = vino_find_input_index(vcs);
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ dprintk("input = %d\n", input);
+
+ if (input == VINO_INPUT_NONE) {
+ return -EINVAL;
+ }
+
+ *i = index;
+
+ return 0;
+}
+
+static int vino_s_input(struct file *file, void *__fh,
+ unsigned int i)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ int input;
+ dprintk("requested input = %d\n", i);
+
+ input = vino_int_enum_input(vcs, i);
+ if (input == VINO_INPUT_NONE)
+ return -EINVAL;
+
+ return vino_set_input(vcs, input);
+}
+
+static int vino_querystd(struct file *file, void *__fh,
+ v4l2_std_id *std)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+ int err = 0;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ switch (vcs->input) {
+ case VINO_INPUT_D1:
+ *std = vino_inputs[vcs->input].std;
+ break;
+ case VINO_INPUT_COMPOSITE:
+ case VINO_INPUT_SVIDEO: {
+ decoder_call(video, querystd, std);
+ break;
+ }
+ default:
+ err = -EINVAL;
+ }
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return err;
+}
+
+static int vino_g_std(struct file *file, void *__fh,
+ v4l2_std_id *std)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ *std = vino_data_norms[vcs->data_norm].std;
+ dprintk("current standard = %d\n", vcs->data_norm);
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return 0;
+}
+
+static int vino_s_std(struct file *file, void *__fh,
+ v4l2_std_id *std)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ if (!vino_is_input_owner(vcs)) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* check if the standard is valid for the current input */
+ if ((*std) & vino_inputs[vcs->input].std) {
+ dprintk("standard accepted\n");
+
+ /* change the video norm for SAA7191
+ * and accept NTSC for D1 (do nothing) */
+
+ if (vcs->input == VINO_INPUT_D1)
+ goto out;
+
+ if ((*std) & V4L2_STD_PAL) {
+ ret = vino_set_data_norm(vcs, VINO_DATA_NORM_PAL,
+ &flags);
+ } else if ((*std) & V4L2_STD_NTSC) {
+ ret = vino_set_data_norm(vcs, VINO_DATA_NORM_NTSC,
+ &flags);
+ } else if ((*std) & V4L2_STD_SECAM) {
+ ret = vino_set_data_norm(vcs, VINO_DATA_NORM_SECAM,
+ &flags);
+ } else {
+ ret = -EINVAL;
+ }
+
+ if (ret) {
+ ret = -EINVAL;
+ }
+ } else {
+ ret = -EINVAL;
+ }
+
+out:
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return ret;
+}
+
+static int vino_enum_fmt_vid_cap(struct file *file, void *__fh,
+ struct v4l2_fmtdesc *fd)
+{
+ dprintk("format index = %d\n", fd->index);
+
+ if (fd->index >= VINO_DATA_FMT_COUNT)
+ return -EINVAL;
+ dprintk("format name = %s\n", vino_data_formats[fd->index].description);
+
+ fd->pixelformat = vino_data_formats[fd->index].pixelformat;
+ strcpy(fd->description, vino_data_formats[fd->index].description);
+ return 0;
+}
+
+static int vino_try_fmt_vid_cap(struct file *file, void *__fh,
+ struct v4l2_format *f)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ struct vino_channel_settings tempvcs;
+ unsigned long flags;
+ struct v4l2_pix_format *pf = &f->fmt.pix;
+
+ dprintk("requested: w = %d, h = %d\n",
+ pf->width, pf->height);
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+ memcpy(&tempvcs, vcs, sizeof(struct vino_channel_settings));
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ tempvcs.data_format = vino_find_data_format(pf->pixelformat);
+ if (tempvcs.data_format == VINO_DATA_FMT_NONE) {
+ tempvcs.data_format = VINO_DATA_FMT_GREY;
+ pf->pixelformat =
+ vino_data_formats[tempvcs.data_format].
+ pixelformat;
+ }
+
+ /* data format must be set before clipping/scaling */
+ vino_set_scaling(&tempvcs, pf->width, pf->height);
+
+ dprintk("data format = %s\n",
+ vino_data_formats[tempvcs.data_format].description);
+
+ pf->width = (tempvcs.clipping.right - tempvcs.clipping.left) /
+ tempvcs.decimation;
+ pf->height = (tempvcs.clipping.bottom - tempvcs.clipping.top) /
+ tempvcs.decimation;
+
+ pf->field = V4L2_FIELD_INTERLACED;
+ pf->bytesperline = tempvcs.line_size;
+ pf->sizeimage = tempvcs.line_size *
+ (tempvcs.clipping.bottom - tempvcs.clipping.top) /
+ tempvcs.decimation;
+ pf->colorspace =
+ vino_data_formats[tempvcs.data_format].colorspace;
+
+ pf->priv = 0;
+ return 0;
+}
+
+static int vino_g_fmt_vid_cap(struct file *file, void *__fh,
+ struct v4l2_format *f)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+ struct v4l2_pix_format *pf = &f->fmt.pix;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ pf->width = (vcs->clipping.right - vcs->clipping.left) /
+ vcs->decimation;
+ pf->height = (vcs->clipping.bottom - vcs->clipping.top) /
+ vcs->decimation;
+ pf->pixelformat =
+ vino_data_formats[vcs->data_format].pixelformat;
+
+ pf->field = V4L2_FIELD_INTERLACED;
+ pf->bytesperline = vcs->line_size;
+ pf->sizeimage = vcs->line_size *
+ (vcs->clipping.bottom - vcs->clipping.top) /
+ vcs->decimation;
+ pf->colorspace =
+ vino_data_formats[vcs->data_format].colorspace;
+
+ pf->priv = 0;
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+ return 0;
+}
+
+static int vino_s_fmt_vid_cap(struct file *file, void *__fh,
+ struct v4l2_format *f)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ int data_format;
+ unsigned long flags;
+ struct v4l2_pix_format *pf = &f->fmt.pix;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ data_format = vino_find_data_format(pf->pixelformat);
+
+ if (data_format == VINO_DATA_FMT_NONE) {
+ vcs->data_format = VINO_DATA_FMT_GREY;
+ pf->pixelformat =
+ vino_data_formats[vcs->data_format].
+ pixelformat;
+ } else {
+ vcs->data_format = data_format;
+ }
+
+ /* data format must be set before clipping/scaling */
+ vino_set_scaling(vcs, pf->width, pf->height);
+
+ dprintk("data format = %s\n",
+ vino_data_formats[vcs->data_format].description);
+
+ pf->width = vcs->clipping.right - vcs->clipping.left;
+ pf->height = vcs->clipping.bottom - vcs->clipping.top;
+
+ pf->field = V4L2_FIELD_INTERLACED;
+ pf->bytesperline = vcs->line_size;
+ pf->sizeimage = vcs->line_size *
+ (vcs->clipping.bottom - vcs->clipping.top) /
+ vcs->decimation;
+ pf->colorspace =
+ vino_data_formats[vcs->data_format].colorspace;
+
+ pf->priv = 0;
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+ return 0;
+}
+
+static int vino_cropcap(struct file *file, void *__fh,
+ struct v4l2_cropcap *ccap)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ const struct vino_data_norm *norm;
+ unsigned long flags;
+
+ switch (ccap->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ norm = &vino_data_norms[vcs->data_norm];
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ ccap->bounds.left = 0;
+ ccap->bounds.top = 0;
+ ccap->bounds.width = norm->width;
+ ccap->bounds.height = norm->height;
+ memcpy(&ccap->defrect, &ccap->bounds,
+ sizeof(struct v4l2_rect));
+
+ ccap->pixelaspect.numerator = 1;
+ ccap->pixelaspect.denominator = 1;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vino_g_crop(struct file *file, void *__fh,
+ struct v4l2_crop *c)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+
+ switch (c->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ c->c.left = vcs->clipping.left;
+ c->c.top = vcs->clipping.top;
+ c->c.width = vcs->clipping.right - vcs->clipping.left;
+ c->c.height = vcs->clipping.bottom - vcs->clipping.top;
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vino_s_crop(struct file *file, void *__fh,
+ struct v4l2_crop *c)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+
+ switch (c->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ vino_set_clipping(vcs, c->c.left, c->c.top,
+ c->c.width, c->c.height);
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vino_g_parm(struct file *file, void *__fh,
+ struct v4l2_streamparm *sp)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+ struct v4l2_captureparm *cp = &sp->parm.capture;
+
+ cp->capability = V4L2_CAP_TIMEPERFRAME;
+ cp->timeperframe.numerator = 1;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ cp->timeperframe.denominator = vcs->fps;
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ /* TODO: cp->readbuffers = xxx; */
+
+ return 0;
+}
+
+static int vino_s_parm(struct file *file, void *__fh,
+ struct v4l2_streamparm *sp)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+ struct v4l2_captureparm *cp = &sp->parm.capture;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ if ((cp->timeperframe.numerator == 0) ||
+ (cp->timeperframe.denominator == 0)) {
+ /* reset framerate */
+ vino_set_default_framerate(vcs);
+ } else {
+ vino_set_framerate(vcs, cp->timeperframe.denominator /
+ cp->timeperframe.numerator);
+ }
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return 0;
+}
+
+static int vino_reqbufs(struct file *file, void *__fh,
+ struct v4l2_requestbuffers *rb)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+
+ if (vcs->reading)
+ return -EBUSY;
+
+ /* TODO: check queue type */
+ if (rb->memory != V4L2_MEMORY_MMAP) {
+ dprintk("type not mmap\n");
+ return -EINVAL;
+ }
+
+ dprintk("count = %d\n", rb->count);
+ if (rb->count > 0) {
+ if (vino_is_capturing(vcs)) {
+ dprintk("busy, capturing\n");
+ return -EBUSY;
+ }
+
+ if (vino_queue_has_mapped_buffers(&vcs->fb_queue)) {
+ dprintk("busy, buffers still mapped\n");
+ return -EBUSY;
+ } else {
+ vcs->streaming = 0;
+ vino_queue_free(&vcs->fb_queue);
+ vino_queue_init(&vcs->fb_queue, &rb->count);
+ }
+ } else {
+ vcs->streaming = 0;
+ vino_capture_stop(vcs);
+ vino_queue_free(&vcs->fb_queue);
+ }
+
+ return 0;
+}
+
+static void vino_v4l2_get_buffer_status(struct vino_channel_settings *vcs,
+ struct vino_framebuffer *fb,
+ struct v4l2_buffer *b)
+{
+ if (vino_queue_outgoing_contains(&vcs->fb_queue,
+ fb->id)) {
+ b->flags &= ~V4L2_BUF_FLAG_QUEUED;
+ b->flags |= V4L2_BUF_FLAG_DONE;
+ } else if (vino_queue_incoming_contains(&vcs->fb_queue,
+ fb->id)) {
+ b->flags &= ~V4L2_BUF_FLAG_DONE;
+ b->flags |= V4L2_BUF_FLAG_QUEUED;
+ } else {
+ b->flags &= ~(V4L2_BUF_FLAG_DONE |
+ V4L2_BUF_FLAG_QUEUED);
+ }
+
+ b->flags &= ~(V4L2_BUF_FLAG_TIMECODE);
+
+ if (fb->map_count > 0)
+ b->flags |= V4L2_BUF_FLAG_MAPPED;
+
+ b->index = fb->id;
+ b->memory = (vcs->fb_queue.type == VINO_MEMORY_MMAP) ?
+ V4L2_MEMORY_MMAP : V4L2_MEMORY_USERPTR;
+ b->m.offset = fb->offset;
+ b->bytesused = fb->data_size;
+ b->length = fb->size;
+ b->field = V4L2_FIELD_INTERLACED;
+ b->sequence = fb->frame_counter;
+ memcpy(&b->timestamp, &fb->timestamp,
+ sizeof(struct timeval));
+ // b->input ?
+
+ dprintk("buffer %d: length = %d, bytesused = %d, offset = %d\n",
+ fb->id, fb->size, fb->data_size, fb->offset);
+}
+
+static int vino_querybuf(struct file *file, void *__fh,
+ struct v4l2_buffer *b)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ struct vino_framebuffer *fb;
+
+ if (vcs->reading)
+ return -EBUSY;
+
+ /* TODO: check queue type */
+ if (b->index >= vino_queue_get_length(&vcs->fb_queue)) {
+ dprintk("invalid index = %d\n",
+ b->index);
+ return -EINVAL;
+ }
+
+ fb = vino_queue_get_buffer(&vcs->fb_queue,
+ b->index);
+ if (fb == NULL) {
+ dprintk("vino_queue_get_buffer() failed");
+ return -EINVAL;
+ }
+
+ vino_v4l2_get_buffer_status(vcs, fb, b);
+
+ return 0;
+}
+
+static int vino_qbuf(struct file *file, void *__fh,
+ struct v4l2_buffer *b)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ struct vino_framebuffer *fb;
+ int ret;
+
+ if (vcs->reading)
+ return -EBUSY;
+
+ /* TODO: check queue type */
+ if (b->memory != V4L2_MEMORY_MMAP) {
+ dprintk("type not mmap\n");
+ return -EINVAL;
+ }
+
+ fb = vino_capture_enqueue(vcs, b->index);
+ if (fb == NULL)
+ return -EINVAL;
+
+ vino_v4l2_get_buffer_status(vcs, fb, b);
+
+ if (vcs->streaming) {
+ ret = vino_capture_next(vcs, 1);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int vino_dqbuf(struct file *file, void *__fh,
+ struct v4l2_buffer *b)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned int nonblocking = file->f_flags & O_NONBLOCK;
+ struct vino_framebuffer *fb;
+ unsigned int incoming, outgoing;
+ int err;
+
+ if (vcs->reading)
+ return -EBUSY;
+
+ /* TODO: check queue type */
+
+ err = vino_queue_get_incoming(&vcs->fb_queue, &incoming);
+ if (err) {
+ dprintk("vino_queue_get_incoming() failed\n");
+ return -EINVAL;
+ }
+ err = vino_queue_get_outgoing(&vcs->fb_queue, &outgoing);
+ if (err) {
+ dprintk("vino_queue_get_outgoing() failed\n");
+ return -EINVAL;
+ }
+
+ dprintk("incoming = %d, outgoing = %d\n", incoming, outgoing);
+
+ if (outgoing == 0) {
+ if (incoming == 0) {
+ dprintk("no incoming or outgoing buffers\n");
+ return -EINVAL;
+ }
+ if (nonblocking) {
+ dprintk("non-blocking I/O was selected and "
+ "there are no buffers to dequeue\n");
+ return -EAGAIN;
+ }
+
+ err = vino_wait_for_frame(vcs);
+ if (err) {
+ err = vino_wait_for_frame(vcs);
+ if (err) {
+ /* interrupted or no frames captured because of
+ * frame skipping */
+ /* vino_capture_failed(vcs); */
+ return -EIO;
+ }
+ }
+ }
+
+ fb = vino_queue_remove(&vcs->fb_queue, &b->index);
+ if (fb == NULL) {
+ dprintk("vino_queue_remove() failed\n");
+ return -EINVAL;
+ }
+
+ err = vino_check_buffer(vcs, fb);
+
+ vino_v4l2_get_buffer_status(vcs, fb, b);
+
+ if (err)
+ return -EIO;
+
+ return 0;
+}
+
+static int vino_streamon(struct file *file, void *__fh,
+ enum v4l2_buf_type i)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned int incoming;
+ int ret;
+ if (vcs->reading)
+ return -EBUSY;
+
+ if (vcs->streaming)
+ return 0;
+
+ // TODO: check queue type
+
+ if (vino_queue_get_length(&vcs->fb_queue) < 1) {
+ dprintk("no buffers allocated\n");
+ return -EINVAL;
+ }
+
+ ret = vino_queue_get_incoming(&vcs->fb_queue, &incoming);
+ if (ret) {
+ dprintk("vino_queue_get_incoming() failed\n");
+ return -EINVAL;
+ }
+
+ vcs->streaming = 1;
+
+ if (incoming > 0) {
+ ret = vino_capture_next(vcs, 1);
+ if (ret) {
+ vcs->streaming = 0;
+
+ dprintk("couldn't start capture\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int vino_streamoff(struct file *file, void *__fh,
+ enum v4l2_buf_type i)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ if (vcs->reading)
+ return -EBUSY;
+
+ if (!vcs->streaming)
+ return 0;
+
+ vcs->streaming = 0;
+ vino_capture_stop(vcs);
+
+ return 0;
+}
+
+static int vino_queryctrl(struct file *file, void *__fh,
+ struct v4l2_queryctrl *queryctrl)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+ int i;
+ int err = 0;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ switch (vcs->input) {
+ case VINO_INPUT_D1:
+ for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) {
+ if (vino_indycam_v4l2_controls[i].id ==
+ queryctrl->id) {
+ memcpy(queryctrl,
+ &vino_indycam_v4l2_controls[i],
+ sizeof(struct v4l2_queryctrl));
+ queryctrl->reserved[0] = 0;
+ goto found;
+ }
+ }
+
+ err = -EINVAL;
+ break;
+ case VINO_INPUT_COMPOSITE:
+ case VINO_INPUT_SVIDEO:
+ for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) {
+ if (vino_saa7191_v4l2_controls[i].id ==
+ queryctrl->id) {
+ memcpy(queryctrl,
+ &vino_saa7191_v4l2_controls[i],
+ sizeof(struct v4l2_queryctrl));
+ queryctrl->reserved[0] = 0;
+ goto found;
+ }
+ }
+
+ err = -EINVAL;
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ found:
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return err;
+}
+
+static int vino_g_ctrl(struct file *file, void *__fh,
+ struct v4l2_control *control)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+ int i;
+ int err = 0;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ switch (vcs->input) {
+ case VINO_INPUT_D1: {
+ err = -EINVAL;
+ for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) {
+ if (vino_indycam_v4l2_controls[i].id == control->id) {
+ err = 0;
+ break;
+ }
+ }
+
+ if (err)
+ goto out;
+
+ err = camera_call(core, g_ctrl, control);
+ if (err)
+ err = -EINVAL;
+ break;
+ }
+ case VINO_INPUT_COMPOSITE:
+ case VINO_INPUT_SVIDEO: {
+ err = -EINVAL;
+ for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) {
+ if (vino_saa7191_v4l2_controls[i].id == control->id) {
+ err = 0;
+ break;
+ }
+ }
+
+ if (err)
+ goto out;
+
+ err = decoder_call(core, g_ctrl, control);
+ if (err)
+ err = -EINVAL;
+ break;
+ }
+ default:
+ err = -EINVAL;
+ }
+
+out:
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return err;
+}
+
+static int vino_s_ctrl(struct file *file, void *__fh,
+ struct v4l2_control *control)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+ int i;
+ int err = 0;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ if (!vino_is_input_owner(vcs)) {
+ err = -EBUSY;
+ goto out;
+ }
+
+ switch (vcs->input) {
+ case VINO_INPUT_D1: {
+ err = -EINVAL;
+ for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) {
+ if (vino_indycam_v4l2_controls[i].id == control->id) {
+ err = 0;
+ break;
+ }
+ }
+ if (err)
+ goto out;
+ if (control->value < vino_indycam_v4l2_controls[i].minimum ||
+ control->value > vino_indycam_v4l2_controls[i].maximum) {
+ err = -ERANGE;
+ goto out;
+ }
+ err = camera_call(core, s_ctrl, control);
+ if (err)
+ err = -EINVAL;
+ break;
+ }
+ case VINO_INPUT_COMPOSITE:
+ case VINO_INPUT_SVIDEO: {
+ err = -EINVAL;
+ for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) {
+ if (vino_saa7191_v4l2_controls[i].id == control->id) {
+ err = 0;
+ break;
+ }
+ }
+ if (err)
+ goto out;
+ if (control->value < vino_saa7191_v4l2_controls[i].minimum ||
+ control->value > vino_saa7191_v4l2_controls[i].maximum) {
+ err = -ERANGE;
+ goto out;
+ }
+
+ err = decoder_call(core, s_ctrl, control);
+ if (err)
+ err = -EINVAL;
+ break;
+ }
+ default:
+ err = -EINVAL;
+ }
+
+out:
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return err;
+}
+
+/* File operations */
+
+static int vino_open(struct file *file)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ int ret = 0;
+ dprintk("open(): channel = %c\n",
+ (vcs->channel == VINO_CHANNEL_A) ? 'A' : 'B');
+
+ mutex_lock(&vcs->mutex);
+
+ if (vcs->users) {
+ dprintk("open(): driver busy\n");
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ret = vino_acquire_input(vcs);
+ if (ret) {
+ dprintk("open(): vino_acquire_input() failed\n");
+ goto out;
+ }
+
+ vcs->users++;
+
+ out:
+ mutex_unlock(&vcs->mutex);
+
+ dprintk("open(): %s!\n", ret ? "failed" : "complete");
+
+ return ret;
+}
+
+static int vino_close(struct file *file)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ dprintk("close():\n");
+
+ mutex_lock(&vcs->mutex);
+
+ vcs->users--;
+
+ if (!vcs->users) {
+ vino_release_input(vcs);
+
+ /* stop DMA and free buffers */
+ vino_capture_stop(vcs);
+ vino_queue_free(&vcs->fb_queue);
+ }
+
+ mutex_unlock(&vcs->mutex);
+
+ return 0;
+}
+
+static void vino_vm_open(struct vm_area_struct *vma)
+{
+ struct vino_framebuffer *fb = vma->vm_private_data;
+
+ fb->map_count++;
+ dprintk("vino_vm_open(): count = %d\n", fb->map_count);
+}
+
+static void vino_vm_close(struct vm_area_struct *vma)
+{
+ struct vino_framebuffer *fb = vma->vm_private_data;
+
+ fb->map_count--;
+ dprintk("vino_vm_close(): count = %d\n", fb->map_count);
+}
+
+static const struct vm_operations_struct vino_vm_ops = {
+ .open = vino_vm_open,
+ .close = vino_vm_close,
+};
+
+static int vino_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+
+ unsigned long start = vma->vm_start;
+ unsigned long size = vma->vm_end - vma->vm_start;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+
+ struct vino_framebuffer *fb = NULL;
+ unsigned int i, length;
+ int ret = 0;
+
+ dprintk("mmap():\n");
+
+ // TODO: reject mmap if already mapped
+
+ if (mutex_lock_interruptible(&vcs->mutex))
+ return -EINTR;
+
+ if (vcs->reading) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ // TODO: check queue type
+
+ if (!(vma->vm_flags & VM_WRITE)) {
+ dprintk("mmap(): app bug: PROT_WRITE please\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ if (!(vma->vm_flags & VM_SHARED)) {
+ dprintk("mmap(): app bug: MAP_SHARED please\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* find the correct buffer using offset */
+ length = vino_queue_get_length(&vcs->fb_queue);
+ if (length == 0) {
+ dprintk("mmap(): queue not initialized\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ for (i = 0; i < length; i++) {
+ fb = vino_queue_get_buffer(&vcs->fb_queue, i);
+ if (fb == NULL) {
+ dprintk("mmap(): vino_queue_get_buffer() failed\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (fb->offset == offset)
+ goto found;
+ }
+
+ dprintk("mmap(): invalid offset = %lu\n", offset);
+ ret = -EINVAL;
+ goto out;
+
+found:
+ dprintk("mmap(): buffer = %d\n", i);
+
+ if (size > (fb->desc_table.page_count * PAGE_SIZE)) {
+ dprintk("mmap(): failed: size = %lu > %lu\n",
+ size, fb->desc_table.page_count * PAGE_SIZE);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ for (i = 0; i < fb->desc_table.page_count; i++) {
+ unsigned long pfn =
+ virt_to_phys((void *)fb->desc_table.virtual[i]) >>
+ PAGE_SHIFT;
+
+ if (size < PAGE_SIZE)
+ break;
+
+ // protection was: PAGE_READONLY
+ if (remap_pfn_range(vma, start, pfn, PAGE_SIZE,
+ vma->vm_page_prot)) {
+ dprintk("mmap(): remap_pfn_range() failed\n");
+ ret = -EAGAIN;
+ goto out;
+ }
+
+ start += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ fb->map_count = 1;
+
+ vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED;
+ vma->vm_flags &= ~VM_IO;
+ vma->vm_private_data = fb;
+ vma->vm_file = file;
+ vma->vm_ops = &vino_vm_ops;
+
+out:
+ mutex_unlock(&vcs->mutex);
+
+ return ret;
+}
+
+static unsigned int vino_poll(struct file *file, poll_table *pt)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned int outgoing;
+ unsigned int ret = 0;
+
+ // lock mutex (?)
+ // TODO: this has to be corrected for different read modes
+
+ dprintk("poll():\n");
+
+ if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) {
+ dprintk("poll(): vino_queue_get_outgoing() failed\n");
+ ret = POLLERR;
+ goto error;
+ }
+ if (outgoing > 0)
+ goto over;
+
+ poll_wait(file, &vcs->fb_queue.frame_wait_queue, pt);
+
+ if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) {
+ dprintk("poll(): vino_queue_get_outgoing() failed\n");
+ ret = POLLERR;
+ goto error;
+ }
+
+over:
+ dprintk("poll(): data %savailable\n",
+ (outgoing > 0) ? "" : "not ");
+
+ if (outgoing > 0)
+ ret = POLLIN | POLLRDNORM;
+
+error:
+ return ret;
+}
+
+static long vino_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ long ret;
+
+ if (mutex_lock_interruptible(&vcs->mutex))
+ return -EINTR;
+
+ ret = video_ioctl2(file, cmd, arg);
+
+ mutex_unlock(&vcs->mutex);
+
+ return ret;
+}
+
+/* Initialization and cleanup */
+
+/* __initdata */
+static int vino_init_stage;
+
+const struct v4l2_ioctl_ops vino_ioctl_ops = {
+ .vidioc_enum_fmt_vid_cap = vino_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vino_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vino_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vino_try_fmt_vid_cap,
+ .vidioc_querycap = vino_querycap,
+ .vidioc_enum_input = vino_enum_input,
+ .vidioc_g_input = vino_g_input,
+ .vidioc_s_input = vino_s_input,
+ .vidioc_g_std = vino_g_std,
+ .vidioc_s_std = vino_s_std,
+ .vidioc_querystd = vino_querystd,
+ .vidioc_cropcap = vino_cropcap,
+ .vidioc_s_crop = vino_s_crop,
+ .vidioc_g_crop = vino_g_crop,
+ .vidioc_s_parm = vino_s_parm,
+ .vidioc_g_parm = vino_g_parm,
+ .vidioc_reqbufs = vino_reqbufs,
+ .vidioc_querybuf = vino_querybuf,
+ .vidioc_qbuf = vino_qbuf,
+ .vidioc_dqbuf = vino_dqbuf,
+ .vidioc_streamon = vino_streamon,
+ .vidioc_streamoff = vino_streamoff,
+ .vidioc_queryctrl = vino_queryctrl,
+ .vidioc_g_ctrl = vino_g_ctrl,
+ .vidioc_s_ctrl = vino_s_ctrl,
+};
+
+static const struct v4l2_file_operations vino_fops = {
+ .owner = THIS_MODULE,
+ .open = vino_open,
+ .release = vino_close,
+ .unlocked_ioctl = vino_ioctl,
+ .mmap = vino_mmap,
+ .poll = vino_poll,
+};
+
+static struct video_device vdev_template = {
+ .name = "NOT SET",
+ .fops = &vino_fops,
+ .ioctl_ops = &vino_ioctl_ops,
+ .tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM,
+};
+
+static void vino_module_cleanup(int stage)
+{
+ switch(stage) {
+ case 11:
+ video_unregister_device(vino_drvdata->b.vdev);
+ vino_drvdata->b.vdev = NULL;
+ case 10:
+ video_unregister_device(vino_drvdata->a.vdev);
+ vino_drvdata->a.vdev = NULL;
+ case 9:
+ i2c_del_adapter(&vino_i2c_adapter);
+ case 8:
+ free_irq(SGI_VINO_IRQ, NULL);
+ case 7:
+ if (vino_drvdata->b.vdev) {
+ video_device_release(vino_drvdata->b.vdev);
+ vino_drvdata->b.vdev = NULL;
+ }
+ case 6:
+ if (vino_drvdata->a.vdev) {
+ video_device_release(vino_drvdata->a.vdev);
+ vino_drvdata->a.vdev = NULL;
+ }
+ case 5:
+ /* all entries in dma_cpu dummy table have the same address */
+ dma_unmap_single(NULL,
+ vino_drvdata->dummy_desc_table.dma_cpu[0],
+ PAGE_SIZE, DMA_FROM_DEVICE);
+ dma_free_coherent(NULL, VINO_DUMMY_DESC_COUNT
+ * sizeof(dma_addr_t),
+ (void *)vino_drvdata->
+ dummy_desc_table.dma_cpu,
+ vino_drvdata->dummy_desc_table.dma);
+ case 4:
+ free_page(vino_drvdata->dummy_page);
+ case 3:
+ v4l2_device_unregister(&vino_drvdata->v4l2_dev);
+ case 2:
+ kfree(vino_drvdata);
+ case 1:
+ iounmap(vino);
+ case 0:
+ break;
+ default:
+ dprintk("vino_module_cleanup(): invalid cleanup stage = %d\n",
+ stage);
+ }
+}
+
+static int vino_probe(void)
+{
+ unsigned long rev_id;
+
+ if (ip22_is_fullhouse()) {
+ printk(KERN_ERR "VINO doesn't exist in IP22 Fullhouse\n");
+ return -ENODEV;
+ }
+
+ if (!(sgimc->systemid & SGIMC_SYSID_EPRESENT)) {
+ printk(KERN_ERR "VINO is not found (EISA BUS not present)\n");
+ return -ENODEV;
+ }
+
+ vino = (struct sgi_vino *)ioremap(VINO_BASE, sizeof(struct sgi_vino));
+ if (!vino) {
+ printk(KERN_ERR "VINO: ioremap() failed\n");
+ return -EIO;
+ }
+ vino_init_stage++;
+
+ if (get_dbe(rev_id, &(vino->rev_id))) {
+ printk(KERN_ERR "Failed to read VINO revision register\n");
+ vino_module_cleanup(vino_init_stage);
+ return -ENODEV;
+ }
+
+ if (VINO_ID_VALUE(rev_id) != VINO_CHIP_ID) {
+ printk(KERN_ERR "Unknown VINO chip ID (Rev/ID: 0x%02lx)\n",
+ rev_id);
+ vino_module_cleanup(vino_init_stage);
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO "VINO revision %ld found\n", VINO_REV_NUM(rev_id));
+
+ return 0;
+}
+
+static int vino_init(void)
+{
+ dma_addr_t dma_dummy_address;
+ int err;
+ int i;
+
+ vino_drvdata = kzalloc(sizeof(struct vino_settings), GFP_KERNEL);
+ if (!vino_drvdata) {
+ vino_module_cleanup(vino_init_stage);
+ return -ENOMEM;
+ }
+ vino_init_stage++;
+ strlcpy(vino_drvdata->v4l2_dev.name, "vino",
+ sizeof(vino_drvdata->v4l2_dev.name));
+ err = v4l2_device_register(NULL, &vino_drvdata->v4l2_dev);
+ if (err)
+ return err;
+ vino_init_stage++;
+
+ /* create a dummy dma descriptor */
+ vino_drvdata->dummy_page = get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!vino_drvdata->dummy_page) {
+ vino_module_cleanup(vino_init_stage);
+ return -ENOMEM;
+ }
+ vino_init_stage++;
+
+ // TODO: use page_count in dummy_desc_table
+
+ vino_drvdata->dummy_desc_table.dma_cpu =
+ dma_alloc_coherent(NULL,
+ VINO_DUMMY_DESC_COUNT * sizeof(dma_addr_t),
+ &vino_drvdata->dummy_desc_table.dma,
+ GFP_KERNEL | GFP_DMA);
+ if (!vino_drvdata->dummy_desc_table.dma_cpu) {
+ vino_module_cleanup(vino_init_stage);
+ return -ENOMEM;
+ }
+ vino_init_stage++;
+
+ dma_dummy_address = dma_map_single(NULL,
+ (void *)vino_drvdata->dummy_page,
+ PAGE_SIZE, DMA_FROM_DEVICE);
+ for (i = 0; i < VINO_DUMMY_DESC_COUNT; i++) {
+ vino_drvdata->dummy_desc_table.dma_cpu[i] = dma_dummy_address;
+ }
+
+ /* initialize VINO */
+
+ vino->control = 0;
+ vino->a.next_4_desc = vino_drvdata->dummy_desc_table.dma;
+ vino->b.next_4_desc = vino_drvdata->dummy_desc_table.dma;
+ udelay(VINO_DESC_FETCH_DELAY);
+
+ vino->intr_status = 0;
+
+ vino->a.fifo_thres = VINO_FIFO_THRESHOLD_DEFAULT;
+ vino->b.fifo_thres = VINO_FIFO_THRESHOLD_DEFAULT;
+
+ return 0;
+}
+
+static int vino_init_channel_settings(struct vino_channel_settings *vcs,
+ unsigned int channel, const char *name)
+{
+ vcs->channel = channel;
+ vcs->input = VINO_INPUT_NONE;
+ vcs->alpha = 0;
+ vcs->users = 0;
+ vcs->data_format = VINO_DATA_FMT_GREY;
+ vcs->data_norm = VINO_DATA_NORM_NTSC;
+ vcs->decimation = 1;
+ vino_set_default_clipping(vcs);
+ vino_set_default_framerate(vcs);
+
+ vcs->capturing = 0;
+
+ mutex_init(&vcs->mutex);
+ spin_lock_init(&vcs->capture_lock);
+
+ mutex_init(&vcs->fb_queue.queue_mutex);
+ spin_lock_init(&vcs->fb_queue.queue_lock);
+ init_waitqueue_head(&vcs->fb_queue.frame_wait_queue);
+
+ vcs->vdev = video_device_alloc();
+ if (!vcs->vdev) {
+ vino_module_cleanup(vino_init_stage);
+ return -ENOMEM;
+ }
+ vino_init_stage++;
+
+ memcpy(vcs->vdev, &vdev_template,
+ sizeof(struct video_device));
+ strcpy(vcs->vdev->name, name);
+ vcs->vdev->release = video_device_release;
+ vcs->vdev->v4l2_dev = &vino_drvdata->v4l2_dev;
+
+ video_set_drvdata(vcs->vdev, vcs);
+
+ return 0;
+}
+
+static int __init vino_module_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO "SGI VINO driver version %s\n",
+ VINO_MODULE_VERSION);
+
+ ret = vino_probe();
+ if (ret)
+ return ret;
+
+ ret = vino_init();
+ if (ret)
+ return ret;
+
+ /* initialize data structures */
+
+ spin_lock_init(&vino_drvdata->vino_lock);
+ spin_lock_init(&vino_drvdata->input_lock);
+
+ ret = vino_init_channel_settings(&vino_drvdata->a, VINO_CHANNEL_A,
+ vino_vdev_name_a);
+ if (ret)
+ return ret;
+
+ ret = vino_init_channel_settings(&vino_drvdata->b, VINO_CHANNEL_B,
+ vino_vdev_name_b);
+ if (ret)
+ return ret;
+
+ /* initialize hardware and register V4L devices */
+
+ ret = request_irq(SGI_VINO_IRQ, vino_interrupt, 0,
+ vino_driver_description, NULL);
+ if (ret) {
+ printk(KERN_ERR "VINO: requesting IRQ %02d failed\n",
+ SGI_VINO_IRQ);
+ vino_module_cleanup(vino_init_stage);
+ return -EAGAIN;
+ }
+ vino_init_stage++;
+
+ ret = i2c_add_adapter(&vino_i2c_adapter);
+ if (ret) {
+ printk(KERN_ERR "VINO I2C bus registration failed\n");
+ vino_module_cleanup(vino_init_stage);
+ return ret;
+ }
+ i2c_set_adapdata(&vino_i2c_adapter, &vino_drvdata->v4l2_dev);
+ vino_init_stage++;
+
+ ret = video_register_device(vino_drvdata->a.vdev,
+ VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ printk(KERN_ERR "VINO channel A Video4Linux-device "
+ "registration failed\n");
+ vino_module_cleanup(vino_init_stage);
+ return -EINVAL;
+ }
+ vino_init_stage++;
+
+ ret = video_register_device(vino_drvdata->b.vdev,
+ VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ printk(KERN_ERR "VINO channel B Video4Linux-device "
+ "registration failed\n");
+ vino_module_cleanup(vino_init_stage);
+ return -EINVAL;
+ }
+ vino_init_stage++;
+
+ vino_drvdata->decoder =
+ v4l2_i2c_new_subdev(&vino_drvdata->v4l2_dev, &vino_i2c_adapter,
+ "saa7191", 0, I2C_ADDRS(0x45));
+ vino_drvdata->camera =
+ v4l2_i2c_new_subdev(&vino_drvdata->v4l2_dev, &vino_i2c_adapter,
+ "indycam", 0, I2C_ADDRS(0x2b));
+
+ dprintk("init complete!\n");
+
+ return 0;
+}
+
+static void __exit vino_module_exit(void)
+{
+ dprintk("exiting, stage = %d ...\n", vino_init_stage);
+ vino_module_cleanup(vino_init_stage);
+ dprintk("cleanup complete, exit!\n");
+}
+
+module_init(vino_module_init);
+module_exit(vino_module_exit);
diff --git a/drivers/media/video/vino.h b/drivers/media/video/vino.h
new file mode 100644
index 00000000..de2d615a
--- /dev/null
+++ b/drivers/media/video/vino.h
@@ -0,0 +1,138 @@
+/*
+ * Driver for the VINO (Video In No Out) system found in SGI Indys.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * Copyright (C) 1999 Ulf Karlsson <ulfc@bun.falkenberg.se>
+ * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
+ */
+
+#ifndef _VINO_H_
+#define _VINO_H_
+
+#define VINO_BASE 0x00080000 /* Vino is in the EISA address space,
+ * but it is not an EISA bus card */
+#define VINO_PAGE_SIZE 4096
+
+struct sgi_vino_channel {
+ u32 _pad_alpha;
+ volatile u32 alpha;
+
+#define VINO_CLIP_X(x) ((x) & 0x3ff) /* bits 0:9 */
+#define VINO_CLIP_ODD(x) (((x) & 0x1ff) << 10) /* bits 10:18 */
+#define VINO_CLIP_EVEN(x) (((x) & 0x1ff) << 19) /* bits 19:27 */
+ u32 _pad_clip_start;
+ volatile u32 clip_start;
+ u32 _pad_clip_end;
+ volatile u32 clip_end;
+
+#define VINO_FRAMERT_FULL 0xfff
+#define VINO_FRAMERT_PAL (1<<0) /* 0=NTSC 1=PAL */
+#define VINO_FRAMERT_RT(x) (((x) & 0xfff) << 1) /* bits 1:12 */
+ u32 _pad_frame_rate;
+ volatile u32 frame_rate;
+
+ u32 _pad_field_counter;
+ volatile u32 field_counter;
+ u32 _pad_line_size;
+ volatile u32 line_size;
+ u32 _pad_line_count;
+ volatile u32 line_count;
+ u32 _pad_page_index;
+ volatile u32 page_index;
+ u32 _pad_next_4_desc;
+ volatile u32 next_4_desc;
+ u32 _pad_start_desc_tbl;
+ volatile u32 start_desc_tbl;
+
+#define VINO_DESC_JUMP (1<<30)
+#define VINO_DESC_STOP (1<<31)
+#define VINO_DESC_VALID (1<<32)
+ u32 _pad_desc_0;
+ volatile u32 desc_0;
+ u32 _pad_desc_1;
+ volatile u32 desc_1;
+ u32 _pad_desc_2;
+ volatile u32 desc_2;
+ u32 _pad_Bdesc_3;
+ volatile u32 desc_3;
+
+ u32 _pad_fifo_thres;
+ volatile u32 fifo_thres;
+ u32 _pad_fifo_read;
+ volatile u32 fifo_read;
+ u32 _pad_fifo_write;
+ volatile u32 fifo_write;
+};
+
+struct sgi_vino {
+#define VINO_CHIP_ID 0xb
+#define VINO_REV_NUM(x) ((x) & 0x0f)
+#define VINO_ID_VALUE(x) (((x) & 0xf0) >> 4)
+ u32 _pad_rev_id;
+ volatile u32 rev_id;
+
+#define VINO_CTRL_LITTLE_ENDIAN (1<<0)
+#define VINO_CTRL_A_EOF_INT (1<<1) /* Field transferred int */
+#define VINO_CTRL_A_FIFO_INT (1<<2) /* FIFO overflow int */
+#define VINO_CTRL_A_EOD_INT (1<<3) /* End of desc table int */
+#define VINO_CTRL_A_INT (VINO_CTRL_A_EOF_INT | \
+ VINO_CTRL_A_FIFO_INT | \
+ VINO_CTRL_A_EOD_INT)
+#define VINO_CTRL_B_EOF_INT (1<<4) /* Field transferred int */
+#define VINO_CTRL_B_FIFO_INT (1<<5) /* FIFO overflow int */
+#define VINO_CTRL_B_EOD_INT (1<<6) /* End of desc table int */
+#define VINO_CTRL_B_INT (VINO_CTRL_B_EOF_INT | \
+ VINO_CTRL_B_FIFO_INT | \
+ VINO_CTRL_B_EOD_INT)
+#define VINO_CTRL_A_DMA_ENBL (1<<7)
+#define VINO_CTRL_A_INTERLEAVE_ENBL (1<<8)
+#define VINO_CTRL_A_SYNC_ENBL (1<<9)
+#define VINO_CTRL_A_SELECT (1<<10) /* 1=D1 0=Philips */
+#define VINO_CTRL_A_RGB (1<<11) /* 1=RGB 0=YUV */
+#define VINO_CTRL_A_LUMA_ONLY (1<<12)
+#define VINO_CTRL_A_DEC_ENBL (1<<13) /* Decimation */
+#define VINO_CTRL_A_DEC_SCALE_MASK 0x1c000 /* bits 14:17 */
+#define VINO_CTRL_A_DEC_SCALE_SHIFT (14)
+#define VINO_CTRL_A_DEC_HOR_ONLY (1<<17) /* Horizontal only */
+#define VINO_CTRL_A_DITHER (1<<18) /* 24 -> 8 bit dither */
+#define VINO_CTRL_B_DMA_ENBL (1<<19)
+#define VINO_CTRL_B_INTERLEAVE_ENBL (1<<20)
+#define VINO_CTRL_B_SYNC_ENBL (1<<21)
+#define VINO_CTRL_B_SELECT (1<<22) /* 1=D1 0=Philips */
+#define VINO_CTRL_B_RGB (1<<23) /* 1=RGB 0=YUV */
+#define VINO_CTRL_B_LUMA_ONLY (1<<24)
+#define VINO_CTRL_B_DEC_ENBL (1<<25) /* Decimation */
+#define VINO_CTRL_B_DEC_SCALE_MASK 0x1c000000 /* bits 26:28 */
+#define VINO_CTRL_B_DEC_SCALE_SHIFT (26)
+#define VINO_CTRL_B_DEC_HOR_ONLY (1<<29) /* Decimation horizontal only */
+#define VINO_CTRL_B_DITHER (1<<30) /* ChanB 24 -> 8 bit dither */
+ u32 _pad_control;
+ volatile u32 control;
+
+#define VINO_INTSTAT_A_EOF (1<<0) /* Field transferred int */
+#define VINO_INTSTAT_A_FIFO (1<<1) /* FIFO overflow int */
+#define VINO_INTSTAT_A_EOD (1<<2) /* End of desc table int */
+#define VINO_INTSTAT_A (VINO_INTSTAT_A_EOF | \
+ VINO_INTSTAT_A_FIFO | \
+ VINO_INTSTAT_A_EOD)
+#define VINO_INTSTAT_B_EOF (1<<3) /* Field transferred int */
+#define VINO_INTSTAT_B_FIFO (1<<4) /* FIFO overflow int */
+#define VINO_INTSTAT_B_EOD (1<<5) /* End of desc table int */
+#define VINO_INTSTAT_B (VINO_INTSTAT_B_EOF | \
+ VINO_INTSTAT_B_FIFO | \
+ VINO_INTSTAT_B_EOD)
+ u32 _pad_intr_status;
+ volatile u32 intr_status;
+
+ u32 _pad_i2c_control;
+ volatile u32 i2c_control;
+ u32 _pad_i2c_data;
+ volatile u32 i2c_data;
+
+ struct sgi_vino_channel a;
+ struct sgi_vino_channel b;
+};
+
+#endif
diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
new file mode 100644
index 00000000..5e8b0710
--- /dev/null
+++ b/drivers/media/video/vivi.c
@@ -0,0 +1,1400 @@
+/*
+ * Virtual Video driver - This code emulates a real video device with v4l2 api
+ *
+ * Copyright (c) 2006 by:
+ * Mauro Carvalho Chehab <mchehab--a.t--infradead.org>
+ * Ted Walther <ted--a.t--enumera.com>
+ * John Sokol <sokol--a.t--videotechnology.com>
+ * http://v4l.videotechnology.com/
+ *
+ * Conversion to videobuf2 by Pawel Osciak & Marek Szyprowski
+ * Copyright (c) 2010 Samsung Electronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the BSD Licence, GNU General Public License
+ * as published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version
+ */
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/font.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <media/videobuf2-vmalloc.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-common.h>
+
+#define VIVI_MODULE_NAME "vivi"
+
+/* Wake up at about 30 fps */
+#define WAKE_NUMERATOR 30
+#define WAKE_DENOMINATOR 1001
+#define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */
+
+#define MAX_WIDTH 1920
+#define MAX_HEIGHT 1200
+
+#define VIVI_VERSION "0.8.1"
+
+MODULE_DESCRIPTION("Video Technology Magazine Virtual Video Capture Board");
+MODULE_AUTHOR("Mauro Carvalho Chehab, Ted Walther and John Sokol");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_VERSION(VIVI_VERSION);
+
+static unsigned video_nr = -1;
+module_param(video_nr, uint, 0644);
+MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect");
+
+static unsigned n_devs = 1;
+module_param(n_devs, uint, 0644);
+MODULE_PARM_DESC(n_devs, "number of video devices to create");
+
+static unsigned debug;
+module_param(debug, uint, 0644);
+MODULE_PARM_DESC(debug, "activates debug info");
+
+static unsigned int vid_limit = 16;
+module_param(vid_limit, uint, 0644);
+MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
+
+/* Global font descriptor */
+static const u8 *font8x16;
+
+#define dprintk(dev, level, fmt, arg...) \
+ v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg)
+
+/* ------------------------------------------------------------------
+ Basic structures
+ ------------------------------------------------------------------*/
+
+struct vivi_fmt {
+ char *name;
+ u32 fourcc; /* v4l2 format id */
+ int depth;
+};
+
+static struct vivi_fmt formats[] = {
+ {
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ },
+ {
+ .name = "4:2:2, packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = 16,
+ },
+ {
+ .name = "RGB565 (LE)",
+ .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
+ .depth = 16,
+ },
+ {
+ .name = "RGB565 (BE)",
+ .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
+ .depth = 16,
+ },
+ {
+ .name = "RGB555 (LE)",
+ .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */
+ .depth = 16,
+ },
+ {
+ .name = "RGB555 (BE)",
+ .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
+ .depth = 16,
+ },
+};
+
+static struct vivi_fmt *get_format(struct v4l2_format *f)
+{
+ struct vivi_fmt *fmt;
+ unsigned int k;
+
+ for (k = 0; k < ARRAY_SIZE(formats); k++) {
+ fmt = &formats[k];
+ if (fmt->fourcc == f->fmt.pix.pixelformat)
+ break;
+ }
+
+ if (k == ARRAY_SIZE(formats))
+ return NULL;
+
+ return &formats[k];
+}
+
+/* buffer for one video frame */
+struct vivi_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct vb2_buffer vb;
+ struct list_head list;
+ struct vivi_fmt *fmt;
+};
+
+struct vivi_dmaqueue {
+ struct list_head active;
+
+ /* thread for generating video stream*/
+ struct task_struct *kthread;
+ wait_queue_head_t wq;
+ /* Counters to control fps rate */
+ int frame;
+ int ini_jiffies;
+};
+
+static LIST_HEAD(vivi_devlist);
+
+struct vivi_dev {
+ struct list_head vivi_devlist;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_ctrl_handler ctrl_handler;
+
+ /* controls */
+ struct v4l2_ctrl *brightness;
+ struct v4l2_ctrl *contrast;
+ struct v4l2_ctrl *saturation;
+ struct v4l2_ctrl *hue;
+ struct {
+ /* autogain/gain cluster */
+ struct v4l2_ctrl *autogain;
+ struct v4l2_ctrl *gain;
+ };
+ struct v4l2_ctrl *volume;
+ struct v4l2_ctrl *button;
+ struct v4l2_ctrl *boolean;
+ struct v4l2_ctrl *int32;
+ struct v4l2_ctrl *int64;
+ struct v4l2_ctrl *menu;
+ struct v4l2_ctrl *string;
+ struct v4l2_ctrl *bitmask;
+
+ spinlock_t slock;
+ struct mutex mutex;
+
+ /* various device info */
+ struct video_device *vfd;
+
+ struct vivi_dmaqueue vidq;
+
+ /* Several counters */
+ unsigned ms;
+ unsigned long jiffies;
+ unsigned button_pressed;
+
+ int mv_count; /* Controls bars movement */
+
+ /* Input Number */
+ int input;
+
+ /* video capture */
+ struct vivi_fmt *fmt;
+ unsigned int width, height;
+ struct vb2_queue vb_vidq;
+ enum v4l2_field field;
+ unsigned int field_count;
+
+ u8 bars[9][3];
+ u8 line[MAX_WIDTH * 4];
+};
+
+/* ------------------------------------------------------------------
+ DMA and thread functions
+ ------------------------------------------------------------------*/
+
+/* Bars and Colors should match positions */
+
+enum colors {
+ WHITE,
+ AMBER,
+ CYAN,
+ GREEN,
+ MAGENTA,
+ RED,
+ BLUE,
+ BLACK,
+ TEXT_BLACK,
+};
+
+/* R G B */
+#define COLOR_WHITE {204, 204, 204}
+#define COLOR_AMBER {208, 208, 0}
+#define COLOR_CYAN { 0, 206, 206}
+#define COLOR_GREEN { 0, 239, 0}
+#define COLOR_MAGENTA {239, 0, 239}
+#define COLOR_RED {205, 0, 0}
+#define COLOR_BLUE { 0, 0, 255}
+#define COLOR_BLACK { 0, 0, 0}
+
+struct bar_std {
+ u8 bar[9][3];
+};
+
+/* Maximum number of bars are 10 - otherwise, the input print code
+ should be modified */
+static struct bar_std bars[] = {
+ { /* Standard ITU-R color bar sequence */
+ { COLOR_WHITE, COLOR_AMBER, COLOR_CYAN, COLOR_GREEN,
+ COLOR_MAGENTA, COLOR_RED, COLOR_BLUE, COLOR_BLACK, COLOR_BLACK }
+ }, {
+ { COLOR_WHITE, COLOR_AMBER, COLOR_BLACK, COLOR_WHITE,
+ COLOR_AMBER, COLOR_BLACK, COLOR_WHITE, COLOR_AMBER, COLOR_BLACK }
+ }, {
+ { COLOR_WHITE, COLOR_CYAN, COLOR_BLACK, COLOR_WHITE,
+ COLOR_CYAN, COLOR_BLACK, COLOR_WHITE, COLOR_CYAN, COLOR_BLACK }
+ }, {
+ { COLOR_WHITE, COLOR_GREEN, COLOR_BLACK, COLOR_WHITE,
+ COLOR_GREEN, COLOR_BLACK, COLOR_WHITE, COLOR_GREEN, COLOR_BLACK }
+ },
+};
+
+#define NUM_INPUTS ARRAY_SIZE(bars)
+
+#define TO_Y(r, g, b) \
+ (((16829 * r + 33039 * g + 6416 * b + 32768) >> 16) + 16)
+/* RGB to V(Cr) Color transform */
+#define TO_V(r, g, b) \
+ (((28784 * r - 24103 * g - 4681 * b + 32768) >> 16) + 128)
+/* RGB to U(Cb) Color transform */
+#define TO_U(r, g, b) \
+ (((-9714 * r - 19070 * g + 28784 * b + 32768) >> 16) + 128)
+
+/* precalculate color bar values to speed up rendering */
+static void precalculate_bars(struct vivi_dev *dev)
+{
+ u8 r, g, b;
+ int k, is_yuv;
+
+ for (k = 0; k < 9; k++) {
+ r = bars[dev->input].bar[k][0];
+ g = bars[dev->input].bar[k][1];
+ b = bars[dev->input].bar[k][2];
+ is_yuv = 0;
+
+ switch (dev->fmt->fourcc) {
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_UYVY:
+ is_yuv = 1;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB565X:
+ r >>= 3;
+ g >>= 2;
+ b >>= 3;
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ case V4L2_PIX_FMT_RGB555X:
+ r >>= 3;
+ g >>= 3;
+ b >>= 3;
+ break;
+ }
+
+ if (is_yuv) {
+ dev->bars[k][0] = TO_Y(r, g, b); /* Luma */
+ dev->bars[k][1] = TO_U(r, g, b); /* Cb */
+ dev->bars[k][2] = TO_V(r, g, b); /* Cr */
+ } else {
+ dev->bars[k][0] = r;
+ dev->bars[k][1] = g;
+ dev->bars[k][2] = b;
+ }
+ }
+}
+
+#define TSTAMP_MIN_Y 24
+#define TSTAMP_MAX_Y (TSTAMP_MIN_Y + 15)
+#define TSTAMP_INPUT_X 10
+#define TSTAMP_MIN_X (54 + TSTAMP_INPUT_X)
+
+static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos)
+{
+ u8 r_y, g_u, b_v;
+ int color;
+ u8 *p;
+
+ r_y = dev->bars[colorpos][0]; /* R or precalculated Y */
+ g_u = dev->bars[colorpos][1]; /* G or precalculated U */
+ b_v = dev->bars[colorpos][2]; /* B or precalculated V */
+
+ for (color = 0; color < 4; color++) {
+ p = buf + color;
+
+ switch (dev->fmt->fourcc) {
+ case V4L2_PIX_FMT_YUYV:
+ switch (color) {
+ case 0:
+ case 2:
+ *p = r_y;
+ break;
+ case 1:
+ *p = g_u;
+ break;
+ case 3:
+ *p = b_v;
+ break;
+ }
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ switch (color) {
+ case 1:
+ case 3:
+ *p = r_y;
+ break;
+ case 0:
+ *p = g_u;
+ break;
+ case 2:
+ *p = b_v;
+ break;
+ }
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ switch (color) {
+ case 0:
+ case 2:
+ *p = (g_u << 5) | b_v;
+ break;
+ case 1:
+ case 3:
+ *p = (r_y << 3) | (g_u >> 3);
+ break;
+ }
+ break;
+ case V4L2_PIX_FMT_RGB565X:
+ switch (color) {
+ case 0:
+ case 2:
+ *p = (r_y << 3) | (g_u >> 3);
+ break;
+ case 1:
+ case 3:
+ *p = (g_u << 5) | b_v;
+ break;
+ }
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ switch (color) {
+ case 0:
+ case 2:
+ *p = (g_u << 5) | b_v;
+ break;
+ case 1:
+ case 3:
+ *p = (r_y << 2) | (g_u >> 3);
+ break;
+ }
+ break;
+ case V4L2_PIX_FMT_RGB555X:
+ switch (color) {
+ case 0:
+ case 2:
+ *p = (r_y << 2) | (g_u >> 3);
+ break;
+ case 1:
+ case 3:
+ *p = (g_u << 5) | b_v;
+ break;
+ }
+ break;
+ }
+ }
+}
+
+static void precalculate_line(struct vivi_dev *dev)
+{
+ int w;
+
+ for (w = 0; w < dev->width * 2; w += 2) {
+ int colorpos = (w / (dev->width / 8) % 8);
+
+ gen_twopix(dev, dev->line + w * 2, colorpos);
+ }
+}
+
+static void gen_text(struct vivi_dev *dev, char *basep,
+ int y, int x, char *text)
+{
+ int line;
+
+ /* Checks if it is possible to show string */
+ if (y + 16 >= dev->height || x + strlen(text) * 8 >= dev->width)
+ return;
+
+ /* Print stream time */
+ for (line = y; line < y + 16; line++) {
+ int j = 0;
+ char *pos = basep + line * dev->width * 2 + x * 2;
+ char *s;
+
+ for (s = text; *s; s++) {
+ u8 chr = font8x16[*s * 16 + line - y];
+ int i;
+
+ for (i = 0; i < 7; i++, j++) {
+ /* Draw white font on black background */
+ if (chr & (1 << (7 - i)))
+ gen_twopix(dev, pos + j * 2, WHITE);
+ else
+ gen_twopix(dev, pos + j * 2, TEXT_BLACK);
+ }
+ }
+ }
+}
+
+static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf)
+{
+ int wmax = dev->width;
+ int hmax = dev->height;
+ struct timeval ts;
+ void *vbuf = vb2_plane_vaddr(&buf->vb, 0);
+ unsigned ms;
+ char str[100];
+ int h, line = 1;
+ s32 gain;
+
+ if (!vbuf)
+ return;
+
+ for (h = 0; h < hmax; h++)
+ memcpy(vbuf + h * wmax * 2, dev->line + (dev->mv_count % wmax) * 2, wmax * 2);
+
+ /* Updates stream time */
+
+ dev->ms += jiffies_to_msecs(jiffies - dev->jiffies);
+ dev->jiffies = jiffies;
+ ms = dev->ms;
+ snprintf(str, sizeof(str), " %02d:%02d:%02d:%03d ",
+ (ms / (60 * 60 * 1000)) % 24,
+ (ms / (60 * 1000)) % 60,
+ (ms / 1000) % 60,
+ ms % 1000);
+ gen_text(dev, vbuf, line++ * 16, 16, str);
+ snprintf(str, sizeof(str), " %dx%d, input %d ",
+ dev->width, dev->height, dev->input);
+ gen_text(dev, vbuf, line++ * 16, 16, str);
+
+ gain = v4l2_ctrl_g_ctrl(dev->gain);
+ mutex_lock(&dev->ctrl_handler.lock);
+ snprintf(str, sizeof(str), " brightness %3d, contrast %3d, saturation %3d, hue %d ",
+ dev->brightness->cur.val,
+ dev->contrast->cur.val,
+ dev->saturation->cur.val,
+ dev->hue->cur.val);
+ gen_text(dev, vbuf, line++ * 16, 16, str);
+ snprintf(str, sizeof(str), " autogain %d, gain %3d, volume %3d ",
+ dev->autogain->cur.val, gain, dev->volume->cur.val);
+ gen_text(dev, vbuf, line++ * 16, 16, str);
+ snprintf(str, sizeof(str), " int32 %d, int64 %lld, bitmask %08x ",
+ dev->int32->cur.val,
+ dev->int64->cur.val64,
+ dev->bitmask->cur.val);
+ gen_text(dev, vbuf, line++ * 16, 16, str);
+ snprintf(str, sizeof(str), " boolean %d, menu %s, string \"%s\" ",
+ dev->boolean->cur.val,
+ dev->menu->qmenu[dev->menu->cur.val],
+ dev->string->cur.string);
+ mutex_unlock(&dev->ctrl_handler.lock);
+ gen_text(dev, vbuf, line++ * 16, 16, str);
+ if (dev->button_pressed) {
+ dev->button_pressed--;
+ snprintf(str, sizeof(str), " button pressed!");
+ gen_text(dev, vbuf, line++ * 16, 16, str);
+ }
+
+ dev->mv_count += 2;
+
+ buf->vb.v4l2_buf.field = dev->field;
+ dev->field_count++;
+ buf->vb.v4l2_buf.sequence = dev->field_count >> 1;
+ do_gettimeofday(&ts);
+ buf->vb.v4l2_buf.timestamp = ts;
+}
+
+static void vivi_thread_tick(struct vivi_dev *dev)
+{
+ struct vivi_dmaqueue *dma_q = &dev->vidq;
+ struct vivi_buffer *buf;
+ unsigned long flags = 0;
+
+ dprintk(dev, 1, "Thread tick\n");
+
+ spin_lock_irqsave(&dev->slock, flags);
+ if (list_empty(&dma_q->active)) {
+ dprintk(dev, 1, "No active queue to serve\n");
+ spin_unlock_irqrestore(&dev->slock, flags);
+ return;
+ }
+
+ buf = list_entry(dma_q->active.next, struct vivi_buffer, list);
+ list_del(&buf->list);
+ spin_unlock_irqrestore(&dev->slock, flags);
+
+ do_gettimeofday(&buf->vb.v4l2_buf.timestamp);
+
+ /* Fill buffer */
+ vivi_fillbuff(dev, buf);
+ dprintk(dev, 1, "filled buffer %p\n", buf);
+
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
+ dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index);
+}
+
+#define frames_to_ms(frames) \
+ ((frames * WAKE_NUMERATOR * 1000) / WAKE_DENOMINATOR)
+
+static void vivi_sleep(struct vivi_dev *dev)
+{
+ struct vivi_dmaqueue *dma_q = &dev->vidq;
+ int timeout;
+ DECLARE_WAITQUEUE(wait, current);
+
+ dprintk(dev, 1, "%s dma_q=0x%08lx\n", __func__,
+ (unsigned long)dma_q);
+
+ add_wait_queue(&dma_q->wq, &wait);
+ if (kthread_should_stop())
+ goto stop_task;
+
+ /* Calculate time to wake up */
+ timeout = msecs_to_jiffies(frames_to_ms(1));
+
+ vivi_thread_tick(dev);
+
+ schedule_timeout_interruptible(timeout);
+
+stop_task:
+ remove_wait_queue(&dma_q->wq, &wait);
+ try_to_freeze();
+}
+
+static int vivi_thread(void *data)
+{
+ struct vivi_dev *dev = data;
+
+ dprintk(dev, 1, "thread started\n");
+
+ set_freezable();
+
+ for (;;) {
+ vivi_sleep(dev);
+
+ if (kthread_should_stop())
+ break;
+ }
+ dprintk(dev, 1, "thread: exit\n");
+ return 0;
+}
+
+static int vivi_start_generating(struct vivi_dev *dev)
+{
+ struct vivi_dmaqueue *dma_q = &dev->vidq;
+
+ dprintk(dev, 1, "%s\n", __func__);
+
+ /* Resets frame counters */
+ dev->ms = 0;
+ dev->mv_count = 0;
+ dev->jiffies = jiffies;
+
+ dma_q->frame = 0;
+ dma_q->ini_jiffies = jiffies;
+ dma_q->kthread = kthread_run(vivi_thread, dev, dev->v4l2_dev.name);
+
+ if (IS_ERR(dma_q->kthread)) {
+ v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n");
+ return PTR_ERR(dma_q->kthread);
+ }
+ /* Wakes thread */
+ wake_up_interruptible(&dma_q->wq);
+
+ dprintk(dev, 1, "returning from %s\n", __func__);
+ return 0;
+}
+
+static void vivi_stop_generating(struct vivi_dev *dev)
+{
+ struct vivi_dmaqueue *dma_q = &dev->vidq;
+
+ dprintk(dev, 1, "%s\n", __func__);
+
+ /* shutdown control thread */
+ if (dma_q->kthread) {
+ kthread_stop(dma_q->kthread);
+ dma_q->kthread = NULL;
+ }
+
+ /*
+ * Typical driver might need to wait here until dma engine stops.
+ * In this case we can abort imiedetly, so it's just a noop.
+ */
+
+ /* Release all active buffers */
+ while (!list_empty(&dma_q->active)) {
+ struct vivi_buffer *buf;
+ buf = list_entry(dma_q->active.next, struct vivi_buffer, list);
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index);
+ }
+}
+/* ------------------------------------------------------------------
+ Videobuf operations
+ ------------------------------------------------------------------*/
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct vivi_dev *dev = vb2_get_drv_priv(vq);
+ unsigned long size;
+
+ size = dev->width * dev->height * 2;
+
+ if (0 == *nbuffers)
+ *nbuffers = 32;
+
+ while (size * *nbuffers > vid_limit * 1024 * 1024)
+ (*nbuffers)--;
+
+ *nplanes = 1;
+
+ sizes[0] = size;
+
+ /*
+ * videobuf2-vmalloc allocator is context-less so no need to set
+ * alloc_ctxs array.
+ */
+
+ dprintk(dev, 1, "%s, count=%d, size=%ld\n", __func__,
+ *nbuffers, size);
+
+ return 0;
+}
+
+static int buffer_init(struct vb2_buffer *vb)
+{
+ struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+
+ BUG_ON(NULL == dev->fmt);
+
+ /*
+ * This callback is called once per buffer, after its allocation.
+ *
+ * Vivi does not allow changing format during streaming, but it is
+ * possible to do so when streaming is paused (i.e. in streamoff state).
+ * Buffers however are not freed when going into streamoff and so
+ * buffer size verification has to be done in buffer_prepare, on each
+ * qbuf.
+ * It would be best to move verification code here to buf_init and
+ * s_fmt though.
+ */
+
+ return 0;
+}
+
+static int buffer_prepare(struct vb2_buffer *vb)
+{
+ struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb);
+ unsigned long size;
+
+ dprintk(dev, 1, "%s, field=%d\n", __func__, vb->v4l2_buf.field);
+
+ BUG_ON(NULL == dev->fmt);
+
+ /*
+ * Theses properties only change when queue is idle, see s_fmt.
+ * The below checks should not be performed here, on each
+ * buffer_prepare (i.e. on each qbuf). Most of the code in this function
+ * should thus be moved to buffer_init and s_fmt.
+ */
+ if (dev->width < 48 || dev->width > MAX_WIDTH ||
+ dev->height < 32 || dev->height > MAX_HEIGHT)
+ return -EINVAL;
+
+ size = dev->width * dev->height * 2;
+ if (vb2_plane_size(vb, 0) < size) {
+ dprintk(dev, 1, "%s data will not fit into plane (%lu < %lu)\n",
+ __func__, vb2_plane_size(vb, 0), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(&buf->vb, 0, size);
+
+ buf->fmt = dev->fmt;
+
+ precalculate_bars(dev);
+ precalculate_line(dev);
+
+ return 0;
+}
+
+static int buffer_finish(struct vb2_buffer *vb)
+{
+ struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+ dprintk(dev, 1, "%s\n", __func__);
+ return 0;
+}
+
+static void buffer_cleanup(struct vb2_buffer *vb)
+{
+ struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+ dprintk(dev, 1, "%s\n", __func__);
+
+}
+
+static void buffer_queue(struct vb2_buffer *vb)
+{
+ struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb);
+ struct vivi_dmaqueue *vidq = &dev->vidq;
+ unsigned long flags = 0;
+
+ dprintk(dev, 1, "%s\n", __func__);
+
+ spin_lock_irqsave(&dev->slock, flags);
+ list_add_tail(&buf->list, &vidq->active);
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+static int start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct vivi_dev *dev = vb2_get_drv_priv(vq);
+ dprintk(dev, 1, "%s\n", __func__);
+ return vivi_start_generating(dev);
+}
+
+/* abort streaming and wait for last buffer */
+static int stop_streaming(struct vb2_queue *vq)
+{
+ struct vivi_dev *dev = vb2_get_drv_priv(vq);
+ dprintk(dev, 1, "%s\n", __func__);
+ vivi_stop_generating(dev);
+ return 0;
+}
+
+static void vivi_lock(struct vb2_queue *vq)
+{
+ struct vivi_dev *dev = vb2_get_drv_priv(vq);
+ mutex_lock(&dev->mutex);
+}
+
+static void vivi_unlock(struct vb2_queue *vq)
+{
+ struct vivi_dev *dev = vb2_get_drv_priv(vq);
+ mutex_unlock(&dev->mutex);
+}
+
+
+static struct vb2_ops vivi_video_qops = {
+ .queue_setup = queue_setup,
+ .buf_init = buffer_init,
+ .buf_prepare = buffer_prepare,
+ .buf_finish = buffer_finish,
+ .buf_cleanup = buffer_cleanup,
+ .buf_queue = buffer_queue,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
+ .wait_prepare = vivi_unlock,
+ .wait_finish = vivi_lock,
+};
+
+/* ------------------------------------------------------------------
+ IOCTL vidioc handling
+ ------------------------------------------------------------------*/
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct vivi_dev *dev = video_drvdata(file);
+
+ strcpy(cap->driver, "vivi");
+ strcpy(cap->card, "vivi");
+ strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info));
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
+ V4L2_CAP_READWRITE;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct vivi_fmt *fmt;
+
+ if (f->index >= ARRAY_SIZE(formats))
+ return -EINVAL;
+
+ fmt = &formats[f->index];
+
+ strlcpy(f->description, fmt->name, sizeof(f->description));
+ f->pixelformat = fmt->fourcc;
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct vivi_dev *dev = video_drvdata(file);
+
+ f->fmt.pix.width = dev->width;
+ f->fmt.pix.height = dev->height;
+ f->fmt.pix.field = dev->field;
+ f->fmt.pix.pixelformat = dev->fmt->fourcc;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * dev->fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+ if (dev->fmt->fourcc == V4L2_PIX_FMT_YUYV ||
+ dev->fmt->fourcc == V4L2_PIX_FMT_UYVY)
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ else
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct vivi_dev *dev = video_drvdata(file);
+ struct vivi_fmt *fmt;
+ enum v4l2_field field;
+
+ fmt = get_format(f);
+ if (!fmt) {
+ dprintk(dev, 1, "Fourcc format (0x%08x) invalid.\n",
+ f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ field = f->fmt.pix.field;
+
+ if (field == V4L2_FIELD_ANY) {
+ field = V4L2_FIELD_INTERLACED;
+ } else if (V4L2_FIELD_INTERLACED != field) {
+ dprintk(dev, 1, "Field type invalid.\n");
+ return -EINVAL;
+ }
+
+ f->fmt.pix.field = field;
+ v4l_bound_align_image(&f->fmt.pix.width, 48, MAX_WIDTH, 2,
+ &f->fmt.pix.height, 32, MAX_HEIGHT, 0, 0);
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+ if (fmt->fourcc == V4L2_PIX_FMT_YUYV ||
+ fmt->fourcc == V4L2_PIX_FMT_UYVY)
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ else
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct vivi_dev *dev = video_drvdata(file);
+ struct vb2_queue *q = &dev->vb_vidq;
+
+ int ret = vidioc_try_fmt_vid_cap(file, priv, f);
+ if (ret < 0)
+ return ret;
+
+ if (vb2_is_streaming(q)) {
+ dprintk(dev, 1, "%s device busy\n", __func__);
+ return -EBUSY;
+ }
+
+ dev->fmt = get_format(f);
+ dev->width = f->fmt.pix.width;
+ dev->height = f->fmt.pix.height;
+ dev->field = f->fmt.pix.field;
+
+ return 0;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct vivi_dev *dev = video_drvdata(file);
+ return vb2_reqbufs(&dev->vb_vidq, p);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct vivi_dev *dev = video_drvdata(file);
+ return vb2_querybuf(&dev->vb_vidq, p);
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct vivi_dev *dev = video_drvdata(file);
+ return vb2_qbuf(&dev->vb_vidq, p);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct vivi_dev *dev = video_drvdata(file);
+ return vb2_dqbuf(&dev->vb_vidq, p, file->f_flags & O_NONBLOCK);
+}
+
+static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct vivi_dev *dev = video_drvdata(file);
+ return vb2_streamon(&dev->vb_vidq, i);
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct vivi_dev *dev = video_drvdata(file);
+ return vb2_streamoff(&dev->vb_vidq, i);
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i)
+{
+ return 0;
+}
+
+/* only one input in this sample driver */
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *inp)
+{
+ if (inp->index >= NUM_INPUTS)
+ return -EINVAL;
+
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ inp->std = V4L2_STD_525_60;
+ sprintf(inp->name, "Camera %u", inp->index);
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct vivi_dev *dev = video_drvdata(file);
+
+ *i = dev->input;
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct vivi_dev *dev = video_drvdata(file);
+
+ if (i >= NUM_INPUTS)
+ return -EINVAL;
+
+ if (i == dev->input)
+ return 0;
+
+ dev->input = i;
+ precalculate_bars(dev);
+ precalculate_line(dev);
+ return 0;
+}
+
+/* --- controls ---------------------------------------------- */
+
+static int vivi_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct vivi_dev *dev = container_of(ctrl->handler, struct vivi_dev, ctrl_handler);
+
+ if (ctrl == dev->autogain)
+ dev->gain->val = jiffies & 0xff;
+ return 0;
+}
+
+static int vivi_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct vivi_dev *dev = container_of(ctrl->handler, struct vivi_dev, ctrl_handler);
+
+ if (ctrl == dev->button)
+ dev->button_pressed = 30;
+ return 0;
+}
+
+/* ------------------------------------------------------------------
+ File operations for the device
+ ------------------------------------------------------------------*/
+
+static ssize_t
+vivi_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+{
+ struct vivi_dev *dev = video_drvdata(file);
+
+ dprintk(dev, 1, "read called\n");
+ return vb2_read(&dev->vb_vidq, data, count, ppos,
+ file->f_flags & O_NONBLOCK);
+}
+
+static unsigned int
+vivi_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct vivi_dev *dev = video_drvdata(file);
+ struct v4l2_fh *fh = file->private_data;
+ struct vb2_queue *q = &dev->vb_vidq;
+ unsigned int res;
+
+ dprintk(dev, 1, "%s\n", __func__);
+ res = vb2_poll(q, file, wait);
+ if (v4l2_event_pending(fh))
+ res |= POLLPRI;
+ else
+ poll_wait(file, &fh->wait, wait);
+ return res;
+}
+
+static int vivi_close(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct vivi_dev *dev = video_drvdata(file);
+
+ dprintk(dev, 1, "close called (dev=%s), file %p\n",
+ video_device_node_name(vdev), file);
+
+ if (v4l2_fh_is_singular_file(file))
+ vb2_queue_release(&dev->vb_vidq);
+ return v4l2_fh_release(file);
+}
+
+static int vivi_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct vivi_dev *dev = video_drvdata(file);
+ int ret;
+
+ dprintk(dev, 1, "mmap called, vma=0x%08lx\n", (unsigned long)vma);
+
+ ret = vb2_mmap(&dev->vb_vidq, vma);
+ dprintk(dev, 1, "vma start=0x%08lx, size=%ld, ret=%d\n",
+ (unsigned long)vma->vm_start,
+ (unsigned long)vma->vm_end - (unsigned long)vma->vm_start,
+ ret);
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops vivi_ctrl_ops = {
+ .g_volatile_ctrl = vivi_g_volatile_ctrl,
+ .s_ctrl = vivi_s_ctrl,
+};
+
+#define VIVI_CID_CUSTOM_BASE (V4L2_CID_USER_BASE | 0xf000)
+
+static const struct v4l2_ctrl_config vivi_ctrl_button = {
+ .ops = &vivi_ctrl_ops,
+ .id = VIVI_CID_CUSTOM_BASE + 0,
+ .name = "Button",
+ .type = V4L2_CTRL_TYPE_BUTTON,
+};
+
+static const struct v4l2_ctrl_config vivi_ctrl_boolean = {
+ .ops = &vivi_ctrl_ops,
+ .id = VIVI_CID_CUSTOM_BASE + 1,
+ .name = "Boolean",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .min = 0,
+ .max = 1,
+ .step = 1,
+ .def = 1,
+};
+
+static const struct v4l2_ctrl_config vivi_ctrl_int32 = {
+ .ops = &vivi_ctrl_ops,
+ .id = VIVI_CID_CUSTOM_BASE + 2,
+ .name = "Integer 32 Bits",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0x80000000,
+ .max = 0x7fffffff,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config vivi_ctrl_int64 = {
+ .ops = &vivi_ctrl_ops,
+ .id = VIVI_CID_CUSTOM_BASE + 3,
+ .name = "Integer 64 Bits",
+ .type = V4L2_CTRL_TYPE_INTEGER64,
+};
+
+static const char * const vivi_ctrl_menu_strings[] = {
+ "Menu Item 0 (Skipped)",
+ "Menu Item 1",
+ "Menu Item 2 (Skipped)",
+ "Menu Item 3",
+ "Menu Item 4",
+ "Menu Item 5 (Skipped)",
+ NULL,
+};
+
+static const struct v4l2_ctrl_config vivi_ctrl_menu = {
+ .ops = &vivi_ctrl_ops,
+ .id = VIVI_CID_CUSTOM_BASE + 4,
+ .name = "Menu",
+ .type = V4L2_CTRL_TYPE_MENU,
+ .min = 1,
+ .max = 4,
+ .def = 3,
+ .menu_skip_mask = 0x04,
+ .qmenu = vivi_ctrl_menu_strings,
+};
+
+static const struct v4l2_ctrl_config vivi_ctrl_string = {
+ .ops = &vivi_ctrl_ops,
+ .id = VIVI_CID_CUSTOM_BASE + 5,
+ .name = "String",
+ .type = V4L2_CTRL_TYPE_STRING,
+ .min = 2,
+ .max = 4,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config vivi_ctrl_bitmask = {
+ .ops = &vivi_ctrl_ops,
+ .id = VIVI_CID_CUSTOM_BASE + 6,
+ .name = "Bitmask",
+ .type = V4L2_CTRL_TYPE_BITMASK,
+ .def = 0x80002000,
+ .min = 0,
+ .max = 0x80402010,
+ .step = 0,
+};
+
+static const struct v4l2_file_operations vivi_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vivi_close,
+ .read = vivi_read,
+ .poll = vivi_poll,
+ .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
+ .mmap = vivi_mmap,
+};
+
+static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static struct video_device vivi_template = {
+ .name = "vivi",
+ .fops = &vivi_fops,
+ .ioctl_ops = &vivi_ioctl_ops,
+ .release = video_device_release,
+
+ .tvnorms = V4L2_STD_525_60,
+ .current_norm = V4L2_STD_NTSC_M,
+};
+
+/* -----------------------------------------------------------------
+ Initialization and module stuff
+ ------------------------------------------------------------------*/
+
+static int vivi_release(void)
+{
+ struct vivi_dev *dev;
+ struct list_head *list;
+
+ while (!list_empty(&vivi_devlist)) {
+ list = vivi_devlist.next;
+ list_del(list);
+ dev = list_entry(list, struct vivi_dev, vivi_devlist);
+
+ v4l2_info(&dev->v4l2_dev, "unregistering %s\n",
+ video_device_node_name(dev->vfd));
+ video_unregister_device(dev->vfd);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ v4l2_ctrl_handler_free(&dev->ctrl_handler);
+ kfree(dev);
+ }
+
+ return 0;
+}
+
+static int __init vivi_create_instance(int inst)
+{
+ struct vivi_dev *dev;
+ struct video_device *vfd;
+ struct v4l2_ctrl_handler *hdl;
+ struct vb2_queue *q;
+ int ret;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
+ "%s-%03d", VIVI_MODULE_NAME, inst);
+ ret = v4l2_device_register(NULL, &dev->v4l2_dev);
+ if (ret)
+ goto free_dev;
+
+ dev->fmt = &formats[0];
+ dev->width = 640;
+ dev->height = 480;
+ hdl = &dev->ctrl_handler;
+ v4l2_ctrl_handler_init(hdl, 11);
+ dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
+ V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200);
+ dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
+ dev->contrast = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 16);
+ dev->saturation = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 255, 1, 127);
+ dev->hue = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
+ V4L2_CID_HUE, -128, 127, 1, 0);
+ dev->autogain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ dev->gain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
+ V4L2_CID_GAIN, 0, 255, 1, 100);
+ dev->button = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_button, NULL);
+ dev->int32 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int32, NULL);
+ dev->int64 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int64, NULL);
+ dev->boolean = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_boolean, NULL);
+ dev->menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_menu, NULL);
+ dev->string = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_string, NULL);
+ dev->bitmask = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_bitmask, NULL);
+ if (hdl->error) {
+ ret = hdl->error;
+ goto unreg_dev;
+ }
+ v4l2_ctrl_auto_cluster(2, &dev->autogain, 0, true);
+ dev->v4l2_dev.ctrl_handler = hdl;
+
+ /* initialize locks */
+ spin_lock_init(&dev->slock);
+
+ /* initialize queue */
+ q = &dev->vb_vidq;
+ memset(q, 0, sizeof(dev->vb_vidq));
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
+ q->drv_priv = dev;
+ q->buf_struct_size = sizeof(struct vivi_buffer);
+ q->ops = &vivi_video_qops;
+ q->mem_ops = &vb2_vmalloc_memops;
+
+ vb2_queue_init(q);
+
+ mutex_init(&dev->mutex);
+
+ /* init video dma queues */
+ INIT_LIST_HEAD(&dev->vidq.active);
+ init_waitqueue_head(&dev->vidq.wq);
+
+ ret = -ENOMEM;
+ vfd = video_device_alloc();
+ if (!vfd)
+ goto unreg_dev;
+
+ *vfd = vivi_template;
+ vfd->debug = debug;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+ set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
+
+ /*
+ * Provide a mutex to v4l2 core. It will be used to protect
+ * all fops and v4l2 ioctls.
+ */
+ vfd->lock = &dev->mutex;
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
+ if (ret < 0)
+ goto rel_vdev;
+
+ video_set_drvdata(vfd, dev);
+
+ /* Now that everything is fine, let's add it to device list */
+ list_add_tail(&dev->vivi_devlist, &vivi_devlist);
+
+ if (video_nr != -1)
+ video_nr++;
+
+ dev->vfd = vfd;
+ v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
+ video_device_node_name(vfd));
+ return 0;
+
+rel_vdev:
+ video_device_release(vfd);
+unreg_dev:
+ v4l2_ctrl_handler_free(hdl);
+ v4l2_device_unregister(&dev->v4l2_dev);
+free_dev:
+ kfree(dev);
+ return ret;
+}
+
+/* This routine allocates from 1 to n_devs virtual drivers.
+
+ The real maximum number of virtual drivers will depend on how many drivers
+ will succeed. This is limited to the maximum number of devices that
+ videodev supports, which is equal to VIDEO_NUM_DEVICES.
+ */
+static int __init vivi_init(void)
+{
+ const struct font_desc *font = find_font("VGA8x16");
+ int ret = 0, i;
+
+ if (font == NULL) {
+ printk(KERN_ERR "vivi: could not find font\n");
+ return -ENODEV;
+ }
+ font8x16 = font->data;
+
+ if (n_devs <= 0)
+ n_devs = 1;
+
+ for (i = 0; i < n_devs; i++) {
+ ret = vivi_create_instance(i);
+ if (ret) {
+ /* If some instantiations succeeded, keep driver */
+ if (i)
+ ret = 0;
+ break;
+ }
+ }
+
+ if (ret < 0) {
+ printk(KERN_ERR "vivi: error %d while loading driver\n", ret);
+ return ret;
+ }
+
+ printk(KERN_INFO "Video Technology Magazine Virtual Video "
+ "Capture Board ver %s successfully loaded.\n",
+ VIVI_VERSION);
+
+ /* n_devs will reflect the actual number of allocated devices */
+ n_devs = i;
+
+ return ret;
+}
+
+static void __exit vivi_exit(void)
+{
+ vivi_release();
+}
+
+module_init(vivi_init);
+module_exit(vivi_exit);
diff --git a/drivers/media/video/vp27smpx.c b/drivers/media/video/vp27smpx.c
new file mode 100644
index 00000000..7cfbc9d9
--- /dev/null
+++ b/drivers/media/video/vp27smpx.c
@@ -0,0 +1,211 @@
+/*
+ * vp27smpx - driver version 0.0.1
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * Based on a tvaudio patch from Takahiro Adachi <tadachi@tadachi-net.com>
+ * and Kazuhiko Kawakami <kazz-0@mail.goo.ne.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/ioctl.h>
+#include <asm/uaccess.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+
+MODULE_DESCRIPTION("vp27smpx driver");
+MODULE_AUTHOR("Hans Verkuil");
+MODULE_LICENSE("GPL");
+
+
+/* ----------------------------------------------------------------------- */
+
+struct vp27smpx_state {
+ struct v4l2_subdev sd;
+ int radio;
+ u32 audmode;
+};
+
+static inline struct vp27smpx_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct vp27smpx_state, sd);
+}
+
+static void vp27smpx_set_audmode(struct v4l2_subdev *sd, u32 audmode)
+{
+ struct vp27smpx_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 data[3] = { 0x00, 0x00, 0x04 };
+
+ switch (audmode) {
+ case V4L2_TUNER_MODE_MONO:
+ case V4L2_TUNER_MODE_LANG1:
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ data[1] = 0x01;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ data[1] = 0x02;
+ break;
+ }
+
+ if (i2c_master_send(client, data, sizeof(data)) != sizeof(data))
+ v4l2_err(sd, "I/O error setting audmode\n");
+ else
+ state->audmode = audmode;
+}
+
+static int vp27smpx_s_radio(struct v4l2_subdev *sd)
+{
+ struct vp27smpx_state *state = to_state(sd);
+
+ state->radio = 1;
+ return 0;
+}
+
+static int vp27smpx_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
+{
+ struct vp27smpx_state *state = to_state(sd);
+
+ state->radio = 0;
+ return 0;
+}
+
+static int vp27smpx_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct vp27smpx_state *state = to_state(sd);
+
+ if (!state->radio)
+ vp27smpx_set_audmode(sd, vt->audmode);
+ return 0;
+}
+
+static int vp27smpx_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct vp27smpx_state *state = to_state(sd);
+
+ if (state->radio)
+ return 0;
+ vt->audmode = state->audmode;
+ vt->capability = V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
+ vt->rxsubchans = V4L2_TUNER_SUB_MONO;
+ return 0;
+}
+
+static int vp27smpx_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_VP27SMPX, 0);
+}
+
+static int vp27smpx_log_status(struct v4l2_subdev *sd)
+{
+ struct vp27smpx_state *state = to_state(sd);
+
+ v4l2_info(sd, "Audio Mode: %u%s\n", state->audmode,
+ state->radio ? " (Radio)" : "");
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops vp27smpx_core_ops = {
+ .log_status = vp27smpx_log_status,
+ .g_chip_ident = vp27smpx_g_chip_ident,
+ .s_std = vp27smpx_s_std,
+};
+
+static const struct v4l2_subdev_tuner_ops vp27smpx_tuner_ops = {
+ .s_radio = vp27smpx_s_radio,
+ .s_tuner = vp27smpx_s_tuner,
+ .g_tuner = vp27smpx_g_tuner,
+};
+
+static const struct v4l2_subdev_ops vp27smpx_ops = {
+ .core = &vp27smpx_core_ops,
+ .tuner = &vp27smpx_tuner_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+/* i2c implementation */
+
+/*
+ * Generic i2c probe
+ * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
+ */
+
+static int vp27smpx_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct vp27smpx_state *state;
+ struct v4l2_subdev *sd;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ state = kzalloc(sizeof(struct vp27smpx_state), GFP_KERNEL);
+ if (state == NULL)
+ return -ENOMEM;
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &vp27smpx_ops);
+ state->audmode = V4L2_TUNER_MODE_STEREO;
+
+ /* initialize vp27smpx */
+ vp27smpx_set_audmode(sd, state->audmode);
+ return 0;
+}
+
+static int vp27smpx_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct i2c_device_id vp27smpx_id[] = {
+ { "vp27smpx", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, vp27smpx_id);
+
+static struct i2c_driver vp27smpx_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "vp27smpx",
+ },
+ .probe = vp27smpx_probe,
+ .remove = vp27smpx_remove,
+ .id_table = vp27smpx_id,
+};
+
+module_i2c_driver(vp27smpx_driver);
diff --git a/drivers/media/video/vpx3220.c b/drivers/media/video/vpx3220.c
new file mode 100644
index 00000000..2f67b4c5
--- /dev/null
+++ b/drivers/media/video/vpx3220.c
@@ -0,0 +1,591 @@
+/*
+ * vpx3220a, vpx3216b & vpx3214c video decoder driver version 0.0.1
+ *
+ * Copyright (C) 2001 Laurent Pinchart <lpinchart@freegates.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+
+MODULE_DESCRIPTION("vpx3220a/vpx3216b/vpx3214c video decoder driver");
+MODULE_AUTHOR("Laurent Pinchart");
+MODULE_LICENSE("GPL");
+
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+
+#define VPX_TIMEOUT_COUNT 10
+
+/* ----------------------------------------------------------------------- */
+
+struct vpx3220 {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+ unsigned char reg[255];
+
+ v4l2_std_id norm;
+ int ident;
+ int input;
+ int enable;
+};
+
+static inline struct vpx3220 *to_vpx3220(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct vpx3220, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct vpx3220, hdl)->sd;
+}
+
+static char *inputs[] = { "internal", "composite", "svideo" };
+
+/* ----------------------------------------------------------------------- */
+
+static inline int vpx3220_write(struct v4l2_subdev *sd, u8 reg, u8 value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct vpx3220 *decoder = i2c_get_clientdata(client);
+
+ decoder->reg[reg] = value;
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static inline int vpx3220_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int vpx3220_fp_status(struct v4l2_subdev *sd)
+{
+ unsigned char status;
+ unsigned int i;
+
+ for (i = 0; i < VPX_TIMEOUT_COUNT; i++) {
+ status = vpx3220_read(sd, 0x29);
+
+ if (!(status & 4))
+ return 0;
+
+ udelay(10);
+
+ if (need_resched())
+ cond_resched();
+ }
+
+ return -1;
+}
+
+static int vpx3220_fp_write(struct v4l2_subdev *sd, u8 fpaddr, u16 data)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ /* Write the 16-bit address to the FPWR register */
+ if (i2c_smbus_write_word_data(client, 0x27, swab16(fpaddr)) == -1) {
+ v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
+ return -1;
+ }
+
+ if (vpx3220_fp_status(sd) < 0)
+ return -1;
+
+ /* Write the 16-bit data to the FPDAT register */
+ if (i2c_smbus_write_word_data(client, 0x28, swab16(data)) == -1) {
+ v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static u16 vpx3220_fp_read(struct v4l2_subdev *sd, u16 fpaddr)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ s16 data;
+
+ /* Write the 16-bit address to the FPRD register */
+ if (i2c_smbus_write_word_data(client, 0x26, swab16(fpaddr)) == -1) {
+ v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
+ return -1;
+ }
+
+ if (vpx3220_fp_status(sd) < 0)
+ return -1;
+
+ /* Read the 16-bit data from the FPDAT register */
+ data = i2c_smbus_read_word_data(client, 0x28);
+ if (data == -1) {
+ v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
+ return -1;
+ }
+
+ return swab16(data);
+}
+
+static int vpx3220_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len)
+{
+ u8 reg;
+ int ret = -1;
+
+ while (len >= 2) {
+ reg = *data++;
+ ret = vpx3220_write(sd, reg, *data++);
+ if (ret < 0)
+ break;
+ len -= 2;
+ }
+
+ return ret;
+}
+
+static int vpx3220_write_fp_block(struct v4l2_subdev *sd,
+ const u16 *data, unsigned int len)
+{
+ u8 reg;
+ int ret = 0;
+
+ while (len > 1) {
+ reg = *data++;
+ ret |= vpx3220_fp_write(sd, reg, *data++);
+ len -= 2;
+ }
+
+ return ret;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static const unsigned short init_ntsc[] = {
+ 0x1c, 0x00, /* NTSC tint angle */
+ 0x88, 17, /* Window 1 vertical */
+ 0x89, 240, /* Vertical lines in */
+ 0x8a, 240, /* Vertical lines out */
+ 0x8b, 000, /* Horizontal begin */
+ 0x8c, 640, /* Horizontal length */
+ 0x8d, 640, /* Number of pixels */
+ 0x8f, 0xc00, /* Disable window 2 */
+ 0xf0, 0x73, /* 13.5 MHz transport, Forced
+ * mode, latch windows */
+ 0xf2, 0x13, /* NTSC M, composite input */
+ 0xe7, 0x1e1, /* Enable vertical standard
+ * locking @ 240 lines */
+};
+
+static const unsigned short init_pal[] = {
+ 0x88, 23, /* Window 1 vertical begin */
+ 0x89, 288, /* Vertical lines in (16 lines
+ * skipped by the VFE) */
+ 0x8a, 288, /* Vertical lines out (16 lines
+ * skipped by the VFE) */
+ 0x8b, 16, /* Horizontal begin */
+ 0x8c, 768, /* Horizontal length */
+ 0x8d, 784, /* Number of pixels
+ * Must be >= Horizontal begin + Horizontal length */
+ 0x8f, 0xc00, /* Disable window 2 */
+ 0xf0, 0x77, /* 13.5 MHz transport, Forced
+ * mode, latch windows */
+ 0xf2, 0x3d1, /* PAL B,G,H,I, composite input */
+ 0xe7, 0x241, /* PAL/SECAM set to 288 lines */
+};
+
+static const unsigned short init_secam[] = {
+ 0x88, 23, /* Window 1 vertical begin */
+ 0x89, 288, /* Vertical lines in (16 lines
+ * skipped by the VFE) */
+ 0x8a, 288, /* Vertical lines out (16 lines
+ * skipped by the VFE) */
+ 0x8b, 16, /* Horizontal begin */
+ 0x8c, 768, /* Horizontal length */
+ 0x8d, 784, /* Number of pixels
+ * Must be >= Horizontal begin + Horizontal length */
+ 0x8f, 0xc00, /* Disable window 2 */
+ 0xf0, 0x77, /* 13.5 MHz transport, Forced
+ * mode, latch windows */
+ 0xf2, 0x3d5, /* SECAM, composite input */
+ 0xe7, 0x241, /* PAL/SECAM set to 288 lines */
+};
+
+static const unsigned char init_common[] = {
+ 0xf2, 0x00, /* Disable all outputs */
+ 0x33, 0x0d, /* Luma : VIN2, Chroma : CIN
+ * (clamp off) */
+ 0xd8, 0xa8, /* HREF/VREF active high, VREF
+ * pulse = 2, Odd/Even flag */
+ 0x20, 0x03, /* IF compensation 0dB/oct */
+ 0xe0, 0xff, /* Open up all comparators */
+ 0xe1, 0x00,
+ 0xe2, 0x7f,
+ 0xe3, 0x80,
+ 0xe4, 0x7f,
+ 0xe5, 0x80,
+ 0xe6, 0x00, /* Brightness set to 0 */
+ 0xe7, 0xe0, /* Contrast to 1.0, noise shaping
+ * 10 to 8 2-bit error diffusion */
+ 0xe8, 0xf8, /* YUV422, CbCr binary offset,
+ * ... (p.32) */
+ 0xea, 0x18, /* LLC2 connected, output FIFO
+ * reset with VACTintern */
+ 0xf0, 0x8a, /* Half full level to 10, bus
+ * shuffler [7:0, 23:16, 15:8] */
+ 0xf1, 0x18, /* Single clock, sync mode, no
+ * FE delay, no HLEN counter */
+ 0xf8, 0x12, /* Port A, PIXCLK, HF# & FE#
+ * strength to 2 */
+ 0xf9, 0x24, /* Port B, HREF, VREF, PREF &
+ * ALPHA strength to 4 */
+};
+
+static const unsigned short init_fp[] = {
+ 0x59, 0,
+ 0xa0, 2070, /* ACC reference */
+ 0xa3, 0,
+ 0xa4, 0,
+ 0xa8, 30,
+ 0xb2, 768,
+ 0xbe, 27,
+ 0x58, 0,
+ 0x26, 0,
+ 0x4b, 0x298, /* PLL gain */
+};
+
+
+static int vpx3220_init(struct v4l2_subdev *sd, u32 val)
+{
+ struct vpx3220 *decoder = to_vpx3220(sd);
+
+ vpx3220_write_block(sd, init_common, sizeof(init_common));
+ vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1);
+ if (decoder->norm & V4L2_STD_NTSC)
+ vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1);
+ else if (decoder->norm & V4L2_STD_PAL)
+ vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
+ else if (decoder->norm & V4L2_STD_SECAM)
+ vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1);
+ else
+ vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
+ return 0;
+}
+
+static int vpx3220_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd)
+{
+ int res = V4L2_IN_ST_NO_SIGNAL, status;
+ v4l2_std_id std = 0;
+
+ status = vpx3220_fp_read(sd, 0x0f3);
+
+ v4l2_dbg(1, debug, sd, "status: 0x%04x\n", status);
+
+ if (status < 0)
+ return status;
+
+ if ((status & 0x20) == 0) {
+ res = 0;
+
+ switch (status & 0x18) {
+ case 0x00:
+ case 0x10:
+ case 0x14:
+ case 0x18:
+ std = V4L2_STD_PAL;
+ break;
+
+ case 0x08:
+ std = V4L2_STD_SECAM;
+ break;
+
+ case 0x04:
+ case 0x0c:
+ case 0x1c:
+ std = V4L2_STD_NTSC;
+ break;
+ }
+ }
+ if (pstd)
+ *pstd = std;
+ if (pstatus)
+ *pstatus = res;
+ return 0;
+}
+
+static int vpx3220_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ v4l2_dbg(1, debug, sd, "querystd\n");
+ return vpx3220_status(sd, NULL, std);
+}
+
+static int vpx3220_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+ v4l2_dbg(1, debug, sd, "g_input_status\n");
+ return vpx3220_status(sd, status, NULL);
+}
+
+static int vpx3220_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct vpx3220 *decoder = to_vpx3220(sd);
+ int temp_input;
+
+ /* Here we back up the input selection because it gets
+ overwritten when we fill the registers with the
+ chosen video norm */
+ temp_input = vpx3220_fp_read(sd, 0xf2);
+
+ v4l2_dbg(1, debug, sd, "s_std %llx\n", (unsigned long long)std);
+ if (std & V4L2_STD_NTSC) {
+ vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1);
+ v4l2_dbg(1, debug, sd, "norm switched to NTSC\n");
+ } else if (std & V4L2_STD_PAL) {
+ vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
+ v4l2_dbg(1, debug, sd, "norm switched to PAL\n");
+ } else if (std & V4L2_STD_SECAM) {
+ vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1);
+ v4l2_dbg(1, debug, sd, "norm switched to SECAM\n");
+ } else {
+ return -EINVAL;
+ }
+
+ decoder->norm = std;
+
+ /* And here we set the backed up video input again */
+ vpx3220_fp_write(sd, 0xf2, temp_input | 0x0010);
+ udelay(10);
+ return 0;
+}
+
+static int vpx3220_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ int data;
+
+ /* RJ: input = 0: ST8 (PCTV) input
+ input = 1: COMPOSITE input
+ input = 2: SVHS input */
+
+ const int input_vals[3][2] = {
+ {0x0c, 0},
+ {0x0d, 0},
+ {0x0e, 1}
+ };
+
+ if (input > 2)
+ return -EINVAL;
+
+ v4l2_dbg(1, debug, sd, "input switched to %s\n", inputs[input]);
+
+ vpx3220_write(sd, 0x33, input_vals[input][0]);
+
+ data = vpx3220_fp_read(sd, 0xf2) & ~(0x0020);
+ if (data < 0)
+ return data;
+ /* 0x0010 is required to latch the setting */
+ vpx3220_fp_write(sd, 0xf2,
+ data | (input_vals[input][1] << 5) | 0x0010);
+
+ udelay(10);
+ return 0;
+}
+
+static int vpx3220_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ v4l2_dbg(1, debug, sd, "s_stream %s\n", enable ? "on" : "off");
+
+ vpx3220_write(sd, 0xf2, (enable ? 0x1b : 0x00));
+ return 0;
+}
+
+static int vpx3220_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ vpx3220_write(sd, 0xe6, ctrl->val);
+ return 0;
+ case V4L2_CID_CONTRAST:
+ /* Bit 7 and 8 is for noise shaping */
+ vpx3220_write(sd, 0xe7, ctrl->val + 192);
+ return 0;
+ case V4L2_CID_SATURATION:
+ vpx3220_fp_write(sd, 0xa0, ctrl->val);
+ return 0;
+ case V4L2_CID_HUE:
+ vpx3220_fp_write(sd, 0x1c, ctrl->val);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int vpx3220_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct vpx3220 *decoder = to_vpx3220(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, decoder->ident, 0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_ctrl_ops vpx3220_ctrl_ops = {
+ .s_ctrl = vpx3220_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops vpx3220_core_ops = {
+ .g_chip_ident = vpx3220_g_chip_ident,
+ .init = vpx3220_init,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+ .s_std = vpx3220_s_std,
+};
+
+static const struct v4l2_subdev_video_ops vpx3220_video_ops = {
+ .s_routing = vpx3220_s_routing,
+ .s_stream = vpx3220_s_stream,
+ .querystd = vpx3220_querystd,
+ .g_input_status = vpx3220_g_input_status,
+};
+
+static const struct v4l2_subdev_ops vpx3220_ops = {
+ .core = &vpx3220_core_ops,
+ .video = &vpx3220_video_ops,
+};
+
+/* -----------------------------------------------------------------------
+ * Client management code
+ */
+
+static int vpx3220_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct vpx3220 *decoder;
+ struct v4l2_subdev *sd;
+ const char *name = NULL;
+ u8 ver;
+ u16 pn;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
+ return -ENODEV;
+
+ decoder = kzalloc(sizeof(struct vpx3220), GFP_KERNEL);
+ if (decoder == NULL)
+ return -ENOMEM;
+ sd = &decoder->sd;
+ v4l2_i2c_subdev_init(sd, client, &vpx3220_ops);
+ decoder->norm = V4L2_STD_PAL;
+ decoder->input = 0;
+ decoder->enable = 1;
+ v4l2_ctrl_handler_init(&decoder->hdl, 4);
+ v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
+ v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 63, 1, 32);
+ v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 4095, 1, 2048);
+ v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops,
+ V4L2_CID_HUE, -512, 511, 1, 0);
+ sd->ctrl_handler = &decoder->hdl;
+ if (decoder->hdl.error) {
+ int err = decoder->hdl.error;
+
+ v4l2_ctrl_handler_free(&decoder->hdl);
+ kfree(decoder);
+ return err;
+ }
+ v4l2_ctrl_handler_setup(&decoder->hdl);
+
+ ver = i2c_smbus_read_byte_data(client, 0x00);
+ pn = (i2c_smbus_read_byte_data(client, 0x02) << 8) +
+ i2c_smbus_read_byte_data(client, 0x01);
+ decoder->ident = V4L2_IDENT_VPX3220A;
+ if (ver == 0xec) {
+ switch (pn) {
+ case 0x4680:
+ name = "vpx3220a";
+ break;
+ case 0x4260:
+ name = "vpx3216b";
+ decoder->ident = V4L2_IDENT_VPX3216B;
+ break;
+ case 0x4280:
+ name = "vpx3214c";
+ decoder->ident = V4L2_IDENT_VPX3214C;
+ break;
+ }
+ }
+ if (name)
+ v4l2_info(sd, "%s found @ 0x%x (%s)\n", name,
+ client->addr << 1, client->adapter->name);
+ else
+ v4l2_info(sd, "chip (%02x:%04x) found @ 0x%x (%s)\n",
+ ver, pn, client->addr << 1, client->adapter->name);
+
+ vpx3220_write_block(sd, init_common, sizeof(init_common));
+ vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1);
+ /* Default to PAL */
+ vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
+ return 0;
+}
+
+static int vpx3220_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct vpx3220 *decoder = to_vpx3220(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&decoder->hdl);
+ kfree(decoder);
+ return 0;
+}
+
+static const struct i2c_device_id vpx3220_id[] = {
+ { "vpx3220a", 0 },
+ { "vpx3216b", 0 },
+ { "vpx3214c", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, vpx3220_id);
+
+static struct i2c_driver vpx3220_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "vpx3220",
+ },
+ .probe = vpx3220_probe,
+ .remove = vpx3220_remove,
+ .id_table = vpx3220_id,
+};
+
+module_i2c_driver(vpx3220_driver);
diff --git a/drivers/media/video/vs6624.c b/drivers/media/video/vs6624.c
new file mode 100644
index 00000000..42ae9dc9
--- /dev/null
+++ b/drivers/media/video/vs6624.c
@@ -0,0 +1,928 @@
+/*
+ * vs6624.c ST VS6624 CMOS image sensor driver
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mediabus.h>
+
+#include "vs6624_regs.h"
+
+#define VGA_WIDTH 640
+#define VGA_HEIGHT 480
+#define QVGA_WIDTH 320
+#define QVGA_HEIGHT 240
+#define QQVGA_WIDTH 160
+#define QQVGA_HEIGHT 120
+#define CIF_WIDTH 352
+#define CIF_HEIGHT 288
+#define QCIF_WIDTH 176
+#define QCIF_HEIGHT 144
+#define QQCIF_WIDTH 88
+#define QQCIF_HEIGHT 72
+
+#define MAX_FRAME_RATE 30
+
+struct vs6624 {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+ struct v4l2_fract frame_rate;
+ struct v4l2_mbus_framefmt fmt;
+ unsigned ce_pin;
+};
+
+static const struct vs6624_format {
+ enum v4l2_mbus_pixelcode mbus_code;
+ enum v4l2_colorspace colorspace;
+} vs6624_formats[] = {
+ {
+ .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+ {
+ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+ {
+ .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ },
+};
+
+static struct v4l2_mbus_framefmt vs6624_default_fmt = {
+ .width = VGA_WIDTH,
+ .height = VGA_HEIGHT,
+ .code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+};
+
+static const u16 vs6624_p1[] = {
+ 0x8104, 0x03,
+ 0x8105, 0x01,
+ 0xc900, 0x03,
+ 0xc904, 0x47,
+ 0xc905, 0x10,
+ 0xc906, 0x80,
+ 0xc907, 0x3a,
+ 0x903a, 0x02,
+ 0x903b, 0x47,
+ 0x903c, 0x15,
+ 0xc908, 0x31,
+ 0xc909, 0xdc,
+ 0xc90a, 0x80,
+ 0xc90b, 0x44,
+ 0x9044, 0x02,
+ 0x9045, 0x31,
+ 0x9046, 0xe2,
+ 0xc90c, 0x07,
+ 0xc90d, 0xe0,
+ 0xc90e, 0x80,
+ 0xc90f, 0x47,
+ 0x9047, 0x90,
+ 0x9048, 0x83,
+ 0x9049, 0x81,
+ 0x904a, 0xe0,
+ 0x904b, 0x60,
+ 0x904c, 0x08,
+ 0x904d, 0x90,
+ 0x904e, 0xc0,
+ 0x904f, 0x43,
+ 0x9050, 0x74,
+ 0x9051, 0x01,
+ 0x9052, 0xf0,
+ 0x9053, 0x80,
+ 0x9054, 0x05,
+ 0x9055, 0xE4,
+ 0x9056, 0x90,
+ 0x9057, 0xc0,
+ 0x9058, 0x43,
+ 0x9059, 0xf0,
+ 0x905a, 0x02,
+ 0x905b, 0x07,
+ 0x905c, 0xec,
+ 0xc910, 0x5d,
+ 0xc911, 0xca,
+ 0xc912, 0x80,
+ 0xc913, 0x5d,
+ 0x905d, 0xa3,
+ 0x905e, 0x04,
+ 0x905f, 0xf0,
+ 0x9060, 0xa3,
+ 0x9061, 0x04,
+ 0x9062, 0xf0,
+ 0x9063, 0x22,
+ 0xc914, 0x72,
+ 0xc915, 0x92,
+ 0xc916, 0x80,
+ 0xc917, 0x64,
+ 0x9064, 0x74,
+ 0x9065, 0x01,
+ 0x9066, 0x02,
+ 0x9067, 0x72,
+ 0x9068, 0x95,
+ 0xc918, 0x47,
+ 0xc919, 0xf2,
+ 0xc91a, 0x81,
+ 0xc91b, 0x69,
+ 0x9169, 0x74,
+ 0x916a, 0x02,
+ 0x916b, 0xf0,
+ 0x916c, 0xec,
+ 0x916d, 0xb4,
+ 0x916e, 0x10,
+ 0x916f, 0x0a,
+ 0x9170, 0x90,
+ 0x9171, 0x80,
+ 0x9172, 0x16,
+ 0x9173, 0xe0,
+ 0x9174, 0x70,
+ 0x9175, 0x04,
+ 0x9176, 0x90,
+ 0x9177, 0xd3,
+ 0x9178, 0xc4,
+ 0x9179, 0xf0,
+ 0x917a, 0x22,
+ 0xc91c, 0x0a,
+ 0xc91d, 0xbe,
+ 0xc91e, 0x80,
+ 0xc91f, 0x73,
+ 0x9073, 0xfc,
+ 0x9074, 0xa3,
+ 0x9075, 0xe0,
+ 0x9076, 0xf5,
+ 0x9077, 0x82,
+ 0x9078, 0x8c,
+ 0x9079, 0x83,
+ 0x907a, 0xa3,
+ 0x907b, 0xa3,
+ 0x907c, 0xe0,
+ 0x907d, 0xfc,
+ 0x907e, 0xa3,
+ 0x907f, 0xe0,
+ 0x9080, 0xc3,
+ 0x9081, 0x9f,
+ 0x9082, 0xff,
+ 0x9083, 0xec,
+ 0x9084, 0x9e,
+ 0x9085, 0xfe,
+ 0x9086, 0x02,
+ 0x9087, 0x0a,
+ 0x9088, 0xea,
+ 0xc920, 0x47,
+ 0xc921, 0x38,
+ 0xc922, 0x80,
+ 0xc923, 0x89,
+ 0x9089, 0xec,
+ 0x908a, 0xd3,
+ 0x908b, 0x94,
+ 0x908c, 0x20,
+ 0x908d, 0x40,
+ 0x908e, 0x01,
+ 0x908f, 0x1c,
+ 0x9090, 0x90,
+ 0x9091, 0xd3,
+ 0x9092, 0xd4,
+ 0x9093, 0xec,
+ 0x9094, 0xf0,
+ 0x9095, 0x02,
+ 0x9096, 0x47,
+ 0x9097, 0x3d,
+ 0xc924, 0x45,
+ 0xc925, 0xca,
+ 0xc926, 0x80,
+ 0xc927, 0x98,
+ 0x9098, 0x12,
+ 0x9099, 0x77,
+ 0x909a, 0xd6,
+ 0x909b, 0x02,
+ 0x909c, 0x45,
+ 0x909d, 0xcd,
+ 0xc928, 0x20,
+ 0xc929, 0xd5,
+ 0xc92a, 0x80,
+ 0xc92b, 0x9e,
+ 0x909e, 0x90,
+ 0x909f, 0x82,
+ 0x90a0, 0x18,
+ 0x90a1, 0xe0,
+ 0x90a2, 0xb4,
+ 0x90a3, 0x03,
+ 0x90a4, 0x0e,
+ 0x90a5, 0x90,
+ 0x90a6, 0x83,
+ 0x90a7, 0xbf,
+ 0x90a8, 0xe0,
+ 0x90a9, 0x60,
+ 0x90aa, 0x08,
+ 0x90ab, 0x90,
+ 0x90ac, 0x81,
+ 0x90ad, 0xfc,
+ 0x90ae, 0xe0,
+ 0x90af, 0xff,
+ 0x90b0, 0xc3,
+ 0x90b1, 0x13,
+ 0x90b2, 0xf0,
+ 0x90b3, 0x90,
+ 0x90b4, 0x81,
+ 0x90b5, 0xfc,
+ 0x90b6, 0xe0,
+ 0x90b7, 0xff,
+ 0x90b8, 0x02,
+ 0x90b9, 0x20,
+ 0x90ba, 0xda,
+ 0xc92c, 0x70,
+ 0xc92d, 0xbc,
+ 0xc92e, 0x80,
+ 0xc92f, 0xbb,
+ 0x90bb, 0x90,
+ 0x90bc, 0x82,
+ 0x90bd, 0x18,
+ 0x90be, 0xe0,
+ 0x90bf, 0xb4,
+ 0x90c0, 0x03,
+ 0x90c1, 0x06,
+ 0x90c2, 0x90,
+ 0x90c3, 0xc1,
+ 0x90c4, 0x06,
+ 0x90c5, 0x74,
+ 0x90c6, 0x05,
+ 0x90c7, 0xf0,
+ 0x90c8, 0x90,
+ 0x90c9, 0xd3,
+ 0x90ca, 0xa0,
+ 0x90cb, 0x02,
+ 0x90cc, 0x70,
+ 0x90cd, 0xbf,
+ 0xc930, 0x72,
+ 0xc931, 0x21,
+ 0xc932, 0x81,
+ 0xc933, 0x3b,
+ 0x913b, 0x7d,
+ 0x913c, 0x02,
+ 0x913d, 0x7f,
+ 0x913e, 0x7b,
+ 0x913f, 0x02,
+ 0x9140, 0x72,
+ 0x9141, 0x25,
+ 0xc934, 0x28,
+ 0xc935, 0xae,
+ 0xc936, 0x80,
+ 0xc937, 0xd2,
+ 0x90d2, 0xf0,
+ 0x90d3, 0x90,
+ 0x90d4, 0xd2,
+ 0x90d5, 0x0a,
+ 0x90d6, 0x02,
+ 0x90d7, 0x28,
+ 0x90d8, 0xb4,
+ 0xc938, 0x28,
+ 0xc939, 0xb1,
+ 0xc93a, 0x80,
+ 0xc93b, 0xd9,
+ 0x90d9, 0x90,
+ 0x90da, 0x83,
+ 0x90db, 0xba,
+ 0x90dc, 0xe0,
+ 0x90dd, 0xff,
+ 0x90de, 0x90,
+ 0x90df, 0xd2,
+ 0x90e0, 0x08,
+ 0x90e1, 0xe0,
+ 0x90e2, 0xe4,
+ 0x90e3, 0xef,
+ 0x90e4, 0xf0,
+ 0x90e5, 0xa3,
+ 0x90e6, 0xe0,
+ 0x90e7, 0x74,
+ 0x90e8, 0xff,
+ 0x90e9, 0xf0,
+ 0x90ea, 0x90,
+ 0x90eb, 0xd2,
+ 0x90ec, 0x0a,
+ 0x90ed, 0x02,
+ 0x90ee, 0x28,
+ 0x90ef, 0xb4,
+ 0xc93c, 0x29,
+ 0xc93d, 0x79,
+ 0xc93e, 0x80,
+ 0xc93f, 0xf0,
+ 0x90f0, 0xf0,
+ 0x90f1, 0x90,
+ 0x90f2, 0xd2,
+ 0x90f3, 0x0e,
+ 0x90f4, 0x02,
+ 0x90f5, 0x29,
+ 0x90f6, 0x7f,
+ 0xc940, 0x29,
+ 0xc941, 0x7c,
+ 0xc942, 0x80,
+ 0xc943, 0xf7,
+ 0x90f7, 0x90,
+ 0x90f8, 0x83,
+ 0x90f9, 0xba,
+ 0x90fa, 0xe0,
+ 0x90fb, 0xff,
+ 0x90fc, 0x90,
+ 0x90fd, 0xd2,
+ 0x90fe, 0x0c,
+ 0x90ff, 0xe0,
+ 0x9100, 0xe4,
+ 0x9101, 0xef,
+ 0x9102, 0xf0,
+ 0x9103, 0xa3,
+ 0x9104, 0xe0,
+ 0x9105, 0x74,
+ 0x9106, 0xff,
+ 0x9107, 0xf0,
+ 0x9108, 0x90,
+ 0x9109, 0xd2,
+ 0x910a, 0x0e,
+ 0x910b, 0x02,
+ 0x910c, 0x29,
+ 0x910d, 0x7f,
+ 0xc944, 0x2a,
+ 0xc945, 0x42,
+ 0xc946, 0x81,
+ 0xc947, 0x0e,
+ 0x910e, 0xf0,
+ 0x910f, 0x90,
+ 0x9110, 0xd2,
+ 0x9111, 0x12,
+ 0x9112, 0x02,
+ 0x9113, 0x2a,
+ 0x9114, 0x48,
+ 0xc948, 0x2a,
+ 0xc949, 0x45,
+ 0xc94a, 0x81,
+ 0xc94b, 0x15,
+ 0x9115, 0x90,
+ 0x9116, 0x83,
+ 0x9117, 0xba,
+ 0x9118, 0xe0,
+ 0x9119, 0xff,
+ 0x911a, 0x90,
+ 0x911b, 0xd2,
+ 0x911c, 0x10,
+ 0x911d, 0xe0,
+ 0x911e, 0xe4,
+ 0x911f, 0xef,
+ 0x9120, 0xf0,
+ 0x9121, 0xa3,
+ 0x9122, 0xe0,
+ 0x9123, 0x74,
+ 0x9124, 0xff,
+ 0x9125, 0xf0,
+ 0x9126, 0x90,
+ 0x9127, 0xd2,
+ 0x9128, 0x12,
+ 0x9129, 0x02,
+ 0x912a, 0x2a,
+ 0x912b, 0x48,
+ 0xc900, 0x01,
+ 0x0000, 0x00,
+};
+
+static const u16 vs6624_p2[] = {
+ 0x806f, 0x01,
+ 0x058c, 0x01,
+ 0x0000, 0x00,
+};
+
+static const u16 vs6624_run_setup[] = {
+ 0x1d18, 0x00, /* Enableconstrainedwhitebalance */
+ VS6624_PEAK_MIN_OUT_G_MSB, 0x3c, /* Damper PeakGain Output MSB */
+ VS6624_PEAK_MIN_OUT_G_LSB, 0x66, /* Damper PeakGain Output LSB */
+ VS6624_CM_LOW_THR_MSB, 0x65, /* Damper Low MSB */
+ VS6624_CM_LOW_THR_LSB, 0xd1, /* Damper Low LSB */
+ VS6624_CM_HIGH_THR_MSB, 0x66, /* Damper High MSB */
+ VS6624_CM_HIGH_THR_LSB, 0x62, /* Damper High LSB */
+ VS6624_CM_MIN_OUT_MSB, 0x00, /* Damper Min output MSB */
+ VS6624_CM_MIN_OUT_LSB, 0x00, /* Damper Min output LSB */
+ VS6624_NORA_DISABLE, 0x00, /* Nora fDisable */
+ VS6624_NORA_USAGE, 0x04, /* Nora usage */
+ VS6624_NORA_LOW_THR_MSB, 0x63, /* Damper Low MSB Changed 0x63 to 0x65 */
+ VS6624_NORA_LOW_THR_LSB, 0xd1, /* Damper Low LSB */
+ VS6624_NORA_HIGH_THR_MSB, 0x68, /* Damper High MSB */
+ VS6624_NORA_HIGH_THR_LSB, 0xdd, /* Damper High LSB */
+ VS6624_NORA_MIN_OUT_MSB, 0x3a, /* Damper Min output MSB */
+ VS6624_NORA_MIN_OUT_LSB, 0x00, /* Damper Min output LSB */
+ VS6624_F2B_DISABLE, 0x00, /* Disable */
+ 0x1d8a, 0x30, /* MAXWeightHigh */
+ 0x1d91, 0x62, /* fpDamperLowThresholdHigh MSB */
+ 0x1d92, 0x4a, /* fpDamperLowThresholdHigh LSB */
+ 0x1d95, 0x65, /* fpDamperHighThresholdHigh MSB */
+ 0x1d96, 0x0e, /* fpDamperHighThresholdHigh LSB */
+ 0x1da1, 0x3a, /* fpMinimumDamperOutputLow MSB */
+ 0x1da2, 0xb8, /* fpMinimumDamperOutputLow LSB */
+ 0x1e08, 0x06, /* MAXWeightLow */
+ 0x1e0a, 0x0a, /* MAXWeightHigh */
+ 0x1601, 0x3a, /* Red A MSB */
+ 0x1602, 0x14, /* Red A LSB */
+ 0x1605, 0x3b, /* Blue A MSB */
+ 0x1606, 0x85, /* BLue A LSB */
+ 0x1609, 0x3b, /* RED B MSB */
+ 0x160a, 0x85, /* RED B LSB */
+ 0x160d, 0x3a, /* Blue B MSB */
+ 0x160e, 0x14, /* Blue B LSB */
+ 0x1611, 0x30, /* Max Distance from Locus MSB */
+ 0x1612, 0x8f, /* Max Distance from Locus MSB */
+ 0x1614, 0x01, /* Enable constrainer */
+ 0x0000, 0x00,
+};
+
+static const u16 vs6624_default[] = {
+ VS6624_CONTRAST0, 0x84,
+ VS6624_SATURATION0, 0x75,
+ VS6624_GAMMA0, 0x11,
+ VS6624_CONTRAST1, 0x84,
+ VS6624_SATURATION1, 0x75,
+ VS6624_GAMMA1, 0x11,
+ VS6624_MAN_RG, 0x80,
+ VS6624_MAN_GG, 0x80,
+ VS6624_MAN_BG, 0x80,
+ VS6624_WB_MODE, 0x1,
+ VS6624_EXPO_COMPENSATION, 0xfe,
+ VS6624_EXPO_METER, 0x0,
+ VS6624_LIGHT_FREQ, 0x64,
+ VS6624_PEAK_GAIN, 0xe,
+ VS6624_PEAK_LOW_THR, 0x28,
+ VS6624_HMIRROR0, 0x0,
+ VS6624_VFLIP0, 0x0,
+ VS6624_ZOOM_HSTEP0_MSB, 0x0,
+ VS6624_ZOOM_HSTEP0_LSB, 0x1,
+ VS6624_ZOOM_VSTEP0_MSB, 0x0,
+ VS6624_ZOOM_VSTEP0_LSB, 0x1,
+ VS6624_PAN_HSTEP0_MSB, 0x0,
+ VS6624_PAN_HSTEP0_LSB, 0xf,
+ VS6624_PAN_VSTEP0_MSB, 0x0,
+ VS6624_PAN_VSTEP0_LSB, 0xf,
+ VS6624_SENSOR_MODE, 0x1,
+ VS6624_SYNC_CODE_SETUP, 0x21,
+ VS6624_DISABLE_FR_DAMPER, 0x0,
+ VS6624_FR_DEN, 0x1,
+ VS6624_FR_NUM_LSB, 0xf,
+ VS6624_INIT_PIPE_SETUP, 0x0,
+ VS6624_IMG_FMT0, 0x0,
+ VS6624_YUV_SETUP, 0x1,
+ VS6624_IMAGE_SIZE0, 0x2,
+ 0x0000, 0x00,
+};
+
+static inline struct vs6624 *to_vs6624(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct vs6624, sd);
+}
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct vs6624, hdl)->sd;
+}
+
+static int vs6624_read(struct v4l2_subdev *sd, u16 index)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 buf[2];
+
+ buf[0] = index >> 8;
+ buf[1] = index;
+ i2c_master_send(client, buf, 2);
+ i2c_master_recv(client, buf, 1);
+
+ return buf[0];
+}
+
+static int vs6624_write(struct v4l2_subdev *sd, u16 index,
+ u8 value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 buf[3];
+
+ buf[0] = index >> 8;
+ buf[1] = index;
+ buf[2] = value;
+
+ return i2c_master_send(client, buf, 3);
+}
+
+static int vs6624_writeregs(struct v4l2_subdev *sd, const u16 *regs)
+{
+ u16 reg;
+ u8 data;
+
+ while (*regs != 0x00) {
+ reg = *regs++;
+ data = *regs++;
+
+ vs6624_write(sd, reg, data);
+ }
+ return 0;
+}
+
+static int vs6624_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_CONTRAST:
+ vs6624_write(sd, VS6624_CONTRAST0, ctrl->val);
+ break;
+ case V4L2_CID_SATURATION:
+ vs6624_write(sd, VS6624_SATURATION0, ctrl->val);
+ break;
+ case V4L2_CID_HFLIP:
+ vs6624_write(sd, VS6624_HMIRROR0, ctrl->val);
+ break;
+ case V4L2_CID_VFLIP:
+ vs6624_write(sd, VS6624_VFLIP0, ctrl->val);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vs6624_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= ARRAY_SIZE(vs6624_formats))
+ return -EINVAL;
+
+ *code = vs6624_formats[index].mbus_code;
+ return 0;
+}
+
+static int vs6624_try_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ int index;
+
+ for (index = 0; index < ARRAY_SIZE(vs6624_formats); index++)
+ if (vs6624_formats[index].mbus_code == fmt->code)
+ break;
+ if (index >= ARRAY_SIZE(vs6624_formats)) {
+ /* default to first format */
+ index = 0;
+ fmt->code = vs6624_formats[0].mbus_code;
+ }
+
+ /* sensor mode is VGA */
+ if (fmt->width > VGA_WIDTH)
+ fmt->width = VGA_WIDTH;
+ if (fmt->height > VGA_HEIGHT)
+ fmt->height = VGA_HEIGHT;
+ fmt->width = fmt->width & (~3);
+ fmt->height = fmt->height & (~3);
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->colorspace = vs6624_formats[index].colorspace;
+ return 0;
+}
+
+static int vs6624_s_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct vs6624 *sensor = to_vs6624(sd);
+ int ret;
+
+ ret = vs6624_try_mbus_fmt(sd, fmt);
+ if (ret)
+ return ret;
+
+ /* set image format */
+ switch (fmt->code) {
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ vs6624_write(sd, VS6624_IMG_FMT0, 0x0);
+ vs6624_write(sd, VS6624_YUV_SETUP, 0x1);
+ break;
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ vs6624_write(sd, VS6624_IMG_FMT0, 0x0);
+ vs6624_write(sd, VS6624_YUV_SETUP, 0x3);
+ break;
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ vs6624_write(sd, VS6624_IMG_FMT0, 0x4);
+ vs6624_write(sd, VS6624_RGB_SETUP, 0x0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set image size */
+ if ((fmt->width == VGA_WIDTH) && (fmt->height == VGA_HEIGHT))
+ vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x2);
+ else if ((fmt->width == QVGA_WIDTH) && (fmt->height == QVGA_HEIGHT))
+ vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x4);
+ else if ((fmt->width == QQVGA_WIDTH) && (fmt->height == QQVGA_HEIGHT))
+ vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x6);
+ else if ((fmt->width == CIF_WIDTH) && (fmt->height == CIF_HEIGHT))
+ vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x3);
+ else if ((fmt->width == QCIF_WIDTH) && (fmt->height == QCIF_HEIGHT))
+ vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x5);
+ else if ((fmt->width == QQCIF_WIDTH) && (fmt->height == QQCIF_HEIGHT))
+ vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x7);
+ else {
+ vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x8);
+ vs6624_write(sd, VS6624_MAN_HSIZE0_MSB, fmt->width >> 8);
+ vs6624_write(sd, VS6624_MAN_HSIZE0_LSB, fmt->width & 0xFF);
+ vs6624_write(sd, VS6624_MAN_VSIZE0_MSB, fmt->height >> 8);
+ vs6624_write(sd, VS6624_MAN_VSIZE0_LSB, fmt->height & 0xFF);
+ vs6624_write(sd, VS6624_CROP_CTRL0, 0x1);
+ }
+
+ sensor->fmt = *fmt;
+
+ return 0;
+}
+
+static int vs6624_g_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct vs6624 *sensor = to_vs6624(sd);
+
+ *fmt = sensor->fmt;
+ return 0;
+}
+
+static int vs6624_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+{
+ struct vs6624 *sensor = to_vs6624(sd);
+ struct v4l2_captureparm *cp = &parms->parm.capture;
+
+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ memset(cp, 0, sizeof(*cp));
+ cp->capability = V4L2_CAP_TIMEPERFRAME;
+ cp->timeperframe.numerator = sensor->frame_rate.denominator;
+ cp->timeperframe.denominator = sensor->frame_rate.numerator;
+ return 0;
+}
+
+static int vs6624_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+{
+ struct vs6624 *sensor = to_vs6624(sd);
+ struct v4l2_captureparm *cp = &parms->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+
+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (cp->extendedmode != 0)
+ return -EINVAL;
+
+ if (tpf->numerator == 0 || tpf->denominator == 0
+ || (tpf->denominator > tpf->numerator * MAX_FRAME_RATE)) {
+ /* reset to max frame rate */
+ tpf->numerator = 1;
+ tpf->denominator = MAX_FRAME_RATE;
+ }
+ sensor->frame_rate.numerator = tpf->denominator;
+ sensor->frame_rate.denominator = tpf->numerator;
+ vs6624_write(sd, VS6624_DISABLE_FR_DAMPER, 0x0);
+ vs6624_write(sd, VS6624_FR_NUM_MSB,
+ sensor->frame_rate.numerator >> 8);
+ vs6624_write(sd, VS6624_FR_NUM_LSB,
+ sensor->frame_rate.numerator & 0xFF);
+ vs6624_write(sd, VS6624_FR_DEN,
+ sensor->frame_rate.denominator & 0xFF);
+ return 0;
+}
+
+static int vs6624_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ if (enable)
+ vs6624_write(sd, VS6624_USER_CMD, 0x2);
+ else
+ vs6624_write(sd, VS6624_USER_CMD, 0x4);
+ udelay(100);
+ return 0;
+}
+
+static int vs6624_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ int rev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ rev = (vs6624_read(sd, VS6624_FW_VSN_MAJOR) << 8)
+ | vs6624_read(sd, VS6624_FW_VSN_MINOR);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_VS6624, rev);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vs6624_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = vs6624_read(sd, reg->reg & 0xffff);
+ reg->size = 1;
+ return 0;
+}
+
+static int vs6624_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ vs6624_write(sd, reg->reg & 0xffff, reg->val & 0xff);
+ return 0;
+}
+#endif
+
+static const struct v4l2_ctrl_ops vs6624_ctrl_ops = {
+ .s_ctrl = vs6624_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops vs6624_core_ops = {
+ .g_chip_ident = vs6624_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = vs6624_g_register,
+ .s_register = vs6624_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops vs6624_video_ops = {
+ .enum_mbus_fmt = vs6624_enum_mbus_fmt,
+ .try_mbus_fmt = vs6624_try_mbus_fmt,
+ .s_mbus_fmt = vs6624_s_mbus_fmt,
+ .g_mbus_fmt = vs6624_g_mbus_fmt,
+ .s_parm = vs6624_s_parm,
+ .g_parm = vs6624_g_parm,
+ .s_stream = vs6624_s_stream,
+};
+
+static const struct v4l2_subdev_ops vs6624_ops = {
+ .core = &vs6624_core_ops,
+ .video = &vs6624_video_ops,
+};
+
+static int __devinit vs6624_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct vs6624 *sensor;
+ struct v4l2_subdev *sd;
+ struct v4l2_ctrl_handler *hdl;
+ const unsigned *ce;
+ int ret;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -EIO;
+
+ ce = client->dev.platform_data;
+ if (ce == NULL)
+ return -EINVAL;
+
+ ret = gpio_request(*ce, "VS6624 Chip Enable");
+ if (ret) {
+ v4l_err(client, "failed to request GPIO %d\n", *ce);
+ return ret;
+ }
+ gpio_direction_output(*ce, 1);
+ /* wait 100ms before any further i2c writes are performed */
+ mdelay(100);
+
+ sensor = kzalloc(sizeof(*sensor), GFP_KERNEL);
+ if (sensor == NULL) {
+ gpio_free(*ce);
+ return -ENOMEM;
+ }
+
+ sd = &sensor->sd;
+ v4l2_i2c_subdev_init(sd, client, &vs6624_ops);
+
+ vs6624_writeregs(sd, vs6624_p1);
+ vs6624_write(sd, VS6624_MICRO_EN, 0x2);
+ vs6624_write(sd, VS6624_DIO_EN, 0x1);
+ mdelay(10);
+ vs6624_writeregs(sd, vs6624_p2);
+
+ vs6624_writeregs(sd, vs6624_default);
+ vs6624_write(sd, VS6624_HSYNC_SETUP, 0xF);
+ vs6624_writeregs(sd, vs6624_run_setup);
+
+ /* set frame rate */
+ sensor->frame_rate.numerator = MAX_FRAME_RATE;
+ sensor->frame_rate.denominator = 1;
+ vs6624_write(sd, VS6624_DISABLE_FR_DAMPER, 0x0);
+ vs6624_write(sd, VS6624_FR_NUM_MSB,
+ sensor->frame_rate.numerator >> 8);
+ vs6624_write(sd, VS6624_FR_NUM_LSB,
+ sensor->frame_rate.numerator & 0xFF);
+ vs6624_write(sd, VS6624_FR_DEN,
+ sensor->frame_rate.denominator & 0xFF);
+
+ sensor->fmt = vs6624_default_fmt;
+ sensor->ce_pin = *ce;
+
+ v4l_info(client, "chip found @ 0x%02x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ hdl = &sensor->hdl;
+ v4l2_ctrl_handler_init(hdl, 4);
+ v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 0xFF, 1, 0x87);
+ v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 0xFF, 1, 0x78);
+ v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ /* hook the control handler into the driver */
+ sd->ctrl_handler = hdl;
+ if (hdl->error) {
+ int err = hdl->error;
+
+ v4l2_ctrl_handler_free(hdl);
+ kfree(sensor);
+ gpio_free(*ce);
+ return err;
+ }
+
+ /* initialize the hardware to the default control values */
+ ret = v4l2_ctrl_handler_setup(hdl);
+ if (ret) {
+ v4l2_ctrl_handler_free(hdl);
+ kfree(sensor);
+ gpio_free(*ce);
+ }
+ return ret;
+}
+
+static int __devexit vs6624_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct vs6624 *sensor = to_vs6624(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+ gpio_free(sensor->ce_pin);
+ kfree(sensor);
+ return 0;
+}
+
+static const struct i2c_device_id vs6624_id[] = {
+ {"vs6624", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, vs6624_id);
+
+static struct i2c_driver vs6624_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "vs6624",
+ },
+ .probe = vs6624_probe,
+ .remove = __devexit_p(vs6624_remove),
+ .id_table = vs6624_id,
+};
+
+static __init int vs6624_init(void)
+{
+ return i2c_add_driver(&vs6624_driver);
+}
+
+static __exit void vs6624_exit(void)
+{
+ i2c_del_driver(&vs6624_driver);
+}
+
+module_init(vs6624_init);
+module_exit(vs6624_exit);
+
+MODULE_DESCRIPTION("VS6624 sensor driver");
+MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/vs6624_regs.h b/drivers/media/video/vs6624_regs.h
new file mode 100644
index 00000000..6ba2ee25
--- /dev/null
+++ b/drivers/media/video/vs6624_regs.h
@@ -0,0 +1,337 @@
+/*
+ * vs6624 - ST VS6624 CMOS image sensor registers
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _VS6624_REGS_H_
+#define _VS6624_REGS_H_
+
+/* low level control registers */
+#define VS6624_MICRO_EN 0xC003 /* power enable for all MCU clock */
+#define VS6624_DIO_EN 0xC044 /* enable digital I/O */
+/* device parameters */
+#define VS6624_DEV_ID_MSB 0x0001 /* device id MSB */
+#define VS6624_DEV_ID_LSB 0x0002 /* device id LSB */
+#define VS6624_FW_VSN_MAJOR 0x0004 /* firmware version major */
+#define VS6624_FW_VSN_MINOR 0x0006 /* firmware version minor */
+#define VS6624_PATCH_VSN_MAJOR 0x0008 /* patch version major */
+#define VS6624_PATCH_VSN_MINOR 0x000A /* patch version minor */
+/* host interface manager control */
+#define VS6624_USER_CMD 0x0180 /* user level control of operating states */
+/* host interface manager status */
+#define VS6624_STATE 0x0202 /* current state of the mode manager */
+/* run mode control */
+#define VS6624_METER_ON 0x0280 /* if false AE and AWB are disabled */
+/* mode setup */
+#define VS6624_ACTIVE_PIPE_SETUP 0x0302 /* select the active bank for non view live mode */
+#define VS6624_SENSOR_MODE 0x0308 /* select the different sensor mode */
+/* pipe setup bank0 */
+#define VS6624_IMAGE_SIZE0 0x0380 /* required output dimension */
+#define VS6624_MAN_HSIZE0_MSB 0x0383 /* input required manual H size MSB */
+#define VS6624_MAN_HSIZE0_LSB 0x0384 /* input required manual H size LSB */
+#define VS6624_MAN_VSIZE0_MSB 0x0387 /* input required manual V size MSB */
+#define VS6624_MAN_VSIZE0_LSB 0x0388 /* input required manual V size LSB */
+#define VS6624_ZOOM_HSTEP0_MSB 0x038B /* set the zoom H step MSB */
+#define VS6624_ZOOM_HSTEP0_LSB 0x038C /* set the zoom H step LSB */
+#define VS6624_ZOOM_VSTEP0_MSB 0x038F /* set the zoom V step MSB */
+#define VS6624_ZOOM_VSTEP0_LSB 0x0390 /* set the zoom V step LSB */
+#define VS6624_ZOOM_CTRL0 0x0392 /* control zoon in, out and stop */
+#define VS6624_PAN_HSTEP0_MSB 0x0395 /* set the pan H step MSB */
+#define VS6624_PAN_HSTEP0_LSB 0x0396 /* set the pan H step LSB */
+#define VS6624_PAN_VSTEP0_MSB 0x0399 /* set the pan V step MSB */
+#define VS6624_PAN_VSTEP0_LSB 0x039A /* set the pan V step LSB */
+#define VS6624_PAN_CTRL0 0x039C /* control pan operation */
+#define VS6624_CROP_CTRL0 0x039E /* select cropping mode */
+#define VS6624_CROP_HSTART0_MSB 0x03A1 /* set the cropping H start address MSB */
+#define VS6624_CROP_HSTART0_LSB 0x03A2 /* set the cropping H start address LSB */
+#define VS6624_CROP_HSIZE0_MSB 0x03A5 /* set the cropping H size MSB */
+#define VS6624_CROP_HSIZE0_LSB 0x03A6 /* set the cropping H size LSB */
+#define VS6624_CROP_VSTART0_MSB 0x03A9 /* set the cropping V start address MSB */
+#define VS6624_CROP_VSTART0_LSB 0x03AA /* set the cropping V start address LSB */
+#define VS6624_CROP_VSIZE0_MSB 0x03AD /* set the cropping V size MSB */
+#define VS6624_CROP_VSIZE0_LSB 0x03AE /* set the cropping V size LSB */
+#define VS6624_IMG_FMT0 0x03B0 /* select required output image format */
+#define VS6624_BAYER_OUT_ALIGN0 0x03B2 /* set bayer output alignment */
+#define VS6624_CONTRAST0 0x03B4 /* contrast control for output */
+#define VS6624_SATURATION0 0x03B6 /* saturation control for output */
+#define VS6624_GAMMA0 0x03B8 /* gamma settings */
+#define VS6624_HMIRROR0 0x03BA /* horizontal image orientation flip */
+#define VS6624_VFLIP0 0x03BC /* vertical image orientation flip */
+#define VS6624_CHANNEL_ID0 0x03BE /* logical DMA channel number */
+/* pipe setup bank1 */
+#define VS6624_IMAGE_SIZE1 0x0400 /* required output dimension */
+#define VS6624_MAN_HSIZE1_MSB 0x0403 /* input required manual H size MSB */
+#define VS6624_MAN_HSIZE1_LSB 0x0404 /* input required manual H size LSB */
+#define VS6624_MAN_VSIZE1_MSB 0x0407 /* input required manual V size MSB */
+#define VS6624_MAN_VSIZE1_LSB 0x0408 /* input required manual V size LSB */
+#define VS6624_ZOOM_HSTEP1_MSB 0x040B /* set the zoom H step MSB */
+#define VS6624_ZOOM_HSTEP1_LSB 0x040C /* set the zoom H step LSB */
+#define VS6624_ZOOM_VSTEP1_MSB 0x040F /* set the zoom V step MSB */
+#define VS6624_ZOOM_VSTEP1_LSB 0x0410 /* set the zoom V step LSB */
+#define VS6624_ZOOM_CTRL1 0x0412 /* control zoon in, out and stop */
+#define VS6624_PAN_HSTEP1_MSB 0x0415 /* set the pan H step MSB */
+#define VS6624_PAN_HSTEP1_LSB 0x0416 /* set the pan H step LSB */
+#define VS6624_PAN_VSTEP1_MSB 0x0419 /* set the pan V step MSB */
+#define VS6624_PAN_VSTEP1_LSB 0x041A /* set the pan V step LSB */
+#define VS6624_PAN_CTRL1 0x041C /* control pan operation */
+#define VS6624_CROP_CTRL1 0x041E /* select cropping mode */
+#define VS6624_CROP_HSTART1_MSB 0x0421 /* set the cropping H start address MSB */
+#define VS6624_CROP_HSTART1_LSB 0x0422 /* set the cropping H start address LSB */
+#define VS6624_CROP_HSIZE1_MSB 0x0425 /* set the cropping H size MSB */
+#define VS6624_CROP_HSIZE1_LSB 0x0426 /* set the cropping H size LSB */
+#define VS6624_CROP_VSTART1_MSB 0x0429 /* set the cropping V start address MSB */
+#define VS6624_CROP_VSTART1_LSB 0x042A /* set the cropping V start address LSB */
+#define VS6624_CROP_VSIZE1_MSB 0x042D /* set the cropping V size MSB */
+#define VS6624_CROP_VSIZE1_LSB 0x042E /* set the cropping V size LSB */
+#define VS6624_IMG_FMT1 0x0430 /* select required output image format */
+#define VS6624_BAYER_OUT_ALIGN1 0x0432 /* set bayer output alignment */
+#define VS6624_CONTRAST1 0x0434 /* contrast control for output */
+#define VS6624_SATURATION1 0x0436 /* saturation control for output */
+#define VS6624_GAMMA1 0x0438 /* gamma settings */
+#define VS6624_HMIRROR1 0x043A /* horizontal image orientation flip */
+#define VS6624_VFLIP1 0x043C /* vertical image orientation flip */
+#define VS6624_CHANNEL_ID1 0x043E /* logical DMA channel number */
+/* view live control */
+#define VS6624_VIEW_LIVE_EN 0x0480 /* enable view live mode */
+#define VS6624_INIT_PIPE_SETUP 0x0482 /* select initial pipe setup bank */
+/* view live status */
+#define VS6624_CUR_PIPE_SETUP 0x0500 /* indicates most recently applied setup bank */
+/* power management */
+#define VS6624_TIME_TO_POWER_DOWN 0x0580 /* automatically transition time to stop mode */
+/* video timing parameter host inputs */
+#define VS6624_EXT_CLK_FREQ_NUM_MSB 0x0605 /* external clock frequency numerator MSB */
+#define VS6624_EXT_CLK_FREQ_NUM_LSB 0x0606 /* external clock frequency numerator LSB */
+#define VS6624_EXT_CLK_FREQ_DEN 0x0608 /* external clock frequency denominator */
+/* video timing control */
+#define VS6624_SYS_CLK_MODE 0x0880 /* decides system clock frequency */
+/* frame dimension parameter host inputs */
+#define VS6624_LIGHT_FREQ 0x0C80 /* AC frequency used for flicker free time */
+#define VS6624_FLICKER_COMPAT 0x0C82 /* flicker compatible frame length */
+/* static frame rate control */
+#define VS6624_FR_NUM_MSB 0x0D81 /* desired frame rate numerator MSB */
+#define VS6624_FR_NUM_LSB 0x0D82 /* desired frame rate numerator LSB */
+#define VS6624_FR_DEN 0x0D84 /* desired frame rate denominator */
+/* automatic frame rate control */
+#define VS6624_DISABLE_FR_DAMPER 0x0E80 /* defines frame rate mode */
+#define VS6624_MIN_DAMPER_OUT_MSB 0x0E8C /* minimum frame rate MSB */
+#define VS6624_MIN_DAMPER_OUT_LSB 0x0E8A /* minimum frame rate LSB */
+/* exposure controls */
+#define VS6624_EXPO_MODE 0x1180 /* exposure mode */
+#define VS6624_EXPO_METER 0x1182 /* weights to be associated with the zones */
+#define VS6624_EXPO_TIME_NUM 0x1184 /* exposure time numerator */
+#define VS6624_EXPO_TIME_DEN 0x1186 /* exposure time denominator */
+#define VS6624_EXPO_TIME_MSB 0x1189 /* exposure time for the Manual Mode MSB */
+#define VS6624_EXPO_TIME_LSB 0x118A /* exposure time for the Manual Mode LSB */
+#define VS6624_EXPO_COMPENSATION 0x1190 /* exposure compensation */
+#define VS6624_DIRECT_COARSE_MSB 0x1195 /* coarse integration lines for Direct Mode MSB */
+#define VS6624_DIRECT_COARSE_LSB 0x1196 /* coarse integration lines for Direct Mode LSB */
+#define VS6624_DIRECT_FINE_MSB 0x1199 /* fine integration pixels for Direct Mode MSB */
+#define VS6624_DIRECT_FINE_LSB 0x119A /* fine integration pixels for Direct Mode LSB */
+#define VS6624_DIRECT_ANAL_GAIN_MSB 0x119D /* analog gain for Direct Mode MSB */
+#define VS6624_DIRECT_ANAL_GAIN_LSB 0x119E /* analog gain for Direct Mode LSB */
+#define VS6624_DIRECT_DIGI_GAIN_MSB 0x11A1 /* digital gain for Direct Mode MSB */
+#define VS6624_DIRECT_DIGI_GAIN_LSB 0x11A2 /* digital gain for Direct Mode LSB */
+#define VS6624_FLASH_COARSE_MSB 0x11A5 /* coarse integration lines for Flash Gun Mode MSB */
+#define VS6624_FLASH_COARSE_LSB 0x11A6 /* coarse integration lines for Flash Gun Mode LSB */
+#define VS6624_FLASH_FINE_MSB 0x11A9 /* fine integration pixels for Flash Gun Mode MSB */
+#define VS6624_FLASH_FINE_LSB 0x11AA /* fine integration pixels for Flash Gun Mode LSB */
+#define VS6624_FLASH_ANAL_GAIN_MSB 0x11AD /* analog gain for Flash Gun Mode MSB */
+#define VS6624_FLASH_ANAL_GAIN_LSB 0x11AE /* analog gain for Flash Gun Mode LSB */
+#define VS6624_FLASH_DIGI_GAIN_MSB 0x11B1 /* digital gain for Flash Gun Mode MSB */
+#define VS6624_FLASH_DIGI_GAIN_LSB 0x11B2 /* digital gain for Flash Gun Mode LSB */
+#define VS6624_FREEZE_AE 0x11B4 /* freeze auto exposure */
+#define VS6624_MAX_INT_TIME_MSB 0x11B7 /* user maximum integration time MSB */
+#define VS6624_MAX_INT_TIME_LSB 0x11B8 /* user maximum integration time LSB */
+#define VS6624_FLASH_AG_THR_MSB 0x11BB /* recommend flash gun analog gain threshold MSB */
+#define VS6624_FLASH_AG_THR_LSB 0x11BC /* recommend flash gun analog gain threshold LSB */
+#define VS6624_ANTI_FLICKER_MODE 0x11C0 /* anti flicker mode */
+/* white balance control */
+#define VS6624_WB_MODE 0x1480 /* set white balance mode */
+#define VS6624_MAN_RG 0x1482 /* user setting for red channel gain */
+#define VS6624_MAN_GG 0x1484 /* user setting for green channel gain */
+#define VS6624_MAN_BG 0x1486 /* user setting for blue channel gain */
+#define VS6624_FLASH_RG_MSB 0x148B /* red gain for Flash Gun MSB */
+#define VS6624_FLASH_RG_LSB 0x148C /* red gain for Flash Gun LSB */
+#define VS6624_FLASH_GG_MSB 0x148F /* green gain for Flash Gun MSB */
+#define VS6624_FLASH_GG_LSB 0x1490 /* green gain for Flash Gun LSB */
+#define VS6624_FLASH_BG_MSB 0x1493 /* blue gain for Flash Gun MSB */
+#define VS6624_FLASH_BG_LSB 0x1494 /* blue gain for Flash Gun LSB */
+/* sensor setup */
+#define VS6624_BC_OFFSET 0x1990 /* Black Correction Offset */
+/* image stability */
+#define VS6624_STABLE_WB 0x1900 /* white balance stable */
+#define VS6624_STABLE_EXPO 0x1902 /* exposure stable */
+#define VS6624_STABLE 0x1906 /* system stable */
+/* flash control */
+#define VS6624_FLASH_MODE 0x1A80 /* flash mode */
+#define VS6624_FLASH_OFF_LINE_MSB 0x1A83 /* off line at flash pulse mode MSB */
+#define VS6624_FLASH_OFF_LINE_LSB 0x1A84 /* off line at flash pulse mode LSB */
+/* flash status */
+#define VS6624_FLASH_RECOM 0x1B00 /* flash gun is recommended */
+#define VS6624_FLASH_GRAB_COMPLETE 0x1B02 /* flash gun image has been grabbed */
+/* scythe filter controls */
+#define VS6624_SCYTHE_FILTER 0x1D80 /* disable scythe defect correction */
+/* jack filter controls */
+#define VS6624_JACK_FILTER 0x1E00 /* disable jack defect correction */
+/* demosaic control */
+#define VS6624_ANTI_ALIAS_FILTER 0x1E80 /* anti alias filter suppress */
+/* color matrix dampers */
+#define VS6624_CM_DISABLE 0x1F00 /* disable color matrix damper */
+#define VS6624_CM_LOW_THR_MSB 0x1F03 /* low threshold for exposure MSB */
+#define VS6624_CM_LOW_THR_LSB 0x1F04 /* low threshold for exposure LSB */
+#define VS6624_CM_HIGH_THR_MSB 0x1F07 /* high threshold for exposure MSB */
+#define VS6624_CM_HIGH_THR_LSB 0x1F08 /* high threshold for exposure LSB */
+#define VS6624_CM_MIN_OUT_MSB 0x1F0B /* minimum possible damper output MSB */
+#define VS6624_CM_MIN_OUT_LSB 0x1F0C /* minimum possible damper output LSB */
+/* peaking control */
+#define VS6624_PEAK_GAIN 0x2000 /* controls peaking gain */
+#define VS6624_PEAK_G_DISABLE 0x2002 /* disable peak gain damping */
+#define VS6624_PEAK_LOW_THR_G_MSB 0x2005 /* low threshold for exposure for gain MSB */
+#define VS6624_PEAK_LOW_THR_G_LSB 0x2006 /* low threshold for exposure for gain LSB */
+#define VS6624_PEAK_HIGH_THR_G_MSB 0x2009 /* high threshold for exposure for gain MSB */
+#define VS6624_PEAK_HIGH_THR_G_LSB 0x200A /* high threshold for exposure for gain LSB */
+#define VS6624_PEAK_MIN_OUT_G_MSB 0x200D /* minimum damper output for gain MSB */
+#define VS6624_PEAK_MIN_OUT_G_LSB 0x200E /* minimum damper output for gain LSB */
+#define VS6624_PEAK_LOW_THR 0x2010 /* adjust degree of coring */
+#define VS6624_PEAK_C_DISABLE 0x2012 /* disable coring damping */
+#define VS6624_PEAK_HIGH_THR 0x2014 /* adjust maximum gain */
+#define VS6624_PEAK_LOW_THR_C_MSB 0x2017 /* low threshold for exposure for coring MSB */
+#define VS6624_PEAK_LOW_THR_C_LSB 0x2018 /* low threshold for exposure for coring LSB */
+#define VS6624_PEAK_HIGH_THR_C_MSB 0x201B /* high threshold for exposure for coring MSB */
+#define VS6624_PEAK_HIGH_THR_C_LSB 0x201C /* high threshold for exposure for coring LSB */
+#define VS6624_PEAK_MIN_OUT_C_MSB 0x201F /* minimum damper output for coring MSB */
+#define VS6624_PEAK_MIN_OUT_C_LSB 0x2020 /* minimum damper output for coring LSB */
+/* pipe 0 RGB to YUV matrix manual control */
+#define VS6624_RYM0_MAN_CTRL 0x2180 /* enable manual RGB to YUV matrix */
+#define VS6624_RYM0_W00_MSB 0x2183 /* row 0 column 0 of YUV matrix MSB */
+#define VS6624_RYM0_W00_LSB 0x2184 /* row 0 column 0 of YUV matrix LSB */
+#define VS6624_RYM0_W01_MSB 0x2187 /* row 0 column 1 of YUV matrix MSB */
+#define VS6624_RYM0_W01_LSB 0x2188 /* row 0 column 1 of YUV matrix LSB */
+#define VS6624_RYM0_W02_MSB 0x218C /* row 0 column 2 of YUV matrix MSB */
+#define VS6624_RYM0_W02_LSB 0x218D /* row 0 column 2 of YUV matrix LSB */
+#define VS6624_RYM0_W10_MSB 0x2190 /* row 1 column 0 of YUV matrix MSB */
+#define VS6624_RYM0_W10_LSB 0x218F /* row 1 column 0 of YUV matrix LSB */
+#define VS6624_RYM0_W11_MSB 0x2193 /* row 1 column 1 of YUV matrix MSB */
+#define VS6624_RYM0_W11_LSB 0x2194 /* row 1 column 1 of YUV matrix LSB */
+#define VS6624_RYM0_W12_MSB 0x2197 /* row 1 column 2 of YUV matrix MSB */
+#define VS6624_RYM0_W12_LSB 0x2198 /* row 1 column 2 of YUV matrix LSB */
+#define VS6624_RYM0_W20_MSB 0x219B /* row 2 column 0 of YUV matrix MSB */
+#define VS6624_RYM0_W20_LSB 0x219C /* row 2 column 0 of YUV matrix LSB */
+#define VS6624_RYM0_W21_MSB 0x21A0 /* row 2 column 1 of YUV matrix MSB */
+#define VS6624_RYM0_W21_LSB 0x219F /* row 2 column 1 of YUV matrix LSB */
+#define VS6624_RYM0_W22_MSB 0x21A3 /* row 2 column 2 of YUV matrix MSB */
+#define VS6624_RYM0_W22_LSB 0x21A4 /* row 2 column 2 of YUV matrix LSB */
+#define VS6624_RYM0_YINY_MSB 0x21A7 /* Y in Y MSB */
+#define VS6624_RYM0_YINY_LSB 0x21A8 /* Y in Y LSB */
+#define VS6624_RYM0_YINCB_MSB 0x21AB /* Y in Cb MSB */
+#define VS6624_RYM0_YINCB_LSB 0x21AC /* Y in Cb LSB */
+#define VS6624_RYM0_YINCR_MSB 0x21B0 /* Y in Cr MSB */
+#define VS6624_RYM0_YINCR_LSB 0x21AF /* Y in Cr LSB */
+/* pipe 1 RGB to YUV matrix manual control */
+#define VS6624_RYM1_MAN_CTRL 0x2200 /* enable manual RGB to YUV matrix */
+#define VS6624_RYM1_W00_MSB 0x2203 /* row 0 column 0 of YUV matrix MSB */
+#define VS6624_RYM1_W00_LSB 0x2204 /* row 0 column 0 of YUV matrix LSB */
+#define VS6624_RYM1_W01_MSB 0x2207 /* row 0 column 1 of YUV matrix MSB */
+#define VS6624_RYM1_W01_LSB 0x2208 /* row 0 column 1 of YUV matrix LSB */
+#define VS6624_RYM1_W02_MSB 0x220C /* row 0 column 2 of YUV matrix MSB */
+#define VS6624_RYM1_W02_LSB 0x220D /* row 0 column 2 of YUV matrix LSB */
+#define VS6624_RYM1_W10_MSB 0x2210 /* row 1 column 0 of YUV matrix MSB */
+#define VS6624_RYM1_W10_LSB 0x220F /* row 1 column 0 of YUV matrix LSB */
+#define VS6624_RYM1_W11_MSB 0x2213 /* row 1 column 1 of YUV matrix MSB */
+#define VS6624_RYM1_W11_LSB 0x2214 /* row 1 column 1 of YUV matrix LSB */
+#define VS6624_RYM1_W12_MSB 0x2217 /* row 1 column 2 of YUV matrix MSB */
+#define VS6624_RYM1_W12_LSB 0x2218 /* row 1 column 2 of YUV matrix LSB */
+#define VS6624_RYM1_W20_MSB 0x221B /* row 2 column 0 of YUV matrix MSB */
+#define VS6624_RYM1_W20_LSB 0x221C /* row 2 column 0 of YUV matrix LSB */
+#define VS6624_RYM1_W21_MSB 0x2220 /* row 2 column 1 of YUV matrix MSB */
+#define VS6624_RYM1_W21_LSB 0x221F /* row 2 column 1 of YUV matrix LSB */
+#define VS6624_RYM1_W22_MSB 0x2223 /* row 2 column 2 of YUV matrix MSB */
+#define VS6624_RYM1_W22_LSB 0x2224 /* row 2 column 2 of YUV matrix LSB */
+#define VS6624_RYM1_YINY_MSB 0x2227 /* Y in Y MSB */
+#define VS6624_RYM1_YINY_LSB 0x2228 /* Y in Y LSB */
+#define VS6624_RYM1_YINCB_MSB 0x222B /* Y in Cb MSB */
+#define VS6624_RYM1_YINCB_LSB 0x222C /* Y in Cb LSB */
+#define VS6624_RYM1_YINCR_MSB 0x2220 /* Y in Cr MSB */
+#define VS6624_RYM1_YINCR_LSB 0x222F /* Y in Cr LSB */
+/* pipe 0 gamma manual control */
+#define VS6624_GAMMA_MAN_CTRL0 0x2280 /* enable manual gamma setup */
+#define VS6624_GAMMA_PEAK_R0 0x2282 /* peaked red channel gamma value */
+#define VS6624_GAMMA_PEAK_G0 0x2284 /* peaked green channel gamma value */
+#define VS6624_GAMMA_PEAK_B0 0x2286 /* peaked blue channel gamma value */
+#define VS6624_GAMMA_UNPEAK_R0 0x2288 /* unpeaked red channel gamma value */
+#define VS6624_GAMMA_UNPEAK_G0 0x228A /* unpeaked green channel gamma value */
+#define VS6624_GAMMA_UNPEAK_B0 0x228C /* unpeaked blue channel gamma value */
+/* pipe 1 gamma manual control */
+#define VS6624_GAMMA_MAN_CTRL1 0x2300 /* enable manual gamma setup */
+#define VS6624_GAMMA_PEAK_R1 0x2302 /* peaked red channel gamma value */
+#define VS6624_GAMMA_PEAK_G1 0x2304 /* peaked green channel gamma value */
+#define VS6624_GAMMA_PEAK_B1 0x2306 /* peaked blue channel gamma value */
+#define VS6624_GAMMA_UNPEAK_R1 0x2308 /* unpeaked red channel gamma value */
+#define VS6624_GAMMA_UNPEAK_G1 0x230A /* unpeaked green channel gamma value */
+#define VS6624_GAMMA_UNPEAK_B1 0x230C /* unpeaked blue channel gamma value */
+/* fade to black */
+#define VS6624_F2B_DISABLE 0x2480 /* disable fade to black */
+#define VS6624_F2B_BLACK_VAL_MSB 0x2483 /* black value MSB */
+#define VS6624_F2B_BLACK_VAL_LSB 0x2484 /* black value LSB */
+#define VS6624_F2B_LOW_THR_MSB 0x2487 /* low threshold for exposure MSB */
+#define VS6624_F2B_LOW_THR_LSB 0x2488 /* low threshold for exposure LSB */
+#define VS6624_F2B_HIGH_THR_MSB 0x248B /* high threshold for exposure MSB */
+#define VS6624_F2B_HIGH_THR_LSB 0x248C /* high threshold for exposure LSB */
+#define VS6624_F2B_MIN_OUT_MSB 0x248F /* minimum damper output MSB */
+#define VS6624_F2B_MIN_OUT_LSB 0x2490 /* minimum damper output LSB */
+/* output formatter control */
+#define VS6624_CODE_CK_EN 0x2580 /* code check enable */
+#define VS6624_BLANK_FMT 0x2582 /* blank format */
+#define VS6624_SYNC_CODE_SETUP 0x2584 /* sync code setup */
+#define VS6624_HSYNC_SETUP 0x2586 /* H sync setup */
+#define VS6624_VSYNC_SETUP 0x2588 /* V sync setup */
+#define VS6624_PCLK_SETUP 0x258A /* PCLK setup */
+#define VS6624_PCLK_EN 0x258C /* PCLK enable */
+#define VS6624_OPF_SP_SETUP 0x258E /* output formatter sp setup */
+#define VS6624_BLANK_DATA_MSB 0x2590 /* blank data MSB */
+#define VS6624_BLANK_DATA_LSB 0x2592 /* blank data LSB */
+#define VS6624_RGB_SETUP 0x2594 /* RGB setup */
+#define VS6624_YUV_SETUP 0x2596 /* YUV setup */
+#define VS6624_VSYNC_RIS_COARSE_H 0x2598 /* V sync rising coarse high */
+#define VS6624_VSYNC_RIS_COARSE_L 0x259A /* V sync rising coarse low */
+#define VS6624_VSYNC_RIS_FINE_H 0x259C /* V sync rising fine high */
+#define VS6624_VSYNC_RIS_FINE_L 0x259E /* V sync rising fine low */
+#define VS6624_VSYNC_FALL_COARSE_H 0x25A0 /* V sync falling coarse high */
+#define VS6624_VSYNC_FALL_COARSE_L 0x25A2 /* V sync falling coarse low */
+#define VS6624_VSYNC_FALL_FINE_H 0x25A4 /* V sync falling fine high */
+#define VS6624_VSYNC_FALL_FINE_L 0x25A6 /* V sync falling fine low */
+#define VS6624_HSYNC_RIS_H 0x25A8 /* H sync rising high */
+#define VS6624_HSYNC_RIS_L 0x25AA /* H sync rising low */
+#define VS6624_HSYNC_FALL_H 0x25AC /* H sync falling high */
+#define VS6624_HSYNC_FALL_L 0x25AE /* H sync falling low */
+#define VS6624_OUT_IF 0x25B0 /* output interface */
+#define VS6624_CCP_EXT_DATA 0x25B2 /* CCP extra data */
+/* NoRA controls */
+#define VS6624_NORA_DISABLE 0x2600 /* NoRA control mode */
+#define VS6624_NORA_USAGE 0x2602 /* usage */
+#define VS6624_NORA_SPLIT_KN 0x2604 /* split kn */
+#define VS6624_NORA_SPLIT_NI 0x2606 /* split ni */
+#define VS6624_NORA_TIGHT_G 0x2608 /* tight green */
+#define VS6624_NORA_DISABLE_NP 0x260A /* disable noro promoting */
+#define VS6624_NORA_LOW_THR_MSB 0x260D /* low threshold for exposure MSB */
+#define VS6624_NORA_LOW_THR_LSB 0x260E /* low threshold for exposure LSB */
+#define VS6624_NORA_HIGH_THR_MSB 0x2611 /* high threshold for exposure MSB */
+#define VS6624_NORA_HIGH_THR_LSB 0x2612 /* high threshold for exposure LSB */
+#define VS6624_NORA_MIN_OUT_MSB 0x2615 /* minimum damper output MSB */
+#define VS6624_NORA_MIN_OUT_LSB 0x2616 /* minimum damper output LSB */
+
+#endif
diff --git a/drivers/media/video/w9966.c b/drivers/media/video/w9966.c
new file mode 100644
index 00000000..7fd7ac56
--- /dev/null
+++ b/drivers/media/video/w9966.c
@@ -0,0 +1,993 @@
+/*
+ Winbond w9966cf Webcam parport driver.
+
+ Version 0.33
+
+ Copyright (C) 2001 Jakob Kemi <jakob.kemi@post.utfors.se>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+/*
+ Supported devices:
+ *Lifeview FlyCam Supra (using the Philips saa7111a chip)
+
+ Does any other model using the w9966 interface chip exist ?
+
+ Todo:
+
+ *Add a working EPP mode, since DMA ECP read isn't implemented
+ in the parport drivers. (That's why it's so sloow)
+
+ *Add support for other ccd-control chips than the saa7111
+ please send me feedback on what kind of chips you have.
+
+ *Add proper probing. I don't know what's wrong with the IEEE1284
+ parport drivers but (IEEE1284_MODE_NIBBLE|IEEE1284_DEVICE_ID)
+ and nibble read seems to be broken for some peripherals.
+
+ *Add probing for onboard SRAM, port directions etc. (if possible)
+
+ *Add support for the hardware compressed modes (maybe using v4l2)
+
+ *Fix better support for the capture window (no skewed images, v4l
+ interface to capt. window)
+
+ *Probably some bugs that I don't know of
+
+ Please support me by sending feedback!
+
+ Changes:
+
+ Alan Cox: Removed RGB mode for kernel merge, added THIS_MODULE
+ and owner support for newer module locks
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <linux/parport.h>
+
+/*#define DEBUG*/ /* Undef me for production */
+
+#ifdef DEBUG
+#define DPRINTF(x, a...) printk(KERN_DEBUG "W9966: %s(): "x, __func__ , ##a)
+#else
+#define DPRINTF(x...)
+#endif
+
+/*
+ * Defines, simple typedefs etc.
+ */
+
+#define W9966_DRIVERNAME "W9966CF Webcam"
+#define W9966_MAXCAMS 4 /* Maximum number of cameras */
+#define W9966_RBUFFER 2048 /* Read buffer (must be an even number) */
+#define W9966_SRAMSIZE 131072 /* 128kb */
+#define W9966_SRAMID 0x02 /* check w9966cf.pdf */
+
+/* Empirically determined window limits */
+#define W9966_WND_MIN_X 16
+#define W9966_WND_MIN_Y 14
+#define W9966_WND_MAX_X 705
+#define W9966_WND_MAX_Y 253
+#define W9966_WND_MAX_W (W9966_WND_MAX_X - W9966_WND_MIN_X)
+#define W9966_WND_MAX_H (W9966_WND_MAX_Y - W9966_WND_MIN_Y)
+
+/* Keep track of our current state */
+#define W9966_STATE_PDEV 0x01
+#define W9966_STATE_CLAIMED 0x02
+#define W9966_STATE_VDEV 0x04
+
+#define W9966_I2C_W_ID 0x48
+#define W9966_I2C_R_ID 0x49
+#define W9966_I2C_R_DATA 0x08
+#define W9966_I2C_R_CLOCK 0x04
+#define W9966_I2C_W_DATA 0x02
+#define W9966_I2C_W_CLOCK 0x01
+
+struct w9966 {
+ struct v4l2_device v4l2_dev;
+ unsigned char dev_state;
+ unsigned char i2c_state;
+ unsigned short ppmode;
+ struct parport *pport;
+ struct pardevice *pdev;
+ struct video_device vdev;
+ unsigned short width;
+ unsigned short height;
+ unsigned char brightness;
+ signed char contrast;
+ signed char color;
+ signed char hue;
+ struct mutex lock;
+};
+
+/*
+ * Module specific properties
+ */
+
+MODULE_AUTHOR("Jakob Kemi <jakob.kemi@post.utfors.se>");
+MODULE_DESCRIPTION("Winbond w9966cf WebCam driver (0.32)");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.33.1");
+
+#ifdef MODULE
+static char *pardev[] = {[0 ... W9966_MAXCAMS] = ""};
+#else
+static char *pardev[] = {[0 ... W9966_MAXCAMS] = "aggressive"};
+#endif
+module_param_array(pardev, charp, NULL, 0);
+MODULE_PARM_DESC(pardev, "pardev: where to search for\n"
+ "\teach camera. 'aggressive' means brute-force search.\n"
+ "\tEg: >pardev=parport3,aggressive,parport2,parport1< would assign\n"
+ "\tcam 1 to parport3 and search every parport for cam 2 etc...");
+
+static int parmode;
+module_param(parmode, int, 0);
+MODULE_PARM_DESC(parmode, "parmode: transfer mode (0=auto, 1=ecp, 2=epp");
+
+static int video_nr = -1;
+module_param(video_nr, int, 0);
+
+static struct w9966 w9966_cams[W9966_MAXCAMS];
+
+/*
+ * Private function defines
+ */
+
+
+/* Set camera phase flags, so we know what to uninit when terminating */
+static inline void w9966_set_state(struct w9966 *cam, int mask, int val)
+{
+ cam->dev_state = (cam->dev_state & ~mask) ^ val;
+}
+
+/* Get camera phase flags */
+static inline int w9966_get_state(struct w9966 *cam, int mask, int val)
+{
+ return ((cam->dev_state & mask) == val);
+}
+
+/* Claim parport for ourself */
+static void w9966_pdev_claim(struct w9966 *cam)
+{
+ if (w9966_get_state(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED))
+ return;
+ parport_claim_or_block(cam->pdev);
+ w9966_set_state(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED);
+}
+
+/* Release parport for others to use */
+static void w9966_pdev_release(struct w9966 *cam)
+{
+ if (w9966_get_state(cam, W9966_STATE_CLAIMED, 0))
+ return;
+ parport_release(cam->pdev);
+ w9966_set_state(cam, W9966_STATE_CLAIMED, 0);
+}
+
+/* Read register from W9966 interface-chip
+ Expects a claimed pdev
+ -1 on error, else register data (byte) */
+static int w9966_read_reg(struct w9966 *cam, int reg)
+{
+ /* ECP, read, regtransfer, REG, REG, REG, REG, REG */
+ const unsigned char addr = 0x80 | (reg & 0x1f);
+ unsigned char val;
+
+ if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_ADDR) != 0)
+ return -1;
+ if (parport_write(cam->pport, &addr, 1) != 1)
+ return -1;
+ if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_DATA) != 0)
+ return -1;
+ if (parport_read(cam->pport, &val, 1) != 1)
+ return -1;
+
+ return val;
+}
+
+/* Write register to W9966 interface-chip
+ Expects a claimed pdev
+ -1 on error */
+static int w9966_write_reg(struct w9966 *cam, int reg, int data)
+{
+ /* ECP, write, regtransfer, REG, REG, REG, REG, REG */
+ const unsigned char addr = 0xc0 | (reg & 0x1f);
+ const unsigned char val = data;
+
+ if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_ADDR) != 0)
+ return -1;
+ if (parport_write(cam->pport, &addr, 1) != 1)
+ return -1;
+ if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_DATA) != 0)
+ return -1;
+ if (parport_write(cam->pport, &val, 1) != 1)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * Ugly and primitive i2c protocol functions
+ */
+
+/* Sets the data line on the i2c bus.
+ Expects a claimed pdev. */
+static void w9966_i2c_setsda(struct w9966 *cam, int state)
+{
+ if (state)
+ cam->i2c_state |= W9966_I2C_W_DATA;
+ else
+ cam->i2c_state &= ~W9966_I2C_W_DATA;
+
+ w9966_write_reg(cam, 0x18, cam->i2c_state);
+ udelay(5);
+}
+
+/* Get peripheral clock line
+ Expects a claimed pdev. */
+static int w9966_i2c_getscl(struct w9966 *cam)
+{
+ const unsigned char state = w9966_read_reg(cam, 0x18);
+ return ((state & W9966_I2C_R_CLOCK) > 0);
+}
+
+/* Sets the clock line on the i2c bus.
+ Expects a claimed pdev. -1 on error */
+static int w9966_i2c_setscl(struct w9966 *cam, int state)
+{
+ unsigned long timeout;
+
+ if (state)
+ cam->i2c_state |= W9966_I2C_W_CLOCK;
+ else
+ cam->i2c_state &= ~W9966_I2C_W_CLOCK;
+
+ w9966_write_reg(cam, 0x18, cam->i2c_state);
+ udelay(5);
+
+ /* we go to high, we also expect the peripheral to ack. */
+ if (state) {
+ timeout = jiffies + 100;
+ while (!w9966_i2c_getscl(cam)) {
+ if (time_after(jiffies, timeout))
+ return -1;
+ }
+ }
+ return 0;
+}
+
+#if 0
+/* Get peripheral data line
+ Expects a claimed pdev. */
+static int w9966_i2c_getsda(struct w9966 *cam)
+{
+ const unsigned char state = w9966_read_reg(cam, 0x18);
+ return ((state & W9966_I2C_R_DATA) > 0);
+}
+#endif
+
+/* Write a byte with ack to the i2c bus.
+ Expects a claimed pdev. -1 on error */
+static int w9966_i2c_wbyte(struct w9966 *cam, int data)
+{
+ int i;
+
+ for (i = 7; i >= 0; i--) {
+ w9966_i2c_setsda(cam, (data >> i) & 0x01);
+
+ if (w9966_i2c_setscl(cam, 1) == -1)
+ return -1;
+ w9966_i2c_setscl(cam, 0);
+ }
+
+ w9966_i2c_setsda(cam, 1);
+
+ if (w9966_i2c_setscl(cam, 1) == -1)
+ return -1;
+ w9966_i2c_setscl(cam, 0);
+
+ return 0;
+}
+
+/* Read a data byte with ack from the i2c-bus
+ Expects a claimed pdev. -1 on error */
+#if 0
+static int w9966_i2c_rbyte(struct w9966 *cam)
+{
+ unsigned char data = 0x00;
+ int i;
+
+ w9966_i2c_setsda(cam, 1);
+
+ for (i = 0; i < 8; i++) {
+ if (w9966_i2c_setscl(cam, 1) == -1)
+ return -1;
+ data = data << 1;
+ if (w9966_i2c_getsda(cam))
+ data |= 0x01;
+
+ w9966_i2c_setscl(cam, 0);
+ }
+ return data;
+}
+#endif
+
+/* Read a register from the i2c device.
+ Expects claimed pdev. -1 on error */
+#if 0
+static int w9966_read_reg_i2c(struct w9966 *cam, int reg)
+{
+ int data;
+
+ w9966_i2c_setsda(cam, 0);
+ w9966_i2c_setscl(cam, 0);
+
+ if (w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 ||
+ w9966_i2c_wbyte(cam, reg) == -1)
+ return -1;
+
+ w9966_i2c_setsda(cam, 1);
+ if (w9966_i2c_setscl(cam, 1) == -1)
+ return -1;
+ w9966_i2c_setsda(cam, 0);
+ w9966_i2c_setscl(cam, 0);
+
+ if (w9966_i2c_wbyte(cam, W9966_I2C_R_ID) == -1)
+ return -1;
+ data = w9966_i2c_rbyte(cam);
+ if (data == -1)
+ return -1;
+
+ w9966_i2c_setsda(cam, 0);
+
+ if (w9966_i2c_setscl(cam, 1) == -1)
+ return -1;
+ w9966_i2c_setsda(cam, 1);
+
+ return data;
+}
+#endif
+
+/* Write a register to the i2c device.
+ Expects claimed pdev. -1 on error */
+static int w9966_write_reg_i2c(struct w9966 *cam, int reg, int data)
+{
+ w9966_i2c_setsda(cam, 0);
+ w9966_i2c_setscl(cam, 0);
+
+ if (w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 ||
+ w9966_i2c_wbyte(cam, reg) == -1 ||
+ w9966_i2c_wbyte(cam, data) == -1)
+ return -1;
+
+ w9966_i2c_setsda(cam, 0);
+ if (w9966_i2c_setscl(cam, 1) == -1)
+ return -1;
+
+ w9966_i2c_setsda(cam, 1);
+
+ return 0;
+}
+
+/* Find a good length for capture window (used both for W and H)
+ A bit ugly but pretty functional. The capture length
+ have to match the downscale */
+static int w9966_findlen(int near, int size, int maxlen)
+{
+ int bestlen = size;
+ int besterr = abs(near - bestlen);
+ int len;
+
+ for (len = size + 1; len < maxlen; len++) {
+ int err;
+ if (((64 * size) % len) != 0)
+ continue;
+
+ err = abs(near - len);
+
+ /* Only continue as long as we keep getting better values */
+ if (err > besterr)
+ break;
+
+ besterr = err;
+ bestlen = len;
+ }
+
+ return bestlen;
+}
+
+/* Modify capture window (if necessary)
+ and calculate downscaling
+ Return -1 on error */
+static int w9966_calcscale(int size, int min, int max, int *beg, int *end, unsigned char *factor)
+{
+ int maxlen = max - min;
+ int len = *end - *beg + 1;
+ int newlen = w9966_findlen(len, size, maxlen);
+ int err = newlen - len;
+
+ /* Check for bad format */
+ if (newlen > maxlen || newlen < size)
+ return -1;
+
+ /* Set factor (6 bit fixed) */
+ *factor = (64 * size) / newlen;
+ if (*factor == 64)
+ *factor = 0x00; /* downscale is disabled */
+ else
+ *factor |= 0x80; /* set downscale-enable bit */
+
+ /* Modify old beginning and end */
+ *beg -= err / 2;
+ *end += err - (err / 2);
+
+ /* Move window if outside borders */
+ if (*beg < min) {
+ *end += min - *beg;
+ *beg += min - *beg;
+ }
+ if (*end > max) {
+ *beg -= *end - max;
+ *end -= *end - max;
+ }
+
+ return 0;
+}
+
+/* Setup the cameras capture window etc.
+ Expects a claimed pdev
+ return -1 on error */
+static int w9966_setup(struct w9966 *cam, int x1, int y1, int x2, int y2, int w, int h)
+{
+ unsigned int i;
+ unsigned int enh_s, enh_e;
+ unsigned char scale_x, scale_y;
+ unsigned char regs[0x1c];
+ unsigned char saa7111_regs[] = {
+ 0x21, 0x00, 0xd8, 0x23, 0x00, 0x80, 0x80, 0x00,
+ 0x88, 0x10, 0x80, 0x40, 0x40, 0x00, 0x01, 0x00,
+ 0x48, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x71, 0xe7, 0x00, 0x00, 0xc0
+ };
+
+
+ if (w * h * 2 > W9966_SRAMSIZE) {
+ DPRINTF("capture window exceeds SRAM size!.\n");
+ w = 200; h = 160; /* Pick default values */
+ }
+
+ w &= ~0x1;
+ if (w < 2)
+ w = 2;
+ if (h < 1)
+ h = 1;
+ if (w > W9966_WND_MAX_W)
+ w = W9966_WND_MAX_W;
+ if (h > W9966_WND_MAX_H)
+ h = W9966_WND_MAX_H;
+
+ cam->width = w;
+ cam->height = h;
+
+ enh_s = 0;
+ enh_e = w * h * 2;
+
+ /* Modify capture window if necessary and calculate downscaling */
+ if (w9966_calcscale(w, W9966_WND_MIN_X, W9966_WND_MAX_X, &x1, &x2, &scale_x) != 0 ||
+ w9966_calcscale(h, W9966_WND_MIN_Y, W9966_WND_MAX_Y, &y1, &y2, &scale_y) != 0)
+ return -1;
+
+ DPRINTF("%dx%d, x: %d<->%d, y: %d<->%d, sx: %d/64, sy: %d/64.\n",
+ w, h, x1, x2, y1, y2, scale_x & ~0x80, scale_y & ~0x80);
+
+ /* Setup registers */
+ regs[0x00] = 0x00; /* Set normal operation */
+ regs[0x01] = 0x18; /* Capture mode */
+ regs[0x02] = scale_y; /* V-scaling */
+ regs[0x03] = scale_x; /* H-scaling */
+
+ /* Capture window */
+ regs[0x04] = (x1 & 0x0ff); /* X-start (8 low bits) */
+ regs[0x05] = (x1 & 0x300)>>8; /* X-start (2 high bits) */
+ regs[0x06] = (y1 & 0x0ff); /* Y-start (8 low bits) */
+ regs[0x07] = (y1 & 0x300)>>8; /* Y-start (2 high bits) */
+ regs[0x08] = (x2 & 0x0ff); /* X-end (8 low bits) */
+ regs[0x09] = (x2 & 0x300)>>8; /* X-end (2 high bits) */
+ regs[0x0a] = (y2 & 0x0ff); /* Y-end (8 low bits) */
+
+ regs[0x0c] = W9966_SRAMID; /* SRAM-banks (1x 128kb) */
+
+ /* Enhancement layer */
+ regs[0x0d] = (enh_s & 0x000ff); /* Enh. start (0-7) */
+ regs[0x0e] = (enh_s & 0x0ff00) >> 8; /* Enh. start (8-15) */
+ regs[0x0f] = (enh_s & 0x70000) >> 16; /* Enh. start (16-17/18??) */
+ regs[0x10] = (enh_e & 0x000ff); /* Enh. end (0-7) */
+ regs[0x11] = (enh_e & 0x0ff00) >> 8; /* Enh. end (8-15) */
+ regs[0x12] = (enh_e & 0x70000) >> 16; /* Enh. end (16-17/18??) */
+
+ /* Misc */
+ regs[0x13] = 0x40; /* VEE control (raw 4:2:2) */
+ regs[0x17] = 0x00; /* ??? */
+ regs[0x18] = cam->i2c_state = 0x00; /* Serial bus */
+ regs[0x19] = 0xff; /* I/O port direction control */
+ regs[0x1a] = 0xff; /* I/O port data register */
+ regs[0x1b] = 0x10; /* ??? */
+
+ /* SAA7111 chip settings */
+ saa7111_regs[0x0a] = cam->brightness;
+ saa7111_regs[0x0b] = cam->contrast;
+ saa7111_regs[0x0c] = cam->color;
+ saa7111_regs[0x0d] = cam->hue;
+
+ /* Reset (ECP-fifo & serial-bus) */
+ if (w9966_write_reg(cam, 0x00, 0x03) == -1)
+ return -1;
+
+ /* Write regs to w9966cf chip */
+ for (i = 0; i < 0x1c; i++)
+ if (w9966_write_reg(cam, i, regs[i]) == -1)
+ return -1;
+
+ /* Write regs to saa7111 chip */
+ for (i = 0; i < 0x20; i++)
+ if (w9966_write_reg_i2c(cam, i, saa7111_regs[i]) == -1)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * Video4linux interfacing
+ */
+
+static int cam_querycap(struct file *file, void *priv,
+ struct v4l2_capability *vcap)
+{
+ struct w9966 *cam = video_drvdata(file);
+
+ strlcpy(vcap->driver, cam->v4l2_dev.name, sizeof(vcap->driver));
+ strlcpy(vcap->card, W9966_DRIVERNAME, sizeof(vcap->card));
+ strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info));
+ vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
+ return 0;
+}
+
+static int cam_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
+{
+ if (vin->index > 0)
+ return -EINVAL;
+ strlcpy(vin->name, "Camera", sizeof(vin->name));
+ vin->type = V4L2_INPUT_TYPE_CAMERA;
+ vin->audioset = 0;
+ vin->tuner = 0;
+ vin->std = 0;
+ vin->status = 0;
+ return 0;
+}
+
+static int cam_g_input(struct file *file, void *fh, unsigned int *inp)
+{
+ *inp = 0;
+ return 0;
+}
+
+static int cam_s_input(struct file *file, void *fh, unsigned int inp)
+{
+ return (inp > 0) ? -EINVAL : 0;
+}
+
+static int cam_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
+ case V4L2_CID_CONTRAST:
+ return v4l2_ctrl_query_fill(qc, -64, 64, 1, 64);
+ case V4L2_CID_SATURATION:
+ return v4l2_ctrl_query_fill(qc, -64, 64, 1, 64);
+ case V4L2_CID_HUE:
+ return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int cam_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct w9966 *cam = video_drvdata(file);
+ int ret = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = cam->brightness;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->value = cam->contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ ctrl->value = cam->color;
+ break;
+ case V4L2_CID_HUE:
+ ctrl->value = cam->hue;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int cam_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct w9966 *cam = video_drvdata(file);
+ int ret = 0;
+
+ mutex_lock(&cam->lock);
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ cam->brightness = ctrl->value;
+ break;
+ case V4L2_CID_CONTRAST:
+ cam->contrast = ctrl->value;
+ break;
+ case V4L2_CID_SATURATION:
+ cam->color = ctrl->value;
+ break;
+ case V4L2_CID_HUE:
+ cam->hue = ctrl->value;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret == 0) {
+ w9966_pdev_claim(cam);
+
+ if (w9966_write_reg_i2c(cam, 0x0a, cam->brightness) == -1 ||
+ w9966_write_reg_i2c(cam, 0x0b, cam->contrast) == -1 ||
+ w9966_write_reg_i2c(cam, 0x0c, cam->color) == -1 ||
+ w9966_write_reg_i2c(cam, 0x0d, cam->hue) == -1) {
+ ret = -EIO;
+ }
+
+ w9966_pdev_release(cam);
+ }
+ mutex_unlock(&cam->lock);
+ return ret;
+}
+
+static int cam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct w9966 *cam = video_drvdata(file);
+ struct v4l2_pix_format *pix = &fmt->fmt.pix;
+
+ pix->width = cam->width;
+ pix->height = cam->height;
+ pix->pixelformat = V4L2_PIX_FMT_YUYV;
+ pix->field = V4L2_FIELD_NONE;
+ pix->bytesperline = 2 * cam->width;
+ pix->sizeimage = 2 * cam->width * cam->height;
+ /* Just a guess */
+ pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ return 0;
+}
+
+static int cam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct v4l2_pix_format *pix = &fmt->fmt.pix;
+
+ if (pix->width < 2)
+ pix->width = 2;
+ if (pix->height < 1)
+ pix->height = 1;
+ if (pix->width > W9966_WND_MAX_W)
+ pix->width = W9966_WND_MAX_W;
+ if (pix->height > W9966_WND_MAX_H)
+ pix->height = W9966_WND_MAX_H;
+ pix->pixelformat = V4L2_PIX_FMT_YUYV;
+ pix->field = V4L2_FIELD_NONE;
+ pix->bytesperline = 2 * pix->width;
+ pix->sizeimage = 2 * pix->width * pix->height;
+ /* Just a guess */
+ pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ return 0;
+}
+
+static int cam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct w9966 *cam = video_drvdata(file);
+ struct v4l2_pix_format *pix = &fmt->fmt.pix;
+ int ret = cam_try_fmt_vid_cap(file, fh, fmt);
+
+ if (ret)
+ return ret;
+
+ mutex_lock(&cam->lock);
+ /* Update camera regs */
+ w9966_pdev_claim(cam);
+ ret = w9966_setup(cam, 0, 0, 1023, 1023, pix->width, pix->height);
+ w9966_pdev_release(cam);
+ mutex_unlock(&cam->lock);
+ return ret;
+}
+
+static int cam_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
+{
+ static struct v4l2_fmtdesc formats[] = {
+ { 0, 0, 0,
+ "YUV 4:2:2", V4L2_PIX_FMT_YUYV,
+ { 0, 0, 0, 0 }
+ },
+ };
+ enum v4l2_buf_type type = fmt->type;
+
+ if (fmt->index > 0)
+ return -EINVAL;
+
+ *fmt = formats[fmt->index];
+ fmt->type = type;
+ return 0;
+}
+
+/* Capture data */
+static ssize_t w9966_v4l_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct w9966 *cam = video_drvdata(file);
+ unsigned char addr = 0xa0; /* ECP, read, CCD-transfer, 00000 */
+ unsigned char __user *dest = (unsigned char __user *)buf;
+ unsigned long dleft = count;
+ unsigned char *tbuf;
+
+ /* Why would anyone want more than this?? */
+ if (count > cam->width * cam->height * 2)
+ return -EINVAL;
+
+ mutex_lock(&cam->lock);
+ w9966_pdev_claim(cam);
+ w9966_write_reg(cam, 0x00, 0x02); /* Reset ECP-FIFO buffer */
+ w9966_write_reg(cam, 0x00, 0x00); /* Return to normal operation */
+ w9966_write_reg(cam, 0x01, 0x98); /* Enable capture */
+
+ /* write special capture-addr and negotiate into data transfer */
+ if ((parport_negotiate(cam->pport, cam->ppmode|IEEE1284_ADDR) != 0) ||
+ (parport_write(cam->pport, &addr, 1) != 1) ||
+ (parport_negotiate(cam->pport, cam->ppmode|IEEE1284_DATA) != 0)) {
+ w9966_pdev_release(cam);
+ mutex_unlock(&cam->lock);
+ return -EFAULT;
+ }
+
+ tbuf = kmalloc(W9966_RBUFFER, GFP_KERNEL);
+ if (tbuf == NULL) {
+ count = -ENOMEM;
+ goto out;
+ }
+
+ while (dleft > 0) {
+ unsigned long tsize = (dleft > W9966_RBUFFER) ? W9966_RBUFFER : dleft;
+
+ if (parport_read(cam->pport, tbuf, tsize) < tsize) {
+ count = -EFAULT;
+ goto out;
+ }
+ if (copy_to_user(dest, tbuf, tsize) != 0) {
+ count = -EFAULT;
+ goto out;
+ }
+ dest += tsize;
+ dleft -= tsize;
+ }
+
+ w9966_write_reg(cam, 0x01, 0x18); /* Disable capture */
+
+out:
+ kfree(tbuf);
+ w9966_pdev_release(cam);
+ mutex_unlock(&cam->lock);
+
+ return count;
+}
+
+static const struct v4l2_file_operations w9966_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = video_ioctl2,
+ .read = w9966_v4l_read,
+};
+
+static const struct v4l2_ioctl_ops w9966_ioctl_ops = {
+ .vidioc_querycap = cam_querycap,
+ .vidioc_g_input = cam_g_input,
+ .vidioc_s_input = cam_s_input,
+ .vidioc_enum_input = cam_enum_input,
+ .vidioc_queryctrl = cam_queryctrl,
+ .vidioc_g_ctrl = cam_g_ctrl,
+ .vidioc_s_ctrl = cam_s_ctrl,
+ .vidioc_enum_fmt_vid_cap = cam_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = cam_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = cam_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = cam_try_fmt_vid_cap,
+};
+
+
+/* Initialize camera device. Setup all internal flags, set a
+ default video mode, setup ccd-chip, register v4l device etc..
+ Also used for 'probing' of hardware.
+ -1 on error */
+static int w9966_init(struct w9966 *cam, struct parport *port)
+{
+ struct v4l2_device *v4l2_dev = &cam->v4l2_dev;
+
+ if (cam->dev_state != 0)
+ return -1;
+
+ strlcpy(v4l2_dev->name, "w9966", sizeof(v4l2_dev->name));
+
+ if (v4l2_device_register(NULL, v4l2_dev) < 0) {
+ v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
+ return -1;
+ }
+ cam->pport = port;
+ cam->brightness = 128;
+ cam->contrast = 64;
+ cam->color = 64;
+ cam->hue = 0;
+
+ /* Select requested transfer mode */
+ switch (parmode) {
+ default: /* Auto-detect (priority: hw-ecp, hw-epp, sw-ecp) */
+ case 0:
+ if (port->modes & PARPORT_MODE_ECP)
+ cam->ppmode = IEEE1284_MODE_ECP;
+ else if (port->modes & PARPORT_MODE_EPP)
+ cam->ppmode = IEEE1284_MODE_EPP;
+ else
+ cam->ppmode = IEEE1284_MODE_ECP;
+ break;
+ case 1: /* hw- or sw-ecp */
+ cam->ppmode = IEEE1284_MODE_ECP;
+ break;
+ case 2: /* hw- or sw-epp */
+ cam->ppmode = IEEE1284_MODE_EPP;
+ break;
+ }
+
+ /* Tell the parport driver that we exists */
+ cam->pdev = parport_register_device(port, "w9966", NULL, NULL, NULL, 0, NULL);
+ if (cam->pdev == NULL) {
+ DPRINTF("parport_register_device() failed\n");
+ return -1;
+ }
+ w9966_set_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV);
+
+ w9966_pdev_claim(cam);
+
+ /* Setup a default capture mode */
+ if (w9966_setup(cam, 0, 0, 1023, 1023, 200, 160) != 0) {
+ DPRINTF("w9966_setup() failed.\n");
+ return -1;
+ }
+
+ w9966_pdev_release(cam);
+
+ /* Fill in the video_device struct and register us to v4l */
+ strlcpy(cam->vdev.name, W9966_DRIVERNAME, sizeof(cam->vdev.name));
+ cam->vdev.v4l2_dev = v4l2_dev;
+ cam->vdev.fops = &w9966_fops;
+ cam->vdev.ioctl_ops = &w9966_ioctl_ops;
+ cam->vdev.release = video_device_release_empty;
+ video_set_drvdata(&cam->vdev, cam);
+
+ mutex_init(&cam->lock);
+
+ if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0)
+ return -1;
+
+ w9966_set_state(cam, W9966_STATE_VDEV, W9966_STATE_VDEV);
+
+ /* All ok */
+ v4l2_info(v4l2_dev, "Found and initialized a webcam on %s.\n",
+ cam->pport->name);
+ return 0;
+}
+
+
+/* Terminate everything gracefully */
+static void w9966_term(struct w9966 *cam)
+{
+ /* Unregister from v4l */
+ if (w9966_get_state(cam, W9966_STATE_VDEV, W9966_STATE_VDEV)) {
+ video_unregister_device(&cam->vdev);
+ w9966_set_state(cam, W9966_STATE_VDEV, 0);
+ }
+
+ /* Terminate from IEEE1284 mode and release pdev block */
+ if (w9966_get_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) {
+ w9966_pdev_claim(cam);
+ parport_negotiate(cam->pport, IEEE1284_MODE_COMPAT);
+ w9966_pdev_release(cam);
+ }
+
+ /* Unregister from parport */
+ if (w9966_get_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) {
+ parport_unregister_device(cam->pdev);
+ w9966_set_state(cam, W9966_STATE_PDEV, 0);
+ }
+ memset(cam, 0, sizeof(*cam));
+}
+
+
+/* Called once for every parport on init */
+static void w9966_attach(struct parport *port)
+{
+ int i;
+
+ for (i = 0; i < W9966_MAXCAMS; i++) {
+ if (w9966_cams[i].dev_state != 0) /* Cam is already assigned */
+ continue;
+ if (strcmp(pardev[i], "aggressive") == 0 || strcmp(pardev[i], port->name) == 0) {
+ if (w9966_init(&w9966_cams[i], port) != 0)
+ w9966_term(&w9966_cams[i]);
+ break; /* return */
+ }
+ }
+}
+
+/* Called once for every parport on termination */
+static void w9966_detach(struct parport *port)
+{
+ int i;
+
+ for (i = 0; i < W9966_MAXCAMS; i++)
+ if (w9966_cams[i].dev_state != 0 && w9966_cams[i].pport == port)
+ w9966_term(&w9966_cams[i]);
+}
+
+
+static struct parport_driver w9966_ppd = {
+ .name = W9966_DRIVERNAME,
+ .attach = w9966_attach,
+ .detach = w9966_detach,
+};
+
+/* Module entry point */
+static int __init w9966_mod_init(void)
+{
+ int i;
+
+ for (i = 0; i < W9966_MAXCAMS; i++)
+ w9966_cams[i].dev_state = 0;
+
+ return parport_register_driver(&w9966_ppd);
+}
+
+/* Module cleanup */
+static void __exit w9966_mod_term(void)
+{
+ parport_unregister_driver(&w9966_ppd);
+}
+
+module_init(w9966_mod_init);
+module_exit(w9966_mod_term);
diff --git a/drivers/media/video/wm8739.c b/drivers/media/video/wm8739.c
new file mode 100644
index 00000000..3bb99e93
--- /dev/null
+++ b/drivers/media/video/wm8739.c
@@ -0,0 +1,294 @@
+/*
+ * wm8739
+ *
+ * Copyright (C) 2005 T. Adachi <tadachi@tadachi-net.com>
+ *
+ * Copyright (C) 2005 Hans Verkuil <hverkuil@xs4all.nl>
+ * - Cleanup
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/ioctl.h>
+#include <asm/uaccess.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+
+MODULE_DESCRIPTION("wm8739 driver");
+MODULE_AUTHOR("T. Adachi, Hans Verkuil");
+MODULE_LICENSE("GPL");
+
+static int debug;
+
+module_param(debug, int, 0644);
+
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+
+/* ------------------------------------------------------------------------ */
+
+enum {
+ R0 = 0, R1,
+ R5 = 5, R6, R7, R8, R9, R15 = 15,
+ TOT_REGS
+};
+
+struct wm8739_state {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+ struct {
+ /* audio cluster */
+ struct v4l2_ctrl *volume;
+ struct v4l2_ctrl *mute;
+ struct v4l2_ctrl *balance;
+ };
+ u32 clock_freq;
+};
+
+static inline struct wm8739_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct wm8739_state, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct wm8739_state, hdl)->sd;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int wm8739_write(struct v4l2_subdev *sd, int reg, u16 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int i;
+
+ if (reg < 0 || reg >= TOT_REGS) {
+ v4l2_err(sd, "Invalid register R%d\n", reg);
+ return -1;
+ }
+
+ v4l2_dbg(1, debug, sd, "write: %02x %02x\n", reg, val);
+
+ for (i = 0; i < 3; i++)
+ if (i2c_smbus_write_byte_data(client,
+ (reg << 1) | (val >> 8), val & 0xff) == 0)
+ return 0;
+ v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg);
+ return -1;
+}
+
+static int wm8739_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ struct wm8739_state *state = to_state(sd);
+ unsigned int work_l, work_r;
+ u8 vol_l; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */
+ u8 vol_r; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */
+ u16 mute;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* normalize ( 65535 to 0 -> 31 to 0 (12dB to -34.5dB) ) */
+ work_l = (min(65536 - state->balance->val, 32768) * state->volume->val) / 32768;
+ work_r = (min(state->balance->val, 32768) * state->volume->val) / 32768;
+
+ vol_l = (long)work_l * 31 / 65535;
+ vol_r = (long)work_r * 31 / 65535;
+
+ /* set audio volume etc. */
+ mute = state->mute->val ? 0x80 : 0;
+
+ /* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB
+ * Default setting: 0x17 = 0 dB
+ */
+ wm8739_write(sd, R0, (vol_l & 0x1f) | mute);
+ wm8739_write(sd, R1, (vol_r & 0x1f) | mute);
+ return 0;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int wm8739_s_clock_freq(struct v4l2_subdev *sd, u32 audiofreq)
+{
+ struct wm8739_state *state = to_state(sd);
+
+ state->clock_freq = audiofreq;
+ /* de-activate */
+ wm8739_write(sd, R9, 0x000);
+ switch (audiofreq) {
+ case 44100:
+ /* 256fps, fs=44.1k */
+ wm8739_write(sd, R8, 0x020);
+ break;
+ case 48000:
+ /* 256fps, fs=48k */
+ wm8739_write(sd, R8, 0x000);
+ break;
+ case 32000:
+ /* 256fps, fs=32k */
+ wm8739_write(sd, R8, 0x018);
+ break;
+ default:
+ break;
+ }
+ /* activate */
+ wm8739_write(sd, R9, 0x001);
+ return 0;
+}
+
+static int wm8739_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_WM8739, 0);
+}
+
+static int wm8739_log_status(struct v4l2_subdev *sd)
+{
+ struct wm8739_state *state = to_state(sd);
+
+ v4l2_info(sd, "Frequency: %u Hz\n", state->clock_freq);
+ v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_ctrl_ops wm8739_ctrl_ops = {
+ .s_ctrl = wm8739_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops wm8739_core_ops = {
+ .log_status = wm8739_log_status,
+ .g_chip_ident = wm8739_g_chip_ident,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+};
+
+static const struct v4l2_subdev_audio_ops wm8739_audio_ops = {
+ .s_clock_freq = wm8739_s_clock_freq,
+};
+
+static const struct v4l2_subdev_ops wm8739_ops = {
+ .core = &wm8739_core_ops,
+ .audio = &wm8739_audio_ops,
+};
+
+/* ------------------------------------------------------------------------ */
+
+/* i2c implementation */
+
+static int wm8739_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct wm8739_state *state;
+ struct v4l2_subdev *sd;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ state = kzalloc(sizeof(struct wm8739_state), GFP_KERNEL);
+ if (state == NULL)
+ return -ENOMEM;
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &wm8739_ops);
+ v4l2_ctrl_handler_init(&state->hdl, 2);
+ state->volume = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops,
+ V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 50736);
+ state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+ state->balance = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops,
+ V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768);
+ sd->ctrl_handler = &state->hdl;
+ if (state->hdl.error) {
+ int err = state->hdl.error;
+
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ return err;
+ }
+ v4l2_ctrl_cluster(3, &state->volume);
+
+ state->clock_freq = 48000;
+
+ /* Initialize wm8739 */
+
+ /* reset */
+ wm8739_write(sd, R15, 0x00);
+ /* filter setting, high path, offet clear */
+ wm8739_write(sd, R5, 0x000);
+ /* ADC, OSC, Power Off mode Disable */
+ wm8739_write(sd, R6, 0x000);
+ /* Digital Audio interface format:
+ Enable Master mode, 24 bit, MSB first/left justified */
+ wm8739_write(sd, R7, 0x049);
+ /* sampling control: normal, 256fs, 48KHz sampling rate */
+ wm8739_write(sd, R8, 0x000);
+ /* activate */
+ wm8739_write(sd, R9, 0x001);
+ /* set volume/mute */
+ v4l2_ctrl_handler_setup(&state->hdl);
+ return 0;
+}
+
+static int wm8739_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct wm8739_state *state = to_state(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(to_state(sd));
+ return 0;
+}
+
+static const struct i2c_device_id wm8739_id[] = {
+ { "wm8739", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8739_id);
+
+static struct i2c_driver wm8739_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "wm8739",
+ },
+ .probe = wm8739_probe,
+ .remove = wm8739_remove,
+ .id_table = wm8739_id,
+};
+
+module_i2c_driver(wm8739_driver);
diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c
new file mode 100644
index 00000000..bee77ea9
--- /dev/null
+++ b/drivers/media/video/wm8775.c
@@ -0,0 +1,342 @@
+/*
+ * wm8775 - driver version 0.0.1
+ *
+ * Copyright (C) 2004 Ulf Eklund <ivtv at eklund.to>
+ *
+ * Based on saa7115 driver
+ *
+ * Copyright (C) 2005 Hans Verkuil <hverkuil@xs4all.nl>
+ * - Cleanup
+ * - V4L2 API update
+ * - sound fixes
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/ioctl.h>
+#include <asm/uaccess.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+#include <media/wm8775.h>
+
+MODULE_DESCRIPTION("wm8775 driver");
+MODULE_AUTHOR("Ulf Eklund, Hans Verkuil");
+MODULE_LICENSE("GPL");
+
+
+
+/* ----------------------------------------------------------------------- */
+
+enum {
+ R7 = 7, R11 = 11,
+ R12, R13, R14, R15, R16, R17, R18, R19, R20, R21, R23 = 23,
+ TOT_REGS
+};
+
+#define ALC_HOLD 0x85 /* R17: use zero cross detection, ALC hold time 42.6 ms */
+#define ALC_EN 0x100 /* R17: ALC enable */
+
+struct wm8775_state {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+ struct v4l2_ctrl *mute;
+ struct v4l2_ctrl *vol;
+ struct v4l2_ctrl *bal;
+ struct v4l2_ctrl *loud;
+ u8 input; /* Last selected input (0-0xf) */
+};
+
+static inline struct wm8775_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct wm8775_state, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct wm8775_state, hdl)->sd;
+}
+
+static int wm8775_write(struct v4l2_subdev *sd, int reg, u16 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int i;
+
+ if (reg < 0 || reg >= TOT_REGS) {
+ v4l2_err(sd, "Invalid register R%d\n", reg);
+ return -1;
+ }
+
+ for (i = 0; i < 3; i++)
+ if (i2c_smbus_write_byte_data(client,
+ (reg << 1) | (val >> 8), val & 0xff) == 0)
+ return 0;
+ v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg);
+ return -1;
+}
+
+static void wm8775_set_audio(struct v4l2_subdev *sd, int quietly)
+{
+ struct wm8775_state *state = to_state(sd);
+ u8 vol_l, vol_r;
+ int muted = 0 != state->mute->val;
+ u16 volume = (u16)state->vol->val;
+ u16 balance = (u16)state->bal->val;
+
+ /* normalize ( 65535 to 0 -> 255 to 0 (+24dB to -103dB) ) */
+ vol_l = (min(65536 - balance, 32768) * volume) >> 23;
+ vol_r = (min(balance, (u16)32768) * volume) >> 23;
+
+ /* Mute */
+ if (muted || quietly)
+ wm8775_write(sd, R21, 0x0c0 | state->input);
+
+ wm8775_write(sd, R14, vol_l | 0x100); /* 0x100= Left channel ADC zero cross enable */
+ wm8775_write(sd, R15, vol_r | 0x100); /* 0x100= Right channel ADC zero cross enable */
+
+ /* Un-mute */
+ if (!muted)
+ wm8775_write(sd, R21, state->input);
+}
+
+static int wm8775_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct wm8775_state *state = to_state(sd);
+
+ /* There are 4 inputs and one output. Zero or more inputs
+ are multiplexed together to the output. Hence there are
+ 16 combinations.
+ If only one input is active (the normal case) then the
+ input values 1, 2, 4 or 8 should be used. */
+ if (input > 15) {
+ v4l2_err(sd, "Invalid input %d.\n", input);
+ return -EINVAL;
+ }
+ state->input = input;
+ if (!v4l2_ctrl_g_ctrl(state->mute))
+ return 0;
+ if (!v4l2_ctrl_g_ctrl(state->vol))
+ return 0;
+ if (!v4l2_ctrl_g_ctrl(state->bal))
+ return 0;
+ wm8775_set_audio(sd, 1);
+ return 0;
+}
+
+static int wm8775_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_BALANCE:
+ wm8775_set_audio(sd, 0);
+ return 0;
+ case V4L2_CID_AUDIO_LOUDNESS:
+ wm8775_write(sd, R17, (ctrl->val ? ALC_EN : 0) | ALC_HOLD);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int wm8775_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_WM8775, 0);
+}
+
+static int wm8775_log_status(struct v4l2_subdev *sd)
+{
+ struct wm8775_state *state = to_state(sd);
+
+ v4l2_info(sd, "Input: %d\n", state->input);
+ v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
+ return 0;
+}
+
+static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
+{
+ wm8775_set_audio(sd, 0);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_ctrl_ops wm8775_ctrl_ops = {
+ .s_ctrl = wm8775_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops wm8775_core_ops = {
+ .log_status = wm8775_log_status,
+ .g_chip_ident = wm8775_g_chip_ident,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+};
+
+static const struct v4l2_subdev_tuner_ops wm8775_tuner_ops = {
+ .s_frequency = wm8775_s_frequency,
+};
+
+static const struct v4l2_subdev_audio_ops wm8775_audio_ops = {
+ .s_routing = wm8775_s_routing,
+};
+
+static const struct v4l2_subdev_ops wm8775_ops = {
+ .core = &wm8775_core_ops,
+ .tuner = &wm8775_tuner_ops,
+ .audio = &wm8775_audio_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+/* i2c implementation */
+
+/*
+ * Generic i2c probe
+ * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
+ */
+
+static int wm8775_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct wm8775_state *state;
+ struct v4l2_subdev *sd;
+ int err;
+ bool is_nova_s = false;
+
+ if (client->dev.platform_data) {
+ struct wm8775_platform_data *data = client->dev.platform_data;
+ is_nova_s = data->is_nova_s;
+ }
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ v4l_info(client, "chip found @ 0x%02x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ state = kzalloc(sizeof(struct wm8775_state), GFP_KERNEL);
+ if (state == NULL)
+ return -ENOMEM;
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &wm8775_ops);
+ state->input = 2;
+
+ v4l2_ctrl_handler_init(&state->hdl, 4);
+ state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+ state->vol = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
+ V4L2_CID_AUDIO_VOLUME, 0, 65535, (65535+99)/100, 0xCF00); /* 0dB*/
+ state->bal = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
+ V4L2_CID_AUDIO_BALANCE, 0, 65535, (65535+99)/100, 32768);
+ state->loud = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
+ V4L2_CID_AUDIO_LOUDNESS, 0, 1, 1, 1);
+ sd->ctrl_handler = &state->hdl;
+ err = state->hdl.error;
+ if (err) {
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ return err;
+ }
+
+ /* Initialize wm8775 */
+
+ /* RESET */
+ wm8775_write(sd, R23, 0x000);
+ /* Disable zero cross detect timeout */
+ wm8775_write(sd, R7, 0x000);
+ /* HPF enable, left justified, 24-bit (Philips) mode */
+ wm8775_write(sd, R11, 0x021);
+ /* Master mode, clock ratio 256fs */
+ wm8775_write(sd, R12, 0x102);
+ /* Powered up */
+ wm8775_write(sd, R13, 0x000);
+
+ if (!is_nova_s) {
+ /* ADC gain +2.5dB, enable zero cross */
+ wm8775_write(sd, R14, 0x1d4);
+ /* ADC gain +2.5dB, enable zero cross */
+ wm8775_write(sd, R15, 0x1d4);
+ /* ALC Stereo, ALC target level -1dB FS max gain +8dB */
+ wm8775_write(sd, R16, 0x1bf);
+ /* Enable gain control, use zero cross detection,
+ ALC hold time 42.6 ms */
+ wm8775_write(sd, R17, 0x185);
+ } else {
+ /* ALC stereo, ALC target level -5dB FS, ALC max gain +8dB */
+ wm8775_write(sd, R16, 0x1bb);
+ /* Set ALC mode and hold time */
+ wm8775_write(sd, R17, (state->loud->val ? ALC_EN : 0) | ALC_HOLD);
+ }
+ /* ALC gain ramp up delay 34 s, ALC gain ramp down delay 33 ms */
+ wm8775_write(sd, R18, 0x0a2);
+ /* Enable noise gate, threshold -72dBfs */
+ wm8775_write(sd, R19, 0x005);
+ if (!is_nova_s) {
+ /* Transient window 4ms, lower PGA gain limit -1dB */
+ wm8775_write(sd, R20, 0x07a);
+ /* LRBOTH = 1, use input 2. */
+ wm8775_write(sd, R21, 0x102);
+ } else {
+ /* Transient window 4ms, ALC min gain -5dB */
+ wm8775_write(sd, R20, 0x0fb);
+
+ wm8775_set_audio(sd, 1); /* set volume/mute/mux */
+ }
+ return 0;
+}
+
+static int wm8775_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct wm8775_state *state = to_state(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ return 0;
+}
+
+static const struct i2c_device_id wm8775_id[] = {
+ { "wm8775", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8775_id);
+
+static struct i2c_driver wm8775_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "wm8775",
+ },
+ .probe = wm8775_probe,
+ .remove = wm8775_remove,
+ .id_table = wm8775_id,
+};
+
+module_i2c_driver(wm8775_driver);
diff --git a/drivers/media/video/wmt/Kconfig b/drivers/media/video/wmt/Kconfig
new file mode 100755
index 00000000..48f69397
--- /dev/null
+++ b/drivers/media/video/wmt/Kconfig
@@ -0,0 +1,19 @@
+#
+# Multimedia Video device configuration
+#
+
+config WMT_VIDEO_LOCK
+ tristate "WonderMedia Video Lock"
+ ---help---
+ Say Y here to enable Video Lock
+
+config WMT_VIDEO_DECODE_JPEG
+ tristate "WonderMedia Hardware JPEG decoder"
+ ---help---
+ Say Y here to use hardware to decode JPEG files
+
+config WMT_VIDEO_DECODE_MSVD
+ tristate "WonderMedia Hardware MSVD decoder"
+ ---help---
+ Say Y here to use hardware to decode MSVD files
+ \ No newline at end of file
diff --git a/drivers/media/video/wmt/Makefile b/drivers/media/video/wmt/Makefile
new file mode 100755
index 00000000..3e02cf24
--- /dev/null
+++ b/drivers/media/video/wmt/Makefile
@@ -0,0 +1,13 @@
+#
+# Makefile for the video capture/playback device drivers.
+#
+
+g_wmt_vd_com-y := wmt-vd.o wmt-codec.o
+g_wmt_lock-y := wmt-lock.o
+g_wmt_msvd-y := msvd/vpu.o
+g_wmt_vd_jdec-y := jpeg/hw-jdec.o jpeg/wmt-jdec.o
+
+obj-$(CONFIG_WMT_VIDEO_LOCK) += g_wmt_lock.o
+obj-$(CONFIG_WMT_VIDEO_DECODE) += g_wmt_vd_com.o
+obj-$(CONFIG_WMT_VIDEO_DECODE_MSVD) += g_wmt_msvd.o
+obj-$(CONFIG_WMT_VIDEO_DECODE_JPEG) += g_wmt_vd_jdec.o
diff --git a/drivers/media/video/wmt/encode/Kconfig b/drivers/media/video/wmt/encode/Kconfig
new file mode 100755
index 00000000..f79a822d
--- /dev/null
+++ b/drivers/media/video/wmt/encode/Kconfig
@@ -0,0 +1,15 @@
+#
+# Multimedia Video device configuration
+#
+
+
+config WMT_VIDEO_ENCODE_H264
+ tristate "WonderMedia Hardware H264 encoder"
+ ---help---
+ Say Y here to use hardware to encode H264 files
+
+config WMT_VIDEO_ENCODE_JPEG
+ tristate "WonderMedia Hardware JPEG encoder"
+ ---help---
+ Say Y here to use hardware to encode JPEG image
+ \ No newline at end of file
diff --git a/drivers/media/video/wmt/encode/Makefile b/drivers/media/video/wmt/encode/Makefile
new file mode 100755
index 00000000..b107ce1a
--- /dev/null
+++ b/drivers/media/video/wmt/encode/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for the video encode device drivers.
+#
+
+g_wmt_ve_com-y := wmt-ve.o
+g_wmt_ve_h264-y := h264/wmt-h264enc.o h264/header.o h264/vlc.o
+g_wmt_ve_jenc-y := jpeg/hw-jenc.o jpeg/wmt-jenc.o
+
+obj-$(CONFIG_WMT_VIDEO_ENCODE) += g_wmt_ve_com.o
+obj-$(CONFIG_WMT_VIDEO_ENCODE_H264) += g_wmt_ve_h264.o
+obj-$(CONFIG_WMT_VIDEO_ENCODE_H264) += g_wmt_ve_jenc.o
diff --git a/drivers/media/video/wmt/encode/jpeg/Kbuild b/drivers/media/video/wmt/encode/jpeg/Kbuild
new file mode 100755
index 00000000..4bcf92f6
--- /dev/null
+++ b/drivers/media/video/wmt/encode/jpeg/Kbuild
@@ -0,0 +1,3 @@
+obj-m += g_wmt_ve_jpeg.o
+g_wmt_ve_jpeg-y := hw-jenc.o wmt-jenc.o
+
diff --git a/drivers/media/video/wmt_v4l2/Kconfig b/drivers/media/video/wmt_v4l2/Kconfig
new file mode 100755
index 00000000..cc91e792
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/Kconfig
@@ -0,0 +1,6 @@
+#
+# Multimedia Video device configuration
+#
+
+
+
diff --git a/drivers/media/video/wmt_v4l2/Makefile b/drivers/media/video/wmt_v4l2/Makefile
new file mode 100755
index 00000000..f0695e69
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/Makefile
@@ -0,0 +1,13 @@
+#
+# Makefile for the video capture/playback device drivers.
+#
+
+obj-y += wmt-vid.o
+obj-y += sensors/
+
+g_wmt_v4l2_tv_decoder-objs += tv_decoder/tv-device.o tv_decoder/wmt-tv-decoder.o
+obj-$(CONFIG_WMT_V4L2_TV_DECODER) += g_wmt_v4l2_tv_decoder.o
+
+
+
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/Kconfig b/drivers/media/video/wmt_v4l2/sensors/Kconfig
new file mode 100755
index 00000000..b1ce310b
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/Kconfig
@@ -0,0 +1,13 @@
+#
+# Multimedia Video device configuration
+#
+
+config WMT_V4L2_CMOS
+ tristate "WonderMedia V4L2 CMOS Support"
+ default y
+ ---help---
+ Say Y here to use hardware CMOS Support.
+
+
+
+ \ No newline at end of file
diff --git a/drivers/media/video/wmt_v4l2/sensors/Makefile b/drivers/media/video/wmt_v4l2/sensors/Makefile
new file mode 100755
index 00000000..7c9df975
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/Makefile
@@ -0,0 +1,49 @@
+#
+# Makefile for the video capture/playback device drivers.
+#
+
+LOCAL_MODULE_NAME = g_wmt_v4l2_cmos
+
+## sensors
+${LOCAL_MODULE_NAME}-objs += gc0307/gc0307.o
+${LOCAL_MODULE_NAME}-objs += gc0308/gc0308.o
+${LOCAL_MODULE_NAME}-objs += gc0309/gc0309.o
+${LOCAL_MODULE_NAME}-objs += gc0328/gc0328.o
+${LOCAL_MODULE_NAME}-objs += gc0329/gc0329.o
+${LOCAL_MODULE_NAME}-objs += gc2035/gc2035.o
+${LOCAL_MODULE_NAME}-objs += gc2155/gc2155.o
+${LOCAL_MODULE_NAME}-objs += ov7675/ov7675.o
+${LOCAL_MODULE_NAME}-objs += ov2643/ov2643.o
+${LOCAL_MODULE_NAME}-objs += ov2659/ov2659.o
+${LOCAL_MODULE_NAME}-objs += ov5640/ov5640.o
+${LOCAL_MODULE_NAME}-objs += ov9740/ov9740.o
+${LOCAL_MODULE_NAME}-objs += hi253/hi253.o
+${LOCAL_MODULE_NAME}-objs += hi257/hi257.o
+${LOCAL_MODULE_NAME}-objs += siv121d/siv121d.o
+${LOCAL_MODULE_NAME}-objs += siv120d/siv120d.o
+${LOCAL_MODULE_NAME}-objs += sid130b/sid130b.o
+${LOCAL_MODULE_NAME}-objs += sp0718/sp0718.o
+${LOCAL_MODULE_NAME}-objs += sp0a19/sp0a19.o
+${LOCAL_MODULE_NAME}-objs += sp0838/sp0838.o
+${LOCAL_MODULE_NAME}-objs += sp2518/sp2518.o
+${LOCAL_MODULE_NAME}-objs += bf3a03/bf3a03.o
+${LOCAL_MODULE_NAME}-objs += bf3920/bf3920.o
+${LOCAL_MODULE_NAME}-objs += bf3703/bf3703.o
+${LOCAL_MODULE_NAME}-objs += s5k5ca/s5k5ca.o
+${LOCAL_MODULE_NAME}-objs += nt99141/nt99141.o
+
+## ATV/DTV
+${LOCAL_MODULE_NAME}-objs += nmi5625/nmidrv_cmos.o
+obj-y += nmi5625/
+
+## flash
+${LOCAL_MODULE_NAME}-objs += flash/flash.o
+${LOCAL_MODULE_NAME}-objs += flash/flash_gpio.o
+${LOCAL_MODULE_NAME}-objs += flash/flash_eup2471.o
+${LOCAL_MODULE_NAME}-objs += flash/flash_eup3618.o
+${LOCAL_MODULE_NAME}-objs += flash/flash_ktd231.o
+${LOCAL_MODULE_NAME}-objs += flash/flash_sgm3780.o
+
+${LOCAL_MODULE_NAME}-objs += wmt-cmos.o cmos-subdev.o
+obj-m += ${LOCAL_MODULE_NAME}.o
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/bf3703/bf3703.c b/drivers/media/video/wmt_v4l2/sensors/bf3703/bf3703.c
new file mode 100755
index 00000000..2066be20
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/bf3703/bf3703.c
@@ -0,0 +1,326 @@
+
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "bf3703.h"
+
+#define sensor_write_array(sd, arr, sz) \
+ cmos_init_8bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write(sd->i2c_addr, reg, val)
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint8_t *regs;
+ size_t size;
+};
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+// CMOS_WIN_SIZE("QVGA", 320, 240, bf3703_320x240),
+ CMOS_WIN_SIZE("VGA", 640, 480, bf3703_640x480),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = bf3703_wb_auto;
+ size = ARRAY_SIZE(bf3703_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = bf3703_wb_incandescent;
+ size = ARRAY_SIZE(bf3703_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = bf3703_wb_daylight;
+ size = ARRAY_SIZE(bf3703_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = bf3703_wb_cloudy;
+ size = ARRAY_SIZE(bf3703_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = bf3703_wb_fluorescent;
+ size = ARRAY_SIZE(bf3703_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_scenemode(struct cmos_subdev *sd, enum v4l2_scene_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case SCENE_MODE_AUTO:
+ regs = bf3703_scene_mode_auto;
+ size = ARRAY_SIZE(bf3703_scene_mode_auto);
+ break;
+ case SCENE_MODE_NIGHTSHOT:
+ regs = bf3703_scene_mode_night;
+ size = ARRAY_SIZE(bf3703_scene_mode_night);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_exposure(struct cmos_subdev *sd, int value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case -2:
+ regs = bf3703_exposure_neg6;
+ size = ARRAY_SIZE(bf3703_exposure_neg6);
+ break;
+ case -1:
+ regs = bf3703_exposure_neg3;
+ size = ARRAY_SIZE(bf3703_exposure_neg3);
+ break;
+ case 0:
+ regs = bf3703_exposure_zero;
+ size = ARRAY_SIZE(bf3703_exposure_zero);
+ break;
+ case 1:
+ regs = bf3703_exposure_pos3;
+ size = ARRAY_SIZE(bf3703_exposure_pos3);
+ break;
+ case 2:
+ regs = bf3703_exposure_pos6;
+ size = ARRAY_SIZE(bf3703_exposure_pos6);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data;
+
+ data = sensor_read(sd, 0x1e);
+
+ switch (value) {
+ case 0:
+ data &= ~0x20;
+ break;
+ case 1:
+ data |= 0x20;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write(sd, 0x1e, data);
+ return 0;
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data;
+
+ data = sensor_read(sd, 0x1e);
+
+ switch (value) {
+ case 0:
+ data &= ~0x10;
+ break;
+ case 1:
+ data |= 0x10;
+ break;
+ default:
+ return -EINVAL;
+ }
+ sensor_write(sd, 0x1e, data);
+
+ return 0;
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ case V4L2_CID_EXPOSURE:
+ return v4l2_ctrl_query_fill(qc, -2, 2, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return sensor_s_scenemode(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return sensor_s_exposure(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ default:
+ case WMT_V4L2_CID_CAMERA_ANTIBANDING:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win) {
+ pr_err("%s, s_mbus_fmt failed, width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, win->regs, win->size);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return 0;
+}
+
+static int sensor_enum_framesizes(struct cmos_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ const struct cmos_win_size *win = &cmos_supported_win_sizes[index];
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ uint32_t data = 0;
+
+ data |= (sensor_read(sd, 0xfc) & 0xff) << 8;
+ data |= (sensor_read(sd, 0xfd) & 0xff);
+
+ return (data == sd->id) ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ if (sensor_identify(sd)) {
+ return -1;
+ }
+
+ sensor_write_array(sd, bf3703_default_regs_init,ARRAY_SIZE(bf3703_default_regs_init));
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ sensor_write_array(sd, bf3703_default_regs_exit,
+ ARRAY_SIZE(bf3703_default_regs_exit));
+ return 0;
+}
+
+static struct cmos_subdev_ops bf3703_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+ .enum_framesizes = sensor_enum_framesizes,
+};
+
+struct cmos_subdev bf3703 = {
+ .name = "bf3703",
+ .i2c_addr = 0x6e,
+ .id = 0x3703,
+ .max_width = 640,
+ .max_height = 480,
+ .ops = &bf3703_ops,
+};
+
+#if 0
+static int __init bf3703_init(void)
+{
+ return cmos_register_sudbdev(&bf3703);
+}
+
+static void __exit bf3703_exit(void)
+{
+ return cmos_unregister_subdev(&bf3703);
+}
+
+module_init(bf3703_init);
+module_exit(bf3703_exit);
+
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/bf3703/bf3703.h b/drivers/media/video/wmt_v4l2/sensors/bf3703/bf3703.h
new file mode 100755
index 00000000..40f4933f
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/bf3703/bf3703.h
@@ -0,0 +1,269 @@
+
+// Scene Mode
+uint8_t bf3703_scene_mode_auto[] = {
+ 0x8e,0x0d, //0x07
+ 0x8f,0xf2 //0x79
+};
+
+uint8_t bf3703_scene_mode_night[] = {
+ 0x8e,0x0e,
+ 0x8f,0xf2,
+};
+
+
+// White Balance
+uint8_t bf3703_wb_auto [] = {
+ 0x13,0x07,
+};
+
+uint8_t bf3703_wb_incandescent [] = {
+ 0x13,0x05,
+ 0x01, 0x1f,
+ 0x02, 0x15,
+};
+
+uint8_t bf3703_wb_fluorescent [] = {
+ 0x13,0x05,
+ 0x01, 0x1a,
+ 0x02, 0x1e,
+};
+
+uint8_t bf3703_wb_daylight [] = {
+ 0x13,0x05,
+ 0x01, 0x13,
+ 0x02, 0x26,
+};
+
+uint8_t bf3703_wb_cloudy [] = {
+ 0x13,0x05,
+ 0x01, 0x10,
+ 0x02, 0x28,
+};
+
+
+// Exposure
+uint8_t bf3703_exposure_neg6[] = {
+0x55, 0xa8,
+};
+
+uint8_t bf3703_exposure_neg3[] = {
+0x55, 0x98,
+};
+
+uint8_t bf3703_exposure_zero[] = {
+0x55, 0x00,
+};
+
+uint8_t bf3703_exposure_pos3[] = {
+0x55, 0x10,
+};
+
+uint8_t bf3703_exposure_pos6[] = {
+0x55, 0x20,
+};
+
+
+// Resolution
+uint8_t bf3703_320x240[]= {
+ 0x12, 0x10,
+
+};
+
+uint8_t bf3703_640x480[] = {
+ 0x12, 0x00,
+};
+
+uint8_t bf3703_default_regs_init[] = {
+
+ 0xff , 0xff,
+ // 0x12 , 0x80,
+ 0x15 , 0x00, //byd add 20120628
+ 0x3a , 0x00, //byd add 20120628
+
+ 0x11 , 0x80, //0x80
+ 0x13 , 0x00,
+ 0x01 , 0x15,
+ 0x02 , 0x24,
+ 0x8c , 0x02,//01 :devided by 2 02 :devided by 1
+ 0x8d , 0x64,//32: devided by 2 64 :devided by 1
+ 0x87 , 0x18,
+ 0x13 , 0x07,
+
+ //black level , ¶ÔÉϵçÆ«ÂÌÓиÄÉÆ , Èç¹ûÐèÒªÇëʹÓÃ
+
+ 0x05 , 0x1f,
+ 0x06 , 0x60,
+ 0x14 , 0x1f,
+ 0x27 , 0x03,
+ 0x06 , 0xe0,
+
+ //lens shading
+ 0x35 , 0x68,
+ 0x65 , 0x68,
+ 0x66 , 0x62,
+ 0x36 , 0x05,
+ 0x37 , 0xf6,
+ 0x38 , 0x46,
+ 0x9b , 0xf6,
+ 0x9c , 0x46,
+ 0xbc , 0x01,
+ 0xbd , 0xf6,
+ 0xbe , 0x46,
+
+ //AE
+ 0x82 , 0x14,
+ 0x83 , 0x23,
+ 0x9a , 0x23, //the same as 0x83
+ 0x84 , 0x1a,
+ 0x85 , 0x20,
+ 0x89 , 0x04, //02 :devided by 2 04 :devided by 1
+ 0x8a , 0x08, //04: devided by 2 05 :devided by 1
+ 0x86 , 0x28,
+ 0x96 , 0xa6, //AE speed
+ 0x97 , 0x0c, //AE speed
+ 0x98 , 0x18, //AE speed
+ //AE target
+ 0x24 , 0x6a, //78 //0x6a
+ 0x25 , 0x7a, //88 //0x7a
+ 0x94 , 0x0a, //INT_OPEN
+ 0x80 , 0x55,
+
+ //denoise
+ 0x70 , 0xff,//denoise //0x6f
+ 0x72 , 0x6f,//denoise //0x4f
+ 0x73 , 0x4f,//denoise //0x2f
+ 0x74 , 0x38,//denoise //0x27
+ 0x77 , 0x90,//È¥³ý¸ñ×ÓÔëÉù
+ 0x7a , 0x0e,//denoise in low light , 0x8e\0x4e\0x0e
+ 0x7b , 0x28,//the same as 0x86
+
+ //black levl
+ 0X1F , 0x20, //G target
+ 0X22 , 0x20, //R target
+ 0X26 , 0x20, //B target
+ // Ä£Äⲿ·Ö²ÎÊý
+ 0X16 , 0x03, //Èç¹û¾õµÃºÚÉ«ÎïÌå²»¹»ºÚ£¬ÓеãÆ«ºì£¬½«0x16дΪ0x03»áÓеã¸ÄÉÆ
+ 0xbb , 0x20, // deglitch ¶ÔxclkÕûÐÎ
+ 0xeb , 0x30,
+ 0xf5 , 0x21,
+ 0xe1 , 0x3c,
+ 0xbb , 0x20,
+ 0X2f , 0Xf6,
+ 0x06 , 0xe0,
+
+ //anti black sun spot
+ 0x61 , 0xd3, //0x61[3]=0 black sun disable
+ 0x79 , 0x48, //0x79[7]=0 black sun disable
+
+ //contrast
+ 0x56 , 0x40,
+
+ //Gamma
+ 0x3b , 0x58, //auto gamma offset adjust in low light
+ 0x3c , 0x20, //auto gamma offset adjust in low light
+ //0x56 , 0x48,
+ 0x39 , 0x80,
+
+ //gamma 4 low noise
+ 0X3f , 0Xb8, //0xa8
+ 0X40 , 0X48,
+ 0X41 , 0X54,
+ 0X42 , 0X4E,
+ 0X43 , 0X44,
+ 0X44 , 0X3E,
+ 0X45 , 0X39,
+ 0X46 , 0X35,
+ 0X47 , 0X31,
+ 0X48 , 0X2E,
+ 0X49 , 0X2B,
+ 0X4b , 0X29,
+ 0X4c , 0X27,
+ 0X4e , 0X23,
+ 0X4f , 0X20,
+ 0X50 , 0X20,
+
+ //ÊÊÖÐ
+ 0x51 , 0x08,
+ 0x52 , 0x0E,
+ 0x53 , 0x06,
+ 0x54 , 0x12,
+ 0x57 , 0x82,
+ 0x58 , 0x70,
+ 0x59 , 0x5C,
+ 0x5a , 0x77,
+ 0x5b , 0x1B,
+ 0x5c , 0x0e, //0x5c[3:0] low light color coefficient£¬smaller , lower noise
+ 0x5d , 0x95,
+
+ 0x60 , 0x20,//color open in low light
+ //AWB
+ 0x6a , 0x81,//Èç¹û·ôÉ«Æ«É«£¬½«0x6aдΪ0x81. //0x01
+ 0x23 , 0x66,//Green gain
+ 0xa0 , 0x07,//0xa0д0x03£¬ºÚÉ«ÎïÌå¸üºì£»0xa0д0x07£¬ºÚÉ«ÎïÌå¸üºÚ£»
+
+ 0xa1 , 0X41,//
+ 0xa2 , 0X0e,
+ 0xa3 , 0X26,
+ 0xa4 , 0X0d,
+ //ÀäÉ«µ÷
+ //0xa5 , 0x26,//The upper limit of red gain
+ 0xa5 , 0x2d, ///*ůɫµ÷*/
+
+ 0xa6 , 0x04,
+ 0xa7 , 0x82,//BLUE Target///0x80
+ 0xa8 , 0x7f,//RED Target
+ 0xa9 , 0x28,
+ 0xaa , 0x28,
+ 0xab , 0x28,
+ 0xac , 0x3c,
+ 0xad , 0xf0,
+ 0xc8 , 0x18,
+ 0xc9 , 0x20,
+ 0xca , 0x17,
+ 0xcb , 0x1f,
+ 0xaf , 0x00,
+ 0xc5 , 0x18,
+ 0xc6 , 0x00,
+ 0xc7 , 0x20,
+ 0xae , 0x83,//Èç¹ûÕÕ»§ÍâÆ«À¶£¬½«´Ë¼Ä´æÆ÷0xaeдΪ0x81¡£
+ 0xcc , 0x30,
+ 0xcd , 0x70,
+ 0xee , 0x4c,// P_TH
+
+ // color sauration
+ 0xb0 , 0xd0,
+ //0xb1 , 0xc0,
+ //0xb2 , 0xaa,
+
+ // ±¥ºÍ¶ÈÑÞÀö
+ 0xb1 , 0xd0,
+ 0xb2 , 0xc0,
+
+ 0xb3 , 0x88,
+
+ //anti webcmera banding
+ 0x9d , 0x82, //0x4c //updata by yang.chunming@byd.com 2013.12.3 16:27
+ 0x9e , 0x6c, //add by yang.chunming@byd.com 2013.12.3 16:27
+ //switch diection
+
+ 0x1e , 0x00, //00:normal 10:IMAGE_V_MIRROR 20:IMAGE_H_MIRROR 30:IMAGE_HV_MIRROR
+
+ //set max 15fps
+ 0x92 , 0xa0,
+ 0x93 , 0x00,
+ 0x2a , 0x00, //0x10
+ 0x2b , 0x88,
+
+ 0x8e ,0x0d, //0x07 set fps as auto mode by yang.chunming@byd.com 2013.12.03
+ 0x8f ,0xf2,
+ //0x8e , 0X03,
+ //0x8f , 0Xfb, //set min 10fps
+
+ 0x20 , 0x40,
+ 0x09 , 0xc1,
+
+};
+
+uint8_t bf3703_default_regs_exit[]={
+};
+//#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/bf3920/bf3920.c b/drivers/media/video/wmt_v4l2/sensors/bf3920/bf3920.c
new file mode 100755
index 00000000..f2d8950a
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/bf3920/bf3920.c
@@ -0,0 +1,330 @@
+
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "bf3920.h"
+
+#define sensor_write_array(sd, arr, sz) \
+ cmos_init_8bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write(sd->i2c_addr, reg, val)
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint8_t *regs;
+ size_t size;
+};
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+// CMOS_WIN_SIZE("QVGA", 320, 240, bf3920_320_240_regs),
+ CMOS_WIN_SIZE("VGA", 640, 480, bf3920_640_480_regs),
+// CMOS_WIN_SIZE("SVGA", 800, 600, bf3920_800_600_regs),
+// CMOS_WIN_SIZE("720p", 1280, 720, bf3920_1280_720_regs),
+ CMOS_WIN_SIZE("UXGA", 1600, 1200, bf3920_1600_1200_rets),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = bf3920_wb_auto;
+ size = ARRAY_SIZE(bf3920_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = bf3920_wb_incandescent;
+ size = ARRAY_SIZE(bf3920_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = bf3920_wb_daylight;
+ size = ARRAY_SIZE(bf3920_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = bf3920_wb_cloudy;
+ size = ARRAY_SIZE(bf3920_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = bf3920_wb_fluorescent;
+ size = ARRAY_SIZE(bf3920_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_scenemode(struct cmos_subdev *sd, enum v4l2_scene_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case SCENE_MODE_AUTO:
+ regs = bf3920_scene_mode_auto;
+ size = ARRAY_SIZE(bf3920_scene_mode_auto);
+ break;
+ case SCENE_MODE_NIGHTSHOT:
+ regs = bf3920_scene_mode_night;
+ size = ARRAY_SIZE(bf3920_scene_mode_night);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_exposure(struct cmos_subdev *sd, enum v4l2_exposure_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case -2:
+ regs = bf3920_exposure_neg6;
+ size = ARRAY_SIZE(bf3920_exposure_neg6);
+ break;
+ case -1:
+ regs = bf3920_exposure_neg3;
+ size = ARRAY_SIZE(bf3920_exposure_neg3);
+ break;
+ case 0:
+ regs = bf3920_exposure_zero;
+ size = ARRAY_SIZE(bf3920_exposure_zero);
+ break;
+ case 1:
+ regs = bf3920_exposure_pos3;
+ size = ARRAY_SIZE(bf3920_exposure_pos3);
+ break;
+ case 2:
+ regs = bf3920_exposure_pos6;
+ size = ARRAY_SIZE(bf3920_exposure_pos6);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data;
+
+ data = sensor_read(sd, 0x1e);
+
+ switch (value) {
+ case 0:
+ data &= ~0x20;
+ break;
+ case 1:
+ data |= 0x20;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sensor_write(sd, 0x1e, data);
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data;
+
+
+
+ data = sensor_read(sd, 0x1e);
+
+ switch (value) {
+ case 0:
+ data &= ~0x10;
+ break;
+ case 1:
+ data |= 0x10;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sensor_write(sd, 0x1e, data);
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ case V4L2_CID_EXPOSURE:
+ return v4l2_ctrl_query_fill(qc, -2, 2, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return sensor_s_scenemode(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return sensor_s_exposure(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ default:
+ case WMT_V4L2_CID_CAMERA_ANTIBANDING:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win) {
+ pr_err("%s, s_mbus_fmt failed, width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+ return -EINVAL;
+ }
+ sensor_write_array(sd, win->regs, win->size);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+
+ return 0;
+}
+
+static int sensor_enum_framesizes(struct cmos_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ const struct cmos_win_size *win = &cmos_supported_win_sizes[index];
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ uint32_t data = 0;
+
+ data |= (sensor_read(sd, 0xfc) & 0xff) << 8;
+ data |= (sensor_read(sd, 0xfd) & 0xff);
+
+ return (data == sd->id) ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ if (sensor_identify(sd)) {
+ return -1;
+ }
+
+ sensor_write_array(sd, bf3920_default_regs_init,
+ ARRAY_SIZE(bf3920_default_regs_init));
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ return 0;
+}
+
+static struct cmos_subdev_ops bf3920_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+ .enum_framesizes = sensor_enum_framesizes,
+};
+
+struct cmos_subdev bf3920 = {
+ .name = "bf3920",
+ .i2c_addr = 0x6e,
+ .id = 0x3920,
+ .max_width = 1600,
+ .max_height = 1200,
+ .ops = &bf3920_ops,
+};
+
+#if 0
+static int __init bf3920_init(void)
+{
+ return cmos_register_sudbdev(&bf3920);
+}
+
+static void __exit bf3920_exit(void)
+{
+ return cmos_unregister_subdev(&bf3920);
+}
+
+module_init(bf3920_init);
+module_exit(bf3920_exit);
+
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/bf3920/bf3920.h b/drivers/media/video/wmt_v4l2/sensors/bf3920/bf3920.h
new file mode 100755
index 00000000..96eefa19
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/bf3920/bf3920.h
@@ -0,0 +1,514 @@
+#ifndef BF3920_H
+#define BF3920_H
+
+// Scene Mode
+uint8_t bf3920_scene_mode_auto[] = {
+ 0x89,0x55,
+ 0xff,0xff,
+};
+
+uint8_t bf3920_scene_mode_night[] = {
+ 0x89,0x75,
+ 0xff,0xff,
+};
+
+
+// White Balance
+uint8_t bf3920_wb_auto [] = {
+ 0x13, 0x07,
+ 0x01, 0x12,
+ 0x02, 0x22,
+ 0xff,0xff,
+};
+
+uint8_t bf3920_wb_incandescent [] = {
+ 0x13, 0x05,
+ 0x01, 0x28,
+ 0x02, 0x0a,
+
+ 0xff,0xff,
+};
+
+uint8_t bf3920_wb_fluorescent [] = {
+ 0x13, 0x05,
+ 0x01, 0x1a,
+ 0x02, 0x1e,
+ 0xff,0xff,
+};
+
+uint8_t bf3920_wb_daylight [] = {
+ 0x13, 0x05,
+ 0x01, 0x10,
+ 0x02, 0x20,
+ 0xff,0xff,
+};
+
+uint8_t bf3920_wb_cloudy [] = {
+ 0x13, 0x05,
+ 0x01, 0x0e,
+ 0x02, 0x24,
+ 0xff,0xff,
+};
+
+
+// Exposure
+uint8_t bf3920_exposure_neg6[] = {
+ 0x56,0x28,
+ 0x55,0xb0,
+};
+
+uint8_t bf3920_exposure_neg3[] = {
+ 0x56,0x28,
+ 0x55,0x98,
+};
+
+uint8_t bf3920_exposure_zero[] = {
+ 0x56,0x28,
+ 0x55,0x00,
+};
+
+uint8_t bf3920_exposure_pos3[] = {
+ 0x56,0x28,
+ 0x55,0x18,
+};
+
+uint8_t bf3920_exposure_pos6[] = {
+ 0x56,0x28,
+ 0x55,0x38,
+};
+
+
+// Resolution
+uint8_t bf3920_1600_1200_rets[]={
+ /* 1600X1200 UXGA */
+
+ 0x1b,0x2c,
+ 0x11,0x30,//38
+ 0x8a,0x7f,//0XBF
+ 0x2a,0x0c,
+ 0x2b,0x1c,
+ 0x92,0x02,
+ 0x93,0x00,
+
+
+ //window
+ 0x4a,0x80,
+ 0xcc,0x00,
+ 0xca,0x00,
+ 0xcb,0x00,
+ 0xcf,0x00,
+ 0xcd,0x00,
+ 0xce,0x00,
+ 0xc0,0x00,
+ 0xc1,0x00,
+ 0xc2,0x00,
+ 0xc3,0x00,
+ 0xc4,0x00,
+ 0xb5,0x00,
+ 0x03,0x60,
+ 0x17,0x00,
+ 0x18,0x40,
+ 0x10,0x40,
+ 0x19,0x00,
+ 0x1a,0xb0,
+ 0x0B,0x00,
+ 0xff, 0xff,
+};
+
+uint8_t bf3920_1280_720_regs[]={
+};
+
+uint8_t bf3920_800_600_regs[]={
+
+ 0x1b,0x2c,
+ 0x11,0x30,//38
+ 0x8a,0x7f,//0XBF
+ 0x2a,0x0c,
+ 0x2b,0x1c,
+ 0x92,0x02,
+ 0x93,0x00,
+
+
+ //window
+ 0x4a,0x80,
+ 0xcc,0x00,
+ 0xca,0x00,
+ 0xcb,0x00,
+ 0xcf,0x00,
+ 0xcd,0x00,
+ 0xce,0x00,
+ 0xc0,0x00,
+ 0xc1,0x00,
+ 0xc2,0x00,
+ 0xc3,0x00,
+ 0xc4,0x00,
+ 0xb5,0x00,
+ 0x03,0x60,
+ 0x17,0x00,
+ 0x18,0x40,
+ 0x10,0x40,
+ 0x19,0x00,
+ 0x1a,0xb0,
+ 0x0B,0x10,
+ 0xff, 0xff,
+};
+
+uint8_t bf3920_640_480_regs[]={
+ 0x1b,0x2c,
+ 0x11,0x30,//38
+ 0x8a,0x7f,//0XBF
+ 0x2a,0x0c,
+ 0x2b,0x1c,
+ 0x92,0x02,
+ 0x93,0x00,
+
+
+ //window
+ 0x4a,0x80,
+ 0xcc,0x00,
+ 0xca,0x00,
+ 0xcb,0x00,
+ 0xcf,0x00,
+ 0xcd,0x00,
+ 0xce,0x00,
+ 0xc0,0xd3,
+ 0xc1,0x04,
+ 0xc2,0xc4,
+ 0xc3,0x11,
+ 0xc4,0x3f,
+ 0xb5,0x3f,
+ 0x03,0x50,
+ 0x17,0x00,
+ 0x18,0x00,
+ 0x10,0x30,
+ 0x19,0x00,
+ 0x1a,0xc0,
+ 0x0B,0x10,
+ 0xff, 0xff,
+};
+
+uint8_t bf3920_320_240_regs[]= {
+
+ 0x1b,0x2c,
+ 0x11,0x30,//38
+ 0x8a,0x7f,//0XBF
+ 0x2a,0x0c,
+ 0x2b,0x1c,
+ 0x92,0x02,
+ 0x93,0x00,
+
+
+ //window
+ 0x4a,0x80,
+ 0xcc,0x00,
+ 0xca,0x00,
+ 0xcb,0x00,
+ 0xcf,0x00,
+ 0xcd,0x00,
+ 0xce,0x00,
+ 0xc0,0xd3,
+ 0xc1,0x04,
+ 0xc2,0xc4,
+ 0xc3,0x11,
+ 0xc4,0x3f,
+ 0xb5,0x3f,
+ 0x03,0x50,
+ 0x17,0x00,
+ 0x18,0x00,
+ 0x10,0x30,
+ 0x19,0x00,
+ 0x1a,0xc0,
+ 0x0B,0x30,
+ 0xff, 0xff,
+
+
+
+
+
+
+
+};
+
+uint8_t bf3920_default_regs_init[]={
+
+
+ 0x09,0x42,
+ 0x29,0x60,
+ 0x12,0x00,
+ 0x13,0x07,
+ 0xe2,0xc4,
+ 0xe7,0x4e,
+ 0x29,0x60,
+ 0x08,0x16,
+ 0x16,0x28,
+ 0x1c,0x00,
+ 0x1d,0xf1,
+ 0xbb,0x30,
+ 0x3e,0x80,
+ 0x27,0x98,
+ 0x11,0x30,
+ 0x15,0x80,
+ 0x3a,0x00,
+
+ //lens shading
+ 0x1e,0x46,
+ 0x35,0x30, //lens shading gain of R
+ 0x65,0x30, //lens shading gain of G
+ 0x66,0x2a, //lens shading gain of B
+ 0xbc,0xd3,
+ 0xbd,0x5c,
+ 0xbe,0x24,
+ 0xbf,0x13,
+ 0x9b,0x5c,
+ 0x9c,0x24,
+ 0x36,0x13,
+ 0x37,0x5c,
+ 0x38,0x24,
+
+ 0x92,0x02,//Dummy Line Insert LSB
+ 0x93,0x00,
+ 0x2a,0x0c,
+ 0x2b,0x13,//Dummy Pixel Insert LSB
+
+ //initial AWB and AE
+ 0x13,0x00,
+ 0x8c,0x0b,
+ 0x8d,0xc4,
+ 0x24,0xc8,
+ 0x87,0x2d,
+ 0x01,0x12,
+ 0x02,0x22,
+ 0x13,0x07,
+
+
+
+ //black sun
+ /*
+ 0xfb,0x06, //72M
+ 0xfb,0x0a,//48M
+ 0xfb,0x0c, //36M
+ 0xfb,0x12, //24M
+ */
+
+
+
+ //denoise and edge enhancement
+ 0x70,0x01,
+ 0x72,0x62,//0x72[7:6]: Bright edge enhancement; 0x72[5:4]: Dark edge enhancement
+ 0x78,0x37,
+ 0x7a,0x29,//0x7a[7:4]: The bigger, the smaller noise; 0x7a[3:0]: The smaller, the smaller noise
+ 0x7d,0x85, //
+
+ 0x39,0xa8, //0xa0 zzg 0xa8
+ 0x3f,0x28, //0x20 zzg 0x28
+
+
+ // gamma default(low noise)
+
+ 0x40,0x20,
+ 0x41,0x25,
+ 0x42,0x23,
+ 0x43,0x1f,
+ 0x44,0x18,
+ 0x45,0x15,
+ 0x46,0x12,
+ 0x47,0x10,
+ 0x48,0x10,
+ 0x49,0x0f,
+ 0x4b,0x0e,
+ 0x4c,0x0c,
+ 0x4e,0x0b,
+ 0x4f,0x09,
+ 0x50,0x07,
+
+
+ //gammaÇåÎúÁÁÀö
+ /*
+ 0x40,0x28,
+ 0x41,0x26,
+ 0x42,0x24,
+ 0x43,0x22,
+ 0x44,0x1c,
+ 0x45,0x19,
+ 0x46,0x11,
+ 0x47,0x10,
+ 0x48,0x0f,
+ 0x49,0x0e,
+ 0x4b,0x0c,
+ 0x4c,0x0a,
+ 0x4e,0x09,
+ 0x4f,0x08,
+ 0x50,0x04,
+ */
+
+ /*
+ //gamma ¹ýÆØÁÁ¶ÈºÃ£¬¸ßÁÁ
+
+ 0x40,0x28,
+ 0x41,0x28,
+ 0x42,0x30,
+ 0x43,0x29,
+ 0x44,0x23,
+ 0x45,0x1b,
+ 0x46,0x17,
+ 0x47,0x0f,
+ 0x48,0x0d,
+ 0x49,0x0b,
+ 0x4b,0x09,
+ 0x4c,0x08,
+ 0x4e,0x07,
+ 0x4f,0x05,
+ 0x50,0x04,
+
+ //// from ryx zzg
+ 0x40,0x25,
+ 0x41,0x28,
+ 0x42,0x25,
+ 0x43,0x23,
+ 0x44,0x1b,
+ 0x45,0x18,
+ 0x46,0x15,
+ 0x47,0x11,
+ 0x48,0x10,
+ 0x49,0x0f,
+ 0x4b,0x0e,
+ 0x4c,0x0c,
+ 0x4e,0x0a,
+ 0x4f,0x06,
+ 0x50,0x05,
+*/
+
+
+
+
+ //color default
+ /*
+ 0x5c,0x00,
+ 0x51,0x32,
+ 0x52,0x17,
+ 0x53,0x8C,
+ 0x54,0x79,
+ 0x57,0x6E,
+ 0x58,0x01,
+ 0x5a,0x36,
+ 0x5e,0x38,
+ */
+
+ //color ÂÌÉ«ºÃºìÉ«Ö÷ϵÊý¸ß
+
+ 0x5c,0x00,
+ 0x51,0x24,//16
+ 0x52,0x0b,//e
+ 0x53,0x77,//6f
+ 0x54,0x65,//66
+ 0x57,0x5e,//76
+ 0x58,0x19,//15
+ 0x5a,0x16,//16
+ 0x5e,0x60,//0x38 zzg
+
+ /*
+ //color ÑÞÀö
+
+ 0x5c,0x00,
+ 0x51,0x2b,
+ 0x52,0x0e,
+ 0x53,0x9f,
+ 0x54,0x7d,
+ 0x57,0x91,
+ 0x58,0x21,
+ 0x5a,0x16,
+ 0x5e,0x38,
+ */
+
+
+ //color É«²Êµ­
+ /*
+ 0x5c,0x00,
+ 0x51,0x24,
+ 0x52,0x0b,
+ 0x53,0x77,
+ 0x54,0x65,
+ 0x57,0x5e,
+ 0x58,0x19,
+ 0x5a,0x16,
+ 0x5e,0x38,
+ */
+ //A color
+ 0x5c,0x80,
+ 0x51,0x22,
+ 0x52,0x00,
+ 0x53,0x96,
+ 0x54,0x8C,
+ 0x57,0x7F,
+ 0x58,0x0B,
+ 0x5a,0x14,
+ 0x5e,0x60, ////0x38 zzg
+ // saturn and auto contrast
+ 0x56,0x28,
+ 0x55,0x00,//when 0x56[7:6]=2'b0x,0x55[7:0]:Brightness control.bit[7]: 0:positive 1:negative [6:0]:value
+ 0xb0,0x8d,
+ 0xb1,0x15,//when 0x56[7:6]=2'b0x,0xb1[7]:auto contrast switch(0:on 1:off)
+ 0x56,0xe8,
+ 0x55,0xf0,//when 0x56[7:6]=2'b11,cb coefficient of F light
+ 0xb0,0xb8,//when 0x56[7:6]=2'b11,cr coefficient of F light
+ 0x56,0xa8,
+ 0x55,0xb8,//when 0x56[7:6]=2'b10,cb coefficient of !F light
+ 0xb0,0xa8,//when 0x56[7:6]=2'b10,cr coefficient of !F light
+ // manual rast
+
+ 0x56,0x28,
+ 0xb1,0x95,//when 0x56[7:6]=2'b0x,0xb1[7]:auto contrast switch(0:on 1:off)
+ 0x56,0xe8,
+ 0xb4,0x40,//when 0x56[7:6]=2'b11,manual contrast coefficient
+
+ //AE
+ 0x13,0x07,
+ 0x24,0x40,// AE Target
+ 0x25,0x88,// Bit[7:4]: AE_LOC_INT; Bit[3:0]:AE_LOC_GLB
+ 0x97,0x30,// Y target value1.
+ 0x98,0x18,// AE ´°¿ÚºÍȨÖØ 0x0a zzg
+ 0x80,0xd2,//Bit[3:2]: the bigger, Y_AVER_MODIFY is smaller 0x92
+ 0x81,0xff,//AE speed //0xe0
+ 0x82,0x30,//minimum global gain 0x30 zzg
+ 0x83,0x60, //0x60 zzg
+ 0x84,0x45, ///0x65 zzg
+ 0x85,0x7d, ///0x90 zzg
+ 0x86,0xa0,//maximum gain zzg 0xc0 0x97
+ 0x89,0x6d,//INT_MAX_MID 9d zzg
+ 0x94,0x82,//Bit[7:4]: Threshold for over exposure pixels, the smaller, the more over exposure pixels; Bit[3:0]: Control the start of AE. 0x42 zzg b2
+
+ //AWB
+ 0x6a,0x81,
+ 0x23,0x66,// green gain
+ 0xa1,0x31,
+ 0xa2,0x0b,//the low limit of blue gain for indoor scene
+ 0xa3,0x25,//the upper limit of blue gain for indoor scene
+ 0xa4,0x09,//the low limit of red gain for indoor scene
+ 0xa5,0x26,//the upper limit of red gain for indoor scene
+ 0xa7,0x9a,//Base B gain²»½¨ÒéÐÞ¸Ä
+ 0xa8,0x15,//Base R gain²»½¨ÒéÐÞ¸Ä
+ 0xa9,0x13,
+ 0xaa,0x12,
+ 0xab,0x16,
+ 0xac,0x20, //avoiding red scence when openning
+ 0xc8,0x10,
+ 0xc9,0x15,
+ 0xd2,0x78,
+ 0xd4,0x20,
+
+ ///awb base gain offset
+ 0xd0,0x11,
+ 0xd1,0x09, ///0x01 zzg
+
+
+ 0x0b,0x10, //800*600
+ 0x09,0x72, ///0x42 HSYNC delay bit[5:4]: HREF delay select, 0:no delay£¬01£ºdelay 2ns£¬10£ºdelay 4ns£¬11£ºdelay 6ns
+ //skin
+ 0xee,0x2a,
+ 0xef,0x1b,
+
+
+};
+
+#endif
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/bf3a03/bf3a03.c b/drivers/media/video/wmt_v4l2/sensors/bf3a03/bf3a03.c
new file mode 100755
index 00000000..dfc0a4bf
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/bf3a03/bf3a03.c
@@ -0,0 +1,335 @@
+
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "bf3a03.h"
+
+#define sensor_write_array(sd, arr, sz) \
+ cmos_init_8bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write(sd->i2c_addr, reg, val)
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint8_t *regs;
+ size_t size;
+};
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+// CMOS_WIN_SIZE("QVGA", 320, 240, bf3a03_320x240),
+ CMOS_WIN_SIZE("VGA", 640, 480, bf3a03_640x480),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = bf3a03_wb_auto;
+ size = ARRAY_SIZE(bf3a03_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = bf3a03_wb_incandescent;
+ size = ARRAY_SIZE(bf3a03_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = bf3a03_wb_daylight;
+ size = ARRAY_SIZE(bf3a03_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = bf3a03_wb_cloudy;
+ size = ARRAY_SIZE(bf3a03_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = bf3a03_wb_fluorescent;
+ size = ARRAY_SIZE(bf3a03_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_scenemode(struct cmos_subdev *sd, enum v4l2_scene_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case SCENE_MODE_AUTO:
+ regs = bf3a03_scene_mode_auto;
+ size = ARRAY_SIZE(bf3a03_scene_mode_auto);
+ break;
+ case SCENE_MODE_NIGHTSHOT:
+ regs = bf3a03_scene_mode_night;
+ size = ARRAY_SIZE(bf3a03_scene_mode_night);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_exposure(struct cmos_subdev *sd, int value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case -2:
+ regs = bf3a03_exposure_neg6;
+ size = ARRAY_SIZE(bf3a03_exposure_neg6);
+ break;
+ case -1:
+ regs = bf3a03_exposure_neg3;
+ size = ARRAY_SIZE(bf3a03_exposure_neg3);
+ break;
+ case 0:
+ regs = bf3a03_exposure_zero;
+ size = ARRAY_SIZE(bf3a03_exposure_zero);
+ break;
+ case 1:
+ regs = bf3a03_exposure_pos3;
+ size = ARRAY_SIZE(bf3a03_exposure_pos3);
+ break;
+ case 2:
+ regs = bf3a03_exposure_pos6;
+ size = ARRAY_SIZE(bf3a03_exposure_pos6);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data;
+ uint8_t retry_times=0;
+
+
+ data = sensor_read(sd, 0x1e);
+
+ switch (value) {
+ case 0:
+ data &= ~0x20;
+ break;
+ case 1:
+ data |= 0x20;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write(sd, 0x1e, data);
+ return 0;
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data;
+ uint8_t retry_times=0;
+
+
+
+ data = sensor_read(sd, 0x1e);
+
+ switch (value) {
+ case 0:
+ data &= ~0x10;
+ break;
+ case 1:
+ data |= 0x10;
+ break;
+ default:
+ return -EINVAL;
+ }
+ sensor_write(sd, 0x1e, data);
+
+
+ return 0;
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ case V4L2_CID_EXPOSURE:
+ return v4l2_ctrl_query_fill(qc, -2, 2, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return sensor_s_scenemode(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return sensor_s_exposure(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ default:
+ case WMT_V4L2_CID_CAMERA_ANTIBANDING:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win) {
+ pr_err("%s, s_mbus_fmt failed, width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, win->regs, win->size);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return 0;
+}
+
+static int sensor_enum_framesizes(struct cmos_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ const struct cmos_win_size *win = &cmos_supported_win_sizes[index];
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ uint32_t data = 0;
+
+ data |= (sensor_read(sd, 0xfc) & 0xff) << 8;
+ data |= (sensor_read(sd, 0xfd) & 0xff);
+
+ return (data == sd->id) ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ uint32_t data = 0;
+
+ if (sensor_identify(sd)) {
+ return -1;
+ }
+
+ sensor_write_array(sd, bf3a03_default_regs_init,
+ ARRAY_SIZE(bf3a03_default_regs_init));
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ sensor_write_array(sd, bf3a03_default_regs_exit,
+ ARRAY_SIZE(bf3a03_default_regs_exit));
+ return 0;
+}
+
+static struct cmos_subdev_ops bf3a03_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+ .enum_framesizes = sensor_enum_framesizes,
+};
+
+struct cmos_subdev bf3a03 = {
+ .name = "bf3a03",
+ .i2c_addr = 0x6e,
+ .id = 0x3a03,
+ .max_width = 640,
+ .max_height = 480,
+ .ops = &bf3a03_ops,
+};
+
+#if 0
+static int __init bf3a03_init(void)
+{
+ return cmos_register_sudbdev(&bf3a03);
+}
+
+static void __exit bf3a03_exit(void)
+{
+ return cmos_unregister_subdev(&bf3a03);
+}
+
+module_init(bf3a03_init);
+module_exit(bf3a03_exit);
+
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/bf3a03/bf3a03.h b/drivers/media/video/wmt_v4l2/sensors/bf3a03/bf3a03.h
new file mode 100755
index 00000000..b7d2ac91
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/bf3a03/bf3a03.h
@@ -0,0 +1,391 @@
+#ifndef BF3A03_H
+#define BF3A03_H
+
+
+// Scene Mode
+uint8_t bf3a03_scene_mode_auto[] = {
+ 0x89, 0x6d,
+};
+
+uint8_t bf3a03_scene_mode_night[] = {
+ 0x89, 0x9d,
+};
+
+
+// White Balance
+uint8_t bf3a03_wb_auto [] = {
+ 0x13,0x07,
+ 0x01, 0x12,
+ 0x02, 0x22,
+};
+
+uint8_t bf3a03_wb_incandescent [] = {
+ 0x13,0x05,
+ 0x01, 0x1f,
+ 0x02, 0x20,
+};
+
+uint8_t bf3a03_wb_fluorescent [] = {
+ 0x13,0x05,
+ 0x01, 0x1a,
+ 0x02, 0x15,
+};
+
+uint8_t bf3a03_wb_daylight [] = {
+ 0x13,0x05,
+ 0x01, 0x15,
+ 0x02, 0x20,
+};
+
+uint8_t bf3a03_wb_cloudy [] = {
+ 0x13,0x05,
+ 0x01, 0x10,
+ 0x02, 0x2c,
+};
+
+
+// Exposure
+uint8_t bf3a03_exposure_neg6[] = {
+ 0x55, 0xb8,
+};
+
+uint8_t bf3a03_exposure_neg3[] = {
+0x55, 0x98,
+};
+
+uint8_t bf3a03_exposure_zero[] = {
+0x55, 0x00,
+};
+
+uint8_t bf3a03_exposure_pos3[] = {
+0x55, 0x18,
+};
+
+uint8_t bf3a03_exposure_pos6[] = {
+0x55, 0x38,
+};
+
+
+// Resolution
+uint8_t bf3a03_320x240[]= {
+ 0xda, 0x00,
+ 0xdb, 0xa2,
+ 0xdc, 0x00,
+ 0xdd, 0x7a,
+ 0xde, 0x00,
+ 0x4a, 0x98,
+ 0x0a, 0x21,
+ 0x10, 0x21,
+ 0x3d, 0x59,
+ 0x6b, 0x02,
+
+ 0x17, 0x00,
+ 0x18, 0xa0,
+ 0x19, 0x00,
+ 0x1a, 0x78,
+ 0x03, 0x00,
+ 0x12, 0x10,
+
+};
+
+uint8_t bf3a03_640x480[] = {
+ 0xda, 0x00,
+ 0xdb, 0xa2,
+ 0xdc, 0x00,
+ 0xdd, 0x7a,
+ 0xde, 0x00,
+ 0x4a, 0x98,
+ 0x0a, 0x21,
+ 0x10, 0x21,
+ 0x3d, 0x59,
+ 0x6b, 0x02,
+
+ 0x17, 0x00,
+ 0x18, 0xa0,
+ 0x19, 0x00,
+ 0x1a, 0x78,
+ 0x03, 0x00,
+ 0x12, 0x00,
+};
+
+uint8_t bf3a03_default_regs_init[] = {
+ //0x12,0x80,
+ 0x09,0x55,
+ 0x15,0x00,
+ 0x1e,0x70,//HV mirror
+
+ //Analog signals
+ 0x06,0x78,
+ 0x21,0x00,
+ 0x3e,0x37,
+ 0x29,0x2b,
+ 0x27,0x98,
+ 0x16,0x25,
+ 0x20,0x00,
+
+ //Clock
+ 0x2f,0x42,//ÓÐÂÌÉ«Ñ©»¨µãʱ£¬Ð´Îª0x4e;
+ 0x11,0x10,//·ÖƵ[1:0]
+ 0x1b,0x09,//±¶Æµ PLLʹÄÜÓë0x2f[1]¡¢0x1b[3]
+
+ //Êý¾ÝÊä³ö¸ñʽ
+ 0x4a,0x98,//Sub¡¢Win
+ 0x12,0x00,//YUV¡¢subʱÐò
+ 0x3a,0x00,//YUVʱÐò
+
+ //Manual
+ 0x13,0x08,//ÊÖдbit[3]GLB GAIN0
+ 0x01,0x14,
+ 0x02,0x20,
+ 0x8c,0x02,
+ 0x8d,0x4c,
+ 0x87,0x16,//GLB GAIN0
+
+ ///Auto
+ 0x13,0x07,
+
+ //Denoise
+ 0x70,0x0f,//²åֵģ¿é¿ª¹Ø
+ 0x3b,0x00,//È¥¸ñ×ÓÔëÉù
+ 0x71,0x0c,//²åֵģ¿é¿ª¹Ø
+ 0x73,0x27,//Denoise
+ 0x75,0x88,//Outdoor denoise
+ 0x76,0xd8,
+ 0x77,0x0a,//Low light denoise
+ 0x78,0xff,
+ 0x79,0x14,
+ 0x7a,0x24,//±ßÔµÔöÇ¿
+ 0x9e,0x04,
+ 0x7d,0x2a,//È¥×ϱß
+
+ //Gamma default
+ 0x39,0xa0,//Gamma offset
+ 0x3f,0xa0,
+ /*
+ 0x90,0x20,
+ 0x5f,0x03,//Dark_sel gamma
+ 0x40,0x22,
+ 0x41,0x23,
+ 0x42,0x28,
+ 0x43,0x25,
+ 0x44,0x1d,
+ 0x45,0x17,
+ 0x46,0x13,
+ 0x47,0x12,
+ 0x48,0x10,
+ 0x49,0x0d,
+ 0x4b,0x0b,
+ 0x4c,0x0b,
+ 0x4e,0x09,
+ 0x4f,0x07,
+ 0x50,0x06,
+
+ //Gamma low noise
+ 0x40,0x24,
+ 0x41,0x30,
+ 0x42,0x24,
+ 0x43,0x1d,
+ 0x44,0x1a,
+ 0x45,0x14,
+ 0x46,0x11,
+ 0x47,0x0e,
+ 0x48,0x0d,
+ 0x49,0x0c,
+ 0x4b,0x0b,
+ 0x4c,0x09,
+ 0x4e,0x09,
+ 0x4f,0x08,
+ 0x50,0x07, */
+
+ //Gamma smooth under over-ex
+ 0x40,0x18,
+ 0x41,0x2c,
+ 0x42,0x28,
+ 0x43,0x20,
+ 0x44,0x16,
+ 0x45,0x10,
+ 0x46,0x0f,
+ 0x47,0x0f,
+ 0x48,0x0e,
+ 0x49,0x0a,
+ 0x4b,0x0b,
+ 0x4c,0x09,
+ 0x4e,0x09,
+ 0x4f,0x08,
+ 0x50,0x06,
+ /*
+ //Gamma sharp and pretty ÇåÎúÁÁÀö
+ 0x40,0x19,
+ 0x41,0x1e,
+ 0x42,0x1f,
+ 0x43,0x20,
+ 0x44,0x1d,
+ 0x45,0x19,
+ 0x46,0x17,
+ 0x47,0x17,
+ 0x48,0x14,
+ 0x49,0x12,
+ 0x4b,0x0f,
+ 0x4c,0x0c,
+ 0x4e,0x08,
+ 0x4f,0x06,
+ 0x50,0x03,
+ */
+
+ //AE
+ 0x24,0x48,//·ÇA¹âY_target 0x50
+ 0x97,0x40,//A¹âY_target
+ 0x25,0x88,//AE_LOC
+ 0x81,0x00,//AE speed
+ 0x82,0x18,
+ 0x83,0x30,
+ 0x84,0x20,
+ 0x85,0x38,
+ 0x86,0x55,
+ 0x94,0x82,//¶ÔAEÁÁ¶ÈȨÖص÷½Ú
+ 0x80,0x92,//¶Ô¹ýÆصãÊýµ÷½Ú,bit[1]choose 50HZ or 60HZ step;
+ 0x98,0xbb,//¶Ô´°¿ÚȨÖص÷½Ú 0x8a
+ 0x89,0x6d,//Ö¡Âʵ÷½Úbit[7:3]
+ 0x8e,0x2c,
+ 0x8f,0x86,
+
+ //Banding
+ 0x2b,0x20,
+ 0x8a,0x93,//50HZ
+ 0x8b,0x7a,//60HZ
+ 0x92,0x6D,
+
+ //Color
+ 0x5a,0xec,//Outdoor color
+ 0x51,0x90,
+ 0x52,0x10,
+ 0x53,0x8d,
+ 0x54,0x88,
+ 0x57,0x82,
+ 0x58,0x8d,
+ 0x5a,0x7c,//A light color
+ 0x51,0x80,
+ 0x52,0x04,
+ 0x53,0x8d,
+ 0x54,0x88,
+ 0x57,0x82,
+ 0x58,0x8d,
+
+ //Color default
+ 0x5a,0x6c,//Indoor color
+ 0x51,0x93,
+ 0x52,0x04,
+ 0x53,0x8a,
+ 0x54,0x88,
+ 0x57,0x02,
+ 0x58,0x8d,
+
+ /*//ColorÉ«²ÊÑÞÀö
+ 0x5a,0x6c,//Indoor color
+ 0x51,0xa0,
+ 0x52,0x01,
+ 0x53,0x8d,
+ 0x54,0x85,
+ 0x57,0x01,
+ 0x58,0x90,
+
+ //Color·ôÉ«½ÏºÃ
+ 0x5a,0x6c,//Indoor color
+ 0x51,0x90,
+ 0x52,0x0a,
+ 0x53,0x84,
+ 0x54,0x05,
+ 0x57,0x05,
+ 0x58,0x87,
+
+ //ColorÉ«²Êµ­
+ 0x5a,0x6c,//Indoor color
+ 0x51,0x85,
+ 0x52,0x06,
+ 0x53,0x8a,
+ 0x54,0x81,
+ 0x57,0x02,
+ 0x58,0x8a,
+ */
+
+ //Saturation
+ 0xb0,0xa0,//A¹â±¥ºÍ¶È
+ 0xb1,0x26,
+ 0xb2,0x1c,
+ 0xb4,0xfd,
+ 0xb0,0x30,//·ÇA¹â±¥ºÍ¶È
+ 0xb1,0xc0, ///0xd8
+ 0xb2,0xa0, ///0xb0
+ 0xb4,0xf1,
+
+ //Contrast
+ 0x3c,0x40,//K1
+ 0x56,0x48,//K2 0xb4[4]Ñ¡Ôñnew or old
+ 0x4d,0x40,//K3
+ 0x59,0x40,//K4
+
+ /*//G gain´óһЩ
+ 0x35,0x56,//shading R
+ 0x65,0x36,//shading G
+ 0x66,0x44,//shading B
+ //AWB
+ 0x6a,0x91,
+ 0x23,0x44,
+ 0xa2,0x04,
+ 0xa3,0x26,
+ 0xa4,0x04,
+ 0xa5,0x26,
+ 0xa7,0x1a,
+ 0xa8,0x10,
+ 0xa9,0x1f,
+ 0xaa,0x16,
+ 0xab,0x16,
+ 0xac,0x30,
+ 0xad,0xf0,
+ 0xae,0x57,
+ 0xc5,0xaa,
+ 0xc7,0x38,
+ 0xc8,0x0d,
+ 0xc9,0x16,
+ 0xd3,0x09,
+ 0xd4,0x15,
+ 0xd0,0x00,
+ 0xd1,0x01,
+ 0xd2,0x18,
+ */
+
+ //G gainСһЩ
+ 0x35,0x46,//shading R
+ 0x65,0x38,//shading G
+ 0x66,0x42,//shading B
+ //AWB
+ 0x6a,0xd1,//AWB
+ 0x23,0x11,//G GAIN
+ 0xa2,0x0b,//·Ç»§Íâ BÉÏÏÂÏß
+ 0xa3,0x26,
+ 0xa4,0x04,//·Ç»§Íâ RÉÏÏÂÏß
+ 0xa5,0x26,
+ 0xa7,0x13,//B GAIN
+ 0xa8,0x8e,//R GAIN
+ 0xa9,0x16,//°×µã
+ 0xaa,0x16,
+ 0xab,0x16,
+ 0xac,0x30,
+ 0xad,0xf0,
+ 0xae,0x57,
+ 0xc5,0x66,
+ 0xc7,0x38,//»§ÄÚ¡¢Í⿪¹Øbit[4]
+ 0xc8,0x0d,//·Ç»§Íâ BÉÏÏÂÏß
+ 0xc9,0x16,
+ 0xd3,0x09,//·Ç»§Íâ RÉÏÏÂÏß
+ 0xd4,0x15,
+ 0xd0,0x00,//A¹âϵÄoffset
+ 0xd1,0x01,//·ÇA¹âϵÄoffset
+ 0xd2,0x18,
+ 0x20,0x40, ////[6] dependent or not
+ 0x09,0x5c,///0x55 0x57
+};
+
+uint8_t bf3a03_default_regs_exit[]={
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/cmos-subdev.c b/drivers/media/video/wmt_v4l2/sensors/cmos-subdev.c
new file mode 100755
index 00000000..9cbd1d36
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/cmos-subdev.c
@@ -0,0 +1,155 @@
+/*
+ * linux/drivers/media/video/wmt_v4l2/cmos/cmos-dev.c
+ * WonderMedia v4l cmos device driver
+ *
+ * Copyright c 2010 WonderMedia Technologies, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/videodev2.h>
+
+#include "cmos-subdev.h"
+#include "../wmt-vid.h"
+
+void cmos_init_8bit_addr(uint8_t *array_addr, size_t array_size, uint8_t i2c_addr)
+{
+ uint32_t addr, data;
+ int i;
+
+ for(i=0;i<array_size;i+=2)
+ {
+ addr = array_addr[i];
+ data = array_addr[i+1];
+ wmt_vid_i2c_write(i2c_addr,addr,data);
+ }
+}
+EXPORT_SYMBOL(cmos_init_8bit_addr);
+
+void cmos_init_16bit_addr(uint32_t *array_addr, size_t array_size, uint32_t i2c_addr)
+{
+ unsigned int addr,data,i;
+
+ for(i=0;i<array_size;i+=2)
+ {
+ addr = array_addr[i];
+ data = array_addr[i+1];
+ wmt_vid_i2c_write16addr(i2c_addr,addr,data);
+ }
+}
+EXPORT_SYMBOL(cmos_init_16bit_addr);
+
+void cmos_init_16bit_addr_16bit_data(uint32_t *array_addr, size_t array_size, uint32_t i2c_addr)
+{
+ unsigned int addr,data,i;
+
+ for(i=0;i<array_size;i+=2)
+ {
+ addr = array_addr[i];
+ data = array_addr[i+1];
+ wmt_vid_i2c_write16data(i2c_addr,addr,data);
+ }
+}
+EXPORT_SYMBOL(cmos_init_16bit_addr_16bit_data);
+
+static LIST_HEAD(__cmos_subdev_list);
+
+int cmos_register_sudbdev(struct cmos_subdev *sd)
+{
+ printk(KERN_INFO "register cmos: %s\n", sd->name);
+ list_add_tail(&sd->list, &__cmos_subdev_list);
+ return 0;
+}
+EXPORT_SYMBOL(cmos_register_sudbdev);
+
+void cmos_unregister_subdev(struct cmos_subdev *sd)
+{
+ printk(KERN_INFO "unregister cmos: %s\n", sd->name);
+ list_del(&sd->list);
+}
+EXPORT_SYMBOL(cmos_unregister_subdev);
+
+struct cmos_subdev *cmos_subdev_get_by_try(void)
+{
+ struct cmos_subdev *sd;
+ struct list_head *node, *next;
+
+ list_for_each_safe(node, next, &__cmos_subdev_list) {
+ sd = container_of(node, struct cmos_subdev, list);
+ if (!cmos_subdev_call(sd, identify)) {
+ return sd;
+ }
+ }
+ return NULL;
+}
+
+// below init all cmos
+
+extern struct cmos_subdev nmi;
+extern struct cmos_subdev gc0307, gc0308, gc0309, gc0311, gc0329, gc0328, gc2015, gc2035,gc2155;
+extern struct cmos_subdev ov2640, ov2643, ov2659, ov3640, ov3660, ov5640,ov7675,ov9740;
+extern struct cmos_subdev hi704, hi253,hi257;
+extern struct cmos_subdev siv121d,siv120d, sid130b;
+extern struct cmos_subdev sp0838, sp0718,sp0a19, sp2518;
+extern struct cmos_subdev bf3a03, bf3920,bf3703;
+extern struct cmos_subdev s5k5ca;
+extern struct cmos_subdev nt99141;
+
+static struct cmos_subdev *devices[] = {
+ &nmi,
+ /* SuperPix */
+ &sp0718, &sp0a19,&sp2518, &sp0838,
+ /* Seti */
+ &siv121d,&siv120d, &sid130b,
+ /* Hynix */
+ &hi253,&hi257,
+ /* Galaxyc */
+ &gc0307,&gc0308, &gc0309, &gc0328, &gc0329, &gc2035,&gc2155,
+ /* OminiVision */
+ &ov2659, &ov5640,&ov7675,&ov9740,&ov2643,
+ /*BYD*/
+ &bf3a03,&bf3920,&bf3703,
+ /* Samsung */
+ &s5k5ca,
+ /*Novatek*/
+ &nt99141,
+ NULL,
+};
+
+int cmos_subdev_init(void)
+{
+ int ret = 0;
+ struct cmos_subdev **sd = &devices[0];
+
+ while (*sd) {
+ ret = cmos_register_sudbdev(*sd);
+ if (ret) {
+ break;
+ }
+ sd++;
+ }
+
+ return ret;
+}
+
+void cmos_subdev_exit(void)
+{
+ struct cmos_subdev **sd = &devices[0];
+
+ while (*sd) {
+ cmos_unregister_subdev(*sd);
+ sd++;
+ }
+}
diff --git a/drivers/media/video/wmt_v4l2/sensors/cmos-subdev.h b/drivers/media/video/wmt_v4l2/sensors/cmos-subdev.h
new file mode 100755
index 00000000..18e0af36
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/cmos-subdev.h
@@ -0,0 +1,108 @@
+/*++
+ * linux/drivers/media/video/wmt_v4l2/cmos/cmos-dev.h
+ * WonderMedia v4l cmos device driver
+ *
+ * Copyright c 2010 WonderMedia Technologies, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+--*/
+
+#ifndef CMOS_DEV_H
+#define CMOS_DEV_H
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-mediabus.h>
+#include <asm/errno.h>
+
+#include <linux/videodev2_wmt.h>
+
+typedef struct {
+ int width;
+ int height;
+ int cmos_v_flip;
+ int cmos_h_flip;
+ unsigned int pix_color_format;
+ int is_rawdata;
+ int mode_switch;
+} cmos_init_arg_t;
+
+struct cmos_subdev;
+
+struct cmos_subdev_ops {
+ /* core */
+ int (*identify)(struct cmos_subdev *sd);
+ int (*init)(struct cmos_subdev *sd);
+ int (*exit)(struct cmos_subdev *sd);
+ int (*queryctrl)(struct cmos_subdev *sd, struct v4l2_queryctrl *qc);
+ int (*querymenu)(struct cmos_subdev *sd, struct v4l2_querymenu *qm);
+ int (*g_ctrl)(struct cmos_subdev *sd, struct v4l2_control *ctrl);
+ int (*s_ctrl)(struct cmos_subdev *sd, struct v4l2_control *ctrl);
+
+ /* video */
+ int (*enum_framesizes)(struct cmos_subdev *sd, struct v4l2_frmsizeenum *fsize);
+ int (*enum_mbus_fmt)(struct cmos_subdev *sd, unsigned int index, enum v4l2_mbus_pixelcode *code);
+ int (*g_mbus_fmt)(struct cmos_subdev *sd, struct v4l2_mbus_framefmt *fmt);
+ int (*try_mbus_fmt)(struct cmos_subdev *sd, struct v4l2_mbus_framefmt *fmt);
+ int (*s_mbus_fmt)(struct cmos_subdev *sd, struct v4l2_mbus_framefmt *fmt);
+ int (*g_parm)(struct cmos_subdev *sd, struct v4l2_streamparm *param);
+ int (*s_parm)(struct cmos_subdev *sd, struct v4l2_streamparm *param);
+ int (*cropcap)(struct cmos_subdev *sd, struct v4l2_cropcap *cc);
+ int (*g_crop)(struct cmos_subdev *sd, struct v4l2_crop *crop);
+ int (*s_crop)(struct cmos_subdev *sd, struct v4l2_crop *crop);
+};
+
+struct cmos_subdev {
+ char name[12];
+ int i2c_addr;
+ int id;
+ int chipid;
+ int max_width;
+ int max_height;
+ struct cmos_subdev_ops *ops;
+ struct list_head list;
+};
+
+#define cmos_subdev_call(sd, f, args...) \
+ (!(sd) ? -ENODEV : (((sd)->ops && (sd)->ops->f) ? \
+ (sd)->ops->f((sd) , ##args) : -ENOIOCTLCMD))
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#endif
+
+extern struct regulator *regulator_get(struct device *dev, const char *id);
+extern void regulator_put(struct regulator *regulator);
+extern int regulator_enable(struct regulator *regulator);
+extern int regulator_disable(struct regulator *regulator);
+extern int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV);
+extern int cmos_register_sudbdev(struct cmos_subdev *cmos_subdev);
+extern void cmos_unregister_subdev(struct cmos_subdev *cmos_subdev);
+
+extern struct cmos_subdev *cmos_subdev_get_by_try(void);
+extern int cmos_subdev_init(void);
+extern void cmos_subdev_exit(void);
+
+void cmos_init_8bit_addr(unsigned char* array_addr, unsigned int array_size, unsigned char i2c_addr);
+void cmos_init_16bit_addr(unsigned int * array_addr, unsigned int array_size, unsigned int i2c_addr);
+void cmos_init_16bit_addr_16bit_data(unsigned int * array_addr, unsigned int array_size, unsigned int i2c_addr);
+
+#endif /* ifndef CMOS_DEV_H */
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/flash/flash.c b/drivers/media/video/wmt_v4l2/sensors/flash/flash.c
new file mode 100755
index 00000000..7d5d3b2c
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/flash/flash.c
@@ -0,0 +1,54 @@
+/*
+ * ==========================================================================
+ *
+ * Filename: flash.c
+ *
+ * Description:
+ *
+ * Version: 0.01
+ * Created: 2013年07月02日 14时16分08秒
+ *
+ * Author: smmei (),
+ * Company:
+ *
+ * ==========================================================================
+ */
+
+#include <linux/init.h>
+#include <linux/errno.h>
+#include "flash.h"
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#endif
+
+static struct flash_dev *fl_devices[] = {
+ &flash_dev_gpio,
+ &flash_dev_eup2471,
+ &flash_dev_eup3618,
+ &flash_dev_ktd231,
+ &flash_dev_sgm3780,
+};
+
+struct flash_dev *flash_instantiation(void)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(fl_devices); i++) {
+ struct flash_dev *fl = fl_devices[i];
+ if (fl->init && fl->init() == 0)
+ return fl;
+ }
+ return NULL;
+}
+
+int flash_set_mode(struct flash_dev *fl, int mode)
+{
+ return (fl && fl->set_mode) ? fl->set_mode (mode) : -EINVAL;
+}
+
+void flash_destroy(struct flash_dev *fl)
+{
+ if (fl && fl->exit)
+ fl->exit();
+}
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/flash/flash.h b/drivers/media/video/wmt_v4l2/sensors/flash/flash.h
new file mode 100755
index 00000000..38051ebc
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/flash/flash.h
@@ -0,0 +1,36 @@
+/*
+ * --------------------------------------------------------------------------
+ *
+ * Filename: flash.h
+ *
+ * Description:
+ *
+ * Version: 0.01
+ * Created: 2013年07月02日 14时17分16秒
+ *
+ * Author: smmei (),
+ * Company:
+ * --------------------------------------------------------------------------
+ */
+#ifndef __FLASH_H__
+#define __FLASH_H__
+
+struct flash_dev {
+ char name[20];
+ int (*init)(void);
+ int (*set_mode)(int mode);
+ void (*exit)(void);
+};
+
+extern struct flash_dev flash_dev_gpio;
+extern struct flash_dev flash_dev_eup2471;
+extern struct flash_dev flash_dev_eup3618;
+extern struct flash_dev flash_dev_ktd231;
+extern struct flash_dev flash_dev_sgm3780;
+
+extern struct flash_dev *flash_instantiation(void);
+extern int flash_set_mode(struct flash_dev *fl, int mode);
+extern void flash_destroy(struct flash_dev *fl);
+
+#endif /* #ifndef __FLASH_H__ */
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/flash/flash_eup2471.c b/drivers/media/video/wmt_v4l2/sensors/flash/flash_eup2471.c
new file mode 100755
index 00000000..d45daea4
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/flash/flash_eup2471.c
@@ -0,0 +1,131 @@
+
+/* wmt.camera.flash <flash>:<en>:<flen>
+ * wmt.camera.flash eup2471:3:2
+ */
+
+#include <linux/gpio.h>
+#include <mach/wmt_env.h>
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "flash.h"
+
+struct eup2471_struct {
+ int gpio_en;
+ int gpio_flen;
+};
+
+static struct eup2471_struct *eup;
+
+#define EUP2471_I2CADDR 0x37
+
+/*
+ * TODO register flash as a subdev
+ */
+static int flash_dev_set_mode(int mode)
+{
+ if (!eup)
+ return -EINVAL;
+
+ switch (mode) {
+ case FLASH_MODE_OFF:
+ gpio_direction_output(eup->gpio_en, 0);
+ gpio_direction_output(eup->gpio_flen, 0);
+ break;
+ case FLASH_MODE_ON:
+ gpio_direction_output(eup->gpio_en, 1);
+ gpio_direction_output(eup->gpio_flen, 0);
+ msleep(1);
+ wmt_vid_i2c_write(EUP2471_I2CADDR, 0x00, 0x00);
+ wmt_vid_i2c_write(EUP2471_I2CADDR, 0x01, 0x33);
+ break;
+ case FLASH_MODE_STROBE:
+ gpio_direction_output(eup->gpio_en, 1);
+ gpio_direction_output(eup->gpio_flen, 1);
+ msleep(1);
+ gpio_direction_output(eup->gpio_flen, 0);
+ break;
+ case FLASH_MODE_TORCH:
+ gpio_direction_output(2, 0);
+ gpio_direction_output(3, 1);
+ msleep(1);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int parse_charger_param(void)
+{
+ char *dev_name = "eup2471";
+ char *env = "wmt.camera.flash";
+ char s[64];
+ size_t l = sizeof(s);
+ int gpio_en, gpio_flen;
+ int rc;
+
+ if (wmt_getsyspara(env, s, &l)) {
+ //pr_err("read %s fail!\n", env);
+ return -EINVAL;
+ }
+
+ if (strncmp(s, dev_name, strlen(dev_name))) {
+ return -EINVAL;
+ }
+
+ rc = sscanf(s, "eup2471:%d:%d", &gpio_en, &gpio_flen);
+ if (rc < 2) {
+ pr_err("bad uboot env: %s\n", env);
+ return -EINVAL;
+ }
+
+ rc = gpio_request(gpio_en, "flash eup2471 en");
+ if (rc) {
+ pr_err("flash en gpio(%d) request failed\n", gpio_en);
+ return rc;
+ }
+
+ rc = gpio_request(gpio_flen, "flash eup2471 flen");
+ if (rc) {
+ pr_err("flash flen gpio(%d) request failed\n", gpio_flen);
+ gpio_free(gpio_en);
+ return rc;
+ }
+
+ eup = kzalloc(sizeof(*eup), GFP_KERNEL);
+ if (!eup)
+ return -ENOMEM;
+
+ eup->gpio_en = gpio_en;
+ eup->gpio_flen = gpio_flen;
+
+ printk("flash eup2471 register ok: en %d, flen %d\n",
+ eup->gpio_en, eup->gpio_flen);
+ return 0;
+}
+
+static int flash_dev_init(void)
+{
+ if (eup)
+ return -EINVAL;
+
+ return parse_charger_param();
+}
+
+static void flash_dev_exit(void)
+{
+ if (eup) {
+ gpio_free(eup->gpio_en);
+ gpio_free(eup->gpio_flen);
+ kfree(eup);
+ eup = NULL;
+ }
+}
+
+struct flash_dev flash_dev_eup2471 = {
+ .name = "eup2471",
+ .init = flash_dev_init,
+ .set_mode = flash_dev_set_mode,
+ .exit = flash_dev_exit,
+};
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/flash/flash_eup3618.c b/drivers/media/video/wmt_v4l2/sensors/flash/flash_eup3618.c
new file mode 100755
index 00000000..f8b48ae8
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/flash/flash_eup3618.c
@@ -0,0 +1,124 @@
+
+/* wmt.camera.flash <flash>:<en>:<flen>
+ * wmt.camera.flash eup3618:14:15
+ */
+
+#include <linux/gpio.h>
+#include <mach/wmt_env.h>
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "flash.h"
+
+struct eup3618_struct {
+ int gpio_en;
+ int gpio_flen;
+};
+
+static struct eup3618_struct *eup;
+
+/*
+ * TODO register flash as a subdev
+ */
+static int flash_dev_set_mode(int mode)
+{
+ if (!eup)
+ return -EINVAL;
+
+ switch (mode) {
+ case FLASH_MODE_OFF:
+ gpio_direction_output(eup->gpio_en, 0);
+ gpio_direction_output(eup->gpio_flen, 0);
+ break;
+ case FLASH_MODE_ON:
+ gpio_direction_output(eup->gpio_en, 1);
+ gpio_direction_output(eup->gpio_flen, 1);
+ break;
+ case FLASH_MODE_STROBE:
+ gpio_direction_output(eup->gpio_en, 0);
+ gpio_direction_output(eup->gpio_flen, 0);
+ break;
+ case FLASH_MODE_TORCH:
+ gpio_direction_output(eup->gpio_en, 1);
+ gpio_direction_output(eup->gpio_flen, 1);
+ msleep(1);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int parse_charger_param(void)
+{
+ char *dev_name = "eup3618";
+ char *env = "wmt.camera.flash";
+ char s[64];
+ size_t l = sizeof(s);
+ int gpio_en, gpio_flen;
+ int rc;
+
+ if (wmt_getsyspara(env, s, &l)) {
+ // pr_err("read %s fail!\n", env);
+ return -EINVAL;
+ }
+
+ if (strncmp(s, dev_name, strlen(dev_name))) {
+ return -EINVAL;
+ }
+
+ rc = sscanf(s, "eup3618:%d:%d", &gpio_en, &gpio_flen);
+ if (rc < 2) {
+ pr_err("bad uboot env: %s\n", env);
+ return -EINVAL;
+ }
+
+ rc = gpio_request(gpio_en, "flash eup3618 en");
+ if (rc) {
+ pr_err("flash en gpio(%d) request failed\n", gpio_en);
+ return rc;
+ }
+
+ rc = gpio_request(gpio_flen, "flash eup3618 flen");
+ if (rc) {
+ pr_err("flash flen gpio(%d) request failed\n", gpio_flen);
+ gpio_free(gpio_en);
+ return rc;
+ }
+
+ eup = kzalloc(sizeof(*eup), GFP_KERNEL);
+ if (!eup)
+ return -ENOMEM;
+
+ eup->gpio_en = gpio_en;
+ eup->gpio_flen = gpio_flen;
+
+ printk("flash eup3618 register ok: en %d, flen %d\n",
+ eup->gpio_en, eup->gpio_flen);
+ return 0;
+}
+
+static int flash_dev_init(void)
+{
+ if (eup)
+ return -EINVAL;
+
+ return parse_charger_param();
+}
+
+static void flash_dev_exit(void)
+{
+ if (eup) {
+ gpio_free(eup->gpio_en);
+ gpio_free(eup->gpio_flen);
+ kfree(eup);
+ eup = NULL;
+ }
+}
+
+struct flash_dev flash_dev_eup3618 = {
+ .name = "eup3618",
+ .init = flash_dev_init,
+ .set_mode = flash_dev_set_mode,
+ .exit = flash_dev_exit,
+};
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/flash/flash_gpio.c b/drivers/media/video/wmt_v4l2/sensors/flash/flash_gpio.c
new file mode 100755
index 00000000..7e29306d
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/flash/flash_gpio.c
@@ -0,0 +1,103 @@
+/*
+ * ==========================================================================
+ *
+ * Filename: flash_gpio.c
+ *
+ * Description: wmt.camera.flash gpio:6
+ *
+ * Version: 0.01
+ * Created: 2013年07月02日 15时00分12秒
+ *
+ * Author: smmei (),
+ * Company:
+ *
+ * ==========================================================================
+ */
+
+#include <linux/gpio.h>
+#include <mach/wmt_env.h>
+#include "../cmos-subdev.h"
+#include "flash.h"
+
+static int fl_gpio = -1;
+
+static int flash_dev_set_mode(int mode)
+{
+ if (fl_gpio < 0)
+ return -EINVAL;
+
+ switch (mode) {
+ case FLASH_MODE_OFF:
+ gpio_direction_output(fl_gpio, 0);
+ break;
+ case FLASH_MODE_ON:
+ gpio_direction_output(fl_gpio, 1);
+ break;
+ case FLASH_MODE_STROBE:
+ gpio_direction_output(fl_gpio, 1);
+ break;
+ case FLASH_MODE_TORCH:
+ gpio_direction_output(fl_gpio, 1);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int parse_charger_param(void)
+{
+ char *dev_name = "gpio";
+ char *env = "wmt.camera.flash";
+ char s[64];
+ size_t l = sizeof(s);
+ int gpio;
+ int rc;
+
+ if (wmt_getsyspara(env, s, &l)) {
+ //pr_err("read %s fail!\n", env);
+ return -EINVAL;
+ }
+
+ if (strncmp(s, dev_name, strlen(dev_name))) {
+ return -EINVAL;
+ }
+
+ rc = sscanf(s, "gpio:%d", &gpio);
+ if (rc < 1) {
+ pr_err("bad uboot env: %s\n", env);
+ return -EINVAL;
+ }
+
+ rc = gpio_request(gpio, "flash gpio");
+ if (rc) {
+ pr_err("flash gpio(%d) request failed\n", gpio);
+ return rc;
+ }
+
+ fl_gpio = gpio;
+ printk("flash gpio%d register success\n", fl_gpio);
+ return 0;
+}
+
+static int flash_dev_init(void)
+{
+ return parse_charger_param();
+}
+
+static void flash_dev_exit(void)
+{
+ if (fl_gpio >= 0) {
+ gpio_free(fl_gpio);
+ fl_gpio = -1;
+ }
+}
+
+struct flash_dev flash_dev_gpio = {
+ .name = "gpio",
+ .init = flash_dev_init,
+ .set_mode = flash_dev_set_mode,
+ .exit = flash_dev_exit,
+};
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/flash/flash_ktd231.c b/drivers/media/video/wmt_v4l2/sensors/flash/flash_ktd231.c
new file mode 100755
index 00000000..a0926e4e
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/flash/flash_ktd231.c
@@ -0,0 +1,156 @@
+
+/* wmt.camera.flash <flash>:<en>:<flen>
+ * wmt.camera.flash ktd231:2
+ */
+
+#include <linux/gpio.h>
+#include <mach/wmt_env.h>
+#include <linux/pwm.h>
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "flash.h"
+#include <asm/io.h>
+
+struct ktd231_struct {
+ int gpio_en;
+ struct pwm_device *pwm;
+};
+
+static struct ktd231_struct *ktd;
+
+static void config_pwm1_pin(int enable)
+{
+ int val;
+ if(enable) {
+ val = readb(0xd8110200+WMT_MMAP_OFFSET);
+ val &= ~(1 << 7);
+ writeb(val, 0xd8110200+WMT_MMAP_OFFSET);
+ }else{
+ val = readb(0xd8110200+WMT_MMAP_OFFSET);
+ val |= (1 << 7);
+ writeb(val, 0xd8110200+WMT_MMAP_OFFSET);
+ }
+}
+
+static void enable_pwm1_32KHz(int enable)
+{
+ if(enable) {
+ pwm_config(ktd->pwm, 15625, 31250);// configuration output 32Khz
+ pwm_enable(ktd->pwm);
+ //config_pwm1_pin(0x01);
+ printk("enable 32khz output\n");
+ }else{
+ pwm_disable(ktd->pwm);
+ //config_pwm1_pin(0x00);
+ printk("disable 32khz output\n");
+ }
+}
+
+/*
+ * TODO register flash as a subdev
+ */
+static int flash_dev_set_mode(int mode)
+{
+ if (!ktd)
+ return -EINVAL;
+
+ switch (mode) {
+ case FLASH_MODE_OFF:
+ gpio_direction_output(ktd->gpio_en, 0);
+ enable_pwm1_32KHz(0);
+ break;
+ case FLASH_MODE_ON:
+ gpio_direction_output(ktd->gpio_en, 1);
+ enable_pwm1_32KHz(0);
+ break;
+ case FLASH_MODE_STROBE:
+ break;
+ case FLASH_MODE_TORCH:
+ gpio_direction_output(ktd->gpio_en, 0);
+ enable_pwm1_32KHz(1);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int parse_charger_param(void)
+{
+ char *dev_name = "ktd231";
+ char *env = "wmt.camera.flash";
+ char s[64];
+ size_t l = sizeof(s);
+ int gpio_en;
+ int rc;
+ struct pwm_device *pwm;
+
+ if (wmt_getsyspara(env, s, &l)) {
+ // pr_err("read %s fail!\n", env);
+ return -EINVAL;
+ }
+
+ if (strncmp(s, dev_name, strlen(dev_name))) {
+ return -EINVAL;
+ }
+
+ rc = sscanf(s, "ktd231:%d", &gpio_en);
+ if (rc < 1) {
+ pr_err("bad uboot env: %s\n", env);
+ return -EINVAL;
+ }
+
+ rc = gpio_request(gpio_en, "flash ktd231 en");
+ if (rc) {
+ pr_err("flash en gpio(%d) request failed\n", gpio_en);
+ return rc;
+ }
+ gpio_direction_output(gpio_en, 0);
+
+ pwm = pwm_request(1, "pwm_flash");
+ if (IS_ERR(pwm)) {
+ pr_err("unable to request PWM for flash\n");
+ gpio_free(gpio_en);
+ return -EINVAL;
+ }
+
+ ktd = kzalloc(sizeof(*ktd), GFP_KERNEL);
+ if (!ktd)
+ return -ENOMEM;
+
+ config_pwm1_pin(0x01);
+
+ ktd->gpio_en = gpio_en;
+ ktd->pwm = pwm;
+ enable_pwm1_32KHz(0);
+ printk("flash ktd231 register ok: en %d\n",ktd->gpio_en);
+
+ return 0;
+}
+
+static int flash_dev_init(void)
+{
+ if (ktd)
+ return -EINVAL;
+
+ return parse_charger_param();
+}
+
+static void flash_dev_exit(void)
+{
+ if (ktd) {
+ enable_pwm1_32KHz(0);
+ gpio_free(ktd->gpio_en);
+ pwm_free(ktd->pwm);
+ kfree(ktd);
+ ktd = NULL;
+ }
+}
+
+struct flash_dev flash_dev_ktd231 = {
+ .name = "ktd231",
+ .init = flash_dev_init,
+ .set_mode = flash_dev_set_mode,
+ .exit = flash_dev_exit,
+};
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/flash/flash_sgm3780.c b/drivers/media/video/wmt_v4l2/sensors/flash/flash_sgm3780.c
new file mode 100755
index 00000000..42353545
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/flash/flash_sgm3780.c
@@ -0,0 +1,207 @@
+
+/* wmt.camera.flash <flash>:<en>:<flen>:<torch_key>
+ * wmt.camera.flash sgm3780:3:2:12
+ */
+
+#include <linux/gpio.h>
+#include <mach/wmt_iomux.h>
+#include <linux/irq.h>
+#include <mach/wmt_env.h>
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "flash.h"
+
+enum key_status {
+ Idle,Pending,Pressure
+};
+
+struct key_struct{
+ int gpio;
+ enum key_status sts;
+ bool need_on;
+ struct work_struct work;
+};
+
+struct sgm3780_struct {
+ int gpio_en;
+ int gpio_flen;
+ struct key_struct torch_key;
+};
+
+static struct sgm3780_struct *eup;
+
+/*
+ * TODO register flash as a subdev
+ */
+static int flash_dev_set_mode(int mode)
+{
+ if (!eup)
+ return -EINVAL;
+
+ switch (mode) {
+ case FLASH_MODE_OFF:
+ gpio_direction_output(eup->gpio_en, 0);
+ gpio_direction_output(eup->gpio_flen, 0);
+ break;
+ case FLASH_MODE_ON:
+ gpio_direction_output(eup->gpio_en, 1);
+ gpio_direction_output(eup->gpio_flen, 1);
+ break;
+ case FLASH_MODE_STROBE:
+ gpio_direction_output(eup->gpio_en, 0);
+ gpio_direction_output(eup->gpio_flen, 0);
+ break;
+ case FLASH_MODE_TORCH:
+ gpio_direction_output(eup->gpio_en, 1);
+ gpio_direction_output(eup->gpio_flen, 0);
+ msleep(1);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void flash_key_scan_work(struct work_struct *work)
+{
+ struct sgm3780_struct *peup = container_of(work, struct sgm3780_struct,torch_key.work);
+
+ if(peup->torch_key.sts !=Idle){
+ pr_err("torch key is busy!!!\n");
+ goto work_done;
+ }
+ peup->torch_key.sts = Pending;
+ msleep(50);
+ if(peup->torch_key.sts == Pending){
+ if(!gpio_get_value(peup->torch_key.gpio)){
+ peup->torch_key.sts = Pressure;
+ }
+ }
+
+ if(peup->torch_key.sts == Pressure){
+ if(peup->torch_key.need_on == true){
+ flash_dev_set_mode(FLASH_MODE_TORCH);
+ peup->torch_key.need_on = false;
+ }else{
+ flash_dev_set_mode(FLASH_MODE_OFF);
+ peup->torch_key.need_on = true;
+ }
+ }
+
+work_done:
+ peup->torch_key.sts = Idle;
+ wmt_gpio_unmask_irq(peup->torch_key.gpio);
+}
+
+static irqreturn_t flash_key_ext_irq (int irq, void *dev_id)
+{
+ struct sgm3780_struct *peup = dev_id;
+
+ if(!is_gpio_irqenable(peup->torch_key.gpio) || !gpio_irqstatus(peup->torch_key.gpio))
+ return IRQ_NONE;
+
+ wmt_gpio_ack_irq(peup->torch_key.gpio);
+ wmt_gpio_mask_irq(peup->torch_key.gpio);
+ schedule_work(&peup->torch_key.work);
+
+ return IRQ_HANDLED;
+}
+
+static void key_setup_extirq(struct sgm3780_struct *peup)
+{
+ int ret;
+
+ wmt_gpio_setpull(peup->torch_key.gpio,WMT_GPIO_PULL_UP);
+ wmt_gpio_set_irq_type(peup->torch_key.gpio,IRQ_TYPE_EDGE_FALLING);
+ wmt_gpio_mask_irq(peup->torch_key.gpio);
+ wmt_gpio_ack_irq(peup->torch_key.gpio);
+ ret = request_irq(IRQ_GPIO, flash_key_ext_irq, IRQF_SHARED,"sgm3780_key_irq", peup);
+ if (ret) {
+ printk("request irq type IRQ_TYPE_EDGE_FALLING fail!\n");
+ return;
+ }
+ wmt_gpio_ack_irq(peup->torch_key.gpio);
+ wmt_gpio_unmask_irq(peup->torch_key.gpio);
+}
+
+static int parse_charger_param(void)
+{
+ char *dev_name = "sgm3780";
+ char *env = "wmt.camera.flash";
+ char s[64];
+ size_t l = sizeof(s);
+ int gpio_en, gpio_flen,gpio_key;
+ int rc;
+
+ if (wmt_getsyspara(env, s, &l)) {
+ // pr_err("read %s fail!\n", env);
+ return -EINVAL;
+ }
+
+ if (strncmp(s, dev_name, strlen(dev_name))) {
+ return -EINVAL;
+ }
+
+ rc = sscanf(s, "sgm3780:%d:%d:%d", &gpio_en, &gpio_flen,&gpio_key);
+ if (rc < 2) {
+ pr_err("bad uboot env: %s\n", env);
+ return -EINVAL;
+ }
+
+ rc = gpio_request(gpio_en, "flash sgm3780 en");
+ if (rc) {
+ pr_err("flash en gpio(%d) request failed\n", gpio_en);
+ return rc;
+ }
+
+ rc = gpio_request(gpio_flen, "flash sgm3780 flen");
+ if (rc) {
+ pr_err("flash flen gpio(%d) request failed\n", gpio_flen);
+ gpio_free(gpio_en);
+ return rc;
+ }
+
+ eup = kzalloc(sizeof(*eup), GFP_KERNEL);
+ if (!eup)
+ return -ENOMEM;
+
+ eup->gpio_en = gpio_en;
+ eup->gpio_flen = gpio_flen;
+ eup->torch_key.gpio = gpio_key;
+ eup->torch_key.need_on = true;
+ INIT_WORK(&eup->torch_key.work,flash_key_scan_work);
+ key_setup_extirq(eup);
+
+ printk("flash sgm3780 register ok: en %d, flen %d, key %d\n",
+ eup->gpio_en, eup->gpio_flen,eup->torch_key.gpio);
+
+ return 0;
+}
+
+static int flash_dev_init(void)
+{
+ if (eup)
+ return -EINVAL;
+
+ return parse_charger_param();
+}
+
+static void flash_dev_exit(void)
+{
+ if (eup) {
+ gpio_free(eup->gpio_en);
+ gpio_free(eup->gpio_flen);
+ cancel_work_sync(&eup->torch_key.work);
+ free_irq(IRQ_GPIO,eup);
+ kfree(eup);
+ eup = NULL;
+ }
+}
+
+struct flash_dev flash_dev_sgm3780 = {
+ .name = "sgm3780",
+ .init = flash_dev_init,
+ .set_mode = flash_dev_set_mode,
+ .exit = flash_dev_exit,
+};
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0307/gc0307-twv82222z2-f10.h b/drivers/media/video/wmt_v4l2/sensors/gc0307/gc0307-twv82222z2-f10.h
new file mode 100755
index 00000000..6e48cd00
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0307/gc0307-twv82222z2-f10.h
@@ -0,0 +1,624 @@
+#ifndef GC0307_H
+#define GC0307_H
+
+
+// Scene Mode
+uint8_t gc0307_scene_mode_auto[] = {
+
+};
+
+uint8_t gc0307_scene_mode_night[] = {
+
+};
+
+
+// White Balance
+uint8_t gc0307_wb_auto [] = {
+ 0xf0,0x00,
+// 0x41,0x2b,
+ 0xc7,0x4c,
+ 0xc8,0x40,
+ 0xc9,0x4a,
+};
+
+uint8_t gc0307_wb_incandescent [] = {
+ //bai re guang
+ 0xf0,0x00,
+// 0x41,0x2b,
+ 0xc7,0x48,
+ 0xc8,0x40,
+ 0xc9,0x5c,
+};
+
+uint8_t gc0307_wb_fluorescent [] = {
+ //ri guang deng
+ 0xf0,0x00,
+// 0x41,0x2b,
+ 0xc7,0x40,
+ 0xc8,0x42,
+ 0xc9,0x50,
+};
+
+uint8_t gc0307_wb_daylight [] = {
+ //tai yang guang
+ 0xf0,0x00,
+// 0x41,0x2b,
+ 0xc7,0x50,
+ 0xc8,0x45,
+ 0xc9,0x40,
+};
+
+uint8_t gc0307_wb_cloudy [] = {
+ 0xf0,0x00,
+// 0x41,0x2b,
+ 0xc7,0x5a,
+ 0xc8,0x42,
+ 0xc9,0x40,
+};
+
+
+// Exposure
+uint8_t gc0307_exposure_neg6[] = {
+ 0xf0,0x00,
+ 0x7a,0xd0,
+ 0xd1,0x38,
+};
+
+uint8_t gc0307_exposure_neg3[] = {
+ 0xf0,0x00,
+ 0x7a,0xe0,
+ 0xd1,0x48,
+};
+
+uint8_t gc0307_exposure_zero[] = {
+ 0xf0,0x00,
+ 0x7a,0x00,
+ 0xd1,0x50,
+};
+
+uint8_t gc0307_exposure_pos3[] = {
+ 0xf0,0x00,
+ 0x7a,0x20,
+ 0xd1,0x58,
+};
+
+uint8_t gc0307_exposure_pos6[] = {
+ 0xf0,0x00,
+ 0x7a,0x40,
+ 0xd1,0x68,
+};
+
+
+// Resolution
+uint8_t gc0307_320x240[]= {
+ 0x05,0x00,
+ 0x06,0x78,
+ 0x07,0x00,
+ 0x08,0xa0,
+ 0x09,0x00,
+ 0x0a,0xf8,
+ 0x0b,0x01,
+ 0x0c,0x48,
+ 0x45,0x24, // bit[7:2]=001001
+ 0x48,0x04, // bit[7]= 0
+ 0xe0,0x02,
+ 0xe1,0x01,
+ 0xe2,0x13,
+ 0xe3,0x0f,
+ 0xe8,0x1c,
+ 0xe9,0x35,
+ 0xea,0x17,
+ 0xeb,0x29,
+ 0xec,0x39,
+};
+
+uint8_t gc0307_640x480[] = {
+#if 0
+ 0x05,0x00,
+ 0x06,0x00,
+ 0x07,0x00,
+ 0x08,0x00,
+ 0x09,0x01,
+ 0x0a,0xe8,
+ 0x0b,0x02,
+ 0x0c,0x80,
+ 0x45,0x26, // bit[7:2]=001001
+ 0x48,0x84, // bit[7]=1
+ 0xe0,0x03,
+ 0xe1,0x02,
+ 0xe2,0x27,
+ 0xe3,0x1e,
+ 0xe8,0x3b,
+ 0xe9,0x6e,
+ 0xea,0x2c,
+ 0xeb,0x50,
+ 0xec,0x73,
+#endif
+};
+
+uint8_t gc0307_default_regs_init[] = {
+
+
+
+
+ 0x43 ,0x00,
+ 0x44 ,0xa2,
+
+
+
+ 0x40 ,0x10,
+ 0x41 ,0x00,
+ 0x42 ,0x10,
+ 0x47 ,0x00,
+ 0x48 ,0xc3,
+ 0x49 ,0x00,
+ 0x4a ,0x00,
+ 0x4b ,0x00,
+ 0x4E ,0x22,
+ 0x4F ,0x01,
+
+
+ 0x1C ,0x00,
+ 0x1D ,0x00,
+
+ 0x11 ,0x05,
+ 0x01 ,0xfa,
+ 0x02 ,0x70,
+ 0x1C ,0x00,
+ 0x1D ,0x00,
+ 0x10 ,0x01,
+ 0x11 ,0x05,
+
+
+
+ 0x05 ,0x00,
+ 0x06 ,0x00,
+ 0x07 ,0x00,
+ 0x08 ,0x00,
+ 0x09 ,0x01,
+ 0x0A ,0xE8,
+ 0x0B ,0x02,
+ 0x0C ,0x80,
+
+
+ 0x0D ,0x22,
+
+ 0x0E ,0x02,
+
+
+ 0x12 ,0x70,
+ 0x13 ,0x00,
+ 0x14 ,0x00,
+ 0x15 ,0xba,
+ 0x16 ,0x13,
+ 0x17 ,0x52,
+
+
+ 0x1E ,0x41,
+ 0x1F ,0x32,
+
+
+ 0x47 ,0x00,
+
+ 0x19 ,0x06,
+ 0x1a ,0x06,
+
+ 0x31 ,0x00,
+ 0x3B ,0x00,
+
+ 0x59 ,0x0f,
+ 0x58 ,0xc6,
+ 0x57 ,0x08,
+ 0x56 ,0x77,
+
+
+ 0x35 ,0xd8,
+
+ 0x36 ,0x40,
+
+ 0x3C ,0x00,
+ 0x3D ,0x00,
+ 0x3E ,0x00,
+ 0x3F ,0x00,
+
+ 0xb5 ,0x70,
+ 0xb6 ,0x40,
+ 0xb7 ,0x00,
+ 0xb8 ,0x38,
+ 0xb9 ,0xc3,
+ 0xba ,0x0f,
+
+ 0x7e ,0x35,
+ 0x7f ,0x86,
+
+ 0x5c ,0x68,
+ 0x5d ,0x78,
+
+
+
+ 0x61 ,0x80,
+ 0x63 ,0x80,
+ 0x65 ,0x98,
+ 0x67 ,0x80,
+ 0x68 ,0x18,
+
+
+ 0x69 ,0x58,
+ 0x6A ,0xf6,
+ 0x6B ,0xfb,
+ 0x6C ,0xf4,
+ 0x6D ,0x5a,
+ 0x6E ,0xe6,
+
+ 0x6f ,0x00,
+
+
+ 0x70 ,0x14,
+ 0x71 ,0x1c,
+ 0x72 ,0x20,
+
+ 0x73 ,0x10,
+ 0x74 ,0x3c,
+ 0x75 ,0x52,
+
+
+ 0x7d ,0x2f,
+ 0x80 ,0x0c,
+ 0x81 ,0x0c,
+ 0x82 ,0x44,
+
+
+ 0x83 ,0x18,
+ 0x84 ,0x18,
+ 0x85 ,0x04,
+ 0x87 ,0x34,
+
+
+
+ 0x88 ,0x04,
+ 0x89 ,0x01,
+ 0x8a ,0x50,
+ 0x8b ,0x50,
+ 0x8c ,0x07,
+
+ 0x50 ,0x0c,
+ 0x5f ,0x3c,
+
+ 0x8e ,0x02,
+ 0x86 ,0x02,
+
+ 0x51 ,0x20,
+ 0x52 ,0x08,
+ 0x53 ,0x00,
+
+
+
+
+ 0x77 ,0x80,
+ 0x78 ,0x00,
+ 0x79 ,0x00,
+ 0x7a ,0x00,
+ 0x7b ,0x40,
+ 0x7c ,0x00,
+
+
+ 0xa0 ,0x40,
+ 0xa1 ,0x40,
+ 0xa2 ,0x34,
+ 0xa3 ,0x34,
+
+ 0xa4 ,0xc8,
+ 0xa5 ,0x02,
+ 0xa6 ,0x28,
+ 0xa7 ,0x02,
+
+
+ 0xa8 ,0xee,
+ 0xa9 ,0x12,
+ 0xaa ,0x01,
+ 0xab ,0x20,
+ 0xac ,0xf0,
+ 0xad ,0x10,
+
+
+ 0xae ,0x18,
+ 0xaf ,0x74,
+ 0xb0 ,0xe0,
+ 0xb1 ,0x20,
+ 0xb2 ,0x6c,
+ 0xb3 ,0x40,
+ 0xb4 ,0x04,
+
+
+ 0xbb ,0x42,
+ 0xbc ,0x60,
+ 0xbd ,0x50,
+ 0xbe ,0x50,
+
+ 0xbf ,0x0c,
+ 0xc0 ,0x06,
+ 0xc1 ,0x60,
+ 0xc2 ,0xf1,
+ 0xc3 ,0x40,
+ 0xc4 ,0x1c,
+ 0xc5 ,0x56,
+ 0xc6 ,0x1d,
+
+ 0xca ,0x70,
+ 0xcb ,0x70,
+ 0xcc ,0x78,
+
+ 0xcd ,0x80,
+ 0xce ,0x80,
+ 0xcf ,0x80,
+
+
+ 0x20 ,0x06,
+ 0x21 ,0xc0,
+ 0x22 ,0x60,
+ 0x23 ,0x88,
+ 0x24 ,0x96,
+ 0x25 ,0x30,
+ 0x26 ,0xd0,
+ 0x27 ,0x00,
+
+ /*0x28 ,0x01,
+ 0x29 ,0x40,
+ 0x2a ,0x01,
+ 0x2b ,0x90,
+ 0x2c ,0x02,
+ 0x2d ,0x80,
+ 0x2e ,0x04,
+ 0x2f ,0x60, */
+
+ 0x28 ,0x01,
+ 0x29 ,0x90,
+ 0x2a ,0x02,
+ 0x2b ,0x58,
+ 0x2c ,0x02,
+ 0x2d ,0x58,
+ 0x2e ,0x05,
+ 0x2f ,0x78,
+
+ 0x30 ,0x20,
+ 0x31 ,0x00,
+ 0x32 ,0x1c,
+ 0x33 ,0x90,
+ 0x34 ,0x10,
+
+ 0xd0 ,0x34,
+
+ 0xd1 ,0x50,
+ 0xd2 ,0x61,
+ 0xd4 ,0x64,
+ 0xd5 ,0x01,
+ 0xd6 ,0x64,
+ 0xd7 ,0x03,
+ 0xd8 ,0x02,
+
+ 0xdd ,0x22,
+
+
+ 0xe0 ,0x03,
+ 0xe1 ,0x02,
+ 0xe2 ,0x27,
+ 0xe3 ,0x1e,
+ 0xe8 ,0x3b,
+ 0xe9 ,0x6e,
+ 0xea ,0x2c,
+ 0xeb ,0x50,
+ 0xec ,0x73,
+
+
+ 0xed ,0x00,
+ 0xee ,0x00,
+ 0xef ,0x00,
+
+
+ 0xf0 ,0x01,
+
+ 0x00 ,0x20,
+ 0x01 ,0x20,
+ 0x02 ,0x20,
+ 0x03 ,0x20,
+ 0x04 ,0x78,
+ 0x05 ,0x78,
+ 0x06 ,0x78,
+ 0x07 ,0x78,
+
+
+
+ 0x10 ,0x04,
+ 0x11 ,0x04,
+ 0x12 ,0x04,
+ 0x13 ,0x04,
+ 0x14 ,0x01,
+ 0x15 ,0x01,
+ 0x16 ,0x01,
+ 0x17 ,0x01,
+
+
+ 0x20 ,0x00,
+ 0x21 ,0x00,
+ 0x22 ,0x00,
+ 0x23 ,0x00,
+ 0x24 ,0x00,
+ 0x25 ,0x00,
+ 0x26 ,0x00,
+ 0x27 ,0x00,
+
+ 0x40 ,0x11,
+
+
+ 0x45 ,0x06,
+ 0x46 ,0x06,
+ 0x47 ,0x05,
+
+ 0x48 ,0x04,
+ 0x49 ,0x03,
+ 0x4a ,0x03,
+
+
+ 0x62 ,0xd8,
+ 0x63 ,0x24,
+ 0x64 ,0x24,
+ 0x65 ,0x24,
+ 0x66 ,0xd8,
+ 0x67 ,0x24,
+
+ 0x5a ,0x00,
+ 0x5b ,0x00,
+ 0x5c ,0x00,
+ 0x5d ,0x00,
+ 0x5e ,0x00,
+ 0x5f ,0x00,
+
+
+
+
+ 0x69 ,0x03,
+
+
+ 0x70 ,0x5d,
+ 0x71 ,0xed,
+ 0x72 ,0xff,
+ 0x73 ,0xe5,
+ 0x74 ,0x5f,
+ 0x75 ,0xe6,
+
+
+ 0x76 ,0x41,
+ 0x77 ,0xef,
+ 0x78 ,0xff,
+ 0x79 ,0xff,
+ 0x7a ,0x5f,
+ 0x7b ,0xfa,
+
+
+
+
+ 0x7e ,0x00,
+ 0x7f ,0x00,
+ 0x80 ,0xc8,
+ 0x81 ,0x06,
+ 0x82 ,0x08,
+
+#if 0
+ 0x83 ,0x23,
+ 0x84 ,0x38,
+ 0x85 ,0x4F,
+ 0x86 ,0x61,
+ 0x87 ,0x72,
+ 0x88 ,0x80,
+ 0x89 ,0x8D,
+ 0x8a ,0xA2,
+ 0x8b ,0xB2,
+ 0x8c ,0xC0,
+ 0x8d ,0xCA,
+ 0x8e ,0xD3,
+ 0x8f ,0xDB,
+ 0x90 ,0xE2,
+ 0x91 ,0xED,
+ 0x92 ,0xF6,
+ 0x93 ,0xFD,
+#endif
+
+0x83 ,0x13,
+0x84 ,0x23,
+0x85 ,0x35,
+0x86 ,0x44,
+0x87 ,0x53,
+0x88 ,0x60,
+0x89 ,0x6D,
+0x8a ,0x84,
+0x8b ,0x98,
+0x8c ,0xaa,
+0x8d ,0xb8,
+0x8e ,0xc6,
+0x8f ,0xd1,
+0x90 ,0xdb,
+0x91 ,0xea,
+0x92 ,0xf5,
+0x93 ,0xFb,
+
+
+ 0x94 ,0x04,
+ 0x95 ,0x0E,
+ 0x96 ,0x1B,
+ 0x97 ,0x28,
+ 0x98 ,0x35,
+ 0x99 ,0x41,
+ 0x9a ,0x4E,
+ 0x9b ,0x67,
+ 0x9c ,0x7E,
+ 0x9d ,0x94,
+ 0x9e ,0xA7,
+ 0x9f ,0xBA,
+ 0xa0 ,0xC8,
+ 0xa1 ,0xD4,
+ 0xa2 ,0xE7,
+ 0xa3 ,0xF4,
+ 0xa4 ,0xFA,
+
+
+ 0xf0 ,0x00,
+ 0x40 ,0x7e,
+ 0x41 ,0x2F,
+
+
+ 0x0f, 0xb2,
+ 0x45, 0x27,
+ 0x47, 0x2c,
+
+
+
+
+#if 0
+
+ 0x0f, 0xb2,
+ 0x45, 0x27,
+ 0x47, 0x2c,
+
+
+ 0x0f, 0xa2,
+ 0x45, 0x26,
+ 0x47, 0x28,
+
+
+ 0x0f, 0x92,
+ 0x45, 0x25,
+ 0x47, 0x24,
+
+
+ 0x0f, 0x82,
+ 0x45, 0x24,
+ 0x47, 0x20,
+#endif
+
+
+
+ 0x43 ,0x40,
+ 0x44 ,0xE2,
+
+ 0xff,0xff,
+
+
+
+ 0x01 ,0xfa ,
+ 0x02 ,0x70,
+ 0x10 ,0x01 ,
+ 0xd6 ,0x64,
+ 0x28 ,0x02,
+ 0x29 ,0x58,
+ 0x2a ,0x03,
+ 0x2b ,0x20,
+ 0x2c ,0x03,
+ 0x2d ,0xe8 ,
+ 0x2e ,0x09,
+ 0x2f ,0xc4,
+
+ 0x7e ,0x65,
+ 0x7f ,0x56,
+
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0307/gc0307.c b/drivers/media/video/wmt_v4l2/sensors/gc0307/gc0307.c
new file mode 100755
index 00000000..7d46e1de
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0307/gc0307.c
@@ -0,0 +1,371 @@
+
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "gc0307.h"
+
+#define sensor_write_array(sd, arr, sz) \
+ cmos_init_8bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write(sd->i2c_addr, reg, val)
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint8_t *regs;
+ size_t size;
+};
+
+struct sensor_flip{
+ int h_flip;
+ int v_flip;
+ int cnt;
+}gc0307_flip;
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+// CMOS_WIN_SIZE("QVGA", 320, 240, gc0307_320x240),
+ CMOS_WIN_SIZE("VGA", 640, 480, gc0307_640x480),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint8_t *regs,data;
+
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ sensor_write(sd, 0xf0, 0x00);
+ data = sensor_read(sd, 0x41);
+ data |=0x04;
+ sensor_write(sd, 0x41, data);
+ regs = gc0307_wb_auto;
+ size = ARRAY_SIZE(gc0307_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ sensor_write(sd, 0xf0, 0x00);
+ data = sensor_read(sd, 0x41);
+ data &=0xfb;
+ sensor_write(sd, 0x41, data);
+ regs = gc0307_wb_incandescent;
+ size = ARRAY_SIZE(gc0307_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ sensor_write(sd, 0xf0, 0x00);
+ data = sensor_read(sd, 0x41);
+ data &=0xfb;
+ sensor_write(sd, 0x41, data);
+ regs = gc0307_wb_daylight;
+ size = ARRAY_SIZE(gc0307_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ sensor_write(sd, 0xf0, 0x00);
+ data = sensor_read(sd, 0x41);
+ data &=0xfb;
+ sensor_write(sd, 0x41, data);
+ regs = gc0307_wb_cloudy;
+ size = ARRAY_SIZE(gc0307_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ sensor_write(sd, 0xf0, 0x00);
+ data = sensor_read(sd, 0x41);
+ data &=0xfb;
+ sensor_write(sd, 0x41, data);
+ regs = gc0307_wb_fluorescent;
+ size = ARRAY_SIZE(gc0307_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ mdelay(80);
+ return 0;
+}
+
+static int sensor_s_scenemode(struct cmos_subdev *sd, enum v4l2_scene_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case SCENE_MODE_AUTO:
+ regs = gc0307_scene_mode_auto;
+ size = ARRAY_SIZE(gc0307_scene_mode_auto);
+ break;
+ case SCENE_MODE_NIGHTSHOT:
+ regs = gc0307_scene_mode_night;
+ size = ARRAY_SIZE(gc0307_scene_mode_night);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_exposure(struct cmos_subdev *sd, int value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case -2:
+ regs = gc0307_exposure_neg6;
+ size = ARRAY_SIZE(gc0307_exposure_neg6);
+ break;
+ case -1:
+ regs = gc0307_exposure_neg3;
+ size = ARRAY_SIZE(gc0307_exposure_neg3);
+ break;
+ case 0:
+ regs = gc0307_exposure_zero;
+ size = ARRAY_SIZE(gc0307_exposure_zero);
+ break;
+ case 1:
+ regs = gc0307_exposure_pos3;
+ size = ARRAY_SIZE(gc0307_exposure_pos3);
+ break;
+ case 2:
+ regs = gc0307_exposure_pos6;
+ size = ARRAY_SIZE(gc0307_exposure_pos6);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_do_flip(struct cmos_subdev *sd)
+{
+ if ((gc0307_flip.h_flip == 0) &&(gc0307_flip.v_flip == 0)){
+ sensor_write( sd, 0x0f, 0xb2);
+ sensor_write( sd, 0x45, 0x27);
+ sensor_write( sd, 0x47, 0x2c);
+ }else if ((gc0307_flip.h_flip == 0) &&(gc0307_flip.v_flip == 1)){
+ sensor_write( sd, 0x0f, 0xa2);
+ sensor_write( sd, 0x45, 0x26);
+ sensor_write( sd, 0x47, 0x28);
+ }else if ((gc0307_flip.h_flip == 1) &&(gc0307_flip.v_flip == 0)){
+ sensor_write( sd, 0x0f, 0x92);
+ sensor_write( sd, 0x45, 0x25);
+ sensor_write( sd, 0x47, 0x24);
+ }else if ((gc0307_flip.h_flip == 1) &&(gc0307_flip.v_flip == 1)){
+ sensor_write( sd, 0x0f, 0x82);
+ sensor_write( sd, 0x45, 0x24);
+ sensor_write( sd, 0x47, 0x20);
+ }
+
+ msleep(100);
+ return 0;
+}
+
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+ gc0307_flip.cnt ++;
+
+ switch (value) {
+ case 0:
+ gc0307_flip.h_flip=0;
+ break;
+ case 1:
+ gc0307_flip.h_flip=1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if(gc0307_flip.cnt==2)
+ sensor_do_flip(sd);
+
+ return 0;
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+ gc0307_flip.cnt ++;
+
+ switch (value) {
+ case 0:
+ gc0307_flip.v_flip=0;
+ break;
+ case 1:
+ gc0307_flip.v_flip=1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if(gc0307_flip.cnt==2)
+ sensor_do_flip(sd);
+
+ return 0;
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ case V4L2_CID_EXPOSURE:
+ return v4l2_ctrl_query_fill(qc, -2, 2, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return sensor_s_scenemode(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return sensor_s_exposure(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ default:
+ case WMT_V4L2_CID_CAMERA_ANTIBANDING:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win) {
+ pr_err("%s, s_mbus_fmt failed, width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, win->regs, win->size);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return 0;
+}
+
+static int sensor_enum_framesizes(struct cmos_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ const struct cmos_win_size *win = &cmos_supported_win_sizes[index];
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ return (sensor_read(sd, 0) == sd->id) ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ if (sensor_identify(sd)) {
+ return -1;
+ }
+ gc0307_flip.cnt=0;
+ sensor_write_array(sd, gc0307_default_regs_init,
+ ARRAY_SIZE(gc0307_default_regs_init));
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ return 0;
+}
+
+static struct cmos_subdev_ops gc0307_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+ .enum_framesizes = sensor_enum_framesizes,
+};
+
+struct cmos_subdev gc0307 = {
+ .name = "gc0307",
+ .i2c_addr = 0x21,
+ .id = 0x99,
+ .max_width = 640,
+ .max_height = 480,
+ .ops = &gc0307_ops,
+};
+
+#if 0
+static int __init gc0307_init(void)
+{
+ return cmos_register_sudbdev(&gc0307);
+}
+
+static void __exit gc0307_exit(void)
+{
+ return cmos_unregister_subdev(&gc0307);
+}
+
+module_init(gc0307_init);
+module_exit(gc0307_exit);
+
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0307/gc0307.h b/drivers/media/video/wmt_v4l2/sensors/gc0307/gc0307.h
new file mode 100755
index 00000000..9c58f202
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0307/gc0307.h
@@ -0,0 +1,567 @@
+#ifndef GC0307_H
+#define GC0307_H
+
+
+// Scene Mode
+uint8_t gc0307_scene_mode_auto[] = {
+
+};
+
+uint8_t gc0307_scene_mode_night[] = {
+
+};
+
+
+// White Balance
+uint8_t gc0307_wb_auto [] = {
+ 0xf0,0x00,
+// 0x41,0x2b,
+ 0xc7,0x4c,
+ 0xc8,0x40,
+ 0xc9,0x4a,
+};
+
+uint8_t gc0307_wb_incandescent [] = {
+ //bai re guang
+ 0xf0,0x00,
+// 0x41,0x2b,
+ 0xc7,0x48,
+ 0xc8,0x40,
+ 0xc9,0x5c,
+};
+
+uint8_t gc0307_wb_fluorescent [] = {
+ //ri guang deng
+ 0xf0,0x00,
+// 0x41,0x2b,
+ 0xc7,0x40,
+ 0xc8,0x42,
+ 0xc9,0x50,
+};
+
+uint8_t gc0307_wb_daylight [] = {
+ //tai yang guang
+ 0xf0,0x00,
+// 0x41,0x2b,
+ 0xc7,0x50,
+ 0xc8,0x45,
+ 0xc9,0x40,
+};
+
+uint8_t gc0307_wb_cloudy [] = {
+ 0xf0,0x00,
+// 0x41,0x2b,
+ 0xc7,0x5a,
+ 0xc8,0x42,
+ 0xc9,0x40,
+};
+
+
+// Exposure
+uint8_t gc0307_exposure_neg6[] = {
+ 0xf0,0x00,
+ 0x7a,0xd0,
+ 0xd1,0x38,
+};
+
+uint8_t gc0307_exposure_neg3[] = {
+ 0xf0,0x00,
+ 0x7a,0xe0,
+ 0xd1,0x48,
+};
+
+uint8_t gc0307_exposure_zero[] = {
+ 0xf0,0x00,
+ 0x7a,0x00,
+ 0xd1,0x50,
+};
+
+uint8_t gc0307_exposure_pos3[] = {
+ 0xf0,0x00,
+ 0x7a,0x20,
+ 0xd1,0x58,
+};
+
+uint8_t gc0307_exposure_pos6[] = {
+ 0xf0,0x00,
+ 0x7a,0x40,
+ 0xd1,0x68,
+};
+
+
+// Resolution
+uint8_t gc0307_320x240[]= {
+ 0x05,0x00,
+ 0x06,0x78,
+ 0x07,0x00,
+ 0x08,0xa0,
+ 0x09,0x00,
+ 0x0a,0xf8,
+ 0x0b,0x01,
+ 0x0c,0x48,
+ 0x45,0x24, // bit[7:2]=001001
+ 0x48,0x04, // bit[7]= 0
+ 0xe0,0x02,
+ 0xe1,0x01,
+ 0xe2,0x13,
+ 0xe3,0x0f,
+ 0xe8,0x1c,
+ 0xe9,0x35,
+ 0xea,0x17,
+ 0xeb,0x29,
+ 0xec,0x39,
+};
+
+uint8_t gc0307_640x480[] = {
+#if 0
+ 0x05,0x00,
+ 0x06,0x00,
+ 0x07,0x00,
+ 0x08,0x00,
+ 0x09,0x01,
+ 0x0a,0xe8,
+ 0x0b,0x02,
+ 0x0c,0x80,
+ 0x45,0x26, // bit[7:2]=001001
+ 0x48,0x84, // bit[7]=1
+ 0xe0,0x03,
+ 0xe1,0x02,
+ 0xe2,0x27,
+ 0xe3,0x1e,
+ 0xe8,0x3b,
+ 0xe9,0x6e,
+ 0xea,0x2c,
+ 0xeb,0x50,
+ 0xec,0x73,
+#endif
+};
+
+uint8_t gc0307_default_regs_init[] = {
+
+ // Initail Sequence Write In.
+ //========= close output
+ 0x43,0x00,
+ 0x44,0xa2,
+
+ //========= close some functions
+ // open them after configure their parmameters
+ 0x40,0x10,
+ 0x41,0x00,
+ 0x42,0x10,
+ 0x47,0x00,//mode1,
+ 0x48,0xc1,//mode2,
+ 0x49,0x00,//dither_mode
+ 0x4a,0x00,//clock_gating_en
+ 0x4b,0x00,//mode_reg3
+ 0x4E,0x23,//sync mode
+ 0x4F,0x01,//AWB, AEC, every N frame
+
+ //========= frame timing
+ 0x01,0x6a,//HB
+ 0x02,0x70,//VB//0c
+ 0x1C,0x00,//Vs_st
+ 0x1D,0x00,//Vs_et
+ 0x10,0x00,//high 4 bits of VB, HB
+ 0x11,0x05,//row_tail, AD_pipe_number
+
+ 0x03,0x01,//row_start
+ 0x04,0x2c,
+
+ //========= windowing
+ 0x05,0x00,//row_start
+ 0x06,0x00,
+ 0x07,0x00,//col start
+ 0x08,0x00,
+ 0x09,0x01,//win height
+ 0x0A,0xE8,
+ 0x0B,0x02,//win width, pixel array only 640
+ 0x0C,0x80,
+
+ //========= analog
+ 0x0D,0x22,//rsh_width
+ 0x0E,0x02,//CISCTL mode2,
+ 0x0F,0xb2,//CISCTL mode1
+
+
+ 0x12,0x70,//7 hrst, 6_4 darsg,
+ 0x13,0x00,//7 CISCTL_restart, 0 apwd
+ 0x14,0x00,//NA
+ 0x15,0xba,//7_4 vref
+ 0x16,0x13,//5to4 _coln_r, __1to0__da18
+ 0x17,0x52,//opa_r, ref_r, sRef_r
+ //0x18,0xc0,//analog_mode, best case for left band.
+ 0x18,0x00,
+
+ 0x1E,0x0d,//tsp_width
+ 0x1F,0x32,//sh_delay
+
+ //========= offset
+ 0x47,0x00, //7__test_image, __6__fixed_pga, //__5__auto_DN,__4__CbCr_fix,
+ //__3to2__dark_sequence, __1__allow_pclk_vcync, __0__LSC_test_image
+ 0x19,0x06, //pga_o
+ 0x1a,0x06, //pga_e
+
+ 0x31,0x00, //4 //pga_oFFset , high 8bits of 11bits
+ 0x3B,0x00, //global_oFFset, low 8bits of 11bits
+
+ 0x59,0x0f, //offset_mode
+ 0x58,0x88, //DARK_VALUE_RATIO_G, DARK_VALUE_RATIO_RB
+ 0x57,0x08, //DARK_CURRENT_RATE
+ 0x56,0x77, //PGA_OFFSET_EVEN_RATIO, PGA_OFFSET_ODD_RATIO
+
+ //========= blk
+ 0x35,0xd8, //blk_mode
+
+ 0x36,0x40,
+
+ 0x3C,0x00,
+ 0x3D,0x00,
+ 0x3E,0x00,
+ 0x3F,0x00,
+
+ 0xb5,0x70,
+ 0xb6,0x40,
+ 0xb7,0x00,
+ 0xb8,0x38,
+ 0xb9,0xc3,
+ 0xba,0x0f,
+
+ 0x7e,0x45,
+ 0x7f,0x66,
+
+ 0x5c,0x48,//78
+ 0x5d,0x58,//88
+
+
+ //========= manual_gain
+ 0x61,0x80,//manual_gain_g1
+ 0x63,0x80,//manual_gain_r
+ 0x65,0x98,//manual_gai_b, 0xa0=1.25, 0x98=1.1875
+ 0x67,0x80,//manual_gain_g2
+ 0x68,0x18,//global_manual_gain 2.4bits
+
+ //=========CC _R
+ 0x69,0x58, //54//58
+ 0x6A,0xf6, //ff
+ 0x6B,0xfb, //fe
+ 0x6C,0xf4, //ff
+ 0x6D,0x5a, //5f
+ 0x6E,0xe6, //e1
+
+ 0x6f,0x00,
+
+ //=========lsc
+ 0x70,0x14,
+ 0x71,0x1c,
+ 0x72,0x20,
+
+ 0x73,0x10,
+ 0x74,0x3c,//480/8
+ 0x75,0x52,// 640/8
+
+ //=========dn
+ 0x7d,0x2f, //dn_mode
+ 0x80,0x0c,//when auto_dn, check 7e,7f
+ 0x81,0x0c,
+ 0x82,0x44,
+
+ //dd
+ 0x83,0x18, //DD_TH1
+ 0x84,0x18, //DD_TH2
+ 0x85,0x04, //DD_TH3
+ 0x87,0x34, //32 b DNDD_low_range X16, DNDD_low_range_C_weight_center
+
+
+ //=========intp-ee
+ 0x88,0x04,
+ 0x89,0x01,
+ 0x8a,0x50,//60
+ 0x8b,0x50,//60
+ 0x8c,0x07,
+
+ 0x50,0x0c,
+ 0x5f,0x3c,
+
+ 0x8e,0x02,
+ 0x86,0x02,
+
+ 0x51,0x20,
+ 0x52,0x08,
+ 0x53,0x00,
+
+
+ //========= YCP
+ //contrast_center
+ 0x77,0x80,//contrast_center
+ 0x78,0x00,//fixed_Cb
+ 0x79,0x00,//fixed_Cr
+ 0x7a,0x00,//luma_offset
+ 0x7b,0x40,//hue_cos
+ 0x7c,0x00,//hue_sin
+
+ //saturation
+ 0xa0,0x40,//global_saturation
+ 0xa1,0x40,//luma_contrast
+ 0xa2,0x34,//saturation_Cb//0x34
+ 0xa3,0x32,// 34 saturation_Cr//0x34
+
+ 0xa4,0xc8,
+ 0xa5,0x02,
+ 0xa6,0x28,
+ 0xa7,0x02,
+
+ //skin
+ 0xa8,0xee,
+ 0xa9,0x12,
+ 0xaa,0x01,
+ 0xab,0x20,
+ 0xac,0xf0,
+ 0xad,0x10,
+
+ //========= ABS
+ 0xae,0x18,// black_pixel_target_number
+ 0xaf,0x74,
+ 0xb0,0xe0,
+ 0xb1,0x20,
+ 0xb2,0x6c,
+ 0xb3,0x40,
+ 0xb4,0x04,
+
+ //========= AWB
+ 0xbb,0x42,
+ 0xbc,0x60,
+ 0xbd,0x50,
+ 0xbe,0x50,
+
+ 0xbf,0x0c,
+ 0xc0,0x06,
+ 0xc1,0x60,
+ 0xc2,0xf1, //f4
+ 0xc3,0x40,
+ 0xc4,0x1c,//18
+ 0xc5,0x56,
+ 0xc6,0x1d,
+
+ 0xca,0x70,//0x70
+ 0xcb,0x70,//0x70
+ 0xcc,0x78,//0x78
+
+ 0xcd,0x80,//R_ratio
+ 0xce,0x80,//G_ratio , cold_white white
+ 0xcf,0x80,//B_ratio
+
+ //========= aecT
+ 0x20,0x06,//02
+ 0x21,0xc0,
+ 0x22,0x40,
+ 0x23,0x88,
+ 0x24,0x96,
+ 0x25,0x30,
+ 0x26,0xd0,
+ 0x27,0x00,
+
+
+ /////23 M
+ 0x28,0x02,//AEC_exp_level_1bit11to8
+ 0x29,0x58,//AEC_exp_level_1bit7to0
+ 0x2a,0x02,//AEC_exp_level_2bit11to8
+ 0x2b,0x58,//AEC_exp_level_2bit7to0
+ 0x2c,0x02,//AEC_exp_level_3bit11to8 659 - 8FPS, 8ca - 6FPS //
+ 0x2d,0x58,//AEC_exp_level_3bit7to0
+ 0x2e,0x02,//AEC_exp_level_4bit11to8 4FPS
+ 0x2f,0xee,//AEC_exp_level_4bit7to0
+
+ 0x30,0x20,
+ 0x31,0x00,
+ 0x32,0x1c,
+ 0x33,0x90,
+ 0x34,0x10,
+
+ 0xd0,0x34,//[2] 1 before gamma, 0 after gamma
+
+ 0xd1,0x50,//AEC_target_Y//0x50
+ 0xd2,0x61,//f2
+ 0xd4,0x4b,//96
+ 0xd5,0x01,// 10
+ 0xd6,0x4b,//antiflicker_step //96
+ 0xd7,0x03,//AEC_exp_time_min //10
+ 0xd8,0x02,
+
+ 0xdd,0x12,
+
+ //========= measure window
+ 0xe0,0x03,
+ 0xe1,0x02,
+ 0xe2,0x27,
+ 0xe3,0x1e,
+ 0xe8,0x3b,
+ 0xe9,0x6e,
+ 0xea,0x2c,
+ 0xeb,0x50,
+ 0xec,0x73,
+
+ //========= close_frame
+ 0xed,0x00,//close_frame_num1 ,can be use to reduce FPS
+ 0xee,0x00,//close_frame_num2
+ 0xef,0x00,//close_frame_num
+
+ // page1
+ 0xf0,0x01,//select page1
+
+ 0x00,0x20,
+ 0x01,0x20,
+ 0x02,0x20,
+ 0x03,0x20,
+ 0x04,0x78,
+ 0x05,0x78,
+ 0x06,0x78,
+ 0x07,0x78,
+
+
+
+ 0x10,0x04,
+ 0x11,0x04,
+ 0x12,0x04,
+ 0x13,0x04,
+ 0x14,0x01,
+ 0x15,0x01,
+ 0x16,0x01,
+ 0x17,0x01,
+
+
+ 0x20,0x00,
+ 0x21,0x00,
+ 0x22,0x00,
+ 0x23,0x00,
+ 0x24,0x00,
+ 0x25,0x00,
+ 0x26,0x00,
+ 0x27,0x00,
+
+ 0x40,0x11,
+
+ //=============================lscP
+ 0x45,0x06,
+ 0x46,0x06,
+ 0x47,0x05,
+
+ 0x48,0x04,
+ 0x49,0x03,
+ 0x4a,0x03,
+
+
+ 0x62,0xd8,
+ 0x63,0x24,
+ 0x64,0x24,
+ 0x65,0x24,
+ 0x66,0xd8,
+ 0x67,0x24,
+
+ 0x5a,0x00,
+ 0x5b,0x00,
+ 0x5c,0x00,
+ 0x5d,0x00,
+ 0x5e,0x00,
+ 0x5f,0x00,
+
+
+ //============================= ccP
+
+ 0x69,0x03,//cc_mode
+
+ //CC_G
+ 0x70,0x5d,
+ 0x71,0xed,
+ 0x72,0xff,
+ 0x73,0xe5,
+ 0x74,0x5f,
+ 0x75,0xe6,
+
+ //CC_B
+ 0x76,0x41,
+ 0x77,0xef,
+ 0x78,0xff,
+ 0x79,0xff,
+ 0x7a,0x5f,
+ 0x7b,0xfa,
+
+
+ //============================= AGP
+
+ 0x7e,0x00,
+ 0x7f,0x30, // 00 select gamma
+ 0x80,0x48, // c8
+ 0x81,0x06,
+ 0x82,0x08,
+
+
+ 0x83,0x23,
+ 0x84,0x38,
+ 0x85,0x4F,
+ 0x86,0x61,
+ 0x87,0x72,
+ 0x88,0x80,
+ 0x89,0x8D,
+ 0x8a,0xA2,
+ 0x8b,0xB2,
+ 0x8c,0xC0,
+ 0x8d,0xCA,
+ 0x8e,0xD3,
+ 0x8f,0xDB,
+ 0x90,0xE2,
+ 0x91,0xED,
+ 0x92,0xF6,
+ 0x93,0xFD,
+ /*
+ 0x83,0x13,
+ 0x84,0x23,
+ 0x85,0x35,
+ 0x86,0x44,
+ 0x87,0x53,
+ 0x88,0x60,
+ 0x89,0x6D,
+ 0x8a,0x84,
+ 0x8b,0x98,
+ 0x8c,0xaa,
+ 0x8d,0xb8,
+ 0x8e,0xc6,
+ 0x8f,0xd1,
+ 0x90,0xdb,
+ 0x91,0xea,
+ 0x92,0xf5,
+ 0x93,0xFb,
+ */
+ //about gamma1 is hex r oct
+ 0x94,0x04,
+ 0x95,0x0E,
+ 0x96,0x1B,
+ 0x97,0x28,
+ 0x98,0x35,
+ 0x99,0x41,
+ 0x9a,0x4E,
+ 0x9b,0x67,
+ 0x9c,0x7E,
+ 0x9d,0x94,
+ 0x9e,0xA7,
+ 0x9f,0xBA,
+ 0xa0,0xC8,
+ 0xa1,0xD4,
+ 0xa2,0xE7,
+ 0xa3,0xF4,
+ 0xa4,0xFA,
+
+ //========= open functions
+ 0xf0,0x00,//set back to page0
+
+ 0x40,0x7e,
+ 0x41,0x2f,
+ 0x43,0x40,
+ 0x44,0xE2,
+
+ 0x0f,0x82,
+ 0x45,0x24,
+ 0x47,0x20,
+ 0x4e,0x26,
+
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-7303.h b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-7303.h
new file mode 100755
index 00000000..06370f36
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-7303.h
@@ -0,0 +1,450 @@
+#ifndef GC0308_H
+#define GC0308_H
+
+
+// Scene Mode
+uint8_t gc0308_scene_mode_auto[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x20,
+};
+
+uint8_t gc0308_scene_mode_night[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x30,
+};
+
+
+// White Balance
+uint8_t gc0308_wb_auto [] = {
+ 0xfe, 0x00, // set page0
+ 0x5a, 0x56,
+ 0x5b, 0x40,
+ 0x5c, 0x4a,
+ 0x22, 0x57,
+};
+
+uint8_t gc0308_wb_incandescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x48,
+ 0x5b, 0x40,
+ 0x5c, 0x5c,
+};
+
+uint8_t gc0308_wb_fluorescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x40,
+ 0x5b, 0x42,
+ 0x5c, 0x50,
+};
+
+uint8_t gc0308_wb_daylight [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x74,
+ 0x5b, 0x52,
+ 0x5c, 0x40,
+};
+
+uint8_t gc0308_wb_cloudy [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x8c,
+ 0x5b, 0x50,
+ 0x5c, 0x40,
+};
+
+
+// Exposure
+uint8_t gc0308_exposure_neg6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xe0,
+ 0xd3, 0x38,
+};
+
+uint8_t gc0308_exposure_neg3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xf0,
+ 0xd3, 0x40,
+};
+
+uint8_t gc0308_exposure_zero[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x00,
+ 0xd3, 0x48,
+};
+
+uint8_t gc0308_exposure_pos3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x10,
+ 0xd3, 0x50,
+};
+
+uint8_t gc0308_exposure_pos6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x20,
+ 0xd3, 0x58,
+};
+
+
+// Resolution
+uint8_t gc0308_320x240[]= {
+ 0xfe, 0x01,
+ 0x54, 0x22,
+ 0x55, 0x03,
+ 0x56, 0x00,
+ 0x57, 0x00,
+ 0x58, 0x00,
+ 0x59, 0x00,
+ 0xfe, 0x00,
+};
+
+uint8_t gc0308_640x480[] = {
+ 0xfe,0x01,
+ 0x54,0x11,
+ 0x55,0x03,
+ 0x56,0x00,
+ 0x57,0x00,
+ 0x58,0x00,
+ 0x59,0x00,
+ 0xfe,0x00,
+ 0x46,0x00,
+ 0xfe,0x00,
+};
+
+uint8_t gc0308_default_regs_init[] = {
+ 0xfe,0x80,
+
+ 0xfe,0x00, // set page0
+
+ 0xd2,0x10, // close AEC
+ 0x22,0x55, // close AWB
+
+ 0x03,0x01,
+ 0x04,0x2c,
+ 0x5a,0x56,
+ 0x5b,0x40,
+ 0x5c,0x4a,
+
+ 0x22,0x57, // Open AWB
+
+ 0x01,0xfa,
+ 0x02,0x70,
+ 0x0f,0x01,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x64, //anti-flicker step [7:0]
+
+ 0xe4,0x03, //02//exp level 1 16.67fps
+ 0xe5,0x20, //58
+ 0xe6,0x03, //exp level 2 12.5fps
+ 0xe7,0x20,
+ 0xe8,0x04, //exp level 3 8.33fps
+ 0xe9,0xb0,
+ 0xea,0x09, //exp level 4 4.00fps
+ 0xeb,0xc4,
+
+ 0xec,0x20,
+
+ 0x05,0x00,
+ 0x06,0x00,
+ 0x07,0x00,
+ 0x08,0x00,
+ 0x09,0x01,
+ 0x0a,0xe8,
+ 0x0b,0x02,
+ 0x0c,0x88,
+ 0x0d,0x02,
+ 0x0e,0x02,
+ 0x10,0x22,
+ 0x11,0xfd,
+ 0x12,0x2a,
+ 0x13,0x00,
+ 0x14,0x10, //0x10
+
+ 0x15,0x0a,
+ 0x16,0x05,
+ 0x17,0x01,
+ 0x18,0x44,
+ 0x19,0x44,
+ 0x1a,0x1e,
+ 0x1b,0x00,
+ 0x1c,0xc1,
+ 0x1d,0x08,
+ 0x1e,0x60,
+ 0x1f,0x16,
+
+
+
+ 0x20,0xff,
+ 0x21,0xf8,
+ 0x22,0x57,
+ 0x24,0xa2,
+ 0x25,0x0f,
+
+ //output sync_mode
+
+
+ 0x26,0x02, //0x03 20101016 zhj
+
+ 0x2f,0x01,
+ 0x30,0xf7,
+ 0x31,0x50,
+ 0x32,0x00,
+ 0x39,0x04,
+ 0x3a,0x18,
+ 0x3b,0x20,
+ 0x3c,0x00,
+ 0x3d,0x00,
+ 0x3e,0x00,
+ 0x3f,0x00,
+ 0x50,0x10,
+ 0x53,0x82,
+ 0x54,0x80,
+ 0x55,0x80,
+ 0x56,0x82,
+ 0x8b,0x40,
+ 0x8c,0x40,
+ 0x8d,0x40,
+ 0x8e,0x2e,
+ 0x8f,0x2e,
+ 0x90,0x2e,
+ 0x91,0x3c,
+ 0x92,0x50,
+ 0x5d,0x12,
+ 0x5e,0x1a,
+ 0x5f,0x24,
+ 0x60,0x07,
+ 0x61,0x15,
+ 0x62,0x1f,//08
+ 0x64,0x03,
+ 0x66,0xe8,
+ 0x67,0x86,
+ 0x68,0xa2,
+ 0x69,0x18,
+ 0x6a,0x0f,
+ 0x6b,0x00,
+ 0x6c,0x5f,
+ 0x6d,0x8f,
+ 0x6e,0x55,
+ 0x6f,0x38,
+ 0x70,0x15,
+ 0x71,0x33,
+ 0x72,0xdc,
+ 0x73,0x80,
+ 0x74,0x02,
+ 0x75,0x3f,
+ 0x76,0x02,
+ 0x77,0x33,//54
+ 0x78,0x88,
+ 0x79,0x81,
+ 0x7a,0x81,
+ 0x7b,0x22,
+ 0x7c,0xff,
+ 0x93,0x48,
+ 0x94,0x00,
+ 0x95,0x05,
+ 0x96,0xe8,
+ 0x97,0x40,
+ 0x98,0xf0,
+ 0xb1,0x38,
+ 0xb2,0x38,
+ 0xbd,0x38,
+ 0xbe,0x36,
+ 0xd0,0xc9,
+ 0xd1,0x10,
+
+ 0xd3,0x80,
+ 0xd5,0xf2,
+ 0xd6,0x16,
+ 0xdb,0x92,
+ 0xdc,0xa5,
+ 0xdf,0x23,
+ 0xd9,0x00,
+ 0xda,0x00,
+ 0xe0,0x09,
+
+ 0xed,0x04,
+ 0xee,0xb0,
+ 0xef,0x60,
+ 0x80,0x03,
+ 0x80,0x03,
+
+
+ 0x9F,0x10,
+ 0xA0,0x20,
+ 0xA1,0x38,
+ 0xA2,0x4E,
+ 0xA3,0x63,
+ 0xA4,0x76,
+ 0xA5,0x87,
+ 0xA6,0xA2,
+ 0xA7,0xB8,
+ 0xA8,0xCA,
+ 0xA9,0xD8,
+ 0xAA,0xE3,
+ 0xAB,0xEB,
+ 0xAC,0xF0,
+ 0xAD,0xF8,
+ 0xAE,0xFD,
+ 0xAF,0xFF,
+
+ 0xc0,0x00,
+ 0xc1,0x10,
+ 0xc2,0x1C,
+ 0xc3,0x30,
+ 0xc4,0x43,
+ 0xc5,0x54,
+ 0xc6,0x65,
+ 0xc7,0x75,
+ 0xc8,0x93,
+ 0xc9,0xB0,
+ 0xca,0xCB,
+ 0xcb,0xE6,
+ 0xcc,0xFF,
+ 0xf0,0x02,
+ 0xf1,0x01,
+ 0xf2,0x01,
+ 0xf3,0x30,
+ 0xf9,0x9f,
+ 0xfa,0x78,
+ //----------------------------
+ 0xfe,0x01,// set page1
+ 0x00,0xf5,
+ 0x02,0x1a,
+ 0x0a,0xa0,
+ 0x0b,0x60,
+ 0x0c,0x08,
+ 0x0e,0x4c,
+ 0x0f,0x39,
+ 0x11,0x3f,
+ 0x12,0x72,
+ 0x13,0x13,
+ 0x14,0x42,
+ 0x15,0x43,
+ 0x16,0xc2,
+ 0x17,0xa8,
+ 0x18,0x18,
+ 0x19,0x40,
+ 0x1a,0xd0,
+ 0x1b,0xf5,
+ 0x70,0x40,
+ 0x71,0x58,
+ 0x72,0x30,
+ 0x73,0x48,
+ 0x74,0x20,
+ 0x75,0x60,
+ 0x77,0x20,
+ 0x78,0x32,
+ 0x30,0x03,
+ 0x31,0x40,
+ 0x32,0xe0,
+ 0x33,0xe0,
+ 0x34,0xe0,
+ 0x35,0xb0,
+ 0x36,0xc0,
+ 0x37,0xc0,
+ 0x38,0x04,
+ 0x39,0x09,
+ 0x3a,0x12,
+ 0x3b,0x1C,
+ 0x3c,0x28,
+ 0x3d,0x31,
+ 0x3e,0x44,
+ 0x3f,0x57,
+ 0x40,0x6C,
+ 0x41,0x81,
+ 0x42,0x94,
+ 0x43,0xA7,
+ 0x44,0xB8,
+ 0x45,0xD6,
+ 0x46,0xEE,
+ 0x47,0x0d,
+ 0xfe,0x00,// set page0
+
+ //-----------Update the registers 2010/07/06-----------
+ //Registers of Page0
+ 0xfe,0x00,// set page0
+ 0x10,0x26,
+ 0x11,0x0d, // fd
+
+ 0x1a,0x2a, // 1e
+
+
+ 0x1c,0x49,// c1
+ 0x1d,0x9a,// 08
+
+
+ 0x1e,0x61,// 60
+
+ 0x3a,0x20,
+
+ 0x50,0x14, // 10
+
+
+ 0x53,0x80,
+ 0x56,0x80,
+
+ 0x8b,0x20,//LSC
+ 0x8c,0x20,
+ 0x8d,0x20,
+ 0x8e,0x14,
+ 0x8f,0x10,
+ 0x90,0x14,
+
+ 0x94,0x02,
+ 0x95,0x07,
+ 0x96,0xe0,
+
+ 0xb1,0x40,// YCPT
+ 0xb2,0x40,
+ 0xb3,0x40,
+ 0xb6,0xe0,
+
+ 0xd0,0xcb,// AECT c9
+
+
+ 0xd3,0x48,// 80
+
+ 0xf2,0x02,
+ 0xf7,0x12,
+ 0xf8,0x0a,
+
+ //Registers of Page1
+ 0xfe,0x01,// set page1
+ 0x02,0x20,
+ 0x04,0x10,
+ 0x05,0x08,
+ 0x06,0x20,
+ 0x08,0x0a,
+
+ 0x0e,0x44,
+ 0x0f,0x32,
+ 0x10,0x41,
+ 0x11,0x37,
+ 0x12,0x22,
+ 0x13,0x19,
+ 0x14,0x44,
+ 0x15,0x44,
+
+ 0x19,0x50,
+ 0x1a,0xd8,
+
+ 0x32,0x10,
+
+ 0x35,0x00,
+ 0x36,0x80,
+ 0x37,0x00,
+ //-----------Update the registers end---------//
+ 0xfe,0x00,// set page0
+ 0xd2,0x90,
+
+ 0x57,0x7d,
+ 0x58,0x80,
+ 0x59,0x80,
+};
+
+uint8_t gc0308_default_regs_exit[]={
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-7304.h b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-7304.h
new file mode 100755
index 00000000..6610814b
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-7304.h
@@ -0,0 +1,546 @@
+#ifndef GC0308_H
+#define GC0308_H
+
+
+// Scene Mode
+uint8_t gc0308_scene_mode_auto[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x20,
+};
+
+uint8_t gc0308_scene_mode_night[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x30,
+};
+
+
+// White Balance
+uint8_t gc0308_wb_auto [] = {
+ 0xfe, 0x00, // set page0
+ 0x5a, 0x56,
+ 0x5b, 0x40,
+ 0x5c, 0x4a,
+ 0x22, 0x57,
+};
+
+uint8_t gc0308_wb_incandescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x48,
+ 0x5b, 0x40,
+ 0x5c, 0x5c,
+};
+
+uint8_t gc0308_wb_fluorescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x40,
+ 0x5b, 0x42,
+ 0x5c, 0x50,
+};
+
+uint8_t gc0308_wb_daylight [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x74,
+ 0x5b, 0x52,
+ 0x5c, 0x40,
+};
+
+uint8_t gc0308_wb_cloudy [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x8c,
+ 0x5b, 0x50,
+ 0x5c, 0x40,
+};
+
+
+// Exposure
+uint8_t gc0308_exposure_neg9[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xc0,
+ 0xd3, 0x20,
+};
+
+uint8_t gc0308_exposure_neg6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xd0,
+ 0xd3, 0x20,
+};
+
+uint8_t gc0308_exposure_neg3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xe0,
+ 0xd3, 0x30,
+};
+
+uint8_t gc0308_exposure_zero[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xf0,
+ 0xd3, 0x40,
+};
+
+uint8_t gc0308_exposure_pos3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x10,
+ 0xd3, 0x50,
+};
+
+uint8_t gc0308_exposure_pos6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x20,
+ 0xd3, 0x68,
+};
+
+uint8_t gc0308_exposure_pos9[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x30,
+ 0xd3, 0x78,
+};
+
+uint8_t gc0308_antibanding_auto[] = {
+ 0xfe,0x00,
+
+ 0x01,0x97,
+ 0x02,0x84,
+ 0x0f,0x03,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x3e, //anti-flicker step [7:0]
+
+ 0xe4,0x02, //exp level 0 12.5fps
+ 0xe5,0x6c,
+ 0xe6,0x02, //exp level 1 10fps
+ 0xe7,0xe8,
+ 0xe8,0x03, //exp level 2 7.69fps
+ 0xe9,0xe0,
+ 0xea,0x05, //exp level 3 5.00fps
+ 0xeb,0xd0,
+
+};
+
+uint8_t gc0308_antibanding_50hz[] = {
+ 0xfe,0x00,
+
+ 0x01,0x26,
+ 0x02,0x98,
+ 0x0f,0x03,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x50, //anti-flicker step [7:0]
+
+ 0xe4,0x02, //exp level 0 12.5fps
+ 0xe5,0x80,
+ 0xe6,0x03, //exp level 1 10fps
+ 0xe7,0x20,
+ 0xe8,0x04, //exp level 2 7.69fps
+ 0xe9,0x10,
+ 0xea,0x06, //exp level 3 5.00fps
+ 0xeb,0x40,
+
+};
+
+uint8_t gc0308_antibanding_60hz[] = {
+ 0xfe,0x00,
+
+ 0x01,0x97,
+ 0x02,0x84,
+ 0x0f,0x03,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x3e, //anti-flicker step [7:0]
+
+ 0xe4,0x02, //exp level 0 12.5fps
+ 0xe5,0x6c,
+ 0xe6,0x02, //exp level 1 10fps
+ 0xe7,0xe8,
+ 0xe8,0x03, //exp level 2 7.69fps
+ 0xe9,0xe0,
+ 0xea,0x05, //exp level 3 5.00fps
+ 0xeb,0xd0,
+
+};
+
+uint8_t gc0308_antibanding_off[] = {
+ 0xfe,0x00,
+
+ 0x01,0x26,
+ 0x02,0x98,
+ 0x0f,0x03,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x50, //anti-flicker step [7:0]
+
+ 0xe4,0x02, //exp level 0 12.5fps
+ 0xe5,0x80,
+ 0xe6,0x03, //exp level 1 10fps
+ 0xe7,0x20,
+ 0xe8,0x04, //exp level 2 7.69fps
+ 0xe9,0x10,
+ 0xea,0x06, //exp level 3 5.00fps
+ 0xeb,0x40,
+
+};
+
+// Resolution
+uint8_t gc0308_320x240[]= {
+ 0xfe, 0x01,
+ 0x54, 0x22,
+ 0x55, 0x03,
+ 0x56, 0x00,
+ 0x57, 0x00,
+ 0x58, 0x00,
+ 0x59, 0x00,
+ 0xfe, 0x00,
+};
+
+uint8_t gc0308_640x480[] = {
+ 0xfe,0x01,
+ 0x54,0x11,
+ 0x55,0x03,
+ 0x56,0x00,
+ 0x57,0x00,
+ 0x58,0x00,
+ 0x59,0x00,
+ 0xfe,0x00,
+ 0x46,0x00,
+ 0xfe,0x00,
+};
+
+uint8_t gc0308_default_regs_init[] = {
+ 0xfe,0x80,
+
+ 0xfe,0x00, // set page0
+
+ 0xd2,0x10, // close AEC
+ 0x22,0x55, // close AWB
+
+ 0x03,0x01,
+ 0x04,0x2c,
+ 0x5a,0x56,
+ 0x5b,0x40,
+ 0x5c,0x4a,
+
+ 0x22,0x57, // Open AWB
+
+ 0x01,0xfa,
+ 0x02,0x70,
+ 0x0f,0x01,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x64, //anti-flicker step [7:0]
+
+ 0xe4,0x02, //exp level 1 16.67fps
+ 0xe5,0x58,
+ 0xe6,0x03, //exp level 2 12.5fps
+ 0xe7,0x20,
+ 0xe8,0x05, //exp level 3 8.33fps
+ 0xe9,0x14,
+ 0xea,0x09, //exp level 4 4.00fps
+ 0xeb,0xc4,
+
+ 0xec,0x20,
+
+ 0x05,0x00,
+ 0x06,0x00,
+ 0x07,0x00,
+ 0x08,0x00,
+ 0x09,0x01,
+ 0x0a,0xe8,
+ 0x0b,0x02,
+ 0x0c,0x88,
+ 0x0d,0x02,
+ 0x0e,0x02,
+ 0x10,0x22,
+ 0x11,0xfd,
+ 0x12,0x2a,
+ 0x13,0x00,
+ 0x14,0x10, //0x10
+
+ 0x15,0x0a,
+ 0x16,0x05,
+ 0x17,0x01,
+ 0x18,0x44,
+ 0x19,0x44,
+ 0x1a,0x1e,
+ 0x1b,0x00,
+ 0x1c,0xc1,
+ 0x1d,0x08,
+ 0x1e,0x60,
+ 0x1f,0x16,
+
+
+
+ 0x20,0xff,
+ 0x21,0xf8,
+ 0x22,0x57,
+ 0x24,0xa2,
+ 0x25,0x0f,
+
+ //output sync_mode
+
+
+ 0x26,0x02, //0x03 20101016 zhj
+
+ 0x2f,0x01,
+ 0x30,0xf7,
+ 0x31,0x50,
+ 0x32,0x00,
+ 0x39,0x04,
+ 0x3a,0x18,
+ 0x3b,0x20,
+ 0x3c,0x00,
+ 0x3d,0x00,
+ 0x3e,0x00,
+ 0x3f,0x00,
+ 0x50,0x10,
+ 0x53,0x82,
+ 0x54,0x80,
+ 0x55,0x80,
+ 0x56,0x82,
+ 0x8b,0x40,
+ 0x8c,0x40,
+ 0x8d,0x40,
+ 0x8e,0x2e,
+ 0x8f,0x2e,
+ 0x90,0x2e,
+ 0x91,0x3c,
+ 0x92,0x50,
+ 0x5d,0x12,
+ 0x5e,0x1a,
+ 0x5f,0x24,
+ 0x60,0x07,
+ 0x61,0x15,
+ 0x62,0x04,
+ 0x64,0x03,
+ 0x66,0xe8,
+ 0x67,0x86,
+ 0x68,0xa2,
+ 0x69,0x18,
+ 0x6a,0x0f,
+ 0x6b,0x00,
+ 0x6c,0x5f,
+ 0x6d,0x8f,
+ 0x6e,0x55,
+ 0x6f,0x38,
+ 0x70,0x20, //0x15
+ 0x71,0x35, //0x33
+ 0x72,0xdc,
+ 0x73,0x80,
+ 0x74,0x02,
+ 0x75,0x3f,
+ 0x76,0x02,
+ 0x77,0x43,//54
+ 0x78,0x88,
+ 0x79,0x81,
+ 0x7a,0x81,
+ 0x7b,0x22,
+ 0x7c,0xff,
+ 0x93,0x44,
+ 0x94,0xfc,
+ 0x95,0x0a,
+ 0x96,0xe8,
+ 0x97,0x44,
+ 0x98,0xf8,
+ 0xb1,0x38,
+ 0xb2,0x38,
+ 0xbd,0x38,
+ 0xbe,0x36,
+ 0xd0,0xc9,
+ 0xd1,0x10,
+
+ 0xd3,0x80,
+ 0xd5,0xf2,
+ 0xd6,0x16,
+ 0xdb,0x92,
+ 0xdc,0xa5,
+ 0xdf,0x23,
+ 0xd9,0x00,
+ 0xda,0x00,
+ 0xe0,0x09,
+
+ 0xed,0x04,
+ 0xee,0xa0,
+ 0xef,0x40,
+ 0x80,0x03,
+ 0x80,0x03,
+
+
+ 0x9F,0x10,
+ 0xA0,0x20,
+ 0xA1,0x38,
+ 0xA2,0x4E,
+ 0xA3,0x63,
+ 0xA4,0x76,
+ 0xA5,0x87,
+ 0xA6,0xA2,
+ 0xA7,0xB8,
+ 0xA8,0xCA,
+ 0xA9,0xD8,
+ 0xAA,0xE3,
+ 0xAB,0xEB,
+ 0xAC,0xF0,
+ 0xAD,0xF8,
+ 0xAE,0xFD,
+ 0xAF,0xFF,
+
+ 0xc0,0x00,
+ 0xc1,0x10,
+ 0xc2,0x1C,
+ 0xc3,0x30,
+ 0xc4,0x43,
+ 0xc5,0x54,
+ 0xc6,0x65,
+ 0xc7,0x75,
+ 0xc8,0x93,
+ 0xc9,0xB0,
+ 0xca,0xCB,
+ 0xcb,0xE6,
+ 0xcc,0xFF,
+ 0xf0,0x02,
+ 0xf1,0x01,
+ 0xf2,0x01,
+ 0xf3,0x30,
+ 0xf9,0x9f,
+ 0xfa,0x78,
+ //----------------------------
+ 0xfe,0x01,// set page1
+ 0x00,0xf5,
+ 0x02,0x1a,
+ 0x0a,0xa0,
+ 0x0b,0x60,
+ 0x0c,0x08,
+ 0x0e,0x4c,
+ 0x0f,0x39,
+ 0x11,0x3f,
+ 0x12,0x72,
+ 0x13,0x13,
+ 0x14,0x3c,
+ 0x15,0x43,
+ 0x16,0xc2,
+ 0x17,0xa8,
+ 0x18,0x18,
+ 0x19,0x40,
+ 0x1a,0xd0,
+ 0x1b,0xf5,
+ 0x70,0x40,
+ 0x71,0x58,
+ 0x72,0x30,
+ 0x73,0x48,
+ 0x74,0x20,
+ 0x75,0x60,
+ 0x77,0x20,
+ 0x78,0x32,
+ 0x30,0x03,
+ 0x31,0x40,
+ 0x32,0xe0,
+ 0x33,0xe0,
+ 0x34,0xe0,
+ 0x35,0xb0,
+ 0x36,0xc0,
+ 0x37,0xc0,
+ 0x38,0x04,
+ 0x39,0x09,
+ 0x3a,0x12,
+ 0x3b,0x1C,
+ 0x3c,0x28,
+ 0x3d,0x31,
+ 0x3e,0x44,
+ 0x3f,0x57,
+ 0x40,0x6C,
+ 0x41,0x81,
+ 0x42,0x94,
+ 0x43,0xA7,
+ 0x44,0xB8,
+ 0x45,0xD6,
+ 0x46,0xEE,
+ 0x47,0x0d,
+ 0xfe,0x00,// set page0
+
+ //-----------Update the registers 2010/07/06-----------
+ //Registers of Page0
+ 0xfe,0x00,// set page0
+ 0x10,0x26,
+ 0x11,0x0d, // fd
+
+ 0x1a,0x2a, // 1e
+
+
+ 0x1c,0x49,// c1
+ 0x1d,0x9a,// 08
+
+
+ 0x1e,0x61,// 60
+
+ 0x3a,0x20,
+
+ 0x50,0x14, // 10
+
+
+ 0x53,0x80,
+ 0x56,0x80,
+
+ 0x8b,0x20,//LSC
+ 0x8c,0x20,
+ 0x8d,0x20,
+ 0x8e,0x14,
+ 0x8f,0x10,
+ 0x90,0x14,
+
+ 0x94,0x02,
+ 0x95,0x07,
+ 0x96,0xe0,
+
+ 0xb1,0x40,// YCPT
+ 0xb2,0x40,
+ 0xb3,0x47,//42
+ 0xb5,0xf8,//fa
+ 0xb6,0xe0,
+
+ 0x57,0x7c,//0x7c
+ 0x58,0x7e,
+ 0x59,0x80,
+
+ 0xd0,0xcb,// AECT c9
+
+
+ 0xd3,0x48,// 80
+
+ 0xf2,0x02,
+ 0xf7,0x12,
+ 0xf8,0x0a,
+
+ //Registers of Page1
+ 0xfe,0x01,// set page1
+ 0x02,0x20,
+ 0x04,0x10,
+ 0x05,0x08,
+ 0x06,0x20,
+ 0x08,0x0a,
+
+ 0x0e,0x44,
+ 0x0f,0x32,
+ 0x10,0x41,
+ 0x11,0x37,
+ 0x12,0x22,
+ 0x13,0x19,
+ 0x14,0x44,
+ 0x15,0x44,
+
+ 0x19,0x50,
+ 0x1a,0xd8,
+
+ 0x32,0x10,
+
+ 0x35,0x00,
+ 0x36,0x80,
+ 0x37,0x00,
+ //-----------Update the registers end---------//
+ 0xfe,0x00,// set page0
+ 0xd2,0x90,
+};
+
+uint8_t gc0308_default_regs_exit[]={
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-781a.h b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-781a.h
new file mode 100755
index 00000000..b6e44f40
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-781a.h
@@ -0,0 +1,448 @@
+#ifndef GC0308_H
+#define GC0308_H
+
+
+// Scene Mode
+uint8_t gc0308_scene_mode_auto[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x20,
+};
+
+uint8_t gc0308_scene_mode_night[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x30,
+};
+
+
+// White Balance
+uint8_t gc0308_wb_auto [] = {
+ 0xfe, 0x00, // set page0
+ 0x5a, 0x56,
+ 0x5b, 0x40,
+ 0x5c, 0x4a,
+ 0x22, 0x57,
+};
+
+uint8_t gc0308_wb_incandescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x48,
+ 0x5b, 0x40,
+ 0x5c, 0x5c,
+};
+
+uint8_t gc0308_wb_fluorescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x40,
+ 0x5b, 0x42,
+ 0x5c, 0x50,
+};
+
+uint8_t gc0308_wb_daylight [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x74,
+ 0x5b, 0x52,
+ 0x5c, 0x40,
+};
+
+uint8_t gc0308_wb_cloudy [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x8c,
+ 0x5b, 0x50,
+ 0x5c, 0x40,
+};
+
+
+// Exposure
+uint8_t gc0308_exposure_neg6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xe0,
+ 0xd3, 0x38,
+};
+
+uint8_t gc0308_exposure_neg3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xf0,
+ 0xd3, 0x40,
+};
+
+uint8_t gc0308_exposure_zero[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x00,
+ 0xd3, 0x48,
+};
+
+uint8_t gc0308_exposure_pos3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x10,
+ 0xd3, 0x50,
+};
+
+uint8_t gc0308_exposure_pos6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x20,
+ 0xd3, 0x58,
+};
+
+
+// Resolution
+uint8_t gc0308_320x240[]= {
+ 0xfe, 0x01,
+ 0x54, 0x22,
+ 0x55, 0x03,
+ 0x56, 0x00,
+ 0x57, 0x00,
+ 0x58, 0x00,
+ 0x59, 0x00,
+ 0xfe, 0x00,
+};
+
+uint8_t gc0308_640x480[] = {
+ 0xfe,0x01,
+ 0x54,0x11,
+ 0x55,0x03,
+ 0x56,0x00,
+ 0x57,0x00,
+ 0x58,0x00,
+ 0x59,0x00,
+ 0xfe,0x00,
+ 0x46,0x00,
+ 0xfe,0x00,
+};
+
+uint8_t gc0308_default_regs_init[] = {
+ 0xfe,0x80,
+
+ 0xfe,0x00, // set page0
+
+ 0xd2,0x10, // close AEC
+ 0x22,0x55, // close AWB
+
+ 0x03,0x01,
+ 0x04,0x2c,
+ 0x5a,0x56,
+ 0x5b,0x40,
+ 0x5c,0x4a,
+
+ 0x22,0x57, // Open AWB
+
+ 0x01,0xfa,
+ 0x02,0x70,
+ 0x0f,0x01,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x64, //anti-flicker step [7:0]
+
+ 0xe4,0x04, //exp level 1 16.67fps
+ 0xe5,0x4c,
+ 0xe6,0x04, //exp level 2 12.5fps
+ 0xe7,0x4c,
+ 0xe8,0x05, //exp level 3 8.33fps
+ 0xe9,0x14,
+ 0xea,0x09, //exp level 4 4.00fps
+ 0xeb,0xc4,
+
+ 0xec,0x20,
+
+ 0x05,0x00,
+ 0x06,0x00,
+ 0x07,0x00,
+ 0x08,0x00,
+ 0x09,0x01,
+ 0x0a,0xe8,
+ 0x0b,0x02,
+ 0x0c,0x88,
+ 0x0d,0x02,
+ 0x0e,0x02,
+ 0x10,0x22,
+ 0x11,0xfd,
+ 0x12,0x2a,
+ 0x13,0x00,
+ 0x14,0x10, //0x10
+
+ 0x15,0x0a,
+ 0x16,0x05,
+ 0x17,0x01,
+ 0x18,0x44,
+ 0x19,0x44,
+ 0x1a,0x1e,
+ 0x1b,0x00,
+ 0x1c,0xc1,
+ 0x1d,0x08,
+ 0x1e,0x60,
+ 0x1f,0x16,
+
+
+
+ 0x20,0xff,
+ 0x21,0xf8,
+ 0x22,0x57,
+ 0x24,0xa2,
+ 0x25,0x0f,
+
+ //output sync_mode
+
+
+ 0x26,0x02, //0x03 20101016 zhj
+
+ 0x2f,0x01,
+ 0x30,0xf7,
+ 0x31,0x50,
+ 0x32,0x00,
+ 0x39,0x04,
+ 0x3a,0x18,
+ 0x3b,0x20,
+ 0x3c,0x00,
+ 0x3d,0x00,
+ 0x3e,0x00,
+ 0x3f,0x00,
+ 0x50,0x10,
+ 0x53,0x82,
+ 0x54,0x80,
+ 0x55,0x80,
+ 0x56,0x82,
+ 0x8b,0x40,
+ 0x8c,0x40,
+ 0x8d,0x40,
+ 0x8e,0x2e,
+ 0x8f,0x2e,
+ 0x90,0x2e,
+ 0x91,0x3c,
+ 0x92,0x50,
+ 0x5d,0x12,
+ 0x5e,0x1a,
+ 0x5f,0x24,
+ 0x60,0x07,
+ 0x61,0x15,
+ 0x62,0x15,//08 ary
+ 0x64,0x01,//03
+ 0x66,0xe8,
+ 0x67,0x86,
+ 0x68,0xa2,
+ 0x69,0x18,
+ 0x6a,0x0f,
+ 0x6b,0x00,
+ 0x6c,0x5f,
+ 0x6d,0x8f,
+ 0x6e,0x55,
+ 0x6f,0x38,
+ 0x70,0x15,
+ 0x71,0x33,
+ 0x72,0xdc,
+ 0x73,0x80,
+ 0x74,0x02,
+ 0x75,0x3f,
+ 0x76,0x02,
+ 0x77,0x46,//54 ary
+ 0x78,0x88,
+ 0x79,0x81,
+ 0x7a,0x81,
+ 0x7b,0x22,
+ 0x7c,0xff,
+ 0x93,0x48,
+ 0x94,0x00,
+ 0x95,0x05,
+ 0x96,0xe8,
+ 0x97,0x40,
+ 0x98,0xf0,
+ 0xb1,0x38,
+ 0xb2,0x38,
+ 0xbd,0x38,
+ 0xbe,0x36,
+ 0xd0,0xc9,
+ 0xd1,0x10,
+
+ 0xd3,0x80,
+ 0xd5,0xf2,
+ 0xd6,0x16,
+ 0xdb,0x92,
+ 0xdc,0xa5,
+ 0xdf,0x23,
+ 0xd9,0x00,
+ 0xda,0x00,
+ 0xe0,0x09,
+
+ 0xed,0x08,//04 ary
+ 0xee,0xa6,//a0 ary
+ 0xef,0x40,
+ 0x80,0x03,
+ 0x80,0x03,
+
+
+
+ 0x9F , 0x10,
+ 0xA0 , 0x20,
+ 0xA1 , 0x38,
+ 0xA2 , 0x4E,
+ 0xA3 , 0x63,
+ 0xA4 , 0x76,
+ 0xA5 , 0x87,
+ 0xA6 , 0xA2,
+ 0xA7 , 0xB8,
+ 0xA8 , 0xCA,
+ 0xA9 , 0xD8,
+ 0xAA , 0xE3,
+ 0xAB , 0xEB,
+ 0xAC , 0xF0,
+ 0xAD , 0xF8,
+ 0xAE , 0xFD,
+ 0xAF , 0xFF,
+
+
+ 0xc0,0x00,
+ 0xc1,0x10,
+ 0xc2,0x1C,
+ 0xc3,0x30,
+ 0xc4,0x43,
+ 0xc5,0x54,
+ 0xc6,0x65,
+ 0xc7,0x75,
+ 0xc8,0x93,
+ 0xc9,0xB0,
+ 0xca,0xCB,
+ 0xcb,0xE6,
+ 0xcc,0xFF,
+ 0xf0,0x02,
+ 0xf1,0x01,
+ 0xf2,0x01,
+ 0xf3,0x30,
+ 0xf9,0x9f,
+ 0xfa,0x78,
+ //----------------------------
+ 0xfe,0x01,// set page1
+ 0x00,0xf5,
+ 0x02,0x1a,
+ 0x0a,0xa0,
+ 0x0b,0x60,
+ 0x0c,0x08,
+ 0x0e,0x4c,
+ 0x0f,0x39,
+ 0x11,0x3f,
+ 0x12,0x72,
+ 0x13,0x13,
+ 0x14,0x42,
+ 0x15,0x43,
+ 0x16,0xc2,
+ 0x17,0xa8,
+ 0x18,0x18,
+ 0x19,0x40,
+ 0x1a,0xd0,
+ 0x1b,0xf5,
+ 0x70,0x40,
+ 0x71,0x58,
+ 0x72,0x30,
+ 0x73,0x48,
+ 0x74,0x20,
+ 0x75,0x60,
+ 0x77,0x20,
+ 0x78,0x32,
+ 0x30,0x03,
+ 0x31,0x40,
+ 0x32,0xe0,
+ 0x33,0xe0,
+ 0x34,0xe0,
+ 0x35,0xb0,
+ 0x36,0xc0,
+ 0x37,0xc0,
+ 0x38,0x04,
+ 0x39,0x09,
+ 0x3a,0x12,
+ 0x3b,0x1C,
+ 0x3c,0x28,
+ 0x3d,0x31,
+ 0x3e,0x44,
+ 0x3f,0x57,
+ 0x40,0x6C,
+ 0x41,0x81,
+ 0x42,0x94,
+ 0x43,0xA7,
+ 0x44,0xB8,
+ 0x45,0xD6,
+ 0x46,0xEE,
+ 0x47,0x0d,
+ 0xfe,0x00,// set page0
+
+ //-----------Update the registers 2010/07/06-----------
+ //Registers of Page0
+ 0xfe,0x00,// set page0
+ 0x10,0x26,
+ 0x11,0x0d, // fd
+
+ 0x1a,0x2a, // 1e
+
+
+ 0x1c,0x49,// c1
+ 0x1d,0x9a,// 08
+
+
+ 0x1e,0x61,// 60
+
+ 0x3a,0x20,
+
+ 0x50,0x14, // 10
+
+
+ 0x53,0x80,
+ 0x56,0x80,
+
+ 0x8b,0x20,//LSC
+ 0x8c,0x20,
+ 0x8d,0x20,
+ 0x8e,0x14,
+ 0x8f,0x10,
+ 0x90,0x14,
+
+ 0x94,0x02,
+ 0x95,0x07,
+ 0x96,0xe0,
+
+ 0xb1,0x3a,// YCPT
+ 0xb2,0x3a,
+ 0xb3,0x42,//40 ary
+ 0xb6,0xe0,
+
+ 0xd0,0xcb,// AECT c9
+
+
+ 0xd3,0x48,// 80
+
+ 0xf2,0x02,
+ 0xf7,0x12,
+ 0xf8,0x0a,
+
+ //Registers of Page1
+ 0xfe,0x01,// set page1
+ 0x02,0x20,
+ 0x04,0x10,
+ 0x05,0x08,
+ 0x06,0x20,
+ 0x08,0x0a,
+
+ 0x0e,0x44,
+ 0x0f,0x32,
+ 0x10,0x41,
+ 0x11,0x37,
+ 0x12,0x22,
+ 0x13,0x19,
+ 0x14,0x44,
+ 0x15,0x44,
+
+ 0x19,0x50,
+ 0x1a,0xd8,
+
+ 0x32,0x10,
+
+ 0x35,0x00,
+ 0x36,0x80,
+ 0x37,0x00,
+ //-----------Update the registers end---------//
+ 0xfe,0x00,// set page0
+ 0xd2,0x90,
+};
+
+uint8_t gc0308_default_regs_exit[]={
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-gsg5-001gt7204-g3.h b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-gsg5-001gt7204-g3.h
new file mode 100755
index 00000000..40c2dce6
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-gsg5-001gt7204-g3.h
@@ -0,0 +1,451 @@
+#ifndef GC0308_H
+#define GC0308_H
+
+
+// Scene Mode
+uint8_t gc0308_scene_mode_auto[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x20,
+};
+
+uint8_t gc0308_scene_mode_night[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x30,
+};
+
+
+// White Balance
+uint8_t gc0308_wb_auto [] = {
+ 0xfe, 0x00, // set page0
+ 0x5a, 0x56,
+ 0x5b, 0x40,
+ 0x5c, 0x4a,
+ 0x22, 0x57,
+};
+
+uint8_t gc0308_wb_incandescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x48,
+ 0x5b, 0x40,
+ 0x5c, 0x5c,
+};
+
+uint8_t gc0308_wb_fluorescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x40,
+ 0x5b, 0x42,
+ 0x5c, 0x50,
+};
+
+uint8_t gc0308_wb_daylight [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x74,
+ 0x5b, 0x52,
+ 0x5c, 0x40,
+};
+
+uint8_t gc0308_wb_cloudy [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x8c,
+ 0x5b, 0x50,
+ 0x5c, 0x40,
+};
+
+
+// Exposure
+uint8_t gc0308_exposure_neg6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xe0,
+ 0xd3, 0x38,
+};
+
+uint8_t gc0308_exposure_neg3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xf0,
+ 0xd3, 0x40,
+};
+
+uint8_t gc0308_exposure_zero[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xfa,
+ 0xd3, 0x48,
+};
+
+uint8_t gc0308_exposure_pos3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x10,
+ 0xd3, 0x50,
+};
+
+uint8_t gc0308_exposure_pos6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x20,
+ 0xd3, 0x58,
+};
+
+
+// Resolution
+uint8_t gc0308_320x240[]= {
+ 0xfe, 0x01,
+ 0x54, 0x22,
+ 0x55, 0x03,
+ 0x56, 0x00,
+ 0x57, 0x00,
+ 0x58, 0x00,
+ 0x59, 0x00,
+ 0xfe, 0x00,
+};
+
+uint8_t gc0308_640x480[] = {
+ 0xfe,0x01,
+ 0x54,0x11,
+ 0x55,0x03,
+ 0x56,0x00,
+ 0x57,0x00,
+ 0x58,0x00,
+ 0x59,0x00,
+ 0xfe,0x00,
+ 0x46,0x00,
+ 0xfe,0x00,
+};
+
+uint8_t gc0308_default_regs_init[] = {
+ 0xfe,0x80,
+
+ 0xfe,0x00, // set page0
+
+ 0xd2,0x10, // close AEC
+ 0x22,0x55, // close AWB
+
+ 0x03,0x01,
+ 0x04,0x2c,
+ 0x5a,0x56,
+ 0x5b,0x40,
+ 0x5c,0x4a,
+
+ 0x22,0x57, // Open AWB
+
+ 0x01,0xfa,
+ 0x02,0x70,
+ 0x0f,0x01,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x64, //anti-flicker step [7:0]
+
+ 0xe4,0x02, //exp level 1 16.67fps
+ 0xe5,0x58,
+ 0xe6,0x03, //exp level 2 12.5fps
+ 0xe7,0x20,
+ 0xe8,0x04, //exp level 3 8.33fps
+ 0xe9,0xb0,
+ 0xea,0x09, //exp level 4 4.00fps
+ 0xeb,0xc4,
+
+ 0xec,0x20,
+
+ 0x05,0x00,
+ 0x06,0x00,
+ 0x07,0x00,
+ 0x08,0x00,
+ 0x09,0x01,
+ 0x0a,0xe8,
+ 0x0b,0x02,
+ 0x0c,0x88,
+ 0x0d,0x02,
+ 0x0e,0x02,
+ 0x10,0x22,
+ 0x11,0xfd,
+ 0x12,0x2a,
+ 0x13,0x00,
+ 0x14,0x10, //0x10
+
+ 0x15,0x0a,
+ 0x16,0x05,
+ 0x17,0x01,
+ 0x18,0x44,
+ 0x19,0x44,
+ 0x1a,0x1e,
+ 0x1b,0x00,
+ 0x1c,0xc1,
+ 0x1d,0x08,
+ 0x1e,0x60,
+ 0x1f,0x16,
+
+
+
+ 0x20,0xff,
+ 0x21,0xf8,
+ 0x22,0x57,
+ 0x24,0xa2,
+ 0x25,0x0f,
+
+ //output sync_mode
+
+
+ 0x26,0x02, //0x03 20101016 zhj
+
+ 0x2f,0x01,
+ 0x30,0xf7,
+ 0x31,0x50,
+ 0x32,0x00,
+ 0x39,0x04,
+ 0x3a,0x18,
+ 0x3b,0x20,
+ 0x3c,0x00,
+ 0x3d,0x00,
+ 0x3e,0x00,
+ 0x3f,0x00,
+ 0x50,0x10,
+ 0x53,0x82,
+ 0x54,0x80,
+ 0x55,0x80,
+ 0x56,0x82,
+ 0x8b,0x40,
+ 0x8c,0x40,
+ 0x8d,0x40,
+ 0x8e,0x2e,
+ 0x8f,0x2e,
+ 0x90,0x2e,
+ 0x91,0x3c,
+ 0x92,0x50,
+ 0x5d,0x12,
+ 0x5e,0x1a,
+ 0x5f,0x24,
+ 0x60,0x07,
+ 0x61,0x15,
+ 0x62,0x04,
+ 0x64,0x03,
+ 0x66,0xe8,
+ 0x67,0x86,
+ 0x68,0xa2,
+ 0x69,0x18,
+ 0x6a,0x0f,
+ 0x6b,0x00,
+ 0x6c,0x5f,
+ 0x6d,0x8f,
+ 0x6e,0x55,
+ 0x6f,0x38,
+ 0x70,0x15,
+ 0x71,0x33,
+ 0x72,0xdc,
+ 0x73,0x80,
+ 0x74,0x02,
+ 0x75,0x3f,
+ 0x76,0x02,
+ 0x77,0x54,
+ 0x78,0x88,
+ 0x79,0x81,
+ 0x7a,0x81,
+ 0x7b,0x22,
+ 0x7c,0xff,
+ 0x93,0x44,
+ 0x94,0xfc,
+ 0x95,0x0a,
+ 0x96,0xe8,
+ 0x97,0x44,
+ 0x98,0xf8,
+ 0xb1,0x38,
+ 0xb2,0x38,
+ 0xbd,0x38,
+ 0xbe,0x36,
+ 0xd0,0xc9,
+ 0xd1,0x10,
+
+ 0xd3,0x80,
+ 0xd5,0xf2,
+ 0xd6,0x16,
+ 0xdb,0x92,
+ 0xdc,0xa5,
+ 0xdf,0x23,
+ 0xd9,0x00,
+ 0xda,0x00,
+ 0xe0,0x09,
+
+ 0xed,0x04,
+ 0xee,0xa0,
+ 0xef,0x40,
+ 0x80,0x03,
+ 0x80,0x03,
+
+
+ 0x9F,0x10,
+ 0xA0,0x20,
+ 0xA1,0x38,
+ 0xA2,0x4E,
+ 0xA3,0x63,
+ 0xA4,0x76,
+ 0xA5,0x87,
+ 0xA6,0xA2,
+ 0xA7,0xB8,
+ 0xA8,0xCA,
+ 0xA9,0xD8,
+ 0xAA,0xE3,
+ 0xAB,0xEB,
+ 0xAC,0xF0,
+ 0xAD,0xF8,
+ 0xAE,0xFD,
+ 0xAF,0xFF,
+
+ 0xc0,0x00,
+ 0xc1,0x10,
+ 0xc2,0x1C,
+ 0xc3,0x30,
+ 0xc4,0x43,
+ 0xc5,0x54,
+ 0xc6,0x65,
+ 0xc7,0x75,
+ 0xc8,0x93,
+ 0xc9,0xB0,
+ 0xca,0xCB,
+ 0xcb,0xE6,
+ 0xcc,0xFF,
+ 0xf0,0x02,
+ 0xf1,0x01,
+ 0xf2,0x01,
+ 0xf3,0x30,
+ 0xf9,0x9f,
+ 0xfa,0x78,
+ //----------------------------
+ 0xfe,0x01,// set page1
+ 0x00,0xf5,
+ 0x02,0x1a,
+ 0x0a,0xa0,
+ 0x0b,0x60,
+ 0x0c,0x08,
+ 0x0e,0x4c,
+ 0x0f,0x39,
+ 0x11,0x3f,
+ 0x12,0x72,
+ 0x13,0x13,
+ 0x14,0x3c,
+ 0x15,0x43,
+ 0x16,0xc2,
+ 0x17,0xa8,
+ 0x18,0x18,
+ 0x19,0x40,
+ 0x1a,0xd0,
+ 0x1b,0xf5,
+ 0x70,0x40,
+ 0x71,0x58,
+ 0x72,0x30,
+ 0x73,0x48,
+ 0x74,0x20,
+ 0x75,0x60,
+ 0x77,0x20,
+ 0x78,0x32,
+ 0x30,0x03,
+ 0x31,0x40,
+ 0x32,0xe0,
+ 0x33,0xe0,
+ 0x34,0xe0,
+ 0x35,0xb0,
+ 0x36,0xc0,
+ 0x37,0xc0,
+ 0x38,0x04,
+ 0x39,0x09,
+ 0x3a,0x12,
+ 0x3b,0x1C,
+ 0x3c,0x28,
+ 0x3d,0x31,
+ 0x3e,0x44,
+ 0x3f,0x57,
+ 0x40,0x6C,
+ 0x41,0x81,
+ 0x42,0x94,
+ 0x43,0xA7,
+ 0x44,0xB8,
+ 0x45,0xD6,
+ 0x46,0xEE,
+ 0x47,0x0d,
+ 0xfe,0x00,// set page0
+
+ //-----------Update the registers 2010/07/06-----------
+ //Registers of Page0
+ 0xfe,0x00,// set page0
+ 0x10,0x26,
+ 0x11,0x0d, // fd
+
+ 0x1a,0x2a, // 1e
+
+
+ 0x1c,0x49,// c1
+ 0x1d,0x9a,// 08
+
+
+ 0x1e,0x61,// 60
+
+ 0x3a,0x20,
+
+ 0x50,0x14, // 10
+
+
+ 0x53,0x80,
+ 0x56,0x80,
+
+ 0x8b,0x20,//LSC
+ 0x8c,0x20,
+ 0x8d,0x20,
+ 0x8e,0x14,
+ 0x8f,0x10,
+ 0x90,0x14,
+
+ 0x94,0x02,
+ 0x95,0x07,
+ 0x96,0xe0,
+
+ 0xb1,0x40,// YCPT
+ 0xb2,0x40,
+ 0xb3,0x42,
+ 0xb5,0xfa,
+ 0xb6,0xe0,
+
+ 0x57,0x7c,
+ 0x58,0x7c,
+ 0x59,0x80,
+
+ 0xd0,0xcb,// AECT c9
+
+
+ 0xd3,0x48,// 80
+
+ 0xf2,0x02,
+ 0xf7,0x12,
+ 0xf8,0x0a,
+
+ //Registers of Page1
+ 0xfe,0x01,// set page1
+ 0x02,0x20,
+ 0x04,0x10,
+ 0x05,0x08,
+ 0x06,0x20,
+ 0x08,0x0a,
+
+ 0x0e,0x44,
+ 0x0f,0x32,
+ 0x10,0x41,
+ 0x11,0x37,
+ 0x12,0x22,
+ 0x13,0x19,
+ 0x14,0x44,
+ 0x15,0x44,
+
+ 0x19,0x50,
+ 0x1a,0xd8,
+
+ 0x32,0x10,
+
+ 0x35,0x00,
+ 0x36,0x80,
+ 0x37,0x00,
+ //-----------Update the registers end---------//
+ 0xfe,0x00,// set page0
+ 0xd2,0x90,
+};
+
+uint8_t gc0308_default_regs_exit[]={
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-gsg5-001gt7204-g3_qx.h b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-gsg5-001gt7204-g3_qx.h
new file mode 100755
index 00000000..243c1a40
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-gsg5-001gt7204-g3_qx.h
@@ -0,0 +1,451 @@
+#ifndef GC0308_H
+#define GC0308_H
+
+
+// Scene Mode
+uint8_t gc0308_scene_mode_auto[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x20,
+};
+
+uint8_t gc0308_scene_mode_night[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x30,
+};
+
+
+// White Balance
+uint8_t gc0308_wb_auto [] = {
+ 0xfe, 0x00, // set page0
+ 0x5a, 0x56,
+ 0x5b, 0x40,
+ 0x5c, 0x4a,
+ 0x22, 0x57,
+};
+
+uint8_t gc0308_wb_incandescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x48,
+ 0x5b, 0x40,
+ 0x5c, 0x5c,
+};
+
+uint8_t gc0308_wb_fluorescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x40,
+ 0x5b, 0x42,
+ 0x5c, 0x50,
+};
+
+uint8_t gc0308_wb_daylight [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x74,
+ 0x5b, 0x52,
+ 0x5c, 0x40,
+};
+
+uint8_t gc0308_wb_cloudy [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x8c,
+ 0x5b, 0x50,
+ 0x5c, 0x40,
+};
+
+
+// Exposure
+uint8_t gc0308_exposure_neg6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xe0,
+ 0xd3, 0x38,
+};
+
+uint8_t gc0308_exposure_neg3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xf0,
+ 0xd3, 0x40,
+};
+
+uint8_t gc0308_exposure_zero[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xfa,
+ 0xd3, 0x48,
+};
+
+uint8_t gc0308_exposure_pos3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x10,
+ 0xd3, 0x50,
+};
+
+uint8_t gc0308_exposure_pos6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x20,
+ 0xd3, 0x58,
+};
+
+
+// Resolution
+uint8_t gc0308_320x240[]= {
+ 0xfe, 0x01,
+ 0x54, 0x22,
+ 0x55, 0x03,
+ 0x56, 0x00,
+ 0x57, 0x00,
+ 0x58, 0x00,
+ 0x59, 0x00,
+ 0xfe, 0x00,
+};
+
+uint8_t gc0308_640x480[] = {
+ 0xfe,0x01,
+ 0x54,0x11,
+ 0x55,0x03,
+ 0x56,0x00,
+ 0x57,0x00,
+ 0x58,0x00,
+ 0x59,0x00,
+ 0xfe,0x00,
+ 0x46,0x00,
+ 0xfe,0x00,
+};
+
+uint8_t gc0308_default_regs_init[] = {
+ 0xfe,0x80,
+
+ 0xfe,0x00, // set page0
+
+ 0xd2,0x10, // close AEC
+ 0x22,0x55, // close AWB
+
+ 0x03,0x01,
+ 0x04,0x2c,
+ 0x5a,0x56,
+ 0x5b,0x40,
+ 0x5c,0x4a,
+
+ 0x22,0x57, // Open AWB
+
+ 0x01,0xfa,
+ 0x02,0x70,
+ 0x0f,0x01,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x64, //anti-flicker step [7:0]
+
+ 0xe4,0x02, //exp level 1 16.67fps
+ 0xe5,0x58,
+ 0xe6,0x03, //exp level 2 12.5fps
+ 0xe7,0x20,
+ 0xe8,0x05, //exp level 3 8.33fps
+ 0xe9,0x14,
+ 0xea,0x09, //exp level 4 4.00fps
+ 0xeb,0xc4,
+
+ 0xec,0x20,
+
+ 0x05,0x00,
+ 0x06,0x00,
+ 0x07,0x00,
+ 0x08,0x00,
+ 0x09,0x01,
+ 0x0a,0xe8,
+ 0x0b,0x02,
+ 0x0c,0x88,
+ 0x0d,0x02,
+ 0x0e,0x02,
+ 0x10,0x22,
+ 0x11,0xfd,
+ 0x12,0x2a,
+ 0x13,0x00,
+ 0x14,0x10, //0x10
+
+ 0x15,0x0a,
+ 0x16,0x05,
+ 0x17,0x01,
+ 0x18,0x44,
+ 0x19,0x44,
+ 0x1a,0x1e,
+ 0x1b,0x00,
+ 0x1c,0xc1,
+ 0x1d,0x08,
+ 0x1e,0x60,
+ 0x1f,0x16,
+
+
+
+ 0x20,0xff,
+ 0x21,0xf8,
+ 0x22,0x57,
+ 0x24,0xa2,
+ 0x25,0x0f,
+
+ //output sync_mode
+
+
+ 0x26,0x02, //0x03 20101016 zhj
+
+ 0x2f,0x01,
+ 0x30,0xf7,
+ 0x31,0x50,
+ 0x32,0x00,
+ 0x39,0x04,
+ 0x3a,0x18,
+ 0x3b,0x20,
+ 0x3c,0x00,
+ 0x3d,0x00,
+ 0x3e,0x00,
+ 0x3f,0x00,
+ 0x50,0x10,
+ 0x53,0x82,
+ 0x54,0x80,
+ 0x55,0x80,
+ 0x56,0x82,
+ 0x8b,0x40,
+ 0x8c,0x40,
+ 0x8d,0x40,
+ 0x8e,0x2e,
+ 0x8f,0x2e,
+ 0x90,0x2e,
+ 0x91,0x3c,
+ 0x92,0x50,
+ 0x5d,0x12,
+ 0x5e,0x1a,
+ 0x5f,0x24,
+ 0x60,0x07,
+ 0x61,0x15,
+ 0x62,0x04,
+ 0x64,0x03,
+ 0x66,0xe8,
+ 0x67,0x86,
+ 0x68,0xa2,
+ 0x69,0x18,
+ 0x6a,0x0f,
+ 0x6b,0x00,
+ 0x6c,0x5f,
+ 0x6d,0x8f,
+ 0x6e,0x55,
+ 0x6f,0x38,
+ 0x70,0x20, //0x15
+ 0x71,0x35, //0x33
+ 0x72,0xdc,
+ 0x73,0x80,
+ 0x74,0x02,
+ 0x75,0x3f,
+ 0x76,0x02,
+ 0x77,0x43,//54
+ 0x78,0x88,
+ 0x79,0x81,
+ 0x7a,0x81,
+ 0x7b,0x22,
+ 0x7c,0xff,
+ 0x93,0x44,
+ 0x94,0xfc,
+ 0x95,0x0a,
+ 0x96,0xe8,
+ 0x97,0x44,
+ 0x98,0xf8,
+ 0xb1,0x38,
+ 0xb2,0x38,
+ 0xbd,0x38,
+ 0xbe,0x36,
+ 0xd0,0xc9,
+ 0xd1,0x10,
+
+ 0xd3,0x80,
+ 0xd5,0xf2,
+ 0xd6,0x16,
+ 0xdb,0x92,
+ 0xdc,0xa5,
+ 0xdf,0x23,
+ 0xd9,0x00,
+ 0xda,0x00,
+ 0xe0,0x09,
+
+ 0xed,0x04,
+ 0xee,0xa0,
+ 0xef,0x40,
+ 0x80,0x03,
+ 0x80,0x03,
+
+
+ 0x9F,0x10,
+ 0xA0,0x20,
+ 0xA1,0x38,
+ 0xA2,0x4E,
+ 0xA3,0x63,
+ 0xA4,0x76,
+ 0xA5,0x87,
+ 0xA6,0xA2,
+ 0xA7,0xB8,
+ 0xA8,0xCA,
+ 0xA9,0xD8,
+ 0xAA,0xE3,
+ 0xAB,0xEB,
+ 0xAC,0xF0,
+ 0xAD,0xF8,
+ 0xAE,0xFD,
+ 0xAF,0xFF,
+
+ 0xc0,0x00,
+ 0xc1,0x10,
+ 0xc2,0x1C,
+ 0xc3,0x30,
+ 0xc4,0x43,
+ 0xc5,0x54,
+ 0xc6,0x65,
+ 0xc7,0x75,
+ 0xc8,0x93,
+ 0xc9,0xB0,
+ 0xca,0xCB,
+ 0xcb,0xE6,
+ 0xcc,0xFF,
+ 0xf0,0x02,
+ 0xf1,0x01,
+ 0xf2,0x01,
+ 0xf3,0x30,
+ 0xf9,0x9f,
+ 0xfa,0x78,
+ //----------------------------
+ 0xfe,0x01,// set page1
+ 0x00,0xf5,
+ 0x02,0x1a,
+ 0x0a,0xa0,
+ 0x0b,0x60,
+ 0x0c,0x08,
+ 0x0e,0x4c,
+ 0x0f,0x39,
+ 0x11,0x3f,
+ 0x12,0x72,
+ 0x13,0x13,
+ 0x14,0x3c,
+ 0x15,0x43,
+ 0x16,0xc2,
+ 0x17,0xa8,
+ 0x18,0x18,
+ 0x19,0x40,
+ 0x1a,0xd0,
+ 0x1b,0xf5,
+ 0x70,0x40,
+ 0x71,0x58,
+ 0x72,0x30,
+ 0x73,0x48,
+ 0x74,0x20,
+ 0x75,0x60,
+ 0x77,0x20,
+ 0x78,0x32,
+ 0x30,0x03,
+ 0x31,0x40,
+ 0x32,0xe0,
+ 0x33,0xe0,
+ 0x34,0xe0,
+ 0x35,0xb0,
+ 0x36,0xc0,
+ 0x37,0xc0,
+ 0x38,0x04,
+ 0x39,0x09,
+ 0x3a,0x12,
+ 0x3b,0x1C,
+ 0x3c,0x28,
+ 0x3d,0x31,
+ 0x3e,0x44,
+ 0x3f,0x57,
+ 0x40,0x6C,
+ 0x41,0x81,
+ 0x42,0x94,
+ 0x43,0xA7,
+ 0x44,0xB8,
+ 0x45,0xD6,
+ 0x46,0xEE,
+ 0x47,0x0d,
+ 0xfe,0x00,// set page0
+
+ //-----------Update the registers 2010/07/06-----------
+ //Registers of Page0
+ 0xfe,0x00,// set page0
+ 0x10,0x26,
+ 0x11,0x0d, // fd
+
+ 0x1a,0x2a, // 1e
+
+
+ 0x1c,0x49,// c1
+ 0x1d,0x9a,// 08
+
+
+ 0x1e,0x61,// 60
+
+ 0x3a,0x20,
+
+ 0x50,0x14, // 10
+
+
+ 0x53,0x80,
+ 0x56,0x80,
+
+ 0x8b,0x20,//LSC
+ 0x8c,0x20,
+ 0x8d,0x20,
+ 0x8e,0x14,
+ 0x8f,0x10,
+ 0x90,0x14,
+
+ 0x94,0x02,
+ 0x95,0x07,
+ 0x96,0xe0,
+
+ 0xb1,0x40,// YCPT
+ 0xb2,0x40,
+ 0xb3,0x47,//42
+ 0xb5,0xf8,//fa
+ 0xb6,0xe0,
+
+ 0x57,0x7f,//0x7c
+ 0x58,0x7c,
+ 0x59,0x80,
+
+ 0xd0,0xcb,// AECT c9
+
+
+ 0xd3,0x48,// 80
+
+ 0xf2,0x02,
+ 0xf7,0x12,
+ 0xf8,0x0a,
+
+ //Registers of Page1
+ 0xfe,0x01,// set page1
+ 0x02,0x20,
+ 0x04,0x10,
+ 0x05,0x08,
+ 0x06,0x20,
+ 0x08,0x0a,
+
+ 0x0e,0x44,
+ 0x0f,0x32,
+ 0x10,0x41,
+ 0x11,0x37,
+ 0x12,0x22,
+ 0x13,0x19,
+ 0x14,0x44,
+ 0x15,0x44,
+
+ 0x19,0x50,
+ 0x1a,0xd8,
+
+ 0x32,0x10,
+
+ 0x35,0x00,
+ 0x36,0x80,
+ 0x37,0x00,
+ //-----------Update the registers end---------//
+ 0xfe,0x00,// set page0
+ 0xd2,0x90,
+};
+
+uint8_t gc0308_default_regs_exit[]={
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-gt7302-gmt.h b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-gt7302-gmt.h
new file mode 100755
index 00000000..5dc8c66c
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-gt7302-gmt.h
@@ -0,0 +1,609 @@
+#ifndef GC0308_H
+#define GC0308_H
+
+
+// Scene Mode
+uint8_t gc0308_scene_mode_auto[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x30,
+};
+
+uint8_t gc0308_scene_mode_night[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x30,
+};
+
+
+// White Balance
+uint8_t gc0308_wb_auto [] = {
+ 0xfe, 0x00, // set page0
+ 0x5a, 0x56,
+ 0x5b, 0x40,
+ 0x5c, 0x4a,
+ 0x22, 0x57,
+};
+
+uint8_t gc0308_wb_incandescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x48,
+ 0x5b, 0x40,
+ 0x5c, 0x5c,
+};
+
+uint8_t gc0308_wb_fluorescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x40,
+ 0x5b, 0x42,
+ 0x5c, 0x50,
+};
+
+uint8_t gc0308_wb_daylight [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x74,
+ 0x5b, 0x52,
+ 0x5c, 0x40,
+};
+
+uint8_t gc0308_wb_cloudy [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x8c,
+ 0x5b, 0x50,
+ 0x5c, 0x40,
+};
+
+
+// Exposure
+uint8_t gc0308_exposure_neg6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xe0,
+ 0xd3, 0x38,
+};
+
+uint8_t gc0308_exposure_neg3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xf0,
+ 0xd3, 0x40,
+};
+
+uint8_t gc0308_exposure_zero[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xfd,
+ 0xd3, 0x40,
+};
+
+uint8_t gc0308_exposure_pos3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x10,
+ 0xd3, 0x50,
+};
+
+uint8_t gc0308_exposure_pos6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x20,
+ 0xd3, 0x58,
+};
+
+uint8_t gc0308_antibanding_auto[] = {
+ #if 0
+ 0xfe,0x00,
+
+ 0x01,0x97,
+ 0x02,0x84,
+ 0x0f,0x03,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x3e, //anti-flicker step [7:0]
+
+ 0xe4,0x02, //exp level 0 12.5fps
+ 0xe5,0x6c,
+ 0xe6,0x02, //exp level 1 10fps
+ 0xe7,0xe8,
+ 0xe8,0x03, //exp level 2 7.69fps
+ 0xe9,0xe0,
+ 0xea,0x05, //exp level 3 5.00fps
+ 0xeb,0xd0,
+ #else
+ 0xfe,0x00,
+
+ 0x01,0x97,
+ 0x02,0x84,
+ 0x0f,0x03,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x3e, //anti-flicker step [7:0]
+
+ 0xe4,0x02, //exp level 0 12.5fps
+ 0xe5,0x6c,
+ 0xe6,0x02, //exp level 1 10fps
+ 0xe7,0xe8,
+ 0xe8,0x03, //exp level 2 7.69fps
+ 0xe9,0xe0,
+ 0xea,0x05, //exp level 3 5.00fps
+ 0xeb,0xd0,
+ #endif
+
+};
+
+uint8_t gc0308_antibanding_50hz[] = {
+ 0xfe,0x00,
+
+ 0x01,0x26,
+ 0x02,0x98,
+ 0x0f,0x03,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x50, //anti-flicker step [7:0]
+
+ 0xe4,0x02, //exp level 0 12.5fps
+ 0xe5,0x80,
+ 0xe6,0x03, //exp level 1 10fps
+ 0xe7,0x20,
+ 0xe8,0x04, //exp level 2 7.69fps
+ 0xe9,0x10,
+ 0xea,0x06, //exp level 3 5.00fps
+ 0xeb,0x40,
+
+};
+
+uint8_t gc0308_antibanding_60hz[] = {
+ 0xfe,0x00,
+
+ 0x01,0x97,
+ 0x02,0x84,
+ 0x0f,0x03,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x3e, //anti-flicker step [7:0]
+
+ 0xe4,0x02, //exp level 0 12.5fps
+ 0xe5,0x6c,
+ 0xe6,0x02, //exp level 1 10fps
+ 0xe7,0xe8,
+ 0xe8,0x03, //exp level 2 7.69fps
+ 0xe9,0xe0,
+ 0xea,0x05, //exp level 3 5.00fps
+ 0xeb,0xd0,
+
+};
+
+uint8_t gc0308_antibanding_off[] = {
+ 0xfe,0x00,
+
+ 0x01,0x26,
+ 0x02,0x98,
+ 0x0f,0x03,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x50, //anti-flicker step [7:0]
+
+ 0xe4,0x02, //exp level 0 12.5fps
+ 0xe5,0x80,
+ 0xe6,0x03, //exp level 1 10fps
+ 0xe7,0x20,
+ 0xe8,0x04, //exp level 2 7.69fps
+ 0xe9,0x10,
+ 0xea,0x06, //exp level 3 5.00fps
+ 0xeb,0x40,
+
+};
+
+
+// Resolution
+uint8_t gc0308_320x240[]= {
+ 0xfe, 0x01,
+ 0x54, 0x22,
+ 0x55, 0x03,
+ 0x56, 0x00,
+ 0x57, 0x00,
+ 0x58, 0x00,
+ 0x59, 0x00,
+ 0xfe, 0x00,
+};
+
+uint8_t gc0308_640x480[] = {
+ 0xfe,0x01,
+ 0x54,0x11,
+ 0x55,0x03,
+ 0x56,0x00,
+ 0x57,0x00,
+ 0x58,0x00,
+ 0x59,0x00,
+ 0xfe,0x00,
+ 0x46,0x00,
+ 0xfe,0x00,
+};
+
+uint8_t gc0308_default_regs_init[] = {
+ 0xfe,0x80,
+
+ 0xfe,0x00, // set page0
+
+ 0xd2,0x10, // close AEC
+ 0x22,0x55, // close AWB
+
+ 0x03,0x01,
+ 0x04,0x2c,
+ 0x5a,0x56,
+ 0x5b,0x40,
+ 0x5c,0x4a,
+
+ 0x22,0x57, // Open AWB
+
+ 0x01,0xfa,
+ 0x02,0x70,
+ 0x0f,0x01,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x64, //anti-flicker step [7:0]
+
+ 0xe4,0x02, //exp level 1 16.67fps
+ 0xe5,0x58,
+ 0xe6,0x03, //exp level 2 12.5fps
+ 0xe7,0x20,
+ 0xe8,0x04, //exp level 3 8.33fps
+ 0xe9,0xb0,
+ 0xea,0x09, //exp level 4 4.00fps
+ 0xeb,0xc4,
+
+ 0xec,0x30,
+
+ 0x05,0x00,
+ 0x06,0x00,
+ 0x07,0x00,
+ 0x08,0x00,
+ 0x09,0x01,
+ 0x0a,0xe8,
+ 0x0b,0x02,
+ 0x0c,0x88,
+ 0x0d,0x02,
+ 0x0e,0x02,
+ 0x10,0x22,
+ 0x11,0xfd,
+ 0x12,0x2a,
+ 0x13,0x00,
+ 0x14,0x10, //0x10
+
+ 0x15,0x0a,
+ 0x16,0x05,
+ 0x17,0x01,
+ 0x18,0x44,
+ 0x19,0x44,
+ 0x1a,0x1e,
+ 0x1b,0x00,
+ 0x1c,0xc1,
+ 0x1d,0x08,
+ 0x1e,0x60,
+ 0x1f,0x16,
+
+
+
+ 0x20,0xff,
+ 0x21,0xf8,
+ 0x22,0x57,
+ 0x24,0xa2,
+ 0x25,0x0f,
+
+ //output sync_mode
+
+
+ 0x26,0x02, //0x03 20101016 zhj
+
+ 0x2f,0x01,
+ 0x30,0xf7,
+ 0x31,0x50,
+ 0x32,0x00,
+ 0x39,0x04,
+ 0x3a,0x18,
+ 0x3b,0x20,
+ 0x3c,0x00,
+ 0x3d,0x00,
+ 0x3e,0x00,
+ 0x3f,0x00,
+ 0x50,0x10,
+ 0x53,0x82,
+ 0x54,0x80,
+ 0x55,0x80,
+ 0x56,0x82,
+ 0x8b,0x40,
+ 0x8c,0x40,
+ 0x8d,0x40,
+ 0x8e,0x2e,
+ 0x8f,0x2e,
+ 0x90,0x2e,
+ 0x91,0x3c,
+ 0x92,0x50,
+ 0x5d,0x12,
+ 0x5e,0x1a,
+ 0x5f,0x24,
+ 0x60,0x07,
+ 0x61,0x15,
+ 0x62,0x0c,
+ 0x64,0x03,
+ 0x66,0xe8,
+ 0x67,0x86,
+ 0x68,0xa2,
+ 0x69,0x18,
+ 0x6a,0x0f,
+ 0x6b,0x00,
+ 0x6c,0x5f,
+ 0x6d,0x8f,
+ 0x6e,0x55,
+ 0x6f,0x38,
+ 0x70,0x15,
+ 0x71,0x33,
+ 0x72,0xdc,
+ 0x73,0x80,
+ 0x74,0x02,
+ 0x75,0x3f,
+ 0x76,0x02,
+ 0x77,0x65,
+ 0x78,0x88,
+ 0x79,0x81,
+ 0x7a,0x81,
+ 0x7b,0x22,
+ 0x7c,0xff,
+ 0x93,0x48,
+ 0x94,0x00,
+ 0x95,0x05,
+ 0x96,0xe8,
+ 0x97,0x40,
+ 0x98,0xf0,
+ 0xb1,0x38,
+ 0xb2,0x38,
+ 0xbd,0x38,
+ 0xbe,0x36,
+ 0xd0,0xc9,
+ 0xd1,0x10,
+
+ 0xd3,0x80,
+ 0xd5,0xf2,
+ 0xd6,0x16,
+ 0xdb,0x92,
+ 0xdc,0xa5,
+ 0xdf,0x23,
+ 0xd9,0x00,
+ 0xda,0x00,
+ 0xe0,0x09,
+
+ 0xed,0x04,
+ 0xee,0xa0,
+ 0xef,0x40,
+ 0x80,0x03,
+ 0x80,0x03,
+/*
+ 0x9F,0x0B,
+ 0xA0,0x16,
+ 0xA1,0x29,
+ 0xA2,0x3C,
+ 0xA3,0x4F,
+ 0xA4,0x5F,
+ 0xA5,0x6F,
+ 0xA6,0x8A,
+ 0xA7,0x9F,
+ 0xA8,0xB4,
+ 0xA9,0xC6,
+ 0xAA,0xD3,
+ 0xAB,0xDD,
+ 0xAC,0xE5,
+ 0xAD,0xF1,
+ 0xAE,0xFA,
+ 0xAF,0xFF,
+*/
+
+
+ 0x9F,0x10,
+ 0xA0,0x20,
+ 0xA1,0x38,
+ 0xA2,0x4E,
+ 0xA3,0x63,
+ 0xA4,0x76,
+ 0xA5,0x87,
+ 0xA6,0xA2,
+ 0xA7,0xB8,
+ 0xA8,0xCA,
+ 0xA9,0xD8,
+ 0xAA,0xE3,
+ 0xAB,0xEB,
+ 0xAC,0xF0,
+ 0xAD,0xF8,
+ 0xAE,0xFD,
+ 0xAF,0xFF,
+
+ /*
+ 0x9F,0x14,
+ 0xA0,0x24,
+ 0xA1,0x3e,
+ 0xA2,0x50,
+ 0xA3,0x63,
+ 0xA4,0x76,
+ 0xA5,0x87,
+ 0xA6,0xA2,
+ 0xA7,0xB8,
+ 0xA8,0xCA,
+ 0xA9,0xD8,
+ 0xAA,0xE3,
+ 0xAB,0xEB,
+ 0xAC,0xF0,
+ 0xAD,0xF8,
+ 0xAE,0xFD,
+ 0xAF,0xFF,
+ */
+ /*
+ 0x9F,0x0E,
+ 0xA0,0x1C,
+ 0xA1,0x34,
+ 0xA2,0x48,
+ 0xA3,0x5A,
+ 0xA4,0x6B,
+ 0xA5,0x7B,
+ 0xA6,0x95,
+ 0xA7,0xAB,
+ 0xA8,0xBF,
+ 0xA9,0xCE,
+ 0xAA,0xD9,
+ 0xAB,0xE4,
+ 0xAC,0xEC,
+ 0xAD,0xF7,
+ 0xAE,0xFD,
+ 0xAF,0xFF,
+*/
+
+ 0xc0,0x00,
+ 0xc1,0x10,
+ 0xc2,0x1C,
+ 0xc3,0x30,
+ 0xc4,0x43,
+ 0xc5,0x54,
+ 0xc6,0x65,
+ 0xc7,0x75,
+ 0xc8,0x93,
+ 0xc9,0xB0,
+ 0xca,0xCB,
+ 0xcb,0xE6,
+ 0xcc,0xFF,
+ 0xf0,0x02,
+ 0xf1,0x01,
+ 0xf2,0x01,
+ 0xf3,0x30,
+ 0xf9,0x9f,
+ 0xfa,0x78,
+ //----------------------------
+ 0xfe,0x01,// set page1
+ 0x00,0xf5,
+ 0x02,0x1a,
+ 0x0a,0xa0,
+ 0x0b,0x60,
+ 0x0c,0x08,
+ 0x0e,0x4c,
+ 0x0f,0x39,
+ 0x11,0x3f,
+ 0x12,0x72,
+ 0x13,0x13,
+ 0x14,0x42,
+ 0x15,0x43,
+ 0x16,0xc2,
+ 0x17,0xa8,
+ 0x18,0x18,
+ 0x19,0x40,
+ 0x1a,0xd0,
+ 0x1b,0xf5,
+ 0x70,0x40,
+ 0x71,0x58,
+ 0x72,0x30,
+ 0x73,0x48,
+ 0x74,0x20,
+ 0x75,0x60,
+ 0x77,0x20,
+ 0x78,0x32,
+ 0x30,0x03,
+ 0x31,0x40,
+ 0x32,0xe0,
+ 0x33,0xe0,
+ 0x34,0xe0,
+ 0x35,0xb0,
+ 0x36,0xc0,
+ 0x37,0xc0,
+ 0x38,0x04,
+ 0x39,0x09,
+ 0x3a,0x12,
+ 0x3b,0x1C,
+ 0x3c,0x28,
+ 0x3d,0x31,
+ 0x3e,0x44,
+ 0x3f,0x57,
+ 0x40,0x6C,
+ 0x41,0x81,
+ 0x42,0x94,
+ 0x43,0xA7,
+ 0x44,0xB8,
+ 0x45,0xD6,
+ 0x46,0xEE,
+ 0x47,0x0d,
+ 0xfe,0x00,// set page0
+
+ //-----------Update the registers 2010/07/06-----------
+ //Registers of Page0
+ 0xfe,0x00,// set page0
+ 0x10,0x26,
+ 0x11,0x0d, // fd
+
+ 0x1a,0x2a, // 1e
+
+
+ 0x1c,0x49,// c1
+ 0x1d,0x9a,// 08
+
+
+ 0x1e,0x61,// 60
+
+ 0x3a,0x20,
+
+ 0x50,0x14, // 10
+
+
+ 0x53,0x80,
+ 0x56,0x80,
+
+ 0x8b,0x20,//LSC
+ 0x8c,0x20,
+ 0x8d,0x20,
+ 0x8e,0x14,
+ 0x8f,0x10,
+ 0x90,0x14,
+
+ 0x94,0x02,
+ 0x95,0x07,
+ 0x96,0xe0,
+
+ 0xb1,0x3a,// YCPT
+ 0xb2,0x3a,
+ 0xb3,0x38,// 3c
+ 0xb6,0xe0,
+
+ 0xd0,0xcb,// AECT c9
+
+
+ 0xd3,0x40,// 80
+ 0xb5,0xf8,
+
+ 0xf2,0x02,
+ 0xf7,0x12,
+ 0xf8,0x0a,
+
+ //Registers of Page1
+ 0xfe,0x01,// set page1
+ 0x02,0x20,
+ 0x04,0x10,
+ 0x05,0x08,
+ 0x06,0x20,
+ 0x08,0x0a,
+
+ 0x0e,0x44,
+ 0x0f,0x32,
+ 0x10,0x41,
+ 0x11,0x37,
+ 0x12,0x22,
+ 0x13,0x19,
+ 0x14,0x44,
+ 0x15,0x44,
+
+ 0x19,0x50,
+ 0x1a,0xd8,
+
+ 0x32,0x10,
+
+ 0x35,0x00,
+ 0x36,0x80,
+ 0x37,0x00,
+ //-----------Update the registers end---------//
+ 0xfe,0x00,// set page0
+ 0xd2,0x90,
+};
+
+uint8_t gc0308_default_regs_exit[]={
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-jfs0672-clw-gt7305.h b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-jfs0672-clw-gt7305.h
new file mode 100755
index 00000000..4f9ad904
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-jfs0672-clw-gt7305.h
@@ -0,0 +1,481 @@
+#ifndef GC0308_H
+#define GC0308_H
+
+
+// Scene Mode
+uint8_t gc0308_scene_mode_auto[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x20,
+};
+
+uint8_t gc0308_scene_mode_night[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x30,
+};
+
+
+// White Balance
+uint8_t gc0308_wb_auto [] = {
+ 0xfe, 0x00, // set page0
+ 0x5a, 0x56,
+ 0x5b, 0x40,
+ 0x5c, 0x4a,
+ 0x22, 0x57,
+};
+
+uint8_t gc0308_wb_incandescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x48,
+ 0x5b, 0x40,
+ 0x5c, 0x5c,
+};
+
+uint8_t gc0308_wb_fluorescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x40,
+ 0x5b, 0x42,
+ 0x5c, 0x50,
+};
+
+uint8_t gc0308_wb_daylight [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x74,
+ 0x5b, 0x52,
+ 0x5c, 0x40,
+};
+
+uint8_t gc0308_wb_cloudy [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x8c,
+ 0x5b, 0x50,
+ 0x5c, 0x40,
+};
+
+
+// Exposure
+uint8_t gc0308_exposure_neg6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xe0,
+ 0xd3, 0x38,
+};
+
+uint8_t gc0308_exposure_neg3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xf0,
+ 0xd3, 0x40,
+};
+
+uint8_t gc0308_exposure_zero[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xfa,//0x00
+ 0xd3, 0x44,//0x48 hyper
+};
+
+uint8_t gc0308_exposure_pos3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x10,
+ 0xd3, 0x50,
+};
+
+uint8_t gc0308_exposure_pos6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x20,
+ 0xd3, 0x58,
+};
+
+
+// Resolution
+uint8_t gc0308_320x240[]= {
+ 0xfe, 0x01,
+ 0x54, 0x22,
+ 0x55, 0x03,
+ 0x56, 0x00,
+ 0x57, 0x00,
+ 0x58, 0x00,
+ 0x59, 0x00,
+ 0xfe, 0x00,
+};
+
+uint8_t gc0308_640x480[] = {
+ 0xfe,0x01,
+ 0x54,0x11,
+ 0x55,0x03,
+ 0x56,0x00,
+ 0x57,0x00,
+ 0x58,0x00,
+ 0x59,0x00,
+ 0xfe,0x00,
+ 0x46,0x00,
+ 0xfe,0x00,
+};
+
+uint8_t gc0308_default_regs_init[] = {
+ 0xfe,0x80,
+
+ 0xfe,0x00, // set page0
+
+ 0xd2,0x10, // close AEC
+ 0x22,0x55, // close AWB
+
+ 0x03,0x01,
+ 0x04,0x2c,
+ 0x5a,0x56,
+ 0x5b,0x40,
+ 0x5c,0x4a,
+
+ 0x22,0x57, // Open AWB
+
+ /*0x01,0xfa,
+ 0x02,0x70,
+ 0x0f,0x01,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x64, //anti-flicker step [7:0]
+
+ 0xe4,0x02, //exp level 1 16.67fps
+ 0xe5,0x58,
+ 0xe6,0x03, //exp level 2 12.5fps
+ 0xe7,0x20,
+ 0xe8,0x04, //exp level 3 8.33fps
+ 0xe9,0xb0,
+ 0xea,0x09, //exp level 4 4.00fps
+ 0xeb,0xc4,*/
+
+ 0x01,0xfa,
+ 0x02,0x70,
+ 0x0f,0x01,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x64, //anti-flicker step [7:0]
+
+ 0xe4,0x02, //exp level 1 16.67fps
+ 0xe5,0x58,
+ 0xe6,0x03, //exp level 2 12.5fps
+ 0xe7,0x20,
+ 0xe8,0x05, //exp level 3 7.14fps
+ 0xe9,0x78,
+ 0xea,0x09, //exp level 4 4.00fps
+ 0xeb,0xc4,//hyper
+
+ 0xec,0x20,
+
+ 0x05,0x00,
+ 0x06,0x00,
+ 0x07,0x00,
+ 0x08,0x00,
+ 0x09,0x01,
+ 0x0a,0xe8,
+ 0x0b,0x02,
+ 0x0c,0x88,
+ 0x0d,0x02,
+ 0x0e,0x02,
+ 0x10,0x22,
+ 0x11,0xfd,
+ 0x12,0x2a,
+ 0x13,0x00,
+ 0x14,0x10, //0x10
+
+ 0x15,0x0a,
+ 0x16,0x05,
+ 0x17,0x01,
+ 0x18,0x44,
+ 0x19,0x44,
+ 0x1a,0x1e,
+ 0x1b,0x00,
+ 0x1c,0xc1,
+ 0x1d,0x08,
+ 0x1e,0x60,
+ 0x1f,0x16,
+
+
+
+ 0x20,0xff,
+ 0x21,0xf8,
+ 0x22,0x57,
+ 0x24,0xa2,
+ 0x25,0x0f,
+
+ //output sync_mode
+
+
+ 0x26,0x02, //0x03 20101016 zhj
+
+ 0x2f,0x01,
+ 0x30,0xf7,
+ 0x31,0x50,
+ 0x32,0x00,
+ 0x39,0x04,
+ 0x3a,0x18,
+ 0x3b,0x20,
+ 0x3c,0x00,
+ 0x3d,0x00,
+ 0x3e,0x00,
+ 0x3f,0x00,
+ 0x50,0x10,
+ 0x53,0x82,
+ 0x54,0x80,
+ 0x55,0x80,
+ 0x56,0x82,
+ 0x8b,0x40,
+ 0x8c,0x40,
+ 0x8d,0x40,
+ 0x8e,0x2e,
+ 0x8f,0x2e,
+ 0x90,0x2e,
+ 0x91,0x3c,
+ 0x92,0x50,
+ 0x5d,0x12,
+ 0x5e,0x1a,
+ 0x5f,0x24,
+ 0x60,0x07,
+ 0x61,0x15,
+ 0x62,0x08,
+ 0x64,0x03,
+ 0x66,0xe8,
+ 0x67,0x86,
+ 0x68,0xa2,
+ 0x69,0x18,
+ 0x6a,0x0f,
+ 0x6b,0x00,
+ 0x6c,0x5f,
+ 0x6d,0x8f,
+ 0x6e,0x55,
+ 0x6f,0x38,
+ 0x70,0x15,
+ 0x71,0x33,
+ 0x72,0xdc,
+ 0x73,0x80,
+ 0x74,0x02,
+ 0x75,0x3f,
+ 0x76,0x02,
+ 0x77,0x54,
+ 0x78,0x88,
+ 0x79,0x81,
+ 0x7a,0x81,
+ 0x7b,0x22,
+ 0x7c,0xff,
+ 0x93,0x53,//0x48
+ 0x94,0x00,
+ 0x95,0x02,//05 hyper
+ 0x96,0xe8,
+ 0x97,0x40,
+ 0x98,0xf0,
+ 0xb1,0x42,//0x38 hyper
+ 0xb2,0x42,
+ 0xbd,0x38,
+ 0xbe,0x36,
+ 0xd0,0xcb,//0xc9 hyper
+ 0xd1,0x10,
+
+ 0xd3,0x44,//0x80 hyper
+ 0xd5,0xf2,
+ 0xd6,0x16,
+ 0xdb,0x92,
+ 0xdc,0xa5,
+ 0xdf,0x23,
+ 0xd9,0x00,
+ 0xda,0x00,
+ 0xe0,0x09,
+
+ 0xed,0x04,
+ 0xee,0xa0,
+ 0xef,0x40,
+ 0x80,0x03,
+ 0x80,0x03,
+
+#if 0 //gamma 3
+ 0x9F,0x10,
+ 0xA0,0x20,
+ 0xA1,0x38,
+ 0xA2,0x4E,
+ 0xA3,0x63,
+ 0xA4,0x76,
+ 0xA5,0x87,
+ 0xA6,0xA2,
+ 0xA7,0xB8,
+ 0xA8,0xCA,
+ 0xA9,0xD8,
+ 0xAA,0xE3,
+ 0xAB,0xEB,
+ 0xAC,0xF0,
+ 0xAD,0xF8,
+ 0xAE,0xFD,
+ 0xAF,0xFF,
+#endif
+ //gamma 2 2013-7-24 dustin
+ 0x9F,0x0E,
+ 0xA0,0x1C,
+ 0xA1,0x34,
+ 0xA2,0x48,
+ 0xA3,0x5A,
+ 0xA4,0x6B,
+ 0xA5,0x7B,
+ 0xA6,0x95,
+ 0xA7,0xAB,
+ 0xA8,0xBF,
+ 0xA9,0xCE,
+ 0xAA,0xD9,
+ 0xAB,0xE4,
+ 0xAC,0xEC,
+ 0xAD,0xF7,
+ 0xAE,0xFD,
+ 0xAF,0xFF,
+
+ 0xc0,0x00,
+ 0xc1,0x10,
+ 0xc2,0x1C,
+ 0xc3,0x30,
+ 0xc4,0x43,
+ 0xc5,0x54,
+ 0xc6,0x65,
+ 0xc7,0x75,
+ 0xc8,0x93,
+ 0xc9,0xB0,
+ 0xca,0xCB,
+ 0xcb,0xE6,
+ 0xcc,0xFF,
+ 0xf0,0x02,
+ 0xf1,0x01,
+ 0xf2,0x01,
+ 0xf3,0x30,
+ 0xf9,0x9f,
+ 0xfa,0x78,
+ //----------------------------
+ 0xfe,0x01,// set page1
+ 0x00,0xf5,
+ 0x02,0x1a,
+ 0x0a,0xa0,
+ 0x0b,0x60,
+ 0x0c,0x08,
+ 0x0e,0x4c,
+ 0x0f,0x39,
+ 0x11,0x3f,
+ 0x12,0x72,
+ 0x13,0x13,
+ 0x14,0x42,
+ 0x15,0x43,
+ 0x16,0xc2,
+ 0x17,0xa8,
+ 0x18,0x18,
+ 0x19,0x40,
+ 0x1a,0xd0,
+ 0x1b,0xf5,
+ 0x70,0x40,
+ 0x71,0x58,
+ 0x72,0x30,
+ 0x73,0x48,
+ 0x74,0x20,
+ 0x75,0x60,
+ 0x77,0x20,
+ 0x78,0x32,
+ 0x30,0x03,
+ 0x31,0x40,
+ 0x32,0xe0,
+ 0x33,0xe0,
+ 0x34,0xe0,
+ 0x35,0xb0,
+ 0x36,0xc0,
+ 0x37,0xc0,
+ 0x38,0x04,
+ 0x39,0x09,
+ 0x3a,0x12,
+ 0x3b,0x1C,
+ 0x3c,0x28,
+ 0x3d,0x31,
+ 0x3e,0x44,
+ 0x3f,0x57,
+ 0x40,0x6C,
+ 0x41,0x81,
+ 0x42,0x94,
+ 0x43,0xA7,
+ 0x44,0xB8,
+ 0x45,0xD6,
+ 0x46,0xEE,
+ 0x47,0x0d,
+ 0xfe,0x00,// set page0
+
+ //-----------Update the registers 2010/07/06-----------
+ //Registers of Page0
+ 0xfe,0x00,// set page0
+ 0x10,0x26,
+ 0x11,0x0d, // fd
+
+ 0x1a,0x2a, // 1e
+
+
+ 0x1c,0x49,// c1
+ 0x1d,0x9a,// 08
+
+
+ 0x1e,0x61,// 60
+
+ 0x3a,0x20,
+
+ 0x50,0x14, // 10
+
+
+ 0x53,0x80,
+ 0x56,0x80,
+
+ 0x8b,0x20,//LSC
+ 0x8c,0x20,
+ 0x8d,0x20,
+ 0x8e,0x14,
+ 0x8f,0x10,
+ 0x90,0x14,
+
+ 0x94,0x02,
+ 0x95,0x07,
+ 0x96,0xe0,
+
+ 0xb1,0x42,// YCPT
+ 0xb2,0x42,//40 hyper
+ 0xb3,0x42,//40 2013-7-24 dustin//45 hyper
+ 0xb6,0xe0,
+
+ 0xd0,0xcb,// AECT c9
+
+
+ 0xd3,0x44,// 80//48 hyper
+
+ 0xf2,0x02,
+ 0xf7,0x12,
+ 0xf8,0x0a,
+
+ //Registers of Page1
+ 0xfe,0x01,// set page1
+ 0x02,0x20,
+ 0x04,0x10,
+ 0x05,0x08,
+ 0x06,0x20,
+ 0x08,0x0a,
+
+ 0x0e,0x44,
+ 0x0f,0x32,
+ 0x10,0x41,
+ 0x11,0x37,
+ 0x12,0x22,
+ 0x13,0x19,
+ 0x14,0x44,
+ 0x15,0x44,
+
+ 0x19,0x50,
+ 0x1a,0xd8,
+
+ 0x32,0x10,
+
+ 0x35,0x00,
+ 0x36,0x80,
+ 0x37,0x00,
+ //-----------Update the registers end---------//
+ 0xfe,0x00,// set page0
+ 0xd2,0x90,
+};
+
+uint8_t gc0308_default_regs_exit[]={
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-kyt-110_v1.0.h b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-kyt-110_v1.0.h
new file mode 100755
index 00000000..9bbaab31
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-kyt-110_v1.0.h
@@ -0,0 +1,446 @@
+#ifndef GC0308_H
+#define GC0308_H
+
+
+// Scene Mode
+uint8_t gc0308_scene_mode_auto[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x20,
+};
+
+uint8_t gc0308_scene_mode_night[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x30,
+};
+
+
+// White Balance
+uint8_t gc0308_wb_auto [] = {
+ 0xfe, 0x00, // set page0
+ 0x5a, 0x56,
+ 0x5b, 0x40,
+ 0x5c, 0x4a,
+ 0x22, 0x57,
+};
+
+uint8_t gc0308_wb_incandescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x48,
+ 0x5b, 0x40,
+ 0x5c, 0x5c,
+};
+
+uint8_t gc0308_wb_fluorescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x40,
+ 0x5b, 0x42,
+ 0x5c, 0x50,
+};
+
+uint8_t gc0308_wb_daylight [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x74,
+ 0x5b, 0x52,
+ 0x5c, 0x40,
+};
+
+uint8_t gc0308_wb_cloudy [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x8c,
+ 0x5b, 0x50,
+ 0x5c, 0x40,
+};
+
+
+// Exposure
+uint8_t gc0308_exposure_neg6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xe0,
+ 0xd3, 0x38,
+};
+
+uint8_t gc0308_exposure_neg3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xf0,
+ 0xd3, 0x40,
+};
+
+uint8_t gc0308_exposure_zero[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x00,
+ 0xd3, 0x48,
+};
+
+uint8_t gc0308_exposure_pos3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x10,
+ 0xd3, 0x50,
+};
+
+uint8_t gc0308_exposure_pos6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x20,
+ 0xd3, 0x58,
+};
+
+
+// Resolution
+uint8_t gc0308_320x240[]= {
+ 0xfe, 0x01,
+ 0x54, 0x22,
+ 0x55, 0x03,
+ 0x56, 0x00,
+ 0x57, 0x00,
+ 0x58, 0x00,
+ 0x59, 0x00,
+ 0xfe, 0x00,
+};
+
+uint8_t gc0308_640x480[] = {
+ 0xfe,0x01,
+ 0x54,0x11,
+ 0x55,0x03,
+ 0x56,0x00,
+ 0x57,0x00,
+ 0x58,0x00,
+ 0x59,0x00,
+ 0xfe,0x00,
+ 0x46,0x00,
+ 0xfe,0x00,
+};
+
+uint8_t gc0308_default_regs_init[] = {
+ 0xfe,0x80,
+
+ 0xfe,0x00, // set page0
+
+ 0xd2,0x10, // close AEC
+ 0x22,0x55, // close AWB
+
+ 0x03,0x01,
+ 0x04,0x2c,
+ 0x5a,0x56,
+ 0x5b,0x40,
+ 0x5c,0x4a,
+
+ 0x22,0x57, // Open AWB
+
+ 0x01,0xfa,
+ 0x02,0xd4,
+ 0x0f,0x01,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x64, //anti-flicker step [7:0]
+
+ 0xe4,0x02, //exp level 1 16.67fps
+ 0xe5,0x58,
+ 0xe6,0x03, //exp level 2 12.5fps
+ 0xe7,0xe8,
+ 0xe8,0x05, //exp level 3 8.33fps
+ 0xe9,0x78,
+ 0xea,0x09, //exp level 4 4.00fps
+ 0xeb,0xc4,
+
+ 0xec,0x20,
+
+ 0x05,0x00,
+ 0x06,0x00,
+ 0x07,0x00,
+ 0x08,0x00,
+ 0x09,0x01,
+ 0x0a,0xe8,
+ 0x0b,0x02,
+ 0x0c,0x88,
+ 0x0d,0x02,
+ 0x0e,0x02,
+ 0x10,0x22,
+ 0x11,0xfd,
+ 0x12,0x2a,
+ 0x13,0x00,
+ 0x14,0x10, //0x10
+
+ 0x15,0x0a,
+ 0x16,0x05,
+ 0x17,0x01,
+ 0x18,0x44,
+ 0x19,0x44,
+ 0x1a,0x1e,
+ 0x1b,0x00,
+ 0x1c,0xc1,
+ 0x1d,0x08,
+ 0x1e,0x60,
+ 0x1f,0x16,
+
+
+
+ 0x20,0xff,
+ 0x21,0xf8,
+ 0x22,0x57,
+ 0x24,0xa2,
+ 0x25,0x0f,
+
+ //output sync_mode
+
+
+ 0x26,0x02, //0x03 20101016 zhj
+
+ 0x2f,0x01,
+ 0x30,0xf7,
+ 0x31,0x50,
+ 0x32,0x00,
+ 0x39,0x04,
+ 0x3a,0x18,
+ 0x3b,0x20,
+ 0x3c,0x00,
+ 0x3d,0x00,
+ 0x3e,0x00,
+ 0x3f,0x00,
+ 0x50,0x10,
+ 0x53,0x82,
+ 0x54,0x80,
+ 0x55,0x80,
+ 0x56,0x82,
+ 0x8b,0x40,
+ 0x8c,0x40,
+ 0x8d,0x40,
+ 0x8e,0x2e,
+ 0x8f,0x2e,
+ 0x90,0x2e,
+ 0x91,0x3c,
+ 0x92,0x50,
+ 0x5d,0x12,
+ 0x5e,0x1a,
+ 0x5f,0x24,
+ 0x60,0x07,
+ 0x61,0x15,
+ 0x62,0x1f,
+ 0x64,0x03,
+ 0x66,0xe8,
+ 0x67,0x86,
+ 0x68,0xa2,
+ 0x69,0x18,
+ 0x6a,0x0f,
+ 0x6b,0x00,
+ 0x6c,0x5f,
+ 0x6d,0x8f,
+ 0x6e,0x55,
+ 0x6f,0x38,
+ 0x70,0x15,
+ 0x71,0x33,
+ 0x72,0xdc,
+ 0x73,0x80,
+ 0x74,0x02,
+ 0x75,0x3f,
+ 0x76,0x02,
+ 0x77,0x34,
+ 0x78,0x88,
+ 0x79,0x81,
+ 0x7a,0x81,
+ 0x7b,0x22,
+ 0x7c,0xff,
+ 0x93,0x48,
+ 0x94,0x00,
+ 0x95,0x05,
+ 0x96,0xe8,
+ 0x97,0x40,
+ 0x98,0xf0,
+ 0xb1,0x38,
+ 0xb2,0x38,
+ 0xbd,0x38,
+ 0xbe,0x36,
+ 0xd0,0xc9,
+ 0xd1,0x10,
+
+ 0xd3,0x80,
+ 0xd5,0xf2,
+ 0xd6,0x16,
+ 0xdb,0x92,
+ 0xdc,0xa5,
+ 0xdf,0x23,
+ 0xd9,0x00,
+ 0xda,0x00,
+ 0xe0,0x09,
+
+ 0xed,0x04,
+ 0xee,0xa0,
+ 0xef,0x40,
+ 0x80,0x03,
+ 0x80,0x03,
+
+
+ 0x9F,0x14,
+ 0xA0,0x24,
+ 0xA1,0x3e,
+ 0xA2,0x50,
+ 0xA3,0x63,
+ 0xA4,0x76,
+ 0xA5,0x87,
+ 0xA6,0xA2,
+ 0xA7,0xB8,
+ 0xA8,0xCA,
+ 0xA9,0xD8,
+ 0xAA,0xE3,
+ 0xAB,0xEB,
+ 0xAC,0xF0,
+ 0xAD,0xF8,
+ 0xAE,0xFD,
+ 0xAF,0xFF,
+
+ 0xc0,0x00,
+ 0xc1,0x10,
+ 0xc2,0x1C,
+ 0xc3,0x30,
+ 0xc4,0x43,
+ 0xc5,0x54,
+ 0xc6,0x65,
+ 0xc7,0x75,
+ 0xc8,0x93,
+ 0xc9,0xB0,
+ 0xca,0xCB,
+ 0xcb,0xE6,
+ 0xcc,0xFF,
+ 0xf0,0x02,
+ 0xf1,0x01,
+ 0xf2,0x01,
+ 0xf3,0x30,
+ 0xf9,0x9f,
+ 0xfa,0x78,
+ //----------------------------
+ 0xfe,0x01,// set page1
+ 0x00,0xf5,
+ 0x02,0x1a,
+ 0x0a,0xa0,
+ 0x0b,0x60,
+ 0x0c,0x08,
+ 0x0e,0x4c,
+ 0x0f,0x39,
+ 0x11,0x3f,
+ 0x12,0x72,
+ 0x13,0x13,
+ 0x14,0x42,
+ 0x15,0x43,
+ 0x16,0xc2,
+ 0x17,0xa8,
+ 0x18,0x18,
+ 0x19,0x40,
+ 0x1a,0xd0,
+ 0x1b,0xf5,
+ 0x70,0x40,
+ 0x71,0x58,
+ 0x72,0x30,
+ 0x73,0x48,
+ 0x74,0x20,
+ 0x75,0x60,
+ 0x77,0x20,
+ 0x78,0x32,
+ 0x30,0x03,
+ 0x31,0x40,
+ 0x32,0xe0,
+ 0x33,0xe0,
+ 0x34,0xe0,
+ 0x35,0xb0,
+ 0x36,0xc0,
+ 0x37,0xc0,
+ 0x38,0x04,
+ 0x39,0x09,
+ 0x3a,0x12,
+ 0x3b,0x1C,
+ 0x3c,0x28,
+ 0x3d,0x31,
+ 0x3e,0x44,
+ 0x3f,0x57,
+ 0x40,0x6C,
+ 0x41,0x81,
+ 0x42,0x94,
+ 0x43,0xA7,
+ 0x44,0xB8,
+ 0x45,0xD6,
+ 0x46,0xEE,
+ 0x47,0x0d,
+ 0xfe,0x00,// set page0
+
+ //-----------Update the registers 2010/07/06-----------
+ //Registers of Page0
+ 0xfe,0x00,// set page0
+ 0x10,0x26,
+ 0x11,0x0d, // fd
+
+ 0x1a,0x2a, // 1e
+
+
+ 0x1c,0x49,// c1
+ 0x1d,0x9a,// 08
+
+
+ 0x1e,0x61,// 60
+
+ 0x3a,0x20,
+
+ 0x50,0x14, // 10
+
+
+ 0x53,0x80,
+ 0x56,0x80,
+
+ 0x8b,0x20,//LSC
+ 0x8c,0x20,
+ 0x8d,0x20,
+ 0x8e,0x14,
+ 0x8f,0x10,
+ 0x90,0x14,
+
+ 0x94,0x02,
+ 0x95,0x07,
+ 0x96,0xe0,
+
+ 0xb1,0x3a,// YCPT
+ 0xb2,0x3a,
+ 0xb3,0x43,
+ 0xb6,0xe0,
+
+ 0xd0,0xcb,// AECT c9
+
+
+ 0xd3,0x45,// 80
+
+ 0xf2,0x02,
+ 0xf7,0x12,
+ 0xf8,0x0a,
+
+ //Registers of Page1
+ 0xfe,0x01,// set page1
+ 0x02,0x20,
+ 0x04,0x10,
+ 0x05,0x08,
+ 0x06,0x20,
+ 0x08,0x0a,
+
+ 0x0e,0x44,
+ 0x0f,0x32,
+ 0x10,0x41,
+ 0x11,0x37,
+ 0x12,0x22,
+ 0x13,0x19,
+ 0x14,0x44,
+ 0x15,0x44,
+
+ 0x19,0x50,
+ 0x1a,0xd8,
+
+ 0x32,0x10,
+
+ 0x35,0x00,
+ 0x36,0x80,
+ 0x37,0x00,
+ //-----------Update the registers end---------//
+ 0xfe,0x00,// set page0
+ 0xd2,0x90,
+};
+
+uint8_t gc0308_default_regs_exit[]={
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-kyt-173_v1.0.h b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-kyt-173_v1.0.h
new file mode 100755
index 00000000..e0fe145b
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-kyt-173_v1.0.h
@@ -0,0 +1,446 @@
+#ifndef GC0308_H
+#define GC0308_H
+
+
+// Scene Mode
+uint8_t gc0308_scene_mode_auto[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x20,
+};
+
+uint8_t gc0308_scene_mode_night[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x30,
+};
+
+
+// White Balance
+uint8_t gc0308_wb_auto [] = {
+ 0xfe, 0x00, // set page0
+ 0x5a, 0x56,
+ 0x5b, 0x40,
+ 0x5c, 0x4a,
+ 0x22, 0x57,
+};
+
+uint8_t gc0308_wb_incandescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x48,
+ 0x5b, 0x40,
+ 0x5c, 0x5c,
+};
+
+uint8_t gc0308_wb_fluorescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x40,
+ 0x5b, 0x42,
+ 0x5c, 0x50,
+};
+
+uint8_t gc0308_wb_daylight [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x74,
+ 0x5b, 0x52,
+ 0x5c, 0x40,
+};
+
+uint8_t gc0308_wb_cloudy [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x8c,
+ 0x5b, 0x50,
+ 0x5c, 0x40,
+};
+
+
+// Exposure
+uint8_t gc0308_exposure_neg6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xe0,
+ 0xd3, 0x38,
+};
+
+uint8_t gc0308_exposure_neg3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xf0,
+ 0xd3, 0x40,
+};
+
+uint8_t gc0308_exposure_zero[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x00,
+ 0xd3, 0x48,
+};
+
+uint8_t gc0308_exposure_pos3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x10,
+ 0xd3, 0x50,
+};
+
+uint8_t gc0308_exposure_pos6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x20,
+ 0xd3, 0x58,
+};
+
+
+// Resolution
+uint8_t gc0308_320x240[]= {
+ 0xfe, 0x01,
+ 0x54, 0x22,
+ 0x55, 0x03,
+ 0x56, 0x00,
+ 0x57, 0x00,
+ 0x58, 0x00,
+ 0x59, 0x00,
+ 0xfe, 0x00,
+};
+
+uint8_t gc0308_640x480[] = {
+ 0xfe,0x01,
+ 0x54,0x11,
+ 0x55,0x03,
+ 0x56,0x00,
+ 0x57,0x00,
+ 0x58,0x00,
+ 0x59,0x00,
+ 0xfe,0x00,
+ 0x46,0x00,
+ 0xfe,0x00,
+};
+
+uint8_t gc0308_default_regs_init[] = {
+ 0xfe,0x80,
+
+ 0xfe,0x00, // set page0
+
+ 0xd2,0x10, // close AEC
+ 0x22,0x55, // close AWB
+
+ 0x03,0x01,
+ 0x04,0x2c,
+ 0x5a,0x56,
+ 0x5b,0x40,
+ 0x5c,0x4a,
+
+ 0x22,0x57, // Open AWB
+
+ 0x01,0xfa,
+ 0x02,0x70,
+ 0x0f,0x01,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x64, //anti-flicker step [7:0]
+
+ 0xe4,0x02, //exp level 1 16.67fps
+ 0xe5,0x58,
+ 0xe6,0x03, //exp level 2 12.5fps
+ 0xe7,0x20,
+ 0xe8,0x04, //exp level 3 8.33fps
+ 0xe9,0xb0,
+ 0xea,0x09, //exp level 4 4.00fps
+ 0xeb,0xc4,
+
+ 0xec,0x20,
+
+ 0x05,0x00,
+ 0x06,0x00,
+ 0x07,0x00,
+ 0x08,0x00,
+ 0x09,0x01,
+ 0x0a,0xe8,
+ 0x0b,0x02,
+ 0x0c,0x88,
+ 0x0d,0x02,
+ 0x0e,0x02,
+ 0x10,0x22,
+ 0x11,0xfd,
+ 0x12,0x2a,
+ 0x13,0x00,
+ 0x14,0x10, //0x10
+
+ 0x15,0x0a,
+ 0x16,0x05,
+ 0x17,0x01,
+ 0x18,0x44,
+ 0x19,0x44,
+ 0x1a,0x1e,
+ 0x1b,0x00,
+ 0x1c,0xc1,
+ 0x1d,0x08,
+ 0x1e,0x60,
+ 0x1f,0x16,
+
+
+
+ 0x20,0xff,
+ 0x21,0xf8,
+ 0x22,0x57,
+ 0x24,0xa2,
+ 0x25,0x0f,
+
+ //output sync_mode
+
+
+ 0x26,0x06, //0x03 20101016 zhj//02 cyrille
+
+ 0x2f,0x01,
+ 0x30,0xf7,
+ 0x31,0x50,
+ 0x32,0x00,
+ 0x39,0x04,
+ 0x3a,0x18,
+ 0x3b,0x20,
+ 0x3c,0x00,
+ 0x3d,0x00,
+ 0x3e,0x00,
+ 0x3f,0x00,
+ 0x50,0x10,
+ 0x53,0x82,
+ 0x54,0x80,
+ 0x55,0x80,
+ 0x56,0x82,
+ 0x8b,0x40,
+ 0x8c,0x40,
+ 0x8d,0x40,
+ 0x8e,0x2e,
+ 0x8f,0x2e,
+ 0x90,0x2e,
+ 0x91,0x3c,
+ 0x92,0x50,
+ 0x5d,0x12,
+ 0x5e,0x1a,
+ 0x5f,0x24,
+ 0x60,0x07,
+ 0x61,0x15,
+ 0x62,0x08,
+ 0x64,0x03,
+ 0x66,0xe8,
+ 0x67,0x86,
+ 0x68,0xa2,
+ 0x69,0x18,
+ 0x6a,0x0f,
+ 0x6b,0x00,
+ 0x6c,0x5f,
+ 0x6d,0x8f,
+ 0x6e,0x55,
+ 0x6f,0x38,
+ 0x70,0x15,
+ 0x71,0x33,
+ 0x72,0xdc,
+ 0x73,0x80,
+ 0x74,0x02,
+ 0x75,0x3f,
+ 0x76,0x02,
+ 0x77,0x54,
+ 0x78,0x88,
+ 0x79,0x81,
+ 0x7a,0x81,
+ 0x7b,0x22,
+ 0x7c,0xff,
+ 0x93,0x48,
+ 0x94,0x00,
+ 0x95,0x05,
+ 0x96,0xe8,
+ 0x97,0x40,
+ 0x98,0xf0,
+ 0xb1,0x38,
+ 0xb2,0x38,
+ 0xbd,0x38,
+ 0xbe,0x36,
+ 0xd0,0xc9,
+ 0xd1,0x10,
+
+ 0xd3,0x80,
+ 0xd5,0xf2,
+ 0xd6,0x16,
+ 0xdb,0x92,
+ 0xdc,0xa5,
+ 0xdf,0x23,
+ 0xd9,0x00,
+ 0xda,0x00,
+ 0xe0,0x09,
+
+ 0xed,0x04,
+ 0xee,0xa0,
+ 0xef,0x40,
+ 0x80,0x03,
+ 0x80,0x03,
+
+
+ 0x9F,0x10,
+ 0xA0,0x20,
+ 0xA1,0x38,
+ 0xA2,0x4E,
+ 0xA3,0x63,
+ 0xA4,0x76,
+ 0xA5,0x87,
+ 0xA6,0xA2,
+ 0xA7,0xB8,
+ 0xA8,0xCA,
+ 0xA9,0xD8,
+ 0xAA,0xE3,
+ 0xAB,0xEB,
+ 0xAC,0xF0,
+ 0xAD,0xF8,
+ 0xAE,0xFD,
+ 0xAF,0xFF,
+
+ 0xc0,0x00,
+ 0xc1,0x10,
+ 0xc2,0x1C,
+ 0xc3,0x30,
+ 0xc4,0x43,
+ 0xc5,0x54,
+ 0xc6,0x65,
+ 0xc7,0x75,
+ 0xc8,0x93,
+ 0xc9,0xB0,
+ 0xca,0xCB,
+ 0xcb,0xE6,
+ 0xcc,0xFF,
+ 0xf0,0x02,
+ 0xf1,0x01,
+ 0xf2,0x01,
+ 0xf3,0x30,
+ 0xf9,0x9f,
+ 0xfa,0x78,
+ //----------------------------
+ 0xfe,0x01,// set page1
+ 0x00,0xf5,
+ 0x02,0x1a,
+ 0x0a,0xa0,
+ 0x0b,0x60,
+ 0x0c,0x08,
+ 0x0e,0x4c,
+ 0x0f,0x39,
+ 0x11,0x3f,
+ 0x12,0x72,
+ 0x13,0x13,
+ 0x14,0x42,
+ 0x15,0x43,
+ 0x16,0xc2,
+ 0x17,0xa8,
+ 0x18,0x18,
+ 0x19,0x40,
+ 0x1a,0xd0,
+ 0x1b,0xf5,
+ 0x70,0x40,
+ 0x71,0x58,
+ 0x72,0x30,
+ 0x73,0x48,
+ 0x74,0x20,
+ 0x75,0x60,
+ 0x77,0x20,
+ 0x78,0x32,
+ 0x30,0x03,
+ 0x31,0x40,
+ 0x32,0xe0,
+ 0x33,0xe0,
+ 0x34,0xe0,
+ 0x35,0xb0,
+ 0x36,0xc0,
+ 0x37,0xc0,
+ 0x38,0x04,
+ 0x39,0x09,
+ 0x3a,0x12,
+ 0x3b,0x1C,
+ 0x3c,0x28,
+ 0x3d,0x31,
+ 0x3e,0x44,
+ 0x3f,0x57,
+ 0x40,0x6C,
+ 0x41,0x81,
+ 0x42,0x94,
+ 0x43,0xA7,
+ 0x44,0xB8,
+ 0x45,0xD6,
+ 0x46,0xEE,
+ 0x47,0x0d,
+ 0xfe,0x00,// set page0
+
+ //-----------Update the registers 2010/07/06-----------
+ //Registers of Page0
+ 0xfe,0x00,// set page0
+ 0x10,0x26,
+ 0x11,0x0d, // fd
+
+ 0x1a,0x2a, // 1e
+
+
+ 0x1c,0x49,// c1
+ 0x1d,0x9a,// 08
+
+
+ 0x1e,0x61,// 60
+
+ 0x3a,0x20,
+
+ 0x50,0x14, // 10
+
+
+ 0x53,0x80,
+ 0x56,0x80,
+
+ 0x8b,0x20,//LSC
+ 0x8c,0x20,
+ 0x8d,0x20,
+ 0x8e,0x14,
+ 0x8f,0x10,
+ 0x90,0x14,
+
+ 0x94,0x02,
+ 0x95,0x07,
+ 0x96,0xe0,
+
+ 0xb1,0x40,// YCPT
+ 0xb2,0x40,
+ 0xb3,0x40,
+ 0xb6,0xe0,
+
+ 0xd0,0xcb,// AECT c9
+
+
+ 0xd3,0x48,// 80
+
+ 0xf2,0x02,
+ 0xf7,0x12,
+ 0xf8,0x0a,
+
+ //Registers of Page1
+ 0xfe,0x01,// set page1
+ 0x02,0x20,
+ 0x04,0x10,
+ 0x05,0x08,
+ 0x06,0x20,
+ 0x08,0x0a,
+
+ 0x0e,0x44,
+ 0x0f,0x32,
+ 0x10,0x41,
+ 0x11,0x37,
+ 0x12,0x22,
+ 0x13,0x19,
+ 0x14,0x44,
+ 0x15,0x44,
+
+ 0x19,0x50,
+ 0x1a,0xd8,
+
+ 0x32,0x10,
+
+ 0x35,0x00,
+ 0x36,0x80,
+ 0x37,0x00,
+ //-----------Update the registers end---------//
+ 0xfe,0x00,// set page0
+ 0xd2,0x90,
+};
+
+uint8_t gc0308_default_regs_exit[]={
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-mrs-gt7305_v1.h b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-mrs-gt7305_v1.h
new file mode 100755
index 00000000..1bc51987
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-mrs-gt7305_v1.h
@@ -0,0 +1,452 @@
+#ifndef GC0308_H
+#define GC0308_H
+
+
+// Scene Mode
+uint8_t gc0308_scene_mode_auto[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x20,
+};
+
+uint8_t gc0308_scene_mode_night[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x30,
+};
+
+
+// White Balance
+uint8_t gc0308_wb_auto [] = {
+ 0xfe, 0x00, // set page0
+ 0x5a, 0x56,
+ 0x5b, 0x40,
+ 0x5c, 0x4a,
+ 0x22, 0x57,
+};
+
+uint8_t gc0308_wb_incandescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x48,
+ 0x5b, 0x40,
+ 0x5c, 0x5c,
+};
+
+uint8_t gc0308_wb_fluorescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x40,
+ 0x5b, 0x42,
+ 0x5c, 0x50,
+};
+
+uint8_t gc0308_wb_daylight [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x74,
+ 0x5b, 0x52,
+ 0x5c, 0x40,
+};
+
+uint8_t gc0308_wb_cloudy [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x8c,
+ 0x5b, 0x50,
+ 0x5c, 0x40,
+};
+
+
+// Exposure
+uint8_t gc0308_exposure_neg6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xe0,
+ 0xd3, 0x38,
+};
+
+uint8_t gc0308_exposure_neg3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xf0,
+ 0xd3, 0x40,
+};
+
+uint8_t gc0308_exposure_zero[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x00,
+ 0xd3, 0x44,//48
+};
+
+uint8_t gc0308_exposure_pos3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x10,
+ 0xd3, 0x50,
+};
+
+uint8_t gc0308_exposure_pos6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x20,
+ 0xd3, 0x58,
+};
+
+
+// Resolution
+uint8_t gc0308_320x240[]= {
+ 0xfe, 0x01,
+ 0x54, 0x22,
+ 0x55, 0x03,
+ 0x56, 0x00,
+ 0x57, 0x00,
+ 0x58, 0x00,
+ 0x59, 0x00,
+ 0xfe, 0x00,
+};
+
+uint8_t gc0308_640x480[] = {
+ 0xfe,0x01,
+ 0x54,0x11,
+ 0x55,0x03,
+ 0x56,0x00,
+ 0x57,0x00,
+ 0x58,0x00,
+ 0x59,0x00,
+ 0xfe,0x00,
+ 0x46,0x00,
+ 0xfe,0x00,
+};
+
+uint8_t gc0308_default_regs_init[] = {
+ 0xfe,0x80,
+
+ 0xfe,0x00, // set page0
+
+ 0xd2,0x10, // close AEC
+ 0x22,0x55, // close AWB
+
+ 0x03,0x01,
+ 0x04,0x2c,
+ 0x5a,0x56,
+ 0x5b,0x40,
+ 0x5c,0x4a,
+
+ 0x22,0x57, // Open AWB
+
+ 0x01,0xfa,
+ 0x02,0x70,
+ 0x0f,0x01,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x64, //anti-flicker step [7:0]
+
+ 0xe4,0x02, //exp level 1 16.67fps
+ 0xe5,0x58,
+ 0xe6,0x03, //exp level 2 12.5fps
+ 0xe7,0x20,
+ 0xe8,0x04, //exp level 3 8.33fps
+ 0xe9,0xb0,
+ 0xea,0x09, //exp level 4 4.00fps
+ 0xeb,0xc4,
+
+ 0xec,0x20,
+
+ 0x05,0x00,
+ 0x06,0x00,
+ 0x07,0x00,
+ 0x08,0x00,
+ 0x09,0x01,
+ 0x0a,0xe8,
+ 0x0b,0x02,
+ 0x0c,0x88,
+ 0x0d,0x02,
+ 0x0e,0x02,
+ 0x10,0x22,
+ 0x11,0xfd,
+ 0x12,0x2a,
+ 0x13,0x00,
+ 0x14,0x10, //0x10
+
+ 0x15,0x0a,
+ 0x16,0x05,
+ 0x17,0x01,
+ 0x18,0x44,
+ 0x19,0x44,
+ 0x1a,0x1e,
+ 0x1b,0x00,
+ 0x1c,0xc1,
+ 0x1d,0x08,
+ 0x1e,0x60,
+ 0x1f,0x16,
+
+
+
+ 0x20,0xff,
+ 0x21,0xf8,
+ 0x22,0x57,
+ 0x24,0xa2,
+ 0x25,0x0f,
+
+ //output sync_mode
+
+
+ 0x26,0x02, //0x03 20101016 zhj
+
+ 0x2f,0x01,
+ 0x30,0xf7,
+ 0x31,0x50,
+ 0x32,0x00,
+ 0x39,0x04,
+ 0x3a,0x18,
+ 0x3b,0x20,
+ 0x3c,0x00,
+ 0x3d,0x00,
+ 0x3e,0x00,
+ 0x3f,0x00,
+ 0x50,0x10,
+ 0x53,0x82,
+ 0x54,0x80,
+ 0x55,0x80,
+ 0x56,0x82,
+
+ 0x57,0x7c,//80
+ 0x58,0x7d,//80
+ 0x59,0x80,//80
+
+
+ 0x8b,0x40,
+ 0x8c,0x40,
+ 0x8d,0x40,
+ 0x8e,0x2e,
+ 0x8f,0x2e,
+ 0x90,0x2e,
+ 0x91,0x3c,
+ 0x92,0x50,
+ 0x5d,0x12,
+ 0x5e,0x1a,
+ 0x5f,0x24,
+ 0x60,0x07,
+ 0x61,0x15,
+ 0x62,0x08,
+ 0x64,0x03,
+ 0x66,0xe8,
+ 0x67,0x86,
+ 0x68,0xa2,
+ 0x69,0x18,
+ 0x6a,0x0f,
+ 0x6b,0x00,
+ 0x6c,0x5f,
+ 0x6d,0x8f,
+ 0x6e,0x55,
+ 0x6f,0x38,
+ 0x70,0x15,
+ 0x71,0x33,
+ 0x72,0xdc,
+ 0x73,0x80,
+ 0x74,0x02,
+ 0x75,0x3f,
+ 0x76,0x02,
+ 0x77,0x54,
+ 0x78,0x88,
+ 0x79,0x81,
+ 0x7a,0x81,
+ 0x7b,0x22,
+ 0x7c,0xff,
+ 0x93,0x44, //48 2013-8-16 dustin
+ 0x94,0x00,
+ 0x95,0x06,
+ 0x96,0xe8,
+ 0x97,0x40,
+ 0x98,0xf0,
+ 0xb1,0x38,
+ 0xb2,0x38,
+ 0xbd,0x38,
+ 0xbe,0x36,
+ 0xd0,0xc9,
+ 0xd1,0x10,
+
+ 0xd3,0x80,
+ 0xd5,0xf2,
+ 0xd6,0x16,
+ 0xdb,0x92,
+ 0xdc,0xa5,
+ 0xdf,0x23,
+ 0xd9,0x00,
+ 0xda,0x00,
+ 0xe0,0x09,
+
+ 0xed,0x04,
+ 0xee,0xa0,
+ 0xef,0x40,
+ 0x80,0x03,
+ 0x80,0x03,
+
+
+ 0x9F,0x10,
+ 0xA0,0x20,
+ 0xA1,0x38,
+ 0xA2,0x4E,
+ 0xA3,0x63,
+ 0xA4,0x76,
+ 0xA5,0x87,
+ 0xA6,0xA2,
+ 0xA7,0xB8,
+ 0xA8,0xCA,
+ 0xA9,0xD8,
+ 0xAA,0xE3,
+ 0xAB,0xEB,
+ 0xAC,0xF0,
+ 0xAD,0xF8,
+ 0xAE,0xFD,
+ 0xAF,0xFF,
+
+ 0xc0,0x00,
+ 0xc1,0x10,
+ 0xc2,0x1C,
+ 0xc3,0x30,
+ 0xc4,0x43,
+ 0xc5,0x54,
+ 0xc6,0x65,
+ 0xc7,0x75,
+ 0xc8,0x93,
+ 0xc9,0xB0,
+ 0xca,0xCB,
+ 0xcb,0xE6,
+ 0xcc,0xFF,
+ 0xf0,0x02,
+ 0xf1,0x01,
+ 0xf2,0x01,
+ 0xf3,0x30,
+ 0xf9,0x9f,
+ 0xfa,0x78,
+ //----------------------------
+ 0xfe,0x01,// set page1
+ 0x00,0xf5,
+ 0x02,0x1a,
+ 0x0a,0xa0,
+ 0x0b,0x60,
+ 0x0c,0x08,
+ 0x0e,0x4c,
+ 0x0f,0x39,
+ 0x11,0x3f,
+ 0x12,0x72,
+ 0x13,0x13,
+ 0x14,0x42,
+ 0x15,0x43,
+ 0x16,0xc2,
+ 0x17,0xa8,
+ 0x18,0x18,
+ 0x19,0x40,
+ 0x1a,0xd0,
+ 0x1b,0xf5,
+ 0x70,0x40,
+ 0x71,0x58,
+ 0x72,0x30,
+ 0x73,0x48,
+ 0x74,0x20,
+ 0x75,0x60,
+ 0x77,0x20,
+ 0x78,0x32,
+ 0x30,0x03,
+ 0x31,0x40,
+ 0x32,0xe0,
+ 0x33,0xe0,
+ 0x34,0xe0,
+ 0x35,0xb0,
+ 0x36,0xc0,
+ 0x37,0xc0,
+ 0x38,0x04,
+ 0x39,0x09,
+ 0x3a,0x12,
+ 0x3b,0x1C,
+ 0x3c,0x28,
+ 0x3d,0x31,
+ 0x3e,0x44,
+ 0x3f,0x57,
+ 0x40,0x6C,
+ 0x41,0x81,
+ 0x42,0x94,
+ 0x43,0xA7,
+ 0x44,0xB8,
+ 0x45,0xD6,
+ 0x46,0xEE,
+ 0x47,0x0d,
+ 0xfe,0x00,// set page0
+
+ //-----------Update the registers 2010/07/06-----------
+ //Registers of Page0
+ 0xfe,0x00,// set page0
+ 0x10,0x26,
+ 0x11,0x0d, // fd
+
+ 0x1a,0x2a, // 1e
+
+
+ 0x1c,0x49,// c1
+ 0x1d,0x9a,// 08
+
+
+ 0x1e,0x61,// 60
+
+ 0x3a,0x20,
+
+ 0x50,0x14, // 10
+
+
+ 0x53,0x80,
+ 0x56,0x80,
+
+ 0x8b,0x20,//LSC
+ 0x8c,0x20,
+ 0x8d,0x20,
+ 0x8e,0x14,
+ 0x8f,0x10,
+ 0x90,0x14,
+
+ 0x94,0x02,
+ 0x95,0x07,
+ 0x96,0xe0,
+
+ 0xb1,0x40,// YCPT
+ 0xb2,0x40,
+ 0xb3,0x3b,//40//3xd
+ 0xb6,0xe0,
+
+ 0xd0,0xcb,// AECT c9
+
+
+ 0xd3,0x44,// 80//48
+
+ 0xf2,0x02,
+ 0xf7,0x12,
+ 0xf8,0x0a,
+
+ //Registers of Page1
+ 0xfe,0x01,// set page1
+ 0x02,0x20,
+ 0x04,0x10,
+ 0x05,0x08,
+ 0x06,0x20,
+ 0x08,0x0a,
+
+ 0x0e,0x44,
+ 0x0f,0x32,
+ 0x10,0x41,
+ 0x11,0x37,
+ 0x12,0x22,
+ 0x13,0x19,
+ 0x14,0x44,
+ 0x15,0x44,
+
+ 0x19,0x50,
+ 0x1a,0xd8,
+
+ 0x32,0x10,
+
+ 0x35,0x00,
+ 0x36,0x80,
+ 0x37,0x00,
+ //-----------Update the registers end---------//
+ 0xfe,0x00,// set page0
+ 0xd2,0x90,
+};
+
+uint8_t gc0308_default_regs_exit[]={
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-mrs-gt7305_v1_qx.h b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-mrs-gt7305_v1_qx.h
new file mode 100755
index 00000000..69692438
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-mrs-gt7305_v1_qx.h
@@ -0,0 +1,452 @@
+#ifndef GC0308_H
+#define GC0308_H
+
+
+// Scene Mode
+uint8_t gc0308_scene_mode_auto[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x20,
+};
+
+uint8_t gc0308_scene_mode_night[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x30,
+};
+
+
+// White Balance
+uint8_t gc0308_wb_auto [] = {
+ 0xfe, 0x00, // set page0
+ 0x5a, 0x56,
+ 0x5b, 0x40,
+ 0x5c, 0x4a,
+ 0x22, 0x57,
+};
+
+uint8_t gc0308_wb_incandescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x48,
+ 0x5b, 0x40,
+ 0x5c, 0x5c,
+};
+
+uint8_t gc0308_wb_fluorescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x40,
+ 0x5b, 0x42,
+ 0x5c, 0x50,
+};
+
+uint8_t gc0308_wb_daylight [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x74,
+ 0x5b, 0x52,
+ 0x5c, 0x40,
+};
+
+uint8_t gc0308_wb_cloudy [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x8c,
+ 0x5b, 0x50,
+ 0x5c, 0x40,
+};
+
+
+// Exposure
+uint8_t gc0308_exposure_neg6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xe0,
+ 0xd3, 0x38,
+};
+
+uint8_t gc0308_exposure_neg3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xf0,
+ 0xd3, 0x40,
+};
+
+uint8_t gc0308_exposure_zero[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x00,
+ 0xd3, 0x44,//48
+};
+
+uint8_t gc0308_exposure_pos3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x10,
+ 0xd3, 0x50,
+};
+
+uint8_t gc0308_exposure_pos6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x20,
+ 0xd3, 0x58,
+};
+
+
+// Resolution
+uint8_t gc0308_320x240[]= {
+ 0xfe, 0x01,
+ 0x54, 0x22,
+ 0x55, 0x03,
+ 0x56, 0x00,
+ 0x57, 0x00,
+ 0x58, 0x00,
+ 0x59, 0x00,
+ 0xfe, 0x00,
+};
+
+uint8_t gc0308_640x480[] = {
+ 0xfe,0x01,
+ 0x54,0x11,
+ 0x55,0x03,
+ 0x56,0x00,
+ 0x57,0x00,
+ 0x58,0x00,
+ 0x59,0x00,
+ 0xfe,0x00,
+ 0x46,0x00,
+ 0xfe,0x00,
+};
+
+uint8_t gc0308_default_regs_init[] = {
+ 0xfe,0x80,
+
+ 0xfe,0x00, // set page0
+
+ 0xd2,0x10, // close AEC
+ 0x22,0x55, // close AWB
+
+ 0x03,0x01,
+ 0x04,0x2c,
+ 0x5a,0x56,
+ 0x5b,0x40,
+ 0x5c,0x4a,
+
+ 0x22,0x57, // Open AWB
+
+ 0x01,0xfa,
+ 0x02,0x70,
+ 0x0f,0x01,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x64, //anti-flicker step [7:0]
+
+ 0xe4,0x02, //exp level 1 16.67fps
+ 0xe5,0x58,
+ 0xe6,0x03, //exp level 2 12.5fps
+ 0xe7,0x20,
+ 0xe8,0x05, //exp level 3 8.33fps
+ 0xe9,0x14,
+ 0xea,0x09, //exp level 4 4.00fps
+ 0xeb,0xc4,
+
+ 0xec,0x20,
+
+ 0x05,0x00,
+ 0x06,0x00,
+ 0x07,0x00,
+ 0x08,0x00,
+ 0x09,0x01,
+ 0x0a,0xe8,
+ 0x0b,0x02,
+ 0x0c,0x88,
+ 0x0d,0x02,
+ 0x0e,0x02,
+ 0x10,0x22,
+ 0x11,0xfd,
+ 0x12,0x2a,
+ 0x13,0x00,
+ 0x14,0x10, //0x10
+
+ 0x15,0x0a,
+ 0x16,0x05,
+ 0x17,0x01,
+ 0x18,0x44,
+ 0x19,0x44,
+ 0x1a,0x1e,
+ 0x1b,0x00,
+ 0x1c,0xc1,
+ 0x1d,0x08,
+ 0x1e,0x60,
+ 0x1f,0x16,
+
+
+
+ 0x20,0xff,
+ 0x21,0xf8,
+ 0x22,0x57,
+ 0x24,0xa2,
+ 0x25,0x0f,
+
+ //output sync_mode
+
+
+ 0x26,0x02, //0x03 20101016 zhj
+
+ 0x2f,0x01,
+ 0x30,0xf7,
+ 0x31,0x50,
+ 0x32,0x00,
+ 0x39,0x04,
+ 0x3a,0x18,
+ 0x3b,0x20,
+ 0x3c,0x00,
+ 0x3d,0x00,
+ 0x3e,0x00,
+ 0x3f,0x00,
+ 0x50,0x10,
+ 0x53,0x82,
+ 0x54,0x80,
+ 0x55,0x80,
+ 0x56,0x82,
+
+ 0x57,0x80,//80
+ 0x58,0x7d,//80
+ 0x59,0x80,//80
+
+
+ 0x8b,0x40,
+ 0x8c,0x40,
+ 0x8d,0x40,
+ 0x8e,0x2e,
+ 0x8f,0x2e,
+ 0x90,0x2e,
+ 0x91,0x3c,
+ 0x92,0x50,
+ 0x5d,0x12,
+ 0x5e,0x1a,
+ 0x5f,0x24,
+ 0x60,0x07,
+ 0x61,0x15,
+ 0x62,0x08,
+ 0x64,0x03,
+ 0x66,0xe8,
+ 0x67,0x86,
+ 0x68,0xa2,
+ 0x69,0x18,
+ 0x6a,0x0f,
+ 0x6b,0x00,
+ 0x6c,0x5f,
+ 0x6d,0x8f,
+ 0x6e,0x55,
+ 0x6f,0x38,
+ 0x70,0x15,
+ 0x71,0x33,
+ 0x72,0xdc,
+ 0x73,0x80,
+ 0x74,0x02,
+ 0x75,0x3f,
+ 0x76,0x02,
+ 0x77,0x65,//54
+ 0x78,0x88,
+ 0x79,0x81,
+ 0x7a,0x81,
+ 0x7b,0x22,
+ 0x7c,0xff,
+ 0x93,0x48, //48 2013-8-16 dustin
+ 0x94,0x00,
+ 0x95,0x06,
+ 0x96,0xe8,
+ 0x97,0x40,
+ 0x98,0xf0,
+ 0xb1,0x38,
+ 0xb2,0x38,
+ 0xbd,0x38,
+ 0xbe,0x36,
+ 0xd0,0xc9,
+ 0xd1,0x10,
+
+ 0xd3,0x80,
+ 0xd5,0xf2,
+ 0xd6,0x16,
+ 0xdb,0x92,
+ 0xdc,0xa5,
+ 0xdf,0x23,
+ 0xd9,0x00,
+ 0xda,0x00,
+ 0xe0,0x09,
+
+ 0xed,0x04,
+ 0xee,0xa0,
+ 0xef,0x40,
+ 0x80,0x03,
+ 0x80,0x03,
+
+
+ 0x9F,0x10,
+ 0xA0,0x20,
+ 0xA1,0x38,
+ 0xA2,0x4E,
+ 0xA3,0x63,
+ 0xA4,0x76,
+ 0xA5,0x87,
+ 0xA6,0xA2,
+ 0xA7,0xB8,
+ 0xA8,0xCA,
+ 0xA9,0xD8,
+ 0xAA,0xE3,
+ 0xAB,0xEB,
+ 0xAC,0xF0,
+ 0xAD,0xF8,
+ 0xAE,0xFD,
+ 0xAF,0xFF,
+
+ 0xc0,0x00,
+ 0xc1,0x10,
+ 0xc2,0x1C,
+ 0xc3,0x30,
+ 0xc4,0x43,
+ 0xc5,0x54,
+ 0xc6,0x65,
+ 0xc7,0x75,
+ 0xc8,0x93,
+ 0xc9,0xB0,
+ 0xca,0xCB,
+ 0xcb,0xE6,
+ 0xcc,0xFF,
+ 0xf0,0x02,
+ 0xf1,0x01,
+ 0xf2,0x01,
+ 0xf3,0x30,
+ 0xf9,0x9f,
+ 0xfa,0x78,
+ //----------------------------
+ 0xfe,0x01,// set page1
+ 0x00,0xf5,
+ 0x02,0x1a,
+ 0x0a,0xa0,
+ 0x0b,0x60,
+ 0x0c,0x08,
+ 0x0e,0x4c,
+ 0x0f,0x39,
+ 0x11,0x3f,
+ 0x12,0x72,
+ 0x13,0x13,
+ 0x14,0x42,
+ 0x15,0x43,
+ 0x16,0xc2,
+ 0x17,0xa8,
+ 0x18,0x18,
+ 0x19,0x40,
+ 0x1a,0xd0,
+ 0x1b,0xf5,
+ 0x70,0x40,
+ 0x71,0x58,
+ 0x72,0x30,
+ 0x73,0x48,
+ 0x74,0x20,
+ 0x75,0x60,
+ 0x77,0x20,
+ 0x78,0x32,
+ 0x30,0x03,
+ 0x31,0x40,
+ 0x32,0xe0,
+ 0x33,0xe0,
+ 0x34,0xe0,
+ 0x35,0xb0,
+ 0x36,0xc0,
+ 0x37,0xc0,
+ 0x38,0x04,
+ 0x39,0x09,
+ 0x3a,0x12,
+ 0x3b,0x1C,
+ 0x3c,0x28,
+ 0x3d,0x31,
+ 0x3e,0x44,
+ 0x3f,0x57,
+ 0x40,0x6C,
+ 0x41,0x81,
+ 0x42,0x94,
+ 0x43,0xA7,
+ 0x44,0xB8,
+ 0x45,0xD6,
+ 0x46,0xEE,
+ 0x47,0x0d,
+ 0xfe,0x00,// set page0
+
+ //-----------Update the registers 2010/07/06-----------
+ //Registers of Page0
+ 0xfe,0x00,// set page0
+ 0x10,0x26,
+ 0x11,0x0d, // fd
+
+ 0x1a,0x2a, // 1e
+
+
+ 0x1c,0x49,// c1
+ 0x1d,0x9a,// 08
+
+
+ 0x1e,0x61,// 60
+
+ 0x3a,0x20,
+
+ 0x50,0x14, // 10
+
+
+ 0x53,0x80,
+ 0x56,0x80,
+
+ 0x8b,0x20,//LSC
+ 0x8c,0x20,
+ 0x8d,0x20,
+ 0x8e,0x14,
+ 0x8f,0x10,
+ 0x90,0x14,
+
+ 0x94,0x02,
+ 0x95,0x07,
+ 0x96,0xe0,
+
+ 0xb1,0x42,// YCPT
+ 0xb2,0x42,
+ 0xb3,0x40,//40//3xd
+ 0xb6,0xe0,
+
+ 0xd0,0xcb,// AECT c9
+
+
+ 0xd3,0x48,// 80//44
+
+ 0xf2,0x02,
+ 0xf7,0x12,
+ 0xf8,0x0a,
+
+ //Registers of Page1
+ 0xfe,0x01,// set page1
+ 0x02,0x20,
+ 0x04,0x10,
+ 0x05,0x08,
+ 0x06,0x20,
+ 0x08,0x0a,
+
+ 0x0e,0x44,
+ 0x0f,0x32,
+ 0x10,0x41,
+ 0x11,0x37,
+ 0x12,0x22,
+ 0x13,0x19,
+ 0x14,0x44,
+ 0x15,0x44,
+
+ 0x19,0x50,
+ 0x1a,0xd8,
+
+ 0x32,0x10,
+
+ 0x35,0x00,
+ 0x36,0x80,
+ 0x37,0x00,
+ //-----------Update the registers end---------//
+ 0xfe,0x00,// set page0
+ 0xd2,0x90,
+};
+
+uint8_t gc0308_default_regs_exit[]={
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-syx-gt7206.h b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-syx-gt7206.h
new file mode 100755
index 00000000..443407b3
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-syx-gt7206.h
@@ -0,0 +1,446 @@
+#ifndef GC0308_H
+#define GC0308_H
+
+
+// Scene Mode
+uint8_t gc0308_scene_mode_auto[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x20,
+};
+
+uint8_t gc0308_scene_mode_night[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x30,
+};
+
+
+// White Balance
+uint8_t gc0308_wb_auto [] = {
+ 0xfe, 0x00, // set page0
+ 0x5a, 0x56,
+ 0x5b, 0x40,
+ 0x5c, 0x4a,
+ 0x22, 0x57,
+};
+
+uint8_t gc0308_wb_incandescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x48,
+ 0x5b, 0x40,
+ 0x5c, 0x5c,
+};
+
+uint8_t gc0308_wb_fluorescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x40,
+ 0x5b, 0x42,
+ 0x5c, 0x50,
+};
+
+uint8_t gc0308_wb_daylight [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x74,
+ 0x5b, 0x52,
+ 0x5c, 0x40,
+};
+
+uint8_t gc0308_wb_cloudy [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x8c,
+ 0x5b, 0x50,
+ 0x5c, 0x40,
+};
+
+
+// Exposure
+uint8_t gc0308_exposure_neg6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xe0,
+ 0xd3, 0x38,
+};
+
+uint8_t gc0308_exposure_neg3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xf0,
+ 0xd3, 0x40,
+};
+
+uint8_t gc0308_exposure_zero[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x00,
+ 0xd3, 0x48,
+};
+
+uint8_t gc0308_exposure_pos3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x10,
+ 0xd3, 0x50,
+};
+
+uint8_t gc0308_exposure_pos6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x20,
+ 0xd3, 0x58,
+};
+
+
+// Resolution
+uint8_t gc0308_320x240[]= {
+ 0xfe, 0x01,
+ 0x54, 0x22,
+ 0x55, 0x03,
+ 0x56, 0x00,
+ 0x57, 0x00,
+ 0x58, 0x00,
+ 0x59, 0x00,
+ 0xfe, 0x00,
+};
+
+uint8_t gc0308_640x480[] = {
+ 0xfe,0x01,
+ 0x54,0x11,
+ 0x55,0x03,
+ 0x56,0x00,
+ 0x57,0x00,
+ 0x58,0x00,
+ 0x59,0x00,
+ 0xfe,0x00,
+ 0x46,0x00,
+ 0xfe,0x00,
+};
+
+uint8_t gc0308_default_regs_init[] = {
+ 0xfe,0x80,
+
+ 0xfe,0x00, // set page0
+
+ 0xd2,0x10, // close AEC
+ 0x22,0x55, // close AWB
+
+ 0x03,0x01,
+ 0x04,0x2c,
+ 0x5a,0x56,
+ 0x5b,0x40,
+ 0x5c,0x4a,
+
+ 0x22,0x57, // Open AWB
+
+ 0x01,0xfa,
+ 0x02,0x70,
+ 0x0f,0x01,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x64, //anti-flicker step [7:0]
+
+ 0xe4,0x02, //exp level 1 16.67fps
+ 0xe5,0x58,
+ 0xe6,0x03, //exp level 2 12.5fps
+ 0xe7,0x20,
+ 0xe8,0x04, //exp level 3 8.33fps
+ 0xe9,0xb0,
+ 0xea,0x09, //exp level 4 4.00fps
+ 0xeb,0xc4,
+
+ 0xec,0x20,
+
+ 0x05,0x00,
+ 0x06,0x00,
+ 0x07,0x00,
+ 0x08,0x00,
+ 0x09,0x01,
+ 0x0a,0xe8,
+ 0x0b,0x02,
+ 0x0c,0x88,
+ 0x0d,0x02,
+ 0x0e,0x02,
+ 0x10,0x22,
+ 0x11,0xfd,
+ 0x12,0x2a,
+ 0x13,0x00,
+ 0x14,0x10, //0x10
+
+ 0x15,0x0a,
+ 0x16,0x05,
+ 0x17,0x01,
+ 0x18,0x44,
+ 0x19,0x44,
+ 0x1a,0x1e,
+ 0x1b,0x00,
+ 0x1c,0xc1,
+ 0x1d,0x08,
+ 0x1e,0x60,
+ 0x1f,0x16,
+
+
+
+ 0x20,0xff,
+ 0x21,0xf8,
+ 0x22,0x57,
+ 0x24,0xa2,
+ 0x25,0x0f,
+
+ //output sync_mode
+
+
+ 0x26,0x02, //0x03 20101016 zhj
+
+ 0x2f,0x01,
+ 0x30,0xf7,
+ 0x31,0x50,
+ 0x32,0x00,
+ 0x39,0x04,
+ 0x3a,0x18,
+ 0x3b,0x20,
+ 0x3c,0x00,
+ 0x3d,0x00,
+ 0x3e,0x00,
+ 0x3f,0x00,
+ 0x50,0x10,
+ 0x53,0x82,
+ 0x54,0x80,
+ 0x55,0x80,
+ 0x56,0x82,
+ 0x8b,0x40,
+ 0x8c,0x40,
+ 0x8d,0x40,
+ 0x8e,0x2e,
+ 0x8f,0x2e,
+ 0x90,0x2e,
+ 0x91,0x3c,
+ 0x92,0x50,
+ 0x5d,0x12,
+ 0x5e,0x1a,
+ 0x5f,0x24,
+ 0x60,0x07,
+ 0x61,0x15,
+ 0x62,0x08,
+ 0x64,0x03,
+ 0x66,0xe8,
+ 0x67,0x86,
+ 0x68,0xa2,
+ 0x69,0x18,
+ 0x6a,0x0f,
+ 0x6b,0x00,
+ 0x6c,0x5f,
+ 0x6d,0x8f,
+ 0x6e,0x55,
+ 0x6f,0x38,
+ 0x70,0x15,
+ 0x71,0x33,
+ 0x72,0xdc,
+ 0x73,0x80,
+ 0x74,0x02,
+ 0x75,0x3f,
+ 0x76,0x02,
+ 0x77,0x54,
+ 0x78,0x88,
+ 0x79,0x81,
+ 0x7a,0x81,
+ 0x7b,0x22,
+ 0x7c,0xff,
+ 0x93,0x48,
+ 0x94,0x00,
+ 0x95,0x05,
+ 0x96,0xe8,
+ 0x97,0x40,
+ 0x98,0xf0,
+ 0xb1,0x38,
+ 0xb2,0x38,
+ 0xbd,0x38,
+ 0xbe,0x36,
+ 0xd0,0xc9,
+ 0xd1,0x10,
+
+ 0xd3,0x80,
+ 0xd5,0xf2,
+ 0xd6,0x16,
+ 0xdb,0x92,
+ 0xdc,0xa5,
+ 0xdf,0x23,
+ 0xd9,0x00,
+ 0xda,0x00,
+ 0xe0,0x09,
+
+ 0xed,0x04,
+ 0xee,0xa0,
+ 0xef,0x40,
+ 0x80,0x03,
+ 0x80,0x03,
+
+
+ 0x9F,0x10,
+ 0xA0,0x20,
+ 0xA1,0x38,
+ 0xA2,0x4E,
+ 0xA3,0x63,
+ 0xA4,0x76,
+ 0xA5,0x87,
+ 0xA6,0xA2,
+ 0xA7,0xB8,
+ 0xA8,0xCA,
+ 0xA9,0xD8,
+ 0xAA,0xE3,
+ 0xAB,0xEB,
+ 0xAC,0xF0,
+ 0xAD,0xF8,
+ 0xAE,0xFD,
+ 0xAF,0xFF,
+
+ 0xc0,0x00,
+ 0xc1,0x10,
+ 0xc2,0x1C,
+ 0xc3,0x30,
+ 0xc4,0x43,
+ 0xc5,0x54,
+ 0xc6,0x65,
+ 0xc7,0x75,
+ 0xc8,0x93,
+ 0xc9,0xB0,
+ 0xca,0xCB,
+ 0xcb,0xE6,
+ 0xcc,0xFF,
+ 0xf0,0x02,
+ 0xf1,0x01,
+ 0xf2,0x01,
+ 0xf3,0x30,
+ 0xf9,0x9f,
+ 0xfa,0x78,
+ //----------------------------
+ 0xfe,0x01,// set page1
+ 0x00,0xf5,
+ 0x02,0x1a,
+ 0x0a,0xa0,
+ 0x0b,0x60,
+ 0x0c,0x08,
+ 0x0e,0x4c,
+ 0x0f,0x39,
+ 0x11,0x3f,
+ 0x12,0x72,
+ 0x13,0x13,
+ 0x14,0x42,
+ 0x15,0x43,
+ 0x16,0xc2,
+ 0x17,0xa8,
+ 0x18,0x18,
+ 0x19,0x40,
+ 0x1a,0xd0,
+ 0x1b,0xf5,
+ 0x70,0x40,
+ 0x71,0x58,
+ 0x72,0x30,
+ 0x73,0x48,
+ 0x74,0x20,
+ 0x75,0x60,
+ 0x77,0x20,
+ 0x78,0x32,
+ 0x30,0x03,
+ 0x31,0x40,
+ 0x32,0xe0,
+ 0x33,0xe0,
+ 0x34,0xe0,
+ 0x35,0xb0,
+ 0x36,0xc0,
+ 0x37,0xc0,
+ 0x38,0x04,
+ 0x39,0x09,
+ 0x3a,0x12,
+ 0x3b,0x1C,
+ 0x3c,0x28,
+ 0x3d,0x31,
+ 0x3e,0x44,
+ 0x3f,0x57,
+ 0x40,0x6C,
+ 0x41,0x81,
+ 0x42,0x94,
+ 0x43,0xA7,
+ 0x44,0xB8,
+ 0x45,0xD6,
+ 0x46,0xEE,
+ 0x47,0x0d,
+ 0xfe,0x00,// set page0
+
+ //-----------Update the registers 2010/07/06-----------
+ //Registers of Page0
+ 0xfe,0x00,// set page0
+ 0x10,0x26,
+ 0x11,0x0d, // fd
+
+ 0x1a,0x2a, // 1e
+
+
+ 0x1c,0x49,// c1
+ 0x1d,0x9a,// 08
+
+
+ 0x1e,0x61,// 60
+
+ 0x3a,0x20,
+
+ 0x50,0x14, // 10
+
+
+ 0x53,0x80,
+ 0x56,0x80,
+
+ 0x8b,0x20,//LSC
+ 0x8c,0x20,
+ 0x8d,0x20,
+ 0x8e,0x14,
+ 0x8f,0x10,
+ 0x90,0x14,
+
+ 0x94,0x00, // 02
+ 0x95,0x07,
+ 0x96,0xf8, // e0
+
+ 0xb1,0x40,// YCPT
+ 0xb2,0x40,
+ 0xb3,0x40,
+ 0xb6,0xe0,
+
+ 0xd0,0xcb,// AECT c9
+
+
+ 0xd3,0x48,// 80
+
+ 0xf2,0x02,
+ 0xf7,0x12,
+ 0xf8,0x0a,
+
+ //Registers of Page1
+ 0xfe,0x01,// set page1
+ 0x02,0x20,
+ 0x04,0x10,
+ 0x05,0x08,
+ 0x06,0x20,
+ 0x08,0x0a,
+
+ 0x0e,0x44,
+ 0x0f,0x32,
+ 0x10,0x41,
+ 0x11,0x37,
+ 0x12,0x22,
+ 0x13,0x19,
+ 0x14,0x44,
+ 0x15,0x44,
+
+ 0x19,0x50,
+ 0x1a,0xd8,
+
+ 0x32,0x10,
+
+ 0x35,0x00,
+ 0x36,0x80,
+ 0x37,0x00,
+ //-----------Update the registers end---------//
+ 0xfe,0x00,// set page0
+ 0xd2,0x90,
+};
+
+uint8_t gc0308_default_regs_exit[]={
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-yb474-gt7303.h b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-yb474-gt7303.h
new file mode 100755
index 00000000..2702a6bb
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-yb474-gt7303.h
@@ -0,0 +1,465 @@
+#ifndef GC0308_H
+#define GC0308_H
+
+
+// Scene Mode
+uint8_t gc0308_scene_mode_auto[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x20,
+};
+
+uint8_t gc0308_scene_mode_night[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x30,
+};
+
+
+// White Balance
+uint8_t gc0308_wb_auto [] = {
+ 0xfe, 0x00, // set page0
+ 0x5a, 0x56,
+ 0x5b, 0x40,
+ 0x5c, 0x4a,
+ 0x22, 0x57,
+};
+
+uint8_t gc0308_wb_incandescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x48,
+ 0x5b, 0x40,
+ 0x5c, 0x5c,
+};
+
+uint8_t gc0308_wb_fluorescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x40,
+ 0x5b, 0x42,
+ 0x5c, 0x50,
+};
+
+uint8_t gc0308_wb_daylight [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x74,
+ 0x5b, 0x52,
+ 0x5c, 0x40,
+};
+
+uint8_t gc0308_wb_cloudy [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x8c,
+ 0x5b, 0x50,
+ 0x5c, 0x40,
+};
+
+
+// Exposure
+uint8_t gc0308_exposure_neg6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xe0,
+ 0xd3, 0x38,
+};
+
+uint8_t gc0308_exposure_neg3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xf0,
+ 0xd3, 0x40,
+};
+
+uint8_t gc0308_exposure_zero[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x00,
+ 0xd3, 0x48,
+};
+
+uint8_t gc0308_exposure_pos3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x10,
+ 0xd3, 0x50,
+};
+
+uint8_t gc0308_exposure_pos6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x20,
+ 0xd3, 0x58,
+};
+
+
+// Resolution
+uint8_t gc0308_320x240[]= {
+ 0xfe, 0x01,
+ 0x54, 0x22,
+ 0x55, 0x03,
+ 0x56, 0x00,
+ 0x57, 0x00,
+ 0x58, 0x00,
+ 0x59, 0x00,
+ 0xfe, 0x00,
+};
+
+uint8_t gc0308_640x480[] = {
+ 0xfe,0x01,
+ 0x54,0x11,
+ 0x55,0x03,
+ 0x56,0x00,
+ 0x57,0x00,
+ 0x58,0x00,
+ 0x59,0x00,
+ 0xfe,0x00,
+ 0x46,0x00,
+ 0xfe,0x00,
+};
+
+uint8_t gc0308_default_regs_init[] = {
+ 0xfe,0x80,
+
+ 0xfe,0x00, // set page0
+
+ 0xd2,0x10, // close AEC
+ 0x22,0x55, // close AWB
+
+ 0x03,0x01,
+ 0x04,0x2c,
+ 0x5a,0x56,
+ 0x5b,0x40,
+ 0x5c,0x4a,
+
+ 0x22,0x57, // Open AWB
+
+ 0x01,0xfa,
+ 0x02,0x70,
+ 0x0f,0x01,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x64, //anti-flicker step [7:0]
+
+ 0xe4,0x02, //exp level 1 16.67fps
+ 0xe5,0x58,
+ 0xe6,0x03, //exp level 2 12.5fps
+ 0xe7,0x20,
+ 0xe8,0x04, //exp level 3 8.33fps
+ 0xe9,0xb0,
+ 0xea,0x09, //exp level 4 4.00fps
+ 0xeb,0xc4,
+
+ 0xec,0x20,
+
+ 0x05,0x00,
+ 0x06,0x00,
+ 0x07,0x00,
+ 0x08,0x00,
+ 0x09,0x01,
+ 0x0a,0xe8,
+ 0x0b,0x02,
+ 0x0c,0x88,
+ 0x0d,0x02,
+ 0x0e,0x02,
+ 0x10,0x22,
+ 0x11,0xfd,
+ 0x12,0x2a,
+ 0x13,0x00,
+ 0x14,0x10, //0x10
+
+ 0x15,0x0a,
+ 0x16,0x05,
+ 0x17,0x01,
+ 0x18,0x44,
+ 0x19,0x44,
+ 0x1a,0x1e,
+ 0x1b,0x00,
+ 0x1c,0xc1,
+ 0x1d,0x08,
+ 0x1e,0x60,
+ 0x1f,0x16,
+
+
+
+ 0x20,0xff,
+ 0x21,0xf8,
+ 0x22,0x57,
+ 0x24,0xa2,
+ 0x25,0x0f,
+
+ //output sync_mode
+
+
+ 0x26,0x02, //0x03 20101016 zhj
+
+ 0x2f,0x01,
+ 0x30,0xf7,
+ 0x31,0x50,
+ 0x32,0x00,
+ 0x39,0x04,
+ 0x3a,0x18,
+ 0x3b,0x20,
+ 0x3c,0x00,
+ 0x3d,0x00,
+ 0x3e,0x00,
+ 0x3f,0x00,
+ 0x50,0x10,
+ 0x53,0x82,
+ 0x54,0x80,
+ 0x55,0x80,
+ 0x56,0x82,
+ 0x8b,0x40,
+ 0x8c,0x40,
+ 0x8d,0x40,
+ 0x8e,0x2e,
+ 0x8f,0x2e,
+ 0x90,0x2e,
+ 0x91,0x3c,
+ 0x92,0x50,
+ 0x5d,0x12,
+ 0x5e,0x1a,
+ 0x5f,0x24,
+ 0x60,0x07,
+ 0x61,0x15,
+ 0x62,0x08,
+ 0x64,0x03,
+ 0x66,0xe8,
+ 0x67,0x86,
+ 0x68,0xa2,
+ 0x69,0x18,
+ 0x6a,0x0f,
+ 0x6b,0x00,
+ 0x6c,0x5f,
+ 0x6d,0x8f,
+ 0x6e,0x55,
+ 0x6f,0x38,
+ 0x70,0x15,
+ 0x71,0x33,
+ 0x72,0xdc,
+ 0x73,0x80,
+ 0x74,0x02,
+ 0x75,0x3f,
+ 0x76,0x02,
+ 0x77,0x54,
+ 0x78,0x88,
+ 0x79,0x81,
+ 0x7a,0x81,
+ 0x7b,0x22,
+ 0x7c,0xff,
+ 0x93,0x48,
+ 0x94,0x00,
+ 0x95,0x05,
+ 0x96,0xe8,
+ 0x97,0x40,
+ 0x98,0xf0,
+ 0xb1,0x38,
+ 0xb2,0x38,
+ 0xbd,0x38,
+ 0xbe,0x36,
+ 0xd0,0xc9,
+ 0xd1,0x10,
+
+ 0xd3,0x80,
+ 0xd5,0xf2,
+ 0xd6,0x16,
+ 0xdb,0x92,
+ 0xdc,0xa5,
+ 0xdf,0x23,
+ 0xd9,0x00,
+ 0xda,0x00,
+ 0xe0,0x09,
+
+ 0xed,0x04,
+ 0xee,0xa0,
+ 0xef,0x40,
+ 0x80,0x03,
+ 0x80,0x03,
+
+#if 0 //gamma 3
+ 0x9F,0x10,
+ 0xA0,0x20,
+ 0xA1,0x38,
+ 0xA2,0x4E,
+ 0xA3,0x63,
+ 0xA4,0x76,
+ 0xA5,0x87,
+ 0xA6,0xA2,
+ 0xA7,0xB8,
+ 0xA8,0xCA,
+ 0xA9,0xD8,
+ 0xAA,0xE3,
+ 0xAB,0xEB,
+ 0xAC,0xF0,
+ 0xAD,0xF8,
+ 0xAE,0xFD,
+ 0xAF,0xFF,
+#endif
+ //gamma 2 2013-7-24 dustin
+ 0x9F,0x0E,
+ 0xA0,0x1C,
+ 0xA1,0x34,
+ 0xA2,0x48,
+ 0xA3,0x5A,
+ 0xA4,0x6B,
+ 0xA5,0x7B,
+ 0xA6,0x95,
+ 0xA7,0xAB,
+ 0xA8,0xBF,
+ 0xA9,0xCE,
+ 0xAA,0xD9,
+ 0xAB,0xE4,
+ 0xAC,0xEC,
+ 0xAD,0xF7,
+ 0xAE,0xFD,
+ 0xAF,0xFF,
+
+ 0xc0,0x00,
+ 0xc1,0x10,
+ 0xc2,0x1C,
+ 0xc3,0x30,
+ 0xc4,0x43,
+ 0xc5,0x54,
+ 0xc6,0x65,
+ 0xc7,0x75,
+ 0xc8,0x93,
+ 0xc9,0xB0,
+ 0xca,0xCB,
+ 0xcb,0xE6,
+ 0xcc,0xFF,
+ 0xf0,0x02,
+ 0xf1,0x01,
+ 0xf2,0x01,
+ 0xf3,0x30,
+ 0xf9,0x9f,
+ 0xfa,0x78,
+ //----------------------------
+ 0xfe,0x01,// set page1
+ 0x00,0xf5,
+ 0x02,0x1a,
+ 0x0a,0xa0,
+ 0x0b,0x60,
+ 0x0c,0x08,
+ 0x0e,0x4c,
+ 0x0f,0x39,
+ 0x11,0x3f,
+ 0x12,0x72,
+ 0x13,0x13,
+ 0x14,0x42,
+ 0x15,0x43,
+ 0x16,0xc2,
+ 0x17,0xa8,
+ 0x18,0x18,
+ 0x19,0x40,
+ 0x1a,0xd0,
+ 0x1b,0xf5,
+ 0x70,0x40,
+ 0x71,0x58,
+ 0x72,0x30,
+ 0x73,0x48,
+ 0x74,0x20,
+ 0x75,0x60,
+ 0x77,0x20,
+ 0x78,0x32,
+ 0x30,0x03,
+ 0x31,0x40,
+ 0x32,0xe0,
+ 0x33,0xe0,
+ 0x34,0xe0,
+ 0x35,0xb0,
+ 0x36,0xc0,
+ 0x37,0xc0,
+ 0x38,0x04,
+ 0x39,0x09,
+ 0x3a,0x12,
+ 0x3b,0x1C,
+ 0x3c,0x28,
+ 0x3d,0x31,
+ 0x3e,0x44,
+ 0x3f,0x57,
+ 0x40,0x6C,
+ 0x41,0x81,
+ 0x42,0x94,
+ 0x43,0xA7,
+ 0x44,0xB8,
+ 0x45,0xD6,
+ 0x46,0xEE,
+ 0x47,0x0d,
+ 0xfe,0x00,// set page0
+
+ //-----------Update the registers 2010/07/06-----------
+ //Registers of Page0
+ 0xfe,0x00,// set page0
+ 0x10,0x26,
+ 0x11,0x0d, // fd
+
+ 0x1a,0x2a, // 1e
+
+
+ 0x1c,0x49,// c1
+ 0x1d,0x9a,// 08
+
+
+ 0x1e,0x61,// 60
+
+ 0x3a,0x20,
+
+ 0x50,0x14, // 10
+
+
+ 0x53,0x80,
+ 0x56,0x80,
+
+ 0x8b,0x20,//LSC
+ 0x8c,0x20,
+ 0x8d,0x20,
+ 0x8e,0x14,
+ 0x8f,0x10,
+ 0x90,0x14,
+
+ 0x94,0x02,
+ 0x95,0x07,
+ 0x96,0xe0,
+
+ 0xb1,0x40,// YCPT
+ 0xb2,0x40,
+ 0xb3,0x45,//40 2013-7-24 dustin
+ 0xb6,0xe0,
+
+ 0xd0,0xcb,// AECT c9
+
+
+ 0xd3,0x48,// 80
+
+ 0xf2,0x02,
+ 0xf7,0x12,
+ 0xf8,0x0a,
+
+ //Registers of Page1
+ 0xfe,0x01,// set page1
+ 0x02,0x20,
+ 0x04,0x10,
+ 0x05,0x08,
+ 0x06,0x20,
+ 0x08,0x0a,
+
+ 0x0e,0x44,
+ 0x0f,0x32,
+ 0x10,0x41,
+ 0x11,0x37,
+ 0x12,0x22,
+ 0x13,0x19,
+ 0x14,0x44,
+ 0x15,0x44,
+
+ 0x19,0x50,
+ 0x1a,0xd8,
+
+ 0x32,0x10,
+
+ 0x35,0x00,
+ 0x36,0x80,
+ 0x37,0x00,
+ //-----------Update the registers end---------//
+ 0xfe,0x00,// set page0
+ 0xd2,0x90,
+};
+
+uint8_t gc0308_default_regs_exit[]={
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-zh_tvbox.h b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-zh_tvbox.h
new file mode 100755
index 00000000..e26931ad
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-zh_tvbox.h
@@ -0,0 +1,447 @@
+#ifndef GC0308_H
+#define GC0308_H
+
+
+// Scene Mode
+uint8_t gc0308_scene_mode_auto[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x20,
+};
+
+uint8_t gc0308_scene_mode_night[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x30,
+};
+
+
+// White Balance
+uint8_t gc0308_wb_auto [] = {
+ 0xfe, 0x00, // set page0
+ 0x5a, 0x56,
+ 0x5b, 0x40,
+ 0x5c, 0x4a,
+ 0x22, 0x57,
+};
+
+uint8_t gc0308_wb_incandescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x48,
+ 0x5b, 0x40,
+ 0x5c, 0x5c,
+};
+
+uint8_t gc0308_wb_fluorescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x40,
+ 0x5b, 0x42,
+ 0x5c, 0x50,
+};
+
+uint8_t gc0308_wb_daylight [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x74,
+ 0x5b, 0x52,
+ 0x5c, 0x40,
+};
+
+uint8_t gc0308_wb_cloudy [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x8c,
+ 0x5b, 0x50,
+ 0x5c, 0x40,
+};
+
+
+// Exposure
+uint8_t gc0308_exposure_neg6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xe0,
+ 0xd3, 0x38,
+};
+
+uint8_t gc0308_exposure_neg3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xf0,
+ 0xd3, 0x40,
+};
+
+uint8_t gc0308_exposure_zero[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x00,
+ 0xd3, 0x48,
+};
+
+uint8_t gc0308_exposure_pos3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x10,
+ 0xd3, 0x50,
+};
+
+uint8_t gc0308_exposure_pos6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x20,
+ 0xd3, 0x58,
+};
+
+
+// Resolution
+uint8_t gc0308_320x240[]= {
+ 0xfe, 0x01,
+ 0x54, 0x22,
+ 0x55, 0x03,
+ 0x56, 0x00,
+ 0x57, 0x00,
+ 0x58, 0x00,
+ 0x59, 0x00,
+ 0xfe, 0x00,
+};
+
+uint8_t gc0308_640x480[] = {
+ 0xfe,0x01,
+ 0x54,0x11,
+ 0x55,0x03,
+ 0x56,0x00,
+ 0x57,0x00,
+ 0x58,0x00,
+ 0x59,0x00,
+ 0xfe,0x00,
+ 0x46,0x00,
+ 0xfe,0x00,
+};
+
+uint8_t gc0308_default_regs_init[] = {
+ 0xfe,0x80,
+
+ 0xfe,0x00, // set page0
+
+ 0xd2,0x10, // close AEC
+ 0x22,0x55, // close AWB
+
+ 0x03,0x01,
+ 0x04,0x2c,
+ 0x5a,0x56,
+ 0x5b,0x40,
+ 0x5c,0x4a,
+
+ 0x22,0x57, // Open AWB
+
+ 0x01,0xfa,
+ 0x02,0x70,
+ 0x0f,0x01,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x64, //anti-flicker step [7:0]
+
+ 0xe4,0x02, //exp level 1 16.67fps
+ 0xe5,0x58,
+ 0xe6,0x03, //exp level 2 12.5fps
+ 0xe7,0xe8,
+ 0xe8,0x05, //exp level 3 8.33fps
+ 0xe9,0x78,
+ 0xea,0x09, //exp level 4 4.00fps
+ 0xeb,0xc4,
+
+ 0xec,0x20,
+
+ 0x05,0x00,
+ 0x06,0x00,
+ 0x07,0x00,
+ 0x08,0x00,
+ 0x09,0x01,
+ 0x0a,0xe8,
+ 0x0b,0x02,
+ 0x0c,0x88,
+ 0x0d,0x02,
+ 0x0e,0x02,
+ 0x10,0x22,
+ 0x11,0xfd,
+ 0x12,0x2a,
+ 0x13,0x00,
+ 0x14,0x10, //0x10
+
+ 0x15,0x0a,
+ 0x16,0x05,
+ 0x17,0x01,
+ 0x18,0x44,
+ 0x19,0x44,
+ 0x1a,0x1e,
+ 0x1b,0x00,
+ 0x1c,0xc1,
+ 0x1d,0x08,
+ 0x1e,0x60,
+ 0x1f,0x16,
+
+
+
+ 0x20,0xff,
+ 0x21,0xf8,
+ 0x22,0x57,
+ 0x24,0xa2,
+ 0x25,0x0f,
+
+ //output sync_mode
+
+
+ 0x26,0x02, //0x03 20101016 zhj
+
+ 0x2f,0x01,
+ 0x30,0xf7,
+ 0x31,0x50,
+ 0x32,0x00,
+ 0x39,0x04,
+ 0x3a,0x18,
+ 0x3b,0x20,
+ 0x3c,0x00,
+ 0x3d,0x00,
+ 0x3e,0x00,
+ 0x3f,0x00,
+ 0x50,0x10,
+ 0x53,0x82,
+ 0x54,0x80,
+ 0x55,0x80,
+ 0x56,0x82,
+ 0x8b,0x40,
+ 0x8c,0x40,
+ 0x8d,0x40,
+ 0x8e,0x2e,
+ 0x8f,0x2e,
+ 0x90,0x2e,
+ 0x91,0x3c,
+ 0x92,0x50,
+ 0x5d,0x12,
+ 0x5e,0x1a,
+ 0x5f,0x24,
+ 0x60,0x07,
+ 0x61,0x15,
+ 0x62,0x15,
+ 0x64,0x03,
+ 0x66,0xe8,
+ 0x67,0x86,
+ 0x68,0xa2,
+ 0x69,0x18,
+ 0x6a,0x0f,
+ 0x6b,0x00,
+ 0x6c,0x5f,
+ 0x6d,0x8f,
+ 0x6e,0x55,
+ 0x6f,0x38,
+ 0x70,0x15,
+ 0x71,0x33,
+ 0x72,0xdc,
+ 0x73,0x80,
+ 0x74,0x02,
+ 0x75,0x3f,
+ 0x76,0x02,
+ 0x77,0x34,
+ 0x78,0x88,
+ 0x79,0x81,
+ 0x7a,0x81,
+ 0x7b,0x22,
+ 0x7c,0xff,
+ 0x93,0x48,
+ 0x94,0x00,
+ 0x95,0x05,
+ 0x96,0xe8,
+ 0x97,0x40,
+ 0x98,0xf0,
+ 0xb1,0x38,
+ 0xb2,0x38,
+ 0xbd,0x08,//38
+ 0xbe,0x36,
+ 0xd0,0xc9,
+ 0xd1,0x10,
+
+ 0xd3,0x80,
+ 0xd5,0xf2,
+ 0xd6,0x16,
+ 0xdb,0x92,
+ 0xdc,0xa5,
+ 0xdf,0x23,
+ 0xd9,0x00,
+ 0xda,0x00,
+ 0xe0,0x09,
+
+ 0xed,0x04,
+ 0xee,0xa0,
+ 0xef,0x40,
+ 0x80,0x03,
+ 0x80,0x03,
+
+
+ 0x9F,0x14,
+ 0xA0,0x24,
+ 0xA1,0x3e,
+ 0xA2,0x50,
+ 0xA3,0x63,
+ 0xA4,0x76,
+ 0xA5,0x87,
+ 0xA6,0xA2,
+ 0xA7,0xB8,
+ 0xA8,0xCA,
+ 0xA9,0xD8,
+ 0xAA,0xE3,
+ 0xAB,0xEB,
+ 0xAC,0xF0,
+ 0xAD,0xF8,
+ 0xAE,0xFD,
+ 0xAF,0xFF,
+
+ 0xc0,0x00,
+ 0xc1,0x10,
+ 0xc2,0x1C,
+ 0xc3,0x30,
+ 0xc4,0x43,
+ 0xc5,0x54,
+ 0xc6,0x65,
+ 0xc7,0x75,
+ 0xc8,0x93,
+ 0xc9,0xB0,
+ 0xca,0xCB,
+ 0xcb,0xE6,
+ 0xcc,0xFF,
+ 0xf0,0x02,
+ 0xf1,0x01,
+ 0xf2,0x01,
+ 0xf3,0x30,
+ 0xf9,0x9f,
+ 0xfa,0x78,
+ //----------------------------
+ 0xfe,0x01,// set page1
+ 0x00,0xf5,
+ 0x02,0x1a,
+ 0x0a,0xa0,
+ 0x0b,0x60,
+ 0x0c,0x08,
+ 0x0e,0x4c,
+ 0x0f,0x39,
+ 0x11,0x3f,
+ 0x12,0x72,
+ 0x13,0x13,
+ 0x14,0x42,
+ 0x15,0x43,
+ 0x16,0xc2,
+ 0x17,0xa8,
+ 0x18,0x18,
+ 0x19,0x40,
+ 0x1a,0xd0,
+ 0x1b,0xf5,
+ 0x70,0x40,
+ 0x71,0x58,
+ 0x72,0x30,
+ 0x73,0x48,
+ 0x74,0x20,
+ 0x75,0x60,
+ 0x77,0x20,
+ 0x78,0x32,
+ 0x30,0x03,
+ 0x31,0x40,
+ 0x32,0xe0,
+ 0x33,0xe0,
+ 0x34,0xe0,
+ 0x35,0xb0,
+ 0x36,0xc0,
+ 0x37,0xc0,
+ 0x38,0x04,
+ 0x39,0x09,
+ 0x3a,0x12,
+ 0x3b,0x1C,
+ 0x3c,0x28,
+ 0x3d,0x31,
+ 0x3e,0x44,
+ 0x3f,0x57,
+ 0x40,0x6C,
+ 0x41,0x81,
+ 0x42,0x94,
+ 0x43,0xA7,
+ 0x44,0xB8,
+ 0x45,0xD6,
+ 0x46,0xEE,
+ 0x47,0x0d,
+ 0xfe,0x00,// set page0
+
+ //-----------Update the registers 2010/07/06-----------
+ //Registers of Page0
+ 0xfe,0x00,// set page0
+ 0x10,0x26,
+ 0x11,0x0d, // fd
+
+ 0x1a,0x2a, // 1e
+
+
+ 0x1c,0x49,// c1
+ 0x1d,0x9a,// 08
+
+
+ 0x1e,0x61,// 60
+
+ 0x3a,0x20,
+
+ 0x50,0x14, // 10
+
+
+ 0x53,0x80,
+ 0x56,0x80,
+
+ 0x8b,0x20,//LSC
+ 0x8c,0x20,
+ 0x8d,0x20,
+ 0x8e,0x14,
+ 0x8f,0x10,
+ 0x90,0x14,
+
+ 0x94,0x02,
+ 0x95,0x07,
+ 0x96,0xe0,
+
+ 0xb1,0x36,// YCPT
+ 0xb2,0x36,
+ 0xb3,0x3c,
+ 0xb5,0xf0,
+ 0xb6,0xe0,
+
+ 0xd0,0xcb,// AECT c9
+
+
+ 0xd3,0x42,// 80
+
+ 0xf2,0x02,
+ 0xf7,0x12,
+ 0xf8,0x0a,
+
+ //Registers of Page1
+ 0xfe,0x01,// set page1
+ 0x02,0x20,
+ 0x04,0x10,
+ 0x05,0x08,
+ 0x06,0x20,
+ 0x08,0x0a,
+
+ 0x0e,0x44,
+ 0x0f,0x32,
+ 0x10,0x41,
+ 0x11,0x37,
+ 0x12,0x22,
+ 0x13,0x19,
+ 0x14,0x44,
+ 0x15,0x44,
+
+ 0x19,0x50,
+ 0x1a,0xd8,
+
+ 0x32,0x10,
+
+ 0x35,0x00,
+ 0x36,0x80,
+ 0x37,0x00,
+ //-----------Update the registers end---------//
+ 0xfe,0x00,// set page0
+ 0xd2,0x90,
+};
+
+uint8_t gc0308_default_regs_exit[]={
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-zyhb-twd8802.h b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-zyhb-twd8802.h
new file mode 100755
index 00000000..817f09cf
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308-zyhb-twd8802.h
@@ -0,0 +1,447 @@
+#ifndef GC0308_H
+#define GC0308_H
+
+
+// Scene Mode
+uint8_t gc0308_scene_mode_auto[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x20,
+};
+
+uint8_t gc0308_scene_mode_night[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x30,
+};
+
+
+// White Balance
+uint8_t gc0308_wb_auto [] = {
+ 0xfe, 0x00, // set page0
+ 0x5a, 0x56,
+ 0x5b, 0x40,
+ 0x5c, 0x4a,
+ 0x22, 0x57,
+};
+
+uint8_t gc0308_wb_incandescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x48,
+ 0x5b, 0x40,
+ 0x5c, 0x5c,
+};
+
+uint8_t gc0308_wb_fluorescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x40,
+ 0x5b, 0x42,
+ 0x5c, 0x50,
+};
+
+uint8_t gc0308_wb_daylight [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x74,
+ 0x5b, 0x52,
+ 0x5c, 0x40,
+};
+
+uint8_t gc0308_wb_cloudy [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x8c,
+ 0x5b, 0x50,
+ 0x5c, 0x40,
+};
+
+
+// Exposure
+uint8_t gc0308_exposure_neg6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xe0,
+ 0xd3, 0x38,
+};
+
+uint8_t gc0308_exposure_neg3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xf0,
+ 0xd3, 0x40,
+};
+
+uint8_t gc0308_exposure_zero[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x00,
+ 0xd3, 0x48,
+};
+
+uint8_t gc0308_exposure_pos3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x10,
+ 0xd3, 0x50,
+};
+
+uint8_t gc0308_exposure_pos6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x20,
+ 0xd3, 0x58,
+};
+
+
+// Resolution
+uint8_t gc0308_320x240[]= {
+ 0xfe, 0x01,
+ 0x54, 0x22,
+ 0x55, 0x03,
+ 0x56, 0x00,
+ 0x57, 0x00,
+ 0x58, 0x00,
+ 0x59, 0x00,
+ 0xfe, 0x00,
+};
+
+uint8_t gc0308_640x480[] = {
+ 0xfe,0x01,
+ 0x54,0x11,
+ 0x55,0x03,
+ 0x56,0x00,
+ 0x57,0x00,
+ 0x58,0x00,
+ 0x59,0x00,
+ 0xfe,0x00,
+ 0x46,0x00,
+ 0xfe,0x00,
+};
+
+uint8_t gc0308_default_regs_init[] = {
+ 0xfe,0x80,
+
+ 0xfe,0x00, // set page0
+
+ 0xd2,0x10, // close AEC
+ 0x22,0x55, // close AWB
+
+ 0x03,0x01,
+ 0x04,0x2c,
+ 0x5a,0x56,
+ 0x5b,0x40,
+ 0x5c,0x4a,
+
+ 0x22,0x57, // Open AWB
+
+ 0x01,0xfa,
+ 0x02,0x70,
+ 0x0f,0x01,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x64, //anti-flicker step [7:0]
+
+ 0xe4,0x02, //exp level 1 16.67fps
+ 0xe5,0x58,
+ 0xe6,0x03, //exp level 2 12.5fps
+ 0xe7,0x20,
+ 0xe8,0x04, //exp level 3 8.33fps
+ 0xe9,0xb0,
+ 0xea,0x09, //exp level 4 4.00fps
+ 0xeb,0xc4,
+
+ 0xec,0x20,
+
+ 0x05,0x00,
+ 0x06,0x00,
+ 0x07,0x00,
+ 0x08,0x00,
+ 0x09,0x01,
+ 0x0a,0xe8,
+ 0x0b,0x02,
+ 0x0c,0x88,
+ 0x0d,0x02,
+ 0x0e,0x02,
+ 0x10,0x22,
+ 0x11,0xfd,
+ 0x12,0x2a,
+ 0x13,0x00,
+ 0x14,0x10, //0x10
+
+ 0x15,0x0a,
+ 0x16,0x05,
+ 0x17,0x01,
+ 0x18,0x44,
+ 0x19,0x44,
+ 0x1a,0x1e,
+ 0x1b,0x00,
+ 0x1c,0xc1,
+ 0x1d,0x08,
+ 0x1e,0x60,
+ 0x1f,0x16,
+
+
+
+ 0x20,0xff,
+ 0x21,0xf8,
+ 0x22,0x57,
+ 0x24,0xa2,
+ 0x25,0x0f,
+
+ //output sync_mode
+
+
+ 0x26,0x02, //0x03 20101016 zhj
+
+ 0x2f,0x01,
+ 0x30,0xf7,
+ 0x31,0x50,
+ 0x32,0x00,
+ 0x39,0x04,
+ 0x3a,0x18,
+ 0x3b,0x20,
+ 0x3c,0x00,
+ 0x3d,0x00,
+ 0x3e,0x00,
+ 0x3f,0x00,
+ 0x50,0x10,
+ 0x53,0x82,
+ 0x54,0x80,
+ 0x55,0x80,
+ 0x56,0x82,
+ 0x8b,0x40,
+ 0x8c,0x40,
+ 0x8d,0x40,
+ 0x8e,0x2e,
+ 0x8f,0x2e,
+ 0x90,0x2e,
+ 0x91,0x3c,
+ 0x92,0x50,
+ 0x5d,0x12,
+ 0x5e,0x1a,
+ 0x5f,0x24,
+ 0x60,0x07,
+ 0x61,0x15,
+ 0x62,0x15,
+ 0x64,0x02,
+ 0x66,0xe8,
+ 0x67,0x86,
+ 0x68,0xa2,
+ 0x69,0x18,
+ 0x6a,0x0f,
+ 0x6b,0x00,
+ 0x6c,0x5f,
+ 0x6d,0x8f,
+ 0x6e,0x55,
+ 0x6f,0x38,
+ 0x70,0x15,
+ 0x71,0x33,
+ 0x72,0xdc,
+ 0x73,0x80,
+ 0x74,0x02,
+ 0x75,0x3f,
+ 0x76,0x02,
+ 0x77,0x65,//54
+ 0x78,0x88,
+ 0x79,0x81,
+ 0x7a,0x81,
+ 0x7b,0x22,
+ 0x7c,0xff,
+ 0x93,0x48,
+ 0x94,0x00,
+ 0x95,0x05,
+ 0x96,0xe8,
+ 0x97,0x40,
+ 0x98,0xf0,
+ 0xb1,0x38,
+ 0xb2,0x38,
+ 0xbd,0x3f,//38
+ 0xbe,0x36,
+ 0xd0,0xc9,
+ 0xd1,0x10,
+
+ 0xd3,0x80,
+ 0xd5,0xf2,
+ 0xd6,0x16,
+ 0xdb,0x92,
+ 0xdc,0xa5,
+ 0xdf,0x23,
+ 0xd9,0x00,
+ 0xda,0x00,
+ 0xe0,0x09,
+
+ 0xed,0x04,
+ 0xee,0xa0,
+ 0xef,0x40,
+ 0x80,0x03,
+ 0x80,0x03,
+
+
+ 0x9F, 0x10, ///case 3:
+ 0xA0, 0x20,
+ 0xA1, 0x38,
+ 0xA2, 0x4E,
+ 0xA3, 0x63,
+ 0xA4, 0x76,
+ 0xA5, 0x87,
+ 0xA6, 0xA2,
+ 0xA7, 0xB8,
+ 0xA8, 0xCA,
+ 0xA9, 0xD8,
+ 0xAA, 0xE3,
+ 0xAB, 0xEB,
+ 0xAC, 0xF0,
+ 0xAD, 0xF8,
+ 0xAE, 0xFD,
+ 0xAF, 0xFF,
+
+ 0xc0,0x00,
+ 0xc1,0x10,
+ 0xc2,0x1C,
+ 0xc3,0x30,
+ 0xc4,0x43,
+ 0xc5,0x54,
+ 0xc6,0x65,
+ 0xc7,0x75,
+ 0xc8,0x93,
+ 0xc9,0xB0,
+ 0xca,0xCB,
+ 0xcb,0xE6,
+ 0xcc,0xFF,
+ 0xf0,0x02,
+ 0xf1,0x01,
+ 0xf2,0x01,
+ 0xf3,0x30,
+ 0xf9,0x9f,
+ 0xfa,0x78,
+ //----------------------------
+ 0xfe,0x01,// set page1
+ 0x00,0xf5,
+ 0x02,0x1a,
+ 0x0a,0xa0,
+ 0x0b,0x60,
+ 0x0c,0x08,
+ 0x0e,0x4c,
+ 0x0f,0x39,
+ 0x11,0x3f,
+ 0x12,0x72,
+ 0x13,0x13,
+ 0x14,0x42,
+ 0x15,0x43,
+ 0x16,0xc2,
+ 0x17,0xa8,
+ 0x18,0x18,
+ 0x19,0x40,
+ 0x1a,0xd0,
+ 0x1b,0xf5,
+ 0x70,0x40,
+ 0x71,0x58,
+ 0x72,0x30,
+ 0x73,0x48,
+ 0x74,0x20,
+ 0x75,0x60,
+ 0x77,0x20,
+ 0x78,0x32,
+ 0x30,0x03,
+ 0x31,0x40,
+ 0x32,0xe0,
+ 0x33,0xe0,
+ 0x34,0xe0,
+ 0x35,0xb0,
+ 0x36,0xc0,
+ 0x37,0xc0,
+ 0x38,0x04,
+ 0x39,0x09,
+ 0x3a,0x12,
+ 0x3b,0x1C,
+ 0x3c,0x28,
+ 0x3d,0x31,
+ 0x3e,0x44,
+ 0x3f,0x57,
+ 0x40,0x6C,
+ 0x41,0x81,
+ 0x42,0x94,
+ 0x43,0xA7,
+ 0x44,0xB8,
+ 0x45,0xD6,
+ 0x46,0xEE,
+ 0x47,0x0d,
+ 0xfe,0x00,// set page0
+
+ //-----------Update the registers 2010/07/06-----------
+ //Registers of Page0
+ 0xfe,0x00,// set page0
+ 0x10,0x26,
+ 0x11,0x0d, // fd
+
+ 0x1a,0x2a, // 1e
+
+
+ 0x1c,0x49,// c1
+ 0x1d,0x9a,// 08
+
+
+ 0x1e,0x61,// 60
+
+ 0x3a,0x20,
+
+ 0x50,0x14, // 10
+
+
+ 0x53,0x80,
+ 0x56,0x80,
+
+ 0x8b,0x20,//LSC
+ 0x8c,0x20,
+ 0x8d,0x20,
+ 0x8e,0x14,
+ 0x8f,0x10,
+ 0x90,0x14,
+
+ 0x94,0x02,
+ 0x95,0x07,
+ 0x96,0xe0,
+
+ 0xb1,0x38,// YCPT
+ 0xb2,0x38,
+ 0xb3,0x44,
+ 0xb5,0x04,
+ 0xb6,0xe0,
+
+ 0xd0,0xcb,// AECT c9
+
+
+ 0xd3,0x4a,// 80
+
+ 0xf2,0x02,
+ 0xf7,0x12,
+ 0xf8,0x0a,
+
+ //Registers of Page1
+ 0xfe,0x01,// set page1
+ 0x02,0x20,
+ 0x04,0x10,
+ 0x05,0x08,
+ 0x06,0x20,
+ 0x08,0x0a,
+
+ 0x0e,0x44,
+ 0x0f,0x32,
+ 0x10,0x41,
+ 0x11,0x37,
+ 0x12,0x22,
+ 0x13,0x19,
+ 0x14,0x44,
+ 0x15,0x44,
+
+ 0x19,0x50,
+ 0x1a,0xd8,
+
+ 0x32,0x10,
+
+ 0x35,0x00,
+ 0x36,0x80,
+ 0x37,0x00,
+ //-----------Update the registers end---------//
+ 0xfe,0x00,// set page0
+ 0xd2,0x90,
+};
+
+uint8_t gc0308_default_regs_exit[]={
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308.c b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308.c
new file mode 100755
index 00000000..29be14b2
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308.c
@@ -0,0 +1,378 @@
+
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "gc0308.h"
+
+#define sensor_write_array(sd, arr, sz) \
+ cmos_init_8bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write(sd->i2c_addr, reg, val)
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint8_t *regs;
+ size_t size;
+};
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+#define FILP_REG_INIT_VALUE 0x10
+#define DELAY_INTERVAL 50
+#define RETRY_TIMES 10
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+// CMOS_WIN_SIZE("QVGA", 320, 240, gc0308_320x240),
+ CMOS_WIN_SIZE("VGA", 640, 480, gc0308_640x480),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = gc0308_wb_auto;
+ size = ARRAY_SIZE(gc0308_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = gc0308_wb_incandescent;
+ size = ARRAY_SIZE(gc0308_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = gc0308_wb_daylight;
+ size = ARRAY_SIZE(gc0308_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = gc0308_wb_cloudy;
+ size = ARRAY_SIZE(gc0308_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = gc0308_wb_fluorescent;
+ size = ARRAY_SIZE(gc0308_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_scenemode(struct cmos_subdev *sd, enum v4l2_scene_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case SCENE_MODE_AUTO:
+ regs = gc0308_scene_mode_auto;
+ size = ARRAY_SIZE(gc0308_scene_mode_auto);
+ break;
+ case SCENE_MODE_NIGHTSHOT:
+ regs = gc0308_scene_mode_night;
+ size = ARRAY_SIZE(gc0308_scene_mode_night);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_exposure(struct cmos_subdev *sd, int value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case -2:
+ regs = gc0308_exposure_neg6;
+ size = ARRAY_SIZE(gc0308_exposure_neg6);
+ break;
+ case -1:
+ regs = gc0308_exposure_neg3;
+ size = ARRAY_SIZE(gc0308_exposure_neg3);
+ break;
+ case 0:
+ regs = gc0308_exposure_zero;
+ size = ARRAY_SIZE(gc0308_exposure_zero);
+ break;
+ case 1:
+ regs = gc0308_exposure_pos3;
+ size = ARRAY_SIZE(gc0308_exposure_pos3);
+ break;
+ case 2:
+ regs = gc0308_exposure_pos6;
+ size = ARRAY_SIZE(gc0308_exposure_pos6);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_antibanding(struct cmos_subdev *sd, int value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case 0:
+ regs = gc0308_antibanding_auto;
+ size = ARRAY_SIZE(gc0308_antibanding_auto);
+ break;
+ case 1:
+ regs = gc0308_antibanding_50hz;
+ size = ARRAY_SIZE(gc0308_antibanding_50hz);
+ break;
+ case 2:
+ regs = gc0308_antibanding_60hz;
+ size = ARRAY_SIZE(gc0308_antibanding_60hz);
+ break;
+ case 3:
+ regs = gc0308_antibanding_off;
+ size = ARRAY_SIZE(gc0308_antibanding_off);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+
+ return 0;
+}
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data,tmp=0;
+ uint8_t retry_times=0;
+
+ sensor_write(sd, 0xfe, 0x00); // set page0
+
+ data = sensor_read(sd, 0x14);
+
+ switch (value) {
+ case 0:
+ data &= ~0x01;
+ break;
+ case 1:
+ data |= 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tmp=data | FILP_REG_INIT_VALUE;
+ for(retry_times=0;retry_times<RETRY_TIMES;retry_times++){
+ sensor_write(sd, 0x14, tmp);
+ msleep(50+retry_times*DELAY_INTERVAL);
+ if(sensor_read(sd, 0x14)==tmp)
+ break;
+ }
+
+ return 0;
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data,tmp=0;
+ uint8_t retry_times=0;
+
+ sensor_write(sd, 0xfe, 0x00); // set page0
+
+ data = sensor_read(sd, 0x14);
+
+ switch (value) {
+ case 0:
+ data &= ~0x02;
+ break;
+ case 1:
+ data |= 0x02;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tmp=data | FILP_REG_INIT_VALUE;
+ for(retry_times=0;retry_times<RETRY_TIMES;retry_times++){
+ sensor_write(sd, 0x14, tmp);
+ msleep(50+retry_times*DELAY_INTERVAL);
+ if(sensor_read(sd, 0x14)==tmp)
+ break;
+ }
+
+ return 0;
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ case V4L2_CID_EXPOSURE:
+ return v4l2_ctrl_query_fill(qc, -2, 2, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return sensor_s_scenemode(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return sensor_s_exposure(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ case V4L2_CID_CAMERA_ANTI_BANDING:
+ return sensor_s_antibanding(sd, ctrl->value);
+ default:
+ case WMT_V4L2_CID_CAMERA_ANTIBANDING:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win) {
+ pr_err("%s, s_mbus_fmt failed, width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, win->regs, win->size);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return 0;
+}
+
+static int sensor_enum_framesizes(struct cmos_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ const struct cmos_win_size *win = &cmos_supported_win_sizes[index];
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ return (sensor_read(sd, 0) == sd->id) ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ if (sensor_identify(sd)) {
+ return -1;
+ }
+
+ sensor_write_array(sd, gc0308_default_regs_init,
+ ARRAY_SIZE(gc0308_default_regs_init));
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ sensor_write_array(sd, gc0308_default_regs_exit,
+ ARRAY_SIZE(gc0308_default_regs_exit));
+ return 0;
+}
+
+static struct cmos_subdev_ops gc0308_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+ .enum_framesizes = sensor_enum_framesizes,
+};
+
+struct cmos_subdev gc0308 = {
+ .name = "gc0308",
+ .i2c_addr = 0x21,
+ .id = 0x9b,
+ .max_width = 640,
+ .max_height = 480,
+ .ops = &gc0308_ops,
+};
+
+#if 0
+static int __init gc0308_init(void)
+{
+ return cmos_register_sudbdev(&gc0308);
+}
+
+static void __exit gc0308_exit(void)
+{
+ return cmos_unregister_subdev(&gc0308);
+}
+
+module_init(gc0308_init);
+module_exit(gc0308_exit);
+
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308.h b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308.h
new file mode 100755
index 00000000..5bd4382b
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308.h
@@ -0,0 +1,530 @@
+#ifndef GC0308_H
+#define GC0308_H
+
+
+// Scene Mode
+uint8_t gc0308_scene_mode_auto[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x20,
+};
+
+uint8_t gc0308_scene_mode_night[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x30,
+};
+
+
+// White Balance
+uint8_t gc0308_wb_auto [] = {
+ 0xfe, 0x00, // set page0
+ 0x5a, 0x56,
+ 0x5b, 0x40,
+ 0x5c, 0x4a,
+ 0x22, 0x57,
+};
+
+uint8_t gc0308_wb_incandescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x48,
+ 0x5b, 0x40,
+ 0x5c, 0x5c,
+};
+
+uint8_t gc0308_wb_fluorescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x40,
+ 0x5b, 0x42,
+ 0x5c, 0x50,
+};
+
+uint8_t gc0308_wb_daylight [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x74,
+ 0x5b, 0x52,
+ 0x5c, 0x40,
+};
+
+uint8_t gc0308_wb_cloudy [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x8c,
+ 0x5b, 0x50,
+ 0x5c, 0x40,
+};
+
+
+// Exposure
+uint8_t gc0308_exposure_neg6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xe0,
+ 0xd3, 0x38,
+};
+
+uint8_t gc0308_exposure_neg3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xf0,
+ 0xd3, 0x40,
+};
+
+uint8_t gc0308_exposure_zero[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x00,
+ 0xd3, 0x48,
+};
+
+uint8_t gc0308_exposure_pos3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x10,
+ 0xd3, 0x50,
+};
+
+uint8_t gc0308_exposure_pos6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x20,
+ 0xd3, 0x58,
+};
+
+uint8_t gc0308_antibanding_auto[] = {
+ 0xfe,0x00,
+
+ 0x01,0x97,
+ 0x02,0x84,
+ 0x0f,0x03,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x3e, //anti-flicker step [7:0]
+
+ 0xe4,0x02, //exp level 0 12.5fps
+ 0xe5,0x6c,
+ 0xe6,0x02, //exp level 1 10fps
+ 0xe7,0xe8,
+ 0xe8,0x03, //exp level 2 7.69fps
+ 0xe9,0xe0,
+ 0xea,0x05, //exp level 3 5.00fps
+ 0xeb,0xd0,
+
+};
+
+uint8_t gc0308_antibanding_50hz[] = {
+ 0xfe,0x00,
+
+ 0x01,0x26,
+ 0x02,0x98,
+ 0x0f,0x03,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x50, //anti-flicker step [7:0]
+
+ 0xe4,0x02, //exp level 0 12.5fps
+ 0xe5,0x80,
+ 0xe6,0x03, //exp level 1 10fps
+ 0xe7,0x20,
+ 0xe8,0x04, //exp level 2 7.69fps
+ 0xe9,0x10,
+ 0xea,0x06, //exp level 3 5.00fps
+ 0xeb,0x40,
+
+};
+
+uint8_t gc0308_antibanding_60hz[] = {
+ 0xfe,0x00,
+
+ 0x01,0x97,
+ 0x02,0x84,
+ 0x0f,0x03,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x3e, //anti-flicker step [7:0]
+
+ 0xe4,0x02, //exp level 0 12.5fps
+ 0xe5,0x6c,
+ 0xe6,0x02, //exp level 1 10fps
+ 0xe7,0xe8,
+ 0xe8,0x03, //exp level 2 7.69fps
+ 0xe9,0xe0,
+ 0xea,0x05, //exp level 3 5.00fps
+ 0xeb,0xd0,
+
+};
+
+uint8_t gc0308_antibanding_off[] = {
+ 0xfe,0x00,
+
+ 0x01,0x26,
+ 0x02,0x98,
+ 0x0f,0x03,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x50, //anti-flicker step [7:0]
+
+ 0xe4,0x02, //exp level 0 12.5fps
+ 0xe5,0x80,
+ 0xe6,0x03, //exp level 1 10fps
+ 0xe7,0x20,
+ 0xe8,0x04, //exp level 2 7.69fps
+ 0xe9,0x10,
+ 0xea,0x06, //exp level 3 5.00fps
+ 0xeb,0x40,
+
+};
+
+
+// Resolution
+uint8_t gc0308_320x240[]= {
+ 0xfe, 0x01,
+ 0x54, 0x22,
+ 0x55, 0x03,
+ 0x56, 0x00,
+ 0x57, 0x00,
+ 0x58, 0x00,
+ 0x59, 0x00,
+ 0xfe, 0x00,
+};
+
+uint8_t gc0308_640x480[] = {
+ 0xfe,0x01,
+ 0x54,0x11,
+ 0x55,0x03,
+ 0x56,0x00,
+ 0x57,0x00,
+ 0x58,0x00,
+ 0x59,0x00,
+ 0xfe,0x00,
+ 0x46,0x00,
+ 0xfe,0x00,
+};
+
+uint8_t gc0308_default_regs_init[] = {
+ 0xfe,0x80,
+
+ 0xfe,0x00, // set page0
+
+ 0xd2,0x10, // close AEC
+ 0x22,0x55, // close AWB
+
+ 0x03,0x01,
+ 0x04,0x2c,
+ 0x5a,0x56,
+ 0x5b,0x40,
+ 0x5c,0x4a,
+
+ 0x22,0x57, // Open AWB
+
+ 0x01,0xfa,
+ 0x02,0x70,
+ 0x0f,0x01,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x64, //anti-flicker step [7:0]
+
+ 0xe4,0x02, //exp level 1 16.67fps
+ 0xe5,0x58,
+ 0xe6,0x03, //exp level 2 12.5fps
+ 0xe7,0x20,
+ 0xe8,0x04, //exp level 3 8.33fps
+ 0xe9,0xb0,
+ 0xea,0x09, //exp level 4 4.00fps
+ 0xeb,0xc4,
+
+ 0xec,0x20,
+
+ 0x05,0x00,
+ 0x06,0x00,
+ 0x07,0x00,
+ 0x08,0x00,
+ 0x09,0x01,
+ 0x0a,0xe8,
+ 0x0b,0x02,
+ 0x0c,0x88,
+ 0x0d,0x02,
+ 0x0e,0x02,
+ 0x10,0x22,
+ 0x11,0xfd,
+ 0x12,0x2a,
+ 0x13,0x00,
+ 0x14,0x10, //0x10
+
+ 0x15,0x0a,
+ 0x16,0x05,
+ 0x17,0x01,
+ 0x18,0x44,
+ 0x19,0x44,
+ 0x1a,0x1e,
+ 0x1b,0x00,
+ 0x1c,0xc1,
+ 0x1d,0x08,
+ 0x1e,0x60,
+ 0x1f,0x16,
+
+
+
+ 0x20,0xff,
+ 0x21,0xf8,
+ 0x22,0x57,
+ 0x24,0xa2,
+ 0x25,0x0f,
+
+ //output sync_mode
+
+
+ 0x26,0x02, //0x03 20101016 zhj
+
+ 0x2f,0x01,
+ 0x30,0xf7,
+ 0x31,0x50,
+ 0x32,0x00,
+ 0x39,0x04,
+ 0x3a,0x18,
+ 0x3b,0x20,
+ 0x3c,0x00,
+ 0x3d,0x00,
+ 0x3e,0x00,
+ 0x3f,0x00,
+ 0x50,0x10,
+ 0x53,0x82,
+ 0x54,0x80,
+ 0x55,0x80,
+ 0x56,0x82,
+ 0x8b,0x40,
+ 0x8c,0x40,
+ 0x8d,0x40,
+ 0x8e,0x2e,
+ 0x8f,0x2e,
+ 0x90,0x2e,
+ 0x91,0x3c,
+ 0x92,0x50,
+ 0x5d,0x12,
+ 0x5e,0x1a,
+ 0x5f,0x24,
+ 0x60,0x07,
+ 0x61,0x15,
+ 0x62,0x08,
+ 0x64,0x03,
+ 0x66,0xe8,
+ 0x67,0x86,
+ 0x68,0xa2,
+ 0x69,0x18,
+ 0x6a,0x0f,
+ 0x6b,0x00,
+ 0x6c,0x5f,
+ 0x6d,0x8f,
+ 0x6e,0x55,
+ 0x6f,0x38,
+ 0x70,0x15,
+ 0x71,0x33,
+ 0x72,0xdc,
+ 0x73,0x80,
+ 0x74,0x02,
+ 0x75,0x3f,
+ 0x76,0x02,
+ 0x77,0x54,
+ 0x78,0x88,
+ 0x79,0x81,
+ 0x7a,0x81,
+ 0x7b,0x22,
+ 0x7c,0xff,
+ 0x93,0x48,
+ 0x94,0x00,
+ 0x95,0x05,
+ 0x96,0xe8,
+ 0x97,0x40,
+ 0x98,0xf0,
+ 0xb1,0x38,
+ 0xb2,0x38,
+ 0xbd,0x38,
+ 0xbe,0x36,
+ 0xd0,0xc9,
+ 0xd1,0x10,
+
+ 0xd3,0x80,
+ 0xd5,0xf2,
+ 0xd6,0x16,
+ 0xdb,0x92,
+ 0xdc,0xa5,
+ 0xdf,0x23,
+ 0xd9,0x00,
+ 0xda,0x00,
+ 0xe0,0x09,
+
+ 0xed,0x04,
+ 0xee,0xa0,
+ 0xef,0x40,
+ 0x80,0x03,
+ 0x80,0x03,
+
+
+ 0x9F,0x14,
+ 0xA0,0x24,
+ 0xA1,0x3e,
+ 0xA2,0x50,
+ 0xA3,0x63,
+ 0xA4,0x76,
+ 0xA5,0x87,
+ 0xA6,0xA2,
+ 0xA7,0xB8,
+ 0xA8,0xCA,
+ 0xA9,0xD8,
+ 0xAA,0xE3,
+ 0xAB,0xEB,
+ 0xAC,0xF0,
+ 0xAD,0xF8,
+ 0xAE,0xFD,
+ 0xAF,0xFF,
+
+ 0xc0,0x00,
+ 0xc1,0x10,
+ 0xc2,0x1C,
+ 0xc3,0x30,
+ 0xc4,0x43,
+ 0xc5,0x54,
+ 0xc6,0x65,
+ 0xc7,0x75,
+ 0xc8,0x93,
+ 0xc9,0xB0,
+ 0xca,0xCB,
+ 0xcb,0xE6,
+ 0xcc,0xFF,
+ 0xf0,0x02,
+ 0xf1,0x01,
+ 0xf2,0x01,
+ 0xf3,0x30,
+ 0xf9,0x9f,
+ 0xfa,0x78,
+ //----------------------------
+ 0xfe,0x01,// set page1
+ 0x00,0xf5,
+ 0x02,0x1a,
+ 0x0a,0xa0,
+ 0x0b,0x60,
+ 0x0c,0x08,
+ 0x0e,0x4c,
+ 0x0f,0x39,
+ 0x11,0x3f,
+ 0x12,0x72,
+ 0x13,0x13,
+ 0x14,0x42,
+ 0x15,0x43,
+ 0x16,0xc2,
+ 0x17,0xa8,
+ 0x18,0x18,
+ 0x19,0x40,
+ 0x1a,0xd0,
+ 0x1b,0xf5,
+ 0x70,0x40,
+ 0x71,0x58,
+ 0x72,0x30,
+ 0x73,0x48,
+ 0x74,0x20,
+ 0x75,0x60,
+ 0x77,0x20,
+ 0x78,0x32,
+ 0x30,0x03,
+ 0x31,0x40,
+ 0x32,0xe0,
+ 0x33,0xe0,
+ 0x34,0xe0,
+ 0x35,0xb0,
+ 0x36,0xc0,
+ 0x37,0xc0,
+ 0x38,0x04,
+ 0x39,0x09,
+ 0x3a,0x12,
+ 0x3b,0x1C,
+ 0x3c,0x28,
+ 0x3d,0x31,
+ 0x3e,0x44,
+ 0x3f,0x57,
+ 0x40,0x6C,
+ 0x41,0x81,
+ 0x42,0x94,
+ 0x43,0xA7,
+ 0x44,0xB8,
+ 0x45,0xD6,
+ 0x46,0xEE,
+ 0x47,0x0d,
+ 0xfe,0x00,// set page0
+
+ //-----------Update the registers 2010/07/06-----------
+ //Registers of Page0
+ 0xfe,0x00,// set page0
+ 0x10,0x26,
+ 0x11,0x0d, // fd
+
+ 0x1a,0x2a, // 1e
+
+
+ 0x1c,0x49,// c1
+ 0x1d,0x9a,// 08
+
+
+ 0x1e,0x61,// 60
+
+ 0x3a,0x20,
+
+ 0x50,0x14, // 10
+
+
+ 0x53,0x80,
+ 0x56,0x80,
+
+ 0x8b,0x20,//LSC
+ 0x8c,0x20,
+ 0x8d,0x20,
+ 0x8e,0x14,
+ 0x8f,0x10,
+ 0x90,0x14,
+
+ 0x94,0x02,
+ 0x95,0x07,
+ 0x96,0xe0,
+
+ 0xb1,0x3a,// YCPT
+ 0xb2,0x3a,
+ 0xb3,0x40,
+ 0xb6,0xe0,
+
+ 0xd0,0xcb,// AECT c9
+
+
+ 0xd3,0x48,// 80
+
+ 0xf2,0x02,
+ 0xf7,0x12,
+ 0xf8,0x0a,
+
+ //Registers of Page1
+ 0xfe,0x01,// set page1
+ 0x02,0x20,
+ 0x04,0x10,
+ 0x05,0x08,
+ 0x06,0x20,
+ 0x08,0x0a,
+
+ 0x0e,0x44,
+ 0x0f,0x32,
+ 0x10,0x41,
+ 0x11,0x37,
+ 0x12,0x22,
+ 0x13,0x19,
+ 0x14,0x44,
+ 0x15,0x44,
+
+ 0x19,0x50,
+ 0x1a,0xd8,
+
+ 0x32,0x10,
+
+ 0x35,0x00,
+ 0x36,0x80,
+ 0x37,0x00,
+ //-----------Update the registers end---------//
+ 0xfe,0x00,// set page0
+ 0xd2,0x90,
+};
+
+uint8_t gc0308_default_regs_exit[]={
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308_7300_hd.h b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308_7300_hd.h
new file mode 100755
index 00000000..424eff2b
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308_7300_hd.h
@@ -0,0 +1,424 @@
+#ifndef GC0308_H
+#define GC0308_H
+
+#define GC0308_I2C_ADDR 0x21
+
+#define gc0308_read_reg(r) wmt_vid_i2c_read(GC0308_I2C_ADDR, r)
+
+#define gc0308_write_reg(r, d) wmt_vid_i2c_write(GC0308_I2C_ADDR, r, d)
+
+#define gc0308_write_array(a) \
+do { \
+ cmos_init_8bit_addr(a, ARRAY_SIZE(a), GC0308_I2C_ADDR); \
+} while (0)
+
+
+// Scene Mode
+unsigned char gc0308_scene_mode_auto[] = {
+ 0xec, 0x20,
+};
+
+unsigned char gc0308_scene_mode_night[] = {
+ 0xec, 0x30,
+};
+
+
+// White Balance
+unsigned char gc0308_wb_auto [] = {
+ 0x5a, 0x56,
+ 0x5b, 0x40,
+ 0x5c, 0x4a,
+ 0x22, 0x57,
+};
+
+unsigned char gc0308_wb_incandescent [] = {
+ 0x22, 0x55,
+ 0x5a, 0x48,
+ 0x5b, 0x40,
+ 0x5c, 0x5c,
+};
+
+unsigned char gc0308_wb_fluorescent [] = {
+ 0x22, 0x55,
+ 0x5a, 0x40,
+ 0x5b, 0x42,
+ 0x5c, 0x50,
+};
+
+unsigned char gc0308_wb_daylight [] = {
+ 0x22, 0x55,
+ 0x5a, 0x74,
+ 0x5b, 0x52,
+ 0x5c, 0x40,
+};
+
+unsigned char gc0308_wb_cloudy [] = {
+ 0x22, 0x55,
+ 0x5a, 0x8c,
+ 0x5b, 0x50,
+ 0x5c, 0x40,
+};
+
+
+// Exposure
+unsigned char gc0308_exposure_neg6[] = {
+ 0xb5, 0xe0,
+ 0xd3, 0x38,
+};
+
+unsigned char gc0308_exposure_neg3[] = {
+ 0xb5, 0xf0,
+ 0xd3, 0x40,
+};
+
+unsigned char gc0308_exposure_zero[] = {
+ 0xb5, 0x00,
+ 0xd3, 0x40,
+};
+
+unsigned char gc0308_exposure_pos3[] = {
+ 0xb5, 0x10,
+ 0xd3, 0x50,
+};
+
+unsigned char gc0308_exposure_pos6[] = {
+ 0xb5, 0x20,
+ 0xd3, 0x58,
+};
+
+
+// Resolution
+unsigned char gc0308_320x240[]= {
+0xfe, 0x01, 0x54, 0x22, 0x55, 0x03, 0x56, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0xfe, 0x00
+};
+
+unsigned char gc0308_640x480[]={
+0xfe,0x80,
+
+0xfe,0x00, // set page0
+
+0xd2,0x10, // close AEC
+0x22,0x55, // close AWB
+
+0x03,0x01,
+0x04,0x2c,
+0x5a,0x56,
+0x5b,0x40,
+0x5c,0x4a,
+
+0x22,0x57, // Open AWB
+
+0x01,0xfa,
+0x02,0x70,
+0x0f,0x01,
+
+0xe2,0x00, //anti-flicker step [11:8]
+0xe3,0x64, //anti-flicker step [7:0]
+
+0xe4,0x02, //exp level 1 16.67fps
+0xe5,0x58,
+0xe6,0x03, //exp level 2 12.5fps
+0xe7,0x20,
+0xe8,0x04, //exp level 3 8.33fps
+0xe9,0xb0,
+0xea,0x09, //exp level 4 4.00fps
+0xeb,0xc4,
+
+0xec,0x20,
+
+0x05,0x00,
+0x06,0x00,
+0x07,0x00,
+0x08,0x00,
+0x09,0x01,
+0x0a,0xe8,
+0x0b,0x02,
+0x0c,0x88,
+0x0d,0x02,
+0x0e,0x02,
+0x10,0x22,
+0x11,0xfd,
+0x12,0x2a,
+0x13,0x00,
+0x14,0x10, //0x10
+
+0x15,0x0a,
+0x16,0x05,
+0x17,0x01,
+0x18,0x44,
+0x19,0x44,
+0x1a,0x1e,
+0x1b,0x00,
+0x1c,0xc1,
+0x1d,0x08,
+0x1e,0x60,
+0x1f,0x03,
+
+
+
+0x20,0xff,
+0x21,0xf8,
+0x22,0x57,
+0x24,0xa2,
+0x25,0x0f,
+
+//output sync_mode
+
+
+0x26,0x02, //0x03 20101016 zhj
+
+0x2f,0x01,
+0x30,0xf7,
+0x31,0x50,
+0x32,0x00,
+0x39,0x04,
+0x3a,0x18,
+0x3b,0x20,
+0x3c,0x00,
+0x3d,0x00,
+0x3e,0x00,
+0x3f,0x00,
+0x50,0x10,
+0x53,0x82,
+0x54,0x80,
+0x55,0x80,
+0x56,0x82,
+0x8b,0x40,
+0x8c,0x40,
+0x8d,0x40,
+0x8e,0x2e,
+0x8f,0x2e,
+0x90,0x2e,
+0x91,0x3c,
+0x92,0x50,
+0x5d,0x12,
+0x5e,0x1a,
+0x5f,0x24,
+0x60,0x07,
+0x61,0x15,
+0x62,0x08,
+0x64,0x03,
+0x66,0xe8,
+0x67,0x86,
+0x68,0xa2,
+0x69,0x18,
+0x6a,0x0f,
+0x6b,0x00,
+0x6c,0x5f,
+0x6d,0x8f,
+0x6e,0x55,
+0x6f,0x38,
+0x70,0x15,
+0x71,0x33,
+0x72,0xdc,
+0x73,0x80,
+0x74,0x02,
+0x75,0x3f,
+0x76,0x02,
+0x77,0x54,
+0x78,0x88,
+0x79,0x81,
+0x7a,0x81,
+0x7b,0x22,
+0x7c,0xff,
+0x93,0x48,
+0x94,0x00,
+0x95,0x05,
+0x96,0xe8,
+0x97,0x40,
+0x98,0xf0,
+0xb1,0x38,
+0xb2,0x38,
+0xbd,0x38,
+0xbe,0x36,
+0xd0,0xc9,
+0xd1,0x10,
+//{{0xd2,0x90,
+0xd3,0x80,
+0xd5,0xf2,
+0xd6,0x16,
+0xdb,0x92,
+0xdc,0xa5,
+0xdf,0x23,
+0xd9,0x00,
+0xda,0x00,
+0xe0,0x09,
+
+0xed,0x04,
+0xee,0xa0,
+0xef,0x40,
+0x80,0x03,
+0x80,0x03,
+
+
+0x9F,0x10,
+0xA0,0x20,
+0xA1,0x38,
+0xA2,0x4E,
+0xA3,0x63,
+0xA4,0x76,
+0xA5,0x87,
+0xA6,0xA2,
+0xA7,0xB8,
+0xA8,0xCA,
+0xA9,0xD8,
+0xAA,0xE3,
+0xAB,0xEB,
+0xAC,0xF0,
+0xAD,0xF8,
+0xAE,0xFD,
+0xAF,0xFF,
+
+0xc0,0x00,
+0xc1,0x10,
+0xc2,0x1C,
+0xc3,0x30,
+0xc4,0x43,
+0xc5,0x54,
+0xc6,0x65,
+0xc7,0x75,
+0xc8,0x93,
+0xc9,0xB0,
+0xca,0xCB,
+0xcb,0xE6,
+0xcc,0xFF,
+0xf0,0x02,
+0xf1,0x01,
+0xf2,0x01,
+0xf3,0x30,
+0xf9,0x9f,
+0xfa,0x78,
+//----------------------------
+0xfe,0x01,// set page1
+0x00,0xf5,
+0x02,0x1a,
+0x0a,0xa0,
+0x0b,0x60,
+0x0c,0x08,
+0x0e,0x4c,
+0x0f,0x39,
+0x11,0x3f,
+0x12,0x72,
+0x13,0x13,
+0x14,0x42,
+0x15,0x43,
+0x16,0xc2,
+0x17,0xa8,
+0x18,0x18,
+0x19,0x40,
+0x1a,0xd0,
+0x1b,0xf5,
+0x70,0x40,
+0x71,0x58,
+0x72,0x30,
+0x73,0x48,
+0x74,0x20,
+0x75,0x60,
+0x77,0x20,
+0x78,0x32,
+0x30,0x03,
+0x31,0x40,
+0x32,0xe0,
+0x33,0xe0,
+0x34,0xe0,
+0x35,0xb0,
+0x36,0xc0,
+0x37,0xc0,
+0x38,0x04,
+0x39,0x09,
+0x3a,0x12,
+0x3b,0x1C,
+0x3c,0x28,
+0x3d,0x31,
+0x3e,0x44,
+0x3f,0x57,
+0x40,0x6C,
+0x41,0x81,
+0x42,0x94,
+0x43,0xA7,
+0x44,0xB8,
+0x45,0xD6,
+0x46,0xEE,
+0x47,0x0d,
+0xfe,0x00,// set page0
+
+//-----------Update the registers 2010/07/06-----------
+ //Registers of Page0
+0xfe,0x00,// set page0
+0x10,0x26,
+0x11,0x0d, // fd},{modified by mormo 2010/07/06
+
+0x1a,0x2a, // 1e},{modified by mormo 2010/07/06
+
+
+0x1c,0x49,// c1},{modified by mormo 2010/07/06
+0x1d,0x9a,// 08},{modified by mormo 2010/07/06
+
+
+0x1e,0x61,// 60},{modified by mormo 2010/07/06
+
+0x3a,0x20,
+
+0x50,0x14, // 10},{modified by mormo 2010/07/06
+
+
+0x53,0x80,
+0x56,0x80,
+
+0x8b,0x20,//LSC
+0x8c,0x20,
+0x8d,0x20,
+0x8e,0x14,
+0x8f,0x10,
+0x90,0x14,
+
+0x94,0x02,
+0x95,0x07,
+0x96,0xe0,
+
+0xb1,0x40,// YCPT
+0xb2,0x40,
+0xb3,0x38,
+0xb6,0xe0,
+
+0xd0,0xcb,// AECT c9},{modifed by mormo 2010/07/06
+
+
+0xd3,0x40,// 80},{modified by mormor 2010/07/06
+
+
+
+0xf2,0x02,
+0xf7,0x12,
+0xf8,0x0a,
+
+ //Registers of Page1
+0xfe,0x01,// set page1
+0x02,0x20,
+0x04,0x10,
+0x05,0x08,
+0x06,0x20,
+0x08,0x0a,
+
+0x0e,0x44,
+0x0f,0x32,
+0x10,0x41,
+0x11,0x37,
+0x12,0x22,
+0x13,0x19,
+0x14,0x44,
+0x15,0x44,
+
+0x19,0x50,
+0x1a,0xd8,
+
+0x32,0x10,
+
+0x35,0x00,
+0x36,0x80,
+0x37,0x00,
+//-----------Update the registers end---------//
+0xfe,0x00,// set page0
+0xd2,0x90,
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308_8335c.h b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308_8335c.h
new file mode 100755
index 00000000..678d799e
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308_8335c.h
@@ -0,0 +1,424 @@
+#ifndef GC0308_H
+#define GC0308_H
+
+#define GC0308_I2C_ADDR 0x21
+
+#define gc0308_read_reg(r) wmt_vid_i2c_read(GC0308_I2C_ADDR, r)
+
+#define gc0308_write_reg(r, d) wmt_vid_i2c_write(GC0308_I2C_ADDR, r, d)
+
+#define gc0308_write_array(a) \
+do { \
+ cmos_init_8bit_addr(a, ARRAY_SIZE(a), GC0308_I2C_ADDR); \
+} while (0)
+
+
+// Scene Mode
+unsigned char gc0308_scene_mode_auto[] = {
+ 0xec, 0x20,
+};
+
+unsigned char gc0308_scene_mode_night[] = {
+ 0xec, 0x30,
+};
+
+
+// White Balance
+unsigned char gc0308_wb_auto [] = {
+ 0x5a, 0x56,
+ 0x5b, 0x40,
+ 0x5c, 0x4a,
+ 0x22, 0x57,
+};
+
+unsigned char gc0308_wb_incandescent [] = {
+ 0x22, 0x55,
+ 0x5a, 0x48,
+ 0x5b, 0x40,
+ 0x5c, 0x5c,
+};
+
+unsigned char gc0308_wb_fluorescent [] = {
+ 0x22, 0x55,
+ 0x5a, 0x40,
+ 0x5b, 0x42,
+ 0x5c, 0x50,
+};
+
+unsigned char gc0308_wb_daylight [] = {
+ 0x22, 0x55,
+ 0x5a, 0x74,
+ 0x5b, 0x52,
+ 0x5c, 0x40,
+};
+
+unsigned char gc0308_wb_cloudy [] = {
+ 0x22, 0x55,
+ 0x5a, 0x8c,
+ 0x5b, 0x50,
+ 0x5c, 0x40,
+};
+
+
+// Exposure
+unsigned char gc0308_exposure_neg6[] = {
+ 0xb5, 0xe0,
+ 0xd3, 0x38,
+};
+
+unsigned char gc0308_exposure_neg3[] = {
+ 0xb5, 0xf0,
+ 0xd3, 0x40,
+};
+
+unsigned char gc0308_exposure_zero[] = {
+ 0xb5, 0x00,
+ 0xd3, 0x48,
+};
+
+unsigned char gc0308_exposure_pos3[] = {
+ 0xb5, 0x10,
+ 0xd3, 0x50,
+};
+
+unsigned char gc0308_exposure_pos6[] = {
+ 0xb5, 0x20,
+ 0xd3, 0x58,
+};
+
+
+// Resolution
+unsigned char gc0308_320x240[]= {
+0xfe, 0x01, 0x54, 0x22, 0x55, 0x03, 0x56, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0xfe, 0x00
+};
+
+unsigned char gc0308_640x480[]={
+0xfe,0x80,
+
+0xfe,0x00, // set page0
+
+0xd2,0x10, // close AEC
+0x22,0x55, // close AWB
+
+0x03,0x01,
+0x04,0x2c,
+0x5a,0x56,
+0x5b,0x40,
+0x5c,0x4a,
+
+0x22,0x57, // Open AWB
+
+0x01,0xfa,
+0x02,0x70,
+0x0f,0x01,
+
+0xe2,0x00, //anti-flicker step [11:8]
+0xe3,0x64, //anti-flicker step [7:0]
+
+0xe4,0x02, //exp level 1 16.67fps
+0xe5,0x58,
+0xe6,0x03, //exp level 2 12.5fps
+0xe7,0x20,
+0xe8,0x04, //exp level 3 8.33fps
+0xe9,0xb0,
+0xea,0x09, //exp level 4 4.00fps
+0xeb,0xc4,
+
+0xec,0x20,
+
+0x05,0x00,
+0x06,0x00,
+0x07,0x00,
+0x08,0x00,
+0x09,0x01,
+0x0a,0xe8,
+0x0b,0x02,
+0x0c,0x88,
+0x0d,0x02,
+0x0e,0x02,
+0x10,0x22,
+0x11,0xfd,
+0x12,0x2a,
+0x13,0x00,
+0x14,0x10, //0x10
+
+0x15,0x0a,
+0x16,0x05,
+0x17,0x01,
+0x18,0x44,
+0x19,0x44,
+0x1a,0x1e,
+0x1b,0x00,
+0x1c,0xc1,
+0x1d,0x08,
+0x1e,0x60,
+0x1f,0x03,
+
+
+
+0x20,0xff,
+0x21,0xf8,
+0x22,0x57,
+0x24,0xa2,
+0x25,0x0f,
+
+//output sync_mode
+
+
+0x26,0x02, //0x03 20101016 zhj
+
+0x2f,0x01,
+0x30,0xf7,
+0x31,0x50,
+0x32,0x00,
+0x39,0x04,
+0x3a,0x18,
+0x3b,0x20,
+0x3c,0x00,
+0x3d,0x00,
+0x3e,0x00,
+0x3f,0x00,
+0x50,0x10,
+0x53,0x82,
+0x54,0x80,
+0x55,0x80,
+0x56,0x82,
+0x8b,0x40,
+0x8c,0x40,
+0x8d,0x40,
+0x8e,0x2e,
+0x8f,0x2e,
+0x90,0x2e,
+0x91,0x3c,
+0x92,0x50,
+0x5d,0x12,
+0x5e,0x1a,
+0x5f,0x24,
+0x60,0x07,
+0x61,0x15,
+0x62,0x08,
+0x64,0x03,
+0x66,0xe8,
+0x67,0x86,
+0x68,0xa2,
+0x69,0x18,
+0x6a,0x0f,
+0x6b,0x00,
+0x6c,0x5f,
+0x6d,0x8f,
+0x6e,0x55,
+0x6f,0x38,
+0x70,0x15,
+0x71,0x33,
+0x72,0xdc,
+0x73,0x80,
+0x74,0x02,
+0x75,0x3f,
+0x76,0x02,
+0x77,0x54,
+0x78,0x88,
+0x79,0x81,
+0x7a,0x81,
+0x7b,0x22,
+0x7c,0xff,
+0x93,0x48,
+0x94,0x00,
+0x95,0x05,
+0x96,0xe8,
+0x97,0x40,
+0x98,0xf0,
+0xb1,0x38,
+0xb2,0x38,
+0xbd,0x38,
+0xbe,0x36,
+0xd0,0xc9,
+0xd1,0x10,
+//{{0xd2,0x90,
+0xd3,0x80,
+0xd5,0xf2,
+0xd6,0x16,
+0xdb,0x92,
+0xdc,0xa5,
+0xdf,0x23,
+0xd9,0x00,
+0xda,0x00,
+0xe0,0x09,
+
+0xed,0x04,
+0xee,0xa0,
+0xef,0x40,
+0x80,0x03,
+0x80,0x03,
+
+
+0x9F,0x10,
+0xA0,0x20,
+0xA1,0x38,
+0xA2,0x4E,
+0xA3,0x63,
+0xA4,0x76,
+0xA5,0x87,
+0xA6,0xA2,
+0xA7,0xB8,
+0xA8,0xCA,
+0xA9,0xD8,
+0xAA,0xE3,
+0xAB,0xEB,
+0xAC,0xF0,
+0xAD,0xF8,
+0xAE,0xFD,
+0xAF,0xFF,
+
+0xc0,0x00,
+0xc1,0x10,
+0xc2,0x1C,
+0xc3,0x30,
+0xc4,0x43,
+0xc5,0x54,
+0xc6,0x65,
+0xc7,0x75,
+0xc8,0x93,
+0xc9,0xB0,
+0xca,0xCB,
+0xcb,0xE6,
+0xcc,0xFF,
+0xf0,0x02,
+0xf1,0x01,
+0xf2,0x01,
+0xf3,0x30,
+0xf9,0x9f,
+0xfa,0x78,
+//----------------------------
+0xfe,0x01,// set page1
+0x00,0xf5,
+0x02,0x1a,
+0x0a,0xa0,
+0x0b,0x60,
+0x0c,0x08,
+0x0e,0x4c,
+0x0f,0x39,
+0x11,0x3f,
+0x12,0x72,
+0x13,0x13,
+0x14,0x42,
+0x15,0x43,
+0x16,0xc2,
+0x17,0xa8,
+0x18,0x18,
+0x19,0x40,
+0x1a,0xd0,
+0x1b,0xf5,
+0x70,0x40,
+0x71,0x58,
+0x72,0x30,
+0x73,0x48,
+0x74,0x20,
+0x75,0x60,
+0x77,0x20,
+0x78,0x32,
+0x30,0x03,
+0x31,0x40,
+0x32,0xe0,
+0x33,0xe0,
+0x34,0xe0,
+0x35,0xb0,
+0x36,0xc0,
+0x37,0xc0,
+0x38,0x04,
+0x39,0x09,
+0x3a,0x12,
+0x3b,0x1C,
+0x3c,0x28,
+0x3d,0x31,
+0x3e,0x44,
+0x3f,0x57,
+0x40,0x6C,
+0x41,0x81,
+0x42,0x94,
+0x43,0xA7,
+0x44,0xB8,
+0x45,0xD6,
+0x46,0xEE,
+0x47,0x0d,
+0xfe,0x00,// set page0
+
+//-----------Update the registers 2010/07/06-----------
+ //Registers of Page0
+0xfe,0x00,// set page0
+0x10,0x26,
+0x11,0x0d, // fd},{modified by mormo 2010/07/06
+
+0x1a,0x2a, // 1e},{modified by mormo 2010/07/06
+
+
+0x1c,0x49,// c1},{modified by mormo 2010/07/06
+0x1d,0x9a,// 08},{modified by mormo 2010/07/06
+
+
+0x1e,0x61,// 60},{modified by mormo 2010/07/06
+
+0x3a,0x20,
+
+0x50,0x14, // 10},{modified by mormo 2010/07/06
+
+
+0x53,0x80,
+0x56,0x80,
+
+0x8b,0x20,//LSC
+0x8c,0x20,
+0x8d,0x20,
+0x8e,0x14,
+0x8f,0x10,
+0x90,0x14,
+
+0x94,0x02,
+0x95,0x07,
+0x96,0xe0,
+
+0xb1,0x40,// YCPT
+0xb2,0x40,
+0xb3,0x40,
+0xb6,0xe0,
+
+0xd0,0xcb,// AECT c9},{modifed by mormo 2010/07/06
+
+
+0xd3,0x48,// 80},{modified by mormor 2010/07/06
+
+
+
+0xf2,0x02,
+0xf7,0x12,
+0xf8,0x0a,
+
+ //Registers of Page1
+0xfe,0x01,// set page1
+0x02,0x20,
+0x04,0x10,
+0x05,0x08,
+0x06,0x20,
+0x08,0x0a,
+
+0x0e,0x44,
+0x0f,0x32,
+0x10,0x41,
+0x11,0x37,
+0x12,0x22,
+0x13,0x19,
+0x14,0x44,
+0x15,0x44,
+
+0x19,0x50,
+0x1a,0xd8,
+
+0x32,0x10,
+
+0x35,0x00,
+0x36,0x80,
+0x37,0x00,
+//-----------Update the registers end---------//
+0xfe,0x00,// set page0
+0xd2,0x90,
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308_8353.h b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308_8353.h
new file mode 100755
index 00000000..717e5a42
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0308/gc0308_8353.h
@@ -0,0 +1,457 @@
+#ifndef GC0308_H
+#define GC0308_H
+
+
+// Scene Mode
+uint8_t gc0308_scene_mode_auto[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x20,
+};
+
+uint8_t gc0308_scene_mode_night[] = {
+ 0xfe, 0x00, // set page0
+ 0xec, 0x30,
+};
+
+
+// White Balance
+uint8_t gc0308_wb_auto [] = {
+ 0xfe, 0x00, // set page0
+ 0x5a, 0x56,
+ 0x5b, 0x40,
+ 0x5c, 0x4a,
+ 0x22, 0x57,
+};
+
+uint8_t gc0308_wb_incandescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x48,
+ 0x5b, 0x40,
+ 0x5c, 0x5c,
+};
+
+uint8_t gc0308_wb_fluorescent [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x40,
+ 0x5b, 0x42,
+ 0x5c, 0x50,
+};
+
+uint8_t gc0308_wb_daylight [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x74,
+ 0x5b, 0x52,
+ 0x5c, 0x40,
+};
+
+uint8_t gc0308_wb_cloudy [] = {
+ 0xfe, 0x00, // set page0
+ 0x22, 0x55,
+ 0x5a, 0x8c,
+ 0x5b, 0x50,
+ 0x5c, 0x40,
+};
+
+
+// Exposure
+uint8_t gc0308_exposure_neg6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xe0,
+ 0xd3, 0x38,
+};
+
+uint8_t gc0308_exposure_neg3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0xf0,
+ 0xd3, 0x40,
+};
+
+uint8_t gc0308_exposure_zero[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x00,
+ 0xd3, 0x48,
+};
+
+uint8_t gc0308_exposure_pos3[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x10,
+ 0xd3, 0x50,
+};
+
+uint8_t gc0308_exposure_pos6[] = {
+ 0xfe, 0x00, // set page0
+ 0xb5, 0x20,
+ 0xd3, 0x58,
+};
+
+
+// Resolution
+uint8_t gc0308_320x240[]= {
+ 0xfe, 0x01,
+ 0x54, 0x22,
+ 0x55, 0x03,
+ 0x56, 0x00,
+ 0x57, 0x00,
+ 0x58, 0x00,
+ 0x59, 0x00,
+ 0xfe, 0x00,
+};
+
+uint8_t gc0308_640x480[]= {
+ 0xfe,0x01,
+ 0x54,0x11,
+ 0x55,0x03,
+ 0x56,0x00,
+ 0x57,0x00,
+ 0x58,0x00,
+ 0x59,0x00,
+ 0xfe,0x00,
+ 0x46,0x00,
+ 0xfe,0x00,
+};
+
+uint8_t gc0308_default_regs_init[]={
+ 0xfe,0x80,
+
+ 0xfe,0x00, // set page0
+
+ 0xd2,0x10, // close AEC
+ 0x22,0x55, // close AWB
+
+ 0x03,0x01,
+ 0x04,0x2c,
+ 0x5a,0x56,
+ 0x5b,0x40,
+ 0x5c,0x4a,
+
+ 0x22,0x57, // Open AWB
+
+ 0x28,0x11,
+ 0x57,0x80,
+ 0x57,0x80,
+ 0x59,0x80,
+
+
+ 0x01,0xfa,
+ 0x02,0x70,
+ 0x0f,0x01,
+
+ 0xe2,0x00, //anti-flicker step [11:8]
+ 0xe3,0x32, //anti-flicker step [7:0] //64
+
+ 0xe4,0x02, //exp level 1 16.67fps
+ 0xe5,0x58,
+ 0xe6,0x02, //exp level 2 12.5fps
+ 0xe7,0x58,
+ 0xe8,0x02, //exp level 3 8.33fps
+ 0xe9,0x58,
+ 0xea,0x03, //exp level 4 4.00fps
+ 0xeb,0xe8,
+
+ 0xec,0x20,
+
+ 0x05,0x00,
+ 0x06,0x00,
+ 0x07,0x00,
+ 0x08,0x00,
+ 0x09,0x01,
+ 0x0a,0xe8,
+ 0x0b,0x02,
+ 0x0c,0x88,
+ 0x0d,0x02,
+ 0x0e,0x02,
+ 0x10,0x22,
+ 0x11,0xfd,
+ 0x12,0x2a,
+ 0x13,0x00,
+ 0x14,0x10, //0x10
+
+ 0x15,0x0a,
+ 0x16,0x05,
+ 0x17,0x01,
+ 0x18,0x44,
+ 0x19,0x44,
+ 0x1a,0x1e,
+ 0x1b,0x00,
+ 0x1c,0xc1,
+ 0x1d,0x08,
+ 0x1e,0x60,
+ 0x1f,0x03,
+
+
+
+ 0x20,0xff,
+ 0x21,0xf8,
+ 0x22,0x57,
+ 0x24,0xa2,
+ 0x25,0x0f,
+
+ //output sync_mode
+
+
+ 0x26,0x02, //0x03 20101016 zhj
+
+ 0x2f,0x01,
+ 0x30,0xf7,
+ 0x31,0x50,
+ 0x32,0x00,
+ 0x39,0x04,
+ 0x3a,0x18,
+ 0x3b,0x20,
+ 0x3c,0x00,
+ 0x3d,0x00,
+ 0x3e,0x00,
+ 0x3f,0x00,
+ 0x50,0x10,
+ 0x53,0x82,
+ 0x54,0x80,
+ 0x55,0x80,
+ 0x56,0x82,
+ 0x8b,0x40,
+ 0x8c,0x40,
+ 0x8d,0x40,
+ 0x8e,0x2e,
+ 0x8f,0x2e,
+ 0x90,0x2e,
+ 0x91,0x3c,
+ 0x92,0x50,
+ 0x5d,0x12,
+ 0x5e,0x1a,
+ 0x5f,0x24,
+ 0x60,0x07,
+ 0x61,0x15,
+ 0x62,0x08,
+ 0x64,0x03,
+ 0x66,0xe8,
+ 0x67,0x86,
+ 0x68,0xa2,
+ 0x69,0x18,
+ 0x6a,0x0f,
+ 0x6b,0x00,
+ 0x6c,0x5f,
+ 0x6d,0x8f,
+ 0x6e,0x55,
+ 0x6f,0x38,
+ 0x70,0x15,
+ 0x71,0x33,
+ 0x72,0xdc,
+ 0x73,0x80,
+ 0x74,0x02,
+ 0x75,0x3f,
+ 0x76,0x02,
+ 0x77,0x54,
+ 0x78,0x88,
+ 0x79,0x81,
+ 0x7a,0x81,
+ 0x7b,0x22,
+ 0x7c,0xff,
+ 0x93,0x48,
+ 0x94,0x00,
+ 0x95,0x05,
+ 0x96,0xe8,
+ 0x97,0x40,
+ 0x98,0xf0,
+ 0xb1,0x38,
+ 0xb2,0x38,
+ 0xbd,0x38,
+ 0xbe,0x36,
+ 0xd0,0xc9,
+ 0xd1,0x10,
+ //{{0xd2,0x90,
+ 0xd3,0x80,
+ 0xd5,0xf2,
+ 0xd6,0x16,
+ 0xdb,0x92,
+ 0xdc,0xa5,
+ 0xdf,0x23,
+ 0xd9,0x00,
+ 0xda,0x00,
+ 0xe0,0x09,
+
+ 0xed,0x04,
+ 0xee,0xa0,
+ 0xef,0x40,
+ 0x80,0x03,
+ 0x80,0x03,
+
+
+ 0x9F,0x10,
+ 0xA0,0x20,
+ 0xA1,0x38,
+ 0xA2,0x4E,
+ 0xA3,0x63,
+ 0xA4,0x76,
+ 0xA5,0x87,
+ 0xA6,0xA2,
+ 0xA7,0xB8,
+ 0xA8,0xCA,
+ 0xA9,0xD8,
+ 0xAA,0xE3,
+ 0xAB,0xEB,
+ 0xAC,0xF0,
+ 0xAD,0xF8,
+ 0xAE,0xFD,
+ 0xAF,0xFF,
+
+ 0xc0,0x00,
+ 0xc1,0x10,
+ 0xc2,0x1C,
+ 0xc3,0x30,
+ 0xc4,0x43,
+ 0xc5,0x54,
+ 0xc6,0x65,
+ 0xc7,0x75,
+ 0xc8,0x93,
+ 0xc9,0xB0,
+ 0xca,0xCB,
+ 0xcb,0xE6,
+ 0xcc,0xFF,
+ 0xf0,0x02,
+ 0xf1,0x01,
+ 0xf2,0x01,
+ 0xf3,0x30,
+ 0xf9,0x9f,
+ 0xfa,0x78,
+ //----------------------------
+ 0xfe,0x01,// set page1
+ 0x00,0xf5,
+ 0x02,0x1a,
+ 0x0a,0xa0,
+ 0x0b,0x60,
+ 0x0c,0x08,
+ 0x0e,0x4c,
+ 0x0f,0x39,
+ 0x11,0x3f,
+ 0x12,0x72,
+ 0x13,0x13,
+ 0x14,0x42,
+ 0x15,0x43,
+ 0x16,0xc2,
+ 0x17,0xa8,
+ 0x18,0x18,
+ 0x19,0x40,
+ 0x1a,0xd0,
+ 0x1b,0xf5,
+ 0x70,0x40,
+ 0x71,0x58,
+ 0x72,0x30,
+ 0x73,0x48,
+ 0x74,0x20,
+ 0x75,0x60,
+ 0x77,0x20,
+ 0x78,0x32,
+ 0x30,0x03,
+ 0x31,0x40,
+ 0x32,0xe0,
+ 0x33,0xe0,
+ 0x34,0xe0,
+ 0x35,0xb0,
+ 0x36,0xc0,
+ 0x37,0xc0,
+ 0x38,0x04,
+ 0x39,0x09,
+ 0x3a,0x12,
+ 0x3b,0x1C,
+ 0x3c,0x28,
+ 0x3d,0x31,
+ 0x3e,0x44,
+ 0x3f,0x57,
+ 0x40,0x6C,
+ 0x41,0x81,
+ 0x42,0x94,
+ 0x43,0xA7,
+ 0x44,0xB8,
+ 0x45,0xD6,
+ 0x46,0xEE,
+ 0x47,0x0d,
+ 0xfe,0x00,// set page0
+
+ //-----------Update the registers 2010/07/06-----------
+ //Registers of Page0
+ 0xfe,0x00,// set page0
+ 0x10,0x26,
+ 0x11,0x0d, // fd},{modified by mormo 2010/07/06
+
+ 0x1a,0x2a, // 1e},{modified by mormo 2010/07/06
+
+
+ 0x1c,0x49,// c1},{modified by mormo 2010/07/06
+ 0x1d,0x9a,// 08},{modified by mormo 2010/07/06
+
+
+ 0x1e,0x61,// 60},{modified by mormo 2010/07/06
+
+ 0x3a,0x20,
+
+ 0x50,0x14, // 10},
+
+
+ 0x53,0x80,
+ 0x56,0x80,
+
+ 0x8b,0x20,//LSC
+ 0x8c,0x20,
+ 0x8d,0x20,
+ 0x8e,0x14,
+ 0x8f,0x10,
+ 0x90,0x14,
+
+ 0x94,0x02,
+ 0x95,0x07,
+ 0x96,0xe0,
+
+ 0xb1,0x40,// YCPT
+ 0xb2,0x40,
+ 0xb3,0x40,
+ 0xb6,0xe0,
+
+ 0xd0,0xcb,// AECT c9
+
+
+ 0xd3,0x48,// 80,
+
+
+
+ 0xf2,0x02,
+ 0xf7,0x12,
+ 0xf8,0x0a,
+
+ //Registers of Page1
+ 0xfe,0x01,// set page1
+ 0x02,0x20,
+ 0x04,0x10,
+ 0x05,0x08,
+ 0x06,0x20,
+ 0x08,0x0a,
+
+ 0x0e,0x44,
+ 0x0f,0x32,
+ 0x10,0x41,
+ 0x11,0x37,
+ 0x12,0x22,
+ 0x13,0x19,
+ 0x14,0x44,
+ 0x15,0x44,
+
+ 0x19,0x50,
+ 0x1a,0xd8,
+
+ 0x32,0x10,
+
+ 0x35,0x00,
+ 0x36,0x80,
+ 0x37,0x00,
+ //-----------Update the registers end---------//
+ 0xfe,0x00,// set page0
+ 0xd2,0x90,
+};
+
+uint8_t gc0308_default_regs_exit[]={
+ 0x28,0x00,
+};
+
+#endif
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0309/gc0309-zyhb0309.h b/drivers/media/video/wmt_v4l2/sensors/gc0309/gc0309-zyhb0309.h
new file mode 100755
index 00000000..2c63765e
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0309/gc0309-zyhb0309.h
@@ -0,0 +1,725 @@
+#ifndef GC0309_H
+#define GC0309_H
+
+// Scene Mode
+uint8_t gc0309_scene_mode_auto[] = {
+};
+
+uint8_t gc0309_scene_mode_night[] = {
+};
+
+
+// White Balance
+uint8_t gc0309_wb_auto [] = {
+ 0x5a, 0x4c ,
+ 0x5b, 0x40 ,
+ 0x5c, 0x4a ,
+ 0x22, 0x57 ,
+};
+
+uint8_t gc0309_wb_incandescent [] = {
+ //bai re guang
+ 0x22,0x55,
+ 0x5a,0x48,
+ 0x5b,0x40,
+ 0x5c,0x5c,
+};
+
+uint8_t gc0309_wb_fluorescent [] = {
+ //ri guang deng
+ 0x22,0x55,
+ 0x5a,0x40,
+ 0x5b,0x42,
+ 0x5c,0x50,
+};
+
+uint8_t gc0309_wb_daylight [] = {
+ 0x22, 0x55 , // Disable AWB
+ 0x5a, 0x50 ,//74
+ 0x5b, 0x45 ,//52
+ 0x5c, 0x40 ,
+
+};
+
+uint8_t gc0309_wb_cloudy [] = {
+ 0x22, 0x55 , // Disable AWB
+ 0x5a, 0x5a ,//8c
+ 0x5b, 0x42 ,//50
+ 0x5c, 0x40 ,
+
+};
+
+uint8_t gc0309_wb_tungsten [] = {
+ 0x22, 0x55 , // Disable AWB
+ 0x5a, 0x48 ,
+ 0x5b, 0x40 ,
+ 0x5c, 0x5c ,
+
+};
+
+
+// Exposure
+uint8_t gc0309_exposure_neg6[] = {
+ 0xd3, 0x38 ,
+
+};
+
+uint8_t gc0309_exposure_neg3[] = {
+ 0xd3, 0x40 ,
+};
+
+uint8_t gc0309_exposure_zero[] = {
+ 0xd3, 0x4a ,//50 cyrille
+};
+
+uint8_t gc0309_exposure_pos3[] = {
+ 0xd3, 0x58 ,
+};
+
+uint8_t gc0309_exposure_pos6[] = {
+ 0xd3, 0x60 ,
+};
+
+
+// Color Effect
+uint8_t gc0309_colorfx_none[] = {
+ 0x23,0x00 ,
+ 0x2d,0x0a ,
+ 0x20,0x7f ,
+ 0xd2,0x90 ,
+ 0x73,0x00 ,
+ 0x77,0x54 ,
+ 0xb3,0x38 , // 40 cyrille
+ 0xb4,0x80 ,
+ 0xba,0x00 ,
+ 0xbb,0x00 ,
+};
+
+uint8_t gc0309_colorfx_bw[] = {
+ 0x23,0x02 ,
+ 0x2d,0x0a ,
+ 0x20,0x7f ,
+ 0xd2,0x90 ,
+ 0x73,0x00 ,
+ 0xb3,0x38 ,// 40 cyrille
+ 0xb4,0x80 ,
+ 0xba,0x00 ,
+ 0xbb,0x00 ,
+
+};
+
+uint8_t gc0309_colorfx_sepia[] = {
+ 0x23,0x02 ,
+ 0x2d,0x0a ,
+ 0x20,0x7f ,
+ 0xd2,0x90 ,
+ 0x73,0x00 ,
+ 0xb3,0x38 ,
+ 0xb4,0x80 ,
+ 0xba,0xd0 ,
+ 0xbb,0x28 ,
+
+};
+
+uint8_t gc0309_colorfx_negative[] = {
+ 0x23,0x01 ,
+ 0x2d,0x0a ,
+ 0x20,0x7f ,
+ 0xd2,0x90 ,
+ 0x73,0x00 ,
+ 0xb3,0x38 ,
+ 0xb4,0x80 ,
+ 0xba,0x00 ,
+ 0xbb,0x00 ,
+
+};
+
+uint8_t gc0309_colorfx_emboss[] = {
+
+};
+
+uint8_t gc0309_colorfx_sketch[] = {
+};
+
+uint8_t gc0309_colorfx_sky_blue[] = {
+ 0x23,0x02 ,
+ 0x2d,0x0a ,
+ 0x20,0x7f ,
+ 0xd2,0x90 ,
+ 0x73,0x00 ,
+ 0xb3,0x38 ,
+ 0xb4,0x80 ,
+ 0xba,0x50 ,
+ 0xbb,0xe0 ,
+};
+
+uint8_t gc0309_colorfx_grass_green[] = {
+// Greenish
+ 0x23,0x02 ,
+ 0x2d,0x0a ,
+ 0x20,0x7f ,
+ 0xd2,0x90 ,
+ 0x77,0x88 ,
+ 0xb3,0x38 ,
+ 0xb4,0x80 ,
+ 0xba,0xc0 ,
+ 0xbb,0xc0 ,
+};
+
+uint8_t gc0309_colorfx_skin_whiten[] = {
+};
+
+uint8_t gc0309_colorfx_vivid[] = {
+};
+
+// Brightness
+uint8_t gc0309_brightness_neg4[] = {
+};
+
+uint8_t gc0309_brightness_neg3[] = {
+};
+
+uint8_t gc0309_brightness_neg2[] = {
+};
+
+uint8_t gc0309_brightness_neg1[] = {
+};
+
+uint8_t gc0309_brightness_zero[] = {
+};
+
+uint8_t gc0309_brightness_pos1[] = {
+};
+
+uint8_t gc0309_brightness_pos2[] = {
+};
+
+uint8_t gc0309_brightness_pos3[] = {
+};
+
+uint8_t gc0309_brightness_pos4[] = {
+};
+
+// Contrast
+uint8_t gc0309_contrast_neg4[] = {
+};
+
+uint8_t gc0309_contrast_neg3[] = {
+};
+
+uint8_t gc0309_contrast_neg2[] = {
+};
+
+uint8_t gc0309_contrast_neg1[] = {
+};
+
+uint8_t gc0309_contrast_zero[] = {
+};
+
+uint8_t gc0309_contrast_pos1[] = {
+};
+
+uint8_t gc0309_contrast_pos2[] = {
+};
+
+uint8_t gc0309_contrast_pos3[] = {
+};
+
+uint8_t gc0309_contrast_pos4[] = {
+};
+
+// Saturation
+uint8_t gc0309_saturation_neg4[] = {
+};
+
+uint8_t gc0309_saturation_neg3[] = {
+};
+
+uint8_t gc0309_saturation_neg2[] = {
+};
+
+uint8_t gc0309_saturation_neg1[] = {
+};
+
+uint8_t gc0309_saturation_zero[] = {
+};
+
+uint8_t gc0309_saturation_pos1[] = {
+};
+
+uint8_t gc0309_saturation_pos2[] = {
+};
+
+uint8_t gc0309_saturation_pos3[] = {
+};
+
+uint8_t gc0309_saturation_pos4[] = {
+};
+
+
+// Resolution
+
+// QCIF
+uint8_t gc0309_176x144[]={
+};
+
+uint8_t gc0309_320x240[]={
+0xfe,0x01,
+0x54,0x22, // 1/2 subsample
+0x55,0x03,
+0x56,0x00,
+0x57,0x00,
+0x58,0x00,
+0x59,0x00,
+
+0xfe,0x00,
+0x46,0x80,//enable crop window mode
+0x47,0x00,
+0x48,0x00,
+0x49,0x00,
+0x4a,0xf0,//240
+0x4b,0x01,
+0x4c,0x40, //320
+0xfe,0x00,
+
+
+};
+
+// CIF
+uint8_t gc0309_352x288[]={
+};
+
+// VGA
+uint8_t gc0309_640x480[]={
+};
+
+
+// Initiliztion
+uint8_t gc0309_default_regs_init[] = {
+
+0xfe,0x80,
+
+0xfe,0x00,
+
+0x1a,0x16,
+
+0xd2,0x10,
+
+0x22,0x55,
+
+0x5a,0x56,
+
+0x5b,0x40,
+
+0x5c,0x4a,
+
+0x22,0x57,
+
+0x01,0xfa, // 32 cyrille
+0x02,0x70,
+0x0f,0x01,
+
+0xe2,0x00,
+0xe3,0x64, //78 cyrille
+
+0x03,0x01,
+0x04,0x2c,
+
+0xe4,0x02,
+0xe5,0x58,
+
+0xe6,0x03,
+0xe7,0xe8,//48 cyrille
+
+0xe8,0x05,
+0xe9,0x14,//a0 cyrille
+
+0xea,0x06,
+0xeb,0x40,
+
+0xec,0x20,
+
+0x05,0x00,
+
+0x06,0x00,
+
+0x07,0x00,
+
+0x08,0x00,
+
+0x09,0x01,
+
+0x0a,0xe8,
+
+0x0b,0x02,
+
+0x0c,0x88,
+
+0x0d,0x02,
+
+0x0e,0x02,
+
+0x10,0x22,
+
+0x11,0x0d,
+
+0x12,0x2a,
+
+0x13,0x00,
+
+0x14,0x10,
+
+0x15,0x0a,
+
+0x16,0x05,
+
+0x17,0x01,
+
+0x1b,0x03,
+
+0x1c,0xc1,
+
+0x1d,0x08,
+
+0x1e,0x20,
+
+0x1f,0x16,
+
+0x20,0xff,
+
+0x21,0xf8,
+
+0x24,0xa2,
+
+0x25,0x0f,
+
+0x26,0x02,
+
+0x2f,0x01,
+
+0x30,0xf7,
+
+0x31,0x40,
+
+0x32,0x00,
+
+0x39,0x04,
+
+0x3a,0x20,
+
+0x3b,0x20,
+
+0x3c,0x02,
+
+0x3d,0x02,
+
+0x3e,0x02,
+
+0x3f,0x02,
+
+0x50,0x24,
+
+0x53,0x82,
+
+0x54,0x80,
+
+0x55,0x80,
+
+0x56,0x82,
+
+0x8b,0x20,
+
+0x8c,0x20,
+
+0x8d,0x20,
+
+0x8e,0x10,
+
+0x8f,0x10,
+
+0x90,0x10,
+
+0x91,0x3c,
+
+0x92,0x50,
+
+0x5d,0x12,
+
+0x5e,0x1a,
+
+0x5f,0x24,
+
+0x60,0x07,
+
+0x61,0x0e,
+
+0x62,0x0c,
+
+0x64,0x03,
+
+0x66,0xe8,
+
+0x67,0x86,
+
+0x68,0xa2,
+
+0x69,0x20,
+
+0x6a,0x0f,
+
+0x6b,0x00,
+
+0x6c,0x53,
+
+0x6d,0x83,
+
+0x6e,0xac,
+
+0x6f,0xac,
+
+0x70,0x15,
+
+0x71,0x33,
+
+0x72,0xdc,
+
+0x73,0x80,
+
+0x74,0x02,
+
+0x75,0x3f,
+
+0x76,0x02,
+
+0x77,0x34, // 54 cyrille
+
+0x78,0x88,
+
+0x79,0x81,
+
+0x7a,0x81,
+
+0x7b,0x22,
+
+0x7c,0xff,
+
+0x93,0x45,
+
+0x94,0x00,
+
+0x95,0x00,
+
+0x96,0x00,
+
+0x97,0x45,
+
+0x98,0xf0,
+
+0x9c,0x00,
+
+0x9d,0x03,
+
+0x9e,0x00,
+
+0xb1,0x40,
+
+0xb2,0x40,
+
+0xb3,0x3b, // cyrille
+
+0xb5,0xfa,// cyrille
+
+0xb8,0x20,
+
+0xbe,0x36,
+
+0xbf,0x00,
+
+0xd0,0xcb,
+
+0xd1,0x10,
+
+0xd3,0x4a, //50 cyrille
+
+0xd5,0xf2,
+
+0xd6,0x16,
+
+0xdb,0x92,
+
+0xdc,0xa5,
+
+0xdf,0x23,
+
+0xd9,0x00,
+
+0xda,0x00,
+
+0xe0,0x09,
+
+0xec,0x20,
+
+0xed,0x04,
+
+0xee,0xa0,
+
+0xef,0x40,
+
+
+
+0x9F, 0x10, //case 3:
+0xA0, 0x20,
+0xA1, 0x38,
+0xA2, 0x4E,
+0xA3, 0x63,
+0xA4, 0x76,
+0xA5, 0x87,
+0xA6, 0xA2,
+0xA7, 0xB8,
+0xA8, 0xCA,
+0xA9, 0xD8,
+0xAA, 0xE3,
+0xAB, 0xEB,
+0xAC, 0xF0,
+0xAD, 0xF8,
+0xAE, 0xFD,
+0xAF, 0xFF,
+
+0xc0,0x00,
+
+0xc1,0x0B,
+
+0xc2,0x15,
+
+0xc3,0x27,
+
+0xc4,0x39,
+
+0xc5,0x49,
+
+0xc6,0x5A,
+
+0xc7,0x6A,
+
+0xc8,0x89,
+
+0xc9,0xA8,
+
+0xca,0xC6,
+
+0xcb,0xE3,
+
+0xcc,0xFF,
+
+0xf0,0x02,
+
+0xf1,0x01,
+
+0xf2,0x00,
+
+0xf3,0x30,
+
+0xf7,0x04,
+
+0xf8,0x02,
+
+0xf9,0x9f,
+
+0xfa,0x78,
+
+0xfe,0x01,
+
+0x00,0xf5,
+
+0x02,0x1a,
+
+0x0a,0xa0,
+
+0x0b,0x60,
+
+0x0c,0x08,
+
+0x0e,0x4c,
+
+0x0f,0x39,
+
+0x11,0x3f,
+
+0x12,0x72,
+
+0x13,0x13,
+
+0x14,0x42,
+
+0x15,0x43,
+
+0x16,0xc2,
+
+0x17,0xa8,
+
+0x18,0x18,
+
+0x19,0x40,
+
+0x1a,0xd0,
+
+0x1b,0xf5,
+
+0x70,0x40,
+
+0x71,0x58,
+
+0x72,0x30,
+
+0x73,0x48,
+
+0x74,0x20,
+
+0x75,0x60,
+
+0xfe,0x00,
+
+0xd2,0x90,
+
+0x8b,0x22,
+
+0x71,0x43,
+
+0x93,0x48,
+
+0x94,0x00,
+
+0x95,0x05,
+
+0x96,0xe8,
+
+0x97,0x40,
+
+0x98,0xf8,
+
+0x9c,0x00,
+
+0x9d,0x00,
+
+0x9e,0x00,
+
+0xd0,0xcb,
+
+0xd3,0x4a, // a0 //50 cyrille
+
+0x31,0x60,
+
+0x1c,0x49,
+
+0x1d,0x98,
+
+0x10,0x26,
+
+0x1a,0x26,
+
+};
+
+uint8_t gc0309_default_regs_exit[] = {
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0309/gc0309.c b/drivers/media/video/wmt_v4l2/sensors/gc0309/gc0309.c
new file mode 100755
index 00000000..488a677f
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0309/gc0309.c
@@ -0,0 +1,346 @@
+
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "gc0309.h"
+
+#define sensor_write_array(sd, arr, sz) \
+ cmos_init_8bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write(sd->i2c_addr, reg, val)
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint8_t *regs;
+ size_t size;
+};
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+#define FILP_REG_INIT_VALUE 0x10
+#define DELAY_INTERVAL 50
+#define RETRY_TIMES 10
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+// CMOS_WIN_SIZE("QVGA", 320, 240, gc0309_320x240),
+ CMOS_WIN_SIZE("VGA", 640, 480, gc0309_640x480),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = gc0309_wb_auto;
+ size = ARRAY_SIZE(gc0309_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = gc0309_wb_incandescent;
+ size = ARRAY_SIZE(gc0309_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = gc0309_wb_daylight;
+ size = ARRAY_SIZE(gc0309_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = gc0309_wb_cloudy;
+ size = ARRAY_SIZE(gc0309_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = gc0309_wb_fluorescent;
+ size = ARRAY_SIZE(gc0309_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_scenemode(struct cmos_subdev *sd, enum v4l2_scene_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case SCENE_MODE_AUTO:
+ regs = gc0309_scene_mode_auto;
+ size = ARRAY_SIZE(gc0309_scene_mode_auto);
+ break;
+ case SCENE_MODE_NIGHTSHOT:
+ regs = gc0309_scene_mode_night;
+ size = ARRAY_SIZE(gc0309_scene_mode_night);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_exposure(struct cmos_subdev *sd, int value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case -2:
+ regs = gc0309_exposure_neg6;
+ size = ARRAY_SIZE(gc0309_exposure_neg6);
+ break;
+ case -1:
+ regs = gc0309_exposure_neg3;
+ size = ARRAY_SIZE(gc0309_exposure_neg3);
+ break;
+ case 0:
+ regs = gc0309_exposure_zero;
+ size = ARRAY_SIZE(gc0309_exposure_zero);
+ break;
+ case 1:
+ regs = gc0309_exposure_pos3;
+ size = ARRAY_SIZE(gc0309_exposure_pos3);
+ break;
+ case 2:
+ regs = gc0309_exposure_pos6;
+ size = ARRAY_SIZE(gc0309_exposure_pos6);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data,tmp=0;
+ uint8_t retry_times=0;
+
+ sensor_write(sd, 0xfe, 0x00); // set page0
+
+ data = sensor_read(sd, 0x14);
+
+ switch (value) {
+ case 0:
+ data &= ~0x01;
+ break;
+ case 1:
+ data |= 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tmp=data | FILP_REG_INIT_VALUE;
+ for(retry_times=0;retry_times<RETRY_TIMES;retry_times++){
+ sensor_write(sd, 0x14, tmp);
+ msleep(50+retry_times*DELAY_INTERVAL);
+ if(sensor_read(sd, 0x14)==tmp)
+ break;
+ }
+
+ return 0;
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data,tmp=0;
+ uint8_t retry_times=0;
+
+ sensor_write(sd, 0xfe, 0x00); // set page0
+
+ data = sensor_read(sd, 0x14);
+
+ switch (value) {
+ case 0:
+ data &= ~0x02;
+ break;
+ case 1:
+ data |= 0x02;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tmp=data | FILP_REG_INIT_VALUE;
+ for(retry_times=0;retry_times<RETRY_TIMES;retry_times++){
+ sensor_write(sd, 0x14, tmp);
+ msleep(50+retry_times*DELAY_INTERVAL);
+ if(sensor_read(sd, 0x14)==tmp)
+ break;
+ }
+
+ return 0;
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ case V4L2_CID_EXPOSURE:
+ return v4l2_ctrl_query_fill(qc, -2, 2, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return sensor_s_scenemode(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return sensor_s_exposure(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ default:
+ case WMT_V4L2_CID_CAMERA_ANTIBANDING:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win) {
+ pr_err("%s, s_mbus_fmt failed, width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, win->regs, win->size);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return 0;
+}
+
+static int sensor_enum_framesizes(struct cmos_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ const struct cmos_win_size *win = &cmos_supported_win_sizes[index];
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ return (sensor_read(sd, 0) == sd->id) ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ if (sensor_identify(sd)) {
+ return -1;
+ }
+
+ sensor_write_array(sd, gc0309_default_regs_init,
+ ARRAY_SIZE(gc0309_default_regs_init));
+ mdelay(200);
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ sensor_write_array(sd, gc0309_default_regs_exit,
+ ARRAY_SIZE(gc0309_default_regs_exit));
+ return 0;
+}
+
+static struct cmos_subdev_ops gc0309_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+ .enum_framesizes = sensor_enum_framesizes,
+};
+
+struct cmos_subdev gc0309 = {
+ .name = "gc0309",
+ .i2c_addr = 0x21,
+ .id = 0xa0,
+ .max_width = 640,
+ .max_height = 480,
+ .ops = &gc0309_ops,
+};
+
+#if 0
+static int __init gc0309_init(void)
+{
+ return cmos_register_sudbdev(&gc0309);
+}
+
+static void __exit gc0309_exit(void)
+{
+ return cmos_unregister_subdev(&gc0309);
+}
+
+module_init(gc0309_init);
+module_exit(gc0309_exit);
+
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0309/gc0309.h b/drivers/media/video/wmt_v4l2/sensors/gc0309/gc0309.h
new file mode 100755
index 00000000..51a2e505
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0309/gc0309.h
@@ -0,0 +1,737 @@
+#ifndef GC0309_H
+#define GC0309_H
+
+// Scene Mode
+uint8_t gc0309_scene_mode_auto[] = {
+};
+
+uint8_t gc0309_scene_mode_night[] = {
+};
+
+
+// White Balance
+uint8_t gc0309_wb_auto [] = {
+ 0x5a, 0x4c ,
+ 0x5b, 0x40 ,
+ 0x5c, 0x4a ,
+ 0x22, 0x57 ,
+};
+
+uint8_t gc0309_wb_incandescent [] = {
+ //bai re guang
+ 0x22,0x55,
+ 0x5a,0x48,
+ 0x5b,0x40,
+ 0x5c,0x5c,
+};
+
+uint8_t gc0309_wb_fluorescent [] = {
+ //ri guang deng
+ 0x22,0x55,
+ 0x5a,0x40,
+ 0x5b,0x42,
+ 0x5c,0x50,
+};
+
+uint8_t gc0309_wb_daylight [] = {
+ 0x22, 0x55 , // Disable AWB
+ 0x5a, 0x50 ,//74
+ 0x5b, 0x45 ,//52
+ 0x5c, 0x40 ,
+
+};
+
+uint8_t gc0309_wb_cloudy [] = {
+ 0x22, 0x55 , // Disable AWB
+ 0x5a, 0x5a ,//8c
+ 0x5b, 0x42 ,//50
+ 0x5c, 0x40 ,
+
+};
+
+uint8_t gc0309_wb_tungsten [] = {
+ 0x22, 0x55 , // Disable AWB
+ 0x5a, 0x48 ,
+ 0x5b, 0x40 ,
+ 0x5c, 0x5c ,
+
+};
+
+
+// Exposure
+uint8_t gc0309_exposure_neg6[] = {
+ 0xf0,0x00,
+ 0xb5,0xc0,
+ 0xd3,0x28
+};
+
+uint8_t gc0309_exposure_neg3[] = {
+ 0xf0,0x00,
+ 0xb5,0xe0,
+ 0xd3,0x38
+};
+
+uint8_t gc0309_exposure_zero[] = {
+ 0xf0,0x00,
+ 0xb5,0x00,
+ 0xd3,0x48
+};
+
+uint8_t gc0309_exposure_pos3[] = {
+ 0xf0,0x00,
+ 0xb5,0x20,
+ 0xd3,0x58
+};
+
+uint8_t gc0309_exposure_pos6[] = {
+ 0xf0,0x00,
+ 0xb5,0x40,
+ 0xd3,0x68
+};
+
+
+// Color Effect
+uint8_t gc0309_colorfx_none[] = {
+ 0x23,0x00 ,
+ 0x2d,0x0a ,
+ 0x20,0x7f ,
+ 0xd2,0x90 ,
+ 0x73,0x00 ,
+ 0x77,0x45 ,
+ 0xb3,0x42 ,
+ 0xb4,0x80 ,
+ 0xba,0x00 ,
+ 0xbb,0x00 ,
+};
+
+uint8_t gc0309_colorfx_bw[] = {
+ 0x23,0x02 ,
+ 0x2d,0x0a ,
+ 0x20,0x7f ,
+ 0xd2,0x90 ,
+ 0x73,0x00 ,
+ 0xb3,0x42 ,
+ 0xb4,0x80 ,
+ 0xba,0x00 ,
+ 0xbb,0x00 ,
+
+};
+
+uint8_t gc0309_colorfx_sepia[] = {
+ 0x23,0x02 ,
+ 0x2d,0x0a ,
+ 0x20,0x7f ,
+ 0xd2,0x90 ,
+ 0x73,0x00 ,
+ 0xb3,0x42 ,
+ 0xb4,0x80 ,
+ 0xba,0xd0 ,
+ 0xbb,0x28 ,
+
+};
+
+uint8_t gc0309_colorfx_negative[] = {
+ 0x23,0x01 ,
+ 0x2d,0x0a ,
+ 0x20,0x7f ,
+ 0xd2,0x90 ,
+ 0x73,0x00 ,
+ 0xb3,0x42 ,
+ 0xb4,0x80 ,
+ 0xba,0x00 ,
+ 0xbb,0x00 ,
+
+};
+
+uint8_t gc0309_colorfx_emboss[] = {
+
+};
+
+uint8_t gc0309_colorfx_sketch[] = {
+};
+
+uint8_t gc0309_colorfx_sky_blue[] = {
+ 0x23,0x02 ,
+ 0x2d,0x0a ,
+ 0x20,0x7f ,
+ 0xd2,0x90 ,
+ 0x73,0x00 ,
+ 0xb3,0x42 ,
+ 0xb4,0x80 ,
+ 0xba,0x50 ,
+ 0xbb,0xe0 ,
+};
+
+uint8_t gc0309_colorfx_grass_green[] = {
+// Greenish
+ 0x23,0x02 ,
+ 0x2d,0x0a ,
+ 0x20,0x7f ,
+ 0xd2,0x90 ,
+ 0x77,0x88 ,
+ 0xb3,0x42 ,
+ 0xb4,0x80 ,
+ 0xba,0xc0 ,
+ 0xbb,0xc0 ,
+};
+
+uint8_t gc0309_colorfx_skin_whiten[] = {
+};
+
+uint8_t gc0309_colorfx_vivid[] = {
+};
+
+// Brightness
+uint8_t gc0309_brightness_neg4[] = {
+};
+
+uint8_t gc0309_brightness_neg3[] = {
+};
+
+uint8_t gc0309_brightness_neg2[] = {
+};
+
+uint8_t gc0309_brightness_neg1[] = {
+};
+
+uint8_t gc0309_brightness_zero[] = {
+};
+
+uint8_t gc0309_brightness_pos1[] = {
+};
+
+uint8_t gc0309_brightness_pos2[] = {
+};
+
+uint8_t gc0309_brightness_pos3[] = {
+};
+
+uint8_t gc0309_brightness_pos4[] = {
+};
+
+// Contrast
+uint8_t gc0309_contrast_neg4[] = {
+};
+
+uint8_t gc0309_contrast_neg3[] = {
+};
+
+uint8_t gc0309_contrast_neg2[] = {
+};
+
+uint8_t gc0309_contrast_neg1[] = {
+};
+
+uint8_t gc0309_contrast_zero[] = {
+};
+
+uint8_t gc0309_contrast_pos1[] = {
+};
+
+uint8_t gc0309_contrast_pos2[] = {
+};
+
+uint8_t gc0309_contrast_pos3[] = {
+};
+
+uint8_t gc0309_contrast_pos4[] = {
+};
+
+// Saturation
+uint8_t gc0309_saturation_neg4[] = {
+};
+
+uint8_t gc0309_saturation_neg3[] = {
+};
+
+uint8_t gc0309_saturation_neg2[] = {
+};
+
+uint8_t gc0309_saturation_neg1[] = {
+};
+
+uint8_t gc0309_saturation_zero[] = {
+};
+
+uint8_t gc0309_saturation_pos1[] = {
+};
+
+uint8_t gc0309_saturation_pos2[] = {
+};
+
+uint8_t gc0309_saturation_pos3[] = {
+};
+
+uint8_t gc0309_saturation_pos4[] = {
+};
+
+
+// Resolution
+
+// QCIF
+uint8_t gc0309_176x144[]={
+};
+
+uint8_t gc0309_320x240[]={
+0xfe,0x01,
+0x54,0x22, // 1/2 subsample
+0x55,0x03,
+0x56,0x00,
+0x57,0x00,
+0x58,0x00,
+0x59,0x00,
+
+0xfe,0x00,
+0x46,0x80,//enable crop window mode
+0x47,0x00,
+0x48,0x00,
+0x49,0x00,
+0x4a,0xf0,//240
+0x4b,0x01,
+0x4c,0x40, //320
+0xfe,0x00,
+
+
+};
+
+// CIF
+uint8_t gc0309_352x288[]={
+};
+
+// VGA
+uint8_t gc0309_640x480[]={
+};
+
+
+// Initiliztion
+uint8_t gc0309_default_regs_init[] = {
+
+0xfe,0x80,
+
+0xfe,0x00,
+
+0x1a,0x16,
+
+0xd2,0x10,
+
+0x22,0x55,
+
+0x5a,0x56,
+
+0x5b,0x40,
+
+0x5c,0x4a,
+
+0x22,0x57,
+
+0x01,0x32,
+0x02,0x70,
+0x0f,0x01,
+
+0xe2,0x00,
+0xe3,0x78,
+
+0x03,0x01,
+0x04,0x2c,
+
+0xe4,0x02,
+0xe5,0x58,
+
+0xe6,0x03,
+0xe7,0x48,
+
+0xe8,0x05,
+0xe9,0xa0,
+
+0xea,0x05,
+0xeb,0xa0,
+
+0x05,0x00,
+
+0x06,0x00,
+
+0x07,0x00,
+
+0x08,0x00,
+
+0x09,0x01,
+
+0x0a,0xe8,
+
+0x0b,0x02,
+
+0x0c,0x88,
+
+0x0d,0x02,
+
+0x0e,0x02,
+
+0x10,0x22,
+
+0x11,0x0d,
+
+0x12,0x2a,
+
+0x13,0x00,
+
+0x14,0x10,
+
+0x15,0x0a,
+
+0x16,0x05,
+
+0x17,0x01,
+
+0x1b,0x03,
+
+0x1c,0xc1,
+
+0x1d,0x08,
+
+0x1e,0x20,
+
+0x1f,0x16,
+
+0x20,0xff,
+
+0x21,0xf8,
+
+0x24,0xa2,
+
+0x25,0x0f,
+
+0x26,0x02,
+
+0x2f,0x01,
+
+0x30,0xf7,
+
+0x31,0x40,
+
+0x32,0x00,
+
+0x39,0x04,
+
+0x3a,0x20,
+
+0x3b,0x20,
+
+0x3c,0x02,
+
+0x3d,0x02,
+
+0x3e,0x02,
+
+0x3f,0x02,
+
+0x50,0x24,
+
+0x53,0x82,
+
+0x54,0x80,
+
+0x55,0x80,
+
+0x56,0x82,
+
+0x8b,0x20,
+
+0x8c,0x20,
+
+0x8d,0x20,
+
+0x8e,0x10,
+
+0x8f,0x10,
+
+0x90,0x10,
+
+0x91,0x3c,
+
+0x92,0x50,
+
+0x5d,0x12,
+
+0x5e,0x1a,
+
+0x5f,0x24,
+
+0x60,0x07,
+
+0x61,0x0e,
+
+0x62,0x1f, //0c
+
+0x64,0x01, //03
+
+0x66,0xe8,
+
+0x67,0x86,
+
+0x68,0xa2,
+
+0x69,0x20,
+
+0x6a,0x0f,
+
+0x6b,0x00,
+
+0x6c,0x53,
+
+0x6d,0x83,
+
+0x6e,0xac,
+
+0x6f,0xac,
+
+0x70,0x15,
+
+0x71,0x33,
+
+0x72,0xdc,
+
+0x73,0x80,
+
+0x74,0x02,
+
+0x75,0x3f,
+
+0x76,0x02,
+
+0x77,0x45,
+
+0x78,0x88,
+
+0x79,0x81,
+
+0x7a,0x81,
+
+0x7b,0x22,
+
+0x7c,0xff,
+
+0x93,0x45,
+
+0x94,0x00,
+
+0x95,0x00,
+
+0x96,0x00,
+
+0x97,0x45,
+
+0x98,0xf0,
+
+0x9c,0x00,
+
+0x9d,0x03,
+
+0x9e,0x00,
+
+0xb1,0x38,
+
+0xb2,0x38,
+
+0xb3,0x42,
+
+0xb8,0x20,
+
+0xbe,0x36,
+
+0xbf,0x00,
+
+0xd0,0xcb,
+
+0xd1,0x10,
+
+0xd3,0x50,
+
+0xd5,0xf2,
+
+0xd6,0x16,
+
+0xdb,0x92,
+
+0xdc,0xa5,
+
+0xdf,0x23,
+
+0xd9,0x00,
+
+0xda,0x00,
+
+0xe0,0x09,
+
+0xec,0x20,
+
+0xed,0x04,
+
+0xee,0xa0,
+
+0xef,0x40,
+
+
+0x9F, 0x0E,//gamma 2
+0xA0, 0x1C,
+0xA1, 0x34,
+0xA2, 0x48,
+0xA3, 0x5A,
+0xA4, 0x6B,
+0xA5, 0x7B,
+0xA6, 0x95,
+0xA7, 0xAB,
+0xA8, 0xBF,
+0xA9, 0xCE,
+0xAA, 0xD9,
+0xAB, 0xE4,
+0xAC, 0xEC,
+0xAD, 0xF7,
+0xAE, 0xFD,
+0xAF, 0xFF,
+
+
+
+0xc0,0x00,
+
+0xc1,0x0B,
+
+0xc2,0x15,
+
+0xc3,0x27,
+
+0xc4,0x39,
+
+0xc5,0x49,
+
+0xc6,0x5A,
+
+0xc7,0x6A,
+
+0xc8,0x89,
+
+0xc9,0xA8,
+
+0xca,0xC6,
+
+0xcb,0xE3,
+
+0xcc,0xFF,
+
+0xf0,0x02,
+
+0xf1,0x01,
+
+0xf2,0x00,
+
+0xf3,0x30,
+
+0xf7,0x04,
+
+0xf8,0x02,
+
+0xf9,0x9f,
+
+0xfa,0x78,
+
+0xfe,0x01,
+
+0x00,0xf5,
+
+0x02,0x1a,
+
+0x0a,0xa0,
+
+0x0b,0x60,
+
+0x0c,0x08,
+
+0x0e,0x4c,
+
+0x0f,0x39,
+
+0x11,0x3f,
+
+0x12,0x72,
+
+0x13,0x13,
+
+0x14,0x42,
+
+0x15,0x43,
+
+0x16,0xc2,
+
+0x17,0xa8,
+
+0x18,0x18,
+
+0x19,0x40,
+
+0x1a,0xd0,
+
+0x1b,0xf5,
+
+0x70,0x40,
+
+0x71,0x58,
+
+0x72,0x30,
+
+0x73,0x48,
+
+0x74,0x20,
+
+0x75,0x60,
+
+0xfe,0x00,
+
+0xd2,0x90,
+
+0x8b,0x22,
+
+0x71,0x43,
+
+0x93,0x48,
+
+0x94,0x00,
+
+0x95,0x05,
+
+0x96,0xe8,
+
+0x97,0x40,
+
+0x98,0xf8,
+
+0x9c,0x00,
+
+0x9d,0x00,
+
+0x9e,0x00,
+
+0xd0,0xcb,
+
+0xd3,0x50, // a0
+
+0x31,0x60,
+
+0x1c,0x49,
+
+0x1d,0x98,
+
+0x10,0x26,
+
+0x1a,0x26,
+
+0xfe,0x00,
+0x57,0x7b,
+0x58,0x80,
+0x59,0x80,
+
+
+};
+
+uint8_t gc0309_default_regs_exit[] = {
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0311/gc0311.c b/drivers/media/video/wmt_v4l2/sensors/gc0311/gc0311.c
new file mode 100755
index 00000000..c057106f
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0311/gc0311.c
@@ -0,0 +1,224 @@
+
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "gc0311.h"
+
+#define sensor_write_array(sd, arr, sz) \
+ cmos_init_8bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write(sd->i2c_addr, reg, val)
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint8_t *regs;
+ size_t size;
+};
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+ CMOS_WIN_SIZE("QVGA", 320, 240, gc0311_320x240),
+ CMOS_WIN_SIZE("VGA", 640, 480, gc0311_640x480),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+ int data;
+
+ data = sensor_read(sd, 0x14);
+
+ switch (value) {
+ case 0:
+ data &= ~0x01;
+ break;
+ case 1:
+ data |= 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sensor_write(sd, 0x14, data);
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+ int data;
+
+ data = sensor_read(sd, 0x14);
+
+ switch (value) {
+ case 0:
+ data &= ~0x02;
+ break;
+ case 1:
+ data |= 0x02;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sensor_write(sd, 0x14, data);
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = gc0311_wb_auto;
+ size = ARRAY_SIZE(gc0311_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = gc0311_wb_incandescent;
+ size = ARRAY_SIZE(gc0311_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = gc0311_wb_daylight;
+ size = ARRAY_SIZE(gc0311_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = gc0311_wb_cloudy;
+ size = ARRAY_SIZE(gc0311_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = gc0311_wb_fluorescent;
+ size = ARRAY_SIZE(gc0311_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win)
+ return -EINVAL;
+
+ sensor_write_array(sd, win->regs, win->size);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return 0;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ return (sensor_read(sd, 0) == sd->id) ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ if (!sensor_identify(sd)) {
+ return -1;
+ }
+
+ sensor_write_array(sd, gc0311_default_regs_init,
+ ARRAY_SIZE(gc0311_default_regs_init));
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ return 0;
+}
+
+static struct cmos_subdev_ops gc0311_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+};
+
+struct cmos_subdev gc0311 = {
+ .name = "gc0311",
+ .i2c_addr = 0x21,
+ .id = 0xbb,
+ .max_width = 640,
+ .max_height = 480,
+ .ops = &gc0311_ops,
+};
+
+static int __init gc0311_init(void)
+{
+ return cmos_register_sudbdev(&gc0311);
+}
+
+static void __exit gc0311_exit(void)
+{
+ return cmos_unregister_subdev(&gc0311);
+}
+
+module_init(gc0311_init);
+module_exit(gc0311_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0311/gc0311.h b/drivers/media/video/wmt_v4l2/sensors/gc0311/gc0311.h
new file mode 100755
index 00000000..1e5a3da6
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0311/gc0311.h
@@ -0,0 +1,444 @@
+#ifndef GC0311_H
+#define GC0311_H
+
+// Scene Mode
+unsigned char gc0311_scene_mode_auto[] = {
+};
+
+unsigned char gc0311_scene_mode_night[] = {
+};
+
+
+// White Balance
+unsigned char gc0311_wb_auto [] = {
+};
+
+unsigned char gc0311_wb_incandescent [] = {
+};
+
+unsigned char gc0311_wb_fluorescent [] = {
+};
+
+unsigned char gc0311_wb_daylight [] = {
+};
+
+unsigned char gc0311_wb_cloudy [] = {
+};
+
+unsigned char gc0311_wb_tungsten [] = {
+};
+
+
+// Exposure
+unsigned char gc0311_exposure_neg6[] = {
+};
+
+unsigned char gc0311_exposure_neg3[] = {
+};
+
+unsigned char gc0311_exposure_zero[] = {
+};
+
+unsigned char gc0311_exposure_pos3[] = {
+};
+
+unsigned char gc0311_exposure_pos6[] = {
+};
+
+
+// Color Effect
+unsigned char gc0311_colorfx_none[] = {
+};
+
+unsigned char gc0311_colorfx_bw[] = {
+};
+
+unsigned char gc0311_colorfx_sepia[] = {
+};
+
+unsigned char gc0311_colorfx_negative[] = {
+};
+
+unsigned char gc0311_colorfx_emboss[] = {
+};
+
+unsigned char gc0311_colorfx_sketch[] = {
+};
+
+unsigned char gc0311_colorfx_sky_blue[] = {
+};
+
+unsigned char gc0311_colorfx_grass_green[] = {
+};
+
+unsigned char gc0311_colorfx_skin_whiten[] = {
+};
+
+unsigned char gc0311_colorfx_vivid[] = {
+};
+
+// Brightness
+unsigned char gc0311_brightness_neg4[] = {
+};
+
+unsigned char gc0311_brightness_neg3[] = {
+};
+
+unsigned char gc0311_brightness_neg2[] = {
+};
+
+unsigned char gc0311_brightness_neg1[] = {
+};
+
+unsigned char gc0311_brightness_zero[] = {
+};
+
+unsigned char gc0311_brightness_pos1[] = {
+};
+
+unsigned char gc0311_brightness_pos2[] = {
+};
+
+unsigned char gc0311_brightness_pos3[] = {
+};
+
+unsigned char gc0311_brightness_pos4[] = {
+};
+
+// Contrast
+unsigned char gc0311_contrast_neg4[] = {
+};
+
+unsigned char gc0311_contrast_neg3[] = {
+};
+
+unsigned char gc0311_contrast_neg2[] = {
+};
+
+unsigned char gc0311_contrast_neg1[] = {
+};
+
+unsigned char gc0311_contrast_zero[] = {
+};
+
+unsigned char gc0311_contrast_pos1[] = {
+};
+
+unsigned char gc0311_contrast_pos2[] = {
+};
+
+unsigned char gc0311_contrast_pos3[] = {
+};
+
+unsigned char gc0311_contrast_pos4[] = {
+};
+
+// Saturation
+unsigned char gc0311_saturation_neg4[] = {
+};
+
+unsigned char gc0311_saturation_neg3[] = {
+};
+
+unsigned char gc0311_saturation_neg2[] = {
+};
+
+unsigned char gc0311_saturation_neg1[] = {
+};
+
+unsigned char gc0311_saturation_zero[] = {
+};
+
+unsigned char gc0311_saturation_pos1[] = {
+};
+
+unsigned char gc0311_saturation_pos2[] = {
+};
+
+unsigned char gc0311_saturation_pos3[] = {
+};
+
+unsigned char gc0311_saturation_pos4[] = {
+};
+
+
+// Resolution
+
+// QCIF
+unsigned char gc0311_176x144[]={
+};
+
+unsigned char gc0311_320x240[]={
+0xfe,0x00,
+0x59,0x22,
+0x5a,0x03,
+0x5b,0x00,
+0x5c,0x00,
+0x5d,0x00,
+0x5e,0x00,
+0x5f,0x00,
+0x60,0x00,
+0x61,0x00,
+0x62,0x00,
+};
+
+unsigned char gc0311_640x480[]={
+};
+
+
+// Initiliztion
+unsigned char gc0311_default_regs_init[] = {
+0xfe,0xf0,
+0xfe,0xf0,
+0xfe,0xf0,
+0x42,0x00,
+0x4f,0x00,
+0x03,0x01,
+0x04,0x20,
+0xfc,0x16,
+0xf1,0x07,
+0xf2,0x01,
+0xfc,0x16,
+0xfe,0x00,
+0x0d,0x01,
+0x0e,0xe8,
+0x0f,0x02,
+0x10,0x88,
+0x09,0x00,
+0x0a,0x00,
+0x0b,0x00,
+0x0c,0x04,
+0x05,0x02,
+0x06,0x2c,
+0x07,0x00,
+0x08,0xb8,
+0x77,0x7c,
+0x78,0x40,
+0x79,0x56,
+0xfe,0x01,
+0x29,0x00,
+0x2a,0x60,
+0x2b,0x02,
+0x2c,0xa0,
+0x2d,0x03,
+0x2e,0x00,
+0x2f,0x03,
+0x30,0xc0,
+0x31,0x07,
+0x32,0x80,
+0x33,0x20,
+0xfe,0x00,
+0x17,0x14,
+0x19,0x04,
+0x1f,0x08,
+0x20,0x01,
+0x21,0x48,
+0x1b,0x48,
+0x22,0xba,
+0x23,0x07,
+0x24,0x16,
+0x70,0x54,
+0x73,0x80,
+0x76,0x80,
+0x26,0xf7,
+0x28,0x7f,
+0x29,0x40,
+0x33,0x18,
+0x34,0x18,
+0x35,0x18,
+0x36,0x18,
+0x40,0xdf,
+0x41,0x2e,
+0x42,0xff,
+0x44,0xa2,
+0x46,0x02,
+0x4d,0x01,
+0x4f,0x01,
+0x7e,0x08,
+0x7f,0xc3,
+0x80,0xe7,
+0x82,0x30,
+0x84,0x02,
+0x89,0x22,
+0x90,0xbc,
+0x92,0x08,
+0x94,0x08,
+0x95,0x64,
+0x9a,0x15,
+0x9c,0x46,
+0xfe,0x00,
+0x63,0x00,
+0x64,0x06,
+0x65,0x0c,
+0x66,0x18,
+0x67,0x2A,
+0x68,0x3D,
+0x69,0x50,
+0x6A,0x60,
+0x6B,0x80,
+0x6C,0xA0,
+0x6D,0xC0,
+0x6E,0xE0,
+0x6F,0xFF,
+0xfe,0x00,
+0xBF,0x0E,
+0xc0,0x1C,
+0xc1,0x34,
+0xc2,0x48,
+0xc3,0x5A,
+0xc4,0x6B,
+0xc5,0x7B,
+0xc6,0x95,
+0xc7,0xAB,
+0xc8,0xBF,
+0xc9,0xCE,
+0xcA,0xD9,
+0xcB,0xE4,
+0xcC,0xEC,
+0xcD,0xF7,
+0xcE,0xFD,
+0xcF,0xFF,
+0xd1,0x36,
+0xd2,0x36,
+0xdd,0x00,
+0xed,0x00,
+0xde,0x38,
+0xe4,0x88,
+0xe5,0x40,
+0xfe,0x01,
+0x18,0x22,
+0x08,0xa4,
+0x09,0xf0,
+0xfe,0x01,
+0x10,0x08,
+0x11,0x11,
+0x12,0x14,
+0x13,0x40,
+0x16,0xd8,
+0x17,0x98,
+0x18,0x01,
+0x21,0xc0,
+0x22,0x40,
+0x06,0x10,
+0x08,0xa0,
+0x50,0xfe,
+0x51,0x05,
+0x52,0x28,
+0x53,0x05,
+0x54,0x10,
+0x55,0x20,
+0x56,0x16,
+0x57,0x10,
+0x58,0xf0,
+0x59,0x10,
+0x5a,0x10,
+0x5b,0xf0,
+0x5e,0xe8,
+0x5f,0x20,
+0x60,0x20,
+0x61,0xe0,
+0x62,0x03,
+0x63,0x30,
+0x64,0xc0,
+0x65,0xd0,
+0x66,0x20,
+0x67,0x00,
+0x6d,0x40,
+0x6e,0x08,
+0x6f,0x08,
+0x70,0x10,
+0x71,0x62,
+0x72,0x2e,
+0x73,0x71,
+0x74,0x23,
+0x75,0x40,
+0x76,0x48,
+0x77,0xc2,
+0x78,0xa5,
+0x79,0x18,
+0x7a,0x40,
+0x7b,0xb0,
+0x7c,0xf5,
+0x81,0x80,
+0x82,0x60,
+0x83,0xa0,
+0x8a,0xf8,
+0x8b,0xf4,
+0x8c,0x0a,
+0x8d,0x00,
+0x8e,0x00,
+0x8f,0x00,
+0x90,0x12,
+0xfe,0x00,
+0xad,0x00,
+0xfe,0x01,
+0xa0,0x00,
+0xa1,0x3c,
+0xa2,0x50,
+0xa3,0x00,
+0xa8,0x09,
+0xa9,0x04,
+0xaa,0x00,
+0xab,0x0c,
+0xac,0x02,
+0xad,0x00,
+0xae,0x15,
+0xaf,0x05,
+0xb0,0x00,
+0xb1,0x0f,
+0xb2,0x06,
+0xb3,0x00,
+0xb4,0x36,
+0xb5,0x2a,
+0xb6,0x25,
+0xba,0x36,
+0xbb,0x25,
+0xbc,0x22,
+0xc0,0x1e,
+0xc1,0x18,
+0xc2,0x17,
+0xc6,0x1c,
+0xc7,0x18,
+0xc8,0x17,
+0xb7,0x00,
+0xb8,0x00,
+0xb9,0x00,
+0xbd,0x00,
+0xbe,0x00,
+0xbf,0x00,
+0xc3,0x00,
+0xc4,0x00,
+0xc5,0x00,
+0xc9,0x00,
+0xca,0x00,
+0xcb,0x00,
+0xa4,0x00,
+0xa5,0x00,
+0xa6,0x00,
+0xa7,0x00,
+0xfe,0x01,
+0x74,0x13,
+0x15,0xfe,
+0x21,0xe0,
+0xfe,0x00,
+0x41,0x6e,
+0x83,0x03,
+0x7e,0x08,
+0x9c,0x64,
+0x95,0x65,
+0xd1,0x2d,
+0xd2,0x2d,
+0xb0,0x13,
+0xb1,0x26,
+0xb2,0x07,
+0xb3,0xf5,
+0xb4,0xea,
+0xb5,0x21,
+0xb6,0x21,
+0xb7,0xe4,
+0xb8,0xfb,
+0xfe,0x00,
+0x50,0x01,
+0x44,0xa2,
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0328/gc0328-gt7302-gmt.h b/drivers/media/video/wmt_v4l2/sensors/gc0328/gc0328-gt7302-gmt.h
new file mode 100755
index 00000000..ecec395d
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0328/gc0328-gt7302-gmt.h
@@ -0,0 +1,686 @@
+#ifndef GC0328_H
+#define GC0328_H
+
+
+// Scene Mode
+unsigned char gc0328_scene_mode_auto[] = {
+ 0xfa, 0x00,
+};
+
+unsigned char gc0328_scene_mode_night[] = {
+ 0xfa, 0x11,
+};
+
+
+// White Balance
+unsigned char gc0328_wb_auto [] = {
+ 0xfe, 0x00,
+ 0x77, 0x60,//57
+ 0x78, 0x40,//4
+ 0x79, 0x60,//45
+ 0x42, 0xfe,
+};
+
+unsigned char gc0328_wb_incandescent [] = {
+ //bai re guang
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x48,
+ 0x78, 0x40,
+ 0x79, 0x5c,
+};
+
+unsigned char gc0328_wb_fluorescent [] = {
+ //ri guang deng
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x40,
+ 0x78, 0x42,
+ 0x79, 0x50,
+};
+
+unsigned char gc0328_wb_daylight [] = {
+ //tai yang guang
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x74,
+ 0x78, 0x52,
+ 0x79, 0x40,
+};
+
+unsigned char gc0328_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x8c,
+ 0x78, 0x50,
+ 0x79, 0x40,
+};
+
+unsigned char gc0328_wb_tungsten [] = {
+ //wu si deng
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x40,
+ 0x78, 0x54,
+ 0x79, 0x70
+};
+
+
+// Exposure
+unsigned char gc0328_exposure_neg6[] = {
+ 0xfe, 0x00,
+ 0xd5, 0xe0,
+ 0xfe, 0x01,
+ 0x13, 0x35,
+ 0xfe, 0x00
+};
+
+unsigned char gc0328_exposure_neg3[] = {
+ 0xfe, 0x00,
+ 0xd5, 0xf0,
+ 0xfe, 0x01,
+ 0x13, 0x37,
+ 0xfe, 0x00
+};
+
+unsigned char gc0328_exposure_zero[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x00,
+ 0xfe, 0x01,
+ 0x13, 0x38,
+ 0xfe, 0x00
+};
+
+unsigned char gc0328_exposure_pos3[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x10,
+ 0xfe, 0x01,
+ 0x13, 0x55,
+ 0xfe, 0x00
+};
+
+unsigned char gc0328_exposure_pos6[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x20,
+ 0xfe, 0x01,
+ 0x13, 0x65,
+ 0xfe, 0x00
+};
+
+
+// Color Effect
+unsigned char gc0328_colorfx_none[] = {
+
+ 0x43, 0x00
+};
+
+unsigned char gc0328_colorfx_bw[] = {
+
+ 0x43, 0x02,
+ 0xda, 0xd0,
+ 0xdb, 0x28
+};
+
+unsigned char gc0328_colorfx_sepia[] = {
+ 0x43, 0x02,
+ 0xda, 0xc0,
+ 0xdb, 0xc0
+};
+
+unsigned char gc0328_colorfx_negative[] = {
+
+ 0x43, 0x01
+};
+
+unsigned char gc0328_colorfx_emboss[] = {
+ 0x43, 0x02,
+ 0xda, 0x50,
+ 0xdb, 0xe0
+};
+
+unsigned char gc0328_colorfx_sketch[] = {
+ 0x43, 0x02,
+ 0xda, 0x00,
+ 0xdb, 0x00
+};
+
+unsigned char gc0328_colorfx_sky_blue[] = {
+ 0x43,0x02,
+ 0xda,0x50,
+ 0xdb,0xe0,
+};
+
+unsigned char gc0328_colorfx_grass_green[] = {
+ 0x43,0x02,
+ 0xda,0xc0,
+ 0xdb,0xc0,
+};
+
+unsigned char gc0328_colorfx_skin_whiten[] = {
+};
+
+unsigned char gc0328_colorfx_vivid[] = {
+};
+
+// Brightness
+unsigned char gc0328_brightness_neg4[] = {
+};
+
+unsigned char gc0328_brightness_neg3[] = {
+};
+
+unsigned char gc0328_brightness_neg2[] = {
+};
+
+unsigned char gc0328_brightness_neg1[] = {
+};
+
+unsigned char gc0328_brightness_zero[] = {
+};
+
+unsigned char gc0328_brightness_pos1[] = {
+};
+
+unsigned char gc0328_brightness_pos2[] = {
+};
+
+unsigned char gc0328_brightness_pos3[] = {
+};
+
+unsigned char gc0328_brightness_pos4[] = {
+};
+
+// Contrast
+unsigned char gc0328_contrast_neg4[] = {
+};
+
+unsigned char gc0328_contrast_neg3[] = {
+};
+
+unsigned char gc0328_contrast_neg2[] = {
+};
+
+unsigned char gc0328_contrast_neg1[] = {
+};
+
+unsigned char gc0328_contrast_zero[] = {
+};
+
+unsigned char gc0328_contrast_pos1[] = {
+};
+
+unsigned char gc0328_contrast_pos2[] = {
+};
+
+unsigned char gc0328_contrast_pos3[] = {
+};
+
+unsigned char gc0328_contrast_pos4[] = {
+};
+
+// Saturation
+unsigned char gc0328_saturation_neg4[] = {
+};
+
+unsigned char gc0328_saturation_neg3[] = {
+};
+
+unsigned char gc0328_saturation_neg2[] = {
+};
+
+unsigned char gc0328_saturation_neg1[] = {
+};
+
+unsigned char gc0328_saturation_zero[] = {
+};
+
+unsigned char gc0328_saturation_pos1[] = {
+};
+
+unsigned char gc0328_saturation_pos2[] = {
+};
+
+unsigned char gc0328_saturation_pos3[] = {
+};
+
+unsigned char gc0328_saturation_pos4[] = {
+};
+
+
+// Resolution
+
+// QCIF
+unsigned char gc0328_176x144[]={
+};
+
+unsigned char gc0328_320x240[]={
+
+};
+
+unsigned char gc0328_640x480[]={
+};
+
+
+// Initiliztion
+unsigned char gc0328_default_regs_init[] = {
+ 0xfe , 0x80,
+ 0xfe , 0x80,
+ 0xfc , 0x16,
+ 0xfc , 0x16,
+ 0xfc , 0x16,
+ 0xfc , 0x16,
+ 0xf1 , 0x00,
+ 0xf2 , 0x00,
+ 0xfe , 0x00,
+ 0x4f , 0x00,
+ 0x03 , 0x00,
+ 0x04 , 0xc0,
+ 0x42 , 0x00,
+ 0x77 , 0x5a,
+ 0x78 , 0x40,
+ 0x79 , 0x56,
+
+ 0xfe , 0x00,
+ 0x0d , 0x01,
+ 0x0e , 0xe8,
+ 0x0f , 0x02,
+ 0x10 , 0x88,
+ 0x09 , 0x00,
+ 0x0a , 0x00,
+ 0x0b , 0x00,
+ 0x0c , 0x00,
+ 0x16 , 0x00,
+ 0x17 , 0x14,
+ 0x18 , 0x0e,
+ 0x19 , 0x06,
+
+ 0x1b , 0x48,
+ 0x1f , 0xC8,
+ 0x20 , 0x01,
+ 0x21 , 0x78,
+ 0x22 , 0xb0,
+ 0x23 , 0x06,
+ 0x24 , 0x11,
+ 0x26 , 0x00,
+ 0x50 , 0x01, //crop mode
+
+ //global gain for range
+ 0x70 , 0x45,
+
+ /////////////banding/////////////
+ 0x05 , 0x02,//hb
+ 0x06 , 0x2c,//
+ 0x07 , 0x00,//vb
+ 0x08 , 0xb8,//
+ 0xfe , 0x01,//
+ 0x29 , 0x00,//anti-flicker step [11:8]
+ 0x2a , 0x60,//anti-flicker step [7:0]
+ 0x2b , 0x02,//exp level 0 14.28fps
+ 0x2c , 0xa0,//
+ 0x2d , 0x03,//exp level 1 12.50fps
+ 0x2e , 0x00,//
+ 0x2f , 0x03,//exp level 2 10.00fps
+ 0x30 , 0xc0,//
+ 0x31 , 0x05,//exp level 3 7.14fps
+ 0x32 , 0x40,//
+ 0xfe , 0x00,//
+
+ ///////////////AWB//////////////
+ 0xfe , 0x01,
+ 0x50 , 0x00,
+ 0x4f , 0x00,
+ 0x4c , 0x01,
+ 0x4f , 0x00,
+ 0x4f , 0x00,
+ 0x4f , 0x00,
+ 0x4f , 0x00,
+ 0x4f , 0x00,
+ 0x4d , 0x30,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4d , 0x40,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4d , 0x50,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4d , 0x60,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4d , 0x70,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4f , 0x01,
+ 0x50 , 0x88,
+ 0xfe , 0x00,
+
+ //////////// BLK//////////////////////
+ 0xfe , 0x00,
+ 0x27 , 0xb7,
+ 0x28 , 0x7F,
+ 0x29 , 0x20,
+ 0x33 , 0x20,
+ 0x34 , 0x20,
+ 0x35 , 0x20,
+ 0x36 , 0x20,
+ 0x32 , 0x08,
+ 0x3b , 0x00,
+ 0x3c , 0x00,
+ 0x3d , 0x00,
+ 0x3e , 0x00,
+ 0x47 , 0x00,
+ 0x48 , 0x00,
+
+ //////////// block enable/////////////
+ 0x40 , 0x7f,
+ 0x41 , 0x26,
+ 0x42 , 0xfb,
+ 0x44 , 0x02, //yuv
+ 0x45 , 0x00,
+ 0x46 , 0x02,
+ 0x4f , 0x01,
+ 0x4b , 0x01,
+ 0x50 , 0x01,
+
+ /////DN & EEINTP/////
+ 0x7e , 0x0a,
+ 0x7f , 0x03,
+ 0x81 , 0x15,
+ 0x82 , 0x90,
+ 0x83 , 0x02,
+ 0x84 , 0xe5,
+ 0x90 , 0x2c,
+ 0x92 , 0x02,
+ 0x94 , 0x02,
+ 0x95 , 0x35,
+
+ ////////////YCP///////////
+ 0xd1 , 0x24,// 0x30 for front
+ 0xd2 , 0x24,// 0x30 for front
+ 0xd3 , 0x40,
+ 0xdd , 0xd3,
+ 0xde , 0x38,
+ 0xe4 , 0x88,
+ 0xe5 , 0x40,
+ 0xd7 , 0x0e,
+
+ ///////////rgb gamma ////////////
+ 0xfe , 0x00,
+ 0xbf , 0x0e,
+ 0xc0 , 0x1c,
+ 0xc1 , 0x34,
+ 0xc2 , 0x48,
+ 0xc3 , 0x5a,
+ 0xc4 , 0x6e,
+ 0xc5 , 0x80,
+ 0xc6 , 0x9c,
+ 0xc7 , 0xb4,
+ 0xc8 , 0xc7,
+ 0xc9 , 0xd7,
+ 0xca , 0xe3,
+ 0xcb , 0xed,
+ 0xcc , 0xf2,
+ 0xcd , 0xf8,
+ 0xce , 0xfd,
+ 0xcf , 0xff,
+
+ /////////////Y gamma//////////
+ 0xfe , 0x00,
+ 0x63 , 0x00,
+ 0x64 , 0x05,
+ 0x65 , 0x0b,
+ 0x66 , 0x19,
+ 0x67 , 0x2e,
+ 0x68 , 0x40,
+ 0x69 , 0x54,
+ 0x6a , 0x66,
+ 0x6b , 0x86,
+ 0x6c , 0xa7,
+ 0x6d , 0xc6,
+ 0x6e , 0xe4,
+ 0x6f , 0xff,
+
+ //////////////ASDE/////////////
+ 0xfe , 0x01,
+ 0x18 , 0x02,
+ 0xfe , 0x00,
+ 0x97 , 0x30,
+ 0x98 , 0x00,
+ 0x9b , 0x60,
+ 0x9c , 0x60,
+ 0xa4 , 0x50,
+ 0xa8 , 0x80,
+ 0xaa , 0x40,
+ 0xa2 , 0x23,
+ 0xad , 0x28,
+
+ //////////////abs///////////
+ 0xfe , 0x01,
+ 0x9c , 0x00,
+ 0x9e , 0xc0,
+ 0x9f , 0x40,
+
+ ////////////// AEC////////////
+ 0xfe , 0x01,
+ 0x08 , 0xa0,
+ 0x09 , 0xe8,
+ 0x10 , 0x08,
+ 0x11 , 0x21,
+ 0x12 , 0x11,
+ 0x13 , 0x45,
+ 0x15 , 0xfc,
+ 0x18 , 0x02,
+ 0x21 , 0xf0,
+ 0x22 , 0x60,
+ 0x23 , 0x30,
+ 0x25 , 0x00,
+ 0x24 , 0x14,
+ 0x3d , 0x80,
+ 0x3e , 0x40,
+
+ ////////////////AWB///////////
+ 0xfe , 0x01,
+ 0x51 , 0x88,
+ 0x52 , 0x12,
+ 0x53 , 0x80,
+ 0x54 , 0x60,
+ 0x55 , 0x01,
+ 0x56 , 0x02,
+ 0x58 , 0x00,
+ 0x5b , 0x02,
+ 0x5e , 0xa4,
+ 0x5f , 0x8a,
+ 0x61 , 0xdc,
+ 0x62 , 0xdc,
+ 0x70 , 0xfc,
+ 0x71 , 0x10,
+ 0x72 , 0x30,
+ 0x73 , 0x0b,
+ 0x74 , 0x0b,
+ 0x75 , 0x01,
+ 0x76 , 0x00,
+ 0x77 , 0x40,
+ 0x78 , 0x70,
+ 0x79 , 0x00,
+ 0x7b , 0x00,
+ 0x7c , 0x71,
+ 0x7d , 0x00,
+ 0x80 , 0x70,
+ 0x81 , 0x58,
+ 0x82 , 0x98,
+ 0x83 , 0x60,
+ 0x84 , 0x58,
+ 0x85 , 0x50,
+ 0xfe , 0x00,
+
+ ////////////////LSC////////////////
+ 0xfe , 0x01,
+ 0xc0 , 0x10,
+ 0xc1 , 0x0c,
+ 0xc2 , 0x0a,
+ 0xc6 , 0x0e,
+ 0xc7 , 0x0b,
+ 0xc8 , 0x0a,
+ 0xba , 0x26,
+ 0xbb , 0x1c,
+ 0xbc , 0x1d,
+ 0xb4 , 0x23,
+ 0xb5 , 0x1c,
+ 0xb6 , 0x1a,
+ 0xc3 , 0x00,
+ 0xc4 , 0x00,
+ 0xc5 , 0x00,
+ 0xc9 , 0x00,
+ 0xca , 0x00,
+ 0xcb , 0x00,
+ 0xbd , 0x00,
+ 0xbe , 0x00,
+ 0xbf , 0x00,
+ 0xb7 , 0x07,
+ 0xb8 , 0x05,
+ 0xb9 , 0x05,
+ 0xa8 , 0x07,
+ 0xa9 , 0x06,
+ 0xaa , 0x00,
+ 0xab , 0x04,
+ 0xac , 0x00,
+ 0xad , 0x02,
+ 0xae , 0x0d,
+ 0xaf , 0x05,
+ 0xb0 , 0x00,
+ 0xb1 , 0x07,
+ 0xb2 , 0x03,
+ 0xb3 , 0x00,
+ 0xa4 , 0x00,
+ 0xa5 , 0x00,
+ 0xa6 , 0x00,
+ 0xa7 , 0x00,
+ 0xa1 , 0x3c,
+ 0xa2 , 0x50,
+ 0xfe , 0x00,
+
+ ///////////////CCT ///////////
+ 0xb1 , 0x12,
+ 0xb2 , 0xf5,
+ 0xb3 , 0xfe,
+ 0xb4 , 0xe0,
+ 0xb5 , 0x15,
+ 0xb6 , 0xc8,
+
+ /////skin CC for front //////
+ //0xb1 , 0x00,
+ //0xb2 , 0x00,
+ //0xb3 , 0x00,
+ //0xb4 , 0xf0,
+ //0xb5 , 0x00,
+ //0xb6 , 0x00,
+
+
+ ///////////////AWB////////////////
+ 0xfe , 0x01,
+ 0x50 , 0x00,
+ 0xfe , 0x01,
+ 0x4f , 0x00,
+ 0x4c , 0x01,
+ 0x4f , 0x00,
+ 0x4f , 0x00,
+ 0x4f , 0x00,
+ 0x4d , 0x34,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x02,
+ 0x4e , 0x02,
+ 0x4d , 0x44,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4d , 0x53,
+ 0x4e , 0x00,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4d , 0x65,
+ 0x4e , 0x04,
+ 0x4d , 0x73,
+ 0x4e , 0x20,
+ 0x4d , 0x83,
+ 0x4e , 0x20,
+ 0x4f , 0x01,
+ 0x50 , 0x88,
+ /////////output////////
+ 0xfe , 0x00,
+ 0xf1 , 0x07,
+ 0xf2 , 0x01,
+
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0328/gc0328-wave7_cold.h b/drivers/media/video/wmt_v4l2/sensors/gc0328/gc0328-wave7_cold.h
new file mode 100755
index 00000000..2d0a6c28
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0328/gc0328-wave7_cold.h
@@ -0,0 +1,606 @@
+#ifndef GC0328_H
+#define GC0328_H
+
+
+// Scene Mode
+unsigned char gc0328_scene_mode_auto[] = {
+ 0xfa, 0x00,
+};
+
+unsigned char gc0328_scene_mode_night[] = {
+ 0xfa, 0x11,
+};
+
+
+// White Balance
+unsigned char gc0328_wb_auto [] = {
+ 0xfe, 0x00,
+ 0x77, 0x60,//57
+ 0x78, 0x40,//4
+ 0x79, 0x60,//45
+ 0x42, 0xfe,
+};
+
+unsigned char gc0328_wb_incandescent [] = {
+ //bai re guang
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x48,
+ 0x78, 0x40,
+ 0x79, 0x5c,
+};
+
+unsigned char gc0328_wb_fluorescent [] = {
+ //ri guang deng
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x40,
+ 0x78, 0x42,
+ 0x79, 0x50,
+};
+
+unsigned char gc0328_wb_daylight [] = {
+ //tai yang guang
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x74,
+ 0x78, 0x52,
+ 0x79, 0x40,
+};
+
+unsigned char gc0328_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x8c,
+ 0x78, 0x50,
+ 0x79, 0x40,
+};
+
+unsigned char gc0328_wb_tungsten [] = {
+ //wu si deng
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x40,
+ 0x78, 0x54,
+ 0x79, 0x70
+};
+
+
+// Exposure
+unsigned char gc0328_exposure_neg6[] = {
+ 0xfe, 0x00,
+ 0xd5, 0xe0,
+ 0xfe, 0x01,
+ 0x13, 0x45,
+ 0xfe, 0x00
+};
+
+unsigned char gc0328_exposure_neg3[] = {
+ 0xfe, 0x00,
+ 0xd5, 0xf0,
+ 0xfe, 0x01,
+ 0x13, 0x47,
+ 0xfe, 0x00
+};
+
+unsigned char gc0328_exposure_zero[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x00,
+ 0xfe, 0x01,
+ 0x13, 0x4a,
+ 0xfe, 0x00
+};
+
+unsigned char gc0328_exposure_pos3[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x10,
+ 0xfe, 0x01,
+ 0x13, 0x55,
+ 0xfe, 0x00
+};
+
+unsigned char gc0328_exposure_pos6[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x20,
+ 0xfe, 0x01,
+ 0x13, 0x65,
+ 0xfe, 0x00
+};
+
+
+// Color Effect
+unsigned char gc0328_colorfx_none[] = {
+
+ 0x43, 0x00
+};
+
+unsigned char gc0328_colorfx_bw[] = {
+
+ 0x43, 0x02,
+ 0xda, 0xd0,
+ 0xdb, 0x28
+};
+
+unsigned char gc0328_colorfx_sepia[] = {
+ 0x43, 0x02,
+ 0xda, 0xc0,
+ 0xdb, 0xc0
+};
+
+unsigned char gc0328_colorfx_negative[] = {
+
+ 0x43, 0x01
+};
+
+unsigned char gc0328_colorfx_emboss[] = {
+ 0x43, 0x02,
+ 0xda, 0x50,
+ 0xdb, 0xe0
+};
+
+unsigned char gc0328_colorfx_sketch[] = {
+ 0x43, 0x02,
+ 0xda, 0x00,
+ 0xdb, 0x00
+};
+
+unsigned char gc0328_colorfx_sky_blue[] = {
+ 0x43,0x02,
+ 0xda,0x50,
+ 0xdb,0xe0,
+};
+
+unsigned char gc0328_colorfx_grass_green[] = {
+ 0x43,0x02,
+ 0xda,0xc0,
+ 0xdb,0xc0,
+};
+
+unsigned char gc0328_colorfx_skin_whiten[] = {
+};
+
+unsigned char gc0328_colorfx_vivid[] = {
+};
+
+// Brightness
+unsigned char gc0328_brightness_neg4[] = {
+};
+
+unsigned char gc0328_brightness_neg3[] = {
+};
+
+unsigned char gc0328_brightness_neg2[] = {
+};
+
+unsigned char gc0328_brightness_neg1[] = {
+};
+
+unsigned char gc0328_brightness_zero[] = {
+};
+
+unsigned char gc0328_brightness_pos1[] = {
+};
+
+unsigned char gc0328_brightness_pos2[] = {
+};
+
+unsigned char gc0328_brightness_pos3[] = {
+};
+
+unsigned char gc0328_brightness_pos4[] = {
+};
+
+// Contrast
+unsigned char gc0328_contrast_neg4[] = {
+};
+
+unsigned char gc0328_contrast_neg3[] = {
+};
+
+unsigned char gc0328_contrast_neg2[] = {
+};
+
+unsigned char gc0328_contrast_neg1[] = {
+};
+
+unsigned char gc0328_contrast_zero[] = {
+};
+
+unsigned char gc0328_contrast_pos1[] = {
+};
+
+unsigned char gc0328_contrast_pos2[] = {
+};
+
+unsigned char gc0328_contrast_pos3[] = {
+};
+
+unsigned char gc0328_contrast_pos4[] = {
+};
+
+// Saturation
+unsigned char gc0328_saturation_neg4[] = {
+};
+
+unsigned char gc0328_saturation_neg3[] = {
+};
+
+unsigned char gc0328_saturation_neg2[] = {
+};
+
+unsigned char gc0328_saturation_neg1[] = {
+};
+
+unsigned char gc0328_saturation_zero[] = {
+};
+
+unsigned char gc0328_saturation_pos1[] = {
+};
+
+unsigned char gc0328_saturation_pos2[] = {
+};
+
+unsigned char gc0328_saturation_pos3[] = {
+};
+
+unsigned char gc0328_saturation_pos4[] = {
+};
+
+
+// Resolution
+
+// QCIF
+unsigned char gc0328_176x144[]={
+};
+
+unsigned char gc0328_320x240[]={
+
+};
+
+unsigned char gc0328_640x480[]={
+};
+
+
+// Initiliztion
+unsigned char gc0328_default_regs_init[] = {
+ 0xfe, 0x80,
+ 0xfe, 0x80,
+ 0xfc, 0x16,
+ 0xfc, 0x16,
+ 0xfc, 0x16,
+ 0xfc, 0x16,
+
+ //0x42, 0x02,
+ 0xfa, 0x11,
+ 0x4f, 0x00,
+ 0x03, 0x06,
+ 0x04, 0x24,
+ 0x4f, 0x01,
+
+ 0xfe, 0x01,
+ 0x4f, 0x00,
+ 0x4c, 0x01,
+ 0xfe, 0x00,
+ //////////////////////////////
+ //AWB
+ ////////////////////////////////
+0xfe, 0x01,
+ 0x51, 0x80,//20
+ 0x52, 0x12,// 16 //1f
+ 0x53, 0x80,//40
+ 0x54, 0x60,//9f
+ 0x55, 0x04,//01
+ 0x56, 0x0e,//00
+ 0x5b, 0x02,//02
+ 0x61, 0xdc,//R2G_stand0[7:0]
+ 0x62, 0xca,//B2G_stand0[7:0]
+
+ 0x70, 0xf5,
+ 0x71, 0x0a,
+ 0x72, 0x18,//y2c
+ 0x73, 0x30,//28 //20, 0x//AWB_C_inter
+ 0x74, 0x58,//40 //20, 0x// AWB_C_max
+ 0x7c, 0x71,//AWB speed,AWB margin
+ 0x7d, 0x00,//10 //AWB every N
+ 0x76, 0x8f,//move mode en,Move mode TH
+ 0x79, 0x00,//00
+
+
+
+ ////AWB////
+0x4f, 0x00,
+ 0x4d, 0x34,
+ 0x4e, 0x04,
+ 0x4e, 0x02,
+ 0x4e, 0x02,
+ 0x4d, 0x43,
+ 0x4e, 0x04,
+ 0x4e, 0x04,//D50
+ 0x4e, 0x02,// d65 add
+ 0x4e, 0x02,// d65 add
+ 0x4d, 0x53,
+ 0x4e, 0x08,//cwf
+ 0x4e, 0x04,
+ 0x4e, 0x02,//D65
+ 0x4e, 0x02,//D65
+ 0x4d, 0x63,
+ 0x4e, 0x10,//tl84
+ 0x4d, 0x72,
+ 0x4e, 0x20,
+ 0x4e, 0x20,
+ 0x4d, 0x82,
+ 0x4e, 0x20,//A
+ 0x4e, 0x20,
+ 0x4d, 0x92,
+ 0x4e, 0x20,//A
+ 0x4e, 0x20,
+ 0x4d, 0xa0,
+ 0x4e, 0x40,// H
+ 0x4e, 0x40,
+ 0x4e, 0x40,
+ 0x4f, 0x01,
+ 0x50, 0x84,//80//
+ 0xfe, 0x00,//page0
+
+ ////////////////////////////////////////////////
+ //////////// BLK //////////////////////
+ ////////////////////////////////////////////////
+ 0x27, 0xf7,
+ 0x28, 0x7F,
+ 0x29, 0x20,
+ 0x33, 0x20,
+ 0x34, 0x20,
+ 0x35, 0x20,
+ 0x36, 0x20,
+ 0x32, 0x08,
+
+ 0x47, 0x00,
+ 0x48, 0x00,
+
+ //////////////////////////////////////////////
+ ////////// page 0 ////////////////////////
+ //////////////////////////////////////////////
+ 0xfe, 0x00,
+ //0x05, 0x00,
+ //0x06, 0xde,
+ //0x07, 0x00,
+ //0x08, 0xa7,
+
+ 0x0d, 0x01,
+ 0x0e, 0xe8,
+ 0x0f, 0x02,
+ 0x10, 0x88,
+ 0x09, 0x00,
+ 0x0a, 0x00,
+ 0x0b, 0x00,
+ 0x0c, 0x00,
+ 0x16, 0x00,
+ 0x17, 0x14,
+ 0x18, 0x0e,
+ 0x19, 0x06,
+
+ 0x1b, 0x48,
+ 0x1f, 0xC8,
+ 0x20, 0x01,
+ 0x21, 0x78,
+ 0x22, 0xb0,
+ 0x23, 0x06,
+ 0x24, 0x16,
+ 0x26, 0x00,
+
+ 0x50, 0x01,
+
+ //global gain for range
+ 0x70, 0x45,
+
+ //////////////////////////////////////
+ //////////// block enable ///
+ //////////////////////////////////////
+ 0x40, 0x7f,
+ 0x41, 0x24,
+
+ 0x42, 0xff,
+
+ 0x45, 0x00,
+ 0x44, 0x02,
+ 0x46, 0x02, //0x03
+
+ 0x4b, 0x01,
+ 0x50, 0x01,
+
+ //DN & EEINTP
+ 0x7e, 0x0a,
+ 0x7f, 0x03,
+ 0x81, 0x15,
+ 0x82, 0x85,
+ 0x83, 0x01,
+ 0x84, 0xe5,
+ 0x90, 0xac,
+ 0x92, 0x03,
+ 0x94, 0x04,//02
+ 0x95, 0x76,//43
+
+
+ ///////YCP
+ 0xd1, 0x30,//38
+ 0xd2, 0x38,//38
+ 0xd3, 0x38,//38
+ 0xdd, 0x54,
+ 0xde, 0x38,
+ 0xe4, 0x88,
+ 0xe5, 0x40,
+ 0xd7, 0x0e,
+
+ //////////////////////////////
+ //////////////// GAMMA //////
+ //////////////////////////////
+ //rgb gamma
+
+
+ 0xBF, 0x10,
+ 0xc0, 0x20,
+ 0xc1, 0x38,
+ 0xc2, 0x4E,
+ 0xc3, 0x63,
+ 0xc4, 0x76,
+ 0xc5, 0x87,
+ 0xc6, 0xA2,
+ 0xc7, 0xB8,
+ 0xc8, 0xCA,
+ 0xc9, 0xD8,
+ 0xcA, 0xE3,
+ 0xcB, 0xEB,
+ 0xcC, 0xF0,
+ 0xcD, 0xF8,
+ 0xcE, 0xFD,
+ 0xcF, 0xFF,
+
+ ///Y gamma
+ 0xfe, 0x00,
+ 0x63, 0x00,
+ 0x64, 0x05,
+ 0x65, 0x0b,
+ 0x66, 0x19,
+ 0x67, 0x2e,
+ 0x68, 0x40,
+ 0x69, 0x54,
+ 0x6a, 0x66,
+ 0x6b, 0x86,
+ 0x6c, 0xa7,
+ 0x6d, 0xc6,
+ 0x6e, 0xe4,
+ 0x6f, 0xff,
+
+
+
+ //////ASDE
+ 0xfe, 0x01,
+ 0x18, 0x02,
+
+ 0xfe, 0x00,
+
+ 0x98, 0x00,
+ 0x9b, 0x20,
+ 0x9c, 0x80,
+ 0xa4, 0x10,
+ 0xa8, 0xB0,
+ 0xaa, 0x40,
+ 0xa2, 0x23,
+ 0xad, 0x01,
+
+ ///////////////////////////////////////
+ ////////// AEC /////////////////////
+ ///////////////////////////////////////
+ 0xfe, 0x01,
+ 0x9c, 0x02,
+ 0x08, 0xa0,
+ 0x09, 0xe8,
+
+ 0x10, 0x08,
+
+ 0x11, 0x91,
+ 0x12, 0x10,
+ 0x13, 0x30,// 45
+ 0x15, 0xfc,
+ 0x21, 0xa0,//c0
+ 0x22, 0x40,//60
+ 0x23, 0x30,
+ 0x25, 0x00,
+ 0x24, 0x14,
+
+
+ //0x28, 0x0a,
+
+ 0xfe,0x00 ,
+ 0x05,0x00 ,
+ 0x06,0x80 , // hb
+ 0x07,0x00 ,
+ 0x08,0x60 ,//vb
+
+ 0xfe,0x01 ,
+ 0x29,0x00 , //anti-flicker step [11:8]
+ 0x2a,0x49 , //anti-flicker step [7:0]
+
+ 0x2b,0x02 , //exp level 0 20fps
+ 0x2c,0x48,
+ 0x2d,0x02 , //exp level 14 fps
+ 0x2e,0xda ,
+ 0x2f,0x03 , //exp level 2 10.00fps
+ 0x30,0xfe ,
+ 0x31,0x05 , //exp level 3 8fps
+ 0x32,0x6b ,
+ 0xfe,0x00 ,
+ //////////////////////////////////////
+ ////////////LSC//////////////////////
+ //////////////////////////////////////
+ 0xfe, 0x01,
+ 0xc0, 0x0f,
+ 0xc1, 0x0b,
+ 0xc2, 0x0b,
+ 0xc6, 0x04,
+ 0xc7, 0x00,
+ 0xc8, 0x00,
+ 0xba, 0x1e,
+ 0xbb, 0x1a,
+ 0xbc, 0x1b,
+ 0xb4, 0x1a,
+ 0xb5, 0x12,
+ 0xb6, 0x14,
+ 0xc3, 0x00,
+ 0xc4, 0x00,
+ 0xc5, 0x00,
+ 0xc9, 0x00,
+ 0xca, 0x00,
+ 0xcb, 0x00,
+ 0xbd, 0x0a,
+ 0xbe, 0x00,
+ 0xbf, 0x00,
+ 0xb7, 0x04,
+ 0xb8, 0x00,
+ 0xb9, 0x00,
+ 0xa8, 0x13,
+ 0xa9, 0x0b,
+ 0xaa, 0x0b,
+ 0xab, 0x20,
+ 0xac, 0x1c,
+ 0xad, 0x22,
+ 0xae, 0x0e,
+ 0xaf, 0x05,
+ 0xb0, 0x06,
+ 0xb1, 0x0d,
+ 0xb2, 0x0a,
+ 0xb3, 0x08,
+ 0xa4, 0x00,
+ 0xa5, 0x00,
+ 0xa6, 0x00,
+ 0xa7, 0x00,
+ 0xa1, 0x3c,
+ 0xa2, 0x50,
+ 0xfe, 0x00,
+
+ ///cct
+ #if 0
+ 0xB1, 0x02,
+ 0xB2, 0x02,
+ 0xB3, 0x07,
+ 0xB4, 0xf0,
+ 0xB5, 0x05,
+ 0xB6, 0xf0,
+
+ 0xf1, 0x07,
+
+ 0xf2, 0x01,
+ #endif
+0xB1,0x08 ,// 40
+ 0xB2,0x02 ,
+ 0xB3,0x03 ,//07
+ 0xB4,0xe0 ,
+ 0xB5,0x00 ,//
+ 0xB6,0xf0 ,
+ 0xB7,0x0a ,
+
+ 0xf1,0x07 ,
+ 0xf1,0x07 ,
+ 0xf2,0x01 ,
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0328/gc0328-wave7_warm.h b/drivers/media/video/wmt_v4l2/sensors/gc0328/gc0328-wave7_warm.h
new file mode 100755
index 00000000..4b7746bf
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0328/gc0328-wave7_warm.h
@@ -0,0 +1,605 @@
+#ifndef GC0328_H
+#define GC0328_H
+
+
+// Scene Mode
+unsigned char gc0328_scene_mode_auto[] = {
+ 0xfa, 0x00,
+};
+
+unsigned char gc0328_scene_mode_night[] = {
+ 0xfa, 0x11,
+};
+
+
+// White Balance
+unsigned char gc0328_wb_auto [] = {
+ 0xfe, 0x00,
+ 0x77, 0x60,//57
+ 0x78, 0x40,//4
+ 0x79, 0x60,//45
+ 0x42, 0xfe,
+};
+
+unsigned char gc0328_wb_incandescent [] = {
+ //bai re guang
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x48,
+ 0x78, 0x40,
+ 0x79, 0x5c,
+};
+
+unsigned char gc0328_wb_fluorescent [] = {
+ //ri guang deng
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x40,
+ 0x78, 0x42,
+ 0x79, 0x50,
+};
+
+unsigned char gc0328_wb_daylight [] = {
+ //tai yang guang
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x74,
+ 0x78, 0x52,
+ 0x79, 0x40,
+};
+
+unsigned char gc0328_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x8c,
+ 0x78, 0x50,
+ 0x79, 0x40,
+};
+
+unsigned char gc0328_wb_tungsten [] = {
+ //wu si deng
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x40,
+ 0x78, 0x54,
+ 0x79, 0x70
+};
+
+
+// Exposure
+unsigned char gc0328_exposure_neg6[] = {
+ 0xfe, 0x00,
+ 0xd5, 0xe0,
+ 0xfe, 0x01,
+ 0x13, 0x45,
+ 0xfe, 0x00
+};
+
+unsigned char gc0328_exposure_neg3[] = {
+ 0xfe, 0x00,
+ 0xd5, 0xf0,
+ 0xfe, 0x01,
+ 0x13, 0x47,
+ 0xfe, 0x00
+};
+
+unsigned char gc0328_exposure_zero[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x05,
+ 0xfe, 0x01,
+ 0x13, 0x50,
+ 0xfe, 0x00
+};
+
+unsigned char gc0328_exposure_pos3[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x10,
+ 0xfe, 0x01,
+ 0x13, 0x65,
+ 0xfe, 0x00
+};
+
+unsigned char gc0328_exposure_pos6[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x20,
+ 0xfe, 0x01,
+ 0x13, 0x75,
+ 0xfe, 0x00
+};
+
+
+// Color Effect
+unsigned char gc0328_colorfx_none[] = {
+
+ 0x43, 0x00
+};
+
+unsigned char gc0328_colorfx_bw[] = {
+
+ 0x43, 0x02,
+ 0xda, 0xd0,
+ 0xdb, 0x28
+};
+
+unsigned char gc0328_colorfx_sepia[] = {
+ 0x43, 0x02,
+ 0xda, 0xc0,
+ 0xdb, 0xc0
+};
+
+unsigned char gc0328_colorfx_negative[] = {
+
+ 0x43, 0x01
+};
+
+unsigned char gc0328_colorfx_emboss[] = {
+ 0x43, 0x02,
+ 0xda, 0x50,
+ 0xdb, 0xe0
+};
+
+unsigned char gc0328_colorfx_sketch[] = {
+ 0x43, 0x02,
+ 0xda, 0x00,
+ 0xdb, 0x00
+};
+
+unsigned char gc0328_colorfx_sky_blue[] = {
+ 0x43,0x02,
+ 0xda,0x50,
+ 0xdb,0xe0,
+};
+
+unsigned char gc0328_colorfx_grass_green[] = {
+ 0x43,0x02,
+ 0xda,0xc0,
+ 0xdb,0xc0,
+};
+
+unsigned char gc0328_colorfx_skin_whiten[] = {
+};
+
+unsigned char gc0328_colorfx_vivid[] = {
+};
+
+// Brightness
+unsigned char gc0328_brightness_neg4[] = {
+};
+
+unsigned char gc0328_brightness_neg3[] = {
+};
+
+unsigned char gc0328_brightness_neg2[] = {
+};
+
+unsigned char gc0328_brightness_neg1[] = {
+};
+
+unsigned char gc0328_brightness_zero[] = {
+};
+
+unsigned char gc0328_brightness_pos1[] = {
+};
+
+unsigned char gc0328_brightness_pos2[] = {
+};
+
+unsigned char gc0328_brightness_pos3[] = {
+};
+
+unsigned char gc0328_brightness_pos4[] = {
+};
+
+// Contrast
+unsigned char gc0328_contrast_neg4[] = {
+};
+
+unsigned char gc0328_contrast_neg3[] = {
+};
+
+unsigned char gc0328_contrast_neg2[] = {
+};
+
+unsigned char gc0328_contrast_neg1[] = {
+};
+
+unsigned char gc0328_contrast_zero[] = {
+};
+
+unsigned char gc0328_contrast_pos1[] = {
+};
+
+unsigned char gc0328_contrast_pos2[] = {
+};
+
+unsigned char gc0328_contrast_pos3[] = {
+};
+
+unsigned char gc0328_contrast_pos4[] = {
+};
+
+// Saturation
+unsigned char gc0328_saturation_neg4[] = {
+};
+
+unsigned char gc0328_saturation_neg3[] = {
+};
+
+unsigned char gc0328_saturation_neg2[] = {
+};
+
+unsigned char gc0328_saturation_neg1[] = {
+};
+
+unsigned char gc0328_saturation_zero[] = {
+};
+
+unsigned char gc0328_saturation_pos1[] = {
+};
+
+unsigned char gc0328_saturation_pos2[] = {
+};
+
+unsigned char gc0328_saturation_pos3[] = {
+};
+
+unsigned char gc0328_saturation_pos4[] = {
+};
+
+
+// Resolution
+
+// QCIF
+unsigned char gc0328_176x144[]={
+};
+
+unsigned char gc0328_320x240[]={
+
+};
+
+unsigned char gc0328_640x480[]={
+};
+
+
+// Initiliztion
+unsigned char gc0328_default_regs_init[] = {
+ 0xfe, 0x80,
+ 0xfe, 0x80,
+ 0xfc, 0x16,
+ 0xfc, 0x16,
+ 0xfc, 0x16,
+ 0xfc, 0x16,
+
+ //0x42, 0x02,
+ 0xfa, 0x11,
+ 0x4f, 0x00,
+ 0x03, 0x06,
+ 0x04, 0x24,
+ 0x4f, 0x01,
+
+ 0xfe, 0x01,
+ 0x4f, 0x00,
+ 0x4c, 0x01,
+ 0xfe, 0x00,
+ //////////////////////////////
+ //AWB
+ ////////////////////////////////
+0xfe, 0x01,
+ 0x51, 0x80,//20
+ 0x52, 0x12,// 16 //1f
+ 0x53, 0x80,//40
+ 0x54, 0x60,//9f
+ 0x55, 0x04,//01
+ 0x56, 0x0e,//00
+ 0x5b, 0x02,//02
+ 0x61, 0xdc,//R2G_stand0[7:0]
+ 0x62, 0xca,//B2G_stand0[7:0]
+
+ 0x70, 0xf5,
+ 0x71, 0x0a,
+ 0x72, 0x18,//y2c
+ 0x73, 0x30,//28 //20, 0x//AWB_C_inter
+ 0x74, 0x58,//40 //20, 0x// AWB_C_max
+ 0x7c, 0x71,//AWB speed,AWB margin
+ 0x7d, 0x00,//10 //AWB every N
+ 0x76, 0x8f,//move mode en,Move mode TH
+ 0x79, 0x00,//00
+
+
+
+ ////AWB////
+0x4f, 0x00,
+ 0x4d, 0x34,
+ 0x4e, 0x04,
+ 0x4e, 0x02,
+ 0x4e, 0x02,
+ 0x4d, 0x43,
+ 0x4e, 0x04,
+ 0x4e, 0x04,//D50
+ 0x4e, 0x02,// d65 add
+ 0x4e, 0x02,// d65 add
+ 0x4d, 0x53,
+ 0x4e, 0x08,//cwf
+ 0x4e, 0x04,
+ 0x4e, 0x02,//D65
+ 0x4e, 0x02,//D65
+ 0x4d, 0x63,
+ 0x4e, 0x10,//tl84
+ 0x4d, 0x72,
+ 0x4e, 0x20,
+ 0x4e, 0x20,
+ 0x4d, 0x82,
+ 0x4e, 0x20,//A
+ 0x4e, 0x20,
+ 0x4d, 0x92,
+ 0x4e, 0x20,//A
+ 0x4e, 0x20,
+ 0x4d, 0xa0,
+ 0x4e, 0x40,// H
+ 0x4e, 0x40,
+ 0x4e, 0x40,
+ 0x4f, 0x01,
+ 0x50, 0x84,//80//
+ 0xfe, 0x00,//page0
+
+ ////////////////////////////////////////////////
+ //////////// BLK //////////////////////
+ ////////////////////////////////////////////////
+ 0x27, 0xf7,
+ 0x28, 0x7F,
+ 0x29, 0x20,
+ 0x33, 0x20,
+ 0x34, 0x20,
+ 0x35, 0x20,
+ 0x36, 0x20,
+ 0x32, 0x08,
+
+ 0x47, 0x00,
+ 0x48, 0x00,
+
+ //////////////////////////////////////////////
+ ////////// page 0 ////////////////////////
+ //////////////////////////////////////////////
+ 0xfe, 0x00,
+ //0x05, 0x00,
+ //0x06, 0xde,
+ //0x07, 0x00,
+ //0x08, 0xa7,
+
+ 0x0d, 0x01,
+ 0x0e, 0xe8,
+ 0x0f, 0x02,
+ 0x10, 0x88,
+ 0x09, 0x00,
+ 0x0a, 0x00,
+ 0x0b, 0x00,
+ 0x0c, 0x00,
+ 0x16, 0x00,
+ 0x17, 0x14,
+ 0x18, 0x0e,
+ 0x19, 0x06,
+
+ 0x1b, 0x48,
+ 0x1f, 0xC8,
+ 0x20, 0x01,
+ 0x21, 0x78,
+ 0x22, 0xb0,
+ 0x23, 0x06,
+ 0x24, 0x16,
+ 0x26, 0x00,
+
+ 0x50, 0x01,
+
+ //global gain for range
+ 0x70, 0x45,
+
+ //////////////////////////////////////
+ //////////// block enable ///
+ //////////////////////////////////////
+ 0x40, 0x7f,
+ 0x41, 0x24,
+
+ 0x42, 0xff,
+
+ 0x45, 0x00,
+ 0x44, 0x02,
+ 0x46, 0x02, //0x03
+
+ 0x4b, 0x01,
+ 0x50, 0x01,
+
+ //DN & EEINTP
+ 0x7e, 0x0a,
+ 0x7f, 0x03,
+ 0x81, 0x15,
+ 0x82, 0x85,
+ 0x83, 0x03,
+ 0x84, 0xe5,
+ 0x90, 0xac,
+ 0x92, 0x02,
+ 0x94, 0x04,//02
+ 0x95, 0x87,//76
+
+
+ ///////YCP
+ 0xd1, 0x30,//38
+ 0xd2, 0x38,//38
+ 0xd3, 0x42,//38
+ 0xdd, 0x54,
+ 0xde, 0x38,
+ 0xe4, 0x88,
+ 0xe5, 0x40,
+ 0xd7, 0x0e,
+
+ //////////////////////////////
+ //////////////// GAMMA //////
+ //////////////////////////////
+ //rgb gamma
+ 0xBF, 0x14,
+ 0xc0, 0x28,
+ 0xc1, 0x44,
+ 0xc2, 0x5D,
+ 0xc3, 0x72,
+ 0xc4, 0x86,
+ 0xc5, 0x95,
+ 0xc6, 0xB1,
+ 0xc7, 0xC6,
+ 0xc8, 0xD5,
+ 0xc9, 0xE1,
+ 0xcA, 0xEA,
+ 0xcB, 0xF1,
+ 0xcC, 0xF5,
+ 0xcD, 0xFB,
+ 0xcE, 0xFE,
+ 0xcF, 0xFF,
+
+ ///Y gamma
+ 0xfe, 0x00,
+ 0x63, 0x00,
+ 0x64, 0x05,
+ 0x65, 0x0b,
+ 0x66, 0x19,
+ 0x67, 0x2e,
+ 0x68, 0x40,
+ 0x69, 0x54,
+ 0x6a, 0x66,
+ 0x6b, 0x86,
+ 0x6c, 0xa7,
+ 0x6d, 0xc6,
+ 0x6e, 0xe4,
+ 0x6f, 0xff,
+
+
+
+ //////ASDE
+ 0xfe, 0x01,
+ 0x18, 0x02,
+
+ 0xfe, 0x00,
+
+ 0x98, 0x00,
+ 0x9b, 0x20,
+ 0x9c, 0x80,
+ 0xa4, 0x10,
+ 0xa8, 0xB0,
+ 0xaa, 0x40,
+ 0xa2, 0x23,
+ 0xad, 0x01,
+
+ ///////////////////////////////////////
+ ////////// AEC /////////////////////
+ ///////////////////////////////////////
+ 0xfe, 0x01,
+ 0x9c, 0x02,
+ 0x08, 0xa0,
+ 0x09, 0xe8,
+
+ 0x10, 0x08,
+
+ 0x11, 0x91,
+ 0x12, 0x10,
+ 0x13, 0x5a,// 45
+ 0x15, 0xfc,
+ 0x21, 0xa0,//c0
+ 0x22, 0x60,//40
+ 0x23, 0x30,
+ 0x25, 0x00,
+ 0x24, 0x14,
+
+
+ //0x28, 0x0a,
+
+ 0xfe,0x00 ,
+ 0x05,0x00 ,
+ 0x06,0x80 , // hb
+ 0x07,0x00 ,
+ 0x08,0x60 ,//vb
+
+ 0xfe,0x01 ,
+ 0x29,0x00 , //anti-flicker step [11:8]
+ 0x2a,0x49 , //anti-flicker step [7:0]
+
+ 0x2b,0x02 , //exp level 0 20fps
+ 0x2c,0x48,
+ 0x2d,0x02 , //exp level 14 fps
+ 0x2e,0xda ,
+ 0x2f,0x03 , //exp level 2 10.00fps
+ 0x30,0xfe ,
+ 0x31,0x05 , //exp level 3 8fps
+ 0x32,0x6b ,
+ 0x33,0x20,
+ 0xfe,0x00 ,
+ //////////////////////////////////////
+ ////////////LSC//////////////////////
+ //////////////////////////////////////
+ 0xfe, 0x01,
+ 0xc0, 0x0f,
+ 0xc1, 0x0b,
+ 0xc2, 0x0b,
+ 0xc6, 0x04,
+ 0xc7, 0x00,
+ 0xc8, 0x00,
+ 0xba, 0x1e,
+ 0xbb, 0x1a,
+ 0xbc, 0x1b,
+ 0xb4, 0x1a,
+ 0xb5, 0x12,
+ 0xb6, 0x14,
+ 0xc3, 0x00,
+ 0xc4, 0x00,
+ 0xc5, 0x00,
+ 0xc9, 0x00,
+ 0xca, 0x00,
+ 0xcb, 0x00,
+ 0xbd, 0x0a,
+ 0xbe, 0x00,
+ 0xbf, 0x00,
+ 0xb7, 0x04,
+ 0xb8, 0x00,
+ 0xb9, 0x00,
+ 0xa8, 0x13,
+ 0xa9, 0x0b,
+ 0xaa, 0x0b,
+ 0xab, 0x20,
+ 0xac, 0x1c,
+ 0xad, 0x22,
+ 0xae, 0x0e,
+ 0xaf, 0x05,
+ 0xb0, 0x06,
+ 0xb1, 0x0d,
+ 0xb2, 0x0a,
+ 0xb3, 0x08,
+ 0xa4, 0x00,
+ 0xa5, 0x00,
+ 0xa6, 0x00,
+ 0xa7, 0x00,
+ 0xa1, 0x3c,
+ 0xa2, 0x50,
+ 0xfe, 0x00,
+
+ ///cct
+ #if 0
+ 0xB1, 0x02,
+ 0xB2, 0x02,
+ 0xB3, 0x07,
+ 0xB4, 0xf0,
+ 0xB5, 0x05,
+ 0xB6, 0xf0,
+
+ 0xf1, 0x07,
+
+ 0xf2, 0x01,
+ #endif
+0xB1,0x08 ,// 40
+ 0xB2,0x02 ,
+ 0xB3,0x03 ,//07
+ 0xB4,0xe0 ,
+ 0xB5,0x00 ,//
+ 0xB6,0xf0 ,
+ 0xB7,0x0a ,
+
+ 0xf1,0x07 ,
+ 0xf1,0x07 ,
+ 0xf2,0x01 ,
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0328/gc0328.c b/drivers/media/video/wmt_v4l2/sensors/gc0328/gc0328.c
new file mode 100755
index 00000000..bb709e91
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0328/gc0328.c
@@ -0,0 +1,346 @@
+
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "gc0328.h"
+
+#define sensor_write_array(sd, arr, sz) \
+ cmos_init_8bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write(sd->i2c_addr, reg, val)
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint8_t *regs;
+ size_t size;
+};
+
+struct sensor_flip{
+ int h_flip;
+ int v_flip;
+}gc0328_flip;
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+ //CMOS_WIN_SIZE("QVGA", 320, 240, gc0328_320x240),
+ CMOS_WIN_SIZE("VGA", 640, 480, gc0328_640x480),
+};
+
+#define FILP_REG_INIT_VALUE 0x14
+#define DELAY_INTERVAL 50
+#define RETRY_TIMES 10
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = gc0328_wb_auto;
+ size = ARRAY_SIZE(gc0328_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = gc0328_wb_incandescent;
+ size = ARRAY_SIZE(gc0328_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = gc0328_wb_daylight;
+ size = ARRAY_SIZE(gc0328_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = gc0328_wb_cloudy;
+ size = ARRAY_SIZE(gc0328_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = gc0328_wb_fluorescent;
+ size = ARRAY_SIZE(gc0328_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_scenemode(struct cmos_subdev *sd, enum v4l2_scene_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case SCENE_MODE_AUTO:
+ regs = gc0328_scene_mode_auto;
+ size = ARRAY_SIZE(gc0328_scene_mode_auto);
+ break;
+ case SCENE_MODE_NIGHTSHOT:
+ regs = gc0328_scene_mode_night;
+ size = ARRAY_SIZE(gc0328_scene_mode_night);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_exposure(struct cmos_subdev *sd, int value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case -2:
+ regs = gc0328_exposure_neg6;
+ size = ARRAY_SIZE(gc0328_exposure_neg6);
+ break;
+ case -1:
+ regs = gc0328_exposure_neg3;
+ size = ARRAY_SIZE(gc0328_exposure_neg3);
+ break;
+ case 0:
+ regs = gc0328_exposure_zero;
+ size = ARRAY_SIZE(gc0328_exposure_zero);
+ break;
+ case 1:
+ regs = gc0328_exposure_pos3;
+ size = ARRAY_SIZE(gc0328_exposure_pos3);
+ break;
+ case 2:
+ regs = gc0328_exposure_pos6;
+ size = ARRAY_SIZE(gc0328_exposure_pos6);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data;
+
+ sensor_write(sd, 0xfe, 0x00); // set page0
+
+ //data = sensor_read(sd, 0x17);
+
+ switch (value) {
+ case 0:
+ gc0328_flip.h_flip = 0x00;
+ data= (0x14&~0x01);
+ break;
+ case 1:
+ gc0328_flip.h_flip =0x01;
+ data = (0x14|0x01);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ data|=gc0328_flip.v_flip;
+ sensor_write(sd, 0x17, data);
+ msleep(50);
+
+ return 0;
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data;
+
+ sensor_write(sd, 0xfe, 0x00); // set page0
+
+ //data = sensor_read(sd, 0x17);
+
+ switch (value) {
+ case 0:
+ gc0328_flip.v_flip = 0x00;
+ data = (0x14&~0x02);
+ break;
+ case 1:
+ gc0328_flip.v_flip = 0x02;
+ data = (0x14|0x02);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ data|=gc0328_flip.h_flip;
+ sensor_write(sd, 0x17, data);
+ msleep(50);
+
+ return 0;
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ case V4L2_CID_EXPOSURE:
+ return v4l2_ctrl_query_fill(qc, -2, 2, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return sensor_s_scenemode(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return sensor_s_exposure(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ default:
+ case WMT_V4L2_CID_CAMERA_ANTIBANDING:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win) {
+ pr_err("%s, s_mbus_fmt failed, width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, win->regs, win->size);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return 0;
+}
+
+static int sensor_enum_framesizes(struct cmos_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ const struct cmos_win_size *win = &cmos_supported_win_sizes[index];
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ return (sensor_read(sd, 0xf0) == sd->id) ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ if (sensor_identify(sd)) {
+ return -1;
+ }
+
+ gc0328_flip.h_flip=0;
+ gc0328_flip.v_flip=0;
+ sensor_write_array(sd, gc0328_default_regs_init,
+ ARRAY_SIZE(gc0328_default_regs_init));
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ //sensor_write_array(sd, gc0328_default_regs_exit,
+ // ARRAY_SIZE(gc0328_default_regs_exit));
+ return 0;
+}
+
+static struct cmos_subdev_ops gc0328_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+ .enum_framesizes = sensor_enum_framesizes,
+};
+
+struct cmos_subdev gc0328 = {
+ .name = "gc0328",
+ .i2c_addr = 0x21,
+ .id = 0x9d,
+ .max_width = 640,
+ .max_height = 480,
+ .ops = &gc0328_ops,
+};
+
+#if 0
+static int __init gc0328_init(void)
+{
+ return cmos_register_sudbdev(&gc0328);
+}
+
+static void __exit gc0328_exit(void)
+{
+ return cmos_unregister_subdev(&gc0328);
+}
+
+module_init(gc0328_init);
+module_exit(gc0328_exit);
+
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0328/gc0328.h b/drivers/media/video/wmt_v4l2/sensors/gc0328/gc0328.h
new file mode 100755
index 00000000..05e49ba5
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0328/gc0328.h
@@ -0,0 +1,604 @@
+#ifndef GC0328_H
+#define GC0328_H
+
+
+// Scene Mode
+unsigned char gc0328_scene_mode_auto[] = {
+ 0xfa, 0x00,
+};
+
+unsigned char gc0328_scene_mode_night[] = {
+ 0xfa, 0x11,
+};
+
+
+// White Balance
+unsigned char gc0328_wb_auto [] = {
+ 0xfe, 0x00,
+ 0x77, 0x60,//57
+ 0x78, 0x40,//4
+ 0x79, 0x60,//45
+ 0x42, 0xfe,
+};
+
+unsigned char gc0328_wb_incandescent [] = {
+ //bai re guang
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x48,
+ 0x78, 0x40,
+ 0x79, 0x5c,
+};
+
+unsigned char gc0328_wb_fluorescent [] = {
+ //ri guang deng
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x40,
+ 0x78, 0x42,
+ 0x79, 0x50,
+};
+
+unsigned char gc0328_wb_daylight [] = {
+ //tai yang guang
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x74,
+ 0x78, 0x52,
+ 0x79, 0x40,
+};
+
+unsigned char gc0328_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x8c,
+ 0x78, 0x50,
+ 0x79, 0x40,
+};
+
+unsigned char gc0328_wb_tungsten [] = {
+ //wu si deng
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x40,
+ 0x78, 0x54,
+ 0x79, 0x70
+};
+
+
+// Exposure
+unsigned char gc0328_exposure_neg6[] = {
+ 0xfe, 0x00,
+ 0xd5, 0xe0,
+ 0xfe, 0x01,
+ 0x13, 0x35,
+ 0xfe, 0x00
+};
+
+unsigned char gc0328_exposure_neg3[] = {
+ 0xfe, 0x00,
+ 0xd5, 0xf0,
+ 0xfe, 0x01,
+ 0x13, 0x37,
+ 0xfe, 0x00
+};
+
+unsigned char gc0328_exposure_zero[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x00,
+ 0xfe, 0x01,
+ 0x13, 0x38,
+ 0xfe, 0x00
+};
+
+unsigned char gc0328_exposure_pos3[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x10,
+ 0xfe, 0x01,
+ 0x13, 0x55,
+ 0xfe, 0x00
+};
+
+unsigned char gc0328_exposure_pos6[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x20,
+ 0xfe, 0x01,
+ 0x13, 0x65,
+ 0xfe, 0x00
+};
+
+
+// Color Effect
+unsigned char gc0328_colorfx_none[] = {
+
+ 0x43, 0x00
+};
+
+unsigned char gc0328_colorfx_bw[] = {
+
+ 0x43, 0x02,
+ 0xda, 0xd0,
+ 0xdb, 0x28
+};
+
+unsigned char gc0328_colorfx_sepia[] = {
+ 0x43, 0x02,
+ 0xda, 0xc0,
+ 0xdb, 0xc0
+};
+
+unsigned char gc0328_colorfx_negative[] = {
+
+ 0x43, 0x01
+};
+
+unsigned char gc0328_colorfx_emboss[] = {
+ 0x43, 0x02,
+ 0xda, 0x50,
+ 0xdb, 0xe0
+};
+
+unsigned char gc0328_colorfx_sketch[] = {
+ 0x43, 0x02,
+ 0xda, 0x00,
+ 0xdb, 0x00
+};
+
+unsigned char gc0328_colorfx_sky_blue[] = {
+ 0x43,0x02,
+ 0xda,0x50,
+ 0xdb,0xe0,
+};
+
+unsigned char gc0328_colorfx_grass_green[] = {
+ 0x43,0x02,
+ 0xda,0xc0,
+ 0xdb,0xc0,
+};
+
+unsigned char gc0328_colorfx_skin_whiten[] = {
+};
+
+unsigned char gc0328_colorfx_vivid[] = {
+};
+
+// Brightness
+unsigned char gc0328_brightness_neg4[] = {
+};
+
+unsigned char gc0328_brightness_neg3[] = {
+};
+
+unsigned char gc0328_brightness_neg2[] = {
+};
+
+unsigned char gc0328_brightness_neg1[] = {
+};
+
+unsigned char gc0328_brightness_zero[] = {
+};
+
+unsigned char gc0328_brightness_pos1[] = {
+};
+
+unsigned char gc0328_brightness_pos2[] = {
+};
+
+unsigned char gc0328_brightness_pos3[] = {
+};
+
+unsigned char gc0328_brightness_pos4[] = {
+};
+
+// Contrast
+unsigned char gc0328_contrast_neg4[] = {
+};
+
+unsigned char gc0328_contrast_neg3[] = {
+};
+
+unsigned char gc0328_contrast_neg2[] = {
+};
+
+unsigned char gc0328_contrast_neg1[] = {
+};
+
+unsigned char gc0328_contrast_zero[] = {
+};
+
+unsigned char gc0328_contrast_pos1[] = {
+};
+
+unsigned char gc0328_contrast_pos2[] = {
+};
+
+unsigned char gc0328_contrast_pos3[] = {
+};
+
+unsigned char gc0328_contrast_pos4[] = {
+};
+
+// Saturation
+unsigned char gc0328_saturation_neg4[] = {
+};
+
+unsigned char gc0328_saturation_neg3[] = {
+};
+
+unsigned char gc0328_saturation_neg2[] = {
+};
+
+unsigned char gc0328_saturation_neg1[] = {
+};
+
+unsigned char gc0328_saturation_zero[] = {
+};
+
+unsigned char gc0328_saturation_pos1[] = {
+};
+
+unsigned char gc0328_saturation_pos2[] = {
+};
+
+unsigned char gc0328_saturation_pos3[] = {
+};
+
+unsigned char gc0328_saturation_pos4[] = {
+};
+
+
+// Resolution
+
+// QCIF
+unsigned char gc0328_176x144[]={
+};
+
+unsigned char gc0328_320x240[]={
+
+};
+
+unsigned char gc0328_640x480[]={
+};
+
+
+// Initiliztion
+unsigned char gc0328_default_regs_init[] = {
+ 0xfe, 0x80,
+ 0xfe, 0x80,
+ 0xfc, 0x16,
+ 0xfc, 0x16,
+ 0xfc, 0x16,
+ 0xfc, 0x16,
+
+ //0x42, 0x02,
+ 0xfa, 0x11,
+ 0x4f, 0x00,
+ 0x03, 0x06,
+ 0x04, 0x24,
+ 0x4f, 0x01,
+
+ 0xfe, 0x01,
+ 0x4f, 0x00,
+ 0x4c, 0x01,
+ 0xfe, 0x00,
+ //////////////////////////////
+ //AWB
+ ////////////////////////////////
+0xfe, 0x01,
+ 0x51, 0x80,//20
+ 0x52, 0x12,// 16 //1f
+ 0x53, 0x80,//40
+ 0x54, 0x60,//9f
+ 0x55, 0x04,//01
+ 0x56, 0x0e,//00
+ 0x5b, 0x02,//02
+ 0x61, 0xdc,//R2G_stand0[7:0]
+ 0x62, 0xca,//B2G_stand0[7:0]
+
+ 0x70, 0xf5,
+ 0x71, 0x0a,
+ 0x72, 0x18,//y2c
+ 0x73, 0x30,//28 //20, 0x//AWB_C_inter
+ 0x74, 0x58,//40 //20, 0x// AWB_C_max
+ 0x7c, 0x71,//AWB speed,AWB margin
+ 0x7d, 0x00,//10 //AWB every N
+ 0x76, 0x8f,//move mode en,Move mode TH
+ 0x79, 0x00,//00
+
+
+
+ ////AWB////
+0x4f, 0x00,
+ 0x4d, 0x34,
+ 0x4e, 0x04,
+ 0x4e, 0x02,
+ 0x4e, 0x02,
+ 0x4d, 0x43,
+ 0x4e, 0x04,
+ 0x4e, 0x04,//D50
+ 0x4e, 0x02,// d65 add
+ 0x4e, 0x02,// d65 add
+ 0x4d, 0x53,
+ 0x4e, 0x08,//cwf
+ 0x4e, 0x04,
+ 0x4e, 0x02,//D65
+ 0x4e, 0x02,//D65
+ 0x4d, 0x63,
+ 0x4e, 0x10,//tl84
+ 0x4d, 0x72,
+ 0x4e, 0x20,
+ 0x4e, 0x20,
+ 0x4d, 0x82,
+ 0x4e, 0x20,//A
+ 0x4e, 0x20,
+ 0x4d, 0x92,
+ 0x4e, 0x20,//A
+ 0x4e, 0x20,
+ 0x4d, 0xa0,
+ 0x4e, 0x40,// H
+ 0x4e, 0x40,
+ 0x4e, 0x40,
+ 0x4f, 0x01,
+ 0x50, 0x84,//80//
+ 0xfe, 0x00,//page0
+
+ ////////////////////////////////////////////////
+ //////////// BLK //////////////////////
+ ////////////////////////////////////////////////
+ 0x27, 0xf7,
+ 0x28, 0x7F,
+ 0x29, 0x20,
+ 0x33, 0x20,
+ 0x34, 0x20,
+ 0x35, 0x20,
+ 0x36, 0x20,
+ 0x32, 0x08,
+
+ 0x47, 0x00,
+ 0x48, 0x00,
+
+ //////////////////////////////////////////////
+ ////////// page 0 ////////////////////////
+ //////////////////////////////////////////////
+ 0xfe, 0x00,
+ //0x05, 0x00,
+ //0x06, 0xde,
+ //0x07, 0x00,
+ //0x08, 0xa7,
+
+ 0x0d, 0x01,
+ 0x0e, 0xe8,
+ 0x0f, 0x02,
+ 0x10, 0x88,
+ 0x09, 0x00,
+ 0x0a, 0x00,
+ 0x0b, 0x00,
+ 0x0c, 0x00,
+ 0x16, 0x00,
+ 0x17, 0x14,
+ 0x18, 0x0e,
+ 0x19, 0x06,
+
+ 0x1b, 0x48,
+ 0x1f, 0xC8,
+ 0x20, 0x01,
+ 0x21, 0x78,
+ 0x22, 0xb0,
+ 0x23, 0x06,
+ 0x24, 0x16,
+ 0x26, 0x00,
+
+ 0x50, 0x01,
+
+ //global gain for range
+ 0x70, 0x45,
+
+ //////////////////////////////////////
+ //////////// block enable ///
+ //////////////////////////////////////
+ 0x40, 0x7f,
+ 0x41, 0x24,
+
+ 0x42, 0xff,
+
+ 0x45, 0x00,
+ 0x44, 0x02,
+ 0x46, 0x02, //0x03
+
+ 0x4b, 0x01,
+ 0x50, 0x01,
+
+ //DN & EEINTP
+ 0x7e, 0x0a,
+ 0x7f, 0x03,
+ 0x81, 0x15,
+ 0x82, 0x85,
+ 0x83, 0x02,
+ 0x84, 0xe5,
+ 0x90, 0xac,
+ 0x92, 0x02,
+ 0x94, 0x04,//02
+ 0x95, 0x76,//43
+
+
+ ///////YCP
+ 0xd1, 0x30,//38
+ 0xd2, 0x38,//38
+ 0xd3, 0x38,//38
+ 0xdd, 0x54,
+ 0xde, 0x38,
+ 0xe4, 0x88,
+ 0xe5, 0x40,
+ 0xd7, 0x0e,
+
+ //////////////////////////////
+ //////////////// GAMMA //////
+ //////////////////////////////
+ //rgb gamma
+ 0xBF, 0x0E,
+ 0xc0, 0x1C,
+ 0xc1, 0x34,
+ 0xc2, 0x48,
+ 0xc3, 0x5A,
+ 0xc4, 0x6B,
+ 0xc5, 0x7B,
+ 0xc6, 0x95,
+ 0xc7, 0xAB,
+ 0xc8, 0xBF,
+ 0xc9, 0xCE,
+ 0xcA, 0xD9,
+ 0xcB, 0xE4,
+ 0xcC, 0xEC,
+ 0xcD, 0xF7,
+ 0xcE, 0xFD,
+ 0xcF, 0xFF,
+
+ ///Y gamma
+ 0xfe, 0x00,
+ 0x63, 0x00,
+ 0x64, 0x05,
+ 0x65, 0x0b,
+ 0x66, 0x19,
+ 0x67, 0x2e,
+ 0x68, 0x40,
+ 0x69, 0x54,
+ 0x6a, 0x66,
+ 0x6b, 0x86,
+ 0x6c, 0xa7,
+ 0x6d, 0xc6,
+ 0x6e, 0xe4,
+ 0x6f, 0xff,
+
+
+
+ //////ASDE
+ 0xfe, 0x01,
+ 0x18, 0x02,
+
+ 0xfe, 0x00,
+
+ 0x98, 0x00,
+ 0x9b, 0x20,
+ 0x9c, 0x80,
+ 0xa4, 0x10,
+ 0xa8, 0xB0,
+ 0xaa, 0x40,
+ 0xa2, 0x23,
+ 0xad, 0x01,
+
+ ///////////////////////////////////////
+ ////////// AEC /////////////////////
+ ///////////////////////////////////////
+ 0xfe, 0x01,
+ 0x9c, 0x02,
+ 0x08, 0xa0,
+ 0x09, 0xe8,
+
+ 0x10, 0x08,
+
+ 0x11, 0x91,
+ 0x12, 0x10,
+ 0x13, 0x30,// 45
+ 0x15, 0xfc,
+ 0x21, 0xa0,//c0
+ 0x22, 0x40,//60
+ 0x23, 0x30,
+ 0x25, 0x00,
+ 0x24, 0x14,
+
+
+ //0x28, 0x0a,
+
+ 0xfe,0x00 ,
+ 0x05,0x00 ,
+ 0x06,0x80 , // hb
+ 0x07,0x00 ,
+ 0x08,0x60 ,//vb
+
+ 0xfe,0x01 ,
+ 0x29,0x00 , //anti-flicker step [11:8]
+ 0x2a,0x49 , //anti-flicker step [7:0]
+
+ 0x2b,0x02 , //exp level 0 20fps
+ 0x2c,0x48,
+ 0x2d,0x02 , //exp level 14 fps
+ 0x2e,0xda ,
+ 0x2f,0x03 , //exp level 2 10.00fps
+ 0x30,0xfe ,
+ 0x31,0x05 , //exp level 3 8fps
+ 0x32,0x6b ,
+ 0xfe,0x00 ,
+ //////////////////////////////////////
+ ////////////LSC//////////////////////
+ //////////////////////////////////////
+ 0xfe, 0x01,
+ 0xc0, 0x0f,
+ 0xc1, 0x0b,
+ 0xc2, 0x0b,
+ 0xc6, 0x04,
+ 0xc7, 0x00,
+ 0xc8, 0x00,
+ 0xba, 0x1e,
+ 0xbb, 0x1a,
+ 0xbc, 0x1b,
+ 0xb4, 0x1a,
+ 0xb5, 0x12,
+ 0xb6, 0x14,
+ 0xc3, 0x00,
+ 0xc4, 0x00,
+ 0xc5, 0x00,
+ 0xc9, 0x00,
+ 0xca, 0x00,
+ 0xcb, 0x00,
+ 0xbd, 0x0a,
+ 0xbe, 0x00,
+ 0xbf, 0x00,
+ 0xb7, 0x04,
+ 0xb8, 0x00,
+ 0xb9, 0x00,
+ 0xa8, 0x13,
+ 0xa9, 0x0b,
+ 0xaa, 0x0b,
+ 0xab, 0x20,
+ 0xac, 0x1c,
+ 0xad, 0x22,
+ 0xae, 0x0e,
+ 0xaf, 0x05,
+ 0xb0, 0x06,
+ 0xb1, 0x0d,
+ 0xb2, 0x0a,
+ 0xb3, 0x08,
+ 0xa4, 0x00,
+ 0xa5, 0x00,
+ 0xa6, 0x00,
+ 0xa7, 0x00,
+ 0xa1, 0x3c,
+ 0xa2, 0x50,
+ 0xfe, 0x00,
+
+ ///cct
+ #if 0
+ 0xB1, 0x02,
+ 0xB2, 0x02,
+ 0xB3, 0x07,
+ 0xB4, 0xf0,
+ 0xB5, 0x05,
+ 0xB6, 0xf0,
+
+ 0xf1, 0x07,
+
+ 0xf2, 0x01,
+ #endif
+0xB1,0x08 ,// 40
+ 0xB2,0x02 ,
+ 0xB3,0x03 ,//07
+ 0xB4,0xe0 ,
+ 0xB5,0x00 ,//
+ 0xB6,0xf0 ,
+ 0xB7,0x0a ,
+
+ 0xf1,0x07 ,
+ 0xf1,0x07 ,
+ 0xf2,0x01 ,
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0328/gc0328_kyt111_v7.h b/drivers/media/video/wmt_v4l2/sensors/gc0328/gc0328_kyt111_v7.h
new file mode 100755
index 00000000..d41427a7
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0328/gc0328_kyt111_v7.h
@@ -0,0 +1,604 @@
+#ifndef GC0328_H
+#define GC0328_H
+
+
+// Scene Mode
+unsigned char gc0328_scene_mode_auto[] = {
+ 0xfa, 0x00,
+};
+
+unsigned char gc0328_scene_mode_night[] = {
+ 0xfa, 0x11,
+};
+
+
+// White Balance
+unsigned char gc0328_wb_auto [] = {
+ 0xfe, 0x00,
+ 0x77, 0x60,//57
+ 0x78, 0x40,//40
+ 0x79, 0x60,//45
+ 0x42, 0xfe,
+};
+
+unsigned char gc0328_wb_incandescent [] = {
+ //bai re guang
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x48,
+ 0x78, 0x40,
+ 0x79, 0x5c,
+};
+
+unsigned char gc0328_wb_fluorescent [] = {
+ //ri guang deng
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x40,
+ 0x78, 0x42,
+ 0x79, 0x50,
+};
+
+unsigned char gc0328_wb_daylight [] = {
+ //tai yang guang
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x74,
+ 0x78, 0x52,
+ 0x79, 0x40,
+};
+
+unsigned char gc0328_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x8c,
+ 0x78, 0x50,
+ 0x79, 0x40,
+};
+
+unsigned char gc0328_wb_tungsten [] = {
+ //wu si deng
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x40,
+ 0x78, 0x54,
+ 0x79, 0x70
+};
+
+
+// Exposure
+unsigned char gc0328_exposure_neg6[] = {
+ 0xfe, 0x00,
+ 0xd5, 0xe0,
+ 0xfe, 0x01,
+ 0x13, 0x35,
+ 0xfe, 0x00
+};
+
+unsigned char gc0328_exposure_neg3[] = {
+ 0xfe, 0x00,
+ 0xd5, 0xf0,
+ 0xfe, 0x01,
+ 0x13, 0x37,
+ 0xfe, 0x00
+};
+
+unsigned char gc0328_exposure_zero[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x00,
+ 0xfe, 0x01,
+ 0x13, 0x38,
+ 0xfe, 0x00
+};
+
+unsigned char gc0328_exposure_pos3[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x10,
+ 0xfe, 0x01,
+ 0x13, 0x55,
+ 0xfe, 0x00
+};
+
+unsigned char gc0328_exposure_pos6[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x20,
+ 0xfe, 0x01,
+ 0x13, 0x65,
+ 0xfe, 0x00
+};
+
+
+// Color Effect
+unsigned char gc0328_colorfx_none[] = {
+
+ 0x43, 0x00
+};
+
+unsigned char gc0328_colorfx_bw[] = {
+
+ 0x43, 0x02,
+ 0xda, 0xd0,
+ 0xdb, 0x28
+};
+
+unsigned char gc0328_colorfx_sepia[] = {
+ 0x43, 0x02,
+ 0xda, 0xc0,
+ 0xdb, 0xc0
+};
+
+unsigned char gc0328_colorfx_negative[] = {
+
+ 0x43, 0x01
+};
+
+unsigned char gc0328_colorfx_emboss[] = {
+ 0x43, 0x02,
+ 0xda, 0x50,
+ 0xdb, 0xe0
+};
+
+unsigned char gc0328_colorfx_sketch[] = {
+ 0x43, 0x02,
+ 0xda, 0x00,
+ 0xdb, 0x00
+};
+
+unsigned char gc0328_colorfx_sky_blue[] = {
+ 0x43,0x02,
+ 0xda,0x50,
+ 0xdb,0xe0,
+};
+
+unsigned char gc0328_colorfx_grass_green[] = {
+ 0x43,0x02,
+ 0xda,0xc0,
+ 0xdb,0xc0,
+};
+
+unsigned char gc0328_colorfx_skin_whiten[] = {
+};
+
+unsigned char gc0328_colorfx_vivid[] = {
+};
+
+// Brightness
+unsigned char gc0328_brightness_neg4[] = {
+};
+
+unsigned char gc0328_brightness_neg3[] = {
+};
+
+unsigned char gc0328_brightness_neg2[] = {
+};
+
+unsigned char gc0328_brightness_neg1[] = {
+};
+
+unsigned char gc0328_brightness_zero[] = {
+};
+
+unsigned char gc0328_brightness_pos1[] = {
+};
+
+unsigned char gc0328_brightness_pos2[] = {
+};
+
+unsigned char gc0328_brightness_pos3[] = {
+};
+
+unsigned char gc0328_brightness_pos4[] = {
+};
+
+// Contrast
+unsigned char gc0328_contrast_neg4[] = {
+};
+
+unsigned char gc0328_contrast_neg3[] = {
+};
+
+unsigned char gc0328_contrast_neg2[] = {
+};
+
+unsigned char gc0328_contrast_neg1[] = {
+};
+
+unsigned char gc0328_contrast_zero[] = {
+};
+
+unsigned char gc0328_contrast_pos1[] = {
+};
+
+unsigned char gc0328_contrast_pos2[] = {
+};
+
+unsigned char gc0328_contrast_pos3[] = {
+};
+
+unsigned char gc0328_contrast_pos4[] = {
+};
+
+// Saturation
+unsigned char gc0328_saturation_neg4[] = {
+};
+
+unsigned char gc0328_saturation_neg3[] = {
+};
+
+unsigned char gc0328_saturation_neg2[] = {
+};
+
+unsigned char gc0328_saturation_neg1[] = {
+};
+
+unsigned char gc0328_saturation_zero[] = {
+};
+
+unsigned char gc0328_saturation_pos1[] = {
+};
+
+unsigned char gc0328_saturation_pos2[] = {
+};
+
+unsigned char gc0328_saturation_pos3[] = {
+};
+
+unsigned char gc0328_saturation_pos4[] = {
+};
+
+
+// Resolution
+
+// QCIF
+unsigned char gc0328_176x144[]={
+};
+
+unsigned char gc0328_320x240[]={
+
+};
+
+unsigned char gc0328_640x480[]={
+};
+
+
+// Initiliztion
+unsigned char gc0328_default_regs_init[] = {
+ 0xfe, 0x80,
+ 0xfe, 0x80,
+ 0xfc, 0x16,
+ 0xfc, 0x16,
+ 0xfc, 0x16,
+ 0xfc, 0x16,
+
+ //0x42, 0x02,
+ 0xfa, 0x11,
+ 0x4f, 0x00,
+ 0x03, 0x06,
+ 0x04, 0x24,
+ 0x4f, 0x01,
+
+ 0xfe, 0x01,
+ 0x4f, 0x00,
+ 0x4c, 0x01,
+ 0xfe, 0x00,
+ //////////////////////////////
+ //AWB
+ ////////////////////////////////
+0xfe, 0x01,
+ 0x51, 0x80,//20
+ 0x52, 0x12,// 16 //1f
+ 0x53, 0x80,//40
+ 0x54, 0x60,//9f
+ 0x55, 0x04,//01
+ 0x56, 0x0e,//00
+ 0x5b, 0x02,//02
+ 0x61, 0xdc,//R2G_stand0[7:0]
+ 0x62, 0xca,//B2G_stand0[7:0]
+
+ 0x70, 0xf5,
+ 0x71, 0x0a,
+ 0x72, 0x18,//y2c
+ 0x73, 0x30,//28 //20, 0x//AWB_C_inter
+ 0x74, 0x58,//40 //20, 0x// AWB_C_max
+ 0x7c, 0x71,//AWB speed,AWB margin
+ 0x7d, 0x00,//10 //AWB every N
+ 0x76, 0x8f,//move mode en,Move mode TH
+ 0x79, 0x00,//00
+
+
+
+ ////AWB////
+0x4f, 0x00,
+ 0x4d, 0x34,
+ 0x4e, 0x04,
+ 0x4e, 0x02,
+ 0x4e, 0x02,
+ 0x4d, 0x43,
+ 0x4e, 0x04,
+ 0x4e, 0x04,//D50
+ 0x4e, 0x02,// d65 add
+ 0x4e, 0x02,// d65 add
+ 0x4d, 0x53,
+ 0x4e, 0x08,//cwf
+ 0x4e, 0x04,
+ 0x4e, 0x02,//D65
+ 0x4e, 0x02,//D65
+ 0x4d, 0x63,
+ 0x4e, 0x10,//tl84
+ 0x4d, 0x72,
+ 0x4e, 0x20,
+ 0x4e, 0x20,
+ 0x4d, 0x82,
+ 0x4e, 0x20,//A
+ 0x4e, 0x20,
+ 0x4d, 0x92,
+ 0x4e, 0x20,//A
+ 0x4e, 0x20,
+ 0x4d, 0xa0,
+ 0x4e, 0x40,// H
+ 0x4e, 0x40,
+ 0x4e, 0x40,
+ 0x4f, 0x01,
+ 0x50, 0x84,//80//
+ 0xfe, 0x00,//page0
+
+ ////////////////////////////////////////////////
+ //////////// BLK //////////////////////
+ ////////////////////////////////////////////////
+ 0x27, 0xf7,
+ 0x28, 0x7F,
+ 0x29, 0x20,
+ 0x33, 0x20,
+ 0x34, 0x20,
+ 0x35, 0x20,
+ 0x36, 0x20,
+ 0x32, 0x08,
+
+ 0x47, 0x00,
+ 0x48, 0x00,
+
+ //////////////////////////////////////////////
+ ////////// page 0 ////////////////////////
+ //////////////////////////////////////////////
+ 0xfe, 0x00,
+ //0x05, 0x00,
+ //0x06, 0xde,
+ //0x07, 0x00,
+ //0x08, 0xa7,
+
+ 0x0d, 0x01,
+ 0x0e, 0xe8,
+ 0x0f, 0x02,
+ 0x10, 0x88,
+ 0x09, 0x00,
+ 0x0a, 0x00,
+ 0x0b, 0x00,
+ 0x0c, 0x00,
+ 0x16, 0x00,
+ 0x17, 0x14,
+ 0x18, 0x0e,
+ 0x19, 0x06,
+
+ 0x1b, 0x48,
+ 0x1f, 0xC8,
+ 0x20, 0x01,
+ 0x21, 0x78,
+ 0x22, 0xb0,
+ 0x23, 0x06,
+ 0x24, 0x16,
+ 0x26, 0x00,
+
+ 0x50, 0x01,
+
+ //global gain for range
+ 0x70, 0x44, //45 2013-7-24 dustin
+
+ //////////////////////////////////////
+ //////////// block enable ///
+ //////////////////////////////////////
+ 0x40, 0x7f,
+ 0x41, 0x24,
+
+ 0x42, 0xff,
+
+ 0x45, 0x00,
+ 0x44, 0x02,
+ 0x46, 0x02, //0x03
+
+ 0x4b, 0x01,
+ 0x50, 0x01,
+
+ //DN & EEINTP
+ 0x7e, 0x0a,
+ 0x7f, 0x03,
+ 0x81, 0x15,
+ 0x82, 0x85,
+ 0x83, 0x02,
+ 0x84, 0xe5,
+ 0x90, 0xac,
+ 0x92, 0x02,
+ 0x94, 0x04,//02
+ 0x95, 0x76,//43
+
+
+ ///////YCP
+ 0xd1, 0x30,//38
+ 0xd2, 0x38,//38
+ 0xd3, 0x38,//38
+ 0xdd, 0x2f,//54 2013-7-24 dustin
+ 0xde, 0x38,
+ 0xe4, 0x88,
+ 0xe5, 0x40,
+ 0xd7, 0x0e,
+
+ //////////////////////////////
+ //////////////// GAMMA //////
+ //////////////////////////////
+ //rgb gamma
+ 0xBF, 0x0E,
+ 0xc0, 0x1C,
+ 0xc1, 0x34,
+ 0xc2, 0x48,
+ 0xc3, 0x5A,
+ 0xc4, 0x6B,
+ 0xc5, 0x7B,
+ 0xc6, 0x95,
+ 0xc7, 0xAB,
+ 0xc8, 0xBF,
+ 0xc9, 0xCE,
+ 0xcA, 0xD9,
+ 0xcB, 0xE4,
+ 0xcC, 0xEC,
+ 0xcD, 0xF7,
+ 0xcE, 0xFD,
+ 0xcF, 0xFF,
+
+ ///Y gamma
+ 0xfe, 0x00,
+ 0x63, 0x00,
+ 0x64, 0x05,
+ 0x65, 0x0b,
+ 0x66, 0x19,
+ 0x67, 0x2e,
+ 0x68, 0x40,
+ 0x69, 0x54,
+ 0x6a, 0x66,
+ 0x6b, 0x86,
+ 0x6c, 0xa7,
+ 0x6d, 0xc6,
+ 0x6e, 0xe4,
+ 0x6f, 0xff,
+
+
+
+ //////ASDE
+ 0xfe, 0x01,
+ 0x18, 0x02,
+
+ 0xfe, 0x00,
+
+ 0x98, 0x00,
+ 0x9b, 0x20,
+ 0x9c, 0x80,
+ 0xa4, 0x10,
+ 0xa8, 0xB0,
+ 0xaa, 0x40,
+ 0xa2, 0x23,
+ 0xad, 0x01,
+
+ ///////////////////////////////////////
+ ////////// AEC /////////////////////
+ ///////////////////////////////////////
+ 0xfe, 0x01,
+ 0x9c, 0x02,
+ 0x08, 0xa0,
+ 0x09, 0xe8,
+
+ 0x10, 0x08,
+
+ 0x11, 0x91,
+ 0x12, 0x10,
+ 0x13, 0x30,// 45
+ 0x15, 0xfc,
+ 0x21, 0xa0,//c0
+ 0x22, 0x40,//60
+ 0x23, 0x30,
+ 0x25, 0x00,
+ 0x24, 0x14,
+
+
+ //0x28, 0x0a,
+
+ 0xfe,0x00 ,
+ 0x05,0x00 ,
+ 0x06,0x80 , // hb
+ 0x07,0x00 ,
+ 0x08,0x60 ,//vb
+
+ 0xfe,0x01 ,
+ 0x29,0x00 , //anti-flicker step [11:8]
+ 0x2a,0x49 , //anti-flicker step [7:0]
+
+ 0x2b,0x02 , //exp level 0 248
+ 0x2c,0x48,
+ 0x2d,0x03 , //exp level 1 2da
+ 0x2e,0x6c ,
+ 0x2f,0x04 , //exp level 2 3fe
+ 0x30,0x47 ,
+ 0x31,0x05 , //exp level 3 56b
+ 0x32,0x6b ,
+ 0xfe,0x00 ,
+ //////////////////////////////////////
+ ////////////LSC//////////////////////
+ //////////////////////////////////////
+ 0xfe, 0x01,
+ 0xc0, 0x0f,
+ 0xc1, 0x0b,
+ 0xc2, 0x0b,
+ 0xc6, 0x04,
+ 0xc7, 0x00,
+ 0xc8, 0x00,
+ 0xba, 0x1e,
+ 0xbb, 0x1a,
+ 0xbc, 0x1b,
+ 0xb4, 0x1a,
+ 0xb5, 0x12,
+ 0xb6, 0x14,
+ 0xc3, 0x00,
+ 0xc4, 0x00,
+ 0xc5, 0x00,
+ 0xc9, 0x00,
+ 0xca, 0x00,
+ 0xcb, 0x00,
+ 0xbd, 0x0a,
+ 0xbe, 0x00,
+ 0xbf, 0x00,
+ 0xb7, 0x04,
+ 0xb8, 0x00,
+ 0xb9, 0x00,
+ 0xa8, 0x13,
+ 0xa9, 0x0b,
+ 0xaa, 0x0b,
+ 0xab, 0x20,
+ 0xac, 0x1c,
+ 0xad, 0x22,
+ 0xae, 0x0e,
+ 0xaf, 0x05,
+ 0xb0, 0x06,
+ 0xb1, 0x0d,
+ 0xb2, 0x0a,
+ 0xb3, 0x08,
+ 0xa4, 0x00,
+ 0xa5, 0x00,
+ 0xa6, 0x00,
+ 0xa7, 0x00,
+ 0xa1, 0x3c,
+ 0xa2, 0x50,
+ 0xfe, 0x00,
+
+ ///cct
+ #if 0
+ 0xB1, 0x02,
+ 0xB2, 0x02,
+ 0xB3, 0x07,
+ 0xB4, 0xf0,
+ 0xB5, 0x05,
+ 0xB6, 0xf0,
+
+ 0xf1, 0x07,
+
+ 0xf2, 0x01,
+ #endif
+0xB1,0x08 ,// 40
+ 0xB2,0x02 ,
+ 0xB3,0x03 ,//07
+ 0xB4,0xe0 ,
+ 0xB5,0x00 ,//
+ 0xB6,0xf0 ,
+ 0xB7,0x0a ,
+
+ 0xf1,0x07 ,
+ 0xf1,0x07 ,
+ 0xf2,0x01 ,
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0328/gc0328_ra474a_v7.h b/drivers/media/video/wmt_v4l2/sensors/gc0328/gc0328_ra474a_v7.h
new file mode 100755
index 00000000..8f0f607a
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0328/gc0328_ra474a_v7.h
@@ -0,0 +1,624 @@
+#ifndef GC0328_H
+#define GC0328_H
+
+
+// Scene Mode
+unsigned char gc0328_scene_mode_auto[] = {
+ 0xfa, 0x00,
+};
+
+unsigned char gc0328_scene_mode_night[] = {
+ 0xfa, 0x11,
+};
+
+
+// White Balance
+unsigned char gc0328_wb_auto [] = {
+ 0xfe, 0x00,
+ 0x77, 0x60,//57
+ 0x78, 0x40,//4
+ 0x79, 0x60,//45
+ 0x42, 0xfe,
+};
+
+unsigned char gc0328_wb_incandescent [] = {
+ //bai re guang
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x48,
+ 0x78, 0x40,
+ 0x79, 0x5c,
+};
+
+unsigned char gc0328_wb_fluorescent [] = {
+ //ri guang deng
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x40,
+ 0x78, 0x42,
+ 0x79, 0x50,
+};
+
+unsigned char gc0328_wb_daylight [] = {
+ //tai yang guang
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x74,
+ 0x78, 0x52,
+ 0x79, 0x40,
+};
+
+unsigned char gc0328_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x8c,
+ 0x78, 0x50,
+ 0x79, 0x40,
+};
+
+unsigned char gc0328_wb_tungsten [] = {
+ //wu si deng
+ 0xfe, 0x00,
+ 0x42, 0xfd,
+ 0x77, 0x40,
+ 0x78, 0x54,
+ 0x79, 0x70
+};
+
+
+// Exposure
+unsigned char gc0328_exposure_neg6[] = {
+ 0xfe, 0x00,
+ 0xd5, 0xe0,
+ 0xfe, 0x01,
+ 0x13, 0x35,
+ 0xfe, 0x00
+};
+
+unsigned char gc0328_exposure_neg3[] = {
+ 0xfe, 0x00,
+ 0xd5, 0xf0,
+ 0xfe, 0x01,
+ 0x13, 0x37,
+ 0xfe, 0x00
+};
+
+unsigned char gc0328_exposure_zero[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x00,
+ 0xfe, 0x01,
+ 0x13, 0x38,
+ 0xfe, 0x00
+};
+
+unsigned char gc0328_exposure_pos3[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x10,
+ 0xfe, 0x01,
+ 0x13, 0x55,
+ 0xfe, 0x00
+};
+
+unsigned char gc0328_exposure_pos6[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x20,
+ 0xfe, 0x01,
+ 0x13, 0x65,
+ 0xfe, 0x00
+};
+
+
+// Color Effect
+unsigned char gc0328_colorfx_none[] = {
+
+ 0x43, 0x00
+};
+
+unsigned char gc0328_colorfx_bw[] = {
+
+ 0x43, 0x02,
+ 0xda, 0xd0,
+ 0xdb, 0x28
+};
+
+unsigned char gc0328_colorfx_sepia[] = {
+ 0x43, 0x02,
+ 0xda, 0xc0,
+ 0xdb, 0xc0
+};
+
+unsigned char gc0328_colorfx_negative[] = {
+
+ 0x43, 0x01
+};
+
+unsigned char gc0328_colorfx_emboss[] = {
+ 0x43, 0x02,
+ 0xda, 0x50,
+ 0xdb, 0xe0
+};
+
+unsigned char gc0328_colorfx_sketch[] = {
+ 0x43, 0x02,
+ 0xda, 0x00,
+ 0xdb, 0x00
+};
+
+unsigned char gc0328_colorfx_sky_blue[] = {
+ 0x43,0x02,
+ 0xda,0x50,
+ 0xdb,0xe0,
+};
+
+unsigned char gc0328_colorfx_grass_green[] = {
+ 0x43,0x02,
+ 0xda,0xc0,
+ 0xdb,0xc0,
+};
+
+unsigned char gc0328_colorfx_skin_whiten[] = {
+};
+
+unsigned char gc0328_colorfx_vivid[] = {
+};
+
+// Brightness
+unsigned char gc0328_brightness_neg4[] = {
+};
+
+unsigned char gc0328_brightness_neg3[] = {
+};
+
+unsigned char gc0328_brightness_neg2[] = {
+};
+
+unsigned char gc0328_brightness_neg1[] = {
+};
+
+unsigned char gc0328_brightness_zero[] = {
+};
+
+unsigned char gc0328_brightness_pos1[] = {
+};
+
+unsigned char gc0328_brightness_pos2[] = {
+};
+
+unsigned char gc0328_brightness_pos3[] = {
+};
+
+unsigned char gc0328_brightness_pos4[] = {
+};
+
+// Contrast
+unsigned char gc0328_contrast_neg4[] = {
+};
+
+unsigned char gc0328_contrast_neg3[] = {
+};
+
+unsigned char gc0328_contrast_neg2[] = {
+};
+
+unsigned char gc0328_contrast_neg1[] = {
+};
+
+unsigned char gc0328_contrast_zero[] = {
+};
+
+unsigned char gc0328_contrast_pos1[] = {
+};
+
+unsigned char gc0328_contrast_pos2[] = {
+};
+
+unsigned char gc0328_contrast_pos3[] = {
+};
+
+unsigned char gc0328_contrast_pos4[] = {
+};
+
+// Saturation
+unsigned char gc0328_saturation_neg4[] = {
+};
+
+unsigned char gc0328_saturation_neg3[] = {
+};
+
+unsigned char gc0328_saturation_neg2[] = {
+};
+
+unsigned char gc0328_saturation_neg1[] = {
+};
+
+unsigned char gc0328_saturation_zero[] = {
+};
+
+unsigned char gc0328_saturation_pos1[] = {
+};
+
+unsigned char gc0328_saturation_pos2[] = {
+};
+
+unsigned char gc0328_saturation_pos3[] = {
+};
+
+unsigned char gc0328_saturation_pos4[] = {
+};
+
+
+// Resolution
+
+// QCIF
+unsigned char gc0328_176x144[]={
+};
+
+unsigned char gc0328_320x240[]={
+
+};
+
+unsigned char gc0328_640x480[]={
+};
+
+
+// Initiliztion
+unsigned char gc0328_default_regs_init[] = {
+ 0xfe, 0x80,
+ 0xfe, 0x80,
+ 0xfc, 0x16,
+ 0xfc, 0x16,
+ 0xfc, 0x16,
+ 0xfc, 0x16,
+
+ //0x42, 0x02,
+// 0xfa, 0x11,
+ 0x4f, 0x00,
+ 0x03, 0x06,
+ 0x04, 0x24,
+ 0x4f, 0x01,
+
+ 0xfe, 0x01,
+ 0x4f, 0x00,
+ 0x4c, 0x01,
+ 0xfe, 0x00,
+ //////////////////////////////
+ //AWB
+ ////////////////////////////////
+0xfe, 0x01,
+ 0x51, 0x80,//20
+ 0x52, 0x12,// 16 //1f
+ 0x53, 0x80,//40
+ 0x54, 0x60,//9f
+ 0x55, 0x04,//01
+ 0x56, 0x0e,//00
+ 0x5b, 0x02,//02
+ 0x61, 0xdc,//R2G_stand0[7:0]
+ 0x62, 0xca,//B2G_stand0[7:0]
+
+ 0x70, 0xf5,
+ 0x71, 0x0a,
+ 0x72, 0x18,//y2c
+ 0x73, 0x30,//28 //20, 0x//AWB_C_inter
+ 0x74, 0x58,//40 //20, 0x// AWB_C_max
+ 0x7c, 0x71,//AWB speed,AWB margin
+ 0x7d, 0x00,//10 //AWB every N
+ 0x76, 0x8f,//move mode en,Move mode TH
+ 0x79, 0x00,//00
+
+
+
+ ////AWB////
+0x4f, 0x00,
+ 0x4d, 0x34,
+ 0x4e, 0x04,
+ 0x4e, 0x02,
+ 0x4e, 0x02,
+ 0x4d, 0x43,
+ 0x4e, 0x04,
+ 0x4e, 0x04,//D50
+ 0x4e, 0x02,// d65 add
+ 0x4e, 0x02,// d65 add
+ 0x4d, 0x53,
+ 0x4e, 0x08,//cwf
+ 0x4e, 0x04,
+ 0x4e, 0x02,//D65
+ 0x4e, 0x02,//D65
+ 0x4d, 0x63,
+ 0x4e, 0x10,//tl84
+ 0x4d, 0x72,
+ 0x4e, 0x20,
+ 0x4e, 0x20,
+ 0x4d, 0x82,
+ 0x4e, 0x20,//A
+ 0x4e, 0x20,
+ 0x4d, 0x92,
+ 0x4e, 0x20,//A
+ 0x4e, 0x20,
+ 0x4d, 0xa0,
+ 0x4e, 0x40,// H
+ 0x4e, 0x40,
+ 0x4e, 0x40,
+ 0x4f, 0x01,
+ 0x50, 0x84,//80//
+ 0xfe, 0x00,//page0
+
+ ////////////////////////////////////////////////
+ //////////// BLK //////////////////////
+ ////////////////////////////////////////////////
+ 0x27, 0xf7,
+ 0x28, 0x7F,
+ 0x29, 0x20,
+ 0x33, 0x20,
+ 0x34, 0x20,
+ 0x35, 0x20,
+ 0x36, 0x20,
+ 0x32, 0x08,
+
+ 0x47, 0x00,
+ 0x48, 0x00,
+
+ //////////////////////////////////////////////
+ ////////// page 0 ////////////////////////
+ //////////////////////////////////////////////
+ 0xfe, 0x00,
+ //0x05, 0x00,
+ //0x06, 0xde,
+ //0x07, 0x00,
+ //0x08, 0xa7,
+
+ 0x0d, 0x01,
+ 0x0e, 0xe8,
+ 0x0f, 0x02,
+ 0x10, 0x88,
+ 0x09, 0x00,
+ 0x0a, 0x00,
+ 0x0b, 0x00,
+ 0x0c, 0x00,
+ 0x16, 0x00,
+ 0x17, 0x14,
+ 0x18, 0x0e,
+ 0x19, 0x06,
+
+ 0x1b, 0x48,
+ 0x1f, 0xC8,
+ 0x20, 0x01,
+ 0x21, 0x78,
+ 0x22, 0xb0,
+ 0x23, 0x06,
+ 0x24, 0x16,
+ 0x26, 0x00,
+
+ 0x50, 0x01,
+
+ //global gain for range
+ 0x70, 0x45,
+
+ //////////////////////////////////////
+ //////////// block enable ///
+ //////////////////////////////////////
+ 0x40, 0x7f,
+ 0x41, 0x24,
+
+ 0x42, 0xff,
+
+ 0x45, 0x00,
+ 0x44, 0x02,
+ 0x46, 0x02, //0x03
+
+ 0x4b, 0x01,
+ 0x50, 0x01,
+
+ //DN & EEINTP
+ 0x7e, 0x0a,
+ 0x7f, 0x03,
+ 0x81, 0x15,
+ 0x82, 0x85,
+ 0x83, 0x02,
+ 0x84, 0xe5,
+ 0x90, 0xac,
+ 0x92, 0x02,
+ 0x94, 0x04,//02
+ 0x95, 0x76,//43
+
+
+ ///////YCP
+ 0xd1, 0x30,//38
+ 0xd2, 0x38,//38
+ 0xd3, 0x38,//38
+ 0xdd, 0x54,
+ 0xde, 0x38,
+ 0xe4, 0x88,
+ 0xe5, 0x40,
+ 0xd7, 0x0e,
+
+ //////////////////////////////
+ //////////////// GAMMA //////
+ //////////////////////////////
+ //rgb gamma
+ 0xBF, 0x0E,
+ 0xc0, 0x1C,
+ 0xc1, 0x34,
+ 0xc2, 0x48,
+ 0xc3, 0x5A,
+ 0xc4, 0x6B,
+ 0xc5, 0x7B,
+ 0xc6, 0x95,
+ 0xc7, 0xAB,
+ 0xc8, 0xBF,
+ 0xc9, 0xCE,
+ 0xcA, 0xD9,
+ 0xcB, 0xE4,
+ 0xcC, 0xEC,
+ 0xcD, 0xF7,
+ 0xcE, 0xFD,
+ 0xcF, 0xFF,
+
+ ///Y gamma
+ 0xfe, 0x00,
+ 0x63, 0x00,
+ 0x64, 0x05,
+ 0x65, 0x0b,
+ 0x66, 0x19,
+ 0x67, 0x2e,
+ 0x68, 0x40,
+ 0x69, 0x54,
+ 0x6a, 0x66,
+ 0x6b, 0x86,
+ 0x6c, 0xa7,
+ 0x6d, 0xc6,
+ 0x6e, 0xe4,
+ 0x6f, 0xff,
+
+
+
+ //////ASDE
+ 0xfe, 0x01,
+ 0x18, 0x02,
+
+ 0xfe, 0x00,
+
+ 0x98, 0x00,
+ 0x9b, 0x20,
+ 0x9c, 0x80,
+ 0xa4, 0x10,
+ 0xa8, 0xB0,
+ 0xaa, 0x40,
+ 0xa2, 0x23,
+ 0xad, 0x01,
+
+ ///////////////////////////////////////
+ ////////// AEC /////////////////////
+ ///////////////////////////////////////
+ 0xfe, 0x01,
+ 0x9c, 0x02,
+ 0x08, 0xa0,
+ 0x09, 0xe8,
+
+ 0x10, 0x08,
+
+ 0x11, 0x91,
+ 0x12, 0x10,
+ 0x13, 0x30,// 45
+ 0x15, 0xfc,
+ 0x21, 0xa0,//c0
+ 0x22, 0x40,//60
+ 0x23, 0x30,
+ 0x25, 0x00,
+ 0x24, 0x14,
+
+
+ //0x28, 0x0a,
+#if 0
+ 0xfe,0x00 ,
+ 0x05,0x00 ,
+ 0x06,0x80 , // hb
+ 0x07,0x00 ,
+ 0x08,0x60 ,//vb
+
+ 0xfe,0x01 ,
+ 0x29,0x00 , //anti-flicker step [11:8]
+ 0x2a,0x49 , //anti-flicker step [7:0]
+
+ 0x2b,0x02 , //exp level 0 20fps
+ 0x2c,0x48,
+ 0x2d,0x02 , //exp level 14 fps
+ 0x2e,0xda ,
+ 0x2f,0x03 , //exp level 2 10.00fps
+ 0x30,0xfe ,
+ 0x31,0x05 , //exp level 3 8fps
+ 0x32,0x6b ,
+ 0xfe,0x00 ,
+ #endif
+ 0xfe,0x00 ,
+ 0x05,0x01 ,
+ 0x06,0x32 , // hb
+ 0x07,0x00 ,
+ 0x08,0x70 ,//vb
+
+ 0xfe,0x01 ,
+ 0x29,0x00 , //anti-flicker step [11:8]
+ 0x2a,0x78 , //anti-flicker step [7:0]
+
+ 0x2b,0x02 , //exp level 0 20fps
+ 0x2c,0x58,
+ 0x2d,0x03 , //exp level 14 fps
+ 0x2e,0x48 ,
+ 0x2f,0x05 , //exp level 2 10.00fps
+ 0x30,0xa0 ,
+ 0x31,0x07 , //exp level 3 8fps
+ 0x32,0x80 ,
+ 0xfe,0x00 ,
+ //////////////////////////////////////
+ ////////////LSC//////////////////////
+ //////////////////////////////////////
+ 0xfe, 0x01,
+ 0xc0, 0x0f,
+ 0xc1, 0x0b,
+ 0xc2, 0x0b,
+ 0xc6, 0x04,
+ 0xc7, 0x00,
+ 0xc8, 0x00,
+ 0xba, 0x1e,
+ 0xbb, 0x1a,
+ 0xbc, 0x1b,
+ 0xb4, 0x1a,
+ 0xb5, 0x12,
+ 0xb6, 0x14,
+ 0xc3, 0x00,
+ 0xc4, 0x00,
+ 0xc5, 0x00,
+ 0xc9, 0x00,
+ 0xca, 0x00,
+ 0xcb, 0x00,
+ 0xbd, 0x0a,
+ 0xbe, 0x00,
+ 0xbf, 0x00,
+ 0xb7, 0x04,
+ 0xb8, 0x00,
+ 0xb9, 0x00,
+ 0xa8, 0x13,
+ 0xa9, 0x0b,
+ 0xaa, 0x0b,
+ 0xab, 0x20,
+ 0xac, 0x1c,
+ 0xad, 0x22,
+ 0xae, 0x0e,
+ 0xaf, 0x05,
+ 0xb0, 0x06,
+ 0xb1, 0x0d,
+ 0xb2, 0x0a,
+ 0xb3, 0x08,
+ 0xa4, 0x00,
+ 0xa5, 0x00,
+ 0xa6, 0x00,
+ 0xa7, 0x00,
+ 0xa1, 0x3c,
+ 0xa2, 0x50,
+ 0xfe, 0x00,
+
+ ///cct
+ #if 0
+ 0xB1, 0x02,
+ 0xB2, 0x02,
+ 0xB3, 0x07,
+ 0xB4, 0xf0,
+ 0xB5, 0x05,
+ 0xB6, 0xf0,
+
+ 0xf1, 0x07,
+
+ 0xf2, 0x01,
+ #endif
+0xB1,0x08 ,// 40
+ 0xB2,0x02 ,
+ 0xB3,0x03 ,//07
+ 0xB4,0xe0 ,
+ 0xB5,0x00 ,//
+ 0xB6,0xf0 ,
+ 0xB7,0x0a ,
+
+ 0xf1,0x07 ,
+ 0xf1,0x07 ,
+ 0xf2,0x01 ,
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0329/gc0329-3nod.h b/drivers/media/video/wmt_v4l2/sensors/gc0329/gc0329-3nod.h
new file mode 100755
index 00000000..148f403c
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0329/gc0329-3nod.h
@@ -0,0 +1,550 @@
+#ifndef GC0329_H
+#define GC0329_H
+
+
+// Scene Mode
+uint8_t gc0329_scene_mode_auto[] = {
+};
+
+uint8_t gc0329_scene_mode_night[] = {
+};
+
+
+// White Balance
+uint8_t gc0329_wb_auto [] = {
+ 0xfe, 0x00,
+ 0x77, 0x57,
+ 0x78, 0x4d,
+ 0x79, 0x45,
+ 0x42, 0xfe, //AWB on
+};
+
+uint8_t gc0329_wb_incandescent [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x48,
+ 0x78, 0x40,
+ 0x79, 0x5c,
+
+};
+
+uint8_t gc0329_wb_fluorescent [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x40,
+ 0x78, 0x42,
+ 0x79, 0x50,
+
+};
+
+uint8_t gc0329_wb_daylight [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x74,
+ 0x78, 0x52,
+ 0x79, 0x40,
+
+};
+
+uint8_t gc0329_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x8c,
+ 0x78, 0x50,
+ 0x79, 0x40,
+
+};
+
+uint8_t gc0329_wb_tungsten [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x40,
+ 0x78, 0x54,
+ 0x79, 0x70,
+
+};
+
+
+// Exposure
+uint8_t gc0329_exposure_neg6[] = {
+ 0xfe, 0x00,
+ 0xd5, 0xe0,
+ 0xfe, 0x01,
+ 0x13, 0x40,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_neg3[] = {
+ 0xfe, 0x00,
+ 0xd5, 0xf0,
+ 0xfe, 0x01,
+ 0x13, 0x48,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_zero[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x00, /// 00
+ 0xfe, 0x01,
+ 0x13, 0x56,//6a cyrille
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_pos3[] = {
+
+ 0xfe, 0x00,
+ 0xd5, 0x10,
+ 0xfe, 0x01,
+ 0x13, 0x70,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_pos6[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x20,
+ 0xfe, 0x01,
+ 0x13, 0x80,
+ 0xfe, 0x00,
+
+};
+
+
+// Color Effect
+uint8_t gc0329_colorfx_none[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x00,
+};
+
+uint8_t gc0329_colorfx_bw[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0x00,
+ 0xdb , 0x00,
+
+};
+
+uint8_t gc0329_colorfx_sepia[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0xd0,
+ 0xdb , 0x28,
+
+};
+
+uint8_t gc0329_colorfx_negative[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x01,
+};
+
+uint8_t gc0329_colorfx_emboss[] = {
+};
+
+uint8_t gc0329_colorfx_sketch[] = {
+
+};
+
+uint8_t gc0329_colorfx_sky_blue[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0x50,
+ 0xdb , 0xe0,
+
+};
+
+uint8_t gc0329_colorfx_grass_green[] = {
+
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0xc0,
+ 0xdb , 0xc0,
+
+};
+
+uint8_t gc0329_colorfx_skin_whiten[] = {
+};
+
+uint8_t gc0329_colorfx_vivid[] = {
+};
+
+// Brightness
+uint8_t gc0329_brightness_neg4[] = {
+};
+
+uint8_t gc0329_brightness_neg3[] = {
+};
+
+uint8_t gc0329_brightness_neg2[] = {
+};
+
+uint8_t gc0329_brightness_neg1[] = {
+};
+
+uint8_t gc0329_brightness_zero[] = {
+};
+
+uint8_t gc0329_brightness_pos1[] = {
+};
+
+uint8_t gc0329_brightness_pos2[] = {
+};
+
+uint8_t gc0329_brightness_pos3[] = {
+};
+
+uint8_t gc0329_brightness_pos4[] = {
+};
+
+// Contrast
+uint8_t gc0329_contrast_neg4[] = {
+};
+
+uint8_t gc0329_contrast_neg3[] = {
+};
+
+uint8_t gc0329_contrast_neg2[] = {
+};
+
+uint8_t gc0329_contrast_neg1[] = {
+};
+
+uint8_t gc0329_contrast_zero[] = {
+};
+
+uint8_t gc0329_contrast_pos1[] = {
+};
+
+uint8_t gc0329_contrast_pos2[] = {
+};
+
+uint8_t gc0329_contrast_pos3[] = {
+};
+
+uint8_t gc0329_contrast_pos4[] = {
+};
+
+// Saturation
+uint8_t gc0329_saturation_neg4[] = {
+};
+
+uint8_t gc0329_saturation_neg3[] = {
+};
+
+uint8_t gc0329_saturation_neg2[] = {
+};
+
+uint8_t gc0329_saturation_neg1[] = {
+};
+
+uint8_t gc0329_saturation_zero[] = {
+};
+
+uint8_t gc0329_saturation_pos1[] = {
+};
+
+uint8_t gc0329_saturation_pos2[] = {
+};
+
+uint8_t gc0329_saturation_pos3[] = {
+};
+
+uint8_t gc0329_saturation_pos4[] = {
+};
+
+
+// Resolution
+
+// QCIF
+uint8_t gc0329_176x144[]={
+};
+
+uint8_t gc0329_320x240[]={
+ 0x59, 0x22,
+};
+
+uint8_t gc0329_640x480[]={
+ 0x59, 0x11,
+};
+
+
+// Initiliztion
+uint8_t gc0329_default_regs_init[] = {
+ 0xfe, 0x80,
+ 0xfc, 0x12,
+ 0xfc, 0x12,
+ 0xfe, 0x00,
+
+ 0xfa, 0x11,
+
+ 0xf0, 0x07,
+ 0xf1, 0x01,
+ 0x73, 0x90,
+ 0x74, 0x80,
+ 0x75, 0x80,
+ 0x76, 0x94,
+ 0xfc, 0x16,
+
+ 0x05, 0x01,
+ 0x06, 0x32,
+ 0x07, 0x00,
+ 0x08, 0x70,
+ 0xfe, 0x01,
+ 0x29, 0x00,
+ 0x2a, 0x3c,
+ 0x2b, 0x02,
+ 0x2c, 0x58,
+ 0x2d, 0x02,
+
+ 0x2e, 0xd0,
+ 0x2f, 0x03,
+ 0x30, 0x48,
+ 0x31, 0x04,
+ 0x32, 0x38,
+ 0x33, 0x20,
+ 0xfe, 0x00,
+
+
+
+
+
+ 0x0a, 0x00,
+ 0x0c, 0x00,
+ 0x17, 0x14,
+ 0x19, 0x05,
+ 0x1b, 0x24,
+ 0x1c, 0x04,
+ 0x1e, 0x00,
+ 0x1f, 0xc8,
+ 0x20, 0x00,
+ 0x21, 0x48,
+ 0x23, 0x22,
+ 0x24, 0x16,
+
+
+ 0x26, 0xf7,
+ 0x28, 0x7f,
+ 0x29, 0x00,
+ 0x32, 0x00,
+ 0x33, 0x20,
+ 0x34, 0x20,
+ 0x35, 0x20,
+ 0x36, 0x20,
+ 0x3b, 0x04,
+ 0x3c, 0x04,
+ 0x3d, 0x04,
+ 0x3e, 0x04,
+
+ 0x40, 0xff,
+ 0x41, 0x00,
+ 0x42, 0xfe,
+ 0x46, 0x02,
+ 0x4b, 0xcb,
+ 0x4d, 0x01,
+ 0x4f, 0x01,
+ 0x70, 0x48,
+ 0x80, 0xe7,
+ 0x82, 0x55,
+ 0x87, 0x4a,
+ 0xfe, 0x01,
+ 0x18, 0x22,
+ 0xfe, 0x00,
+ 0x9c, 0x0a,
+ 0xa4, 0x50,
+ 0xa5, 0x21,
+ 0xa7, 0x35,
+ 0xdd, 0x54,
+ 0x95, 0x35,
+ 0xfe, 0x00,
+ ///gamma
+ /*
+ 0xbf, 0x06,
+ 0xc0, 0x14,
+ 0xc1, 0x27,
+ 0xc2, 0x3b,
+ 0xc3, 0x4f,
+ 0xc4, 0x62,
+ 0xc5, 0x72,
+ 0xc6, 0x8d,
+ 0xc7, 0xa4,
+ 0xc8, 0xb8,
+ 0xc9, 0xc9,
+ 0xca, 0xd6,
+ 0xcb, 0xe0,
+ 0xcc, 0xe8,
+ 0xcd, 0xf4,
+ 0xce, 0xfc,
+ 0xcf, 0xff,
+ */
+
+ 0xBF, 0x0B,
+ 0xc0, 0x16,
+ 0xc1, 0x29,
+ 0xc2, 0x3C,
+ 0xc3, 0x4F,
+ 0xc4, 0x5F,
+ 0xc5, 0x6F,
+ 0xc6, 0x8A,
+ 0xc7, 0x9F,
+ 0xc8, 0xB4,
+ 0xc9, 0xC6,
+ 0xcA, 0xD3,
+ 0xcB, 0xDD,
+ 0xcC, 0xE5,
+ 0xcD, 0xF1,
+ 0xcE, 0xFA,
+ 0xcF, 0xFF,
+ /*0xBF, 0x14,
+ 0xc0, 0x28,
+ 0xc1, 0x44,
+ 0xc2, 0x5D,
+ 0xc3, 0x72,
+ 0xc4, 0x86,
+ 0xc5, 0x95,
+ 0xc6, 0xB1,
+ 0xc7, 0xC6,
+ 0xc8, 0xD5,
+ 0xc9, 0xE1,
+ 0xcA, 0xEA,
+ 0xcB, 0xF1,
+ 0xcC, 0xF5,
+ 0xcD, 0xFB,
+ 0xcE, 0xFE,
+ 0xcF, 0xFF,*/
+
+
+ 0xfe, 0x00,
+ 0xb3, 0x40,//44 cyrille
+ 0xb4, 0xfd,
+ 0xb5, 0x02,
+ 0xb6, 0xfa,
+ 0xb7, 0x48,
+ 0xb8, 0xf0,
+ 0x50, 0x01,
+ 0x19, 0x05,
+ 0x20, 0x01,
+ 0x22, 0xba,
+ 0x21, 0x48,
+ 0xfe, 0x00,
+ 0xd1, 0x28,// 34 cyrille
+ 0xd2, 0x26,//34 cyrille
+ 0x7a, 0x7d,// cyrille
+ 0x7b, 0x81,// cyrille
+ 0x7c, 0x80,// cyrille
+ 0xfe, 0x01,
+ 0x10, 0x40,
+ 0x12, 0x27,//17 cyrille
+ 0x13, 0x56,//60 cyrille
+ 0x17, 0x88,
+ 0x21, 0x80,//b0 cyrille
+ 0x22, 0x40,//60 cyrille
+ 0x3c, 0x82,
+ 0x3d, 0x55,
+ 0x3e, 0x48,
+ 0xfe, 0x01,
+ 0x06, 0x08,
+ 0x07, 0x06,
+ 0x08, 0xa6,
+ 0x09, 0xee,
+ 0x6a, 0x40,
+ 0x6b, 0x39,
+ 0xd0, 0x00,
+ 0xd2, 0x2c,
+ 0xd3, 0x80,
+ 0x9c, 0x00,
+ 0x9d, 0x10,
+ 0x9e, 0xc0,
+ 0x9f, 0x40,
+ 0xfe, 0x01,
+ 0xa0, 0x00,
+ 0xa1, 0x3c,
+ 0xa2, 0x50,
+ 0xa3, 0x00,
+ 0xa8, 0x0f,
+ 0xa9, 0x08,
+ 0xaa, 0x00,
+ 0xab, 0x04,
+ 0xac, 0x00,
+ 0xad, 0x07,
+ 0xae, 0x0e,
+ 0xaf, 0x00,
+ 0xb0, 0x00,
+ 0xb1, 0x09,
+ 0xb2, 0x00,
+ 0xb3, 0x00,
+ 0xb4, 0x31,
+ 0xb5, 0x19,
+ 0xb6, 0x24,
+ 0xba, 0x3a,
+ 0xbb, 0x24,
+ 0xbc, 0x2a,
+ 0xc0, 0x17,
+ 0xc1, 0x13,
+ 0xc2, 0x17,
+ 0xc6, 0x21,
+ 0xc7, 0x1c,
+ 0xc8, 0x1c,
+ 0xb7, 0x00,
+ 0xb8, 0x00,
+ 0xb9, 0x00,
+ 0xbd, 0x00,
+ 0xbe, 0x00,
+ 0xbf, 0x00,
+ 0xc3, 0x00,
+ 0xc4, 0x00,
+ 0xc5, 0x00,
+ 0xc9, 0x00,
+ 0xca, 0x00,
+ 0xcb, 0x00,
+ 0xa4, 0x00,
+ 0xa5, 0x00,
+ 0xa6, 0x00,
+ 0xa7, 0x00,
+ 0xfe, 0x00,
+
+ 0xfe,0x01,
+ 0x18,0x22,
+ 0x21,0x80,//c0 cyrille
+ 0x06,0x12,
+ 0x08,0x9c,
+ 0x51,0x28,
+ 0x52,0x10,
+ 0x53,0x20,
+ 0x54,0x40,
+ 0x55,0x16,
+ 0x56,0x30,
+ 0x58,0x60,
+ 0x59,0x08,
+ 0x5c,0x35,
+ 0x5d,0x72,
+ 0x67,0x80,
+ 0x68,0x60,
+ 0x69,0x90,
+ 0x6c,0x30,
+ 0x6d,0x60,
+ 0x70,0x10,
+ 0xfe,0x00,
+ 0x9c,0x0a,
+ 0xa0,0xaf,
+ 0xa2,0xff,
+ 0xa4,0x60,
+ 0xa5,0x31,
+ 0xa7,0x35,
+ 0x42,0xfe,
+ 0xd1,0x28, //34 cyrille
+ 0xd2,0x26, //34 cyrille
+ 0xd3,0x40,// 3b cyrille
+ 0xd5,0x00,//0c cyrille
+ 0xfe,0x00,
+
+ 0xa0, 0xaf,
+ 0xa2, 0xff,
+ 0x44, 0xa2,
+};
+
+uint8_t gc0329_default_regs_exit[] = {
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0329/gc0329-3nod_TL070VA5.h b/drivers/media/video/wmt_v4l2/sensors/gc0329/gc0329-3nod_TL070VA5.h
new file mode 100755
index 00000000..eabe66b7
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0329/gc0329-3nod_TL070VA5.h
@@ -0,0 +1,560 @@
+#ifndef GC0329_H
+#define GC0329_H
+
+
+// Scene Mode
+uint8_t gc0329_scene_mode_auto[] = {
+};
+
+uint8_t gc0329_scene_mode_night[] = {
+};
+
+
+// White Balance
+uint8_t gc0329_wb_auto [] = {
+ 0xfe, 0x00,
+ 0x77, 0x57,
+ 0x78, 0x4d,
+ 0x79, 0x45,
+ 0x42, 0xfe, //AWB on
+};
+
+uint8_t gc0329_wb_incandescent [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x48,
+ 0x78, 0x40,
+ 0x79, 0x5c,
+
+};
+
+uint8_t gc0329_wb_fluorescent [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x40,
+ 0x78, 0x42,
+ 0x79, 0x50,
+
+};
+
+uint8_t gc0329_wb_daylight [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x74,
+ 0x78, 0x52,
+ 0x79, 0x40,
+
+};
+
+uint8_t gc0329_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x8c,
+ 0x78, 0x50,
+ 0x79, 0x40,
+
+};
+
+uint8_t gc0329_wb_tungsten [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x40,
+ 0x78, 0x54,
+ 0x79, 0x70,
+
+};
+
+
+// Exposure
+uint8_t gc0329_exposure_neg6[] = {
+ 0xfe, 0x00,
+ 0xd5, 0xe0,
+ 0xfe, 0x01,
+ 0x13, 0x40,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_neg3[] = {
+ 0xfe, 0x00,
+ 0xd5, 0xf0,
+ 0xfe, 0x01,
+ 0x13, 0x48,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_zero[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x00, /// 00
+ 0xfe, 0x01,
+ 0x13, 0x56,//6a cyrille
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_pos3[] = {
+
+ 0xfe, 0x00,
+ 0xd5, 0x10,
+ 0xfe, 0x01,
+ 0x13, 0x70,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_pos6[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x20,
+ 0xfe, 0x01,
+ 0x13, 0x80,
+ 0xfe, 0x00,
+
+};
+
+
+// Color Effect
+uint8_t gc0329_colorfx_none[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x00,
+};
+
+uint8_t gc0329_colorfx_bw[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0x00,
+ 0xdb , 0x00,
+
+};
+
+uint8_t gc0329_colorfx_sepia[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0xd0,
+ 0xdb , 0x28,
+
+};
+
+uint8_t gc0329_colorfx_negative[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x01,
+};
+
+uint8_t gc0329_colorfx_emboss[] = {
+};
+
+uint8_t gc0329_colorfx_sketch[] = {
+
+};
+
+uint8_t gc0329_colorfx_sky_blue[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0x50,
+ 0xdb , 0xe0,
+
+};
+
+uint8_t gc0329_colorfx_grass_green[] = {
+
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0xc0,
+ 0xdb , 0xc0,
+
+};
+
+uint8_t gc0329_colorfx_skin_whiten[] = {
+};
+
+uint8_t gc0329_colorfx_vivid[] = {
+};
+
+// Brightness
+uint8_t gc0329_brightness_neg4[] = {
+};
+
+uint8_t gc0329_brightness_neg3[] = {
+};
+
+uint8_t gc0329_brightness_neg2[] = {
+};
+
+uint8_t gc0329_brightness_neg1[] = {
+};
+
+uint8_t gc0329_brightness_zero[] = {
+};
+
+uint8_t gc0329_brightness_pos1[] = {
+};
+
+uint8_t gc0329_brightness_pos2[] = {
+};
+
+uint8_t gc0329_brightness_pos3[] = {
+};
+
+uint8_t gc0329_brightness_pos4[] = {
+};
+
+// Contrast
+uint8_t gc0329_contrast_neg4[] = {
+};
+
+uint8_t gc0329_contrast_neg3[] = {
+};
+
+uint8_t gc0329_contrast_neg2[] = {
+};
+
+uint8_t gc0329_contrast_neg1[] = {
+};
+
+uint8_t gc0329_contrast_zero[] = {
+};
+
+uint8_t gc0329_contrast_pos1[] = {
+};
+
+uint8_t gc0329_contrast_pos2[] = {
+};
+
+uint8_t gc0329_contrast_pos3[] = {
+};
+
+uint8_t gc0329_contrast_pos4[] = {
+};
+
+// Saturation
+uint8_t gc0329_saturation_neg4[] = {
+};
+
+uint8_t gc0329_saturation_neg3[] = {
+};
+
+uint8_t gc0329_saturation_neg2[] = {
+};
+
+uint8_t gc0329_saturation_neg1[] = {
+};
+
+uint8_t gc0329_saturation_zero[] = {
+};
+
+uint8_t gc0329_saturation_pos1[] = {
+};
+
+uint8_t gc0329_saturation_pos2[] = {
+};
+
+uint8_t gc0329_saturation_pos3[] = {
+};
+
+uint8_t gc0329_saturation_pos4[] = {
+};
+
+
+// Resolution
+
+// QCIF
+uint8_t gc0329_176x144[]={
+};
+
+uint8_t gc0329_320x240[]={
+ 0x59, 0x22,
+};
+
+uint8_t gc0329_640x480[]={
+ 0x59, 0x11,
+};
+
+
+// Initiliztion
+uint8_t gc0329_default_regs_init[] = {
+ 0xfe, 0x80,
+ 0xfc, 0x12,
+ 0xfc, 0x12,
+ 0xfe, 0x00,
+
+ 0xfa, 0x11,
+
+ 0xf0, 0x07,
+ 0xf1, 0x01,
+ 0x73, 0x90,
+ 0x74, 0x80,
+ 0x75, 0x80,
+ 0x76, 0x94,
+ 0xfc, 0x16,
+
+ 0x05, 0x01,
+ 0x06, 0x32,
+ 0x07, 0x00,
+ 0x08, 0x70,
+ 0xfe, 0x01,
+ 0x29, 0x00,
+ 0x2a, 0x3c,
+ /*
+ 0x2b, 0x02,
+ 0x2c, 0x58,
+ 0x2d, 0x02,
+
+ 0x2e, 0xd0,
+ 0x2f, 0x03,
+ 0x30, 0x48,
+ 0x31, 0x04,
+ 0x32, 0x38,*/
+ 0x2b, 0x02,
+ 0x2c, 0xd0,
+ 0x2d, 0x03,
+
+ 0x2e, 0x48,
+ 0x2f, 0x03,
+ 0x30, 0x84,
+ 0x31, 0x04,
+ 0x32, 0x38,
+ 0x33, 0x20,
+ 0xfe, 0x00,
+
+
+
+
+
+ 0x0a, 0x00,
+ 0x0c, 0x00,
+ 0x17, 0x14, //14
+ 0x19, 0x05,
+ 0x1b, 0x24,
+ 0x1c, 0x04,
+ 0x1e, 0x00,
+ 0x1f, 0xc8,
+ 0x20, 0x00,
+ 0x21, 0x48,
+ 0x23, 0x22,
+ 0x24, 0x13,//0x16
+
+
+ 0x26, 0xf7,
+ 0x28, 0x7f,
+ 0x29, 0x00,
+ 0x32, 0x00,
+ 0x33, 0x20,
+ 0x34, 0x20,
+ 0x35, 0x20,
+ 0x36, 0x20,
+ 0x3b, 0x04,
+ 0x3c, 0x04,
+ 0x3d, 0x04,
+ 0x3e, 0x04,
+
+ 0x40, 0xff,
+ 0x41, 0x00,
+ 0x42, 0xfe,
+ 0x46, 0x02,
+ 0x4b, 0xcb,
+ 0x4d, 0x01,
+ 0x4f, 0x01,
+ 0x70, 0x48,
+ 0x80, 0xe7,
+ 0x82, 0x60,//0x55
+ 0x87, 0x4a,
+ 0xfe, 0x01,
+ 0x18, 0x22,
+ 0xfe, 0x00,
+ 0x9c, 0x0a,
+ 0xa4, 0x50,
+ 0xa5, 0x21,
+ 0xa7, 0x35,
+ 0xdd, 0x54,
+ 0x95, 0x45,//0x35
+ 0xfe, 0x00,
+ ///gamma
+ /*
+ 0xbf, 0x06,
+ 0xc0, 0x14,
+ 0xc1, 0x27,
+ 0xc2, 0x3b,
+ 0xc3, 0x4f,
+ 0xc4, 0x62,
+ 0xc5, 0x72,
+ 0xc6, 0x8d,
+ 0xc7, 0xa4,
+ 0xc8, 0xb8,
+ 0xc9, 0xc9,
+ 0xca, 0xd6,
+ 0xcb, 0xe0,
+ 0xcc, 0xe8,
+ 0xcd, 0xf4,
+ 0xce, 0xfc,
+ 0xcf, 0xff,
+ */
+
+ 0xBF, 0x0B,
+ 0xc0, 0x16,
+ 0xc1, 0x29,
+ 0xc2, 0x3C,
+ 0xc3, 0x4F,
+ 0xc4, 0x5F,
+ 0xc5, 0x6F,
+ 0xc6, 0x8A,
+ 0xc7, 0x9F,
+ 0xc8, 0xB4,
+ 0xc9, 0xC6,
+ 0xcA, 0xD3,
+ 0xcB, 0xDD,
+ 0xcC, 0xE5,
+ 0xcD, 0xF1,
+ 0xcE, 0xFA,
+ 0xcF, 0xFF,
+ /*0xBF, 0x14,
+ 0xc0, 0x28,
+ 0xc1, 0x44,
+ 0xc2, 0x5D,
+ 0xc3, 0x72,
+ 0xc4, 0x86,
+ 0xc5, 0x95,
+ 0xc6, 0xB1,
+ 0xc7, 0xC6,
+ 0xc8, 0xD5,
+ 0xc9, 0xE1,
+ 0xcA, 0xEA,
+ 0xcB, 0xF1,
+ 0xcC, 0xF5,
+ 0xcD, 0xFB,
+ 0xcE, 0xFE,
+ 0xcF, 0xFF,*/
+
+
+ 0xfe, 0x00,
+ 0xb3, 0x38,//44 cyrille 40
+ 0xb4, 0xfd,
+ 0xb5, 0x02,
+ 0xb6, 0xfa,
+ 0xb7, 0x48,
+ 0xb8, 0xf0,
+ 0x50, 0x01,
+ 0x19, 0x05,
+ 0x20, 0x01,
+ 0x22, 0xba,
+ 0x21, 0x48,
+ 0xfe, 0x00,
+ 0xd1, 0x28,// 34 cyrille
+ 0xd2, 0x26,//34 cyrille
+ 0x7a, 0x7a,// cyrille 7d
+ 0x7b, 0x81,// cyrille
+ 0x7c, 0x80,// cyrille
+ 0xfe, 0x01,
+ 0x10, 0x40,
+ 0x12, 0x27,//17 cyrille
+ 0x13, 0x56,//60 cyrille
+ 0x17, 0x88,
+ 0x21, 0x80,//b0 cyrille
+ 0x22, 0x40,//60 cyrille
+ 0x3c, 0x82,
+ 0x3d, 0x55,
+ 0x3e, 0x48,
+ 0xfe, 0x01,
+ 0x06, 0x08,
+ 0x07, 0x06,
+ 0x08, 0xa6,
+ 0x09, 0xee,
+ 0x6a, 0x40,
+ 0x6b, 0x39,
+ 0xd0, 0x00,
+ 0xd2, 0x2c,
+ 0xd3, 0x80,
+ 0x9c, 0x00,
+ 0x9d, 0x10,
+ 0x9e, 0xc0,
+ 0x9f, 0x40,
+ 0xfe, 0x01,
+ 0xa0, 0x00,
+ 0xa1, 0x3c,
+ 0xa2, 0x50,
+ 0xa3, 0x00,
+ 0xa8, 0x0f,
+ 0xa9, 0x08,
+ 0xaa, 0x00,
+ 0xab, 0x04,
+ 0xac, 0x00,
+ 0xad, 0x07,
+ 0xae, 0x0e,
+ 0xaf, 0x00,
+ 0xb0, 0x00,
+ 0xb1, 0x09,
+ 0xb2, 0x00,
+ 0xb3, 0x00,
+ 0xb4, 0x31,
+ 0xb5, 0x19,
+ 0xb6, 0x24,
+ 0xba, 0x3a,
+ 0xbb, 0x24,
+ 0xbc, 0x2a,
+ 0xc0, 0x17,
+ 0xc1, 0x13,
+ 0xc2, 0x17,
+ 0xc6, 0x21,
+ 0xc7, 0x1c,
+ 0xc8, 0x1c,
+ 0xb7, 0x00,
+ 0xb8, 0x00,
+ 0xb9, 0x00,
+ 0xbd, 0x00,
+ 0xbe, 0x00,
+ 0xbf, 0x00,
+ 0xc3, 0x00,
+ 0xc4, 0x00,
+ 0xc5, 0x00,
+ 0xc9, 0x00,
+ 0xca, 0x00,
+ 0xcb, 0x00,
+ 0xa4, 0x00,
+ 0xa5, 0x00,
+ 0xa6, 0x00,
+ 0xa7, 0x00,
+ 0xfe, 0x00,
+
+ 0xfe,0x01,
+ 0x18,0x22,
+ 0x21,0x80,//c0 cyrille
+ 0x06,0x12,
+ 0x08,0x9c,
+ 0x51,0x28,
+ 0x52,0x10,
+ 0x53,0x20,
+ 0x54,0x40,
+ 0x55,0x16,
+ 0x56,0x30,
+ 0x58,0x60,
+ 0x59,0x08,
+ 0x5c,0x35,
+ 0x5d,0x72,
+ 0x67,0x80,
+ 0x68,0x60,
+ 0x69,0x90,
+ 0x6c,0x30,
+ 0x6d,0x60,
+ 0x70,0x10,
+ 0xfe,0x00,
+ 0x9c,0x0a,
+ 0xa0,0xaf,
+ 0xa2,0xff,
+ 0xa4,0x60,
+ 0xa5,0x31,
+ 0xa7,0x35,
+ 0x42,0xfe,
+ 0xd1,0x28, //34 cyrille
+ 0xd2,0x26, //34 cyrille
+ 0xd3,0x40,// 3b cyrille
+ 0xd5,0x00,//0c cyrille
+ 0xfe,0x00,
+
+ 0xa0, 0xaf,
+ 0xa2, 0xff,
+ 0x44, 0xa2,
+};
+
+uint8_t gc0329_default_regs_exit[] = {
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0329/gc0329-jmt781.h b/drivers/media/video/wmt_v4l2/sensors/gc0329/gc0329-jmt781.h
new file mode 100755
index 00000000..0dbf462f
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0329/gc0329-jmt781.h
@@ -0,0 +1,496 @@
+#ifndef GC0329_H
+#define GC0329_H
+
+
+// Scene Mode
+uint8_t gc0329_scene_mode_auto[] = {
+};
+
+uint8_t gc0329_scene_mode_night[] = {
+};
+
+
+// White Balance
+uint8_t gc0329_wb_auto [] = {
+ 0xfe, 0x00,
+ 0x77, 0x57,
+ 0x78, 0x4d,
+ 0x79, 0x45,
+ 0x42, 0xfe, //AWB on
+};
+
+uint8_t gc0329_wb_incandescent [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x48,
+ 0x78, 0x40,
+ 0x79, 0x5c,
+
+};
+
+uint8_t gc0329_wb_fluorescent [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x40,
+ 0x78, 0x42,
+ 0x79, 0x50,
+
+};
+
+uint8_t gc0329_wb_daylight [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x74,
+ 0x78, 0x52,
+ 0x79, 0x40,
+
+};
+
+uint8_t gc0329_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x8c,
+ 0x78, 0x50,
+ 0x79, 0x40,
+
+};
+
+uint8_t gc0329_wb_tungsten [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x40,
+ 0x78, 0x54,
+ 0x79, 0x70,
+
+};
+
+
+// Exposure
+uint8_t gc0329_exposure_neg6[] = {
+ 0xfe, 0x00,
+ 0xd5, 0xe0,
+ 0xfe, 0x01,
+ 0x13, 0x40,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_neg3[] = {
+ 0xfe, 0x00,
+ 0xd5, 0xf0,
+ 0xfe, 0x01,
+ 0x13, 0x48,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_zero[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x00,
+ 0xfe, 0x01,
+ 0x13, 0x60,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_pos3[] = {
+
+ 0xfe, 0x00,
+ 0xd5, 0x10,
+ 0xfe, 0x01,
+ 0x13, 0x70,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_pos6[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x20,
+ 0xfe, 0x01,
+ 0x13, 0x80,
+ 0xfe, 0x00,
+
+};
+
+
+// Color Effect
+uint8_t gc0329_colorfx_none[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x00,
+};
+
+uint8_t gc0329_colorfx_bw[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0x00,
+ 0xdb , 0x00,
+
+};
+
+uint8_t gc0329_colorfx_sepia[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0xd0,
+ 0xdb , 0x28,
+
+};
+
+uint8_t gc0329_colorfx_negative[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x01,
+};
+
+uint8_t gc0329_colorfx_emboss[] = {
+};
+
+uint8_t gc0329_colorfx_sketch[] = {
+
+};
+
+uint8_t gc0329_colorfx_sky_blue[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0x50,
+ 0xdb , 0xe0,
+
+};
+
+uint8_t gc0329_colorfx_grass_green[] = {
+
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0xc0,
+ 0xdb , 0xc0,
+
+};
+
+uint8_t gc0329_colorfx_skin_whiten[] = {
+};
+
+uint8_t gc0329_colorfx_vivid[] = {
+};
+
+// Brightness
+uint8_t gc0329_brightness_neg4[] = {
+};
+
+uint8_t gc0329_brightness_neg3[] = {
+};
+
+uint8_t gc0329_brightness_neg2[] = {
+};
+
+uint8_t gc0329_brightness_neg1[] = {
+};
+
+uint8_t gc0329_brightness_zero[] = {
+};
+
+uint8_t gc0329_brightness_pos1[] = {
+};
+
+uint8_t gc0329_brightness_pos2[] = {
+};
+
+uint8_t gc0329_brightness_pos3[] = {
+};
+
+uint8_t gc0329_brightness_pos4[] = {
+};
+
+// Contrast
+uint8_t gc0329_contrast_neg4[] = {
+};
+
+uint8_t gc0329_contrast_neg3[] = {
+};
+
+uint8_t gc0329_contrast_neg2[] = {
+};
+
+uint8_t gc0329_contrast_neg1[] = {
+};
+
+uint8_t gc0329_contrast_zero[] = {
+};
+
+uint8_t gc0329_contrast_pos1[] = {
+};
+
+uint8_t gc0329_contrast_pos2[] = {
+};
+
+uint8_t gc0329_contrast_pos3[] = {
+};
+
+uint8_t gc0329_contrast_pos4[] = {
+};
+
+// Saturation
+uint8_t gc0329_saturation_neg4[] = {
+};
+
+uint8_t gc0329_saturation_neg3[] = {
+};
+
+uint8_t gc0329_saturation_neg2[] = {
+};
+
+uint8_t gc0329_saturation_neg1[] = {
+};
+
+uint8_t gc0329_saturation_zero[] = {
+};
+
+uint8_t gc0329_saturation_pos1[] = {
+};
+
+uint8_t gc0329_saturation_pos2[] = {
+};
+
+uint8_t gc0329_saturation_pos3[] = {
+};
+
+uint8_t gc0329_saturation_pos4[] = {
+};
+
+
+// Resolution
+
+// QCIF
+uint8_t gc0329_176x144[]={
+};
+
+uint8_t gc0329_320x240[]={
+ 0x59, 0x22,
+};
+
+uint8_t gc0329_640x480[]={
+ 0x59, 0x11,
+};
+
+
+// Initiliztion
+uint8_t gc0329_default_regs_init[] = {
+ 0xfe, 0x80,
+ 0xfc, 0x12,
+ 0xfc, 0x12,
+ 0xfe, 0x00,
+
+ 0xfa, 0x11,
+
+ 0xf0, 0x07,
+ 0xf1, 0x01,
+ 0x73, 0x90,
+ 0x74, 0x80,
+ 0x75, 0x80,
+ 0x76, 0x94,
+ 0xfc, 0x16,
+
+ 0x05, 0x01,
+ 0x06, 0x32,
+ 0x07, 0x00,
+ 0x08, 0x70,
+ 0xfe, 0x01,
+ 0x29, 0x00,
+ 0x2a, 0x3c,
+ 0x2b, 0x02,
+ 0x2c, 0x58,
+ 0x2d, 0x03,
+
+ 0x2e, 0x48,
+ 0x2f, 0x03,
+ 0x30, 0x48,
+ 0x31, 0x05,
+ 0x32, 0xa0,
+ 0x33, 0x20,
+ 0xfe, 0x00,
+
+
+
+
+
+ 0x0a, 0x00,
+ 0x0c, 0x00,
+ 0x17, 0x14,
+ 0x19, 0x05,
+ 0x1b, 0x24,
+ 0x1c, 0x04,
+ 0x1e, 0x00,
+ 0x1f, 0xc8,
+ 0x20, 0x00,
+ 0x21, 0x48,
+ 0x23, 0x22,
+ 0x24, 0x16,
+ 0x26, 0xf7,
+ 0x32, 0x04,
+ 0x33, 0x20,
+ 0x34, 0x20,
+ 0x35, 0x20,
+ 0x36, 0x20,
+ 0x40, 0xff,
+ 0x41, 0x00,
+ 0x42, 0xfe,
+ 0x46, 0x02,
+ 0x4b, 0xcb,
+ 0x4d, 0x01,
+ 0x4f, 0x01,
+ 0x70, 0x48,
+ 0x80, 0xe7,
+ 0x82, 0x55,
+ 0x87, 0x4a,
+ 0xfe, 0x01,
+ 0x18, 0x22,
+ 0xfe, 0x00,
+ 0x9c, 0x0a,
+ 0xa4, 0x50,
+ 0xa5, 0x21,
+ 0xa7, 0x35,
+ 0xdd, 0x54,
+ 0x95, 0x35,
+ 0xfe, 0x00,
+ 0xbf, 0x06,
+ 0xc0, 0x14,
+ 0xc1, 0x27,
+ 0xc2, 0x3b,
+ 0xc3, 0x4f,
+ 0xc4, 0x62,
+ 0xc5, 0x72,
+ 0xc6, 0x8d,
+ 0xc7, 0xa4,
+ 0xc8, 0xb8,
+ 0xc9, 0xc9,
+ 0xca, 0xd6,
+ 0xcb, 0xe0,
+ 0xcc, 0xe8,
+ 0xcd, 0xf4,
+ 0xce, 0xfc,
+ 0xcf, 0xff,
+ 0xfe, 0x00,
+
+ 0xb3, 0x40,// 44
+ 0xb4, 0xfd,
+ 0xb5, 0x02,
+ 0xb6, 0xf0,// fa
+ 0xb7, 0x48,
+ 0xb8, 0xf0,
+
+ 0x50, 0x01,
+ 0x19, 0x05,
+ 0x20, 0x01,
+ 0x22, 0xba,
+ 0x21, 0x48,
+ 0xfe, 0x00,
+ 0xd1, 0x34,
+ 0xd2, 0x34,
+ 0xfe, 0x01,
+ 0x10, 0x40,
+ 0x12, 0x07,
+ 0x13, 0x60,
+ 0x17, 0x88,
+ 0x21, 0xb0,
+ 0x22, 0x48,
+ 0x3c, 0x95,
+ 0x3d, 0x50,
+ 0x3e, 0x48,
+ 0xfe, 0x01,
+ 0x06, 0x08,
+ 0x07, 0x06,
+ 0x08, 0xa6,
+ 0x09, 0xee,
+ 0x6a, 0x40,
+ 0x6b, 0x39,
+ 0xd0, 0x00,
+ 0xd2, 0x2c,
+ 0xd3, 0x80,
+ 0x9c, 0x02,
+ 0x9d, 0x10,
+ 0xfe, 0x01,
+ 0xa0, 0x00,
+ 0xa1, 0x3c,
+ 0xa2, 0x50,
+ 0xa3, 0x00,
+ 0xa8, 0x0f,
+ 0xa9, 0x08,
+ 0xaa, 0x00,
+ 0xab, 0x04,
+ 0xac, 0x00,
+ 0xad, 0x07,
+ 0xae, 0x0e,
+ 0xaf, 0x00,
+ 0xb0, 0x00,
+ 0xb1, 0x09,
+ 0xb2, 0x00,
+ 0xb3, 0x00,
+ 0xb4, 0x31,
+ 0xb5, 0x19,
+ 0xb6, 0x24,
+ 0xba, 0x3a,
+ 0xbb, 0x24,
+ 0xbc, 0x2a,
+ 0xc0, 0x17,
+ 0xc1, 0x13,
+ 0xc2, 0x17,
+ 0xc6, 0x21,
+ 0xc7, 0x1c,
+ 0xc8, 0x1c,
+ 0xb7, 0x00,
+ 0xb8, 0x00,
+ 0xb9, 0x00,
+ 0xbd, 0x00,
+ 0xbe, 0x00,
+ 0xbf, 0x00,
+ 0xc3, 0x00,
+ 0xc4, 0x00,
+ 0xc5, 0x00,
+ 0xc9, 0x00,
+ 0xca, 0x00,
+ 0xcb, 0x00,
+ 0xa4, 0x00,
+ 0xa5, 0x00,
+ 0xa6, 0x00,
+ 0xa7, 0x00,
+ 0xfe, 0x00,
+
+ 0xfe,0x01,
+ 0x18,0x22,
+ 0x21,0xc0,
+ 0x06,0x12,
+ 0x08,0x9c,
+ 0x51,0x28,
+ 0x52,0x10,
+ 0x53,0x20,
+ 0x54,0x40,
+ 0x55,0x16,
+ 0x56,0x30,
+ 0x58,0x60,
+ 0x59,0x08,
+ 0x5c,0x35,
+ 0x5d,0x72,
+ 0x67,0x80,
+ 0x68,0x60,
+ 0x69,0x90,
+ 0x6c,0x30,
+ 0x6d,0x60,
+ 0x70,0x10,
+ 0xfe,0x00,
+ 0x9c,0x0a,
+ 0xa0,0xaf,
+ 0xa2,0xff,
+ 0xa4,0x60,
+ 0xa5,0x31,
+ 0xa7,0x35,
+ 0x42,0xfe,
+ 0xd1,0x34,
+ 0xd2,0x34,
+ 0xfe,0x00,
+
+ 0xa0, 0xaf,
+ 0xa2, 0xff,
+ 0x44, 0xa2,
+};
+
+uint8_t gc0329_default_regs_exit[] = {
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0329/gc0329-kyt-174-v7.h b/drivers/media/video/wmt_v4l2/sensors/gc0329/gc0329-kyt-174-v7.h
new file mode 100755
index 00000000..dfa6527f
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0329/gc0329-kyt-174-v7.h
@@ -0,0 +1,992 @@
+#ifndef GC0329_H
+#define GC0329_H
+
+
+// Scene Mode
+uint8_t gc0329_scene_mode_auto[] = {
+};
+
+uint8_t gc0329_scene_mode_night[] = {
+};
+
+
+// White Balance
+uint8_t gc0329_wb_auto [] = {
+ 0xfe, 0x00,
+// 0x77, 0x57,
+// 0x78, 0x4d,
+// 0x79, 0x45,
+ 0x42, 0xfe, //AWB on
+};
+
+uint8_t gc0329_wb_incandescent [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x48,
+ 0x78, 0x40,
+ 0x79, 0x5c,
+
+};
+
+uint8_t gc0329_wb_fluorescent [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x40,
+ 0x78, 0x42,
+ 0x79, 0x50,
+
+};
+
+uint8_t gc0329_wb_daylight [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x74,
+ 0x78, 0x52,
+ 0x79, 0x40,
+
+};
+
+uint8_t gc0329_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x8c,
+ 0x78, 0x50,
+ 0x79, 0x40,
+
+};
+
+uint8_t gc0329_wb_tungsten [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x40,
+ 0x78, 0x54,
+ 0x79, 0x70,
+
+};
+
+
+// Exposure
+uint8_t gc0329_exposure_neg6[] = {
+ 0xfe, 0x00,
+ 0xd5, 0xe0,
+ 0xfe, 0x01,
+ 0x13, 0x48,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_neg3[] = {
+ 0xfe, 0x00,
+ 0xd5, 0xf0,
+ 0xfe, 0x01,
+ 0x13, 0x75,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_zero[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x00,
+ 0xfe, 0x01,
+ 0x13, 0x96,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_pos3[] = {
+
+ 0xfe, 0x00,
+ 0xd5, 0x10,
+ 0xfe, 0x01,
+ 0x13, 0x98,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_pos6[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x20,
+ 0xfe, 0x01,
+ 0x13, 0x9c,
+ 0xfe, 0x00,
+
+};
+
+
+// Color Effect
+uint8_t gc0329_colorfx_none[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x00,
+};
+
+uint8_t gc0329_colorfx_bw[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0x00,
+ 0xdb , 0x00,
+
+};
+
+uint8_t gc0329_colorfx_sepia[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0xd0,
+ 0xdb , 0x28,
+
+};
+
+uint8_t gc0329_colorfx_negative[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x01,
+};
+
+uint8_t gc0329_colorfx_emboss[] = {
+};
+
+uint8_t gc0329_colorfx_sketch[] = {
+
+};
+
+uint8_t gc0329_colorfx_sky_blue[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0x50,
+ 0xdb , 0xe0,
+
+};
+
+uint8_t gc0329_colorfx_grass_green[] = {
+
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0xc0,
+ 0xdb , 0xc0,
+
+};
+
+uint8_t gc0329_colorfx_skin_whiten[] = {
+};
+
+uint8_t gc0329_colorfx_vivid[] = {
+};
+
+// Brightness
+uint8_t gc0329_brightness_neg4[] = {
+};
+
+uint8_t gc0329_brightness_neg3[] = {
+};
+
+uint8_t gc0329_brightness_neg2[] = {
+};
+
+uint8_t gc0329_brightness_neg1[] = {
+};
+
+uint8_t gc0329_brightness_zero[] = {
+};
+
+uint8_t gc0329_brightness_pos1[] = {
+};
+
+uint8_t gc0329_brightness_pos2[] = {
+};
+
+uint8_t gc0329_brightness_pos3[] = {
+};
+
+uint8_t gc0329_brightness_pos4[] = {
+};
+
+// Contrast
+uint8_t gc0329_contrast_neg4[] = {
+};
+
+uint8_t gc0329_contrast_neg3[] = {
+};
+
+uint8_t gc0329_contrast_neg2[] = {
+};
+
+uint8_t gc0329_contrast_neg1[] = {
+};
+
+uint8_t gc0329_contrast_zero[] = {
+};
+
+uint8_t gc0329_contrast_pos1[] = {
+};
+
+uint8_t gc0329_contrast_pos2[] = {
+};
+
+uint8_t gc0329_contrast_pos3[] = {
+};
+
+uint8_t gc0329_contrast_pos4[] = {
+};
+
+// Saturation
+uint8_t gc0329_saturation_neg4[] = {
+};
+
+uint8_t gc0329_saturation_neg3[] = {
+};
+
+uint8_t gc0329_saturation_neg2[] = {
+};
+
+uint8_t gc0329_saturation_neg1[] = {
+};
+
+uint8_t gc0329_saturation_zero[] = {
+};
+
+uint8_t gc0329_saturation_pos1[] = {
+};
+
+uint8_t gc0329_saturation_pos2[] = {
+};
+
+uint8_t gc0329_saturation_pos3[] = {
+};
+
+uint8_t gc0329_saturation_pos4[] = {
+};
+
+
+// Resolution
+
+// QCIF
+uint8_t gc0329_176x144[]={
+};
+
+uint8_t gc0329_320x240[]={
+ 0x59, 0x22,
+};
+
+uint8_t gc0329_640x480[]={
+ 0x59, 0x11,
+};
+
+
+// Initiliztion
+uint8_t gc0329_default_regs_init[] = {
+#if 1
+ 0xfe, 0x80,
+ 0xfc, 0x12,
+ 0xfc, 0x12,
+ 0xfe, 0x00,
+ 0xf0, 0x07,
+ 0xf1, 0x01,
+
+//20131009 50hz anti fliker
+ 0xfa, 0x11, //0x00 clk_div
+ 0x05, 0x01,
+ 0x06, 0x32,
+ 0x07, 0x00,
+ 0x08, 0x70,
+
+ 0xfe, 0x01,
+ 0x29, 0x00, //anti-flicker step [11:8]
+ 0x2a, 0x3c, //anti-flicker step [7:0]
+
+ 0x2b, 0x02, //exp level 0 14.28fps
+ 0x2c, 0x58,
+ 0x2d, 0x02, //exp level 1 12.50fps
+ 0x2e, 0xd0,
+ 0x2f, 0x03, //exp level 2 10.00fps
+ 0x30, 0x48,
+ 0x31, 0x04, //exp level 3 7.14fps
+ 0x32, 0x38,
+ 0x33, 0x20,
+ 0xfe, 0x00,
+//
+ 0x73, 0x90,
+ 0x74, 0x80,
+ 0x75, 0x80,
+ 0x76, 0x94,
+ 0xfc, 0x16,
+ 0x0a, 0x02,
+ 0x0c, 0x02,
+ 0x17, 0x14,
+ 0x19, 0x05,
+ 0x1b, 0x24,
+ 0x1c, 0x04,
+ 0x1e, 0x00,
+ 0x1f, 0xc0,
+ 0x20, 0x00,
+ 0x21, 0x48,
+ 0x23, 0x22,
+ 0x24, 0x16,
+ 0x26, 0xf7,
+ 0x32, 0x04,
+ 0x33, 0x20,
+ 0x34, 0x20,
+ 0x35, 0x20,
+ 0x36, 0x20,
+ 0x40, 0xff,
+ 0x41, 0x00,
+ 0x42, 0xfe,
+ 0x46, 0x02,
+ 0x4b, 0xcb,
+ 0x4d, 0x01,
+ 0x4f, 0x01,
+ 0x70, 0x48,
+ 0x80, 0xe7,
+ 0x82, 0x25,//55
+ 0x83, 0x01,//03
+ 0x87, 0x4a,
+ 0xfe, 0x01,
+ 0x18, 0x22,
+ 0xfe, 0x00,
+ 0x9c, 0x0a,
+ 0xa4, 0x50,
+ 0xa5, 0x21,
+ 0xa7, 0x35,
+ 0xdd, 0x04,//54
+ 0x95, 0x35,
+ 0xfe, 0x00,
+ 0xbf, 0x06,
+ 0xc0, 0x14,
+ 0xc1, 0x27,
+ 0xc2, 0x3b,
+ 0xc3, 0x4f,
+ 0xc4, 0x62,
+ 0xc5, 0x72,
+ 0xc6, 0x8d,
+ 0xc7, 0xa4,
+ 0xc8, 0xb8,
+ 0xc9, 0xc9,
+ 0xca, 0xd6,
+ 0xcb, 0xe0,
+ 0xcc, 0xe8,
+ 0xcd, 0xf4,
+ 0xce, 0xfc,
+ 0xcf, 0xff,
+ 0xfe, 0x00,
+ 0xb3, 0x38,
+ 0xb4, 0xfd,
+ 0xb5, 0x02,
+ 0xb6, 0xfa,
+ 0xb7, 0x48,
+ 0xb8, 0xf0,
+ 0x50, 0x01,
+ 0x19, 0x05,
+ 0x20, 0x01,
+ 0x22, 0xba,
+ 0x21, 0x48,
+ 0xfe, 0x00,
+ 0xd1, 0x28,
+ 0xd2, 0x28,
+ 0xd3, 0x32,//40
+ 0xfe, 0x01,
+ 0x10, 0x40,
+ 0x12, 0x07,
+ 0x13, 0x96,
+ 0x17, 0x88,
+ 0x21, 0xb0,
+ 0x22, 0x48,
+ 0x3c, 0x95,
+ 0x3d, 0x50,
+ 0x3e, 0x48,
+ 0xfe, 0x01,
+ 0x06, 0x08,
+ 0x07, 0x06,
+ 0x08, 0xa6,
+ 0x09, 0xee,
+ 0x6a, 0x40,
+ 0x6b, 0x39,
+ 0xd0, 0x00,
+ 0xd2, 0x2c,
+ 0xd3, 0x80,
+ 0x9c, 0x02,
+ 0x9d, 0x10,
+ 0xfe, 0x01,
+ 0xa0, 0x00,
+ 0xa1, 0x3c,
+ 0xa2, 0x50,
+ 0xa3, 0x00,
+ 0xa8, 0x0f,
+ 0xa9, 0x08,
+ 0xaa, 0x00,
+ 0xab, 0x04,
+ 0xac, 0x00,
+ 0xad, 0x07,
+ 0xae, 0x0e,
+ 0xaf, 0x00,
+ 0xb0, 0x00,
+ 0xb1, 0x09,
+ 0xb2, 0x00,
+ 0xb3, 0x00,
+ 0xb4, 0x31,
+ 0xb5, 0x19,
+ 0xb6, 0x24,
+ 0xba, 0x3a,
+ 0xbb, 0x24,
+ 0xbc, 0x2a,
+ 0xc0, 0x17,
+ 0xc1, 0x13,
+ 0xc2, 0x17,
+ 0xc6, 0x21,
+ 0xc7, 0x1c,
+ 0xc8, 0x1c,
+ 0xb7, 0x00,
+ 0xb8, 0x00,
+ 0xb9, 0x00,
+ 0xbd, 0x00,
+ 0xbe, 0x00,
+ 0xbf, 0x00,
+ 0xc3, 0x00,
+ 0xc4, 0x00,
+ 0xc5, 0x00,
+ 0xc9, 0x00,
+ 0xca, 0x00,
+ 0xcb, 0x00,
+ 0xa4, 0x00,
+ 0xa5, 0x00,
+ 0xa6, 0x00,
+ 0xa7, 0x00,
+ 0xfe, 0x00,
+
+ 0xfe,0x01,
+ 0x18,0x22,
+ 0x21,0xc0,
+ 0x06,0x12,
+ 0x08,0x9c,
+ 0x51,0x28,
+ 0x52,0x10,
+ 0x53,0x20,
+ 0x54,0x40,
+ 0x55,0x16,
+ 0x56,0x30,
+ 0x58,0x60,
+ 0x59,0x08,
+ 0x5c,0x35,
+ 0x5d,0x72,
+ 0x67,0x80,
+ 0x68,0x60,
+ 0x69,0x90,
+ 0x6c,0x30,
+ 0x6d,0x60,
+ 0x70,0x10,
+ 0xfe,0x00,
+ 0x9c,0x0a,
+ 0xa0,0xaf,
+ 0xa2,0xff,
+ 0xa4,0x60,
+ 0xa5,0x31,
+ 0xa7,0x35,
+ 0x42,0xfe,
+ 0xd1,0x34,
+ 0xd2,0x34,
+ 0xfe,0x00,
+
+ 0xa0, 0xaf,
+ 0xa2, 0xff,
+ 0x44, 0xa2,
+#else
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfc,0x16,
+ 0xfc,0x16,
+ 0xfc,0x16,
+ 0xfc,0x16,
+ 0xfe,0x00,
+ 0xf0,0x07,
+ 0xf1,0x01,
+
+ 0xfa,0x11, //0x00 clk_div
+
+ 0x73, 0x90,
+ 0x74, 0x80,
+ 0x75, 0x80,
+ 0x76, 0x94,
+
+ 0x42, 0x00,
+ 0x77, 0x57,
+ 0x78, 0x4d,
+ 0x79, 0x45,
+ //0x42, 0xfc,
+
+#if 1 //12M clk
+ 0x05, 0x01,
+ 0x06, 0x32,
+ 0x07, 0x00,
+ 0x08, 0x70,
+
+ 0xfe, 0x01,
+ 0x29, 0x00, //anti-flicker step [11:8]
+ 0x2a, 0x3c, //anti-flicker step [7:0]
+
+ 0x2b, 0x02, //exp level 0 14.28fps
+ 0x2c, 0x58,
+ 0x2d, 0x02, //exp level 1 12.50fps
+ 0x2e, 0xd0,
+ 0x2f, 0x03, //exp level 2 10.00fps
+ 0x30, 0x48,
+ 0x31, 0x04, //exp level 3 7.14fps
+ 0x32, 0x38,
+ 0x33, 0x20,
+ 0xfe, 0x00,
+#else //24M clk
+ 0x05, 0x02,
+ 0x06, 0x2c,
+ 0x07, 0x00,
+ 0x08, 0xb8,
+
+ 0xfe, 0x01,
+ 0x29, 0x00, //anti-flicker step [11:8]
+ 0x2a, 0x60, //anti-flicker step [7:0]
+
+ 0x2b, 0x02, //exp level 0 14.28fps
+ 0x2c, 0xa0,
+ 0x2d, 0x03, //exp level 1 12.50fps
+ 0x2e, 0x00,
+ 0x2f, 0x03, //exp level 2 10.00fps
+ 0x30, 0xc0,
+ 0x31, 0x05, //exp level 3 7.14fps
+ 0x32, 0x40,
+ 0x33, 0x20,
+ 0xfe, 0x00,
+#endif
+ ////////////////////analog////////////////////
+ 0x0a, 0x02,
+ 0x0c, 0x02,
+ 0x17, 0x14,
+ 0x19, 0x05,
+ 0x1b, 0x24,
+ 0x1c, 0x04,
+ 0x1e, 0x08,
+ 0x1f, 0xc8, // c0 20120720
+ 0x20, 0x00,
+ 0x21, 0x48,
+ 0x22, 0xba,
+ 0x23, 0x22,
+ 0x24, 0x16,
+
+ ////////////////////blk////////////////////
+ 0x26,0xf7,
+ 0x28,0x7f,// 20130819 lanking
+ 0x29,0x80,
+ 0x32,0x04,
+ 0x33,0x20,
+ 0x34,0x20,
+ 0x35,0x20,
+ 0x36,0x20,
+
+ ////////////////////ISP BLOCK ENABL////////////////////
+ 0x40, 0xff,
+ 0x41, 0x44,
+ 0x42, 0x7e,
+ 0x44, 0xa2, ///yuv order
+ 0x46, 0x03, // vsync
+ 0x4b, 0xca,
+ 0x4d, 0x01,
+ 0x4f, 0x01,
+ 0x70, 0x48,
+
+ //0xb0,0x00,
+ //0xbc,0x00,
+ //0xbd,0x00,
+ //0xbe,0x00,
+ ////////////////////DNDD////////////////////
+ 0x80, 0xe7,
+ 0x82, 0x55,
+ 0x83, 0x03,
+ 0x87, 0x4a,
+
+ ////////////////////INTPEE////////////////////
+ 0x95,0x45,
+
+ ////////////////////ASDE////////////////////
+ //0xfe,0x01,
+ //0x18,0x22,
+ //0xfe,0x00,
+ //0x9c,0x0a,
+ //0xa0,0xaf,
+ //0xa2,0xff,
+ //0xa4,0x50,
+ //0xa5,0x21,
+ //0xa7,0x35,
+
+ ////////////////////RGB gamma////////////////////
+ //RGB gamma m4'
+ 0xbf, 0x06,
+ 0xc0, 0x14,
+ 0xc1, 0x27,
+ 0xc2, 0x3b,
+ 0xc3, 0x4f,
+ 0xc4, 0x62,
+ 0xc5, 0x72,
+ 0xc6, 0x8d,
+ 0xc7, 0xa4,
+ 0xc8, 0xb8,
+ 0xc9, 0xc9,
+ 0xcA, 0xd6,
+ 0xcB, 0xe0,
+ 0xcC, 0xe8,
+ 0xcD, 0xf4,
+ 0xcE, 0xFc,
+ 0xcF, 0xFF,
+#if 0
+ case GC0329_RGB_Gamma_m1: //smallest gamma curve
+ 0xfe, 0x00,
+ 0xbf, 0x06,
+ 0xc0, 0x12,
+ 0xc1, 0x22,
+ 0xc2, 0x35,
+ 0xc3, 0x4b,
+ 0xc4, 0x5f,
+ 0xc5, 0x72,
+ 0xc6, 0x8d,
+ 0xc7, 0xa4,
+ 0xc8, 0xb8,
+ 0xc9, 0xc8,
+ 0xca, 0xd4,
+ 0xcb, 0xde,
+ 0xcc, 0xe6,
+ 0xcd, 0xf1,
+ 0xce, 0xf8,
+ 0xcf, 0xfd,
+
+ case GC0329_RGB_Gamma_m2:
+ 0xBF, 0x08,
+ 0xc0, 0x0F,
+ 0xc1, 0x21,
+ 0xc2, 0x32,
+ 0xc3, 0x43,
+ 0xc4, 0x50,
+ 0xc5, 0x5E,
+ 0xc6, 0x78,
+ 0xc7, 0x90,
+ 0xc8, 0xA6,
+ 0xc9, 0xB9,
+ 0xcA, 0xC9,
+ 0xcB, 0xD6,
+ 0xcC, 0xE0,
+ 0xcD, 0xEE,
+ 0xcE, 0xF8,
+ 0xcF, 0xFF,
+
+ case GC0329_RGB_Gamma_m3:
+ 0xBF, 0x0B,
+ 0xc0, 0x16,
+ 0xc1, 0x29,
+ 0xc2, 0x3C,
+ 0xc3, 0x4F,
+ 0xc4, 0x5F,
+ 0xc5, 0x6F,
+ 0xc6, 0x8A,
+ 0xc7, 0x9F,
+ 0xc8, 0xB4,
+ 0xc9, 0xC6,
+ 0xcA, 0xD3,
+ 0xcB, 0xDD,
+ 0xcC, 0xE5,
+ 0xcD, 0xF1,
+ 0xcE, 0xFA,
+ 0xcF, 0xFF,
+
+ case GC0329_RGB_Gamma_m4:
+ 0xBF, 0x0E,
+ 0xc0, 0x1C,
+ 0xc1, 0x34,
+ 0xc2, 0x48,
+ 0xc3, 0x5A,
+ 0xc4, 0x6B,
+ 0xc5, 0x7B,
+ 0xc6, 0x95,
+ 0xc7, 0xAB,
+ 0xc8, 0xBF,
+ 0xc9, 0xCE,
+ 0xcA, 0xD9,
+ 0xcB, 0xE4,
+ 0xcC, 0xEC,
+ 0xcD, 0xF7,
+ 0xcE, 0xFD,
+ 0xcF, 0xFF,
+
+ case GC0329_RGB_Gamma_m5:
+ 0xBF, 0x10,
+ 0xc0, 0x20,
+ 0xc1, 0x38,
+ 0xc2, 0x4E,
+ 0xc3, 0x63,
+ 0xc4, 0x76,
+ 0xc5, 0x87,
+ 0xc6, 0xA2,
+ 0xc7, 0xB8,
+ 0xc8, 0xCA,
+ 0xc9, 0xD8,
+ 0xcA, 0xE3,
+ 0xcB, 0xEB,
+ 0xcC, 0xF0,
+ 0xcD, 0xF8,
+ 0xcE, 0xFD,
+ 0xcF, 0xFF,
+
+ case GC0329_RGB_Gamma_m6: // largest gamma curve
+ 0xBF, 0x14,
+ 0xc0, 0x28,
+ 0xc1, 0x44,
+ 0xc2, 0x5D,
+ 0xc3, 0x72,
+ 0xc4, 0x86,
+ 0xc5, 0x95,
+ 0xc6, 0xB1,
+ 0xc7, 0xC6,
+ 0xc8, 0xD5,
+ 0xc9, 0xE1,
+ 0xcA, 0xEA,
+ 0xcB, 0xF1,
+ 0xcC, 0xF5,
+ 0xcD, 0xFB,
+ 0xcE, 0xFE,
+ 0xcF, 0xFF,
+
+ case GC0329_RGB_Gamma_night: //Gamma for night mode
+ 0xBF, 0x0B,
+ 0xc0, 0x16,
+ 0xc1, 0x29,
+ 0xc2, 0x3C,
+ 0xc3, 0x4F,
+ 0xc4, 0x5F,
+ 0xc5, 0x6F,
+ 0xc6, 0x8A,
+ 0xc7, 0x9F,
+ 0xc8, 0xB4,
+ 0xc9, 0xC6,
+ 0xcA, 0xD3,
+ 0xcB, 0xDD,
+ 0xcC, 0xE5,
+ 0xcD, 0xF1,
+ 0xcE, 0xFA,
+ 0xcF, 0xFF,
+
+ //GC0329_RGB_Gamma_m1 default
+ 0xfe, 0x00,
+ 0xbf, 0x06,
+ 0xc0, 0x12,
+ 0xc1, 0x22,
+ 0xc2, 0x35,
+ 0xc3, 0x4b,
+ 0xc4, 0x5f,
+ 0xc5, 0x72,
+ 0xc6, 0x8d,
+ 0xc7, 0xa4,
+ 0xc8, 0xb8,
+ 0xc9, 0xc8,
+ 0xca, 0xd4,
+ 0xcb, 0xde,
+ 0xcc, 0xe6,
+ 0xcd, 0xf1,
+ 0xce, 0xf8,
+ 0xcf, 0xfd,
+#endif
+
+ //////////////////CC///////////////////
+ 0xfe,0x00,
+
+ 0xb3,0x44,
+ 0xb4,0xfd,
+ 0xb5,0x02,
+ 0xb6,0xfa,
+ 0xb7,0x48,
+ 0xb8,0xf0,
+
+ // crop
+ 0x50,0x01,
+
+ ////////////////////YCP////////////////////
+ 0xfe,0x00,
+ 0xd1,0x38,
+ 0xd2,0x38,
+ 0xd5, 0x00, /// luma_offset
+ 0xdd,0x54,
+
+ ////////////////////AEC////////////////////
+ 0xfe,0x01,
+ 0x10,0x40,
+ 0x11,0x21,
+ 0x12,0x07,
+ 0x13,0x50,
+ 0x17,0x88,
+ 0x21,0xb0,
+ 0x22,0x48,
+ 0x3c,0x95,
+ 0x3d,0x50,
+ 0x3e,0x48,
+
+ ////////////////////AWB////////////////////
+ 0xfe,0x01,
+ 0x06,0x16,
+ 0x07,0x06,
+ 0x08,0x98,
+ 0x09,0xee,
+ 0x50,0xfc,
+ 0x51,0x20,
+ 0x52,0x0b,
+ 0x53,0x20,
+ 0x54,0x40,
+ 0x55,0x10,
+ 0x56,0x20,
+ //0x57,0x40,
+ 0x58,0xa0,
+ 0x59,0x28,
+ 0x5a,0x02,
+ 0x5b,0x63,
+ 0x5c,0x34,
+ 0x5d,0x73,
+ 0x5e,0x11,
+ 0x5f,0x40,
+ 0x60,0x40,
+ 0x61,0xc8,
+ 0x62,0xa0,
+ 0x63,0x40,
+ 0x64,0x50,
+ 0x65,0x98,
+ 0x66,0xfa,
+ 0x67,0x70,
+ 0x68,0x58,
+ 0x69,0x85,
+ 0x6a,0x40,
+ 0x6b,0x39,
+ 0x6c,0x18,
+ 0x6d,0x28,
+ 0x6e,0x41,
+ 0x70,0x02,
+ 0x71,0x00,
+ 0x72,0x10,
+ 0x73,0x40,
+
+ //0x74,0x32,
+ //0x75,0x40,
+ //0x76,0x30,
+ //0x77,0x48,
+ //0x7a,0x50,
+ //0x7b,0x20,
+
+ 0x80,0x60,
+ 0x81,0x50,
+ 0x82,0x42,
+ 0x83,0x40,
+ 0x84,0x40,
+ 0x85,0x40,
+
+ 0x74,0x40,
+ 0x75,0x58,
+ 0x76,0x24,
+ 0x77,0x40,
+ 0x78,0x20,
+ 0x79,0x60,
+ 0x7a,0x58,
+ 0x7b,0x20,
+ 0x7c,0x30,
+ 0x7d,0x35,
+ 0x7e,0x10,
+ 0x7f,0x08,
+
+ ////////////////////ABS////////////////////
+ 0x9c,0x02,
+ 0x9d,0x20,
+ //0x9f,0x40,
+
+ ////////////////////CC-AWB////////////////////
+ 0xd0,0x00,
+ 0xd2,0x2c,
+ 0xd3,0x80,
+
+ ////////////////////LSC///////////////////
+ 0xfe,0x01,
+ 0xa0,0x00,
+ 0xa1,0x3c,
+ 0xa2,0x50,
+ 0xa3,0x00,
+ 0xa8,0x0f,
+ 0xa9,0x08,
+ 0xaa,0x00,
+ 0xab,0x04,
+ 0xac,0x00,
+ 0xad,0x07,
+ 0xae,0x0e,
+ 0xaf,0x00,
+ 0xb0,0x00,
+ 0xb1,0x09,
+ 0xb2,0x00,
+ 0xb3,0x00,
+ 0xb4,0x31,
+ 0xb5,0x19,
+ 0xb6,0x24,
+ 0xba,0x3a,
+ 0xbb,0x24,
+ 0xbc,0x2a,
+ 0xc0,0x17,
+ 0xc1,0x13,
+ 0xc2,0x17,
+ 0xc6,0x21,
+ 0xc7,0x1c,
+ 0xc8,0x1c,
+ 0xb7,0x00,
+ 0xb8,0x00,
+ 0xb9,0x00,
+ 0xbd,0x00,
+ 0xbe,0x00,
+ 0xbf,0x00,
+ 0xc3,0x00,
+ 0xc4,0x00,
+ 0xc5,0x00,
+ 0xc9,0x00,
+ 0xca,0x00,
+ 0xcb,0x00,
+ 0xa4,0x00,
+ 0xa5,0x00,
+ 0xa6,0x00,
+ 0xa7,0x00,
+
+ ////////////////////asde ///////////////////
+ //0xa0,0xaf,
+ //0xa2,0xff,
+
+ ////////////20120427/////////////
+ 0xfe,0x01,
+ 0x18,0x22,
+ 0x21,0xc0,
+ 0x06,0x12,
+ 0x08,0x9c,
+ 0x51,0x28,
+ 0x52,0x10,
+ 0x53,0x20,
+ 0x54,0x40,
+ 0x55,0x16,
+ 0x56,0x30,
+ 0x58,0x60,
+ 0x59,0x08,
+ 0x5c,0x35,
+ 0x5d,0x72,
+ 0x67,0x80,
+ 0x68,0x60,
+ 0x69,0x90,
+ 0x6c,0x30,
+ 0x6d,0x60,
+ 0x70,0x10,
+
+ 0xfe,0x00,
+ 0x9c,0x0a,
+ 0xa0,0xaf,
+ 0xa2,0xff,
+ 0xa4,0x60,
+ 0xa5,0x31,
+ 0xa7,0x35,
+ 0x42,0xfe,
+ 0xd1,0x34,
+ 0xd2,0x34,
+ 0xfe,0x00,
+#endif
+
+};
+
+uint8_t gc0329_default_regs_exit[] = {
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0329/gc0329-zh1426.h b/drivers/media/video/wmt_v4l2/sensors/gc0329/gc0329-zh1426.h
new file mode 100755
index 00000000..37dd3384
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0329/gc0329-zh1426.h
@@ -0,0 +1,530 @@
+#ifndef GC0329_H
+#define GC0329_H
+
+
+// Scene Mode
+uint8_t gc0329_scene_mode_auto[] = {
+};
+
+uint8_t gc0329_scene_mode_night[] = {
+};
+
+
+// White Balance
+uint8_t gc0329_wb_auto [] = {
+ 0xfe, 0x00,
+ 0x77, 0x57,
+ 0x78, 0x4d,
+ 0x79, 0x45,
+ 0x42, 0xfe, //AWB on
+};
+
+uint8_t gc0329_wb_incandescent [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x48,
+ 0x78, 0x40,
+ 0x79, 0x5c,
+
+};
+
+uint8_t gc0329_wb_fluorescent [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x40,
+ 0x78, 0x42,
+ 0x79, 0x50,
+
+};
+
+uint8_t gc0329_wb_daylight [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x74,
+ 0x78, 0x52,
+ 0x79, 0x40,
+
+};
+
+uint8_t gc0329_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x8c,
+ 0x78, 0x50,
+ 0x79, 0x40,
+
+};
+
+uint8_t gc0329_wb_tungsten [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x40,
+ 0x78, 0x54,
+ 0x79, 0x70,
+
+};
+
+
+// Exposure
+uint8_t gc0329_exposure_neg6[] = {
+ 0xfe, 0x00,
+ 0xd5, 0xe0,
+ 0xfe, 0x01,
+ 0x13, 0x40,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_neg3[] = {
+ 0xfe, 0x00,
+ 0xd5, 0xf0,
+ 0xfe, 0x01,
+ 0x13, 0x48,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_zero[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x04, /// 00
+ 0xfe, 0x01,
+ 0x13, 0x6a,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_pos3[] = {
+
+ 0xfe, 0x00,
+ 0xd5, 0x10,
+ 0xfe, 0x01,
+ 0x13, 0x70,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_pos6[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x20,
+ 0xfe, 0x01,
+ 0x13, 0x80,
+ 0xfe, 0x00,
+
+};
+
+
+// Color Effect
+uint8_t gc0329_colorfx_none[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x00,
+};
+
+uint8_t gc0329_colorfx_bw[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0x00,
+ 0xdb , 0x00,
+
+};
+
+uint8_t gc0329_colorfx_sepia[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0xd0,
+ 0xdb , 0x28,
+
+};
+
+uint8_t gc0329_colorfx_negative[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x01,
+};
+
+uint8_t gc0329_colorfx_emboss[] = {
+};
+
+uint8_t gc0329_colorfx_sketch[] = {
+
+};
+
+uint8_t gc0329_colorfx_sky_blue[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0x50,
+ 0xdb , 0xe0,
+
+};
+
+uint8_t gc0329_colorfx_grass_green[] = {
+
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0xc0,
+ 0xdb , 0xc0,
+
+};
+
+uint8_t gc0329_colorfx_skin_whiten[] = {
+};
+
+uint8_t gc0329_colorfx_vivid[] = {
+};
+
+// Brightness
+uint8_t gc0329_brightness_neg4[] = {
+};
+
+uint8_t gc0329_brightness_neg3[] = {
+};
+
+uint8_t gc0329_brightness_neg2[] = {
+};
+
+uint8_t gc0329_brightness_neg1[] = {
+};
+
+uint8_t gc0329_brightness_zero[] = {
+};
+
+uint8_t gc0329_brightness_pos1[] = {
+};
+
+uint8_t gc0329_brightness_pos2[] = {
+};
+
+uint8_t gc0329_brightness_pos3[] = {
+};
+
+uint8_t gc0329_brightness_pos4[] = {
+};
+
+// Contrast
+uint8_t gc0329_contrast_neg4[] = {
+};
+
+uint8_t gc0329_contrast_neg3[] = {
+};
+
+uint8_t gc0329_contrast_neg2[] = {
+};
+
+uint8_t gc0329_contrast_neg1[] = {
+};
+
+uint8_t gc0329_contrast_zero[] = {
+};
+
+uint8_t gc0329_contrast_pos1[] = {
+};
+
+uint8_t gc0329_contrast_pos2[] = {
+};
+
+uint8_t gc0329_contrast_pos3[] = {
+};
+
+uint8_t gc0329_contrast_pos4[] = {
+};
+
+// Saturation
+uint8_t gc0329_saturation_neg4[] = {
+};
+
+uint8_t gc0329_saturation_neg3[] = {
+};
+
+uint8_t gc0329_saturation_neg2[] = {
+};
+
+uint8_t gc0329_saturation_neg1[] = {
+};
+
+uint8_t gc0329_saturation_zero[] = {
+};
+
+uint8_t gc0329_saturation_pos1[] = {
+};
+
+uint8_t gc0329_saturation_pos2[] = {
+};
+
+uint8_t gc0329_saturation_pos3[] = {
+};
+
+uint8_t gc0329_saturation_pos4[] = {
+};
+
+
+// Resolution
+
+// QCIF
+uint8_t gc0329_176x144[]={
+};
+
+uint8_t gc0329_320x240[]={
+ 0x59, 0x22,
+};
+
+uint8_t gc0329_640x480[]={
+ 0x59, 0x11,
+};
+
+
+// Initiliztion
+uint8_t gc0329_default_regs_init[] = {
+ 0xfe, 0x80,
+ 0xfc, 0x12,
+ 0xfc, 0x12,
+ 0xfe, 0x00,
+
+ 0xfa, 0x11,
+
+ 0xf0, 0x07,
+ 0xf1, 0x01,
+ 0x73, 0x90,
+ 0x74, 0x80,
+ 0x75, 0x80,
+ 0x76, 0x94,
+ 0xfc, 0x16,
+
+ 0x05, 0x01,
+ 0x06, 0x32,
+ 0x07, 0x00,
+ 0x08, 0x70,
+ 0xfe, 0x01,
+ 0x29, 0x00,
+ 0x2a, 0x3c,
+ 0x2b, 0x02,
+ 0x2c, 0x58,
+ 0x2d, 0x03,
+
+ 0x2e, 0x48,
+ 0x2f, 0x03,
+ 0x30, 0x48,
+ 0x31, 0x05,
+ 0x32, 0xa0,
+ 0x33, 0x20,
+ 0xfe, 0x00,
+
+
+
+
+
+ 0x0a, 0x00,
+ 0x0c, 0x00,
+ 0x17, 0x14,
+ 0x19, 0x05,
+ 0x1b, 0x24,
+ 0x1c, 0x04,
+ 0x1e, 0x00,
+ 0x1f, 0xc8,
+ 0x20, 0x00,
+ 0x21, 0x48,
+ 0x23, 0x22,
+ 0x24, 0x16,
+
+
+ 0x26, 0xf7,
+ 0x28, 0x7f,
+ 0x29, 0x00,
+ 0x32, 0x00,
+ 0x33, 0x20,
+ 0x34, 0x20,
+ 0x35, 0x20,
+ 0x36, 0x20,
+ 0x3b, 0x04,
+ 0x3c, 0x04,
+ 0x3d, 0x04,
+ 0x3e, 0x04,
+
+ 0x40, 0xff,
+ 0x41, 0x00,
+ 0x42, 0xfe,
+ 0x46, 0x02,
+ 0x4b, 0xcb,
+ 0x4d, 0x01,
+ 0x4f, 0x01,
+ 0x70, 0x48,
+ 0x80, 0xe7,
+ 0x82, 0x55,
+ 0x87, 0x4a,
+ 0xfe, 0x01,
+ 0x18, 0x22,
+ 0xfe, 0x00,
+ 0x9c, 0x0a,
+ 0xa4, 0x50,
+ 0xa5, 0x21,
+ 0xa7, 0x35,
+ 0xdd, 0x54,
+ 0x95, 0x35,
+ 0xfe, 0x00,
+ ///gamma
+ /*
+ 0xbf, 0x06,
+ 0xc0, 0x14,
+ 0xc1, 0x27,
+ 0xc2, 0x3b,
+ 0xc3, 0x4f,
+ 0xc4, 0x62,
+ 0xc5, 0x72,
+ 0xc6, 0x8d,
+ 0xc7, 0xa4,
+ 0xc8, 0xb8,
+ 0xc9, 0xc9,
+ 0xca, 0xd6,
+ 0xcb, 0xe0,
+ 0xcc, 0xe8,
+ 0xcd, 0xf4,
+ 0xce, 0xfc,
+ 0xcf, 0xff,
+ */
+
+ 0xBF, 0x10,
+ 0xc0, 0x20,
+ 0xc1, 0x38,
+ 0xc2, 0x4E,
+ 0xc3, 0x63,
+ 0xc4, 0x76,
+ 0xc5, 0x87,
+ 0xc6, 0xA2,
+ 0xc7, 0xB8,
+ 0xc8, 0xCA,
+ 0xc9, 0xD8,
+ 0xcA, 0xE3,
+ 0xcB, 0xEB,
+ 0xcC, 0xF0,
+ 0xcD, 0xF8,
+ 0xcE, 0xFD,
+ 0xcF, 0xFF,
+
+
+ 0xfe, 0x00,
+ 0xb3, 0x44,
+ 0xb4, 0xfd,
+ 0xb5, 0x02,
+ 0xb6, 0xfa,
+ 0xb7, 0x48,
+ 0xb8, 0xf0,
+ 0x50, 0x01,
+ 0x19, 0x05,
+ 0x20, 0x01,
+ 0x22, 0xba,
+ 0x21, 0x48,
+ 0xfe, 0x00,
+ 0xd1, 0x34,
+ 0xd2, 0x34,
+ 0xfe, 0x01,
+ 0x10, 0x40,
+ 0x12, 0x07,
+ 0x13, 0x64,
+ 0x17, 0x88,
+ 0x21, 0xb0,
+ 0x22, 0x58,
+ 0x3c, 0x85,
+ 0x3d, 0x50,
+ 0x3e, 0x48,
+ 0xfe, 0x01,
+ 0x06, 0x08,
+ 0x07, 0x06,
+ 0x08, 0xa6,
+ 0x09, 0xee,
+ 0x6a, 0x40,
+ 0x6b, 0x39,
+ 0xd0, 0x00,
+ 0xd2, 0x2c,
+ 0xd3, 0x80,
+ 0x9c, 0x00,
+ 0x9d, 0x10,
+ 0x9e, 0xc0,
+ 0x9f, 0x40,
+ 0xfe, 0x01,
+ 0xa0, 0x00,
+ 0xa1, 0x3c,
+ 0xa2, 0x50,
+ 0xa3, 0x00,
+ 0xa8, 0x0f,
+ 0xa9, 0x08,
+ 0xaa, 0x00,
+ 0xab, 0x04,
+ 0xac, 0x00,
+ 0xad, 0x07,
+ 0xae, 0x0e,
+ 0xaf, 0x00,
+ 0xb0, 0x00,
+ 0xb1, 0x09,
+ 0xb2, 0x00,
+ 0xb3, 0x00,
+ 0xb4, 0x31,
+ 0xb5, 0x19,
+ 0xb6, 0x24,
+ 0xba, 0x3a,
+ 0xbb, 0x24,
+ 0xbc, 0x2a,
+ 0xc0, 0x17,
+ 0xc1, 0x13,
+ 0xc2, 0x17,
+ 0xc6, 0x21,
+ 0xc7, 0x1c,
+ 0xc8, 0x1c,
+ 0xb7, 0x00,
+ 0xb8, 0x00,
+ 0xb9, 0x00,
+ 0xbd, 0x00,
+ 0xbe, 0x00,
+ 0xbf, 0x00,
+ 0xc3, 0x00,
+ 0xc4, 0x00,
+ 0xc5, 0x00,
+ 0xc9, 0x00,
+ 0xca, 0x00,
+ 0xcb, 0x00,
+ 0xa4, 0x00,
+ 0xa5, 0x00,
+ 0xa6, 0x00,
+ 0xa7, 0x00,
+ 0xfe, 0x00,
+
+ 0xfe,0x01,
+ 0x18,0x22,
+ 0x21,0xc0,
+ 0x06,0x12,
+ 0x08,0x9c,
+ 0x51,0x28,
+ 0x52,0x10,
+ 0x53,0x20,
+ 0x54,0x40,
+ 0x55,0x16,
+ 0x56,0x30,
+ 0x58,0x60,
+ 0x59,0x08,
+ 0x5c,0x35,
+ 0x5d,0x72,
+ 0x67,0x80,
+ 0x68,0x60,
+ 0x69,0x90,
+ 0x6c,0x30,
+ 0x6d,0x60,
+ 0x70,0x10,
+ 0xfe,0x00,
+ 0x9c,0x0a,
+ 0xa0,0xaf,
+ 0xa2,0xff,
+ 0xa4,0x60,
+ 0xa5,0x31,
+ 0xa7,0x35,
+ 0x42,0xfe,
+ 0xd1,0x34,
+ 0xd2,0x34,
+ 0xd3,0x3c,
+ 0xd5,0x08,
+ 0xfe,0x00,
+
+ 0xa0, 0xaf,
+ 0xa2, 0xff,
+ 0x44, 0xa2,
+};
+
+uint8_t gc0329_default_regs_exit[] = {
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0329/gc0329.c b/drivers/media/video/wmt_v4l2/sensors/gc0329/gc0329.c
new file mode 100755
index 00000000..f0c0ff58
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0329/gc0329.c
@@ -0,0 +1,345 @@
+
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "gc0329.h"
+
+#define sensor_write_array(sd, arr, sz) \
+ cmos_init_8bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write(sd->i2c_addr, reg, val)
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint8_t *regs;
+ size_t size;
+};
+
+struct sensor_flip{
+ int h_flip;
+ int v_flip;
+}gc0329_flip;
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+ //CMOS_WIN_SIZE("QVGA", 320, 240, gc0329_320x240),
+ CMOS_WIN_SIZE("VGA", 640, 480, gc0329_640x480),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+
+ return NULL;
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = gc0329_wb_auto;
+ size = ARRAY_SIZE(gc0329_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = gc0329_wb_incandescent;
+ size = ARRAY_SIZE(gc0329_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = gc0329_wb_daylight;
+ size = ARRAY_SIZE(gc0329_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = gc0329_wb_cloudy;
+ size = ARRAY_SIZE(gc0329_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = gc0329_wb_fluorescent;
+ size = ARRAY_SIZE(gc0329_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_scenemode(struct cmos_subdev *sd, enum v4l2_scene_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case SCENE_MODE_AUTO:
+ regs = gc0329_scene_mode_auto;
+ size = ARRAY_SIZE(gc0329_scene_mode_auto);
+ break;
+ case SCENE_MODE_NIGHTSHOT:
+ regs = gc0329_scene_mode_night;
+ size = ARRAY_SIZE(gc0329_scene_mode_night);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_exposure(struct cmos_subdev *sd, int value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case -2:
+ regs = gc0329_exposure_neg6;
+ size = ARRAY_SIZE(gc0329_exposure_neg6);
+ break;
+ case -1:
+ regs = gc0329_exposure_neg3;
+ size = ARRAY_SIZE(gc0329_exposure_neg3);
+ break;
+ case 0:
+ regs = gc0329_exposure_zero;
+ size = ARRAY_SIZE(gc0329_exposure_zero);
+ break;
+ case 1:
+ regs = gc0329_exposure_pos3;
+ size = ARRAY_SIZE(gc0329_exposure_pos3);
+ break;
+ case 2:
+ regs = gc0329_exposure_pos6;
+ size = ARRAY_SIZE(gc0329_exposure_pos6);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data;
+
+ sensor_write(sd, 0xfe, 0x00); // set page0
+
+ //data = sensor_read(sd, 0x17);
+
+ switch (value) {
+ case 0:
+ gc0329_flip.h_flip = 0x00;
+ data= (0x14&~0x01);
+ break;
+ case 1:
+ gc0329_flip.h_flip =0x01;
+ data = (0x14|0x01);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ data|=gc0329_flip.v_flip;
+ sensor_write(sd, 0x17, data);
+ msleep(150);
+
+ return 0;
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data;
+
+ sensor_write(sd, 0xfe, 0x00); // set page0
+
+ //data = sensor_read(sd, 0x17);
+
+ switch (value) {
+ case 0:
+ gc0329_flip.v_flip = 0x00;
+ data = (0x14&~0x02);
+ break;
+ case 1:
+ gc0329_flip.v_flip = 0x02;
+ data = (0x14|0x02);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ data|=gc0329_flip.h_flip;
+ sensor_write(sd, 0x17, data);
+ msleep(150);
+
+ return 0;
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ case V4L2_CID_EXPOSURE:
+ return v4l2_ctrl_query_fill(qc, -2, 2, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return sensor_s_scenemode(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return sensor_s_exposure(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ default:
+ case WMT_V4L2_CID_CAMERA_ANTIBANDING:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win) {
+ pr_err("%s, s_mbus_fmt failed, width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, win->regs, win->size);
+ msleep(300);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return 0;
+}
+
+static int sensor_enum_framesizes(struct cmos_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ const struct cmos_win_size *win = &cmos_supported_win_sizes[index];
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ sensor_write(sd, 0xfc, 0x16);
+ return (sensor_read(sd, 0) == sd->id) ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ if (sensor_identify(sd)) {
+ return -1;
+ }
+
+ gc0329_flip.h_flip=0;
+ gc0329_flip.v_flip=0;
+ sensor_write_array(sd, gc0329_default_regs_init,
+ ARRAY_SIZE(gc0329_default_regs_init));
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ sensor_write_array(sd, gc0329_default_regs_exit,
+ ARRAY_SIZE(gc0329_default_regs_exit));
+ return 0;
+}
+
+static struct cmos_subdev_ops gc0329_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+ .enum_framesizes = sensor_enum_framesizes,
+};
+
+struct cmos_subdev gc0329 = {
+ .name = "gc0329",
+ .i2c_addr = 0x31,
+ .id = 0xc0,
+ .max_width = 640,
+ .max_height = 480,
+ .ops = &gc0329_ops,
+};
+
+#if 0
+static int __init gc0329_init(void)
+{
+ return cmos_register_sudbdev(&gc0329);
+}
+
+static void __exit gc0329_exit(void)
+{
+ return cmos_unregister_subdev(&gc0329);
+}
+
+module_init(gc0329_init);
+module_exit(gc0329_exit);
+
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0329/gc0329.h b/drivers/media/video/wmt_v4l2/sensors/gc0329/gc0329.h
new file mode 100755
index 00000000..9cd67ac5
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0329/gc0329.h
@@ -0,0 +1,530 @@
+#ifndef GC0329_H
+#define GC0329_H
+
+
+// Scene Mode
+uint8_t gc0329_scene_mode_auto[] = {
+};
+
+uint8_t gc0329_scene_mode_night[] = {
+};
+
+
+// White Balance
+uint8_t gc0329_wb_auto [] = {
+ 0xfe, 0x00,
+ 0x77, 0x57,
+ 0x78, 0x4d,
+ 0x79, 0x45,
+ 0x42, 0xfe, //AWB on
+};
+
+uint8_t gc0329_wb_incandescent [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x48,
+ 0x78, 0x40,
+ 0x79, 0x5c,
+
+};
+
+uint8_t gc0329_wb_fluorescent [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x40,
+ 0x78, 0x42,
+ 0x79, 0x50,
+
+};
+
+uint8_t gc0329_wb_daylight [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x74,
+ 0x78, 0x52,
+ 0x79, 0x40,
+
+};
+
+uint8_t gc0329_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x8c,
+ 0x78, 0x50,
+ 0x79, 0x40,
+
+};
+
+uint8_t gc0329_wb_tungsten [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x40,
+ 0x78, 0x54,
+ 0x79, 0x70,
+
+};
+
+
+// Exposure
+uint8_t gc0329_exposure_neg6[] = {
+ 0xfe, 0x00,
+ 0xd5, 0xe0,
+ 0xfe, 0x01,
+ 0x13, 0x40,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_neg3[] = {
+ 0xfe, 0x00,
+ 0xd5, 0xf0,
+ 0xfe, 0x01,
+ 0x13, 0x48,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_zero[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x04, /// 00
+ 0xfe, 0x01,
+ 0x13, 0x6a,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_pos3[] = {
+
+ 0xfe, 0x00,
+ 0xd5, 0x10,
+ 0xfe, 0x01,
+ 0x13, 0x70,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_pos6[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x20,
+ 0xfe, 0x01,
+ 0x13, 0x80,
+ 0xfe, 0x00,
+
+};
+
+
+// Color Effect
+uint8_t gc0329_colorfx_none[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x00,
+};
+
+uint8_t gc0329_colorfx_bw[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0x00,
+ 0xdb , 0x00,
+
+};
+
+uint8_t gc0329_colorfx_sepia[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0xd0,
+ 0xdb , 0x28,
+
+};
+
+uint8_t gc0329_colorfx_negative[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x01,
+};
+
+uint8_t gc0329_colorfx_emboss[] = {
+};
+
+uint8_t gc0329_colorfx_sketch[] = {
+
+};
+
+uint8_t gc0329_colorfx_sky_blue[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0x50,
+ 0xdb , 0xe0,
+
+};
+
+uint8_t gc0329_colorfx_grass_green[] = {
+
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0xc0,
+ 0xdb , 0xc0,
+
+};
+
+uint8_t gc0329_colorfx_skin_whiten[] = {
+};
+
+uint8_t gc0329_colorfx_vivid[] = {
+};
+
+// Brightness
+uint8_t gc0329_brightness_neg4[] = {
+};
+
+uint8_t gc0329_brightness_neg3[] = {
+};
+
+uint8_t gc0329_brightness_neg2[] = {
+};
+
+uint8_t gc0329_brightness_neg1[] = {
+};
+
+uint8_t gc0329_brightness_zero[] = {
+};
+
+uint8_t gc0329_brightness_pos1[] = {
+};
+
+uint8_t gc0329_brightness_pos2[] = {
+};
+
+uint8_t gc0329_brightness_pos3[] = {
+};
+
+uint8_t gc0329_brightness_pos4[] = {
+};
+
+// Contrast
+uint8_t gc0329_contrast_neg4[] = {
+};
+
+uint8_t gc0329_contrast_neg3[] = {
+};
+
+uint8_t gc0329_contrast_neg2[] = {
+};
+
+uint8_t gc0329_contrast_neg1[] = {
+};
+
+uint8_t gc0329_contrast_zero[] = {
+};
+
+uint8_t gc0329_contrast_pos1[] = {
+};
+
+uint8_t gc0329_contrast_pos2[] = {
+};
+
+uint8_t gc0329_contrast_pos3[] = {
+};
+
+uint8_t gc0329_contrast_pos4[] = {
+};
+
+// Saturation
+uint8_t gc0329_saturation_neg4[] = {
+};
+
+uint8_t gc0329_saturation_neg3[] = {
+};
+
+uint8_t gc0329_saturation_neg2[] = {
+};
+
+uint8_t gc0329_saturation_neg1[] = {
+};
+
+uint8_t gc0329_saturation_zero[] = {
+};
+
+uint8_t gc0329_saturation_pos1[] = {
+};
+
+uint8_t gc0329_saturation_pos2[] = {
+};
+
+uint8_t gc0329_saturation_pos3[] = {
+};
+
+uint8_t gc0329_saturation_pos4[] = {
+};
+
+
+// Resolution
+
+// QCIF
+uint8_t gc0329_176x144[]={
+};
+
+uint8_t gc0329_320x240[]={
+ 0x59, 0x22,
+};
+
+uint8_t gc0329_640x480[]={
+ 0x59, 0x11,
+};
+
+
+// Initiliztion
+uint8_t gc0329_default_regs_init[] = {
+ 0xfe, 0x80,
+ 0xfc, 0x12,
+ 0xfc, 0x12,
+ 0xfe, 0x00,
+
+ 0xfa, 0x00,
+
+ 0xf0, 0x07,
+ 0xf1, 0x01,
+ 0x73, 0x90,
+ 0x74, 0x80,
+ 0x75, 0x80,
+ 0x76, 0x94,
+ 0xfc, 0x16,
+
+ 0x05, 0x01,
+ 0x06, 0xfa,
+ 0x07, 0x00,
+ 0x08, 0x70,
+ 0xfe, 0x01,
+ 0x29, 0x00,
+ 0x2a, 0x64,
+ 0x2b, 0x02,
+ 0x2c, 0x58,
+ 0x2d, 0x04,
+
+ 0x2e, 0xb0,
+ 0x2f, 0x06,
+ 0x30, 0x40,
+ 0x31, 0x09,
+ 0x32, 0xc4,
+ 0x33, 0x20,
+ 0xfe, 0x00,
+
+
+
+
+
+ 0x0a, 0x00,
+ 0x0c, 0x00,
+ 0x17, 0x14,
+ 0x19, 0x05,
+ 0x1b, 0x24,
+ 0x1c, 0x04,
+ 0x1e, 0x00,
+ 0x1f, 0xc8,
+ 0x20, 0x00,
+ 0x21, 0x48,
+ 0x23, 0x22,
+ 0x24, 0x16,
+
+
+ 0x26, 0xf7,
+ 0x28, 0x7f,
+ 0x29, 0x00,
+ 0x32, 0x00,
+ 0x33, 0x20,
+ 0x34, 0x20,
+ 0x35, 0x20,
+ 0x36, 0x20,
+ 0x3b, 0x04,
+ 0x3c, 0x04,
+ 0x3d, 0x04,
+ 0x3e, 0x04,
+
+ 0x40, 0xff,
+ 0x41, 0x00,
+ 0x42, 0xfe,
+ 0x46, 0x02,
+ 0x4b, 0xcb,
+ 0x4d, 0x01,
+ 0x4f, 0x01,
+ 0x70, 0x48,
+ 0x80, 0xe7,
+ 0x82, 0x55,
+ 0x87, 0x4a,
+ 0xfe, 0x01,
+ 0x18, 0x22,
+ 0xfe, 0x00,
+ 0x9c, 0x0a,
+ 0xa4, 0x50,
+ 0xa5, 0x21,
+ 0xa7, 0x35,
+ 0xdd, 0x54,
+ 0x95, 0x35,
+ 0xfe, 0x00,
+ ///gamma
+ /*
+ 0xbf, 0x06,
+ 0xc0, 0x14,
+ 0xc1, 0x27,
+ 0xc2, 0x3b,
+ 0xc3, 0x4f,
+ 0xc4, 0x62,
+ 0xc5, 0x72,
+ 0xc6, 0x8d,
+ 0xc7, 0xa4,
+ 0xc8, 0xb8,
+ 0xc9, 0xc9,
+ 0xca, 0xd6,
+ 0xcb, 0xe0,
+ 0xcc, 0xe8,
+ 0xcd, 0xf4,
+ 0xce, 0xfc,
+ 0xcf, 0xff,
+ */
+
+ 0xBF, 0x14,
+ 0xc0, 0x28,
+ 0xc1, 0x44,
+ 0xc2, 0x5D,
+ 0xc3, 0x72,
+ 0xc4, 0x86,
+ 0xc5, 0x95,
+ 0xc6, 0xB1,
+ 0xc7, 0xC6,
+ 0xc8, 0xD5,
+ 0xc9, 0xE1,
+ 0xcA, 0xEA,
+ 0xcB, 0xF1,
+ 0xcC, 0xF5,
+ 0xcD, 0xFB,
+ 0xcE, 0xFE,
+ 0xcF, 0xFF,
+
+
+ 0xfe, 0x00,
+ 0xb3, 0x44,
+ 0xb4, 0xfd,
+ 0xb5, 0x02,
+ 0xb6, 0xfa,
+ 0xb7, 0x48,
+ 0xb8, 0xf0,
+ 0x50, 0x01,
+ 0x19, 0x05,
+ 0x20, 0x01,
+ 0x22, 0xba,
+ 0x21, 0x48,
+ 0xfe, 0x00,
+ 0xd1, 0x34,
+ 0xd2, 0x34,
+ 0xfe, 0x01,
+ 0x10, 0x40,
+ 0x12, 0x17,
+ 0x13, 0x60,
+ 0x17, 0x88,
+ 0x21, 0xb0,
+ 0x22, 0x60,
+ 0x3c, 0x82,
+ 0x3d, 0x55,
+ 0x3e, 0x48,
+ 0xfe, 0x01,
+ 0x06, 0x08,
+ 0x07, 0x06,
+ 0x08, 0xa6,
+ 0x09, 0xee,
+ 0x6a, 0x40,
+ 0x6b, 0x39,
+ 0xd0, 0x00,
+ 0xd2, 0x2c,
+ 0xd3, 0x80,
+ 0x9c, 0x00,
+ 0x9d, 0x10,
+ 0x9e, 0xc0,
+ 0x9f, 0x40,
+ 0xfe, 0x01,
+ 0xa0, 0x00,
+ 0xa1, 0x3c,
+ 0xa2, 0x50,
+ 0xa3, 0x00,
+ 0xa8, 0x0f,
+ 0xa9, 0x08,
+ 0xaa, 0x00,
+ 0xab, 0x04,
+ 0xac, 0x00,
+ 0xad, 0x07,
+ 0xae, 0x0e,
+ 0xaf, 0x00,
+ 0xb0, 0x00,
+ 0xb1, 0x09,
+ 0xb2, 0x00,
+ 0xb3, 0x00,
+ 0xb4, 0x31,
+ 0xb5, 0x19,
+ 0xb6, 0x24,
+ 0xba, 0x3a,
+ 0xbb, 0x24,
+ 0xbc, 0x2a,
+ 0xc0, 0x17,
+ 0xc1, 0x13,
+ 0xc2, 0x17,
+ 0xc6, 0x21,
+ 0xc7, 0x1c,
+ 0xc8, 0x1c,
+ 0xb7, 0x00,
+ 0xb8, 0x00,
+ 0xb9, 0x00,
+ 0xbd, 0x00,
+ 0xbe, 0x00,
+ 0xbf, 0x00,
+ 0xc3, 0x00,
+ 0xc4, 0x00,
+ 0xc5, 0x00,
+ 0xc9, 0x00,
+ 0xca, 0x00,
+ 0xcb, 0x00,
+ 0xa4, 0x00,
+ 0xa5, 0x00,
+ 0xa6, 0x00,
+ 0xa7, 0x00,
+ 0xfe, 0x00,
+
+ 0xfe,0x01,
+ 0x18,0x22,
+ 0x21,0xc0,
+ 0x06,0x12,
+ 0x08,0x9c,
+ 0x51,0x28,
+ 0x52,0x10,
+ 0x53,0x20,
+ 0x54,0x40,
+ 0x55,0x16,
+ 0x56,0x30,
+ 0x58,0x60,
+ 0x59,0x08,
+ 0x5c,0x35,
+ 0x5d,0x72,
+ 0x67,0x80,
+ 0x68,0x60,
+ 0x69,0x90,
+ 0x6c,0x30,
+ 0x6d,0x60,
+ 0x70,0x10,
+ 0xfe,0x00,
+ 0x9c,0x0a,
+ 0xa0,0xaf,
+ 0xa2,0xff,
+ 0xa4,0x60,
+ 0xa5,0x31,
+ 0xa7,0x35,
+ 0x42,0xfe,
+ 0xd1,0x34,
+ 0xd2,0x34,
+ 0xd3,0x3b,
+ 0xd5,0x0c,
+ 0xfe,0x00,
+
+ 0xa0, 0xaf,
+ 0xa2, 0xff,
+ 0x44, 0xa2,
+};
+
+uint8_t gc0329_default_regs_exit[] = {
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0329/gc0329_shuttle.h b/drivers/media/video/wmt_v4l2/sensors/gc0329/gc0329_shuttle.h
new file mode 100644
index 00000000..6d847ff1
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0329/gc0329_shuttle.h
@@ -0,0 +1,560 @@
+#ifndef GC0329_H
+#define GC0329_H
+
+
+// Scene Mode
+uint8_t gc0329_scene_mode_auto[] = {
+};
+
+uint8_t gc0329_scene_mode_night[] = {
+};
+
+
+// White Balance
+uint8_t gc0329_wb_auto [] = {
+ 0xfe, 0x00,
+ 0x77, 0x57,
+ 0x78, 0x4d,
+ 0x79, 0x45,
+ 0x42, 0xfe, //AWB on
+};
+
+uint8_t gc0329_wb_incandescent [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x48,
+ 0x78, 0x40,
+ 0x79, 0x5c,
+
+};
+
+uint8_t gc0329_wb_fluorescent [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x40,
+ 0x78, 0x42,
+ 0x79, 0x50,
+
+};
+
+uint8_t gc0329_wb_daylight [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x74,
+ 0x78, 0x52,
+ 0x79, 0x40,
+
+};
+
+uint8_t gc0329_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x8c,
+ 0x78, 0x50,
+ 0x79, 0x40,
+
+};
+
+uint8_t gc0329_wb_tungsten [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x40,
+ 0x78, 0x54,
+ 0x79, 0x70,
+
+};
+
+
+// Exposure
+uint8_t gc0329_exposure_neg6[] = {
+ 0xfe, 0x00,
+ 0xd5, 0xe0,
+ 0xfe, 0x01,
+ 0x13, 0x40,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_neg3[] = {
+ 0xfe, 0x00,
+ 0xd5, 0xf0,
+ 0xfe, 0x01,
+ 0x13, 0x48,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_zero[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x00, /// 00
+ 0xfe, 0x01,
+ 0x13, 0x56,//6a cyrille
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_pos3[] = {
+
+ 0xfe, 0x00,
+ 0xd5, 0x10,
+ 0xfe, 0x01,
+ 0x13, 0x70,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_pos6[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x20,
+ 0xfe, 0x01,
+ 0x13, 0x80,
+ 0xfe, 0x00,
+
+};
+
+
+// Color Effect
+uint8_t gc0329_colorfx_none[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x00,
+};
+
+uint8_t gc0329_colorfx_bw[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0x00,
+ 0xdb , 0x00,
+
+};
+
+uint8_t gc0329_colorfx_sepia[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0xd0,
+ 0xdb , 0x28,
+
+};
+
+uint8_t gc0329_colorfx_negative[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x01,
+};
+
+uint8_t gc0329_colorfx_emboss[] = {
+};
+
+uint8_t gc0329_colorfx_sketch[] = {
+
+};
+
+uint8_t gc0329_colorfx_sky_blue[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0x50,
+ 0xdb , 0xe0,
+
+};
+
+uint8_t gc0329_colorfx_grass_green[] = {
+
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0xc0,
+ 0xdb , 0xc0,
+
+};
+
+uint8_t gc0329_colorfx_skin_whiten[] = {
+};
+
+uint8_t gc0329_colorfx_vivid[] = {
+};
+
+// Brightness
+uint8_t gc0329_brightness_neg4[] = {
+};
+
+uint8_t gc0329_brightness_neg3[] = {
+};
+
+uint8_t gc0329_brightness_neg2[] = {
+};
+
+uint8_t gc0329_brightness_neg1[] = {
+};
+
+uint8_t gc0329_brightness_zero[] = {
+};
+
+uint8_t gc0329_brightness_pos1[] = {
+};
+
+uint8_t gc0329_brightness_pos2[] = {
+};
+
+uint8_t gc0329_brightness_pos3[] = {
+};
+
+uint8_t gc0329_brightness_pos4[] = {
+};
+
+// Contrast
+uint8_t gc0329_contrast_neg4[] = {
+};
+
+uint8_t gc0329_contrast_neg3[] = {
+};
+
+uint8_t gc0329_contrast_neg2[] = {
+};
+
+uint8_t gc0329_contrast_neg1[] = {
+};
+
+uint8_t gc0329_contrast_zero[] = {
+};
+
+uint8_t gc0329_contrast_pos1[] = {
+};
+
+uint8_t gc0329_contrast_pos2[] = {
+};
+
+uint8_t gc0329_contrast_pos3[] = {
+};
+
+uint8_t gc0329_contrast_pos4[] = {
+};
+
+// Saturation
+uint8_t gc0329_saturation_neg4[] = {
+};
+
+uint8_t gc0329_saturation_neg3[] = {
+};
+
+uint8_t gc0329_saturation_neg2[] = {
+};
+
+uint8_t gc0329_saturation_neg1[] = {
+};
+
+uint8_t gc0329_saturation_zero[] = {
+};
+
+uint8_t gc0329_saturation_pos1[] = {
+};
+
+uint8_t gc0329_saturation_pos2[] = {
+};
+
+uint8_t gc0329_saturation_pos3[] = {
+};
+
+uint8_t gc0329_saturation_pos4[] = {
+};
+
+
+// Resolution
+
+// QCIF
+uint8_t gc0329_176x144[]={
+};
+
+uint8_t gc0329_320x240[]={
+ 0x59, 0x22,
+};
+
+uint8_t gc0329_640x480[]={
+ 0x59, 0x11,
+};
+
+
+// Initiliztion
+uint8_t gc0329_default_regs_init[] = {
+ 0xfe, 0x80,
+ 0xfc, 0x12,
+ 0xfc, 0x12,
+ 0xfe, 0x00,
+
+ 0xfa, 0x11,
+
+ 0xf0, 0x07,
+ 0xf1, 0x01,
+ 0x73, 0x90,
+ 0x74, 0x80,
+ 0x75, 0x80,
+ 0x76, 0x94,
+ 0xfc, 0x16,
+
+ 0x05, 0x01,
+ 0x06, 0x32,
+ 0x07, 0x00,
+ 0x08, 0x70,
+ 0xfe, 0x01,
+ 0x29, 0x00,
+ 0x2a, 0x3c,
+ /*
+ 0x2b, 0x02,
+ 0x2c, 0x58,
+ 0x2d, 0x02,
+
+ 0x2e, 0xd0,
+ 0x2f, 0x03,
+ 0x30, 0x48,
+ 0x31, 0x04,
+ 0x32, 0x38,*/
+ 0x2b, 0x02,
+ 0x2c, 0xd0,
+ 0x2d, 0x03,
+
+ 0x2e, 0x48,
+ 0x2f, 0x03,
+ 0x30, 0x84,
+ 0x31, 0x04,
+ 0x32, 0x38,
+ 0x33, 0x20,
+ 0xfe, 0x00,
+
+
+
+
+
+ 0x0a, 0x00,
+ 0x0c, 0x00,
+ 0x17, 0x14, //14
+ 0x19, 0x05,
+ 0x1b, 0x24,
+ 0x1c, 0x04,
+ 0x1e, 0x00,
+ 0x1f, 0xc8,
+ 0x20, 0x00,
+ 0x21, 0x48,
+ 0x23, 0x22,
+ 0x24, 0x13,//0x16
+
+
+ 0x26, 0xf7,
+ 0x28, 0x7f,
+ 0x29, 0x00,
+ 0x32, 0x00,
+ 0x33, 0x20,
+ 0x34, 0x20,
+ 0x35, 0x20,
+ 0x36, 0x20,
+ 0x3b, 0x04,
+ 0x3c, 0x04,
+ 0x3d, 0x04,
+ 0x3e, 0x04,
+
+ 0x40, 0xff,
+ 0x41, 0x00,
+ 0x42, 0xfe,
+ 0x46, 0x02,
+ 0x4b, 0xcb,
+ 0x4d, 0x01,
+ 0x4f, 0x01,
+ 0x70, 0x48,
+ 0x80, 0xe7,
+ 0x82, 0x60,//0x55
+ 0x87, 0x4a,
+ 0xfe, 0x01,
+ 0x18, 0x22,
+ 0xfe, 0x00,
+ 0x9c, 0x0a,
+ 0xa4, 0x50,
+ 0xa5, 0x21,
+ 0xa7, 0x35,
+ 0xdd, 0x54,
+ 0x95, 0x45,//0x35
+ 0xfe, 0x00,
+ ///gamma
+ /*
+ 0xbf, 0x06,
+ 0xc0, 0x14,
+ 0xc1, 0x27,
+ 0xc2, 0x3b,
+ 0xc3, 0x4f,
+ 0xc4, 0x62,
+ 0xc5, 0x72,
+ 0xc6, 0x8d,
+ 0xc7, 0xa4,
+ 0xc8, 0xb8,
+ 0xc9, 0xc9,
+ 0xca, 0xd6,
+ 0xcb, 0xe0,
+ 0xcc, 0xe8,
+ 0xcd, 0xf4,
+ 0xce, 0xfc,
+ 0xcf, 0xff,
+ */
+
+ 0xBF, 0x0B,
+ 0xc0, 0x16,
+ 0xc1, 0x29,
+ 0xc2, 0x3C,
+ 0xc3, 0x4F,
+ 0xc4, 0x5F,
+ 0xc5, 0x6F,
+ 0xc6, 0x8A,
+ 0xc7, 0x9F,
+ 0xc8, 0xB4,
+ 0xc9, 0xC6,
+ 0xcA, 0xD3,
+ 0xcB, 0xDD,
+ 0xcC, 0xE5,
+ 0xcD, 0xF1,
+ 0xcE, 0xFA,
+ 0xcF, 0xFF,
+ /*0xBF, 0x14,
+ 0xc0, 0x28,
+ 0xc1, 0x44,
+ 0xc2, 0x5D,
+ 0xc3, 0x72,
+ 0xc4, 0x86,
+ 0xc5, 0x95,
+ 0xc6, 0xB1,
+ 0xc7, 0xC6,
+ 0xc8, 0xD5,
+ 0xc9, 0xE1,
+ 0xcA, 0xEA,
+ 0xcB, 0xF1,
+ 0xcC, 0xF5,
+ 0xcD, 0xFB,
+ 0xcE, 0xFE,
+ 0xcF, 0xFF,*/
+
+
+ 0xfe, 0x00,
+ 0xb3, 0x38,//44 cyrille 40
+ 0xb4, 0xfd,
+ 0xb5, 0x02,
+ 0xb6, 0xfa,
+ 0xb7, 0x48,
+ 0xb8, 0xf0,
+ 0x50, 0x01,
+ 0x19, 0x05,
+ 0x20, 0x01,
+ 0x22, 0xba,
+ 0x21, 0x48,
+ 0xfe, 0x00,
+ 0xd1, 0x28,// 34 cyrille
+ 0xd2, 0x26,//34 cyrille
+ 0x7a, 0x70,// cyrille 7d
+ 0x7b, 0x84,// cyrille
+ 0x7c, 0x80,// cyrille
+ 0xfe, 0x01,
+ 0x10, 0x40,
+ 0x12, 0x27,//17 cyrille
+ 0x13, 0x56,//60 cyrille
+ 0x17, 0x88,
+ 0x21, 0x80,//b0 cyrille
+ 0x22, 0x40,//60 cyrille
+ 0x3c, 0x82,
+ 0x3d, 0x55,
+ 0x3e, 0x48,
+ 0xfe, 0x01,
+ 0x06, 0x08,
+ 0x07, 0x06,
+ 0x08, 0xa6,
+ 0x09, 0xee,
+ 0x6a, 0x40,
+ 0x6b, 0x39,
+ 0xd0, 0x00,
+ 0xd2, 0x2c,
+ 0xd3, 0x80,
+ 0x9c, 0x00,
+ 0x9d, 0x10,
+ 0x9e, 0xc0,
+ 0x9f, 0x40,
+ 0xfe, 0x01,
+ 0xa0, 0x00,
+ 0xa1, 0x3c,
+ 0xa2, 0x50,
+ 0xa3, 0x00,
+ 0xa8, 0x0f,
+ 0xa9, 0x08,
+ 0xaa, 0x00,
+ 0xab, 0x04,
+ 0xac, 0x00,
+ 0xad, 0x07,
+ 0xae, 0x0e,
+ 0xaf, 0x00,
+ 0xb0, 0x00,
+ 0xb1, 0x09,
+ 0xb2, 0x00,
+ 0xb3, 0x00,
+ 0xb4, 0x31,
+ 0xb5, 0x19,
+ 0xb6, 0x24,
+ 0xba, 0x3a,
+ 0xbb, 0x24,
+ 0xbc, 0x2a,
+ 0xc0, 0x17,
+ 0xc1, 0x13,
+ 0xc2, 0x17,
+ 0xc6, 0x21,
+ 0xc7, 0x1c,
+ 0xc8, 0x1c,
+ 0xb7, 0x00,
+ 0xb8, 0x00,
+ 0xb9, 0x00,
+ 0xbd, 0x00,
+ 0xbe, 0x00,
+ 0xbf, 0x00,
+ 0xc3, 0x00,
+ 0xc4, 0x00,
+ 0xc5, 0x00,
+ 0xc9, 0x00,
+ 0xca, 0x00,
+ 0xcb, 0x00,
+ 0xa4, 0x00,
+ 0xa5, 0x00,
+ 0xa6, 0x00,
+ 0xa7, 0x00,
+ 0xfe, 0x00,
+
+ 0xfe,0x01,
+ 0x18,0x22,
+ 0x21,0x80,//c0 cyrille
+ 0x06,0x12,
+ 0x08,0x9c,
+ 0x51,0x28,
+ 0x52,0x10,
+ 0x53,0x20,
+ 0x54,0x40,
+ 0x55,0x16,
+ 0x56,0x30,
+ 0x58,0x60,
+ 0x59,0x08,
+ 0x5c,0x35,
+ 0x5d,0x72,
+ 0x67,0x80,
+ 0x68,0x60,
+ 0x69,0x90,
+ 0x6c,0x30,
+ 0x6d,0x60,
+ 0x70,0x10,
+ 0xfe,0x00,
+ 0x9c,0x0a,
+ 0xa0,0xaf,
+ 0xa2,0xff,
+ 0xa4,0x60,
+ 0xa5,0x31,
+ 0xa7,0x35,
+ 0x42,0xfe,
+ 0xd1,0x28, //34 cyrille
+ 0xd2,0x26, //34 cyrille
+ 0xd3,0x40,// 3b cyrille
+ 0xd5,0x00,//0c cyrille
+ 0xfe,0x00,
+
+ 0xa0, 0xaf,
+ 0xa2, 0xff,
+ 0x44, 0xa2,
+};
+
+uint8_t gc0329_default_regs_exit[] = {
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc0329/gc0329_style_s729.h b/drivers/media/video/wmt_v4l2/sensors/gc0329/gc0329_style_s729.h
new file mode 100755
index 00000000..06e42a1a
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc0329/gc0329_style_s729.h
@@ -0,0 +1,530 @@
+#ifndef GC0329_H
+#define GC0329_H
+
+
+// Scene Mode
+uint8_t gc0329_scene_mode_auto[] = {
+};
+
+uint8_t gc0329_scene_mode_night[] = {
+};
+
+
+// White Balance
+uint8_t gc0329_wb_auto [] = {
+ 0xfe, 0x00,
+ 0x77, 0x57,
+ 0x78, 0x4d,
+ 0x79, 0x45,
+ 0x42, 0xfe, //AWB on
+};
+
+uint8_t gc0329_wb_incandescent [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x48,
+ 0x78, 0x40,
+ 0x79, 0x5c,
+
+};
+
+uint8_t gc0329_wb_fluorescent [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x40,
+ 0x78, 0x42,
+ 0x79, 0x50,
+
+};
+
+uint8_t gc0329_wb_daylight [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x74,
+ 0x78, 0x52,
+ 0x79, 0x40,
+
+};
+
+uint8_t gc0329_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x8c,
+ 0x78, 0x50,
+ 0x79, 0x40,
+
+};
+
+uint8_t gc0329_wb_tungsten [] = {
+ 0xfe, 0x00,
+ 0x42, 0xfc,
+ 0x77, 0x40,
+ 0x78, 0x54,
+ 0x79, 0x70,
+
+};
+
+
+// Exposure
+uint8_t gc0329_exposure_neg6[] = {
+ 0xfe, 0x00,
+ 0xd5, 0xe0,
+ 0xfe, 0x01,
+ 0x13, 0x40,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_neg3[] = {
+ 0xfe, 0x00,
+ 0xd5, 0xf0,
+ 0xfe, 0x01,
+ 0x13, 0x48,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_zero[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x04, /// 00
+ 0xfe, 0x01,
+ 0x13, 0x6a,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_pos3[] = {
+
+ 0xfe, 0x00,
+ 0xd5, 0x10,
+ 0xfe, 0x01,
+ 0x13, 0x70,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc0329_exposure_pos6[] = {
+ 0xfe, 0x00,
+ 0xd5, 0x20,
+ 0xfe, 0x01,
+ 0x13, 0x80,
+ 0xfe, 0x00,
+
+};
+
+
+// Color Effect
+uint8_t gc0329_colorfx_none[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x00,
+};
+
+uint8_t gc0329_colorfx_bw[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0x00,
+ 0xdb , 0x00,
+
+};
+
+uint8_t gc0329_colorfx_sepia[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0xd0,
+ 0xdb , 0x28,
+
+};
+
+uint8_t gc0329_colorfx_negative[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x01,
+};
+
+uint8_t gc0329_colorfx_emboss[] = {
+};
+
+uint8_t gc0329_colorfx_sketch[] = {
+
+};
+
+uint8_t gc0329_colorfx_sky_blue[] = {
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0x50,
+ 0xdb , 0xe0,
+
+};
+
+uint8_t gc0329_colorfx_grass_green[] = {
+
+ 0xfe, 0x00,
+ 0x43 , 0x02,
+ 0xda , 0xc0,
+ 0xdb , 0xc0,
+
+};
+
+uint8_t gc0329_colorfx_skin_whiten[] = {
+};
+
+uint8_t gc0329_colorfx_vivid[] = {
+};
+
+// Brightness
+uint8_t gc0329_brightness_neg4[] = {
+};
+
+uint8_t gc0329_brightness_neg3[] = {
+};
+
+uint8_t gc0329_brightness_neg2[] = {
+};
+
+uint8_t gc0329_brightness_neg1[] = {
+};
+
+uint8_t gc0329_brightness_zero[] = {
+};
+
+uint8_t gc0329_brightness_pos1[] = {
+};
+
+uint8_t gc0329_brightness_pos2[] = {
+};
+
+uint8_t gc0329_brightness_pos3[] = {
+};
+
+uint8_t gc0329_brightness_pos4[] = {
+};
+
+// Contrast
+uint8_t gc0329_contrast_neg4[] = {
+};
+
+uint8_t gc0329_contrast_neg3[] = {
+};
+
+uint8_t gc0329_contrast_neg2[] = {
+};
+
+uint8_t gc0329_contrast_neg1[] = {
+};
+
+uint8_t gc0329_contrast_zero[] = {
+};
+
+uint8_t gc0329_contrast_pos1[] = {
+};
+
+uint8_t gc0329_contrast_pos2[] = {
+};
+
+uint8_t gc0329_contrast_pos3[] = {
+};
+
+uint8_t gc0329_contrast_pos4[] = {
+};
+
+// Saturation
+uint8_t gc0329_saturation_neg4[] = {
+};
+
+uint8_t gc0329_saturation_neg3[] = {
+};
+
+uint8_t gc0329_saturation_neg2[] = {
+};
+
+uint8_t gc0329_saturation_neg1[] = {
+};
+
+uint8_t gc0329_saturation_zero[] = {
+};
+
+uint8_t gc0329_saturation_pos1[] = {
+};
+
+uint8_t gc0329_saturation_pos2[] = {
+};
+
+uint8_t gc0329_saturation_pos3[] = {
+};
+
+uint8_t gc0329_saturation_pos4[] = {
+};
+
+
+// Resolution
+
+// QCIF
+uint8_t gc0329_176x144[]={
+};
+
+uint8_t gc0329_320x240[]={
+ 0x59, 0x22,
+};
+
+uint8_t gc0329_640x480[]={
+ 0x59, 0x11,
+};
+
+
+// Initiliztion
+uint8_t gc0329_default_regs_init[] = {
+ 0xfe, 0x80,
+ 0xfc, 0x12,
+ 0xfc, 0x12,
+ 0xfe, 0x00,
+
+ 0xfa, 0x00,
+
+ 0xf0, 0x07,
+ 0xf1, 0x01,
+ 0x73, 0x90,
+ 0x74, 0x80,
+ 0x75, 0x80,
+ 0x76, 0x94,
+ 0xfc, 0x16,
+
+ 0x05, 0x01,
+ 0x06, 0xfa,
+ 0x07, 0x00,
+ 0x08, 0x70,
+ 0xfe, 0x01,
+ 0x29, 0x00,
+ 0x2a, 0x64,
+ 0x2b, 0x02,
+ 0x2c, 0x58,
+ 0x2d, 0x04,
+
+ 0x2e, 0xb0,
+ 0x2f, 0x06,
+ 0x30, 0x40,
+ 0x31, 0x09,
+ 0x32, 0xc4,
+ 0x33, 0x20,
+ 0xfe, 0x00,
+
+
+
+
+
+ 0x0a, 0x02,
+ 0x0c, 0x02,
+ 0x17, 0x14,
+ 0x19, 0x05,
+ 0x1b, 0x24,
+ 0x1c, 0x04,
+ 0x1e, 0x00,
+ 0x1f, 0xc8,
+ 0x20, 0x00,
+ 0x21, 0x48,
+ 0x23, 0x22,
+ 0x24, 0x15,
+
+
+ 0x26, 0xf7,
+ 0x28, 0x7f,
+ 0x29, 0x00,
+ 0x32, 0x00,
+ 0x33, 0x20,
+ 0x34, 0x20,
+ 0x35, 0x20,
+ 0x36, 0x20,
+ 0x3b, 0x04,
+ 0x3c, 0x04,
+ 0x3d, 0x04,
+ 0x3e, 0x04,
+
+ 0x40, 0xff,
+ 0x41, 0x00,
+ 0x42, 0xfe,
+ 0x46, 0x02,
+ 0x4b, 0xcb,
+ 0x4d, 0x01,
+ 0x4f, 0x01,
+ 0x70, 0x48,
+ 0x80, 0xe7,
+ 0x82, 0x45,
+ 0x87, 0x4a,
+ 0xfe, 0x01,
+ 0x18, 0x22,
+ 0xfe, 0x00,
+ 0x9c, 0x0a,
+ 0xa4, 0x50,
+ 0xa5, 0x21,
+ 0xa7, 0x35,
+ 0xdd, 0x54,
+ 0x95, 0x35,
+ 0xfe, 0x00,
+ ///gamma
+ /*
+ 0xbf, 0x0b,
+ 0xc0, 0x16,
+ 0xc1, 0x32,
+ 0xc2, 0x3f,
+ 0xc3, 0x50,
+ 0xc4, 0x6d,
+ 0xc5, 0x78,
+ 0xc6, 0x92,
+ 0xc7, 0xae,
+ 0xc8, 0xbd,
+ 0xc9, 0xcf,
+ 0xca, 0xdc,
+ 0xcb, 0xe7,
+ 0xcc, 0xed,
+ 0xcd, 0xf9,
+ 0xce, 0xfd,
+ 0xcf, 0xff,
+ */
+
+ 0xBF, 0x14,
+ 0xc0, 0x28,
+ 0xc1, 0x44,
+ 0xc2, 0x5D,
+ 0xc3, 0x72,
+ 0xc4, 0x86,
+ 0xc5, 0x95,
+ 0xc6, 0xB1,
+ 0xc7, 0xC6,
+ 0xc8, 0xD5,
+ 0xc9, 0xE1,
+ 0xcA, 0xEA,
+ 0xcB, 0xF1,
+ 0xcC, 0xF5,
+ 0xcD, 0xFB,
+ 0xcE, 0xFE,
+ 0xcF, 0xFF,
+
+
+ 0xfe, 0x00,
+ 0xb3, 0x44,
+ 0xb4, 0xfd,
+ 0xb5, 0x02,
+ 0xb6, 0xfa,
+ 0xb7, 0x48,
+ 0xb8, 0xf0,
+ 0x50, 0x01,
+ 0x19, 0x05,
+ 0x20, 0x01,
+ 0x22, 0xba,
+ 0x21, 0x48,
+ 0xfe, 0x00,
+ 0xd1, 0x34,
+ 0xd2, 0x34,
+ 0xfe, 0x01,
+ 0x10, 0x40,
+ 0x12, 0x17,
+ 0x13, 0x60,
+ 0x17, 0x88,
+ 0x21, 0xb0,
+ 0x22, 0x60,
+ 0x3c, 0x82,
+ 0x3d, 0x55,
+ 0x3e, 0x48,
+ 0xfe, 0x01,
+ 0x06, 0x08,
+ 0x07, 0x06,
+ 0x08, 0xa6,
+ 0x09, 0xee,
+ 0x6a, 0x40,
+ 0x6b, 0x39,
+ 0xd0, 0x00,
+ 0xd2, 0x2c,
+ 0xd3, 0x80,
+ 0x9c, 0x00,
+ 0x9d, 0x10,
+ 0x9e, 0xc0,
+ 0x9f, 0x40,
+ 0xfe, 0x01,
+ 0xa0, 0x00,
+ 0xa1, 0x3c,
+ 0xa2, 0x50,
+ 0xa3, 0x00,
+ 0xa8, 0x0f,
+ 0xa9, 0x08,
+ 0xaa, 0x00,
+ 0xab, 0x04,
+ 0xac, 0x00,
+ 0xad, 0x07,
+ 0xae, 0x0e,
+ 0xaf, 0x00,
+ 0xb0, 0x00,
+ 0xb1, 0x09,
+ 0xb2, 0x00,
+ 0xb3, 0x00,
+ 0xb4, 0x31,
+ 0xb5, 0x19,
+ 0xb6, 0x24,
+ 0xba, 0x3a,
+ 0xbb, 0x24,
+ 0xbc, 0x2a,
+ 0xc0, 0x17,
+ 0xc1, 0x13,
+ 0xc2, 0x17,
+ 0xc6, 0x21,
+ 0xc7, 0x1c,
+ 0xc8, 0x1c,
+ 0xb7, 0x00,
+ 0xb8, 0x00,
+ 0xb9, 0x00,
+ 0xbd, 0x00,
+ 0xbe, 0x00,
+ 0xbf, 0x00,
+ 0xc3, 0x00,
+ 0xc4, 0x00,
+ 0xc5, 0x00,
+ 0xc9, 0x00,
+ 0xca, 0x00,
+ 0xcb, 0x00,
+ 0xa4, 0x00,
+ 0xa5, 0x00,
+ 0xa6, 0x00,
+ 0xa7, 0x00,
+ 0xfe, 0x00,
+
+ 0xfe,0x01,
+ 0x18,0x22,
+ 0x21,0xc0,
+ 0x06,0x12,
+ 0x08,0x9c,
+ 0x51,0x28,
+ 0x52,0x10,
+ 0x53,0x20,
+ 0x54,0x40,
+ 0x55,0x16,
+ 0x56,0x30,
+ 0x58,0x60,
+ 0x59,0x08,
+ 0x5c,0x35,
+ 0x5d,0x72,
+ 0x67,0x80,
+ 0x68,0x60,
+ 0x69,0x90,
+ 0x6c,0x30,
+ 0x6d,0x60,
+ 0x70,0x10,
+ 0xfe,0x00,
+ 0x9c,0x0a,
+ 0xa0,0xaf,
+ 0xa2,0xff,
+ 0xa4,0x60,
+ 0xa5,0x31,
+ 0xa7,0x35,
+ 0x42,0xfe,
+ 0xd1,0x34,
+ 0xd2,0x34,
+ 0xd3,0x3b,
+ 0xd5,0x0c,
+ 0xfe,0x00,
+
+ 0xa0, 0xaf,
+ 0xa2, 0xff,
+ 0x44, 0xa2,
+};
+
+uint8_t gc0329_default_regs_exit[] = {
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc2015/gc2015.c b/drivers/media/video/wmt_v4l2/sensors/gc2015/gc2015.c
new file mode 100755
index 00000000..fb0d5351
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc2015/gc2015.c
@@ -0,0 +1,225 @@
+
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "gc2015.h"
+
+#define sensor_write_array(sd, arr, sz) \
+ cmos_init_8bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write(sd->i2c_addr, reg, val)
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint8_t *regs;
+ size_t size;
+};
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+ CMOS_WIN_SIZE("QVGA", 320, 240, gc2015_320x240),
+ CMOS_WIN_SIZE("VGA", 640, 480, gc2015_640x480),
+ CMOS_WIN_SIZE("UXGA", 1600, 1200, gc2015_1600x1200),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+ int data;
+
+ data = sensor_read(sd, 0x14);
+
+ switch (value) {
+ case 0:
+ data &= ~0x01;
+ break;
+ case 1:
+ data |= 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sensor_write(sd, 0x14, data);
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+ int data;
+
+ data = sensor_read(sd, 0x14);
+
+ switch (value) {
+ case 0:
+ data &= ~0x02;
+ break;
+ case 1:
+ data |= 0x02;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sensor_write(sd, 0x14, data);
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = gc2015_wb_auto;
+ size = ARRAY_SIZE(gc2015_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = gc2015_wb_incandescent;
+ size = ARRAY_SIZE(gc2015_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = gc2015_wb_daylight;
+ size = ARRAY_SIZE(gc2015_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = gc2015_wb_cloudy;
+ size = ARRAY_SIZE(gc2015_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = gc2015_wb_fluorescent;
+ size = ARRAY_SIZE(gc2015_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win)
+ return -EINVAL;
+
+ sensor_write_array(sd, win->regs, win->size);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return 0;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ return (sensor_read(sd, 0) == sd->id) ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ if (!sensor_identify(sd)) {
+ return -1;
+ }
+
+ sensor_write_array(sd, gc2015_default_regs_init,
+ ARRAY_SIZE(gc2015_default_regs_init));
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ return 0;
+}
+
+static struct cmos_subdev_ops gc2015_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+};
+
+struct cmos_subdev gc2015 = {
+ .name = "gc2015",
+ .i2c_addr = 0x30,
+ .id = 0x2005,
+ .max_width = 1600,
+ .max_height = 1200,
+ .ops = &gc2015_ops,
+};
+
+static int __init gc2015_init(void)
+{
+ return cmos_register_sudbdev(&gc2015);
+}
+
+static void __exit gc2015_exit(void)
+{
+ return cmos_unregister_subdev(&gc2015);
+}
+
+module_init(gc2015_init);
+module_exit(gc2015_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc2015/gc2015.h b/drivers/media/video/wmt_v4l2/sensors/gc2015/gc2015.h
new file mode 100755
index 00000000..78016867
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc2015/gc2015.h
@@ -0,0 +1,601 @@
+#ifndef GC2015_H
+#define GC2015_H
+
+
+// Scene Mode
+uint8_t gc2015_scene_mode_auto[] = {
+};
+
+uint8_t gc2015_scene_mode_night[] = {
+};
+
+
+// White Balance
+uint8_t gc2015_wb_auto [] = {
+};
+
+uint8_t gc2015_wb_incandescent [] = {
+};
+
+uint8_t gc2015_wb_fluorescent [] = {
+};
+
+uint8_t gc2015_wb_daylight [] = {
+};
+
+uint8_t gc2015_wb_cloudy [] = {
+};
+
+uint8_t gc2015_wb_tungsten [] = {
+};
+
+
+// Exposure
+uint8_t gc2015_exposure_neg6[] = {
+};
+
+uint8_t gc2015_exposure_neg3[] = {
+};
+
+uint8_t gc2015_exposure_zero[] = {
+};
+
+uint8_t gc2015_exposure_pos3[] = {
+};
+
+uint8_t gc2015_exposure_pos6[] = {
+};
+
+
+// Color Effect
+uint8_t gc2015_colorfx_none[] = {
+};
+
+uint8_t gc2015_colorfx_bw[] = {
+};
+
+uint8_t gc2015_colorfx_sepia[] = {
+};
+
+uint8_t gc2015_colorfx_negative[] = {
+};
+
+uint8_t gc2015_colorfx_emboss[] = {
+};
+
+uint8_t gc2015_colorfx_sketch[] = {
+};
+
+uint8_t gc2015_colorfx_sky_blue[] = {
+};
+
+uint8_t gc2015_colorfx_grass_green[] = {
+};
+
+uint8_t gc2015_colorfx_skin_whiten[] = {
+};
+
+uint8_t gc2015_colorfx_vivid[] = {
+};
+
+// Brightness
+uint8_t gc2015_brightness_neg4[] = {
+};
+
+uint8_t gc2015_brightness_neg3[] = {
+};
+
+uint8_t gc2015_brightness_neg2[] = {
+};
+
+uint8_t gc2015_brightness_neg1[] = {
+};
+
+uint8_t gc2015_brightness_zero[] = {
+};
+
+uint8_t gc2015_brightness_pos1[] = {
+};
+
+uint8_t gc2015_brightness_pos2[] = {
+};
+
+uint8_t gc2015_brightness_pos3[] = {
+};
+
+uint8_t gc2015_brightness_pos4[] = {
+};
+
+// Contrast
+uint8_t gc2015_contrast_neg4[] = {
+};
+
+uint8_t gc2015_contrast_neg3[] = {
+};
+
+uint8_t gc2015_contrast_neg2[] = {
+};
+
+uint8_t gc2015_contrast_neg1[] = {
+};
+
+uint8_t gc2015_contrast_zero[] = {
+};
+
+uint8_t gc2015_contrast_pos1[] = {
+};
+
+uint8_t gc2015_contrast_pos2[] = {
+};
+
+uint8_t gc2015_contrast_pos3[] = {
+};
+
+uint8_t gc2015_contrast_pos4[] = {
+};
+
+// Saturation
+uint8_t gc2015_saturation_neg4[] = {
+};
+
+uint8_t gc2015_saturation_neg3[] = {
+};
+
+uint8_t gc2015_saturation_neg2[] = {
+};
+
+uint8_t gc2015_saturation_neg1[] = {
+};
+
+uint8_t gc2015_saturation_zero[] = {
+};
+
+uint8_t gc2015_saturation_pos1[] = {
+};
+
+uint8_t gc2015_saturation_pos2[] = {
+};
+
+uint8_t gc2015_saturation_pos3[] = {
+};
+
+uint8_t gc2015_saturation_pos4[] = {
+};
+
+
+// Resolution
+
+// QCIF
+uint8_t gc2015_176x144[]={
+};
+
+// QVGA
+uint8_t gc2015_320x240[]={
+0xfe, 0x80,
+0xfe, 0x80,
+0xfe, 0x80,
+0xfe, 0x00,
+0x45, 0x00,
+0x02, 0x01,
+0x2a, 0xca,
+0x48, 0x40,
+0x59, 0x11,
+0x50, 0x01,
+0x51, 0x00,
+0x52, 0x00,
+0x53, 0x00,
+0x54, 0x00,
+0x55, 0x02,
+0x56, 0x58,
+0x57, 0x03,
+0x58, 0x20,
+0x59, 0xaa,
+0x5b, 0x01,
+0x5c, 0x34,
+0x5d, 0x00,
+0x5e, 0x00,
+0x5f, 0x01,
+0x60, 0x34,
+0x61, 0x00,
+0x62, 0x00,
+0xfe, 0x01,
+0xb0, 0x13,
+0xb1, 0x20,
+0xb2, 0x20,
+0xb3, 0x20,
+0xb4, 0x20,
+0xb5, 0x20,
+0xb6, 0x20,
+0xb7, 0x00,
+0xb8, 0x80,
+0xb9, 0x80,
+0xba, 0x13,
+0xbb, 0x20,
+0xbc, 0x20,
+0xbd, 0x20,
+0xbe, 0x20,
+0xbf, 0x20,
+0xc0, 0x20,
+0xc1, 0x00,
+0xc2, 0x80,
+0xc3, 0x80,
+0xfe, 0x00,
+0x29, 0x00,
+0x2b, 0x06,
+0x32, 0x0c,
+0x33, 0x0f,
+0x34, 0x00,
+0x35, 0x88,
+0x37, 0x16,
+0x40, 0xff,
+0x41, 0x24,
+0x42, 0x76,
+0x4b, 0xea,
+0x4d, 0x03,
+0x4f, 0x01,
+0x63, 0x77,
+0x66, 0x00,
+0x6d, 0x04,
+0x6e, 0x18,
+0x70, 0x18,
+0x6f, 0x10,
+0x71, 0x10,
+0x73, 0x03,
+0x80, 0x07,
+0x82, 0x08,
+0x8a, 0x7c,
+0x8c, 0x02,
+0x8e, 0x02,
+0x8f, 0x48,
+0xb0, 0x44,
+0xb1, 0xfe,
+0xb2, 0x00,
+0xb3, 0xf8,
+0xb4, 0x48,
+0xb5, 0xf8,
+0xb6, 0x00,
+0xb7, 0x04,
+0xb8, 0x00,
+0xbF, 0x0E,
+0xc0, 0x1C,
+0xc1, 0x34,
+0xc2, 0x48,
+0xc3, 0x5A,
+0xc4, 0x6B,
+0xc5, 0x7B,
+0xc6, 0x95,
+0xc7, 0xAB,
+0xc8, 0xBF,
+0xc9, 0xCE,
+0xcA, 0xD9,
+0xcB, 0xE4,
+0xcC, 0xEC,
+0xcD, 0xF7,
+0xcE, 0xFD,
+0xcF, 0xFF,
+0xCF, 0xFF,
+0xd1, 0x38,
+0xd2, 0x38,
+0xde, 0x21,
+0x98, 0x30,
+0x99, 0xf0,
+0x9b, 0x00,
+0xfe, 0x01,
+0x10, 0x45,
+0x11, 0x32,
+0x13, 0x60,
+0x17, 0x00,
+0x1c, 0x96,
+0x1e, 0x11,
+0x21, 0xc0,
+0x22, 0x40,
+0x2d, 0x06,
+0x2e, 0x00,
+0x1e, 0x32,
+0x33, 0x00,
+0x57, 0x40,
+0x5d, 0x44,
+0x5c, 0x35,
+0x5e, 0x29,
+0x5f, 0x50,
+0x60, 0x50,
+0x65, 0xc0,
+0x80, 0x82,
+0x81, 0x00,
+0x83, 0x00,
+0xfe, 0x00,
+0x45, 0x0f,
+0x46, 0x02,
+//0x46, 0x03,
+0x44, 0xa2,
+0xfe, 0x00,
+0xfe, 0x01,
+0x33, 0x20,
+0xfe, 0x00,
+0x43, 0x00,
+0x42, 0x74,
+0x7a, 0x5f,
+0x7b, 0x40,
+0x7c, 0x47,
+0x42, 0x76,
+0xfe, 0x01,
+0x13, 0x60,
+0xfe, 0x00,
+0xd5, 0x00,
+0x05, 0x01,
+0x06, 0xc1,
+0x07, 0x00,
+0x08, 0x40,
+0xfe, 0x01,
+0x29, 0x01,
+0x2a, 0x00,
+0x2b, 0x05,
+0x2c, 0x00,
+0x2d, 0x06,
+0x2e, 0x00,
+0x2f, 0x08,
+0x30, 0x00,
+0x31, 0x09,
+0x32, 0x00,
+0xfe, 0x00,
+0xfe, 0x01,
+0x33, 0x20,
+0xfe, 0x00,
+};
+
+// CIF
+uint8_t gc2015_352x288[]={
+};
+
+// VGA
+uint8_t gc2015_640x480[]={
+ 0xfe,0x00,
+ 0x02,0x01,
+ 0x2a,0xca,
+ 0x59,0x55,
+ 0x5a,0x06,
+ 0x5b,0x00,
+ 0x5c,0x00,
+ 0x5d,0x01,
+ 0x5e,0x23,
+ 0x5f,0x00,
+ 0x60,0x00,
+ 0x61,0x01,
+ 0x62,0x23,
+ 0x50,0x01,
+ 0x51,0x00,
+ 0x52,0x00,
+ 0x53,0x00,
+ 0x54,0x00,
+ 0x55,0x01,
+ 0x56,0xe0,
+ 0x57,0x02,
+ 0x58,0x80,
+ 0x48,0x68,
+ 0x4f,0x01,
+};
+
+// SVGA
+uint8_t gc2015_800x600[]={
+};
+
+// XGA
+uint8_t gc2015_1024x768[]={
+};
+
+// 720p
+uint8_t gc2015_1280x720[]={
+};
+
+// UXGA
+uint8_t gc2015_1600x1200[]={
+0x4f,0x00,
+0x42,0x74,
+0xfe,0x00,
+0x02,0x00,
+0x2a,0x0a,
+0x59,0x11,
+0x5a,0x06,
+0x5b,0x00,
+0x5c,0x00,
+0x5d,0x00,
+0x5e,0x00,
+0x5f,0x00,
+0x60,0x00,
+0x61,0x00,
+0x62,0x00,
+0x50,0x01,
+0x51,0x00,
+0x52,0x00,
+0x53,0x00,
+0x54,0x00,
+0x55,0x04,
+0x56,0xb0,
+0x57,0x06,
+0x58,0x40,
+0x48,0x60,
+0x0,0x0,
+0x12,0x1,
+0x13,0x2a,
+0x6e,0x1b,
+0x6f,0x20,
+0x70,0x1b,
+0x71,0x20,
+};
+
+
+// Initiliztion
+uint8_t gc2015_default_regs_init[] = {
+0xfe, 0x80,
+0xfe, 0x80,
+0xfe, 0x80,
+0xfe, 0x00,
+0x45, 0x00,
+0x02, 0x01,
+0x2a, 0xca,
+0x48, 0x40,
+0x59, 0x11,
+0x50, 0x01,
+0x51, 0x00,
+0x52, 0x00,
+0x53, 0x00,
+0x54, 0x00,
+0x55, 0x02,
+0x56, 0x58,
+0x57, 0x03,
+0x58, 0x20,
+0x59, 0x55,
+0x5b, 0x01,
+0x5c, 0x34,
+0x5d, 0x00,
+0x5e, 0x00,
+0x5f, 0x01,
+0x60, 0x34,
+0x61, 0x00,
+0x62, 0x00,
+0xfe, 0x01,
+0xb0, 0x13,
+0xb1, 0x20,
+0xb2, 0x20,
+0xb3, 0x20,
+0xb4, 0x20,
+0xb5, 0x20,
+0xb6, 0x20,
+0xb7, 0x00,
+0xb8, 0x80,
+0xb9, 0x80,
+0xba, 0x13,
+0xbb, 0x20,
+0xbc, 0x20,
+0xbd, 0x20,
+0xbe, 0x20,
+0xbf, 0x20,
+0xc0, 0x20,
+0xc1, 0x00,
+0xc2, 0x80,
+0xc3, 0x80,
+0xfe, 0x00,
+0x29, 0x00,
+0x2b, 0x06,
+0x32, 0x0c,
+0x33, 0x0f,
+0x34, 0x00,
+0x35, 0x88,
+0x37, 0x16,
+0x40, 0xff,
+0x41, 0x24,
+0x42, 0x76,
+0x4b, 0xea,
+0x4d, 0x03,
+0x4f, 0x01,
+0x63, 0x77,
+0x66, 0x00,
+0x6d, 0x04,
+0x6e, 0x18,
+0x70, 0x18,
+0x6f, 0x10,
+0x71, 0x10,
+0x73, 0x03,
+0x80, 0x07,
+0x82, 0x08,
+0x8a, 0x7c,
+0x8c, 0x02,
+0x8e, 0x02,
+0x8f, 0x22,//48
+0xb0, 0x44,
+0xb1, 0xfe,
+0xb2, 0x00,
+0xb3, 0xf8,
+0xb4, 0x48,
+0xb5, 0xf8,
+0xb6, 0x00,
+0xb7, 0x04,
+0xb8, 0x00,
+0xbF, 0x0E,
+0xc0, 0x1C,
+0xc1, 0x34,
+0xc2, 0x48,
+0xc3, 0x5A,
+0xc4, 0x6B,
+0xc5, 0x7B,
+0xc6, 0x95,
+0xc7, 0xAB,
+0xc8, 0xBF,
+0xc9, 0xCE,
+0xcA, 0xD9,
+0xcB, 0xE4,
+0xcC, 0xEC,
+0xcD, 0xF7,
+0xcE, 0xFD,
+0xcF, 0xFF,
+0xCF, 0xFF,
+0xd1, 0x38,
+0xd2, 0x38,
+0xde, 0x21,
+0x98, 0x30,
+0x99, 0xf0,
+0x9b, 0x00,
+0xfe, 0x01,
+0x10, 0x45,
+0x11, 0x32,
+0x13, 0x70,//60
+0x17, 0x00,
+0x1c, 0x96,
+0x1e, 0x11,
+0x21, 0xc0,
+0x22, 0x40,
+0x2d, 0x06,
+0x2e, 0x00,
+0x1e, 0x32,
+0x33, 0x00,
+0x57, 0x40,
+0x5d, 0x44,
+0x5c, 0x35,
+0x5e, 0x29,
+0x5f, 0x50,
+0x60, 0x50,
+0x65, 0xc0,
+0x80, 0x82,
+0x81, 0x00,
+0x83, 0x00,
+0xfe, 0x00,
+0x45, 0x0f,
+0x46, 0x02,
+//0x46, 0x03,
+0x44, 0xa2,
+0xfe, 0x00,
+0xfe, 0x01,
+0x33, 0x20,
+0xfe, 0x00,
+0x43, 0x00,
+0x42, 0x74,
+0x7a, 0x5f,
+0x7b, 0x40,
+0x7c, 0x47,
+0x42, 0x76,
+0xfe, 0x01,
+0x13, 0x70,//60
+0xfe, 0x00,
+0xd5, 0x00,
+0x05, 0x01,
+0x06, 0xc1,
+0x07, 0x00,
+0x08, 0x40,
+0xfe, 0x01,
+0x29, 0x01,
+0x2a, 0x00,
+0x2b, 0x05,
+0x2c, 0x00,
+0x2d, 0x06,
+0x2e, 0x00,
+0x2f, 0x08,
+0x30, 0x00,
+0x31, 0x09,
+0x32, 0x00,
+0xfe, 0x00,
+0xfe, 0x01,
+0x33, 0x20,
+0xfe, 0x00,
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-3nod.h b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-3nod.h
new file mode 100755
index 00000000..b8057c96
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-3nod.h
@@ -0,0 +1,1024 @@
+#ifndef GC2035_H
+#define GC2035_H
+
+
+// Scene Mode
+uint8_t gc2035_scene_mode_auto[] = {
+ 0xfe,0x01,
+ 0x3e,0x40,
+ 0x1f,0xa0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x50,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe,0x00,
+};
+
+uint8_t gc2035_scene_mode_night[] = {
+ 0xfe, 0x01,
+ 0x3e, 0x60,
+ 0x1f,0xe0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x80,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe, 0x00,
+};
+
+
+// White Balance
+uint8_t gc2035_wb_auto [] = {
+ 0xfe, 0x00,
+// 0xb3, 0x61, //2013-8-16 dustin
+// 0xb4, 0x40,
+// 0xb5, 0x61,
+ 0x82, 0xfe,
+};
+
+uint8_t gc2035_wb_incandescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0xa8,
+};
+
+uint8_t gc2035_wb_fluorescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0x78,
+};
+
+uint8_t gc2035_wb_daylight [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x78,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+uint8_t gc2035_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x58,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+
+// Exposure
+uint8_t gc2035_exposure_neg6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x68,
+ 0xfe, 0x02,
+ 0xd5, 0xe0,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc2035_exposure_neg3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x70,
+ 0xfe, 0x02,
+ 0xd5, 0xf0,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc2035_exposure_zero[] = {
+ 0xfe, 0x01,
+ 0x13, 0x75,//78 cyrille
+ 0xfe, 0x02,
+ 0xd5, 0xf8,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_pos3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x80,
+ 0xfe, 0x02,
+ 0xd5, 0x10,
+ 0xfe, 0x00,
+
+
+};
+
+uint8_t gc2035_exposure_pos6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x88,
+ 0xfe, 0x02,
+ 0xd5, 0x20,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_176_144_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x33,
+ 0x9b,0x00,
+ 0x9f,0x00,
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0x92,//600
+ 0x97,0x00,
+ 0x98,0xb0,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+
+ 0xfe,0x03,
+ 0x42,0x60,
+ 0x43,0x01,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_320_240_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x22,
+ 0x9b,0x00,
+ 0x9f,0x00,
+
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0xf2,//600
+ 0x97,0x01,
+ 0x98,0x40,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x02,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_1280_720_regs[]={
+ 0xfe,0x00,
+ 0x0a,0xf0,
+ 0x0c,0xa0,
+ 0x0d,0x02,
+ 0x0e,0xd8,
+ 0x0f,0x05,
+ 0x10,0x18,
+ 0xfe,0x00,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x05,0x01,
+ 0x06,0xaa,
+ 0x07,0x00,
+ 0x08,0xf6,
+ 0xfe,0x01,
+ 0x27,0x00,
+ 0x28,0xa1,
+ 0x29,0x03,
+ 0x2a,0xc6,
+ 0x2b,0x04,
+ 0x2c,0x67,
+ 0x2d,0x06,
+ 0x2e,0x4a,
+ 0x2f,0x07,
+ 0x30,0x8c,
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xb6,0x03,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x99,0x11,
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0xd0,
+ 0x97,0x05,
+ 0x98,0x00,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+ 0xfa,0x11,
+};
+
+uint8_t gc2035_1600_1200_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x02,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x04,
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+
+ 0xc8,0x00,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+};
+
+uint8_t gc2035_800_600_regs[]={
+ 0xfe,0x00,
+ 0xfa,0x00,
+ 0xc8,0x00,
+
+ 0x99,0x22,
+ 0x9a,0x07,
+ 0x9b,0x00,
+ 0x9c,0x00,
+ 0x9d,0x00,
+ 0x9e,0x00,
+ 0x9f,0x00,
+ 0xa0,0x00,
+ 0xa1,0x00,
+ 0xa2,0x00,
+
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0x58,
+ 0x97,0x03,
+ 0x98,0x20,
+};
+
+uint8_t gc2035_640_480_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x00,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+
+ 0xc8,0x15,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x00,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+ 0xb6,0x03,
+};
+
+uint8_t gc2035_default_regs_init[]={
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfc,0x06,
+ 0xf2,0x00,
+ 0xf3,0x00,
+ 0xf4,0x00,
+ 0xf5,0x00,
+ 0xf9,0xfe, //[0] pll enable
+ 0xfa,0x00,
+ 0xf6,0x00,
+ 0xf7,0x15, //pll enable
+
+ 0xf8,0x84,//85
+ 0xfe,0x00,
+ 0x82,0x00,
+ 0xb3,0x60,
+ 0xb4,0x40,
+ 0xb5,0x60,
+
+ 0x03,0x02,
+ 0x04,0x71,
+
+ ////,/meare window ///////////
+ 0xfe,0x00,
+ 0xec,0x06,//04
+ 0xed,0x06,//04
+ 0xee,0x62,//60
+ 0xef,0x92,//90
+
+ ////,//anog/////////////
+ 0x0a,0x00, //row start
+ 0x0c,0x00, //col start
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06, //Window setting
+ 0x10,0x58,
+ 0x17,0x14, //[0]mirror [1]flip
+ 0x18,0x0a, //0a 2012.10.26
+ 0x19,0x0a, //AD pipe number
+ 0x1a,0x01, //CISCTL mode4
+ 0x1b,0x8b,
+ 0x1c,0x05,//add by lanking 20130403
+ 0x1e,0x88, //analog mode1 [7] tx-high en [5:3]COL_bias
+ 0x1f,0x08, //[3] tx-low en//
+ 0x20,0x05, //[0]adclk mode},{0x[1]rowclk_MODE [2]rsthigh_en
+ 0x21,0x0f, //[6:4]rsg
+ 0x22,0xf0, //[3:0]vref 20130403
+ 0x23,0xc3, //f3//ADC_r
+ 0x24,0x17, //pad drive 16
+
+
+ 0xfe,0x01,
+ 0x11,0x20,//AEC_out_slope},{0x
+ 0x1f,0xc0,//max_post_gain
+ 0x20,0x48,//max_pre_gain
+ 0x47,0x30,//AEC_outdoor_th
+ 0x0b,0x10,//
+ 0x13,0x75,//y_target 84
+ 0xfe,0x00,
+
+#if 0
+ 0x05,0x01,//hb
+ 0x06,0x11,
+ 0x07,0x00,//vb
+ 0x08,0x50,
+ 0xfe,0x01,
+ 0x27,0x00,//step
+ 0x28,0xa0,
+ 0x29,0x05,//level1
+ 0x2a,0x00,
+ 0x2b,0x05,//level2
+ 0x2c,0x00,
+ 0x2d,0x06,//6e8//level3
+ 0x2e,0xe0,
+ 0x2f,0x0a,//level4
+ 0x30,0x00,
+#endif
+ 0x05,0x01,//hb
+ 0x06,0x5c,
+ 0x07,0x00,//vb
+ 0x08,0x32,
+ 0xfe,0x01,
+ 0x27,0x00,//step
+ 0x28,0x7d,
+ 0x29,0x04,//level1
+ 0x2a,0xe2,
+ 0x2b,0x05,//level2
+ 0x2c,0xdc,
+ 0x2d,0x05,//6e8//level3
+ 0x2e,0xdc,
+ 0x2f,0x09,//level4
+ 0x30,0xc4,
+
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xfe,0x00, //0x},{0x},{0x},{0x},{0x
+ 0xb6,0x03, //AEC enable
+ 0xfe,0x00,
+
+ ////,BLK////
+ 0x3f,0x00, //prc close
+ 0x40,0x77,//
+ 0x42,0x7f,
+ 0x43,0x30,
+ 0x5c,0x08,
+ 0x5e,0x20,
+ 0x5f,0x20,
+ 0x60,0x20,
+ 0x61,0x20,
+ 0x62,0x20,
+ 0x63,0x20,
+ 0x64,0x20,
+ 0x65,0x20,
+
+ //blo,/////////
+ 0x80,0xff,
+ 0x81,0x26,//38},{0x//skin_Y 8c_debug
+ 0x87,0x90, //[7]middle gamma
+ 0x84,0x02, //output put foramat
+ 0x86,0x02, //02 //sync plority
+ 0x8b,0xbc,
+ 0xb0,0x80, //globle gain
+ 0xc0,0x40,//Yuv bypass
+
+ ////lsc///////////
+ 0xfe,0x01,
+ 0xc2,0x38,
+ 0xc3,0x25,
+ 0xc4,0x21,
+ 0xc8,0x19,
+ 0xc9,0x12,
+ 0xca,0x0e,
+ 0xbc,0x43,
+ 0xbd,0x18,
+ 0xbe,0x1b,
+ 0xb6,0x40,
+ 0xb7,0x2e,
+ 0xb8,0x26,
+ 0xc5,0x05,
+ 0xc6,0x03,
+ 0xc7,0x04,
+ 0xcb,0x00,
+ 0xcc,0x00,
+ 0xcd,0x00,
+ 0xbf,0x14,
+ 0xc0,0x22,
+ 0xc1,0x1b,
+ 0xb9,0x00,
+ 0xba,0x05,
+ 0xbb,0x05,
+ 0xaa,0x35,
+ 0xab,0x33,
+ 0xac,0x33,
+ 0xad,0x25,
+ 0xae,0x22,
+ 0xaf,0x27,
+ 0xb0,0x1d,
+ 0xb1,0x20,
+ 0xb2,0x22,
+ 0xb3,0x14,
+ 0xb4,0x15,
+ 0xb5,0x16,
+ 0xd0,0x00,
+ 0xd2,0x07,
+ 0xd3,0x08,
+ 0xd8,0x00,
+ 0xda,0x13,
+ 0xdb,0x17,
+ 0xdc,0x00,
+ 0xde,0x0a,
+ 0xdf,0x08,
+ 0xd4,0x00,
+ 0xd6,0x00,
+ 0xd7,0x0c,
+ 0xa4,0x00,
+ 0xa5,0x00,
+ 0xa6,0x00,
+ 0xa7,0x00,
+ 0xa8,0x00,
+ 0xa9,0x00,
+ 0xa1,0x80,
+ 0xa2,0x80,
+
+ ////,/cc////////////
+ 0xfe,0x02,
+ 0xc0,0x01,
+ 0xc1,0x40, //Green_cc for d
+ 0xc2,0xfc,
+ 0xc3,0x05,
+ 0xc4,0xec,
+ 0xc5,0x42,
+ 0xc6,0xf8,
+ 0xc7,0x40,//for cwf
+ 0xc8,0xf8,
+ 0xc9,0x06,
+ 0xca,0xfd,
+ 0xcb,0x3e,
+ 0xcc,0xf3,
+ 0xcd,0x36,//for A
+ 0xce,0xf6,
+ 0xcf,0x04,
+ 0xe3,0x0c,
+ 0xe4,0x44,
+ 0xe5,0xe5,
+ 0xfe,0x00,
+
+ ////,b stt ////////////////AWB ,ar
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x10, // 10
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x20, // 20
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00, // 30
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x40, // 40
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x50, // 50
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x60, // 60
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x70, // 70
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x80, // 80
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x90, // 90
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xa0, // a0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xb0, // b0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xc0, // c0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xd0, // d0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4f,0x01,
+ ////,wb vue////////
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x40,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x53,
+ 0x4e,0x08,
+ 0x4e,0x04,
+ 0x4d,0x62,
+ 0x4e,0x10,
+ 0x4d,0x72,
+ 0x4e,0x20,
+ 0x4f,0x01,
+
+ ///a,///
+ 0xfe,0x01,
+ 0x50,0x88,//c0//[6]green mode
+ 0x52,0x40,
+ 0x54,0x60,
+ 0x56,0x06,
+ 0x57,0x20, //pre adjust
+ 0x58,0x01,
+ 0x5b,0x02, //AWB_gain_delta
+ 0x61,0xaa,//R/G stand
+ 0x62,0xaa,//R/G stand
+ 0x71,0x00,
+ 0x74,0x10, //0x//AWB_C_max
+ 0x77,0x08, // 0x//AWB_p2_x
+ 0x78,0xfd, //AWB_p2_y
+ 0x86,0x30,
+ 0x87,0x00,
+ 0x88,0x04,//06},{0x//[1]dark mode
+ 0x8a,0xc0,//awb move mode
+ 0x89,0x75,
+ 0x84,0x08, //0x//auto_window
+ 0x8b,0x00, // 0x//awb compare luma
+ 0x8d,0x70, //awb gain limit R
+ 0x8e,0x70,//G
+ 0x8f,0xf4,//B
+ 0xfe,0x00,
+ 0x82,0x02,//awb_en
+ ////wb end /////////////
+
+ //===,====de
+ 0xfe,0x01,
+
+ 0x9c,0x04,//00
+
+ 0x21,0xbf,
+ 0xfe,0x02,
+ 0xa4,0x00,//
+ 0xa5,0x40, //lsc_th
+ 0xa2,0xa0, //lsc_dec_slope
+ 0xa6,0x80, //dd_th
+ 0xa7,0x80, //ot_th
+ 0xab,0x31, //
+ 0xa9,0x6f, //
+ 0xb0,0x99, //0x//edge effect slope low
+ 0xb1,0x34,//edge effect slope low
+ 0xb3,0x80, //saturation dec slope
+ 0xde,0xb6, //
+ 0x38,0x0f, //
+ 0x39,0x60, //
+ 0xfe,0x00,
+ 0x81,0x26,
+ 0xfe,0x02,
+ 0x83,0x00,//
+ 0x84,0x45,//
+ ////,///Y//////////
+ 0xd1,0x38,//saturation_cb//34 cyrille
+ 0xd2,0x38,//saturation_Cr//34 cyrille
+ 0xd3,0x40,//contrast ?
+ 0xd4,0x80,//contrast center
+ 0xd5,0x00,//luma_offset
+ 0xdc,0x30,
+ 0xdd,0xb8,//edge_sa_g,b
+ 0xfe,0x00,
+ ////,dd/////////
+ 0xfe,0x02,
+ 0x88,0x1f,//dn_b_base
+ 0x8c,0xf6, //[2]b_in_dark_inc
+ 0x89,0x03, //dn_c_weight
+ ////,E /////////
+ 0xfe,0x02,
+ 0x90,0x6c,// EEINTP mode1
+ 0x97,0x45,// edge effect
+ //==,=======RGB Gamma
+ 0xfe,0x02,
+ 0x15,0x0a,
+ 0x16,0x12,
+ 0x17,0x19,
+ 0x18,0x1f,
+ 0x19,0x2c,
+ 0x1a,0x38,
+ 0x1b,0x42,
+ 0x1c,0x4e,
+ 0x1d,0x63,
+ 0x1e,0x76,
+ 0x1f,0x87,
+ 0x20,0x96,
+ 0x21,0xa2,
+ 0x22,0xb8,
+ 0x23,0xca,
+ 0x24,0xd8,
+ 0x25,0xe3,
+ 0x26,0xf0,
+ 0x27,0xf8,
+ 0x28,0xfd,
+ 0x29,0xff,
+
+ //===,=========y gamma
+ 0xfe,0x02,
+ 0x2b,0x00,
+ 0x2c,0x04,
+ 0x2d,0x09,
+ 0x2e,0x18,
+ 0x2f,0x27,
+ 0x30,0x37,
+ 0x31,0x49,
+ 0x32,0x5c,
+ 0x33,0x7e,
+ 0x34,0xa0,
+ 0x35,0xc0,
+ 0x36,0xe0,
+ 0x37,0xff,
+ ///1,x120ize//
+ 0xfe,0x00,//
+ 0x90,0x01, //0x//crop enable
+ 0x95,0x04, //0x//1600x1200
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+
+ 0xfe,0x03,
+ 0x42,0x40,
+ 0x43,0x06, //output buf width
+ 0x41,0x02, // Pclk_polarity
+ 0x40,0x40, //00
+ 0x17,0x00, //widv
+ 0xfe,0x00,
+ //ou,t DV////
+
+ 0xfe,0x00,
+ 0x82,0xfe, // fe
+ 0xf2,0x70,
+ 0xf3,0xff,
+ 0xf4,0x00,
+ 0xf5,0x30,
+
+ #if 0
+ //,///ssumple 640X4800//////
+ 0xfe,0x00,
+ 0xfa,0x00,
+ 0xc8,0x00,
+
+ 0x99,0x22,
+ 0x9a,0x07,
+ 0x9b,0x00,
+ 0x9c,0x00,
+ 0x9d,0x00,
+ 0x9e,0x00,
+ 0x9f,0x00,
+ 0xa0,0x00,
+ 0xa1,0x00,
+ 0xa2,0x00,
+
+
+ 0x90,0x01,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+ #endif
+
+
+ 0xfe,0x00,
+ 0x0c,0x00,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+
+ 0xc8,0x15,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x00,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+ 0xb6,0x03,
+
+#if 1
+ //// re zao///
+ 0xfe ,0x00,
+ 0x22 ,0xf0,
+ 0xfe ,0x01,
+ 0x21 ,0xff,
+ 0xfe ,0x02,
+ 0x8a ,0x33,
+ 0x8c ,0x76,
+ 0x8d ,0x85,
+ 0xa6 ,0xf0,
+ 0xae ,0x9f,
+ 0xa2 ,0x90,
+ 0xa5 ,0x40,
+ 0xa7 ,0x30,
+ 0xb0 ,0x88,
+ 0x38 ,0x0b,
+ 0x39 ,0x30,
+ 0xfe ,0x00,
+ 0x87 ,0xb0,
+
+
+ /*
+ //, sma RGB gamma////
+ 0xfe,0x02,
+ 0x15,0x0b,
+ 0x16,0x0e,
+ 0x17,0x10,
+ 0x18,0x12,
+ 0x19,0x19,
+ 0x1a,0x21,
+ 0x1b,0x29,
+ 0x1c,0x31,
+ 0x1d,0x41,
+ 0x1e,0x50,
+ 0x1f,0x5f,
+ 0x20,0x6d,
+ 0x21,0x79,
+ 0x22,0x91,
+ 0x23,0xa5,
+ 0x24,0xb9,
+ 0x25,0xc9,
+ 0x26,0xe1,
+ 0x27,0xee,
+ 0x28,0xf7,
+ 0x29,0xff,
+ */
+ //da,sun///
+ 0xfe,0x02,
+ 0x40,0x06,
+ 0x41,0x23,
+ 0x42,0x3f,
+ 0x43,0x06,
+ 0x44,0x00,
+ 0x45,0x00,
+ 0x46,0x14,
+ 0x47,0x09,
+
+ #endif
+};
+
+uint8_t gc2035_default_regs_exit[]={
+ 0xfe, 0x00,
+ 0xf1, 0x00,
+ 0xf2, 0x00,
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-781a.h b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-781a.h
new file mode 100755
index 00000000..c6fbc88f
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-781a.h
@@ -0,0 +1,1027 @@
+#ifndef GC2035_H
+#define GC2035_H
+
+
+// Scene Mode
+uint8_t gc2035_scene_mode_auto[] = {
+ 0xfe,0x01,
+ 0x3e,0x40,
+ 0x1f,0xa0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x50,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe,0x00,
+};
+
+uint8_t gc2035_scene_mode_night[] = {
+ 0xfe, 0x01,
+ 0x3e, 0x60,
+ 0x1f,0xe0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x80,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe, 0x00,
+};
+
+
+// White Balance
+uint8_t gc2035_wb_auto [] = {
+ 0xfe, 0x00,
+// 0xb3, 0x61, //2013-8-16 dustin
+// 0xb4, 0x40,
+// 0xb5, 0x61,
+ 0x82, 0xfe,
+};
+
+uint8_t gc2035_wb_incandescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0xa8,
+};
+
+uint8_t gc2035_wb_fluorescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0x78,
+};
+
+uint8_t gc2035_wb_daylight [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x78,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+uint8_t gc2035_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x58,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+
+// Exposure
+uint8_t gc2035_exposure_neg6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x68,
+ 0xfe, 0x02,
+ 0xd5, 0xe0,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc2035_exposure_neg3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x70,
+ 0xfe, 0x02,
+ 0xd5, 0xf0,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc2035_exposure_zero[] = {
+ 0xfe, 0x01,
+ 0x13, 0x78,
+ 0xfe, 0x02,
+ 0xd5, 0xf8,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_pos3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x80,
+ 0xfe, 0x02,
+ 0xd5, 0x10,
+ 0xfe, 0x00,
+
+
+};
+
+uint8_t gc2035_exposure_pos6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x88,
+ 0xfe, 0x02,
+ 0xd5, 0x20,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_176_144_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x33,
+ 0x9b,0x00,
+ 0x9f,0x00,
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0x92,//600
+ 0x97,0x00,
+ 0x98,0xb0,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+
+ 0xfe,0x03,
+ 0x42,0x60,
+ 0x43,0x01,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_320_240_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x22,
+ 0x9b,0x00,
+ 0x9f,0x00,
+
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0xf2,//600
+ 0x97,0x01,
+ 0x98,0x40,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x02,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_1280_720_regs[]={
+ 0xfe,0x00,
+ 0x0a,0xf0,
+ 0x0c,0xa0,
+ 0x0d,0x02,
+ 0x0e,0xd8,
+ 0x0f,0x05,
+ 0x10,0x18,
+ 0xfe,0x00,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x05,0x01,
+ 0x06,0xaa,
+ 0x07,0x00,
+ 0x08,0xf6,
+ 0xfe,0x01,
+ 0x27,0x00,
+ 0x28,0xa1,
+ 0x29,0x03,
+ 0x2a,0xc6,
+ 0x2b,0x04,
+ 0x2c,0x67,
+ 0x2d,0x06,
+ 0x2e,0x4a,
+ 0x2f,0x07,
+ 0x30,0x8c,
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xb6,0x03,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x99,0x11,
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0xd0,
+ 0x97,0x05,
+ 0x98,0x00,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+ 0xfa,0x11,
+};
+
+uint8_t gc2035_1600_1200_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x02,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x04,
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+
+ 0xc8,0x00,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+};
+
+uint8_t gc2035_800_600_regs[]={
+ 0xfe,0x00,
+ 0xfa,0x00,
+ 0xc8,0x00,
+
+ 0x99,0x22,
+ 0x9a,0x07,
+ 0x9b,0x00,
+ 0x9c,0x00,
+ 0x9d,0x00,
+ 0x9e,0x00,
+ 0x9f,0x00,
+ 0xa0,0x00,
+ 0xa1,0x00,
+ 0xa2,0x00,
+
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0x58,
+ 0x97,0x03,
+ 0x98,0x20,
+};
+
+uint8_t gc2035_640_480_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x00,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+
+ 0xc8,0x15,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x00,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+ 0xb6,0x03,
+};
+
+uint8_t gc2035_default_regs_init[]={
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfc,0x06,
+ 0xf2,0x00,
+ 0xf3,0x00,
+ 0xf4,0x00,
+ 0xf5,0x00,
+ 0xf9,0xfe, //[0] pll enable
+ 0xfa,0x00,
+ 0xf6,0x00,
+ 0xf7,0x15, //pll enable
+
+ 0xf8,0x84,//85
+ 0xfe,0x00,
+ 0x82,0x00,
+ 0xb3,0x60,
+ 0xb4,0x40,
+ 0xb5,0x60,
+
+ 0x03,0x02,
+ 0x04,0x71,
+
+ ////,/meare window ///////////
+ 0xfe,0x00,
+ 0xec,0x06,//04
+ 0xed,0x06,//04
+ 0xee,0x62,//60
+ 0xef,0x92,//90
+
+ ////,//anog/////////////
+ 0x0a,0x00, //row start
+ 0x0c,0x00, //col start
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06, //Window setting
+ 0x10,0x58,
+ 0x17,0x14, //[0]mirror [1]flip
+ 0x18,0x0a, //0a 2012.10.26
+ 0x19,0x0a, //AD pipe number
+ 0x1a,0x01, //CISCTL mode4
+ 0x1b,0x8b,
+ 0x1c,0x05,//add by lanking 20130403
+ 0x1e,0x88, //analog mode1 [7] tx-high en [5:3]COL_bias
+ 0x1f,0x08, //[3] tx-low en//
+ 0x20,0x05, //[0]adclk mode},{0x[1]rowclk_MODE [2]rsthigh_en
+ 0x21,0x0f, //[6:4]rsg
+ 0x22,0xf0, //[3:0]vref 20130403
+ 0x23,0xc3, //f3//ADC_r
+ 0x24,0x17, //pad drive 16
+
+
+ 0xfe,0x01,
+ 0x11,0x20,//AEC_out_slope},{0x
+ 0x1f,0xc0,//max_post_gain
+ 0x20,0x48,//max_pre_gain
+ 0x47,0x30,//AEC_outdoor_th
+ 0x0b,0x10,//
+ 0x13,0x78,//y_target 84
+ 0xfe,0x00,
+
+#if 0
+ 0x05,0x01,//hb
+ 0x06,0x11,
+ 0x07,0x00,//vb
+ 0x08,0x50,
+ 0xfe,0x01,
+ 0x27,0x00,//step
+ 0x28,0xa0,
+ 0x29,0x05,//level1
+ 0x2a,0x00,
+ 0x2b,0x05,//level2
+ 0x2c,0x00,
+ 0x2d,0x06,//6e8//level3
+ 0x2e,0xe0,
+ 0x2f,0x0a,//level4
+ 0x30,0x00,
+#endif
+ 0x05,0x01,//hb
+ 0x06,0x5c,
+ 0x07,0x00,//vb
+ 0x08,0x32,
+ 0xfe,0x01,
+ 0x27,0x00,//step
+ 0x28,0x7d,
+ 0x29,0x05,//level1
+ 0x2a,0xdc,
+ 0x2b,0x05,//level2
+ 0x2c,0xdc,
+ 0x2d,0x06,//6e8//level3
+ 0x2e,0x59,
+ 0x2f,0x09,//level4
+ 0x30,0xc4,
+
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xfe,0x00, //0x},{0x},{0x},{0x},{0x
+ 0xb6,0x03, //AEC enable
+ 0xfe,0x00,
+
+ ////,BLK////
+ 0x3f,0x00, //prc close
+ 0x40,0x77,//
+ 0x42,0x7f,
+ 0x43,0x30,
+ 0x5c,0x08,
+ 0x5e,0x20,
+ 0x5f,0x20,
+ 0x60,0x20,
+ 0x61,0x20,
+ 0x62,0x20,
+ 0x63,0x20,
+ 0x64,0x20,
+ 0x65,0x20,
+
+ //blo,/////////
+ 0x80,0xff,
+ 0x81,0x26,//38},{0x//skin_Y 8c_debug
+ 0x87,0x90, //[7]middle gamma
+ 0x84,0x02, //output put foramat
+ 0x86,0x02, //02 //sync plority
+ 0x8b,0xbc,
+ 0xb0,0x80, //globle gain
+ 0xc0,0x40,//Yuv bypass
+
+ ////lsc///////////
+ 0xfe,0x01,
+ 0xc2,0x38,
+ 0xc3,0x25,
+ 0xc4,0x21,
+ 0xc8,0x19,
+ 0xc9,0x12,
+ 0xca,0x0e,
+ 0xbc,0x43,
+ 0xbd,0x18,
+ 0xbe,0x1b,
+ 0xb6,0x40,
+ 0xb7,0x2e,
+ 0xb8,0x26,
+ 0xc5,0x05,
+ 0xc6,0x03,
+ 0xc7,0x04,
+ 0xcb,0x00,
+ 0xcc,0x00,
+ 0xcd,0x00,
+ 0xbf,0x14,
+ 0xc0,0x22,
+ 0xc1,0x1b,
+ 0xb9,0x00,
+ 0xba,0x05,
+ 0xbb,0x05,
+ 0xaa,0x35,
+ 0xab,0x33,
+ 0xac,0x33,
+ 0xad,0x25,
+ 0xae,0x22,
+ 0xaf,0x27,
+ 0xb0,0x1d,
+ 0xb1,0x20,
+ 0xb2,0x22,
+ 0xb3,0x14,
+ 0xb4,0x15,
+ 0xb5,0x16,
+ 0xd0,0x00,
+ 0xd2,0x07,
+ 0xd3,0x08,
+ 0xd8,0x00,
+ 0xda,0x13,
+ 0xdb,0x17,
+ 0xdc,0x00,
+ 0xde,0x0a,
+ 0xdf,0x08,
+ 0xd4,0x00,
+ 0xd6,0x00,
+ 0xd7,0x0c,
+ 0xa4,0x00,
+ 0xa5,0x00,
+ 0xa6,0x00,
+ 0xa7,0x00,
+ 0xa8,0x00,
+ 0xa9,0x00,
+ 0xa1,0x80,
+ 0xa2,0x80,
+
+ ////,/cc////////////
+ 0xfe,0x02,
+ 0xc0,0x01,
+ 0xc1,0x3d, //Green_cc for d//40 ary
+ 0xc2,0xfc,
+ 0xc3,0x05,
+ 0xc4,0xec,
+ 0xc5,0x42,
+ 0xc6,0xf8,
+ 0xc7,0x40,//for cwf
+ 0xc8,0xf8,
+ 0xc9,0x06,
+ 0xca,0xfd,
+ 0xcb,0x3e,
+ 0xcc,0xf3,
+ 0xcd,0x36,//for A
+ 0xce,0xf6,
+ 0xcf,0x04,
+ 0xe3,0x0c,
+ 0xe4,0x44,
+ 0xe5,0xe5,
+ 0xfe,0x00,
+
+ ////,b stt ////////////////AWB ,ar
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x10, // 10
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x20, // 20
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00, // 30
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x40, // 40
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x50, // 50
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x60, // 60
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x70, // 70
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x80, // 80
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x90, // 90
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xa0, // a0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xb0, // b0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xc0, // c0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xd0, // d0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4f,0x01,
+ ////,wb vue////////
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x40,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x53,
+ 0x4e,0x08,
+ 0x4e,0x04,
+ 0x4d,0x62,
+ 0x4e,0x10,
+ 0x4d,0x72,
+ 0x4e,0x20,
+ 0x4f,0x01,
+
+ ///a,///
+ 0xfe,0x01,
+ 0x50,0x88,//c0//[6]green mode
+ 0x52,0x40,
+ 0x54,0x60,
+ 0x56,0x06,
+ 0x57,0x20, //pre adjust
+ 0x58,0x01,
+ 0x5b,0x02, //AWB_gain_delta
+ 0x61,0xaa,//R/G stand
+ 0x62,0xaa,//R/G stand
+ 0x71,0x00,
+ 0x74,0x10, //0x//AWB_C_max
+ 0x77,0x08, // 0x//AWB_p2_x
+ 0x78,0xfd, //AWB_p2_y
+ 0x86,0x30,
+ 0x87,0x00,
+ 0x88,0x04,//06},{0x//[1]dark mode
+ 0x8a,0xc0,//awb move mode
+ 0x89,0x75,
+ 0x84,0x08, //0x//auto_window
+ 0x8b,0x00, // 0x//awb compare luma
+ 0x8d,0x70, //awb gain limit R
+ 0x8e,0x70,//G
+ 0x8f,0xf4,//B
+ 0xfe,0x00,
+ 0x82,0x02,//awb_en
+ ////wb end /////////////
+
+ //===,====de
+ 0xfe,0x01,
+
+ 0x9c,0x04,//00
+
+ 0x21,0xbf,
+ 0xfe,0x02,
+ 0xa4,0x00,//
+ 0xa5,0x40, //lsc_th
+ 0xa2,0xa0, //lsc_dec_slope
+ 0xa6,0x80, //dd_th
+ 0xa7,0x80, //ot_th
+ 0xab,0x31, //
+ 0xa9,0x6f, //
+ 0xb0,0x99, //0x//edge effect slope low
+ 0xb1,0x34,//edge effect slope low
+ 0xb3,0x80, //saturation dec slope
+ 0xde,0xb6, //
+ 0x38,0x0f, //
+ 0x39,0x60, //
+ 0xfe,0x00,
+ 0x81,0x26,
+ 0xfe,0x02,
+ 0x83,0x00,//
+ 0x84,0x45,//
+ ////,///Y//////////
+ 0xd1,0x34,//saturation_cb
+ 0xd2,0x34,//saturation_Cr
+ 0xd3,0x3e,//contrast ? {{0xd4},{0x80}},//contrast center //40 ary
+ 0xd5,0xf6,//luma_offset //f8 ary
+ 0xdc,0x30,
+ 0xdd,0xb8,//edge_sa_g,b
+ 0xfe,0x00,
+ ////,dd/////////
+ 0xfe,0x02,
+ 0x88,0x1f,//dn_b_base//1c ary
+ 0x8c,0xf6, //[2]b_in_dark_inc
+ 0x89,0x03, //dn_c_weight
+ ////,E /////////
+ 0xfe,0x02,
+ 0x90,0x6c,// EEINTP mode1
+ 0x97,0x45,// edge effect
+ //==,=======RGB Gamma
+ 0xfe,0x02,
+ 0x15,0x0a,
+ 0x16,0x12,
+ 0x17,0x19,
+ 0x18,0x1f,
+ 0x19,0x2c,
+ 0x1a,0x38,
+ 0x1b,0x42,
+ 0x1c,0x4e,
+ 0x1d,0x63,
+ 0x1e,0x76,
+ 0x1f,0x87,
+ 0x20,0x96,
+ 0x21,0xa2,
+ 0x22,0xb8,
+ 0x23,0xca,
+ 0x24,0xd8,
+ 0x25,0xe3,
+ 0x26,0xf0,
+ 0x27,0xf8,
+ 0x28,0xfd,
+ 0x29,0xff,
+
+ //===,=========y gamma
+ 0xfe,0x02,
+ 0x2b,0x00,
+ 0x2c,0x04,
+ 0x2d,0x09,
+ 0x2e,0x18,
+ 0x2f,0x27,
+ 0x30,0x37,
+ 0x31,0x49,
+ 0x32,0x5c,
+ 0x33,0x7e,
+ 0x34,0xa0,
+ 0x35,0xc0,
+ 0x36,0xe0,
+ 0x37,0xff,
+ ///1,x120ize//
+ 0xfe,0x00,//
+ 0x90,0x01, //0x//crop enable
+ 0x95,0x04, //0x//1600x1200
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+
+ 0xfe,0x03,
+ 0x42,0x40,
+ 0x43,0x06, //output buf width
+ 0x41,0x02, // Pclk_polarity
+ 0x40,0x40, //00
+ 0x17,0x00, //widv
+ 0xfe,0x00,
+ //ou,t DV////
+
+ 0xfe,0x00,
+ 0x82,0xfe, // fe
+ 0xf2,0x70,
+ 0xf3,0xff,
+ 0xf4,0x00,
+ 0xf5,0x30,
+
+ #if 0
+ //,///ssumple 640X4800//////
+ 0xfe,0x00,
+ 0xfa,0x00,
+ 0xc8,0x00,
+
+ 0x99,0x22,
+ 0x9a,0x07,
+ 0x9b,0x00,
+ 0x9c,0x00,
+ 0x9d,0x00,
+ 0x9e,0x00,
+ 0x9f,0x00,
+ 0xa0,0x00,
+ 0xa1,0x00,
+ 0xa2,0x00,
+
+
+ 0x90,0x01,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+ #endif
+
+
+ 0xfe,0x00,
+ 0x0c,0x00,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+
+ 0xc8,0x15,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x00,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+ 0xb6,0x03,
+
+ 0xad,0x7d,//ary
+ 0xae,0x80,
+ 0xaf,0x80,
+
+#if 1
+ //// re zao///
+ 0xfe ,0x00,
+ 0x22 ,0xf0,
+ 0xfe ,0x01,
+ 0x21 ,0xff,
+ 0xfe ,0x02,
+ 0x8a ,0x33,
+ 0x8c ,0x76,
+ 0x8d ,0x85,
+ 0xa6 ,0xf0,
+ 0xae ,0x9f,
+ 0xa2 ,0x90,
+ 0xa5 ,0x40,
+ 0xa7 ,0x30,
+ 0xb0 ,0x88,
+ 0x38 ,0x0b,
+ 0x39 ,0x30,
+ 0xfe ,0x00,
+ 0x87 ,0xb0,
+
+
+ /*
+ //, sma RGB gamma////
+ 0xfe,0x02,
+ 0x15,0x0b,
+ 0x16,0x0e,
+ 0x17,0x10,
+ 0x18,0x12,
+ 0x19,0x19,
+ 0x1a,0x21,
+ 0x1b,0x29,
+ 0x1c,0x31,
+ 0x1d,0x41,
+ 0x1e,0x50,
+ 0x1f,0x5f,
+ 0x20,0x6d,
+ 0x21,0x79,
+ 0x22,0x91,
+ 0x23,0xa5,
+ 0x24,0xb9,
+ 0x25,0xc9,
+ 0x26,0xe1,
+ 0x27,0xee,
+ 0x28,0xf7,
+ 0x29,0xff,
+ */
+ //da,sun///
+ 0xfe,0x02,
+ 0x40,0x06,
+ 0x41,0x23,
+ 0x42,0x3f,
+ 0x43,0x06,
+ 0x44,0x00,
+ 0x45,0x00,
+ 0x46,0x14,
+ 0x47,0x09,
+
+ #endif
+};
+
+uint8_t gc2035_default_regs_exit[]={
+ 0xfe, 0x00,
+ 0xf1, 0x00,
+ 0xf2, 0x00,
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-8335c.h b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-8335c.h
new file mode 100755
index 00000000..8fe749eb
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-8335c.h
@@ -0,0 +1,899 @@
+#ifndef GC2035_H
+#define GC2035_H
+
+#define GC2035_I2C_ADDR 0x3c
+
+#define gc2035_read_reg(r) wmt_vid_i2c_read(GC2035_I2C_ADDR, r)
+
+#define gc2035_write_reg(r, d) wmt_vid_i2c_write(GC2035_I2C_ADDR, r, d)
+
+#define gc2035_write_array(a) \
+do { \
+ cmos_init_8bit_addr(a, ARRAY_SIZE(a), GC2035_I2C_ADDR); \
+} while (0)
+
+
+// Scene Mode
+unsigned char gc2035_scene_mode_auto[] = {
+0xfe,0x01,
+0x3e,0x40,
+ 0x1f,0xa0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x50,//max_pre_gain 60 2013-7-22 dustin
+0xfe,0x00,
+};
+
+unsigned char gc2035_scene_mode_night[] = {
+0xfe, 0x01,
+0x3e, 0x60,
+ 0x1f,0xe0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x80,//max_pre_gain 60 2013-7-22 dustin
+0xfe, 0x00,
+};
+
+
+// White Balance
+unsigned char gc2035_wb_auto [] = {
+0xfe, 0x00,
+// 0xb3, 0x61, //2013-8-16 dustin
+// 0xb4, 0x40,
+// 0xb5, 0x61,
+0x82, 0xfe,
+};
+
+unsigned char gc2035_wb_incandescent [] = {
+0xfe, 0x00,
+0x82, 0xfc,
+0xb3, 0x50,
+0xb4, 0x40,
+0xb5, 0xa8,
+};
+
+unsigned char gc2035_wb_fluorescent [] = {
+0xfe, 0x00,
+0x82, 0xfc,
+0xb3, 0xa0,
+0xb4, 0x45,
+0xb5, 0x40,
+};
+
+unsigned char gc2035_wb_daylight [] = {
+0xfe, 0x00,
+0x82, 0xfc,
+0xb3, 0x78,
+0xb4, 0x40,
+0xb5, 0x50,
+};
+
+unsigned char gc2035_wb_cloudy [] = {
+0xfe, 0x00,
+0x82, 0xfc,
+0xb3, 0x58,
+0xb4, 0x40,
+0xb5, 0x50,
+};
+
+
+// Exposure
+unsigned char gc2035_exposure_neg6[] = {
+0xfe, 0x01,
+0x13, 0x68,
+0xfe, 0x02,
+0xd5, 0xe0,
+0xfe, 0x00,
+};
+
+unsigned char gc2035_exposure_neg3[] = {
+0xfe, 0x01,
+0x13, 0x70,
+0xfe, 0x02,
+0xd5, 0xf0,
+0xfe, 0x00,
+};
+
+unsigned char gc2035_exposure_zero[] = {
+0xfe, 0x01,
+0x13, 0x78,
+0xfe, 0x02,
+0xd5, 0x00,
+0xfe, 0x00,
+};
+
+unsigned char gc2035_exposure_pos3[] = {
+0xfe, 0x01,
+0x13, 0x80,
+0xfe, 0x02,
+0xd5, 0x10,
+0xfe, 0x00,
+};
+
+unsigned char gc2035_exposure_pos6[] = {
+0xfe, 0x01,
+0x13, 0x88,
+0xfe, 0x02,
+0xd5, 0x20,
+0xfe, 0x00,
+};
+
+
+// Resolution
+unsigned char gc2035_1600x1200_key[]={
+0xfe,0x00,
+0x0c,0x02,
+
+0x0d,0x04,
+0x0e,0xc0,
+0x0f,0x06,
+0x10,0x58,
+
+0x90,0x01,
+0x99,0x11,
+0x95,0x04,
+0x96,0xb0,
+0x97,0x06,
+0x98,0x40,
+0x99,0x11,
+
+0xc8,0x00,
+0xfa,0x11,
+0xfe,0x03,
+0x42,0x80,
+0x43,0x06,
+0x41,0x00,
+0x40,0x00,
+0x17,0x01,
+0xfe,0x00,
+0xc8,0x00,
+};
+
+unsigned char gc2035_640x480_key[]={
+0xfe,0x00,
+0x0c,0x00,
+0x0d,0x04,
+0x0e,0xc0,
+0x0f,0x06,
+0x10,0x58,
+
+0x90,0x01,
+0x99,0x11,
+0x95,0x01,
+0x96,0xe0,
+0x97,0x02,
+0x98,0x80,
+0xc8,0x15,
+0xfa,0x00,
+0xfe,0x03,
+0x42,0x00,
+0x43,0x05,
+0x41,0x02,
+0x40,0x40,
+0x17,0x00,
+0xfe,0x00,
+0xc8,0x55,
+0xb6,0x03,
+};
+
+unsigned char gc2035_640x480[]={
+0xfe,0x80,
+0xfe,0x80,
+0xfe,0x80,
+0xfc,0x06,
+0xf9,0xfe,
+0xfa,0x00,
+0xf6,0x00,
+0xf7,0x17,
+0xf8,0x00,
+0xfe,0x00,
+0x82,0x00,
+0xb3,0x60,
+0xb4,0x40,
+0xb5,0x60,
+0x03,0x05,
+0x04,0x08,
+0xfe,0x00,
+0xec,0x04,
+0xed,0x04,
+0xee,0x60,
+0xef,0x90,
+0x0c,0x00,
+0x0d,0x04,
+0x0e,0xc0,
+0x0f,0x06,
+0x10,0x58,
+0x17,0x14,
+0x18,0x0a,
+0x19,0x0a,
+0x1a,0x01,
+0x1b,0x8b,
+0x1c,0x05,
+0x1e,0x88, //analog mode1 [7] tx-high en [5:3]COL_bias
+0x1f,0x08, //[3] tx-low en//
+0x20,0x05, //[0]adclk mode,0x[1]rowclk_MODE [2]rsthigh_en
+0x21,0x0f, //[6:4]rsg
+0x22,0xd0, //[3:0]vref
+0x23,0xc3, //f3//ADC_r
+0x24,0x17, //pad drive 16
+
+ //AEC
+0xfe,0x01,
+0x11,0x20,//AEC_out_slope,0x
+0x1f,0xc0,//max_post_gain
+0x20,0x60,//max_pre_gain
+0x47,0x30,//AEC_outdoor_th
+0x0b,0x10,//
+0x13,0x75,//y_target
+0xfe,0x00,
+
+
+0x05,0x01,//hb
+0x06,0x11,
+0x07,0x00,//vb
+0x08,0x50,
+0xfe,0x01,
+0x27,0x00,//step
+0x28,0xa0,
+0x29,0x05,//level1
+0x2a,0x00,
+0x2b,0x05,//level2
+0x2c,0x00,
+0x2d,0x06,//6e8//level3
+0x2e,0xe0,
+0x2f,0x0b,//level4
+0x30,0x40,
+0x3e,0x40,
+0xfe,0x00,
+0xb6,0x03,
+0xfe,0x00,
+0x3f,0x00,
+0x40,0x77,
+0x42,0x7f,
+0x43,0x30,
+0x5c,0x08,
+0x5e,0x20,
+0x5f,0x20,
+0x60,0x20,
+0x61,0x20,
+0x62,0x20,
+0x63,0x20,
+0x64,0x20,
+0x65,0x20,
+0x66,0x20,
+0x67,0x20,
+0x68,0x20,
+0x69,0x20,
+0x90,0x01,
+0x95,0x04,
+0x96,0xb0,
+0x97,0x06,
+0x98,0x40,
+0xfe,0x03,
+0x42,0x80,
+0x43,0x06,
+0x41,0x00,
+0x40,0x00,
+0x17,0x01,
+0xfe,0x00,
+0x80,0xff,
+0x81,0x26,
+0x03,0x05,
+0x04,0x2e,
+0x84,0x02,
+0x86,0x02,
+0x87,0x80,
+0x8b,0xbc,
+0xa7,0x80,
+0xa8,0x80,
+0xb0,0x80,
+0xc0,0x40,
+//LSC
+0xfe,0x01,
+0xc2,0x38,
+0xc3,0x25,
+0xc4,0x21,
+0xc8,0x19,
+0xc9,0x12,
+0xca,0x0e,
+0xbc,0x43,
+0xbd,0x18,
+0xbe,0x1b,
+0xb6,0x40,
+0xb7,0x2e,
+0xb8,0x26,
+0xc5,0x05,
+0xc6,0x03,
+0xc7,0x04,
+0xcb,0x00,
+0xcc,0x00,
+0xcd,0x00,
+0xbf,0x14,
+0xc0,0x22,
+0xc1,0x1b,
+0xb9,0x00,
+0xba,0x05,
+0xbb,0x05,
+0xaa,0x35,
+0xab,0x33,
+0xac,0x33,
+0xad,0x25,
+0xae,0x22,
+0xaf,0x27,
+0xb0,0x1d,
+0xb1,0x20,
+0xb2,0x22,
+0xb3,0x14,
+0xb4,0x15,
+0xb5,0x16,
+0xd0,0x00,
+0xd2,0x07,
+0xd3,0x08,
+0xd8,0x00,
+0xda,0x13,
+0xdb,0x17,
+0xdc,0x00,
+0xde,0x0a,
+0xdf,0x08,
+0xd4,0x00,
+0xd6,0x00,
+0xd7,0x0c,
+0xa4,0x00,
+0xa5,0x00,
+0xa6,0x00,
+0xa7,0x00,
+0xa8,0x00,
+0xa9,0x00,
+0xa1,0x80,
+0xa2,0x80,
+
+ //=================================cc
+0xfe,0x02,
+0xc0,0x01,
+0xc1,0x40, //Green_cc for d
+0xc2,0xfc,
+0xc3,0x05,
+0xc4,0xec,
+0xc5,0x42,
+0xc6,0xf8,
+0xc7,0x40,//for cwf
+0xc8,0xf8,
+0xc9,0x06,
+0xca,0xfd,
+0xcb,0x3e,
+0xcc,0xf3,
+0xcd,0x36,//for A
+0xce,0xf6,
+0xcf,0x04,
+0xe3,0x0c,
+0xe4,0x44,
+0xe5,0xe5,
+0xfe,0x00,
+
+//==============================awb
+ //AWB clear
+0xfe,0x01,
+0x4f,0x00,
+0x4d,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4d,0x10, // 10
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4d,0x20, // 20
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4d,0x30,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00, // 30
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4d,0x40, // 40
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4d,0x50, // 50
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4d,0x60, // 60
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4d,0x70, // 70
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4d,0x80, // 80
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4d,0x90, // 90
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4d,0xa0, // a0
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4d,0xb0, // b0
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4d,0xc0, // c0
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4d,0xd0, // d0
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4e,0x00,
+0x4f,0x01,
+ /////// awb value////////
+0xfe,0x01,
+0x4f,0x00,
+0x4d,0x30,
+0x4e,0x00,
+0x4e,0x80,
+0x4e,0x80,
+0x4e,0x02,
+0x4e,0x02,
+0x4d,0x40,
+0x4e,0x00,
+0x4e,0x80,
+0x4e,0x80,
+0x4e,0x02,
+0x4e,0x02,
+0x4e,0x02,
+0x4d,0x53,
+0x4e,0x08,
+0x4e,0x04,
+0x4d,0x62,
+0x4e,0x10,
+0x4d,0x72,
+0x4e,0x20,
+0x4f,0x01,
+
+/////awb////
+0xfe,0x01,
+0x50,0x88,//c0//[6]green mode
+0x52,0x40,
+0x54,0x60,
+0x56,0x06,
+0x57,0x20, //pre adjust
+0x58,0x01,
+0x5b,0x02, //AWB_gain_delta
+0x61,0xaa,//R/G stand
+0x62,0xaa,//R/G stand
+0x71,0x00,
+0x74,0x10,//AWB_C_max
+0x77,0x08, // 0x//AWB_p2_x
+0x78,0xfd, //AWB_p2_y
+0x86,0x30,
+0x87,0x00,
+0x88,0x04,//06,[1]dark mode
+0x8a,0xc0,//awb move mode
+0x89,0x75,
+0x84,0x08,//auto_window
+0x8b,0x00, // 0x//awb compare luma
+0x8d,0x70, //awb gain limit R
+0x8e,0x70,//G
+0x8f,0xf4,//B
+0xfe,0x00,
+0x82,0x02,//awb_en
+
+0xfe,0x01,
+0x21,0xbf,
+0xfe,0x02,
+0xa4,0x00,//
+0xa5,0x40, //lsc_th
+0xa2,0xa0, //lsc_dec_slope
+0xa6,0x80, //dd_th
+0xa7,0x80, //ot_th
+0xab,0x31, //
+0xa9,0x6f, //
+0xb0,0x99, //0x//edge effect slope low
+0xb1,0x34,//edge effect slope low
+0xb3,0x80, //saturation dec slope
+0xde,0xb6, //
+0x38,0x0f, //
+0x39,0x60, //
+0xfe,0x00,
+0x81,0x26,
+0xfe,0x02,
+0x83,0x00,//
+0x84,0x45,//
+ ////////////YCP//////////
+0xd1,0x40,//saturation_cb
+0xd2,0x40,//saturation_Cr
+0xd3,0x40,//contrast ?
+0xd4,0x80,//contrast center
+0xd5,0x00,//luma_offset
+0xdc,0x30,
+0xdd,0xb8,
+0xfe,0x00,
+//gamma
+0xfe,0x02,
+0x88,0x15,//dn_b_base
+0x8c,0xf6, //[2]b_in_dark_inc
+0x89,0x03, //dn_c_weight
+ ////////EE ///////////
+0xfe,0x02,
+0x90,0x6c,// EEINTP mode1
+0x97,0x45,// edge effect
+ ////==============RGB Gamma
+0xfe,0x02,
+0x15,0x0a,
+0x16,0x12,
+0x17,0x19,
+0x18,0x1f,
+0x19,0x2c,
+0x1a,0x38,
+0x1b,0x42,
+0x1c,0x4e,
+0x1d,0x63,
+0x1e,0x76,
+0x1f,0x87,
+0x20,0x96,
+0x21,0xa2,
+0x22,0xb8,
+0x23,0xca,
+0x24,0xd8,
+0x25,0xe3,
+0x26,0xf0,
+0x27,0xf8,
+0x28,0xfd,
+0x29,0xff,
+
+ ///=================y gamma
+0xfe,0x02,
+0x2b,0x00,
+0x2c,0x04,
+0x2d,0x09,
+0x2e,0x18,
+0x2f,0x27,
+0x30,0x37,
+0x31,0x49,
+0x32,0x5c,
+0x33,0x7e,
+0x34,0xa0,
+0x35,0xc0,
+0x36,0xe0,
+0x37,0xff,
+0xfe,0x00,
+
+0xfe,0x00,
+0x82,0xfe,
+0xf2,0x70,
+0xf3,0xff,
+0xf4,0x00,
+0xf5,0x30,
+
+0xfe,0x00,
+0x90,0x01,
+0x95,0x01,
+0x96,0xe0,
+0x97,0x02,
+0x98,0x80,
+0xc8,0x14,
+0xf7,0x11,
+0xf8,0x85,
+0xfa,0x00,
+0x05,0x01,
+0x06,0x0a,
+0x07,0x00,
+0x08,0x58,
+0xfe,0x01,
+0x27,0x00,
+0x28,0xa1,
+0x29,0x05,
+0x2a,0x08,
+0x2b,0x05,
+0x2c,0x08,
+0x2d,0x06,
+0x2e,0xeb,
+0x3e,0x40,
+0xfe,0x03,
+0x42,0x04,
+0x43,0x05,
+0x41,0x02,
+0x40,0x40,
+0x17,0x00,
+0xfe,0x00,
+0xc8,0x55,
+};
+
+unsigned char gc2035_1280x720[]={
+0xfe,0x00,
+0x0a,0xf0,
+0x0c,0xa0,
+0x0d,0x02,
+0x0e,0xd8,
+0x0f,0x05,
+0x10,0x18,
+0xfe,0x00,
+0xec,0x04,
+0xed,0x04,
+0xee,0x50,
+0xef,0x58,
+0x05,0x01,
+0x06,0xaa,
+0x07,0x00,
+0x08,0xf6,
+0xfe,0x01,
+0x27,0x00,
+0x28,0xa1,
+0x29,0x03,
+0x2a,0xc6,
+0x2b,0x04,
+0x2c,0x67,
+0x2d,0x06,
+0x2e,0x4a,
+0x2f,0x07,
+0x30,0x8c,
+0x3e,0x40,
+0xfe,0x00,
+0xb6,0x03,
+0xec,0x04,
+0xed,0x04,
+0xee,0x50,
+0xef,0x58,
+0x99,0x11,
+0x90,0x01,
+0x95,0x02,
+0x96,0xd0,
+0x97,0x05,
+0x98,0x00,
+0xfe,0x03,
+0x42,0x80,
+0x43,0x06,
+0x41,0x00,
+0x40,0x00,
+0x17,0x01,
+0xfe,0x00,
+0xc8,0x00,
+0xfa,0x11,
+};
+
+unsigned char gc2035_320x240[]={
+0xfe,0x00,
+
+
+0x99,0x22,
+0x9b,0x00,
+0x9f,0x00,
+
+
+0x90,0x01,
+
+0x95,0x00,
+0x96,0xf2,//600
+0x97,0x01,
+0x98,0x40,//800
+
+0xc8,0x15,
+0xf7,0x11,
+0xfe,0x03,
+0x42,0x80,
+0x43,0x02,
+0x41,0x02,
+0x40,0x40,
+0x17,0x00,
+0xfe,0x00,
+0xc8,0x54,
+};
+
+unsigned char gc2035_176x144[]={
+0xfe,0x00,
+
+
+0x99,0x33,
+0x9b,0x00,
+0x9f,0x00,
+0x90,0x01,
+
+
+
+0x95,0x00,
+0x96,0x92,//600
+0x97,0x00,
+0x98,0xb0,//800
+
+
+
+
+0xc8,0x15,
+0xf7,0x11,
+
+
+0xfe,0x03,
+0x42,0x60,
+0x43,0x01,
+0x41,0x02,
+0x40,0x40,
+0x17,0x00,
+0xfe,0x00,
+0xc8,0x54,
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-forfun-v91.h b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-forfun-v91.h
new file mode 100755
index 00000000..42e399ad
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-forfun-v91.h
@@ -0,0 +1,1105 @@
+#ifndef GC2035_H
+#define GC2035_H
+
+
+// Scene Mode
+uint8_t gc2035_scene_mode_auto[] = {
+ 0xfe,0x01,
+ 0x3e,0x40,
+ 0x1f,0xa0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x50,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe,0x00,
+};
+
+uint8_t gc2035_scene_mode_night[] = {
+ 0xfe, 0x01,
+ 0x3e, 0x60,
+ 0x1f,0xe0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x80,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe, 0x00,
+};
+
+
+// White Balance
+uint8_t gc2035_wb_auto [] = {
+ 0xfe, 0x00,
+// 0xb3, 0x61, //2013-8-16 dustin
+// 0xb4, 0x40,
+// 0xb5, 0x61,
+ 0x82, 0xfe,
+};
+
+uint8_t gc2035_wb_incandescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0xa8,
+};
+
+uint8_t gc2035_wb_fluorescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0x78,
+};
+
+uint8_t gc2035_wb_daylight [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x78,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+uint8_t gc2035_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x58,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+
+// Exposure
+uint8_t gc2035_exposure_neg6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x68,
+ 0xfe, 0x02,
+ 0xd5, 0xe0,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc2035_exposure_neg3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x70,
+ 0xfe, 0x02,
+ 0xd5, 0xf0,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc2035_exposure_zero[] = {
+ 0xfe, 0x01,
+ 0x13, 0x78,
+ 0xfe, 0x02,
+ 0xd5, 0xf8,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_pos3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x80,
+ 0xfe, 0x02,
+ 0xd5, 0x10,
+ 0xfe, 0x00,
+
+
+};
+
+uint8_t gc2035_exposure_pos6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x88,
+ 0xfe, 0x02,
+ 0xd5, 0x20,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_antibanding_auto[] = {
+ 0xfe, 0x00,
+ 0x05, 0x01,
+ 0x06, 0x39,
+ 0x07, 0x00,
+ 0x08, 0x14,
+ 0xfe, 0x01,
+ 0x27, 0x00,
+ 0x28, 0x8b,
+ 0x29, 0x04,
+ 0x2a, 0x58,
+ 0x2b, 0x04,
+ 0x2c, 0x58,
+ 0x2d, 0x06,
+ 0x2e, 0x84,
+ 0x2f, 0x08,
+ 0x30, 0xb0,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_antibanding_50hz[] = {
+ 0xfe , 0x00,
+ 0x05 , 0x01,// HB
+ 0x06 , 0x23,
+ 0x07 , 0x00,// VB
+ 0x08 , 0x14,
+ 0xfe , 0x01,
+ 0x27 , 0x00,// step
+ 0x28 , 0xaa,
+ 0x29 , 0x04,// level 0 13.75
+ 0x2a , 0xa6,
+ 0x2b , 0x04,// level 1 13.75
+ 0x2c , 0xa6,
+ 0x2d , 0x06,// level 2 10
+ 0x2e , 0xa4,
+ 0x2f , 0x08,// level 3 7.5
+ 0x30 , 0xa2,
+ 0xfe , 0x00,
+
+
+};
+
+uint8_t gc2035_antibanding_60hz[] = {
+ 0xfe, 0x00,
+ 0x05, 0x01,
+ 0x06, 0x39,
+ 0x07, 0x00,
+ 0x08, 0x14,
+ 0xfe, 0x01,
+ 0x27, 0x00,
+ 0x28, 0x8b,
+ 0x29, 0x04,
+ 0x2a, 0x58,
+ 0x2b, 0x04,
+ 0x2c, 0x58,
+ 0x2d, 0x06,
+ 0x2e, 0x84,
+ 0x2f, 0x08,
+ 0x30, 0xb0,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_antibanding_off[] = {
+ 0xfe , 0x00,
+ 0x05 , 0x01,// HB
+ 0x06 , 0x23,
+ 0x07 , 0x00,// VB
+ 0x08 , 0x14,
+ 0xfe , 0x01,
+ 0x27 , 0x00,// step
+ 0x28 , 0xaa,
+ 0x29 , 0x04,// level 0 13.75
+ 0x2a , 0xa6,
+ 0x2b , 0x04,// level 1 13.75
+ 0x2c , 0xa6,
+ 0x2d , 0x06,// level 2 10
+ 0x2e , 0xa4,
+ 0x2f , 0x08,// level 3 7.5
+ 0x30 , 0xa2,
+ 0xfe , 0x00,
+};
+
+uint8_t gc2035_176_144_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x33,
+ 0x9b,0x00,
+ 0x9f,0x00,
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0x92,//600
+ 0x97,0x00,
+ 0x98,0xb0,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+
+ 0xfe,0x03,
+ 0x42,0x60,
+ 0x43,0x01,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_320_240_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x22,
+ 0x9b,0x00,
+ 0x9f,0x00,
+
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0xf2,//600
+ 0x97,0x01,
+ 0x98,0x40,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x02,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_1280_720_regs[]={
+ 0xfe,0x00,
+ 0x0a,0xf0,
+ 0x0c,0xa0,
+ 0x0d,0x02,
+ 0x0e,0xd8,
+ 0x0f,0x05,
+ 0x10,0x18,
+ 0xfe,0x00,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x05,0x01,
+ 0x06,0xaa,
+ 0x07,0x00,
+ 0x08,0xf6,
+ 0xfe,0x01,
+ 0x27,0x00,
+ 0x28,0xa1,
+ 0x29,0x03,
+ 0x2a,0xc6,
+ 0x2b,0x04,
+ 0x2c,0x67,
+ 0x2d,0x06,
+ 0x2e,0x4a,
+ 0x2f,0x07,
+ 0x30,0x8c,
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xb6,0x03,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x99,0x11,
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0xd0,
+ 0x97,0x05,
+ 0x98,0x00,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+ 0xfa,0x11,
+};
+
+uint8_t gc2035_1600_1200_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x02,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x04,
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+
+ 0xc8,0x00,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+};
+
+uint8_t gc2035_800_600_regs[]={
+ 0xfe,0x00,
+ 0xfa,0x00,
+ 0xc8,0x00,
+
+ 0x99,0x22,
+ 0x9a,0x07,
+ 0x9b,0x00,
+ 0x9c,0x00,
+ 0x9d,0x00,
+ 0x9e,0x00,
+ 0x9f,0x00,
+ 0xa0,0x00,
+ 0xa1,0x00,
+ 0xa2,0x00,
+
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0x58,
+ 0x97,0x03,
+ 0x98,0x20,
+};
+
+uint8_t gc2035_640_480_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x00,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+
+ 0xc8,0x15,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x00,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+ 0xb6,0x03,
+};
+
+uint8_t gc2035_default_regs_init[]={
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfc,0x06,
+ 0xf2,0x00,
+ 0xf3,0x00,
+ 0xf4,0x00,
+ 0xf5,0x00,
+ 0xf9,0xfe, //[0] pll enable
+ 0xfa,0x00,
+ 0xf6,0x00,
+ 0xf7,0x15, //pll enable
+
+ 0xf8,0x84,//85
+ 0xfe,0x00,
+ 0x82,0x00,
+ 0xb3,0x60,
+ 0xb4,0x40,
+ 0xb5,0x60,
+
+ 0x03,0x02,
+ 0x04,0x71,
+
+ ////,/meare window ///////////
+ 0xfe,0x00,
+ 0xec,0x06,//04
+ 0xed,0x06,//04
+ 0xee,0x62,//60
+ 0xef,0x92,//90
+
+ ////,//anog/////////////
+ 0x0a,0x00, //row start
+ 0x0c,0x00, //col start
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06, //Window setting
+ 0x10,0x58,
+ 0x17,0x14, //[0]mirror [1]flip
+ 0x18,0x0a, //0a 2012.10.26
+ 0x19,0x0a, //AD pipe number
+ 0x1a,0x01, //CISCTL mode4
+ 0x1b,0x8b,
+ 0x1c,0x05,//add by lanking 20130403
+ 0x1e,0x88, //analog mode1 [7] tx-high en [5:3]COL_bias
+ 0x1f,0x08, //[3] tx-low en//
+ 0x20,0x05, //[0]adclk mode},{0x[1]rowclk_MODE [2]rsthigh_en
+ 0x21,0x0f, //[6:4]rsg
+ 0x22,0xf0, //[3:0]vref 20130403
+ 0x23,0xc3, //f3//ADC_r
+ 0x24,0x17, //pad drive 16
+
+
+ 0xfe,0x01,
+ 0x11,0x20,//AEC_out_slope},{0x
+ 0x1f,0xc0,//max_post_gain
+ 0x20,0x48,//max_pre_gain
+ 0x47,0x30,//AEC_outdoor_th
+ 0x0b,0x10,//
+ 0x13,0x78,//y_target 84
+ 0xfe,0x00,
+
+#if 0
+ 0x05,0x01,//hb
+ 0x06,0x11,
+ 0x07,0x00,//vb
+ 0x08,0x50,
+ 0xfe,0x01,
+ 0x27,0x00,//step
+ 0x28,0xa0,
+ 0x29,0x05,//level1
+ 0x2a,0x00,
+ 0x2b,0x05,//level2
+ 0x2c,0x00,
+ 0x2d,0x06,//6e8//level3
+ 0x2e,0xe0,
+ 0x2f,0x0a,//level4
+ 0x30,0x00,
+#endif
+ 0x05,0x01,//hb
+ 0x06,0x5c,
+ 0x07,0x00,//vb
+ 0x08,0x32,
+ 0xfe,0x01,
+ 0x27,0x00,//step
+ 0x28,0x7d,
+ 0x29,0x04,//level1
+ 0x2a,0xe2,
+ 0x2b,0x05,//level2
+ 0x2c,0xdc,
+ 0x2d,0x05,//6e8//level3
+ 0x2e,0xdc,
+ 0x2f,0x09,//level4
+ 0x30,0xc4,
+
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xfe,0x00, //0x},{0x},{0x},{0x},{0x
+ 0xb6,0x03, //AEC enable
+ 0xfe,0x00,
+
+ ////,BLK////
+ 0x3f,0x00, //prc close
+ 0x40,0x77,//
+ 0x42,0x7f,
+ 0x43,0x30,
+ 0x5c,0x08,
+ 0x5e,0x20,
+ 0x5f,0x20,
+ 0x60,0x20,
+ 0x61,0x20,
+ 0x62,0x20,
+ 0x63,0x20,
+ 0x64,0x20,
+ 0x65,0x20,
+
+ //blo,/////////
+ 0x80,0xff,
+ 0x81,0x26,//38},{0x//skin_Y 8c_debug
+ 0x87,0x90, //[7]middle gamma
+ 0x84,0x02, //output put foramat
+ 0x86,0x02, //02 //sync plority
+ 0x8b,0xbc,
+ 0xb0,0x80, //globle gain
+ 0xc0,0x40,//Yuv bypass
+
+ ////lsc///////////
+ 0xfe,0x01,
+ 0xc2,0x38,
+ 0xc3,0x25,
+ 0xc4,0x21,
+ 0xc8,0x19,
+ 0xc9,0x12,
+ 0xca,0x0e,
+ 0xbc,0x43,
+ 0xbd,0x18,
+ 0xbe,0x1b,
+ 0xb6,0x40,
+ 0xb7,0x2e,
+ 0xb8,0x26,
+ 0xc5,0x05,
+ 0xc6,0x03,
+ 0xc7,0x04,
+ 0xcb,0x00,
+ 0xcc,0x00,
+ 0xcd,0x00,
+ 0xbf,0x14,
+ 0xc0,0x22,
+ 0xc1,0x1b,
+ 0xb9,0x00,
+ 0xba,0x05,
+ 0xbb,0x05,
+ 0xaa,0x35,
+ 0xab,0x33,
+ 0xac,0x33,
+ 0xad,0x25,
+ 0xae,0x22,
+ 0xaf,0x27,
+ 0xb0,0x1d,
+ 0xb1,0x20,
+ 0xb2,0x22,
+ 0xb3,0x14,
+ 0xb4,0x15,
+ 0xb5,0x16,
+ 0xd0,0x00,
+ 0xd2,0x07,
+ 0xd3,0x08,
+ 0xd8,0x00,
+ 0xda,0x13,
+ 0xdb,0x17,
+ 0xdc,0x00,
+ 0xde,0x0a,
+ 0xdf,0x08,
+ 0xd4,0x00,
+ 0xd6,0x00,
+ 0xd7,0x0c,
+ 0xa4,0x00,
+ 0xa5,0x00,
+ 0xa6,0x00,
+ 0xa7,0x00,
+ 0xa8,0x00,
+ 0xa9,0x00,
+ 0xa1,0x80,
+ 0xa2,0x80,
+
+ ////,/cc////////////
+ 0xfe,0x02,
+ 0xc0,0x01,
+ 0xc1,0x40, //Green_cc for d
+ 0xc2,0xfc,
+ 0xc3,0x05,
+ 0xc4,0xec,
+ 0xc5,0x42,
+ 0xc6,0xf8,
+ 0xc7,0x40,//for cwf
+ 0xc8,0xf8,
+ 0xc9,0x06,
+ 0xca,0xfd,
+ 0xcb,0x3e,
+ 0xcc,0xf3,
+ 0xcd,0x36,//for A
+ 0xce,0xf6,
+ 0xcf,0x04,
+ 0xe3,0x0c,
+ 0xe4,0x44,
+ 0xe5,0xe5,
+ 0xfe,0x00,
+
+ ////,b stt ////////////////AWB ,ar
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x10, // 10
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x20, // 20
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00, // 30
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x40, // 40
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x50, // 50
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x60, // 60
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x70, // 70
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x80, // 80
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x90, // 90
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xa0, // a0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xb0, // b0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xc0, // c0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xd0, // d0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4f,0x01,
+ ////,wb vue////////
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x40,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x53,
+ 0x4e,0x08,
+ 0x4e,0x04,
+ 0x4d,0x62,
+ 0x4e,0x10,
+ 0x4d,0x72,
+ 0x4e,0x20,
+ 0x4f,0x01,
+
+ ///a,///
+ 0xfe,0x01,
+ 0x50,0x88,//c0//[6]green mode
+ 0x52,0x40,
+ 0x54,0x60,
+ 0x56,0x06,
+ 0x57,0x20, //pre adjust
+ 0x58,0x01,
+ 0x5b,0x02, //AWB_gain_delta
+ 0x61,0xaa,//R/G stand
+ 0x62,0xaa,//R/G stand
+ 0x71,0x00,
+ 0x74,0x10, //0x//AWB_C_max
+ 0x77,0x08, // 0x//AWB_p2_x
+ 0x78,0xfd, //AWB_p2_y
+ 0x86,0x30,
+ 0x87,0x00,
+ 0x88,0x04,//06},{0x//[1]dark mode
+ 0x8a,0xc0,//awb move mode
+ 0x89,0x75,
+ 0x84,0x08, //0x//auto_window
+ 0x8b,0x00, // 0x//awb compare luma
+ 0x8d,0x70, //awb gain limit R
+ 0x8e,0x70,//G
+ 0x8f,0xf4,//B
+ 0xfe,0x00,
+ 0x82,0x02,//awb_en
+ ////wb end /////////////
+
+ //===,====de
+ 0xfe,0x01,
+
+ 0x9c,0x04,//00
+
+ 0x21,0xbf,
+ 0xfe,0x02,
+ 0xa4,0x00,//
+ 0xa5,0x40, //lsc_th
+ 0xa2,0xa0, //lsc_dec_slope
+ 0xa6,0x80, //dd_th
+ 0xa7,0x80, //ot_th
+ 0xab,0x31, //
+ 0xa9,0x6f, //
+ 0xb0,0x99, //0x//edge effect slope low
+ 0xb1,0x34,//edge effect slope low
+ 0xb3,0x80, //saturation dec slope
+ 0xde,0xb6, //
+ 0x38,0x0f, //
+ 0x39,0x60, //
+ 0xfe,0x00,
+ 0x81,0x26,
+ 0xfe,0x02,
+ 0x83,0x00,//
+ 0x84,0x45,//
+ ////,///Y//////////
+ 0xd1,0x40,//saturation_cb//34 cyrille
+ 0xd2,0x40,//saturation_Cr//34 cyrille
+ 0xd3,0x44,//contrast ? //40 cyrille
+ 0xd5,0xf8,//luma_offset
+ 0xdc,0x30,
+ 0xdd,0xb8,//edge_sa_g,b
+ 0xfe,0x00,
+ ////,dd/////////
+ 0xfe,0x02,
+ 0x88,0x1c,//dn_b_base
+ 0x8c,0xf6, //[2]b_in_dark_inc
+ 0x89,0x03, //dn_c_weight
+ ////,E /////////
+ 0xfe,0x02,
+ 0x90,0x6c,// EEINTP mode1
+ 0x97,0x67,// edge effect//45 cyrille
+ //==,=======RGB Gamma
+ 0xfe,0x02,
+ 0x15,0x0a,
+ 0x16,0x12,
+ 0x17,0x19,
+ 0x18,0x1f,
+ 0x19,0x2c,
+ 0x1a,0x38,
+ 0x1b,0x42,
+ 0x1c,0x4e,
+ 0x1d,0x63,
+ 0x1e,0x76,
+ 0x1f,0x87,
+ 0x20,0x96,
+ 0x21,0xa2,
+ 0x22,0xb8,
+ 0x23,0xca,
+ 0x24,0xd8,
+ 0x25,0xe3,
+ 0x26,0xf0,
+ 0x27,0xf8,
+ 0x28,0xfd,
+ 0x29,0xff,
+
+ //===,=========y gamma
+ 0xfe,0x02,
+ 0x2b,0x00,
+ 0x2c,0x04,
+ 0x2d,0x09,
+ 0x2e,0x18,
+ 0x2f,0x27,
+ 0x30,0x37,
+ 0x31,0x49,
+ 0x32,0x5c,
+ 0x33,0x7e,
+ 0x34,0xa0,
+ 0x35,0xc0,
+ 0x36,0xe0,
+ 0x37,0xff,
+ ///1,x120ize//
+ 0xfe,0x00,//
+ 0x90,0x01, //0x//crop enable
+ 0x95,0x04, //0x//1600x1200
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+
+ 0xfe,0x03,
+ 0x42,0x40,
+ 0x43,0x06, //output buf width
+ 0x41,0x02, // Pclk_polarity
+ 0x40,0x40, //00
+ 0x17,0x00, //widv
+ 0xfe,0x00,
+ //ou,t DV////
+
+ 0xfe,0x00,
+ 0x82,0xfe, // fe
+ 0xf2,0x70,
+ 0xf3,0xff,
+ 0xf4,0x00,
+ 0xf5,0x30,
+
+ #if 0
+ //,///ssumple 640X4800//////
+ 0xfe,0x00,
+ 0xfa,0x00,
+ 0xc8,0x00,
+
+ 0x99,0x22,
+ 0x9a,0x07,
+ 0x9b,0x00,
+ 0x9c,0x00,
+ 0x9d,0x00,
+ 0x9e,0x00,
+ 0x9f,0x00,
+ 0xa0,0x00,
+ 0xa1,0x00,
+ 0xa2,0x00,
+
+
+ 0x90,0x01,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+ #endif
+
+
+ 0xfe,0x00,
+ 0x0c,0x00,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+
+ 0xc8,0x15,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x00,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+ 0xb6,0x03,
+
+#if 1
+ //// re zao///
+ 0xfe ,0x00,
+ 0x22 ,0xf0,
+ 0xfe ,0x01,
+ 0x21 ,0xff,
+ 0xfe ,0x02,
+ 0x8a ,0x33,
+ 0x8c ,0x76,
+ 0x8d ,0x85,
+ 0xa6 ,0xf0,
+ 0xae ,0x9f,
+ 0xa2 ,0x90,
+ 0xa5 ,0x40,
+ 0xa7 ,0x30,
+ 0xb0 ,0x88,
+ 0x38 ,0x0b,
+ 0x39 ,0x30,
+ 0xfe ,0x00,
+ 0x87 ,0xb0,
+
+
+ /*
+ //, sma RGB gamma////
+ 0xfe,0x02,
+ 0x15,0x0b,
+ 0x16,0x0e,
+ 0x17,0x10,
+ 0x18,0x12,
+ 0x19,0x19,
+ 0x1a,0x21,
+ 0x1b,0x29,
+ 0x1c,0x31,
+ 0x1d,0x41,
+ 0x1e,0x50,
+ 0x1f,0x5f,
+ 0x20,0x6d,
+ 0x21,0x79,
+ 0x22,0x91,
+ 0x23,0xa5,
+ 0x24,0xb9,
+ 0x25,0xc9,
+ 0x26,0xe1,
+ 0x27,0xee,
+ 0x28,0xf7,
+ 0x29,0xff,
+ */
+ //da,sun///
+ 0xfe,0x02,
+ 0x40,0x06,
+ 0x41,0x23,
+ 0x42,0x3f,
+ 0x43,0x06,
+ 0x44,0x00,
+ 0x45,0x00,
+ 0x46,0x14,
+ 0x47,0x09,
+
+ #endif
+};
+
+uint8_t gc2035_default_regs_exit[]={
+ 0xfe, 0x00,
+ 0xf1, 0x00,
+ 0xf2, 0x00,
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-htr_t75v.h b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-htr_t75v.h
new file mode 100755
index 00000000..0a01437f
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-htr_t75v.h
@@ -0,0 +1,1040 @@
+#ifndef GC2035_H
+#define GC2035_H
+
+
+// Scene Mode
+uint8_t gc2035_scene_mode_auto[] = {
+ 0xfe,0x01,
+ 0x3e,0x40,
+ 0x1f,0xa0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x50,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe,0x00,
+};
+
+uint8_t gc2035_scene_mode_night[] = {
+ 0xfe, 0x01,
+ 0x3e, 0x60,
+ 0x1f,0xe0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x80,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe, 0x00,
+};
+
+
+// White Balance
+uint8_t gc2035_wb_auto [] = {
+ 0xfe, 0x00,
+// 0xb3, 0x61, //2013-8-16 dustin
+// 0xb4, 0x40,
+// 0xb5, 0x61,
+ 0x82, 0xfe,
+};
+
+uint8_t gc2035_wb_incandescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0xa8,
+};
+
+uint8_t gc2035_wb_fluorescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0x78,
+};
+
+uint8_t gc2035_wb_daylight [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x78,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+uint8_t gc2035_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x58,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+
+// Exposure
+uint8_t gc2035_exposure_neg6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x68,
+ 0xfe, 0x02,
+ 0xd5, 0xe0,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc2035_exposure_neg3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x70,
+ 0xfe, 0x02,
+ 0xd5, 0xf0,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc2035_exposure_zero[] = {
+ 0xfe, 0x01,
+ 0x13, 0x78,
+ 0xfe, 0x02,
+ 0xd5, 0xf8,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_pos3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x80,
+ 0xfe, 0x02,
+ 0xd5, 0x10,
+ 0xfe, 0x00,
+
+
+};
+
+uint8_t gc2035_exposure_pos6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x88,
+ 0xfe, 0x02,
+ 0xd5, 0x20,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_176_144_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x33,
+ 0x9b,0x00,
+ 0x9f,0x00,
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0x92,//600
+ 0x97,0x00,
+ 0x98,0xb0,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+
+ 0xfe,0x03,
+ 0x42,0x60,
+ 0x43,0x01,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+ 0xf7,0x15,
+ 0xf8,0x85,
+};
+
+uint8_t gc2035_320_240_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x22,
+ 0x9b,0x00,
+ 0x9f,0x00,
+
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0xf2,//600
+ 0x97,0x01,
+ 0x98,0x40,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x02,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+ 0xf7,0x15,
+ 0xf8,0x85,
+};
+
+uint8_t gc2035_1280_720_regs[]={
+ 0xfe,0x00,
+ 0x0a,0xf0,
+ 0x0c,0xa0,
+ 0x0d,0x02,
+ 0x0e,0xd8,
+ 0x0f,0x05,
+ 0x10,0x18,
+ 0xfe,0x00,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x05,0x01,
+ 0x06,0xaa,
+ 0x07,0x00,
+ 0x08,0xf6,
+ 0xfe,0x01,
+ 0x27,0x00,
+ 0x28,0xa1,
+ 0x29,0x03,
+ 0x2a,0xc6,
+ 0x2b,0x04,
+ 0x2c,0x67,
+ 0x2d,0x06,
+ 0x2e,0x4a,
+ 0x2f,0x07,
+ 0x30,0x8c,
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xb6,0x03,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x99,0x11,
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0xd0,
+ 0x97,0x05,
+ 0x98,0x00,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+ 0xfa,0x11,
+ 0xf7,0x15,
+ 0xf8,0x84,
+};
+
+uint8_t gc2035_1600_1200_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x02,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x04,
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+
+ 0xc8,0x00,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+ 0xf7,0x15,
+ 0xf8,0x85,
+};
+
+uint8_t gc2035_800_600_regs[]={
+ 0xfe,0x00,
+ 0xfa,0x00,
+ 0xc8,0x00,
+
+ 0x99,0x22,
+ 0x9a,0x07,
+ 0x9b,0x00,
+ 0x9c,0x00,
+ 0x9d,0x00,
+ 0x9e,0x00,
+ 0x9f,0x00,
+ 0xa0,0x00,
+ 0xa1,0x00,
+ 0xa2,0x00,
+
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0x58,
+ 0x97,0x03,
+ 0x98,0x20,
+ 0xf7,0x15,
+ 0xf8,0x85,
+};
+
+uint8_t gc2035_640_480_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x00,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+
+ 0xc8,0x15,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x00,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+ 0xb6,0x03,
+ 0xf7,0x15,
+ 0xf8,0x85,
+};
+
+uint8_t gc2035_default_regs_init[]={
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfc,0x06,
+ 0xf2,0x00,
+ 0xf3,0x00,
+ 0xf4,0x00,
+ 0xf5,0x00,
+ 0xf9,0xfe, //[0] pll enable
+ 0xfa,0x00,
+ 0xf6,0x00,
+ 0xf7,0x15, //pll enable
+
+ 0xf8,0x85,//84
+ 0xfe,0x00,
+ 0x82,0x00,
+ 0xb3,0x60,
+ 0xb4,0x40,
+ 0xb5,0x60,
+#if 0 // max fr: 10fps
+ 0x03,0x02,
+ 0x04,0x71,
+#endif
+#if 1 // max fr: 12.5fps
+ 0x03,0x02,
+ 0x04,0x80,
+#endif
+ ////,/meare window ///////////
+ 0xfe,0x00,
+ 0xec,0x06,//04
+ 0xed,0x06,//04
+ 0xee,0x62,//60
+ 0xef,0x92,//90
+
+ ////,//anog/////////////
+ 0x0a,0x00, //row start
+ 0x0c,0x00, //col start
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06, //Window setting
+ 0x10,0x58,
+ 0x17,0x14, //[0]mirror [1]flip
+ 0x18,0x0a, //0a 2012.10.26
+ 0x19,0x0a, //AD pipe number
+ 0x1a,0x01, //CISCTL mode4
+ 0x1b,0x8b,
+ 0x1c,0x05,//add by lanking 20130403
+ 0x1e,0x88, //analog mode1 [7] tx-high en [5:3]COL_bias
+ 0x1f,0x08, //[3] tx-low en//
+ 0x20,0x05, //[0]adclk mode},{0x[1]rowclk_MODE [2]rsthigh_en
+ 0x21,0x0f, //[6:4]rsg
+ 0x22,0xf0, //[3:0]vref 20130403
+ 0x23,0xc3, //f3//ADC_r
+ 0x24,0x17, //pad drive 16
+
+
+ 0xfe,0x01,
+ 0x11,0x20,//AEC_out_slope},{0x
+ 0x1f,0xc0,//max_post_gain
+ 0x20,0x48,//max_pre_gain
+ 0x47,0x30,//AEC_outdoor_th
+ 0x0b,0x10,//
+ 0x13,0x78,//y_target 84
+ 0xfe,0x00,
+
+#if 1 // max fr: 12.5fps
+ 0x05,0x01,//hb
+ 0x06,0x11,
+ 0x07,0x00,//vb
+ 0x08,0x50,
+ 0xfe,0x01,
+ 0x27,0x00,//step
+ 0x28,0xa0,
+ 0x29,0x05,//level1
+ 0x2a,0x00,
+ 0x2b,0x05,//level2
+ 0x2c,0x00,
+ 0x2d,0x06,//6e8//level3
+ 0x2e,0x40,
+ 0x2f,0x0a,//level4
+ 0x30,0x00,
+#endif
+#if 0 // max fr: 10fps
+ 0x05,0x01,//hb
+ 0x06,0x5c,
+ 0x07,0x00,//vb
+ 0x08,0x32,
+ 0xfe,0x01,
+ 0x27,0x00,//step
+ 0x28,0x7d,
+ 0x29,0x04,//level1
+ 0x2a,0xe2,
+ 0x2b,0x05,//level2
+ 0x2c,0xdc,
+ 0x2d,0x05,//6e8//level3
+ 0x2e,0xdc,
+ 0x2f,0x09,//level4
+ 0x30,0xc4,
+#endif
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xfe,0x00, //0x},{0x},{0x},{0x},{0x
+ 0xb6,0x03, //AEC enable
+ 0xfe,0x00,
+
+ ////,BLK////
+ 0x3f,0x00, //prc close
+ 0x40,0x77,//
+ 0x42,0x7f,
+ 0x43,0x30,
+ 0x5c,0x08,
+ 0x5e,0x20,
+ 0x5f,0x20,
+ 0x60,0x20,
+ 0x61,0x20,
+ 0x62,0x20,
+ 0x63,0x20,
+ 0x64,0x20,
+ 0x65,0x20,
+
+ //blo,/////////
+ 0x80,0xff,
+ 0x81,0x26,//38},{0x//skin_Y 8c_debug
+ 0x87,0x90, //[7]middle gamma
+ 0x84,0x02, //output put foramat
+ 0x86,0x02, //02 //sync plority
+ 0x8b,0xbc,
+ 0xb0,0x80, //globle gain
+ 0xc0,0x40,//Yuv bypass
+
+ ////lsc///////////
+ 0xfe,0x01,
+ 0xc2,0x38,
+ 0xc3,0x25,
+ 0xc4,0x21,
+ 0xc8,0x19,
+ 0xc9,0x12,
+ 0xca,0x0e,
+ 0xbc,0x43,
+ 0xbd,0x18,
+ 0xbe,0x1b,
+ 0xb6,0x40,
+ 0xb7,0x2e,
+ 0xb8,0x26,
+ 0xc5,0x05,
+ 0xc6,0x03,
+ 0xc7,0x04,
+ 0xcb,0x00,
+ 0xcc,0x00,
+ 0xcd,0x00,
+ 0xbf,0x14,
+ 0xc0,0x22,
+ 0xc1,0x1b,
+ 0xb9,0x00,
+ 0xba,0x05,
+ 0xbb,0x05,
+ 0xaa,0x35,
+ 0xab,0x33,
+ 0xac,0x33,
+ 0xad,0x25,
+ 0xae,0x22,
+ 0xaf,0x27,
+ 0xb0,0x1d,
+ 0xb1,0x20,
+ 0xb2,0x22,
+ 0xb3,0x14,
+ 0xb4,0x15,
+ 0xb5,0x16,
+ 0xd0,0x00,
+ 0xd2,0x07,
+ 0xd3,0x08,
+ 0xd8,0x00,
+ 0xda,0x13,
+ 0xdb,0x17,
+ 0xdc,0x00,
+ 0xde,0x0a,
+ 0xdf,0x08,
+ 0xd4,0x00,
+ 0xd6,0x00,
+ 0xd7,0x0c,
+ 0xa4,0x00,
+ 0xa5,0x00,
+ 0xa6,0x00,
+ 0xa7,0x00,
+ 0xa8,0x00,
+ 0xa9,0x00,
+ 0xa1,0x80,
+ 0xa2,0x80,
+
+ ////,/cc////////////
+ 0xfe,0x02,
+ 0xc0,0x01,
+ 0xc1,0x40, //Green_cc for d
+ 0xc2,0xfc,
+ 0xc3,0x05,
+ 0xc4,0xec,
+ 0xc5,0x42,
+ 0xc6,0xf8,
+ 0xc7,0x40,//for cwf
+ 0xc8,0xf8,
+ 0xc9,0x06,
+ 0xca,0xfd,
+ 0xcb,0x3e,
+ 0xcc,0xf3,
+ 0xcd,0x36,//for A
+ 0xce,0xf6,
+ 0xcf,0x04,
+ 0xe3,0x0c,
+ 0xe4,0x44,
+ 0xe5,0xe5,
+ 0xfe,0x00,
+
+ ////,b stt ////////////////AWB ,ar
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x10, // 10
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x20, // 20
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00, // 30
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x40, // 40
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x50, // 50
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x60, // 60
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x70, // 70
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x80, // 80
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x90, // 90
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xa0, // a0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xb0, // b0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xc0, // c0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xd0, // d0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4f,0x01,
+ ////,wb vue////////
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x40,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x53,
+ 0x4e,0x08,
+ 0x4e,0x04,
+ 0x4d,0x62,
+ 0x4e,0x10,
+ 0x4d,0x72,
+ 0x4e,0x20,
+ 0x4f,0x01,
+
+ ///a,///
+ 0xfe,0x01,
+ 0x50,0x88,//c0//[6]green mode
+ 0x52,0x40,
+ 0x54,0x60,
+ 0x56,0x06,
+ 0x57,0x20, //pre adjust
+ 0x58,0x01,
+ 0x5b,0x02, //AWB_gain_delta
+ 0x61,0xaa,//R/G stand
+ 0x62,0xaa,//R/G stand
+ 0x71,0x00,
+ 0x74,0x10, //0x//AWB_C_max
+ 0x77,0x08, // 0x//AWB_p2_x
+ 0x78,0xfd, //AWB_p2_y
+ 0x86,0x30,
+ 0x87,0x00,
+ 0x88,0x04,//06},{0x//[1]dark mode
+ 0x8a,0xc0,//awb move mode
+ 0x89,0x75,
+ 0x84,0x08, //0x//auto_window
+ 0x8b,0x00, // 0x//awb compare luma
+ 0x8d,0x70, //awb gain limit R
+ 0x8e,0x70,//G
+ 0x8f,0xf4,//B
+ 0xfe,0x00,
+ 0x82,0x02,//awb_en
+ ////wb end /////////////
+
+ //===,====de
+ 0xfe,0x01,
+
+ 0x9c,0x04,//00
+
+ 0x21,0xbf,
+ 0xfe,0x02,
+ 0xa4,0x00,//
+ 0xa5,0x40, //lsc_th
+ 0xa2,0xa0, //lsc_dec_slope
+ 0xa6,0x80, //dd_th
+ 0xa7,0x80, //ot_th
+ 0xab,0x31, //
+ 0xa9,0x6f, //
+ 0xb0,0x99, //0x//edge effect slope low
+ 0xb1,0x34,//edge effect slope low
+ 0xb3,0x80, //saturation dec slope
+ 0xde,0xb6, //
+ 0x38,0x0f, //
+ 0x39,0x60, //
+ 0xfe,0x00,
+ 0x81,0x26,
+ 0xfe,0x02,
+ 0x83,0x00,//
+ 0x84,0x45,//
+ ////,///Y//////////
+ 0xd1,0x34,//saturation_cb
+ 0xd2,0x34,//saturation_Cr
+ 0xd3,0x40,//contrast ? {{0xd4},{0x80}},//contrast center
+ 0xd5,0xf8,//luma_offset
+ 0xdc,0x30,
+ 0xdd,0xb8,//edge_sa_g,b
+ 0xfe,0x00,
+ ////,dd/////////
+ 0xfe,0x02,
+ 0x88,0x1c,//dn_b_base
+ 0x8c,0xf6, //[2]b_in_dark_inc
+ 0x89,0x03, //dn_c_weight
+ ////,E /////////
+ 0xfe,0x02,
+ 0x90,0x6c,// EEINTP mode1
+ 0x97,0x45,// edge effect
+ //==,=======RGB Gamma
+ 0xfe,0x02,
+ 0x15,0x0a,
+ 0x16,0x12,
+ 0x17,0x19,
+ 0x18,0x1f,
+ 0x19,0x2c,
+ 0x1a,0x38,
+ 0x1b,0x42,
+ 0x1c,0x4e,
+ 0x1d,0x63,
+ 0x1e,0x76,
+ 0x1f,0x87,
+ 0x20,0x96,
+ 0x21,0xa2,
+ 0x22,0xb8,
+ 0x23,0xca,
+ 0x24,0xd8,
+ 0x25,0xe3,
+ 0x26,0xf0,
+ 0x27,0xf8,
+ 0x28,0xfd,
+ 0x29,0xff,
+
+ //===,=========y gamma
+ 0xfe,0x02,
+ 0x2b,0x00,
+ 0x2c,0x04,
+ 0x2d,0x09,
+ 0x2e,0x18,
+ 0x2f,0x27,
+ 0x30,0x37,
+ 0x31,0x49,
+ 0x32,0x5c,
+ 0x33,0x7e,
+ 0x34,0xa0,
+ 0x35,0xc0,
+ 0x36,0xe0,
+ 0x37,0xff,
+ ///1,x120ize//
+ 0xfe,0x00,//
+ 0x90,0x01, //0x//crop enable
+ 0x95,0x04, //0x//1600x1200
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+
+ 0xfe,0x03,
+ 0x42,0x40,
+ 0x43,0x06, //output buf width
+ 0x41,0x02, // Pclk_polarity
+ 0x40,0x40, //00
+ 0x17,0x00, //widv
+ 0xfe,0x00,
+ //ou,t DV////
+
+ 0xfe,0x00,
+ 0x82,0xfe, // fe
+ 0xf2,0x70,
+ 0xf3,0xff,
+ 0xf4,0x00,
+ 0xf5,0x30,
+
+ #if 0
+ //,///ssumple 640X4800//////
+ 0xfe,0x00,
+ 0xfa,0x00,
+ 0xc8,0x00,
+
+ 0x99,0x22,
+ 0x9a,0x07,
+ 0x9b,0x00,
+ 0x9c,0x00,
+ 0x9d,0x00,
+ 0x9e,0x00,
+ 0x9f,0x00,
+ 0xa0,0x00,
+ 0xa1,0x00,
+ 0xa2,0x00,
+
+
+ 0x90,0x01,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+ #endif
+
+
+ 0xfe,0x00,
+ 0x0c,0x00,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+
+ 0xc8,0x15,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x00,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+ 0xb6,0x03,
+
+#if 1
+ //// re zao///
+ 0xfe ,0x00,
+ 0x22 ,0xf0,
+ 0xfe ,0x01,
+ 0x21 ,0xff,
+ 0xfe ,0x02,
+ 0x8a ,0x33,
+ 0x8c ,0x76,
+ 0x8d ,0x85,
+ 0xa6 ,0xf0,
+ 0xae ,0x9f,
+ 0xa2 ,0x90,
+ 0xa5 ,0x40,
+ 0xa7 ,0x30,
+ 0xb0 ,0x88,
+ 0x38 ,0x0b,
+ 0x39 ,0x30,
+ 0xfe ,0x00,
+ 0x87 ,0xb0,
+
+
+ /*
+ //, sma RGB gamma////
+ 0xfe,0x02,
+ 0x15,0x0b,
+ 0x16,0x0e,
+ 0x17,0x10,
+ 0x18,0x12,
+ 0x19,0x19,
+ 0x1a,0x21,
+ 0x1b,0x29,
+ 0x1c,0x31,
+ 0x1d,0x41,
+ 0x1e,0x50,
+ 0x1f,0x5f,
+ 0x20,0x6d,
+ 0x21,0x79,
+ 0x22,0x91,
+ 0x23,0xa5,
+ 0x24,0xb9,
+ 0x25,0xc9,
+ 0x26,0xe1,
+ 0x27,0xee,
+ 0x28,0xf7,
+ 0x29,0xff,
+ */
+ //da,sun///
+ 0xfe,0x02,
+ 0x40,0x06,
+ 0x41,0x23,
+ 0x42,0x3f,
+ 0x43,0x06,
+ 0x44,0x00,
+ 0x45,0x00,
+ 0x46,0x14,
+ 0x47,0x09,
+
+ #endif
+};
+
+uint8_t gc2035_default_regs_exit[]={
+ 0xfe, 0x00,
+ 0xf1, 0x00,
+ 0xf2, 0x00,
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-jmt781.h b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-jmt781.h
new file mode 100755
index 00000000..3a513bf1
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-jmt781.h
@@ -0,0 +1,954 @@
+#ifndef GC2035_H
+#define GC2035_H
+
+
+// Scene Mode
+uint8_t gc2035_scene_mode_auto[] = {
+ 0xfe,0x01,
+ 0x3e,0x40,
+ 0x1f,0xa0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x50,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe,0x00,
+};
+
+uint8_t gc2035_scene_mode_night[] = {
+ 0xfe, 0x01,
+ 0x3e, 0x60,
+ 0x1f,0xe0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x80,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe, 0x00,
+};
+
+
+// White Balance
+uint8_t gc2035_wb_auto [] = {
+ 0xfe, 0x00,
+// 0xb3, 0x61, //2013-8-16 dustin
+// 0xb4, 0x40,
+// 0xb5, 0x61,
+ 0x82, 0xfe,
+};
+
+uint8_t gc2035_wb_incandescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0xa8,
+};
+
+uint8_t gc2035_wb_fluorescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0x78,
+};
+
+uint8_t gc2035_wb_daylight [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x78,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+uint8_t gc2035_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x58,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+
+// Exposure
+uint8_t gc2035_exposure_neg6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x60,
+ 0xfe, 0x02,
+ 0xd5, 0xe0,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_neg3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x78,
+ 0xfe, 0x02,
+ 0xd5, 0xf0,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_zero[] = {
+ 0xfe, 0x01,
+ 0x13, 0x88,
+ 0xfe, 0x02,
+ 0xd5, 0x00,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_pos3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x8a,
+ 0xfe, 0x02,
+ 0xd5, 0x10,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_pos6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x90,
+ 0xfe, 0x02,
+ 0xd5, 0x20,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_176_144_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x33,
+ 0x9b,0x00,
+ 0x9f,0x00,
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0x92,//600
+ 0x97,0x00,
+ 0x98,0xb0,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+
+ 0xfe,0x03,
+ 0x42,0x60,
+ 0x43,0x01,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_320_240_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x22,
+ 0x9b,0x00,
+ 0x9f,0x00,
+
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0xf2,//600
+ 0x97,0x01,
+ 0x98,0x40,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x02,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_1280_720_regs[]={
+ 0xfe,0x00,
+ 0x0a,0xf0,
+ 0x0c,0xa0,
+ 0x0d,0x02,
+ 0x0e,0xd8,
+ 0x0f,0x05,
+ 0x10,0x18,
+ 0xfe,0x00,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x05,0x01,
+ 0x06,0xaa,
+ 0x07,0x00,
+ 0x08,0xf6,
+ 0xfe,0x01,
+ 0x27,0x00,
+ 0x28,0xa1,
+ 0x29,0x03,
+ 0x2a,0xc6,
+ 0x2b,0x04,
+ 0x2c,0x67,
+ 0x2d,0x06,
+ 0x2e,0x4a,
+ 0x2f,0x07,
+ 0x30,0x8c,
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xb6,0x03,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x99,0x11,
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0xd0,
+ 0x97,0x05,
+ 0x98,0x00,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+ 0xfa,0x11,
+};
+
+uint8_t gc2035_1600_1200_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x02,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x04,
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+
+ 0xc8,0x00,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+};
+
+uint8_t gc2035_800_600_regs[]={
+ 0xfe,0x00,
+ 0xfa,0x00,
+ 0xc8,0x00,
+
+ 0x99,0x22,
+ 0x9a,0x07,
+ 0x9b,0x00,
+ 0x9c,0x00,
+ 0x9d,0x00,
+ 0x9e,0x00,
+ 0x9f,0x00,
+ 0xa0,0x00,
+ 0xa1,0x00,
+ 0xa2,0x00,
+
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0x58,
+ 0x97,0x03,
+ 0x98,0x20,
+};
+
+uint8_t gc2035_640_480_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x00,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+
+ 0xc8,0x15,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x00,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+ 0xb6,0x03,
+};
+
+uint8_t gc2035_default_regs_init[]={
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfc,0x06,
+ 0xf9,0xfe,
+ 0xfa,0x00,
+ 0xf6,0x00,
+ 0xf7,0x17,
+ 0xf8,0x00,
+ 0xfe,0x00,
+ 0x82,0x00,
+ //0xb3,0x60,//close manual WB 2013.0723
+ //0xb4,0x40,
+ //0xb5,0x60,
+ 0x03,0x05,
+ 0x04,0x08,
+ 0xfe,0x00,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x60,
+ 0xef,0x90,
+ 0x0c,0x00,
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+ 0x17,0x14,
+ 0x18,0x0a,
+ 0x19,0x0a,
+ 0x1a,0x01,
+ 0x1b,0x8b,
+ 0x1c,0x05,
+ 0x1e,0x88, //analog mode1 [7] tx-high en [5:3]COL_bias
+ 0x1f,0x08, //[3] tx-low en//
+ 0x20,0x05, //[0]adclk mode,0x[1]rowclk_MODE [2]rsthigh_en
+ 0x21,0x0f, //[6:4]rsg
+ 0x22,0xd0, //[3:0]vref
+ 0x23,0xc3, //f3//ADC_r
+ 0x24,0x17, //pad drive 16
+
+ //AEC
+ 0xfe,0x01,
+ 0x0c,0x01,
+ 0x11,0x20,//AEC_out_slope,0x
+ 0x1f,0xa0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x50,//max_pre_gain 60 2013-7-22 dustin
+ 0x47,0x30,//AEC_outdoor_th
+ 0x0b,0x10,//
+ 0x13,0x88,//y_target
+ 0xfe,0x00,
+
+ 0x05,0x01,//hb
+ 0x06,0x11,
+ 0x07,0x00,//vb
+ 0x08,0x50,
+ 0xfe,0x01,
+ 0x27,0x00,//step
+ 0x28,0xa0,
+ 0x29,0x05,//level1
+ 0x2a,0x00,
+ 0x2b,0x05,//level2
+ 0x2c,0x00,
+ 0x2d,0x05,//6e8//level3
+ 0x2e,0x00,
+ 0x2f,0x0b,//level4
+ 0x30,0x40,
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xb6,0x03,
+ 0xfe,0x00,
+ 0x3f,0x00,
+ 0x40,0x77,
+ 0x42,0x7f,
+ 0x43,0x30,
+ 0x5c,0x08,
+ 0x5e,0x20,
+ 0x5f,0x20,
+ 0x60,0x20,
+ 0x61,0x20,
+ 0x62,0x20,
+ 0x63,0x20,
+ 0x64,0x20,
+ 0x65,0x20,
+ 0x66,0x20,
+ 0x67,0x20,
+ 0x68,0x20,
+ 0x69,0x20,
+ 0x90,0x01,
+ 0x95,0x04,
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0x80,0xff,
+ 0x81,0x26,
+ 0x03,0x05,
+ 0x04,0x2e,
+ 0x84,0x02,
+ 0x86,0x02,
+ 0x87,0x80,
+ 0x8b,0xbc,
+ 0xa7,0x80,
+ 0xa8,0x80,
+ 0xb0,0x80,
+ 0xc0,0x40,
+ //LSC
+ 0xfe,0x01,
+ 0xc2,0x38,
+ 0xc3,0x25,
+ 0xc4,0x21,
+ 0xc8,0x19,
+ 0xc9,0x12,
+ 0xca,0x0e,
+ 0xbc,0x43,
+ 0xbd,0x18,
+ 0xbe,0x1b,
+ 0xb6,0x40,
+ 0xb7,0x2e,
+ 0xb8,0x26,
+ 0xc5,0x05,
+ 0xc6,0x03,
+ 0xc7,0x04,
+ 0xcb,0x00,
+ 0xcc,0x00,
+ 0xcd,0x00,
+ 0xbf,0x14,
+ 0xc0,0x22,
+ 0xc1,0x1b,
+ 0xb9,0x00,
+ 0xba,0x05,
+ 0xbb,0x05,
+ 0xaa,0x35,
+ 0xab,0x33,
+ 0xac,0x33,
+ 0xad,0x25,
+ 0xae,0x22,
+ 0xaf,0x27,
+ 0xb0,0x1d,
+ 0xb1,0x20,
+ 0xb2,0x22,
+ 0xb3,0x14,
+ 0xb4,0x15,
+ 0xb5,0x16,
+ 0xd0,0x00,
+ 0xd2,0x07,
+ 0xd3,0x08,
+ 0xd8,0x00,
+ 0xda,0x13,
+ 0xdb,0x17,
+ 0xdc,0x00,
+ 0xde,0x0a,
+ 0xdf,0x08,
+ 0xd4,0x00,
+ 0xd6,0x00,
+ 0xd7,0x0c,
+ 0xa4,0x00,
+ 0xa5,0x00,
+ 0xa6,0x00,
+ 0xa7,0x00,
+ 0xa8,0x00,
+ 0xa9,0x00,
+ 0xa1,0x80,
+ 0xa2,0x80,
+
+ //=================================cc
+ 0xfe,0x02,
+ 0xc0,0x01,
+ 0xc1,0x40, //Green_cc for d
+ 0xc2,0xfc,
+ 0xc3,0x05,
+ 0xc4,0xec,
+ 0xc5,0x42,
+ 0xc6,0xf8,
+ 0xc7,0x40,//for cwf
+ 0xc8,0xf8,
+ 0xc9,0x06,
+ 0xca,0xfd,
+ 0xcb,0x3e,
+ 0xcc,0xf3,
+ 0xcd,0x36,//for A
+ 0xce,0xf6,
+ 0xcf,0x04,
+ 0xe3,0x0c,
+ 0xe4,0x44,
+ 0xe5,0xe5,
+ 0xfe,0x00,
+
+ //==============================awb
+ //AWB clear
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x10, // 10
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x20, // 20
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00, // 30
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x40, // 40
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x50, // 50
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x60, // 60
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x70, // 70
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x80, // 80
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x90, // 90
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xa0, // a0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xb0, // b0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xc0, // c0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xd0, // d0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4f,0x01,
+ /////// awb value////////
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x40,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x53,
+ 0x4e,0x08,
+ 0x4e,0x04,
+ 0x4d,0x62,
+ 0x4e,0x10,
+ 0x4d,0x72,
+ 0x4e,0x20,
+ 0x4f,0x01,
+
+ /////awb////
+ 0xfe,0x01,
+ 0x50,0x88,//c0//[6]green mode
+ 0x52,0x40,
+ 0x54,0x60,
+ 0x56,0x06,
+ 0x57,0x20, //pre adjust
+ 0x58,0x01,
+ 0x5b,0x02, //AWB_gain_delta
+ 0x61,0xaa,//R/G stand
+ 0x62,0xaa,//R/G stand
+ 0x71,0x00,
+ 0x74,0x10,//AWB_C_max
+ 0x77,0x08, // 0x//AWB_p2_x
+ 0x78,0xfd, //AWB_p2_y
+ 0x86,0x30,
+ 0x87,0x00,
+ 0x88,0x04,//06,[1]dark mode
+ 0x8a,0xc0,//awb move mode
+ 0x89,0x75,
+ 0x84,0x08,//auto_window
+ 0x8b,0x00, // 0x//awb compare luma
+ 0x8d,0x70, //awb gain limit R
+ 0x8e,0x70,//G
+ 0x8f,0xf4,//B
+ 0xfe,0x00,
+ 0x82,0x02,//awb_en
+
+ 0xfe,0x01,
+ 0x21,0xbf,
+ 0xfe,0x02,
+ 0xa4,0x00,//
+ 0xa5,0x40, //lsc_th
+ 0xa2,0xa0, //lsc_dec_slope
+ 0xa6,0x80, //dd_th
+ 0xa7,0x80, //ot_th
+ 0xab,0x31, //
+ 0xa9,0x6f, //
+ 0xb0,0x99, //0x//edge effect slope low
+ 0xb1,0x34,//edge effect slope low
+ 0xb3,0x80, //saturation dec slope
+ 0xde,0xb6, //
+ 0x38,0x0f, //
+ 0x39,0x60, //
+ 0xfe,0x00,
+ 0x81,0x24,
+ 0xfe,0x02,
+ 0x83,0x00,//
+ 0x84,0x45,//
+ ////////////YCP//////////
+ 0xd1,0x40,// 40 saturation_cb
+ 0xd2,0x40,//40 saturation_Cr
+
+ 0xaf,0x7e,
+
+
+ 0xd3,0x40,//3ccontrast ? 40 2013-7-22 dustin
+ 0xd4,0x80,//contrast center
+ 0xd5,0xf8,//luma_offset
+ 0xdc,0x30,
+ 0xdd,0xb8,
+ 0xfe,0x00,
+ //gamma
+ 0xfe,0x02,
+ 0x88,0x1f,//dn_b_base 15 2013-7-22 dustin
+ 0x8c,0xf6, //[2]b_in_dark_inc
+ 0x89,0x02, //dn_c_weight 03 2013-7-22 dustin
+ ////////EE ///////////
+ 0xfe,0x02,
+ 0x90,0x6c,// EEINTP mode1
+ 0x97,0x9a,//78 edge effect
+ ////==============RGB Gamma
+ 0xfe,0x02,
+ 0x15,0x0a,
+ 0x16,0x12,
+ 0x17,0x19,
+ 0x18,0x1f,
+ 0x19,0x2c,
+ 0x1a,0x38,
+ 0x1b,0x42,
+ 0x1c,0x4e,
+ 0x1d,0x63,
+ 0x1e,0x76,
+ 0x1f,0x87,
+ 0x20,0x96,
+ 0x21,0xa2,
+ 0x22,0xb8,
+ 0x23,0xca,
+ 0x24,0xd8,
+ 0x25,0xe3,
+ 0x26,0xf0,
+ 0x27,0xf8,
+ 0x28,0xfd,
+ 0x29,0xff,
+
+ ///=================y gamma
+ 0xfe,0x02,
+ 0x2b,0x00,
+ 0x2c,0x04,
+ 0x2d,0x09,
+ 0x2e,0x18,
+ 0x2f,0x27,
+ 0x30,0x37,
+ 0x31,0x49,
+ 0x32,0x5c,
+ 0x33,0x7e,
+ 0x34,0xa0,
+ 0x35,0xc0,
+ 0x36,0xe0,
+ 0x37,0xff,
+ 0xfe,0x00,
+
+ 0xfe,0x00,
+ 0x82,0xfe,
+ 0xf2,0x70,
+ 0xf3,0xff,
+ 0xf4,0x00,
+ 0xf5,0x30,
+
+ 0xfe,0x00,
+ 0x90,0x01,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+ 0xc8,0x14,
+ 0xf7,0x11,
+ 0xf8,0x85,
+ 0xfa,0x00,
+ 0x05,0x01,
+ 0x06,0x0a,
+ 0x07,0x00,
+ 0x08,0x58,
+ 0xfe,0x01,
+ 0x27,0x00,
+ 0x28,0xa1,
+ 0x29,0x05,
+ 0x2a,0x08,
+ 0x2b,0x05,
+ 0x2c,0x08,
+ 0x2d,0x05,
+ 0x2e,0x08,
+
+ 0x2f,0x05,
+ 0x30,0x08,
+
+ 0x3e,0x40,
+ 0xfe,0x03,
+ 0x42,0x04,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+
+
+#if 0
+ //////// re zao///
+ 0xfe,0x00,
+ 0x22,0xd0,
+ 0xfe,0x01,
+ 0x21,0xff,
+ 0xfe,0x02,
+ 0x8a,0x33,
+ 0x8c,0x76,
+ 0x8d,0x85,
+ 0xa6,0xf0,
+ 0xae,0x9f,
+ 0xa2,0x90,
+ 0xa5,0x40,
+ 0xa7,0x30,
+ 0xb0,0x88,
+ 0x38,0x0b,
+ 0x39,0x30,
+ 0xfe,0x00,
+ 0x87,0xb0,
+
+ /// small RGB gamma////
+
+
+ ///dark sun/////
+ 0xfe , 0x02,
+ 0x40 , 0x06,
+ 0x41 , 0x23,
+ 0x42 , 0x3f,
+ 0x43 , 0x06,
+ 0x44 , 0x00,
+ 0x45 , 0x00,
+ 0x46 , 0x14,
+ 0x47 , 0x09,
+ 0xfe,0x00,
+
+ #endif
+};
+
+uint8_t gc2035_default_regs_exit[]={
+ 0xfe, 0x00,
+ 0xf1, 0x00,
+ 0xf2, 0x00,
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-kyt110_v7.h b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-kyt110_v7.h
new file mode 100755
index 00000000..522ad251
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-kyt110_v7.h
@@ -0,0 +1,1042 @@
+#ifndef GC2035_H
+#define GC2035_H
+
+
+// Scene Mode
+uint8_t gc2035_scene_mode_auto[] = {
+ 0xfe,0x01,
+ 0x3e,0x40,
+ 0x1f,0xa0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x50,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe,0x00,
+};
+
+uint8_t gc2035_scene_mode_night[] = {
+ 0xfe, 0x01,
+ 0x3e, 0x60,
+ 0x1f,0xe0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x80,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe, 0x00,
+};
+
+
+// White Balance
+uint8_t gc2035_wb_auto [] = {
+ 0xfe, 0x00,
+// 0xb3, 0x61, //2013-8-16 dustin
+// 0xb4, 0x40,
+// 0xb5, 0x61,
+ 0x82, 0xfe,
+};
+
+uint8_t gc2035_wb_incandescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0xa8,
+};
+
+uint8_t gc2035_wb_fluorescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0xa0,
+ 0xb4, 0x45,
+ 0xb5, 0x40,
+};
+
+uint8_t gc2035_wb_daylight [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x78,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+uint8_t gc2035_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x58,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+
+// Exposure
+uint8_t gc2035_exposure_neg6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x68,
+ 0xfe, 0x02,
+ 0xd5, 0xe0,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_neg3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x70,
+ 0xfe, 0x02,
+ 0xd5, 0xf0,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_zero[] = {
+ 0xfe, 0x01,
+ 0x13, 0x78,
+ 0xfe, 0x02,
+ 0xd5, 0x00,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_pos3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x80,
+ 0xfe, 0x02,
+ 0xd5, 0x10,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_pos6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x88,
+ 0xfe, 0x02,
+ 0xd5, 0x20,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_176_144_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x33,
+ 0x9b,0x00,
+ 0x9f,0x00,
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0x92,//600
+ 0x97,0x00,
+ 0x98,0xb0,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+
+ 0xfe,0x03,
+ 0x42,0x60,
+ 0x43,0x01,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_320_240_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x22,
+ 0x9b,0x00,
+ 0x9f,0x00,
+
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0xf2,//600
+ 0x97,0x01,
+ 0x98,0x40,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x02,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_1280_720_regs[]={
+ 0xfe,0x00,
+ 0x0a,0xf0,
+ 0x0c,0xa0,
+ 0x0d,0x02,
+ 0x0e,0xd8,
+ 0x0f,0x05,
+ 0x10,0x18,
+ 0xfe,0x00,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x05,0x01,
+ 0x06,0xaa,
+ 0x07,0x00,
+ 0x08,0xf6,
+ 0xfe,0x01,
+ 0x27,0x00,
+ 0x28,0xa1,
+ 0x29,0x03,
+ 0x2a,0xc6,
+ 0x2b,0x04,
+ 0x2c,0x67,
+ 0x2d,0x06,
+ 0x2e,0x4a,
+ 0x2f,0x07,
+ 0x30,0x8c,
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xb6,0x03,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x99,0x11,
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0xd0,
+ 0x97,0x05,
+ 0x98,0x00,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+ 0xfa,0x11,
+};
+
+uint8_t gc2035_1600_1200_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x02,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x04,
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+
+ 0xc8,0x00,
+ 0xf7,0x17,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+};
+
+uint8_t gc2035_800_600_regs[]={
+ 0xfe,0x00,
+ 0xfa,0x00,
+ 0xc8,0x00,
+ 0xf7,0x15,
+
+ 0x99,0x22,
+ 0x9a,0x07,
+ 0x9b,0x00,
+ 0x9c,0x00,
+ 0x9d,0x00,
+ 0x9e,0x00,
+ 0x9f,0x00,
+ 0xa0,0x00,
+ 0xa1,0x00,
+ 0xa2,0x00,
+
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0x58,
+ 0x97,0x03,
+ 0x98,0x20,
+};
+
+uint8_t gc2035_640_480_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x00,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+
+ 0xc8,0x15,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x00,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+ 0xb6,0x03,
+};
+
+uint8_t gc2035_default_regs_init[]={
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfc,0x06,
+ 0xf9,0xfe,
+ 0xfa,0x00,
+ 0xf6,0x00,
+#if 0
+ 0xf7,0x17,
+ 0xf8,0x00,
+#endif
+ 0xf7,0x15,
+ 0xf8,0x84,
+ 0xfe,0x00,
+ 0x82,0x00,
+ //0xb3,0x60,//close manual wb 2013.07.23
+ //0xb4,0x40,
+ //0xb5,0x60,
+ 0x03,0x01,
+ 0x04,0xf4,
+ 0xfe,0x00,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x60,
+ 0xef,0x90,
+ 0x0c,0x00,
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+ 0x17,0x14,
+ 0x18,0x0a,
+ 0x19,0x0a,
+ 0x1a,0x01,
+ 0x1b,0x8b,
+ 0x1c,0x05,
+ 0x1e,0x88, //analog mode1 [7] tx-high en [5:3]COL_bias
+ 0x1f,0x08, //[3] tx-low en//
+ 0x20,0x05, //[0]adclk mode,0x[1]rowclk_MODE [2]rsthigh_en
+ 0x21,0x0f, //[6:4]rsg
+ 0x22,0xd0, //[3:0]vref
+ 0x23,0xc3, //f3//ADC_r
+ 0x24,0x17, //pad drive 16
+
+ //AEC
+ 0xfe,0x01,
+ 0x11,0x20,//AEC_out_slope,0x
+ 0x1f,0xc0,//max_post_gain
+ 0x20,0x60,//max_pre_gain
+ 0x47,0x30,//AEC_outdoor_th
+ 0x0b,0x10,//
+ 0x13,0x80,//y_target//75
+ 0xfe,0x00,
+#if 0
+ 0x05,0x01,//hb
+ 0x06,0x11,
+ 0x07,0x00,//vb
+ 0x08,0x50,
+ 0xfe,0x01,
+ 0x27,0x00,//step
+ 0x28,0xa0,
+ 0x29,0x05,//level1
+ 0x2a,0x00,
+ 0x2b,0x05,//level2
+ 0x2c,0x00,
+ 0x2d,0x05,//6e8//level3
+ 0x2e,0x00,
+ 0x2f,0x0a,//level4
+ 0x30,0x00,
+#endif
+ 0x05,0x01,//hb 15c
+ 0x06,0x58,
+ 0x07,0x00,//vb
+ 0x08,0x22,
+ 0xfe,0x01,
+ 0x27,0x00,//step
+ 0x28,0x7d,
+ 0x29,0x04,//level1
+ 0x2a,0xe2,
+ 0x2b,0x04,//level2
+ 0x2c,0xe2,
+ 0x2d,0x05,//6e8//level3
+ 0x2e,0xdc,
+ 0x2f,0x09,//level4
+ 0x30,0xc4,
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xb6,0x03,
+ 0xfe,0x00,
+ 0x3f,0x00,
+ 0x40,0x77,
+ 0x42,0x7f,
+ 0x43,0x30,
+ 0x5c,0x08,
+ 0x5e,0x20,
+ 0x5f,0x20,
+ 0x60,0x20,
+ 0x61,0x20,
+ 0x62,0x20,
+ 0x63,0x20,
+ 0x64,0x20,
+ 0x65,0x20,
+ 0x66,0x20,
+ 0x67,0x20,
+ 0x68,0x20,
+ 0x69,0x20,
+ 0x90,0x01,
+ 0x95,0x04,
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0x80,0xff,
+ 0x81,0x26,
+#if 0
+ 0x03,0x05,//
+ 0x04,0x2e,
+#endif
+ 0x03,0x01,//
+ 0x04,0xf4,
+ 0x84,0x02,
+ 0x86,0x02,
+ 0x87,0x80,
+ 0x8b,0xbc,
+ 0xa7,0x80,
+ 0xa8,0x80,
+ 0xb0,0x80,
+ 0xc0,0x40,
+ //LSC
+ 0xfe,0x01,
+ 0xc2,0x38,
+ 0xc3,0x25,
+ 0xc4,0x21,
+ 0xc8,0x19,
+ 0xc9,0x12,
+ 0xca,0x0e,
+ 0xbc,0x22,// 43
+ 0xbd,0x18,
+ 0xbe,0x1b,
+ 0xb6,0x2e,// 40
+ 0xb7,0x2e,
+ 0xb8,0x26,
+ 0xc5,0x05,
+ 0xc6,0x03,
+ 0xc7,0x04,
+ 0xcb,0x00,
+ 0xcc,0x00,
+ 0xcd,0x00,
+ 0xbf,0x14,
+ 0xc0,0x22,
+ 0xc1,0x1b,
+ 0xb9,0x00,
+ 0xba,0x05,
+ 0xbb,0x05,
+ 0xaa,0x35,
+ 0xab,0x33,
+ 0xac,0x33,
+ 0xad,0x25,
+ 0xae,0x22,
+ 0xaf,0x27,
+ 0xb0,0x1d,
+ 0xb1,0x20,
+ 0xb2,0x22,
+ 0xb3,0x14,
+ 0xb4,0x15,
+ 0xb5,0x16,
+ 0xd0,0x00,
+ 0xd2,0x07,
+ 0xd3,0x08,
+ 0xd8,0x00,
+ 0xda,0x13,
+ 0xdb,0x17,
+ 0xdc,0x00,
+ 0xde,0x0a,
+ 0xdf,0x08,
+ 0xd4,0x00,
+ 0xd6,0x00,
+ 0xd7,0x0c,
+ 0xa4,0x00,
+ 0xa5,0x00,
+ 0xa6,0x00,
+ 0xa7,0x00,
+ 0xa8,0x00,
+ 0xa9,0x00,
+ 0xa1,0x80,
+ 0xa2,0x80,
+
+ //=================================cc
+ 0xfe,0x02,
+ 0xc0,0x01,
+ 0xc1,0x40, //Green_cc for d
+ 0xc2,0xfc,
+ 0xc3,0x05,
+ 0xc4,0xec,
+ 0xc5,0x42,
+ 0xc6,0xf8,
+ 0xc7,0x40,//for cwf
+ 0xc8,0xf8,
+ 0xc9,0x06,
+ 0xca,0xfd,
+ 0xcb,0x3e,
+ 0xcc,0xf3,
+ 0xcd,0x36,//for A
+ 0xce,0xf6,
+ 0xcf,0x04,
+ 0xe3,0x0c,
+ 0xe4,0x44,
+ 0xe5,0xe5,
+ 0xfe,0x00,
+
+ //==============================awb
+ //AWB clear
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x10, // 10
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x20, // 20
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00, // 30
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x40, // 40
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x50, // 50
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x60, // 60
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x70, // 70
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x80, // 80
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x90, // 90
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xa0, // a0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xb0, // b0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xc0, // c0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xd0, // d0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4f,0x01,
+ /////// awb value////////
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x40,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x53,
+ 0x4e,0x00,//08
+ 0x4e,0x04,
+ 0x4d,0x62,
+ 0x4e,0x10,
+ 0x4d,0x72,
+ 0x4e,0x20,
+ 0x4f,0x01,
+
+ /////awb////
+ 0xfe,0x01,
+ 0x50,0x88,//c0//[6]green mode
+ 0x52,0x40,
+ 0x54,0x60,
+ 0x56,0x06,
+ 0x57,0x20, //pre adjust
+ 0x58,0x01,
+ 0x5b,0x02, //AWB_gain_delta
+ 0x61,0xaa,//R/G stand
+ 0x62,0xaa,//R/G stand
+ 0x71,0x00,
+ 0x74,0x10,//AWB_C_max
+ 0x77,0x08, // 0x//AWB_p2_x
+ 0x78,0xfd, //AWB_p2_y
+ 0x86,0x30,
+ 0x87,0x00,
+ 0x88,0x04,//06,[1]dark mode
+ 0x8a,0xc0,//awb move mode
+ 0x89,0x75,
+ 0x84,0x08,//auto_window
+ 0x8b,0x00, // 0x//awb compare luma
+ 0x8d,0x70, //awb gain limit R
+ 0x8e,0x70,//G
+ 0x8f,0xf4,//B
+ 0xfe,0x00,
+ 0x82,0x02,//awb_en
+
+ 0xfe,0x01,
+ 0x21,0xbf,
+ 0xfe,0x02,
+ 0xa4,0x00,//
+ 0xa5,0x40, //lsc_th
+ 0xa2,0xa0, //lsc_dec_slope
+ 0xa6,0x80, //dd_th
+ 0xa7,0x80, //ot_th
+ 0xab,0x31, //
+ 0xa9,0x6f, //
+ 0xb0,0x99, //0x//edge effect slope low
+ 0xb1,0x34,//edge effect slope low
+ 0xb3,0x80, //saturation dec slope
+ 0xde,0xb6, //
+ 0x38,0x0f, //
+ 0x39,0x60, //
+ 0xfe,0x00,
+ 0x81,0x26,
+ 0xfe,0x02,
+ 0x83,0x00,//
+ 0x84,0x45,//
+ ////////////YCP//////////
+ 0xd1,0x38,//saturation_cb//40
+ 0xd2,0x38,//40 saturation_Cr//46
+
+ 0xaf,0x7e,
+
+
+ 0xd3,0x42,//contrast ?
+ 0xd4,0x80,//contrast center
+ 0xd5,0x00,//luma_offset
+ 0xdc,0x30,
+ 0xdd,0xb8,
+ 0xfe,0x00,
+ //gamma
+ 0xfe,0x02,
+ 0x88,0x15,//dn_b_base
+ 0x8c,0xf6, //[2]b_in_dark_inc
+ 0x89,0x03, //dn_c_weight
+ ////////EE ///////////
+ 0xfe,0x02,
+ 0x90,0x6c,// EEINTP mode1
+ 0x97,0x56,// edge effect//78
+ ////==============RGB Gamma
+ 0xfe,0x02,
+ 0x15,0x0a,
+ 0x16,0x12,
+ 0x17,0x19,
+ 0x18,0x1f,
+ 0x19,0x2c,
+ 0x1a,0x38,
+ 0x1b,0x42,
+ 0x1c,0x4e,
+ 0x1d,0x63,
+ 0x1e,0x76,
+ 0x1f,0x87,
+ 0x20,0x96,
+ 0x21,0xa2,
+ 0x22,0xb8,
+ 0x23,0xca,
+ 0x24,0xd8,
+ 0x25,0xe3,
+ 0x26,0xf0,
+ 0x27,0xf8,
+ 0x28,0xfd,
+ 0x29,0xff,
+
+ ///=================y gamma
+ 0xfe,0x02,
+ 0x2b,0x00,
+ 0x2c,0x04,
+ 0x2d,0x09,
+ 0x2e,0x18,
+ 0x2f,0x27,
+ 0x30,0x37,
+ 0x31,0x49,
+ 0x32,0x5c,
+ 0x33,0x7e,
+ 0x34,0xa0,
+ 0x35,0xc0,
+ 0x36,0xe0,
+ 0x37,0xff,
+ 0xfe,0x00,
+
+ 0xfe,0x00,
+ 0x82,0xfe,
+ 0xf2,0x70,
+ 0xf3,0xff,
+ 0xf4,0x00,
+ 0xf5,0x30,
+
+ 0xfe,0x00,
+ 0x90,0x01,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+ 0xc8,0x14,
+#if 0
+ 0xf7,0x11,
+ 0xf8,0x85,
+#endif
+ 0xf7,0x15,
+ 0xfa,0x00,
+ 0x05,0x01,
+ 0x06,0x58,
+ 0x07,0x00,
+ 0x08,0x22,
+ 0xfe,0x01,
+ 0x27,0x00,
+ 0x28,0x7d,
+ 0x29,0x04,
+ 0x2a,0xe2,
+ 0x2b,0x04,
+ 0x2c,0xe2,
+ 0x2d,0x05,
+ 0x2e,0xdc,
+
+ 0x2f,0x09,
+ 0x30,0xc4,
+
+ 0x3e,0x40,
+ 0xfe,0x03,
+ 0x42,0x04,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+
+#if 1
+0xfe,0x00,
+0x22,0xd0,
+0xfe,0x01,
+0x21,0xff,
+0xfe,0x02,
+0x8a,0x33,
+0x8c,0x76,
+0x8d,0x85,
+0xa6,0xf0,
+0xae,0x9f,
+0xa2,0x90,
+0xa5,0x40,
+0xa7,0x30,
+0xb0,0x88,
+0x38,0x0b,
+0x39,0x30,
+
+0xfe,0x00,
+0x87,0xb0,
+
+0xfe,0x02,
+0x40,0x06,
+0x41,0x23,
+0x42,0x3f,
+0x43,0x06,
+0x44,0x00,
+0x45,0x00,
+0x46,0x14,
+0x47,0x09,
+
+
+0xfe,0x00,
+0x18,0x0a,
+0x40,0x73,///use ndark
+0x41,0x0b,///BLK row select
+0x5e,0x14,
+0x5f,0x14,
+0x60,0x14,
+0x61,0x14,
+
+0x62,0x14,
+0x63,0x14,
+0x64,0x14,
+0x65,0x14,
+
+0x66,0x1d,
+0x67,0x1d,
+0x68,0x1d,
+0x69,0x1d,
+#endif
+
+#if 0
+///////////////20130628//////////
+0xfe,0x00,
+0x18,0x0a,
+0x40,0x73,///use ndark
+0x41,0x0b,///BLK row select
+0x5e,0x14,
+0x5f,0x14,
+0x60,0x14,
+0x61,0x14,
+
+0x62,0x14,
+0x63,0x14,
+0x64,0x14,
+0x65,0x14,
+
+0x66,0x1d,
+0x67,0x1d,
+0x68,0x1d,
+0x69,0x1d,
+
+
+/////////RGB gamma/////////
+0xfe,0x02,
+0x15,0x0b,
+0x16,0x0e,
+0x17,0x10,
+0x18,0x12,
+0x19,0x19,
+0x1a,0x21,
+0x1b,0x29,
+0x1c,0x31,
+0x1d,0x41,
+0x1e,0x50,
+0x1f,0x5f,
+0x20,0x6d,
+0x21,0x79,
+0x22,0x91,
+0x23,0xa5,
+0x24,0xb9,
+0x25,0xc9,
+0x26,0xe1,
+0x27,0xee,
+0x28,0xf7,
+0x29,0xff,
+0xfe,0x00,
+#endif
+};
+
+uint8_t gc2035_default_regs_exit[]={
+ 0xfe, 0x00,
+ 0xf1, 0x00,
+ 0xf2, 0x00,
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-mid8-wms8368.h b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-mid8-wms8368.h
new file mode 100755
index 00000000..e75394ac
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-mid8-wms8368.h
@@ -0,0 +1,1025 @@
+#ifndef GC2035_H
+#define GC2035_H
+
+
+// Scene Mode
+uint8_t gc2035_scene_mode_auto[] = {
+ 0xfe,0x01,
+ 0x3e,0x40,
+ 0x1f,0xa0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x50,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe,0x00,
+};
+
+uint8_t gc2035_scene_mode_night[] = {
+ 0xfe, 0x01,
+ 0x3e, 0x60,
+ 0x1f,0xe0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x80,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe, 0x00,
+};
+
+
+// White Balance
+uint8_t gc2035_wb_auto [] = {
+ 0xfe, 0x00,
+// 0xb3, 0x61, //2013-8-16 dustin
+// 0xb4, 0x40,
+// 0xb5, 0x61,
+ 0x82, 0xfe,
+};
+
+uint8_t gc2035_wb_incandescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0xa8,
+};
+
+uint8_t gc2035_wb_fluorescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0x78,
+};
+
+uint8_t gc2035_wb_daylight [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x78,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+uint8_t gc2035_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x58,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+
+// Exposure
+uint8_t gc2035_exposure_neg6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x68,
+ 0xfe, 0x02,
+ 0xd5, 0xe0,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc2035_exposure_neg3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x70,
+ 0xfe, 0x02,
+ 0xd5, 0xf0,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc2035_exposure_zero[] = {
+ 0xfe, 0x01,
+ 0x13, 0x78,// 78 cyrille
+ 0xfe, 0x02,
+ 0xd5, 0xf8,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_pos3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x80,///80 cyrille
+ 0xfe, 0x02,
+ 0xd5, 0x10,
+ 0xfe, 0x00,
+
+
+};
+
+uint8_t gc2035_exposure_pos6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x90,
+ 0xfe, 0x02,
+ 0xd5, 0x20,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_176_144_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x33,
+ 0x9b,0x00,
+ 0x9f,0x00,
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0x92,//600
+ 0x97,0x00,
+ 0x98,0xb0,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+
+ 0xfe,0x03,
+ 0x42,0x60,
+ 0x43,0x01,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_320_240_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x22,
+ 0x9b,0x00,
+ 0x9f,0x00,
+
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0xf2,//600
+ 0x97,0x01,
+ 0x98,0x40,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x02,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_1280_720_regs[]={
+ 0xfe,0x00,
+ 0x0a,0xf0,
+ 0x0c,0xa0,
+ 0x0d,0x02,
+ 0x0e,0xd8,
+ 0x0f,0x05,
+ 0x10,0x18,
+ 0xfe,0x00,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x05,0x01,
+ 0x06,0xaa,
+ 0x07,0x00,
+ 0x08,0xf6,
+ 0xfe,0x01,
+ 0x27,0x00,
+ 0x28,0xa1,
+ 0x29,0x03,
+ 0x2a,0xc6,
+ 0x2b,0x04,
+ 0x2c,0x67,
+ 0x2d,0x06,
+ 0x2e,0x4a,
+ 0x2f,0x07,
+ 0x30,0x8c,
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xb6,0x03,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x99,0x11,
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0xd0,
+ 0x97,0x05,
+ 0x98,0x00,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+ 0xfa,0x11,
+};
+
+uint8_t gc2035_1600_1200_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x02,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x04,
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+
+ 0xc8,0x00,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+};
+
+uint8_t gc2035_800_600_regs[]={
+ 0xfe,0x00,
+ 0xfa,0x00,
+ 0xc8,0x00,
+
+ 0x99,0x22,
+ 0x9a,0x07,
+ 0x9b,0x00,
+ 0x9c,0x00,
+ 0x9d,0x00,
+ 0x9e,0x00,
+ 0x9f,0x00,
+ 0xa0,0x00,
+ 0xa1,0x00,
+ 0xa2,0x00,
+
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0x58,
+ 0x97,0x03,
+ 0x98,0x20,
+};
+
+uint8_t gc2035_640_480_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x00,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+
+ 0xc8,0x15,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x00,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+ 0xb6,0x03,
+};
+
+uint8_t gc2035_default_regs_init[]={
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfc,0x06,
+ 0xf2,0x00,
+ 0xf3,0x00,
+ 0xf4,0x00,
+ 0xf5,0x00,
+ 0xf9,0xfe, //[0] pll enable
+ 0xfa,0x00,
+ 0xf6,0x00,
+ 0xf7,0x15, //pll enable
+
+ 0xf8,0x84,//85
+ 0xfe,0x00,
+ 0x82,0x00,
+ 0xb3,0x60,
+ 0xb4,0x40,
+ 0xb5,0x60,
+
+ 0x03,0x02,
+ 0x04,0x71,
+
+ ////,/meare window ///////////
+ 0xfe,0x00,
+ 0xec,0x06,//04
+ 0xed,0x06,//04
+ 0xee,0x62,//60
+ 0xef,0x92,//90
+
+ ////,//anog/////////////
+ 0x0a,0x00, //row start
+ 0x0c,0x00, //col start
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06, //Window setting
+ 0x10,0x58,
+ 0x17,0x14, //[0]mirror [1]flip
+ 0x18,0x0a, //0a 2012.10.26
+ 0x19,0x0a, //AD pipe number
+ 0x1a,0x01, //CISCTL mode4
+ 0x1b,0x8b,
+ 0x1c,0x05,//add by lanking 20130403
+ 0x1e,0x88, //analog mode1 [7] tx-high en [5:3]COL_bias
+ 0x1f,0x08, //[3] tx-low en//
+ 0x20,0x05, //[0]adclk mode},{0x[1]rowclk_MODE [2]rsthigh_en
+ 0x21,0x0f, //[6:4]rsg
+ 0x22,0xf0, //[3:0]vref 20130403
+ 0x23,0xc3, //f3//ADC_r
+ 0x24,0x17, //pad drive 16
+
+
+ 0xfe,0x01,
+ 0x11,0x20,//AEC_out_slope},{0x
+ 0x1f,0xc0,//max_post_gain
+ 0x20,0x48,//max_pre_gain
+ 0x47,0x30,//AEC_outdoor_th
+ 0x0b,0x10,//
+ 0x0c,0x21,// cyrille
+ 0x13,0x78,//y_target 84//78 cyrille
+ 0xfe,0x00,
+
+#if 0
+ 0x05,0x01,//hb
+ 0x06,0x11,
+ 0x07,0x00,//vb
+ 0x08,0x50,
+ 0xfe,0x01,
+ 0x27,0x00,//step
+ 0x28,0xa0,
+ 0x29,0x05,//level1
+ 0x2a,0x00,
+ 0x2b,0x05,//level2
+ 0x2c,0x00,
+ 0x2d,0x06,//6e8//level3
+ 0x2e,0xe0,
+ 0x2f,0x0a,//level4
+ 0x30,0x00,
+#else
+ 0x05,0x01,//hb
+ 0x06,0x5c,
+ 0x07,0x00,//vb
+ 0x08,0x32,
+ 0xfe,0x01,
+ 0x27,0x00,//step
+ 0x28,0x7d,
+ 0x29,0x04,//level1
+ 0x2a,0xe2,
+ 0x2b,0x05,//level2
+ 0x2c,0xdc,
+ 0x2d,0x06,//6e8//level3
+ 0x2e,0x59,
+ 0x2f,0x09,//level4
+ 0x30,0xc4,
+#endif
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xfe,0x00, //0x},{0x},{0x},{0x},{0x
+ 0xb6,0x03, //AEC enable
+ 0xfe,0x00,
+
+ ////,BLK////
+ 0x3f,0x00, //prc close
+ 0x40,0x77,//
+ 0x42,0x7f,
+ 0x43,0x30,
+ 0x5c,0x08,
+ 0x5e,0x20,
+ 0x5f,0x20,
+ 0x60,0x20,
+ 0x61,0x20,
+ 0x62,0x20,
+ 0x63,0x20,
+ 0x64,0x20,
+ 0x65,0x20,
+
+ //blo,/////////
+ 0x80,0xff,
+ 0x81,0x26,//38},{0x//skin_Y 8c_debug
+ 0x87,0x90, //[7]middle gamma
+ 0x84,0x02, //output put foramat
+ 0x86,0x02, //02 //sync plority
+ 0x8b,0xbc,
+ 0xb0,0x80, //globle gain
+ 0xc0,0x40,//Yuv bypass
+
+ ////lsc///////////
+ 0xfe,0x01,
+ 0xc2,0x38,
+ 0xc3,0x25,
+ 0xc4,0x21,
+ 0xc8,0x19,
+ 0xc9,0x12,
+ 0xca,0x0e,
+ 0xbc,0x43,
+ 0xbd,0x18,
+ 0xbe,0x1b,
+ 0xb6,0x40,
+ 0xb7,0x2e,
+ 0xb8,0x26,
+ 0xc5,0x05,
+ 0xc6,0x03,
+ 0xc7,0x04,
+ 0xcb,0x00,
+ 0xcc,0x00,
+ 0xcd,0x00,
+ 0xbf,0x14,
+ 0xc0,0x22,
+ 0xc1,0x1b,
+ 0xb9,0x00,
+ 0xba,0x05,
+ 0xbb,0x05,
+ 0xaa,0x35,
+ 0xab,0x33,
+ 0xac,0x33,
+ 0xad,0x25,
+ 0xae,0x22,
+ 0xaf,0x27,
+ 0xb0,0x1d,
+ 0xb1,0x20,
+ 0xb2,0x22,
+ 0xb3,0x14,
+ 0xb4,0x15,
+ 0xb5,0x16,
+ 0xd0,0x00,
+ 0xd2,0x07,
+ 0xd3,0x08,
+ 0xd8,0x00,
+ 0xda,0x13,
+ 0xdb,0x17,
+ 0xdc,0x00,
+ 0xde,0x0a,
+ 0xdf,0x08,
+ 0xd4,0x00,
+ 0xd6,0x00,
+ 0xd7,0x0c,
+ 0xa4,0x00,
+ 0xa5,0x00,
+ 0xa6,0x00,
+ 0xa7,0x00,
+ 0xa8,0x00,
+ 0xa9,0x00,
+ 0xa1,0x80,
+ 0xa2,0x80,
+
+ ////,/cc////////////
+ 0xfe,0x02,
+ 0xc0,0x01,
+ 0xc1,0x44, //Green_cc for d//40 cyrille
+ 0xc2,0xfc,
+ 0xc3,0x05,
+ 0xc4,0xec,
+ 0xc5,0x42,
+ 0xc6,0xf8,
+ 0xc7,0x40,//for cwf
+ 0xc8,0xf8,
+ 0xc9,0x06,
+ 0xca,0xfd,
+ 0xcb,0x3e,
+ 0xcc,0xf3,
+ 0xcd,0x36,//for A
+ 0xce,0xf6,
+ 0xcf,0x04,
+ 0xe3,0x0c,
+ 0xe4,0x44,
+ 0xe5,0xe5,
+ 0xfe,0x00,
+
+ ////,b stt ////////////////AWB ,ar
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x10, // 10
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x20, // 20
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00, // 30
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x40, // 40
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x50, // 50
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x60, // 60
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x70, // 70
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x80, // 80
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x90, // 90
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xa0, // a0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xb0, // b0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xc0, // c0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xd0, // d0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4f,0x01,
+ ////,wb vue////////
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x40,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x53,
+ 0x4e,0x08,
+ 0x4e,0x04,
+ 0x4d,0x62,
+ 0x4e,0x10,
+ 0x4d,0x72,
+ 0x4e,0x20,
+ 0x4f,0x01,
+
+ ///a,///
+ 0xfe,0x01,
+ 0x50,0x88,//c0//[6]green mode
+ 0x52,0x40,
+ 0x54,0x60,
+ 0x56,0x06,
+ 0x57,0x20, //pre adjust
+ 0x58,0x01,
+ 0x5b,0x02, //AWB_gain_delta
+ 0x61,0xaa,//R/G stand
+ 0x62,0xaa,//R/G stand
+ 0x71,0x00,
+ 0x74,0x10, //0x//AWB_C_max
+ 0x77,0x08, // 0x//AWB_p2_x
+ 0x78,0xfd, //AWB_p2_y
+ 0x86,0x30,
+ 0x87,0x00,
+ 0x88,0x04,//06},{0x//[1]dark mode
+ 0x8a,0xc0,//awb move mode
+ 0x89,0x75,
+ 0x84,0x08, //0x//auto_window
+ 0x8b,0x00, // 0x//awb compare luma
+ 0x8d,0x70, //awb gain limit R
+ 0x8e,0x70,//G
+ 0x8f,0xf4,//B
+ 0xfe,0x00,
+ 0x82,0x02,//awb_en
+ ////wb end /////////////
+
+ //===,====de
+ 0xfe,0x01,
+
+ 0x9c,0x00,//04 cyrille
+
+ 0x21,0xbf,
+ 0xfe,0x02,
+ 0xa4,0x00,//
+ 0xa5,0x40, //lsc_th
+ 0xa2,0xa0, //lsc_dec_slope
+ 0xa6,0x80, //dd_th
+ 0xa7,0x80, //ot_th
+ 0xab,0x31, //
+ 0xa9,0x6f, //
+ 0xb0,0x99, //0x//edge effect slope low
+ 0xb1,0x34,//edge effect slope low
+ 0xb3,0x80, //saturation dec slope
+ 0xde,0xb6, //
+ 0x38,0x0f, //
+ 0x39,0x60, //
+ 0xfe,0x00,
+ 0x81,0x26,
+ 0xfe,0x02,
+ 0x83,0x00,//
+ 0x84,0x45,//
+ ////,///Y//////////
+ 0xd1,0x39,//saturation_cb
+ 0xd2,0x39,//saturation_Cr
+ 0xd3,0x42,//contrast ?
+ 0xd4,0x80,//contrast center
+ 0xd5,0x00,//luma_offset
+ 0xdc,0x30,
+ 0xdd,0xb8,//edge_sa_g,b
+ 0xfe,0x00,
+ ////,dd/////////
+ 0xfe,0x02,
+ 0x88,0x15,//dn_b_base
+ 0x8c,0xf6, //[2]b_in_dark_inc
+ 0x89,0x03, //dn_c_weight
+ ////,E /////////
+ 0xfe,0x02,
+ 0x90,0x6c,// EEINTP mode1
+ 0x97,0x56,// edge effect//45 cyrille
+ //==,=======RGB Gamma
+ 0xfe,0x02,
+ 0x15,0x0a,
+ 0x16,0x12,
+ 0x17,0x19,
+ 0x18,0x1f,
+ 0x19,0x2c,
+ 0x1a,0x38,
+ 0x1b,0x42,
+ 0x1c,0x4e,
+ 0x1d,0x63,
+ 0x1e,0x76,
+ 0x1f,0x87,
+ 0x20,0x96,
+ 0x21,0xa2,
+ 0x22,0xb8,
+ 0x23,0xca,
+ 0x24,0xd8,
+ 0x25,0xe3,
+ 0x26,0xf0,
+ 0x27,0xf8,
+ 0x28,0xfd,
+ 0x29,0xff,
+
+ //===,=========y gamma
+ 0xfe,0x02,
+ 0x2b,0x00,
+ 0x2c,0x04,
+ 0x2d,0x09,
+ 0x2e,0x18,
+ 0x2f,0x27,
+ 0x30,0x37,
+ 0x31,0x49,
+ 0x32,0x5c,
+ 0x33,0x7e,
+ 0x34,0xa0,
+ 0x35,0xc0,
+ 0x36,0xe0,
+ 0x37,0xff,
+ ///1,x120ize//
+ 0xfe,0x00,//
+ 0x90,0x01, //0x//crop enable
+ 0x95,0x04, //0x//1600x1200
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+
+ 0xfe,0x03,
+ 0x42,0x40,
+ 0x43,0x06, //output buf width
+ 0x41,0x02, // Pclk_polarity
+ 0x40,0x40, //00
+ 0x17,0x00, //widv
+ 0xfe,0x00,
+ //ou,t DV////
+
+ 0xfe,0x00,
+ 0x82,0xfe, // fe
+ 0xf2,0x70,
+ 0xf3,0xff,
+ 0xf4,0x00,
+ 0xf5,0x30,
+
+ #if 0
+ //,///ssumple 640X4800//////
+ 0xfe,0x00,
+ 0xfa,0x00,
+ 0xc8,0x00,
+
+ 0x99,0x22,
+ 0x9a,0x07,
+ 0x9b,0x00,
+ 0x9c,0x00,
+ 0x9d,0x00,
+ 0x9e,0x00,
+ 0x9f,0x00,
+ 0xa0,0x00,
+ 0xa1,0x00,
+ 0xa2,0x00,
+
+
+ 0x90,0x01,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+ #endif
+
+
+ 0xfe,0x00,
+ 0x0c,0x00,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+
+ 0xc8,0x15,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x00,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+ 0xb6,0x03,
+
+#if 0
+ //// re zao///
+ 0xfe ,0x00,
+ 0x22 ,0xf0,
+ 0xfe ,0x01,
+ 0x21 ,0xff,
+ 0xfe ,0x02,
+ 0x8a ,0x33,
+ 0x8c ,0x76,
+ 0x8d ,0x85,
+ 0xa6 ,0xf0,
+ 0xae ,0x9f,
+ 0xa2 ,0x90,
+ 0xa5 ,0x40,
+ 0xa7 ,0x30,
+ 0xb0 ,0x88,
+ 0x38 ,0x0b,
+ 0x39 ,0x30,
+ 0xfe ,0x00,
+ 0x87 ,0xb0,
+
+
+ /*
+ //, sma RGB gamma////
+ 0xfe,0x02,
+ 0x15,0x0b,
+ 0x16,0x0e,
+ 0x17,0x10,
+ 0x18,0x12,
+ 0x19,0x19,
+ 0x1a,0x21,
+ 0x1b,0x29,
+ 0x1c,0x31,
+ 0x1d,0x41,
+ 0x1e,0x50,
+ 0x1f,0x5f,
+ 0x20,0x6d,
+ 0x21,0x79,
+ 0x22,0x91,
+ 0x23,0xa5,
+ 0x24,0xb9,
+ 0x25,0xc9,
+ 0x26,0xe1,
+ 0x27,0xee,
+ 0x28,0xf7,
+ 0x29,0xff,
+ */
+ //da,sun///
+ 0xfe,0x02,
+ 0x40,0x06,
+ 0x41,0x23,
+ 0x42,0x3f,
+ 0x43,0x06,
+ 0x44,0x00,
+ 0x45,0x00,
+ 0x46,0x14,
+ 0x47,0x09,
+
+ #endif
+};
+
+uint8_t gc2035_default_regs_exit[]={
+ 0xfe, 0x00,
+ 0xf1, 0x00,
+ 0xf2, 0x00,
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-rb352a_v7.h b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-rb352a_v7.h
new file mode 100755
index 00000000..f338be3c
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-rb352a_v7.h
@@ -0,0 +1,914 @@
+#ifndef GC2035_H
+#define GC2035_H
+
+
+// Scene Mode
+uint8_t gc2035_scene_mode_auto[] = {
+ 0xfe,0x01,
+ 0x3e,0x40,
+ 0x1f,0xa0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x50,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe,0x00,
+};
+
+uint8_t gc2035_scene_mode_night[] = {
+ 0xfe, 0x01,
+ 0x3e, 0x60,
+ 0x1f,0xe0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x80,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe, 0x00,
+};
+
+
+// White Balance
+uint8_t gc2035_wb_auto [] = {
+ 0xfe, 0x00,
+// 0xb3, 0x61, //2013-8-16 dustin
+// 0xb4, 0x40,
+// 0xb5, 0x61,
+ 0x82, 0xfe,
+};
+
+uint8_t gc2035_wb_incandescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0xa8,
+};
+
+uint8_t gc2035_wb_fluorescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0xa0,
+ 0xb4, 0x45,
+ 0xb5, 0x40,
+};
+
+uint8_t gc2035_wb_daylight [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x78,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+uint8_t gc2035_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x58,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+
+// Exposure
+uint8_t gc2035_exposure_neg6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x68,
+ 0xfe, 0x02,
+ 0xd5, 0xe0,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_neg3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x70,
+ 0xfe, 0x02,
+ 0xd5, 0xf0,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_zero[] = {
+ 0xfe, 0x01,
+ 0x13, 0x78,
+ 0xfe, 0x02,
+ 0xd5, 0x00,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_pos3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x80,
+ 0xfe, 0x02,
+ 0xd5, 0x10,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_pos6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x88,
+ 0xfe, 0x02,
+ 0xd5, 0x20,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_176_144_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x33,
+ 0x9b,0x00,
+ 0x9f,0x00,
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0x92,//600
+ 0x97,0x00,
+ 0x98,0xb0,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+
+ 0xfe,0x03,
+ 0x42,0x60,
+ 0x43,0x01,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_320_240_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x22,
+ 0x9b,0x00,
+ 0x9f,0x00,
+
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0xf2,//600
+ 0x97,0x01,
+ 0x98,0x40,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x02,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_1280_720_regs[]={
+ 0xfe,0x00,
+ 0x0a,0xf0,
+ 0x0c,0xa0,
+ 0x0d,0x02,
+ 0x0e,0xd8,
+ 0x0f,0x05,
+ 0x10,0x18,
+ 0xfe,0x00,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x05,0x01,
+ 0x06,0xaa,
+ 0x07,0x00,
+ 0x08,0xf6,
+ 0xfe,0x01,
+ 0x27,0x00,
+ 0x28,0xa1,
+ 0x29,0x03,
+ 0x2a,0xc6,
+ 0x2b,0x04,
+ 0x2c,0x67,
+ 0x2d,0x06,
+ 0x2e,0x4a,
+ 0x2f,0x07,
+ 0x30,0x8c,
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xb6,0x03,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x99,0x11,
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0xd0,
+ 0x97,0x05,
+ 0x98,0x00,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+ 0xfa,0x11,
+};
+
+uint8_t gc2035_1600_1200_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x02,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x04,
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+
+ 0xc8,0x00,
+ 0xf7,0x17,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+};
+
+uint8_t gc2035_800_600_regs[]={
+ 0xfe,0x00,
+ 0xfa,0x00,
+ 0xc8,0x00,
+
+ 0x99,0x22,
+ 0x9a,0x07,
+ 0x9b,0x00,
+ 0x9c,0x00,
+ 0x9d,0x00,
+ 0x9e,0x00,
+ 0x9f,0x00,
+ 0xa0,0x00,
+ 0xa1,0x00,
+ 0xa2,0x00,
+
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0x58,
+ 0x97,0x03,
+ 0x98,0x20,
+};
+
+uint8_t gc2035_640_480_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x00,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+
+ 0xc8,0x15,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x00,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+ 0xb6,0x03,
+};
+
+uint8_t gc2035_default_regs_init[]={
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfc,0x06,
+ 0xf9,0xfe,
+ 0xfa,0x00,
+ 0xf6,0x00,
+ 0xf7,0x17,
+ 0xf8,0x00,
+ 0xfe,0x00,
+ 0x82,0x00,
+ //0xb3,0x60,//close manual wb 2013.07.23
+ //0xb4,0x40,
+ //0xb5,0x60,
+ 0x03,0x05,
+ 0x04,0x08,
+ 0xfe,0x00,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x60,
+ 0xef,0x90,
+ 0x0c,0x00,
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+ 0x17,0x14,
+ 0x18,0x0a,
+ 0x19,0x0a,
+ 0x1a,0x01,
+ 0x1b,0x8b,
+ 0x1c,0x05,
+ 0x1e,0x88, //analog mode1 [7] tx-high en [5:3]COL_bias
+ 0x1f,0x08, //[3] tx-low en//
+ 0x20,0x05, //[0]adclk mode,0x[1]rowclk_MODE [2]rsthigh_en
+ 0x21,0x0f, //[6:4]rsg
+ 0x22,0xd0, //[3:0]vref
+ 0x23,0xc3, //f3//ADC_r
+ 0x24,0x17, //pad drive 16
+
+ //AEC
+ 0xfe,0x01,
+ 0x11,0x20,//AEC_out_slope,0x
+ 0x1f,0xa0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x50,//max_pre_gain 60 2013-7-22 dustin
+ 0x47,0x30,//AEC_outdoor_th
+ 0x0b,0x10,//
+ 0x13,0x75,//y_target
+ 0xfe,0x00,
+
+ 0x05,0x01,//hb
+ 0x06,0x11,
+ 0x07,0x00,//vb
+ 0x08,0x50,
+ 0xfe,0x01,
+ 0x27,0x00,//step
+ 0x28,0xa0,
+ 0x29,0x05,//level1
+ 0x2a,0x00,
+ 0x2b,0x05,//level2
+ 0x2c,0x00,
+ 0x2d,0x05,//6e8//level3
+ 0x2e,0x00,
+ 0x2f,0x0b,//level4
+ 0x30,0x40,
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xb6,0x03,
+ 0xfe,0x00,
+ 0x3f,0x00,
+ 0x40,0x77,
+ 0x42,0x7f,
+ 0x43,0x30,
+ 0x5c,0x08,
+ 0x5e,0x20,
+ 0x5f,0x20,
+ 0x60,0x20,
+ 0x61,0x20,
+ 0x62,0x20,
+ 0x63,0x20,
+ 0x64,0x20,
+ 0x65,0x20,
+ 0x66,0x20,
+ 0x67,0x20,
+ 0x68,0x20,
+ 0x69,0x20,
+ 0x90,0x01,
+ 0x95,0x04,
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0x80,0xff,
+ 0x81,0x26,
+ 0x03,0x05,
+ 0x04,0x2e,
+ 0x84,0x02,
+ 0x86,0x02,
+ 0x87,0x80,
+ 0x8b,0xbc,
+ 0xa7,0x80,
+ 0xa8,0x80,
+ 0xb0,0x80,
+ 0xc0,0x40,
+ //LSC
+ 0xfe,0x01,
+ 0xc2,0x38,
+ 0xc3,0x25,
+ 0xc4,0x21,
+ 0xc8,0x19,
+ 0xc9,0x12,
+ 0xca,0x0e,
+ 0xbc,0x43,
+ 0xbd,0x18,
+ 0xbe,0x1b,
+ 0xb6,0x40,
+ 0xb7,0x2e,
+ 0xb8,0x26,
+ 0xc5,0x05,
+ 0xc6,0x03,
+ 0xc7,0x04,
+ 0xcb,0x00,
+ 0xcc,0x00,
+ 0xcd,0x00,
+ 0xbf,0x14,
+ 0xc0,0x22,
+ 0xc1,0x1b,
+ 0xb9,0x00,
+ 0xba,0x05,
+ 0xbb,0x05,
+ 0xaa,0x35,
+ 0xab,0x33,
+ 0xac,0x33,
+ 0xad,0x25,
+ 0xae,0x22,
+ 0xaf,0x27,
+ 0xb0,0x1d,
+ 0xb1,0x20,
+ 0xb2,0x22,
+ 0xb3,0x14,
+ 0xb4,0x15,
+ 0xb5,0x16,
+ 0xd0,0x00,
+ 0xd2,0x07,
+ 0xd3,0x08,
+ 0xd8,0x00,
+ 0xda,0x13,
+ 0xdb,0x17,
+ 0xdc,0x00,
+ 0xde,0x0a,
+ 0xdf,0x08,
+ 0xd4,0x00,
+ 0xd6,0x00,
+ 0xd7,0x0c,
+ 0xa4,0x00,
+ 0xa5,0x00,
+ 0xa6,0x00,
+ 0xa7,0x00,
+ 0xa8,0x00,
+ 0xa9,0x00,
+ 0xa1,0x80,
+ 0xa2,0x80,
+
+ //=================================cc
+ 0xfe,0x02,
+ 0xc0,0x01,
+ 0xc1,0x40, //Green_cc for d
+ 0xc2,0xfc,
+ 0xc3,0x05,
+ 0xc4,0xec,
+ 0xc5,0x42,
+ 0xc6,0xf8,
+ 0xc7,0x40,//for cwf
+ 0xc8,0xf8,
+ 0xc9,0x06,
+ 0xca,0xfd,
+ 0xcb,0x3e,
+ 0xcc,0xf3,
+ 0xcd,0x36,//for A
+ 0xce,0xf6,
+ 0xcf,0x04,
+ 0xe3,0x0c,
+ 0xe4,0x44,
+ 0xe5,0xe5,
+ 0xfe,0x00,
+
+ //==============================awb
+ //AWB clear
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x10, // 10
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x20, // 20
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00, // 30
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x40, // 40
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x50, // 50
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x60, // 60
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x70, // 70
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x80, // 80
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x90, // 90
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xa0, // a0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xb0, // b0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xc0, // c0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xd0, // d0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4f,0x01,
+ /////// awb value////////
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x40,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x53,
+ 0x4e,0x08,
+ 0x4e,0x04,
+ 0x4d,0x62,
+ 0x4e,0x10,
+ 0x4d,0x72,
+ 0x4e,0x20,
+ 0x4f,0x01,
+
+ /////awb////
+ 0xfe,0x01,
+ 0x50,0x88,//c0//[6]green mode
+ 0x52,0x40,
+ 0x54,0x60,
+ 0x56,0x06,
+ 0x57,0x20, //pre adjust
+ 0x58,0x01,
+ 0x5b,0x02, //AWB_gain_delta
+ 0x61,0xaa,//R/G stand
+ 0x62,0xaa,//R/G stand
+ 0x71,0x00,
+ 0x74,0x10,//AWB_C_max
+ 0x77,0x08, // 0x//AWB_p2_x
+ 0x78,0xfd, //AWB_p2_y
+ 0x86,0x30,
+ 0x87,0x00,
+ 0x88,0x04,//06,[1]dark mode
+ 0x8a,0xc0,//awb move mode
+ 0x89,0x75,
+ 0x84,0x08,//auto_window
+ 0x8b,0x00, // 0x//awb compare luma
+ 0x8d,0x70, //awb gain limit R
+ 0x8e,0x70,//G
+ 0x8f,0xf4,//B
+ 0xfe,0x00,
+ 0x82,0x02,//awb_en
+
+ 0xfe,0x01,
+ 0x21,0xbf,
+ 0xfe,0x02,
+ 0xa4,0x00,//
+ 0xa5,0x40, //lsc_th
+ 0xa2,0xa0, //lsc_dec_slope
+ 0xa6,0x80, //dd_th
+ 0xa7,0x80, //ot_th
+ 0xab,0x31, //
+ 0xa9,0x6f, //
+ 0xb0,0x99, //0x//edge effect slope low
+ 0xb1,0x34,//edge effect slope low
+ 0xb3,0x80, //saturation dec slope
+ 0xde,0xb6, //
+ 0x38,0x0f, //
+ 0x39,0x60, //
+ 0xfe,0x00,
+ 0x81,0x26,
+ 0xfe,0x02,
+ 0x83,0x00,//
+ 0x84,0x45,//
+ ////////////YCP//////////
+ 0xd1,0x40,//saturation_cb
+ 0xd2,0x46,//40 saturation_Cr
+
+ 0xaf,0x7e,
+
+
+ 0xd3,0x48,//contrast ? 40 2013-7-22 dustin
+ 0xd4,0x80,//contrast center
+ 0xd5,0x00,//luma_offset
+ 0xdc,0x30,
+ 0xdd,0xb8,
+ 0xfe,0x00,
+ //gamma
+ 0xfe,0x02,
+ 0x88,0x1f,//dn_b_base 15 2013-7-22 dustin
+ 0x8c,0xf6, //[2]b_in_dark_inc
+ 0x89,0x02, //dn_c_weight 03 2013-7-22 dustin
+ ////////EE ///////////
+ 0xfe,0x02,
+ 0x90,0x6c,// EEINTP mode1
+ 0x97,0x78,// edge effect
+ ////==============RGB Gamma
+ 0xfe,0x02,
+ 0x15,0x0a,
+ 0x16,0x12,
+ 0x17,0x19,
+ 0x18,0x1f,
+ 0x19,0x2c,
+ 0x1a,0x38,
+ 0x1b,0x42,
+ 0x1c,0x4e,
+ 0x1d,0x63,
+ 0x1e,0x76,
+ 0x1f,0x87,
+ 0x20,0x96,
+ 0x21,0xa2,
+ 0x22,0xb8,
+ 0x23,0xca,
+ 0x24,0xd8,
+ 0x25,0xe3,
+ 0x26,0xf0,
+ 0x27,0xf8,
+ 0x28,0xfd,
+ 0x29,0xff,
+
+ ///=================y gamma
+ 0xfe,0x02,
+ 0x2b,0x00,
+ 0x2c,0x04,
+ 0x2d,0x09,
+ 0x2e,0x18,
+ 0x2f,0x27,
+ 0x30,0x37,
+ 0x31,0x49,
+ 0x32,0x5c,
+ 0x33,0x7e,
+ 0x34,0xa0,
+ 0x35,0xc0,
+ 0x36,0xe0,
+ 0x37,0xff,
+ 0xfe,0x00,
+
+ 0xfe,0x00,
+ 0x82,0xfe,
+ 0xf2,0x70,
+ 0xf3,0xff,
+ 0xf4,0x00,
+ 0xf5,0x30,
+
+ 0xfe,0x00,
+ 0x90,0x01,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+ 0xc8,0x14,
+ 0xf7,0x11,
+ 0xf8,0x85,
+ 0xfa,0x00,
+ 0x05,0x01,
+ 0x06,0x0a,
+ 0x07,0x00,
+ 0x08,0x58,
+ 0xfe,0x01,
+ 0x27,0x00,
+ 0x28,0xa1,
+ 0x29,0x05,
+ 0x2a,0x08,
+ 0x2b,0x05,
+ 0x2c,0x08,
+ 0x2d,0x05,
+ 0x2e,0x08,
+
+ 0x2f,0x05,
+ 0x30,0x08,
+
+ 0x3e,0x40,
+ 0xfe,0x03,
+ 0x42,0x04,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+};
+
+uint8_t gc2035_default_regs_exit[]={
+ 0xfe, 0x00,
+ 0xf1, 0x00,
+ 0xf2, 0x00,
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-rb357a_v7.h b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-rb357a_v7.h
new file mode 100755
index 00000000..eb5759f7
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-rb357a_v7.h
@@ -0,0 +1,914 @@
+#ifndef GC2035_H
+#define GC2035_H
+
+
+// Scene Mode
+uint8_t gc2035_scene_mode_auto[] = {
+ 0xfe,0x01,
+ 0x3e,0x40,
+ 0x1f,0xa0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x50,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe,0x00,
+};
+
+uint8_t gc2035_scene_mode_night[] = {
+ 0xfe, 0x01,
+ 0x3e, 0x60,
+ 0x1f,0xe0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x80,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe, 0x00,
+};
+
+
+// White Balance
+uint8_t gc2035_wb_auto [] = {
+ 0xfe, 0x00,
+// 0xb3, 0x61, //2013-8-16 dustin
+// 0xb4, 0x40,
+// 0xb5, 0x61,
+ 0x82, 0xfe,
+};
+
+uint8_t gc2035_wb_incandescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0xa8,
+};
+
+uint8_t gc2035_wb_fluorescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0xa0,
+ 0xb4, 0x45,
+ 0xb5, 0x40,
+};
+
+uint8_t gc2035_wb_daylight [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x78,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+uint8_t gc2035_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x58,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+
+// Exposure
+uint8_t gc2035_exposure_neg6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x68,
+ 0xfe, 0x02,
+ 0xd5, 0xe0,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_neg3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x70,
+ 0xfe, 0x02,
+ 0xd5, 0xf0,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_zero[] = {
+ 0xfe, 0x01,
+ 0x13, 0x78,
+ 0xfe, 0x02,
+ 0xd5, 0x00,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_pos3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x80,
+ 0xfe, 0x02,
+ 0xd5, 0x10,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_pos6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x88,
+ 0xfe, 0x02,
+ 0xd5, 0x20,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_176_144_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x33,
+ 0x9b,0x00,
+ 0x9f,0x00,
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0x92,//600
+ 0x97,0x00,
+ 0x98,0xb0,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+
+ 0xfe,0x03,
+ 0x42,0x60,
+ 0x43,0x01,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_320_240_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x22,
+ 0x9b,0x00,
+ 0x9f,0x00,
+
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0xf2,//600
+ 0x97,0x01,
+ 0x98,0x40,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x02,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_1280_720_regs[]={
+ 0xfe,0x00,
+ 0x0a,0xf0,
+ 0x0c,0xa0,
+ 0x0d,0x02,
+ 0x0e,0xd8,
+ 0x0f,0x05,
+ 0x10,0x18,
+ 0xfe,0x00,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x05,0x01,
+ 0x06,0xaa,
+ 0x07,0x00,
+ 0x08,0xf6,
+ 0xfe,0x01,
+ 0x27,0x00,
+ 0x28,0xa1,
+ 0x29,0x03,
+ 0x2a,0xc6,
+ 0x2b,0x04,
+ 0x2c,0x67,
+ 0x2d,0x06,
+ 0x2e,0x4a,
+ 0x2f,0x07,
+ 0x30,0x8c,
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xb6,0x03,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x99,0x11,
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0xd0,
+ 0x97,0x05,
+ 0x98,0x00,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+ 0xfa,0x11,
+};
+
+uint8_t gc2035_1600_1200_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x02,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x04,
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+
+ 0xc8,0x00,
+ 0xf7,0x17,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+};
+
+uint8_t gc2035_800_600_regs[]={
+ 0xfe,0x00,
+ 0xfa,0x00,
+ 0xc8,0x00,
+
+ 0x99,0x22,
+ 0x9a,0x07,
+ 0x9b,0x00,
+ 0x9c,0x00,
+ 0x9d,0x00,
+ 0x9e,0x00,
+ 0x9f,0x00,
+ 0xa0,0x00,
+ 0xa1,0x00,
+ 0xa2,0x00,
+
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0x58,
+ 0x97,0x03,
+ 0x98,0x20,
+};
+
+uint8_t gc2035_640_480_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x00,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+
+ 0xc8,0x15,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x00,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+ 0xb6,0x03,
+};
+
+uint8_t gc2035_default_regs_init[]={
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfc,0x06,
+ 0xf9,0xfe,
+ 0xfa,0x00,
+ 0xf6,0x00,
+ 0xf7,0x17,
+ 0xf8,0x00,
+ 0xfe,0x00,
+ 0x82,0x00,
+ //0xb3,0x60,////close manual wb 2013.07.23
+ //0xb4,0x40,
+ //0xb5,0x60,
+ 0x03,0x05,
+ 0x04,0x08,
+ 0xfe,0x00,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x60,
+ 0xef,0x90,
+ 0x0c,0x00,
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+ 0x17,0x14,
+ 0x18,0x0a,
+ 0x19,0x0a,
+ 0x1a,0x01,
+ 0x1b,0x8b,
+ 0x1c,0x05,
+ 0x1e,0x88, //analog mode1 [7] tx-high en [5:3]COL_bias
+ 0x1f,0x08, //[3] tx-low en//
+ 0x20,0x05, //[0]adclk mode,0x[1]rowclk_MODE [2]rsthigh_en
+ 0x21,0x0f, //[6:4]rsg
+ 0x22,0xd0, //[3:0]vref
+ 0x23,0xc3, //f3//ADC_r
+ 0x24,0x17, //pad drive 16
+
+ //AEC
+ 0xfe,0x01,
+ 0x11,0x20,//AEC_out_slope,0x
+ 0x1f,0xc0,//max_post_gain
+ 0x20,0x60,//max_pre_gain
+ 0x47,0x30,//AEC_outdoor_th
+ 0x0b,0x10,//
+ 0x13,0x75,//y_target
+ 0xfe,0x00,
+
+ 0x05,0x01,//hb
+ 0x06,0x11,
+ 0x07,0x00,//vb
+ 0x08,0x50,
+ 0xfe,0x01,
+ 0x27,0x00,//step
+ 0x28,0xa0,
+ 0x29,0x05,//level1
+ 0x2a,0x00,
+ 0x2b,0x05,//level2
+ 0x2c,0x00,
+ 0x2d,0x05,//6e8//level3
+ 0x2e,0x00,
+ 0x2f,0x0b,//level4
+ 0x30,0x40,
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xb6,0x03,
+ 0xfe,0x00,
+ 0x3f,0x00,
+ 0x40,0x77,
+ 0x42,0x7f,
+ 0x43,0x30,
+ 0x5c,0x08,
+ 0x5e,0x20,
+ 0x5f,0x20,
+ 0x60,0x20,
+ 0x61,0x20,
+ 0x62,0x20,
+ 0x63,0x20,
+ 0x64,0x20,
+ 0x65,0x20,
+ 0x66,0x20,
+ 0x67,0x20,
+ 0x68,0x20,
+ 0x69,0x20,
+ 0x90,0x01,
+ 0x95,0x04,
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0x80,0xff,
+ 0x81,0x26,
+ 0x03,0x05,
+ 0x04,0x2e,
+ 0x84,0x02,
+ 0x86,0x02,
+ 0x87,0x80,
+ 0x8b,0xbc,
+ 0xa7,0x80,
+ 0xa8,0x80,
+ 0xb0,0x80,
+ 0xc0,0x40,
+ //LSC
+ 0xfe,0x01,
+ 0xc2,0x38,
+ 0xc3,0x25,
+ 0xc4,0x21,
+ 0xc8,0x19,
+ 0xc9,0x12,
+ 0xca,0x0e,
+ 0xbc,0x43,
+ 0xbd,0x18,
+ 0xbe,0x1b,
+ 0xb6,0x40,
+ 0xb7,0x2e,
+ 0xb8,0x26,
+ 0xc5,0x05,
+ 0xc6,0x03,
+ 0xc7,0x04,
+ 0xcb,0x00,
+ 0xcc,0x00,
+ 0xcd,0x00,
+ 0xbf,0x14,
+ 0xc0,0x22,
+ 0xc1,0x1b,
+ 0xb9,0x00,
+ 0xba,0x05,
+ 0xbb,0x05,
+ 0xaa,0x35,
+ 0xab,0x33,
+ 0xac,0x33,
+ 0xad,0x25,
+ 0xae,0x22,
+ 0xaf,0x27,
+ 0xb0,0x1d,
+ 0xb1,0x20,
+ 0xb2,0x22,
+ 0xb3,0x14,
+ 0xb4,0x15,
+ 0xb5,0x16,
+ 0xd0,0x00,
+ 0xd2,0x07,
+ 0xd3,0x08,
+ 0xd8,0x00,
+ 0xda,0x13,
+ 0xdb,0x17,
+ 0xdc,0x00,
+ 0xde,0x0a,
+ 0xdf,0x08,
+ 0xd4,0x00,
+ 0xd6,0x00,
+ 0xd7,0x0c,
+ 0xa4,0x00,
+ 0xa5,0x00,
+ 0xa6,0x00,
+ 0xa7,0x00,
+ 0xa8,0x00,
+ 0xa9,0x00,
+ 0xa1,0x80,
+ 0xa2,0x80,
+
+ //=================================cc
+ 0xfe,0x02,
+ 0xc0,0x01,
+ 0xc1,0x40, //Green_cc for d
+ 0xc2,0xfc,
+ 0xc3,0x05,
+ 0xc4,0xec,
+ 0xc5,0x42,
+ 0xc6,0xf8,
+ 0xc7,0x40,//for cwf
+ 0xc8,0xf8,
+ 0xc9,0x06,
+ 0xca,0xfd,
+ 0xcb,0x3e,
+ 0xcc,0xf3,
+ 0xcd,0x36,//for A
+ 0xce,0xf6,
+ 0xcf,0x04,
+ 0xe3,0x0c,
+ 0xe4,0x44,
+ 0xe5,0xe5,
+ 0xfe,0x00,
+
+ //==============================awb
+ //AWB clear
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x10, // 10
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x20, // 20
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00, // 30
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x40, // 40
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x50, // 50
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x60, // 60
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x70, // 70
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x80, // 80
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x90, // 90
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xa0, // a0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xb0, // b0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xc0, // c0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xd0, // d0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4f,0x01,
+ /////// awb value////////
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x40,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x53,
+ 0x4e,0x08,
+ 0x4e,0x04,
+ 0x4d,0x62,
+ 0x4e,0x10,
+ 0x4d,0x72,
+ 0x4e,0x20,
+ 0x4f,0x01,
+
+ /////awb////
+ 0xfe,0x01,
+ 0x50,0x88,//c0//[6]green mode
+ 0x52,0x40,
+ 0x54,0x60,
+ 0x56,0x06,
+ 0x57,0x20, //pre adjust
+ 0x58,0x01,
+ 0x5b,0x02, //AWB_gain_delta
+ 0x61,0xaa,//R/G stand
+ 0x62,0xaa,//R/G stand
+ 0x71,0x00,
+ 0x74,0x10,//AWB_C_max
+ 0x77,0x08, // 0x//AWB_p2_x
+ 0x78,0xfd, //AWB_p2_y
+ 0x86,0x30,
+ 0x87,0x00,
+ 0x88,0x04,//06,[1]dark mode
+ 0x8a,0xc0,//awb move mode
+ 0x89,0x75,
+ 0x84,0x08,//auto_window
+ 0x8b,0x00, // 0x//awb compare luma
+ 0x8d,0x70, //awb gain limit R
+ 0x8e,0x70,//G
+ 0x8f,0xf4,//B
+ 0xfe,0x00,
+ 0x82,0x02,//awb_en
+
+ 0xfe,0x01,
+ 0x21,0xbf,
+ 0xfe,0x02,
+ 0xa4,0x00,//
+ 0xa5,0x40, //lsc_th
+ 0xa2,0xa0, //lsc_dec_slope
+ 0xa6,0x80, //dd_th
+ 0xa7,0x80, //ot_th
+ 0xab,0x31, //
+ 0xa9,0x6f, //
+ 0xb0,0x99, //0x//edge effect slope low
+ 0xb1,0x34,//edge effect slope low
+ 0xb3,0x80, //saturation dec slope
+ 0xde,0xb6, //
+ 0x38,0x0f, //
+ 0x39,0x60, //
+ 0xfe,0x00,
+ 0x81,0x26,
+ 0xfe,0x02,
+ 0x83,0x00,//
+ 0x84,0x45,//
+ ////////////YCP//////////
+ 0xd1,0x40,//saturation_cb
+ 0xd2,0x46,//40 saturation_Cr
+
+ 0xaf,0x7e,
+
+
+ 0xd3,0x40,//contrast ?
+ 0xd4,0x80,//contrast center
+ 0xd5,0x00,//luma_offset
+ 0xdc,0x30,
+ 0xdd,0xb8,
+ 0xfe,0x00,
+ //gamma
+ 0xfe,0x02,
+ 0x88,0x15,//dn_b_base
+ 0x8c,0xf6, //[2]b_in_dark_inc
+ 0x89,0x03, //dn_c_weight
+ ////////EE ///////////
+ 0xfe,0x02,
+ 0x90,0x6c,// EEINTP mode1
+ 0x97,0x78,// edge effect
+ ////==============RGB Gamma
+ 0xfe,0x02,
+ 0x15,0x0a,
+ 0x16,0x12,
+ 0x17,0x19,
+ 0x18,0x1f,
+ 0x19,0x2c,
+ 0x1a,0x38,
+ 0x1b,0x42,
+ 0x1c,0x4e,
+ 0x1d,0x63,
+ 0x1e,0x76,
+ 0x1f,0x87,
+ 0x20,0x96,
+ 0x21,0xa2,
+ 0x22,0xb8,
+ 0x23,0xca,
+ 0x24,0xd8,
+ 0x25,0xe3,
+ 0x26,0xf0,
+ 0x27,0xf8,
+ 0x28,0xfd,
+ 0x29,0xff,
+
+ ///=================y gamma
+ 0xfe,0x02,
+ 0x2b,0x00,
+ 0x2c,0x04,
+ 0x2d,0x09,
+ 0x2e,0x18,
+ 0x2f,0x27,
+ 0x30,0x37,
+ 0x31,0x49,
+ 0x32,0x5c,
+ 0x33,0x7e,
+ 0x34,0xa0,
+ 0x35,0xc0,
+ 0x36,0xe0,
+ 0x37,0xff,
+ 0xfe,0x00,
+
+ 0xfe,0x00,
+ 0x82,0xfe,
+ 0xf2,0x70,
+ 0xf3,0xff,
+ 0xf4,0x00,
+ 0xf5,0x30,
+
+ 0xfe,0x00,
+ 0x90,0x01,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+ 0xc8,0x14,
+ 0xf7,0x11,
+ 0xf8,0x85,
+ 0xfa,0x00,
+ 0x05,0x01,
+ 0x06,0x0a,
+ 0x07,0x00,
+ 0x08,0x58,
+ 0xfe,0x01,
+ 0x27,0x00,
+ 0x28,0xa1,
+ 0x29,0x05,
+ 0x2a,0x08,
+ 0x2b,0x05,
+ 0x2c,0x08,
+ 0x2d,0x05,
+ 0x2e,0x08,
+
+ 0x2f,0x05,
+ 0x30,0x08,
+
+ 0x3e,0x40,
+ 0xfe,0x03,
+ 0x42,0x04,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+};
+
+uint8_t gc2035_default_regs_exit[]={
+ 0xfe, 0x00,
+ 0xf1, 0x00,
+ 0xf2, 0x00,
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-shutter.c b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-shutter.c
new file mode 100755
index 00000000..c3c11719
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-shutter.c
@@ -0,0 +1,441 @@
+
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "gc2035.h"
+
+#define sensor_write_array(sd, arr, sz) \
+ cmos_init_8bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write(sd->i2c_addr, reg, val)
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint8_t *regs;
+ size_t size;
+};
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+#define FILP_REG_INIT_VALUE 0x14
+#define DELAY_INTERVAL 50
+#define RETRY_TIMES 10
+static uint32_t Preview_Shutter = 0;
+static uint32_t Capture_Shutter = 0;
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+ //CMOS_WIN_SIZE("QVGA", 320, 240, gc2035_320_240_regs),
+ CMOS_WIN_SIZE("VGA", 640, 480, gc2035_640_480_regs),
+ //CMOS_WIN_SIZE("SVGA", 800, 600, gc2035_800_600_regs),
+ //CMOS_WIN_SIZE("720p", 1280, 720, gc2035_1280_720_regs),
+ CMOS_WIN_SIZE("UXGA", 1600, 1200, gc2035_1600_1200_regs),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = gc2035_wb_auto;
+ size = ARRAY_SIZE(gc2035_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = gc2035_wb_incandescent;
+ size = ARRAY_SIZE(gc2035_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = gc2035_wb_daylight;
+ size = ARRAY_SIZE(gc2035_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = gc2035_wb_cloudy;
+ size = ARRAY_SIZE(gc2035_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = gc2035_wb_fluorescent;
+ size = ARRAY_SIZE(gc2035_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_scenemode(struct cmos_subdev *sd, enum v4l2_scene_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case SCENE_MODE_AUTO:
+ regs = gc2035_scene_mode_auto;
+ size = ARRAY_SIZE(gc2035_scene_mode_auto);
+ break;
+ case SCENE_MODE_NIGHTSHOT:
+ regs = gc2035_scene_mode_night;
+ size = ARRAY_SIZE(gc2035_scene_mode_night);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_exposure(struct cmos_subdev *sd, enum v4l2_exposure_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case -2:
+ regs = gc2035_exposure_neg6;
+ size = ARRAY_SIZE(gc2035_exposure_neg6);
+ break;
+ case -1:
+ regs = gc2035_exposure_neg3;
+ size = ARRAY_SIZE(gc2035_exposure_neg3);
+ break;
+ case 0:
+ regs = gc2035_exposure_zero;
+ size = ARRAY_SIZE(gc2035_exposure_zero);
+ break;
+ case 1:
+ regs = gc2035_exposure_pos3;
+ size = ARRAY_SIZE(gc2035_exposure_pos3);
+ break;
+ case 2:
+ regs = gc2035_exposure_pos6;
+ size = ARRAY_SIZE(gc2035_exposure_pos6);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_antibanding(struct cmos_subdev *sd, int value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case 0:
+ regs = gc2035_antibanding_auto;
+ size = ARRAY_SIZE(gc2035_antibanding_auto);
+ break;
+ case 1:
+ regs = gc2035_antibanding_50hz;
+ size = ARRAY_SIZE(gc2035_antibanding_50hz);
+ break;
+ case 2:
+ regs = gc2035_antibanding_60hz;
+ size = ARRAY_SIZE(gc2035_antibanding_60hz);
+ break;
+ case 3:
+ regs = gc2035_antibanding_off;
+ size = ARRAY_SIZE(gc2035_antibanding_off);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+
+ return 0;
+}
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data,tmp=0 ;
+ uint8_t retry_times=0;
+
+ sensor_write(sd, 0xfe, 0x00); // set page0
+ data=sensor_read(sd, 0x17);
+
+ switch (value) {
+ case 0:
+ data &= ~0x01;
+ break;
+ case 1:
+ data |= 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tmp=data | FILP_REG_INIT_VALUE;
+ for(retry_times=0;retry_times<RETRY_TIMES;retry_times++){
+ sensor_write(sd, 0x17, tmp);
+ msleep(50+retry_times*DELAY_INTERVAL);
+ if(sensor_read(sd, 0x17)==tmp)
+ break;
+ }
+
+ return 0;
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data,tmp=0;
+ uint8_t retry_times=0;
+
+ sensor_write(sd, 0xfe, 0x00); // set page0
+ data=sensor_read(sd, 0x17);
+
+ switch (value) {
+ case 0:
+ data &= ~0x02;
+ break;
+ case 1:
+ data |= 0x02;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tmp=data | FILP_REG_INIT_VALUE;
+ for(retry_times=0;retry_times<RETRY_TIMES;retry_times++){
+ sensor_write(sd, 0x17, tmp);
+ msleep(50+retry_times*DELAY_INTERVAL);
+ if(sensor_read(sd, 0x17)==tmp)
+ break;
+ }
+ return 0;
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ case V4L2_CID_EXPOSURE:
+ return v4l2_ctrl_query_fill(qc, -2, 2, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return sensor_s_scenemode(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return sensor_s_exposure(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ case V4L2_CID_CAMERA_ANTI_BANDING:
+ return sensor_s_antibanding(sd, ctrl->value);
+ default:
+ case WMT_V4L2_CID_CAMERA_ANTIBANDING:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+uint32_t sensor_read_shutter(struct cmos_subdev *sd)
+{
+ return (sensor_read(sd,0x03) << 8)|sensor_read(sd,0x04) ;
+}
+
+
+static void sensor_write_shutter(struct cmos_subdev *sd,uint32_t shutter)
+{
+
+ if(shutter < 1)
+ return;
+
+ sensor_write(sd,0x03, (shutter >> 8) & 0xff);
+ sensor_write(sd,0x04, shutter & 0xff);
+}
+
+
+static void sensor_set_ae_mode(struct cmos_subdev *sd,bool AE_enable)
+{
+ sensor_write(sd,0xfe, 0x00);
+ if (AE_enable == true)
+ {
+ // turn on AEC/AGC
+ sensor_write(sd,0xb6, 0x03);
+ }
+ else
+ {
+ // turn off AEC/AGC
+ sensor_write(sd,0xb6, 0x00);
+ }
+}
+
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+ uint32_t shutter = 0;
+
+ printk(KERN_DEBUG "%s, s_mbus_fmt width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win) {
+ pr_err("%s, s_mbus_fmt failed, width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, win->regs, win->size);
+ if(mf->width == 640 && mf->height ==480){
+ sensor_write_shutter(sd,Preview_Shutter);
+ sensor_set_ae_mode(sd,true);
+ }else if(mf->width == 1600 && mf->height ==1200){
+ sensor_set_ae_mode(sd,false);
+ shutter = sensor_read_shutter(sd);
+ Preview_Shutter = shutter;
+ shutter = shutter /2;
+ Capture_Shutter = shutter;
+ sensor_write_shutter(sd,Capture_Shutter);
+
+
+ }
+
+ msleep(400);
+ //msleep(500);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return 0;
+}
+
+static int sensor_enum_framesizes(struct cmos_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ const struct cmos_win_size *win = &cmos_supported_win_sizes[index];
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ uint32_t data = 0;
+
+ data |= (sensor_read(sd, 0xf0) & 0xff) << 8;
+ data |= (sensor_read(sd, 0xf1) & 0xff);
+
+ return (data == sd->id) ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ if (sensor_identify(sd)) {
+ return -1;
+ }
+
+ sensor_write_array(sd, gc2035_default_regs_init,
+ ARRAY_SIZE(gc2035_default_regs_init));
+
+ msleep(100);
+ Preview_Shutter =sensor_read_shutter(sd);
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ sensor_write_array(sd, gc2035_default_regs_exit,
+ ARRAY_SIZE(gc2035_default_regs_exit));
+ return 0;
+}
+
+static struct cmos_subdev_ops gc2035_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+ .enum_framesizes = sensor_enum_framesizes,
+};
+
+struct cmos_subdev gc2035 = {
+ .name = "gc2035",
+ .i2c_addr = 0x3c,
+ .id = 0x2035,
+ .max_width = 1600,
+ .max_height = 1200,
+ .ops = &gc2035_ops,
+};
+
+#if 0
+static int __init gc2035_init(void)
+{
+ return cmos_register_sudbdev(&gc2035);
+}
+
+static void __exit gc2035_exit(void)
+{
+ return cmos_unregister_subdev(&gc2035);
+}
+
+module_init(gc2035_init);
+module_exit(gc2035_exit);
+
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-tvboxh2-ori.h b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-tvboxh2-ori.h
new file mode 100755
index 00000000..39bd4d4e
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-tvboxh2-ori.h
@@ -0,0 +1,1064 @@
+#ifndef GC2035_H
+#define GC2035_H
+
+
+// Scene Mode
+uint8_t gc2035_scene_mode_auto[] = {
+ 0xfe,0x01,
+ 0x3e,0x40,
+ 0x1f,0xa0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x50,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe,0x00,
+};
+
+uint8_t gc2035_scene_mode_night[] = {
+ 0xfe, 0x01,
+ 0x3e, 0x60,
+ 0x1f,0xe0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x80,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe, 0x00,
+};
+
+
+// White Balance
+uint8_t gc2035_wb_auto [] = {
+ 0xfe, 0x00,
+// 0xb3, 0x61, //2013-8-16 dustin
+// 0xb4, 0x40,
+// 0xb5, 0x61,
+ 0x82, 0xfe,
+};
+
+uint8_t gc2035_wb_incandescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0xa8,
+};
+
+uint8_t gc2035_wb_fluorescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0x78,
+};
+
+uint8_t gc2035_wb_daylight [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x78,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+uint8_t gc2035_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x58,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+
+// Exposure
+uint8_t gc2035_exposure_neg6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x68,
+ 0xfe, 0x02,
+ 0xd5, 0xe0,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc2035_exposure_neg3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x70,
+ 0xfe, 0x02,
+ 0xd5, 0xf0,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc2035_exposure_zero[] = {
+ 0xfe, 0x01,
+ 0x13, 0x80,
+ 0xfe, 0x02,
+ 0xd5, 0x05,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_pos3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x80,
+ 0xfe, 0x02,
+ 0xd5, 0x10,
+ 0xfe, 0x00,
+
+
+};
+
+uint8_t gc2035_exposure_pos6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x88,
+ 0xfe, 0x02,
+ 0xd5, 0x20,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_antibanding_auto[] = {
+ 0xfe, 0x00,
+ 0x05, 0x01,
+ 0x06, 0x39,
+ 0x07, 0x00,
+ 0x08, 0x14,
+ 0xfe, 0x01,
+ 0x27, 0x00,
+ 0x28, 0x8b,
+ 0x29, 0x04,
+ 0x2a, 0x58,
+ 0x2b, 0x04,
+ 0x2c, 0x58,
+ 0x2d, 0x06,
+ 0x2e, 0x84,
+ 0x2f, 0x08,
+ 0x30, 0xb0,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_antibanding_50hz[] = {
+ 0xfe , 0x00,
+ 0x05 , 0x01,// HB
+ 0x06 , 0x23,
+ 0x07 , 0x00,// VB
+ 0x08 , 0x14,
+ 0xfe , 0x01,
+ 0x27 , 0x00,// step
+ 0x28 , 0xaa,
+ 0x29 , 0x04,// level 0 13.75
+ 0x2a , 0xa6,
+ 0x2b , 0x06,// level 1 10
+ 0x2c , 0xa4,
+ 0x2d , 0x08,// level 2 7.5
+ 0x2e , 0xa2,
+ 0x2f , 0x0b,// level 3
+ 0x30 , 0x4a,
+ 0xfe , 0x00,
+
+
+};
+
+uint8_t gc2035_antibanding_60hz[] = {
+ 0xfe, 0x00,
+ 0x05, 0x01,
+ 0x06, 0x39,
+ 0x07, 0x00,
+ 0x08, 0x14,
+ 0xfe, 0x01,
+ 0x27, 0x00,
+ 0x28, 0x8b,
+ 0x29, 0x04,
+ 0x2a, 0x58,
+ 0x2b, 0x04,
+ 0x2c, 0x58,
+ 0x2d, 0x06,
+ 0x2e, 0x84,
+ 0x2f, 0x08,
+ 0x30, 0xb0,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_antibanding_off[] = {
+ 0xfe , 0x00,
+ 0x05 , 0x01,// HB
+ 0x06 , 0x23,
+ 0x07 , 0x00,// VB
+ 0x08 , 0x14,
+ 0xfe , 0x01,
+ 0x27 , 0x00,// step
+ 0x28 , 0xaa,
+ 0x29 , 0x04,// level 0 13.75
+ 0x2a , 0xa6,
+ 0x2b , 0x06,// level 1 10
+ 0x2c , 0xa4,
+ 0x2d , 0x08,// level 2 7.5
+ 0x2e , 0xa2,
+ 0x2f , 0x0b,// level 3
+ 0x30 , 0x4a,
+ 0xfe , 0x00,
+};
+
+uint8_t gc2035_176_144_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x33,
+ 0x9b,0x00,
+ 0x9f,0x00,
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0x92,//600
+ 0x97,0x00,
+ 0x98,0xb0,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+
+ 0xfe,0x03,
+ 0x42,0x60,
+ 0x43,0x01,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_320_240_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x22,
+ 0x9b,0x00,
+ 0x9f,0x00,
+
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0xf2,//600
+ 0x97,0x01,
+ 0x98,0x40,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x02,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_1280_720_regs[]={
+ 0xfe,0x00,
+ 0x0a,0xf0,
+ 0x0c,0xa0,
+ 0x0d,0x02,
+ 0x0e,0xd8,
+ 0x0f,0x05,
+ 0x10,0x18,
+ 0xfe,0x00,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x05,0x01,
+ 0x06,0xaa,
+ 0x07,0x00,
+ 0x08,0xf6,
+ 0xfe,0x01,
+ 0x27,0x00,
+ 0x28,0xa1,
+ 0x29,0x03,
+ 0x2a,0xc6,
+ 0x2b,0x04,
+ 0x2c,0x67,
+ 0x2d,0x06,
+ 0x2e,0x4a,
+ 0x2f,0x07,
+ 0x30,0x8c,
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xb6,0x03,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x99,0x11,
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0xd0,
+ 0x97,0x05,
+ 0x98,0x00,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+ 0xfa,0x11,
+};
+
+uint8_t gc2035_1600_1200_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x02,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x04,
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+
+ 0xc8,0x00,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+};
+
+uint8_t gc2035_800_600_regs[]={
+ 0xfe,0x00,
+ 0xfa,0x00,
+ 0xc8,0x00,
+
+ 0x99,0x22,
+ 0x9a,0x07,
+ 0x9b,0x00,
+ 0x9c,0x00,
+ 0x9d,0x00,
+ 0x9e,0x00,
+ 0x9f,0x00,
+ 0xa0,0x00,
+ 0xa1,0x00,
+ 0xa2,0x00,
+
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0x58,
+ 0x97,0x03,
+ 0x98,0x20,
+};
+
+uint8_t gc2035_640_480_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x00,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+
+ 0xc8,0x15,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x00,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+ 0xb6,0x03,
+};
+
+uint8_t gc2035_default_regs_init[]={
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfc,0x06,
+ 0xf2,0x00,
+ 0xf3,0x00,
+ 0xf4,0x00,
+ 0xf5,0x00,
+ 0xf9,0xfe, //[0] pll enable
+ 0xfa,0x00,
+ 0xf6,0x00,
+ 0xf7,0x15, //pll enable
+
+ 0xf8,0x84,//85
+ 0xfe,0x00,
+ 0x82,0x00,
+ 0xb3,0x60,
+ 0xb4,0x40,
+ 0xb5,0x60,
+
+ 0x03,0x02,
+ 0x04,0x71,
+
+ ////,/meare window ///////////
+ 0xfe,0x00,
+ 0xec,0x06,//04
+ 0xed,0x06,//04
+ 0xee,0x62,//60
+ 0xef,0x92,//90
+
+ ////,//anog/////////////
+ 0x0a,0x00, //row start
+ 0x0c,0x00, //col start
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06, //Window setting
+ 0x10,0x58,
+ 0x17,0x14, //[0]mirror [1]flip
+ 0x18,0x0a, //0a 2012.10.26
+ 0x19,0x0a, //AD pipe number
+ 0x1a,0x01, //CISCTL mode4
+ 0x1b,0x8b,
+ 0x1c,0x05,//add by lanking 20130403
+ 0x1e,0x88, //analog mode1 [7] tx-high en [5:3]COL_bias
+ 0x1f,0x08, //[3] tx-low en//
+ 0x20,0x05, //[0]adclk mode},{0x[1]rowclk_MODE [2]rsthigh_en
+ 0x21,0x0f, //[6:4]rsg
+ 0x22,0xf0, //[3:0]vref 20130403
+ 0x23,0xc3, //f3//ADC_r
+ 0x24,0x17, //pad drive 16
+
+
+ 0xfe,0x01,
+ 0x11,0x20,//AEC_out_slope},{0x
+ 0x1f,0xb0,//max_post_gain
+ 0x20,0x48,//max_pre_gain
+ 0x47,0x30,//AEC_outdoor_th
+ 0x0b,0x10,//
+ 0x13,0x78,//y_target 84
+ 0xfe,0x00,
+
+ 0x05,0x01,//hb
+ 0x06,0x5c,
+ 0x07,0x00,//vb
+ 0x08,0x32,
+ 0xfe,0x01,
+ 0x27,0x00,//step
+ 0x28,0x7d,
+ 0x29,0x04,//level1
+ 0x2a,0xe2,
+ 0x2b,0x05,//level2 5dc
+ 0x2c,0xdc,
+ 0x2d,0x06,//6e8//level3
+ 0x2e,0xd6,
+ 0x2f,0x09,//level4
+ 0x30,0xc4,
+
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xfe,0x00, //0x},{0x},{0x},{0x},{0x
+ 0xb6,0x03, //AEC enable
+ 0xfe,0x00,
+
+ ////,BLK////
+ 0x3f,0x00, //prc close
+ 0x40,0x77,//
+ 0x42,0x7f,
+ 0x43,0x30,
+ 0x5c,0x08,
+ 0x5e,0x20,
+ 0x5f,0x20,
+ 0x60,0x20,
+ 0x61,0x20,
+ 0x62,0x20,
+ 0x63,0x20,
+ 0x64,0x20,
+ 0x65,0x20,
+
+ //blo,/////////
+ 0x80,0xff,
+ 0x81,0x26,//38},{0x//skin_Y 8c_debug
+ 0x87,0x90, //[7]middle gamma
+ 0x84,0x02, //output put foramat
+ 0x86,0x02, //02 //sync plority
+ 0x8b,0xbc,
+ 0xb0,0x80, //globle gain
+ 0xc0,0x40,//Yuv bypass
+
+ ////lsc///////////
+ 0xfe,0x01,
+ 0xc2,0x25,
+ 0xc3,0x09,
+ 0xc4,0x02,
+ 0xc8,0x12,
+ 0xc9,0x04,
+ 0xca,0x00,
+ 0xbc,0x3b,
+ 0xbd,0x11,
+ 0xbe,0x10,
+ 0xb6,0x5a,
+ 0xb7,0x27,
+ 0xb8,0x1e,
+ 0xc5,0x00,
+ 0xc6,0x00,
+ 0xc7,0x00,
+ 0xcb,0x00,
+ 0xcc,0x00,
+ 0xcd,0x00,
+ 0xbf,0x00,
+ 0xc0,0x00,
+ 0xc1,0x00,
+ 0xb9,0x00,
+ 0xba,0x09,
+ 0xbb,0x09,
+ 0xaa,0x4a,
+ 0xab,0x3d,
+ 0xac,0x3c,
+ 0xad,0x26,
+ 0xae,0x1f,
+ 0xaf,0x22,
+ 0xb0,0x3c,
+ 0xb1,0x31,
+ 0xb2,0x29,
+ 0xb3,0x3f,
+ 0xb4,0x30,
+ 0xb5,0x36,
+ 0xd0,0x00,
+ 0xd2,0x00,
+ 0xd3,0x00,
+ 0xd8,0x00,
+ 0xda,0x14,
+ 0xdb,0x24,
+ 0xdc,0x00,
+ 0xde,0x00,
+ 0xdf,0x1c,
+ 0xd4,0x00,
+ 0xd6,0x00,
+ 0xd7,0x00,
+ 0xa4,0x00,
+ 0xa5,0x00,
+ 0xa6,0x00,
+ 0xa7,0x00,
+ 0xa8,0x00,
+ 0xa9,0x00,
+ 0xa1,0x80,
+ 0xa2,0x80,
+
+ ////,/cc////////////
+ 0xfe,0x02,
+ 0xc0,0x01,
+ 0xc1,0x3e, //Green_cc for d
+ 0xc2,0xfc,
+ 0xc3,0x05,
+ 0xc4,0xea,
+ 0xc5,0x42,
+ 0xc6,0xf8,
+ 0xc7,0x40,//for cwf
+ 0xc8,0xf8,
+ 0xc9,0x06,
+ 0xca,0xfd,
+ 0xcb,0x3e,
+ 0xcc,0xf3,
+ 0xcd,0x36,//for A
+ 0xce,0xf6,
+ 0xcf,0x04,
+ 0xe3,0x0c,
+ 0xe4,0x44,
+ 0xe5,0xe5,
+ 0xfe,0x00,
+
+ ////,b stt ////////////////AWB ,ar
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x10, // 10
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x20, // 20
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00, // 30
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x40, // 40
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x50, // 50
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x60, // 60
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x70, // 70
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x80, // 80
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x90, // 90
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xa0, // a0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xb0, // b0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xc0, // c0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xd0, // d0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4f,0x01,
+ ////,wb vue////////
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x40,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x53,
+ 0x4e,0x08,
+ 0x4e,0x04,
+ 0x4d,0x62,
+ 0x4e,0x10,
+ 0x4d,0x72,
+ 0x4e,0x20,
+ 0x4f,0x01,
+
+ ///a,///
+ 0xfe,0x01,
+ 0x50,0x88,//c0//[6]green mode
+ 0x52,0x40,
+ 0x54,0x60,
+ 0x56,0x06,
+ 0x57,0x20, //pre adjust
+ 0x58,0x01,
+ 0x5b,0x02, //AWB_gain_delta
+ 0x61,0xaa,//R/G stand
+ 0x62,0xaa,//R/G stand
+ 0x71,0x00,
+ 0x74,0x10, //0x//AWB_C_max
+ 0x77,0x08, // 0x//AWB_p2_x
+ 0x78,0xfd, //AWB_p2_y
+ 0x86,0x30,
+ 0x87,0x00,
+ 0x88,0x04,//06},{0x//[1]dark mode
+ 0x8a,0xc0,//awb move mode
+ 0x89,0x75,
+ 0x84,0x08, //0x//auto_window
+ 0x8b,0x00, // 0x//awb compare luma
+ 0x8d,0x70, //awb gain limit R
+ 0x8e,0x70,//G
+ 0x8f,0xf4,//B
+ 0xfe,0x00,
+ 0x82,0x02,//awb_en
+ ////wb end /////////////
+
+ //===,====de
+ 0xfe,0x01,
+
+ 0x9c,0x04,//00
+
+ 0x21,0xbf,
+ 0xfe,0x02,
+ 0xa4,0x00,//
+ 0xa5,0x40, //lsc_th
+ 0xa2,0xa0, //lsc_dec_slope
+ 0xa6,0x80, //dd_th
+ 0xa7,0x80, //ot_th
+ 0xab,0x31, //
+ 0xa9,0x6f, //
+ 0xb0,0x99, //0x//edge effect slope low
+ 0xb1,0x34,//edge effect slope low
+ 0xb3,0x80, //saturation dec slope
+ 0xde,0xb6, //
+ 0x38,0x0f, //
+ 0x39,0x60, //
+ 0xfe,0x00,
+ 0x81,0x26,
+ 0xfe,0x02,
+ 0x83,0x00,//
+ 0x84,0x45,//
+ ////,///Y//////////
+ 0xd1,0x20,//saturation_cb
+ 0xd2,0x20,//saturation_Cr
+ 0xd3,0x42,//contrast ? {{0xd4},{0x80}},//contrast center
+ 0xd5,0x00,//luma_offset
+ 0xdc,0x30,
+ 0xdd,0xb8,//edge_sa_g,b
+ 0xfe,0x00,
+ ////,dd/////////
+ 0xfe,0x02,
+ 0x88,0x1c,//dn_b_base
+ 0x8c,0xf6, //[2]b_in_dark_inc
+ 0x89,0x03, //dn_c_weight
+ ////,E /////////
+ 0xfe,0x02,
+ 0x90,0x6c,// EEINTP mode1
+ 0x97,0x75,// edge effect
+ //==,=======RGB Gamma
+ 0xfe,0x02,
+ 0x15,0x0a,
+ 0x16,0x12,
+ 0x17,0x19,
+ 0x18,0x1f,
+ 0x19,0x2c,
+ 0x1a,0x38,
+ 0x1b,0x42,
+ 0x1c,0x4e,
+ 0x1d,0x63,
+ 0x1e,0x76,
+ 0x1f,0x87,
+ 0x20,0x96,
+ 0x21,0xa2,
+ 0x22,0xb8,
+ 0x23,0xca,
+ 0x24,0xd8,
+ 0x25,0xe3,
+ 0x26,0xf0,
+ 0x27,0xf8,
+ 0x28,0xfd,
+ 0x29,0xff,
+
+ //===,=========y gamma
+ 0xfe,0x02,
+ 0x2b,0x00,
+ 0x2c,0x04,
+ 0x2d,0x09,
+ 0x2e,0x18,
+ 0x2f,0x27,
+ 0x30,0x37,
+ 0x31,0x49,
+ 0x32,0x5c,
+ 0x33,0x7e,
+ 0x34,0xa0,
+ 0x35,0xc0,
+ 0x36,0xe0,
+ 0x37,0xff,
+ ///1,x120ize//
+ 0xfe,0x00,//
+ 0x90,0x01, //0x//crop enable
+ 0x95,0x04, //0x//1600x1200
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+ 0xad,0x7d,////////////////////////////////////////////////////////R-ratio
+ 0xfe,0x03,
+ 0x42,0x40,
+ 0x43,0x06, //output buf width
+ 0x41,0x02, // Pclk_polarity
+ 0x40,0x40, //00
+ 0x17,0x00, //widv
+ 0xfe,0x00,
+ //ou,t DV////
+
+ 0xfe,0x00,
+ 0x82,0xfe, // fe
+ 0xf2,0x70,
+ 0xf3,0xff,
+ 0xf4,0x00,
+ 0xf5,0x30,
+
+
+
+ 0xfe,0x00,
+ 0x0c,0x00,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+
+ 0xc8,0x15,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x00,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+ 0xb6,0x03,
+
+#if 1
+ //// re zao///
+ 0xfe ,0x00,
+ 0x22 ,0xf0,
+ 0xfe ,0x01,
+ 0x21 ,0xff,
+ 0xfe ,0x02,
+ 0x8a ,0x33,
+ 0x8c ,0x76,
+ 0x8d ,0x85,
+ 0xa6 ,0xf0,
+ 0xae ,0x9f,
+ 0xa2 ,0x90,
+ 0xa5 ,0x40,
+ 0xa7 ,0x30,
+ 0xb0 ,0x88,
+ 0x38 ,0x0b,
+ 0x39 ,0x30,
+ 0xfe ,0x00,
+ 0x87 ,0xb0,
+
+
+
+ /* //, sma RGB gamma////
+ 0xfe,0x02,
+ 0x15,0x0b,
+ 0x16,0x0e,
+ 0x17,0x10,
+ 0x18,0x12,
+ 0x19,0x19,
+ 0x1a,0x21,
+ 0x1b,0x29,
+ 0x1c,0x31,
+ 0x1d,0x41,
+ 0x1e,0x50,
+ 0x1f,0x5f,
+ 0x20,0x6d,
+ 0x21,0x79,
+ 0x22,0x91,
+ 0x23,0xa5,
+ 0x24,0xb9,
+ 0x25,0xc9,
+ 0x26,0xe1,
+ 0x27,0xee,
+ 0x28,0xf7,
+ 0x29,0xff,
+ */
+ //da,sun///
+ 0xfe,0x02,
+ 0x40,0x06,
+ 0x41,0x23,
+ 0x42,0x3f,
+ 0x43,0x06,
+ 0x44,0x00,
+ 0x45,0x00,
+ 0x46,0x14,
+ 0x47,0x09,
+
+ #endif
+};
+
+uint8_t gc2035_default_regs_exit[]={
+ 0xfe, 0x00,
+ 0xf1, 0x00,
+ 0xf2, 0x00,
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-v7_maz7z233.h b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-v7_maz7z233.h
new file mode 100755
index 00000000..7d7d4f27
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-v7_maz7z233.h
@@ -0,0 +1,1105 @@
+#ifndef GC2035_H
+#define GC2035_H
+
+
+// Scene Mode
+uint8_t gc2035_scene_mode_auto[] = {
+ 0xfe,0x01,
+ 0x3e,0x40,
+ 0x1f,0xa0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x50,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe,0x00,
+};
+
+uint8_t gc2035_scene_mode_night[] = {
+ 0xfe, 0x01,
+ 0x3e, 0x60,
+ 0x1f,0xe0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x80,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe, 0x00,
+};
+
+
+// White Balance
+uint8_t gc2035_wb_auto [] = {
+ 0xfe, 0x00,
+// 0xb3, 0x61, //2013-8-16 dustin
+// 0xb4, 0x40,
+// 0xb5, 0x61,
+ 0x82, 0xfe,
+};
+
+uint8_t gc2035_wb_incandescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0xa8,
+};
+
+uint8_t gc2035_wb_fluorescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0x78,
+};
+
+uint8_t gc2035_wb_daylight [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x78,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+uint8_t gc2035_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x58,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+
+// Exposure
+uint8_t gc2035_exposure_neg6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x68,
+ 0xfe, 0x02,
+ 0xd5, 0xe0,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc2035_exposure_neg3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x70,
+ 0xfe, 0x02,
+ 0xd5, 0xf0,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc2035_exposure_zero[] = {
+ 0xfe, 0x01,
+ 0x13, 0x78,
+ 0xfe, 0x02,
+ 0xd5, 0xf8,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_pos3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x80,
+ 0xfe, 0x02,
+ 0xd5, 0x10,
+ 0xfe, 0x00,
+
+
+};
+
+uint8_t gc2035_exposure_pos6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x88,
+ 0xfe, 0x02,
+ 0xd5, 0x20,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_antibanding_auto[] = {
+ 0xfe, 0x00,
+ 0x05, 0x01,
+ 0x06, 0x39,
+ 0x07, 0x00,
+ 0x08, 0x14,
+ 0xfe, 0x01,
+ 0x27, 0x00,
+ 0x28, 0x8b,
+ 0x29, 0x04,
+ 0x2a, 0x58,
+ 0x2b, 0x04,
+ 0x2c, 0x58,
+ 0x2d, 0x06,
+ 0x2e, 0x84,
+ 0x2f, 0x08,
+ 0x30, 0xb0,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_antibanding_50hz[] = {
+ 0xfe , 0x00,
+ 0x05 , 0x01,// HB
+ 0x06 , 0x23,
+ 0x07 , 0x00,// VB
+ 0x08 , 0x14,
+ 0xfe , 0x01,
+ 0x27 , 0x00,// step
+ 0x28 , 0xaa,
+ 0x29 , 0x04,// level 0 13.75
+ 0x2a , 0xa6,
+ 0x2b , 0x07,// level 1 13.75 // 4a6
+ 0x2c , 0xf8,
+ 0x2d , 0x07,// level 2 10 // 6a4
+ 0x2e , 0xf8,
+ 0x2f , 0x08,// level 3 7.5
+ 0x30 , 0xa2,
+ 0xfe , 0x00,
+
+
+};
+
+uint8_t gc2035_antibanding_60hz[] = {
+ 0xfe, 0x00,
+ 0x05, 0x01,
+ 0x06, 0x39,
+ 0x07, 0x00,
+ 0x08, 0x14,
+ 0xfe, 0x01,
+ 0x27, 0x00,
+ 0x28, 0x8b,
+ 0x29, 0x04,
+ 0x2a, 0x58,
+ 0x2b, 0x04,
+ 0x2c, 0x58,
+ 0x2d, 0x06,
+ 0x2e, 0x84,
+ 0x2f, 0x08,
+ 0x30, 0xb0,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_antibanding_off[] = {
+ 0xfe , 0x00,
+ 0x05 , 0x01,// HB
+ 0x06 , 0x23,
+ 0x07 , 0x00,// VB
+ 0x08 , 0x14,
+ 0xfe , 0x01,
+ 0x27 , 0x00,// step
+ 0x28 , 0xaa,
+ 0x29 , 0x04,// level 0 13.75
+ 0x2a , 0xa6,
+ 0x2b , 0x04,// level 1 13.75
+ 0x2c , 0xa6,
+ 0x2d , 0x06,// level 2 10
+ 0x2e , 0xa4,
+ 0x2f , 0x08,// level 3 7.5
+ 0x30 , 0xa2,
+ 0xfe , 0x00,
+};
+
+uint8_t gc2035_176_144_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x33,
+ 0x9b,0x00,
+ 0x9f,0x00,
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0x92,//600
+ 0x97,0x00,
+ 0x98,0xb0,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+
+ 0xfe,0x03,
+ 0x42,0x60,
+ 0x43,0x01,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_320_240_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x22,
+ 0x9b,0x00,
+ 0x9f,0x00,
+
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0xf2,//600
+ 0x97,0x01,
+ 0x98,0x40,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x02,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_1280_720_regs[]={
+ 0xfe,0x00,
+ 0x0a,0xf0,
+ 0x0c,0xa0,
+ 0x0d,0x02,
+ 0x0e,0xd8,
+ 0x0f,0x05,
+ 0x10,0x18,
+ 0xfe,0x00,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x05,0x01,
+ 0x06,0xaa,
+ 0x07,0x00,
+ 0x08,0xf6,
+ 0xfe,0x01,
+ 0x27,0x00,
+ 0x28,0xa1,
+ 0x29,0x03,
+ 0x2a,0xc6,
+ 0x2b,0x04,
+ 0x2c,0x67,
+ 0x2d,0x06,
+ 0x2e,0x4a,
+ 0x2f,0x07,
+ 0x30,0x8c,
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xb6,0x03,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x99,0x11,
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0xd0,
+ 0x97,0x05,
+ 0x98,0x00,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+ 0xfa,0x11,
+};
+
+uint8_t gc2035_1600_1200_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x02,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x04,
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+
+ 0xc8,0x00,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+};
+
+uint8_t gc2035_800_600_regs[]={
+ 0xfe,0x00,
+ 0xfa,0x00,
+ 0xc8,0x00,
+
+ 0x99,0x22,
+ 0x9a,0x07,
+ 0x9b,0x00,
+ 0x9c,0x00,
+ 0x9d,0x00,
+ 0x9e,0x00,
+ 0x9f,0x00,
+ 0xa0,0x00,
+ 0xa1,0x00,
+ 0xa2,0x00,
+
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0x58,
+ 0x97,0x03,
+ 0x98,0x20,
+};
+
+uint8_t gc2035_640_480_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x00,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+
+ 0xc8,0x15,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x00,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+ 0xb6,0x03,
+};
+
+uint8_t gc2035_default_regs_init[]={
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfc,0x06,
+ 0xf2,0x00,
+ 0xf3,0x00,
+ 0xf4,0x00,
+ 0xf5,0x00,
+ 0xf9,0xfe, //[0] pll enable
+ 0xfa,0x00,
+ 0xf6,0x00,
+ 0xf7,0x15, //pll enable
+
+ 0xf8,0x84,//85
+ 0xfe,0x00,
+ 0x82,0x00,
+ 0xb3,0x60,
+ 0xb4,0x40,
+ 0xb5,0x60,
+
+ 0x03,0x02,
+ 0x04,0x71,
+
+ ////,/meare window ///////////
+ 0xfe,0x00,
+ 0xec,0x06,//04
+ 0xed,0x06,//04
+ 0xee,0x62,//60
+ 0xef,0x92,//90
+
+ ////,//anog/////////////
+ 0x0a,0x00, //row start
+ 0x0c,0x00, //col start
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06, //Window setting
+ 0x10,0x58,
+ 0x17,0x14, //[0]mirror [1]flip
+ 0x18,0x0a, //0a 2012.10.26
+ 0x19,0x0a, //AD pipe number
+ 0x1a,0x01, //CISCTL mode4
+ 0x1b,0x8b,
+ 0x1c,0x05,//add by lanking 20130403
+ 0x1e,0x88, //analog mode1 [7] tx-high en [5:3]COL_bias
+ 0x1f,0x08, //[3] tx-low en//
+ 0x20,0x05, //[0]adclk mode},{0x[1]rowclk_MODE [2]rsthigh_en
+ 0x21,0x0f, //[6:4]rsg
+ 0x22,0xf0, //[3:0]vref 20130403
+ 0x23,0xc3, //f3//ADC_r
+ 0x24,0x17, //pad drive 16
+
+
+ 0xfe,0x01,
+ 0x11,0x20,//AEC_out_slope},{0x
+ 0x1f,0xb8,//max_post_gain
+ 0x20,0x50,//max_pre_gain // 48
+ 0x47,0x30,//AEC_outdoor_th
+ 0x0b,0x10,//
+ 0x13,0x78,//y_target 84
+ 0xfe,0x00,
+
+#if 1
+ 0x05,0x01,//hb
+ 0x06,0x11,
+ 0x07,0x00,//vb
+ 0x08,0x50,
+ 0xfe,0x01,
+ 0x27,0x00,//step
+ 0x28,0xa0,
+ 0x29,0x05,//level1
+ 0x2a,0x00,
+ 0x2b,0x05,//level2
+ 0x2c,0x00,
+ 0x2d,0x06,//6e8//level3
+ 0x2e,0xe0,
+ 0x2f,0x0a,//level4
+ 0x30,0x00,
+#endif
+ 0x05,0x01,//hb
+ 0x06,0x5c,
+ 0x07,0x00,//vb
+ 0x08,0x32,
+ 0xfe,0x01,
+ 0x27,0x00,//step
+ 0x28,0x7d,
+ 0x29,0x04,//level1
+ 0x2a,0xe2,
+ 0x2b,0x05,//level2
+ 0x2c,0xdc,
+ 0x2d,0x05,//6e8//level3
+ 0x2e,0xdc,
+ 0x2f,0x09,//level4
+ 0x30,0xc4,
+
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xfe,0x00, //0x},{0x},{0x},{0x},{0x
+ 0xb6,0x03, //AEC enable
+ 0xfe,0x00,
+
+ ////,BLK////
+ 0x3f,0x00, //prc close
+ 0x40,0x77,//
+ 0x42,0x7f,
+ 0x43,0x30,
+ 0x5c,0x08,
+ 0x5e,0x20,
+ 0x5f,0x20,
+ 0x60,0x20,
+ 0x61,0x20,
+ 0x62,0x20,
+ 0x63,0x20,
+ 0x64,0x20,
+ 0x65,0x20,
+
+ //blo,/////////
+ 0x80,0xff,
+ 0x81,0x26,//38},{0x//skin_Y 8c_debug
+ 0x87,0x90, //[7]middle gamma
+ 0x84,0x02, //output put foramat
+ 0x86,0x02, //02 //sync plority
+ 0x8b,0xbc,
+ 0xb0,0x80, //globle gain
+ 0xc0,0x40,//Yuv bypass
+
+ ////lsc///////////
+ 0xfe,0x01,
+ 0xc2,0x38,
+ 0xc3,0x25,
+ 0xc4,0x21,
+ 0xc8,0x19,
+ 0xc9,0x12,
+ 0xca,0x0e,
+ 0xbc,0x43,
+ 0xbd,0x18,
+ 0xbe,0x1b,
+ 0xb6,0x40,
+ 0xb7,0x2e,
+ 0xb8,0x26,
+ 0xc5,0x05,
+ 0xc6,0x03,
+ 0xc7,0x04,
+ 0xcb,0x00,
+ 0xcc,0x00,
+ 0xcd,0x00,
+ 0xbf,0x14,
+ 0xc0,0x22,
+ 0xc1,0x1b,
+ 0xb9,0x00,
+ 0xba,0x05,
+ 0xbb,0x05,
+ 0xaa,0x35,
+ 0xab,0x33,
+ 0xac,0x33,
+ 0xad,0x25,
+ 0xae,0x22,
+ 0xaf,0x27,
+ 0xb0,0x1d,
+ 0xb1,0x20,
+ 0xb2,0x22,
+ 0xb3,0x14,
+ 0xb4,0x15,
+ 0xb5,0x16,
+ 0xd0,0x00,
+ 0xd2,0x07,
+ 0xd3,0x08,
+ 0xd8,0x00,
+ 0xda,0x13,
+ 0xdb,0x17,
+ 0xdc,0x00,
+ 0xde,0x0a,
+ 0xdf,0x08,
+ 0xd4,0x00,
+ 0xd6,0x00,
+ 0xd7,0x0c,
+ 0xa4,0x00,
+ 0xa5,0x00,
+ 0xa6,0x00,
+ 0xa7,0x00,
+ 0xa8,0x00,
+ 0xa9,0x00,
+ 0xa1,0x80,
+ 0xa2,0x80,
+
+ ////,/cc////////////
+ 0xfe,0x02,
+ 0xc0,0x01,
+ 0xc1,0x40, //Green_cc for d
+ 0xc2,0xfc,
+ 0xc3,0x05,
+ 0xc4,0xec,
+ 0xc5,0x42,
+ 0xc6,0xf8,
+ 0xc7,0x40,//for cwf
+ 0xc8,0xf8,
+ 0xc9,0x06,
+ 0xca,0xfd,
+ 0xcb,0x3e,
+ 0xcc,0xf3,
+ 0xcd,0x36,//for A
+ 0xce,0xf6,
+ 0xcf,0x04,
+ 0xe3,0x0c,
+ 0xe4,0x44,
+ 0xe5,0xe5,
+ 0xfe,0x00,
+
+ ////,b stt ////////////////AWB ,ar
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x10, // 10
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x20, // 20
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00, // 30
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x40, // 40
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x50, // 50
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x60, // 60
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x70, // 70
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x80, // 80
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x90, // 90
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xa0, // a0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xb0, // b0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xc0, // c0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xd0, // d0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4f,0x01,
+ ////,wb vue////////
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x40,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x53,
+ 0x4e,0x08,
+ 0x4e,0x04,
+ 0x4d,0x62,
+ 0x4e,0x10,
+ 0x4d,0x72,
+ 0x4e,0x20,
+ 0x4f,0x01,
+
+ ///a,///
+ 0xfe,0x01,
+ 0x50,0x88,//c0//[6]green mode
+ 0x52,0x40,
+ 0x54,0x60,
+ 0x56,0x06,
+ 0x57,0x20, //pre adjust
+ 0x58,0x01,
+ 0x5b,0x02, //AWB_gain_delta
+ 0x61,0xaa,//R/G stand
+ 0x62,0xaa,//R/G stand
+ 0x71,0x00,
+ 0x74,0x10, //0x//AWB_C_max
+ 0x77,0x08, // 0x//AWB_p2_x
+ 0x78,0xfd, //AWB_p2_y
+ 0x86,0x30,
+ 0x87,0x00,
+ 0x88,0x04,//06},{0x//[1]dark mode
+ 0x8a,0xc0,//awb move mode
+ 0x89,0x75,
+ 0x84,0x08, //0x//auto_window
+ 0x8b,0x00, // 0x//awb compare luma
+ 0x8d,0x70, //awb gain limit R
+ 0x8e,0x70,//G
+ 0x8f,0xf4,//B
+ 0xfe,0x00,
+ 0x82,0x02,//awb_en
+ ////wb end /////////////
+
+ //===,====de
+ 0xfe,0x01,
+
+ 0x9c,0x04,//00
+
+ 0x21,0xbf,
+ 0xfe,0x02,
+ 0xa4,0x00,//
+ 0xa5,0x40, //lsc_th
+ 0xa2,0xa0, //lsc_dec_slope
+ 0xa6,0x80, //dd_th
+ 0xa7,0x80, //ot_th
+ 0xab,0x31, //
+ 0xa9,0x6f, //
+ 0xb0,0x99, //0x//edge effect slope low
+ 0xb1,0x34,//edge effect slope low
+ 0xb3,0x80, //saturation dec slope
+ 0xde,0xb6, //
+ 0x38,0x0f, //
+ 0x39,0x60, //
+ 0xfe,0x00,
+ 0x81,0x26,
+ 0xfe,0x02,
+ 0x83,0x00,//
+ 0x84,0x45,//
+ ////,///Y//////////
+ 0xd1,0x34,//saturation_cb
+ 0xd2,0x34,//saturation_Cr
+ 0xd3,0x45,//contrast ? {{0xd4},{0x80}},//contrast center // 40
+ 0xd5,0xfa,//luma_offset // f8
+ 0xdc,0x30,
+ 0xdd,0xb8,//edge_sa_g,b
+ 0xfe,0x00,
+ ////,dd/////////
+ 0xfe,0x02,
+ 0x88,0x08,//dn_b_base // 1c
+ 0x8c,0xf6, //[2]b_in_dark_inc
+ 0x89,0x03, //dn_c_weight
+ ////,E /////////
+ 0xfe,0x02,
+ 0x90,0x6c,// EEINTP mode1
+ 0x97,0x78,// edge effect // 45
+ //==,=======RGB Gamma
+ 0xfe,0x02,
+ 0x15,0x0a,
+ 0x16,0x12,
+ 0x17,0x19,
+ 0x18,0x1f,
+ 0x19,0x2c,
+ 0x1a,0x38,
+ 0x1b,0x42,
+ 0x1c,0x4e,
+ 0x1d,0x63,
+ 0x1e,0x76,
+ 0x1f,0x87,
+ 0x20,0x96,
+ 0x21,0xa2,
+ 0x22,0xb8,
+ 0x23,0xca,
+ 0x24,0xd8,
+ 0x25,0xe3,
+ 0x26,0xf0,
+ 0x27,0xf8,
+ 0x28,0xfd,
+ 0x29,0xff,
+
+ //===,=========y gamma
+ 0xfe,0x02,
+ 0x2b,0x00,
+ 0x2c,0x04,
+ 0x2d,0x09,
+ 0x2e,0x18,
+ 0x2f,0x27,
+ 0x30,0x37,
+ 0x31,0x49,
+ 0x32,0x5c,
+ 0x33,0x7e,
+ 0x34,0xa0,
+ 0x35,0xc0,
+ 0x36,0xe0,
+ 0x37,0xff,
+ ///1,x120ize//
+ 0xfe,0x00,//
+ 0x90,0x01, //0x//crop enable
+ 0x95,0x04, //0x//1600x1200
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+
+ 0xfe,0x03,
+ 0x42,0x40,
+ 0x43,0x06, //output buf width
+ 0x41,0x02, // Pclk_polarity
+ 0x40,0x40, //00
+ 0x17,0x00, //widv
+ 0xfe,0x00,
+ //ou,t DV////
+
+ 0xfe,0x00,
+ 0x82,0xfe, // fe
+ 0xf2,0x70,
+ 0xf3,0xff,
+ 0xf4,0x00,
+ 0xf5,0x30,
+
+ #if 0
+ //,///ssumple 640X4800//////
+ 0xfe,0x00,
+ 0xfa,0x00,
+ 0xc8,0x00,
+
+ 0x99,0x22,
+ 0x9a,0x07,
+ 0x9b,0x00,
+ 0x9c,0x00,
+ 0x9d,0x00,
+ 0x9e,0x00,
+ 0x9f,0x00,
+ 0xa0,0x00,
+ 0xa1,0x00,
+ 0xa2,0x00,
+
+
+ 0x90,0x01,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+ #endif
+
+
+ 0xfe,0x00,
+ 0x0c,0x00,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+
+ 0xc8,0x15,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x00,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+ 0xb6,0x03,
+
+#if 1
+ //// re zao///
+ 0xfe ,0x00,
+ 0x22 ,0xf0,
+ 0xfe ,0x01,
+ 0x21 ,0xff,
+ 0xfe ,0x02,
+ 0x8a ,0x33,
+ 0x8c ,0x76,
+ 0x8d ,0x85,
+ 0xa6 ,0xf0,
+ 0xae ,0x9f,
+ 0xa2 ,0x90,
+ 0xa5 ,0x40,
+ 0xa7 ,0x30,
+ 0xb0 ,0x88,
+ 0x38 ,0x0b,
+ 0x39 ,0x30,
+ 0xfe ,0x00,
+ 0x87 ,0xb0,
+
+
+ /*
+ //, sma RGB gamma////
+ 0xfe,0x02,
+ 0x15,0x0b,
+ 0x16,0x0e,
+ 0x17,0x10,
+ 0x18,0x12,
+ 0x19,0x19,
+ 0x1a,0x21,
+ 0x1b,0x29,
+ 0x1c,0x31,
+ 0x1d,0x41,
+ 0x1e,0x50,
+ 0x1f,0x5f,
+ 0x20,0x6d,
+ 0x21,0x79,
+ 0x22,0x91,
+ 0x23,0xa5,
+ 0x24,0xb9,
+ 0x25,0xc9,
+ 0x26,0xe1,
+ 0x27,0xee,
+ 0x28,0xf7,
+ 0x29,0xff,*/
+
+ //da,sun///
+ 0xfe,0x02,
+ 0x40,0x06,
+ 0x41,0x23,
+ 0x42,0x3f,
+ 0x43,0x06,
+ 0x44,0x00,
+ 0x45,0x00,
+ 0x46,0x14,
+ 0x47,0x09,
+
+ #endif
+};
+
+uint8_t gc2035_default_regs_exit[]={
+ 0xfe, 0x00,
+ 0xf1, 0x00,
+ 0xf2, 0x00,
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-xhb_gbeffz2743l.h b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-xhb_gbeffz2743l.h
new file mode 100755
index 00000000..afb142a6
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-xhb_gbeffz2743l.h
@@ -0,0 +1,1023 @@
+#ifndef GC2035_H
+#define GC2035_H
+
+
+// Scene Mode
+uint8_t gc2035_scene_mode_auto[] = {
+ 0xfe,0x01,
+ 0x3e,0x40,
+ 0x1f,0xa0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x50,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe,0x00,
+};
+
+uint8_t gc2035_scene_mode_night[] = {
+ 0xfe, 0x01,
+ 0x3e, 0x60,
+ 0x1f,0xe0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x80,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe, 0x00,
+};
+
+
+// White Balance
+uint8_t gc2035_wb_auto [] = {
+ 0xfe, 0x00,
+// 0xb3, 0x61, //2013-8-16 dustin
+// 0xb4, 0x40,
+// 0xb5, 0x61,
+ 0x82, 0xfe,
+};
+
+uint8_t gc2035_wb_incandescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0xa8,
+};
+
+uint8_t gc2035_wb_fluorescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0x78,
+};
+
+uint8_t gc2035_wb_daylight [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x78,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+uint8_t gc2035_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x58,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+
+// Exposure
+uint8_t gc2035_exposure_neg6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x60,
+ 0xfe, 0x02,
+ 0xd5, 0xe0,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc2035_exposure_neg3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x78,
+ 0xfe, 0x02,
+ 0xd5, 0xf0,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc2035_exposure_zero[] = {
+ 0xfe, 0x01,
+ 0x13, 0x84,
+ 0xfe, 0x02,
+ 0xd5, 0xf8,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_pos3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x88,
+ 0xfe, 0x02,
+ 0xd5, 0x10,
+ 0xfe, 0x00,
+
+
+};
+
+uint8_t gc2035_exposure_pos6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x90,
+ 0xfe, 0x02,
+ 0xd5, 0x20,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_176_144_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x33,
+ 0x9b,0x00,
+ 0x9f,0x00,
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0x92,//600
+ 0x97,0x00,
+ 0x98,0xb0,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+
+ 0xfe,0x03,
+ 0x42,0x60,
+ 0x43,0x01,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_320_240_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x22,
+ 0x9b,0x00,
+ 0x9f,0x00,
+
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0xf2,//600
+ 0x97,0x01,
+ 0x98,0x40,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x02,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_1280_720_regs[]={
+ 0xfe,0x00,
+ 0x0a,0xf0,
+ 0x0c,0xa0,
+ 0x0d,0x02,
+ 0x0e,0xd8,
+ 0x0f,0x05,
+ 0x10,0x18,
+ 0xfe,0x00,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x05,0x01,
+ 0x06,0xaa,
+ 0x07,0x00,
+ 0x08,0xf6,
+ 0xfe,0x01,
+ 0x27,0x00,
+ 0x28,0xa1,
+ 0x29,0x03,
+ 0x2a,0xc6,
+ 0x2b,0x04,
+ 0x2c,0x67,
+ 0x2d,0x06,
+ 0x2e,0x4a,
+ 0x2f,0x07,
+ 0x30,0x8c,
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xb6,0x03,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x99,0x11,
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0xd0,
+ 0x97,0x05,
+ 0x98,0x00,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+ 0xfa,0x11,
+};
+
+uint8_t gc2035_1600_1200_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x02,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x04,
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+
+ 0xc8,0x00,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+};
+
+uint8_t gc2035_800_600_regs[]={
+ 0xfe,0x00,
+ 0xfa,0x00,
+ 0xc8,0x00,
+
+ 0x99,0x22,
+ 0x9a,0x07,
+ 0x9b,0x00,
+ 0x9c,0x00,
+ 0x9d,0x00,
+ 0x9e,0x00,
+ 0x9f,0x00,
+ 0xa0,0x00,
+ 0xa1,0x00,
+ 0xa2,0x00,
+
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0x58,
+ 0x97,0x03,
+ 0x98,0x20,
+};
+
+uint8_t gc2035_640_480_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x00,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+
+ 0xc8,0x15,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x00,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+ 0xb6,0x03,
+};
+
+uint8_t gc2035_default_regs_init[]={
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfc,0x06,
+ 0xf2,0x00,
+ 0xf3,0x00,
+ 0xf4,0x00,
+ 0xf5,0x00,
+ 0xf9,0xfe, //[0] pll enable
+ 0xfa,0x00,
+ 0xf6,0x00,
+ 0xf7,0x15, //pll enable
+
+ 0xf8,0x84,//85
+ 0xfe,0x00,
+ 0x82,0x00,
+ 0xb3,0x60,
+ 0xb4,0x40,
+ 0xb5,0x60,
+
+ 0x03,0x02,
+ 0x04,0x71,
+
+ ////,/meare window ///////////
+ 0xfe,0x00,
+ 0xec,0x06,//04
+ 0xed,0x06,//04
+ 0xee,0x62,//60
+ 0xef,0x92,//90
+
+ ////,//anog/////////////
+ 0x0a,0x00, //row start
+ 0x0c,0x00, //col start
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06, //Window setting
+ 0x10,0x58,
+ 0x17,0x14, //[0]mirror [1]flip
+ 0x18,0x0a, //0a 2012.10.26
+ 0x19,0x0a, //AD pipe number
+ 0x1a,0x01, //CISCTL mode4
+ 0x1b,0x8b,
+ 0x1c,0x05,//add by lanking 20130403
+ 0x1e,0x88, //analog mode1 [7] tx-high en [5:3]COL_bias
+ 0x1f,0x08, //[3] tx-low en//
+ 0x20,0x05, //[0]adclk mode},{0x[1]rowclk_MODE [2]rsthigh_en
+ 0x21,0x0f, //[6:4]rsg
+ 0x22,0xf0, //[3:0]vref 20130403
+ 0x23,0xc3, //f3//ADC_r
+ 0x24,0x17, //pad drive 16
+
+
+ 0xfe,0x01,
+ 0x11,0x20,//AEC_out_slope},{0x
+ 0x1f,0xc0,//max_post_gain
+ 0x20,0x48,//max_pre_gain
+ 0x47,0x30,//AEC_outdoor_th
+ 0x0b,0x10,//
+ 0x13,0x84,//y_target
+ 0xfe,0x00,
+
+#if 0
+ 0x05,0x01,//hb
+ 0x06,0x11,
+ 0x07,0x00,//vb
+ 0x08,0x50,
+ 0xfe,0x01,
+ 0x27,0x00,//step
+ 0x28,0xa0,
+ 0x29,0x05,//level1
+ 0x2a,0x00,
+ 0x2b,0x05,//level2
+ 0x2c,0x00,
+ 0x2d,0x06,//6e8//level3
+ 0x2e,0xe0,
+ 0x2f,0x0a,//level4
+ 0x30,0x00,
+#endif
+ 0x05,0x01,//hb
+ 0x06,0x5c,
+ 0x07,0x00,//vb
+ 0x08,0x32,
+ 0xfe,0x01,
+ 0x27,0x00,//step
+ 0x28,0x7d,
+ 0x29,0x05,//level1
+ 0x2a,0xdc,
+ 0x2b,0x06,//level2
+ 0x2c,0x59,
+ 0x2d,0x06,//6e8//level3
+ 0x2e,0xd6,
+ 0x2f,0x09,//level4
+ 0x30,0xc4,
+
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xfe,0x00, //0x},{0x},{0x},{0x},{0x
+ 0xb6,0x03, //AEC enable
+ 0xfe,0x00,
+
+ ////,BLK////
+ 0x3f,0x00, //prc close
+ 0x40,0x77,//
+ 0x42,0x7f,
+ 0x43,0x30,
+ 0x5c,0x08,
+ 0x5e,0x20,
+ 0x5f,0x20,
+ 0x60,0x20,
+ 0x61,0x20,
+ 0x62,0x20,
+ 0x63,0x20,
+ 0x64,0x20,
+ 0x65,0x20,
+
+ //blo,/////////
+ 0x80,0xff,
+ 0x81,0x26,//38},{0x//skin_Y 8c_debug
+ 0x87,0x90, //[7]middle gamma
+ 0x84,0x02, //output put foramat
+ 0x86,0x02, //02 //sync plority
+ 0x8b,0xbc,
+ 0xb0,0x80, //globle gain
+ 0xc0,0x40,//Yuv bypass
+
+ ////lsc///////////
+ 0xfe,0x01,
+ 0xc2,0x38,
+ 0xc3,0x25,
+ 0xc4,0x21,
+ 0xc8,0x19,
+ 0xc9,0x12,
+ 0xca,0x0e,
+ 0xbc,0x43,
+ 0xbd,0x18,
+ 0xbe,0x1b,
+ 0xb6,0x40,
+ 0xb7,0x2e,
+ 0xb8,0x26,
+ 0xc5,0x05,
+ 0xc6,0x03,
+ 0xc7,0x04,
+ 0xcb,0x00,
+ 0xcc,0x00,
+ 0xcd,0x00,
+ 0xbf,0x14,
+ 0xc0,0x22,
+ 0xc1,0x1b,
+ 0xb9,0x00,
+ 0xba,0x05,
+ 0xbb,0x05,
+ 0xaa,0x35,
+ 0xab,0x33,
+ 0xac,0x33,
+ 0xad,0x25,
+ 0xae,0x22,
+ 0xaf,0x27,
+ 0xb0,0x1d,
+ 0xb1,0x20,
+ 0xb2,0x22,
+ 0xb3,0x14,
+ 0xb4,0x15,
+ 0xb5,0x16,
+ 0xd0,0x00,
+ 0xd2,0x07,
+ 0xd3,0x08,
+ 0xd8,0x00,
+ 0xda,0x13,
+ 0xdb,0x17,
+ 0xdc,0x00,
+ 0xde,0x0a,
+ 0xdf,0x08,
+ 0xd4,0x00,
+ 0xd6,0x00,
+ 0xd7,0x0c,
+ 0xa4,0x00,
+ 0xa5,0x00,
+ 0xa6,0x00,
+ 0xa7,0x00,
+ 0xa8,0x00,
+ 0xa9,0x00,
+ 0xa1,0x80,
+ 0xa2,0x80,
+
+ ////,/cc////////////
+ 0xfe,0x02,
+ 0xc0,0x01,
+ 0xc1,0x40, //Green_cc for d
+ 0xc2,0xfc,
+ 0xc3,0x05,
+ 0xc4,0xec,
+ 0xc5,0x42,
+ 0xc6,0xf8,
+ 0xc7,0x40,//for cwf
+ 0xc8,0xf8,
+ 0xc9,0x06,
+ 0xca,0xfd,
+ 0xcb,0x3e,
+ 0xcc,0xf3,
+ 0xcd,0x36,//for A
+ 0xce,0xf6,
+ 0xcf,0x04,
+ 0xe3,0x0c,
+ 0xe4,0x44,
+ 0xe5,0xe5,
+ 0xfe,0x00,
+
+ ////,b stt ////////////////AWB ,ar
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x10, // 10
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x20, // 20
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00, // 30
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x40, // 40
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x50, // 50
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x60, // 60
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x70, // 70
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x80, // 80
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x90, // 90
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xa0, // a0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xb0, // b0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xc0, // c0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xd0, // d0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4f,0x01,
+ ////,wb vue////////
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x40,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x53,
+ 0x4e,0x08,
+ 0x4e,0x04,
+ 0x4d,0x62,
+ 0x4e,0x10,
+ 0x4d,0x72,
+ 0x4e,0x20,
+ 0x4f,0x01,
+
+ ///a,///
+ 0xfe,0x01,
+ 0x50,0x88,//c0//[6]green mode
+ 0x52,0x40,
+ 0x54,0x60,
+ 0x56,0x06,
+ 0x57,0x20, //pre adjust
+ 0x58,0x01,
+ 0x5b,0x02, //AWB_gain_delta
+ 0x61,0xaa,//R/G stand
+ 0x62,0xaa,//R/G stand
+ 0x71,0x00,
+ 0x74,0x10, //0x//AWB_C_max
+ 0x77,0x08, // 0x//AWB_p2_x
+ 0x78,0xfd, //AWB_p2_y
+ 0x86,0x30,
+ 0x87,0x00,
+ 0x88,0x04,//06},{0x//[1]dark mode
+ 0x8a,0xc0,//awb move mode
+ 0x89,0x75,
+ 0x84,0x08, //0x//auto_window
+ 0x8b,0x00, // 0x//awb compare luma
+ 0x8d,0x70, //awb gain limit R
+ 0x8e,0x70,//G
+ 0x8f,0xf4,//B
+ 0xfe,0x00,
+ 0x82,0x02,//awb_en
+ ////wb end /////////////
+
+ //===,====de
+ 0xfe,0x01,
+
+ 0x9c,0x04,//00
+
+ 0x21,0xbf,
+ 0xfe,0x02,
+ 0xa4,0x00,//
+ 0xa5,0x40, //lsc_th
+ 0xa2,0xa0, //lsc_dec_slope
+ 0xa6,0x80, //dd_th
+ 0xa7,0x80, //ot_th
+ 0xab,0x31, //
+ 0xa9,0x6f, //
+ 0xb0,0x99, //0x//edge effect slope low
+ 0xb1,0x34,//edge effect slope low
+ 0xb3,0x80, //saturation dec slope
+ 0xde,0xb6, //
+ 0x38,0x0f, //
+ 0x39,0x60, //
+ 0xfe,0x00,
+ 0x81,0x26,
+ 0xfe,0x02,
+ 0x83,0x00,//
+ 0x84,0x45,//
+ ////,///Y//////////
+ 0xd1,0x34,//saturation_cb
+ 0xd2,0x34,//saturation_Cr
+ 0xd3,0x40,//contrast ? {{0xd4},{0x80}},//contrast center
+ 0xd5,0xf8,//luma_offset
+ 0xdc,0x30,
+ 0xdd,0xb8,//edge_sa_g,b
+ 0xfe,0x00,
+ ////,dd/////////
+ 0xfe,0x02,
+ 0x88,0x1c,//dn_b_base
+ 0x8c,0xf6, //[2]b_in_dark_inc
+ 0x89,0x03, //dn_c_weight
+ ////,E /////////
+ 0xfe,0x02,
+ 0x90,0x6c,// EEINTP mode1
+ 0x97,0x45,// edge effect
+ //==,=======RGB Gamma
+ 0xfe,0x02,
+ 0x15,0x0a,
+ 0x16,0x12,
+ 0x17,0x19,
+ 0x18,0x1f,
+ 0x19,0x2c,
+ 0x1a,0x38,
+ 0x1b,0x42,
+ 0x1c,0x4e,
+ 0x1d,0x63,
+ 0x1e,0x76,
+ 0x1f,0x87,
+ 0x20,0x96,
+ 0x21,0xa2,
+ 0x22,0xb8,
+ 0x23,0xca,
+ 0x24,0xd8,
+ 0x25,0xe3,
+ 0x26,0xf0,
+ 0x27,0xf8,
+ 0x28,0xfd,
+ 0x29,0xff,
+
+ //===,=========y gamma
+ 0xfe,0x02,
+ 0x2b,0x00,
+ 0x2c,0x04,
+ 0x2d,0x09,
+ 0x2e,0x18,
+ 0x2f,0x27,
+ 0x30,0x37,
+ 0x31,0x49,
+ 0x32,0x5c,
+ 0x33,0x7e,
+ 0x34,0xa0,
+ 0x35,0xc0,
+ 0x36,0xe0,
+ 0x37,0xff,
+ ///1,x120ize//
+ 0xfe,0x00,//
+ 0x90,0x01, //0x//crop enable
+ 0x95,0x04, //0x//1600x1200
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+
+ 0xfe,0x03,
+ 0x42,0x40,
+ 0x43,0x06, //output buf width
+ 0x41,0x02, // Pclk_polarity
+ 0x40,0x40, //00
+ 0x17,0x00, //widv
+ 0xfe,0x00,
+ //ou,t DV////
+
+ 0xfe,0x00,
+ 0x82,0xfe, // fe
+ 0xf2,0x70,
+ 0xf3,0xff,
+ 0xf4,0x00,
+ 0xf5,0x30,
+
+ #if 0
+ //,///ssumple 640X4800//////
+ 0xfe,0x00,
+ 0xfa,0x00,
+ 0xc8,0x00,
+
+ 0x99,0x22,
+ 0x9a,0x07,
+ 0x9b,0x00,
+ 0x9c,0x00,
+ 0x9d,0x00,
+ 0x9e,0x00,
+ 0x9f,0x00,
+ 0xa0,0x00,
+ 0xa1,0x00,
+ 0xa2,0x00,
+
+
+ 0x90,0x01,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+ #endif
+
+
+ 0xfe,0x00,
+ 0x0c,0x00,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+
+ 0xc8,0x15,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x00,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+ 0xb6,0x03,
+
+#if 1
+ //// re zao///
+ 0xfe ,0x00,
+ 0x22 ,0xf0,
+ 0xfe ,0x01,
+ 0x21 ,0xff,
+ 0xfe ,0x02,
+ 0x8a ,0x33,
+ 0x8c ,0x76,
+ 0x8d ,0x85,
+ 0xa6 ,0xf0,
+ 0xae ,0x9f,
+ 0xa2 ,0x90,
+ 0xa5 ,0x40,
+ 0xa7 ,0x30,
+ 0xb0 ,0x88,
+ 0x38 ,0x0b,
+ 0x39 ,0x30,
+ 0xfe ,0x00,
+ 0x87 ,0xb0,
+
+
+ /*
+ //, sma RGB gamma////
+ 0xfe,0x02,
+ 0x15,0x0b,
+ 0x16,0x0e,
+ 0x17,0x10,
+ 0x18,0x12,
+ 0x19,0x19,
+ 0x1a,0x21,
+ 0x1b,0x29,
+ 0x1c,0x31,
+ 0x1d,0x41,
+ 0x1e,0x50,
+ 0x1f,0x5f,
+ 0x20,0x6d,
+ 0x21,0x79,
+ 0x22,0x91,
+ 0x23,0xa5,
+ 0x24,0xb9,
+ 0x25,0xc9,
+ 0x26,0xe1,
+ 0x27,0xee,
+ 0x28,0xf7,
+ 0x29,0xff,
+ */
+ //da,sun///
+ 0xfe,0x02,
+ 0x40,0x06,
+ 0x41,0x23,
+ 0x42,0x3f,
+ 0x43,0x06,
+ 0x44,0x00,
+ 0x45,0x00,
+ 0x46,0x14,
+ 0x47,0x09,
+
+ #endif
+};
+
+uint8_t gc2035_default_regs_exit[]={
+ 0xfe, 0x00,
+ 0xf1, 0x00,
+ 0xf2, 0x00,
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-zh1426.h b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-zh1426.h
new file mode 100755
index 00000000..63ac5b1a
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-zh1426.h
@@ -0,0 +1,954 @@
+#ifndef GC2035_H
+#define GC2035_H
+
+
+// Scene Mode
+uint8_t gc2035_scene_mode_auto[] = {
+ 0xfe,0x01,
+ 0x3e,0x40,
+ 0x1f,0xa0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x50,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe,0x00,
+};
+
+uint8_t gc2035_scene_mode_night[] = {
+ 0xfe, 0x01,
+ 0x3e, 0x60,
+ 0x1f,0xe0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x80,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe, 0x00,
+};
+
+
+// White Balance
+uint8_t gc2035_wb_auto [] = {
+ 0xfe, 0x00,
+// 0xb3, 0x61, //2013-8-16 dustin
+// 0xb4, 0x40,
+// 0xb5, 0x61,
+ 0x82, 0xfe,
+};
+
+uint8_t gc2035_wb_incandescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0xa8,
+};
+
+uint8_t gc2035_wb_fluorescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0x78,
+};
+
+uint8_t gc2035_wb_daylight [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x78,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+uint8_t gc2035_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x58,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+
+// Exposure
+uint8_t gc2035_exposure_neg6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x60,
+ 0xfe, 0x02,
+ 0xd5, 0xe0,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_neg3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x70,
+ 0xfe, 0x02,
+ 0xd5, 0xf0,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_zero[] = {
+ 0xfe, 0x01,
+ 0x13, 0x78,
+ 0xfe, 0x02,
+ 0xd5, 0xfd, /// 00
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_pos3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x88,
+ 0xfe, 0x02,
+ 0xd5, 0x10,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_pos6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x90,
+ 0xfe, 0x02,
+ 0xd5, 0x20,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_176_144_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x33,
+ 0x9b,0x00,
+ 0x9f,0x00,
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0x92,//600
+ 0x97,0x00,
+ 0x98,0xb0,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+
+ 0xfe,0x03,
+ 0x42,0x60,
+ 0x43,0x01,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_320_240_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x22,
+ 0x9b,0x00,
+ 0x9f,0x00,
+
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0xf2,//600
+ 0x97,0x01,
+ 0x98,0x40,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x02,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_1280_720_regs[]={
+ 0xfe,0x00,
+ 0x0a,0xf0,
+ 0x0c,0xa0,
+ 0x0d,0x02,
+ 0x0e,0xd8,
+ 0x0f,0x05,
+ 0x10,0x18,
+ 0xfe,0x00,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x05,0x01,
+ 0x06,0xaa,
+ 0x07,0x00,
+ 0x08,0xf6,
+ 0xfe,0x01,
+ 0x27,0x00,
+ 0x28,0xa1,
+ 0x29,0x03,
+ 0x2a,0xc6,
+ 0x2b,0x04,
+ 0x2c,0x67,
+ 0x2d,0x06,
+ 0x2e,0x4a,
+ 0x2f,0x07,
+ 0x30,0x8c,
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xb6,0x03,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x99,0x11,
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0xd0,
+ 0x97,0x05,
+ 0x98,0x00,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+ 0xfa,0x11,
+};
+
+uint8_t gc2035_1600_1200_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x02,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x04,
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+
+ 0xc8,0x00,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+};
+
+uint8_t gc2035_800_600_regs[]={
+ 0xfe,0x00,
+ 0xfa,0x00,
+ 0xc8,0x00,
+
+ 0x99,0x22,
+ 0x9a,0x07,
+ 0x9b,0x00,
+ 0x9c,0x00,
+ 0x9d,0x00,
+ 0x9e,0x00,
+ 0x9f,0x00,
+ 0xa0,0x00,
+ 0xa1,0x00,
+ 0xa2,0x00,
+
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0x58,
+ 0x97,0x03,
+ 0x98,0x20,
+};
+
+uint8_t gc2035_640_480_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x00,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+
+ 0xc8,0x15,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x00,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+ 0xb6,0x03,
+};
+
+uint8_t gc2035_default_regs_init[]={
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfc,0x06,
+ 0xf9,0xfe,
+ 0xfa,0x00,
+ 0xf6,0x00,
+ 0xf7,0x17,
+ 0xf8,0x00,
+ 0xfe,0x00,
+ 0x82,0x00,
+ //0xb3,0x60,//close manual WB 2013.0723
+ //0xb4,0x40,
+ //0xb5,0x60,
+ 0x03,0x05,
+ 0x04,0x08,
+ 0xfe,0x00,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x60,
+ 0xef,0x90,
+ 0x0c,0x00,
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+ 0x17,0x14,
+ 0x18,0x0a,
+ 0x19,0x0a,
+ 0x1a,0x01,
+ 0x1b,0x8b,
+ 0x1c,0x05,
+ 0x1e,0x88, //analog mode1 [7] tx-high en [5:3]COL_bias
+ 0x1f,0x08, //[3] tx-low en//
+ 0x20,0x05, //[0]adclk mode,0x[1]rowclk_MODE [2]rsthigh_en
+ 0x21,0x0f, //[6:4]rsg
+ 0x22,0xd0, //[3:0]vref
+ 0x23,0xc3, //f3//ADC_r
+ 0x24,0x17, //pad drive 16
+
+ //AEC
+ 0xfe,0x01,
+ 0x0c,0x01,
+ 0x11,0x20,//AEC_out_slope,0x
+ 0x1f,0xb0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x60,//max_pre_gain 60 2013-7-22 dustin
+ 0x47,0x30,//AEC_outdoor_th
+ 0x0b,0x10,//
+ 0x13,0x7a,//y_target
+ 0xfe,0x00,
+
+ 0x05,0x01,//hb
+ 0x06,0x11,
+ 0x07,0x00,//vb
+ 0x08,0x50,
+ 0xfe,0x01,
+ 0x27,0x00,//step
+ 0x28,0xa0,
+ 0x29,0x05,//level1
+ 0x2a,0x00,
+ 0x2b,0x05,//level2
+ 0x2c,0x00,
+ 0x2d,0x06,//6e8//level3
+ 0x2e,0x40,
+ 0x2f,0x0b,//level4
+ 0x30,0x40,
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xb6,0x03,
+ 0xfe,0x00,
+ 0x3f,0x00,
+ 0x40,0x77,
+ 0x42,0x7f,
+ 0x43,0x30,
+ 0x5c,0x08,
+ 0x5e,0x20,
+ 0x5f,0x20,
+ 0x60,0x20,
+ 0x61,0x20,
+ 0x62,0x20,
+ 0x63,0x20,
+ 0x64,0x20,
+ 0x65,0x20,
+ 0x66,0x20,
+ 0x67,0x20,
+ 0x68,0x20,
+ 0x69,0x20,
+ 0x90,0x01,
+ 0x95,0x04,
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0x80,0xff,
+ 0x81,0x26,
+ 0x03,0x05,
+ 0x04,0x2e,
+ 0x84,0x02,
+ 0x86,0x02,
+ 0x87,0x80,
+ 0x8b,0xbc,
+ 0xa7,0x80,
+ 0xa8,0x80,
+ 0xb0,0x80,
+ 0xc0,0x40,
+ //LSC
+ 0xfe,0x01,
+ 0xc2,0x38,
+ 0xc3,0x25,
+ 0xc4,0x21,
+ 0xc8,0x19,
+ 0xc9,0x12,
+ 0xca,0x0e,
+ 0xbc,0x43,
+ 0xbd,0x18,
+ 0xbe,0x1b,
+ 0xb6,0x40,
+ 0xb7,0x2e,
+ 0xb8,0x26,
+ 0xc5,0x05,
+ 0xc6,0x03,
+ 0xc7,0x04,
+ 0xcb,0x00,
+ 0xcc,0x00,
+ 0xcd,0x00,
+ 0xbf,0x14,
+ 0xc0,0x22,
+ 0xc1,0x1b,
+ 0xb9,0x00,
+ 0xba,0x05,
+ 0xbb,0x05,
+ 0xaa,0x35,
+ 0xab,0x33,
+ 0xac,0x33,
+ 0xad,0x25,
+ 0xae,0x22,
+ 0xaf,0x27,
+ 0xb0,0x1d,
+ 0xb1,0x20,
+ 0xb2,0x22,
+ 0xb3,0x14,
+ 0xb4,0x15,
+ 0xb5,0x16,
+ 0xd0,0x00,
+ 0xd2,0x07,
+ 0xd3,0x08,
+ 0xd8,0x00,
+ 0xda,0x13,
+ 0xdb,0x17,
+ 0xdc,0x00,
+ 0xde,0x0a,
+ 0xdf,0x08,
+ 0xd4,0x00,
+ 0xd6,0x00,
+ 0xd7,0x0c,
+ 0xa4,0x00,
+ 0xa5,0x00,
+ 0xa6,0x00,
+ 0xa7,0x00,
+ 0xa8,0x00,
+ 0xa9,0x00,
+ 0xa1,0x80,
+ 0xa2,0x80,
+
+ //=================================cc
+ 0xfe,0x02,
+ 0xc0,0x01,
+ 0xc1,0x40, //Green_cc for d
+ 0xc2,0xfc,
+ 0xc3,0x05,
+ 0xc4,0xec,
+ 0xc5,0x42,
+ 0xc6,0xf8,
+ 0xc7,0x40,//for cwf
+ 0xc8,0xf8,
+ 0xc9,0x06,
+ 0xca,0xfd,
+ 0xcb,0x3e,
+ 0xcc,0xf3,
+ 0xcd,0x36,//for A
+ 0xce,0xf6,
+ 0xcf,0x04,
+ 0xe3,0x0c,
+ 0xe4,0x44,
+ 0xe5,0xe5,
+ 0xfe,0x00,
+
+ //==============================awb
+ //AWB clear
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x10, // 10
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x20, // 20
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00, // 30
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x40, // 40
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x50, // 50
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x60, // 60
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x70, // 70
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x80, // 80
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x90, // 90
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xa0, // a0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xb0, // b0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xc0, // c0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xd0, // d0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4f,0x01,
+ /////// awb value////////
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x40,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x53,
+ 0x4e,0x08,
+ 0x4e,0x04,
+ 0x4d,0x62,
+ 0x4e,0x10,
+ 0x4d,0x72,
+ 0x4e,0x20,
+ 0x4f,0x01,
+
+ /////awb////
+ 0xfe,0x01,
+ 0x50,0x88,//c0//[6]green mode
+ 0x52,0x40,
+ 0x54,0x60,
+ 0x56,0x06,
+ 0x57,0x20, //pre adjust
+ 0x58,0x01,
+ 0x5b,0x02, //AWB_gain_delta
+ 0x61,0xaa,//R/G stand
+ 0x62,0xaa,//R/G stand
+ 0x71,0x00,
+ 0x74,0x10,//AWB_C_max
+ 0x77,0x08, // 0x//AWB_p2_x
+ 0x78,0xfd, //AWB_p2_y
+ 0x86,0x30,
+ 0x87,0x00,
+ 0x88,0x04,//06,[1]dark mode
+ 0x8a,0xc0,//awb move mode
+ 0x89,0x75,
+ 0x84,0x08,//auto_window
+ 0x8b,0x00, // 0x//awb compare luma
+ 0x8d,0x70, //awb gain limit R
+ 0x8e,0x70,//G
+ 0x8f,0xf4,//B
+ 0xfe,0x00,
+ 0x82,0x02,//awb_en
+
+ 0xfe,0x01,
+ 0x21,0xbf,
+ 0xfe,0x02,
+ 0xa4,0x00,//
+ 0xa5,0x40, //lsc_th
+ 0xa2,0xa0, //lsc_dec_slope
+ 0xa6,0x80, //dd_th
+ 0xa7,0x60, //ot_th
+ 0xab,0x31, //
+ 0xa9,0x6f, //
+ 0xb0,0x99, //0x//edge effect slope low
+ 0xb1,0x34,//edge effect slope low
+ 0xb3,0x80, //saturation dec slope
+ 0xde,0xb6, //
+ 0x38,0x0f, //
+ 0x39,0x60, //
+ 0xfe,0x00,
+ 0x81,0x24,
+ 0xfe,0x02,
+ 0x83,0x00,//
+ 0x84,0x45,//
+ ////////////YCP//////////
+ 0xd1,0x40,// 40 saturation_cb
+ 0xd2,0x40,//40 saturation_Cr
+
+ 0xaf,0x7e,
+
+
+ 0xd3,0x40,//
+ 0xd4,0x80,//contrast center
+ 0xd5,0xf8,//luma_offset
+ 0xdc,0x30,
+ 0xdd,0xb8,
+ 0xfe,0x00,
+ //gamma
+ 0xfe,0x02,
+ 0x88,0x1f,//dn_b_base 15 2013-7-22 dustin
+ 0x8c,0xf6, //[2]b_in_dark_inc
+ 0x89,0x02, //dn_c_weight 03 2013-7-22 dustin
+ ////////EE ///////////
+ 0xfe,0x02,
+ 0x90,0x6c,// EEINTP mode1
+ 0x97,0x9a,//78 edge effect
+ ////==============RGB Gamma
+ 0xfe,0x02,
+ 0x15,0x0a,
+ 0x16,0x12,
+ 0x17,0x19,
+ 0x18,0x1f,
+ 0x19,0x2c,
+ 0x1a,0x38,
+ 0x1b,0x42,
+ 0x1c,0x4e,
+ 0x1d,0x63,
+ 0x1e,0x76,
+ 0x1f,0x87,
+ 0x20,0x96,
+ 0x21,0xa2,
+ 0x22,0xb8,
+ 0x23,0xca,
+ 0x24,0xd8,
+ 0x25,0xe3,
+ 0x26,0xf0,
+ 0x27,0xf8,
+ 0x28,0xfd,
+ 0x29,0xff,
+
+ ///=================y gamma
+ 0xfe,0x02,
+ 0x2b,0x00,
+ 0x2c,0x06, /// 04
+ 0x2d,0x0a, // 09
+ 0x2e,0x18,
+ 0x2f,0x27,
+ 0x30,0x37,
+ 0x31,0x49,
+ 0x32,0x5c,
+ 0x33,0x7e,
+ 0x34,0xa0,
+ 0x35,0xc0,
+ 0x36,0xe0,
+ 0x37,0xff,
+ 0xfe,0x00,
+
+ 0xfe,0x00,
+ 0x82,0xfe,
+ 0xf2,0x70,
+ 0xf3,0xff,
+ 0xf4,0x00,
+ 0xf5,0x30,
+
+ 0xfe,0x00,
+ 0x90,0x01,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+ 0xc8,0x14,
+ 0xf7,0x11,
+ 0xf8,0x85,
+ 0xfa,0x00,
+ 0x05,0x01,
+ 0x06,0x0a,
+ 0x07,0x00,
+ 0x08,0x58,
+ 0xfe,0x01,
+ 0x27,0x00,
+ 0x28,0xa1,
+ 0x29,0x05,
+ 0x2a,0x08,
+ 0x2b,0x05,
+ 0x2c,0x08,
+ 0x2d,0x06,
+ 0x2e,0x4a,
+
+ 0x2f,0x05,
+ 0x30,0x08,
+
+ 0x3e,0x40,
+ 0xfe,0x03,
+ 0x42,0x04,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+
+
+#if 0
+ //////// re zao///
+ 0xfe,0x00,
+ 0x22,0xd0,
+ 0xfe,0x01,
+ 0x21,0xff,
+ 0xfe,0x02,
+ 0x8a,0x33,
+ 0x8c,0x76,
+ 0x8d,0x85,
+ 0xa6,0xf0,
+ 0xae,0x9f,
+ 0xa2,0x90,
+ 0xa5,0x40,
+ 0xa7,0x30,
+ 0xb0,0x88,
+ 0x38,0x0b,
+ 0x39,0x30,
+ 0xfe,0x00,
+ 0x87,0xb0,
+
+ /// small RGB gamma////
+
+
+ ///dark sun/////
+ 0xfe , 0x02,
+ 0x40 , 0x06,
+ 0x41 , 0x23,
+ 0x42 , 0x3f,
+ 0x43 , 0x06,
+ 0x44 , 0x00,
+ 0x45 , 0x00,
+ 0x46 , 0x14,
+ 0x47 , 0x09,
+ 0xfe,0x00,
+
+ #endif
+};
+
+uint8_t gc2035_default_regs_exit[]={
+ 0xfe, 0x00,
+ 0xf1, 0x00,
+ 0xf2, 0x00,
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-zh1547a-tp785.h b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-zh1547a-tp785.h
new file mode 100755
index 00000000..875dec12
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035-zh1547a-tp785.h
@@ -0,0 +1,1030 @@
+#ifndef GC2035_H
+#define GC2035_H
+
+
+// Scene Mode
+uint8_t gc2035_scene_mode_auto[] = {
+ 0xfe,0x01,
+ 0x3e,0x40,
+ 0x1f,0xa0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x50,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe,0x00,
+};
+
+uint8_t gc2035_scene_mode_night[] = {
+ 0xfe, 0x01,
+ 0x3e, 0x60,
+ 0x1f,0xe0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x80,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe, 0x00,
+};
+
+
+// White Balance
+uint8_t gc2035_wb_auto [] = {
+ 0xfe, 0x00,
+// 0xb3, 0x61, //2013-8-16 dustin
+// 0xb4, 0x40,
+// 0xb5, 0x61,
+ 0x82, 0xfe,
+};
+
+uint8_t gc2035_wb_incandescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0xa8,
+};
+
+uint8_t gc2035_wb_fluorescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0x78,
+};
+
+uint8_t gc2035_wb_daylight [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x78,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+uint8_t gc2035_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x58,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+
+// Exposure
+uint8_t gc2035_exposure_neg6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x68,
+ 0xfe, 0x02,
+ 0xd5, 0xe0,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc2035_exposure_neg3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x70,
+ 0xfe, 0x02,
+ 0xd5, 0xf0,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc2035_exposure_zero[] = {
+ 0xfe, 0x01,
+ 0x13, 0x78,
+ 0xfe, 0x02,
+ 0xd5, 0xf8,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_pos3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x80,
+ 0xfe, 0x02,
+ 0xd5, 0x10,
+ 0xfe, 0x00,
+
+
+};
+
+uint8_t gc2035_exposure_pos6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x88,
+ 0xfe, 0x02,
+ 0xd5, 0x20,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_176_144_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x33,
+ 0x9b,0x00,
+ 0x9f,0x00,
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0x92,//600
+ 0x97,0x00,
+ 0x98,0xb0,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+
+ 0xfe,0x03,
+ 0x42,0x60,
+ 0x43,0x01,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_320_240_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x22,
+ 0x9b,0x00,
+ 0x9f,0x00,
+
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0xf2,//600
+ 0x97,0x01,
+ 0x98,0x40,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x02,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_1280_720_regs[]={
+ 0xfe,0x00,
+ 0x0a,0xf0,
+ 0x0c,0xa0,
+ 0x0d,0x02,
+ 0x0e,0xd8,
+ 0x0f,0x05,
+ 0x10,0x18,
+ 0xfe,0x00,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x05,0x01,
+ 0x06,0xaa,
+ 0x07,0x00,
+ 0x08,0xf6,
+ 0xfe,0x01,
+ 0x27,0x00,
+ 0x28,0xa1,
+ 0x29,0x03,
+ 0x2a,0xc6,
+ 0x2b,0x04,
+ 0x2c,0x67,
+ 0x2d,0x06,
+ 0x2e,0x4a,
+ 0x2f,0x07,
+ 0x30,0x8c,
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xb6,0x03,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x99,0x11,
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0xd0,
+ 0x97,0x05,
+ 0x98,0x00,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+ 0xfa,0x11,
+};
+
+uint8_t gc2035_1600_1200_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x02,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x04,
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+
+ 0xc8,0x00,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+};
+
+uint8_t gc2035_800_600_regs[]={
+ 0xfe,0x00,
+ 0xfa,0x00,
+ 0xc8,0x00,
+
+ 0x99,0x22,
+ 0x9a,0x07,
+ 0x9b,0x00,
+ 0x9c,0x00,
+ 0x9d,0x00,
+ 0x9e,0x00,
+ 0x9f,0x00,
+ 0xa0,0x00,
+ 0xa1,0x00,
+ 0xa2,0x00,
+
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0x58,
+ 0x97,0x03,
+ 0x98,0x20,
+};
+
+uint8_t gc2035_640_480_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x00,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+
+ 0xc8,0x15,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x00,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+ 0xb6,0x03,
+};
+
+uint8_t gc2035_default_regs_init[]={
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfc,0x06,
+ 0xf2,0x00,
+ 0xf3,0x00,
+ 0xf4,0x00,
+ 0xf5,0x00,
+ 0xf9,0xfe, //[0] pll enable
+ 0xfa,0x00,
+ 0xf6,0x00,
+ 0xf7,0x15, //pll enable
+
+ 0xf8,0x84,//85
+ 0xfe,0x00,
+ 0x82,0x00,
+ 0xb3,0x60,
+ 0xb4,0x40,
+ 0xb5,0x60,
+
+ 0x03,0x02,
+ 0x04,0x71,
+
+ ////,/meare window ///////////
+ 0xfe,0x00,
+ 0xec,0x06,//04
+ 0xed,0x06,//04
+ 0xee,0x62,//60
+ 0xef,0x92,//90
+
+ ////,//anog/////////////
+ 0x0a,0x00, //row start
+ 0x0c,0x00, //col start
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06, //Window setting
+ 0x10,0x58,
+ 0x17,0x14, //[0]mirror [1]flip
+ 0x18,0x0a, //0a 2012.10.26
+ 0x19,0x0a, //AD pipe number
+ 0x1a,0x01, //CISCTL mode4
+ 0x1b,0x8b,
+ 0x1c,0x05,//add by lanking 20130403
+ 0x1e,0x88, //analog mode1 [7] tx-high en [5:3]COL_bias
+ 0x1f,0x08, //[3] tx-low en//
+ 0x20,0x05, //[0]adclk mode},{0x[1]rowclk_MODE [2]rsthigh_en
+ 0x21,0x0f, //[6:4]rsg
+ 0x22,0xf0, //[3:0]vref 20130403
+ 0x23,0xc3, //f3//ADC_r
+ 0x24,0x17, //pad drive 16
+
+
+ 0xfe,0x01,
+ 0x11,0x20,//AEC_out_slope},{0x
+ 0x1f,0xc0,//max_post_gain
+ 0x20,0x48,//max_pre_gain
+ 0x47,0x30,//AEC_outdoor_th
+ 0x0b,0x10,//
+ 0x13,0x78,//y_target 84
+ 0xfe,0x00,
+
+#if 0
+ 0x05,0x01,//hb
+ 0x06,0x11,
+ 0x07,0x00,//vb
+ 0x08,0x50,
+ 0xfe,0x01,
+ 0x27,0x00,//step
+ 0x28,0xa0,
+ 0x29,0x05,//level1
+ 0x2a,0x00,
+ 0x2b,0x05,//level2
+ 0x2c,0x00,
+ 0x2d,0x06,//6e8//level3
+ 0x2e,0xe0,
+ 0x2f,0x0a,//level4
+ 0x30,0x00,
+#endif
+ 0x05,0x01,//hb
+ 0x06,0x5c,
+ 0x07,0x00,//vb
+ 0x08,0x32,
+ 0xfe,0x01,
+ 0x27,0x00,//step
+ 0x28,0x7d,
+ 0x29,0x04,//level1
+ 0x2a,0xe2,
+ 0x2b,0x04,//level2
+ 0x2c,0xe2,
+ 0x2d,0x04,//6e8//level3
+ 0x2e,0xe2,
+ 0x2f,0x07,//level4
+ 0x30,0x53,
+
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xfe,0x00, //0x},{0x},{0x},{0x},{0x
+ 0xb6,0x03, //AEC enable
+ 0xfe,0x00,
+
+ 0xad,0x7d,
+ 0xae,0x81,
+ 0xaf,0x7e,
+
+ ////,BLK////
+ 0x3f,0x00, //prc close
+ 0x40,0x77,//
+ 0x42,0x7f,
+ 0x43,0x30,
+ 0x5c,0x08,
+ 0x5e,0x20,
+ 0x5f,0x20,
+ 0x60,0x20,
+ 0x61,0x20,
+ 0x62,0x20,
+ 0x63,0x20,
+ 0x64,0x20,
+ 0x65,0x20,
+
+ //blo,/////////
+ 0x80,0xff,
+ 0x81,0x26,//38},{0x//skin_Y 8c_debug
+ 0x87,0x90, //[7]middle gamma
+ 0x84,0x02, //output put foramat
+ 0x86,0x02, //02 //sync plority
+ 0x8b,0xbc,
+ 0xb0,0x80, //globle gain
+ 0xc0,0x40,//Yuv bypass
+
+ ////lsc///////////
+ 0xfe,0x01,
+ 0xc2,0x38,
+ 0xc3,0x25,
+ 0xc4,0x21,
+ 0xc8,0x19,
+ 0xc9,0x12,
+ 0xca,0x0e,
+ 0xbc,0x43,
+ 0xbd,0x18,
+ 0xbe,0x1b,
+ 0xb6,0x40,
+ 0xb7,0x2e,
+ 0xb8,0x26,
+ 0xc5,0x05,
+ 0xc6,0x03,
+ 0xc7,0x04,
+ 0xcb,0x00,
+ 0xcc,0x00,
+ 0xcd,0x00,
+ 0xbf,0x14,
+ 0xc0,0x22,
+ 0xc1,0x1b,
+ 0xb9,0x00,
+ 0xba,0x05,
+ 0xbb,0x05,
+ 0xaa,0x35,
+ 0xab,0x33,
+ 0xac,0x33,
+ 0xad,0x25,
+ 0xae,0x22,
+ 0xaf,0x27,
+ 0xb0,0x1d,
+ 0xb1,0x20,
+ 0xb2,0x22,
+ 0xb3,0x14,
+ 0xb4,0x15,
+ 0xb5,0x16,
+ 0xd0,0x00,
+ 0xd2,0x07,
+ 0xd3,0x08,
+ 0xd8,0x00,
+ 0xda,0x13,
+ 0xdb,0x17,
+ 0xdc,0x00,
+ 0xde,0x0a,
+ 0xdf,0x08,
+ 0xd4,0x00,
+ 0xd6,0x00,
+ 0xd7,0x0c,
+ 0xa4,0x00,
+ 0xa5,0x00,
+ 0xa6,0x00,
+ 0xa7,0x00,
+ 0xa8,0x00,
+ 0xa9,0x00,
+ 0xa1,0x80,
+ 0xa2,0x80,
+
+ ////,/cc////////////
+ 0xfe,0x02,
+ 0xc0,0x01,
+ 0xc1,0x40, //Green_cc for d
+ 0xc2,0xfc,
+ 0xc3,0x05,
+ 0xc4,0xec,
+ 0xc5,0x42,
+ 0xc6,0xf8,
+ 0xc7,0x40,//for cwf
+ 0xc8,0xf8,
+ 0xc9,0x06,
+ 0xca,0xfd,
+ 0xcb,0x3e,
+ 0xcc,0xf3,
+ 0xcd,0x36,//for A
+ 0xce,0xf6,
+ 0xcf,0x04,
+ 0xe3,0x0c,
+ 0xe4,0x44,
+ 0xe5,0xe5,
+ 0xfe,0x00,
+
+ ////,b stt ////////////////AWB ,ar
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x10, // 10
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x20, // 20
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00, // 30
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x40, // 40
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x50, // 50
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x60, // 60
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x70, // 70
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x80, // 80
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x90, // 90
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xa0, // a0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xb0, // b0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xc0, // c0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xd0, // d0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4f,0x01,
+ ////,wb vue////////
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x40,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x52,
+ 0x4e,0x08,
+ 0x4e,0x08,
+ 0x4e,0x04,
+ 0x4d,0x62,
+ 0x4e,0x10,
+ 0x4e,0x10,
+ 0x4d,0x72,
+ 0x4e,0x20,
+ 0x4f,0x01,
+
+ ///a,///
+ 0xfe,0x01,
+ 0x50,0x88,//c0//[6]green mode
+ 0x52,0x40,
+ 0x54,0x60,
+ 0x56,0x06,
+ 0x57,0x20, //pre adjust
+ 0x58,0x01,
+ 0x5b,0x02, //AWB_gain_delta
+ 0x61,0xaa,//R/G stand
+ 0x62,0xaa,//R/G stand
+ 0x71,0x00,
+ 0x74,0x10, //0x//AWB_C_max
+ 0x77,0x08, // 0x//AWB_p2_x
+ 0x78,0xfd, //AWB_p2_y
+ 0x86,0x30,
+ 0x87,0x00,
+ 0x88,0x04,//06},{0x//[1]dark mode
+ 0x8a,0xc0,//awb move mode
+ 0x89,0x75,
+ 0x84,0x08, //0x//auto_window
+ 0x8b,0x00, // 0x//awb compare luma
+ 0x8d,0x70, //awb gain limit R
+ 0x8e,0x70,//G
+ 0x8f,0xf4,//B
+ 0xfe,0x00,
+ 0x82,0x02,//awb_en
+ ////wb end /////////////
+
+ //===,====de
+ 0xfe,0x01,
+
+ 0x9c,0x04,//00
+
+ 0x21,0xbf,
+ 0xfe,0x02,
+ 0xa4,0x00,//
+ 0xa5,0x40, //lsc_th
+ 0xa2,0xa0, //lsc_dec_slope
+ 0xa6,0x80, //dd_th
+ 0xa7,0x80, //ot_th
+ 0xab,0x31, //
+ 0xa9,0x6f, //
+ 0xb0,0x99, //0x//edge effect slope low
+ 0xb1,0x34,//edge effect slope low
+ 0xb3,0x80, //saturation dec slope
+ 0xde,0xb6, //
+ 0x38,0x0f, //
+ 0x39,0x60, //
+ 0xfe,0x00,
+ 0x81,0x26,
+ 0xfe,0x02,
+ 0x83,0x00,//
+ 0x84,0x45,//
+ ////,///Y//////////
+ 0xd1,0x38,//saturation_cb
+ 0xd2,0x38,//saturation_Cr
+ 0xd3,0x40,//contrast ? {{0xd4},{0x80}},//contrast center
+ 0xd5,0xf8,//luma_offset
+ 0xdc,0x30,
+ 0xdd,0xb8,//edge_sa_g,b
+ 0xfe,0x00,
+ ////,dd/////////
+ 0xfe,0x02,
+ 0x88,0x1c,//dn_b_base
+ 0x8c,0xf6, //[2]b_in_dark_inc
+ 0x89,0x03, //dn_c_weight
+ ////,E /////////
+ 0xfe,0x02,
+ 0x90,0x6c,// EEINTP mode1
+ 0x97,0x45,// edge effect
+ //==,=======RGB Gamma
+ 0xfe,0x02,
+ 0x15,0x0a,
+ 0x16,0x12,
+ 0x17,0x19,
+ 0x18,0x1f,
+ 0x19,0x2c,
+ 0x1a,0x38,
+ 0x1b,0x42,
+ 0x1c,0x4e,
+ 0x1d,0x63,
+ 0x1e,0x76,
+ 0x1f,0x87,
+ 0x20,0x96,
+ 0x21,0xa2,
+ 0x22,0xb8,
+ 0x23,0xca,
+ 0x24,0xd8,
+ 0x25,0xe3,
+ 0x26,0xf0,
+ 0x27,0xf8,
+ 0x28,0xfd,
+ 0x29,0xff,
+
+ //===,=========y gamma
+ 0xfe,0x02,
+ 0x2b,0x00,
+ 0x2c,0x04,
+ 0x2d,0x09,
+ 0x2e,0x18,
+ 0x2f,0x27,
+ 0x30,0x37,
+ 0x31,0x49,
+ 0x32,0x5c,
+ 0x33,0x7e,
+ 0x34,0xa0,
+ 0x35,0xc0,
+ 0x36,0xe0,
+ 0x37,0xff,
+ ///1,x120ize//
+ 0xfe,0x00,//
+ 0x90,0x01, //0x//crop enable
+ 0x95,0x04, //0x//1600x1200
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+
+ 0xfe,0x03,
+ 0x42,0x40,
+ 0x43,0x06, //output buf width
+ 0x41,0x02, // Pclk_polarity
+ 0x40,0x40, //00
+ 0x17,0x00, //widv
+ 0xfe,0x00,
+ //ou,t DV////
+
+ 0xfe,0x00,
+ 0x82,0xfe, // fe
+ 0xf2,0x70,
+ 0xf3,0xff,
+ 0xf4,0x00,
+ 0xf5,0x30,
+
+ #if 0
+ //,///ssumple 640X4800//////
+ 0xfe,0x00,
+ 0xfa,0x00,
+ 0xc8,0x00,
+
+ 0x99,0x22,
+ 0x9a,0x07,
+ 0x9b,0x00,
+ 0x9c,0x00,
+ 0x9d,0x00,
+ 0x9e,0x00,
+ 0x9f,0x00,
+ 0xa0,0x00,
+ 0xa1,0x00,
+ 0xa2,0x00,
+
+
+ 0x90,0x01,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+ #endif
+
+
+ 0xfe,0x00,
+ 0x0c,0x00,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+
+ 0xc8,0x15,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x00,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+ 0xb6,0x03,
+
+#if 1
+ //// re zao///
+ 0xfe ,0x00,
+ 0x22 ,0xf0,
+ 0xfe ,0x01,
+ 0x21 ,0xff,
+ 0xfe ,0x02,
+ 0x8a ,0x33,
+ 0x8c ,0x76,
+ 0x8d ,0x85,
+ 0xa6 ,0xf0,
+ 0xae ,0x9f,
+ 0xa2 ,0x90,
+ 0xa5 ,0x40,
+ 0xa7 ,0x30,
+ 0xb0 ,0x88,
+ 0x38 ,0x0b,
+ 0x39 ,0x30,
+ 0xfe ,0x00,
+ 0x87 ,0xb0,
+
+
+ /*
+ //, sma RGB gamma////
+ 0xfe,0x02,
+ 0x15,0x0b,
+ 0x16,0x0e,
+ 0x17,0x10,
+ 0x18,0x12,
+ 0x19,0x19,
+ 0x1a,0x21,
+ 0x1b,0x29,
+ 0x1c,0x31,
+ 0x1d,0x41,
+ 0x1e,0x50,
+ 0x1f,0x5f,
+ 0x20,0x6d,
+ 0x21,0x79,
+ 0x22,0x91,
+ 0x23,0xa5,
+ 0x24,0xb9,
+ 0x25,0xc9,
+ 0x26,0xe1,
+ 0x27,0xee,
+ 0x28,0xf7,
+ 0x29,0xff,
+ */
+ //da,sun///
+ 0xfe,0x02,
+ 0x40,0x06,
+ 0x41,0x23,
+ 0x42,0x3f,
+ 0x43,0x06,
+ 0x44,0x00,
+ 0x45,0x00,
+ 0x46,0x14,
+ 0x47,0x09,
+
+ #endif
+};
+
+uint8_t gc2035_default_regs_exit[]={
+ 0xfe, 0x00,
+ 0xf1, 0x00,
+ 0xf2, 0x00,
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035.c b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035.c
new file mode 100755
index 00000000..b655582c
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035.c
@@ -0,0 +1,390 @@
+
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "gc2035.h"
+
+#define sensor_write_array(sd, arr, sz) \
+ cmos_init_8bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write(sd->i2c_addr, reg, val)
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint8_t *regs;
+ size_t size;
+};
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+#define FILP_REG_INIT_VALUE 0x14
+#define DELAY_INTERVAL 50
+#define RETRY_TIMES 10
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+ //CMOS_WIN_SIZE("QVGA", 320, 240, gc2035_320_240_regs),
+ CMOS_WIN_SIZE("VGA", 640, 480, gc2035_640_480_regs),
+ //CMOS_WIN_SIZE("SVGA", 800, 600, gc2035_800_600_regs),
+ //CMOS_WIN_SIZE("720p", 1280, 720, gc2035_1280_720_regs),
+ CMOS_WIN_SIZE("UXGA", 1600, 1200, gc2035_1600_1200_regs),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = gc2035_wb_auto;
+ size = ARRAY_SIZE(gc2035_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = gc2035_wb_incandescent;
+ size = ARRAY_SIZE(gc2035_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = gc2035_wb_daylight;
+ size = ARRAY_SIZE(gc2035_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = gc2035_wb_cloudy;
+ size = ARRAY_SIZE(gc2035_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = gc2035_wb_fluorescent;
+ size = ARRAY_SIZE(gc2035_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_scenemode(struct cmos_subdev *sd, enum v4l2_scene_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case SCENE_MODE_AUTO:
+ regs = gc2035_scene_mode_auto;
+ size = ARRAY_SIZE(gc2035_scene_mode_auto);
+ break;
+ case SCENE_MODE_NIGHTSHOT:
+ regs = gc2035_scene_mode_night;
+ size = ARRAY_SIZE(gc2035_scene_mode_night);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_exposure(struct cmos_subdev *sd, enum v4l2_exposure_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case -2:
+ regs = gc2035_exposure_neg6;
+ size = ARRAY_SIZE(gc2035_exposure_neg6);
+ break;
+ case -1:
+ regs = gc2035_exposure_neg3;
+ size = ARRAY_SIZE(gc2035_exposure_neg3);
+ break;
+ case 0:
+ regs = gc2035_exposure_zero;
+ size = ARRAY_SIZE(gc2035_exposure_zero);
+ break;
+ case 1:
+ regs = gc2035_exposure_pos3;
+ size = ARRAY_SIZE(gc2035_exposure_pos3);
+ break;
+ case 2:
+ regs = gc2035_exposure_pos6;
+ size = ARRAY_SIZE(gc2035_exposure_pos6);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_antibanding(struct cmos_subdev *sd, int value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case 0:
+ regs = gc2035_antibanding_auto;
+ size = ARRAY_SIZE(gc2035_antibanding_auto);
+ break;
+ case 1:
+ regs = gc2035_antibanding_50hz;
+ size = ARRAY_SIZE(gc2035_antibanding_50hz);
+ break;
+ case 2:
+ regs = gc2035_antibanding_60hz;
+ size = ARRAY_SIZE(gc2035_antibanding_60hz);
+ break;
+ case 3:
+ regs = gc2035_antibanding_off;
+ size = ARRAY_SIZE(gc2035_antibanding_off);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+
+ return 0;
+}
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data,tmp=0 ;
+ uint8_t retry_times=0;
+
+ sensor_write(sd, 0xfe, 0x00); // set page0
+ data=sensor_read(sd, 0x17);
+
+ switch (value) {
+ case 0:
+ data &= ~0x01;
+ break;
+ case 1:
+ data |= 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tmp=data | FILP_REG_INIT_VALUE;
+ for(retry_times=0;retry_times<RETRY_TIMES;retry_times++){
+ sensor_write(sd, 0x17, tmp);
+ msleep(50+retry_times*DELAY_INTERVAL);
+ if(sensor_read(sd, 0x17)==tmp)
+ break;
+ }
+
+ return 0;
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data,tmp=0;
+ uint8_t retry_times=0;
+
+ sensor_write(sd, 0xfe, 0x00); // set page0
+ data=sensor_read(sd, 0x17);
+
+ switch (value) {
+ case 0:
+ data &= ~0x02;
+ break;
+ case 1:
+ data |= 0x02;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tmp=data | FILP_REG_INIT_VALUE;
+ for(retry_times=0;retry_times<RETRY_TIMES;retry_times++){
+ sensor_write(sd, 0x17, tmp);
+ msleep(50+retry_times*DELAY_INTERVAL);
+ if(sensor_read(sd, 0x17)==tmp)
+ break;
+ }
+ return 0;
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ case V4L2_CID_EXPOSURE:
+ return v4l2_ctrl_query_fill(qc, -2, 2, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return sensor_s_scenemode(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return sensor_s_exposure(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ case V4L2_CID_CAMERA_ANTI_BANDING:
+ return sensor_s_antibanding(sd, ctrl->value);
+ default:
+ case WMT_V4L2_CID_CAMERA_ANTIBANDING:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ printk(KERN_DEBUG "%s, s_mbus_fmt width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win) {
+ pr_err("%s, s_mbus_fmt failed, width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, win->regs, win->size);
+ msleep(150);
+ //msleep(500);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return 0;
+}
+
+static int sensor_enum_framesizes(struct cmos_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ const struct cmos_win_size *win = &cmos_supported_win_sizes[index];
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ uint32_t data = 0;
+
+ data |= (sensor_read(sd, 0xf0) & 0xff) << 8;
+ data |= (sensor_read(sd, 0xf1) & 0xff);
+
+ return (data == sd->id) ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ if (sensor_identify(sd)) {
+ return -1;
+ }
+
+ sensor_write_array(sd, gc2035_default_regs_init,
+ ARRAY_SIZE(gc2035_default_regs_init));
+
+ msleep(100);
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ sensor_write_array(sd, gc2035_default_regs_exit,
+ ARRAY_SIZE(gc2035_default_regs_exit));
+ return 0;
+}
+
+static struct cmos_subdev_ops gc2035_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+ .enum_framesizes = sensor_enum_framesizes,
+};
+
+struct cmos_subdev gc2035 = {
+ .name = "gc2035",
+ .i2c_addr = 0x3c,
+ .id = 0x2035,
+ .max_width = 1600,
+ .max_height = 1200,
+ .ops = &gc2035_ops,
+};
+
+#if 0
+static int __init gc2035_init(void)
+{
+ return cmos_register_sudbdev(&gc2035);
+}
+
+static void __exit gc2035_exit(void)
+{
+ return cmos_unregister_subdev(&gc2035);
+}
+
+module_init(gc2035_init);
+module_exit(gc2035_exit);
+
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035.h b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035.h
new file mode 100755
index 00000000..99ee760d
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035.h
@@ -0,0 +1,1105 @@
+#ifndef GC2035_H
+#define GC2035_H
+
+
+// Scene Mode
+uint8_t gc2035_scene_mode_auto[] = {
+ 0xfe,0x01,
+ 0x3e,0x40,
+ 0x1f,0xa0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x50,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe,0x00,
+};
+
+uint8_t gc2035_scene_mode_night[] = {
+ 0xfe, 0x01,
+ 0x3e, 0x60,
+ 0x1f,0xe0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x80,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe, 0x00,
+};
+
+
+// White Balance
+uint8_t gc2035_wb_auto [] = {
+ 0xfe, 0x00,
+// 0xb3, 0x61, //2013-8-16 dustin
+// 0xb4, 0x40,
+// 0xb5, 0x61,
+ 0x82, 0xfe,
+};
+
+uint8_t gc2035_wb_incandescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0xa8,
+};
+
+uint8_t gc2035_wb_fluorescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0x78,
+};
+
+uint8_t gc2035_wb_daylight [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x78,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+uint8_t gc2035_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x58,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+
+// Exposure
+uint8_t gc2035_exposure_neg6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x68,
+ 0xfe, 0x02,
+ 0xd5, 0xe0,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc2035_exposure_neg3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x70,
+ 0xfe, 0x02,
+ 0xd5, 0xf0,
+ 0xfe, 0x00,
+
+};
+
+uint8_t gc2035_exposure_zero[] = {
+ 0xfe, 0x01,
+ 0x13, 0x78,
+ 0xfe, 0x02,
+ 0xd5, 0xf8,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_pos3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x80,
+ 0xfe, 0x02,
+ 0xd5, 0x10,
+ 0xfe, 0x00,
+
+
+};
+
+uint8_t gc2035_exposure_pos6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x88,
+ 0xfe, 0x02,
+ 0xd5, 0x20,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_antibanding_auto[] = {
+ 0xfe, 0x00,
+ 0x05, 0x01,
+ 0x06, 0x39,
+ 0x07, 0x00,
+ 0x08, 0x14,
+ 0xfe, 0x01,
+ 0x27, 0x00,
+ 0x28, 0x8b,
+ 0x29, 0x04,
+ 0x2a, 0x58,
+ 0x2b, 0x04,
+ 0x2c, 0x58,
+ 0x2d, 0x06,
+ 0x2e, 0x84,
+ 0x2f, 0x08,
+ 0x30, 0xb0,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_antibanding_50hz[] = {
+ 0xfe , 0x00,
+ 0x05 , 0x01,// HB
+ 0x06 , 0x23,
+ 0x07 , 0x00,// VB
+ 0x08 , 0x14,
+ 0xfe , 0x01,
+ 0x27 , 0x00,// step
+ 0x28 , 0xaa,
+ 0x29 , 0x04,// level 0 13.75
+ 0x2a , 0xa6,
+ 0x2b , 0x04,// level 1 13.75
+ 0x2c , 0xa6,
+ 0x2d , 0x06,// level 2 10
+ 0x2e , 0xa4,
+ 0x2f , 0x08,// level 3 7.5
+ 0x30 , 0xa2,
+ 0xfe , 0x00,
+
+
+};
+
+uint8_t gc2035_antibanding_60hz[] = {
+ 0xfe, 0x00,
+ 0x05, 0x01,
+ 0x06, 0x39,
+ 0x07, 0x00,
+ 0x08, 0x14,
+ 0xfe, 0x01,
+ 0x27, 0x00,
+ 0x28, 0x8b,
+ 0x29, 0x04,
+ 0x2a, 0x58,
+ 0x2b, 0x04,
+ 0x2c, 0x58,
+ 0x2d, 0x06,
+ 0x2e, 0x84,
+ 0x2f, 0x08,
+ 0x30, 0xb0,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_antibanding_off[] = {
+ 0xfe , 0x00,
+ 0x05 , 0x01,// HB
+ 0x06 , 0x23,
+ 0x07 , 0x00,// VB
+ 0x08 , 0x14,
+ 0xfe , 0x01,
+ 0x27 , 0x00,// step
+ 0x28 , 0xaa,
+ 0x29 , 0x04,// level 0 13.75
+ 0x2a , 0xa6,
+ 0x2b , 0x04,// level 1 13.75
+ 0x2c , 0xa6,
+ 0x2d , 0x06,// level 2 10
+ 0x2e , 0xa4,
+ 0x2f , 0x08,// level 3 7.5
+ 0x30 , 0xa2,
+ 0xfe , 0x00,
+};
+
+uint8_t gc2035_176_144_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x33,
+ 0x9b,0x00,
+ 0x9f,0x00,
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0x92,//600
+ 0x97,0x00,
+ 0x98,0xb0,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+
+ 0xfe,0x03,
+ 0x42,0x60,
+ 0x43,0x01,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_320_240_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x22,
+ 0x9b,0x00,
+ 0x9f,0x00,
+
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0xf2,//600
+ 0x97,0x01,
+ 0x98,0x40,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x02,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_1280_720_regs[]={
+ 0xfe,0x00,
+ 0x0a,0xf0,
+ 0x0c,0xa0,
+ 0x0d,0x02,
+ 0x0e,0xd8,
+ 0x0f,0x05,
+ 0x10,0x18,
+ 0xfe,0x00,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x05,0x01,
+ 0x06,0xaa,
+ 0x07,0x00,
+ 0x08,0xf6,
+ 0xfe,0x01,
+ 0x27,0x00,
+ 0x28,0xa1,
+ 0x29,0x03,
+ 0x2a,0xc6,
+ 0x2b,0x04,
+ 0x2c,0x67,
+ 0x2d,0x06,
+ 0x2e,0x4a,
+ 0x2f,0x07,
+ 0x30,0x8c,
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xb6,0x03,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x99,0x11,
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0xd0,
+ 0x97,0x05,
+ 0x98,0x00,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+ 0xfa,0x11,
+};
+
+uint8_t gc2035_1600_1200_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x02,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x04,
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+
+ 0xc8,0x00,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+};
+
+uint8_t gc2035_800_600_regs[]={
+ 0xfe,0x00,
+ 0xfa,0x00,
+ 0xc8,0x00,
+
+ 0x99,0x22,
+ 0x9a,0x07,
+ 0x9b,0x00,
+ 0x9c,0x00,
+ 0x9d,0x00,
+ 0x9e,0x00,
+ 0x9f,0x00,
+ 0xa0,0x00,
+ 0xa1,0x00,
+ 0xa2,0x00,
+
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0x58,
+ 0x97,0x03,
+ 0x98,0x20,
+};
+
+uint8_t gc2035_640_480_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x00,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+
+ 0xc8,0x15,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x00,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+ 0xb6,0x03,
+};
+
+uint8_t gc2035_default_regs_init[]={
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfe,0x80,
+ 0xfc,0x06,
+ 0xf2,0x00,
+ 0xf3,0x00,
+ 0xf4,0x00,
+ 0xf5,0x00,
+ 0xf9,0xfe, //[0] pll enable
+ 0xfa,0x00,
+ 0xf6,0x00,
+ 0xf7,0x15, //pll enable
+
+ 0xf8,0x84,//85
+ 0xfe,0x00,
+ 0x82,0x00,
+ 0xb3,0x60,
+ 0xb4,0x40,
+ 0xb5,0x60,
+
+ 0x03,0x02,
+ 0x04,0x71,
+
+ ////,/meare window ///////////
+ 0xfe,0x00,
+ 0xec,0x06,//04
+ 0xed,0x06,//04
+ 0xee,0x62,//60
+ 0xef,0x92,//90
+
+ ////,//anog/////////////
+ 0x0a,0x00, //row start
+ 0x0c,0x00, //col start
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06, //Window setting
+ 0x10,0x58,
+ 0x17,0x14, //[0]mirror [1]flip
+ 0x18,0x0a, //0a 2012.10.26
+ 0x19,0x0a, //AD pipe number
+ 0x1a,0x01, //CISCTL mode4
+ 0x1b,0x8b,
+ 0x1c,0x05,//add by lanking 20130403
+ 0x1e,0x88, //analog mode1 [7] tx-high en [5:3]COL_bias
+ 0x1f,0x08, //[3] tx-low en//
+ 0x20,0x05, //[0]adclk mode},{0x[1]rowclk_MODE [2]rsthigh_en
+ 0x21,0x0f, //[6:4]rsg
+ 0x22,0xf0, //[3:0]vref 20130403
+ 0x23,0xc3, //f3//ADC_r
+ 0x24,0x17, //pad drive 16
+
+
+ 0xfe,0x01,
+ 0x11,0x20,//AEC_out_slope},{0x
+ 0x1f,0xc0,//max_post_gain
+ 0x20,0x48,//max_pre_gain
+ 0x47,0x30,//AEC_outdoor_th
+ 0x0b,0x10,//
+ 0x13,0x78,//y_target 84
+ 0xfe,0x00,
+
+#if 0
+ 0x05,0x01,//hb
+ 0x06,0x11,
+ 0x07,0x00,//vb
+ 0x08,0x50,
+ 0xfe,0x01,
+ 0x27,0x00,//step
+ 0x28,0xa0,
+ 0x29,0x05,//level1
+ 0x2a,0x00,
+ 0x2b,0x05,//level2
+ 0x2c,0x00,
+ 0x2d,0x06,//6e8//level3
+ 0x2e,0xe0,
+ 0x2f,0x0a,//level4
+ 0x30,0x00,
+#endif
+ 0x05,0x01,//hb
+ 0x06,0x5c,
+ 0x07,0x00,//vb
+ 0x08,0x32,
+ 0xfe,0x01,
+ 0x27,0x00,//step
+ 0x28,0x7d,
+ 0x29,0x04,//level1
+ 0x2a,0xe2,
+ 0x2b,0x05,//level2
+ 0x2c,0xdc,
+ 0x2d,0x05,//6e8//level3
+ 0x2e,0xdc,
+ 0x2f,0x09,//level4
+ 0x30,0xc4,
+
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xfe,0x00, //0x},{0x},{0x},{0x},{0x
+ 0xb6,0x03, //AEC enable
+ 0xfe,0x00,
+
+ ////,BLK////
+ 0x3f,0x00, //prc close
+ 0x40,0x77,//
+ 0x42,0x7f,
+ 0x43,0x30,
+ 0x5c,0x08,
+ 0x5e,0x20,
+ 0x5f,0x20,
+ 0x60,0x20,
+ 0x61,0x20,
+ 0x62,0x20,
+ 0x63,0x20,
+ 0x64,0x20,
+ 0x65,0x20,
+
+ //blo,/////////
+ 0x80,0xff,
+ 0x81,0x26,//38},{0x//skin_Y 8c_debug
+ 0x87,0x90, //[7]middle gamma
+ 0x84,0x02, //output put foramat
+ 0x86,0x02, //02 //sync plority
+ 0x8b,0xbc,
+ 0xb0,0x80, //globle gain
+ 0xc0,0x40,//Yuv bypass
+
+ ////lsc///////////
+ 0xfe,0x01,
+ 0xc2,0x38,
+ 0xc3,0x25,
+ 0xc4,0x21,
+ 0xc8,0x19,
+ 0xc9,0x12,
+ 0xca,0x0e,
+ 0xbc,0x43,
+ 0xbd,0x18,
+ 0xbe,0x1b,
+ 0xb6,0x40,
+ 0xb7,0x2e,
+ 0xb8,0x26,
+ 0xc5,0x05,
+ 0xc6,0x03,
+ 0xc7,0x04,
+ 0xcb,0x00,
+ 0xcc,0x00,
+ 0xcd,0x00,
+ 0xbf,0x14,
+ 0xc0,0x22,
+ 0xc1,0x1b,
+ 0xb9,0x00,
+ 0xba,0x05,
+ 0xbb,0x05,
+ 0xaa,0x35,
+ 0xab,0x33,
+ 0xac,0x33,
+ 0xad,0x25,
+ 0xae,0x22,
+ 0xaf,0x27,
+ 0xb0,0x1d,
+ 0xb1,0x20,
+ 0xb2,0x22,
+ 0xb3,0x14,
+ 0xb4,0x15,
+ 0xb5,0x16,
+ 0xd0,0x00,
+ 0xd2,0x07,
+ 0xd3,0x08,
+ 0xd8,0x00,
+ 0xda,0x13,
+ 0xdb,0x17,
+ 0xdc,0x00,
+ 0xde,0x0a,
+ 0xdf,0x08,
+ 0xd4,0x00,
+ 0xd6,0x00,
+ 0xd7,0x0c,
+ 0xa4,0x00,
+ 0xa5,0x00,
+ 0xa6,0x00,
+ 0xa7,0x00,
+ 0xa8,0x00,
+ 0xa9,0x00,
+ 0xa1,0x80,
+ 0xa2,0x80,
+
+ ////,/cc////////////
+ 0xfe,0x02,
+ 0xc0,0x01,
+ 0xc1,0x40, //Green_cc for d
+ 0xc2,0xfc,
+ 0xc3,0x05,
+ 0xc4,0xec,
+ 0xc5,0x42,
+ 0xc6,0xf8,
+ 0xc7,0x40,//for cwf
+ 0xc8,0xf8,
+ 0xc9,0x06,
+ 0xca,0xfd,
+ 0xcb,0x3e,
+ 0xcc,0xf3,
+ 0xcd,0x36,//for A
+ 0xce,0xf6,
+ 0xcf,0x04,
+ 0xe3,0x0c,
+ 0xe4,0x44,
+ 0xe5,0xe5,
+ 0xfe,0x00,
+
+ ////,b stt ////////////////AWB ,ar
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x10, // 10
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x20, // 20
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00, // 30
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x40, // 40
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x50, // 50
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x60, // 60
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x70, // 70
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x80, // 80
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0x90, // 90
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xa0, // a0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xb0, // b0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xc0, // c0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4d,0xd0, // d0
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4e,0x00,
+ 0x4f,0x01,
+ ////,wb vue////////
+ 0xfe,0x01,
+ 0x4f,0x00,
+ 0x4d,0x30,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x40,
+ 0x4e,0x00,
+ 0x4e,0x80,
+ 0x4e,0x80,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4e,0x02,
+ 0x4d,0x53,
+ 0x4e,0x08,
+ 0x4e,0x04,
+ 0x4d,0x62,
+ 0x4e,0x10,
+ 0x4d,0x72,
+ 0x4e,0x20,
+ 0x4f,0x01,
+
+ ///a,///
+ 0xfe,0x01,
+ 0x50,0x88,//c0//[6]green mode
+ 0x52,0x40,
+ 0x54,0x60,
+ 0x56,0x06,
+ 0x57,0x20, //pre adjust
+ 0x58,0x01,
+ 0x5b,0x02, //AWB_gain_delta
+ 0x61,0xaa,//R/G stand
+ 0x62,0xaa,//R/G stand
+ 0x71,0x00,
+ 0x74,0x10, //0x//AWB_C_max
+ 0x77,0x08, // 0x//AWB_p2_x
+ 0x78,0xfd, //AWB_p2_y
+ 0x86,0x30,
+ 0x87,0x00,
+ 0x88,0x04,//06},{0x//[1]dark mode
+ 0x8a,0xc0,//awb move mode
+ 0x89,0x75,
+ 0x84,0x08, //0x//auto_window
+ 0x8b,0x00, // 0x//awb compare luma
+ 0x8d,0x70, //awb gain limit R
+ 0x8e,0x70,//G
+ 0x8f,0xf4,//B
+ 0xfe,0x00,
+ 0x82,0x02,//awb_en
+ ////wb end /////////////
+
+ //===,====de
+ 0xfe,0x01,
+
+ 0x9c,0x04,//00
+
+ 0x21,0xbf,
+ 0xfe,0x02,
+ 0xa4,0x00,//
+ 0xa5,0x40, //lsc_th
+ 0xa2,0xa0, //lsc_dec_slope
+ 0xa6,0x80, //dd_th
+ 0xa7,0x80, //ot_th
+ 0xab,0x31, //
+ 0xa9,0x6f, //
+ 0xb0,0x99, //0x//edge effect slope low
+ 0xb1,0x34,//edge effect slope low
+ 0xb3,0x80, //saturation dec slope
+ 0xde,0xb6, //
+ 0x38,0x0f, //
+ 0x39,0x60, //
+ 0xfe,0x00,
+ 0x81,0x26,
+ 0xfe,0x02,
+ 0x83,0x00,//
+ 0x84,0x45,//
+ ////,///Y//////////
+ 0xd1,0x34,//saturation_cb
+ 0xd2,0x34,//saturation_Cr
+ 0xd3,0x40,//contrast ? {{0xd4},{0x80}},//contrast center
+ 0xd5,0xf8,//luma_offset
+ 0xdc,0x30,
+ 0xdd,0xb8,//edge_sa_g,b
+ 0xfe,0x00,
+ ////,dd/////////
+ 0xfe,0x02,
+ 0x88,0x1c,//dn_b_base
+ 0x8c,0xf6, //[2]b_in_dark_inc
+ 0x89,0x03, //dn_c_weight
+ ////,E /////////
+ 0xfe,0x02,
+ 0x90,0x6c,// EEINTP mode1
+ 0x97,0x45,// edge effect
+ //==,=======RGB Gamma
+ 0xfe,0x02,
+ 0x15,0x0a,
+ 0x16,0x12,
+ 0x17,0x19,
+ 0x18,0x1f,
+ 0x19,0x2c,
+ 0x1a,0x38,
+ 0x1b,0x42,
+ 0x1c,0x4e,
+ 0x1d,0x63,
+ 0x1e,0x76,
+ 0x1f,0x87,
+ 0x20,0x96,
+ 0x21,0xa2,
+ 0x22,0xb8,
+ 0x23,0xca,
+ 0x24,0xd8,
+ 0x25,0xe3,
+ 0x26,0xf0,
+ 0x27,0xf8,
+ 0x28,0xfd,
+ 0x29,0xff,
+
+ //===,=========y gamma
+ 0xfe,0x02,
+ 0x2b,0x00,
+ 0x2c,0x04,
+ 0x2d,0x09,
+ 0x2e,0x18,
+ 0x2f,0x27,
+ 0x30,0x37,
+ 0x31,0x49,
+ 0x32,0x5c,
+ 0x33,0x7e,
+ 0x34,0xa0,
+ 0x35,0xc0,
+ 0x36,0xe0,
+ 0x37,0xff,
+ ///1,x120ize//
+ 0xfe,0x00,//
+ 0x90,0x01, //0x//crop enable
+ 0x95,0x04, //0x//1600x1200
+ 0x96,0xb0,
+ 0x97,0x06,
+ 0x98,0x40,
+
+ 0xfe,0x03,
+ 0x42,0x40,
+ 0x43,0x06, //output buf width
+ 0x41,0x02, // Pclk_polarity
+ 0x40,0x40, //00
+ 0x17,0x00, //widv
+ 0xfe,0x00,
+ //ou,t DV////
+
+ 0xfe,0x00,
+ 0x82,0xfe, // fe
+ 0xf2,0x70,
+ 0xf3,0xff,
+ 0xf4,0x00,
+ 0xf5,0x30,
+
+ #if 0
+ //,///ssumple 640X4800//////
+ 0xfe,0x00,
+ 0xfa,0x00,
+ 0xc8,0x00,
+
+ 0x99,0x22,
+ 0x9a,0x07,
+ 0x9b,0x00,
+ 0x9c,0x00,
+ 0x9d,0x00,
+ 0x9e,0x00,
+ 0x9f,0x00,
+ 0xa0,0x00,
+ 0xa1,0x00,
+ 0xa2,0x00,
+
+
+ 0x90,0x01,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+ #endif
+
+
+ 0xfe,0x00,
+ 0x0c,0x00,
+
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+
+ 0xc8,0x15,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x00,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+ 0xb6,0x03,
+
+#if 1
+ //// re zao///
+ 0xfe ,0x00,
+ 0x22 ,0xf0,
+ 0xfe ,0x01,
+ 0x21 ,0xff,
+ 0xfe ,0x02,
+ 0x8a ,0x33,
+ 0x8c ,0x76,
+ 0x8d ,0x85,
+ 0xa6 ,0xf0,
+ 0xae ,0x9f,
+ 0xa2 ,0x90,
+ 0xa5 ,0x40,
+ 0xa7 ,0x30,
+ 0xb0 ,0x88,
+ 0x38 ,0x0b,
+ 0x39 ,0x30,
+ 0xfe ,0x00,
+ 0x87 ,0xb0,
+
+
+ /*
+ //, sma RGB gamma////
+ 0xfe,0x02,
+ 0x15,0x0b,
+ 0x16,0x0e,
+ 0x17,0x10,
+ 0x18,0x12,
+ 0x19,0x19,
+ 0x1a,0x21,
+ 0x1b,0x29,
+ 0x1c,0x31,
+ 0x1d,0x41,
+ 0x1e,0x50,
+ 0x1f,0x5f,
+ 0x20,0x6d,
+ 0x21,0x79,
+ 0x22,0x91,
+ 0x23,0xa5,
+ 0x24,0xb9,
+ 0x25,0xc9,
+ 0x26,0xe1,
+ 0x27,0xee,
+ 0x28,0xf7,
+ 0x29,0xff,
+ */
+ //da,sun///
+ 0xfe,0x02,
+ 0x40,0x06,
+ 0x41,0x23,
+ 0x42,0x3f,
+ 0x43,0x06,
+ 0x44,0x00,
+ 0x45,0x00,
+ 0x46,0x14,
+ 0x47,0x09,
+
+ #endif
+};
+
+uint8_t gc2035_default_regs_exit[]={
+ 0xfe, 0x00,
+ 0xf1, 0x00,
+ 0xf2, 0x00,
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035_pid10_hl_pc1089.h b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035_pid10_hl_pc1089.h
new file mode 100755
index 00000000..45a63cdf
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035_pid10_hl_pc1089.h
@@ -0,0 +1,1158 @@
+#ifndef GC2035_H
+#define GC2035_H
+
+
+// Scene Mode
+uint8_t gc2035_scene_mode_auto[] = {
+ 0xfe,0x01,
+ 0x3e,0x40,
+ 0x1f,0xa0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x50,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe,0x00,
+};
+
+uint8_t gc2035_scene_mode_night[] = {
+ 0xfe, 0x01,
+ 0x3e, 0x60,
+ 0x1f,0xe0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x80,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe, 0x00,
+};
+
+
+// White Balance
+uint8_t gc2035_wb_auto [] = {
+ 0xfe, 0x00,
+// 0xb3, 0x61, //2013-8-16 dustin
+// 0xb4, 0x40,
+// 0xb5, 0x61,
+ 0x82, 0xfe,
+};
+
+uint8_t gc2035_wb_incandescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0xa8,
+};
+
+uint8_t gc2035_wb_fluorescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0x78,
+};
+
+uint8_t gc2035_wb_daylight [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x78,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+uint8_t gc2035_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x58,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+
+// Exposure
+uint8_t gc2035_exposure_neg6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x68,
+ 0xfe, 0x02,
+ 0xd5, 0xd0,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_neg3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x70,
+ 0xfe, 0x02,
+ 0xd5, 0xe0,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_zero[] = {
+ 0xfe, 0x01,
+ 0x13, 0x78,//78
+ 0xfe, 0x02,
+ 0xd5, 0xf8,//00
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_pos3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x80,//80
+ 0xfe, 0x02,
+ 0xd5, 0x10,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_pos6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x88,//88
+ 0xfe, 0x02,
+ 0xd5, 0x20,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_antibanding_auto[] = {
+ 0xfe, 0x00,
+ 0x05, 0x01,
+ 0x06, 0x39,
+ 0x07, 0x00,
+ 0x08, 0x14,
+ 0xfe, 0x01,
+ 0x27, 0x00,
+ 0x28, 0x8b,
+ 0x29, 0x04,
+ 0x2a, 0x58,
+ 0x2b, 0x04,
+ 0x2c, 0x58,
+ 0x2d, 0x06,
+ 0x2e, 0x84,
+ 0x2f, 0x08,
+ 0x30, 0xb0,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_antibanding_50hz[] = {
+ 0xfe , 0x00,
+ 0x05 , 0x01,// HB
+ 0x06 , 0x23,
+ 0x07 , 0x00,// VB
+ 0x08 , 0x14,
+ 0xfe , 0x01,
+ 0x27 , 0x00,// step
+ 0x28 , 0xaa,
+ 0x29 , 0x04,// level 0 13.75
+ 0x2a , 0xa6,
+ 0x2b , 0x04,// level 1 13.75
+ 0x2c , 0xa6,
+ 0x2d , 0x06,// level 2 10
+ 0x2e , 0xa4,
+ 0x2f , 0x08,// level 3 7.5
+ 0x30 , 0xa2,
+ 0xfe , 0x00,
+
+
+};
+
+uint8_t gc2035_antibanding_60hz[] = {
+ 0xfe, 0x00,
+ 0x05, 0x01,
+ 0x06, 0x39,
+ 0x07, 0x00,
+ 0x08, 0x14,
+ 0xfe, 0x01,
+ 0x27, 0x00,
+ 0x28, 0x8b,
+ 0x29, 0x04,
+ 0x2a, 0x58,
+ 0x2b, 0x04,
+ 0x2c, 0x58,
+ 0x2d, 0x06,
+ 0x2e, 0x84,
+ 0x2f, 0x08,
+ 0x30, 0xb0,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_antibanding_off[] = {
+ 0xfe , 0x00,
+ 0x05 , 0x01,// HB
+ 0x06 , 0x23,
+ 0x07 , 0x00,// VB
+ 0x08 , 0x14,
+ 0xfe , 0x01,
+ 0x27 , 0x00,// step
+ 0x28 , 0xaa,
+ 0x29 , 0x04,// level 0 13.75
+ 0x2a , 0xa6,
+ 0x2b , 0x04,// level 1 13.75
+ 0x2c , 0xa6,
+ 0x2d , 0x06,// level 2 10
+ 0x2e , 0xa4,
+ 0x2f , 0x08,// level 3 7.5
+ 0x30 , 0xa2,
+ 0xfe , 0x00,
+};
+
+uint8_t gc2035_176_144_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x33,
+ 0x9b,0x00,
+ 0x9f,0x00,
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0x92,//600
+ 0x97,0x00,
+ 0x98,0xb0,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+
+ 0xfe,0x03,
+ 0x42,0x60,
+ 0x43,0x01,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_320_240_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x22,
+ 0x9b,0x00,
+ 0x9f,0x00,
+
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0xf2,//600
+ 0x97,0x01,
+ 0x98,0x40,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x02,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_1280_720_regs[]={
+ 0xfe,0x00,
+ 0x0a,0xf0,
+ 0x0c,0xa0,
+ 0x0d,0x02,
+ 0x0e,0xd8,
+ 0x0f,0x05,
+ 0x10,0x18,
+ 0xfe,0x00,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x05,0x01,
+ 0x06,0xaa,
+ 0x07,0x00,
+ 0x08,0xf6,
+ 0xfe,0x01,
+ 0x27,0x00,
+ 0x28,0xa1,
+ 0x29,0x03,
+ 0x2a,0xc6,
+ 0x2b,0x04,
+ 0x2c,0x67,
+ 0x2d,0x06,
+ 0x2e,0x4a,
+ 0x2f,0x07,
+ 0x30,0x8c,
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xb6,0x03,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x99,0x11,
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0xd0,
+ 0x97,0x05,
+ 0x98,0x00,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+ 0xfa,0x11,
+};
+
+uint8_t gc2035_1600_1200_regs[]= {
+ 0xfe , 0x00,
+ 0xfa , 0x11,
+ 0xc8 , 0x00,
+
+ 0x90 , 0x01,
+ 0x95 , 0x04,
+ 0x96 , 0xb0,
+ 0x97 , 0x06,
+ 0x98 , 0x40,
+};
+
+uint8_t gc2035_800_600_regs[]={
+ 0xfe,0x00,
+ 0xfa,0x00,
+ 0xc8,0x00,
+
+ 0x99,0x22,
+ 0x9a,0x07,
+ 0x9b,0x00,
+ 0x9c,0x00,
+ 0x9d,0x00,
+ 0x9e,0x00,
+ 0x9f,0x00,
+ 0xa0,0x00,
+ 0xa1,0x00,
+ 0xa2,0x00,
+
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0x58,
+ 0x97,0x03,
+ 0x98,0x20,
+};
+
+uint8_t gc2035_640_480_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x00,
+ 0xfa,0x00,
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+
+ 0xc8,0x15,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x00,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+ 0xb6,0x03,
+};
+
+uint8_t gc2035_default_regs_init[]={
+
+ 0xfe , 0x80,
+ 0xfe , 0x80,
+ 0xfe , 0x80,
+ 0xfc , 0x06,
+ 0xf2 , 0x00,
+ 0xf3 , 0x00,
+ 0xf4 , 0x00,
+ 0xf5 , 0x00,
+ 0xf9 , 0xfe, //[0] pll enable
+ 0xfa , 0x00,
+ 0xf6 , 0x00,
+ 0xf7 , 0x15, //pll enable
+ 0xf8 , 0x85,
+ 0xfe , 0x00,
+ 0x82 , 0x00,
+ 0xb3 , 0x60,
+ 0xb4 , 0x40,
+ 0xb5 , 0x60,
+
+ 0x03 , 0x02,
+ 0x04 , 0xa8,
+
+ //////////measure window ///////////
+ 0xfe , 0x00,
+ 0xec , 0x06,//04
+ 0xed , 0x06,//04
+ 0xee , 0x62,//60
+ 0xef , 0x92,//90
+
+ ///////////analog/////////////
+ 0x0a , 0x00, //row start
+ 0x0c , 0x00, //col start
+ 0x0d , 0x04,
+ 0x0e , 0xc0,
+ 0x0f , 0x06, //Window setting
+ 0x10 , 0x58,
+ 0x17 , 0x14, //[0]mirror [1]flip
+ 0x18 , 0x0e, //0a 2012.10.26
+ 0x19 , 0x0c, //AD pipe number
+ 0x1a , 0x01, //CISCTL mode4
+ 0x1b , 0x8b,
+ 0x1c , 0x05,//add by lanking 20130403
+ 0x1e , 0x88, //analog mode1 [7] tx-high en [5:3]COL_bias
+ 0x1f , 0x08, //[3] tx-low en//
+ 0x20 , 0x05, //[0]adclk mode , 0x[1]rowclk_MODE [2]rsthigh_en
+ 0x21 , 0x0f, //[6:4]rsg
+ 0x22 , 0xf0, //[3:0]vref d0 20130628
+ 0x23 , 0xc3, //f3//ADC_r
+ 0x24 , 0x17, //pad drive 16
+
+ //AWB_gray
+ 0xfe , 0x01,
+ 0x4f , 0x00,
+ 0x4d , 0x32, // 30
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4d , 0x42, // 40
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4d , 0x52, // 50
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4d , 0x62, // 60
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4d , 0x72, // 70
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4d , 0x82, // 80
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4d , 0x92, // 90
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4f , 0x01,
+ 0x50 , 0x88,
+ 0xfe , 0x00,
+ 0x82 , 0xfe,
+
+ ///////awb start ////////////////
+ 0xfe , 0x01,
+ 0x50 , 0x88,//c0//[6]green mode
+ 0x52 , 0x40,
+ 0x54 , 0x60,
+ 0x56 , 0x06,
+ 0x57 , 0x20,//pre adjust
+ 0x58 , 0x01,
+ 0x5b , 0x02,//AWB_gain_delta
+ 0x61 , 0xaa,//R/G stand
+ 0x62 , 0xaa,//R/G stand
+ 0x71 , 0x00,
+ 0x74 , 0x10,//AWB_C_max
+ 0x77 , 0x08,//AWB_p2_x
+ 0x78 , 0xfd,//AWB_p2_y
+ 0x86 , 0x30,
+ 0x87 , 0x00,
+ 0x88 , 0x04,//[1]dark mode
+ 0x8a , 0xc0,//awb move mode
+ 0x89 , 0x75,
+ 0x84 , 0x08,//auto_window
+ 0x8b , 0x00,//awb compare luma
+ 0x8d , 0x70,//awb gain limit R
+ 0x8e , 0x70,//G
+ 0x8f , 0xf4,//B
+ /////////awb end /////////////
+
+ //AEC
+ 0xfe , 0x01,
+ 0x11 , 0x20,//AEC_out_slope , 0x
+ 0x1f , 0xc0,//max_post_gain
+ 0x20 , 0x60,//max_pre_gain
+ 0x47 , 0x30,//AEC_outdoor_th
+ 0x0b , 0x10,//
+ 0x13 , 0x78,//y_target 75
+
+
+ 0xfe , 0x00,
+ 0x05 , 0x01,
+ 0x06 , 0x23,
+ 0x07 , 0x00,
+ 0x08 , 0x14,
+ 0xfe , 0x01,
+ 0x27 , 0x00,
+ 0x28 , 0xaa,
+ 0x29 , 0x04,// level 0 13.75
+ 0x2a , 0xa6,
+ 0x2b , 0x04,// level 1 13.75
+ 0x2c , 0xa6,
+ 0x2d , 0x06,// level 2 10
+ 0x2e , 0xa4,
+ 0x2f , 0x08,// level 3 7.5
+ 0x30 , 0xa2,
+ 0xfe , 0x00,//
+ 0xb6 , 0x03,//AEC enable
+ 0xfe , 0x00,
+
+ /////////BLK//////
+ 0x3f , 0x00,//prc close
+ 0x40 , 0x77,//
+ 0x42 , 0x7f,
+ 0x43 , 0x30,
+ 0x5c , 0x08,
+ 0x5e , 0x20,
+ 0x5f , 0x20,
+ 0x60 , 0x20,
+ 0x61 , 0x20,
+ 0x62 , 0x20,
+ 0x63 , 0x20,
+ 0x64 , 0x20,
+ 0x65 , 0x20,
+
+ ///block////////////
+ 0x80 , 0xff,
+ 0x81 , 0x26,//38 //skin_Y 8c_debug
+ 0x87 , 0x90,//[7]middle gamma
+ 0x84 , 0x02,//output put foramat
+ 0x86 , 0x06,//02 //sync plority lanking
+ 0x8b , 0xbc,
+ 0xb0 , 0x80,//globle gain
+ 0xc0 , 0x40,//Yuv bypass
+
+ //////lsc/////////////
+#if 0
+ 0xfe , 0x01,
+ 0xc2 , 0x38,
+ 0xc3 , 0x25,
+ 0xc4 , 0x21,
+ 0xc8 , 0x19,
+ 0xc9 , 0x12,
+ 0xca , 0x0e,
+ 0xbc , 0x43,
+ 0xbd , 0x18,
+ 0xbe , 0x1b,
+ 0xb6 , 0x40,
+ 0xb7 , 0x2e,
+ 0xb8 , 0x26,
+ 0xc5 , 0x05,
+ 0xc6 , 0x03,
+ 0xc7 , 0x04,
+ 0xcb , 0x00,
+ 0xcc , 0x00,
+ 0xcd , 0x00,
+ 0xbf , 0x14,
+ 0xc0 , 0x22,
+ 0xc1 , 0x1b,
+ 0xb9 , 0x00,
+ 0xba , 0x05,
+ 0xbb , 0x05,
+ 0xaa , 0x35,
+ 0xab , 0x33,
+ 0xac , 0x33,
+ 0xad , 0x25,
+ 0xae , 0x22,
+ 0xaf , 0x27,
+ 0xb0 , 0x1d,
+ 0xb1 , 0x20,
+ 0xb2 , 0x22,
+ 0xb3 , 0x14,
+ 0xb4 , 0x15,
+ 0xb5 , 0x16,
+ 0xd0 , 0x00,
+ 0xd2 , 0x07,
+ 0xd3 , 0x08,
+ 0xd8 , 0x00,
+ 0xda , 0x13,
+ 0xdb , 0x17,
+ 0xdc , 0x00,
+ 0xde , 0x0a,
+ 0xdf , 0x08,
+ 0xd4 , 0x00,
+ 0xd6 , 0x00,
+ 0xd7 , 0x0c,
+ 0xa4 , 0x00,
+ 0xa5 , 0x00,
+ 0xa6 , 0x00,
+ 0xa7 , 0x00,
+ 0xa8 , 0x00,
+ 0xa9 , 0x00,
+ 0xa1 , 0x80,
+ 0xa2 , 0x80,
+#endif
+ 0xfe,0x01,
+ 0xa1,0x80, // center_row
+ 0xa2,0x80, // center_col
+ 0xa4,0x07, // sign of b1
+ 0xa5,0x00, // sign of b1
+ 0xa6,0x00, // sign of b4
+ 0xa7,0x00, // sign of b4
+ 0xa8,0x00, // sign of b22
+ 0xa9,0x07, // sign of b22
+ 0xaa,0x1e, // Q1_b1 of R
+ 0xab,0x49, // Q1_b1 of G
+ 0xac,0x1e, // Q1_b1 of B
+ 0xad,0x30, // Q2_b1 of R
+ 0xae,0x1c, // Q2_b1 of G
+ 0xaf,0x1b, // Q2_b1 of B
+ 0xb0,0x56, // Q3_b1 of R
+ 0xb1,0x88, // Q3_b1 of G
+ 0xb2,0x53, // Q3_b1 of B
+ 0xb3,0xff, // Q4_b1 of R
+ 0xb4,0xf8, // Q4_b1 of G
+ 0xb5,0xcb, // Q4_b1 of B
+ 0xb6,0x62, // right_b2 of R
+ 0xb7,0x19, // right_b2 of G
+ 0xb8,0x00, // right_b2 of B
+ 0xb9,0xff, // right_b4 of R
+ 0xba,0xff, // right_b4 of G
+ 0xbb,0xff, // right_b4 of B
+ 0xbc,0x67, // left_b2 of R
+ 0xbd,0x3a, // left_b2 of G
+ 0xbe,0x25, // left_b2 of B
+ 0xbf,0x65, // left_b4 of R
+ 0xc0,0x49, // left_b4 of G
+ 0xc1,0x60, // left_b4 of B
+ 0xc2,0x3f, // up_b2 of R
+ 0xc3,0x1b, // up_b2 of G
+ 0xc4,0x19, // up_b2 of B
+ 0xc5,0x45, // up_b4 of R
+ 0xc6,0x39, // up_b4 of G
+ 0xc7,0x19, // up_b4 of B
+ 0xc8,0x12, // down_b2 of R
+ 0xc9,0x02, // down_b2 of G
+ 0xca,0x00, // down_b2 of B
+ 0xcb,0xff, // down_b4 of R
+ 0xcc,0xff, // down_b4 of G
+ 0xcd,0xf3, // down_b4 of B
+ 0xd0,0xff, // right_up_b22 of R
+ 0xd2,0x3b, // right_up_b22 of G
+ 0xd3,0x7d, // right_up_b22 of B
+ 0xd4,0xff, // right_down_b22 of R
+ 0xd6,0xff, // right_down_b22 of G
+ 0xd7,0xff, // right_down_b22 of B
+ 0xd8,0xff, // left_up_b22 of R
+ 0xda,0x89, // left_up_b22 of G
+ 0xdb,0xa3, // left_up_b22 of B
+ 0xdc,0x1c, // left_down_b22 of R
+ 0xde,0x99, // left_down_b22 of G
+ 0xdf,0x27, // left_down_b22 of B
+ 0xfe,0x00,
+
+ //////////cc//////////////
+ 0xfe , 0x02,
+ 0xc0 , 0x01,
+ 0xc1 , 0x38,//Green_cc for d 40
+ 0xc2 , 0xfc,
+ 0xc3 , 0x05,
+ 0xc4 , 0xec,
+ 0xc5 , 0x42,
+ 0xc6 , 0xf8,
+ 0xc7 , 0x40,//for cwf
+ 0xc8 , 0xf8,
+ 0xc9 , 0x06,
+ 0xca , 0xfd,
+ 0xcb , 0x3e,
+ 0xcc , 0xf3,
+ 0xcd , 0x36,//for A
+ 0xce , 0xf6,
+ 0xcf , 0x04,
+ 0xe3 , 0x0c,
+ 0xe4 , 0x44,
+ 0xe5 , 0xe5,
+ 0xfe , 0x00,
+
+ ///==========asde
+ 0xfe , 0x01,
+ 0x21 , 0xbf,
+ 0xfe , 0x02,
+ 0xa4 , 0x00,
+ 0xa5 , 0x40,//lsc_th
+ 0xa2 , 0xa0,//lsc_dec_slope
+ 0xa6 , 0x80,//dd_th
+ 0xa7 , 0x80,//ot_th
+ 0xab , 0x31,
+ 0xa9 , 0x6f,
+ 0xb0 , 0x99,//edge effect slope low
+ 0xb1 , 0x34,//edge effect slope low
+ 0xb3 , 0x80,//saturation dec slope
+ 0xde , 0xb6,
+ 0x38 , 0x0f,
+ 0x39 , 0x60,
+ 0xfe , 0x00,
+ 0x81 , 0x26,
+ 0xfe , 0x02,
+ 0x83 , 0x00,
+ 0x84 , 0x45,
+ ////////////YCP//////////
+ 0xd1 , 0x38,//saturation_cb
+ 0xd2 , 0x34,//saturation_Cr 38
+ 0xd3 , 0x40,//contrast
+ 0xd4 , 0x80,//contrast center
+ 0xd5 , 0xf8,//luma_offset
+ 0xdc , 0x30,
+ 0xdd , 0xb8,//edge_sa_g,b
+ 0xfe , 0x00,
+ //////////dndd///////////
+ 0xfe , 0x02,
+ 0x88 , 0x15,//dn_b_base
+ 0x8c , 0xf6,//[2]b_in_dark_inc
+ 0x89 , 0x03,//dn_c_weight
+ //////////EE ///////////
+ 0xfe , 0x02,
+ 0x90 , 0x6c,// EEINTP mode1
+ 0x97 , 0x45,// edge effect
+ ////==============RGB Gamma ////
+ 0xfe , 0x02,
+ 0x15 , 0x0a,
+ 0x16 , 0x12,
+ 0x17 , 0x19,
+ 0x18 , 0x1f,
+ 0x19 , 0x2c,
+ 0x1a , 0x38,
+ 0x1b , 0x42,
+ 0x1c , 0x4e,
+ 0x1d , 0x63,
+ 0x1e , 0x76,
+ 0x1f , 0x87,
+ 0x20 , 0x96,
+ 0x21 , 0xa2,
+ 0x22 , 0xb8,
+ 0x23 , 0xca,
+ 0x24 , 0xd8,
+ 0x25 , 0xe3,
+ 0x26 , 0xf0,
+ 0x27 , 0xf8,
+ 0x28 , 0xfd,
+ 0x29 , 0xff,
+ //// small RGB gamma////
+ /*
+ 0xfe , 0x02,
+ 0x15 , 0x0b,
+ 0x16 , 0x0e,
+ 0x17 , 0x10,
+ 0x18 , 0x12,
+ 0x19 , 0x19,
+ 0x1a , 0x21,
+ 0x1b , 0x29,
+ 0x1c , 0x31,
+ 0x1d , 0x41,
+ 0x1e , 0x50,
+ 0x1f , 0x5f,
+ 0x20 , 0x6d,
+ 0x21 , 0x79,
+ 0x22 , 0x91,
+ 0x23 , 0xa5,
+ 0x24 , 0xb9,
+ 0x25 , 0xc9,
+ 0x26 , 0xe1,
+ 0x27 , 0xee,
+ 0x28 , 0xf7,
+ 0x29 , 0xff,
+ */
+
+ ///=================y gamma
+ 0xfe , 0x02,
+ 0x2b , 0x00,
+ 0x2c , 0x04,
+ 0x2d , 0x09,
+ 0x2e , 0x18,
+ 0x2f , 0x27,
+ 0x30 , 0x37,
+ 0x31 , 0x49,
+ 0x32 , 0x5c,
+ 0x33 , 0x7e,
+ 0x34 , 0xa0,
+ 0x35 , 0xc0,
+ 0x36 , 0xe0,
+ 0x37 , 0xff,
+
+
+ //AWB clear
+ 0xfe , 0x01,
+ 0x4f , 0x00,
+ 0x4d , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4d , 0x10, // 10
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4d , 0x20, // 20
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4d , 0x30,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00, // 30
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4d , 0x40, // 40
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4d , 0x50, // 50
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4d , 0x60, // 60
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4d , 0x70, // 70
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4d , 0x80, // 80
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4d , 0x90, // 90
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4d , 0xa0, // a0
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4d , 0xb0, // b0
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4d , 0xc0, // c0
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4d , 0xd0, // d0
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4f , 0x01,
+ /////// awb value////////
+ 0xfe , 0x01,
+ 0x4f , 0x00,
+ 0x4d , 0x30,
+ 0x4e , 0x00,
+ 0x4e , 0x80,
+ 0x4e , 0x80,
+ 0x4e , 0x02,
+ 0x4e , 0x02,
+ 0x4d , 0x40,
+ 0x4e , 0x00,
+ 0x4e , 0x80,
+ 0x4e , 0x80,
+ 0x4e , 0x02,
+ 0x4e , 0x02,
+ 0x4e , 0x02,
+ 0x4d , 0x53,
+ 0x4e , 0x08,
+ 0x4e , 0x04,
+ 0x4d , 0x62,
+ 0x4e , 0x10,
+ 0x4d , 0x72,
+ 0x4e , 0x20,
+ 0x4f , 0x01,
+
+ /////1600x1200size//
+ 0xfe , 0x00,//
+ 0x90 , 0x01, ////crop enable
+ 0x95 , 0x04, ////1600x1200
+ 0x96 , 0xb0,
+ 0x97 , 0x06,
+ 0x98 , 0x40,
+
+ 0xfe , 0x03,
+ 0x17 , 0x00, //widv
+ 0x40 , 0x40, //00
+ 0x41 , 0x02, // Pclk_polarity
+ 0x42 , 0x40,
+ 0x43 , 0x06, //output buf width
+
+ 0xfe , 0x00,
+ 0xad , 0x7c,
+ 0xae , 0x81,
+ 0xaf , 0x80,
+
+
+ ////output DVP/////
+ 0xfe , 0x00,
+ 0x82 , 0xfe,
+ 0xf2 , 0x70,
+ 0xf3 , 0xff,
+ 0xf4 , 0x00,
+ 0xf5 , 0x30,
+#if 0
+ 0xfe , 0x00 ,
+ 0x80 , 0x08 ,
+ 0x81 , 0x00 ,
+ 0x82 , 0x00 ,
+ 0xa3 , 0x80 ,
+ 0xa4 , 0x80 ,
+ 0xa5 , 0x80 ,
+ 0xa6 , 0x80 ,
+ 0xa7 , 0x80 ,
+ 0xa8 , 0x80 ,
+ 0xa9 , 0x80 ,
+ 0xaa , 0x80 ,
+ 0xad , 0x80 ,
+ 0xae , 0x80 ,
+ 0xaf , 0x80 ,
+ 0xb3 , 0x40 ,
+ 0xb4 , 0x40 ,
+ 0xb5 , 0x40 ,
+ 0xfe , 0x01 ,
+ 0x0a , 0x40 ,
+ 0x13 , 0x48 ,
+ 0x9f , 0x40 ,
+ 0xfe , 0x02 ,
+ 0xd0 , 0x40 ,
+ 0xd1 , 0x20 ,
+ 0xd2 , 0x20 ,
+ 0xd3 , 0x40 ,
+ 0xd5 , 0x00 ,
+ 0xdd , 0x00 ,
+ 0xfe , 0x00 ,
+#endif
+};
+
+uint8_t gc2035_default_regs_exit[]={
+ 0xfe, 0x00,
+ 0xf1, 0x00,
+ 0xf2, 0x00,
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035_pid13_hl_pc1388.h b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035_pid13_hl_pc1388.h
new file mode 100755
index 00000000..a7f149f7
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc2035/gc2035_pid13_hl_pc1388.h
@@ -0,0 +1,1152 @@
+#ifndef GC2035_H
+#define GC2035_H
+
+
+// Scene Mode
+uint8_t gc2035_scene_mode_auto[] = {
+ 0xfe,0x01,
+ 0x3e,0x40,
+ 0x1f,0xa0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x50,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe,0x00,
+};
+
+uint8_t gc2035_scene_mode_night[] = {
+ 0xfe, 0x01,
+ 0x3e, 0x60,
+ 0x1f,0xe0,//max_post_gain c0 2013-7-22 dustin
+ 0x20,0x80,//max_pre_gain 60 2013-7-22 dustin
+ 0xfe, 0x00,
+};
+
+
+// White Balance
+uint8_t gc2035_wb_auto [] = {
+ 0xfe, 0x00,
+// 0xb3, 0x61, //2013-8-16 dustin
+// 0xb4, 0x40,
+// 0xb5, 0x61,
+ 0x82, 0xfe,
+};
+
+uint8_t gc2035_wb_incandescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0xa8,
+};
+
+uint8_t gc2035_wb_fluorescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0x78,
+};
+
+uint8_t gc2035_wb_daylight [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x78,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+uint8_t gc2035_wb_cloudy [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x58,
+ 0xb4, 0x40,
+ 0xb5, 0x50,
+};
+
+
+// Exposure
+uint8_t gc2035_exposure_neg6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x68,
+ 0xfe, 0x02,
+ 0xd5, 0xe0,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_neg3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x70,
+ 0xfe, 0x02,
+ 0xd5, 0xf0,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_zero[] = {
+ 0xfe, 0x01,
+ 0x13, 0x88,//78
+ 0xfe, 0x02,
+ 0xd5, 0x04,//00
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_pos3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x98,//80
+ 0xfe, 0x02,
+ 0xd5, 0x10,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_exposure_pos6[] = {
+ 0xfe, 0x01,
+ 0x13, 0xa8,//88
+ 0xfe, 0x02,
+ 0xd5, 0x20,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_antibanding_auto[] = {
+ 0xfe, 0x00,
+ 0x05, 0x01,
+ 0x06, 0x39,
+ 0x07, 0x00,
+ 0x08, 0x14,
+ 0xfe, 0x01,
+ 0x27, 0x00,
+ 0x28, 0x8b,
+ 0x29, 0x04,
+ 0x2a, 0x58,
+ 0x2b, 0x04,
+ 0x2c, 0x58,
+ 0x2d, 0x06,
+ 0x2e, 0x84,
+ 0x2f, 0x08,
+ 0x30, 0xb0,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_antibanding_50hz[] = {
+ 0xfe , 0x00,
+ 0x05 , 0x01,// HB
+ 0x06 , 0x23,
+ 0x07 , 0x00,// VB
+ 0x08 , 0x14,
+ 0xfe , 0x01,
+ 0x27 , 0x00,// step
+ 0x28 , 0xaa,
+ 0x29 , 0x04,// level 0 13.75
+ 0x2a , 0xa6,
+ 0x2b , 0x04,// level 1 13.75
+ 0x2c , 0xa6,
+ 0x2d , 0x06,// level 2 10
+ 0x2e , 0xa4,
+ 0x2f , 0x08,// level 3 7.5
+ 0x30 , 0xa2,
+ 0xfe , 0x00,
+
+
+};
+
+uint8_t gc2035_antibanding_60hz[] = {
+ 0xfe, 0x00,
+ 0x05, 0x01,
+ 0x06, 0x39,
+ 0x07, 0x00,
+ 0x08, 0x14,
+ 0xfe, 0x01,
+ 0x27, 0x00,
+ 0x28, 0x8b,
+ 0x29, 0x04,
+ 0x2a, 0x58,
+ 0x2b, 0x04,
+ 0x2c, 0x58,
+ 0x2d, 0x06,
+ 0x2e, 0x84,
+ 0x2f, 0x08,
+ 0x30, 0xb0,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2035_antibanding_off[] = {
+ 0xfe , 0x00,
+ 0x05 , 0x01,// HB
+ 0x06 , 0x23,
+ 0x07 , 0x00,// VB
+ 0x08 , 0x14,
+ 0xfe , 0x01,
+ 0x27 , 0x00,// step
+ 0x28 , 0xaa,
+ 0x29 , 0x04,// level 0 13.75
+ 0x2a , 0xa6,
+ 0x2b , 0x04,// level 1 13.75
+ 0x2c , 0xa6,
+ 0x2d , 0x06,// level 2 10
+ 0x2e , 0xa4,
+ 0x2f , 0x08,// level 3 7.5
+ 0x30 , 0xa2,
+ 0xfe , 0x00,
+};
+
+uint8_t gc2035_176_144_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x33,
+ 0x9b,0x00,
+ 0x9f,0x00,
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0x92,//600
+ 0x97,0x00,
+ 0x98,0xb0,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+
+ 0xfe,0x03,
+ 0x42,0x60,
+ 0x43,0x01,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_320_240_regs[]={
+ 0xfe,0x00,
+
+ 0x99,0x22,
+ 0x9b,0x00,
+ 0x9f,0x00,
+
+ 0x90,0x01,
+
+ 0x95,0x00,
+ 0x96,0xf2,//600
+ 0x97,0x01,
+ 0x98,0x40,//800
+
+ 0xc8,0x15,
+ 0xf7,0x11,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x02,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x54,
+};
+
+uint8_t gc2035_1280_720_regs[]={
+ 0xfe,0x00,
+ 0x0a,0xf0,
+ 0x0c,0xa0,
+ 0x0d,0x02,
+ 0x0e,0xd8,
+ 0x0f,0x05,
+ 0x10,0x18,
+ 0xfe,0x00,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x05,0x01,
+ 0x06,0xaa,
+ 0x07,0x00,
+ 0x08,0xf6,
+ 0xfe,0x01,
+ 0x27,0x00,
+ 0x28,0xa1,
+ 0x29,0x03,
+ 0x2a,0xc6,
+ 0x2b,0x04,
+ 0x2c,0x67,
+ 0x2d,0x06,
+ 0x2e,0x4a,
+ 0x2f,0x07,
+ 0x30,0x8c,
+ 0x3e,0x40,
+ 0xfe,0x00,
+ 0xb6,0x03,
+ 0xec,0x04,
+ 0xed,0x04,
+ 0xee,0x50,
+ 0xef,0x58,
+ 0x99,0x11,
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0xd0,
+ 0x97,0x05,
+ 0x98,0x00,
+ 0xfe,0x03,
+ 0x42,0x80,
+ 0x43,0x06,
+ 0x41,0x00,
+ 0x40,0x00,
+ 0x17,0x01,
+ 0xfe,0x00,
+ 0xc8,0x00,
+ 0xfa,0x11,
+};
+
+uint8_t gc2035_1600_1200_regs[]= {
+ 0xfe , 0x00,
+ 0xfa , 0x11,
+ 0xc8 , 0x00,
+
+ 0x90 , 0x01,
+ 0x95 , 0x04,
+ 0x96 , 0xb0,
+ 0x97 , 0x06,
+ 0x98 , 0x40,
+};
+
+uint8_t gc2035_800_600_regs[]={
+ 0xfe,0x00,
+ 0xfa,0x00,
+ 0xc8,0x00,
+
+ 0x99,0x22,
+ 0x9a,0x07,
+ 0x9b,0x00,
+ 0x9c,0x00,
+ 0x9d,0x00,
+ 0x9e,0x00,
+ 0x9f,0x00,
+ 0xa0,0x00,
+ 0xa1,0x00,
+ 0xa2,0x00,
+
+ 0x90,0x01,
+ 0x95,0x02,
+ 0x96,0x58,
+ 0x97,0x03,
+ 0x98,0x20,
+};
+
+uint8_t gc2035_640_480_regs[]={
+ 0xfe,0x00,
+ 0x0c,0x00,
+ 0xfa,0x00,
+ 0x0d,0x04,
+ 0x0e,0xc0,
+ 0x0f,0x06,
+ 0x10,0x58,
+
+ 0x90,0x01,
+ 0x99,0x11,
+ 0x95,0x01,
+ 0x96,0xe0,
+ 0x97,0x02,
+ 0x98,0x80,
+
+ 0xc8,0x15,
+ 0xf7,0x15,
+ 0xfe,0x03,
+ 0x42,0x00,
+ 0x43,0x05,
+ 0x41,0x02,
+ 0x40,0x40,
+ 0x17,0x00,
+ 0xfe,0x00,
+ 0xc8,0x55,
+ 0xb6,0x03,
+};
+
+uint8_t gc2035_default_regs_init[]={
+
+ 0xfe , 0x80,
+ 0xfe , 0x80,
+ 0xfe , 0x80,
+ 0xfc , 0x06,
+ 0xf2 , 0x00,
+ 0xf3 , 0x00,
+ 0xf4 , 0x00,
+ 0xf5 , 0x00,
+ 0xf9 , 0xfe, //[0] pll enable
+ 0xfa , 0x00,
+ 0xf6 , 0x00,
+ 0xf7 , 0x15, //pll enable
+ 0xf8 , 0x85,
+ 0xfe , 0x00,
+ 0x82 , 0x00,
+ 0xb3 , 0x60,
+ 0xb4 , 0x40,
+ 0xb5 , 0x60,
+
+ 0x03 , 0x02,
+ 0x04 , 0xa8,
+
+ //////////measure window ///////////
+ 0xfe , 0x00,
+ 0xec , 0x06,//04
+ 0xed , 0x06,//04
+ 0xee , 0x62,//60
+ 0xef , 0x92,//90
+
+ ///////////analog/////////////
+ 0x0a , 0x00, //row start
+ 0x0c , 0x00, //col start
+ 0x0d , 0x04,
+ 0x0e , 0xc0,
+ 0x0f , 0x06, //Window setting
+ 0x10 , 0x58,
+ 0x17 , 0x14, //[0]mirror [1]flip
+ 0x18 , 0x0e, //0a 2012.10.26
+ 0x19 , 0x0c, //AD pipe number
+ 0x1a , 0x01, //CISCTL mode4
+ 0x1b , 0x8b,
+ 0x1c , 0x05,//add by lanking 20130403
+ 0x1e , 0x88, //analog mode1 [7] tx-high en [5:3]COL_bias
+ 0x1f , 0x08, //[3] tx-low en//
+ 0x20 , 0x05, //[0]adclk mode , 0x[1]rowclk_MODE [2]rsthigh_en
+ 0x21 , 0x0f, //[6:4]rsg
+ 0x22 , 0xf0, //[3:0]vref d0 20130628
+ 0x23 , 0xc3, //f3//ADC_r
+ 0x24 , 0x17, //pad drive 16
+
+ //AWB_gray
+ 0xfe , 0x01,
+ 0x4f , 0x00,
+ 0x4d , 0x32, // 30
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4d , 0x42, // 40
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4d , 0x52, // 50
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4d , 0x62, // 60
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4d , 0x72, // 70
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4d , 0x82, // 80
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4d , 0x92, // 90
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4e , 0x04,
+ 0x4f , 0x01,
+ 0x50 , 0x88,
+ 0xfe , 0x00,
+ 0x82 , 0xfe,
+
+ ///////awb start ////////////////
+ 0xfe , 0x01,
+ 0x50 , 0x88,//c0//[6]green mode
+ 0x52 , 0x40,
+ 0x54 , 0x60,
+ 0x56 , 0x06,
+ 0x57 , 0x20,//pre adjust
+ 0x58 , 0x01,
+ 0x5b , 0x02,//AWB_gain_delta
+ 0x61 , 0xaa,//R/G stand
+ 0x62 , 0xaa,//R/G stand
+ 0x71 , 0x00,
+ 0x74 , 0x10,//AWB_C_max
+ 0x77 , 0x08,//AWB_p2_x
+ 0x78 , 0xfd,//AWB_p2_y
+ 0x86 , 0x30,
+ 0x87 , 0x00,
+ 0x88 , 0x04,//[1]dark mode
+ 0x8a , 0xc0,//awb move mode
+ 0x89 , 0x75,
+ 0x84 , 0x08,//auto_window
+ 0x8b , 0x00,//awb compare luma
+ 0x8d , 0x70,//awb gain limit R
+ 0x8e , 0x70,//G
+ 0x8f , 0xf4,//B
+ /////////awb end /////////////
+
+ //AEC
+ 0xfe , 0x01,
+ 0x11 , 0x20,//AEC_out_slope , 0x
+ 0x1f , 0xc0,//max_post_gain
+ 0x20 , 0x60,//max_pre_gain
+ 0x47 , 0x30,//AEC_outdoor_th
+ 0x0b , 0x10,//
+ 0x13 , 0x88,//y_target 75
+
+
+ 0xfe , 0x00,
+ 0x05 , 0x01,
+ 0x06 , 0x23,
+ 0x07 , 0x00,
+ 0x08 , 0x14,
+ 0xfe , 0x01,
+ 0x27 , 0x00,
+ 0x28 , 0xaa,
+ 0x29 , 0x04,// level 0 13.75
+ 0x2a , 0xa6,
+ 0x2b , 0x04,// level 1 13.75
+ 0x2c , 0xa6,
+ 0x2d , 0x06,// level 2 10
+ 0x2e , 0xa4,
+ 0x2f , 0x08,// level 3 7.5
+ 0x30 , 0xa2,
+ 0xfe , 0x00,//
+ 0xb6 , 0x03,//AEC enable
+ 0xfe , 0x00,
+
+ /////////BLK//////
+ 0x3f , 0x00,//prc close
+ 0x40 , 0x77,//
+ 0x42 , 0x7f,
+ 0x43 , 0x30,
+ 0x5c , 0x08,
+ 0x5e , 0x20,
+ 0x5f , 0x20,
+ 0x60 , 0x20,
+ 0x61 , 0x20,
+ 0x62 , 0x20,
+ 0x63 , 0x20,
+ 0x64 , 0x20,
+ 0x65 , 0x20,
+
+ ///block////////////
+ 0x80 , 0xff,
+ 0x81 , 0x26,//38 //skin_Y 8c_debug
+ 0x87 , 0x90,//[7]middle gamma
+ 0x84 , 0x02,//output put foramat
+ 0x86 , 0x06,//02 //sync plority lanking
+ 0x8b , 0xbc,
+ 0xb0 , 0x80,//globle gain
+ 0xc0 , 0x40,//Yuv bypass
+
+ //////lsc/////////////
+#if 0
+ 0xfe , 0x01,
+ 0xc2 , 0x38,
+ 0xc3 , 0x25,
+ 0xc4 , 0x21,
+ 0xc8 , 0x19,
+ 0xc9 , 0x12,
+ 0xca , 0x0e,
+ 0xbc , 0x43,
+ 0xbd , 0x18,
+ 0xbe , 0x1b,
+ 0xb6 , 0x40,
+ 0xb7 , 0x2e,
+ 0xb8 , 0x26,
+ 0xc5 , 0x05,
+ 0xc6 , 0x03,
+ 0xc7 , 0x04,
+ 0xcb , 0x00,
+ 0xcc , 0x00,
+ 0xcd , 0x00,
+ 0xbf , 0x14,
+ 0xc0 , 0x22,
+ 0xc1 , 0x1b,
+ 0xb9 , 0x00,
+ 0xba , 0x05,
+ 0xbb , 0x05,
+ 0xaa , 0x35,
+ 0xab , 0x33,
+ 0xac , 0x33,
+ 0xad , 0x25,
+ 0xae , 0x22,
+ 0xaf , 0x27,
+ 0xb0 , 0x1d,
+ 0xb1 , 0x20,
+ 0xb2 , 0x22,
+ 0xb3 , 0x14,
+ 0xb4 , 0x15,
+ 0xb5 , 0x16,
+ 0xd0 , 0x00,
+ 0xd2 , 0x07,
+ 0xd3 , 0x08,
+ 0xd8 , 0x00,
+ 0xda , 0x13,
+ 0xdb , 0x17,
+ 0xdc , 0x00,
+ 0xde , 0x0a,
+ 0xdf , 0x08,
+ 0xd4 , 0x00,
+ 0xd6 , 0x00,
+ 0xd7 , 0x0c,
+ 0xa4 , 0x00,
+ 0xa5 , 0x00,
+ 0xa6 , 0x00,
+ 0xa7 , 0x00,
+ 0xa8 , 0x00,
+ 0xa9 , 0x00,
+ 0xa1 , 0x80,
+ 0xa2 , 0x80,
+#endif
+ 0xfe,0x01,
+ 0xa1,0x80, // center_row
+ 0xa2,0x80, // center_col
+ 0xa4,0x07, // sign of b1
+ 0xa5,0x00, // sign of b1
+ 0xa6,0x00, // sign of b4
+ 0xa7,0x00, // sign of b4
+ 0xa8,0x00, // sign of b22
+ 0xa9,0x07, // sign of b22
+ 0xaa,0x1e, // Q1_b1 of R
+ 0xab,0x49, // Q1_b1 of G
+ 0xac,0x1e, // Q1_b1 of B
+ 0xad,0x30, // Q2_b1 of R
+ 0xae,0x1c, // Q2_b1 of G
+ 0xaf,0x1b, // Q2_b1 of B
+ 0xb0,0x56, // Q3_b1 of R
+ 0xb1,0x88, // Q3_b1 of G
+ 0xb2,0x53, // Q3_b1 of B
+ 0xb3,0xff, // Q4_b1 of R
+ 0xb4,0xf8, // Q4_b1 of G
+ 0xb5,0xcb, // Q4_b1 of B
+ 0xb6,0x62, // right_b2 of R
+ 0xb7,0x19, // right_b2 of G
+ 0xb8,0x00, // right_b2 of B
+ 0xb9,0xff, // right_b4 of R
+ 0xba,0xff, // right_b4 of G
+ 0xbb,0xff, // right_b4 of B
+ 0xbc,0x67, // left_b2 of R
+ 0xbd,0x3a, // left_b2 of G
+ 0xbe,0x25, // left_b2 of B
+ 0xbf,0x65, // left_b4 of R
+ 0xc0,0x49, // left_b4 of G
+ 0xc1,0x60, // left_b4 of B
+ 0xc2,0x3f, // up_b2 of R
+ 0xc3,0x1b, // up_b2 of G
+ 0xc4,0x19, // up_b2 of B
+ 0xc5,0x45, // up_b4 of R
+ 0xc6,0x39, // up_b4 of G
+ 0xc7,0x19, // up_b4 of B
+ 0xc8,0x12, // down_b2 of R
+ 0xc9,0x02, // down_b2 of G
+ 0xca,0x00, // down_b2 of B
+ 0xcb,0xff, // down_b4 of R
+ 0xcc,0xff, // down_b4 of G
+ 0xcd,0xf3, // down_b4 of B
+ 0xd0,0xff, // right_up_b22 of R
+ 0xd2,0x3b, // right_up_b22 of G
+ 0xd3,0x7d, // right_up_b22 of B
+ 0xd4,0xff, // right_down_b22 of R
+ 0xd6,0xff, // right_down_b22 of G
+ 0xd7,0xff, // right_down_b22 of B
+ 0xd8,0xff, // left_up_b22 of R
+ 0xda,0x89, // left_up_b22 of G
+ 0xdb,0xa3, // left_up_b22 of B
+ 0xdc,0x1c, // left_down_b22 of R
+ 0xde,0x99, // left_down_b22 of G
+ 0xdf,0x27, // left_down_b22 of B
+ 0xfe,0x00,
+
+ //////////cc//////////////
+ 0xfe , 0x02,
+ 0xc0 , 0x01,
+ 0xc1 , 0x40,//Green_cc for d
+ 0xc2 , 0xfc,
+ 0xc3 , 0x05,
+ 0xc4 , 0xec,
+ 0xc5 , 0x42,
+ 0xc6 , 0xf8,
+ 0xc7 , 0x40,//for cwf
+ 0xc8 , 0xf8,
+ 0xc9 , 0x06,
+ 0xca , 0xfd,
+ 0xcb , 0x3e,
+ 0xcc , 0xf3,
+ 0xcd , 0x36,//for A
+ 0xce , 0xf6,
+ 0xcf , 0x04,
+ 0xe3 , 0x0c,
+ 0xe4 , 0x44,
+ 0xe5 , 0xe5,
+ 0xfe , 0x00,
+
+ ///==========asde
+ 0xfe , 0x01,
+ 0x21 , 0xbf,
+ 0xfe , 0x02,
+ 0xa4 , 0x00,
+ 0xa5 , 0x40,//lsc_th
+ 0xa2 , 0xa0,//lsc_dec_slope
+ 0xa6 , 0x80,//dd_th
+ 0xa7 , 0x80,//ot_th
+ 0xab , 0x31,
+ 0xa9 , 0x6f,
+ 0xb0 , 0x99,//edge effect slope low
+ 0xb1 , 0x34,//edge effect slope low
+ 0xb3 , 0x80,//saturation dec slope
+ 0xde , 0xb6,
+ 0x38 , 0x0f,
+ 0x39 , 0x60,
+ 0xfe , 0x00,
+ 0x81 , 0x26,
+ 0xfe , 0x02,
+ 0x83 , 0x00,
+ 0x84 , 0x45,
+ ////////////YCP//////////
+ 0xd1 , 0x38,//saturation_cb
+ 0xd2 , 0x38,//saturation_Cr
+ 0xd3 , 0x40,//contrast
+ 0xd4 , 0x80,//contrast center
+ 0xd5 , 0x00,//luma_offset
+ 0xdc , 0x30,
+ 0xdd , 0xb8,//edge_sa_g,b
+ 0xfe , 0x00,
+ //////////dndd///////////
+ 0xfe , 0x02,
+ 0x88 , 0x15,//dn_b_base
+ 0x8c , 0xf6,//[2]b_in_dark_inc
+ 0x89 , 0x03,//dn_c_weight
+ //////////EE ///////////
+ 0xfe , 0x02,
+ 0x90 , 0x6c,// EEINTP mode1
+ 0x97 , 0x45,// edge effect
+ ////==============RGB Gamma ////
+ 0xfe , 0x02,
+ 0x15 , 0x0a,
+ 0x16 , 0x12,
+ 0x17 , 0x19,
+ 0x18 , 0x1f,
+ 0x19 , 0x2c,
+ 0x1a , 0x38,
+ 0x1b , 0x42,
+ 0x1c , 0x4e,
+ 0x1d , 0x63,
+ 0x1e , 0x76,
+ 0x1f , 0x87,
+ 0x20 , 0x96,
+ 0x21 , 0xa2,
+ 0x22 , 0xb8,
+ 0x23 , 0xca,
+ 0x24 , 0xd8,
+ 0x25 , 0xe3,
+ 0x26 , 0xf0,
+ 0x27 , 0xf8,
+ 0x28 , 0xfd,
+ 0x29 , 0xff,
+ //// small RGB gamma////
+ /*
+ 0xfe , 0x02,
+ 0x15 , 0x0b,
+ 0x16 , 0x0e,
+ 0x17 , 0x10,
+ 0x18 , 0x12,
+ 0x19 , 0x19,
+ 0x1a , 0x21,
+ 0x1b , 0x29,
+ 0x1c , 0x31,
+ 0x1d , 0x41,
+ 0x1e , 0x50,
+ 0x1f , 0x5f,
+ 0x20 , 0x6d,
+ 0x21 , 0x79,
+ 0x22 , 0x91,
+ 0x23 , 0xa5,
+ 0x24 , 0xb9,
+ 0x25 , 0xc9,
+ 0x26 , 0xe1,
+ 0x27 , 0xee,
+ 0x28 , 0xf7,
+ 0x29 , 0xff,
+ */
+
+ ///=================y gamma
+ 0xfe , 0x02,
+ 0x2b , 0x00,
+ 0x2c , 0x04,
+ 0x2d , 0x09,
+ 0x2e , 0x18,
+ 0x2f , 0x27,
+ 0x30 , 0x37,
+ 0x31 , 0x49,
+ 0x32 , 0x5c,
+ 0x33 , 0x7e,
+ 0x34 , 0xa0,
+ 0x35 , 0xc0,
+ 0x36 , 0xe0,
+ 0x37 , 0xff,
+
+
+ //AWB clear
+ 0xfe , 0x01,
+ 0x4f , 0x00,
+ 0x4d , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4d , 0x10, // 10
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4d , 0x20, // 20
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4d , 0x30,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00, // 30
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4d , 0x40, // 40
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4d , 0x50, // 50
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4d , 0x60, // 60
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4d , 0x70, // 70
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4d , 0x80, // 80
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4d , 0x90, // 90
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4d , 0xa0, // a0
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4d , 0xb0, // b0
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4d , 0xc0, // c0
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4d , 0xd0, // d0
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4e , 0x00,
+ 0x4f , 0x01,
+ /////// awb value////////
+ 0xfe , 0x01,
+ 0x4f , 0x00,
+ 0x4d , 0x30,
+ 0x4e , 0x00,
+ 0x4e , 0x80,
+ 0x4e , 0x80,
+ 0x4e , 0x02,
+ 0x4e , 0x02,
+ 0x4d , 0x40,
+ 0x4e , 0x00,
+ 0x4e , 0x80,
+ 0x4e , 0x80,
+ 0x4e , 0x02,
+ 0x4e , 0x02,
+ 0x4e , 0x02,
+ 0x4d , 0x53,
+ 0x4e , 0x08,
+ 0x4e , 0x04,
+ 0x4d , 0x62,
+ 0x4e , 0x10,
+ 0x4d , 0x72,
+ 0x4e , 0x20,
+ 0x4f , 0x01,
+
+ /////1600x1200size//
+ 0xfe , 0x00,//
+ 0x90 , 0x01, ////crop enable
+ 0x95 , 0x04, ////1600x1200
+ 0x96 , 0xb0,
+ 0x97 , 0x06,
+ 0x98 , 0x40,
+
+ 0xfe , 0x03,
+ 0x17 , 0x00, //widv
+ 0x40 , 0x40, //00
+ 0x41 , 0x02, // Pclk_polarity
+ 0x42 , 0x40,
+ 0x43 , 0x06, //output buf width
+
+ ////output DVP/////
+ 0xfe , 0x00,
+ 0x82 , 0xfe,
+ 0xf2 , 0x70,
+ 0xf3 , 0xff,
+ 0xf4 , 0x00,
+ 0xf5 , 0x30,
+#if 0
+ 0xfe , 0x00 ,
+ 0x80 , 0x08 ,
+ 0x81 , 0x00 ,
+ 0x82 , 0x00 ,
+ 0xa3 , 0x80 ,
+ 0xa4 , 0x80 ,
+ 0xa5 , 0x80 ,
+ 0xa6 , 0x80 ,
+ 0xa7 , 0x80 ,
+ 0xa8 , 0x80 ,
+ 0xa9 , 0x80 ,
+ 0xaa , 0x80 ,
+ 0xad , 0x80 ,
+ 0xae , 0x80 ,
+ 0xaf , 0x80 ,
+ 0xb3 , 0x40 ,
+ 0xb4 , 0x40 ,
+ 0xb5 , 0x40 ,
+ 0xfe , 0x01 ,
+ 0x0a , 0x40 ,
+ 0x13 , 0x48 ,
+ 0x9f , 0x40 ,
+ 0xfe , 0x02 ,
+ 0xd0 , 0x40 ,
+ 0xd1 , 0x20 ,
+ 0xd2 , 0x20 ,
+ 0xd3 , 0x40 ,
+ 0xd5 , 0x00 ,
+ 0xdd , 0x00 ,
+ 0xfe , 0x00 ,
+#endif
+};
+
+uint8_t gc2035_default_regs_exit[]={
+ 0xfe, 0x00,
+ 0xf1, 0x00,
+ 0xf2, 0x00,
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc2155/gc2155.c b/drivers/media/video/wmt_v4l2/sensors/gc2155/gc2155.c
new file mode 100755
index 00000000..5369dd18
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc2155/gc2155.c
@@ -0,0 +1,388 @@
+
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "gc2155.h"
+
+#define sensor_write_array(sd, arr, sz) \
+ cmos_init_8bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write(sd->i2c_addr, reg, val)
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint8_t *regs;
+ size_t size;
+};
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+#define FILP_REG_INIT_VALUE 0x14
+#define DELAY_INTERVAL 50
+#define RETRY_TIMES 10
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+ //CMOS_WIN_SIZE("QVGA", 320, 240, gc2155_320_240_regs),
+ CMOS_WIN_SIZE("VGA", 640, 480, gc2155_640_480_regs),
+ CMOS_WIN_SIZE("UXGA", 1600, 1200, gc2155_1600_1200_regs),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = gc2155_wb_auto;
+ size = ARRAY_SIZE(gc2155_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = gc2155_wb_incandescent;
+ size = ARRAY_SIZE(gc2155_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = gc2155_wb_daylight;
+ size = ARRAY_SIZE(gc2155_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = gc2155_wb_cloudy;
+ size = ARRAY_SIZE(gc2155_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = gc2155_wb_fluorescent;
+ size = ARRAY_SIZE(gc2155_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_scenemode(struct cmos_subdev *sd, enum v4l2_scene_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case SCENE_MODE_AUTO:
+ regs = gc2155_scene_mode_auto;
+ size = ARRAY_SIZE(gc2155_scene_mode_auto);
+ break;
+ case SCENE_MODE_NIGHTSHOT:
+ regs = gc2155_scene_mode_night;
+ size = ARRAY_SIZE(gc2155_scene_mode_night);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_exposure(struct cmos_subdev *sd, enum v4l2_exposure_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case -2:
+ regs = gc2155_exposure_neg6;
+ size = ARRAY_SIZE(gc2155_exposure_neg6);
+ break;
+ case -1:
+ regs = gc2155_exposure_neg3;
+ size = ARRAY_SIZE(gc2155_exposure_neg3);
+ break;
+ case 0:
+ regs = gc2155_exposure_zero;
+ size = ARRAY_SIZE(gc2155_exposure_zero);
+ break;
+ case 1:
+ regs = gc2155_exposure_pos3;
+ size = ARRAY_SIZE(gc2155_exposure_pos3);
+ break;
+ case 2:
+ regs = gc2155_exposure_pos6;
+ size = ARRAY_SIZE(gc2155_exposure_pos6);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_antibanding(struct cmos_subdev *sd, int value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case 0:
+ regs = gc2155_antibanding_auto;
+ size = ARRAY_SIZE(gc2155_antibanding_auto);
+ break;
+ case 1:
+ regs = gc2155_antibanding_50hz;
+ size = ARRAY_SIZE(gc2155_antibanding_50hz);
+ break;
+ case 2:
+ regs = gc2155_antibanding_60hz;
+ size = ARRAY_SIZE(gc2155_antibanding_60hz);
+ break;
+ case 3:
+ regs = gc2155_antibanding_off;
+ size = ARRAY_SIZE(gc2155_antibanding_off);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+
+ return 0;
+}
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data,tmp=0 ;
+ uint8_t retry_times=0;
+
+ sensor_write(sd, 0xfe, 0x00); // set page0
+ data=sensor_read(sd, 0x17);
+
+ switch (value) {
+ case 0:
+ data &= ~0x01;
+ break;
+ case 1:
+ data |= 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tmp=data | FILP_REG_INIT_VALUE;
+ for(retry_times=0;retry_times<RETRY_TIMES;retry_times++){
+ sensor_write(sd, 0x17, tmp);
+ msleep(50+retry_times*DELAY_INTERVAL);
+ if(sensor_read(sd, 0x17)==tmp)
+ break;
+ }
+
+ return 0;
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data,tmp=0;
+ uint8_t retry_times=0;
+
+ sensor_write(sd, 0xfe, 0x00); // set page0
+ data=sensor_read(sd, 0x17);
+
+ switch (value) {
+ case 0:
+ data &= ~0x02;
+ break;
+ case 1:
+ data |= 0x02;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tmp=data | FILP_REG_INIT_VALUE;
+ for(retry_times=0;retry_times<RETRY_TIMES;retry_times++){
+ sensor_write(sd, 0x17, tmp);
+ msleep(50+retry_times*DELAY_INTERVAL);
+ if(sensor_read(sd, 0x17)==tmp)
+ break;
+ }
+ return 0;
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ case V4L2_CID_EXPOSURE:
+ return v4l2_ctrl_query_fill(qc, -2, 2, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return sensor_s_scenemode(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return sensor_s_exposure(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ case V4L2_CID_CAMERA_ANTI_BANDING:
+ return sensor_s_antibanding(sd, ctrl->value);
+ default:
+ case WMT_V4L2_CID_CAMERA_ANTIBANDING:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ printk(KERN_DEBUG "%s, s_mbus_fmt width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win) {
+ pr_err("%s, s_mbus_fmt failed, width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, win->regs, win->size);
+ msleep(150);
+ //msleep(500);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return 0;
+}
+
+static int sensor_enum_framesizes(struct cmos_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ const struct cmos_win_size *win = &cmos_supported_win_sizes[index];
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ uint32_t data = 0;
+
+ data |= (sensor_read(sd, 0xf0) & 0xff) << 8;
+ data |= (sensor_read(sd, 0xf1) & 0xff);
+
+ return (data == sd->id) ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ if (sensor_identify(sd)) {
+ return -1;
+ }
+
+ sensor_write_array(sd, gc2155_default_regs_init,
+ ARRAY_SIZE(gc2155_default_regs_init));
+
+ msleep(100);
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ sensor_write_array(sd, gc2155_default_regs_exit,
+ ARRAY_SIZE(gc2155_default_regs_exit));
+ return 0;
+}
+
+static struct cmos_subdev_ops gc2155_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+ .enum_framesizes = sensor_enum_framesizes,
+};
+
+struct cmos_subdev gc2155 = {
+ .name = "gc2155",
+ .i2c_addr = 0x3c,
+ .id = 0x2155,
+ .max_width = 1600,
+ .max_height = 1200,
+ .ops = &gc2155_ops,
+};
+
+#if 0
+static int __init gc2155_init(void)
+{
+ return cmos_register_sudbdev(&gc2155);
+}
+
+static void __exit gc2155_exit(void)
+{
+ return cmos_unregister_subdev(&gc2155);
+}
+
+module_init(gc2155_init);
+module_exit(gc2155_exit);
+
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/gc2155/gc2155.h b/drivers/media/video/wmt_v4l2/sensors/gc2155/gc2155.h
new file mode 100755
index 00000000..74d11e72
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/gc2155/gc2155.h
@@ -0,0 +1,1074 @@
+#ifndef GC2155_H
+#define GC2155_H
+
+
+// Scene Mode
+uint8_t gc2155_scene_mode_auto[] = {
+ 0xfe,0x01,
+
+ 0x3c,0x40,
+ 0xfe,0x00,
+};
+
+uint8_t gc2155_scene_mode_night[] = {
+ 0xfe,0x01,
+
+ 0x3c,0x60,
+ 0xfe,0x00,
+};
+
+uint8_t gc2155_wb_auto [] = {
+ 0xfe , 0x00,
+ 0x82 , 0xfe,
+};
+
+uint8_t gc2155_wb_incandescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0xa8,
+
+};
+
+uint8_t gc2155_wb_fluorescent [] = {
+ 0xfe, 0x00,
+ 0x82, 0xfc,
+ 0xb3, 0x50,
+ 0xb4, 0x40,
+ 0xb5, 0xb8,
+};
+
+uint8_t gc2155_wb_daylight [] = {
+ 0xfe , 0x00,
+ 0x82 , 0xfc,
+ 0xb3 , 0x70,
+ 0xb4 , 0x40,
+ 0xb5 , 0x50,
+
+};
+
+uint8_t gc2155_wb_cloudy [] = {
+ 0xfe , 0x00,
+ 0x82 , 0xfc,
+ 0xb3 , 0x58,
+ 0xb4 , 0x40,
+ 0xb5 , 0x50,
+};
+
+
+// Exposure
+uint8_t gc2155_exposure_neg6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x10,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2155_exposure_neg3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x20,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2155_exposure_zero[] = {
+ 0xfe, 0x01,
+ 0x13, 0x30,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2155_exposure_pos3[] = {
+ 0xfe, 0x01,
+ 0x13, 0x40,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2155_exposure_pos6[] = {
+ 0xfe, 0x01,
+ 0x13, 0x50,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2155_antibanding_auto[] = {
+
+};
+
+uint8_t gc2155_antibanding_50hz[] = {
+
+};
+
+uint8_t gc2155_antibanding_60hz[] = {
+
+};
+
+uint8_t gc2155_antibanding_off[] = {
+
+};
+
+uint8_t gc2155_176_144_regs[]={
+
+};
+
+uint8_t gc2155_320_240_regs[]={
+
+};
+
+uint8_t gc2155_1280_720_regs[]= {
+#if defined(GC2155_SUBSAMPLE) //subsample for 720P
+ 0xfe , 0x00,
+ //0xfa , 0x11,
+ 0xb6 , 0x01,
+ 0xfd , 0x00,
+ //// crop window
+ 0xfe , 0x00,
+ 0x99 , 0x55,
+ 0x9a , 0x06,
+ 0x9b , 0x00,
+ 0x9c , 0x00,
+ 0x9d , 0x01,
+ 0x9e , 0x23,
+ 0x9f , 0x00,
+ 0xa0 , 0x00,
+ 0xa1 , 0x01,
+ 0xa2 , 0x23,
+
+ 0x90 , 0x01,
+ 0x91 , 0x00,
+ 0x92 , 0x78,
+ 0x93 , 0x00,
+ 0x94 , 0x00,
+ 0x95 , 0x02,
+ 0x96 , 0xd0,
+ 0x97 , 0x05,
+ 0x98 , 0x00,
+
+ //// AWB
+ 0xfe , 0x00,
+ 0xec , 0x02,
+ 0xed , 0x04,
+ 0xee , 0x60,
+ 0xef , 0x90,
+ 0xfe , 0x01,
+ 0x74 , 0x01,
+ //// AEC
+ 0xfe , 0x01,
+ 0x01 , 0x08,
+ 0x02 , 0xc0,
+ 0x03 , 0x04,
+ 0x04 , 0x90,
+ 0x05 , 0x30,
+ 0x06 , 0x98,
+ 0x07 , 0x28,
+ 0x08 , 0x6c,
+ 0x0a , 0xc2,
+ 0x21 , 0x15,
+ 0xfe , 0x00,
+ ///////banding setting 20fps fixed///
+ 0xfe , 0x00,
+ 0x03 , 0x03,
+ 0x04 , 0xe8,
+ 0x05 , 0x01,
+ 0x06 , 0x56,
+ 0x07 , 0x00,
+ 0x08 , 0x32,
+ 0xfe , 0x01,
+ 0x25 , 0x00,
+ 0x26 , 0xfa,
+ 0x27 , 0x04,
+ 0x28 , 0xe2, //20fps
+ 0x29 , 0x04,
+ 0x2a , 0xe2, //16fps 5dc
+ 0x2b , 0x04,
+ 0x2c , 0xe2, //16fps 6d6 5dc
+ 0x2d , 0x04,
+ 0x2e , 0xe2, //8fps bb8
+ 0x3c , 0x00, //8fps
+
+ 0xfe , 0x00,
+#else
+ ////////windowing 1280X720///////
+ 0xfe,0x00,
+ 0xb6,0x01,
+ 0xfd,0x00,//close scaler
+
+ //windowing setting
+ 0xfe,0x00,
+ 0x09,0x00,
+ 0x0a,0xf0, //row start (1280-720)/2
+ 0x0b,0x00,
+ 0x0c,0xa0, //col start (1600-1280)/2
+ 0x0d,0x02,
+ 0x0e,0xe0,//D8
+ 0x0f,0x05, //Window setting
+ 0x10,0x10, //18
+
+ 0x99,0x11,
+ 0x9a,0x06,
+ 0x9b,0x00,
+ 0x9c,0x00,
+ 0x9d,0x00,
+ 0x9e,0x00,
+ 0x9f,0x00,
+ 0xa0,0x00,
+ 0xa1,0x00,
+ 0xa2,0x00,
+
+ 0x90,0x01, //crop enable
+ 0x95,0x02,
+ 0x96,0xd0,
+ 0x97,0x05,
+ 0x98,0x00,
+
+ //// AWB
+ 0xfe , 0x00,
+ 0xec , 0x04,
+ 0xed , 0x02,
+ 0xee , 0x50,
+ 0xef , 0x5a,
+ 0xfe , 0x01,
+ 0x74 , 0x01,
+ //// AEC
+ 0xfe , 0x01,
+ 0x01,0x04,
+ 0x02,0x50,
+ 0x03,0x02,
+ 0x04,0x58,
+ 0x05,0x20,
+ 0x06,0x30,
+ 0x07,0x20,
+ 0x08,0x40,
+ 0x09,0x80,
+ 0x0a,0xc2,
+ 0x21,0x15,
+ 0xfe,0x00,
+
+ //banding setting
+#if 0 ///just fixed 20fps frame rate
+ 0xfe,0x00,
+ 0x03,0x04,
+ 0x04,0xe7,//N*STEP
+
+ 0x05,0x01,
+ 0x06,0xf1,//HB
+ 0x07,0x00,
+ 0x08,0x80,//VB
+ 0xfe,0x01,
+ 0x25,0x00,
+ 0x26,0xfb,//STEP
+ 0x27,0x04,
+ 0x28,0xe7,
+ 0x29,0x04,
+ 0x2a,0xe7,
+ 0x2b,0x04,
+ 0x2c,0xe7,
+ 0x2d,0x04,
+ 0x2e,0xe7,
+ 0x3c,0x00,
+ 0xfe,0x00,
+
+#endif
+
+/******720P high Frame Rate 16fps~32fps,start set***********************/
+ //banding setting
+ 0xfe,0x00,
+ 0x03,0x02,
+ 0x04,0x34,//N*STEP
+ 0x05,0x01,
+ 0x06,0x6e,//HB
+ 0x07,0x00,
+ 0x08,0x80,//VB
+ 0xfe,0x01,
+ 0x25,0x01,
+ 0x26,0x1a,//STEP
+
+ 0x27,0x03,
+ 0x28,0x4e,//lev 1 32fps
+
+ 0x29,0x04,
+ 0x2a,0x68,//lev 2 24fps
+
+ 0x2b,0x05,
+ 0x2c,0x82,//lev 3 20fps
+
+ 0x2d,0x06,
+ 0x2e,0x9c,//lev 4 16fps
+ 0x3c,0x00,
+ 0xfe,0x00,
+/////////if you set highest Frame Rate,please write 0x00 to 0x3c register//
+/******720P high Frame Rate 16fps~32fps,end set*************************/
+#endif
+};
+
+uint8_t gc2155_1600_1200_regs[]={
+ //SENSORDB("GC2155_Sensor_2M",
+ 0xfe , 0x00,
+ 0xb6 , 0x00,
+ 0xfa , 0x11,
+ 0xfd , 0x00,
+ //// crop window
+ 0xfe , 0x00,
+ 0x90 , 0x01,
+ 0x91 , 0x00,
+ 0x92 , 0x00,
+ 0x93 , 0x00,
+ 0x94 , 0x00,
+ 0x95 , 0x04,
+ 0x96 , 0xb0,
+ 0x97 , 0x06,
+ 0x98 , 0x40,
+ 0x99 , 0x11,
+ 0x9a , 0x06,
+
+ 0x9b , 0x00,
+ 0x9c , 0x00,
+ 0x9d , 0x00,
+ 0x9e , 0x00,
+ 0x9f , 0x00,
+ 0xa0 , 0x00,
+ 0xa1 , 0x00,
+ 0xa2 ,0x00,
+
+ //// AWB
+ 0xfe , 0x00,
+ 0xec , 0x02,
+ 0xed , 0x04,
+ 0xee , 0x60,
+ 0xef , 0x90,
+ 0xfe , 0x01,
+ 0x74 , 0x01,
+ //// AEC
+ 0xfe , 0x01,
+ 0x01 , 0x08,
+ 0x02 , 0xc0,
+ 0x03 , 0x04,
+ 0x04 , 0x90,
+ 0x05 , 0x30,
+ 0x06 , 0x98,
+ 0x07 , 0x28,
+ 0x08 , 0x6c,
+ 0x0a , 0xc2,
+ 0x21 , 0x15, //if 0xfa=11,then 0x21=15;else if 0xfa=00,then 0x21=14
+ 0xfe , 0x00,
+ //// gamma
+ 0xfe , 0x00,
+ 0xc3 , 0x00, //if shutter/2 when capture,then exp_gamma_th/2
+ 0xc4 , 0x90,
+ 0xc5 , 0x98,
+ 0xfe , 0x00,
+};
+
+uint8_t gc2155_800_600_regs[]= {
+ //SENSORDB("GC2155_Sensor_SVGA",
+ 0xfe , 0x00,
+ 0xb6 , 0x01,
+ 0xfa , 0x00,
+ 0xfd , 0x01,
+
+////window setting/////
+ 0x09 , 0x00,
+ 0x0a , 0x00,
+ 0x0b , 0x00,
+ 0x0c , 0x00,
+ 0x0d , 0x04,
+ 0x0e , 0xc0,
+ 0x0f , 0x06,
+ 0x10 , 0x50,
+
+ //// crop window
+ 0xfe , 0x00,
+ 0x90 , 0x01,
+ 0x91 , 0x00,
+ 0x92 , 0x00,
+ 0x93 , 0x00,
+ 0x94 , 0x00,
+ 0x95 , 0x02,
+ 0x96 , 0x58,
+ 0x97 , 0x03,
+ 0x98 , 0x20,
+ 0x99 , 0x11,
+ 0x9a , 0x06,
+ 0x9b , 0x00,
+ 0x9c , 0x00,
+ 0x9d , 0x00,
+ 0x9e , 0x00,
+ 0x9f , 0x00,
+ 0xa0 , 0x00,
+ 0xa1 , 0x00,
+ 0xa2 ,0x00,
+
+ //// AWB
+ 0xfe , 0x00,
+ 0xec , 0x01,
+ 0xed , 0x02,
+ 0xee , 0x30,
+ 0xef , 0x48,
+ 0xfe , 0x01,
+ 0x74 , 0x00,
+ //// AEC
+ 0xfe , 0x01,
+ 0x01 , 0x04,
+ 0x02 , 0x60,
+ 0x03 , 0x02,
+ 0x04 , 0x48,
+ 0x05 , 0x18,
+ 0x06 , 0x4c,
+ 0x07 , 0x14,
+ 0x08 , 0x36,
+ 0x0a , 0xc0,
+ 0x21 , 0x14,
+ 0xfe , 0x00,
+ //// gamma
+ 0xfe , 0x00,
+ 0xc3 , 0x11,
+ 0xc4 , 0x20,
+ 0xc5 , 0x30,
+ 0xfe , 0x00,
+
+//////////////frame rate 50Hz/////////
+#if 1
+ 0xfe , 0x00,
+ 0x05 , 0x01,
+ 0x06 , 0x56,
+ 0x07 , 0x00,
+ 0x08 , 0x32,
+ 0xfe , 0x01,
+ 0x25 , 0x00,
+ 0x26 , 0xfa,
+ 0x27 , 0x04,
+ 0x28 , 0xe2, //20fps
+ 0x29 , 0x06,
+ 0x2a , 0xd6, //16fps
+ 0x2b , 0x07,
+ 0x2c , 0xd0, //12fps
+ 0x2d , 0x0b,
+ 0x2e , 0xb8, //8fps
+ 0xfe , 0x00,
+#else
+ //////////////frame rate 50Hz
+ 0xfe, 0x00,
+ 0x05, 0x02,
+ 0x06, 0x2d,
+ 0x07, 0x00,
+ 0x08, 0xa0,
+ 0xfe, 0x01,
+ 0x25, 0x00,
+ 0x26, 0xd4,
+ 0x27, 0x04,
+ 0x28, 0xf8,
+ 0x29, 0x08,
+ 0x2a, 0x48,
+ 0x2b, 0x0a,
+ 0x2c, 0xc4,
+ 0x2d, 0x0f,
+ 0x2e, 0xbc,
+ 0xfe, 0x00,
+#endif
+
+};
+
+uint8_t gc2155_640_480_regs[]= {
+ 0xfe , 0x00,
+ 0xfa , 0x00,
+ 0xfd , 0x01,
+ 0xb6 , 0x01,
+ //// crop window
+ 0xfe , 0x00,
+ 0x99 , 0x55,
+ 0x9a , 0x06,
+ 0x9b , 0x01,
+ 0x9c , 0x34,
+ 0x9d , 0x00,
+ 0x9e , 0x00,
+ 0x9f , 0x01,
+ 0xa0 , 0x34,
+ 0xa1 , 0x00,
+ 0xa2 ,0x00,
+ 0x90 , 0x01, //crop enable
+ 0x91 , 0x00,
+ 0x92 , 0x00,
+ 0x93 , 0x00,
+ 0x94 , 0x00,
+ 0x95 , 0x01,
+ 0x96 , 0xe0,
+ 0x97 , 0x02,
+ 0x98 , 0x80,
+
+ //// AWB
+ 0xfe , 0x00,
+ 0xec , 0x01,
+ 0xed , 0x02,
+ 0xee , 0x30,
+ 0xef , 0x48,
+ 0xfe , 0x01,
+ 0x74 , 0x00,
+ //// AEC
+ 0xfe , 0x01,
+ 0x01 , 0x04,
+ 0x02 , 0x60,
+ 0x03 , 0x02,
+ 0x04 , 0x48,
+ 0x05 , 0x18,
+ 0x06 , 0x4c,
+ 0x07 , 0x14,
+ 0x08 , 0x36,
+ 0x0a , 0xc0,
+ 0x21 , 0x14,
+ 0xfe , 0x00,
+};
+
+uint8_t gc2155_default_regs_init[] = {
+ 0xfe, 0xf0,
+ 0xfe, 0xf0,
+ 0xfe, 0xf0,
+ 0xfc, 0x06,
+ 0xf6, 0x00,
+ 0xf7, 0x1d,
+ 0xf8, 0x84,
+ 0xfa, 0x00,
+ 0xf9, 0xfe,
+ 0xf2, 0x0f,
+ ////////////////////////////////////////////////////
+ ////////////////// Analog & Cisctl ////////////////
+ ////////////////////////////////////////////////////
+ 0xfe, 0x00,
+ 0x03, 0x04,
+ 0x04, 0x00,
+ 0x05, 0x01,
+ 0x06, 0x68,
+ 0x09, 0x00,
+ 0x0a, 0x00,
+ 0x0b, 0x00,
+ 0x0c, 0x00,
+ 0x0d, 0x04,
+ 0x0e, 0xc0,
+ 0x0f, 0x06,
+ 0x10, 0x52,
+ 0x12, 0x2e,
+ 0x17, 0x14,
+ 0x18, 0x02,
+ 0x19, 0x0f,
+ 0x1a, 0x01,
+ 0x1b, 0x4b,
+ 0x1c, 0x07,
+ 0x1d, 0x10,
+ 0x1e, 0x98,
+ 0x1f, 0x78,
+ 0x20, 0x03,
+ 0x21, 0x40,
+ 0x22, 0xB0,
+ 0x24, 0x16,
+ 0x25, 0x01,
+ 0x26, 0x10,
+ 0x2d, 0x40,
+ 0x30, 0x01,
+ 0x33, 0x01,
+ 0x34, 0x01,
+ /////////////////////////////////////////////////
+ ////////////////// ISP reg //////////////////////
+ ////////////////////////////////////////////////////
+ 0x80, 0xff,
+ 0x81, 0x24,
+ 0x82, 0xfE,
+ 0x83, 0x00,
+ 0x84, 0x02,
+ 0x86, 0x02,
+ 0x88, 0x03,
+ 0x89, 0x03,
+ 0x85, 0x30,
+ 0x8a, 0x00,
+ 0x8b, 0x00,
+ 0xb0, 0x50,
+ 0xc3, 0x00,
+ 0xc4, 0x18,
+ 0xc5, 0x20,
+ 0xc6, 0x38,
+ 0xc7, 0x40,
+ 0xec, 0x06,
+ 0xed, 0x04,
+ 0xee, 0x60,
+ 0xef, 0x90,
+ 0xb6, 0x01,
+ 0x90, 0x01,
+ 0x91, 0x00,
+ 0x92, 0x00,
+ 0x93, 0x00,
+ 0x94, 0x00,
+ 0x95, 0x04,
+ 0x96, 0xb0,
+ 0x97, 0x06,
+ 0x98, 0x40,
+
+ ///////////////////////////////////////////////
+ /////////// BLK ////////////////////////
+ ///////////////////////////////////////////////
+ 0x18, 0x02,
+ 0x40, 0x42,
+ 0x41, 0x00,
+ 0x43, 0x54,
+ 0x5e, 0x00,
+ 0x5f, 0x00,
+ 0x60, 0x00,
+ 0x61, 0x00,
+ 0x62, 0x00,
+ 0x63, 0x00,
+ 0x64, 0x00,
+ 0x65, 0x00,
+ 0x66, 0x20,
+ 0x67, 0x20,
+ 0x68, 0x20,
+ 0x69, 0x20,
+ 0x6a, 0x00,
+ 0x6b, 0x00,
+ 0x6c, 0x00,
+ 0x6d, 0x00,
+ 0x6e, 0x00,
+ 0x6f, 0x00,
+ 0x70, 0x00,
+ 0x71, 0x00,
+ 0x72, 0xf0,
+ 0x7e, 0x3c,
+ 0x7f, 0x00,
+ 0xfe, 0x02,
+ 0x49, 0x04,
+ 0xfe, 0x00,
+ ///////////////////////////////////////////////
+ /////////// AEC ////////////////////////
+ ///////////////////////////////////////////////
+ 0xfe, 0x01,
+ 0x01, 0x04,
+ 0x02, 0xc0,
+ 0x03, 0x04,
+ 0x04, 0x90,
+ 0x05, 0x30,
+ 0x06, 0x90,
+ 0x07, 0x20,
+ 0x08, 0x70,
+ 0x09, 0x00,
+ 0x0a, 0xc2,
+ 0x0b, 0x11,
+ 0x0c, 0x00,
+ 0x13, 0x30,
+ 0x17, 0x00,
+ 0x1c, 0x11,
+ 0x1e, 0x61,
+ 0x1f, 0x30,
+ 0x20, 0x40,
+ 0x22, 0x80,
+ 0x23, 0x20,
+ 0x12, 0x35,
+ 0x15, 0x50,
+ 0x10, 0x31,
+ 0x3e, 0x28,
+ 0x3f, 0xe0,
+ 0x40, 0xe0,
+ 0x41, 0x0f,
+
+ /////////////////////////////
+ //////// INTPEE /////////////
+ /////////////////////////////
+ 0xfe, 0x02,
+ 0x90, 0x6c,
+ 0x91, 0x02,
+ 0x92, 0x44,
+ 0x97, 0x36,
+ 0xa2, 0x11,
+ 0xfe, 0x00,
+
+ /////////////////////////////
+ //////// DNDD///////////////
+ /////////////////////////////
+ 0xfe, 0x02,
+ 0x80, 0xe1,
+ 0x81, 0x08,
+ 0x82, 0x05,
+ 0x83, 0x1f,
+ 0x84, 0x0a,
+ 0x86, 0x50,
+ 0x87, 0x30,
+ 0x88, 0x15,
+ 0x89, 0x60,
+ 0x8a, 0x40,
+ 0x8b, 0x20,
+
+ ///////////////////////////////////////////////
+ /////////// ASDE ////////////////////////
+ ///////////////////////////////////////////////
+ 0xfe, 0x01,
+ 0x21, 0x14,
+ 0xfe, 0x02,
+ 0x4b, 0x00,
+ 0xa3, 0x40,
+ 0xa4, 0x20,
+ 0xa5, 0x10,
+ 0xa6, 0x20,
+ 0xab, 0x40,
+ 0xae, 0x0c,
+ 0xb3, 0x42,
+ 0xb4, 0x24,
+ 0xb6, 0x40,
+ 0xb7, 0x01,
+ 0xb9, 0x32,
+ 0xfe, 0x00,
+
+ ///////////////////gamma1////////////////////
+ ////Gamma
+ 0xfe, 0x02,
+ 0x10, 0x06,
+ 0x11, 0x0d,
+ 0x12, 0x15,
+ 0x13, 0x1b,
+ 0x14, 0x2a,
+ 0x15, 0x38,
+ 0x16, 0x48,
+ 0x17, 0x55,
+ 0x18, 0x6b,
+ 0x19, 0x7e,
+ 0x1a, 0x92,
+ 0x1b, 0xa3,
+ 0x1c, 0xaf,
+ 0x1d, 0xc5,
+ 0x1e, 0xd5,
+ 0x1f, 0xe0,
+ 0x20, 0xe7,
+ 0x21, 0xee,
+ 0x22, 0xf1,
+ 0x23, 0xf8,
+ 0x24, 0xfd,
+ 0x25, 0xff,
+
+ ///////////////////gamma2////////////////////
+ ////Gamma
+ 0xfe, 0x02,
+ 0x26, 0x06,
+ 0x27, 0x10,
+ 0x28, 0x18,
+ 0x29, 0x1d,
+ 0x2a, 0x26,
+ 0x2b, 0x2d,
+ 0x2c, 0x33,
+ 0x2d, 0x3a,
+ 0x2e, 0x45,
+ 0x2f, 0x50,
+ 0x30, 0x5a,
+ 0x31, 0x62,
+ 0x32, 0x6b,
+ 0x33, 0x7b,
+ 0x34, 0x8a,
+ 0x35, 0x99,
+ 0x36, 0xa7,
+ 0x37, 0xb4,
+ 0x38, 0xbf,
+ 0x39, 0xd7,
+ 0x3a, 0xec,
+ 0x3b, 0xff,
+
+ ///////////////////////////////////////////////
+ /////////// YCP ///////////////////////
+ ///////////////////////////////////////////////
+ 0xfe, 0x02,
+ 0xd1, 0x28,
+ 0xd2, 0x28,
+ 0xdd, 0x14,
+ 0xde, 0x84,
+
+ ////////////////////////////
+ //////// LSC ///////////////
+ ////////////////////////////
+ 0xfe, 0x01,
+ 0xc2, 0x13,
+ 0xc3, 0x05,
+ 0xc4, 0x01,
+ 0xc8, 0x10,
+ 0xc9, 0x0b,
+ 0xca, 0x06,
+ 0xbc, 0x34,
+ 0xbd, 0x21,
+ 0xbe, 0x16,
+ 0xb6, 0x2d,
+ 0xb7, 0x20,
+ 0xb8, 0x19,
+ 0xc5, 0x0b,
+ 0xc6, 0x29,
+ 0xc7, 0x1d,
+ 0xcb, 0x0b,
+ 0xcc, 0x15,
+ 0xcd, 0x16,
+ 0xbf, 0x11,
+ 0xc0, 0x03,
+ 0xc1, 0x04,
+ 0xb9, 0x0f,
+ 0xba, 0x00,
+ 0xbb, 0x06,
+ 0xaa, 0x00,
+ 0xab, 0x00,
+ 0xac, 0x00,
+ 0xad, 0x00,
+ 0xae, 0x00,
+ 0xaf, 0x00,
+ 0xb0, 0x1b,
+ 0xb1, 0x1e,
+ 0xb2, 0x16,
+ 0xb3, 0x00,
+ 0xb4, 0x00,
+ 0xb5, 0x00,
+ 0xd0, 0x2c,
+ 0xd1, 0x24,
+ 0xd2, 0x18,
+ 0xd6, 0x24,
+ 0xd7, 0x1d,
+ 0xd8, 0x08,
+ 0xd9, 0x62,
+ 0xda, 0x63,
+ 0xdb, 0x48,
+ 0xd3, 0x1f,
+ 0xd4, 0x21,
+ 0xd5, 0x11,
+ 0xa4, 0x00,
+ 0xa5, 0x00,
+ 0xa6, 0x56,
+ 0xa7, 0x00,
+ 0xa8, 0x77,
+ 0xa9, 0x77,
+ 0xa1, 0x80,
+ 0xa2, 0x80,
+
+ ///////////////////////////////////////////////
+ /////////// AWB ////////////////////////
+ ///////////////////////////////////////////////
+ 0xfe, 0x01,
+ 0x4f, 0x00,
+ 0x4f, 0x00,
+ 0x4b, 0x01,
+ 0x4f, 0x00,
+ 0x4c, 0x01,
+ 0x4d, 0x70,
+ 0x4e, 0x02,
+ 0x4c, 0x01,
+ 0x4d, 0x6f,
+ 0x4e, 0x02,
+ 0x4c, 0x01,
+ 0x4d, 0x8f,
+ 0x4e, 0x02,
+ 0x4c, 0x01,
+ 0x4d, 0x6e,
+ 0x4e, 0x02,
+ 0x4c, 0x01,
+ 0x4d, 0x8e,
+ 0x4e, 0x02,
+ 0x4c, 0x01,
+ 0x4d, 0xad,
+ 0x4e, 0x03,
+ 0x4c, 0x01,
+ 0x4d, 0x6d,
+ 0x4e, 0x03,
+ 0x4c, 0x01,
+ 0x4d, 0x8d,
+ 0x4e, 0x03,
+ 0x4c, 0x01,
+ 0x4d, 0xcd,
+ 0x4e, 0x03,
+ 0x4c, 0x01,
+ 0x4d, 0x6c,
+ 0x4e, 0x03,
+ 0x4c, 0x01,
+ 0x4d, 0x8c,
+ 0x4e, 0x03,
+ 0x4c, 0x01,
+ 0x4d, 0xac,
+ 0x4e, 0x03,
+ 0x4c, 0x01,
+ 0x4d, 0xcc,
+ 0x4e, 0x03,
+ 0x4c, 0x01,
+ 0x4d, 0xec,
+ 0x4e, 0x03,
+ 0x4c, 0x02,
+ 0x4d, 0x0c,
+ 0x4e, 0x03,
+ 0x4c, 0x01,
+ 0x4d, 0x6b,
+ 0x4e, 0x03,
+ 0x4c, 0x01,
+ 0x4d, 0x8b,
+ 0x4e, 0x03,
+ 0x4c, 0x01,
+ 0x4d, 0xab,
+ 0x4e, 0x03,
+ 0x4c, 0x01,
+ 0x4d, 0xcb,
+ 0x4e, 0x03,
+ //0x4c, 0x01,
+ //0x4d, 0xeb,
+ //0x4e, 0x03,
+ 0x4c, 0x02,
+ 0x4d, 0x0b,
+ 0x4e, 0x03,
+ 0x4c, 0x02,
+ 0x4d, 0x2b,
+ 0x4e, 0x03,
+ 0x4c, 0x01,
+ 0x4d, 0xca,
+ 0x4e, 0x03,
+ 0x4c, 0x01,
+ 0x4d, 0xea,
+ 0x4e, 0x03,
+ 0x4c, 0x02,
+ 0x4d, 0x2a,
+ 0x4e, 0x03,
+ 0x4c, 0x02,
+ 0x4d, 0x4a,
+ 0x4e, 0x03,
+ 0x4c, 0x02,
+ 0x4d, 0x6a,
+ 0x4e, 0x03,
+ 0x4c, 0x02,
+ 0x4d, 0x49,
+ 0x4e, 0x03,
+ 0x4c, 0x02,
+ 0x4d, 0x69,
+ 0x4e, 0x03,
+ 0x4c, 0x01,
+ 0x4d, 0xaa,
+ 0x4e, 0x04,
+ 0x4c, 0x01,
+ 0x4d, 0xa9,
+ 0x4e, 0x04,
+ 0x4c, 0x01,
+ 0x4d, 0xc9,
+ 0x4e, 0x04,
+ 0x4c, 0x01,
+ 0x4d, 0xe8,
+ 0x4e, 0x04,
+ 0x4c, 0x01,
+ 0x4d, 0xeb,
+ 0x4e, 0x05,
+ 0x4c, 0x02,
+ 0x4d, 0x0a,
+ 0x4e, 0x05,
+ 0x4c, 0x01,
+ 0x4d, 0xe9,
+ 0x4e, 0x05,
+ 0x4c, 0x02,
+ 0x4d, 0x09,
+ 0x4e, 0x05,
+ 0x4c, 0x02,
+ 0x4d, 0x29,
+ 0x4e, 0x05,
+ 0x4c, 0x02,
+ 0x4d, 0x08,
+ 0x4e, 0x05,
+ 0x4c, 0x02,
+ 0x4d, 0x89,
+ 0x4e, 0x06,
+ 0x4c, 0x02,
+ 0x4d, 0x68,
+ 0x4e, 0x06,
+ 0x4c, 0x02,
+ 0x4d, 0x88,
+ 0x4e, 0x06,
+ 0x4c, 0x02,
+ 0x4d, 0x87,
+ 0x4e, 0x06,
+ 0x4c, 0x02,
+ 0x4d, 0xa7,
+ 0x4e, 0x07,
+ 0x4c, 0x02,
+ 0x4d, 0xc7,
+ 0x4e, 0x07,
+ 0x4f, 0x01,
+ 0xfe, 0x01,
+ 0x50, 0x80,
+ 0x51, 0x9c,
+ 0x52, 0x63,
+ 0x53, 0x38,
+ 0x54, 0xc7,
+ 0x56, 0x06,
+ 0x58, 0x08,
+ 0x5b, 0x00,
+ 0x5c, 0x74,
+ 0x5d, 0x8b,
+ 0x63, 0xa4,
+ 0x65, 0x04,
+ 0x67, 0xb2,
+ 0x68, 0xac,
+ 0x69, 0x00,
+ 0x6a, 0xb2,
+ 0x6b, 0xac,
+ 0x6c, 0xb2,
+ 0x6d, 0xac,
+ 0x6e, 0x40,
+ 0x6f, 0x50,
+ 0x73, 0xe0,
+ 0x70, 0x10,
+ 0x71, 0xe0,
+ 0x74, 0x01,
+ 0x75, 0x01,
+ 0x7f, 0x08,
+ 0x76, 0x70,
+ 0x77, 0x58,
+ 0x78, 0xd8,
+ 0xfe, 0x00,
+
+ //////////////////////////////////////////
+ /////////// CC ////////////////////////
+ //////////////////////////////////////////
+ 0xfe, 0x02,
+ 0xc0, 0x01,
+ 0xc1, 0x50,
+ 0xc2, 0x00,
+ 0xc3, 0x00,
+ 0xc4, 0xf0,
+ 0xc5, 0x45,
+ 0xc6, 0xf0,
+ 0xc7, 0x40,
+ 0xc8, 0x00,
+ 0xc9, 0x00,
+ 0xca, 0x00,
+ 0xcb, 0x40,
+ 0xcc, 0x00,
+ 0xcd, 0x40,
+ 0xce, 0x00,
+ 0xcf, 0x00,
+ 0xe3, 0x00,
+ 0xe4, 0x40,
+ 0xe5, 0x00,
+ 0xfe, 0x00,
+ 0xf2, 0x0f,
+
+ //////////////////////////////////////////
+ /////////// BUF ////////////////////////
+ //////////////////////////////////////////
+ 0xfe, 0x03,
+ 0x40, 0x00,
+ 0x41, 0x00,
+ 0x42, 0x40,
+ 0x43, 0x06,
+ 0x04, 0x20,
+ 0x06, 0x00,
+ 0x17, 0xe0,
+ 0xfe, 0x00,
+
+ //////////////frame rate 50Hz
+ 0xfe, 0x00,
+ 0x05, 0x02,
+ 0x06, 0x2d,
+ 0x07, 0x00,
+ 0x08, 0xa0,
+ 0xfe, 0x01,
+ 0x25, 0x00,
+ 0x26, 0xd4,
+ 0x27, 0x04,
+ 0x28, 0xf8,
+ 0x29, 0x08,
+ 0x2a, 0x48,
+ 0x2b, 0x0b,
+ 0x2c, 0x98,
+ 0x2d, 0x0f,
+ 0x2e, 0xbc,
+ 0xfe, 0x00,
+};
+
+uint8_t gc2155_default_regs_exit[]={
+
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/hi253/hi253-bk-wave7.h b/drivers/media/video/wmt_v4l2/sensors/hi253/hi253-bk-wave7.h
new file mode 100755
index 00000000..27f406c0
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/hi253/hi253-bk-wave7.h
@@ -0,0 +1,2147 @@
+#ifndef YACD511_H
+#define YACD511_H
+
+// Scene Mode
+uint8_t hi253_scene_mode_auto[] = {
+ 0x03,0x10,
+ 0x41,0x00,
+ 0x03,0x20,
+ 0xb2,0x80,
+ 0xff,0xff,
+};
+
+uint8_t hi253_scene_mode_night[] = {
+ 0x03,0x10,
+ 0x41,0x15,
+ 0x03,0x20,
+ 0xb2,0xf0,
+ 0xff,0xff,
+};
+
+
+// White Balance
+uint8_t hi253_wb_auto [] = {
+ 0x03,0x22,
+ //0x11,0x2e,
+ 0x83,0x52, // RMAX
+ 0x84, 0x16, // RMIN
+ 0x85, 0x5a, // BMAX
+ 0x86, 0x23, // BMIN
+ 0xff,0xff,
+};
+
+uint8_t hi253_wb_incandescent [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x2a,
+ 0x82,0x3f,
+ 0x83,0x35,
+ 0x84,0x28,
+ 0x85,0x45,
+ 0x86,0x3b,
+ 0xff,0xff,
+};
+
+uint8_t hi253_wb_fluorescent [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x26,
+ 0x82,0x44,
+ 0x83,0x2f,
+ 0x84,0x24,
+ 0x85,0x4a,
+ 0x86,0x40,
+ 0xff,0xff,
+};
+
+uint8_t hi253_wb_daylight [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x3d,
+ 0x82,0x2e,
+ 0x83,0x40,
+ 0x84,0x33,
+ 0x85,0x33,
+ 0x86,0x28,
+ 0xff,0xff,
+};
+
+uint8_t hi253_wb_cloudy [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x50,
+ 0x82,0x25,
+ 0x83,0x55,
+ 0x84,0x4b,
+ 0x85,0x28,
+ 0x86,0x20,
+ 0xff,0xff,
+};
+
+
+// Exposure
+uint8_t hi253_exposure_neg6[] = {
+ 0x03,0x10,
+ 0x40,0xa8,
+};
+
+uint8_t hi253_exposure_neg3[] = {
+ 0x03,0x10,
+ 0x40,0x98,
+};
+
+uint8_t hi253_exposure_zero[] = {
+ 0x03,0x10,
+ 0x40,0x85,
+};
+
+uint8_t hi253_exposure_pos3[] = {
+ 0x03,0x10,
+ 0x40,0x08,
+};
+
+uint8_t hi253_exposure_pos6[] = {
+ 0x03,0x10,
+ 0x40,0x28,
+};
+
+
+// Resolution
+uint8_t hi253_1600_1200_rets[]={
+ 0x03, 0x00,
+ 0x10, 0x00,
+ 0x12, 0x04,
+ 0x20, 0x00,
+ 0x21, 0x0a,
+ 0x22, 0x00,
+ 0x23, 0x0a,
+
+ 0x03, 0x18,
+ 0x10, 0x00,
+ 0xff, 0xff,
+};
+
+uint8_t hi253_1280_720_regs[]={
+};
+
+uint8_t hi253_800_600_regs[]={
+
+ 0x03, 0x00,
+ 0x10, 0x10,
+ 0x12, 0x04,
+
+ 0x03, 0x18,
+ 0x10, 0x00,
+ 0xff, 0xff,
+};
+
+uint8_t hi253_640_480_regs[]={
+ 0x03, 0x00,
+ 0x10, 0x10,
+ 0x12, 0x04,
+
+ 0x03, 0x18,
+ 0x12, 0x20,
+ 0x10, 0x07,
+ 0x11, 0x00,
+ 0x20, 0x05,
+ 0x21, 0x00,
+ 0x22, 0x03,
+ 0x23, 0xc0,
+ 0x24, 0x00,
+ 0x25, 0x04,
+ 0x26, 0x00,
+ 0x27, 0x00,
+ 0x28, 0x05,
+ 0x29, 0x04,
+ 0x2a, 0x03,
+ 0x2b, 0xc0,
+ 0x2c, 0x0a,
+ 0x2d, 0x00,
+ 0x2e, 0x0a,
+ 0x2f, 0x00,
+ 0x30, 0x45,
+ 0xff, 0xff,
+};
+
+uint8_t hi253_320_240_regs[]= {
+ 0x01, 0xf9,
+ 0x08, 0x0f,
+ 0x01, 0xf8,
+
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+
+ 0x0e, 0x00,
+
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+
+ 0x0e, 0x00,
+ 0x01, 0xf1,
+ 0x08, 0x00,
+ 0x01, 0xf3,
+ 0x01, 0xf1,
+
+ 0x03, 0x20,
+ 0x10, 0x0c,
+ 0x03, 0x22,
+ 0x10, 0x69,
+
+ //Page 00
+ 0x03, 0x00,
+ 0x10, 0x11, //lxh
+ 0x11, 0x90,
+ 0x12, 0x04,
+ 0x0b, 0xaa,
+ 0x0c, 0xaa,
+ 0x0d, 0xaa,
+ 0x20, 0x00,
+ 0x21, 0x0a, //lxh
+ 0x22, 0x00,
+ 0x23, 0x0a, //lxh
+ 0x24, 0x04,
+ 0x25, 0xb0,
+ 0x26, 0x06,
+ 0x27, 0x40,
+
+ 0x40, 0x00,
+ 0x41, 0xe8,//lxh
+ 0x42, 0x00,
+ 0x43, 0x14,//lxh
+
+ 0x45, 0x04,
+ 0x46, 0x18,
+ 0x47, 0xd8,
+
+ 0xe1, 0x0f,
+
+ //BLC
+ 0x80, 0x2e,
+ 0x81, 0x7e,
+ 0x82, 0x90,
+ 0x83, 0x00,
+ 0x84, 0x0c,
+ 0x85, 0x00,
+ 0x90, 0x0c,
+ 0x91, 0x0c,
+ 0x92, 0x78,
+ 0x93, 0x70,
+ 0x94, 0x75,
+ 0x95, 0x70,
+ 0x96, 0xdc,
+ 0x97, 0xfe,
+ 0x98, 0x20,
+
+ //OutDoor BLC
+ 0x99,0x42,
+ 0x9a,0x42,
+ 0x9b,0x42,
+ 0x9c,0x42,
+
+ //Dark BLC
+ 0xa0, 0x02,
+ 0xa2, 0x02,
+ 0xa4, 0x02,
+ 0xa6, 0x02,
+
+ //Normal BLC
+ 0xa8, 0x42,
+ 0xaa, 0x42,
+ 0xac, 0x42,
+ 0xae, 0x42,
+
+ //Page 02
+ 0x03, 0x02,
+ 0x12, 0x03,
+ 0x13, 0x03,
+ 0x16, 0x00,
+ 0x17, 0x8C,
+ 0x18, 0x4c,
+ 0x19, 0x00,
+ 0x1a, 0x39,
+ 0x1c, 0x09,
+ 0x1d, 0x40,
+ 0x1e, 0x30,
+ 0x1f, 0x10,
+ 0x20, 0x77,
+ 0x21, 0xde,
+ 0x22, 0xa7,
+ 0x23, 0x30,
+ 0x27, 0x3c,
+ 0x2b, 0x80,
+ 0x2e, 0x00,
+ 0x2f, 0x00,
+ 0x30, 0x05,
+ 0x50, 0x20,
+ 0x52, 0x01,
+ 0x53, 0xc1,
+ 0x55, 0x1c,
+ 0x56, 0x11,
+ 0x5d, 0xA2,
+ 0x5e, 0x5a,
+ 0x60, 0x87,
+ 0x61, 0x99,
+ 0x62, 0x88,
+ 0x63, 0x97,
+ 0x64, 0x88,
+ 0x65, 0x97,
+ 0x67, 0x0c,
+ 0x68, 0x0c,
+ 0x69, 0x0c,
+ 0x72, 0x89,
+ 0x73, 0x96,
+ 0x74, 0x89,
+ 0x75, 0x96,
+ 0x76, 0x89,
+ 0x77, 0x96,
+ 0x7C, 0x85,
+ 0x7d, 0xaf,
+ 0x80, 0x01,
+ 0x81, 0x7f,
+ 0x82, 0x13,
+ 0x83, 0x24,
+ 0x84, 0x7d,
+ 0x85, 0x81,
+ 0x86, 0x7d,
+ 0x87, 0x81,
+ 0x92, 0x48,
+ 0x93, 0x54,
+ 0x94, 0x7d,
+ 0x95, 0x81,
+ 0x96, 0x7d,
+ 0x97, 0x81,
+ 0xa0, 0x02,
+ 0xa1, 0x7b,
+ 0xa2, 0x02,
+ 0xa3, 0x7b,
+ 0xa4, 0x7b,
+ 0xa5, 0x02,
+ 0xa6, 0x7b,
+ 0xa7, 0x02,
+ 0xa8, 0x85,
+ 0xa9, 0x8c,
+ 0xaa, 0x85,
+ 0xab, 0x8c,
+ 0xac, 0x10,
+ 0xad, 0x16,
+ 0xae, 0x10,
+ 0xaf, 0x16,
+ 0xb0, 0x99,
+ 0xb1, 0xa3,
+ 0xb2, 0xa4,
+ 0xb3, 0xae,
+ 0xb4, 0x9b,
+ 0xb5, 0xa2,
+ 0xb6, 0xa6,
+ 0xb7, 0xac,
+ 0xb8, 0x9b,
+ 0xb9, 0x9f,
+ 0xba, 0xa6,
+ 0xbb, 0xaa,
+ 0xbc, 0x9b,
+ 0xbd, 0x9f,
+ 0xbe, 0xa6,
+ 0xbf, 0xaa,
+ 0xc4, 0x2c,
+ 0xc5, 0x43,
+ 0xc6, 0x63,
+ 0xc7, 0x79,
+ 0xc8, 0x2d,
+ 0xc9, 0x42,
+ 0xca, 0x2d,
+ 0xcb, 0x42,
+ 0xcc, 0x64,
+ 0xcd, 0x78,
+ 0xce, 0x64,
+ 0xcf, 0x78,
+ 0xd0, 0x0a,
+ 0xd1, 0x09,
+ 0xd4, 0x0c,
+ 0xd5, 0x0c,
+ 0xd6, 0x78,
+ 0xd7, 0x70,//lxh
+ 0xe0, 0xc4,
+ 0xe1, 0xc4,
+ 0xe2, 0xc4,
+ 0xe3, 0xc4,
+ 0xe4, 0x00,
+ 0xe8, 0x80,
+ 0xe9, 0x40,
+ 0xea, 0x7f,
+ 0xf0, 0x01,
+ 0xf1, 0x01,
+ 0xf2, 0x01,
+ 0xf3, 0x01,
+ 0xf4, 0x01,
+
+ //PAGE10
+ 0x03, 0x10,
+ 0x10, 0x03, //lxh
+ 0x11, 0x03, //lxh,normal
+ 0x12, 0x30,
+ 0x13, 0x02,
+ 0x20, 0x00,
+
+ 0x40, 0x88,
+ 0x41, 0x05,
+ 0x48, 0x88,// 84
+ 0x50, 0x90,
+ 0x30, 0x00,
+ 0x31, 0x00,
+ 0x32, 0x00,
+ 0x33, 0x00,
+
+ 0x34, 0x30,
+ 0x35, 0x00,
+ 0x36, 0x00,
+ 0x38, 0x00,
+ 0x3e, 0x58,
+ 0x3f, 0x00,
+
+ //Saturation
+ 0x60, 0x67,
+ 0x61, 0x78,// 74
+ 0x62, 0x78,// 76
+ 0x63, 0x50,
+ 0x64, 0x41,
+
+ 0x66, 0x33,
+ 0x67, 0x00,
+
+ 0x6a, 0x90,
+ 0x6b, 0x80,
+ 0x6c, 0x80,
+ 0x6d, 0xa0,
+
+ 0x76, 0x01,
+ 0x74, 0x66,
+ 0x79, 0x06,
+
+ //Page 11
+ 0x03, 0x11,
+ 0x10, 0x7f,//lxh,3f
+ 0x11, 0x40,
+ 0x12, 0xba,
+ 0x13, 0xcb,
+ 0x26, 0x20,
+ 0x27, 0x22,
+ 0x28, 0x0f,
+ 0x29, 0x10,
+ 0x2b, 0x30,
+ 0x2c, 0x32,
+
+ //Out2 D-LPF th
+ 0x30, 0x70,
+ 0x31, 0x10,
+ 0x32, 0x65,
+ 0x33, 0x09,
+ 0x34, 0x06,
+ 0x35, 0x04,
+
+ //Out1 D-LPF th
+ 0x36, 0x70,
+ 0x37, 0x18,
+ 0x38, 0x65,
+ 0x39, 0x09,
+ 0x3a, 0x06,
+ 0x3b, 0x04,
+
+ //Indoor D-LPF th
+ 0x3c, 0x80,
+ 0x3d, 0x18,
+ 0x3e, 0x80,
+ 0x3f, 0x0c,
+ 0x40, 0x09,
+ 0x41, 0x06,
+
+ 0x42, 0x80,
+ 0x43, 0x18,
+ 0x44, 0x80,
+ 0x45, 0x12,
+ 0x46, 0x10,
+ 0x47, 0x10,
+ 0x48, 0x90,
+ 0x49, 0x40,
+ 0x4a, 0x80,
+ 0x4b, 0x13,
+ 0x4c, 0x10,
+ 0x4d, 0x11,
+ 0x4e, 0x80,
+ 0x4f, 0x30,
+ 0x50, 0x80,
+ 0x51, 0x13,
+ 0x52, 0x10,
+ 0x53, 0x13,
+ 0x54, 0x11,
+ 0x55, 0x17,
+ 0x56, 0x20,
+ 0x57, 0x20,
+ 0x58, 0x20,
+ 0x59, 0x30,
+ 0x5a, 0x18,
+ 0x5b, 0x00,
+ 0x5c, 0x00,
+ 0x60, 0x3f,
+ 0x62, 0x50,
+ 0x70, 0x06,
+
+ //Page 12
+ 0x03, 0x12,
+ 0x20, 0x0f,
+ 0x21, 0x0f,
+ 0x25, 0x30,
+ 0x28, 0x00,
+ 0x29, 0x00,
+ 0x2a, 0x00,
+ 0x30, 0x50,
+ 0x31, 0x18,
+ 0x32, 0x32,
+ 0x33, 0x40,
+ 0x34, 0x50,
+ 0x35, 0x70,
+ 0x36, 0xa0,
+
+ //Out2 th
+ 0x40, 0xa0,
+ 0x41, 0x40,
+ 0x42, 0xa0,
+ 0x43, 0x90,
+ 0x44, 0x90,
+ 0x45, 0x80,
+
+ //Out1 th
+ 0x46, 0xb0,
+ 0x47, 0x55,
+ 0x48, 0xa0,
+ 0x49, 0x90,
+ 0x4a, 0x90,
+ 0x4b, 0x80,
+
+ //In door th
+ 0x4c, 0xb0,
+ 0x4d, 0x40,
+ 0x4e, 0x90,
+ 0x4f, 0x90,
+ 0x50, 0xe6,
+ 0x51, 0x80,
+
+ //Dark1 th
+ 0x52, 0xb0,
+ 0x53, 0x60,
+ 0x54, 0xc0,
+ 0x55, 0xc0,
+ 0x56, 0xc0,
+ 0x57, 0x80,
+
+ //Dark2 th
+ 0x58, 0x90,
+ 0x59, 0x40,
+ 0x5a, 0xd0,
+ 0x5b, 0xd0,
+ 0x5c, 0xe0,
+ 0x5d, 0x80,
+
+ //Dark3 th
+ 0x5e, 0x88,
+ 0x5f, 0x40,
+ 0x60, 0xe0,
+ 0x61, 0xe6,
+ 0x62, 0xe6,
+ 0x63, 0x80,
+
+ 0x70, 0x15,
+ 0x71, 0x01,
+
+ 0x72, 0x18,
+ 0x73, 0x01,
+
+ 0x74, 0x25,
+ 0x75, 0x15,
+ 0x80, 0x30,
+ 0x81, 0x50,
+ 0x82, 0x80,
+ 0x85, 0x1a,
+ 0x88, 0x00,
+ 0x89, 0x00,
+ 0x90, 0x5d,
+
+ 0xc5, 0x30,
+ 0xc6, 0x2a,
+
+ 0xD0, 0x0c,
+ 0xD1, 0x80,
+ 0xD2, 0x67,
+ 0xD3, 0x00,
+ 0xD4, 0x00,
+ 0xD5, 0x02,
+ 0xD6, 0xff,
+ 0xD7, 0x18,
+ 0x3b, 0x06,
+ 0x3c, 0x06,
+
+ 0xc5, 0x30,
+ 0xc6, 0x2a,
+
+ //Page 13
+ 0x03, 0x13,
+ 0x10, 0xcb,
+ 0x11, 0x7b,
+ 0x12, 0x07,
+ 0x14, 0x00,
+
+ 0x20, 0x15,
+ 0x21, 0x13,
+ 0x22, 0x33,
+ 0x23, 0x04,
+ 0x24, 0x09,
+ 0x25, 0x08,
+ 0x26, 0x18,
+ 0x27, 0x30,
+ 0x29, 0x12,
+ 0x2a, 0x50,
+
+ //Low clip th
+ 0x2b, 0x06,
+ 0x2c, 0x06,
+ 0x25, 0x08,
+ 0x2d, 0x0c,
+ 0x2e, 0x12,
+ 0x2f, 0x12,
+
+ //Out2 Edge
+ 0x50, 0x10,
+ 0x51, 0x14,
+ 0x52, 0x10,
+ 0x53, 0x0c,
+ 0x54, 0x0f,
+ 0x55, 0x0c,
+
+ //Out1 Edge
+ 0x56, 0x10,
+ 0x57, 0x13,
+ 0x58, 0x10,
+ 0x59, 0x0c,
+ 0x5a, 0x0f,
+ 0x5b, 0x0c,
+
+ //Indoor Edge
+ 0x5c, 0x0a,
+ 0x5d, 0x0b,
+ 0x5e, 0x0a,
+ 0x5f, 0x08,
+ 0x60, 0x09,
+ 0x61, 0x08,
+
+ //Dark1 Edge
+ 0x62, 0x08,
+ 0x63, 0x08,
+ 0x64, 0x08,
+ 0x65, 0x06,
+ 0x66, 0x06,
+ 0x67, 0x06,
+
+ //Dark2 Edge
+ 0x68, 0x07,
+ 0x69, 0x07,
+ 0x6a, 0x07,
+ 0x6b, 0x05,
+ 0x6c, 0x05,
+ 0x6d, 0x05,
+
+ //Dark3 Edge
+ 0x6e, 0x07,
+ 0x6f, 0x07,
+ 0x70, 0x07,
+ 0x71, 0x05,
+ 0x72, 0x05,
+ 0x73, 0x05,
+
+ //2DY
+ 0x80, 0xfd,
+ 0x81, 0x1f,
+ 0x82, 0x05,
+ 0x83, 0x01,
+
+ 0x90, 0x15,
+ 0x91, 0x15,
+ 0x92, 0x33,
+ 0x93, 0x30,
+ 0x94, 0x03,
+ 0x95, 0x14,
+ 0x97, 0x30,
+ 0x99, 0x30,
+
+ 0xa0, 0x04,
+ 0xa1, 0x05,
+ 0xa2, 0x04,
+ 0xa3, 0x05,
+ 0xa4, 0x07,
+ 0xa5, 0x08,
+ 0xa6, 0x07,
+ 0xa7, 0x08,
+ 0xa8, 0x07,
+ 0xa9, 0x08,
+ 0xaa, 0x07,
+ 0xab, 0x08,
+
+ //Out2
+ 0xb0, 0x22,
+ 0xb1, 0x2a,
+ 0xb2, 0x28,
+ 0xb3, 0x22,
+ 0xb4, 0x2a,
+ 0xb5, 0x28,
+
+ //Out1
+ 0xb6, 0x22,
+ 0xb7, 0x2a,
+ 0xb8, 0x28,
+ 0xb9, 0x22,
+ 0xba, 0x2a,
+ 0xbb, 0x28,
+
+ 0xbc, 0x17,
+ 0xbd, 0x17,
+ 0xbe, 0x17,
+ 0xbf, 0x17,
+ 0xc0, 0x17,
+ 0xc1, 0x17,
+
+ //Dark1
+ 0xc2, 0x1e,
+ 0xc3, 0x12,
+ 0xc4, 0x10,
+ 0xc5, 0x1e,
+ 0xc6, 0x12,
+ 0xc7, 0x10,
+
+ //Dark2
+ 0xc8, 0x18,
+ 0xc9, 0x05,
+ 0xca, 0x05,
+ 0xcb, 0x18,
+ 0xcc, 0x05,
+ 0xcd, 0x05,
+
+ //Dark3
+ 0xce, 0x18,
+ 0xcf, 0x05,
+ 0xd0, 0x05,
+ 0xd1, 0x18,
+ 0xd2, 0x05,
+ 0xd3, 0x05,
+
+ //Page 14
+ 0x03, 0x14,
+ 0x10, 0x11,
+ 0x20, 0x40,
+ 0x21, 0x80,
+ 0x23, 0x80,
+ 0x22, 0x80,
+ 0x23, 0x80,
+ 0x24, 0x80,
+
+ 0x30, 0xc8,
+ 0x31, 0x2b,
+ 0x32, 0x00,
+ 0x33, 0x00,
+ 0x34, 0x90,
+
+ 0x40, 0x40,
+ 0x50, 0x2d,
+ 0x60, 0x28,
+ 0x70, 0x2d,
+
+ //Page 15
+ 0x03, 0x15,
+ 0x10, 0x0f,
+ 0x14, 0x52,
+ 0x15, 0x42,
+ 0x16, 0x32,
+ 0x17, 0x2f,
+
+ //CMC
+ 0x30, 0x8f,
+ 0x31, 0x59,
+ 0x32, 0x0a,
+ 0x33, 0x15,
+ 0x34, 0x5b,
+ 0x35, 0x06,
+ 0x36, 0x07,
+ 0x37, 0x40,
+ 0x38, 0x86,
+
+ //CMC OFS
+ 0x40, 0x95,
+ 0x41, 0x1f,
+ 0x42, 0x8a,
+ 0x43, 0x86,
+ 0x44, 0x0a,
+ 0x45, 0x84,
+ 0x46, 0x87,
+ 0x47, 0x9b,
+ 0x48, 0x23,
+
+ //CMC POFS
+ 0x50, 0x8c,
+ 0x51, 0x0c,
+ 0x52, 0x00,
+ 0x53, 0x07,
+ 0x54, 0x17,
+ 0x55, 0x9d,
+ 0x56, 0x00,
+ 0x57, 0x0b,
+ 0x58, 0x89,
+
+ 0x80, 0x03,
+ 0x85, 0x40,
+ 0x87, 0x02,
+ 0x88, 0x00,
+ 0x89, 0x00,
+ 0x8a, 0x00,
+
+ 0x03, 0x16,
+ 0x10, 0x31,//GMA_CTL
+ 0x18, 0x5e,//AG_ON
+ 0x19, 0x5d,//AG_OFF
+ 0x1A, 0x0e,//TIME_ON
+ 0x1B, 0x01,//TIME_OFF
+ 0x1C, 0xdc,//OUT_ON
+ 0x1D, 0xfe,//OUT_OFF
+ //GMA
+ 0x30, 0x00,
+ 0x31, 0x09,
+ 0x32, 0x15,
+ 0x33, 0x29,
+ 0x34, 0x53,
+ 0x35, 0x6c,
+ 0x36, 0x81,
+ 0x37, 0x94,
+ 0x38, 0xa4,
+ 0x39, 0xb3,
+ 0x3a, 0xc0,
+ 0x3b, 0xcb,
+ 0x3c, 0xd5,
+ 0x3d, 0xde,
+ 0x3e, 0xe6,
+ 0x3f, 0xee,
+ 0x40, 0xf5,
+ 0x41, 0xfc,
+ 0x42, 0xff,
+ //RGMA
+ 0x50, 0x00,
+ 0x51, 0x09,
+ 0x52, 0x1f,
+ 0x53, 0x37,
+ 0x54, 0x5b,
+ 0x55, 0x76,
+ 0x56, 0x8d,
+ 0x57, 0xa1,
+ 0x58, 0xb2,
+ 0x59, 0xbe,
+ 0x5a, 0xc9,
+ 0x5b, 0xd2,
+ 0x5c, 0xdb,
+ 0x5d, 0xe3,
+ 0x5e, 0xeb,
+ 0x5f, 0xf0,
+ 0x60, 0xf5,
+ 0x61, 0xf7,
+ 0x62, 0xf8,
+ //BGMA
+ 0x70, 0x00,
+ 0x71, 0x08,
+ 0x72, 0x17,
+ 0x73, 0x2f,
+ 0x74, 0x53,
+ 0x75, 0x6c,
+ 0x76, 0x81,
+ 0x77, 0x94,
+ 0x78, 0xa4,
+ 0x79, 0xb3,
+ 0x7a, 0xc0,
+ 0x7b, 0xcb,
+ 0x7c, 0xd5,
+ 0x7d, 0xde,
+ 0x7e, 0xe6,
+ 0x7f, 0xee,
+ 0x80, 0xf4,
+ 0x81, 0xfa,
+ 0x82, 0xff,
+
+ 0x03, 0x17,
+ 0xc4, 0x6e,
+ 0xc5, 0x5c,
+
+
+ 0x03, 0x18,
+ 0x12, 0x20,
+ 0x10, 0x07,
+ 0x11, 0x00,
+ 0x20, 0x02,
+ 0x21, 0x80,
+ 0x22, 0x00,
+ 0x23, 0xf0,
+ 0x24, 0x00,
+ 0x25, 0x06,
+ 0x26, 0x00,
+ 0x27, 0x00,
+ 0x28, 0x02,
+ 0x29, 0x86,
+ 0x2a, 0x00,
+ 0x2b, 0xf0,
+ 0x2c, 0x14,
+ 0x2d, 0x00,
+ 0x2e, 0x14,
+ 0x2f, 0x00,
+ 0x30, 0x65,
+ 0x03, 0x20,
+ 0x10, 0x1c,
+ 0x18, 0x38,
+ 0x20, 0x01,
+ 0x21, 0x30,
+ 0x22, 0x10,
+ 0x23, 0x00,
+ 0x24, 0x04,
+
+ 0x28, 0xff,
+ 0x29, 0xad,
+
+ 0x2a, 0xf0,
+ 0x2b, 0x34,
+ 0x30, 0x78,
+ 0x2c, 0xc3,
+ 0x2d, 0x5f,
+ 0x2e, 0x33,
+ // 0x30, 0xf8,
+ 0x32, 0x03,
+ 0x33, 0x2e,
+ 0x34, 0x30,
+ 0x35, 0xd4,
+ 0x36, 0xfe,
+ 0x37, 0x32,
+ 0x38, 0x04,
+ 0x47, 0xf0,
+
+ //Y_Frame TH
+ 0x50, 0x45,
+ 0x51, 0x88,
+
+ 0x56, 0x10,
+ 0x57, 0xb7,
+ 0x58, 0x14,
+ 0x59, 0x88,
+ 0x5a, 0x04,
+
+ 0x60, 0x55,
+ 0x61, 0x55,
+ 0x62, 0x6a,
+ 0x63, 0xa9,
+ 0x64, 0x6a,
+ 0x65, 0xa9,
+ 0x66, 0x6a,
+ 0x67, 0xa9,
+ 0x68, 0x6b,
+ 0x69, 0xe9,
+ 0x6a, 0x6a,
+ 0x6b, 0xa9,
+ 0x6c, 0x6a,
+ 0x6d, 0xa9,
+ 0x6e, 0x55,
+ 0x6f, 0x55,
+ // 0x70, 0x42,
+ 0x70, 0x42,
+ 0x71, 0xBb,
+
+ // haunting control
+ 0x76, 0x21,
+ 0x77, 0x02,
+ 0x78, 0x22,
+ 0x79, 0x2a,
+
+ 0x78, 0x24,
+ 0x79, 0x23,
+ 0x7a, 0x23,
+ 0x7b, 0x22,
+ 0x7d, 0x23,
+ 0x83, 0x01, //EXP Normal 33.33 fps
+ 0x84, 0x5f,
+ 0x85, 0x90,
+ 0x86, 0x01, //EXPMin 7500.00 fps
+ 0x87, 0xf4,
+ 0x88, 0x05, //EXP Max 8.33 fps
+ 0x89, 0x7e,
+ 0x8a, 0x40,
+ 0x8B, 0x75, //EXP100
+ 0x8C, 0x30,
+ 0x8D, 0x61, //EXP120
+ 0x8E, 0xa8,
+ 0x9c, 0x17, //EXP Limit 500.00 fps
+ 0x9d, 0x70,
+ 0x9e, 0x01, //EXP Unit
+ 0x9f, 0xf4,
+ 0xa0, 0x03,
+ 0xa1, 0xa9,
+ 0xa2, 0x80,
+ 0xb0, 0x1d,
+ 0xb1, 0x1a,
+ 0xb2, 0x80,
+ 0xb3, 0x1a,
+ 0xb4, 0x1a,
+ 0xb5, 0x44,
+ 0xb6, 0x2f,
+ 0xb7, 0x28,
+ 0xb8, 0x25,
+ 0xb9, 0x22,
+ 0xba, 0x21,
+ 0xbb, 0x20,
+ 0xbc, 0x1f,
+ 0xbd, 0x1f,
+ 0xc0, 0x30,
+ 0xc1, 0x20,
+ 0xc2, 0x20,
+ 0xc3, 0x20,
+ 0xc4, 0x08,
+ 0xc8, 0x60,
+ 0xc9, 0x40,
+
+ //Page 22
+ 0x03, 0x22, // Page 22
+ 0x10, 0xfd, // AWBCTL1
+ 0x11, 0x2e, // AWBCTL2
+ 0x19, 0x01,
+ 0x20, 0x30,
+ 0x21, 0x80,
+ 0x23, 0x08,
+ 0x24, 0x01,
+
+ 0x30, 0x80, // ULVL
+ 0x31, 0x80, // VLVL
+ 0x38, 0x11, // UVTH1
+ 0x39, 0x34, // UVTH2
+ 0x40, 0xf7, // YRANGE
+
+ 0x41, 0x77, // CDIFF
+ 0x42, 0x55, // CSUM2
+ 0x43, 0xf0,
+ 0x44, 0x66,
+ 0x45, 0x33,
+ 0x46, 0x01, // WHTPXLTH1
+ 0x47, 0x94,
+
+ 0x50, 0xb2,
+ 0x51, 0x81,
+ 0x52, 0x98,
+
+ 0x80, 0x38, // RGAIN
+ 0x81, 0x20, // GGAIN
+ 0x82, 0x38, // BGAIN
+
+ 0x83, 0x5e, // RMAX
+ 0x84, 0x20, // RMIN
+ 0x85, 0x53, // BMAX
+ 0x86, 0x15, // BMIN
+
+ 0x87, 0x54, // RMAXM
+ 0x88, 0x20, // RMINM
+ 0x89, 0x3f, // BMAXM
+ 0x8a, 0x1c, // BMINM
+
+ 0x8b, 0x54, // RMAXB
+ 0x8c, 0x3f, // RMINB
+ 0x8d, 0x24, // BMAXB
+ 0x8e, 0x1c, // BMINB
+
+ 0x8f, 0x60, // BGAINPARA1
+ 0x90, 0x5f, // BGAINPARA2
+ 0x91, 0x5c, // BGAINPARA3
+ 0x92, 0x4C, // BGAINPARA4
+ 0x93, 0x41, // BGAINPARA5
+ 0x94, 0x3b, // BGAINPARA6
+ 0x95, 0x36, // BGAINPARA7
+ 0x96, 0x30, // BGAINPARA8
+ 0x97, 0x27, // BGAINPARA9
+ 0x98, 0x20, // BGAINPARA10
+ 0x99, 0x1C, // BGAINPARA11
+ 0x9a, 0x19, // BGAINPARA12
+
+ 0x9b, 0x88, // BGAINBND1
+ 0x9c, 0x88, // BGAINBND2
+ 0x9d, 0x48, // RGAINTH1
+ 0x9e, 0x38, // RGAINTH2
+ 0x9f, 0x30, // RGAINTH3
+
+ 0xa0, 0x74, // RDELTA1
+ 0xa1, 0x35, // BDELTA1
+ 0xa2, 0xaf, // RDELTA2
+ 0xa3, 0xf7, // BDELTA2
+
+ 0xa4, 0x10, // AWBEXPLMT1
+ 0xa5, 0x50, // AWBEXPLMT2
+ 0xa6, 0xc4, // AWBEXPLMT3
+
+ 0xad, 0x40,
+ 0xae, 0x4a,
+
+ 0xaf, 0x2a,
+ 0xb0, 0x29,
+
+ 0xb1, 0x20,
+ 0xb4, 0xff,
+ 0xb8, 0x6b,
+ 0xb9, 0x00,
+
+
+ 0x03, 0x24,
+ 0x10, 0x01,
+ 0x18, 0x06,
+ 0x30, 0x06,
+ 0x31, 0x90,
+ 0x32, 0x25,
+ 0x33, 0xa2,
+ 0x34, 0x26,
+ 0x35, 0x58,
+ 0x36, 0x60,
+ 0x37, 0x00,
+ 0x38, 0x50,
+ 0x39, 0x00,
+
+ 0x03, 0x20,
+ 0x10, 0x9c,
+ 0x03, 0x22,
+ 0x10, 0xe9,
+
+ //Page 00
+ 0x03, 0x00,
+ 0x0e, 0x03,
+ 0x0e, 0x73,
+
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+
+ 0x03, 0x00,
+ 0x01, 0xf8,
+
+};
+
+uint8_t hi253_default_regs_init[]={
+
+0x01, 0xf9, //sleep on
+0x08, 0x0f, //Hi-Z on
+0x01, 0xf8, //sleep off
+0x03, 0x00, // Dummy 750us START
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00, // Dummy 750us END
+0x0e, 0x03, //PLL On
+0x0e, 0x73, //PLLx2
+0x03, 0x00, // Dummy 750us START
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00, // Dummy 750us END
+0x0e, 0x00, //PLL off
+0x01, 0xf1, //sleep on
+0x08, 0x00, //Hi-Z off
+0x01, 0xf3,
+0x01, 0xf1,
+// PAGE 20
+0x03, 0x20, //page 20
+0x10, 0x1c, //ae off
+// PAGE 22
+0x03, 0x22, //page 22
+0x10, 0x69, //awb off
+/////// PAGE 0 START ///////
+0x03, 0x00,
+0x10, 0x10, // Sub1/2_Preview2 Mode_H binning
+0x11, 0x90,
+0x12, 0x04,//00
+0x0b, 0xaa, // ESD Check Register
+0x0c, 0xaa, // ESD Check Register
+0x0d, 0xaa, // ESD Check Register
+0x20, 0x00, // Windowing start point Y
+0x21, 0x04,
+0x22, 0x00, // Windowing start point X
+0x23, 0x07,
+0x24, 0x04,
+0x25, 0xb0,
+0x26, 0x06,
+0x27, 0x40, // WINROW END
+0x40, 0x01, //Hblank 408
+0x41, 0x68,
+0x42, 0x00, //Vblank 20
+0x43, 0x14,
+0x45, 0x04,
+0x46, 0x18,
+0x47, 0xd8,
+//BLC
+0x80, 0x2e,
+0x81, 0x7e,
+0x82, 0x90,
+0x83, 0x00,
+0x84, 0x0c,
+0x85, 0x00,
+0x90, 0x0e, //BLC_TIME_TH_ON
+0x91, 0x0e, //BLC_TIME_TH_OFF
+0x92, 0x78, //BLC_AG_TH_ON
+0x93, 0x70, //BLC_AG_TH_OFF
+0x94, 0x75,
+0x95, 0x70,
+0x96, 0xdc,
+0x97, 0xfe,
+0x98, 0x38,
+//OutDoor BLC
+0x99, 0x42,
+0x9a, 0x42,
+0x9b, 0x42,
+0x9c, 0x42,
+//Dark BLC
+0xa0, 0x00,
+0xa2, 0x00,
+0xa4, 0x00,
+0xa6, 0x00,
+//Normal BLC
+0xa8, 0x42,
+0xaa, 0x42,
+0xac, 0x42,
+0xae, 0x42,
+/////// PAGE 2 START ///////
+0x03, 0x02, //Page 02
+0x10, 0x00, //Mode_test
+0x11, 0x00, //Mode_dead_test
+0x12, 0x03, //pwr_ctl_ctl1
+0x13, 0x03, //Mode_ana_test
+0x14, 0x00, //mode_memory
+0x16, 0x00, //dcdc_ctl1
+0x17, 0x8c, //dcdc_ctl2
+0x18, 0x4C, //analog_func1
+0x19, 0x00, //analog_func2
+0x1a, 0x39, //analog_func3
+0x1b, 0x00, //analog_func4
+0x1c, 0x09, //dcdc_ctl3
+0x1d, 0x40, //dcdc_ctl4
+0x1e, 0x30, //analog_func7
+0x1f, 0x10, //analog_func8
+0x20, 0x77, //pixel bias
+0x21, 0xde, //adc,asp bias
+0x22, 0xa7, //main,bus bias
+0x23, 0x30, //clamp
+0x24, 0x4a,
+0x25, 0x10,
+0x27, 0x3c,
+0x28, 0x00,
+0x29, 0x0c,
+0x2a, 0x80,
+0x2b, 0x80,
+0x2c, 0x02,
+0x2d, 0xa0,
+0x2e, 0x11,
+0x2f, 0xa1,
+0x30, 0x05, //swap_ctl
+0x31, 0x99,
+0x32, 0x00,
+0x33, 0x00,
+0x34, 0x22,
+0x38, 0x88,
+0x39, 0x88,
+0x50, 0x20,
+0x51, 0x03, //00 don't touch
+0x52, 0x01,
+0x53, 0xc1,
+0x54, 0x10,
+0x55, 0x1c,
+0x56, 0x11,
+0x58, 0x10,
+0x59, 0x0e,
+0x5d, 0xa2,
+0x5e, 0x5a,
+0x60, 0x87,
+0x61, 0x99,
+0x62, 0x88,
+0x63, 0x97,
+0x64, 0x88,
+0x65, 0x97,
+0x67, 0x0c,
+0x68, 0x0c,
+0x69, 0x0c,
+0x6a, 0xb4,
+0x6b, 0xc4,
+0x6c, 0xb5,
+0x6d, 0xc2,
+0x6e, 0xb5,
+0x6f, 0xc0,
+0x70, 0xb6,
+0x71, 0xb8,
+0x72, 0x89,
+0x73, 0x96,
+0x74, 0x89,
+0x75, 0x96,
+0x76, 0x89,
+0x77, 0x96,
+0x7c, 0x85,
+0x7d, 0xaf,
+0x80, 0x01,
+0x81, 0x7f,
+0x82, 0x13,
+0x83, 0x24,
+0x84, 0x7d,
+0x85, 0x81,
+0x86, 0x7d,
+0x87, 0x81,
+0x88, 0xab,
+0x89, 0xbc,
+0x8a, 0xac,
+0x8b, 0xba,
+0x8c, 0xad,
+0x8d, 0xb8,
+0x8e, 0xae,
+0x8f, 0xb2,
+0x90, 0xb3,
+0x91, 0xb7,
+0x92, 0x48,
+0x93, 0x54,
+0x94, 0x7d,
+0x95, 0x81,
+0x96, 0x7d,
+0x97, 0x81,
+0xa0, 0x02,
+0xa1, 0x7b,
+0xa2, 0x02,
+0xa3, 0x7b,
+0xa4, 0x7b,
+0xa5, 0x02,
+0xa6, 0x7b,
+0xa7, 0x02,
+0xa8, 0x85,
+0xa9, 0x8c,
+0xaa, 0x85,
+0xab, 0x8c,
+0xac, 0x10,
+0xad, 0x16,
+0xae, 0x10,
+0xaf, 0x16,
+0xb0, 0x99,
+0xb1, 0xa3,
+0xb2, 0xa4,
+0xb3, 0xae,
+0xb4, 0x9b,
+0xb5, 0xa2,
+0xb6, 0xa6,
+0xb7, 0xac,
+0xb8, 0x9b,
+0xb9, 0x9f,
+0xba, 0xa6,
+0xbb, 0xaa,
+0xbc, 0x9b,
+0xbd, 0x9f,
+0xbe, 0xa6,
+0xbf, 0xaa,
+0xc4, 0x2c,
+0xc5, 0x43,
+0xc6, 0x63,
+0xc7, 0x79,
+0xc8, 0x2d,
+0xc9, 0x42,
+0xca, 0x2d,
+0xcb, 0x42,
+0xcc, 0x64,
+0xcd, 0x78,
+0xce, 0x64,
+0xcf, 0x78,
+0xd0, 0x0a,
+0xd1, 0x09,
+0xd2, 0x20,
+0xd3, 0x00,
+0xd4, 0x0e, //DCDC_TIME_TH_ON don't touch
+0xd5, 0x0e, //DCDC_TIME_TH_OFF don't touch
+0xd6, 0x78, //DCDC_AG_TH_ON 78
+0xd7, 0x70, //DCDC_AG_TH_OFF 70
+0xe0, 0xc4,
+0xe1, 0xc4,
+0xe2, 0xc4,
+0xe3, 0xc4,
+0xe4, 0x00,
+0xe8, 0x80,
+0xe9, 0x40,
+0xea, 0x7f,
+0xf0, 0x01,
+0xf1, 0x01,
+0xf2, 0x01,
+0xf3, 0x01,
+0xf4, 0x01,
+/////// PAGE 3 ///////
+0x03, 0x03,
+0x10, 0x10,
+/////// PAGE 10 START ///////
+0x03, 0x10,
+0x10, 0x03, // CrYCbY // For Demoset 0x03
+0x12, 0x30,
+0x13, 0x0a,//add jacky open constrast
+0x20, 0x00,
+0x30, 0x00,
+0x31, 0x00,
+0x32, 0x00,
+0x33, 0x00,
+0x34, 0x30,
+0x35, 0x00,
+0x36, 0x00,
+0x38, 0x00,
+0x3e, 0x58,
+0x3f, 0x00,
+0x40, 0x85, // YOFS
+0x41, 0x00, // DYOFS
+0x48, 0x95,// 84
+0x60, 0x67,//6b AG ratio jacky
+
+0x61, 0x75, //7e //8e //88 //70
+0x62, 0x7e, //7e //8e //88 //80
+0x63, 0x50, //Double_AG 50->30
+0x64, 0x41,
+0x66, 0x42,
+0x67, 0x20,
+0x6a, 0x80, //8a
+0x6b, 0x84, //74
+0x6c, 0x80, //7e //7a
+0x6d, 0x80, //8e
+/////// PAGE 11 START ///////
+0x03, 0x11,
+0x10, 0x7f,
+0x11, 0x40,
+0x12, 0x0a, // Blue Max-Filter Delete
+0x13, 0xbb,
+0x26, 0x31, // Double_AG 31->20
+0x27, 0x34, // Double_AG 34->22
+0x28, 0x0f,
+0x29, 0x10,
+0x2b, 0x30,
+0x2c, 0x32,
+//Out2 D-LPF th
+0x30, 0x70,
+0x31, 0x10,
+0x32, 0x58,
+0x33, 0x09,
+0x34, 0x06,
+0x35, 0x03,
+//Out1 D-LPF th
+0x36, 0x70,
+0x37, 0x18,
+0x38, 0x58,
+0x39, 0x09,
+0x3a, 0x06,
+0x3b, 0x03,
+//Indoor D-LPF th
+0x3c, 0x80,
+0x3d, 0x18,
+0x3e, 0x80,
+0x3f, 0x0c,
+0x40, 0x09,
+0x41, 0x06,
+
+0x42, 0x80,
+0x43, 0x18,
+0x44, 0x90,
+0x45, 0x12,
+0x46, 0x10,
+0x47, 0x10,
+0x48, 0x90,
+0x49, 0x40,
+0x4a, 0x80,
+0x4b, 0x13,
+0x4c, 0x10,
+0x4d, 0x11,
+0x4e, 0x80,
+0x4f, 0x30,
+0x50, 0x80,
+0x51, 0x13,
+0x52, 0x10,
+0x53, 0x13,
+0x54, 0x11,
+0x55, 0x17,
+0x56, 0x20,
+0x57, 0x20,
+0x58, 0x20,
+0x59, 0x30,
+0x5a, 0x18,
+0x5b, 0x00,
+0x5c, 0x00,
+0x60, 0x3f,
+0x62, 0x50,
+0x70, 0x06,
+/////// PAGE 12 START ///////
+0x03, 0x12,
+0x20, 0x0f,
+0x21, 0x0f,
+0x25, 0x00, //0x30
+0x28, 0x00,
+0x29, 0x00,
+0x2a, 0x00,
+0x30, 0x50,
+0x31, 0x18,
+0x32, 0x32,
+0x33, 0x40,
+0x34, 0x50,
+0x35, 0x70,
+0x36, 0xa0,
+//Out2 th
+0x40, 0xa0,
+0x41, 0x40,
+0x42, 0xa0,
+0x43, 0x90,
+0x44, 0x90,
+0x45, 0x80,
+//Out1 th
+0x46, 0xb0,
+0x47, 0x55,
+0x48, 0xa0,
+0x49, 0x90,
+0x4a, 0x90,
+0x4b, 0x80,
+//Indoor th
+0x4c, 0xb0,
+0x4d, 0x40,
+0x4e, 0x90,
+0x4f, 0x90,
+0x50, 0xe6,
+0x51, 0x80,
+//Dark1 th
+0x52, 0xb0,
+0x53, 0x60,
+0x54, 0xc0,
+0x55, 0xc0,
+0x56, 0xc0,
+0x57, 0xa0,
+//Dark2 th
+0x58, 0x90,
+0x59, 0x40,
+0x5a, 0xd0,
+0x5b, 0xd0,
+0x5c, 0xe0,
+0x5d, 0xa0,
+
+//Dark3 th
+0x5e, 0x88,
+0x5f, 0x40,
+0x60, 0xe0,
+0x61, 0xe6,
+0x62, 0xe6,
+0x63, 0xb0,
+
+0x70, 0x15,
+0x71, 0x01, //Don't Touch register
+0x72, 0x18,
+0x73, 0x01, //Don't Touch register
+0x74, 0x25,
+0x75, 0x15,
+0x80, 0x20,
+0x81, 0x40,
+0x82, 0x65,
+0x85, 0x1a,
+0x88, 0x00,
+0x89, 0x00,
+0x90, 0x5d, //For capture
+0x91, 0x88,
+0x98, 0x7d,
+0x99, 0x28,
+0x9A, 0x14,
+0x9B, 0xc8,
+0x9C, 0x02,
+0x9D, 0x1e,
+0x9E, 0x28,
+0x9F, 0x07,
+0xA0, 0x32,
+0xA4, 0x04,
+0xA5, 0x0e,
+0xA6, 0x0c,
+0xA7, 0x04,
+0xA8, 0x3c,
+
+0xAA, 0x14,
+0xAB, 0x11,
+0xAC, 0x0f,
+0xAD, 0x16,
+0xAE, 0x15,
+0xAF, 0x14,
+
+0xB1, 0xaa,
+0xB2, 0x96,
+0xB3, 0x28,
+//Dont Touch register
+0xB8, 0x78,
+0xB9, 0xa0,
+0xBA, 0xb4,
+0xBB, 0x14,
+0xBC, 0x14,
+0xBD, 0x14,
+0xBE, 0x64,
+0xBF, 0x64,
+0xC0, 0x64,
+0xC1, 0x64,
+0xC2, 0x04,
+0xC3, 0x03,
+0xC4, 0x0c,
+0xC5, 0x30,
+0xC6, 0x2a,
+0xD0, 0x0c, //CI Option/CI DPC
+0xD1, 0x80,
+0xD2, 0x67,
+0xD3, 0x00,
+0xD4, 0x00,
+0xD5, 0x02,
+0xD6, 0xff,
+0xD7, 0x18,
+//End
+0x3b, 0x06,
+0x3c, 0x06,
+0xc5, 0x00,//55->48
+0xc6, 0x00,//48->40
+/////// PAGE 13 START ///////
+0x03, 0x13,
+//Edge
+0x10, 0xcb,
+0x11, 0x7b,
+0x12, 0x07,
+0x14, 0x00,
+0x20, 0x15,
+0x21, 0x13,
+0x22, 0x33,
+0x23, 0x04,
+0x24, 0x09,
+0x25, 0x08,
+0x26, 0x18,
+0x27, 0x30,
+0x29, 0x12,
+0x2a, 0x50,
+
+//Low clip th
+0x2b, 0x06,
+0x2c, 0x06,
+0x25, 0x08,
+0x2d, 0x0c,
+0x2e, 0x12,
+0x2f, 0x12,
+
+//Out2 Edge
+0x50, 0x10,
+0x51, 0x14,
+0x52, 0x10,
+0x53, 0x0c,
+0x54, 0x0f,
+0x55, 0x0c,
+
+//Out1 Edge
+0x56, 0x10,
+0x57, 0x13,
+0x58, 0x10,
+0x59, 0x0c,
+0x5a, 0x0f,
+0x5b, 0x0c,
+
+//Indoor Edge
+0x5c, 0x0a,
+0x5d, 0x0b,
+0x5e, 0x0a,
+0x5f, 0x08,
+0x60, 0x09,
+0x61, 0x08,
+
+//Dark1 Edge
+0x62, 0x08,
+0x63, 0x08,
+0x64, 0x08,
+0x65, 0x06,
+0x66, 0x06,
+0x67, 0x06,
+
+//Dark2 Edge
+0x68, 0x07,
+0x69, 0x07,
+0x6a, 0x07,
+0x6b, 0x05,
+0x6c, 0x05,
+0x6d, 0x05,
+//Dark3 Edge
+0x6e, 0x07,
+0x6f, 0x07,
+0x70, 0x07,
+0x71, 0x05,
+0x72, 0x05,
+0x73, 0x05,
+0x80, 0xfd,
+0x81, 0x1f,
+0x82, 0x05,
+0x83, 0x01,
+0x90, 0x15,
+0x91, 0x15,
+0x92, 0x33,
+0x93, 0x30,
+0x94, 0x03,
+0x95, 0x14,
+0x97, 0x30,
+0x99, 0x30,
+
+0xa0, 0x04,
+0xa1, 0x05,
+0xa2, 0x04,
+0xa3, 0x05,
+0xa4, 0x07,
+0xa5, 0x08,
+0xa6, 0x07,
+0xa7, 0x08,
+0xa8, 0x07,
+0xa9, 0x08,
+0xaa, 0x07,
+0xab, 0x08,
+
+//Out2
+0xb0, 0x22,
+0xb1, 0x2a,
+0xb2, 0x28,
+0xb3, 0x22,
+0xb4, 0x2a,
+0xb5, 0x28,
+
+//Out1
+0xb6, 0x22,
+0xb7, 0x2a,
+0xb8, 0x28,
+0xb9, 0x22,
+0xba, 0x2a,
+0xbb, 0x28,
+
+//Indoor
+0xbc, 0x17,
+0xbd, 0x17,
+0xbe, 0x17,
+0xbf, 0x07,
+0xc0, 0x07,
+0xc1, 0x07,
+
+//Dark1
+0xc2, 0x0e,
+0xc3, 0x02,
+0xc4, 0x00,
+0xc5, 0x0e,
+0xc6, 0x02,
+0xc7, 0x00,
+
+//Dark2
+0xc8, 0x18,
+0xc9, 0x05,
+0xca, 0x05,
+0xcb, 0x18,
+0xcc, 0x05,
+0xcd, 0x05,
+
+//Dark3
+0xce, 0x18,
+0xcf, 0x05,
+0xd0, 0x05,
+0xd1, 0x18,
+0xd2, 0x05,
+0xd3, 0x05,
+
+/////// PAGE 14 START ///////
+0x03, 0x14,
+0x10, 0x11,
+0x14, 0x80, // GX
+0x15, 0x80, // GY
+0x16, 0x80, // RX
+0x17, 0x80, // RY
+0x18, 0x80, // BX
+0x19, 0x80, // BY
+0x20, 0x80, //X 60 //a0
+0x21, 0x80, //Y
+0x22, 0x78,
+0x23, 0x80,
+0x24, 0x80,
+0x30, 0xc8,
+0x31, 0x2b,
+0x32, 0x00,
+0x33, 0x00,
+0x34, 0x90,
+0x40, 0x56, //0x48//65
+0x50, 0x44, //0x34
+0x60, 0x46, //0x29
+0x70, 0x44, //0x34
+/////// PAGE 15 START ///////
+0x03, 0x15,
+0x10, 0x0f,
+//Rstep H 16
+//Rstep L 14
+0x14, 0x4f, //CMCOFSGH
+0x15, 0x32, //CMCOFSGM
+0x16, 0x24, //CMCOFSGL
+0x17, 0x2f, //CMC SIGN
+//CMC
+0x30, 0x91,
+0x31, 0x6a,
+0x32, 0x18,
+0x33, 0x17,
+0x34, 0x53,
+0x35, 0x08,
+0x36, 0x08,
+0x37, 0x3e,
+0x38, 0x87,
+//CMC OFS
+0x40, 0xa3,
+0x41, 0x3a,
+0x42, 0x93,
+0x43, 0x0b,
+0x44, 0x86,
+0x45, 0x06,
+0x46, 0x00,
+0x47, 0xa2,
+0x48, 0x22,
+//CMC POFS
+0x50, 0x82,
+0x51, 0x06,
+0x52, 0x83,
+0x53, 0x03,
+0x54, 0x11,
+0x55, 0x8e,
+0x56, 0x00,
+0x57, 0x06,
+0x58, 0x87,
+
+0x80, 0x03,
+0x85, 0x40,
+0x87, 0x02,
+0x88, 0x00,
+0x89, 0x00,
+0x8a, 0x00,
+
+/////// PAGE 16 START ///////
+0x03, 0x16,
+0x10, 0x31,//GMA_CTL
+0x18, 0x37,//AG_ON
+0x19, 0x36,//AG_OFF
+0x1A, 0x0e,//TIME_ON
+0x1B, 0x01,//TIME_OFF
+0x1C, 0xdc,//OUT_ON
+0x1D, 0xfe,//OUT_OFF
+//GMA
+0x30, 0x00,
+0x31, 0x08,
+0x32, 0x1f,
+0x33, 0x35,
+0x34, 0x55,
+0x35, 0x73,
+0x36, 0x86,
+0x37, 0x96,
+0x38, 0xa6,
+0x39, 0xb6,
+0x3a, 0xc5,
+0x3b, 0xd0,
+0x3c, 0xd8,
+0x3d, 0xe0,
+0x3e, 0xe8,
+0x3f, 0xf0,
+0x40, 0xf7,
+0x41, 0xfd,
+0x42, 0xff,
+//RGMA
+0x50, 0x00,
+0x51, 0x05,
+0x52, 0x1b,
+0x53, 0x36,
+0x54, 0x5a,
+0x55, 0x75,
+0x56, 0x89,
+0x57, 0x9c,
+0x58, 0xac,
+0x59, 0xb8,
+0x5a, 0xc7,
+0x5b, 0xd2,
+0x5c, 0xdc,
+0x5d, 0xe5,
+0x5e, 0xed,
+0x5f, 0xf2,
+0x60, 0xf7,
+0x61, 0xf9,
+0x62, 0xfa,
+//BGMA
+0x70, 0x00,
+0x71, 0x0a,
+0x72, 0x19,
+0x73, 0x31,
+0x74, 0x55,
+0x75, 0x6e,
+0x76, 0x83,
+0x77, 0x96,
+0x78, 0xa6,
+0x79, 0xb5,
+0x7a, 0xc2,
+0x7b, 0xcd,
+0x7c, 0xd7,
+0x7d, 0xe0,
+0x7e, 0xe8,
+0x7f, 0xf0,
+0x80, 0xf6,
+0x81, 0xfc,
+0x82, 0xff,
+/////// PAGE 17 START ///////
+0x03, 0x17,
+0x10, 0xf7,
+/////// PAGE 18 START ///////
+
+ 0x03, 0x18,
+ 0x10, 0x00,
+
+/////// PAGE 20 START ///////
+0x03, 0x20,
+0x11, 0x1c,
+0x18, 0x30,
+0x1a, 0x08,
+0x20, 0x01, //05_lowtemp Y Mean off
+0x21, 0x30,
+0x22, 0x10,
+0x23, 0x00,
+0x24, 0x00, //Uniform Scene Off
+0x28, 0xe7,
+0x29, 0x0d, //20100305 ad->0d
+0x2a, 0xff,
+0x2b, 0x34, //f4->Adaptive on //04 off
+0x2c, 0xc2,
+0x2d, 0xcf, //fe->AE Speed option
+0x2e, 0x33,
+0x30, 0x78, //f8
+0x32, 0x03,
+0x33, 0x2e,
+0x34, 0x30,
+0x35, 0xd4,
+0x36, 0xfe,
+0x37, 0x32,
+0x38, 0x04,
+0x39, 0x22, //AE_escapeC10
+0x3a, 0xde, //AE_escapeC11
+0x3b, 0x22, //AE_escapeC1
+0x3c, 0xde, //AE_escapeC2
+0x50, 0x45,
+0x51, 0x88,
+0x56, 0x03,
+0x57, 0xf7,
+0x58, 0x14,
+0x59, 0x88,
+0x5a, 0x04,
+/*
+0x60, 0xff,
+0x61, 0xff,
+0x62, 0xea,
+0x63, 0xab,
+0x64, 0xea,
+0x65, 0xab,
+0x66, 0xea,//eb
+0x67, 0x2b,//eb
+0x68, 0xe8,//eb
+0x69, 0x2b,//eb
+0x6a, 0xea,
+0x6b, 0xab,
+0x6c, 0xea,
+0x6d, 0xab,
+0x6e, 0xff,
+0x6f, 0xff,
+*/
+
+0x60, 0x55,
+0x61, 0x55,
+0x62, 0x6a,
+0x63, 0xa9,
+0x64, 0x6a,
+0x65, 0xa9,
+0x66, 0x6b,//eb
+0x67, 0xe9,//eb
+0x68, 0x6b,//eb
+0x69, 0xe9,//eb
+0x6a, 0x6a,
+0x6b, 0xa9,
+0x6c, 0x6a,
+0x6d, 0xa9,
+0x6e, 0x55,
+0x6f, 0x55,
+
+0x70, 0x70, //6e
+0x71, 0x89, //00 //-4
+0x76, 0x43,
+0x77, 0xe2, //04 //f2
+0x78, 0x23, //Yth1
+0x79, 0x46, //Yth2 //46
+0x7a, 0x23, //23
+0x7b, 0x22, //22
+0x7d, 0x23,
+0x83, 0x01, //EXP Normal 33.33 fps
+0x84, 0x5f,
+0x85, 0x90,
+0x86, 0x01, //EXPMin 7500.00 fps
+0x87, 0xf4,
+0x88, 0x06, //EXP Max 8.33 fps
+0x89, 0x68,
+0x8a, 0xa0,
+0x8B, 0x75, //EXP100
+0x8C, 0x30,
+0x8D, 0x61, //EXP120
+0x8E, 0xa8,
+0x9c, 0x17, //EXP Limit 500.00 fps
+0x9d, 0x70,
+0x9e, 0x01, //EXP Unit
+0x9f, 0xf4,
+0xa3, 0x00, //Outdoor Int
+0xa4, 0x61,
+
+0xb0, 0x18,
+0xb1, 0x14, //ADC 400->560
+0xb2, 0x80,//a0
+0xb3, 0x18,
+0xb4, 0x1a,
+0xb5, 0x44,
+0xb6, 0x2f,
+0xb7, 0x28,
+0xb8, 0x25,
+0xb9, 0x22,
+0xba, 0x21,
+0xbb, 0x20,
+0xbc, 0x1f,
+0xbd, 0x1f,
+0xc0, 0x14,
+0xc1, 0x1f,
+0xc2, 0x1f,
+0xc3, 0x18, //2b
+0xc4, 0x10, //08
+0xc8, 0x80,
+0xc9, 0x40,
+/////// PAGE 22 START ///////
+0x03, 0x22,
+0x10, 0xfd,
+0x11, 0x2e,
+0x19, 0x01,
+0x20, 0x30,
+0x21, 0x80,
+0x24, 0x01,
+0x30, 0x80,
+0x31, 0x80,
+0x38, 0x11,
+0x39, 0x34,
+0x40, 0xf7,
+0x41, 0x55,
+0x42, 0x33, //43
+
+0x43, 0xf6,
+0x44, 0x55, //44
+0x45, 0x44, //33
+
+0x46, 0x00,
+0x50, 0xb2,
+0x51, 0x81,
+0x52, 0x98,
+0x80, 0x38,//40
+0x81, 0x20,
+0x82, 0x30,
+
+0x83, 0x52,
+0x84, 0x16,//1E
+0x85, 0x5A,//52
+0x86, 0x23,
+
+0x87, 0x45,
+0x88, 0x35,
+0x89, 0x47,
+0x8a, 0x2b,
+
+0x8b, 0x40,
+0x8c, 0x33,
+0x8d, 0x38,
+0x8e, 0x30,
+
+0x8f, 0x53,
+0x90, 0x52,
+0x91, 0x50,
+0x92, 0x4c,
+0x93, 0x43,
+0x94, 0x37,
+0x95, 0x2f,//2a
+0x96, 0x26,//24
+0x97, 0x21,//20
+0x98, 0x1e,
+0x99, 0x1f,
+0x9a, 0x20,
+0x9b, 0x88,
+0x9c, 0x88,
+0x9d, 0x48,
+0x9e, 0x38,
+0x9f, 0x30,
+0xa0, 0x60,
+0xa1, 0x34,
+0xa2, 0x7f,
+0xa3, 0xff,
+0xa4, 0x14,
+0xa5, 0x2c,
+0xa6, 0xcf,
+0xad, 0x40,
+0xae, 0x4a,
+0xaf, 0x28,
+0xb0, 0x26,
+0xb1, 0x00,
+0xb8, 0xa0,
+0xb9, 0x00,
+// PAGE 20
+0x03, 0x20, //page 20
+0x10, 0x9c, //ae off
+// PAGE 22
+0x03, 0x22, //page 22
+0x10, 0xe9, //awb off
+// PAGE 0
+0x03, 0x00,
+0x0e, 0x03, //PLL On
+0x0e, 0x73, //PLLx2
+0x03, 0x00, // Dummy 750us
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+ 0x03, 0x00,
+ 0x01, 0xf8,
+
+};
+
+#endif
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/hi253/hi253-jfs0672-clw-gt7305.h b/drivers/media/video/wmt_v4l2/sensors/hi253/hi253-jfs0672-clw-gt7305.h
new file mode 100755
index 00000000..a53d3783
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/hi253/hi253-jfs0672-clw-gt7305.h
@@ -0,0 +1,2159 @@
+#ifndef YACD511_H
+#define YACD511_H
+
+// Scene Mode
+uint8_t hi253_scene_mode_auto[] = {
+ 0x03,0x10,
+ 0x41,0x08,
+ 0x03,0x20,
+ 0xb2,0x80,
+ 0xff,0xff,
+};
+
+uint8_t hi253_scene_mode_night[] = {
+ 0x03,0x10,
+ 0x41,0x15,
+ 0x03,0x20,
+ 0xb2,0xf0,
+ 0xff,0xff,
+};
+
+
+// White Balance
+uint8_t hi253_wb_auto [] = {
+ 0x03,0x22,
+ 0x11,0x2e,
+ 0x83,0x53, // RMAX
+ 0x84, 0x16, // RMIN
+ 0x85, 0x5a, // BMAX
+ 0x86, 0x25, // BMIN
+ 0xff,0xff,
+};
+
+uint8_t hi253_wb_incandescent [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x2a,
+ 0x82,0x3f,
+ 0x83,0x35,
+ 0x84,0x28,
+ 0x85,0x45,
+ 0x86,0x3b,
+ 0xff,0xff,
+};
+
+uint8_t hi253_wb_fluorescent [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x20,
+ 0x82,0x4d,
+ 0x83,0x25,
+ 0x84,0x1b,
+ 0x85,0x55,
+ 0x86,0x48,
+ 0xff,0xff,
+};
+
+uint8_t hi253_wb_daylight [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x3d,
+ 0x82,0x2e,
+ 0x83,0x40,
+ 0x84,0x33,
+ 0x85,0x33,
+ 0x86,0x28,
+ 0xff,0xff,
+};
+
+uint8_t hi253_wb_cloudy [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x50,
+ 0x82,0x25,
+ 0x83,0x55,
+ 0x84,0x4b,
+ 0x85,0x28,
+ 0x86,0x20,
+ 0xff,0xff,
+};
+
+
+// Exposure
+uint8_t hi253_exposure_neg6[] = {
+ 0x03,0x10,
+ 0x40,0xa8,
+};
+
+uint8_t hi253_exposure_neg3[] = {
+ 0x03,0x10,
+ 0x40,0x98,
+};
+
+uint8_t hi253_exposure_zero[] = {
+ 0x03,0x10,
+ 0x40,0x80,
+};
+
+uint8_t hi253_exposure_pos3[] = {
+ 0x03,0x10,
+ 0x40,0x08,
+};
+
+uint8_t hi253_exposure_pos6[] = {
+ 0x03,0x10,
+ 0x40,0x28,
+};
+
+
+// Resolution
+uint8_t hi253_1600_1200_rets[]={
+ 0x03, 0x00,
+ 0x10, 0x00,
+ 0x12, 0x04,
+ 0x20, 0x00,
+ 0x21, 0x0a,
+ 0x22, 0x00,
+ 0x23, 0x0a,
+
+ 0x03, 0x18,
+ 0x10, 0x00,
+ 0xff, 0xff,
+};
+
+uint8_t hi253_1280_720_regs[]={
+};
+
+uint8_t hi253_800_600_regs[]={
+
+ 0x03, 0x00,
+ 0x10, 0x10,
+ 0x12, 0x04,
+
+ 0x03, 0x18,
+ 0x10, 0x00,
+ 0xff, 0xff,
+};
+
+uint8_t hi253_640_480_regs[]={
+ 0x03, 0x00,
+ 0x10, 0x10,
+ 0x12, 0x04,
+
+ 0x03, 0x18,
+ 0x12, 0x20,
+ 0x10, 0x07,
+ 0x11, 0x00,
+ 0x20, 0x05,
+ 0x21, 0x00,
+ 0x22, 0x03,
+ 0x23, 0xc0,
+ 0x24, 0x00,
+ 0x25, 0x04,
+ 0x26, 0x00,
+ 0x27, 0x00,
+ 0x28, 0x05,
+ 0x29, 0x04,
+ 0x2a, 0x03,
+ 0x2b, 0xc0,
+ 0x2c, 0x0a,
+ 0x2d, 0x00,
+ 0x2e, 0x0a,
+ 0x2f, 0x00,
+ 0x30, 0x45,
+ 0xff, 0xff,
+};
+
+uint8_t hi253_320_240_regs[]= {
+ 0x01, 0xf9,
+ 0x08, 0x0f,
+ 0x01, 0xf8,
+
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+
+ 0x0e, 0x00,
+
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+
+ 0x0e, 0x00,
+ 0x01, 0xf1,
+ 0x08, 0x00,
+ 0x01, 0xf3,
+ 0x01, 0xf1,
+
+ 0x03, 0x20,
+ 0x10, 0x0c,
+ 0x03, 0x22,
+ 0x10, 0x69,
+
+ //Page 00
+ 0x03, 0x00,
+ 0x10, 0x11, //lxh
+ 0x11, 0x90,
+ 0x12, 0x04,
+ 0x0b, 0xaa,
+ 0x0c, 0xaa,
+ 0x0d, 0xaa,
+ 0x20, 0x00,
+ 0x21, 0x0a, //lxh
+ 0x22, 0x00,
+ 0x23, 0x0a, //lxh
+ 0x24, 0x04,
+ 0x25, 0xb0,
+ 0x26, 0x06,
+ 0x27, 0x40,
+
+ 0x40, 0x00,
+ 0x41, 0xe8,//lxh
+ 0x42, 0x00,
+ 0x43, 0x14,//lxh
+
+ 0x45, 0x04,
+ 0x46, 0x18,
+ 0x47, 0xd8,
+
+ 0xe1, 0x0f,
+
+ //BLC
+ 0x80, 0x2e,
+ 0x81, 0x7e,
+ 0x82, 0x90,
+ 0x83, 0x00,
+ 0x84, 0x0c,
+ 0x85, 0x00,
+ 0x90, 0x0c,
+ 0x91, 0x0c,
+ 0x92, 0x98,
+ 0x93, 0x90,
+ 0x94, 0x75,
+ 0x95, 0x70,
+ 0x96, 0xdc,
+ 0x97, 0xfe,
+ 0x98, 0x20,
+
+ //OutDoor BLC
+ 0x99,0x42,
+ 0x9a,0x42,
+ 0x9b,0x42,
+ 0x9c,0x42,
+
+ //Dark BLC
+ 0xa0, 0x02,
+ 0xa2, 0x02,
+ 0xa4, 0x02,
+ 0xa6, 0x02,
+
+ //Normal BLC
+ 0xa8, 0x42,
+ 0xaa, 0x42,
+ 0xac, 0x42,
+ 0xae, 0x42,
+
+ //Page 02
+ 0x03, 0x02,
+ 0x12, 0x03,
+ 0x13, 0x03,
+ 0x16, 0x00,
+ 0x17, 0x8C,
+ 0x18, 0x4c,
+ 0x19, 0x00,
+ 0x1a, 0x39,
+ 0x1c, 0x09,
+ 0x1d, 0x40,
+ 0x1e, 0x30,
+ 0x1f, 0x10,
+ 0x20, 0x77,
+ 0x21, 0xde,
+ 0x22, 0xa7,
+ 0x23, 0x30,
+ 0x27, 0x3c,
+ 0x2b, 0x80,
+ 0x2e, 0x00,
+ 0x2f, 0x00,
+ 0x30, 0x05,
+ 0x50, 0x20,
+ 0x52, 0x01,
+ 0x53, 0xc1,
+ 0x55, 0x1c,
+ 0x56, 0x11,
+ 0x5d, 0xA2,
+ 0x5e, 0x5a,
+ 0x60, 0x87,
+ 0x61, 0x99,
+ 0x62, 0x88,
+ 0x63, 0x97,
+ 0x64, 0x88,
+ 0x65, 0x97,
+ 0x67, 0x0c,
+ 0x68, 0x0c,
+ 0x69, 0x0c,
+ 0x72, 0x89,
+ 0x73, 0x96,
+ 0x74, 0x89,
+ 0x75, 0x96,
+ 0x76, 0x89,
+ 0x77, 0x96,
+ 0x7C, 0x85,
+ 0x7d, 0xaf,
+ 0x80, 0x01,
+ 0x81, 0x7f,
+ 0x82, 0x13,
+ 0x83, 0x24,
+ 0x84, 0x7d,
+ 0x85, 0x81,
+ 0x86, 0x7d,
+ 0x87, 0x81,
+ 0x92, 0x48,
+ 0x93, 0x54,
+ 0x94, 0x7d,
+ 0x95, 0x81,
+ 0x96, 0x7d,
+ 0x97, 0x81,
+ 0xa0, 0x02,
+ 0xa1, 0x7b,
+ 0xa2, 0x02,
+ 0xa3, 0x7b,
+ 0xa4, 0x7b,
+ 0xa5, 0x02,
+ 0xa6, 0x7b,
+ 0xa7, 0x02,
+ 0xa8, 0x85,
+ 0xa9, 0x8c,
+ 0xaa, 0x85,
+ 0xab, 0x8c,
+ 0xac, 0x10,
+ 0xad, 0x16,
+ 0xae, 0x10,
+ 0xaf, 0x16,
+ 0xb0, 0x99,
+ 0xb1, 0xa3,
+ 0xb2, 0xa4,
+ 0xb3, 0xae,
+ 0xb4, 0x9b,
+ 0xb5, 0xa2,
+ 0xb6, 0xa6,
+ 0xb7, 0xac,
+ 0xb8, 0x9b,
+ 0xb9, 0x9f,
+ 0xba, 0xa6,
+ 0xbb, 0xaa,
+ 0xbc, 0x9b,
+ 0xbd, 0x9f,
+ 0xbe, 0xa6,
+ 0xbf, 0xaa,
+ 0xc4, 0x2c,
+ 0xc5, 0x43,
+ 0xc6, 0x63,
+ 0xc7, 0x79,
+ 0xc8, 0x2d,
+ 0xc9, 0x42,
+ 0xca, 0x2d,
+ 0xcb, 0x42,
+ 0xcc, 0x64,
+ 0xcd, 0x78,
+ 0xce, 0x64,
+ 0xcf, 0x78,
+ 0xd0, 0x0a,
+ 0xd1, 0x09,
+ 0xd4, 0x0c,
+ 0xd5, 0x0c,
+ 0xd6, 0x98,
+ 0xd7, 0x90,//lxh
+ 0xe0, 0xc4,
+ 0xe1, 0xc4,
+ 0xe2, 0xc4,
+ 0xe3, 0xc4,
+ 0xe4, 0x00,
+ 0xe8, 0x80,
+ 0xe9, 0x40,
+ 0xea, 0x7f,
+ 0xf0, 0x01,
+ 0xf1, 0x01,
+ 0xf2, 0x01,
+ 0xf3, 0x01,
+ 0xf4, 0x01,
+
+ //PAGE10
+ 0x03, 0x10,
+ 0x10, 0x03, //lxh
+ 0x11, 0x03, //lxh,normal
+ 0x12, 0x30,
+ 0x13, 0x02,
+ 0x20, 0x00,
+
+ 0x40, 0x88,
+ 0x41, 0x05,
+ 0x48, 0x88,// 84
+ 0x50, 0x90,
+ 0x30, 0x00,
+ 0x31, 0x00,
+ 0x32, 0x00,
+ 0x33, 0x00,
+
+ 0x34, 0x30,
+ 0x35, 0x00,
+ 0x36, 0x00,
+ 0x38, 0x00,
+ 0x3e, 0x58,
+ 0x3f, 0x00,
+
+ //Saturation
+ 0x60, 0x67,
+ 0x61, 0x84,// 74
+ 0x62, 0x80,// 76
+ 0x63, 0x50,
+ 0x64, 0x41,
+
+ 0x66, 0x33,
+ 0x67, 0x00,
+
+ 0x6a, 0x90,
+ 0x6b, 0x80,
+ 0x6c, 0x80,
+ 0x6d, 0xa0,
+
+ 0x76, 0x01,
+ 0x74, 0x66,
+ 0x79, 0x06,
+
+ //Page 11
+ 0x03, 0x11,
+ 0x10, 0x7f,//lxh,3f
+ 0x11, 0x40,
+ 0x12, 0xba,
+ 0x13, 0xcb,
+ 0x26, 0x20,
+ 0x27, 0x22,
+ 0x28, 0x0f,
+ 0x29, 0x10,
+ 0x2b, 0x30,
+ 0x2c, 0x32,
+
+ //Out2 D-LPF th
+ 0x30, 0x70,
+ 0x31, 0x10,
+ 0x32, 0x65,
+ 0x33, 0x09,
+ 0x34, 0x06,
+ 0x35, 0x04,
+
+ //Out1 D-LPF th
+ 0x36, 0x70,
+ 0x37, 0x18,
+ 0x38, 0x65,
+ 0x39, 0x09,
+ 0x3a, 0x06,
+ 0x3b, 0x04,
+
+ //Indoor D-LPF th
+ 0x3c, 0x80,
+ 0x3d, 0x18,
+ 0x3e, 0x80,
+ 0x3f, 0x0c,
+ 0x40, 0x09,
+ 0x41, 0x06,
+
+ 0x42, 0x80,
+ 0x43, 0x18,
+ 0x44, 0x80,
+ 0x45, 0x12,
+ 0x46, 0x10,
+ 0x47, 0x10,
+ 0x48, 0x90,
+ 0x49, 0x40,
+ 0x4a, 0x80,
+ 0x4b, 0x13,
+ 0x4c, 0x10,
+ 0x4d, 0x11,
+ 0x4e, 0x80,
+ 0x4f, 0x30,
+ 0x50, 0x80,
+ 0x51, 0x13,
+ 0x52, 0x10,
+ 0x53, 0x13,
+ 0x54, 0x11,
+ 0x55, 0x17,
+ 0x56, 0x20,
+ 0x57, 0x20,
+ 0x58, 0x20,
+ 0x59, 0x30,
+ 0x5a, 0x18,
+ 0x5b, 0x00,
+ 0x5c, 0x00,
+ 0x60, 0x3f,
+ 0x62, 0x50,
+ 0x70, 0x06,
+
+ //Page 12
+ 0x03, 0x12,
+ 0x20, 0x0f,
+ 0x21, 0x0f,
+ 0x25, 0x30,
+ 0x28, 0x00,
+ 0x29, 0x00,
+ 0x2a, 0x00,
+ 0x30, 0x50,
+ 0x31, 0x18,
+ 0x32, 0x32,
+ 0x33, 0x40,
+ 0x34, 0x50,
+ 0x35, 0x70,
+ 0x36, 0xa0,
+
+ //Out2 th
+ 0x40, 0xa0,
+ 0x41, 0x40,
+ 0x42, 0xa0,
+ 0x43, 0x90,
+ 0x44, 0x90,
+ 0x45, 0x80,
+
+ //Out1 th
+ 0x46, 0xb0,
+ 0x47, 0x55,
+ 0x48, 0xa0,
+ 0x49, 0x90,
+ 0x4a, 0x90,
+ 0x4b, 0x80,
+
+ //In door th
+ 0x4c, 0xb0,
+ 0x4d, 0x40,
+ 0x4e, 0x90,
+ 0x4f, 0x90,
+ 0x50, 0xe6,
+ 0x51, 0x80,
+
+ //Dark1 th
+ 0x52, 0xb0,
+ 0x53, 0x60,
+ 0x54, 0xc0,
+ 0x55, 0xc0,
+ 0x56, 0xc0,
+ 0x57, 0x80,
+
+ //Dark2 th
+ 0x58, 0x90,
+ 0x59, 0x40,
+ 0x5a, 0xd0,
+ 0x5b, 0xd0,
+ 0x5c, 0xe0,
+ 0x5d, 0x80,
+
+ //Dark3 th
+ 0x5e, 0x88,
+ 0x5f, 0x40,
+ 0x60, 0xe0,
+ 0x61, 0xe6,
+ 0x62, 0xe6,
+ 0x63, 0x80,
+
+ 0x70, 0x15,
+ 0x71, 0x01,
+
+ 0x72, 0x18,
+ 0x73, 0x01,
+
+ 0x74, 0x25,
+ 0x75, 0x15,
+ 0x80, 0x30,
+ 0x81, 0x50,
+ 0x82, 0x80,
+ 0x85, 0x1a,
+ 0x88, 0x00,
+ 0x89, 0x00,
+ 0x90, 0x5d,
+
+ 0xc5, 0x30,
+ 0xc6, 0x2a,
+
+ 0xD0, 0x0c,
+ 0xD1, 0x80,
+ 0xD2, 0x67,
+ 0xD3, 0x00,
+ 0xD4, 0x00,
+ 0xD5, 0x02,
+ 0xD6, 0xff,
+ 0xD7, 0x18,
+ 0x3b, 0x06,
+ 0x3c, 0x06,
+
+ 0xc5, 0x30,
+ 0xc6, 0x2a,
+
+ //Page 13
+ 0x03, 0x13,
+ 0x10, 0xcb,
+ 0x11, 0x7b,
+ 0x12, 0x07,
+ 0x14, 0x00,
+
+ 0x20, 0x15,
+ 0x21, 0x13,
+ 0x22, 0x33,
+ 0x23, 0x04,
+ 0x24, 0x09,
+ 0x25, 0x08,
+ 0x26, 0x18,
+ 0x27, 0x30,
+ 0x29, 0x12,
+ 0x2a, 0x50,
+
+ //Low clip th
+ 0x2b, 0x06,
+ 0x2c, 0x06,
+ 0x25, 0x08,
+ 0x2d, 0x0c,
+ 0x2e, 0x12,
+ 0x2f, 0x12,
+
+ //Out2 Edge
+ 0x50, 0x10,
+ 0x51, 0x14,
+ 0x52, 0x10,
+ 0x53, 0x0c,
+ 0x54, 0x0f,
+ 0x55, 0x0c,
+
+ //Out1 Edge
+ 0x56, 0x10,
+ 0x57, 0x13,
+ 0x58, 0x10,
+ 0x59, 0x0c,
+ 0x5a, 0x0f,
+ 0x5b, 0x0c,
+
+ //Indoor Edge
+ 0x5c, 0x0a,
+ 0x5d, 0x0b,
+ 0x5e, 0x0a,
+ 0x5f, 0x08,
+ 0x60, 0x09,
+ 0x61, 0x08,
+
+ //Dark1 Edge
+ 0x62, 0x08,
+ 0x63, 0x08,
+ 0x64, 0x08,
+ 0x65, 0x06,
+ 0x66, 0x06,
+ 0x67, 0x06,
+
+ //Dark2 Edge
+ 0x68, 0x07,
+ 0x69, 0x07,
+ 0x6a, 0x07,
+ 0x6b, 0x05,
+ 0x6c, 0x05,
+ 0x6d, 0x05,
+
+ //Dark3 Edge
+ 0x6e, 0x07,
+ 0x6f, 0x07,
+ 0x70, 0x07,
+ 0x71, 0x05,
+ 0x72, 0x05,
+ 0x73, 0x05,
+
+ //2DY
+ 0x80, 0xfd,
+ 0x81, 0x1f,
+ 0x82, 0x05,
+ 0x83, 0x01,
+
+ 0x90, 0x15,
+ 0x91, 0x15,
+ 0x92, 0x33,
+ 0x93, 0x30,
+ 0x94, 0x03,
+ 0x95, 0x14,
+ 0x97, 0x30,
+ 0x99, 0x30,
+
+ 0xa0, 0x04,
+ 0xa1, 0x05,
+ 0xa2, 0x04,
+ 0xa3, 0x05,
+ 0xa4, 0x07,
+ 0xa5, 0x08,
+ 0xa6, 0x07,
+ 0xa7, 0x08,
+ 0xa8, 0x07,
+ 0xa9, 0x08,
+ 0xaa, 0x07,
+ 0xab, 0x08,
+
+ //Out2
+ 0xb0, 0x22,
+ 0xb1, 0x2a,
+ 0xb2, 0x28,
+ 0xb3, 0x22,
+ 0xb4, 0x2a,
+ 0xb5, 0x28,
+
+ //Out1
+ 0xb6, 0x22,
+ 0xb7, 0x2a,
+ 0xb8, 0x28,
+ 0xb9, 0x22,
+ 0xba, 0x2a,
+ 0xbb, 0x28,
+
+ 0xbc, 0x17,
+ 0xbd, 0x17,
+ 0xbe, 0x17,
+ 0xbf, 0x17,
+ 0xc0, 0x17,
+ 0xc1, 0x17,
+
+ //Dark1
+ 0xc2, 0x1e,
+ 0xc3, 0x12,
+ 0xc4, 0x10,
+ 0xc5, 0x1e,
+ 0xc6, 0x12,
+ 0xc7, 0x10,
+
+ //Dark2
+ 0xc8, 0x18,
+ 0xc9, 0x05,
+ 0xca, 0x05,
+ 0xcb, 0x18,
+ 0xcc, 0x05,
+ 0xcd, 0x05,
+
+ //Dark3
+ 0xce, 0x18,
+ 0xcf, 0x05,
+ 0xd0, 0x05,
+ 0xd1, 0x18,
+ 0xd2, 0x05,
+ 0xd3, 0x05,
+
+ //Page 14
+ 0x03, 0x14,
+ 0x10, 0x11,
+ 0x20, 0x40,
+ 0x21, 0x80,
+ 0x23, 0x80,
+ 0x22, 0x80,
+ 0x23, 0x80,
+ 0x24, 0x80,
+
+ 0x30, 0xc8,
+ 0x31, 0x2b,
+ 0x32, 0x00,
+ 0x33, 0x00,
+ 0x34, 0x90,
+
+ 0x40, 0x40,
+ 0x50, 0x2d,
+ 0x60, 0x28,
+ 0x70, 0x2d,
+
+ //Page 15
+ 0x03, 0x15,
+ 0x10, 0x0f,
+ 0x14, 0x52,
+ 0x15, 0x42,
+ 0x16, 0x32,
+ 0x17, 0x2f,
+
+ //CMC
+ 0x30, 0x8f,
+ 0x31, 0x59,
+ 0x32, 0x0a,
+ 0x33, 0x15,
+ 0x34, 0x5b,
+ 0x35, 0x06,
+ 0x36, 0x07,
+ 0x37, 0x40,
+ 0x38, 0x86,
+
+ //CMC OFS
+ 0x40, 0x95,
+ 0x41, 0x1f,
+ 0x42, 0x8a,
+ 0x43, 0x86,
+ 0x44, 0x0a,
+ 0x45, 0x84,
+ 0x46, 0x87,
+ 0x47, 0x9b,
+ 0x48, 0x23,
+
+ //CMC POFS
+ 0x50, 0x8c,
+ 0x51, 0x0c,
+ 0x52, 0x00,
+ 0x53, 0x07,
+ 0x54, 0x17,
+ 0x55, 0x9d,
+ 0x56, 0x00,
+ 0x57, 0x0b,
+ 0x58, 0x89,
+
+ 0x80, 0x03,
+ 0x85, 0x40,
+ 0x87, 0x02,
+ 0x88, 0x00,
+ 0x89, 0x00,
+ 0x8a, 0x00,
+
+ 0x03, 0x16,
+ 0x10, 0x31,//GMA_CTL
+ 0x18, 0x5e,//AG_ON
+ 0x19, 0x5d,//AG_OFF
+ 0x1A, 0x0e,//TIME_ON
+ 0x1B, 0x01,//TIME_OFF
+ 0x1C, 0xdc,//OUT_ON
+ 0x1D, 0xfe,//OUT_OFF
+ //GMA
+ 0x30, 0x00,
+ 0x31, 0x09,
+ 0x32, 0x15,
+ 0x33, 0x29,
+ 0x34, 0x53,
+ 0x35, 0x6c,
+ 0x36, 0x81,
+ 0x37, 0x94,
+ 0x38, 0xa4,
+ 0x39, 0xb3,
+ 0x3a, 0xc0,
+ 0x3b, 0xcb,
+ 0x3c, 0xd5,
+ 0x3d, 0xde,
+ 0x3e, 0xe6,
+ 0x3f, 0xee,
+ 0x40, 0xf5,
+ 0x41, 0xfc,
+ 0x42, 0xff,
+ //RGMA
+ 0x50, 0x00,
+ 0x51, 0x09,
+ 0x52, 0x1f,
+ 0x53, 0x37,
+ 0x54, 0x5b,
+ 0x55, 0x76,
+ 0x56, 0x8d,
+ 0x57, 0xa1,
+ 0x58, 0xb2,
+ 0x59, 0xbe,
+ 0x5a, 0xc9,
+ 0x5b, 0xd2,
+ 0x5c, 0xdb,
+ 0x5d, 0xe3,
+ 0x5e, 0xeb,
+ 0x5f, 0xf0,
+ 0x60, 0xf5,
+ 0x61, 0xf7,
+ 0x62, 0xf8,
+ //BGMA
+ 0x70, 0x00,
+ 0x71, 0x08,
+ 0x72, 0x17,
+ 0x73, 0x2f,
+ 0x74, 0x53,
+ 0x75, 0x6c,
+ 0x76, 0x81,
+ 0x77, 0x94,
+ 0x78, 0xa4,
+ 0x79, 0xb3,
+ 0x7a, 0xc0,
+ 0x7b, 0xcb,
+ 0x7c, 0xd5,
+ 0x7d, 0xde,
+ 0x7e, 0xe6,
+ 0x7f, 0xee,
+ 0x80, 0xf4,
+ 0x81, 0xfa,
+ 0x82, 0xff,
+
+ 0x03, 0x17,
+ 0xc4, 0x6e,
+ 0xc5, 0x5c,
+
+
+ 0x03, 0x18,
+ 0x12, 0x20,
+ 0x10, 0x07,
+ 0x11, 0x00,
+ 0x20, 0x02,
+ 0x21, 0x80,
+ 0x22, 0x00,
+ 0x23, 0xf0,
+ 0x24, 0x00,
+ 0x25, 0x06,
+ 0x26, 0x00,
+ 0x27, 0x00,
+ 0x28, 0x02,
+ 0x29, 0x86,
+ 0x2a, 0x00,
+ 0x2b, 0xf0,
+ 0x2c, 0x14,
+ 0x2d, 0x00,
+ 0x2e, 0x14,
+ 0x2f, 0x00,
+ 0x30, 0x65,
+ 0x03, 0x20,
+ 0x10, 0x1c,
+ 0x18, 0x38,
+ 0x20, 0x01,
+ 0x21, 0x30,
+ 0x22, 0x10,
+ 0x23, 0x00,
+ 0x24, 0x04,
+
+ 0x28, 0xff,
+ 0x29, 0xad,
+
+ 0x2a, 0xf0,
+ 0x2b, 0x34,
+ 0x30, 0x78,
+ 0x2c, 0xc3,
+ 0x2d, 0x5f,
+ 0x2e, 0x33,
+ // 0x30, 0xf8,
+ 0x32, 0x03,
+ 0x33, 0x2e,
+ 0x34, 0x30,
+ 0x35, 0xd4,
+ 0x36, 0xfe,
+ 0x37, 0x32,
+ 0x38, 0x04,
+ 0x47, 0xf0,
+
+ //Y_Frame TH
+ 0x50, 0x45,
+ 0x51, 0x88,
+
+ 0x56, 0x10,
+ 0x57, 0xb7,
+ 0x58, 0x14,
+ 0x59, 0x88,
+ 0x5a, 0x04,
+
+ 0x60, 0x55,
+ 0x61, 0x55,
+ 0x62, 0x6a,
+ 0x63, 0xa9,
+ 0x64, 0x6a,
+ 0x65, 0xa9,
+ 0x66, 0x6a,
+ 0x67, 0xa9,
+ 0x68, 0x6b,
+ 0x69, 0xe9,
+ 0x6a, 0x6a,
+ 0x6b, 0xa9,
+ 0x6c, 0x6a,
+ 0x6d, 0xa9,
+ 0x6e, 0x55,
+ 0x6f, 0x55,
+ // 0x70, 0x42,
+ 0x70, 0x45,
+ 0x71, 0xBb,
+
+ // haunting control
+ 0x76, 0x21,
+ 0x77, 0x02,
+ 0x78, 0x22,
+ 0x79, 0x2a,
+
+ 0x78, 0x24,
+ 0x79, 0x23,
+ 0x7a, 0x23,
+ 0x7b, 0x22,
+ 0x7d, 0x23,
+ 0x83, 0x01, //EXP Normal 33.33 fps
+ 0x84, 0x5f,
+ 0x85, 0x00,
+ 0x86, 0x01, //EXPMin 6410.26 fps
+ 0x87, 0xd4,
+ 0x88, 0x05, //EXP Max 8.33 fps
+ 0x89, 0x7c,
+ 0x8a, 0x00,
+ 0x8B, 0x75, //EXP100
+ 0x8C, 0x00,
+ 0x8D, 0x60, //EXP120
+ 0x8E, 0xe4,
+
+
+ 0x98, 0xdc,
+ 0x99, 0x45,
+ 0x9a, 0x0d,
+ 0x9b, 0xde,
+ 0x9c, 0x08,
+ 0x9d, 0x0a,
+ 0x9e, 0x01,
+ 0x10, 0x9c,
+ 0x18, 0x30,
+ 0x90, 0x0c,
+ 0x91, 0x0c,
+ 0x92, 0xd8,
+ 0x93, 0xd0,
+
+ 0x9c, 0x0c, //EXP Limit 915.75 fps
+ 0x9d, 0xcc,
+ 0x9e, 0x01, //EXP Unit
+ 0x9f, 0xd4,
+ 0xa0, 0x03,
+ 0xa1, 0xa9,
+ 0xa2, 0x80,
+ 0xb0, 0x1d,
+ 0xb1, 0x1a,
+ 0xb2, 0xb0,
+ 0xb3, 0x1a,
+ 0xb4, 0x1a,
+ 0xb5, 0x44,
+ 0xb6, 0x2f,
+ 0xb7, 0x28,
+ 0xb8, 0x25,
+ 0xb9, 0x22,
+ 0xba, 0x21,
+ 0xbb, 0x20,
+ 0xbc, 0x1f,
+ 0xbd, 0x1f,
+ 0xc0, 0x30,
+ 0xc1, 0x20,
+ 0xc2, 0x20,
+ 0xc3, 0x20,
+ 0xc4, 0x08,
+ 0xc8, 0x60,
+ 0xc9, 0x40,
+
+ //Page 22
+ 0x03, 0x22, // Page 22
+ 0x10, 0xfd, // AWBCTL1
+ 0x11, 0x2e, // AWBCTL2
+ 0x19, 0x01,
+ 0x20, 0x30,
+ 0x21, 0x80,
+ 0x23, 0x08,
+ 0x24, 0x01,
+
+ 0x30, 0x80, // ULVL
+ 0x31, 0x80, // VLVL
+ 0x38, 0x11, // UVTH1
+ 0x39, 0x34, // UVTH2
+ 0x40, 0xf7, // YRANGE
+
+ 0x41, 0x77, // CDIFF
+ 0x42, 0x55, // CSUM2
+ 0x43, 0xf0,
+ 0x44, 0x66,
+ 0x45, 0x33,
+ 0x46, 0x01, // WHTPXLTH1
+ 0x47, 0x94,
+
+ 0x50, 0xb2,
+ 0x51, 0x81,
+ 0x52, 0x98,
+
+ 0x80, 0x38, // RGAIN
+ 0x81, 0x20, // GGAIN
+ 0x82, 0x38, // BGAIN
+
+ 0x83, 0x5e, // RMAX
+ 0x84, 0x20, // RMIN
+ 0x85, 0x53, // BMAX
+ 0x86, 0x15, // BMIN
+
+ 0x87, 0x54, // RMAXM
+ 0x88, 0x20, // RMINM
+ 0x89, 0x3f, // BMAXM
+ 0x8a, 0x1c, // BMINM
+
+ 0x8b, 0x54, // RMAXB
+ 0x8c, 0x3f, // RMINB
+ 0x8d, 0x24, // BMAXB
+ 0x8e, 0x1c, // BMINB
+
+ 0x8f, 0x60, // BGAINPARA1
+ 0x90, 0x5f, // BGAINPARA2
+ 0x91, 0x5c, // BGAINPARA3
+ 0x92, 0x4C, // BGAINPARA4
+ 0x93, 0x41, // BGAINPARA5
+ 0x94, 0x3b, // BGAINPARA6
+ 0x95, 0x36, // BGAINPARA7
+ 0x96, 0x30, // BGAINPARA8
+ 0x97, 0x27, // BGAINPARA9
+ 0x98, 0x20, // BGAINPARA10
+ 0x99, 0x1C, // BGAINPARA11
+ 0x9a, 0x19, // BGAINPARA12
+
+ 0x9b, 0x88, // BGAINBND1
+ 0x9c, 0x88, // BGAINBND2
+ 0x9d, 0x48, // RGAINTH1
+ 0x9e, 0x38, // RGAINTH2
+ 0x9f, 0x30, // RGAINTH3
+
+ 0xa0, 0x74, // RDELTA1
+ 0xa1, 0x35, // BDELTA1
+ 0xa2, 0xaf, // RDELTA2
+ 0xa3, 0xf7, // BDELTA2
+
+ 0xa4, 0x10, // AWBEXPLMT1
+ 0xa5, 0x50, // AWBEXPLMT2
+ 0xa6, 0xc4, // AWBEXPLMT3
+
+ 0xad, 0x40,
+ 0xae, 0x4a,
+
+ 0xaf, 0x2a,
+ 0xb0, 0x29,
+
+ 0xb1, 0x20,
+ 0xb4, 0xff,
+ 0xb8, 0x6b,
+ 0xb9, 0x00,
+
+
+ 0x03, 0x24,
+ 0x10, 0x01,
+ 0x18, 0x06,
+ 0x30, 0x06,
+ 0x31, 0x90,
+ 0x32, 0x25,
+ 0x33, 0xa2,
+ 0x34, 0x26,
+ 0x35, 0x58,
+ 0x36, 0x60,
+ 0x37, 0x00,
+ 0x38, 0x50,
+ 0x39, 0x00,
+
+ 0x03, 0x20,
+ 0x10, 0x9c,
+ 0x03, 0x22,
+ 0x10, 0xe9,
+
+ //Page 00
+ 0x03, 0x00,
+ 0x0e, 0x03,
+ 0x0e, 0x73,
+
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+
+ 0x03, 0x00,
+ 0x01, 0xf8,
+
+};
+
+uint8_t hi253_default_regs_init[]={
+
+0x01, 0xf9, //sleep on
+0x08, 0x0f, //Hi-Z on
+0x01, 0xf8, //sleep off
+0x03, 0x00, // Dummy 750us START
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00, // Dummy 750us END
+0x0e, 0x04, //PLL On
+0x0e, 0x74, //PLLx2
+0x03, 0x00, // Dummy 750us START
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00, // Dummy 750us END
+0x0e, 0x00, //PLL off
+0x01, 0xf1, //sleep on
+0x08, 0x00, //Hi-Z off
+0x01, 0xf3,
+0x01, 0xf1,
+// PAGE 20
+0x03, 0x20, //page 20
+0x10, 0x1c, //ae off
+// PAGE 22
+0x03, 0x22, //page 22
+0x10, 0x69, //awb off
+/////// PAGE 0 START ///////
+0x03, 0x00,
+0x10, 0x10, // Sub1/2_Preview2 Mode_H binning
+0x11, 0x90,
+0x12, 0x04,//00
+0x0b, 0xaa, // ESD Check Register
+0x0c, 0xaa, // ESD Check Register
+0x0d, 0xaa, // ESD Check Register
+0x20, 0x00, // Windowing start point Y
+0x21, 0x04,
+0x22, 0x00, // Windowing start point X
+0x23, 0x07,
+0x24, 0x04,
+0x25, 0xb0,
+0x26, 0x06,
+0x27, 0x40, // WINROW END
+0x40, 0x01, //Hblank 408
+0x41, 0x68,
+0x42, 0x00, //Vblank 20
+0x43, 0x14,
+0x45, 0x04,
+0x46, 0x18,
+0x47, 0xd8,
+//BLC
+0x80, 0x2e,
+0x81, 0x7e,
+0x82, 0x90,
+0x83, 0x00,
+0x84, 0x0c,
+0x85, 0x00,
+0x90, 0x0c, //BLC_TIME_TH_ON
+0x91, 0x0c, //BLC_TIME_TH_OFF
+0x92, 0x78, //BLC_AG_TH_ON
+0x93, 0x70, //BLC_AG_TH_OFF
+0x94, 0x75,
+0x95, 0x70,
+0x96, 0xdc,
+0x97, 0xfe,
+0x98, 0x38,
+//OutDoor BLC
+0x99, 0x42,
+0x9a, 0x42,
+0x9b, 0x42,
+0x9c, 0x42,
+//Dark BLC
+0xa0, 0x08,
+0xa2, 0x08,
+0xa4, 0x08,
+0xa6, 0x08,
+//Normal BLC
+0xa8, 0x40,
+0xaa, 0x40,
+0xac, 0x40,
+0xae, 0x40,
+/////// PAGE 2 START ///////
+0x03, 0x02, //Page 02
+0x10, 0x00, //Mode_test
+0x11, 0x00, //Mode_dead_test
+0x12, 0x03, //pwr_ctl_ctl1
+0x13, 0x03, //Mode_ana_test
+0x14, 0x00, //mode_memory
+0x16, 0x00, //dcdc_ctl1
+0x17, 0x8c, //dcdc_ctl2
+0x18, 0x4C, //analog_func1
+0x19, 0x00, //analog_func2
+0x1a, 0x39, //analog_func3
+0x1b, 0x00, //analog_func4
+0x1c, 0x09, //dcdc_ctl3
+0x1d, 0x40, //dcdc_ctl4
+0x1e, 0x30, //analog_func7
+0x1f, 0x10, //analog_func8
+0x20, 0x77, //pixel bias
+0x21, 0xde, //adc,asp bias
+0x22, 0xa7, //main,bus bias
+0x23, 0x30, //clamp
+0x24, 0x4a,
+0x25, 0x10,
+0x27, 0x3c,
+0x28, 0x00,
+0x29, 0x0c,
+0x2a, 0x80,
+0x2b, 0x80,
+0x2c, 0x02,
+0x2d, 0xa0,
+0x2e, 0x00, //11
+0x2f, 0x00, //a1
+0x30, 0x05, //swap_ctl
+0x31, 0x99,
+0x32, 0x00,
+0x33, 0x00,
+0x34, 0x22,
+0x38, 0x88,
+0x39, 0x88,
+0x50, 0x20,
+0x51, 0x03, //00 don't touch
+0x52, 0x01,
+0x53, 0xc1,
+0x54, 0x10,
+0x55, 0x1c,
+0x56, 0x11,
+0x58, 0x10,
+0x59, 0x0e,
+0x5d, 0xa2,
+0x5e, 0x5a,
+0x60, 0x87,
+0x61, 0x99,
+0x62, 0x88,
+0x63, 0x97,
+0x64, 0x88,
+0x65, 0x97,
+0x67, 0x0c,
+0x68, 0x0c,
+0x69, 0x0c,
+0x6a, 0xb4,
+0x6b, 0xc4,
+0x6c, 0xb5,
+0x6d, 0xc2,
+0x6e, 0xb5,
+0x6f, 0xc0,
+0x70, 0xb6,
+0x71, 0xb8,
+0x72, 0x89,
+0x73, 0x96,
+0x74, 0x89,
+0x75, 0x96,
+0x76, 0x89,
+0x77, 0x96,
+0x7c, 0x85,
+0x7d, 0xaf,
+0x80, 0x01,
+0x81, 0x7f,
+0x82, 0x13,
+0x83, 0x24,
+0x84, 0x7d,
+0x85, 0x81,
+0x86, 0x7d,
+0x87, 0x81,
+0x88, 0xab,
+0x89, 0xbc,
+0x8a, 0xac,
+0x8b, 0xba,
+0x8c, 0xad,
+0x8d, 0xb8,
+0x8e, 0xae,
+0x8f, 0xb2,
+0x90, 0xb3,
+0x91, 0xb7,
+0x92, 0x48,
+0x93, 0x54,
+0x94, 0x7d,
+0x95, 0x81,
+0x96, 0x7d,
+0x97, 0x81,
+0xa0, 0x02,
+0xa1, 0x7b,
+0xa2, 0x02,
+0xa3, 0x7b,
+0xa4, 0x7b,
+0xa5, 0x02,
+0xa6, 0x7b,
+0xa7, 0x02,
+0xa8, 0x85,
+0xa9, 0x8c,
+0xaa, 0x85,
+0xab, 0x8c,
+0xac, 0x10,
+0xad, 0x16,
+0xae, 0x10,
+0xaf, 0x16,
+0xb0, 0x99,
+0xb1, 0xa3,
+0xb2, 0xa4,
+0xb3, 0xae,
+0xb4, 0x9b,
+0xb5, 0xa2,
+0xb6, 0xa6,
+0xb7, 0xac,
+0xb8, 0x9b,
+0xb9, 0x9f,
+0xba, 0xa6,
+0xbb, 0xaa,
+0xbc, 0x9b,
+0xbd, 0x9f,
+0xbe, 0xa6,
+0xbf, 0xaa,
+0xc4, 0x2c,
+0xc5, 0x43,
+0xc6, 0x63,
+0xc7, 0x79,
+0xc8, 0x2d,
+0xc9, 0x42,
+0xca, 0x2d,
+0xcb, 0x42,
+0xcc, 0x64,
+0xcd, 0x78,
+0xce, 0x64,
+0xcf, 0x78,
+0xd0, 0x0a,
+0xd1, 0x09,
+0xd2, 0x20,
+0xd3, 0x00,
+0xd4, 0x0c, //DCDC_TIME_TH_ON don't touch
+0xd5, 0x0c, //DCDC_TIME_TH_OFF don't touch
+0xd6, 0x78, //DCDC_AG_TH_ON 78
+0xd7, 0x70, //DCDC_AG_TH_OFF 70
+0xe0, 0xc4,
+0xe1, 0xc4,
+0xe2, 0xc4,
+0xe3, 0xc4,
+0xe4, 0x00,
+0xe8, 0x80,
+0xe9, 0x40,
+0xea, 0x7f,
+0xf0, 0x01,
+0xf1, 0x01,
+0xf2, 0x01,
+0xf3, 0x01,
+0xf4, 0x01,
+/////// PAGE 3 ///////
+0x03, 0x03,
+0x10, 0x10,
+/////// PAGE 10 START ///////
+0x03, 0x10,
+0x10, 0x03, // CrYCbY // For Demoset 0x03
+0x12, 0x30,
+0x13, 0x0a,//add jacky open constrast
+0x20, 0x00,
+0x30, 0x00,
+0x31, 0x00,
+0x32, 0x00,
+0x33, 0x00,
+0x34, 0x30,
+0x35, 0x00,
+0x36, 0x00,
+0x38, 0x00,
+0x3e, 0x58,
+0x3f, 0x00,
+0x40, 0x80, // YOFS
+0x41, 0x08, // DYOFS
+0x48, 0x7b,// 84
+0x60, 0x67,//6b AG ratio jacky
+
+0x61, 0x64, //7e //8e //88 //70
+0x62, 0x70, //7e //8e //88 //80
+0x63, 0x50, //Double_AG 50->30
+0x64, 0x41,
+0x66, 0x42,
+0x67, 0x20,
+0x6a, 0x80, //8a
+0x6b, 0x84, //74
+0x6c, 0x80, //7e //7a
+0x6d, 0x80, //8e
+/////// PAGE 11 START ///////
+0x03, 0x11,
+0x10, 0x7f,
+0x11, 0x40,
+0x12, 0x0a, // Blue Max-Filter Delete
+0x13, 0xbb,
+0x26, 0x51, // Double_AG 31->20
+0x27, 0x54, // Double_AG 34->22
+0x28, 0x0f,
+0x29, 0x10,
+0x2b, 0x30,
+0x2c, 0x32,
+//Out2 D-LPF th
+0x30, 0x70,
+0x31, 0x10,
+0x32, 0x58,
+0x33, 0x09,
+0x34, 0x06,
+0x35, 0x03,
+//Out1 D-LPF th
+0x36, 0x70,
+0x37, 0x18,
+0x38, 0x58,
+0x39, 0x09,
+0x3a, 0x06,
+0x3b, 0x03,
+//Indoor D-LPF th
+0x3c, 0x80,
+0x3d, 0x18,
+ 0x3e, 0x90,
+0x3f, 0x0c,
+0x40, 0x09,
+0x41, 0x06,
+0x42, 0x80,
+0x43, 0x18,
+ 0x44, 0x90,
+0x45, 0x12,
+0x46, 0x10,
+0x47, 0x10,
+0x48, 0x90,
+0x49, 0x40,
+0x4a, 0x80,
+0x4b, 0x13,
+0x4c, 0x10,
+0x4d, 0x11,
+0x4e, 0x80,
+0x4f, 0x30,
+0x50, 0x80,
+0x51, 0x13,
+0x52, 0x10,
+0x53, 0x13,
+0x54, 0x11,
+0x55, 0x17,
+0x56, 0x20,
+0x57, 0x01,
+0x58, 0x00,
+0x59, 0x00,
+0x5a, 0x1f, //18
+0x5b, 0x00,
+0x5c, 0x00,
+0x60, 0x3f,
+0x62, 0x60,
+0x70, 0x06,
+/////// PAGE 12 START ///////
+0x03, 0x12,
+0x20, 0x0f,
+0x21, 0x0f,
+0x25, 0x00, //0x30
+0x28, 0x00,
+0x29, 0x00,
+0x2a, 0x00,
+0x30, 0x50,
+0x31, 0x18,
+0x32, 0x32,
+0x33, 0x40,
+0x34, 0x50,
+0x35, 0x70,
+0x36, 0xa0,
+//Out2 th
+0x40, 0xa0,
+0x41, 0x40,
+0x42, 0xa0,
+0x43, 0x90,
+0x44, 0x90,
+0x45, 0x80,
+//Out1 th
+0x46, 0xb0,
+0x47, 0x55,
+0x48, 0xa0,
+0x49, 0x90,
+0x4a, 0x90,
+0x4b, 0x80,
+//Indoor th
+0x4c, 0xb0,
+0x4d, 0x40,
+0x4e, 0x90,
+0x4f, 0x90,
+0x50, 0xa0,
+0x51, 0x80,
+//Dark1 th
+0x52, 0x00, //b0
+0x53, 0x50, //60
+0x54, 0xd4, //c0
+0x55, 0xc0, //c0
+0x56, 0x70, //b0
+0x57, 0xec, //70
+//Dark2 th
+0x58, 0x60, //90
+0x59, 0x40, //
+0x5a, 0xd0,
+0x5b, 0xd0,
+0x5c, 0xc0,
+0x5d, 0x9b, //70
+//Dark3 th
+0x5e, 0x70,//88
+0x5f, 0x40,
+0x60, 0xe0,
+0x61, 0xe0,
+0x62, 0xe0,
+0x63, 0xb4,//80
+0x70, 0x15,
+0x71, 0x01, //Don't Touch register
+0x72, 0x18,
+0x73, 0x01, //Don't Touch register
+0x74, 0x25,
+0x75, 0x15,
+0x80, 0x20,
+0x81, 0x40,
+0x82, 0x65,
+0x85, 0x1a,
+0x88, 0x00,
+0x89, 0x00,
+0x90, 0x5d, //For capture
+0x91, 0x88,
+0x98, 0x7d,
+0x99, 0x28,
+0x9A, 0x14,
+0x9B, 0xc8,
+0x9C, 0x02,
+0x9D, 0x1e,
+0x9E, 0x28,
+0x9F, 0x07,
+0xA0, 0x32,
+0xA4, 0x04,
+0xA5, 0x0e,
+0xA6, 0x0c,
+0xA7, 0x04,
+0xA8, 0x3c,
+
+0xAA, 0x14,
+0xAB, 0x11,
+0xAC, 0x0f,
+0xAD, 0x16,
+0xAE, 0x15,
+0xAF, 0x14,
+
+0xB1, 0xaa,
+0xB2, 0x96,
+0xB3, 0x28,
+//Dont Touch register
+0xB8, 0x78,
+0xB9, 0xa0,
+0xBA, 0xb4,
+0xBB, 0x14,
+0xBC, 0x14,
+0xBD, 0x00, //14
+0xBE, 0x64,
+0xBF, 0x64,
+0xC0, 0x64,
+0xC1, 0x64,
+0xC2, 0x04,
+0xC3, 0x03,
+0xC4, 0x0c,
+0xC5, 0x30,
+0xC6, 0x2a,
+0xD0, 0x0c, //CI Option/CI DPC
+0xD1, 0x80,
+0xD2, 0x67,
+0xD3, 0x00,
+0xD4, 0x00,
+0xD5, 0x02,
+0xD6, 0xff,
+0xD7, 0x18,
+//End
+0x3b, 0x06,
+0x3c, 0x06,
+0xc5, 0x00,//55->48
+0xc6, 0x00,//48->40
+/////// PAGE 13 START ///////
+0x03, 0x13,
+//Edge
+0x10, 0xcb,
+0x11, 0x7b,
+0x12, 0x07,
+0x14, 0x00,
+0x20, 0x15,
+0x21, 0x13,
+0x22, 0x33,
+0x23, 0x05,
+0x24, 0x09,
+0x26, 0x18,
+0x27, 0x30,
+0x29, 0x12,
+0x2a, 0x50,
+//Low clip th
+0x2b, 0x01, //Out2 02
+0x2c, 0x01, //Out1 02
+0x25, 0x06,
+0x2d, 0x0c,
+0x2e, 0x12,
+0x2f, 0x12,
+//Out2 Edge
+0x50, 0x18,
+0x51, 0x1c,
+0x52, 0x1b,
+0x53, 0x15,
+0x54, 0x18,
+0x55, 0x15,
+//Out1 Edge
+0x56, 0x18,
+0x57, 0x1c,
+0x58, 0x1b,
+0x59, 0x15,
+0x5a, 0x18,
+0x5b, 0x15,
+//Indoor Edge
+0x5c, 0x0f,//0b
+0x5d, 0x21,//21
+0x5e, 0x1c,//0a
+0x5f, 0x0c,//08
+0x60, 0x2f,//19
+0x61, 0x1c,//08
+//Dark1 Edge
+0x62, 0x08,
+0x63, 0x28,
+0x64, 0x08,
+0x65, 0x06,
+0x66, 0x26,
+0x67, 0x06,
+//Dark2 Edge
+0x68, 0x07,
+0x69, 0x07,
+0x6a, 0x07,
+0x6b, 0x05,
+0x6c, 0x05,
+0x6d, 0x05,
+//Dark3 Edge
+0x6e, 0x07,
+0x6f, 0x07,
+0x70, 0x07,
+0x71, 0x05,
+0x72, 0x05,
+0x73, 0x05,
+0x80, 0xfd,
+0x81, 0x1f,
+0x82, 0x05,
+0x83, 0x31,
+0x90, 0x05,
+0x91, 0x05,
+0x92, 0x33,
+0x93, 0x30,
+0x94, 0x03,
+0x95, 0x14,
+0x97, 0x20,
+0x99, 0x20,
+0xa0, 0x01,
+0xa1, 0x02,
+0xa2, 0x01,
+0xa3, 0x02,
+0xa4, 0x05,
+0xa5, 0x05,
+0xa6, 0x07,
+0xa7, 0x08,
+0xa8, 0x07,
+0xa9, 0x08,
+0xaa, 0x07,
+0xab, 0x08,
+0xb0, 0x22,
+0xb1, 0x2a,
+0xb2, 0x28,
+0xb3, 0x22,
+0xb4, 0x2a,
+0xb5, 0x28,
+0xb6, 0x22,
+0xb7, 0x2a,
+0xb8, 0x28,
+0xb9, 0x22,
+0xba, 0x2a,
+0xbb, 0x28,
+0xbc, 0x25,
+0xbd, 0x2a,
+0xbe, 0x27,
+0xbf, 0x25,
+0xc0, 0x2a,
+0xc1, 0x27,
+0xc2, 0x1e,
+0xc3, 0x24,
+0xc4, 0x20,
+0xc5, 0x1e,
+0xc6, 0x24,
+0xc7, 0x20,
+0xc8, 0x18,
+0xc9, 0x20,
+0xca, 0x1e,
+0xcb, 0x18,
+0xcc, 0x20,
+0xcd, 0x1e,
+0xce, 0x18,
+0xcf, 0x20,
+0xd0, 0x1e,
+0xd1, 0x18,
+0xd2, 0x20,
+0xd3, 0x1e,
+/////// PAGE 14 START ///////
+0x03, 0x14,
+0x10, 0x11,
+0x14, 0x80, // GX
+0x15, 0x80, // GY
+0x16, 0x80, // RX
+0x17, 0x80, // RY
+0x18, 0x80, // BX
+0x19, 0x80, // BY
+0x20, 0x80, //X 60 //a0
+0x21, 0x80, //Y
+0x22, 0x80,
+0x23, 0x80,
+0x24, 0x80,
+0x30, 0xc8,
+0x31, 0x2b,
+0x32, 0x00,
+0x33, 0x00,
+0x34, 0x90,
+0x40, 0x56, //0x48//65
+0x50, 0x44, //0x34
+0x60, 0x46, //0x29
+0x70, 0x44, //0x34
+/////// PAGE 15 START ///////
+0x03, 0x15,
+0x10, 0x0f,
+//Rstep H 16
+//Rstep L 14
+0x14, 0x4f, //CMCOFSGH
+0x15, 0x32, //CMCOFSGM
+0x16, 0x24, //CMCOFSGL
+0x17, 0x2f, //CMC SIGN
+//CMC
+0x30, 0x91,
+0x31, 0x6a,
+0x32, 0x18,
+0x33, 0x17,
+0x34, 0x53,
+0x35, 0x08,
+0x36, 0x08,
+0x37, 0x3e,
+0x38, 0x87,
+//CMC OFS
+0x40, 0xa3,
+0x41, 0x3a,
+0x42, 0x93,
+0x43, 0x0b,
+0x44, 0x86,
+0x45, 0x06,
+0x46, 0x00,
+0x47, 0xa2,
+0x48, 0x22,
+//CMC POFS
+0x50, 0x82,
+0x51, 0x06,
+0x52, 0x83,
+0x53, 0x03,
+0x54, 0x11,
+0x55, 0x8e,
+0x56, 0x00,
+0x57, 0x06,
+0x58, 0x87,
+
+0x80, 0x03,
+0x85, 0x40,
+0x87, 0x02,
+0x88, 0x00,
+0x89, 0x00,
+0x8a, 0x00,
+
+
+
+/////// PAGE 16 START ///////
+0x03, 0x16,
+0x10, 0x31,//GMA_CTL
+0x18, 0x5e,//AG_ON
+0x19, 0x5d,//AG_OFF
+0x1A, 0x0e,//TIME_ON
+0x1B, 0x01,//TIME_OFF
+0x1C, 0xdc,//OUT_ON
+0x1D, 0xfe,//OUT_OFF
+
+//GMA
+0x30, 0x00,
+0x31, 0x06,
+0x32, 0x0e,
+0x33, 0x23,
+0x34, 0x55,
+0x35, 0x7f,
+0x36, 0x99,
+0x37, 0xac,
+0x38, 0xba,
+0x39, 0xc5,
+0x3a, 0xcd,
+0x3b, 0xd4,
+0x3c, 0xdb,
+0x3d, 0xe2,
+0x3e, 0xe7,
+0x3f, 0xec,
+0x40, 0xf3,
+0x41, 0xfa,
+0x42, 0xff,
+//RGMA
+0x50, 0x00,
+0x51, 0x07,
+0x52, 0x12,
+0x53, 0x27,
+0x54, 0x57,
+0x55, 0x7f,
+0x56, 0x99,
+0x57, 0xac,
+0x58, 0xba,
+0x59, 0xc5,
+0x5a, 0xcd,
+0x5b, 0xd4,
+0x5c, 0xdb,
+0x5d, 0xe2,
+0x5e, 0xe7,
+0x5f, 0xec,
+0x60, 0xf3,
+0x61, 0xfa,
+0x62, 0xff,
+//BGMA
+0x70, 0x00,
+0x71, 0x09,
+0x72, 0x16,
+0x73, 0x2c,
+0x74, 0x59,
+0x75, 0x7f,
+0x76, 0x99,
+0x77, 0xac,
+0x78, 0xba,
+0x79, 0xc5,
+0x7a, 0xcd,
+0x7b, 0xd4,
+0x7c, 0xdb,
+0x7d, 0xe2,
+0x7e, 0xe7,
+0x7f, 0xec,
+0x80, 0xf3,
+0x81, 0xfa,
+0x82, 0xff,
+/////// PAGE 17 START ///////
+0x03, 0x17,
+0x10, 0xf7,
+/////// PAGE 18 START ///////
+
+ 0x03, 0x18,
+ 0x12, 0x20,
+ 0x10, 0x07,
+ 0x11, 0x00,
+ 0x20, 0x05,
+ 0x21, 0x00,
+ 0x22, 0x03,
+ 0x23, 0xc0,
+ 0x24, 0x00,
+ 0x25, 0x04,
+ 0x26, 0x00,
+ 0x27, 0x00,
+ 0x28, 0x05,
+ 0x29, 0x04,
+ 0x2a, 0x03,
+ 0x2b, 0xc0,
+ 0x2c, 0x0a,
+ 0x2d, 0x00,
+ 0x2e, 0x0a,
+ 0x2f, 0x00,
+ 0x30, 0x45,
+
+/////// PAGE 20 START ///////
+0x03, 0x20,
+0x11, 0x1c,
+0x18, 0x30,
+0x1a, 0x08,
+0x20, 0x01, //05_lowtemp Y Mean off
+0x21, 0x30,
+0x22, 0x10,
+0x23, 0x00,
+0x24, 0x00, //Uniform Scene Off
+0x28, 0xe7,
+0x29, 0x0d, //20100305 ad->0d
+0x2a, 0xff,
+0x2b, 0x34, //f4->Adaptive on //04 off
+0x2c, 0xc2,
+0x2d, 0xcf, //fe->AE Speed option
+0x2e, 0x33,
+0x30, 0x78, //f8
+0x32, 0x03,
+0x33, 0x2e,
+0x34, 0x30,
+0x35, 0xd4,
+0x36, 0xfe,
+0x37, 0x32,
+0x38, 0x04,
+0x39, 0x22, //AE_escapeC10
+0x3a, 0xde, //AE_escapeC11
+0x3b, 0x22, //AE_escapeC1
+0x3c, 0xde, //AE_escapeC2
+0x50, 0x45,
+0x51, 0x88,
+0x56, 0x03,
+0x57, 0xf7,
+0x58, 0x14,
+0x59, 0x88,
+0x5a, 0x04,
+
+/*
+0x60, 0xff,
+0x61, 0xff,
+0x62, 0xea,
+0x63, 0xab,
+0x64, 0xea,
+0x65, 0xab,
+0x66, 0xea,//eb
+0x67, 0x2b,//eb
+0x68, 0xea,//eb
+0x69, 0x2b,//eb
+0x6a, 0xea,
+0x6b, 0xab,
+0x6c, 0xea,
+0x6d, 0xab,
+0x6e, 0xff,
+0x6f, 0xff,
+*/
+
+0x60, 0x55,
+0x61, 0x55,
+0x62, 0x6a,
+0x63, 0xa9,
+0x64, 0x6a,
+0x65, 0xa9,
+0x66, 0x6b,//eb
+0x67, 0xe9,//eb
+0x68, 0x6b,//eb
+0x69, 0xe9,//eb
+0x6a, 0x6a,
+0x6b, 0xa9,
+0x6c, 0x6a,
+0x6d, 0xa9,
+0x6e, 0x55,
+0x6f, 0x55,
+
+0x70, 0x6e, //6e
+0x71, 0x89, //00 //-4
+0x76, 0x43,
+0x77, 0xe2, //04 //f2
+0x78, 0x23, //Yth1
+0x79, 0x46, //Yth2 //46
+0x7a, 0x23, //23
+0x7b, 0x22, //22
+0x7d, 0x23,
+0x83, 0x03, //EXP Normal 33.33 fps
+0x84, 0x6e,
+0x85, 0xe8,
+0x86, 0x01, //EXPMin 7500.00 fps
+0x87, 0xf4,
+0x88, 0x06, //EXP Max 8.33 fps
+0x89, 0xdd,
+0x8a, 0xd0,
+0x8B, 0x92, //EXP100
+0x8C, 0x7c,
+0x8D, 0x7a, //EXP120
+0x8E, 0x12,
+0x9c, 0x17, //EXP Limit 500.00 fps
+0x9d, 0x70,
+0x9e, 0x01, //EXP Unit
+0x9f, 0xf4,
+0xb0, 0x18,
+0xb1, 0x14, //ADC 400->560
+0xb2, 0x80,//a0
+0xb3, 0x18,
+0xb4, 0x1a,
+0xb5, 0x44,
+0xb6, 0x2f,
+0xb7, 0x28,
+0xb8, 0x25,
+0xb9, 0x22,
+0xba, 0x21,
+0xbb, 0x20,
+0xbc, 0x1f,
+0xbd, 0x1f,
+0xc0, 0x14,
+0xc1, 0x1f,
+0xc2, 0x1f,
+0xc3, 0x18, //2b
+0xc4, 0x10, //08
+0xc8, 0x80,
+0xc9, 0x40,
+/////// PAGE 22 START ///////
+0x03, 0x22,
+0x10, 0xfd,
+0x11, 0x2e,
+0x19, 0x01,
+0x20, 0x30,
+0x21, 0x80,
+0x24, 0x01,
+0x30, 0x80,
+0x31, 0x80,
+0x38, 0x11,
+0x39, 0x34,
+0x40, 0xf7,
+0x41, 0x55,
+0x42, 0x33, //43
+
+0x43, 0xf6,
+0x44, 0x55, //44
+0x45, 0x44, //33
+
+0x46, 0x00,
+0x50, 0xb2,
+0x51, 0x81,
+0x52, 0x98,
+0x80, 0x38,//40
+0x81, 0x20,
+0x82, 0x30,
+
+0x83, 0x53,
+0x84, 0x16,//1E
+0x85, 0x5A,//52
+0x86, 0x25,
+
+0x87, 0x45,
+0x88, 0x35,
+0x89, 0x47,
+0x8a, 0x2b,
+
+0x8b, 0x40,
+0x8c, 0x33,
+0x8d, 0x38,
+0x8e, 0x30,
+
+0x8f, 0x53,
+0x90, 0x52,
+0x91, 0x50,
+0x92, 0x4c,
+0x93, 0x43,
+0x94, 0x37,
+0x95, 0x2f,//2a
+0x96, 0x26,//24
+0x97, 0x21,//20
+0x98, 0x1e,
+0x99, 0x1f,
+0x9a, 0x20,
+0x9b, 0x88,
+0x9c, 0x88,
+0x9d, 0x48,
+0x9e, 0x38,
+0x9f, 0x30,
+0xa0, 0x60,
+0xa1, 0x34,
+0xa2, 0x6f,
+0xa3, 0xff,
+0xa4, 0x14,
+0xa5, 0x2c,
+0xa6, 0xcf,
+0xad, 0x40,
+0xae, 0x4a,
+0xaf, 0x28,
+0xb0, 0x26,
+0xb1, 0x00,
+0xb8, 0xa0,
+0xb9, 0x00,
+// PAGE 20
+0x03, 0x20, //page 20
+0x10, 0x9c, //ae off
+// PAGE 22
+0x03, 0x22, //page 22
+0x10, 0xe9, //awb off
+// PAGE 0
+0x03, 0x00,
+0x0e, 0x04, //PLL On
+0x0e, 0x74, //PLLx2
+0x03, 0x00, // Dummy 750us
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+ 0x03, 0x00,
+ 0x01, 0xf8,
+
+};
+
+#endif
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/hi253/hi253-mrs-gt7305_v1.h b/drivers/media/video/wmt_v4l2/sensors/hi253/hi253-mrs-gt7305_v1.h
new file mode 100755
index 00000000..72c8da76
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/hi253/hi253-mrs-gt7305_v1.h
@@ -0,0 +1,2133 @@
+#ifndef YACD511_H
+#define YACD511_H
+
+// Scene Mode
+uint8_t hi253_scene_mode_auto[] = {
+ 0x03,0x10,
+ 0x41,0x00,
+ 0x03,0x20,
+ 0xb2,0x80,
+ 0xff,0xff,
+};
+
+uint8_t hi253_scene_mode_night[] = {
+ 0x03,0x10,
+ 0x41,0x15,
+ 0x03,0x20,
+ 0xb2,0xb0,
+ 0xff,0xff,
+};
+
+
+// White Balance
+uint8_t hi253_wb_auto [] = {
+ 0x03,0x22,
+ 0x11,0x2e,
+ 0x83,0x60, // RMAX
+ 0x84, 0x16, // RMIN
+ 0x85, 0x60, // BMAX
+ 0x86, 0x20, // BMIN
+ 0xff,0xff,
+};
+
+uint8_t hi253_wb_incandescent [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x2a,
+ 0x82,0x3f,
+ 0x83,0x35,
+ 0x84,0x28,
+ 0x85,0x45,
+ 0x86,0x3b,
+ 0xff,0xff,
+};
+
+uint8_t hi253_wb_fluorescent [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x20,
+ 0x82,0x4d,
+ 0x83,0x25,
+ 0x84,0x1b,
+ 0x85,0x55,
+ 0x86,0x48,
+ 0xff,0xff,
+};
+
+uint8_t hi253_wb_daylight [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x3d,
+ 0x82,0x2e,
+ 0x83,0x40,
+ 0x84,0x33,
+ 0x85,0x33,
+ 0x86,0x28,
+ 0xff,0xff,
+};
+
+uint8_t hi253_wb_cloudy [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x50,
+ 0x82,0x25,
+ 0x83,0x55,
+ 0x84,0x4b,
+ 0x85,0x28,
+ 0x86,0x20,
+ 0xff,0xff,
+};
+
+
+// Exposure
+uint8_t hi253_exposure_neg6[] = {
+ 0x03,0x10,
+ 0x40,0xa8,
+};
+
+uint8_t hi253_exposure_neg3[] = {
+ 0x03,0x10,
+ 0x40,0x98,
+};
+
+uint8_t hi253_exposure_zero[] = {
+ 0x03,0x10,
+ 0x40,0x84,//85
+};
+
+uint8_t hi253_exposure_pos3[] = {
+ 0x03,0x10,
+ 0x40,0x08,
+};
+
+uint8_t hi253_exposure_pos6[] = {
+ 0x03,0x10,
+ 0x40,0x28,
+};
+
+
+// Resolution
+uint8_t hi253_1600_1200_rets[]={
+ 0x03, 0x00,
+ 0x10, 0x00,
+ 0x12, 0x04,
+ 0x03, 0x18,
+ 0x10, 0x00,
+ 0xff, 0xff,
+};
+
+uint8_t hi253_1280_720_regs[]={
+};
+
+uint8_t hi253_800_600_regs[]={
+
+ 0x03, 0x00,
+ 0x10, 0x10,
+ 0x12, 0x04,
+
+ 0x03, 0x18,
+ 0x10, 0x00,
+ 0xff, 0xff,
+};
+
+uint8_t hi253_640_480_regs[]={
+ 0x03, 0x00,
+ 0x10, 0x10,
+ 0x12, 0x04,
+
+ 0x03, 0x18,
+ 0x12, 0x20,
+ 0x10, 0x07,
+ 0x11, 0x00,
+ 0x20, 0x05,
+ 0x21, 0x00,
+ 0x22, 0x03,
+ 0x23, 0xc0,
+ 0x24, 0x00,
+ 0x25, 0x04,
+ 0x26, 0x00,
+ 0x27, 0x00,
+ 0x28, 0x05,
+ 0x29, 0x04,
+ 0x2a, 0x03,
+ 0x2b, 0xc0,
+ 0x2c, 0x0a,
+ 0x2d, 0x00,
+ 0x2e, 0x0a,
+ 0x2f, 0x00,
+ 0x30, 0x45,
+ 0xff, 0xff,
+};
+
+uint8_t hi253_320_240_regs[]= {
+ 0x01, 0xf9,
+ 0x08, 0x0f,
+ 0x01, 0xf8,
+
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+
+ 0x0e, 0x00,
+
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+
+ 0x0e, 0x00,
+ 0x01, 0xf1,
+ 0x08, 0x00,
+ 0x01, 0xf3,
+ 0x01, 0xf1,
+
+ 0x03, 0x20,
+ 0x10, 0x0c,
+ 0x03, 0x22,
+ 0x10, 0x69,
+
+ //Page 00
+ 0x03, 0x00,
+ 0x10, 0x11, //lxh
+ 0x11, 0x90,
+ 0x12, 0x04,
+ 0x0b, 0xaa,
+ 0x0c, 0xaa,
+ 0x0d, 0xaa,
+ 0x20, 0x00,
+ 0x21, 0x0a, //lxh
+ 0x22, 0x00,
+ 0x23, 0x0a, //lxh
+ 0x24, 0x04,
+ 0x25, 0xb0,
+ 0x26, 0x06,
+ 0x27, 0x40,
+
+ 0x40, 0x00,
+ 0x41, 0xe8,//lxh
+ 0x42, 0x00,
+ 0x43, 0x14,//lxh
+
+ 0x45, 0x04,
+ 0x46, 0x18,
+ 0x47, 0xd8,
+
+ 0xe1, 0x0f,
+
+ //BLC
+ 0x80, 0x2e,
+ 0x81, 0x7e,
+ 0x82, 0x90,
+ 0x83, 0x00,
+ 0x84, 0x0c,
+ 0x85, 0x00,
+ 0x90, 0x0c,
+ 0x91, 0x0c,
+ 0x92, 0x98,
+ 0x93, 0x90,
+ 0x94, 0x75,
+ 0x95, 0x70,
+ 0x96, 0xdc,
+ 0x97, 0xfe,
+ 0x98, 0x20,
+
+ //OutDoor BLC
+ 0x99,0x42,
+ 0x9a,0x42,
+ 0x9b,0x42,
+ 0x9c,0x42,
+
+ //Dark BLC
+ 0xa0, 0x42,
+ 0xa2, 0x42,
+ 0xa4, 0x42,
+ 0xa6, 0x42,
+
+ //Normal BLC
+ 0xa8, 0x42,
+ 0xaa, 0x42,
+ 0xac, 0x42,
+ 0xae, 0x42,
+
+ //Page 02
+ 0x03, 0x02,
+ 0x12, 0x03,
+ 0x13, 0x03,
+ 0x16, 0x00,
+ 0x17, 0x8C,
+ 0x18, 0x4c,
+ 0x19, 0x00,
+ 0x1a, 0x39,
+ 0x1c, 0x09,
+ 0x1d, 0x40,
+ 0x1e, 0x30,
+ 0x1f, 0x10,
+ 0x20, 0x77,
+ 0x21, 0xde,
+ 0x22, 0xa7,
+ 0x23, 0x30,
+ 0x27, 0x3c,
+ 0x2b, 0x80,
+ 0x2e, 0x00,
+ 0x2f, 0x00,
+ 0x30, 0x05,
+ 0x50, 0x20,
+ 0x52, 0x01,
+ 0x53, 0xc1,
+ 0x55, 0x1c,
+ 0x56, 0x11,
+ 0x5d, 0xA2,
+ 0x5e, 0x5a,
+ 0x60, 0x87,
+ 0x61, 0x99,
+ 0x62, 0x88,
+ 0x63, 0x97,
+ 0x64, 0x88,
+ 0x65, 0x97,
+ 0x67, 0x0c,
+ 0x68, 0x0c,
+ 0x69, 0x0c,
+ 0x72, 0x89,
+ 0x73, 0x96,
+ 0x74, 0x89,
+ 0x75, 0x96,
+ 0x76, 0x89,
+ 0x77, 0x96,
+ 0x7C, 0x85,
+ 0x7d, 0xaf,
+ 0x80, 0x01,
+ 0x81, 0x7f,
+ 0x82, 0x13,
+ 0x83, 0x24,
+ 0x84, 0x7d,
+ 0x85, 0x81,
+ 0x86, 0x7d,
+ 0x87, 0x81,
+ 0x92, 0x48,
+ 0x93, 0x54,
+ 0x94, 0x7d,
+ 0x95, 0x81,
+ 0x96, 0x7d,
+ 0x97, 0x81,
+ 0xa0, 0x02,
+ 0xa1, 0x7b,
+ 0xa2, 0x02,
+ 0xa3, 0x7b,
+ 0xa4, 0x7b,
+ 0xa5, 0x02,
+ 0xa6, 0x7b,
+ 0xa7, 0x02,
+ 0xa8, 0x85,
+ 0xa9, 0x8c,
+ 0xaa, 0x85,
+ 0xab, 0x8c,
+ 0xac, 0x10,
+ 0xad, 0x16,
+ 0xae, 0x10,
+ 0xaf, 0x16,
+ 0xb0, 0x99,
+ 0xb1, 0xa3,
+ 0xb2, 0xa4,
+ 0xb3, 0xae,
+ 0xb4, 0x9b,
+ 0xb5, 0xa2,
+ 0xb6, 0xa6,
+ 0xb7, 0xac,
+ 0xb8, 0x9b,
+ 0xb9, 0x9f,
+ 0xba, 0xa6,
+ 0xbb, 0xaa,
+ 0xbc, 0x9b,
+ 0xbd, 0x9f,
+ 0xbe, 0xa6,
+ 0xbf, 0xaa,
+ 0xc4, 0x2c,
+ 0xc5, 0x43,
+ 0xc6, 0x63,
+ 0xc7, 0x79,
+ 0xc8, 0x2d,
+ 0xc9, 0x42,
+ 0xca, 0x2d,
+ 0xcb, 0x42,
+ 0xcc, 0x64,
+ 0xcd, 0x78,
+ 0xce, 0x64,
+ 0xcf, 0x78,
+ 0xd0, 0x0a,
+ 0xd1, 0x09,
+ 0xd4, 0x0c,
+ 0xd5, 0x0c,
+ 0xd6, 0x98,
+ 0xd7, 0x90,//lxh
+ 0xe0, 0xc4,
+ 0xe1, 0xc4,
+ 0xe2, 0xc4,
+ 0xe3, 0xc4,
+ 0xe4, 0x00,
+ 0xe8, 0x80,
+ 0xe9, 0x40,
+ 0xea, 0x7f,
+ 0xf0, 0x01,
+ 0xf1, 0x01,
+ 0xf2, 0x01,
+ 0xf3, 0x01,
+ 0xf4, 0x01,
+
+ //PAGE10
+ 0x03, 0x10,
+ 0x10, 0x03, //lxh
+ 0x11, 0x03, //lxh,normal
+ 0x12, 0x30,
+ 0x13, 0x02,
+ 0x20, 0x00,
+
+ 0x40, 0x88,
+ 0x41, 0x05,
+ 0x48, 0x88,// 84
+ 0x50, 0x90,
+ 0x30, 0x00,
+ 0x31, 0x00,
+ 0x32, 0x00,
+ 0x33, 0x00,
+
+ 0x34, 0x30,
+ 0x35, 0x00,
+ 0x36, 0x00,
+ 0x38, 0x00,
+ 0x3e, 0x58,
+ 0x3f, 0x00,
+
+ //Saturation
+ 0x60, 0x67,
+ 0x61, 0x84,// 74
+ 0x62, 0x78,// 80
+ 0x63, 0x50,
+ 0x64, 0x41,
+
+ 0x66, 0x33,
+ 0x67, 0x00,
+
+ 0x6a, 0x90,
+ 0x6b, 0x80,
+ 0x6c, 0x80,
+ 0x6d, 0xa0,
+
+ 0x76, 0x01,
+ 0x74, 0x66,
+ 0x79, 0x06,
+
+ //Page 11
+ 0x03, 0x11,
+ 0x10, 0x7f,//lxh,3f
+ 0x11, 0x40,
+ 0x12, 0xba,
+ 0x13, 0xcb,
+ 0x26, 0x20,
+ 0x27, 0x22,
+ 0x28, 0x0f,
+ 0x29, 0x10,
+ 0x2b, 0x30,
+ 0x2c, 0x32,
+
+ //Out2 D-LPF th
+ 0x30, 0x70,
+ 0x31, 0x10,
+ 0x32, 0x65,
+ 0x33, 0x09,
+ 0x34, 0x06,
+ 0x35, 0x04,
+
+ //Out1 D-LPF th
+ 0x36, 0x70,
+ 0x37, 0x18,
+ 0x38, 0x65,
+ 0x39, 0x09,
+ 0x3a, 0x06,
+ 0x3b, 0x04,
+
+ //Indoor D-LPF th
+ 0x3c, 0x80,
+ 0x3d, 0x18,
+ 0x3e, 0x80,
+ 0x3f, 0x0c,
+ 0x40, 0x09,
+ 0x41, 0x06,
+
+ 0x42, 0x80,
+ 0x43, 0x18,
+ 0x44, 0x80,
+ 0x45, 0x12,
+ 0x46, 0x10,
+ 0x47, 0x10,
+ 0x48, 0x90,
+ 0x49, 0x40,
+ 0x4a, 0x80,
+ 0x4b, 0x13,
+ 0x4c, 0x10,
+ 0x4d, 0x11,
+ 0x4e, 0x80,
+ 0x4f, 0x30,
+ 0x50, 0x80,
+ 0x51, 0x13,
+ 0x52, 0x10,
+ 0x53, 0x13,
+ 0x54, 0x11,
+ 0x55, 0x17,
+ 0x56, 0x20,
+ 0x57, 0x20,
+ 0x58, 0x20,
+ 0x59, 0x30,
+ 0x5a, 0x18,
+ 0x5b, 0x00,
+ 0x5c, 0x00,
+ 0x60, 0x3f,
+ 0x62, 0x50,
+ 0x70, 0x06,
+
+ //Page 12
+ 0x03, 0x12,
+ 0x20, 0x0f,
+ 0x21, 0x0f,
+ 0x25, 0x30,
+ 0x28, 0x00,
+ 0x29, 0x00,
+ 0x2a, 0x00,
+ 0x30, 0x50,
+ 0x31, 0x18,
+ 0x32, 0x32,
+ 0x33, 0x40,
+ 0x34, 0x50,
+ 0x35, 0x70,
+ 0x36, 0xa0,
+
+ //Out2 th
+ 0x40, 0xa0,
+ 0x41, 0x40,
+ 0x42, 0xa0,
+ 0x43, 0x90,
+ 0x44, 0x90,
+ 0x45, 0x80,
+
+ //Out1 th
+ 0x46, 0xb0,
+ 0x47, 0x55,
+ 0x48, 0xa0,
+ 0x49, 0x90,
+ 0x4a, 0x90,
+ 0x4b, 0x80,
+
+ //In door th
+ 0x4c, 0xb0,
+ 0x4d, 0x40,
+ 0x4e, 0x90,
+ 0x4f, 0x90,
+ 0x50, 0xe6,
+ 0x51, 0x80,
+
+ //Dark1 th
+ 0x52, 0xb0,
+ 0x53, 0x60,
+ 0x54, 0xc0,
+ 0x55, 0xc0,
+ 0x56, 0xc0,
+ 0x57, 0x80,
+
+ //Dark2 th
+ 0x58, 0x90,
+ 0x59, 0x40,
+ 0x5a, 0xd0,
+ 0x5b, 0xd0,
+ 0x5c, 0xe0,
+ 0x5d, 0x80,
+
+ //Dark3 th
+ 0x5e, 0x88,
+ 0x5f, 0x40,
+ 0x60, 0xe0,
+ 0x61, 0xe6,
+ 0x62, 0xe6,
+ 0x63, 0x80,
+
+ 0x70, 0x15,
+ 0x71, 0x01,
+
+ 0x72, 0x18,
+ 0x73, 0x01,
+
+ 0x74, 0x25,
+ 0x75, 0x15,
+ 0x80, 0x30,
+ 0x81, 0x50,
+ 0x82, 0x80,
+ 0x85, 0x1a,
+ 0x88, 0x00,
+ 0x89, 0x00,
+ 0x90, 0x5d,
+
+ 0xc5, 0x30,
+ 0xc6, 0x2a,
+
+ 0xD0, 0x0c,
+ 0xD1, 0x80,
+ 0xD2, 0x67,
+ 0xD3, 0x00,
+ 0xD4, 0x00,
+ 0xD5, 0x02,
+ 0xD6, 0xff,
+ 0xD7, 0x18,
+ 0x3b, 0x06,
+ 0x3c, 0x06,
+
+ 0xc5, 0x30,
+ 0xc6, 0x2a,
+
+ //Page 13
+ 0x03, 0x13,
+ 0x10, 0xcb,
+ 0x11, 0x7b,
+ 0x12, 0x07,
+ 0x14, 0x00,
+
+ 0x20, 0x15,
+ 0x21, 0x13,
+ 0x22, 0x33,
+ 0x23, 0x04,
+ 0x24, 0x09,
+ 0x25, 0x08,
+ 0x26, 0x18,
+ 0x27, 0x30,
+ 0x29, 0x12,
+ 0x2a, 0x50,
+
+ //Low clip th
+ 0x2b, 0x06,
+ 0x2c, 0x06,
+ 0x25, 0x08,
+ 0x2d, 0x0c,
+ 0x2e, 0x12,
+ 0x2f, 0x12,
+
+ //Out2 Edge
+ 0x50, 0x10,
+ 0x51, 0x14,
+ 0x52, 0x10,
+ 0x53, 0x0c,
+ 0x54, 0x0f,
+ 0x55, 0x0c,
+
+ //Out1 Edge
+ 0x56, 0x10,
+ 0x57, 0x13,
+ 0x58, 0x10,
+ 0x59, 0x0c,
+ 0x5a, 0x0f,
+ 0x5b, 0x0c,
+
+ //Indoor Edge
+ 0x5c, 0x0a,
+ 0x5d, 0x0b,
+ 0x5e, 0x0a,
+ 0x5f, 0x08,
+ 0x60, 0x09,
+ 0x61, 0x08,
+
+ //Dark1 Edge
+ 0x62, 0x08,
+ 0x63, 0x08,
+ 0x64, 0x08,
+ 0x65, 0x06,
+ 0x66, 0x06,
+ 0x67, 0x06,
+
+ //Dark2 Edge
+ 0x68, 0x07,
+ 0x69, 0x07,
+ 0x6a, 0x07,
+ 0x6b, 0x05,
+ 0x6c, 0x05,
+ 0x6d, 0x05,
+
+ //Dark3 Edge
+ 0x6e, 0x07,
+ 0x6f, 0x07,
+ 0x70, 0x07,
+ 0x71, 0x05,
+ 0x72, 0x05,
+ 0x73, 0x05,
+
+ //2DY
+ 0x80, 0xfd,
+ 0x81, 0x1f,
+ 0x82, 0x05,
+ 0x83, 0x01,
+
+ 0x90, 0x15,
+ 0x91, 0x15,
+ 0x92, 0x33,
+ 0x93, 0x30,
+ 0x94, 0x03,
+ 0x95, 0x14,
+ 0x97, 0x30,
+ 0x99, 0x30,
+
+ 0xa0, 0x04,
+ 0xa1, 0x05,
+ 0xa2, 0x04,
+ 0xa3, 0x05,
+ 0xa4, 0x07,
+ 0xa5, 0x08,
+ 0xa6, 0x07,
+ 0xa7, 0x08,
+ 0xa8, 0x07,
+ 0xa9, 0x08,
+ 0xaa, 0x07,
+ 0xab, 0x08,
+
+ //Out2
+ 0xb0, 0x22,
+ 0xb1, 0x2a,
+ 0xb2, 0x28,
+ 0xb3, 0x22,
+ 0xb4, 0x2a,
+ 0xb5, 0x28,
+
+ //Out1
+ 0xb6, 0x22,
+ 0xb7, 0x2a,
+ 0xb8, 0x28,
+ 0xb9, 0x22,
+ 0xba, 0x2a,
+ 0xbb, 0x28,
+
+ 0xbc, 0x17,
+ 0xbd, 0x17,
+ 0xbe, 0x17,
+ 0xbf, 0x17,
+ 0xc0, 0x17,
+ 0xc1, 0x17,
+
+ //Dark1
+ 0xc2, 0x1e,
+ 0xc3, 0x12,
+ 0xc4, 0x10,
+ 0xc5, 0x1e,
+ 0xc6, 0x12,
+ 0xc7, 0x10,
+
+ //Dark2
+ 0xc8, 0x18,
+ 0xc9, 0x05,
+ 0xca, 0x05,
+ 0xcb, 0x18,
+ 0xcc, 0x05,
+ 0xcd, 0x05,
+
+ //Dark3
+ 0xce, 0x18,
+ 0xcf, 0x05,
+ 0xd0, 0x05,
+ 0xd1, 0x18,
+ 0xd2, 0x05,
+ 0xd3, 0x05,
+
+ //Page 14
+ 0x03, 0x14,
+ 0x10, 0x11,
+ 0x20, 0x40,
+ 0x21, 0x80,
+ 0x23, 0x80,
+ 0x22, 0x80,
+ 0x23, 0x80,
+ 0x24, 0x80,
+
+ 0x30, 0xc8,
+ 0x31, 0x2b,
+ 0x32, 0x00,
+ 0x33, 0x00,
+ 0x34, 0x90,
+
+ 0x40, 0x40,
+ 0x50, 0x2d,
+ 0x60, 0x28,
+ 0x70, 0x2d,
+
+ //Page 15
+ 0x03, 0x15,
+ 0x10, 0x0f,
+ 0x14, 0x52,
+ 0x15, 0x42,
+ 0x16, 0x32,
+ 0x17, 0x2f,
+
+ //CMC
+ 0x30, 0x8f,
+ 0x31, 0x59,
+ 0x32, 0x0a,
+ 0x33, 0x15,
+ 0x34, 0x5b,
+ 0x35, 0x06,
+ 0x36, 0x07,
+ 0x37, 0x40,
+ 0x38, 0x86,
+
+ //CMC OFS
+ 0x40, 0x95,
+ 0x41, 0x1f,
+ 0x42, 0x8a,
+ 0x43, 0x86,
+ 0x44, 0x0a,
+ 0x45, 0x84,
+ 0x46, 0x87,
+ 0x47, 0x9b,
+ 0x48, 0x23,
+
+ //CMC POFS
+ 0x50, 0x8c,
+ 0x51, 0x0c,
+ 0x52, 0x00,
+ 0x53, 0x07,
+ 0x54, 0x17,
+ 0x55, 0x9d,
+ 0x56, 0x00,
+ 0x57, 0x0b,
+ 0x58, 0x89,
+
+ 0x80, 0x03,
+ 0x85, 0x40,
+ 0x87, 0x02,
+ 0x88, 0x00,
+ 0x89, 0x00,
+ 0x8a, 0x00,
+
+ 0x03, 0x16,
+ 0x10, 0x31,//GMA_CTL
+ 0x18, 0x5e,//AG_ON
+ 0x19, 0x5d,//AG_OFF
+ 0x1A, 0x0e,//TIME_ON
+ 0x1B, 0x01,//TIME_OFF
+ 0x1C, 0xdc,//OUT_ON
+ 0x1D, 0xfe,//OUT_OFF
+ //GMA
+ 0x30, 0x00,
+ 0x31, 0x09,
+ 0x32, 0x15,
+ 0x33, 0x29,
+ 0x34, 0x53,
+ 0x35, 0x6c,
+ 0x36, 0x81,
+ 0x37, 0x94,
+ 0x38, 0xa4,
+ 0x39, 0xb3,
+ 0x3a, 0xc0,
+ 0x3b, 0xcb,
+ 0x3c, 0xd5,
+ 0x3d, 0xde,
+ 0x3e, 0xe6,
+ 0x3f, 0xee,
+ 0x40, 0xf5,
+ 0x41, 0xfc,
+ 0x42, 0xff,
+ //RGMA
+ 0x50, 0x00,
+ 0x51, 0x09,
+ 0x52, 0x1f,
+ 0x53, 0x37,
+ 0x54, 0x5b,
+ 0x55, 0x76,
+ 0x56, 0x8d,
+ 0x57, 0xa1,
+ 0x58, 0xb2,
+ 0x59, 0xbe,
+ 0x5a, 0xc9,
+ 0x5b, 0xd2,
+ 0x5c, 0xdb,
+ 0x5d, 0xe3,
+ 0x5e, 0xeb,
+ 0x5f, 0xf0,
+ 0x60, 0xf5,
+ 0x61, 0xf7,
+ 0x62, 0xf8,
+ //BGMA
+ 0x70, 0x00,
+ 0x71, 0x08,
+ 0x72, 0x17,
+ 0x73, 0x2f,
+ 0x74, 0x53,
+ 0x75, 0x6c,
+ 0x76, 0x81,
+ 0x77, 0x94,
+ 0x78, 0xa4,
+ 0x79, 0xb3,
+ 0x7a, 0xc0,
+ 0x7b, 0xcb,
+ 0x7c, 0xd5,
+ 0x7d, 0xde,
+ 0x7e, 0xe6,
+ 0x7f, 0xee,
+ 0x80, 0xf4,
+ 0x81, 0xfa,
+ 0x82, 0xff,
+
+ 0x03, 0x17,
+ 0xc4, 0x6e,
+ 0xc5, 0x5c,
+
+
+ 0x03, 0x18,
+ 0x12, 0x20,
+ 0x10, 0x07,
+ 0x11, 0x00,
+ 0x20, 0x02,
+ 0x21, 0x80,
+ 0x22, 0x00,
+ 0x23, 0xf0,
+ 0x24, 0x00,
+ 0x25, 0x06,
+ 0x26, 0x00,
+ 0x27, 0x00,
+ 0x28, 0x02,
+ 0x29, 0x86,
+ 0x2a, 0x00,
+ 0x2b, 0xf0,
+ 0x2c, 0x14,
+ 0x2d, 0x00,
+ 0x2e, 0x14,
+ 0x2f, 0x00,
+ 0x30, 0x65,
+ 0x03, 0x20,
+ 0x10, 0x1c,
+ 0x18, 0x38,
+ 0x20, 0x01,
+ 0x21, 0x30,
+ 0x22, 0x10,
+ 0x23, 0x00,
+ 0x24, 0x04,
+
+ 0x28, 0xff,
+ 0x29, 0xad,
+
+ 0x2a, 0xf0,
+ 0x2b, 0x34,
+ 0x30, 0x78,
+ 0x2c, 0xc3,
+ 0x2d, 0x5f,
+ 0x2e, 0x33,
+ // 0x30, 0xf8,
+ 0x32, 0x03,
+ 0x33, 0x2e,
+ 0x34, 0x30,
+ 0x35, 0xd4,
+ 0x36, 0xfe,
+ 0x37, 0x32,
+ 0x38, 0x04,
+ 0x47, 0xf0,
+
+ //Y_Frame TH
+ 0x50, 0x45,
+ 0x51, 0x88,
+
+ 0x56, 0x10,
+ 0x57, 0xb7,
+ 0x58, 0x14,
+ 0x59, 0x88,
+ 0x5a, 0x04,
+
+ 0x60, 0x55,
+ 0x61, 0x55,
+ 0x62, 0x6a,
+ 0x63, 0xa9,
+ 0x64, 0x6a,
+ 0x65, 0xa9,
+ 0x66, 0x6a,
+ 0x67, 0xa9,
+ 0x68, 0x6b,
+ 0x69, 0xe9,
+ 0x6a, 0x6a,
+ 0x6b, 0xa9,
+ 0x6c, 0x6a,
+ 0x6d, 0xa9,
+ 0x6e, 0x55,
+ 0x6f, 0x55,
+ 0x70, 0x42,//45
+ 0x71, 0xBb,
+
+ // haunting control
+ 0x76, 0x21,
+ 0x77, 0x02,
+ 0x78, 0x22,
+ 0x79, 0x2a,
+
+ 0x78, 0x24,
+ 0x79, 0x23,
+ 0x7a, 0x23,
+ 0x7b, 0x22,
+ 0x7d, 0x23,
+ 0x83, 0x01, //EXP Normal 33.33 fps
+ 0x84, 0x5f,
+ 0x85, 0x00,
+ 0x86, 0x01, //EXPMin 6410.26 fps
+ 0x87, 0xd4,
+ 0x88, 0x05, //EXP Max 8.33 fps
+ 0x89, 0x7c,
+ 0x8a, 0x00,
+ 0x8B, 0x75, //EXP100
+ 0x8C, 0x00,
+ 0x8D, 0x60, //EXP120
+ 0x8E, 0xe4,
+
+
+ 0x98, 0xdc,
+ 0x99, 0x45,
+ 0x9a, 0x0d,
+ 0x9b, 0xde,
+ 0x9c, 0x08,
+ 0x9d, 0x0a,
+ 0x9e, 0x01,
+ 0x10, 0x9c,
+ 0x18, 0x30,
+ 0x90, 0x0c,
+ 0x91, 0x0c,
+ 0x92, 0xd8,
+ 0x93, 0xd0,
+
+ 0x9c, 0x0c, //EXP Limit 915.75 fps
+ 0x9d, 0xcc,
+ 0x9e, 0x01, //EXP Unit
+ 0x9f, 0xd4,
+ 0xa0, 0x03,
+ 0xa1, 0xa9,
+ 0xa2, 0x80,
+ 0xb0, 0x1d,
+ 0xb1, 0x1a,
+ 0xb2, 0xb0,
+ 0xb3, 0x1a,
+ 0xb4, 0x1a,
+ 0xb5, 0x44,
+ 0xb6, 0x2f,
+ 0xb7, 0x28,
+ 0xb8, 0x25,
+ 0xb9, 0x22,
+ 0xba, 0x21,
+ 0xbb, 0x20,
+ 0xbc, 0x1f,
+ 0xbd, 0x1f,
+ 0xc0, 0x30,
+ 0xc1, 0x20,
+ 0xc2, 0x20,
+ 0xc3, 0x20,
+ 0xc4, 0x08,
+ 0xc8, 0x60,
+ 0xc9, 0x40,
+
+ //Page 22
+ 0x03, 0x22, // Page 22
+ 0x10, 0xfd, // AWBCTL1
+ 0x11, 0x2e, // AWBCTL2
+ 0x19, 0x01,
+ 0x20, 0x30,
+ 0x21, 0x80,
+ 0x23, 0x08,
+ 0x24, 0x01,
+
+ 0x30, 0x80, // ULVL
+ 0x31, 0x80, // VLVL
+ 0x38, 0x11, // UVTH1
+ 0x39, 0x34, // UVTH2
+ 0x40, 0xf7, // YRANGE
+
+ 0x41, 0x77, // CDIFF
+ 0x42, 0x55, // CSUM2
+ 0x43, 0xf0,
+ 0x44, 0x66,
+ 0x45, 0x33,
+ 0x46, 0x01, // WHTPXLTH1
+ 0x47, 0x94,
+
+ 0x50, 0xb2,
+ 0x51, 0x81,
+ 0x52, 0x98,
+
+ 0x80, 0x38, // RGAIN
+ 0x81, 0x20, // GGAIN
+ 0x82, 0x38, // BGAIN
+
+ 0x83, 0x5e, // RMAX
+ 0x84, 0x20, // RMIN
+ 0x85, 0x53, // BMAX
+ 0x86, 0x15, // BMIN
+
+ 0x87, 0x54, // RMAXM
+ 0x88, 0x20, // RMINM
+ 0x89, 0x3f, // BMAXM
+ 0x8a, 0x1c, // BMINM
+
+ 0x8b, 0x54, // RMAXB
+ 0x8c, 0x3f, // RMINB
+ 0x8d, 0x24, // BMAXB
+ 0x8e, 0x1c, // BMINB
+
+ 0x8f, 0x60, // BGAINPARA1
+ 0x90, 0x5f, // BGAINPARA2
+ 0x91, 0x5c, // BGAINPARA3
+ 0x92, 0x4C, // BGAINPARA4
+ 0x93, 0x41, // BGAINPARA5
+ 0x94, 0x3b, // BGAINPARA6
+ 0x95, 0x36, // BGAINPARA7
+ 0x96, 0x30, // BGAINPARA8
+ 0x97, 0x27, // BGAINPARA9
+ 0x98, 0x20, // BGAINPARA10
+ 0x99, 0x1C, // BGAINPARA11
+ 0x9a, 0x19, // BGAINPARA12
+
+ 0x9b, 0x88, // BGAINBND1
+ 0x9c, 0x88, // BGAINBND2
+ 0x9d, 0x48, // RGAINTH1
+ 0x9e, 0x38, // RGAINTH2
+ 0x9f, 0x30, // RGAINTH3
+
+ 0xa0, 0x74, // RDELTA1
+ 0xa1, 0x35, // BDELTA1
+ 0xa2, 0xaf, // RDELTA2
+ 0xa3, 0xf7, // BDELTA2
+
+ 0xa4, 0x10, // AWBEXPLMT1
+ 0xa5, 0x50, // AWBEXPLMT2
+ 0xa6, 0xc4, // AWBEXPLMT3
+
+ 0xad, 0x40,
+ 0xae, 0x4a,
+
+ 0xaf, 0x2a,
+ 0xb0, 0x29,
+
+ 0xb1, 0x20,
+ 0xb4, 0xff,
+ 0xb8, 0x6b,
+ 0xb9, 0x00,
+
+
+ 0x03, 0x24,
+ 0x10, 0x01,
+ 0x18, 0x06,
+ 0x30, 0x06,
+ 0x31, 0x90,
+ 0x32, 0x25,
+ 0x33, 0xa2,
+ 0x34, 0x26,
+ 0x35, 0x58,
+ 0x36, 0x60,
+ 0x37, 0x00,
+ 0x38, 0x50,
+ 0x39, 0x00,
+
+ 0x03, 0x20,
+ 0x10, 0x9c,
+ 0x03, 0x22,
+ 0x10, 0xe9,
+
+ //Page 00
+ 0x03, 0x00,
+ 0x0e, 0x03,
+ 0x0e, 0x73,
+
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+
+ 0x03, 0x00,
+ 0x01, 0xf8,
+
+};
+
+uint8_t hi253_default_regs_init[]={
+
+0x01, 0xf9, //sleep on
+0x08, 0x0f, //Hi-Z on
+0x01, 0xf8, //sleep off
+0x03, 0x00, // Dummy 750us START
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00, // Dummy 750us END
+0x0e, 0x03, //PLL On
+0x0e, 0x73, //PLLx2
+0x03, 0x00, // Dummy 750us START
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00, // Dummy 750us END
+0x0e, 0x00, //PLL off
+0x01, 0xf1, //sleep on
+0x08, 0x00, //Hi-Z off
+0x01, 0xf3,
+0x01, 0xf1,
+// PAGE 20
+0x03, 0x20, //page 20
+0x10, 0x1c, //ae off
+// PAGE 22
+0x03, 0x22, //page 22
+0x10, 0x69, //awb off
+/////// PAGE 0 START ///////
+0x03, 0x00,
+0x10, 0x10, // Sub1/2_Preview2 Mode_H binning
+0x11, 0x90,
+0x12, 0x04,//00
+0x0b, 0xaa, // ESD Check Register
+0x0c, 0xaa, // ESD Check Register
+0x0d, 0xaa, // ESD Check Register
+0x20, 0x00, // Windowing start point Y
+0x21, 0x04,
+0x22, 0x00, // Windowing start point X
+0x23, 0x07,
+0x24, 0x04,
+0x25, 0xb0,
+0x26, 0x06,
+0x27, 0x40, // WINROW END
+0x40, 0x01, //Hblank 408
+0x41, 0x68,
+0x42, 0x00, //Vblank 20
+0x43, 0x14,
+0x45, 0x04,
+0x46, 0x18,
+0x47, 0xd8,
+//BLC
+0x80, 0x2e,
+0x81, 0x7e,
+0x82, 0x90,
+0x83, 0x00,
+0x84, 0x0c,
+0x85, 0x00,
+0x90, 0x0c, //BLC_TIME_TH_ON
+0x91, 0x0c, //BLC_TIME_TH_OFF
+0x92, 0x78, //BLC_AG_TH_ON
+0x93, 0x70, //BLC_AG_TH_OFF
+0x94, 0x75,
+0x95, 0x70,
+0x96, 0xdc,
+0x97, 0xfe,
+0x98, 0x38,
+//OutDoor BLC
+0x99, 0x42,
+0x9a, 0x42,
+0x9b, 0x42,
+0x9c, 0x42,
+//Dark BLC
+0xa0, 0x00,
+0xa2, 0x00,
+0xa4, 0x00,
+0xa6, 0x00,
+//Normal BLC
+0xa8, 0x41,
+0xaa, 0x41,
+0xac, 0x41,
+0xae, 0x41,
+/////// PAGE 2 START ///////
+0x03, 0x02, //Page 02
+0x10, 0x00, //Mode_test
+0x11, 0x00, //Mode_dead_test
+0x12, 0x03, //pwr_ctl_ctl1
+0x13, 0x03, //Mode_ana_test
+0x14, 0x00, //mode_memory
+0x16, 0x00, //dcdc_ctl1
+0x17, 0x8c, //dcdc_ctl2
+0x18, 0x4C, //analog_func1
+0x19, 0x00, //analog_func2
+0x1a, 0x39, //analog_func3
+0x1b, 0x00, //analog_func4
+0x1c, 0x09, //dcdc_ctl3
+0x1d, 0x40, //dcdc_ctl4
+0x1e, 0x30, //analog_func7
+0x1f, 0x10, //analog_func8
+0x20, 0x77, //pixel bias
+0x21, 0xde, //adc,asp bias
+0x22, 0xa7, //main,bus bias
+0x23, 0x30, //clamp
+0x24, 0x4a,
+0x25, 0x10,
+0x27, 0x3c,
+0x28, 0x00,
+0x29, 0x0c,
+0x2a, 0x80,
+0x2b, 0x80,
+0x2c, 0x02,
+0x2d, 0xa0,
+0x2e, 0x00, //11
+0x2f, 0x00, //a1
+0x30, 0x05, //swap_ctl
+0x31, 0x99,
+0x32, 0x00,
+0x33, 0x00,
+0x34, 0x22,
+0x38, 0x88,
+0x39, 0x88,
+0x50, 0x20,
+0x51, 0x03, //00 don't touch
+0x52, 0x01,
+0x53, 0xc1,
+0x54, 0x10,
+0x55, 0x1c,
+0x56, 0x11,
+0x58, 0x10,
+0x59, 0x0e,
+0x5d, 0xa2,
+0x5e, 0x5a,
+0x60, 0x87,
+0x61, 0x99,
+0x62, 0x88,
+0x63, 0x97,
+0x64, 0x88,
+0x65, 0x97,
+0x67, 0x0c,
+0x68, 0x0c,
+0x69, 0x0c,
+0x6a, 0xb4,
+0x6b, 0xc4,
+0x6c, 0xb5,
+0x6d, 0xc2,
+0x6e, 0xb5,
+0x6f, 0xc0,
+0x70, 0xb6,
+0x71, 0xb8,
+0x72, 0x89,
+0x73, 0x96,
+0x74, 0x89,
+0x75, 0x96,
+0x76, 0x89,
+0x77, 0x96,
+0x7c, 0x85,
+0x7d, 0xaf,
+0x80, 0x01,
+0x81, 0x7f,
+0x82, 0x13,
+0x83, 0x24,
+0x84, 0x7d,
+0x85, 0x81,
+0x86, 0x7d,
+0x87, 0x81,
+0x88, 0xab,
+0x89, 0xbc,
+0x8a, 0xac,
+0x8b, 0xba,
+0x8c, 0xad,
+0x8d, 0xb8,
+0x8e, 0xae,
+0x8f, 0xb2,
+0x90, 0xb3,
+0x91, 0xb7,
+0x92, 0x48,
+0x93, 0x54,
+0x94, 0x7d,
+0x95, 0x81,
+0x96, 0x7d,
+0x97, 0x81,
+0xa0, 0x02,
+0xa1, 0x7b,
+0xa2, 0x02,
+0xa3, 0x7b,
+0xa4, 0x7b,
+0xa5, 0x02,
+0xa6, 0x7b,
+0xa7, 0x02,
+0xa8, 0x85,
+0xa9, 0x8c,
+0xaa, 0x85,
+0xab, 0x8c,
+0xac, 0x10,
+0xad, 0x16,
+0xae, 0x10,
+0xaf, 0x16,
+0xb0, 0x99,
+0xb1, 0xa3,
+0xb2, 0xa4,
+0xb3, 0xae,
+0xb4, 0x9b,
+0xb5, 0xa2,
+0xb6, 0xa6,
+0xb7, 0xac,
+0xb8, 0x9b,
+0xb9, 0x9f,
+0xba, 0xa6,
+0xbb, 0xaa,
+0xbc, 0x9b,
+0xbd, 0x9f,
+0xbe, 0xa6,
+0xbf, 0xaa,
+0xc4, 0x2c,
+0xc5, 0x43,
+0xc6, 0x63,
+0xc7, 0x79,
+0xc8, 0x2d,
+0xc9, 0x42,
+0xca, 0x2d,
+0xcb, 0x42,
+0xcc, 0x64,
+0xcd, 0x78,
+0xce, 0x64,
+0xcf, 0x78,
+0xd0, 0x0a,
+0xd1, 0x09,
+0xd2, 0x20,
+0xd3, 0x00,
+0xd4, 0x0c, //DCDC_TIME_TH_ON don't touch
+0xd5, 0x0c, //DCDC_TIME_TH_OFF don't touch
+0xd6, 0x78, //DCDC_AG_TH_ON 78
+0xd7, 0x70, //DCDC_AG_TH_OFF 70
+0xe0, 0xc4,
+0xe1, 0xc4,
+0xe2, 0xc4,
+0xe3, 0xc4,
+0xe4, 0x00,
+0xe8, 0x80,
+0xe9, 0x40,
+0xea, 0x7f,
+0xf0, 0x01,
+0xf1, 0x01,
+0xf2, 0x01,
+0xf3, 0x01,
+0xf4, 0x01,
+/////// PAGE 3 ///////
+0x03, 0x03,
+0x10, 0x10,
+/////// PAGE 10 START ///////
+0x03, 0x10,
+0x10, 0x03, // CrYCbY // For Demoset 0x03
+0x12, 0x30,
+0x13, 0x0a,//add jacky open constrast
+0x20, 0x00,
+0x30, 0x00,
+0x31, 0x00,
+0x32, 0x00,
+0x33, 0x00,
+0x34, 0x30,
+0x35, 0x00,
+0x36, 0x00,
+0x38, 0x00,
+0x3e, 0x58,
+0x3f, 0x00,
+0x40, 0x84, // YOFS-85
+0x41, 0x00, // DYOFS
+0x48, 0x80,// 84
+0x60, 0x67,//6b AG ratio jacky
+
+0x61, 0x70, //7e //8e //88 //70
+0x62, 0x68, //7e //8e //88 //80
+0x63, 0x50, //Double_AG 50->30
+0x64, 0x41,
+0x66, 0x42,
+0x67, 0x20,
+0x6a, 0x80, //8a
+0x6b, 0x84, //74
+0x6c, 0x80, //7e //7a
+0x6d, 0x80, //8e
+/////// PAGE 11 START ///////
+0x03, 0x11,
+0x10, 0x7f,
+0x11, 0x40,
+0x12, 0x0a, // Blue Max-Filter Delete
+0x13, 0xbb,
+0x26, 0x51, // Double_AG 31->20
+0x27, 0x54, // Double_AG 34->22
+0x28, 0x0f,
+0x29, 0x10,
+0x2b, 0x30,
+0x2c, 0x32,
+//Out2 D-LPF th
+0x30, 0x70,
+0x31, 0x10,
+0x32, 0x58,
+0x33, 0x09,
+0x34, 0x06,
+0x35, 0x03,
+//Out1 D-LPF th
+0x36, 0x70,
+0x37, 0x18,
+0x38, 0x58,
+0x39, 0x09,
+0x3a, 0x06,
+0x3b, 0x03,
+//Indoor D-LPF th
+0x3c, 0x80,
+0x3d, 0x18,
+ 0x3e, 0x90,
+0x3f, 0x0c,
+0x40, 0x09,
+0x41, 0x06,
+0x42, 0x80,
+0x43, 0x18,
+ 0x44, 0x90,
+0x45, 0x12,
+0x46, 0x10,
+0x47, 0x10,
+0x48, 0x90,
+0x49, 0x40,
+0x4a, 0x80,
+0x4b, 0x13,
+0x4c, 0x10,
+0x4d, 0x11,
+0x4e, 0x80,
+0x4f, 0x30,
+0x50, 0x80,
+0x51, 0x13,
+0x52, 0x10,
+0x53, 0x13,
+0x54, 0x11,
+0x55, 0x17,
+0x56, 0x20,
+0x57, 0x01,
+0x58, 0x00,
+0x59, 0x00,
+0x5a, 0x1f, //18
+0x5b, 0x00,
+0x5c, 0x00,
+0x60, 0x3f,
+0x62, 0x60,
+0x70, 0x06,
+/////// PAGE 12 START ///////
+0x03, 0x12,
+0x20, 0x0f,
+0x21, 0x0f,
+0x25, 0x00, //0x30
+0x28, 0x00,
+0x29, 0x00,
+0x2a, 0x00,
+0x30, 0x50,
+0x31, 0x18,
+0x32, 0x32,
+0x33, 0x40,
+0x34, 0x50,
+0x35, 0x70,
+0x36, 0xa0,
+//Out2 th
+0x40, 0xa0,
+0x41, 0x40,
+0x42, 0xa0,
+0x43, 0x90,
+0x44, 0x90,
+0x45, 0x80,
+//Out1 th
+0x46, 0xb0,
+0x47, 0x55,
+0x48, 0xa0,
+0x49, 0x90,
+0x4a, 0x90,
+0x4b, 0x80,
+//Indoor th
+0x4c, 0xb0,
+0x4d, 0x40,
+0x4e, 0x90,
+0x4f, 0x90,
+0x50, 0xa0,
+0x51, 0x80,
+//Dark1 th
+0x52, 0x00, //b0
+0x53, 0x50, //60
+0x54, 0xd4, //c0
+0x55, 0xc0, //c0
+0x56, 0x70, //b0
+0x57, 0xec, //70
+//Dark2 th
+0x58, 0x60, //90
+0x59, 0x40, //
+0x5a, 0xd0,
+0x5b, 0xd0,
+0x5c, 0xc0,
+0x5d, 0x9b, //70
+//Dark3 th
+0x5e, 0x70,//88
+0x5f, 0x40,
+0x60, 0xe0,
+0x61, 0xe0,
+0x62, 0xe0,
+0x63, 0xb4,//80
+0x70, 0x15,
+0x71, 0x01, //Don't Touch register
+0x72, 0x18,
+0x73, 0x01, //Don't Touch register
+0x74, 0x25,
+0x75, 0x15,
+0x80, 0x20,
+0x81, 0x40,
+0x82, 0x65,
+0x85, 0x1a,
+0x88, 0x00,
+0x89, 0x00,
+0x90, 0x5d, //For capture
+0x91, 0x88,
+0x98, 0x7d,
+0x99, 0x28,
+0x9A, 0x14,
+0x9B, 0xc8,
+0x9C, 0x02,
+0x9D, 0x1e,
+0x9E, 0x28,
+0x9F, 0x07,
+0xA0, 0x32,
+0xA4, 0x04,
+0xA5, 0x0e,
+0xA6, 0x0c,
+0xA7, 0x04,
+0xA8, 0x3c,
+
+0xAA, 0x14,
+0xAB, 0x11,
+0xAC, 0x0f,
+0xAD, 0x16,
+0xAE, 0x15,
+0xAF, 0x14,
+
+0xB1, 0xaa,
+0xB2, 0x96,
+0xB3, 0x28,
+//Dont Touch register
+0xB8, 0x78,
+0xB9, 0xa0,
+0xBA, 0xb4,
+0xBB, 0x14,
+0xBC, 0x14,
+0xBD, 0x00, //14
+0xBF, 0x64,
+0xC0, 0x64,
+0xC1, 0x64,
+0xC2, 0x04,
+0xC3, 0x03,
+0xC4, 0x0c,
+0xC5, 0x30,
+0xC6, 0x2a,
+0xD0, 0x0c, //CI Option/CI DPC
+0xD1, 0x80,
+0xD2, 0x67,
+0xD3, 0x00,
+0xD4, 0x00,
+0xD5, 0x02,
+0xD6, 0xff,
+0xD7, 0x18,
+//End
+0x3b, 0x06,
+0x3c, 0x06,
+0xc5, 0x00,//55->48
+0xc6, 0x00,//48->40
+/////// PAGE 13 START ///////
+0x03, 0x13,
+//Edge
+0x10, 0xcb,
+0x11, 0x7b,
+0x12, 0x07,
+0x14, 0x00,
+0x20, 0x15,
+0x21, 0x13,
+0x22, 0x33,
+0x23, 0x05,
+0x24, 0x09,
+0x26, 0x18,
+0x27, 0x30,
+0x29, 0x12,
+0x2a, 0x50,
+//Low clip th
+0x2b, 0x01, //Out2 02
+0x2c, 0x01, //Out1 02
+0x25, 0x06,
+0x2d, 0x0c,
+0x2e, 0x12,
+0x2f, 0x12,
+//Out2 Edge
+0x50, 0x18,
+0x51, 0x1c,
+0x52, 0x1b,
+0x53, 0x15,
+0x54, 0x18,
+0x55, 0x15,
+//Out1 Edge
+0x56, 0x18,
+0x57, 0x1c,
+0x58, 0x1b,
+0x59, 0x15,
+0x5a, 0x18,
+0x5b, 0x15,
+//Indoor Edge
+0x5c, 0x1f,//0b
+0x5d, 0x21,//21
+0x5e, 0x1c,//0a
+0x5f, 0x1c,//08
+0x60, 0x2f,//19
+0x61, 0x1c,//08
+//Dark1 Edge
+0x62, 0x08,
+0x63, 0x28,
+0x64, 0x08,
+0x65, 0x06,
+0x66, 0x26,
+0x67, 0x06,
+//Dark2 Edge
+0x68, 0x07,
+0x69, 0x07,
+0x6a, 0x07,
+0x6b, 0x05,
+0x6c, 0x05,
+0x6d, 0x05,
+//Dark3 Edge
+0x6e, 0x07,
+0x6f, 0x07,
+0x70, 0x07,
+0x71, 0x05,
+0x72, 0x05,
+0x73, 0x05,
+0x80, 0xfd,
+0x81, 0x1f,
+0x82, 0x05,
+0x83, 0x31,
+0x90, 0x05,
+0x91, 0x05,
+0x92, 0x33,
+0x93, 0x30,
+0x94, 0x03,
+0x95, 0x14,
+0x97, 0x20,
+0x99, 0x20,
+0xa0, 0x01,
+0xa1, 0x02,
+0xa2, 0x01,
+0xa3, 0x02,
+0xa4, 0x05,
+0xa5, 0x05,
+0xa6, 0x07,
+0xa7, 0x08,
+0xa8, 0x07,
+0xa9, 0x08,
+0xaa, 0x07,
+0xab, 0x08,
+0xb0, 0x22,
+0xb1, 0x2a,
+0xb2, 0x28,
+0xb3, 0x22,
+0xb4, 0x2a,
+0xb5, 0x28,
+0xb6, 0x22,
+0xb7, 0x2a,
+0xb8, 0x28,
+0xb9, 0x22,
+0xba, 0x2a,
+0xbb, 0x28,
+0xbc, 0x25,
+0xbd, 0x2a,
+0xbe, 0x27,
+0xbf, 0x25,
+0xc0, 0x2a,
+0xc1, 0x27,
+0xc2, 0x1e,
+0xc3, 0x24,
+0xc4, 0x20,
+0xc5, 0x1e,
+0xc6, 0x24,
+0xc7, 0x20,
+0xc8, 0x18,
+0xc9, 0x20,
+0xca, 0x1e,
+0xcb, 0x18,
+0xcc, 0x20,
+0xcd, 0x1e,
+0xce, 0x18,
+0xcf, 0x20,
+0xd0, 0x1e,
+0xd1, 0x18,
+0xd2, 0x20,
+0xd3, 0x1e,
+/////// PAGE 14 START ///////
+0x03, 0x14,
+0x10, 0x11,
+0x14, 0x80, // GX
+0x15, 0x80, // GY
+0x16, 0x80, // RX
+0x17, 0x80, // RY
+0x18, 0x80, // BX
+0x19, 0x80, // BY
+0x20, 0x80, //X 60 //a0
+0x21, 0x80, //Y
+0x22, 0x78,
+0x23, 0x80,
+0x24, 0x80,
+0x30, 0xc8,
+0x31, 0x2b,
+0x32, 0x00,
+0x33, 0x00,
+0x34, 0x90,
+0x40, 0x56, //0x48//65
+0x50, 0x44, //0x34
+0x60, 0x46, //0x29
+0x70, 0x44, //0x34
+/////// PAGE 15 START ///////
+0x03, 0x15,
+0x10, 0x0f,
+//Rstep H 16
+//Rstep L 14
+0x14, 0x4f, //CMCOFSGH
+0x15, 0x32, //CMCOFSGM
+0x16, 0x24, //CMCOFSGL
+0x17, 0x2f, //CMC SIGN
+//CMC
+0x30, 0x91,
+0x31, 0x6a,
+0x32, 0x18,
+0x33, 0x17,
+0x34, 0x53,
+0x35, 0x08,
+0x36, 0x08,
+0x37, 0x3e,
+0x38, 0x87,
+//CMC OFS
+0x40, 0xa3,
+0x41, 0x3a,
+0x42, 0x93,
+0x43, 0x0b,
+0x44, 0x86,
+0x45, 0x06,
+0x46, 0x00,
+0x47, 0xa2,
+0x48, 0x22,
+//CMC POFS
+0x50, 0x82,
+0x51, 0x06,
+0x52, 0x83,
+0x53, 0x03,
+0x54, 0x11,
+0x55, 0x8e,
+0x56, 0x00,
+0x57, 0x06,
+0x58, 0x87,
+
+0x80, 0x03,
+0x85, 0x40,
+0x87, 0x02,
+0x88, 0x00,
+0x89, 0x00,
+0x8a, 0x00,
+
+
+
+/////// PAGE 16 START ///////
+0x03, 0x16,
+0x10, 0x31,//GMA_CTL
+0x18, 0x5e,//AG_ON
+0x19, 0x5d,//AG_OFF
+0x1A, 0x0e,//TIME_ON
+0x1B, 0x01,//TIME_OFF
+0x1C, 0xdc,//OUT_ON
+0x1D, 0xfe,//OUT_OFF
+
+//GMA
+0x30, 0x00,
+0x31, 0x06,
+0x32, 0x0e,
+0x33, 0x23,
+0x34, 0x55,
+0x35, 0x7f,
+0x36, 0x99,
+0x37, 0xac,
+0x38, 0xba,
+0x39, 0xc5,
+0x3a, 0xcd,
+0x3b, 0xd4,
+0x3c, 0xdb,
+0x3d, 0xe2,
+0x3e, 0xe7,
+0x3f, 0xec,
+0x40, 0xf3,
+0x41, 0xfa,
+0x42, 0xff,
+//RGMA
+0x50, 0x00,
+0x51, 0x07,
+0x52, 0x12,
+0x53, 0x27,
+0x54, 0x57,
+0x55, 0x7f,
+0x56, 0x99,
+0x57, 0xac,
+0x58, 0xba,
+0x59, 0xc5,
+0x5a, 0xcd,
+0x5b, 0xd4,
+0x5c, 0xdb,
+0x5d, 0xe2,
+0x5e, 0xe7,
+0x5f, 0xec,
+0x60, 0xf3,
+0x61, 0xfa,
+0x62, 0xff,
+//BGMA
+0x70, 0x00,
+0x71, 0x09,
+0x72, 0x16,
+0x73, 0x2c,
+0x74, 0x59,
+0x75, 0x7f,
+0x76, 0x99,
+0x77, 0xac,
+0x78, 0xba,
+0x79, 0xc5,
+0x7a, 0xcd,
+0x7b, 0xd4,
+0x7c, 0xdb,
+0x7d, 0xe2,
+0x7e, 0xe7,
+0x7f, 0xec,
+0x80, 0xf3,
+0x81, 0xfa,
+0x82, 0xff,
+/////// PAGE 17 START ///////
+0x03, 0x17,
+0x10, 0xf7,
+/////// PAGE 18 START ///////
+
+ 0x03, 0x18,
+ 0x10, 0x00,
+
+/////// PAGE 20 START ///////
+0x03, 0x20,
+0x11, 0x1c,
+0x18, 0x30,
+0x1a, 0x08,
+0x20, 0x01, //05_lowtemp Y Mean off
+0x21, 0x30,
+0x22, 0x10,
+0x23, 0x00,
+0x24, 0x00, //Uniform Scene Off
+0x28, 0xe7,
+0x29, 0x0d, //20100305 ad->0d
+0x2a, 0xff,
+0x2b, 0x34, //f4->Adaptive on //04 off
+0x2c, 0xc2,
+0x2d, 0xcf, //fe->AE Speed option
+0x2e, 0x33,
+0x30, 0x78, //f8
+0x32, 0x03,
+0x33, 0x2e,
+0x34, 0x30,
+0x35, 0xd4,
+0x36, 0xfe,
+0x37, 0x32,
+0x38, 0x04,
+0x39, 0x22, //AE_escapeC10
+0x3a, 0xde, //AE_escapeC11
+0x3b, 0x22, //AE_escapeC1
+0x3c, 0xde, //AE_escapeC2
+0x50, 0x45,
+0x51, 0x88,
+0x56, 0x03,
+0x57, 0xf7,
+0x58, 0x14,
+0x59, 0x88,
+0x5a, 0x04,
+
+0x60, 0xff,
+0x61, 0xff,
+0x62, 0xea,
+0x63, 0xab,
+0x64, 0xea,
+0x65, 0xab,
+0x66, 0xea,//eb
+0x67, 0x2b,//eb
+0x68, 0xe8,//eb
+0x69, 0x2b,//eb
+0x6a, 0xea,
+0x6b, 0xab,
+0x6c, 0xea,
+0x6d, 0xab,
+0x6e, 0xff,
+0x6f, 0xff,
+
+/*
+0x60, 0x55,
+0x61, 0x55,
+0x62, 0x6a,
+0x63, 0xa9,
+0x64, 0x6f,
+0x65, 0xf9,
+0x66, 0x6f,//eb
+0x67, 0xf9,//eb
+0x68, 0x6f,//eb
+0x69, 0xf9,//eb
+0x6a, 0x6f,
+0x6b, 0xf9,
+0x6c, 0x6a,
+0x6d, 0xa9,
+0x6e, 0x55,
+0x6f, 0x55,
+*/
+
+0x70, 0x76, //6e
+0x71, 0x89, //00 //-4
+0x76, 0x43,
+0x77, 0xe2, //04 //f2
+0x78, 0x23, //Yth1
+0x79, 0x46, //Yth2 //46
+0x7a, 0x23, //23
+0x7b, 0x22, //22
+0x7d, 0x23,
+0x83, 0x01, //EXP Normal 33.33 fps
+0x84, 0x5f,
+0x85, 0x90,
+0x86, 0x01, //EXPMin 7500.00 fps
+0x87, 0xf4,
+0x88, 0x05, //EXP Max 8.33 fps
+0x89, 0x7e,
+0x8a, 0x40,
+0x8B, 0x75, //EXP100
+0x8C, 0x30,
+0x8D, 0x61, //EXP120
+0x8E, 0xa8,
+0x9c, 0x17, //EXP Limit 500.00 fps
+0x9d, 0x70,
+0x9e, 0x01, //EXP Unit
+0x9f, 0xf4,
+0xb0, 0x18,
+0xb1, 0x14, //ADC 400->560
+0xb2, 0x80,//a0
+0xb3, 0x18,
+0xb4, 0x1a,
+0xb5, 0x44,
+0xb6, 0x2f,
+0xb7, 0x28,
+0xb8, 0x25,
+0xb9, 0x22,
+0xba, 0x21,
+0xbb, 0x20,
+0xbc, 0x1f,
+0xbd, 0x1f,
+0xc0, 0x14,
+0xc1, 0x1f,
+0xc2, 0x1f,
+0xc3, 0x18, //2b
+0xc4, 0x10, //08
+0xc8, 0x80,
+0xc9, 0x40,
+/////// PAGE 22 START ///////
+0x03, 0x22,
+0x10, 0xfd,
+0x11, 0x2e,
+0x19, 0x01,
+0x20, 0x30,
+0x21, 0x80,
+0x24, 0x01,
+0x30, 0x80,
+0x31, 0x80,
+0x38, 0x11,
+0x39, 0x34,
+0x40, 0xf7,
+0x41, 0x55,
+0x42, 0x33, //43
+
+0x43, 0xf6,
+0x44, 0x55, //44
+0x45, 0x44, //33
+
+0x46, 0x00,
+0x50, 0xb2,
+0x51, 0x81,
+0x52, 0x98,
+0x80, 0x38,//40
+0x81, 0x20,
+0x82, 0x30,
+
+0x83, 0x60,
+0x84, 0x16,//1E
+0x85, 0x60,//52
+0x86, 0x20,
+
+0x87, 0x45,
+0x88, 0x35,
+0x89, 0x47,
+0x8a, 0x2b,
+
+0x8b, 0x40,
+0x8c, 0x33,
+0x8d, 0x38,
+0x8e, 0x30,
+
+0x8f, 0x53,
+0x90, 0x52,
+0x91, 0x50,
+0x92, 0x4c,
+0x93, 0x43,
+0x94, 0x37,
+0x95, 0x2f,//2a
+0x96, 0x26,//24
+0x97, 0x21,//20
+0x98, 0x1e,
+0x99, 0x1f,
+0x9a, 0x20,
+0x9b, 0x88,
+0x9c, 0x88,
+0x9d, 0x48,
+0x9e, 0x38,
+0x9f, 0x30,
+0xa0, 0x60,
+0xa1, 0x34,
+0xa2, 0x6f,
+0xa3, 0xff,
+0xa4, 0x14,
+0xa5, 0x2c,
+0xa6, 0xcf,
+0xad, 0x40,
+0xae, 0x4a,
+0xaf, 0x28,
+0xb0, 0x26,
+0xb1, 0x00,
+0xb8, 0xa0,
+0xb9, 0x00,
+// PAGE 20
+0x03, 0x20, //page 20
+0x10, 0x9c, //ae off
+// PAGE 22
+0x03, 0x22, //page 22
+0x10, 0xe9, //awb off
+// PAGE 0
+0x03, 0x00,
+0x0e, 0x03, //PLL On
+0x0e, 0x73, //PLLx2
+0x03, 0x00, // Dummy 750us
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+ 0x03, 0x00,
+ 0x01, 0xf8,
+
+};
+
+#endif
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/hi253/hi253-mrs-wave7.h b/drivers/media/video/wmt_v4l2/sensors/hi253/hi253-mrs-wave7.h
new file mode 100755
index 00000000..feadda2c
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/hi253/hi253-mrs-wave7.h
@@ -0,0 +1,2147 @@
+#ifndef YACD511_H
+#define YACD511_H
+
+// Scene Mode
+uint8_t hi253_scene_mode_auto[] = {
+ 0x03,0x10,
+ 0x41,0x00,
+ 0x03,0x20,
+ 0xb2,0x80,
+ 0xff,0xff,
+};
+
+uint8_t hi253_scene_mode_night[] = {
+ 0x03,0x10,
+ 0x41,0x15,
+ 0x03,0x20,
+ 0xb2,0xf0,
+ 0xff,0xff,
+};
+
+
+// White Balance
+uint8_t hi253_wb_auto [] = {
+ 0x03,0x22,
+ //0x11,0x2e,
+ 0x83,0x52, // RMAX
+ 0x84, 0x16, // RMIN
+ 0x85, 0x5a, // BMAX
+ 0x86, 0x23, // BMIN
+ 0xff,0xff,
+};
+
+uint8_t hi253_wb_incandescent [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x2a,
+ 0x82,0x3f,
+ 0x83,0x35,
+ 0x84,0x28,
+ 0x85,0x45,
+ 0x86,0x3b,
+ 0xff,0xff,
+};
+
+uint8_t hi253_wb_fluorescent [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x26,
+ 0x82,0x44,
+ 0x83,0x2f,
+ 0x84,0x24,
+ 0x85,0x4a,
+ 0x86,0x40,
+ 0xff,0xff,
+};
+
+uint8_t hi253_wb_daylight [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x3d,
+ 0x82,0x2e,
+ 0x83,0x40,
+ 0x84,0x33,
+ 0x85,0x33,
+ 0x86,0x28,
+ 0xff,0xff,
+};
+
+uint8_t hi253_wb_cloudy [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x50,
+ 0x82,0x25,
+ 0x83,0x55,
+ 0x84,0x4b,
+ 0x85,0x28,
+ 0x86,0x20,
+ 0xff,0xff,
+};
+
+
+// Exposure
+uint8_t hi253_exposure_neg6[] = {
+ 0x03,0x10,
+ 0x40,0xa8,
+};
+
+uint8_t hi253_exposure_neg3[] = {
+ 0x03,0x10,
+ 0x40,0x98,
+};
+
+uint8_t hi253_exposure_zero[] = {
+ 0x03,0x10,
+ 0x40,0x88,
+};
+
+uint8_t hi253_exposure_pos3[] = {
+ 0x03,0x10,
+ 0x40,0x08,
+};
+
+uint8_t hi253_exposure_pos6[] = {
+ 0x03,0x10,
+ 0x40,0x28,
+};
+
+
+// Resolution
+uint8_t hi253_1600_1200_rets[]={
+ 0x03, 0x00,
+ 0x10, 0x00,
+ 0x12, 0x04,
+ 0x20, 0x00,
+ 0x21, 0x0a,
+ 0x22, 0x00,
+ 0x23, 0x0a,
+
+ 0x03, 0x18,
+ 0x10, 0x00,
+ 0xff, 0xff,
+};
+
+uint8_t hi253_1280_720_regs[]={
+};
+
+uint8_t hi253_800_600_regs[]={
+
+ 0x03, 0x00,
+ 0x10, 0x10,
+ 0x12, 0x04,
+
+ 0x03, 0x18,
+ 0x10, 0x00,
+ 0xff, 0xff,
+};
+
+uint8_t hi253_640_480_regs[]={
+ 0x03, 0x00,
+ 0x10, 0x10,
+ 0x12, 0x04,
+
+ 0x03, 0x18,
+ 0x12, 0x20,
+ 0x10, 0x07,
+ 0x11, 0x00,
+ 0x20, 0x05,
+ 0x21, 0x00,
+ 0x22, 0x03,
+ 0x23, 0xc0,
+ 0x24, 0x00,
+ 0x25, 0x04,
+ 0x26, 0x00,
+ 0x27, 0x00,
+ 0x28, 0x05,
+ 0x29, 0x04,
+ 0x2a, 0x03,
+ 0x2b, 0xc0,
+ 0x2c, 0x0a,
+ 0x2d, 0x00,
+ 0x2e, 0x0a,
+ 0x2f, 0x00,
+ 0x30, 0x45,
+ 0xff, 0xff,
+};
+
+uint8_t hi253_320_240_regs[]= {
+ 0x01, 0xf9,
+ 0x08, 0x0f,
+ 0x01, 0xf8,
+
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+
+ 0x0e, 0x00,
+
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+
+ 0x0e, 0x00,
+ 0x01, 0xf1,
+ 0x08, 0x00,
+ 0x01, 0xf3,
+ 0x01, 0xf1,
+
+ 0x03, 0x20,
+ 0x10, 0x0c,
+ 0x03, 0x22,
+ 0x10, 0x69,
+
+ //Page 00
+ 0x03, 0x00,
+ 0x10, 0x11, //lxh
+ 0x11, 0x90,
+ 0x12, 0x04,
+ 0x0b, 0xaa,
+ 0x0c, 0xaa,
+ 0x0d, 0xaa,
+ 0x20, 0x00,
+ 0x21, 0x0a, //lxh
+ 0x22, 0x00,
+ 0x23, 0x0a, //lxh
+ 0x24, 0x04,
+ 0x25, 0xb0,
+ 0x26, 0x06,
+ 0x27, 0x40,
+
+ 0x40, 0x00,
+ 0x41, 0xe8,//lxh
+ 0x42, 0x00,
+ 0x43, 0x14,//lxh
+
+ 0x45, 0x04,
+ 0x46, 0x18,
+ 0x47, 0xd8,
+
+ 0xe1, 0x0f,
+
+ //BLC
+ 0x80, 0x2e,
+ 0x81, 0x7e,
+ 0x82, 0x90,
+ 0x83, 0x00,
+ 0x84, 0x0c,
+ 0x85, 0x00,
+ 0x90, 0x0c,
+ 0x91, 0x0c,
+ 0x92, 0x78,
+ 0x93, 0x70,
+ 0x94, 0x75,
+ 0x95, 0x70,
+ 0x96, 0xdc,
+ 0x97, 0xfe,
+ 0x98, 0x20,
+
+ //OutDoor BLC
+ 0x99,0x42,
+ 0x9a,0x42,
+ 0x9b,0x42,
+ 0x9c,0x42,
+
+ //Dark BLC
+ 0xa0, 0x02,
+ 0xa2, 0x02,
+ 0xa4, 0x02,
+ 0xa6, 0x02,
+
+ //Normal BLC
+ 0xa8, 0x42,
+ 0xaa, 0x42,
+ 0xac, 0x42,
+ 0xae, 0x42,
+
+ //Page 02
+ 0x03, 0x02,
+ 0x12, 0x03,
+ 0x13, 0x03,
+ 0x16, 0x00,
+ 0x17, 0x8C,
+ 0x18, 0x4c,
+ 0x19, 0x00,
+ 0x1a, 0x39,
+ 0x1c, 0x09,
+ 0x1d, 0x40,
+ 0x1e, 0x30,
+ 0x1f, 0x10,
+ 0x20, 0x77,
+ 0x21, 0xde,
+ 0x22, 0xa7,
+ 0x23, 0x30,
+ 0x27, 0x3c,
+ 0x2b, 0x80,
+ 0x2e, 0x00,
+ 0x2f, 0x00,
+ 0x30, 0x05,
+ 0x50, 0x20,
+ 0x52, 0x01,
+ 0x53, 0xc1,
+ 0x55, 0x1c,
+ 0x56, 0x11,
+ 0x5d, 0xA2,
+ 0x5e, 0x5a,
+ 0x60, 0x87,
+ 0x61, 0x99,
+ 0x62, 0x88,
+ 0x63, 0x97,
+ 0x64, 0x88,
+ 0x65, 0x97,
+ 0x67, 0x0c,
+ 0x68, 0x0c,
+ 0x69, 0x0c,
+ 0x72, 0x89,
+ 0x73, 0x96,
+ 0x74, 0x89,
+ 0x75, 0x96,
+ 0x76, 0x89,
+ 0x77, 0x96,
+ 0x7C, 0x85,
+ 0x7d, 0xaf,
+ 0x80, 0x01,
+ 0x81, 0x7f,
+ 0x82, 0x13,
+ 0x83, 0x24,
+ 0x84, 0x7d,
+ 0x85, 0x81,
+ 0x86, 0x7d,
+ 0x87, 0x81,
+ 0x92, 0x48,
+ 0x93, 0x54,
+ 0x94, 0x7d,
+ 0x95, 0x81,
+ 0x96, 0x7d,
+ 0x97, 0x81,
+ 0xa0, 0x02,
+ 0xa1, 0x7b,
+ 0xa2, 0x02,
+ 0xa3, 0x7b,
+ 0xa4, 0x7b,
+ 0xa5, 0x02,
+ 0xa6, 0x7b,
+ 0xa7, 0x02,
+ 0xa8, 0x85,
+ 0xa9, 0x8c,
+ 0xaa, 0x85,
+ 0xab, 0x8c,
+ 0xac, 0x10,
+ 0xad, 0x16,
+ 0xae, 0x10,
+ 0xaf, 0x16,
+ 0xb0, 0x99,
+ 0xb1, 0xa3,
+ 0xb2, 0xa4,
+ 0xb3, 0xae,
+ 0xb4, 0x9b,
+ 0xb5, 0xa2,
+ 0xb6, 0xa6,
+ 0xb7, 0xac,
+ 0xb8, 0x9b,
+ 0xb9, 0x9f,
+ 0xba, 0xa6,
+ 0xbb, 0xaa,
+ 0xbc, 0x9b,
+ 0xbd, 0x9f,
+ 0xbe, 0xa6,
+ 0xbf, 0xaa,
+ 0xc4, 0x2c,
+ 0xc5, 0x43,
+ 0xc6, 0x63,
+ 0xc7, 0x79,
+ 0xc8, 0x2d,
+ 0xc9, 0x42,
+ 0xca, 0x2d,
+ 0xcb, 0x42,
+ 0xcc, 0x64,
+ 0xcd, 0x78,
+ 0xce, 0x64,
+ 0xcf, 0x78,
+ 0xd0, 0x0a,
+ 0xd1, 0x09,
+ 0xd4, 0x0c,
+ 0xd5, 0x0c,
+ 0xd6, 0x78,
+ 0xd7, 0x70,//lxh
+ 0xe0, 0xc4,
+ 0xe1, 0xc4,
+ 0xe2, 0xc4,
+ 0xe3, 0xc4,
+ 0xe4, 0x00,
+ 0xe8, 0x80,
+ 0xe9, 0x40,
+ 0xea, 0x7f,
+ 0xf0, 0x01,
+ 0xf1, 0x01,
+ 0xf2, 0x01,
+ 0xf3, 0x01,
+ 0xf4, 0x01,
+
+ //PAGE10
+ 0x03, 0x10,
+ 0x10, 0x03, //lxh
+ 0x11, 0x03, //lxh,normal
+ 0x12, 0x30,
+ 0x13, 0x02,
+ 0x20, 0x00,
+
+ 0x40, 0x88,
+ 0x41, 0x05,
+ 0x48, 0x88,// 84
+ 0x50, 0x90,
+ 0x30, 0x00,
+ 0x31, 0x00,
+ 0x32, 0x00,
+ 0x33, 0x00,
+
+ 0x34, 0x30,
+ 0x35, 0x00,
+ 0x36, 0x00,
+ 0x38, 0x00,
+ 0x3e, 0x58,
+ 0x3f, 0x00,
+
+ //Saturation
+ 0x60, 0x67,
+ 0x61, 0x78,// 74
+ 0x62, 0x78,// 76
+ 0x63, 0x50,
+ 0x64, 0x41,
+
+ 0x66, 0x33,
+ 0x67, 0x00,
+
+ 0x6a, 0x90,
+ 0x6b, 0x80,
+ 0x6c, 0x80,
+ 0x6d, 0xa0,
+
+ 0x76, 0x01,
+ 0x74, 0x66,
+ 0x79, 0x06,
+
+ //Page 11
+ 0x03, 0x11,
+ 0x10, 0x7f,//lxh,3f
+ 0x11, 0x40,
+ 0x12, 0xba,
+ 0x13, 0xcb,
+ 0x26, 0x20,
+ 0x27, 0x22,
+ 0x28, 0x0f,
+ 0x29, 0x10,
+ 0x2b, 0x30,
+ 0x2c, 0x32,
+
+ //Out2 D-LPF th
+ 0x30, 0x70,
+ 0x31, 0x10,
+ 0x32, 0x65,
+ 0x33, 0x09,
+ 0x34, 0x06,
+ 0x35, 0x04,
+
+ //Out1 D-LPF th
+ 0x36, 0x70,
+ 0x37, 0x18,
+ 0x38, 0x65,
+ 0x39, 0x09,
+ 0x3a, 0x06,
+ 0x3b, 0x04,
+
+ //Indoor D-LPF th
+ 0x3c, 0x80,
+ 0x3d, 0x18,
+ 0x3e, 0x80,
+ 0x3f, 0x0c,
+ 0x40, 0x09,
+ 0x41, 0x06,
+
+ 0x42, 0x80,
+ 0x43, 0x18,
+ 0x44, 0x80,
+ 0x45, 0x12,
+ 0x46, 0x10,
+ 0x47, 0x10,
+ 0x48, 0x90,
+ 0x49, 0x40,
+ 0x4a, 0x80,
+ 0x4b, 0x13,
+ 0x4c, 0x10,
+ 0x4d, 0x11,
+ 0x4e, 0x80,
+ 0x4f, 0x30,
+ 0x50, 0x80,
+ 0x51, 0x13,
+ 0x52, 0x10,
+ 0x53, 0x13,
+ 0x54, 0x11,
+ 0x55, 0x17,
+ 0x56, 0x20,
+ 0x57, 0x20,
+ 0x58, 0x20,
+ 0x59, 0x30,
+ 0x5a, 0x18,
+ 0x5b, 0x00,
+ 0x5c, 0x00,
+ 0x60, 0x3f,
+ 0x62, 0x50,
+ 0x70, 0x06,
+
+ //Page 12
+ 0x03, 0x12,
+ 0x20, 0x0f,
+ 0x21, 0x0f,
+ 0x25, 0x30,
+ 0x28, 0x00,
+ 0x29, 0x00,
+ 0x2a, 0x00,
+ 0x30, 0x50,
+ 0x31, 0x18,
+ 0x32, 0x32,
+ 0x33, 0x40,
+ 0x34, 0x50,
+ 0x35, 0x70,
+ 0x36, 0xa0,
+
+ //Out2 th
+ 0x40, 0xa0,
+ 0x41, 0x40,
+ 0x42, 0xa0,
+ 0x43, 0x90,
+ 0x44, 0x90,
+ 0x45, 0x80,
+
+ //Out1 th
+ 0x46, 0xb0,
+ 0x47, 0x55,
+ 0x48, 0xa0,
+ 0x49, 0x90,
+ 0x4a, 0x90,
+ 0x4b, 0x80,
+
+ //In door th
+ 0x4c, 0xb0,
+ 0x4d, 0x40,
+ 0x4e, 0x90,
+ 0x4f, 0x90,
+ 0x50, 0xe6,
+ 0x51, 0x80,
+
+ //Dark1 th
+ 0x52, 0xb0,
+ 0x53, 0x60,
+ 0x54, 0xc0,
+ 0x55, 0xc0,
+ 0x56, 0xc0,
+ 0x57, 0x80,
+
+ //Dark2 th
+ 0x58, 0x90,
+ 0x59, 0x40,
+ 0x5a, 0xd0,
+ 0x5b, 0xd0,
+ 0x5c, 0xe0,
+ 0x5d, 0x80,
+
+ //Dark3 th
+ 0x5e, 0x88,
+ 0x5f, 0x40,
+ 0x60, 0xe0,
+ 0x61, 0xe6,
+ 0x62, 0xe6,
+ 0x63, 0x80,
+
+ 0x70, 0x15,
+ 0x71, 0x01,
+
+ 0x72, 0x18,
+ 0x73, 0x01,
+
+ 0x74, 0x25,
+ 0x75, 0x15,
+ 0x80, 0x30,
+ 0x81, 0x50,
+ 0x82, 0x80,
+ 0x85, 0x1a,
+ 0x88, 0x00,
+ 0x89, 0x00,
+ 0x90, 0x5d,
+
+ 0xc5, 0x30,
+ 0xc6, 0x2a,
+
+ 0xD0, 0x0c,
+ 0xD1, 0x80,
+ 0xD2, 0x67,
+ 0xD3, 0x00,
+ 0xD4, 0x00,
+ 0xD5, 0x02,
+ 0xD6, 0xff,
+ 0xD7, 0x18,
+ 0x3b, 0x06,
+ 0x3c, 0x06,
+
+ 0xc5, 0x30,
+ 0xc6, 0x2a,
+
+ //Page 13
+ 0x03, 0x13,
+ 0x10, 0xcb,
+ 0x11, 0x7b,
+ 0x12, 0x07,
+ 0x14, 0x00,
+
+ 0x20, 0x15,
+ 0x21, 0x13,
+ 0x22, 0x33,
+ 0x23, 0x04,
+ 0x24, 0x09,
+ 0x25, 0x08,
+ 0x26, 0x18,
+ 0x27, 0x30,
+ 0x29, 0x12,
+ 0x2a, 0x50,
+
+ //Low clip th
+ 0x2b, 0x06,
+ 0x2c, 0x06,
+ 0x25, 0x08,
+ 0x2d, 0x0c,
+ 0x2e, 0x12,
+ 0x2f, 0x12,
+
+ //Out2 Edge
+ 0x50, 0x10,
+ 0x51, 0x14,
+ 0x52, 0x10,
+ 0x53, 0x0c,
+ 0x54, 0x0f,
+ 0x55, 0x0c,
+
+ //Out1 Edge
+ 0x56, 0x10,
+ 0x57, 0x13,
+ 0x58, 0x10,
+ 0x59, 0x0c,
+ 0x5a, 0x0f,
+ 0x5b, 0x0c,
+
+ //Indoor Edge
+ 0x5c, 0x0a,
+ 0x5d, 0x0b,
+ 0x5e, 0x0a,
+ 0x5f, 0x08,
+ 0x60, 0x09,
+ 0x61, 0x08,
+
+ //Dark1 Edge
+ 0x62, 0x08,
+ 0x63, 0x08,
+ 0x64, 0x08,
+ 0x65, 0x06,
+ 0x66, 0x06,
+ 0x67, 0x06,
+
+ //Dark2 Edge
+ 0x68, 0x07,
+ 0x69, 0x07,
+ 0x6a, 0x07,
+ 0x6b, 0x05,
+ 0x6c, 0x05,
+ 0x6d, 0x05,
+
+ //Dark3 Edge
+ 0x6e, 0x07,
+ 0x6f, 0x07,
+ 0x70, 0x07,
+ 0x71, 0x05,
+ 0x72, 0x05,
+ 0x73, 0x05,
+
+ //2DY
+ 0x80, 0xfd,
+ 0x81, 0x1f,
+ 0x82, 0x05,
+ 0x83, 0x01,
+
+ 0x90, 0x15,
+ 0x91, 0x15,
+ 0x92, 0x33,
+ 0x93, 0x30,
+ 0x94, 0x03,
+ 0x95, 0x14,
+ 0x97, 0x30,
+ 0x99, 0x30,
+
+ 0xa0, 0x04,
+ 0xa1, 0x05,
+ 0xa2, 0x04,
+ 0xa3, 0x05,
+ 0xa4, 0x07,
+ 0xa5, 0x08,
+ 0xa6, 0x07,
+ 0xa7, 0x08,
+ 0xa8, 0x07,
+ 0xa9, 0x08,
+ 0xaa, 0x07,
+ 0xab, 0x08,
+
+ //Out2
+ 0xb0, 0x22,
+ 0xb1, 0x2a,
+ 0xb2, 0x28,
+ 0xb3, 0x22,
+ 0xb4, 0x2a,
+ 0xb5, 0x28,
+
+ //Out1
+ 0xb6, 0x22,
+ 0xb7, 0x2a,
+ 0xb8, 0x28,
+ 0xb9, 0x22,
+ 0xba, 0x2a,
+ 0xbb, 0x28,
+
+ 0xbc, 0x17,
+ 0xbd, 0x17,
+ 0xbe, 0x17,
+ 0xbf, 0x17,
+ 0xc0, 0x17,
+ 0xc1, 0x17,
+
+ //Dark1
+ 0xc2, 0x1e,
+ 0xc3, 0x12,
+ 0xc4, 0x10,
+ 0xc5, 0x1e,
+ 0xc6, 0x12,
+ 0xc7, 0x10,
+
+ //Dark2
+ 0xc8, 0x18,
+ 0xc9, 0x05,
+ 0xca, 0x05,
+ 0xcb, 0x18,
+ 0xcc, 0x05,
+ 0xcd, 0x05,
+
+ //Dark3
+ 0xce, 0x18,
+ 0xcf, 0x05,
+ 0xd0, 0x05,
+ 0xd1, 0x18,
+ 0xd2, 0x05,
+ 0xd3, 0x05,
+
+ //Page 14
+ 0x03, 0x14,
+ 0x10, 0x11,
+ 0x20, 0x40,
+ 0x21, 0x80,
+ 0x23, 0x80,
+ 0x22, 0x80,
+ 0x23, 0x80,
+ 0x24, 0x80,
+
+ 0x30, 0xc8,
+ 0x31, 0x2b,
+ 0x32, 0x00,
+ 0x33, 0x00,
+ 0x34, 0x90,
+
+ 0x40, 0x40,
+ 0x50, 0x2d,
+ 0x60, 0x28,
+ 0x70, 0x2d,
+
+ //Page 15
+ 0x03, 0x15,
+ 0x10, 0x0f,
+ 0x14, 0x52,
+ 0x15, 0x42,
+ 0x16, 0x32,
+ 0x17, 0x2f,
+
+ //CMC
+ 0x30, 0x8f,
+ 0x31, 0x59,
+ 0x32, 0x0a,
+ 0x33, 0x15,
+ 0x34, 0x5b,
+ 0x35, 0x06,
+ 0x36, 0x07,
+ 0x37, 0x40,
+ 0x38, 0x86,
+
+ //CMC OFS
+ 0x40, 0x95,
+ 0x41, 0x1f,
+ 0x42, 0x8a,
+ 0x43, 0x86,
+ 0x44, 0x0a,
+ 0x45, 0x84,
+ 0x46, 0x87,
+ 0x47, 0x9b,
+ 0x48, 0x23,
+
+ //CMC POFS
+ 0x50, 0x8c,
+ 0x51, 0x0c,
+ 0x52, 0x00,
+ 0x53, 0x07,
+ 0x54, 0x17,
+ 0x55, 0x9d,
+ 0x56, 0x00,
+ 0x57, 0x0b,
+ 0x58, 0x89,
+
+ 0x80, 0x03,
+ 0x85, 0x40,
+ 0x87, 0x02,
+ 0x88, 0x00,
+ 0x89, 0x00,
+ 0x8a, 0x00,
+
+ 0x03, 0x16,
+ 0x10, 0x31,//GMA_CTL
+ 0x18, 0x5e,//AG_ON
+ 0x19, 0x5d,//AG_OFF
+ 0x1A, 0x0e,//TIME_ON
+ 0x1B, 0x01,//TIME_OFF
+ 0x1C, 0xdc,//OUT_ON
+ 0x1D, 0xfe,//OUT_OFF
+ //GMA
+ 0x30, 0x00,
+ 0x31, 0x09,
+ 0x32, 0x15,
+ 0x33, 0x29,
+ 0x34, 0x53,
+ 0x35, 0x6c,
+ 0x36, 0x81,
+ 0x37, 0x94,
+ 0x38, 0xa4,
+ 0x39, 0xb3,
+ 0x3a, 0xc0,
+ 0x3b, 0xcb,
+ 0x3c, 0xd5,
+ 0x3d, 0xde,
+ 0x3e, 0xe6,
+ 0x3f, 0xee,
+ 0x40, 0xf5,
+ 0x41, 0xfc,
+ 0x42, 0xff,
+ //RGMA
+ 0x50, 0x00,
+ 0x51, 0x09,
+ 0x52, 0x1f,
+ 0x53, 0x37,
+ 0x54, 0x5b,
+ 0x55, 0x76,
+ 0x56, 0x8d,
+ 0x57, 0xa1,
+ 0x58, 0xb2,
+ 0x59, 0xbe,
+ 0x5a, 0xc9,
+ 0x5b, 0xd2,
+ 0x5c, 0xdb,
+ 0x5d, 0xe3,
+ 0x5e, 0xeb,
+ 0x5f, 0xf0,
+ 0x60, 0xf5,
+ 0x61, 0xf7,
+ 0x62, 0xf8,
+ //BGMA
+ 0x70, 0x00,
+ 0x71, 0x08,
+ 0x72, 0x17,
+ 0x73, 0x2f,
+ 0x74, 0x53,
+ 0x75, 0x6c,
+ 0x76, 0x81,
+ 0x77, 0x94,
+ 0x78, 0xa4,
+ 0x79, 0xb3,
+ 0x7a, 0xc0,
+ 0x7b, 0xcb,
+ 0x7c, 0xd5,
+ 0x7d, 0xde,
+ 0x7e, 0xe6,
+ 0x7f, 0xee,
+ 0x80, 0xf4,
+ 0x81, 0xfa,
+ 0x82, 0xff,
+
+ 0x03, 0x17,
+ 0xc4, 0x6e,
+ 0xc5, 0x5c,
+
+
+ 0x03, 0x18,
+ 0x12, 0x20,
+ 0x10, 0x07,
+ 0x11, 0x00,
+ 0x20, 0x02,
+ 0x21, 0x80,
+ 0x22, 0x00,
+ 0x23, 0xf0,
+ 0x24, 0x00,
+ 0x25, 0x06,
+ 0x26, 0x00,
+ 0x27, 0x00,
+ 0x28, 0x02,
+ 0x29, 0x86,
+ 0x2a, 0x00,
+ 0x2b, 0xf0,
+ 0x2c, 0x14,
+ 0x2d, 0x00,
+ 0x2e, 0x14,
+ 0x2f, 0x00,
+ 0x30, 0x65,
+ 0x03, 0x20,
+ 0x10, 0x1c,
+ 0x18, 0x38,
+ 0x20, 0x01,
+ 0x21, 0x30,
+ 0x22, 0x10,
+ 0x23, 0x00,
+ 0x24, 0x04,
+
+ 0x28, 0xff,
+ 0x29, 0xad,
+
+ 0x2a, 0xf0,
+ 0x2b, 0x34,
+ 0x30, 0x78,
+ 0x2c, 0xc3,
+ 0x2d, 0x5f,
+ 0x2e, 0x33,
+ // 0x30, 0xf8,
+ 0x32, 0x03,
+ 0x33, 0x2e,
+ 0x34, 0x30,
+ 0x35, 0xd4,
+ 0x36, 0xfe,
+ 0x37, 0x32,
+ 0x38, 0x04,
+ 0x47, 0xf0,
+
+ //Y_Frame TH
+ 0x50, 0x45,
+ 0x51, 0x88,
+
+ 0x56, 0x10,
+ 0x57, 0xb7,
+ 0x58, 0x14,
+ 0x59, 0x88,
+ 0x5a, 0x04,
+
+ 0x60, 0x55,
+ 0x61, 0x55,
+ 0x62, 0x6a,
+ 0x63, 0xa9,
+ 0x64, 0x6a,
+ 0x65, 0xa9,
+ 0x66, 0x6a,
+ 0x67, 0xa9,
+ 0x68, 0x6b,
+ 0x69, 0xe9,
+ 0x6a, 0x6a,
+ 0x6b, 0xa9,
+ 0x6c, 0x6a,
+ 0x6d, 0xa9,
+ 0x6e, 0x55,
+ 0x6f, 0x55,
+ // 0x70, 0x42,
+ 0x70, 0x42,
+ 0x71, 0xBb,
+
+ // haunting control
+ 0x76, 0x21,
+ 0x77, 0x02,
+ 0x78, 0x22,
+ 0x79, 0x2a,
+
+ 0x78, 0x24,
+ 0x79, 0x23,
+ 0x7a, 0x23,
+ 0x7b, 0x22,
+ 0x7d, 0x23,
+ 0x83, 0x01, //EXP Normal 33.33 fps
+ 0x84, 0x5f,
+ 0x85, 0x90,
+ 0x86, 0x01, //EXPMin 7500.00 fps
+ 0x87, 0xf4,
+ 0x88, 0x05, //EXP Max 8.33 fps
+ 0x89, 0x7e,
+ 0x8a, 0x40,
+ 0x8B, 0x75, //EXP100
+ 0x8C, 0x30,
+ 0x8D, 0x61, //EXP120
+ 0x8E, 0xa8,
+ 0x9c, 0x17, //EXP Limit 500.00 fps
+ 0x9d, 0x70,
+ 0x9e, 0x01, //EXP Unit
+ 0x9f, 0xf4,
+ 0xa0, 0x03,
+ 0xa1, 0xa9,
+ 0xa2, 0x80,
+ 0xb0, 0x1d,
+ 0xb1, 0x1a,
+ 0xb2, 0x80,
+ 0xb3, 0x1a,
+ 0xb4, 0x1a,
+ 0xb5, 0x44,
+ 0xb6, 0x2f,
+ 0xb7, 0x28,
+ 0xb8, 0x25,
+ 0xb9, 0x22,
+ 0xba, 0x21,
+ 0xbb, 0x20,
+ 0xbc, 0x1f,
+ 0xbd, 0x1f,
+ 0xc0, 0x30,
+ 0xc1, 0x20,
+ 0xc2, 0x20,
+ 0xc3, 0x20,
+ 0xc4, 0x08,
+ 0xc8, 0x60,
+ 0xc9, 0x40,
+
+ //Page 22
+ 0x03, 0x22, // Page 22
+ 0x10, 0xfd, // AWBCTL1
+ 0x11, 0x2e, // AWBCTL2
+ 0x19, 0x01,
+ 0x20, 0x30,
+ 0x21, 0x80,
+ 0x23, 0x08,
+ 0x24, 0x01,
+
+ 0x30, 0x80, // ULVL
+ 0x31, 0x80, // VLVL
+ 0x38, 0x11, // UVTH1
+ 0x39, 0x34, // UVTH2
+ 0x40, 0xf7, // YRANGE
+
+ 0x41, 0x77, // CDIFF
+ 0x42, 0x55, // CSUM2
+ 0x43, 0xf0,
+ 0x44, 0x66,
+ 0x45, 0x33,
+ 0x46, 0x01, // WHTPXLTH1
+ 0x47, 0x94,
+
+ 0x50, 0xb2,
+ 0x51, 0x81,
+ 0x52, 0x98,
+
+ 0x80, 0x38, // RGAIN
+ 0x81, 0x20, // GGAIN
+ 0x82, 0x38, // BGAIN
+
+ 0x83, 0x5e, // RMAX
+ 0x84, 0x20, // RMIN
+ 0x85, 0x53, // BMAX
+ 0x86, 0x15, // BMIN
+
+ 0x87, 0x54, // RMAXM
+ 0x88, 0x20, // RMINM
+ 0x89, 0x3f, // BMAXM
+ 0x8a, 0x1c, // BMINM
+
+ 0x8b, 0x54, // RMAXB
+ 0x8c, 0x3f, // RMINB
+ 0x8d, 0x24, // BMAXB
+ 0x8e, 0x1c, // BMINB
+
+ 0x8f, 0x60, // BGAINPARA1
+ 0x90, 0x5f, // BGAINPARA2
+ 0x91, 0x5c, // BGAINPARA3
+ 0x92, 0x4C, // BGAINPARA4
+ 0x93, 0x41, // BGAINPARA5
+ 0x94, 0x3b, // BGAINPARA6
+ 0x95, 0x36, // BGAINPARA7
+ 0x96, 0x30, // BGAINPARA8
+ 0x97, 0x27, // BGAINPARA9
+ 0x98, 0x20, // BGAINPARA10
+ 0x99, 0x1C, // BGAINPARA11
+ 0x9a, 0x19, // BGAINPARA12
+
+ 0x9b, 0x88, // BGAINBND1
+ 0x9c, 0x88, // BGAINBND2
+ 0x9d, 0x48, // RGAINTH1
+ 0x9e, 0x38, // RGAINTH2
+ 0x9f, 0x30, // RGAINTH3
+
+ 0xa0, 0x74, // RDELTA1
+ 0xa1, 0x35, // BDELTA1
+ 0xa2, 0xaf, // RDELTA2
+ 0xa3, 0xf7, // BDELTA2
+
+ 0xa4, 0x10, // AWBEXPLMT1
+ 0xa5, 0x50, // AWBEXPLMT2
+ 0xa6, 0xc4, // AWBEXPLMT3
+
+ 0xad, 0x40,
+ 0xae, 0x4a,
+
+ 0xaf, 0x2a,
+ 0xb0, 0x29,
+
+ 0xb1, 0x20,
+ 0xb4, 0xff,
+ 0xb8, 0x6b,
+ 0xb9, 0x00,
+
+
+ 0x03, 0x24,
+ 0x10, 0x01,
+ 0x18, 0x06,
+ 0x30, 0x06,
+ 0x31, 0x90,
+ 0x32, 0x25,
+ 0x33, 0xa2,
+ 0x34, 0x26,
+ 0x35, 0x58,
+ 0x36, 0x60,
+ 0x37, 0x00,
+ 0x38, 0x50,
+ 0x39, 0x00,
+
+ 0x03, 0x20,
+ 0x10, 0x9c,
+ 0x03, 0x22,
+ 0x10, 0xe9,
+
+ //Page 00
+ 0x03, 0x00,
+ 0x0e, 0x03,
+ 0x0e, 0x73,
+
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+
+ 0x03, 0x00,
+ 0x01, 0xf8,
+
+};
+
+uint8_t hi253_default_regs_init[]={
+
+0x01, 0xf9, //sleep on
+0x08, 0x0f, //Hi-Z on
+0x01, 0xf8, //sleep off
+0x03, 0x00, // Dummy 750us START
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00, // Dummy 750us END
+0x0e, 0x03, //PLL On
+0x0e, 0x73, //PLLx2
+0x03, 0x00, // Dummy 750us START
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00, // Dummy 750us END
+0x0e, 0x00, //PLL off
+0x01, 0xf1, //sleep on
+0x08, 0x00, //Hi-Z off
+0x01, 0xf3,
+0x01, 0xf1,
+// PAGE 20
+0x03, 0x20, //page 20
+0x10, 0x1c, //ae off
+// PAGE 22
+0x03, 0x22, //page 22
+0x10, 0x69, //awb off
+/////// PAGE 0 START ///////
+0x03, 0x00,
+0x10, 0x10, // Sub1/2_Preview2 Mode_H binning
+0x11, 0x90,
+0x12, 0x04,//00
+0x0b, 0xaa, // ESD Check Register
+0x0c, 0xaa, // ESD Check Register
+0x0d, 0xaa, // ESD Check Register
+0x20, 0x00, // Windowing start point Y
+0x21, 0x04,
+0x22, 0x00, // Windowing start point X
+0x23, 0x07,
+0x24, 0x04,
+0x25, 0xb0,
+0x26, 0x06,
+0x27, 0x40, // WINROW END
+0x40, 0x01, //Hblank 408
+0x41, 0x68,
+0x42, 0x00, //Vblank 20
+0x43, 0x14,
+0x45, 0x04,
+0x46, 0x18,
+0x47, 0xd8,
+//BLC
+0x80, 0x2e,
+0x81, 0x7e,
+0x82, 0x90,
+0x83, 0x00,
+0x84, 0x0c,
+0x85, 0x00,
+0x90, 0x0e, //BLC_TIME_TH_ON
+0x91, 0x0e, //BLC_TIME_TH_OFF
+0x92, 0x78, //BLC_AG_TH_ON
+0x93, 0x70, //BLC_AG_TH_OFF
+0x94, 0x75,
+0x95, 0x70,
+0x96, 0xdc,
+0x97, 0xfe,
+0x98, 0x38,
+//OutDoor BLC
+0x99, 0x42,
+0x9a, 0x42,
+0x9b, 0x42,
+0x9c, 0x42,
+//Dark BLC
+0xa0, 0x00,
+0xa2, 0x00,
+0xa4, 0x00,
+0xa6, 0x00,
+//Normal BLC
+0xa8, 0x42,
+0xaa, 0x42,
+0xac, 0x42,
+0xae, 0x42,
+/////// PAGE 2 START ///////
+0x03, 0x02, //Page 02
+0x10, 0x00, //Mode_test
+0x11, 0x00, //Mode_dead_test
+0x12, 0x03, //pwr_ctl_ctl1
+0x13, 0x03, //Mode_ana_test
+0x14, 0x00, //mode_memory
+0x16, 0x00, //dcdc_ctl1
+0x17, 0x8c, //dcdc_ctl2
+0x18, 0x4C, //analog_func1
+0x19, 0x00, //analog_func2
+0x1a, 0x39, //analog_func3
+0x1b, 0x00, //analog_func4
+0x1c, 0x09, //dcdc_ctl3
+0x1d, 0x40, //dcdc_ctl4
+0x1e, 0x30, //analog_func7
+0x1f, 0x10, //analog_func8
+0x20, 0x77, //pixel bias
+0x21, 0xde, //adc,asp bias
+0x22, 0xa7, //main,bus bias
+0x23, 0x30, //clamp
+0x24, 0x4a,
+0x25, 0x10,
+0x27, 0x3c,
+0x28, 0x00,
+0x29, 0x0c,
+0x2a, 0x80,
+0x2b, 0x80,
+0x2c, 0x02,
+0x2d, 0xa0,
+0x2e, 0x11,
+0x2f, 0xa1,
+0x30, 0x05, //swap_ctl
+0x31, 0x99,
+0x32, 0x00,
+0x33, 0x00,
+0x34, 0x22,
+0x38, 0x88,
+0x39, 0x88,
+0x50, 0x20,
+0x51, 0x03, //00 don't touch
+0x52, 0x01,
+0x53, 0xc1,
+0x54, 0x10,
+0x55, 0x1c,
+0x56, 0x11,
+0x58, 0x10,
+0x59, 0x0e,
+0x5d, 0xa2,
+0x5e, 0x5a,
+0x60, 0x87,
+0x61, 0x99,
+0x62, 0x88,
+0x63, 0x97,
+0x64, 0x88,
+0x65, 0x97,
+0x67, 0x0c,
+0x68, 0x0c,
+0x69, 0x0c,
+0x6a, 0xb4,
+0x6b, 0xc4,
+0x6c, 0xb5,
+0x6d, 0xc2,
+0x6e, 0xb5,
+0x6f, 0xc0,
+0x70, 0xb6,
+0x71, 0xb8,
+0x72, 0x89,
+0x73, 0x96,
+0x74, 0x89,
+0x75, 0x96,
+0x76, 0x89,
+0x77, 0x96,
+0x7c, 0x85,
+0x7d, 0xaf,
+0x80, 0x01,
+0x81, 0x7f,
+0x82, 0x13,
+0x83, 0x24,
+0x84, 0x7d,
+0x85, 0x81,
+0x86, 0x7d,
+0x87, 0x81,
+0x88, 0xab,
+0x89, 0xbc,
+0x8a, 0xac,
+0x8b, 0xba,
+0x8c, 0xad,
+0x8d, 0xb8,
+0x8e, 0xae,
+0x8f, 0xb2,
+0x90, 0xb3,
+0x91, 0xb7,
+0x92, 0x48,
+0x93, 0x54,
+0x94, 0x7d,
+0x95, 0x81,
+0x96, 0x7d,
+0x97, 0x81,
+0xa0, 0x02,
+0xa1, 0x7b,
+0xa2, 0x02,
+0xa3, 0x7b,
+0xa4, 0x7b,
+0xa5, 0x02,
+0xa6, 0x7b,
+0xa7, 0x02,
+0xa8, 0x85,
+0xa9, 0x8c,
+0xaa, 0x85,
+0xab, 0x8c,
+0xac, 0x10,
+0xad, 0x16,
+0xae, 0x10,
+0xaf, 0x16,
+0xb0, 0x99,
+0xb1, 0xa3,
+0xb2, 0xa4,
+0xb3, 0xae,
+0xb4, 0x9b,
+0xb5, 0xa2,
+0xb6, 0xa6,
+0xb7, 0xac,
+0xb8, 0x9b,
+0xb9, 0x9f,
+0xba, 0xa6,
+0xbb, 0xaa,
+0xbc, 0x9b,
+0xbd, 0x9f,
+0xbe, 0xa6,
+0xbf, 0xaa,
+0xc4, 0x2c,
+0xc5, 0x43,
+0xc6, 0x63,
+0xc7, 0x79,
+0xc8, 0x2d,
+0xc9, 0x42,
+0xca, 0x2d,
+0xcb, 0x42,
+0xcc, 0x64,
+0xcd, 0x78,
+0xce, 0x64,
+0xcf, 0x78,
+0xd0, 0x0a,
+0xd1, 0x09,
+0xd2, 0x20,
+0xd3, 0x00,
+0xd4, 0x0e, //DCDC_TIME_TH_ON don't touch
+0xd5, 0x0e, //DCDC_TIME_TH_OFF don't touch
+0xd6, 0x78, //DCDC_AG_TH_ON 78
+0xd7, 0x70, //DCDC_AG_TH_OFF 70
+0xe0, 0xc4,
+0xe1, 0xc4,
+0xe2, 0xc4,
+0xe3, 0xc4,
+0xe4, 0x00,
+0xe8, 0x80,
+0xe9, 0x40,
+0xea, 0x7f,
+0xf0, 0x01,
+0xf1, 0x01,
+0xf2, 0x01,
+0xf3, 0x01,
+0xf4, 0x01,
+/////// PAGE 3 ///////
+0x03, 0x03,
+0x10, 0x10,
+/////// PAGE 10 START ///////
+0x03, 0x10,
+0x10, 0x03, // CrYCbY // For Demoset 0x03
+0x12, 0x30,
+0x13, 0x0a,//add jacky open constrast
+0x20, 0x00,
+0x30, 0x00,
+0x31, 0x00,
+0x32, 0x00,
+0x33, 0x00,
+0x34, 0x30,
+0x35, 0x00,
+0x36, 0x00,
+0x38, 0x00,
+0x3e, 0x58,
+0x3f, 0x00,
+0x40, 0x88, // YOFS
+0x41, 0x00, // DYOFS
+0x48, 0xa0,// 84
+0x60, 0x67,//6b AG ratio jacky
+
+0x61, 0x7a, //7e //8e //88 //70
+0x62, 0x78, //7e //8e //88 //80
+0x63, 0x50, //Double_AG 50->30
+0x64, 0x41,
+0x66, 0x42,
+0x67, 0x20,
+0x6a, 0x80, //8a
+0x6b, 0x84, //74
+0x6c, 0x80, //7e //7a
+0x6d, 0x80, //8e
+/////// PAGE 11 START ///////
+0x03, 0x11,
+0x10, 0x7f,
+0x11, 0x40,
+0x12, 0x0a, // Blue Max-Filter Delete
+0x13, 0xbb,
+0x26, 0x31, // Double_AG 31->20
+0x27, 0x34, // Double_AG 34->22
+0x28, 0x0f,
+0x29, 0x10,
+0x2b, 0x30,
+0x2c, 0x32,
+//Out2 D-LPF th
+0x30, 0x70,
+0x31, 0x10,
+0x32, 0x58,
+0x33, 0x09,
+0x34, 0x06,
+0x35, 0x03,
+//Out1 D-LPF th
+0x36, 0x70,
+0x37, 0x18,
+0x38, 0x58,
+0x39, 0x09,
+0x3a, 0x06,
+0x3b, 0x03,
+//Indoor D-LPF th
+0x3c, 0x80,
+0x3d, 0x18,
+0x3e, 0x80,
+0x3f, 0x0c,
+0x40, 0x09,
+0x41, 0x06,
+
+0x42, 0x80,
+0x43, 0x18,
+0x44, 0x90,
+0x45, 0x12,
+0x46, 0x10,
+0x47, 0x10,
+0x48, 0x90,
+0x49, 0x40,
+0x4a, 0x80,
+0x4b, 0x13,
+0x4c, 0x10,
+0x4d, 0x11,
+0x4e, 0x80,
+0x4f, 0x30,
+0x50, 0x80,
+0x51, 0x13,
+0x52, 0x10,
+0x53, 0x13,
+0x54, 0x11,
+0x55, 0x17,
+0x56, 0x20,
+0x57, 0x20,
+0x58, 0x20,
+0x59, 0x30,
+0x5a, 0x18,
+0x5b, 0x00,
+0x5c, 0x00,
+0x60, 0x3f,
+0x62, 0x50,
+0x70, 0x06,
+/////// PAGE 12 START ///////
+0x03, 0x12,
+0x20, 0x0f,
+0x21, 0x0f,
+0x25, 0x00, //0x30
+0x28, 0x00,
+0x29, 0x00,
+0x2a, 0x00,
+0x30, 0x50,
+0x31, 0x18,
+0x32, 0x32,
+0x33, 0x40,
+0x34, 0x50,
+0x35, 0x70,
+0x36, 0xa0,
+//Out2 th
+0x40, 0xa0,
+0x41, 0x40,
+0x42, 0xa0,
+0x43, 0x90,
+0x44, 0x90,
+0x45, 0x80,
+//Out1 th
+0x46, 0xb0,
+0x47, 0x55,
+0x48, 0xa0,
+0x49, 0x90,
+0x4a, 0x90,
+0x4b, 0x80,
+//Indoor th
+0x4c, 0xb0,
+0x4d, 0x40,
+0x4e, 0x90,
+0x4f, 0x90,
+0x50, 0xe6,
+0x51, 0x80,
+//Dark1 th
+0x52, 0xb0,
+0x53, 0x60,
+0x54, 0xc0,
+0x55, 0xc0,
+0x56, 0xc0,
+0x57, 0xa0,
+//Dark2 th
+0x58, 0x90,
+0x59, 0x40,
+0x5a, 0xd0,
+0x5b, 0xd0,
+0x5c, 0xe0,
+0x5d, 0xa0,
+
+//Dark3 th
+0x5e, 0x88,
+0x5f, 0x40,
+0x60, 0xe0,
+0x61, 0xe6,
+0x62, 0xe6,
+0x63, 0xb0,
+
+0x70, 0x15,
+0x71, 0x01, //Don't Touch register
+0x72, 0x18,
+0x73, 0x01, //Don't Touch register
+0x74, 0x25,
+0x75, 0x15,
+0x80, 0x20,
+0x81, 0x40,
+0x82, 0x65,
+0x85, 0x1a,
+0x88, 0x00,
+0x89, 0x00,
+0x90, 0x5d, //For capture
+0x91, 0x88,
+0x98, 0x7d,
+0x99, 0x28,
+0x9A, 0x14,
+0x9B, 0xc8,
+0x9C, 0x02,
+0x9D, 0x1e,
+0x9E, 0x28,
+0x9F, 0x07,
+0xA0, 0x32,
+0xA4, 0x04,
+0xA5, 0x0e,
+0xA6, 0x0c,
+0xA7, 0x04,
+0xA8, 0x3c,
+
+0xAA, 0x14,
+0xAB, 0x11,
+0xAC, 0x0f,
+0xAD, 0x16,
+0xAE, 0x15,
+0xAF, 0x14,
+
+0xB1, 0xaa,
+0xB2, 0x96,
+0xB3, 0x28,
+//Dont Touch register
+0xB8, 0x78,
+0xB9, 0xa0,
+0xBA, 0xb4,
+0xBB, 0x14,
+0xBC, 0x14,
+0xBD, 0x14,
+0xBE, 0x64,
+0xBF, 0x64,
+0xC0, 0x64,
+0xC1, 0x64,
+0xC2, 0x04,
+0xC3, 0x03,
+0xC4, 0x0c,
+0xC5, 0x30,
+0xC6, 0x2a,
+0xD0, 0x0c, //CI Option/CI DPC
+0xD1, 0x80,
+0xD2, 0x67,
+0xD3, 0x00,
+0xD4, 0x00,
+0xD5, 0x02,
+0xD6, 0xff,
+0xD7, 0x18,
+//End
+0x3b, 0x06,
+0x3c, 0x06,
+0xc5, 0x00,//55->48
+0xc6, 0x00,//48->40
+/////// PAGE 13 START ///////
+0x03, 0x13,
+//Edge
+0x10, 0xcb,
+0x11, 0x7b,
+0x12, 0x07,
+0x14, 0x00,
+0x20, 0x15,
+0x21, 0x13,
+0x22, 0x33,
+0x23, 0x04,
+0x24, 0x09,
+0x25, 0x08,
+0x26, 0x18,
+0x27, 0x30,
+0x29, 0x12,
+0x2a, 0x50,
+
+//Low clip th
+0x2b, 0x06,
+0x2c, 0x06,
+0x25, 0x08,
+0x2d, 0x0c,
+0x2e, 0x12,
+0x2f, 0x12,
+
+//Out2 Edge
+0x50, 0x10,
+0x51, 0x14,
+0x52, 0x10,
+0x53, 0x0c,
+0x54, 0x0f,
+0x55, 0x0c,
+
+//Out1 Edge
+0x56, 0x10,
+0x57, 0x13,
+0x58, 0x10,
+0x59, 0x0c,
+0x5a, 0x0f,
+0x5b, 0x0c,
+
+//Indoor Edge
+0x5c, 0x0a,
+0x5d, 0x0b,
+0x5e, 0x0a,
+0x5f, 0x08,
+0x60, 0x09,
+0x61, 0x08,
+
+//Dark1 Edge
+0x62, 0x08,
+0x63, 0x08,
+0x64, 0x08,
+0x65, 0x06,
+0x66, 0x06,
+0x67, 0x06,
+
+//Dark2 Edge
+0x68, 0x07,
+0x69, 0x07,
+0x6a, 0x07,
+0x6b, 0x05,
+0x6c, 0x05,
+0x6d, 0x05,
+//Dark3 Edge
+0x6e, 0x07,
+0x6f, 0x07,
+0x70, 0x07,
+0x71, 0x05,
+0x72, 0x05,
+0x73, 0x05,
+0x80, 0xfd,
+0x81, 0x1f,
+0x82, 0x05,
+0x83, 0x01,
+0x90, 0x15,
+0x91, 0x15,
+0x92, 0x33,
+0x93, 0x30,
+0x94, 0x03,
+0x95, 0x14,
+0x97, 0x30,
+0x99, 0x30,
+
+0xa0, 0x04,
+0xa1, 0x05,
+0xa2, 0x04,
+0xa3, 0x05,
+0xa4, 0x07,
+0xa5, 0x08,
+0xa6, 0x07,
+0xa7, 0x08,
+0xa8, 0x07,
+0xa9, 0x08,
+0xaa, 0x07,
+0xab, 0x08,
+
+//Out2
+0xb0, 0x22,
+0xb1, 0x2a,
+0xb2, 0x28,
+0xb3, 0x22,
+0xb4, 0x2a,
+0xb5, 0x28,
+
+//Out1
+0xb6, 0x22,
+0xb7, 0x2a,
+0xb8, 0x28,
+0xb9, 0x22,
+0xba, 0x2a,
+0xbb, 0x28,
+
+//Indoor
+0xbc, 0x07,
+0xbd, 0x07,
+0xbe, 0x07,
+0xbf, 0x07,
+0xc0, 0x07,
+0xc1, 0x07,
+
+//Dark1
+0xc2, 0x0e,
+0xc3, 0x02,
+0xc4, 0x00,
+0xc5, 0x0e,
+0xc6, 0x02,
+0xc7, 0x00,
+
+//Dark2
+0xc8, 0x18,
+0xc9, 0x05,
+0xca, 0x05,
+0xcb, 0x18,
+0xcc, 0x05,
+0xcd, 0x05,
+
+//Dark3
+0xce, 0x18,
+0xcf, 0x05,
+0xd0, 0x05,
+0xd1, 0x18,
+0xd2, 0x05,
+0xd3, 0x05,
+
+/////// PAGE 14 START ///////
+0x03, 0x14,
+0x10, 0x11,
+0x14, 0x80, // GX
+0x15, 0x80, // GY
+0x16, 0x80, // RX
+0x17, 0x80, // RY
+0x18, 0x80, // BX
+0x19, 0x80, // BY
+0x20, 0x80, //X 60 //a0
+0x21, 0x80, //Y
+0x22, 0x78,
+0x23, 0x80,
+0x24, 0x80,
+0x30, 0xc8,
+0x31, 0x2b,
+0x32, 0x00,
+0x33, 0x00,
+0x34, 0x90,
+0x40, 0x56, //0x48//65
+0x50, 0x44, //0x34
+0x60, 0x46, //0x29
+0x70, 0x44, //0x34
+/////// PAGE 15 START ///////
+0x03, 0x15,
+0x10, 0x0f,
+//Rstep H 16
+//Rstep L 14
+0x14, 0x4f, //CMCOFSGH
+0x15, 0x32, //CMCOFSGM
+0x16, 0x24, //CMCOFSGL
+0x17, 0x2f, //CMC SIGN
+//CMC
+0x30, 0x91,
+0x31, 0x6a,
+0x32, 0x18,
+0x33, 0x17,
+0x34, 0x53,
+0x35, 0x08,
+0x36, 0x08,
+0x37, 0x3e,
+0x38, 0x87,
+//CMC OFS
+0x40, 0xa3,
+0x41, 0x3a,
+0x42, 0x93,
+0x43, 0x0b,
+0x44, 0x86,
+0x45, 0x06,
+0x46, 0x00,
+0x47, 0xa2,
+0x48, 0x22,
+//CMC POFS
+0x50, 0x82,
+0x51, 0x06,
+0x52, 0x83,
+0x53, 0x03,
+0x54, 0x11,
+0x55, 0x8e,
+0x56, 0x00,
+0x57, 0x06,
+0x58, 0x87,
+
+0x80, 0x03,
+0x85, 0x40,
+0x87, 0x02,
+0x88, 0x00,
+0x89, 0x00,
+0x8a, 0x00,
+
+/////// PAGE 16 START ///////
+0x03, 0x16,
+0x10, 0x31,//GMA_CTL
+0x18, 0x37,//AG_ON
+0x19, 0x36,//AG_OFF
+0x1A, 0x0e,//TIME_ON
+0x1B, 0x01,//TIME_OFF
+0x1C, 0xdc,//OUT_ON
+0x1D, 0xfe,//OUT_OFF
+//GMA
+0x30, 0x00,
+0x31, 0x08,
+0x32, 0x1f,
+0x33, 0x35,
+0x34, 0x55,
+0x35, 0x73,
+0x36, 0x86,
+0x37, 0x96,
+0x38, 0xa6,
+0x39, 0xb6,
+0x3a, 0xc5,
+0x3b, 0xd0,
+0x3c, 0xd8,
+0x3d, 0xe0,
+0x3e, 0xe8,
+0x3f, 0xf0,
+0x40, 0xf7,
+0x41, 0xfd,
+0x42, 0xff,
+//RGMA
+0x50, 0x00,
+0x51, 0x05,
+0x52, 0x1b,
+0x53, 0x36,
+0x54, 0x5a,
+0x55, 0x75,
+0x56, 0x89,
+0x57, 0x9c,
+0x58, 0xac,
+0x59, 0xb8,
+0x5a, 0xc7,
+0x5b, 0xd2,
+0x5c, 0xdc,
+0x5d, 0xe5,
+0x5e, 0xed,
+0x5f, 0xf2,
+0x60, 0xf7,
+0x61, 0xf9,
+0x62, 0xfa,
+//BGMA
+0x70, 0x00,
+0x71, 0x0a,
+0x72, 0x19,
+0x73, 0x31,
+0x74, 0x55,
+0x75, 0x6e,
+0x76, 0x83,
+0x77, 0x96,
+0x78, 0xa6,
+0x79, 0xb5,
+0x7a, 0xc2,
+0x7b, 0xcd,
+0x7c, 0xd7,
+0x7d, 0xe0,
+0x7e, 0xe8,
+0x7f, 0xf0,
+0x80, 0xf6,
+0x81, 0xfc,
+0x82, 0xff,
+/////// PAGE 17 START ///////
+0x03, 0x17,
+0x10, 0xf7,
+/////// PAGE 18 START ///////
+
+ 0x03, 0x18,
+ 0x10, 0x00,
+
+/////// PAGE 20 START ///////
+0x03, 0x20,
+0x11, 0x1c,
+0x18, 0x30,
+0x1a, 0x08,
+0x20, 0x01, //05_lowtemp Y Mean off
+0x21, 0x30,
+0x22, 0x10,
+0x23, 0x00,
+0x24, 0x00, //Uniform Scene Off
+0x28, 0xe7,
+0x29, 0x0d, //20100305 ad->0d
+0x2a, 0xff,
+0x2b, 0x34, //f4->Adaptive on //04 off
+0x2c, 0xc2,
+0x2d, 0xcf, //fe->AE Speed option
+0x2e, 0x33,
+0x30, 0x78, //f8
+0x32, 0x03,
+0x33, 0x2e,
+0x34, 0x30,
+0x35, 0xd4,
+0x36, 0xfe,
+0x37, 0x32,
+0x38, 0x04,
+0x39, 0x22, //AE_escapeC10
+0x3a, 0xde, //AE_escapeC11
+0x3b, 0x22, //AE_escapeC1
+0x3c, 0xde, //AE_escapeC2
+0x50, 0x45,
+0x51, 0x88,
+0x56, 0x03,
+0x57, 0xf7,
+0x58, 0x14,
+0x59, 0x88,
+0x5a, 0x04,
+/*
+0x60, 0xff,
+0x61, 0xff,
+0x62, 0xea,
+0x63, 0xab,
+0x64, 0xea,
+0x65, 0xab,
+0x66, 0xea,//eb
+0x67, 0x2b,//eb
+0x68, 0xe8,//eb
+0x69, 0x2b,//eb
+0x6a, 0xea,
+0x6b, 0xab,
+0x6c, 0xea,
+0x6d, 0xab,
+0x6e, 0xff,
+0x6f, 0xff,
+*/
+
+0x60, 0x55,
+0x61, 0x55,
+0x62, 0x6a,
+0x63, 0xa9,
+0x64, 0x6a,
+0x65, 0xa9,
+0x66, 0x6b,//eb
+0x67, 0xe9,//eb
+0x68, 0x6b,//eb
+0x69, 0xe9,//eb
+0x6a, 0x6a,
+0x6b, 0xa9,
+0x6c, 0x6a,
+0x6d, 0xa9,
+0x6e, 0x55,
+0x6f, 0x55,
+
+0x70, 0x6e, //6e
+0x71, 0x89, //00 //-4
+0x76, 0x43,
+0x77, 0xe2, //04 //f2
+0x78, 0x23, //Yth1
+0x79, 0x46, //Yth2 //46
+0x7a, 0x23, //23
+0x7b, 0x22, //22
+0x7d, 0x23,
+0x83, 0x01, //EXP Normal 33.33 fps
+0x84, 0x5f,
+0x85, 0x90,
+0x86, 0x01, //EXPMin 7500.00 fps
+0x87, 0xf4,
+0x88, 0x06, //EXP Max 8.33 fps
+0x89, 0x68,
+0x8a, 0xa0,
+0x8B, 0x75, //EXP100
+0x8C, 0x30,
+0x8D, 0x61, //EXP120
+0x8E, 0xa8,
+0x9c, 0x17, //EXP Limit 500.00 fps
+0x9d, 0x70,
+0x9e, 0x01, //EXP Unit
+0x9f, 0xf4,
+0xa3, 0x00, //Outdoor Int
+0xa4, 0x61,
+
+0xb0, 0x18,
+0xb1, 0x14, //ADC 400->560
+0xb2, 0x80,//a0
+0xb3, 0x18,
+0xb4, 0x1a,
+0xb5, 0x44,
+0xb6, 0x2f,
+0xb7, 0x28,
+0xb8, 0x25,
+0xb9, 0x22,
+0xba, 0x21,
+0xbb, 0x20,
+0xbc, 0x1f,
+0xbd, 0x1f,
+0xc0, 0x14,
+0xc1, 0x1f,
+0xc2, 0x1f,
+0xc3, 0x18, //2b
+0xc4, 0x10, //08
+0xc8, 0x80,
+0xc9, 0x40,
+/////// PAGE 22 START ///////
+0x03, 0x22,
+0x10, 0xfd,
+0x11, 0x2e,
+0x19, 0x01,
+0x20, 0x30,
+0x21, 0x80,
+0x24, 0x01,
+0x30, 0x80,
+0x31, 0x80,
+0x38, 0x11,
+0x39, 0x34,
+0x40, 0xf7,
+0x41, 0x55,
+0x42, 0x33, //43
+
+0x43, 0xf6,
+0x44, 0x55, //44
+0x45, 0x44, //33
+
+0x46, 0x00,
+0x50, 0xb2,
+0x51, 0x81,
+0x52, 0x98,
+0x80, 0x38,//40
+0x81, 0x20,
+0x82, 0x30,
+
+0x83, 0x52,
+0x84, 0x16,//1E
+0x85, 0x5A,//52
+0x86, 0x23,
+
+0x87, 0x45,
+0x88, 0x35,
+0x89, 0x47,
+0x8a, 0x2b,
+
+0x8b, 0x40,
+0x8c, 0x33,
+0x8d, 0x38,
+0x8e, 0x30,
+
+0x8f, 0x53,
+0x90, 0x52,
+0x91, 0x50,
+0x92, 0x4c,
+0x93, 0x43,
+0x94, 0x37,
+0x95, 0x2f,//2a
+0x96, 0x26,//24
+0x97, 0x21,//20
+0x98, 0x1e,
+0x99, 0x1f,
+0x9a, 0x20,
+0x9b, 0x88,
+0x9c, 0x88,
+0x9d, 0x48,
+0x9e, 0x38,
+0x9f, 0x30,
+0xa0, 0x60,
+0xa1, 0x34,
+0xa2, 0x7f,
+0xa3, 0xff,
+0xa4, 0x14,
+0xa5, 0x2c,
+0xa6, 0xcf,
+0xad, 0x40,
+0xae, 0x4a,
+0xaf, 0x28,
+0xb0, 0x26,
+0xb1, 0x00,
+0xb8, 0xa0,
+0xb9, 0x00,
+// PAGE 20
+0x03, 0x20, //page 20
+0x10, 0x9c, //ae off
+// PAGE 22
+0x03, 0x22, //page 22
+0x10, 0xe9, //awb off
+// PAGE 0
+0x03, 0x00,
+0x0e, 0x03, //PLL On
+0x0e, 0x73, //PLLx2
+0x03, 0x00, // Dummy 750us
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+ 0x03, 0x00,
+ 0x01, 0xf8,
+
+};
+
+#endif
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/hi253/hi253-syx-gt7206.h b/drivers/media/video/wmt_v4l2/sensors/hi253/hi253-syx-gt7206.h
new file mode 100755
index 00000000..814d61ea
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/hi253/hi253-syx-gt7206.h
@@ -0,0 +1,2159 @@
+#ifndef YACD511_H
+#define YACD511_H
+
+// Scene Mode
+uint8_t hi253_scene_mode_auto[] = {
+ 0x03,0x10,
+ 0x41,0x00,
+ 0x03,0x20,
+ 0xb2,0x80,
+ 0xff,0xff,
+};
+
+uint8_t hi253_scene_mode_night[] = {
+ 0x03,0x10,
+ 0x41,0x15,
+ 0x03,0x20,
+ 0xb2,0xf0,
+ 0xff,0xff,
+};
+
+
+// White Balance
+uint8_t hi253_wb_auto [] = {
+ 0x03,0x22,
+ 0x11,0x2e,
+ 0x83,0x53, // RMAX
+ 0x84, 0x16, // RMIN
+ 0x85, 0x5a, // BMAX
+ 0x86, 0x25, // BMIN
+ 0xff,0xff,
+};
+
+uint8_t hi253_wb_incandescent [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x2a,
+ 0x82,0x3f,
+ 0x83,0x35,
+ 0x84,0x28,
+ 0x85,0x45,
+ 0x86,0x3b,
+ 0xff,0xff,
+};
+
+uint8_t hi253_wb_fluorescent [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x20,
+ 0x82,0x4d,
+ 0x83,0x25,
+ 0x84,0x1b,
+ 0x85,0x55,
+ 0x86,0x48,
+ 0xff,0xff,
+};
+
+uint8_t hi253_wb_daylight [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x3d,
+ 0x82,0x2e,
+ 0x83,0x40,
+ 0x84,0x33,
+ 0x85,0x33,
+ 0x86,0x28,
+ 0xff,0xff,
+};
+
+uint8_t hi253_wb_cloudy [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x50,
+ 0x82,0x25,
+ 0x83,0x55,
+ 0x84,0x4b,
+ 0x85,0x28,
+ 0x86,0x20,
+ 0xff,0xff,
+};
+
+
+// Exposure
+uint8_t hi253_exposure_neg6[] = {
+ 0x03,0x10,
+ 0x40,0xa8,
+};
+
+uint8_t hi253_exposure_neg3[] = {
+ 0x03,0x10,
+ 0x40,0x98,
+};
+
+uint8_t hi253_exposure_zero[] = {
+ 0x03,0x10,
+ 0x40,0x88,
+};
+
+uint8_t hi253_exposure_pos3[] = {
+ 0x03,0x10,
+ 0x40,0x08,
+};
+
+uint8_t hi253_exposure_pos6[] = {
+ 0x03,0x10,
+ 0x40,0x28,
+};
+
+
+// Resolution
+uint8_t hi253_1600_1200_rets[]={
+ 0x03, 0x00,
+ 0x10, 0x00,
+ 0x12, 0x04,
+ 0x20, 0x00,
+ 0x21, 0x0a,
+ 0x22, 0x00,
+ 0x23, 0x0a,
+
+ 0x03, 0x18,
+ 0x10, 0x00,
+ 0xff, 0xff,
+};
+
+uint8_t hi253_1280_720_regs[]={
+};
+
+uint8_t hi253_800_600_regs[]={
+
+ 0x03, 0x00,
+ 0x10, 0x10,
+ 0x12, 0x04,
+
+ 0x03, 0x18,
+ 0x10, 0x00,
+ 0xff, 0xff,
+};
+
+uint8_t hi253_640_480_regs[]={
+ 0x03, 0x00,
+ 0x10, 0x10,
+ 0x12, 0x04,
+
+ 0x03, 0x18,
+ 0x12, 0x20,
+ 0x10, 0x07,
+ 0x11, 0x00,
+ 0x20, 0x05,
+ 0x21, 0x00,
+ 0x22, 0x03,
+ 0x23, 0xc0,
+ 0x24, 0x00,
+ 0x25, 0x04,
+ 0x26, 0x00,
+ 0x27, 0x00,
+ 0x28, 0x05,
+ 0x29, 0x04,
+ 0x2a, 0x03,
+ 0x2b, 0xc0,
+ 0x2c, 0x0a,
+ 0x2d, 0x00,
+ 0x2e, 0x0a,
+ 0x2f, 0x00,
+ 0x30, 0x45,
+ 0xff, 0xff,
+};
+
+uint8_t hi253_320_240_regs[]= {
+ 0x01, 0xf9,
+ 0x08, 0x0f,
+ 0x01, 0xf8,
+
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+
+ 0x0e, 0x00,
+
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+
+ 0x0e, 0x00,
+ 0x01, 0xf1,
+ 0x08, 0x00,
+ 0x01, 0xf3,
+ 0x01, 0xf1,
+
+ 0x03, 0x20,
+ 0x10, 0x0c,
+ 0x03, 0x22,
+ 0x10, 0x69,
+
+ //Page 00
+ 0x03, 0x00,
+ 0x10, 0x11, //lxh
+ 0x11, 0x90,
+ 0x12, 0x04,
+ 0x0b, 0xaa,
+ 0x0c, 0xaa,
+ 0x0d, 0xaa,
+ 0x20, 0x00,
+ 0x21, 0x0a, //lxh
+ 0x22, 0x00,
+ 0x23, 0x0a, //lxh
+ 0x24, 0x04,
+ 0x25, 0xb0,
+ 0x26, 0x06,
+ 0x27, 0x40,
+
+ 0x40, 0x00,
+ 0x41, 0xe8,//lxh
+ 0x42, 0x00,
+ 0x43, 0x14,//lxh
+
+ 0x45, 0x04,
+ 0x46, 0x18,
+ 0x47, 0xd8,
+
+ 0xe1, 0x0f,
+
+ //BLC
+ 0x80, 0x2e,
+ 0x81, 0x7e,
+ 0x82, 0x90,
+ 0x83, 0x00,
+ 0x84, 0x0c,
+ 0x85, 0x00,
+ 0x90, 0x0c,
+ 0x91, 0x0c,
+ 0x92, 0x98,
+ 0x93, 0x90,
+ 0x94, 0x75,
+ 0x95, 0x70,
+ 0x96, 0xdc,
+ 0x97, 0xfe,
+ 0x98, 0x20,
+
+ //OutDoor BLC
+ 0x99,0x42,
+ 0x9a,0x42,
+ 0x9b,0x42,
+ 0x9c,0x42,
+
+ //Dark BLC
+ 0xa0, 0x02,
+ 0xa2, 0x02,
+ 0xa4, 0x02,
+ 0xa6, 0x02,
+
+ //Normal BLC
+ 0xa8, 0x42,
+ 0xaa, 0x42,
+ 0xac, 0x42,
+ 0xae, 0x42,
+
+ //Page 02
+ 0x03, 0x02,
+ 0x12, 0x03,
+ 0x13, 0x03,
+ 0x16, 0x00,
+ 0x17, 0x8C,
+ 0x18, 0x4c,
+ 0x19, 0x00,
+ 0x1a, 0x39,
+ 0x1c, 0x09,
+ 0x1d, 0x40,
+ 0x1e, 0x30,
+ 0x1f, 0x10,
+ 0x20, 0x77,
+ 0x21, 0xde,
+ 0x22, 0xa7,
+ 0x23, 0x30,
+ 0x27, 0x3c,
+ 0x2b, 0x80,
+ 0x2e, 0x00,
+ 0x2f, 0x00,
+ 0x30, 0x05,
+ 0x50, 0x20,
+ 0x52, 0x01,
+ 0x53, 0xc1,
+ 0x55, 0x1c,
+ 0x56, 0x11,
+ 0x5d, 0xA2,
+ 0x5e, 0x5a,
+ 0x60, 0x87,
+ 0x61, 0x99,
+ 0x62, 0x88,
+ 0x63, 0x97,
+ 0x64, 0x88,
+ 0x65, 0x97,
+ 0x67, 0x0c,
+ 0x68, 0x0c,
+ 0x69, 0x0c,
+ 0x72, 0x89,
+ 0x73, 0x96,
+ 0x74, 0x89,
+ 0x75, 0x96,
+ 0x76, 0x89,
+ 0x77, 0x96,
+ 0x7C, 0x85,
+ 0x7d, 0xaf,
+ 0x80, 0x01,
+ 0x81, 0x7f,
+ 0x82, 0x13,
+ 0x83, 0x24,
+ 0x84, 0x7d,
+ 0x85, 0x81,
+ 0x86, 0x7d,
+ 0x87, 0x81,
+ 0x92, 0x48,
+ 0x93, 0x54,
+ 0x94, 0x7d,
+ 0x95, 0x81,
+ 0x96, 0x7d,
+ 0x97, 0x81,
+ 0xa0, 0x02,
+ 0xa1, 0x7b,
+ 0xa2, 0x02,
+ 0xa3, 0x7b,
+ 0xa4, 0x7b,
+ 0xa5, 0x02,
+ 0xa6, 0x7b,
+ 0xa7, 0x02,
+ 0xa8, 0x85,
+ 0xa9, 0x8c,
+ 0xaa, 0x85,
+ 0xab, 0x8c,
+ 0xac, 0x10,
+ 0xad, 0x16,
+ 0xae, 0x10,
+ 0xaf, 0x16,
+ 0xb0, 0x99,
+ 0xb1, 0xa3,
+ 0xb2, 0xa4,
+ 0xb3, 0xae,
+ 0xb4, 0x9b,
+ 0xb5, 0xa2,
+ 0xb6, 0xa6,
+ 0xb7, 0xac,
+ 0xb8, 0x9b,
+ 0xb9, 0x9f,
+ 0xba, 0xa6,
+ 0xbb, 0xaa,
+ 0xbc, 0x9b,
+ 0xbd, 0x9f,
+ 0xbe, 0xa6,
+ 0xbf, 0xaa,
+ 0xc4, 0x2c,
+ 0xc5, 0x43,
+ 0xc6, 0x63,
+ 0xc7, 0x79,
+ 0xc8, 0x2d,
+ 0xc9, 0x42,
+ 0xca, 0x2d,
+ 0xcb, 0x42,
+ 0xcc, 0x64,
+ 0xcd, 0x78,
+ 0xce, 0x64,
+ 0xcf, 0x78,
+ 0xd0, 0x0a,
+ 0xd1, 0x09,
+ 0xd4, 0x0c,
+ 0xd5, 0x0c,
+ 0xd6, 0x98,
+ 0xd7, 0x90,//lxh
+ 0xe0, 0xc4,
+ 0xe1, 0xc4,
+ 0xe2, 0xc4,
+ 0xe3, 0xc4,
+ 0xe4, 0x00,
+ 0xe8, 0x80,
+ 0xe9, 0x40,
+ 0xea, 0x7f,
+ 0xf0, 0x01,
+ 0xf1, 0x01,
+ 0xf2, 0x01,
+ 0xf3, 0x01,
+ 0xf4, 0x01,
+
+ //PAGE10
+ 0x03, 0x10,
+ 0x10, 0x03, //lxh
+ 0x11, 0x03, //lxh,normal
+ 0x12, 0x30,
+ 0x13, 0x02,
+ 0x20, 0x00,
+
+ 0x40, 0x88,
+ 0x41, 0x05,
+ 0x48, 0x88,// 84
+ 0x50, 0x90,
+ 0x30, 0x00,
+ 0x31, 0x00,
+ 0x32, 0x00,
+ 0x33, 0x00,
+
+ 0x34, 0x30,
+ 0x35, 0x00,
+ 0x36, 0x00,
+ 0x38, 0x00,
+ 0x3e, 0x58,
+ 0x3f, 0x00,
+
+ //Saturation
+ 0x60, 0x67,
+ 0x61, 0x84,// 74
+ 0x62, 0x80,// 76
+ 0x63, 0x50,
+ 0x64, 0x41,
+
+ 0x66, 0x33,
+ 0x67, 0x00,
+
+ 0x6a, 0x90,
+ 0x6b, 0x80,
+ 0x6c, 0x80,
+ 0x6d, 0xa0,
+
+ 0x76, 0x01,
+ 0x74, 0x66,
+ 0x79, 0x06,
+
+ //Page 11
+ 0x03, 0x11,
+ 0x10, 0x7f,//lxh,3f
+ 0x11, 0x40,
+ 0x12, 0xba,
+ 0x13, 0xcb,
+ 0x26, 0x20,
+ 0x27, 0x22,
+ 0x28, 0x0f,
+ 0x29, 0x10,
+ 0x2b, 0x30,
+ 0x2c, 0x32,
+
+ //Out2 D-LPF th
+ 0x30, 0x70,
+ 0x31, 0x10,
+ 0x32, 0x65,
+ 0x33, 0x09,
+ 0x34, 0x06,
+ 0x35, 0x04,
+
+ //Out1 D-LPF th
+ 0x36, 0x70,
+ 0x37, 0x18,
+ 0x38, 0x65,
+ 0x39, 0x09,
+ 0x3a, 0x06,
+ 0x3b, 0x04,
+
+ //Indoor D-LPF th
+ 0x3c, 0x80,
+ 0x3d, 0x18,
+ 0x3e, 0x80,
+ 0x3f, 0x0c,
+ 0x40, 0x09,
+ 0x41, 0x06,
+
+ 0x42, 0x80,
+ 0x43, 0x18,
+ 0x44, 0x80,
+ 0x45, 0x12,
+ 0x46, 0x10,
+ 0x47, 0x10,
+ 0x48, 0x90,
+ 0x49, 0x40,
+ 0x4a, 0x80,
+ 0x4b, 0x13,
+ 0x4c, 0x10,
+ 0x4d, 0x11,
+ 0x4e, 0x80,
+ 0x4f, 0x30,
+ 0x50, 0x80,
+ 0x51, 0x13,
+ 0x52, 0x10,
+ 0x53, 0x13,
+ 0x54, 0x11,
+ 0x55, 0x17,
+ 0x56, 0x20,
+ 0x57, 0x20,
+ 0x58, 0x20,
+ 0x59, 0x30,
+ 0x5a, 0x18,
+ 0x5b, 0x00,
+ 0x5c, 0x00,
+ 0x60, 0x3f,
+ 0x62, 0x50,
+ 0x70, 0x06,
+
+ //Page 12
+ 0x03, 0x12,
+ 0x20, 0x0f,
+ 0x21, 0x0f,
+ 0x25, 0x30,
+ 0x28, 0x00,
+ 0x29, 0x00,
+ 0x2a, 0x00,
+ 0x30, 0x50,
+ 0x31, 0x18,
+ 0x32, 0x32,
+ 0x33, 0x40,
+ 0x34, 0x50,
+ 0x35, 0x70,
+ 0x36, 0xa0,
+
+ //Out2 th
+ 0x40, 0xa0,
+ 0x41, 0x40,
+ 0x42, 0xa0,
+ 0x43, 0x90,
+ 0x44, 0x90,
+ 0x45, 0x80,
+
+ //Out1 th
+ 0x46, 0xb0,
+ 0x47, 0x55,
+ 0x48, 0xa0,
+ 0x49, 0x90,
+ 0x4a, 0x90,
+ 0x4b, 0x80,
+
+ //In door th
+ 0x4c, 0xb0,
+ 0x4d, 0x40,
+ 0x4e, 0x90,
+ 0x4f, 0x90,
+ 0x50, 0xe6,
+ 0x51, 0x80,
+
+ //Dark1 th
+ 0x52, 0xb0,
+ 0x53, 0x60,
+ 0x54, 0xc0,
+ 0x55, 0xc0,
+ 0x56, 0xc0,
+ 0x57, 0x80,
+
+ //Dark2 th
+ 0x58, 0x90,
+ 0x59, 0x40,
+ 0x5a, 0xd0,
+ 0x5b, 0xd0,
+ 0x5c, 0xe0,
+ 0x5d, 0x80,
+
+ //Dark3 th
+ 0x5e, 0x88,
+ 0x5f, 0x40,
+ 0x60, 0xe0,
+ 0x61, 0xe6,
+ 0x62, 0xe6,
+ 0x63, 0x80,
+
+ 0x70, 0x15,
+ 0x71, 0x01,
+
+ 0x72, 0x18,
+ 0x73, 0x01,
+
+ 0x74, 0x25,
+ 0x75, 0x15,
+ 0x80, 0x30,
+ 0x81, 0x50,
+ 0x82, 0x80,
+ 0x85, 0x1a,
+ 0x88, 0x00,
+ 0x89, 0x00,
+ 0x90, 0x5d,
+
+ 0xc5, 0x30,
+ 0xc6, 0x2a,
+
+ 0xD0, 0x0c,
+ 0xD1, 0x80,
+ 0xD2, 0x67,
+ 0xD3, 0x00,
+ 0xD4, 0x00,
+ 0xD5, 0x02,
+ 0xD6, 0xff,
+ 0xD7, 0x18,
+ 0x3b, 0x06,
+ 0x3c, 0x06,
+
+ 0xc5, 0x30,
+ 0xc6, 0x2a,
+
+ //Page 13
+ 0x03, 0x13,
+ 0x10, 0xcb,
+ 0x11, 0x7b,
+ 0x12, 0x07,
+ 0x14, 0x00,
+
+ 0x20, 0x15,
+ 0x21, 0x13,
+ 0x22, 0x33,
+ 0x23, 0x04,
+ 0x24, 0x09,
+ 0x25, 0x08,
+ 0x26, 0x18,
+ 0x27, 0x30,
+ 0x29, 0x12,
+ 0x2a, 0x50,
+
+ //Low clip th
+ 0x2b, 0x06,
+ 0x2c, 0x06,
+ 0x25, 0x08,
+ 0x2d, 0x0c,
+ 0x2e, 0x12,
+ 0x2f, 0x12,
+
+ //Out2 Edge
+ 0x50, 0x10,
+ 0x51, 0x14,
+ 0x52, 0x10,
+ 0x53, 0x0c,
+ 0x54, 0x0f,
+ 0x55, 0x0c,
+
+ //Out1 Edge
+ 0x56, 0x10,
+ 0x57, 0x13,
+ 0x58, 0x10,
+ 0x59, 0x0c,
+ 0x5a, 0x0f,
+ 0x5b, 0x0c,
+
+ //Indoor Edge
+ 0x5c, 0x0a,
+ 0x5d, 0x0b,
+ 0x5e, 0x0a,
+ 0x5f, 0x08,
+ 0x60, 0x09,
+ 0x61, 0x08,
+
+ //Dark1 Edge
+ 0x62, 0x08,
+ 0x63, 0x08,
+ 0x64, 0x08,
+ 0x65, 0x06,
+ 0x66, 0x06,
+ 0x67, 0x06,
+
+ //Dark2 Edge
+ 0x68, 0x07,
+ 0x69, 0x07,
+ 0x6a, 0x07,
+ 0x6b, 0x05,
+ 0x6c, 0x05,
+ 0x6d, 0x05,
+
+ //Dark3 Edge
+ 0x6e, 0x07,
+ 0x6f, 0x07,
+ 0x70, 0x07,
+ 0x71, 0x05,
+ 0x72, 0x05,
+ 0x73, 0x05,
+
+ //2DY
+ 0x80, 0xfd,
+ 0x81, 0x1f,
+ 0x82, 0x05,
+ 0x83, 0x01,
+
+ 0x90, 0x15,
+ 0x91, 0x15,
+ 0x92, 0x33,
+ 0x93, 0x30,
+ 0x94, 0x03,
+ 0x95, 0x14,
+ 0x97, 0x30,
+ 0x99, 0x30,
+
+ 0xa0, 0x04,
+ 0xa1, 0x05,
+ 0xa2, 0x04,
+ 0xa3, 0x05,
+ 0xa4, 0x07,
+ 0xa5, 0x08,
+ 0xa6, 0x07,
+ 0xa7, 0x08,
+ 0xa8, 0x07,
+ 0xa9, 0x08,
+ 0xaa, 0x07,
+ 0xab, 0x08,
+
+ //Out2
+ 0xb0, 0x22,
+ 0xb1, 0x2a,
+ 0xb2, 0x28,
+ 0xb3, 0x22,
+ 0xb4, 0x2a,
+ 0xb5, 0x28,
+
+ //Out1
+ 0xb6, 0x22,
+ 0xb7, 0x2a,
+ 0xb8, 0x28,
+ 0xb9, 0x22,
+ 0xba, 0x2a,
+ 0xbb, 0x28,
+
+ 0xbc, 0x17,
+ 0xbd, 0x17,
+ 0xbe, 0x17,
+ 0xbf, 0x17,
+ 0xc0, 0x17,
+ 0xc1, 0x17,
+
+ //Dark1
+ 0xc2, 0x1e,
+ 0xc3, 0x12,
+ 0xc4, 0x10,
+ 0xc5, 0x1e,
+ 0xc6, 0x12,
+ 0xc7, 0x10,
+
+ //Dark2
+ 0xc8, 0x18,
+ 0xc9, 0x05,
+ 0xca, 0x05,
+ 0xcb, 0x18,
+ 0xcc, 0x05,
+ 0xcd, 0x05,
+
+ //Dark3
+ 0xce, 0x18,
+ 0xcf, 0x05,
+ 0xd0, 0x05,
+ 0xd1, 0x18,
+ 0xd2, 0x05,
+ 0xd3, 0x05,
+
+ //Page 14
+ 0x03, 0x14,
+ 0x10, 0x11,
+ 0x20, 0x40,
+ 0x21, 0x80,
+ 0x23, 0x80,
+ 0x22, 0x80,
+ 0x23, 0x80,
+ 0x24, 0x80,
+
+ 0x30, 0xc8,
+ 0x31, 0x2b,
+ 0x32, 0x00,
+ 0x33, 0x00,
+ 0x34, 0x90,
+
+ 0x40, 0x40,
+ 0x50, 0x2d,
+ 0x60, 0x28,
+ 0x70, 0x2d,
+
+ //Page 15
+ 0x03, 0x15,
+ 0x10, 0x0f,
+ 0x14, 0x52,
+ 0x15, 0x42,
+ 0x16, 0x32,
+ 0x17, 0x2f,
+
+ //CMC
+ 0x30, 0x8f,
+ 0x31, 0x59,
+ 0x32, 0x0a,
+ 0x33, 0x15,
+ 0x34, 0x5b,
+ 0x35, 0x06,
+ 0x36, 0x07,
+ 0x37, 0x40,
+ 0x38, 0x86,
+
+ //CMC OFS
+ 0x40, 0x95,
+ 0x41, 0x1f,
+ 0x42, 0x8a,
+ 0x43, 0x86,
+ 0x44, 0x0a,
+ 0x45, 0x84,
+ 0x46, 0x87,
+ 0x47, 0x9b,
+ 0x48, 0x23,
+
+ //CMC POFS
+ 0x50, 0x8c,
+ 0x51, 0x0c,
+ 0x52, 0x00,
+ 0x53, 0x07,
+ 0x54, 0x17,
+ 0x55, 0x9d,
+ 0x56, 0x00,
+ 0x57, 0x0b,
+ 0x58, 0x89,
+
+ 0x80, 0x03,
+ 0x85, 0x40,
+ 0x87, 0x02,
+ 0x88, 0x00,
+ 0x89, 0x00,
+ 0x8a, 0x00,
+
+ 0x03, 0x16,
+ 0x10, 0x31,//GMA_CTL
+ 0x18, 0x5e,//AG_ON
+ 0x19, 0x5d,//AG_OFF
+ 0x1A, 0x0e,//TIME_ON
+ 0x1B, 0x01,//TIME_OFF
+ 0x1C, 0xdc,//OUT_ON
+ 0x1D, 0xfe,//OUT_OFF
+ //GMA
+ 0x30, 0x00,
+ 0x31, 0x09,
+ 0x32, 0x15,
+ 0x33, 0x29,
+ 0x34, 0x53,
+ 0x35, 0x6c,
+ 0x36, 0x81,
+ 0x37, 0x94,
+ 0x38, 0xa4,
+ 0x39, 0xb3,
+ 0x3a, 0xc0,
+ 0x3b, 0xcb,
+ 0x3c, 0xd5,
+ 0x3d, 0xde,
+ 0x3e, 0xe6,
+ 0x3f, 0xee,
+ 0x40, 0xf5,
+ 0x41, 0xfc,
+ 0x42, 0xff,
+ //RGMA
+ 0x50, 0x00,
+ 0x51, 0x09,
+ 0x52, 0x1f,
+ 0x53, 0x37,
+ 0x54, 0x5b,
+ 0x55, 0x76,
+ 0x56, 0x8d,
+ 0x57, 0xa1,
+ 0x58, 0xb2,
+ 0x59, 0xbe,
+ 0x5a, 0xc9,
+ 0x5b, 0xd2,
+ 0x5c, 0xdb,
+ 0x5d, 0xe3,
+ 0x5e, 0xeb,
+ 0x5f, 0xf0,
+ 0x60, 0xf5,
+ 0x61, 0xf7,
+ 0x62, 0xf8,
+ //BGMA
+ 0x70, 0x00,
+ 0x71, 0x08,
+ 0x72, 0x17,
+ 0x73, 0x2f,
+ 0x74, 0x53,
+ 0x75, 0x6c,
+ 0x76, 0x81,
+ 0x77, 0x94,
+ 0x78, 0xa4,
+ 0x79, 0xb3,
+ 0x7a, 0xc0,
+ 0x7b, 0xcb,
+ 0x7c, 0xd5,
+ 0x7d, 0xde,
+ 0x7e, 0xe6,
+ 0x7f, 0xee,
+ 0x80, 0xf4,
+ 0x81, 0xfa,
+ 0x82, 0xff,
+
+ 0x03, 0x17,
+ 0xc4, 0x6e,
+ 0xc5, 0x5c,
+
+
+ 0x03, 0x18,
+ 0x12, 0x20,
+ 0x10, 0x07,
+ 0x11, 0x00,
+ 0x20, 0x02,
+ 0x21, 0x80,
+ 0x22, 0x00,
+ 0x23, 0xf0,
+ 0x24, 0x00,
+ 0x25, 0x06,
+ 0x26, 0x00,
+ 0x27, 0x00,
+ 0x28, 0x02,
+ 0x29, 0x86,
+ 0x2a, 0x00,
+ 0x2b, 0xf0,
+ 0x2c, 0x14,
+ 0x2d, 0x00,
+ 0x2e, 0x14,
+ 0x2f, 0x00,
+ 0x30, 0x65,
+ 0x03, 0x20,
+ 0x10, 0x1c,
+ 0x18, 0x38,
+ 0x20, 0x01,
+ 0x21, 0x30,
+ 0x22, 0x10,
+ 0x23, 0x00,
+ 0x24, 0x04,
+
+ 0x28, 0xff,
+ 0x29, 0xad,
+
+ 0x2a, 0xf0,
+ 0x2b, 0x34,
+ 0x30, 0x78,
+ 0x2c, 0xc3,
+ 0x2d, 0x5f,
+ 0x2e, 0x33,
+ // 0x30, 0xf8,
+ 0x32, 0x03,
+ 0x33, 0x2e,
+ 0x34, 0x30,
+ 0x35, 0xd4,
+ 0x36, 0xfe,
+ 0x37, 0x32,
+ 0x38, 0x04,
+ 0x47, 0xf0,
+
+ //Y_Frame TH
+ 0x50, 0x45,
+ 0x51, 0x88,
+
+ 0x56, 0x10,
+ 0x57, 0xb7,
+ 0x58, 0x14,
+ 0x59, 0x88,
+ 0x5a, 0x04,
+
+ 0x60, 0x55,
+ 0x61, 0x55,
+ 0x62, 0x6a,
+ 0x63, 0xa9,
+ 0x64, 0x6a,
+ 0x65, 0xa9,
+ 0x66, 0x6a,
+ 0x67, 0xa9,
+ 0x68, 0x6b,
+ 0x69, 0xe9,
+ 0x6a, 0x6a,
+ 0x6b, 0xa9,
+ 0x6c, 0x6a,
+ 0x6d, 0xa9,
+ 0x6e, 0x55,
+ 0x6f, 0x55,
+ // 0x70, 0x42,
+ 0x70, 0x45,
+ 0x71, 0xBb,
+
+ // haunting control
+ 0x76, 0x21,
+ 0x77, 0x02,
+ 0x78, 0x22,
+ 0x79, 0x2a,
+
+ 0x78, 0x24,
+ 0x79, 0x23,
+ 0x7a, 0x23,
+ 0x7b, 0x22,
+ 0x7d, 0x23,
+ 0x83, 0x01, //EXP Normal 33.33 fps
+ 0x84, 0x5f,
+ 0x85, 0x00,
+ 0x86, 0x01, //EXPMin 6410.26 fps
+ 0x87, 0xd4,
+ 0x88, 0x05, //EXP Max 8.33 fps
+ 0x89, 0x7c,
+ 0x8a, 0x00,
+ 0x8B, 0x75, //EXP100
+ 0x8C, 0x00,
+ 0x8D, 0x60, //EXP120
+ 0x8E, 0xe4,
+
+
+ 0x98, 0xdc,
+ 0x99, 0x45,
+ 0x9a, 0x0d,
+ 0x9b, 0xde,
+ 0x9c, 0x08,
+ 0x9d, 0x0a,
+ 0x9e, 0x01,
+ 0x10, 0x9c,
+ 0x18, 0x30,
+ 0x90, 0x0c,
+ 0x91, 0x0c,
+ 0x92, 0xd8,
+ 0x93, 0xd0,
+
+ 0x9c, 0x0c, //EXP Limit 915.75 fps
+ 0x9d, 0xcc,
+ 0x9e, 0x01, //EXP Unit
+ 0x9f, 0xd4,
+ 0xa0, 0x03,
+ 0xa1, 0xa9,
+ 0xa2, 0x80,
+ 0xb0, 0x1d,
+ 0xb1, 0x1a,
+ 0xb2, 0xb0,
+ 0xb3, 0x1a,
+ 0xb4, 0x1a,
+ 0xb5, 0x44,
+ 0xb6, 0x2f,
+ 0xb7, 0x28,
+ 0xb8, 0x25,
+ 0xb9, 0x22,
+ 0xba, 0x21,
+ 0xbb, 0x20,
+ 0xbc, 0x1f,
+ 0xbd, 0x1f,
+ 0xc0, 0x30,
+ 0xc1, 0x20,
+ 0xc2, 0x20,
+ 0xc3, 0x20,
+ 0xc4, 0x08,
+ 0xc8, 0x60,
+ 0xc9, 0x40,
+
+ //Page 22
+ 0x03, 0x22, // Page 22
+ 0x10, 0xfd, // AWBCTL1
+ 0x11, 0x2e, // AWBCTL2
+ 0x19, 0x01,
+ 0x20, 0x30,
+ 0x21, 0x80,
+ 0x23, 0x08,
+ 0x24, 0x01,
+
+ 0x30, 0x80, // ULVL
+ 0x31, 0x80, // VLVL
+ 0x38, 0x11, // UVTH1
+ 0x39, 0x34, // UVTH2
+ 0x40, 0xf7, // YRANGE
+
+ 0x41, 0x77, // CDIFF
+ 0x42, 0x55, // CSUM2
+ 0x43, 0xf0,
+ 0x44, 0x66,
+ 0x45, 0x33,
+ 0x46, 0x01, // WHTPXLTH1
+ 0x47, 0x94,
+
+ 0x50, 0xb2,
+ 0x51, 0x81,
+ 0x52, 0x98,
+
+ 0x80, 0x38, // RGAIN
+ 0x81, 0x20, // GGAIN
+ 0x82, 0x38, // BGAIN
+
+ 0x83, 0x5e, // RMAX
+ 0x84, 0x20, // RMIN
+ 0x85, 0x53, // BMAX
+ 0x86, 0x15, // BMIN
+
+ 0x87, 0x54, // RMAXM
+ 0x88, 0x20, // RMINM
+ 0x89, 0x3f, // BMAXM
+ 0x8a, 0x1c, // BMINM
+
+ 0x8b, 0x54, // RMAXB
+ 0x8c, 0x3f, // RMINB
+ 0x8d, 0x24, // BMAXB
+ 0x8e, 0x1c, // BMINB
+
+ 0x8f, 0x60, // BGAINPARA1
+ 0x90, 0x5f, // BGAINPARA2
+ 0x91, 0x5c, // BGAINPARA3
+ 0x92, 0x4C, // BGAINPARA4
+ 0x93, 0x41, // BGAINPARA5
+ 0x94, 0x3b, // BGAINPARA6
+ 0x95, 0x36, // BGAINPARA7
+ 0x96, 0x30, // BGAINPARA8
+ 0x97, 0x27, // BGAINPARA9
+ 0x98, 0x20, // BGAINPARA10
+ 0x99, 0x1C, // BGAINPARA11
+ 0x9a, 0x19, // BGAINPARA12
+
+ 0x9b, 0x88, // BGAINBND1
+ 0x9c, 0x88, // BGAINBND2
+ 0x9d, 0x48, // RGAINTH1
+ 0x9e, 0x38, // RGAINTH2
+ 0x9f, 0x30, // RGAINTH3
+
+ 0xa0, 0x74, // RDELTA1
+ 0xa1, 0x35, // BDELTA1
+ 0xa2, 0xaf, // RDELTA2
+ 0xa3, 0xf7, // BDELTA2
+
+ 0xa4, 0x10, // AWBEXPLMT1
+ 0xa5, 0x50, // AWBEXPLMT2
+ 0xa6, 0xc4, // AWBEXPLMT3
+
+ 0xad, 0x40,
+ 0xae, 0x4a,
+
+ 0xaf, 0x2a,
+ 0xb0, 0x29,
+
+ 0xb1, 0x20,
+ 0xb4, 0xff,
+ 0xb8, 0x6b,
+ 0xb9, 0x00,
+
+
+ 0x03, 0x24,
+ 0x10, 0x01,
+ 0x18, 0x06,
+ 0x30, 0x06,
+ 0x31, 0x90,
+ 0x32, 0x25,
+ 0x33, 0xa2,
+ 0x34, 0x26,
+ 0x35, 0x58,
+ 0x36, 0x60,
+ 0x37, 0x00,
+ 0x38, 0x50,
+ 0x39, 0x00,
+
+ 0x03, 0x20,
+ 0x10, 0x9c,
+ 0x03, 0x22,
+ 0x10, 0xe9,
+
+ //Page 00
+ 0x03, 0x00,
+ 0x0e, 0x03,
+ 0x0e, 0x73,
+
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+
+ 0x03, 0x00,
+ 0x01, 0xf8,
+
+};
+
+uint8_t hi253_default_regs_init[]={
+
+0x01, 0xf9, //sleep on
+0x08, 0x0f, //Hi-Z on
+0x01, 0xf8, //sleep off
+0x03, 0x00, // Dummy 750us START
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00, // Dummy 750us END
+0x0e, 0x04, //PLL On
+0x0e, 0x74, //PLLx2
+0x03, 0x00, // Dummy 750us START
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00, // Dummy 750us END
+0x0e, 0x00, //PLL off
+0x01, 0xf1, //sleep on
+0x08, 0x00, //Hi-Z off
+0x01, 0xf3,
+0x01, 0xf1,
+// PAGE 20
+0x03, 0x20, //page 20
+0x10, 0x1c, //ae off
+// PAGE 22
+0x03, 0x22, //page 22
+0x10, 0x69, //awb off
+/////// PAGE 0 START ///////
+0x03, 0x00,
+0x10, 0x10, // Sub1/2_Preview2 Mode_H binning
+0x11, 0x90,
+0x12, 0x04,//00
+0x0b, 0xaa, // ESD Check Register
+0x0c, 0xaa, // ESD Check Register
+0x0d, 0xaa, // ESD Check Register
+0x20, 0x00, // Windowing start point Y
+0x21, 0x04,
+0x22, 0x00, // Windowing start point X
+0x23, 0x07,
+0x24, 0x04,
+0x25, 0xb0,
+0x26, 0x06,
+0x27, 0x40, // WINROW END
+0x40, 0x01, //Hblank 408
+0x41, 0x68,
+0x42, 0x00, //Vblank 20
+0x43, 0x14,
+0x45, 0x04,
+0x46, 0x18,
+0x47, 0xd8,
+//BLC
+0x80, 0x2e,
+0x81, 0x7e,
+0x82, 0x90,
+0x83, 0x00,
+0x84, 0x0c,
+0x85, 0x00,
+0x90, 0x0c, //BLC_TIME_TH_ON
+0x91, 0x0c, //BLC_TIME_TH_OFF
+0x92, 0x78, //BLC_AG_TH_ON
+0x93, 0x70, //BLC_AG_TH_OFF
+0x94, 0x75,
+0x95, 0x70,
+0x96, 0xdc,
+0x97, 0xfe,
+0x98, 0x38,
+//OutDoor BLC
+0x99, 0x42,
+0x9a, 0x42,
+0x9b, 0x42,
+0x9c, 0x42,
+//Dark BLC
+0xa0, 0x08,
+0xa2, 0x08,
+0xa4, 0x08,
+0xa6, 0x08,
+//Normal BLC
+0xa8, 0x40,
+0xaa, 0x40,
+0xac, 0x40,
+0xae, 0x40,
+/////// PAGE 2 START ///////
+0x03, 0x02, //Page 02
+0x10, 0x00, //Mode_test
+0x11, 0x00, //Mode_dead_test
+0x12, 0x03, //pwr_ctl_ctl1
+0x13, 0x03, //Mode_ana_test
+0x14, 0x00, //mode_memory
+0x16, 0x00, //dcdc_ctl1
+0x17, 0x8c, //dcdc_ctl2
+0x18, 0x4C, //analog_func1
+0x19, 0x00, //analog_func2
+0x1a, 0x39, //analog_func3
+0x1b, 0x00, //analog_func4
+0x1c, 0x09, //dcdc_ctl3
+0x1d, 0x40, //dcdc_ctl4
+0x1e, 0x30, //analog_func7
+0x1f, 0x10, //analog_func8
+0x20, 0x77, //pixel bias
+0x21, 0xde, //adc,asp bias
+0x22, 0xa7, //main,bus bias
+0x23, 0x30, //clamp
+0x24, 0x4a,
+0x25, 0x10,
+0x27, 0x3c,
+0x28, 0x00,
+0x29, 0x0c,
+0x2a, 0x80,
+0x2b, 0x80,
+0x2c, 0x02,
+0x2d, 0xa0,
+0x2e, 0x00, //11
+0x2f, 0x00, //a1
+0x30, 0x05, //swap_ctl
+0x31, 0x99,
+0x32, 0x00,
+0x33, 0x00,
+0x34, 0x22,
+0x38, 0x88,
+0x39, 0x88,
+0x50, 0x20,
+0x51, 0x03, //00 don't touch
+0x52, 0x01,
+0x53, 0xc1,
+0x54, 0x10,
+0x55, 0x1c,
+0x56, 0x11,
+0x58, 0x10,
+0x59, 0x0e,
+0x5d, 0xa2,
+0x5e, 0x5a,
+0x60, 0x87,
+0x61, 0x99,
+0x62, 0x88,
+0x63, 0x97,
+0x64, 0x88,
+0x65, 0x97,
+0x67, 0x0c,
+0x68, 0x0c,
+0x69, 0x0c,
+0x6a, 0xb4,
+0x6b, 0xc4,
+0x6c, 0xb5,
+0x6d, 0xc2,
+0x6e, 0xb5,
+0x6f, 0xc0,
+0x70, 0xb6,
+0x71, 0xb8,
+0x72, 0x89,
+0x73, 0x96,
+0x74, 0x89,
+0x75, 0x96,
+0x76, 0x89,
+0x77, 0x96,
+0x7c, 0x85,
+0x7d, 0xaf,
+0x80, 0x01,
+0x81, 0x7f,
+0x82, 0x13,
+0x83, 0x24,
+0x84, 0x7d,
+0x85, 0x81,
+0x86, 0x7d,
+0x87, 0x81,
+0x88, 0xab,
+0x89, 0xbc,
+0x8a, 0xac,
+0x8b, 0xba,
+0x8c, 0xad,
+0x8d, 0xb8,
+0x8e, 0xae,
+0x8f, 0xb2,
+0x90, 0xb3,
+0x91, 0xb7,
+0x92, 0x48,
+0x93, 0x54,
+0x94, 0x7d,
+0x95, 0x81,
+0x96, 0x7d,
+0x97, 0x81,
+0xa0, 0x02,
+0xa1, 0x7b,
+0xa2, 0x02,
+0xa3, 0x7b,
+0xa4, 0x7b,
+0xa5, 0x02,
+0xa6, 0x7b,
+0xa7, 0x02,
+0xa8, 0x85,
+0xa9, 0x8c,
+0xaa, 0x85,
+0xab, 0x8c,
+0xac, 0x10,
+0xad, 0x16,
+0xae, 0x10,
+0xaf, 0x16,
+0xb0, 0x99,
+0xb1, 0xa3,
+0xb2, 0xa4,
+0xb3, 0xae,
+0xb4, 0x9b,
+0xb5, 0xa2,
+0xb6, 0xa6,
+0xb7, 0xac,
+0xb8, 0x9b,
+0xb9, 0x9f,
+0xba, 0xa6,
+0xbb, 0xaa,
+0xbc, 0x9b,
+0xbd, 0x9f,
+0xbe, 0xa6,
+0xbf, 0xaa,
+0xc4, 0x2c,
+0xc5, 0x43,
+0xc6, 0x63,
+0xc7, 0x79,
+0xc8, 0x2d,
+0xc9, 0x42,
+0xca, 0x2d,
+0xcb, 0x42,
+0xcc, 0x64,
+0xcd, 0x78,
+0xce, 0x64,
+0xcf, 0x78,
+0xd0, 0x0a,
+0xd1, 0x09,
+0xd2, 0x20,
+0xd3, 0x00,
+0xd4, 0x0c, //DCDC_TIME_TH_ON don't touch
+0xd5, 0x0c, //DCDC_TIME_TH_OFF don't touch
+0xd6, 0x78, //DCDC_AG_TH_ON 78
+0xd7, 0x70, //DCDC_AG_TH_OFF 70
+0xe0, 0xc4,
+0xe1, 0xc4,
+0xe2, 0xc4,
+0xe3, 0xc4,
+0xe4, 0x00,
+0xe8, 0x80,
+0xe9, 0x40,
+0xea, 0x7f,
+0xf0, 0x01,
+0xf1, 0x01,
+0xf2, 0x01,
+0xf3, 0x01,
+0xf4, 0x01,
+/////// PAGE 3 ///////
+0x03, 0x03,
+0x10, 0x10,
+/////// PAGE 10 START ///////
+0x03, 0x10,
+0x10, 0x03, // CrYCbY // For Demoset 0x03
+0x12, 0x30,
+0x13, 0x0a,//add jacky open constrast
+0x20, 0x00,
+0x30, 0x00,
+0x31, 0x00,
+0x32, 0x00,
+0x33, 0x00,
+0x34, 0x30,
+0x35, 0x00,
+0x36, 0x00,
+0x38, 0x00,
+0x3e, 0x58,
+0x3f, 0x00,
+0x40, 0x80, // YOFS
+0x41, 0x00, // DYOFS
+0x48, 0x80,// 84
+0x60, 0x67,//6b AG ratio jacky
+
+0x61, 0x70, //7e //8e //88 //70
+0x62, 0x70, //7e //8e //88 //80
+0x63, 0x50, //Double_AG 50->30
+0x64, 0x41,
+0x66, 0x42,
+0x67, 0x20,
+0x6a, 0x80, //8a
+0x6b, 0x84, //74
+0x6c, 0x80, //7e //7a
+0x6d, 0x80, //8e
+/////// PAGE 11 START ///////
+0x03, 0x11,
+0x10, 0x7f,
+0x11, 0x40,
+0x12, 0x0a, // Blue Max-Filter Delete
+0x13, 0xbb,
+0x26, 0x51, // Double_AG 31->20
+0x27, 0x54, // Double_AG 34->22
+0x28, 0x0f,
+0x29, 0x10,
+0x2b, 0x30,
+0x2c, 0x32,
+//Out2 D-LPF th
+0x30, 0x70,
+0x31, 0x10,
+0x32, 0x58,
+0x33, 0x09,
+0x34, 0x06,
+0x35, 0x03,
+//Out1 D-LPF th
+0x36, 0x70,
+0x37, 0x18,
+0x38, 0x58,
+0x39, 0x09,
+0x3a, 0x06,
+0x3b, 0x03,
+//Indoor D-LPF th
+0x3c, 0x80,
+0x3d, 0x18,
+ 0x3e, 0x90,
+0x3f, 0x0c,
+0x40, 0x09,
+0x41, 0x06,
+0x42, 0x80,
+0x43, 0x18,
+ 0x44, 0x90,
+0x45, 0x12,
+0x46, 0x10,
+0x47, 0x10,
+0x48, 0x90,
+0x49, 0x40,
+0x4a, 0x80,
+0x4b, 0x13,
+0x4c, 0x10,
+0x4d, 0x11,
+0x4e, 0x80,
+0x4f, 0x30,
+0x50, 0x80,
+0x51, 0x13,
+0x52, 0x10,
+0x53, 0x13,
+0x54, 0x11,
+0x55, 0x17,
+0x56, 0x20,
+0x57, 0x01,
+0x58, 0x00,
+0x59, 0x00,
+0x5a, 0x1f, //18
+0x5b, 0x00,
+0x5c, 0x00,
+0x60, 0x3f,
+0x62, 0x60,
+0x70, 0x06,
+/////// PAGE 12 START ///////
+0x03, 0x12,
+0x20, 0x0f,
+0x21, 0x0f,
+0x25, 0x00, //0x30
+0x28, 0x00,
+0x29, 0x00,
+0x2a, 0x00,
+0x30, 0x50,
+0x31, 0x18,
+0x32, 0x32,
+0x33, 0x40,
+0x34, 0x50,
+0x35, 0x70,
+0x36, 0xa0,
+//Out2 th
+0x40, 0xa0,
+0x41, 0x40,
+0x42, 0xa0,
+0x43, 0x90,
+0x44, 0x90,
+0x45, 0x80,
+//Out1 th
+0x46, 0xb0,
+0x47, 0x55,
+0x48, 0xa0,
+0x49, 0x90,
+0x4a, 0x90,
+0x4b, 0x80,
+//Indoor th
+0x4c, 0xb0,
+0x4d, 0x40,
+0x4e, 0x90,
+0x4f, 0x90,
+0x50, 0xa0,
+0x51, 0x80,
+//Dark1 th
+0x52, 0x00, //b0
+0x53, 0x50, //60
+0x54, 0xd4, //c0
+0x55, 0xc0, //c0
+0x56, 0x70, //b0
+0x57, 0xec, //70
+//Dark2 th
+0x58, 0x60, //90
+0x59, 0x40, //
+0x5a, 0xd0,
+0x5b, 0xd0,
+0x5c, 0xc0,
+0x5d, 0x9b, //70
+//Dark3 th
+0x5e, 0x70,//88
+0x5f, 0x40,
+0x60, 0xe0,
+0x61, 0xe0,
+0x62, 0xe0,
+0x63, 0xb4,//80
+0x70, 0x15,
+0x71, 0x01, //Don't Touch register
+0x72, 0x18,
+0x73, 0x01, //Don't Touch register
+0x74, 0x25,
+0x75, 0x15,
+0x80, 0x20,
+0x81, 0x40,
+0x82, 0x65,
+0x85, 0x1a,
+0x88, 0x00,
+0x89, 0x00,
+0x90, 0x5d, //For capture
+0x91, 0x88,
+0x98, 0x7d,
+0x99, 0x28,
+0x9A, 0x14,
+0x9B, 0xc8,
+0x9C, 0x02,
+0x9D, 0x1e,
+0x9E, 0x28,
+0x9F, 0x07,
+0xA0, 0x32,
+0xA4, 0x04,
+0xA5, 0x0e,
+0xA6, 0x0c,
+0xA7, 0x04,
+0xA8, 0x3c,
+
+0xAA, 0x14,
+0xAB, 0x11,
+0xAC, 0x0f,
+0xAD, 0x16,
+0xAE, 0x15,
+0xAF, 0x14,
+
+0xB1, 0xaa,
+0xB2, 0x96,
+0xB3, 0x28,
+//Dont Touch register
+0xB8, 0x78,
+0xB9, 0xa0,
+0xBA, 0xb4,
+0xBB, 0x14,
+0xBC, 0x14,
+0xBD, 0x00, //14
+0xBE, 0x64,
+0xBF, 0x64,
+0xC0, 0x64,
+0xC1, 0x64,
+0xC2, 0x04,
+0xC3, 0x03,
+0xC4, 0x0c,
+0xC5, 0x30,
+0xC6, 0x2a,
+0xD0, 0x0c, //CI Option/CI DPC
+0xD1, 0x80,
+0xD2, 0x67,
+0xD3, 0x00,
+0xD4, 0x00,
+0xD5, 0x02,
+0xD6, 0xff,
+0xD7, 0x18,
+//End
+0x3b, 0x06,
+0x3c, 0x06,
+0xc5, 0x00,//55->48
+0xc6, 0x00,//48->40
+/////// PAGE 13 START ///////
+0x03, 0x13,
+//Edge
+0x10, 0xcb,
+0x11, 0x7b,
+0x12, 0x07,
+0x14, 0x00,
+0x20, 0x15,
+0x21, 0x13,
+0x22, 0x33,
+0x23, 0x05,
+0x24, 0x09,
+0x26, 0x18,
+0x27, 0x30,
+0x29, 0x12,
+0x2a, 0x50,
+//Low clip th
+0x2b, 0x01, //Out2 02
+0x2c, 0x01, //Out1 02
+0x25, 0x06,
+0x2d, 0x0c,
+0x2e, 0x12,
+0x2f, 0x12,
+//Out2 Edge
+0x50, 0x18,
+0x51, 0x1c,
+0x52, 0x1b,
+0x53, 0x15,
+0x54, 0x18,
+0x55, 0x15,
+//Out1 Edge
+0x56, 0x18,
+0x57, 0x1c,
+0x58, 0x1b,
+0x59, 0x15,
+0x5a, 0x18,
+0x5b, 0x15,
+//Indoor Edge
+0x5c, 0x0f,//0b
+0x5d, 0x21,//21
+0x5e, 0x1c,//0a
+0x5f, 0x0c,//08
+0x60, 0x2f,//19
+0x61, 0x1c,//08
+//Dark1 Edge
+0x62, 0x08,
+0x63, 0x28,
+0x64, 0x08,
+0x65, 0x06,
+0x66, 0x26,
+0x67, 0x06,
+//Dark2 Edge
+0x68, 0x07,
+0x69, 0x07,
+0x6a, 0x07,
+0x6b, 0x05,
+0x6c, 0x05,
+0x6d, 0x05,
+//Dark3 Edge
+0x6e, 0x07,
+0x6f, 0x07,
+0x70, 0x07,
+0x71, 0x05,
+0x72, 0x05,
+0x73, 0x05,
+0x80, 0xfd,
+0x81, 0x1f,
+0x82, 0x05,
+0x83, 0x31,
+0x90, 0x05,
+0x91, 0x05,
+0x92, 0x33,
+0x93, 0x30,
+0x94, 0x03,
+0x95, 0x14,
+0x97, 0x20,
+0x99, 0x20,
+0xa0, 0x01,
+0xa1, 0x02,
+0xa2, 0x01,
+0xa3, 0x02,
+0xa4, 0x05,
+0xa5, 0x05,
+0xa6, 0x07,
+0xa7, 0x08,
+0xa8, 0x07,
+0xa9, 0x08,
+0xaa, 0x07,
+0xab, 0x08,
+0xb0, 0x22,
+0xb1, 0x2a,
+0xb2, 0x28,
+0xb3, 0x22,
+0xb4, 0x2a,
+0xb5, 0x28,
+0xb6, 0x22,
+0xb7, 0x2a,
+0xb8, 0x28,
+0xb9, 0x22,
+0xba, 0x2a,
+0xbb, 0x28,
+0xbc, 0x25,
+0xbd, 0x2a,
+0xbe, 0x27,
+0xbf, 0x25,
+0xc0, 0x2a,
+0xc1, 0x27,
+0xc2, 0x1e,
+0xc3, 0x24,
+0xc4, 0x20,
+0xc5, 0x1e,
+0xc6, 0x24,
+0xc7, 0x20,
+0xc8, 0x18,
+0xc9, 0x20,
+0xca, 0x1e,
+0xcb, 0x18,
+0xcc, 0x20,
+0xcd, 0x1e,
+0xce, 0x18,
+0xcf, 0x20,
+0xd0, 0x1e,
+0xd1, 0x18,
+0xd2, 0x20,
+0xd3, 0x1e,
+/////// PAGE 14 START ///////
+0x03, 0x14,
+0x10, 0x11,
+0x14, 0x80, // GX
+0x15, 0x80, // GY
+0x16, 0x80, // RX
+0x17, 0x80, // RY
+0x18, 0x80, // BX
+0x19, 0x80, // BY
+0x20, 0x80, //X 60 //a0
+0x21, 0x80, //Y
+0x22, 0x80,
+0x23, 0x80,
+0x24, 0x80,
+0x30, 0xc8,
+0x31, 0x2b,
+0x32, 0x00,
+0x33, 0x00,
+0x34, 0x90,
+0x40, 0x48, //0x48//65
+0x50, 0x44, //0x34
+0x60, 0x46, //0x29
+0x70, 0x44, //0x34
+/////// PAGE 15 START ///////
+0x03, 0x15,
+0x10, 0x0f,
+//Rstep H 16
+//Rstep L 14
+0x14, 0x4f, //CMCOFSGH
+0x15, 0x32, //CMCOFSGM
+0x16, 0x24, //CMCOFSGL
+0x17, 0x2f, //CMC SIGN
+//CMC
+0x30, 0x91,
+0x31, 0x6a,
+0x32, 0x18,
+0x33, 0x17,
+0x34, 0x53,
+0x35, 0x08,
+0x36, 0x08,
+0x37, 0x3e,
+0x38, 0x87,
+//CMC OFS
+0x40, 0xa3,
+0x41, 0x3a,
+0x42, 0x93,
+0x43, 0x0b,
+0x44, 0x86,
+0x45, 0x06,
+0x46, 0x00,
+0x47, 0xa2,
+0x48, 0x22,
+//CMC POFS
+0x50, 0x82,
+0x51, 0x06,
+0x52, 0x83,
+0x53, 0x03,
+0x54, 0x11,
+0x55, 0x8e,
+0x56, 0x00,
+0x57, 0x06,
+0x58, 0x87,
+
+0x80, 0x03,
+0x85, 0x40,
+0x87, 0x02,
+0x88, 0x00,
+0x89, 0x00,
+0x8a, 0x00,
+
+
+
+/////// PAGE 16 START ///////
+0x03, 0x16,
+0x10, 0x31,//GMA_CTL
+0x18, 0x5e,//AG_ON
+0x19, 0x5d,//AG_OFF
+0x1A, 0x0e,//TIME_ON
+0x1B, 0x01,//TIME_OFF
+0x1C, 0xdc,//OUT_ON
+0x1D, 0xfe,//OUT_OFF
+
+//GMA
+0x30, 0x00,
+0x31, 0x06,
+0x32, 0x0e,
+0x33, 0x23,
+0x34, 0x55,
+0x35, 0x7f,
+0x36, 0x99,
+0x37, 0xac,
+0x38, 0xba,
+0x39, 0xc5,
+0x3a, 0xcd,
+0x3b, 0xd4,
+0x3c, 0xdb,
+0x3d, 0xe2,
+0x3e, 0xe7,
+0x3f, 0xec,
+0x40, 0xf3,
+0x41, 0xfa,
+0x42, 0xff,
+//RGMA
+0x50, 0x00,
+0x51, 0x07,
+0x52, 0x12,
+0x53, 0x27,
+0x54, 0x57,
+0x55, 0x7f,
+0x56, 0x99,
+0x57, 0xac,
+0x58, 0xba,
+0x59, 0xc5,
+0x5a, 0xcd,
+0x5b, 0xd4,
+0x5c, 0xdb,
+0x5d, 0xe2,
+0x5e, 0xe7,
+0x5f, 0xec,
+0x60, 0xf3,
+0x61, 0xfa,
+0x62, 0xff,
+//BGMA
+0x70, 0x00,
+0x71, 0x09,
+0x72, 0x16,
+0x73, 0x2c,
+0x74, 0x59,
+0x75, 0x7f,
+0x76, 0x99,
+0x77, 0xac,
+0x78, 0xba,
+0x79, 0xc5,
+0x7a, 0xcd,
+0x7b, 0xd4,
+0x7c, 0xdb,
+0x7d, 0xe2,
+0x7e, 0xe7,
+0x7f, 0xec,
+0x80, 0xf3,
+0x81, 0xfa,
+0x82, 0xff,
+/////// PAGE 17 START ///////
+0x03, 0x17,
+0x10, 0xf7,
+/////// PAGE 18 START ///////
+
+ 0x03, 0x18,
+ 0x12, 0x20,
+ 0x10, 0x07,
+ 0x11, 0x00,
+ 0x20, 0x05,
+ 0x21, 0x00,
+ 0x22, 0x03,
+ 0x23, 0xc0,
+ 0x24, 0x00,
+ 0x25, 0x04,
+ 0x26, 0x00,
+ 0x27, 0x00,
+ 0x28, 0x05,
+ 0x29, 0x04,
+ 0x2a, 0x03,
+ 0x2b, 0xc0,
+ 0x2c, 0x0a,
+ 0x2d, 0x00,
+ 0x2e, 0x0a,
+ 0x2f, 0x00,
+ 0x30, 0x45,
+
+/////// PAGE 20 START ///////
+0x03, 0x20,
+0x11, 0x1c,
+0x18, 0x30,
+0x1a, 0x08,
+0x20, 0x01, //05_lowtemp Y Mean off
+0x21, 0x30,
+0x22, 0x10,
+0x23, 0x00,
+0x24, 0x00, //Uniform Scene Off
+0x28, 0xe7,
+0x29, 0x0d, //20100305 ad->0d
+0x2a, 0xff,
+0x2b, 0x34, //f4->Adaptive on //04 off
+0x2c, 0xc2,
+0x2d, 0xcf, //fe->AE Speed option
+0x2e, 0x33,
+0x30, 0x78, //f8
+0x32, 0x03,
+0x33, 0x2e,
+0x34, 0x30,
+0x35, 0xd4,
+0x36, 0xfe,
+0x37, 0x32,
+0x38, 0x04,
+0x39, 0x22, //AE_escapeC10
+0x3a, 0xde, //AE_escapeC11
+0x3b, 0x22, //AE_escapeC1
+0x3c, 0xde, //AE_escapeC2
+0x50, 0x45,
+0x51, 0x88,
+0x56, 0x03,
+0x57, 0xf7,
+0x58, 0x14,
+0x59, 0x88,
+0x5a, 0x04,
+
+/*
+0x60, 0xff,
+0x61, 0xff,
+0x62, 0xea,
+0x63, 0xab,
+0x64, 0xea,
+0x65, 0xab,
+0x66, 0xea,//eb
+0x67, 0x2b,//eb
+0x68, 0xea,//eb
+0x69, 0x2b,//eb
+0x6a, 0xea,
+0x6b, 0xab,
+0x6c, 0xea,
+0x6d, 0xab,
+0x6e, 0xff,
+0x6f, 0xff,
+*/
+
+0x60, 0x55,
+0x61, 0x55,
+0x62, 0x6a,
+0x63, 0xa9,
+0x64, 0x6a,
+0x65, 0xa9,
+0x66, 0x6b,//eb
+0x67, 0xe9,//eb
+0x68, 0x6b,//eb
+0x69, 0xe9,//eb
+0x6a, 0x6a,
+0x6b, 0xa9,
+0x6c, 0x6a,
+0x6d, 0xa9,
+0x6e, 0x55,
+0x6f, 0x55,
+
+0x70, 0x6e, //6e
+0x71, 0x89, //00 //-4
+0x76, 0x43,
+0x77, 0xe2, //04 //f2
+0x78, 0x23, //Yth1
+0x79, 0x46, //Yth2 //46
+0x7a, 0x23, //23
+0x7b, 0x22, //22
+0x7d, 0x23,
+0x83, 0x03, //EXP Normal 33.33 fps
+0x84, 0x6e,
+0x85, 0xe8,
+0x86, 0x01, //EXPMin 7500.00 fps
+0x87, 0xf4,
+0x88, 0x06, //EXP Max 8.33 fps
+0x89, 0xdd,
+0x8a, 0xd0,
+0x8B, 0x92, //EXP100
+0x8C, 0x7c,
+0x8D, 0x7a, //EXP120
+0x8E, 0x12,
+0x9c, 0x17, //EXP Limit 500.00 fps
+0x9d, 0x70,
+0x9e, 0x01, //EXP Unit
+0x9f, 0xf4,
+0xb0, 0x18,
+0xb1, 0x14, //ADC 400->560
+0xb2, 0x80,//a0
+0xb3, 0x18,
+0xb4, 0x1a,
+0xb5, 0x44,
+0xb6, 0x2f,
+0xb7, 0x28,
+0xb8, 0x25,
+0xb9, 0x22,
+0xba, 0x21,
+0xbb, 0x20,
+0xbc, 0x1f,
+0xbd, 0x1f,
+0xc0, 0x14,
+0xc1, 0x1f,
+0xc2, 0x1f,
+0xc3, 0x18, //2b
+0xc4, 0x10, //08
+0xc8, 0x80,
+0xc9, 0x40,
+/////// PAGE 22 START ///////
+0x03, 0x22,
+0x10, 0xfd,
+0x11, 0x2e,
+0x19, 0x01,
+0x20, 0x30,
+0x21, 0x80,
+0x24, 0x01,
+0x30, 0x80,
+0x31, 0x80,
+0x38, 0x11,
+0x39, 0x34,
+0x40, 0xf7,
+0x41, 0x55,
+0x42, 0x33, //43
+
+0x43, 0xf6,
+0x44, 0x55, //44
+0x45, 0x44, //33
+
+0x46, 0x00,
+0x50, 0xb2,
+0x51, 0x81,
+0x52, 0x98,
+0x80, 0x38,//40
+0x81, 0x20,
+0x82, 0x30,
+
+0x83, 0x53,
+0x84, 0x16,//1E
+0x85, 0x5A,//52
+0x86, 0x25,
+
+0x87, 0x45,
+0x88, 0x35,
+0x89, 0x47,
+0x8a, 0x2b,
+
+0x8b, 0x40,
+0x8c, 0x33,
+0x8d, 0x38,
+0x8e, 0x30,
+
+0x8f, 0x53,
+0x90, 0x52,
+0x91, 0x50,
+0x92, 0x4c,
+0x93, 0x43,
+0x94, 0x37,
+0x95, 0x2f,//2a
+0x96, 0x26,//24
+0x97, 0x21,//20
+0x98, 0x1e,
+0x99, 0x1f,
+0x9a, 0x20,
+0x9b, 0x88,
+0x9c, 0x88,
+0x9d, 0x48,
+0x9e, 0x38,
+0x9f, 0x30,
+0xa0, 0x60,
+0xa1, 0x34,
+0xa2, 0x6f,
+0xa3, 0xff,
+0xa4, 0x14,
+0xa5, 0x2c,
+0xa6, 0xcf,
+0xad, 0x40,
+0xae, 0x4a,
+0xaf, 0x28,
+0xb0, 0x26,
+0xb1, 0x00,
+0xb8, 0xa0,
+0xb9, 0x00,
+// PAGE 20
+0x03, 0x20, //page 20
+0x10, 0x9c, //ae off
+// PAGE 22
+0x03, 0x22, //page 22
+0x10, 0xe9, //awb off
+// PAGE 0
+0x03, 0x00,
+0x0e, 0x04, //PLL On
+0x0e, 0x74, //PLLx2
+0x03, 0x00, // Dummy 750us
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+ 0x03, 0x00,
+ 0x01, 0xf8,
+
+};
+
+#endif
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/hi253/hi253-wave7.h b/drivers/media/video/wmt_v4l2/sensors/hi253/hi253-wave7.h
new file mode 100755
index 00000000..f95c4a22
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/hi253/hi253-wave7.h
@@ -0,0 +1,2128 @@
+#ifndef YACD511_H
+#define YACD511_H
+
+// Scene Mode
+uint8_t hi253_scene_mode_auto[] = {
+ 0x03,0x10,
+ 0x41,0x00,
+ 0x03,0x20,
+ 0xb2,0x80,
+ 0xff,0xff,
+};
+
+uint8_t hi253_scene_mode_night[] = {
+ 0x03,0x10,
+ 0x41,0x15,
+ 0x03,0x20,
+ 0xb2,0xf0,
+ 0xff,0xff,
+};
+
+
+// White Balance
+uint8_t hi253_wb_auto [] = {
+ 0x03,0x22,
+ //0x11,0x2e,
+ 0x83,0x52, // RMAX
+ 0x84, 0x16, // RMIN
+ 0x85, 0x5a, // BMAX
+ 0x86, 0x23, // BMIN
+ 0xff,0xff,
+};
+
+uint8_t hi253_wb_incandescent [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x2a,
+ 0x82,0x3f,
+ 0x83,0x35,
+ 0x84,0x28,
+ 0x85,0x45,
+ 0x86,0x3b,
+ 0xff,0xff,
+};
+
+uint8_t hi253_wb_fluorescent [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x26,
+ 0x82,0x44,
+ 0x83,0x2f,
+ 0x84,0x24,
+ 0x85,0x4a,
+ 0x86,0x40,
+ 0xff,0xff,
+};
+
+uint8_t hi253_wb_daylight [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x3d,
+ 0x82,0x2e,
+ 0x83,0x40,
+ 0x84,0x33,
+ 0x85,0x33,
+ 0x86,0x28,
+ 0xff,0xff,
+};
+
+uint8_t hi253_wb_cloudy [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x50,
+ 0x82,0x25,
+ 0x83,0x55,
+ 0x84,0x4b,
+ 0x85,0x28,
+ 0x86,0x20,
+ 0xff,0xff,
+};
+
+
+// Exposure
+uint8_t hi253_exposure_neg6[] = {
+ 0x03,0x10,
+ 0x40,0xa8,
+};
+
+uint8_t hi253_exposure_neg3[] = {
+ 0x03,0x10,
+ 0x40,0x98,
+};
+
+uint8_t hi253_exposure_zero[] = {
+ 0x03,0x10,
+ 0x40,0x85,
+};
+
+uint8_t hi253_exposure_pos3[] = {
+ 0x03,0x10,
+ 0x40,0x08,
+};
+
+uint8_t hi253_exposure_pos6[] = {
+ 0x03,0x10,
+ 0x40,0x28,
+};
+
+
+// Resolution
+uint8_t hi253_1600_1200_rets[]={
+ 0x03, 0x00,
+ 0x10, 0x00,
+ 0x12, 0x04,
+ 0x20, 0x00,
+ 0x21, 0x0a,
+ 0x22, 0x00,
+ 0x23, 0x0a,
+
+ 0x03, 0x18,
+ 0x10, 0x00,
+ 0xff, 0xff,
+};
+
+uint8_t hi253_1280_720_regs[]={
+};
+
+uint8_t hi253_800_600_regs[]={
+
+ 0x03, 0x00,
+ 0x10, 0x10,
+ 0x12, 0x04,
+
+ 0x03, 0x18,
+ 0x10, 0x00,
+ 0xff, 0xff,
+};
+
+uint8_t hi253_640_480_regs[]={
+ 0x03, 0x00,
+ 0x10, 0x10,
+ 0x12, 0x04,
+
+ 0x03, 0x18,
+ 0x12, 0x20,
+ 0x10, 0x07,
+ 0x11, 0x00,
+ 0x20, 0x05,
+ 0x21, 0x00,
+ 0x22, 0x03,
+ 0x23, 0xc0,
+ 0x24, 0x00,
+ 0x25, 0x04,
+ 0x26, 0x00,
+ 0x27, 0x00,
+ 0x28, 0x05,
+ 0x29, 0x04,
+ 0x2a, 0x03,
+ 0x2b, 0xc0,
+ 0x2c, 0x0a,
+ 0x2d, 0x00,
+ 0x2e, 0x0a,
+ 0x2f, 0x00,
+ 0x30, 0x45,
+ 0xff, 0xff,
+};
+
+uint8_t hi253_320_240_regs[]= {
+ 0x01, 0xf9,
+ 0x08, 0x0f,
+ 0x01, 0xf8,
+
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+
+ 0x0e, 0x00,
+
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+
+ 0x0e, 0x00,
+ 0x01, 0xf1,
+ 0x08, 0x00,
+ 0x01, 0xf3,
+ 0x01, 0xf1,
+
+ 0x03, 0x20,
+ 0x10, 0x0c,
+ 0x03, 0x22,
+ 0x10, 0x69,
+
+ //Page 00
+ 0x03, 0x00,
+ 0x10, 0x11, //lxh
+ 0x11, 0x90,
+ 0x12, 0x04,
+ 0x0b, 0xaa,
+ 0x0c, 0xaa,
+ 0x0d, 0xaa,
+ 0x20, 0x00,
+ 0x21, 0x0a, //lxh
+ 0x22, 0x00,
+ 0x23, 0x0a, //lxh
+ 0x24, 0x04,
+ 0x25, 0xb0,
+ 0x26, 0x06,
+ 0x27, 0x40,
+
+ 0x40, 0x00,
+ 0x41, 0xe8,//lxh
+ 0x42, 0x00,
+ 0x43, 0x14,//lxh
+
+ 0x45, 0x04,
+ 0x46, 0x18,
+ 0x47, 0xd8,
+
+ 0xe1, 0x0f,
+
+ //BLC
+ 0x80, 0x2e,
+ 0x81, 0x7e,
+ 0x82, 0x90,
+ 0x83, 0x00,
+ 0x84, 0x0c,
+ 0x85, 0x00,
+ 0x90, 0x0c,
+ 0x91, 0x0c,
+ 0x92, 0x78,
+ 0x93, 0x70,
+ 0x94, 0x75,
+ 0x95, 0x70,
+ 0x96, 0xdc,
+ 0x97, 0xfe,
+ 0x98, 0x20,
+
+ //OutDoor BLC
+ 0x99,0x42,
+ 0x9a,0x42,
+ 0x9b,0x42,
+ 0x9c,0x42,
+
+ //Dark BLC
+ 0xa0, 0x02,
+ 0xa2, 0x02,
+ 0xa4, 0x02,
+ 0xa6, 0x02,
+
+ //Normal BLC
+ 0xa8, 0x42,
+ 0xaa, 0x42,
+ 0xac, 0x42,
+ 0xae, 0x42,
+
+ //Page 02
+ 0x03, 0x02,
+ 0x12, 0x03,
+ 0x13, 0x03,
+ 0x16, 0x00,
+ 0x17, 0x8C,
+ 0x18, 0x4c,
+ 0x19, 0x00,
+ 0x1a, 0x39,
+ 0x1c, 0x09,
+ 0x1d, 0x40,
+ 0x1e, 0x30,
+ 0x1f, 0x10,
+ 0x20, 0x77,
+ 0x21, 0xde,
+ 0x22, 0xa7,
+ 0x23, 0x30,
+ 0x27, 0x3c,
+ 0x2b, 0x80,
+ 0x2e, 0x00,
+ 0x2f, 0x00,
+ 0x30, 0x05,
+ 0x50, 0x20,
+ 0x52, 0x01,
+ 0x53, 0xc1,
+ 0x55, 0x1c,
+ 0x56, 0x11,
+ 0x5d, 0xA2,
+ 0x5e, 0x5a,
+ 0x60, 0x87,
+ 0x61, 0x99,
+ 0x62, 0x88,
+ 0x63, 0x97,
+ 0x64, 0x88,
+ 0x65, 0x97,
+ 0x67, 0x0c,
+ 0x68, 0x0c,
+ 0x69, 0x0c,
+ 0x72, 0x89,
+ 0x73, 0x96,
+ 0x74, 0x89,
+ 0x75, 0x96,
+ 0x76, 0x89,
+ 0x77, 0x96,
+ 0x7C, 0x85,
+ 0x7d, 0xaf,
+ 0x80, 0x01,
+ 0x81, 0x7f,
+ 0x82, 0x13,
+ 0x83, 0x24,
+ 0x84, 0x7d,
+ 0x85, 0x81,
+ 0x86, 0x7d,
+ 0x87, 0x81,
+ 0x92, 0x48,
+ 0x93, 0x54,
+ 0x94, 0x7d,
+ 0x95, 0x81,
+ 0x96, 0x7d,
+ 0x97, 0x81,
+ 0xa0, 0x02,
+ 0xa1, 0x7b,
+ 0xa2, 0x02,
+ 0xa3, 0x7b,
+ 0xa4, 0x7b,
+ 0xa5, 0x02,
+ 0xa6, 0x7b,
+ 0xa7, 0x02,
+ 0xa8, 0x85,
+ 0xa9, 0x8c,
+ 0xaa, 0x85,
+ 0xab, 0x8c,
+ 0xac, 0x10,
+ 0xad, 0x16,
+ 0xae, 0x10,
+ 0xaf, 0x16,
+ 0xb0, 0x99,
+ 0xb1, 0xa3,
+ 0xb2, 0xa4,
+ 0xb3, 0xae,
+ 0xb4, 0x9b,
+ 0xb5, 0xa2,
+ 0xb6, 0xa6,
+ 0xb7, 0xac,
+ 0xb8, 0x9b,
+ 0xb9, 0x9f,
+ 0xba, 0xa6,
+ 0xbb, 0xaa,
+ 0xbc, 0x9b,
+ 0xbd, 0x9f,
+ 0xbe, 0xa6,
+ 0xbf, 0xaa,
+ 0xc4, 0x2c,
+ 0xc5, 0x43,
+ 0xc6, 0x63,
+ 0xc7, 0x79,
+ 0xc8, 0x2d,
+ 0xc9, 0x42,
+ 0xca, 0x2d,
+ 0xcb, 0x42,
+ 0xcc, 0x64,
+ 0xcd, 0x78,
+ 0xce, 0x64,
+ 0xcf, 0x78,
+ 0xd0, 0x0a,
+ 0xd1, 0x09,
+ 0xd4, 0x0c,
+ 0xd5, 0x0c,
+ 0xd6, 0x78,
+ 0xd7, 0x70,//lxh
+ 0xe0, 0xc4,
+ 0xe1, 0xc4,
+ 0xe2, 0xc4,
+ 0xe3, 0xc4,
+ 0xe4, 0x00,
+ 0xe8, 0x80,
+ 0xe9, 0x40,
+ 0xea, 0x7f,
+ 0xf0, 0x01,
+ 0xf1, 0x01,
+ 0xf2, 0x01,
+ 0xf3, 0x01,
+ 0xf4, 0x01,
+
+ //PAGE10
+ 0x03, 0x10,
+ 0x10, 0x03, //lxh
+ 0x11, 0x03, //lxh,normal
+ 0x12, 0x30,
+ 0x13, 0x02,
+ 0x20, 0x00,
+
+ 0x40, 0x88,
+ 0x41, 0x05,
+ 0x48, 0x88,// 84
+ 0x50, 0x90,
+ 0x30, 0x00,
+ 0x31, 0x00,
+ 0x32, 0x00,
+ 0x33, 0x00,
+
+ 0x34, 0x30,
+ 0x35, 0x00,
+ 0x36, 0x00,
+ 0x38, 0x00,
+ 0x3e, 0x58,
+ 0x3f, 0x00,
+
+ //Saturation
+ 0x60, 0x67,
+ 0x61, 0x78,// 74
+ 0x62, 0x78,// 76
+ 0x63, 0x50,
+ 0x64, 0x41,
+
+ 0x66, 0x33,
+ 0x67, 0x00,
+
+ 0x6a, 0x90,
+ 0x6b, 0x80,
+ 0x6c, 0x80,
+ 0x6d, 0xa0,
+
+ 0x76, 0x01,
+ 0x74, 0x66,
+ 0x79, 0x06,
+
+ //Page 11
+ 0x03, 0x11,
+ 0x10, 0x7f,//lxh,3f
+ 0x11, 0x40,
+ 0x12, 0xba,
+ 0x13, 0xcb,
+ 0x26, 0x20,
+ 0x27, 0x22,
+ 0x28, 0x0f,
+ 0x29, 0x10,
+ 0x2b, 0x30,
+ 0x2c, 0x32,
+
+ //Out2 D-LPF th
+ 0x30, 0x70,
+ 0x31, 0x10,
+ 0x32, 0x65,
+ 0x33, 0x09,
+ 0x34, 0x06,
+ 0x35, 0x04,
+
+ //Out1 D-LPF th
+ 0x36, 0x70,
+ 0x37, 0x18,
+ 0x38, 0x65,
+ 0x39, 0x09,
+ 0x3a, 0x06,
+ 0x3b, 0x04,
+
+ //Indoor D-LPF th
+ 0x3c, 0x80,
+ 0x3d, 0x18,
+ 0x3e, 0x80,
+ 0x3f, 0x0c,
+ 0x40, 0x09,
+ 0x41, 0x06,
+
+ 0x42, 0x80,
+ 0x43, 0x18,
+ 0x44, 0x80,
+ 0x45, 0x12,
+ 0x46, 0x10,
+ 0x47, 0x10,
+ 0x48, 0x90,
+ 0x49, 0x40,
+ 0x4a, 0x80,
+ 0x4b, 0x13,
+ 0x4c, 0x10,
+ 0x4d, 0x11,
+ 0x4e, 0x80,
+ 0x4f, 0x30,
+ 0x50, 0x80,
+ 0x51, 0x13,
+ 0x52, 0x10,
+ 0x53, 0x13,
+ 0x54, 0x11,
+ 0x55, 0x17,
+ 0x56, 0x20,
+ 0x57, 0x20,
+ 0x58, 0x20,
+ 0x59, 0x30,
+ 0x5a, 0x18,
+ 0x5b, 0x00,
+ 0x5c, 0x00,
+ 0x60, 0x3f,
+ 0x62, 0x50,
+ 0x70, 0x06,
+
+ //Page 12
+ 0x03, 0x12,
+ 0x20, 0x0f,
+ 0x21, 0x0f,
+ 0x25, 0x30,
+ 0x28, 0x00,
+ 0x29, 0x00,
+ 0x2a, 0x00,
+ 0x30, 0x50,
+ 0x31, 0x18,
+ 0x32, 0x32,
+ 0x33, 0x40,
+ 0x34, 0x50,
+ 0x35, 0x70,
+ 0x36, 0xa0,
+
+ //Out2 th
+ 0x40, 0xa0,
+ 0x41, 0x40,
+ 0x42, 0xa0,
+ 0x43, 0x90,
+ 0x44, 0x90,
+ 0x45, 0x80,
+
+ //Out1 th
+ 0x46, 0xb0,
+ 0x47, 0x55,
+ 0x48, 0xa0,
+ 0x49, 0x90,
+ 0x4a, 0x90,
+ 0x4b, 0x80,
+
+ //In door th
+ 0x4c, 0xb0,
+ 0x4d, 0x40,
+ 0x4e, 0x90,
+ 0x4f, 0x90,
+ 0x50, 0xe6,
+ 0x51, 0x80,
+
+ //Dark1 th
+ 0x52, 0xb0,
+ 0x53, 0x60,
+ 0x54, 0xc0,
+ 0x55, 0xc0,
+ 0x56, 0xc0,
+ 0x57, 0x80,
+
+ //Dark2 th
+ 0x58, 0x90,
+ 0x59, 0x40,
+ 0x5a, 0xd0,
+ 0x5b, 0xd0,
+ 0x5c, 0xe0,
+ 0x5d, 0x80,
+
+ //Dark3 th
+ 0x5e, 0x88,
+ 0x5f, 0x40,
+ 0x60, 0xe0,
+ 0x61, 0xe6,
+ 0x62, 0xe6,
+ 0x63, 0x80,
+
+ 0x70, 0x15,
+ 0x71, 0x01,
+
+ 0x72, 0x18,
+ 0x73, 0x01,
+
+ 0x74, 0x25,
+ 0x75, 0x15,
+ 0x80, 0x30,
+ 0x81, 0x50,
+ 0x82, 0x80,
+ 0x85, 0x1a,
+ 0x88, 0x00,
+ 0x89, 0x00,
+ 0x90, 0x5d,
+
+ 0xc5, 0x30,
+ 0xc6, 0x2a,
+
+ 0xD0, 0x0c,
+ 0xD1, 0x80,
+ 0xD2, 0x67,
+ 0xD3, 0x00,
+ 0xD4, 0x00,
+ 0xD5, 0x02,
+ 0xD6, 0xff,
+ 0xD7, 0x18,
+ 0x3b, 0x06,
+ 0x3c, 0x06,
+
+ 0xc5, 0x30,
+ 0xc6, 0x2a,
+
+ //Page 13
+ 0x03, 0x13,
+ 0x10, 0xcb,
+ 0x11, 0x7b,
+ 0x12, 0x07,
+ 0x14, 0x00,
+
+ 0x20, 0x15,
+ 0x21, 0x13,
+ 0x22, 0x33,
+ 0x23, 0x04,
+ 0x24, 0x09,
+ 0x25, 0x08,
+ 0x26, 0x18,
+ 0x27, 0x30,
+ 0x29, 0x12,
+ 0x2a, 0x50,
+
+ //Low clip th
+ 0x2b, 0x06,
+ 0x2c, 0x06,
+ 0x25, 0x08,
+ 0x2d, 0x0c,
+ 0x2e, 0x12,
+ 0x2f, 0x12,
+
+ //Out2 Edge
+ 0x50, 0x10,
+ 0x51, 0x14,
+ 0x52, 0x10,
+ 0x53, 0x0c,
+ 0x54, 0x0f,
+ 0x55, 0x0c,
+
+ //Out1 Edge
+ 0x56, 0x10,
+ 0x57, 0x13,
+ 0x58, 0x10,
+ 0x59, 0x0c,
+ 0x5a, 0x0f,
+ 0x5b, 0x0c,
+
+ //Indoor Edge
+ 0x5c, 0x0a,
+ 0x5d, 0x0b,
+ 0x5e, 0x0a,
+ 0x5f, 0x08,
+ 0x60, 0x09,
+ 0x61, 0x08,
+
+ //Dark1 Edge
+ 0x62, 0x08,
+ 0x63, 0x08,
+ 0x64, 0x08,
+ 0x65, 0x06,
+ 0x66, 0x06,
+ 0x67, 0x06,
+
+ //Dark2 Edge
+ 0x68, 0x07,
+ 0x69, 0x07,
+ 0x6a, 0x07,
+ 0x6b, 0x05,
+ 0x6c, 0x05,
+ 0x6d, 0x05,
+
+ //Dark3 Edge
+ 0x6e, 0x07,
+ 0x6f, 0x07,
+ 0x70, 0x07,
+ 0x71, 0x05,
+ 0x72, 0x05,
+ 0x73, 0x05,
+
+ //2DY
+ 0x80, 0xfd,
+ 0x81, 0x1f,
+ 0x82, 0x05,
+ 0x83, 0x01,
+
+ 0x90, 0x15,
+ 0x91, 0x15,
+ 0x92, 0x33,
+ 0x93, 0x30,
+ 0x94, 0x03,
+ 0x95, 0x14,
+ 0x97, 0x30,
+ 0x99, 0x30,
+
+ 0xa0, 0x04,
+ 0xa1, 0x05,
+ 0xa2, 0x04,
+ 0xa3, 0x05,
+ 0xa4, 0x07,
+ 0xa5, 0x08,
+ 0xa6, 0x07,
+ 0xa7, 0x08,
+ 0xa8, 0x07,
+ 0xa9, 0x08,
+ 0xaa, 0x07,
+ 0xab, 0x08,
+
+ //Out2
+ 0xb0, 0x22,
+ 0xb1, 0x2a,
+ 0xb2, 0x28,
+ 0xb3, 0x22,
+ 0xb4, 0x2a,
+ 0xb5, 0x28,
+
+ //Out1
+ 0xb6, 0x22,
+ 0xb7, 0x2a,
+ 0xb8, 0x28,
+ 0xb9, 0x22,
+ 0xba, 0x2a,
+ 0xbb, 0x28,
+
+ 0xbc, 0x17,
+ 0xbd, 0x17,
+ 0xbe, 0x17,
+ 0xbf, 0x17,
+ 0xc0, 0x17,
+ 0xc1, 0x17,
+
+ //Dark1
+ 0xc2, 0x1e,
+ 0xc3, 0x12,
+ 0xc4, 0x10,
+ 0xc5, 0x1e,
+ 0xc6, 0x12,
+ 0xc7, 0x10,
+
+ //Dark2
+ 0xc8, 0x18,
+ 0xc9, 0x05,
+ 0xca, 0x05,
+ 0xcb, 0x18,
+ 0xcc, 0x05,
+ 0xcd, 0x05,
+
+ //Dark3
+ 0xce, 0x18,
+ 0xcf, 0x05,
+ 0xd0, 0x05,
+ 0xd1, 0x18,
+ 0xd2, 0x05,
+ 0xd3, 0x05,
+
+ //Page 14
+ 0x03, 0x14,
+ 0x10, 0x11,
+ 0x20, 0x40,
+ 0x21, 0x80,
+ 0x23, 0x80,
+ 0x22, 0x80,
+ 0x23, 0x80,
+ 0x24, 0x80,
+
+ 0x30, 0xc8,
+ 0x31, 0x2b,
+ 0x32, 0x00,
+ 0x33, 0x00,
+ 0x34, 0x90,
+
+ 0x40, 0x40,
+ 0x50, 0x2d,
+ 0x60, 0x28,
+ 0x70, 0x2d,
+
+ //Page 15
+ 0x03, 0x15,
+ 0x10, 0x0f,
+ 0x14, 0x52,
+ 0x15, 0x42,
+ 0x16, 0x32,
+ 0x17, 0x2f,
+
+ //CMC
+ 0x30, 0x8f,
+ 0x31, 0x59,
+ 0x32, 0x0a,
+ 0x33, 0x15,
+ 0x34, 0x5b,
+ 0x35, 0x06,
+ 0x36, 0x07,
+ 0x37, 0x40,
+ 0x38, 0x86,
+
+ //CMC OFS
+ 0x40, 0x95,
+ 0x41, 0x1f,
+ 0x42, 0x8a,
+ 0x43, 0x86,
+ 0x44, 0x0a,
+ 0x45, 0x84,
+ 0x46, 0x87,
+ 0x47, 0x9b,
+ 0x48, 0x23,
+
+ //CMC POFS
+ 0x50, 0x8c,
+ 0x51, 0x0c,
+ 0x52, 0x00,
+ 0x53, 0x07,
+ 0x54, 0x17,
+ 0x55, 0x9d,
+ 0x56, 0x00,
+ 0x57, 0x0b,
+ 0x58, 0x89,
+
+ 0x80, 0x03,
+ 0x85, 0x40,
+ 0x87, 0x02,
+ 0x88, 0x00,
+ 0x89, 0x00,
+ 0x8a, 0x00,
+
+ 0x03, 0x16,
+ 0x10, 0x31,//GMA_CTL
+ 0x18, 0x5e,//AG_ON
+ 0x19, 0x5d,//AG_OFF
+ 0x1A, 0x0e,//TIME_ON
+ 0x1B, 0x01,//TIME_OFF
+ 0x1C, 0xdc,//OUT_ON
+ 0x1D, 0xfe,//OUT_OFF
+ //GMA
+ 0x30, 0x00,
+ 0x31, 0x09,
+ 0x32, 0x15,
+ 0x33, 0x29,
+ 0x34, 0x53,
+ 0x35, 0x6c,
+ 0x36, 0x81,
+ 0x37, 0x94,
+ 0x38, 0xa4,
+ 0x39, 0xb3,
+ 0x3a, 0xc0,
+ 0x3b, 0xcb,
+ 0x3c, 0xd5,
+ 0x3d, 0xde,
+ 0x3e, 0xe6,
+ 0x3f, 0xee,
+ 0x40, 0xf5,
+ 0x41, 0xfc,
+ 0x42, 0xff,
+ //RGMA
+ 0x50, 0x00,
+ 0x51, 0x09,
+ 0x52, 0x1f,
+ 0x53, 0x37,
+ 0x54, 0x5b,
+ 0x55, 0x76,
+ 0x56, 0x8d,
+ 0x57, 0xa1,
+ 0x58, 0xb2,
+ 0x59, 0xbe,
+ 0x5a, 0xc9,
+ 0x5b, 0xd2,
+ 0x5c, 0xdb,
+ 0x5d, 0xe3,
+ 0x5e, 0xeb,
+ 0x5f, 0xf0,
+ 0x60, 0xf5,
+ 0x61, 0xf7,
+ 0x62, 0xf8,
+ //BGMA
+ 0x70, 0x00,
+ 0x71, 0x08,
+ 0x72, 0x17,
+ 0x73, 0x2f,
+ 0x74, 0x53,
+ 0x75, 0x6c,
+ 0x76, 0x81,
+ 0x77, 0x94,
+ 0x78, 0xa4,
+ 0x79, 0xb3,
+ 0x7a, 0xc0,
+ 0x7b, 0xcb,
+ 0x7c, 0xd5,
+ 0x7d, 0xde,
+ 0x7e, 0xe6,
+ 0x7f, 0xee,
+ 0x80, 0xf4,
+ 0x81, 0xfa,
+ 0x82, 0xff,
+
+ 0x03, 0x17,
+ 0xc4, 0x6e,
+ 0xc5, 0x5c,
+
+
+ 0x03, 0x18,
+ 0x12, 0x20,
+ 0x10, 0x07,
+ 0x11, 0x00,
+ 0x20, 0x02,
+ 0x21, 0x80,
+ 0x22, 0x00,
+ 0x23, 0xf0,
+ 0x24, 0x00,
+ 0x25, 0x06,
+ 0x26, 0x00,
+ 0x27, 0x00,
+ 0x28, 0x02,
+ 0x29, 0x86,
+ 0x2a, 0x00,
+ 0x2b, 0xf0,
+ 0x2c, 0x14,
+ 0x2d, 0x00,
+ 0x2e, 0x14,
+ 0x2f, 0x00,
+ 0x30, 0x65,
+ 0x03, 0x20,
+ 0x10, 0x1c,
+ 0x18, 0x38,
+ 0x20, 0x01,
+ 0x21, 0x30,
+ 0x22, 0x10,
+ 0x23, 0x00,
+ 0x24, 0x04,
+
+ 0x28, 0xff,
+ 0x29, 0xad,
+
+ 0x2a, 0xf0,
+ 0x2b, 0x34,
+ 0x30, 0x78,
+ 0x2c, 0xc3,
+ 0x2d, 0x5f,
+ 0x2e, 0x33,
+ // 0x30, 0xf8,
+ 0x32, 0x03,
+ 0x33, 0x2e,
+ 0x34, 0x30,
+ 0x35, 0xd4,
+ 0x36, 0xfe,
+ 0x37, 0x32,
+ 0x38, 0x04,
+ 0x47, 0xf0,
+
+ //Y_Frame TH
+ 0x50, 0x45,
+ 0x51, 0x88,
+
+ 0x56, 0x10,
+ 0x57, 0xb7,
+ 0x58, 0x14,
+ 0x59, 0x88,
+ 0x5a, 0x04,
+
+ 0x60, 0x55,
+ 0x61, 0x55,
+ 0x62, 0x6a,
+ 0x63, 0xa9,
+ 0x64, 0x6a,
+ 0x65, 0xa9,
+ 0x66, 0x6a,
+ 0x67, 0xa9,
+ 0x68, 0x6b,
+ 0x69, 0xe9,
+ 0x6a, 0x6a,
+ 0x6b, 0xa9,
+ 0x6c, 0x6a,
+ 0x6d, 0xa9,
+ 0x6e, 0x55,
+ 0x6f, 0x55,
+ // 0x70, 0x42,
+ 0x70, 0x42,
+ 0x71, 0xBb,
+
+ // haunting control
+ 0x76, 0x21,
+ 0x77, 0x02,
+ 0x78, 0x22,
+ 0x79, 0x2a,
+
+ 0x78, 0x24,
+ 0x79, 0x23,
+ 0x7a, 0x23,
+ 0x7b, 0x22,
+ 0x7d, 0x23,
+ 0x83, 0x01, //EXP Normal 33.33 fps
+ 0x84, 0x5f,
+ 0x85, 0x90,
+ 0x86, 0x01, //EXPMin 7500.00 fps
+ 0x87, 0xf4,
+ 0x88, 0x05, //EXP Max 8.33 fps
+ 0x89, 0x7e,
+ 0x8a, 0x40,
+ 0x8B, 0x75, //EXP100
+ 0x8C, 0x30,
+ 0x8D, 0x61, //EXP120
+ 0x8E, 0xa8,
+ 0x9c, 0x17, //EXP Limit 500.00 fps
+ 0x9d, 0x70,
+ 0x9e, 0x01, //EXP Unit
+ 0x9f, 0xf4,
+ 0xa0, 0x03,
+ 0xa1, 0xa9,
+ 0xa2, 0x80,
+ 0xb0, 0x1d,
+ 0xb1, 0x1a,
+ 0xb2, 0x80,
+ 0xb3, 0x1a,
+ 0xb4, 0x1a,
+ 0xb5, 0x44,
+ 0xb6, 0x2f,
+ 0xb7, 0x28,
+ 0xb8, 0x25,
+ 0xb9, 0x22,
+ 0xba, 0x21,
+ 0xbb, 0x20,
+ 0xbc, 0x1f,
+ 0xbd, 0x1f,
+ 0xc0, 0x30,
+ 0xc1, 0x20,
+ 0xc2, 0x20,
+ 0xc3, 0x20,
+ 0xc4, 0x08,
+ 0xc8, 0x60,
+ 0xc9, 0x40,
+
+ //Page 22
+ 0x03, 0x22, // Page 22
+ 0x10, 0xfd, // AWBCTL1
+ 0x11, 0x2e, // AWBCTL2
+ 0x19, 0x01,
+ 0x20, 0x30,
+ 0x21, 0x80,
+ 0x23, 0x08,
+ 0x24, 0x01,
+
+ 0x30, 0x80, // ULVL
+ 0x31, 0x80, // VLVL
+ 0x38, 0x11, // UVTH1
+ 0x39, 0x34, // UVTH2
+ 0x40, 0xf7, // YRANGE
+
+ 0x41, 0x77, // CDIFF
+ 0x42, 0x55, // CSUM2
+ 0x43, 0xf0,
+ 0x44, 0x66,
+ 0x45, 0x33,
+ 0x46, 0x01, // WHTPXLTH1
+ 0x47, 0x94,
+
+ 0x50, 0xb2,
+ 0x51, 0x81,
+ 0x52, 0x98,
+
+ 0x80, 0x38, // RGAIN
+ 0x81, 0x20, // GGAIN
+ 0x82, 0x38, // BGAIN
+
+ 0x83, 0x5e, // RMAX
+ 0x84, 0x20, // RMIN
+ 0x85, 0x53, // BMAX
+ 0x86, 0x15, // BMIN
+
+ 0x87, 0x54, // RMAXM
+ 0x88, 0x20, // RMINM
+ 0x89, 0x3f, // BMAXM
+ 0x8a, 0x1c, // BMINM
+
+ 0x8b, 0x54, // RMAXB
+ 0x8c, 0x3f, // RMINB
+ 0x8d, 0x24, // BMAXB
+ 0x8e, 0x1c, // BMINB
+
+ 0x8f, 0x60, // BGAINPARA1
+ 0x90, 0x5f, // BGAINPARA2
+ 0x91, 0x5c, // BGAINPARA3
+ 0x92, 0x4C, // BGAINPARA4
+ 0x93, 0x41, // BGAINPARA5
+ 0x94, 0x3b, // BGAINPARA6
+ 0x95, 0x36, // BGAINPARA7
+ 0x96, 0x30, // BGAINPARA8
+ 0x97, 0x27, // BGAINPARA9
+ 0x98, 0x20, // BGAINPARA10
+ 0x99, 0x1C, // BGAINPARA11
+ 0x9a, 0x19, // BGAINPARA12
+
+ 0x9b, 0x88, // BGAINBND1
+ 0x9c, 0x88, // BGAINBND2
+ 0x9d, 0x48, // RGAINTH1
+ 0x9e, 0x38, // RGAINTH2
+ 0x9f, 0x30, // RGAINTH3
+
+ 0xa0, 0x74, // RDELTA1
+ 0xa1, 0x35, // BDELTA1
+ 0xa2, 0xaf, // RDELTA2
+ 0xa3, 0xf7, // BDELTA2
+
+ 0xa4, 0x10, // AWBEXPLMT1
+ 0xa5, 0x50, // AWBEXPLMT2
+ 0xa6, 0xc4, // AWBEXPLMT3
+
+ 0xad, 0x40,
+ 0xae, 0x4a,
+
+ 0xaf, 0x2a,
+ 0xb0, 0x29,
+
+ 0xb1, 0x20,
+ 0xb4, 0xff,
+ 0xb8, 0x6b,
+ 0xb9, 0x00,
+
+
+ 0x03, 0x24,
+ 0x10, 0x01,
+ 0x18, 0x06,
+ 0x30, 0x06,
+ 0x31, 0x90,
+ 0x32, 0x25,
+ 0x33, 0xa2,
+ 0x34, 0x26,
+ 0x35, 0x58,
+ 0x36, 0x60,
+ 0x37, 0x00,
+ 0x38, 0x50,
+ 0x39, 0x00,
+
+ 0x03, 0x20,
+ 0x10, 0x9c,
+ 0x03, 0x22,
+ 0x10, 0xe9,
+
+ //Page 00
+ 0x03, 0x00,
+ 0x0e, 0x03,
+ 0x0e, 0x73,
+
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+
+ 0x03, 0x00,
+ 0x01, 0xf8,
+
+};
+
+uint8_t hi253_default_regs_init[]={
+
+0x01, 0xf9, //sleep on
+0x08, 0x0f, //Hi-Z on
+0x01, 0xf8, //sleep off
+0x03, 0x00, // Dummy 750us START
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00, // Dummy 750us END
+0x0e, 0x03, //PLL On
+0x0e, 0x73, //PLLx2
+0x03, 0x00, // Dummy 750us START
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00, // Dummy 750us END
+0x0e, 0x00, //PLL off
+0x01, 0xf1, //sleep on
+0x08, 0x00, //Hi-Z off
+0x01, 0xf3,
+0x01, 0xf1,
+// PAGE 20
+0x03, 0x20, //page 20
+0x10, 0x1c, //ae off
+// PAGE 22
+0x03, 0x22, //page 22
+0x10, 0x69, //awb off
+/////// PAGE 0 START ///////
+0x03, 0x00,
+0x10, 0x10, // Sub1/2_Preview2 Mode_H binning
+0x11, 0x90,
+0x12, 0x04,//00
+0x0b, 0xaa, // ESD Check Register
+0x0c, 0xaa, // ESD Check Register
+0x0d, 0xaa, // ESD Check Register
+0x20, 0x00, // Windowing start point Y
+0x21, 0x04,
+0x22, 0x00, // Windowing start point X
+0x23, 0x07,
+0x24, 0x04,
+0x25, 0xb0,
+0x26, 0x06,
+0x27, 0x40, // WINROW END
+0x40, 0x01, //Hblank 408
+0x41, 0x68,
+0x42, 0x00, //Vblank 20
+0x43, 0x14,
+0x45, 0x04,
+0x46, 0x18,
+0x47, 0xd8,
+//BLC
+0x80, 0x2e,
+0x81, 0x7e,
+0x82, 0x90,
+0x83, 0x00,
+0x84, 0x0c,
+0x85, 0x00,
+0x90, 0x0e, //BLC_TIME_TH_ON
+0x91, 0x0e, //BLC_TIME_TH_OFF
+0x92, 0x78, //BLC_AG_TH_ON
+0x93, 0x70, //BLC_AG_TH_OFF
+0x94, 0x75,
+0x95, 0x70,
+0x96, 0xdc,
+0x97, 0xfe,
+0x98, 0x38,
+//OutDoor BLC
+0x99, 0x42,
+0x9a, 0x42,
+0x9b, 0x42,
+0x9c, 0x42,
+//Dark BLC
+0xa0, 0x00,
+0xa2, 0x00,
+0xa4, 0x00,
+0xa6, 0x00,
+//Normal BLC
+0xa8, 0x42,
+0xaa, 0x42,
+0xac, 0x42,
+0xae, 0x42,
+/////// PAGE 2 START ///////
+0x03, 0x02, //Page 02
+0x10, 0x00, //Mode_test
+0x11, 0x00, //Mode_dead_test
+0x12, 0x03, //pwr_ctl_ctl1
+0x13, 0x03, //Mode_ana_test
+0x14, 0x00, //mode_memory
+0x16, 0x00, //dcdc_ctl1
+0x17, 0x8c, //dcdc_ctl2
+0x18, 0x4C, //analog_func1
+0x19, 0x00, //analog_func2
+0x1a, 0x39, //analog_func3
+0x1b, 0x00, //analog_func4
+0x1c, 0x09, //dcdc_ctl3
+0x1d, 0x40, //dcdc_ctl4
+0x1e, 0x30, //analog_func7
+0x1f, 0x10, //analog_func8
+0x20, 0x77, //pixel bias
+0x21, 0xde, //adc,asp bias
+0x22, 0xa7, //main,bus bias
+0x23, 0x30, //clamp
+0x24, 0x4a,
+0x25, 0x10,
+0x27, 0x3c,
+0x28, 0x00,
+0x29, 0x0c,
+0x2a, 0x80,
+0x2b, 0x80,
+0x2c, 0x02,
+0x2d, 0xa0,
+0x2e, 0x00, //11
+0x2f, 0x00, //a1
+0x30, 0x05, //swap_ctl
+0x31, 0x99,
+0x32, 0x00,
+0x33, 0x00,
+0x34, 0x22,
+0x38, 0x88,
+0x39, 0x88,
+0x50, 0x20,
+0x51, 0x03, //00 don't touch
+0x52, 0x01,
+0x53, 0xc1,
+0x54, 0x10,
+0x55, 0x1c,
+0x56, 0x11,
+0x58, 0x10,
+0x59, 0x0e,
+0x5d, 0xa2,
+0x5e, 0x5a,
+0x60, 0x87,
+0x61, 0x99,
+0x62, 0x88,
+0x63, 0x97,
+0x64, 0x88,
+0x65, 0x97,
+0x67, 0x0c,
+0x68, 0x0c,
+0x69, 0x0c,
+0x6a, 0xb4,
+0x6b, 0xc4,
+0x6c, 0xb5,
+0x6d, 0xc2,
+0x6e, 0xb5,
+0x6f, 0xc0,
+0x70, 0xb6,
+0x71, 0xb8,
+0x72, 0x89,
+0x73, 0x96,
+0x74, 0x89,
+0x75, 0x96,
+0x76, 0x89,
+0x77, 0x96,
+0x7c, 0x85,
+0x7d, 0xaf,
+0x80, 0x01,
+0x81, 0x7f,
+0x82, 0x13,
+0x83, 0x24,
+0x84, 0x7d,
+0x85, 0x81,
+0x86, 0x7d,
+0x87, 0x81,
+0x88, 0xab,
+0x89, 0xbc,
+0x8a, 0xac,
+0x8b, 0xba,
+0x8c, 0xad,
+0x8d, 0xb8,
+0x8e, 0xae,
+0x8f, 0xb2,
+0x90, 0xb3,
+0x91, 0xb7,
+0x92, 0x48,
+0x93, 0x54,
+0x94, 0x7d,
+0x95, 0x81,
+0x96, 0x7d,
+0x97, 0x81,
+0xa0, 0x02,
+0xa1, 0x7b,
+0xa2, 0x02,
+0xa3, 0x7b,
+0xa4, 0x7b,
+0xa5, 0x02,
+0xa6, 0x7b,
+0xa7, 0x02,
+0xa8, 0x85,
+0xa9, 0x8c,
+0xaa, 0x85,
+0xab, 0x8c,
+0xac, 0x10,
+0xad, 0x16,
+0xae, 0x10,
+0xaf, 0x16,
+0xb0, 0x99,
+0xb1, 0xa3,
+0xb2, 0xa4,
+0xb3, 0xae,
+0xb4, 0x9b,
+0xb5, 0xa2,
+0xb6, 0xa6,
+0xb7, 0xac,
+0xb8, 0x9b,
+0xb9, 0x9f,
+0xba, 0xa6,
+0xbb, 0xaa,
+0xbc, 0x9b,
+0xbd, 0x9f,
+0xbe, 0xa6,
+0xbf, 0xaa,
+0xc4, 0x2c,
+0xc5, 0x43,
+0xc6, 0x63,
+0xc7, 0x79,
+0xc8, 0x2d,
+0xc9, 0x42,
+0xca, 0x2d,
+0xcb, 0x42,
+0xcc, 0x64,
+0xcd, 0x78,
+0xce, 0x64,
+0xcf, 0x78,
+0xd0, 0x0a,
+0xd1, 0x09,
+0xd2, 0x20,
+0xd3, 0x00,
+0xd4, 0x0e, //DCDC_TIME_TH_ON don't touch
+0xd5, 0x0e, //DCDC_TIME_TH_OFF don't touch
+0xd6, 0x78, //DCDC_AG_TH_ON 78
+0xd7, 0x70, //DCDC_AG_TH_OFF 70
+0xe0, 0xc4,
+0xe1, 0xc4,
+0xe2, 0xc4,
+0xe3, 0xc4,
+0xe4, 0x00,
+0xe8, 0x80,
+0xe9, 0x40,
+0xea, 0x7f,
+0xf0, 0x01,
+0xf1, 0x01,
+0xf2, 0x01,
+0xf3, 0x01,
+0xf4, 0x01,
+/////// PAGE 3 ///////
+0x03, 0x03,
+0x10, 0x10,
+/////// PAGE 10 START ///////
+0x03, 0x10,
+0x10, 0x03, // CrYCbY // For Demoset 0x03
+0x12, 0x30,
+0x13, 0x0a,//add jacky open constrast
+0x20, 0x00,
+0x30, 0x00,
+0x31, 0x00,
+0x32, 0x00,
+0x33, 0x00,
+0x34, 0x30,
+0x35, 0x00,
+0x36, 0x00,
+0x38, 0x00,
+0x3e, 0x58,
+0x3f, 0x00,
+0x40, 0x85, // YOFS
+0x41, 0x00, // DYOFS
+0x48, 0x8a,
+0x60, 0x67,//6b AG ratio jacky
+
+0x61, 0x7e, //7e //8e //88 //70
+0x62, 0x7e, //7e //8e //88 //80
+0x63, 0x50, //Double_AG 50->30
+0x64, 0x41,
+0x66, 0x42,
+0x67, 0x20,
+0x6a, 0x80, //8a
+0x6b, 0x84, //74
+0x6c, 0x80, //7e //7a
+0x6d, 0x80, //8e
+/////// PAGE 11 START ///////
+0x03, 0x11,
+0x10, 0x7f,
+0x11, 0x40,
+0x12, 0x0a, // Blue Max-Filter Delete
+0x13, 0xbb,
+0x26, 0x51, // Double_AG 31->20
+0x27, 0x54, // Double_AG 34->22
+0x28, 0x0f,
+0x29, 0x10,
+0x2b, 0x30,
+0x2c, 0x32,
+//Out2 D-LPF th
+0x30, 0x70,
+0x31, 0x10,
+0x32, 0x58,
+0x33, 0x09,
+0x34, 0x06,
+0x35, 0x03,
+//Out1 D-LPF th
+0x36, 0x70,
+0x37, 0x18,
+0x38, 0x58,
+0x39, 0x09,
+0x3a, 0x06,
+0x3b, 0x03,
+//Indoor D-LPF th
+0x3c, 0x80,
+0x3d, 0x18,
+0x3e, 0x80,
+0x3f, 0x0c,
+0x40, 0x09,
+0x41, 0x06,
+0x42, 0x80,
+0x43, 0x18,
+0x44, 0x90,
+0x45, 0x12,
+0x46, 0x10,
+0x47, 0x10,
+0x48, 0x90,
+0x49, 0x40,
+0x4a, 0x80,
+0x4b, 0x13,
+0x4c, 0x10,
+0x4d, 0x11,
+0x4e, 0x80,
+0x4f, 0x30,
+0x50, 0x80,
+0x51, 0x13,
+0x52, 0x10,
+0x53, 0x13,
+0x54, 0x11,
+0x55, 0x17,
+0x56, 0x20,
+0x57, 0x01,
+0x58, 0x00,
+0x59, 0x00,
+0x5a, 0x1f, //18
+0x5b, 0x00,
+0x5c, 0x00,
+0x60, 0x3f,
+0x62, 0x60,
+0x70, 0x06,
+/////// PAGE 12 START ///////
+0x03, 0x12,
+0x20, 0x0f,
+0x21, 0x0f,
+0x25, 0x00, //0x30
+0x28, 0x00,
+0x29, 0x00,
+0x2a, 0x00,
+0x30, 0x50,
+0x31, 0x18,
+0x32, 0x32,
+0x33, 0x40,
+0x34, 0x50,
+0x35, 0x70,
+0x36, 0xa0,
+//Out2 th
+0x40, 0xa0,
+0x41, 0x40,
+0x42, 0xa0,
+0x43, 0x90,
+0x44, 0x90,
+0x45, 0x80,
+//Out1 th
+0x46, 0xb0,
+0x47, 0x55,
+0x48, 0xa0,
+0x49, 0x90,
+0x4a, 0x90,
+0x4b, 0x80,
+//Indoor th
+0x4c, 0xb0,
+0x4d, 0x40,
+0x4e, 0x90,
+0x4f, 0x90,
+0x50, 0xa0,
+0x51, 0x80,
+//Dark1 th
+0x52, 0x00, //b0
+0x53, 0x50, //60
+0x54, 0xd4, //c0
+0x55, 0xc0, //c0
+0x56, 0x70, //b0
+0x57, 0xec, //70
+//Dark2 th
+0x58, 0x60, //90
+0x59, 0x40, //
+0x5a, 0xd0,
+0x5b, 0xd0,
+0x5c, 0xc0,
+0x5d, 0x9b, //70
+//Dark3 th
+0x5e, 0x70,//88
+0x5f, 0x40,
+0x60, 0xe0,
+0x61, 0xe0,
+0x62, 0xe0,
+0x63, 0xb4,//80
+0x70, 0x15,
+0x71, 0x01, //Don't Touch register
+0x72, 0x18,
+0x73, 0x01, //Don't Touch register
+0x74, 0x25,
+0x75, 0x15,
+0x80, 0x20,
+0x81, 0x40,
+0x82, 0x65,
+0x85, 0x1a,
+0x88, 0x00,
+0x89, 0x00,
+0x90, 0x5d, //For capture
+0x91, 0x88,
+0x98, 0x7d,
+0x99, 0x28,
+0x9A, 0x14,
+0x9B, 0xc8,
+0x9C, 0x02,
+0x9D, 0x1e,
+0x9E, 0x28,
+0x9F, 0x07,
+0xA0, 0x32,
+0xA4, 0x04,
+0xA5, 0x0e,
+0xA6, 0x0c,
+0xA7, 0x04,
+0xA8, 0x3c,
+
+0xAA, 0x14,
+0xAB, 0x11,
+0xAC, 0x0f,
+0xAD, 0x16,
+0xAE, 0x15,
+0xAF, 0x14,
+
+0xB1, 0xaa,
+0xB2, 0x96,
+0xB3, 0x28,
+//Dont Touch register
+0xB8, 0x78,
+0xB9, 0xa0,
+0xBA, 0xb4,
+0xBB, 0x14,
+0xBC, 0x14,
+0xBD, 0x00, //14
+0xBE, 0x64,
+0xBF, 0x64,
+0xC0, 0x64,
+0xC1, 0x64,
+0xC2, 0x04,
+0xC3, 0x03,
+0xC4, 0x0c,
+0xC5, 0x30,
+0xC6, 0x2a,
+0xD0, 0x0c, //CI Option/CI DPC
+0xD1, 0x80,
+0xD2, 0x67,
+0xD3, 0x00,
+0xD4, 0x00,
+0xD5, 0x02,
+0xD6, 0xff,
+0xD7, 0x18,
+//End
+0x3b, 0x06,
+0x3c, 0x06,
+0xc5, 0x00,//55->48
+0xc6, 0x00,//48->40
+/////// PAGE 13 START ///////
+0x03, 0x13,
+//Edge
+0x10, 0xcb,
+0x11, 0x7b,
+0x12, 0x07,
+0x14, 0x00,
+0x20, 0x15,
+0x21, 0x13,
+0x22, 0x33,
+0x23, 0x05,
+0x24, 0x09,
+0x26, 0x18,
+0x27, 0x30,
+0x29, 0x12,
+0x2a, 0x50,
+//Low clip th
+0x2b, 0x01, //Out2 02
+0x2c, 0x01, //Out1 02
+0x25, 0x06,
+0x2d, 0x0c,
+0x2e, 0x12,
+0x2f, 0x12,
+//Out2 Edge
+0x50, 0x18,
+0x51, 0x1c,
+0x52, 0x1b,
+0x53, 0x15,
+0x54, 0x18,
+0x55, 0x15,
+//Out1 Edge
+0x56, 0x18,
+0x57, 0x1c,
+0x58, 0x1b,
+0x59, 0x15,
+0x5a, 0x18,
+0x5b, 0x15,
+//Indoor Edge
+0x5c, 0x2b,
+0x5d, 0x3c,
+0x5e, 0x2a,
+0x5f, 0x28,
+0x60, 0x39,
+0x61, 0x28,
+//Dark1 Edge
+0x62, 0x18,
+0x63, 0x28,
+0x64, 0x18,
+0x65, 0x16,
+0x66, 0x26,
+0x67, 0x16,
+//Dark2 Edge
+0x68, 0x07,
+0x69, 0x17,
+0x6a, 0x07,
+0x6b, 0x05,
+0x6c, 0x15,
+0x6d, 0x05,
+//Dark3 Edge
+0x6e, 0x07,
+0x6f, 0x07,
+0x70, 0x07,
+0x71, 0x05,
+0x72, 0x05,
+0x73, 0x05,
+0x80, 0xfd,
+0x81, 0x1f,
+0x82, 0x05,
+0x83, 0x31,
+0x90, 0x05,
+0x91, 0x05,
+0x92, 0x33,
+0x93, 0x30,
+0x94, 0x03,
+0x95, 0x14,
+0x97, 0x20,
+0x99, 0x20,
+0xa0, 0x01,
+0xa1, 0x02,
+0xa2, 0x01,
+0xa3, 0x02,
+0xa4, 0x05,
+0xa5, 0x05,
+0xa6, 0x07,
+0xa7, 0x08,
+0xa8, 0x07,
+0xa9, 0x08,
+0xaa, 0x07,
+0xab, 0x08,
+0xb0, 0x22,
+0xb1, 0x2a,
+0xb2, 0x28,
+0xb3, 0x22,
+0xb4, 0x2a,
+0xb5, 0x28,
+0xb6, 0x22,
+0xb7, 0x2a,
+0xb8, 0x28,
+0xb9, 0x22,
+0xba, 0x2a,
+0xbb, 0x28,
+//Indoor
+0xbc, 0x15,
+0xbd, 0x2a,
+0xbe, 0x27,
+0xbf, 0x15,
+0xc0, 0x2a,
+0xc1, 0x27,
+//Dark1
+0xc2, 0x1e,
+0xc3, 0x24,
+0xc4, 0x10,
+0xc5, 0x1e,
+0xc6, 0x24,
+0xc7, 0x10,
+//Dark2
+0xc8, 0x18,
+0xc9, 0x20,
+0xca, 0x1e,
+0xcb, 0x18,
+0xcc, 0x20,
+0xcd, 0x1e,
+//Dark3
+0xce, 0x18,
+0xcf, 0x20,
+0xd0, 0x1e,
+0xd1, 0x18,
+0xd2, 0x20,
+0xd3, 0x1e,
+/////// PAGE 14 START ///////
+0x03, 0x14,
+0x10, 0x11,
+0x14, 0x80, // GX
+0x15, 0x80, // GY
+0x16, 0x80, // RX
+0x17, 0x80, // RY
+0x18, 0x80, // BX
+0x19, 0x80, // BY
+0x20, 0x80, //X 60 //a0
+0x21, 0x80, //Y
+0x22, 0x78,
+0x23, 0x80,
+0x24, 0x80,
+0x30, 0xc8,
+0x31, 0x2b,
+0x32, 0x00,
+0x33, 0x00,
+0x34, 0x90,
+0x40, 0x56, //0x48//65
+0x50, 0x44, //0x34
+0x60, 0x46, //0x29
+0x70, 0x44, //0x34
+/////// PAGE 15 START ///////
+0x03, 0x15,
+0x10, 0x0f,
+//Rstep H 16
+//Rstep L 14
+0x14, 0x4f, //CMCOFSGH
+0x15, 0x32, //CMCOFSGM
+0x16, 0x24, //CMCOFSGL
+0x17, 0x2f, //CMC SIGN
+//CMC
+0x30, 0x91,
+0x31, 0x6a,
+0x32, 0x18,
+0x33, 0x17,
+0x34, 0x53,
+0x35, 0x08,
+0x36, 0x08,
+0x37, 0x3e,
+0x38, 0x87,
+//CMC OFS
+0x40, 0xa3,
+0x41, 0x3a,
+0x42, 0x93,
+0x43, 0x0b,
+0x44, 0x86,
+0x45, 0x06,
+0x46, 0x00,
+0x47, 0xa2,
+0x48, 0x22,
+//CMC POFS
+0x50, 0x82,
+0x51, 0x06,
+0x52, 0x83,
+0x53, 0x03,
+0x54, 0x11,
+0x55, 0x8e,
+0x56, 0x00,
+0x57, 0x06,
+0x58, 0x87,
+
+0x80, 0x03,
+0x85, 0x40,
+0x87, 0x02,
+0x88, 0x00,
+0x89, 0x00,
+0x8a, 0x00,
+
+/////// PAGE 16 START ///////
+0x03, 0x16,
+0x10, 0x31,//GMA_CTL
+0x18, 0x5e,//AG_ON
+0x19, 0x5d,//AG_OFF
+0x1A, 0x0e,//TIME_ON
+0x1B, 0x01,//TIME_OFF
+0x1C, 0xdc,//OUT_ON
+0x1D, 0xfe,//OUT_OFF
+
+//GMA
+0x30, 0x00,
+0x31, 0x09,
+0x32, 0x19,
+0x33, 0x34,
+0x34, 0x5e,
+0x35, 0x7f,
+0x36, 0x99,
+0x37, 0xac,
+0x38, 0xba,
+0x39, 0xc5,
+0x3a, 0xcd,
+0x3b, 0xd4,
+0x3c, 0xdb,
+0x3d, 0xe2,
+0x3e, 0xe7,
+0x3f, 0xec,
+0x40, 0xf3,
+0x41, 0xfa,
+0x42, 0xff,
+//RGMA
+0x50, 0x00,
+0x51, 0x05,
+0x52, 0x1b,
+0x53, 0x36,
+0x54, 0x5a,
+0x55, 0x75,
+0x56, 0x89,
+0x57, 0x9c,
+0x58, 0xac,
+0x59, 0xb8,
+0x5a, 0xc7,
+0x5b, 0xd2,
+0x5c, 0xdc,
+0x5d, 0xe5,
+0x5e, 0xed,
+0x5f, 0xf2,
+0x60, 0xf7,
+0x61, 0xf9,
+0x62, 0xfa,
+//BGMA
+0x70, 0x00,
+0x71, 0x0a,
+0x72, 0x19,
+0x73, 0x31,
+0x74, 0x55,
+0x75, 0x6e,
+0x76, 0x83,
+0x77, 0x96,
+0x78, 0xa6,
+0x79, 0xb5,
+0x7a, 0xc2,
+0x7b, 0xcd,
+0x7c, 0xd7,
+0x7d, 0xe0,
+0x7e, 0xe8,
+0x7f, 0xf0,
+0x80, 0xf6,
+0x81, 0xfc,
+0x82, 0xff,
+/////// PAGE 17 START ///////
+0x03, 0x17,
+0x10, 0xf7,
+/////// PAGE 18 START ///////
+
+ 0x03, 0x18,
+ 0x10, 0x00,
+
+/////// PAGE 20 START ///////
+0x03, 0x20,
+0x11, 0x1c,
+0x18, 0x30,
+0x1a, 0x08,
+0x20, 0x01, //05_lowtemp Y Mean off
+0x21, 0x30,
+0x22, 0x10,
+0x23, 0x00,
+0x24, 0x00, //Uniform Scene Off
+0x28, 0xe7,
+0x29, 0x0d, //20100305 ad->0d
+0x2a, 0xff,
+0x2b, 0x34, //f4->Adaptive on //04 off
+0x2c, 0xc2,
+0x2d, 0xcf, //fe->AE Speed option
+0x2e, 0x33,
+0x30, 0x78, //f8
+0x32, 0x03,
+0x33, 0x2e,
+0x34, 0x30,
+0x35, 0xd4,
+0x36, 0xfe,
+0x37, 0x32,
+0x38, 0x04,
+0x39, 0x22, //AE_escapeC10
+0x3a, 0xde, //AE_escapeC11
+0x3b, 0x22, //AE_escapeC1
+0x3c, 0xde, //AE_escapeC2
+0x50, 0x45,
+0x51, 0x88,
+0x56, 0x03,
+0x57, 0xf7,
+0x58, 0x14,
+0x59, 0x88,
+0x5a, 0x04,
+/*
+0x60, 0xff,
+0x61, 0xff,
+0x62, 0xea,
+0x63, 0xab,
+0x64, 0xea,
+0x65, 0xab,
+0x66, 0xea,//eb
+0x67, 0x2b,//eb
+0x68, 0xe8,//eb
+0x69, 0x2b,//eb
+0x6a, 0xea,
+0x6b, 0xab,
+0x6c, 0xea,
+0x6d, 0xab,
+0x6e, 0xff,
+0x6f, 0xff,
+*/
+
+0x60, 0x55,
+0x61, 0x55,
+0x62, 0x6a,
+0x63, 0xa9,
+0x64, 0x6a,
+0x65, 0xa9,
+0x66, 0x6b,//eb
+0x67, 0xe9,//eb
+0x68, 0x6b,//eb
+0x69, 0xe9,//eb
+0x6a, 0x6a,
+0x6b, 0xa9,
+0x6c, 0x6a,
+0x6d, 0xa9,
+0x6e, 0x55,
+0x6f, 0x55,
+
+0x70, 0x76, //6e
+0x71, 0x89, //00 //-4
+0x76, 0x43,
+0x77, 0xe2, //04 //f2
+0x78, 0x23, //Yth1
+0x79, 0x46, //Yth2 //46
+0x7a, 0x23, //23
+0x7b, 0x22, //22
+0x7d, 0x23,
+0x83, 0x01, //EXP Normal 33.33 fps
+0x84, 0x5f,
+0x85, 0x90,
+0x86, 0x01, //EXPMin 7500.00 fps
+0x87, 0xf4,
+0x88, 0x06, //EXP Max 8.33 fps
+0x89, 0x68,
+0x8a, 0xa0,
+0x8B, 0x75, //EXP100
+0x8C, 0x30,
+0x8D, 0x61, //EXP120
+0x8E, 0xa8,
+0x9c, 0x17, //EXP Limit 500.00 fps
+0x9d, 0x70,
+0x9e, 0x01, //EXP Unit
+0x9f, 0xf4,
+0xa3, 0x00, //Outdoor Int
+0xa4, 0x61,
+
+0xb0, 0x18,
+0xb1, 0x14, //ADC 400->560
+0xb2, 0x80,//a0
+0xb3, 0x18,
+0xb4, 0x1a,
+0xb5, 0x44,
+0xb6, 0x2f,
+0xb7, 0x28,
+0xb8, 0x25,
+0xb9, 0x22,
+0xba, 0x21,
+0xbb, 0x20,
+0xbc, 0x1f,
+0xbd, 0x1f,
+0xc0, 0x14,
+0xc1, 0x1f,
+0xc2, 0x1f,
+0xc3, 0x18, //2b
+0xc4, 0x10, //08
+0xc8, 0x80,
+0xc9, 0x40,
+/////// PAGE 22 START ///////
+0x03, 0x22,
+0x10, 0xfd,
+0x11, 0x2e,
+0x19, 0x01,
+0x20, 0x30,
+0x21, 0x80,
+0x24, 0x01,
+0x30, 0x80,
+0x31, 0x80,
+0x38, 0x11,
+0x39, 0x34,
+0x40, 0xf7,
+0x41, 0x55,
+0x42, 0x33, //43
+
+0x43, 0xf6,
+0x44, 0x55, //44
+0x45, 0x44, //33
+
+0x46, 0x00,
+0x50, 0xb2,
+0x51, 0x81,
+0x52, 0x98,
+0x80, 0x38,//40
+0x81, 0x20,
+0x82, 0x30,
+
+0x83, 0x52,
+0x84, 0x16,//1E
+0x85, 0x5A,//52
+0x86, 0x23,
+
+0x87, 0x45,
+0x88, 0x35,
+0x89, 0x47,
+0x8a, 0x2b,
+
+0x8b, 0x40,
+0x8c, 0x33,
+0x8d, 0x38,
+0x8e, 0x30,
+
+0x8f, 0x53,
+0x90, 0x52,
+0x91, 0x50,
+0x92, 0x4c,
+0x93, 0x43,
+0x94, 0x37,
+0x95, 0x2f,//2a
+0x96, 0x26,//24
+0x97, 0x21,//20
+0x98, 0x1e,
+0x99, 0x1f,
+0x9a, 0x20,
+0x9b, 0x88,
+0x9c, 0x88,
+0x9d, 0x48,
+0x9e, 0x38,
+0x9f, 0x30,
+0xa0, 0x60,
+0xa1, 0x34,
+0xa2, 0x7f,
+0xa3, 0xff,
+0xa4, 0x14,
+0xa5, 0x2c,
+0xa6, 0xcf,
+0xad, 0x40,
+0xae, 0x4a,
+0xaf, 0x28,
+0xb0, 0x26,
+0xb1, 0x00,
+0xb8, 0xa0,
+0xb9, 0x00,
+// PAGE 20
+0x03, 0x20, //page 20
+0x10, 0x9c, //ae off
+// PAGE 22
+0x03, 0x22, //page 22
+0x10, 0xe9, //awb off
+// PAGE 0
+0x03, 0x00,
+0x0e, 0x03, //PLL On
+0x0e, 0x73, //PLLx2
+0x03, 0x00, // Dummy 750us
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+ 0x03, 0x00,
+ 0x01, 0xf8,
+
+};
+
+#endif
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/hi253/hi253.c b/drivers/media/video/wmt_v4l2/sensors/hi253/hi253.c
new file mode 100755
index 00000000..b5a72b49
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/hi253/hi253.c
@@ -0,0 +1,333 @@
+
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "hi253.h"
+
+#define sensor_write_array(sd, arr, sz) \
+ cmos_init_8bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write(sd->i2c_addr, reg, val)
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint8_t *regs;
+ size_t size;
+};
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+// CMOS_WIN_SIZE("QVGA", 320, 240, hi253_320_240_regs),
+ CMOS_WIN_SIZE("VGA", 640, 480, hi253_640_480_regs),
+// CMOS_WIN_SIZE("SVGA", 800, 600, hi253_800_600_regs),
+// CMOS_WIN_SIZE("720p", 1280, 720, hi253_1280_720_regs),
+ CMOS_WIN_SIZE("UXGA", 1600, 1200, hi253_1600_1200_rets),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = hi253_wb_auto;
+ size = ARRAY_SIZE(hi253_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = hi253_wb_incandescent;
+ size = ARRAY_SIZE(hi253_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = hi253_wb_daylight;
+ size = ARRAY_SIZE(hi253_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = hi253_wb_cloudy;
+ size = ARRAY_SIZE(hi253_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = hi253_wb_fluorescent;
+ size = ARRAY_SIZE(hi253_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_scenemode(struct cmos_subdev *sd, enum v4l2_scene_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case SCENE_MODE_AUTO:
+ regs = hi253_scene_mode_auto;
+ size = ARRAY_SIZE(hi253_scene_mode_auto);
+ break;
+ case SCENE_MODE_NIGHTSHOT:
+ regs = hi253_scene_mode_night;
+ size = ARRAY_SIZE(hi253_scene_mode_night);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_exposure(struct cmos_subdev *sd, enum v4l2_exposure_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case -2:
+ regs = hi253_exposure_neg6;
+ size = ARRAY_SIZE(hi253_exposure_neg6);
+ break;
+ case -1:
+ regs = hi253_exposure_neg3;
+ size = ARRAY_SIZE(hi253_exposure_neg3);
+ break;
+ case 0:
+ regs = hi253_exposure_zero;
+ size = ARRAY_SIZE(hi253_exposure_zero);
+ break;
+ case 1:
+ regs = hi253_exposure_pos3;
+ size = ARRAY_SIZE(hi253_exposure_pos3);
+ break;
+ case 2:
+ regs = hi253_exposure_pos6;
+ size = ARRAY_SIZE(hi253_exposure_pos6);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data;
+
+ sensor_write(sd, 0x03, 0x00);
+
+ data = sensor_read(sd, 0x11);
+
+ switch (value) {
+ case 0:
+ data &= ~0x01;
+ break;
+ case 1:
+ data |= 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sensor_write(sd, 0x11, data);
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data;
+
+ sensor_write(sd, 0x03, 0x00);
+
+ data = sensor_read(sd, 0x11);
+
+ switch (value) {
+ case 0:
+ data &= ~0x02;
+ break;
+ case 1:
+ data |= 0x02;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sensor_write(sd, 0x11, data);
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ case V4L2_CID_EXPOSURE:
+ return v4l2_ctrl_query_fill(qc, -2, 2, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return sensor_s_scenemode(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return sensor_s_exposure(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ default:
+ case WMT_V4L2_CID_CAMERA_ANTIBANDING:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win) {
+ pr_err("%s, s_mbus_fmt failed, width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+ return -EINVAL;
+ }
+ sensor_write_array(sd, win->regs, win->size);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+
+ return 0;
+}
+
+static int sensor_enum_framesizes(struct cmos_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ const struct cmos_win_size *win = &cmos_supported_win_sizes[index];
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ int data;
+
+ sensor_write(sd, 0x03, 0x0);
+
+ data = sensor_read(sd, 0x04);
+
+ return (data == sd->id) ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ if (sensor_identify(sd)) {
+ return -1;
+ }
+
+ sensor_write_array(sd, hi253_default_regs_init,
+ ARRAY_SIZE(hi253_default_regs_init));
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ return 0;
+}
+
+static struct cmos_subdev_ops hi253_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+ .enum_framesizes = sensor_enum_framesizes,
+};
+
+struct cmos_subdev hi253 = {
+ .name = "hi253",
+ .i2c_addr = 0x20,
+ .id = 0x92,
+ .max_width = 1600,
+ .max_height = 1200,
+ .ops = &hi253_ops,
+};
+
+#if 0
+static int __init hi253_init(void)
+{
+ return cmos_register_sudbdev(&hi253);
+}
+
+static void __exit hi253_exit(void)
+{
+ return cmos_unregister_subdev(&hi253);
+}
+
+module_init(hi253_init);
+module_exit(hi253_exit);
+
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/hi253/hi253.h b/drivers/media/video/wmt_v4l2/sensors/hi253/hi253.h
new file mode 100755
index 00000000..929104a9
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/hi253/hi253.h
@@ -0,0 +1,2124 @@
+#ifndef YACD511_H
+#define YACD511_H
+
+// Scene Mode
+uint8_t hi253_scene_mode_auto[] = {
+ 0x03,0x10,
+ 0x41,0x00,
+ 0x03,0x20,
+ 0xb2,0x80,
+ 0xff,0xff,
+};
+
+uint8_t hi253_scene_mode_night[] = {
+ 0x03,0x10,
+ 0x41,0x15,
+ 0x03,0x20,
+ 0xb2,0xf0,
+ 0xff,0xff,
+};
+
+
+// White Balance
+uint8_t hi253_wb_auto [] = {
+ 0x03,0x22,
+ //0x11,0x2e,
+ 0x83,0x52, // RMAX
+ 0x84, 0x16, // RMIN
+ 0x85, 0x5a, // BMAX
+ 0x86, 0x23, // BMIN
+ 0xff,0xff,
+};
+
+uint8_t hi253_wb_incandescent [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x20,
+ 0x82,0x60,
+ 0x83,0x20,
+ 0x84,0x18,
+ 0x85,0x60,
+ 0x86,0x64,
+ 0xff,0xff,
+};
+
+uint8_t hi253_wb_fluorescent [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x41,
+ 0x82,0x58,
+ 0x83,0x44,
+ 0x84,0x22,
+ 0x85,0x55,
+ 0x86,0x53,
+ 0xff,0xff,
+};
+
+uint8_t hi253_wb_daylight [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x59,
+ 0x82,0x29,
+ 0x83,0x60,
+ 0x84,0x50,
+ 0x85,0x2f,
+ 0x86,0x23,
+ 0xff,0xff,
+};
+
+uint8_t hi253_wb_cloudy [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x71,
+ 0x82,0x2b,
+ 0x83,0x72,
+ 0x84,0x70,
+ 0x85,0x2b,
+ 0x86,0x28,
+ 0xff,0xff,
+};
+
+
+// Exposure
+uint8_t hi253_exposure_neg6[] = {
+ 0x03,0x10,
+ 0x40,0xa8,
+};
+
+uint8_t hi253_exposure_neg3[] = {
+ 0x03,0x10,
+ 0x40,0x98,
+};
+
+uint8_t hi253_exposure_zero[] = {
+ 0x03,0x10,
+ 0x40,0x90,
+};
+
+uint8_t hi253_exposure_pos3[] = {
+ 0x03,0x10,
+ 0x40,0x08,
+};
+
+uint8_t hi253_exposure_pos6[] = {
+ 0x03,0x10,
+ 0x40,0x28,
+};
+
+
+// Resolution
+uint8_t hi253_1600_1200_rets[]={
+ 0x03, 0x00,
+ 0x10, 0x00,
+ 0x12, 0x04,
+ 0x20, 0x00,
+ 0x21, 0x0a,
+ 0x22, 0x00,
+ 0x23, 0x0a,
+
+ 0x03, 0x18,
+ 0x10, 0x00,
+ 0xff, 0xff,
+};
+
+uint8_t hi253_1280_720_regs[]={
+};
+
+uint8_t hi253_800_600_regs[]={
+
+ 0x03, 0x00,
+ 0x10, 0x10,
+ 0x12, 0x04,
+
+ 0x03, 0x18,
+ 0x10, 0x00,
+ 0xff, 0xff,
+};
+
+uint8_t hi253_640_480_regs[]={
+ 0x03, 0x00,
+ 0x10, 0x10,
+ 0x12, 0x04,
+
+ 0x03, 0x18,
+ 0x12, 0x20,
+ 0x10, 0x07,
+ 0x11, 0x00,
+ 0x20, 0x05,
+ 0x21, 0x00,
+ 0x22, 0x03,
+ 0x23, 0xc0,
+ 0x24, 0x00,
+ 0x25, 0x04,
+ 0x26, 0x00,
+ 0x27, 0x00,
+ 0x28, 0x05,
+ 0x29, 0x04,
+ 0x2a, 0x03,
+ 0x2b, 0xc0,
+ 0x2c, 0x0a,
+ 0x2d, 0x00,
+ 0x2e, 0x0a,
+ 0x2f, 0x00,
+ 0x30, 0x45,
+ 0xff, 0xff,
+};
+
+uint8_t hi253_320_240_regs[]= {
+ 0x01, 0xf9,
+ 0x08, 0x0f,
+ 0x01, 0xf8,
+
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+
+ 0x0e, 0x00,
+
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+
+ 0x0e, 0x00,
+ 0x01, 0xf1,
+ 0x08, 0x00,
+ 0x01, 0xf3,
+ 0x01, 0xf1,
+
+ 0x03, 0x20,
+ 0x10, 0x0c,
+ 0x03, 0x22,
+ 0x10, 0x69,
+
+ //Page 00
+ 0x03, 0x00,
+ 0x10, 0x11, //lxh
+ 0x11, 0x90,
+ 0x12, 0x04,
+ 0x0b, 0xaa,
+ 0x0c, 0xaa,
+ 0x0d, 0xaa,
+ 0x20, 0x00,
+ 0x21, 0x0a, //lxh
+ 0x22, 0x00,
+ 0x23, 0x0a, //lxh
+ 0x24, 0x04,
+ 0x25, 0xb0,
+ 0x26, 0x06,
+ 0x27, 0x40,
+
+ 0x40, 0x00,
+ 0x41, 0xe8,//lxh
+ 0x42, 0x00,
+ 0x43, 0x14,//lxh
+
+ 0x45, 0x04,
+ 0x46, 0x18,
+ 0x47, 0xd8,
+
+ 0xe1, 0x0f,
+
+ //BLC
+ 0x80, 0x2e,
+ 0x81, 0x7e,
+ 0x82, 0x90,
+ 0x83, 0x00,
+ 0x84, 0x0c,
+ 0x85, 0x00,
+ 0x90, 0x0c,
+ 0x91, 0x0c,
+ 0x92, 0x78,
+ 0x93, 0x70,
+ 0x94, 0x75,
+ 0x95, 0x70,
+ 0x96, 0xdc,
+ 0x97, 0xfe,
+ 0x98, 0x20,
+
+ //OutDoor BLC
+ 0x99,0x42,
+ 0x9a,0x42,
+ 0x9b,0x42,
+ 0x9c,0x42,
+
+ //Dark BLC
+ 0xa0, 0x02,
+ 0xa2, 0x02,
+ 0xa4, 0x02,
+ 0xa6, 0x02,
+
+ //Normal BLC
+ 0xa8, 0x42,
+ 0xaa, 0x42,
+ 0xac, 0x42,
+ 0xae, 0x42,
+
+ //Page 02
+ 0x03, 0x02,
+ 0x12, 0x03,
+ 0x13, 0x03,
+ 0x16, 0x00,
+ 0x17, 0x8C,
+ 0x18, 0x4c,
+ 0x19, 0x00,
+ 0x1a, 0x39,
+ 0x1c, 0x09,
+ 0x1d, 0x40,
+ 0x1e, 0x30,
+ 0x1f, 0x10,
+ 0x20, 0x77,
+ 0x21, 0xde,
+ 0x22, 0xa7,
+ 0x23, 0x30,
+ 0x27, 0x3c,
+ 0x2b, 0x80,
+ 0x2e, 0x00,
+ 0x2f, 0x00,
+ 0x30, 0x05,
+ 0x50, 0x20,
+ 0x52, 0x01,
+ 0x53, 0xc1,
+ 0x55, 0x1c,
+ 0x56, 0x11,
+ 0x5d, 0xA2,
+ 0x5e, 0x5a,
+ 0x60, 0x87,
+ 0x61, 0x99,
+ 0x62, 0x88,
+ 0x63, 0x97,
+ 0x64, 0x88,
+ 0x65, 0x97,
+ 0x67, 0x0c,
+ 0x68, 0x0c,
+ 0x69, 0x0c,
+ 0x72, 0x89,
+ 0x73, 0x96,
+ 0x74, 0x89,
+ 0x75, 0x96,
+ 0x76, 0x89,
+ 0x77, 0x96,
+ 0x7C, 0x85,
+ 0x7d, 0xaf,
+ 0x80, 0x01,
+ 0x81, 0x7f,
+ 0x82, 0x13,
+ 0x83, 0x24,
+ 0x84, 0x7d,
+ 0x85, 0x81,
+ 0x86, 0x7d,
+ 0x87, 0x81,
+ 0x92, 0x48,
+ 0x93, 0x54,
+ 0x94, 0x7d,
+ 0x95, 0x81,
+ 0x96, 0x7d,
+ 0x97, 0x81,
+ 0xa0, 0x02,
+ 0xa1, 0x7b,
+ 0xa2, 0x02,
+ 0xa3, 0x7b,
+ 0xa4, 0x7b,
+ 0xa5, 0x02,
+ 0xa6, 0x7b,
+ 0xa7, 0x02,
+ 0xa8, 0x85,
+ 0xa9, 0x8c,
+ 0xaa, 0x85,
+ 0xab, 0x8c,
+ 0xac, 0x10,
+ 0xad, 0x16,
+ 0xae, 0x10,
+ 0xaf, 0x16,
+ 0xb0, 0x99,
+ 0xb1, 0xa3,
+ 0xb2, 0xa4,
+ 0xb3, 0xae,
+ 0xb4, 0x9b,
+ 0xb5, 0xa2,
+ 0xb6, 0xa6,
+ 0xb7, 0xac,
+ 0xb8, 0x9b,
+ 0xb9, 0x9f,
+ 0xba, 0xa6,
+ 0xbb, 0xaa,
+ 0xbc, 0x9b,
+ 0xbd, 0x9f,
+ 0xbe, 0xa6,
+ 0xbf, 0xaa,
+ 0xc4, 0x2c,
+ 0xc5, 0x43,
+ 0xc6, 0x63,
+ 0xc7, 0x79,
+ 0xc8, 0x2d,
+ 0xc9, 0x42,
+ 0xca, 0x2d,
+ 0xcb, 0x42,
+ 0xcc, 0x64,
+ 0xcd, 0x78,
+ 0xce, 0x64,
+ 0xcf, 0x78,
+ 0xd0, 0x0a,
+ 0xd1, 0x09,
+ 0xd4, 0x0c,
+ 0xd5, 0x0c,
+ 0xd6, 0x78,
+ 0xd7, 0x70,//lxh
+ 0xe0, 0xc4,
+ 0xe1, 0xc4,
+ 0xe2, 0xc4,
+ 0xe3, 0xc4,
+ 0xe4, 0x00,
+ 0xe8, 0x80,
+ 0xe9, 0x40,
+ 0xea, 0x7f,
+ 0xf0, 0x01,
+ 0xf1, 0x01,
+ 0xf2, 0x01,
+ 0xf3, 0x01,
+ 0xf4, 0x01,
+
+ //PAGE10
+ 0x03, 0x10,
+ 0x10, 0x03, //lxh
+ 0x11, 0x03, //lxh,normal
+ 0x12, 0x30,
+ 0x13, 0x02,
+ 0x20, 0x00,
+
+ 0x40, 0x88,
+ 0x41, 0x05,
+ 0x48, 0x88,// 84
+ 0x50, 0x90,
+ 0x30, 0x00,
+ 0x31, 0x00,
+ 0x32, 0x00,
+ 0x33, 0x00,
+
+ 0x34, 0x30,
+ 0x35, 0x00,
+ 0x36, 0x00,
+ 0x38, 0x00,
+ 0x3e, 0x58,
+ 0x3f, 0x00,
+
+ //Saturation
+ 0x60, 0x67,
+ 0x61, 0x78,// 74
+ 0x62, 0x78,// 76
+ 0x63, 0x50,
+ 0x64, 0x41,
+
+ 0x66, 0x33,
+ 0x67, 0x00,
+
+ 0x6a, 0x90,
+ 0x6b, 0x80,
+ 0x6c, 0x80,
+ 0x6d, 0xa0,
+
+ 0x76, 0x01,
+ 0x74, 0x66,
+ 0x79, 0x06,
+
+ //Page 11
+ 0x03, 0x11,
+ 0x10, 0x7f,//lxh,3f
+ 0x11, 0x40,
+ 0x12, 0xba,
+ 0x13, 0xcb,
+ 0x26, 0x20,
+ 0x27, 0x22,
+ 0x28, 0x0f,
+ 0x29, 0x10,
+ 0x2b, 0x30,
+ 0x2c, 0x32,
+
+ //Out2 D-LPF th
+ 0x30, 0x70,
+ 0x31, 0x10,
+ 0x32, 0x65,
+ 0x33, 0x09,
+ 0x34, 0x06,
+ 0x35, 0x04,
+
+ //Out1 D-LPF th
+ 0x36, 0x70,
+ 0x37, 0x18,
+ 0x38, 0x65,
+ 0x39, 0x09,
+ 0x3a, 0x06,
+ 0x3b, 0x04,
+
+ //Indoor D-LPF th
+ 0x3c, 0x80,
+ 0x3d, 0x18,
+ 0x3e, 0x80,
+ 0x3f, 0x0c,
+ 0x40, 0x09,
+ 0x41, 0x06,
+
+ 0x42, 0x80,
+ 0x43, 0x18,
+ 0x44, 0x80,
+ 0x45, 0x12,
+ 0x46, 0x10,
+ 0x47, 0x10,
+ 0x48, 0x90,
+ 0x49, 0x40,
+ 0x4a, 0x80,
+ 0x4b, 0x13,
+ 0x4c, 0x10,
+ 0x4d, 0x11,
+ 0x4e, 0x80,
+ 0x4f, 0x30,
+ 0x50, 0x80,
+ 0x51, 0x13,
+ 0x52, 0x10,
+ 0x53, 0x13,
+ 0x54, 0x11,
+ 0x55, 0x17,
+ 0x56, 0x20,
+ 0x57, 0x20,
+ 0x58, 0x20,
+ 0x59, 0x30,
+ 0x5a, 0x18,
+ 0x5b, 0x00,
+ 0x5c, 0x00,
+ 0x60, 0x3f,
+ 0x62, 0x50,
+ 0x70, 0x06,
+
+ //Page 12
+ 0x03, 0x12,
+ 0x20, 0x0f,
+ 0x21, 0x0f,
+ 0x25, 0x30,
+ 0x28, 0x00,
+ 0x29, 0x00,
+ 0x2a, 0x00,
+ 0x30, 0x50,
+ 0x31, 0x18,
+ 0x32, 0x32,
+ 0x33, 0x40,
+ 0x34, 0x50,
+ 0x35, 0x70,
+ 0x36, 0xa0,
+
+ //Out2 th
+ 0x40, 0xa0,
+ 0x41, 0x40,
+ 0x42, 0xa0,
+ 0x43, 0x90,
+ 0x44, 0x90,
+ 0x45, 0x80,
+
+ //Out1 th
+ 0x46, 0xb0,
+ 0x47, 0x55,
+ 0x48, 0xa0,
+ 0x49, 0x90,
+ 0x4a, 0x90,
+ 0x4b, 0x80,
+
+ //In door th
+ 0x4c, 0xb0,
+ 0x4d, 0x40,
+ 0x4e, 0x90,
+ 0x4f, 0x90,
+ 0x50, 0xe6,
+ 0x51, 0x80,
+
+ //Dark1 th
+ 0x52, 0xb0,
+ 0x53, 0x60,
+ 0x54, 0xc0,
+ 0x55, 0xc0,
+ 0x56, 0xc0,
+ 0x57, 0x80,
+
+ //Dark2 th
+ 0x58, 0x90,
+ 0x59, 0x40,
+ 0x5a, 0xd0,
+ 0x5b, 0xd0,
+ 0x5c, 0xe0,
+ 0x5d, 0x80,
+
+ //Dark3 th
+ 0x5e, 0x88,
+ 0x5f, 0x40,
+ 0x60, 0xe0,
+ 0x61, 0xe6,
+ 0x62, 0xe6,
+ 0x63, 0x80,
+
+ 0x70, 0x15,
+ 0x71, 0x01,
+
+ 0x72, 0x18,
+ 0x73, 0x01,
+
+ 0x74, 0x25,
+ 0x75, 0x15,
+ 0x80, 0x30,
+ 0x81, 0x50,
+ 0x82, 0x80,
+ 0x85, 0x1a,
+ 0x88, 0x00,
+ 0x89, 0x00,
+ 0x90, 0x5d,
+
+ 0xc5, 0x30,
+ 0xc6, 0x2a,
+
+ 0xD0, 0x0c,
+ 0xD1, 0x80,
+ 0xD2, 0x67,
+ 0xD3, 0x00,
+ 0xD4, 0x00,
+ 0xD5, 0x02,
+ 0xD6, 0xff,
+ 0xD7, 0x18,
+ 0x3b, 0x06,
+ 0x3c, 0x06,
+
+ 0xc5, 0x30,
+ 0xc6, 0x2a,
+
+ //Page 13
+ 0x03, 0x13,
+ 0x10, 0xcb,
+ 0x11, 0x7b,
+ 0x12, 0x07,
+ 0x14, 0x00,
+
+ 0x20, 0x15,
+ 0x21, 0x13,
+ 0x22, 0x33,
+ 0x23, 0x04,
+ 0x24, 0x09,
+ 0x25, 0x08,
+ 0x26, 0x18,
+ 0x27, 0x30,
+ 0x29, 0x12,
+ 0x2a, 0x50,
+
+ //Low clip th
+ 0x2b, 0x06,
+ 0x2c, 0x06,
+ 0x25, 0x08,
+ 0x2d, 0x0c,
+ 0x2e, 0x12,
+ 0x2f, 0x12,
+
+ //Out2 Edge
+ 0x50, 0x10,
+ 0x51, 0x14,
+ 0x52, 0x10,
+ 0x53, 0x0c,
+ 0x54, 0x0f,
+ 0x55, 0x0c,
+
+ //Out1 Edge
+ 0x56, 0x10,
+ 0x57, 0x13,
+ 0x58, 0x10,
+ 0x59, 0x0c,
+ 0x5a, 0x0f,
+ 0x5b, 0x0c,
+
+ //Indoor Edge
+ 0x5c, 0x0a,
+ 0x5d, 0x0b,
+ 0x5e, 0x0a,
+ 0x5f, 0x08,
+ 0x60, 0x09,
+ 0x61, 0x08,
+
+ //Dark1 Edge
+ 0x62, 0x08,
+ 0x63, 0x08,
+ 0x64, 0x08,
+ 0x65, 0x06,
+ 0x66, 0x06,
+ 0x67, 0x06,
+
+ //Dark2 Edge
+ 0x68, 0x07,
+ 0x69, 0x07,
+ 0x6a, 0x07,
+ 0x6b, 0x05,
+ 0x6c, 0x05,
+ 0x6d, 0x05,
+
+ //Dark3 Edge
+ 0x6e, 0x07,
+ 0x6f, 0x07,
+ 0x70, 0x07,
+ 0x71, 0x05,
+ 0x72, 0x05,
+ 0x73, 0x05,
+
+ //2DY
+ 0x80, 0xfd,
+ 0x81, 0x1f,
+ 0x82, 0x05,
+ 0x83, 0x01,
+
+ 0x90, 0x15,
+ 0x91, 0x15,
+ 0x92, 0x33,
+ 0x93, 0x30,
+ 0x94, 0x03,
+ 0x95, 0x14,
+ 0x97, 0x30,
+ 0x99, 0x30,
+
+ 0xa0, 0x04,
+ 0xa1, 0x05,
+ 0xa2, 0x04,
+ 0xa3, 0x05,
+ 0xa4, 0x07,
+ 0xa5, 0x08,
+ 0xa6, 0x07,
+ 0xa7, 0x08,
+ 0xa8, 0x07,
+ 0xa9, 0x08,
+ 0xaa, 0x07,
+ 0xab, 0x08,
+
+ //Out2
+ 0xb0, 0x22,
+ 0xb1, 0x2a,
+ 0xb2, 0x28,
+ 0xb3, 0x22,
+ 0xb4, 0x2a,
+ 0xb5, 0x28,
+
+ //Out1
+ 0xb6, 0x22,
+ 0xb7, 0x2a,
+ 0xb8, 0x28,
+ 0xb9, 0x22,
+ 0xba, 0x2a,
+ 0xbb, 0x28,
+
+ 0xbc, 0x17,
+ 0xbd, 0x17,
+ 0xbe, 0x17,
+ 0xbf, 0x17,
+ 0xc0, 0x17,
+ 0xc1, 0x17,
+
+ //Dark1
+ 0xc2, 0x1e,
+ 0xc3, 0x12,
+ 0xc4, 0x10,
+ 0xc5, 0x1e,
+ 0xc6, 0x12,
+ 0xc7, 0x10,
+
+ //Dark2
+ 0xc8, 0x18,
+ 0xc9, 0x05,
+ 0xca, 0x05,
+ 0xcb, 0x18,
+ 0xcc, 0x05,
+ 0xcd, 0x05,
+
+ //Dark3
+ 0xce, 0x18,
+ 0xcf, 0x05,
+ 0xd0, 0x05,
+ 0xd1, 0x18,
+ 0xd2, 0x05,
+ 0xd3, 0x05,
+
+ //Page 14
+ 0x03, 0x14,
+ 0x10, 0x11,
+ 0x20, 0x40,
+ 0x21, 0x80,
+ 0x23, 0x80,
+ 0x22, 0x80,
+ 0x23, 0x80,
+ 0x24, 0x80,
+
+ 0x30, 0xc8,
+ 0x31, 0x2b,
+ 0x32, 0x00,
+ 0x33, 0x00,
+ 0x34, 0x90,
+
+ 0x40, 0x40,
+ 0x50, 0x2d,
+ 0x60, 0x28,
+ 0x70, 0x2d,
+
+ //Page 15
+ 0x03, 0x15,
+ 0x10, 0x0f,
+ 0x14, 0x52,
+ 0x15, 0x42,
+ 0x16, 0x32,
+ 0x17, 0x2f,
+
+ //CMC
+ 0x30, 0x8f,
+ 0x31, 0x59,
+ 0x32, 0x0a,
+ 0x33, 0x15,
+ 0x34, 0x5b,
+ 0x35, 0x06,
+ 0x36, 0x07,
+ 0x37, 0x40,
+ 0x38, 0x86,
+
+ //CMC OFS
+ 0x40, 0x95,
+ 0x41, 0x1f,
+ 0x42, 0x8a,
+ 0x43, 0x86,
+ 0x44, 0x0a,
+ 0x45, 0x84,
+ 0x46, 0x87,
+ 0x47, 0x9b,
+ 0x48, 0x23,
+
+ //CMC POFS
+ 0x50, 0x8c,
+ 0x51, 0x0c,
+ 0x52, 0x00,
+ 0x53, 0x07,
+ 0x54, 0x17,
+ 0x55, 0x9d,
+ 0x56, 0x00,
+ 0x57, 0x0b,
+ 0x58, 0x89,
+
+ 0x80, 0x03,
+ 0x85, 0x40,
+ 0x87, 0x02,
+ 0x88, 0x00,
+ 0x89, 0x00,
+ 0x8a, 0x00,
+
+ 0x03, 0x16,
+ 0x10, 0x31,//GMA_CTL
+ 0x18, 0x5e,//AG_ON
+ 0x19, 0x5d,//AG_OFF
+ 0x1A, 0x0e,//TIME_ON
+ 0x1B, 0x01,//TIME_OFF
+ 0x1C, 0xdc,//OUT_ON
+ 0x1D, 0xfe,//OUT_OFF
+ //GMA
+ 0x30, 0x00,
+ 0x31, 0x09,
+ 0x32, 0x15,
+ 0x33, 0x29,
+ 0x34, 0x53,
+ 0x35, 0x6c,
+ 0x36, 0x81,
+ 0x37, 0x94,
+ 0x38, 0xa4,
+ 0x39, 0xb3,
+ 0x3a, 0xc0,
+ 0x3b, 0xcb,
+ 0x3c, 0xd5,
+ 0x3d, 0xde,
+ 0x3e, 0xe6,
+ 0x3f, 0xee,
+ 0x40, 0xf5,
+ 0x41, 0xfc,
+ 0x42, 0xff,
+ //RGMA
+ 0x50, 0x00,
+ 0x51, 0x09,
+ 0x52, 0x1f,
+ 0x53, 0x37,
+ 0x54, 0x5b,
+ 0x55, 0x76,
+ 0x56, 0x8d,
+ 0x57, 0xa1,
+ 0x58, 0xb2,
+ 0x59, 0xbe,
+ 0x5a, 0xc9,
+ 0x5b, 0xd2,
+ 0x5c, 0xdb,
+ 0x5d, 0xe3,
+ 0x5e, 0xeb,
+ 0x5f, 0xf0,
+ 0x60, 0xf5,
+ 0x61, 0xf7,
+ 0x62, 0xf8,
+ //BGMA
+ 0x70, 0x00,
+ 0x71, 0x08,
+ 0x72, 0x17,
+ 0x73, 0x2f,
+ 0x74, 0x53,
+ 0x75, 0x6c,
+ 0x76, 0x81,
+ 0x77, 0x94,
+ 0x78, 0xa4,
+ 0x79, 0xb3,
+ 0x7a, 0xc0,
+ 0x7b, 0xcb,
+ 0x7c, 0xd5,
+ 0x7d, 0xde,
+ 0x7e, 0xe6,
+ 0x7f, 0xee,
+ 0x80, 0xf4,
+ 0x81, 0xfa,
+ 0x82, 0xff,
+
+ 0x03, 0x17,
+ 0xc4, 0x6e,
+ 0xc5, 0x5c,
+
+
+ 0x03, 0x18,
+ 0x12, 0x20,
+ 0x10, 0x07,
+ 0x11, 0x00,
+ 0x20, 0x02,
+ 0x21, 0x80,
+ 0x22, 0x00,
+ 0x23, 0xf0,
+ 0x24, 0x00,
+ 0x25, 0x06,
+ 0x26, 0x00,
+ 0x27, 0x00,
+ 0x28, 0x02,
+ 0x29, 0x86,
+ 0x2a, 0x00,
+ 0x2b, 0xf0,
+ 0x2c, 0x14,
+ 0x2d, 0x00,
+ 0x2e, 0x14,
+ 0x2f, 0x00,
+ 0x30, 0x65,
+ 0x03, 0x20,
+ 0x10, 0x1c,
+ 0x18, 0x38,
+ 0x20, 0x01,
+ 0x21, 0x30,
+ 0x22, 0x10,
+ 0x23, 0x00,
+ 0x24, 0x04,
+
+ 0x28, 0xff,
+ 0x29, 0xad,
+
+ 0x2a, 0xf0,
+ 0x2b, 0x34,
+ 0x30, 0x78,
+ 0x2c, 0xc3,
+ 0x2d, 0x5f,
+ 0x2e, 0x33,
+ // 0x30, 0xf8,
+ 0x32, 0x03,
+ 0x33, 0x2e,
+ 0x34, 0x30,
+ 0x35, 0xd4,
+ 0x36, 0xfe,
+ 0x37, 0x32,
+ 0x38, 0x04,
+ 0x47, 0xf0,
+
+ //Y_Frame TH
+ 0x50, 0x45,
+ 0x51, 0x88,
+
+ 0x56, 0x10,
+ 0x57, 0xb7,
+ 0x58, 0x14,
+ 0x59, 0x88,
+ 0x5a, 0x04,
+
+ 0x60, 0x55,
+ 0x61, 0x55,
+ 0x62, 0x6a,
+ 0x63, 0xa9,
+ 0x64, 0x6a,
+ 0x65, 0xa9,
+ 0x66, 0x6a,
+ 0x67, 0xa9,
+ 0x68, 0x6b,
+ 0x69, 0xe9,
+ 0x6a, 0x6a,
+ 0x6b, 0xa9,
+ 0x6c, 0x6a,
+ 0x6d, 0xa9,
+ 0x6e, 0x55,
+ 0x6f, 0x55,
+ // 0x70, 0x42,
+ 0x70, 0x42,
+ 0x71, 0xBb,
+
+ // haunting control
+ 0x76, 0x21,
+ 0x77, 0x02,
+ 0x78, 0x22,
+ 0x79, 0x2a,
+
+ 0x78, 0x24,
+ 0x79, 0x23,
+ 0x7a, 0x23,
+ 0x7b, 0x22,
+ 0x7d, 0x23,
+ 0x83, 0x01, //EXP Normal 33.33 fps
+ 0x84, 0x5f,
+ 0x85, 0x90,
+ 0x86, 0x01, //EXPMin 7500.00 fps
+ 0x87, 0xf4,
+ 0x88, 0x05, //EXP Max 8.33 fps
+ 0x89, 0x7e,
+ 0x8a, 0x40,
+ 0x8B, 0x75, //EXP100
+ 0x8C, 0x30,
+ 0x8D, 0x61, //EXP120
+ 0x8E, 0xa8,
+ 0x9c, 0x17, //EXP Limit 500.00 fps
+ 0x9d, 0x70,
+ 0x9e, 0x01, //EXP Unit
+ 0x9f, 0xf4,
+ 0xa0, 0x03,
+ 0xa1, 0xa9,
+ 0xa2, 0x80,
+ 0xb0, 0x1d,
+ 0xb1, 0x1a,
+ 0xb2, 0x80,
+ 0xb3, 0x1a,
+ 0xb4, 0x1a,
+ 0xb5, 0x44,
+ 0xb6, 0x2f,
+ 0xb7, 0x28,
+ 0xb8, 0x25,
+ 0xb9, 0x22,
+ 0xba, 0x21,
+ 0xbb, 0x20,
+ 0xbc, 0x1f,
+ 0xbd, 0x1f,
+ 0xc0, 0x30,
+ 0xc1, 0x20,
+ 0xc2, 0x20,
+ 0xc3, 0x20,
+ 0xc4, 0x08,
+ 0xc8, 0x60,
+ 0xc9, 0x40,
+
+ //Page 22
+ 0x03, 0x22, // Page 22
+ 0x10, 0xfd, // AWBCTL1
+ 0x11, 0x2e, // AWBCTL2
+ 0x19, 0x01,
+ 0x20, 0x30,
+ 0x21, 0x80,
+ 0x23, 0x08,
+ 0x24, 0x01,
+
+ 0x30, 0x80, // ULVL
+ 0x31, 0x80, // VLVL
+ 0x38, 0x11, // UVTH1
+ 0x39, 0x34, // UVTH2
+ 0x40, 0xf7, // YRANGE
+
+ 0x41, 0x77, // CDIFF
+ 0x42, 0x55, // CSUM2
+ 0x43, 0xf0,
+ 0x44, 0x66,
+ 0x45, 0x33,
+ 0x46, 0x01, // WHTPXLTH1
+ 0x47, 0x94,
+
+ 0x50, 0xb2,
+ 0x51, 0x81,
+ 0x52, 0x98,
+
+ 0x80, 0x38, // RGAIN
+ 0x81, 0x20, // GGAIN
+ 0x82, 0x38, // BGAIN
+
+ 0x83, 0x5e, // RMAX
+ 0x84, 0x20, // RMIN
+ 0x85, 0x53, // BMAX
+ 0x86, 0x15, // BMIN
+
+ 0x87, 0x54, // RMAXM
+ 0x88, 0x20, // RMINM
+ 0x89, 0x3f, // BMAXM
+ 0x8a, 0x1c, // BMINM
+
+ 0x8b, 0x54, // RMAXB
+ 0x8c, 0x3f, // RMINB
+ 0x8d, 0x24, // BMAXB
+ 0x8e, 0x1c, // BMINB
+
+ 0x8f, 0x60, // BGAINPARA1
+ 0x90, 0x5f, // BGAINPARA2
+ 0x91, 0x5c, // BGAINPARA3
+ 0x92, 0x4C, // BGAINPARA4
+ 0x93, 0x41, // BGAINPARA5
+ 0x94, 0x3b, // BGAINPARA6
+ 0x95, 0x36, // BGAINPARA7
+ 0x96, 0x30, // BGAINPARA8
+ 0x97, 0x27, // BGAINPARA9
+ 0x98, 0x20, // BGAINPARA10
+ 0x99, 0x1C, // BGAINPARA11
+ 0x9a, 0x19, // BGAINPARA12
+
+ 0x9b, 0x88, // BGAINBND1
+ 0x9c, 0x88, // BGAINBND2
+ 0x9d, 0x48, // RGAINTH1
+ 0x9e, 0x38, // RGAINTH2
+ 0x9f, 0x30, // RGAINTH3
+
+ 0xa0, 0x74, // RDELTA1
+ 0xa1, 0x35, // BDELTA1
+ 0xa2, 0xaf, // RDELTA2
+ 0xa3, 0xf7, // BDELTA2
+
+ 0xa4, 0x10, // AWBEXPLMT1
+ 0xa5, 0x50, // AWBEXPLMT2
+ 0xa6, 0xc4, // AWBEXPLMT3
+
+ 0xad, 0x40,
+ 0xae, 0x4a,
+
+ 0xaf, 0x2a,
+ 0xb0, 0x29,
+
+ 0xb1, 0x20,
+ 0xb4, 0xff,
+ 0xb8, 0x6b,
+ 0xb9, 0x00,
+
+
+ 0x03, 0x24,
+ 0x10, 0x01,
+ 0x18, 0x06,
+ 0x30, 0x06,
+ 0x31, 0x90,
+ 0x32, 0x25,
+ 0x33, 0xa2,
+ 0x34, 0x26,
+ 0x35, 0x58,
+ 0x36, 0x60,
+ 0x37, 0x00,
+ 0x38, 0x50,
+ 0x39, 0x00,
+
+ 0x03, 0x20,
+ 0x10, 0x9c,
+ 0x03, 0x22,
+ 0x10, 0xe9,
+
+ //Page 00
+ 0x03, 0x00,
+ 0x0e, 0x03,
+ 0x0e, 0x73,
+
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+ 0x03, 0x00,
+
+ 0x03, 0x00,
+ 0x01, 0xf8,
+
+};
+
+uint8_t hi253_default_regs_init[]={
+
+0x01, 0xf9, //sleep on
+0x08, 0x0f, //Hi-Z on
+0x01, 0xf8, //sleep off
+0x03, 0x00, // Dummy 750us START
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00, // Dummy 750us END
+0x0e, 0x03, //PLL On
+0x0e, 0x73, //PLLx2
+0x03, 0x00, // Dummy 750us START
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00, // Dummy 750us END
+0x0e, 0x00, //PLL off
+0x01, 0xf1, //sleep on
+0x08, 0x00, //Hi-Z off
+0x01, 0xf3,
+0x01, 0xf1,
+// PAGE 20
+0x03, 0x20, //page 20
+0x10, 0x1c, //ae off
+// PAGE 22
+0x03, 0x22, //page 22
+0x10, 0x69, //awb off
+/////// PAGE 0 START ///////
+0x03, 0x00,
+0x10, 0x10, // Sub1/2_Preview2 Mode_H binning
+0x11, 0x90,
+0x12, 0x04,//00
+0x0b, 0xaa, // ESD Check Register
+0x0c, 0xaa, // ESD Check Register
+0x0d, 0xaa, // ESD Check Register
+0x20, 0x00, // Windowing start point Y
+0x21, 0x04,
+0x22, 0x00, // Windowing start point X
+0x23, 0x07,
+0x24, 0x04,
+0x25, 0xb0,
+0x26, 0x06,
+0x27, 0x40, // WINROW END
+0x40, 0x01, //Hblank 408
+0x41, 0x68,
+0x42, 0x00, //Vblank 20
+0x43, 0x14,
+0x45, 0x04,
+0x46, 0x18,
+0x47, 0xd8,
+//BLC
+0x80, 0x2e,
+0x81, 0x7e,
+0x82, 0x90,
+0x83, 0x00,
+0x84, 0x0c,
+0x85, 0x00,
+0x90, 0x0c, //BLC_TIME_TH_ON
+0x91, 0x0c, //BLC_TIME_TH_OFF
+0x92, 0x78, //BLC_AG_TH_ON
+0x93, 0x70, //BLC_AG_TH_OFF
+0x94, 0x75,
+0x95, 0x70,
+0x96, 0xdc,
+0x97, 0xfe,
+0x98, 0x38,
+//OutDoor BLC
+0x99, 0x42,
+0x9a, 0x42,
+0x9b, 0x42,
+0x9c, 0x42,
+//Dark BLC
+0xa0, 0x00,
+0xa2, 0x00,
+0xa4, 0x00,
+0xa6, 0x00,
+//Normal BLC
+0xa8, 0x42,
+0xaa, 0x42,
+0xac, 0x42,
+0xae, 0x42,
+/////// PAGE 2 START ///////
+0x03, 0x02, //Page 02
+0x10, 0x00, //Mode_test
+0x11, 0x00, //Mode_dead_test
+0x12, 0x03, //pwr_ctl_ctl1
+0x13, 0x03, //Mode_ana_test
+0x14, 0x00, //mode_memory
+0x16, 0x00, //dcdc_ctl1
+0x17, 0x8c, //dcdc_ctl2
+0x18, 0x4C, //analog_func1
+0x19, 0x00, //analog_func2
+0x1a, 0x39, //analog_func3
+0x1b, 0x00, //analog_func4
+0x1c, 0x09, //dcdc_ctl3
+0x1d, 0x40, //dcdc_ctl4
+0x1e, 0x30, //analog_func7
+0x1f, 0x10, //analog_func8
+0x20, 0x77, //pixel bias
+0x21, 0xde, //adc,asp bias
+0x22, 0xa7, //main,bus bias
+0x23, 0x30, //clamp
+0x24, 0x4a,
+0x25, 0x10,
+0x27, 0x3c,
+0x28, 0x00,
+0x29, 0x0c,
+0x2a, 0x80,
+0x2b, 0x80,
+0x2c, 0x02,
+0x2d, 0xa0,
+0x2e, 0x00, //11
+0x2f, 0x00, //a1
+0x30, 0x05, //swap_ctl
+0x31, 0x99,
+0x32, 0x00,
+0x33, 0x00,
+0x34, 0x22,
+0x38, 0x88,
+0x39, 0x88,
+0x50, 0x20,
+0x51, 0x03, //00 don't touch
+0x52, 0x01,
+0x53, 0xc1,
+0x54, 0x10,
+0x55, 0x1c,
+0x56, 0x11,
+0x58, 0x10,
+0x59, 0x0e,
+0x5d, 0xa2,
+0x5e, 0x5a,
+0x60, 0x87,
+0x61, 0x99,
+0x62, 0x88,
+0x63, 0x97,
+0x64, 0x88,
+0x65, 0x97,
+0x67, 0x0c,
+0x68, 0x0c,
+0x69, 0x0c,
+0x6a, 0xb4,
+0x6b, 0xc4,
+0x6c, 0xb5,
+0x6d, 0xc2,
+0x6e, 0xb5,
+0x6f, 0xc0,
+0x70, 0xb6,
+0x71, 0xb8,
+0x72, 0x89,
+0x73, 0x96,
+0x74, 0x89,
+0x75, 0x96,
+0x76, 0x89,
+0x77, 0x96,
+0x7c, 0x85,
+0x7d, 0xaf,
+0x80, 0x01,
+0x81, 0x7f,
+0x82, 0x13,
+0x83, 0x24,
+0x84, 0x7d,
+0x85, 0x81,
+0x86, 0x7d,
+0x87, 0x81,
+0x88, 0xab,
+0x89, 0xbc,
+0x8a, 0xac,
+0x8b, 0xba,
+0x8c, 0xad,
+0x8d, 0xb8,
+0x8e, 0xae,
+0x8f, 0xb2,
+0x90, 0xb3,
+0x91, 0xb7,
+0x92, 0x48,
+0x93, 0x54,
+0x94, 0x7d,
+0x95, 0x81,
+0x96, 0x7d,
+0x97, 0x81,
+0xa0, 0x02,
+0xa1, 0x7b,
+0xa2, 0x02,
+0xa3, 0x7b,
+0xa4, 0x7b,
+0xa5, 0x02,
+0xa6, 0x7b,
+0xa7, 0x02,
+0xa8, 0x85,
+0xa9, 0x8c,
+0xaa, 0x85,
+0xab, 0x8c,
+0xac, 0x10,
+0xad, 0x16,
+0xae, 0x10,
+0xaf, 0x16,
+0xb0, 0x99,
+0xb1, 0xa3,
+0xb2, 0xa4,
+0xb3, 0xae,
+0xb4, 0x9b,
+0xb5, 0xa2,
+0xb6, 0xa6,
+0xb7, 0xac,
+0xb8, 0x9b,
+0xb9, 0x9f,
+0xba, 0xa6,
+0xbb, 0xaa,
+0xbc, 0x9b,
+0xbd, 0x9f,
+0xbe, 0xa6,
+0xbf, 0xaa,
+0xc4, 0x2c,
+0xc5, 0x43,
+0xc6, 0x63,
+0xc7, 0x79,
+0xc8, 0x2d,
+0xc9, 0x42,
+0xca, 0x2d,
+0xcb, 0x42,
+0xcc, 0x64,
+0xcd, 0x78,
+0xce, 0x64,
+0xcf, 0x78,
+0xd0, 0x0a,
+0xd1, 0x09,
+0xd2, 0x20,
+0xd3, 0x00,
+0xd4, 0x0c, //DCDC_TIME_TH_ON don't touch
+0xd5, 0x0c, //DCDC_TIME_TH_OFF don't touch
+0xd6, 0x78, //DCDC_AG_TH_ON 78
+0xd7, 0x70, //DCDC_AG_TH_OFF 70
+0xe0, 0xc4,
+0xe1, 0xc4,
+0xe2, 0xc4,
+0xe3, 0xc4,
+0xe4, 0x00,
+0xe8, 0x80,
+0xe9, 0x40,
+0xea, 0x7f,
+0xf0, 0x01,
+0xf1, 0x01,
+0xf2, 0x01,
+0xf3, 0x01,
+0xf4, 0x01,
+/////// PAGE 3 ///////
+0x03, 0x03,
+0x10, 0x10,
+/////// PAGE 10 START ///////
+0x03, 0x10,
+0x10, 0x03, // CrYCbY // For Demoset 0x03
+0x12, 0x30,
+0x13, 0x0a,//add jacky open constrast
+0x20, 0x00,
+0x30, 0x00,
+0x31, 0x00,
+0x32, 0x00,
+0x33, 0x00,
+0x34, 0x30,
+0x35, 0x00,
+0x36, 0x00,
+0x38, 0x00,
+0x3e, 0x58,
+0x3f, 0x00,
+0x40, 0x90, // YOFS
+0x41, 0x00, // DYOFS
+0x48, 0x84,// 84
+0x60, 0x67,//6b AG ratio jacky
+
+0x61, 0x70, //7e //8e //88 //70
+0x62, 0x68, //7e //8e //88 //80
+0x63, 0x50, //Double_AG 50->30
+0x64, 0x41,
+0x66, 0x42,
+0x67, 0x20,
+0x6a, 0x80, //8a
+0x6b, 0x84, //74
+0x6c, 0x80, //7e //7a
+0x6d, 0x80, //8e
+/////// PAGE 11 START ///////
+0x03, 0x11,
+0x10, 0x7f,
+0x11, 0x40,
+0x12, 0x0a, // Blue Max-Filter Delete
+0x13, 0xbb,
+0x26, 0x51, // Double_AG 31->20
+0x27, 0x54, // Double_AG 34->22
+0x28, 0x0f,
+0x29, 0x10,
+0x2b, 0x30,
+0x2c, 0x32,
+//Out2 D-LPF th
+0x30, 0x70,
+0x31, 0x10,
+0x32, 0x58,
+0x33, 0x09,
+0x34, 0x06,
+0x35, 0x03,
+//Out1 D-LPF th
+0x36, 0x70,
+0x37, 0x18,
+0x38, 0x58,
+0x39, 0x09,
+0x3a, 0x06,
+0x3b, 0x03,
+//Indoor D-LPF th
+0x3c, 0x80,
+0x3d, 0x18,
+ 0x3e, 0x80,
+0x3f, 0x0c,
+0x40, 0x09,
+0x41, 0x06,
+0x42, 0x80,
+0x43, 0x18,
+ 0x44, 0x90,
+0x45, 0x12,
+0x46, 0x10,
+0x47, 0x10,
+0x48, 0x90,
+0x49, 0x40,
+0x4a, 0x80,
+0x4b, 0x13,
+0x4c, 0x10,
+0x4d, 0x11,
+0x4e, 0x80,
+0x4f, 0x30,
+0x50, 0x80,
+0x51, 0x13,
+0x52, 0x10,
+0x53, 0x13,
+0x54, 0x11,
+0x55, 0x17,
+0x56, 0x20,
+0x57, 0x01,
+0x58, 0x00,
+0x59, 0x00,
+0x5a, 0x1f, //18
+0x5b, 0x00,
+0x5c, 0x00,
+0x60, 0x3f,
+0x62, 0x60,
+0x70, 0x06,
+/////// PAGE 12 START ///////
+0x03, 0x12,
+0x20, 0x0f,
+0x21, 0x0f,
+0x25, 0x00, //0x30
+0x28, 0x00,
+0x29, 0x00,
+0x2a, 0x00,
+0x30, 0x50,
+0x31, 0x18,
+0x32, 0x32,
+0x33, 0x40,
+0x34, 0x50,
+0x35, 0x70,
+0x36, 0xa0,
+//Out2 th
+0x40, 0xa0,
+0x41, 0x40,
+0x42, 0xa0,
+0x43, 0x90,
+0x44, 0x90,
+0x45, 0x80,
+//Out1 th
+0x46, 0xb0,
+0x47, 0x55,
+0x48, 0xa0,
+0x49, 0x90,
+0x4a, 0x90,
+0x4b, 0x80,
+//Indoor th
+0x4c, 0xb0,
+0x4d, 0x40,
+0x4e, 0x90,
+0x4f, 0x90,
+0x50, 0xa0,
+0x51, 0x80,
+//Dark1 th
+0x52, 0x00, //b0
+0x53, 0x50, //60
+0x54, 0xd4, //c0
+0x55, 0xc0, //c0
+0x56, 0x70, //b0
+0x57, 0xec, //70
+//Dark2 th
+0x58, 0x60, //90
+0x59, 0x40, //
+0x5a, 0xd0,
+0x5b, 0xd0,
+0x5c, 0xc0,
+0x5d, 0x9b, //70
+//Dark3 th
+0x5e, 0x70,//88
+0x5f, 0x40,
+0x60, 0xe0,
+0x61, 0xe0,
+0x62, 0xe0,
+0x63, 0xb4,//80
+0x70, 0x15,
+0x71, 0x01, //Don't Touch register
+0x72, 0x18,
+0x73, 0x01, //Don't Touch register
+0x74, 0x25,
+0x75, 0x15,
+0x80, 0x20,
+0x81, 0x40,
+0x82, 0x65,
+0x85, 0x1a,
+0x88, 0x00,
+0x89, 0x00,
+0x90, 0x5d, //For capture
+0x91, 0x88,
+0x98, 0x7d,
+0x99, 0x28,
+0x9A, 0x14,
+0x9B, 0xc8,
+0x9C, 0x02,
+0x9D, 0x1e,
+0x9E, 0x28,
+0x9F, 0x07,
+0xA0, 0x32,
+0xA4, 0x04,
+0xA5, 0x0e,
+0xA6, 0x0c,
+0xA7, 0x04,
+0xA8, 0x3c,
+
+0xAA, 0x14,
+0xAB, 0x11,
+0xAC, 0x0f,
+0xAD, 0x16,
+0xAE, 0x15,
+0xAF, 0x14,
+
+0xB1, 0xaa,
+0xB2, 0x96,
+0xB3, 0x28,
+//Dont Touch register
+0xB8, 0x78,
+0xB9, 0xa0,
+0xBA, 0xb4,
+0xBB, 0x14,
+0xBC, 0x14,
+0xBD, 0x00, //14
+0xBE, 0x64,
+0xBF, 0x64,
+0xC0, 0x64,
+0xC1, 0x64,
+0xC2, 0x04,
+0xC3, 0x03,
+0xC4, 0x0c,
+0xC5, 0x30,
+0xC6, 0x2a,
+0xD0, 0x0c, //CI Option/CI DPC
+0xD1, 0x80,
+0xD2, 0x67,
+0xD3, 0x00,
+0xD4, 0x00,
+0xD5, 0x02,
+0xD6, 0xff,
+0xD7, 0x18,
+//End
+0x3b, 0x06,
+0x3c, 0x06,
+0xc5, 0x00,//55->48
+0xc6, 0x00,//48->40
+/////// PAGE 13 START ///////
+0x03, 0x13,
+//Edge
+0x10, 0xcb,
+0x11, 0x7b,
+0x12, 0x07,
+0x14, 0x00,
+0x20, 0x15,
+0x21, 0x13,
+0x22, 0x33,
+0x23, 0x05,
+0x24, 0x09,
+0x26, 0x18,
+0x27, 0x30,
+0x29, 0x12,
+0x2a, 0x50,
+//Low clip th
+0x2b, 0x01, //Out2 02
+0x2c, 0x01, //Out1 02
+0x25, 0x06,
+0x2d, 0x0c,
+0x2e, 0x12,
+0x2f, 0x12,
+//Out2 Edge
+0x50, 0x18,
+0x51, 0x1c,
+0x52, 0x1b,
+0x53, 0x15,
+0x54, 0x18,
+0x55, 0x15,
+//Out1 Edge
+0x56, 0x18,
+0x57, 0x1c,
+0x58, 0x1b,
+0x59, 0x15,
+0x5a, 0x18,
+0x5b, 0x15,
+//Indoor Edge
+0x5c, 0x0b,
+0x5d, 0x2c,
+0x5e, 0x1a,
+0x5f, 0x08,
+0x60, 0x29,
+0x61, 0x18,
+//Dark1 Edge
+0x62, 0x08,
+0x63, 0x28,
+0x64, 0x08,
+0x65, 0x06,
+0x66, 0x26,
+0x67, 0x06,
+//Dark2 Edge
+0x68, 0x07,
+0x69, 0x07,
+0x6a, 0x07,
+0x6b, 0x05,
+0x6c, 0x05,
+0x6d, 0x05,
+//Dark3 Edge
+0x6e, 0x07,
+0x6f, 0x07,
+0x70, 0x07,
+0x71, 0x05,
+0x72, 0x05,
+0x73, 0x05,
+0x80, 0xfd,
+0x81, 0x1f,
+0x82, 0x05,
+0x83, 0x31,
+0x90, 0x05,
+0x91, 0x05,
+0x92, 0x33,
+0x93, 0x30,
+0x94, 0x03,
+0x95, 0x14,
+0x97, 0x20,
+0x99, 0x20,
+0xa0, 0x01,
+0xa1, 0x02,
+0xa2, 0x01,
+0xa3, 0x02,
+0xa4, 0x05,
+0xa5, 0x05,
+0xa6, 0x07,
+0xa7, 0x08,
+0xa8, 0x07,
+0xa9, 0x08,
+0xaa, 0x07,
+0xab, 0x08,
+0xb0, 0x22,
+0xb1, 0x2a,
+0xb2, 0x28,
+0xb3, 0x22,
+0xb4, 0x2a,
+0xb5, 0x28,
+0xb6, 0x22,
+0xb7, 0x2a,
+0xb8, 0x28,
+0xb9, 0x22,
+0xba, 0x2a,
+0xbb, 0x28,
+0xbc, 0x25,
+0xbd, 0x2a,
+0xbe, 0x27,
+0xbf, 0x25,
+0xc0, 0x2a,
+0xc1, 0x27,
+0xc2, 0x1e,
+0xc3, 0x24,
+0xc4, 0x20,
+0xc5, 0x1e,
+0xc6, 0x24,
+0xc7, 0x20,
+0xc8, 0x18,
+0xc9, 0x20,
+0xca, 0x1e,
+0xcb, 0x18,
+0xcc, 0x20,
+0xcd, 0x1e,
+0xce, 0x18,
+0xcf, 0x20,
+0xd0, 0x1e,
+0xd1, 0x18,
+0xd2, 0x20,
+0xd3, 0x1e,
+/////// PAGE 14 START ///////
+0x03, 0x14,
+0x10, 0x11,
+0x14, 0x80, // GX
+0x15, 0x80, // GY
+0x16, 0x80, // RX
+0x17, 0x80, // RY
+0x18, 0x80, // BX
+0x19, 0x80, // BY
+0x20, 0x80, //X 60 //a0
+0x21, 0x80, //Y
+0x22, 0x78,
+0x23, 0x80,
+0x24, 0x80,
+0x30, 0xc8,
+0x31, 0x2b,
+0x32, 0x00,
+0x33, 0x00,
+0x34, 0x90,
+0x40, 0x56, //0x48//65
+0x50, 0x44, //0x34
+0x60, 0x46, //0x29
+0x70, 0x44, //0x34
+/////// PAGE 15 START ///////
+0x03, 0x15,
+0x10, 0x0f,
+//Rstep H 16
+//Rstep L 14
+0x14, 0x4f, //CMCOFSGH
+0x15, 0x32, //CMCOFSGM
+0x16, 0x24, //CMCOFSGL
+0x17, 0x2f, //CMC SIGN
+//CMC
+0x30, 0x91,
+0x31, 0x6a,
+0x32, 0x18,
+0x33, 0x17,
+0x34, 0x53,
+0x35, 0x08,
+0x36, 0x08,
+0x37, 0x3e,
+0x38, 0x87,
+//CMC OFS
+0x40, 0xa3,
+0x41, 0x3a,
+0x42, 0x93,
+0x43, 0x0b,
+0x44, 0x86,
+0x45, 0x06,
+0x46, 0x00,
+0x47, 0xa2,
+0x48, 0x22,
+//CMC POFS
+0x50, 0x82,
+0x51, 0x06,
+0x52, 0x83,
+0x53, 0x03,
+0x54, 0x11,
+0x55, 0x8e,
+0x56, 0x00,
+0x57, 0x06,
+0x58, 0x87,
+
+0x80, 0x03,
+0x85, 0x40,
+0x87, 0x02,
+0x88, 0x00,
+0x89, 0x00,
+0x8a, 0x00,
+
+
+
+/////// PAGE 16 START ///////
+0x03, 0x16,
+0x10, 0x31,//GMA_CTL
+0x18, 0x5e,//AG_ON
+0x19, 0x5d,//AG_OFF
+0x1A, 0x0e,//TIME_ON
+0x1B, 0x01,//TIME_OFF
+0x1C, 0xdc,//OUT_ON
+0x1D, 0xfe,//OUT_OFF
+
+//GMA
+0x30, 0x00,
+0x31, 0x06,
+0x32, 0x0e,
+0x33, 0x23,
+0x34, 0x55,
+0x35, 0x7f,
+0x36, 0x99,
+0x37, 0xac,
+0x38, 0xba,
+0x39, 0xc5,
+0x3a, 0xcd,
+0x3b, 0xd4,
+0x3c, 0xdb,
+0x3d, 0xe2,
+0x3e, 0xe7,
+0x3f, 0xec,
+0x40, 0xf3,
+0x41, 0xfa,
+0x42, 0xff,
+//RGMA
+0x50, 0x00,
+0x51, 0x07,
+0x52, 0x12,
+0x53, 0x27,
+0x54, 0x57,
+0x55, 0x7f,
+0x56, 0x99,
+0x57, 0xac,
+0x58, 0xba,
+0x59, 0xc5,
+0x5a, 0xcd,
+0x5b, 0xd4,
+0x5c, 0xdb,
+0x5d, 0xe2,
+0x5e, 0xe7,
+0x5f, 0xec,
+0x60, 0xf3,
+0x61, 0xfa,
+0x62, 0xff,
+//BGMA
+0x70, 0x00,
+0x71, 0x09,
+0x72, 0x16,
+0x73, 0x2c,
+0x74, 0x59,
+0x75, 0x7f,
+0x76, 0x99,
+0x77, 0xac,
+0x78, 0xba,
+0x79, 0xc5,
+0x7a, 0xcd,
+0x7b, 0xd4,
+0x7c, 0xdb,
+0x7d, 0xe2,
+0x7e, 0xe7,
+0x7f, 0xec,
+0x80, 0xf3,
+0x81, 0xfa,
+0x82, 0xff,
+/////// PAGE 17 START ///////
+0x03, 0x17,
+0x10, 0xf7,
+/////// PAGE 18 START ///////
+
+ 0x03, 0x18,
+ 0x10, 0x00,
+
+/////// PAGE 20 START ///////
+0x03, 0x20,
+0x11, 0x1c,
+0x18, 0x30,
+0x1a, 0x08,
+0x20, 0x01, //05_lowtemp Y Mean off
+0x21, 0x30,
+0x22, 0x10,
+0x23, 0x00,
+0x24, 0x00, //Uniform Scene Off
+0x28, 0xe7,
+0x29, 0x0d, //20100305 ad->0d
+0x2a, 0xff,
+0x2b, 0x34, //f4->Adaptive on //04 off
+0x2c, 0xc2,
+0x2d, 0xcf, //fe->AE Speed option
+0x2e, 0x33,
+0x30, 0x78, //f8
+0x32, 0x03,
+0x33, 0x2e,
+0x34, 0x30,
+0x35, 0xd4,
+0x36, 0xfe,
+0x37, 0x32,
+0x38, 0x04,
+0x39, 0x22, //AE_escapeC10
+0x3a, 0xde, //AE_escapeC11
+0x3b, 0x22, //AE_escapeC1
+0x3c, 0xde, //AE_escapeC2
+0x50, 0x45,
+0x51, 0x88,
+0x56, 0x03,
+0x57, 0xf7,
+0x58, 0x14,
+0x59, 0x88,
+0x5a, 0x04,
+
+0x60, 0xff,
+0x61, 0xff,
+0x62, 0xea,
+0x63, 0xab,
+0x64, 0xea,
+0x65, 0xab,
+0x66, 0xea,//eb
+0x67, 0x2b,//eb
+0x68, 0xe8,//eb
+0x69, 0x2b,//eb
+0x6a, 0xea,
+0x6b, 0xab,
+0x6c, 0xea,
+0x6d, 0xab,
+0x6e, 0xff,
+0x6f, 0xff,
+
+/*
+0x60, 0x55,
+0x61, 0x55,
+0x62, 0x6a,
+0x63, 0xa9,
+0x64, 0x6f,
+0x65, 0xf9,
+0x66, 0x6f,//eb
+0x67, 0xf9,//eb
+0x68, 0x6f,//eb
+0x69, 0xf9,//eb
+0x6a, 0x6f,
+0x6b, 0xf9,
+0x6c, 0x6a,
+0x6d, 0xa9,
+0x6e, 0x55,
+0x6f, 0x55,
+*/
+
+0x70, 0x76, //6e
+0x71, 0x89, //00 //-4
+0x76, 0x43,
+0x77, 0xe2, //04 //f2
+0x78, 0x23, //Yth1
+0x79, 0x46, //Yth2 //46
+0x7a, 0x23, //23
+0x7b, 0x22, //22
+0x7d, 0x23,
+0x83, 0x01, //EXP Normal 33.33 fps
+0x84, 0x5f,
+0x85, 0x90,
+0x86, 0x01, //EXPMin 7500.00 fps
+0x87, 0xf4,
+0x88, 0x05, //EXP Max 8.33 fps
+0x89, 0x7e,
+0x8a, 0x40,
+0x8B, 0x75, //EXP100
+0x8C, 0x30,
+0x8D, 0x61, //EXP120
+0x8E, 0xa8,
+0x9c, 0x17, //EXP Limit 500.00 fps
+0x9d, 0x70,
+0x9e, 0x01, //EXP Unit
+0x9f, 0xf4,
+0xb0, 0x18,
+0xb1, 0x14, //ADC 400->560
+0xb2, 0x80,//a0
+0xb3, 0x18,
+0xb4, 0x1a,
+0xb5, 0x44,
+0xb6, 0x2f,
+0xb7, 0x28,
+0xb8, 0x25,
+0xb9, 0x22,
+0xba, 0x21,
+0xbb, 0x20,
+0xbc, 0x1f,
+0xbd, 0x1f,
+0xc0, 0x14,
+0xc1, 0x1f,
+0xc2, 0x1f,
+0xc3, 0x18, //2b
+0xc4, 0x10, //08
+0xc8, 0x80,
+0xc9, 0x40,
+/////// PAGE 22 START ///////
+0x03, 0x22,
+0x10, 0xfd,
+0x11, 0x2e,
+0x19, 0x01,
+0x20, 0x30,
+0x21, 0x80,
+0x24, 0x01,
+0x30, 0x80,
+0x31, 0x80,
+0x38, 0x11,
+0x39, 0x34,
+0x40, 0xf7,
+0x41, 0x55,
+0x42, 0x33, //43
+
+0x43, 0xf6,
+0x44, 0x55, //44
+0x45, 0x44, //33
+
+0x46, 0x00,
+0x50, 0xb2,
+0x51, 0x81,
+0x52, 0x98,
+0x80, 0x38,//40
+0x81, 0x20,
+0x82, 0x30,
+
+0x83, 0x52,
+0x84, 0x16,//1E
+0x85, 0x5A,//52
+0x86, 0x23,
+
+0x87, 0x45,
+0x88, 0x35,
+0x89, 0x47,
+0x8a, 0x2b,
+
+0x8b, 0x40,
+0x8c, 0x33,
+0x8d, 0x38,
+0x8e, 0x30,
+
+0x8f, 0x53,
+0x90, 0x52,
+0x91, 0x50,
+0x92, 0x4c,
+0x93, 0x43,
+0x94, 0x37,
+0x95, 0x2f,//2a
+0x96, 0x26,//24
+0x97, 0x21,//20
+0x98, 0x1e,
+0x99, 0x1f,
+0x9a, 0x20,
+0x9b, 0x88,
+0x9c, 0x88,
+0x9d, 0x48,
+0x9e, 0x38,
+0x9f, 0x30,
+0xa0, 0x60,
+0xa1, 0x34,
+0xa2, 0x6f,
+0xa3, 0xff,
+0xa4, 0x14,
+0xa5, 0x2c,
+0xa6, 0xcf,
+0xad, 0x40,
+0xae, 0x4a,
+0xaf, 0x28,
+0xb0, 0x26,
+0xb1, 0x00,
+0xb8, 0xa0,
+0xb9, 0x00,
+// PAGE 20
+0x03, 0x20, //page 20
+0x10, 0x9c, //ae off
+// PAGE 22
+0x03, 0x22, //page 22
+0x10, 0xe9, //awb off
+// PAGE 0
+0x03, 0x00,
+0x0e, 0x03, //PLL On
+0x0e, 0x73, //PLLx2
+0x03, 0x00, // Dummy 750us
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+0x03, 0x00,
+ 0x03, 0x00,
+ 0x01, 0xf8,
+
+};
+
+#endif
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/hi257/hi257.c b/drivers/media/video/wmt_v4l2/sensors/hi257/hi257.c
new file mode 100755
index 00000000..61a6859e
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/hi257/hi257.c
@@ -0,0 +1,333 @@
+
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "hi257.h"
+
+#define sensor_write_array(sd, arr, sz) \
+ cmos_init_8bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write(sd->i2c_addr, reg, val)
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint8_t *regs;
+ size_t size;
+};
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+// CMOS_WIN_SIZE("QVGA", 320, 240, hi257_320_240_regs),
+ CMOS_WIN_SIZE("VGA", 640, 480, hi257_640_480_regs),
+// CMOS_WIN_SIZE("SVGA", 800, 600, hi257_800_600_regs),
+// CMOS_WIN_SIZE("720p", 1280, 720, hi257_1280_720_regs),
+ CMOS_WIN_SIZE("UXGA", 1600, 1200, hi257_1600_1200_rets),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = hi257_wb_auto;
+ size = ARRAY_SIZE(hi257_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = hi257_wb_incandescent;
+ size = ARRAY_SIZE(hi257_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = hi257_wb_daylight;
+ size = ARRAY_SIZE(hi257_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = hi257_wb_cloudy;
+ size = ARRAY_SIZE(hi257_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = hi257_wb_fluorescent;
+ size = ARRAY_SIZE(hi257_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_scenemode(struct cmos_subdev *sd, enum v4l2_scene_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case SCENE_MODE_AUTO:
+ regs = hi257_scene_mode_auto;
+ size = ARRAY_SIZE(hi257_scene_mode_auto);
+ break;
+ case SCENE_MODE_NIGHTSHOT:
+ regs = hi257_scene_mode_night;
+ size = ARRAY_SIZE(hi257_scene_mode_night);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_exposure(struct cmos_subdev *sd, enum v4l2_exposure_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case -2:
+ regs = hi257_exposure_neg6;
+ size = ARRAY_SIZE(hi257_exposure_neg6);
+ break;
+ case -1:
+ regs = hi257_exposure_neg3;
+ size = ARRAY_SIZE(hi257_exposure_neg3);
+ break;
+ case 0:
+ regs = hi257_exposure_zero;
+ size = ARRAY_SIZE(hi257_exposure_zero);
+ break;
+ case 1:
+ regs = hi257_exposure_pos3;
+ size = ARRAY_SIZE(hi257_exposure_pos3);
+ break;
+ case 2:
+ regs = hi257_exposure_pos6;
+ size = ARRAY_SIZE(hi257_exposure_pos6);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data;
+
+ sensor_write(sd, 0x03, 0x00);
+
+ data = sensor_read(sd, 0x11);
+
+ switch (value) {
+ case 0:
+ data &= ~0x01;
+ break;
+ case 1:
+ data |= 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sensor_write(sd, 0x11, data);
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data;
+
+ sensor_write(sd, 0x03, 0x00);
+
+ data = sensor_read(sd, 0x11);
+
+ switch (value) {
+ case 0:
+ data &= ~0x02;
+ break;
+ case 1:
+ data |= 0x02;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sensor_write(sd, 0x11, data);
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ case V4L2_CID_EXPOSURE:
+ return v4l2_ctrl_query_fill(qc, -2, 2, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return sensor_s_scenemode(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return sensor_s_exposure(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ default:
+ case WMT_V4L2_CID_CAMERA_ANTIBANDING:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win) {
+ pr_err("%s, s_mbus_fmt failed, width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+ return -EINVAL;
+ }
+ sensor_write_array(sd, win->regs, win->size);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+
+ return 0;
+}
+
+static int sensor_enum_framesizes(struct cmos_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ const struct cmos_win_size *win = &cmos_supported_win_sizes[index];
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ int data;
+
+ sensor_write(sd, 0x03, 0x0);
+
+ data = sensor_read(sd, 0x04);
+
+ return (data == sd->id) ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ if (sensor_identify(sd)) {
+ return -1;
+ }
+
+ sensor_write_array(sd, hi257_default_regs_init,
+ ARRAY_SIZE(hi257_default_regs_init));
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ return 0;
+}
+
+static struct cmos_subdev_ops hi257_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+ .enum_framesizes = sensor_enum_framesizes,
+};
+
+struct cmos_subdev hi257 = {
+ .name = "hi257",
+ .i2c_addr = 0x20,
+ .id = 0xc4,
+ .max_width = 1600,
+ .max_height = 1200,
+ .ops = &hi257_ops,
+};
+
+#if 0
+static int __init hi257_init(void)
+{
+ return cmos_register_sudbdev(&hi257);
+}
+
+static void __exit hi257_exit(void)
+{
+ return cmos_unregister_subdev(&hi257);
+}
+
+module_init(hi257_init);
+module_exit(hi257_exit);
+
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/hi257/hi257.h b/drivers/media/video/wmt_v4l2/sensors/hi257/hi257.h
new file mode 100755
index 00000000..a64be87a
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/hi257/hi257.h
@@ -0,0 +1,2195 @@
+#ifndef HI257_H
+#define HI257_H
+
+// Scene Mode
+uint8_t hi257_scene_mode_auto[] = {
+ 0x03,0x10,
+ 0x41,0x00,
+ 0x03,0x20,
+ 0xb2,0x60,
+ 0xff,0xff,
+};
+
+uint8_t hi257_scene_mode_night[] = {
+ 0x03,0x10,
+ 0x41,0x15,
+ 0x03,0x20,
+ 0xb2,0x80,
+ 0xff,0xff,
+};
+
+
+// White Balance
+uint8_t hi257_wb_auto [] = {
+ 0x03,0x22,
+ 0x11,0x2e,
+ 0x83,0x58, // RMAX
+ 0x84,0x16, // RMIN
+ 0x85,0x4f, // BMAX
+ 0x86,0x1a, // BMIN
+ 0xff,0xff,
+};
+
+uint8_t hi257_wb_incandescent [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x2a,
+ 0x82,0x3f,
+ 0x83,0x35,
+ 0x84,0x28,
+ 0x85,0x45,
+ 0x86,0x3b,
+ 0xff,0xff,
+};
+
+uint8_t hi257_wb_fluorescent [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x20,
+ 0x82,0x4d,
+ 0x83,0x25,
+ 0x84,0x1b,
+ 0x85,0x55,
+ 0x86,0x48,
+ 0xff,0xff,
+};
+
+uint8_t hi257_wb_daylight [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x3d,
+ 0x82,0x2e,
+ 0x83,0x40,
+ 0x84,0x33,
+ 0x85,0x33,
+ 0x86,0x28,
+ 0xff,0xff,
+};
+
+uint8_t hi257_wb_cloudy [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x50,
+ 0x82,0x25,
+ 0x83,0x55,
+ 0x84,0x4b,
+ 0x85,0x28,
+ 0x86,0x20,
+ 0xff,0xff,
+};
+
+
+// Exposure
+uint8_t hi257_exposure_neg6[] = {
+ 0x03,0x10,
+ 0x40,0xa8,
+};
+
+uint8_t hi257_exposure_neg3[] = {
+ 0x03,0x10,
+ 0x40,0x98,
+};
+
+uint8_t hi257_exposure_zero[] = {
+ 0x03,0x10,
+ 0x40,0x80,
+};
+
+uint8_t hi257_exposure_pos3[] = {
+ 0x03,0x10,
+ 0x40,0x08,
+};
+
+uint8_t hi257_exposure_pos6[] = {
+ 0x03,0x10,
+ 0x40,0x28,
+};
+
+
+// Resolution
+uint8_t hi257_1600_1200_rets[]={
+ 0x03, 0x00,
+ 0x10, 0x00,
+ 0x12, 0x04,
+ 0x20, 0x00,
+ 0x21, 0x0a,
+ 0x22, 0x00,
+ 0x23, 0x0a,
+
+ 0x03, 0x18,
+ 0x10, 0x00,
+ 0xff, 0xff,
+};
+
+uint8_t hi257_1280_720_regs[]={
+};
+
+uint8_t hi257_800_600_regs[]={
+
+ 0x03, 0x00,
+ 0x10, 0x10,
+ 0x12, 0x04,
+
+ 0x03, 0x18,
+ 0x10, 0x00,
+ 0xff, 0xff,
+};
+
+uint8_t hi257_640_480_regs[]={
+ 0x03, 0x00,
+ 0x10, 0x10,
+ 0x12, 0x04,
+
+ 0x03, 0x18,
+ 0x12, 0x20,
+ 0x10, 0x07,
+ 0x11, 0x00,
+ 0x20, 0x05,
+ 0x21, 0x00,
+ 0x22, 0x03,
+ 0x23, 0xc0,
+ 0x24, 0x00,
+ 0x25, 0x00,
+ 0x26, 0x00,
+ 0x27, 0x00,
+ 0x28, 0x05,
+ 0x29, 0x00,
+ 0x2a, 0x03,
+ 0x2b, 0xc0,
+ 0x2c, 0x0a,
+ 0x2d, 0x00,
+ 0x2e, 0x0a,
+ 0x2f, 0x00,
+ 0x30, 0x44,
+ 0xff, 0xff,
+};
+
+uint8_t hi257_320_240_regs[]= {
+ 0x01,0x01, //sleep on
+0x01,0x03, //sleep off
+0x01,0x01, //sleep on
+
+0x03,0x00, //Dummy 750us
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+
+0x08,0x00,
+0x09,0x77, // pad strength = max
+0x0a,0x07, // pad strength = max
+
+0x0e,0x03, //PLL On
+0x0e,0x73, //PLLx2
+
+0x03,0x00, //Dummy 750us
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+
+
+///// PAGE 20 /////
+0x03,0x20, //page 20
+0x10,0x1c, //AE off 50hz
+
+///// PAGE 22 /////
+0x03,0x22, //page 22
+0x10,0x69, //AWB off
+
+
+///// Initial Start /////
+///// PAGE 0 Start /////
+0x03,0x00, //page 0
+0x10,0x91,
+0x11,0x90, //Windowing On + 1Frame Skip
+0x12,0x04, //Rinsing edge 0x04 // Falling edge 0x00
+0x14,0x05,
+
+0x20,0x00, //Row H
+0x21,0x02, //Row L
+0x22,0x00, //Col H
+0x23,0x04, //Col L
+
+0x24,0x04, //Window height_H //= 1200
+0x25,0xb0, //Window height_L //
+0x26,0x06, //Window width_H //= 1600
+0x27,0x40, //Window wight_L
+
+0x40,0x01, //Hblank_360
+0x41,0x78,
+0x42,0x00, //Vblank
+0x43,0x14, //Flick Stop
+
+0x50,0x00, //Test Pattern
+
+///// BLC /////
+0x80,0x2e,
+0x81,0x7e,
+0x82,0x90,
+0x83,0x00,
+0x84,0xcc, //20130604 0x0c->0xcc
+0x85,0x00,
+0x86,0x00,
+0x87,0x0f,
+0x88,0x34,
+0x8a,0x0b,
+0x8e,0x80, //Pga Blc Hold
+
+0x90,0x0c, //BLC_TIME_TH_ON
+0x91,0x0c, //BLC_TIME_TH_OFF
+0x92,0x58, //BLC_AG_TH_ON
+0x93,0x50, //BLC_AG_TH_OFF
+0x96,0xdc, //BLC Outdoor Th On
+0x97,0xfe, //BLC Outdoor Th Off
+0x98,0x38,
+
+//OutDoor BLC
+0x99,0x43, //R,Gr,B,Gb Offset
+
+//Dark BLC
+0xa0,0x02, //R,Gr,B,Gb Offset
+
+//Normal BLC
+0xa8,0x43, //R,Gr,B,Gb Offset
+///// PAGE 0 END /////
+
+///// PAGE 2 START /////
+0x03,0x02,
+0x10,0x00,
+0x13,0x00,
+0x14,0x00,
+0x18,0xcc,
+0x19,0x01, // pmos switch on (for cfpn)
+0x1A,0x39, //20130604 0x09->0xcc
+0x1B,0x00,
+0x1C,0x1a, // for ncp
+0x1D,0x14, // for ncp
+0x1E,0x30, // for ncp
+0x1F,0x10,
+
+0x20,0x77,
+0x21,0xde,
+0x22,0xa7,
+0x23,0x30,
+0x24,0x77,
+0x25,0x10,
+0x26,0x10,
+0x27,0x3c,
+0x2b,0x80,
+0x2c,0x02,
+0x2d,0x58,
+0x2e,0x11,//20130604 0xde->0x11
+0x2f,0x11,//20130604 0xa7->0x11
+
+0x30,0x00,
+0x31,0x99,
+0x32,0x00,
+0x33,0x00,
+0x34,0x22,
+0x36,0x75,
+0x38,0x88,
+0x39,0x88,
+0x3d,0x03,
+0x3f,0x02,
+
+0x49,0xc1,//20130604 0x87->0xd1 --> mode Change Issue modify -> 0xc1
+0x4a,0x10,
+
+0x50,0x21,
+0x53,0xb1,
+0x54,0x10,
+0x55,0x1c, // for ncp
+0x56,0x11,
+0x58,0x3a,//20130604 add
+0x59,0x38,//20130604 add
+0x5d,0xa2,
+0x5e,0x5a,
+
+0x60,0x87,
+0x61,0x98,
+0x62,0x88,
+0x63,0x96,
+0x64,0x88,
+0x65,0x96,
+0x67,0x3f,
+0x68,0x3f,
+0x69,0x3f,
+
+0x72,0x89,
+0x73,0x95,
+0x74,0x89,
+0x75,0x95,
+0x7C,0x84,
+0x7D,0xaf,
+
+0x80,0x01,
+0x81,0x7a,
+0x82,0x13,
+0x83,0x24,
+0x84,0x78,
+0x85,0x7c,
+
+0x92,0x44,
+0x93,0x59,
+0x94,0x78,
+0x95,0x7c,
+
+0xA0,0x02,
+0xA1,0x74,
+0xA4,0x74,
+0xA5,0x02,
+0xA8,0x85,
+0xA9,0x8c,
+0xAC,0x10,
+0xAD,0x16,
+
+0xB0,0x99,
+0xB1,0xa3,
+0xB4,0x9b,
+0xB5,0xa2,
+0xB8,0x9b,
+0xB9,0x9f,
+0xBC,0x9b,
+0xBD,0x9f,
+
+0xc4,0x29,
+0xc5,0x40,
+0xc6,0x5c,
+0xc7,0x72,
+0xc8,0x2a,
+0xc9,0x3f,
+0xcc,0x5d,
+0xcd,0x71,
+
+0xd0,0x10,
+0xd1,0x14,
+0xd2,0x20,
+0xd3,0x00,
+0xd4,0x0c, //DCDC_TIME_TH_ON
+0xd5,0x0c, //DCDC_TIME_TH_OFF
+0xd6,0x58, //DCDC_AG_TH_ON
+0xd7,0x50, //DCDC_AG_TH_OFF
+0xdc,0x00,
+0xdd,0xa3,
+0xde,0x00,
+0xdf,0x84,
+
+0xe0,0xa4,
+0xe1,0xa4,
+0xe2,0xa4,
+0xe3,0xa4,
+0xe4,0xa4,
+0xe5,0x01,
+0xe8,0x00,
+0xe9,0x00,
+0xea,0x77,
+
+0xF0,0x00,
+0xF1,0x00,
+0xF2,0x00,
+
+///// PAGE 2 END /////
+
+///// PAGE 10 START /////
+0x03,0x10, //page 10
+0x10,0x03, //S2D enable _ YUYV Order o??
+0x11,0x03,
+0x12,0xf0,
+0x13,0x01,
+
+0x20,0x00,
+0x21,0x40,
+0x22,0x0f,
+0x24,0x20,
+0x25,0x10,
+0x26,0x01,
+0x27,0x02,
+0x28,0x11,
+
+0x40,0x00,
+0x41,0x00, //D-YOffset Th
+0x42,0x04, //Cb Offset
+0x43,0x04, //Cr Offset
+0x44,0x80,
+0x45,0x80,
+0x46,0xf0,
+0x48,0x80,
+0x4a,0x80,
+
+0x50,0xa0, //D-YOffset AG
+
+0x60,0x0f,
+0x61,0xc0, //Sat B
+0x62,0xc0, //Sat R
+0x63,0x58, //Auto-De Color
+
+0x66,0x42,
+0x67,0x22,
+
+0x6a,0x7c, //White Protection Offset Dark/Indoor
+0x74,0x0c, //White Protection Offset Outdoor
+0x76,0x01, //White Protection Enable
+///// PAGE 10 END /////
+
+///// PAGE 11 START /////
+0x03,0x11, //page 11
+
+//LPF Auto Control
+0x20,0x00,
+0x21,0x00,
+0x26,0x5a, // Double_AG
+0x27,0x58, // Double_AG
+0x28,0x0f,
+0x29,0x10,
+0x2b,0x30,
+0x2c,0x32,
+
+//GBGR
+0x70,0x2b,
+0x74,0x30,
+0x75,0x18,
+0x76,0x30,
+0x77,0xff,
+0x78,0xa0,
+0x79,0xff, //Dark GbGr Th
+0x7a,0x30,
+0x7b,0x20,
+0x7c,0xf4, //Dark Dy Th B[7:4]
+///// PAGE 11 END /////
+
+///// PAGE 12 START /////
+0x03,0x12, //page 11
+
+//YC2D
+0x10,0x03, //Y DPC Enable
+0x11,0x08, //
+0x12,0x10, //0x30 -> 0x10
+0x20,0x53, //Y_lpf_enable
+0x21,0x03, //C_lpf_enable_on
+0x22,0xe6, //YC2D_CrCbY_Dy
+
+0x23,0x14, //Outdoor Dy Th
+0x24,0x20, //Indoor Dy Th // For reso Limit 0x20
+0x25,0x30, //Dark Dy Th
+
+//Outdoor LPF Flat
+0x30,0xff, //Y Hi Th
+0x31,0x00, //Y Lo Th
+0x32,0xf0, //Std Hi Th //Reso Improve Th Low //50
+0x33,0x00, //Std Lo Th
+0x34,0x00, //Median ratio
+
+//Indoor LPF Flat
+0x35,0xff, //Y Hi Th
+0x36,0x00, //Y Lo Th
+0x37,0xff, //Std Hi Th //Reso Improve Th Low //50
+0x38,0x00, //Std Lo Th
+0x39,0x00, //Median ratio
+
+//Dark LPF Flat
+0x3a,0xff, //Y Hi Th
+0x3b,0x00, //Y Lo Th
+0x3c,0x93, //Std Hi Th //Reso Improve Th Low //50
+0x3d,0x00, //Std Lo Th
+0x3e,0x00, //Median ratio
+
+//Outdoor Cindition
+0x46,0xa0, //Out Lum Hi
+0x47,0x40, //Out Lum Lo
+
+//Indoor Cindition
+0x4c,0xb0, //Indoor Lum Hi
+0x4d,0x40, //Indoor Lum Lo
+
+//Dark Cindition
+0x52,0xb0, //Dark Lum Hi
+0x53,0x50, //Dark Lum Lo
+
+//C-Filter
+0x70,0x10, //Outdoor(2:1) AWM Th Horizontal
+0x71,0x0a, //Outdoor(2:1) Diff Th Vertical
+0x72,0x10, //Indoor,Dark1 AWM Th Horizontal
+0x73,0x0a, //Indoor,Dark1 Diff Th Vertical
+0x74,0x18, //Dark(2:3) AWM Th Horizontal
+0x75,0x0f, //Dark(2:3) Diff Th Vertical
+
+//DPC
+0x90,0x7d,
+0x91,0x34,
+0x99,0x28,
+0x9c,0x14,
+0x9d,0x15,
+0x9e,0x28,
+0x9f,0x28,
+0xb0,0x0e, //Zipper noise Detault change (0x75->0x0e)
+0xb8,0x44,
+0xb9,0x15,
+///// PAGE 12 END /////
+
+///// PAGE 13 START /////
+0x03,0x13, //page 13
+
+0x80,0xfd, //Sharp2D enable _ YUYV Order o??
+0x81,0x07, //Sharp2D Clip/Limit
+0x82,0x73, //Sharp2D Filter
+0x83,0x00, //Sharp2D Low Clip
+0x85,0x00,
+
+0x92,0x33, //Sharp2D Slop n/p
+0x93,0x30, //Sharp2D LClip
+0x94,0x02, //Sharp2D HiClip1 Th
+0x95,0xf0, //Sharp2D HiClip2 Th
+0x96,0x1e, //Sharp2D HiClip2 Resolution
+0x97,0x40,
+0x98,0x80,
+0x99,0x40,
+
+//Sharp Lclp
+0xa2,0x04, //Outdoor Lclip_N
+0xa3,0x05, //Outdoor Lclip_P
+0xa4,0x06, //Indoor Lclip_N 0x03 For reso Limit 0x0e
+0xa5,0x07, //Indoor Lclip_P 0x0f For reso Limit 0x0f
+0xa6,0x80, //Dark Lclip_N
+0xa7,0x80, //Dark Lclip_P
+
+//Outdoor Slope
+0xb6,0x28, //Lum negative Hi
+0xb7,0x20, //Lum negative middle
+0xb8,0x24, //Lum negative Low
+0xb9,0x28, //Lum postive Hi
+0xba,0x20, //Lum postive middle
+0xbb,0x24, //Lum postive Low
+
+//Indoor Slope
+0xbc,0x20, //Lum negative Hi
+0xbd,0x20, //Lum negative middle
+0xbe,0x20, //Lum negative Low
+0xbf,0x20, //Lum postive Hi
+0xc0,0x20, //Lum postive middle
+0xc1,0x20, //Lum postive Low
+
+//Dark Slope
+0xc2,0x14, //Lum negative Hi
+0xc3,0x24, //Lum negative middle
+0xc4,0x1d, //Lum negative Low
+0xc5,0x14, //Lum postive Hi
+0xc6,0x24, //Lum postive middle
+0xc7,0x1d, //Lum postive Low
+///// PAGE 13 END /////
+
+///// PAGE 14 START /////
+0x03,0x14, //page 14
+0x10,0x01,
+
+0x20,0xA0,//X-Center
+0x21,0x80,//Y-Center
+
+0x22,0x12, //LSC R 1b->15 20130125
+0x23,0x0f, //LSC G
+0x24,0x0f, //LSC B
+
+0x25,0xf0, //LSC Off
+0x26,0xf0, //LSC On
+///// PAGE 14 END /////
+
+/////// PAGE 15 START ///////
+0x03,0x15, //page 15
+0x10,0x21,
+0x14,0x44,
+0x15,0x34,
+0x16,0x26,
+0x17,0x2f,
+
+//CMC
+0x30,0xdd,
+0x31,0x68,
+0x32,0x0b,
+0x33,0x2c,
+0x34,0xce,
+0x35,0x22,
+0x36,0x0e,
+0x37,0x42,
+0x38,0xd0,
+
+//CMC OFS
+0x40,0x90,
+0x41,0x16,
+0x42,0x86,
+0x43,0x01,
+0x44,0x10,
+0x45,0x91,
+0x46,0x82,
+0x47,0x9c,
+0x48,0x1d,
+
+//CMC POFS
+0x50,0x0d,
+0x51,0x91,
+0x52,0x04,
+0x53,0x07,
+0x54,0x19,
+0x55,0xa0,
+0x56,0x00,
+0x57,0x98,
+0x58,0x18,
+///// PAGE 15 END /////
+
+///// PAGE 16 START /////
+0x03,0x16, //page 16
+0x10,0x31,
+0x18,0x5a,// Double_AG 5e->37
+0x19,0x58,// Double_AG 5e->36
+0x1a,0x0e,
+0x1b,0x01,
+0x1c,0xdc,
+0x1d,0xfe,
+
+//Indoor
+0x30,0x00,
+0x31,0x06,
+0x32,0x12,
+0x33,0x29,
+0x34,0x4d,
+0x35,0x6c,
+0x36,0x81,
+0x37,0x94,
+0x38,0xa4,
+0x39,0xb3,
+0x3a,0xc0,
+0x3b,0xcb,
+0x3c,0xd5,
+0x3d,0xde,
+0x3e,0xe6,
+0x3f,0xee,
+0x40,0xf5,
+0x41,0xfc,
+0x42,0xff,
+
+
+//Outdoor
+0x50,0x00,
+0x51,0x06,
+0x52,0x12,
+0x53,0x29,
+0x54,0x45,
+0x55,0x60,
+0x56,0x7a,
+0x57,0x8e,
+0x58,0xa1,
+0x59,0xac,
+0x5a,0xb8,
+0x5b,0xc5,
+0x5c,0xd0,
+0x5d,0xda,
+0x5e,0xe3,
+0x5f,0xeb,
+0x60,0xf0,
+0x61,0xf4,
+0x62,0xf5,
+
+//Dark
+0x70,0x00,
+0x71,0x0e,
+0x72,0x17,
+0x73,0x26,
+0x74,0x3f,
+0x75,0x56,
+0x76,0x6c,
+0x77,0x81,
+0x78,0x94,
+0x79,0xa5,
+0x7a,0xb3,
+0x7b,0xc4,
+0x7c,0xce,
+0x7d,0xd9,
+0x7e,0xe4,
+0x7f,0xec,
+0x80,0xf5,
+0x81,0xfc,
+0x82,0xff,
+///// PAGE 16 END /////
+
+///// PAGE 17 START /////
+0x03,0x17, //page 17
+0xc1,0x00,
+0xc4,0x4b,
+0xc5,0x3f,
+0xc6,0x02,
+0xc7,0x20,
+///// PAGE 17 END /////
+
+///// PAGE 18 START /////
+ //page 18
+ 0x03, 0x18,
+ 0x12, 0x20,
+ 0x10, 0x07,
+ 0x11, 0x00,
+ 0x20, 0x02,
+ 0x21, 0x80,
+ 0x22, 0x01,
+ 0x23, 0xe0,
+ 0x24, 0x00,
+ 0x25, 0x00,
+ 0x26, 0x00,
+ 0x27, 0x00,
+ 0x28, 0x02,
+ 0x29, 0x80,
+ 0x2a, 0x01,
+ 0x2b, 0xe0,
+ 0x2c, 0x14,
+ 0x2d, 0x00,
+ 0x2e, 0x14,
+ 0x2f, 0x00,
+ 0x30, 0x64,
+///// PAGE 18 END /////
+
+///// PAGE 19 START /////
+0x03,0x19, //Page 0x18
+0x10,0x7f, //mcmc_ctl1 MCMC Enable B:[0]
+0x11,0x7f, //mcmc_ctl2
+0x12,0x1b, //mcmc_delta1
+0x13,0xac, //mcmc_center1
+0x14,0x1e, //mcmc_delta2
+0x15,0x6e, //mcmc_center2
+0x16,0x0a, //mcmc_delta3
+0x17,0xb8, //mcmc_center3
+0x18,0x1e, //mcmc_delta4
+0x19,0xe6, //mcmc_center4
+0x1a,0x9e, //mcmc_delta5
+0x1b,0x22, //mcmc_center5
+0x1c,0x9e, //mcmc_delta6
+0x1d,0x5e, //mcmc_center6
+0x1e,0x70,//mcmc_sat_gain1
+0x1f,0x37, //mcmc_sat_gain2
+0x20,0x21, //mcmc_sat_gain3
+0x21,0x43, //mcmc_sat_gain4
+0x22,0x2f, //mcmc_sat_gain5
+0x23,0x37, //mcmc_sat_gain6
+0x24,0x00, //mcmc_hue_angle1
+0x25,0x07, //mcmc_hue_angle2
+0x26,0x09, //mcmc_hue_angle3
+0x27,0x04, //mcmc_hue_angle4
+0x28,0x00, //mcmc_hue_angle5
+0x29,0x8c, //mcmc_hue_angle6
+
+0x53,0x10, //mcmc_ctl3
+
+0x6c,0xff, //mcmc_lum_ctl1 sat hue offset
+0x6d,0x3f, //mcmc_lum_ctl2 gain
+0x6e,0x00, //mcmc_lum_ctl3 hue
+0x6f,0x00, //mcmc_lum_ctl4 rgb offset
+0x70,0x00, //mcmc_lum_ctl5 rgb scale
+
+0x71,0x3f, //mcmc_lum_gain_wgt_th1 R1 magenta
+0x72,0x3f, //mcmc_lum_gain_wgt_th2 R1
+0x73,0x3f, //mcmc_lum_gain_wgt_th3 R1
+0x74,0x3f, //mcmc_lum_gain_wgt_th4 R1
+0x75,0x30, //mcmc_rg1_lum_sp1 R1
+0x76,0x50, //mcmc_rg1_lum_sp2 R1
+0x77,0x80, //mcmc_rg1_lum_sp3 R1
+0x78,0xb0, //mcmc_rg1_lum_sp4 R1
+
+0x79,0x3f, //mcmc_lum_gain_wgt_th1 R2 bright green
+0x7a,0x3f, //mcmc_lum_gain_wgt_th2 R2
+0x7b,0x3f, //mcmc_lum_gain_wgt_th3 R2
+0x7c,0x3f, //mcmc_lum_gain_wgt_th4 R2
+0x7d,0x28, //mcmc_rg2_lum_sp1 R2
+0x7e,0x50, //mcmc_rg2_lum_sp2 R2
+0x7f,0x80, //mcmc_rg2_lum_sp3 R2
+0x80,0xb0, //mcmc_rg2_lum_sp4 R2
+
+0x81,0x28, //mcmc_lum_gain_wgt_th1 R3 dark green
+0x82,0x3f, //mcmc_lum_gain_wgt_th2 R3
+0x83,0x3f, //mcmc_lum_gain_wgt_th3 R3
+0x84,0x3f, //mcmc_lum_gain_wgt_th4 R3
+0x85,0x28, //mcmc_rg3_lum_sp1 R3
+0x86,0x50, //mcmc_rg3_lum_sp2 R3
+0x87,0x80, //mcmc_rg3_lum_sp3 R3
+0x88,0xb0, //mcmc_rg3_lum_sp4 R3
+
+0x89,0x1a, //mcmc_lum_gain_wgt_th1 R4 skin
+0x8a,0x28, //mcmc_lum_gain_wgt_th2 R4
+0x8b,0x3f, //mcmc_lum_gain_wgt_th3 R4
+0x8c,0x3f, //mcmc_lum_gain_wgt_th4 R4
+0x8d,0x10, //mcmc_rg3_lum_sp1 R4
+0x8e,0x30, //mcmc_rg3_lum_sp2 R4
+0x8f,0x60, //mcmc_rg3_lum_sp3 R4
+0x90,0x90, //mcmc_rg3_lum_sp4 R4
+
+0x91,0x1a, //mcmc_rg5_gain_wgt_th1 R5 cyan
+0x92,0x28, //mcmc_rg5_gain_wgt_th2 R5
+0x93,0x3f, //mcmc_rg5_gain_wgt_th3 R5
+0x94,0x3f, //mcmc_rg5_gain_wgt_th4 R5
+0x95,0x28, //mcmc_rg5_lum_sp1 R5
+0x96,0x50, //mcmc_rg5_lum_sp2 R5
+0x97,0x80, //mcmc_rg5_lum_sp3 R5
+0x98,0xb0, //mcmc_rg5_lum_sp4 R5
+
+0x99,0x1a, //mcmc_rg6_gain_wgt_th1 R6 blue
+0x9a,0x28, //mcmc_rg6_gain_wgt_th2 R6
+0x9b,0x3f, //mcmc_rg6_gain_wgt_th3 R6
+0x9c,0x3f, //mcmc_rg6_gain_wgt_th4 R6
+0x9d,0x28, //mcmc_rg6_lum_sp1 R6
+0x9e,0x50, //mcmc_rg6_lum_sp2 R6
+0x9f,0x80, //mcmc_rg6_lum_sp3 R6
+0xa0,0xb0, //mcmc_rg6_lum_sp4 R6
+
+0xa2,0x00,
+0xe5,0x80, //add 20120709 Bit[7] On MCMC --> YC2D_LPF
+
+/////// PAGE 20 START ///////
+0x03,0x20,
+0x10,0x1c,
+0x11,0x14,
+0x18,0x30,
+0x20,0x25, //8x8 Ae weight 0~7 Outdoor / Weight Outdoor On B[5]
+0x21,0x30,
+0x22,0x10,
+0x23,0x00,
+
+0x28,0xe7,
+0x29,0x0d,
+0x2a,0xff,
+0x2b,0x04, //Adaptive Off,1/100 Flicker
+
+0x2c,0x83, //AE After CI
+0x2d,0x03,
+0x2e,0x13,
+0x2f,0x0b,
+
+0x30,0x78,
+0x31,0xd7,
+0x32,0x10,
+0x33,0x2e,
+0x34,0x20,
+0x35,0xd4,
+0x36,0xfe,
+0x37,0x32,
+0x38,0x04,
+0x39,0x22,
+0x3a,0xde,
+0x3b,0x22,
+0x3c,0xde,
+0x3d,0xe1,
+
+0x50,0x45,
+0x51,0x88,
+
+0x56,0x1f, // for tracking
+0x57,0xa6, // for tracking
+0x58,0x1a, // for tracking
+0x59,0x7a, // for tracking
+
+0x5a,0x04,
+0x5b,0x04,
+
+0x5e,0xc7,
+0x5f,0x95,
+
+0x62,0x10,
+0x63,0xc0,
+0x64,0x10,
+0x65,0x8a,
+0x66,0x58,
+0x67,0x58,
+
+0x70,0x54, //6c
+0x71,0x80, //81(+4),89(-4)
+
+0x76,0x32,
+0x77,0xb1,
+0x78,0x22, //24
+0x79,0x32, // Y Target 70 => 25,72 => 26 //
+0x7a,0x23, //23
+0x7b,0x22, //22
+0x7d,0x23,
+
+0x83,0x02, //EXP Normal 33.33 fps
+0x84,0xbf,
+0x85,0x20,
+
+0x86,0x01, //EXPMin 12000.00 fps
+0x87,0xf4,
+
+0x88,0x0b, //EXP Max(120Hz) 8.00 fps
+0x89,0x71,
+0x8a,0xb0,
+
+0xa5,0x0b, //EXP Max(100Hz) 7.69 fps
+0xa6,0xe6,
+0xa7,0xe0,
+
+0x8B,0xea, //EXP100
+0x8C,0x60,
+
+0x8D,0xc3, //EXP120
+0x8E,0x50,
+
+0x9c,0x0f, //EXP Limit 1500.00 fps
+0x9d,0xa0,
+0x9e,0x01, //EXP Unit
+0x9f,0xf4,
+
+0xa3,0x00,
+0xa4,0xfd, //Exp 12000
+
+0xb0,0x15,
+0xb1,0x14,
+0xb2,0x60,
+0xb3,0x1a,
+0xb4,0x1c,
+0xb5,0x48,
+0xb6,0x32,
+0xb7,0x2b,
+0xb8,0x27,
+0xb9,0x25,
+0xba,0x23,
+0xbb,0x22,
+0xbc,0x22,
+0xbd,0x21,
+
+0xc0,0x10,
+0xc1,0x40,
+0xc2,0x40,
+0xc3,0x40,
+0xc4,0x06,
+
+0xc8,0x80,
+0xc9,0x80,
+///// PAGE 20 END /////
+
+///// PAGE 21 START /////
+0x03,0x21, //page 21
+
+//Indoor Weight
+0x20,0x11,
+0x21,0x11,
+0x22,0x11,
+0x23,0x11,
+0x24,0x14,
+0x25,0x44,
+0x26,0x44,
+0x27,0x41,
+0x28,0x14,
+0x29,0x44,
+0x2a,0x44,
+0x2b,0x41,
+0x2c,0x14,
+0x2d,0x47,
+0x2e,0x74,
+0x2f,0x41,
+0x30,0x14,
+0x31,0x47,
+0x32,0x74,
+0x33,0x41,
+0x34,0x14,
+0x35,0x44,
+0x36,0x44,
+0x37,0x41,
+0x38,0x14,
+0x39,0x44,
+0x3a,0x44,
+0x3b,0x41,
+0x3c,0x11,
+0x3d,0x11,
+0x3e,0x11,
+0x3f,0x11,
+
+//Outdoor Weight
+0x40,0x11,
+0x41,0x11,
+0x42,0x11,
+0x43,0x11,
+0x44,0x14,
+0x45,0x44,
+0x46,0x44,
+0x47,0x41,
+0x48,0x14,
+0x49,0x44,
+0x4a,0x44,
+0x4b,0x41,
+0x4c,0x14,
+0x4d,0x47,
+0x4e,0x74,
+0x4f,0x41,
+0x50,0x14,
+0x51,0x47,
+0x52,0x74,
+0x53,0x41,
+0x54,0x14,
+0x55,0x44,
+0x56,0x44,
+0x57,0x41,
+0x58,0x14,
+0x59,0x44,
+0x5a,0x44,
+0x5b,0x41,
+0x5c,0x11,
+0x5d,0x11,
+0x5e,0x11,
+0x5f,0x11,
+
+
+///// PAGE 22 START /////
+0x03,0x22, //page 22
+0x10,0xfd,
+0x11,0x2e,
+0x19,0x00, //Low On //
+0x20,0x30, //For AWB Speed
+0x21,0x80,
+0x22,0x00,
+0x23,0x00,
+0x24,0x01,
+0x25,0x7e, //for Tracking setting
+
+0x30,0x80,
+0x31,0x80,
+0x38,0x11,
+0x39,0x34,
+0x40,0xe4, //Stb Yth
+0x41,0x33, //Stb cdiff
+0x42,0x22, //Stb csum
+0x43,0xf3, //Unstb Yth
+0x44,0x55, //Unstb cdiff
+0x45,0x33, //Unstb csum
+0x46,0x00,
+0x47,0xa2,
+0x48,0x02,
+0x49,0x0a,
+
+0x60,0x04,
+0x61,0xc4,
+0x62,0x04,
+0x63,0x92,
+0x66,0x04,
+0x67,0xc4,
+0x68,0x04,
+0x69,0x92,
+
+0x80,0x38,
+0x81,0x20,
+0x82,0x38,
+
+0x83,0x58,
+0x84,0x16,
+0x85,0x4f,
+0x86,0x1a,
+
+0x87,0x42,
+0x88,0x30,
+0x89,0x27,
+0x8a,0x18,
+
+0x8b,0x3d,
+0x8c,0x32,
+0x8d,0x24,
+0x8e,0x1d,
+
+0x8f,0x4d, //4e
+0x90,0x46, //4d
+0x91,0x40, //4c
+0x92,0x3a, //4a
+0x93,0x2f, //46
+0x94,0x21,
+0x95,0x19,
+0x96,0x16,
+0x97,0x13,
+0x98,0x12,
+0x99,0x11,
+0x9a,0x10,
+
+0x9b,0xbb,
+0x9c,0xaa,
+0x9d,0x48,
+0x9e,0x38,
+0x9f,0x30,
+
+0xa0,0x70,
+0xa1,0x54,
+0xa2,0x6f,
+0xa3,0xff,
+
+0xa4,0x14, //1536fps
+0xa5,0x2c, //698fps
+0xa6,0xcf, //148fps
+
+0xad,0x40,
+0xae,0x4a,
+
+0xaf,0x28, //Low temp Rgain
+0xb0,0x26, //Low temp Rgain
+
+0xb1,0x00,
+0xb4,0xbf, //For Tracking AWB Weight
+0xb8,0x91, //(0+,1-)High Cb ,(0+,1-)Low Cr
+0xb9,0x00,
+/////// PAGE 22 END ///////
+
+/////// PAGE 20 ///////
+0x03,0x20,
+0x10,0x9c, //AE On 50hz
+
+/////// PAGE 22 ///////
+0x03,0x22,
+0x10,0xe9, //AWB On
+
+/////// PAGE 0 ///////
+0x03,0x00,
+0x0e,0x03,
+0x0e,0x73,
+
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+
+0x03,0x18,
+0x12,0x20,
+0x10,0x07,
+0x11,0x00,
+0x20,0x02,
+0x21,0x80,
+0x22,0x03,
+0x23,0xc0,
+0x24,0x00,
+0x25,0x00,
+0x26,0x00,
+0x27,0x00,
+0x28,0x02,
+0x29,0x80,
+0x2a,0x03,
+0x2b,0xc0,
+0x2c,0x0a,
+0x2d,0x00,
+0x2e,0x0a,
+0x2f,0x00,
+0x30,0x24,
+
+0x03,0x00,
+0x01,0x00,
+
+};
+
+uint8_t hi257_default_regs_init[]={
+
+0x01,0x01, //sleep on
+0x01,0x03, //sleep off
+0x01,0x01, //sleep on
+
+0x03,0x00, //Dummy 750us
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+
+0x08,0x00,
+0x09,0x77, // pad strength = max
+0x0a,0x07, // pad strength = max
+
+0x0e,0x03, //PLL On
+0x0e,0x73, //PLLx2
+
+0x03,0x00, //Dummy 750us
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+
+
+///// PAGE 20 /////
+0x03,0x20, //page 20
+0x10,0x1c, //AE off 50hz
+
+///// PAGE 22 /////
+0x03,0x22, //page 22
+0x10,0x69, //AWB off
+
+
+///// Initial Start /////
+///// PAGE 0 Start /////
+0x03,0x00, //page 0
+0x10,0x90,
+0x11,0x90, //Windowing On + 1Frame Skip
+0x12,0x04, //Rinsing edge 0x04 // Falling edge 0x00
+0x14,0x05,
+
+0x20,0x00, //Row H
+0x21,0x02, //Row L
+0x22,0x00, //Col H
+0x23,0x04, //Col L
+
+0x24,0x04, //Window height_H //= 1200
+0x25,0xb0, //Window height_L //
+0x26,0x06, //Window width_H //= 1600
+0x27,0x40, //Window wight_L
+
+0x40,0x01, //Hblank_360
+0x41,0x78,
+0x42,0x00, //Vblank
+0x43,0x14, //Flick Stop
+
+0x50,0x00, //Test Pattern
+
+///// BLC /////
+0x80,0x2e,
+0x81,0x7e,
+0x82,0x90,
+0x83,0x00,
+0x84,0xcc, //20130604 0x0c->0xcc
+0x85,0x00,
+0x86,0x00,
+0x87,0x0f,
+0x88,0x34,
+0x8a,0x0b,
+0x8e,0x80, //Pga Blc Hold
+
+0x90,0x0c, //BLC_TIME_TH_ON
+0x91,0x0c, //BLC_TIME_TH_OFF
+0x92,0x58, //BLC_AG_TH_ON
+0x93,0x50, //BLC_AG_TH_OFF
+0x96,0xdc, //BLC Outdoor Th On
+0x97,0xfe, //BLC Outdoor Th Off
+0x98,0x38,
+
+//OutDoor BLC
+0x99,0x43, //R,Gr,B,Gb Offset
+
+//Dark BLC
+0xa0,0x02, //R,Gr,B,Gb Offset
+
+//Normal BLC
+0xa8,0x43, //R,Gr,B,Gb Offset
+///// PAGE 0 END /////
+
+///// PAGE 2 START /////
+0x03,0x02,
+0x10,0x00,
+0x13,0x00,
+0x14,0x00,
+0x18,0xcc,
+0x19,0x01, // pmos switch on (for cfpn)
+0x1A,0x39, //20130604 0x09->0xcc
+0x1B,0x00,
+0x1C,0x1a, // for ncp
+0x1D,0x14, // for ncp
+0x1E,0x30, // for ncp
+0x1F,0x10,
+
+0x20,0x77,
+0x21,0xde,
+0x22,0xa7,
+0x23,0x30,
+0x24,0x77,
+0x25,0x10,
+0x26,0x10,
+0x27,0x3c,
+0x2b,0x80,
+0x2c,0x02,
+0x2d,0x58,
+0x2e,0x11,//20130604 0xde->0x11
+0x2f,0x11,//20130604 0xa7->0x11
+
+0x30,0x00,
+0x31,0x99,
+0x32,0x00,
+0x33,0x00,
+0x34,0x22,
+0x36,0x75,
+0x38,0x88,
+0x39,0x88,
+0x3d,0x03,
+0x3f,0x02,
+
+0x49,0xc1,//20130604 0x87->0xd1 --> mode Change Issue modify -> 0xc1
+0x4a,0x10,
+
+0x50,0x21,
+0x53,0xb1,
+0x54,0x10,
+0x55,0x1c, // for ncp
+0x56,0x11,
+0x58,0x3a,//20130604 add
+0x59,0x38,//20130604 add
+0x5d,0xa2,
+0x5e,0x5a,
+
+0x60,0x87,
+0x61,0x98,
+0x62,0x88,
+0x63,0x96,
+0x64,0x88,
+0x65,0x96,
+0x67,0x3f,
+0x68,0x3f,
+0x69,0x3f,
+
+0x72,0x89,
+0x73,0x95,
+0x74,0x89,
+0x75,0x95,
+0x7C,0x84,
+0x7D,0xaf,
+
+0x80,0x01,
+0x81,0x7a,
+0x82,0x13,
+0x83,0x24,
+0x84,0x78,
+0x85,0x7c,
+
+0x92,0x44,
+0x93,0x59,
+0x94,0x78,
+0x95,0x7c,
+
+0xA0,0x02,
+0xA1,0x74,
+0xA4,0x74,
+0xA5,0x02,
+0xA8,0x85,
+0xA9,0x8c,
+0xAC,0x10,
+0xAD,0x16,
+
+0xB0,0x99,
+0xB1,0xa3,
+0xB4,0x9b,
+0xB5,0xa2,
+0xB8,0x9b,
+0xB9,0x9f,
+0xBC,0x9b,
+0xBD,0x9f,
+
+0xc4,0x29,
+0xc5,0x40,
+0xc6,0x5c,
+0xc7,0x72,
+0xc8,0x2a,
+0xc9,0x3f,
+0xcc,0x5d,
+0xcd,0x71,
+
+0xd0,0x10,
+0xd1,0x14,
+0xd2,0x20,
+0xd3,0x00,
+0xd4,0x0c, //DCDC_TIME_TH_ON
+0xd5,0x0c, //DCDC_TIME_TH_OFF
+0xd6,0x58, //DCDC_AG_TH_ON
+0xd7,0x50, //DCDC_AG_TH_OFF
+0xdc,0x00,
+0xdd,0xa3,
+0xde,0x00,
+0xdf,0x84,
+
+0xe0,0xa4,
+0xe1,0xa4,
+0xe2,0xa4,
+0xe3,0xa4,
+0xe4,0xa4,
+0xe5,0x01,
+0xe8,0x00,
+0xe9,0x00,
+0xea,0x77,
+
+0xF0,0x00,
+0xF1,0x00,
+0xF2,0x00,
+
+///// PAGE 2 END /////
+
+///// PAGE 10 START /////
+0x03,0x10, //page 10
+0x10,0x03, //S2D enable _ YUYV Order o??
+0x11,0x03,
+0x12,0xf0,
+0x13,0x01,
+
+0x20,0x00,
+0x21,0x40,
+0x22,0x0f,
+0x24,0x20,
+0x25,0x10,
+0x26,0x01,
+0x27,0x02,
+0x28,0x11,
+
+0x40,0x00,
+0x41,0x00, //D-YOffset Th
+0x42,0x04, //Cb Offset
+0x43,0x04, //Cr Offset
+0x44,0x80,
+0x45,0x80,
+0x46,0xf0,
+0x48,0x80,
+0x4a,0x80,
+
+0x50,0xa0, //D-YOffset AG
+
+0x60,0x0f,
+0x61,0xc0, //Sat B
+0x62,0xc0, //Sat R
+0x63,0x58, //Auto-De Color
+
+0x66,0x42,
+0x67,0x22,
+
+0x6a,0x7c, //White Protection Offset Dark/Indoor
+0x74,0x0c, //White Protection Offset Outdoor
+0x76,0x01, //White Protection Enable
+///// PAGE 10 END /////
+
+///// PAGE 11 START /////
+0x03,0x11, //page 11
+
+//LPF Auto Control
+0x20,0x00,
+0x21,0x00,
+0x26,0x5a, // Double_AG
+0x27,0x58, // Double_AG
+0x28,0x0f,
+0x29,0x10,
+0x2b,0x30,
+0x2c,0x32,
+
+//GBGR
+0x70,0x2b,
+0x74,0x30,
+0x75,0x18,
+0x76,0x30,
+0x77,0xff,
+0x78,0xa0,
+0x79,0xff, //Dark GbGr Th
+0x7a,0x30,
+0x7b,0x20,
+0x7c,0xf4, //Dark Dy Th B[7:4]
+///// PAGE 11 END /////
+
+///// PAGE 12 START /////
+0x03,0x12, //page 11
+
+//YC2D
+0x10,0x03, //Y DPC Enable
+0x11,0x08, //
+0x12,0x10, //0x30 -> 0x10
+0x20,0x53, //Y_lpf_enable
+0x21,0x03, //C_lpf_enable_on
+0x22,0xe6, //YC2D_CrCbY_Dy
+
+0x23,0x14, //Outdoor Dy Th
+0x24,0x20, //Indoor Dy Th // For reso Limit 0x20
+0x25,0x30, //Dark Dy Th
+
+//Outdoor LPF Flat
+0x30,0xff, //Y Hi Th
+0x31,0x00, //Y Lo Th
+0x32,0xf0, //Std Hi Th //Reso Improve Th Low //50
+0x33,0x00, //Std Lo Th
+0x34,0x00, //Median ratio
+
+//Indoor LPF Flat
+0x35,0xff, //Y Hi Th
+0x36,0x00, //Y Lo Th
+0x37,0xff, //Std Hi Th //Reso Improve Th Low //50
+0x38,0x00, //Std Lo Th
+0x39,0x00, //Median ratio
+
+//Dark LPF Flat
+0x3a,0xff, //Y Hi Th
+0x3b,0x00, //Y Lo Th
+0x3c,0x93, //Std Hi Th //Reso Improve Th Low //50
+0x3d,0x00, //Std Lo Th
+0x3e,0x00, //Median ratio
+
+//Outdoor Cindition
+0x46,0xa0, //Out Lum Hi
+0x47,0x40, //Out Lum Lo
+
+//Indoor Cindition
+0x4c,0xb0, //Indoor Lum Hi
+0x4d,0x40, //Indoor Lum Lo
+
+//Dark Cindition
+0x52,0xb0, //Dark Lum Hi
+0x53,0x50, //Dark Lum Lo
+
+//C-Filter
+0x70,0x10, //Outdoor(2:1) AWM Th Horizontal
+0x71,0x0a, //Outdoor(2:1) Diff Th Vertical
+0x72,0x10, //Indoor,Dark1 AWM Th Horizontal
+0x73,0x0a, //Indoor,Dark1 Diff Th Vertical
+0x74,0x18, //Dark(2:3) AWM Th Horizontal
+0x75,0x0f, //Dark(2:3) Diff Th Vertical
+
+//DPC
+0x90,0x7d,
+0x91,0x34,
+0x99,0x28,
+0x9c,0x14,
+0x9d,0x15,
+0x9e,0x28,
+0x9f,0x28,
+0xb0,0x0e, //Zipper noise Detault change (0x75->0x0e)
+0xb8,0x44,
+0xb9,0x15,
+///// PAGE 12 END /////
+
+///// PAGE 13 START /////
+0x03,0x13, //page 13
+
+0x80,0xfd, //Sharp2D enable _ YUYV Order o??
+0x81,0x07, //Sharp2D Clip/Limit
+0x82,0x73, //Sharp2D Filter
+0x83,0x00, //Sharp2D Low Clip
+0x85,0x00,
+
+0x92,0x33, //Sharp2D Slop n/p
+0x93,0x30, //Sharp2D LClip
+0x94,0x02, //Sharp2D HiClip1 Th
+0x95,0xf0, //Sharp2D HiClip2 Th
+0x96,0x1e, //Sharp2D HiClip2 Resolution
+0x97,0x40,
+0x98,0x80,
+0x99,0x40,
+
+//Sharp Lclp
+0xa2,0x04, //Outdoor Lclip_N
+0xa3,0x05, //Outdoor Lclip_P
+0xa4,0x06, //Indoor Lclip_N 0x03 For reso Limit 0x0e
+0xa5,0x07, //Indoor Lclip_P 0x0f For reso Limit 0x0f
+0xa6,0x80, //Dark Lclip_N
+0xa7,0x80, //Dark Lclip_P
+
+//Outdoor Slope
+0xb6,0x28, //Lum negative Hi
+0xb7,0x20, //Lum negative middle
+0xb8,0x24, //Lum negative Low
+0xb9,0x28, //Lum postive Hi
+0xba,0x20, //Lum postive middle
+0xbb,0x24, //Lum postive Low
+
+//Indoor Slope
+0xbc,0x20, //Lum negative Hi
+0xbd,0x20, //Lum negative middle
+0xbe,0x20, //Lum negative Low
+0xbf,0x20, //Lum postive Hi
+0xc0,0x20, //Lum postive middle
+0xc1,0x20, //Lum postive Low
+
+//Dark Slope
+0xc2,0x14, //Lum negative Hi
+0xc3,0x24, //Lum negative middle
+0xc4,0x1d, //Lum negative Low
+0xc5,0x14, //Lum postive Hi
+0xc6,0x24, //Lum postive middle
+0xc7,0x1d, //Lum postive Low
+///// PAGE 13 END /////
+
+///// PAGE 14 START /////
+0x03,0x14, //page 14
+0x10,0x01,
+
+0x20,0xA0,//X-Center
+0x21,0x80,//Y-Center
+
+0x22,0x12, //LSC R 1b->15 20130125
+0x23,0x0f, //LSC G
+0x24,0x0f, //LSC B
+
+0x25,0xf0, //LSC Off
+0x26,0xf0, //LSC On
+///// PAGE 14 END /////
+
+/////// PAGE 15 START ///////
+0x03,0x15, //page 15
+0x10,0x21,
+0x14,0x44,
+0x15,0x34,
+0x16,0x26,
+0x17,0x2f,
+
+//CMC
+0x30,0xdd,
+0x31,0x68,
+0x32,0x0b,
+0x33,0x2c,
+0x34,0xce,
+0x35,0x22,
+0x36,0x0e,
+0x37,0x42,
+0x38,0xd0,
+
+//CMC OFS
+0x40,0x90,
+0x41,0x16,
+0x42,0x86,
+0x43,0x01,
+0x44,0x10,
+0x45,0x91,
+0x46,0x82,
+0x47,0x9c,
+0x48,0x1d,
+
+//CMC POFS
+0x50,0x0d,
+0x51,0x91,
+0x52,0x04,
+0x53,0x07,
+0x54,0x19,
+0x55,0xa0,
+0x56,0x00,
+0x57,0x98,
+0x58,0x18,
+///// PAGE 15 END /////
+
+///// PAGE 16 START /////
+0x03,0x16, //page 16
+0x10,0x31,
+0x18,0x5a,// Double_AG 5e->37
+0x19,0x58,// Double_AG 5e->36
+0x1a,0x0e,
+0x1b,0x01,
+0x1c,0xdc,
+0x1d,0xfe,
+
+//Indoor
+0x30,0x00,
+0x31,0x06,
+0x32,0x12,
+0x33,0x29,
+0x34,0x4d,
+0x35,0x6c,
+0x36,0x81,
+0x37,0x94,
+0x38,0xa4,
+0x39,0xb3,
+0x3a,0xc0,
+0x3b,0xcb,
+0x3c,0xd5,
+0x3d,0xde,
+0x3e,0xe6,
+0x3f,0xee,
+0x40,0xf5,
+0x41,0xfc,
+0x42,0xff,
+
+
+//Outdoor
+0x50,0x00,
+0x51,0x06,
+0x52,0x12,
+0x53,0x29,
+0x54,0x45,
+0x55,0x60,
+0x56,0x7a,
+0x57,0x8e,
+0x58,0xa1,
+0x59,0xac,
+0x5a,0xb8,
+0x5b,0xc5,
+0x5c,0xd0,
+0x5d,0xda,
+0x5e,0xe3,
+0x5f,0xeb,
+0x60,0xf0,
+0x61,0xf4,
+0x62,0xf5,
+
+//Dark
+0x70,0x00,
+0x71,0x0e,
+0x72,0x17,
+0x73,0x26,
+0x74,0x3f,
+0x75,0x56,
+0x76,0x6c,
+0x77,0x81,
+0x78,0x94,
+0x79,0xa5,
+0x7a,0xb3,
+0x7b,0xc4,
+0x7c,0xce,
+0x7d,0xd9,
+0x7e,0xe4,
+0x7f,0xec,
+0x80,0xf5,
+0x81,0xfc,
+0x82,0xff,
+///// PAGE 16 END /////
+
+///// PAGE 17 START /////
+0x03,0x17, //page 17
+0xc1,0x00,
+0xc4,0x4b,
+0xc5,0x3f,
+0xc6,0x02,
+0xc7,0x20,
+///// PAGE 17 END /////
+
+///// PAGE 18 START /////
+0x03,0x18, //page 18
+0x14,0x00, //Cubic Off
+///// PAGE 18 END /////
+
+///// PAGE 19 START /////
+0x03,0x19, //Page 0x18
+0x10,0x7f, //mcmc_ctl1 MCMC Enable B:[0]
+0x11,0x7f, //mcmc_ctl2
+0x12,0x1b, //mcmc_delta1
+0x13,0xac, //mcmc_center1
+0x14,0x1e, //mcmc_delta2
+0x15,0x6e, //mcmc_center2
+0x16,0x0a, //mcmc_delta3
+0x17,0xb8, //mcmc_center3
+0x18,0x1e, //mcmc_delta4
+0x19,0xe6, //mcmc_center4
+0x1a,0x9e, //mcmc_delta5
+0x1b,0x22, //mcmc_center5
+0x1c,0x9e, //mcmc_delta6
+0x1d,0x5e, //mcmc_center6
+0x1e,0x70,//mcmc_sat_gain1
+0x1f,0x37, //mcmc_sat_gain2
+0x20,0x21, //mcmc_sat_gain3
+0x21,0x43, //mcmc_sat_gain4
+0x22,0x2f, //mcmc_sat_gain5
+0x23,0x37, //mcmc_sat_gain6
+0x24,0x00, //mcmc_hue_angle1
+0x25,0x07, //mcmc_hue_angle2
+0x26,0x09, //mcmc_hue_angle3
+0x27,0x04, //mcmc_hue_angle4
+0x28,0x00, //mcmc_hue_angle5
+0x29,0x8c, //mcmc_hue_angle6
+
+0x53,0x10, //mcmc_ctl3
+
+0x6c,0xff, //mcmc_lum_ctl1 sat hue offset
+0x6d,0x3f, //mcmc_lum_ctl2 gain
+0x6e,0x00, //mcmc_lum_ctl3 hue
+0x6f,0x00, //mcmc_lum_ctl4 rgb offset
+0x70,0x00, //mcmc_lum_ctl5 rgb scale
+
+0x71,0x3f, //mcmc_lum_gain_wgt_th1 R1 magenta
+0x72,0x3f, //mcmc_lum_gain_wgt_th2 R1
+0x73,0x3f, //mcmc_lum_gain_wgt_th3 R1
+0x74,0x3f, //mcmc_lum_gain_wgt_th4 R1
+0x75,0x30, //mcmc_rg1_lum_sp1 R1
+0x76,0x50, //mcmc_rg1_lum_sp2 R1
+0x77,0x80, //mcmc_rg1_lum_sp3 R1
+0x78,0xb0, //mcmc_rg1_lum_sp4 R1
+
+0x79,0x3f, //mcmc_lum_gain_wgt_th1 R2 bright green
+0x7a,0x3f, //mcmc_lum_gain_wgt_th2 R2
+0x7b,0x3f, //mcmc_lum_gain_wgt_th3 R2
+0x7c,0x3f, //mcmc_lum_gain_wgt_th4 R2
+0x7d,0x28, //mcmc_rg2_lum_sp1 R2
+0x7e,0x50, //mcmc_rg2_lum_sp2 R2
+0x7f,0x80, //mcmc_rg2_lum_sp3 R2
+0x80,0xb0, //mcmc_rg2_lum_sp4 R2
+
+0x81,0x28, //mcmc_lum_gain_wgt_th1 R3 dark green
+0x82,0x3f, //mcmc_lum_gain_wgt_th2 R3
+0x83,0x3f, //mcmc_lum_gain_wgt_th3 R3
+0x84,0x3f, //mcmc_lum_gain_wgt_th4 R3
+0x85,0x28, //mcmc_rg3_lum_sp1 R3
+0x86,0x50, //mcmc_rg3_lum_sp2 R3
+0x87,0x80, //mcmc_rg3_lum_sp3 R3
+0x88,0xb0, //mcmc_rg3_lum_sp4 R3
+
+0x89,0x1a, //mcmc_lum_gain_wgt_th1 R4 skin
+0x8a,0x28, //mcmc_lum_gain_wgt_th2 R4
+0x8b,0x3f, //mcmc_lum_gain_wgt_th3 R4
+0x8c,0x3f, //mcmc_lum_gain_wgt_th4 R4
+0x8d,0x10, //mcmc_rg3_lum_sp1 R4
+0x8e,0x30, //mcmc_rg3_lum_sp2 R4
+0x8f,0x60, //mcmc_rg3_lum_sp3 R4
+0x90,0x90, //mcmc_rg3_lum_sp4 R4
+
+0x91,0x1a, //mcmc_rg5_gain_wgt_th1 R5 cyan
+0x92,0x28, //mcmc_rg5_gain_wgt_th2 R5
+0x93,0x3f, //mcmc_rg5_gain_wgt_th3 R5
+0x94,0x3f, //mcmc_rg5_gain_wgt_th4 R5
+0x95,0x28, //mcmc_rg5_lum_sp1 R5
+0x96,0x50, //mcmc_rg5_lum_sp2 R5
+0x97,0x80, //mcmc_rg5_lum_sp3 R5
+0x98,0xb0, //mcmc_rg5_lum_sp4 R5
+
+0x99,0x1a, //mcmc_rg6_gain_wgt_th1 R6 blue
+0x9a,0x28, //mcmc_rg6_gain_wgt_th2 R6
+0x9b,0x3f, //mcmc_rg6_gain_wgt_th3 R6
+0x9c,0x3f, //mcmc_rg6_gain_wgt_th4 R6
+0x9d,0x28, //mcmc_rg6_lum_sp1 R6
+0x9e,0x50, //mcmc_rg6_lum_sp2 R6
+0x9f,0x80, //mcmc_rg6_lum_sp3 R6
+0xa0,0xb0, //mcmc_rg6_lum_sp4 R6
+
+0xa2,0x00,
+0xe5,0x80, //add 20120709 Bit[7] On MCMC --> YC2D_LPF
+
+/////// PAGE 20 START ///////
+0x03,0x20,
+0x10,0x1c,
+0x11,0x14,
+0x18,0x30,
+0x20,0x25, //8x8 Ae weight 0~7 Outdoor / Weight Outdoor On B[5]
+0x21,0x30,
+0x22,0x10,
+0x23,0x00,
+
+0x28,0xe7,
+0x29,0x0d,
+0x2a,0xff,
+0x2b,0x04, //Adaptive Off,1/100 Flicker
+
+0x2c,0x83, //AE After CI
+0x2d,0x03,
+0x2e,0x13,
+0x2f,0x0b,
+
+0x30,0x78,
+0x31,0xd7,
+0x32,0x10,
+0x33,0x2e,
+0x34,0x20,
+0x35,0xd4,
+0x36,0xfe,
+0x37,0x32,
+0x38,0x04,
+0x39,0x22,
+0x3a,0xde,
+0x3b,0x22,
+0x3c,0xde,
+0x3d,0xe1,
+
+0x50,0x45,
+0x51,0x88,
+
+0x56,0x1f, // for tracking
+0x57,0xa6, // for tracking
+0x58,0x1a, // for tracking
+0x59,0x7a, // for tracking
+
+0x5a,0x04,
+0x5b,0x04,
+
+0x5e,0xc7,
+0x5f,0x95,
+
+0x62,0x10,
+0x63,0xc0,
+0x64,0x10,
+0x65,0x8a,
+0x66,0x58,
+0x67,0x58,
+
+0x70,0x54, //6c
+0x71,0x80, //81(+4),89(-4)
+
+0x76,0x32,
+0x77,0xb1,
+0x78,0x22, //24
+0x79,0x32, // Y Target 70 => 25,72 => 26 //
+0x7a,0x23, //23
+0x7b,0x22, //22
+0x7d,0x23,
+
+0x83,0x02, //EXP Normal 33.33 fps
+0x84,0xbf,
+0x85,0x20,
+
+0x86,0x01, //EXPMin 12000.00 fps
+0x87,0xf4,
+
+0x88,0x0b, //EXP Max(120Hz) 8.00 fps
+0x89,0x71,
+0x8a,0xb0,
+
+0xa5,0x0b, //EXP Max(100Hz) 7.69 fps
+0xa6,0xe6,
+0xa7,0xe0,
+
+0x8B,0xea, //EXP100
+0x8C,0x60,
+
+0x8D,0xc3, //EXP120
+0x8E,0x50,
+
+0x9c,0x0f, //EXP Limit 1500.00 fps
+0x9d,0xa0,
+0x9e,0x01, //EXP Unit
+0x9f,0xf4,
+
+0xa3,0x00,
+0xa4,0xfd, //Exp 12000
+
+0xb0,0x15,
+0xb1,0x14,
+0xb2,0x60,
+0xb3,0x1a,
+0xb4,0x1c,
+0xb5,0x48,
+0xb6,0x32,
+0xb7,0x2b,
+0xb8,0x27,
+0xb9,0x25,
+0xba,0x23,
+0xbb,0x22,
+0xbc,0x22,
+0xbd,0x21,
+
+0xc0,0x10,
+0xc1,0x40,
+0xc2,0x40,
+0xc3,0x40,
+0xc4,0x06,
+
+0xc8,0x80,
+0xc9,0x80,
+///// PAGE 20 END /////
+
+///// PAGE 21 START /////
+0x03,0x21, //page 21
+
+//Indoor Weight
+0x20,0x11,
+0x21,0x11,
+0x22,0x11,
+0x23,0x11,
+0x24,0x14,
+0x25,0x44,
+0x26,0x44,
+0x27,0x41,
+0x28,0x14,
+0x29,0x44,
+0x2a,0x44,
+0x2b,0x41,
+0x2c,0x14,
+0x2d,0x47,
+0x2e,0x74,
+0x2f,0x41,
+0x30,0x14,
+0x31,0x47,
+0x32,0x74,
+0x33,0x41,
+0x34,0x14,
+0x35,0x44,
+0x36,0x44,
+0x37,0x41,
+0x38,0x14,
+0x39,0x44,
+0x3a,0x44,
+0x3b,0x41,
+0x3c,0x11,
+0x3d,0x11,
+0x3e,0x11,
+0x3f,0x11,
+
+//Outdoor Weight
+0x40,0x11,
+0x41,0x11,
+0x42,0x11,
+0x43,0x11,
+0x44,0x14,
+0x45,0x44,
+0x46,0x44,
+0x47,0x41,
+0x48,0x14,
+0x49,0x44,
+0x4a,0x44,
+0x4b,0x41,
+0x4c,0x14,
+0x4d,0x47,
+0x4e,0x74,
+0x4f,0x41,
+0x50,0x14,
+0x51,0x47,
+0x52,0x74,
+0x53,0x41,
+0x54,0x14,
+0x55,0x44,
+0x56,0x44,
+0x57,0x41,
+0x58,0x14,
+0x59,0x44,
+0x5a,0x44,
+0x5b,0x41,
+0x5c,0x11,
+0x5d,0x11,
+0x5e,0x11,
+0x5f,0x11,
+
+
+///// PAGE 22 START /////
+0x03,0x22, //page 22
+0x10,0xfd,
+0x11,0x2e,
+0x19,0x00, //Low On //
+0x20,0x30, //For AWB Speed
+0x21,0x80,
+0x22,0x00,
+0x23,0x00,
+0x24,0x01,
+0x25,0x7e, //for Tracking setting
+
+0x30,0x80,
+0x31,0x80,
+0x38,0x11,
+0x39,0x34,
+0x40,0xe4, //Stb Yth
+0x41,0x33, //Stb cdiff
+0x42,0x22, //Stb csum
+0x43,0xf3, //Unstb Yth
+0x44,0x55, //Unstb cdiff
+0x45,0x33, //Unstb csum
+0x46,0x00,
+0x47,0xa2,
+0x48,0x02,
+0x49,0x0a,
+
+0x60,0x04,
+0x61,0xc4,
+0x62,0x04,
+0x63,0x92,
+0x66,0x04,
+0x67,0xc4,
+0x68,0x04,
+0x69,0x92,
+
+0x80,0x38,
+0x81,0x20,
+0x82,0x38,
+
+0x83,0x58,
+0x84,0x16,
+0x85,0x4f,
+0x86,0x1a,
+
+0x87,0x42,
+0x88,0x30,
+0x89,0x27,
+0x8a,0x18,
+
+0x8b,0x3d,
+0x8c,0x32,
+0x8d,0x24,
+0x8e,0x1d,
+
+0x8f,0x4d, //4e
+0x90,0x46, //4d
+0x91,0x40, //4c
+0x92,0x3a, //4a
+0x93,0x2f, //46
+0x94,0x21,
+0x95,0x19,
+0x96,0x16,
+0x97,0x13,
+0x98,0x12,
+0x99,0x11,
+0x9a,0x10,
+
+0x9b,0xbb,
+0x9c,0xaa,
+0x9d,0x48,
+0x9e,0x38,
+0x9f,0x30,
+
+0xa0,0x70,
+0xa1,0x54,
+0xa2,0x6f,
+0xa3,0xff,
+
+0xa4,0x14, //1536fps
+0xa5,0x2c, //698fps
+0xa6,0xcf, //148fps
+
+0xad,0x40,
+0xae,0x4a,
+
+0xaf,0x28, //Low temp Rgain
+0xb0,0x26, //Low temp Rgain
+
+0xb1,0x00,
+0xb4,0xbf, //For Tracking AWB Weight
+0xb8,0x91, //(0+,1-)High Cb ,(0+,1-)Low Cr
+0xb9,0x00,
+/////// PAGE 22 END ///////
+
+/////// PAGE 20 ///////
+0x03,0x20,
+0x10,0x9c, //AE On 50hz
+
+/////// PAGE 22 ///////
+0x03,0x22,
+0x10,0xe9, //AWB On
+
+/////// PAGE 0 ///////
+0x03,0x00,
+0x0e,0x03,
+0x0e,0x73,
+
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+
+0x03,0x18,
+0x12,0x20,
+0x10,0x07,
+0x11,0x00,
+0x20,0x02,
+0x21,0x80,
+0x22,0x03,
+0x23,0xc0,
+0x24,0x00,
+0x25,0x00,
+0x26,0x00,
+0x27,0x00,
+0x28,0x02,
+0x29,0x80,
+0x2a,0x03,
+0x2b,0xc0,
+0x2c,0x0a,
+0x2d,0x00,
+0x2e,0x0a,
+0x2f,0x00,
+0x30,0x24,
+
+0x03,0x00,
+0x01,0x00,
+
+};
+
+#endif
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/hi257/hi257_kyt162_v7.h b/drivers/media/video/wmt_v4l2/sensors/hi257/hi257_kyt162_v7.h
new file mode 100755
index 00000000..a64be87a
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/hi257/hi257_kyt162_v7.h
@@ -0,0 +1,2195 @@
+#ifndef HI257_H
+#define HI257_H
+
+// Scene Mode
+uint8_t hi257_scene_mode_auto[] = {
+ 0x03,0x10,
+ 0x41,0x00,
+ 0x03,0x20,
+ 0xb2,0x60,
+ 0xff,0xff,
+};
+
+uint8_t hi257_scene_mode_night[] = {
+ 0x03,0x10,
+ 0x41,0x15,
+ 0x03,0x20,
+ 0xb2,0x80,
+ 0xff,0xff,
+};
+
+
+// White Balance
+uint8_t hi257_wb_auto [] = {
+ 0x03,0x22,
+ 0x11,0x2e,
+ 0x83,0x58, // RMAX
+ 0x84,0x16, // RMIN
+ 0x85,0x4f, // BMAX
+ 0x86,0x1a, // BMIN
+ 0xff,0xff,
+};
+
+uint8_t hi257_wb_incandescent [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x2a,
+ 0x82,0x3f,
+ 0x83,0x35,
+ 0x84,0x28,
+ 0x85,0x45,
+ 0x86,0x3b,
+ 0xff,0xff,
+};
+
+uint8_t hi257_wb_fluorescent [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x20,
+ 0x82,0x4d,
+ 0x83,0x25,
+ 0x84,0x1b,
+ 0x85,0x55,
+ 0x86,0x48,
+ 0xff,0xff,
+};
+
+uint8_t hi257_wb_daylight [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x3d,
+ 0x82,0x2e,
+ 0x83,0x40,
+ 0x84,0x33,
+ 0x85,0x33,
+ 0x86,0x28,
+ 0xff,0xff,
+};
+
+uint8_t hi257_wb_cloudy [] = {
+ 0x03,0x22,
+ 0x11,0x28,
+ 0x80,0x50,
+ 0x82,0x25,
+ 0x83,0x55,
+ 0x84,0x4b,
+ 0x85,0x28,
+ 0x86,0x20,
+ 0xff,0xff,
+};
+
+
+// Exposure
+uint8_t hi257_exposure_neg6[] = {
+ 0x03,0x10,
+ 0x40,0xa8,
+};
+
+uint8_t hi257_exposure_neg3[] = {
+ 0x03,0x10,
+ 0x40,0x98,
+};
+
+uint8_t hi257_exposure_zero[] = {
+ 0x03,0x10,
+ 0x40,0x80,
+};
+
+uint8_t hi257_exposure_pos3[] = {
+ 0x03,0x10,
+ 0x40,0x08,
+};
+
+uint8_t hi257_exposure_pos6[] = {
+ 0x03,0x10,
+ 0x40,0x28,
+};
+
+
+// Resolution
+uint8_t hi257_1600_1200_rets[]={
+ 0x03, 0x00,
+ 0x10, 0x00,
+ 0x12, 0x04,
+ 0x20, 0x00,
+ 0x21, 0x0a,
+ 0x22, 0x00,
+ 0x23, 0x0a,
+
+ 0x03, 0x18,
+ 0x10, 0x00,
+ 0xff, 0xff,
+};
+
+uint8_t hi257_1280_720_regs[]={
+};
+
+uint8_t hi257_800_600_regs[]={
+
+ 0x03, 0x00,
+ 0x10, 0x10,
+ 0x12, 0x04,
+
+ 0x03, 0x18,
+ 0x10, 0x00,
+ 0xff, 0xff,
+};
+
+uint8_t hi257_640_480_regs[]={
+ 0x03, 0x00,
+ 0x10, 0x10,
+ 0x12, 0x04,
+
+ 0x03, 0x18,
+ 0x12, 0x20,
+ 0x10, 0x07,
+ 0x11, 0x00,
+ 0x20, 0x05,
+ 0x21, 0x00,
+ 0x22, 0x03,
+ 0x23, 0xc0,
+ 0x24, 0x00,
+ 0x25, 0x00,
+ 0x26, 0x00,
+ 0x27, 0x00,
+ 0x28, 0x05,
+ 0x29, 0x00,
+ 0x2a, 0x03,
+ 0x2b, 0xc0,
+ 0x2c, 0x0a,
+ 0x2d, 0x00,
+ 0x2e, 0x0a,
+ 0x2f, 0x00,
+ 0x30, 0x44,
+ 0xff, 0xff,
+};
+
+uint8_t hi257_320_240_regs[]= {
+ 0x01,0x01, //sleep on
+0x01,0x03, //sleep off
+0x01,0x01, //sleep on
+
+0x03,0x00, //Dummy 750us
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+
+0x08,0x00,
+0x09,0x77, // pad strength = max
+0x0a,0x07, // pad strength = max
+
+0x0e,0x03, //PLL On
+0x0e,0x73, //PLLx2
+
+0x03,0x00, //Dummy 750us
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+
+
+///// PAGE 20 /////
+0x03,0x20, //page 20
+0x10,0x1c, //AE off 50hz
+
+///// PAGE 22 /////
+0x03,0x22, //page 22
+0x10,0x69, //AWB off
+
+
+///// Initial Start /////
+///// PAGE 0 Start /////
+0x03,0x00, //page 0
+0x10,0x91,
+0x11,0x90, //Windowing On + 1Frame Skip
+0x12,0x04, //Rinsing edge 0x04 // Falling edge 0x00
+0x14,0x05,
+
+0x20,0x00, //Row H
+0x21,0x02, //Row L
+0x22,0x00, //Col H
+0x23,0x04, //Col L
+
+0x24,0x04, //Window height_H //= 1200
+0x25,0xb0, //Window height_L //
+0x26,0x06, //Window width_H //= 1600
+0x27,0x40, //Window wight_L
+
+0x40,0x01, //Hblank_360
+0x41,0x78,
+0x42,0x00, //Vblank
+0x43,0x14, //Flick Stop
+
+0x50,0x00, //Test Pattern
+
+///// BLC /////
+0x80,0x2e,
+0x81,0x7e,
+0x82,0x90,
+0x83,0x00,
+0x84,0xcc, //20130604 0x0c->0xcc
+0x85,0x00,
+0x86,0x00,
+0x87,0x0f,
+0x88,0x34,
+0x8a,0x0b,
+0x8e,0x80, //Pga Blc Hold
+
+0x90,0x0c, //BLC_TIME_TH_ON
+0x91,0x0c, //BLC_TIME_TH_OFF
+0x92,0x58, //BLC_AG_TH_ON
+0x93,0x50, //BLC_AG_TH_OFF
+0x96,0xdc, //BLC Outdoor Th On
+0x97,0xfe, //BLC Outdoor Th Off
+0x98,0x38,
+
+//OutDoor BLC
+0x99,0x43, //R,Gr,B,Gb Offset
+
+//Dark BLC
+0xa0,0x02, //R,Gr,B,Gb Offset
+
+//Normal BLC
+0xa8,0x43, //R,Gr,B,Gb Offset
+///// PAGE 0 END /////
+
+///// PAGE 2 START /////
+0x03,0x02,
+0x10,0x00,
+0x13,0x00,
+0x14,0x00,
+0x18,0xcc,
+0x19,0x01, // pmos switch on (for cfpn)
+0x1A,0x39, //20130604 0x09->0xcc
+0x1B,0x00,
+0x1C,0x1a, // for ncp
+0x1D,0x14, // for ncp
+0x1E,0x30, // for ncp
+0x1F,0x10,
+
+0x20,0x77,
+0x21,0xde,
+0x22,0xa7,
+0x23,0x30,
+0x24,0x77,
+0x25,0x10,
+0x26,0x10,
+0x27,0x3c,
+0x2b,0x80,
+0x2c,0x02,
+0x2d,0x58,
+0x2e,0x11,//20130604 0xde->0x11
+0x2f,0x11,//20130604 0xa7->0x11
+
+0x30,0x00,
+0x31,0x99,
+0x32,0x00,
+0x33,0x00,
+0x34,0x22,
+0x36,0x75,
+0x38,0x88,
+0x39,0x88,
+0x3d,0x03,
+0x3f,0x02,
+
+0x49,0xc1,//20130604 0x87->0xd1 --> mode Change Issue modify -> 0xc1
+0x4a,0x10,
+
+0x50,0x21,
+0x53,0xb1,
+0x54,0x10,
+0x55,0x1c, // for ncp
+0x56,0x11,
+0x58,0x3a,//20130604 add
+0x59,0x38,//20130604 add
+0x5d,0xa2,
+0x5e,0x5a,
+
+0x60,0x87,
+0x61,0x98,
+0x62,0x88,
+0x63,0x96,
+0x64,0x88,
+0x65,0x96,
+0x67,0x3f,
+0x68,0x3f,
+0x69,0x3f,
+
+0x72,0x89,
+0x73,0x95,
+0x74,0x89,
+0x75,0x95,
+0x7C,0x84,
+0x7D,0xaf,
+
+0x80,0x01,
+0x81,0x7a,
+0x82,0x13,
+0x83,0x24,
+0x84,0x78,
+0x85,0x7c,
+
+0x92,0x44,
+0x93,0x59,
+0x94,0x78,
+0x95,0x7c,
+
+0xA0,0x02,
+0xA1,0x74,
+0xA4,0x74,
+0xA5,0x02,
+0xA8,0x85,
+0xA9,0x8c,
+0xAC,0x10,
+0xAD,0x16,
+
+0xB0,0x99,
+0xB1,0xa3,
+0xB4,0x9b,
+0xB5,0xa2,
+0xB8,0x9b,
+0xB9,0x9f,
+0xBC,0x9b,
+0xBD,0x9f,
+
+0xc4,0x29,
+0xc5,0x40,
+0xc6,0x5c,
+0xc7,0x72,
+0xc8,0x2a,
+0xc9,0x3f,
+0xcc,0x5d,
+0xcd,0x71,
+
+0xd0,0x10,
+0xd1,0x14,
+0xd2,0x20,
+0xd3,0x00,
+0xd4,0x0c, //DCDC_TIME_TH_ON
+0xd5,0x0c, //DCDC_TIME_TH_OFF
+0xd6,0x58, //DCDC_AG_TH_ON
+0xd7,0x50, //DCDC_AG_TH_OFF
+0xdc,0x00,
+0xdd,0xa3,
+0xde,0x00,
+0xdf,0x84,
+
+0xe0,0xa4,
+0xe1,0xa4,
+0xe2,0xa4,
+0xe3,0xa4,
+0xe4,0xa4,
+0xe5,0x01,
+0xe8,0x00,
+0xe9,0x00,
+0xea,0x77,
+
+0xF0,0x00,
+0xF1,0x00,
+0xF2,0x00,
+
+///// PAGE 2 END /////
+
+///// PAGE 10 START /////
+0x03,0x10, //page 10
+0x10,0x03, //S2D enable _ YUYV Order o??
+0x11,0x03,
+0x12,0xf0,
+0x13,0x01,
+
+0x20,0x00,
+0x21,0x40,
+0x22,0x0f,
+0x24,0x20,
+0x25,0x10,
+0x26,0x01,
+0x27,0x02,
+0x28,0x11,
+
+0x40,0x00,
+0x41,0x00, //D-YOffset Th
+0x42,0x04, //Cb Offset
+0x43,0x04, //Cr Offset
+0x44,0x80,
+0x45,0x80,
+0x46,0xf0,
+0x48,0x80,
+0x4a,0x80,
+
+0x50,0xa0, //D-YOffset AG
+
+0x60,0x0f,
+0x61,0xc0, //Sat B
+0x62,0xc0, //Sat R
+0x63,0x58, //Auto-De Color
+
+0x66,0x42,
+0x67,0x22,
+
+0x6a,0x7c, //White Protection Offset Dark/Indoor
+0x74,0x0c, //White Protection Offset Outdoor
+0x76,0x01, //White Protection Enable
+///// PAGE 10 END /////
+
+///// PAGE 11 START /////
+0x03,0x11, //page 11
+
+//LPF Auto Control
+0x20,0x00,
+0x21,0x00,
+0x26,0x5a, // Double_AG
+0x27,0x58, // Double_AG
+0x28,0x0f,
+0x29,0x10,
+0x2b,0x30,
+0x2c,0x32,
+
+//GBGR
+0x70,0x2b,
+0x74,0x30,
+0x75,0x18,
+0x76,0x30,
+0x77,0xff,
+0x78,0xa0,
+0x79,0xff, //Dark GbGr Th
+0x7a,0x30,
+0x7b,0x20,
+0x7c,0xf4, //Dark Dy Th B[7:4]
+///// PAGE 11 END /////
+
+///// PAGE 12 START /////
+0x03,0x12, //page 11
+
+//YC2D
+0x10,0x03, //Y DPC Enable
+0x11,0x08, //
+0x12,0x10, //0x30 -> 0x10
+0x20,0x53, //Y_lpf_enable
+0x21,0x03, //C_lpf_enable_on
+0x22,0xe6, //YC2D_CrCbY_Dy
+
+0x23,0x14, //Outdoor Dy Th
+0x24,0x20, //Indoor Dy Th // For reso Limit 0x20
+0x25,0x30, //Dark Dy Th
+
+//Outdoor LPF Flat
+0x30,0xff, //Y Hi Th
+0x31,0x00, //Y Lo Th
+0x32,0xf0, //Std Hi Th //Reso Improve Th Low //50
+0x33,0x00, //Std Lo Th
+0x34,0x00, //Median ratio
+
+//Indoor LPF Flat
+0x35,0xff, //Y Hi Th
+0x36,0x00, //Y Lo Th
+0x37,0xff, //Std Hi Th //Reso Improve Th Low //50
+0x38,0x00, //Std Lo Th
+0x39,0x00, //Median ratio
+
+//Dark LPF Flat
+0x3a,0xff, //Y Hi Th
+0x3b,0x00, //Y Lo Th
+0x3c,0x93, //Std Hi Th //Reso Improve Th Low //50
+0x3d,0x00, //Std Lo Th
+0x3e,0x00, //Median ratio
+
+//Outdoor Cindition
+0x46,0xa0, //Out Lum Hi
+0x47,0x40, //Out Lum Lo
+
+//Indoor Cindition
+0x4c,0xb0, //Indoor Lum Hi
+0x4d,0x40, //Indoor Lum Lo
+
+//Dark Cindition
+0x52,0xb0, //Dark Lum Hi
+0x53,0x50, //Dark Lum Lo
+
+//C-Filter
+0x70,0x10, //Outdoor(2:1) AWM Th Horizontal
+0x71,0x0a, //Outdoor(2:1) Diff Th Vertical
+0x72,0x10, //Indoor,Dark1 AWM Th Horizontal
+0x73,0x0a, //Indoor,Dark1 Diff Th Vertical
+0x74,0x18, //Dark(2:3) AWM Th Horizontal
+0x75,0x0f, //Dark(2:3) Diff Th Vertical
+
+//DPC
+0x90,0x7d,
+0x91,0x34,
+0x99,0x28,
+0x9c,0x14,
+0x9d,0x15,
+0x9e,0x28,
+0x9f,0x28,
+0xb0,0x0e, //Zipper noise Detault change (0x75->0x0e)
+0xb8,0x44,
+0xb9,0x15,
+///// PAGE 12 END /////
+
+///// PAGE 13 START /////
+0x03,0x13, //page 13
+
+0x80,0xfd, //Sharp2D enable _ YUYV Order o??
+0x81,0x07, //Sharp2D Clip/Limit
+0x82,0x73, //Sharp2D Filter
+0x83,0x00, //Sharp2D Low Clip
+0x85,0x00,
+
+0x92,0x33, //Sharp2D Slop n/p
+0x93,0x30, //Sharp2D LClip
+0x94,0x02, //Sharp2D HiClip1 Th
+0x95,0xf0, //Sharp2D HiClip2 Th
+0x96,0x1e, //Sharp2D HiClip2 Resolution
+0x97,0x40,
+0x98,0x80,
+0x99,0x40,
+
+//Sharp Lclp
+0xa2,0x04, //Outdoor Lclip_N
+0xa3,0x05, //Outdoor Lclip_P
+0xa4,0x06, //Indoor Lclip_N 0x03 For reso Limit 0x0e
+0xa5,0x07, //Indoor Lclip_P 0x0f For reso Limit 0x0f
+0xa6,0x80, //Dark Lclip_N
+0xa7,0x80, //Dark Lclip_P
+
+//Outdoor Slope
+0xb6,0x28, //Lum negative Hi
+0xb7,0x20, //Lum negative middle
+0xb8,0x24, //Lum negative Low
+0xb9,0x28, //Lum postive Hi
+0xba,0x20, //Lum postive middle
+0xbb,0x24, //Lum postive Low
+
+//Indoor Slope
+0xbc,0x20, //Lum negative Hi
+0xbd,0x20, //Lum negative middle
+0xbe,0x20, //Lum negative Low
+0xbf,0x20, //Lum postive Hi
+0xc0,0x20, //Lum postive middle
+0xc1,0x20, //Lum postive Low
+
+//Dark Slope
+0xc2,0x14, //Lum negative Hi
+0xc3,0x24, //Lum negative middle
+0xc4,0x1d, //Lum negative Low
+0xc5,0x14, //Lum postive Hi
+0xc6,0x24, //Lum postive middle
+0xc7,0x1d, //Lum postive Low
+///// PAGE 13 END /////
+
+///// PAGE 14 START /////
+0x03,0x14, //page 14
+0x10,0x01,
+
+0x20,0xA0,//X-Center
+0x21,0x80,//Y-Center
+
+0x22,0x12, //LSC R 1b->15 20130125
+0x23,0x0f, //LSC G
+0x24,0x0f, //LSC B
+
+0x25,0xf0, //LSC Off
+0x26,0xf0, //LSC On
+///// PAGE 14 END /////
+
+/////// PAGE 15 START ///////
+0x03,0x15, //page 15
+0x10,0x21,
+0x14,0x44,
+0x15,0x34,
+0x16,0x26,
+0x17,0x2f,
+
+//CMC
+0x30,0xdd,
+0x31,0x68,
+0x32,0x0b,
+0x33,0x2c,
+0x34,0xce,
+0x35,0x22,
+0x36,0x0e,
+0x37,0x42,
+0x38,0xd0,
+
+//CMC OFS
+0x40,0x90,
+0x41,0x16,
+0x42,0x86,
+0x43,0x01,
+0x44,0x10,
+0x45,0x91,
+0x46,0x82,
+0x47,0x9c,
+0x48,0x1d,
+
+//CMC POFS
+0x50,0x0d,
+0x51,0x91,
+0x52,0x04,
+0x53,0x07,
+0x54,0x19,
+0x55,0xa0,
+0x56,0x00,
+0x57,0x98,
+0x58,0x18,
+///// PAGE 15 END /////
+
+///// PAGE 16 START /////
+0x03,0x16, //page 16
+0x10,0x31,
+0x18,0x5a,// Double_AG 5e->37
+0x19,0x58,// Double_AG 5e->36
+0x1a,0x0e,
+0x1b,0x01,
+0x1c,0xdc,
+0x1d,0xfe,
+
+//Indoor
+0x30,0x00,
+0x31,0x06,
+0x32,0x12,
+0x33,0x29,
+0x34,0x4d,
+0x35,0x6c,
+0x36,0x81,
+0x37,0x94,
+0x38,0xa4,
+0x39,0xb3,
+0x3a,0xc0,
+0x3b,0xcb,
+0x3c,0xd5,
+0x3d,0xde,
+0x3e,0xe6,
+0x3f,0xee,
+0x40,0xf5,
+0x41,0xfc,
+0x42,0xff,
+
+
+//Outdoor
+0x50,0x00,
+0x51,0x06,
+0x52,0x12,
+0x53,0x29,
+0x54,0x45,
+0x55,0x60,
+0x56,0x7a,
+0x57,0x8e,
+0x58,0xa1,
+0x59,0xac,
+0x5a,0xb8,
+0x5b,0xc5,
+0x5c,0xd0,
+0x5d,0xda,
+0x5e,0xe3,
+0x5f,0xeb,
+0x60,0xf0,
+0x61,0xf4,
+0x62,0xf5,
+
+//Dark
+0x70,0x00,
+0x71,0x0e,
+0x72,0x17,
+0x73,0x26,
+0x74,0x3f,
+0x75,0x56,
+0x76,0x6c,
+0x77,0x81,
+0x78,0x94,
+0x79,0xa5,
+0x7a,0xb3,
+0x7b,0xc4,
+0x7c,0xce,
+0x7d,0xd9,
+0x7e,0xe4,
+0x7f,0xec,
+0x80,0xf5,
+0x81,0xfc,
+0x82,0xff,
+///// PAGE 16 END /////
+
+///// PAGE 17 START /////
+0x03,0x17, //page 17
+0xc1,0x00,
+0xc4,0x4b,
+0xc5,0x3f,
+0xc6,0x02,
+0xc7,0x20,
+///// PAGE 17 END /////
+
+///// PAGE 18 START /////
+ //page 18
+ 0x03, 0x18,
+ 0x12, 0x20,
+ 0x10, 0x07,
+ 0x11, 0x00,
+ 0x20, 0x02,
+ 0x21, 0x80,
+ 0x22, 0x01,
+ 0x23, 0xe0,
+ 0x24, 0x00,
+ 0x25, 0x00,
+ 0x26, 0x00,
+ 0x27, 0x00,
+ 0x28, 0x02,
+ 0x29, 0x80,
+ 0x2a, 0x01,
+ 0x2b, 0xe0,
+ 0x2c, 0x14,
+ 0x2d, 0x00,
+ 0x2e, 0x14,
+ 0x2f, 0x00,
+ 0x30, 0x64,
+///// PAGE 18 END /////
+
+///// PAGE 19 START /////
+0x03,0x19, //Page 0x18
+0x10,0x7f, //mcmc_ctl1 MCMC Enable B:[0]
+0x11,0x7f, //mcmc_ctl2
+0x12,0x1b, //mcmc_delta1
+0x13,0xac, //mcmc_center1
+0x14,0x1e, //mcmc_delta2
+0x15,0x6e, //mcmc_center2
+0x16,0x0a, //mcmc_delta3
+0x17,0xb8, //mcmc_center3
+0x18,0x1e, //mcmc_delta4
+0x19,0xe6, //mcmc_center4
+0x1a,0x9e, //mcmc_delta5
+0x1b,0x22, //mcmc_center5
+0x1c,0x9e, //mcmc_delta6
+0x1d,0x5e, //mcmc_center6
+0x1e,0x70,//mcmc_sat_gain1
+0x1f,0x37, //mcmc_sat_gain2
+0x20,0x21, //mcmc_sat_gain3
+0x21,0x43, //mcmc_sat_gain4
+0x22,0x2f, //mcmc_sat_gain5
+0x23,0x37, //mcmc_sat_gain6
+0x24,0x00, //mcmc_hue_angle1
+0x25,0x07, //mcmc_hue_angle2
+0x26,0x09, //mcmc_hue_angle3
+0x27,0x04, //mcmc_hue_angle4
+0x28,0x00, //mcmc_hue_angle5
+0x29,0x8c, //mcmc_hue_angle6
+
+0x53,0x10, //mcmc_ctl3
+
+0x6c,0xff, //mcmc_lum_ctl1 sat hue offset
+0x6d,0x3f, //mcmc_lum_ctl2 gain
+0x6e,0x00, //mcmc_lum_ctl3 hue
+0x6f,0x00, //mcmc_lum_ctl4 rgb offset
+0x70,0x00, //mcmc_lum_ctl5 rgb scale
+
+0x71,0x3f, //mcmc_lum_gain_wgt_th1 R1 magenta
+0x72,0x3f, //mcmc_lum_gain_wgt_th2 R1
+0x73,0x3f, //mcmc_lum_gain_wgt_th3 R1
+0x74,0x3f, //mcmc_lum_gain_wgt_th4 R1
+0x75,0x30, //mcmc_rg1_lum_sp1 R1
+0x76,0x50, //mcmc_rg1_lum_sp2 R1
+0x77,0x80, //mcmc_rg1_lum_sp3 R1
+0x78,0xb0, //mcmc_rg1_lum_sp4 R1
+
+0x79,0x3f, //mcmc_lum_gain_wgt_th1 R2 bright green
+0x7a,0x3f, //mcmc_lum_gain_wgt_th2 R2
+0x7b,0x3f, //mcmc_lum_gain_wgt_th3 R2
+0x7c,0x3f, //mcmc_lum_gain_wgt_th4 R2
+0x7d,0x28, //mcmc_rg2_lum_sp1 R2
+0x7e,0x50, //mcmc_rg2_lum_sp2 R2
+0x7f,0x80, //mcmc_rg2_lum_sp3 R2
+0x80,0xb0, //mcmc_rg2_lum_sp4 R2
+
+0x81,0x28, //mcmc_lum_gain_wgt_th1 R3 dark green
+0x82,0x3f, //mcmc_lum_gain_wgt_th2 R3
+0x83,0x3f, //mcmc_lum_gain_wgt_th3 R3
+0x84,0x3f, //mcmc_lum_gain_wgt_th4 R3
+0x85,0x28, //mcmc_rg3_lum_sp1 R3
+0x86,0x50, //mcmc_rg3_lum_sp2 R3
+0x87,0x80, //mcmc_rg3_lum_sp3 R3
+0x88,0xb0, //mcmc_rg3_lum_sp4 R3
+
+0x89,0x1a, //mcmc_lum_gain_wgt_th1 R4 skin
+0x8a,0x28, //mcmc_lum_gain_wgt_th2 R4
+0x8b,0x3f, //mcmc_lum_gain_wgt_th3 R4
+0x8c,0x3f, //mcmc_lum_gain_wgt_th4 R4
+0x8d,0x10, //mcmc_rg3_lum_sp1 R4
+0x8e,0x30, //mcmc_rg3_lum_sp2 R4
+0x8f,0x60, //mcmc_rg3_lum_sp3 R4
+0x90,0x90, //mcmc_rg3_lum_sp4 R4
+
+0x91,0x1a, //mcmc_rg5_gain_wgt_th1 R5 cyan
+0x92,0x28, //mcmc_rg5_gain_wgt_th2 R5
+0x93,0x3f, //mcmc_rg5_gain_wgt_th3 R5
+0x94,0x3f, //mcmc_rg5_gain_wgt_th4 R5
+0x95,0x28, //mcmc_rg5_lum_sp1 R5
+0x96,0x50, //mcmc_rg5_lum_sp2 R5
+0x97,0x80, //mcmc_rg5_lum_sp3 R5
+0x98,0xb0, //mcmc_rg5_lum_sp4 R5
+
+0x99,0x1a, //mcmc_rg6_gain_wgt_th1 R6 blue
+0x9a,0x28, //mcmc_rg6_gain_wgt_th2 R6
+0x9b,0x3f, //mcmc_rg6_gain_wgt_th3 R6
+0x9c,0x3f, //mcmc_rg6_gain_wgt_th4 R6
+0x9d,0x28, //mcmc_rg6_lum_sp1 R6
+0x9e,0x50, //mcmc_rg6_lum_sp2 R6
+0x9f,0x80, //mcmc_rg6_lum_sp3 R6
+0xa0,0xb0, //mcmc_rg6_lum_sp4 R6
+
+0xa2,0x00,
+0xe5,0x80, //add 20120709 Bit[7] On MCMC --> YC2D_LPF
+
+/////// PAGE 20 START ///////
+0x03,0x20,
+0x10,0x1c,
+0x11,0x14,
+0x18,0x30,
+0x20,0x25, //8x8 Ae weight 0~7 Outdoor / Weight Outdoor On B[5]
+0x21,0x30,
+0x22,0x10,
+0x23,0x00,
+
+0x28,0xe7,
+0x29,0x0d,
+0x2a,0xff,
+0x2b,0x04, //Adaptive Off,1/100 Flicker
+
+0x2c,0x83, //AE After CI
+0x2d,0x03,
+0x2e,0x13,
+0x2f,0x0b,
+
+0x30,0x78,
+0x31,0xd7,
+0x32,0x10,
+0x33,0x2e,
+0x34,0x20,
+0x35,0xd4,
+0x36,0xfe,
+0x37,0x32,
+0x38,0x04,
+0x39,0x22,
+0x3a,0xde,
+0x3b,0x22,
+0x3c,0xde,
+0x3d,0xe1,
+
+0x50,0x45,
+0x51,0x88,
+
+0x56,0x1f, // for tracking
+0x57,0xa6, // for tracking
+0x58,0x1a, // for tracking
+0x59,0x7a, // for tracking
+
+0x5a,0x04,
+0x5b,0x04,
+
+0x5e,0xc7,
+0x5f,0x95,
+
+0x62,0x10,
+0x63,0xc0,
+0x64,0x10,
+0x65,0x8a,
+0x66,0x58,
+0x67,0x58,
+
+0x70,0x54, //6c
+0x71,0x80, //81(+4),89(-4)
+
+0x76,0x32,
+0x77,0xb1,
+0x78,0x22, //24
+0x79,0x32, // Y Target 70 => 25,72 => 26 //
+0x7a,0x23, //23
+0x7b,0x22, //22
+0x7d,0x23,
+
+0x83,0x02, //EXP Normal 33.33 fps
+0x84,0xbf,
+0x85,0x20,
+
+0x86,0x01, //EXPMin 12000.00 fps
+0x87,0xf4,
+
+0x88,0x0b, //EXP Max(120Hz) 8.00 fps
+0x89,0x71,
+0x8a,0xb0,
+
+0xa5,0x0b, //EXP Max(100Hz) 7.69 fps
+0xa6,0xe6,
+0xa7,0xe0,
+
+0x8B,0xea, //EXP100
+0x8C,0x60,
+
+0x8D,0xc3, //EXP120
+0x8E,0x50,
+
+0x9c,0x0f, //EXP Limit 1500.00 fps
+0x9d,0xa0,
+0x9e,0x01, //EXP Unit
+0x9f,0xf4,
+
+0xa3,0x00,
+0xa4,0xfd, //Exp 12000
+
+0xb0,0x15,
+0xb1,0x14,
+0xb2,0x60,
+0xb3,0x1a,
+0xb4,0x1c,
+0xb5,0x48,
+0xb6,0x32,
+0xb7,0x2b,
+0xb8,0x27,
+0xb9,0x25,
+0xba,0x23,
+0xbb,0x22,
+0xbc,0x22,
+0xbd,0x21,
+
+0xc0,0x10,
+0xc1,0x40,
+0xc2,0x40,
+0xc3,0x40,
+0xc4,0x06,
+
+0xc8,0x80,
+0xc9,0x80,
+///// PAGE 20 END /////
+
+///// PAGE 21 START /////
+0x03,0x21, //page 21
+
+//Indoor Weight
+0x20,0x11,
+0x21,0x11,
+0x22,0x11,
+0x23,0x11,
+0x24,0x14,
+0x25,0x44,
+0x26,0x44,
+0x27,0x41,
+0x28,0x14,
+0x29,0x44,
+0x2a,0x44,
+0x2b,0x41,
+0x2c,0x14,
+0x2d,0x47,
+0x2e,0x74,
+0x2f,0x41,
+0x30,0x14,
+0x31,0x47,
+0x32,0x74,
+0x33,0x41,
+0x34,0x14,
+0x35,0x44,
+0x36,0x44,
+0x37,0x41,
+0x38,0x14,
+0x39,0x44,
+0x3a,0x44,
+0x3b,0x41,
+0x3c,0x11,
+0x3d,0x11,
+0x3e,0x11,
+0x3f,0x11,
+
+//Outdoor Weight
+0x40,0x11,
+0x41,0x11,
+0x42,0x11,
+0x43,0x11,
+0x44,0x14,
+0x45,0x44,
+0x46,0x44,
+0x47,0x41,
+0x48,0x14,
+0x49,0x44,
+0x4a,0x44,
+0x4b,0x41,
+0x4c,0x14,
+0x4d,0x47,
+0x4e,0x74,
+0x4f,0x41,
+0x50,0x14,
+0x51,0x47,
+0x52,0x74,
+0x53,0x41,
+0x54,0x14,
+0x55,0x44,
+0x56,0x44,
+0x57,0x41,
+0x58,0x14,
+0x59,0x44,
+0x5a,0x44,
+0x5b,0x41,
+0x5c,0x11,
+0x5d,0x11,
+0x5e,0x11,
+0x5f,0x11,
+
+
+///// PAGE 22 START /////
+0x03,0x22, //page 22
+0x10,0xfd,
+0x11,0x2e,
+0x19,0x00, //Low On //
+0x20,0x30, //For AWB Speed
+0x21,0x80,
+0x22,0x00,
+0x23,0x00,
+0x24,0x01,
+0x25,0x7e, //for Tracking setting
+
+0x30,0x80,
+0x31,0x80,
+0x38,0x11,
+0x39,0x34,
+0x40,0xe4, //Stb Yth
+0x41,0x33, //Stb cdiff
+0x42,0x22, //Stb csum
+0x43,0xf3, //Unstb Yth
+0x44,0x55, //Unstb cdiff
+0x45,0x33, //Unstb csum
+0x46,0x00,
+0x47,0xa2,
+0x48,0x02,
+0x49,0x0a,
+
+0x60,0x04,
+0x61,0xc4,
+0x62,0x04,
+0x63,0x92,
+0x66,0x04,
+0x67,0xc4,
+0x68,0x04,
+0x69,0x92,
+
+0x80,0x38,
+0x81,0x20,
+0x82,0x38,
+
+0x83,0x58,
+0x84,0x16,
+0x85,0x4f,
+0x86,0x1a,
+
+0x87,0x42,
+0x88,0x30,
+0x89,0x27,
+0x8a,0x18,
+
+0x8b,0x3d,
+0x8c,0x32,
+0x8d,0x24,
+0x8e,0x1d,
+
+0x8f,0x4d, //4e
+0x90,0x46, //4d
+0x91,0x40, //4c
+0x92,0x3a, //4a
+0x93,0x2f, //46
+0x94,0x21,
+0x95,0x19,
+0x96,0x16,
+0x97,0x13,
+0x98,0x12,
+0x99,0x11,
+0x9a,0x10,
+
+0x9b,0xbb,
+0x9c,0xaa,
+0x9d,0x48,
+0x9e,0x38,
+0x9f,0x30,
+
+0xa0,0x70,
+0xa1,0x54,
+0xa2,0x6f,
+0xa3,0xff,
+
+0xa4,0x14, //1536fps
+0xa5,0x2c, //698fps
+0xa6,0xcf, //148fps
+
+0xad,0x40,
+0xae,0x4a,
+
+0xaf,0x28, //Low temp Rgain
+0xb0,0x26, //Low temp Rgain
+
+0xb1,0x00,
+0xb4,0xbf, //For Tracking AWB Weight
+0xb8,0x91, //(0+,1-)High Cb ,(0+,1-)Low Cr
+0xb9,0x00,
+/////// PAGE 22 END ///////
+
+/////// PAGE 20 ///////
+0x03,0x20,
+0x10,0x9c, //AE On 50hz
+
+/////// PAGE 22 ///////
+0x03,0x22,
+0x10,0xe9, //AWB On
+
+/////// PAGE 0 ///////
+0x03,0x00,
+0x0e,0x03,
+0x0e,0x73,
+
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+
+0x03,0x18,
+0x12,0x20,
+0x10,0x07,
+0x11,0x00,
+0x20,0x02,
+0x21,0x80,
+0x22,0x03,
+0x23,0xc0,
+0x24,0x00,
+0x25,0x00,
+0x26,0x00,
+0x27,0x00,
+0x28,0x02,
+0x29,0x80,
+0x2a,0x03,
+0x2b,0xc0,
+0x2c,0x0a,
+0x2d,0x00,
+0x2e,0x0a,
+0x2f,0x00,
+0x30,0x24,
+
+0x03,0x00,
+0x01,0x00,
+
+};
+
+uint8_t hi257_default_regs_init[]={
+
+0x01,0x01, //sleep on
+0x01,0x03, //sleep off
+0x01,0x01, //sleep on
+
+0x03,0x00, //Dummy 750us
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+
+0x08,0x00,
+0x09,0x77, // pad strength = max
+0x0a,0x07, // pad strength = max
+
+0x0e,0x03, //PLL On
+0x0e,0x73, //PLLx2
+
+0x03,0x00, //Dummy 750us
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+
+
+///// PAGE 20 /////
+0x03,0x20, //page 20
+0x10,0x1c, //AE off 50hz
+
+///// PAGE 22 /////
+0x03,0x22, //page 22
+0x10,0x69, //AWB off
+
+
+///// Initial Start /////
+///// PAGE 0 Start /////
+0x03,0x00, //page 0
+0x10,0x90,
+0x11,0x90, //Windowing On + 1Frame Skip
+0x12,0x04, //Rinsing edge 0x04 // Falling edge 0x00
+0x14,0x05,
+
+0x20,0x00, //Row H
+0x21,0x02, //Row L
+0x22,0x00, //Col H
+0x23,0x04, //Col L
+
+0x24,0x04, //Window height_H //= 1200
+0x25,0xb0, //Window height_L //
+0x26,0x06, //Window width_H //= 1600
+0x27,0x40, //Window wight_L
+
+0x40,0x01, //Hblank_360
+0x41,0x78,
+0x42,0x00, //Vblank
+0x43,0x14, //Flick Stop
+
+0x50,0x00, //Test Pattern
+
+///// BLC /////
+0x80,0x2e,
+0x81,0x7e,
+0x82,0x90,
+0x83,0x00,
+0x84,0xcc, //20130604 0x0c->0xcc
+0x85,0x00,
+0x86,0x00,
+0x87,0x0f,
+0x88,0x34,
+0x8a,0x0b,
+0x8e,0x80, //Pga Blc Hold
+
+0x90,0x0c, //BLC_TIME_TH_ON
+0x91,0x0c, //BLC_TIME_TH_OFF
+0x92,0x58, //BLC_AG_TH_ON
+0x93,0x50, //BLC_AG_TH_OFF
+0x96,0xdc, //BLC Outdoor Th On
+0x97,0xfe, //BLC Outdoor Th Off
+0x98,0x38,
+
+//OutDoor BLC
+0x99,0x43, //R,Gr,B,Gb Offset
+
+//Dark BLC
+0xa0,0x02, //R,Gr,B,Gb Offset
+
+//Normal BLC
+0xa8,0x43, //R,Gr,B,Gb Offset
+///// PAGE 0 END /////
+
+///// PAGE 2 START /////
+0x03,0x02,
+0x10,0x00,
+0x13,0x00,
+0x14,0x00,
+0x18,0xcc,
+0x19,0x01, // pmos switch on (for cfpn)
+0x1A,0x39, //20130604 0x09->0xcc
+0x1B,0x00,
+0x1C,0x1a, // for ncp
+0x1D,0x14, // for ncp
+0x1E,0x30, // for ncp
+0x1F,0x10,
+
+0x20,0x77,
+0x21,0xde,
+0x22,0xa7,
+0x23,0x30,
+0x24,0x77,
+0x25,0x10,
+0x26,0x10,
+0x27,0x3c,
+0x2b,0x80,
+0x2c,0x02,
+0x2d,0x58,
+0x2e,0x11,//20130604 0xde->0x11
+0x2f,0x11,//20130604 0xa7->0x11
+
+0x30,0x00,
+0x31,0x99,
+0x32,0x00,
+0x33,0x00,
+0x34,0x22,
+0x36,0x75,
+0x38,0x88,
+0x39,0x88,
+0x3d,0x03,
+0x3f,0x02,
+
+0x49,0xc1,//20130604 0x87->0xd1 --> mode Change Issue modify -> 0xc1
+0x4a,0x10,
+
+0x50,0x21,
+0x53,0xb1,
+0x54,0x10,
+0x55,0x1c, // for ncp
+0x56,0x11,
+0x58,0x3a,//20130604 add
+0x59,0x38,//20130604 add
+0x5d,0xa2,
+0x5e,0x5a,
+
+0x60,0x87,
+0x61,0x98,
+0x62,0x88,
+0x63,0x96,
+0x64,0x88,
+0x65,0x96,
+0x67,0x3f,
+0x68,0x3f,
+0x69,0x3f,
+
+0x72,0x89,
+0x73,0x95,
+0x74,0x89,
+0x75,0x95,
+0x7C,0x84,
+0x7D,0xaf,
+
+0x80,0x01,
+0x81,0x7a,
+0x82,0x13,
+0x83,0x24,
+0x84,0x78,
+0x85,0x7c,
+
+0x92,0x44,
+0x93,0x59,
+0x94,0x78,
+0x95,0x7c,
+
+0xA0,0x02,
+0xA1,0x74,
+0xA4,0x74,
+0xA5,0x02,
+0xA8,0x85,
+0xA9,0x8c,
+0xAC,0x10,
+0xAD,0x16,
+
+0xB0,0x99,
+0xB1,0xa3,
+0xB4,0x9b,
+0xB5,0xa2,
+0xB8,0x9b,
+0xB9,0x9f,
+0xBC,0x9b,
+0xBD,0x9f,
+
+0xc4,0x29,
+0xc5,0x40,
+0xc6,0x5c,
+0xc7,0x72,
+0xc8,0x2a,
+0xc9,0x3f,
+0xcc,0x5d,
+0xcd,0x71,
+
+0xd0,0x10,
+0xd1,0x14,
+0xd2,0x20,
+0xd3,0x00,
+0xd4,0x0c, //DCDC_TIME_TH_ON
+0xd5,0x0c, //DCDC_TIME_TH_OFF
+0xd6,0x58, //DCDC_AG_TH_ON
+0xd7,0x50, //DCDC_AG_TH_OFF
+0xdc,0x00,
+0xdd,0xa3,
+0xde,0x00,
+0xdf,0x84,
+
+0xe0,0xa4,
+0xe1,0xa4,
+0xe2,0xa4,
+0xe3,0xa4,
+0xe4,0xa4,
+0xe5,0x01,
+0xe8,0x00,
+0xe9,0x00,
+0xea,0x77,
+
+0xF0,0x00,
+0xF1,0x00,
+0xF2,0x00,
+
+///// PAGE 2 END /////
+
+///// PAGE 10 START /////
+0x03,0x10, //page 10
+0x10,0x03, //S2D enable _ YUYV Order o??
+0x11,0x03,
+0x12,0xf0,
+0x13,0x01,
+
+0x20,0x00,
+0x21,0x40,
+0x22,0x0f,
+0x24,0x20,
+0x25,0x10,
+0x26,0x01,
+0x27,0x02,
+0x28,0x11,
+
+0x40,0x00,
+0x41,0x00, //D-YOffset Th
+0x42,0x04, //Cb Offset
+0x43,0x04, //Cr Offset
+0x44,0x80,
+0x45,0x80,
+0x46,0xf0,
+0x48,0x80,
+0x4a,0x80,
+
+0x50,0xa0, //D-YOffset AG
+
+0x60,0x0f,
+0x61,0xc0, //Sat B
+0x62,0xc0, //Sat R
+0x63,0x58, //Auto-De Color
+
+0x66,0x42,
+0x67,0x22,
+
+0x6a,0x7c, //White Protection Offset Dark/Indoor
+0x74,0x0c, //White Protection Offset Outdoor
+0x76,0x01, //White Protection Enable
+///// PAGE 10 END /////
+
+///// PAGE 11 START /////
+0x03,0x11, //page 11
+
+//LPF Auto Control
+0x20,0x00,
+0x21,0x00,
+0x26,0x5a, // Double_AG
+0x27,0x58, // Double_AG
+0x28,0x0f,
+0x29,0x10,
+0x2b,0x30,
+0x2c,0x32,
+
+//GBGR
+0x70,0x2b,
+0x74,0x30,
+0x75,0x18,
+0x76,0x30,
+0x77,0xff,
+0x78,0xa0,
+0x79,0xff, //Dark GbGr Th
+0x7a,0x30,
+0x7b,0x20,
+0x7c,0xf4, //Dark Dy Th B[7:4]
+///// PAGE 11 END /////
+
+///// PAGE 12 START /////
+0x03,0x12, //page 11
+
+//YC2D
+0x10,0x03, //Y DPC Enable
+0x11,0x08, //
+0x12,0x10, //0x30 -> 0x10
+0x20,0x53, //Y_lpf_enable
+0x21,0x03, //C_lpf_enable_on
+0x22,0xe6, //YC2D_CrCbY_Dy
+
+0x23,0x14, //Outdoor Dy Th
+0x24,0x20, //Indoor Dy Th // For reso Limit 0x20
+0x25,0x30, //Dark Dy Th
+
+//Outdoor LPF Flat
+0x30,0xff, //Y Hi Th
+0x31,0x00, //Y Lo Th
+0x32,0xf0, //Std Hi Th //Reso Improve Th Low //50
+0x33,0x00, //Std Lo Th
+0x34,0x00, //Median ratio
+
+//Indoor LPF Flat
+0x35,0xff, //Y Hi Th
+0x36,0x00, //Y Lo Th
+0x37,0xff, //Std Hi Th //Reso Improve Th Low //50
+0x38,0x00, //Std Lo Th
+0x39,0x00, //Median ratio
+
+//Dark LPF Flat
+0x3a,0xff, //Y Hi Th
+0x3b,0x00, //Y Lo Th
+0x3c,0x93, //Std Hi Th //Reso Improve Th Low //50
+0x3d,0x00, //Std Lo Th
+0x3e,0x00, //Median ratio
+
+//Outdoor Cindition
+0x46,0xa0, //Out Lum Hi
+0x47,0x40, //Out Lum Lo
+
+//Indoor Cindition
+0x4c,0xb0, //Indoor Lum Hi
+0x4d,0x40, //Indoor Lum Lo
+
+//Dark Cindition
+0x52,0xb0, //Dark Lum Hi
+0x53,0x50, //Dark Lum Lo
+
+//C-Filter
+0x70,0x10, //Outdoor(2:1) AWM Th Horizontal
+0x71,0x0a, //Outdoor(2:1) Diff Th Vertical
+0x72,0x10, //Indoor,Dark1 AWM Th Horizontal
+0x73,0x0a, //Indoor,Dark1 Diff Th Vertical
+0x74,0x18, //Dark(2:3) AWM Th Horizontal
+0x75,0x0f, //Dark(2:3) Diff Th Vertical
+
+//DPC
+0x90,0x7d,
+0x91,0x34,
+0x99,0x28,
+0x9c,0x14,
+0x9d,0x15,
+0x9e,0x28,
+0x9f,0x28,
+0xb0,0x0e, //Zipper noise Detault change (0x75->0x0e)
+0xb8,0x44,
+0xb9,0x15,
+///// PAGE 12 END /////
+
+///// PAGE 13 START /////
+0x03,0x13, //page 13
+
+0x80,0xfd, //Sharp2D enable _ YUYV Order o??
+0x81,0x07, //Sharp2D Clip/Limit
+0x82,0x73, //Sharp2D Filter
+0x83,0x00, //Sharp2D Low Clip
+0x85,0x00,
+
+0x92,0x33, //Sharp2D Slop n/p
+0x93,0x30, //Sharp2D LClip
+0x94,0x02, //Sharp2D HiClip1 Th
+0x95,0xf0, //Sharp2D HiClip2 Th
+0x96,0x1e, //Sharp2D HiClip2 Resolution
+0x97,0x40,
+0x98,0x80,
+0x99,0x40,
+
+//Sharp Lclp
+0xa2,0x04, //Outdoor Lclip_N
+0xa3,0x05, //Outdoor Lclip_P
+0xa4,0x06, //Indoor Lclip_N 0x03 For reso Limit 0x0e
+0xa5,0x07, //Indoor Lclip_P 0x0f For reso Limit 0x0f
+0xa6,0x80, //Dark Lclip_N
+0xa7,0x80, //Dark Lclip_P
+
+//Outdoor Slope
+0xb6,0x28, //Lum negative Hi
+0xb7,0x20, //Lum negative middle
+0xb8,0x24, //Lum negative Low
+0xb9,0x28, //Lum postive Hi
+0xba,0x20, //Lum postive middle
+0xbb,0x24, //Lum postive Low
+
+//Indoor Slope
+0xbc,0x20, //Lum negative Hi
+0xbd,0x20, //Lum negative middle
+0xbe,0x20, //Lum negative Low
+0xbf,0x20, //Lum postive Hi
+0xc0,0x20, //Lum postive middle
+0xc1,0x20, //Lum postive Low
+
+//Dark Slope
+0xc2,0x14, //Lum negative Hi
+0xc3,0x24, //Lum negative middle
+0xc4,0x1d, //Lum negative Low
+0xc5,0x14, //Lum postive Hi
+0xc6,0x24, //Lum postive middle
+0xc7,0x1d, //Lum postive Low
+///// PAGE 13 END /////
+
+///// PAGE 14 START /////
+0x03,0x14, //page 14
+0x10,0x01,
+
+0x20,0xA0,//X-Center
+0x21,0x80,//Y-Center
+
+0x22,0x12, //LSC R 1b->15 20130125
+0x23,0x0f, //LSC G
+0x24,0x0f, //LSC B
+
+0x25,0xf0, //LSC Off
+0x26,0xf0, //LSC On
+///// PAGE 14 END /////
+
+/////// PAGE 15 START ///////
+0x03,0x15, //page 15
+0x10,0x21,
+0x14,0x44,
+0x15,0x34,
+0x16,0x26,
+0x17,0x2f,
+
+//CMC
+0x30,0xdd,
+0x31,0x68,
+0x32,0x0b,
+0x33,0x2c,
+0x34,0xce,
+0x35,0x22,
+0x36,0x0e,
+0x37,0x42,
+0x38,0xd0,
+
+//CMC OFS
+0x40,0x90,
+0x41,0x16,
+0x42,0x86,
+0x43,0x01,
+0x44,0x10,
+0x45,0x91,
+0x46,0x82,
+0x47,0x9c,
+0x48,0x1d,
+
+//CMC POFS
+0x50,0x0d,
+0x51,0x91,
+0x52,0x04,
+0x53,0x07,
+0x54,0x19,
+0x55,0xa0,
+0x56,0x00,
+0x57,0x98,
+0x58,0x18,
+///// PAGE 15 END /////
+
+///// PAGE 16 START /////
+0x03,0x16, //page 16
+0x10,0x31,
+0x18,0x5a,// Double_AG 5e->37
+0x19,0x58,// Double_AG 5e->36
+0x1a,0x0e,
+0x1b,0x01,
+0x1c,0xdc,
+0x1d,0xfe,
+
+//Indoor
+0x30,0x00,
+0x31,0x06,
+0x32,0x12,
+0x33,0x29,
+0x34,0x4d,
+0x35,0x6c,
+0x36,0x81,
+0x37,0x94,
+0x38,0xa4,
+0x39,0xb3,
+0x3a,0xc0,
+0x3b,0xcb,
+0x3c,0xd5,
+0x3d,0xde,
+0x3e,0xe6,
+0x3f,0xee,
+0x40,0xf5,
+0x41,0xfc,
+0x42,0xff,
+
+
+//Outdoor
+0x50,0x00,
+0x51,0x06,
+0x52,0x12,
+0x53,0x29,
+0x54,0x45,
+0x55,0x60,
+0x56,0x7a,
+0x57,0x8e,
+0x58,0xa1,
+0x59,0xac,
+0x5a,0xb8,
+0x5b,0xc5,
+0x5c,0xd0,
+0x5d,0xda,
+0x5e,0xe3,
+0x5f,0xeb,
+0x60,0xf0,
+0x61,0xf4,
+0x62,0xf5,
+
+//Dark
+0x70,0x00,
+0x71,0x0e,
+0x72,0x17,
+0x73,0x26,
+0x74,0x3f,
+0x75,0x56,
+0x76,0x6c,
+0x77,0x81,
+0x78,0x94,
+0x79,0xa5,
+0x7a,0xb3,
+0x7b,0xc4,
+0x7c,0xce,
+0x7d,0xd9,
+0x7e,0xe4,
+0x7f,0xec,
+0x80,0xf5,
+0x81,0xfc,
+0x82,0xff,
+///// PAGE 16 END /////
+
+///// PAGE 17 START /////
+0x03,0x17, //page 17
+0xc1,0x00,
+0xc4,0x4b,
+0xc5,0x3f,
+0xc6,0x02,
+0xc7,0x20,
+///// PAGE 17 END /////
+
+///// PAGE 18 START /////
+0x03,0x18, //page 18
+0x14,0x00, //Cubic Off
+///// PAGE 18 END /////
+
+///// PAGE 19 START /////
+0x03,0x19, //Page 0x18
+0x10,0x7f, //mcmc_ctl1 MCMC Enable B:[0]
+0x11,0x7f, //mcmc_ctl2
+0x12,0x1b, //mcmc_delta1
+0x13,0xac, //mcmc_center1
+0x14,0x1e, //mcmc_delta2
+0x15,0x6e, //mcmc_center2
+0x16,0x0a, //mcmc_delta3
+0x17,0xb8, //mcmc_center3
+0x18,0x1e, //mcmc_delta4
+0x19,0xe6, //mcmc_center4
+0x1a,0x9e, //mcmc_delta5
+0x1b,0x22, //mcmc_center5
+0x1c,0x9e, //mcmc_delta6
+0x1d,0x5e, //mcmc_center6
+0x1e,0x70,//mcmc_sat_gain1
+0x1f,0x37, //mcmc_sat_gain2
+0x20,0x21, //mcmc_sat_gain3
+0x21,0x43, //mcmc_sat_gain4
+0x22,0x2f, //mcmc_sat_gain5
+0x23,0x37, //mcmc_sat_gain6
+0x24,0x00, //mcmc_hue_angle1
+0x25,0x07, //mcmc_hue_angle2
+0x26,0x09, //mcmc_hue_angle3
+0x27,0x04, //mcmc_hue_angle4
+0x28,0x00, //mcmc_hue_angle5
+0x29,0x8c, //mcmc_hue_angle6
+
+0x53,0x10, //mcmc_ctl3
+
+0x6c,0xff, //mcmc_lum_ctl1 sat hue offset
+0x6d,0x3f, //mcmc_lum_ctl2 gain
+0x6e,0x00, //mcmc_lum_ctl3 hue
+0x6f,0x00, //mcmc_lum_ctl4 rgb offset
+0x70,0x00, //mcmc_lum_ctl5 rgb scale
+
+0x71,0x3f, //mcmc_lum_gain_wgt_th1 R1 magenta
+0x72,0x3f, //mcmc_lum_gain_wgt_th2 R1
+0x73,0x3f, //mcmc_lum_gain_wgt_th3 R1
+0x74,0x3f, //mcmc_lum_gain_wgt_th4 R1
+0x75,0x30, //mcmc_rg1_lum_sp1 R1
+0x76,0x50, //mcmc_rg1_lum_sp2 R1
+0x77,0x80, //mcmc_rg1_lum_sp3 R1
+0x78,0xb0, //mcmc_rg1_lum_sp4 R1
+
+0x79,0x3f, //mcmc_lum_gain_wgt_th1 R2 bright green
+0x7a,0x3f, //mcmc_lum_gain_wgt_th2 R2
+0x7b,0x3f, //mcmc_lum_gain_wgt_th3 R2
+0x7c,0x3f, //mcmc_lum_gain_wgt_th4 R2
+0x7d,0x28, //mcmc_rg2_lum_sp1 R2
+0x7e,0x50, //mcmc_rg2_lum_sp2 R2
+0x7f,0x80, //mcmc_rg2_lum_sp3 R2
+0x80,0xb0, //mcmc_rg2_lum_sp4 R2
+
+0x81,0x28, //mcmc_lum_gain_wgt_th1 R3 dark green
+0x82,0x3f, //mcmc_lum_gain_wgt_th2 R3
+0x83,0x3f, //mcmc_lum_gain_wgt_th3 R3
+0x84,0x3f, //mcmc_lum_gain_wgt_th4 R3
+0x85,0x28, //mcmc_rg3_lum_sp1 R3
+0x86,0x50, //mcmc_rg3_lum_sp2 R3
+0x87,0x80, //mcmc_rg3_lum_sp3 R3
+0x88,0xb0, //mcmc_rg3_lum_sp4 R3
+
+0x89,0x1a, //mcmc_lum_gain_wgt_th1 R4 skin
+0x8a,0x28, //mcmc_lum_gain_wgt_th2 R4
+0x8b,0x3f, //mcmc_lum_gain_wgt_th3 R4
+0x8c,0x3f, //mcmc_lum_gain_wgt_th4 R4
+0x8d,0x10, //mcmc_rg3_lum_sp1 R4
+0x8e,0x30, //mcmc_rg3_lum_sp2 R4
+0x8f,0x60, //mcmc_rg3_lum_sp3 R4
+0x90,0x90, //mcmc_rg3_lum_sp4 R4
+
+0x91,0x1a, //mcmc_rg5_gain_wgt_th1 R5 cyan
+0x92,0x28, //mcmc_rg5_gain_wgt_th2 R5
+0x93,0x3f, //mcmc_rg5_gain_wgt_th3 R5
+0x94,0x3f, //mcmc_rg5_gain_wgt_th4 R5
+0x95,0x28, //mcmc_rg5_lum_sp1 R5
+0x96,0x50, //mcmc_rg5_lum_sp2 R5
+0x97,0x80, //mcmc_rg5_lum_sp3 R5
+0x98,0xb0, //mcmc_rg5_lum_sp4 R5
+
+0x99,0x1a, //mcmc_rg6_gain_wgt_th1 R6 blue
+0x9a,0x28, //mcmc_rg6_gain_wgt_th2 R6
+0x9b,0x3f, //mcmc_rg6_gain_wgt_th3 R6
+0x9c,0x3f, //mcmc_rg6_gain_wgt_th4 R6
+0x9d,0x28, //mcmc_rg6_lum_sp1 R6
+0x9e,0x50, //mcmc_rg6_lum_sp2 R6
+0x9f,0x80, //mcmc_rg6_lum_sp3 R6
+0xa0,0xb0, //mcmc_rg6_lum_sp4 R6
+
+0xa2,0x00,
+0xe5,0x80, //add 20120709 Bit[7] On MCMC --> YC2D_LPF
+
+/////// PAGE 20 START ///////
+0x03,0x20,
+0x10,0x1c,
+0x11,0x14,
+0x18,0x30,
+0x20,0x25, //8x8 Ae weight 0~7 Outdoor / Weight Outdoor On B[5]
+0x21,0x30,
+0x22,0x10,
+0x23,0x00,
+
+0x28,0xe7,
+0x29,0x0d,
+0x2a,0xff,
+0x2b,0x04, //Adaptive Off,1/100 Flicker
+
+0x2c,0x83, //AE After CI
+0x2d,0x03,
+0x2e,0x13,
+0x2f,0x0b,
+
+0x30,0x78,
+0x31,0xd7,
+0x32,0x10,
+0x33,0x2e,
+0x34,0x20,
+0x35,0xd4,
+0x36,0xfe,
+0x37,0x32,
+0x38,0x04,
+0x39,0x22,
+0x3a,0xde,
+0x3b,0x22,
+0x3c,0xde,
+0x3d,0xe1,
+
+0x50,0x45,
+0x51,0x88,
+
+0x56,0x1f, // for tracking
+0x57,0xa6, // for tracking
+0x58,0x1a, // for tracking
+0x59,0x7a, // for tracking
+
+0x5a,0x04,
+0x5b,0x04,
+
+0x5e,0xc7,
+0x5f,0x95,
+
+0x62,0x10,
+0x63,0xc0,
+0x64,0x10,
+0x65,0x8a,
+0x66,0x58,
+0x67,0x58,
+
+0x70,0x54, //6c
+0x71,0x80, //81(+4),89(-4)
+
+0x76,0x32,
+0x77,0xb1,
+0x78,0x22, //24
+0x79,0x32, // Y Target 70 => 25,72 => 26 //
+0x7a,0x23, //23
+0x7b,0x22, //22
+0x7d,0x23,
+
+0x83,0x02, //EXP Normal 33.33 fps
+0x84,0xbf,
+0x85,0x20,
+
+0x86,0x01, //EXPMin 12000.00 fps
+0x87,0xf4,
+
+0x88,0x0b, //EXP Max(120Hz) 8.00 fps
+0x89,0x71,
+0x8a,0xb0,
+
+0xa5,0x0b, //EXP Max(100Hz) 7.69 fps
+0xa6,0xe6,
+0xa7,0xe0,
+
+0x8B,0xea, //EXP100
+0x8C,0x60,
+
+0x8D,0xc3, //EXP120
+0x8E,0x50,
+
+0x9c,0x0f, //EXP Limit 1500.00 fps
+0x9d,0xa0,
+0x9e,0x01, //EXP Unit
+0x9f,0xf4,
+
+0xa3,0x00,
+0xa4,0xfd, //Exp 12000
+
+0xb0,0x15,
+0xb1,0x14,
+0xb2,0x60,
+0xb3,0x1a,
+0xb4,0x1c,
+0xb5,0x48,
+0xb6,0x32,
+0xb7,0x2b,
+0xb8,0x27,
+0xb9,0x25,
+0xba,0x23,
+0xbb,0x22,
+0xbc,0x22,
+0xbd,0x21,
+
+0xc0,0x10,
+0xc1,0x40,
+0xc2,0x40,
+0xc3,0x40,
+0xc4,0x06,
+
+0xc8,0x80,
+0xc9,0x80,
+///// PAGE 20 END /////
+
+///// PAGE 21 START /////
+0x03,0x21, //page 21
+
+//Indoor Weight
+0x20,0x11,
+0x21,0x11,
+0x22,0x11,
+0x23,0x11,
+0x24,0x14,
+0x25,0x44,
+0x26,0x44,
+0x27,0x41,
+0x28,0x14,
+0x29,0x44,
+0x2a,0x44,
+0x2b,0x41,
+0x2c,0x14,
+0x2d,0x47,
+0x2e,0x74,
+0x2f,0x41,
+0x30,0x14,
+0x31,0x47,
+0x32,0x74,
+0x33,0x41,
+0x34,0x14,
+0x35,0x44,
+0x36,0x44,
+0x37,0x41,
+0x38,0x14,
+0x39,0x44,
+0x3a,0x44,
+0x3b,0x41,
+0x3c,0x11,
+0x3d,0x11,
+0x3e,0x11,
+0x3f,0x11,
+
+//Outdoor Weight
+0x40,0x11,
+0x41,0x11,
+0x42,0x11,
+0x43,0x11,
+0x44,0x14,
+0x45,0x44,
+0x46,0x44,
+0x47,0x41,
+0x48,0x14,
+0x49,0x44,
+0x4a,0x44,
+0x4b,0x41,
+0x4c,0x14,
+0x4d,0x47,
+0x4e,0x74,
+0x4f,0x41,
+0x50,0x14,
+0x51,0x47,
+0x52,0x74,
+0x53,0x41,
+0x54,0x14,
+0x55,0x44,
+0x56,0x44,
+0x57,0x41,
+0x58,0x14,
+0x59,0x44,
+0x5a,0x44,
+0x5b,0x41,
+0x5c,0x11,
+0x5d,0x11,
+0x5e,0x11,
+0x5f,0x11,
+
+
+///// PAGE 22 START /////
+0x03,0x22, //page 22
+0x10,0xfd,
+0x11,0x2e,
+0x19,0x00, //Low On //
+0x20,0x30, //For AWB Speed
+0x21,0x80,
+0x22,0x00,
+0x23,0x00,
+0x24,0x01,
+0x25,0x7e, //for Tracking setting
+
+0x30,0x80,
+0x31,0x80,
+0x38,0x11,
+0x39,0x34,
+0x40,0xe4, //Stb Yth
+0x41,0x33, //Stb cdiff
+0x42,0x22, //Stb csum
+0x43,0xf3, //Unstb Yth
+0x44,0x55, //Unstb cdiff
+0x45,0x33, //Unstb csum
+0x46,0x00,
+0x47,0xa2,
+0x48,0x02,
+0x49,0x0a,
+
+0x60,0x04,
+0x61,0xc4,
+0x62,0x04,
+0x63,0x92,
+0x66,0x04,
+0x67,0xc4,
+0x68,0x04,
+0x69,0x92,
+
+0x80,0x38,
+0x81,0x20,
+0x82,0x38,
+
+0x83,0x58,
+0x84,0x16,
+0x85,0x4f,
+0x86,0x1a,
+
+0x87,0x42,
+0x88,0x30,
+0x89,0x27,
+0x8a,0x18,
+
+0x8b,0x3d,
+0x8c,0x32,
+0x8d,0x24,
+0x8e,0x1d,
+
+0x8f,0x4d, //4e
+0x90,0x46, //4d
+0x91,0x40, //4c
+0x92,0x3a, //4a
+0x93,0x2f, //46
+0x94,0x21,
+0x95,0x19,
+0x96,0x16,
+0x97,0x13,
+0x98,0x12,
+0x99,0x11,
+0x9a,0x10,
+
+0x9b,0xbb,
+0x9c,0xaa,
+0x9d,0x48,
+0x9e,0x38,
+0x9f,0x30,
+
+0xa0,0x70,
+0xa1,0x54,
+0xa2,0x6f,
+0xa3,0xff,
+
+0xa4,0x14, //1536fps
+0xa5,0x2c, //698fps
+0xa6,0xcf, //148fps
+
+0xad,0x40,
+0xae,0x4a,
+
+0xaf,0x28, //Low temp Rgain
+0xb0,0x26, //Low temp Rgain
+
+0xb1,0x00,
+0xb4,0xbf, //For Tracking AWB Weight
+0xb8,0x91, //(0+,1-)High Cb ,(0+,1-)Low Cr
+0xb9,0x00,
+/////// PAGE 22 END ///////
+
+/////// PAGE 20 ///////
+0x03,0x20,
+0x10,0x9c, //AE On 50hz
+
+/////// PAGE 22 ///////
+0x03,0x22,
+0x10,0xe9, //AWB On
+
+/////// PAGE 0 ///////
+0x03,0x00,
+0x0e,0x03,
+0x0e,0x73,
+
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+0x03,0x00,
+
+0x03,0x18,
+0x12,0x20,
+0x10,0x07,
+0x11,0x00,
+0x20,0x02,
+0x21,0x80,
+0x22,0x03,
+0x23,0xc0,
+0x24,0x00,
+0x25,0x00,
+0x26,0x00,
+0x27,0x00,
+0x28,0x02,
+0x29,0x80,
+0x2a,0x03,
+0x2b,0xc0,
+0x2c,0x0a,
+0x2d,0x00,
+0x2e,0x0a,
+0x2f,0x00,
+0x30,0x24,
+
+0x03,0x00,
+0x01,0x00,
+
+};
+
+#endif
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/nmi5625/Makefile b/drivers/media/video/wmt_v4l2/sensors/nmi5625/Makefile
new file mode 100755
index 00000000..f77cbbad
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/nmi5625/Makefile
@@ -0,0 +1,4 @@
+# obj-y += nmidrv_i2c.o
+nmidrv-objs += nmidrv_kernel.o
+obj-y += nmidrv.o
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/nmi5625/nmidrv_cmos.c b/drivers/media/video/wmt_v4l2/sensors/nmi5625/nmidrv_cmos.c
new file mode 100755
index 00000000..646d3cfb
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/nmi5625/nmidrv_cmos.c
@@ -0,0 +1,158 @@
+
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "nmidrv_cmos.h"
+
+#define sensor_write_array(sd, arr, sz)
+#define sensor_read(sd, reg)
+#define sensor_write(sd, reg, val)
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint8_t *regs;
+ size_t size;
+};
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+#define FILP_REG_INIT_VALUE 0x10
+#define DELAY_INTERVAL 50
+#define RETRY_TIMES 10
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+ CMOS_WIN_SIZE("QVGA", 320, 240, nmi_320_240_regs),
+// CMOS_WIN_SIZE("VGA", 640, 480, nmi_640_480_regs),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win) {
+ pr_err("%s, s_mbus_fmt failed, width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, win->regs, win->size);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return 0;
+}
+
+static int sensor_enum_framesizes(struct cmos_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ const struct cmos_win_size *win = &cmos_supported_win_sizes[index];
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ extern int nmi_running(void);
+ return nmi_running() ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ if (sensor_identify(sd)) {
+ return -1;
+ }
+
+ sensor_write_array(sd, nmi_default_regs_init,
+ ARRAY_SIZE(nmi_default_regs_init));
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ sensor_write_array(sd, nmi_default_regs_exit,
+ ARRAY_SIZE(nmi_default_regs_exit));
+ return 0;
+}
+
+static struct cmos_subdev_ops nmi_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+ .enum_framesizes = sensor_enum_framesizes,
+};
+
+struct cmos_subdev nmi = {
+ .name = "nmi",
+ .i2c_addr = 0xff,
+ .id = 0xff,
+ .max_width = 320,
+ .max_height = 240,
+ .ops = &nmi_ops,
+};
+
+#if 0
+int nmi_cmos_init(void)
+{
+ return cmos_register_sudbdev(&nmi);
+}
+
+void nmi_cmos_exit(void)
+{
+ return cmos_unregister_subdev(&nmi);
+}
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/nmi5625/nmidrv_cmos.h b/drivers/media/video/wmt_v4l2/sensors/nmi5625/nmidrv_cmos.h
new file mode 100755
index 00000000..4b24659c
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/nmi5625/nmidrv_cmos.h
@@ -0,0 +1,219 @@
+
+#ifndef __NMI_H_
+#define __NMI_H_
+
+// Scene Mode
+uint8_t nmi_scene_mode_auto[] = {
+};
+
+uint8_t nmi_scene_mode_night[] = {
+};
+
+
+// White Balance
+uint8_t nmi_wb_auto [] = {
+};
+
+uint8_t nmi_wb_incandescent [] = {
+};
+
+uint8_t nmi_wb_fluorescent [] = {
+};
+
+uint8_t nmi_wb_daylight [] = {
+};
+
+uint8_t nmi_wb_cloudy [] = {
+};
+
+uint8_t nmi_wb_tungsten [] = {
+};
+
+
+// Exposure
+uint8_t nmi_exposure_neg6[] = {
+};
+
+uint8_t nmi_exposure_neg3[] = {
+};
+
+uint8_t nmi_exposure_zero[] = {
+};
+
+uint8_t nmi_exposure_pos3[] = {
+};
+
+uint8_t nmi_exposure_pos6[] = {
+};
+
+
+// Color Effect
+uint8_t nmi_colorfx_none[] = {
+};
+
+uint8_t nmi_colorfx_bw[] = {
+};
+
+uint8_t nmi_colorfx_sepia[] = {
+};
+
+uint8_t nmi_colorfx_negative[] = {
+};
+
+uint8_t nmi_colorfx_emboss[] = {
+};
+
+uint8_t nmi_colorfx_sketch[] = {
+};
+
+uint8_t nmi_colorfx_sky_blue[] = {
+};
+
+uint8_t nmi_colorfx_grass_green[] = {
+};
+
+uint8_t nmi_colorfx_skin_whiten[] = {
+};
+
+uint8_t nmi_colorfx_vivid[] = {
+};
+
+// Brightness
+uint8_t nmi_brightness_neg4[] = {
+};
+
+uint8_t nmi_brightness_neg3[] = {
+};
+
+uint8_t nmi_brightness_neg2[] = {
+};
+
+uint8_t nmi_brightness_neg1[] = {
+};
+
+uint8_t nmi_brightness_zero[] = {
+};
+
+uint8_t nmi_brightness_pos1[] = {
+};
+
+uint8_t nmi_brightness_pos2[] = {
+};
+
+uint8_t nmi_brightness_pos3[] = {
+};
+
+uint8_t nmi_brightness_pos4[] = {
+};
+
+// Contrast
+uint8_t nmi_contrast_neg4[] = {
+};
+
+uint8_t nmi_contrast_neg3[] = {
+};
+
+uint8_t nmi_contrast_neg2[] = {
+};
+
+uint8_t nmi_contrast_neg1[] = {
+};
+
+uint8_t nmi_contrast_zero[] = {
+};
+
+uint8_t nmi_contrast_pos1[] = {
+};
+
+uint8_t nmi_contrast_pos2[] = {
+};
+
+uint8_t nmi_contrast_pos3[] = {
+};
+
+uint8_t nmi_contrast_pos4[] = {
+};
+
+// Saturation
+uint8_t nmi_saturation_neg4[] = {
+};
+
+uint8_t nmi_saturation_neg3[] = {
+};
+
+uint8_t nmi_saturation_neg2[] = {
+};
+
+uint8_t nmi_saturation_neg1[] = {
+};
+
+uint8_t nmi_saturation_zero[] = {
+};
+
+uint8_t nmi_saturation_pos1[] = {
+};
+
+uint8_t nmi_saturation_pos2[] = {
+};
+
+uint8_t nmi_saturation_pos3[] = {
+};
+
+uint8_t nmi_saturation_pos4[] = {
+};
+
+
+// Resolution
+
+// QCIF
+uint8_t nmi_176_144_regs[]={
+};
+
+// QVGA
+uint8_t nmi_320_240_regs[]={
+};
+
+// CIF
+uint8_t nmi_352_288_regs[]={
+};
+
+// VGA
+uint8_t nmi_640_480_regs[]={
+};
+
+// SVGA
+uint8_t nmi_800_600_regs[]={
+};
+
+// XGA
+uint8_t nmi_1024_768_regs[]={
+};
+
+// 720p
+uint8_t nmi_1280_720_regs[]={
+};
+
+// UXGA
+uint8_t nmi_1600_1200_regs[]={
+};
+
+// 1080p
+uint8_t nmi_1920_1080_regs[]={
+};
+
+// QXGA
+uint8_t nmi_2048_1536_regs[]={
+};
+
+uint8_t nmi_2592_1944_regs[]={
+};
+
+// Initiliztion
+uint8_t nmi_default_regs_init[] = {
+};
+
+// Exit
+uint8_t nmi_default_regs_exit[] = {
+};
+
+#endif /* #ifndef __NMI_H_ */
diff --git a/drivers/media/video/wmt_v4l2/sensors/nmi5625/nmidrv_custom_wmt.h b/drivers/media/video/wmt_v4l2/sensors/nmi5625/nmidrv_custom_wmt.h
new file mode 100755
index 00000000..0832f3a5
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/nmi5625/nmidrv_custom_wmt.h
@@ -0,0 +1,70 @@
+
+#ifndef _NMIDRV_CUSTOM_H_
+#define _NMIDRV_CUSTOM_H_
+/******************************************************************************
+**
+** Copyright (c) Newport Media Inc. All rights reserved.
+**
+** Module Name: nmidrv_custom.h
+**
+** This module implements the porting interface for the NMI ATV driver.
+** It can be used as an example for the driver porting.
+**
+**
+*******************************************************************************/
+
+#include <mach/wmt_iomux.h>
+
+//if pin is unuse,please define it as 0xff
+#define NMI_PIN_UNUSE 0xFF
+
+//step 1: configure NMI600 power pin and reset pin .
+//if VDDIO and VCORE use each pin, define NMI_POWER_VDDIO_PIN & NMI_POWER_VCORE_PIN
+//if VDDIO and VCORE use same pin, only define NMI_POWER_VDDIO_PIN,
+//and define NMI_POWER_VCORE_PIN as 0xff !!!
+#define NMI_POWER_VDDIO_PIN NMI_PIN_UNUSE
+#define NMI_POWER_VCORE_PIN WMT_PIN_GP0_GPIO2
+
+#define NMI_RESET_PIN WMT_PIN_GP62_SUSGPIO0
+
+
+// step 2: configure NMI600 i2c bus .
+//if use hardware I2C , please define NMI_HW_I2C
+#define NMI_HW_I2C
+#define NMI_HW_I2C_PORT 4
+#define NMI_I2C_RW_LENGTH 256
+
+//if use gpio i2c , please modify the defination below
+// note: when nmi600 power off, nmi600 iic sda and scl will be set to input mode.
+// so if other devices share iic with nmi600, maybe i2c needs initilize again.
+#define NMI_SCL_PIN NMI_PIN_UNUSE
+#define NMI_SDA_PIN NMI_PIN_UNUSE
+
+/******************************************************************************
+**
+** (B) Nmi Function Prototype (for porting on MTK 65XX)
+**
+*******************************************************************************/
+#define NMI_SET_GPIO_MODE_ENABLE(PIN) if(PIN!=NMI_PIN_UNUSE) \
+ gpio_request(PIN, "ATV,DTV")
+
+#define NMI_SET_GPIO_DIR(PIN,DIR) \
+do { \
+ if (PIN!=NMI_PIN_UNUSE) { \
+ if (DIR) \
+ gpio_direction_output(PIN, 1); \
+ else \
+ gpio_direction_input(PIN); \
+ } \
+} while (0)
+
+#define NMI_SET_GPIO_PULL_DISABLE(PIN) if(PIN!=NMI_PIN_UNUSE) \
+ wmt_gpio_setpull(PIN, WMT_GPIO_PULL_NONE)
+#define NMI_SET_GPIO_PULL_ENABLE(PIN) if(PIN!=NMI_PIN_UNUSE) \
+ wmt_gpio_setpull(PIN, WMT_GPIO_PULL_UP)
+#define NMI_SET_GPIO_LEVEL(PIN,LEVEL) if(PIN!=NMI_PIN_UNUSE) \
+ gpio_set_value(PIN, LEVEL)
+#define NMI_GET_GPIO_LEVEL(PIN) gpio_get_value(PIN)
+
+
+#endif /*_NMIDRV_CUSTOM_H_*/
diff --git a/drivers/media/video/wmt_v4l2/sensors/nmi5625/nmidrv_i2c.c b/drivers/media/video/wmt_v4l2/sensors/nmi5625/nmidrv_i2c.c
new file mode 100755
index 00000000..55df9fdd
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/nmi5625/nmidrv_i2c.c
@@ -0,0 +1,484 @@
+/******************************************************************************
+**
+** Copyright (c) Newport Media Inc. All rights reserved.
+**
+** Module Name: nmidrv_i2c.c
+**
+** This module implements the i2c interface NMI ATV/DTV
+**
+**
+*******************************************************************************/
+#include "nmidrv_i2c.h"
+#include "nmidrv_kernel.h"
+#include "nmidrv_custom_mtk.h"
+
+#ifndef NMI_HW_I2C
+
+#include "mach/mt6516_gpio.h"
+#include <linux/delay.h>
+
+/******************************************************************************
+**
+** I2c Defines
+**
+*******************************************************************************/
+#undef _DRIVE_ //this mode is nmi600 Recommend mode. please don't change it.
+
+#define I2C_DELAY_UNIT 2
+
+#define set_sda_output() NMI_SET_GPIO_DIR(NMI_SDA_PIN,1); \
+ NMI_SET_GPIO_LEVEL(NMI_SDA_PIN,0)
+#define set_sda_input() NMI_SET_GPIO_DIR(NMI_SDA_PIN,0)
+#define set_scl_output() NMI_SET_GPIO_DIR(NMI_SCL_PIN,1); \
+ NMI_SET_GPIO_LEVEL(NMI_SCL_PIN,0)
+#define set_scl_input() NMI_SET_GPIO_DIR(NMI_SCL_PIN,0)
+
+#ifdef _DRIVE_
+#define set_i2c_scl_PIN NMI_SET_GPIO_LEVEL(NMI_SCL_PIN,1)
+#define clr_i2c_scl_PIN NMI_SET_GPIO_LEVEL(NMI_SCL_PIN,0)
+#define set_i2c_sda_PIN NMI_SET_GPIO_LEVEL(NMI_SDA_PIN,1)
+#define clr_i2c_sda_PIN NMI_SET_GPIO_LEVEL(NMI_SDA_PIN,0)
+#else
+#define set_i2c_scl_PIN NMI_SET_GPIO_LEVEL(NMI_SCL_PIN,1)
+#define clr_i2c_scl_PIN NMI_SET_GPIO_LEVEL(NMI_SCL_PIN,0)
+#define set_i2c_sda_PIN NMI_SET_GPIO_DIR(NMI_SDA_PIN,0)
+#define clr_i2c_sda_PIN NMI_SET_GPIO_DIR(NMI_SDA_PIN,1)
+#endif
+
+#define get_i2c_sda_PIN NMI_GET_GPIO_LEVEL(NMI_SDA_PIN)
+#define get_i2c_scl_PIN NMI_GET_GPIO_LEVEL(NMI_SCL_PIN)
+
+
+
+
+/******************************************************************************
+**
+** I2c Platform Functions
+**
+*******************************************************************************/
+
+static void i2c_delay(unsigned int time)
+{
+#if 0
+ while(time--) {
+ ;
+ }
+#else
+ udelay(time);
+#endif
+}
+
+static void i2c_begin(void)
+{
+#ifdef _DRIVE_
+
+ /* set SDA to high */
+ set_i2c_sda_PIN;
+ i2c_delay(I2C_DELAY_UNIT << 0);
+
+ /* set SCL to high */
+ set_i2c_scl_PIN;
+ i2c_delay(I2C_DELAY_UNIT << 0);
+
+ /* set SDA to low */
+ clr_i2c_sda_PIN;
+ i2c_delay(I2C_DELAY_UNIT << 0);
+
+ /* set SCL to low */
+ clr_i2c_scl_PIN;
+ i2c_delay(I2C_DELAY_UNIT << 0);
+
+#else
+
+ /* set SDA to high */
+ set_sda_input();
+ i2c_delay(I2C_DELAY_UNIT << 0);
+
+ /* set SCL to high */
+ set_scl_input();
+ i2c_delay(I2C_DELAY_UNIT << 0);
+
+ /* set SDA to low */
+ set_sda_output();
+ i2c_delay(I2C_DELAY_UNIT << 0);
+
+ /* set SCL to low */
+ set_scl_output();
+ i2c_delay(I2C_DELAY_UNIT << 0);
+#endif
+
+}
+
+static void i2c_end(void)
+{
+#ifdef _DRIVE_
+ /* set SDA to low */
+ clr_i2c_sda_PIN;
+ i2c_delay(I2C_DELAY_UNIT << 2);
+
+ /* set SCL to high */
+ set_i2c_scl_PIN;
+ i2c_delay(I2C_DELAY_UNIT << 0);
+
+ /* set SDA to high */
+ set_i2c_sda_PIN;
+ i2c_delay(I2C_DELAY_UNIT << 0);
+
+#else
+ set_sda_output();
+ i2c_delay(I2C_DELAY_UNIT << 2);
+ set_scl_input();
+ i2c_delay(I2C_DELAY_UNIT << 3);
+ set_sda_input();
+ i2c_delay(I2C_DELAY_UNIT << 4);
+#endif
+}
+
+static void i2c_write_ask(unsigned char flag)
+{
+#ifdef _DRIVE_
+ /* set SDA to high to ack */
+ if(flag)
+ set_i2c_sda_PIN;
+ else
+ clr_i2c_sda_PIN;
+ i2c_delay(I2C_DELAY_UNIT << 0);
+
+ /* toggle clock */
+ set_i2c_scl_PIN;
+ i2c_delay(I2C_DELAY_UNIT << 0);
+ clr_i2c_scl_PIN;
+ i2c_delay(I2C_DELAY_UNIT << 0);
+
+ /* set SDA to 1 */
+ set_i2c_sda_PIN;
+ i2c_delay(I2C_DELAY_UNIT << 0);
+
+#else
+
+ //set_sda_output();
+
+ if(flag)
+ set_sda_input();
+ //else
+ //set_sda_output();
+
+ i2c_delay(I2C_DELAY_UNIT << 0);
+ set_scl_input();
+ i2c_delay(I2C_DELAY_UNIT << 0);
+ set_scl_output();
+ i2c_delay(I2C_DELAY_UNIT << 0);
+ set_sda_input();
+ i2c_delay(I2C_DELAY_UNIT << 0);
+#endif
+}
+
+static unsigned char i2c_read_ack(void)
+{
+ unsigned char ret;
+
+#ifdef _DRIVE_
+ /* set SDA to input */
+ set_sda_input();
+ /* delay */
+ i2c_delay(I2C_DELAY_UNIT << 0);
+
+ /* read */
+ if (!get_i2c_sda_PIN) {
+ ret = 1;
+ } else {
+ ret = 0;
+ dPrint(N_ERR,"[MTKI2C] 1. i2c_read_ack (Error.. No Ack received)\n");
+ i2c_delay(I2C_DELAY_UNIT << 0);
+ if (!get_i2c_sda_PIN) {
+ ret = 1;
+ dPrint(N_ERR,"[MTKI2C] 2.i2c_read_ack (Correct after additional delay.)\n");
+ } else {
+ ret = 0;
+ dPrint(N_ERR,"[MTKI2C] 2.i2c_read_ack (Error.. No Ack received)\n");
+ }
+ }
+
+ /* set SCL high */
+ set_i2c_scl_PIN;
+ i2c_delay(I2C_DELAY_UNIT << 0);
+
+ /* set SCL low */
+ clr_i2c_scl_PIN;
+ i2c_delay(I2C_DELAY_UNIT << 0);
+
+ /* set SDA back to output */
+ set_sda_output();
+
+#else
+
+ set_sda_input();
+ i2c_delay(I2C_DELAY_UNIT << 0);
+
+ if (!get_i2c_sda_PIN) {
+ ret = 1;
+ } else {
+ ret = 0;
+ dPrint(N_ERR,"[MTKI2C] 1. i2c_read_ack (Error.. No Ack received)\n");
+ i2c_delay(I2C_DELAY_UNIT << 0);
+ if (!get_i2c_sda_PIN) {
+ ret = 1;
+ dPrint(N_ERR,"[MTKI2C] 2.i2c_read_ack (Correct after additional delay.)\n");
+ } else {
+ ret = 0;
+ dPrint(N_ERR,"[MTKI2C] 2.i2c_read_ack (Error.. No Ack received)\n");
+ }
+ }
+
+ set_scl_input();
+ i2c_delay(I2C_DELAY_UNIT << 0);
+ set_scl_output();
+ i2c_delay(I2C_DELAY_UNIT << 0);
+
+#endif
+ return ret;
+}
+
+static unsigned char i2c_read_byte(void)
+{
+ unsigned char i;
+ unsigned char ret = 0;
+
+#ifdef _DRIVE_
+
+ /* set SDA input */
+ set_sda_input();
+
+ /* loop */
+ for (i = 0; i < 8; i++) {
+ /* delay */
+ i2c_delay(I2C_DELAY_UNIT << 0);
+
+ /* set SCL high */
+ set_i2c_scl_PIN;
+ /* delay */
+ i2c_delay(I2C_DELAY_UNIT << 0);
+
+ /* read SDA */
+ ret = ret<<1;
+ if (get_i2c_sda_PIN)
+ ret |= 1;
+ /* delay */
+ i2c_delay(I2C_DELAY_UNIT << 0);
+
+ /* set SCL low */
+ clr_i2c_scl_PIN;
+ /* delay */
+ i2c_delay(I2C_DELAY_UNIT << 0);
+
+ /* if end, set SDA output */
+ if (i == 7) {
+ set_sda_output();
+ }
+ /* delay */
+ i2c_delay(I2C_DELAY_UNIT << 0);
+ }
+
+#else
+ int retry,retry_val = 10000000;
+
+ ret = 0;
+
+ set_sda_input();
+ for (i = 0; i < 8; i++) {
+ i2c_delay(I2C_DELAY_UNIT << 0);
+
+ set_scl_input();
+
+ i2c_delay(I2C_DELAY_UNIT << 0);
+ ret = ret<<1;
+ if (get_i2c_sda_PIN)
+ ret |= 1;
+ i2c_delay(I2C_DELAY_UNIT << 0);
+
+ retry = retry_val;
+ while (retry >= 0)
+ {
+ if (get_i2c_scl_PIN)
+ break;
+ else
+ {
+ i2c_delay(I2C_DELAY_UNIT << 0);
+ retry--;
+ }
+ }
+
+ //if (retry != retry_val)
+ if (retry < (retry_val-10000))
+ {
+ //NMI_ERROR("[MTKI2C] i2c_read_byte: retry = %d\n",retry);
+ }
+
+ set_scl_output();
+ i2c_delay(I2C_DELAY_UNIT << 0);
+
+ if (i==7){
+ set_sda_output();
+ }
+ i2c_delay(I2C_DELAY_UNIT << 0);
+ }
+
+#endif
+
+ return ret;
+}
+
+static unsigned char i2c_write_byte(unsigned char data)
+{
+ unsigned char i;
+
+#ifdef _DRIVE_
+ /* loop */
+ for (i = 0; i < 8; i++) {
+ /* set SDA high or low depend on the data bit */
+ if (data & 0x80)
+ set_i2c_sda_PIN;
+ else
+ clr_i2c_sda_PIN;
+ /* delay */
+ i2c_delay(I2C_DELAY_UNIT << 0);
+
+ data <<= 1;
+
+ /* set SCL high */
+ set_i2c_scl_PIN;
+ /* delay */
+ i2c_delay(I2C_DELAY_UNIT << 0);
+
+ /* set SCL low */
+ clr_i2c_scl_PIN;
+ /* delay */
+ i2c_delay(I2C_DELAY_UNIT << 0);
+ }
+#else
+ int retry, retry_val = 10000000;
+
+ //set_sda_output();
+
+ for (i = 0; i < 8; i++) {
+ if (data & 0x80)
+ set_sda_input();
+ else
+ set_sda_output();
+
+ data <<= 1;
+
+ i2c_delay(I2C_DELAY_UNIT << 0);
+ set_scl_input();
+ i2c_delay(I2C_DELAY_UNIT << 0);
+ retry = retry_val;
+ while (retry >= 0)
+ {
+ if (get_i2c_scl_PIN)
+ break;
+ else
+ {
+ i2c_delay(I2C_DELAY_UNIT << 0);
+ retry--;
+ }
+ }
+
+ //if (retry != retry_val)
+ if (retry < (retry_val-10000))
+ {
+ dPrint(N_TRACE,"i2c write_byte: retry = %d\n",retry);
+ }
+ set_scl_output();
+ i2c_delay(I2C_DELAY_UNIT << 0);
+ }
+
+#endif
+
+ return i2c_read_ack();
+}
+
+/******************************************************************************
+**
+** I2c Global Functions
+**
+*******************************************************************************/
+
+int nmi_i2c_init(void)
+{
+ dPrint(N_TRACE,"nmi_i2c_init: enter...\n");
+
+ //disable all inside pull( pullup & pulldown)
+ NMI_SET_GPIO_PULL_DISABLE(NMI_SDA_PIN);
+ NMI_SET_GPIO_PULL_DISABLE(NMI_SCL_PIN);
+ //set gpio mode
+ NMI_SET_GPIO_MODE_ENABLE(NMI_SDA_PIN);
+ NMI_SET_GPIO_MODE_ENABLE(NMI_SCL_PIN);
+
+#ifdef _DRIVE_
+ //set output mode
+ NMI_SET_GPIO_DIR( NMI_SDA_PIN,1);
+ NMI_SET_GPIO_DIR( NMI_SCL_PIN,1);
+ //set gpio high
+ NMI_SET_GPIO_LEVEL( NMI_SDA_PIN,1);
+ NMI_SET_GPIO_LEVEL( NMI_SCL_PIN,1);
+#else
+ //set input mode
+ NMI_SET_GPIO_DIR( NMI_SDA_PIN,0);
+ NMI_SET_GPIO_DIR( NMI_SCL_PIN,0);
+#endif
+
+ dPrint(N_TRACE,"nmi_i2c_init: exit...\n");
+
+ return 1;
+}
+
+void nmi_i2c_deinit(void)
+{
+ dPrint(N_TRACE,"nmi_i2c_deinit: enter...\n");
+
+ NMI_SET_GPIO_MODE_ENABLE(NMI_SDA_PIN);
+ NMI_SET_GPIO_MODE_ENABLE(NMI_SCL_PIN);
+
+ //set as input
+ NMI_SET_GPIO_DIR( NMI_SDA_PIN,0);
+ NMI_SET_GPIO_DIR( NMI_SCL_PIN,0);
+
+ dPrint(N_TRACE,"nmi_i2c_deinit: exit...\n");
+
+}
+
+int nmi_i2c_read(unsigned char adr, unsigned char *b, unsigned long sz)
+{
+ int i;
+
+ i2c_begin();
+ i2c_write_byte((adr << 1)|0x1);
+
+ for(i = 0; i < sz; i++) {
+ b[i] = i2c_read_byte();
+
+ if(i == sz-1)
+ i2c_write_ask(1);
+ else
+ i2c_write_ask(0);
+ }
+
+ i2c_end();
+ return 1;
+}
+
+int nmi_i2c_write(unsigned char adr, unsigned char *b, unsigned long sz)
+{
+ int i;
+
+ i2c_begin();
+
+ i2c_write_byte((adr << 1));
+ for(i = 0; i < sz; i++) {
+ i2c_write_byte(b[i]);
+ }
+
+ i2c_end();
+ return 1;
+}
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/nmi5625/nmidrv_i2c.h b/drivers/media/video/wmt_v4l2/sensors/nmi5625/nmidrv_i2c.h
new file mode 100755
index 00000000..5640295b
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/nmi5625/nmidrv_i2c.h
@@ -0,0 +1,14 @@
+
+#ifndef _NMIDRV_I2C_H_
+#define _NMIDRV_I2C_H_
+
+#include "nmidrv_custom_mtk.h"
+
+//enternal interface
+#ifndef NMI_HW_I2C
+int nmi_i2c_init(void);
+void nmi_i2c_deinit(void);
+int nmi_i2c_read(unsigned char, unsigned char *, unsigned long);
+int nmi_i2c_write(unsigned char, unsigned char *, unsigned long);
+#endif
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/nmi5625/nmidrv_kernel.c b/drivers/media/video/wmt_v4l2/sensors/nmi5625/nmidrv_kernel.c
new file mode 100755
index 00000000..601c2072
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/nmi5625/nmidrv_kernel.c
@@ -0,0 +1,435 @@
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/kdev_t.h>
+#include <linux/cdev.h>
+#include <asm/uaccess.h>
+#include <linux/gpio.h>
+#include <linux/fs.h>
+#include <mach/wmt_env.h>
+
+#include "nmidrv_kernel.h"
+#include "nmidrv_custom_wmt.h"
+
+extern int nmi_cmos_init(void);
+extern void nmi_cmos_exit(void);
+
+struct nmi_5625_dev {
+ struct i2c_client *i2c_client_atv;
+
+ struct mutex mu;
+ struct class *tv_class;
+ dev_t devn;
+ struct cdev cdv;
+};
+
+static int already_init = 0;
+static struct nmi_5625_dev nd;
+static u8 i2cBuf[32];
+
+static bool g_bIsAtvStart = false;
+
+int nmi_running(void)
+{
+ return g_bIsAtvStart;
+}
+EXPORT_SYMBOL(nmi_running);
+
+/**************************************************************
+
+ file operation:
+
+ **************************************************************/
+
+static int nmi5625_open(struct inode *inode, struct file *file)
+{
+ int ret = 0;
+
+ func_enter();
+ if (!already_init) {
+ ret = -ENODEV;
+ goto _fail_;
+ }
+
+ /***************************************
+ initialize 2.8V 1.2V RESET GPIO mode for reference
+ ****************************************/
+
+ //PWR Enable
+ NMI_SET_GPIO_MODE_ENABLE(NMI_POWER_VDDIO_PIN);
+ NMI_SET_GPIO_DIR(NMI_POWER_VDDIO_PIN, 1);
+ NMI_SET_GPIO_PULL_DISABLE(NMI_POWER_VDDIO_PIN);
+ NMI_SET_GPIO_LEVEL(NMI_POWER_VDDIO_PIN, 0);
+
+ //LDO_Enable
+ NMI_SET_GPIO_MODE_ENABLE(NMI_POWER_VCORE_PIN);
+ NMI_SET_GPIO_DIR(NMI_POWER_VCORE_PIN, 1);
+ NMI_SET_GPIO_PULL_DISABLE(NMI_POWER_VCORE_PIN);
+ NMI_SET_GPIO_LEVEL(NMI_POWER_VCORE_PIN, 0);
+
+ //Reset
+ NMI_SET_GPIO_MODE_ENABLE(NMI_RESET_PIN);
+ NMI_SET_GPIO_DIR(NMI_RESET_PIN, 1);
+ NMI_SET_GPIO_PULL_DISABLE(NMI_RESET_PIN);
+ NMI_SET_GPIO_LEVEL(NMI_RESET_PIN, 0);
+
+#ifndef NMI_HW_I2C
+ nmi_i2c_init();
+#endif
+ file->private_data = (void *)&nd;
+
+_fail_:
+
+ func_exit();
+ return ret;
+}
+
+static int nmi5625_release(struct inode * inode, struct file * file)
+{
+ int ret = 0;
+ //struct nmi_5625_dev *d = file->private_data;
+
+ /**
+ nothing to do
+ **/
+ func_enter();
+
+#ifndef NMI_HW_I2C
+ nmi_i2c_deinit();
+#endif
+ func_exit();
+ return ret;
+}
+
+static long nmi5625_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct nmi_5625_dev *d = file->private_data;
+ int ret = 0;
+
+ //func_enter();
+
+ switch ((cmd&0xffff0000)) {
+
+ case NMI_POWER_VDDIO_CTL:
+ dPrint(N_TRACE,"NM5625_PWR_2P8_CTL, power %s\n", (arg==1)?"on":"off");
+ if (arg == 1){ /* on */
+ NMI_SET_GPIO_LEVEL(NMI_POWER_VDDIO_PIN, 1);
+ }
+ else{
+ NMI_SET_GPIO_LEVEL(NMI_POWER_VDDIO_PIN, 0);
+ }
+ break;
+
+ case NMI_POWER_VCORE_CTL:
+ dPrint(N_TRACE,"NM5625_PWR_1P2_CTL, power %s\n", (arg==1)?"on":"off");
+ if (arg == 1){ /* on */
+ NMI_SET_GPIO_LEVEL(NMI_POWER_VCORE_PIN, 1);
+ g_bIsAtvStart = 1;
+ }
+ else{
+ NMI_SET_GPIO_LEVEL(NMI_POWER_VCORE_PIN, 0);
+ g_bIsAtvStart = 0;
+ }
+ break;
+
+ case NMI_FM_POWER_VCORE_CTL:
+ dPrint(N_TRACE,"NMI_FM_POWER_VCORE_CTL, power %s\n", (arg==1)?"on":"off");
+ if (arg == 1){ /* on */
+ NMI_SET_GPIO_LEVEL(NMI_POWER_VCORE_PIN, 1);
+ //g_bIsAtvStart = 1;
+ }
+ else{
+ NMI_SET_GPIO_LEVEL(NMI_POWER_VCORE_PIN, 0);
+ //g_bIsAtvStart = 0;
+ }
+ break;
+ case NMI_RESET_CTL:
+ dPrint(N_TRACE,"NM5625_ATV_RESET_CTL, reset %s\n", (arg==1)?"high":"low");
+ if (arg == 1) {
+ NMI_SET_GPIO_LEVEL(NMI_RESET_PIN, 1);
+ }
+ else {
+ NMI_SET_GPIO_LEVEL(NMI_RESET_PIN, 0);
+ }
+ break;
+ case NMI_I2C_READ:
+ {
+ u8 *kbuf = &i2cBuf[0];
+ int size = cmd&0xffff; /* Note: I used the lower 16 bits for size */
+ int len = size;
+
+ dPrint(N_TRACE,"NM5625_ATV_I2C_READ\n");
+ mutex_lock(&d->mu);
+
+#ifdef NMI_HW_I2C
+
+ while(len) {
+ int sz;
+ if (len > NMI_I2C_RW_LENGTH)
+ sz = NMI_I2C_RW_LENGTH;
+ else
+ sz = len;
+ ret = i2c_master_recv(d->i2c_client_atv, kbuf, sz);
+ if (ret < 0) {
+ dPrint(N_ERR, "nmi: failed i2c read...(%d)\n", ret);
+ //kfree(kbuf);
+ mutex_unlock(&d->mu);
+ goto _fail_;
+ }
+ kbuf += NMI_I2C_RW_LENGTH;
+ len -= sz;
+ }
+
+#else
+ ret = nmi_i2c_read(0x60,kbuf,size);
+#endif
+ dPrint(N_TRACE,"kernel:nmi_i2c_read buf is (%p), size is (%d)\n",kbuf,size);
+
+ if (copy_to_user((void *)arg, i2cBuf, size) ) {
+ dPrint(N_ERR, "nmi: failed copy to user...\n");
+ ret = -EFAULT;
+ //kfree(kbuf);
+ mutex_unlock(&d->mu);
+ goto _fail_;
+ }
+ //kfree(kbuf);
+ mutex_unlock(&d->mu);
+ }
+ break;
+ case NMI_I2C_WRITE:
+ {
+ u8 *kbuf = &i2cBuf[0];
+ int size = cmd&0xffff; /* Note: I used the lower 16 bits for size */
+ int len = size;
+ dPrint(N_TRACE,"NM5625_ATV_I2C_WRITE\n");
+ if (copy_from_user((void *)kbuf, (void *)arg, size)) {
+ dPrint(N_ERR, "nmi: failed copy from user...\n");
+ ret = -EFAULT;
+ goto _fail_;
+ }
+ mutex_lock(&d->mu);
+
+#ifdef NMI_HW_I2C
+
+ while (len) {
+ int sz;
+ if (len > NMI_I2C_RW_LENGTH)
+ sz = NMI_I2C_RW_LENGTH;
+ else
+ sz = len;
+
+ ret = i2c_master_send(d->i2c_client_atv, kbuf, sz);
+
+ if (ret < 0) {
+ dPrint(N_ERR, "nmi: failed i2c write...(%d)\n", ret);
+ //kfree(kbuf);
+ mutex_unlock(&d->mu);
+ goto _fail_;
+ }
+ kbuf += NMI_I2C_RW_LENGTH;
+ len -= sz;
+ }
+
+#else
+ ret = nmi_i2c_write(0x60,kbuf,size);
+#endif
+ dPrint(N_TRACE,"kernel:nmi_i2c_write buf is (%p), size is (%d)\n",kbuf,size);
+
+ mutex_unlock(&d->mu);
+ }
+ break;
+ default:
+ break;
+ }
+
+_fail_:
+ //func_exit();
+ dPrint(N_TRACE, "nmi_ioctl return value...(%d)\n", ret);
+ return ret;
+}
+
+static const struct file_operations nmi5625_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = nmi5625_ioctl,
+ .open = nmi5625_open,
+ .release = nmi5625_release,
+};
+
+/**************************************************************
+
+i2c:
+
+ **************************************************************/
+
+static int nmi5625_remove(struct i2c_client *client)
+{
+ return 0;
+}
+
+static int nmi5625_detect(struct i2c_client *client, int kind, struct i2c_board_info *info)
+{
+ strcpy(info->type, "nmiatv");
+ return 0;
+}
+
+static int nmi5625_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ int ret = 0;
+ struct device *dev;
+ func_enter();
+
+ if (!already_init) {
+ memset(&nd, 0, sizeof(struct nmi_5625_dev));
+
+ /**
+ initialize mutex
+ **/
+ mutex_init(&nd.mu);
+
+ /**
+ register our driver
+ **/
+ if ((ret = alloc_chrdev_region (&nd.devn, 0, 1, "nmi")) < 0) {
+ dPrint(N_ERR, "nmi: failed unable to get major...%d\n", ret);
+ goto _fail_;
+ }
+ dPrint(N_INFO, "nmi:dynamic major(%d),minor(%d)\n", MAJOR(nd.devn), MINOR(nd.devn));
+
+ cdev_init(&nd.cdv, &nmi5625_fops);
+ nd.cdv.owner = THIS_MODULE;
+ ret = cdev_add(&nd.cdv, nd.devn, 1);
+ if (ret) {
+ dPrint(N_ERR, "nmi: failed to add device...%d\n", ret);
+ goto _fail_;
+ }
+
+
+ nd.tv_class = class_create(THIS_MODULE, "atv");
+ if (IS_ERR(nd.tv_class)) {
+ dPrint(N_ERR, "nmi: failed to create the atv class\n");
+ }
+
+ dev = device_create(nd.tv_class, NULL, nd.devn, NULL, "nmi");
+ if (IS_ERR(dev)) {
+ dPrint(N_ERR, "nmi: failed to create device\n");
+ }
+ /*User interface end */
+
+ already_init = 1;
+ }
+
+ nd.i2c_client_atv = client;
+
+_fail_:
+
+ func_exit();
+ return ret;
+}
+
+static const struct i2c_device_id nmi5625_id[] = {
+ {"nmiatv", 0},
+ {},
+};
+
+//static unsigned short force[] = {2, 0xc0, I2C_CLIENT_END, I2C_CLIENT_END};
+//static const unsigned short * const forces[] = { force, NULL };
+//static struct i2c_client_address_data addr_data = { .forces = forces,};
+
+static struct i2c_driver nmi5625_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "nmiatv",
+ },
+ .probe = nmi5625_probe,
+ .detect = nmi5625_detect,
+ .remove = nmi5625_remove,
+ .id_table = nmi5625_id,
+ // .address_data =&addr_data,
+};
+
+static struct i2c_board_info nmi5625_i2c_dev = {
+ I2C_BOARD_INFO("nmiatv", 0x60),
+};
+
+/**************************************************************
+
+Module:
+
+ **************************************************************/
+
+static int parse_nmi_param(void)
+{
+ char env[] = "wmt.nmi.param";
+ char buf[64];
+ size_t l = sizeof(buf);
+ int nr = -EINVAL;
+
+ if (wmt_getsyspara(env, buf, &l) == 0) {
+ sscanf(buf, "i2c-%d", &nr);
+ pr_info("nmi5625 i2c adapter %d\n", nr);
+ }
+
+ return nr;
+}
+
+static __init int nmi5625_init(void)
+{
+ int ret;
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+ int nr;
+ func_enter();
+
+ nr = parse_nmi_param();
+ if (nr < 0)
+ return nr;
+
+ adapter = i2c_get_adapter(nr);
+ client = i2c_new_device(adapter, &nmi5625_i2c_dev);
+ i2c_put_adapter(adapter);
+ if (!client) {
+ printk("nmi i2c_new_device failed\n");
+ return -EINVAL;
+ }
+
+ ret = i2c_add_driver(&nmi5625_i2c_driver);
+ if (ret < 0) {
+ dPrint(N_ERR, "nmi: failed register i2c driver...(%d)\n", ret);
+ }
+
+ func_exit();
+
+ return ret;
+}
+
+static __exit void nmi5625_clean(void)
+{
+ func_enter();
+
+ i2c_del_driver(&nmi5625_i2c_driver);
+
+ if (already_init) {
+ device_destroy(nd.tv_class, nd.devn);
+ class_destroy(nd.tv_class);
+ cdev_del(&nd.cdv);
+ unregister_chrdev_region(nd.devn, 1);
+ already_init = 0;
+ i2c_unregister_device(nd.i2c_client_atv);
+ nd.i2c_client_atv = NULL;
+ }
+
+ func_exit();
+}
+
+module_init(nmi5625_init);
+module_exit(nmi5625_clean);
+
+MODULE_AUTHOR("nmi");
+MODULE_DESCRIPTION("nmi TV 5625 driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/nmi5625/nmidrv_kernel.h b/drivers/media/video/wmt_v4l2/sensors/nmi5625/nmidrv_kernel.h
new file mode 100755
index 00000000..1b9f116a
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/nmi5625/nmidrv_kernel.h
@@ -0,0 +1,42 @@
+#ifndef _NMIDRV_KERNEL_H_
+#define _NMIDRV_KERNEL_H_
+
+/**************************************************************
+ IO Control Command defination:
+**************************************************************/
+#define NMI_POWER_VDDIO_CTL 0x10000000
+#define NMI_POWER_VCORE_CTL 0x20000000
+#define NMI_RESET_CTL 0x30000000
+#define NMI_I2C_READ 0x40000000
+#define NMI_I2C_WRITE 0x50000000
+#define NMI_FM_POWER_VCORE_CTL 0x60000000
+
+/**************************************************************
+ Debug Trace Defination:
+**************************************************************/
+
+#define DEBUG
+
+#define N_INIT 0x00000001
+#define N_ERR 0x00000002
+#define N_FUNC 0x00000004
+#define N_TRACE 0x00000008
+#define N_INFO 0x00000010
+
+static unsigned int dflag = N_INIT|N_ERR|N_FUNC|N_INFO;
+
+#ifdef DEBUG
+#define dPrint(f, str...) if (dflag & f) printk (str)
+#else
+#define dPrint(f, str...) /* nothing */
+#endif
+
+#define func_enter() dPrint (N_TRACE, "nmi: %s...enter\n", __func__)
+#define func_exit() dPrint(N_TRACE, "nmi: %s...exit\n", __func__)
+
+
+#ifdef NMI_HW_I2C
+#define NMI_I2C_RW_LENGTH 256
+#endif
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/nt99141/nt99141.c b/drivers/media/video/wmt_v4l2/sensors/nt99141/nt99141.c
new file mode 100755
index 00000000..cfd725ec
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/nt99141/nt99141.c
@@ -0,0 +1,448 @@
+/*
+ * Driver for nt99141 CMOS Image Sensor from Novatek
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <asm/errno.h>
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "nt99141.h"
+
+//#define sensor_write_array(sd, arr, sz) cmos_init_16bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read16addr(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write16addr(sd->i2c_addr, reg, val)
+
+static int sensor_fps;
+static int sensor_set_fps(struct cmos_subdev *sd, int fps);
+static struct timer_list day_night_timer;
+static struct cmos_subdev *day_night_sd;
+static struct work_struct day_night_work;
+
+static void sensor_write_array(struct cmos_subdev *sd, uint32_t *arr, size_t sz)
+{
+ uint32_t reg, data;
+ int i;
+
+ for (i = 0; i < sz; i += 2) {
+ reg = arr[i];
+ data = arr[i+1];
+
+ if (reg == 0xffff)
+ msleep(data);
+ else
+ wmt_vid_i2c_write16addr(sd->i2c_addr, reg ,data);
+ }
+}
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint32_t *regs;
+ size_t size;
+};
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+ CMOS_WIN_SIZE("QVGA", 320, 240, nt99141_320_240_regs),
+ CMOS_WIN_SIZE("VGA", 640, 480, nt99141_640_480_regs),
+ CMOS_WIN_SIZE("720p", 1280, 720, nt99141_1280_720_regs),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint32_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = nt99141_wb_auto;
+ size = ARRAY_SIZE(nt99141_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = nt99141_wb_incandescent;
+ size = ARRAY_SIZE(nt99141_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = nt99141_wb_daylight;
+ size = ARRAY_SIZE(nt99141_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = nt99141_wb_cloudy;
+ size = ARRAY_SIZE(nt99141_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = nt99141_wb_fluorescent;
+ size = ARRAY_SIZE(nt99141_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ msleep(100);
+ return 0;
+}
+
+static int sensor_s_scenemode(struct cmos_subdev *sd, enum v4l2_scene_mode value)
+{
+ uint32_t *regs;
+ size_t size;
+
+ switch (value) {
+ case SCENE_MODE_AUTO:
+ regs = nt99141_scene_mode_auto;
+ size = ARRAY_SIZE(nt99141_scene_mode_auto);
+ break;
+ case SCENE_MODE_NIGHTSHOT:
+ regs = nt99141_scene_mode_night;
+ size = ARRAY_SIZE(nt99141_scene_mode_night);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_exposure(struct cmos_subdev *sd, enum v4l2_exposure_mode value)
+{
+ uint32_t *regs;
+ size_t size;
+
+ switch (value) {
+ case -2:
+ regs = nt99141_exposure_neg6;
+ size = ARRAY_SIZE(nt99141_exposure_neg6);
+ break;
+ case -1:
+ regs = nt99141_exposure_neg3;
+ size = ARRAY_SIZE(nt99141_exposure_neg3);
+ break;
+ case 0:
+ regs = nt99141_exposure_zero;
+ size = ARRAY_SIZE(nt99141_exposure_zero);
+ break;
+ case 1:
+ regs = nt99141_exposure_pos3;
+ size = ARRAY_SIZE(nt99141_exposure_pos3);
+ break;
+ case 2:
+ regs = nt99141_exposure_pos6;
+ size = ARRAY_SIZE(nt99141_exposure_pos6);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+ uint32_t data = sensor_read(sd, 0x3022);
+
+ switch (value) {
+ case 0:
+ data &= ~0x02;
+ break;
+ case 1:
+ data |= 0x02;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sensor_write(sd, 0x3022, data);
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+ uint32_t data = sensor_read(sd, 0x3022);
+
+ switch (value) {
+ case 0:
+ data &= ~0x01;
+ break;
+ case 1:
+ data |= 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sensor_write(sd, 0x3022, data);
+}
+
+
+static int sensor_s_DayNightMode(struct cmos_subdev *sd)
+{
+ uint32_t Data_EV = 0 ;
+
+ Data_EV = sensor_read(sd, 0x32d6);
+
+ if(Data_EV > 0x9A)
+ {
+ sensor_write(sd, 0x302a, 0x08);
+ }
+ else
+ {
+ sensor_write(sd, 0x302a, 0x04);
+ }
+
+ return 0;
+
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ case V4L2_CID_EXPOSURE:
+ return v4l2_ctrl_query_fill(qc, -2, 2, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return sensor_s_scenemode(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return sensor_s_exposure(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ default:
+ case WMT_V4L2_CID_CAMERA_FLASH:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win) {
+ pr_err("%s, s_mbus_fmt failed, width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, win->regs, win->size);
+ sensor_set_fps(sd,sensor_fps);
+ msleep(200);
+
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return 0;
+}
+
+static int sensor_enum_framesizes(struct cmos_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ const struct cmos_win_size *win = &cmos_supported_win_sizes[index];
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sensor_set_fps(struct cmos_subdev *sd, int fps)
+{
+ switch(fps){
+ case 5:
+ sensor_write_array(sd, nt99141_5fps_regs,
+ ARRAY_SIZE(nt99141_5fps_regs));
+ break;
+ case 10:
+ sensor_write_array(sd, nt99141_10fps_regs,
+ ARRAY_SIZE(nt99141_10fps_regs));
+ break;
+ case 15:
+ sensor_write_array(sd, nt99141_15fps_regs,
+ ARRAY_SIZE(nt99141_15fps_regs));
+ break;
+ case 20:
+ sensor_write_array(sd, nt99141_20fps_regs,
+ ARRAY_SIZE(nt99141_20fps_regs));
+ case 25:
+ sensor_write_array(sd, nt99141_25fps_regs,
+ ARRAY_SIZE(nt99141_25fps_regs));
+ case 30:
+ sensor_write_array(sd, nt99141_30fps_regs,
+ ARRAY_SIZE(nt99141_30fps_regs));
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int sensor_g_parm(struct cmos_subdev *sd, struct v4l2_streamparm *parms)
+{
+ return 0;
+}
+
+static int sensor_s_parm(struct cmos_subdev *sd, struct v4l2_streamparm *parms)
+{
+ struct v4l2_captureparm *cp = &parms->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+
+ if (tpf->numerator == 0 || tpf->denominator == 0) {
+ return 0;
+ }
+
+ sensor_fps = tpf->denominator;//cause tpf->numerator == 1 in HAL
+
+
+ return 0;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ uint32_t data = 0;
+
+ data |= (sensor_read(sd, 0x3000) & 0xff) << 8;
+ data |= (sensor_read(sd, 0x3001) & 0xff);
+
+ return (data == sd->id) ? 0 : -EINVAL;
+}
+
+static void day_night_do_work(struct work_struct *work)
+{
+ sensor_s_DayNightMode(day_night_sd);
+}
+
+static void day_night_timer_func(unsigned long __data)
+{
+ schedule_work(&day_night_work);
+ mod_timer(&day_night_timer, jiffies + msecs_to_jiffies(3000));
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ if (sensor_identify(sd))
+ return -1;
+
+ sensor_write_array(sd, nt99141_default_regs_init,
+ ARRAY_SIZE(nt99141_default_regs_init));
+
+ day_night_sd = sd;
+ INIT_WORK(&day_night_work, day_night_do_work);
+ setup_timer(&day_night_timer, day_night_timer_func, (unsigned long)sd);
+ mod_timer(&day_night_timer, jiffies + msecs_to_jiffies(3000));
+
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ sensor_write_array(sd, nt99141_default_regs_exit,
+ ARRAY_SIZE(nt99141_default_regs_exit));
+ cancel_work_sync(&day_night_work);
+ del_timer_sync(&day_night_timer);
+
+ return 0;
+}
+
+static struct cmos_subdev_ops nt99141_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+ .enum_framesizes = sensor_enum_framesizes,
+ .g_parm = sensor_g_parm,
+ .s_parm = sensor_s_parm,
+
+};
+
+struct cmos_subdev nt99141 = {
+ .name = "nt99141",
+ .i2c_addr = 0x2a,
+ .id = 0x1410,
+ .max_width = 1280,
+ .max_height = 720,
+ .ops = &nt99141_ops,
+};
+
+#if 0
+static int __init nt99141_init(void)
+{
+ return cmos_register_sudbdev(&nt99141);
+}
+
+static void __exit nt99141_exit(void)
+{
+ cmos_unregister_subdev(&nt99141);
+ return;
+}
+
+module_init(nt99141_init);
+module_exit(nt99141_exit);
+
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/nt99141/nt99141.h b/drivers/media/video/wmt_v4l2/sensors/nt99141/nt99141.h
new file mode 100755
index 00000000..be3b87c0
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/nt99141/nt99141.h
@@ -0,0 +1,537 @@
+#ifndef NT99141_H
+#define NT99141_H
+
+// Scene Mode
+uint32_t nt99141_scene_mode_auto[] = {
+ 0x32C4,0x28,
+ 0x302a,0x04,
+};
+
+uint32_t nt99141_scene_mode_night[] = {
+ 0x32C4,0x30,
+ 0x302a,0x08,
+};
+
+
+// White Balance
+uint32_t nt99141_wb_auto [] = {
+ 0x3201, 0x7F,
+};
+
+uint32_t nt99141_wb_incandescent [] = {
+ 0x3201, 0x6F,
+ 0x3290, 0x01, // 304 / 256 = 1.187
+ 0x3291, 0x30,
+ 0x3296, 0x01, // 459 / 256 = 1.793
+ 0x3297, 0xCB,
+ 0x3060, 0x01,
+};
+
+uint32_t nt99141_wb_fluorescent [] = {
+ 0x3201, 0x6F,
+ 0x3290, 0x01, // 368 / 256 = 1.437
+ 0x3291, 0x70,
+ 0x3296, 0x01, // 511 / 256 = 1.996
+ 0x3297, 0xFF,
+ 0x3060, 0x01,
+};
+
+uint32_t nt99141_wb_daylight [] = {
+ 0x3201, 0x6F,
+ 0x3290, 0x01, // 312 / 256 = 1.218
+ 0x3291, 0x38,
+ 0x3296, 0x01, // 360 / 256 = 1.406
+ 0x3297, 0x68,
+ 0x3060, 0x01,
+};
+
+uint32_t nt99141_wb_cloudy [] = {
+ 0x3201, 0x6F,
+ 0x3290, 0x01, // 337 / 256 = 1.316
+ 0x3291, 0x51,
+ 0x3296, 0x01, // 256 / 256 = 1.0
+ 0x3297, 0x00,
+ 0x3060, 0x01,
+};
+
+uint32_t nt99141_wb_tungsten [] = {
+ 0x3201, 0x6F,
+ 0x3290, 0x01, // 256 / 256 = 1.0
+ 0x3291, 0x00,
+ 0x3296, 0x02, // 560 / 256 = 2.1875
+ 0x3297, 0x30,
+ 0x3060, 0x01,
+};
+
+
+// Exposure
+uint32_t nt99141_exposure_neg6[] = {
+ 0x32F2, 0x40,
+};
+
+uint32_t nt99141_exposure_neg3[] = {
+ 0x32F2, 0x60,
+};
+
+uint32_t nt99141_exposure_zero[] = {
+ 0x32F2, 0x80,
+};
+
+uint32_t nt99141_exposure_pos3[] = {
+ 0x32F2, 0xA0,
+};
+
+uint32_t nt99141_exposure_pos6[] = {
+ 0x32F2, 0xC0,
+};
+
+// Resolution
+uint32_t nt99141_320_240_regs[] = {
+ //[YUYV_320x240_PCLK_74MHZ_15.00_30.03_Fps]
+ 0x32BF, 0x60,
+ 0x32C0, 0x6A,
+ 0x32C1, 0x6A,
+ 0x32C2, 0x6A,
+ 0x32C3, 0x00,
+ 0x32C4, 0x2F,
+ 0x32C5, 0x20,
+ 0x32C6, 0x20,
+ 0x32C7, 0x00,
+ 0x32C8, 0xDD,
+ 0x32C9, 0x6A,
+ 0x32CA, 0x8A,
+ 0x32CB, 0x8A,
+ 0x32CC, 0x8A,
+ 0x32CD, 0x8A,
+ 0x32DB, 0x7B,
+ 0x32E0, 0x01,
+ 0x32E1, 0x40,
+ 0x32E2, 0x00,
+ 0x32E3, 0xF0,
+ 0x32E4, 0x02,
+ 0x32E5, 0x02,
+ 0x32E6, 0x02,
+ 0x32E7, 0x03,
+ 0x3200, 0x3E,
+ 0x3201, 0x0F,
+ 0x3028, 0x24,
+ 0x3029, 0x20,
+ 0x302A, 0x04,
+ 0x3022, 0x24,
+ 0x3023, 0x24,
+ 0x3002, 0x00,
+ 0x3003, 0xA4,
+ 0x3004, 0x00,
+ 0x3005, 0x04,
+ 0x3006, 0x04,
+ 0x3007, 0x63,
+ 0x3008, 0x02,
+ 0x3009, 0xD3,
+ 0x300A, 0x06,
+ 0x300B, 0x8A,
+ 0x300C, 0x02,
+ 0x300D, 0xE0,
+ 0x300E, 0x03,
+ 0x300F, 0xC0,
+ 0x3010, 0x02,
+ 0x3011, 0xD0,
+ 0x32B8, 0x43,
+ 0x32B9, 0x35,
+ 0x32BB, 0x87,
+ 0x32BC, 0x3C,
+ 0x32BD, 0x40,
+ 0x32BE, 0x38,
+ 0x320A, 0xB2,
+ 0x3201, 0x7F,
+ 0x3021, 0x06,
+ 0x3060, 0x01,
+
+};
+
+uint32_t nt99141_640_480_regs[] = {
+ //[YUYV_640x480__PCLK_74MHZ_15.00_30.03_Fps]
+ 0x32BF, 0x60,
+ 0x32C0, 0x6A,
+ 0x32C1, 0x6A,
+ 0x32C2, 0x6A,
+ 0x32C3, 0x00,
+ 0x32C4, 0x2F,
+ 0x32C5, 0x20,
+ 0x32C6, 0x20,
+ 0x32C7, 0x00,
+ 0x32C8, 0xDD,
+ 0x32C9, 0x6A,
+ 0x32CA, 0x8A,
+ 0x32CB, 0x8A,
+ 0x32CC, 0x8A,
+ 0x32CD, 0x8A,
+ 0x32DB, 0x7B,
+ 0x32E0, 0x02,
+ 0x32E1, 0x80,
+ 0x32E2, 0x01,
+ 0x32E3, 0xE0,
+ 0x32E4, 0x00,
+ 0x32E5, 0x80,
+ 0x32E6, 0x00,
+ 0x32E7, 0x80,
+ 0x3200, 0x3E,
+ 0x3201, 0x0F,
+ 0x3028, 0x24,
+ 0x3029, 0x20,
+ 0x302A, 0x04,
+ 0x3022, 0x24,
+ 0x3023, 0x24,
+ 0x3002, 0x00,
+ 0x3003, 0xA4,
+ 0x3004, 0x00,
+ 0x3005, 0x04,
+ 0x3006, 0x04,
+ 0x3007, 0x63,
+ 0x3008, 0x02,
+ 0x3009, 0xD3,
+ 0x300A, 0x06,
+ 0x300B, 0x8A,
+ 0x300C, 0x02,
+ 0x300D, 0xE0,
+ 0x300E, 0x03,
+ 0x300F, 0xC0,
+ 0x3010, 0x02,
+ 0x3011, 0xD0,
+ 0x32B8, 0x3F,
+ 0x32B9, 0x31,
+ 0x32BB, 0x87,
+ 0x32BC, 0x38,
+ 0x32BD, 0x3C,
+ 0x32BE, 0x34,
+ 0x3201, 0x7F,
+ 0x320A, 0xB2,
+ 0x3021, 0x06,
+ 0x3060, 0x01,
+ 0x320A, 0xB2,
+};
+
+uint32_t nt99141_1280_720_regs[] = {
+ //[YUYV_1280x720_PCLK_74mhz_15.00_30.03_Fps]
+
+ 0x32BF, 0x60,
+ 0x32C0, 0x6A,
+ 0x32C1, 0x6A,
+ 0x32C2, 0x6A,
+ 0x32C3, 0x00,
+ 0x32C4, 0x2F,
+ 0x32C5, 0x20,
+ 0x32C6, 0x20,
+ 0x32C7, 0x00,
+ 0x32C8, 0xDD,
+ 0x32C9, 0x6A,
+ 0x32CA, 0x8A,
+ 0x32CB, 0x8A,
+ 0x32CC, 0x8A,
+ 0x32CD, 0x8A,
+ 0x32DB, 0x7B,
+ 0x32E0, 0x05,
+ 0x32E1, 0x00,
+ 0x32E2, 0x02,
+ 0x32E3, 0xD0,
+ 0x32E4, 0x00,
+ 0x32E5, 0x00,
+ 0x32E6, 0x00,
+ 0x32E7, 0x00,
+ 0x3200, 0x3E,
+ 0x3201, 0x0F,
+ 0x3028, 0x24,
+ 0x3029, 0x20,
+ 0x302A, 0x04,
+ 0x3022, 0x24,
+ 0x3023, 0x24,
+ 0x3002, 0x00,
+ 0x3003, 0x04,
+ 0x3004, 0x00,
+ 0x3005, 0x04,
+ 0x3006, 0x05,
+ 0x3007, 0x03,
+ 0x3008, 0x02,
+ 0x3009, 0xD3,
+ 0x300A, 0x06,
+ 0x300B, 0x8A,
+ 0x300C, 0x02,
+ 0x300D, 0xE0,
+ 0x300E, 0x05,
+ 0x300F, 0x00,
+ 0x3010, 0x02,
+ 0x3011, 0xD0,
+ 0x32B8, 0x3F,
+ 0x32B9, 0x31,
+ 0x32BB, 0x87,
+ 0x32BC, 0x38,
+ 0x32BD, 0x3C,
+ 0x32BE, 0x34,
+ 0x320A, 0x00,
+ 0x3201, 0x7F,
+ 0x3021, 0x06,
+ 0x3060, 0x01,
+
+
+};
+
+uint32_t nt99141_5fps_regs[] = {
+ 0x32BF, 0x60,
+ 0x32C0, 0x84,
+ 0x32C1, 0x84,
+ 0x32C2, 0x84,
+ 0x32C3, 0x00,
+ //0x32C4, 0x20,
+ 0x32C5, 0x20,
+ 0x32C6, 0x20,
+ 0x32C7, 0x00,
+ 0x32C8, 0xDD,
+ 0x32C9, 0x84,
+ 0x32CA, 0xA4,
+ 0x32CB, 0xA4,
+ 0x32CC, 0xA4,
+ 0x32CD, 0xA4,
+ 0x32DB, 0x7B,
+ 0x32D0, 0x01,
+};
+
+uint32_t nt99141_10fps_regs[] = {
+ 0x32BF, 0x60,
+ 0x32C0, 0x74,
+ 0x32C1, 0x74,
+ 0x32C2, 0x74,
+ 0x32C3, 0x00,
+ //0x32C4, 0x20,
+ 0x32C5, 0x20,
+ 0x32C6, 0x20,
+ 0x32C7, 0x00,
+ 0x32C8, 0xDD,
+ 0x32C9, 0x74,
+ 0x32CA, 0x94,
+ 0x32CB, 0x94,
+ 0x32CC, 0x94,
+ 0x32CD, 0x94,
+ 0x32DB, 0x7B,
+ 0x32D0, 0x01,
+};
+
+uint32_t nt99141_15fps_regs[] = {
+ 0x32BF, 0x60,
+ 0x32C0, 0x6A,
+ 0x32C1, 0x6A,
+ 0x32C2, 0x6A,
+ 0x32C3, 0x00,
+ //0x32C4, 0x20,
+ 0x32C5, 0x20,
+ 0x32C6, 0x20,
+ 0x32C7, 0x00,
+ 0x32C8, 0xDD,
+ 0x32C9, 0x6A,
+ 0x32CA, 0x8A,
+ 0x32CB, 0x8A,
+ 0x32CC, 0x8A,
+ 0x32CD, 0x8A,
+ 0x32DB, 0x7B,
+ 0x32D0, 0x01,
+};
+
+
+uint32_t nt99141_20fps_regs[] = {
+ 0x32BF, 0x60,
+ 0x32C0, 0x64,
+ 0x32C1, 0x64,
+ 0x32C2, 0x64,
+ 0x32C3, 0x00,
+ //0x32C4, 0x20,
+ 0x32C5, 0x20,
+ 0x32C6, 0x20,
+ 0x32C7, 0x00,
+ 0x32C8, 0xDD,
+ 0x32C9, 0x64,
+ 0x32CA, 0x84,
+ 0x32CB, 0x84,
+ 0x32CC, 0x84,
+ 0x32CD, 0x84,
+ 0x32DB, 0x7B,
+ 0x32D0, 0x01,
+};
+
+
+uint32_t nt99141_25fps_regs[] = {
+ 0x32BF, 0x60,
+ 0x32C0, 0x60,
+ 0x32C1, 0x60,
+ 0x32C2, 0x60,
+ 0x32C3, 0x00,
+ //0x32C4, 0x20,
+ 0x32C5, 0x20,
+ 0x32C6, 0x20,
+ 0x32C7, 0x00,
+ 0x32C8, 0xDD,
+ 0x32C9, 0x60,
+ 0x32CA, 0x80,
+ 0x32CB, 0x80,
+ 0x32CC, 0x80,
+ 0x32CD, 0x80,
+ 0x32DB, 0x7B,
+ 0x32D0, 0x01,
+};
+
+
+uint32_t nt99141_30fps_regs[] = {
+ 0x32BF, 0x60,
+ 0x32C0, 0x5A,
+ 0x32C1, 0x5A,
+ 0x32C2, 0x5A,
+ 0x32C3, 0x00,
+ //0x32C4, 0x20,
+ 0x32C5, 0x20,
+ 0x32C6, 0x20,
+ 0x32C7, 0x00,
+ 0x32C8, 0xDD,
+ 0x32C9, 0x5A,
+ 0x32CA, 0x7A,
+ 0x32CB, 0x7A,
+ 0x32CC, 0x7A,
+ 0x32CD, 0x7A,
+ 0x32DB, 0x7B,
+ 0x32D0, 0x01,
+};
+
+// init
+uint32_t nt99141_default_regs_init[]= {
+ 0x3109,0x04,
+ 0x3040,0x04,
+ 0x3041,0x02,
+ 0x3042,0xFF,
+ 0x3043,0x08,
+ 0x3052,0xE0,
+ 0x305F,0x33,
+ 0x3100,0x07,
+ 0x3106,0x03,
+ 0x3105,0x01,
+ 0x3108,0x05,
+ 0x3110,0x22,
+ 0x3111,0x57,
+ 0x3112,0x22,
+ 0x3113,0x55,
+ 0x3114,0x05,
+ 0x3135,0x00,
+ 0x32F0,0x01,
+ 0x3210,0x05,
+ 0x3211,0x05,
+ 0x3212,0x05,
+ 0x3213,0x05,
+ 0x3214,0x05,
+ 0x3215,0x05,
+ 0x3216,0x05,
+ 0x3217,0x05,
+ 0x3218,0x05,
+ 0x3219,0x05,
+ 0x321A,0x05,
+ 0x321B,0x05,
+ 0x321C,0x05,
+ 0x321D,0x05,
+ 0x321E,0x05,
+ 0x321F,0x05,
+ 0x3231,0xFF,
+ 0x3290,0x01,
+ 0x3291,0x80,
+ 0x3296,0x01,
+ 0x3297,0x73,
+ 0x3250,0x80,
+ 0x3251,0x03,
+ 0x3252,0xFF,
+ 0x3253,0x00,
+ 0x3254,0x03,
+ 0x3255,0xFF,
+ 0x3256,0x00,
+ 0x3257,0x50,
+ 0x3270,0x00,
+ 0x3271,0x14,
+ 0x3272,0x22,
+ 0x3273,0x36,
+ 0x3274,0x4A,
+ 0x3275,0x5A,
+ 0x3276,0x73,
+ 0x3277,0x88,
+ 0x3278,0x98,
+ 0x3279,0xA7,
+ 0x327A,0xC0,
+ 0x327B,0xD4,
+ 0x327C,0xE6,
+ 0x327D,0xF5,
+ 0x327E,0xFF,
+ 0x3302,0x00,
+ 0x3303,0x40,
+ 0x3304,0x00,
+ 0x3305,0x96,
+ 0x3306,0x00,
+ 0x3307,0x29,
+ 0x3308,0x07,
+ 0x3309,0xBA,
+ 0x330A,0x06,
+ 0x330B,0xF5,
+ 0x330C,0x01,
+ 0x330D,0x51,
+ 0x330E,0x01,
+ 0x330F,0x30,
+ 0x3310,0x07,
+ 0x3311,0x16,
+ 0x3312,0x07,
+ 0x3313,0xBA,
+ 0x3326,0x02,
+ 0x32F6,0x0F,
+ 0x32F9,0x42,
+ 0x32FA,0x24,
+ 0x3325,0x4A,
+ 0x3330,0x00,
+ 0x3331,0x0A,
+ 0x3332,0xFF,
+ 0x3338,0x30,
+ 0x3339,0x84,
+ 0x333A,0x00,
+ 0x333F,0x07,
+ 0x3360,0x10,
+ 0x3361,0x18,
+ 0x3362,0x1f,
+ 0x3363,0x37,
+ 0x3364,0x80,
+ 0x3365,0x76,
+ 0x3366,0x70,
+ 0x3367,0x68,
+ 0x3368,0x30,
+ 0x3369,0x28,
+ 0x336A,0x20,
+ 0x336B,0x10,
+ 0x336C,0x00,
+ 0x336D,0x20,
+ 0x336E,0x1C,
+ 0x336F,0x18,
+ 0x3370,0x0E,
+ 0x3371,0x38,
+ 0x3372,0x3C,
+ 0x3373,0x3F,
+ 0x3374,0x3F,
+ 0x338A,0x34,
+ 0x338B,0x7F,
+ 0x338C,0x10,
+ 0x338D,0x23,
+ 0x338E,0x7F,
+ 0x338F,0x14,
+ 0x3375,0x10,
+ 0x3376,0x12,
+ 0x3377,0x14,
+ 0x3378,0x16,
+
+ 0x3012,0x02,
+ 0x3013,0xD0,
+ 0x3024,0x0a,
+ 0x3069,0x01,
+ 0x306A,0x03,
+};
+
+uint32_t nt99141_default_regs_exit[]={
+
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/ov2640/ov2640.h b/drivers/media/video/wmt_v4l2/sensors/ov2640/ov2640.h
new file mode 100755
index 00000000..534302c3
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/ov2640/ov2640.h
@@ -0,0 +1,66 @@
+#ifndef OV2640_H
+#define OV2640_H
+
+unsigned char ov2640_800_600_cfg_0[]={
+0xFF,0x1 ,0x12,0x80,0xFF,0x0 ,0x2C,0xFF,0x2E,0xDF,0xFF,0x1 ,0x12,0x80,0x3C,0x35,
+0x11,0xFF,0x4 ,0x28,0x13,0xE5,0x14,0x48,0x2C,0x0C,0x33,0x78,0x3B,0xF7,0x3E,0x0 ,
+0x43,0x11,0x16,0x10,0x39,0x82,0x35,0x88,0x22,0x0A,0x37,0x40,0x23,0x0 ,0x34,0xA0,
+0x36,0x1A,0x6 ,0x2 ,0x7 ,0xC0,0x0D,0xB7,0x0E,0x1 ,0x4C,0x0 ,0x4A,0x81,0x21,0x99,
+0x9 ,0x2 ,0x24,0x3E,0x25,0x34,0x26,0x81,0x5C,0x0 ,0x63,0x0 ,0x7C,0x0 ,0x61,0x70,
+0x62,0x80,0x20,0x80,0x28,0x30,0x6C,0x0 ,0x6E,0x0 ,0x70,0x2 ,0x71,0x94,0x73,0xC1,
+0x3D,0x34,0x5A,0x57,0x4F,0xBB,0x50,0x9C,0x6D,0x80,0x39,0x2 ,0x3A,0x33,0x3B,0xFB,
+0x3C,0x32,0xFF,0x0 ,0xE0,0x14,0x76,0xFF,0x33,0xA0,0x42,0x20,0x43,0x18,0x4C,0x0 ,
+0x83,0xD0,0x88,0x0F,0xD7,0x3 ,0xD9,0x10,0xDA,0x4 ,0xD3,0x82,0xF9,0xD0,0x44,0x6 ,
+0xD1,0x7 ,0xD2,0x2 ,0xD2,0x0 ,0xD8,0x11,0xC8,0x8 ,0xC9,0x80,0x7C,0x0 ,0x7D,0x7 ,
+0x7C,0x7 ,0x7D,0x20,0x90,0x0 ,0x91,0x0E,0x91,0x1A,0x91,0x31,0x91,0x5A,0x91,0x69,
+0x91,0x75,0x91,0x7E,0x91,0x88,0x91,0x8F,0x91,0x96,0x91,0xA3,0x91,0xAF,0x91,0xC4,
+0x91,0xD7,0x91,0xE8,0x91,0x20,0x92,0x0 ,0x93,0x6 ,0x93,0xE3,0x93,0x5 ,0x93,0x5 ,
+0x93,0x0 ,0x93,0x4 ,0x93,0x0 ,0x93,0x0 ,0x93,0x0 ,0x93,0x0 ,0x93,0x0 ,0x93,0x0 ,
+0x96,0x0 ,0x97,0x8 ,0x97,0x19,0x97,0x2 ,0x97,0x0C,0x97,0x24,0x97,0x30,0x97,0x28,
+0x97,0x26,0x97,0x2 ,0x97,0x98,0x97,0x80,0x97,0x0 ,0x97,0x0 ,0xC3,0xED,0xC4,0x9A,
+0xA4,0x0 ,0xC5,0x11,0xC6,0x51,0xC7,0x10,0xB6,0x66,0xB8,0xA5,0xB7,0x64,0xB9,0x7C,
+0xB3,0xAF,0xB4,0x97,0xB5,0xFF,0xB0,0xC5,0xB1,0x94,0xB2,0x0F,0xC4,0x5C,0xA8,0x0 ,
+0xC0,0xC8,0xC1,0x96,0x86,0x1D,0x50,0x0 ,0x51,0x90,0x52,0x18,0x53,0x0 ,0x54,0x0 ,
+0x55,0x88,0x57,0x0 ,0x5A,0x90,0x5B,0x18,0x5C,0x5 ,0xC3,0xED,0x7F,0x0 ,0xDA,0x4 ,
+0xE5,0x1F,0xE1,0x67,0xE0,0x0 ,0xDD,0xFF,0x5 ,0x0 ,0xFF,0x0 ,0xDD,0xF1,0xE0,0x4 ,
+0xFF,0x1 ,0x12,0x0 ,0x11,0x0 ,0x17,0x11,0x18,0x75,0x19,0x1 ,0x1A,0x97,0x32,0x36,
+0x4F,0xBB,0x50,0x9c,0x5A,0x57,0x6D,0x80,0x0F,0x4F,0x3 ,0x8F,0x7C,0x5 ,0xFF,0x0 ,
+0xC0,0xC8,0xC1,0x96,0x8C,0x0 ,0x86,0x1D,0xD7,0x1 ,0xD3,0x82,0xDA,0x4 ,0xE0,0x0 ,
+0xE1,0x67,0xDD,0xFF,0xFF,0x1 ,0xFF,0x0 ,0xDD,0xF1,0xE0,0x4 ,0xFF,0x1 ,0x11,0x1 ,
+0xFF,0x0 ,0xC7,0x90,0x92,0x1 ,0x93,0xA0,0x92,0x0 ,0x93,0x6 ,0x92,0x2 ,0x93,0x8 ,
+0xC3,0xFD,0xA4,0x20,0x87,0xC0,0x7C,0x8 ,0x7D,0x28,0x7D,0x18,0x96,0x3 ,0x97,0x8 ,
+0x97,0x16,0x97,0x20,0x97,0x1B,0x97,0x18,0x97,0x2 ,0xFF,0x1 ,0x0F,0x4F,0x3 ,0x8F,
+0x2D,0x0 ,0x2E,0x0 ,0x22,0x0 ,0x70,0x6 ,0x14,0x8 ,0x14,0x48,0xFF,0x0 ,0xE0,0x0 ,
+0xDD,0xFF,0xFF,0x1 ,0x0C,0x38,0xFF,0x0 ,0x92,0x1 ,0x93,0xC1,0xFF,0x0 ,0x92,0x1 ,
+0x93,0xE1,0xFF,0x0 ,0x7C,0x3 ,0x7D,0x40,0x7D,0x40,0xFF,0x0 ,0x7C,0x1 ,0x7D,0x80,
+0x7D,0x0 ,0x7C,0x0A,0x7D,0x6 ,0xFF,0x0 ,0x7C,0x7 ,0x7D,0x20,0x7D,0x20,0x7D,0x20,
+0x7D,0x6 ,0xFF,0x0 ,0x7C,0x9 ,0x7D,0x20,0x7D,0x6 ,0xFF,0x1 ,0x24,0x3E,0x25,0x34,
+0x26,0x81,
+};
+
+unsigned char ov2640_800_600_cfg_1[]={
+0xFF,0x0 ,0xDD,0xF1,0xE0,0x4 ,0xFF,0x1 ,0x11,0x0 ,0xFF,0x0 ,0xC7,0x10,0x94,0x0 ,
+0x93,0x6 ,0x93,0xE3,0x93,0x5 ,0x93,0x5 ,0xC3,0xED,0xA4,0x0 ,0x87,0xD0,0x7C,0x8 ,
+0x7D,0x20,0x7D,0x20,0x96,0x3 ,0x97,0x0C,0x97,0x24,0x97,0x30,0x97,0x28,0x97,0x26,
+0x97,0x2 ,0xFF,0x1 ,0x0F,0x4F,0x3 ,0x8F,0x2D,0x0 ,0x2E,0x0 ,0x22,0x0A,0x70,0x2 ,
+0x14,0x8 ,0x14,0x48,0xFF,0x0 ,0xE0,0x0 ,0xDD,0xFF,0xFF,0x1 ,0xC0,0x38,0xFF,0x0 ,
+0xDD,0xF1,0xE0,0x4 ,0xFF,0x1 ,0x11,0x1 ,0xFF,0x0 ,0xC7,0x10,0x94,0x0 ,0x93,0x6 ,
+0x93,0xE3,0x93,0x5 ,0x93,0x5 ,0xC3,0xED,0xA4,0x0 ,0x87,0xD0,0x7C,0x8 ,0x7D,0x20,
+0x7D,0x20,0x96,0x3 ,0x97,0x0C,0x97,0x24,0x97,0x30,0x97,0x28,0x97,0x26,0x97,0x2 ,
+0xFF,0x1 ,0x0F,0x4F,0x3 ,0x8F,0x2D,0x0 ,0x2E,0x0 ,0x22,0x0A,0x70,0x2 ,0x14,0x8 ,
+0x14,0x48,0xFF,0x0 ,0xE0,0x0 ,0xDD,0xFF,0xFF,0x1 ,0x0C,0x38,
+};
+
+unsigned char ov2640_800_600_cfg_2[]={
+0xFF,0x0 ,0xDD,0xF1,0xE0,0x4 ,0xE1,0x67,0xDA,0x0 ,0xFF,0x0 ,0xDD,0xF1,0xE0,0x4 ,
+0xFF,0x1 ,0x12,0x0 ,0x11,0x1 ,0x17,0x11,0x18,0x75,0x19,0x1 ,0x1A,0x97,0x32,0x36,
+0x4F,0xBB,0x5A,0x57,0x50,0x9C,0x6D,0x80,0xFF,0x0 ,0xC0,0xC8,0xC1,0x96,0x8C,0x0 ,
+0x86,0x3D,0x50,0x89,0x51,0x90,0x52,0x2C,0x53,0x0 ,0x54,0x0 ,0x55,0x88,0x57,0x0 ,
+0x5A,0xC8,0x5B,0x96,0x5C,0x0 ,0xD3,0x4 ,0xE0,0x0 ,0xDD,0xFF,0xFF,0x1 ,0x0C,0x38,
+0xFF,0x0 ,0xDD,0xF1,0xE0,0x4 ,0xFF,0x1 ,0x12,0x0 ,0x11,0x1 ,0x17,0x11,0x18,0x75,
+0x19,0x1 ,0x1A,0x97,0x32,0x36,0x4F,0xBB,0x5A,0x57,0x50,0x9C,0x6D,0x80,0xFF,0x0 ,
+0xC0,0xC8,0xC1,0x96,0x8C,0x0 ,0x86,0x3D,0x50,0x89,0x51,0x90,0x52,0x2C,0x53,0x0 ,
+0x54,0x0 ,0x55,0x88,0x57,0x0 ,0x5A,0xC8,0x5B,0x96,0x5C,0x0 ,0xD3,0x4 ,0xE0,0x0 ,
+0xDD,0xFF,0xd3,0x2,0xFF,0x1 ,0x0C,0x38,0X11,0X0
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/ov2643/ov2643.c b/drivers/media/video/wmt_v4l2/sensors/ov2643/ov2643.c
new file mode 100755
index 00000000..53731fae
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/ov2643/ov2643.c
@@ -0,0 +1,329 @@
+
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "ov2643.h"
+
+#define sensor_write_array(sd, arr, sz) \
+ cmos_init_8bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write(sd->i2c_addr, reg, val)
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint8_t *regs;
+ size_t size;
+};
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+ CMOS_WIN_SIZE("VGA", 640, 480, ov2643_640_480_regs),
+ CMOS_WIN_SIZE("720p", 1280, 720, ov2643_1280_720_regs),
+ CMOS_WIN_SIZE("UXGA", 1600, 1200, ov2643_1600_1200_regs),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = ov2643_wb_auto;
+ size = ARRAY_SIZE(ov2643_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = ov2643_wb_incandescent;
+ size = ARRAY_SIZE(ov2643_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = ov2643_wb_daylight;
+ size = ARRAY_SIZE(ov2643_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = ov2643_wb_cloudy;
+ size = ARRAY_SIZE(ov2643_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = ov2643_wb_fluorescent;
+ size = ARRAY_SIZE(ov2643_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_scenemode(struct cmos_subdev *sd, enum v4l2_scene_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case SCENE_MODE_AUTO:
+ regs = ov2643_scene_mode_auto;
+ size = ARRAY_SIZE(ov2643_scene_mode_auto);
+ break;
+ case SCENE_MODE_NIGHTSHOT:
+ regs = ov2643_scene_mode_night;
+ size = ARRAY_SIZE(ov2643_scene_mode_night);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_exposure(struct cmos_subdev *sd, enum v4l2_exposure_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case -2:
+ regs = ov2643_exposure_neg6;
+ size = ARRAY_SIZE(ov2643_exposure_neg6);
+ break;
+ case -1:
+ regs = ov2643_exposure_neg3;
+ size = ARRAY_SIZE(ov2643_exposure_neg3);
+ break;
+ case 0:
+ regs = ov2643_exposure_zero;
+ size = ARRAY_SIZE(ov2643_exposure_zero);
+ break;
+ case 1:
+ regs = ov2643_exposure_pos3;
+ size = ARRAY_SIZE(ov2643_exposure_pos3);
+ break;
+ case 2:
+ regs = ov2643_exposure_pos6;
+ size = ARRAY_SIZE(ov2643_exposure_pos6);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data;
+
+ data = sensor_read(sd, 0x12);
+
+ switch (value) {
+ case 0:
+ data &= ~0x20;
+ break;
+ case 1:
+ data |= 0x20;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sensor_write(sd, 0x12, data);
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data;
+
+ data = sensor_read(sd, 0x12);
+
+ switch (value) {
+ case 0:
+ data &= ~0x10;
+ break;
+ case 1:
+ data |= 0x10;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sensor_write(sd, 0x12, data);
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ case V4L2_CID_EXPOSURE:
+ return v4l2_ctrl_query_fill(qc, -2, 2, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return sensor_s_scenemode(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return sensor_s_exposure(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ default:
+ case WMT_V4L2_CID_CAMERA_ANTIBANDING:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win) {
+ pr_err("%s, s_mbus_fmt failed, width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+ return -EINVAL;
+ }
+ sensor_write_array(sd, win->regs, win->size);
+ msleep(300);
+
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+
+ return 0;
+}
+
+static int sensor_enum_framesizes(struct cmos_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ const struct cmos_win_size *win = &cmos_supported_win_sizes[index];
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ int data;
+
+ data = sensor_read(sd, 0xa);
+ data <<=8;
+ data += sensor_read(sd ,0xb);
+
+ return (data == sd->id) ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ if (sensor_identify(sd)) {
+ return -1;
+ }
+
+ sensor_write_array(sd, ov2643_default_regs_init,
+ ARRAY_SIZE(ov2643_default_regs_init));
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ return 0;
+}
+
+static struct cmos_subdev_ops ov2643_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+ .enum_framesizes = sensor_enum_framesizes,
+};
+
+struct cmos_subdev ov2643 = {
+ .name = "ov2643",
+ .i2c_addr = 0x30,
+ .id = 0x2643,
+ .max_width = 1600,
+ .max_height = 1200,
+ .ops = &ov2643_ops,
+};
+
+#if 0
+static int __init ov2643_init(void)
+{
+ return cmos_register_sudbdev(&ov2643);
+}
+
+static void __exit ov2643_exit(void)
+{
+ return cmos_unregister_subdev(&ov2643);
+}
+
+module_init(ov2643_init);
+module_exit(ov2643_exit);
+
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/ov2643/ov2643.h b/drivers/media/video/wmt_v4l2/sensors/ov2643/ov2643.h
new file mode 100755
index 00000000..b041bf32
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/ov2643/ov2643.h
@@ -0,0 +1,380 @@
+#ifndef OV2643_H
+#define OV2643_H
+
+// Scene Mode
+uint8_t ov2643_scene_mode_auto[] = {
+ 0x03,0x10,
+ 0x41,0x00,
+ 0x03,0x20,
+ 0xb2,0x80,
+ 0xff,0xff,
+};
+
+uint8_t ov2643_scene_mode_night[] = {
+ 0x03,0x10,
+ 0x41,0x15,
+ 0x03,0x20,
+ 0xb2,0xf0,
+ 0xff,0xff,
+};
+
+
+// White Balance
+uint8_t ov2643_wb_auto [] = {
+
+};
+
+uint8_t ov2643_wb_incandescent [] = {
+
+};
+
+uint8_t ov2643_wb_fluorescent [] = {
+
+};
+
+uint8_t ov2643_wb_daylight [] = {
+
+};
+
+uint8_t ov2643_wb_cloudy [] = {
+
+};
+
+
+// Exposure
+uint8_t ov2643_exposure_neg6[] = {
+
+};
+
+uint8_t ov2643_exposure_neg3[] = {
+
+};
+
+uint8_t ov2643_exposure_zero[] = {
+
+};
+
+uint8_t ov2643_exposure_pos3[] = {
+
+};
+
+uint8_t ov2643_exposure_pos6[] = {
+
+};
+
+
+// Resolution
+uint8_t ov2643_1600_1200_regs[]={
+ //;pclk=72mhz,30fps/pclk=36mhz,15fps
+ //0x12,0x80,
+ 0xc3,0x1f,
+ 0xc4,0xff,
+ 0x3d,0x48,
+ 0xdd,0xa5,
+ //;windows setup
+ 0x20,0x01,
+ 0x21,0x25,
+ 0x22,0x00,
+ 0x23,0x0c,
+ 0x24,0x64,//;0x640=1600
+ 0x25,0x08,
+ 0x26,0x4b,//;0x4b0=1200
+ 0x27,0x06,
+ 0x28,0x42,
+ //;format setting
+ 0x12,0x08,//+MIRROR*0x20+FLIP*0x10,//mirror and flip
+ 0x39,0x10,
+ 0xcd,0x12,
+ 0x3d,0x08,
+ //;frame setting
+ 0x0e,0x10,//clock 48Mhz PCLK
+ 0x0f,0x14,
+ 0x10,0x0a,
+ 0x11,0x00,
+
+ 0x29,0x07,//;dummy pixels
+ 0x2a,0x9a,
+ 0x2b,0x04,//;dummy lines//0x04
+ 0x2c,0xd4,// //0xd4
+ //for 12.5fps 0x4d4*1.2=0x5cb
+ 0x2d,0x00,
+ 0x2e,0x00,
+
+ 0x1c,0x25,//vsync width
+ 0x1d,0x06,//;banding
+ 0x1e,0x00,
+ 0x1f,0xb9,//0xba,//0x5d,
+
+ 0xde,0xc4,//added 2011-9-2 9:21:54
+ //0x75,0x6a,//advanced AWB for 2M
+};
+
+uint8_t ov2643_1280_720_regs[]={
+ //;pclk=72mhz,30fps/pclk=36mhz,15fps
+ //0x12,0x80,
+ 0xc3,0x1f,
+ 0xc4,0xff,
+ 0x3d,0x48,
+ 0xdd,0xa5,
+ //;windows setup
+ 0x20,0x01,//0x01,//
+ 0x21,0x25,//0x60,//
+ 0x22,0x00,
+ 0x23,0x0c,
+ 0x24,0x50,//;0x500=1280
+ 0x25,0x04,
+ 0x26,0x2d,//;0x2d0=720
+ 0x27,0x04,
+ 0x28,0x42,
+
+ //format
+ 0x12,0x48,//+MIRROR*0x20+FLIP*0x10,//mirror and flip
+ 0x39,0x10,
+ 0xcd,0x12,
+
+ //;frame setting
+ 0x0e,0x10,//0x10,//0xb4,//clock 48Mhz PCLK
+ 0x0f,0x14,
+ 0x10,0x0a,
+ 0x11,0x00,
+
+ 0x29,0x06,//;dummy pixels //24.75M 0x29,0x06//24M 0x29,0x06,//
+ 0x2a,0x40, //24.75M 0x2a,0x72//24M 0x2a,0x40,//
+ 0x2b,0x02,//;dummy lines //24.75M 0x2b,0x02//24M 0x2b,0x02,//
+ 0x2c,0xee, //24.75M 0x2c,0xee//24M 0x2c,0xee,//
+ //for 25fps 0x2ee*1.2=0x384
+
+ 0x1c,0x25,//vsync width
+ 0x1d,0x02,//0x04,//0x02,
+ 0x1e,0x00,
+ 0x1f,0xe1,
+
+ 0xde,0x7c,
+ 0x3d,0x08,
+};
+
+uint8_t ov2643_640_480_regs[]={
+ //;pclk=72mhz,30fps/pclk=36mhz,15fps
+ //0x12,0x80,
+ 0xc3,0x1f,
+ 0xc4,0xff,
+ 0x3d,0x48,
+ 0xdd,0xa5,
+ //;windows setup
+ 0x20,0x01,
+ 0x21,0x98,
+ 0x22,0x00,
+ 0x23,0x06,
+ 0x24,0x28,//;0x280=640
+ 0x25,0x04,
+ 0x26,0x1e,//;0x1e0=480
+ 0x27,0x04,
+ 0x28,0x40,
+
+ //format
+ 0x12,0x09,
+ 0x39,0xd0,
+ 0xcd,0x13,
+ 0x3d,0x08,
+
+ //;frame setting
+ 0x0e,0x10,//clock 48Mhz PCLK
+ 0x0f,0x14,
+ 0x10,0x0a,
+ 0x11,0x00,
+ 0x29,0x07,//;dummy pixels//24.75M 0x29,0x07//24M 0x29,0x07,//->ov setting
+ 0x2a,0x93, //24.75M 0x2a,0xd0//24M 0x2a,0x93,//->ov setting
+ 0x2b,0x02,//;dummy lines //24.75M 0x2b,0x02//24M 0x2b,0x02,//->ov setting
+ 0x2c,0x6a, //24.75M 0x2c,0x6a//24M 0x2c,0x6a,//->ov setting
+ //for 25fps 0x26a*1.2=0x2e6
+ 0x1c,0x25,//vsync width
+ 0x1d,0x02,
+ 0x1e,0x00,
+ 0x1f,0xb9,
+
+ 0xde,0x7c,
+ 0x3d,0x08,
+};
+
+
+uint8_t ov2643_default_regs_init[]={
+ 0x12,0x80, //reset
+ 0x13,0xff,
+ //;pclk=72mhz,30fps/pclk=36mhz,15fps
+ 0xc3,0x1f,
+ 0xc4,0xff,
+ 0x3d,0x48,
+ 0xdd,0xa5,
+ //;windows setup
+ 0x20,0x01,
+ 0x21,0x98,
+ 0x22,0x00,
+ 0x23,0x06,
+ 0x24,0x28,//;0x280=640
+ 0x25,0x04,
+ 0x26,0x1e,//;0x1e0=480
+ 0x27,0x04,
+ 0x28,0x40,
+ //;format setting
+ 0x12,0x09,
+ 0x39,0xd0,
+ 0xcd,0x13,
+ 0x3d,0x08,
+ //;frame setting
+ 0x0e,0x10,//clock 48Mhz PCLK
+ 0x0f,0x14,
+ 0x10,0x0a,
+ 0x11,0x00,
+ 0x29,0x07,//;dummy pixels//24.75M 0x29,0x07//24M 0x29,0x07,//->ov setting
+ 0x2a,0x93, //24.75M 0x2a,0xd0//24M 0x2a,0x93,//->ov setting
+ 0x2b,0x02,//;dummy lines //24.75M 0x2b,0x02//24M 0x2b,0x02,//->ov setting
+ 0x2c,0x6a, //24.75M 0x2c,0x6a//24M 0x2c,0x6a,//->ov setting
+ //for 25fps 0x26a*1.2=0x2e6
+ 0x1c,0x25,//vsync width
+ 0x1d,0x02,
+ 0x1e,0x00,
+ 0x1f,0xb9,
+
+ //common part except 0x3d,0xde
+ 0x14,0x87, //band filter bit7: 1:50Hz 0:60Hz bit4:
+ 0x15,0x42,
+ 0x3c,0xa4,
+ 0x18,0x78,//set to default then set to csi_ae value to wake up quickly
+ 0x19,0x68,//
+ 0x1a,0x71,
+ 0x37,0xe8,
+ 0x16,0x90,
+ 0x43,0x00,
+ 0x40,0xfb,
+ 0xa9,0x44,
+ 0x2f,0xec,
+ 0x35,0x10,
+ 0x36,0x10,
+ 0x0c,0x00,
+ 0x0d,0x00,
+ 0xd0,0x93,
+ 0xdc,0x2b,
+ 0xd9,0x41,
+ 0xd3,0x02,
+ 0xde,0x7c,
+ 0x3d,0x08,
+ 0x0c,0x00,
+ 0x9b,0x69,
+ 0x9c,0x7d,
+ 0x9d,0x7d,
+ 0x9e,0x69,
+ 0x35,0x04,
+ 0x36,0x04,
+ //;gamma
+ //normal ////enhanced////zqh3 ////zqh2 ////zqh1 ////reset //
+ 0x65,0x04,//0x65,0x12,//0x65,0x04,//0x65,0x04,//0x65,0x07,//0x65,0x05,//
+ 0x66,0x07,//0x66,0x20,//0x66,0x07,//0x66,0x07,//0x66,0x12,//0x66,0x0c,//
+ 0x67,0x19,//0x67,0x39,//0x67,0x19,//0x67,0x19,//0x67,0x1f,//0x67,0x1c,//
+ 0x68,0x34,//0x68,0x4e,//0x68,0x34,//0x68,0x34,//0x68,0x35,//0x68,0x2a,//
+ 0x69,0x4a,//0x69,0x62,//0x69,0x4a,//0x69,0x4a,//0x69,0x4a,//0x69,0x39,//
+ 0x6a,0x5a,//0x6a,0x74,//0x6a,0x5a,//0x6a,0x5a,//0x6a,0x5d,//0x6a,0x45,//
+ 0x6b,0x67,//0x6b,0x85,//0x6b,0x67,//0x6b,0x6b,//0x6b,0x6f,//0x6b,0x52,//
+ 0x6c,0x71,//0x6c,0x92,//0x6c,0x71,//0x6c,0x78,//0x6c,0x7d,//0x6c,0x5d,//
+ 0x6d,0x7c,//0x6d,0x9e,//0x6d,0x7c,//0x6d,0x84,//0x6d,0x8a,//0x6d,0x68,//
+ 0x6e,0x8c,//0x6e,0xb2,//0x6e,0x8c,//0x6e,0x98,//0x6e,0x9f,//0x6e,0x7f,//
+ 0x6f,0x9b,//0x6f,0xc0,//0x6f,0x9b,//0x6f,0xa6,//0x6f,0xae,//0x6f,0x91,//
+ 0x70,0xa9,//0x70,0xcc,//0x70,0xa9,//0x70,0xb2,//0x70,0xbb,//0x70,0xa5,//
+ 0x71,0xc0,//0x71,0xe0,//0x71,0xc0,//0x71,0xc6,//0x71,0xd0,//0x71,0xc6,//
+ 0x72,0xd5,//0x72,0xee,//0x72,0xd5,//0x72,0xd5,//0x72,0xdf,//0x72,0xde,//
+ 0x73,0xe8,//0x73,0xf6,//0x73,0xe8,//0x73,0xe8,//0x73,0xe8,//0x73,0xef,//
+ 0x74,0x20,//0x74,0x11,//0x74,0x20,//0x74,0x20,//0x74,0x20,//0x74,0x16,//
+ //;color matrix
+ //ttune //ov seeting //
+ 0xab,0x20,//0xab,0x28,//
+ 0xac,0x5b,//0xac,0x48,//
+ 0xad,0x05,//0xad,0x10,//
+ 0xae,0x1b,//0xae,0x18,//
+ 0xaf,0x76,//0xaf,0x75,//
+ 0xb0,0x90,//0xb0,0x8c,//
+ 0xb1,0x90,//0xb1,0x8d,//
+ 0xb2,0x8c,//0xb2,0x8c,//
+ 0xb3,0x04,//0xb3,0x00,//
+ 0xb4,0x98,//0xb4,0x98,//
+ 0xb5,0x00,//0xb5,0x00,//
+ //;lens shading
+ 0x40,0xFB,//0x40,0x08,//
+ 0x4c,0x03,//0x4c,0x03,//
+ 0x4d,0x30,//0x4d,0xd0,//
+ 0x4e,0x02,//0x4e,0x02,//
+ 0x4f,0x5c,//0x4f,0x5c,//
+ 0x50,0x3e,//0x50,0x3e,//
+ 0x51,0x00,//0x51,0x00,//
+ 0x52,0x66,//0x52,0x66,//
+ 0x53,0x03,//0x53,0x03,//
+ 0x54,0x30,//0x54,0xd0,//
+ 0x55,0x02,//0x55,0x02,//
+ 0x56,0x5c,//0x56,0x5c,//
+ 0x57,0x47,//0x57,0x47,//
+ 0x58,0x00,//0x58,0x00,//
+ 0x59,0x66,//0x59,0x66,//
+ 0x5a,0x03,//0x5a,0x03,//
+ 0x5b,0x20,//0x5b,0xd0,//
+ 0x5c,0x02,//0x5c,0x02,//
+ 0x5d,0x5c,//0x5d,0x5c,/
+ 0x5e,0x3e,//0x5e,0x3e,//
+ 0x5f,0x00,//0x5f,0x00,//
+ 0x60,0x66,//0x60,0x66,//
+
+ 0x41,0x1f,
+ 0xb5,0x01,
+ 0xb6,0x07,
+ 0xb9,0x3c,
+ 0xba,0x28,
+ 0xb7,0x90,
+ 0xb8,0x08,
+ 0xbf,0x0c,
+ 0xc0,0x3e,
+ 0xa3,0x0a,
+ 0xa4,0x0f,
+ 0xa5,0x09,//denoise threshold
+ 0xa6,0x16,
+ 0x9f,0x0a,
+ 0xa0,0x0f,
+ 0xa7,0x0a,
+ 0xa8,0x0f,
+ 0xa1,0x18,//0xa1,0x10,
+ 0xa2,0x10,//0xa2,0x04,
+ 0xa9,0x00,//0xa9,0x04,
+ 0xaa,0xa6,
+ //;awb
+ 0x75,0x68,//0x75,0x6a,//
+ 0x76,0x11,//0x76,0x11,//
+ 0x77,0x92,//0x77,0x92,//
+ 0x78,0xa1,//0x78,0xa1,//
+ 0x79,0xe1,//0x79,0xe1,//
+ 0x7a,0x02,//0x7a,0x02,//
+ 0x7c,0x0e,//0x7c,0x05,//
+ 0x7d,0x12,//0x7d,0x08,//
+ 0x7e,0x12,//0x7e,0x08,//
+ 0x7f,0x54,//0x7f,0x7c,//
+ 0x80,0x78,//0x80,0x58,//
+ 0x81,0xa2,//0x81,0x2a,//
+ 0x82,0x80,//0x82,0xc5,//
+ 0x83,0x4e,//0x83,0x46,//
+ 0x84,0x40,//0x84,0x3a,//
+ 0x85,0x4c,//0x85,0x54,//
+ 0x86,0x43,//0x86,0x44,//
+ 0x87,0xf8,//0x87,0xf8,//
+ 0x88,0x08,//0x88,0x08,//
+ 0x89,0x70,//0x89,0x70,//
+ 0x8a,0xf0,//0x8a,0xf0,//
+ 0x8b,0xf0,//0x8b,0xf0,//
+
+ 0x90,0xe3,
+ 0x93,0x10,
+ 0x94,0x20,
+ 0x95,0x10,
+ 0x96,0x18,
+
+ 0xff,0xff,//delay 255ms
+ 0xff,0xff,//delay 255ms
+};
+;
+
+#endif
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/ov2659/ov2659.c b/drivers/media/video/wmt_v4l2/sensors/ov2659/ov2659.c
new file mode 100755
index 00000000..b6e2d636
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/ov2659/ov2659.c
@@ -0,0 +1,346 @@
+/*
+ * Driver for ov2659 CMOS Image Sensor from Omnivision
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <asm/errno.h>
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "ov2659.h"
+
+//#define sensor_write_array(sd, arr, sz) cmos_init_16bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read16addr(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write16addr(sd->i2c_addr, reg, val)
+
+static void sensor_write_array(struct cmos_subdev *sd, uint32_t *arr, size_t sz)
+{
+ uint32_t reg, data;
+ int i;
+
+ for (i = 0; i < sz; i += 2) {
+ reg = arr[i];
+ data = arr[i+1];
+
+ if (reg == 0xffff)
+ msleep(data);
+ else
+ wmt_vid_i2c_write16addr(sd->i2c_addr, reg ,data);
+ }
+}
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint32_t *regs;
+ size_t size;
+};
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+ CMOS_WIN_SIZE("VGA", 640, 480, ov2659_640_480_regs),
+ CMOS_WIN_SIZE("SVGA", 800, 600, ov2659_800_600_regs),
+ CMOS_WIN_SIZE("720p", 1280, 720, ov2659_1280_720_regs),
+ CMOS_WIN_SIZE("UXGA", 1600, 1200, ov2659_1600_1200_regs),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint32_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = ov2659_wb_auto;
+ size = ARRAY_SIZE(ov2659_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = ov2659_wb_incandescent;
+ size = ARRAY_SIZE(ov2659_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = ov2659_wb_daylight;
+ size = ARRAY_SIZE(ov2659_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = ov2659_wb_cloudy;
+ size = ARRAY_SIZE(ov2659_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = ov2659_wb_fluorescent;
+ size = ARRAY_SIZE(ov2659_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ msleep(100);
+ return 0;
+}
+
+static int sensor_s_scenemode(struct cmos_subdev *sd, enum v4l2_scene_mode value)
+{
+ uint32_t *regs;
+ size_t size;
+
+ switch (value) {
+ case SCENE_MODE_AUTO:
+ regs = ov2659_scene_mode_auto;
+ size = ARRAY_SIZE(ov2659_scene_mode_auto);
+ break;
+ case SCENE_MODE_NIGHTSHOT:
+ regs = ov2659_scene_mode_night;
+ size = ARRAY_SIZE(ov2659_scene_mode_night);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_exposure(struct cmos_subdev *sd, enum v4l2_exposure_mode value)
+{
+ uint32_t *regs;
+ size_t size;
+
+ switch (value) {
+ case -2:
+ regs = ov2659_exposure_neg6;
+ size = ARRAY_SIZE(ov2659_exposure_neg6);
+ break;
+ case -1:
+ regs = ov2659_exposure_neg3;
+ size = ARRAY_SIZE(ov2659_exposure_neg3);
+ break;
+ case 0:
+ regs = ov2659_exposure_zero;
+ size = ARRAY_SIZE(ov2659_exposure_zero);
+ break;
+ case 1:
+ regs = ov2659_exposure_pos3;
+ size = ARRAY_SIZE(ov2659_exposure_pos3);
+ break;
+ case 2:
+ regs = ov2659_exposure_pos6;
+ size = ARRAY_SIZE(ov2659_exposure_pos6);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+ uint32_t data = sensor_read(sd, 0x3821);
+
+ switch (value) {
+ case 0:
+ data &= ~0x06;
+ break;
+ case 1:
+ data |= 0x06;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sensor_write(sd, 0x3821, data);
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+ uint32_t data = sensor_read(sd, 0x3820);
+
+ switch (value) {
+ case 0:
+ data &= ~0x06;
+ break;
+ case 1:
+ data |= 0x06;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sensor_write(sd, 0x3820, data);
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ case V4L2_CID_EXPOSURE:
+ return v4l2_ctrl_query_fill(qc, -2, 2, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return sensor_s_scenemode(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return sensor_s_exposure(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ default:
+ case WMT_V4L2_CID_CAMERA_FLASH:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win) {
+ pr_err("%s, s_mbus_fmt failed, width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, win->regs, win->size);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return 0;
+}
+
+static int sensor_enum_framesizes(struct cmos_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ const struct cmos_win_size *win = &cmos_supported_win_sizes[index];
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ uint32_t data = 0;
+
+ data |= (sensor_read(sd, 0x300a) & 0xff) << 8;
+ data |= (sensor_read(sd, 0x300b) & 0xff);
+
+ return (data == sd->id) ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ if (sensor_identify(sd))
+ return -1;
+
+ sensor_write_array(sd, ov2659_default_regs_init,
+ ARRAY_SIZE(ov2659_default_regs_init));
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ sensor_write_array(sd, ov2659_default_regs_exit,
+ ARRAY_SIZE(ov2659_default_regs_exit));
+ return 0;
+}
+
+static struct cmos_subdev_ops ov2659_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+ .enum_framesizes = sensor_enum_framesizes,
+};
+
+struct cmos_subdev ov2659 = {
+ .name = "ov2659",
+ .i2c_addr = 0x30,
+ .id = 0x2656,
+ .max_width = 1600,
+ .max_height = 1200,
+ .ops = &ov2659_ops,
+};
+
+#if 0
+static int __init ov2659_init(void)
+{
+ return cmos_register_sudbdev(&ov2659);
+}
+
+static void __exit ov2659_exit(void)
+{
+ cmos_unregister_subdev(&ov2659);
+ return;
+}
+
+module_init(ov2659_init);
+module_exit(ov2659_exit);
+
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/ov2659/ov2659.h b/drivers/media/video/wmt_v4l2/sensors/ov2659/ov2659.h
new file mode 100755
index 00000000..c5c8b8f3
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/ov2659/ov2659.h
@@ -0,0 +1,1197 @@
+#ifndef OV2659_H
+#define OV2659_H
+
+#define STREAMING_DELAY_MS 300
+
+// Scene Mode
+uint32_t ov2659_scene_mode_auto[] = {
+// 0x3208, 0x00,
+
+ 0x3A00, 0x3c,
+ 0x3a02, 0x03,
+ 0x3a03, 0x9c,
+ 0x3a14, 0x03,
+ 0x3a15, 0x9c,
+
+// 0x3208, 0x10,
+// 0x3208, 0xa0,
+
+};
+
+uint32_t ov2659_scene_mode_night[] = {
+// 0x3208, 0x00,
+
+ 0x3A00, 0x3c,
+ 0x3a02, 0x0e,
+ 0x3a03, 0x70,
+ 0x3a14, 0x0e,
+ 0x3a15, 0x70,
+
+// 0x3208, 0x10,
+// 0x3208, 0xa0,
+};
+
+
+// White Balance
+uint32_t ov2659_wb_auto [] = {
+ 0x3406, 0x00,
+};
+
+uint32_t ov2659_wb_incandescent [] = {
+ 0x3208, 0x00,
+
+ 0x3406, 0x01,
+
+ 0x3400, 0x04,
+ 0x3401, 0xe0,
+ 0x3402, 0x04,
+ 0x3403, 0x00,
+ 0x3404, 0x05,
+ 0x3405, 0xa0,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_wb_fluorescent [] = {
+ 0x3208, 0x00,
+
+ 0x3406, 0x01,
+
+ 0x3400, 0x04,
+ 0x3401, 0x00,
+ 0x3402, 0x04,
+ 0x3403, 0x00,
+ 0x3404, 0x06,
+ 0x3405, 0x50,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_wb_daylight [] = {
+ 0x3208, 0x00,
+
+ 0x3406, 0x01,
+
+ 0x3400, 0x06,
+ 0x3401, 0x10,
+ 0x3402, 0x04,
+ 0x3403, 0x00,
+ 0x3404, 0x04,
+ 0x3405, 0x48,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_wb_cloudy [] = {
+ 0x3208, 0x00,
+
+ 0x3406, 0x01,
+
+ 0x3400, 0x06,
+ 0x3401, 0x30,
+ 0x3402, 0x04,
+ 0x3403, 0x00,
+ 0x3404, 0x04,
+ 0x3405, 0x30,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_wb_tungsten [] = {
+ 0x3208, 0x00,
+
+ 0x3406, 0x01,
+
+ 0x3400, 0x05,
+ 0x3401, 0x48,
+ 0x3402, 0x04,
+ 0x3403, 0x00,
+ 0x3404, 0x05,
+ 0x3405, 0xe0,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+
+// Exposure
+uint32_t ov2659_exposure_neg6[] = {
+ 0x3208, 0x00,
+
+ 0x3a0f, 0x18,
+ 0x3a10, 0x10,
+ 0x3a11, 0x30,
+ 0x3a1b, 0x18,
+ 0x3a1e, 0x10,
+ 0x3a1f, 0x08,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_exposure_neg3[] = {
+ 0x3208, 0x00,
+
+ 0x3a0f, 0x28,
+ 0x3a10, 0x20,
+ 0x3a11, 0x50,
+ 0x3a1b, 0x28,
+ 0x3a1e, 0x20,
+ 0x3a1f, 0x10,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_exposure_zero[] = {
+ 0x3208, 0x00,
+
+ 0x3a0f, 0x30,
+ 0x3a10, 0x28,
+ 0x3a11, 0x61,
+ 0x3a1b, 0x30,
+ 0x3a1e, 0x28,
+ 0x3a1f, 0x14,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_exposure_pos3[] = {
+ 0x3208, 0x00,
+
+ 0x3a0f, 0x38,
+ 0x3a10, 0x30,
+ 0x3a11, 0x80,
+ 0x3a1b, 0x38,
+ 0x3a1e, 0x30,
+ 0x3a1f, 0x18,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_exposure_pos6[] = {
+ 0x3208, 0x00,
+
+ 0x3a0f, 0x58,
+ 0x3a10, 0x68,
+ 0x3a11, 0xb0,
+ 0x3a1b, 0xa8,
+ 0x3a1e, 0x60,
+ 0x3a1f, 0x40,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+/*
+ * The color effect settings
+ */
+uint32_t ov2659_colorfx_none[] = {
+ 0x3208, 0x00,
+
+ 0x5001, 0x1f,
+ 0x507B, 0x06,
+ 0x507e, 0x3a,
+ 0x507f, 0x10,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_colorfx_bw[] = {
+ 0x3208, 0x00,
+
+ 0x5001, 0x1f,
+ 0x507B, 0x26,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_colorfx_sepia[] = {
+ 0x3208, 0x00,
+
+ 0x5001, 0x1f,
+ 0x507B, 0x1e,
+ 0x507e, 0x40,
+ 0x507f, 0xa0,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_colorfx_negative[] = {
+ 0x3208, 0x00,
+
+ 0x5001, 0x1f,
+ 0x507B, 0x46,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_colorfx_emboss[] = {
+
+};
+
+uint32_t ov2659_colorfx_sketch[] = {
+};
+
+uint32_t ov2659_colorfx_sky_blue[] = {
+};
+
+uint32_t ov2659_colorfx_grass_green[] = {
+};
+
+uint32_t ov2659_colorfx_skin_whiten[] = {
+};
+
+uint32_t ov2659_colorfx_vivid[] = {
+};
+
+// Brightness
+uint32_t ov2659_brightness_neg4[] = {
+ 0x3208, 0x00,
+
+ 0x5082, 0x40,
+ 0x5083, 0x09,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_brightness_neg3[] = {
+ 0x3208, 0x00,
+
+ 0x5082, 0x30,
+ 0x5083, 0x09,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_brightness_neg2[] = {
+ 0x3208, 0x00,
+
+ 0x5082, 0x20,
+ 0x5083, 0x09,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_brightness_neg1[] = {
+ 0x3208, 0x00,
+
+ 0x5082, 0x10,
+ 0x5083, 0x09,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_brightness_zero[] = {
+ 0x3208, 0x00,
+
+ 0x5082, 0x00,
+ 0x5083, 0x01,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_brightness_pos1[] = {
+ 0x3208, 0x00,
+
+ 0x5082, 0x10,
+ 0x5083, 0x01,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+
+};
+
+uint32_t ov2659_brightness_pos2[] = {
+ 0x3208, 0x00,
+
+ 0x5082, 0x20,
+ 0x5083, 0x01,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_brightness_pos3[] = {
+ 0x3208, 0x00,
+
+ 0x5082, 0x30,
+ 0x5083, 0x01,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_brightness_pos4[] = {
+ 0x3208, 0x00,
+
+ 0x5082, 0x40,
+ 0x5083, 0x01,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+// Contrast
+uint32_t ov2659_contrast_neg4[] = {
+ 0x3208, 0x00,
+
+ 0x5080, 0x10,
+ 0x5081, 0x10,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_contrast_neg3[] = {
+ 0x3208, 0x00,
+
+ 0x5080, 0x14,
+ 0x5081, 0x14,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_contrast_neg2[] = {
+ 0x3208, 0x00,
+
+ 0x5080, 0x18,
+ 0x5081, 0x18,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_contrast_neg1[] = {
+ 0x3208, 0x00,
+
+ 0x5080, 0x1c,
+ 0x5081, 0x1c,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_contrast_zero[] = {
+ 0x3208, 0x00,
+
+ 0x5080, 0x20,
+ 0x5081, 0x20,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_contrast_pos1[] = {
+ 0x3208, 0x00,
+
+ 0x5080, 0x24,
+ 0x5081, 0x24,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_contrast_pos2[] = {
+ 0x3208, 0x00,
+
+ 0x5080, 0x28,
+ 0x5081, 0x28,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_contrast_pos3[] = {
+ 0x3208, 0x00,
+
+ 0x5080, 0x2c,
+ 0x5081, 0x2c,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_contrast_pos4[] = {
+ 0x3208, 0x00,
+
+ 0x5080, 0x30,
+ 0x5081, 0x30,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+// Saturation
+uint32_t ov2659_saturation_neg4[] = {
+ 0x3208, 0x00,
+
+ 0x5073, 0x1a,//0.6
+ 0x5074, 0xc8,
+ 0x5075, 0xe2,
+ 0x5076, 0xe2,
+ 0x5077, 0xd8,
+ 0x5078, 0x0a,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_saturation_neg3[] = {
+ 0x3208, 0x00,
+
+ 0x5073, 0x1a,//0.7
+ 0x5074, 0xc8,
+ 0x5075, 0xe2,
+ 0x5076, 0xe2,
+ 0x5077, 0xd8,
+ 0x5078, 0x0a,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_saturation_neg2[] = {
+ 0x3208, 0x00,
+
+ 0x5073, 0x1a,//0.8
+ 0x5074, 0xc8,
+ 0x5075, 0xe2,
+ 0x5076, 0xe2,
+ 0x5077, 0xd8,
+ 0x5078, 0x0a,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_saturation_neg1[] = {
+ 0x3208, 0x00,
+
+ 0x5073, 0x1a,//0.9
+ 0x5074, 0xc8,
+ 0x5075, 0xe2,
+ 0x5076, 0xe2,
+ 0x5077, 0xd8,
+ 0x5078, 0x0a,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_saturation_zero[] = {
+ 0x3208, 0x00,
+
+ 0x5073, 0x1a,//1.0
+ 0x5074, 0xc8,
+ 0x5075, 0xe2,
+ 0x5076, 0xe2,
+ 0x5077, 0xd8,
+ 0x5078, 0x0a,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_saturation_pos1[] = {
+ 0x3208, 0x00,
+
+ 0x5073, 0x1a,//1.1
+ 0x5074, 0xc8,
+ 0x5075, 0xe2,
+ 0x5076, 0xe2,
+ 0x5077, 0xd8,
+ 0x5078, 0x0a,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_saturation_pos2[] = {
+ 0x3208, 0x00,
+
+ 0x5073, 0x1a,//1.2
+ 0x5074, 0xc8,
+ 0x5075, 0xe2,
+ 0x5076, 0xe2,
+ 0x5077, 0xd8,
+ 0x5078, 0x0a,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_saturation_pos3[] = {
+ 0x3208, 0x00,
+
+ 0x5073, 0x1a,//1.3
+ 0x5074, 0xc8,
+ 0x5075, 0xe2,
+ 0x5076, 0xe2,
+ 0x5077, 0xd8,
+ 0x5078, 0x0a,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+uint32_t ov2659_saturation_pos4[] = {
+ 0x3208, 0x00,
+
+ 0x5073, 0x1a,//1.4
+ 0x5074, 0xc8,
+ 0x5075, 0xe2,
+ 0x5076, 0xe2,
+ 0x5077, 0xd8,
+ 0x5078, 0x0a,
+
+ 0x3208, 0x10,
+ 0x3208, 0xa0,
+};
+
+
+// Resolution
+uint32_t ov2659_640_480_regs[]={
+ 0x0100, 0x00,
+
+ 0x3a00, 0x3c,
+// 0x3406, 0x00,
+ 0x3503, 0x00,
+
+ 0x5066, 0x28,
+ 0x5067, 0x10,
+ 0x506a, 0x0c,
+ 0x506b, 0x1c,
+
+ 0x3800, 0x00,
+ 0x3801, 0x00,
+ 0x3802, 0x00,
+ 0x3803, 0x00,
+ 0x3804, 0x06,
+ 0x3805, 0x5f,
+ 0x3806, 0x04,
+ 0x3807, 0xb7,
+ 0x3808, 0x02,
+ 0x3809, 0x80,
+ 0x380a, 0x01,
+ 0x380b, 0xe0,
+ 0x3811, 0x08,
+ 0x3813, 0x02,
+ 0x3814, 0x31,
+ 0x3815, 0x31,
+
+ 0x3820, 0x81,
+ 0x3821, 0x01,
+ 0x5002, 0x10,
+ 0x4608, 0x00,
+ 0x4609, 0xa0,
+
+ 0x3623, 0x00,
+ 0x3634, 0x76,
+ 0x3701, 0x44,
+ 0x3208, 0xa1,
+ 0x3705, 0x0c,
+ 0x370a, 0x52,
+
+ 0x3003, 0x80,
+ 0x3004, 0x10,
+ 0x3005, 0x18,
+ 0x3006, 0x0d,
+
+ 0x380c, 0x05,
+ 0x380d, 0x14,
+ 0x380e, 0x02,
+ 0x380f, 0x68,
+
+ 0x3a08, 0x00,
+ 0x3a09, 0xb9,
+ 0x3a0e, 0x03,
+ 0x3a0a, 0x00,
+ 0x3a0b, 0x9a,
+ 0x3a0d, 0x04,
+
+ 0x0100, 0x01,
+
+ 0x301d, 0x08,
+ //delay 10ms
+ 0xffff, 10,
+
+ 0x301d, 0x00,
+
+ 0xffff, STREAMING_DELAY_MS,
+
+ //±ØÐëÖØе÷Óð×ƽºâ²Ëµ¥
+};
+
+uint32_t ov2659_800_600_regs[]={
+ 0x0100, 0x00,
+
+ 0x3a00, 0x3c,
+ 0x3406, 0x00,
+ 0x3503, 0x00,
+
+ 0x5066, 0x28,
+ 0x5067, 0x10,
+ 0x506a, 0x0c,
+ 0x506b, 0x1c,
+
+ 0x3800, 0x00,
+ 0x3801, 0x00,
+ 0x3802, 0x00,
+ 0x3803, 0x00,
+ 0x3804, 0x06,
+ 0x3805, 0x5f,
+ 0x3806, 0x04,
+ 0x3807, 0xb7,
+ 0x3808, 0x03,
+ 0x3809, 0x20,
+ 0x380a, 0x02,
+ 0x380b, 0x58,
+ 0x3811, 0x08,
+ 0x3813, 0x02,
+ 0x3814, 0x31,
+ 0x3815, 0x31,
+
+ 0x3820, 0x81,
+ 0x3821, 0x01,
+ 0x5002, 0x00,
+ 0x4608, 0x00,
+ 0x4609, 0x80,
+
+ 0x3623, 0x00,
+ 0x3634, 0x76,
+ 0x3701, 0x44,
+ 0x3208, 0xa1,
+ 0x3705, 0x0c,
+ 0x370a, 0x52,
+
+ 0x3003, 0x80,
+ 0x3004, 0x10,
+ 0x3005, 0x18,
+ 0x3006, 0x0d,
+
+ 0x380c, 0x05,
+ 0x380d, 0x14,
+ 0x380e, 0x02,
+ 0x380f, 0x68,
+
+ 0x3a08, 0x00,
+ 0x3a09, 0xb9,
+ 0x3a0e, 0x03,
+ 0x3a0a, 0x00,
+ 0x3a0b, 0x9a,
+ 0x3a0d, 0x04,
+
+ 0x0100, 0x01,
+
+ 0x301d, 0x08,
+ //delay 10ms
+ 0xffff, 10,
+
+ 0x301d, 0x00,
+
+ 0xffff, STREAMING_DELAY_MS,
+ //±ØÐëÖØе÷Óð×ƽºâ²Ëµ¥
+};
+
+uint32_t ov2659_1280_720_regs[]={
+ 0x0103, 0x01,
+ //delay 5ms
+ 0xffff, 5,
+
+ 0x3000, 0x0f,
+ 0x3001, 0xff,
+ 0x3002, 0xff,
+ 0x0100, 0x01,
+ 0x3633, 0x3d,
+ 0x3620, 0x02,
+ 0x3631, 0x11,
+ 0x3612, 0x04,
+ 0x3630, 0x20,
+ 0x4702, 0x02,
+ 0x370c, 0x34,
+
+ 0x4003, 0x88,
+
+ 0x3800, 0x00,
+ 0x3801, 0xa0,
+ 0x3802, 0x00,
+ 0x3803, 0xf0,
+ 0x3804, 0x05,
+ 0x3805, 0xbf,
+ 0x3806, 0x03,
+ 0x3807, 0xcb,
+ 0x3808, 0x05,
+ 0x3809, 0x00,
+ 0x380a, 0x02,
+ 0x380b, 0xd0,
+ 0x3811, 0x10,
+ 0x3813, 0x06,
+ 0x3814, 0x11,
+ 0x3815, 0x11,
+
+ 0x3820, 0x80,
+ 0x3821, 0x00,
+ 0x5002, 0x00,
+ 0x4608, 0x00,
+ 0x4609, 0x80,
+
+ 0x3623, 0x02,
+ 0x3634, 0x44,
+ 0x3701, 0x41,
+ 0x3208, 0xa1,
+
+ 0x3208, 0x02,
+ 0x3702, 0x30,
+ 0x3703, 0x48,
+ 0x3704, 0x48,
+ 0x3208, 0x12,
+ 0x3208, 0xa2,
+
+ 0x3705, 0x18,
+ 0x370a, 0x12,
+
+ 0x3003, 0x80,
+ 0x3004, 0x10,//30fps
+ 0x3005, 0x24,
+ 0x3006, 0x0d,
+
+ 0x380c, 0x06,
+ 0x380d, 0x4c,
+ 0x380e, 0x02,
+ 0x380f, 0xe8,
+
+ 0x3a08, 0x00,
+ 0x3a09, 0xdf,
+ 0x3a0e, 0x03,
+ 0x3a0a, 0x00,
+ 0x3a0b, 0xba,
+ 0x3a0d, 0x04,
+
+ 0x350c, 0x00,
+ 0x350d, 0x00,
+ 0x4300, 0x30,
+ 0x5086, 0x02,
+ 0x5000, 0xfb,
+ 0x5001, 0x1f,
+ 0x507e, 0x32,
+ 0x507f, 0x10,
+ 0x507c, 0x80,
+ 0x507d, 0x00,
+ 0x507b, 0x06,
+
+ 0x5025, 0x06,
+ 0x5026, 0x0c,
+ 0x5027, 0x19,
+ 0x5028, 0x34,
+ 0x5029, 0x4e,
+ 0x502a, 0x5f,
+ 0x502b, 0x6d,
+ 0x502c, 0x78,
+ 0x502d, 0x84,
+ 0x502e, 0x95,
+ 0x502f, 0xa5,
+ 0x5030, 0xb4,
+ 0x5031, 0xcc,
+ 0x5032, 0xe2,
+ 0x5033, 0xf6,
+ 0x5034, 0x11,
+
+ 0x5070, 0x28,
+ 0x5071, 0x48,
+ 0x5072, 0x10,
+ 0x5073, 0x1b,
+ 0x5074, 0xd0,
+ 0x5075, 0xeb,
+ 0x5076, 0xeb,
+ 0x5077, 0xe0,
+ 0x5078, 0x0b,
+ 0x5079, 0x98,
+ 0x507a, 0x01,
+
+ 0x5035, 0x6a,
+ 0x5036, 0x11,
+ 0x5037, 0x92,
+ 0x5038, 0x21,
+ 0x5039, 0xe1,
+ 0x503a, 0x01,
+ 0x503c, 0x10,
+ 0x503d, 0x10,
+ 0x503e, 0x10,
+ 0x503f, 0x62,
+ 0x5040, 0x5c,
+ 0x5041, 0x0e,
+ 0x5042, 0x9c,
+ 0x5043, 0x20,
+ 0x5044, 0x36,
+ 0x5045, 0x22,
+ 0x5046, 0x5c,
+ 0x5047, 0xf8,
+ 0x5048, 0x08,
+ 0x5049, 0x70,
+ 0x504a, 0xf0,
+ 0x504b, 0xf0,
+
+ 0x5000, 0xfb,
+ 0x500c, 0x03,
+ 0x500d, 0x22,
+ 0x500e, 0x02,
+ 0x500f, 0x64,
+ 0x5010, 0x5a,
+ 0x5011, 0x18,
+ 0x5012, 0x66,
+ 0x5013, 0x03,
+ 0x5014, 0x24,
+ 0x5015, 0x02,
+ 0x5016, 0x60,
+ 0x5017, 0x58,
+ 0x5018, 0x10,
+ 0x5019, 0x66,
+ 0x501a, 0x03,
+ 0x501b, 0x16,
+ 0x501c, 0x02,
+ 0x501d, 0x68,
+ 0x501e, 0x50,
+ 0x501f, 0x10,
+ 0x5020, 0x66,
+
+ 0x506e, 0x44,
+ 0x5064, 0x08,
+ 0x5065, 0x10,
+ 0x5066, 0x16,
+ 0x5067, 0x10,
+ 0x506c, 0x08,
+ 0x506d, 0x10,
+ 0x506f, 0xa6,
+ 0x5068, 0x08,
+ 0x5069, 0x10,
+ 0x506a, 0x18,
+ 0x506b, 0x28,
+ 0x5084, 0x10,
+ 0x5085, 0x3c,
+ 0x5005, 0x80,
+
+ 0x3a0f, 0x30,
+ 0x3a10, 0x28,
+ 0x3a11, 0x61,
+ 0x3a1b, 0x30,
+ 0x3a1e, 0x28,
+ 0x3a1f, 0x14,
+
+ 0x5060, 0x69,
+ 0x5061, 0xbe,
+ 0x5062, 0xbe,
+ 0x5063, 0x69,
+
+ 0x3a19, 0x38,
+ 0x4009, 0x02,
+
+ 0x3503, 0x00,
+
+ 0x3a00, 0x38,
+ 0x3a05, 0x30,
+ 0x3a14, 0x02,
+ 0x3a15, 0xe8,
+ 0x3a02, 0x02,
+ 0x3a03, 0xe8,
+
+ 0x3011, 0x82,
+};
+
+uint32_t ov2659_1600_1200_regs[]={
+ 0x0100, 0x00,
+
+ 0x3a00, 0x38,
+ 0x3406, 0x01,
+ 0x3503, 0x07,
+
+ 0x5066, 0x20,
+ 0x5067, 0x08,
+ 0x506a, 0x0c,
+ 0x506b, 0x1c,
+
+ 0x3800, 0x00,
+ 0x3801, 0x00,
+ 0x3802, 0x00,
+ 0x3803, 0x00,
+ 0x3804, 0x06,
+ 0x3805, 0x5f,
+ 0x3806, 0x04,
+ 0x3807, 0xbb,
+ 0x3808, 0x06,
+ 0x3809, 0x40,
+ 0x380a, 0x04,
+ 0x380b, 0xb0,
+ 0x3811, 0x10,
+ 0x3813, 0x06,
+ 0x3814, 0x11,
+ 0x3815, 0x11,
+
+ 0x3820, 0x80,
+ 0x3821, 0x00,
+ 0x5002, 0x00,
+ 0x4608, 0x00,
+ 0x4609, 0x80,
+
+ 0x3623, 0x00,
+ 0x3634, 0x44,
+ 0x3701, 0x44,
+ 0x3208, 0xa2,
+ 0x3705, 0x18,
+ 0x370a, 0x12,
+
+ 0x3003, 0x80,
+ 0x3004, 0x20,
+ 0x3005, 0x24,
+ 0x3006, 0x0d,
+
+ 0x380c, 0x07,
+ 0x380d, 0x9f,
+ 0x380e, 0x04,
+ 0x380f, 0xd0,
+
+ 0x3a08, 0x00,
+ 0x3a09, 0x5c,
+ 0x3a0e, 0x0d,
+ 0x3a0a, 0x00,
+ 0x3a0b, 0x4d,
+ 0x3a0d, 0x10,
+
+ 0x4003, 0x88,
+
+ 0x0100, 0x01,
+ 0xffff, STREAMING_DELAY_MS,
+};
+
+// init
+uint32_t ov2659_default_regs_init[]={
+ 0x0103, 0x01,
+ //delay 5ms
+ 0xffff, 5,
+
+ 0x3000, 0x0f,
+ 0x3001, 0xff,
+ 0x3002, 0xff,
+ 0x0100, 0x01,
+ 0x3633, 0x3d,
+ 0x3620, 0x02,
+ 0x3631, 0x11,
+ 0x3612, 0x04,
+ 0x3630, 0x20,
+ 0x4702, 0x02,
+ 0x370c, 0x34,
+
+ 0x4003, 0x88,
+
+ 0x3800, 0x00,
+ 0x3801, 0x00,
+ 0x3802, 0x00,
+ 0x3803, 0x00,
+ 0x3804, 0x06,
+ 0x3805, 0x5f,
+ 0x3806, 0x04,
+ 0x3807, 0xb7,
+ 0x3808, 0x03,
+ 0x3809, 0x20,
+ 0x380a, 0x02,
+ 0x380b, 0x58,
+ 0x3811, 0x08,
+ 0x3813, 0x02,
+ 0x3814, 0x31,
+ 0x3815, 0x31,
+
+ 0x3820, 0x81,
+ 0x3821, 0x01,
+ 0x5002, 0x00,
+ 0x4608, 0x00,
+ 0x4609, 0x80,
+
+ 0x3623, 0x00,
+ 0x3634, 0x76,
+ 0x3701, 0x44,
+
+ 0x3208, 0x01,
+ 0x3702, 0x18,
+ 0x3703, 0x24,
+ 0x3704, 0x24,
+ 0x3208, 0x11,
+
+ 0x3208, 0x02,
+ 0x3702, 0x30,
+ 0x3703, 0x48,
+ 0x3704, 0x48,
+ 0x3208, 0x12,
+
+ 0x3705, 0x0c,
+ 0x370a, 0x52,
+
+ 0x3003, 0x80,
+ 0x3004, 0x10,
+ 0x3005, 0x18,
+ 0x3006, 0x0d,
+
+ 0x380c, 0x05,
+ 0x380d, 0x14,
+ 0x380e, 0x02,
+ 0x380f, 0x68,
+
+ 0x3a08, 0x00,
+ 0x3a09, 0xb9,
+ 0x3a0e, 0x03,
+ 0x3a0a, 0x00,
+ 0x3a0b, 0x9a,
+ 0x3a0d, 0x04,
+
+ 0x3a00, 0x3c,
+ 0x3a02, 0x03,
+ 0x3a03, 0x9c,
+ 0x3a14, 0x03,
+ 0x3a15, 0x9c,
+
+ 0x350c, 0x00,
+ 0x350d, 0x00,
+ 0x4300, 0x30,
+ 0x5086, 0x02,
+ 0x5000, 0xfb,
+ 0x5001, 0x1f,
+ 0x507e, 0x3a,
+ 0x507f, 0x10,
+ 0x507c, 0x80,
+ 0x507d, 0x00,
+ 0x507b, 0x06,
+
+ 0x5025, 0x06,
+ 0x5026, 0x0c,
+ 0x5027, 0x1c,
+ 0x5028, 0x36,
+ 0x5029, 0x4e,
+ 0x502a, 0x5f,
+ 0x502b, 0x6d,
+ 0x502c, 0x78,
+ 0x502d, 0x84,
+ 0x502e, 0x95,
+ 0x502f, 0xa5,
+ 0x5030, 0xb4,
+ 0x5031, 0xc8,
+ 0x5032, 0xde,
+ 0x5033, 0xf0,
+ 0x5034, 0x15,
+
+ 0x5070, 0x28,
+ 0x5071, 0x48,
+ 0x5072, 0x10,
+ 0x5073, 0x1a,
+ 0x5074, 0xc8,
+ 0x5075, 0xe2,
+ 0x5076, 0xe2,
+ 0x5077, 0xd8,
+ 0x5078, 0x0a,
+ 0x5079, 0x98,
+ 0x507a, 0x01,
+
+ 0x5035, 0x6a,
+ 0x5036, 0x11,
+ 0x5037, 0x92,
+ 0x5038, 0x25,
+ 0x5039, 0xe1,
+ 0x503a, 0x01,
+ 0x503c, 0x11,
+ 0x503d, 0x10,
+ 0x503e, 0x10,
+ 0x503f, 0x5a,
+ 0x5040, 0x51,
+ 0x5041, 0x0e,
+ 0x5042, 0xa0,
+ 0x5043, 0x49,
+ 0x5044, 0x34,
+ 0x5045, 0x22,
+ 0x5046, 0x3f,
+ 0x5047, 0xf8,
+ 0x5048, 0x08,
+ 0x5049, 0x70,
+ 0x504a, 0xf0,
+ 0x504b, 0xf0,
+
+ 0x5000, 0xfb,
+ 0x500c, 0x03,
+ 0x500d, 0x1d,
+ 0x500e, 0x02,
+ 0x500f, 0x63,
+ 0x5010, 0x60,
+ 0x5011, 0x00,
+ 0x5012, 0x66,
+ 0x5013, 0x03,
+ 0x5014, 0x24,
+ 0x5015, 0x02,
+ 0x5016, 0x66,
+ 0x5017, 0x62,
+ 0x5018, 0x00,
+ 0x5019, 0x66,
+ 0x501a, 0x03,
+ 0x501b, 0x16,
+ 0x501c, 0x02,
+ 0x501d, 0x76,
+ 0x501e, 0x5d,
+ 0x501f, 0x00,
+ 0x5020, 0x66,
+
+ 0x506e, 0x44,
+ 0x5064, 0x08,
+ 0x5065, 0x10,
+ 0x5066, 0x16,
+ 0x5067, 0x10,
+ 0x506c, 0x08,
+ 0x506d, 0x10,
+ 0x506f, 0xa6,
+ 0x5068, 0x08,
+ 0x5069, 0x10,
+ 0x506a, 0x18,
+ 0x506b, 0x28,
+ 0x5084, 0x10,
+ 0x5085, 0x3c,
+ 0x5005, 0x80,
+
+ 0x3a0f, 0x30,
+ 0x3a10, 0x28,
+ 0x3a11, 0x61,
+ 0x3a1b, 0x30,
+ 0x3a1e, 0x28,
+ 0x3a1f, 0x14,
+
+ 0x5060, 0x69,
+ 0x5061, 0xbe,
+ 0x5062, 0xbe,
+ 0x5063, 0x69,
+
+ 0x3a19, 0x38,
+ 0x4009, 0x02,
+
+ 0x3503, 0x00,
+
+ 0x3011, 0x82,
+};
+
+uint32_t ov2659_default_regs_exit[]={
+ 0x0100, 0x00,
+ 0x3000, 0x0c,
+ 0x3001, 0x00,
+ 0x3002, 0x1f,
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/ov3640/ov3640.h b/drivers/media/video/wmt_v4l2/sensors/ov3640/ov3640.h
new file mode 100755
index 00000000..0851a1a7
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/ov3640/ov3640.h
@@ -0,0 +1,3620 @@
+#ifndef OV3640_H
+#define OV3640_H
+
+unsigned int ov3640_640x480[]={
+
+0x304d, 0x45,
+0x30a7, 0x5e,
+0x3087, 0x16,
+0x309c, 0x1a,
+0x30a2, 0xe4,
+0x30aa, 0x42,
+0x30b0, 0xff,
+0x30b1, 0xff,
+0x30b2, 0x10,
+0x300e, 0x32,
+0x300f, 0x21,
+0x3010, 0x20,
+0x3011, 0x04,
+0x304c, 0x81,
+0x30d7, 0x10,
+0x30d9, 0x0d,
+0x30db, 0x08,
+0x3016, 0x82,
+0x3018, 0x38,
+0x3019, 0x30,
+0x301a, 0x61,
+0x307d, 0x00,
+0x3087, 0x02,
+0x3082, 0x20,
+0x3015, 0x12,
+0x3014, 0x04,
+0x3013, 0xf7,
+0x303c, 0x08,
+0x303d, 0x18,
+0x303e, 0x06,
+0x303f, 0x0c,
+0x3030, 0x62,
+0x3031, 0x26,
+0x3032, 0xe6,
+0x3033, 0x6e,
+0x3034, 0xea,
+0x3035, 0xae,
+0x3036, 0xa6,
+0x3037, 0x6a,
+0x3104, 0x02,
+0x3105, 0xfd,
+0x3106, 0x00,
+0x3107, 0xff,
+0x3300, 0x12,
+0x3301, 0xde,
+0x3302, 0xef,
+0x336a, 0x52,
+0x3370, 0x46,
+0x3376, 0x38,
+0x3300, 0x13,
+0x30b8, 0x20,
+0x30b9, 0x17,
+0x30ba, 0x04,
+0x30bb, 0x08,
+0x3507, 0x06,
+0x350a, 0x4f,
+0x3100, 0x02,
+0x3301, 0xde,
+0x3304, 0x00,
+0x3400, 0x00,
+0x3404, 0x00,
+0x335f, 0x68,
+0x3360, 0x18,
+0x3361, 0x0c,
+0x3362, 0x12,
+0x3363, 0x88,
+0x3364, 0xe4,
+0x3403, 0x42,
+0x3088, 0x02,
+0x3089, 0x80,
+0x308a, 0x01,
+0x308b, 0xe0,
+0x308d, 0x04,
+0x3086, 0x03,
+0x3086, 0x00,
+0x3011, 0x01,
+0x304c, 0x85,
+0x3300, 0x13,
+0x3367, 0x34,
+0x3368, 0x44,
+0x3369, 0x00,
+0x336A, 0x3F,
+0x336B, 0x07,
+0x336C, 0x00,
+0x336D, 0x34,
+0x336E, 0x5E,
+0x336F, 0x00,
+0x3370, 0x37,
+0x3371, 0x07,
+0x3372, 0x00,
+0x3373, 0x34,
+0x3374, 0x56,
+0x3375, 0x20,
+0x3376, 0x34,
+0x3377, 0x07,
+0x3378, 0x00,
+0x3317, 0x04,
+0x3316, 0xf8,
+0x3312, 0x0f,
+0x3314, 0x23,
+0x3313, 0x1f,
+0x3315, 0x33,
+0x3311, 0xb1,
+0x3310, 0xda,
+0x330c, 0x18,
+0x330d, 0x18,
+0x330e, 0x4c,
+0x330f, 0x6a,
+0x330b, 0x1c,
+0x3306, 0x5c,
+0x3307, 0x11,
+0x3308, 0x25,
+0x3340, 0x0E,
+0x3341, 0x4D,
+0x3342, 0x2C,
+0x3343, 0x2C,
+0x3344, 0x8A,
+0x3345, 0xB4,
+0x3346, 0xAA,
+0x3347, 0xB6,
+0x3348, 0x0C,
+0x3349, 0x98,
+0x333F, 0x04,
+0x3355, 0x02,
+0x3358, 0x46,
+0x3359, 0x46,
+0x331b, 0x08,
+0x331c, 0x16,
+0x331d, 0x2d,
+0x331e, 0x54,
+0x331f, 0x66,
+0x3320, 0x73,
+0x3321, 0x80,
+0x3322, 0x8c,
+0x3323, 0x95,
+0x3324, 0x9d,
+0x3325, 0xac,
+0x3326, 0xb8,
+0x3327, 0xcc,
+0x3328, 0xdd,
+0x3329, 0xee,
+0x332a, 0x1d,
+0x3018, 0x40,
+0x3019, 0x38,
+0x332e, 0x04,
+0x332f, 0x04,
+0x3331, 0x02,
+0x3014, 0x84,
+0x3013, 0xe7,
+0x3070, 0x00,
+0x3071, 0xeb,
+0x3072, 0x00,
+0x3073, 0xc4,
+0x301c, 0x05,
+0x301d, 0x06,
+
+//the following is from ovt file
+
+0x308c,0x00,
+0x3104,0x02,
+0x3105,0xff,
+0x3106,0x00,
+0x3107,0xff,
+0x8000,0x02,
+0x8001,0x00,
+0x8002,0x06,
+0x8003,0x02,
+0x8004,0x07,
+0x8005,0xa9,
+0x8006,0x78,
+0x8007,0x7f,
+0x8008,0xe4,
+0x8009,0xf6,
+0x800a,0xd8,
+0x800b,0xfd,
+0x800c,0x75,
+0x800d,0x81,
+0x800e,0x45,
+0x800f,0x02,
+0x8010,0x0a,
+0x8011,0xdf,
+0x8012,0x32,
+0x8013,0x02,
+0x8014,0x00,
+0x8015,0x12,
+0x8016,0xe8,
+0x8017,0x8f,
+0x8018,0xf0,
+0x8019,0xa4,
+0x801a,0xcc,
+0x801b,0x8b,
+0x801c,0xf0,
+0x801d,0xa4,
+0x801e,0x2c,
+0x801f,0xfc,
+0x8020,0xe9,
+0x8021,0x8e,
+0x8022,0xf0,
+0x8023,0xa4,
+0x8024,0x2c,
+0x8025,0xfc,
+0x8026,0x8a,
+0x8027,0xf0,
+0x8028,0xed,
+0x8029,0xa4,
+0x802a,0x2c,
+0x802b,0xfc,
+0x802c,0xea,
+0x802d,0x8e,
+0x802e,0xf0,
+0x802f,0xa4,
+0x8030,0xcd,
+0x8031,0xa8,
+0x8032,0xf0,
+0x8033,0x8b,
+0x8034,0xf0,
+0x8035,0xa4,
+0x8036,0x2d,
+0x8037,0xcc,
+0x8038,0x38,
+0x8039,0x25,
+0x803a,0xf0,
+0x803b,0xfd,
+0x803c,0xe9,
+0x803d,0x8f,
+0x803e,0xf0,
+0x803f,0xa4,
+0x8040,0x2c,
+0x8041,0xcd,
+0x8042,0x35,
+0x8043,0xf0,
+0x8044,0xfc,
+0x8045,0xeb,
+0x8046,0x8e,
+0x8047,0xf0,
+0x8048,0xa4,
+0x8049,0xfe,
+0x804a,0xa9,
+0x804b,0xf0,
+0x804c,0xeb,
+0x804d,0x8f,
+0x804e,0xf0,
+0x804f,0xa4,
+0x8050,0xcf,
+0x8051,0xc5,
+0x8052,0xf0,
+0x8053,0x2e,
+0x8054,0xcd,
+0x8055,0x39,
+0x8056,0xfe,
+0x8057,0xe4,
+0x8058,0x3c,
+0x8059,0xfc,
+0x805a,0xea,
+0x805b,0xa4,
+0x805c,0x2d,
+0x805d,0xce,
+0x805e,0x35,
+0x805f,0xf0,
+0x8060,0xfd,
+0x8061,0xe4,
+0x8062,0x3c,
+0x8063,0xfc,
+0x8064,0x22,
+0x8065,0x75,
+0x8066,0xf0,
+0x8067,0x08,
+0x8068,0x75,
+0x8069,0x82,
+0x806a,0x00,
+0x806b,0xef,
+0x806c,0x2f,
+0x806d,0xff,
+0x806e,0xee,
+0x806f,0x33,
+0x8070,0xfe,
+0x8071,0xcd,
+0x8072,0x33,
+0x8073,0xcd,
+0x8074,0xcc,
+0x8075,0x33,
+0x8076,0xcc,
+0x8077,0xc5,
+0x8078,0x82,
+0x8079,0x33,
+0x807a,0xc5,
+0x807b,0x82,
+0x807c,0x9b,
+0x807d,0xed,
+0x807e,0x9a,
+0x807f,0xec,
+0x8080,0x99,
+0x8081,0xe5,
+0x8082,0x82,
+0x8083,0x98,
+0x8084,0x40,
+0x8085,0x0c,
+0x8086,0xf5,
+0x8087,0x82,
+0x8088,0xee,
+0x8089,0x9b,
+0x808a,0xfe,
+0x808b,0xed,
+0x808c,0x9a,
+0x808d,0xfd,
+0x808e,0xec,
+0x808f,0x99,
+0x8090,0xfc,
+0x8091,0x0f,
+0x8092,0xd5,
+0x8093,0xf0,
+0x8094,0xd6,
+0x8095,0xe4,
+0x8096,0xce,
+0x8097,0xfb,
+0x8098,0xe4,
+0x8099,0xcd,
+0x809a,0xfa,
+0x809b,0xe4,
+0x809c,0xcc,
+0x809d,0xf9,
+0x809e,0xa8,
+0x809f,0x82,
+0x80a0,0x22,
+0x80a1,0xb8,
+0x80a2,0x00,
+0x80a3,0xc1,
+0x80a4,0xb9,
+0x80a5,0x00,
+0x80a6,0x59,
+0x80a7,0xba,
+0x80a8,0x00,
+0x80a9,0x2d,
+0x80aa,0xec,
+0x80ab,0x8b,
+0x80ac,0xf0,
+0x80ad,0x84,
+0x80ae,0xcf,
+0x80af,0xce,
+0x80b0,0xcd,
+0x80b1,0xfc,
+0x80b2,0xe5,
+0x80b3,0xf0,
+0x80b4,0xcb,
+0x80b5,0xf9,
+0x80b6,0x78,
+0x80b7,0x18,
+0x80b8,0xef,
+0x80b9,0x2f,
+0x80ba,0xff,
+0x80bb,0xee,
+0x80bc,0x33,
+0x80bd,0xfe,
+0x80be,0xed,
+0x80bf,0x33,
+0x80c0,0xfd,
+0x80c1,0xec,
+0x80c2,0x33,
+0x80c3,0xfc,
+0x80c4,0xeb,
+0x80c5,0x33,
+0x80c6,0xfb,
+0x80c7,0x10,
+0x80c8,0xd7,
+0x80c9,0x03,
+0x80ca,0x99,
+0x80cb,0x40,
+0x80cc,0x04,
+0x80cd,0xeb,
+0x80ce,0x99,
+0x80cf,0xfb,
+0x80d0,0x0f,
+0x80d1,0xd8,
+0x80d2,0xe5,
+0x80d3,0xe4,
+0x80d4,0xf9,
+0x80d5,0xfa,
+0x80d6,0x22,
+0x80d7,0x78,
+0x80d8,0x18,
+0x80d9,0xef,
+0x80da,0x2f,
+0x80db,0xff,
+0x80dc,0xee,
+0x80dd,0x33,
+0x80de,0xfe,
+0x80df,0xed,
+0x80e0,0x33,
+0x80e1,0xfd,
+0x80e2,0xec,
+0x80e3,0x33,
+0x80e4,0xfc,
+0x80e5,0xc9,
+0x80e6,0x33,
+0x80e7,0xc9,
+0x80e8,0x10,
+0x80e9,0xd7,
+0x80ea,0x05,
+0x80eb,0x9b,
+0x80ec,0xe9,
+0x80ed,0x9a,
+0x80ee,0x40,
+0x80ef,0x07,
+0x80f0,0xec,
+0x80f1,0x9b,
+0x80f2,0xfc,
+0x80f3,0xe9,
+0x80f4,0x9a,
+0x80f5,0xf9,
+0x80f6,0x0f,
+0x80f7,0xd8,
+0x80f8,0xe0,
+0x80f9,0xe4,
+0x80fa,0xc9,
+0x80fb,0xfa,
+0x80fc,0xe4,
+0x80fd,0xcc,
+0x80fe,0xfb,
+0x80ff,0x22,
+0x8100,0x75,
+0x8101,0xf0,
+0x8102,0x10,
+0x8103,0xef,
+0x8104,0x2f,
+0x8105,0xff,
+0x8106,0xee,
+0x8107,0x33,
+0x8108,0xfe,
+0x8109,0xed,
+0x810a,0x33,
+0x810b,0xfd,
+0x810c,0xcc,
+0x810d,0x33,
+0x810e,0xcc,
+0x810f,0xc8,
+0x8110,0x33,
+0x8111,0xc8,
+0x8112,0x10,
+0x8113,0xd7,
+0x8114,0x07,
+0x8115,0x9b,
+0x8116,0xec,
+0x8117,0x9a,
+0x8118,0xe8,
+0x8119,0x99,
+0x811a,0x40,
+0x811b,0x0a,
+0x811c,0xed,
+0x811d,0x9b,
+0x811e,0xfd,
+0x811f,0xec,
+0x8120,0x9a,
+0x8121,0xfc,
+0x8122,0xe8,
+0x8123,0x99,
+0x8124,0xf8,
+0x8125,0x0f,
+0x8126,0xd5,
+0x8127,0xf0,
+0x8128,0xda,
+0x8129,0xe4,
+0x812a,0xcd,
+0x812b,0xfb,
+0x812c,0xe4,
+0x812d,0xcc,
+0x812e,0xfa,
+0x812f,0xe4,
+0x8130,0xc8,
+0x8131,0xf9,
+0x8132,0x22,
+0x8133,0xe8,
+0x8134,0x60,
+0x8135,0x0f,
+0x8136,0xef,
+0x8137,0xc3,
+0x8138,0x33,
+0x8139,0xff,
+0x813a,0xee,
+0x813b,0x33,
+0x813c,0xfe,
+0x813d,0xed,
+0x813e,0x33,
+0x813f,0xfd,
+0x8140,0xec,
+0x8141,0x33,
+0x8142,0xfc,
+0x8143,0xd8,
+0x8144,0xf1,
+0x8145,0x22,
+0x8146,0xa4,
+0x8147,0x25,
+0x8148,0x82,
+0x8149,0xf5,
+0x814a,0x82,
+0x814b,0xe5,
+0x814c,0xf0,
+0x814d,0x35,
+0x814e,0x83,
+0x814f,0xf5,
+0x8150,0x83,
+0x8151,0x22,
+0x8152,0xd0,
+0x8153,0x83,
+0x8154,0xd0,
+0x8155,0x82,
+0x8156,0xf8,
+0x8157,0xe4,
+0x8158,0x93,
+0x8159,0x70,
+0x815a,0x12,
+0x815b,0x74,
+0x815c,0x01,
+0x815d,0x93,
+0x815e,0x70,
+0x815f,0x0d,
+0x8160,0xa3,
+0x8161,0xa3,
+0x8162,0x93,
+0x8163,0xf8,
+0x8164,0x74,
+0x8165,0x01,
+0x8166,0x93,
+0x8167,0xf5,
+0x8168,0x82,
+0x8169,0x88,
+0x816a,0x83,
+0x816b,0xe4,
+0x816c,0x73,
+0x816d,0x74,
+0x816e,0x02,
+0x816f,0x93,
+0x8170,0x68,
+0x8171,0x60,
+0x8172,0xef,
+0x8173,0xa3,
+0x8174,0xa3,
+0x8175,0xa3,
+0x8176,0x80,
+0x8177,0xdf,
+0x8178,0x75,
+0x8179,0x0c,
+0x817a,0x0a,
+0x817b,0xa2,
+0x817c,0xaf,
+0x817d,0x92,
+0x817e,0x22,
+0x817f,0xc2,
+0x8180,0xaf,
+0x8181,0xc2,
+0x8182,0x23,
+0x8183,0x12,
+0x8184,0x04,
+0x8185,0x07,
+0x8186,0x12,
+0x8187,0x04,
+0x8188,0x4d,
+0x8189,0x12,
+0x818a,0x04,
+0x818b,0x07,
+0x818c,0x75,
+0x818d,0x32,
+0x818e,0x05,
+0x818f,0xaf,
+0x8190,0x32,
+0x8191,0x15,
+0x8192,0x32,
+0x8193,0xef,
+0x8194,0x70,
+0x8195,0xf9,
+0x8196,0xd2,
+0x8197,0x18,
+0x8198,0x12,
+0x8199,0x04,
+0x819a,0x09,
+0x819b,0x75,
+0x819c,0x32,
+0x819d,0x0a,
+0x819e,0xaf,
+0x819f,0x32,
+0x81a0,0x15,
+0x81a1,0x32,
+0x81a2,0xef,
+0x81a3,0x70,
+0x81a4,0xf9,
+0x81a5,0xc2,
+0x81a6,0x19,
+0x81a7,0x12,
+0x81a8,0x04,
+0x81a9,0x09,
+0x81aa,0x75,
+0x81ab,0x32,
+0x81ac,0x05,
+0x81ad,0xaf,
+0x81ae,0x32,
+0x81af,0x15,
+0x81b0,0x32,
+0x81b1,0xef,
+0x81b2,0x70,
+0x81b3,0xf9,
+0x81b4,0xc2,
+0x81b5,0x18,
+0x81b6,0x12,
+0x81b7,0x04,
+0x81b8,0x09,
+0x81b9,0x75,
+0x81ba,0x32,
+0x81bb,0x05,
+0x81bc,0xaf,
+0x81bd,0x32,
+0x81be,0x15,
+0x81bf,0x32,
+0x81c0,0xef,
+0x81c1,0x70,
+0x81c2,0xf9,
+0x81c3,0x75,
+0x81c4,0x0d,
+0x81c5,0x18,
+0x81c6,0x12,
+0x81c7,0x02,
+0x81c8,0xb8,
+0x81c9,0x75,
+0x81ca,0x32,
+0x81cb,0x0a,
+0x81cc,0xaf,
+0x81cd,0x32,
+0x81ce,0x15,
+0x81cf,0x32,
+0x81d0,0xef,
+0x81d1,0x70,
+0x81d2,0xf9,
+0x81d3,0xd2,
+0x81d4,0x18,
+0x81d5,0x12,
+0x81d6,0x04,
+0x81d7,0x09,
+0x81d8,0x12,
+0x81d9,0x04,
+0x81da,0x97,
+0x81db,0xaf,
+0x81dc,0x32,
+0x81dd,0x15,
+0x81de,0x32,
+0x81df,0xef,
+0x81e0,0x70,
+0x81e1,0xf9,
+0x81e2,0xc2,
+0x81e3,0x18,
+0x81e4,0x12,
+0x81e5,0x04,
+0x81e6,0x09,
+0x81e7,0x75,
+0x81e8,0x32,
+0x81e9,0x0a,
+0x81ea,0xaf,
+0x81eb,0x32,
+0x81ec,0x15,
+0x81ed,0x32,
+0x81ee,0xef,
+0x81ef,0x70,
+0x81f0,0xf9,
+0x81f1,0x30,
+0x81f2,0x11,
+0x81f3,0x03,
+0x81f4,0x02,
+0x81f5,0x02,
+0x81f6,0x6f,
+0x81f7,0x12,
+0x81f8,0x04,
+0x81f9,0x07,
+0x81fa,0x12,
+0x81fb,0x04,
+0x81fc,0x4d,
+0x81fd,0xe5,
+0x81fe,0x0a,
+0x81ff,0xf5,
+0x8200,0x0d,
+0x8201,0x12,
+0x8202,0x02,
+0x8203,0xb8,
+0x8204,0x75,
+0x8205,0x32,
+0x8206,0x0a,
+0x8207,0xaf,
+0x8208,0x32,
+0x8209,0x15,
+0x820a,0x32,
+0x820b,0xef,
+0x820c,0x70,
+0x820d,0xf9,
+0x820e,0xd2,
+0x820f,0x18,
+0x8210,0x12,
+0x8211,0x04,
+0x8212,0x09,
+0x8213,0x12,
+0x8214,0x04,
+0x8215,0x97,
+0x8216,0xaf,
+0x8217,0x32,
+0x8218,0x15,
+0x8219,0x32,
+0x821a,0xef,
+0x821b,0x70,
+0x821c,0xf9,
+0x821d,0xc2,
+0x821e,0x18,
+0x821f,0x12,
+0x8220,0x04,
+0x8221,0x09,
+0x8222,0x75,
+0x8223,0x32,
+0x8224,0x0a,
+0x8225,0xaf,
+0x8226,0x32,
+0x8227,0x15,
+0x8228,0x32,
+0x8229,0xef,
+0x822a,0x70,
+0x822b,0xf9,
+0x822c,0x30,
+0x822d,0x11,
+0x822e,0x04,
+0x822f,0x15,
+0x8230,0x0c,
+0x8231,0x80,
+0x8232,0x45,
+0x8233,0x12,
+0x8234,0x04,
+0x8235,0x07,
+0x8236,0x12,
+0x8237,0x04,
+0x8238,0x4d,
+0x8239,0x85,
+0x823a,0x0b,
+0x823b,0x0d,
+0x823c,0x12,
+0x823d,0x0a,
+0x823e,0x9b,
+0x823f,0xc2,
+0x8240,0x0f,
+0x8241,0x12,
+0x8242,0x04,
+0x8243,0x4f,
+0x8244,0x75,
+0x8245,0x32,
+0x8246,0x0a,
+0x8247,0xaf,
+0x8248,0x32,
+0x8249,0x15,
+0x824a,0x32,
+0x824b,0xef,
+0x824c,0x70,
+0x824d,0xf9,
+0x824e,0xd2,
+0x824f,0x18,
+0x8250,0x12,
+0x8251,0x04,
+0x8252,0x09,
+0x8253,0x12,
+0x8254,0x04,
+0x8255,0x97,
+0x8256,0xaf,
+0x8257,0x32,
+0x8258,0x15,
+0x8259,0x32,
+0x825a,0xef,
+0x825b,0x70,
+0x825c,0xf9,
+0x825d,0xc2,
+0x825e,0x18,
+0x825f,0x12,
+0x8260,0x04,
+0x8261,0x09,
+0x8262,0x75,
+0x8263,0x32,
+0x8264,0x0a,
+0x8265,0xaf,
+0x8266,0x32,
+0x8267,0x15,
+0x8268,0x32,
+0x8269,0xef,
+0x826a,0x70,
+0x826b,0xf9,
+0x826c,0x30,
+0x826d,0x11,
+0x826e,0x06,
+0x826f,0x15,
+0x8270,0x0c,
+0x8271,0xd2,
+0x8272,0x23,
+0x8273,0x80,
+0x8274,0x03,
+0x8275,0xe4,
+0x8276,0xf5,
+0x8277,0x0c,
+0x8278,0x12,
+0x8279,0x04,
+0x827a,0x07,
+0x827b,0x12,
+0x827c,0x04,
+0x827d,0x4d,
+0x827e,0xc2,
+0x827f,0x19,
+0x8280,0x12,
+0x8281,0x04,
+0x8282,0x09,
+0x8283,0x75,
+0x8284,0x32,
+0x8285,0x05,
+0x8286,0xaf,
+0x8287,0x32,
+0x8288,0x15,
+0x8289,0x32,
+0x828a,0xef,
+0x828b,0x70,
+0x828c,0xf9,
+0x828d,0xd2,
+0x828e,0x18,
+0x828f,0x12,
+0x8290,0x04,
+0x8291,0x09,
+0x8292,0x75,
+0x8293,0x32,
+0x8294,0x05,
+0x8295,0xaf,
+0x8296,0x32,
+0x8297,0x15,
+0x8298,0x32,
+0x8299,0xef,
+0x829a,0x70,
+0x829b,0xf9,
+0x829c,0x12,
+0x829d,0x04,
+0x829e,0x07,
+0x829f,0x75,
+0x82a0,0x32,
+0x82a1,0x05,
+0x82a2,0xaf,
+0x82a3,0x32,
+0x82a4,0x15,
+0x82a5,0x32,
+0x82a6,0xef,
+0x82a7,0x70,
+0x82a8,0xf9,
+0x82a9,0xa2,
+0x82aa,0x22,
+0x82ab,0x92,
+0x82ac,0xaf,
+0x82ad,0xe5,
+0x82ae,0x0c,
+0x82af,0xd3,
+0x82b0,0x94,
+0x82b1,0x00,
+0x82b2,0x40,
+0x82b3,0x03,
+0x82b4,0x02,
+0x82b5,0x01,
+0x82b6,0x7f,
+0x82b7,0x22,
+0x82b8,0x12,
+0x82b9,0x0a,
+0x82ba,0x9b,
+0x82bb,0xc2,
+0x82bc,0x0f,
+0x82bd,0x90,
+0x82be,0x30,
+0x82bf,0xb1,
+0x82c0,0xe5,
+0x82c1,0x21,
+0x82c2,0xf0,
+0x82c3,0x22,
+0x82c4,0x90,
+0x82c5,0x33,
+0x82c6,0x5f,
+0x82c7,0xe0,
+0x82c8,0x54,
+0x82c9,0x0f,
+0x82ca,0xfe,
+0x82cb,0xa3,
+0x82cc,0xe0,
+0x82cd,0xfd,
+0x82ce,0xed,
+0x82cf,0xff,
+0x82d0,0xee,
+0x82d1,0x54,
+0x82d2,0x0f,
+0x82d3,0xf5,
+0x82d4,0x0d,
+0x82d5,0x8f,
+0x82d6,0x0e,
+0x82d7,0x90,
+0x82d8,0x33,
+0x82d9,0x5f,
+0x82da,0xe0,
+0x82db,0x54,
+0x82dc,0x70,
+0x82dd,0x75,
+0x82de,0xf0,
+0x82df,0x10,
+0x82e0,0xa4,
+0x82e1,0xff,
+0x82e2,0x90,
+0x82e3,0x33,
+0x82e4,0x61,
+0x82e5,0xe0,
+0x82e6,0xfd,
+0x82e7,0xef,
+0x82e8,0x4d,
+0x82e9,0xff,
+0x82ea,0xe5,
+0x82eb,0xf0,
+0x82ec,0x54,
+0x82ed,0x07,
+0x82ee,0xf5,
+0x82ef,0x0f,
+0x82f0,0x8f,
+0x82f1,0x10,
+0x82f2,0xe5,
+0x82f3,0x0e,
+0x82f4,0xae,
+0x82f5,0x0d,
+0x82f6,0x78,
+0x82f7,0x05,
+0x82f8,0xce,
+0x82f9,0xc3,
+0x82fa,0x13,
+0x82fb,0xce,
+0x82fc,0x13,
+0x82fd,0xd8,
+0x82fe,0xf9,
+0x82ff,0xf5,
+0x8300,0x0e,
+0x8301,0x8e,
+0x8302,0x0d,
+0x8303,0xe5,
+0x8304,0x10,
+0x8305,0xae,
+0x8306,0x0f,
+0x8307,0x78,
+0x8308,0x03,
+0x8309,0xce,
+0x830a,0xc3,
+0x830b,0x13,
+0x830c,0xce,
+0x830d,0x13,
+0x830e,0xd8,
+0x830f,0xf9,
+0x8310,0xf5,
+0x8311,0x10,
+0x8312,0x8e,
+0x8313,0x0f,
+0x8314,0x85,
+0x8315,0x2a,
+0x8316,0x11,
+0x8317,0x85,
+0x8318,0x2b,
+0x8319,0x13,
+0x831a,0x85,
+0x831b,0x2c,
+0x831c,0x12,
+0x831d,0x85,
+0x831e,0x2d,
+0x831f,0x14,
+0x8320,0x12,
+0x8321,0x04,
+0x8322,0xab,
+0x8323,0xaf,
+0x8324,0x11,
+0x8325,0x12,
+0x8326,0x03,
+0x8327,0xdf,
+0x8328,0x8f,
+0x8329,0x11,
+0x832a,0x12,
+0x832b,0x04,
+0x832c,0xab,
+0x832d,0xaf,
+0x832e,0x12,
+0x832f,0x12,
+0x8330,0x03,
+0x8331,0xdf,
+0x8332,0x8f,
+0x8333,0x12,
+0x8334,0x12,
+0x8335,0x04,
+0x8336,0x56,
+0x8337,0xaf,
+0x8338,0x13,
+0x8339,0xfc,
+0x833a,0xfd,
+0x833b,0xfe,
+0x833c,0x12,
+0x833d,0x00,
+0x833e,0x16,
+0x833f,0x12,
+0x8340,0x03,
+0x8341,0xfe,
+0x8342,0x7b,
+0x8343,0x1e,
+0x8344,0x12,
+0x8345,0x03,
+0x8346,0xf7,
+0x8347,0x8f,
+0x8348,0x13,
+0x8349,0x12,
+0x834a,0x04,
+0x834b,0x56,
+0x834c,0xaf,
+0x834d,0x14,
+0x834e,0xfc,
+0x834f,0xfd,
+0x8350,0xfe,
+0x8351,0x12,
+0x8352,0x00,
+0x8353,0x16,
+0x8354,0x12,
+0x8355,0x03,
+0x8356,0xfe,
+0x8357,0xe4,
+0x8358,0x7b,
+0x8359,0x1e,
+0x835a,0x12,
+0x835b,0x03,
+0x835c,0xf8,
+0x835d,0x8f,
+0x835e,0x14,
+0x835f,0xc3,
+0x8360,0xe5,
+0x8361,0x12,
+0x8362,0x95,
+0x8363,0x11,
+0x8364,0xff,
+0x8365,0x0f,
+0x8366,0xef,
+0x8367,0xc3,
+0x8368,0x13,
+0x8369,0xff,
+0x836a,0xc3,
+0x836b,0x94,
+0x836c,0x02,
+0x836d,0x50,
+0x836e,0x27,
+0x836f,0xe5,
+0x8370,0x11,
+0x8371,0x9f,
+0x8372,0x40,
+0x8373,0x06,
+0x8374,0xe5,
+0x8375,0x11,
+0x8376,0x9f,
+0x8377,0xfe,
+0x8378,0x80,
+0x8379,0x02,
+0x837a,0x7e,
+0x837b,0x00,
+0x837c,0x8e,
+0x837d,0x11,
+0x837e,0xef,
+0x837f,0xfd,
+0x8380,0xe5,
+0x8381,0x12,
+0x8382,0x2d,
+0x8383,0xfd,
+0x8384,0xe4,
+0x8385,0x33,
+0x8386,0xfc,
+0x8387,0xc3,
+0x8388,0xed,
+0x8389,0x95,
+0x838a,0x0e,
+0x838b,0xec,
+0x838c,0x95,
+0x838d,0x0d,
+0x838e,0x50,
+0x838f,0x02,
+0x8390,0x80,
+0x8391,0x02,
+0x8392,0xad,
+0x8393,0x0e,
+0x8394,0x8d,
+0x8395,0x12,
+0x8396,0xc3,
+0x8397,0xe5,
+0x8398,0x14,
+0x8399,0x95,
+0x839a,0x13,
+0x839b,0xff,
+0x839c,0xc3,
+0x839d,0x94,
+0x839e,0x08,
+0x839f,0x50,
+0x83a0,0x29,
+0x83a1,0xe5,
+0x83a2,0x13,
+0x83a3,0x9f,
+0x83a4,0x40,
+0x83a5,0x06,
+0x83a6,0xe5,
+0x83a7,0x13,
+0x83a8,0x9f,
+0x83a9,0xfe,
+0x83aa,0x80,
+0x83ab,0x02,
+0x83ac,0x7e,
+0x83ad,0x00,
+0x83ae,0x8e,
+0x83af,0x13,
+0x83b0,0xef,
+0x83b1,0xfd,
+0x83b2,0xe5,
+0x83b3,0x14,
+0x83b4,0x2d,
+0x83b5,0xfd,
+0x83b6,0xe4,
+0x83b7,0x33,
+0x83b8,0xfc,
+0x83b9,0xc3,
+0x83ba,0xed,
+0x83bb,0x95,
+0x83bc,0x10,
+0x83bd,0xec,
+0x83be,0x95,
+0x83bf,0x0f,
+0x83c0,0x50,
+0x83c1,0x04,
+0x83c2,0xaf,
+0x83c3,0x05,
+0x83c4,0x80,
+0x83c5,0x02,
+0x83c6,0xaf,
+0x83c7,0x10,
+0x83c8,0x8f,
+0x83c9,0x14,
+0x83ca,0x90,
+0x83cb,0x39,
+0x83cc,0x0a,
+0x83cd,0xe5,
+0x83ce,0x11,
+0x83cf,0xf0,
+0x83d0,0xa3,
+0x83d1,0xe5,
+0x83d2,0x13,
+0x83d3,0xf0,
+0x83d4,0xa3,
+0x83d5,0xe5,
+0x83d6,0x12,
+0x83d7,0xf0,
+0x83d8,0xa3,
+0x83d9,0xe5,
+0x83da,0x14,
+0x83db,0xf0,
+0x83dc,0xc2,
+0x83dd,0x20,
+0x83de,0x22,
+0x83df,0xab,
+0x83e0,0x0c,
+0x83e1,0xaa,
+0x83e2,0x0b,
+0x83e3,0xa9,
+0x83e4,0x0a,
+0x83e5,0xa8,
+0x83e6,0x09,
+0x83e7,0xfc,
+0x83e8,0xfd,
+0x83e9,0xfe,
+0x83ea,0x12,
+0x83eb,0x00,
+0x83ec,0x16,
+0x83ed,0x8f,
+0x83ee,0x0c,
+0x83ef,0x8e,
+0x83f0,0x0b,
+0x83f1,0x8d,
+0x83f2,0x0a,
+0x83f3,0x8c,
+0x83f4,0x09,
+0x83f5,0x7b,
+0x83f6,0x28,
+0x83f7,0xe4,
+0x83f8,0xfa,
+0x83f9,0xf9,
+0x83fa,0xf8,
+0x83fb,0x12,
+0x83fc,0x00,
+0x83fd,0xa1,
+0x83fe,0x8f,
+0x83ff,0x0c,
+0x8400,0x8e,
+0x8401,0x0b,
+0x8402,0x8d,
+0x8403,0x0a,
+0x8404,0x8c,
+0x8405,0x09,
+0x8406,0x22,
+0x8407,0xd2,
+0x8408,0x19,
+0x8409,0x90,
+0x840a,0x30,
+0x840b,0xb4,
+0x840c,0xe5,
+0x840d,0x23,
+0x840e,0xf0,
+0x840f,0x22,
+0x8410,0x85,
+0x8411,0x2f,
+0x8412,0x82,
+0x8413,0x85,
+0x8414,0x2e,
+0x8415,0x83,
+0x8416,0xe5,
+0x8417,0x43,
+0x8418,0x75,
+0x8419,0xf0,
+0x841a,0x02,
+0x841b,0x12,
+0x841c,0x01,
+0x841d,0x46,
+0x841e,0xe4,
+0x841f,0x93,
+0x8420,0xfe,
+0x8421,0x74,
+0x8422,0x01,
+0x8423,0x93,
+0x8424,0xff,
+0x8425,0x85,
+0x8426,0x2f,
+0x8427,0x82,
+0x8428,0x85,
+0x8429,0x2e,
+0x842a,0x83,
+0x842b,0xe4,
+0x842c,0x93,
+0x842d,0xfc,
+0x842e,0x74,
+0x842f,0x01,
+0x8430,0x93,
+0x8431,0xfd,
+0x8432,0xc3,
+0x8433,0xef,
+0x8434,0x9d,
+0x8435,0xff,
+0x8436,0xee,
+0x8437,0x9c,
+0x8438,0x22,
+0x8439,0x12,
+0x843a,0x00,
+0x843b,0x16,
+0x843c,0x8f,
+0x843d,0x3c,
+0x843e,0x8e,
+0x843f,0x3b,
+0x8440,0x8d,
+0x8441,0x3a,
+0x8442,0x8c,
+0x8443,0x39,
+0x8444,0xaf,
+0x8445,0x3c,
+0x8446,0xae,
+0x8447,0x3b,
+0x8448,0xad,
+0x8449,0x3a,
+0x844a,0xac,
+0x844b,0x39,
+0x844c,0x22,
+0x844d,0xd2,
+0x844e,0x0f,
+0x844f,0x90,
+0x8450,0x30,
+0x8451,0xb1,
+0x8452,0xe5,
+0x8453,0x21,
+0x8454,0xf0,
+0x8455,0x22,
+0x8456,0xe4,
+0x8457,0x85,
+0x8458,0x10,
+0x8459,0x0c,
+0x845a,0x85,
+0x845b,0x0f,
+0x845c,0x0b,
+0x845d,0xf5,
+0x845e,0x0a,
+0x845f,0xf5,
+0x8460,0x09,
+0x8461,0xab,
+0x8462,0x0c,
+0x8463,0xaa,
+0x8464,0x0b,
+0x8465,0xa9,
+0x8466,0x0a,
+0x8467,0xa8,
+0x8468,0x09,
+0x8469,0x22,
+0x846a,0x85,
+0x846b,0x2f,
+0x846c,0x82,
+0x846d,0x85,
+0x846e,0x2e,
+0x846f,0x83,
+0x8470,0x22,
+0x8471,0xff,
+0x8472,0xe4,
+0x8473,0x94,
+0x8474,0x00,
+0x8475,0xfe,
+0x8476,0xe4,
+0x8477,0xfc,
+0x8478,0xfd,
+0x8479,0x02,
+0x847a,0x01,
+0x847b,0x33,
+0x847c,0xc2,
+0x847d,0x07,
+0x847e,0xc2,
+0x847f,0x06,
+0x8480,0xc2,
+0x8481,0x02,
+0x8482,0xc2,
+0x8483,0x01,
+0x8484,0xc2,
+0x8485,0x00,
+0x8486,0xc2,
+0x8487,0x03,
+0x8488,0xd2,
+0x8489,0x04,
+0x848a,0x22,
+0x848b,0x90,
+0x848c,0x33,
+0x848d,0xb3,
+0x848e,0xe4,
+0x848f,0xf0,
+0x8490,0xa3,
+0x8491,0xf0,
+0x8492,0xa3,
+0x8493,0xf0,
+0x8494,0xa3,
+0x8495,0xf0,
+0x8496,0x22,
+0x8497,0xa3,
+0x8498,0xe0,
+0x8499,0xf5,
+0x849a,0x22,
+0x849b,0x75,
+0x849c,0x32,
+0x849d,0x0a,
+0x849e,0x22,
+0x849f,0x12,
+0x84a0,0x00,
+0x84a1,0xa1,
+0x84a2,0x8f,
+0x84a3,0x3c,
+0x84a4,0x8e,
+0x84a5,0x3b,
+0x84a6,0x8d,
+0x84a7,0x3a,
+0x84a8,0x8c,
+0x84a9,0x39,
+0x84aa,0x22,
+0x84ab,0xe4,
+0x84ac,0x85,
+0x84ad,0x0e,
+0x84ae,0x0c,
+0x84af,0x85,
+0x84b0,0x0d,
+0x84b1,0x0b,
+0x84b2,0xf5,
+0x84b3,0x0a,
+0x84b4,0xf5,
+0x84b5,0x09,
+0x84b6,0x22,
+0x84b7,0xe4,
+0x84b8,0xfc,
+0x84b9,0xfd,
+0x84ba,0xfe,
+0x84bb,0x02,
+0x84bc,0x01,
+0x84bd,0x33,
+0x84be,0x90,
+0x84bf,0x0b,
+0x84c0,0x4c,
+0x84c1,0xe4,
+0x84c2,0x93,
+0x84c3,0xff,
+0x84c4,0x90,
+0x84c5,0x30,
+0x84c6,0x0a,
+0x84c7,0xe0,
+0x84c8,0x22,
+0x84c9,0xc2,
+0x84ca,0x02,
+0x84cb,0xc2,
+0x84cc,0x01,
+0x84cd,0xd2,
+0x84ce,0x00,
+0x84cf,0xc2,
+0x84d0,0x03,
+0x84d1,0xc2,
+0x84d2,0x04,
+0x84d3,0x22,
+0x84d4,0x75,
+0x84d5,0xf0,
+0x84d6,0x02,
+0x84d7,0x02,
+0x84d8,0x01,
+0x84d9,0x46,
+0x84da,0xe4,
+0x84db,0x93,
+0x84dc,0xf5,
+0x84dd,0x43,
+0x84de,0xa3,
+0x84df,0xe4,
+0x84e0,0x93,
+0x84e1,0xf5,
+0x84e2,0x28,
+0x84e3,0x22,
+0x84e4,0xd2,
+0x84e5,0x02,
+0x84e6,0xd2,
+0x84e7,0x01,
+0x84e8,0xc2,
+0x84e9,0x00,
+0x84ea,0x22,
+0x84eb,0xd3,
+0x84ec,0xe5,
+0x84ed,0x3c,
+0x84ee,0x94,
+0x84ef,0xff,
+0x84f0,0xe5,
+0x84f1,0x3b,
+0x84f2,0x94,
+0x84f3,0x03,
+0x84f4,0x22,
+0x84f5,0x30,
+0x84f6,0x04,
+0x84f7,0x03,
+0x84f8,0x02,
+0x84f9,0x05,
+0x84fa,0xd5,
+0x84fb,0xd2,
+0x84fc,0x04,
+0x84fd,0xe5,
+0x84fe,0x45,
+0x84ff,0xb4,
+0x8500,0x01,
+0x8501,0x06,
+0x8502,0x12,
+0x8503,0x0c,
+0x8504,0xfe,
+0x8505,0x02,
+0x8506,0x05,
+0x8507,0xce,
+0x8508,0xe5,
+0x8509,0x45,
+0x850a,0xb4,
+0x850b,0x02,
+0x850c,0x06,
+0x850d,0x12,
+0x850e,0x0d,
+0x850f,0x0f,
+0x8510,0x02,
+0x8511,0x05,
+0x8512,0xce,
+0x8513,0xe5,
+0x8514,0x45,
+0x8515,0xb4,
+0x8516,0x03,
+0x8517,0x05,
+0x8518,0xe4,
+0x8519,0xf5,
+0x851a,0x09,
+0x851b,0x80,
+0x851c,0x08,
+0x851d,0xe5,
+0x851e,0x45,
+0x851f,0xb4,
+0x8520,0x04,
+0x8521,0x09,
+0x8522,0x85,
+0x8523,0x43,
+0x8524,0x09,
+0x8525,0x12,
+0x8526,0x09,
+0x8527,0xee,
+0x8528,0x02,
+0x8529,0x05,
+0x852a,0xce,
+0x852b,0xe5,
+0x852c,0x45,
+0x852d,0x64,
+0x852e,0x0f,
+0x852f,0x70,
+0x8530,0x15,
+0x8531,0x12,
+0x8532,0x04,
+0x8533,0xeb,
+0x8534,0x40,
+0x8535,0x06,
+0x8536,0x7e,
+0x8537,0x03,
+0x8538,0x7f,
+0x8539,0xff,
+0x853a,0x80,
+0x853b,0x04,
+0x853c,0xae,
+0x853d,0x3b,
+0x853e,0xaf,
+0x853f,0x3c,
+0x8540,0x12,
+0x8541,0x05,
+0x8542,0xd6,
+0x8543,0x02,
+0x8544,0x05,
+0x8545,0xce,
+0x8546,0xe5,
+0x8547,0x45,
+0x8548,0x64,
+0x8549,0x10,
+0x854a,0x60,
+0x854b,0x03,
+0x854c,0x02,
+0x854d,0x05,
+0x854e,0xce,
+0x854f,0xf5,
+0x8550,0x39,
+0x8551,0xf5,
+0x8552,0x3a,
+0x8553,0xf5,
+0x8554,0x3b,
+0x8555,0xab,
+0x8556,0x3c,
+0x8557,0xaa,
+0x8558,0x3b,
+0x8559,0xa9,
+0x855a,0x3a,
+0x855b,0xa8,
+0x855c,0x39,
+0x855d,0x12,
+0x855e,0x04,
+0x855f,0x10,
+0x8560,0xfe,
+0x8561,0xe4,
+0x8562,0xfc,
+0x8563,0xfd,
+0x8564,0x12,
+0x8565,0x04,
+0x8566,0x39,
+0x8567,0xe4,
+0x8568,0x7b,
+0x8569,0xff,
+0x856a,0xfa,
+0x856b,0xf9,
+0x856c,0xf8,
+0x856d,0x12,
+0x856e,0x04,
+0x856f,0x9f,
+0x8570,0x12,
+0x8571,0x04,
+0x8572,0x6a,
+0x8573,0xe4,
+0x8574,0x93,
+0x8575,0xfe,
+0x8576,0x74,
+0x8577,0x01,
+0x8578,0x93,
+0x8579,0xff,
+0x857a,0xe4,
+0x857b,0xfc,
+0x857c,0xfd,
+0x857d,0xe5,
+0x857e,0x3c,
+0x857f,0x2f,
+0x8580,0xf5,
+0x8581,0x3c,
+0x8582,0xe5,
+0x8583,0x3b,
+0x8584,0x3e,
+0x8585,0xf5,
+0x8586,0x3b,
+0x8587,0xed,
+0x8588,0x35,
+0x8589,0x3a,
+0x858a,0xf5,
+0x858b,0x3a,
+0x858c,0xec,
+0x858d,0x35,
+0x858e,0x39,
+0x858f,0xf5,
+0x8590,0x39,
+0x8591,0x12,
+0x8592,0x04,
+0x8593,0xeb,
+0x8594,0x40,
+0x8595,0x06,
+0x8596,0x7e,
+0x8597,0x03,
+0x8598,0x7f,
+0x8599,0xff,
+0x859a,0x80,
+0x859b,0x04,
+0x859c,0xae,
+0x859d,0x3b,
+0x859e,0xaf,
+0x859f,0x3c,
+0x85a0,0x12,
+0x85a1,0x05,
+0x85a2,0xd6,
+0x85a3,0xe4,
+0x85a4,0xf5,
+0x85a5,0x3a,
+0x85a6,0xf5,
+0x85a7,0x3a,
+0x85a8,0xe5,
+0x85a9,0x3a,
+0x85aa,0xd3,
+0x85ab,0x95,
+0x85ac,0x43,
+0x85ad,0x50,
+0x85ae,0x1c,
+0x85af,0x12,
+0x85b0,0x04,
+0x85b1,0x6a,
+0x85b2,0xaf,
+0x85b3,0x3a,
+0x85b4,0x75,
+0x85b5,0xf0,
+0x85b6,0x02,
+0x85b7,0xef,
+0x85b8,0x12,
+0x85b9,0x01,
+0x85ba,0x46,
+0x85bb,0xc3,
+0x85bc,0x74,
+0x85bd,0x01,
+0x85be,0x93,
+0x85bf,0x95,
+0x85c0,0x3c,
+0x85c1,0xe4,
+0x85c2,0x93,
+0x85c3,0x95,
+0x85c4,0x3b,
+0x85c5,0x50,
+0x85c6,0x04,
+0x85c7,0x05,
+0x85c8,0x3a,
+0x85c9,0x80,
+0x85ca,0xdd,
+0x85cb,0x85,
+0x85cc,0x3a,
+0x85cd,0x44,
+0x85ce,0x90,
+0x85cf,0x3f,
+0x85d0,0x01,
+0x85d1,0xe4,
+0x85d2,0xf0,
+0x85d3,0xd2,
+0x85d4,0x25,
+0x85d5,0x22,
+0x85d6,0x8e,
+0x85d7,0x3b,
+0x85d8,0x8f,
+0x85d9,0x3c,
+0x85da,0x85,
+0x85db,0x3b,
+0x85dc,0x37,
+0x85dd,0x85,
+0x85de,0x3c,
+0x85df,0x38,
+0x85e0,0xe5,
+0x85e1,0x3c,
+0x85e2,0xc4,
+0x85e3,0xf8,
+0x85e4,0x54,
+0x85e5,0x0f,
+0x85e6,0xc8,
+0x85e7,0x68,
+0x85e8,0xf5,
+0x85e9,0x3c,
+0x85ea,0xe5,
+0x85eb,0x3b,
+0x85ec,0xc4,
+0x85ed,0x54,
+0x85ee,0xf0,
+0x85ef,0x48,
+0x85f0,0xf5,
+0x85f1,0x3b,
+0x85f2,0x85,
+0x85f3,0x3b,
+0x85f4,0x0a,
+0x85f5,0x85,
+0x85f6,0x3c,
+0x85f7,0x0b,
+0x85f8,0x12,
+0x85f9,0x01,
+0x85fa,0x78,
+0x85fb,0x22,
+0x85fc,0xe5,
+0x85fd,0x29,
+0x85fe,0x70,
+0x85ff,0x03,
+0x8600,0x02,
+0x8601,0x06,
+0x8602,0xd4,
+0x8603,0xc2,
+0x8604,0xaf,
+0x8605,0xaf,
+0x8606,0x29,
+0x8607,0xe4,
+0x8608,0xf5,
+0x8609,0x29,
+0x860a,0xd2,
+0x860b,0xaf,
+0x860c,0x90,
+0x860d,0x3f,
+0x860e,0x01,
+0x860f,0xe0,
+0x8610,0xf5,
+0x8611,0x45,
+0x8612,0xa3,
+0x8613,0xe0,
+0x8614,0xf5,
+0x8615,0x39,
+0x8616,0xa3,
+0x8617,0xe0,
+0x8618,0xf5,
+0x8619,0x3a,
+0x861a,0xa3,
+0x861b,0xe0,
+0x861c,0xf5,
+0x861d,0x3b,
+0x861e,0xa3,
+0x861f,0xe0,
+0x8620,0xf5,
+0x8621,0x3c,
+0x8622,0xef,
+0x8623,0x12,
+0x8624,0x01,
+0x8625,0x52,
+0x8626,0x06,
+0x8627,0x4b,
+0x8628,0x03,
+0x8629,0x06,
+0x862a,0x5a,
+0x862b,0x05,
+0x862c,0x06,
+0x862d,0x81,
+0x862e,0x06,
+0x862f,0x06,
+0x8630,0x6f,
+0x8631,0x08,
+0x8632,0x06,
+0x8633,0xa2,
+0x8634,0x09,
+0x8635,0x06,
+0x8636,0x8e,
+0x8637,0x10,
+0x8638,0x06,
+0x8639,0xa2,
+0x863a,0x12,
+0x863b,0x06,
+0x863c,0xa7,
+0x863d,0x20,
+0x863e,0x06,
+0x863f,0xb5,
+0x8640,0x21,
+0x8641,0x06,
+0x8642,0xba,
+0x8643,0x30,
+0x8644,0x06,
+0x8645,0xc5,
+0x8646,0xd0,
+0x8647,0x00,
+0x8648,0x00,
+0x8649,0x06,
+0x864a,0xc9,
+0x864b,0x30,
+0x864c,0x05,
+0x864d,0x7b,
+0x864e,0x20,
+0x864f,0x00,
+0x8650,0x78,
+0x8651,0xd2,
+0x8652,0x07,
+0x8653,0xc2,
+0x8654,0x06,
+0x8655,0x12,
+0x8656,0x04,
+0x8657,0xc9,
+0x8658,0x80,
+0x8659,0x21,
+0x865a,0x30,
+0x865b,0x05,
+0x865c,0x6c,
+0x865d,0x20,
+0x865e,0x00,
+0x865f,0x69,
+0x8660,0xc2,
+0x8661,0x07,
+0x8662,0xd2,
+0x8663,0x06,
+0x8664,0xc2,
+0x8665,0x03,
+0x8666,0x12,
+0x8667,0x04,
+0x8668,0xe4,
+0x8669,0xc2,
+0x866a,0x04,
+0x866b,0xc2,
+0x866c,0x21,
+0x866d,0x80,
+0x866e,0x5a,
+0x866f,0x12,
+0x8670,0x04,
+0x8671,0x7c,
+0x8672,0x30,
+0x8673,0x05,
+0x8674,0x06,
+0x8675,0xe4,
+0x8676,0xf5,
+0x8677,0x09,
+0x8678,0x12,
+0x8679,0x09,
+0x867a,0xee,
+0x867b,0xc2,
+0x867c,0x21,
+0x867d,0xd2,
+0x867e,0x25,
+0x867f,0x80,
+0x8680,0x48,
+0x8681,0x30,
+0x8682,0x07,
+0x8683,0x45,
+0x8684,0x30,
+0x8685,0x06,
+0x8686,0x42,
+0x8687,0x12,
+0x8688,0x04,
+0x8689,0xc9,
+0x868a,0xd2,
+0x868b,0x21,
+0x868c,0x80,
+0x868d,0x3b,
+0x868e,0x20,
+0x868f,0x07,
+0x8690,0x03,
+0x8691,0x30,
+0x8692,0x06,
+0x8693,0x09,
+0x8694,0xe5,
+0x8695,0x45,
+0x8696,0x64,
+0x8697,0x0e,
+0x8698,0x70,
+0x8699,0x2f,
+0x869a,0x20,
+0x869b,0x00,
+0x869c,0x2c,
+0x869d,0x12,
+0x869e,0x06,
+0x869f,0xd5,
+0x86a0,0x80,
+0x86a1,0x27,
+0x86a2,0x12,
+0x86a3,0x02,
+0x86a4,0xc4,
+0x86a5,0x80,
+0x86a6,0x22,
+0x86a7,0x30,
+0x86a8,0x05,
+0x86a9,0x1f,
+0x86aa,0x20,
+0x86ab,0x07,
+0x86ac,0x1c,
+0x86ad,0x20,
+0x86ae,0x06,
+0x86af,0x19,
+0x86b0,0x12,
+0x86b1,0x0c,
+0x86b2,0xa6,
+0x86b3,0x80,
+0x86b4,0x14,
+0x86b5,0x12,
+0x86b6,0x09,
+0x86b7,0x85,
+0x86b8,0x80,
+0x86b9,0x0f,
+0x86ba,0x20,
+0x86bb,0x07,
+0x86bc,0x0c,
+0x86bd,0x20,
+0x86be,0x06,
+0x86bf,0x09,
+0x86c0,0x12,
+0x86c1,0x0d,
+0x86c2,0x3c,
+0x86c3,0x80,
+0x86c4,0x04,
+0x86c5,0xd2,
+0x86c6,0x26,
+0x86c7,0xc2,
+0x86c8,0x26,
+0x86c9,0x20,
+0x86ca,0x07,
+0x86cb,0x03,
+0x86cc,0x20,
+0x86cd,0x06,
+0x86ce,0x05,
+0x86cf,0x90,
+0x86d0,0x3f,
+0x86d1,0x01,
+0x86d2,0xe4,
+0x86d3,0xf0,
+0x86d4,0x22,
+0x86d5,0xe5,
+0x86d6,0x45,
+0x86d7,0x24,
+0x86d8,0xfe,
+0x86d9,0x60,
+0x86da,0x19,
+0x86db,0x14,
+0x86dc,0x60,
+0x86dd,0x2c,
+0x86de,0x24,
+0x86df,0x02,
+0x86e0,0x60,
+0x86e1,0x03,
+0x86e2,0x02,
+0x86e3,0x07,
+0x86e4,0xa8,
+0x86e5,0xe5,
+0x86e6,0x3c,
+0x86e7,0xd3,
+0x86e8,0x94,
+0x86e9,0x03,
+0x86ea,0x40,
+0x86eb,0x03,
+0x86ec,0x75,
+0x86ed,0x3c,
+0x86ee,0x03,
+0x86ef,0xe4,
+0x86f0,0xf5,
+0x86f1,0x09,
+0x86f2,0x80,
+0x86f3,0x0d,
+0x86f4,0xe5,
+0x86f5,0x3c,
+0x86f6,0xd3,
+0x86f7,0x94,
+0x86f8,0x05,
+0x86f9,0x40,
+0x86fa,0x03,
+0x86fb,0x75,
+0x86fc,0x3c,
+0x86fd,0x05,
+0x86fe,0x75,
+0x86ff,0x09,
+0x8700,0x01,
+0x8701,0x85,
+0x8702,0x3c,
+0x8703,0x0a,
+0x8704,0x12,
+0x8705,0x08,
+0x8706,0xfa,
+0x8707,0xd2,
+0x8708,0x20,
+0x8709,0x22,
+0x870a,0xe5,
+0x870b,0x39,
+0x870c,0xd3,
+0x870d,0x94,
+0x870e,0x28,
+0x870f,0x40,
+0x8710,0x04,
+0x8711,0x7f,
+0x8712,0x28,
+0x8713,0x80,
+0x8714,0x02,
+0x8715,0xaf,
+0x8716,0x39,
+0x8717,0x8f,
+0x8718,0x39,
+0x8719,0xe5,
+0x871a,0x3a,
+0x871b,0xd3,
+0x871c,0x94,
+0x871d,0x1e,
+0x871e,0x40,
+0x871f,0x04,
+0x8720,0x7f,
+0x8721,0x1e,
+0x8722,0x80,
+0x8723,0x02,
+0x8724,0xaf,
+0x8725,0x3a,
+0x8726,0x8f,
+0x8727,0x3a,
+0x8728,0xe5,
+0x8729,0x3b,
+0x872a,0xd3,
+0x872b,0x94,
+0x872c,0x28,
+0x872d,0x40,
+0x872e,0x04,
+0x872f,0x7f,
+0x8730,0x28,
+0x8731,0x80,
+0x8732,0x02,
+0x8733,0xaf,
+0x8734,0x3b,
+0x8735,0x8f,
+0x8736,0x3b,
+0x8737,0xe5,
+0x8738,0x3c,
+0x8739,0xd3,
+0x873a,0x94,
+0x873b,0x1e,
+0x873c,0x40,
+0x873d,0x04,
+0x873e,0x7f,
+0x873f,0x1e,
+0x8740,0x80,
+0x8741,0x02,
+0x8742,0xaf,
+0x8743,0x3c,
+0x8744,0x8f,
+0x8745,0x3c,
+0x8746,0xaf,
+0x8747,0x3a,
+0x8748,0x78,
+0x8749,0x10,
+0x874a,0x12,
+0x874b,0x04,
+0x874c,0xb7,
+0x874d,0xc0,
+0x874e,0x04,
+0x874f,0xc0,
+0x8750,0x05,
+0x8751,0xc0,
+0x8752,0x06,
+0x8753,0xc0,
+0x8754,0x07,
+0x8755,0xaf,
+0x8756,0x39,
+0x8757,0x78,
+0x8758,0x18,
+0x8759,0x12,
+0x875a,0x04,
+0x875b,0xb7,
+0x875c,0xd0,
+0x875d,0x03,
+0x875e,0xd0,
+0x875f,0x02,
+0x8760,0xd0,
+0x8761,0x01,
+0x8762,0xd0,
+0x8763,0x00,
+0x8764,0xef,
+0x8765,0x4b,
+0x8766,0xff,
+0x8767,0xee,
+0x8768,0x4a,
+0x8769,0xfe,
+0x876a,0xed,
+0x876b,0x49,
+0x876c,0xfd,
+0x876d,0xec,
+0x876e,0x48,
+0x876f,0xfc,
+0x8770,0xc0,
+0x8771,0x04,
+0x8772,0xc0,
+0x8773,0x05,
+0x8774,0xc0,
+0x8775,0x06,
+0x8776,0xc0,
+0x8777,0x07,
+0x8778,0xaf,
+0x8779,0x3b,
+0x877a,0xe4,
+0x877b,0xfc,
+0x877c,0xfd,
+0x877d,0xfe,
+0x877e,0x78,
+0x877f,0x08,
+0x8780,0x12,
+0x8781,0x01,
+0x8782,0x33,
+0x8783,0xd0,
+0x8784,0x03,
+0x8785,0xd0,
+0x8786,0x02,
+0x8787,0xd0,
+0x8788,0x01,
+0x8789,0xd0,
+0x878a,0x00,
+0x878b,0xef,
+0x878c,0x4b,
+0x878d,0xfb,
+0x878e,0xee,
+0x878f,0x4a,
+0x8790,0xfa,
+0x8791,0xed,
+0x8792,0x49,
+0x8793,0xf9,
+0x8794,0xec,
+0x8795,0x48,
+0x8796,0xf8,
+0x8797,0xaf,
+0x8798,0x3c,
+0x8799,0xeb,
+0x879a,0x4f,
+0x879b,0xf5,
+0x879c,0x2d,
+0x879d,0xea,
+0x879e,0xf5,
+0x879f,0x2c,
+0x87a0,0xe9,
+0x87a1,0xf5,
+0x87a2,0x2b,
+0x87a3,0xe8,
+0x87a4,0xf5,
+0x87a5,0x2a,
+0x87a6,0xd2,
+0x87a7,0x20,
+0x87a8,0x22,
+0x87a9,0xc0,
+0x87aa,0xe0,
+0x87ab,0xc0,
+0x87ac,0x83,
+0x87ad,0xc0,
+0x87ae,0x82,
+0x87af,0xc0,
+0x87b0,0xd0,
+0x87b1,0x75,
+0x87b2,0xd0,
+0x87b3,0x00,
+0x87b4,0xc0,
+0x87b5,0x00,
+0x87b6,0xc0,
+0x87b7,0x06,
+0x87b8,0xc0,
+0x87b9,0x07,
+0x87ba,0x90,
+0x87bb,0x37,
+0x87bc,0x08,
+0x87bd,0xe0,
+0x87be,0xf5,
+0x87bf,0x08,
+0x87c0,0xe5,
+0x87c1,0x08,
+0x87c2,0x30,
+0x87c3,0xe3,
+0x87c4,0x23,
+0x87c5,0x20,
+0x87c6,0x26,
+0x87c7,0x20,
+0x87c8,0x90,
+0x87c9,0x3a,
+0x87ca,0x00,
+0x87cb,0x74,
+0x87cc,0x81,
+0x87cd,0xf0,
+0x87ce,0x90,
+0x87cf,0x3a,
+0x87d0,0x03,
+0x87d1,0xe0,
+0x87d2,0xf5,
+0x87d3,0x3e,
+0x87d4,0xe0,
+0x87d5,0xf5,
+0x87d6,0x3d,
+0x87d7,0x90,
+0x87d8,0x3a,
+0x87d9,0x00,
+0x87da,0x74,
+0x87db,0x85,
+0x87dc,0xf0,
+0x87dd,0x90,
+0x87de,0x3a,
+0x87df,0x03,
+0x87e0,0xe0,
+0x87e1,0xf5,
+0x87e2,0x40,
+0x87e3,0xe0,
+0x87e4,0xf5,
+0x87e5,0x3f,
+0x87e6,0xd2,
+0x87e7,0x2f,
+0x87e8,0xe5,
+0x87e9,0x08,
+0x87ea,0x30,
+0x87eb,0xe5,
+0x87ec,0x56,
+0x87ed,0x90,
+0x87ee,0x30,
+0x87ef,0x1b,
+0x87f0,0xe0,
+0x87f1,0xf5,
+0x87f2,0x42,
+0x87f3,0xe5,
+0x87f4,0x41,
+0x87f5,0x24,
+0x87f6,0x02,
+0x87f7,0xff,
+0x87f8,0xe4,
+0x87f9,0x33,
+0x87fa,0xfe,
+0x87fb,0xc3,
+0x87fc,0xef,
+0x87fd,0x95,
+0x87fe,0x42,
+0x87ff,0x74,
+0x8800,0x80,
+0x8801,0xf8,
+0x8802,0x6e,
+0x8803,0x98,
+0x8804,0x50,
+0x8805,0x02,
+0x8806,0x80,
+0x8807,0x01,
+0x8808,0xc3,
+0x8809,0x92,
+0x880a,0x24,
+0x880b,0xe5,
+0x880c,0x42,
+0x880d,0x24,
+0x880e,0x02,
+0x880f,0xff,
+0x8810,0xe4,
+0x8811,0x33,
+0x8812,0xfe,
+0x8813,0xc3,
+0x8814,0xef,
+0x8815,0x95,
+0x8816,0x41,
+0x8817,0x74,
+0x8818,0x80,
+0x8819,0xf8,
+0x881a,0x6e,
+0x881b,0x98,
+0x881c,0x50,
+0x881d,0x02,
+0x881e,0x80,
+0x881f,0x02,
+0x8820,0xa2,
+0x8821,0x24,
+0x8822,0x92,
+0x8823,0x24,
+0x8824,0x30,
+0x8825,0x24,
+0x8826,0x04,
+0x8827,0xaf,
+0x8828,0x42,
+0x8829,0x80,
+0x882a,0x02,
+0x882b,0xaf,
+0x882c,0x41,
+0x882d,0x8f,
+0x882e,0x41,
+0x882f,0x30,
+0x8830,0x27,
+0x8831,0x11,
+0x8832,0x90,
+0x8833,0x33,
+0x8834,0x00,
+0x8835,0xe0,
+0x8836,0x30,
+0x8837,0x29,
+0x8838,0x05,
+0x8839,0x44,
+0x883a,0x40,
+0x883b,0xf0,
+0x883c,0x80,
+0x883d,0x03,
+0x883e,0x54,
+0x883f,0xbf,
+0x8840,0xf0,
+0x8841,0xc2,
+0x8842,0x27,
+0x8843,0xe5,
+0x8844,0x08,
+0x8845,0x30,
+0x8846,0xe1,
+0x8847,0x08,
+0x8848,0x90,
+0x8849,0x3f,
+0x884a,0x00,
+0x884b,0xe0,
+0x884c,0xf5,
+0x884d,0x29,
+0x884e,0xe4,
+0x884f,0xf0,
+0x8850,0x90,
+0x8851,0x37,
+0x8852,0x08,
+0x8853,0xe5,
+0x8854,0x08,
+0x8855,0xf0,
+0x8856,0xd0,
+0x8857,0x07,
+0x8858,0xd0,
+0x8859,0x06,
+0x885a,0xd0,
+0x885b,0x00,
+0x885c,0xd0,
+0x885d,0xd0,
+0x885e,0xd0,
+0x885f,0x82,
+0x8860,0xd0,
+0x8861,0x83,
+0x8862,0xd0,
+0x8863,0xe0,
+0x8864,0x32,
+0x8865,0x12,
+0x8866,0x04,
+0x8867,0xbe,
+0x8868,0xb5,
+0x8869,0x07,
+0x886a,0x03,
+0x886b,0xd3,
+0x886c,0x80,
+0x886d,0x01,
+0x886e,0xc3,
+0x886f,0x40,
+0x8870,0x03,
+0x8871,0x02,
+0x8872,0x08,
+0x8873,0xf9,
+0x8874,0x90,
+0x8875,0x31,
+0x8876,0x00,
+0x8877,0xe0,
+0x8878,0x54,
+0x8879,0xfe,
+0x887a,0xf0,
+0x887b,0xe0,
+0x887c,0x54,
+0x887d,0xfd,
+0x887e,0xf0,
+0x887f,0xa3,
+0x8880,0xe4,
+0x8881,0xf0,
+0x8882,0x90,
+0x8883,0x33,
+0x8884,0x00,
+0x8885,0xe0,
+0x8886,0x54,
+0x8887,0xbf,
+0x8888,0xf0,
+0x8889,0x12,
+0x888a,0x04,
+0x888b,0x8b,
+0x888c,0x90,
+0x888d,0x33,
+0x888e,0xb0,
+0x888f,0xf0,
+0x8890,0xa3,
+0x8891,0xf0,
+0x8892,0xa3,
+0x8893,0xf0,
+0x8894,0x90,
+0x8895,0x30,
+0x8896,0xb2,
+0x8897,0xe0,
+0x8898,0x44,
+0x8899,0x08,
+0x889a,0xf0,
+0x889b,0x90,
+0x889c,0x30,
+0x889d,0xb0,
+0x889e,0xe0,
+0x889f,0x44,
+0x88a0,0x01,
+0x88a1,0xf0,
+0x88a2,0xa3,
+0x88a3,0xe0,
+0x88a4,0x44,
+0x88a5,0x0c,
+0x88a6,0xf0,
+0x88a7,0x90,
+0x88a8,0x30,
+0x88a9,0xb4,
+0x88aa,0xe0,
+0x88ab,0x44,
+0x88ac,0x07,
+0x88ad,0xf0,
+0x88ae,0xe0,
+0x88af,0xf5,
+0x88b0,0x23,
+0x88b1,0x90,
+0x88b2,0x30,
+0x88b3,0xb1,
+0x88b4,0xe0,
+0x88b5,0xf5,
+0x88b6,0x21,
+0x88b7,0x90,
+0x88b8,0x39,
+0x88b9,0x01,
+0x88ba,0x74,
+0x88bb,0x35,
+0x88bc,0xf0,
+0x88bd,0x90,
+0x88be,0x39,
+0x88bf,0x00,
+0x88c0,0x74,
+0x88c1,0x20,
+0x88c2,0xf0,
+0x88c3,0x90,
+0x88c4,0x37,
+0x88c5,0x00,
+0x88c6,0x74,
+0x88c7,0xff,
+0x88c8,0xf0,
+0x88c9,0xa3,
+0x88ca,0xf0,
+0x88cb,0x90,
+0x88cc,0x37,
+0x88cd,0x00,
+0x88ce,0xe0,
+0x88cf,0x54,
+0x88d0,0xf7,
+0x88d1,0xf0,
+0x88d2,0xe0,
+0x88d3,0x54,
+0x88d4,0xdf,
+0x88d5,0xf0,
+0x88d6,0x90,
+0x88d7,0x31,
+0x88d8,0x0f,
+0x88d9,0x74,
+0x88da,0x3f,
+0x88db,0xf0,
+0x88dc,0xa3,
+0x88dd,0xe4,
+0x88de,0xf0,
+0x88df,0xa3,
+0x88e0,0x74,
+0x88e1,0x3f,
+0x88e2,0xf0,
+0x88e3,0xa3,
+0x88e4,0x74,
+0x88e5,0x01,
+0x88e6,0xf0,
+0x88e7,0x90,
+0x88e8,0x37,
+0x88e9,0x00,
+0x88ea,0xe0,
+0x88eb,0x54,
+0x88ec,0xfd,
+0x88ed,0xf0,
+0x88ee,0x90,
+0x88ef,0x37,
+0x88f0,0x08,
+0x88f1,0x74,
+0x88f2,0xff,
+0x88f3,0xf0,
+0x88f4,0xa3,
+0x88f5,0xf0,
+0x88f6,0x75,
+0x88f7,0xa8,
+0x88f8,0x01,
+0x88f9,0x22,
+0x88fa,0xe5,
+0x88fb,0x0a,
+0x88fc,0x25,
+0x88fd,0xe0,
+0x88fe,0x25,
+0x88ff,0xe0,
+0x8900,0xf5,
+0x8901,0x0b,
+0x8902,0xe5,
+0x8903,0x09,
+0x8904,0x60,
+0x8905,0x09,
+0x8906,0xe5,
+0x8907,0x0a,
+0x8908,0x75,
+0x8909,0xf0,
+0x890a,0x03,
+0x890b,0xa4,
+0x890c,0xff,
+0x890d,0x80,
+0x890e,0x02,
+0x890f,0xaf,
+0x8910,0x0b,
+0x8911,0x8f,
+0x8912,0x0c,
+0x8913,0xc3,
+0x8914,0x74,
+0x8915,0x0f,
+0x8916,0x9f,
+0x8917,0x78,
+0x8918,0x10,
+0x8919,0x12,
+0x891a,0x04,
+0x891b,0x71,
+0x891c,0xc0,
+0x891d,0x04,
+0x891e,0xc0,
+0x891f,0x05,
+0x8920,0xc0,
+0x8921,0x06,
+0x8922,0xc0,
+0x8923,0x07,
+0x8924,0xc3,
+0x8925,0x74,
+0x8926,0x14,
+0x8927,0x95,
+0x8928,0x0b,
+0x8929,0x78,
+0x892a,0x18,
+0x892b,0x12,
+0x892c,0x04,
+0x892d,0x71,
+0x892e,0xd0,
+0x892f,0x03,
+0x8930,0xd0,
+0x8931,0x02,
+0x8932,0xd0,
+0x8933,0x01,
+0x8934,0xd0,
+0x8935,0x00,
+0x8936,0xef,
+0x8937,0x4b,
+0x8938,0xff,
+0x8939,0xee,
+0x893a,0x4a,
+0x893b,0xfe,
+0x893c,0xed,
+0x893d,0x49,
+0x893e,0xfd,
+0x893f,0xec,
+0x8940,0x48,
+0x8941,0xfc,
+0x8942,0xc0,
+0x8943,0x04,
+0x8944,0xc0,
+0x8945,0x05,
+0x8946,0xc0,
+0x8947,0x06,
+0x8948,0xc0,
+0x8949,0x07,
+0x894a,0xe5,
+0x894b,0x0b,
+0x894c,0x24,
+0x894d,0x14,
+0x894e,0xff,
+0x894f,0xe4,
+0x8950,0x33,
+0x8951,0xfe,
+0x8952,0xe4,
+0x8953,0xfc,
+0x8954,0xfd,
+0x8955,0x78,
+0x8956,0x08,
+0x8957,0x12,
+0x8958,0x01,
+0x8959,0x33,
+0x895a,0xd0,
+0x895b,0x03,
+0x895c,0xd0,
+0x895d,0x02,
+0x895e,0xd0,
+0x895f,0x01,
+0x8960,0xd0,
+0x8961,0x00,
+0x8962,0xef,
+0x8963,0x4b,
+0x8964,0xfb,
+0x8965,0xee,
+0x8966,0x4a,
+0x8967,0xfa,
+0x8968,0xed,
+0x8969,0x49,
+0x896a,0xf9,
+0x896b,0xec,
+0x896c,0x48,
+0x896d,0xf8,
+0x896e,0xe5,
+0x896f,0x0c,
+0x8970,0x24,
+0x8971,0x0f,
+0x8972,0xff,
+0x8973,0xe4,
+0x8974,0x33,
+0x8975,0xfe,
+0x8976,0xeb,
+0x8977,0x4f,
+0x8978,0xf5,
+0x8979,0x2d,
+0x897a,0xea,
+0x897b,0x4e,
+0x897c,0xf5,
+0x897d,0x2c,
+0x897e,0xe9,
+0x897f,0xf5,
+0x8980,0x2b,
+0x8981,0xe8,
+0x8982,0xf5,
+0x8983,0x2a,
+0x8984,0x22,
+0x8985,0xe5,
+0x8986,0x45,
+0x8987,0x64,
+0x8988,0x01,
+0x8989,0x70,
+0x898a,0x50,
+0x898b,0x12,
+0x898c,0x04,
+0x898d,0x6a,
+0x898e,0xe5,
+0x898f,0x44,
+0x8990,0x12,
+0x8991,0x04,
+0x8992,0x18,
+0x8993,0xfe,
+0x8994,0xe4,
+0x8995,0x8f,
+0x8996,0x3c,
+0x8997,0x8e,
+0x8998,0x3b,
+0x8999,0xf5,
+0x899a,0x3a,
+0x899b,0xf5,
+0x899c,0x39,
+0x899d,0x12,
+0x899e,0x04,
+0x899f,0x44,
+0x89a0,0x7b,
+0x89a1,0xff,
+0x89a2,0xfa,
+0x89a3,0xf9,
+0x89a4,0xf8,
+0x89a5,0x12,
+0x89a6,0x04,
+0x89a7,0x39,
+0x89a8,0xc0,
+0x89a9,0x04,
+0x89aa,0xc0,
+0x89ab,0x05,
+0x89ac,0xc0,
+0x89ad,0x06,
+0x89ae,0xc0,
+0x89af,0x07,
+0x89b0,0x12,
+0x89b1,0x04,
+0x89b2,0x10,
+0x89b3,0xab,
+0x89b4,0x07,
+0x89b5,0xfa,
+0x89b6,0xe4,
+0x89b7,0xf9,
+0x89b8,0xf8,
+0x89b9,0xd0,
+0x89ba,0x07,
+0x89bb,0xd0,
+0x89bc,0x06,
+0x89bd,0xd0,
+0x89be,0x05,
+0x89bf,0xd0,
+0x89c0,0x04,
+0x89c1,0x12,
+0x89c2,0x04,
+0x89c3,0x9f,
+0x89c4,0x85,
+0x89c5,0x3c,
+0x89c6,0x39,
+0x89c7,0x85,
+0x89c8,0x44,
+0x89c9,0x3a,
+0x89ca,0x12,
+0x89cb,0x04,
+0x89cc,0x6a,
+0x89cd,0xe5,
+0x89ce,0x44,
+0x89cf,0x12,
+0x89d0,0x04,
+0x89d1,0xd4,
+0x89d2,0xe4,
+0x89d3,0x93,
+0x89d4,0xf5,
+0x89d5,0x3b,
+0x89d6,0x74,
+0x89d7,0x01,
+0x89d8,0x93,
+0x89d9,0xf5,
+0x89da,0x3c,
+0x89db,0x90,
+0x89dc,0x3f,
+0x89dd,0x02,
+0x89de,0xe5,
+0x89df,0x39,
+0x89e0,0xf0,
+0x89e1,0xa3,
+0x89e2,0xe5,
+0x89e3,0x3a,
+0x89e4,0xf0,
+0x89e5,0xa3,
+0x89e6,0xe5,
+0x89e7,0x3b,
+0x89e8,0xf0,
+0x89e9,0xa3,
+0x89ea,0xe5,
+0x89eb,0x3c,
+0x89ec,0xf0,
+0x89ed,0x22,
+0x89ee,0xe5,
+0x89ef,0x09,
+0x89f0,0xd3,
+0x89f1,0x95,
+0x89f2,0x43,
+0x89f3,0x40,
+0x89f4,0x01,
+0x89f5,0x22,
+0x89f6,0x12,
+0x89f7,0x04,
+0x89f8,0x6a,
+0x89f9,0xe5,
+0x89fa,0x09,
+0x89fb,0x12,
+0x89fc,0x04,
+0x89fd,0xd4,
+0x89fe,0xe4,
+0x89ff,0x93,
+0x8a00,0xfe,
+0x8a01,0x74,
+0x8a02,0x01,
+0x8a03,0x93,
+0x8a04,0xff,
+0x8a05,0x4e,
+0x8a06,0x60,
+0x8a07,0x21,
+0x8a08,0x8e,
+0x8a09,0x37,
+0x8a0a,0x8f,
+0x8a0b,0x38,
+0x8a0c,0xef,
+0x8a0d,0xc4,
+0x8a0e,0xf8,
+0x8a0f,0x54,
+0x8a10,0x0f,
+0x8a11,0xc8,
+0x8a12,0x68,
+0x8a13,0xff,
+0x8a14,0xee,
+0x8a15,0xc4,
+0x8a16,0x54,
+0x8a17,0xf0,
+0x8a18,0x48,
+0x8a19,0xfe,
+0x8a1a,0x43,
+0x8a1b,0x07,
+0x8a1c,0x0d,
+0x8a1d,0x8e,
+0x8a1e,0x0a,
+0x8a1f,0x8f,
+0x8a20,0x0b,
+0x8a21,0x12,
+0x8a22,0x01,
+0x8a23,0x78,
+0x8a24,0x30,
+0x8a25,0x23,
+0x8a26,0x22,
+0x8a27,0xc3,
+0x8a28,0x22,
+0x8a29,0x75,
+0x8a2a,0x0a,
+0x8a2b,0x00,
+0x8a2c,0x75,
+0x8a2d,0x0b,
+0x8a2e,0x0d,
+0x8a2f,0x12,
+0x8a30,0x01,
+0x8a31,0x78,
+0x8a32,0x30,
+0x8a33,0x23,
+0x8a34,0x02,
+0x8a35,0xc3,
+0x8a36,0x22,
+0x8a37,0x75,
+0x8a38,0x0a,
+0x8a39,0x00,
+0x8a3a,0x75,
+0x8a3b,0x0b,
+0x8a3c,0x64,
+0x8a3d,0x12,
+0x8a3e,0x0c,
+0x8a3f,0x67,
+0x8a40,0x75,
+0x8a41,0x0a,
+0x8a42,0x80,
+0x8a43,0x75,
+0x8a44,0x0b,
+0x8a45,0x00,
+0x8a46,0x12,
+0x8a47,0x01,
+0x8a48,0x78,
+0x8a49,0x85,
+0x8a4a,0x09,
+0x8a4b,0x44,
+0x8a4c,0xd3,
+0x8a4d,0x22,
+0x8a4e,0xc2,
+0x8a4f,0x25,
+0x8a50,0x20,
+0x8a51,0x05,
+0x8a52,0x05,
+0x8a53,0x75,
+0x8a54,0x09,
+0x8a55,0xee,
+0x8a56,0x80,
+0x8a57,0x36,
+0x8a58,0x20,
+0x8a59,0x07,
+0x8a5a,0x08,
+0x8a5b,0x20,
+0x8a5c,0x06,
+0x8a5d,0x05,
+0x8a5e,0xe4,
+0x8a5f,0xf5,
+0x8a60,0x09,
+0x8a61,0x80,
+0x8a62,0x2b,
+0x8a63,0x20,
+0x8a64,0x07,
+0x8a65,0x08,
+0x8a66,0x30,
+0x8a67,0x06,
+0x8a68,0x05,
+0x8a69,0x75,
+0x8a6a,0x09,
+0x8a6b,0x20,
+0x8a6c,0x80,
+0x8a6d,0x20,
+0x8a6e,0x30,
+0x8a6f,0x00,
+0x8a70,0x05,
+0x8a71,0x75,
+0x8a72,0x09,
+0x8a73,0x01,
+0x8a74,0x80,
+0x8a75,0x18,
+0x8a76,0xe5,
+0x8a77,0x20,
+0x8a78,0x54,
+0x8a79,0x07,
+0x8a7a,0xff,
+0x8a7b,0xbf,
+0x8a7c,0x06,
+0x8a7d,0x0d,
+0x8a7e,0x30,
+0x8a7f,0x21,
+0x8a80,0x04,
+0x8a81,0x7f,
+0x8a82,0x12,
+0x8a83,0x80,
+0x8a84,0x02,
+0x8a85,0x7f,
+0x8a86,0x02,
+0x8a87,0x8f,
+0x8a88,0x09,
+0x8a89,0x80,
+0x8a8a,0x03,
+0x8a8b,0x75,
+0x8a8c,0x09,
+0x8a8d,0xfe,
+0x8a8e,0x90,
+0x8a8f,0x3f,
+0x8a90,0x07,
+0x8a91,0xe5,
+0x8a92,0x09,
+0x8a93,0xf0,
+0x8a94,0x90,
+0x8a95,0x3f,
+0x8a96,0x06,
+0x8a97,0xe5,
+0x8a98,0x44,
+0x8a99,0xf0,
+0x8a9a,0x22,
+0x8a9b,0x85,
+0x8a9c,0x0d,
+0x8a9d,0x0e,
+0x8a9e,0x7f,
+0x8a9f,0x08,
+0x8aa0,0xe5,
+0x8aa1,0x0e,
+0x8aa2,0x30,
+0x8aa3,0xe7,
+0x8aa4,0x04,
+0x8aa5,0xd2,
+0x8aa6,0x19,
+0x8aa7,0x80,
+0x8aa8,0x02,
+0x8aa9,0xc2,
+0x8aaa,0x19,
+0x8aab,0x12,
+0x8aac,0x04,
+0x8aad,0x09,
+0x8aae,0x75,
+0x8aaf,0x32,
+0x8ab0,0x0a,
+0x8ab1,0xae,
+0x8ab2,0x32,
+0x8ab3,0x15,
+0x8ab4,0x32,
+0x8ab5,0xee,
+0x8ab6,0x70,
+0x8ab7,0xf9,
+0x8ab8,0xe5,
+0x8ab9,0x0e,
+0x8aba,0x25,
+0x8abb,0xe0,
+0x8abc,0xf5,
+0x8abd,0x0e,
+0x8abe,0xd2,
+0x8abf,0x18,
+0x8ac0,0x12,
+0x8ac1,0x04,
+0x8ac2,0x09,
+0x8ac3,0x75,
+0x8ac4,0x32,
+0x8ac5,0x0a,
+0x8ac6,0xae,
+0x8ac7,0x32,
+0x8ac8,0x15,
+0x8ac9,0x32,
+0x8aca,0xee,
+0x8acb,0x70,
+0x8acc,0xf9,
+0x8acd,0xc2,
+0x8ace,0x18,
+0x8acf,0x12,
+0x8ad0,0x04,
+0x8ad1,0x09,
+0x8ad2,0x75,
+0x8ad3,0x32,
+0x8ad4,0x05,
+0x8ad5,0xae,
+0x8ad6,0x32,
+0x8ad7,0x15,
+0x8ad8,0x32,
+0x8ad9,0xee,
+0x8ada,0x70,
+0x8adb,0xf9,
+0x8adc,0xdf,
+0x8add,0xc2,
+0x8ade,0x22,
+0x8adf,0x90,
+0x8ae0,0x3f,
+0x8ae1,0x07,
+0x8ae2,0x74,
+0x8ae3,0xfa,
+0x8ae4,0xf0,
+0x8ae5,0x12,
+0x8ae6,0x08,
+0x8ae7,0x65,
+0x8ae8,0x12,
+0x8ae9,0x0b,
+0x8aea,0xfb,
+0x8aeb,0xe4,
+0x8aec,0xf5,
+0x8aed,0x29,
+0x8aee,0xd2,
+0x8aef,0xaf,
+0x8af0,0x12,
+0x8af1,0x05,
+0x8af2,0xfc,
+0x8af3,0x30,
+0x8af4,0x20,
+0x8af5,0x03,
+0x8af6,0x12,
+0x8af7,0x02,
+0x8af8,0xc4,
+0x8af9,0x30,
+0x8afa,0x25,
+0x8afb,0x03,
+0x8afc,0x12,
+0x8afd,0x0a,
+0x8afe,0x4e,
+0x8aff,0x30,
+0x8b00,0x2f,
+0x8b01,0xee,
+0x8b02,0xc2,
+0x8b03,0x2f,
+0x8b04,0xd2,
+0x8b05,0x26,
+0x8b06,0x30,
+0x8b07,0x00,
+0x8b08,0x05,
+0x8b09,0x12,
+0x8b0a,0x0b,
+0x8b0b,0x7b,
+0x8b0c,0x80,
+0x8b0d,0x09,
+0x8b0e,0x20,
+0x8b0f,0x07,
+0x8b10,0x06,
+0x8b11,0x30,
+0x8b12,0x06,
+0x8b13,0x03,
+0x8b14,0x12,
+0x8b15,0x04,
+0x8b16,0xf5,
+0x8b17,0xc2,
+0x8b18,0x26,
+0x8b19,0x80,
+0x8b1a,0xd5,
+0x8b1b,0xe5,
+0x8b1c,0x44,
+0x8b1d,0x70,
+0x8b1e,0x19,
+0x8b1f,0x12,
+0x8b20,0x0d,
+0x8b21,0x4b,
+0x8b22,0xc2,
+0x8b23,0x30,
+0x8b24,0x12,
+0x8b25,0x0b,
+0x8b26,0xd2,
+0x8b27,0xc2,
+0x8b28,0x30,
+0x8b29,0x12,
+0x8b2a,0x0c,
+0x8b2b,0x89,
+0x8b2c,0xc2,
+0x8b2d,0x03,
+0x8b2e,0x12,
+0x8b2f,0x0c,
+0x8b30,0xfe,
+0x8b31,0xd2,
+0x8b32,0x02,
+0x8b33,0xd2,
+0x8b34,0x01,
+0x8b35,0xd2,
+0x8b36,0x00,
+0x8b37,0x22,
+0x8b38,0x30,
+0x8b39,0x03,
+0x8b3a,0x08,
+0x8b3b,0xc2,
+0x8b3c,0x03,
+0x8b3d,0xc2,
+0x8b3e,0x04,
+0x8b3f,0x12,
+0x8b40,0x04,
+0x8b41,0xe4,
+0x8b42,0x22,
+0x8b43,0xe4,
+0x8b44,0xf5,
+0x8b45,0x09,
+0x8b46,0x12,
+0x8b47,0x09,
+0x8b48,0xee,
+0x8b49,0xd2,
+0x8b4a,0x03,
+0x8b4b,0x22,
+0x8b4c,0x36,
+0x8b4d,0x0c,
+0x8b4e,0x04,
+0x8b4f,0x00,
+0x8b50,0x00,
+0x8b51,0x00,
+0x8b52,0xc8,
+0x8b53,0x01,
+0x8b54,0x2c,
+0x8b55,0x01,
+0x8b56,0x5e,
+0x8b57,0x01,
+0x8b58,0x8b,
+0x8b59,0x01,
+0x8b5a,0xb8,
+0x8b5b,0x01,
+0x8b5c,0xe5,
+0x8b5d,0x02,
+0x8b5e,0x12,
+0x8b5f,0x02,
+0x8b60,0x3f,
+0x8b61,0x02,
+0x8b62,0x6c,
+0x8b63,0x02,
+0x8b64,0x99,
+0x8b65,0x02,
+0x8b66,0xc6,
+0x8b67,0x02,
+0x8b68,0xf3,
+0x8b69,0x07,
+0x8b6a,0x00,
+0x8b6b,0x02,
+0x8b6c,0x4e,
+0x8b6d,0x02,
+0x8b6e,0x6c,
+0x8b6f,0x02,
+0x8b70,0x8a,
+0x8b71,0x02,
+0x8b72,0xa8,
+0x8b73,0x02,
+0x8b74,0xc6,
+0x8b75,0x02,
+0x8b76,0xe4,
+0x8b77,0x03,
+0x8b78,0x02,
+0x8b79,0x03,
+0x8b7a,0x20,
+0x8b7b,0xe5,
+0x8b7c,0x20,
+0x8b7d,0x54,
+0x8b7e,0x07,
+0x8b7f,0xff,
+0x8b80,0xbf,
+0x8b81,0x01,
+0x8b82,0x03,
+0x8b83,0x02,
+0x8b84,0x0b,
+0x8b85,0x1b,
+0x8b86,0xe5,
+0x8b87,0x20,
+0x8b88,0x54,
+0x8b89,0x07,
+0x8b8a,0xff,
+0x8b8b,0xbf,
+0x8b8c,0x07,
+0x8b8d,0x03,
+0x8b8e,0x02,
+0x8b8f,0x0c,
+0x8b90,0x44,
+0x8b91,0xe5,
+0x8b92,0x20,
+0x8b93,0x54,
+0x8b94,0x07,
+0x8b95,0xff,
+0x8b96,0xbf,
+0x8b97,0x03,
+0x8b98,0x03,
+0x8b99,0x02,
+0x8b9a,0x0b,
+0x8b9b,0xa8,
+0x8b9c,0xe5,
+0x8b9d,0x20,
+0x8b9e,0x54,
+0x8b9f,0x07,
+0x8ba0,0xff,
+0x8ba1,0xbf,
+0x8ba2,0x05,
+0x8ba3,0x03,
+0x8ba4,0x12,
+0x8ba5,0x0d,
+0x8ba6,0x59,
+0x8ba7,0x22,
+0x8ba8,0x12,
+0x8ba9,0x0c,
+0x8baa,0x21,
+0x8bab,0xd2,
+0x8bac,0x30,
+0x8bad,0x12,
+0x8bae,0x0b,
+0x8baf,0xd2,
+0x8bb0,0xd2,
+0x8bb1,0x30,
+0x8bb2,0x12,
+0x8bb3,0x0c,
+0x8bb4,0x89,
+0x8bb5,0xe5,
+0x8bb6,0x43,
+0x8bb7,0xd3,
+0x8bb8,0x95,
+0x8bb9,0x44,
+0x8bba,0x40,
+0x8bbb,0x03,
+0x8bbc,0xd3,
+0x8bbd,0x80,
+0x8bbe,0x01,
+0x8bbf,0xc3,
+0x8bc0,0x50,
+0x8bc1,0x0c,
+0x8bc2,0x20,
+0x8bc3,0x28,
+0x8bc4,0x06,
+0x8bc5,0x30,
+0x8bc6,0x2d,
+0x8bc7,0x03,
+0x8bc8,0x20,
+0x8bc9,0x2e,
+0x8bca,0x03,
+0x8bcb,0x02,
+0x8bcc,0x0c,
+0x8bcd,0xfe,
+0x8bce,0x12,
+0x8bcf,0x0c,
+0x8bd0,0xbf,
+0x8bd1,0x22,
+0x8bd2,0x30,
+0x8bd3,0x30,
+0x8bd4,0x09,
+0x8bd5,0x30,
+0x8bd6,0x2d,
+0x8bd7,0x06,
+0x8bd8,0xae,
+0x8bd9,0x33,
+0x8bda,0xaf,
+0x8bdb,0x34,
+0x8bdc,0x80,
+0x8bdd,0x04,
+0x8bde,0xae,
+0x8bdf,0x3d,
+0x8be0,0xaf,
+0x8be1,0x3e,
+0x8be2,0x8e,
+0x8be3,0x33,
+0x8be4,0x8f,
+0x8be5,0x34,
+0x8be6,0x30,
+0x8be7,0x30,
+0x8be8,0x09,
+0x8be9,0x30,
+0x8bea,0x2e,
+0x8beb,0x06,
+0x8bec,0xae,
+0x8bed,0x35,
+0x8bee,0xaf,
+0x8bef,0x36,
+0x8bf0,0x80,
+0x8bf1,0x04,
+0x8bf2,0xae,
+0x8bf3,0x3f,
+0x8bf4,0xaf,
+0x8bf5,0x40,
+0x8bf6,0x8e,
+0x8bf7,0x35,
+0x8bf8,0x8f,
+0x8bf9,0x36,
+0x8bfa,0x22,
+0x8bfb,0x12,
+0x8bfc,0x0c,
+0x8bfd,0xec,
+0x8bfe,0x12,
+0x8bff,0x0d,
+0x8c00,0x67,
+0x8c01,0x50,
+0x8c02,0x04,
+0x8c03,0xd2,
+0x8c04,0x05,
+0x8c05,0x80,
+0x8c06,0x02,
+0x8c07,0xc2,
+0x8c08,0x05,
+0x8c09,0x12,
+0x8c0a,0x04,
+0x8c0b,0x7c,
+0x8c0c,0xc2,
+0x8c0d,0x28,
+0x8c0e,0xc2,
+0x8c0f,0x21,
+0x8c10,0xd2,
+0x8c11,0x25,
+0x8c12,0x12,
+0x8c13,0x04,
+0x8c14,0xbe,
+0x8c15,0xb5,
+0x8c16,0x07,
+0x8c17,0x03,
+0x8c18,0xd3,
+0x8c19,0x80,
+0x8c1a,0x01,
+0x8c1b,0xc3,
+0x8c1c,0x40,
+0x8c1d,0x02,
+0x8c1e,0xc2,
+0x8c1f,0x05,
+0x8c20,0x22,
+0x8c21,0xd3,
+0x8c22,0xe5,
+0x8c23,0x34,
+0x8c24,0x95,
+0x8c25,0x3e,
+0x8c26,0xe5,
+0x8c27,0x33,
+0x8c28,0x95,
+0x8c29,0x3d,
+0x8c2a,0x40,
+0x8c2b,0x03,
+0x8c2c,0xd3,
+0x8c2d,0x80,
+0x8c2e,0x01,
+0x8c2f,0xc3,
+0x8c30,0x92,
+0x8c31,0x2d,
+0x8c32,0xd3,
+0x8c33,0xe5,
+0x8c34,0x36,
+0x8c35,0x95,
+0x8c36,0x40,
+0x8c37,0xe5,
+0x8c38,0x35,
+0x8c39,0x95,
+0x8c3a,0x3f,
+0x8c3b,0x40,
+0x8c3c,0x03,
+0x8c3d,0xd3,
+0x8c3e,0x80,
+0x8c3f,0x01,
+0x8c40,0xc3,
+0x8c41,0x92,
+0x8c42,0x2e,
+0x8c43,0x22,
+0x8c44,0x12,
+0x8c45,0x0c,
+0x8c46,0x21,
+0x8c47,0xd2,
+0x8c48,0x30,
+0x8c49,0x12,
+0x8c4a,0x0b,
+0x8c4b,0xd2,
+0x8c4c,0xd2,
+0x8c4d,0x30,
+0x8c4e,0x12,
+0x8c4f,0x0c,
+0x8c50,0x89,
+0x8c51,0x12,
+0x8c52,0x0c,
+0x8c53,0xfe,
+0x8c54,0xe5,
+0x8c55,0x28,
+0x8c56,0xd3,
+0x8c57,0x95,
+0x8c58,0x44,
+0x8c59,0x40,
+0x8c5a,0x05,
+0x8c5b,0xe4,
+0x8c5c,0x95,
+0x8c5d,0x44,
+0x8c5e,0x40,
+0x8c5f,0x06,
+0x8c60,0xc2,
+0x8c61,0x02,
+0x8c62,0xd2,
+0x8c63,0x01,
+0x8c64,0xd2,
+0x8c65,0x00,
+0x8c66,0x22,
+0x8c67,0xe4,
+0x8c68,0xff,
+0x8c69,0xfe,
+0x8c6a,0xc3,
+0x8c6b,0xef,
+0x8c6c,0x95,
+0x8c6d,0x0b,
+0x8c6e,0xee,
+0x8c6f,0x95,
+0x8c70,0x0a,
+0x8c71,0x50,
+0x8c72,0x15,
+0x8c73,0x7d,
+0x8c74,0x8a,
+0x8c75,0x7c,
+0x8c76,0x02,
+0x8c77,0xed,
+0x8c78,0x1d,
+0x8c79,0xaa,
+0x8c7a,0x04,
+0x8c7b,0x70,
+0x8c7c,0x01,
+0x8c7d,0x1c,
+0x8c7e,0x4a,
+0x8c7f,0x70,
+0x8c80,0xf6,
+0x8c81,0x0f,
+0x8c82,0xbf,
+0x8c83,0x00,
+0x8c84,0x01,
+0x8c85,0x0e,
+0x8c86,0x80,
+0x8c87,0xe2,
+0x8c88,0x22,
+0x8c89,0x30,
+0x8c8a,0x30,
+0x8c8b,0x07,
+0x8c8c,0x30,
+0x8c8d,0x2d,
+0x8c8e,0x04,
+0x8c8f,0xaf,
+0x8c90,0x30,
+0x8c91,0x80,
+0x8c92,0x02,
+0x8c93,0xaf,
+0x8c94,0x44,
+0x8c95,0x8f,
+0x8c96,0x30,
+0x8c97,0x30,
+0x8c98,0x30,
+0x8c99,0x07,
+0x8c9a,0x30,
+0x8c9b,0x2e,
+0x8c9c,0x04,
+0x8c9d,0xaf,
+0x8c9e,0x31,
+0x8c9f,0x80,
+0x8ca0,0x02,
+0x8ca1,0xaf,
+0x8ca2,0x44,
+0x8ca3,0x8f,
+0x8ca4,0x31,
+0x8ca5,0x22,
+0x8ca6,0xe5,
+0x8ca7,0x45,
+0x8ca8,0xb4,
+0x8ca9,0x01,
+0x8caa,0x05,
+0x8cab,0x12,
+0x8cac,0x0d,
+0x8cad,0x1e,
+0x8cae,0x80,
+0x8caf,0x08,
+0x8cb0,0xe5,
+0x8cb1,0x45,
+0x8cb2,0xb4,
+0x8cb3,0x02,
+0x8cb4,0x09,
+0x8cb5,0x12,
+0x8cb6,0x0d,
+0x8cb7,0x2d,
+0x8cb8,0xe4,
+0x8cb9,0xf5,
+0x8cba,0x09,
+0x8cbb,0x12,
+0x8cbc,0x09,
+0x8cbd,0xee,
+0x8cbe,0x22,
+0x8cbf,0xaf,
+0x8cc0,0x31,
+0x8cc1,0xe5,
+0x8cc2,0x44,
+0x8cc3,0xb5,
+0x8cc4,0x07,
+0x8cc5,0x03,
+0x8cc6,0x02,
+0x8cc7,0x0c,
+0x8cc8,0xd8,
+0x8cc9,0x8f,
+0x8cca,0x09,
+0x8ccb,0x12,
+0x8ccc,0x09,
+0x8ccd,0xee,
+0x8cce,0xd2,
+0x8ccf,0x02,
+0x8cd0,0xc2,
+0x8cd1,0x01,
+0x8cd2,0xd2,
+0x8cd3,0x00,
+0x8cd4,0x75,
+0x8cd5,0x27,
+0x8cd6,0x03,
+0x8cd7,0x22,
+0x8cd8,0xc2,
+0x8cd9,0x03,
+0x8cda,0xd2,
+0x8cdb,0x04,
+0x8cdc,0x12,
+0x8cdd,0x04,
+0x8cde,0xe4,
+0x8cdf,0xc2,
+0x8ce0,0x30,
+0x8ce1,0x12,
+0x8ce2,0x0b,
+0x8ce3,0xd2,
+0x8ce4,0xc2,
+0x8ce5,0x30,
+0x8ce6,0x12,
+0x8ce7,0x0c,
+0x8ce8,0x89,
+0x8ce9,0xd2,
+0x8cea,0x25,
+0x8ceb,0x22,
+0x8cec,0x12,
+0x8ced,0x04,
+0x8cee,0x8b,
+0x8cef,0xf5,
+0x8cf0,0x09,
+0x8cf1,0x75,
+0x8cf2,0x0a,
+0x8cf3,0x01,
+0x8cf4,0x12,
+0x8cf5,0x08,
+0x8cf6,0xfa,
+0x8cf7,0xd2,
+0x8cf8,0x20,
+0x8cf9,0xc2,
+0x8cfa,0x26,
+0x8cfb,0xc2,
+0x8cfc,0x2f,
+0x8cfd,0x22,
+0x8cfe,0xe5,
+0x8cff,0x44,
+0x8d00,0xc3,
+0x8d01,0x95,
+0x8d02,0x43,
+0x8d03,0x40,
+0x8d04,0x01,
+0x8d05,0x22,
+0x8d06,0xe5,
+0x8d07,0x44,
+0x8d08,0x04,
+0x8d09,0xf5,
+0x8d0a,0x09,
+0x8d0b,0x12,
+0x8d0c,0x09,
+0x8d0d,0xee,
+0x8d0e,0x22,
+0x8d0f,0xe5,
+0x8d10,0x44,
+0x8d11,0x70,
+0x8d12,0x02,
+0x8d13,0xc3,
+0x8d14,0x22,
+0x8d15,0xe5,
+0x8d16,0x44,
+0x8d17,0x14,
+0x8d18,0xf5,
+0x8d19,0x09,
+0x8d1a,0x12,
+0x8d1b,0x09,
+0x8d1c,0xee,
+0x8d1d,0x22,
+0x8d1e,0x75,
+0x8d1f,0x2e,
+0x8d20,0x0b,
+0x8d21,0x75,
+0x8d22,0x2f,
+0x8d23,0x4f,
+0x8d24,0x90,
+0x8d25,0x0b,
+0x8d26,0x4d,
+0x8d27,0x12,
+0x8d28,0x04,
+0x8d29,0xda,
+0x8d2a,0xc2,
+0x8d2b,0x2c,
+0x8d2c,0x22,
+0x8d2d,0x75,
+0x8d2e,0x2e,
+0x8d2f,0x0b,
+0x8d30,0x75,
+0x8d31,0x2f,
+0x8d32,0x6b,
+0x8d33,0x90,
+0x8d34,0x0b,
+0x8d35,0x69,
+0x8d36,0x12,
+0x8d37,0x04,
+0x8d38,0xda,
+0x8d39,0xd2,
+0x8d3a,0x2c,
+0x8d3b,0x22,
+0x8d3c,0xe5,
+0x8d3d,0x45,
+0x8d3e,0x24,
+0x8d3f,0xfe,
+0x8d40,0x60,
+0x8d41,0x06,
+0x8d42,0x04,
+0x8d43,0x70,
+0x8d44,0x05,
+0x8d45,0xd2,
+0x8d46,0x28,
+0x8d47,0x22,
+0x8d48,0xc2,
+0x8d49,0x28,
+0x8d4a,0x22,
+0x8d4b,0xe4,
+0x8d4c,0xf5,
+0x8d4d,0x33,
+0x8d4e,0xf5,
+0x8d4f,0x34,
+0x8d50,0xf5,
+0x8d51,0x35,
+0x8d52,0xf5,
+0x8d53,0x36,
+0x8d54,0xc2,
+0x8d55,0x2d,
+0x8d56,0xc2,
+0x8d57,0x2e,
+0x8d58,0x22,
+0x8d59,0xe5,
+0x8d5a,0x27,
+0x8d5b,0xd3,
+0x8d5c,0x94,
+0x8d5d,0x00,
+0x8d5e,0x40,
+0x8d5f,0x03,
+0x8d60,0x15,
+0x8d61,0x27,
+0x8d62,0x22,
+0x8d63,0x12,
+0x8d64,0x0c,
+0x8d65,0xd8,
+0x8d66,0x22,
+0x8d67,0x12,
+0x8d68,0x0d,
+0x8d69,0x1e,
+0x8d6a,0xe4,
+0x8d6b,0xf5,
+0x8d6c,0x09,
+0x8d6d,0x12,
+0x8d6e,0x09,
+0x8d6f,0xee,
+0x8d70,0x22,
+0x3F00,0x00,
+0x3F01,0x00,
+0x3F02,0x00,
+0x3F03,0x00,
+0x3F04,0x00,
+0x3F05,0x00,
+0x3F06,0x00,
+0x3F07,0xFF,
+0x3104,0x00,
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/ov3660/ov3660.c b/drivers/media/video/wmt_v4l2/sensors/ov3660/ov3660.c
new file mode 100755
index 00000000..3b913277
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/ov3660/ov3660.c
@@ -0,0 +1,209 @@
+/*
+ * Driver for ov3660 CMOS Image Sensor from Omnivision
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <asm/errno.h>
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "ov3660.h"
+
+#define sensor_write_array(sd, arr, sz) \
+ cmos_init_16bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read16addr(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write16addr(sd->i2c_addr, reg, val)
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint32_t *regs;
+ size_t size;
+};
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+ CMOS_WIN_SIZE("VGA", 640, 480, ov3660_640_480_regs),
+ CMOS_WIN_SIZE("QXGA", 2048, 1536, ov3660_2048_1536_regs),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint32_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = ov3660_wb_auto;
+ size = ARRAY_SIZE(ov3660_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = ov3660_wb_incandescent;
+ size = ARRAY_SIZE(ov3660_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = ov3660_wb_daylight;
+ size = ARRAY_SIZE(ov3660_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = ov3660_wb_cloudy;
+ size = ARRAY_SIZE(ov3660_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = ov3660_wb_fluorescent;
+ size = ARRAY_SIZE(ov3660_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win)
+ return -EINVAL;
+
+ sensor_write_array(sd, win->regs, win->size);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ /*
+ * select suitable win
+ */
+ win = cmos_select_win(&mf->width, &mf->height);
+
+ return 0;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ uint32_t data;
+
+ data = sensor_read(sd, 0x300a);
+ data <<= 8;
+ data |= sensor_read(sd, 0x300b);
+
+ return (data == sd->id) ? 0 : 1;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ if (!sensor_identify(sd))
+ return -1;
+
+ sensor_write(sd, 0x3103, 0x11);
+ sensor_write(sd, 0x3008, 0x82);
+ msleep(5);
+ sensor_write_array(sd, ov3660_default_regs_init,
+ ARRAY_SIZE(ov3660_default_regs_init));
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ sensor_write(sd, 0x3017, 0x80);
+ sensor_write(sd, 0x3018, 0x03);
+ return 0;
+}
+
+static struct cmos_subdev_ops ov3660_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+};
+
+struct cmos_subdev ov3660 = {
+ .name = "ov3660",
+ .i2c_addr = 0x3c,
+ .id = 0x3660,
+ .max_width = 2048,
+ .max_height = 1536,
+ .ops = &ov3660_ops,
+};
+
+static int __init ov3660_init(void)
+{
+ return cmos_register_sudbdev(&ov3660);
+}
+
+static void __exit ov3660_exit(void)
+{
+ cmos_unregister_subdev(&ov3660);
+ return;
+}
+
+module_init(ov3660_init);
+module_exit(ov3660_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/wmt_v4l2/sensors/ov3660/ov3660.h b/drivers/media/video/wmt_v4l2/sensors/ov3660/ov3660.h
new file mode 100755
index 00000000..bbe1169c
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/ov3660/ov3660.h
@@ -0,0 +1,598 @@
+#ifndef OV3660_H
+#define OV3660_H
+
+// Scene Mode
+uint32_t ov3660_scene_mode_auto[] = {
+};
+
+uint32_t ov3660_scene_mode_night[] = {
+};
+
+
+// White Balance
+uint32_t ov3660_wb_auto [] = {
+};
+
+uint32_t ov3660_wb_incandescent [] = {
+};
+
+uint32_t ov3660_wb_fluorescent [] = {
+};
+
+uint32_t ov3660_wb_daylight [] = {
+};
+
+uint32_t ov3660_wb_cloudy [] = {
+};
+
+uint32_t ov3660_wb_tungsten [] = {
+};
+
+
+// Exposure
+uint32_t ov3660_exposure_neg6[] = {
+};
+
+uint32_t ov3660_exposure_neg3[] = {
+};
+
+uint32_t ov3660_exposure_zero[] = {
+};
+
+uint32_t ov3660_exposure_pos3[] = {
+};
+
+uint32_t ov3660_exposure_pos6[] = {
+};
+
+/*
+* The color effect settings
+*/
+uint32_t ov3660_colorfx_none[] = {
+};
+
+uint32_t ov3660_colorfx_bw[] = {
+};
+
+uint32_t ov3660_colorfx_sepia[] = {
+};
+
+uint32_t ov3660_colorfx_negative[] = {
+};
+
+uint32_t ov3660_colorfx_emboss[] = {
+};
+
+uint32_t ov3660_colorfx_sketch[] = {
+};
+
+uint32_t ov3660_colorfx_sky_blue[] = {
+};
+
+uint32_t ov3660_colorfx_grass_green[] = {
+};
+
+uint32_t ov3660_colorfx_skin_whiten[] = {
+};
+
+uint32_t ov3660_colorfx_vivid[] = {
+};
+
+// Brightness
+uint32_t ov3660_brightness_neg4[] = {
+};
+
+uint32_t ov3660_brightness_neg3[] = {
+};
+
+uint32_t ov3660_brightness_neg2[] = {
+};
+
+uint32_t ov3660_brightness_neg1[] = {
+};
+
+uint32_t ov3660_brightness_zero[] = {
+};
+
+uint32_t ov3660_brightness_pos1[] = {
+};
+
+uint32_t ov3660_brightness_pos2[] = {
+};
+
+uint32_t ov3660_brightness_pos3[] = {
+};
+
+uint32_t ov3660_brightness_pos4[] = {
+};
+
+// Contrast
+uint32_t ov3660_contrast_neg4[] = {
+};
+
+uint32_t ov3660_contrast_neg3[] = {
+};
+
+uint32_t ov3660_contrast_neg2[] = {
+};
+
+uint32_t ov3660_contrast_neg1[] = {
+};
+
+uint32_t ov3660_contrast_zero[] = {
+};
+
+uint32_t ov3660_contrast_pos1[] = {
+};
+
+uint32_t ov3660_contrast_pos2[] = {
+};
+
+uint32_t ov3660_contrast_pos3[] = {
+};
+
+uint32_t ov3660_contrast_pos4[] = {
+};
+
+// Saturation
+uint32_t ov3660_saturation_neg4[] = {
+};
+
+uint32_t ov3660_saturation_neg3[] = {
+};
+
+uint32_t ov3660_saturation_neg2[] = {
+};
+
+uint32_t ov3660_saturation_neg1[] = {
+};
+
+uint32_t ov3660_saturation_zero[] = {
+};
+
+uint32_t ov3660_saturation_pos1[] = {
+};
+
+uint32_t ov3660_saturation_pos2[] = {
+};
+
+uint32_t ov3660_saturation_pos3[] = {
+};
+
+uint32_t ov3660_saturation_pos4[] = {
+};
+
+
+
+uint32_t ov3660_640_480_regs[]={
+0x3008,0x42,
+
+0x3503,0x00,
+0x3a00,0x3c,
+
+0x5302,0x38,
+0x5303,0x18,
+0x5306,0x18,
+0x5307,0x28,
+
+0x3800,0x00,
+0x3801,0x00,
+0x3802,0x00,
+0x3803,0x00,
+0x3804,0x08,
+0x3805,0x1f,
+0x3806,0x06,
+0x3807,0x09,
+0x3808,0x02,
+0x3809,0x80,
+0x380a,0x01,
+0x380b,0xe0,
+0x3810,0x00,
+0x3811,0x08,
+0x3812,0x00,
+0x3813,0x02,
+0x3814,0x31,
+0x3815,0x31,
+
+/*0x3826,0x23,
+0x303a,0x00,
+0x303b,0x1b,
+0x303c,0x11,//30fps
+0x303d,0x30,
+0x3824,0x02,
+0x460c,0x22,
+
+0x380c,0x08,
+0x380d,0xfc,
+0x380e,0x03,
+0x380f,0x10,
+
+0x3a08,0x00,
+0x3a09,0xeb,
+0x3a0e,0x03,
+0x3a0a,0x00,
+0x3a0b,0xc4,
+0x3a0d,0x04,*/
+
+0x3826,0x23,
+0x303a,0x00,
+0x303b,0x17,//1b
+0x303c,0x11,//25fps
+0x303d,0x30,
+0x3824,0x02,
+0x460c,0x22,
+
+0x380c,0x08,
+0x380d,0xfc,
+0x380e,0x03,
+0x380f,0x20,//10
+
+0x3c01,0x80,
+0x3c00,0x04,
+0x3a08,0x00,
+0x3a09,0xc8,//eb
+0x3a0e,0x04,//03
+0x3a0a,0x00,
+0x3a0b,0xa7,//c4
+0x3a0d,0x04,
+
+0x3a00,0x3c,//25fps-20fps
+0x3a14,0x03,//04
+0x3a15,0xea,//98
+0x3a02,0x03,//04
+0x3a03,0xea,//98
+
+0x3820,0x01,
+0x3821,0x07,
+0x4514,0xbb,
+0x3618,0x00,
+0x3708,0x66,
+0x3709,0x12,
+0x4520,0x0b,
+
+0x5001,0xa3,
+
+0x3008,0x02,
+};
+
+uint32_t ov3660_2048_1536_regs[]={
+
+0x3008,0x42,
+//0x3503,0x07,
+//0x3a00,0x38,
+
+0x5001,0x83,
+
+0x5302,0x38,
+0x5303,0x18,
+0x5306,0x1c,
+0x5307,0x2c,
+
+0x3800,0x00,
+0x3801,0x00,
+0x3802,0x00,
+0x3803,0x00,
+0x3804,0x08,
+0x3805,0x1f,
+0x3806,0x06,
+0x3807,0x0b,
+0x3808,0x08,
+0x3809,0x00,
+0x380a,0x06,
+0x380b,0x00,
+0x3810,0x00,
+0x3811,0x10,
+0x3812,0x00,
+0x3813,0x06,
+0x3814,0x11,
+0x3815,0x11,
+
+0x3826,0x23,
+0x303a,0x00,
+0x303b,0x17,//1b
+0x303c,0x12,//6.25fps
+0x303d,0x30,
+0x3824,0x01,
+0x460c,0x20,
+
+0x380c,0x08,
+0x380d,0xfc,
+0x380e,0x06,
+0x380f,0x40,//20
+
+0x3a08,0x00,
+0x3a09,0x64,//76
+0x3a0e,0x10,//0d
+0x3a0a,0x00,
+0x3a0b,0x53,//62
+0x3a0d,0x13,//10
+
+0x3820,0x40,
+0x3821,0x06,
+0x4514,0x00,
+0x3618,0x78,
+0x3708,0x63,
+0x3709,0x12,
+0x4520,0xb0,
+
+0x3008,0x02,
+};
+
+uint32_t ov3660_default_regs_init[]={
+//0x3008,0x82,//reset 5m delay
+
+0x3103,0x13,
+0x3008,0x42,
+0x3017,0xff,
+0x3018,0xff,
+0x302c,0x03,
+
+0x3611,0x01,
+0x3612,0x2d,
+
+0x3032,0x00,
+0x3614,0x80,
+0x3618,0x00,
+0x3619,0x75,
+0x3622,0x80,
+0x3623,0x00,
+0x3624,0x03,
+0x3630,0x52,
+0x3632,0x07,
+0x3633,0xd2,
+0x3704,0x80,
+0x3708,0x66,
+0x3709,0x12,
+0x370b,0x12,
+0x3717,0x00,
+0x371b,0x60,
+0x371c,0x00,
+0x3901,0x13,
+
+0x3600,0x08,
+0x3620,0x43,
+0x3702,0x20,
+0x3739,0x48,
+0x3730,0x20,
+0x370c,0x0c,
+
+0x3a18,0x00,
+0x3a19,0xf8,
+
+0x3000,0x10,
+0x3002,0x1c,
+0x3004,0xef,
+0x3006,0xc3,
+
+0x6700,0x05,
+0x6701,0x19,
+0x6702,0xfd,
+0x6703,0xd1,
+0x6704,0xff,
+0x6705,0xff,
+
+0x3800,0x00,
+0x3801,0x00,
+0x3802,0x00,
+0x3803,0x00,
+0x3804,0x08,
+0x3805,0x1f,
+0x3806,0x06,
+0x3807,0x09,
+0x3808,0x02,
+0x3809,0x80,
+0x380a,0x01,
+0x380b,0xe0,
+0x3810,0x00,
+0x3811,0x08,
+0x3812,0x00,
+0x3813,0x02,
+0x3814,0x31,
+0x3815,0x31,
+
+0x3826,0x23,
+0x303a,0x00,
+0x303b,0x17,//1b
+0x303c,0x11,//25fps
+0x303d,0x30,
+0x3824,0x02,
+0x460c,0x22,
+
+0x380c,0x08,
+0x380d,0xfc,
+0x380e,0x03,
+0x380f,0x20,//10
+
+0x3c01,0x80,
+0x3c00,0x04,
+0x3a08,0x00,
+0x3a09,0xc8,//eb
+0x3a0e,0x04,//03
+0x3a0a,0x00,
+0x3a0b,0xa7,//c4
+0x3a0d,0x04,
+
+0x3a00,0x3c,//25fps-20fps
+0x3a14,0x03,//04
+0x3a15,0xea,//98
+0x3a02,0x03,//04
+0x3a03,0xea,//98
+
+0x4300,0x30,
+0x440e,0x08,
+0x4520,0x0b,
+0x460b,0x37,
+0x4713,0x02,
+0x471c,0xd0,
+0x5086,0x00,
+
+0x5001,0x03,
+0x5002,0x00,
+0x501f,0x00,
+
+0x3820,0x01,
+0x3821,0x07,
+0x4514,0xbb,
+0x3008,0x02,
+
+0x5180,0xff,
+0x5181,0xf2,
+0x5182,0x00,
+0x5183,0x14,
+0x5184,0x25,
+0x5185,0x24,
+0x5186,0x16,
+0x5187,0x16,
+0x5188,0x16,
+0x5189,0x68,
+0x518a,0x60,
+0x518b,0xe0,
+0x518c,0xb2,
+0x518d,0x42,
+0x518e,0x35,
+0x518f,0x56,
+0x5190,0x56,
+0x5191,0xf8,
+0x5192,0x04,
+0x5193,0x70,
+0x5194,0xf0,
+0x5195,0xf0,
+0x5196,0x03,
+0x5197,0x01,
+0x5198,0x04,
+0x5199,0x12,
+0x519a,0x04,
+0x519b,0x00,
+0x519c,0x06,
+0x519d,0x82,
+0x519e,0x38,
+
+0x5381,0x1c,
+0x5382,0x5a,
+0x5383,0x12,
+0x5384,0x02,
+0x5385,0x6f,
+0x5386,0x71,
+0x5387,0x7c,
+0x5388,0x6a,
+0x5389,0x12,
+0x538a,0x01,
+0x538b,0x98,
+
+0x5481,0x06,
+0x5482,0x0e,
+0x5483,0x1e,
+0x5484,0x42,
+0x5485,0x54,
+0x5486,0x68,
+0x5487,0x76,
+0x5488,0x82,
+0x5489,0x8e,
+0x548a,0x9b,
+0x548b,0xac,
+0x548c,0xba,
+0x548d,0xd0,
+0x548e,0xe3,
+0x548f,0xf6,
+0x5490,0x11,
+
+0x5000,0xa7,
+0x5800,0x17,
+0x5801,0x0C,
+0x5802,0x0B,
+0x5803,0x0C,
+0x5804,0x10,
+0x5805,0x1D,
+0x5806,0x07,
+0x5807,0x05,
+0x5808,0x04,
+0x5809,0x04,
+0x580a,0x07,
+0x580b,0x0C,
+0x580c,0x05,
+0x580d,0x02,
+0x580e,0x00,
+0x580f,0x00,
+0x5810,0x04,
+0x5811,0x09,
+0x5812,0x05,
+0x5813,0x02,
+0x5814,0x00,
+0x5815,0x01,
+0x5816,0x04,
+0x5817,0x0A,
+0x5818,0x09,
+0x5819,0x06,
+0x581a,0x05,
+0x581b,0x06,
+0x581c,0x09,
+0x581d,0x0E,
+0x581e,0x1B,
+0x581f,0x0F,
+0x5820,0x0E,
+0x5821,0x10,
+0x5822,0x13,
+0x5823,0x24,
+0x5824,0x52,
+0x5825,0x23,
+0x5826,0x13,
+0x5827,0x33,
+0x5828,0x43,
+0x5829,0x24,
+0x582a,0x12,
+0x582b,0x22,
+0x582c,0x12,
+0x582d,0x14,
+0x582e,0x13,
+0x582f,0x31,
+0x5830,0x40,
+0x5831,0x31,
+0x5832,0x03,
+0x5833,0x24,
+0x5834,0x23,
+0x5835,0x22,
+0x5836,0x23,
+0x5837,0x14,
+0x5838,0x42,
+0x5839,0x23,
+0x583a,0x13,
+0x583b,0x23,
+0x583c,0x53,
+0x583d,0xDF,
+
+0x3a0f,0x38,
+0x3a10,0x30,
+0x3a1b,0x38,
+0x3a1e,0x30,
+0x3a11,0x70,
+0x3a1f,0x14,
+
+0x5302,0x38,
+0x5303,0x18,
+0x5306,0x18,
+0x5307,0x28,
+
+0x4002,0xc5,
+0x4003,0x81,
+0x4005,0x12,
+
+0x5688,0x11,
+0x5689,0x11,
+0x568a,0x11,
+0x568b,0x11,
+0x568c,0x11,
+0x568d,0x11,
+0x568e,0x11,
+0x568f,0x11,
+
+0x5001,0xa3,
+0x5580,0x06,
+0x5588,0x00,
+0x5583,0x40,
+0x5584,0x2c,
+
+//0x3611,0x01,// remove interference fringe
+//0x3612,0x2d,
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/ov5640/ov5640-mid8-wms8368.h b/drivers/media/video/wmt_v4l2/sensors/ov5640/ov5640-mid8-wms8368.h
new file mode 100755
index 00000000..ce07751a
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/ov5640/ov5640-mid8-wms8368.h
@@ -0,0 +1,1296 @@
+#ifndef OV5640_H
+#define OV5640_H
+
+#include "ov5640_firmware.h"
+
+
+// Scene Mode
+uint32_t ov5640_scene_mode_auto[] = {
+ 0x3a00, 0x3c,
+ 0x3a02, 0x0b,
+ 0x3a03, 0x88,
+ 0x3a14, 0x0b,
+ 0x3a15, 0x88,
+};
+
+uint32_t ov5640_scene_mode_night[] = {
+ 0x3a00, 0x3c,
+ 0x3a02, 0x17,
+ 0x3a03, 0x10,
+ 0x3a14, 0x17,
+ 0x3a15, 0x10,
+};
+
+
+// White Balance
+uint32_t ov5640_wb_auto [] = {
+ 0x3406, 0x00,
+};
+
+uint32_t ov5640_wb_incandescent [] = {
+ 0x3212, 0x03,
+ 0x3406, 0x01,
+ 0x3400, 0x05,
+ 0x3401, 0x48,
+ 0x3402, 0x04,
+ 0x3403, 0x00,
+ 0x3404, 0x07,
+ 0x3405, 0xcf,
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_wb_fluorescent [] = {
+ 0x3212, 0x03,
+ 0x3406, 0x01,
+ 0x3400, 0x05,
+ 0x3401, 0x8c,
+ 0x3402, 0x04,
+ 0x3403, 0x00,
+ 0x3404, 0x06,
+ 0x3405, 0xe8,
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_wb_daylight [] = {
+ 0x3212, 0x03,
+ 0x3406, 0x01,
+ 0x3400, 0x06,
+ 0x3401, 0x1c,
+ 0x3402, 0x04,
+ 0x3403, 0x00,
+ 0x3404, 0x04,
+ 0x3405, 0xf3,
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_wb_cloudy [] = {
+ 0x3212, 0x03,
+ 0x3406, 0x01,
+ 0x3400, 0x06,
+ 0x3401, 0x48,
+ 0x3402, 0x04,
+ 0x3403, 0x00,
+ 0x3404, 0x04,
+ 0x3405, 0xd3,
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_wb_tungsten [] = {
+ 0x3212, 0x03,
+ 0x3406, 0x01,
+ 0x3400, 0x04,
+ 0x3401, 0x10,
+ 0x3402, 0x04,
+ 0x3403, 0x00,
+ 0x3404, 0x08,
+ 0x3405, 0x40,
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+
+// Exposure
+uint32_t ov5640_exposure_neg6[] = {
+ 0x3a0f, 0x20,
+ 0x3a10, 0x18,
+ 0x3a11, 0x41,
+ 0x3a1b, 0x20,
+ 0x3a1e, 0x18,
+ 0x3a1f, 0x10,
+};
+
+uint32_t ov5640_exposure_neg3[] = {
+ 0x3a0f, 0x28,
+ 0x3a10, 0x20,
+ 0x3a11, 0x51,
+ 0x3a1b, 0x28,
+ 0x3a1e, 0x20,
+ 0x3a1f, 0x10,
+};
+
+uint32_t ov5640_exposure_zero[] = {
+ 0x3a0f, 0x30,
+ 0x3a10, 0x28,
+ 0x3a11, 0x61,
+ 0x3a1b, 0x30,
+ 0x3a1e, 0x28,
+ 0x3a1f, 0x10,
+};
+
+uint32_t ov5640_exposure_pos3[] = {
+ 0x3a0f, 0x40,
+ 0x3a10, 0x38,
+ 0x3a11, 0x71,
+ 0x3a1b, 0x40,
+ 0x3a1e, 0x38,
+ 0x3a1f, 0x10,
+};
+
+uint32_t ov5640_exposure_pos6[] = {
+ 0x3a0f, 0x50,
+ 0x3a10, 0x48,
+ 0x3a11, 0x90,
+ 0x3a1b, 0x50,
+ 0x3a1e, 0x48,
+ 0x3a1f, 0x20,
+};
+
+/*
+ * The color effect settings
+ */
+uint32_t ov5640_colorfx_none[] = {
+ 0x3212, 0x03,
+ 0x5580, 0x06,
+ 0x5583, 0x40,
+ 0x5584, 0x10,
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_colorfx_bw[] = {
+ 0x3212, 0x03,
+ 0x5580, 0x1e,
+ 0x5583, 0x80,
+ 0x5584, 0x80,
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_colorfx_sepia[] = {
+ 0x3212, 0x03,
+ 0x5580, 0x1e,
+ 0x5583, 0x40,
+ 0x5584, 0xa0,
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_colorfx_negative[] = {
+ 0x3212, 0x03,
+ 0x5580, 0x46,
+ 0x5583, 0x40,
+ 0x5584, 0x10,
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_colorfx_emboss[] = {
+};
+
+uint32_t ov5640_colorfx_sketch[] = {
+};
+
+uint32_t ov5640_colorfx_sky_blue[] = {
+};
+
+uint32_t ov5640_colorfx_grass_green[] = {
+};
+
+uint32_t ov5640_colorfx_skin_whiten[] = {
+};
+
+uint32_t ov5640_colorfx_vivid[] = {
+};
+
+// Brightness
+uint32_t ov5640_brightness_neg4[] = {
+ 0x3212, 0x03,
+
+ 0x5587, 0x40,
+ 0x5588, 0x09,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_brightness_neg3[] = {
+ 0x3212, 0x03,
+
+ 0x5587, 0x30,
+ 0x5588, 0x09,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_brightness_neg2[] = {
+ 0x3212, 0x03,
+
+ 0x5587, 0x20,
+ 0x5588, 0x09,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_brightness_neg1[] = {
+ 0x3212, 0x03,
+
+ 0x5587, 0x10,
+ 0x5588, 0x09,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_brightness_zero[] = {
+ 0x3212, 0x03,
+
+ 0x5587, 0x00,
+ 0x5588, 0x01,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+
+};
+
+uint32_t ov5640_brightness_pos1[] = {
+ 0x3212, 0x03,
+
+ 0x5587, 0x10,
+ 0x5588, 0x01,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_brightness_pos2[] = {
+ 0x3212, 0x03,
+
+ 0x5587, 0x20,
+ 0x5588, 0x01,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_brightness_pos3[] = {
+ 0x3212, 0x03,
+
+ 0x5587, 0x30,
+ 0x5588, 0x01,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_brightness_pos4[] = {
+ 0x3212, 0x03,
+
+ 0x5587, 0x40,
+ 0x5588, 0x01,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+// Contrast
+uint32_t ov5640_contrast_neg4[] = {
+ 0x3212, 0x03,
+
+ 0x5586, 0x10,
+ 0x5585, 0x10,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_contrast_neg3[] = {
+ 0x3212, 0x03,
+
+ 0x5586, 0x14,
+ 0x5585, 0x14,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_contrast_neg2[] = {
+ 0x3212, 0x03,
+
+ 0x5586, 0x18,
+ 0x5585, 0x18,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_contrast_neg1[] = {
+ 0x3212, 0x03,
+
+ 0x5586, 0x1c,
+ 0x5585, 0x1c,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_contrast_zero[] = {
+ 0x3212, 0x03,
+
+ 0x5586, 0x20,
+ 0x5585, 0x00,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_contrast_pos1[] = {
+ 0x3212, 0x03,
+
+ 0x5586, 0x24,
+ 0x5585, 0x10,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_contrast_pos2[] = {
+ 0x3212, 0x03,
+
+ 0x5586, 0x28,
+ 0x5585, 0x18,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_contrast_pos3[] = {
+ 0x3212, 0x03,
+
+ 0x5586, 0x2c,
+ 0x5585, 0x1c,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_contrast_pos4[] = {
+ 0x3212, 0x03,
+
+ 0x5586, 0x30,
+ 0x5585, 0x20,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+// Saturation
+uint32_t ov5640_saturation_neg4[] = {
+ 0x5583, 0x20,
+};
+
+uint32_t ov5640_saturation_neg3[] = {
+ 0x5583, 0x28,
+};
+
+uint32_t ov5640_saturation_neg2[] = {
+ 0x5583, 0x30,
+};
+
+uint32_t ov5640_saturation_neg1[] = {
+ 0x5583, 0x38,
+};
+
+uint32_t ov5640_saturation_zero[] = {
+ 0x5583, 0x40,
+};
+
+uint32_t ov5640_saturation_pos1[] = {
+ 0x5583, 0x48,
+};
+
+uint32_t ov5640_saturation_pos2[] = {
+ 0x5583, 0x50,
+};
+
+uint32_t ov5640_saturation_pos3[] = {
+ 0x5583, 0x58,
+};
+
+uint32_t ov5640_saturation_pos4[] = {
+ 0x5583, 0x60,
+};
+
+
+uint32_t ov5640_2592_1944_regs[] = {
+ 0x4202, 0x0f,
+
+ 0x3c07, 0x07,
+ 0x5189, 0x66,
+
+ 0x5302, 0x1c,
+ 0x5303, 0x08,
+ 0x5306, 0x08,
+ 0x5307, 0x18,
+
+ 0x3503, 0x07,
+ 0x3a00, 0x38,
+
+ 0x3820, 0x41,
+ 0x3821, 0x07,
+
+ 0x3800, 0x00,
+ 0x3801, 0x00,
+ 0x3802, 0x00,
+ 0x3803, 0x00,
+ 0x3804, 0x0a,
+ 0x3805, 0x3f,
+ 0x3806, 0x07,
+ 0x3807, 0x9f,
+
+ /* Width * Height 2592x1944 */
+ 0x3808, 0x0a,
+ 0x3809, 0x20,
+ 0x380a, 0x07,
+ 0x380b, 0x98,
+
+ 0x3813, 0x04,
+ 0x3814, 0x11,
+ 0x3815, 0x11,
+
+ 0x3034, 0x1a,
+ 0x3035, 0x21,
+ 0x3036, 0x69,
+ 0x3037, 0x13,
+
+ 0x380c, 0x0b,
+ 0x380d, 0x1c,
+ 0x380e, 0x09,
+ 0x380f, 0x3a,
+
+ 0x3a08, 0x01,
+ 0x3a09, 0x27,
+ 0x3a0e, 0x08,
+ 0x3a0a, 0x00,
+ 0x3a0b, 0xf6,
+ 0x3a0d, 0x09,
+
+ 0x3618, 0x04,
+ 0x3612, 0x2b,
+ 0x3709, 0x12,
+ 0x370c, 0x00,
+
+ 0x4004, 0x06,
+ 0x3002, 0x1c,
+ 0x3006, 0xc3,
+ 0x3824, 0x01,
+ 0x5001, 0x83,
+
+ 0x4713, 0x02,
+ 0x4407, 0x0c,
+ 0x460b, 0x37,
+ 0x460c, 0x20,
+
+ 0x4202, 0x00,
+};
+
+uint32_t ov5640_2048_1536_regs[] = {
+ 0x4202, 0x0f,
+
+ 0x3c07, 0x07,
+ 0x5189, 0x66,
+
+ 0x5302, 0x1c,
+ 0x5303, 0x08,
+ 0x5306, 0x08,
+ 0x5307, 0x18,
+
+ 0x3503, 0x07,
+ 0x3a00, 0x38,
+
+ 0x3820, 0x41,
+ 0x3821, 0x07,
+
+ 0x3800, 0x00,
+ 0x3801, 0x00,
+ 0x3802, 0x00,
+ 0x3803, 0x00,
+ 0x3804, 0x0a,
+ 0x3805, 0x3f,
+ 0x3806, 0x07,
+ 0x3807, 0x9f,
+
+ /* Width * Height 2048x1536 */
+ 0x3808, 0x08,
+ 0x3809, 0x00,
+ 0x380a, 0x06,
+ 0x380b, 0x00,
+
+ 0x3813, 0x04,
+ 0x3814, 0x11,
+ 0x3815, 0x11,
+
+ 0x3034, 0x1a,
+ 0x3035, 0x21,
+ 0x3036, 0x69,
+ 0x3037, 0x13,
+
+ 0x380c, 0x0b,
+ 0x380d, 0x1c,
+ 0x380e, 0x09,
+ 0x380f, 0x3a,
+
+ 0x3a08, 0x01,
+ 0x3a09, 0x27,
+ 0x3a0e, 0x08,
+ 0x3a0a, 0x00,
+ 0x3a0b, 0xf6,
+ 0x3a0d, 0x09,
+
+ 0x3618, 0x04,
+ 0x3612, 0x2b,
+ 0x3709, 0x12,
+ 0x370c, 0x00,
+
+ 0x4004, 0x06,
+ 0x3002, 0x1c,
+ 0x3006, 0xc3,
+ 0x3824, 0x01,
+ 0x5001, 0xa3,
+
+ 0x4713, 0x02,
+ 0x4407, 0x0c,
+ 0x460b, 0x37,
+ 0x460c, 0x20,
+
+ 0x4202, 0x00,
+};
+
+uint32_t ov5640_1600_1200_regs[] = {
+ 0x4202, 0x0f,
+
+ 0x3c07, 0x07,
+ 0x5189, 0x66,
+
+ 0x5302, 0x1c,
+ 0x5303, 0x08,
+ 0x5306, 0x08,
+ 0x5307, 0x18,
+
+ 0x3503, 0x07,
+ 0x3a00, 0x38,
+
+ 0x3820, 0x41,
+ 0x3821, 0x07,
+
+ 0x3800, 0x00,
+ 0x3801, 0x00,
+ 0x3802, 0x00,
+ 0x3803, 0x00,
+ 0x3804, 0x0a,
+ 0x3805, 0x3f,
+ 0x3806, 0x07,
+ 0x3807, 0x9f,
+
+ /* Width * Height 1600x1200 */
+ 0x3808, 0x06,
+ 0x3809, 0x40,
+ 0x380a, 0x04,
+ 0x380b, 0xb0,
+
+ 0x3813, 0x04,
+ 0x3814, 0x11,
+ 0x3815, 0x11,
+
+ 0x3034, 0x1a,
+ 0x3035, 0x21,
+ 0x3036, 0x69,
+ 0x3037, 0x13,
+
+ 0x380c, 0x0b,
+ 0x380d, 0x1c,
+ 0x380e, 0x09,
+ 0x380f, 0x3a,
+
+ 0x3a08, 0x01,
+ 0x3a09, 0x27,
+ 0x3a0e, 0x08,
+ 0x3a0a, 0x00,
+ 0x3a0b, 0xf6,
+ 0x3a0d, 0x09,
+
+ 0x3618, 0x04,
+ 0x3612, 0x2b,
+ 0x3709, 0x12,
+ 0x370c, 0x00,
+
+ 0x4004, 0x06,
+ 0x3002, 0x1c,
+ 0x3006, 0xc3,
+ 0x3824, 0x01,
+ 0x5001, 0xa3,
+
+ 0x4713, 0x02,
+ 0x4407, 0x0c,
+ 0x460b, 0x37,
+ 0x460c, 0x20,
+
+ 0x4202, 0x00,
+};
+
+uint32_t ov5640_1280_720_regs[] = {
+ 0x3008,0x42,
+ 0x3034,0x1a,
+ //0x3035,0x21,
+ 0x3036,0x69,
+ 0x3037,0x13,
+ 0x3108,0x01,
+ 0x3824,0x01,
+ 0xffff,0x32,
+ 0x3808,0x05,
+ 0x3809,0x00,
+ 0x380a,0x02,
+ 0x380b,0xd0,
+ 0x380c,0x07,
+ 0x380d,0x64,
+ 0x380e,0x02,
+ 0x380f,0xe4,
+
+ 0x3a08,0x00,
+ 0x3a09,0xdd,
+ 0x3a0a,0x00,
+ 0x3a0b,0xb8,
+ 0x3a0e,0x03,
+ 0x3a0d,0x04,
+
+ 0x3c07,0x07,
+ 0x3814,0x31,
+ 0x3815,0x31,
+ 0x3800,0x00,
+ 0x3801,0x00,
+ 0x3802,0x00,
+ 0x3803,0xfa,
+ 0x3804,0x0a,
+ 0x3805,0x3f,
+ 0x3806,0x06,
+ 0x3807,0xa9,
+ 0x3810,0x00,
+ 0x3811,0x10,
+ 0x3812,0x00,
+ 0x3813,0x04,
+
+ 0x5308,0x65,
+ 0x5302,0x00,
+ 0x4002,0x45,
+ 0x4005,0x18,
+
+ 0x3618,0x00,
+ 0x3612,0x29,
+ 0x3709,0x52,
+ 0x370c,0x03,
+ 0x3a02,0x02,
+ 0x3a03,0xe0,
+ 0x3a14,0x02,
+ 0x3a15,0xe0,
+
+ 0x4004,0x02,
+ 0x3002,0x1c,
+ 0x3006,0xc3,
+ 0x460b,0x37,
+ 0x460c,0x20,
+ 0x4837,0x16,
+ 0x5001,0x83,
+ 0x3503,0x00,
+
+ 0x302c,0xc2,
+ 0x3008,0x02,
+ 0xffff,0x32,
+};
+
+uint32_t ov5640_800_600_regs[] = {
+ 0x4202, 0x0f,
+
+ 0x3c07, 0x08,
+ 0x5189, 0x72,
+
+ 0x3503, 0x00,
+ 0x3a00, 0x3c,
+
+ 0x5302, 0x38,
+ 0x5303, 0x18,
+ 0x5306, 0x06,
+ 0x5307, 0x16,
+
+ 0x3820, 0x41,
+ 0x3821, 0x07,
+
+ 0x3800, 0x00,
+ 0x3801, 0x00,
+ 0x3802, 0x00,
+ 0x3803, 0x04,
+ 0x3804, 0x0a,
+ 0x3805, 0x3f,
+ 0x3806, 0x07,
+ 0x3807, 0x9b,
+
+ /* Width * Height 800x600 */
+ 0x3808, 0x03,
+ 0x3809, 0x20,
+ 0x380a, 0x02,
+ 0x380b, 0x58,
+
+ 0x3810, 0x00,
+ 0x3811, 0x10,
+ 0x3812, 0x00,
+ 0x3813, 0x06,
+ 0x3814, 0x31,
+ 0x3815, 0x31,
+
+ 0x3034, 0x1a,
+ 0x3035, 0x11,
+ 0x3036, 0x46,
+ 0x3037, 0x13,
+
+ 0x380c, 0x07,
+ 0x380d, 0x68,
+ 0x380e, 0x03,
+ 0x380f, 0xd8,
+
+ 0x3a08, 0x01,
+ 0x3a09, 0x27,
+ 0x3a0e, 0x03,
+ 0x3a0a, 0x00,
+ 0x3a0b, 0xf6,
+ 0x3a0d, 0x04,
+
+ 0x3618, 0x00,
+ 0x3612, 0x29,
+ 0x3709, 0x52,
+ 0x370c, 0x03,
+
+ 0x4004, 0x02,
+ 0x3002, 0x1c,
+ 0x3006, 0xc3,
+ 0x3824, 0x02,
+ 0x5001, 0xa3,
+
+ 0x4713, 0x03,
+ 0x4407, 0x04,
+ 0x460b, 0x35,
+ 0x460c, 0x22,
+ 0x4837, 0x22,
+
+ 0x4202, 0x00,
+
+// 0x3023, 0x01,
+// 0x3022, 0x04,
+};
+
+uint32_t ov5640_320_240_regs[] = {
+ 0x4202, 0x0f,
+
+ 0x3c07, 0x08,
+ 0x5189, 0x72,
+
+ 0x3503, 0x00,
+ 0x3a00, 0x3c,
+
+ 0x5302, 0x38,
+ 0x5303, 0x18,
+ 0x5306, 0x06,
+ 0x5307, 0x16,
+
+ 0x3820, 0x41,
+ 0x3821, 0x07,
+
+ 0x3800, 0x00,
+ 0x3801, 0x00,
+ 0x3802, 0x00,
+ 0x3803, 0x04,
+ 0x3804, 0x0a,
+ 0x3805, 0x3f,
+ 0x3806, 0x07,
+ 0x3807, 0x9b,
+
+ //320x240
+ 0x3808, 0x01,
+ 0x3809, 0x40,
+ 0x380a, 0x00,
+ 0x380b, 0xf0,
+
+ 0x3810, 0x00,
+ 0x3811, 0x10,
+ 0x3812, 0x00,
+ 0x3813, 0x06,
+ 0x3814, 0x31,
+ 0x3815, 0x31,
+
+ 0x3034, 0x1a,
+ 0x3035, 0x11,
+ 0x3036, 0x46,
+ 0x3037, 0x13,
+
+ 0x380c, 0x07,
+ 0x380d, 0x68,
+ 0x380e, 0x03,
+ 0x380f, 0xd8,
+
+ 0x3a08, 0x01,
+ 0x3a09, 0x27,
+ 0x3a0e, 0x03,
+ 0x3a0a, 0x00,
+ 0x3a0b, 0xf6,
+ 0x3a0d, 0x04,
+
+ 0x3618, 0x00,
+ 0x3612, 0x29,
+ 0x3709, 0x52,
+ 0x370c, 0x03,
+
+ 0x4004, 0x02,
+ 0x3002, 0x1c,
+ 0x3006, 0xc3,
+ 0x3824, 0x02,
+ 0x5001, 0xa3,
+
+ 0x4713, 0x03,
+ 0x4407, 0x04,
+ 0x460b, 0x35,
+ 0x460c, 0x22,
+ 0x4837, 0x22,
+
+ 0x4202, 0x00,
+
+// 0x3023, 0x01,
+// 0x3022, 0x04,
+};
+
+uint32_t ov5640_640_480_regs[] = {
+ 0x4202, 0x0f,
+
+ 0x3c07, 0x08,
+ 0x5189, 0x72,
+
+ 0x3503, 0x00,
+ 0x3a00, 0x3c,
+
+ 0x5302, 0x20,//38
+ 0x5303, 0x08, //18
+ 0x5306, 0x26,//06
+ 0x5307, 0x1c,//16
+
+ 0x3820, 0x41,
+ 0x3821, 0x07,
+
+ 0x3800, 0x00,
+ 0x3801, 0x00,
+ 0x3802, 0x00,
+ 0x3803, 0x04,
+ 0x3804, 0x0a,
+ 0x3805, 0x3f,
+ 0x3806, 0x07,
+ 0x3807, 0x9b,
+
+ 0x3808, 0x02,
+ 0x3809, 0x80,
+ 0x380a, 0x01,
+ 0x380b, 0xe0,
+
+ 0x3810, 0x00,
+ 0x3811, 0x10,
+ 0x3812, 0x00,
+ 0x3813, 0x06,
+ 0x3814, 0x31,
+ 0x3815, 0x31,
+
+ 0x3034, 0x1a,
+ 0x3035, 0x11, //11
+ 0x3036, 0x46,
+ 0x3037, 0x13,
+
+ 0x380c, 0x07,
+ 0x380d, 0x68,
+ 0x380e, 0x03,
+ 0x380f, 0xd8,
+
+ 0x3a08, 0x01,
+ 0x3a09, 0x27,
+ 0x3a0e, 0x03,
+ 0x3a0a, 0x00,
+ 0x3a0b, 0xf6,
+ 0x3a0d, 0x04,
+
+ 0x3618, 0x00,
+ 0x3612, 0x29,
+ 0x3709, 0x52,
+ 0x370c, 0x03,
+
+ 0x4004, 0x02,
+ 0x3002, 0x1c,
+ 0x3006, 0xc3,
+ 0x3824, 0x02,
+ 0x5001, 0xa3,
+
+ 0x4713, 0x03,
+ 0x4407, 0x04,
+ 0x460b, 0x35,
+ 0x460c, 0x22,
+ 0x4837, 0x22,
+
+ 0x4202, 0x00,
+
+// 0x3023, 0x01,
+// 0x3022, 0x04,
+};
+
+uint32_t ov5640_default_regs_init[] = {
+ 0x3008, 0x42,
+ 0x3103, 0x03,
+ 0x3017, 0x7f,
+ 0x3018, 0xff,
+
+ 0x3031, 0x00,
+ 0x302c, 0xc2,
+
+ 0x3108, 0x01,
+ 0x3630, 0x36,
+ 0x3631, 0x0e,
+ 0x3632, 0xe2,
+ 0x3633, 0x12,
+ 0x3621, 0xe0,
+ 0x3704, 0xa0,
+ 0x3703, 0x5a,
+ 0x3715, 0x78,
+ 0x3717, 0x01,
+ 0x370b, 0x60,
+ 0x3705, 0x1a,
+ 0x3905, 0x02,
+ 0x3906, 0x10,
+ 0x3901, 0x0a,
+ 0x3731, 0x12,
+ 0x3600, 0x08,
+ 0x3601, 0x33,
+ 0x302d, 0x60,
+ 0x3620, 0x52,
+ 0x371b, 0x20,
+ 0x471c, 0x50,
+
+ 0x3a13, 0x43,
+ 0x3a18, 0x00,
+ 0x3a19, 0xf8,
+
+ 0x3635, 0x13,
+ 0x3636, 0x03,
+ 0x3634, 0x40,
+ 0x3622, 0x01,
+
+ 0x3c04, 0x28,
+ 0x3c05, 0x98,
+ 0x3c06, 0x00,
+ 0x3c07, 0x08,
+ 0x3c08, 0x00,
+ 0x3c09, 0x1c,
+ 0x3c0a, 0x9c,
+ 0x3c0b, 0x40,
+
+ 0x3820, 0x41,
+ 0x3821, 0x07,
+
+ 0x3800, 0x00,
+ 0x3801, 0x00,
+ 0x3802, 0x00,
+ 0x3803, 0x04,
+ 0x3804, 0x0a,
+ 0x3805, 0x3f,
+ 0x3806, 0x07,
+ 0x3807, 0x9b,
+ 0x3808, 0x02,
+ 0x3809, 0x80,
+ 0x380a, 0x01,
+ 0x380b, 0xe0,
+ 0x3810, 0x00,
+ 0x3811, 0x10,
+ 0x3812, 0x00,
+ 0x3813, 0x06,
+ 0x3814, 0x31,
+ 0x3815, 0x31,
+
+ 0x3034, 0x1a,
+ 0x3035, 0x11,
+ 0x3036, 0x46,
+ 0x3037, 0x13,
+
+ 0x380c, 0x07,
+ 0x380d, 0x68,
+ 0x380e, 0x03,
+ 0x380f, 0xd8,
+
+ 0x3c01, 0xb4,
+ 0x3c00, 0x04,
+ 0x3a08, 0x01,
+ 0x3a09, 0x27,
+ 0x3a0e, 0x03,
+ 0x3a0a, 0x00,
+ 0x3a0b, 0xf6,
+ 0x3a0d, 0x04,
+
+ 0x3a00, 0x3c,
+ 0x3a02, 0x0b,
+ 0x3a03, 0x88,
+ 0x3a14, 0x0b,
+ 0x3a15, 0x88,
+
+ 0x3618, 0x00,
+ 0x3612, 0x29,
+ 0x3708, 0x64,
+ 0x3709, 0x52,
+ 0x370c, 0x03,
+
+ 0x4001, 0x02,
+ 0x4004, 0x02,
+ 0x3000, 0x00,
+ 0x3002, 0x1c,
+ 0x3004, 0xff,
+ 0x3006, 0xc3,
+ 0x300e, 0x58,
+ 0x302e, 0x00,
+ 0x4300, 0x30,
+ 0x501f, 0x00,
+ 0x4713, 0x03,
+ 0x4407, 0x04,
+ 0x440e, 0x00,
+ 0x460b, 0x35,
+ 0x460c, 0x22,
+ 0x4837, 0x22,
+ 0x3824, 0x02,
+ 0x5001, 0xa3,
+
+ 0x5180, 0xff,
+ 0x5181, 0xf2,
+ 0x5182, 0x00,
+ 0x5183, 0x14,
+ 0x5184, 0x25,
+ 0x5185, 0x24,
+ 0x5186, 0x16,
+ 0x5187, 0x16,
+ 0x5188, 0x16,
+ 0x5189, 0x72,
+ 0x518a, 0x68,
+ 0x518b, 0xe0,
+ 0x518c, 0xb2,
+ 0x518d, 0x42,
+ 0x518e, 0x3e,
+ 0x518f, 0x4c,
+ 0x5190, 0x56,
+ 0x5191, 0xf8,
+ 0x5192, 0x04,
+ 0x5193, 0x70,
+ 0x5194, 0xf0,
+ 0x5195, 0xf0,
+ 0x5196, 0x03,
+ 0x5197, 0x01,
+ 0x5198, 0x04,
+ 0x5199, 0x12,
+ 0x519a, 0x04,
+ 0x519b, 0x00,
+ 0x519c, 0x06,
+ 0x519d, 0x82,
+ 0x519e, 0x38,
+
+ 0x5381, 0x1e,
+ 0x5382, 0x5b,
+ 0x5383, 0x12,
+ 0x5384, 0x07,
+ 0x5385, 0x7f,
+ 0x5386, 0x86,
+ 0x5387, 0x8a,
+ 0x5388, 0x80,
+ 0x5389, 0x0a,
+ 0x538a, 0x01,
+ 0x538b, 0x98,
+
+ 0x5480, 0x01,
+ 0x5481, 0x06,
+ 0x5482, 0x12,
+ 0x5483, 0x24,
+ 0x5484, 0x4a,
+ 0x5485, 0x58,
+ 0x5486, 0x65,
+ 0x5487, 0x72,
+ 0x5488, 0x7d,
+ 0x5489, 0x88,
+ 0x548a, 0x92,
+ 0x548b, 0xa3,
+ 0x548c, 0xb2,
+ 0x548d, 0xc8,
+ 0x548e, 0xdd,
+ 0x548f, 0xf0,
+ 0x5490, 0x15,
+
+ 0x5300, 0x08,
+ 0x5301, 0x30,
+ 0x5302, 0x3f,
+ 0x5303, 0x10,
+ 0x5304, 0x08,
+ 0x5305, 0x30,
+ 0x5306, 0x04,
+ 0x5307, 0x14,
+ 0x5309, 0x08,
+ 0x530a, 0x30,
+ 0x530b, 0x04,
+ 0x530c, 0x06,
+
+ 0x5580, 0x06,
+ 0x5583, 0x40,
+ 0x5584, 0x20,
+ 0x5589, 0x10,
+ 0x558a, 0x00,
+ 0x558b, 0xf8,
+
+ 0x5000, 0xa7,
+#if 0
+ 0x5800, 0x20,
+ 0x5801, 0x19,
+ 0x5802, 0x17,
+ 0x5803, 0x16,
+ 0x5804, 0x18,
+ 0x5805, 0x21,
+ 0x5806, 0x0F,
+ 0x5807, 0x0A,
+ 0x5808, 0x07,
+ 0x5809, 0x07,
+ 0x580A, 0x0A,
+ 0x580B, 0x0C,
+ 0x580C, 0x0A,
+ 0x580D, 0x03,
+ 0x580E, 0x01,
+ 0x580F, 0x01,
+ 0x5810, 0x03,
+ 0x5811, 0x09,
+ 0x5812, 0x0A,
+ 0x5813, 0x03,
+ 0x5814, 0x01,
+ 0x5815, 0x01,
+ 0x5816, 0x03,
+ 0x5817, 0x08,
+ 0x5818, 0x10,
+ 0x5819, 0x0A,
+ 0x581A, 0x06,
+ 0x581B, 0x06,
+ 0x581C, 0x08,
+ 0x581D, 0x0E,
+ 0x581E, 0x22,
+ 0x581F, 0x18,
+ 0x5820, 0x13,
+ 0x5821, 0x12,
+ 0x5822, 0x16,
+ 0x5823, 0x1E,
+ 0x5824, 0x64,
+ 0x5825, 0x2A,
+ 0x5826, 0x2C,
+ 0x5827, 0x2A,
+ 0x5828, 0x46,
+ 0x5829, 0x2A,
+ 0x582A, 0x26,
+ 0x582B, 0x24,
+ 0x582C, 0x26,
+ 0x582D, 0x2A,
+ 0x582E, 0x28,
+ 0x582F, 0x42,
+ 0x5830, 0x40,
+ 0x5831, 0x42,
+ 0x5832, 0x08,
+ 0x5833, 0x28,
+ 0x5834, 0x26,
+ 0x5835, 0x24,
+ 0x5836, 0x26,
+ 0x5837, 0x2A,
+ 0x5838, 0x44,
+ 0x5839, 0x4A,
+ 0x583A, 0x2C,
+ 0x583B, 0x2a,
+ 0x583C, 0x46,
+ 0x583D, 0xCE,
+#endif
+
+ 0x5800, 0x29,
+ 0x5801, 0x18,
+ 0x5802, 0x13,
+ 0x5803, 0x13,
+ 0x5804, 0x1A,
+ 0x5805, 0x28,
+ 0x5806, 0x0D,
+ 0x5807, 0x09,
+ 0x5808, 0x06,
+ 0x5809, 0x06,
+ 0x580a, 0x08,
+ 0x580b, 0x11,
+ 0x580c, 0x09,
+ 0x580d, 0x04,
+ 0x580e, 0x01,
+ 0x580f, 0x01,
+ 0x5810, 0x04,
+ 0x5811, 0x09,
+ 0x5812, 0x09,
+ 0x5813, 0x04,
+ 0x5814, 0x01,
+ 0x5815, 0x01,
+ 0x5816, 0x04,
+ 0x5817, 0x0A,
+ 0x5818, 0x0E,
+ 0x5819, 0x09,
+ 0x581a, 0x06,
+ 0x581b, 0x06,
+ 0x581c, 0x09,
+ 0x581d, 0x11,
+ 0x581e, 0x28,
+ 0x581f, 0x18,
+ 0x5820, 0x12,
+ 0x5821, 0x13,
+ 0x5822, 0x19,
+ 0x5823, 0x2C,
+ 0x5824, 0x3B,
+ 0x5825, 0x27,
+ 0x5826, 0x08,
+ 0x5827, 0x27,
+ 0x5828, 0x0E,
+ 0x5829, 0x17,
+ 0x582a, 0x25,
+ 0x582b, 0x23,
+ 0x582c, 0x15,
+ 0x582d, 0x06,
+ 0x582e, 0x08,
+ 0x582f, 0x42,
+ 0x5830, 0x40,
+ 0x5831, 0x32,
+ 0x5832, 0x07,
+ 0x5833, 0x27,
+ 0x5834, 0x25,
+ 0x5835, 0x24,
+ 0x5836, 0x25,
+ 0x5837, 0x05,
+ 0x5838, 0x29,
+ 0x5839, 0x2A,
+ 0x583a, 0x0A,
+ 0x583b, 0x18,
+ 0x583c, 0x4B,
+ 0x583d, 0xCE,
+ 0x5688, 0x11,//weight
+ 0x5689, 0x11,
+ 0x568a, 0x11,
+ 0x568b, 0x11,
+ 0x568c, 0x11,
+ 0x568d, 0x11,
+ 0x568e, 0x11,
+ 0x568f, 0x11,
+
+ 0x5025, 0x00,
+
+ 0x3a0f, 0x30,
+ 0x3a10, 0x28,
+ 0x3a1b, 0x30,
+ 0x3a1e, 0x28,
+ 0x3a11, 0x61,
+ 0x3a1f, 0x10,
+
+ 0x4005, 0x1a,
+
+ 0x3503, 0x00,
+ 0x3008, 0x02,
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/ov5640/ov5640.c b/drivers/media/video/wmt_v4l2/sensors/ov5640/ov5640.c
new file mode 100755
index 00000000..30863151
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/ov5640/ov5640.c
@@ -0,0 +1,572 @@
+/*
+ * Driver for ov5640 CMOS Image Sensor from Omnivision
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/gpio.h>
+#include <asm/errno.h>
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "ov5640.h"
+
+#define sensor_write_array(sd, arr, sz) \
+ cmos_init_16bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read16addr(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write16addr(sd->i2c_addr, reg, val)
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint32_t *regs;
+ size_t size;
+};
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+struct ov5640_af {
+ int pos_x;
+ int pos_y;
+};
+
+static struct ov5640_af ov5640_af;
+static int sensor_fps;
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+ CMOS_WIN_SIZE("QVGA", 320, 240, ov5640_320_240_regs),
+ CMOS_WIN_SIZE("VGA", 640, 480, ov5640_640_480_regs),
+ CMOS_WIN_SIZE("SVGA", 800, 600, ov5640_800_600_regs),
+ CMOS_WIN_SIZE("720p", 1280, 720, ov5640_1280_720_regs),
+ CMOS_WIN_SIZE("200w", 1600, 1200, ov5640_1600_1200_regs),
+ CMOS_WIN_SIZE("300w", 2048, 1536, ov5640_2048_1536_regs),
+ CMOS_WIN_SIZE("QSXGA", 2592, 1944, ov5640_2592_1944_regs),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint32_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = ov5640_wb_auto;
+ size = ARRAY_SIZE(ov5640_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = ov5640_wb_incandescent;
+ size = ARRAY_SIZE(ov5640_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = ov5640_wb_daylight;
+ size = ARRAY_SIZE(ov5640_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = ov5640_wb_cloudy;
+ size = ARRAY_SIZE(ov5640_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = ov5640_wb_fluorescent;
+ size = ARRAY_SIZE(ov5640_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_scenemode(struct cmos_subdev *sd, enum v4l2_scene_mode value)
+{
+ uint32_t *regs;
+ size_t size;
+
+ switch (value) {
+ case SCENE_MODE_AUTO:
+ regs = ov5640_scene_mode_auto;
+ size = ARRAY_SIZE(ov5640_scene_mode_auto);
+ break;
+ case SCENE_MODE_NIGHTSHOT:
+ regs = ov5640_scene_mode_night;
+ size = ARRAY_SIZE(ov5640_scene_mode_night);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_autoexposure(struct cmos_subdev *sd, int value)
+{
+ switch (value) {
+ case 0:
+ break;
+ case 1:
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int sensor_s_exposure(struct cmos_subdev *sd, enum v4l2_exposure_mode value)
+{
+ uint32_t *regs;
+ size_t size;
+
+ switch (value) {
+ case -2:
+ regs = ov5640_exposure_neg6;
+ size = ARRAY_SIZE(ov5640_exposure_neg6);
+ break;
+ case -1:
+ regs = ov5640_exposure_neg3;
+ size = ARRAY_SIZE(ov5640_exposure_neg3);
+ break;
+ case 0:
+ regs = ov5640_exposure_zero;
+ size = ARRAY_SIZE(ov5640_exposure_zero);
+ break;
+ case 1:
+ regs = ov5640_exposure_pos3;
+ size = ARRAY_SIZE(ov5640_exposure_pos3);
+ break;
+ case 2:
+ regs = ov5640_exposure_pos6;
+ size = ARRAY_SIZE(ov5640_exposure_pos6);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+ uint32_t data = sensor_read(sd, 0x3821);
+
+ switch (value) {
+ case 0:
+ data &= ~0x06;
+ break;
+ case 1:
+ data |= 0x06;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sensor_write(sd, 0x3821, data);
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+ uint32_t data = sensor_read(sd, 0x3820);
+
+ switch (value) {
+ case 0:
+ data &= ~0x06;
+ break;
+ case 1:
+ data |= 0x06;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sensor_write(sd, 0x3820, data);
+}
+
+static inline int sensor_s_af_area_x(struct cmos_subdev *sd, int value)
+{
+ ov5640_af.pos_x = value;
+ return 0;
+}
+
+static inline int sensor_s_af_area_y(struct cmos_subdev *sd, int value)
+{
+ ov5640_af.pos_y = value;
+ return 0;
+}
+
+// auto focus
+static int sensor_s_af(struct cmos_subdev *sd, int value)
+{
+ printk(KERN_DEBUG "%s, AF mode %d, (%d,%d)\n", sd->name, value, ov5640_af.pos_x, ov5640_af.pos_y);
+ switch (value) {
+ case FOCUS_MODE_AUTO:
+ sensor_write(sd, 0x3024, ov5640_af.pos_x/8);
+ sensor_write(sd, 0x3025, ov5640_af.pos_y/8);
+ sensor_write(sd, 0x3026, 80);
+ sensor_write(sd, 0x3027, 60);
+ sensor_write(sd, 0x3023, 0x01);
+ sensor_write(sd, 0x3022, 0x81);
+ msleep(10);
+ return sensor_write(sd, 0x3022, 0x03); // Single AF
+ case FOCUS_MODE_CONTINUOUS:
+ return sensor_write(sd, 0x3022, 0x04); // Continous AF
+ case FOCUS_MODE_FIXED:
+ return sensor_write(sd, 0x3022, 0x06); // Stop AF
+ case FOCUS_MODE_INFINITY:
+ return sensor_write(sd, 0x3022, 0x08); // Release AF
+ case FOCUS_MODE_CONTINUOUS_VIDEO:
+ return sensor_write(sd, 0x3022, 0x08); // Release AF
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int sensor_g_af(struct cmos_subdev *sd, int *value)
+{
+ int state;
+
+ state = sensor_read(sd, 0x3029);
+ switch (state) {
+ case 0x00: /* focusing */
+ *value = FOCUS_RESULT_FOCUSING;
+ break;
+ case 0x10:
+ case 0x20:
+ *value = FOCUS_RESULT_SUCCEED;
+ break;
+ case 0x70: /* focused, moto released */
+ default:
+ *value = FOCUS_RESULT_FAILED;
+ break;
+ }
+ return 0;
+}
+
+// shutter
+static int sensor_s_shutter(struct cmos_subdev *sd)
+{
+ uint32_t temp;
+ uint16_t temp1;
+ uint16_t temp2;
+ uint16_t temp3;
+
+ sensor_write(sd, 0x3503,0x07); // 0x03 fix AE
+ sensor_write(sd, 0x3a00,0x38); // disable night
+
+ temp1 = sensor_read(sd, 0x3500);
+ temp2 = sensor_read(sd, 0x3501);
+ temp3 = sensor_read(sd, 0x3502);
+
+ pr_debug("%s: 0x%x, 0x%x, 0x%x\n", __func__, temp1, temp2, temp3);
+
+ temp = ((temp1<<12) | (temp2<<4) | (temp3>>4));
+ temp = temp / 2;//这个系数根æ®preview modeå’Œcapture modeçš„frame rate进行修改
+ temp = temp * 16;
+
+ temp1 = (temp & 0x0000ff);
+ temp2 = (temp & 0x00ff00) >> 8;
+ temp3 = (temp & 0xff0000) >> 16;
+
+ sensor_write(sd, 0x3500,temp3);
+ sensor_write(sd, 0x3501,temp2);
+ sensor_write(sd, 0x3502,temp1);
+
+ return 0;
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ case V4L2_CID_EXPOSURE_AUTO:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_EXPOSURE:
+ return v4l2_ctrl_query_fill(qc, -2, 2, 1, 0);
+ case V4L2_CID_FOCUS_AUTO:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 0, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_flash(struct cmos_subdev *sd, int *value)
+{
+ int environmental, critical;
+
+ environmental = sensor_read(sd, 0x350b);
+ critical = sensor_read(sd, 0x3a19);
+
+ printk(KERN_DEBUG "%s environmental 0x%x,criticali 0x%x\n",
+ __func__, environmental,critical);
+
+ *value = (environmental >= critical);
+ return 0;
+}
+
+static int sensor_g_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_AUTO_FOCUS_RESULT:
+ return sensor_g_af(sd, &ctrl->value);
+ case V4L2_CID_CAMERA_FLASH_MODE_AUTO:
+ return sensor_g_flash(sd, &ctrl->value);
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return sensor_s_scenemode(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE_AUTO:
+ return sensor_s_autoexposure(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return sensor_s_exposure(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ case V4L2_CID_CAMERA_FOCUS_MODE:
+ return sensor_s_af(sd, ctrl->value);
+ case V4L2_CID_CAMERA_FOCUS_POSITION_X:
+ return sensor_s_af_area_x(sd, ctrl->value);
+ case V4L2_CID_CAMERA_FOCUS_POSITION_Y:
+ return sensor_s_af_area_y(sd, ctrl->value);
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int sensor_set_fps(struct cmos_subdev *sd, int fps)
+{
+ uint32_t array[]={
+ 0x380c,0x07,
+ 0x380d,0x64,
+ 0x380e,0x02,
+ 0x380f,0xe4,
+ };
+
+ if(fps == 0)
+ return 0;
+ switch(fps){
+ case 26 ... 30:
+ break;
+ case 21 ... 25:
+ array[5] = 0x03;array[7] = 0x85;
+ break;
+ case 16 ... 20:
+ array[5] = 0x04;array[7] = 0x60;
+ break;
+ case 11 ... 15:
+ break;
+ case 5 ... 10:
+ array[5] = 0x04;array[7] = 0x75;
+ break;
+ }
+ sensor_write_array(sd, array, ARRAY_SIZE(array));
+ msleep(50);
+ sensor_fps =0;
+ return 0;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ if (mf->width >= 1024) {
+ sensor_s_shutter(sd);
+ }
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win) {
+ pr_err("%s, s_mbus_fmt failed, width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+ return -EINVAL;
+ }
+ if((mf->width ==1280)&&(mf->height == 720)){
+ if(sensor_fps != 0){
+ if(sensor_fps >15)
+ sensor_write(sd, 0x3035,0x21);
+ else
+ sensor_write(sd, 0x3035,0x41);
+ msleep(60);
+ }else{
+ sensor_write(sd, 0x3035,0x21);
+ }
+ }
+ sensor_write_array(sd, win->regs, win->size);
+ if((mf->width ==1280)&&(mf->height == 720)){
+ sensor_set_fps(sd,sensor_fps);
+ }
+ msleep(200);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return 0;
+}
+
+static int sensor_enum_framesizes(struct cmos_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ const struct cmos_win_size *win = &cmos_supported_win_sizes[index];
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sensor_g_parm(struct cmos_subdev *sd, struct v4l2_streamparm *parms)
+{
+ return 0;
+}
+
+static int sensor_s_parm(struct cmos_subdev *sd, struct v4l2_streamparm *parms)
+{
+ struct v4l2_captureparm *cp = &parms->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+
+ if (tpf->numerator == 0 || tpf->denominator == 0) {
+ return 0;
+ }
+
+ sensor_fps = tpf->denominator;//cause tpf->numerator == 1 in HAL
+
+
+ return 0;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ uint32_t data = 0;
+
+ data |= (sensor_read(sd, 0x300a) & 0xff) << 8;
+ data |= (sensor_read(sd, 0x300b) & 0xff);
+
+ return (data == sd->id) ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ if (sensor_identify(sd))
+ return -1;
+ sensor_fps = 0;
+ sensor_write_array(sd, ov5640_firmware, ARRAY_SIZE(ov5640_firmware));
+ msleep(10);
+
+ sensor_write(sd, 0x3103, 0x11);
+ sensor_write(sd, 0x3008, 0x82);
+ msleep(5);
+ sensor_write_array(sd, ov5640_default_regs_init,
+ ARRAY_SIZE(ov5640_default_regs_init));
+
+ msleep(50);
+ sensor_write(sd, 0x3024, 320/8);
+ sensor_write(sd, 0x3025, 240/8);
+ sensor_write(sd, 0x3026, 80);
+ sensor_write(sd, 0x3027, 60);
+ sensor_s_af(sd, FOCUS_MODE_CONTINUOUS);
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ sensor_write(sd, 0x3017, 0x80);
+ sensor_write(sd, 0x3018, 0x03);
+ return 0;
+}
+
+static struct cmos_subdev_ops ov5640_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .g_ctrl = sensor_g_ctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+ .g_parm = sensor_g_parm,
+ .s_parm = sensor_s_parm,
+ .enum_framesizes = sensor_enum_framesizes,
+};
+
+struct cmos_subdev ov5640 = {
+ .name = "ov5640",
+ .i2c_addr = 0x3c,
+ .id = 0x5640,
+ .max_width = 2592,
+ .max_height = 1944,
+ .ops = &ov5640_ops,
+};
+
+#if 0
+static int __init ov5640_init(void)
+{
+ return cmos_register_sudbdev(&ov5640);
+}
+
+static void __exit ov5640_exit(void)
+{
+ cmos_unregister_subdev(&ov5640);
+ return;
+}
+
+module_init(ov5640_init);
+module_exit(ov5640_exit);
+
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/ov5640/ov5640.h b/drivers/media/video/wmt_v4l2/sensors/ov5640/ov5640.h
new file mode 100755
index 00000000..9c57d1dc
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/ov5640/ov5640.h
@@ -0,0 +1,1296 @@
+#ifndef OV5640_H
+#define OV5640_H
+
+#include "ov5640_firmware.h"
+
+
+// Scene Mode
+uint32_t ov5640_scene_mode_auto[] = {
+ 0x3a00, 0x3c,
+ 0x3a02, 0x0b,
+ 0x3a03, 0x88,
+ 0x3a14, 0x0b,
+ 0x3a15, 0x88,
+};
+
+uint32_t ov5640_scene_mode_night[] = {
+ 0x3a00, 0x3c,
+ 0x3a02, 0x17,
+ 0x3a03, 0x10,
+ 0x3a14, 0x17,
+ 0x3a15, 0x10,
+};
+
+
+// White Balance
+uint32_t ov5640_wb_auto [] = {
+ 0x3406, 0x00,
+};
+
+uint32_t ov5640_wb_incandescent [] = {
+ 0x3212, 0x03,
+ 0x3406, 0x01,
+ 0x3400, 0x05,
+ 0x3401, 0x48,
+ 0x3402, 0x04,
+ 0x3403, 0x00,
+ 0x3404, 0x07,
+ 0x3405, 0xcf,
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_wb_fluorescent [] = {
+ 0x3212, 0x03,
+ 0x3406, 0x01,
+ 0x3400, 0x05,
+ 0x3401, 0x8c,
+ 0x3402, 0x04,
+ 0x3403, 0x00,
+ 0x3404, 0x06,
+ 0x3405, 0xe8,
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_wb_daylight [] = {
+ 0x3212, 0x03,
+ 0x3406, 0x01,
+ 0x3400, 0x06,
+ 0x3401, 0x1c,
+ 0x3402, 0x04,
+ 0x3403, 0x00,
+ 0x3404, 0x04,
+ 0x3405, 0xf3,
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_wb_cloudy [] = {
+ 0x3212, 0x03,
+ 0x3406, 0x01,
+ 0x3400, 0x06,
+ 0x3401, 0x48,
+ 0x3402, 0x04,
+ 0x3403, 0x00,
+ 0x3404, 0x04,
+ 0x3405, 0xd3,
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_wb_tungsten [] = {
+ 0x3212, 0x03,
+ 0x3406, 0x01,
+ 0x3400, 0x04,
+ 0x3401, 0x10,
+ 0x3402, 0x04,
+ 0x3403, 0x00,
+ 0x3404, 0x08,
+ 0x3405, 0x40,
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+
+// Exposure
+uint32_t ov5640_exposure_neg6[] = {
+ 0x3a0f, 0x20,
+ 0x3a10, 0x18,
+ 0x3a11, 0x41,
+ 0x3a1b, 0x20,
+ 0x3a1e, 0x18,
+ 0x3a1f, 0x10,
+};
+
+uint32_t ov5640_exposure_neg3[] = {
+ 0x3a0f, 0x28,
+ 0x3a10, 0x20,
+ 0x3a11, 0x51,
+ 0x3a1b, 0x28,
+ 0x3a1e, 0x20,
+ 0x3a1f, 0x10,
+};
+
+uint32_t ov5640_exposure_zero[] = {
+ 0x3a0f, 0x30,
+ 0x3a10, 0x28,
+ 0x3a11, 0x61,
+ 0x3a1b, 0x30,
+ 0x3a1e, 0x28,
+ 0x3a1f, 0x10,
+};
+
+uint32_t ov5640_exposure_pos3[] = {
+ 0x3a0f, 0x40,
+ 0x3a10, 0x38,
+ 0x3a11, 0x71,
+ 0x3a1b, 0x40,
+ 0x3a1e, 0x38,
+ 0x3a1f, 0x10,
+};
+
+uint32_t ov5640_exposure_pos6[] = {
+ 0x3a0f, 0x50,
+ 0x3a10, 0x48,
+ 0x3a11, 0x90,
+ 0x3a1b, 0x50,
+ 0x3a1e, 0x48,
+ 0x3a1f, 0x20,
+};
+
+/*
+ * The color effect settings
+ */
+uint32_t ov5640_colorfx_none[] = {
+ 0x3212, 0x03,
+ 0x5580, 0x06,
+ 0x5583, 0x40,
+ 0x5584, 0x10,
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_colorfx_bw[] = {
+ 0x3212, 0x03,
+ 0x5580, 0x1e,
+ 0x5583, 0x80,
+ 0x5584, 0x80,
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_colorfx_sepia[] = {
+ 0x3212, 0x03,
+ 0x5580, 0x1e,
+ 0x5583, 0x40,
+ 0x5584, 0xa0,
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_colorfx_negative[] = {
+ 0x3212, 0x03,
+ 0x5580, 0x46,
+ 0x5583, 0x40,
+ 0x5584, 0x10,
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_colorfx_emboss[] = {
+};
+
+uint32_t ov5640_colorfx_sketch[] = {
+};
+
+uint32_t ov5640_colorfx_sky_blue[] = {
+};
+
+uint32_t ov5640_colorfx_grass_green[] = {
+};
+
+uint32_t ov5640_colorfx_skin_whiten[] = {
+};
+
+uint32_t ov5640_colorfx_vivid[] = {
+};
+
+// Brightness
+uint32_t ov5640_brightness_neg4[] = {
+ 0x3212, 0x03,
+
+ 0x5587, 0x40,
+ 0x5588, 0x09,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_brightness_neg3[] = {
+ 0x3212, 0x03,
+
+ 0x5587, 0x30,
+ 0x5588, 0x09,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_brightness_neg2[] = {
+ 0x3212, 0x03,
+
+ 0x5587, 0x20,
+ 0x5588, 0x09,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_brightness_neg1[] = {
+ 0x3212, 0x03,
+
+ 0x5587, 0x10,
+ 0x5588, 0x09,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_brightness_zero[] = {
+ 0x3212, 0x03,
+
+ 0x5587, 0x00,
+ 0x5588, 0x01,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+
+};
+
+uint32_t ov5640_brightness_pos1[] = {
+ 0x3212, 0x03,
+
+ 0x5587, 0x10,
+ 0x5588, 0x01,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_brightness_pos2[] = {
+ 0x3212, 0x03,
+
+ 0x5587, 0x20,
+ 0x5588, 0x01,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_brightness_pos3[] = {
+ 0x3212, 0x03,
+
+ 0x5587, 0x30,
+ 0x5588, 0x01,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_brightness_pos4[] = {
+ 0x3212, 0x03,
+
+ 0x5587, 0x40,
+ 0x5588, 0x01,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+// Contrast
+uint32_t ov5640_contrast_neg4[] = {
+ 0x3212, 0x03,
+
+ 0x5586, 0x10,
+ 0x5585, 0x10,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_contrast_neg3[] = {
+ 0x3212, 0x03,
+
+ 0x5586, 0x14,
+ 0x5585, 0x14,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_contrast_neg2[] = {
+ 0x3212, 0x03,
+
+ 0x5586, 0x18,
+ 0x5585, 0x18,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_contrast_neg1[] = {
+ 0x3212, 0x03,
+
+ 0x5586, 0x1c,
+ 0x5585, 0x1c,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_contrast_zero[] = {
+ 0x3212, 0x03,
+
+ 0x5586, 0x20,
+ 0x5585, 0x00,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_contrast_pos1[] = {
+ 0x3212, 0x03,
+
+ 0x5586, 0x24,
+ 0x5585, 0x10,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_contrast_pos2[] = {
+ 0x3212, 0x03,
+
+ 0x5586, 0x28,
+ 0x5585, 0x18,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_contrast_pos3[] = {
+ 0x3212, 0x03,
+
+ 0x5586, 0x2c,
+ 0x5585, 0x1c,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+uint32_t ov5640_contrast_pos4[] = {
+ 0x3212, 0x03,
+
+ 0x5586, 0x30,
+ 0x5585, 0x20,
+
+ 0x3212, 0x13,
+ 0x3212, 0xa3,
+};
+
+// Saturation
+uint32_t ov5640_saturation_neg4[] = {
+ 0x5583, 0x20,
+};
+
+uint32_t ov5640_saturation_neg3[] = {
+ 0x5583, 0x28,
+};
+
+uint32_t ov5640_saturation_neg2[] = {
+ 0x5583, 0x30,
+};
+
+uint32_t ov5640_saturation_neg1[] = {
+ 0x5583, 0x38,
+};
+
+uint32_t ov5640_saturation_zero[] = {
+ 0x5583, 0x40,
+};
+
+uint32_t ov5640_saturation_pos1[] = {
+ 0x5583, 0x48,
+};
+
+uint32_t ov5640_saturation_pos2[] = {
+ 0x5583, 0x50,
+};
+
+uint32_t ov5640_saturation_pos3[] = {
+ 0x5583, 0x58,
+};
+
+uint32_t ov5640_saturation_pos4[] = {
+ 0x5583, 0x60,
+};
+
+
+uint32_t ov5640_2592_1944_regs[] = {
+ 0x4202, 0x0f,
+
+ 0x3c07, 0x07,
+ 0x5189, 0x66,
+
+ 0x5302, 0x1c,
+ 0x5303, 0x08,
+ 0x5306, 0x08,
+ 0x5307, 0x18,
+
+ 0x3503, 0x07,
+ 0x3a00, 0x38,
+
+ 0x3820, 0x41,
+ 0x3821, 0x07,
+
+ 0x3800, 0x00,
+ 0x3801, 0x00,
+ 0x3802, 0x00,
+ 0x3803, 0x00,
+ 0x3804, 0x0a,
+ 0x3805, 0x3f,
+ 0x3806, 0x07,
+ 0x3807, 0x9f,
+
+ /* Width * Height 2592x1944 */
+ 0x3808, 0x0a,
+ 0x3809, 0x20,
+ 0x380a, 0x07,
+ 0x380b, 0x98,
+
+ 0x3813, 0x04,
+ 0x3814, 0x11,
+ 0x3815, 0x11,
+
+ 0x3034, 0x1a,
+ 0x3035, 0x21,
+ 0x3036, 0x69,
+ 0x3037, 0x13,
+
+ 0x380c, 0x0b,
+ 0x380d, 0x1c,
+ 0x380e, 0x09,
+ 0x380f, 0x3a,
+
+ 0x3a08, 0x01,
+ 0x3a09, 0x27,
+ 0x3a0e, 0x08,
+ 0x3a0a, 0x00,
+ 0x3a0b, 0xf6,
+ 0x3a0d, 0x09,
+
+ 0x3618, 0x04,
+ 0x3612, 0x2b,
+ 0x3709, 0x12,
+ 0x370c, 0x00,
+
+ 0x4004, 0x06,
+ 0x3002, 0x1c,
+ 0x3006, 0xc3,
+ 0x3824, 0x01,
+ 0x5001, 0x83,
+
+ 0x4713, 0x02,
+ 0x4407, 0x0c,
+ 0x460b, 0x37,
+ 0x460c, 0x20,
+
+ 0x4202, 0x00,
+};
+
+uint32_t ov5640_2048_1536_regs[] = {
+ 0x4202, 0x0f,
+
+ 0x3c07, 0x07,
+ 0x5189, 0x66,
+
+ 0x5302, 0x1c,
+ 0x5303, 0x08,
+ 0x5306, 0x08,
+ 0x5307, 0x18,
+
+ 0x3503, 0x07,
+ 0x3a00, 0x38,
+
+ 0x3820, 0x41,
+ 0x3821, 0x07,
+
+ 0x3800, 0x00,
+ 0x3801, 0x00,
+ 0x3802, 0x00,
+ 0x3803, 0x00,
+ 0x3804, 0x0a,
+ 0x3805, 0x3f,
+ 0x3806, 0x07,
+ 0x3807, 0x9f,
+
+ /* Width * Height 2048x1536 */
+ 0x3808, 0x08,
+ 0x3809, 0x00,
+ 0x380a, 0x06,
+ 0x380b, 0x00,
+
+ 0x3813, 0x04,
+ 0x3814, 0x11,
+ 0x3815, 0x11,
+
+ 0x3034, 0x1a,
+ 0x3035, 0x21,
+ 0x3036, 0x69,
+ 0x3037, 0x13,
+
+ 0x380c, 0x0b,
+ 0x380d, 0x1c,
+ 0x380e, 0x09,
+ 0x380f, 0x3a,
+
+ 0x3a08, 0x01,
+ 0x3a09, 0x27,
+ 0x3a0e, 0x08,
+ 0x3a0a, 0x00,
+ 0x3a0b, 0xf6,
+ 0x3a0d, 0x09,
+
+ 0x3618, 0x04,
+ 0x3612, 0x2b,
+ 0x3709, 0x12,
+ 0x370c, 0x00,
+
+ 0x4004, 0x06,
+ 0x3002, 0x1c,
+ 0x3006, 0xc3,
+ 0x3824, 0x01,
+ 0x5001, 0xa3,
+
+ 0x4713, 0x02,
+ 0x4407, 0x0c,
+ 0x460b, 0x37,
+ 0x460c, 0x20,
+
+ 0x4202, 0x00,
+};
+
+uint32_t ov5640_1600_1200_regs[] = {
+ 0x4202, 0x0f,
+
+ 0x3c07, 0x07,
+ 0x5189, 0x66,
+
+ 0x5302, 0x1c,
+ 0x5303, 0x08,
+ 0x5306, 0x08,
+ 0x5307, 0x18,
+
+ 0x3503, 0x07,
+ 0x3a00, 0x38,
+
+ 0x3820, 0x41,
+ 0x3821, 0x07,
+
+ 0x3800, 0x00,
+ 0x3801, 0x00,
+ 0x3802, 0x00,
+ 0x3803, 0x00,
+ 0x3804, 0x0a,
+ 0x3805, 0x3f,
+ 0x3806, 0x07,
+ 0x3807, 0x9f,
+
+ /* Width * Height 1600x1200 */
+ 0x3808, 0x06,
+ 0x3809, 0x40,
+ 0x380a, 0x04,
+ 0x380b, 0xb0,
+
+ 0x3813, 0x04,
+ 0x3814, 0x11,
+ 0x3815, 0x11,
+
+ 0x3034, 0x1a,
+ 0x3035, 0x21,
+ 0x3036, 0x69,
+ 0x3037, 0x13,
+
+ 0x380c, 0x0b,
+ 0x380d, 0x1c,
+ 0x380e, 0x09,
+ 0x380f, 0x3a,
+
+ 0x3a08, 0x01,
+ 0x3a09, 0x27,
+ 0x3a0e, 0x08,
+ 0x3a0a, 0x00,
+ 0x3a0b, 0xf6,
+ 0x3a0d, 0x09,
+
+ 0x3618, 0x04,
+ 0x3612, 0x2b,
+ 0x3709, 0x12,
+ 0x370c, 0x00,
+
+ 0x4004, 0x06,
+ 0x3002, 0x1c,
+ 0x3006, 0xc3,
+ 0x3824, 0x01,
+ 0x5001, 0xa3,
+
+ 0x4713, 0x02,
+ 0x4407, 0x0c,
+ 0x460b, 0x37,
+ 0x460c, 0x20,
+
+ 0x4202, 0x00,
+};
+
+uint32_t ov5640_1280_720_regs[] = {
+ 0x3008,0x42,
+ 0x3034,0x1a,
+ //0x3035,0x21,
+ 0x3036,0x69,
+ 0x3037,0x13,
+ 0x3108,0x01,
+ 0x3824,0x01,
+ 0xffff,0x32,
+ 0x3808,0x05,
+ 0x3809,0x00,
+ 0x380a,0x02,
+ 0x380b,0xd0,
+ 0x380c,0x07,
+ 0x380d,0x64,
+ 0x380e,0x02,
+ 0x380f,0xe4,
+
+ 0x3a08,0x00,
+ 0x3a09,0xdd,
+ 0x3a0a,0x00,
+ 0x3a0b,0xb8,
+ 0x3a0e,0x03,
+ 0x3a0d,0x04,
+
+ 0x3c07,0x07,
+ 0x3814,0x31,
+ 0x3815,0x31,
+ 0x3800,0x00,
+ 0x3801,0x00,
+ 0x3802,0x00,
+ 0x3803,0xfa,
+ 0x3804,0x0a,
+ 0x3805,0x3f,
+ 0x3806,0x06,
+ 0x3807,0xa9,
+ 0x3810,0x00,
+ 0x3811,0x10,
+ 0x3812,0x00,
+ 0x3813,0x04,
+
+ 0x5308,0x65,
+ 0x5302,0x00,
+ 0x4002,0x45,
+ 0x4005,0x18,
+
+ 0x3618,0x00,
+ 0x3612,0x29,
+ 0x3709,0x52,
+ 0x370c,0x03,
+ 0x3a02,0x02,
+ 0x3a03,0xe0,
+ 0x3a14,0x02,
+ 0x3a15,0xe0,
+
+ 0x4004,0x02,
+ 0x3002,0x1c,
+ 0x3006,0xc3,
+ 0x460b,0x37,
+ 0x460c,0x20,
+ 0x4837,0x16,
+ 0x5001,0x83,
+ 0x3503,0x00,
+
+ 0x302c,0xc2,
+ 0x3008,0x02,
+ 0xffff,0x32,
+};
+
+uint32_t ov5640_800_600_regs[] = {
+ 0x4202, 0x0f,
+
+ 0x3c07, 0x08,
+ 0x5189, 0x72,
+
+ 0x3503, 0x00,
+ 0x3a00, 0x3c,
+
+ 0x5302, 0x38,
+ 0x5303, 0x18,
+ 0x5306, 0x06,
+ 0x5307, 0x16,
+
+ 0x3820, 0x41,
+ 0x3821, 0x07,
+
+ 0x3800, 0x00,
+ 0x3801, 0x00,
+ 0x3802, 0x00,
+ 0x3803, 0x04,
+ 0x3804, 0x0a,
+ 0x3805, 0x3f,
+ 0x3806, 0x07,
+ 0x3807, 0x9b,
+
+ /* Width * Height 800x600 */
+ 0x3808, 0x03,
+ 0x3809, 0x20,
+ 0x380a, 0x02,
+ 0x380b, 0x58,
+
+ 0x3810, 0x00,
+ 0x3811, 0x10,
+ 0x3812, 0x00,
+ 0x3813, 0x06,
+ 0x3814, 0x31,
+ 0x3815, 0x31,
+
+ 0x3034, 0x1a,
+ 0x3035, 0x11,
+ 0x3036, 0x46,
+ 0x3037, 0x13,
+
+ 0x380c, 0x07,
+ 0x380d, 0x68,
+ 0x380e, 0x03,
+ 0x380f, 0xd8,
+
+ 0x3a08, 0x01,
+ 0x3a09, 0x27,
+ 0x3a0e, 0x03,
+ 0x3a0a, 0x00,
+ 0x3a0b, 0xf6,
+ 0x3a0d, 0x04,
+
+ 0x3618, 0x00,
+ 0x3612, 0x29,
+ 0x3709, 0x52,
+ 0x370c, 0x03,
+
+ 0x4004, 0x02,
+ 0x3002, 0x1c,
+ 0x3006, 0xc3,
+ 0x3824, 0x02,
+ 0x5001, 0xa3,
+
+ 0x4713, 0x03,
+ 0x4407, 0x04,
+ 0x460b, 0x35,
+ 0x460c, 0x22,
+ 0x4837, 0x22,
+
+ 0x4202, 0x00,
+
+// 0x3023, 0x01,
+// 0x3022, 0x04,
+};
+
+uint32_t ov5640_320_240_regs[] = {
+ 0x4202, 0x0f,
+
+ 0x3c07, 0x08,
+ 0x5189, 0x72,
+
+ 0x3503, 0x00,
+ 0x3a00, 0x3c,
+
+ 0x5302, 0x38,
+ 0x5303, 0x18,
+ 0x5306, 0x06,
+ 0x5307, 0x16,
+
+ 0x3820, 0x41,
+ 0x3821, 0x07,
+
+ 0x3800, 0x00,
+ 0x3801, 0x00,
+ 0x3802, 0x00,
+ 0x3803, 0x04,
+ 0x3804, 0x0a,
+ 0x3805, 0x3f,
+ 0x3806, 0x07,
+ 0x3807, 0x9b,
+
+ //320x240
+ 0x3808, 0x01,
+ 0x3809, 0x40,
+ 0x380a, 0x00,
+ 0x380b, 0xf0,
+
+ 0x3810, 0x00,
+ 0x3811, 0x10,
+ 0x3812, 0x00,
+ 0x3813, 0x06,
+ 0x3814, 0x31,
+ 0x3815, 0x31,
+
+ 0x3034, 0x1a,
+ 0x3035, 0x11,
+ 0x3036, 0x46,
+ 0x3037, 0x13,
+
+ 0x380c, 0x07,
+ 0x380d, 0x68,
+ 0x380e, 0x03,
+ 0x380f, 0xd8,
+
+ 0x3a08, 0x01,
+ 0x3a09, 0x27,
+ 0x3a0e, 0x03,
+ 0x3a0a, 0x00,
+ 0x3a0b, 0xf6,
+ 0x3a0d, 0x04,
+
+ 0x3618, 0x00,
+ 0x3612, 0x29,
+ 0x3709, 0x52,
+ 0x370c, 0x03,
+
+ 0x4004, 0x02,
+ 0x3002, 0x1c,
+ 0x3006, 0xc3,
+ 0x3824, 0x02,
+ 0x5001, 0xa3,
+
+ 0x4713, 0x03,
+ 0x4407, 0x04,
+ 0x460b, 0x35,
+ 0x460c, 0x22,
+ 0x4837, 0x22,
+
+ 0x4202, 0x00,
+
+// 0x3023, 0x01,
+// 0x3022, 0x04,
+};
+
+uint32_t ov5640_640_480_regs[] = {
+ 0x4202, 0x0f,
+
+ 0x3c07, 0x08,
+ 0x5189, 0x72,
+
+ 0x3503, 0x00,
+ 0x3a00, 0x3c,
+
+ 0x5302, 0x38,
+ 0x5303, 0x18,
+ 0x5306, 0x06,
+ 0x5307, 0x16,
+
+ 0x3820, 0x41,
+ 0x3821, 0x07,
+
+ 0x3800, 0x00,
+ 0x3801, 0x00,
+ 0x3802, 0x00,
+ 0x3803, 0x04,
+ 0x3804, 0x0a,
+ 0x3805, 0x3f,
+ 0x3806, 0x07,
+ 0x3807, 0x9b,
+
+ 0x3808, 0x02,
+ 0x3809, 0x80,
+ 0x380a, 0x01,
+ 0x380b, 0xe0,
+
+ 0x3810, 0x00,
+ 0x3811, 0x10,
+ 0x3812, 0x00,
+ 0x3813, 0x06,
+ 0x3814, 0x31,
+ 0x3815, 0x31,
+
+ 0x3034, 0x1a,
+ 0x3035, 0x11,
+ 0x3036, 0x46,
+ 0x3037, 0x13,
+
+ 0x380c, 0x07,
+ 0x380d, 0x68,
+ 0x380e, 0x03,
+ 0x380f, 0xd8,
+
+ 0x3a08, 0x01,
+ 0x3a09, 0x27,
+ 0x3a0e, 0x03,
+ 0x3a0a, 0x00,
+ 0x3a0b, 0xf6,
+ 0x3a0d, 0x04,
+
+ 0x3618, 0x00,
+ 0x3612, 0x29,
+ 0x3709, 0x52,
+ 0x370c, 0x03,
+
+ 0x4004, 0x02,
+ 0x3002, 0x1c,
+ 0x3006, 0xc3,
+ 0x3824, 0x02,
+ 0x5001, 0xa3,
+
+ 0x4713, 0x03,
+ 0x4407, 0x04,
+ 0x460b, 0x35,
+ 0x460c, 0x22,
+ 0x4837, 0x22,
+
+ 0x4202, 0x00,
+
+// 0x3023, 0x01,
+// 0x3022, 0x04,
+};
+
+uint32_t ov5640_default_regs_init[] = {
+ 0x3008, 0x42,
+ 0x3103, 0x03,
+ 0x3017, 0x7f,
+ 0x3018, 0xff,
+
+ 0x3031, 0x00,
+ 0x302c, 0xc2,
+
+ 0x3108, 0x01,
+ 0x3630, 0x36,
+ 0x3631, 0x0e,
+ 0x3632, 0xe2,
+ 0x3633, 0x12,
+ 0x3621, 0xe0,
+ 0x3704, 0xa0,
+ 0x3703, 0x5a,
+ 0x3715, 0x78,
+ 0x3717, 0x01,
+ 0x370b, 0x60,
+ 0x3705, 0x1a,
+ 0x3905, 0x02,
+ 0x3906, 0x10,
+ 0x3901, 0x0a,
+ 0x3731, 0x12,
+ 0x3600, 0x08,
+ 0x3601, 0x33,
+ 0x302d, 0x60,
+ 0x3620, 0x52,
+ 0x371b, 0x20,
+ 0x471c, 0x50,
+
+ 0x3a13, 0x43,
+ 0x3a18, 0x00,
+ 0x3a19, 0xf8,
+
+ 0x3635, 0x13,
+ 0x3636, 0x03,
+ 0x3634, 0x40,
+ 0x3622, 0x01,
+
+ 0x3c04, 0x28,
+ 0x3c05, 0x98,
+ 0x3c06, 0x00,
+ 0x3c07, 0x08,
+ 0x3c08, 0x00,
+ 0x3c09, 0x1c,
+ 0x3c0a, 0x9c,
+ 0x3c0b, 0x40,
+
+ 0x3820, 0x41,
+ 0x3821, 0x07,
+
+ 0x3800, 0x00,
+ 0x3801, 0x00,
+ 0x3802, 0x00,
+ 0x3803, 0x04,
+ 0x3804, 0x0a,
+ 0x3805, 0x3f,
+ 0x3806, 0x07,
+ 0x3807, 0x9b,
+ 0x3808, 0x02,
+ 0x3809, 0x80,
+ 0x380a, 0x01,
+ 0x380b, 0xe0,
+ 0x3810, 0x00,
+ 0x3811, 0x10,
+ 0x3812, 0x00,
+ 0x3813, 0x06,
+ 0x3814, 0x31,
+ 0x3815, 0x31,
+
+ 0x3034, 0x1a,
+ 0x3035, 0x11,
+ 0x3036, 0x46,
+ 0x3037, 0x13,
+
+ 0x380c, 0x07,
+ 0x380d, 0x68,
+ 0x380e, 0x03,
+ 0x380f, 0xd8,
+
+ 0x3c01, 0xb4,
+ 0x3c00, 0x04,
+ 0x3a08, 0x01,
+ 0x3a09, 0x27,
+ 0x3a0e, 0x03,
+ 0x3a0a, 0x00,
+ 0x3a0b, 0xf6,
+ 0x3a0d, 0x04,
+
+ 0x3a00, 0x3c,
+ 0x3a02, 0x0b,
+ 0x3a03, 0x88,
+ 0x3a14, 0x0b,
+ 0x3a15, 0x88,
+
+ 0x3618, 0x00,
+ 0x3612, 0x29,
+ 0x3708, 0x64,
+ 0x3709, 0x52,
+ 0x370c, 0x03,
+
+ 0x4001, 0x02,
+ 0x4004, 0x02,
+ 0x3000, 0x00,
+ 0x3002, 0x1c,
+ 0x3004, 0xff,
+ 0x3006, 0xc3,
+ 0x300e, 0x58,
+ 0x302e, 0x00,
+ 0x4300, 0x30,
+ 0x501f, 0x00,
+ 0x4713, 0x03,
+ 0x4407, 0x04,
+ 0x440e, 0x00,
+ 0x460b, 0x35,
+ 0x460c, 0x22,
+ 0x4837, 0x22,
+ 0x3824, 0x02,
+ 0x5001, 0xa3,
+
+ 0x5180, 0xff,
+ 0x5181, 0xf2,
+ 0x5182, 0x00,
+ 0x5183, 0x14,
+ 0x5184, 0x25,
+ 0x5185, 0x24,
+ 0x5186, 0x16,
+ 0x5187, 0x16,
+ 0x5188, 0x16,
+ 0x5189, 0x72,
+ 0x518a, 0x68,
+ 0x518b, 0xe0,
+ 0x518c, 0xb2,
+ 0x518d, 0x42,
+ 0x518e, 0x3e,
+ 0x518f, 0x4c,
+ 0x5190, 0x56,
+ 0x5191, 0xf8,
+ 0x5192, 0x04,
+ 0x5193, 0x70,
+ 0x5194, 0xf0,
+ 0x5195, 0xf0,
+ 0x5196, 0x03,
+ 0x5197, 0x01,
+ 0x5198, 0x04,
+ 0x5199, 0x12,
+ 0x519a, 0x04,
+ 0x519b, 0x00,
+ 0x519c, 0x06,
+ 0x519d, 0x82,
+ 0x519e, 0x38,
+
+ 0x5381, 0x1e,
+ 0x5382, 0x5b,
+ 0x5383, 0x12,
+ 0x5384, 0x07,
+ 0x5385, 0x7f,
+ 0x5386, 0x86,
+ 0x5387, 0x8a,
+ 0x5388, 0x80,
+ 0x5389, 0x0a,
+ 0x538a, 0x01,
+ 0x538b, 0x98,
+
+ 0x5480, 0x01,
+ 0x5481, 0x06,
+ 0x5482, 0x12,
+ 0x5483, 0x24,
+ 0x5484, 0x4a,
+ 0x5485, 0x58,
+ 0x5486, 0x65,
+ 0x5487, 0x72,
+ 0x5488, 0x7d,
+ 0x5489, 0x88,
+ 0x548a, 0x92,
+ 0x548b, 0xa3,
+ 0x548c, 0xb2,
+ 0x548d, 0xc8,
+ 0x548e, 0xdd,
+ 0x548f, 0xf0,
+ 0x5490, 0x15,
+
+ 0x5300, 0x08,
+ 0x5301, 0x30,
+ 0x5302, 0x3f,
+ 0x5303, 0x10,
+ 0x5304, 0x08,
+ 0x5305, 0x30,
+ 0x5306, 0x04,
+ 0x5307, 0x14,
+ 0x5309, 0x08,
+ 0x530a, 0x30,
+ 0x530b, 0x04,
+ 0x530c, 0x06,
+
+ 0x5580, 0x06,
+ 0x5583, 0x40,
+ 0x5584, 0x20,
+ 0x5589, 0x10,
+ 0x558a, 0x00,
+ 0x558b, 0xf8,
+
+ 0x5000, 0xa7,
+#if 0
+ 0x5800, 0x20,
+ 0x5801, 0x19,
+ 0x5802, 0x17,
+ 0x5803, 0x16,
+ 0x5804, 0x18,
+ 0x5805, 0x21,
+ 0x5806, 0x0F,
+ 0x5807, 0x0A,
+ 0x5808, 0x07,
+ 0x5809, 0x07,
+ 0x580A, 0x0A,
+ 0x580B, 0x0C,
+ 0x580C, 0x0A,
+ 0x580D, 0x03,
+ 0x580E, 0x01,
+ 0x580F, 0x01,
+ 0x5810, 0x03,
+ 0x5811, 0x09,
+ 0x5812, 0x0A,
+ 0x5813, 0x03,
+ 0x5814, 0x01,
+ 0x5815, 0x01,
+ 0x5816, 0x03,
+ 0x5817, 0x08,
+ 0x5818, 0x10,
+ 0x5819, 0x0A,
+ 0x581A, 0x06,
+ 0x581B, 0x06,
+ 0x581C, 0x08,
+ 0x581D, 0x0E,
+ 0x581E, 0x22,
+ 0x581F, 0x18,
+ 0x5820, 0x13,
+ 0x5821, 0x12,
+ 0x5822, 0x16,
+ 0x5823, 0x1E,
+ 0x5824, 0x64,
+ 0x5825, 0x2A,
+ 0x5826, 0x2C,
+ 0x5827, 0x2A,
+ 0x5828, 0x46,
+ 0x5829, 0x2A,
+ 0x582A, 0x26,
+ 0x582B, 0x24,
+ 0x582C, 0x26,
+ 0x582D, 0x2A,
+ 0x582E, 0x28,
+ 0x582F, 0x42,
+ 0x5830, 0x40,
+ 0x5831, 0x42,
+ 0x5832, 0x08,
+ 0x5833, 0x28,
+ 0x5834, 0x26,
+ 0x5835, 0x24,
+ 0x5836, 0x26,
+ 0x5837, 0x2A,
+ 0x5838, 0x44,
+ 0x5839, 0x4A,
+ 0x583A, 0x2C,
+ 0x583B, 0x2a,
+ 0x583C, 0x46,
+ 0x583D, 0xCE,
+#endif
+
+ 0x5800, 0x29,
+ 0x5801, 0x18,
+ 0x5802, 0x13,
+ 0x5803, 0x13,
+ 0x5804, 0x1A,
+ 0x5805, 0x28,
+ 0x5806, 0x0D,
+ 0x5807, 0x09,
+ 0x5808, 0x06,
+ 0x5809, 0x06,
+ 0x580a, 0x08,
+ 0x580b, 0x11,
+ 0x580c, 0x09,
+ 0x580d, 0x04,
+ 0x580e, 0x01,
+ 0x580f, 0x01,
+ 0x5810, 0x04,
+ 0x5811, 0x09,
+ 0x5812, 0x09,
+ 0x5813, 0x04,
+ 0x5814, 0x01,
+ 0x5815, 0x01,
+ 0x5816, 0x04,
+ 0x5817, 0x0A,
+ 0x5818, 0x0E,
+ 0x5819, 0x09,
+ 0x581a, 0x06,
+ 0x581b, 0x06,
+ 0x581c, 0x09,
+ 0x581d, 0x11,
+ 0x581e, 0x28,
+ 0x581f, 0x18,
+ 0x5820, 0x12,
+ 0x5821, 0x13,
+ 0x5822, 0x19,
+ 0x5823, 0x2C,
+ 0x5824, 0x3B,
+ 0x5825, 0x27,
+ 0x5826, 0x08,
+ 0x5827, 0x27,
+ 0x5828, 0x0E,
+ 0x5829, 0x17,
+ 0x582a, 0x25,
+ 0x582b, 0x23,
+ 0x582c, 0x15,
+ 0x582d, 0x06,
+ 0x582e, 0x08,
+ 0x582f, 0x42,
+ 0x5830, 0x40,
+ 0x5831, 0x32,
+ 0x5832, 0x07,
+ 0x5833, 0x27,
+ 0x5834, 0x25,
+ 0x5835, 0x24,
+ 0x5836, 0x25,
+ 0x5837, 0x05,
+ 0x5838, 0x29,
+ 0x5839, 0x2A,
+ 0x583a, 0x0A,
+ 0x583b, 0x18,
+ 0x583c, 0x4B,
+ 0x583d, 0xCE,
+ 0x5688, 0x11,//weight
+ 0x5689, 0x11,
+ 0x568a, 0x11,
+ 0x568b, 0x11,
+ 0x568c, 0x11,
+ 0x568d, 0x11,
+ 0x568e, 0x11,
+ 0x568f, 0x11,
+
+ 0x5025, 0x00,
+
+ 0x3a0f, 0x30,
+ 0x3a10, 0x28,
+ 0x3a1b, 0x30,
+ 0x3a1e, 0x28,
+ 0x3a11, 0x61,
+ 0x3a1f, 0x10,
+
+ 0x4005, 0x1a,
+
+ 0x3503, 0x00,
+ 0x3008, 0x02,
+};
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/ov5640/ov5640_firmware.h b/drivers/media/video/wmt_v4l2/sensors/ov5640/ov5640_firmware.h
new file mode 100755
index 00000000..a738d52a
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/ov5640/ov5640_firmware.h
@@ -0,0 +1,4095 @@
+#ifndef __OV5640_FIRMWARE_H__
+#define __OV5640_FIRMWARE_H__
+uint32_t ov5640_firmware[] = {
+ 0x3000, 0x20,
+ 0x8000, 0x02,
+ 0x8001, 0x0f,
+ 0x8002, 0xd6,
+ 0x8003, 0x02,
+ 0x8004, 0x0a,
+ 0x8005, 0x39,
+ 0x8006, 0xc2,
+ 0x8007, 0x01,
+ 0x8008, 0x22,
+ 0x8009, 0x22,
+ 0x800a, 0x00,
+ 0x800b, 0x02,
+ 0x800c, 0x0f,
+ 0x800d, 0xb2,
+ 0x800e, 0xe5,
+ 0x800f, 0x1f,
+ 0x8010, 0x70,
+ 0x8011, 0x72,
+ 0x8012, 0xf5,
+ 0x8013, 0x1e,
+ 0x8014, 0xd2,
+ 0x8015, 0x35,
+ 0x8016, 0xff,
+ 0x8017, 0xef,
+ 0x8018, 0x25,
+ 0x8019, 0xe0,
+ 0x801a, 0x24,
+ 0x801b, 0x4e,
+ 0x801c, 0xf8,
+ 0x801d, 0xe4,
+ 0x801e, 0xf6,
+ 0x801f, 0x08,
+ 0x8020, 0xf6,
+ 0x8021, 0x0f,
+ 0x8022, 0xbf,
+ 0x8023, 0x34,
+ 0x8024, 0xf2,
+ 0x8025, 0x90,
+ 0x8026, 0x0e,
+ 0x8027, 0x93,
+ 0x8028, 0xe4,
+ 0x8029, 0x93,
+ 0x802a, 0xff,
+ 0x802b, 0xe5,
+ 0x802c, 0x4b,
+ 0x802d, 0xc3,
+ 0x802e, 0x9f,
+ 0x802f, 0x50,
+ 0x8030, 0x04,
+ 0x8031, 0x7f,
+ 0x8032, 0x05,
+ 0x8033, 0x80,
+ 0x8034, 0x02,
+ 0x8035, 0x7f,
+ 0x8036, 0xfb,
+ 0x8037, 0x78,
+ 0x8038, 0xbd,
+ 0x8039, 0xa6,
+ 0x803a, 0x07,
+ 0x803b, 0x12,
+ 0x803c, 0x0f,
+ 0x803d, 0x04,
+ 0x803e, 0x40,
+ 0x803f, 0x04,
+ 0x8040, 0x7f,
+ 0x8041, 0x03,
+ 0x8042, 0x80,
+ 0x8043, 0x02,
+ 0x8044, 0x7f,
+ 0x8045, 0x30,
+ 0x8046, 0x78,
+ 0x8047, 0xbc,
+ 0x8048, 0xa6,
+ 0x8049, 0x07,
+ 0x804a, 0xe6,
+ 0x804b, 0x18,
+ 0x804c, 0xf6,
+ 0x804d, 0x08,
+ 0x804e, 0xe6,
+ 0x804f, 0x78,
+ 0x8050, 0xb9,
+ 0x8051, 0xf6,
+ 0x8052, 0x78,
+ 0x8053, 0xbc,
+ 0x8054, 0xe6,
+ 0x8055, 0x78,
+ 0x8056, 0xba,
+ 0x8057, 0xf6,
+ 0x8058, 0x78,
+ 0x8059, 0xbf,
+ 0x805a, 0x76,
+ 0x805b, 0x33,
+ 0x805c, 0xe4,
+ 0x805d, 0x08,
+ 0x805e, 0xf6,
+ 0x805f, 0x78,
+ 0x8060, 0xb8,
+ 0x8061, 0x76,
+ 0x8062, 0x01,
+ 0x8063, 0x75,
+ 0x8064, 0x4a,
+ 0x8065, 0x02,
+ 0x8066, 0x78,
+ 0x8067, 0xb6,
+ 0x8068, 0xf6,
+ 0x8069, 0x08,
+ 0x806a, 0xf6,
+ 0x806b, 0x74,
+ 0x806c, 0xff,
+ 0x806d, 0x78,
+ 0x806e, 0xc1,
+ 0x806f, 0xf6,
+ 0x8070, 0x08,
+ 0x8071, 0xf6,
+ 0x8072, 0x75,
+ 0x8073, 0x1f,
+ 0x8074, 0x01,
+ 0x8075, 0x78,
+ 0x8076, 0xbc,
+ 0x8077, 0xe6,
+ 0x8078, 0x75,
+ 0x8079, 0xf0,
+ 0x807a, 0x05,
+ 0x807b, 0xa4,
+ 0x807c, 0xf5,
+ 0x807d, 0x4b,
+ 0x807e, 0x12,
+ 0x807f, 0x0a,
+ 0x8080, 0xff,
+ 0x8081, 0xc2,
+ 0x8082, 0x37,
+ 0x8083, 0x22,
+ 0x8084, 0x78,
+ 0x8085, 0xb8,
+ 0x8086, 0xe6,
+ 0x8087, 0xd3,
+ 0x8088, 0x94,
+ 0x8089, 0x00,
+ 0x808a, 0x40,
+ 0x808b, 0x02,
+ 0x808c, 0x16,
+ 0x808d, 0x22,
+ 0x808e, 0xe5,
+ 0x808f, 0x1f,
+ 0x8090, 0xb4,
+ 0x8091, 0x05,
+ 0x8092, 0x23,
+ 0x8093, 0xe4,
+ 0x8094, 0xf5,
+ 0x8095, 0x1f,
+ 0x8096, 0xc2,
+ 0x8097, 0x01,
+ 0x8098, 0x78,
+ 0x8099, 0xb6,
+ 0x809a, 0xe6,
+ 0x809b, 0xfe,
+ 0x809c, 0x08,
+ 0x809d, 0xe6,
+ 0x809e, 0xff,
+ 0x809f, 0x78,
+ 0x80a0, 0x4e,
+ 0x80a1, 0xa6,
+ 0x80a2, 0x06,
+ 0x80a3, 0x08,
+ 0x80a4, 0xa6,
+ 0x80a5, 0x07,
+ 0x80a6, 0xa2,
+ 0x80a7, 0x37,
+ 0x80a8, 0xe4,
+ 0x80a9, 0x33,
+ 0x80aa, 0xf5,
+ 0x80ab, 0x3c,
+ 0x80ac, 0x90,
+ 0x80ad, 0x30,
+ 0x80ae, 0x28,
+ 0x80af, 0xf0,
+ 0x80b0, 0x75,
+ 0x80b1, 0x1e,
+ 0x80b2, 0x10,
+ 0x80b3, 0xd2,
+ 0x80b4, 0x35,
+ 0x80b5, 0x22,
+ 0x80b6, 0xe5,
+ 0x80b7, 0x4b,
+ 0x80b8, 0x75,
+ 0x80b9, 0xf0,
+ 0x80ba, 0x05,
+ 0x80bb, 0x84,
+ 0x80bc, 0x78,
+ 0x80bd, 0xbc,
+ 0x80be, 0xf6,
+ 0x80bf, 0x90,
+ 0x80c0, 0x0e,
+ 0x80c1, 0x8c,
+ 0x80c2, 0xe4,
+ 0x80c3, 0x93,
+ 0x80c4, 0xff,
+ 0x80c5, 0x25,
+ 0x80c6, 0xe0,
+ 0x80c7, 0x24,
+ 0x80c8, 0x0a,
+ 0x80c9, 0xf8,
+ 0x80ca, 0xe6,
+ 0x80cb, 0xfc,
+ 0x80cc, 0x08,
+ 0x80cd, 0xe6,
+ 0x80ce, 0xfd,
+ 0x80cf, 0x78,
+ 0x80d0, 0xbc,
+ 0x80d1, 0xe6,
+ 0x80d2, 0x25,
+ 0x80d3, 0xe0,
+ 0x80d4, 0x24,
+ 0x80d5, 0x4e,
+ 0x80d6, 0xf8,
+ 0x80d7, 0xa6,
+ 0x80d8, 0x04,
+ 0x80d9, 0x08,
+ 0x80da, 0xa6,
+ 0x80db, 0x05,
+ 0x80dc, 0xef,
+ 0x80dd, 0x12,
+ 0x80de, 0x0f,
+ 0x80df, 0x0b,
+ 0x80e0, 0xd3,
+ 0x80e1, 0x78,
+ 0x80e2, 0xb7,
+ 0x80e3, 0x96,
+ 0x80e4, 0xee,
+ 0x80e5, 0x18,
+ 0x80e6, 0x96,
+ 0x80e7, 0x40,
+ 0x80e8, 0x0d,
+ 0x80e9, 0x78,
+ 0x80ea, 0xbc,
+ 0x80eb, 0xe6,
+ 0x80ec, 0x78,
+ 0x80ed, 0xb9,
+ 0x80ee, 0xf6,
+ 0x80ef, 0x78,
+ 0x80f0, 0xb6,
+ 0x80f1, 0xa6,
+ 0x80f2, 0x06,
+ 0x80f3, 0x08,
+ 0x80f4, 0xa6,
+ 0x80f5, 0x07,
+ 0x80f6, 0x90,
+ 0x80f7, 0x0e,
+ 0x80f8, 0x8c,
+ 0x80f9, 0xe4,
+ 0x80fa, 0x93,
+ 0x80fb, 0x12,
+ 0x80fc, 0x0f,
+ 0x80fd, 0x0b,
+ 0x80fe, 0xc3,
+ 0x80ff, 0x78,
+ 0x8100, 0xc2,
+ 0x8101, 0x96,
+ 0x8102, 0xee,
+ 0x8103, 0x18,
+ 0x8104, 0x96,
+ 0x8105, 0x50,
+ 0x8106, 0x0d,
+ 0x8107, 0x78,
+ 0x8108, 0xbc,
+ 0x8109, 0xe6,
+ 0x810a, 0x78,
+ 0x810b, 0xba,
+ 0x810c, 0xf6,
+ 0x810d, 0x78,
+ 0x810e, 0xc1,
+ 0x810f, 0xa6,
+ 0x8110, 0x06,
+ 0x8111, 0x08,
+ 0x8112, 0xa6,
+ 0x8113, 0x07,
+ 0x8114, 0x78,
+ 0x8115, 0xb6,
+ 0x8116, 0xe6,
+ 0x8117, 0xfe,
+ 0x8118, 0x08,
+ 0x8119, 0xe6,
+ 0x811a, 0xc3,
+ 0x811b, 0x78,
+ 0x811c, 0xc2,
+ 0x811d, 0x96,
+ 0x811e, 0xff,
+ 0x811f, 0xee,
+ 0x8120, 0x18,
+ 0x8121, 0x96,
+ 0x8122, 0x78,
+ 0x8123, 0xc3,
+ 0x8124, 0xf6,
+ 0x8125, 0x08,
+ 0x8126, 0xa6,
+ 0x8127, 0x07,
+ 0x8128, 0x90,
+ 0x8129, 0x0e,
+ 0x812a, 0x95,
+ 0x812b, 0xe4,
+ 0x812c, 0x18,
+ 0x812d, 0x12,
+ 0x812e, 0x0e,
+ 0x812f, 0xe9,
+ 0x8130, 0x40,
+ 0x8131, 0x02,
+ 0x8132, 0xd2,
+ 0x8133, 0x37,
+ 0x8134, 0x78,
+ 0x8135, 0xbc,
+ 0x8136, 0xe6,
+ 0x8137, 0x08,
+ 0x8138, 0x26,
+ 0x8139, 0x08,
+ 0x813a, 0xf6,
+ 0x813b, 0xe5,
+ 0x813c, 0x1f,
+ 0x813d, 0x64,
+ 0x813e, 0x01,
+ 0x813f, 0x70,
+ 0x8140, 0x4a,
+ 0x8141, 0xe6,
+ 0x8142, 0xc3,
+ 0x8143, 0x78,
+ 0x8144, 0xc0,
+ 0x8145, 0x12,
+ 0x8146, 0x0e,
+ 0x8147, 0xdf,
+ 0x8148, 0x40,
+ 0x8149, 0x05,
+ 0x814a, 0x12,
+ 0x814b, 0x0e,
+ 0x814c, 0xda,
+ 0x814d, 0x40,
+ 0x814e, 0x39,
+ 0x814f, 0x12,
+ 0x8150, 0x0f,
+ 0x8151, 0x02,
+ 0x8152, 0x40,
+ 0x8153, 0x04,
+ 0x8154, 0x7f,
+ 0x8155, 0xfe,
+ 0x8156, 0x80,
+ 0x8157, 0x02,
+ 0x8158, 0x7f,
+ 0x8159, 0x02,
+ 0x815a, 0x78,
+ 0x815b, 0xbd,
+ 0x815c, 0xa6,
+ 0x815d, 0x07,
+ 0x815e, 0x78,
+ 0x815f, 0xb9,
+ 0x8160, 0xe6,
+ 0x8161, 0x24,
+ 0x8162, 0x03,
+ 0x8163, 0x78,
+ 0x8164, 0xbf,
+ 0x8165, 0xf6,
+ 0x8166, 0x78,
+ 0x8167, 0xb9,
+ 0x8168, 0xe6,
+ 0x8169, 0x24,
+ 0x816a, 0xfd,
+ 0x816b, 0x78,
+ 0x816c, 0xc0,
+ 0x816d, 0xf6,
+ 0x816e, 0x12,
+ 0x816f, 0x0f,
+ 0x8170, 0x02,
+ 0x8171, 0x40,
+ 0x8172, 0x06,
+ 0x8173, 0x78,
+ 0x8174, 0xc0,
+ 0x8175, 0xe6,
+ 0x8176, 0xff,
+ 0x8177, 0x80,
+ 0x8178, 0x04,
+ 0x8179, 0x78,
+ 0x817a, 0xbf,
+ 0x817b, 0xe6,
+ 0x817c, 0xff,
+ 0x817d, 0x78,
+ 0x817e, 0xbe,
+ 0x817f, 0xa6,
+ 0x8180, 0x07,
+ 0x8181, 0x75,
+ 0x8182, 0x1f,
+ 0x8183, 0x02,
+ 0x8184, 0x78,
+ 0x8185, 0xb8,
+ 0x8186, 0x76,
+ 0x8187, 0x01,
+ 0x8188, 0x02,
+ 0x8189, 0x02,
+ 0x818a, 0x4a,
+ 0x818b, 0xe5,
+ 0x818c, 0x1f,
+ 0x818d, 0x64,
+ 0x818e, 0x02,
+ 0x818f, 0x60,
+ 0x8190, 0x03,
+ 0x8191, 0x02,
+ 0x8192, 0x02,
+ 0x8193, 0x2a,
+ 0x8194, 0x78,
+ 0x8195, 0xbe,
+ 0x8196, 0xe6,
+ 0x8197, 0xff,
+ 0x8198, 0xc3,
+ 0x8199, 0x78,
+ 0x819a, 0xc0,
+ 0x819b, 0x12,
+ 0x819c, 0x0e,
+ 0x819d, 0xe0,
+ 0x819e, 0x40,
+ 0x819f, 0x08,
+ 0x81a0, 0x12,
+ 0x81a1, 0x0e,
+ 0x81a2, 0xda,
+ 0x81a3, 0x50,
+ 0x81a4, 0x03,
+ 0x81a5, 0x02,
+ 0x81a6, 0x02,
+ 0x81a7, 0x28,
+ 0x81a8, 0x12,
+ 0x81a9, 0x0f,
+ 0x81aa, 0x02,
+ 0x81ab, 0x40,
+ 0x81ac, 0x04,
+ 0x81ad, 0x7f,
+ 0x81ae, 0xff,
+ 0x81af, 0x80,
+ 0x81b0, 0x02,
+ 0x81b1, 0x7f,
+ 0x81b2, 0x01,
+ 0x81b3, 0x78,
+ 0x81b4, 0xbd,
+ 0x81b5, 0xa6,
+ 0x81b6, 0x07,
+ 0x81b7, 0x78,
+ 0x81b8, 0xb9,
+ 0x81b9, 0xe6,
+ 0x81ba, 0x04,
+ 0x81bb, 0x78,
+ 0x81bc, 0xbf,
+ 0x81bd, 0xf6,
+ 0x81be, 0x78,
+ 0x81bf, 0xb9,
+ 0x81c0, 0xe6,
+ 0x81c1, 0x14,
+ 0x81c2, 0x78,
+ 0x81c3, 0xc0,
+ 0x81c4, 0xf6,
+ 0x81c5, 0x18,
+ 0x81c6, 0x12,
+ 0x81c7, 0x0f,
+ 0x81c8, 0x04,
+ 0x81c9, 0x40,
+ 0x81ca, 0x04,
+ 0x81cb, 0xe6,
+ 0x81cc, 0xff,
+ 0x81cd, 0x80,
+ 0x81ce, 0x02,
+ 0x81cf, 0x7f,
+ 0x81d0, 0x00,
+ 0x81d1, 0x78,
+ 0x81d2, 0xbf,
+ 0x81d3, 0xa6,
+ 0x81d4, 0x07,
+ 0x81d5, 0xd3,
+ 0x81d6, 0x08,
+ 0x81d7, 0xe6,
+ 0x81d8, 0x64,
+ 0x81d9, 0x80,
+ 0x81da, 0x94,
+ 0x81db, 0x80,
+ 0x81dc, 0x40,
+ 0x81dd, 0x04,
+ 0x81de, 0xe6,
+ 0x81df, 0xff,
+ 0x81e0, 0x80,
+ 0x81e1, 0x02,
+ 0x81e2, 0x7f,
+ 0x81e3, 0x00,
+ 0x81e4, 0x78,
+ 0x81e5, 0xc0,
+ 0x81e6, 0xa6,
+ 0x81e7, 0x07,
+ 0x81e8, 0xc3,
+ 0x81e9, 0x18,
+ 0x81ea, 0xe6,
+ 0x81eb, 0x64,
+ 0x81ec, 0x80,
+ 0x81ed, 0x94,
+ 0x81ee, 0xb3,
+ 0x81ef, 0x50,
+ 0x81f0, 0x04,
+ 0x81f1, 0xe6,
+ 0x81f2, 0xff,
+ 0x81f3, 0x80,
+ 0x81f4, 0x02,
+ 0x81f5, 0x7f,
+ 0x81f6, 0x33,
+ 0x81f7, 0x78,
+ 0x81f8, 0xbf,
+ 0x81f9, 0xa6,
+ 0x81fa, 0x07,
+ 0x81fb, 0xc3,
+ 0x81fc, 0x08,
+ 0x81fd, 0xe6,
+ 0x81fe, 0x64,
+ 0x81ff, 0x80,
+ 0x8200, 0x94,
+ 0x8201, 0xb3,
+ 0x8202, 0x50,
+ 0x8203, 0x04,
+ 0x8204, 0xe6,
+ 0x8205, 0xff,
+ 0x8206, 0x80,
+ 0x8207, 0x02,
+ 0x8208, 0x7f,
+ 0x8209, 0x33,
+ 0x820a, 0x78,
+ 0x820b, 0xc0,
+ 0x820c, 0xa6,
+ 0x820d, 0x07,
+ 0x820e, 0x12,
+ 0x820f, 0x0f,
+ 0x8210, 0x02,
+ 0x8211, 0x40,
+ 0x8212, 0x06,
+ 0x8213, 0x78,
+ 0x8214, 0xc0,
+ 0x8215, 0xe6,
+ 0x8216, 0xff,
+ 0x8217, 0x80,
+ 0x8218, 0x04,
+ 0x8219, 0x78,
+ 0x821a, 0xbf,
+ 0x821b, 0xe6,
+ 0x821c, 0xff,
+ 0x821d, 0x78,
+ 0x821e, 0xbe,
+ 0x821f, 0xa6,
+ 0x8220, 0x07,
+ 0x8221, 0x75,
+ 0x8222, 0x1f,
+ 0x8223, 0x03,
+ 0x8224, 0x78,
+ 0x8225, 0xb8,
+ 0x8226, 0x76,
+ 0x8227, 0x01,
+ 0x8228, 0x80,
+ 0x8229, 0x20,
+ 0x822a, 0xe5,
+ 0x822b, 0x1f,
+ 0x822c, 0x64,
+ 0x822d, 0x03,
+ 0x822e, 0x70,
+ 0x822f, 0x26,
+ 0x8230, 0x78,
+ 0x8231, 0xbe,
+ 0x8232, 0xe6,
+ 0x8233, 0xff,
+ 0x8234, 0xc3,
+ 0x8235, 0x78,
+ 0x8236, 0xc0,
+ 0x8237, 0x12,
+ 0x8238, 0x0e,
+ 0x8239, 0xe0,
+ 0x823a, 0x40,
+ 0x823b, 0x05,
+ 0x823c, 0x12,
+ 0x823d, 0x0e,
+ 0x823e, 0xda,
+ 0x823f, 0x40,
+ 0x8240, 0x09,
+ 0x8241, 0x78,
+ 0x8242, 0xb9,
+ 0x8243, 0xe6,
+ 0x8244, 0x78,
+ 0x8245, 0xbe,
+ 0x8246, 0xf6,
+ 0x8247, 0x75,
+ 0x8248, 0x1f,
+ 0x8249, 0x04,
+ 0x824a, 0x78,
+ 0x824b, 0xbe,
+ 0x824c, 0xe6,
+ 0x824d, 0x75,
+ 0x824e, 0xf0,
+ 0x824f, 0x05,
+ 0x8250, 0xa4,
+ 0x8251, 0xf5,
+ 0x8252, 0x4b,
+ 0x8253, 0x02,
+ 0x8254, 0x0a,
+ 0x8255, 0xff,
+ 0x8256, 0xe5,
+ 0x8257, 0x1f,
+ 0x8258, 0xb4,
+ 0x8259, 0x04,
+ 0x825a, 0x10,
+ 0x825b, 0x90,
+ 0x825c, 0x0e,
+ 0x825d, 0x94,
+ 0x825e, 0xe4,
+ 0x825f, 0x78,
+ 0x8260, 0xc3,
+ 0x8261, 0x12,
+ 0x8262, 0x0e,
+ 0x8263, 0xe9,
+ 0x8264, 0x40,
+ 0x8265, 0x02,
+ 0x8266, 0xd2,
+ 0x8267, 0x37,
+ 0x8268, 0x75,
+ 0x8269, 0x1f,
+ 0x826a, 0x05,
+ 0x826b, 0x22,
+ 0x826c, 0x30,
+ 0x826d, 0x01,
+ 0x826e, 0x03,
+ 0x826f, 0x02,
+ 0x8270, 0x04,
+ 0x8271, 0xc0,
+ 0x8272, 0x30,
+ 0x8273, 0x02,
+ 0x8274, 0x03,
+ 0x8275, 0x02,
+ 0x8276, 0x04,
+ 0x8277, 0xc0,
+ 0x8278, 0x90,
+ 0x8279, 0x51,
+ 0x827a, 0xa5,
+ 0x827b, 0xe0,
+ 0x827c, 0x78,
+ 0x827d, 0x93,
+ 0x827e, 0xf6,
+ 0x827f, 0xa3,
+ 0x8280, 0xe0,
+ 0x8281, 0x08,
+ 0x8282, 0xf6,
+ 0x8283, 0xa3,
+ 0x8284, 0xe0,
+ 0x8285, 0x08,
+ 0x8286, 0xf6,
+ 0x8287, 0xe5,
+ 0x8288, 0x1f,
+ 0x8289, 0x70,
+ 0x828a, 0x3c,
+ 0x828b, 0x75,
+ 0x828c, 0x1e,
+ 0x828d, 0x20,
+ 0x828e, 0xd2,
+ 0x828f, 0x35,
+ 0x8290, 0x12,
+ 0x8291, 0x0c,
+ 0x8292, 0x7a,
+ 0x8293, 0x78,
+ 0x8294, 0x7e,
+ 0x8295, 0xa6,
+ 0x8296, 0x06,
+ 0x8297, 0x08,
+ 0x8298, 0xa6,
+ 0x8299, 0x07,
+ 0x829a, 0x78,
+ 0x829b, 0x8b,
+ 0x829c, 0xa6,
+ 0x829d, 0x09,
+ 0x829e, 0x18,
+ 0x829f, 0x76,
+ 0x82a0, 0x01,
+ 0x82a1, 0x12,
+ 0x82a2, 0x0c,
+ 0x82a3, 0x5b,
+ 0x82a4, 0x78,
+ 0x82a5, 0x4e,
+ 0x82a6, 0xa6,
+ 0x82a7, 0x06,
+ 0x82a8, 0x08,
+ 0x82a9, 0xa6,
+ 0x82aa, 0x07,
+ 0x82ab, 0x78,
+ 0x82ac, 0x8b,
+ 0x82ad, 0xe6,
+ 0x82ae, 0x78,
+ 0x82af, 0x6e,
+ 0x82b0, 0xf6,
+ 0x82b1, 0x75,
+ 0x82b2, 0x1f,
+ 0x82b3, 0x01,
+ 0x82b4, 0x78,
+ 0x82b5, 0x93,
+ 0x82b6, 0xe6,
+ 0x82b7, 0x78,
+ 0x82b8, 0x90,
+ 0x82b9, 0xf6,
+ 0x82ba, 0x78,
+ 0x82bb, 0x94,
+ 0x82bc, 0xe6,
+ 0x82bd, 0x78,
+ 0x82be, 0x91,
+ 0x82bf, 0xf6,
+ 0x82c0, 0x78,
+ 0x82c1, 0x95,
+ 0x82c2, 0xe6,
+ 0x82c3, 0x78,
+ 0x82c4, 0x92,
+ 0x82c5, 0xf6,
+ 0x82c6, 0x22,
+ 0x82c7, 0x79,
+ 0x82c8, 0x90,
+ 0x82c9, 0xe7,
+ 0x82ca, 0xd3,
+ 0x82cb, 0x78,
+ 0x82cc, 0x93,
+ 0x82cd, 0x96,
+ 0x82ce, 0x40,
+ 0x82cf, 0x05,
+ 0x82d0, 0xe7,
+ 0x82d1, 0x96,
+ 0x82d2, 0xff,
+ 0x82d3, 0x80,
+ 0x82d4, 0x08,
+ 0x82d5, 0xc3,
+ 0x82d6, 0x79,
+ 0x82d7, 0x93,
+ 0x82d8, 0xe7,
+ 0x82d9, 0x78,
+ 0x82da, 0x90,
+ 0x82db, 0x96,
+ 0x82dc, 0xff,
+ 0x82dd, 0x78,
+ 0x82de, 0x88,
+ 0x82df, 0x76,
+ 0x82e0, 0x00,
+ 0x82e1, 0x08,
+ 0x82e2, 0xa6,
+ 0x82e3, 0x07,
+ 0x82e4, 0x79,
+ 0x82e5, 0x91,
+ 0x82e6, 0xe7,
+ 0x82e7, 0xd3,
+ 0x82e8, 0x78,
+ 0x82e9, 0x94,
+ 0x82ea, 0x96,
+ 0x82eb, 0x40,
+ 0x82ec, 0x05,
+ 0x82ed, 0xe7,
+ 0x82ee, 0x96,
+ 0x82ef, 0xff,
+ 0x82f0, 0x80,
+ 0x82f1, 0x08,
+ 0x82f2, 0xc3,
+ 0x82f3, 0x79,
+ 0x82f4, 0x94,
+ 0x82f5, 0xe7,
+ 0x82f6, 0x78,
+ 0x82f7, 0x91,
+ 0x82f8, 0x96,
+ 0x82f9, 0xff,
+ 0x82fa, 0x12,
+ 0x82fb, 0x0c,
+ 0x82fc, 0x8e,
+ 0x82fd, 0x79,
+ 0x82fe, 0x92,
+ 0x82ff, 0xe7,
+ 0x8300, 0xd3,
+ 0x8301, 0x78,
+ 0x8302, 0x95,
+ 0x8303, 0x96,
+ 0x8304, 0x40,
+ 0x8305, 0x05,
+ 0x8306, 0xe7,
+ 0x8307, 0x96,
+ 0x8308, 0xff,
+ 0x8309, 0x80,
+ 0x830a, 0x08,
+ 0x830b, 0xc3,
+ 0x830c, 0x79,
+ 0x830d, 0x95,
+ 0x830e, 0xe7,
+ 0x830f, 0x78,
+ 0x8310, 0x92,
+ 0x8311, 0x96,
+ 0x8312, 0xff,
+ 0x8313, 0x12,
+ 0x8314, 0x0c,
+ 0x8315, 0x8e,
+ 0x8316, 0x12,
+ 0x8317, 0x0c,
+ 0x8318, 0x5b,
+ 0x8319, 0x78,
+ 0x831a, 0x8a,
+ 0x831b, 0xe6,
+ 0x831c, 0x25,
+ 0x831d, 0xe0,
+ 0x831e, 0x24,
+ 0x831f, 0x4e,
+ 0x8320, 0xf8,
+ 0x8321, 0xa6,
+ 0x8322, 0x06,
+ 0x8323, 0x08,
+ 0x8324, 0xa6,
+ 0x8325, 0x07,
+ 0x8326, 0x78,
+ 0x8327, 0x8a,
+ 0x8328, 0xe6,
+ 0x8329, 0x24,
+ 0x832a, 0x6e,
+ 0x832b, 0xf8,
+ 0x832c, 0xa6,
+ 0x832d, 0x09,
+ 0x832e, 0x78,
+ 0x832f, 0x8a,
+ 0x8330, 0xe6,
+ 0x8331, 0x24,
+ 0x8332, 0x01,
+ 0x8333, 0xff,
+ 0x8334, 0xe4,
+ 0x8335, 0x33,
+ 0x8336, 0xfe,
+ 0x8337, 0xd3,
+ 0x8338, 0xef,
+ 0x8339, 0x94,
+ 0x833a, 0x0f,
+ 0x833b, 0xee,
+ 0x833c, 0x64,
+ 0x833d, 0x80,
+ 0x833e, 0x94,
+ 0x833f, 0x80,
+ 0x8340, 0x40,
+ 0x8341, 0x04,
+ 0x8342, 0x7f,
+ 0x8343, 0x00,
+ 0x8344, 0x80,
+ 0x8345, 0x05,
+ 0x8346, 0x78,
+ 0x8347, 0x8a,
+ 0x8348, 0xe6,
+ 0x8349, 0x04,
+ 0x834a, 0xff,
+ 0x834b, 0x78,
+ 0x834c, 0x8a,
+ 0x834d, 0xa6,
+ 0x834e, 0x07,
+ 0x834f, 0xe5,
+ 0x8350, 0x1f,
+ 0x8351, 0xb4,
+ 0x8352, 0x01,
+ 0x8353, 0x0a,
+ 0x8354, 0xe6,
+ 0x8355, 0x60,
+ 0x8356, 0x03,
+ 0x8357, 0x02,
+ 0x8358, 0x04,
+ 0x8359, 0xc0,
+ 0x835a, 0x75,
+ 0x835b, 0x1f,
+ 0x835c, 0x02,
+ 0x835d, 0x22,
+ 0x835e, 0x12,
+ 0x835f, 0x0c,
+ 0x8360, 0x7a,
+ 0x8361, 0x78,
+ 0x8362, 0x80,
+ 0x8363, 0xa6,
+ 0x8364, 0x06,
+ 0x8365, 0x08,
+ 0x8366, 0xa6,
+ 0x8367, 0x07,
+ 0x8368, 0x12,
+ 0x8369, 0x0c,
+ 0x836a, 0x7a,
+ 0x836b, 0x78,
+ 0x836c, 0x82,
+ 0x836d, 0xa6,
+ 0x836e, 0x06,
+ 0x836f, 0x08,
+ 0x8370, 0xa6,
+ 0x8371, 0x07,
+ 0x8372, 0x78,
+ 0x8373, 0x6e,
+ 0x8374, 0xe6,
+ 0x8375, 0x78,
+ 0x8376, 0x8c,
+ 0x8377, 0xf6,
+ 0x8378, 0x78,
+ 0x8379, 0x6e,
+ 0x837a, 0xe6,
+ 0x837b, 0x78,
+ 0x837c, 0x8d,
+ 0x837d, 0xf6,
+ 0x837e, 0x7f,
+ 0x837f, 0x01,
+ 0x8380, 0xef,
+ 0x8381, 0x25,
+ 0x8382, 0xe0,
+ 0x8383, 0x24,
+ 0x8384, 0x4f,
+ 0x8385, 0xf9,
+ 0x8386, 0xc3,
+ 0x8387, 0x78,
+ 0x8388, 0x81,
+ 0x8389, 0xe6,
+ 0x838a, 0x97,
+ 0x838b, 0x18,
+ 0x838c, 0xe6,
+ 0x838d, 0x19,
+ 0x838e, 0x97,
+ 0x838f, 0x50,
+ 0x8390, 0x0a,
+ 0x8391, 0x12,
+ 0x8392, 0x0c,
+ 0x8393, 0x82,
+ 0x8394, 0x78,
+ 0x8395, 0x80,
+ 0x8396, 0xa6,
+ 0x8397, 0x04,
+ 0x8398, 0x08,
+ 0x8399, 0xa6,
+ 0x839a, 0x05,
+ 0x839b, 0x74,
+ 0x839c, 0x6e,
+ 0x839d, 0x2f,
+ 0x839e, 0xf9,
+ 0x839f, 0x78,
+ 0x83a0, 0x8c,
+ 0x83a1, 0xe6,
+ 0x83a2, 0xc3,
+ 0x83a3, 0x97,
+ 0x83a4, 0x50,
+ 0x83a5, 0x08,
+ 0x83a6, 0x74,
+ 0x83a7, 0x6e,
+ 0x83a8, 0x2f,
+ 0x83a9, 0xf8,
+ 0x83aa, 0xe6,
+ 0x83ab, 0x78,
+ 0x83ac, 0x8c,
+ 0x83ad, 0xf6,
+ 0x83ae, 0xef,
+ 0x83af, 0x25,
+ 0x83b0, 0xe0,
+ 0x83b1, 0x24,
+ 0x83b2, 0x4f,
+ 0x83b3, 0xf9,
+ 0x83b4, 0xd3,
+ 0x83b5, 0x78,
+ 0x83b6, 0x83,
+ 0x83b7, 0xe6,
+ 0x83b8, 0x97,
+ 0x83b9, 0x18,
+ 0x83ba, 0xe6,
+ 0x83bb, 0x19,
+ 0x83bc, 0x97,
+ 0x83bd, 0x40,
+ 0x83be, 0x0a,
+ 0x83bf, 0x12,
+ 0x83c0, 0x0c,
+ 0x83c1, 0x82,
+ 0x83c2, 0x78,
+ 0x83c3, 0x82,
+ 0x83c4, 0xa6,
+ 0x83c5, 0x04,
+ 0x83c6, 0x08,
+ 0x83c7, 0xa6,
+ 0x83c8, 0x05,
+ 0x83c9, 0x74,
+ 0x83ca, 0x6e,
+ 0x83cb, 0x2f,
+ 0x83cc, 0xf9,
+ 0x83cd, 0x78,
+ 0x83ce, 0x8d,
+ 0x83cf, 0xe6,
+ 0x83d0, 0xd3,
+ 0x83d1, 0x97,
+ 0x83d2, 0x40,
+ 0x83d3, 0x08,
+ 0x83d4, 0x74,
+ 0x83d5, 0x6e,
+ 0x83d6, 0x2f,
+ 0x83d7, 0xf8,
+ 0x83d8, 0xe6,
+ 0x83d9, 0x78,
+ 0x83da, 0x8d,
+ 0x83db, 0xf6,
+ 0x83dc, 0x0f,
+ 0x83dd, 0xef,
+ 0x83de, 0x64,
+ 0x83df, 0x10,
+ 0x83e0, 0x70,
+ 0x83e1, 0x9e,
+ 0x83e2, 0xc3,
+ 0x83e3, 0x79,
+ 0x83e4, 0x81,
+ 0x83e5, 0xe7,
+ 0x83e6, 0x78,
+ 0x83e7, 0x83,
+ 0x83e8, 0x96,
+ 0x83e9, 0xff,
+ 0x83ea, 0x19,
+ 0x83eb, 0xe7,
+ 0x83ec, 0x18,
+ 0x83ed, 0x96,
+ 0x83ee, 0x78,
+ 0x83ef, 0x84,
+ 0x83f0, 0xf6,
+ 0x83f1, 0x08,
+ 0x83f2, 0xa6,
+ 0x83f3, 0x07,
+ 0x83f4, 0xc3,
+ 0x83f5, 0x79,
+ 0x83f6, 0x8c,
+ 0x83f7, 0xe7,
+ 0x83f8, 0x78,
+ 0x83f9, 0x8d,
+ 0x83fa, 0x96,
+ 0x83fb, 0x08,
+ 0x83fc, 0xf6,
+ 0x83fd, 0xd3,
+ 0x83fe, 0x79,
+ 0x83ff, 0x81,
+ 0x8400, 0xe7,
+ 0x8401, 0x78,
+ 0x8402, 0x7f,
+ 0x8403, 0x96,
+ 0x8404, 0x19,
+ 0x8405, 0xe7,
+ 0x8406, 0x18,
+ 0x8407, 0x96,
+ 0x8408, 0x40,
+ 0x8409, 0x05,
+ 0x840a, 0x09,
+ 0x840b, 0xe7,
+ 0x840c, 0x08,
+ 0x840d, 0x80,
+ 0x840e, 0x06,
+ 0x840f, 0xc3,
+ 0x8410, 0x79,
+ 0x8411, 0x7f,
+ 0x8412, 0xe7,
+ 0x8413, 0x78,
+ 0x8414, 0x81,
+ 0x8415, 0x96,
+ 0x8416, 0xff,
+ 0x8417, 0x19,
+ 0x8418, 0xe7,
+ 0x8419, 0x18,
+ 0x841a, 0x96,
+ 0x841b, 0xfe,
+ 0x841c, 0x78,
+ 0x841d, 0x86,
+ 0x841e, 0xa6,
+ 0x841f, 0x06,
+ 0x8420, 0x08,
+ 0x8421, 0xa6,
+ 0x8422, 0x07,
+ 0x8423, 0x79,
+ 0x8424, 0x8c,
+ 0x8425, 0xe7,
+ 0x8426, 0xd3,
+ 0x8427, 0x78,
+ 0x8428, 0x8b,
+ 0x8429, 0x96,
+ 0x842a, 0x40,
+ 0x842b, 0x05,
+ 0x842c, 0xe7,
+ 0x842d, 0x96,
+ 0x842e, 0xff,
+ 0x842f, 0x80,
+ 0x8430, 0x08,
+ 0x8431, 0xc3,
+ 0x8432, 0x79,
+ 0x8433, 0x8b,
+ 0x8434, 0xe7,
+ 0x8435, 0x78,
+ 0x8436, 0x8c,
+ 0x8437, 0x96,
+ 0x8438, 0xff,
+ 0x8439, 0x78,
+ 0x843a, 0x8f,
+ 0x843b, 0xa6,
+ 0x843c, 0x07,
+ 0x843d, 0xe5,
+ 0x843e, 0x1f,
+ 0x843f, 0x64,
+ 0x8440, 0x02,
+ 0x8441, 0x70,
+ 0x8442, 0x69,
+ 0x8443, 0x90,
+ 0x8444, 0x0e,
+ 0x8445, 0x91,
+ 0x8446, 0x93,
+ 0x8447, 0xff,
+ 0x8448, 0x18,
+ 0x8449, 0xe6,
+ 0x844a, 0xc3,
+ 0x844b, 0x9f,
+ 0x844c, 0x50,
+ 0x844d, 0x72,
+ 0x844e, 0x12,
+ 0x844f, 0x0c,
+ 0x8450, 0x4a,
+ 0x8451, 0x12,
+ 0x8452, 0x0c,
+ 0x8453, 0x2f,
+ 0x8454, 0x90,
+ 0x8455, 0x0e,
+ 0x8456, 0x8e,
+ 0x8457, 0x12,
+ 0x8458, 0x0c,
+ 0x8459, 0x38,
+ 0x845a, 0x78,
+ 0x845b, 0x80,
+ 0x845c, 0x12,
+ 0x845d, 0x0c,
+ 0x845e, 0x6b,
+ 0x845f, 0x7b,
+ 0x8460, 0x04,
+ 0x8461, 0x12,
+ 0x8462, 0x0c,
+ 0x8463, 0x1d,
+ 0x8464, 0xc3,
+ 0x8465, 0x12,
+ 0x8466, 0x06,
+ 0x8467, 0x45,
+ 0x8468, 0x50,
+ 0x8469, 0x56,
+ 0x846a, 0x90,
+ 0x846b, 0x0e,
+ 0x846c, 0x92,
+ 0x846d, 0xe4,
+ 0x846e, 0x93,
+ 0x846f, 0xff,
+ 0x8470, 0x78,
+ 0x8471, 0x8f,
+ 0x8472, 0xe6,
+ 0x8473, 0x9f,
+ 0x8474, 0x40,
+ 0x8475, 0x02,
+ 0x8476, 0x80,
+ 0x8477, 0x11,
+ 0x8478, 0x90,
+ 0x8479, 0x0e,
+ 0x847a, 0x90,
+ 0x847b, 0xe4,
+ 0x847c, 0x93,
+ 0x847d, 0xff,
+ 0x847e, 0xd3,
+ 0x847f, 0x78,
+ 0x8480, 0x89,
+ 0x8481, 0xe6,
+ 0x8482, 0x9f,
+ 0x8483, 0x18,
+ 0x8484, 0xe6,
+ 0x8485, 0x94,
+ 0x8486, 0x00,
+ 0x8487, 0x40,
+ 0x8488, 0x03,
+ 0x8489, 0x75,
+ 0x848a, 0x1f,
+ 0x848b, 0x05,
+ 0x848c, 0x12,
+ 0x848d, 0x0c,
+ 0x848e, 0x4a,
+ 0x848f, 0x12,
+ 0x8490, 0x0c,
+ 0x8491, 0x2f,
+ 0x8492, 0x90,
+ 0x8493, 0x0e,
+ 0x8494, 0x8f,
+ 0x8495, 0x12,
+ 0x8496, 0x0c,
+ 0x8497, 0x38,
+ 0x8498, 0x78,
+ 0x8499, 0x7e,
+ 0x849a, 0x12,
+ 0x849b, 0x0c,
+ 0x849c, 0x6b,
+ 0x849d, 0x7b,
+ 0x849e, 0x40,
+ 0x849f, 0x12,
+ 0x84a0, 0x0c,
+ 0x84a1, 0x1d,
+ 0x84a2, 0xd3,
+ 0x84a3, 0x12,
+ 0x84a4, 0x06,
+ 0x84a5, 0x45,
+ 0x84a6, 0x40,
+ 0x84a7, 0x18,
+ 0x84a8, 0x75,
+ 0x84a9, 0x1f,
+ 0x84aa, 0x05,
+ 0x84ab, 0x22,
+ 0x84ac, 0xe5,
+ 0x84ad, 0x1f,
+ 0x84ae, 0xb4,
+ 0x84af, 0x05,
+ 0x84b0, 0x0f,
+ 0x84b1, 0xd2,
+ 0x84b2, 0x01,
+ 0x84b3, 0xc2,
+ 0x84b4, 0x02,
+ 0x84b5, 0xe4,
+ 0x84b6, 0xf5,
+ 0x84b7, 0x1f,
+ 0x84b8, 0xf5,
+ 0x84b9, 0x1e,
+ 0x84ba, 0xd2,
+ 0x84bb, 0x35,
+ 0x84bc, 0xd2,
+ 0x84bd, 0x33,
+ 0x84be, 0xd2,
+ 0x84bf, 0x36,
+ 0x84c0, 0x22,
+ 0x84c1, 0xef,
+ 0x84c2, 0x8d,
+ 0x84c3, 0xf0,
+ 0x84c4, 0xa4,
+ 0x84c5, 0xa8,
+ 0x84c6, 0xf0,
+ 0x84c7, 0xcf,
+ 0x84c8, 0x8c,
+ 0x84c9, 0xf0,
+ 0x84ca, 0xa4,
+ 0x84cb, 0x28,
+ 0x84cc, 0xce,
+ 0x84cd, 0x8d,
+ 0x84ce, 0xf0,
+ 0x84cf, 0xa4,
+ 0x84d0, 0x2e,
+ 0x84d1, 0xfe,
+ 0x84d2, 0x22,
+ 0x84d3, 0xbc,
+ 0x84d4, 0x00,
+ 0x84d5, 0x0b,
+ 0x84d6, 0xbe,
+ 0x84d7, 0x00,
+ 0x84d8, 0x29,
+ 0x84d9, 0xef,
+ 0x84da, 0x8d,
+ 0x84db, 0xf0,
+ 0x84dc, 0x84,
+ 0x84dd, 0xff,
+ 0x84de, 0xad,
+ 0x84df, 0xf0,
+ 0x84e0, 0x22,
+ 0x84e1, 0xe4,
+ 0x84e2, 0xcc,
+ 0x84e3, 0xf8,
+ 0x84e4, 0x75,
+ 0x84e5, 0xf0,
+ 0x84e6, 0x08,
+ 0x84e7, 0xef,
+ 0x84e8, 0x2f,
+ 0x84e9, 0xff,
+ 0x84ea, 0xee,
+ 0x84eb, 0x33,
+ 0x84ec, 0xfe,
+ 0x84ed, 0xec,
+ 0x84ee, 0x33,
+ 0x84ef, 0xfc,
+ 0x84f0, 0xee,
+ 0x84f1, 0x9d,
+ 0x84f2, 0xec,
+ 0x84f3, 0x98,
+ 0x84f4, 0x40,
+ 0x84f5, 0x05,
+ 0x84f6, 0xfc,
+ 0x84f7, 0xee,
+ 0x84f8, 0x9d,
+ 0x84f9, 0xfe,
+ 0x84fa, 0x0f,
+ 0x84fb, 0xd5,
+ 0x84fc, 0xf0,
+ 0x84fd, 0xe9,
+ 0x84fe, 0xe4,
+ 0x84ff, 0xce,
+ 0x8500, 0xfd,
+ 0x8501, 0x22,
+ 0x8502, 0xed,
+ 0x8503, 0xf8,
+ 0x8504, 0xf5,
+ 0x8505, 0xf0,
+ 0x8506, 0xee,
+ 0x8507, 0x84,
+ 0x8508, 0x20,
+ 0x8509, 0xd2,
+ 0x850a, 0x1c,
+ 0x850b, 0xfe,
+ 0x850c, 0xad,
+ 0x850d, 0xf0,
+ 0x850e, 0x75,
+ 0x850f, 0xf0,
+ 0x8510, 0x08,
+ 0x8511, 0xef,
+ 0x8512, 0x2f,
+ 0x8513, 0xff,
+ 0x8514, 0xed,
+ 0x8515, 0x33,
+ 0x8516, 0xfd,
+ 0x8517, 0x40,
+ 0x8518, 0x07,
+ 0x8519, 0x98,
+ 0x851a, 0x50,
+ 0x851b, 0x06,
+ 0x851c, 0xd5,
+ 0x851d, 0xf0,
+ 0x851e, 0xf2,
+ 0x851f, 0x22,
+ 0x8520, 0xc3,
+ 0x8521, 0x98,
+ 0x8522, 0xfd,
+ 0x8523, 0x0f,
+ 0x8524, 0xd5,
+ 0x8525, 0xf0,
+ 0x8526, 0xea,
+ 0x8527, 0x22,
+ 0x8528, 0xe8,
+ 0x8529, 0x8f,
+ 0x852a, 0xf0,
+ 0x852b, 0xa4,
+ 0x852c, 0xcc,
+ 0x852d, 0x8b,
+ 0x852e, 0xf0,
+ 0x852f, 0xa4,
+ 0x8530, 0x2c,
+ 0x8531, 0xfc,
+ 0x8532, 0xe9,
+ 0x8533, 0x8e,
+ 0x8534, 0xf0,
+ 0x8535, 0xa4,
+ 0x8536, 0x2c,
+ 0x8537, 0xfc,
+ 0x8538, 0x8a,
+ 0x8539, 0xf0,
+ 0x853a, 0xed,
+ 0x853b, 0xa4,
+ 0x853c, 0x2c,
+ 0x853d, 0xfc,
+ 0x853e, 0xea,
+ 0x853f, 0x8e,
+ 0x8540, 0xf0,
+ 0x8541, 0xa4,
+ 0x8542, 0xcd,
+ 0x8543, 0xa8,
+ 0x8544, 0xf0,
+ 0x8545, 0x8b,
+ 0x8546, 0xf0,
+ 0x8547, 0xa4,
+ 0x8548, 0x2d,
+ 0x8549, 0xcc,
+ 0x854a, 0x38,
+ 0x854b, 0x25,
+ 0x854c, 0xf0,
+ 0x854d, 0xfd,
+ 0x854e, 0xe9,
+ 0x854f, 0x8f,
+ 0x8550, 0xf0,
+ 0x8551, 0xa4,
+ 0x8552, 0x2c,
+ 0x8553, 0xcd,
+ 0x8554, 0x35,
+ 0x8555, 0xf0,
+ 0x8556, 0xfc,
+ 0x8557, 0xeb,
+ 0x8558, 0x8e,
+ 0x8559, 0xf0,
+ 0x855a, 0xa4,
+ 0x855b, 0xfe,
+ 0x855c, 0xa9,
+ 0x855d, 0xf0,
+ 0x855e, 0xeb,
+ 0x855f, 0x8f,
+ 0x8560, 0xf0,
+ 0x8561, 0xa4,
+ 0x8562, 0xcf,
+ 0x8563, 0xc5,
+ 0x8564, 0xf0,
+ 0x8565, 0x2e,
+ 0x8566, 0xcd,
+ 0x8567, 0x39,
+ 0x8568, 0xfe,
+ 0x8569, 0xe4,
+ 0x856a, 0x3c,
+ 0x856b, 0xfc,
+ 0x856c, 0xea,
+ 0x856d, 0xa4,
+ 0x856e, 0x2d,
+ 0x856f, 0xce,
+ 0x8570, 0x35,
+ 0x8571, 0xf0,
+ 0x8572, 0xfd,
+ 0x8573, 0xe4,
+ 0x8574, 0x3c,
+ 0x8575, 0xfc,
+ 0x8576, 0x22,
+ 0x8577, 0x75,
+ 0x8578, 0xf0,
+ 0x8579, 0x08,
+ 0x857a, 0x75,
+ 0x857b, 0x82,
+ 0x857c, 0x00,
+ 0x857d, 0xef,
+ 0x857e, 0x2f,
+ 0x857f, 0xff,
+ 0x8580, 0xee,
+ 0x8581, 0x33,
+ 0x8582, 0xfe,
+ 0x8583, 0xcd,
+ 0x8584, 0x33,
+ 0x8585, 0xcd,
+ 0x8586, 0xcc,
+ 0x8587, 0x33,
+ 0x8588, 0xcc,
+ 0x8589, 0xc5,
+ 0x858a, 0x82,
+ 0x858b, 0x33,
+ 0x858c, 0xc5,
+ 0x858d, 0x82,
+ 0x858e, 0x9b,
+ 0x858f, 0xed,
+ 0x8590, 0x9a,
+ 0x8591, 0xec,
+ 0x8592, 0x99,
+ 0x8593, 0xe5,
+ 0x8594, 0x82,
+ 0x8595, 0x98,
+ 0x8596, 0x40,
+ 0x8597, 0x0c,
+ 0x8598, 0xf5,
+ 0x8599, 0x82,
+ 0x859a, 0xee,
+ 0x859b, 0x9b,
+ 0x859c, 0xfe,
+ 0x859d, 0xed,
+ 0x859e, 0x9a,
+ 0x859f, 0xfd,
+ 0x85a0, 0xec,
+ 0x85a1, 0x99,
+ 0x85a2, 0xfc,
+ 0x85a3, 0x0f,
+ 0x85a4, 0xd5,
+ 0x85a5, 0xf0,
+ 0x85a6, 0xd6,
+ 0x85a7, 0xe4,
+ 0x85a8, 0xce,
+ 0x85a9, 0xfb,
+ 0x85aa, 0xe4,
+ 0x85ab, 0xcd,
+ 0x85ac, 0xfa,
+ 0x85ad, 0xe4,
+ 0x85ae, 0xcc,
+ 0x85af, 0xf9,
+ 0x85b0, 0xa8,
+ 0x85b1, 0x82,
+ 0x85b2, 0x22,
+ 0x85b3, 0xb8,
+ 0x85b4, 0x00,
+ 0x85b5, 0xc1,
+ 0x85b6, 0xb9,
+ 0x85b7, 0x00,
+ 0x85b8, 0x59,
+ 0x85b9, 0xba,
+ 0x85ba, 0x00,
+ 0x85bb, 0x2d,
+ 0x85bc, 0xec,
+ 0x85bd, 0x8b,
+ 0x85be, 0xf0,
+ 0x85bf, 0x84,
+ 0x85c0, 0xcf,
+ 0x85c1, 0xce,
+ 0x85c2, 0xcd,
+ 0x85c3, 0xfc,
+ 0x85c4, 0xe5,
+ 0x85c5, 0xf0,
+ 0x85c6, 0xcb,
+ 0x85c7, 0xf9,
+ 0x85c8, 0x78,
+ 0x85c9, 0x18,
+ 0x85ca, 0xef,
+ 0x85cb, 0x2f,
+ 0x85cc, 0xff,
+ 0x85cd, 0xee,
+ 0x85ce, 0x33,
+ 0x85cf, 0xfe,
+ 0x85d0, 0xed,
+ 0x85d1, 0x33,
+ 0x85d2, 0xfd,
+ 0x85d3, 0xec,
+ 0x85d4, 0x33,
+ 0x85d5, 0xfc,
+ 0x85d6, 0xeb,
+ 0x85d7, 0x33,
+ 0x85d8, 0xfb,
+ 0x85d9, 0x10,
+ 0x85da, 0xd7,
+ 0x85db, 0x03,
+ 0x85dc, 0x99,
+ 0x85dd, 0x40,
+ 0x85de, 0x04,
+ 0x85df, 0xeb,
+ 0x85e0, 0x99,
+ 0x85e1, 0xfb,
+ 0x85e2, 0x0f,
+ 0x85e3, 0xd8,
+ 0x85e4, 0xe5,
+ 0x85e5, 0xe4,
+ 0x85e6, 0xf9,
+ 0x85e7, 0xfa,
+ 0x85e8, 0x22,
+ 0x85e9, 0x78,
+ 0x85ea, 0x18,
+ 0x85eb, 0xef,
+ 0x85ec, 0x2f,
+ 0x85ed, 0xff,
+ 0x85ee, 0xee,
+ 0x85ef, 0x33,
+ 0x85f0, 0xfe,
+ 0x85f1, 0xed,
+ 0x85f2, 0x33,
+ 0x85f3, 0xfd,
+ 0x85f4, 0xec,
+ 0x85f5, 0x33,
+ 0x85f6, 0xfc,
+ 0x85f7, 0xc9,
+ 0x85f8, 0x33,
+ 0x85f9, 0xc9,
+ 0x85fa, 0x10,
+ 0x85fb, 0xd7,
+ 0x85fc, 0x05,
+ 0x85fd, 0x9b,
+ 0x85fe, 0xe9,
+ 0x85ff, 0x9a,
+ 0x8600, 0x40,
+ 0x8601, 0x07,
+ 0x8602, 0xec,
+ 0x8603, 0x9b,
+ 0x8604, 0xfc,
+ 0x8605, 0xe9,
+ 0x8606, 0x9a,
+ 0x8607, 0xf9,
+ 0x8608, 0x0f,
+ 0x8609, 0xd8,
+ 0x860a, 0xe0,
+ 0x860b, 0xe4,
+ 0x860c, 0xc9,
+ 0x860d, 0xfa,
+ 0x860e, 0xe4,
+ 0x860f, 0xcc,
+ 0x8610, 0xfb,
+ 0x8611, 0x22,
+ 0x8612, 0x75,
+ 0x8613, 0xf0,
+ 0x8614, 0x10,
+ 0x8615, 0xef,
+ 0x8616, 0x2f,
+ 0x8617, 0xff,
+ 0x8618, 0xee,
+ 0x8619, 0x33,
+ 0x861a, 0xfe,
+ 0x861b, 0xed,
+ 0x861c, 0x33,
+ 0x861d, 0xfd,
+ 0x861e, 0xcc,
+ 0x861f, 0x33,
+ 0x8620, 0xcc,
+ 0x8621, 0xc8,
+ 0x8622, 0x33,
+ 0x8623, 0xc8,
+ 0x8624, 0x10,
+ 0x8625, 0xd7,
+ 0x8626, 0x07,
+ 0x8627, 0x9b,
+ 0x8628, 0xec,
+ 0x8629, 0x9a,
+ 0x862a, 0xe8,
+ 0x862b, 0x99,
+ 0x862c, 0x40,
+ 0x862d, 0x0a,
+ 0x862e, 0xed,
+ 0x862f, 0x9b,
+ 0x8630, 0xfd,
+ 0x8631, 0xec,
+ 0x8632, 0x9a,
+ 0x8633, 0xfc,
+ 0x8634, 0xe8,
+ 0x8635, 0x99,
+ 0x8636, 0xf8,
+ 0x8637, 0x0f,
+ 0x8638, 0xd5,
+ 0x8639, 0xf0,
+ 0x863a, 0xda,
+ 0x863b, 0xe4,
+ 0x863c, 0xcd,
+ 0x863d, 0xfb,
+ 0x863e, 0xe4,
+ 0x863f, 0xcc,
+ 0x8640, 0xfa,
+ 0x8641, 0xe4,
+ 0x8642, 0xc8,
+ 0x8643, 0xf9,
+ 0x8644, 0x22,
+ 0x8645, 0xeb,
+ 0x8646, 0x9f,
+ 0x8647, 0xf5,
+ 0x8648, 0xf0,
+ 0x8649, 0xea,
+ 0x864a, 0x9e,
+ 0x864b, 0x42,
+ 0x864c, 0xf0,
+ 0x864d, 0xe9,
+ 0x864e, 0x9d,
+ 0x864f, 0x42,
+ 0x8650, 0xf0,
+ 0x8651, 0xe8,
+ 0x8652, 0x9c,
+ 0x8653, 0x45,
+ 0x8654, 0xf0,
+ 0x8655, 0x22,
+ 0x8656, 0xe8,
+ 0x8657, 0x60,
+ 0x8658, 0x0f,
+ 0x8659, 0xec,
+ 0x865a, 0xc3,
+ 0x865b, 0x13,
+ 0x865c, 0xfc,
+ 0x865d, 0xed,
+ 0x865e, 0x13,
+ 0x865f, 0xfd,
+ 0x8660, 0xee,
+ 0x8661, 0x13,
+ 0x8662, 0xfe,
+ 0x8663, 0xef,
+ 0x8664, 0x13,
+ 0x8665, 0xff,
+ 0x8666, 0xd8,
+ 0x8667, 0xf1,
+ 0x8668, 0x22,
+ 0x8669, 0xe8,
+ 0x866a, 0x60,
+ 0x866b, 0x0f,
+ 0x866c, 0xef,
+ 0x866d, 0xc3,
+ 0x866e, 0x33,
+ 0x866f, 0xff,
+ 0x8670, 0xee,
+ 0x8671, 0x33,
+ 0x8672, 0xfe,
+ 0x8673, 0xed,
+ 0x8674, 0x33,
+ 0x8675, 0xfd,
+ 0x8676, 0xec,
+ 0x8677, 0x33,
+ 0x8678, 0xfc,
+ 0x8679, 0xd8,
+ 0x867a, 0xf1,
+ 0x867b, 0x22,
+ 0x867c, 0xe4,
+ 0x867d, 0x93,
+ 0x867e, 0xfc,
+ 0x867f, 0x74,
+ 0x8680, 0x01,
+ 0x8681, 0x93,
+ 0x8682, 0xfd,
+ 0x8683, 0x74,
+ 0x8684, 0x02,
+ 0x8685, 0x93,
+ 0x8686, 0xfe,
+ 0x8687, 0x74,
+ 0x8688, 0x03,
+ 0x8689, 0x93,
+ 0x868a, 0xff,
+ 0x868b, 0x22,
+ 0x868c, 0xe6,
+ 0x868d, 0xfb,
+ 0x868e, 0x08,
+ 0x868f, 0xe6,
+ 0x8690, 0xf9,
+ 0x8691, 0x08,
+ 0x8692, 0xe6,
+ 0x8693, 0xfa,
+ 0x8694, 0x08,
+ 0x8695, 0xe6,
+ 0x8696, 0xcb,
+ 0x8697, 0xf8,
+ 0x8698, 0x22,
+ 0x8699, 0xec,
+ 0x869a, 0xf6,
+ 0x869b, 0x08,
+ 0x869c, 0xed,
+ 0x869d, 0xf6,
+ 0x869e, 0x08,
+ 0x869f, 0xee,
+ 0x86a0, 0xf6,
+ 0x86a1, 0x08,
+ 0x86a2, 0xef,
+ 0x86a3, 0xf6,
+ 0x86a4, 0x22,
+ 0x86a5, 0xa4,
+ 0x86a6, 0x25,
+ 0x86a7, 0x82,
+ 0x86a8, 0xf5,
+ 0x86a9, 0x82,
+ 0x86aa, 0xe5,
+ 0x86ab, 0xf0,
+ 0x86ac, 0x35,
+ 0x86ad, 0x83,
+ 0x86ae, 0xf5,
+ 0x86af, 0x83,
+ 0x86b0, 0x22,
+ 0x86b1, 0xd0,
+ 0x86b2, 0x83,
+ 0x86b3, 0xd0,
+ 0x86b4, 0x82,
+ 0x86b5, 0xf8,
+ 0x86b6, 0xe4,
+ 0x86b7, 0x93,
+ 0x86b8, 0x70,
+ 0x86b9, 0x12,
+ 0x86ba, 0x74,
+ 0x86bb, 0x01,
+ 0x86bc, 0x93,
+ 0x86bd, 0x70,
+ 0x86be, 0x0d,
+ 0x86bf, 0xa3,
+ 0x86c0, 0xa3,
+ 0x86c1, 0x93,
+ 0x86c2, 0xf8,
+ 0x86c3, 0x74,
+ 0x86c4, 0x01,
+ 0x86c5, 0x93,
+ 0x86c6, 0xf5,
+ 0x86c7, 0x82,
+ 0x86c8, 0x88,
+ 0x86c9, 0x83,
+ 0x86ca, 0xe4,
+ 0x86cb, 0x73,
+ 0x86cc, 0x74,
+ 0x86cd, 0x02,
+ 0x86ce, 0x93,
+ 0x86cf, 0x68,
+ 0x86d0, 0x60,
+ 0x86d1, 0xef,
+ 0x86d2, 0xa3,
+ 0x86d3, 0xa3,
+ 0x86d4, 0xa3,
+ 0x86d5, 0x80,
+ 0x86d6, 0xdf,
+ 0x86d7, 0x90,
+ 0x86d8, 0x38,
+ 0x86d9, 0x04,
+ 0x86da, 0x78,
+ 0x86db, 0x52,
+ 0x86dc, 0x12,
+ 0x86dd, 0x0b,
+ 0x86de, 0xfd,
+ 0x86df, 0x90,
+ 0x86e0, 0x38,
+ 0x86e1, 0x00,
+ 0x86e2, 0xe0,
+ 0x86e3, 0xfe,
+ 0x86e4, 0xa3,
+ 0x86e5, 0xe0,
+ 0x86e6, 0xfd,
+ 0x86e7, 0xed,
+ 0x86e8, 0xff,
+ 0x86e9, 0xc3,
+ 0x86ea, 0x12,
+ 0x86eb, 0x0b,
+ 0x86ec, 0x9e,
+ 0x86ed, 0x90,
+ 0x86ee, 0x38,
+ 0x86ef, 0x10,
+ 0x86f0, 0x12,
+ 0x86f1, 0x0b,
+ 0x86f2, 0x92,
+ 0x86f3, 0x90,
+ 0x86f4, 0x38,
+ 0x86f5, 0x06,
+ 0x86f6, 0x78,
+ 0x86f7, 0x54,
+ 0x86f8, 0x12,
+ 0x86f9, 0x0b,
+ 0x86fa, 0xfd,
+ 0x86fb, 0x90,
+ 0x86fc, 0x38,
+ 0x86fd, 0x02,
+ 0x86fe, 0xe0,
+ 0x86ff, 0xfe,
+ 0x8700, 0xa3,
+ 0x8701, 0xe0,
+ 0x8702, 0xfd,
+ 0x8703, 0xed,
+ 0x8704, 0xff,
+ 0x8705, 0xc3,
+ 0x8706, 0x12,
+ 0x8707, 0x0b,
+ 0x8708, 0x9e,
+ 0x8709, 0x90,
+ 0x870a, 0x38,
+ 0x870b, 0x12,
+ 0x870c, 0x12,
+ 0x870d, 0x0b,
+ 0x870e, 0x92,
+ 0x870f, 0xa3,
+ 0x8710, 0xe0,
+ 0x8711, 0xb4,
+ 0x8712, 0x31,
+ 0x8713, 0x07,
+ 0x8714, 0x78,
+ 0x8715, 0x52,
+ 0x8716, 0x79,
+ 0x8717, 0x52,
+ 0x8718, 0x12,
+ 0x8719, 0x0c,
+ 0x871a, 0x13,
+ 0x871b, 0x90,
+ 0x871c, 0x38,
+ 0x871d, 0x14,
+ 0x871e, 0xe0,
+ 0x871f, 0xb4,
+ 0x8720, 0x71,
+ 0x8721, 0x15,
+ 0x8722, 0x78,
+ 0x8723, 0x52,
+ 0x8724, 0xe6,
+ 0x8725, 0xfe,
+ 0x8726, 0x08,
+ 0x8727, 0xe6,
+ 0x8728, 0x78,
+ 0x8729, 0x02,
+ 0x872a, 0xce,
+ 0x872b, 0xc3,
+ 0x872c, 0x13,
+ 0x872d, 0xce,
+ 0x872e, 0x13,
+ 0x872f, 0xd8,
+ 0x8730, 0xf9,
+ 0x8731, 0x79,
+ 0x8732, 0x53,
+ 0x8733, 0xf7,
+ 0x8734, 0xee,
+ 0x8735, 0x19,
+ 0x8736, 0xf7,
+ 0x8737, 0x90,
+ 0x8738, 0x38,
+ 0x8739, 0x15,
+ 0x873a, 0xe0,
+ 0x873b, 0xb4,
+ 0x873c, 0x31,
+ 0x873d, 0x07,
+ 0x873e, 0x78,
+ 0x873f, 0x54,
+ 0x8740, 0x79,
+ 0x8741, 0x54,
+ 0x8742, 0x12,
+ 0x8743, 0x0c,
+ 0x8744, 0x13,
+ 0x8745, 0x90,
+ 0x8746, 0x38,
+ 0x8747, 0x15,
+ 0x8748, 0xe0,
+ 0x8749, 0xb4,
+ 0x874a, 0x71,
+ 0x874b, 0x15,
+ 0x874c, 0x78,
+ 0x874d, 0x54,
+ 0x874e, 0xe6,
+ 0x874f, 0xfe,
+ 0x8750, 0x08,
+ 0x8751, 0xe6,
+ 0x8752, 0x78,
+ 0x8753, 0x02,
+ 0x8754, 0xce,
+ 0x8755, 0xc3,
+ 0x8756, 0x13,
+ 0x8757, 0xce,
+ 0x8758, 0x13,
+ 0x8759, 0xd8,
+ 0x875a, 0xf9,
+ 0x875b, 0x79,
+ 0x875c, 0x55,
+ 0x875d, 0xf7,
+ 0x875e, 0xee,
+ 0x875f, 0x19,
+ 0x8760, 0xf7,
+ 0x8761, 0x79,
+ 0x8762, 0x52,
+ 0x8763, 0x12,
+ 0x8764, 0x0b,
+ 0x8765, 0xd9,
+ 0x8766, 0x09,
+ 0x8767, 0x12,
+ 0x8768, 0x0b,
+ 0x8769, 0xd9,
+ 0x876a, 0xaf,
+ 0x876b, 0x47,
+ 0x876c, 0x12,
+ 0x876d, 0x0b,
+ 0x876e, 0xb2,
+ 0x876f, 0xe5,
+ 0x8770, 0x44,
+ 0x8771, 0xfb,
+ 0x8772, 0x7a,
+ 0x8773, 0x00,
+ 0x8774, 0xfd,
+ 0x8775, 0x7c,
+ 0x8776, 0x00,
+ 0x8777, 0x12,
+ 0x8778, 0x04,
+ 0x8779, 0xd3,
+ 0x877a, 0x78,
+ 0x877b, 0x5a,
+ 0x877c, 0xa6,
+ 0x877d, 0x06,
+ 0x877e, 0x08,
+ 0x877f, 0xa6,
+ 0x8780, 0x07,
+ 0x8781, 0xaf,
+ 0x8782, 0x45,
+ 0x8783, 0x12,
+ 0x8784, 0x0b,
+ 0x8785, 0xb2,
+ 0x8786, 0xad,
+ 0x8787, 0x03,
+ 0x8788, 0x7c,
+ 0x8789, 0x00,
+ 0x878a, 0x12,
+ 0x878b, 0x04,
+ 0x878c, 0xd3,
+ 0x878d, 0x78,
+ 0x878e, 0x56,
+ 0x878f, 0xa6,
+ 0x8790, 0x06,
+ 0x8791, 0x08,
+ 0x8792, 0xa6,
+ 0x8793, 0x07,
+ 0x8794, 0xaf,
+ 0x8795, 0x48,
+ 0x8796, 0x78,
+ 0x8797, 0x54,
+ 0x8798, 0x12,
+ 0x8799, 0x0b,
+ 0x879a, 0xb4,
+ 0x879b, 0xe5,
+ 0x879c, 0x43,
+ 0x879d, 0xfb,
+ 0x879e, 0xfd,
+ 0x879f, 0x7c,
+ 0x87a0, 0x00,
+ 0x87a1, 0x12,
+ 0x87a2, 0x04,
+ 0x87a3, 0xd3,
+ 0x87a4, 0x78,
+ 0x87a5, 0x5c,
+ 0x87a6, 0xa6,
+ 0x87a7, 0x06,
+ 0x87a8, 0x08,
+ 0x87a9, 0xa6,
+ 0x87aa, 0x07,
+ 0x87ab, 0xaf,
+ 0x87ac, 0x46,
+ 0x87ad, 0x7e,
+ 0x87ae, 0x00,
+ 0x87af, 0x78,
+ 0x87b0, 0x54,
+ 0x87b1, 0x12,
+ 0x87b2, 0x0b,
+ 0x87b3, 0xb6,
+ 0x87b4, 0xad,
+ 0x87b5, 0x03,
+ 0x87b6, 0x7c,
+ 0x87b7, 0x00,
+ 0x87b8, 0x12,
+ 0x87b9, 0x04,
+ 0x87ba, 0xd3,
+ 0x87bb, 0x78,
+ 0x87bc, 0x58,
+ 0x87bd, 0xa6,
+ 0x87be, 0x06,
+ 0x87bf, 0x08,
+ 0x87c0, 0xa6,
+ 0x87c1, 0x07,
+ 0x87c2, 0xc3,
+ 0x87c3, 0x78,
+ 0x87c4, 0x5b,
+ 0x87c5, 0xe6,
+ 0x87c6, 0x94,
+ 0x87c7, 0x08,
+ 0x87c8, 0x18,
+ 0x87c9, 0xe6,
+ 0x87ca, 0x94,
+ 0x87cb, 0x00,
+ 0x87cc, 0x50,
+ 0x87cd, 0x05,
+ 0x87ce, 0x76,
+ 0x87cf, 0x00,
+ 0x87d0, 0x08,
+ 0x87d1, 0x76,
+ 0x87d2, 0x08,
+ 0x87d3, 0xc3,
+ 0x87d4, 0x78,
+ 0x87d5, 0x5d,
+ 0x87d6, 0xe6,
+ 0x87d7, 0x94,
+ 0x87d8, 0x08,
+ 0x87d9, 0x18,
+ 0x87da, 0xe6,
+ 0x87db, 0x94,
+ 0x87dc, 0x00,
+ 0x87dd, 0x50,
+ 0x87de, 0x05,
+ 0x87df, 0x76,
+ 0x87e0, 0x00,
+ 0x87e1, 0x08,
+ 0x87e2, 0x76,
+ 0x87e3, 0x08,
+ 0x87e4, 0x78,
+ 0x87e5, 0x5a,
+ 0x87e6, 0x12,
+ 0x87e7, 0x0b,
+ 0x87e8, 0xc6,
+ 0x87e9, 0xff,
+ 0x87ea, 0xd3,
+ 0x87eb, 0x78,
+ 0x87ec, 0x57,
+ 0x87ed, 0xe6,
+ 0x87ee, 0x9f,
+ 0x87ef, 0x18,
+ 0x87f0, 0xe6,
+ 0x87f1, 0x9e,
+ 0x87f2, 0x40,
+ 0x87f3, 0x0e,
+ 0x87f4, 0x78,
+ 0x87f5, 0x5a,
+ 0x87f6, 0xe6,
+ 0x87f7, 0x13,
+ 0x87f8, 0xfe,
+ 0x87f9, 0x08,
+ 0x87fa, 0xe6,
+ 0x87fb, 0x78,
+ 0x87fc, 0x57,
+ 0x87fd, 0x12,
+ 0x87fe, 0x0c,
+ 0x87ff, 0x08,
+ 0x8800, 0x80,
+ 0x8801, 0x04,
+ 0x8802, 0x7e,
+ 0x8803, 0x00,
+ 0x8804, 0x7f,
+ 0x8805, 0x00,
+ 0x8806, 0x78,
+ 0x8807, 0x5e,
+ 0x8808, 0x12,
+ 0x8809, 0x0b,
+ 0x880a, 0xbe,
+ 0x880b, 0xff,
+ 0x880c, 0xd3,
+ 0x880d, 0x78,
+ 0x880e, 0x59,
+ 0x880f, 0xe6,
+ 0x8810, 0x9f,
+ 0x8811, 0x18,
+ 0x8812, 0xe6,
+ 0x8813, 0x9e,
+ 0x8814, 0x40,
+ 0x8815, 0x0e,
+ 0x8816, 0x78,
+ 0x8817, 0x5c,
+ 0x8818, 0xe6,
+ 0x8819, 0x13,
+ 0x881a, 0xfe,
+ 0x881b, 0x08,
+ 0x881c, 0xe6,
+ 0x881d, 0x78,
+ 0x881e, 0x59,
+ 0x881f, 0x12,
+ 0x8820, 0x0c,
+ 0x8821, 0x08,
+ 0x8822, 0x80,
+ 0x8823, 0x04,
+ 0x8824, 0x7e,
+ 0x8825, 0x00,
+ 0x8826, 0x7f,
+ 0x8827, 0x00,
+ 0x8828, 0xe4,
+ 0x8829, 0xfc,
+ 0x882a, 0xfd,
+ 0x882b, 0x78,
+ 0x882c, 0x62,
+ 0x882d, 0x12,
+ 0x882e, 0x06,
+ 0x882f, 0x99,
+ 0x8830, 0x78,
+ 0x8831, 0x5a,
+ 0x8832, 0x12,
+ 0x8833, 0x0b,
+ 0x8834, 0xc6,
+ 0x8835, 0x78,
+ 0x8836, 0x57,
+ 0x8837, 0x26,
+ 0x8838, 0xff,
+ 0x8839, 0xee,
+ 0x883a, 0x18,
+ 0x883b, 0x36,
+ 0x883c, 0xfe,
+ 0x883d, 0x78,
+ 0x883e, 0x66,
+ 0x883f, 0x12,
+ 0x8840, 0x0b,
+ 0x8841, 0xbe,
+ 0x8842, 0x78,
+ 0x8843, 0x59,
+ 0x8844, 0x26,
+ 0x8845, 0xff,
+ 0x8846, 0xee,
+ 0x8847, 0x18,
+ 0x8848, 0x36,
+ 0x8849, 0xfe,
+ 0x884a, 0xe4,
+ 0x884b, 0xfc,
+ 0x884c, 0xfd,
+ 0x884d, 0x78,
+ 0x884e, 0x6a,
+ 0x884f, 0x12,
+ 0x8850, 0x06,
+ 0x8851, 0x99,
+ 0x8852, 0x12,
+ 0x8853, 0x0b,
+ 0x8854, 0xce,
+ 0x8855, 0x78,
+ 0x8856, 0x66,
+ 0x8857, 0x12,
+ 0x8858, 0x06,
+ 0x8859, 0x8c,
+ 0x885a, 0xd3,
+ 0x885b, 0x12,
+ 0x885c, 0x06,
+ 0x885d, 0x45,
+ 0x885e, 0x40,
+ 0x885f, 0x08,
+ 0x8860, 0x12,
+ 0x8861, 0x0b,
+ 0x8862, 0xce,
+ 0x8863, 0x78,
+ 0x8864, 0x66,
+ 0x8865, 0x12,
+ 0x8866, 0x06,
+ 0x8867, 0x99,
+ 0x8868, 0x78,
+ 0x8869, 0x54,
+ 0x886a, 0x12,
+ 0x886b, 0x0b,
+ 0x886c, 0xd0,
+ 0x886d, 0x78,
+ 0x886e, 0x6a,
+ 0x886f, 0x12,
+ 0x8870, 0x06,
+ 0x8871, 0x8c,
+ 0x8872, 0xd3,
+ 0x8873, 0x12,
+ 0x8874, 0x06,
+ 0x8875, 0x45,
+ 0x8876, 0x40,
+ 0x8877, 0x0a,
+ 0x8878, 0x78,
+ 0x8879, 0x54,
+ 0x887a, 0x12,
+ 0x887b, 0x0b,
+ 0x887c, 0xd0,
+ 0x887d, 0x78,
+ 0x887e, 0x6a,
+ 0x887f, 0x12,
+ 0x8880, 0x06,
+ 0x8881, 0x99,
+ 0x8882, 0x78,
+ 0x8883, 0x61,
+ 0x8884, 0xe6,
+ 0x8885, 0x90,
+ 0x8886, 0x60,
+ 0x8887, 0x01,
+ 0x8888, 0xf0,
+ 0x8889, 0x78,
+ 0x888a, 0x65,
+ 0x888b, 0xe6,
+ 0x888c, 0xa3,
+ 0x888d, 0xf0,
+ 0x888e, 0x78,
+ 0x888f, 0x69,
+ 0x8890, 0xe6,
+ 0x8891, 0xa3,
+ 0x8892, 0xf0,
+ 0x8893, 0x78,
+ 0x8894, 0x55,
+ 0x8895, 0xe6,
+ 0x8896, 0xa3,
+ 0x8897, 0xf0,
+ 0x8898, 0x7d,
+ 0x8899, 0x01,
+ 0x889a, 0x78,
+ 0x889b, 0x61,
+ 0x889c, 0x12,
+ 0x889d, 0x0b,
+ 0x889e, 0xe9,
+ 0x889f, 0x24,
+ 0x88a0, 0x01,
+ 0x88a1, 0x12,
+ 0x88a2, 0x0b,
+ 0x88a3, 0xa6,
+ 0x88a4, 0x78,
+ 0x88a5, 0x65,
+ 0x88a6, 0x12,
+ 0x88a7, 0x0b,
+ 0x88a8, 0xe9,
+ 0x88a9, 0x24,
+ 0x88aa, 0x02,
+ 0x88ab, 0x12,
+ 0x88ac, 0x0b,
+ 0x88ad, 0xa6,
+ 0x88ae, 0x78,
+ 0x88af, 0x69,
+ 0x88b0, 0x12,
+ 0x88b1, 0x0b,
+ 0x88b2, 0xe9,
+ 0x88b3, 0x24,
+ 0x88b4, 0x03,
+ 0x88b5, 0x12,
+ 0x88b6, 0x0b,
+ 0x88b7, 0xa6,
+ 0x88b8, 0x78,
+ 0x88b9, 0x6d,
+ 0x88ba, 0x12,
+ 0x88bb, 0x0b,
+ 0x88bc, 0xe9,
+ 0x88bd, 0x24,
+ 0x88be, 0x04,
+ 0x88bf, 0x12,
+ 0x88c0, 0x0b,
+ 0x88c1, 0xa6,
+ 0x88c2, 0x0d,
+ 0x88c3, 0xbd,
+ 0x88c4, 0x05,
+ 0x88c5, 0xd4,
+ 0x88c6, 0xc2,
+ 0x88c7, 0x0e,
+ 0x88c8, 0xc2,
+ 0x88c9, 0x06,
+ 0x88ca, 0x22,
+ 0x88cb, 0x85,
+ 0x88cc, 0x08,
+ 0x88cd, 0x41,
+ 0x88ce, 0x90,
+ 0x88cf, 0x30,
+ 0x88d0, 0x24,
+ 0x88d1, 0xe0,
+ 0x88d2, 0xf5,
+ 0x88d3, 0x3d,
+ 0x88d4, 0xa3,
+ 0x88d5, 0xe0,
+ 0x88d6, 0xf5,
+ 0x88d7, 0x3e,
+ 0x88d8, 0xa3,
+ 0x88d9, 0xe0,
+ 0x88da, 0xf5,
+ 0x88db, 0x3f,
+ 0x88dc, 0xa3,
+ 0x88dd, 0xe0,
+ 0x88de, 0xf5,
+ 0x88df, 0x40,
+ 0x88e0, 0xa3,
+ 0x88e1, 0xe0,
+ 0x88e2, 0xf5,
+ 0x88e3, 0x3c,
+ 0x88e4, 0xd2,
+ 0x88e5, 0x34,
+ 0x88e6, 0xe5,
+ 0x88e7, 0x41,
+ 0x88e8, 0x12,
+ 0x88e9, 0x06,
+ 0x88ea, 0xb1,
+ 0x88eb, 0x09,
+ 0x88ec, 0x31,
+ 0x88ed, 0x03,
+ 0x88ee, 0x09,
+ 0x88ef, 0x35,
+ 0x88f0, 0x04,
+ 0x88f1, 0x09,
+ 0x88f2, 0x3b,
+ 0x88f3, 0x05,
+ 0x88f4, 0x09,
+ 0x88f5, 0x3e,
+ 0x88f6, 0x06,
+ 0x88f7, 0x09,
+ 0x88f8, 0x41,
+ 0x88f9, 0x07,
+ 0x88fa, 0x09,
+ 0x88fb, 0x4a,
+ 0x88fc, 0x08,
+ 0x88fd, 0x09,
+ 0x88fe, 0x5b,
+ 0x88ff, 0x12,
+ 0x8900, 0x09,
+ 0x8901, 0x73,
+ 0x8902, 0x18,
+ 0x8903, 0x09,
+ 0x8904, 0x89,
+ 0x8905, 0x19,
+ 0x8906, 0x09,
+ 0x8907, 0x5e,
+ 0x8908, 0x1a,
+ 0x8909, 0x09,
+ 0x890a, 0x6a,
+ 0x890b, 0x1b,
+ 0x890c, 0x09,
+ 0x890d, 0xad,
+ 0x890e, 0x80,
+ 0x890f, 0x09,
+ 0x8910, 0xb2,
+ 0x8911, 0x81,
+ 0x8912, 0x0a,
+ 0x8913, 0x1d,
+ 0x8914, 0x8f,
+ 0x8915, 0x0a,
+ 0x8916, 0x09,
+ 0x8917, 0x90,
+ 0x8918, 0x0a,
+ 0x8919, 0x1d,
+ 0x891a, 0x91,
+ 0x891b, 0x0a,
+ 0x891c, 0x1d,
+ 0x891d, 0x92,
+ 0x891e, 0x0a,
+ 0x891f, 0x1d,
+ 0x8920, 0x93,
+ 0x8921, 0x0a,
+ 0x8922, 0x1d,
+ 0x8923, 0x94,
+ 0x8924, 0x0a,
+ 0x8925, 0x1d,
+ 0x8926, 0x98,
+ 0x8927, 0x0a,
+ 0x8928, 0x17,
+ 0x8929, 0x9f,
+ 0x892a, 0x0a,
+ 0x892b, 0x1a,
+ 0x892c, 0xec,
+ 0x892d, 0x00,
+ 0x892e, 0x00,
+ 0x892f, 0x0a,
+ 0x8930, 0x38,
+ 0x8931, 0x12,
+ 0x8932, 0x0f,
+ 0x8933, 0x74,
+ 0x8934, 0x22,
+ 0x8935, 0x12,
+ 0x8936, 0x0f,
+ 0x8937, 0x74,
+ 0x8938, 0xd2,
+ 0x8939, 0x03,
+ 0x893a, 0x22,
+ 0x893b, 0xd2,
+ 0x893c, 0x03,
+ 0x893d, 0x22,
+ 0x893e, 0xc2,
+ 0x893f, 0x03,
+ 0x8940, 0x22,
+ 0x8941, 0xa2,
+ 0x8942, 0x37,
+ 0x8943, 0xe4,
+ 0x8944, 0x33,
+ 0x8945, 0xf5,
+ 0x8946, 0x3c,
+ 0x8947, 0x02,
+ 0x8948, 0x0a,
+ 0x8949, 0x1d,
+ 0x894a, 0xc2,
+ 0x894b, 0x01,
+ 0x894c, 0xc2,
+ 0x894d, 0x02,
+ 0x894e, 0xc2,
+ 0x894f, 0x03,
+ 0x8950, 0x12,
+ 0x8951, 0x0d,
+ 0x8952, 0x0d,
+ 0x8953, 0x75,
+ 0x8954, 0x1e,
+ 0x8955, 0x70,
+ 0x8956, 0xd2,
+ 0x8957, 0x35,
+ 0x8958, 0x02,
+ 0x8959, 0x0a,
+ 0x895a, 0x1d,
+ 0x895b, 0x02,
+ 0x895c, 0x0a,
+ 0x895d, 0x04,
+ 0x895e, 0x85,
+ 0x895f, 0x40,
+ 0x8960, 0x4a,
+ 0x8961, 0x85,
+ 0x8962, 0x3c,
+ 0x8963, 0x4b,
+ 0x8964, 0x12,
+ 0x8965, 0x0a,
+ 0x8966, 0xff,
+ 0x8967, 0x02,
+ 0x8968, 0x0a,
+ 0x8969, 0x1d,
+ 0x896a, 0x85,
+ 0x896b, 0x4a,
+ 0x896c, 0x40,
+ 0x896d, 0x85,
+ 0x896e, 0x4b,
+ 0x896f, 0x3c,
+ 0x8970, 0x02,
+ 0x8971, 0x0a,
+ 0x8972, 0x1d,
+ 0x8973, 0xe4,
+ 0x8974, 0xf5,
+ 0x8975, 0x22,
+ 0x8976, 0xf5,
+ 0x8977, 0x23,
+ 0x8978, 0x85,
+ 0x8979, 0x40,
+ 0x897a, 0x31,
+ 0x897b, 0x85,
+ 0x897c, 0x3f,
+ 0x897d, 0x30,
+ 0x897e, 0x85,
+ 0x897f, 0x3e,
+ 0x8980, 0x2f,
+ 0x8981, 0x85,
+ 0x8982, 0x3d,
+ 0x8983, 0x2e,
+ 0x8984, 0x12,
+ 0x8985, 0x0f,
+ 0x8986, 0x46,
+ 0x8987, 0x80,
+ 0x8988, 0x1f,
+ 0x8989, 0x75,
+ 0x898a, 0x22,
+ 0x898b, 0x00,
+ 0x898c, 0x75,
+ 0x898d, 0x23,
+ 0x898e, 0x01,
+ 0x898f, 0x74,
+ 0x8990, 0xff,
+ 0x8991, 0xf5,
+ 0x8992, 0x2d,
+ 0x8993, 0xf5,
+ 0x8994, 0x2c,
+ 0x8995, 0xf5,
+ 0x8996, 0x2b,
+ 0x8997, 0xf5,
+ 0x8998, 0x2a,
+ 0x8999, 0x12,
+ 0x899a, 0x0f,
+ 0x899b, 0x46,
+ 0x899c, 0x85,
+ 0x899d, 0x2d,
+ 0x899e, 0x40,
+ 0x899f, 0x85,
+ 0x89a0, 0x2c,
+ 0x89a1, 0x3f,
+ 0x89a2, 0x85,
+ 0x89a3, 0x2b,
+ 0x89a4, 0x3e,
+ 0x89a5, 0x85,
+ 0x89a6, 0x2a,
+ 0x89a7, 0x3d,
+ 0x89a8, 0xe4,
+ 0x89a9, 0xf5,
+ 0x89aa, 0x3c,
+ 0x89ab, 0x80,
+ 0x89ac, 0x70,
+ 0x89ad, 0x12,
+ 0x89ae, 0x0f,
+ 0x89af, 0x16,
+ 0x89b0, 0x80,
+ 0x89b1, 0x6b,
+ 0x89b2, 0x85,
+ 0x89b3, 0x3d,
+ 0x89b4, 0x45,
+ 0x89b5, 0x85,
+ 0x89b6, 0x3e,
+ 0x89b7, 0x46,
+ 0x89b8, 0xe5,
+ 0x89b9, 0x47,
+ 0x89ba, 0xc3,
+ 0x89bb, 0x13,
+ 0x89bc, 0xff,
+ 0x89bd, 0xe5,
+ 0x89be, 0x45,
+ 0x89bf, 0xc3,
+ 0x89c0, 0x9f,
+ 0x89c1, 0x50,
+ 0x89c2, 0x02,
+ 0x89c3, 0x8f,
+ 0x89c4, 0x45,
+ 0x89c5, 0xe5,
+ 0x89c6, 0x48,
+ 0x89c7, 0xc3,
+ 0x89c8, 0x13,
+ 0x89c9, 0xff,
+ 0x89ca, 0xe5,
+ 0x89cb, 0x46,
+ 0x89cc, 0xc3,
+ 0x89cd, 0x9f,
+ 0x89ce, 0x50,
+ 0x89cf, 0x02,
+ 0x89d0, 0x8f,
+ 0x89d1, 0x46,
+ 0x89d2, 0xe5,
+ 0x89d3, 0x47,
+ 0x89d4, 0xc3,
+ 0x89d5, 0x13,
+ 0x89d6, 0xff,
+ 0x89d7, 0xfd,
+ 0x89d8, 0xe5,
+ 0x89d9, 0x45,
+ 0x89da, 0x2d,
+ 0x89db, 0xfd,
+ 0x89dc, 0xe4,
+ 0x89dd, 0x33,
+ 0x89de, 0xfc,
+ 0x89df, 0xe5,
+ 0x89e0, 0x44,
+ 0x89e1, 0x12,
+ 0x89e2, 0x0f,
+ 0x89e3, 0x90,
+ 0x89e4, 0x40,
+ 0x89e5, 0x05,
+ 0x89e6, 0xe5,
+ 0x89e7, 0x44,
+ 0x89e8, 0x9f,
+ 0x89e9, 0xf5,
+ 0x89ea, 0x45,
+ 0x89eb, 0xe5,
+ 0x89ec, 0x48,
+ 0x89ed, 0xc3,
+ 0x89ee, 0x13,
+ 0x89ef, 0xff,
+ 0x89f0, 0xfd,
+ 0x89f1, 0xe5,
+ 0x89f2, 0x46,
+ 0x89f3, 0x2d,
+ 0x89f4, 0xfd,
+ 0x89f5, 0xe4,
+ 0x89f6, 0x33,
+ 0x89f7, 0xfc,
+ 0x89f8, 0xe5,
+ 0x89f9, 0x43,
+ 0x89fa, 0x12,
+ 0x89fb, 0x0f,
+ 0x89fc, 0x90,
+ 0x89fd, 0x40,
+ 0x89fe, 0x05,
+ 0x89ff, 0xe5,
+ 0x8a00, 0x43,
+ 0x8a01, 0x9f,
+ 0x8a02, 0xf5,
+ 0x8a03, 0x46,
+ 0x8a04, 0x12,
+ 0x8a05, 0x06,
+ 0x8a06, 0xd7,
+ 0x8a07, 0x80,
+ 0x8a08, 0x14,
+ 0x8a09, 0x85,
+ 0x8a0a, 0x40,
+ 0x8a0b, 0x48,
+ 0x8a0c, 0x85,
+ 0x8a0d, 0x3f,
+ 0x8a0e, 0x47,
+ 0x8a0f, 0x85,
+ 0x8a10, 0x3e,
+ 0x8a11, 0x46,
+ 0x8a12, 0x85,
+ 0x8a13, 0x3d,
+ 0x8a14, 0x45,
+ 0x8a15, 0x80,
+ 0x8a16, 0x06,
+ 0x8a17, 0x02,
+ 0x8a18, 0x06,
+ 0x8a19, 0xd7,
+ 0x8a1a, 0x12,
+ 0x8a1b, 0x0d,
+ 0x8a1c, 0x7e,
+ 0x8a1d, 0x90,
+ 0x8a1e, 0x30,
+ 0x8a1f, 0x24,
+ 0x8a20, 0xe5,
+ 0x8a21, 0x3d,
+ 0x8a22, 0xf0,
+ 0x8a23, 0xa3,
+ 0x8a24, 0xe5,
+ 0x8a25, 0x3e,
+ 0x8a26, 0xf0,
+ 0x8a27, 0xa3,
+ 0x8a28, 0xe5,
+ 0x8a29, 0x3f,
+ 0x8a2a, 0xf0,
+ 0x8a2b, 0xa3,
+ 0x8a2c, 0xe5,
+ 0x8a2d, 0x40,
+ 0x8a2e, 0xf0,
+ 0x8a2f, 0xa3,
+ 0x8a30, 0xe5,
+ 0x8a31, 0x3c,
+ 0x8a32, 0xf0,
+ 0x8a33, 0x90,
+ 0x8a34, 0x30,
+ 0x8a35, 0x23,
+ 0x8a36, 0xe4,
+ 0x8a37, 0xf0,
+ 0x8a38, 0x22,
+ 0x8a39, 0xc0,
+ 0x8a3a, 0xe0,
+ 0x8a3b, 0xc0,
+ 0x8a3c, 0x83,
+ 0x8a3d, 0xc0,
+ 0x8a3e, 0x82,
+ 0x8a3f, 0xc0,
+ 0x8a40, 0xd0,
+ 0x8a41, 0x90,
+ 0x8a42, 0x3f,
+ 0x8a43, 0x0c,
+ 0x8a44, 0xe0,
+ 0x8a45, 0xf5,
+ 0x8a46, 0x32,
+ 0x8a47, 0xe5,
+ 0x8a48, 0x32,
+ 0x8a49, 0x30,
+ 0x8a4a, 0xe3,
+ 0x8a4b, 0x74,
+ 0x8a4c, 0x30,
+ 0x8a4d, 0x36,
+ 0x8a4e, 0x66,
+ 0x8a4f, 0x90,
+ 0x8a50, 0x60,
+ 0x8a51, 0x19,
+ 0x8a52, 0xe0,
+ 0x8a53, 0xf5,
+ 0x8a54, 0x0a,
+ 0x8a55, 0xa3,
+ 0x8a56, 0xe0,
+ 0x8a57, 0xf5,
+ 0x8a58, 0x0b,
+ 0x8a59, 0x90,
+ 0x8a5a, 0x60,
+ 0x8a5b, 0x1d,
+ 0x8a5c, 0xe0,
+ 0x8a5d, 0xf5,
+ 0x8a5e, 0x14,
+ 0x8a5f, 0xa3,
+ 0x8a60, 0xe0,
+ 0x8a61, 0xf5,
+ 0x8a62, 0x15,
+ 0x8a63, 0x90,
+ 0x8a64, 0x60,
+ 0x8a65, 0x21,
+ 0x8a66, 0xe0,
+ 0x8a67, 0xf5,
+ 0x8a68, 0x0c,
+ 0x8a69, 0xa3,
+ 0x8a6a, 0xe0,
+ 0x8a6b, 0xf5,
+ 0x8a6c, 0x0d,
+ 0x8a6d, 0x90,
+ 0x8a6e, 0x60,
+ 0x8a6f, 0x29,
+ 0x8a70, 0xe0,
+ 0x8a71, 0xf5,
+ 0x8a72, 0x0e,
+ 0x8a73, 0xa3,
+ 0x8a74, 0xe0,
+ 0x8a75, 0xf5,
+ 0x8a76, 0x0f,
+ 0x8a77, 0x90,
+ 0x8a78, 0x60,
+ 0x8a79, 0x31,
+ 0x8a7a, 0xe0,
+ 0x8a7b, 0xf5,
+ 0x8a7c, 0x10,
+ 0x8a7d, 0xa3,
+ 0x8a7e, 0xe0,
+ 0x8a7f, 0xf5,
+ 0x8a80, 0x11,
+ 0x8a81, 0x90,
+ 0x8a82, 0x60,
+ 0x8a83, 0x39,
+ 0x8a84, 0xe0,
+ 0x8a85, 0xf5,
+ 0x8a86, 0x12,
+ 0x8a87, 0xa3,
+ 0x8a88, 0xe0,
+ 0x8a89, 0xf5,
+ 0x8a8a, 0x13,
+ 0x8a8b, 0x30,
+ 0x8a8c, 0x01,
+ 0x8a8d, 0x06,
+ 0x8a8e, 0x30,
+ 0x8a8f, 0x33,
+ 0x8a90, 0x03,
+ 0x8a91, 0xd3,
+ 0x8a92, 0x80,
+ 0x8a93, 0x01,
+ 0x8a94, 0xc3,
+ 0x8a95, 0x92,
+ 0x8a96, 0x09,
+ 0x8a97, 0x30,
+ 0x8a98, 0x02,
+ 0x8a99, 0x06,
+ 0x8a9a, 0x30,
+ 0x8a9b, 0x33,
+ 0x8a9c, 0x03,
+ 0x8a9d, 0xd3,
+ 0x8a9e, 0x80,
+ 0x8a9f, 0x01,
+ 0x8aa0, 0xc3,
+ 0x8aa1, 0x92,
+ 0x8aa2, 0x0a,
+ 0x8aa3, 0x30,
+ 0x8aa4, 0x33,
+ 0x8aa5, 0x0c,
+ 0x8aa6, 0x30,
+ 0x8aa7, 0x03,
+ 0x8aa8, 0x09,
+ 0x8aa9, 0x20,
+ 0x8aaa, 0x02,
+ 0x8aab, 0x06,
+ 0x8aac, 0x20,
+ 0x8aad, 0x01,
+ 0x8aae, 0x03,
+ 0x8aaf, 0xd3,
+ 0x8ab0, 0x80,
+ 0x8ab1, 0x01,
+ 0x8ab2, 0xc3,
+ 0x8ab3, 0x92,
+ 0x8ab4, 0x0b,
+ 0x8ab5, 0x90,
+ 0x8ab6, 0x30,
+ 0x8ab7, 0x01,
+ 0x8ab8, 0xe0,
+ 0x8ab9, 0x44,
+ 0x8aba, 0x40,
+ 0x8abb, 0xf0,
+ 0x8abc, 0xe0,
+ 0x8abd, 0x54,
+ 0x8abe, 0xbf,
+ 0x8abf, 0xf0,
+ 0x8ac0, 0xe5,
+ 0x8ac1, 0x32,
+ 0x8ac2, 0x30,
+ 0x8ac3, 0xe1,
+ 0x8ac4, 0x14,
+ 0x8ac5, 0x30,
+ 0x8ac6, 0x34,
+ 0x8ac7, 0x11,
+ 0x8ac8, 0x90,
+ 0x8ac9, 0x30,
+ 0x8aca, 0x22,
+ 0x8acb, 0xe0,
+ 0x8acc, 0xf5,
+ 0x8acd, 0x08,
+ 0x8ace, 0xe4,
+ 0x8acf, 0xf0,
+ 0x8ad0, 0x30,
+ 0x8ad1, 0x00,
+ 0x8ad2, 0x03,
+ 0x8ad3, 0xd3,
+ 0x8ad4, 0x80,
+ 0x8ad5, 0x01,
+ 0x8ad6, 0xc3,
+ 0x8ad7, 0x92,
+ 0x8ad8, 0x08,
+ 0x8ad9, 0xe5,
+ 0x8ada, 0x32,
+ 0x8adb, 0x30,
+ 0x8adc, 0xe5,
+ 0x8add, 0x12,
+ 0x8ade, 0x90,
+ 0x8adf, 0x56,
+ 0x8ae0, 0xa1,
+ 0x8ae1, 0xe0,
+ 0x8ae2, 0xf5,
+ 0x8ae3, 0x09,
+ 0x8ae4, 0x30,
+ 0x8ae5, 0x31,
+ 0x8ae6, 0x09,
+ 0x8ae7, 0x30,
+ 0x8ae8, 0x05,
+ 0x8ae9, 0x03,
+ 0x8aea, 0xd3,
+ 0x8aeb, 0x80,
+ 0x8aec, 0x01,
+ 0x8aed, 0xc3,
+ 0x8aee, 0x92,
+ 0x8aef, 0x0d,
+ 0x8af0, 0x90,
+ 0x8af1, 0x3f,
+ 0x8af2, 0x0c,
+ 0x8af3, 0xe5,
+ 0x8af4, 0x32,
+ 0x8af5, 0xf0,
+ 0x8af6, 0xd0,
+ 0x8af7, 0xd0,
+ 0x8af8, 0xd0,
+ 0x8af9, 0x82,
+ 0x8afa, 0xd0,
+ 0x8afb, 0x83,
+ 0x8afc, 0xd0,
+ 0x8afd, 0xe0,
+ 0x8afe, 0x32,
+ 0x8aff, 0x90,
+ 0x8b00, 0x0e,
+ 0x8b01, 0x7e,
+ 0x8b02, 0xe4,
+ 0x8b03, 0x93,
+ 0x8b04, 0xfe,
+ 0x8b05, 0x74,
+ 0x8b06, 0x01,
+ 0x8b07, 0x93,
+ 0x8b08, 0xff,
+ 0x8b09, 0xc3,
+ 0x8b0a, 0x90,
+ 0x8b0b, 0x0e,
+ 0x8b0c, 0x7c,
+ 0x8b0d, 0x74,
+ 0x8b0e, 0x01,
+ 0x8b0f, 0x93,
+ 0x8b10, 0x9f,
+ 0x8b11, 0xff,
+ 0x8b12, 0xe4,
+ 0x8b13, 0x93,
+ 0x8b14, 0x9e,
+ 0x8b15, 0xfe,
+ 0x8b16, 0xe4,
+ 0x8b17, 0x8f,
+ 0x8b18, 0x3b,
+ 0x8b19, 0x8e,
+ 0x8b1a, 0x3a,
+ 0x8b1b, 0xf5,
+ 0x8b1c, 0x39,
+ 0x8b1d, 0xf5,
+ 0x8b1e, 0x38,
+ 0x8b1f, 0xab,
+ 0x8b20, 0x3b,
+ 0x8b21, 0xaa,
+ 0x8b22, 0x3a,
+ 0x8b23, 0xa9,
+ 0x8b24, 0x39,
+ 0x8b25, 0xa8,
+ 0x8b26, 0x38,
+ 0x8b27, 0xaf,
+ 0x8b28, 0x4b,
+ 0x8b29, 0xfc,
+ 0x8b2a, 0xfd,
+ 0x8b2b, 0xfe,
+ 0x8b2c, 0x12,
+ 0x8b2d, 0x05,
+ 0x8b2e, 0x28,
+ 0x8b2f, 0x12,
+ 0x8b30, 0x0d,
+ 0x8b31, 0xe1,
+ 0x8b32, 0xe4,
+ 0x8b33, 0x7b,
+ 0x8b34, 0xff,
+ 0x8b35, 0xfa,
+ 0x8b36, 0xf9,
+ 0x8b37, 0xf8,
+ 0x8b38, 0x12,
+ 0x8b39, 0x05,
+ 0x8b3a, 0xb3,
+ 0x8b3b, 0x12,
+ 0x8b3c, 0x0d,
+ 0x8b3d, 0xe1,
+ 0x8b3e, 0x90,
+ 0x8b3f, 0x0e,
+ 0x8b40, 0x69,
+ 0x8b41, 0xe4,
+ 0x8b42, 0x12,
+ 0x8b43, 0x0d,
+ 0x8b44, 0xf6,
+ 0x8b45, 0x12,
+ 0x8b46, 0x0d,
+ 0x8b47, 0xe1,
+ 0x8b48, 0xe4,
+ 0x8b49, 0x85,
+ 0x8b4a, 0x4a,
+ 0x8b4b, 0x37,
+ 0x8b4c, 0xf5,
+ 0x8b4d, 0x36,
+ 0x8b4e, 0xf5,
+ 0x8b4f, 0x35,
+ 0x8b50, 0xf5,
+ 0x8b51, 0x34,
+ 0x8b52, 0xaf,
+ 0x8b53, 0x37,
+ 0x8b54, 0xae,
+ 0x8b55, 0x36,
+ 0x8b56, 0xad,
+ 0x8b57, 0x35,
+ 0x8b58, 0xac,
+ 0x8b59, 0x34,
+ 0x8b5a, 0xa3,
+ 0x8b5b, 0x12,
+ 0x8b5c, 0x0d,
+ 0x8b5d, 0xf6,
+ 0x8b5e, 0x8f,
+ 0x8b5f, 0x37,
+ 0x8b60, 0x8e,
+ 0x8b61, 0x36,
+ 0x8b62, 0x8d,
+ 0x8b63, 0x35,
+ 0x8b64, 0x8c,
+ 0x8b65, 0x34,
+ 0x8b66, 0xe5,
+ 0x8b67, 0x3b,
+ 0x8b68, 0x45,
+ 0x8b69, 0x37,
+ 0x8b6a, 0xf5,
+ 0x8b6b, 0x3b,
+ 0x8b6c, 0xe5,
+ 0x8b6d, 0x3a,
+ 0x8b6e, 0x45,
+ 0x8b6f, 0x36,
+ 0x8b70, 0xf5,
+ 0x8b71, 0x3a,
+ 0x8b72, 0xe5,
+ 0x8b73, 0x39,
+ 0x8b74, 0x45,
+ 0x8b75, 0x35,
+ 0x8b76, 0xf5,
+ 0x8b77, 0x39,
+ 0x8b78, 0xe5,
+ 0x8b79, 0x38,
+ 0x8b7a, 0x45,
+ 0x8b7b, 0x34,
+ 0x8b7c, 0xf5,
+ 0x8b7d, 0x38,
+ 0x8b7e, 0xe4,
+ 0x8b7f, 0xf5,
+ 0x8b80, 0x22,
+ 0x8b81, 0xf5,
+ 0x8b82, 0x23,
+ 0x8b83, 0x85,
+ 0x8b84, 0x3b,
+ 0x8b85, 0x31,
+ 0x8b86, 0x85,
+ 0x8b87, 0x3a,
+ 0x8b88, 0x30,
+ 0x8b89, 0x85,
+ 0x8b8a, 0x39,
+ 0x8b8b, 0x2f,
+ 0x8b8c, 0x85,
+ 0x8b8d, 0x38,
+ 0x8b8e, 0x2e,
+ 0x8b8f, 0x02,
+ 0x8b90, 0x0f,
+ 0x8b91, 0x46,
+ 0x8b92, 0xe0,
+ 0x8b93, 0xa3,
+ 0x8b94, 0xe0,
+ 0x8b95, 0x75,
+ 0x8b96, 0xf0,
+ 0x8b97, 0x02,
+ 0x8b98, 0xa4,
+ 0x8b99, 0xff,
+ 0x8b9a, 0xae,
+ 0x8b9b, 0xf0,
+ 0x8b9c, 0xc3,
+ 0x8b9d, 0x08,
+ 0x8b9e, 0xe6,
+ 0x8b9f, 0x9f,
+ 0x8ba0, 0xf6,
+ 0x8ba1, 0x18,
+ 0x8ba2, 0xe6,
+ 0x8ba3, 0x9e,
+ 0x8ba4, 0xf6,
+ 0x8ba5, 0x22,
+ 0x8ba6, 0xff,
+ 0x8ba7, 0xe5,
+ 0x8ba8, 0xf0,
+ 0x8ba9, 0x34,
+ 0x8baa, 0x60,
+ 0x8bab, 0x8f,
+ 0x8bac, 0x82,
+ 0x8bad, 0xf5,
+ 0x8bae, 0x83,
+ 0x8baf, 0xec,
+ 0x8bb0, 0xf0,
+ 0x8bb1, 0x22,
+ 0x8bb2, 0x78,
+ 0x8bb3, 0x52,
+ 0x8bb4, 0x7e,
+ 0x8bb5, 0x00,
+ 0x8bb6, 0xe6,
+ 0x8bb7, 0xfc,
+ 0x8bb8, 0x08,
+ 0x8bb9, 0xe6,
+ 0x8bba, 0xfd,
+ 0x8bbb, 0x02,
+ 0x8bbc, 0x04,
+ 0x8bbd, 0xc1,
+ 0x8bbe, 0xe4,
+ 0x8bbf, 0xfc,
+ 0x8bc0, 0xfd,
+ 0x8bc1, 0x12,
+ 0x8bc2, 0x06,
+ 0x8bc3, 0x99,
+ 0x8bc4, 0x78,
+ 0x8bc5, 0x5c,
+ 0x8bc6, 0xe6,
+ 0x8bc7, 0xc3,
+ 0x8bc8, 0x13,
+ 0x8bc9, 0xfe,
+ 0x8bca, 0x08,
+ 0x8bcb, 0xe6,
+ 0x8bcc, 0x13,
+ 0x8bcd, 0x22,
+ 0x8bce, 0x78,
+ 0x8bcf, 0x52,
+ 0x8bd0, 0xe6,
+ 0x8bd1, 0xfe,
+ 0x8bd2, 0x08,
+ 0x8bd3, 0xe6,
+ 0x8bd4, 0xff,
+ 0x8bd5, 0xe4,
+ 0x8bd6, 0xfc,
+ 0x8bd7, 0xfd,
+ 0x8bd8, 0x22,
+ 0x8bd9, 0xe7,
+ 0x8bda, 0xc4,
+ 0x8bdb, 0xf8,
+ 0x8bdc, 0x54,
+ 0x8bdd, 0xf0,
+ 0x8bde, 0xc8,
+ 0x8bdf, 0x68,
+ 0x8be0, 0xf7,
+ 0x8be1, 0x09,
+ 0x8be2, 0xe7,
+ 0x8be3, 0xc4,
+ 0x8be4, 0x54,
+ 0x8be5, 0x0f,
+ 0x8be6, 0x48,
+ 0x8be7, 0xf7,
+ 0x8be8, 0x22,
+ 0x8be9, 0xe6,
+ 0x8bea, 0xfc,
+ 0x8beb, 0xed,
+ 0x8bec, 0x75,
+ 0x8bed, 0xf0,
+ 0x8bee, 0x04,
+ 0x8bef, 0xa4,
+ 0x8bf0, 0x22,
+ 0x8bf1, 0x12,
+ 0x8bf2, 0x06,
+ 0x8bf3, 0x7c,
+ 0x8bf4, 0x8f,
+ 0x8bf5, 0x48,
+ 0x8bf6, 0x8e,
+ 0x8bf7, 0x47,
+ 0x8bf8, 0x8d,
+ 0x8bf9, 0x46,
+ 0x8bfa, 0x8c,
+ 0x8bfb, 0x45,
+ 0x8bfc, 0x22,
+ 0x8bfd, 0xe0,
+ 0x8bfe, 0xfe,
+ 0x8bff, 0xa3,
+ 0x8c00, 0xe0,
+ 0x8c01, 0xfd,
+ 0x8c02, 0xee,
+ 0x8c03, 0xf6,
+ 0x8c04, 0xed,
+ 0x8c05, 0x08,
+ 0x8c06, 0xf6,
+ 0x8c07, 0x22,
+ 0x8c08, 0x13,
+ 0x8c09, 0xff,
+ 0x8c0a, 0xc3,
+ 0x8c0b, 0xe6,
+ 0x8c0c, 0x9f,
+ 0x8c0d, 0xff,
+ 0x8c0e, 0x18,
+ 0x8c0f, 0xe6,
+ 0x8c10, 0x9e,
+ 0x8c11, 0xfe,
+ 0x8c12, 0x22,
+ 0x8c13, 0xe6,
+ 0x8c14, 0xc3,
+ 0x8c15, 0x13,
+ 0x8c16, 0xf7,
+ 0x8c17, 0x08,
+ 0x8c18, 0xe6,
+ 0x8c19, 0x13,
+ 0x8c1a, 0x09,
+ 0x8c1b, 0xf7,
+ 0x8c1c, 0x22,
+ 0x8c1d, 0xad,
+ 0x8c1e, 0x39,
+ 0x8c1f, 0xac,
+ 0x8c20, 0x38,
+ 0x8c21, 0xfa,
+ 0x8c22, 0xf9,
+ 0x8c23, 0xf8,
+ 0x8c24, 0x12,
+ 0x8c25, 0x05,
+ 0x8c26, 0x28,
+ 0x8c27, 0x8f,
+ 0x8c28, 0x3b,
+ 0x8c29, 0x8e,
+ 0x8c2a, 0x3a,
+ 0x8c2b, 0x8d,
+ 0x8c2c, 0x39,
+ 0x8c2d, 0x8c,
+ 0x8c2e, 0x38,
+ 0x8c2f, 0xab,
+ 0x8c30, 0x37,
+ 0x8c31, 0xaa,
+ 0x8c32, 0x36,
+ 0x8c33, 0xa9,
+ 0x8c34, 0x35,
+ 0x8c35, 0xa8,
+ 0x8c36, 0x34,
+ 0x8c37, 0x22,
+ 0x8c38, 0x93,
+ 0x8c39, 0xff,
+ 0x8c3a, 0xe4,
+ 0x8c3b, 0xfc,
+ 0x8c3c, 0xfd,
+ 0x8c3d, 0xfe,
+ 0x8c3e, 0x12,
+ 0x8c3f, 0x05,
+ 0x8c40, 0x28,
+ 0x8c41, 0x8f,
+ 0x8c42, 0x37,
+ 0x8c43, 0x8e,
+ 0x8c44, 0x36,
+ 0x8c45, 0x8d,
+ 0x8c46, 0x35,
+ 0x8c47, 0x8c,
+ 0x8c48, 0x34,
+ 0x8c49, 0x22,
+ 0x8c4a, 0x78,
+ 0x8c4b, 0x84,
+ 0x8c4c, 0xe6,
+ 0x8c4d, 0xfe,
+ 0x8c4e, 0x08,
+ 0x8c4f, 0xe6,
+ 0x8c50, 0xff,
+ 0x8c51, 0xe4,
+ 0x8c52, 0x8f,
+ 0x8c53, 0x37,
+ 0x8c54, 0x8e,
+ 0x8c55, 0x36,
+ 0x8c56, 0xf5,
+ 0x8c57, 0x35,
+ 0x8c58, 0xf5,
+ 0x8c59, 0x34,
+ 0x8c5a, 0x22,
+ 0x8c5b, 0x90,
+ 0x8c5c, 0x0e,
+ 0x8c5d, 0x8c,
+ 0x8c5e, 0xe4,
+ 0x8c5f, 0x93,
+ 0x8c60, 0x25,
+ 0x8c61, 0xe0,
+ 0x8c62, 0x24,
+ 0x8c63, 0x0a,
+ 0x8c64, 0xf8,
+ 0x8c65, 0xe6,
+ 0x8c66, 0xfe,
+ 0x8c67, 0x08,
+ 0x8c68, 0xe6,
+ 0x8c69, 0xff,
+ 0x8c6a, 0x22,
+ 0x8c6b, 0xe6,
+ 0x8c6c, 0xfe,
+ 0x8c6d, 0x08,
+ 0x8c6e, 0xe6,
+ 0x8c6f, 0xff,
+ 0x8c70, 0xe4,
+ 0x8c71, 0x8f,
+ 0x8c72, 0x3b,
+ 0x8c73, 0x8e,
+ 0x8c74, 0x3a,
+ 0x8c75, 0xf5,
+ 0x8c76, 0x39,
+ 0x8c77, 0xf5,
+ 0x8c78, 0x38,
+ 0x8c79, 0x22,
+ 0x8c7a, 0x78,
+ 0x8c7b, 0x4e,
+ 0x8c7c, 0xe6,
+ 0x8c7d, 0xfe,
+ 0x8c7e, 0x08,
+ 0x8c7f, 0xe6,
+ 0x8c80, 0xff,
+ 0x8c81, 0x22,
+ 0x8c82, 0xef,
+ 0x8c83, 0x25,
+ 0x8c84, 0xe0,
+ 0x8c85, 0x24,
+ 0x8c86, 0x4e,
+ 0x8c87, 0xf8,
+ 0x8c88, 0xe6,
+ 0x8c89, 0xfc,
+ 0x8c8a, 0x08,
+ 0x8c8b, 0xe6,
+ 0x8c8c, 0xfd,
+ 0x8c8d, 0x22,
+ 0x8c8e, 0x78,
+ 0x8c8f, 0x89,
+ 0x8c90, 0xef,
+ 0x8c91, 0x26,
+ 0x8c92, 0xf6,
+ 0x8c93, 0x18,
+ 0x8c94, 0xe4,
+ 0x8c95, 0x36,
+ 0x8c96, 0xf6,
+ 0x8c97, 0x22,
+ 0x8c98, 0x75,
+ 0x8c99, 0x89,
+ 0x8c9a, 0x03,
+ 0x8c9b, 0x75,
+ 0x8c9c, 0xa8,
+ 0x8c9d, 0x01,
+ 0x8c9e, 0x75,
+ 0x8c9f, 0xb8,
+ 0x8ca0, 0x04,
+ 0x8ca1, 0x75,
+ 0x8ca2, 0x34,
+ 0x8ca3, 0xff,
+ 0x8ca4, 0x75,
+ 0x8ca5, 0x35,
+ 0x8ca6, 0x0e,
+ 0x8ca7, 0x75,
+ 0x8ca8, 0x36,
+ 0x8ca9, 0x15,
+ 0x8caa, 0x75,
+ 0x8cab, 0x37,
+ 0x8cac, 0x0d,
+ 0x8cad, 0x12,
+ 0x8cae, 0x0e,
+ 0x8caf, 0x9a,
+ 0x8cb0, 0x12,
+ 0x8cb1, 0x00,
+ 0x8cb2, 0x09,
+ 0x8cb3, 0x12,
+ 0x8cb4, 0x0f,
+ 0x8cb5, 0x16,
+ 0x8cb6, 0x12,
+ 0x8cb7, 0x00,
+ 0x8cb8, 0x06,
+ 0x8cb9, 0xd2,
+ 0x8cba, 0x00,
+ 0x8cbb, 0xd2,
+ 0x8cbc, 0x34,
+ 0x8cbd, 0xd2,
+ 0x8cbe, 0xaf,
+ 0x8cbf, 0x75,
+ 0x8cc0, 0x34,
+ 0x8cc1, 0xff,
+ 0x8cc2, 0x75,
+ 0x8cc3, 0x35,
+ 0x8cc4, 0x0e,
+ 0x8cc5, 0x75,
+ 0x8cc6, 0x36,
+ 0x8cc7, 0x49,
+ 0x8cc8, 0x75,
+ 0x8cc9, 0x37,
+ 0x8cca, 0x03,
+ 0x8ccb, 0x12,
+ 0x8ccc, 0x0e,
+ 0x8ccd, 0x9a,
+ 0x8cce, 0x30,
+ 0x8ccf, 0x08,
+ 0x8cd0, 0x09,
+ 0x8cd1, 0xc2,
+ 0x8cd2, 0x34,
+ 0x8cd3, 0x12,
+ 0x8cd4, 0x08,
+ 0x8cd5, 0xcb,
+ 0x8cd6, 0xc2,
+ 0x8cd7, 0x08,
+ 0x8cd8, 0xd2,
+ 0x8cd9, 0x34,
+ 0x8cda, 0x30,
+ 0x8cdb, 0x0b,
+ 0x8cdc, 0x09,
+ 0x8cdd, 0xc2,
+ 0x8cde, 0x36,
+ 0x8cdf, 0x12,
+ 0x8ce0, 0x02,
+ 0x8ce1, 0x6c,
+ 0x8ce2, 0xc2,
+ 0x8ce3, 0x0b,
+ 0x8ce4, 0xd2,
+ 0x8ce5, 0x36,
+ 0x8ce6, 0x30,
+ 0x8ce7, 0x09,
+ 0x8ce8, 0x09,
+ 0x8ce9, 0xc2,
+ 0x8cea, 0x36,
+ 0x8ceb, 0x12,
+ 0x8cec, 0x00,
+ 0x8ced, 0x0e,
+ 0x8cee, 0xc2,
+ 0x8cef, 0x09,
+ 0x8cf0, 0xd2,
+ 0x8cf1, 0x36,
+ 0x8cf2, 0x30,
+ 0x8cf3, 0x0e,
+ 0x8cf4, 0x03,
+ 0x8cf5, 0x12,
+ 0x8cf6, 0x06,
+ 0x8cf7, 0xd7,
+ 0x8cf8, 0x30,
+ 0x8cf9, 0x35,
+ 0x8cfa, 0xd3,
+ 0x8cfb, 0x90,
+ 0x8cfc, 0x30,
+ 0x8cfd, 0x29,
+ 0x8cfe, 0xe5,
+ 0x8cff, 0x1e,
+ 0x8d00, 0xf0,
+ 0x8d01, 0xb4,
+ 0x8d02, 0x10,
+ 0x8d03, 0x05,
+ 0x8d04, 0x90,
+ 0x8d05, 0x30,
+ 0x8d06, 0x23,
+ 0x8d07, 0xe4,
+ 0x8d08, 0xf0,
+ 0x8d09, 0xc2,
+ 0x8d0a, 0x35,
+ 0x8d0b, 0x80,
+ 0x8d0c, 0xc1,
+ 0x8d0d, 0xe4,
+ 0x8d0e, 0xf5,
+ 0x8d0f, 0x4b,
+ 0x8d10, 0x90,
+ 0x8d11, 0x0e,
+ 0x8d12, 0x7a,
+ 0x8d13, 0x93,
+ 0x8d14, 0xff,
+ 0x8d15, 0xe4,
+ 0x8d16, 0x8f,
+ 0x8d17, 0x37,
+ 0x8d18, 0xf5,
+ 0x8d19, 0x36,
+ 0x8d1a, 0xf5,
+ 0x8d1b, 0x35,
+ 0x8d1c, 0xf5,
+ 0x8d1d, 0x34,
+ 0x8d1e, 0xaf,
+ 0x8d1f, 0x37,
+ 0x8d20, 0xae,
+ 0x8d21, 0x36,
+ 0x8d22, 0xad,
+ 0x8d23, 0x35,
+ 0x8d24, 0xac,
+ 0x8d25, 0x34,
+ 0x8d26, 0x90,
+ 0x8d27, 0x0e,
+ 0x8d28, 0x6a,
+ 0x8d29, 0x12,
+ 0x8d2a, 0x0d,
+ 0x8d2b, 0xf6,
+ 0x8d2c, 0x8f,
+ 0x8d2d, 0x37,
+ 0x8d2e, 0x8e,
+ 0x8d2f, 0x36,
+ 0x8d30, 0x8d,
+ 0x8d31, 0x35,
+ 0x8d32, 0x8c,
+ 0x8d33, 0x34,
+ 0x8d34, 0x90,
+ 0x8d35, 0x0e,
+ 0x8d36, 0x72,
+ 0x8d37, 0x12,
+ 0x8d38, 0x06,
+ 0x8d39, 0x7c,
+ 0x8d3a, 0xef,
+ 0x8d3b, 0x45,
+ 0x8d3c, 0x37,
+ 0x8d3d, 0xf5,
+ 0x8d3e, 0x37,
+ 0x8d3f, 0xee,
+ 0x8d40, 0x45,
+ 0x8d41, 0x36,
+ 0x8d42, 0xf5,
+ 0x8d43, 0x36,
+ 0x8d44, 0xed,
+ 0x8d45, 0x45,
+ 0x8d46, 0x35,
+ 0x8d47, 0xf5,
+ 0x8d48, 0x35,
+ 0x8d49, 0xec,
+ 0x8d4a, 0x45,
+ 0x8d4b, 0x34,
+ 0x8d4c, 0xf5,
+ 0x8d4d, 0x34,
+ 0x8d4e, 0xe4,
+ 0x8d4f, 0xf5,
+ 0x8d50, 0x22,
+ 0x8d51, 0xf5,
+ 0x8d52, 0x23,
+ 0x8d53, 0x85,
+ 0x8d54, 0x37,
+ 0x8d55, 0x31,
+ 0x8d56, 0x85,
+ 0x8d57, 0x36,
+ 0x8d58, 0x30,
+ 0x8d59, 0x85,
+ 0x8d5a, 0x35,
+ 0x8d5b, 0x2f,
+ 0x8d5c, 0x85,
+ 0x8d5d, 0x34,
+ 0x8d5e, 0x2e,
+ 0x8d5f, 0x12,
+ 0x8d60, 0x0f,
+ 0x8d61, 0x46,
+ 0x8d62, 0xe4,
+ 0x8d63, 0xf5,
+ 0x8d64, 0x22,
+ 0x8d65, 0xf5,
+ 0x8d66, 0x23,
+ 0x8d67, 0x90,
+ 0x8d68, 0x0e,
+ 0x8d69, 0x72,
+ 0x8d6a, 0x12,
+ 0x8d6b, 0x0d,
+ 0x8d6c, 0xea,
+ 0x8d6d, 0x12,
+ 0x8d6e, 0x0f,
+ 0x8d6f, 0x46,
+ 0x8d70, 0xe4,
+ 0x8d71, 0xf5,
+ 0x8d72, 0x22,
+ 0x8d73, 0xf5,
+ 0x8d74, 0x23,
+ 0x8d75, 0x90,
+ 0x8d76, 0x0e,
+ 0x8d77, 0x6e,
+ 0x8d78, 0x12,
+ 0x8d79, 0x0d,
+ 0x8d7a, 0xea,
+ 0x8d7b, 0x02,
+ 0x8d7c, 0x0f,
+ 0x8d7d, 0x46,
+ 0x8d7e, 0xe5,
+ 0x8d7f, 0x40,
+ 0x8d80, 0x24,
+ 0x8d81, 0xf2,
+ 0x8d82, 0xf5,
+ 0x8d83, 0x37,
+ 0x8d84, 0xe5,
+ 0x8d85, 0x3f,
+ 0x8d86, 0x34,
+ 0x8d87, 0x43,
+ 0x8d88, 0xf5,
+ 0x8d89, 0x36,
+ 0x8d8a, 0xe5,
+ 0x8d8b, 0x3e,
+ 0x8d8c, 0x34,
+ 0x8d8d, 0xa2,
+ 0x8d8e, 0xf5,
+ 0x8d8f, 0x35,
+ 0x8d90, 0xe5,
+ 0x8d91, 0x3d,
+ 0x8d92, 0x34,
+ 0x8d93, 0x28,
+ 0x8d94, 0xf5,
+ 0x8d95, 0x34,
+ 0x8d96, 0xe5,
+ 0x8d97, 0x37,
+ 0x8d98, 0xff,
+ 0x8d99, 0xe4,
+ 0x8d9a, 0xfe,
+ 0x8d9b, 0xfd,
+ 0x8d9c, 0xfc,
+ 0x8d9d, 0x78,
+ 0x8d9e, 0x18,
+ 0x8d9f, 0x12,
+ 0x8da0, 0x06,
+ 0x8da1, 0x69,
+ 0x8da2, 0x8f,
+ 0x8da3, 0x40,
+ 0x8da4, 0x8e,
+ 0x8da5, 0x3f,
+ 0x8da6, 0x8d,
+ 0x8da7, 0x3e,
+ 0x8da8, 0x8c,
+ 0x8da9, 0x3d,
+ 0x8daa, 0xe5,
+ 0x8dab, 0x37,
+ 0x8dac, 0x54,
+ 0x8dad, 0xa0,
+ 0x8dae, 0xff,
+ 0x8daf, 0xe5,
+ 0x8db0, 0x36,
+ 0x8db1, 0xfe,
+ 0x8db2, 0xe4,
+ 0x8db3, 0xfd,
+ 0x8db4, 0xfc,
+ 0x8db5, 0x78,
+ 0x8db6, 0x07,
+ 0x8db7, 0x12,
+ 0x8db8, 0x06,
+ 0x8db9, 0x56,
+ 0x8dba, 0x78,
+ 0x8dbb, 0x10,
+ 0x8dbc, 0x12,
+ 0x8dbd, 0x0f,
+ 0x8dbe, 0x9a,
+ 0x8dbf, 0xe4,
+ 0x8dc0, 0xff,
+ 0x8dc1, 0xfe,
+ 0x8dc2, 0xe5,
+ 0x8dc3, 0x35,
+ 0x8dc4, 0xfd,
+ 0x8dc5, 0xe4,
+ 0x8dc6, 0xfc,
+ 0x8dc7, 0x78,
+ 0x8dc8, 0x0e,
+ 0x8dc9, 0x12,
+ 0x8dca, 0x06,
+ 0x8dcb, 0x56,
+ 0x8dcc, 0x12,
+ 0x8dcd, 0x0f,
+ 0x8dce, 0x9d,
+ 0x8dcf, 0xe4,
+ 0x8dd0, 0xff,
+ 0x8dd1, 0xfe,
+ 0x8dd2, 0xfd,
+ 0x8dd3, 0xe5,
+ 0x8dd4, 0x34,
+ 0x8dd5, 0xfc,
+ 0x8dd6, 0x78,
+ 0x8dd7, 0x18,
+ 0x8dd8, 0x12,
+ 0x8dd9, 0x06,
+ 0x8dda, 0x56,
+ 0x8ddb, 0x78,
+ 0x8ddc, 0x08,
+ 0x8ddd, 0x12,
+ 0x8dde, 0x0f,
+ 0x8ddf, 0x9a,
+ 0x8de0, 0x22,
+ 0x8de1, 0x8f,
+ 0x8de2, 0x3b,
+ 0x8de3, 0x8e,
+ 0x8de4, 0x3a,
+ 0x8de5, 0x8d,
+ 0x8de6, 0x39,
+ 0x8de7, 0x8c,
+ 0x8de8, 0x38,
+ 0x8de9, 0x22,
+ 0x8dea, 0x12,
+ 0x8deb, 0x06,
+ 0x8dec, 0x7c,
+ 0x8ded, 0x8f,
+ 0x8dee, 0x31,
+ 0x8def, 0x8e,
+ 0x8df0, 0x30,
+ 0x8df1, 0x8d,
+ 0x8df2, 0x2f,
+ 0x8df3, 0x8c,
+ 0x8df4, 0x2e,
+ 0x8df5, 0x22,
+ 0x8df6, 0x93,
+ 0x8df7, 0xf9,
+ 0x8df8, 0xf8,
+ 0x8df9, 0x02,
+ 0x8dfa, 0x06,
+ 0x8dfb, 0x69,
+ 0x8dfc, 0x00,
+ 0x8dfd, 0x00,
+ 0x8dfe, 0x00,
+ 0x8dff, 0x00,
+ 0x8e00, 0x12,
+ 0x8e01, 0x01,
+ 0x8e02, 0x17,
+ 0x8e03, 0x08,
+ 0x8e04, 0x31,
+ 0x8e05, 0x15,
+ 0x8e06, 0x53,
+ 0x8e07, 0x54,
+ 0x8e08, 0x44,
+ 0x8e09, 0x20,
+ 0x8e0a, 0x20,
+ 0x8e0b, 0x20,
+ 0x8e0c, 0x20,
+ 0x8e0d, 0x20,
+ 0x8e0e, 0x13,
+ 0x8e0f, 0x01,
+ 0x8e10, 0x10,
+ 0x8e11, 0x01,
+ 0x8e12, 0x56,
+ 0x8e13, 0x40,
+ 0x8e14, 0x1a,
+ 0x8e15, 0x30,
+ 0x8e16, 0x29,
+ 0x8e17, 0x7e,
+ 0x8e18, 0x00,
+ 0x8e19, 0x30,
+ 0x8e1a, 0x04,
+ 0x8e1b, 0x20,
+ 0x8e1c, 0xdf,
+ 0x8e1d, 0x30,
+ 0x8e1e, 0x05,
+ 0x8e1f, 0x40,
+ 0x8e20, 0xbf,
+ 0x8e21, 0x50,
+ 0x8e22, 0x03,
+ 0x8e23, 0x00,
+ 0x8e24, 0xfd,
+ 0x8e25, 0x50,
+ 0x8e26, 0x27,
+ 0x8e27, 0x01,
+ 0x8e28, 0xfe,
+ 0x8e29, 0x60,
+ 0x8e2a, 0x00,
+ 0x8e2b, 0x11,
+ 0x8e2c, 0x00,
+ 0x8e2d, 0x3f,
+ 0x8e2e, 0x05,
+ 0x8e2f, 0x30,
+ 0x8e30, 0x00,
+ 0x8e31, 0x3f,
+ 0x8e32, 0x06,
+ 0x8e33, 0x22,
+ 0x8e34, 0x00,
+ 0x8e35, 0x3f,
+ 0x8e36, 0x01,
+ 0x8e37, 0x2a,
+ 0x8e38, 0x00,
+ 0x8e39, 0x3f,
+ 0x8e3a, 0x02,
+ 0x8e3b, 0x00,
+ 0x8e3c, 0x00,
+ 0x8e3d, 0x36,
+ 0x8e3e, 0x06,
+ 0x8e3f, 0x07,
+ 0x8e40, 0x00,
+ 0x8e41, 0x3f,
+ 0x8e42, 0x0b,
+ 0x8e43, 0x0f,
+ 0x8e44, 0xf0,
+ 0x8e45, 0x00,
+ 0x8e46, 0x00,
+ 0x8e47, 0x00,
+ 0x8e48, 0x00,
+ 0x8e49, 0x30,
+ 0x8e4a, 0x01,
+ 0x8e4b, 0x40,
+ 0x8e4c, 0xbf,
+ 0x8e4d, 0x30,
+ 0x8e4e, 0x01,
+ 0x8e4f, 0x00,
+ 0x8e50, 0xbf,
+ 0x8e51, 0x30,
+ 0x8e52, 0x29,
+ 0x8e53, 0x70,
+ 0x8e54, 0x00,
+ 0x8e55, 0x3a,
+ 0x8e56, 0x00,
+ 0x8e57, 0x00,
+ 0x8e58, 0xff,
+ 0x8e59, 0x3a,
+ 0x8e5a, 0x00,
+ 0x8e5b, 0x00,
+ 0x8e5c, 0xff,
+ 0x8e5d, 0x36,
+ 0x8e5e, 0x03,
+ 0x8e5f, 0x36,
+ 0x8e60, 0x02,
+ 0x8e61, 0x41,
+ 0x8e62, 0x44,
+ 0x8e63, 0x58,
+ 0x8e64, 0x20,
+ 0x8e65, 0x18,
+ 0x8e66, 0x10,
+ 0x8e67, 0x0a,
+ 0x8e68, 0x04,
+ 0x8e69, 0x04,
+ 0x8e6a, 0x00,
+ 0x8e6b, 0x03,
+ 0x8e6c, 0xff,
+ 0x8e6d, 0x64,
+ 0x8e6e, 0x00,
+ 0x8e6f, 0x00,
+ 0x8e70, 0x80,
+ 0x8e71, 0x00,
+ 0x8e72, 0x00,
+ 0x8e73, 0x00,
+ 0x8e74, 0x00,
+ 0x8e75, 0x00,
+ 0x8e76, 0x00,
+ 0x8e77, 0x02,
+ 0x8e78, 0x04,
+ 0x8e79, 0x06,
+ 0x8e7a, 0x06,
+ 0x8e7b, 0x00,
+ 0x8e7c, 0x02,
+ 0x8e7d, 0x60, //;cc
+ 0x8e7e, 0x00,
+ 0x8e7f, 0x70,
+ 0x8e80, 0x50,
+ 0x8e81, 0x3c,
+ 0x8e82, 0x28,
+ 0x8e83, 0x1e,
+ 0x8e84, 0x10,
+ 0x8e85, 0x10,
+ 0x8e86, 0x50,
+ 0x8e87, 0x2d,
+ 0x8e88, 0x28,
+ 0x8e89, 0x16,
+ 0x8e8a, 0x10,
+ 0x8e8b, 0x10,
+ 0x8e8c, 0x02,
+ 0x8e8d, 0x00,
+ 0x8e8e, 0x10,
+ 0x8e8f, 0x30,
+ 0x8e90, 0x0a,
+ 0x8e91, 0x04,
+ 0x8e92, 0x05,
+ 0x8e93, 0x08,
+ 0x8e94, 0x06,
+ 0x8e95, 0x05,
+ 0x8e96, 0x00,
+ 0x8e97, 0xa5,
+ 0x8e98, 0x5a,
+ 0x8e99, 0x00,
+ 0x8e9a, 0xae,
+ 0x8e9b, 0x35,
+ 0x8e9c, 0xaf,
+ 0x8e9d, 0x36,
+ 0x8e9e, 0xe4,
+ 0x8e9f, 0xfd,
+ 0x8ea0, 0xed,
+ 0x8ea1, 0xc3,
+ 0x8ea2, 0x95,
+ 0x8ea3, 0x37,
+ 0x8ea4, 0x50,
+ 0x8ea5, 0x33,
+ 0x8ea6, 0x12,
+ 0x8ea7, 0x0f,
+ 0x8ea8, 0xe2,
+ 0x8ea9, 0xe4,
+ 0x8eaa, 0x93,
+ 0x8eab, 0xf5,
+ 0x8eac, 0x38,
+ 0x8ead, 0x74,
+ 0x8eae, 0x01,
+ 0x8eaf, 0x93,
+ 0x8eb0, 0xf5,
+ 0x8eb1, 0x39,
+ 0x8eb2, 0x45,
+ 0x8eb3, 0x38,
+ 0x8eb4, 0x60,
+ 0x8eb5, 0x23,
+ 0x8eb6, 0x85,
+ 0x8eb7, 0x39,
+ 0x8eb8, 0x82,
+ 0x8eb9, 0x85,
+ 0x8eba, 0x38,
+ 0x8ebb, 0x83,
+ 0x8ebc, 0xe0,
+ 0x8ebd, 0xfc,
+ 0x8ebe, 0x12,
+ 0x8ebf, 0x0f,
+ 0x8ec0, 0xe2,
+ 0x8ec1, 0x74,
+ 0x8ec2, 0x03,
+ 0x8ec3, 0x93,
+ 0x8ec4, 0x52,
+ 0x8ec5, 0x04,
+ 0x8ec6, 0x12,
+ 0x8ec7, 0x0f,
+ 0x8ec8, 0xe2,
+ 0x8ec9, 0x74,
+ 0x8eca, 0x02,
+ 0x8ecb, 0x93,
+ 0x8ecc, 0x42,
+ 0x8ecd, 0x04,
+ 0x8ece, 0x85,
+ 0x8ecf, 0x39,
+ 0x8ed0, 0x82,
+ 0x8ed1, 0x85,
+ 0x8ed2, 0x38,
+ 0x8ed3, 0x83,
+ 0x8ed4, 0xec,
+ 0x8ed5, 0xf0,
+ 0x8ed6, 0x0d,
+ 0x8ed7, 0x80,
+ 0x8ed8, 0xc7,
+ 0x8ed9, 0x22,
+ 0x8eda, 0x78,
+ 0x8edb, 0xbe,
+ 0x8edc, 0xe6,
+ 0x8edd, 0xd3,
+ 0x8ede, 0x08,
+ 0x8edf, 0xff,
+ 0x8ee0, 0xe6,
+ 0x8ee1, 0x64,
+ 0x8ee2, 0x80,
+ 0x8ee3, 0xf8,
+ 0x8ee4, 0xef,
+ 0x8ee5, 0x64,
+ 0x8ee6, 0x80,
+ 0x8ee7, 0x98,
+ 0x8ee8, 0x22,
+ 0x8ee9, 0x93,
+ 0x8eea, 0xff,
+ 0x8eeb, 0x7e,
+ 0x8eec, 0x00,
+ 0x8eed, 0xe6,
+ 0x8eee, 0xfc,
+ 0x8eef, 0x08,
+ 0x8ef0, 0xe6,
+ 0x8ef1, 0xfd,
+ 0x8ef2, 0x12,
+ 0x8ef3, 0x04,
+ 0x8ef4, 0xc1,
+ 0x8ef5, 0x78,
+ 0x8ef6, 0xc1,
+ 0x8ef7, 0xe6,
+ 0x8ef8, 0xfc,
+ 0x8ef9, 0x08,
+ 0x8efa, 0xe6,
+ 0x8efb, 0xfd,
+ 0x8efc, 0xd3,
+ 0x8efd, 0xef,
+ 0x8efe, 0x9d,
+ 0x8eff, 0xee,
+ 0x8f00, 0x9c,
+ 0x8f01, 0x22,
+ 0x8f02, 0x78,
+ 0x8f03, 0xbd,
+ 0x8f04, 0xd3,
+ 0x8f05, 0xe6,
+ 0x8f06, 0x64,
+ 0x8f07, 0x80,
+ 0x8f08, 0x94,
+ 0x8f09, 0x80,
+ 0x8f0a, 0x22,
+ 0x8f0b, 0x25,
+ 0x8f0c, 0xe0,
+ 0x8f0d, 0x24,
+ 0x8f0e, 0x0a,
+ 0x8f0f, 0xf8,
+ 0x8f10, 0xe6,
+ 0x8f11, 0xfe,
+ 0x8f12, 0x08,
+ 0x8f13, 0xe6,
+ 0x8f14, 0xff,
+ 0x8f15, 0x22,
+ 0x8f16, 0xe5,
+ 0x8f17, 0x3c,
+ 0x8f18, 0xd3,
+ 0x8f19, 0x94,
+ 0x8f1a, 0x00,
+ 0x8f1b, 0x40,
+ 0x8f1c, 0x0b,
+ 0x8f1d, 0x90,
+ 0x8f1e, 0x0e,
+ 0x8f1f, 0x88,
+ 0x8f20, 0x12,
+ 0x8f21, 0x0b,
+ 0x8f22, 0xf1,
+ 0x8f23, 0x90,
+ 0x8f24, 0x0e,
+ 0x8f25, 0x86,
+ 0x8f26, 0x80,
+ 0x8f27, 0x09,
+ 0x8f28, 0x90,
+ 0x8f29, 0x0e,
+ 0x8f2a, 0x82,
+ 0x8f2b, 0x12,
+ 0x8f2c, 0x0b,
+ 0x8f2d, 0xf1,
+ 0x8f2e, 0x90,
+ 0x8f2f, 0x0e,
+ 0x8f30, 0x80,
+ 0x8f31, 0xe4,
+ 0x8f32, 0x93,
+ 0x8f33, 0xf5,
+ 0x8f34, 0x44,
+ 0x8f35, 0xa3,
+ 0x8f36, 0xe4,
+ 0x8f37, 0x93,
+ 0x8f38, 0xf5,
+ 0x8f39, 0x43,
+ 0x8f3a, 0xd2,
+ 0x8f3b, 0x06,
+ 0x8f3c, 0x30,
+ 0x8f3d, 0x06,
+ 0x8f3e, 0x03,
+ 0x8f3f, 0xd3,
+ 0x8f40, 0x80,
+ 0x8f41, 0x01,
+ 0x8f42, 0xc3,
+ 0x8f43, 0x92,
+ 0x8f44, 0x0e,
+ 0x8f45, 0x22,
+ 0x8f46, 0xa2,
+ 0x8f47, 0xaf,
+ 0x8f48, 0x92,
+ 0x8f49, 0x32,
+ 0x8f4a, 0xc2,
+ 0x8f4b, 0xaf,
+ 0x8f4c, 0xe5,
+ 0x8f4d, 0x23,
+ 0x8f4e, 0x45,
+ 0x8f4f, 0x22,
+ 0x8f50, 0x90,
+ 0x8f51, 0x0e,
+ 0x8f52, 0x5d,
+ 0x8f53, 0x60,
+ 0x8f54, 0x0e,
+ 0x8f55, 0x12,
+ 0x8f56, 0x0f,
+ 0x8f57, 0xcb,
+ 0x8f58, 0xe0,
+ 0x8f59, 0xf5,
+ 0x8f5a, 0x2c,
+ 0x8f5b, 0x12,
+ 0x8f5c, 0x0f,
+ 0x8f5d, 0xc8,
+ 0x8f5e, 0xe0,
+ 0x8f5f, 0xf5,
+ 0x8f60, 0x2d,
+ 0x8f61, 0x80,
+ 0x8f62, 0x0c,
+ 0x8f63, 0x12,
+ 0x8f64, 0x0f,
+ 0x8f65, 0xcb,
+ 0x8f66, 0xe5,
+ 0x8f67, 0x30,
+ 0x8f68, 0xf0,
+ 0x8f69, 0x12,
+ 0x8f6a, 0x0f,
+ 0x8f6b, 0xc8,
+ 0x8f6c, 0xe5,
+ 0x8f6d, 0x31,
+ 0x8f6e, 0xf0,
+ 0x8f6f, 0xa2,
+ 0x8f70, 0x32,
+ 0x8f71, 0x92,
+ 0x8f72, 0xaf,
+ 0x8f73, 0x22,
+ 0x8f74, 0xd2,
+ 0x8f75, 0x01,
+ 0x8f76, 0xc2,
+ 0x8f77, 0x02,
+ 0x8f78, 0xe4,
+ 0x8f79, 0xf5,
+ 0x8f7a, 0x1f,
+ 0x8f7b, 0xf5,
+ 0x8f7c, 0x1e,
+ 0x8f7d, 0xd2,
+ 0x8f7e, 0x35,
+ 0x8f7f, 0xd2,
+ 0x8f80, 0x33,
+ 0x8f81, 0xd2,
+ 0x8f82, 0x36,
+ 0x8f83, 0xd2,
+ 0x8f84, 0x01,
+ 0x8f85, 0xc2,
+ 0x8f86, 0x02,
+ 0x8f87, 0xf5,
+ 0x8f88, 0x1f,
+ 0x8f89, 0xf5,
+ 0x8f8a, 0x1e,
+ 0x8f8b, 0xd2,
+ 0x8f8c, 0x35,
+ 0x8f8d, 0xd2,
+ 0x8f8e, 0x33,
+ 0x8f8f, 0x22,
+ 0x8f90, 0xfb,
+ 0x8f91, 0xd3,
+ 0x8f92, 0xed,
+ 0x8f93, 0x9b,
+ 0x8f94, 0x74,
+ 0x8f95, 0x80,
+ 0x8f96, 0xf8,
+ 0x8f97, 0x6c,
+ 0x8f98, 0x98,
+ 0x8f99, 0x22,
+ 0x8f9a, 0x12,
+ 0x8f9b, 0x06,
+ 0x8f9c, 0x69,
+ 0x8f9d, 0xe5,
+ 0x8f9e, 0x40,
+ 0x8f9f, 0x2f,
+ 0x8fa0, 0xf5,
+ 0x8fa1, 0x40,
+ 0x8fa2, 0xe5,
+ 0x8fa3, 0x3f,
+ 0x8fa4, 0x3e,
+ 0x8fa5, 0xf5,
+ 0x8fa6, 0x3f,
+ 0x8fa7, 0xe5,
+ 0x8fa8, 0x3e,
+ 0x8fa9, 0x3d,
+ 0x8faa, 0xf5,
+ 0x8fab, 0x3e,
+ 0x8fac, 0xe5,
+ 0x8fad, 0x3d,
+ 0x8fae, 0x3c,
+ 0x8faf, 0xf5,
+ 0x8fb0, 0x3d,
+ 0x8fb1, 0x22,
+ 0x8fb2, 0xc0,
+ 0x8fb3, 0xe0,
+ 0x8fb4, 0xc0,
+ 0x8fb5, 0x83,
+ 0x8fb6, 0xc0,
+ 0x8fb7, 0x82,
+ 0x8fb8, 0x90,
+ 0x8fb9, 0x3f,
+ 0x8fba, 0x0d,
+ 0x8fbb, 0xe0,
+ 0x8fbc, 0xf5,
+ 0x8fbd, 0x33,
+ 0x8fbe, 0xe5,
+ 0x8fbf, 0x33,
+ 0x8fc0, 0xf0,
+ 0x8fc1, 0xd0,
+ 0x8fc2, 0x82,
+ 0x8fc3, 0xd0,
+ 0x8fc4, 0x83,
+ 0x8fc5, 0xd0,
+ 0x8fc6, 0xe0,
+ 0x8fc7, 0x32,
+ 0x8fc8, 0x90,
+ 0x8fc9, 0x0e,
+ 0x8fca, 0x5f,
+ 0x8fcb, 0xe4,
+ 0x8fcc, 0x93,
+ 0x8fcd, 0xfe,
+ 0x8fce, 0x74,
+ 0x8fcf, 0x01,
+ 0x8fd0, 0x93,
+ 0x8fd1, 0xf5,
+ 0x8fd2, 0x82,
+ 0x8fd3, 0x8e,
+ 0x8fd4, 0x83,
+ 0x8fd5, 0x22,
+ 0x8fd6, 0x78,
+ 0x8fd7, 0x7f,
+ 0x8fd8, 0xe4,
+ 0x8fd9, 0xf6,
+ 0x8fda, 0xd8,
+ 0x8fdb, 0xfd,
+ 0x8fdc, 0x75,
+ 0x8fdd, 0x81,
+ 0x8fde, 0xcd,
+ 0x8fdf, 0x02,
+ 0x8fe0, 0x0c,
+ 0x8fe1, 0x98,
+ 0x8fe2, 0x8f,
+ 0x8fe3, 0x82,
+ 0x8fe4, 0x8e,
+ 0x8fe5, 0x83,
+ 0x8fe6, 0x75,
+ 0x8fe7, 0xf0,
+ 0x8fe8, 0x04,
+ 0x8fe9, 0xed,
+ 0x8fea, 0x02,
+ 0x8feb, 0x06,
+ 0x8fec, 0xa5,
+ 0x3022, 0x00,
+ 0x3023, 0x00,
+ 0x3024, 0x00,
+ 0x3025, 0x00,
+ 0x3026, 0x00,
+ 0x3027, 0x00,
+ 0x3028, 0x00,
+ 0x3029, 0x7F,
+ 0x3000, 0x00,
+};
+
+#endif /* #ifndef __OV5640_FIRMWARE_H__ */
+
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/ov7675/ov7675.c b/drivers/media/video/wmt_v4l2/sensors/ov7675/ov7675.c
new file mode 100755
index 00000000..89b72f9e
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/ov7675/ov7675.c
@@ -0,0 +1,321 @@
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "ov7675.h"
+
+#define sensor_write_array(sd, arr, sz) \
+ cmos_init_8bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write(sd->i2c_addr, reg, val)
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint8_t *regs;
+ size_t size;
+};
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+// CMOS_WIN_SIZE("QVGA", 320, 240, ov7675_320x240),
+ CMOS_WIN_SIZE("VGA", 640, 480, ov7675_640x480),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = ov7675_wb_auto;
+ size = ARRAY_SIZE(ov7675_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = ov7675_wb_incandescent;
+ size = ARRAY_SIZE(ov7675_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = ov7675_wb_daylight;
+ size = ARRAY_SIZE(ov7675_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = ov7675_wb_cloudy;
+ size = ARRAY_SIZE(ov7675_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = ov7675_wb_fluorescent;
+ size = ARRAY_SIZE(ov7675_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_scenemode(struct cmos_subdev *sd, enum v4l2_scene_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case SCENE_MODE_AUTO:
+ regs = ov7675_scene_mode_auto;
+ size = ARRAY_SIZE(ov7675_scene_mode_auto);
+ break;
+ case SCENE_MODE_NIGHTSHOT:
+ regs = ov7675_scene_mode_night;
+ size = ARRAY_SIZE(ov7675_scene_mode_night);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_exposure(struct cmos_subdev *sd, int value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case -2:
+ regs = ov7675_exposure_neg6;
+ size = ARRAY_SIZE(ov7675_exposure_neg6);
+ break;
+ case -1:
+ regs = ov7675_exposure_neg3;
+ size = ARRAY_SIZE(ov7675_exposure_neg3);
+ break;
+ case 0:
+ regs = ov7675_exposure_zero;
+ size = ARRAY_SIZE(ov7675_exposure_zero);
+ break;
+ case 1:
+ regs = ov7675_exposure_pos3;
+ size = ARRAY_SIZE(ov7675_exposure_pos3);
+ break;
+ case 2:
+ regs = ov7675_exposure_pos6;
+ size = ARRAY_SIZE(ov7675_exposure_pos6);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data;
+
+ data = sensor_read(sd, 0x1e);
+
+ switch (value) {
+ case 0:
+ data &= ~0x20;
+ break;
+ case 1:
+ data |= 0x20;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write(sd, 0x1e, data);
+ msleep(20);
+
+ return 0;
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data;
+
+ data = sensor_read(sd, 0x1e);
+
+ switch (value) {
+ case 0:
+ data &= ~0x10;
+ break;
+ case 1:
+ data |= 0x10;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write(sd, 0x1e, data);
+ msleep(20);
+ return 0;
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ case V4L2_CID_EXPOSURE:
+ return v4l2_ctrl_query_fill(qc, -2, 2, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return sensor_s_scenemode(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return sensor_s_exposure(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ default:
+ case WMT_V4L2_CID_CAMERA_ANTIBANDING:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win) {
+ pr_err("%s, s_mbus_fmt failed, width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, win->regs, win->size);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return 0;
+}
+
+static int sensor_enum_framesizes(struct cmos_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ const struct cmos_win_size *win = &cmos_supported_win_sizes[index];
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ return (sensor_read(sd, 0x0a) == sd->id) ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ if (sensor_identify(sd)) {
+ return -1;
+ }
+
+ sensor_write_array(sd, ov7675_default_regs_init,
+ ARRAY_SIZE(ov7675_default_regs_init));
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ return 0;
+}
+
+static struct cmos_subdev_ops ov7675_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+ .enum_framesizes = sensor_enum_framesizes,
+};
+
+struct cmos_subdev ov7675 = {
+ .name = "ov7675",
+ .i2c_addr = 0x21,
+ .id = 0x76,
+ .max_width = 640,
+ .max_height = 480,
+ .ops = &ov7675_ops,
+};
+
+#if 0
+static int __init ov7675_init(void)
+{
+ return cmos_register_sudbdev(&ov7675);
+}
+
+static void __exit ov7675_exit(void)
+{
+ return cmos_unregister_subdev(&ov7675);
+}
+
+module_init(ov7675_init);
+module_exit(ov7675_exit);
+
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/ov7675/ov7675.h b/drivers/media/video/wmt_v4l2/sensors/ov7675/ov7675.h
new file mode 100755
index 00000000..160a45ee
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/ov7675/ov7675.h
@@ -0,0 +1,258 @@
+#ifndef OV7675_H
+#define OV7675_H
+
+
+// Scene Mode
+uint8_t ov7675_scene_mode_auto[] = {
+
+};
+
+uint8_t ov7675_scene_mode_night[] = {
+
+};
+
+// White Balance
+uint8_t ov7675_wb_auto [] = {
+ 0x01,0x56,
+ 0x02,0x44,
+ 0x13, 0xe7,
+};
+
+uint8_t ov7675_wb_incandescent [] = {
+ 0x13, 0xe5,
+ 0x01,0x9a,
+ 0x02,0x40,
+ 0x6A, 0x48,
+};
+
+uint8_t ov7675_wb_fluorescent [] = {
+ 0x13, 0xe5,
+ 0x01,0x8b,
+ 0x02,0x42,
+ 0x6A, 0x40,
+};
+
+uint8_t ov7675_wb_daylight [] = {
+ 0x13, 0xe5,
+ 0x01,0x56,
+ 0x02,0x5c,
+ 0x6A, 0x42,
+};
+
+uint8_t ov7675_wb_cloudy [] = {
+ 0x13, 0xe5,
+ 0x01,0x58,
+ 0x02,0x60,
+ 0x6A, 0x40,
+};
+
+
+// Exposure
+uint8_t ov7675_exposure_neg6[] = {
+ 0x55,0xb0,
+};
+
+uint8_t ov7675_exposure_neg3[] = {
+ 0x55,0x98,
+};
+
+uint8_t ov7675_exposure_zero[] = {
+ 0x55,0x00,
+};
+
+uint8_t ov7675_exposure_pos3[] = {
+ 0x55,0x18,
+};
+
+uint8_t ov7675_exposure_pos6[] = {
+ 0x55,0x30,
+};
+
+
+// Resolution
+uint8_t ov7675_320x240[]= {
+
+};
+
+uint8_t ov7675_640x480[] = {
+ 0x17, 0x13,
+ 0x18, 0x01,
+ 0x32, 0xbf,
+ 0x19, 0x03,
+ 0x1a, 0x7b,
+ 0x03, 0x0a,
+};
+
+uint8_t ov7675_default_regs_init[] = {
+ 0x12, 0x80,
+ 0x11, 0x80,
+ 0x2a, 0x00,
+ 0x2b, 0x00,
+ 0x92, 0x66,
+ 0x93, 0x00,
+ 0x12, 0x00,
+ 0x3a, 0x04,
+ 0x3d, 0xC1,
+ 0x15, 0x00,
+ 0xc1, 0x7f,
+ 0x17, 0x13,
+ 0x18, 0x01,
+ 0x32, 0xbF,
+ 0x19, 0x03,
+ 0x1a, 0x7b,
+ 0x03, 0x0a,
+ 0x0c, 0x00,
+ 0x3e, 0x00,
+ 0x70, 0x3a,
+ 0x71, 0x35,
+ 0x72, 0x11,
+ 0x73, 0xf0,
+ 0xa2, 0x02,
+ 0x13, 0xe0,
+ 0x10, 0x00,
+ 0x14, 0x28,
+ 0xa5, 0x06,
+ 0xab, 0x07,
+ 0x24, 0x58,
+ 0x25, 0x48,
+ 0x26, 0x93,
+ 0x9f, 0x78,
+ 0xa0, 0x68,
+ 0xa1, 0x03,
+ 0xa6, 0xD8,
+ 0xa7, 0xD8,
+ 0xa8, 0xf0,
+ 0xa9, 0x90,
+ 0xaa, 0x14,
+ 0x13, 0xe5,
+ 0x0e, 0x61,
+ 0x0f, 0x4b,
+ 0x16, 0x02,
+ 0x1e, 0x17, //0x07//0x27
+ 0x21, 0x02,
+ 0x22, 0x91,
+ 0x29, 0x07,
+ 0x33, 0x0b,
+ 0x35, 0x0b,
+ 0x37, 0x1d,
+ 0x38, 0x71,
+ 0x39, 0x2a,
+ 0x3c, 0x78,
+ 0x4d, 0x40,
+ 0x4e, 0x20,
+ 0x69, 0x00,
+ 0x6b, 0x0a,
+ 0x74, 0x10,
+ 0x8d, 0x4f,
+ 0x8e, 0x00,
+ 0x8f, 0x00,
+ 0x90, 0x00,
+ 0x91, 0x00,
+ 0x96, 0x00,
+ 0x9a, 0x80,
+ 0xb0, 0x84,
+ 0xb1, 0x0c,
+ 0xb2, 0x0e,
+ 0xb3, 0x82,
+ 0xb8, 0x0a,
+ 0xbb, 0xa1,
+ 0x0d, 0x60,
+ 0x42, 0x80,
+ 0x62, 0x00,
+ 0x63, 0x00,
+ 0x64, 0x10,
+ 0x65, 0x07,
+ 0x66, 0x05,
+ 0x94, 0x10,
+ 0x95, 0x12,
+ 0x7a, 0x20,// 0x7a, 0x24,
+ 0x7b, 0x16,// 0x7b, 0x04,
+ 0x7c, 0x23,// 0x7c, 0x07,
+ 0x7d, 0x3c,// 0x7d, 0x12,
+ 0x7e, 0x5c,// 0x7e, 0x2f,
+ 0x7f, 0x69,// 0x7f, 0x3f,
+ 0x80, 0x75,// 0x80, 0x4d,
+ 0x81, 0x7e,// 0x81, 0x5a,
+ 0x82, 0x88,// 0x82, 0x69,
+ 0x83, 0x8f,// 0x83, 0x74,
+ 0x84, 0x96,// 0x84, 0x7f,
+ 0x85, 0xa3,// 0x85, 0x91,
+ 0x86, 0xaf,// 0x86, 0x9e,
+ 0x87, 0xc4,// 0x87, 0xbb,
+ 0x88, 0xd7,// 0x88, 0xd2,
+ 0x89, 0xe8,// 0x89, 0xe5,
+ 0x43, 0x0a,
+ 0x44, 0xf0,
+ 0x45, 0x34,
+ 0x46, 0x58,
+ 0x47, 0x28,
+ 0x48, 0x3a,
+ 0x59, 0x88,
+ 0x5a, 0x88,
+ 0x5b, 0xc2,
+ 0x5c, 0x60,
+ 0x5d, 0x58,
+ 0x5e, 0x10,
+ 0x6c, 0x0a,
+ 0x6d, 0x55,
+ 0x6e, 0x11,
+ 0x6f, 0x9e,
+ 0x6a, 0x40,
+ 0x01, 0x56,
+ 0x02, 0x44,
+ 0x13, 0xe7,
+ 0x4f, 0x95,
+ 0x50, 0x99,
+ 0x51, 0x04,
+ 0x52, 0x1a,
+ 0x53, 0x7f,
+ 0x54, 0x99,
+ 0x58, 0x1a,
+ 0x3f, 0x02,
+ 0x75, 0x63,
+ 0x76, 0xe1,
+ 0x4c, 0x00,
+ 0x77, 0x04,//0x01
+ 0x4b, 0x09,
+ 0xc9, 0x60,
+ 0x41, 0x38,
+ 0x56, 0x40,
+ 0x34, 0x11,
+ 0x3b, 0xaa,
+ 0xa4, 0x88,
+ 0x96, 0x00,
+ 0x97, 0x30,
+ 0x98, 0x20,
+ 0x99, 0x30,
+ 0x9a, 0x84,
+ 0x9b, 0x29,
+ 0x9c, 0x03,
+ 0x9d, 0x99,
+ 0x9e, 0x99,
+ 0x78, 0x04,
+ 0x79, 0x01,
+ 0xc8, 0xf0,
+ 0x79, 0x0f,
+ 0xc8, 0x00,
+ 0x79, 0x10,
+ 0xc8, 0x7e,
+ 0x79, 0x0a,
+ 0xc8, 0x80,
+ 0x79, 0x0b,
+ 0xc8, 0x01,
+ 0x79, 0x0c,
+ 0xc8, 0x0f,
+ 0x79, 0x0d,
+ 0xc8, 0x20,
+ 0x79, 0x09,
+ 0xc8, 0x80,
+ 0x79, 0x02,
+ 0xc8, 0xc0,
+ 0x79, 0x03,
+ 0xc8, 0x40,
+ 0x79, 0x05,
+ 0xc8, 0x30,
+ 0x79, 0x26,
+};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/ov9740/ov9740.c b/drivers/media/video/wmt_v4l2/sensors/ov9740/ov9740.c
new file mode 100755
index 00000000..38715b57
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/ov9740/ov9740.c
@@ -0,0 +1,348 @@
+/*
+ * Driver for ov9740 CMOS Image Sensor from Omnivision
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <asm/errno.h>
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "ov9740.h"
+
+//#define sensor_write_array(sd, arr, sz) cmos_init_16bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read16addr(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write16addr(sd->i2c_addr, reg, val)
+
+static void sensor_write_array(struct cmos_subdev *sd, uint32_t *arr, size_t sz)
+{
+ uint32_t reg, data;
+ int i;
+
+ for (i = 0; i < sz; i += 2) {
+ reg = arr[i];
+ data = arr[i+1];
+
+ if (reg == 0xffff)
+ msleep(data);
+ else
+ wmt_vid_i2c_write16addr(sd->i2c_addr, reg ,data);
+ }
+}
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint32_t *regs;
+ size_t size;
+};
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+ CMOS_WIN_SIZE("VGA", 640, 480, ov9740_640_480_regs),
+ //CMOS_WIN_SIZE("SVGA", 800, 600, ov9740_800_600_regs),
+ CMOS_WIN_SIZE("720p", 1280, 720, ov9740_1280_720_regs),
+// CMOS_WIN_SIZE("UXGA", 1600, 1200, ov9740_1600_1200_regs),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint32_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = ov9740_wb_auto;
+ size = ARRAY_SIZE(ov9740_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = ov9740_wb_incandescent;
+ size = ARRAY_SIZE(ov9740_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = ov9740_wb_daylight;
+ size = ARRAY_SIZE(ov9740_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = ov9740_wb_cloudy;
+ size = ARRAY_SIZE(ov9740_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = ov9740_wb_fluorescent;
+ size = ARRAY_SIZE(ov9740_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ msleep(100);
+ return 0;
+}
+
+static int sensor_s_scenemode(struct cmos_subdev *sd, enum v4l2_scene_mode value)
+{
+ uint32_t *regs;
+ size_t size;
+
+ switch (value) {
+ case SCENE_MODE_AUTO:
+ regs = ov9740_scene_mode_auto;
+ size = ARRAY_SIZE(ov9740_scene_mode_auto);
+ break;
+ case SCENE_MODE_NIGHTSHOT:
+ regs = ov9740_scene_mode_night;
+ size = ARRAY_SIZE(ov9740_scene_mode_night);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_exposure(struct cmos_subdev *sd, enum v4l2_exposure_mode value)
+{
+ uint32_t *regs;
+ size_t size;
+
+ switch (value) {
+ case -2:
+ regs = ov9740_exposure_neg6;
+ size = ARRAY_SIZE(ov9740_exposure_neg6);
+ break;
+ case -1:
+ regs = ov9740_exposure_neg3;
+ size = ARRAY_SIZE(ov9740_exposure_neg3);
+ break;
+ case 0:
+ regs = ov9740_exposure_zero;
+ size = ARRAY_SIZE(ov9740_exposure_zero);
+ break;
+ case 1:
+ regs = ov9740_exposure_pos3;
+ size = ARRAY_SIZE(ov9740_exposure_pos3);
+ break;
+ case 2:
+ regs = ov9740_exposure_pos6;
+ size = ARRAY_SIZE(ov9740_exposure_pos6);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+ uint32_t data = sensor_read(sd, 0x0101);
+
+ switch (value) {
+ case 0:
+ data &= ~0x01;
+ break;
+ case 1:
+ data |= 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sensor_write(sd, 0x0101, data);
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+ uint32_t data = sensor_read(sd, 0x0101);
+
+ switch (value) {
+ case 0:
+ data &= ~0x02;
+ break;
+ case 1:
+ data |= 0x02;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sensor_write(sd, 0x0101, data);
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ case V4L2_CID_EXPOSURE:
+ return v4l2_ctrl_query_fill(qc, -2, 2, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return sensor_s_scenemode(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return sensor_s_exposure(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ default:
+ case WMT_V4L2_CID_CAMERA_FLASH:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win) {
+ pr_err("%s, s_mbus_fmt failed, width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, win->regs, win->size);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return 0;
+}
+
+static int sensor_enum_framesizes(struct cmos_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ const struct cmos_win_size *win = &cmos_supported_win_sizes[index];
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ uint32_t data = 0;
+
+
+ data |= (sensor_read(sd, 0x300a) & 0xff) << 8;
+ data |= (sensor_read(sd, 0x300b) & 0xff);
+
+ return (data == sd->id) ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ uint32_t data = 0;
+ if (sensor_identify(sd))
+ return -1;
+
+ sensor_write_array(sd, ov9740_default_regs_init,
+ ARRAY_SIZE(ov9740_default_regs_init));
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ sensor_write_array(sd, ov9740_default_regs_exit,
+ ARRAY_SIZE(ov9740_default_regs_exit));
+ return 0;
+}
+
+static struct cmos_subdev_ops ov9740_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+ .enum_framesizes = sensor_enum_framesizes,
+};
+
+struct cmos_subdev ov9740 = {
+ .name = "ov9740",
+ .i2c_addr = 0x10,
+ .id = 0x9740,
+ .max_width = 1600,
+ .max_height = 1200,
+ .ops = &ov9740_ops,
+};
+
+#if 0
+static int __init ov9740_init(void)
+{
+ return cmos_register_sudbdev(&ov9740);
+}
+
+static void __exit ov9740_exit(void)
+{
+ cmos_unregister_subdev(&ov9740);
+ return;
+}
+
+module_init(ov9740_init);
+module_exit(ov9740_exit);
+
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/ov9740/ov9740.h b/drivers/media/video/wmt_v4l2/sensors/ov9740/ov9740.h
new file mode 100755
index 00000000..f6b6c5e9
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/ov9740/ov9740.h
@@ -0,0 +1,688 @@
+#ifndef OV9740_H
+#define OV9740_H
+
+#define STREAMING_DELAY_MS 300
+
+#define OV9740_MODEL_ID_HI 0x0000
+#define OV9740_MODEL_ID_LO 0x0001
+#define OV9740_REVISION_NUMBER 0x0002
+#define OV9740_MANUFACTURER_ID 0x0003
+#define OV9740_SMIA_VERSION 0x0004
+
+/* General Setup Registers */
+#define OV9740_MODE_SELECT 0x0100
+#define OV9740_IMAGE_ORT 0x0101
+#define OV9740_SOFTWARE_RESET 0x0103
+#define OV9740_GRP_PARAM_HOLD 0x0104
+#define OV9740_MSK_CORRUP_FM 0x0105
+
+/* Timing Setting */
+#define OV9740_FRM_LENGTH_LN_HI 0x0340 /* VTS */
+#define OV9740_FRM_LENGTH_LN_LO 0x0341 /* VTS */
+#define OV9740_LN_LENGTH_PCK_HI 0x0342 /* HTS */
+#define OV9740_LN_LENGTH_PCK_LO 0x0343 /* HTS */
+#define OV9740_X_ADDR_START_HI 0x0344
+#define OV9740_X_ADDR_START_LO 0x0345
+#define OV9740_Y_ADDR_START_HI 0x0346
+#define OV9740_Y_ADDR_START_LO 0x0347
+#define OV9740_X_ADDR_END_HI 0x0348
+#define OV9740_X_ADDR_END_LO 0x0349
+#define OV9740_Y_ADDR_END_HI 0x034A
+#define OV9740_Y_ADDR_END_LO 0x034B
+#define OV9740_X_OUTPUT_SIZE_HI 0x034C
+#define OV9740_X_OUTPUT_SIZE_LO 0x034D
+#define OV9740_Y_OUTPUT_SIZE_HI 0x034E
+#define OV9740_Y_OUTPUT_SIZE_LO 0x034F
+
+/* IO Control Registers */
+#define OV9740_IO_CREL00 0x3002
+#define OV9740_IO_CREL01 0x3004
+#define OV9740_IO_CREL02 0x3005
+#define OV9740_IO_OUTPUT_SEL01 0x3026
+#define OV9740_IO_OUTPUT_SEL02 0x3027
+
+/* AWB Registers */
+#define OV9740_AWB_MANUAL_CTRL 0x3406
+
+/* Analog Control Registers */
+#define OV9740_ANALOG_CTRL01 0x3601
+#define OV9740_ANALOG_CTRL02 0x3602
+#define OV9740_ANALOG_CTRL03 0x3603
+#define OV9740_ANALOG_CTRL04 0x3604
+#define OV9740_ANALOG_CTRL10 0x3610
+#define OV9740_ANALOG_CTRL12 0x3612
+#define OV9740_ANALOG_CTRL20 0x3620
+#define OV9740_ANALOG_CTRL21 0x3621
+#define OV9740_ANALOG_CTRL22 0x3622
+#define OV9740_ANALOG_CTRL30 0x3630
+#define OV9740_ANALOG_CTRL31 0x3631
+#define OV9740_ANALOG_CTRL32 0x3632
+#define OV9740_ANALOG_CTRL33 0x3633
+
+/* Sensor Control */
+#define OV9740_SENSOR_CTRL03 0x3703
+#define OV9740_SENSOR_CTRL04 0x3704
+#define OV9740_SENSOR_CTRL05 0x3705
+#define OV9740_SENSOR_CTRL07 0x3707
+
+/* Timing Control */
+#define OV9740_TIMING_CTRL17 0x3817
+#define OV9740_TIMING_CTRL19 0x3819
+#define OV9740_TIMING_CTRL33 0x3833
+#define OV9740_TIMING_CTRL35 0x3835
+
+/* Banding Filter */
+#define OV9740_AEC_MAXEXPO_60_H 0x3A02
+#define OV9740_AEC_MAXEXPO_60_L 0x3A03
+#define OV9740_AEC_B50_STEP_HI 0x3A08
+#define OV9740_AEC_B50_STEP_LO 0x3A09
+#define OV9740_AEC_B60_STEP_HI 0x3A0A
+#define OV9740_AEC_B60_STEP_LO 0x3A0B
+#define OV9740_AEC_CTRL0D 0x3A0D
+#define OV9740_AEC_CTRL0E 0x3A0E
+#define OV9740_AEC_MAXEXPO_50_H 0x3A14
+#define OV9740_AEC_MAXEXPO_50_L 0x3A15
+
+/* AEC/AGC Control */
+#define OV9740_AEC_ENABLE 0x3503
+#define OV9740_GAIN_CEILING_01 0x3A18
+#define OV9740_GAIN_CEILING_02 0x3A19
+#define OV9740_AEC_HI_THRESHOLD 0x3A11
+#define OV9740_AEC_3A1A 0x3A1A
+#define OV9740_AEC_CTRL1B_WPT2 0x3A1B
+#define OV9740_AEC_CTRL0F_WPT 0x3A0F
+#define OV9740_AEC_CTRL10_BPT 0x3A10
+#define OV9740_AEC_CTRL1E_BPT2 0x3A1E
+#define OV9740_AEC_LO_THRESHOLD 0x3A1F
+
+/* BLC Control */
+#define OV9740_BLC_AUTO_ENABLE 0x4002
+#define OV9740_BLC_MODE 0x4005
+
+/* VFIFO */
+#define OV9740_VFIFO_READ_START_HI 0x4608
+#define OV9740_VFIFO_READ_START_LO 0x4609
+
+/* DVP Control */
+#define OV9740_DVP_VSYNC_CTRL02 0x4702
+#define OV9740_DVP_VSYNC_MODE 0x4704
+#define OV9740_DVP_VSYNC_CTRL06 0x4706
+
+/* PLL Setting */
+#define OV9740_PLL_MODE_CTRL01 0x3104
+#define OV9740_PRE_PLL_CLK_DIV 0x0305
+#define OV9740_PLL_MULTIPLIER 0x0307
+#define OV9740_VT_SYS_CLK_DIV 0x0303
+#define OV9740_VT_PIX_CLK_DIV 0x0301
+#define OV9740_PLL_CTRL3010 0x3010
+#define OV9740_VFIFO_CTRL00 0x460E
+
+/* ISP Control */
+#define OV9740_ISP_CTRL00 0x5000
+#define OV9740_ISP_CTRL01 0x5001
+#define OV9740_ISP_CTRL03 0x5003
+#define OV9740_ISP_CTRL05 0x5005
+#define OV9740_ISP_CTRL12 0x5012
+#define OV9740_ISP_CTRL19 0x5019
+#define OV9740_ISP_CTRL1A 0x501A
+#define OV9740_ISP_CTRL1E 0x501E
+#define OV9740_ISP_CTRL1F 0x501F
+#define OV9740_ISP_CTRL20 0x5020
+#define OV9740_ISP_CTRL21 0x5021
+
+/* AWB */
+#define OV9740_AWB_CTRL00 0x5180
+#define OV9740_AWB_CTRL01 0x5181
+#define OV9740_AWB_CTRL02 0x5182
+#define OV9740_AWB_CTRL03 0x5183
+#define OV9740_AWB_ADV_CTRL01 0x5184
+#define OV9740_AWB_ADV_CTRL02 0x5185
+#define OV9740_AWB_ADV_CTRL03 0x5186
+#define OV9740_AWB_ADV_CTRL04 0x5187
+#define OV9740_AWB_ADV_CTRL05 0x5188
+#define OV9740_AWB_ADV_CTRL06 0x5189
+#define OV9740_AWB_ADV_CTRL07 0x518A
+#define OV9740_AWB_ADV_CTRL08 0x518B
+#define OV9740_AWB_ADV_CTRL09 0x518C
+#define OV9740_AWB_ADV_CTRL10 0x518D
+#define OV9740_AWB_ADV_CTRL11 0x518E
+#define OV9740_AWB_CTRL0F 0x518F
+#define OV9740_AWB_CTRL10 0x5190
+#define OV9740_AWB_CTRL11 0x5191
+#define OV9740_AWB_CTRL12 0x5192
+#define OV9740_AWB_CTRL13 0x5193
+#define OV9740_AWB_CTRL14 0x5194
+
+/* MIPI Control */
+#define OV9740_MIPI_CTRL00 0x4800
+#define OV9740_MIPI_3837 0x3837
+#define OV9740_MIPI_CTRL01 0x4801
+#define OV9740_MIPI_CTRL03 0x4803
+#define OV9740_MIPI_CTRL05 0x4805
+#define OV9740_VFIFO_RD_CTRL 0x4601
+#define OV9740_MIPI_CTRL_3012 0x3012
+#define OV9740_SC_CMMM_MIPI_CTR 0x3014
+
+// Scene Mode
+uint32_t ov9740_scene_mode_auto[] = {};
+
+uint32_t ov9740_scene_mode_night[] = {};
+
+
+// White Balance
+uint32_t ov9740_wb_auto [] = {};
+
+uint32_t ov9740_wb_incandescent [] = {};
+
+uint32_t ov9740_wb_fluorescent [] = {};
+
+uint32_t ov9740_wb_daylight [] = {};
+
+uint32_t ov9740_wb_cloudy [] = {};
+
+uint32_t ov9740_wb_tungsten [] = {};
+
+
+// Exposure
+uint32_t ov9740_exposure_neg6[] = {};
+
+uint32_t ov9740_exposure_neg3[] = {};
+
+uint32_t ov9740_exposure_zero[] = {};
+
+uint32_t ov9740_exposure_pos3[] = {};
+
+uint32_t ov9740_exposure_pos6[] = {};
+
+/*
+ * The color effect settings
+ */
+uint32_t ov9740_colorfx_none[] = {};
+
+uint32_t ov9740_colorfx_bw[] = {};
+
+uint32_t ov9740_colorfx_sepia[] = {};
+
+uint32_t ov9740_colorfx_negative[] = {};
+
+uint32_t ov9740_colorfx_emboss[] = {
+
+};
+
+uint32_t ov9740_colorfx_sketch[] = {
+};
+
+uint32_t ov9740_colorfx_sky_blue[] = {
+};
+
+uint32_t ov9740_colorfx_grass_green[] = {
+};
+
+uint32_t ov9740_colorfx_skin_whiten[] = {
+};
+
+uint32_t ov9740_colorfx_vivid[] = {
+};
+
+// Brightness
+uint32_t ov9740_brightness_neg4[] = {};
+
+uint32_t ov9740_brightness_neg3[] = {};
+
+uint32_t ov9740_brightness_neg2[] = {};
+
+uint32_t ov9740_brightness_neg1[] = {};
+
+uint32_t ov9740_brightness_zero[] = {};
+
+uint32_t ov9740_brightness_pos1[] = {};
+
+uint32_t ov9740_brightness_pos2[] = {};
+
+uint32_t ov9740_brightness_pos3[] = {};
+
+uint32_t ov9740_brightness_pos4[] = {};
+
+// Contrast
+uint32_t ov9740_contrast_neg4[] = {};
+
+uint32_t ov9740_contrast_neg3[] = {};
+
+uint32_t ov9740_contrast_neg2[] = {};
+
+uint32_t ov9740_contrast_neg1[] = {};
+
+uint32_t ov9740_contrast_zero[] = {};
+
+uint32_t ov9740_contrast_pos1[] = {};
+
+uint32_t ov9740_contrast_pos2[] = {};
+
+uint32_t ov9740_contrast_pos3[] = {};
+
+uint32_t ov9740_contrast_pos4[] = {};
+
+// Saturation
+uint32_t ov9740_saturation_neg4[] = {};
+
+uint32_t ov9740_saturation_neg3[] = {};
+
+uint32_t ov9740_saturation_neg2[] = {};
+
+uint32_t ov9740_saturation_neg1[] = {};
+
+uint32_t ov9740_saturation_zero[] = {};
+
+uint32_t ov9740_saturation_pos1[] = {};
+
+uint32_t ov9740_saturation_pos2[] = {};
+
+uint32_t ov9740_saturation_pos3[] = {};
+
+uint32_t ov9740_saturation_pos4[] = {};
+
+
+// Resolution
+uint32_t ov9740_640_480_regs[]={
+ 0x0101, 0x01 ,
+ 0x3104, 0x20 ,
+ 0x0305, 0x03 ,
+ 0x0307, 0x5b ,
+ 0x0303, 0x02 ,
+ 0x0301, 0x0a ,
+ 0x3010, 0x01 ,
+ 0x300e, 0x12 ,
+ 0x0340, 0x03 ,
+ 0x0341, 0x07 ,
+ 0x0342, 0x06 ,
+ 0x0343, 0x62 ,
+ 0x0344, 0x00 ,
+ 0x0345, 0xa8 ,
+ 0x0346, 0x00 ,
+ 0x0347, 0x04 ,
+ 0x0348, 0x04 ,
+ 0x0349, 0x67 ,
+ 0x034a, 0x02 ,
+ 0x034b, 0xd8 ,
+ 0x034c, 0x02 ,
+ 0x034d, 0x80 ,
+ 0x034e, 0x01 ,
+ 0x034f, 0xe0 ,
+ 0x3707, 0x11 ,
+ 0x3833, 0x04 ,
+ 0x3835, 0x04 ,
+ 0x3819, 0x6e ,
+ 0x3817, 0x94 ,
+ 0x3831, 0x40 ,
+ 0x381a, 0x00 ,
+ 0x4608, 0x02 ,
+ 0x4609, 0x70 ,
+ 0x5001, 0xff ,
+ 0x5003, 0xff ,
+ 0x501e, 0x03 ,
+ 0x501f, 0xc0 ,
+ 0x5020, 0x02 ,
+ 0x5021, 0xd0 ,
+ 0x530d, 0x06 ,
+ 0x0100, 0x01 ,
+};
+
+uint32_t ov9740_800_600_regs[]={};
+
+uint32_t ov9740_1280_720_regs[]={
+ 0x0101, 0x01 ,
+ 0x3104, 0x20 ,
+ 0x0305, 0x03 ,
+ 0x0307, 0x5f ,
+ 0x0303, 0x01 ,
+ 0x0301, 0x0a ,
+ 0x3010, 0x01 ,
+ 0x300e, 0x01 ,
+ 0x0340, 0x03 ,
+ 0x0341, 0x07 ,
+ 0x0342, 0x06 ,
+ 0x0343, 0x62 ,
+ 0x0344, 0x00 ,
+ 0x0345, 0x08 ,
+ 0x0346, 0x00 ,
+ 0x0347, 0x04 ,
+ 0x0348, 0x05 ,
+ 0x0349, 0x0c ,
+ 0x034a, 0x02 ,
+ 0x034b, 0xd8 ,
+ 0x034c, 0x05 ,
+ 0x034d, 0x00 ,
+ 0x034e, 0x02 ,
+ 0x034f, 0xd0 ,
+ 0x3707, 0x11 ,
+ 0x3833, 0x04 ,
+ 0x3835, 0x04 ,
+ 0x3819, 0x6e ,
+ 0x3817, 0x94 ,
+ 0x3831, 0x40 ,
+ 0x381a, 0x00 ,
+ 0x4608, 0x00 ,
+ 0x4609, 0x04 ,
+ 0x5001, 0xff ,
+ 0x5003, 0xff ,
+ 0x501e, 0x05 ,
+ 0x501f, 0x00 ,
+ 0x5020, 0x02 ,
+ 0x5021, 0xd0 ,
+ 0x530d, 0x06 ,
+ 0x0100, 0x01 ,
+};
+
+uint32_t ov9740_1600_1200_regs[]={
+
+};
+
+// init
+
+uint32_t ov9740_default_regs_init[]={
+// Software RESET
+ 0x0103, 0x01 ,
+//Mirror on, flip off
+ 0x0101, 0x01 ,
+// PLL setting
+ 0x3104, 0x20 , // Reset PLL2
+ 0x0305, 0x03 , // PLL1 pre_divider
+ 0x0307, 0x5f , // PLL1 multiplier
+ 0x0303, 0x01 , // PLL1 video pclk divider
+ 0x0301, 0x0a , // PLL1 video sysclk divider
+ 0x3010, 0x01 , // PLL1 scale divider
+//Timing setting
+ 0x0340, 0x03 , // VTS
+ 0x0341, 0x07 , // VTS
+ 0x0342, 0x06 , // HTS
+ 0x0343, 0x62 , // HTS
+ 0x0344, 0x00 , // X start
+ 0x0345, 0x08 , // X start
+ 0x0346, 0x00 , // Y start
+ 0x0347, 0x04 , // Y start
+ 0x0348, 0x05 , // X end
+ 0x0349, 0x0c , // X end
+ 0x034a, 0x02 , // Y end
+ 0x034b, 0xd8 , // Y end
+ 0x034c, 0x05 , // H output size
+ 0x034d, 0x00 , // H output size
+ 0x034e, 0x02 , // V output size
+ 0x034f, 0xd0 , // V output size
+// Output select
+ 0x3002, 0xe8 , // Vsync, Href, PCLK outout
+ 0x3004, 0x03 , // D[9:8] output
+ 0x3005, 0xff , // D[7:0] output
+ 0x3013, 0x60 , // MIPI control?
+ 0x3026, 0x00 , // D[9:8] data path
+ 0x3027, 0x00 , // D[7:0] data path
+// Analog control
+ 0x3601, 0x40 , // Analog control
+ 0x3602, 0x16 , // Analog control
+ 0x3603, 0xaa , // Analog control
+ 0x3604, 0x0c , // Analog control
+ 0x3610, 0xa1 , // Analog control
+ 0x3612, 0x24 , // Analog control
+ 0x3620, 0x66 , // Analog control
+ 0x3621, 0xc0 , // Analog control
+ 0x3622, 0x9f , // Analog control
+ 0x3630, 0xd2 , // Analog control
+ 0x3631, 0x5e , // Analog control
+ 0x3632, 0x27 , // Analog control
+ 0x3633, 0x50 , // Analog control
+// Sensor control
+ 0x3703, 0x42 , // Sensor control
+ 0x3704, 0x10 , // Sensor control
+ 0x3705, 0x45 , // Sensor control
+ 0x3707, 0x11 , // Sensor control
+// Timing control
+ 0x3817, 0x94 , // Internal timing control
+ 0x3819, 0x6e , // Internal timing control
+ 0x3831, 0x40 , // Digital gain enable
+ 0x3833, 0x04 , // Internal timing control
+ 0x3835, 0x04 , // Internal timing control
+// AEC/AGC control
+ 0x3503, 0x10 , // AEC/AGC off
+ 0x3a18, 0x01 , // Gain ceiling
+ 0x3a19, 0xb5 , // Gain ceiling = 27.3x
+ 0x3a1a, 0x05 , // Max diff
+ 0x3a11, 0x90 , // control zone High
+ 0x3a1b, 0x4a , // stable high out
+ 0x3a0f, 0x48 , // stable high in
+ 0x3a10, 0x44 , // stable low in
+ 0x3a1e, 0x42 , // stable low out
+ 0x3a1f, 0x22 , // control zone low
+// Banding filter
+ 0x3a08, 0x00 , // 50Hz banding step
+ 0x3a09, 0xe8 , // 50Hz banding step
+ 0x3a0e, 0x03 , // 50Hz banding Max
+ 0x3a14, 0x09 , // 50Hz Max exposure
+ 0x3a15, 0x15 , // 50Hz Max exposure
+ 0x3a0a, 0x00 , // 60Hz banding step
+ 0x3a0b, 0xc0 , // 60Hz banding step
+ 0x3a0d, 0x04 , // 60Hz banding Max
+ 0x3a02, 0x09 , // 60Hz Max exposure
+ 0x3a03, 0x15 , // 60Hz Max exposure
+//50/60 detection
+ 0x3c0a, 0x9c , // Number of samples
+ 0x3c0b, 0x3f , // Number of samples
+// BLC control
+ 0x4002, 0x45 , // BLC auto enable
+ 0x4005, 0x18 , // BLC mode
+// DVP control
+ 0x4702, 0x04 , // Vsync length high
+ 0x4704, 0x00 , // Vsync mode
+ 0x4706, 0x08 , // SOF/EOF negative edge to Vsync positive edge
+// ISP control
+ 0x5000, 0xff , // [7]LC [6]Gamma [3]DNS [2]BPC [1]WPC [0]CIP
+ 0x5001, 0xff , // [7]SDE [6]UV adjust [4]scale [3]contrast [2]UV average [1]CMX [0]AWB
+ 0x5003, 0xff , // [7]PAD [5]Buffer [3]Vario [1]BLC [0]AWB gain
+//AWB
+ 0x5180, 0xf0 , //AWB setting
+ 0x5181, 0x00 , //AWB setting
+ 0x5182, 0x41 , //AWB setting
+ 0x5183, 0x42 , //AWB setting
+ 0x5184, 0x80 , //AWB setting
+ 0x5185, 0x68 , //AWB setting
+ 0x5186, 0x93 , //AWB setting
+ 0x5187, 0xa8 , //AWB setting
+ 0x5188, 0x17 , //AWB setting
+ 0x5189, 0x45 , //AWB setting
+ 0x518a, 0x27 , //AWB setting
+ 0x518b, 0x41 , //AWB setting
+ 0x518c, 0x2d , //AWB setting
+ 0x518d, 0xf0 , //AWB setting
+ 0x518e, 0x10 , //AWB setting
+ 0x518f, 0xff , //AWB setting
+ 0x5190, 0x00 , //AWB setting
+ 0x5191, 0xff , //AWB setting
+ 0x5192, 0x00 , //AWB setting
+ 0x5193, 0xff , //AWB setting
+ 0x5194, 0x00 , //AWB setting
+// DNS
+ 0x529a, 0x02 , //noise Y list 0
+ 0x529b, 0x08 , //noise Y list 1
+ 0x529c, 0x0a , //noise Y list 2
+ 0x529d, 0x10 , //noise Y list 3
+ 0x529e, 0x10 , //noise Y list 4
+ 0x529f, 0x28 , //noise Y list 5
+ 0x52a0, 0x32 , //noise Y list 6
+ 0x52a2, 0x00 , //noise UV list 0[8]
+ 0x52a3, 0x02 , //noise UV list 0[7:0]
+ 0x52a4, 0x00 , //noise UV list 1[8]
+ 0x52a5, 0x04 , //noise UV list 1[7:0]
+ 0x52a6, 0x00 , //noise UV list 2[8]
+ 0x52a7, 0x08 , //noise UV list 2[7:0]
+ 0x52a8, 0x00 , //noise UV list 3[8]
+ 0x52a9, 0x10 , //noise UV list 3[7:0]
+ 0x52aa, 0x00 , //noise UV list 4[8]
+ 0x52ab, 0x38 , //noise UV list 4[7:0]
+ 0x52ac, 0x00 , //noise UV list 5[8]
+ 0x52ad, 0x3c , //noise UV list 5[7:0]
+ 0x52ae, 0x00 , //noise UV list 6[8]
+ 0x52af, 0x4c , //noise UV list 6[7:0]
+//CIP
+ 0x530d, 0x06 , //max sharpen
+//CMX
+ 0x5380, 0x01 , //CMX00[9:8]
+ 0x5381, 0x00 , //CMX00[7:0]
+ 0x5382, 0x00 , //CMX01[9:8]
+ 0x5383, 0x0d , //CMX01[7:0]
+ 0x5384, 0x00 , //CMX02[9:8]
+ 0x5385, 0x2f , //CMX02[7:0]
+ 0x5386, 0x00 , //CMX10[9:8]
+ 0x5387, 0x00 , //CMX10[7:0]
+ 0x5388, 0x00 , //CMX11[9:8]
+ 0x5389, 0xd3 , //CMX11[7:0]
+ 0x538a, 0x00 , //CMX12[9:8]
+ 0x538b, 0x0f , //CMX12[7:0]
+ 0x538c, 0x00 , //CMX20[9:8]
+ 0x538d, 0x00 , //CMX20[7:0]
+ 0x538e, 0x00 , //CMX21[9:8]
+ 0x538f, 0x32 , //CMX21[7:0]
+ 0x5390, 0x00 , //CMX22[9:8]
+ 0x5391, 0x94 , //CMX22[7:0]
+ 0x5392, 0x00 , //bit[0] CMX22 sign
+ 0x5393, 0xa4 , //CMX 21,20, 12, 11, 10, 02, 01, 00 sign
+ 0x5394, 0x18 , //CMX shift
+// Contrast
+ 0x5401, 0x2c , // bit[7:4] th1, birt[3:0]th2
+ 0x5403, 0x28 , // maximum high level
+ 0x5404, 0x06 , // minimum high level
+ 0x5405, 0xe0 , // minimum high level
+//Y Gamma
+ 0x5480, 0x04 , //Y List 0
+ 0x5481, 0x12 , //Y List 1
+ 0x5482, 0x27 , //Y List 2
+ 0x5483, 0x49 , //Y List 3
+ 0x5484, 0x57 , //Y List 4
+ 0x5485, 0x66 , //Y List 5
+ 0x5486, 0x75 , //Y List 6
+ 0x5487, 0x81 , //Y List 7
+ 0x5488, 0x8c , //Y List 8
+ 0x5489, 0x95 , //Y List 9
+ 0x548a, 0xa5 , //Y List a
+ 0x548b, 0xb2 , //Y List b
+ 0x548c, 0xc8 , //Y List c
+ 0x548d, 0xd9 , //Y List d
+ 0x548e, 0xec , //Y List e
+//UV Gamma
+ 0x5490, 0x01 , //UV List 0[11:8]
+ 0x5491, 0xc0 , //UV List 0[7:0]
+ 0x5492, 0x03 , //UV List 1[11:8]
+ 0x5493, 0x00 , //UV List 1[7:0]
+ 0x5494, 0x03 , //UV List 2[11:8]
+ 0x5495, 0xe0 , //UV List 2[7:0]
+ 0x5496, 0x03 , //UV List 3[11:8]
+ 0x5497, 0x10 , //UV List 3[7:0]
+ 0x5498, 0x02 , //UV List 4[11:8]
+ 0x5499, 0xac , //UV List 4[7:0]
+ 0x549a, 0x02 , //UV List 5[11:8]
+ 0x549b, 0x75 , //UV List 5[7:0]
+ 0x549c, 0x02 , //UV List 6[11:8]
+ 0x549d, 0x44 , //UV List 6[7:0]
+ 0x549e, 0x02 , //UV List 7[11:8]
+ 0x549f, 0x20 , //UV List 7[7:0]
+ 0x54a0, 0x02 , //UV List 8[11:8]
+ 0x54a1, 0x07 , //UV List 8[7:0]
+ 0x54a2, 0x01 , //UV List 9[11:8]
+ 0x54a3, 0xec , //UV List 9[7:0]
+ 0x54a4, 0x01 , //UV List 10[11:8]
+ 0x54a5, 0xc0 , //UV List 10[7:0]
+ 0x54a6, 0x01 , //UV List 11[11:8]
+ 0x54a7, 0x9b , //UV List 11[7:0]
+ 0x54a8, 0x01 , //UV List 12[11:8]
+ 0x54a9, 0x63 , //UV List 12[7:0]
+ 0x54aa, 0x01 , //UV List 13[11:8]
+ 0x54ab, 0x2b , //UV List 13[7:0]
+ 0x54ac, 0x01 , //UV List 14[11:8]
+ 0x54ad, 0x22 , //UV List 14[7:0]
+
+// UV adjusti
+ 0x4708, 0x01 ,
+ 0x4300, 0x30 ,
+ 0x5501, 0x1c , //UV adjust manual
+ 0x5502, 0x00 , //UV adjust th1[8]
+ 0x5503, 0x40 , //UV adjust th1[7:0]
+ 0x5504, 0x00 , //UV adjust th2[8]
+ 0x5505, 0x80 , //UV adjust th2[7:0]
+// Lens correction
+ 0x5800, 0x1c , // Lenc g00
+ 0x5801, 0x16 , // Lenc g01
+ 0x5802, 0x15 , // Lenc g02
+ 0x5803, 0x16 , // Lenc g03
+ 0x5804, 0x18 , // Lenc g04
+ 0x5805, 0x1a , // Lenc g05
+ 0x5806, 0x0c , // Lenc g10
+ 0x5807, 0x0a , // Lenc g11
+ 0x5808, 0x08 , // Lenc g12
+ 0x5809, 0x08 , // Lenc g13
+ 0x580a, 0x0a , // Lenc g14
+ 0x580b, 0x0b , // Lenc g15
+ 0x580c, 0x05 , // Lenc g20
+ 0x580d, 0x02 , // Lenc g21
+ 0x580e, 0x00 , // Lenc g22
+ 0x580f, 0x00 , // Lenc g23
+ 0x5810, 0x02 , // Lenc g24
+ 0x5811, 0x05 , // Lenc g25
+ 0x5812, 0x04 , // Lenc g30
+ 0x5813, 0x01 , // Lenc g31
+ 0x5814, 0x00 , // Lenc g32
+ 0x5815, 0x00 , // Lenc g33
+ 0x5816, 0x02 , // Lenc g34
+ 0x5817, 0x03 , // Lenc g35
+ 0x5818, 0x0a , // Lenc g40
+ 0x5819, 0x07 , // Lenc g41
+ 0x581a, 0x05 , // Lenc g42
+ 0x581b, 0x05 , // Lenc g43
+ 0x581c, 0x08 , // Lenc g44
+ 0x581d, 0x0b , // Lenc g45
+ 0x581e, 0x15 , // Lenc g50
+ 0x581f, 0x14 , // Lenc g51
+ 0x5820, 0x14 , // Lenc g52
+ 0x5821, 0x13 , // Lenc g53
+ 0x5822, 0x17 , // Lenc g54
+ 0x5823, 0x16 , // Lenc g55
+ 0x5824, 0x46 , // Lenc b00, r00
+ 0x5825, 0x4c , // Lenc b01, r01
+ 0x5826, 0x6c , // Lenc b02, r02
+ 0x5827, 0x4c , // Lenc b03, r03
+ 0x5828, 0x80 , // Lenc b04, r04
+ 0x5829, 0x2e , // Lenc b10, r10
+ 0x582a, 0x48 , // Lenc b11, r11
+ 0x582b, 0x46 , // Lenc b12, r12
+ 0x582c, 0x2a , // Lenc b13, r13
+ 0x582d, 0x68 , // Lenc b14, r14
+ 0x582e, 0x08 , // Lenc b20, r20
+ 0x582f, 0x26 , // Lenc b21, r21
+ 0x5830, 0x44 , // Lenc b22, r22
+ 0x5831, 0x46 , // Lenc b23, r23
+ 0x5832, 0x62 , // Lenc b24, r24
+ 0x5833, 0x0c , // Lenc b30, r30
+ 0x5834, 0x28 , // Lenc b31, r31
+ 0x5835, 0x46 , // Lenc b32, r32
+ 0x5836, 0x28 , // Lenc b33, r33
+ 0x5837, 0x88 , // Lenc b34, r34
+ 0x5838, 0x0e , // Lenc b40, r40
+ 0x5839, 0x0e , // Lenc b41, r41
+ 0x583a, 0x2c , // Lenc b42, r42
+ 0x583b, 0x2e , // Lenc b43, r43
+ 0x583c, 0x46 , // Lenc b44, r44
+ 0x583d, 0xca , // Lenc b offset, r offset
+ 0x583e, 0xf0 , // max gain
+ 0x5842, 0x02 , // br hscale[10:8]
+ 0x5843, 0x5e , // br hscale[7:0]
+ 0x5844, 0x04 , // br vscale[10:8]
+ 0x5845, 0x32 , // br vscale[7:0]
+ 0x5846, 0x03 , // g hscael[11:8]
+ 0x5847, 0x29 , // g hscale[7:0]
+ 0x5848, 0x02 , // g vscale[11:8]
+ 0x5849, 0xcc , // g vscale[7:0]
+ 0x5580, 0x06 , // sharpness on, saturation on
+ 0x3c00, 0x04 , // band 50hz
+ 0x3c01, 0x80 , // manual band on
+// Start streaming
+ 0x0100, 0x01 , // start streaming
+};
+uint32_t ov9740_default_regs_exit[]={};
+
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/s5k5ca/s5k5ca.c b/drivers/media/video/wmt_v4l2/sensors/s5k5ca/s5k5ca.c
new file mode 100755
index 00000000..868fe727
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/s5k5ca/s5k5ca.c
@@ -0,0 +1,374 @@
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "s5k5ca.h"
+
+#define sensor_write_array(sd, arr, sz) \
+ cmos_init_16bit_addr_16bit_data(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read16data(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write16data(sd->i2c_addr, reg, val)
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint32_t *regs;
+ size_t size;
+};
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+ CMOS_WIN_SIZE("VGA", 640, 480, s5k5ca_640_480_regs),
+ CMOS_WIN_SIZE("VGA", 2048, 1536, s5k5ca_2048_1536_regs),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint32_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = s5k5ca_wb_auto;
+ size = ARRAY_SIZE(s5k5ca_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = s5k5ca_wb_incandescent;
+ size = ARRAY_SIZE(s5k5ca_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = s5k5ca_wb_daylight;
+ size = ARRAY_SIZE(s5k5ca_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = s5k5ca_wb_cloudy;
+ size = ARRAY_SIZE(s5k5ca_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = s5k5ca_wb_fluorescent;
+ size = ARRAY_SIZE(s5k5ca_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_scenemode(struct cmos_subdev *sd, enum v4l2_scene_mode value)
+{
+ uint32_t *regs;
+ size_t size;
+
+ switch (value) {
+ case SCENE_MODE_AUTO:
+ regs = s5k5ca_scene_mode_auto;
+ size = ARRAY_SIZE(s5k5ca_scene_mode_auto);
+ break;
+ case SCENE_MODE_NIGHTSHOT:
+ regs = s5k5ca_scene_mode_night;
+ size = ARRAY_SIZE(s5k5ca_scene_mode_night);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_exposure(struct cmos_subdev *sd, enum v4l2_exposure_mode value)
+{
+ uint32_t *regs;
+ size_t size;
+
+ switch (value) {
+ case -2:
+ regs = s5k5ca_exposure_neg6;
+ size = ARRAY_SIZE(s5k5ca_exposure_neg6);
+ break;
+ case -1:
+ regs = s5k5ca_exposure_neg3;
+ size = ARRAY_SIZE(s5k5ca_exposure_neg3);
+ break;
+ case 0:
+ regs = s5k5ca_exposure_zero;
+ size = ARRAY_SIZE(s5k5ca_exposure_zero);
+ break;
+ case 1:
+ regs = s5k5ca_exposure_pos3;
+ size = ARRAY_SIZE(s5k5ca_exposure_pos3);
+ break;
+ case 2:
+ regs = s5k5ca_exposure_pos6;
+ size = ARRAY_SIZE(s5k5ca_exposure_pos6);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+
+ uint32_t data;
+
+ sensor_write(sd,0xfcfc,0xd000);
+ sensor_write(sd,0x0028,0x7000);
+ sensor_write(sd,0x002a,0x0296);
+
+ data = 0;
+
+ switch (value) {
+ case 0:
+ data &= ~0x01;
+ break;
+ case 1:
+ data |= 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+ sensor_write(sd,0x0f12,data);
+ sensor_write(sd,0x0f12,data);
+ sensor_write(sd,0x002a,0x02c6);
+ sensor_write(sd,0x0f12,data);
+ sensor_write(sd,0x0f12,data);
+ sensor_write(sd,0x002a,0x02f6);
+ sensor_write(sd,0x0f12,data);
+ sensor_write(sd,0x0f12,data);
+ sensor_write(sd,0x002a,0x0326);
+ sensor_write(sd,0x0f12,data);
+ sensor_write(sd,0x0f12,data);
+ msleep(100);
+
+ return 0;
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+
+ uint32_t data;
+
+ sensor_write(sd,0xfcfc,0xd000);
+ sensor_write(sd,0x0028,0x7000);
+ sensor_write(sd,0x002a,0x0296);
+
+ data = 0;
+
+ switch (value) {
+ case 0:
+ data &= ~0x02;
+ break;
+ case 1:
+ data |= 0x02;
+ break;
+ default:
+ return -EINVAL;
+ }
+ sensor_write(sd,0x0f12,data);
+ sensor_write(sd,0x0f12,data);
+ sensor_write(sd,0x002a,0x02c6);
+ sensor_write(sd,0x0f12,data);
+ sensor_write(sd,0x0f12,data);
+ sensor_write(sd,0x002a,0x02f6);
+ sensor_write(sd,0x0f12,data);
+ sensor_write(sd,0x0f12,data);
+ sensor_write(sd,0x002a,0x0326);
+ sensor_write(sd,0x0f12,data);
+ sensor_write(sd,0x0f12,data);
+ msleep(100);
+
+ return 0;
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ case V4L2_CID_EXPOSURE:
+ return v4l2_ctrl_query_fill(qc, -2, 2, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return sensor_s_scenemode(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return sensor_s_exposure(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ default:
+ case WMT_V4L2_CID_CAMERA_ANTIBANDING:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win) {
+ pr_err("%s, s_mbus_fmt failed, width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+ return -EINVAL;
+ }
+ sensor_write_array(sd, win->regs, win->size);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+
+ return 0;
+}
+
+static int sensor_enum_framesizes(struct cmos_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ const struct cmos_win_size *win = &cmos_supported_win_sizes[index];
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ uint32_t data = 0;
+
+ sensor_write(sd,0xfcfc,0x0000);
+ data = sensor_read(sd,0x0040);
+
+ return (data == sd->id) ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ if (sensor_identify(sd)) {
+ return -1;
+ }
+
+ sensor_write_array(sd, s5k5ca_default_regs_init0,
+ ARRAY_SIZE(s5k5ca_default_regs_init0));
+ msleep(100);
+ sensor_write_array(sd, s5k5ca_default_regs_init1,
+ ARRAY_SIZE(s5k5ca_default_regs_init1));
+ msleep(100);
+ sensor_write_array(sd, s5k5ca_default_regs_init2,
+ ARRAY_SIZE(s5k5ca_default_regs_init2));
+
+ msleep(100);
+ sensor_write_array(sd, s5k5ca_default_regs_init3,
+ ARRAY_SIZE(s5k5ca_default_regs_init3));
+
+ msleep(100);
+ sensor_write_array(sd, s5k5ca_default_regs_init4,
+ ARRAY_SIZE(s5k5ca_default_regs_init3));
+
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ return 0;
+}
+
+static struct cmos_subdev_ops s5k5ca_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+ .enum_framesizes = sensor_enum_framesizes,
+};
+
+struct cmos_subdev s5k5ca = {
+ .name = "s5k5ca",
+ .i2c_addr = 0x3c,
+ .id = 0x5ca,
+ .max_width = 2048,
+ .max_height = 1536,
+ .ops = &s5k5ca_ops,
+};
+
+#if 0
+static int __init s5k5ca_init(void)
+{
+ return cmos_register_sudbdev(&s5k5ca);
+}
+
+static void __exit s5k5ca_exit(void)
+{
+ return cmos_unregister_subdev(&s5k5ca);
+}
+
+module_init(s5k5ca_init);
+module_exit(s5k5ca_exit);
+
+MODULE_LICENSE("GPL");
+#endif
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/s5k5ca/s5k5ca.h b/drivers/media/video/wmt_v4l2/sensors/s5k5ca/s5k5ca.h
new file mode 100755
index 00000000..6a5ed704
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/s5k5ca/s5k5ca.h
@@ -0,0 +1,3288 @@
+#ifndef S5K5CA_H
+#define S5K5CA_H
+
+// Scene Mode
+uint32_t s5k5ca_scene_mode_auto[] = {
+0xfcfc,0xd000,
+0x0028,0x7000,
+0x002A,0x023C,
+0x0F12,0x0000,
+0x002A,0x0240,
+0x0F12,0x0001,
+0x002A,0x0230,
+0x0F12,0x0001,
+0x002A,0x023E,
+0x0F12,0x0001,
+0x002A,0x0220,
+0x0F12,0x0001,
+0x0F12,0x0001,
+};
+
+uint32_t s5k5ca_scene_mode_night[] = {
+0xfcfc,0xd000,
+0x0028,0x7000,
+0x002A,0x023C,
+0x0F12,0x0003,
+0x002A,0x0240,
+0x0F12,0x0001,
+0x002A,0x0230,
+0x0F12,0x0001,
+0x002A,0x023E,
+0x0F12,0x0001,
+0x002A,0x0220,
+0x0F12,0x0001,
+0x0F12,0x0001,
+
+};
+
+
+// White Balance
+uint32_t s5k5ca_wb_auto [] = {
+0x002A,0x04D2,
+0x0F12,0x067F,
+};
+
+uint32_t s5k5ca_wb_incandescent [] = {
+0x002A, 0x04D2,
+0x0F12, 0x0677,
+0x002A, 0x04A0,
+0x0F12, 0x0126,
+0x0F12, 0x0001,
+0x002A, 0x04A4,
+0x0F12, 0x0100,
+0x0F12, 0x0001,
+0x002A, 0x04A8,
+0x0F12, 0x01C5,
+0x0F12, 0x0001,
+};
+
+uint32_t s5k5ca_wb_fluorescent [] = {
+0x002A, 0x04D2,
+0x0F12, 0x0677,
+0x002A, 0x04A0,
+0x0F12, 0x0145,
+0x0F12, 0x0001,
+0x002A, 0x04A4,
+0x0F12, 0x0100,
+0x0F12, 0x0001,
+0x002A, 0x04A8,
+0x0F12, 0x01EB,
+0x0F12, 0x0001,
+};
+
+uint32_t s5k5ca_wb_daylight [] = {
+0x002A, 0x04D2,
+0x0F12, 0x0677,
+0x002A, 0x04A0,
+0x0F12, 0x0166,
+0x0F12, 0x0001,
+0x002A, 0x04A4,
+0x0F12, 0x0100,
+0x0F12, 0x0001,
+0x002A, 0x04A8,
+0x0F12, 0x0145,
+0x0F12, 0x0001,
+};
+
+uint32_t s5k5ca_wb_cloudy [] = {
+0x002A, 0x04D2,
+0x0F12, 0x0677,
+0x002A, 0x04A0,
+0x0F12, 0x0128,
+0x0F12, 0x0001,
+0x002A, 0x04A4,
+0x0F12, 0x0100,
+0x0F12, 0x0001,
+0x002A, 0x04A8,
+0x0F12, 0x01AB,
+0x0F12, 0x0001,
+};
+
+
+// Exposure
+uint32_t s5k5ca_exposure_neg6[] = {
+0x0028, 0x7000,
+0x002A, 0x0F70,
+0x0F12, 0x000D,
+};
+
+uint32_t s5k5ca_exposure_neg3[] = {
+0x0028, 0x7000,
+0x002A, 0x0F70,
+0x0F12, 0x0031,
+};
+
+uint32_t s5k5ca_exposure_zero[] = {
+0x0028, 0x7000,
+0x002A, 0x0F70,
+0x0F12, 0x003D,
+};
+
+uint32_t s5k5ca_exposure_pos3[] = {
+0x0028, 0x7000,
+0x002A, 0x0F70,
+0x0F12, 0x004b,
+};
+
+uint32_t s5k5ca_exposure_pos6[] = {
+0x0028, 0x7000,
+0x002A, 0x0F70,
+0x0F12, 0x0075,
+};
+
+
+// Resolution
+uint32_t s5k5ca_2048_1536_regs[]={
+0xfcfc,0xd000,
+0x0028,0x7000,
+0x002a,0x0244,
+0x0f12,0x0000,
+0x0f12,0x0001,
+0x002a,0x0230,
+0x0f12,0x0001,
+0x002a,0x0224,
+0x0f12,0x0001,
+0x0f12,0x0001,
+};
+
+
+uint32_t s5k5ca_640_480_regs[]={
+0xfcfc,0xd000,
+0x0028,0x7000,
+0x002A,0x023C,
+0x0F12,0x0000,
+0x002A,0x0240,
+0x0F12,0x0001,
+0x002A,0x0230,
+0x0F12,0x0001,
+0x002A,0x023E,
+0x0F12,0x0001,
+0x002A,0x0220,
+0x0F12,0x0001,
+0x0F12,0x0001,
+};
+
+
+uint32_t s5k5ca_default_regs_init0[]={
+0xFCFC, 0xD000,
+0x0010, 0x0001,
+0x1030, 0x0000,
+0x0014, 0x0001,
+};
+
+uint32_t s5k5ca_default_regs_init1[]={
+0x0028, 0x7000,
+0x002A, 0x2CF8,
+0x0F12, 0xB570,
+0x0F12, 0x4828,
+0x0F12, 0x21C0,
+0x0F12, 0x8041,
+0x0F12, 0x4826,
+0x0F12, 0x4A27,
+0x0F12, 0x3020,
+0x0F12, 0x8382,
+0x0F12, 0x1D12,
+0x0F12, 0x83C2,
+0x0F12, 0x4823,
+0x0F12, 0x3040,
+0x0F12, 0x8041,
+0x0F12, 0x4822,
+0x0F12, 0x4923,
+0x0F12, 0x3060,
+0x0F12, 0x8381,
+0x0F12, 0x1D09,
+0x0F12, 0x83C1,
+0x0F12, 0x4822,
+0x0F12, 0x491E,
+0x0F12, 0x8802,
+0x0F12, 0x3980,
+0x0F12, 0x804A,
+0x0F12, 0x8842,
+0x0F12, 0x808A,
+0x0F12, 0x8882,
+0x0F12, 0x80CA,
+0x0F12, 0x88C2,
+0x0F12, 0x810A,
+0x0F12, 0x8902,
+0x0F12, 0x491D,
+0x0F12, 0x80CA,
+0x0F12, 0x8942,
+0x0F12, 0x814A,
+0x0F12, 0x8982,
+0x0F12, 0x830A,
+0x0F12, 0x89C2,
+0x0F12, 0x834A,
+0x0F12, 0x8A00,
+0x0F12, 0x4919,
+0x0F12, 0x8188,
+0x0F12, 0x4919,
+0x0F12, 0x481A,
+0x0F12, 0xF000,
+0x0F12, 0xF9FC,
+0x0F12, 0x4819,
+0x0F12, 0x4D1A,
+0x0F12, 0x6028,
+0x0F12, 0x491A,
+0x0F12, 0x0028,
+0x0F12, 0x3080,
+0x0F12, 0x6341,
+0x0F12, 0x4919,
+0x0F12, 0x4819,
+0x0F12, 0xF000,
+0x0F12, 0xF9F1,
+0x0F12, 0x4919,
+0x0F12, 0x0028,
+0x0F12, 0x3040,
+0x0F12, 0x62C1,
+0x0F12, 0x4818,
+0x0F12, 0x002C,
+0x0F12, 0x3C40,
+0x0F12, 0x63E0,
+0x0F12, 0x4817,
+0x0F12, 0x6320,
+0x0F12, 0x4817,
+0x0F12, 0x61A8,
+0x0F12, 0x4917,
+0x0F12, 0x4817,
+0x0F12, 0xF000,
+0x0F12, 0xF9E1,
+0x0F12, 0x4917,
+0x0F12, 0x4817,
+0x0F12, 0xF000,
+0x0F12, 0xF9DD,
+0x0F12, 0x4817,
+0x0F12, 0x62A0,
+0x0F12, 0xBC70,
+0x0F12, 0xBC08,
+0x0F12, 0x4718,
+0x0F12, 0x1100,
+0x0F12, 0xD000,
+0x0F12, 0x267C,
+0x0F12, 0x0000,
+0x0F12, 0x3904,
+0x0F12, 0x0000,
+0x0F12, 0x3274,
+0x0F12, 0x7000,
+0x0F12, 0xF400,
+0x0F12, 0xD000,
+0x0F12, 0xF520,
+0x0F12, 0xD000,
+0x0F12, 0x2DF5,
+0x0F12, 0x7000,
+0x0F12, 0x89A9,
+0x0F12, 0x0000,
+0x0F12, 0x2E47,
+0x0F12, 0x7000,
+0x0F12, 0x00C0,
+0x0F12, 0x7000,
+0x0F12, 0x2E6F,
+0x0F12, 0x7000,
+0x0F12, 0x2EA5,
+0x0F12, 0x7000,
+0x0F12, 0xB4F7,
+0x0F12, 0x0000,
+0x0F12, 0x2F2F,
+0x0F12, 0x7000,
+0x0F12, 0x2F53,
+0x0F12, 0x7000,
+0x0F12, 0x2FF9,
+0x0F12, 0x7000,
+0x0F12, 0x300D,
+0x0F12, 0x7000,
+0x0F12, 0x2FE1,
+0x0F12, 0x7000,
+0x0F12, 0x013D,
+0x0F12, 0x0001,
+0x0F12, 0x3093,
+0x0F12, 0x7000,
+0x0F12, 0x5823,
+0x0F12, 0x0000,
+0x0F12, 0x30E1,
+0x0F12, 0x7000,
+0x0F12, 0xB570,
+0x0F12, 0x6804,
+0x0F12, 0x6845,
+0x0F12, 0x6881,
+0x0F12, 0x6840,
+0x0F12, 0x2900,
+0x0F12, 0x6880,
+0x0F12, 0xD007,
+0x0F12, 0x49BA,
+0x0F12, 0x8949,
+0x0F12, 0x084A,
+0x0F12, 0x1880,
+0x0F12, 0xF000,
+0x0F12, 0xF9A6,
+0x0F12, 0x80A0,
+0x0F12, 0xE000,
+0x0F12, 0x80A0,
+0x0F12, 0x88A0,
+0x0F12, 0x2800,
+0x0F12, 0xD010,
+0x0F12, 0x68A9,
+0x0F12, 0x6828,
+0x0F12, 0x084A,
+0x0F12, 0x1880,
+0x0F12, 0xF000,
+0x0F12, 0xF99A,
+0x0F12, 0x8020,
+0x0F12, 0x1D2D,
+0x0F12, 0xCD03,
+0x0F12, 0x084A,
+0x0F12, 0x1880,
+0x0F12, 0xF000,
+0x0F12, 0xF993,
+0x0F12, 0x8060,
+0x0F12, 0xBC70,
+0x0F12, 0xBC08,
+0x0F12, 0x4718,
+0x0F12, 0x2000,
+0x0F12, 0x8060,
+0x0F12, 0x8020,
+0x0F12, 0xE7F8,
+0x0F12, 0xB510,
+0x0F12, 0xF000,
+0x0F12, 0xF98E,
+0x0F12, 0x49A9,
+0x0F12, 0x8A08,
+0x0F12, 0x6889,
+0x0F12, 0x4348,
+0x0F12, 0x49A8,
+0x0F12, 0x0080,
+0x0F12, 0xF000,
+0x0F12, 0xF980,
+0x0F12, 0x213F,
+0x0F12, 0x283F,
+0x0F12, 0xD200,
+0x0F12, 0x0001,
+0x0F12, 0x48A5,
+0x0F12, 0x8141,
+0x0F12, 0xBC10,
+0x0F12, 0xBC08,
+0x0F12, 0x4718,
+0x0F12, 0xB510,
+0x0F12, 0xF000,
+0x0F12, 0xF982,
+0x0F12, 0x48A2,
+0x0F12, 0x8A40,
+0x0F12, 0x2800,
+0x0F12, 0xD0F5,
+0x0F12, 0x48A1,
+0x0F12, 0x49A2,
+0x0F12, 0x8800,
+0x0F12, 0x4AA2,
+0x0F12, 0x2805,
+0x0F12, 0xD003,
+0x0F12, 0x4BA1,
+0x0F12, 0x795B,
+0x0F12, 0x2B00,
+0x0F12, 0xD003,
+0x0F12, 0x2001,
+0x0F12, 0x8008,
+0x0F12, 0x8010,
+0x0F12, 0xE7E7,
+0x0F12, 0x2800,
+0x0F12, 0xD1E5,
+0x0F12, 0x2000,
+0x0F12, 0x8008,
+0x0F12, 0x8010,
+0x0F12, 0xE7E1,
+0x0F12, 0xB5F8,
+0x0F12, 0x2407,
+0x0F12, 0x2C06,
+0x0F12, 0xD035,
+0x0F12, 0x2C07,
+0x0F12, 0xD033,
+0x0F12, 0x4894,
+0x0F12, 0x8BC1,
+0x0F12, 0x2900,
+0x0F12, 0xD02A,
+0x0F12, 0x00A2,
+0x0F12, 0x1815,
+0x0F12, 0x4A95,
+0x0F12, 0x6DEE,
+0x0F12, 0x8A92,
+0x0F12, 0x4296,
+0x0F12, 0xD923,
+0x0F12, 0x0028,
+0x0F12, 0x3080,
+0x0F12, 0x0007,
+0x0F12, 0x69C0,
+0x0F12, 0xF000,
+0x0F12, 0xF945,
+0x0F12, 0x1C71,
+0x0F12, 0x0280,
+0x0F12, 0xF000,
+0x0F12, 0xF941,
+0x0F12, 0x0006,
+0x0F12, 0x4889,
+0x0F12, 0x0061,
+0x0F12, 0x1808,
+0x0F12, 0x8D80,
+0x0F12, 0x0A01,
+0x0F12, 0x0600,
+0x0F12, 0x0E00,
+0x0F12, 0x1A08,
+0x0F12, 0xF000,
+0x0F12, 0xF94C,
+0x0F12, 0x0002,
+0x0F12, 0x6DE9,
+0x0F12, 0x6FE8,
+0x0F12, 0x1A08,
+0x0F12, 0x4351,
+0x0F12, 0x0300,
+0x0F12, 0x1C49,
+0x0F12, 0xF000,
+0x0F12, 0xF92D,
+0x0F12, 0x0401,
+0x0F12, 0x0430,
+0x0F12, 0x0C00,
+0x0F12, 0x4301,
+0x0F12, 0x61F9,
+0x0F12, 0xE004,
+0x0F12, 0x00A2,
+0x0F12, 0x4981,
+0x0F12, 0x1810,
+0x0F12, 0x3080,
+0x0F12, 0x61C1,
+0x0F12, 0x1E64,
+0x0F12, 0xD2C5,
+0x0F12, 0x2006,
+0x0F12, 0xF000,
+0x0F12, 0xF93B,
+0x0F12, 0x2007,
+0x0F12, 0xF000,
+0x0F12, 0xF938,
+0x0F12, 0xBCF8,
+0x0F12, 0xBC08,
+0x0F12, 0x4718,
+0x0F12, 0xB510,
+0x0F12, 0xF000,
+0x0F12, 0xF93A,
+0x0F12, 0x2800,
+0x0F12, 0xD00A,
+0x0F12, 0x4872,
+0x0F12, 0x8B81,
+0x0F12, 0x0089,
+0x0F12, 0x1808,
+0x0F12, 0x6DC1,
+0x0F12, 0x4874,
+0x0F12, 0x8A80,
+0x0F12, 0x4281,
+0x0F12, 0xD901,
+0x0F12, 0x2001,
+0x0F12, 0xE78C,
+0x0F12, 0x2000,
+0x0F12, 0xE78A,
+0x0F12, 0xB5F8,
+0x0F12, 0x0004,
+0x0F12, 0x4F71,
+0x0F12, 0x227D,
+0x0F12, 0x8938,
+0x0F12, 0x0152,
+0x0F12, 0x4342,
+0x0F12, 0x486F,
+0x0F12, 0x9000,
+0x0F12, 0x8A01,
+0x0F12, 0x0848,
+0x0F12, 0x1810,
+0x0F12, 0xF000,
+0x0F12, 0xF8F7,
+0x0F12, 0x210F,
+0x0F12, 0xF000,
+0x0F12, 0xF922,
+0x0F12, 0x496B,
+0x0F12, 0x8C49,
+0x0F12, 0x090E,
+0x0F12, 0x0136,
+0x0F12, 0x4306,
+0x0F12, 0x496A,
+0x0F12, 0x2C00,
+0x0F12, 0xD003,
+0x0F12, 0x2001,
+0x0F12, 0x0240,
+0x0F12, 0x4330,
+0x0F12, 0x8108,
+0x0F12, 0x4859,
+0x0F12, 0x2C00,
+0x0F12, 0x8D00,
+0x0F12, 0xD001,
+0x0F12, 0x2501,
+0x0F12, 0xE000,
+0x0F12, 0x2500,
+0x0F12, 0x4963,
+0x0F12, 0x4328,
+0x0F12, 0x8008,
+0x0F12, 0x207D,
+0x0F12, 0x00C0,
+0x0F12, 0xF000,
+0x0F12, 0xF910,
+0x0F12, 0x2C00,
+0x0F12, 0x495F,
+0x0F12, 0x0328,
+0x0F12, 0x4330,
+0x0F12, 0x8108,
+0x0F12, 0x88F8,
+0x0F12, 0x2C00,
+0x0F12, 0x01AA,
+0x0F12, 0x4310,
+0x0F12, 0x8088,
+0x0F12, 0x9800,
+0x0F12, 0x8A01,
+0x0F12, 0x485A,
+0x0F12, 0xF000,
+0x0F12, 0xF8CB,
+0x0F12, 0x495A,
+0x0F12, 0x8809,
+0x0F12, 0x4348,
+0x0F12, 0x0400,
+0x0F12, 0x0C00,
+0x0F12, 0xF000,
+0x0F12, 0xF8FA,
+0x0F12, 0x0020,
+0x0F12, 0xF000,
+0x0F12, 0xF8FF,
+0x0F12, 0x4856,
+0x0F12, 0x7004,
+0x0F12, 0xE7A3,
+0x0F12, 0xB510,
+0x0F12, 0x0004,
+0x0F12, 0xF000,
+0x0F12, 0xF900,
+0x0F12, 0x6020,
+0x0F12, 0x4953,
+0x0F12, 0x8B49,
+0x0F12, 0x0789,
+0x0F12, 0xD001,
+0x0F12, 0x0040,
+0x0F12, 0x6020,
+0x0F12, 0xE737,
+0x0F12, 0xB510,
+0x0F12, 0xF000,
+0x0F12, 0xF8FD,
+0x0F12, 0x484F,
+0x0F12, 0x8880,
+0x0F12, 0x0601,
+0x0F12, 0x4845,
+0x0F12, 0x1609,
+0x0F12, 0x8141,
+0x0F12, 0xE72D,
+0x0F12, 0xB5F8,
+0x0F12, 0x000F,
+0x0F12, 0x4C38,
+0x0F12, 0x3420,
+0x0F12, 0x2500,
+0x0F12, 0x5765,
+0x0F12, 0x0039,
+0x0F12, 0xF000,
+0x0F12, 0xF8F5,
+0x0F12, 0x9000,
+0x0F12, 0x2600,
+0x0F12, 0x57A6,
+0x0F12, 0x4C3D,
+0x0F12, 0x42AE,
+0x0F12, 0xD01B,
+0x0F12, 0x4D44,
+0x0F12, 0x8AE8,
+0x0F12, 0x2800,
+0x0F12, 0xD013,
+0x0F12, 0x4830,
+0x0F12, 0x8A01,
+0x0F12, 0x8B80,
+0x0F12, 0x4378,
+0x0F12, 0xF000,
+0x0F12, 0xF88F,
+0x0F12, 0x89A9,
+0x0F12, 0x1A41,
+0x0F12, 0x483E,
+0x0F12, 0x3820,
+0x0F12, 0x8AC0,
+0x0F12, 0x4348,
+0x0F12, 0x17C1,
+0x0F12, 0x0D89,
+0x0F12, 0x1808,
+0x0F12, 0x1280,
+0x0F12, 0x8961,
+0x0F12, 0x1A08,
+0x0F12, 0x8160,
+0x0F12, 0xE003,
+0x0F12, 0x88A8,
+0x0F12, 0x0600,
+0x0F12, 0x1600,
+0x0F12, 0x8160,
+0x0F12, 0x200A,
+0x0F12, 0x5E20,
+0x0F12, 0x42B0,
+0x0F12, 0xD011,
+0x0F12, 0xF000,
+0x0F12, 0xF88D,
+0x0F12, 0x1D40,
+0x0F12, 0x00C3,
+0x0F12, 0x1A18,
+0x0F12, 0x214B,
+0x0F12, 0xF000,
+0x0F12, 0xF871,
+0x0F12, 0x211F,
+0x0F12, 0xF000,
+0x0F12, 0xF89C,
+0x0F12, 0x210A,
+0x0F12, 0x5E61,
+0x0F12, 0x0FC9,
+0x0F12, 0x0149,
+0x0F12, 0x4301,
+0x0F12, 0x481C,
+0x0F12, 0x81C1,
+0x0F12, 0x9800,
+0x0F12, 0xE74A,
+0x0F12, 0xB5F1,
+0x0F12, 0xB082,
+0x0F12, 0x2500,
+0x0F12, 0x4829,
+0x0F12, 0x9001,
+0x0F12, 0x2400,
+0x0F12, 0x2028,
+0x0F12, 0x4368,
+0x0F12, 0x4A28,
+0x0F12, 0x4916,
+0x0F12, 0x1887,
+0x0F12, 0x1840,
+0x0F12, 0x9000,
+0x0F12, 0x9800,
+0x0F12, 0x0066,
+0x0F12, 0x9A01,
+0x0F12, 0x1980,
+0x0F12, 0x218C,
+0x0F12, 0x5A09,
+0x0F12, 0x8A80,
+0x0F12, 0x8812,
+0x0F12, 0xF000,
+0x0F12, 0xF8AC,
+0x0F12, 0x53B8,
+0x0F12, 0x1C64,
+0x0F12, 0x2C14,
+0x0F12, 0xDBF1,
+0x0F12, 0x1C6D,
+0x0F12, 0x2D03,
+0x0F12, 0xDBE6,
+0x0F12, 0x9802,
+0x0F12, 0x6800,
+0x0F12, 0x0600,
+0x0F12, 0x0E00,
+0x0F12, 0xF000,
+0x0F12, 0xF8A7,
+0x0F12, 0xBCFE,
+0x0F12, 0xBC08,
+0x0F12, 0x4718,
+0x0F12, 0xB510,
+0x0F12, 0x4B19,
+0x0F12, 0x2200,
+0x0F12, 0x801A,
+0x0F12, 0xF000,
+0x0F12, 0xF8A6,
+0x0F12, 0xE6BC,
+0x0F12, 0x0000,
+0x0F12, 0x0C3C,
+0x0F12, 0x7000,
+0x0F12, 0x2C2C,
+0x0F12, 0x7000,
+0x0F12, 0xC824,
+0x0F12, 0x0031,
+0x0F12, 0xF2A0,
+0x0F12, 0xD000,
+0x0F12, 0x3274,
+0x0F12, 0x7000,
+0x0F12, 0x26E8,
+0x0F12, 0x7000,
+0x0F12, 0x6100,
+0x0F12, 0xD000,
+0x0F12, 0x6500,
+0x0F12, 0xD000,
+0x0F12, 0x1A7C,
+0x0F12, 0x7000,
+0x0F12, 0x1120,
+0x0F12, 0x7000,
+0x0F12, 0xFFFF,
+0x0F12, 0x0000,
+0x0F12, 0x3374,
+0x0F12, 0x7000,
+0x0F12, 0x1D6C,
+0x0F12, 0x7000,
+0x0F12, 0x167C,
+0x0F12, 0x7000,
+0x0F12, 0xF400,
+0x0F12, 0xD000,
+0x0F12, 0x40A0,
+0x0F12, 0x00DD,
+0x0F12, 0xF520,
+0x0F12, 0xD000,
+0x0F12, 0x2C29,
+0x0F12, 0x7000,
+0x0F12, 0x1A54,
+0x0F12, 0x7000,
+0x0F12, 0x1564,
+0x0F12, 0x7000,
+0x0F12, 0x2440,
+0x0F12, 0x7000,
+0x0F12, 0x05A0,
+0x0F12, 0x7000,
+0x0F12, 0xB0A0,
+0x0F12, 0xD000,
+0x0F12, 0x4778,
+0x0F12, 0x46C0,
+0x0F12, 0xC000,
+0x0F12, 0xE59F,
+0x0F12, 0xFF1C,
+0x0F12, 0xE12F,
+0x0F12, 0x1A3F,
+0x0F12, 0x0001,
+0x0F12, 0x4778,
+0x0F12, 0x46C0,
+0x0F12, 0xF004,
+0x0F12, 0xE51F,
+0x0F12, 0x1F48,
+0x0F12, 0x0001,
+0x0F12, 0x4778,
+0x0F12, 0x46C0,
+0x0F12, 0xC000,
+0x0F12, 0xE59F,
+0x0F12, 0xFF1C,
+0x0F12, 0xE12F,
+0x0F12, 0xF7C1,
+0x0F12, 0x0000,
+0x0F12, 0x4778,
+0x0F12, 0x46C0,
+0x0F12, 0xC000,
+0x0F12, 0xE59F,
+0x0F12, 0xFF1C,
+0x0F12, 0xE12F,
+0x0F12, 0x24BD,
+0x0F12, 0x0000,
+0x0F12, 0x4778,
+0x0F12, 0x46C0,
+0x0F12, 0xC000,
+0x0F12, 0xE59F,
+0x0F12, 0xFF1C,
+0x0F12, 0xE12F,
+0x0F12, 0x36DD,
+0x0F12, 0x0000,
+0x0F12, 0x4778,
+0x0F12, 0x46C0,
+0x0F12, 0xC000,
+0x0F12, 0xE59F,
+0x0F12, 0xFF1C,
+0x0F12, 0xE12F,
+0x0F12, 0xB4CF,
+0x0F12, 0x0000,
+0x0F12, 0x4778,
+0x0F12, 0x46C0,
+0x0F12, 0xC000,
+0x0F12, 0xE59F,
+0x0F12, 0xFF1C,
+0x0F12, 0xE12F,
+0x0F12, 0xB5D7,
+0x0F12, 0x0000,
+0x0F12, 0x4778,
+0x0F12, 0x46C0,
+0x0F12, 0xC000,
+0x0F12, 0xE59F,
+0x0F12, 0xFF1C,
+0x0F12, 0xE12F,
+0x0F12, 0x36ED,
+0x0F12, 0x0000,
+0x0F12, 0x4778,
+0x0F12, 0x46C0,
+0x0F12, 0xC000,
+0x0F12, 0xE59F,
+0x0F12, 0xFF1C,
+0x0F12, 0xE12F,
+0x0F12, 0xF53F,
+0x0F12, 0x0000,
+0x0F12, 0x4778,
+0x0F12, 0x46C0,
+0x0F12, 0xC000,
+0x0F12, 0xE59F,
+0x0F12, 0xFF1C,
+0x0F12, 0xE12F,
+0x0F12, 0xF5D9,
+0x0F12, 0x0000,
+0x0F12, 0x4778,
+0x0F12, 0x46C0,
+0x0F12, 0xC000,
+0x0F12, 0xE59F,
+0x0F12, 0xFF1C,
+0x0F12, 0xE12F,
+0x0F12, 0x013D,
+0x0F12, 0x0001,
+0x0F12, 0x4778,
+0x0F12, 0x46C0,
+0x0F12, 0xC000,
+0x0F12, 0xE59F,
+0x0F12, 0xFF1C,
+0x0F12, 0xE12F,
+0x0F12, 0xF5C9,
+0x0F12, 0x0000,
+0x0F12, 0x4778,
+0x0F12, 0x46C0,
+0x0F12, 0xC000,
+0x0F12, 0xE59F,
+0x0F12, 0xFF1C,
+0x0F12, 0xE12F,
+0x0F12, 0xFAA9,
+0x0F12, 0x0000,
+0x0F12, 0x4778,
+0x0F12, 0x46C0,
+0x0F12, 0xC000,
+0x0F12, 0xE59F,
+0x0F12, 0xFF1C,
+0x0F12, 0xE12F,
+0x0F12, 0x3723,
+0x0F12, 0x0000,
+0x0F12, 0x4778,
+0x0F12, 0x46C0,
+0x0F12, 0xC000,
+0x0F12, 0xE59F,
+0x0F12, 0xFF1C,
+0x0F12, 0xE12F,
+0x0F12, 0x5823,
+0x0F12, 0x0000,
+0x0F12, 0x4778,
+0x0F12, 0x46C0,
+0x0F12, 0xC000,
+0x0F12, 0xE59F,
+0x0F12, 0xFF1C,
+0x0F12, 0xE12F,
+0x0F12, 0x1857,
+0x0F12, 0x0001,
+0x0F12, 0x9F9E,
+0x0F12, 0x0000,
+0x002A, 0x157A,
+0x0F12, 0x0001,
+0x002A, 0x1578,
+0x0F12, 0x0001,
+0x002A, 0x1576,
+0x0F12, 0x0020,
+0x002A, 0x1574,
+0x0F12, 0x0006,
+0x002A, 0x156E,
+0x0F12, 0x0001,
+0x002A, 0x1568,
+0x0F12, 0x00FC,
+0x002A, 0x155A,
+0x0F12, 0x01CC,
+0x002A, 0x157E,
+0x0F12, 0x0C80,
+0x0F12, 0x0578,
+0x002A, 0x157C,
+0x0F12, 0x0190,
+0x002A, 0x1570,
+0x0F12, 0x00A0,
+0x0F12, 0x0010,
+0x002A, 0x12C4,
+0x0F12, 0x006A,
+0x002A, 0x12C8,
+0x0F12, 0x08AC,
+0x0F12, 0x0050,
+0x002A, 0x1696,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x00C6,
+0x0F12, 0x00C6,
+0x002A, 0x1690,
+0x0F12, 0x0001,
+0x002A, 0x12B0,
+0x0F12, 0x0055,
+0x0F12, 0x005A,
+0x002A, 0x337A,
+0x0F12, 0x0006,
+0x0F12, 0x0068,
+0x002A, 0x169E,
+0x0F12, 0x0007,
+0x002A, 0x0BF6,
+0x0F12, 0x0000,
+0x002A, 0x327C,
+0x0F12, 0x1000,
+0x0F12, 0x6998,
+0x0F12, 0x0078,
+0x0F12, 0x04FE,
+0x0F12, 0x8800,
+0x002A, 0x3274,
+0x0F12, 0x0155,
+0x0F12, 0x0155,
+0x0F12, 0x1555,
+0x0F12, 0x0555,
+0x0028, 0x7000,
+0x002A, 0x0572,
+0x0F12, 0x0007,
+0x0028, 0x7000,
+0x002A, 0x12D2,
+0x0F12, 0x0003,
+0x0F12, 0x0003,
+0x0F12, 0x0003,
+0x0F12, 0x0003,
+0x0F12, 0x0884,
+0x0F12, 0x08CF,
+0x0F12, 0x0500,
+0x0F12, 0x054B,
+0x0F12, 0x0001,
+0x0F12, 0x0001,
+0x0F12, 0x0001,
+0x0F12, 0x0001,
+0x0F12, 0x0885,
+0x0F12, 0x0467,
+0x0F12, 0x0501,
+0x0F12, 0x02A5,
+0x0F12, 0x0001,
+0x0F12, 0x046A,
+0x0F12, 0x0001,
+0x0F12, 0x02A8,
+0x0F12, 0x0885,
+0x0F12, 0x08D0,
+0x0F12, 0x0501,
+0x0F12, 0x054C,
+0x0F12, 0x0006,
+0x0F12, 0x0020,
+0x0F12, 0x0006,
+0x0F12, 0x0020,
+0x0F12, 0x0881,
+0x0F12, 0x0463,
+0x0F12, 0x04FD,
+0x0F12, 0x02A1,
+0x0F12, 0x0006,
+0x0F12, 0x0489,
+0x0F12, 0x0006,
+0x0F12, 0x02C7,
+0x0F12, 0x0881,
+0x0F12, 0x08CC,
+0x0F12, 0x04FD,
+0x0F12, 0x0548,
+0x0F12, 0x03A2,
+0x0F12, 0x01D3,
+0x0F12, 0x01E0,
+0x0F12, 0x00F2,
+0x0F12, 0x03F2,
+0x0F12, 0x0223,
+0x0F12, 0x0230,
+0x0F12, 0x0142,
+0x0F12, 0x03A2,
+0x0F12, 0x063C,
+0x0F12, 0x01E0,
+0x0F12, 0x0399,
+0x0F12, 0x03F2,
+0x0F12, 0x068C,
+0x0F12, 0x0230,
+0x0F12, 0x03E9,
+0x0F12, 0x0002,
+0x0F12, 0x0002,
+0x0F12, 0x0002,
+0x0F12, 0x0002,
+0x0F12, 0x003C,
+0x0F12, 0x003C,
+0x0F12, 0x003C,
+0x0F12, 0x003C,
+0x0F12, 0x01D3,
+0x0F12, 0x01D3,
+0x0F12, 0x00F2,
+0x0F12, 0x00F2,
+0x0F12, 0x020B,
+0x0F12, 0x024A,
+0x0F12, 0x012A,
+0x0F12, 0x0169,
+0x0F12, 0x0002,
+0x0F12, 0x046B,
+0x0F12, 0x0002,
+0x0F12, 0x02A9,
+0x0F12, 0x0419,
+0x0F12, 0x04A5,
+0x0F12, 0x0257,
+0x0F12, 0x02E3,
+0x0F12, 0x0630,
+0x0F12, 0x063C,
+0x0F12, 0x038D,
+0x0F12, 0x0399,
+0x0F12, 0x0668,
+0x0F12, 0x06B3,
+0x0F12, 0x03C5,
+0x0F12, 0x0410,
+0x0F12, 0x0001,
+0x0F12, 0x0001,
+0x0F12, 0x0001,
+0x0F12, 0x0001,
+0x0F12, 0x03A2,
+0x0F12, 0x01D3,
+0x0F12, 0x01E0,
+0x0F12, 0x00F2,
+0x0F12, 0x0000,
+0x0F12, 0x0461,
+0x0F12, 0x0000,
+0x0F12, 0x029F,
+0x0F12, 0x0000,
+0x0F12, 0x063C,
+0x0F12, 0x0000,
+0x0F12, 0x0399,
+0x0F12, 0x003D,
+0x0F12, 0x003D,
+0x0F12, 0x003D,
+0x0F12, 0x003D,
+0x0F12, 0x01D0,
+0x0F12, 0x01D0,
+0x0F12, 0x00EF,
+0x0F12, 0x00EF,
+0x0F12, 0x020C,
+0x0F12, 0x024B,
+0x0F12, 0x012B,
+0x0F12, 0x016A,
+0x0F12, 0x039F,
+0x0F12, 0x045E,
+0x0F12, 0x01DD,
+0x0F12, 0x029C,
+0x0F12, 0x041A,
+0x0F12, 0x04A6,
+0x0F12, 0x0258,
+0x0F12, 0x02E4,
+0x0F12, 0x062D,
+0x0F12, 0x0639,
+0x0F12, 0x038A,
+0x0F12, 0x0396,
+0x0F12, 0x0669,
+0x0F12, 0x06B4,
+0x0F12, 0x03C6,
+0x0F12, 0x0411,
+0x0F12, 0x087C,
+0x0F12, 0x08C7,
+0x0F12, 0x04F8,
+0x0F12, 0x0543,
+0x0F12, 0x0040,
+0x0F12, 0x0040,
+0x0F12, 0x0040,
+0x0F12, 0x0040,
+0x0F12, 0x01D0,
+0x0F12, 0x01D0,
+0x0F12, 0x00EF,
+0x0F12, 0x00EF,
+0x0F12, 0x020F,
+0x0F12, 0x024E,
+0x0F12, 0x012E,
+0x0F12, 0x016D,
+0x0F12, 0x039F,
+0x0F12, 0x045E,
+0x0F12, 0x01DD,
+0x0F12, 0x029C,
+0x0F12, 0x041D,
+0x0F12, 0x04A9,
+0x0F12, 0x025B,
+0x0F12, 0x02E7,
+0x0F12, 0x062D,
+0x0F12, 0x0639,
+0x0F12, 0x038A,
+0x0F12, 0x0396,
+0x0F12, 0x066C,
+0x0F12, 0x06B7,
+0x0F12, 0x03C9,
+0x0F12, 0x0414,
+0x0F12, 0x087C,
+0x0F12, 0x08C7,
+0x0F12, 0x04F8,
+0x0F12, 0x0543,
+0x0F12, 0x0040,
+0x0F12, 0x0040,
+0x0F12, 0x0040,
+0x0F12, 0x0040,
+0x0F12, 0x01D0,
+0x0F12, 0x01D0,
+0x0F12, 0x00EF,
+0x0F12, 0x00EF,
+0x0F12, 0x020F,
+0x0F12, 0x024E,
+0x0F12, 0x012E,
+0x0F12, 0x016D,
+0x0F12, 0x039F,
+0x0F12, 0x045E,
+0x0F12, 0x01DD,
+0x0F12, 0x029C,
+0x0F12, 0x041D,
+0x0F12, 0x04A9,
+0x0F12, 0x025B,
+0x0F12, 0x02E7,
+0x0F12, 0x062D,
+0x0F12, 0x0639,
+0x0F12, 0x038A,
+0x0F12, 0x0396,
+0x0F12, 0x066C,
+0x0F12, 0x06B7,
+0x0F12, 0x03C9,
+0x0F12, 0x0414,
+0x0F12, 0x087C,
+0x0F12, 0x08C7,
+0x0F12, 0x04F8,
+0x0F12, 0x0543,
+0x0F12, 0x003D,
+0x0F12, 0x003D,
+0x0F12, 0x003D,
+0x0F12, 0x003D,
+0x0F12, 0x01D2,
+0x0F12, 0x01D2,
+0x0F12, 0x00F1,
+0x0F12, 0x00F1,
+0x0F12, 0x020C,
+0x0F12, 0x024B,
+0x0F12, 0x012B,
+0x0F12, 0x016A,
+0x0F12, 0x03A1,
+0x0F12, 0x0460,
+0x0F12, 0x01DF,
+0x0F12, 0x029E,
+0x0F12, 0x041A,
+0x0F12, 0x04A6,
+0x0F12, 0x0258,
+0x0F12, 0x02E4,
+0x0F12, 0x062F,
+0x0F12, 0x063B,
+0x0F12, 0x038C,
+0x0F12, 0x0398,
+0x0F12, 0x0669,
+0x0F12, 0x06B4,
+0x0F12, 0x03C6,
+0x0F12, 0x0411,
+0x0F12, 0x087E,
+0x0F12, 0x08C9,
+0x0F12, 0x04FA,
+0x0F12, 0x0545,
+0x0F12, 0x03A2,
+0x0F12, 0x01D3,
+0x0F12, 0x01E0,
+0x0F12, 0x00F2,
+0x0F12, 0x03AF,
+0x0F12, 0x01E0,
+0x0F12, 0x01ED,
+0x0F12, 0x00FF,
+0x0F12, 0x0000,
+0x0F12, 0x0461,
+0x0F12, 0x0000,
+0x0F12, 0x029F,
+0x0F12, 0x0000,
+0x0F12, 0x046E,
+0x0F12, 0x0000,
+0x0F12, 0x02AC,
+0x0F12, 0x0000,
+0x0F12, 0x063C,
+0x0F12, 0x0000,
+0x0F12, 0x0399,
+0x0F12, 0x0000,
+0x0F12, 0x0649,
+0x0F12, 0x0000,
+0x0F12, 0x03A6,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x03AA,
+0x0F12, 0x01DB,
+0x0F12, 0x01E8,
+0x0F12, 0x00FA,
+0x0F12, 0x03B7,
+0x0F12, 0x01E8,
+0x0F12, 0x01F5,
+0x0F12, 0x0107,
+0x0F12, 0x0000,
+0x0F12, 0x0469,
+0x0F12, 0x0000,
+0x0F12, 0x02A7,
+0x0F12, 0x0000,
+0x0F12, 0x0476,
+0x0F12, 0x0000,
+0x0F12, 0x02B4,
+0x0F12, 0x0000,
+0x0F12, 0x0644,
+0x0F12, 0x0000,
+0x0F12, 0x03A1,
+0x0F12, 0x0000,
+0x0F12, 0x0651,
+0x0F12, 0x0000,
+0x0F12, 0x03AE,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0001,
+0x0F12, 0x0001,
+0x0F12, 0x0001,
+0x0F12, 0x0001,
+0x0F12, 0x000F,
+0x0F12, 0x000F,
+0x0F12, 0x000F,
+0x0F12, 0x000F,
+0x0F12, 0x05AD,
+0x0F12, 0x03DE,
+0x0F12, 0x030A,
+0x0F12, 0x021C,
+0x0F12, 0x062F,
+0x0F12, 0x0460,
+0x0F12, 0x038C,
+0x0F12, 0x029E,
+0x0F12, 0x07FC,
+0x0F12, 0x0847,
+0x0F12, 0x0478,
+0x0F12, 0x04C3,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x002A, 0x01D4,
+0x0F12, 0x0000,
+0x002A, 0x01DE,
+0x0F12, 0x0003,
+0x0F12, 0x0000,
+0x002A, 0x01E4,
+0x0F12, 0x0041,
+0x002A, 0x01E8,
+0x0F12, 0x2A0C,
+0x0F12, 0x0190,
+0x002A, 0x025A,
+0x0F12, 0x0100,
+0x0F12, 0x0110,
+0x0F12, 0x0200,
+0x0F12, 0x0238,
+0x0F12, 0x018C,
+0x0F12, 0x01C0,
+0x0F12, 0x00E6,
+0x0F12, 0x0132,
+0x0F12, 0x0001,
+0x002A, 0x0586,
+0x0F12, 0x00FF,
+0x002A, 0x115E,
+0x0F12, 0x0003,
+0x002A, 0x10D4,
+0x0F12, 0x1000,
+0x002A, 0x10DE,
+0x0F12, 0x0004,
+0x002A, 0x106C,
+0x0F12, 0x0202,
+0x002A, 0x10CA,
+0x0F12, 0x00C0,
+0x002A, 0x1060,
+0x0F12, 0x0000,
+0x0F12, 0xB300,
+0x002A, 0x10F4,
+0x0F12, 0x0280,
+0x002A, 0x1100,
+0x0F12, 0x03A0,
+0x0F12, 0x0320,
+0x002A, 0x1134,
+0x0F12, 0x0030,
+0x002A, 0x1154,
+0x0F12, 0x0060,
+0x002A, 0x10E2,
+0x0F12, 0x0000,
+0x002A, 0x1072,
+0x0F12, 0x003C,
+0x002A, 0x1074,
+0x0F12, 0x0013,
+0x0F12, 0x002C,
+0x0F12, 0x0030,
+0x0F12, 0x0033,
+0x0F12, 0x0036,
+0x0F12, 0x0039,
+0x0F12, 0x003D,
+0x0F12, 0x0041,
+0x0F12, 0x0045,
+0x0F12, 0x0049,
+0x0F12, 0x004F,
+0x0F12, 0x0055,
+0x0F12, 0x005D,
+0x0F12, 0x0065,
+0x0F12, 0x006D,
+0x0F12, 0x0077,
+0x0F12, 0x0083,
+0x0F12, 0x008F,
+0x0F12, 0x009B,
+0x0F12, 0x00A7,
+0x0F12, 0x00B3,
+0x002A, 0x0252,
+0x0F12, 0x0003,
+0x002A, 0x12B8,
+0x0F12, 0x1000,
+0x002A, 0x158A,
+0x0F12, 0xEAF0,
+0x002A, 0x15C6,
+0x0F12, 0x0020,
+0x0F12, 0x0060,
+0x002A, 0x15BC,
+0x0F12, 0x0200,
+0x002A, 0x1608,
+0x0F12, 0x0100,
+0x0F12, 0x0100,
+0x0F12, 0x0100,
+0x0F12, 0x0100,
+0x002A, 0x0F70,
+0x0F12, 0x0040,
+0x002A, 0x0530,
+0x0F12, 0x3415,
+0x002A, 0x0534,
+0x0F12, 0x682A,
+0x002A, 0x167C,
+0x0F12, 0x8235,
+0x002A, 0x1680,
+0x0F12, 0xc350,
+0x002A, 0x0538,
+0x0F12, 0x3415,
+0x002A, 0x053C,
+0x0F12, 0x682A,
+0x002A, 0x1684,
+0x0F12, 0x8235,
+0x002A, 0x1688,
+0x0F12, 0xC350,
+0x002A, 0x0540,
+0x0F12, 0x01B3,
+0x0F12, 0x01B3,
+0x002A, 0x168C,
+0x0F12, 0x02A0,
+0x0F12, 0x0710,
+0x002A, 0x0544,
+0x0F12, 0x0100,
+0x0F12, 0x8000,
+0x002A, 0x1694,
+0x0F12, 0x0001,
+0x002A, 0x021A,
+0x0F12, 0x0000,
+0x002A, 0x04d2,
+0x0F12, 0x065f,
+0x002A, 0x04ba,
+0x0F12, 0x0001,
+0x0F12, 0x0001,
+0x002A, 0x06CE,
+0x0F12, 0x0100,
+0x0F12, 0x0100,
+0x0F12, 0x0100,
+0x0F12, 0x0100,
+0x0F12, 0x0100,
+0x0F12, 0x0100,
+0x0F12, 0x0100,
+0x0F12, 0x0100,
+0x0F12, 0x0100,
+0x0F12, 0x0100,
+0x0F12, 0x0100,
+0x0F12, 0x0100,
+0x0F12, 0x00e0,
+0x0F12, 0x00f8,
+0x0F12, 0x00f8,
+0x0F12, 0x0100,
+0x0F12, 0x00e8,
+0x0F12, 0x00f8,
+0x0F12, 0x00f8,
+0x0F12, 0x0100,
+0x0F12, 0x00f0,
+0x0F12, 0x0100,
+0x0F12, 0x0100,
+0x0F12, 0x0100,
+0x0F12, 0x00f0,
+0x0F12, 0x0100,
+0x0F12, 0x0100,
+0x0F12, 0x0100,
+0x0F12, 0x00f8,
+0x0F12, 0x0100,
+0x0F12, 0x0100,
+0x0F12, 0x0100,
+0x0F12, 0x0036,
+0x0F12, 0x001F,
+0x0F12, 0x0020,
+0x0F12, 0x0000,
+0x0F12, 0x0036,
+0x0F12, 0x001F,
+0x0F12, 0x0020,
+0x0F12, 0x0000,
+0x0F12, 0x0036,
+0x0F12, 0x001F,
+0x0F12, 0x0020,
+0x0F12, 0x0000,
+0x0F12, 0x0010,
+0x0F12, 0x001F,
+0x0F12, 0x0020,
+0x0F12, 0x0000,
+0x0F12, 0x0020,
+0x0F12, 0x001F,
+0x0F12, 0x0020,
+0x0F12, 0x0000,
+0x0F12, 0x0036,
+0x0F12, 0x001F,
+0x0F12, 0x0020,
+0x0F12, 0x0000,
+0x0F12, 0x0036,
+0x0F12, 0x001F,
+0x0F12, 0x0020,
+0x0F12, 0x0000,
+0x0F12, 0x0036,
+0x0F12, 0x001F,
+0x0F12, 0x0020,
+0x0F12, 0x0000,
+0x002A, 0x075A,
+0x0F12, 0x0000,
+0x0F12, 0x0400,
+0x0F12, 0x0300,
+0x0F12, 0x0010,
+0x0F12, 0x0011,
+0x002A, 0x347C,
+0x0F12, 0x01D9,
+0x0F12, 0x019D,
+0x0F12, 0x015C,
+0x0F12, 0x0125,
+0x0F12, 0x00FE,
+0x0F12, 0x00E5,
+0x0F12, 0x00DA,
+0x0F12, 0x00E5,
+0x0F12, 0x0100,
+0x0F12, 0x012D,
+0x0F12, 0x016B,
+0x0F12, 0x01B3,
+0x0F12, 0x01F2,
+0x0F12, 0x01A7,
+0x0F12, 0x0165,
+0x0F12, 0x011E,
+0x0F12, 0x00E3,
+0x0F12, 0x00B6,
+0x0F12, 0x009C,
+0x0F12, 0x0092,
+0x0F12, 0x009D,
+0x0F12, 0x00BB,
+0x0F12, 0x00F0,
+0x0F12, 0x0133,
+0x0F12, 0x0182,
+0x0F12, 0x01CD,
+0x0F12, 0x0170,
+0x0F12, 0x012A,
+0x0F12, 0x00DC,
+0x0F12, 0x009A,
+0x0F12, 0x006E,
+0x0F12, 0x0053,
+0x0F12, 0x004A,
+0x0F12, 0x0055,
+0x0F12, 0x0076,
+0x0F12, 0x00AC,
+0x0F12, 0x00F5,
+0x0F12, 0x0147,
+0x0F12, 0x0196,
+0x0F12, 0x014C,
+0x0F12, 0x0102,
+0x0F12, 0x00B1,
+0x0F12, 0x006F,
+0x0F12, 0x0041,
+0x0F12, 0x0027,
+0x0F12, 0x001F,
+0x0F12, 0x002A,
+0x0F12, 0x004B,
+0x0F12, 0x0083,
+0x0F12, 0x00CE,
+0x0F12, 0x0128,
+0x0F12, 0x0177,
+0x0F12, 0x0133,
+0x0F12, 0x00E6,
+0x0F12, 0x0094,
+0x0F12, 0x0052,
+0x0F12, 0x0025,
+0x0F12, 0x000C,
+0x0F12, 0x0004,
+0x0F12, 0x0010,
+0x0F12, 0x0030,
+0x0F12, 0x0069,
+0x0F12, 0x00B6,
+0x0F12, 0x0112,
+0x0F12, 0x0168,
+0x0F12, 0x012F,
+0x0F12, 0x00E3,
+0x0F12, 0x008E,
+0x0F12, 0x004C,
+0x0F12, 0x0020,
+0x0F12, 0x0007,
+0x0F12, 0x0000,
+0x0F12, 0x000B,
+0x0F12, 0x002D,
+0x0F12, 0x0065,
+0x0F12, 0x00B4,
+0x0F12, 0x0114,
+0x0F12, 0x016B,
+0x0F12, 0x0138,
+0x0F12, 0x00EB,
+0x0F12, 0x0099,
+0x0F12, 0x0058,
+0x0F12, 0x002B,
+0x0F12, 0x0012,
+0x0F12, 0x000B,
+0x0F12, 0x0017,
+0x0F12, 0x0039,
+0x0F12, 0x0074,
+0x0F12, 0x00C2,
+0x0F12, 0x0121,
+0x0F12, 0x0177,
+0x0F12, 0x0158,
+0x0F12, 0x010C,
+0x0F12, 0x00BC,
+0x0F12, 0x007B,
+0x0F12, 0x004E,
+0x0F12, 0x0034,
+0x0F12, 0x002D,
+0x0F12, 0x003B,
+0x0F12, 0x005E,
+0x0F12, 0x0099,
+0x0F12, 0x00E7,
+0x0F12, 0x0145,
+0x0F12, 0x0197,
+0x0F12, 0x017E,
+0x0F12, 0x0138,
+0x0F12, 0x00EB,
+0x0F12, 0x00AC,
+0x0F12, 0x0080,
+0x0F12, 0x0067,
+0x0F12, 0x0061,
+0x0F12, 0x006E,
+0x0F12, 0x0091,
+0x0F12, 0x00CA,
+0x0F12, 0x0116,
+0x0F12, 0x016E,
+0x0F12, 0x01C0,
+0x0F12, 0x01BB,
+0x0F12, 0x0177,
+0x0F12, 0x0131,
+0x0F12, 0x00F7,
+0x0F12, 0x00CE,
+0x0F12, 0x00B6,
+0x0F12, 0x00AF,
+0x0F12, 0x00BD,
+0x0F12, 0x00DF,
+0x0F12, 0x0113,
+0x0F12, 0x0156,
+0x0F12, 0x01AA,
+0x0F12, 0x01F7,
+0x0F12, 0x01EF,
+0x0F12, 0x01B2,
+0x0F12, 0x0170,
+0x0F12, 0x013E,
+0x0F12, 0x0119,
+0x0F12, 0x0103,
+0x0F12, 0x00FF,
+0x0F12, 0x010D,
+0x0F12, 0x012B,
+0x0F12, 0x0158,
+0x0F12, 0x0194,
+0x0F12, 0x01DA,
+0x0F12, 0x0207,
+0x0F12, 0x0198,
+0x0F12, 0x0156,
+0x0F12, 0x011D,
+0x0F12, 0x00EF,
+0x0F12, 0x00C9,
+0x0F12, 0x00B2,
+0x0F12, 0x00A9,
+0x0F12, 0x00B1,
+0x0F12, 0x00C5,
+0x0F12, 0x00E7,
+0x0F12, 0x0113,
+0x0F12, 0x0152,
+0x0F12, 0x018C,
+0x0F12, 0x016E,
+0x0F12, 0x0127,
+0x0F12, 0x00E8,
+0x0F12, 0x00B6,
+0x0F12, 0x0090,
+0x0F12, 0x0078,
+0x0F12, 0x0070,
+0x0F12, 0x0078,
+0x0F12, 0x008F,
+0x0F12, 0x00B5,
+0x0F12, 0x00E7,
+0x0F12, 0x0126,
+0x0F12, 0x016C,
+0x0F12, 0x013D,
+0x0F12, 0x00F6,
+0x0F12, 0x00B5,
+0x0F12, 0x0080,
+0x0F12, 0x0058,
+0x0F12, 0x0040,
+0x0F12, 0x0038,
+0x0F12, 0x0042,
+0x0F12, 0x005B,
+0x0F12, 0x0083,
+0x0F12, 0x00B9,
+0x0F12, 0x00F9,
+0x0F12, 0x0141,
+0x0F12, 0x011E,
+0x0F12, 0x00D7,
+0x0F12, 0x0094,
+0x0F12, 0x005E,
+0x0F12, 0x0035,
+0x0F12, 0x001E,
+0x0F12, 0x0017,
+0x0F12, 0x0021,
+0x0F12, 0x003C,
+0x0F12, 0x0066,
+0x0F12, 0x009F,
+0x0F12, 0x00E2,
+0x0F12, 0x012A,
+0x0F12, 0x010A,
+0x0F12, 0x00C0,
+0x0F12, 0x007B,
+0x0F12, 0x0045,
+0x0F12, 0x001F,
+0x0F12, 0x0008,
+0x0F12, 0x0002,
+0x0F12, 0x000C,
+0x0F12, 0x0027,
+0x0F12, 0x0052,
+0x0F12, 0x008C,
+0x0F12, 0x00D2,
+0x0F12, 0x011E,
+0x0F12, 0x0106,
+0x0F12, 0x00BC,
+0x0F12, 0x0077,
+0x0F12, 0x0040,
+0x0F12, 0x001A,
+0x0F12, 0x0005,
+0x0F12, 0x0000,
+0x0F12, 0x000A,
+0x0F12, 0x0026,
+0x0F12, 0x0052,
+0x0F12, 0x008D,
+0x0F12, 0x00D5,
+0x0F12, 0x0121,
+0x0F12, 0x010A,
+0x0F12, 0x00C1,
+0x0F12, 0x007E,
+0x0F12, 0x0049,
+0x0F12, 0x0023,
+0x0F12, 0x000E,
+0x0F12, 0x0009,
+0x0F12, 0x0015,
+0x0F12, 0x0031,
+0x0F12, 0x005D,
+0x0F12, 0x0097,
+0x0F12, 0x00DF,
+0x0F12, 0x0129,
+0x0F12, 0x0121,
+0x0F12, 0x00DA,
+0x0F12, 0x009A,
+0x0F12, 0x0065,
+0x0F12, 0x0040,
+0x0F12, 0x002B,
+0x0F12, 0x0026,
+0x0F12, 0x0031,
+0x0F12, 0x004E,
+0x0F12, 0x007C,
+0x0F12, 0x00B6,
+0x0F12, 0x00FB,
+0x0F12, 0x0143,
+0x0F12, 0x0140,
+0x0F12, 0x00FB,
+0x0F12, 0x00BD,
+0x0F12, 0x008C,
+0x0F12, 0x0068,
+0x0F12, 0x0054,
+0x0F12, 0x004F,
+0x0F12, 0x005B,
+0x0F12, 0x0077,
+0x0F12, 0x00A2,
+0x0F12, 0x00DA,
+0x0F12, 0x011C,
+0x0F12, 0x0163,
+0x0F12, 0x0173,
+0x0F12, 0x012D,
+0x0F12, 0x00F5,
+0x0F12, 0x00C6,
+0x0F12, 0x00A5,
+0x0F12, 0x0092,
+0x0F12, 0x008F,
+0x0F12, 0x009B,
+0x0F12, 0x00B5,
+0x0F12, 0x00DC,
+0x0F12, 0x010C,
+0x0F12, 0x014C,
+0x0F12, 0x0197,
+0x0F12, 0x019F,
+0x0F12, 0x015F,
+0x0F12, 0x0128,
+0x0F12, 0x00FF,
+0x0F12, 0x00E1,
+0x0F12, 0x00D0,
+0x0F12, 0x00CF,
+0x0F12, 0x00DA,
+0x0F12, 0x00F4,
+0x0F12, 0x0116,
+0x0F12, 0x0142,
+0x0F12, 0x017A,
+0x0F12, 0x01A8,
+0x0F12, 0x0191,
+0x0F12, 0x0152,
+0x0F12, 0x0118,
+0x0F12, 0x00EB,
+0x0F12, 0x00C8,
+0x0F12, 0x00B4,
+0x0F12, 0x00AF,
+0x0F12, 0x00BB,
+0x0F12, 0x00D5,
+0x0F12, 0x00FE,
+0x0F12, 0x012E,
+0x0F12, 0x016E,
+0x0F12, 0x01AC,
+0x0F12, 0x0166,
+0x0F12, 0x0121,
+0x0F12, 0x00E3,
+0x0F12, 0x00B3,
+0x0F12, 0x008E,
+0x0F12, 0x0079,
+0x0F12, 0x0074,
+0x0F12, 0x0081,
+0x0F12, 0x009D,
+0x0F12, 0x00C7,
+0x0F12, 0x00FF,
+0x0F12, 0x013F,
+0x0F12, 0x0188,
+0x0F12, 0x0134,
+0x0F12, 0x00F1,
+0x0F12, 0x00B1,
+0x0F12, 0x007C,
+0x0F12, 0x0057,
+0x0F12, 0x0041,
+0x0F12, 0x003C,
+0x0F12, 0x0048,
+0x0F12, 0x0065,
+0x0F12, 0x0091,
+0x0F12, 0x00CA,
+0x0F12, 0x010C,
+0x0F12, 0x0157,
+0x0F12, 0x0118,
+0x0F12, 0x00D2,
+0x0F12, 0x0091,
+0x0F12, 0x005C,
+0x0F12, 0x0035,
+0x0F12, 0x001E,
+0x0F12, 0x0019,
+0x0F12, 0x0025,
+0x0F12, 0x0043,
+0x0F12, 0x0070,
+0x0F12, 0x00A9,
+0x0F12, 0x00EE,
+0x0F12, 0x0136,
+0x0F12, 0x0105,
+0x0F12, 0x00BD,
+0x0F12, 0x007B,
+0x0F12, 0x0046,
+0x0F12, 0x001F,
+0x0F12, 0x0009,
+0x0F12, 0x0003,
+0x0F12, 0x000E,
+0x0F12, 0x002B,
+0x0F12, 0x0057,
+0x0F12, 0x0091,
+0x0F12, 0x00D7,
+0x0F12, 0x0121,
+0x0F12, 0x0105,
+0x0F12, 0x00BC,
+0x0F12, 0x0078,
+0x0F12, 0x0043,
+0x0F12, 0x001D,
+0x0F12, 0x0006,
+0x0F12, 0x0000,
+0x0F12, 0x000B,
+0x0F12, 0x0026,
+0x0F12, 0x0052,
+0x0F12, 0x008C,
+0x0F12, 0x00D3,
+0x0F12, 0x011E,
+0x0F12, 0x010D,
+0x0F12, 0x00C5,
+0x0F12, 0x0083,
+0x0F12, 0x004E,
+0x0F12, 0x0027,
+0x0F12, 0x000F,
+0x0F12, 0x0008,
+0x0F12, 0x0012,
+0x0F12, 0x002E,
+0x0F12, 0x0059,
+0x0F12, 0x0091,
+0x0F12, 0x00D6,
+0x0F12, 0x0120,
+0x0F12, 0x012A,
+0x0F12, 0x00E2,
+0x0F12, 0x00A2,
+0x0F12, 0x006D,
+0x0F12, 0x0045,
+0x0F12, 0x002D,
+0x0F12, 0x0025,
+0x0F12, 0x002E,
+0x0F12, 0x0049,
+0x0F12, 0x0073,
+0x0F12, 0x00AA,
+0x0F12, 0x00EC,
+0x0F12, 0x0135,
+0x0F12, 0x0149,
+0x0F12, 0x0105,
+0x0F12, 0x00C9,
+0x0F12, 0x0096,
+0x0F12, 0x006F,
+0x0F12, 0x0056,
+0x0F12, 0x004E,
+0x0F12, 0x0056,
+0x0F12, 0x006F,
+0x0F12, 0x0096,
+0x0F12, 0x00CA,
+0x0F12, 0x0109,
+0x0F12, 0x0153,
+0x0F12, 0x0181,
+0x0F12, 0x013B,
+0x0F12, 0x0102,
+0x0F12, 0x00D2,
+0x0F12, 0x00AE,
+0x0F12, 0x0096,
+0x0F12, 0x008D,
+0x0F12, 0x0094,
+0x0F12, 0x00AA,
+0x0F12, 0x00CC,
+0x0F12, 0x00F8,
+0x0F12, 0x0135,
+0x0F12, 0x0183,
+0x0F12, 0x01B0,
+0x0F12, 0x016F,
+0x0F12, 0x0139,
+0x0F12, 0x010E,
+0x0F12, 0x00ED,
+0x0F12, 0x00D6,
+0x0F12, 0x00CD,
+0x0F12, 0x00D3,
+0x0F12, 0x00E8,
+0x0F12, 0x0106,
+0x0F12, 0x012D,
+0x0F12, 0x0163,
+0x0F12, 0x0195,
+0x0F12, 0x0146,
+0x0F12, 0x011B,
+0x0F12, 0x00EC,
+0x0F12, 0x00C6,
+0x0F12, 0x00AD,
+0x0F12, 0x009F,
+0x0F12, 0x009C,
+0x0F12, 0x00A6,
+0x0F12, 0x00BC,
+0x0F12, 0x00DF,
+0x0F12, 0x010B,
+0x0F12, 0x0146,
+0x0F12, 0x0176,
+0x0F12, 0x0120,
+0x0F12, 0x00EC,
+0x0F12, 0x00BA,
+0x0F12, 0x0093,
+0x0F12, 0x007A,
+0x0F12, 0x006C,
+0x0F12, 0x0069,
+0x0F12, 0x0073,
+0x0F12, 0x008B,
+0x0F12, 0x00AD,
+0x0F12, 0x00DD,
+0x0F12, 0x0118,
+0x0F12, 0x0156,
+0x0F12, 0x00F0,
+0x0F12, 0x00BF,
+0x0F12, 0x0089,
+0x0F12, 0x0062,
+0x0F12, 0x0047,
+0x0F12, 0x003A,
+0x0F12, 0x0038,
+0x0F12, 0x0041,
+0x0F12, 0x0058,
+0x0F12, 0x007C,
+0x0F12, 0x00AC,
+0x0F12, 0x00E7,
+0x0F12, 0x0123,
+0x0F12, 0x00D5,
+0x0F12, 0x00A1,
+0x0F12, 0x006C,
+0x0F12, 0x0044,
+0x0F12, 0x0029,
+0x0F12, 0x001B,
+0x0F12, 0x0018,
+0x0F12, 0x0022,
+0x0F12, 0x0039,
+0x0F12, 0x005D,
+0x0F12, 0x008E,
+0x0F12, 0x00CA,
+0x0F12, 0x0105,
+0x0F12, 0x00C1,
+0x0F12, 0x008C,
+0x0F12, 0x0057,
+0x0F12, 0x002F,
+0x0F12, 0x0014,
+0x0F12, 0x0006,
+0x0F12, 0x0003,
+0x0F12, 0x000B,
+0x0F12, 0x0022,
+0x0F12, 0x0046,
+0x0F12, 0x0076,
+0x0F12, 0x00B4,
+0x0F12, 0x00F0,
+0x0F12, 0x00C1,
+0x0F12, 0x008B,
+0x0F12, 0x0055,
+0x0F12, 0x002C,
+0x0F12, 0x0011,
+0x0F12, 0x0003,
+0x0F12, 0x0000,
+0x0F12, 0x0007,
+0x0F12, 0x001D,
+0x0F12, 0x0040,
+0x0F12, 0x0070,
+0x0F12, 0x00AF,
+0x0F12, 0x00EC,
+0x0F12, 0x00C6,
+0x0F12, 0x0092,
+0x0F12, 0x005D,
+0x0F12, 0x0035,
+0x0F12, 0x0019,
+0x0F12, 0x000A,
+0x0F12, 0x0006,
+0x0F12, 0x000E,
+0x0F12, 0x0024,
+0x0F12, 0x0046,
+0x0F12, 0x0074,
+0x0F12, 0x00B0,
+0x0F12, 0x00ED,
+0x0F12, 0x00E0,
+0x0F12, 0x00AA,
+0x0F12, 0x0078,
+0x0F12, 0x0050,
+0x0F12, 0x0034,
+0x0F12, 0x0024,
+0x0F12, 0x001F,
+0x0F12, 0x0026,
+0x0F12, 0x003A,
+0x0F12, 0x005D,
+0x0F12, 0x008B,
+0x0F12, 0x00C4,
+0x0F12, 0x00FE,
+0x0F12, 0x00FE,
+0x0F12, 0x00CB,
+0x0F12, 0x0099,
+0x0F12, 0x0074,
+0x0F12, 0x005A,
+0x0F12, 0x004B,
+0x0F12, 0x0046,
+0x0F12, 0x004A,
+0x0F12, 0x005C,
+0x0F12, 0x007A,
+0x0F12, 0x00A7,
+0x0F12, 0x00E0,
+0x0F12, 0x0119,
+0x0F12, 0x0131,
+0x0F12, 0x00FD,
+0x0F12, 0x00CF,
+0x0F12, 0x00AC,
+0x0F12, 0x0092,
+0x0F12, 0x0084,
+0x0F12, 0x007F,
+0x0F12, 0x0085,
+0x0F12, 0x0094,
+0x0F12, 0x00AE,
+0x0F12, 0x00CF,
+0x0F12, 0x0107,
+0x0F12, 0x0148,
+0x0F12, 0x015F,
+0x0F12, 0x012F,
+0x0F12, 0x0102,
+0x0F12, 0x00E3,
+0x0F12, 0x00CA,
+0x0F12, 0x00BD,
+0x0F12, 0x00B8,
+0x0F12, 0x00BC,
+0x0F12, 0x00CB,
+0x0F12, 0x00E4,
+0x0F12, 0x0104,
+0x0F12, 0x012E,
+0x0F12, 0x0148,
+0x002A, 0x074E,
+0x0F12, 0x0001,
+0x002A, 0x0D30,
+0x0F12, 0x02A8,
+0x0F12, 0x0347,
+0x002A, 0x06B8,
+0x0F12, 0x00C0,
+0x0F12, 0x00E0,
+0x0F12, 0x0120,
+0x0F12, 0x0124,
+0x0F12, 0x0156,
+0x0F12, 0x017F,
+0x0F12, 0x018F,
+0x002A, 0x0664,
+0x0F12, 0x013E,
+0x002A, 0x06C6,
+0x0F12, 0x010B,
+0x0F12, 0x0103,
+0x0F12, 0x00FC,
+0x0F12, 0x010C,
+0x002A, 0x0C48,
+0x0F12, 0x03C8,
+0x0F12, 0x03DE,
+0x0F12, 0x0372,
+0x0F12, 0x03EA,
+0x0F12, 0x0336,
+0x0F12, 0x03DE,
+0x0F12, 0x0302,
+0x0F12, 0x03A2,
+0x0F12, 0x02C8,
+0x0F12, 0x0368,
+0x0F12, 0x0292,
+0x0F12, 0x033A,
+0x0F12, 0x0276,
+0x0F12, 0x0306,
+0x0F12, 0x025A,
+0x0F12, 0x02C2,
+0x0F12, 0x0246,
+0x0F12, 0x02A2,
+0x0F12, 0x0232,
+0x0F12, 0x0298,
+0x0F12, 0x021E,
+0x0F12, 0x027E,
+0x0F12, 0x0208,
+0x0F12, 0x026C,
+0x0F12, 0x01EE,
+0x0F12, 0x025C,
+0x0F12, 0x01F0,
+0x0F12, 0x0248,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0005,
+0x002A, 0x0C9C,
+0x0F12, 0x000E,
+0x002A, 0x0CA0,
+0x0F12, 0x0108,
+0x002A, 0x0CE0,
+0x0F12, 0x03D4,
+0x0F12, 0x043E,
+0x0F12, 0x035C,
+0x0F12, 0x0438,
+0x0F12, 0x02F0,
+0x0F12, 0x042D,
+0x0F12, 0x029A,
+0x0F12, 0x03EF,
+0x0F12, 0x025E,
+0x0F12, 0x0395,
+0x0F12, 0x022E,
+0x0F12, 0x0346,
+0x0F12, 0x0200,
+0x0F12, 0x02F6,
+0x0F12, 0x01CE,
+0x0F12, 0x02C8,
+0x0F12, 0x01BB,
+0x0F12, 0x0287,
+0x0F12, 0x01E2,
+0x0F12, 0x0239,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0006,
+0x002A, 0x0D18,
+0x0F12, 0x00AE,
+0x002A, 0x0CA4,
+0x0F12, 0x026E,
+0x0F12, 0x02A4,
+0x0F12, 0x0262,
+0x0F12, 0x02A8,
+0x0F12, 0x0256,
+0x0F12, 0x02AE,
+0x0F12, 0x0248,
+0x0F12, 0x02A4,
+0x0F12, 0x023E,
+0x0F12, 0x029A,
+0x0F12, 0x023A,
+0x0F12, 0x0290,
+0x0F12, 0x023A,
+0x0F12, 0x027E,
+0x0F12, 0x0244,
+0x0F12, 0x0266,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0004,
+0x002A, 0x0CD8,
+0x0F12, 0x0008,
+0x002A, 0x0CDC,
+0x0F12, 0x0204,
+0x002A, 0x0D1C,
+0x0F12, 0x037C,
+0x002A, 0x0D20,
+0x0F12, 0x0157,
+0x002A, 0x0D24,
+0x0F12, 0x3EB8,
+0x002A, 0x0D2C,
+0x0F12, 0x013D,
+0x0F12, 0x011E,
+0x002A, 0x0D46,
+0x0F12, 0x04C0,
+0x002A, 0x0D28,
+0x0F12, 0x0270,
+0x0F12, 0x0240,
+0x002A, 0x0D5C,
+0x0F12, 0x7FFF,
+0x0F12, 0x0050,
+0x002A, 0x2316,
+0x0F12, 0x0006,
+0x002A, 0x0E44,
+0x0F12, 0x0525,
+0x0F12, 0x0400,
+0x0F12, 0x078C,
+0x002A, 0x0E36,
+0x0F12, 0x0028,
+0x0F12, 0xFFD8,
+0x0F12, 0x0000,
+0x002A, 0x0DD4,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0xFFF0,
+0x0F12, 0x0080,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0xFFF0,
+0x0F12, 0x0080,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0xFFF0,
+0x0F12, 0x0080,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0032,
+0x0F12, 0x0000,
+0x0F12, 0x0020,
+0x0F12, 0xFFC0,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0032,
+0x0F12, 0x0000,
+0x0F12, 0x0020,
+0x0F12, 0xFFC0,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0032,
+0x0F12, 0x0000,
+0x0F12, 0x0020,
+0x0F12, 0xFFC0,
+0x0F12, 0x02D9,
+0x0F12, 0x0357,
+0x0F12, 0x03D1,
+0x0F12, 0x0DF6,
+0x0F12, 0x0ED8,
+0x0F12, 0x0F51,
+0x0F12, 0x0F5C,
+0x0F12, 0x0F8F,
+0x0F12, 0x1006,
+0x0F12, 0x00AC,
+0x0F12, 0x00BD,
+0x0F12, 0x0049,
+0x0F12, 0x00F5,
+0x002A, 0x0E4A,
+0x0F12, 0x0002,
+0x002A, 0x051A,
+0x0F12, 0x010E,
+0x0F12, 0x00F5,
+0x002A, 0x0F76,
+0x0F12, 0x0007,
+0x002A, 0x1034,
+0x0F12, 0x00C0,
+0x0F12, 0x00E0,
+0x0F12, 0x0104,
+0x0F12, 0x0129,
+0x0F12, 0x0156,
+0x0F12, 0x017F,
+0x0F12, 0x018F,
+0x0F12, 0x0120,
+0x0F12, 0x0120,
+0x0F12, 0x0120,
+0x0F12, 0x0100,
+0x0F12, 0x0100,
+0x0F12, 0x0100,
+0x0F12, 0x0100,
+0x002A, 0x3288,
+0x0F12, 0x0000,
+0x0F12, 0x0008,
+0x0F12, 0x0013,
+0x0F12, 0x002C,
+0x0F12, 0x0062,
+0x0F12, 0x00CD,
+0x0F12, 0x0129,
+0x0F12, 0x0151,
+0x0F12, 0x0174,
+0x0F12, 0x01AA,
+0x0F12, 0x01D7,
+0x0F12, 0x01FE,
+0x0F12, 0x0221,
+0x0F12, 0x025D,
+0x0F12, 0x0291,
+0x0F12, 0x02EB,
+0x0F12, 0x033A,
+0x0F12, 0x0380,
+0x0F12, 0x03C2,
+0x0F12, 0x03FF,
+0x0F12, 0x0000,
+0x0F12, 0x0008,
+0x0F12, 0x0013,
+0x0F12, 0x002C,
+0x0F12, 0x0062,
+0x0F12, 0x00CD,
+0x0F12, 0x0129,
+0x0F12, 0x0151,
+0x0F12, 0x0174,
+0x0F12, 0x01AA,
+0x0F12, 0x01D7,
+0x0F12, 0x01FE,
+0x0F12, 0x0221,
+0x0F12, 0x025D,
+0x0F12, 0x0291,
+0x0F12, 0x02EB,
+0x0F12, 0x033A,
+0x0F12, 0x0380,
+0x0F12, 0x03C2,
+0x0F12, 0x03FF,
+0x0F12, 0x0000,
+0x0F12, 0x0008,
+0x0F12, 0x0013,
+0x0F12, 0x002C,
+0x0F12, 0x0062,
+0x0F12, 0x00CD,
+0x0F12, 0x0129,
+0x0F12, 0x0151,
+0x0F12, 0x0174,
+0x0F12, 0x01AA,
+0x0F12, 0x01D7,
+0x0F12, 0x01FE,
+0x0F12, 0x0221,
+0x0F12, 0x025D,
+0x0F12, 0x0291,
+0x0F12, 0x02EB,
+0x0F12, 0x033A,
+0x0F12, 0x0380,
+0x0F12, 0x03C2,
+0x0F12, 0x03FF,
+0x0F12, 0x0000,
+0x0F12, 0x0008,
+0x0F12, 0x0013,
+0x0F12, 0x002C,
+0x0F12, 0x0062,
+0x0F12, 0x00CD,
+0x0F12, 0x0129,
+0x0F12, 0x0151,
+0x0F12, 0x0174,
+0x0F12, 0x01AA,
+0x0F12, 0x01D7,
+0x0F12, 0x01FE,
+0x0F12, 0x0221,
+0x0F12, 0x025D,
+0x0F12, 0x0291,
+0x0F12, 0x02EB,
+0x0F12, 0x033A,
+0x0F12, 0x0380,
+0x0F12, 0x03C2,
+0x0F12, 0x03FF,
+0x0F12, 0x0000,
+0x0F12, 0x0008,
+0x0F12, 0x0013,
+0x0F12, 0x002C,
+0x0F12, 0x0062,
+0x0F12, 0x00CD,
+0x0F12, 0x0129,
+0x0F12, 0x0151,
+0x0F12, 0x0174,
+0x0F12, 0x01AA,
+0x0F12, 0x01D7,
+0x0F12, 0x01FE,
+0x0F12, 0x0221,
+0x0F12, 0x025D,
+0x0F12, 0x0291,
+0x0F12, 0x02EB,
+0x0F12, 0x033A,
+0x0F12, 0x0380,
+0x0F12, 0x03C2,
+0x0F12, 0x03FF,
+0x0F12, 0x0000,
+0x0F12, 0x0008,
+0x0F12, 0x0013,
+0x0F12, 0x002C,
+0x0F12, 0x0062,
+0x0F12, 0x00CD,
+0x0F12, 0x0129,
+0x0F12, 0x0151,
+0x0F12, 0x0174,
+0x0F12, 0x01AA,
+0x0F12, 0x01D7,
+0x0F12, 0x01FE,
+0x0F12, 0x0221,
+0x0F12, 0x025D,
+0x0F12, 0x0291,
+0x0F12, 0x02EB,
+0x0F12, 0x033A,
+0x0F12, 0x0380,
+0x0F12, 0x03C2,
+0x0F12, 0x03FF,
+0x002A, 0x06A6,
+0x0F12, 0x00C0,
+0x0F12, 0x00E0,
+0x0F12, 0x0110,
+0x0F12, 0x0139,
+0x0F12, 0x0166,
+0x0F12, 0x019F,
+0x002A, 0x33A4,
+0x0F12, 0x0181,
+0x0F12, 0xFF88,
+0x0F12, 0xFF90,
+0x0F12, 0xFE6B,
+0x0F12, 0x0106,
+0x0F12, 0xFF0B,
+0x0F12, 0xFFDD,
+0x0F12, 0xFFEE,
+0x0F12, 0x01CB,
+0x0F12, 0x0187,
+0x0F12, 0x00A6,
+0x0F12, 0xFEBE,
+0x0F12, 0x021C,
+0x0F12, 0xFF5F,
+0x0F12, 0x0175,
+0x0F12, 0xFEE7,
+0x0F12, 0x0106,
+0x0F12, 0x00F3,
+0x0F12, 0x0181,
+0x0F12, 0xFF88,
+0x0F12, 0xFF90,
+0x0F12, 0xFE6B,
+0x0F12, 0x0106,
+0x0F12, 0xFF0B,
+0x0F12, 0xFFDD,
+0x0F12, 0xFFEE,
+0x0F12, 0x01CB,
+0x0F12, 0x0187,
+0x0F12, 0x00A6,
+0x0F12, 0xFEBE,
+0x0F12, 0x021C,
+0x0F12, 0xFF5F,
+0x0F12, 0x0175,
+0x0F12, 0xFEE7,
+0x0F12, 0x0106,
+0x0F12, 0x00F3,
+0x0F12, 0x0181,
+0x0F12, 0xFF88,
+0x0F12, 0xFF90,
+0x0F12, 0xFE6B,
+0x0F12, 0x0106,
+0x0F12, 0xFF0B,
+0x0F12, 0xFFDD,
+0x0F12, 0xFFEE,
+0x0F12, 0x01CB,
+0x0F12, 0x0187,
+0x0F12, 0x00A6,
+0x0F12, 0xFEBE,
+0x0F12, 0x021C,
+0x0F12, 0xFF5F,
+0x0F12, 0x0175,
+0x0F12, 0xFEE7,
+0x0F12, 0x0106,
+0x0F12, 0x00F3,
+0x0F12, 0x01FD,
+0x0F12, 0xFFAB,
+0x0F12, 0xFFED,
+0x0F12, 0xFEB5,
+0x0F12, 0x0112,
+0x0F12, 0xFEDC,
+0x0F12, 0xFFD2,
+0x0F12, 0x0015,
+0x0F12, 0x01A1,
+0x0F12, 0x0111,
+0x0F12, 0x009D,
+0x0F12, 0xFECB,
+0x0F12, 0x01FC,
+0x0F12, 0xFF99,
+0x0F12, 0x01A9,
+0x0F12, 0xFF26,
+0x0F12, 0x012B,
+0x0F12, 0x00DF,
+0x0F12, 0x01E3,
+0x0F12, 0xFFA4,
+0x0F12, 0xFFDB,
+0x0F12, 0xFEB5,
+0x0F12, 0x0112,
+0x0F12, 0xFEDC,
+0x0F12, 0xFFD2,
+0x0F12, 0xFFFE,
+0x0F12, 0x01B7,
+0x0F12, 0x00E8,
+0x0F12, 0x0095,
+0x0F12, 0xFF0D,
+0x0F12, 0x0182,
+0x0F12, 0xFF29,
+0x0F12, 0x0146,
+0x0F12, 0xFF26,
+0x0F12, 0x012B,
+0x0F12, 0x00DF,
+0x0F12, 0x01E3,
+0x0F12, 0xFFA4,
+0x0F12, 0xFFDB,
+0x0F12, 0xFEB5,
+0x0F12, 0x0112,
+0x0F12, 0xFEDC,
+0x0F12, 0xFFD2,
+0x0F12, 0xFFFE,
+0x0F12, 0x01B7,
+0x0F12, 0x00E8,
+0x0F12, 0x0095,
+0x0F12, 0xFF0D,
+0x0F12, 0x0182,
+0x0F12, 0xFF29,
+0x0F12, 0x0146,
+0x0F12, 0xFF26,
+0x0F12, 0x012B,
+0x0F12, 0x00DF,
+0x002A, 0x3380,
+0x0F12, 0x020E,
+0x0F12, 0xFF88,
+0x0F12, 0xFFCE,
+0x0F12, 0xFE3D,
+0x0F12, 0x0158,
+0x0F12, 0xFF03,
+0x0F12, 0xFF99,
+0x0F12, 0x0018,
+0x0F12, 0x0235,
+0x0F12, 0x0101,
+0x0F12, 0x0116,
+0x0F12, 0xFF00,
+0x0F12, 0x01C5,
+0x0F12, 0xFF80,
+0x0F12, 0x019D,
+0x0F12, 0xFE7A,
+0x0F12, 0x0179,
+0x0F12, 0x0179,
+0x002A, 0x0764,
+0x0F12, 0x0049,
+0x0F12, 0x005F,
+0x0F12, 0x00CB,
+0x0F12, 0x01E0,
+0x0F12, 0x0220,
+0x002A, 0x07C4,
+0x0F12, 0x0014,
+0x0F12, 0x0000,
+0x0F12, 0x0014,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x00C1,
+0x0F12, 0x03FF,
+0x0F12, 0x009C,
+0x0F12, 0x0251,
+0x0F12, 0x03FF,
+0x0F12, 0x000C,
+0x0F12, 0x0010,
+0x0F12, 0x012C,
+0x0F12, 0x03E8,
+0x0F12, 0x0046,
+0x0F12, 0x005A,
+0x0F12, 0x0070,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x01AA,
+0x0F12, 0x003C,
+0x0F12, 0x003C,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x003E,
+0x0F12, 0x0008,
+0x0F12, 0x003C,
+0x0F12, 0x001E,
+0x0F12, 0x003C,
+0x0F12, 0x001E,
+0x0F12, 0x0A24,
+0x0F12, 0x1701,
+0x0F12, 0x0229,
+0x0F12, 0x1403,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x00FF,
+0x0F12, 0x045A,
+0x0F12, 0x1414,
+0x0F12, 0x0301,
+0x0F12, 0xFF07,
+0x0F12, 0x081E,
+0x0F12, 0x0A14,
+0x0F12, 0x0F0F,
+0x0F12, 0x0A00,
+0x0F12, 0x0032,
+0x0F12, 0x000E,
+0x0F12, 0x0002,
+0x0F12, 0x00FF,
+0x0F12, 0x1102,
+0x0F12, 0x001B,
+0x0F12, 0x0900,
+0x0F12, 0x0600,
+0x0F12, 0x0504,
+0x0F12, 0x0306,
+0x0F12, 0x4603,
+0x0F12, 0x0480,
+0x0F12, 0x003C,
+0x0F12, 0x0080,
+0x0F12, 0x0101,
+0x0F12, 0x0707,
+0x0F12, 0x4601,
+0x0F12, 0x4944,
+0x0F12, 0x5044,
+0x0F12, 0x0500,
+0x0F12, 0x0003,
+0x0F12, 0x5400,
+0x0F12, 0x0714,
+0x0F12, 0x32FF,
+0x0F12, 0x5A04,
+0x0F12, 0x201E,
+0x0F12, 0x4012,
+0x0F12, 0x0204,
+0x0F12, 0x1403,
+0x0F12, 0x0114,
+0x0F12, 0x0101,
+0x0F12, 0x4446,
+0x0F12, 0x646E,
+0x0F12, 0x0028,
+0x0F12, 0x030A,
+0x0F12, 0x0000,
+0x0F12, 0x141E,
+0x0F12, 0xFF07,
+0x0F12, 0x0432,
+0x0F12, 0x0000,
+0x0F12, 0x0F0F,
+0x0F12, 0x0440,
+0x0F12, 0x0302,
+0x0F12, 0x1414,
+0x0F12, 0x0101,
+0x0F12, 0x4601,
+0x0F12, 0x6E44,
+0x0F12, 0x2864,
+0x0F12, 0x0A00,
+0x0F12, 0x0003,
+0x0F12, 0x1E00,
+0x0F12, 0x0714,
+0x0F12, 0x32FF,
+0x0F12, 0x0004,
+0x0F12, 0x0F00,
+0x0F12, 0x400F,
+0x0F12, 0x0204,
+0x0F12, 0x0003,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0014,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x00C1,
+0x0F12, 0x03FF,
+0x0F12, 0x009C,
+0x0F12, 0x0251,
+0x0F12, 0x03FF,
+0x0F12, 0x000C,
+0x0F12, 0x0010,
+0x0F12, 0x012C,
+0x0F12, 0x03E8,
+0x0F12, 0x0046,
+0x0F12, 0x005A,
+0x0F12, 0x0070,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x01AE,
+0x0F12, 0x001E,
+0x0F12, 0x001E,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x003E,
+0x0F12, 0x0008,
+0x0F12, 0x003C,
+0x0F12, 0x001E,
+0x0F12, 0x003C,
+0x0F12, 0x001E,
+0x0F12, 0x0A24,
+0x0F12, 0x1701,
+0x0F12, 0x0229,
+0x0F12, 0x1403,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x00FF,
+0x0F12, 0x045A,
+0x0F12, 0x1414,
+0x0F12, 0x0301,
+0x0F12, 0xFF07,
+0x0F12, 0x081E,
+0x0F12, 0x0A14,
+0x0F12, 0x0F0F,
+0x0F12, 0x0A00,
+0x0F12, 0x0032,
+0x0F12, 0x000E,
+0x0F12, 0x0002,
+0x0F12, 0x00FF,
+0x0F12, 0x1102,
+0x0F12, 0x001B,
+0x0F12, 0x0900,
+0x0F12, 0x0600,
+0x0F12, 0x0504,
+0x0F12, 0x0306,
+0x0F12, 0x4603,
+0x0F12, 0x0480,
+0x0F12, 0x0046,
+0x0F12, 0x0080,
+0x0F12, 0x0101,
+0x0F12, 0x0707,
+0x0F12, 0x1E01,
+0x0F12, 0x491E,
+0x0F12, 0x5044,
+0x0F12, 0x0500,
+0x0F12, 0x0004,
+0x0F12, 0x3C0A,
+0x0F12, 0x0714,
+0x0F12, 0x3214,
+0x0F12, 0x5A03,
+0x0F12, 0x121E,
+0x0F12, 0x4012,
+0x0F12, 0x0604,
+0x0F12, 0x1E06,
+0x0F12, 0x011E,
+0x0F12, 0x0101,
+0x0F12, 0x3A3C,
+0x0F12, 0x585A,
+0x0F12, 0x0028,
+0x0F12, 0x030A,
+0x0F12, 0x0000,
+0x0F12, 0x141E,
+0x0F12, 0xFF07,
+0x0F12, 0x0432,
+0x0F12, 0x0000,
+0x0F12, 0x0F0F,
+0x0F12, 0x0440,
+0x0F12, 0x0302,
+0x0F12, 0x1E1E,
+0x0F12, 0x0101,
+0x0F12, 0x3C01,
+0x0F12, 0x5A3A,
+0x0F12, 0x2858,
+0x0F12, 0x0A00,
+0x0F12, 0x0003,
+0x0F12, 0x1E00,
+0x0F12, 0x0714,
+0x0F12, 0x32FF,
+0x0F12, 0x0004,
+0x0F12, 0x0F00,
+0x0F12, 0x400F,
+0x0F12, 0x0204,
+0x0F12, 0x0003,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x00C1,
+0x0F12, 0x03FF,
+0x0F12, 0x009C,
+0x0F12, 0x0251,
+0x0F12, 0x03FF,
+0x0F12, 0x000C,
+0x0F12, 0x0010,
+0x0F12, 0x012C,
+0x0F12, 0x03E8,
+0x0F12, 0x0046,
+0x0F12, 0x005A,
+0x0F12, 0x0070,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0226,
+0x0F12, 0x001E,
+0x0F12, 0x001E,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x004E,
+0x0F12, 0x0000,
+0x0F12, 0x003C,
+0x0F12, 0x001E,
+0x0F12, 0x003C,
+0x0F12, 0x001E,
+0x0F12, 0x0A24,
+0x0F12, 0x1701,
+0x0F12, 0x0229,
+0x0F12, 0x1403,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0906,
+0x0F12, 0x00FF,
+0x0F12, 0x045A,
+0x0F12, 0x1414,
+0x0F12, 0x0301,
+0x0F12, 0xFF07,
+0x0F12, 0x081E,
+0x0F12, 0x0A14,
+0x0F12, 0x0F0F,
+0x0F12, 0x0A00,
+0x0F12, 0x0090,
+0x0F12, 0x000A,
+0x0F12, 0x0002,
+0x0F12, 0x00FF,
+0x0F12, 0x1102,
+0x0F12, 0x001B,
+0x0F12, 0x0900,
+0x0F12, 0x0600,
+0x0F12, 0x0504,
+0x0F12, 0x0306,
+0x0F12, 0x4602,
+0x0F12, 0x0880,
+0x0F12, 0x0080,
+0x0F12, 0x0080,
+0x0F12, 0x0101,
+0x0F12, 0x0707,
+0x0F12, 0x1E01,
+0x0F12, 0x3C1E,
+0x0F12, 0x5028,
+0x0F12, 0x0500,
+0x0F12, 0x1A04,
+0x0F12, 0x280A,
+0x0F12, 0x080C,
+0x0F12, 0x1414,
+0x0F12, 0x6A03,
+0x0F12, 0x121E,
+0x0F12, 0x4012,
+0x0F12, 0x0604,
+0x0F12, 0x2806,
+0x0F12, 0x0128,
+0x0F12, 0x0101,
+0x0F12, 0x2224,
+0x0F12, 0x3236,
+0x0F12, 0x0028,
+0x0F12, 0x030A,
+0x0F12, 0x0410,
+0x0F12, 0x141E,
+0x0F12, 0xFF07,
+0x0F12, 0x0432,
+0x0F12, 0x4050,
+0x0F12, 0x0F0F,
+0x0F12, 0x0440,
+0x0F12, 0x0302,
+0x0F12, 0x2828,
+0x0F12, 0x0101,
+0x0F12, 0x2401,
+0x0F12, 0x3622,
+0x0F12, 0x2832,
+0x0F12, 0x0A00,
+0x0F12, 0x1003,
+0x0F12, 0x1E04,
+0x0F12, 0x0714,
+0x0F12, 0x32FF,
+0x0F12, 0x5004,
+0x0F12, 0x0F40,
+0x0F12, 0x400F,
+0x0F12, 0x0204,
+0x0F12, 0x0003,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x00C1,
+0x0F12, 0x03FF,
+0x0F12, 0x009C,
+0x0F12, 0x0251,
+0x0F12, 0x03FF,
+0x0F12, 0x000C,
+0x0F12, 0x0010,
+0x0F12, 0x00C8,
+0x0F12, 0x03E8,
+0x0F12, 0x0046,
+0x0F12, 0x0050,
+0x0F12, 0x0070,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0226,
+0x0F12, 0x0014,
+0x0F12, 0x0014,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x004E,
+0x0F12, 0x0000,
+0x0F12, 0x002D,
+0x0F12, 0x0019,
+0x0F12, 0x002D,
+0x0F12, 0x0019,
+0x0F12, 0x0A24,
+0x0F12, 0x1701,
+0x0F12, 0x0229,
+0x0F12, 0x1403,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0906,
+0x0F12, 0x00FF,
+0x0F12, 0x045A,
+0x0F12, 0x1414,
+0x0F12, 0x0301,
+0x0F12, 0xFF07,
+0x0F12, 0x081E,
+0x0F12, 0x0A14,
+0x0F12, 0x0F0F,
+0x0F12, 0x0A00,
+0x0F12, 0x0090,
+0x0F12, 0x000A,
+0x0F12, 0x0001,
+0x0F12, 0x00FF,
+0x0F12, 0x1002,
+0x0F12, 0x001E,
+0x0F12, 0x0900,
+0x0F12, 0x0600,
+0x0F12, 0x0504,
+0x0F12, 0x0307,
+0x0F12, 0x5002,
+0x0F12, 0x0080,
+0x0F12, 0x0080,
+0x0F12, 0x0080,
+0x0F12, 0x0101,
+0x0F12, 0x0707,
+0x0F12, 0x1E01,
+0x0F12, 0x2A1E,
+0x0F12, 0x5020,
+0x0F12, 0x0500,
+0x0F12, 0x1A04,
+0x0F12, 0x280A,
+0x0F12, 0x080C,
+0x0F12, 0x1414,
+0x0F12, 0x6A03,
+0x0F12, 0x121E,
+0x0F12, 0x4012,
+0x0F12, 0x0604,
+0x0F12, 0x3C06,
+0x0F12, 0x013C,
+0x0F12, 0x0101,
+0x0F12, 0x1C1E,
+0x0F12, 0x1E22,
+0x0F12, 0x0028,
+0x0F12, 0x030A,
+0x0F12, 0x0214,
+0x0F12, 0x0E14,
+0x0F12, 0xFF06,
+0x0F12, 0x0432,
+0x0F12, 0x4052,
+0x0F12, 0x150C,
+0x0F12, 0x0440,
+0x0F12, 0x0302,
+0x0F12, 0x3C3C,
+0x0F12, 0x0101,
+0x0F12, 0x1E01,
+0x0F12, 0x221C,
+0x0F12, 0x281E,
+0x0F12, 0x0A00,
+0x0F12, 0x1403,
+0x0F12, 0x1402,
+0x0F12, 0x060E,
+0x0F12, 0x32FF,
+0x0F12, 0x5204,
+0x0F12, 0x0C40,
+0x0F12, 0x4015,
+0x0F12, 0x0204,
+0x0F12, 0x0003,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x00C1,
+0x0F12, 0x03FF,
+0x0F12, 0x009C,
+0x0F12, 0x0251,
+0x0F12, 0x03FF,
+0x0F12, 0x000C,
+0x0F12, 0x0010,
+0x0F12, 0x0032,
+0x0F12, 0x028A,
+0x0F12, 0x0032,
+0x0F12, 0x01F4,
+0x0F12, 0x0070,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x01AA,
+0x0F12, 0x003C,
+0x0F12, 0x0050,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0044,
+0x0F12, 0x0014,
+0x0F12, 0x0046,
+0x0F12, 0x0019,
+0x0F12, 0x0046,
+0x0F12, 0x0019,
+0x0F12, 0x0A24,
+0x0F12, 0x1701,
+0x0F12, 0x0229,
+0x0F12, 0x0503,
+0x0F12, 0x080F,
+0x0F12, 0x0808,
+0x0F12, 0x0000,
+0x0F12, 0x00FF,
+0x0F12, 0x012D,
+0x0F12, 0x1414,
+0x0F12, 0x0301,
+0x0F12, 0xFF07,
+0x0F12, 0x061E,
+0x0F12, 0x0A1E,
+0x0F12, 0x0606,
+0x0F12, 0x0A03,
+0x0F12, 0x378B,
+0x0F12, 0x1028,
+0x0F12, 0x0001,
+0x0F12, 0x00FF,
+0x0F12, 0x1002,
+0x0F12, 0x001E,
+0x0F12, 0x0900,
+0x0F12, 0x0600,
+0x0F12, 0x0504,
+0x0F12, 0x0307,
+0x0F12, 0x5001,
+0x0F12, 0x0080,
+0x0F12, 0x0080,
+0x0F12, 0x0080,
+0x0F12, 0x0E0E,
+0x0F12, 0x0101,
+0x0F12, 0x3201,
+0x0F12, 0x1832,
+0x0F12, 0x210C,
+0x0F12, 0x0A00,
+0x0F12, 0x1E04,
+0x0F12, 0x0A08,
+0x0F12, 0x070C,
+0x0F12, 0x3264,
+0x0F12, 0x5A02,
+0x0F12, 0x1040,
+0x0F12, 0x4012,
+0x0F12, 0x0604,
+0x0F12, 0x4606,
+0x0F12, 0x0146,
+0x0F12, 0x0101,
+0x0F12, 0x1C18,
+0x0F12, 0x1819,
+0x0F12, 0x0028,
+0x0F12, 0x030A,
+0x0F12, 0x0514,
+0x0F12, 0x0C14,
+0x0F12, 0xFF05,
+0x0F12, 0x0432,
+0x0F12, 0x4052,
+0x0F12, 0x1514,
+0x0F12, 0x0440,
+0x0F12, 0x0302,
+0x0F12, 0x4646,
+0x0F12, 0x0101,
+0x0F12, 0x1801,
+0x0F12, 0x191C,
+0x0F12, 0x2818,
+0x0F12, 0x0A00,
+0x0F12, 0x1403,
+0x0F12, 0x1405,
+0x0F12, 0x050C,
+0x0F12, 0x32FF,
+0x0F12, 0x5204,
+0x0F12, 0x1440,
+0x0F12, 0x4015,
+0x0F12, 0x0204,
+0x0F12, 0x0003,
+0x0F12, 0x7DFA,
+0x0F12, 0xFFBD,
+0x0F12, 0x26FE,
+0x0F12, 0xF7BC,
+0x0F12, 0x7E06,
+0x0F12, 0x00D3,
+0x002A, 0x3904,
+0x0F12, 0x0007,
+0x0F12, 0x00E2,
+0x0F12, 0x0005,
+0x0F12, 0x00E2,
+0x002A, 0x0F7E,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0101,
+0x0F12, 0x0101,
+0x0F12, 0x0101,
+0x0F12, 0x0101,
+0x0F12, 0x0101,
+0x0F12, 0x0201,
+0x0F12, 0x0102,
+0x0F12, 0x0101,
+0x0F12, 0x0101,
+0x0F12, 0x0202,
+0x0F12, 0x0202,
+0x0F12, 0x0101,
+0x0F12, 0x0101,
+0x0F12, 0x0202,
+0x0F12, 0x0202,
+0x0F12, 0x0101,
+0x0F12, 0x0201,
+0x0F12, 0x0202,
+0x0F12, 0x0202,
+0x0F12, 0x0102,
+0x0F12, 0x0201,
+0x0F12, 0x0202,
+0x0F12, 0x0202,
+0x0F12, 0x0102,
+0x0F12, 0x0101,
+0x0F12, 0x0101,
+0x0F12, 0x0101,
+0x0F12, 0x0101,
+0x002A, 0x01CC,
+0x0F12, 0x5DC0,
+0x0F12, 0x0000,
+0x002A, 0x01EE,
+0x0F12, 0x0003,
+0x0F12, 0x0000,
+0x002A, 0x01F6,
+0x0F12, 0x1F40,
+0x0F12, 0x32A8,
+0x0F12, 0x32E8,
+0x0F12, 0x1F40,
+0x0F12, 0x2ea0,
+0x0F12, 0x2f00,
+0x0F12, 0x38A4,
+0x0F12, 0x37A4,
+0x0F12, 0x39A4,
+0x002A, 0x0208,
+0x0F12, 0x0001,
+};
+
+uint32_t s5k5ca_default_regs_init2[]={
+0x0028, 0x7000,
+0x002A, 0x026C,
+0x0F12, 0x0280,
+0x0F12, 0x01E0,
+0x0F12, 0x0005,
+0x0F12, 0x32e8,
+0x0F12, 0x32A8,
+0x0F12, 0x0100,
+0x0F12, 0x0800,
+0x0F12, 0x0046,
+0x0F12, 0x0010,
+0x0F12, 0x01E0,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0001,
+0x0F12, 0x029a,
+0x0F12, 0x014E,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+
+//Preview config[1] 320 240
+0x0028, 0x7000,
+0x002A, 0x029C,
+0x0F12, 0x0140,
+0x0F12, 0x00F0,
+0x0F12, 0x0005,
+0x0F12, 0x32E8,
+0x0F12, 0x32a8,
+0x0F12, 0x0100,
+0x0F12, 0x0800,
+0x0F12, 0x0046,
+0x0F12, 0x0010,
+0x0F12, 0x01E0,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0001,
+0x0F12, 0x029A,
+0x0F12, 0x014E,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+
+//Preview config[2] 176 144
+0x0028, 0x7000,
+0x002A, 0x02CC,
+0x0F12, 0x00B0,
+0x0F12, 0x0090,
+0x0F12, 0x0005,
+0x0F12, 0x32E8,
+0x0F12, 0x32a8,
+0x0F12, 0x0100,
+0x0F12, 0x0800,
+0x0F12, 0x0046,
+0x0F12, 0x0010,
+0x0F12, 0x01E0,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0001,
+0x0F12, 0x029A,
+0x0F12, 0x014E,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+
+//Preview config[3] night
+0x0028, 0x7000,
+0x002A, 0x02FC,
+0x0F12, 0x0280,
+0x0F12, 0x01E0,
+0x0F12, 0x0005,
+0x0F12, 0x32E8,
+0x0F12, 0x32a8,
+0x0F12, 0x0100,
+0x0F12, 0x0800,
+0x0F12, 0x0046,
+0x0F12, 0x0010,
+0x0F12, 0x01E0,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0001,
+0x0F12, 0x0535,
+0x0F12, 0x029A,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+
+0x002A, 0x035C,
+0x0F12, 0x0000,
+0x0F12, 0x0800,
+0x0F12, 0x0600,
+0x0F12, 0x0005,
+0x0F12, 0x32E8,
+0x0F12, 0x32a8,
+0x0F12, 0x0100,
+0x0F12, 0x0800,
+0x0F12, 0x0046,
+0x0F12, 0x0010,
+0x0F12, 0x01E0,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0002,
+0x0F12, 0x0535,
+0x0F12, 0x0535,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0800,
+0x0F12, 0x0600,
+0x0F12, 0x0005,
+0x0F12, 0x32E8,
+0x0F12, 0x32a8,
+0x0F12, 0x0100,
+0x0F12, 0x0800,
+0x0F12, 0x0046,
+0x0F12, 0x0010,
+0x0F12, 0x01E0,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0002,
+0x0F12, 0x09C4,
+0x0F12, 0x0535,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+0x0F12, 0x0000,
+};
+
+uint32_t s5k5ca_default_regs_init3[]={
+0x002A, 0x0208,
+0x0F12, 0x0001,
+0x002A, 0x023C,
+0x0F12, 0x0000,
+0x002A, 0x0244,
+0x0F12, 0x0000,
+0x002A, 0x0240,
+0x0F12, 0x0001,
+0x002A, 0x0230,
+0x0F12, 0x0001,
+0x002A, 0x023E,
+0x0F12, 0x0001,
+0x002A, 0x0246,
+0x0F12, 0x0001,
+0x002A, 0x0220,
+0x0F12, 0x0001,
+0x0F12, 0x0001,
+0x1000, 0x0001,
+};
+
+uint32_t s5k5ca_default_regs_init4[]={
+0x002A, 0x0DA0,
+0x0F12, 0x0005,
+0x002A, 0x0D88,
+0x0F12, 0x0038,
+0x0F12, 0x0074,
+0x0F12, 0xFFF1,
+0x0F12, 0x00BF,
+0x0F12, 0xFF9B,
+0x0F12, 0x00DB,
+0x0F12, 0xFF56,
+0x0F12, 0x00F0,
+0x0F12, 0xFEFF,
+0x0F12, 0x010F,
+0x0F12, 0x0E74,
+0x002A, 0x0DA8,
+0x0F12, 0x0BB8,
+0x002A, 0x0DA4,
+0x0F12, 0x274E,
+0x002A, 0x0DCA,
+0x0F12, 0x0030,
+0x002A, 0x3286,
+0x0F12, 0x0001,
+0x002A, 0x032C,
+0x0F12, 0xAAAA,
+0x002A, 0x032E,
+0x0F12, 0xFFFE,
+0x0F12, 0x0000,
+0x0F12, 0x001C,
+0x0F12, 0x0000,
+};
+
+#endif
+
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/sid130b/sid130b.c b/drivers/media/video/wmt_v4l2/sensors/sid130b/sid130b.c
new file mode 100755
index 00000000..f5565774
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/sid130b/sid130b.c
@@ -0,0 +1,328 @@
+
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "sid130b.h"
+
+#define sensor_write_array(sd, arr, sz) \
+ cmos_init_8bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write(sd->i2c_addr, reg, val)
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint8_t *regs;
+ size_t size;
+};
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+// CMOS_WIN_SIZE("QVGA", 320, 240, sid130b_320_240_regs),
+ CMOS_WIN_SIZE("VGA", 640, 480, sid130b_640_480_regs),
+ CMOS_WIN_SIZE("UXGA", 1600, 1200, sid130b_1600_1200_regs),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = sid130b_wb_auto;
+ size = ARRAY_SIZE(sid130b_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = sid130b_wb_incandescent;
+ size = ARRAY_SIZE(sid130b_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = sid130b_wb_daylight;
+ size = ARRAY_SIZE(sid130b_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = sid130b_wb_cloudy;
+ size = ARRAY_SIZE(sid130b_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = sid130b_wb_fluorescent;
+ size = ARRAY_SIZE(sid130b_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_scenemode(struct cmos_subdev *sd, enum v4l2_scene_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case SCENE_MODE_AUTO:
+ regs = sid130b_scene_mode_auto;
+ size = ARRAY_SIZE(sid130b_scene_mode_auto);
+ break;
+ case SCENE_MODE_NIGHTSHOT:
+ regs = sid130b_scene_mode_night;
+ size = ARRAY_SIZE(sid130b_scene_mode_night);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_exposure(struct cmos_subdev *sd, enum v4l2_exposure_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case -2:
+ regs = sid130b_exposure_neg6;
+ size = ARRAY_SIZE(sid130b_exposure_neg6);
+ break;
+ case -1:
+ regs = sid130b_exposure_neg3;
+ size = ARRAY_SIZE(sid130b_exposure_neg3);
+ break;
+ case 0:
+ regs = sid130b_exposure_zero;
+ size = ARRAY_SIZE(sid130b_exposure_zero);
+ break;
+ case 1:
+ regs = sid130b_exposure_pos3;
+ size = ARRAY_SIZE(sid130b_exposure_pos3);
+ break;
+ case 2:
+ regs = sid130b_exposure_pos6;
+ size = ARRAY_SIZE(sid130b_exposure_pos6);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data;
+
+ sensor_write(sd, 0x00, 0x00);
+
+ data = sensor_read(sd, 0x04);
+
+ switch (value) {
+ case 0:
+ data &= ~0x01;
+ break;
+ case 1:
+ data |= 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sensor_write(sd, 0x04, data);
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data;
+
+ sensor_write(sd, 0x00, 0x00);
+
+ data = sensor_read(sd, 0x04);
+
+ switch (value) {
+ case 0:
+ data &= ~0x02;
+ break;
+ case 1:
+ data |= 0x02;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sensor_write(sd, 0x04, data);
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ case V4L2_CID_EXPOSURE:
+ return v4l2_ctrl_query_fill(qc, -2, 2, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return sensor_s_scenemode(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return sensor_s_exposure(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ default:
+ case WMT_V4L2_CID_CAMERA_ANTIBANDING:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win) {
+ pr_err("%s, s_mbus_fmt failed, width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, win->regs, win->size);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return 0;
+}
+
+static int sensor_enum_framesizes(struct cmos_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ const struct cmos_win_size *win = &cmos_supported_win_sizes[index];
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ int data;
+
+ sensor_write(sd, 0x0, 0x0);
+
+ data = sensor_read(sd, 0x01);
+
+ return (data == sd->id) ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ if (sensor_identify(sd)) {
+ return -1;
+ }
+
+ sensor_write_array(sd, sid130b_default_regs_init,
+ ARRAY_SIZE(sid130b_default_regs_init));
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ return 0;
+}
+
+static struct cmos_subdev_ops sid130b_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+ .enum_framesizes = sensor_enum_framesizes,
+};
+
+struct cmos_subdev sid130b = {
+ .name = "sid130b",
+ .i2c_addr = 0x37,
+ .id = 0x1b,
+ .max_width = 1600,
+ .max_height = 1200,
+ .ops = &sid130b_ops,
+};
+
+#if 0
+static int __init sid130b_init(void)
+{
+ return cmos_register_sudbdev(&sid130b);
+}
+
+static void __exit sid130b_exit(void)
+{
+ return cmos_unregister_subdev(&sid130b);
+}
+
+module_init(sid130b_init);
+module_exit(sid130b_exit);
+
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/sid130b/sid130b.h b/drivers/media/video/wmt_v4l2/sensors/sid130b/sid130b.h
new file mode 100755
index 00000000..614fb629
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/sid130b/sid130b.h
@@ -0,0 +1,957 @@
+
+#ifndef __SIV130B_H_
+#define __SIV130B_H_
+
+// Scene Mode
+uint8_t sid130b_scene_mode_auto[] = {
+ 0x00,0x01,
+ 0x11,0x0c,//0a,
+ 0x40,0x45,//00,
+ 0xff,0xff,
+};
+
+uint8_t sid130b_scene_mode_night[] = {
+ 0x00,0x01,
+ 0x11,0x0e,//14,
+ 0x40,0x60,
+ 0xff,0xff,
+};
+
+
+// White Balance
+uint8_t sid130b_wb_auto [] = {
+ 0x00,0x02,
+ 0x10,0xd0,
+ 0xff,0xff,
+};
+
+uint8_t sid130b_wb_incandescent [] = {
+ 0x00,0x02,
+ 0x10,0x00,
+ 0x50,0xaa,
+ 0x51,0xbe,
+ 0xff,0xff,
+};
+
+uint8_t sid130b_wb_fluorescent [] = {
+ 0x00,0x02,
+ 0x10,0x00,
+ 0x50,0xc2,
+ 0x51,0x9e,
+ 0xff,0xff,
+};
+
+uint8_t sid130b_wb_daylight [] = {
+ 0x00,0x02,
+ 0x10,0x00,
+ 0x50,0xaa,
+ 0x51,0x90,
+ 0xff,0xff,
+};
+
+uint8_t sid130b_wb_cloudy [] = {
+ 0x00,0x02,
+ 0x10,0x00,
+ 0x50,0xd0,
+ 0x51,0x88,
+ 0xff,0xff,
+};
+
+uint8_t sid130b_wb_tungsten [] = {
+};
+
+
+// Exposure
+uint8_t sid130b_exposure_neg6[] = {
+
+ 0x00,0x04,
+ 0xb6,0xc0,//00,
+
+
+
+
+};
+
+uint8_t sid130b_exposure_neg3[] = {
+
+ 0x00,0x04,
+ 0xb6,0xb0,//00,
+
+
+
+};
+
+uint8_t sid130b_exposure_zero[] = {
+
+ 0x00,0x04,
+ 0xb6,0x90,//00,
+
+
+
+};
+
+uint8_t sid130b_exposure_pos3[] = {
+
+ 0x00,0x04,
+ 0xb6,0x00,//00,
+
+
+};
+
+uint8_t sid130b_exposure_pos6[] = {
+
+ 0x00,0x04,
+ 0xb6,0x10,//00,
+
+
+
+};
+
+
+// Color Effect
+uint8_t sid130b_colorfx_none[] = {
+};
+
+uint8_t sid130b_colorfx_bw[] = {
+};
+
+uint8_t sid130b_colorfx_sepia[] = {
+};
+
+uint8_t sid130b_colorfx_negative[] = {
+};
+
+uint8_t sid130b_colorfx_emboss[] = {
+};
+
+uint8_t sid130b_colorfx_sketch[] = {
+};
+
+uint8_t sid130b_colorfx_sky_blue[] = {
+};
+
+uint8_t sid130b_colorfx_grass_green[] = {
+};
+
+uint8_t sid130b_colorfx_skin_whiten[] = {
+};
+
+uint8_t sid130b_colorfx_vivid[] = {
+};
+
+// Brightness
+uint8_t sid130b_brightness_neg4[] = {
+
+};
+
+uint8_t sid130b_brightness_neg3[] = {
+};
+
+uint8_t sid130b_brightness_neg2[] = {
+};
+
+uint8_t sid130b_brightness_neg1[] = {
+};
+
+uint8_t sid130b_brightness_zero[] = {
+};
+
+uint8_t sid130b_brightness_pos1[] = {
+};
+
+uint8_t sid130b_brightness_pos2[] = {
+};
+
+uint8_t sid130b_brightness_pos3[] = {
+};
+
+uint8_t sid130b_brightness_pos4[] = {
+};
+
+// Contrast
+uint8_t sid130b_contrast_neg4[] = {
+};
+
+uint8_t sid130b_contrast_neg3[] = {
+};
+
+uint8_t sid130b_contrast_neg2[] = {
+};
+
+uint8_t sid130b_contrast_neg1[] = {
+};
+
+uint8_t sid130b_contrast_zero[] = {
+};
+
+uint8_t sid130b_contrast_pos1[] = {
+};
+
+uint8_t sid130b_contrast_pos2[] = {
+};
+
+uint8_t sid130b_contrast_pos3[] = {
+};
+
+uint8_t sid130b_contrast_pos4[] = {
+};
+
+// Saturation
+uint8_t sid130b_saturation_neg4[] = {
+};
+
+uint8_t sid130b_saturation_neg3[] = {
+};
+
+uint8_t sid130b_saturation_neg2[] = {
+};
+
+uint8_t sid130b_saturation_neg1[] = {
+};
+
+uint8_t sid130b_saturation_zero[] = {
+};
+
+uint8_t sid130b_saturation_pos1[] = {
+};
+
+uint8_t sid130b_saturation_pos2[] = {
+};
+
+uint8_t sid130b_saturation_pos3[] = {
+};
+
+uint8_t sid130b_saturation_pos4[] = {
+};
+
+
+// Resolution
+
+// QVGA
+uint8_t sid130b_320_240_regs[]={
+
+ 0x00,0x03,
+ 0x94,0x01,
+ 0x95,0x40,
+ 0x96,0x00,
+ 0x97,0xf0,
+ 0xff,0xff,
+
+};
+
+// VGA
+uint8_t sid130b_640_480_regs[]={
+
+ 0x00,0x00,
+ 0x04,0x00,
+ 0x05,0x0f,
+ 0x06,0x0f,
+ 0x00,0x03,
+ 0x94,0x06, //0x02
+ 0x95,0x40, //0x80
+ 0x96,0x04, //0x01
+ 0x97,0xb0, //0xe0
+
+ 0x00,0x04,
+ 0xe0,0xe0,
+ 0xe1,0x66,
+ 0xe2,0x66,
+ 0xe3,0x00,
+ 0xe4,0x50,
+ 0xe5,0x03,
+ 0xe6,0xc5,//00,
+ 0xe7,0x02,//00,
+ 0xe8,0x80,//00,
+ 0xff,0xff,
+
+
+
+
+};
+//SVGA
+uint8_t sid130b_800_600_regs[]={
+ 0x00,0x00,
+ 0x04,0x10,
+ 0x05,0x0f,
+ 0x06,0x8e,
+ 0x00,0x03,
+ 0x94,0x03, //0x02
+ 0x95,0x20, //0x80
+ 0x96,0x02, //0x01
+ 0x97,0x58, //0xe0
+ 0x00,0x04,
+ 0xe0,0x00,
+ 0xe1,0x00,
+ 0xe2,0x00,
+ 0xe3,0x00,
+ 0xe4,0x00,
+ 0xe5,0x00,
+ 0xe6,0x00,
+ 0xe7,0x00,
+ 0xe8,0x00,
+ 0xff,0xff,
+};
+
+
+// UXGA
+uint8_t sid130b_1600_1200_regs[]={
+ 0x00,0x00,
+ 0x04,0x10,
+ 0x05,0x0f,
+ 0x00,0x03,
+ 0x94,0x06,
+ 0x95,0x40,
+ 0x96,0x04,
+ 0x97,0xb0,
+ 0x00,0x04,
+ 0xe0,0x00,
+ 0xe1,0x00,
+ 0xe2,0x00,
+ 0xe3,0x00,
+ 0xe4,0x00,
+ 0xe5,0x00,
+ 0xe6,0x00,
+ 0xe7,0x00,
+ 0xe8,0x00,
+ 0xff,0xff,
+};
+
+ // Initiliztion
+ uint8_t sid130b_default_regs_init[] = {
+ 0x00,0x00,
+ 0x04,0x00,
+ 0x05,0x0F,
+ 0x06,0x0f,//86,
+ 0x07,0x88,//08,
+ 0x08,0xa2,
+ 0x09,0x11,
+ 0x0A,0x11,
+ 0x10,0x11,//17,
+ 0x11,0x01,
+ 0x12,0x8A,
+ 0x13,0x1a,//16,
+ 0x17,0xcb,//CF,
+ 0x18,0x38,//34,
+
+ 0x40,0x0f,
+ 0x41,0x17,
+ 0x42,0x52,
+ 0x43,0x80,
+ 0x44,0x00,
+ 0x45,0x00,
+
+ 0x00,0x00,
+ 0x20,0x00,
+ 0x21,0x02,
+ 0x23,0x51,//29,
+ 0x00,0x01,
+ 0x35,0x78,//50,
+ 0x00,0x00,
+ 0x24,0x02,
+ //0x25,0xe6,
+ 0x27,0x51,//29,
+ 0x00,0x01,
+ 0x34,0x78,//50,
+
+ 0x00,0x01,
+ 0x10,0x80,
+ 0x11,0x0c,//10,//0c,
+ 0x12,0x75,
+ 0x13,0x75,
+ 0x14,0x75,
+ 0x17,0xC4,
+ //0x1c,0x02,
+ //0x19,0x00,
+ //0x1A,0x00,
+ //0x1c,0x02,
+ 0x36,0x40,
+
+ 0x40,0x45,
+ 0x41,0x28,//20,
+ 0x42,0x20,
+ 0x43,0x00,
+ 0x44,0x00,
+ 0x45,0x01,
+ 0x46,0x03,
+ 0x47,0x05,
+ 0x48,0x0a,
+ 0x49,0x0e,
+ 0x4A,0x13,
+ 0x4B,0x18,
+ 0x4C,0x1a,
+ 0x4D,0x1e,
+ 0x4E,0x0f,
+ 0x4F,0x09,
+ 0x50,0x07,
+ 0x51,0x05,
+ 0x52,0x04,
+ 0x53,0x03,
+ 0x54,0x02,
+ 0x55,0x01,
+
+ //0x60,0x7e,
+ //0x61,0xff,
+ //0x62,0xff,
+ //0x63,0xff,
+ //0x64,0xff,
+ //0x65,0x7e,
+ //0x66,0x50,
+ //0x67,0x50,
+ //0x68,0x50,
+ //0x69,0x50,
+ //0x6A,0x50,
+ //0x6B,0x00,
+ //0x6C,0x06,
+
+ 0xc0,0x11,
+ 0xc1,0x11,
+ 0xc2,0x11,
+ 0xc3,0x01,
+ 0xc4,0x0f,
+
+ 0x00,0x02,
+ 0x10,0xD0,
+ 0x11,0x11,
+ 0x13,0x7f,//80,
+ 0x14,0x7f,
+ 0x15,0xe0,
+ 0x16,0x90,
+ 0x17,0xd0,
+ 0x18,0x78,
+ 0x19,0x8c,
+ 0x1A,0x68,
+ 0x1B,0x8c,
+ 0x1C,0x68,
+ 0x1D,0x90,
+ 0x1E,0x74,
+ 0x20,0xF0,
+ 0x21,0x70,
+ 0x22,0xa4,
+ 0x23,0x10,
+ 0x25,0x20,
+ 0x26,0x05,
+ 0x27,0x63,
+ 0x28,0x63,
+
+ 0x29,0xa4,
+ 0x2A,0x8c,
+
+ 0x30,0x00,
+ 0x31,0x10,
+ 0x32,0x00,
+ 0x33,0x10,
+ 0x34,0x06,
+ 0x35,0x30,
+ 0x36,0x04,
+ 0x37,0xA0,
+ 0x40,0x01,
+ 0x41,0x04,
+ 0x42,0x08,
+ 0x43,0x10,
+ 0x44,0x13,
+ 0x45,0x6B,
+ 0x46,0x82,
+ 0x52,0x82,
+
+
+ 0x53,0x90, //a2
+ 0x54,0xa0, //b4
+ 0x55,0x90, //a4
+ 0x56,0xa0, //b2
+ 0x57,0xba,
+ 0x58,0x88, //93
+ 0x59,0xbe,
+ 0x5A,0x88, //A
+ 0xB4,0x05,
+ 0xB5,0x0F,
+ 0xB6,0x06,
+ 0xB7,0x06,
+ 0xB8,0x40,
+ 0xB9,0x10,
+ 0xBA,0x06,
+
+
+ 0x00,0x03,
+ 0x10,0xFF,
+ 0x11,0x1D,
+ 0x12,0x3D,
+ 0x13,0xFF,
+ 0x14,0x00,
+ 0x15,0xc0,
+
+ 0x30,0x88,
+ 0x31,0x14,
+ 0x32,0x08,
+ 0x33,0x06,
+ 0x34,0x04,
+ 0x35,0x04,
+ 0x36,0x44,
+ 0x37,0x66,
+ 0x38,0x01,
+ 0x39,0x10,
+ 0x3A,0x20,
+ 0x3B,0x20,
+ 0x3C,0x20,
+ 0x3D,0x04,
+ 0x3E,0x08,
+ 0x3F,0x10,
+ 0x40,0x10,
+ 0x41,0x20,
+ 0x42,0xff,
+ 0x43,0x80,
+ 0x44,0x40,
+ 0x45,0x06,
+ 0x46,0x40,
+ 0x47,0x1e,//30,
+ #if 0
+ 0x50,0x00,
+ 0x51,0x00,
+ 0x52,0x00,
+ 0x53,0x00,
+
+ 0x54,0x08,
+ 0x55,0x00,
+ 0x56,0x00,
+ 0x57,0x00,
+
+ 0x58,0x08,
+ 0x59,0x00,
+ 0x5a,0x00,
+ 0x5b,0x00,
+
+ 0x5c,0x14,
+ 0x5d,0x0f,
+ 0x5e,0x24,
+ 0x5f,0x12,
+ 0x63,0x80, //80
+ 0x64,0x82,
+ 0x65,0x82,
+ 0x66,0x80, //80
+
+ 0x6b,0x00,
+ 0x6c,0x00,
+ 0x6d,0x12,
+ 0x6e,0x45,
+ 0x6f,0x66,
+ 0x70,0x65,
+
+ 0x71,0x00,
+ 0x72,0x00,
+ 0x73,0x23,
+ 0x74,0x44,
+ 0x75,0x55,
+ 0x76,0x66,
+
+
+ 0x77,0x00,
+ 0x78,0x00,
+ 0x79,0x23,
+ 0x7a,0x44,
+ 0x7b,0x55,
+ 0x7c,0x66,
+
+ 0x7d,0x00,
+ 0x7e,0x00,
+ 0x7f,0x12,
+ 0x80,0x33,
+ 0x81,0x44,
+ 0x82,0x55,
+ #else
+ 0x50, 0x00,
+ 0x51, 0x00,
+ 0x52, 0x00,
+ 0x53, 0x00,
+ 0x54, 0x00,
+ 0x55, 0x00,
+ 0x56, 0x00,
+ 0x57, 0x00,
+ 0x58, 0x00,
+ 0x59, 0x00,
+ 0x5A, 0x00,
+ 0x5B, 0x00,
+ 0x5C, 0x00,
+ 0x5D, 0x00,
+ 0x5E, 0x00,
+ 0x5F, 0x00,
+ 0x60, 0x32,
+ 0x61, 0x2f,
+ 0x62, 0x67,
+ 0x63, 0x80,
+ 0x64, 0x80,
+ 0x65, 0x80,
+ 0x66, 0x80,
+ 0x6B, 0x0a,
+ 0x6C, 0xdc,
+ 0x6D, 0xba,
+ 0x6E, 0x98,
+ 0x6F, 0x75,
+ 0x70, 0x31,
+ 0x71, 0x0a,
+ 0x72, 0xdc,
+ 0x73, 0xba,
+ 0x74, 0x98,
+ 0x75, 0x75,
+ 0x76, 0x31,
+ 0x77, 0x0a,
+ 0x78, 0xdc,
+ 0x79, 0xba,
+ 0x7A, 0x98,
+ 0x7B, 0x75,
+ 0x7C, 0x31,
+ 0x7D, 0x0a,
+ 0x7E, 0xdc,
+ 0x7F, 0xba,
+ 0x80, 0x98,
+ 0x81, 0x75,
+ 0x82, 0x31,
+ #endif
+
+ 0x94,0x06,//03,
+ 0x95,0x40,//20,
+ 0x96,0x04,//02,
+ 0x97,0xb0,//58,
+
+ 0xA0,0x2F,
+ 0xA1,0xB7,
+ 0xA2,0xB7,
+ 0xA3,0xB7,
+ 0xA4,0x07,
+
+ 0xA5,0xFF,
+ 0xA6,0x06,
+ 0xA7,0xFF,
+ 0xA8,0x00,
+ 0xA9,0x00,
+ 0xAA,0x00,
+ 0xAB,0x00,
+ 0xAC,0x60,
+ 0xAD,0x18,
+ 0xAE,0x10,
+ 0xAF,0x20,
+ 0xB0,0x08,
+ 0xB1,0x00,
+
+ 0xc0,0x2f,
+ 0xc1,0x80,
+ 0xc2,0xb8,
+ 0xc3,0x08,
+ 0xc4,0xe0,
+ 0xc5,0x73,
+ 0xc6,0xed,
+ 0xc7,0xe8,
+ 0xc8,0xbf,
+ 0xc9,0x99,
+ 0xca,0x00,
+ 0xcb,0x00,
+ 0xcc,0x00,
+ 0xcd,0x00,
+ 0xce,0x00,
+
+
+ 0xd0,0x2f,
+ 0xd1,0x66, //90
+ 0xd2,0xd4,
+ 0xd3,0x06,
+ 0xd4,0xf0,
+ 0xd5,0x5a,
+ 0xd6,0xf6,
+ 0xd7,0xf9,
+ 0xd8,0xbf,
+ 0xd9,0x88,
+ 0xda,0x00,
+ 0xdb,0x00,
+ 0xdc,0x00,
+ 0xdd,0x00,
+ 0xde,0x00,
+
+
+ 0xe0,0x3f,
+ 0xe1,0x75,
+ 0xe2,0xd7,
+ 0xe3,0xf3,
+ 0xe4,0xe0,
+ 0xe5,0x89,
+ 0xe6,0xd6,
+ 0xe7,0xe5,
+ 0xe8,0x9f,
+ 0xe9,0xbb,
+ 0xea,0x34,
+ 0xeb,0x91,
+ 0xec,0x2d,
+ 0xed,0xe2,
+ 0xee,0x00,
+
+
+ 0x00,0x04,
+
+ 0x10, 0x00,
+ 0x11, 0x06,
+ 0x12, 0x0E,
+ 0x13, 0x1E,
+ 0x14, 0x3c,//40},
+ 0x15, 0x5b,//5e},
+ 0x16, 0x71,//74},
+ 0x17, 0x85,//87},
+ 0x18, 0x98,
+ 0x19, 0xa6,
+ 0x1A, 0xb3,
+ 0x1B, 0xca,
+ 0x1C, 0xde,
+ 0x1D, 0xf0,
+ 0x1E, 0xf8,
+ 0x1F, 0xFF,
+
+ 0x20, 0x00,
+ 0x21, 0x06,
+ 0x22, 0x0E,
+ 0x23, 0x1E,
+ 0x24, 0x3c,//40},
+ 0x25, 0x5b,//5e},
+ 0x26, 0x71,//74},
+ 0x27, 0x85,//87},
+ 0x28, 0x98,
+ 0x29, 0xa6,
+ 0x2A, 0xb3,
+ 0x2B, 0xca,
+ 0x2C, 0xde,
+ 0x2D, 0xf0,
+ 0x2E, 0xf8,
+ 0x2F, 0xFF,
+
+
+ 0x30, 0x00,
+ 0x31, 0x06,
+ 0x32, 0x0E,
+ 0x33, 0x1E,
+ 0x34, 0x3c,//40},
+ 0x35, 0x5b,//5e},
+ 0x36, 0x71,//74},
+ 0x37, 0x85,//87},
+ 0x38, 0x98,
+ 0x39, 0xa6,
+ 0x3A, 0xb3,
+ 0x3B, 0xca,
+ 0x3C, 0xde,
+ 0x3D, 0xf0,
+ 0x3E, 0xf8,
+ 0x3F, 0xFF,
+
+ 0x60,0x33,
+ 0x61,0x20,
+ 0x62,0xE4,
+ 0x63,0xFA,
+ 0x64,0x13,
+ 0x65,0x25,
+ 0x66,0x07,
+ 0x67,0xF5,
+ 0x68,0xEA,
+ 0x69,0x20,
+ 0x6A,0xC8,
+ 0x6B,0xC4,
+ 0x6C,0x84,
+ 0x6D,0x04,
+ 0x6E,0x0C,
+ 0x6F,0x00,
+
+ 0x70,0x00,
+ 0x71,0x00,
+ 0x72,0x00,
+ 0x73,0x00,
+ 0x74,0x00,
+ 0x75,0x00,
+ 0x77,0x00,
+ 0x78,0x00,
+ 0x79,0x00,
+ 0x7a,0x00,
+ 0x7b,0x00,
+ 0x7c,0x00,
+ 0x7d,0x00,
+ 0x7e,0x00,
+ 0x7f,0x00,
+
+
+
+ 0x80,0x22,
+ 0x81,0x10,
+ 0x82,0x18,
+ 0x83,0x06,
+ //0x84,0x18,
+ 0x85,0x06,
+ //0x86,0x06,
+ 0x87,0x06,
+ //0x88,0x18,
+ 0x89,0x06,
+ //0x8a,0x00,
+ //0x8b,0x24,
+ //0x8c,0x24,
+ //0X90,0x16,
+ 0x91,0x03,
+ 0x93,0x60,
+
+
+ 0x94,0x09,
+ 0x95,0x09,
+ 0x96,0x4C,
+ 0x97,0x66,
+ 0x9A,0xfa,
+ 0xA1,0x08,
+ 0xA2,0x10,
+ 0xA3,0x16,
+ 0xA4,0x20,
+ 0xA5,0x30,
+ 0xA6,0xa0,
+ //0xA7,0x06,
+ //0xA8,0x40,
+ 0xA9,0x20,
+ 0xAA,0x28,
+
+ 0xac,0xff,
+ 0xad,0x09,
+ 0xae,0x96,
+ 0xaf,0x18,
+ 0xb2,0x30,
+ 0xb3,0x11,
+
+ 0xb7,0x00,
+ 0xb8,0x00,
+ 0xb9,0x40, //y-contrast
+
+ //color all turation
+ 0xba,0x0f,
+ 0xbb,0x0f,
+ 0xbc,0x0f,//10,
+ 0xbd,0x0f,//10,
+ 0xbe,0x10,
+ 0xbf,0x10,
+ 0xc0,0x11,
+ 0xc1,0x11,
+ 0xc2,0x12,
+ 0xc3,0x12,
+ 0xc4,0x12,
+ 0xc5,0x12,
+ 0xcc,0x04,
+ 0xcd,0x40,
+ 0xce,0x00,
+
+
+#if 0
+ 0xe0,0x00,
+ 0xe1,0x00,
+ 0xe2,0x00,
+ 0xe3,0x00,
+ 0xe4,0x00,
+ 0xe5,0x00,
+ 0xe6,0x00,
+ 0xe7,0x00,
+ 0xe8,0x00,
+#else //´Ó1600x1200ÖнØÈ¡640x480
+ 0xe0,0xe0,
+ 0xe1,0x66,
+ 0xe2,0x66,
+ 0xe3,0x00,
+ 0xe4,0x50,
+ 0xe5,0x03,
+ 0xe6,0xc5,//00,
+ 0xe7,0x02,//00,
+ 0xe8,0x80,//00,
+#endif
+
+ 0x00,0x05,
+
+ 0x10,0x00,
+ 0x11,0x00,
+ 0x12,0x00,
+ 0x13,0x00,
+ 0x14,0x00,
+ 0x15,0x00,
+ 0x16,0x00,
+ 0x17,0x00,
+ 0x18,0x00,
+
+ 0x30,0x00,
+ 0x31,0x00,
+ 0x32,0x00,
+ 0x33,0x00,
+
+
+ 0x40,0x15,
+ 0x41,0x28,
+ 0x42,0x04,
+ 0x43,0x15,
+ 0x44,0x28,
+ 0x45,0x04,
+ 0x46,0x15,
+ 0x47,0x28,
+ 0x48,0x04,
+
+ 0x90,0x00,
+ 0x91,0x00,
+ 0x92,0x00,
+ 0x93,0x00,
+ 0x94,0x00,
+ 0x95,0x00,
+ 0x96,0x00,
+
+ 0x99,0x00,
+ 0xA0,0x00,
+ 0xA1,0x00,
+ 0xA2,0x00,
+ 0xA3,0x00,
+ 0xA4,0x00,
+ 0xA5,0x00,
+ 0xA6,0x00,
+ 0xA7,0x00,
+ 0xA8,0x00,
+ 0xA9,0x00,
+ 0xAA,0x00,
+ 0xAB,0x00,
+ 0xAC,0x00,
+ 0xAD,0x00,
+ 0xAE,0x00,
+ 0xAF,0x00,
+
+ 0xB0,0xf0,
+ 0xB1,0x00,
+ 0xB5,0x00,
+ 0xB8,0x00,
+ 0xB9,0x00,
+ 0xBa,0x00,
+ 0xBb,0x00,
+
+ 0xc0, 0x00,
+ 0xc1, 0x03,
+ 0xc2, 0x06,
+ 0xc3, 0x0d,
+ 0xc4, 0x1d,
+ 0xc5, 0x2e,
+ 0xc6, 0x3e,
+ 0xc7, 0x4f,
+ 0xc8, 0x60,
+ 0xc9, 0x70,
+ 0xca, 0x80,
+ 0xcb, 0xa0,
+ 0xcc, 0xc0,
+ 0xcd, 0xe0,
+ 0xce, 0xf0,
+ 0xcf, 0xff,
+
+ //0xe0,0x01,
+ //0xe1,0x03,
+ //0xe2,0x04,
+ //0xe3,0x0c,
+ //0xe4,0x11,
+ //0xe5,0x16,
+ //0xe6,0x1b,
+ //0xe7,0x24,
+ //0xe8,0x30,
+ 0x00,0x00,
+ 0x03,0xc5,
+ 0x00,0x01,
+ 0x10,0x80,
+ };
+
+// Exit
+uint8_t sid130b_default_regs_exit[] = {
+};
+
+#endif /* #ifndef __SIV130B_H_ */
diff --git a/drivers/media/video/wmt_v4l2/sensors/siv120d/siv120d.c b/drivers/media/video/wmt_v4l2/sensors/siv120d/siv120d.c
new file mode 100755
index 00000000..7a57ceeb
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/siv120d/siv120d.c
@@ -0,0 +1,332 @@
+
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "siv120d.h"
+
+#define sensor_write_array(sd, arr, sz) \
+ cmos_init_8bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write(sd->i2c_addr, reg, val)
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint8_t *regs;
+ size_t size;
+};
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+ CMOS_WIN_SIZE("QVGA", 320, 240, siv120d_320_240_regs),
+ CMOS_WIN_SIZE("VGA", 640, 480, siv120d_640_480_regs),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = siv120d_wb_auto;
+ size = ARRAY_SIZE(siv120d_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = siv120d_wb_incandescent;
+ size = ARRAY_SIZE(siv120d_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = siv120d_wb_daylight;
+ size = ARRAY_SIZE(siv120d_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = siv120d_wb_cloudy;
+ size = ARRAY_SIZE(siv120d_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = siv120d_wb_fluorescent;
+ size = ARRAY_SIZE(siv120d_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_scenemode(struct cmos_subdev *sd, enum v4l2_scene_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case SCENE_MODE_AUTO:
+ regs = siv120d_scene_mode_auto;
+ size = ARRAY_SIZE(siv120d_scene_mode_auto);
+ break;
+ case SCENE_MODE_NIGHTSHOT:
+ regs = siv120d_scene_mode_night;
+ size = ARRAY_SIZE(siv120d_scene_mode_night);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_exposure(struct cmos_subdev *sd, enum v4l2_exposure_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case -2:
+ regs = siv120d_exposure_neg6;
+ size = ARRAY_SIZE(siv120d_exposure_neg6);
+ break;
+ case -1:
+ regs = siv120d_exposure_neg3;
+ size = ARRAY_SIZE(siv120d_exposure_neg3);
+ break;
+ case 0:
+ regs = siv120d_exposure_zero;
+ size = ARRAY_SIZE(siv120d_exposure_zero);
+ break;
+ case 1:
+ regs = siv120d_exposure_pos3;
+ size = ARRAY_SIZE(siv120d_exposure_pos3);
+ break;
+ case 2:
+ regs = siv120d_exposure_pos6;
+ size = ARRAY_SIZE(siv120d_exposure_pos6);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data;
+
+ sensor_write(sd, 0x00, 0x00);
+
+ data = sensor_read(sd, 0x04);
+
+ switch (value) {
+ case 0:
+ data &= ~0x01;
+ break;
+ case 1:
+ data |= 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sensor_write(sd, 0x04, data);
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data;
+
+ sensor_write(sd, 0x00, 0x00);
+
+ data = sensor_read(sd, 0x04);
+
+ switch (value) {
+ case 0:
+ data &= ~0x02;
+ break;
+ case 1:
+ data |= 0x02;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sensor_write(sd, 0x04, data);
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ case V4L2_CID_EXPOSURE:
+ return v4l2_ctrl_query_fill(qc, -2, 2, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return sensor_s_scenemode(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return sensor_s_exposure(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ default:
+ case WMT_V4L2_CID_CAMERA_ANTIBANDING:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win) {
+ pr_err("%s, s_mbus_fmt failed, width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, win->regs, win->size);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return 0;
+}
+
+static int sensor_enum_framesizes(struct cmos_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ const struct cmos_win_size *win = &cmos_supported_win_sizes[index];
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ int data;
+
+ sensor_write(sd, 0x00, 0x0);
+
+ data = sensor_read(sd, 0x01);
+
+ return (data == sd->id) ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ int version;
+
+ if (sensor_identify(sd)) {
+ return -1;
+ }
+
+ version = sensor_read(sd, 0x02);
+ printk("version:0x%x\n",version);
+ sensor_write_array(sd, siv120d_default_regs_init_120d,
+ ARRAY_SIZE(siv120d_default_regs_init_120d));
+
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ return 0;
+}
+
+static struct cmos_subdev_ops siv120d_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+ .enum_framesizes = sensor_enum_framesizes,
+};
+
+struct cmos_subdev siv120d = {
+ .name = "siv120d",
+ .i2c_addr = 0x33,
+ .id = 0x12,
+ .max_width = 640,
+ .max_height = 480,
+ .ops = &siv120d_ops,
+};
+
+#if 0
+static int __init siv120d_init(void)
+{
+ return cmos_register_sudbdev(&siv120d);
+}
+
+static void __exit siv120d_exit(void)
+{
+ return cmos_unregister_subdev(&siv120d);
+}
+
+module_init(siv120d_init);
+module_exit(siv120d_exit);
+
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/siv120d/siv120d.h b/drivers/media/video/wmt_v4l2/sensors/siv120d/siv120d.h
new file mode 100755
index 00000000..f95e06c0
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/siv120d/siv120d.h
@@ -0,0 +1,485 @@
+#ifndef SIV120D_H
+#define SIV120D_H
+
+
+// Scene Mode
+uint8_t siv120d_scene_mode_auto[] = {
+ 0x00,0x01,
+ 0x11,0x10,
+ 0x40,0x40,
+};
+
+uint8_t siv120d_scene_mode_night[] = {
+ 0x00,0x01,
+ 0x11,0x14,
+ 0x40,0x50,
+};
+
+
+// White Balance
+uint8_t siv120d_wb_auto [] = {
+ 0x00,0x02,
+ //0x60,0x96,
+ //0x61,0x90,
+ //0x62,0x60,
+ 0x10,0xd3,
+};
+
+uint8_t siv120d_wb_incandescent [] = {
+ 0x00,0x02,
+ 0x10,0x00,
+ 0x60,0x98,
+ 0x61,0xC8,
+};
+
+uint8_t siv120d_wb_fluorescent [] = {
+ 0x00,0x02,
+ 0x10,0x00,
+ 0x60,0xAA,
+ 0x61,0xBE,
+};
+
+uint8_t siv120d_wb_daylight [] = {
+ 0x00,0x02,
+ 0x10,0x00,
+ 0x60,0xb0,
+ 0x61,0x74,
+};
+
+uint8_t siv120d_wb_cloudy [] = {
+ 0x00,0x02,
+ 0x10,0x00,
+ 0x60,0xb4,
+ 0x61,0x64,
+};
+
+uint8_t siv120d_wb_tungsten [] = {
+ 0x00,0x02,
+ 0x10,0x00,
+ 0x60,0x90,
+ 0x61,0xC0,
+};
+
+
+// Exposure
+uint8_t siv120d_exposure_neg6[] = {
+ 0x00,0x03,
+ 0xAB,0xa8,
+};
+
+uint8_t siv120d_exposure_neg3[] = {
+ 0x00,0x03,
+ 0xAB,0x00,
+};
+
+uint8_t siv120d_exposure_zero[] = {
+ 0x00,0x03,
+ 0xAB,0x18,
+};
+
+uint8_t siv120d_exposure_pos3[] = {
+ 0x00,0x03,
+ 0xAB,0x28,
+};
+
+uint8_t siv120d_exposure_pos6[] = {
+ 0x00,0x03,
+ 0xAB,0x38,
+};
+
+// Color Effect
+uint8_t siv120d_colorfx_none[] = {
+ 0x00,0x03,
+ 0xB6,0x00,
+};
+
+uint8_t siv120d_colorfx_bw[] = {
+ 0x00,0x03,
+ 0xB6,0x40,
+};
+
+uint8_t siv120d_colorfx_sepia[] = {
+ 0x00,0x03,
+ 0xB6,0x80,
+ 0xB7,0x60,
+ 0xB8,0xA0,
+};
+
+uint8_t siv120d_colorfx_negative[] = {
+ 0x00,0x03,
+ 0xB6,0x20,
+
+};
+
+uint8_t siv120d_colorfx_emboss[] = {
+ 0x00,0x03,
+ 0xB6,0x08,
+ 0xB7,0x00,
+ 0xB8,0x00,
+};
+
+uint8_t siv120d_colorfx_sketch[] = {
+ 0x00,0x03,
+ 0xB6,0x04,
+ 0xB7,0x00,
+ 0xB8,0x00,
+};
+
+uint8_t siv120d_colorfx_sky_blue[] = {
+ 0x00,0x03,
+ 0xB6,0xa0,
+ 0xB7,0xc0,
+ 0xB8,0x50,
+};
+
+uint8_t siv120d_colorfx_grass_green[] = {
+ 0x00,0x03,
+ 0xB6,0x80,
+ 0xB7,0x50,
+ 0xB8,0x50,
+};
+
+uint8_t siv120d_colorfx_skin_whiten[] = {
+};
+
+// Resolution
+uint8_t siv120d_320_240_regs[]={
+
+
+0x00,0x00,
+0x06,0x06,
+0x12,0x3d,
+0x00,0x03,
+0xc0,0x10,
+0xc1,0x00,
+0xc2,0x40,
+0xc3,0x00,
+0xc4,0xf0,
+
+ };
+
+uint8_t siv120d_640_480_regs[]={
+0x00,0x00,
+0x06,0x04,
+0x12,0x3d,
+0x00,0x03,
+0xc0,0x24,
+0xc1,0x00,
+0xc2,0x80,
+0xc3,0x00,
+0xc4,0xe0,
+
+};
+
+uint8_t siv120d_default_regs_init_120d[]={
+
+ 0x00, 0x00,
+ 0x04, 0x00,
+ 0x05, 0x03, //VGA Output
+ 0x07, 0x70,
+ 0x10, 0x34,
+ 0x11, 0x27,
+ 0x12, 0x21,//33}},
+ 0x16, 0xc6,
+ 0x17, 0xaa,
+
+ // SIV120D 50Hz - 24MHz
+ 0x20,0x10,
+ 0x21,0xc3,
+ 0x23,0x4d,
+ 0x00,0x01,
+ 0x34,0x60,
+
+ // SIV120D 50Hz - Still Mode
+ 0x00,0x00,
+ 0x24,0x10,
+ 0x25,0xC3,
+ 0x27,0x4D,
+
+ // Vendor recommended value ##Don't change##
+ 0x00,0x00,
+ 0x40,0x00,
+ 0x41,0x00,
+ 0x42,0x00,
+ 0x43,0x00,
+
+ // AE
+ 0x00, 0x01,
+ 0x11, 0x14, // 6fps at lowlux
+ 0x12, 0x78,//79}},//7d}},//80}},//78}},// D65 target 0x74
+ 0x13, 0x78,//79}},//7d}},//80}},//78}},// CWF target 0x74
+ 0x14, 0x78,//79}},//7d}},//80}},//78}},// A target , 0x74
+ 0x1E, 0x08,// ini gain, 0x08
+ 0x34, 0x1f,//0x1A //STST
+ 0x40, 0x36,//48}},//0x50 //0x38 // Max x6
+ /*
+ 0x41, 0x20, //AG_TOP1 0x28
+ 0x42, 0x20, //AG_TOP0 0x28
+ 0x43, 0x00, //AG_MIN1 0x08
+ 0x44, 0x00, //AG_MIN0 0x08
+ 0x45, 0x00, //G50_dec 0x09
+ 0x46, 0x0a, //G33_dec 0x17
+ 0x47, 0x10, //G25_dec 0x1d
+ 0x48, 0x13, //G20_dec 0x21
+ 0x49, 0x15, //G12_dec 0x23
+ 0x4a, 0x18, //G09_dec 0x24
+ 0x4b, 0x1a, //G06_dec 0x26
+ 0x4c, 0x1d, //G03_dec 0x27
+ 0x4d, 0x20, //G100_inc 0x27
+ 0x4e, 0x10, //G50_inc 0x1a
+ 0x4f, 0x0a, //G33_inc 0x14
+ 0x50, 0x08, //G25_inc 0x11
+ 0x51, 0x06, //G20_inc 0x0f
+ 0x52, 0x05, //G12_inc 0x0d
+ 0x53, 0x04, //G09_inc 0x0c
+ 0x54, 0x02,// //G06_inc 0x0a
+ 0x55, 0x01,// //G03_inc 0x09
+ */
+ //AWB
+ 0x00, 0x02,
+ 0x10, 0x00,
+ 0x11, 0xc0,
+ 0x12, 0x80,
+ 0x13, 0x80,
+ 0x14, 0x80,
+ 0x15, 0xfe, // R gain Top
+ 0x16, 0x80, // R gain bottom
+ 0x17, 0xcb, // B gain Top
+ 0x18, 0x70, // B gain bottom 0x80
+ 0x19, 0x94, // Cr top value 0x90
+ 0x1a, 0x6c, // Cr bottom value 0x70
+ 0x1b, 0x94, // Cb top value 0x90
+ 0x1c, 0x6c, // Cb bottom value 0x70
+ 0x1d, 0x94, // 0xa0
+ 0x1e, 0x6c, // 0x60
+ 0x20, 0xe8, // AWB luminous top value
+ 0x21, 0x30, // AWB luminous bottom value 0x20
+ 0x22, 0xa4,
+ 0x23, 0x20,
+ 0x25, 0x20,
+ 0x26, 0x0f,
+ 0x27, 0x10, // 60 BRTSRT
+ 0x28, 0xcb,//cb}}, // b4 BRTRGNTOP result 0xb2
+ 0x29, 0xc8, // b0 BRTRGNBOT
+ 0x2a, 0xa0, // 92 BRTBGNTOP result 0x90
+ 0x2b, 0x98, // 8e BRTBGNBOT
+ 0x2c, 0x88, // RGAINCONT
+ 0x2d, 0x88, // BGAINCONT
+
+ 0x30, 0x00,
+ 0x31, 0x10,
+ 0x32, 0x00,
+ 0x33, 0x10,
+ 0x34, 0x02,
+ 0x35, 0x76,
+ 0x36, 0x01,
+ 0x37, 0xd6,
+ 0x40, 0x01,
+ 0x41, 0x04,
+ 0x42, 0x08,
+ 0x43, 0x10,
+ 0x44, 0x12,
+ 0x45, 0x35,
+ 0x46, 0x64,
+ 0x50, 0x33,
+ 0x51, 0x20,
+ 0x52, 0xe5,
+ 0x53, 0xfb,
+ 0x54, 0x13,
+ 0x55, 0x26,
+ 0x56, 0x07,
+ 0x57, 0xf5,
+ 0x58, 0xea,
+ 0x59, 0x21,
+
+ 0x62, 0x88, // G gain
+
+ 0x63, 0xb3, // R D30 to D20
+ 0x64, 0xb8,
+ 0x65, 0xb3, // R D20 to D30
+ 0x66, 0xb8,
+
+ 0x67, 0xdd, // R D65 to D30
+ 0x68, 0xa0, // B D65 to D30
+ 0x69, 0xdd, // R D30 to D65
+ 0x6a, 0xa0, // B D30 to D65
+
+ //IDP
+ 0x00,0x03,
+ 0x10,0xFF, // IDP function enable
+ 0x11,0x1D, // PCLK polarity
+ 0x12,0x7d, // Y,Cb,Cr order sequence
+ 0x14,0x04, // don't change
+ 0x8c,0x10, //CMA select
+
+
+ // DPCNR
+ 0x17, 0x28, // DPCNRCTRL
+ 0x18, 0x04, // DPTHR
+ 0x19, 0x50, // C DP Number {{ Normal [7:6] Dark [5:4] }} | [3:0] DPTHRMIN
+ 0x1A, 0x50, // G DP Number {{ Normal [7:6] Dark [5:4] }} | [3:0] DPTHRMAX
+ 0x1B, 0x12, // DPTHRSLP{{ [7:4] @ Normal | [3:0] @ Dark }}
+ 0x1C, 0x00, // NRTHR
+ 0x1D, 0x0f,//08}}, // [5:0] NRTHRMIN 0x48
+ 0x1E, 0x0f,//08}}, // [5:0] NRTHRMAX 0x48
+ 0x1F, 0x3f,//28}}, // NRTHRSLP{{ [7:4] @ Normal | [3:0] @ Dark }}, 0x2f
+ 0x20, 0x04, // IllumiInfo STRTNOR
+ 0x21, 0x0f, // IllumiInfo STRTDRK
+
+ // Gamma
+ //0x30, 0x00 , //0x0
+ 0x31, 0x03 ,//0x080x31, 0x04 },//0x08 0x03
+ 0x32, 0x0b ,//0x100x32, 0x0b },//0x10 0x08
+ 0x33, 0x1e,//0x1B0x33, 0x24 },//0x1B 0x16
+ 0x34, 0x46,//0x370x34, 0x49 },//0x37 0x36
+ 0x35, 0x62,//0x4D0x35, 0x66 },//0x4D 0x56
+ 0x36, 0x78,//0x600x36, 0x7C },//0x60 0x77
+ 0x37, 0x8b,//0x720x37, 0x8D },//0x72 0x88
+ 0x38, 0x9B,//0x820x38, 0x9B },//0x82 0x9a
+ 0x39, 0xA8,//0x910x39, 0xAA },//0x91 0xA9
+ 0x3a, 0xb6,//0xA00x3a, 0xb6 },//0xA0 0xb5
+ 0x3b, 0xcc,//0xBA0x3b, 0xca },//0xBA 0xca
+ 0x3c, 0xdf,//0xD30x3c, 0xdc },//0xD3 0xdd
+ 0x3d, 0xf0,//0xEA0x3d, 0xef },//0xEA 0xee
+
+ // Sha0x3f, 0xFFding Register Setting }},
+ 0x40, 0x0a,
+ 0x41, 0xdc,
+ 0x42, 0x77,
+ 0x43, 0x66,
+ 0x44, 0x55,
+ 0x45, 0x44,
+ 0x46, 0x11, // left R gain[7:4], right R gain[3:0]
+ 0x47, 0x11, // top R gain[7:4], bottom R gain[3:0]
+ 0x48, 0x10, // left Gr gain[7:4], right Gr gain[3:0] 0x21
+ 0x49, 0x00, // top Gr gain[7:4], bottom Gr gain[3:0]
+ 0x4a, 0x00, // left Gb gain[7:4], right Gb gain[3:0] 0x02
+ 0x4b, 0x00, // top Gb gain[7:4], bottom Gb gain[3:0]
+ 0x4c, 0x00, // left B gain[7:4], right B gain[3:0]
+ 0x4d, 0x11, // top B gain[7:4], bottom B gain[3:0]
+ 0x4e, 0x04, // X-axis center high[3:2], Y-axis center high[1:0]
+ 0x4f, 0x98, // X-axis center low[7:0] 0x50
+ 0x50, 0xd8, // Y-axis center low[7:0] 0xf6
+ 0x51, 0x80, // Shading Center Gain
+ 0x52, 0x00, // Shading R Offset
+ 0x53, 0x00, // Shading Gr Offset
+ 0x54, 0x00, // Shading Gb Offset
+ 0x55, 0x00, // Shading B Offset
+
+ // Interpolation
+ 0x60, 0x57, //INT outdoor condition
+ 0x61, 0x57, //INT normal condition
+
+ 0x62, 0x77, //ASLPCTRL 7:4 GE, 3:0 YE
+ 0x63, 0x38, //YDTECTRL {{YE}} [7] fixed,
+ 0x64, 0x38, //GPEVCTRL {{GE}} [7] fixed,
+
+ 0x66, 0x0c, //SATHRMIN
+ 0x67, 0xff,
+ 0x68, 0x04, //SATHRSRT
+ 0x69, 0x08, //SATHRSLP
+
+ 0x6a, 0xaf, //PTDFATHR [7] fixed, [5:0] value
+ 0x6b, 0x58, //PTDLOTHR [6] fixed, [5:0] value
+ 0x6d, 0x88, //PTDLOTHR [6] fixed, [5:0] value
+
+ 0x8f, 0x00, //Cb/Cr coring
+
+ // Color matrix {{D65}} - Daylight
+
+
+ 0x71, 0x42,
+ 0x72, 0xbf,
+ 0x73, 0x00,
+ 0x74, 0x0f,
+ 0x75, 0x31,
+ 0x76, 0x00,
+ 0x77, 0x00,
+ 0x78, 0xbc,
+ 0x79, 0x44,
+
+ // Color matrix (D30) - CWF
+ 0x7a, 0x3a,
+ 0x7b, 0xcd,
+ 0x7c, 0xfa,
+ 0x7d, 0x12,
+ 0x7e, 0x2c,
+ 0x7f, 0x02,
+ 0x80, 0xf7,
+ 0x81, 0xc7,
+ 0x82, 0x42,
+
+ // Color matrix (D20) - A
+ 0x83, 0x3a,
+ 0x84, 0xcd,
+ 0x85, 0xfa,
+ 0x86, 0x12,
+ 0x87, 0x2c,
+ 0x88, 0x02,
+ 0x89, 0xf7,
+ 0x8a, 0xc7,
+ 0x8b, 0x42,
+
+ 0x8c, 0x10,
+ 0x8d, 0x04,
+ 0x8e, 0x02,
+ 0x8f, 0x00,
+
+ 0x90, 0x10,
+ 0x91, 0x10,
+ 0x92, 0x02,
+
+ 0x96, 0x02,
+
+ 0x9f, 0x0c,
+ 0xa0, 0x0c,
+ 0xa1, 0x22,
+
+ 0xa9, 0x12,
+ 0xaa, 0x12,
+ 0xab, 0x90,
+ 0xae, 0x40,
+ 0xaf, 0x86,
+ 0xb9, 0x10,
+ 0xba, 0x20,
+
+ // inverse color space conversion
+ 0xcc, 0x40,
+ 0xcd, 0x00,
+ 0xce, 0x58,
+ 0xcf, 0x40,
+ 0xd0, 0xea,
+ 0xd1, 0xd3,
+ 0xd2, 0x40,
+ 0xd3, 0x6f,
+ 0xd4, 0x00,
+
+ // ee/nr
+ 0xd9, 0x0b,
+ 0xda, 0x1f,
+ 0xdb, 0x05,
+ 0xdc, 0x08,
+ 0xdd, 0x3C,
+ 0xde, 0xF9, //NOIZCTRL
+
+ //window
+ 0xc0, 0x24,
+ 0xC1, 0x00,
+ 0xC2, 0x80,
+ 0xC3, 0x00,
+ 0xC4, 0xE0,
+ // memory speed
+ 0xe5, 0x15,
+ 0xe6, 0x20,
+ 0xe7, 0x04,
+
+ 0x00, 0x02,
+ 0x10, 0xd3,
+
+ //Sensor On
+ 0x00, 0x00,
+ 0x03, 0x05,
+
+};
+
+#endif \ No newline at end of file
diff --git a/drivers/media/video/wmt_v4l2/sensors/siv121d/siv121d-kyt-093-du.h b/drivers/media/video/wmt_v4l2/sensors/siv121d/siv121d-kyt-093-du.h
new file mode 100755
index 00000000..2d83463c
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/siv121d/siv121d-kyt-093-du.h
@@ -0,0 +1,1128 @@
+#ifndef SIV121D_H
+#define SIV121D_H
+
+
+// Scene Mode
+uint8_t siv121d_scene_mode_auto[] = {
+ 0x00,0x02,
+ 0x11,0x10,
+ 0x40,0x40,
+};
+
+uint8_t siv121d_scene_mode_night[] = {
+ 0x00,0x02,
+ 0x11,0x18,
+ 0x40,0x60,
+};
+
+
+// White Balance
+uint8_t siv121d_wb_auto [] = {
+ 0x00,0x03,
+ 0x10,0xd0,
+ //0x60,0xC5,
+ //0x61,0x95,
+};
+
+uint8_t siv121d_wb_incandescent [] = {
+ 0x00,0x03,
+ 0x10,0x00,
+ 0x60,0x80,
+ 0x61,0xE0,
+};
+
+uint8_t siv121d_wb_fluorescent [] = {
+ 0x00,0x03,
+ 0x10,0x00,
+ 0x60,0xB8,
+ 0x61,0xCC,
+};
+
+uint8_t siv121d_wb_daylight [] = {
+ 0x00,0x03,
+ 0x10,0x00,
+ 0x60,0xD8,
+ 0x61,0x90,
+};
+
+uint8_t siv121d_wb_cloudy [] = {
+ 0x00,0x03,
+ 0x10,0x00,
+ 0x60,0xB4,
+ 0x61,0x90,
+};
+
+uint8_t siv121d_wb_tungsten [] = {
+ 0x00,0x03,
+ 0x10,0x00,
+ 0x60,0x7B,
+ 0x61,0xa0,
+};
+
+
+// Exposure
+uint8_t siv121d_exposure_neg6[] = {
+ 0x00,0x04,
+ 0xAB,0xa8,
+};
+
+uint8_t siv121d_exposure_neg3[] = {
+ 0x00,0x04,
+ 0xAB,0x90,
+};
+
+uint8_t siv121d_exposure_zero[] = {
+ 0x00,0x04,
+ 0xAB,0x00,
+};
+
+uint8_t siv121d_exposure_pos3[] = {
+ 0x00,0x04,
+ 0xAB,0x18,
+};
+
+uint8_t siv121d_exposure_pos6[] = {
+ 0x00,0x04,
+ 0xAB,0x30,
+};
+
+// Color Effect
+uint8_t siv121d_colorfx_none[] = {
+ 0x00,0x04,
+ 0xB6,0x00,
+};
+
+uint8_t siv121d_colorfx_bw[] = {
+ 0x00,0x04,
+ 0xB6,0x40,
+};
+
+uint8_t siv121d_colorfx_sepia[] = {
+ 0x00,0x04,
+ 0xB6,0x80,
+ 0xB7,0x60,
+ 0xB8,0xA0,
+};
+
+uint8_t siv121d_colorfx_negative[] = {
+ 0x00,0x04,
+ 0xB6,0x20,
+
+};
+
+uint8_t siv121d_colorfx_emboss[] = {
+ 0x00,0x04,
+ 0xB6,0x08,
+ 0xB7,0x00,
+ 0xB8,0x00,
+};
+
+uint8_t siv121d_colorfx_sketch[] = {
+ 0x00,0x04,
+ 0xB6,0x04,
+ 0xB7,0x00,
+ 0xB8,0x00,
+};
+
+uint8_t siv121d_colorfx_sky_blue[] = {
+ 0x00,0x04,
+ 0xB6,0xa0,
+ 0xB7,0xc0,
+ 0xB8,0x50,
+};
+
+uint8_t siv121d_colorfx_grass_green[] = {
+ 0x00,0x04,
+ 0xB6,0x80,
+ 0xB7,0x50,
+ 0xB8,0x50,
+};
+
+uint8_t siv121d_colorfx_skin_whiten[] = {
+};
+
+uint8_t sensor_colorfx_vivid[] = {
+};
+
+// Brightness
+uint8_t sensor_brightness_neg4[] = {
+ 0x00,0x04,
+ 0xAB,0xC0,
+};
+
+uint8_t sensor_brightness_neg3[] = {
+ 0x00,0x04,
+ 0xAB,0xB0,
+};
+
+uint8_t sensor_brightness_neg2[] = {
+ 0x00,0x04,
+ 0xAB,0xA0,
+};
+
+uint8_t sensor_brightness_neg1[] = {
+ 0x00,0x04,
+ 0xAB,0x90,
+};
+
+uint8_t sensor_brightness_zero[] = {
+ 0x00,0x04,
+ 0xAB,0x00,
+};
+
+uint8_t sensor_brightness_pos1[] = {
+ 0x00,0x04,
+ 0xAB,0x10,
+};
+
+uint8_t sensor_brightness_pos2[] = {
+ 0x00,0x04,
+ 0xAB,0x20,
+};
+
+uint8_t sensor_brightness_pos3[] = {
+ 0x00,0x04,
+ 0xAB,0x30,
+};
+
+uint8_t sensor_brightness_pos4[] = {
+ 0x00,0x04,
+ 0xAB,0x40,
+};
+
+
+// Contrast
+uint8_t sensor_contrast_neg4[] = {
+ 0x00,0x04,
+ 0xA8,0x0D,
+
+};
+
+uint8_t sensor_contrast_neg3[] = {
+ 0x00,0x04,
+ 0xA8,0x0E,
+};
+
+uint8_t sensor_contrast_neg2[] = {
+ 0x00,0x04,
+ 0xA8,0x0F,
+};
+
+uint8_t sensor_contrast_neg1[] = {
+ 0x00,0x04,
+ 0xA8,0x10,
+};
+
+uint8_t sensor_contrast_zero[] = {
+ 0x00,0x04,
+ 0xA8,0x10,
+};
+
+uint8_t sensor_contrast_pos1[] = {
+ 0x00,0x04,
+ 0xA8,0x12,
+};
+
+uint8_t sensor_contrast_pos2[] = {
+ 0x00,0x04,
+ 0xA8,0x13,
+};
+
+uint8_t sensor_contrast_pos3[] = {
+ 0x00,0x04,
+ 0xA8,0x14,
+};
+
+uint8_t sensor_contrast_pos4[] = {
+ 0x00,0x04,
+ 0xA8,0x15,
+};
+
+// Saturation
+uint8_t sensor_saturation_neg4[] = {
+};
+
+uint8_t sensor_saturation_neg3[] = {
+};
+
+uint8_t sensor_saturation_neg2[] = {
+};
+
+uint8_t sensor_saturation_neg1[] = {
+};
+
+uint8_t sensor_saturation_zero[] = {
+};
+
+uint8_t sensor_saturation_pos1[] = {
+};
+
+uint8_t sensor_saturation_pos2[] = {
+};
+
+uint8_t sensor_saturation_pos3[] = {
+};
+
+uint8_t sensor_saturation_pos4[] = {
+};
+
+
+// Resolution
+uint8_t siv121d_320_240_regs[]={
+ 0x00,0x01,
+ 0x06,0x06,
+ 0x00,0x04,
+ 0xc0,0x10,
+ 0xc1,0x00,
+ 0xc2,0x40,
+ 0xc3,0x00,
+ 0xc4,0xf0,
+};
+
+uint8_t siv121d_640_480_regs[]={
+ 0x00,0x01,
+ 0x06,0x04,
+ 0x00,0x04,
+ 0xc0,0x24,
+ 0xc1,0x00,
+ 0xc2,0x80,
+ 0xc3,0x00,
+ 0xc4,0xe0,
+};
+
+uint8_t siv121d_default_regs_init_121d[]={
+#if 1
+ 0x00 ,0x00 ,
+ 0x00 ,0x01 ,
+ 0x03 ,0x0a ,
+
+ 0x00 ,0x01 ,
+ 0x03 ,0x0a ,
+
+ 0x00 ,0x00 ,
+ 0x03 ,0x04 ,
+ 0x10 ,0x85 , //85
+ 0x11 ,0x74 , //0x11 0x61 davis20120514
+
+ 0x00 ,0x01 ,
+ 0x04 ,0x00 , //00
+ 0x06 ,0x04 ,
+ 0x10 ,0x46 ,
+ 0x11 ,0x23 ,
+ 0x12 ,0x21 ,
+ 0x17 ,0x86 ,
+ 0x18 ,0x00 ,
+ 0x20 ,0x00 ,
+ 0x21 ,0x05 ,
+ 0x22 ,0x01 ,
+ 0x23 ,0x69 , //add
+ 0x40 ,0x0F ,
+ 0x41 ,0x90 ,
+ 0x42 ,0xd2 ,
+ 0x43 ,0x00 ,
+
+ // AE
+ 0x00 ,0x02 ,
+ 0x10 ,0x80 ,
+ 0x11 ,0x10 ,
+ 0x12 ,0x65 , // D65 target 60
+ 0x14 ,0x62 , // A target 5
+ 0x34 ,0x96 ,
+ 0x40 ,0x40 , // 40
+ 0x5f ,0x01 ,
+ 0x41 ,0x28 ,//20 ,
+ 0x42 ,0x28 ,//20 ,
+ 0x44 ,0x08 ,//00 ,
+ 0x45 ,0x08 ,//00 ,
+ 0x46 ,0x15 ,//0a ,
+ 0x47 ,0x1c ,//10 ,
+ 0x48 ,0x20 ,//13 ,
+ 0x49 ,0x21 ,//15 ,
+ 0x4a ,0x23 ,//18 ,
+ 0x4b ,0x24 ,//1a ,
+ 0x4c ,0x25 ,//1d ,
+ 0x4d ,0x28 ,//20 ,
+ 0x4e ,0x1c ,//10 ,
+ 0x4f ,0x15 ,//0a ,
+
+ 0x50 ,0x10 ,//08 ,
+ 0x51 ,0x10 ,//06 ,
+ 0x52 ,0x0e ,//05 ,
+ 0x53 ,0x0c ,//04 ,
+ 0x54 ,0x0b ,//02 ,
+ 0x55 ,0x0a ,//01 ,
+ 0x90 ,0x80 ,
+ 0x91 ,0x80 ,
+
+ // AWB
+ 0x00 ,0x03 ,
+ 0x10 ,0xd0 , //
+ 0x11 ,0xc0 ,//c0
+ 0x13 ,0x81 , //Cr target
+ 0x14 ,0x7d , //Cb target 7d
+ 0x15 ,0xc0 , // R gain Top e0
+ 0x16 ,0x8c , // R gain bottom 8c //
+ 0x17 ,0xe0 , // B gain Top e0
+ 0x18 ,0x70 , // B gain bottom 0x80 //
+ 0x19 ,0x8c , // Cr top value 8c //
+ 0x1a ,0x64 , // Cr bottom value 0x64 60 //YCbYCr
+ 0x1b ,0x98 , // Cb top value 0x98
+ 0x1c ,0x6c , // Cb bottom value 0x6c // 0x01
+ 0x1d ,0x94 , // 0xa0
+ 0x1e ,0x6c , // 0x60
+ 0x20 ,0xe8 , // AWB luminous top value
+ 0x21 ,0x30 , // AWB luminous bottom value 0x20
+ 0x22 ,0xb8 ,
+ 0x23 ,0x10 ,
+ 0x25 ,0x20 ,
+ 0x26 ,0x0f ,
+ 0x27 ,0x08 , // BRTSRT
+ 0x28 ,0x0f , // BRTEND
+ 0x29 ,0xba , // BRTRGNBOT
+ 0x2a ,0xa0 , // BRTBGNTOP
+
+ 0x40 ,0x01 ,
+ 0x41 ,0x04 ,
+ 0x42 ,0x08 ,
+ 0x43 ,0x10 ,
+ 0x44 ,0x12 , // 0x12
+ 0x45 ,0x35 , //35
+ 0x46 ,0xfc , // fc
+ 0x62 ,0x84 , //84 //
+ 0x63 ,0x90 , // R D30 to D20
+ 0x64 ,0xd0 , // B D30 to D20
+ 0x65 ,0x90 , // R D20 to D30
+ 0x66 ,0xd0 , // B D20 to D30
+
+ // IDP
+ 0x00 ,0x04 ,
+ 0x10 ,0xff ,
+ // 0x11 ,0x1d ,
+ 0x11 ,0x1d , //zhongwei changed
+ 0x12 ,0x3d ,//zhongwei++
+
+
+ // DPCBNR
+ 0x18 ,0xbe ,//fe , // DPCNRCTRL
+ 0x19 ,0x00 ,//04 , // DPCTHV
+ 0x1A ,0x00 ,//01 , // DPCTHVSLP
+ 0x1B ,0x00 ,//08 , // DPCTHVDIFSRT
+ 0x1C ,0x0f ,//08 , // DPCTHVDIFSLP
+ 0x1d ,0xFF , // DPCTHVMAX
+
+ 0x1E ,0x04 , // BNRTHV 0c
+ 0x1F ,0x04 , // BNRTHVSLPN 10
+ 0x20 ,0x10 , // BNRTHVSLPD
+ 0x21 ,0x00 , /// BNRNEICNT / 0x08
+ 0x22 ,0x10 , // STRTNOR // 0x03
+ 0x23 ,0x40 , // STRTDRK
+
+ // Gamma
+ 0x31 ,0x08 ,//0x080x31, 0x04 },//0x08 0x03
+ 0x32 ,0x12 ,//0x100x32, 0x0b },//0x10 0x08
+ 0x33 ,0x24 ,//0x1B0x33, 0x24 },//0x1B 0x16
+ 0x34 ,0x47 ,//0x370x34, 0x49 },//0x37 0x36
+ 0x35 ,0x64 ,//0x4D0x35, 0x66 },//0x4D 0x56
+ 0x36 ,0x7c ,//0x600x36, 0x7C },//0x60 0x77
+ 0x37 ,0x8f ,//0x720x37, 0x8D },//0x72 0x88
+ 0x38 ,0x9f ,//0x820x38, 0x9B },//0x82 0x9a
+ 0x39 ,0xad ,//0x910x39, 0xAA },//0x91 0xA9
+ 0x3a ,0xba ,//0xA00x3a, 0xb6 },//0xA0 0xb5
+ 0x3b ,0xcf ,//0xBA0x3b, 0xca },//0xBA 0xca
+ 0x3c ,0xe2 ,//0xD30x3c, 0xdc },//0xD3 0xdd
+ 0x3d ,0xf0 ,//0xEA0x3d, 0xef },//0xEA 0xee
+
+ // Shading Register Setting
+ 0x40 ,0x04 , //06
+ 0x41 ,0x44 ,
+ 0x42 ,0x43 ,
+ 0x43 ,0x20 ,
+ 0x44 ,0x11 , // left R gain[7:4], right R gain[3:0] 22
+ 0x45 ,0x11 , // top R gain[7:4], bottom R gain[3:0] 22
+ 0x46 ,0x00 , // left G gain[7:4], right G gain[3:0]
+ 0x47 ,0x00 , // top G gain[7:4], bottom G gain[3:0] 11
+ 0x48 ,0x00 , // left B gain[7:4], right B gain[3:0]
+ 0x49 ,0x00 , // top B gain[7:4], bottom B gain[3:0] //color matrix default
+ 0x4a ,0x04 , // X-axis center high[3:2], Y-axis center high[1:0]
+ 0x4b ,0x60 , // X-axis center low[7:0] 48
+ 0x4c ,0xe0 , // Y-axis center low[7:0] e8
+ 0x4d ,0x88 , // Shading Center Gain 80
+ 0x4e ,0x00 , // Shading R Offset
+ 0x4f ,0x00 , // Shading Gr Offset
+ 0x50 ,0x00 , // Shading B Offset
+
+
+ // Interpolation
+ 0x60 ,0x7f ,
+ 0x61 ,0x08 ,
+
+ // Color matrix (D65) - Daylight
+ 0x71 ,0x34 , //0x34 0x39
+ 0x72 ,0xCe , //0xCe 0xCc
+ 0x73 ,0xFe , //0xFe 0xFb
+ 0x74 ,0x13 , //0x13 0x10
+ 0x75 ,0x25 , //0x25 0x21
+ 0x76 ,0x08 , //0x08 0x0f
+ 0x77 ,0xec , //0xec 0xf8
+ 0x78 ,0xd1 , //0xd1 0xc0
+ 0x79 ,0x47 , //0x47 0x48
+
+ // Color matrix (D20) - A
+ 0x83 ,0x38 , //0x3c
+ 0x84 ,0xd1 , //0xc6
+ 0x85 ,0xf7 , //0xff
+ 0x86 ,0x12 , //0x12
+ 0x87 ,0x25 , //0x24
+ 0x88 ,0x09 , //0x0a
+ 0x89 ,0xe1 , //0xed
+ 0x8a ,0xc7 , //0xc2
+ 0x8b ,0x58 , //0x51
+
+ 0x8c ,0x10 , //CMA select
+
+ //G Edge
+ 0x90 ,0x20 , //Upper gain
+ 0x91 ,0x40 , //down gain
+ 0x92 ,0x44 , //[7:4] upper coring [3:0] down coring
+ 0x9a ,0x40 ,
+ 0x9b ,0x40 ,
+ 0x9c ,0x38 , //edge suppress start 30
+ 0x9d ,0x30 , //edge suppress slope
+ 0xa8 ,0x10 ,
+ 0xa9 ,0x13 ,
+ 0xaa ,0x13 ,
+ 0xb9 ,0x30 , // nightmode 38 at gain 0x48 5fps
+ 0xba ,0x44 , // nightmode 80 at gain 0x48 5fps
+ 0xde ,0x80 ,
+
+ 0xe5 ,0x15 ,
+ 0xe6 ,0x02 ,
+ 0xe7 ,0x04 ,
+
+ //Sensor On //Sensor On
+ 0x00 ,0x01 ,
+ 0x03 ,0x01 ,
+ 0x00 ,0x00 ,
+#else
+ 0x00,0x00,
+ 0x03,0x04,
+ 0x10,0x86,//8d}}, //85
+ 0x11,0x74,// f1}}, //0x11 0x61 davis20120514
+
+ 0x00,0x01,
+ 0x04,0x00, //00
+ 0x06,0x04,
+ 0x10,0x11,//46}},
+ 0x11,0x25,//23}},
+ 0x12,0x21,
+ 0x17,0x94,//86}},
+ 0x18,0x00,
+ 0x20,0x00,
+ 0x21,0x05,
+ 0x22,0x01,
+ 0x23,0x69, //add
+ 0x40,0x0F,
+ 0x41,0x90,
+ 0x42,0xd2,
+ 0x43,0x00,
+
+ // AE
+ 0x00,0x02,
+ 0x10,0x84,
+ 0x11,0x14,//0c}},//10}},
+ 0x12,0x68,//71,//70}}, // D65 target 60
+ 0x14,0x40,//68, // A target 5d
+ 0x34,0x96,
+ 0x40,0x38,//48}},//40}}, // 40
+ 0x44,0x08,//00}},
+
+
+ // AWB
+ 0x00,0x03,
+ 0x10,0xD0,//c3}}, //
+ 0x11,0xc1,//c0}},//c0
+ 0x12,0x80,
+ 0x13,0x80, //Cr target 0x7e
+ 0x14,0x80, //Cb target 7f
+ 0x15,0xe0,//c0}}, // R gain Top e0
+ 0x16,0x7C,//88}},//8c}}, // R gain bottom 8c //
+ 0x17,0xe0, // B gain Top e0
+ 0x18,0x80,//70}}, // B gain bottom 0x80 //
+ 0x19,0x8C,//90}}, // Cr top value 8c //
+ 0x1a,0x64, // Cr bottom value 0x64 60 //YCbYCr
+ 0x1b,0x94, // Cb top value 0x98
+ 0x1c,0x6c, // Cb bottom value 0x6c // 0x01
+ 0x1d,0x94, // 0xa0
+ 0x1e,0x6c, // 0x60
+ 0x20,0xe8, // AWB luminous top value
+ 0x21,0x30, // AWB luminous bottom value 0x20
+ 0x22,0xb8,
+ 0x23,0x10,
+ 0x25,0x08,//20}},
+ 0x26,0x0f,//0f}},
+ 0x27,0x60, // BRTSRT
+ 0x28,0x70, // BRTEND
+ 0x29,0xb7, // BRTRGNBOT
+ 0x2a,0xa3, // BRTBGNTOP
+
+ 0x40,0x01,
+ 0x41,0x03,//04}},
+ 0x42,0x08,
+ 0x43,0x10,
+ 0x44,0x13,//12}}, // 0x12
+ 0x45,0x6a,//35}}, //35
+ 0x46,0xca, // fc
+
+ 0x62,0x80,//84}}, //84 //
+
+ 0x63,0x90,//9c}}, // R D30 to D20
+ 0x64,0xD0,//d4}}, // B D30 to D20
+ 0x65,0x98,//a0}},//90}}, // R D20 to D30
+ 0x66,0xd0, // B D20 to D30
+
+ // IDP
+ 0x00,0x04,
+ 0x10,0x7f,//f,
+
+ 0x11,0x1d, // changed
+ 0x12,0x3d,//++
+ 0x13,0xfe,//++
+ 0x14,0x01,//++
+
+
+ // DPCBNR
+ 0x18,0xbf,//fe, // DPCNRCTRL
+ 0x19,0x00,//04, // DPCTHV
+ 0x1A,0x00,//01, // DPCTHVSLP
+ 0x1B,0x00,//08,//08}}, // DPCTHVDIFSRT
+
+
+
+ 0x1E,0x04, // BNRTHV 0c
+ 0x1F,0x08,//04}}, // BNRTHVSLPN 10
+ 0x20,0x20, // BNRTHVSLPD
+ 0x21,0x00, /// BNRNEICNT / 0x08
+ 0x22,0x08, // STRTNOR // 0x03
+ 0x23,0x38,//40}}, // STRTDRK
+ 0x24,0x00,
+
+ // Gamma
+
+ 0x31, 0x11,//03,//0x080x31, 0x04 },//0x08 0x03
+ 0x32, 0x21,//0b,//0x100x32, 0x0b },//0x10 0x08
+ 0x33, 0x37,//1e,//0x1B0x33, 0x24 },//0x1B 0x16
+ 0x34, 0x54,//46,//0x370x34, 0x49 },//0x37 0x36
+ 0x35, 0x6e,//62,//0x4D0x35, 0x66 },//0x4D 0x56
+ 0x36, 0x84,//0x600x36, 0x7C },//0x60 0x77
+ 0x37, 0x96,//0x720x37, 0x8D },//0x72 0x88
+ 0x38, 0xa7,//0x820x38, 0x9B },//0x82 0x9a
+ 0x39, 0xb7,//0x910x39, 0xAA },//0x91 0xA9
+ 0x3a, 0xc4,//0xA00x3a, 0xb6 },//0xA0 0xb5
+ 0x3b, 0xd9,//0xBA0x3b, 0xca },//0xBA 0xca
+ 0x3c, 0xea,//0xD30x3c, 0xdc },//0xD3 0xdd
+ 0x3d, 0xf9,//0xEA0x3d, 0xef },//0xEA 0xee
+
+ // Shading Register Setting
+ 0x40,0x55,//04}}, //06
+ 0x41,0x44,
+ 0x42,0x33,//43}},
+ 0x43,0x00,
+ 0x44,0x22, // left R gain[7:4], right R gain[3:0] 22
+ 0x45,0x22, // top R gain[7:4], bottom R gain[3:0] 22
+ 0x46,0x00, // left G gain[7:4], right G gain[3:0]
+ 0x47,0x03, // top G gain[7:4], bottom G gain[3:0] 11
+ 0x48,0x01, // left B gain[7:4], right B gain[3:0]
+ 0x49,0x11, // top B gain[7:4], bottom B gain[3:0] //color matrix default
+ 0x4a,0x05, // X-axis center high[3:2], Y-axis center high[1:0]
+ 0x4b,0x40, // X-axis center low[7:0] 48
+ 0x4c,0x10, // Y-axis center low[7:0] e8
+ 0x4d,0x80, // Shading Center Gain 80
+ 0x4e,0x00, // Shading R Offset
+ 0x4f,0x00, // Shading Gr Offset
+ 0x50,0x00, // Shading B Offset
+
+
+ // Interpolation
+ 0x60,0x7f,
+ 0x61,0x08,
+
+ // Color matrix (D65) - Daylight
+
+
+ 0x71,0x39, //0x34 0x39
+ 0x72,0xC8, //0xCe 0xCc
+ 0x73,0xff, //0xFe 0xFb
+ 0x74,0x13, //0x13 0x10
+ 0x75,0x25, //0x25 0x21
+ 0x76,0x08, //0x08 0x0f
+ 0x77,0xf8, //0xec 0xf8
+ 0x78,0xc0, //0xd1 0xc0
+ 0x79,0x48, //0x47 0x48
+
+ // Color matrix (D20) - A
+ 0x83,0x34, //0x3c
+ 0x84,0xd4, //0xc6
+ 0x85,0xf8, //0xff
+ 0x86,0x12, //0x12
+ 0x87,0x25, //0x24
+ 0x88,0x09, //0x0a
+ 0x89,0xf0, //0xed
+ 0x8a,0xbe, //0xc2
+ 0x8b,0x54, //0x51
+
+
+ 0x8c,0x10, //CMA select
+
+ //G Edge
+ 0x90,0x30, //Upper gain
+ 0x91,0x40, //down gain
+ 0x92,0x33,//22}}, //[7:4] upper coring [3:0] down coring
+ 0x9a,0x40,
+ 0x9b,0x40,
+ 0x9c,0x38, //edge suppress start 30
+ 0x9d,0x30, //edge suppress slope
+
+ 0x9f,0x26,
+ 0xa0,0x11,
+
+ 0xa8,0x11,//10}},
+ 0xa9,0x11,//10}},
+ 0xaa,0x11,
+
+ 0xbf,0x20,
+
+ 0xb9,0x28,//30}}, // nightmode 38 at gain 0x48 5fps
+ 0xba,0x41,//44}}, // nightmode 80 at gain 0x48 5fps
+ 0xde,0x80,
+
+ 0xe5,0x15,
+ 0xe6,0x02,
+ 0xe7,0x04,
+
+ //Sensor On //Sensor On
+ 0x00,0x01,
+ 0x03,0x01,
+
+ 0x00,0x00,
+#endif
+};
+
+uint8_t siv121d_default_regs_init_121du[] = {
+#if 0
+ 0x00 ,0x00 ,
+ 0x00 ,0x01 ,
+ 0x03 ,0x0a ,
+
+ 0x00 ,0x01 ,
+ 0x03 ,0x0a ,
+
+ 0x00 ,0x00 ,
+ 0x03 ,0x04 ,
+ 0x10 ,0x85 , //85
+ 0x11 ,0x74 , //0x11 0x61 davis20120514
+
+ 0x00 ,0x01 ,
+ 0x04 ,0x00 , //00
+ 0x06 ,0x04 ,
+ 0x10 ,0x46 ,
+ 0x11 ,0x23 ,
+ 0x12 ,0x21 ,
+ 0x17 ,0x86 ,
+ 0x18 ,0x00 ,
+ 0x20 ,0x00 ,
+ 0x21 ,0x05 ,
+ 0x22 ,0x01 ,
+ 0x23 ,0x69 , //add
+ 0x40 ,0x0F ,
+ 0x41 ,0x90 ,
+ 0x42 ,0xd2 ,
+ 0x43 ,0x00 ,
+
+ // AE
+ 0x00 ,0x02 ,
+ 0x10 ,0x80 ,
+ 0x11 ,0x10 ,
+ 0x12 ,0x65 , // D65 target 60
+ 0x14 ,0x62 , // A target 5
+ 0x34 ,0x96 ,
+ 0x40 ,0x40 , // 40
+ 0x5f ,0x01 ,
+ 0x41 ,0x28 ,//20 ,
+ 0x42 ,0x28 ,//20 ,
+ 0x44 ,0x08 ,//00 ,
+ 0x45 ,0x08 ,//00 ,
+ 0x46 ,0x15 ,//0a ,
+ 0x47 ,0x1c ,//10 ,
+ 0x48 ,0x20 ,//13 ,
+ 0x49 ,0x21 ,//15 ,
+ 0x4a ,0x23 ,//18 ,
+ 0x4b ,0x24 ,//1a ,
+ 0x4c ,0x25 ,//1d ,
+ 0x4d ,0x28 ,//20 ,
+ 0x4e ,0x1c ,//10 ,
+ 0x4f ,0x15 ,//0a ,
+
+ 0x50 ,0x10 ,//08 ,
+ 0x51 ,0x10 ,//06 ,
+ 0x52 ,0x0e ,//05 ,
+ 0x53 ,0x0c ,//04 ,
+ 0x54 ,0x0b ,//02 ,
+ 0x55 ,0x0a ,//01 ,
+ 0x90 ,0x80 ,
+ 0x91 ,0x80 ,
+
+ // AWB
+ 0x00 ,0x03 ,
+ 0x10 ,0xd0 , //
+ 0x11 ,0xc0 ,//c0
+ 0x13 ,0x81 , //Cr target
+ 0x14 ,0x7d , //Cb target 7d
+ 0x15 ,0xc0 , // R gain Top e0
+ 0x16 ,0x8c , // R gain bottom 8c //
+ 0x17 ,0xe0 , // B gain Top e0
+ 0x18 ,0x70 , // B gain bottom 0x80 //
+ 0x19 ,0x8c , // Cr top value 8c //
+ 0x1a ,0x64 , // Cr bottom value 0x64 60 //YCbYCr
+ 0x1b ,0x98 , // Cb top value 0x98
+ 0x1c ,0x6c , // Cb bottom value 0x6c // 0x01
+ 0x1d ,0x94 , // 0xa0
+ 0x1e ,0x6c , // 0x60
+ 0x20 ,0xe8 , // AWB luminous top value
+ 0x21 ,0x30 , // AWB luminous bottom value 0x20
+ 0x22 ,0xb8 ,
+ 0x23 ,0x10 ,
+ 0x25 ,0x20 ,
+ 0x26 ,0x0f ,
+ 0x27 ,0x08 , // BRTSRT
+ 0x28 ,0x0f , // BRTEND
+ 0x29 ,0xba , // BRTRGNBOT
+ 0x2a ,0xa0 , // BRTBGNTOP
+
+ 0x40 ,0x01 ,
+ 0x41 ,0x04 ,
+ 0x42 ,0x08 ,
+ 0x43 ,0x10 ,
+ 0x44 ,0x12 , // 0x12
+ 0x45 ,0x35 , //35
+ 0x46 ,0xfc , // fc
+ 0x62 ,0x84 , //84 //
+ 0x63 ,0x90 , // R D30 to D20
+ 0x64 ,0xd0 , // B D30 to D20
+ 0x65 ,0x90 , // R D20 to D30
+ 0x66 ,0xd0 , // B D20 to D30
+
+ // IDP
+ 0x00 ,0x04 ,
+ 0x10 ,0xff ,
+ // 0x11 ,0x1d ,
+ 0x11 ,0x1d , //zhongwei changed
+ 0x12 ,0x3d ,//zhongwei++
+
+
+ // DPCBNR
+ 0x18 ,0xbe ,//fe , // DPCNRCTRL
+ 0x19 ,0x00 ,//04 , // DPCTHV
+ 0x1A ,0x00 ,//01 , // DPCTHVSLP
+ 0x1B ,0x00 ,//08 , // DPCTHVDIFSRT
+ 0x1C ,0x0f ,//08 , // DPCTHVDIFSLP
+ 0x1d ,0xFF , // DPCTHVMAX
+
+ 0x1E ,0x04 , // BNRTHV 0c
+ 0x1F ,0x04 , // BNRTHVSLPN 10
+ 0x20 ,0x10 , // BNRTHVSLPD
+ 0x21 ,0x00 , /// BNRNEICNT / 0x08
+ 0x22 ,0x10 , // STRTNOR // 0x03
+ 0x23 ,0x40 , // STRTDRK
+
+ // Gamma
+ 0x31 ,0x08 ,//0x080x31, 0x04 },//0x08 0x03
+ 0x32 ,0x12 ,//0x100x32, 0x0b },//0x10 0x08
+ 0x33 ,0x24 ,//0x1B0x33, 0x24 },//0x1B 0x16
+ 0x34 ,0x47 ,//0x370x34, 0x49 },//0x37 0x36
+ 0x35 ,0x64 ,//0x4D0x35, 0x66 },//0x4D 0x56
+ 0x36 ,0x7c ,//0x600x36, 0x7C },//0x60 0x77
+ 0x37 ,0x8f ,//0x720x37, 0x8D },//0x72 0x88
+ 0x38 ,0x9f ,//0x820x38, 0x9B },//0x82 0x9a
+ 0x39 ,0xad ,//0x910x39, 0xAA },//0x91 0xA9
+ 0x3a ,0xba ,//0xA00x3a, 0xb6 },//0xA0 0xb5
+ 0x3b ,0xcf ,//0xBA0x3b, 0xca },//0xBA 0xca
+ 0x3c ,0xe2 ,//0xD30x3c, 0xdc },//0xD3 0xdd
+ 0x3d ,0xf0 ,//0xEA0x3d, 0xef },//0xEA 0xee
+
+ // Shading Register Setting
+ 0x40 ,0x04 , //06
+ 0x41 ,0x44 ,
+ 0x42 ,0x43 ,
+ 0x43 ,0x20 ,
+ 0x44 ,0x11 , // left R gain[7:4], right R gain[3:0] 22
+ 0x45 ,0x11 , // top R gain[7:4], bottom R gain[3:0] 22
+ 0x46 ,0x00 , // left G gain[7:4], right G gain[3:0]
+ 0x47 ,0x00 , // top G gain[7:4], bottom G gain[3:0] 11
+ 0x48 ,0x00 , // left B gain[7:4], right B gain[3:0]
+ 0x49 ,0x00 , // top B gain[7:4], bottom B gain[3:0] //color matrix default
+ 0x4a ,0x04 , // X-axis center high[3:2], Y-axis center high[1:0]
+ 0x4b ,0x60 , // X-axis center low[7:0] 48
+ 0x4c ,0xe0 , // Y-axis center low[7:0] e8
+ 0x4d ,0x88 , // Shading Center Gain 80
+ 0x4e ,0x00 , // Shading R Offset
+ 0x4f ,0x00 , // Shading Gr Offset
+ 0x50 ,0x00 , // Shading B Offset
+
+
+ // Interpolation
+ 0x60 ,0x7f ,
+ 0x61 ,0x08 ,
+
+ // Color matrix (D65) - Daylight
+ 0x71 ,0x34 , //0x34 0x39
+ 0x72 ,0xCe , //0xCe 0xCc
+ 0x73 ,0xFe , //0xFe 0xFb
+ 0x74 ,0x13 , //0x13 0x10
+ 0x75 ,0x25 , //0x25 0x21
+ 0x76 ,0x08 , //0x08 0x0f
+ 0x77 ,0xec , //0xec 0xf8
+ 0x78 ,0xd1 , //0xd1 0xc0
+ 0x79 ,0x47 , //0x47 0x48
+
+ // Color matrix (D20) - A
+ 0x83 ,0x38 , //0x3c
+ 0x84 ,0xd1 , //0xc6
+ 0x85 ,0xf7 , //0xff
+ 0x86 ,0x12 , //0x12
+ 0x87 ,0x25 , //0x24
+ 0x88 ,0x09 , //0x0a
+ 0x89 ,0xe1 , //0xed
+ 0x8a ,0xc7 , //0xc2
+ 0x8b ,0x58 , //0x51
+
+ 0x8c ,0x10 , //CMA select
+
+ //G Edge
+ 0x90 ,0x20 , //Upper gain
+ 0x91 ,0x40 , //down gain
+ 0x92 ,0x44 , //[7:4] upper coring [3:0] down coring
+ 0x9a ,0x40 ,
+ 0x9b ,0x40 ,
+ 0x9c ,0x38 , //edge suppress start 30
+ 0x9d ,0x30 , //edge suppress slope
+ 0xa8 ,0x10 ,
+ 0xa9 ,0x13 ,
+ 0xaa ,0x13 ,
+ 0xb9 ,0x30 , // nightmode 38 at gain 0x48 5fps
+ 0xba ,0x44 , // nightmode 80 at gain 0x48 5fps
+ 0xde ,0x80 ,
+
+ 0xe5 ,0x15 ,
+ 0xe6 ,0x02 ,
+ 0xe7 ,0x04 ,
+
+ //Sensor On //Sensor On
+ 0x00 ,0x01 ,
+ 0x03 ,0x01 ,
+ 0x00 ,0x00 ,
+#else
+ 0x00,0x00,
+ 0x03,0x04,
+ 0x10,0x86,//8d}}, //85
+ 0x11,0x74,// f1}}, //0x11 0x61 davis20120514
+
+ 0x00,0x01,
+ 0x04,0x00, //00
+ 0x06,0x04,
+ 0x10,0x11,//46}},
+ 0x11,0x25,//23}},
+ 0x12,0x21,
+ 0x17,0x94,//86}},
+ 0x18,0x00,
+ 0x20,0x00,
+ 0x21,0x05,
+ 0x22,0x01,
+ 0x23,0x69, //add
+ 0x40,0x0F,
+ 0x41,0x90,
+ 0x42,0xd2,
+ 0x43,0x00,
+
+ // AE
+ 0x00,0x02,
+ 0x10,0x84,
+ 0x11,0x14,//0c}},//10}},
+ 0x12,0x68,//71,//70}}, // D65 target 60
+ 0x14,0x40,//68, // A target 5d
+ 0x34,0x96,
+ 0x40,0x38,//48}},//40}}, // 40
+ 0x44,0x08,//00}},
+
+
+ // AWB
+ 0x00,0x03,
+ 0x10,0xD0,//c3}}, //
+ 0x11,0xc1,//c0}},//c0
+ 0x12,0x80,
+ 0x13,0x80, //Cr target 0x7e
+ 0x14,0x80, //Cb target 7f
+ 0x15,0xe0,//c0}}, // R gain Top e0
+ 0x16,0x7C,//88}},//8c}}, // R gain bottom 8c //
+ 0x17,0xe0, // B gain Top e0
+ 0x18,0x80,//70}}, // B gain bottom 0x80 //
+ 0x19,0x8C,//90}}, // Cr top value 8c //
+ 0x1a,0x64, // Cr bottom value 0x64 60 //YCbYCr
+ 0x1b,0x94, // Cb top value 0x98
+ 0x1c,0x6c, // Cb bottom value 0x6c // 0x01
+ 0x1d,0x94, // 0xa0
+ 0x1e,0x6c, // 0x60
+ 0x20,0xe8, // AWB luminous top value
+ 0x21,0x30, // AWB luminous bottom value 0x20
+ 0x22,0xb8,
+ 0x23,0x10,
+ 0x25,0x08,//20}},
+ 0x26,0x0f,//0f}},
+ 0x27,0x60, // BRTSRT
+ 0x28,0x70, // BRTEND
+ 0x29,0xb7, // BRTRGNBOT
+ 0x2a,0xa3, // BRTBGNTOP
+
+ 0x40,0x01,
+ 0x41,0x03,//04}},
+ 0x42,0x08,
+ 0x43,0x10,
+ 0x44,0x13,//12}}, // 0x12
+ 0x45,0x6a,//35}}, //35
+ 0x46,0xca, // fc
+
+ 0x62,0x80,//84}}, //84 //
+
+ 0x63,0x90,//9c}}, // R D30 to D20
+ 0x64,0xD0,//d4}}, // B D30 to D20
+ 0x65,0x98,//a0}},//90}}, // R D20 to D30
+ 0x66,0xd0, // B D20 to D30
+
+ // IDP
+ 0x00,0x04,
+ 0x10,0x7f,//f,
+
+ 0x11,0x1d, // changed
+ 0x12,0x3d,//++
+ 0x13,0xfe,//++
+ 0x14,0x01,//++
+
+
+ // DPCBNR
+ 0x18,0xbf,//fe, // DPCNRCTRL
+ 0x19,0x00,//04, // DPCTHV
+ 0x1A,0x00,//01, // DPCTHVSLP
+ 0x1B,0x00,//08,//08}}, // DPCTHVDIFSRT
+
+
+
+ 0x1E,0x04, // BNRTHV 0c
+ 0x1F,0x08,//04}}, // BNRTHVSLPN 10
+ 0x20,0x20, // BNRTHVSLPD
+ 0x21,0x00, /// BNRNEICNT / 0x08
+ 0x22,0x08, // STRTNOR // 0x03
+ 0x23,0x38,//40}}, // STRTDRK
+ 0x24,0x00,
+
+ // Gamma
+
+ 0x31, 0x11,//03,//0x080x31, 0x04 },//0x08 0x03
+ 0x32, 0x21,//0b,//0x100x32, 0x0b },//0x10 0x08
+ 0x33, 0x37,//1e,//0x1B0x33, 0x24 },//0x1B 0x16
+ 0x34, 0x54,//46,//0x370x34, 0x49 },//0x37 0x36
+ 0x35, 0x6e,//62,//0x4D0x35, 0x66 },//0x4D 0x56
+ 0x36, 0x84,//0x600x36, 0x7C },//0x60 0x77
+ 0x37, 0x96,//0x720x37, 0x8D },//0x72 0x88
+ 0x38, 0xa7,//0x820x38, 0x9B },//0x82 0x9a
+ 0x39, 0xb7,//0x910x39, 0xAA },//0x91 0xA9
+ 0x3a, 0xc4,//0xA00x3a, 0xb6 },//0xA0 0xb5
+ 0x3b, 0xd9,//0xBA0x3b, 0xca },//0xBA 0xca
+ 0x3c, 0xea,//0xD30x3c, 0xdc },//0xD3 0xdd
+ 0x3d, 0xf9,//0xEA0x3d, 0xef },//0xEA 0xee
+
+ // Shading Register Setting
+ 0x40,0x55,//04}}, //06
+ 0x41,0x44,
+ 0x42,0x33,//43}},
+ 0x43,0x00,
+ 0x44,0x22, // left R gain[7:4], right R gain[3:0] 22
+ 0x45,0x22, // top R gain[7:4], bottom R gain[3:0] 22
+ 0x46,0x00, // left G gain[7:4], right G gain[3:0]
+ 0x47,0x03, // top G gain[7:4], bottom G gain[3:0] 11
+ 0x48,0x01, // left B gain[7:4], right B gain[3:0]
+ 0x49,0x11, // top B gain[7:4], bottom B gain[3:0] //color matrix default
+ 0x4a,0x05, // X-axis center high[3:2], Y-axis center high[1:0]
+ 0x4b,0x40, // X-axis center low[7:0] 48
+ 0x4c,0x10, // Y-axis center low[7:0] e8
+ 0x4d,0x80, // Shading Center Gain 80
+ 0x4e,0x00, // Shading R Offset
+ 0x4f,0x00, // Shading Gr Offset
+ 0x50,0x00, // Shading B Offset
+
+
+ // Interpolation
+ 0x60,0x7f,
+ 0x61,0x08,
+
+ // Color matrix (D65) - Daylight
+
+
+ 0x71,0x39, //0x34 0x39
+ 0x72,0xC8, //0xCe 0xCc
+ 0x73,0xff, //0xFe 0xFb
+ 0x74,0x13, //0x13 0x10
+ 0x75,0x25, //0x25 0x21
+ 0x76,0x08, //0x08 0x0f
+ 0x77,0xf8, //0xec 0xf8
+ 0x78,0xc0, //0xd1 0xc0
+ 0x79,0x48, //0x47 0x48
+
+ // Color matrix (D20) - A
+ 0x83,0x34, //0x3c
+ 0x84,0xd4, //0xc6
+ 0x85,0xf8, //0xff
+ 0x86,0x12, //0x12
+ 0x87,0x25, //0x24
+ 0x88,0x09, //0x0a
+ 0x89,0xf0, //0xed
+ 0x8a,0xbe, //0xc2
+ 0x8b,0x54, //0x51
+
+
+ 0x8c,0x10, //CMA select
+
+ //G Edge
+ 0x90,0x30, //Upper gain
+ 0x91,0x40, //down gain
+ 0x92,0x33,//22}}, //[7:4] upper coring [3:0] down coring
+ 0x9a,0x40,
+ 0x9b,0x40,
+ 0x9c,0x38, //edge suppress start 30
+ 0x9d,0x30, //edge suppress slope
+
+ 0x9f,0x26,
+ 0xa0,0x11,
+
+ 0xa8,0x11,//10}},
+ 0xa9,0x11,//10}},
+ 0xaa,0x11,
+
+ 0xbf,0x20,
+
+ 0xb9,0x28,//30}}, // nightmode 38 at gain 0x48 5fps
+ 0xba,0x41,//44}}, // nightmode 80 at gain 0x48 5fps
+ 0xde,0x80,
+
+ 0xe5,0x15,
+ 0xe6,0x02,
+ 0xe7,0x04,
+
+ //Sensor On //Sensor On
+ 0x00,0x01,
+ 0x03,0x01,
+
+ 0x00,0x00,
+#endif
+};
+
+#endif
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/siv121d/siv121d.c b/drivers/media/video/wmt_v4l2/sensors/siv121d/siv121d.c
new file mode 100755
index 00000000..55c80db7
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/siv121d/siv121d.c
@@ -0,0 +1,337 @@
+
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "siv121d.h"
+
+#define sensor_write_array(sd, arr, sz) \
+ cmos_init_8bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write(sd->i2c_addr, reg, val)
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint8_t *regs;
+ size_t size;
+};
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+ CMOS_WIN_SIZE("QVGA", 320, 240, siv121d_320_240_regs),
+ CMOS_WIN_SIZE("VGA", 640, 480, siv121d_640_480_regs),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = siv121d_wb_auto;
+ size = ARRAY_SIZE(siv121d_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = siv121d_wb_incandescent;
+ size = ARRAY_SIZE(siv121d_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = siv121d_wb_daylight;
+ size = ARRAY_SIZE(siv121d_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = siv121d_wb_cloudy;
+ size = ARRAY_SIZE(siv121d_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = siv121d_wb_fluorescent;
+ size = ARRAY_SIZE(siv121d_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_scenemode(struct cmos_subdev *sd, enum v4l2_scene_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case SCENE_MODE_AUTO:
+ regs = siv121d_scene_mode_auto;
+ size = ARRAY_SIZE(siv121d_scene_mode_auto);
+ break;
+ case SCENE_MODE_NIGHTSHOT:
+ regs = siv121d_scene_mode_night;
+ size = ARRAY_SIZE(siv121d_scene_mode_night);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_exposure(struct cmos_subdev *sd, enum v4l2_exposure_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case -2:
+ regs = siv121d_exposure_neg6;
+ size = ARRAY_SIZE(siv121d_exposure_neg6);
+ break;
+ case -1:
+ regs = siv121d_exposure_neg3;
+ size = ARRAY_SIZE(siv121d_exposure_neg3);
+ break;
+ case 0:
+ regs = siv121d_exposure_zero;
+ size = ARRAY_SIZE(siv121d_exposure_zero);
+ break;
+ case 1:
+ regs = siv121d_exposure_pos3;
+ size = ARRAY_SIZE(siv121d_exposure_pos3);
+ break;
+ case 2:
+ regs = siv121d_exposure_pos6;
+ size = ARRAY_SIZE(siv121d_exposure_pos6);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data;
+
+ sensor_write(sd, 0x00, 0x01);
+
+ data = sensor_read(sd, 0x04);
+
+ switch (value) {
+ case 0:
+ data &= ~0x01;
+ break;
+ case 1:
+ data |= 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sensor_write(sd, 0x04, data);
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data;
+
+ sensor_write(sd, 0x00, 0x01);
+
+ data = sensor_read(sd, 0x04);
+
+ switch (value) {
+ case 0:
+ data &= ~0x02;
+ break;
+ case 1:
+ data |= 0x02;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sensor_write(sd, 0x04, data);
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ case V4L2_CID_EXPOSURE:
+ return v4l2_ctrl_query_fill(qc, -2, 2, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return sensor_s_scenemode(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return sensor_s_exposure(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ default:
+ case WMT_V4L2_CID_CAMERA_ANTIBANDING:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win) {
+ pr_err("%s, s_mbus_fmt failed, width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, win->regs, win->size);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return 0;
+}
+
+static int sensor_enum_framesizes(struct cmos_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ const struct cmos_win_size *win = &cmos_supported_win_sizes[index];
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ int data;
+
+ sensor_write(sd, 0x00, 0x0);
+
+ data = sensor_read(sd, 0x01);
+
+ return (data == sd->id) ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ int version;
+
+ if (sensor_identify(sd)) {
+ return -1;
+ }
+
+ version = sensor_read(sd, 0x02);
+ printk("version:0x%x\n",version);
+ if (version == 0x10) {
+ sensor_write_array(sd, siv121d_default_regs_init_121d,
+ ARRAY_SIZE(siv121d_default_regs_init_121d));
+ } else if (version == 0x15) {
+ sensor_write_array(sd, siv121d_default_regs_init_121du,
+ ARRAY_SIZE(siv121d_default_regs_init_121du));
+
+ }
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ return 0;
+}
+
+static struct cmos_subdev_ops siv121d_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+ .enum_framesizes = sensor_enum_framesizes,
+};
+
+struct cmos_subdev siv121d = {
+ .name = "siv121d",
+ .i2c_addr = 0x33,
+ .id = 0xde,
+ .max_width = 640,
+ .max_height = 480,
+ .ops = &siv121d_ops,
+};
+
+#if 0
+static int __init siv121d_init(void)
+{
+ return cmos_register_sudbdev(&siv121d);
+}
+
+static void __exit siv121d_exit(void)
+{
+ return cmos_unregister_subdev(&siv121d);
+}
+
+module_init(siv121d_init);
+module_exit(siv121d_exit);
+
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/siv121d/siv121d.h b/drivers/media/video/wmt_v4l2/sensors/siv121d/siv121d.h
new file mode 100755
index 00000000..eb7a8e72
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/siv121d/siv121d.h
@@ -0,0 +1,1128 @@
+#ifndef SIV121D_H
+#define SIV121D_H
+
+
+// Scene Mode
+uint8_t siv121d_scene_mode_auto[] = {
+ 0x00,0x02,
+ 0x11,0x10,
+ 0x40,0x40,
+};
+
+uint8_t siv121d_scene_mode_night[] = {
+ 0x00,0x02,
+ 0x11,0x18,
+ 0x40,0x60,
+};
+
+
+// White Balance
+uint8_t siv121d_wb_auto [] = {
+ 0x00,0x03,
+ 0x10,0xd0,
+ //0x60,0xC5,
+ //0x61,0x95,
+};
+
+uint8_t siv121d_wb_incandescent [] = {
+ 0x00,0x03,
+ 0x10,0x00,
+ 0x60,0x80,
+ 0x61,0xE0,
+};
+
+uint8_t siv121d_wb_fluorescent [] = {
+ 0x00,0x03,
+ 0x10,0x00,
+ 0x60,0xB8,
+ 0x61,0xCC,
+};
+
+uint8_t siv121d_wb_daylight [] = {
+ 0x00,0x03,
+ 0x10,0x00,
+ 0x60,0xD8,
+ 0x61,0x90,
+};
+
+uint8_t siv121d_wb_cloudy [] = {
+ 0x00,0x03,
+ 0x10,0x00,
+ 0x60,0xB4,
+ 0x61,0x90,
+};
+
+uint8_t siv121d_wb_tungsten [] = {
+ 0x00,0x03,
+ 0x10,0x00,
+ 0x60,0x7B,
+ 0x61,0xa0,
+};
+
+
+// Exposure
+uint8_t siv121d_exposure_neg6[] = {
+ 0x00,0x04,
+ 0xAB,0xa8,
+};
+
+uint8_t siv121d_exposure_neg3[] = {
+ 0x00,0x04,
+ 0xAB,0x90,
+};
+
+uint8_t siv121d_exposure_zero[] = {
+ 0x00,0x04,
+ 0xAB,0x00,
+};
+
+uint8_t siv121d_exposure_pos3[] = {
+ 0x00,0x04,
+ 0xAB,0x18,
+};
+
+uint8_t siv121d_exposure_pos6[] = {
+ 0x00,0x04,
+ 0xAB,0x30,
+};
+
+// Color Effect
+uint8_t siv121d_colorfx_none[] = {
+ 0x00,0x04,
+ 0xB6,0x00,
+};
+
+uint8_t siv121d_colorfx_bw[] = {
+ 0x00,0x04,
+ 0xB6,0x40,
+};
+
+uint8_t siv121d_colorfx_sepia[] = {
+ 0x00,0x04,
+ 0xB6,0x80,
+ 0xB7,0x60,
+ 0xB8,0xA0,
+};
+
+uint8_t siv121d_colorfx_negative[] = {
+ 0x00,0x04,
+ 0xB6,0x20,
+
+};
+
+uint8_t siv121d_colorfx_emboss[] = {
+ 0x00,0x04,
+ 0xB6,0x08,
+ 0xB7,0x00,
+ 0xB8,0x00,
+};
+
+uint8_t siv121d_colorfx_sketch[] = {
+ 0x00,0x04,
+ 0xB6,0x04,
+ 0xB7,0x00,
+ 0xB8,0x00,
+};
+
+uint8_t siv121d_colorfx_sky_blue[] = {
+ 0x00,0x04,
+ 0xB6,0xa0,
+ 0xB7,0xc0,
+ 0xB8,0x50,
+};
+
+uint8_t siv121d_colorfx_grass_green[] = {
+ 0x00,0x04,
+ 0xB6,0x80,
+ 0xB7,0x50,
+ 0xB8,0x50,
+};
+
+uint8_t siv121d_colorfx_skin_whiten[] = {
+};
+
+uint8_t sensor_colorfx_vivid[] = {
+};
+
+// Brightness
+uint8_t sensor_brightness_neg4[] = {
+ 0x00,0x04,
+ 0xAB,0xC0,
+};
+
+uint8_t sensor_brightness_neg3[] = {
+ 0x00,0x04,
+ 0xAB,0xB0,
+};
+
+uint8_t sensor_brightness_neg2[] = {
+ 0x00,0x04,
+ 0xAB,0xA0,
+};
+
+uint8_t sensor_brightness_neg1[] = {
+ 0x00,0x04,
+ 0xAB,0x90,
+};
+
+uint8_t sensor_brightness_zero[] = {
+ 0x00,0x04,
+ 0xAB,0x00,
+};
+
+uint8_t sensor_brightness_pos1[] = {
+ 0x00,0x04,
+ 0xAB,0x10,
+};
+
+uint8_t sensor_brightness_pos2[] = {
+ 0x00,0x04,
+ 0xAB,0x20,
+};
+
+uint8_t sensor_brightness_pos3[] = {
+ 0x00,0x04,
+ 0xAB,0x30,
+};
+
+uint8_t sensor_brightness_pos4[] = {
+ 0x00,0x04,
+ 0xAB,0x40,
+};
+
+
+// Contrast
+uint8_t sensor_contrast_neg4[] = {
+ 0x00,0x04,
+ 0xA8,0x0D,
+
+};
+
+uint8_t sensor_contrast_neg3[] = {
+ 0x00,0x04,
+ 0xA8,0x0E,
+};
+
+uint8_t sensor_contrast_neg2[] = {
+ 0x00,0x04,
+ 0xA8,0x0F,
+};
+
+uint8_t sensor_contrast_neg1[] = {
+ 0x00,0x04,
+ 0xA8,0x10,
+};
+
+uint8_t sensor_contrast_zero[] = {
+ 0x00,0x04,
+ 0xA8,0x10,
+};
+
+uint8_t sensor_contrast_pos1[] = {
+ 0x00,0x04,
+ 0xA8,0x12,
+};
+
+uint8_t sensor_contrast_pos2[] = {
+ 0x00,0x04,
+ 0xA8,0x13,
+};
+
+uint8_t sensor_contrast_pos3[] = {
+ 0x00,0x04,
+ 0xA8,0x14,
+};
+
+uint8_t sensor_contrast_pos4[] = {
+ 0x00,0x04,
+ 0xA8,0x15,
+};
+
+// Saturation
+uint8_t sensor_saturation_neg4[] = {
+};
+
+uint8_t sensor_saturation_neg3[] = {
+};
+
+uint8_t sensor_saturation_neg2[] = {
+};
+
+uint8_t sensor_saturation_neg1[] = {
+};
+
+uint8_t sensor_saturation_zero[] = {
+};
+
+uint8_t sensor_saturation_pos1[] = {
+};
+
+uint8_t sensor_saturation_pos2[] = {
+};
+
+uint8_t sensor_saturation_pos3[] = {
+};
+
+uint8_t sensor_saturation_pos4[] = {
+};
+
+
+// Resolution
+uint8_t siv121d_320_240_regs[]={
+ 0x00,0x01,
+ 0x06,0x06,
+ 0x00,0x04,
+ 0xc0,0x10,
+ 0xc1,0x00,
+ 0xc2,0x40,
+ 0xc3,0x00,
+ 0xc4,0xf0,
+};
+
+uint8_t siv121d_640_480_regs[]={
+ 0x00,0x01,
+ 0x06,0x04,
+ 0x00,0x04,
+ 0xc0,0x24,
+ 0xc1,0x00,
+ 0xc2,0x80,
+ 0xc3,0x00,
+ 0xc4,0xe0,
+};
+
+uint8_t siv121d_default_regs_init_121d[]={
+#if 1
+ 0x00 ,0x00 ,
+ 0x00 ,0x01 ,
+ 0x03 ,0x0a ,
+
+ 0x00 ,0x01 ,
+ 0x03 ,0x0a ,
+
+ 0x00 ,0x00 ,
+ 0x03 ,0x04 ,
+ 0x10 ,0x85 , //85
+ 0x11 ,0x74 , //0x11 0x61 davis20120514
+
+ 0x00 ,0x01 ,
+ 0x04 ,0x00 , //00
+ 0x06 ,0x04 ,
+ 0x10 ,0x46 ,
+ 0x11 ,0x23 ,
+ 0x12 ,0x21 ,
+ 0x17 ,0x86 ,
+ 0x18 ,0x00 ,
+ 0x20 ,0x00 ,
+ 0x21 ,0x05 ,
+ 0x22 ,0x01 ,
+ 0x23 ,0x69 , //add
+ 0x40 ,0x0F ,
+ 0x41 ,0x90 ,
+ 0x42 ,0xd2 ,
+ 0x43 ,0x00 ,
+
+ // AE
+ 0x00 ,0x02 ,
+ 0x10 ,0x80 ,
+ 0x11 ,0x10 ,
+ 0x12 ,0x65 , // D65 target 60
+ 0x14 ,0x62 , // A target 5
+ 0x34 ,0x96 ,
+ 0x40 ,0x40 , // 40
+ 0x5f ,0x01 ,
+ 0x41 ,0x28 ,//20 ,
+ 0x42 ,0x28 ,//20 ,
+ 0x44 ,0x08 ,//00 ,
+ 0x45 ,0x08 ,//00 ,
+ 0x46 ,0x15 ,//0a ,
+ 0x47 ,0x1c ,//10 ,
+ 0x48 ,0x20 ,//13 ,
+ 0x49 ,0x21 ,//15 ,
+ 0x4a ,0x23 ,//18 ,
+ 0x4b ,0x24 ,//1a ,
+ 0x4c ,0x25 ,//1d ,
+ 0x4d ,0x28 ,//20 ,
+ 0x4e ,0x1c ,//10 ,
+ 0x4f ,0x15 ,//0a ,
+
+ 0x50 ,0x10 ,//08 ,
+ 0x51 ,0x10 ,//06 ,
+ 0x52 ,0x0e ,//05 ,
+ 0x53 ,0x0c ,//04 ,
+ 0x54 ,0x0b ,//02 ,
+ 0x55 ,0x0a ,//01 ,
+ 0x90 ,0x80 ,
+ 0x91 ,0x80 ,
+
+ // AWB
+ 0x00 ,0x03 ,
+ 0x10 ,0xd0 , //
+ 0x11 ,0xc0 ,//c0
+ 0x13 ,0x81 , //Cr target
+ 0x14 ,0x7d , //Cb target 7d
+ 0x15 ,0xc0 , // R gain Top e0
+ 0x16 ,0x8c , // R gain bottom 8c //
+ 0x17 ,0xe0 , // B gain Top e0
+ 0x18 ,0x70 , // B gain bottom 0x80 //
+ 0x19 ,0x8c , // Cr top value 8c //
+ 0x1a ,0x64 , // Cr bottom value 0x64 60 //YCbYCr
+ 0x1b ,0x98 , // Cb top value 0x98
+ 0x1c ,0x6c , // Cb bottom value 0x6c // 0x01
+ 0x1d ,0x94 , // 0xa0
+ 0x1e ,0x6c , // 0x60
+ 0x20 ,0xe8 , // AWB luminous top value
+ 0x21 ,0x30 , // AWB luminous bottom value 0x20
+ 0x22 ,0xb8 ,
+ 0x23 ,0x10 ,
+ 0x25 ,0x20 ,
+ 0x26 ,0x0f ,
+ 0x27 ,0x08 , // BRTSRT
+ 0x28 ,0x0f , // BRTEND
+ 0x29 ,0xba , // BRTRGNBOT
+ 0x2a ,0xa0 , // BRTBGNTOP
+
+ 0x40 ,0x01 ,
+ 0x41 ,0x04 ,
+ 0x42 ,0x08 ,
+ 0x43 ,0x10 ,
+ 0x44 ,0x12 , // 0x12
+ 0x45 ,0x35 , //35
+ 0x46 ,0xfc , // fc
+ 0x62 ,0x84 , //84 //
+ 0x63 ,0x90 , // R D30 to D20
+ 0x64 ,0xd0 , // B D30 to D20
+ 0x65 ,0x90 , // R D20 to D30
+ 0x66 ,0xd0 , // B D20 to D30
+
+ // IDP
+ 0x00 ,0x04 ,
+ 0x10 ,0xff ,
+ // 0x11 ,0x1d ,
+ 0x11 ,0x1d , //zhongwei changed
+ 0x12 ,0x3d ,//zhongwei++
+
+
+ // DPCBNR
+ 0x18 ,0xbe ,//fe , // DPCNRCTRL
+ 0x19 ,0x00 ,//04 , // DPCTHV
+ 0x1A ,0x00 ,//01 , // DPCTHVSLP
+ 0x1B ,0x00 ,//08 , // DPCTHVDIFSRT
+ 0x1C ,0x0f ,//08 , // DPCTHVDIFSLP
+ 0x1d ,0xFF , // DPCTHVMAX
+
+ 0x1E ,0x04 , // BNRTHV 0c
+ 0x1F ,0x04 , // BNRTHVSLPN 10
+ 0x20 ,0x10 , // BNRTHVSLPD
+ 0x21 ,0x00 , /// BNRNEICNT / 0x08
+ 0x22 ,0x10 , // STRTNOR // 0x03
+ 0x23 ,0x40 , // STRTDRK
+
+ // Gamma
+ 0x31 ,0x08 ,//0x080x31, 0x04 },//0x08 0x03
+ 0x32 ,0x12 ,//0x100x32, 0x0b },//0x10 0x08
+ 0x33 ,0x24 ,//0x1B0x33, 0x24 },//0x1B 0x16
+ 0x34 ,0x47 ,//0x370x34, 0x49 },//0x37 0x36
+ 0x35 ,0x64 ,//0x4D0x35, 0x66 },//0x4D 0x56
+ 0x36 ,0x7c ,//0x600x36, 0x7C },//0x60 0x77
+ 0x37 ,0x8f ,//0x720x37, 0x8D },//0x72 0x88
+ 0x38 ,0x9f ,//0x820x38, 0x9B },//0x82 0x9a
+ 0x39 ,0xad ,//0x910x39, 0xAA },//0x91 0xA9
+ 0x3a ,0xba ,//0xA00x3a, 0xb6 },//0xA0 0xb5
+ 0x3b ,0xcf ,//0xBA0x3b, 0xca },//0xBA 0xca
+ 0x3c ,0xe2 ,//0xD30x3c, 0xdc },//0xD3 0xdd
+ 0x3d ,0xf0 ,//0xEA0x3d, 0xef },//0xEA 0xee
+
+ // Shading Register Setting
+ 0x40 ,0x04 , //06
+ 0x41 ,0x44 ,
+ 0x42 ,0x43 ,
+ 0x43 ,0x20 ,
+ 0x44 ,0x11 , // left R gain[7:4], right R gain[3:0] 22
+ 0x45 ,0x11 , // top R gain[7:4], bottom R gain[3:0] 22
+ 0x46 ,0x00 , // left G gain[7:4], right G gain[3:0]
+ 0x47 ,0x00 , // top G gain[7:4], bottom G gain[3:0] 11
+ 0x48 ,0x00 , // left B gain[7:4], right B gain[3:0]
+ 0x49 ,0x00 , // top B gain[7:4], bottom B gain[3:0] //color matrix default
+ 0x4a ,0x04 , // X-axis center high[3:2], Y-axis center high[1:0]
+ 0x4b ,0x60 , // X-axis center low[7:0] 48
+ 0x4c ,0xe0 , // Y-axis center low[7:0] e8
+ 0x4d ,0x88 , // Shading Center Gain 80
+ 0x4e ,0x00 , // Shading R Offset
+ 0x4f ,0x00 , // Shading Gr Offset
+ 0x50 ,0x00 , // Shading B Offset
+
+
+ // Interpolation
+ 0x60 ,0x7f ,
+ 0x61 ,0x08 ,
+
+ // Color matrix (D65) - Daylight
+ 0x71 ,0x34 , //0x34 0x39
+ 0x72 ,0xCe , //0xCe 0xCc
+ 0x73 ,0xFe , //0xFe 0xFb
+ 0x74 ,0x13 , //0x13 0x10
+ 0x75 ,0x25 , //0x25 0x21
+ 0x76 ,0x08 , //0x08 0x0f
+ 0x77 ,0xec , //0xec 0xf8
+ 0x78 ,0xd1 , //0xd1 0xc0
+ 0x79 ,0x47 , //0x47 0x48
+
+ // Color matrix (D20) - A
+ 0x83 ,0x38 , //0x3c
+ 0x84 ,0xd1 , //0xc6
+ 0x85 ,0xf7 , //0xff
+ 0x86 ,0x12 , //0x12
+ 0x87 ,0x25 , //0x24
+ 0x88 ,0x09 , //0x0a
+ 0x89 ,0xe1 , //0xed
+ 0x8a ,0xc7 , //0xc2
+ 0x8b ,0x58 , //0x51
+
+ 0x8c ,0x10 , //CMA select
+
+ //G Edge
+ 0x90 ,0x20 , //Upper gain
+ 0x91 ,0x40 , //down gain
+ 0x92 ,0x44 , //[7:4] upper coring [3:0] down coring
+ 0x9a ,0x40 ,
+ 0x9b ,0x40 ,
+ 0x9c ,0x38 , //edge suppress start 30
+ 0x9d ,0x30 , //edge suppress slope
+ 0xa8 ,0x10 ,
+ 0xa9 ,0x13 ,
+ 0xaa ,0x13 ,
+ 0xb9 ,0x30 , // nightmode 38 at gain 0x48 5fps
+ 0xba ,0x44 , // nightmode 80 at gain 0x48 5fps
+ 0xde ,0x80 ,
+
+ 0xe5 ,0x15 ,
+ 0xe6 ,0x02 ,
+ 0xe7 ,0x04 ,
+
+ //Sensor On //Sensor On
+ 0x00 ,0x01 ,
+ 0x03 ,0x01 ,
+ 0x00 ,0x00 ,
+#else
+ 0x00,0x00,
+ 0x03,0x04,
+ 0x10,0x86,//8d}}, //85
+ 0x11,0x74,// f1}}, //0x11 0x61 davis20120514
+
+ 0x00,0x01,
+ 0x04,0x00, //00
+ 0x06,0x04,
+ 0x10,0x11,//46}},
+ 0x11,0x25,//23}},
+ 0x12,0x21,
+ 0x17,0x94,//86}},
+ 0x18,0x00,
+ 0x20,0x00,
+ 0x21,0x05,
+ 0x22,0x01,
+ 0x23,0x69, //add
+ 0x40,0x0F,
+ 0x41,0x90,
+ 0x42,0xd2,
+ 0x43,0x00,
+
+ // AE
+ 0x00,0x02,
+ 0x10,0x84,
+ 0x11,0x14,//0c}},//10}},
+ 0x12,0x68,//71,//70}}, // D65 target 60
+ 0x14,0x40,//68, // A target 5d
+ 0x34,0x96,
+ 0x40,0x38,//48}},//40}}, // 40
+ 0x44,0x08,//00}},
+
+
+ // AWB
+ 0x00,0x03,
+ 0x10,0xD0,//c3}}, //
+ 0x11,0xc1,//c0}},//c0
+ 0x12,0x80,
+ 0x13,0x80, //Cr target 0x7e
+ 0x14,0x80, //Cb target 7f
+ 0x15,0xe0,//c0}}, // R gain Top e0
+ 0x16,0x7C,//88}},//8c}}, // R gain bottom 8c //
+ 0x17,0xe0, // B gain Top e0
+ 0x18,0x80,//70}}, // B gain bottom 0x80 //
+ 0x19,0x8C,//90}}, // Cr top value 8c //
+ 0x1a,0x64, // Cr bottom value 0x64 60 //YCbYCr
+ 0x1b,0x94, // Cb top value 0x98
+ 0x1c,0x6c, // Cb bottom value 0x6c // 0x01
+ 0x1d,0x94, // 0xa0
+ 0x1e,0x6c, // 0x60
+ 0x20,0xe8, // AWB luminous top value
+ 0x21,0x30, // AWB luminous bottom value 0x20
+ 0x22,0xb8,
+ 0x23,0x10,
+ 0x25,0x08,//20}},
+ 0x26,0x0f,//0f}},
+ 0x27,0x60, // BRTSRT
+ 0x28,0x70, // BRTEND
+ 0x29,0xb7, // BRTRGNBOT
+ 0x2a,0xa3, // BRTBGNTOP
+
+ 0x40,0x01,
+ 0x41,0x03,//04}},
+ 0x42,0x08,
+ 0x43,0x10,
+ 0x44,0x13,//12}}, // 0x12
+ 0x45,0x6a,//35}}, //35
+ 0x46,0xca, // fc
+
+ 0x62,0x80,//84}}, //84 //
+
+ 0x63,0x90,//9c}}, // R D30 to D20
+ 0x64,0xD0,//d4}}, // B D30 to D20
+ 0x65,0x98,//a0}},//90}}, // R D20 to D30
+ 0x66,0xd0, // B D20 to D30
+
+ // IDP
+ 0x00,0x04,
+ 0x10,0x7f,//f,
+
+ 0x11,0x1d, // changed
+ 0x12,0x3d,//++
+ 0x13,0xfe,//++
+ 0x14,0x01,//++
+
+
+ // DPCBNR
+ 0x18,0xbf,//fe, // DPCNRCTRL
+ 0x19,0x00,//04, // DPCTHV
+ 0x1A,0x00,//01, // DPCTHVSLP
+ 0x1B,0x00,//08,//08}}, // DPCTHVDIFSRT
+
+
+
+ 0x1E,0x04, // BNRTHV 0c
+ 0x1F,0x08,//04}}, // BNRTHVSLPN 10
+ 0x20,0x20, // BNRTHVSLPD
+ 0x21,0x00, /// BNRNEICNT / 0x08
+ 0x22,0x08, // STRTNOR // 0x03
+ 0x23,0x38,//40}}, // STRTDRK
+ 0x24,0x00,
+
+ // Gamma
+
+ 0x31, 0x11,//03,//0x080x31, 0x04 },//0x08 0x03
+ 0x32, 0x21,//0b,//0x100x32, 0x0b },//0x10 0x08
+ 0x33, 0x37,//1e,//0x1B0x33, 0x24 },//0x1B 0x16
+ 0x34, 0x54,//46,//0x370x34, 0x49 },//0x37 0x36
+ 0x35, 0x6e,//62,//0x4D0x35, 0x66 },//0x4D 0x56
+ 0x36, 0x84,//0x600x36, 0x7C },//0x60 0x77
+ 0x37, 0x96,//0x720x37, 0x8D },//0x72 0x88
+ 0x38, 0xa7,//0x820x38, 0x9B },//0x82 0x9a
+ 0x39, 0xb7,//0x910x39, 0xAA },//0x91 0xA9
+ 0x3a, 0xc4,//0xA00x3a, 0xb6 },//0xA0 0xb5
+ 0x3b, 0xd9,//0xBA0x3b, 0xca },//0xBA 0xca
+ 0x3c, 0xea,//0xD30x3c, 0xdc },//0xD3 0xdd
+ 0x3d, 0xf9,//0xEA0x3d, 0xef },//0xEA 0xee
+
+ // Shading Register Setting
+ 0x40,0x55,//04}}, //06
+ 0x41,0x44,
+ 0x42,0x33,//43}},
+ 0x43,0x00,
+ 0x44,0x22, // left R gain[7:4], right R gain[3:0] 22
+ 0x45,0x22, // top R gain[7:4], bottom R gain[3:0] 22
+ 0x46,0x00, // left G gain[7:4], right G gain[3:0]
+ 0x47,0x03, // top G gain[7:4], bottom G gain[3:0] 11
+ 0x48,0x01, // left B gain[7:4], right B gain[3:0]
+ 0x49,0x11, // top B gain[7:4], bottom B gain[3:0] //color matrix default
+ 0x4a,0x05, // X-axis center high[3:2], Y-axis center high[1:0]
+ 0x4b,0x40, // X-axis center low[7:0] 48
+ 0x4c,0x10, // Y-axis center low[7:0] e8
+ 0x4d,0x80, // Shading Center Gain 80
+ 0x4e,0x00, // Shading R Offset
+ 0x4f,0x00, // Shading Gr Offset
+ 0x50,0x00, // Shading B Offset
+
+
+ // Interpolation
+ 0x60,0x7f,
+ 0x61,0x08,
+
+ // Color matrix (D65) - Daylight
+
+
+ 0x71,0x39, //0x34 0x39
+ 0x72,0xC8, //0xCe 0xCc
+ 0x73,0xff, //0xFe 0xFb
+ 0x74,0x13, //0x13 0x10
+ 0x75,0x25, //0x25 0x21
+ 0x76,0x08, //0x08 0x0f
+ 0x77,0xf8, //0xec 0xf8
+ 0x78,0xc0, //0xd1 0xc0
+ 0x79,0x48, //0x47 0x48
+
+ // Color matrix (D20) - A
+ 0x83,0x34, //0x3c
+ 0x84,0xd4, //0xc6
+ 0x85,0xf8, //0xff
+ 0x86,0x12, //0x12
+ 0x87,0x25, //0x24
+ 0x88,0x09, //0x0a
+ 0x89,0xf0, //0xed
+ 0x8a,0xbe, //0xc2
+ 0x8b,0x54, //0x51
+
+
+ 0x8c,0x10, //CMA select
+
+ //G Edge
+ 0x90,0x30, //Upper gain
+ 0x91,0x40, //down gain
+ 0x92,0x33,//22}}, //[7:4] upper coring [3:0] down coring
+ 0x9a,0x40,
+ 0x9b,0x40,
+ 0x9c,0x38, //edge suppress start 30
+ 0x9d,0x30, //edge suppress slope
+
+ 0x9f,0x26,
+ 0xa0,0x11,
+
+ 0xa8,0x11,//10}},
+ 0xa9,0x11,//10}},
+ 0xaa,0x11,
+
+ 0xbf,0x20,
+
+ 0xb9,0x28,//30}}, // nightmode 38 at gain 0x48 5fps
+ 0xba,0x41,//44}}, // nightmode 80 at gain 0x48 5fps
+ 0xde,0x80,
+
+ 0xe5,0x15,
+ 0xe6,0x02,
+ 0xe7,0x04,
+
+ //Sensor On //Sensor On
+ 0x00,0x01,
+ 0x03,0x01,
+
+ 0x00,0x00,
+#endif
+};
+
+uint8_t siv121d_default_regs_init_121du[] = {
+#if 0
+ 0x00 ,0x00 ,
+ 0x00 ,0x01 ,
+ 0x03 ,0x0a ,
+
+ 0x00 ,0x01 ,
+ 0x03 ,0x0a ,
+
+ 0x00 ,0x00 ,
+ 0x03 ,0x04 ,
+ 0x10 ,0x85 , //85
+ 0x11 ,0x74 , //0x11 0x61 davis20120514
+
+ 0x00 ,0x01 ,
+ 0x04 ,0x00 , //00
+ 0x06 ,0x04 ,
+ 0x10 ,0x46 ,
+ 0x11 ,0x23 ,
+ 0x12 ,0x21 ,
+ 0x17 ,0x86 ,
+ 0x18 ,0x00 ,
+ 0x20 ,0x00 ,
+ 0x21 ,0x05 ,
+ 0x22 ,0x01 ,
+ 0x23 ,0x69 , //add
+ 0x40 ,0x0F ,
+ 0x41 ,0x90 ,
+ 0x42 ,0xd2 ,
+ 0x43 ,0x00 ,
+
+ // AE
+ 0x00 ,0x02 ,
+ 0x10 ,0x80 ,
+ 0x11 ,0x10 ,
+ 0x12 ,0x65 , // D65 target 60
+ 0x14 ,0x62 , // A target 5
+ 0x34 ,0x96 ,
+ 0x40 ,0x40 , // 40
+ 0x5f ,0x01 ,
+ 0x41 ,0x28 ,//20 ,
+ 0x42 ,0x28 ,//20 ,
+ 0x44 ,0x08 ,//00 ,
+ 0x45 ,0x08 ,//00 ,
+ 0x46 ,0x15 ,//0a ,
+ 0x47 ,0x1c ,//10 ,
+ 0x48 ,0x20 ,//13 ,
+ 0x49 ,0x21 ,//15 ,
+ 0x4a ,0x23 ,//18 ,
+ 0x4b ,0x24 ,//1a ,
+ 0x4c ,0x25 ,//1d ,
+ 0x4d ,0x28 ,//20 ,
+ 0x4e ,0x1c ,//10 ,
+ 0x4f ,0x15 ,//0a ,
+
+ 0x50 ,0x10 ,//08 ,
+ 0x51 ,0x10 ,//06 ,
+ 0x52 ,0x0e ,//05 ,
+ 0x53 ,0x0c ,//04 ,
+ 0x54 ,0x0b ,//02 ,
+ 0x55 ,0x0a ,//01 ,
+ 0x90 ,0x80 ,
+ 0x91 ,0x80 ,
+
+ // AWB
+ 0x00 ,0x03 ,
+ 0x10 ,0xd0 , //
+ 0x11 ,0xc0 ,//c0
+ 0x13 ,0x81 , //Cr target
+ 0x14 ,0x7d , //Cb target 7d
+ 0x15 ,0xc0 , // R gain Top e0
+ 0x16 ,0x8c , // R gain bottom 8c //
+ 0x17 ,0xe0 , // B gain Top e0
+ 0x18 ,0x70 , // B gain bottom 0x80 //
+ 0x19 ,0x8c , // Cr top value 8c //
+ 0x1a ,0x64 , // Cr bottom value 0x64 60 //YCbYCr
+ 0x1b ,0x98 , // Cb top value 0x98
+ 0x1c ,0x6c , // Cb bottom value 0x6c // 0x01
+ 0x1d ,0x94 , // 0xa0
+ 0x1e ,0x6c , // 0x60
+ 0x20 ,0xe8 , // AWB luminous top value
+ 0x21 ,0x30 , // AWB luminous bottom value 0x20
+ 0x22 ,0xb8 ,
+ 0x23 ,0x10 ,
+ 0x25 ,0x20 ,
+ 0x26 ,0x0f ,
+ 0x27 ,0x08 , // BRTSRT
+ 0x28 ,0x0f , // BRTEND
+ 0x29 ,0xba , // BRTRGNBOT
+ 0x2a ,0xa0 , // BRTBGNTOP
+
+ 0x40 ,0x01 ,
+ 0x41 ,0x04 ,
+ 0x42 ,0x08 ,
+ 0x43 ,0x10 ,
+ 0x44 ,0x12 , // 0x12
+ 0x45 ,0x35 , //35
+ 0x46 ,0xfc , // fc
+ 0x62 ,0x84 , //84 //
+ 0x63 ,0x90 , // R D30 to D20
+ 0x64 ,0xd0 , // B D30 to D20
+ 0x65 ,0x90 , // R D20 to D30
+ 0x66 ,0xd0 , // B D20 to D30
+
+ // IDP
+ 0x00 ,0x04 ,
+ 0x10 ,0xff ,
+ // 0x11 ,0x1d ,
+ 0x11 ,0x1d , //zhongwei changed
+ 0x12 ,0x3d ,//zhongwei++
+
+
+ // DPCBNR
+ 0x18 ,0xbe ,//fe , // DPCNRCTRL
+ 0x19 ,0x00 ,//04 , // DPCTHV
+ 0x1A ,0x00 ,//01 , // DPCTHVSLP
+ 0x1B ,0x00 ,//08 , // DPCTHVDIFSRT
+ 0x1C ,0x0f ,//08 , // DPCTHVDIFSLP
+ 0x1d ,0xFF , // DPCTHVMAX
+
+ 0x1E ,0x04 , // BNRTHV 0c
+ 0x1F ,0x04 , // BNRTHVSLPN 10
+ 0x20 ,0x10 , // BNRTHVSLPD
+ 0x21 ,0x00 , /// BNRNEICNT / 0x08
+ 0x22 ,0x10 , // STRTNOR // 0x03
+ 0x23 ,0x40 , // STRTDRK
+
+ // Gamma
+ 0x31 ,0x08 ,//0x080x31, 0x04 },//0x08 0x03
+ 0x32 ,0x12 ,//0x100x32, 0x0b },//0x10 0x08
+ 0x33 ,0x24 ,//0x1B0x33, 0x24 },//0x1B 0x16
+ 0x34 ,0x47 ,//0x370x34, 0x49 },//0x37 0x36
+ 0x35 ,0x64 ,//0x4D0x35, 0x66 },//0x4D 0x56
+ 0x36 ,0x7c ,//0x600x36, 0x7C },//0x60 0x77
+ 0x37 ,0x8f ,//0x720x37, 0x8D },//0x72 0x88
+ 0x38 ,0x9f ,//0x820x38, 0x9B },//0x82 0x9a
+ 0x39 ,0xad ,//0x910x39, 0xAA },//0x91 0xA9
+ 0x3a ,0xba ,//0xA00x3a, 0xb6 },//0xA0 0xb5
+ 0x3b ,0xcf ,//0xBA0x3b, 0xca },//0xBA 0xca
+ 0x3c ,0xe2 ,//0xD30x3c, 0xdc },//0xD3 0xdd
+ 0x3d ,0xf0 ,//0xEA0x3d, 0xef },//0xEA 0xee
+
+ // Shading Register Setting
+ 0x40 ,0x04 , //06
+ 0x41 ,0x44 ,
+ 0x42 ,0x43 ,
+ 0x43 ,0x20 ,
+ 0x44 ,0x11 , // left R gain[7:4], right R gain[3:0] 22
+ 0x45 ,0x11 , // top R gain[7:4], bottom R gain[3:0] 22
+ 0x46 ,0x00 , // left G gain[7:4], right G gain[3:0]
+ 0x47 ,0x00 , // top G gain[7:4], bottom G gain[3:0] 11
+ 0x48 ,0x00 , // left B gain[7:4], right B gain[3:0]
+ 0x49 ,0x00 , // top B gain[7:4], bottom B gain[3:0] //color matrix default
+ 0x4a ,0x04 , // X-axis center high[3:2], Y-axis center high[1:0]
+ 0x4b ,0x60 , // X-axis center low[7:0] 48
+ 0x4c ,0xe0 , // Y-axis center low[7:0] e8
+ 0x4d ,0x88 , // Shading Center Gain 80
+ 0x4e ,0x00 , // Shading R Offset
+ 0x4f ,0x00 , // Shading Gr Offset
+ 0x50 ,0x00 , // Shading B Offset
+
+
+ // Interpolation
+ 0x60 ,0x7f ,
+ 0x61 ,0x08 ,
+
+ // Color matrix (D65) - Daylight
+ 0x71 ,0x34 , //0x34 0x39
+ 0x72 ,0xCe , //0xCe 0xCc
+ 0x73 ,0xFe , //0xFe 0xFb
+ 0x74 ,0x13 , //0x13 0x10
+ 0x75 ,0x25 , //0x25 0x21
+ 0x76 ,0x08 , //0x08 0x0f
+ 0x77 ,0xec , //0xec 0xf8
+ 0x78 ,0xd1 , //0xd1 0xc0
+ 0x79 ,0x47 , //0x47 0x48
+
+ // Color matrix (D20) - A
+ 0x83 ,0x38 , //0x3c
+ 0x84 ,0xd1 , //0xc6
+ 0x85 ,0xf7 , //0xff
+ 0x86 ,0x12 , //0x12
+ 0x87 ,0x25 , //0x24
+ 0x88 ,0x09 , //0x0a
+ 0x89 ,0xe1 , //0xed
+ 0x8a ,0xc7 , //0xc2
+ 0x8b ,0x58 , //0x51
+
+ 0x8c ,0x10 , //CMA select
+
+ //G Edge
+ 0x90 ,0x20 , //Upper gain
+ 0x91 ,0x40 , //down gain
+ 0x92 ,0x44 , //[7:4] upper coring [3:0] down coring
+ 0x9a ,0x40 ,
+ 0x9b ,0x40 ,
+ 0x9c ,0x38 , //edge suppress start 30
+ 0x9d ,0x30 , //edge suppress slope
+ 0xa8 ,0x10 ,
+ 0xa9 ,0x13 ,
+ 0xaa ,0x13 ,
+ 0xb9 ,0x30 , // nightmode 38 at gain 0x48 5fps
+ 0xba ,0x44 , // nightmode 80 at gain 0x48 5fps
+ 0xde ,0x80 ,
+
+ 0xe5 ,0x15 ,
+ 0xe6 ,0x02 ,
+ 0xe7 ,0x04 ,
+
+ //Sensor On //Sensor On
+ 0x00 ,0x01 ,
+ 0x03 ,0x01 ,
+ 0x00 ,0x00 ,
+#else
+ 0x00,0x00,
+ 0x03,0x04,
+ 0x10,0x86,//8d}}, //85
+ 0x11,0x74,// f1}}, //0x11 0x61 davis20120514
+
+ 0x00,0x01,
+ 0x04,0x00, //00
+ 0x06,0x04,
+ 0x10,0x11,//46}},
+ 0x11,0x25,//23}},
+ 0x12,0x21,
+ 0x17,0x94,//86}},
+ 0x18,0x00,
+ 0x20,0x00,
+ 0x21,0x05,
+ 0x22,0x01,
+ 0x23,0x69, //add
+ 0x40,0x0F,
+ 0x41,0x90,
+ 0x42,0xd2,
+ 0x43,0x00,
+
+ // AE
+ 0x00,0x02,
+ 0x10,0x84,
+ 0x11,0x14,//0c}},//10}},
+ 0x12,0x68,//71,//70}}, // D65 target 60
+ 0x14,0x40,//68, // A target 5d
+ 0x34,0x96,
+ 0x40,0x38,//48}},//40}}, // 40
+ 0x44,0x08,//00}},
+
+
+ // AWB
+ 0x00,0x03,
+ 0x10,0xD0,//c3}}, //
+ 0x11,0xc1,//c0}},//c0
+ 0x12,0x80,
+ 0x13,0x80, //Cr target 0x7e
+ 0x14,0x80, //Cb target 7f
+ 0x15,0xe0,//c0}}, // R gain Top e0
+ 0x16,0x7C,//88}},//8c}}, // R gain bottom 8c //
+ 0x17,0xe0, // B gain Top e0
+ 0x18,0x80,//70}}, // B gain bottom 0x80 //
+ 0x19,0x8C,//90}}, // Cr top value 8c //
+ 0x1a,0x64, // Cr bottom value 0x64 60 //YCbYCr
+ 0x1b,0x94, // Cb top value 0x98
+ 0x1c,0x6c, // Cb bottom value 0x6c // 0x01
+ 0x1d,0x94, // 0xa0
+ 0x1e,0x6c, // 0x60
+ 0x20,0xe8, // AWB luminous top value
+ 0x21,0x30, // AWB luminous bottom value 0x20
+ 0x22,0xb8,
+ 0x23,0x10,
+ 0x25,0x08,//20}},
+ 0x26,0x0f,//0f}},
+ 0x27,0x60, // BRTSRT
+ 0x28,0x70, // BRTEND
+ 0x29,0xb7, // BRTRGNBOT
+ 0x2a,0xa3, // BRTBGNTOP
+
+ 0x40,0x01,
+ 0x41,0x03,//04}},
+ 0x42,0x08,
+ 0x43,0x10,
+ 0x44,0x13,//12}}, // 0x12
+ 0x45,0x6a,//35}}, //35
+ 0x46,0xca, // fc
+
+ 0x62,0x80,//84}}, //84 //
+
+ 0x63,0x90,//9c}}, // R D30 to D20
+ 0x64,0xD0,//d4}}, // B D30 to D20
+ 0x65,0x98,//a0}},//90}}, // R D20 to D30
+ 0x66,0xd0, // B D20 to D30
+
+ // IDP
+ 0x00,0x04,
+ 0x10,0x7f,//f,
+
+ 0x11,0x0d, // changed
+ 0x12,0x3d,//++
+ 0x13,0xfe,//++
+ 0x14,0x01,//++
+
+
+ // DPCBNR
+ 0x18,0xbf,//fe, // DPCNRCTRL
+ 0x19,0x00,//04, // DPCTHV
+ 0x1A,0x00,//01, // DPCTHVSLP
+ 0x1B,0x00,//08,//08}}, // DPCTHVDIFSRT
+
+
+
+ 0x1E,0x04, // BNRTHV 0c
+ 0x1F,0x08,//04}}, // BNRTHVSLPN 10
+ 0x20,0x20, // BNRTHVSLPD
+ 0x21,0x00, /// BNRNEICNT / 0x08
+ 0x22,0x08, // STRTNOR // 0x03
+ 0x23,0x38,//40}}, // STRTDRK
+ 0x24,0x00,
+
+ // Gamma
+
+ 0x31, 0x11,//03,//0x080x31, 0x04 },//0x08 0x03
+ 0x32, 0x21,//0b,//0x100x32, 0x0b },//0x10 0x08
+ 0x33, 0x37,//1e,//0x1B0x33, 0x24 },//0x1B 0x16
+ 0x34, 0x54,//46,//0x370x34, 0x49 },//0x37 0x36
+ 0x35, 0x6e,//62,//0x4D0x35, 0x66 },//0x4D 0x56
+ 0x36, 0x84,//0x600x36, 0x7C },//0x60 0x77
+ 0x37, 0x96,//0x720x37, 0x8D },//0x72 0x88
+ 0x38, 0xa7,//0x820x38, 0x9B },//0x82 0x9a
+ 0x39, 0xb7,//0x910x39, 0xAA },//0x91 0xA9
+ 0x3a, 0xc4,//0xA00x3a, 0xb6 },//0xA0 0xb5
+ 0x3b, 0xd9,//0xBA0x3b, 0xca },//0xBA 0xca
+ 0x3c, 0xea,//0xD30x3c, 0xdc },//0xD3 0xdd
+ 0x3d, 0xf9,//0xEA0x3d, 0xef },//0xEA 0xee
+
+ // Shading Register Setting
+ 0x40,0x55,//04}}, //06
+ 0x41,0x44,
+ 0x42,0x33,//43}},
+ 0x43,0x00,
+ 0x44,0x22, // left R gain[7:4], right R gain[3:0] 22
+ 0x45,0x22, // top R gain[7:4], bottom R gain[3:0] 22
+ 0x46,0x00, // left G gain[7:4], right G gain[3:0]
+ 0x47,0x03, // top G gain[7:4], bottom G gain[3:0] 11
+ 0x48,0x01, // left B gain[7:4], right B gain[3:0]
+ 0x49,0x11, // top B gain[7:4], bottom B gain[3:0] //color matrix default
+ 0x4a,0x05, // X-axis center high[3:2], Y-axis center high[1:0]
+ 0x4b,0x40, // X-axis center low[7:0] 48
+ 0x4c,0x10, // Y-axis center low[7:0] e8
+ 0x4d,0x80, // Shading Center Gain 80
+ 0x4e,0x00, // Shading R Offset
+ 0x4f,0x00, // Shading Gr Offset
+ 0x50,0x00, // Shading B Offset
+
+
+ // Interpolation
+ 0x60,0x7f,
+ 0x61,0x08,
+
+ // Color matrix (D65) - Daylight
+
+
+ 0x71,0x39, //0x34 0x39
+ 0x72,0xC8, //0xCe 0xCc
+ 0x73,0xff, //0xFe 0xFb
+ 0x74,0x13, //0x13 0x10
+ 0x75,0x25, //0x25 0x21
+ 0x76,0x08, //0x08 0x0f
+ 0x77,0xf8, //0xec 0xf8
+ 0x78,0xc0, //0xd1 0xc0
+ 0x79,0x48, //0x47 0x48
+
+ // Color matrix (D20) - A
+ 0x83,0x34, //0x3c
+ 0x84,0xd4, //0xc6
+ 0x85,0xf8, //0xff
+ 0x86,0x12, //0x12
+ 0x87,0x25, //0x24
+ 0x88,0x09, //0x0a
+ 0x89,0xf0, //0xed
+ 0x8a,0xbe, //0xc2
+ 0x8b,0x54, //0x51
+
+
+ 0x8c,0x10, //CMA select
+
+ //G Edge
+ 0x90,0x30, //Upper gain
+ 0x91,0x40, //down gain
+ 0x92,0x33,//22}}, //[7:4] upper coring [3:0] down coring
+ 0x9a,0x40,
+ 0x9b,0x40,
+ 0x9c,0x38, //edge suppress start 30
+ 0x9d,0x30, //edge suppress slope
+
+ 0x9f,0x26,
+ 0xa0,0x11,
+
+ 0xa8,0x11,//10}},
+ 0xa9,0x11,//10}},
+ 0xaa,0x11,
+
+ 0xbf,0x20,
+
+ 0xb9,0x28,//30}}, // nightmode 38 at gain 0x48 5fps
+ 0xba,0x41,//44}}, // nightmode 80 at gain 0x48 5fps
+ 0xde,0x80,
+
+ 0xe5,0x15,
+ 0xe6,0x02,
+ 0xe7,0x04,
+
+ //Sensor On //Sensor On
+ 0x00,0x01,
+ 0x03,0x01,
+
+ 0x00,0x00,
+#endif
+};
+
+#endif
+
diff --git a/drivers/media/video/wmt_v4l2/sensors/sp0718/sp0718-gt7302-gmt.h b/drivers/media/video/wmt_v4l2/sensors/sp0718/sp0718-gt7302-gmt.h
new file mode 100755
index 00000000..986da258
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/sp0718/sp0718-gt7302-gmt.h
@@ -0,0 +1,992 @@
+
+#ifndef __SP0718_H_
+#define __SP0718_H_
+
+#define SP0718_NORMAL_Y0ffset 0x20
+#define SP0718_LOWLIGHT_Y0ffset 0x25
+//AE target
+#define SP0718_P1_0xeb 0x78
+#define SP0718_P1_0xec 0x6c
+//HEQ
+#define SP0718_P1_0x10 0x00//outdoor
+#define SP0718_P1_0x14 0x1e //0x18
+#define SP0718_P1_0x11 0x00//nr
+#define SP0718_P1_0x15 0x14 //0x10
+#define SP0718_P1_0x12 0x00//dummy
+#define SP0718_P1_0x16 0x14 //0x10
+#define SP0718_P1_0x13 0x00//low
+#define SP0718_P1_0x17 0x08
+//AWB pre gain
+#define SP0718_P2_0x26 0xc8
+#define SP0718_P2_0x27 0xb6
+//SAT u
+#define SP0718_P1_0xd3 0x9c
+#define SP0718_P1_0xd4 0x98
+#define SP0718_P1_0xd5 0x8c
+#define SP0718_P1_0xd6 0x84
+//SAT v
+#define SP0718_P1_0xd7 0x9c
+#define SP0718_P1_0xd8 0x98
+#define SP0718_P1_0xd9 0x8c
+#define SP0718_P1_0xda 0x84
+
+
+// Scene Mode
+uint8_t sp0718_scene_mode_auto[] = {
+ 0xfd,0x01,
+ 0xcd,SP0718_NORMAL_Y0ffset,
+ 0xce,0x1f,
+ //capture preview daylight 24M 50hz 15-8FPS
+
+ 0xfd,0x00,
+ 0x03,0x01,
+ 0x04,0xb0,
+ 0x06,0x00,
+ 0x09,0x03,
+ 0x0a,0x31,
+ 0xfd,0x01,
+ 0xef,0x48,
+ 0xf0,0x00,
+ 0x02,0x0e,
+ 0x03,0x01,
+ 0x06,0x42,
+ 0x07,0x00,
+ 0x08,0x01,
+ 0x09,0x00,
+ //Status,
+ 0xfd,0x02,
+ 0xbe,0xf0,
+ 0xbf,0x03,
+ 0xd0,0xf0,
+ 0xd1,0x03,
+ 0xfd,0x01,
+ 0x5b,0x03,
+ 0x5c,0xf0,
+
+
+ 0xfd,0x00,
+};
+
+uint8_t sp0718_scene_mode_night[] = {
+ 0xfd,0x01,
+ 0xcd,SP0718_LOWLIGHT_Y0ffset,
+ 0xce,0x1f,
+ //capture preview night 24M 50hz 15-6FPS
+ 0xfd,0x00,
+ 0x03,0x01,
+ 0x04,0xce,
+ 0x06,0x00,
+ 0x09,0x02,
+ 0x0a,0xc4,
+ 0xfd,0x01,
+ 0xef,0x4d,
+ 0xf0,0x00,
+ 0x02,0x10,
+ 0x03,0x01,
+ 0x06,0x47,
+ 0x07,0x00,
+ 0x08,0x01,
+ 0x09,0x00,
+ //Statu
+ 0xfd,0x02,
+ 0xbe,0xd0,
+ 0xbf,0x04,
+ 0xd0,0xd0,
+ 0xd1,0x04,
+ 0xfd,0x01,
+ 0x5b,0x04,
+ 0x5c,0xd0,
+
+ 0xfd,0x00,
+};
+
+// White Balance
+uint8_t sp0718_wb_auto[] = {
+ 0xfd,0x01,
+ 0x32,0x15,
+ 0xfd,0x02,
+ 0x26,0xc8,
+ 0x27,0xb6,
+ 0xfd,0x00,
+};
+
+uint8_t sp0718_wb_incandescent[] = {
+ 0xfd,0x01,
+ 0x32,0x05,
+ 0xfd,0x02,
+ 0x26,0xaa,
+ 0x27,0xce,
+ 0xfd,0x00,
+};
+
+uint8_t sp0718_wb_fluorescent[] = {
+ 0xfd,0x01,
+ 0x32,0x05,
+ 0xfd,0x02,
+ 0x26,0x91,
+ 0x27,0xc8,
+ 0xfd,0x00,
+};
+
+uint8_t sp0718_wb_daylight[] = {
+ 0xfd,0x01,
+ 0x32,0x05,
+ 0xfd,0x02,
+ 0x26,0xc8,
+ 0x27,0x89,
+ 0xfd,0x00,
+};
+
+uint8_t sp0718_wb_cloudy[] = {
+ 0xfd,0x01,
+ 0x32,0x05,
+ 0xfd,0x02,
+ 0x26,0xdc,
+ 0x27,0x75,
+ 0xfd,0x00,
+};
+
+uint8_t sp0718_wb_tungsten[] = {
+ 0xfd,0x01,
+ 0x32,0x05,
+ 0xfd,0x02,
+ 0x26,0x75,
+ 0x27,0xe2,
+ 0xfd,0x00,
+};
+
+
+// Exposure
+uint8_t sp0718_exposure_neg2[] = {
+ 0xfd,0x01,
+ 0xdb,0xe0,//level -3
+};
+
+uint8_t sp0718_exposure_neg1[] = {
+ 0xfd,0x01,
+ 0xdb,0xf0,
+};
+
+uint8_t sp0718_exposure_zero[] = {
+ 0xfd,0x01,
+ 0xdb,0x00,//level -3
+};
+
+uint8_t sp0718_exposure_pos1[] = {
+ 0xfd,0x01,
+ 0xdb,0x10,
+};
+
+uint8_t sp0718_exposure_pos2[] = {
+ 0xfd,0x01,
+ 0xdb,0x20,
+};
+
+
+// Color Effect
+uint8_t sp0718_colorfx_none[] = {
+ 0xfd, 0x01,
+ 0x66, 0x00,
+ 0x67, 0x80,
+ 0x68, 0x80,
+};
+
+uint8_t sp0718_colorfx_bw[] = {
+ 0xfd, 0x01,
+ 0x66, 0x20,
+ 0x67, 0x80,
+ 0x68, 0x80,
+};
+
+uint8_t sp0718_colorfx_sepia[] = {
+ 0xfd, 0x01,
+ 0x66, 0x10,
+ 0x67, 0xc0,
+ 0x68, 0x20,
+};
+
+uint8_t sp0718_colorfx_negative[] = {
+ //Negative
+ 0xfd, 0x01,
+ 0x66, 0x04,
+ 0x67, 0x80,
+ 0x68, 0x80,
+};
+
+uint8_t sp0718_colorfx_emboss[] = {
+};
+
+uint8_t sp0718_colorfx_sketch[] = {
+};
+
+uint8_t sp0718_colorfx_sky_blue[] = {
+ 0xfd, 0x01,
+ 0x66, 0x10,
+ 0x67, 0x20,
+ 0x68, 0xf0,
+};
+
+uint8_t sp0718_colorfx_grass_green[] = {
+ 0xfd, 0x01,
+ 0x66, 0x10,
+ 0x67, 0x20,
+ 0x68, 0x20,
+};
+
+uint8_t sp0718_colorfx_skin_whiten[] = {
+};
+
+uint8_t sp0718_colorfx_vivid[] = {
+};
+
+// Brightness
+uint8_t sp0718_brightness_neg4[] = {
+ 0xfd,0x01,
+ 0xdb,0xc0,
+};
+
+uint8_t sp0718_brightness_neg3[] = {
+ 0xfd,0x01,
+ 0xdb,0xd0,
+};
+
+uint8_t sp0718_brightness_neg2[] = {
+ 0xfd,0x01,
+ 0xdb,0xe0,
+};
+
+uint8_t sp0718_brightness_neg1[] = {
+ 0xfd,0x01,
+ 0xdb,0xf0,
+};
+
+uint8_t sp0718_brightness_zero[] = {
+ 0xfd,0x01,
+ 0xdb,0x00
+};
+
+uint8_t sp0718_brightness_pos1[] = {
+ 0xfd,0x01,
+ 0xdb,0x10,
+};
+
+uint8_t sp0718_brightness_pos2[] = {
+ 0xfd,0x01,
+ 0xdb,0x20,
+};
+
+uint8_t sp0718_brightness_pos3[] = {
+ 0xfd,0x01,
+ 0xdb,0x30,
+};
+
+uint8_t sp0718_brightness_pos4[] = {
+ 0xfd,0x01,
+ 0xdb,0x40,
+};
+
+
+// contrast
+uint8_t sp0718_contrast_neg4[] = {
+ //level -4
+ 0xfd,0x01,
+ 0x10,SP0718_P1_0x10, //out
+ 0x14,SP0718_P1_0x14-0x10,
+ 0x11,SP0718_P1_0x11, //nr
+ 0x15,SP0718_P1_0x15-0x10,
+ 0x12,SP0718_P1_0x12, //dummy
+ 0x16,SP0718_P1_0x16-0x10,
+ 0x13,SP0718_P1_0x13, //low
+ 0x17,SP0718_P1_0x17,
+};
+
+uint8_t sp0718_contrast_neg3[] = {
+ //level -3
+ 0xfd,0x01,
+ 0x10,SP0718_P1_0x10, //out
+ 0x14,SP0718_P1_0x14-0x0c,
+ 0x11,SP0718_P1_0x11, //nr
+ 0x15,SP0718_P1_0x15-0x0c,
+ 0x12,SP0718_P1_0x12, //dummy
+ 0x16,SP0718_P1_0x16-0x0c,
+ 0x13,SP0718_P1_0x13, //low
+ 0x17,SP0718_P1_0x17,
+};
+
+uint8_t sp0718_contrast_neg2[] = {
+ //level -2
+ 0xfd,0x01,
+ 0x10,SP0718_P1_0x10, //out
+ 0x14,SP0718_P1_0x14-0x08,
+ 0x11,SP0718_P1_0x11, //nr
+ 0x15,SP0718_P1_0x15-0x08,
+ 0x12,SP0718_P1_0x12, //dummy
+ 0x16,SP0718_P1_0x16-0x08,
+ 0x13,SP0718_P1_0x13, //low
+ 0x17,SP0718_P1_0x17,
+};
+
+uint8_t sp0718_contrast_neg1[] = {
+ //level -1
+ 0xfd,0x01,
+ 0x10,SP0718_P1_0x10, //out
+ 0x14,SP0718_P1_0x14-0x04,
+ 0x11,SP0718_P1_0x11, //nr
+ 0x15,SP0718_P1_0x15-0x04,
+ 0x12,SP0718_P1_0x12, //dummy
+ 0x16,SP0718_P1_0x16-0x04,
+ 0x13,SP0718_P1_0x13, //low
+ 0x17,SP0718_P1_0x17,
+};
+
+uint8_t sp0718_contrast_zero[] = {
+ //level 0
+ 0xfd,0x01,
+ 0x10,SP0718_P1_0x10, //out
+ 0x14,SP0718_P1_0x14,
+ 0x11,SP0718_P1_0x11, //nr
+ 0x15,SP0718_P1_0x15,
+ 0x12,SP0718_P1_0x12, //dummy
+ 0x16,SP0718_P1_0x16,
+ 0x13,SP0718_P1_0x13, //low
+ 0x17,SP0718_P1_0x17,
+};
+
+uint8_t sp0718_contrast_pos1[] = {
+ //level +1
+ 0xfd,0x01,
+ 0x10,SP0718_P1_0x10+0x08, //out
+ 0x14,SP0718_P1_0x14+0x08,
+ 0x11,SP0718_P1_0x11+0x08, //nr
+ 0x15,SP0718_P1_0x15+0x08,
+ 0x12,SP0718_P1_0x12+0x08, //dummy
+ 0x16,SP0718_P1_0x16+0x08,
+ 0x13,SP0718_P1_0x13+0x08, //low
+ 0x17,SP0718_P1_0x17+0x08,
+};
+
+uint8_t sp0718_contrast_pos2[] = {
+ //level +2
+ 0xfd,0x01,
+ 0x10,SP0718_P1_0x10+0x10, //out
+ 0x14,SP0718_P1_0x14+0x10,
+ 0x11,SP0718_P1_0x11+0x10, //nr
+ 0x15,SP0718_P1_0x15+0x10,
+ 0x12,SP0718_P1_0x12+0x10, //dummy
+ 0x16,SP0718_P1_0x16+0x10,
+ 0x13,SP0718_P1_0x13+0x10, //low
+ 0x17,SP0718_P1_0x17+0x10,
+};
+
+uint8_t sp0718_contrast_pos3[] = {
+ //level +3
+ 0xfd,0x01,
+ 0x10,SP0718_P1_0x10+0x18, //out
+ 0x14,SP0718_P1_0x14+0x18,
+ 0x11,SP0718_P1_0x11+0x18, //nr
+ 0x15,SP0718_P1_0x15+0x18,
+ 0x12,SP0718_P1_0x12+0x18, //dummy
+ 0x16,SP0718_P1_0x16+0x18,
+ 0x13,SP0718_P1_0x13+0x18, //low
+ 0x17,SP0718_P1_0x17+0x18,
+};
+
+uint8_t sp0718_contrast_pos4[] = {
+ //level +4
+ 0xfd,0x01,
+ 0x10,SP0718_P1_0x10+0x20, //out
+ 0x14,SP0718_P1_0x14+0x20,
+ 0x11,SP0718_P1_0x11+0x20, //nr
+ 0x15,SP0718_P1_0x15+0x20,
+ 0x12,SP0718_P1_0x12+0x20, //dummy
+ 0x16,SP0718_P1_0x16+0x20,
+ 0x13,SP0718_P1_0x13+0x20, //low
+ 0x17,SP0718_P1_0x17+0x20,
+};
+
+// saturation
+uint8_t sp0718_saturation_neg4[] = {
+ //sat u
+ 0xfd,0x01,
+ 0xd3,SP0718_P1_0xd3-0x20,
+ 0xd4,SP0718_P1_0xd4-0x20,
+ 0xd5,SP0718_P1_0xd5-0x20,
+ 0xd6,SP0718_P1_0xd6-0x20,
+ //sat v -0x20
+ 0xd7,SP0718_P1_0xd7-0x20,
+ 0xd8,SP0718_P1_0xd8-0x20,
+ 0xd9,SP0718_P1_0xd9-0x20,
+ 0xda,SP0718_P1_0xda-0x20,
+};
+
+uint8_t sp0718_saturation_neg3[] = {
+ //sat u
+ 0xfd,0x01,
+ 0xd3,SP0718_P1_0xd3-0x18,
+ 0xd4,SP0718_P1_0xd4-0x18,
+ 0xd5,SP0718_P1_0xd5-0x18,
+ 0xd6,SP0718_P1_0xd6-0x18,
+ //sat v -0x18
+ 0xd7,SP0718_P1_0xd7-0x18,
+ 0xd8,SP0718_P1_0xd8-0x18,
+ 0xd9,SP0718_P1_0xd9-0x18,
+ 0xda,SP0718_P1_0xda-0x18,
+};
+
+uint8_t sp0718_saturation_neg2[] = {
+ //sat u
+ 0xfd,0x01,
+ 0xd3,SP0718_P1_0xd3-0x10,
+ 0xd4,SP0718_P1_0xd4-0x10,
+ 0xd5,SP0718_P1_0xd5-0x10,
+ 0xd6,SP0718_P1_0xd6-0x10,
+ //sat v -0x10
+ 0xd7,SP0718_P1_0xd7-0x10,
+ 0xd8,SP0718_P1_0xd8-0x10,
+ 0xd9,SP0718_P1_0xd9-0x10,
+ 0xda,SP0718_P1_0xda-0x10,
+};
+
+uint8_t sp0718_saturation_neg1[] = {
+ //sat u
+ 0xfd,0x01,
+ 0xd3,SP0718_P1_0xd3-0x08,
+ 0xd4,SP0718_P1_0xd4-0x08,
+ 0xd5,SP0718_P1_0xd5-0x08,
+ 0xd6,SP0718_P1_0xd6-0x08,
+ //sat v -0x08
+ 0xd7,SP0718_P1_0xd7-0x08,
+ 0xd8,SP0718_P1_0xd8-0x08,
+ 0xd9,SP0718_P1_0xd9-0x08,
+ 0xda,SP0718_P1_0xda-0x08,
+};
+
+uint8_t sp0718_saturation_zero[] = {
+ //sat u
+ 0xfd,0x01,
+ 0xd3,SP0718_P1_0xd3,
+ 0xd4,SP0718_P1_0xd4,
+ 0xd5,SP0718_P1_0xd5,
+ 0xd6,SP0718_P1_0xd6,
+ //sat v
+ 0xd7,SP0718_P1_0xd7,
+ 0xd8,SP0718_P1_0xd8,
+ 0xd9,SP0718_P1_0xd9,
+ 0xda,SP0718_P1_0xda,
+};
+
+uint8_t sp0718_saturation_pos1[] = {
+ //sat u
+ 0xfd,0x01,
+ 0xd3,SP0718_P1_0xd3+0x08,
+ 0xd4,SP0718_P1_0xd4+0x08,
+ 0xd5,SP0718_P1_0xd5+0x08,
+ 0xd6,SP0718_P1_0xd6+0x08,
+ //sat v +0x08
+ 0xd7,SP0718_P1_0xd7+0x08,
+ 0xd8,SP0718_P1_0xd8+0x08,
+ 0xd9,SP0718_P1_0xd9+0x08,
+ 0xda,SP0718_P1_0xda+0x08,
+};
+
+uint8_t sp0718_saturation_pos2[] = {
+ //sat u
+ 0xfd,0x01,
+ 0xd3,SP0718_P1_0xd3+0x10,
+ 0xd4,SP0718_P1_0xd4+0x10,
+ 0xd5,SP0718_P1_0xd5+0x10,
+ 0xd6,SP0718_P1_0xd6+0x10,
+ //sat v +0x10
+ 0xd7,SP0718_P1_0xd7+0x10,
+ 0xd8,SP0718_P1_0xd8+0x10,
+ 0xd9,SP0718_P1_0xd9+0x10,
+ 0xda,SP0718_P1_0xda+0x10,
+};
+
+uint8_t sp0718_saturation_pos3[] = {
+ //sat u
+ 0xfd,0x01,
+ 0xd3,SP0718_P1_0xd3+0x18,
+ 0xd4,SP0718_P1_0xd4+0x18,
+ 0xd5,SP0718_P1_0xd5+0x18,
+ 0xd6,SP0718_P1_0xd6+0x18,
+ //sat v +0x18
+ 0xd7,SP0718_P1_0xd7+0x18,
+ 0xd8,SP0718_P1_0xd8+0x18,
+ 0xd9,SP0718_P1_0xd9+0x18,
+ 0xda,SP0718_P1_0xda+0x18,
+};
+
+uint8_t sp0718_saturation_pos4[] = {
+ //sat u
+ 0xfd,0x01,
+ 0xd3,SP0718_P1_0xd3+0x20,
+ 0xd4,SP0718_P1_0xd4+0x20,
+ 0xd5,SP0718_P1_0xd5+0x20,
+ 0xd6,SP0718_P1_0xd6+0x20,
+ //sat v +0x20
+ 0xd7,SP0718_P1_0xd7+0x20,
+ 0xd8,SP0718_P1_0xd8+0x20,
+ 0xd9,SP0718_P1_0xd9+0x20,
+ 0xda,SP0718_P1_0xda+0x20,
+};
+
+
+// Resolution
+
+// QCIF
+uint8_t sp0718_176x144[]={
+};
+
+// QVGA
+uint8_t sp0718_320x240[]={
+};
+
+// CIF
+uint8_t sp0718_352x288[]={
+};
+
+// VGA
+uint8_t sp0718_640x480[]={
+ 0xfd,0x01,
+ 0x4a,0x00,
+ 0x4b,0x01,
+ 0x4c,0xe0,
+ 0x4d,0x00,
+ 0x4e,0x02,
+ 0x4f,0x80,
+
+ 0xfd,0x02,
+ 0x0f,0x00, //1//subsampledisable
+
+ 0xfd,0x00,
+ 0x30,0x00,
+};
+
+// SVGA
+uint8_t sp0718_800x600[]={
+};
+
+// XGA
+uint8_t sp0718_1024x768[]={
+};
+
+// 720p
+uint8_t sp0718_1280x720[]={
+};
+
+// UXGA
+uint8_t sp0718_1600x1200[]={
+};
+
+// 1080p
+uint8_t sp0718_1920x1080[]={
+};
+
+// QXGA
+uint8_t sp0718_2048x1536[]={
+};
+
+uint8_t sp0718_2592x1944[]={
+};
+
+// Initiliztion
+uint8_t sp0718_default_regs_init[] = {
+ 0xfd,0x00,
+ 0x1C,0x00,
+ 0x31,0x10,
+ 0x27,0xb3,//0xb3 //2x gain
+
+ 0x1b,0x17, // driver
+ 0x1f,0x30,
+ 0x30,0x00, // div clk
+
+ 0x26,0xaa,
+ 0x37,0x02,
+ 0x28,0x8f,
+ 0x1a,0x73,
+ 0x1e,0x1b,
+ 0x21,0x06, //blackout voltage
+ 0x22,0x2a, //colbias
+ 0x0f,0x3f,
+ 0x10,0x3e,
+ 0x11,0x00,
+ 0x12,0x01,
+ 0x13,0x3f,
+ 0x14,0x04,
+ 0x15,0x30,
+ 0x16,0x31,
+ 0x17,0x01,
+ 0x69,0x31,
+ 0x6a,0x2a,
+ 0x6b,0x33,
+ 0x6c,0x1a,
+ 0x6d,0x32,
+ 0x6e,0x28,
+ 0x6f,0x29,
+ 0x70,0x34,
+ 0x71,0x18,
+ 0x36,0x00,//02 delete badframe
+ 0xfd,0x01,
+ 0x5d,0x51,//position
+ 0xf2,0x19,
+
+ //Blacklevel
+ 0x1f,0x10,
+ 0x20,0x1f,
+ //pregain
+ 0xfd,0x02,
+ 0x00,0x88,
+ 0x01,0x88,
+ //SI15_SP0718 24M 50Hz 15-8fps
+
+ 0xfd,0x00,
+ 0x03,0x01,
+ 0x04,0xb0,
+ 0x06,0x00,
+ 0x09,0x03,
+ 0x0a,0x31,
+ 0xfd,0x01,
+ 0xef,0x48,
+ 0xf0,0x00,
+ 0x02,0x0e,
+ 0x03,0x01,
+ 0x06,0x42,
+ 0x07,0x00,
+ 0x08,0x01,
+ 0x09,0x00,
+ //Status,
+ 0xfd,0x02,
+ 0xbe,0xf0,
+ 0xbf,0x03,
+ 0xd0,0xf0,
+ 0xd1,0x03,
+ 0xfd,0x01,
+ 0x5b,0x03,
+ 0x5c,0xf0,
+
+ //rpc
+ 0xfd,0x01,
+ 0xe0,0x40,////24//4c//48//4c//44//4c//3e//3c//3a//38//rpc_1base_max
+ 0xe1,0x30,////24//3c//38//3c//36//3c//30//2e//2c//2a//rpc_2base_max
+ 0xe2,0x2e,////24//34//30//34//2e//34//2a//28//26//26//rpc_3base_max
+ 0xe3,0x2a,////24//2a//2e//2c//2e//2a//2e//26//24//22//rpc_4base_max
+ 0xe4,0x2a,////24//2a//2e//2c//2e//2a//2e//26//24//22//rpc_5base_max
+ 0xe5,0x28,////24//2c//2a//2c//28//2c//24//22//20//rpc_6base_max
+ 0xe6,0x28,////24//2c//2a//2c//28//2c//24//22//20//rpc_7base_max
+ 0xe7,0x26,////24//2a//28//2a//26//2a//22//20//20//1e//rpc_8base_max
+ 0xe8,0x26,////24//2a//28//2a//26//2a//22//20//20//1e//rpc_9base_max
+ 0xe9,0x26,////24//2a//28//2a//26//2a//22//20//20//1e//rpc_10base_max
+ 0xea,0x26,////24//28//26//28//24//28//20//1f//1e//1d//rpc_11base_max
+ 0xf3,0x26,////24//28//26//28//24//28//20//1f//1e//1d//rpc_12base_max
+ 0xf4,0x26,////24//28//26//28//24//28//20//1f//1e//1d//rpc_13base_max
+ //ae gain &status
+ 0xfd,0x01,
+ 0x04,0xe0,//rpc_max_indr
+ 0x05,0x26,//1e//rpc_min_indr
+ 0x0a,0xa0,//rpc_max_outdr
+ 0x0b,0x26,//rpc_min_outdr
+ 0x5a,0x40,//dp rpc
+ 0xfd,0x02,
+ 0xbc,0xa0,//rpc_heq_low
+ 0xbd,0x80,//rpc_heq_dummy
+ 0xb8,0x80,//mean_normal_dummy
+ 0xb9,0x90,//mean_dummy_normal
+
+ //ae target
+ 0xfd,0x01,
+ 0xeb,SP0718_P1_0xeb,//78
+ 0xec,SP0718_P1_0xec,//78
+ 0xed,0x0a,
+ 0xee,0x10,
+
+ //lsc
+ 0xfd,0x01,
+ 0x26,0x30,
+ 0x27,0x2c,
+ 0x28,0x07,
+ 0x29,0x08,
+ 0x2a,0x40,
+ 0x2b,0x03,
+ 0x2c,0x00,
+ 0x2d,0x00,
+
+ 0xa1,0x24,
+ 0xa2,0x27,
+ 0xa3,0x27,
+ 0xa4,0x2b,
+ 0xa5,0x1c,
+ 0xa6,0x1a,
+ 0xa7,0x1a,
+ 0xa8,0x1a,
+ 0xa9,0x18,
+ 0xaa,0x1c,
+ 0xab,0x17,
+ 0xac,0x17,
+ 0xad,0x08,
+ 0xae,0x08,
+ 0xaf,0x08,
+ 0xb0,0x00,
+ 0xb1,0x00,
+ 0xb2,0x00,
+ 0xb3,0x00,
+ 0xb4,0x00,
+ 0xb5,0x02,
+ 0xb6,0x06,
+ 0xb7,0x00,
+ 0xb8,0x00,
+
+
+ //DP
+ 0xfd,0x01,
+ 0x48,0x09,
+ 0x49,0x99,
+
+ //awb
+ 0xfd,0x01,
+ 0x32,0x05,
+ 0xfd,0x00,
+ 0xe7,0x03,
+ 0xfd,0x02,
+ 0x26,0xc8,
+ 0x27,0xb6,
+ 0xfd,0x00,
+ 0xe7,0x00,
+ 0xfd,0x02,
+ 0x1b,0x80,
+ 0x1a,0x80,
+ 0x18,0x26,
+ 0x19,0x28,
+ 0xfd,0x02,
+ 0x2a,0x00,
+ 0x2b,0x08,
+ 0x28,0xef,//0xa0//f8
+ 0x29,0x20,
+
+ //d65 90 e2 93
+ 0x66,0x42,//0x59//0x60////0x58//4e//0x48
+ 0x67,0x62,//0x74//0x70//0x78//6b//0x69
+ 0x68,0xee,//0xd6//0xe3//0xd5//cb//0xaa
+ 0x69,0x18,//0xf4//0xf3//0xf8//ed
+ 0x6a,0xa6,//0xa5
+ //indoor 91
+ 0x7c,0x3b,//0x45//30//41//0x2f//0x44
+ 0x7d,0x5b,//0x70//60//55//0x4b//0x6f
+ 0x7e,0x15,//0a//0xed
+ 0x7f,0x39,//23//0x28
+ 0x80,0xaa,//0xa6
+ //cwf 92
+ 0x70,0x3e,//0x38//41//0x3b
+ 0x71,0x59,//0x5b//5f//0x55
+ 0x72,0x31,//0x30//22//0x28
+ 0x73,0x4f,//0x54//44//0x45
+ 0x74,0xaa,
+ //tl84 93
+ 0x6b,0x1b,//0x18//11
+ 0x6c,0x3a,//0x3c//25//0x2f
+ 0x6d,0x3e,//0x3a//35
+ 0x6e,0x59,//0x5c//46//0x52
+ 0x6f,0xaa,
+ //f 94
+ 0x61,0xea,//0x03//0x00//f4//0xed
+ 0x62,0x03,//0x1a//0x25//0f//0f
+ 0x63,0x6a,//0x62//0x60//52//0x5d
+ 0x64,0x8a,//0x7d//0x85//70//0x75//0x8f
+ 0x65,0x6a,//0xaa//6a
+
+ 0x75,0x80,
+ 0x76,0x20,
+ 0x77,0x00,
+ 0x24,0x25,
+
+ //Õë¶ÔÊÒÄÚµ÷Æ«²»¹ýµÆÏä²âÊÔʹÓÃ//Õë¶ÔÈËÁ³µ÷Æ«
+ 0x20,0xd8,
+ 0x21,0xa3,//82//a8Æ«°µÕնȻ¹Óе÷Æ«
+ 0x22,0xd0,//e3//bc
+ 0x23,0x86,
+
+ //outdoor r\b range
+ 0x78,0xc3,//d8
+ 0x79,0xba,//82
+ 0x7a,0xa6,//e3
+ 0x7b,0x99,//86
+
+
+ //skin
+ 0x08,0x15,//
+ 0x09,0x04,//
+ 0x0a,0x20,//
+ 0x0b,0x12,//
+ 0x0c,0x27,//
+ 0x0d,0x06,//
+ 0x0e,0x63,//
+
+ //wt th
+ 0x3b,0x10,
+ //gw
+ 0x31,0x60,
+ 0x32,0x60,
+ 0x33,0xc0,
+ 0x35,0x6f,
+
+ // sharp
+ 0xfd,0x02,
+ 0xde,0x0f,
+ 0xd2,0x02,//6//¿ØÖƺڰױߣ»0-±ß´Ö£¬f-±äϸ
+ 0xd3,0x06,
+ 0xd4,0x06,
+ 0xd5,0x06,
+ 0xd7,0x20,//10//2x¸ù¾ÝÔöÒæÅжÏÂÖÀªãÐÖµ
+ 0xd8,0x30,//24//1A//4x
+ 0xd9,0x38,//28//8x
+ 0xda,0x38,//16x
+ 0xdb,0x08,//
+ 0xe8,0x58,//48//ÂÖÀªÇ¿¶È
+ 0xe9,0x48,
+ 0xea,0x30,
+ 0xeb,0x20,
+ 0xec,0x48,//60//80
+ 0xed,0x48,//50//60
+ 0xee,0x30,
+ 0xef,0x20,
+ //ƽ̹ÇøÓòÈñ»¯Á¦¶È
+ 0xf3,0x50,
+ 0xf4,0x10,
+ 0xf5,0x10,
+ 0xf6,0x10,
+ //dns
+ 0xfd,0x01,
+ 0x64,0x44,//ÑØ·½Ïò±ßԵƽ»¬Á¦¶È //0-×îÇ¿£¬8-×îÈõ
+ 0x65,0x22,
+ 0x6d,0x04,//8//ǿƽ»¬£¨Æ½Ì¹£©ÇøÓòƽ»¬ãÐÖµ
+ 0x6e,0x06,//8
+ 0x6f,0x10,
+ 0x70,0x10,
+ 0x71,0x08,//0d//Èõƽ»¬£¨·Çƽ̹£©ÇøÓòƽ»¬ãÐÖµ
+ 0x72,0x12,//1b
+ 0x73,0x1c,//20
+ 0x74,0x24,
+ 0x75,0x44,//[7:4]ƽ̹ÇøÓòÇ¿¶È£¬[3:0]·Çƽ̹ÇøÓòÇ¿¶È£»0-×îÇ¿£¬8-×îÈõ£»
+ 0x76,0x02,//46
+ 0x77,0x02,//33
+ 0x78,0x02,
+ 0x81,0x10,//18//2x//¸ù¾ÝÔöÒæÅж¨ÇøÓòãÐÖµ£¬µÍÓÚÕâ¸ö×öǿƽ»¬¡¢´óÓÚÕâ¸ö×öÈõƽ»¬£»
+ 0x82,0x20,//30//4x
+ 0x83,0x30,//40//8x
+ 0x84,0x48,//50//16x
+ 0x85,0x0c,//12/8+reg0x81 µÚ¶þãÐÖµ£¬ÔÚƽ̹ºÍ·Çƽ̹ÇøÓò×öÁ¬½Ó
+ 0xfd,0x02,
+ 0xdc,0x0f,
+
+ //gamma
+ 0xfd,0x01,
+ 0x8b,0x00,//00//00
+ 0x8c,0x0c,//0c//09
+ 0x8d,0x19,//19//17
+ 0x8e,0x25,//25//24
+ 0x8f,0x30,//30//33
+ 0x90,0x44,//44//47
+ 0x91,0x4e,//54//58
+ 0x92,0x5f,//61//64
+ 0x93,0x6c,//6d//70
+ 0x94,0x82,//80//81
+ 0x95,0x94,//92//8f
+ 0x96,0xa6,//a1//9b
+ 0x97,0xb2,//ad//a5
+ 0x98,0xbf,//ba//b0
+ 0x99,0xc9,//c4//ba
+ 0x9a,0xd1,//cf//c4
+ 0x9b,0xd8,//d7//ce
+ 0x9c,0xe0,//e0//d7
+ 0x9d,0xe8,//e8//e1
+ 0x9e,0xef,//ef//ea
+ 0x9f,0xf8,//f7//f5
+ 0xa0,0xff,//ff//ff
+ //CCM
+ 0xfd,0x02,
+ 0x15,0xd0,//b>th
+ 0x16,0x95,//r<th
+ //gc¾µÍ·ÕÕÈËÁ³Æ«»Æ
+ //!F
+ 0xa0,0x80,//80
+ 0xa1,0x00,//00
+ 0xa2,0x00,//00
+ 0xa3,0x00,//06
+ 0xa4,0x8c,//8c
+ 0xa5,0xf4,//ed
+ 0xa6,0x0c,//0c
+ 0xa7,0xf4,//f4
+ 0xa8,0x80,//80
+ 0xa9,0x00,//00
+ 0xaa,0x30,//30
+ 0xab,0x0c,//0c
+ //F
+ 0xac,0x8c,
+ 0xad,0xf4,
+ 0xae,0x00,
+ 0xaf,0xed,
+ 0xb0,0x8c,
+ 0xb1,0x06,
+ 0xb2,0xf4,
+ 0xb3,0xf4,
+ 0xb4,0x99,
+ 0xb5,0x0c,
+ 0xb6,0x03,
+ 0xb7,0x0f,
+
+ //sat u
+ 0xfd,0x01,
+ 0xd3,SP0718_P1_0xd3,//0x88//50
+ 0xd4,SP0718_P1_0xd4,//0x88//50
+ 0xd5,SP0718_P1_0xd5,//50
+ 0xd6,SP0718_P1_0xd6,//50
+ //sat v
+ 0xd7,SP0718_P1_0xd7,//0x88//50
+ 0xd8,SP0718_P1_0xd8,//0x88//50
+ 0xd9,SP0718_P1_0xd9,//50
+ 0xda,SP0718_P1_0xda,//50
+ //auto_sat
+ 0xdd,0x30,
+ 0xde,0x10,
+ 0xd2,0x01,//autosa_en
+ 0xdf,0xff,//a0//y_mean_th
+
+ //uv_th
+ 0xfd,0x01,
+ 0xc2,0xaa,
+ 0xc3,0xaa,
+ 0xc4,0x66,
+ 0xc5,0x66,
+
+ //heq
+ 0xfd,0x01,
+ 0x0f,0xff,
+ 0x10,SP0718_P1_0x10, //out
+ 0x14,SP0718_P1_0x14,
+ 0x11,SP0718_P1_0x11, //nr
+ 0x15,SP0718_P1_0x15,
+ 0x12,SP0718_P1_0x12, //dummy
+ 0x16,SP0718_P1_0x16,
+ 0x13,SP0718_P1_0x13, //low
+ 0x17,SP0718_P1_0x17,
+
+ 0xfd,0x01,
+ 0xcd,0x20,
+ 0xce,0x1f,
+ 0xcf,0x20,
+ 0xd0,0x55,
+ //auto
+ 0xfd,0x01,
+ 0xfb,0x33,
+ //0x32,0x15,
+ 0x33,0xff,
+ 0x34,0xe7,
+ 0x35,0x40,
+ 0x36,0x08,
+};
+
+// Exit
+uint8_t sp0718_default_regs_exit[] = {
+};
+
+#endif /* #ifndef __SP0718_H_ */
diff --git a/drivers/media/video/wmt_v4l2/sensors/sp0718/sp0718.c b/drivers/media/video/wmt_v4l2/sensors/sp0718/sp0718.c
new file mode 100755
index 00000000..5ecd7a0c
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/sp0718/sp0718.c
@@ -0,0 +1,344 @@
+
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "sp0718.h"
+
+#define sensor_write_array(sd, arr, sz) \
+ cmos_init_8bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write(sd->i2c_addr, reg, val)
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint8_t *regs;
+ size_t size;
+};
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+// CMOS_WIN_SIZE("QVGA", 320, 240, sp0718_320x240),
+ CMOS_WIN_SIZE("VGA", 640, 480, sp0718_640x480),
+};
+
+#define FILP_REG_INIT_VALUE 0x10
+#define DELAY_INTERVAL 50
+#define RETRY_TIMES 10
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = sp0718_wb_auto;
+ size = ARRAY_SIZE(sp0718_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = sp0718_wb_incandescent;
+ size = ARRAY_SIZE(sp0718_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = sp0718_wb_daylight;
+ size = ARRAY_SIZE(sp0718_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = sp0718_wb_cloudy;
+ size = ARRAY_SIZE(sp0718_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = sp0718_wb_fluorescent;
+ size = ARRAY_SIZE(sp0718_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_scenemode(struct cmos_subdev *sd, enum v4l2_scene_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case SCENE_MODE_AUTO:
+ regs = sp0718_scene_mode_auto;
+ size = ARRAY_SIZE(sp0718_scene_mode_auto);
+ break;
+ case SCENE_MODE_NIGHTSHOT:
+ regs = sp0718_scene_mode_night;
+ size = ARRAY_SIZE(sp0718_scene_mode_night);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_exposure(struct cmos_subdev *sd, int value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case -2:
+ regs = sp0718_exposure_neg2;
+ size = ARRAY_SIZE(sp0718_exposure_neg2);
+ break;
+ case -1:
+ regs = sp0718_exposure_neg1;
+ size = ARRAY_SIZE(sp0718_exposure_neg1);
+ break;
+ case 0:
+ regs = sp0718_exposure_zero;
+ size = ARRAY_SIZE(sp0718_exposure_zero);
+ break;
+ case 1:
+ regs = sp0718_exposure_pos1;
+ size = ARRAY_SIZE(sp0718_exposure_pos1);
+ break;
+ case 2:
+ regs = sp0718_exposure_pos2;
+ size = ARRAY_SIZE(sp0718_exposure_pos2);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data,tmp=0;
+ uint8_t retry_times=0;
+
+ sensor_write(sd, 0xfd, 0x00); // set page0
+ data = sensor_read(sd, 0x31);
+ switch (value) {
+ case 0:
+ data &= ~0x20;
+ break;
+ case 1:
+ data |= 0x20;
+ break;
+ default:
+ return -EINVAL;
+ }
+ tmp=data | FILP_REG_INIT_VALUE;
+ for(retry_times=0;retry_times<RETRY_TIMES;retry_times++){
+ sensor_write(sd, 0x31, tmp);
+ msleep(50+retry_times*DELAY_INTERVAL);
+ if(sensor_read(sd, 0x31)==tmp)
+ break;
+ }
+ printk(KERN_DEBUG "%s, 0x%x\n", __func__, data);
+ return 0;
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data,tmp=0;
+ uint8_t retry_times=0;
+
+ sensor_write(sd, 0xfd, 0x00); // set page0
+ data = sensor_read(sd, 0x31);
+ switch (value) {
+ case 0:
+ data &= ~0x40;
+ break;
+ case 1:
+ data |= 0x40;
+ break;
+ default:
+ return -EINVAL;
+ }
+ tmp=data | FILP_REG_INIT_VALUE;
+ for(retry_times=0;retry_times<RETRY_TIMES;retry_times++){
+ sensor_write(sd, 0x31, tmp);
+ msleep(50+retry_times*DELAY_INTERVAL);
+ if(sensor_read(sd, 0x31)==tmp)
+ break;
+ }
+ printk(KERN_DEBUG "%s, 0x%x\n", __func__, data);
+ return 0;
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ case V4L2_CID_EXPOSURE:
+ return v4l2_ctrl_query_fill(qc, -2, 2, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return sensor_s_scenemode(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return sensor_s_exposure(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ default:
+ case WMT_V4L2_CID_CAMERA_ANTIBANDING:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win) {
+ pr_err("%s, s_mbus_fmt failed, width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, win->regs, win->size);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return 0;
+}
+
+static int sensor_enum_framesizes(struct cmos_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ const struct cmos_win_size *win = &cmos_supported_win_sizes[index];
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ uint8_t data = 0;
+
+ sensor_write(sd, 0xfd, 0x0);
+ data = sensor_read(sd, 0x02);
+
+ return (data == sd->id) ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ if (sensor_identify(sd)) {
+ return -1;
+ }
+
+ sensor_write_array(sd, sp0718_default_regs_init,
+ ARRAY_SIZE(sp0718_default_regs_init));
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ sensor_write_array(sd, sp0718_default_regs_exit,
+ ARRAY_SIZE(sp0718_default_regs_exit));
+ return 0;
+}
+
+static struct cmos_subdev_ops sp0718_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+ .enum_framesizes = sensor_enum_framesizes,
+};
+
+struct cmos_subdev sp0718 = {
+ .name = "sp0718",
+ .i2c_addr = 0x21,
+ .id = 0x71,
+ .max_width = 640,
+ .max_height = 480,
+ .ops = &sp0718_ops,
+};
+
+#if 0
+static int __init sp0718_init(void)
+{
+ return cmos_register_sudbdev(&sp0718);
+}
+
+static void __exit sp0718_exit(void)
+{
+ return cmos_unregister_subdev(&sp0718);
+}
+
+module_init(sp0718_init);
+module_exit(sp0718_exit);
+
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/sp0718/sp0718.h b/drivers/media/video/wmt_v4l2/sensors/sp0718/sp0718.h
new file mode 100755
index 00000000..e868f6c0
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/sp0718/sp0718.h
@@ -0,0 +1,990 @@
+
+#ifndef __SP0718_H_
+#define __SP0718_H_
+
+#define SP0718_NORMAL_Y0ffset 0x20
+#define SP0718_LOWLIGHT_Y0ffset 0x25
+//AE target
+#define SP0718_P1_0xeb 0x78
+#define SP0718_P1_0xec 0x6c
+//HEQ
+#define SP0718_P1_0x10 0x00//outdoor
+#define SP0718_P1_0x14 0x20
+#define SP0718_P1_0x11 0x00//nr
+#define SP0718_P1_0x15 0x14
+#define SP0718_P1_0x12 0x00//dummy
+#define SP0718_P1_0x16 0x10
+#define SP0718_P1_0x13 0x00//low
+#define SP0718_P1_0x17 0x08
+//AWB pre gain
+#define SP0718_P2_0x26 0xc8
+#define SP0718_P2_0x27 0xb6
+//SAT u
+#define SP0718_P1_0xd3 0x9c
+#define SP0718_P1_0xd4 0x98
+#define SP0718_P1_0xd5 0x8c
+#define SP0718_P1_0xd6 0x84
+//SAT v
+#define SP0718_P1_0xd7 0x9c
+#define SP0718_P1_0xd8 0x98
+#define SP0718_P1_0xd9 0x8c
+#define SP0718_P1_0xda 0x84
+
+
+// Scene Mode
+uint8_t sp0718_scene_mode_auto[] = {
+ 0xfd,0x01,
+ 0xcd,SP0718_NORMAL_Y0ffset,
+ 0xce,0x1f,
+ //capture preview daylight 24M 50hz 15-8FPS
+ 0xfd,0x00,
+ 0x03,0x01,
+ 0x04,0xce,
+ 0x06,0x00,
+ 0x09,0x02,
+ 0x0a,0xc4,
+ 0xfd,0x01,
+ 0xef,0x4d,
+ 0xf0,0x00,
+ 0x02,0x0c,
+ 0x03,0x01,
+ 0x06,0x47,
+ 0x07,0x00,
+ 0x08,0x01,
+ 0x09,0x00,
+ //Status
+ 0xfd,0x02,
+ 0xbe,0x9c,
+ 0xbf,0x03,
+ 0xd0,0x9c,
+ 0xd1,0x03,
+ 0xfd,0x01,
+ 0x5b,0x03,
+ 0x5c,0x9c,
+
+ 0xfd,0x00,
+};
+
+uint8_t sp0718_scene_mode_night[] = {
+ 0xfd,0x01,
+ 0xcd,SP0718_LOWLIGHT_Y0ffset,
+ 0xce,0x1f,
+ //capture preview night 24M 50hz 15-6FPS
+ 0xfd,0x00,
+ 0x03,0x01,
+ 0x04,0xce,
+ 0x06,0x00,
+ 0x09,0x02,
+ 0x0a,0xc4,
+ 0xfd,0x01,
+ 0xef,0x4d,
+ 0xf0,0x00,
+ 0x02,0x10,
+ 0x03,0x01,
+ 0x06,0x47,
+ 0x07,0x00,
+ 0x08,0x01,
+ 0x09,0x00,
+ //Statu
+ 0xfd,0x02,
+ 0xbe,0xd0,
+ 0xbf,0x04,
+ 0xd0,0xd0,
+ 0xd1,0x04,
+ 0xfd,0x01,
+ 0x5b,0x04,
+ 0x5c,0xd0,
+
+ 0xfd,0x00,
+};
+
+// White Balance
+uint8_t sp0718_wb_auto[] = {
+ 0xfd,0x01,
+ 0x32,0x15,
+ 0xfd,0x02,
+ 0x26,0xc8,
+ 0x27,0xb6,
+ 0xfd,0x00,
+};
+
+uint8_t sp0718_wb_incandescent[] = {
+ 0xfd,0x01,
+ 0x32,0x05,
+ 0xfd,0x02,
+ 0x26,0xaa,
+ 0x27,0xce,
+ 0xfd,0x00,
+};
+
+uint8_t sp0718_wb_fluorescent[] = {
+ 0xfd,0x01,
+ 0x32,0x05,
+ 0xfd,0x02,
+ 0x26,0x91,
+ 0x27,0xc8,
+ 0xfd,0x00,
+};
+
+uint8_t sp0718_wb_daylight[] = {
+ 0xfd,0x01,
+ 0x32,0x05,
+ 0xfd,0x02,
+ 0x26,0xc8,
+ 0x27,0x89,
+ 0xfd,0x00,
+};
+
+uint8_t sp0718_wb_cloudy[] = {
+ 0xfd,0x01,
+ 0x32,0x05,
+ 0xfd,0x02,
+ 0x26,0xdc,
+ 0x27,0x75,
+ 0xfd,0x00,
+};
+
+uint8_t sp0718_wb_tungsten[] = {
+ 0xfd,0x01,
+ 0x32,0x05,
+ 0xfd,0x02,
+ 0x26,0x75,
+ 0x27,0xe2,
+ 0xfd,0x00,
+};
+
+
+// Exposure
+uint8_t sp0718_exposure_neg2[] = {
+ 0xfd,0x01,
+ 0xdb,0xe0,//level -3
+};
+
+uint8_t sp0718_exposure_neg1[] = {
+ 0xfd,0x01,
+ 0xdb,0xf0,
+};
+
+uint8_t sp0718_exposure_zero[] = {
+ 0xfd,0x01,
+ 0xdb,0x00,//level -3
+};
+
+uint8_t sp0718_exposure_pos1[] = {
+ 0xfd,0x01,
+ 0xdb,0x10,
+};
+
+uint8_t sp0718_exposure_pos2[] = {
+ 0xfd,0x01,
+ 0xdb,0x20,
+};
+
+
+// Color Effect
+uint8_t sp0718_colorfx_none[] = {
+ 0xfd, 0x01,
+ 0x66, 0x00,
+ 0x67, 0x80,
+ 0x68, 0x80,
+};
+
+uint8_t sp0718_colorfx_bw[] = {
+ 0xfd, 0x01,
+ 0x66, 0x20,
+ 0x67, 0x80,
+ 0x68, 0x80,
+};
+
+uint8_t sp0718_colorfx_sepia[] = {
+ 0xfd, 0x01,
+ 0x66, 0x10,
+ 0x67, 0xc0,
+ 0x68, 0x20,
+};
+
+uint8_t sp0718_colorfx_negative[] = {
+ //Negative
+ 0xfd, 0x01,
+ 0x66, 0x04,
+ 0x67, 0x80,
+ 0x68, 0x80,
+};
+
+uint8_t sp0718_colorfx_emboss[] = {
+};
+
+uint8_t sp0718_colorfx_sketch[] = {
+};
+
+uint8_t sp0718_colorfx_sky_blue[] = {
+ 0xfd, 0x01,
+ 0x66, 0x10,
+ 0x67, 0x20,
+ 0x68, 0xf0,
+};
+
+uint8_t sp0718_colorfx_grass_green[] = {
+ 0xfd, 0x01,
+ 0x66, 0x10,
+ 0x67, 0x20,
+ 0x68, 0x20,
+};
+
+uint8_t sp0718_colorfx_skin_whiten[] = {
+};
+
+uint8_t sp0718_colorfx_vivid[] = {
+};
+
+// Brightness
+uint8_t sp0718_brightness_neg4[] = {
+ 0xfd,0x01,
+ 0xdb,0xc0,
+};
+
+uint8_t sp0718_brightness_neg3[] = {
+ 0xfd,0x01,
+ 0xdb,0xd0,
+};
+
+uint8_t sp0718_brightness_neg2[] = {
+ 0xfd,0x01,
+ 0xdb,0xe0,
+};
+
+uint8_t sp0718_brightness_neg1[] = {
+ 0xfd,0x01,
+ 0xdb,0xf0,
+};
+
+uint8_t sp0718_brightness_zero[] = {
+ 0xfd,0x01,
+ 0xdb,0x00
+};
+
+uint8_t sp0718_brightness_pos1[] = {
+ 0xfd,0x01,
+ 0xdb,0x10,
+};
+
+uint8_t sp0718_brightness_pos2[] = {
+ 0xfd,0x01,
+ 0xdb,0x20,
+};
+
+uint8_t sp0718_brightness_pos3[] = {
+ 0xfd,0x01,
+ 0xdb,0x30,
+};
+
+uint8_t sp0718_brightness_pos4[] = {
+ 0xfd,0x01,
+ 0xdb,0x40,
+};
+
+
+// contrast
+uint8_t sp0718_contrast_neg4[] = {
+ //level -4
+ 0xfd,0x01,
+ 0x10,SP0718_P1_0x10, //out
+ 0x14,SP0718_P1_0x14-0x10,
+ 0x11,SP0718_P1_0x11, //nr
+ 0x15,SP0718_P1_0x15-0x10,
+ 0x12,SP0718_P1_0x12, //dummy
+ 0x16,SP0718_P1_0x16-0x10,
+ 0x13,SP0718_P1_0x13, //low
+ 0x17,SP0718_P1_0x17,
+};
+
+uint8_t sp0718_contrast_neg3[] = {
+ //level -3
+ 0xfd,0x01,
+ 0x10,SP0718_P1_0x10, //out
+ 0x14,SP0718_P1_0x14-0x0c,
+ 0x11,SP0718_P1_0x11, //nr
+ 0x15,SP0718_P1_0x15-0x0c,
+ 0x12,SP0718_P1_0x12, //dummy
+ 0x16,SP0718_P1_0x16-0x0c,
+ 0x13,SP0718_P1_0x13, //low
+ 0x17,SP0718_P1_0x17,
+};
+
+uint8_t sp0718_contrast_neg2[] = {
+ //level -2
+ 0xfd,0x01,
+ 0x10,SP0718_P1_0x10, //out
+ 0x14,SP0718_P1_0x14-0x08,
+ 0x11,SP0718_P1_0x11, //nr
+ 0x15,SP0718_P1_0x15-0x08,
+ 0x12,SP0718_P1_0x12, //dummy
+ 0x16,SP0718_P1_0x16-0x08,
+ 0x13,SP0718_P1_0x13, //low
+ 0x17,SP0718_P1_0x17,
+};
+
+uint8_t sp0718_contrast_neg1[] = {
+ //level -1
+ 0xfd,0x01,
+ 0x10,SP0718_P1_0x10, //out
+ 0x14,SP0718_P1_0x14-0x04,
+ 0x11,SP0718_P1_0x11, //nr
+ 0x15,SP0718_P1_0x15-0x04,
+ 0x12,SP0718_P1_0x12, //dummy
+ 0x16,SP0718_P1_0x16-0x04,
+ 0x13,SP0718_P1_0x13, //low
+ 0x17,SP0718_P1_0x17,
+};
+
+uint8_t sp0718_contrast_zero[] = {
+ //level 0
+ 0xfd,0x01,
+ 0x10,SP0718_P1_0x10, //out
+ 0x14,SP0718_P1_0x14,
+ 0x11,SP0718_P1_0x11, //nr
+ 0x15,SP0718_P1_0x15,
+ 0x12,SP0718_P1_0x12, //dummy
+ 0x16,SP0718_P1_0x16,
+ 0x13,SP0718_P1_0x13, //low
+ 0x17,SP0718_P1_0x17,
+};
+
+uint8_t sp0718_contrast_pos1[] = {
+ //level +1
+ 0xfd,0x01,
+ 0x10,SP0718_P1_0x10+0x08, //out
+ 0x14,SP0718_P1_0x14+0x08,
+ 0x11,SP0718_P1_0x11+0x08, //nr
+ 0x15,SP0718_P1_0x15+0x08,
+ 0x12,SP0718_P1_0x12+0x08, //dummy
+ 0x16,SP0718_P1_0x16+0x08,
+ 0x13,SP0718_P1_0x13+0x08, //low
+ 0x17,SP0718_P1_0x17+0x08,
+};
+
+uint8_t sp0718_contrast_pos2[] = {
+ //level +2
+ 0xfd,0x01,
+ 0x10,SP0718_P1_0x10+0x10, //out
+ 0x14,SP0718_P1_0x14+0x10,
+ 0x11,SP0718_P1_0x11+0x10, //nr
+ 0x15,SP0718_P1_0x15+0x10,
+ 0x12,SP0718_P1_0x12+0x10, //dummy
+ 0x16,SP0718_P1_0x16+0x10,
+ 0x13,SP0718_P1_0x13+0x10, //low
+ 0x17,SP0718_P1_0x17+0x10,
+};
+
+uint8_t sp0718_contrast_pos3[] = {
+ //level +3
+ 0xfd,0x01,
+ 0x10,SP0718_P1_0x10+0x18, //out
+ 0x14,SP0718_P1_0x14+0x18,
+ 0x11,SP0718_P1_0x11+0x18, //nr
+ 0x15,SP0718_P1_0x15+0x18,
+ 0x12,SP0718_P1_0x12+0x18, //dummy
+ 0x16,SP0718_P1_0x16+0x18,
+ 0x13,SP0718_P1_0x13+0x18, //low
+ 0x17,SP0718_P1_0x17+0x18,
+};
+
+uint8_t sp0718_contrast_pos4[] = {
+ //level +4
+ 0xfd,0x01,
+ 0x10,SP0718_P1_0x10+0x20, //out
+ 0x14,SP0718_P1_0x14+0x20,
+ 0x11,SP0718_P1_0x11+0x20, //nr
+ 0x15,SP0718_P1_0x15+0x20,
+ 0x12,SP0718_P1_0x12+0x20, //dummy
+ 0x16,SP0718_P1_0x16+0x20,
+ 0x13,SP0718_P1_0x13+0x20, //low
+ 0x17,SP0718_P1_0x17+0x20,
+};
+
+// saturation
+uint8_t sp0718_saturation_neg4[] = {
+ //sat u
+ 0xfd,0x01,
+ 0xd3,SP0718_P1_0xd3-0x20,
+ 0xd4,SP0718_P1_0xd4-0x20,
+ 0xd5,SP0718_P1_0xd5-0x20,
+ 0xd6,SP0718_P1_0xd6-0x20,
+ //sat v -0x20
+ 0xd7,SP0718_P1_0xd7-0x20,
+ 0xd8,SP0718_P1_0xd8-0x20,
+ 0xd9,SP0718_P1_0xd9-0x20,
+ 0xda,SP0718_P1_0xda-0x20,
+};
+
+uint8_t sp0718_saturation_neg3[] = {
+ //sat u
+ 0xfd,0x01,
+ 0xd3,SP0718_P1_0xd3-0x18,
+ 0xd4,SP0718_P1_0xd4-0x18,
+ 0xd5,SP0718_P1_0xd5-0x18,
+ 0xd6,SP0718_P1_0xd6-0x18,
+ //sat v -0x18
+ 0xd7,SP0718_P1_0xd7-0x18,
+ 0xd8,SP0718_P1_0xd8-0x18,
+ 0xd9,SP0718_P1_0xd9-0x18,
+ 0xda,SP0718_P1_0xda-0x18,
+};
+
+uint8_t sp0718_saturation_neg2[] = {
+ //sat u
+ 0xfd,0x01,
+ 0xd3,SP0718_P1_0xd3-0x10,
+ 0xd4,SP0718_P1_0xd4-0x10,
+ 0xd5,SP0718_P1_0xd5-0x10,
+ 0xd6,SP0718_P1_0xd6-0x10,
+ //sat v -0x10
+ 0xd7,SP0718_P1_0xd7-0x10,
+ 0xd8,SP0718_P1_0xd8-0x10,
+ 0xd9,SP0718_P1_0xd9-0x10,
+ 0xda,SP0718_P1_0xda-0x10,
+};
+
+uint8_t sp0718_saturation_neg1[] = {
+ //sat u
+ 0xfd,0x01,
+ 0xd3,SP0718_P1_0xd3-0x08,
+ 0xd4,SP0718_P1_0xd4-0x08,
+ 0xd5,SP0718_P1_0xd5-0x08,
+ 0xd6,SP0718_P1_0xd6-0x08,
+ //sat v -0x08
+ 0xd7,SP0718_P1_0xd7-0x08,
+ 0xd8,SP0718_P1_0xd8-0x08,
+ 0xd9,SP0718_P1_0xd9-0x08,
+ 0xda,SP0718_P1_0xda-0x08,
+};
+
+uint8_t sp0718_saturation_zero[] = {
+ //sat u
+ 0xfd,0x01,
+ 0xd3,SP0718_P1_0xd3,
+ 0xd4,SP0718_P1_0xd4,
+ 0xd5,SP0718_P1_0xd5,
+ 0xd6,SP0718_P1_0xd6,
+ //sat v
+ 0xd7,SP0718_P1_0xd7,
+ 0xd8,SP0718_P1_0xd8,
+ 0xd9,SP0718_P1_0xd9,
+ 0xda,SP0718_P1_0xda,
+};
+
+uint8_t sp0718_saturation_pos1[] = {
+ //sat u
+ 0xfd,0x01,
+ 0xd3,SP0718_P1_0xd3+0x08,
+ 0xd4,SP0718_P1_0xd4+0x08,
+ 0xd5,SP0718_P1_0xd5+0x08,
+ 0xd6,SP0718_P1_0xd6+0x08,
+ //sat v +0x08
+ 0xd7,SP0718_P1_0xd7+0x08,
+ 0xd8,SP0718_P1_0xd8+0x08,
+ 0xd9,SP0718_P1_0xd9+0x08,
+ 0xda,SP0718_P1_0xda+0x08,
+};
+
+uint8_t sp0718_saturation_pos2[] = {
+ //sat u
+ 0xfd,0x01,
+ 0xd3,SP0718_P1_0xd3+0x10,
+ 0xd4,SP0718_P1_0xd4+0x10,
+ 0xd5,SP0718_P1_0xd5+0x10,
+ 0xd6,SP0718_P1_0xd6+0x10,
+ //sat v +0x10
+ 0xd7,SP0718_P1_0xd7+0x10,
+ 0xd8,SP0718_P1_0xd8+0x10,
+ 0xd9,SP0718_P1_0xd9+0x10,
+ 0xda,SP0718_P1_0xda+0x10,
+};
+
+uint8_t sp0718_saturation_pos3[] = {
+ //sat u
+ 0xfd,0x01,
+ 0xd3,SP0718_P1_0xd3+0x18,
+ 0xd4,SP0718_P1_0xd4+0x18,
+ 0xd5,SP0718_P1_0xd5+0x18,
+ 0xd6,SP0718_P1_0xd6+0x18,
+ //sat v +0x18
+ 0xd7,SP0718_P1_0xd7+0x18,
+ 0xd8,SP0718_P1_0xd8+0x18,
+ 0xd9,SP0718_P1_0xd9+0x18,
+ 0xda,SP0718_P1_0xda+0x18,
+};
+
+uint8_t sp0718_saturation_pos4[] = {
+ //sat u
+ 0xfd,0x01,
+ 0xd3,SP0718_P1_0xd3+0x20,
+ 0xd4,SP0718_P1_0xd4+0x20,
+ 0xd5,SP0718_P1_0xd5+0x20,
+ 0xd6,SP0718_P1_0xd6+0x20,
+ //sat v +0x20
+ 0xd7,SP0718_P1_0xd7+0x20,
+ 0xd8,SP0718_P1_0xd8+0x20,
+ 0xd9,SP0718_P1_0xd9+0x20,
+ 0xda,SP0718_P1_0xda+0x20,
+};
+
+
+// Resolution
+
+// QCIF
+uint8_t sp0718_176x144[]={
+};
+
+// QVGA
+uint8_t sp0718_320x240[]={
+};
+
+// CIF
+uint8_t sp0718_352x288[]={
+};
+
+// VGA
+uint8_t sp0718_640x480[]={
+ 0xfd,0x01,
+ 0x4a,0x00,
+ 0x4b,0x01,
+ 0x4c,0xe0,
+ 0x4d,0x00,
+ 0x4e,0x02,
+ 0x4f,0x80,
+
+ 0xfd,0x02,
+ 0x0f,0x00, //1//subsampledisable
+
+ 0xfd,0x00,
+ 0x30,0x00,
+};
+
+// SVGA
+uint8_t sp0718_800x600[]={
+};
+
+// XGA
+uint8_t sp0718_1024x768[]={
+};
+
+// 720p
+uint8_t sp0718_1280x720[]={
+};
+
+// UXGA
+uint8_t sp0718_1600x1200[]={
+};
+
+// 1080p
+uint8_t sp0718_1920x1080[]={
+};
+
+// QXGA
+uint8_t sp0718_2048x1536[]={
+};
+
+uint8_t sp0718_2592x1944[]={
+};
+
+// Initiliztion
+uint8_t sp0718_default_regs_init[] = {
+ 0xfd,0x00,
+ 0x1C,0x00,
+ 0x31,0x10,
+ 0x27,0xb3,//0xb3 //2x gain
+
+ 0x1b,0x17, // driver
+ 0x1f,0x30,
+ 0x30,0x00, // div clk
+
+ 0x26,0xaa,
+ 0x37,0x02,
+ 0x28,0x8f,
+ 0x1a,0x73,
+ 0x1e,0x1b,
+ 0x21,0x06, //blackout voltage
+ 0x22,0x2a, //colbias
+ 0x0f,0x3f,
+ 0x10,0x3e,
+ 0x11,0x00,
+ 0x12,0x01,
+ 0x13,0x3f,
+ 0x14,0x04,
+ 0x15,0x30,
+ 0x16,0x31,
+ 0x17,0x01,
+ 0x69,0x31,
+ 0x6a,0x2a,
+ 0x6b,0x33,
+ 0x6c,0x1a,
+ 0x6d,0x32,
+ 0x6e,0x28,
+ 0x6f,0x29,
+ 0x70,0x34,
+ 0x71,0x18,
+ 0x36,0x00,//02 delete badframe
+ 0xfd,0x01,
+ 0x5d,0x51,//position
+ 0xf2,0x19,
+
+ //Blacklevel
+ 0x1f,0x10,
+ 0x20,0x1f,
+ //pregain
+ 0xfd,0x02,
+ 0x00,0x88,
+ 0x01,0x88,
+ //SI15_SP0718 24M 50Hz 15-8fps
+ //ae setting
+ 0xfd,0x00,
+ 0x03,0x01,
+ 0x04,0xce,
+ 0x06,0x00,
+ 0x09,0x02,
+ 0x0a,0xc4,
+ 0xfd,0x01,
+ 0xef,0x4d,
+ 0xf0,0x00,
+ 0x02,0x0c,
+ 0x03,0x01,
+ 0x06,0x47,
+ 0x07,0x00,
+ 0x08,0x01,
+ 0x09,0x00,
+ //Status
+ 0xfd,0x02,
+ 0xbe,0x9c,
+ 0xbf,0x03,
+ 0xd0,0x9c,
+ 0xd1,0x03,
+ 0xfd,0x01,
+ 0x5b,0x03,
+ 0x5c,0x9c,
+
+ //rpc
+ 0xfd,0x01,
+ 0xe0,0x40,////24//4c//48//4c//44//4c//3e//3c//3a//38//rpc_1base_max
+ 0xe1,0x30,////24//3c//38//3c//36//3c//30//2e//2c//2a//rpc_2base_max
+ 0xe2,0x2e,////24//34//30//34//2e//34//2a//28//26//26//rpc_3base_max
+ 0xe3,0x2a,////24//2a//2e//2c//2e//2a//2e//26//24//22//rpc_4base_max
+ 0xe4,0x2a,////24//2a//2e//2c//2e//2a//2e//26//24//22//rpc_5base_max
+ 0xe5,0x28,////24//2c//2a//2c//28//2c//24//22//20//rpc_6base_max
+ 0xe6,0x28,////24//2c//2a//2c//28//2c//24//22//20//rpc_7base_max
+ 0xe7,0x26,////24//2a//28//2a//26//2a//22//20//20//1e//rpc_8base_max
+ 0xe8,0x26,////24//2a//28//2a//26//2a//22//20//20//1e//rpc_9base_max
+ 0xe9,0x26,////24//2a//28//2a//26//2a//22//20//20//1e//rpc_10base_max
+ 0xea,0x26,////24//28//26//28//24//28//20//1f//1e//1d//rpc_11base_max
+ 0xf3,0x26,////24//28//26//28//24//28//20//1f//1e//1d//rpc_12base_max
+ 0xf4,0x26,////24//28//26//28//24//28//20//1f//1e//1d//rpc_13base_max
+ //ae gain &status
+ 0xfd,0x01,
+ 0x04,0xe0,//rpc_max_indr
+ 0x05,0x26,//1e//rpc_min_indr
+ 0x0a,0xa0,//rpc_max_outdr
+ 0x0b,0x26,//rpc_min_outdr
+ 0x5a,0x40,//dp rpc
+ 0xfd,0x02,
+ 0xbc,0xa0,//rpc_heq_low
+ 0xbd,0x80,//rpc_heq_dummy
+ 0xb8,0x80,//mean_normal_dummy
+ 0xb9,0x90,//mean_dummy_normal
+
+ //ae target
+ 0xfd,0x01,
+ 0xeb,SP0718_P1_0xeb,//78
+ 0xec,SP0718_P1_0xec,//78
+ 0xed,0x0a,
+ 0xee,0x10,
+
+ //lsc
+ 0xfd,0x01,
+ 0x26,0x30,
+ 0x27,0x2c,
+ 0x28,0x07,
+ 0x29,0x08,
+ 0x2a,0x40,
+ 0x2b,0x03,
+ 0x2c,0x00,
+ 0x2d,0x00,
+
+ 0xa1,0x24,
+ 0xa2,0x27,
+ 0xa3,0x27,
+ 0xa4,0x2b,
+ 0xa5,0x1c,
+ 0xa6,0x1a,
+ 0xa7,0x1a,
+ 0xa8,0x1a,
+ 0xa9,0x18,
+ 0xaa,0x1c,
+ 0xab,0x17,
+ 0xac,0x17,
+ 0xad,0x08,
+ 0xae,0x08,
+ 0xaf,0x08,
+ 0xb0,0x00,
+ 0xb1,0x00,
+ 0xb2,0x00,
+ 0xb3,0x00,
+ 0xb4,0x00,
+ 0xb5,0x02,
+ 0xb6,0x06,
+ 0xb7,0x00,
+ 0xb8,0x00,
+
+
+ //DP
+ 0xfd,0x01,
+ 0x48,0x09,
+ 0x49,0x99,
+
+ //awb
+ 0xfd,0x01,
+ 0x32,0x05,
+ 0xfd,0x00,
+ 0xe7,0x03,
+ 0xfd,0x02,
+ 0x26,0xc8,
+ 0x27,0xb6,
+ 0xfd,0x00,
+ 0xe7,0x00,
+ 0xfd,0x02,
+ 0x1b,0x80,
+ 0x1a,0x80,
+ 0x18,0x26,
+ 0x19,0x28,
+ 0xfd,0x02,
+ 0x2a,0x00,
+ 0x2b,0x08,
+ 0x28,0xef,//0xa0//f8
+ 0x29,0x20,
+
+ //d65 90 e2 93
+ 0x66,0x42,//0x59//0x60////0x58//4e//0x48
+ 0x67,0x62,//0x74//0x70//0x78//6b//0x69
+ 0x68,0xee,//0xd6//0xe3//0xd5//cb//0xaa
+ 0x69,0x18,//0xf4//0xf3//0xf8//ed
+ 0x6a,0xa6,//0xa5
+ //indoor 91
+ 0x7c,0x3b,//0x45//30//41//0x2f//0x44
+ 0x7d,0x5b,//0x70//60//55//0x4b//0x6f
+ 0x7e,0x15,//0a//0xed
+ 0x7f,0x39,//23//0x28
+ 0x80,0xaa,//0xa6
+ //cwf 92
+ 0x70,0x3e,//0x38//41//0x3b
+ 0x71,0x59,//0x5b//5f//0x55
+ 0x72,0x31,//0x30//22//0x28
+ 0x73,0x4f,//0x54//44//0x45
+ 0x74,0xaa,
+ //tl84 93
+ 0x6b,0x1b,//0x18//11
+ 0x6c,0x3a,//0x3c//25//0x2f
+ 0x6d,0x3e,//0x3a//35
+ 0x6e,0x59,//0x5c//46//0x52
+ 0x6f,0xaa,
+ //f 94
+ 0x61,0xea,//0x03//0x00//f4//0xed
+ 0x62,0x03,//0x1a//0x25//0f//0f
+ 0x63,0x6a,//0x62//0x60//52//0x5d
+ 0x64,0x8a,//0x7d//0x85//70//0x75//0x8f
+ 0x65,0x6a,//0xaa//6a
+
+ 0x75,0x80,
+ 0x76,0x20,
+ 0x77,0x00,
+ 0x24,0x25,
+
+ //Õë¶ÔÊÒÄÚµ÷Æ«²»¹ýµÆÏä²âÊÔʹÓÃ//Õë¶ÔÈËÁ³µ÷Æ«
+ 0x20,0xd8,
+ 0x21,0xa3,//82//a8Æ«°µÕնȻ¹Óе÷Æ«
+ 0x22,0xd0,//e3//bc
+ 0x23,0x86,
+
+ //outdoor r\b range
+ 0x78,0xc3,//d8
+ 0x79,0xba,//82
+ 0x7a,0xa6,//e3
+ 0x7b,0x99,//86
+
+
+ //skin
+ 0x08,0x15,//
+ 0x09,0x04,//
+ 0x0a,0x20,//
+ 0x0b,0x12,//
+ 0x0c,0x27,//
+ 0x0d,0x06,//
+ 0x0e,0x63,//
+
+ //wt th
+ 0x3b,0x10,
+ //gw
+ 0x31,0x60,
+ 0x32,0x60,
+ 0x33,0xc0,
+ 0x35,0x6f,
+
+ // sharp
+ 0xfd,0x02,
+ 0xde,0x0f,
+ 0xd2,0x02,//6//¿ØÖƺڰױߣ»0-±ß´Ö£¬f-±äϸ
+ 0xd3,0x06,
+ 0xd4,0x06,
+ 0xd5,0x06,
+ 0xd7,0x20,//10//2x¸ù¾ÝÔöÒæÅжÏÂÖÀªãÐÖµ
+ 0xd8,0x30,//24//1A//4x
+ 0xd9,0x38,//28//8x
+ 0xda,0x38,//16x
+ 0xdb,0x08,//
+ 0xe8,0x58,//48//ÂÖÀªÇ¿¶È
+ 0xe9,0x48,
+ 0xea,0x30,
+ 0xeb,0x20,
+ 0xec,0x48,//60//80
+ 0xed,0x48,//50//60
+ 0xee,0x30,
+ 0xef,0x20,
+ //ƽ̹ÇøÓòÈñ»¯Á¦¶È
+ 0xf3,0x50,
+ 0xf4,0x10,
+ 0xf5,0x10,
+ 0xf6,0x10,
+ //dns
+ 0xfd,0x01,
+ 0x64,0x44,//ÑØ·½Ïò±ßԵƽ»¬Á¦¶È //0-×îÇ¿£¬8-×îÈõ
+ 0x65,0x22,
+ 0x6d,0x04,//8//ǿƽ»¬£¨Æ½Ì¹£©ÇøÓòƽ»¬ãÐÖµ
+ 0x6e,0x06,//8
+ 0x6f,0x10,
+ 0x70,0x10,
+ 0x71,0x08,//0d//Èõƽ»¬£¨·Çƽ̹£©ÇøÓòƽ»¬ãÐÖµ
+ 0x72,0x12,//1b
+ 0x73,0x1c,//20
+ 0x74,0x24,
+ 0x75,0x44,//[7:4]ƽ̹ÇøÓòÇ¿¶È£¬[3:0]·Çƽ̹ÇøÓòÇ¿¶È£»0-×îÇ¿£¬8-×îÈõ£»
+ 0x76,0x02,//46
+ 0x77,0x02,//33
+ 0x78,0x02,
+ 0x81,0x10,//18//2x//¸ù¾ÝÔöÒæÅж¨ÇøÓòãÐÖµ£¬µÍÓÚÕâ¸ö×öǿƽ»¬¡¢´óÓÚÕâ¸ö×öÈõƽ»¬£»
+ 0x82,0x20,//30//4x
+ 0x83,0x30,//40//8x
+ 0x84,0x48,//50//16x
+ 0x85,0x0c,//12/8+reg0x81 µÚ¶þãÐÖµ£¬ÔÚƽ̹ºÍ·Çƽ̹ÇøÓò×öÁ¬½Ó
+ 0xfd,0x02,
+ 0xdc,0x0f,
+
+ //gamma
+ 0xfd,0x01,
+ 0x8b,0x00,//00//00
+ 0x8c,0x0a,//0c//09
+ 0x8d,0x16,//19//17
+ 0x8e,0x1f,//25//24
+ 0x8f,0x2a,//30//33
+ 0x90,0x3c,//44//47
+ 0x91,0x4e,//54//58
+ 0x92,0x5f,//61//64
+ 0x93,0x6c,//6d//70
+ 0x94,0x82,//80//81
+ 0x95,0x94,//92//8f
+ 0x96,0xa6,//a1//9b
+ 0x97,0xb2,//ad//a5
+ 0x98,0xbf,//ba//b0
+ 0x99,0xc9,//c4//ba
+ 0x9a,0xd1,//cf//c4
+ 0x9b,0xd8,//d7//ce
+ 0x9c,0xe0,//e0//d7
+ 0x9d,0xe8,//e8//e1
+ 0x9e,0xef,//ef//ea
+ 0x9f,0xf8,//f7//f5
+ 0xa0,0xff,//ff//ff
+ //CCM
+ 0xfd,0x02,
+ 0x15,0xd0,//b>th
+ 0x16,0x95,//r<th
+ //gc¾µÍ·ÕÕÈËÁ³Æ«»Æ
+ //!F
+ 0xa0,0x80,//80
+ 0xa1,0x00,//00
+ 0xa2,0x00,//00
+ 0xa3,0x00,//06
+ 0xa4,0x8c,//8c
+ 0xa5,0xf4,//ed
+ 0xa6,0x0c,//0c
+ 0xa7,0xf4,//f4
+ 0xa8,0x80,//80
+ 0xa9,0x00,//00
+ 0xaa,0x30,//30
+ 0xab,0x0c,//0c
+ //F
+ 0xac,0x8c,
+ 0xad,0xf4,
+ 0xae,0x00,
+ 0xaf,0xed,
+ 0xb0,0x8c,
+ 0xb1,0x06,
+ 0xb2,0xf4,
+ 0xb3,0xf4,
+ 0xb4,0x99,
+ 0xb5,0x0c,
+ 0xb6,0x03,
+ 0xb7,0x0f,
+
+ //sat u
+ 0xfd,0x01,
+ 0xd3,SP0718_P1_0xd3,//0x88//50
+ 0xd4,SP0718_P1_0xd4,//0x88//50
+ 0xd5,SP0718_P1_0xd5,//50
+ 0xd6,SP0718_P1_0xd6,//50
+ //sat v
+ 0xd7,SP0718_P1_0xd7,//0x88//50
+ 0xd8,SP0718_P1_0xd8,//0x88//50
+ 0xd9,SP0718_P1_0xd9,//50
+ 0xda,SP0718_P1_0xda,//50
+ //auto_sat
+ 0xdd,0x30,
+ 0xde,0x10,
+ 0xd2,0x01,//autosa_en
+ 0xdf,0xff,//a0//y_mean_th
+
+ //uv_th
+ 0xfd,0x01,
+ 0xc2,0xaa,
+ 0xc3,0xaa,
+ 0xc4,0x66,
+ 0xc5,0x66,
+
+ //heq
+ 0xfd,0x01,
+ 0x0f,0xff,
+ 0x10,SP0718_P1_0x10, //out
+ 0x14,SP0718_P1_0x14,
+ 0x11,SP0718_P1_0x11, //nr
+ 0x15,SP0718_P1_0x15,
+ 0x12,SP0718_P1_0x12, //dummy
+ 0x16,SP0718_P1_0x16,
+ 0x13,SP0718_P1_0x13, //low
+ 0x17,SP0718_P1_0x17,
+
+ 0xfd,0x01,
+ 0xcd,0x20,
+ 0xce,0x1f,
+ 0xcf,0x20,
+ 0xd0,0x55,
+ //auto
+ 0xfd,0x01,
+ 0xfb,0x33,
+ //0x32,0x15,
+ 0x33,0xff,
+ 0x34,0xe7,
+ 0x35,0x40,
+ 0x36,0x08,
+};
+
+// Exit
+uint8_t sp0718_default_regs_exit[] = {
+};
+
+#endif /* #ifndef __SP0718_H_ */
diff --git a/drivers/media/video/wmt_v4l2/sensors/sp0838/sp0838.c b/drivers/media/video/wmt_v4l2/sensors/sp0838/sp0838.c
new file mode 100755
index 00000000..b0ce7454
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/sp0838/sp0838.c
@@ -0,0 +1,326 @@
+
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "sp0838.h"
+
+#define sensor_write_array(sd, arr, sz) \
+ cmos_init_8bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write(sd->i2c_addr, reg, val)
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint8_t *regs;
+ size_t size;
+};
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+// CMOS_WIN_SIZE("QVGA", 320, 240, sp0838_320x240),
+ CMOS_WIN_SIZE("VGA", 640, 480, sp0838_640x480),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = sp0838_wb_auto;
+ size = ARRAY_SIZE(sp0838_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = sp0838_wb_incandescent;
+ size = ARRAY_SIZE(sp0838_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = sp0838_wb_daylight;
+ size = ARRAY_SIZE(sp0838_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = sp0838_wb_cloudy;
+ size = ARRAY_SIZE(sp0838_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = sp0838_wb_fluorescent;
+ size = ARRAY_SIZE(sp0838_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_scenemode(struct cmos_subdev *sd, enum v4l2_scene_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case SCENE_MODE_AUTO:
+ regs = sp0838_scene_mode_auto;
+ size = ARRAY_SIZE(sp0838_scene_mode_auto);
+ break;
+ case SCENE_MODE_NIGHTSHOT:
+ regs = sp0838_scene_mode_night;
+ size = ARRAY_SIZE(sp0838_scene_mode_night);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_exposure(struct cmos_subdev *sd, int value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case -2:
+ regs = sp0838_exposure_neg2;
+ size = ARRAY_SIZE(sp0838_exposure_neg2);
+ break;
+ case -1:
+ regs = sp0838_exposure_neg1;
+ size = ARRAY_SIZE(sp0838_exposure_neg1);
+ break;
+ case 0:
+ regs = sp0838_exposure_zero;
+ size = ARRAY_SIZE(sp0838_exposure_zero);
+ break;
+ case 1:
+ regs = sp0838_exposure_pos1;
+ size = ARRAY_SIZE(sp0838_exposure_pos1);
+ break;
+ case 2:
+ regs = sp0838_exposure_pos2;
+ size = ARRAY_SIZE(sp0838_exposure_pos2);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data;
+
+ sensor_write(sd, 0xfd, 0x00); // set page0
+ data = sensor_read(sd, 0x31);
+ switch (value) {
+ case 0:
+ data &= ~0x20;
+ break;
+ case 1:
+ data |= 0x20;
+ break;
+ default:
+ return -EINVAL;
+ }
+ sensor_write(sd, 0x31, data);
+ printk(KERN_DEBUG "%s, 0x%x\n", __func__, data);
+ return 0;
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data;
+
+ sensor_write(sd, 0xfd, 0x00); // set page0
+ data = sensor_read(sd, 0x31);
+ switch (value) {
+ case 0:
+ data &= ~0x40;
+ break;
+ case 1:
+ data |= 0x40;
+ break;
+ default:
+ return -EINVAL;
+ }
+ sensor_write(sd, 0x31, data);
+ printk(KERN_DEBUG "%s, 0x%x\n", __func__, data);
+ return 0;
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ case V4L2_CID_EXPOSURE:
+ return v4l2_ctrl_query_fill(qc, -2, 2, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return sensor_s_scenemode(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return sensor_s_exposure(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ default:
+ case WMT_V4L2_CID_CAMERA_ANTIBANDING:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win) {
+ pr_err("%s, s_mbus_fmt failed, width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, win->regs, win->size);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return 0;
+}
+
+static int sensor_enum_framesizes(struct cmos_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ const struct cmos_win_size *win = &cmos_supported_win_sizes[index];
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ uint8_t data = 0;
+
+ sensor_write(sd, 0xfd, 0x0);
+ data = sensor_read(sd, 0x02);
+
+ return (data == sd->id) ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ if (sensor_identify(sd)) {
+ return -1;
+ }
+
+ sensor_write_array(sd, sp0838_default_regs_init,
+ ARRAY_SIZE(sp0838_default_regs_init));
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ sensor_write_array(sd, sp0838_default_regs_exit,
+ ARRAY_SIZE(sp0838_default_regs_exit));
+ return 0;
+}
+
+static struct cmos_subdev_ops sp0838_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+ .enum_framesizes = sensor_enum_framesizes,
+};
+
+struct cmos_subdev sp0838 = {
+ .name = "sp0838",
+ .i2c_addr = 0x18,
+ .id = 0x27,
+ .max_width = 640,
+ .max_height = 480,
+ .ops = &sp0838_ops,
+};
+
+#if 0
+static int __init sp0838_init(void)
+{
+ return cmos_register_sudbdev(&sp0838);
+}
+
+static void __exit sp0838_exit(void)
+{
+ return cmos_unregister_subdev(&sp0838);
+}
+
+module_init(sp0838_init);
+module_exit(sp0838_exit);
+
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/sp0838/sp0838.h b/drivers/media/video/wmt_v4l2/sensors/sp0838/sp0838.h
new file mode 100755
index 00000000..6f98a9ef
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/sp0838/sp0838.h
@@ -0,0 +1,788 @@
+
+#ifndef __SP0838_H_
+#define __SP0838_H_
+
+//ECLK Drv
+#define Pre_Value_P0_0x30 0x00
+//Filter en&dis
+#define Pre_Value_P0_0x56 0x70
+#define Pre_Value_P0_0x57 0x10 //filter outdoor
+#define Pre_Value_P0_0x58 0x10 //filter indoor
+#define Pre_Value_P0_0x59 0x10 //filter night
+#define Pre_Value_P0_0x5a 0x08 //smooth outdoor 0x02
+#define Pre_Value_P0_0x5b 0x14 //smooth indoor 0x02
+#define Pre_Value_P0_0x5c 0x30 //smooht night 0x20
+//outdoor sharpness
+#define Pre_Value_P0_0x65 0x03
+#define Pre_Value_P0_0x66 0x01
+#define Pre_Value_P0_0x67 0x03
+#define Pre_Value_P0_0x68 0x46
+//indoor sharpness
+#define Pre_Value_P0_0x6b 0x04
+#define Pre_Value_P0_0x6c 0x01
+#define Pre_Value_P0_0x6d 0x03// 0x03
+#define Pre_Value_P0_0x6e 0x43
+//night sharpness
+#define Pre_Value_P0_0x71 0x05
+#define Pre_Value_P0_0x72 0x01
+#define Pre_Value_P0_0x73 0x03
+#define Pre_Value_P0_0x74 0x43
+//color
+#define Pre_Value_P0_0x7f 0xd7 //R
+#define Pre_Value_P0_0x87 0xf8 //B
+//satutation
+#define Pre_Value_P0_0xd8 0x48
+#define Pre_Value_P0_0xd9 0x44//0x48
+#define Pre_Value_P0_0xda 0x3a//0x48
+#define Pre_Value_P0_0xdb 0x38//0x48
+//AE target
+#define Pre_Value_P0_0xf7 0x78
+#define Pre_Value_P0_0xf8 0x63
+#define Pre_Value_P0_0xf9 0x68
+#define Pre_Value_P0_0xfa 0x53
+//HEQ
+#define Pre_Value_P0_0xdd 0x78 // 0x70
+#define Pre_Value_P0_0xde 0x88 // 0x90
+//AWB pre gain
+#define Pre_Value_P1_0x28 0x75
+#define Pre_Value_P1_0x29 0x4e
+
+//VBLANK
+#define Pre_Value_P0_0x05 0x00
+#define Pre_Value_P0_0x06 0x00
+//HBLANK
+#define Pre_Value_P0_0x09 0x01
+#define Pre_Value_P0_0x0a 0x76
+
+
+// Scene Mode
+uint8_t sp0838_scene_mode_auto[] = {
+ //caprure preview daylight 24M 50hz 20-8FPS maxgain:0x70
+ 0xfd,0x00,
+ 0x05,0x0 ,
+ 0x06,0x0 ,
+ 0x09,0x1 ,
+ 0x0a,0x76,
+ 0xf0,0x62,
+ 0xf1,0x0 ,
+ 0xf2,0x5f,
+ 0xf5,0x78,
+ 0xfd,0x01,
+ 0x00,0xb2,
+ 0x0f,0x60,
+ 0x16,0x60,
+ 0x17,0xa2,
+ 0x18,0xaa,
+ 0x1b,0x60,
+ 0x1c,0xaa,
+ 0xb4,0x20,
+ 0xb5,0x3a,
+ 0xb6,0x5e,
+ 0xb9,0x40,
+ 0xba,0x4f,
+ 0xbb,0x47,
+ 0xbc,0x45,
+ 0xbd,0x43,
+ 0xbe,0x42,
+ 0xbf,0x42,
+ 0xc0,0x42,
+ 0xc1,0x41,
+ 0xc2,0x41,
+ 0xc3,0x41,
+ 0xc4,0x41,
+ 0xc5,0x70,
+ 0xc6,0x41,
+ 0xca,0x70,
+ 0xcb,0xc ,
+ 0x14,0x20,
+ 0x15,0x0f,
+ 0xfd,0x00,
+};
+
+uint8_t sp0838_scene_mode_night[] = {
+ //capture preview night 24M 50hz 20-6FPS maxgain:0x78
+ 0xfd,0x00,
+ 0x05,0x0 ,
+ 0x06,0x0 ,
+ 0x09,0x1 ,
+ 0x0a,0x76,
+ 0xf0,0x62,
+ 0xf1,0x0 ,
+ 0xf2,0x5f,
+ 0xf5,0x78,
+ 0xfd,0x01,
+ 0x00,0xc0,
+ 0x0f,0x60,
+ 0x16,0x60,
+ 0x17,0xa8,
+ 0x18,0xb0,
+ 0x1b,0x60,
+ 0x1c,0xb0,
+ 0xb4,0x20,
+ 0xb5,0x3a,
+ 0xb6,0x5e,
+ 0xb9,0x40,
+ 0xba,0x4f,
+ 0xbb,0x47,
+ 0xbc,0x45,
+ 0xbd,0x43,
+ 0xbe,0x42,
+ 0xbf,0x42,
+ 0xc0,0x42,
+ 0xc1,0x41,
+ 0xc2,0x41,
+ 0xc3,0x41,
+ 0xc4,0x41,
+ 0xc5,0x41,
+ 0xc6,0x41,
+ 0xca,0x78,
+ 0xcb,0x10,
+ 0x14,0x20,
+ 0x15,0x1f,
+ 0xfd,0x00,
+};
+
+
+// White Balance
+uint8_t sp0838_wb_auto [] = {
+ 0xfd,0x01,
+ 0x28,Pre_Value_P1_0x28,
+ 0x29,Pre_Value_P1_0x29,
+ 0xfd,0x00,
+ 0x32,0x15,
+ 0xfd,0x00,
+};
+
+uint8_t sp0838_wb_incandescent [] = {
+ 0xfd,0x00, //2800K-3000K
+ 0x32,0x05,
+ 0xfd,0x01,
+ 0x28,0x41,
+ 0x29,0x71,
+ 0xfd,0x00,
+};
+
+uint8_t sp0838_wb_fluorescent [] = {
+ 0xfd,0x00, //4200K-5000K
+ 0x32,0x05,
+ 0xfd,0x01,
+ 0x28,0x5a,
+ 0x29,0x62,
+ 0xfd,0x00,
+};
+
+uint8_t sp0838_wb_daylight [] = {
+ 0xfd,0x00,
+ 0x32,0x05,
+ 0xfd,0x01,
+ 0x28,0x6b,
+ 0x29,0x48,
+ 0xfd,0x00,
+};
+
+uint8_t sp0838_wb_cloudy [] = {
+ 0xfd,0x00,
+ 0x32,0x05,
+ 0xfd,0x01,
+ 0x28,0x71,
+ 0x29,0x41,
+ 0xfd,0x00,
+};
+
+uint8_t sp0838_wb_tungsten [] = {
+ 0xfd,0x00, //4000K
+ 0x32,0x05,
+ 0xfd,0x01,
+ 0x28,0x57,
+ 0x29,0x66,
+ 0xfd,0x00
+};
+
+
+// Exposure
+uint8_t sp0838_exposure_neg2[] = {
+ //level -2
+ 0xfd,0x00,
+ 0xed,Pre_Value_P0_0xf7-0x20+0x04,
+ 0xf7,Pre_Value_P0_0xf7-0x20,
+ 0xf8,Pre_Value_P0_0xf8-0x20,
+ 0xec,Pre_Value_P0_0xf8-0x20-0x04,
+ 0xef,Pre_Value_P0_0xf9-0x20+0x04,
+ 0xf9,Pre_Value_P0_0xf9-0x20,
+ 0xfa,Pre_Value_P0_0xfa-0x20,
+ 0xee,Pre_Value_P0_0xfa-0x20-0x04,
+ 0xfd, 0x00,
+};
+
+uint8_t sp0838_exposure_neg1[] = {
+ //level -2
+ 0xfd,0x00,
+ 0xed,Pre_Value_P0_0xf7-0x10+0x04,
+ 0xf7,Pre_Value_P0_0xf7-0x10,
+ 0xf8,Pre_Value_P0_0xf8-0x10,
+ 0xec,Pre_Value_P0_0xf8-0x10-0x04,
+ 0xef,Pre_Value_P0_0xf9-0x10+0x04,
+ 0xf9,Pre_Value_P0_0xf9-0x10,
+ 0xfa,Pre_Value_P0_0xfa-0x10,
+ 0xee,Pre_Value_P0_0xfa-0x10-0x04,
+
+ 0xfd, 0x00,
+};
+
+uint8_t sp0838_exposure_zero[] = {
+ //level 0
+ 0xfd,0x00,
+ 0xed,Pre_Value_P0_0xf7+0x04,
+ 0xf7,Pre_Value_P0_0xf7,
+ 0xf8,Pre_Value_P0_0xf8,
+ 0xec,Pre_Value_P0_0xf8-0x04,
+ 0xef,Pre_Value_P0_0xf9+0x04,
+ 0xf9,Pre_Value_P0_0xf9,
+ 0xfa,Pre_Value_P0_0xfa,
+ 0xee,Pre_Value_P0_0xfa-0x04,
+ 0xfd, 0x00,
+};
+
+uint8_t sp0838_exposure_pos1[] = {
+ //level +2
+ 0xfd,0x00,
+ 0xed,Pre_Value_P0_0xf7+0x10+0x04,
+ 0xf7,Pre_Value_P0_0xf7+0x10,
+ 0xf8,Pre_Value_P0_0xf8+0x10,
+ 0xec,Pre_Value_P0_0xf8+0x10-0x04,
+ 0xef,Pre_Value_P0_0xf9+0x10+0x04,
+ 0xf9,Pre_Value_P0_0xf9+0x10,
+ 0xfa,Pre_Value_P0_0xfa+0x10,
+ 0xee,Pre_Value_P0_0xfa+0x10-0x04,
+ 0xfd, 0x00,
+};
+
+uint8_t sp0838_exposure_pos2[] = {
+ //level +2
+ 0xfd,0x00,
+ 0xed,Pre_Value_P0_0xf7+0x20+0x04,
+ 0xf7,Pre_Value_P0_0xf7+0x20,
+ 0xf8,Pre_Value_P0_0xf8+0x20,
+ 0xec,Pre_Value_P0_0xf8+0x20-0x04,
+ 0xef,Pre_Value_P0_0xf9+0x20+0x04,
+ 0xf9,Pre_Value_P0_0xf9+0x20,
+ 0xfa,Pre_Value_P0_0xfa+0x20,
+ 0xee,Pre_Value_P0_0xfa+0x20-0x04,
+ 0xfd, 0x00,
+};
+
+
+// Color Effect
+uint8_t sp0838_colorfx_none[] = {
+ 0xfd, 0x00,
+ 0x62, 0x00,
+ 0x63, 0x80,
+ 0x64, 0x80,
+};
+
+uint8_t sp0838_colorfx_bw[] = {
+ 0xfd, 0x00,
+ 0x62, 0x40,
+ 0x63, 0x80,
+ 0x64, 0x80,
+};
+
+uint8_t sp0838_colorfx_sepia[] = {
+ 0xfd, 0x00,
+ 0x62, 0x20,
+ 0x63, 0xc0,
+ 0x64, 0x20,
+
+};
+
+uint8_t sp0838_colorfx_negative[] = {
+ 0xfd, 0x00,
+ 0x62, 0x10,
+ 0x63, 0x80,
+ 0x64, 0x80,
+};
+
+uint8_t sp0838_colorfx_emboss[] = {
+ 0xfd, 0x00,
+ 0x62, 0x02,
+ 0x63, 0x80,
+ 0x64, 0x80,
+};
+
+uint8_t sp0838_colorfx_sketch[] = {
+ 0xfd, 0x00,
+ 0x62, 0x01,
+ 0x63, 0x80,
+ 0x64, 0x80,
+};
+
+uint8_t sp0838_colorfx_sky_blue[] = {
+ 0xfd, 0x00,
+ 0x62, 0x20,
+ 0x63, 0x20,
+ 0x64, 0xf0,
+};
+
+uint8_t sp0838_colorfx_grass_green[] = {
+ 0xfd, 0x00,
+ 0x62, 0x20,
+ 0x63, 0x20,
+ 0x64, 0x20,
+};
+
+uint8_t sp0838_colorfx_skin_whiten[] = {
+};
+
+uint8_t sp0838_colorfx_vivid[] = {
+};
+
+// Brightness
+uint8_t sp0838_brightness_neg4[] = {
+ 0xfd,0x00,
+ 0xdc,0xc0,//level -4
+};
+
+uint8_t sp0838_brightness_neg3[] = {
+ 0xfd,0x00,
+ 0xdc,0xd0,//level -3
+};
+
+uint8_t sp0838_brightness_neg2[] = {
+ 0xfd,0x00,
+ 0xdc,0xe0,//level -2
+};
+
+uint8_t sp0838_brightness_neg1[] = {
+ 0xfd,0x00,
+ 0xdc,0x00,//level -1
+};
+
+uint8_t sp0838_brightness_zero[] = {
+ 0xfd,0x00,
+ 0xdc,0x00,//level 0
+};
+
+uint8_t sp0838_brightness_pos1[] = {
+ 0xfd,0x00,
+ 0xdc,0x10,//level +1
+};
+
+uint8_t sp0838_brightness_pos2[] = {
+ 0xfd,0x00,
+ 0xdc,0x20,//level +2
+};
+
+uint8_t sp0838_brightness_pos3[] = {
+ 0xfd,0x00,
+ 0xdc,0x30,//level +3
+};
+
+uint8_t sp0838_brightness_pos4[] = {
+ 0xfd,0x00,
+ 0xdc,0x40,//level +4
+};
+
+// Contrast
+uint8_t sp0838_contrast_neg4[] = {
+ 0xfd, 0x00,
+ 0xdd,Pre_Value_P0_0xdd-0x40, //level -4
+ 0xde,Pre_Value_P0_0xde-0x40,
+
+};
+
+uint8_t sp0838_contrast_neg3[] = {
+ 0xfd, 0x00,
+ 0xdd,Pre_Value_P0_0xdd-0x30, //level -3
+ 0xde,Pre_Value_P0_0xde-0x30,
+
+};
+
+uint8_t sp0838_contrast_neg2[] = {
+ 0xfd, 0x00,
+ 0xdd,Pre_Value_P0_0xdd-0x20, //level -2
+ 0xde,Pre_Value_P0_0xde-0x20,
+
+};
+
+uint8_t sp0838_contrast_neg1[] = {
+ 0xfd, 0x00,
+ 0xdd, Pre_Value_P0_0xdd-0x10, //level -1
+ 0xde, Pre_Value_P0_0xde-0x10,
+
+};
+
+uint8_t sp0838_contrast_zero[] = {
+ 0xfd, 0x00,
+ 0xdd, Pre_Value_P0_0xdd, //level 0
+ 0xde, Pre_Value_P0_0xde,
+
+};
+
+uint8_t sp0838_contrast_pos1[] = {
+ 0xfd, 0x00,
+ 0xdd,Pre_Value_P0_0xdd+0x10, //level +1
+ 0xde,Pre_Value_P0_0xde+0x10,
+
+};
+
+uint8_t sp0838_contrast_pos2[] = {
+ 0xfd, 0x00,
+ 0xdd,Pre_Value_P0_0xdd+0x20, //level +2
+ 0xde,Pre_Value_P0_0xde+0x20,
+
+};
+
+uint8_t sp0838_contrast_pos3[] = {
+ 0xfd, 0x00,
+ 0xdd,Pre_Value_P0_0xdd+0x30, //level +3
+ 0xde,Pre_Value_P0_0xde+0x30,
+
+};
+
+uint8_t sp0838_contrast_pos4[] = {
+ 0xfd, 0x00,
+ 0xdd,Pre_Value_P0_0xdd+0x40, //level +4
+ 0xde,Pre_Value_P0_0xde+0x40,
+
+};
+
+// Saturation
+uint8_t sp0838_saturation_neg4[] = {
+ 0xfd, 0x00,
+ 0xd8, 0x00,
+ 0xd9, 0x00,
+ 0xda, 0x00,
+ 0xdb, 0x00,
+};
+
+uint8_t sp0838_saturation_neg3[] = {
+ 0xfd, 0x00,
+ 0xd8, Pre_Value_P0_0xd8-0x30,
+ 0xd9, Pre_Value_P0_0xd9-0x30,
+ 0xda, Pre_Value_P0_0xda-0x30,
+ 0xdb, Pre_Value_P0_0xdb-0x30,
+};
+
+uint8_t sp0838_saturation_neg2[] = {
+ 0xfd, 0x00,
+ 0xd8, Pre_Value_P0_0xd8-0x20,
+ 0xd9, Pre_Value_P0_0xd9-0x20,
+ 0xda, Pre_Value_P0_0xda-0x20,
+ 0xdb, Pre_Value_P0_0xdb-0x20,
+};
+
+uint8_t sp0838_saturation_neg1[] = {
+ 0xfd, 0x00,
+ 0xd8, Pre_Value_P0_0xd8-0x10,
+ 0xd9, Pre_Value_P0_0xd9-0x10,
+ 0xda, Pre_Value_P0_0xda-0x10,
+ 0xdb, Pre_Value_P0_0xdb-0x10,
+};
+
+uint8_t sp0838_saturation_zero[] = {
+ 0xfd, 0x00,
+ 0xd8, Pre_Value_P0_0xd8,
+ 0xd9, Pre_Value_P0_0xd9,
+ 0xda, Pre_Value_P0_0xda,
+ 0xdb, Pre_Value_P0_0xdb,
+
+};
+
+uint8_t sp0838_saturation_pos1[] = {
+ 0xfd, 0x00,
+ 0xd8, Pre_Value_P0_0xd8+0x10,
+ 0xd9, Pre_Value_P0_0xd9+0x10,
+ 0xda, Pre_Value_P0_0xda+0x10,
+ 0xdb, Pre_Value_P0_0xdb+0x10,
+};
+
+uint8_t sp0838_saturation_pos2[] = {
+ 0xfd, 0x00,
+ 0xd8, Pre_Value_P0_0xd8+0x20,
+ 0xd9, Pre_Value_P0_0xd9+0x20,
+ 0xda, Pre_Value_P0_0xda+0x20,
+ 0xdb, Pre_Value_P0_0xdb+0x20,
+
+};
+
+uint8_t sp0838_saturation_pos3[] = {
+ 0xfd, 0x00,
+ 0xd8, Pre_Value_P0_0xd8+0x30,
+ 0xd9, Pre_Value_P0_0xd9+0x30,
+ 0xda, Pre_Value_P0_0xda+0x30,
+ 0xdb, Pre_Value_P0_0xdb+0x30,
+
+};
+
+uint8_t sp0838_saturation_pos4[] = {
+ 0xfd, 0x00,
+ 0xd8, Pre_Value_P0_0xd8+0x40,
+ 0xd9, Pre_Value_P0_0xd9+0x40,
+ 0xda, Pre_Value_P0_0xda+0x40,
+ 0xdb, Pre_Value_P0_0xdb+0x40,
+
+};
+
+
+// Resolution
+
+// QCIF
+uint8_t sp0838_176x144[]={
+};
+
+// QVGA
+uint8_t sp0838_320x240[]={
+};
+
+// CIF
+uint8_t sp0838_352x288[]={
+};
+
+// VGA
+uint8_t sp0838_640x480[]={
+};
+
+// SVGA
+uint8_t sp0838_800x600[]={
+};
+
+// XGA
+uint8_t sp0838_1024x768[]={
+};
+
+// 720p
+uint8_t sp0838_1280x720[]={
+};
+
+// UXGA
+uint8_t sp0838_1600x1200[]={
+};
+
+// 1080p
+uint8_t sp0838_1920x1080[]={
+};
+
+// QXGA
+uint8_t sp0838_2048x1536[]={
+};
+
+uint8_t sp0838_2592x1944[]={
+};
+
+// Initiliztion
+uint8_t sp0838_default_regs_init[] = {
+ //SP0838 ini
+ 0xfd,0x00,//P0
+ 0x1B,0x02,
+ 0x27,0xe8,
+ 0x28,0x0B,
+ 0x32,0x00,
+ 0x22,0xc0,
+ 0x26,0x10,
+ 0x5f,0x11,//Bayer order
+ 0xfd,0x01,//P1
+ 0x25,0x1a,//Awb start
+ 0x26,0xfb,
+ 0x28,Pre_Value_P1_0x28,
+ 0x29,Pre_Value_P1_0x29,
+ 0xfd,0x00,
+ 0xe7,0x03,
+ 0xe7,0x00,
+ 0xfd,0x01,
+ 0x31,0x60,//64
+ 0x32,0x18,
+ 0x4d,0xdc,
+ 0x4e,0x53,
+ 0x41,0x8c,
+ 0x42,0x57,
+ 0x55,0xff,
+ 0x56,0x00,
+ 0x59,0x82,
+ 0x5a,0x00,
+ 0x5d,0xff,
+ 0x5e,0x6f,
+ 0x57,0xff,
+ 0x58,0x00,
+ 0x5b,0xff,
+ 0x5c,0xa8,
+ 0x5f,0x75,
+ 0x60,0x00,
+ 0x2d,0x00,
+ 0x2e,0x00,
+ 0x2f,0x00,
+ 0x30,0x00,
+ 0x33,0x00,
+ 0x34,0x00,
+ 0x37,0x00,
+ 0x38,0x00,//awb end
+ 0xfd,0x00,//P0
+ 0x33,0x6f,//LSC BPC EN
+ 0x51,0x3f,//BPC debug start
+ 0x52,0x09,
+ 0x53,0x00,
+ 0x54,0x00,
+ 0x55,0x10,//BPC debug end
+ 0x4f,0x08,//blueedge
+ 0x50,0x08,
+ 0x57,Pre_Value_P0_0x57,//Raw filter debut start
+ 0x58,Pre_Value_P0_0x58,
+ 0x59,Pre_Value_P0_0x59,
+ 0x56,Pre_Value_P0_0x56,
+ 0x5a,Pre_Value_P0_0x5a,
+ 0x5b,Pre_Value_P0_0x5b,
+ 0x5c,Pre_Value_P0_0x5c,//Raw filter debut end
+ 0x65,Pre_Value_P0_0x65,//Sharpness debug start
+ 0x66,Pre_Value_P0_0x66,
+ 0x67,Pre_Value_P0_0x67,
+ 0x68,Pre_Value_P0_0x68,
+ 0x69,0x7f,
+ 0x6a,0x01,
+ 0x6b,Pre_Value_P0_0x6b,
+ 0x6c,Pre_Value_P0_0x6c,
+ 0x6d,Pre_Value_P0_0x6d,//Edge gain normal
+ 0x6e,Pre_Value_P0_0x6e,//Edge gain normal
+ 0x6f,0x7f,
+ 0x70,0x01,
+ 0x71,Pre_Value_P0_0x71, //Èñ»¯ãÐÖµ
+ 0x72,Pre_Value_P0_0x72, //ÈõÂÖÀªãÐÖµ
+ 0x73,Pre_Value_P0_0x73, //±ßÔµÕýÏòÔöÒæÖµ
+ 0x74,Pre_Value_P0_0x74, //±ßÔµ·´ÏòÔöÒæÖµ
+ 0x75,0x7f, //ʹÄÜλ
+ 0x76,0x01,//Sharpness debug end
+ 0xcb,0x07,//HEQ&Saturation debug start
+ 0xcc,0x04,
+ 0xce,0xff,
+ 0xcf,0x10,
+ 0xd0,0x20,
+ 0xd1,0x00,
+ 0xd2,0x1c,
+ 0xd3,0x16,
+ 0xd4,0x00,
+ 0xd6,0x1c,
+ 0xd7,0x16,
+ 0xdd,Pre_Value_P0_0xdd,//Contrast
+ 0xde,Pre_Value_P0_0xde,//HEQ&Saturation debug end
+ 0x7f,Pre_Value_P0_0x7f,//Color Correction start
+ 0x80,0xbc,
+ 0x81,0xed,
+ 0x82,0xd7,
+ 0x83,0xd4,
+ 0x84,0xd6,
+ 0x85,0xff,
+ 0x86,0x89,
+ 0x87,Pre_Value_P0_0x87,
+ 0x88,0x3c,
+ 0x89,0x33,
+ 0x8a,0x0f,//Color Correction end
+ 0x8b,0x0 ,//gamma start
+ 0x8c,0x1a,
+ 0x8d,0x29,
+ 0x8e,0x41,
+ 0x8f,0x62,
+ 0x90,0x7c,
+ 0x91,0x90,
+ 0x92,0xa2,
+ 0x93,0xaf,
+ 0x94,0xbc,
+ 0x95,0xc5,
+ 0x96,0xcd,
+ 0x97,0xd5,
+ 0x98,0xdd,
+ 0x99,0xe5,
+ 0x9a,0xed,
+ 0x9b,0xf5,
+ 0xfd,0x01,//P1
+ 0x8d,0xfd,
+ 0x8e,0xff,//gamma end
+ 0xfd,0x00,//P0
+ 0xca,0xcf,
+ 0xd8,Pre_Value_P0_0xd8,//UV outdoor
+ 0xd9,Pre_Value_P0_0xd9,//UV indoor
+ 0xda,Pre_Value_P0_0xda,//UV dummy
+ 0xdb,Pre_Value_P0_0xdb,//UV lowlight
+ 0xb9,0x00,//Ygamma start
+ 0xba,0x04,
+ 0xbb,0x08,
+ 0xbc,0x10,
+ 0xbd,0x20,
+ 0xbe,0x30,
+ 0xbf,0x40,
+ 0xc0,0x50,
+ 0xc1,0x60,
+ 0xc2,0x70,
+ 0xc3,0x80,
+ 0xc4,0x90,
+ 0xc5,0xA0,
+ 0xc6,0xB0,
+ 0xc7,0xC0,
+ 0xc8,0xD0,
+ 0xc9,0xE0,
+ 0xfd,0x01,//P1
+ 0x89,0xf0,
+ 0x8a,0xff,//Ygamma end
+ 0xfd,0x00,//P0
+ 0xe8,0x30,//AEdebug start
+ 0xe9,0x30,
+ 0xea,0x40,//Alc Window sel
+ 0xf4,0x1b,//outdoor mode sel
+ 0xf5,0x80,
+ 0xf7,Pre_Value_P0_0xf7,//AE target
+ 0xf8,Pre_Value_P0_0xf8,
+ 0xf9,Pre_Value_P0_0xf9,//AE target
+ 0xfa,Pre_Value_P0_0xfa,
+ 0xfd,0x01,//P1
+ 0x09,0x31,//AE Step 3.0
+ 0x0a,0x85,
+ 0x0b,0x0b,//AE Step 3.0
+ 0x14,0x20,
+ 0x15,0x0f,
+ //caprure preview daylight 24M 50hz 20-8FPS maxgain:0x70
+ 0xfd,0x00,
+ 0x05,Pre_Value_P0_0x05 ,
+ 0x06,Pre_Value_P0_0x06 ,
+ 0x09,Pre_Value_P0_0x09 ,
+ 0x0a,Pre_Value_P0_0x0a ,
+ 0xf0,0x62,
+ 0xf1,0x0 ,
+ 0xf2,0x5f,
+ 0xf5,0x78,
+ 0xfd,0x01,
+ 0x00,0xb2,
+ 0x0f,0x60,
+ 0x16,0x60,
+ 0x17,0xa2,
+ 0x18,0xaa,
+ 0x1b,0x60,
+ 0x1c,0xaa,
+ 0xb4,0x20,
+ 0xb5,0x3a,
+ 0xb6,0x5e,
+ 0xb9,0x40,
+ 0xba,0x4f,
+ 0xbb,0x47,
+ 0xbc,0x45,
+ 0xbd,0x43,
+ 0xbe,0x42,
+ 0xbf,0x42,
+ 0xc0,0x42,
+ 0xc1,0x41,
+ 0xc2,0x41,
+ 0xc3,0x41,
+ 0xc4,0x41,
+ 0xc5,0x70,
+ 0xc6,0x41,
+ 0xca,0x70,
+ 0xcb,0x0c,
+ 0xfd,0x00,
+ 0xfd,0x00, //P0
+ 0x32,0x15, //Auto_mode set
+ 0x34,0x66, //Isp_mode set
+ 0x35,0x40, //out format
+ 0x36,0x80,
+};
+
+// Exit
+uint8_t sp0838_default_regs_exit[] = {
+};
+
+#endif /* #ifndef __SP0838_H_ */
diff --git a/drivers/media/video/wmt_v4l2/sensors/sp0a19/sp0a19.c b/drivers/media/video/wmt_v4l2/sensors/sp0a19/sp0a19.c
new file mode 100755
index 00000000..28465154
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/sp0a19/sp0a19.c
@@ -0,0 +1,344 @@
+
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "sp0a19.h"
+
+#define sensor_write_array(sd, arr, sz) \
+ cmos_init_8bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write(sd->i2c_addr, reg, val)
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint8_t *regs;
+ size_t size;
+};
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+#define FILP_REG_INIT_VALUE 0x00
+#define DELAY_INTERVAL 50
+#define RETRY_TIMES 10
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+ CMOS_WIN_SIZE("QVGA", 320, 240, sp0a19_320x240),
+ CMOS_WIN_SIZE("VGA", 640, 480, sp0a19_640x480),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = sp0a19_wb_auto;
+ size = ARRAY_SIZE(sp0a19_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = sp0a19_wb_incandescent;
+ size = ARRAY_SIZE(sp0a19_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = sp0a19_wb_daylight;
+ size = ARRAY_SIZE(sp0a19_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = sp0a19_wb_cloudy;
+ size = ARRAY_SIZE(sp0a19_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = sp0a19_wb_fluorescent;
+ size = ARRAY_SIZE(sp0a19_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_scenemode(struct cmos_subdev *sd, enum v4l2_scene_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case SCENE_MODE_AUTO:
+ regs = sp0a19_scene_mode_auto;
+ size = ARRAY_SIZE(sp0a19_scene_mode_auto);
+ break;
+ case SCENE_MODE_NIGHTSHOT:
+ regs = sp0a19_scene_mode_night;
+ size = ARRAY_SIZE(sp0a19_scene_mode_night);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_exposure(struct cmos_subdev *sd, int value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case -2:
+ regs = sp0a19_exposure_neg2;
+ size = ARRAY_SIZE(sp0a19_exposure_neg2);
+ break;
+ case -1:
+ regs = sp0a19_exposure_neg1;
+ size = ARRAY_SIZE(sp0a19_exposure_neg1);
+ break;
+ case 0:
+ regs = sp0a19_exposure_zero;
+ size = ARRAY_SIZE(sp0a19_exposure_zero);
+ break;
+ case 1:
+ regs = sp0a19_exposure_pos1;
+ size = ARRAY_SIZE(sp0a19_exposure_pos1);
+ break;
+ case 2:
+ regs = sp0a19_exposure_pos2;
+ size = ARRAY_SIZE(sp0a19_exposure_pos2);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data,tmp=0;
+ uint8_t retry_times=0;
+
+
+ sensor_write(sd, 0xfd, 0x00); // set page0
+ data = sensor_read(sd, 0x31);
+
+ switch (value) {
+ case 0:
+ data &= ~0x20;
+ break;
+ case 1:
+ data |= 0x20;
+ break;
+ default:
+ return -EINVAL;
+ }
+ tmp=data;
+ for(retry_times=0;retry_times<RETRY_TIMES;retry_times++){
+ sensor_write(sd, 0x31, tmp);
+ msleep(50+retry_times*DELAY_INTERVAL);
+ if(sensor_read(sd, 0x31)==tmp)
+ break;
+ }
+ printk(KERN_DEBUG "%s, 0x%x\n", __func__, data);
+ return 0;
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data,tmp=0;
+ uint8_t retry_times=0;
+
+ sensor_write(sd, 0xfd, 0x00); // set page0
+ data = sensor_read(sd, 0x31);
+ switch (value) {
+ case 0:
+ data &= ~0x40;
+ break;
+ case 1:
+ data |= 0x40;
+ break;
+ default:
+ return -EINVAL;
+ }
+ tmp=data;
+ for(retry_times=0;retry_times<RETRY_TIMES;retry_times++){
+ sensor_write(sd, 0x31, tmp);
+ msleep(50+retry_times*DELAY_INTERVAL);
+ if(sensor_read(sd, 0x31)==tmp)
+ break;
+ }
+ printk(KERN_DEBUG "%s, 0x%x\n", __func__, data);
+ return 0;
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ case V4L2_CID_EXPOSURE:
+ return v4l2_ctrl_query_fill(qc, -2, 2, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return sensor_s_scenemode(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return sensor_s_exposure(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ default:
+ case WMT_V4L2_CID_CAMERA_ANTIBANDING:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win) {
+ pr_err("%s, s_mbus_fmt failed, width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, win->regs, win->size);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return 0;
+}
+
+static int sensor_enum_framesizes(struct cmos_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ const struct cmos_win_size *win = &cmos_supported_win_sizes[index];
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ uint8_t data = 0;
+
+ sensor_write(sd, 0xfd, 0x0);
+ data = sensor_read(sd, 0x02);
+ return (data == sd->id) ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ if (sensor_identify(sd)) {
+ return -1;
+ }
+ sensor_write_array(sd, sp0a19_default_regs_init,
+ ARRAY_SIZE(sp0a19_default_regs_init));
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ sensor_write_array(sd, sp0a19_default_regs_exit,
+ ARRAY_SIZE(sp0a19_default_regs_exit));
+ return 0;
+}
+
+static struct cmos_subdev_ops sp0a19_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+ .enum_framesizes = sensor_enum_framesizes,
+};
+
+struct cmos_subdev sp0a19 = {
+ .name = "sp0a19",
+ .i2c_addr = 0x21,
+ .id = 0xa6,
+ .max_width = 640,
+ .max_height = 480,
+ .ops = &sp0a19_ops,
+};
+
+#if 0
+static int __init sp0a19_init(void)
+{
+ return cmos_register_sudbdev(&sp0a19);
+}
+
+static void __exit sp0a19_exit(void)
+{
+ return cmos_unregister_subdev(&sp0a19);
+}
+
+module_init(sp0a19_init);
+module_exit(sp0a19_exit);
+
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/sp0a19/sp0a19.h b/drivers/media/video/wmt_v4l2/sensors/sp0a19/sp0a19.h
new file mode 100755
index 00000000..79db4c7c
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/sp0a19/sp0a19.h
@@ -0,0 +1,667 @@
+
+#ifndef __SP0a19_H_
+#define __SP0a19_H_
+
+//ECLK Drv
+#define Pre_Value_P0_0x30 0x00
+//Filter en&dis
+#define Pre_Value_P0_0x56 0x70
+#define Pre_Value_P0_0x57 0x10 //filter outdoor
+#define Pre_Value_P0_0x58 0x10 //filter indoor
+#define Pre_Value_P0_0x59 0x10 //filter night
+#define Pre_Value_P0_0x5a 0x08 //smooth outdoor 0x02
+#define Pre_Value_P0_0x5b 0x14 //smooth indoor 0x02
+#define Pre_Value_P0_0x5c 0x30 //smooht night 0x20
+//outdoor sharpness
+#define Pre_Value_P0_0x65 0x03
+#define Pre_Value_P0_0x66 0x01
+#define Pre_Value_P0_0x67 0x03
+#define Pre_Value_P0_0x68 0x46
+//indoor sharpness
+#define Pre_Value_P0_0x6b 0x04
+#define Pre_Value_P0_0x6c 0x01
+#define Pre_Value_P0_0x6d 0x03// 0x03
+#define Pre_Value_P0_0x6e 0x43
+//night sharpness
+#define Pre_Value_P0_0x71 0x05
+#define Pre_Value_P0_0x72 0x01
+#define Pre_Value_P0_0x73 0x03
+#define Pre_Value_P0_0x74 0x43
+//color
+#define Pre_Value_P0_0x7f 0xd7 //R
+#define Pre_Value_P0_0x87 0xf8 //B
+//satutation
+#define Pre_Value_P0_0xd8 0x48
+#define Pre_Value_P0_0xd9 0x44//0x48
+#define Pre_Value_P0_0xda 0x3a//0x48
+#define Pre_Value_P0_0xdb 0x38//0x48
+//AE target
+#define Pre_Value_P0_0xf7 0x78
+#define Pre_Value_P0_0xf8 0x63
+#define Pre_Value_P0_0xf9 0x68
+#define Pre_Value_P0_0xfa 0x53
+//HEQ
+#define Pre_Value_P0_0xdd 0x78 // 0x70
+#define Pre_Value_P0_0xde 0x88 // 0x90
+//AWB pre gain
+#define Pre_Value_P1_0x28 0x75
+#define Pre_Value_P1_0x29 0x4e
+
+//VBLANK
+#define Pre_Value_P0_0x05 0x00
+#define Pre_Value_P0_0x06 0x00
+//HBLANK
+#define Pre_Value_P0_0x09 0x01
+#define Pre_Value_P0_0x0a 0x76
+
+//AE target
+#define SP0A19_P0_0xf7 0x88
+#define SP0A19_P0_0xf8 0x80
+#define SP0A19_P0_0xf9 0x70
+#define SP0A19_P0_0xfa 0x68
+//HEQ
+#define SP0A19_P0_0xdd 0x7c
+#define SP0A19_P0_0xde 0xc4
+
+// Scene Mode
+uint8_t sp0a19_scene_mode_auto[] = {
+ //caprure preview daylight 24M 50hz 20-8FPS maxgain:0x70
+
+};
+
+uint8_t sp0a19_scene_mode_night[] = {
+ //capture preview night 24M 50hz 20-6FPS maxgain:0x78
+
+};
+
+
+// White Balance
+uint8_t sp0a19_wb_auto [] = {
+ 0xfd,0x01,
+ 0x28,0xc5,
+ 0x29,0x9b,
+ 0xfd,0x00,
+ 0x32,0x15, //awb & ae opened
+ 0xfd,0x00,
+};
+
+uint8_t sp0a19_wb_incandescent [] = {
+ 0xfd,0x00,
+ 0x32,0x05,
+ 0xfd,0x01,
+ 0x28,0x89,
+ 0x29,0xb8,
+ 0xfd,0x00,
+};
+
+uint8_t sp0a19_wb_fluorescent [] = {
+ 0xfd,0x00,
+ 0x32,0x05,
+ 0xfd,0x01,
+ 0x28,0xaf,
+ 0x29,0x99,
+ 0xfd,0x00,
+};
+
+uint8_t sp0a19_wb_daylight [] = {
+ 0xfd,0x00,
+ 0x32,0x05,
+ 0xfd,0x01,
+ 0x28,0xbc,
+ 0x29,0x5d,
+ 0xfd,0x00,
+};
+
+uint8_t sp0a19_wb_cloudy [] = {
+ 0xfd,0x00,
+ 0x32,0x05,
+ 0xfd,0x01,
+ 0x28,0xbf,
+ 0x29,0x89,
+ 0xfd,0x00,
+};
+
+uint8_t sp0a19_wb_tungsten [] = {
+ 0xfd,0x00,
+ 0x32,0x05,
+ 0xfd,0x01,
+ 0x28,0x90,
+ 0x29,0xc7,
+ 0xfd,0x00,
+};
+
+
+// Exposure
+uint8_t sp0a19_exposure_neg2[] = {
+};
+
+uint8_t sp0a19_exposure_neg1[] = {
+};
+
+uint8_t sp0a19_exposure_zero[] = {
+};
+
+uint8_t sp0a19_exposure_pos1[] = {
+};
+
+uint8_t sp0a19_exposure_pos2[] = {
+};
+
+
+// Color Effect
+uint8_t sp0a19_colorfx_none[] = {
+ 0xfd, 0x00,
+ 0x62, 0x00,
+ 0x63, 0x80,
+ 0x64, 0x80,
+};
+
+uint8_t sp0a19_colorfx_bw[] = {
+ 0xfd, 0x00,
+ 0x62, 0x20,
+ 0x63, 0x80,
+ 0x64, 0x80,
+};
+
+uint8_t sp0a19_colorfx_sepia[] = {
+ 0xfd, 0x00,
+ 0x62, 0x10,
+ 0x63, 0xc0,
+ 0x64, 0x20,
+};
+
+uint8_t sp0a19_colorfx_negative[] = {
+ 0xfd, 0x00,
+ 0x62, 0x10,
+ 0x63, 0x80,
+ 0x64, 0x80,
+};
+
+uint8_t sp0a19_colorfx_emboss[] = {
+};
+
+uint8_t sp0a19_colorfx_sketch[] = {
+};
+
+uint8_t sp0a19_colorfx_sky_blue[] = {
+};
+
+uint8_t sp0a19_colorfx_grass_green[] = {
+};
+
+uint8_t sp0a19_colorfx_skin_whiten[] = {
+};
+
+uint8_t sp0a19_colorfx_vivid[] = {
+};
+
+// Brightness
+uint8_t sp0a19_brightness_neg4[] = {
+};
+
+uint8_t sp0a19_brightness_neg3[] = {
+};
+
+uint8_t sp0a19_brightness_neg2[] = {
+};
+
+uint8_t sp0a19_brightness_neg1[] = {
+};
+
+uint8_t sp0a19_brightness_zero[] = {
+};
+
+uint8_t sp0a19_brightness_pos1[] = {
+};
+
+uint8_t sp0a19_brightness_pos2[] = {
+};
+
+uint8_t sp0a19_brightness_pos3[] = {
+};
+
+uint8_t sp0a19_brightness_pos4[] = {
+};
+
+// Contrast
+uint8_t sp0a19_contrast_neg4[] = {
+};
+
+uint8_t sp0a19_contrast_neg3[] = {
+};
+
+uint8_t sp0a19_contrast_neg2[] = {
+};
+
+uint8_t sp0a19_contrast_neg1[] = {
+};
+
+uint8_t sp0a19_contrast_zero[] = {
+};
+
+uint8_t sp0a19_contrast_pos1[] = {
+};
+
+uint8_t sp0a19_contrast_pos2[] = {
+};
+
+uint8_t sp0a19_contrast_pos3[] = {
+};
+
+uint8_t sp0a19_contrast_pos4[] = {
+};
+
+// Saturation
+uint8_t sp0a19_saturation_neg4[] = {
+};
+
+uint8_t sp0a19_saturation_neg3[] = {
+};
+
+uint8_t sp0a19_saturation_neg2[] = {
+};
+
+uint8_t sp0a19_saturation_neg1[] = {
+};
+
+uint8_t sp0a19_saturation_zero[] = {
+};
+
+uint8_t sp0a19_saturation_pos1[] = {
+};
+
+uint8_t sp0a19_saturation_pos2[] = {
+};
+
+uint8_t sp0a19_saturation_pos3[] = {
+};
+
+uint8_t sp0a19_saturation_pos4[] = {
+};
+
+
+// Resolution
+
+// QCIF
+uint8_t sp0a19_176x144[]={
+};
+
+// QVGA
+uint8_t sp0a19_320x240[]={
+ 0xfd,0x01,
+ 0x0e,0x03,
+ 0xfd,0x00,
+ 0x30,0x10,
+};
+
+// CIF
+uint8_t sp0a19_352x288[]={
+};
+
+// VGA
+uint8_t sp0a19_640x480[]={
+ 0xfd,0x01,
+ 0x0e,0x00,
+ 0xfd,0x00,
+ 0x30,0x00,
+};
+
+// SVGA
+uint8_t sp0a19_800x600[]={
+};
+
+// XGA
+uint8_t sp0a19_1024x768[]={
+};
+
+// 720p
+uint8_t sp0a19_1280x720[]={
+};
+
+// UXGA
+uint8_t sp0a19_1600x1200[]={
+};
+
+// 1080p
+uint8_t sp0a19_1920x1080[]={
+};
+
+// QXGA
+uint8_t sp0a19_2048x1536[]={
+};
+
+uint8_t sp0a19_2592x1944[]={
+};
+
+// Initiliztion
+uint8_t sp0a19_default_regs_init[] = {
+ /*init registers code.*/
+ 0xfd,0x00,
+ 0x1c,0x28,
+ 0x32,0x00,
+ 0x0f,0x2f,
+ 0x10,0x2e,
+ 0x11,0x00,
+ 0x12,0x18,
+ 0x13,0x2f,
+ 0x14,0x00,
+ 0x15,0x3f,
+ 0x16,0x00,
+ 0x17,0x18,
+ 0x25,0x40,
+ 0x1a,0x0b,
+ 0x1b,0xc ,
+ 0x1e,0xb ,
+ 0x20,0x3f,
+ 0x21,0x13,
+ 0x22,0x19,
+ 0x26,0x1a,
+ 0x27,0xab,
+ 0x28,0xfd,
+ 0x30,0x00,
+ 0x31,0x00, //0x10
+ 0xfb,0x31,
+ 0x1f,0x08,
+
+ //Blacklevel
+ 0xfd,0x00,
+ 0x65,0x00,//06
+ 0x66,0x00,//06
+ 0x67,0x00,//06
+ 0x68,0x00,//06
+ 0x45,0x00,
+ 0x46,0x0f,
+
+ //ae setting
+ 0xfd,0x00,
+ 0x03,0x01,
+ 0x04,0x32,
+ 0x06,0x00,
+ 0x09,0x01,
+ 0x0a,0x46,
+ 0xf0,0x66,
+ 0xf1,0x00,
+ 0xfd,0x01,
+ 0x90,0x0c,
+ 0x92,0x01,
+ 0x98,0x66,
+ 0x99,0x00,
+ 0x9a,0x01,
+ 0x9b,0x00,
+
+ //Status
+ 0xfd,0x01,
+ 0xce,0xc8,
+ 0xcf,0x04,
+ 0xd0,0xc8,
+ 0xd1,0x04,
+
+ 0xfd,0x01,
+ 0xc4,0x56,
+ 0xc5,0x8f,
+ 0xca,0x30,
+ 0xcb,0x45,
+ 0xcc,0x70,
+ 0xcd,0x48,
+ 0xfd,0x00,
+
+ //lsc for st
+ 0xfd,0x01,
+ 0x35,0x15,
+ 0x36,0x15,
+ 0x37,0x15,
+ 0x38,0x15,
+ 0x39,0x15,
+ 0x3a,0x15,
+ 0x3b,0x13,
+ 0x3c,0x15,
+ 0x3d,0x15,
+ 0x3e,0x15,
+ 0x3f,0x15,
+ 0x40,0x18,
+ 0x41,0x00,
+ 0x42,0x04,
+ 0x43,0x04,
+ 0x44,0x00,
+ 0x45,0x00,
+ 0x46,0x00,
+ 0x47,0x00,
+ 0x48,0x00,
+ 0x49,0xfd,
+ 0x4a,0x00,
+ 0x4b,0x00,
+ 0x4c,0xfd,
+ 0xfd,0x00,
+ //awb 1
+ 0xfd,0x01,
+ 0x28,0xc5,
+ 0x29,0x9b,
+ 0x2e,0x02,
+ 0x2f,0x16,
+ 0x17,0x17,
+ 0x18,0x19,
+ 0x19,0x45,
+ 0x2a,0xef,
+ 0x2b,0x15,
+ //awb2
+ 0xfd,0x01,
+ 0x73,0x80,
+ 0x1a,0x80,
+ 0x1b,0x80,
+ //d65
+ 0x65,0xd5,
+ 0x66,0xfa,
+ 0x67,0x72,
+ 0x68,0x8a,
+ //indoor
+ 0x69,0xc6,
+ 0x6a,0xee,
+ 0x6b,0x94,
+ 0x6c,0xab,
+ //f
+ 0x61,0x7a,
+ 0x62,0x98,
+ 0x63,0xc5,
+ 0x64,0xe6,
+ //cwf
+ 0x6d,0xb9,
+ 0x6e,0xde,
+ 0x6f,0xb2,
+ 0x70,0xd5,
+
+ //////////////////skin detect//////
+ 0xfd,0x01,
+ 0x08,0x15,
+ 0x09,0x04,
+ 0x0a,0x20,
+ 0x0b,0x12,
+ 0x0c,0x27,
+ 0x0d,0x06,
+ 0x0f,0x63,
+ //BPC_grad
+ 0xfd,0x00,
+ 0x79,0xf0,
+ 0x7a,0x80, //f0
+ 0x7b,0x80, //f0
+ 0x7c,0x20, //f0
+
+ //smooth
+ 0xfd,0x00,
+ //µ¥Í¨µÀ¼äƽ»¬ãÐÖµ
+ 0x57,0x08, //raw_dif_thr_outdoor
+ 0x58,0x0c, //raw_dif_thr_normal
+ 0x56,0x10, //raw_dif_thr_dummy
+ 0x59,0x15, //raw_dif_thr_lowlight
+ //GrGbƽ»¬ãÐÖµ
+ 0x89,0x08, //raw_grgb_thr_outdoor
+ 0x8a,0x0c, //raw_grgb_thr_normal
+ 0x9c,0x10, //raw_grgb_thr_dummy
+ 0x9d,0x15, //raw_grgb_thr_lowlight
+
+ //Gr\GbÖ®¼äƽ»¬Ç¿¶È
+ 0x81,0xe0, //raw_gflt_fac_outdoor
+ 0x82,0x80, //80//raw_gflt_fac_normal
+ 0x83,0x30, //raw_gflt_fac_dummy
+ 0x84,0x18, //raw_gflt_fac_lowlight
+ //Gr¡¢Gbµ¥Í¨µÀÄÚƽ»¬Ç¿¶È
+ 0x85,0xe0, //raw_gf_fac_outdoor
+ 0x86,0x80, //raw_gf_fac_normal
+ 0x87,0x30, //raw_gf_fac_dummy
+ 0x88,0x18, //raw_gf_fac_lowlight
+ //R¡¢Bƽ»¬Ç¿¶È
+ 0x5a,0xff, //raw_rb_fac_outdoor
+ 0x5b,0xe0, //40//raw_rb_fac_normal
+ 0x5c,0x60, //raw_rb_fac_dummy
+ 0x5d,0x20, //raw_rb_fac_lowlight
+
+ //sharpen
+ 0xfd,0x01,
+ 0xe2,0x50, //sharpen_y_base
+ 0xe4,0xa0, //sharpen_y_max
+
+ 0xe5,0x08, //rangek_neg_outdoor
+ 0xd3,0x08, //rangek_pos_outdoor
+ 0xd7,0x08, //range_base_outdoor
+
+ 0xe6,0x0a, //rangek_neg_normal
+ 0xd4,0x0a, //rangek_pos_normal
+ 0xd8,0x0a, //range_base_normal
+
+ 0xe7,0x12, //rangek_neg_dummy
+ 0xd5,0x12, //rangek_pos_dummy
+ 0xd9,0x12, //range_base_dummy
+
+ 0xd2,0x15, //rangek_neg_lowlight
+ 0xd6,0x15, //rangek_pos_lowlight
+ 0xda,0x15, //range_base_lowlight
+
+ 0xe8,0x20,//sharp_fac_pos_outdoor
+ 0xec,0x2c,//sharp_fac_neg_outdoor
+ 0xe9,0x20,//sharp_fac_pos_nr
+ 0xed,0x2c,//sharp_fac_neg_nr
+ 0xea,0x18,//sharp_fac_pos_dummy
+ 0xef,0x1c,//sharp_fac_neg_dummy
+ 0xeb,0x15,//sharp_fac_pos_low
+ 0xf0,0x18,//sharp_fac_neg_low
+
+ //CCM
+ 0xfd,0x01,
+ 0xa0,0x66,//80(ºìÉ«½Ó½ü£¬·ôÉ«²»ÀíÏë)
+ 0xa1,0x0 ,//0
+ 0xa2,0x19,//0
+ 0xa3,0xf3,//f0
+ 0xa4,0x8e,//a6
+ 0xa5,0x0 ,//ea
+ 0xa6,0x0 ,//0
+ 0xa7,0xe6,//e6
+ 0xa8,0x9a,//9a
+ 0xa9,0x0 ,//0
+ 0xaa,0x3 ,//33
+ 0xab,0xc ,//c
+ 0xfd,0x00,
+
+ //gamma
+
+ 0xfd,0x00,
+ 0x8b,0x00,//0
+ 0x8c,0x0c,//11
+ 0x8d,0x19,//19
+ 0x8e,0x2C,//28
+ 0x8f,0x49,//46
+ 0x90,0x61,//61
+ 0x91,0x77,//78
+ 0x92,0x8A,//8A
+ 0x93,0x9B,//9B
+ 0x94,0xA9,//A9
+ 0x95,0xB5,//B5
+ 0x96,0xC0,//C0
+ 0x97,0xCA,//CA
+ 0x98,0xD4,//D4
+ 0x99,0xDD,//DD
+ 0x9a,0xE6,//E6
+ 0x9b,0xEb,//EF
+ 0xfd,0x01,//01
+ 0x8d,0xF2,//F7
+ 0x8e,0xFa,//FF
+
+ //rpc
+ 0xfd,0x00,
+ 0xe0,0x4c,
+ 0xe1,0x3c,
+ 0xe2,0x34,
+ 0xe3,0x2e,
+ 0xe4,0x2e,
+ 0xe5,0x2c,
+ 0xe6,0x2c,
+ 0xe8,0x2a,
+ 0xe9,0x2a,
+ 0xea,0x2a,
+ 0xeb,0x28,
+ 0xf5,0x28,
+ 0xf6,0x28,
+ //ae min gain
+ 0xfd,0x01,
+ 0x94,0xa0,//rpc_max_indr
+ 0x95,0x28,//1e//rpc_min_indr
+ 0x9c,0xa0,//rpc_max_outdr
+ 0x9d,0x28,//rpc_min_outdr
+
+ //ae target
+ 0xfd,0x00,
+ 0xed,SP0A19_P0_0xf7+0x04,
+ 0xf7,SP0A19_P0_0xf7,
+ 0xf8,SP0A19_P0_0xf8,
+ 0xec,SP0A19_P0_0xf8-0x04,
+ 0xef,SP0A19_P0_0xf9+0x04,
+ 0xf9,SP0A19_P0_0xf9,
+ 0xfa,SP0A19_P0_0xfa,
+ 0xee,SP0A19_P0_0xfa-0x04,
+ //gray detect
+ 0xfd,0x01,
+ 0x30,0x40,
+ 0x31,0x70,//
+ 0x32,0x40,//80
+ 0x33,0xef,//
+ 0x34,0x05,//04
+ 0x4d,0x2f,//40
+ 0x4e,0x20,//
+ 0x4f,0x16,//13
+ //lowlight lum
+ 0xfd,0x00,
+ 0xb2,0x10,//lum_limit
+ 0xb3,0x1f,//lum_set
+ 0xb4,0x30,//black_vt
+ 0xb5,0x45,//white_vt
+ //saturation
+ 0xfd,0x00,
+ 0xbe,0xff,
+ 0xbf,0x01,
+ 0xc0,0xff,
+ 0xc1,0xd8,
+ 0xd3,0x80,//0x78
+ 0xd4,0x78,//0x78
+ 0xd6,0x68,//0x78
+ 0xd7,0x50,//0x78
+ //HEQ
+ 0xfd,0x00,
+ 0xdc,0x00,
+ 0xdd,SP0A19_P0_0xdd,
+ 0xde,SP0A19_P0_0xde,//80
+ 0xdf,0x80,
+ //func enable
+ 0xfd,0x00,
+ 0x32,0x15,//d
+ 0x34,0x76,//16
+ 0x35,0x40,
+ 0x33,0xef,
+ 0x5f,0x51,
+ 0xfd,0x00,
+ 0x36,0x80,
+
+ };
+
+// Exit
+uint8_t sp0a19_default_regs_exit[] = {
+};
+
+#endif /* #ifndef __sp0a19_H_ */
diff --git a/drivers/media/video/wmt_v4l2/sensors/sp2518/sp2518.c b/drivers/media/video/wmt_v4l2/sensors/sp2518/sp2518.c
new file mode 100755
index 00000000..40de4d91
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/sp2518/sp2518.c
@@ -0,0 +1,326 @@
+
+#include "../cmos-subdev.h"
+#include "../../wmt-vid.h"
+#include "sp2518.h"
+
+#define sensor_write_array(sd, arr, sz) \
+ cmos_init_8bit_addr(arr, sz, (sd)->i2c_addr)
+
+#define sensor_read(sd, reg) \
+ wmt_vid_i2c_read(sd->i2c_addr, reg)
+
+#define sensor_write(sd, reg, val) \
+ wmt_vid_i2c_write(sd->i2c_addr, reg, val)
+
+struct cmos_win_size {
+ char *name;
+ int width;
+ int height;
+ uint8_t *regs;
+ size_t size;
+};
+
+#define CMOS_WIN_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r, .size = ARRAY_SIZE(r) }
+
+static const struct cmos_win_size cmos_supported_win_sizes[] = {
+ CMOS_WIN_SIZE("VGA", 640, 480, sp2518_640_480_regs),
+ CMOS_WIN_SIZE("SVGA", 800, 600, sp2518_800_600_regs),
+ CMOS_WIN_SIZE("200w", 1600, 1200, sp2518_1600_1200_regs),
+};
+
+static const struct cmos_win_size *cmos_select_win(u32 *width, u32 *height)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ if (cmos_supported_win_sizes[i].width == *width &&
+ cmos_supported_win_sizes[i].height == *height) {
+ *width = cmos_supported_win_sizes[i].width;
+ *height = cmos_supported_win_sizes[i].height;
+ return &cmos_supported_win_sizes[i];
+ }
+ }
+ return NULL;
+}
+
+static int sensor_s_wb(struct cmos_subdev *sd, enum v4l2_wb_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case WHITE_BALANCE_AUTO:
+ regs = sp2518_wb_auto;
+ size = ARRAY_SIZE(sp2518_wb_auto);
+ break;
+ case WHITE_BALANCE_INCANDESCENCE:
+ regs = sp2518_wb_incandescent;
+ size = ARRAY_SIZE(sp2518_wb_incandescent);
+ break;
+ case WHITE_BALANCE_DAYLIGHT:
+ regs = sp2518_wb_daylight;
+ size = ARRAY_SIZE(sp2518_wb_daylight);
+ break;
+ case WHITE_BALANCE_CLOUDY:
+ regs = sp2518_wb_cloudy;
+ size = ARRAY_SIZE(sp2518_wb_cloudy);
+ break;
+ case WHITE_BALANCE_FLUORESCENT:
+ regs = sp2518_wb_fluorescent;
+ size = ARRAY_SIZE(sp2518_wb_fluorescent);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_scenemode(struct cmos_subdev *sd, enum v4l2_scene_mode value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case SCENE_MODE_AUTO:
+ regs = sp2518_scene_mode_auto;
+ size = ARRAY_SIZE(sp2518_scene_mode_auto);
+ break;
+ case SCENE_MODE_NIGHTSHOT:
+ regs = sp2518_scene_mode_night;
+ size = ARRAY_SIZE(sp2518_scene_mode_night);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_exposure(struct cmos_subdev *sd, int value)
+{
+ uint8_t *regs;
+ size_t size;
+
+ switch (value) {
+ case -2:
+ regs = sp2518_exposure_neg2;
+ size = ARRAY_SIZE(sp2518_exposure_neg2);
+ break;
+ case -1:
+ regs = sp2518_exposure_neg1;
+ size = ARRAY_SIZE(sp2518_exposure_neg1);
+ break;
+ case 0:
+ regs = sp2518_exposure_zero;
+ size = ARRAY_SIZE(sp2518_exposure_zero);
+ break;
+ case 1:
+ regs = sp2518_exposure_pos1;
+ size = ARRAY_SIZE(sp2518_exposure_pos1);
+ break;
+ case 2:
+ regs = sp2518_exposure_pos2;
+ size = ARRAY_SIZE(sp2518_exposure_pos2);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, regs, size);
+ return 0;
+}
+
+static int sensor_s_hflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data;
+
+ sensor_write(sd, 0xfd, 0x00); // set page0
+ data = sensor_read(sd, 0x31);
+ switch (value) {
+ case 0:
+ data &= ~0x20;
+ break;
+ case 1:
+ data |= 0x20;
+ break;
+ default:
+ return -EINVAL;
+ }
+ sensor_write(sd, 0x31, data);
+ printk(KERN_DEBUG "%s, 0x%x\n", __func__, data);
+ return 0;
+}
+
+static int sensor_s_vflip(struct cmos_subdev *sd, int value)
+{
+ uint8_t data;
+
+ sensor_write(sd, 0xfd, 0x00); // set page0
+ data = sensor_read(sd, 0x31);
+ switch (value) {
+ case 0:
+ data &= ~0x40;
+ break;
+ case 1:
+ data |= 0x40;
+ break;
+ default:
+ return -EINVAL;
+ }
+ sensor_write(sd, 0x31, data);
+ printk(KERN_DEBUG "%s, 0x%x\n", __func__, data);
+ return 0;
+}
+
+static int sensor_queryctrl(struct cmos_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return v4l2_ctrl_query_fill(qc, 0, 3, 1, 0);
+ case V4L2_CID_EXPOSURE:
+ return v4l2_ctrl_query_fill(qc, -2, 2, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int sensor_s_ctrl(struct cmos_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_CAMERA_SCENE_MODE:
+ return sensor_s_scenemode(sd, ctrl->value);
+ case V4L2_CID_DO_WHITE_BALANCE:
+ return sensor_s_wb(sd, ctrl->value);
+ case V4L2_CID_EXPOSURE:
+ return sensor_s_exposure(sd, ctrl->value);
+ case V4L2_CID_HFLIP:
+ return sensor_s_hflip(sd, ctrl->value);
+ case V4L2_CID_VFLIP:
+ return sensor_s_vflip(sd, ctrl->value);
+ default:
+ case WMT_V4L2_CID_CAMERA_ANTIBANDING:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int sensor_g_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return -EINVAL;
+}
+
+static int sensor_s_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct cmos_win_size *win;
+
+ win = cmos_select_win(&mf->width, &mf->height);
+ if (!win) {
+ pr_err("%s, s_mbus_fmt failed, width %d, height %d\n",
+ sd->name, mf->width, mf->height);
+ return -EINVAL;
+ }
+
+ sensor_write_array(sd, win->regs, win->size);
+ return 0;
+}
+
+static int sensor_try_mbus_fmt(struct cmos_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ return 0;
+}
+
+static int sensor_enum_framesizes(struct cmos_subdev *sd,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i;
+ int num_valid = -1;
+ __u32 index = fsize->index;
+
+ for (i = 0; i < ARRAY_SIZE(cmos_supported_win_sizes); i++) {
+ const struct cmos_win_size *win = &cmos_supported_win_sizes[index];
+ if (index == ++num_valid) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = win->width;
+ fsize->discrete.height = win->height;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sensor_identify(struct cmos_subdev *sd)
+{
+ uint8_t data = 0;
+
+ sensor_write(sd, 0xfd, 0x0);
+ data = sensor_read(sd, 0x02);
+ return (data == sd->id) ? 0 : -EINVAL;
+}
+
+static int sensor_init(struct cmos_subdev *sd)
+{
+ if (sensor_identify(sd)) {
+ return -1;
+ }
+
+ sensor_write_array(sd, sp2518_default_regs_init,
+ ARRAY_SIZE(sp2518_default_regs_init));
+ return 0;
+}
+
+static int sensor_exit(struct cmos_subdev *sd)
+{
+ sensor_write_array(sd, sp2518_default_regs_exit,
+ ARRAY_SIZE(sp2518_default_regs_exit));
+ return 0;
+}
+
+static struct cmos_subdev_ops sp2518_ops = {
+ .identify = sensor_identify,
+ .init = sensor_init,
+ .exit = sensor_exit,
+ .queryctrl = sensor_queryctrl,
+ .s_ctrl = sensor_s_ctrl,
+ .s_mbus_fmt = sensor_s_mbus_fmt,
+ .g_mbus_fmt = sensor_g_mbus_fmt,
+ .try_mbus_fmt = sensor_try_mbus_fmt,
+ .enum_framesizes = sensor_enum_framesizes,
+};
+
+struct cmos_subdev sp2518 = {
+ .name = "sp2518",
+ .i2c_addr = 0x30,
+ .id = 0x53,
+ .max_width = 1600,
+ .max_height = 1200,
+ .ops = &sp2518_ops,
+};
+
+#if 0
+static int __init sp2518_init(void)
+{
+ return cmos_register_sudbdev(&sp2518);
+}
+
+static void __exit sp2518_exit(void)
+{
+ return cmos_unregister_subdev(&sp2518);
+}
+
+module_init(sp2518_init);
+module_exit(sp2518_exit);
+
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/media/video/wmt_v4l2/sensors/sp2518/sp2518.h b/drivers/media/video/wmt_v4l2/sensors/sp2518/sp2518.h
new file mode 100755
index 00000000..73b0ed0e
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/sp2518/sp2518.h
@@ -0,0 +1,1221 @@
+
+#ifndef __SP2518_H_
+#define __SP2518_H_
+
+#define SP2518_P0_0xf7 0x80//78
+#define SP2518_P0_0xf8 0x74//6e
+#define SP2518_P0_0xf9 0x80//74
+#define SP2518_P0_0xfa 0x74//6a
+//HEQ
+#define SP2518_P0_0xdd 0x80
+#define SP2518_P0_0xde 0x95//90
+//sharpen
+#define SP2518_P1_0xe9 0x20//0x30//14
+#define SP2518_P1_0xed 0x20//0x30//10
+//sat
+#define SP2518_P0_0xd3 0x77
+#define SP2518_P0_0xd4 0x77
+#define SP2518_P0_0xd6 0x77
+#define SP2518_P0_0xd7 0x77
+#define SP2518_P0_0xd8 0x77
+#define SP2518_P0_0xd9 0x77
+#define SP2518_P0_0xda 0x77
+#define SP2518_P0_0xdb 0x77
+//auto lum
+#define SP2518_NORMAL_Y0ffset 0x10
+#define SP2518_LOWLIGHT_Y0ffset 0x20
+
+#define CLK24M_48M
+// #define CLK24M_72M
+
+// Scene Mode
+uint8_t sp2518_scene_mode_auto[] = {
+ 0xfd , 0x00,
+ 0xb2,SP2518_NORMAL_Y0ffset,
+ 0xb3,0x1f,
+
+#ifdef CLK24M_48M
+ #if 0
+ //capture preview daylight 48M 50hz fix 9FPS maxgain
+ 0xfd,0x00,
+ 0x03,0x02,
+ 0x04,0xbe,
+ 0x09,0x00,
+ 0x0a,0x64,
+ 0xf0,0x75,
+ 0xf1,0x00,
+ 0xfd,0x01,
+ 0x90,0x0b,
+ 0x92,0x01,
+ 0x98,0x75,
+ 0x99,0x00,
+ 0x9a,0x01,
+ 0x9b,0x00,
+
+ // status
+ 0xfd,0x01,
+ 0xce,0x07,
+ 0xcf,0x05,
+ 0xd0,0x07,
+ 0xd1,0x05,
+ 0xd7,0x77,//exp_nr_outd_8lsb
+ 0xd8,0x00,
+ 0xd9,0x7b,//exp_outd_nr_8lsb
+ 0xda,0x00,
+ 0xfd,0x00,
+ #else
+ //capture preview daylight 48M 50hz fix 8FPS maxgain
+ 0xfd,0x00,
+ 0x03,0x02,
+ 0x04,0x70,
+ 0x09,0x00,
+ 0x0a,0xe4,
+ 0xf0,0x68,
+ 0xf1,0x00,
+ 0xfd,0x01,
+ 0x90,0x0c,
+ 0x92,0x01,
+ 0x98,0x68,
+ 0x99,0x00,
+ 0x9a,0x01,
+ 0x9b,0x00,
+
+ // status
+ 0xfd,0x01,
+ 0xce,0xe0,
+ 0xcf,0x04,
+ 0xd0,0xe0,
+ 0xd1,0x04,
+ 0xd7,0x64,//exp_nr_outd_8lsb
+ 0xd8,0x00,
+ 0xd9,0x68,//exp_outd_nr_8lsb
+ 0xda,0x00,
+ 0xfd,0x00,
+ #endif
+#elif defined(CLK24M_72M)
+ //capture preview daylight 72M 50hz 8-13FPS maxgain
+ 0xfd , 0x00,
+ 0x03 , 0xf6,
+ 0x04 , 0xbe,
+ 0x09 , 0x00,
+ 0x0a , 0x8b,
+ 0xf0 , 0xa9,
+ 0xf1 , 0x00,
+ 0xfd , 0x01,
+ 0x90 , 0x0c,
+ 0x92 , 0x01,
+ 0x98 , 0xa9,
+ 0x99 , 0x00,
+ 0x9a , 0x01,
+ 0x9b , 0x00,
+
+ // status
+ 0xfd , 0x01,
+ 0xce , 0xec,
+ 0xcf , 0x07,
+ 0xd0 , 0xec,
+ 0xd1 , 0x07,
+ 0xd7 , 0xab,//exp_nr_outd_8lsb
+ 0xd8 , 0x00,
+ 0xd9 , 0xaf,//exp_outd_nr_8lsb
+ 0xda , 0x00,
+ 0xfd , 0x00,
+#endif
+};
+
+uint8_t sp2518_scene_mode_night[] = {
+ 0xfd , 0x00,
+ 0xb2 , SP2518_LOWLIGHT_Y0ffset,
+ 0xb3 , 0x1f,
+
+#ifdef CLK24M_48M
+ //capture preview night 48M 50hz fix 6FPS maxgain
+ 0xfd,0x00,
+ 0x03,0x01,
+ 0x04,0xd4,
+ 0x09,0x02,
+ 0x0a,0x64,
+ 0xf0,0x4e,
+ 0xf1,0x00,
+ 0xfd,0x01,
+ 0x90,0x10,
+ 0x92,0x01,
+ 0x98,0x4e,
+ 0x99,0x00,
+ 0x9a,0x01,
+ 0x9b,0x00,
+
+ // status
+ 0xfd,0x01,
+ 0xce,0xe0,
+ 0xcf,0x04,
+ 0xd0,0xe0,
+ 0xd1,0x04,
+ 0xd7,0x50,//exp_nr_outd_8lsb
+ 0xd8,0x00,
+ 0xd9,0x54,//exp_outd_nr_8lsb
+ 0xda,0x00,
+ 0xfd,0x00,
+#elif defined(CLK24M_72M)
+ //capture preview night 72M 50hz fix 6FPS maxgain
+ 0xfd , 0x00,
+ 0x03 , 0x01,
+ 0x04 , 0xd4,
+ 0x05 , 0x00,
+ 0x06 , 0x00,
+ 0x07 , 0x00,
+ 0x08 , 0x00,
+ 0x09 , 0x05,
+ 0x0a , 0x66,
+ 0xf0 , 0x4e,
+ 0xf1 , 0x00,
+ 0xfd , 0x01,
+ 0x90 , 0x10,
+ 0x92 , 0x01,
+ 0x98 , 0x4e,
+ 0x99 , 0x00,
+ 0x9a , 0x01,
+ 0x9b , 0x00,
+ //Status
+ 0xfd , 0x01,
+ 0xce , 0xe0,
+ 0xcf , 0x04,
+ 0xd0 , 0xe0,
+ 0xd1 , 0x04,
+ 0xd7 , 0x50,
+ 0xd8 , 0x00,
+ 0xd9 , 0x54,
+ 0xda , 0x00,
+ 0xfd , 0x00,
+#endif
+};
+
+
+// White Balance
+uint8_t sp2518_wb_auto [] = {
+ 0xfd, 0x01,
+ 0x28, 0xce,
+ 0x29, 0x8a,
+ 0xfd, 0x00,
+ 0x32, 0x0d,
+ 0xfd, 0x00,
+};
+
+uint8_t sp2518_wb_incandescent [] = {
+ 0xfd,0x00,
+ 0x32,0x05,
+ 0xfd,0x01,
+ 0x28,0x7b,
+ 0x29,0xd3,
+ 0xfd,0x00,
+};
+
+uint8_t sp2518_wb_fluorescent [] = {
+ 0xfd,0x00,
+ 0x32,0x05,
+ 0xfd,0x01,
+ 0x28,0xb4,
+ 0x29,0xc4,
+ 0xfd,0x00,
+};
+
+uint8_t sp2518_wb_daylight [] = {
+ 0xfd,0x00,
+ 0x32,0x05,
+ 0xfd,0x01,
+ 0x28,0xc1,
+ 0x29,0x88,
+ 0xfd,0x00,
+};
+
+uint8_t sp2518_wb_cloudy [] = {
+ 0xfd,0x00,
+ 0x32,0x05,
+ 0xfd,0x01,
+ 0x28,0xe2,
+ 0x29,0x82,
+ 0xfd,0x00,
+};
+
+uint8_t sp2518_wb_tungsten [] = {
+ 0xfd,0x00,
+ 0x32,0x05,
+ 0xfd,0x01,
+ 0x28,0xae,
+ 0x29,0xcc,
+ 0xfd,0x00,
+};
+
+
+// Exposure
+uint8_t sp2518_exposure_neg2[] = {
+ 0xfd,0x00,
+ 0xdc,0xe0,
+};
+
+uint8_t sp2518_exposure_neg1[] = {
+ 0xfd,0x00,
+ 0xdc,0xf0,
+};
+
+uint8_t sp2518_exposure_zero[] = {
+ 0xfd,0x00,
+ 0xdc,0x00,
+};
+
+uint8_t sp2518_exposure_pos1[] = {
+ 0xfd,0x00,
+ 0xdc,0x10,
+};
+
+uint8_t sp2518_exposure_pos2[] = {
+ 0xfd,0x00,
+ 0xdc,0x20,
+};
+
+
+// Color Effect
+uint8_t sp2518_colorfx_none[] = {
+ 0xfd,0x00,
+ 0x62,0x00,
+ 0x63,0x80,
+ 0x64,0x80,
+};
+
+uint8_t sp2518_colorfx_bw[] = {
+ 0xfd,0x00,
+ 0x62,0x20,
+ 0x63,0x80,
+ 0x64,0x80,
+};
+
+uint8_t sp2518_colorfx_sepia[] = {
+ 0xfd,0x00,
+ 0x62,0x10,
+ 0x63,0xb0,
+ 0x64,0x40,
+};
+
+uint8_t sp2518_colorfx_negative[] = {
+ 0xfd,0x00,
+ 0x62,0x04,
+ 0x63,0x80,
+ 0x64,0x80,
+};
+
+uint8_t sp2518_colorfx_emboss[] = {
+ 0xfd,0x00,
+ 0x62,0x01,
+ 0x63,0x80,
+ 0x64,0x80,
+};
+
+uint8_t sp2518_colorfx_sketch[] = {
+ 0xfd,0x00,
+ 0x62,0x40,
+ 0x63,0x80,
+ 0x64,0x80,
+};
+
+uint8_t sp2518_colorfx_sky_blue[] = {
+ 0xfd,0x00,
+ 0x62,0x10,
+ 0x63,0x80,
+ 0x64,0xb0,
+};
+
+uint8_t sp2518_colorfx_grass_green[] = {
+ 0xfd,0x00,
+ 0x62,0x10,
+ 0x63,0x50,
+ 0x64,0x50,
+};
+
+uint8_t sp2518_colorfx_skin_whiten[] = {
+};
+
+uint8_t sp2518_colorfx_vivid[] = {
+};
+
+// Brightness
+uint8_t sp2518_brightness_neg4[] = {
+ 0xfd,0x00,
+ 0xdc,0xc0,
+};
+
+uint8_t sp2518_brightness_neg3[] = {
+ 0xfd,0x00,
+ 0xdc,0xd0,
+};
+
+uint8_t sp2518_brightness_neg2[] = {
+ 0xfd,0x00,
+ 0xdc,0xe0,
+};
+
+uint8_t sp2518_brightness_neg1[] = {
+ 0xfd,0x00,
+ 0xdc,0xf0,
+};
+
+uint8_t sp2518_brightness_zero[] = {
+ 0xfd,0x00,
+ 0xdc,0x00,
+};
+
+uint8_t sp2518_brightness_pos1[] = {
+ 0xfd,0x00,
+ 0xdc,0x10,
+};
+
+uint8_t sp2518_brightness_pos2[] = {
+ 0xfd,0x00,
+ 0xdc,0x20,
+};
+
+uint8_t sp2518_brightness_pos3[] = {
+ 0xfd,0x00,
+ 0xdc,0x30,
+};
+
+uint8_t sp2518_brightness_pos4[] = {
+ 0xfd,0x00,
+ 0xdc,0x40,
+};
+
+// Contrast
+uint8_t sp2518_contrast_neg4[] = {
+ 0xfd,0x00,
+ 0xde,SP2518_P0_0xde-0x20,
+};
+
+uint8_t sp2518_contrast_neg3[] = {
+ 0xfd,0x00,
+ 0xde,SP2518_P0_0xde-0x18,
+};
+
+uint8_t sp2518_contrast_neg2[] = {
+ 0xfd,0x00,
+ 0xde,SP2518_P0_0xde-0x10,
+};
+
+uint8_t sp2518_contrast_neg1[] = {
+ 0xfd,0x00,
+ 0xde,SP2518_P0_0xde-0x08,
+};
+
+uint8_t sp2518_contrast_zero[] = {
+ 0xfd,0x00,
+ 0xde,SP2518_P0_0xde,
+};
+
+uint8_t sp2518_contrast_pos1[] = {
+ 0xfd,0x00,
+ 0xde,SP2518_P0_0xde+0x08,
+};
+
+uint8_t sp2518_contrast_pos2[] = {
+ 0xfd,0x00,
+ 0xde,SP2518_P0_0xde+0x10,
+};
+
+uint8_t sp2518_contrast_pos3[] = {
+ 0xfd,0x00,
+ 0xde,SP2518_P0_0xde+0x18,
+};
+
+uint8_t sp2518_contrast_pos4[] = {
+ 0xfd,0x00,
+ 0xde,SP2518_P0_0xde+0x20,
+};
+
+// Saturation
+uint8_t sp2518_saturation_neg4[] = {
+ 0xfd,0x00,
+ 0xd3,SP2518_P0_0xd3-32,
+ 0xd4,SP2518_P0_0xd4-32,
+ 0xd6,SP2518_P0_0xd6-32,
+ 0xd7,SP2518_P0_0xd7-32,
+ 0xd8,SP2518_P0_0xd8-32,//UV outdoor
+ 0xd9,SP2518_P0_0xd9-32,//UV indoor
+ 0xda,SP2518_P0_0xda-32,//UV dummy
+ 0xdb,SP2518_P0_0xdb-32,//UV lowlight
+};
+
+uint8_t sp2518_saturation_neg3[] = {
+ 0xfd,0x00,
+ 0xd3,SP2518_P0_0xd3-24,
+ 0xd4,SP2518_P0_0xd4-24,
+ 0xd6,SP2518_P0_0xd6-24,
+ 0xd7,SP2518_P0_0xd7-24,
+ 0xd8,SP2518_P0_0xd8-24,//UV outdoor
+ 0xd9,SP2518_P0_0xd9-24,//UV indoor
+ 0xda,SP2518_P0_0xda-24,//UV dummy
+ 0xdb,SP2518_P0_0xdb-24,//UV lowlight
+};
+
+uint8_t sp2518_saturation_neg2[] = {
+ 0xfd,0x00,
+ 0xd3,SP2518_P0_0xd3-16,
+ 0xd4,SP2518_P0_0xd4-16,
+ 0xd6,SP2518_P0_0xd6-16,
+ 0xd7,SP2518_P0_0xd7-16,
+ 0xd8,SP2518_P0_0xd8-16,//UV outdoor
+ 0xd9,SP2518_P0_0xd9-16,//UV indoor
+ 0xda,SP2518_P0_0xda-16,//UV dummy
+ 0xdb,SP2518_P0_0xdb-16,//UV lowlight
+};
+
+uint8_t sp2518_saturation_neg1[] = {
+ 0xfd,0x00,
+ 0xd3,SP2518_P0_0xd3-8,
+ 0xd4,SP2518_P0_0xd4-8,
+ 0xd6,SP2518_P0_0xd6-8,
+ 0xd7,SP2518_P0_0xd7-8,
+ 0xd8,SP2518_P0_0xd8-8,//UV outdoor
+ 0xd9,SP2518_P0_0xd9-8,//UV indoor
+ 0xda,SP2518_P0_0xda-8,//UV dummy
+ 0xdb,SP2518_P0_0xdb-8,//UV lowlight
+};
+
+uint8_t sp2518_saturation_zero[] = {
+ 0xfd,0x00,
+ 0xd3,SP2518_P0_0xd3,
+ 0xd4,SP2518_P0_0xd4,
+ 0xd6,SP2518_P0_0xd6,
+ 0xd7,SP2518_P0_0xd7,
+ 0xd8,SP2518_P0_0xd8,//UV outdoor
+ 0xd9,SP2518_P0_0xd9,//UV indoor
+ 0xda,SP2518_P0_0xda,//UV dummy
+ 0xdb,SP2518_P0_0xdb,//UV lowlight
+};
+
+uint8_t sp2518_saturation_pos1[] = {
+ 0xfd,0x00,
+ 0xd3,SP2518_P0_0xd3+8,
+ 0xd4,SP2518_P0_0xd4+8,
+ 0xd6,SP2518_P0_0xd6+8,
+ 0xd7,SP2518_P0_0xd7+8,
+ 0xd8,SP2518_P0_0xd8+8,//UV outdoor
+ 0xd9,SP2518_P0_0xd9+8,//UV indoor
+ 0xda,SP2518_P0_0xda+8,//UV dummy
+ 0xdb,SP2518_P0_0xdb+8,//UV lowlight
+};
+
+uint8_t sp2518_saturation_pos2[] = {
+ 0xfd,0x00,
+ 0xd3,SP2518_P0_0xd3+16,
+ 0xd4,SP2518_P0_0xd4+16,
+ 0xd6,SP2518_P0_0xd6+16,
+ 0xd7,SP2518_P0_0xd7+16,
+ 0xd8,SP2518_P0_0xd8+16,//UV outdoor
+ 0xd9,SP2518_P0_0xd9+16,//UV indoor
+ 0xda,SP2518_P0_0xda+16,//UV dummy
+ 0xdb,SP2518_P0_0xdb+16,//UV lowlight
+};
+
+uint8_t sp2518_saturation_pos3[] = {
+ 0xfd,0x00,
+ 0xd3,SP2518_P0_0xd3+24,
+ 0xd4,SP2518_P0_0xd4+24,
+ 0xd6,SP2518_P0_0xd6+24,
+ 0xd7,SP2518_P0_0xd7+24,
+ 0xd8,SP2518_P0_0xd8+24,//UV outdoor
+ 0xd9,SP2518_P0_0xd9+24,//UV indoor
+ 0xda,SP2518_P0_0xda+24,//UV dummy
+ 0xdb,SP2518_P0_0xdb+24,//UV lowlight
+};
+
+uint8_t sp2518_saturation_pos4[] = {
+ 0xfd,0x00,
+ 0xd3,SP2518_P0_0xd3+32,
+ 0xd4,SP2518_P0_0xd4+32,
+ 0xd6,SP2518_P0_0xd6+32,
+ 0xd7,SP2518_P0_0xd7+32,
+ 0xd8,SP2518_P0_0xd8+32,//UV outdoor
+ 0xd9,SP2518_P0_0xd9+32,//UV indoor
+ 0xda,SP2518_P0_0xda+32,//UV dummy
+ 0xdb,SP2518_P0_0xdb+32,//UV lowlight
+};
+
+
+// Resolution
+
+// QCIF
+uint8_t sp2518_176x144[]={
+};
+
+// QVGA
+uint8_t sp2518_320x240[]={
+};
+
+// CIF
+uint8_t sp2518_352x288[]={
+};
+
+// VGA
+uint8_t sp2518_640_480_regs[]={
+ 0xfd,0x00,
+
+ 0x47,0x00,
+ 0x48,0x00,
+ 0x49,0x04,
+ 0x4a,0xb0,
+
+ 0x4b,0x00,
+ 0x4c,0x00,
+ 0x4d,0x06,
+ 0x4e,0x40,
+
+ 0xfd,0x01,
+ 0x06,0x00,
+ 0x07,0x50,
+ 0x08,0x00,
+ 0x09,0x50,
+ 0x0a,0x01, //480
+ 0x0b,0xe0,
+ 0x0c,0x02, //640
+ 0x0d,0x80,
+ 0x0e,0x01,
+ 0xfd,0x00,
+};
+
+// SVGA
+uint8_t sp2518_800_600_regs[]={
+ 0xfd,0x00,
+
+ 0x47,0x00,
+ 0x48,0x00,
+ 0x49,0x04,
+ 0x4a,0xb0,
+
+ 0x4b,0x00,
+ 0x4c,0x00,
+ 0x4d,0x06,
+ 0x4e,0x40,
+ 0xfd,0x01,
+ 0x06,0x00,
+ 0x07,0x40,
+ 0x08,0x00,
+ 0x09,0x40,
+ 0x0a,0x02, //600
+ 0x0b,0x58,
+ 0x0c,0x03, //800
+ 0x0d,0x20,
+ 0x0e,0x01,
+ 0xfd,0x00,
+};
+
+// XGA
+uint8_t sp2518_1024x768[]={
+};
+
+// 720p
+uint8_t sp2518_1280x720[]={
+};
+
+// UXGA
+uint8_t sp2518_1600_1200_regs[]={
+ 0xfd,0x00,
+ 0x47,0x00,
+ 0x48,0x00,
+ 0x49,0x04,
+ 0x4a,0xb0,
+
+ 0x4b,0x00,
+ 0x4c,0x00,
+ 0x4d,0x06,
+ 0x4e,0x40,
+
+ 0xfd,0x01,
+ 0x0e,0x00,
+ 0xfd,0x00,
+};
+
+// 1080p
+uint8_t sp2518_1920x1080[]={
+};
+
+// QXGA
+uint8_t sp2518_2048x1536[]={
+};
+
+uint8_t sp2518_2592x1944[]={
+};
+
+// Initiliztion
+uint8_t sp2518_default_regs_init[] = {
+ 0xfd,0x00,
+ 0x1b,0x1a,//drv ability
+ 0x0e,0x01,
+
+ 0x0f,0x2f,
+ 0x10,0x2e,
+ 0x11,0x00,
+ 0x12,0x4f,
+ 0x14,0x40,// 0x20 zch 20120920
+ 0x16,0x02,
+ 0x17,0x10,
+ 0x1a,0x1f,
+ 0x1e,0x81,
+ 0x21,0x00,
+ 0x22,0x1b,
+ 0x25,0x10,
+ 0x26,0x25,
+ 0x27,0x6d,
+ 0x2c,0x23,//31 Ronlus remove balck dot0x45,
+ 0x2d,0x75,
+ 0x2e,0x38,//sxga 0x18
+
+ 0x31,0x10,//mirror upside down
+ 0x44,0x03,
+ 0x6f,0x00,
+ 0xa0,0x04,
+ 0x5f,0x01,
+ 0x32,0x00,
+ 0xfd,0x01,
+ 0x2c,0x00,
+ 0x2d,0x00,
+ 0xfd,0x00,
+ 0xfb,0x83,
+ 0xf4,0x09,
+ //Pregain
+ 0xfd,0x01,
+ 0xc6,0x90,
+ 0xc7,0x90,
+ 0xc8,0x90,
+ 0xc9,0x90,
+ //blacklevel
+ 0xfd,0x00,
+ 0x65,0x08,
+ 0x66,0x08,
+ 0x67,0x08,
+ 0x68,0x08,
+
+ //bpc
+ 0x46,0xff,
+ //rpc
+ 0xfd,0x00,
+ 0xe0,0x6c,
+ 0xe1,0x54,
+ 0xe2,0x48,
+ 0xe3,0x40,
+ 0xe4,0x40,
+ 0xe5,0x3e,
+ 0xe6,0x3e,
+ 0xe8,0x3a,
+ 0xe9,0x3a,
+ 0xea,0x3a,
+ 0xeb,0x38,
+ 0xf5,0x38,
+ 0xf6,0x38,
+ 0xfd,0x01,
+ 0x94,0xc0,//f8
+ 0x95,0x38,
+ 0x9c,0x6c,
+ 0x9d,0x38,
+#ifdef CLK24M_48M
+ #if 0
+ //capture preview daylight 48M 50hz fix 9FPS maxgain
+ 0xfd,0x00,
+ 0x30,0x04,
+ 0x03,0x02,
+ 0x04,0xbe,
+ 0x09,0x00,
+ 0x0a,0x64,
+ 0xf0,0x75,
+ 0xf1,0x00,
+ 0xfd,0x01,
+ 0x90,0x0b,
+ 0x92,0x01,
+ 0x98,0x75,
+ 0x99,0x00,
+ 0x9a,0x01,
+ 0x9b,0x00,
+
+ // status
+ 0xfd,0x01,
+ 0xce,0x07,
+ 0xcf,0x05,
+ 0xd0,0x07,
+ 0xd1,0x05,
+ 0xd7,0x77,//exp_nr_outd_8lsb
+ 0xd8,0x00,
+ 0xd9,0x7b,//exp_outd_nr_8lsb
+ 0xda,0x00,
+ 0xfd,0x00,
+ #else
+ //capture preview daylight 48M 50hz fix 8FPS maxgain
+ 0xfd,0x00,
+ 0x30,0x04,
+ 0x03,0x02,
+ 0x04,0x70,
+ 0x09,0x00,
+ 0x0a,0xe4,
+ 0xf0,0x68,
+ 0xf1,0x00,
+ 0xfd,0x01,
+ 0x90,0x0c,
+ 0x92,0x01,
+ 0x98,0x68,
+ 0x99,0x00,
+ 0x9a,0x01,
+ 0x9b,0x00,
+
+ // status
+ 0xfd,0x01,
+ 0xce,0xe0,
+ 0xcf,0x04,
+ 0xd0,0xe0,
+ 0xd1,0x04,
+ 0xd7,0x64,//exp_nr_outd_8lsb
+ 0xd8,0x00,
+ 0xd9,0x68,//exp_outd_nr_8lsb
+ 0xda,0x00,
+ 0xfd,0x00,
+ #endif
+#elif defined(CLK24M_72M)
+ //capturepreviewdaylight72M50hz8-13FPSmaxgain
+ 0xfd,0x00,
+ 0x30,0x08
+ 0x03,0xf6,
+ 0x04,0xbe,
+ 0x09,0x00,
+ 0x0a,0x8b,
+ 0xf0,0xa9,
+ 0xf1,0x00,
+ 0xfd,0x01,
+ 0x90,0x0c,
+ 0x92,0x01,
+ 0x98,0xa9,
+ 0x99,0x00,
+ 0x9a,0x01,
+ 0x9b,0x00,
+
+ //status
+ 0xfd,0x01,
+ 0xce,0xec,
+ 0xcf,0x07,
+ 0xd0,0xec,
+ 0xd1,0x07,
+ 0xd7,0xab,//exp_nr_outd_8lsb
+ 0xd8,0x00,
+ 0xd9,0xaf,//exp_outd_nr_8lsb
+ 0xda,0x00,
+ 0xfd,0x00,
+#endif
+
+#if 0
+ /*24*3pll 8~13fps 50hz*/
+ 0xfd , 0x00,
+ 0x03 , 0x03,
+ 0x04 , 0xf6,
+ 0x05 , 0x00,
+ 0x06 , 0x00,
+ 0x07 , 0x00,
+ 0x08 , 0x00,
+ 0x09 , 0x00,
+ 0x0a , 0x8b,
+ 0x2f , 0x00,
+ 0x30 , 0x08, //0x08 modify by sp_yjp,20120809
+ 0xf0 , 0xa9,
+ 0xf1 , 0x00,
+ 0xfd , 0x01,
+ 0x90 , 0x0c,
+ 0x92 , 0x01,
+ 0x98 , 0xa9,
+ 0x99 , 0x00,
+ 0x9a , 0x01,
+ 0x9b , 0x00,
+
+ //Status
+ 0xfd , 0x01,
+ 0xce , 0xec,
+ 0xcf , 0x07,
+ 0xd0 , 0xec,
+ 0xd1 , 0x07,
+ 0xd7 , 0xab,//a5
+ 0xd8 , 0x00,
+ 0xd9 , 0xaf,//a9
+ 0xda , 0x00,
+ 0xfd , 0x00,
+#endif
+
+ 0xfd,0x01,
+ 0xca,0x30,//mean dummy2low
+ 0xcb,0x50,//mean low2dummy
+ 0xcc,0xc0,//f8;rpc low
+ 0xcd,0xc0,//rpc dummy
+ 0xd5,0x80,//mean normal2dummy
+ 0xd6,0x90,//mean dummy2normal
+ 0xfd,0x00,
+
+ //lens shading
+ 0xfd,0x00,
+ 0xa1,0x20,
+ 0xa2,0x20,
+ 0xa3,0x20,
+ 0xa4,0xff,
+ 0xa5,0x80,
+ 0xa6,0x80,
+ 0xfd,0x01,
+ 0x64,0x1e,//28
+ 0x65,0x1c,//25
+ 0x66,0x1c,//2a
+ 0x67,0x16,//25
+ 0x68,0x1c,//25
+ 0x69,0x1c,//29
+ 0x6a,0x1a,//28
+ 0x6b,0x16,//20
+ 0x6c,0x1a,//22
+ 0x6d,0x1a,//22
+ 0x6e,0x1a,//22
+ 0x6f,0x16,//1c
+ 0xb8,0x04,//0a
+ 0xb9,0x13,//0a
+ 0xba,0x00,//23
+ 0xbb,0x03,//14
+ 0xbc,0x03,//08
+ 0xbd,0x11,//08
+ 0xbe,0x00,//12
+ 0xbf,0x02,//00
+ 0xc0,0x04,//05
+ 0xc1,0x0e,//05
+ 0xc2,0x00,//18
+ 0xc3,0x05,//08
+ //raw filter
+ 0xfd,0x01,
+ 0xde,0x0f,
+ 0xfd,0x00,
+ 0x57,0x08,//raw_dif_thr
+ 0x58,0x08,//a
+ 0x56,0x08,//a
+ 0x59,0x10,
+
+ 0x5a,0xa0,//raw_rb_fac_outdoor
+ 0xc4,0xa0,//60raw_rb_fac_indoor
+ 0x43,0xa0,//40raw_rb_fac_dummy
+ 0xad,0x40,//raw_rb_fac_low
+
+ 0x4f,0xa0,//raw_gf_fac_outdoor
+ 0xc3,0xa0,//60raw_gf_fac_indoor
+ 0x3f,0xa0,//40raw_gf_fac_dummy
+ 0x42,0x40,//raw_gf_fac_low
+ 0xc2,0x15,
+
+ 0xb6,0x80,//raw_gflt_fac_outdoor
+ 0xb7,0x80,//60raw_gflt_fac_normal
+ 0xb8,0x40,//40raw_gflt_fac_dummy
+ 0xb9,0x20,//raw_gflt_fac_low
+
+ 0xfd,0x01,
+ 0x50,0x0c,//raw_grgb_thr
+ 0x51,0x0c,
+ 0x52,0x10,
+ 0x53,0x10,
+ 0xfd,0x00,
+ // awb1
+ 0xfd,0x01,
+ 0x11,0x10,
+ 0x12,0x1f,
+ 0x16,0x1c,
+ 0x18,0x00,
+ 0x19,0x00,
+ 0x1b,0x96,
+ 0x1a,0x9a,//95
+ 0x1e,0x2f,
+ 0x1f,0x29,
+ 0x20,0xff,
+ 0x22,0xff,
+ 0x28,0xce,
+ 0x29,0x8a,
+ 0xfd,0x00,
+ 0xe7,0x03,
+ 0xe7,0x00,
+ 0xfd,0x01,
+ 0x2a,0xf0,
+ 0x2b,0x10,
+ 0x2e,0x04,
+ 0x2f,0x18,
+ 0x21,0x60,
+ 0x23,0x60,
+ 0x8b,0xab,
+ 0x8f,0x12,
+ //awb2
+ 0xfd,0x01,
+ 0x1a,0x80,
+ 0x1b,0x80,
+ 0x43,0x80,
+ //outdoor
+ 0x00,0xd4,
+ 0x01,0xb0,
+ 0x02,0x90,
+ 0x03,0x78,
+ //d65
+ 0x35,0xd6,//d6;b0
+ 0x36,0xf0,//f0;d1;e9
+ 0x37,0x7a,//8a;70
+ 0x38,0x9a,//dc;9a;af
+ //indoor
+ 0x39,0xab,
+ 0x3a,0xca,
+ 0x3b,0xa3,
+ 0x3c,0xc1,
+ //f
+ 0x31,0x82,//7d
+ 0x32,0xa5,//a0;74
+ 0x33,0xd6,//d2
+ 0x34,0xec,//e8
+ 0x3d,0xa5,//a7;88
+ 0x3e,0xc2,//be;bb
+ 0x3f,0xa7,//b3;ad
+ 0x40,0xc5,//c5;d0
+ //Color Correction
+ 0xfd,0x01,
+ 0x1c,0xc0,
+ 0x1d,0x95,
+ 0xa0,0xa6,//b8
+ 0xa1,0xda,//;d5
+ 0xa2,0x00,//;f2
+ 0xa3,0x06,//;e8
+ 0xa4,0xb2,//;95
+ 0xa5,0xc7,//;03
+ 0xa6,0x00,//;f2
+ 0xa7,0xce,//;c4
+ 0xa8,0xb2,//;ca
+ 0xa9,0x0c,//;3c
+ 0xaa,0x30,//;03
+ 0xab,0x0c,//;0f
+ 0xac,0xc0,//b8
+ 0xad,0xc0,//d5
+ 0xae,0x00,//f2
+ 0xaf,0xf2,//e8
+ 0xb0,0xa6,//95
+ 0xb1,0xe8,//03
+ 0xb2,0x00,//f2
+ 0xb3,0xe7,//c4
+ 0xb4,0x99,//ca
+ 0xb5,0x0c,//3c
+ 0xb6,0x33,//03
+ 0xb7,0x0c,//0f
+ //Saturation
+ 0xfd,0x00,
+ 0xbf,0x01,
+ 0xbe,0xbb,
+ 0xc0,0xb0,
+ 0xc1,0xf0,
+
+ 0xd3,SP2518_P0_0xd3,
+ 0xd4,SP2518_P0_0xd4,
+ 0xd6,SP2518_P0_0xd6,
+ 0xd7,SP2518_P0_0xd7,
+ 0xd8,SP2518_P0_0xd8,//UV outdoor
+ 0xd9,SP2518_P0_0xd9,//UV indoor
+ 0xda,SP2518_P0_0xda,//UV dummy
+ 0xdb,SP2518_P0_0xdb,//UV lowlight
+
+ //uv_dif
+ 0xfd,0x00,
+ 0xf3,0x03,
+ 0xb0,0x00,
+ 0xb1,0x23,
+ //gamma1
+ 0xfd,0x00,//
+ 0x8b,0x0 ,//0 ;0
+ 0x8c,0xA ,//14;A
+ 0x8d,0x13,//24;13
+ 0x8e,0x25,//3a;25
+ 0x8f,0x43,//59;43
+ 0x90,0x5D,//6f;5D
+ 0x91,0x74,//84;74
+ 0x92,0x88,//95;88
+ 0x93,0x9A,//a3;9A
+ 0x94,0xA9,//b1;A9
+ 0x95,0xB5,//be;B5
+ 0x96,0xC0,//c7;C0
+ 0x97,0xCA,//d1;CA
+ 0x98,0xD4,//d9;D4
+ 0x99,0xDD,//e1;DD
+ 0x9a,0xE6,//e9;E6
+ 0x9b,0xEF,//f1;EF
+ 0xfd,0x01,//01;01
+ 0x8d,0xF7,//f9;F7
+ 0x8e,0xFF,//ff;FF
+ //gamma2
+ 0xfd,0x00,//
+ 0x78,0x0 ,//0
+ 0x79,0xA ,//14
+ 0x7a,0x13,//24
+ 0x7b,0x25,//3a
+ 0x7c,0x43,//59
+ 0x7d,0x5D,//6f
+ 0x7e,0x74,//84
+ 0x7f,0x88,//95
+ 0x80,0x9A,//a3
+ 0x81,0xA9,//b1
+ 0x82,0xB5,//be
+ 0x83,0xC0,//c7
+ 0x84,0xCA,//d1
+ 0x85,0xD4,//d9
+ 0x86,0xDD,//e1
+ 0x87,0xE6,//e9
+ 0x88,0xEF,//f1
+ 0x89,0xF7,//f9
+ 0x8a,0xFF,//ff
+ /*//¹â°ß¹ý¶ÈºÃ
+ //gamma1
+ 0xfd,0x00,
+ 0x8b,0x00,
+ 0x8c,0x14,
+ 0x8d,0x24,
+ 0x8e,0x3A,
+ 0x8f,0x59,
+ 0x90,0x70,
+ 0x91,0x85,
+ 0x92,0x96,
+ 0x93,0xA6,
+ 0x94,0xB3,
+ 0x95,0xBE,
+ 0x96,0xC9,
+ 0x97,0xD2,
+ 0x98,0xDB,
+ 0x99,0xE3,
+ 0x9a,0xEB,
+ 0x9b,0xF2,
+ 0xfd,0x01,
+ 0x8d,0xF9,
+ 0x8e,0xFF,
+ //gamma2
+ 0xfd,0x00,
+ 0x78,0x00,
+ 0x79,0x14,
+ 0x7a,0x24,
+ 0x7b,0x3A,
+ 0x7c,0x59,
+ 0x7d,0x70,
+ 0x7e,0x85,
+ 0x7f,0x96,
+ 0x80,0xA6,
+ 0x81,0xB3,
+ 0x82,0xBE,
+ 0x83,0xC9,
+ 0x84,0xD2,
+ 0x85,0xDB,
+ 0x86,0xE3,
+ 0x87,0xEB,
+ 0x88,0xF2,
+ 0x89,0xF9,
+ 0x8a,0xFF,
+ */
+ //gamma_ae
+ 0xfd,0x01,
+ 0x96,0x46,
+ 0x97,0x14,
+ 0x9f,0x06,
+ //HEQ
+ 0xfd,0x00,//
+ 0xdd,SP2518_P0_0xdd,//0x80
+ 0xde,SP2518_P0_0xde,//a0
+ 0xdf,0x80,//
+ //Ytarget
+ 0xfd,0x00,//
+ 0xed,SP2518_P0_0xf7+0x04,
+ 0xf7,SP2518_P0_0xf7,
+ 0xf8,SP2518_P0_0xf8,
+ 0xec,SP2518_P0_0xf8-0x04,
+ 0xef,SP2518_P0_0xf9+0x04,
+ 0xf9,SP2518_P0_0xf9,
+ 0xfa,SP2518_P0_0xfa,
+ 0xee,SP2518_P0_0xfa-0x04,
+
+ //sharpen
+ 0xfd,0x01,
+ 0xdf,0x0f,
+ 0xe5,0x10,
+ 0xe7,0x10,
+ 0xe8,0x20,
+ 0xec,0x20,
+ 0xe9,0x20,
+ 0xed,0x20,
+ 0xea,0x10,
+ 0xef,0x10,
+ 0xeb,0x10,
+ 0xf0,0x10,
+ //;gw
+ 0xfd,0x01,//
+ 0x70,0x76,//
+ 0x7b,0x40,//
+ 0x81,0x30,//
+ //;Y_offset
+ 0xfd,0x00,
+ 0xb2,0X10,
+ 0xb3,0x1f,
+ 0xb4,0x30,
+ 0xb5,0x50,
+ //;CNR
+ 0xfd,0x00,
+ 0x5b,0x20,
+ 0x61,0x80,
+ 0x77,0x80,
+ 0xca,0x80,
+ //;YNR
+ 0xab,0x00,
+ 0xac,0x02,
+ 0xae,0x08,
+ 0xaf,0x20,
+ 0xfd,0x00,
+ 0x32,0x0d,
+ 0x33,0xcf,//ef
+ 0x34,0x7f,//3f
+ 0x35,0x40,//3
+ 0xe7,0x03,
+ 0xe7,0x00,
+
+ //SP2518_config_window(WINDOW_SIZE_VGA)
+#if 0 //
+ 0xfd,0x00,
+ 0x4b,0x00,
+ 0x4c,0x00,
+ 0x47,0x00,
+ 0x48,0x00,
+ 0x4d,0x06,
+ 0x4e,0x40,
+ 0x49,0x04,
+ 0x4a,0xb0,
+
+ 0xfd,0x01,
+ 0x06,0x00,
+ 0x07,0x50,
+ 0x08,0x00,
+ 0x09,0x50,
+ 0x0a,0x01, //480
+ 0x0b,0xe0,
+ 0x0c,0x02, //640
+ 0x0d,0x80,
+ 0x0e,0x01,
+ 0xfd,0x00,
+#else //uxga
+ 0xfd,0x00,
+ 0x47,0x00,
+ 0x48,0x00,
+ 0x49,0x04,
+ 0x4a,0xb0,
+
+ 0x4b,0x00,
+ 0x4c,0x00,
+ 0x4d,0x06,
+ 0x4e,0x40,
+
+ 0xfd,0x01,
+ 0x0e,0x00,
+ 0xfd,0x00,
+#endif
+ 0x36, 0x80,
+ 0x5d,0x0e,//vsync delay
+};
+
+// Exit
+uint8_t sp2518_default_regs_exit[] = {
+};
+
+#endif /* #ifndef __SP2518_H_ */
diff --git a/drivers/media/video/wmt_v4l2/sensors/template/template.h b/drivers/media/video/wmt_v4l2/sensors/template/template.h
new file mode 100755
index 00000000..a812b4c8
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/template/template.h
@@ -0,0 +1,219 @@
+
+#ifndef __TEMPLATE_H_
+#define __TEMPLATE_H_
+
+// Scene Mode
+uint8_t template_scene_mode_auto[] = {
+};
+
+uint8_t template_scene_mode_night[] = {
+};
+
+
+// White Balance
+uint8_t template_wb_auto [] = {
+};
+
+uint8_t template_wb_incandescent [] = {
+};
+
+uint8_t template_wb_fluorescent [] = {
+};
+
+uint8_t template_wb_daylight [] = {
+};
+
+uint8_t template_wb_cloudy [] = {
+};
+
+uint8_t template_wb_tungsten [] = {
+};
+
+
+// Exposure
+uint8_t template_exposure_neg6[] = {
+};
+
+uint8_t template_exposure_neg3[] = {
+};
+
+uint8_t template_exposure_zero[] = {
+};
+
+uint8_t template_exposure_pos3[] = {
+};
+
+uint8_t template_exposure_pos6[] = {
+};
+
+
+// Color Effect
+uint8_t template_colorfx_none[] = {
+};
+
+uint8_t template_colorfx_bw[] = {
+};
+
+uint8_t template_colorfx_sepia[] = {
+};
+
+uint8_t template_colorfx_negative[] = {
+};
+
+uint8_t template_colorfx_emboss[] = {
+};
+
+uint8_t template_colorfx_sketch[] = {
+};
+
+uint8_t template_colorfx_sky_blue[] = {
+};
+
+uint8_t template_colorfx_grass_green[] = {
+};
+
+uint8_t template_colorfx_skin_whiten[] = {
+};
+
+uint8_t template_colorfx_vivid[] = {
+};
+
+// Brightness
+uint8_t template_brightness_neg4[] = {
+};
+
+uint8_t template_brightness_neg3[] = {
+};
+
+uint8_t template_brightness_neg2[] = {
+};
+
+uint8_t template_brightness_neg1[] = {
+};
+
+uint8_t template_brightness_zero[] = {
+};
+
+uint8_t template_brightness_pos1[] = {
+};
+
+uint8_t template_brightness_pos2[] = {
+};
+
+uint8_t template_brightness_pos3[] = {
+};
+
+uint8_t template_brightness_pos4[] = {
+};
+
+// Contrast
+uint8_t template_contrast_neg4[] = {
+};
+
+uint8_t template_contrast_neg3[] = {
+};
+
+uint8_t template_contrast_neg2[] = {
+};
+
+uint8_t template_contrast_neg1[] = {
+};
+
+uint8_t template_contrast_zero[] = {
+};
+
+uint8_t template_contrast_pos1[] = {
+};
+
+uint8_t template_contrast_pos2[] = {
+};
+
+uint8_t template_contrast_pos3[] = {
+};
+
+uint8_t template_contrast_pos4[] = {
+};
+
+// Saturation
+uint8_t template_saturation_neg4[] = {
+};
+
+uint8_t template_saturation_neg3[] = {
+};
+
+uint8_t template_saturation_neg2[] = {
+};
+
+uint8_t template_saturation_neg1[] = {
+};
+
+uint8_t template_saturation_zero[] = {
+};
+
+uint8_t template_saturation_pos1[] = {
+};
+
+uint8_t template_saturation_pos2[] = {
+};
+
+uint8_t template_saturation_pos3[] = {
+};
+
+uint8_t template_saturation_pos4[] = {
+};
+
+
+// Resolution
+
+// QCIF
+uint8_t template_176_144_regs[]={
+};
+
+// QVGA
+uint8_t template_320_240_regs[]={
+};
+
+// CIF
+uint8_t template_352_288_regs[]={
+};
+
+// VGA
+uint8_t template_640_480_regs[]={
+};
+
+// SVGA
+uint8_t template_800_600_regs[]={
+};
+
+// XGA
+uint8_t template_1024_768_regs[]={
+};
+
+// 720p
+uint8_t template_1280_720_regs[]={
+};
+
+// UXGA
+uint8_t template_1600_1200_regs[]={
+};
+
+// 1080p
+uint8_t template_1920_1080_regs[]={
+};
+
+// QXGA
+uint8_t template_2048_1536_regs[]={
+};
+
+uint8_t template_2592_1944_regs[]={
+};
+
+// Initiliztion
+uint8_t template_default_regs_init[] = {
+};
+
+// Exit
+uint8_t template_default_regs_exit[] = {
+};
+
+#endif /* #ifndef __TEMPLATE_H_ */
diff --git a/drivers/media/video/wmt_v4l2/sensors/wmt-cmos.c b/drivers/media/video/wmt_v4l2/sensors/wmt-cmos.c
new file mode 100755
index 00000000..a6ed5e50
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/wmt-cmos.c
@@ -0,0 +1,1264 @@
+/*++
+ * linux/drivers/media/video/wmt_v4l2/cmos/wmt-cmos.c
+ * WonderMedia v4l cmos device driver
+ *
+ * Copyright c 2010 WonderMedia Technologies, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+ --*/
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/major.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/i2c.h>
+
+#include <linux/wmt-mb.h>
+#include <mach/wmt_env.h>
+
+
+#include "wmt-cmos.h"
+#include "cmos-subdev.h"
+#include "../wmt-vid.h"
+
+#include "flash/flash.h"
+
+#define THE_MB_USER "CMOS-MB"
+
+#undef pr_err
+#define pr_err(fmt, args...) printk("[" THE_MB_USER "] " "%s, %d:" fmt, __func__, __LINE__, ##args)
+
+//#define CMOS_REG_TRACE
+#ifdef CMOS_REG_TRACE
+#define CMOS_REG_SET32(addr, val) \
+ PRINT("REG_SET:0x%x -> 0x%0x\n", addr, val);\
+ REG32_VAL(addr) = (val)
+#else
+#define CMOS_REG_SET32(addr, val) REG32_VAL(addr) = (val)
+#endif
+
+//#define CMOS_DEBUG /* Flag to enable debug message */
+//#define CMOS_TRACE
+
+#ifdef CMOS_DEBUG
+#define DBG_MSG(fmt, args...) printk("{%s} " fmt, __FUNCTION__ , ## args)
+#else
+#define DBG_MSG(fmt, args...)
+#endif
+
+#ifdef CMOS_TRACE
+#define TRACE(fmt, args...) printk("{%s}: " fmt, __FUNCTION__ , ## args)
+#else
+#define TRACE(fmt, args...)
+#endif
+
+#define MAX_FB_IN_QUEUE 10
+#define NUM_INPUTS 3
+
+#define CAMERA_TYPE_NA 0 /* not avaliable */
+#define CAMERA_TYPE_CMOS 1 /* CMOS */
+#define CAMERA_TYPE_USB 2 /* USB */
+
+struct regulator *cam_vccvid = NULL;
+struct regulator *cam_dvdd = NULL;
+
+typedef enum {
+ STS_CMOS_READY = 0,
+ STS_CMOS_WAIT = 0x0001,
+ STS_CMOS_RUNNING = 0x0002,
+ STS_CMOS_FB_DONE = 0x0004
+} cmos_status;
+
+typedef struct {
+ VID_FB_M;
+ struct list_head list;
+} cmos_fb_t;
+
+struct cmos_drvinfo {
+ struct list_head head;
+
+ unsigned int frame_size;
+ unsigned int width;
+ unsigned int height;
+
+ unsigned int dft_y_addr;
+ unsigned int dft_c_addr;
+
+ cmos_fb_t fb_pool[MAX_FB_IN_QUEUE];
+ unsigned int fb_cnt;
+
+ unsigned int streamoff;
+ unsigned int dqbuf;
+
+ cmos_status status;
+};
+
+// sync with UbootParam.h in Hal.
+enum {
+ CAMERA_CAP_FLASH = 1 << 0,
+ CAMERA_CAP_FLASH_AUTO = 1 << 1,
+ CAMERA_CAP_HDR = 1 << 2,
+};
+
+struct cmos_dev {
+ int type;
+ int pwdn_gpio;
+ int pwdn_pol;
+ int mirror;
+ u32 cap;
+
+ int id;
+ struct cmos_subdev *sd;
+};
+
+struct cmos_info {
+ int input;
+ int input_count;
+
+ struct cmos_drvinfo drvinfo;
+ struct cmos_dev cd[NUM_INPUTS];
+ struct flash_dev *fl;
+
+ struct mutex lock;
+};
+
+static inline struct cmos_subdev *current_subdev(struct cmos_info *info)
+{
+ if (!info || info->input >= NUM_INPUTS)
+ return NULL;
+
+ return info->cd[info->input].sd;
+}
+
+static DECLARE_WAIT_QUEUE_HEAD(cmos_wait);
+static spinlock_t cmos_lock;
+static int cmos_dev_ref;
+
+static int cmos_dev_setup(struct cmos_info *info)
+{
+ struct cmos_dev *cd;
+ int rc = 0;
+ int i;
+
+ for (i = 0; i < info->input_count; i++) {
+ cd = &info->cd[i];
+ if (cd->type != CAMERA_TYPE_CMOS)
+ continue;
+ rc = gpio_request(cd->pwdn_gpio, "cmos pwdn");
+ if (rc) {
+ pr_err("cmos gpio%d request failed\n", cd->pwdn_gpio);
+ goto err_gpio_req;
+ }
+ gpio_direction_output(cd->pwdn_gpio, !cd->pwdn_pol);
+ }
+ return 0;
+
+err_gpio_req:
+ while (i--)
+ gpio_free(info->cd[i].pwdn_gpio);
+ return rc;
+}
+
+static void cmos_dev_power_up(struct cmos_dev *cd)
+{
+ if (cd->pwdn_gpio > 0) {
+ printk(KERN_DEBUG "%s, gpio-%d\n", __func__, cd->pwdn_gpio);
+ gpio_direction_output(cd->pwdn_gpio, cd->pwdn_pol);
+ msleep(20);
+ }
+}
+
+static void cmos_dev_power_down(struct cmos_dev *cd)
+{
+ if (cd->pwdn_gpio > 0) {
+ printk(KERN_DEBUG "%s, gpio-%d\n", __func__, cd->pwdn_gpio);
+ gpio_direction_output(cd->pwdn_gpio, !cd->pwdn_pol);
+ }
+}
+
+static void cmos_dev_release(struct cmos_info *info)
+{
+ struct cmos_dev *cd;
+ int i;
+
+ for (i = 0; i < info->input_count; i++) {
+ cd = &info->cd[i];
+ if (cd->type != CAMERA_TYPE_CMOS)
+ continue;
+ cmos_dev_power_down(cd);
+ gpio_free(cd->pwdn_gpio);
+ }
+}
+
+static int parse_camera_param(struct cmos_info *info)
+{
+ static char env[] = "wmt.camera.param";
+ struct cmos_dev cd[2];
+ char buf[64];
+ size_t l = sizeof(buf);
+ int i, n;
+ int rc;
+
+ rc = wmt_getsyspara(env, buf, &l);
+ if (rc) {
+ pr_err("Invalid param, please set %s\n", env);
+ return -EINVAL;
+ }
+
+ memset(cd, 0, sizeof(cd));
+ sscanf(buf, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%x",
+ &cd[1].type, &cd[1].pwdn_gpio, &cd[1].pwdn_pol, &cd[1].mirror, &cd[1].cap,
+ &cd[0].type, &cd[0].pwdn_gpio, &cd[0].pwdn_pol, &cd[0].mirror, &cd[0].cap);
+
+ for (n = 0, i = 0; i < ARRAY_SIZE(cd); i++) {
+ struct cmos_dev *c = &cd[i];
+
+ if (c->type != CAMERA_TYPE_CMOS && c->type != CAMERA_TYPE_USB)
+ continue;
+
+ info->cd[n] = cd[i];
+ info->cd[n].id = n;
+
+ pr_info("#%d: type %d, pwdn_gpio %d, pwdn_pol %d, mirror %d, cap 0x%x\n",
+ n, c->type, c->pwdn_gpio, c->pwdn_pol, c->mirror, c->cap);
+ n++;
+ }
+ info->input_count = n;
+
+ return (n == 0) ? -EINVAL : 0;
+}
+
+static int cam_enable(struct cmos_drvinfo *drv, bool en)
+{
+ drv->streamoff = en ? 0 : 1;
+
+ CMOS_REG_SET32(REG_VID_CMOS_EN, en); /* enable CMOS */
+
+ return 0;
+}
+
+static int print_queue(struct cmos_drvinfo *drv)
+{
+#ifdef CMOS_DEBUG
+ struct list_head *next;
+ cmos_fb_t *fb;
+
+ TRACE("Enter\n");
+
+ list_for_each(next, &drv->head) {
+ fb = (cmos_fb_t *) list_entry(next, cmos_fb_t, list);
+ DBG_MSG("[%d] y_addr: 0x%x, c_addr: 0x%x\n",
+ fb->id, fb->y_addr, fb->c_addr );
+ }
+ TRACE("Leave\n");
+#endif
+ return 0;
+}
+
+static int put_queue(struct cmos_drvinfo *drv, cmos_fb_t *fb_in)
+{
+ TRACE("Enter\n");
+
+ list_add_tail(&fb_in->list, &drv->head);
+ wmb();
+ print_queue(drv);
+
+ TRACE("Leave\n");
+
+ return 0;
+}
+
+static int pop_queue(struct cmos_drvinfo *drv, cmos_fb_t *fb_in)
+{
+ TRACE("Enter\n");
+
+ list_del(&fb_in->list);
+ wmb();
+ print_queue(drv);
+
+ TRACE("Leave\n");
+
+ return 0;
+}
+
+static int cmos_cam_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
+{
+ TRACE("Enter\n");
+
+ strlcpy(cap->driver, "wm88xx", sizeof(cap->driver));
+
+ cap->version = KERNEL_VERSION(0, 0, 1);
+
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+
+ TRACE("Leave\n");
+ return 0;
+}
+
+static int cmos_cam_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap)
+{
+ pr_err("Not support now!\n");
+ return -EINVAL;
+}
+
+struct wmt_fmt {
+ char *name;
+ u32 fourcc; /* v4l2 format id */
+ int depth;
+};
+
+static struct wmt_fmt formats[] = {
+ {
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ },
+};
+
+static int cmos_cam_enum_fmt_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+ struct wmt_fmt *fmt;
+
+ if (f->index >= ARRAY_SIZE(formats))
+ return -EINVAL;
+
+ fmt = &formats[f->index];
+
+ strlcpy(f->description, fmt->name, sizeof(f->description));
+ f->pixelformat = fmt->fourcc;
+ return 0;
+}
+
+static int cmos_cam_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct cmos_info *info = file->private_data;
+ struct cmos_subdev *sd = current_subdev(info);
+ int ret;
+
+ mutex_lock(&info->lock);
+ ret = cmos_subdev_call(sd, enum_framesizes, fsize);
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static int cmos_cam_g_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct cmos_info *info = file->private_data;
+ struct cmos_drvinfo *drv = &info->drvinfo;
+
+ f->fmt.pix.width = drv->width;
+ f->fmt.pix.height = drv->height;
+ return 0;
+}
+
+#define pixfmtstr(x) (x) & 0xff, ((x) >> 8) & 0xff, ((x) >> 16) & 0xff, \
+ ((x) >> 24) & 0xff
+
+static int cmos_cam_s_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct cmos_info *info = file->private_data;
+ struct cmos_drvinfo *drv = &info->drvinfo;
+ struct cmos_subdev *sd = current_subdev(info);
+ struct v4l2_mbus_framefmt mf;
+ int rc;
+
+ TRACE("Enter\n");
+ mutex_lock(&info->lock);
+
+ printk(KERN_DEBUG "%s: S_FMT %dx%d, fmt %c%c%c%c\n",
+ sd->name, f->fmt.pix.width, f->fmt.pix.height,
+ pixfmtstr(f->fmt.pix.pixelformat));
+
+ mf.width = f->fmt.pix.width;
+ mf.height = f->fmt.pix.height;
+ mf.field = f->fmt.pix.field;
+ mf.colorspace = f->fmt.pix.colorspace;
+
+ rc = cmos_subdev_call(sd, s_mbus_fmt, &mf);
+ if (rc) {
+ pr_err("%s, %s: s_mbus_fmt fail\n", __func__, sd->name);
+ goto out;
+ }
+
+ /* host set fmt */
+ rc = wmt_vid_set_mode(mf.width, mf.height, f->fmt.pix.pixelformat);
+ if (rc) {
+ goto out;
+ }
+
+ drv->width = mf.width;
+ drv->height = mf.height;
+
+ switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_RGB32: // *4 for ARGB display frambuffer
+ drv->frame_size = drv->width * drv->height * 4;
+ break;
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV12:
+ drv->frame_size = (drv->width * drv->height * 3) / 2;
+ break;
+ case V4L2_PIX_FMT_YUYV: // YC422
+ drv->frame_size = drv->width * drv->height * 2;
+ break;
+ default:
+ drv->frame_size = 0;
+ rc = -EINVAL;
+ break;
+ }
+
+out:
+ mutex_unlock(&info->lock);
+ TRACE("Leave\n");
+ return rc;
+}
+
+static int cmos_cam_try_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct cmos_info *info = file->private_data;
+ struct cmos_subdev *sd = current_subdev(info);
+ struct v4l2_mbus_framefmt mf;
+ int ret;
+
+ TRACE("Enter\n");
+ mutex_lock(&info->lock);
+
+ mf.width = f->fmt.pix.width;
+ mf.height = f->fmt.pix.height;
+ mf.field = f->fmt.pix.field;
+ mf.colorspace = f->fmt.pix.colorspace;
+
+ ret = cmos_subdev_call(sd, try_mbus_fmt, &mf);
+ if (!ret) {
+ f->fmt.pix.width = mf.width;
+ f->fmt.pix.height = mf.height;
+ }
+
+ mutex_unlock(&info->lock);
+ TRACE("Leave\n");
+ return ret;
+}
+
+static int cmos_cam_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb)
+{
+ struct cmos_info *info = file->private_data;
+ struct cmos_drvinfo *drv = &info->drvinfo;
+ cmos_fb_t *fb;
+ int i, j;
+ int ret = 0;
+
+ TRACE("Enter\n");
+ mutex_lock(&info->lock);
+
+ if ((rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+ (rb->memory != V4L2_MEMORY_MMAP) ||
+ (rb->count > MAX_FB_IN_QUEUE) ) {
+ pr_err("rb->type: 0x%x, rb->memory: 0x%x, rb->count: %d\n",
+ rb->type, rb->memory, rb->count);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!drv->streamoff) {
+ pr_err("reqbufs: streaming active\n");
+ ret = -EBUSY;
+ goto out;
+ }
+
+ if (rb->count == 0) {
+ /* Free memory */
+ for (i = 0; i < drv->fb_cnt; i++) {
+ fb = &drv->fb_pool[i];
+ if (fb->y_addr) {
+ mb_free(fb->y_addr);
+ memset(fb, 0, sizeof(cmos_fb_t));
+ }
+ }
+ drv->fb_cnt = 0;
+ INIT_LIST_HEAD(&drv->head);
+ goto out;
+ }
+
+ drv->fb_cnt = rb->count;
+
+ DBG_MSG(" Frame Size: %d Bytes\n", drv->frame_size);
+ DBG_MSG(" fb_cnt: %d\n", drv->fb_cnt);
+
+ /* Allocate memory */
+ for (i = 0; i < drv->fb_cnt; i++) {
+ fb = &drv->fb_pool[i];
+ fb->y_addr = mb_alloc(drv->frame_size);
+ DBG_MSG("%d CMOS MB_allocate size %d\n", i,drv->frame_size);
+ if (fb->y_addr == 0) {
+ /* Allocate MB memory size fail */
+ pr_err("[%d] >> Allocate MB memory (%d) fail!\n", i, drv->frame_size);
+ for (j=0; j<i; j++) {
+ fb = &drv->fb_pool[j];
+ mb_free(fb->y_addr);
+ }
+ ret = -EINVAL;
+ goto out;
+ }
+ fb->c_addr = fb->y_addr + drv->width * drv->height;
+ fb->id = i;
+ fb->is_busy = 0;
+ fb->done = 0;
+ DBG_MSG("[%d] fb: 0x%x, y_addr: 0x%x, c_addr: 0x%x\n",
+ i, (unsigned int)fb, fb->y_addr, fb->c_addr);
+ }
+
+out:
+ mutex_unlock(&info->lock);
+ TRACE("Leave\n");
+ return ret;
+}
+
+static int cmos_cam_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct cmos_info *info = file->private_data;
+ struct cmos_drvinfo *drv = &info->drvinfo;
+
+ TRACE("Enter (index: %d)\n", b->index);
+ mutex_lock(&info->lock);
+
+ if (b->index < MAX_FB_IN_QUEUE) {
+ cmos_fb_t *fb = &drv->fb_pool[b->index];
+ b->length = drv->frame_size;
+ b->m.offset = fb->y_addr;
+ } else {
+ b->length = 0;
+ b->m.offset = 0;
+ }
+ DBG_MSG(" b->length: %d\n", b->length);
+ DBG_MSG(" b->m.offset: 0x%x\n", b->m.offset);
+
+ mutex_unlock(&info->lock);
+ TRACE("Leave (index: %d)\n", b->index);
+ return 0;
+}
+
+static int cmos_cam_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct cmos_info *info = file->private_data;
+ struct cmos_drvinfo *drv = &info->drvinfo;
+ cmos_fb_t *fb;
+
+ TRACE("Enter (index: %d)\n", b->index);
+ mutex_lock(&info->lock);
+
+ fb = &drv->fb_pool[b->index];
+ put_queue(drv, fb);
+
+ mutex_unlock(&info->lock);
+ TRACE("Leave (index: %d)\n", b->index);
+ return 0;
+}
+
+static int cmos_cam_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct cmos_info *info = file->private_data;
+ struct cmos_drvinfo *drv = &info->drvinfo;
+ unsigned long flags =0;
+ cmos_fb_t *fb = 0;
+ int ret = 0;
+
+ TRACE("Enter (index: %d)\n", b->index);
+ mutex_lock(&info->lock);
+
+ if (drv->streamoff) {
+#if 0
+ struct list_head *next;
+
+ /* CMOS sensor did not work now */
+ list_for_each(next, &drv->head) {
+ fb = (cmos_fb_t *)list_entry(next, cmos_fb_t, list);
+ if (fb) {
+ pop_queue(drv, fb);
+ break;
+ }
+ }
+#else
+ ret = -EINVAL;
+ pr_err("stream is off\n");
+#endif
+ goto EXIT_cmos_cam_dqbuf;
+ }
+
+ fb = (cmos_fb_t *)wmt_vid_get_cur_fb();
+ if (!fb) {
+ goto EXIT_cmos_cam_dqbuf;
+ }
+
+ spin_lock_irqsave(&cmos_lock, flags);
+
+ DBG_MSG("Set DQBUF on\n");
+ drv->dqbuf = 1;
+
+ spin_unlock_irqrestore(&cmos_lock, flags);
+
+ if ((wait_event_interruptible_timeout(cmos_wait,
+ (drv->status & STS_CMOS_FB_DONE),
+ 400) == 0)) {
+ pr_err("CMOS Time out in 400 ms\n");
+ ret = -ETIMEDOUT;
+ goto EXIT_cmos_cam_dqbuf;
+ }
+ drv->status &= (~STS_CMOS_FB_DONE);
+
+ DBG_MSG("[%d] fb: 0x%p\n", fb->id, fb);
+ pop_queue(drv, fb);
+
+ b->length = drv->frame_size;
+ b->m.offset = fb->y_addr;
+ b->index = fb->id;
+
+EXIT_cmos_cam_dqbuf:
+ mutex_unlock(&info->lock);
+ TRACE("Leave (index: %d)\n", b->index);
+ return ret;
+}
+
+static int cmos_cam_enum_input(struct file *file, void *fh, struct v4l2_input *inp)
+{
+ if (inp->index >= NUM_INPUTS)
+ return -EINVAL;
+
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ inp->std = V4L2_STD_ALL;
+ sprintf(inp->name, "Camera %u", inp->index);
+ return 0;
+}
+
+static int cmos_cam_g_input(struct file *file, void *fh, unsigned int *i)
+{
+ struct cmos_info *info = file->private_data;
+ TRACE("Enter\n");
+ mutex_lock(&info->lock);
+ *i = info->input;
+ mutex_unlock(&info->lock);
+ TRACE("Leave\n");
+ return 0;
+}
+
+static int cmos_cam_s_input(struct file *file, void *fh, unsigned int i)
+{
+ struct cmos_info *info = file->private_data;
+ struct cmos_dev *cd;
+ struct cmos_subdev *sd;
+ int rc,j;
+
+ TRACE("Enter\n");
+ mutex_lock(&info->lock);
+
+ if (i >= info->input_count) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ {
+ /* Here we use info->cd[2] as nmi cmos device */
+ extern int nmi_running(void);
+ if (nmi_running()) {
+ pr_info("nmi running, use cmos device 2 ...\n");
+ i = 2;
+ }
+ }
+
+ for (j = 0; j < info->input_count; j++) {
+ if(j==i)
+ continue;
+ cd = &info->cd[j];
+ if (cd->type != CAMERA_TYPE_CMOS)
+ continue;
+ cmos_dev_power_down(cd);
+ }
+
+ info->input = i;
+
+ cd = &info->cd[i];
+ sd = cd->sd;
+
+ cmos_dev_power_up(cd);
+
+ if (!sd) {
+ sd = cmos_subdev_get_by_try();
+ if (!sd) {
+ pr_err("cmos detect failed\n");
+ rc = -EINVAL;
+ goto err_detect;
+ }
+ pr_info("cmos #%d: detected %s\n", i, sd->name);
+ cd->sd = sd;
+ }
+
+ rc = cmos_subdev_call(sd, init);
+ if (rc) {
+ goto err_init;
+ }
+
+ pr_info("[cmos] %s init success\n", sd->name);
+ goto out;
+
+err_init:
+err_detect:
+ cmos_dev_power_down(cd);
+out:
+ mutex_unlock(&info->lock);
+ TRACE("Leave\n");
+ return rc;
+}
+
+static int cmos_cam_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *a)
+{
+ struct cmos_info *info = file->private_data;
+ struct cmos_subdev *sd = current_subdev(info);
+ int ret;
+
+ TRACE("Enter\n");
+ mutex_lock(&info->lock);
+
+ if (a->id == V4L2_CID_CAMERA_FLASH_MODE) {
+ struct cmos_dev *cd = &info->cd[info->input];
+ ret = (cd->cap & CAMERA_CAP_FLASH) ? v4l2_ctrl_query_fill(a, 0, 4, 1, 0) : -EINVAL;
+ } else
+ ret = cmos_subdev_call(sd, queryctrl, a);
+
+ mutex_unlock(&info->lock);
+ TRACE("Leave\n");
+ return ret;
+}
+
+static int cmos_cam_g_ctrl(struct file *file, void *fh, struct v4l2_control *a)
+{
+ struct cmos_info *info = file->private_data;
+ struct cmos_subdev *sd = current_subdev(info);
+ int ret;
+
+ TRACE("Enter\n");
+ mutex_lock(&info->lock);
+ ret = cmos_subdev_call(sd, g_ctrl, a);
+ mutex_unlock(&info->lock);
+ TRACE("Leave\n");
+ return ret;
+}
+
+static int cmos_cam_s_ctrl(struct file *file, void *fh, struct v4l2_control *a)
+{
+ struct cmos_info *info = file->private_data;
+ struct cmos_subdev *sd = current_subdev(info);
+ char *s;
+ int ret;
+
+ TRACE("Enter\n");
+ mutex_lock(&info->lock);
+
+ /* for debug */
+ switch (a->id) {
+ case V4L2_CID_CAMERA_SCENE_MODE: s = "Scene Mode"; break;
+ case V4L2_CID_DO_WHITE_BALANCE: s = "White Balance"; break;
+ case V4L2_CID_EXPOSURE: s = "Exposure"; break;
+ case V4L2_CID_HFLIP: s = "H-flip"; break;
+ case V4L2_CID_VFLIP: s = "V-flip"; break;
+ case V4L2_CID_CAMERA_FLASH_MODE: s = "Flash Mode"; break;
+ case V4L2_CID_CAMERA_FOCUS_MODE: s = "Focus Mode"; break;
+ case V4L2_CID_CAMERA_FOCUS_POSITION_X: s = "Focus Area X"; break;
+ case V4L2_CID_CAMERA_FOCUS_POSITION_Y: s = "Focus Area Y"; break;
+ default: s = "Unknown"; break;
+ }
+ printk(KERN_DEBUG "%s: S_CTRL: %s, value %d\n", sd->name, s, a->value);
+
+ if (a->id == V4L2_CID_CAMERA_FLASH_MODE) {
+ struct cmos_dev *cd = &info->cd[info->input];
+ if (!(cd->cap & CAMERA_CAP_FLASH))
+ ret = -EINVAL;
+ else
+ ret = flash_set_mode(info->fl, a->value);
+ } else
+ ret = cmos_subdev_call(sd, s_ctrl, a);
+
+ mutex_unlock(&info->lock);
+ TRACE("Leave\n");
+ return ret;
+}
+
+static int cmos_cam_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *parm)
+{
+ struct cmos_info *info = file->private_data;
+ struct cmos_subdev *sd = current_subdev(info);
+ int ret;
+
+ TRACE("Enter\n");
+ mutex_lock(&info->lock);
+ ret= cmos_subdev_call(sd,g_parm, parm);
+ mutex_unlock(&info->lock);
+ TRACE("Leave\n");
+
+ return ret;
+}
+
+static int cmos_cam_s_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *parm)
+{
+ struct cmos_info *info = file->private_data;
+ struct cmos_subdev *sd = current_subdev(info);
+ int ret;
+
+ TRACE("Enter\n");
+ mutex_lock(&info->lock);
+ ret= cmos_subdev_call(sd,s_parm, parm);
+ mutex_unlock(&info->lock);
+ TRACE("Leave\n");
+
+ return ret;
+}
+
+static int cmos_cam_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+ struct cmos_info *info = file->private_data;
+ struct cmos_drvinfo *drv = &info->drvinfo;
+ cmos_fb_t *fb;
+ int ret = 0;
+
+ TRACE("Enter\n");
+ mutex_lock(&info->lock);
+
+ if (!list_empty(&drv->head)) {
+ fb = list_first_entry(&drv->head, cmos_fb_t, list);
+ wmt_vid_set_cur_fb((vid_fb_t *)fb);
+ cam_enable(drv, true);
+ } else
+ ret = -EINVAL;
+
+ mutex_unlock(&info->lock);
+ TRACE("Leave\n");
+ return 0;
+}
+
+static int cmos_cam_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+ struct cmos_info *info = file->private_data;
+ struct cmos_drvinfo *drv = &info->drvinfo;
+
+ TRACE("Enter\n");
+ mutex_lock(&info->lock);
+
+ cam_enable(drv, false);
+
+ mutex_unlock(&info->lock);
+ TRACE("Leave\n");
+ return 0;
+}
+
+static int cmos_cam_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ int ret;
+
+ TRACE("Enter\n");
+
+ vma->vm_flags |= VM_IO | VM_RESERVED;
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+ ret = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+ vma->vm_end - vma->vm_start, vma->vm_page_prot);
+
+ TRACE("Leave\n");
+ return ret;
+}
+
+static irqreturn_t cmos_isr(int irq, void *data)
+{
+ struct cmos_info *info = data;
+ struct cmos_drvinfo *drv;
+ struct list_head *next;
+ cmos_fb_t *cur_fb, *fb;
+ unsigned long flags = 0;
+ int mb_count = 0;
+
+ //TRACE("Enter\n");
+// printk("i");
+
+ if (!info) {
+ return IRQ_NONE;
+ }
+
+ drv = &info->drvinfo;
+
+ if (drv->dqbuf == 1) {
+ spin_lock_irqsave(&cmos_lock, flags);
+ drv->dqbuf = 0;
+// DBG_MSG("Set DBBUF off\n");
+ spin_unlock_irqrestore(&cmos_lock, flags);
+
+ cur_fb = (cmos_fb_t *)wmt_vid_get_cur_fb();
+ list_for_each(next, &drv->head) {
+ fb = (cmos_fb_t *) list_entry(next, cmos_fb_t, list);
+ if (fb == cur_fb) {
+ /*--------------------------------------------------------------
+ Get next FB
+ --------------------------------------------------------------*/
+ fb = (cmos_fb_t *) list_entry(cur_fb->list.next, cmos_fb_t, list);
+
+ do {
+ if (((vid_fb_t *)fb)->y_addr != 0)
+ mb_count = mb_counter(((vid_fb_t *)fb)->y_addr);
+
+ if (mb_count != 1) {
+ fb = (cmos_fb_t *) list_entry(fb->list.next, cmos_fb_t, list);
+// DBG_MSG("mbcount skip 0x%08x mb_count %d \n",
+// ((vid_fb_t *)fb)->y_addr,mb_count);
+ continue;
+ }
+ } while ((mb_count != 1) && (fb != cur_fb));
+
+ cur_fb->is_busy = 0;
+ cur_fb->done = 1;
+
+// DBG_MSG("[%d] done: %d, is_busy: %d\n",
+// cur_fb->id, cur_fb->done, cur_fb->is_busy);
+// DBG_MSG("[%d] New FB done: %d, is_busy: %d\n",
+// fb->id, fb->done, fb->is_busy);
+
+ wmt_vid_set_cur_fb((vid_fb_t *)fb);
+ drv->status |= STS_CMOS_FB_DONE;
+ wake_up_interruptible(&cmos_wait);
+ break;
+ }
+ }
+ } /* if( drv->dqbuf == 1 ) */
+
+ CMOS_REG_SET32(REG_VID_INT_CTRL, REG32_VAL(REG_VID_INT_CTRL));
+
+ //TRACE("Leave\n");
+// printk("o");
+
+ return IRQ_HANDLED;
+}
+
+static int cmos_cam_open(struct file *filp)
+{
+ struct cmos_info *info = video_drvdata(filp);
+ struct cmos_drvinfo *drv;
+ int ret = 0;
+
+ printk(KERN_DEBUG "%s()\n", __func__);
+ //if(cam_vccvid)
+ //regulator_enable(cam_vccvid);
+
+ //if(cam_dvdd)
+ //regulator_enable(cam_dvdd);
+
+ TRACE("Enter\n");
+ mutex_lock(&info->lock);
+ if (cmos_dev_ref) {
+ ret = -EBUSY;
+ goto out;
+ }
+ cmos_dev_ref++;
+
+ ret = request_irq(WMT_VID_IRQ, (void *)&cmos_isr, IRQF_SHARED,
+ "wmt-cmos", (void *)info);
+ if (ret) {
+ pr_err("CMOS: Failed to register CMOS irq %i\n", WMT_VID_IRQ);
+ cmos_dev_ref--;
+ goto out;
+ }
+
+ drv = &info->drvinfo;
+ drv->dqbuf = 0;
+ drv->status = STS_CMOS_READY;
+ INIT_LIST_HEAD(&drv->head);
+
+ spin_lock_init(&cmos_lock);
+
+ filp->private_data = info;
+
+ wmt_vid_open(VID_MODE_CMOS, NULL);
+ cam_enable(drv, false);
+
+out:
+ mutex_unlock(&info->lock);
+ TRACE("Leave\n");
+ return ret;
+}
+
+static int cmos_cam_release(struct file *filp)
+{
+ struct cmos_info *info = filp->private_data;
+ struct cmos_drvinfo *drv = &info->drvinfo;
+ struct cmos_subdev *sd;
+ int i;
+
+ TRACE("Enter\n");
+ mutex_lock(&info->lock);
+
+ free_irq(WMT_VID_IRQ, (void *)info);
+
+ if (drv) {
+ /* Free memory */
+ for (i = 0; i < drv->fb_cnt; i++) {
+ cmos_fb_t *fb = &drv->fb_pool[i];
+ if (fb->y_addr) {
+ mb_free(fb->y_addr);
+ memset(fb, 0, sizeof(cmos_fb_t));
+ }
+ }
+ }
+
+ sd = current_subdev(info);
+ if (sd) {
+ cmos_subdev_call(sd, exit);
+ }
+
+ cmos_dev_power_down(&info->cd[info->input]);
+ wmt_vid_close(VID_MODE_CMOS);
+ cmos_dev_ref--;
+
+// if(cam_vccvid)
+// regulator_disable(cam_vccvid);
+
+ //if(cam_dvdd)
+ //regulator_disable(cam_dvdd);
+
+ mutex_unlock(&info->lock);
+ TRACE("Leave\n");
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops cmos_ioctl_ops = {
+ .vidioc_querycap = cmos_cam_querycap,
+ .vidioc_enum_input = cmos_cam_enum_input,
+ .vidioc_g_input = cmos_cam_g_input,
+ .vidioc_s_input = cmos_cam_s_input,
+ .vidioc_reqbufs = cmos_cam_reqbufs,
+ .vidioc_querybuf = cmos_cam_querybuf,
+ .vidioc_qbuf = cmos_cam_qbuf,
+ .vidioc_dqbuf = cmos_cam_dqbuf,
+ .vidioc_streamon = cmos_cam_streamon,
+ .vidioc_streamoff = cmos_cam_streamoff,
+ .vidioc_enum_fmt_vid_cap = cmos_cam_enum_fmt_cap,
+ .vidioc_enum_framesizes = cmos_cam_enum_framesizes,
+ .vidioc_g_fmt_vid_cap = cmos_cam_g_fmt_cap,
+ .vidioc_s_fmt_vid_cap = cmos_cam_s_fmt_cap,
+ .vidioc_try_fmt_vid_cap = cmos_cam_try_fmt_cap,
+ .vidioc_g_parm = cmos_cam_g_parm,
+ .vidioc_s_parm = cmos_cam_s_parm,
+ .vidioc_queryctrl = cmos_cam_queryctrl,
+ .vidioc_s_ctrl = cmos_cam_s_ctrl,
+ .vidioc_g_ctrl = cmos_cam_g_ctrl,
+ .vidioc_cropcap = cmos_cam_cropcap,
+};
+
+static const struct v4l2_file_operations cmos_cam_fops = {
+ .owner = THIS_MODULE,
+ .open = cmos_cam_open,
+ .release = cmos_cam_release,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = cmos_cam_mmap,
+};
+
+static struct video_device cmos_cam = {
+ .name = "cmos_cam",
+ .fops = &cmos_cam_fops,
+ .ioctl_ops = &cmos_ioctl_ops,
+ .release = video_device_release_empty,
+ .tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM,
+ .minor = -1
+};
+
+static int cmos_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct cmos_info *info;
+
+ info = kzalloc(sizeof *info, GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ // ldo6 can't disable.
+ cam_vccvid = regulator_get(NULL, "ldo6");
+ if (IS_ERR(cam_vccvid)){
+ cam_vccvid = NULL;
+ pr_err("failed to get camera vccvid\n");
+ }
+
+ cam_dvdd= regulator_get(NULL, "ldo5");
+ if (IS_ERR(cam_dvdd)){
+ cam_dvdd = NULL;
+ pr_err("failed to get camera dvdd\n");
+ }
+
+ if(cam_vccvid){
+ regulator_set_voltage(cam_vccvid, 2800000, 2800000);
+ regulator_enable(cam_vccvid);
+// regulator_disable(cam_vccvid);
+ }
+
+ if(cam_dvdd){
+ regulator_set_voltage(cam_dvdd, 1800000, 1800000);
+ regulator_enable(cam_dvdd);
+ //regulator_disable(cam_dvdd);
+ }
+
+ ret = parse_camera_param(info);
+ if (ret)
+ goto err_param;
+
+ ret = cmos_dev_setup(info);
+ if (ret)
+ goto err_dev_seup;
+
+ ret = video_register_device(&cmos_cam, VFL_TYPE_GRABBER, -1);
+ if (ret) {
+ pr_err("WonderMedia CMOS camera register failed\n");
+ goto err_video_register;
+ }
+
+ mutex_init(&info->lock);
+ video_set_drvdata(&cmos_cam, info);
+
+ info->fl = flash_instantiation();
+
+ pr_info("WonderMedia CMOS camera register OK \n");
+ return 0;
+
+err_video_register:
+err_dev_seup:
+err_param:
+ kfree(info);
+ return ret;
+}
+
+static int cmos_remove(struct platform_device *pdev)
+{
+ struct cmos_info *info = video_get_drvdata(&cmos_cam);
+
+ pr_info("%s()\n", __func__);
+
+ flash_destroy(info->fl);
+ video_unregister_device(&cmos_cam);
+ cmos_dev_release(info);
+ mutex_destroy(&info->lock);
+ kfree(info);
+
+ if(cam_vccvid)
+ regulator_put(cam_vccvid);
+
+ if(cam_dvdd)
+ regulator_put(cam_dvdd);
+ return 0;
+}
+
+static int cmos_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ printk(KERN_DEBUG "%s()\n", __func__);
+ return 0;
+}
+
+static int cmos_resume(struct platform_device *pdev)
+{
+ struct cmos_info *info = video_get_drvdata(&cmos_cam);
+ struct cmos_dev *cd;
+ int i;
+
+ printk(KERN_DEBUG "%s()\n", __func__);
+
+ for (i = 0; i < info->input_count; i++) {
+ cd = &info->cd[i];
+ if (cd->type != CAMERA_TYPE_CMOS)
+ continue;
+ gpio_re_enabled(cd->pwdn_gpio);
+ }
+
+ return 0;
+}
+
+static struct platform_driver cmos_driver = {
+ .driver = {
+ .name = "cmos",
+ },
+ .remove = cmos_remove,
+ .suspend = cmos_suspend,
+ .resume = cmos_resume,
+};
+
+static void cmos_platform_release(struct device *device) { }
+
+static struct platform_device cmos_device = {
+ .name = "cmos",
+ .dev = {
+ .release = cmos_platform_release,
+ },
+};
+
+static int __init cmos_init(void)
+{
+ int ret;
+
+ cmos_subdev_init();
+
+ ret = wmt_vid_i2c_init(0, 0x52);
+ if (ret) {
+ goto err_i2c_init;
+ }
+
+ ret = platform_device_register(&cmos_device);
+ if (ret) {
+ goto err_dev_register;
+ }
+
+ ret = platform_driver_probe(&cmos_driver, cmos_probe);
+ if (ret) {
+ goto err_drv_probe;
+ }
+
+ return 0;
+
+err_drv_probe:
+ platform_device_unregister(&cmos_device);
+err_dev_register:
+ wmt_vid_i2c_release();
+err_i2c_init:
+ cmos_subdev_exit();
+ return ret;
+}
+
+static void __exit cmos_exit(void)
+{
+ pr_info("%s()\n", __func__);
+
+ platform_driver_unregister(&cmos_driver);
+ platform_device_unregister(&cmos_device);
+ wmt_vid_i2c_release();
+ cmos_subdev_exit();
+}
+
+module_init(cmos_init);
+module_exit(cmos_exit);
+
+MODULE_AUTHOR("WonderMedia SW Team Max Chen");
+MODULE_DESCRIPTION("cmos device driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/wmt_v4l2/sensors/wmt-cmos.h b/drivers/media/video/wmt_v4l2/sensors/wmt-cmos.h
new file mode 100755
index 00000000..e69de29b
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/sensors/wmt-cmos.h
diff --git a/drivers/media/video/wmt_v4l2/wmt-vid.c b/drivers/media/video/wmt_v4l2/wmt-vid.c
new file mode 100755
index 00000000..934f0ba9
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/wmt-vid.c
@@ -0,0 +1,820 @@
+/*++
+ * linux/drivers/media/video/wmt_v4l2/wmt-vid.c
+ * WonderMedia v4l video input device driver
+ *
+ * Copyright c 2010 WonderMedia Technologies, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+--*/
+
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/videodev2.h>
+#include "wmt-vid.h"
+
+//#define VID_REG_TRACE
+#ifdef VID_REG_TRACE
+#define VID_REG_SET32(addr, val) \
+ PRINT("REG_SET:0x%x -> 0x%0x\n", addr, val);\
+ REG32_VAL(addr) = (val)
+#else
+#define VID_REG_SET32(addr, val) REG32_VAL(addr) = (val)
+#endif
+
+//#define VID_DEBUG /* Flag to enable debug message */
+//#define VID_DEBUG_DETAIL
+//#define VID_TRACE
+//#define VID_INFO
+
+#ifdef VID_DEBUG
+#define DBG_MSG(fmt, args...) PRINT("{%s} " fmt, __FUNCTION__ , ## args)
+#else
+#define DBG_MSG(fmt, args...)
+#endif
+
+#ifdef VID_DEBUG_DETAIL
+#define DBG_DETAIL(fmt, args...) PRINT("{%s} " fmt, __FUNCTION__ , ## args)
+#else
+#define DBG_DETAIL(fmt, args...)
+#endif
+
+#ifdef VID_TRACE
+ #define TRACE(fmt, args...) PRINT("{%s}: " fmt, __FUNCTION__ , ## args)
+#else
+ #define TRACE(fmt, args...)
+#endif
+
+#ifdef VID_INFO
+ #define DBG_INFO(fmt, args...) PRINT("[INFO] {%s}: " fmt, __FUNCTION__ , ## args)
+#else
+ #define DBG_INFO(fmt, args...)
+#endif
+
+#define DBG_ERR(fmt, args...) PRINT("*E* {%s} " fmt, __FUNCTION__ , ## args)
+
+#define VID_INT_MODE
+
+static vid_fb_t *_cur_fb;
+static vid_fb_t *_prev_fb;
+
+static spinlock_t vid_lock;
+static unsigned int vid_i2c_gpio_en = 0;
+
+static swi2c_reg_t vid_i2c0_scl;
+static swi2c_reg_t vid_i2c0_sda;
+static swi2c_handle_t vid_swi2c;
+
+static struct i2c_adapter *vid_i2c_adapter[2];
+static struct i2c_client *vid_i2c_client[2];
+
+static int vid_dev_tot_num;
+static int vid_dev_num ;
+
+#define WMT_VID_I2C_CHANNEL 2
+
+static void vid_gpio_init(vid_mode mode)
+{
+ auto_pll_divisor(DEV_C24MOUT, CLK_ENABLE, 0, 0);
+ auto_pll_divisor(DEV_VID, CLK_ENABLE, 0, 0);
+
+ GPIO_CTRL_GP8_VDIN_BYTE_VAL = 0x0;
+ PULL_EN_GP8_VDIN_BYTE_VAL = 0x0;
+
+ GPIO_CTRL_GP9_VSYNC_BYTE_VAL &= ~(BIT0|BIT1|BIT2);
+ PULL_EN_GP9_VSYNC_BYTE_VAL &= ~(BIT0|BIT1|BIT2);
+
+ GPIO_CTRL_GP17_I2C_BYTE_VAL &= ~BIT6; //24Mhz on , not set to GPIO
+
+ return;
+}
+
+void vid_set_ntsc_656(void)
+{
+ VID_REG_SET32( REG_BASE_VID+0x08, 0x0 );
+ VID_REG_SET32( REG_BASE_VID+0x30, 0x000006b4); // N_LN_LENGTH : 133m: d1716(6b4h) , 166m: d1715(6b3)
+}
+
+void vid_set_pal_656(void)
+{
+ VID_REG_SET32( REG_BASE_VID+0x08, 0x0 );
+ VID_REG_SET32( REG_BASE_VID+0x34, 0x000006c0 ); // P_LN_LENGTH : 133m: d1728(6c0h) , 166m: d1727(6bf)
+}
+
+void wmt_vid_set_common_mode(vid_tvsys_e tvsys)
+{
+ TRACE("wmt_vid_set_common_mode() \n");
+
+ if (tvsys == VID_NTSC)
+ vid_set_ntsc_656();
+ else
+ vid_set_pal_656();
+
+ VID_REG_SET32( REG_BASE_VID+0x04, 0x100 ); // TMODE[0],REG_UPDATE[8]
+
+ if (tvsys==VID_NTSC) {
+ VID_REG_SET32( REG_BASE_VID+0x14, 0x10a0004 ); // VSBB[25:16]:VSBT[9:0]; 139/1=PAL 10A/4=NTSC
+ VID_REG_SET32( REG_BASE_VID+0x1c, 0x1370018 ); // PAL_TACTEND[25:16], PAL_TACTBEG[9:0]
+ VID_REG_SET32( REG_BASE_VID+0x20, 0x26f0151 ); // PAL_BACTEND[25:16], PAL_BACTBEG[9:0]
+
+ VID_REG_SET32( REG_BASE_VID+0x68, 720 ); // WIDTH[7:0];
+ VID_REG_SET32( REG_BASE_VID+0x6C, 768 ); // LN_WIDTH[7:0]
+ VID_REG_SET32( REG_BASE_VID+0x70, 480 ); // VID_HEIGHT[9:0]
+ } else {
+ VID_REG_SET32( REG_BASE_VID+0x14, 0x1390001 ); // VSBB[25:16]:VSBT[9:0]; 139/1=PAL 10A/4=NTSC
+ VID_REG_SET32( REG_BASE_VID+0x1c, 0x1360017 ); // PAL_TACTEND[25:16], PAL_TACTBEG[9:0]
+ VID_REG_SET32( REG_BASE_VID+0x20, 0x26f0150 ); // PAL_BACTEND[25:16], PAL_BACTBEG[9:0]
+
+ VID_REG_SET32( REG_BASE_VID+0x68, 720 ); // WIDTH[7:0];
+ VID_REG_SET32( REG_BASE_VID+0x6C, 768 ); // LN_WIDTH[7:0]
+ VID_REG_SET32( REG_BASE_VID+0x70, 576 ); // VID_HEIGHT[9:0]
+ }
+
+ VID_REG_SET32( REG_BASE_VID+0x10, 0x20001 ); // HSB[9:0];HSW[9:0];HSP[28]
+ VID_REG_SET32( REG_BASE_VID+0x18, 0x3f0002 ); // HVDLY @[25:16];VSW @[9:0];VSP[28]
+ VID_REG_SET32( REG_BASE_VID+0x24, 0x1060017 ); // NTSC_TACTEND[25:16],NTSC_TACTBEG[9:0]
+ VID_REG_SET32( REG_BASE_VID+0x28, 0x20d011e ); // NTSC_BACTEND[25:16],NTSC_BACTBEG[9:0]
+
+ VID_REG_SET32( REG_BASE_VID+0x78, 0 ); // REG_OUTPUT444_EN,( 0:422 format, 1:444 format )
+ VID_REG_SET32( REG_BASE_VID+0x7C, 0 ); // REG_HSCALE_MODE(0:bypass , 1: horizontal scale 1/2)
+ VID_REG_SET32( REG_BASE_VID+0x74, 0x1 ); // VID Memory write Enable
+ VID_REG_SET32( REG_BASE_VID+0xfc, 0x1 ); // BIT SWAP
+ //VID_REG_SET32( REG_BASE_VID+0x00, 0x1 ); // VID Enable
+}
+
+
+int wmt_vid_i2c_xfer(struct i2c_msg *msg, unsigned int num, int bus_id)
+{
+ int ret = 1;
+ int i = 0;
+
+ if (num > 1) {
+ for (i = 0; i < num - 1; ++i)
+ msg[i].flags |= I2C_M_NOSTART;
+ }
+ ret = i2c_transfer(vid_i2c_adapter[vid_dev_num], msg, num);
+
+ if (ret <= 0) {
+ printk("[%s] fail ret %d \n", __func__, ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+int write_array_2_i2c(int slave_addr, char *array_addr, int array_size)
+{
+ int i=0;
+
+ for (i = 0; i < array_size; i += 2) {
+ wmt_vid_i2c_write(slave_addr, array_addr[i], array_addr[i+1]);
+ }
+
+ return 0;
+}
+
+int wmt_vid_i2c_write_page(int chipId ,unsigned int index,char *pdata,int len)
+{
+ struct i2c_msg msg[1];
+ unsigned char buf[4];
+ int ret;
+
+ buf[0] = index;
+ memcpy(&buf[1],pdata,len);
+ msg[0].addr = chipId;
+ msg[0].flags = 0 ;
+ msg[0].flags &= ~(I2C_M_RD);
+ msg[0].len = len;
+ msg[0].buf = buf;
+
+ ret = wmt_vid_i2c_xfer(msg,1,1);
+
+ return ret;
+}
+
+int wmt_vid_i2c_read_page(int chipId ,unsigned int index,char *pdata,int len)
+{
+ struct i2c_msg msg[2];
+ unsigned char buf[4];
+
+ memset(buf,0x55,len+1);
+ buf[0] = index;
+
+ msg[0].addr = chipId;
+ msg[0].flags = 0 ;
+ msg[0].flags &= ~(I2C_M_RD);
+ msg[0].len = 1;
+ msg[0].buf = buf;
+
+ msg[1].addr = chipId;
+ msg[1].flags = 0 ;
+ msg[1].flags |= (I2C_M_RD);
+ msg[1].len = len;
+ msg[1].buf = buf;
+
+ wmt_vid_i2c_xfer(msg,2,1);
+
+ memcpy(pdata,buf,len);
+
+ return 0;
+}
+
+int wmt_vid_i2c_read(int chipId ,unsigned int index)
+{
+ char retval;
+
+ if (vid_i2c_gpio_en) {
+ wmt_swi2c_read(&vid_swi2c, chipId*2, index, &retval, 1);
+ } else {
+ wmt_vid_i2c_read_page( chipId ,index,&retval,1) ;
+ }
+
+ return retval;
+}
+
+int wmt_vid_i2c_write(int chipId ,unsigned int index,char data)
+{
+ if (vid_i2c_gpio_en) {
+ wmt_swi2c_write(&vid_swi2c, chipId*2, index, &data, 2);
+ } else {
+ wmt_vid_i2c_write_page(chipId ,index, &data, 2);
+ }
+
+ return 0;
+}
+
+int wmt_vid_i2c_write16addr(int chipId ,unsigned int index,unsigned int data)
+{
+ int ret=0;
+ struct i2c_msg msg[1];
+ unsigned char buf[4];
+
+ if (vid_i2c_gpio_en) {
+ DBG_ERR("wmt_vid_i2c_write16addr() Not support GPIO mode now");
+ return -1;
+ }
+
+ buf[0]=((index>>8) & 0xff);
+ buf[1]=(index & 0xff);
+ buf[2]=(data & 0xff);
+
+ msg[0].addr = chipId;
+ msg[0].flags = 0;
+ msg[0].flags &= ~(I2C_M_RD);
+ msg[0].len = 3;
+ msg[0].buf = buf;
+ ret = wmt_vid_i2c_xfer(msg,1,1);
+
+ return ret;
+}
+
+int wmt_vid_i2c_write16data(int chipId ,unsigned int index,unsigned int data)
+{
+ int ret=0;
+ struct i2c_msg msg[1];
+ unsigned char buf[4];
+
+ if (vid_i2c_gpio_en) {
+ DBG_ERR("wmt_vid_i2c_write16data() Not support GPIO mode now");
+ return -1;
+ }
+
+ buf[0]=((index>>8) & 0xff);
+ buf[1]=(index & 0xff);
+ buf[2]=((data>>8) & 0xff);
+ buf[3]=(data & 0xff);
+
+ msg[0].addr = chipId;
+ msg[0].flags = 0;
+ msg[0].flags &= ~(I2C_M_RD);
+ msg[0].len = 4;
+ msg[0].buf = buf;
+ ret = wmt_vid_i2c_xfer(msg,1,1);
+
+ return ret;
+}
+
+int wmt_vid_i2c_read16addr(int chipId ,unsigned int index)
+{
+ int ret=0;
+ struct i2c_msg msg[2];
+ unsigned char buf[2];
+ unsigned int val;
+
+ if (vid_i2c_gpio_en) {
+ DBG_ERR("wmt_vid_i2c_read16addr() Not support GPIO mode now");
+ return -1;
+ }
+
+ buf[0]=((index>>8) & 0xff);
+ buf[1]=(index & 0xff);
+
+ msg[0].addr = chipId;
+ msg[0].flags = 0;
+ msg[0].flags &= ~(I2C_M_RD);
+ msg[0].len = 2;
+ msg[0].buf = buf;
+
+ msg[1].addr = chipId;
+ msg[1].flags = 0;
+ msg[1].flags |= (I2C_M_RD);
+ msg[1].len = 1;
+ msg[1].buf = buf;
+
+ ret = wmt_vid_i2c_xfer(msg,2,1);
+
+ val=buf[0];
+ return val;
+}
+
+int wmt_vid_i2c_read16data(int chipId ,unsigned int index)
+{
+ int ret=0;
+ struct i2c_msg msg[2];
+ unsigned char buf[2];
+ unsigned int val;
+
+ if (vid_i2c_gpio_en) {
+ DBG_ERR("wmt_vid_i2c_read16data() Not support GPIO mode now");
+ return -1;
+ }
+
+ buf[0]=((index>>8) & 0xff);
+ buf[1]=(index & 0xff);
+
+ msg[0].addr = chipId;
+ msg[0].flags = 0;
+ msg[0].flags &= ~(I2C_M_RD);
+ msg[0].len = 2;
+ msg[0].buf = buf;
+
+ msg[1].addr = chipId;
+ msg[1].flags = 0;
+ msg[1].flags |= (I2C_M_RD);
+ msg[1].len = 2;
+ msg[1].buf = buf;
+
+ ret = wmt_vid_i2c_xfer(msg,2,1);
+
+ //printk("wmt_vid_i2c_read16data(%x,%x)\n",buf[0],buf[1]);
+
+ val = ((buf[0]&0xff)<<8) | (buf[1]&0xff);
+
+ return val;
+}
+
+//---dev_0---------------------------------------------
+
+struct wmt_vid_i2c_priv_0 {
+ unsigned int var;
+};
+
+static int __devinit wmt_vid_i2c_probe_0(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wmt_vid_i2c_priv *wmt_vid_i2c;
+ int ret=0;
+
+ printk("wmt_vid_i2c_probe_0() \n");
+ if (!i2c_check_functionality(vid_i2c_client[0]->adapter, I2C_FUNC_I2C)) {
+ printk(KERN_ERR "wmt_vid_i2c_probe: need I2C_FUNC_I2C\n");
+ return -ENODEV;
+ }
+
+ wmt_vid_i2c = kzalloc(sizeof(struct wmt_vid_i2c_priv_0), GFP_KERNEL);
+ if (wmt_vid_i2c == NULL) {
+ printk("wmt_vid_i2c_probe kzalloc fail");
+ return -ENOMEM;
+ }
+ i2c_set_clientdata(i2c, wmt_vid_i2c);
+
+ return ret;
+}
+
+static int wmt_vid_i2c_remove_0(struct i2c_client *client)
+{
+ kfree(i2c_get_clientdata(vid_i2c_client[0]));
+ return 0;
+}
+
+static const struct i2c_device_id wmt_vid_i2c_id_0[] = {
+ { "wmt_vid_i2c_0", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wmt_vid_i2c_id_0);
+
+static struct i2c_driver wmt_vid_i2c_driver_0 = {
+ .probe = wmt_vid_i2c_probe_0,
+ .remove = wmt_vid_i2c_remove_0,
+ .id_table = wmt_vid_i2c_id_0,
+ .driver = {
+ .name = "wmt_vid_i2c_0",
+ },
+};
+
+static struct i2c_board_info wmt_vid_i2c_board_info_0 = {
+ .type = "wmt_vid_i2c_0",
+ .flags = 0x00,
+ .platform_data = NULL,
+ .archdata = NULL,
+ .irq = -1,
+};
+
+//---dev_1---------------------------------------------
+struct wmt_vid_i2c_priv_1 {
+ unsigned int var;
+};
+
+static int __devinit wmt_vid_i2c_probe_1(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wmt_vid_i2c_priv *wmt_vid_i2c;
+ int ret=0;
+
+ printk("wmt_vid_i2c_probe_1() \n");
+
+ if (!i2c_check_functionality(vid_i2c_client[1]->adapter, I2C_FUNC_I2C)) {
+ printk(KERN_ERR "wmt_vid_i2c_probe: need I2C_FUNC_I2C\n");
+ return -ENODEV;
+ }
+
+ wmt_vid_i2c = kzalloc(sizeof(struct wmt_vid_i2c_priv_1), GFP_KERNEL);
+ if (wmt_vid_i2c == NULL) {
+ printk("wmt_vid_i2c_probe kzalloc fail");
+ return -ENOMEM;
+ }
+
+ i2c_set_clientdata(i2c, wmt_vid_i2c);
+
+ return ret;
+}
+
+static int wmt_vid_i2c_remove_1(struct i2c_client *client)
+{
+ kfree(i2c_get_clientdata(vid_i2c_client[1]));
+ return 0;
+}
+
+static const struct i2c_device_id wmt_vid_i2c_id_1[] = {
+ { "wmt_vid_i2c_1", 0},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wmt_vid_i2c_id_1);
+
+static struct i2c_driver wmt_vid_i2c_driver_1 = {
+ .probe = wmt_vid_i2c_probe_1,
+ .remove = wmt_vid_i2c_remove_1,
+ .id_table = wmt_vid_i2c_id_1,
+ .driver = { .name = "wmt_vid_i2c_1", },
+};
+
+static struct i2c_board_info wmt_vid_i2c_board_info_1 = {
+ .type = "wmt_vid_i2c_1",
+ .flags = 0x00,
+ .platform_data = NULL,
+ .archdata = NULL,
+ .irq = -1,
+};
+
+//---------------------------------------------------------
+int wmt_vid_i2c_set_same_dev(void)
+{
+ vid_dev_tot_num = 2;
+ vid_i2c_adapter[1] = vid_i2c_adapter[0];
+ vid_i2c_client[1] = vid_i2c_client[0];
+
+ return 0;
+}
+
+int wmt_vid_i2c_init(int dev_num, short dev_id)
+{
+ struct i2c_board_info *wmt_vid_i2c_bi ;
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+
+ int ret =0;
+
+ printk("wmt_vid_i2c_init() dev_num %d dev_id 0x%02x \n",dev_num,dev_id);
+
+ if (dev_num == 0)
+ wmt_vid_i2c_bi = &wmt_vid_i2c_board_info_0;
+ else
+ wmt_vid_i2c_bi = &wmt_vid_i2c_board_info_1;
+
+ adapter = i2c_get_adapter(WMT_VID_I2C_CHANNEL);
+ if (adapter == NULL) {
+ printk("can not get i2c adapter, client address error");
+ return -ENODEV;
+ }
+
+ wmt_vid_i2c_bi->addr = dev_id;
+
+ client = i2c_new_device(adapter, wmt_vid_i2c_bi);
+ if ( client == NULL) {
+ printk("allocate i2c client failed");
+ return -ENOMEM;
+ }
+
+ vid_dev_tot_num = dev_num+1;
+ vid_i2c_client[dev_num] = client;
+ vid_i2c_adapter[dev_num] = adapter;
+
+ i2c_put_adapter(adapter);
+ if (dev_num == 0)
+ ret = i2c_add_driver(&wmt_vid_i2c_driver_0);
+ else
+ ret = i2c_add_driver(&wmt_vid_i2c_driver_1);
+
+ if (ret) {
+ printk("i2c_add_driver fail");
+ }
+
+ return ret;
+}
+
+int wmt_vid_i2c_release(void)
+{
+ int i;
+
+ for (i = 0; i < vid_dev_tot_num; i++) {
+ i2c_unregister_device(vid_i2c_client[i]);
+ if (i == 0) {
+ wmt_vid_i2c_remove_0(vid_i2c_client[i]);
+ i2c_del_driver(&wmt_vid_i2c_driver_0);
+ } else {
+ wmt_vid_i2c_remove_1(vid_i2c_client[i]);
+ i2c_del_driver(&wmt_vid_i2c_driver_1);
+ }
+ }
+
+ return 0;
+}
+
+int wmt_vid_set_mode(int width, int height, unsigned int pix_format)
+{
+ TRACE("Enter\n");
+
+ VID_REG_SET32(REG_VID_WIDTH, width); /* VID output width */
+ VID_REG_SET32(REG_VID_LINE_WIDTH, width);
+ VID_REG_SET32(REG_VID_HEIGHT, height); /* VID output height */
+
+ if (pix_format == V4L2_PIX_FMT_YUYV) // YUV422
+ {
+ DBG_INFO("V4L2_PIX_FMT_YUYV \n");
+ VID_REG_SET32( REG_BASE_VID+0x90, 0x0); //CMOS Sensor Input mode, set to YUV
+ VID_REG_SET32( REG_BASE_VID+0x98, 0x0); //CMOS RGB Output Mode, only RGB565 need set to '1'
+ VID_REG_SET32( REG_BASE_VID+0x78, 0x0); //REG_OUTPUT444_EN, set to YUV 422
+ }
+ else if (pix_format == V4L2_PIX_FMT_YUV444) {
+ DBG_INFO("V4L2_PIX_FMT_YUYV \n");
+ VID_REG_SET32( REG_BASE_VID+0x90, 0x0); //CMOS Sensor Input mode, set to YUV
+ VID_REG_SET32( REG_BASE_VID+0x98, 0x0); //CMOS RGB Output Mode, only RGB565 need set to '1'
+ VID_REG_SET32( REG_BASE_VID+0x78, 0x1); //REG_OUTPUT444_EN, set to YUV 444
+ }
+ else if (pix_format == V4L2_PIX_FMT_RGB565) {
+ DBG_INFO("V4L2_PIX_FMT_RGB565 \n");
+ VID_REG_SET32( REG_BASE_VID+0x90, 0x1); //CMOS Sensor Input mode, Set to RGB
+ VID_REG_SET32( REG_BASE_VID+0x98, 0x1); //CMOS RGB Output Mode, only RGB565 need set to '1'
+ }
+ else if (pix_format == V4L2_PIX_FMT_RGB32) {
+ DBG_INFO("V4L2_PIX_FMT_RGB888 \n");
+ VID_REG_SET32( REG_BASE_VID+0x90, 0x1); //CMOS Sensor Input mode, Set to RGB
+ VID_REG_SET32( REG_BASE_VID+0x98, 0x0); //CMOS RGB Output Mode , only RGB565 need set to '1'
+ }
+ else if (pix_format == V4L2_PIX_FMT_NV12) {
+ DBG_INFO("V4L2_PIX_FMT_NV12 \n");
+ VID_REG_SET32( REG_BASE_VID+0x90, 0x0); //CMOS Sensor Input mode, set to YUV
+ VID_REG_SET32( REG_BASE_VID+0x98, 0x0); //CMOS RGB Output Mode, only RGB565 need set to '1'
+ VID_REG_SET32( REG_BASE_VID+0x78, 0x10);
+ VID_REG_SET32( REG_BASE_VID+0x74, 0x01);
+ }
+ else if (pix_format == V4L2_PIX_FMT_NV21) {
+ DBG_INFO("V4L2_PIX_FMT_NV21 \n");
+ VID_REG_SET32( REG_BASE_VID+0x90, 0x0); //CMOS Sensor Input mode, set to YUV
+ VID_REG_SET32( REG_BASE_VID+0x98, 0x0); //CMOS RGB Output Mode, only RGB565 need set to '1'
+ VID_REG_SET32( REG_BASE_VID+0x78, 0x10);
+ VID_REG_SET32( REG_BASE_VID+0x74, 0x11);
+ }
+ else {
+ pix_format = V4L2_PIX_FMT_YUYV;
+ DBG_INFO("V4L2_PIX_unknow , set to V4L2_PIX_FMT_YUYV \n");
+ }
+
+ TRACE("Leave\n");
+
+ return 0;
+}
+
+int wmt_vid_set_cur_fb(vid_fb_t *fb)
+{
+ unsigned long flags =0;
+
+ spin_lock_irqsave(&vid_lock, flags);
+
+ _prev_fb = _cur_fb;
+ _cur_fb = fb;
+
+ fb->is_busy = 1;
+ fb->done = 0;
+ wmt_vid_set_addr(fb->y_addr, fb->c_addr);
+
+ spin_unlock_irqrestore(&vid_lock, flags);
+
+ return 0;
+}
+
+vid_fb_t *wmt_vid_get_cur_fb(void)
+{
+ unsigned long flags =0;
+ vid_fb_t *fb;
+
+ spin_lock_irqsave(&vid_lock, flags);
+ fb = _cur_fb;
+ spin_unlock_irqrestore(&vid_lock, flags);
+
+ return fb;
+}
+
+int wmt_vid_set_addr(unsigned int y_addr, unsigned int c_addr)
+{
+ unsigned int cur_y_addr; // temp
+ unsigned int cur_c_addr; // temp
+
+ TRACE("Enter\n");
+
+ cur_y_addr = REG32_VAL(REG_VID_Y0_SA);
+ cur_c_addr = REG32_VAL(REG_VID_C0_SA);
+
+ if ((y_addr != cur_y_addr) || (c_addr != cur_c_addr)) {
+ VID_REG_SET32( REG_VID_Y0_SA, y_addr); /* VID Y FB address */
+ VID_REG_SET32( REG_VID_C0_SA, c_addr); /* VID C FB address */
+ }
+
+ TRACE("Leave\n");
+ return 0;
+}
+
+int wmt_vid_open(vid_mode mode, cmos_uboot_env_t *uboot_env)
+{
+ int value, int_ctrl;
+ TRACE("Enter\n");
+
+ //vid_i2c_gpio_en = uboot_env->i2c_gpio_en;
+ DBG_INFO(" vid_i2c_gpio_en 0x%08x \n",vid_i2c_gpio_en);
+ if ((vid_i2c_gpio_en) && (uboot_env != NULL))
+ {
+ memset(&vid_i2c0_scl, 0, sizeof(vid_i2c0_scl));
+ memset(&vid_i2c0_sda, 0, sizeof(vid_i2c0_sda));
+
+ vid_i2c0_scl.bit_mask = 1 << uboot_env->i2c_gpio_scl_binum;
+ vid_i2c0_scl.pull_en_bit_mask = 1 << uboot_env->reg_i2c_gpio_scl_gpio_pe_bitnum;
+
+ vid_i2c0_scl.data_in = uboot_env->reg_i2c_gpio_scl_gpio_in ;
+ vid_i2c0_scl.gpio_en = uboot_env->reg_i2c_gpio_scl_gpio_en ;
+ vid_i2c0_scl.out_en = uboot_env->reg_i2c_gpio_scl_gpio_od ;
+ vid_i2c0_scl.data_out = uboot_env->reg_i2c_gpio_scl_gpio_oc ;
+ vid_i2c0_scl.pull_en = uboot_env->reg_i2c_gpio_scl_gpio_pe ;
+
+
+ vid_i2c0_sda.bit_mask = 1 << uboot_env->i2c_gpio_sda_binum;
+ vid_i2c0_sda.pull_en_bit_mask = 1 << uboot_env->reg_i2c_gpio_sda_gpio_pe_bitnum;
+
+ vid_i2c0_sda.data_in = uboot_env->reg_i2c_gpio_sda_gpio_in ;
+ vid_i2c0_sda.gpio_en = uboot_env->reg_i2c_gpio_sda_gpio_en ;
+ vid_i2c0_sda.out_en = uboot_env->reg_i2c_gpio_sda_gpio_od ;
+ vid_i2c0_sda.data_out = uboot_env->reg_i2c_gpio_sda_gpio_oc ;
+ vid_i2c0_sda.pull_en = uboot_env->reg_i2c_gpio_sda_gpio_pe ;
+
+ if (vid_i2c0_scl.data_in & 0x1) {
+ vid_i2c0_scl.bit_mask <<= 8;
+ vid_i2c0_scl.pull_en_bit_mask <<= 8;
+ vid_i2c0_scl.data_in -= 1;
+ vid_i2c0_scl.gpio_en -= 1;
+ vid_i2c0_scl.out_en -= 1;
+ vid_i2c0_scl.data_out -= 1;
+ vid_i2c0_scl.pull_en -= 1;
+
+ }
+ if (vid_i2c0_sda.data_in & 0x1) {
+ vid_i2c0_sda.bit_mask <<= 8;
+ vid_i2c0_sda.pull_en_bit_mask <<= 8;
+ vid_i2c0_sda.data_in -= 1;
+ vid_i2c0_sda.gpio_en -= 1;
+ vid_i2c0_sda.out_en -= 1;
+ vid_i2c0_sda.data_out -= 1;
+ vid_i2c0_sda.pull_en -= 1;
+ }
+
+ vid_swi2c.scl_reg = &vid_i2c0_scl;
+ vid_swi2c.sda_reg = &vid_i2c0_sda;
+
+ DBG_INFO("SCL 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x \n",
+ vid_i2c0_scl.bit_mask, vid_i2c0_scl.pull_en_bit_mask,
+ vid_i2c0_scl.data_in, vid_i2c0_scl.gpio_en,
+ vid_i2c0_scl.out_en, vid_i2c0_scl.data_out,
+ vid_i2c0_scl.pull_en);
+ DBG_INFO("SDA 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x \n",
+ vid_i2c0_sda.bit_mask, vid_i2c0_sda.pull_en_bit_mask,
+ vid_i2c0_sda.data_in, vid_i2c0_sda.gpio_en,
+ vid_i2c0_sda.out_en, vid_i2c0_sda.data_out,
+ vid_i2c0_sda.pull_en);
+ }
+
+ /*--------------------------------------------------------------------------
+ Step 1: Init GPIO for CMOS or TVDEC mode
+ --------------------------------------------------------------------------*/
+ vid_gpio_init(mode);
+
+ /*--------------------------------------------------------------------------
+ Step 2: Init CMOS or TVDEC module
+ --------------------------------------------------------------------------*/
+ value = REG32_VAL(REG_VID_TVDEC_CTRL);
+ VID_REG_SET32( REG_VID_MEMIF_EN, 0x1 );
+ VID_REG_SET32( REG_VID_OUTPUT_FORMAT, 0x0 ); // 0: 422 1: 444
+
+ int_ctrl = 0x00;
+ if (mode == VID_MODE_CMOS) {
+ VID_REG_SET32( REG_VID_TVDEC_CTRL, (value & 0xFFFFFFE)); /* disable TV decoder */
+ VID_REG_SET32( REG_VID_CMOS_PIXEL_SWAP, 0x2); /* 0x2 for YUYV */
+#ifdef VID_INT_MODE
+ int_ctrl = 0x0808;
+#endif
+ VID_REG_SET32( REG_VID_INT_CTRL, int_ctrl );
+
+ }
+ else {
+ VID_REG_SET32( REG_VID_TVDEC_CTRL, (value | 0x1) ); /* enable TV decoder */
+#ifdef VID_INT_MODE
+ int_ctrl = 0x0404;
+#endif
+ VID_REG_SET32( REG_VID_INT_CTRL, int_ctrl );
+ VID_REG_SET32( REG_VID_CMOS_EN, 0x0); /* disable CMOS */
+ wmt_vid_set_common_mode(VID_NTSC);
+
+ }
+
+ _cur_fb = 0;
+ _prev_fb = 0;
+
+ spin_lock_init(&vid_lock);
+
+ TRACE("Leave\n");
+
+ return 0;
+}
+
+int wmt_vid_close(vid_mode mode)
+{
+ TRACE("Enter\n");
+
+ auto_pll_divisor(DEV_VID, CLK_DISABLE, 0, 0);
+ auto_pll_divisor(DEV_C24MOUT, CLK_DISABLE, 0, 0);
+
+ GPIO_CTRL_GP17_I2C_BYTE_VAL |= BIT6;//24Mhz off , set to GPIO
+
+ if(mode == VID_MODE_CMOS) {
+ VID_REG_SET32( REG_VID_CMOS_EN, 0x0); /* disable CMOS */
+ }
+ else {
+ int value = REG32_VAL(REG_VID_TVDEC_CTRL);
+ VID_REG_SET32( REG_VID_TVDEC_CTRL, (value & 0xFFFFFFE)); /* disable TV decoder */
+ }
+ TRACE("Leave\n");
+
+ return 0;
+}
+
+EXPORT_SYMBOL(wmt_vid_set_mode);
+EXPORT_SYMBOL(wmt_vid_i2c_write);
+EXPORT_SYMBOL(wmt_vid_i2c_read);
+EXPORT_SYMBOL(wmt_vid_i2c_write16addr);
+EXPORT_SYMBOL(wmt_vid_i2c_read16addr);
+EXPORT_SYMBOL(wmt_vid_i2c_write16data);
+EXPORT_SYMBOL(wmt_vid_i2c_read16data);
+EXPORT_SYMBOL(wmt_vid_set_cur_fb);
+EXPORT_SYMBOL(wmt_vid_get_cur_fb);
+EXPORT_SYMBOL(wmt_vid_set_addr);
+EXPORT_SYMBOL(wmt_vid_close);
+EXPORT_SYMBOL(wmt_vid_open);
+EXPORT_SYMBOL(wmt_vid_set_common_mode);
+EXPORT_SYMBOL(wmt_vid_i2c_init);
+EXPORT_SYMBOL(wmt_vid_i2c_release);
+
+MODULE_AUTHOR("WonderMedia SW Team Max Chen");
+MODULE_DESCRIPTION("wmt-vid driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/media/video/wmt_v4l2/wmt-vid.h b/drivers/media/video/wmt_v4l2/wmt-vid.h
new file mode 100755
index 00000000..98918cdd
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/wmt-vid.h
@@ -0,0 +1,168 @@
+/*++
+ * linux/drivers/media/video/wmt_v4l2/wmt-vid.h
+ * WonderMedia v4l video input device driver
+ *
+ * Copyright c 2010 WonderMedia Technologies, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+--*/
+
+#ifndef WMT_VID_H
+#define WMT_VID_H
+
+#include <mach/hardware.h>
+#include "wmt-vidreg.h"
+#include "../../../video/wmt/sw_i2c.h"
+
+#ifdef __KERNEL__
+ #define PRINT printk
+#else
+ #define PRINT printf
+ #define KERN_ERR
+ #define KERN_WARNING
+ #define KERN_INFO
+ #define KERN_DEBUG
+#endif
+
+#define BIT0 0x00000001
+#define BIT1 0x00000002
+#define BIT2 0x00000004
+#define BIT3 0x00000008
+#define BIT4 0x00000010
+#define BIT5 0x00000020
+#define BIT6 0x00000040
+#define BIT7 0x00000080
+#define BIT8 0x00000100
+#define BIT9 0x00000200
+#define BIT10 0x00000400
+#define BIT11 0x00000800
+#define BIT12 0x00001000
+#define BIT13 0x00002000
+#define BIT14 0x00004000
+#define BIT15 0x00008000
+#define BIT16 0x00010000
+#define BIT17 0x00020000
+#define BIT18 0x00040000
+#define BIT19 0x00080000
+#define BIT20 0x00100000
+#define BIT21 0x00200000
+#define BIT22 0x00400000
+#define BIT23 0x00800000
+#define BIT24 0x01000000
+#define BIT25 0x02000000
+#define BIT26 0x04000000
+#define BIT27 0x08000000
+#define BIT28 0x10000000
+#define BIT29 0x20000000
+#define BIT30 0x40000000
+#define BIT31 0x80000000
+
+typedef enum{
+ VID_MODE_CMOS, /* CMOS sensor mode */
+ VID_MODE_TVDEC /* TV Decoder mode */
+} vid_mode;
+
+/*------------------------------------------------------------------------------
+ Definitions of structures
+------------------------------------------------------------------------------*/
+#define VID_FB_M \
+ unsigned int y_addr; \
+ unsigned int c_addr; \
+ unsigned int id; \
+ unsigned int done; \
+ unsigned int is_busy
+/* End of VD_IOCTL_CMD_M */
+
+/* Following structure is used for all HW decoder as input arguments */
+typedef struct {
+ VID_FB_M;
+} vid_fb_t;
+
+typedef struct {
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+ char name[12];
+ unsigned int gpio_pwr_active_level;
+ unsigned int gpio_pwr_bitnum;
+ unsigned int gpio_pwr_reg_gpio_en;
+ unsigned int gpio_pwr_reg_gpio_od;
+ unsigned int gpio_pwr_reg_gpio_oc;
+ short i2c_addr;
+} cmos_dev_t;
+
+typedef struct {
+ unsigned int en;
+ unsigned int dummy;
+
+ unsigned int dev_tot_num;
+ unsigned int dev_id;
+ unsigned int is_two_same_dev;
+ cmos_dev_t dev[2];
+
+ unsigned int v_flip;//v_mirror
+ unsigned int h_flip;//h_mirror
+ unsigned int i2c_gpio_en;
+
+ unsigned int i2c_gpio_scl_binum;
+ unsigned int i2c_gpio_sda_binum;
+
+ unsigned int reg_i2c_gpio_scl_gpio_in;
+ unsigned int reg_i2c_gpio_scl_gpio_en;
+ unsigned int reg_i2c_gpio_scl_gpio_od;
+ unsigned int reg_i2c_gpio_scl_gpio_oc;
+ unsigned int reg_i2c_gpio_scl_gpio_pe;
+ unsigned int reg_i2c_gpio_scl_gpio_pe_bitnum;
+
+ unsigned int reg_i2c_gpio_sda_gpio_in;
+ unsigned int reg_i2c_gpio_sda_gpio_en;
+ unsigned int reg_i2c_gpio_sda_gpio_od;
+ unsigned int reg_i2c_gpio_sda_gpio_oc;
+ unsigned int reg_i2c_gpio_sda_gpio_pe;
+ unsigned int reg_i2c_gpio_sda_gpio_pe_bitnum;
+
+ char isMIPI;
+} cmos_uboot_env_t;
+
+typedef enum {
+ VID_NTSC,
+ VID_PAL
+} vid_tvsys_e;
+
+
+int wmt_vid_open(vid_mode mode,cmos_uboot_env_t *cmos_uboot_env);
+int wmt_vid_close(vid_mode mode);
+int wmt_vid_set_mode(int width, int height, unsigned int pix_format);
+int wmt_vid_set_addr(unsigned int y_addr, unsigned int c_addr);
+
+int wmt_vid_set_cur_fb(vid_fb_t *fb);
+vid_fb_t * wmt_vid_get_cur_fb(void);
+int write_array_2_i2c(int slave_addr, char *array_addr, int array_size);
+
+int wmt_vid_i2c_write(int chipId ,unsigned int index,char data);
+int wmt_vid_i2c_read(int chipId ,unsigned int index) ;
+int wmt_vid_i2c_read_page(int chipId ,unsigned int index,char *pdata,int len);
+int wmt_vid_i2c_write_page(int chipId ,unsigned int index,char *pdata,int len);
+int wmt_vid_i2c_write16addr(int chipId ,unsigned int index,unsigned int data);
+int wmt_vid_i2c_read16addr(int chipId ,unsigned int index);
+int wmt_vid_i2c_write16data(int chipId ,unsigned int index,unsigned int data);
+int wmt_vid_i2c_read16data(int chipId ,unsigned int index);
+int wmt_vid_i2c_init( int dev_num, short dev_id);
+int wmt_vid_i2c_release(void);
+int wmt_vid_i2c_set_same_dev(void);
+
+#endif
+
diff --git a/drivers/media/video/wmt_v4l2/wmt-vidreg.h b/drivers/media/video/wmt_v4l2/wmt-vidreg.h
new file mode 100755
index 00000000..c7abddc8
--- /dev/null
+++ b/drivers/media/video/wmt_v4l2/wmt-vidreg.h
@@ -0,0 +1,87 @@
+/*++
+ * linux/drivers/media/video/wmt_v4l2/wmt-vidreg.h
+ * WonderMedia v4l video input device driver
+ *
+ * Copyright c 2010 WonderMedia Technologies, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+--*/
+#ifndef WMT_VIDREG_H
+/* To assert that only one occurrence is included */
+#define WMT_VIDREG_H
+
+/*-------------------- MODULE DEPENDENCY -------------------------------------*/
+
+/*-------------------- EXPORTED PRIVATE CONSTANTS ----------------------------*/
+
+#define WMT_VID_IRQ IRQ_VPP_IRQ10 // this may be changed by chip
+
+#define REG_BASE_VID VID_BASE_ADDR
+
+/*------------------------------------------------------------------------------
+ Definitions of VID Registers
+ About following definitions, please refer "WM3437 VID/CMOS Register List"
+
+ Prefix meanings:
+ REG_VID_xxx: used for both TV deccoder and CMOS
+ REG_VID_TVDEC_xxx: used for TV encoder only
+ REG_VID_CMOS_XXX: used for CMOS only
+------------------------------------------------------------------------------*/
+#define REG_VID_TVDEC_EN (REG_BASE_VID + 0x00)
+#define REG_VID_TVDEC_CTRL (REG_BASE_VID + 0x04)
+#define REG_VID_Y0_SA (REG_BASE_VID + 0x60)
+#define REG_VID_C0_SA (REG_BASE_VID + 0x64)
+#define REG_VID_WIDTH (REG_BASE_VID + 0x68)
+#define REG_VID_LINE_WIDTH (REG_BASE_VID + 0x6C)
+#define REG_VID_HEIGHT (REG_BASE_VID + 0x70)
+#define REG_VID_MEMIF_EN (REG_BASE_VID + 0x74)
+#define REG_VID_OUTPUT_FORMAT (REG_BASE_VID + 0x78)
+#define REG_VID_H_SCALE (REG_BASE_VID + 0x7C)
+#define REG_VID_CMOS_EN (REG_BASE_VID + 0x80)
+#define REG_VID_CMOS_PIXEL_SWAP (REG_BASE_VID + 0x84)
+
+#define REG_VID_INT_CTRL (REG_BASE_VID + 0xF0)
+#define REG_VID_STS (REG_BASE_VID + 0xF4)
+#define REG_VIDCLK_INV (REG_BASE_VID + 0xFc)
+
+
+/*-------------------- EXPORTED PRIVATE TYPES---------------------------------*/
+/* typedef void viaapi_xxx_t; *//*Example*/
+
+/*------------------------------------------------------------------------------
+ Definitions of structures
+------------------------------------------------------------------------------*/
+
+
+/*-------------------- EXPORTED PRIVATE VARIABLES -----------------------------*/
+#ifdef WMT_VID_C
+ #define EXTERN
+#else
+ #define EXTERN extern
+#endif
+
+#undef EXTERN
+
+/*--------------------- EXPORTED PRIVATE MACROS -------------------------------*/
+
+/*--------------------- EXPORTED PRIVATE FUNCTIONS ---------------------------*/
+/* extern void viaapi_xxxx(vdp_Void); *//*Example*/
+
+
+#endif /* ifndef WMT_VIDREG_H */
+
+/*=== END wmt-vidreg.h ==========================================================*/
diff --git a/drivers/media/video/zoran/Kconfig b/drivers/media/video/zoran/Kconfig
new file mode 100644
index 00000000..fd4120e4
--- /dev/null
+++ b/drivers/media/video/zoran/Kconfig
@@ -0,0 +1,74 @@
+config VIDEO_ZORAN
+ tristate "Zoran ZR36057/36067 Video For Linux"
+ depends on PCI && I2C_ALGOBIT && VIDEO_V4L2 && VIRT_TO_BUS
+ help
+ Say Y for support for MJPEG capture cards based on the Zoran
+ 36057/36067 PCI controller chipset. This includes the Iomega
+ Buz, Pinnacle DC10+ and the Linux Media Labs LML33. There is
+ a driver homepage at <http://mjpeg.sf.net/driver-zoran/>. For
+ more information, check <file:Documentation/video4linux/Zoran>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called zr36067.
+
+config VIDEO_ZORAN_DC30
+ tristate "Pinnacle/Miro DC30(+) support"
+ depends on VIDEO_ZORAN
+ select VIDEO_ADV7175 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_VPX3220 if VIDEO_HELPER_CHIPS_AUTO
+ help
+ Support for the Pinnacle/Miro DC30(+) MJPEG capture/playback
+ card. This also supports really old DC10 cards based on the
+ zr36050 MJPEG codec and zr36016 VFE.
+
+config VIDEO_ZORAN_ZR36060
+ tristate "Zoran ZR36060"
+ depends on VIDEO_ZORAN
+ help
+ Say Y to support Zoran boards based on 36060 chips.
+ This includes Iomega Buz, Pinnacle DC10, Linux media Labs 33
+ and 33 R10 and AverMedia 6 boards.
+
+config VIDEO_ZORAN_BUZ
+ tristate "Iomega Buz support"
+ depends on VIDEO_ZORAN_ZR36060
+ select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_SAA7185 if VIDEO_HELPER_CHIPS_AUTO
+ help
+ Support for the Iomega Buz MJPEG capture/playback card.
+
+config VIDEO_ZORAN_DC10
+ tristate "Pinnacle/Miro DC10(+) support"
+ depends on VIDEO_ZORAN_ZR36060
+ select VIDEO_SAA7110 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_ADV7175 if VIDEO_HELPER_CHIPS_AUTO
+ help
+ Support for the Pinnacle/Miro DC10(+) MJPEG capture/playback
+ card.
+
+config VIDEO_ZORAN_LML33
+ tristate "Linux Media Labs LML33 support"
+ depends on VIDEO_ZORAN_ZR36060
+ select VIDEO_BT819 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_BT856 if VIDEO_HELPER_CHIPS_AUTO
+ help
+ Support for the Linux Media Labs LML33 MJPEG capture/playback
+ card.
+
+config VIDEO_ZORAN_LML33R10
+ tristate "Linux Media Labs LML33R10 support"
+ depends on VIDEO_ZORAN_ZR36060
+ select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_ADV7170 if VIDEO_HELPER_CHIPS_AUTO
+ help
+ support for the Linux Media Labs LML33R10 MJPEG capture/playback
+ card.
+
+config VIDEO_ZORAN_AVS6EYES
+ tristate "AverMedia 6 Eyes support (EXPERIMENTAL)"
+ depends on VIDEO_ZORAN_ZR36060 && EXPERIMENTAL
+ select VIDEO_BT856 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_BT866 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_KS0127 if VIDEO_HELPER_CHIPS_AUTO
+ help
+ Support for the AverMedia 6 Eyes video surveillance card.
diff --git a/drivers/media/video/zoran/Makefile b/drivers/media/video/zoran/Makefile
new file mode 100644
index 00000000..44cc1335
--- /dev/null
+++ b/drivers/media/video/zoran/Makefile
@@ -0,0 +1,6 @@
+zr36067-objs := zoran_procfs.o zoran_device.o \
+ zoran_driver.o zoran_card.o
+
+obj-$(CONFIG_VIDEO_ZORAN) += zr36067.o videocodec.o
+obj-$(CONFIG_VIDEO_ZORAN_DC30) += zr36050.o zr36016.o
+obj-$(CONFIG_VIDEO_ZORAN_ZR36060) += zr36060.o
diff --git a/drivers/media/video/zoran/videocodec.c b/drivers/media/video/zoran/videocodec.c
new file mode 100644
index 00000000..c0107163
--- /dev/null
+++ b/drivers/media/video/zoran/videocodec.c
@@ -0,0 +1,407 @@
+/*
+ * VIDEO MOTION CODECs internal API for video devices
+ *
+ * Interface for MJPEG (and maybe later MPEG/WAVELETS) codec's
+ * bound to a master device.
+ *
+ * (c) 2002 Wolfgang Scherr <scherr@net4you.at>
+ *
+ * $Id: videocodec.c,v 1.1.2.8 2003/03/29 07:16:04 rbultje Exp $
+ *
+ * ------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+#define VIDEOCODEC_VERSION "v0.2"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+
+// kernel config is here (procfs flag)
+
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <asm/uaccess.h>
+#endif
+
+#include "videocodec.h"
+
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-4)");
+
+#define dprintk(num, format, args...) \
+ do { \
+ if (debug >= num) \
+ printk(format, ##args); \
+ } while (0)
+
+struct attached_list {
+ struct videocodec *codec;
+ struct attached_list *next;
+};
+
+struct codec_list {
+ const struct videocodec *codec;
+ int attached;
+ struct attached_list *list;
+ struct codec_list *next;
+};
+
+static struct codec_list *codeclist_top = NULL;
+
+/* ================================================= */
+/* function prototypes of the master/slave interface */
+/* ================================================= */
+
+struct videocodec *
+videocodec_attach (struct videocodec_master *master)
+{
+ struct codec_list *h = codeclist_top;
+ struct attached_list *a, *ptr;
+ struct videocodec *codec;
+ int res;
+
+ if (!master) {
+ dprintk(1, KERN_ERR "videocodec_attach: no data\n");
+ return NULL;
+ }
+
+ dprintk(2,
+ "videocodec_attach: '%s', flags %lx, magic %lx\n",
+ master->name, master->flags, master->magic);
+
+ if (!h) {
+ dprintk(1,
+ KERN_ERR
+ "videocodec_attach: no device available\n");
+ return NULL;
+ }
+
+ while (h) {
+ // attach only if the slave has at least the flags
+ // expected by the master
+ if ((master->flags & h->codec->flags) == master->flags) {
+ dprintk(4, "videocodec_attach: try '%s'\n",
+ h->codec->name);
+
+ if (!try_module_get(h->codec->owner))
+ return NULL;
+
+ codec = kmemdup(h->codec, sizeof(struct videocodec),
+ GFP_KERNEL);
+ if (!codec) {
+ dprintk(1,
+ KERN_ERR
+ "videocodec_attach: no mem\n");
+ goto out_module_put;
+ }
+
+ snprintf(codec->name, sizeof(codec->name),
+ "%s[%d]", codec->name, h->attached);
+ codec->master_data = master;
+ res = codec->setup(codec);
+ if (res == 0) {
+ dprintk(3, "videocodec_attach '%s'\n",
+ codec->name);
+ ptr = kzalloc(sizeof(struct attached_list), GFP_KERNEL);
+ if (!ptr) {
+ dprintk(1,
+ KERN_ERR
+ "videocodec_attach: no memory\n");
+ goto out_kfree;
+ }
+ ptr->codec = codec;
+
+ a = h->list;
+ if (!a) {
+ h->list = ptr;
+ dprintk(4,
+ "videocodec: first element\n");
+ } else {
+ while (a->next)
+ a = a->next; // find end
+ a->next = ptr;
+ dprintk(4,
+ "videocodec: in after '%s'\n",
+ h->codec->name);
+ }
+
+ h->attached += 1;
+ return codec;
+ } else {
+ kfree(codec);
+ }
+ }
+ h = h->next;
+ }
+
+ dprintk(1, KERN_ERR "videocodec_attach: no codec found!\n");
+ return NULL;
+
+ out_module_put:
+ module_put(h->codec->owner);
+ out_kfree:
+ kfree(codec);
+ return NULL;
+}
+
+int
+videocodec_detach (struct videocodec *codec)
+{
+ struct codec_list *h = codeclist_top;
+ struct attached_list *a, *prev;
+ int res;
+
+ if (!codec) {
+ dprintk(1, KERN_ERR "videocodec_detach: no data\n");
+ return -EINVAL;
+ }
+
+ dprintk(2,
+ "videocodec_detach: '%s', type: %x, flags %lx, magic %lx\n",
+ codec->name, codec->type, codec->flags, codec->magic);
+
+ if (!h) {
+ dprintk(1,
+ KERN_ERR "videocodec_detach: no device left...\n");
+ return -ENXIO;
+ }
+
+ while (h) {
+ a = h->list;
+ prev = NULL;
+ while (a) {
+ if (codec == a->codec) {
+ res = a->codec->unset(a->codec);
+ if (res >= 0) {
+ dprintk(3,
+ "videocodec_detach: '%s'\n",
+ a->codec->name);
+ a->codec->master_data = NULL;
+ } else {
+ dprintk(1,
+ KERN_ERR
+ "videocodec_detach: '%s'\n",
+ a->codec->name);
+ a->codec->master_data = NULL;
+ }
+ if (prev == NULL) {
+ h->list = a->next;
+ dprintk(4,
+ "videocodec: delete first\n");
+ } else {
+ prev->next = a->next;
+ dprintk(4,
+ "videocodec: delete middle\n");
+ }
+ module_put(a->codec->owner);
+ kfree(a->codec);
+ kfree(a);
+ h->attached -= 1;
+ return 0;
+ }
+ prev = a;
+ a = a->next;
+ }
+ h = h->next;
+ }
+
+ dprintk(1, KERN_ERR "videocodec_detach: given codec not found!\n");
+ return -EINVAL;
+}
+
+int
+videocodec_register (const struct videocodec *codec)
+{
+ struct codec_list *ptr, *h = codeclist_top;
+
+ if (!codec) {
+ dprintk(1, KERN_ERR "videocodec_register: no data!\n");
+ return -EINVAL;
+ }
+
+ dprintk(2,
+ "videocodec: register '%s', type: %x, flags %lx, magic %lx\n",
+ codec->name, codec->type, codec->flags, codec->magic);
+
+ ptr = kzalloc(sizeof(struct codec_list), GFP_KERNEL);
+ if (!ptr) {
+ dprintk(1, KERN_ERR "videocodec_register: no memory\n");
+ return -ENOMEM;
+ }
+ ptr->codec = codec;
+
+ if (!h) {
+ codeclist_top = ptr;
+ dprintk(4, "videocodec: hooked in as first element\n");
+ } else {
+ while (h->next)
+ h = h->next; // find the end
+ h->next = ptr;
+ dprintk(4, "videocodec: hooked in after '%s'\n",
+ h->codec->name);
+ }
+
+ return 0;
+}
+
+int
+videocodec_unregister (const struct videocodec *codec)
+{
+ struct codec_list *prev = NULL, *h = codeclist_top;
+
+ if (!codec) {
+ dprintk(1, KERN_ERR "videocodec_unregister: no data!\n");
+ return -EINVAL;
+ }
+
+ dprintk(2,
+ "videocodec: unregister '%s', type: %x, flags %lx, magic %lx\n",
+ codec->name, codec->type, codec->flags, codec->magic);
+
+ if (!h) {
+ dprintk(1,
+ KERN_ERR
+ "videocodec_unregister: no device left...\n");
+ return -ENXIO;
+ }
+
+ while (h) {
+ if (codec == h->codec) {
+ if (h->attached) {
+ dprintk(1,
+ KERN_ERR
+ "videocodec: '%s' is used\n",
+ h->codec->name);
+ return -EBUSY;
+ }
+ dprintk(3, "videocodec: unregister '%s' is ok.\n",
+ h->codec->name);
+ if (prev == NULL) {
+ codeclist_top = h->next;
+ dprintk(4,
+ "videocodec: delete first element\n");
+ } else {
+ prev->next = h->next;
+ dprintk(4,
+ "videocodec: delete middle element\n");
+ }
+ kfree(h);
+ return 0;
+ }
+ prev = h;
+ h = h->next;
+ }
+
+ dprintk(1,
+ KERN_ERR
+ "videocodec_unregister: given codec not found!\n");
+ return -EINVAL;
+}
+
+#ifdef CONFIG_PROC_FS
+static int proc_videocodecs_show(struct seq_file *m, void *v)
+{
+ struct codec_list *h = codeclist_top;
+ struct attached_list *a;
+
+ seq_printf(m, "<S>lave or attached <M>aster name type flags magic ");
+ seq_printf(m, "(connected as)\n");
+
+ h = codeclist_top;
+ while (h) {
+ seq_printf(m, "S %32s %04x %08lx %08lx (TEMPLATE)\n",
+ h->codec->name, h->codec->type,
+ h->codec->flags, h->codec->magic);
+ a = h->list;
+ while (a) {
+ seq_printf(m, "M %32s %04x %08lx %08lx (%s)\n",
+ a->codec->master_data->name,
+ a->codec->master_data->type,
+ a->codec->master_data->flags,
+ a->codec->master_data->magic,
+ a->codec->name);
+ a = a->next;
+ }
+ h = h->next;
+ }
+
+ return 0;
+}
+
+static int proc_videocodecs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, proc_videocodecs_show, NULL);
+}
+
+static const struct file_operations videocodecs_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = proc_videocodecs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+#endif
+
+/* ===================== */
+/* hook in driver module */
+/* ===================== */
+static int __init
+videocodec_init (void)
+{
+#ifdef CONFIG_PROC_FS
+ static struct proc_dir_entry *videocodec_proc_entry;
+#endif
+
+ printk(KERN_INFO "Linux video codec intermediate layer: %s\n",
+ VIDEOCODEC_VERSION);
+
+#ifdef CONFIG_PROC_FS
+ videocodec_proc_entry = proc_create("videocodecs", 0, NULL, &videocodecs_proc_fops);
+ if (!videocodec_proc_entry) {
+ dprintk(1, KERN_ERR "videocodec: can't init procfs.\n");
+ }
+#endif
+ return 0;
+}
+
+static void __exit
+videocodec_exit (void)
+{
+#ifdef CONFIG_PROC_FS
+ remove_proc_entry("videocodecs", NULL);
+#endif
+}
+
+EXPORT_SYMBOL(videocodec_attach);
+EXPORT_SYMBOL(videocodec_detach);
+EXPORT_SYMBOL(videocodec_register);
+EXPORT_SYMBOL(videocodec_unregister);
+
+module_init(videocodec_init);
+module_exit(videocodec_exit);
+
+MODULE_AUTHOR("Wolfgang Scherr <scherr@net4you.at>");
+MODULE_DESCRIPTION("Intermediate API module for video codecs "
+ VIDEOCODEC_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/zoran/videocodec.h b/drivers/media/video/zoran/videocodec.h
new file mode 100644
index 00000000..def55585
--- /dev/null
+++ b/drivers/media/video/zoran/videocodec.h
@@ -0,0 +1,353 @@
+/*
+ * VIDEO MOTION CODECs internal API for video devices
+ *
+ * Interface for MJPEG (and maybe later MPEG/WAVELETS) codec's
+ * bound to a master device.
+ *
+ * (c) 2002 Wolfgang Scherr <scherr@net4you.at>
+ *
+ * $Id: videocodec.h,v 1.1.2.4 2003/01/14 21:15:03 rbultje Exp $
+ *
+ * ------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+/* =================== */
+/* general description */
+/* =================== */
+
+/* Should ease the (re-)usage of drivers supporting cards with (different)
+ video codecs. The codecs register to this module their functionality,
+ and the processors (masters) can attach to them if they fit.
+
+ The codecs are typically have a "strong" binding to their master - so I
+ don't think it makes sense to have a full blown interfacing as with e.g.
+ i2c. If you have an other opinion, let's discuss & implement it :-)))
+
+ Usage:
+
+ The slave has just to setup the videocodec structure and use two functions:
+ videocodec_register(codecdata);
+ videocodec_unregister(codecdata);
+ The best is just calling them at module (de-)initialisation.
+
+ The master sets up the structure videocodec_master and calls:
+ codecdata=videocodec_attach(master_codecdata);
+ videocodec_detach(codecdata);
+
+ The slave is called during attach/detach via functions setup previously
+ during register. At that time, the master_data pointer is set up
+ and the slave can access any io registers of the master device (in the case
+ the slave is bound to it). Otherwise it doesn't need this functions and
+ therfor they may not be initialized.
+
+ The other functions are just for convenience, as they are for sure used by
+ most/all of the codecs. The last ones may be omitted, too.
+
+ See the structure declaration below for more information and which data has
+ to be set up for the master and the slave.
+
+ ----------------------------------------------------------------------------
+ The master should have "knowledge" of the slave and vice versa. So the data
+ structures sent to/from slave via set_data/get_data set_image/get_image are
+ device dependent and vary between MJPEG/MPEG/WAVELET/... devices. (!!!!)
+ ----------------------------------------------------------------------------
+*/
+
+
+/* ========================================== */
+/* description of the videocodec_io structure */
+/* ========================================== */
+
+/*
+ ==== master setup ====
+ name -> name of the device structure for reference and debugging
+ master_data -> data ref. for the master (e.g. the zr36055,57,67)
+ readreg -> ref. to read-fn from register (setup by master, used by slave)
+ writereg -> ref. to write-fn to register (setup by master, used by slave)
+ this two functions do the lowlevel I/O job
+
+ ==== slave functionality setup ====
+ slave_data -> data ref. for the slave (e.g. the zr36050,60)
+ check -> fn-ref. checks availability of an device, returns -EIO on failure or
+ the type on success
+ this makes espcecially sense if a driver module supports more than
+ one codec which may be quite similar to access, nevertheless it
+ is good for a first functionality check
+
+ -- main functions you always need for compression/decompression --
+
+ set_mode -> this fn-ref. resets the entire codec, and sets up the mode
+ with the last defined norm/size (or device default if not
+ available) - it returns 0 if the mode is possible
+ set_size -> this fn-ref. sets the norm and image size for
+ compression/decompression (returns 0 on success)
+ the norm param is defined in videodev2.h (V4L2_STD_*)
+
+ additional setup may be available, too - but the codec should work with
+ some default values even without this
+
+ set_data -> sets device-specific data (tables, quality etc.)
+ get_data -> query device-specific data (tables, quality etc.)
+
+ if the device delivers interrupts, they may be setup/handled here
+ setup_interrupt -> codec irq setup (not needed for 36050/60)
+ handle_interrupt -> codec irq handling (not needed for 36050/60)
+
+ if the device delivers pictures, they may be handled here
+ put_image -> puts image data to the codec (not needed for 36050/60)
+ get_image -> gets image data from the codec (not needed for 36050/60)
+ the calls include frame numbers and flags (even/odd/...)
+ if needed and a flag which allows blocking until its ready
+*/
+
+/* ============== */
+/* user interface */
+/* ============== */
+
+/*
+ Currently there is only a information display planned, as the layer
+ is not visible for the user space at all.
+
+ Information is available via procfs. The current entry is "/proc/videocodecs"
+ but it makes sense to "hide" it in the /proc/video tree of v4l(2) --TODO--.
+
+A example for such an output is:
+
+<S>lave or attached <M>aster name type flags magic (connected as)
+S zr36050 0002 0000d001 00000000 (TEMPLATE)
+M zr36055[0] 0001 0000c001 00000000 (zr36050[0])
+M zr36055[1] 0001 0000c001 00000000 (zr36050[1])
+
+*/
+
+
+/* =============================================== */
+/* special defines for the videocodec_io structure */
+/* =============================================== */
+
+#ifndef __LINUX_VIDEOCODEC_H
+#define __LINUX_VIDEOCODEC_H
+
+#include <linux/videodev2.h>
+
+#define CODEC_DO_COMPRESSION 0
+#define CODEC_DO_EXPANSION 1
+
+/* this are the current codec flags I think they are needed */
+/* -> type value in structure */
+#define CODEC_FLAG_JPEG 0x00000001L // JPEG codec
+#define CODEC_FLAG_MPEG 0x00000002L // MPEG1/2/4 codec
+#define CODEC_FLAG_DIVX 0x00000004L // DIVX codec
+#define CODEC_FLAG_WAVELET 0x00000008L // WAVELET codec
+ // room for other types
+
+#define CODEC_FLAG_MAGIC 0x00000800L // magic key must match
+#define CODEC_FLAG_HARDWARE 0x00001000L // is a hardware codec
+#define CODEC_FLAG_VFE 0x00002000L // has direct video frontend
+#define CODEC_FLAG_ENCODER 0x00004000L // compression capability
+#define CODEC_FLAG_DECODER 0x00008000L // decompression capability
+#define CODEC_FLAG_NEEDIRQ 0x00010000L // needs irq handling
+#define CODEC_FLAG_RDWRPIC 0x00020000L // handles picture I/O
+
+/* a list of modes, some are just examples (is there any HW?) */
+#define CODEC_MODE_BJPG 0x0001 // Baseline JPEG
+#define CODEC_MODE_LJPG 0x0002 // Lossless JPEG
+#define CODEC_MODE_MPEG1 0x0003 // MPEG 1
+#define CODEC_MODE_MPEG2 0x0004 // MPEG 2
+#define CODEC_MODE_MPEG4 0x0005 // MPEG 4
+#define CODEC_MODE_MSDIVX 0x0006 // MS DivX
+#define CODEC_MODE_ODIVX 0x0007 // Open DivX
+#define CODEC_MODE_WAVELET 0x0008 // Wavelet
+
+/* this are the current codec types I want to implement */
+/* -> type value in structure */
+#define CODEC_TYPE_NONE 0
+#define CODEC_TYPE_L64702 1
+#define CODEC_TYPE_ZR36050 2
+#define CODEC_TYPE_ZR36016 3
+#define CODEC_TYPE_ZR36060 4
+
+/* the type of data may be enhanced by future implementations (data-fn.'s) */
+/* -> used in command */
+#define CODEC_G_STATUS 0x0000 /* codec status (query only) */
+#define CODEC_S_CODEC_MODE 0x0001 /* codec mode (baseline JPEG, MPEG1,... */
+#define CODEC_G_CODEC_MODE 0x8001
+#define CODEC_S_VFE 0x0002 /* additional video frontend setup */
+#define CODEC_G_VFE 0x8002
+#define CODEC_S_MMAP 0x0003 /* MMAP setup (if available) */
+
+#define CODEC_S_JPEG_TDS_BYTE 0x0010 /* target data size in bytes */
+#define CODEC_G_JPEG_TDS_BYTE 0x8010
+#define CODEC_S_JPEG_SCALE 0x0011 /* scaling factor for quant. tables */
+#define CODEC_G_JPEG_SCALE 0x8011
+#define CODEC_S_JPEG_HDT_DATA 0x0018 /* huffman-tables */
+#define CODEC_G_JPEG_HDT_DATA 0x8018
+#define CODEC_S_JPEG_QDT_DATA 0x0019 /* quantizing-tables */
+#define CODEC_G_JPEG_QDT_DATA 0x8019
+#define CODEC_S_JPEG_APP_DATA 0x001A /* APP marker */
+#define CODEC_G_JPEG_APP_DATA 0x801A
+#define CODEC_S_JPEG_COM_DATA 0x001B /* COM marker */
+#define CODEC_G_JPEG_COM_DATA 0x801B
+
+#define CODEC_S_PRIVATE 0x1000 /* "private" commands start here */
+#define CODEC_G_PRIVATE 0x9000
+
+#define CODEC_G_FLAG 0x8000 /* this is how 'get' is detected */
+
+/* types of transfer, directly user space or a kernel buffer (image-fn.'s) */
+/* -> used in get_image, put_image */
+#define CODEC_TRANSFER_KERNEL 0 /* use "memcopy" */
+#define CODEC_TRANSFER_USER 1 /* use "to/from_user" */
+
+
+/* ========================= */
+/* the structures itself ... */
+/* ========================= */
+
+struct vfe_polarity {
+ unsigned int vsync_pol:1;
+ unsigned int hsync_pol:1;
+ unsigned int field_pol:1;
+ unsigned int blank_pol:1;
+ unsigned int subimg_pol:1;
+ unsigned int poe_pol:1;
+ unsigned int pvalid_pol:1;
+ unsigned int vclk_pol:1;
+};
+
+struct vfe_settings {
+ __u32 x, y; /* Offsets into image */
+ __u32 width, height; /* Area to capture */
+ __u16 decimation; /* Decimation divider */
+ __u16 flags; /* Flags for capture */
+ __u16 quality; /* quality of the video */
+};
+
+struct tvnorm {
+ u16 Wt, Wa, HStart, HSyncStart, Ht, Ha, VStart;
+};
+
+struct jpeg_com_marker {
+ int len; /* number of usable bytes in data */
+ char data[60];
+};
+
+struct jpeg_app_marker {
+ int appn; /* number app segment */
+ int len; /* number of usable bytes in data */
+ char data[60];
+};
+
+struct videocodec {
+ struct module *owner;
+ /* -- filled in by slave device during register -- */
+ char name[32];
+ unsigned long magic; /* may be used for client<->master attaching */
+ unsigned long flags; /* functionality flags */
+ unsigned int type; /* codec type */
+
+ /* -- these is filled in later during master device attach -- */
+
+ struct videocodec_master *master_data;
+
+ /* -- these are filled in by the slave device during register -- */
+
+ void *data; /* private slave data */
+
+ /* attach/detach client functions (indirect call) */
+ int (*setup) (struct videocodec * codec);
+ int (*unset) (struct videocodec * codec);
+
+ /* main functions, every client needs them for sure! */
+ // set compression or decompression (or freeze, stop, standby, etc)
+ int (*set_mode) (struct videocodec * codec,
+ int mode);
+ // setup picture size and norm (for the codec's video frontend)
+ int (*set_video) (struct videocodec * codec,
+ struct tvnorm * norm,
+ struct vfe_settings * cap,
+ struct vfe_polarity * pol);
+ // other control commands, also mmap setup etc.
+ int (*control) (struct videocodec * codec,
+ int type,
+ int size,
+ void *data);
+
+ /* additional setup/query/processing (may be NULL pointer) */
+ // interrupt setup / handling (for irq's delivered by master)
+ int (*setup_interrupt) (struct videocodec * codec,
+ long mode);
+ int (*handle_interrupt) (struct videocodec * codec,
+ int source,
+ long flag);
+ // picture interface (if any)
+ long (*put_image) (struct videocodec * codec,
+ int tr_type,
+ int block,
+ long *fr_num,
+ long *flag,
+ long size,
+ void *buf);
+ long (*get_image) (struct videocodec * codec,
+ int tr_type,
+ int block,
+ long *fr_num,
+ long *flag,
+ long size,
+ void *buf);
+};
+
+struct videocodec_master {
+ /* -- filled in by master device for registration -- */
+ char name[32];
+ unsigned long magic; /* may be used for client<->master attaching */
+ unsigned long flags; /* functionality flags */
+ unsigned int type; /* master type */
+
+ void *data; /* private master data */
+
+ __u32(*readreg) (struct videocodec * codec,
+ __u16 reg);
+ void (*writereg) (struct videocodec * codec,
+ __u16 reg,
+ __u32 value);
+};
+
+
+/* ================================================= */
+/* function prototypes of the master/slave interface */
+/* ================================================= */
+
+/* attach and detach commands for the master */
+// * master structure needs to be kmalloc'ed before calling attach
+// and free'd after calling detach
+// * returns pointer on success, NULL on failure
+extern struct videocodec *videocodec_attach(struct videocodec_master *);
+// * 0 on success, <0 (errno) on failure
+extern int videocodec_detach(struct videocodec *);
+
+/* register and unregister commands for the slaves */
+// * 0 on success, <0 (errno) on failure
+extern int videocodec_register(const struct videocodec *);
+// * 0 on success, <0 (errno) on failure
+extern int videocodec_unregister(const struct videocodec *);
+
+/* the other calls are directly done via the videocodec structure! */
+
+#endif /*ifndef __LINUX_VIDEOCODEC_H */
diff --git a/drivers/media/video/zoran/zoran.h b/drivers/media/video/zoran/zoran.h
new file mode 100644
index 00000000..d7166afc
--- /dev/null
+++ b/drivers/media/video/zoran/zoran.h
@@ -0,0 +1,401 @@
+/*
+ * zoran - Iomega Buz driver
+ *
+ * Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de>
+ *
+ * based on
+ *
+ * zoran.0.0.3 Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+ *
+ * and
+ *
+ * bttv - Bt848 frame grabber driver
+ * Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
+ * & Marcus Metzler (mocm@thp.uni-koeln.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _BUZ_H_
+#define _BUZ_H_
+
+#include <media/v4l2-device.h>
+
+struct zoran_sync {
+ unsigned long frame; /* number of buffer that has been free'd */
+ unsigned long length; /* number of code bytes in buffer (capture only) */
+ unsigned long seq; /* frame sequence number */
+ struct timeval timestamp; /* timestamp */
+};
+
+
+#define ZORAN_NAME "ZORAN" /* name of the device */
+
+#define ZR_DEVNAME(zr) ((zr)->name)
+
+#define BUZ_MAX_WIDTH (zr->timing->Wa)
+#define BUZ_MAX_HEIGHT (zr->timing->Ha)
+#define BUZ_MIN_WIDTH 32 /* never display less than 32 pixels */
+#define BUZ_MIN_HEIGHT 24 /* never display less than 24 rows */
+
+#define BUZ_NUM_STAT_COM 4
+#define BUZ_MASK_STAT_COM 3
+
+#define BUZ_MAX_FRAME 256 /* Must be a power of 2 */
+#define BUZ_MASK_FRAME 255 /* Must be BUZ_MAX_FRAME-1 */
+
+#define BUZ_MAX_INPUT 16
+
+#if VIDEO_MAX_FRAME <= 32
+# define V4L_MAX_FRAME 32
+#elif VIDEO_MAX_FRAME <= 64
+# define V4L_MAX_FRAME 64
+#else
+# error "Too many video frame buffers to handle"
+#endif
+#define V4L_MASK_FRAME (V4L_MAX_FRAME - 1)
+
+#define MAX_FRAME (BUZ_MAX_FRAME > VIDEO_MAX_FRAME ? BUZ_MAX_FRAME : VIDEO_MAX_FRAME)
+
+#include "zr36057.h"
+
+enum card_type {
+ UNKNOWN = -1,
+
+ /* Pinnacle/Miro */
+ DC10_old, /* DC30 like */
+ DC10_new, /* DC10plus like */
+ DC10plus,
+ DC30,
+ DC30plus,
+
+ /* Linux Media Labs */
+ LML33,
+ LML33R10,
+
+ /* Iomega */
+ BUZ,
+
+ /* AverMedia */
+ AVS6EYES,
+
+ /* total number of cards */
+ NUM_CARDS
+};
+
+enum zoran_codec_mode {
+ BUZ_MODE_IDLE, /* nothing going on */
+ BUZ_MODE_MOTION_COMPRESS, /* grabbing frames */
+ BUZ_MODE_MOTION_DECOMPRESS, /* playing frames */
+ BUZ_MODE_STILL_COMPRESS, /* still frame conversion */
+ BUZ_MODE_STILL_DECOMPRESS /* still frame conversion */
+};
+
+enum zoran_buffer_state {
+ BUZ_STATE_USER, /* buffer is owned by application */
+ BUZ_STATE_PEND, /* buffer is queued in pend[] ready to feed to I/O */
+ BUZ_STATE_DMA, /* buffer is queued in dma[] for I/O */
+ BUZ_STATE_DONE /* buffer is ready to return to application */
+};
+
+enum zoran_map_mode {
+ ZORAN_MAP_MODE_RAW,
+ ZORAN_MAP_MODE_JPG_REC,
+#define ZORAN_MAP_MODE_JPG ZORAN_MAP_MODE_JPG_REC
+ ZORAN_MAP_MODE_JPG_PLAY,
+};
+
+enum gpio_type {
+ ZR_GPIO_JPEG_SLEEP = 0,
+ ZR_GPIO_JPEG_RESET,
+ ZR_GPIO_JPEG_FRAME,
+ ZR_GPIO_VID_DIR,
+ ZR_GPIO_VID_EN,
+ ZR_GPIO_VID_RESET,
+ ZR_GPIO_CLK_SEL1,
+ ZR_GPIO_CLK_SEL2,
+ ZR_GPIO_MAX,
+};
+
+enum gpcs_type {
+ GPCS_JPEG_RESET = 0,
+ GPCS_JPEG_START,
+ GPCS_MAX,
+};
+
+struct zoran_format {
+ char *name;
+ __u32 fourcc;
+ int colorspace;
+ int depth;
+ __u32 flags;
+ __u32 vfespfr;
+};
+/* flags */
+#define ZORAN_FORMAT_COMPRESSED 1<<0
+#define ZORAN_FORMAT_OVERLAY 1<<1
+#define ZORAN_FORMAT_CAPTURE 1<<2
+#define ZORAN_FORMAT_PLAYBACK 1<<3
+
+/* overlay-settings */
+struct zoran_overlay_settings {
+ int is_set;
+ int x, y, width, height; /* position */
+ int clipcount; /* position and number of clips */
+ const struct zoran_format *format; /* overlay format */
+};
+
+/* v4l-capture settings */
+struct zoran_v4l_settings {
+ int width, height, bytesperline; /* capture size */
+ const struct zoran_format *format; /* capture format */
+};
+
+/* jpg-capture/-playback settings */
+struct zoran_jpg_settings {
+ int decimation; /* this bit is used to set everything to default */
+ int HorDcm, VerDcm, TmpDcm; /* capture decimation settings (TmpDcm=1 means both fields) */
+ int field_per_buff, odd_even; /* field-settings (odd_even=1 (+TmpDcm=1) means top-field-first) */
+ int img_x, img_y, img_width, img_height; /* crop settings (subframe capture) */
+ struct v4l2_jpegcompression jpg_comp; /* JPEG-specific capture settings */
+};
+
+struct zoran_mapping {
+ struct file *file;
+ int count;
+};
+
+struct zoran_buffer {
+ struct zoran_mapping *map;
+ enum zoran_buffer_state state; /* state: unused/pending/dma/done */
+ struct zoran_sync bs; /* DONE: info to return to application */
+ union {
+ struct {
+ __le32 *frag_tab; /* addresses of frag table */
+ u32 frag_tab_bus; /* same value cached to save time in ISR */
+ } jpg;
+ struct {
+ char *fbuffer; /* virtual address of frame buffer */
+ unsigned long fbuffer_phys;/* physical address of frame buffer */
+ unsigned long fbuffer_bus;/* bus address of frame buffer */
+ } v4l;
+ };
+};
+
+enum zoran_lock_activity {
+ ZORAN_FREE, /* free for use */
+ ZORAN_ACTIVE, /* active but unlocked */
+ ZORAN_LOCKED, /* locked */
+};
+
+/* buffer collections */
+struct zoran_buffer_col {
+ enum zoran_lock_activity active; /* feature currently in use? */
+ unsigned int num_buffers, buffer_size;
+ struct zoran_buffer buffer[MAX_FRAME]; /* buffers */
+ u8 allocated; /* Flag if buffers are allocated */
+ u8 need_contiguous; /* Flag if contiguous buffers are needed */
+ /* only applies to jpg buffers, raw buffers are always contiguous */
+};
+
+struct zoran;
+
+/* zoran_fh contains per-open() settings */
+struct zoran_fh {
+ struct zoran *zr;
+
+ enum zoran_map_mode map_mode; /* Flag which bufferset will map by next mmap() */
+
+ struct zoran_overlay_settings overlay_settings;
+ u32 *overlay_mask; /* overlay mask */
+ enum zoran_lock_activity overlay_active;/* feature currently in use? */
+
+ struct zoran_buffer_col buffers; /* buffers' info */
+
+ struct zoran_v4l_settings v4l_settings; /* structure with a lot of things to play with */
+ struct zoran_jpg_settings jpg_settings; /* structure with a lot of things to play with */
+};
+
+struct card_info {
+ enum card_type type;
+ char name[32];
+ const char *i2c_decoder; /* i2c decoder device */
+ const unsigned short *addrs_decoder;
+ const char *i2c_encoder; /* i2c encoder device */
+ const unsigned short *addrs_encoder;
+ u16 video_vfe, video_codec; /* videocodec types */
+ u16 audio_chip; /* audio type */
+
+ int inputs; /* number of video inputs */
+ struct input {
+ int muxsel;
+ char name[32];
+ } input[BUZ_MAX_INPUT];
+
+ v4l2_std_id norms;
+ struct tvnorm *tvn[3]; /* supported TV norms */
+
+ u32 jpeg_int; /* JPEG interrupt */
+ u32 vsync_int; /* VSYNC interrupt */
+ s8 gpio[ZR_GPIO_MAX];
+ u8 gpcs[GPCS_MAX];
+
+ struct vfe_polarity vfe_pol;
+ u8 gpio_pol[ZR_GPIO_MAX];
+
+ /* is the /GWS line connected? */
+ u8 gws_not_connected;
+
+ /* avs6eyes mux setting */
+ u8 input_mux;
+
+ void (*init) (struct zoran * zr);
+};
+
+struct zoran {
+ struct v4l2_device v4l2_dev;
+ struct video_device *video_dev;
+
+ struct i2c_adapter i2c_adapter; /* */
+ struct i2c_algo_bit_data i2c_algo; /* */
+ u32 i2cbr;
+
+ struct v4l2_subdev *decoder; /* video decoder sub-device */
+ struct v4l2_subdev *encoder; /* video encoder sub-device */
+
+ struct videocodec *codec; /* video codec */
+ struct videocodec *vfe; /* video front end */
+
+ struct mutex resource_lock; /* prevent evil stuff */
+ struct mutex other_lock; /* please merge with above */
+
+ u8 initialized; /* flag if zoran has been correctly initialized */
+ int user; /* number of current users */
+ struct card_info card;
+ struct tvnorm *timing;
+
+ unsigned short id; /* number of this device */
+ char name[32]; /* name of this device */
+ struct pci_dev *pci_dev; /* PCI device */
+ unsigned char revision; /* revision of zr36057 */
+ unsigned char __iomem *zr36057_mem;/* pointer to mapped IO memory */
+
+ spinlock_t spinlock; /* Spinlock */
+
+ /* Video for Linux parameters */
+ int input; /* card's norm and input */
+ v4l2_std_id norm;
+
+ /* Current buffer params */
+ void *vbuf_base;
+ int vbuf_height, vbuf_width;
+ int vbuf_depth;
+ int vbuf_bytesperline;
+
+ struct zoran_overlay_settings overlay_settings;
+ u32 *overlay_mask; /* overlay mask */
+ enum zoran_lock_activity overlay_active; /* feature currently in use? */
+
+ wait_queue_head_t v4l_capq;
+
+ int v4l_overlay_active; /* Overlay grab is activated */
+ int v4l_memgrab_active; /* Memory grab is activated */
+
+ int v4l_grab_frame; /* Frame number being currently grabbed */
+#define NO_GRAB_ACTIVE (-1)
+ unsigned long v4l_grab_seq; /* Number of frames grabbed */
+ struct zoran_v4l_settings v4l_settings; /* structure with a lot of things to play with */
+
+ /* V4L grab queue of frames pending */
+ unsigned long v4l_pend_head;
+ unsigned long v4l_pend_tail;
+ unsigned long v4l_sync_tail;
+ int v4l_pend[V4L_MAX_FRAME];
+ struct zoran_buffer_col v4l_buffers; /* V4L buffers' info */
+
+ /* Buz MJPEG parameters */
+ enum zoran_codec_mode codec_mode; /* status of codec */
+ struct zoran_jpg_settings jpg_settings; /* structure with a lot of things to play with */
+
+ wait_queue_head_t jpg_capq; /* wait here for grab to finish */
+
+ /* grab queue counts/indices, mask with BUZ_MASK_STAT_COM before using as index */
+ /* (dma_head - dma_tail) is number active in DMA, must be <= BUZ_NUM_STAT_COM */
+ /* (value & BUZ_MASK_STAT_COM) corresponds to index in stat_com table */
+ unsigned long jpg_que_head; /* Index where to put next buffer which is queued */
+ unsigned long jpg_dma_head; /* Index of next buffer which goes into stat_com */
+ unsigned long jpg_dma_tail; /* Index of last buffer in stat_com */
+ unsigned long jpg_que_tail; /* Index of last buffer in queue */
+ unsigned long jpg_seq_num; /* count of frames since grab/play started */
+ unsigned long jpg_err_seq; /* last seq_num before error */
+ unsigned long jpg_err_shift;
+ unsigned long jpg_queued_num; /* count of frames queued since grab/play started */
+
+ /* zr36057's code buffer table */
+ __le32 *stat_com; /* stat_com[i] is indexed by dma_head/tail & BUZ_MASK_STAT_COM */
+
+ /* (value & BUZ_MASK_FRAME) corresponds to index in pend[] queue */
+ int jpg_pend[BUZ_MAX_FRAME];
+
+ /* array indexed by frame number */
+ struct zoran_buffer_col jpg_buffers; /* MJPEG buffers' info */
+
+ /* Additional stuff for testing */
+#ifdef CONFIG_PROC_FS
+ struct proc_dir_entry *zoran_proc;
+#else
+ void *zoran_proc;
+#endif
+ int testing;
+ int jpeg_error;
+ int intr_counter_GIRQ1;
+ int intr_counter_GIRQ0;
+ int intr_counter_CodRepIRQ;
+ int intr_counter_JPEGRepIRQ;
+ int field_counter;
+ int IRQ1_in;
+ int IRQ1_out;
+ int JPEG_in;
+ int JPEG_out;
+ int JPEG_0;
+ int JPEG_1;
+ int END_event_missed;
+ int JPEG_missed;
+ int JPEG_error;
+ int num_errors;
+ int JPEG_max_missed;
+ int JPEG_min_missed;
+
+ u32 last_isr;
+ unsigned long frame_num;
+
+ wait_queue_head_t test_q;
+};
+
+static inline struct zoran *to_zoran(struct v4l2_device *v4l2_dev)
+{
+ return container_of(v4l2_dev, struct zoran, v4l2_dev);
+}
+
+/* There was something called _ALPHA_BUZ that used the PCI address instead of
+ * the kernel iomapped address for btread/btwrite. */
+#define btwrite(dat,adr) writel((dat), zr->zr36057_mem+(adr))
+#define btread(adr) readl(zr->zr36057_mem+(adr))
+
+#define btand(dat,adr) btwrite((dat) & btread(adr), adr)
+#define btor(dat,adr) btwrite((dat) | btread(adr), adr)
+#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr)
+
+#endif
diff --git a/drivers/media/video/zoran/zoran_card.c b/drivers/media/video/zoran/zoran_card.c
new file mode 100644
index 00000000..c3602d6c
--- /dev/null
+++ b/drivers/media/video/zoran/zoran_card.c
@@ -0,0 +1,1524 @@
+/*
+ * Zoran zr36057/zr36067 PCI controller driver, for the
+ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
+ * Media Labs LML33/LML33R10.
+ *
+ * This part handles card-specific data and detection
+ *
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ *
+ * Currently maintained by:
+ * Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * Laurent Pinchart <laurent.pinchart@skynet.be>
+ * Mailinglist <mjpeg-users@lists.sf.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/delay.h>
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+
+#include <linux/proc_fs.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/videodev2.h>
+#include <linux/spinlock.h>
+#include <linux/sem.h>
+#include <linux/kmod.h>
+#include <linux/wait.h>
+
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <media/v4l2-common.h>
+#include <media/bt819.h>
+
+#include "videocodec.h"
+#include "zoran.h"
+#include "zoran_card.h"
+#include "zoran_device.h"
+#include "zoran_procfs.h"
+
+extern const struct zoran_format zoran_formats[];
+
+static int card[BUZ_MAX] = { [0 ... (BUZ_MAX-1)] = -1 };
+module_param_array(card, int, NULL, 0444);
+MODULE_PARM_DESC(card, "Card type");
+
+/*
+ The video mem address of the video card.
+ The driver has a little database for some videocards
+ to determine it from there. If your video card is not in there
+ you have either to give it to the driver as a parameter
+ or set in in a VIDIOCSFBUF ioctl
+ */
+
+static unsigned long vidmem; /* default = 0 - Video memory base address */
+module_param(vidmem, ulong, 0444);
+MODULE_PARM_DESC(vidmem, "Default video memory base address");
+
+/*
+ Default input and video norm at startup of the driver.
+*/
+
+static unsigned int default_input; /* default 0 = Composite, 1 = S-Video */
+module_param(default_input, uint, 0444);
+MODULE_PARM_DESC(default_input,
+ "Default input (0=Composite, 1=S-Video, 2=Internal)");
+
+static int default_mux = 1; /* 6 Eyes input selection */
+module_param(default_mux, int, 0644);
+MODULE_PARM_DESC(default_mux,
+ "Default 6 Eyes mux setting (Input selection)");
+
+static int default_norm; /* default 0 = PAL, 1 = NTSC 2 = SECAM */
+module_param(default_norm, int, 0444);
+MODULE_PARM_DESC(default_norm, "Default norm (0=PAL, 1=NTSC, 2=SECAM)");
+
+/* /dev/videoN, -1 for autodetect */
+static int video_nr[BUZ_MAX] = { [0 ... (BUZ_MAX-1)] = -1 };
+module_param_array(video_nr, int, NULL, 0444);
+MODULE_PARM_DESC(video_nr, "Video device number (-1=Auto)");
+
+int v4l_nbufs = 4;
+int v4l_bufsize = 864; /* Everybody should be able to work with this setting */
+module_param(v4l_nbufs, int, 0644);
+MODULE_PARM_DESC(v4l_nbufs, "Maximum number of V4L buffers to use");
+module_param(v4l_bufsize, int, 0644);
+MODULE_PARM_DESC(v4l_bufsize, "Maximum size per V4L buffer (in kB)");
+
+int jpg_nbufs = 32;
+int jpg_bufsize = 512; /* max size for 100% quality full-PAL frame */
+module_param(jpg_nbufs, int, 0644);
+MODULE_PARM_DESC(jpg_nbufs, "Maximum number of JPG buffers to use");
+module_param(jpg_bufsize, int, 0644);
+MODULE_PARM_DESC(jpg_bufsize, "Maximum size per JPG buffer (in kB)");
+
+int pass_through = 0; /* 1=Pass through TV signal when device is not used */
+ /* 0=Show color bar when device is not used (LML33: only if lml33dpath=1) */
+module_param(pass_through, int, 0644);
+MODULE_PARM_DESC(pass_through,
+ "Pass TV signal through to TV-out when idling");
+
+int zr36067_debug = 1;
+module_param_named(debug, zr36067_debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-5)");
+
+#define ZORAN_VERSION "0.10.1"
+
+MODULE_DESCRIPTION("Zoran-36057/36067 JPEG codec driver");
+MODULE_AUTHOR("Serguei Miridonov");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(ZORAN_VERSION);
+
+#define ZR_DEVICE(subven, subdev, data) { \
+ .vendor = PCI_VENDOR_ID_ZORAN, .device = PCI_DEVICE_ID_ZORAN_36057, \
+ .subvendor = (subven), .subdevice = (subdev), .driver_data = (data) }
+
+static struct pci_device_id zr36067_pci_tbl[] = {
+ ZR_DEVICE(PCI_VENDOR_ID_MIRO, PCI_DEVICE_ID_MIRO_DC10PLUS, DC10plus),
+ ZR_DEVICE(PCI_VENDOR_ID_MIRO, PCI_DEVICE_ID_MIRO_DC30PLUS, DC30plus),
+ ZR_DEVICE(PCI_VENDOR_ID_ELECTRONICDESIGNGMBH, PCI_DEVICE_ID_LML_33R10, LML33R10),
+ ZR_DEVICE(PCI_VENDOR_ID_IOMEGA, PCI_DEVICE_ID_IOMEGA_BUZ, BUZ),
+ ZR_DEVICE(PCI_ANY_ID, PCI_ANY_ID, NUM_CARDS),
+ {0}
+};
+MODULE_DEVICE_TABLE(pci, zr36067_pci_tbl);
+
+static unsigned int zoran_num; /* number of cards found */
+
+/* videocodec bus functions ZR36060 */
+static u32
+zr36060_read (struct videocodec *codec,
+ u16 reg)
+{
+ struct zoran *zr = (struct zoran *) codec->master_data->data;
+ __u32 data;
+
+ if (post_office_wait(zr)
+ || post_office_write(zr, 0, 1, reg >> 8)
+ || post_office_write(zr, 0, 2, reg & 0xff)) {
+ return -1;
+ }
+
+ data = post_office_read(zr, 0, 3) & 0xff;
+ return data;
+}
+
+static void
+zr36060_write (struct videocodec *codec,
+ u16 reg,
+ u32 val)
+{
+ struct zoran *zr = (struct zoran *) codec->master_data->data;
+
+ if (post_office_wait(zr)
+ || post_office_write(zr, 0, 1, reg >> 8)
+ || post_office_write(zr, 0, 2, reg & 0xff)) {
+ return;
+ }
+
+ post_office_write(zr, 0, 3, val & 0xff);
+}
+
+/* videocodec bus functions ZR36050 */
+static u32
+zr36050_read (struct videocodec *codec,
+ u16 reg)
+{
+ struct zoran *zr = (struct zoran *) codec->master_data->data;
+ __u32 data;
+
+ if (post_office_wait(zr)
+ || post_office_write(zr, 1, 0, reg >> 2)) { // reg. HIGHBYTES
+ return -1;
+ }
+
+ data = post_office_read(zr, 0, reg & 0x03) & 0xff; // reg. LOWBYTES + read
+ return data;
+}
+
+static void
+zr36050_write (struct videocodec *codec,
+ u16 reg,
+ u32 val)
+{
+ struct zoran *zr = (struct zoran *) codec->master_data->data;
+
+ if (post_office_wait(zr)
+ || post_office_write(zr, 1, 0, reg >> 2)) { // reg. HIGHBYTES
+ return;
+ }
+
+ post_office_write(zr, 0, reg & 0x03, val & 0xff); // reg. LOWBYTES + wr. data
+}
+
+/* videocodec bus functions ZR36016 */
+static u32
+zr36016_read (struct videocodec *codec,
+ u16 reg)
+{
+ struct zoran *zr = (struct zoran *) codec->master_data->data;
+ __u32 data;
+
+ if (post_office_wait(zr)) {
+ return -1;
+ }
+
+ data = post_office_read(zr, 2, reg & 0x03) & 0xff; // read
+ return data;
+}
+
+/* hack for in zoran_device.c */
+void
+zr36016_write (struct videocodec *codec,
+ u16 reg,
+ u32 val)
+{
+ struct zoran *zr = (struct zoran *) codec->master_data->data;
+
+ if (post_office_wait(zr)) {
+ return;
+ }
+
+ post_office_write(zr, 2, reg & 0x03, val & 0x0ff); // wr. data
+}
+
+/*
+ * Board specific information
+ */
+
+static void
+dc10_init (struct zoran *zr)
+{
+ dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
+
+ /* Pixel clock selection */
+ GPIO(zr, 4, 0);
+ GPIO(zr, 5, 1);
+ /* Enable the video bus sync signals */
+ GPIO(zr, 7, 0);
+}
+
+static void
+dc10plus_init (struct zoran *zr)
+{
+ dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
+}
+
+static void
+buz_init (struct zoran *zr)
+{
+ dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
+
+ /* some stuff from Iomega */
+ pci_write_config_dword(zr->pci_dev, 0xfc, 0x90680f15);
+ pci_write_config_dword(zr->pci_dev, 0x0c, 0x00012020);
+ pci_write_config_dword(zr->pci_dev, 0xe8, 0xc0200000);
+}
+
+static void
+lml33_init (struct zoran *zr)
+{
+ dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
+
+ GPIO(zr, 2, 1); // Set Composite input/output
+}
+
+static void
+avs6eyes_init (struct zoran *zr)
+{
+ // AverMedia 6-Eyes original driver by Christer Weinigel
+
+ // Lifted straight from Christer's old driver and
+ // modified slightly by Martin Samuelsson.
+
+ int mux = default_mux; /* 1 = BT866, 7 = VID1 */
+
+ GPIO(zr, 4, 1); /* Bt866 SLEEP on */
+ udelay(2);
+
+ GPIO(zr, 0, 1); /* ZR36060 /RESET on */
+ GPIO(zr, 1, 0); /* ZR36060 /SLEEP on */
+ GPIO(zr, 2, mux & 1); /* MUX S0 */
+ GPIO(zr, 3, 0); /* /FRAME on */
+ GPIO(zr, 4, 0); /* Bt866 SLEEP off */
+ GPIO(zr, 5, mux & 2); /* MUX S1 */
+ GPIO(zr, 6, 0); /* ? */
+ GPIO(zr, 7, mux & 4); /* MUX S2 */
+
+}
+
+static char *
+codecid_to_modulename (u16 codecid)
+{
+ char *name = NULL;
+
+ switch (codecid) {
+ case CODEC_TYPE_ZR36060:
+ name = "zr36060";
+ break;
+ case CODEC_TYPE_ZR36050:
+ name = "zr36050";
+ break;
+ case CODEC_TYPE_ZR36016:
+ name = "zr36016";
+ break;
+ }
+
+ return name;
+}
+
+// struct tvnorm {
+// u16 Wt, Wa, HStart, HSyncStart, Ht, Ha, VStart;
+// };
+
+static struct tvnorm f50sqpixel = { 944, 768, 83, 880, 625, 576, 16 };
+static struct tvnorm f60sqpixel = { 780, 640, 51, 716, 525, 480, 12 };
+static struct tvnorm f50ccir601 = { 864, 720, 75, 804, 625, 576, 18 };
+static struct tvnorm f60ccir601 = { 858, 720, 57, 788, 525, 480, 16 };
+
+static struct tvnorm f50ccir601_lml33 = { 864, 720, 75+34, 804, 625, 576, 18 };
+static struct tvnorm f60ccir601_lml33 = { 858, 720, 57+34, 788, 525, 480, 16 };
+
+/* The DC10 (57/16/50) uses VActive as HSync, so HStart must be 0 */
+static struct tvnorm f50sqpixel_dc10 = { 944, 768, 0, 880, 625, 576, 0 };
+static struct tvnorm f60sqpixel_dc10 = { 780, 640, 0, 716, 525, 480, 12 };
+
+/* FIXME: I cannot swap U and V in saa7114, so i do one
+ * pixel left shift in zoran (75 -> 74)
+ * (Maxim Yevtyushkin <max@linuxmedialabs.com>) */
+static struct tvnorm f50ccir601_lm33r10 = { 864, 720, 74+54, 804, 625, 576, 18 };
+static struct tvnorm f60ccir601_lm33r10 = { 858, 720, 56+54, 788, 525, 480, 16 };
+
+/* FIXME: The ks0127 seem incapable of swapping U and V, too, which is why I
+ * copy Maxim's left shift hack for the 6 Eyes.
+ *
+ * Christer's driver used the unshifted norms, though...
+ * /Sam */
+static struct tvnorm f50ccir601_avs6eyes = { 864, 720, 74, 804, 625, 576, 18 };
+static struct tvnorm f60ccir601_avs6eyes = { 858, 720, 56, 788, 525, 480, 16 };
+
+static const unsigned short vpx3220_addrs[] = { 0x43, 0x47, I2C_CLIENT_END };
+static const unsigned short saa7110_addrs[] = { 0x4e, 0x4f, I2C_CLIENT_END };
+static const unsigned short saa7111_addrs[] = { 0x25, 0x24, I2C_CLIENT_END };
+static const unsigned short saa7114_addrs[] = { 0x21, 0x20, I2C_CLIENT_END };
+static const unsigned short adv717x_addrs[] = { 0x6a, 0x6b, 0x2a, 0x2b, I2C_CLIENT_END };
+static const unsigned short ks0127_addrs[] = { 0x6c, 0x6d, I2C_CLIENT_END };
+static const unsigned short saa7185_addrs[] = { 0x44, I2C_CLIENT_END };
+static const unsigned short bt819_addrs[] = { 0x45, I2C_CLIENT_END };
+static const unsigned short bt856_addrs[] = { 0x44, I2C_CLIENT_END };
+static const unsigned short bt866_addrs[] = { 0x44, I2C_CLIENT_END };
+
+static struct card_info zoran_cards[NUM_CARDS] __devinitdata = {
+ {
+ .type = DC10_old,
+ .name = "DC10(old)",
+ .i2c_decoder = "vpx3220a",
+ .addrs_decoder = vpx3220_addrs,
+ .video_codec = CODEC_TYPE_ZR36050,
+ .video_vfe = CODEC_TYPE_ZR36016,
+
+ .inputs = 3,
+ .input = {
+ { 1, "Composite" },
+ { 2, "S-Video" },
+ { 0, "Internal/comp" }
+ },
+ .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
+ .tvn = {
+ &f50sqpixel_dc10,
+ &f60sqpixel_dc10,
+ &f50sqpixel_dc10
+ },
+ .jpeg_int = 0,
+ .vsync_int = ZR36057_ISR_GIRQ1,
+ .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 },
+ .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 },
+ .gpcs = { -1, 0 },
+ .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
+ .gws_not_connected = 0,
+ .input_mux = 0,
+ .init = &dc10_init,
+ }, {
+ .type = DC10_new,
+ .name = "DC10(new)",
+ .i2c_decoder = "saa7110",
+ .addrs_decoder = saa7110_addrs,
+ .i2c_encoder = "adv7175",
+ .addrs_encoder = adv717x_addrs,
+ .video_codec = CODEC_TYPE_ZR36060,
+
+ .inputs = 3,
+ .input = {
+ { 0, "Composite" },
+ { 7, "S-Video" },
+ { 5, "Internal/comp" }
+ },
+ .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
+ .tvn = {
+ &f50sqpixel,
+ &f60sqpixel,
+ &f50sqpixel},
+ .jpeg_int = ZR36057_ISR_GIRQ0,
+ .vsync_int = ZR36057_ISR_GIRQ1,
+ .gpio = { 3, 0, 6, 1, 2, -1, 4, 5 },
+ .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
+ .gpcs = { -1, 1},
+ .vfe_pol = { 1, 1, 1, 1, 0, 0, 0, 0 },
+ .gws_not_connected = 0,
+ .input_mux = 0,
+ .init = &dc10plus_init,
+ }, {
+ .type = DC10plus,
+ .name = "DC10plus",
+ .i2c_decoder = "saa7110",
+ .addrs_decoder = saa7110_addrs,
+ .i2c_encoder = "adv7175",
+ .addrs_encoder = adv717x_addrs,
+ .video_codec = CODEC_TYPE_ZR36060,
+
+ .inputs = 3,
+ .input = {
+ { 0, "Composite" },
+ { 7, "S-Video" },
+ { 5, "Internal/comp" }
+ },
+ .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
+ .tvn = {
+ &f50sqpixel,
+ &f60sqpixel,
+ &f50sqpixel
+ },
+ .jpeg_int = ZR36057_ISR_GIRQ0,
+ .vsync_int = ZR36057_ISR_GIRQ1,
+ .gpio = { 3, 0, 6, 1, 2, -1, 4, 5 },
+ .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
+ .gpcs = { -1, 1 },
+ .vfe_pol = { 1, 1, 1, 1, 0, 0, 0, 0 },
+ .gws_not_connected = 0,
+ .input_mux = 0,
+ .init = &dc10plus_init,
+ }, {
+ .type = DC30,
+ .name = "DC30",
+ .i2c_decoder = "vpx3220a",
+ .addrs_decoder = vpx3220_addrs,
+ .i2c_encoder = "adv7175",
+ .addrs_encoder = adv717x_addrs,
+ .video_codec = CODEC_TYPE_ZR36050,
+ .video_vfe = CODEC_TYPE_ZR36016,
+
+ .inputs = 3,
+ .input = {
+ { 1, "Composite" },
+ { 2, "S-Video" },
+ { 0, "Internal/comp" }
+ },
+ .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
+ .tvn = {
+ &f50sqpixel_dc10,
+ &f60sqpixel_dc10,
+ &f50sqpixel_dc10
+ },
+ .jpeg_int = 0,
+ .vsync_int = ZR36057_ISR_GIRQ1,
+ .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 },
+ .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 },
+ .gpcs = { -1, 0 },
+ .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
+ .gws_not_connected = 0,
+ .input_mux = 0,
+ .init = &dc10_init,
+ }, {
+ .type = DC30plus,
+ .name = "DC30plus",
+ .i2c_decoder = "vpx3220a",
+ .addrs_decoder = vpx3220_addrs,
+ .i2c_encoder = "adv7175",
+ .addrs_encoder = adv717x_addrs,
+ .video_codec = CODEC_TYPE_ZR36050,
+ .video_vfe = CODEC_TYPE_ZR36016,
+
+ .inputs = 3,
+ .input = {
+ { 1, "Composite" },
+ { 2, "S-Video" },
+ { 0, "Internal/comp" }
+ },
+ .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
+ .tvn = {
+ &f50sqpixel_dc10,
+ &f60sqpixel_dc10,
+ &f50sqpixel_dc10
+ },
+ .jpeg_int = 0,
+ .vsync_int = ZR36057_ISR_GIRQ1,
+ .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 },
+ .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 },
+ .gpcs = { -1, 0 },
+ .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
+ .gws_not_connected = 0,
+ .input_mux = 0,
+ .init = &dc10_init,
+ }, {
+ .type = LML33,
+ .name = "LML33",
+ .i2c_decoder = "bt819a",
+ .addrs_decoder = bt819_addrs,
+ .i2c_encoder = "bt856",
+ .addrs_encoder = bt856_addrs,
+ .video_codec = CODEC_TYPE_ZR36060,
+
+ .inputs = 2,
+ .input = {
+ { 0, "Composite" },
+ { 7, "S-Video" }
+ },
+ .norms = V4L2_STD_NTSC|V4L2_STD_PAL,
+ .tvn = {
+ &f50ccir601_lml33,
+ &f60ccir601_lml33,
+ NULL
+ },
+ .jpeg_int = ZR36057_ISR_GIRQ1,
+ .vsync_int = ZR36057_ISR_GIRQ0,
+ .gpio = { 1, -1, 3, 5, 7, -1, -1, -1 },
+ .gpio_pol = { 0, 0, 0, 0, 1, 0, 0, 0 },
+ .gpcs = { 3, 1 },
+ .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 },
+ .gws_not_connected = 1,
+ .input_mux = 0,
+ .init = &lml33_init,
+ }, {
+ .type = LML33R10,
+ .name = "LML33R10",
+ .i2c_decoder = "saa7114",
+ .addrs_decoder = saa7114_addrs,
+ .i2c_encoder = "adv7170",
+ .addrs_encoder = adv717x_addrs,
+ .video_codec = CODEC_TYPE_ZR36060,
+
+ .inputs = 2,
+ .input = {
+ { 0, "Composite" },
+ { 7, "S-Video" }
+ },
+ .norms = V4L2_STD_NTSC|V4L2_STD_PAL,
+ .tvn = {
+ &f50ccir601_lm33r10,
+ &f60ccir601_lm33r10,
+ NULL
+ },
+ .jpeg_int = ZR36057_ISR_GIRQ1,
+ .vsync_int = ZR36057_ISR_GIRQ0,
+ .gpio = { 1, -1, 3, 5, 7, -1, -1, -1 },
+ .gpio_pol = { 0, 0, 0, 0, 1, 0, 0, 0 },
+ .gpcs = { 3, 1 },
+ .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 },
+ .gws_not_connected = 1,
+ .input_mux = 0,
+ .init = &lml33_init,
+ }, {
+ .type = BUZ,
+ .name = "Buz",
+ .i2c_decoder = "saa7111",
+ .addrs_decoder = saa7111_addrs,
+ .i2c_encoder = "saa7185",
+ .addrs_encoder = saa7185_addrs,
+ .video_codec = CODEC_TYPE_ZR36060,
+
+ .inputs = 2,
+ .input = {
+ { 3, "Composite" },
+ { 7, "S-Video" }
+ },
+ .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
+ .tvn = {
+ &f50ccir601,
+ &f60ccir601,
+ &f50ccir601
+ },
+ .jpeg_int = ZR36057_ISR_GIRQ1,
+ .vsync_int = ZR36057_ISR_GIRQ0,
+ .gpio = { 1, -1, 3, -1, -1, -1, -1, -1 },
+ .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
+ .gpcs = { 3, 1 },
+ .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 },
+ .gws_not_connected = 1,
+ .input_mux = 0,
+ .init = &buz_init,
+ }, {
+ .type = AVS6EYES,
+ .name = "6-Eyes",
+ /* AverMedia chose not to brand the 6-Eyes. Thus it
+ can't be autodetected, and requires card=x. */
+ .i2c_decoder = "ks0127",
+ .addrs_decoder = ks0127_addrs,
+ .i2c_encoder = "bt866",
+ .addrs_encoder = bt866_addrs,
+ .video_codec = CODEC_TYPE_ZR36060,
+
+ .inputs = 10,
+ .input = {
+ { 0, "Composite 1" },
+ { 1, "Composite 2" },
+ { 2, "Composite 3" },
+ { 4, "Composite 4" },
+ { 5, "Composite 5" },
+ { 6, "Composite 6" },
+ { 8, "S-Video 1" },
+ { 9, "S-Video 2" },
+ {10, "S-Video 3" },
+ {15, "YCbCr" }
+ },
+ .norms = V4L2_STD_NTSC|V4L2_STD_PAL,
+ .tvn = {
+ &f50ccir601_avs6eyes,
+ &f60ccir601_avs6eyes,
+ NULL
+ },
+ .jpeg_int = ZR36057_ISR_GIRQ1,
+ .vsync_int = ZR36057_ISR_GIRQ0,
+ .gpio = { 1, 0, 3, -1, -1, -1, -1, -1 },// Validity unknown /Sam
+ .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, // Validity unknown /Sam
+ .gpcs = { 3, 1 }, // Validity unknown /Sam
+ .vfe_pol = { 1, 0, 0, 0, 0, 1, 0, 0 }, // Validity unknown /Sam
+ .gws_not_connected = 1,
+ .input_mux = 1,
+ .init = &avs6eyes_init,
+ }
+
+};
+
+/*
+ * I2C functions
+ */
+/* software I2C functions */
+static int
+zoran_i2c_getsda (void *data)
+{
+ struct zoran *zr = (struct zoran *) data;
+
+ return (btread(ZR36057_I2CBR) >> 1) & 1;
+}
+
+static int
+zoran_i2c_getscl (void *data)
+{
+ struct zoran *zr = (struct zoran *) data;
+
+ return btread(ZR36057_I2CBR) & 1;
+}
+
+static void
+zoran_i2c_setsda (void *data,
+ int state)
+{
+ struct zoran *zr = (struct zoran *) data;
+
+ if (state)
+ zr->i2cbr |= 2;
+ else
+ zr->i2cbr &= ~2;
+ btwrite(zr->i2cbr, ZR36057_I2CBR);
+}
+
+static void
+zoran_i2c_setscl (void *data,
+ int state)
+{
+ struct zoran *zr = (struct zoran *) data;
+
+ if (state)
+ zr->i2cbr |= 1;
+ else
+ zr->i2cbr &= ~1;
+ btwrite(zr->i2cbr, ZR36057_I2CBR);
+}
+
+static const struct i2c_algo_bit_data zoran_i2c_bit_data_template = {
+ .setsda = zoran_i2c_setsda,
+ .setscl = zoran_i2c_setscl,
+ .getsda = zoran_i2c_getsda,
+ .getscl = zoran_i2c_getscl,
+ .udelay = 10,
+ .timeout = 100,
+};
+
+static int
+zoran_register_i2c (struct zoran *zr)
+{
+ memcpy(&zr->i2c_algo, &zoran_i2c_bit_data_template,
+ sizeof(struct i2c_algo_bit_data));
+ zr->i2c_algo.data = zr;
+ strlcpy(zr->i2c_adapter.name, ZR_DEVNAME(zr),
+ sizeof(zr->i2c_adapter.name));
+ i2c_set_adapdata(&zr->i2c_adapter, &zr->v4l2_dev);
+ zr->i2c_adapter.algo_data = &zr->i2c_algo;
+ zr->i2c_adapter.dev.parent = &zr->pci_dev->dev;
+ return i2c_bit_add_bus(&zr->i2c_adapter);
+}
+
+static void
+zoran_unregister_i2c (struct zoran *zr)
+{
+ i2c_del_adapter(&zr->i2c_adapter);
+}
+
+/* Check a zoran_params struct for correctness, insert default params */
+
+int
+zoran_check_jpg_settings (struct zoran *zr,
+ struct zoran_jpg_settings *settings,
+ int try)
+{
+ int err = 0, err0 = 0;
+
+ dprintk(4,
+ KERN_DEBUG
+ "%s: %s - dec: %d, Hdcm: %d, Vdcm: %d, Tdcm: %d\n",
+ ZR_DEVNAME(zr), __func__, settings->decimation, settings->HorDcm,
+ settings->VerDcm, settings->TmpDcm);
+ dprintk(4,
+ KERN_DEBUG
+ "%s: %s - x: %d, y: %d, w: %d, y: %d\n",
+ ZR_DEVNAME(zr), __func__, settings->img_x, settings->img_y,
+ settings->img_width, settings->img_height);
+ /* Check decimation, set default values for decimation = 1, 2, 4 */
+ switch (settings->decimation) {
+ case 1:
+
+ settings->HorDcm = 1;
+ settings->VerDcm = 1;
+ settings->TmpDcm = 1;
+ settings->field_per_buff = 2;
+ settings->img_x = 0;
+ settings->img_y = 0;
+ settings->img_width = BUZ_MAX_WIDTH;
+ settings->img_height = BUZ_MAX_HEIGHT / 2;
+ break;
+ case 2:
+
+ settings->HorDcm = 2;
+ settings->VerDcm = 1;
+ settings->TmpDcm = 2;
+ settings->field_per_buff = 1;
+ settings->img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0;
+ settings->img_y = 0;
+ settings->img_width =
+ (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH;
+ settings->img_height = BUZ_MAX_HEIGHT / 2;
+ break;
+ case 4:
+
+ if (zr->card.type == DC10_new) {
+ dprintk(1,
+ KERN_DEBUG
+ "%s: %s - HDec by 4 is not supported on the DC10\n",
+ ZR_DEVNAME(zr), __func__);
+ err0++;
+ break;
+ }
+
+ settings->HorDcm = 4;
+ settings->VerDcm = 2;
+ settings->TmpDcm = 2;
+ settings->field_per_buff = 1;
+ settings->img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0;
+ settings->img_y = 0;
+ settings->img_width =
+ (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH;
+ settings->img_height = BUZ_MAX_HEIGHT / 2;
+ break;
+ case 0:
+
+ /* We have to check the data the user has set */
+
+ if (settings->HorDcm != 1 && settings->HorDcm != 2 &&
+ (zr->card.type == DC10_new || settings->HorDcm != 4)) {
+ settings->HorDcm = clamp(settings->HorDcm, 1, 2);
+ err0++;
+ }
+ if (settings->VerDcm != 1 && settings->VerDcm != 2) {
+ settings->VerDcm = clamp(settings->VerDcm, 1, 2);
+ err0++;
+ }
+ if (settings->TmpDcm != 1 && settings->TmpDcm != 2) {
+ settings->TmpDcm = clamp(settings->TmpDcm, 1, 2);
+ err0++;
+ }
+ if (settings->field_per_buff != 1 &&
+ settings->field_per_buff != 2) {
+ settings->field_per_buff = clamp(settings->field_per_buff, 1, 2);
+ err0++;
+ }
+ if (settings->img_x < 0) {
+ settings->img_x = 0;
+ err0++;
+ }
+ if (settings->img_y < 0) {
+ settings->img_y = 0;
+ err0++;
+ }
+ if (settings->img_width < 0 || settings->img_width > BUZ_MAX_WIDTH) {
+ settings->img_width = clamp(settings->img_width, 0, (int)BUZ_MAX_WIDTH);
+ err0++;
+ }
+ if (settings->img_height < 0 || settings->img_height > BUZ_MAX_HEIGHT / 2) {
+ settings->img_height = clamp(settings->img_height, 0, BUZ_MAX_HEIGHT / 2);
+ err0++;
+ }
+ if (settings->img_x + settings->img_width > BUZ_MAX_WIDTH) {
+ settings->img_x = BUZ_MAX_WIDTH - settings->img_width;
+ err0++;
+ }
+ if (settings->img_y + settings->img_height > BUZ_MAX_HEIGHT / 2) {
+ settings->img_y = BUZ_MAX_HEIGHT / 2 - settings->img_height;
+ err0++;
+ }
+ if (settings->img_width % (16 * settings->HorDcm) != 0) {
+ settings->img_width -= settings->img_width % (16 * settings->HorDcm);
+ if (settings->img_width == 0)
+ settings->img_width = 16 * settings->HorDcm;
+ err0++;
+ }
+ if (settings->img_height % (8 * settings->VerDcm) != 0) {
+ settings->img_height -= settings->img_height % (8 * settings->VerDcm);
+ if (settings->img_height == 0)
+ settings->img_height = 8 * settings->VerDcm;
+ err0++;
+ }
+
+ if (!try && err0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - error in params for decimation = 0\n",
+ ZR_DEVNAME(zr), __func__);
+ err++;
+ }
+ break;
+ default:
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - decimation = %d, must be 0, 1, 2 or 4\n",
+ ZR_DEVNAME(zr), __func__, settings->decimation);
+ err++;
+ break;
+ }
+
+ if (settings->jpg_comp.quality > 100)
+ settings->jpg_comp.quality = 100;
+ if (settings->jpg_comp.quality < 5)
+ settings->jpg_comp.quality = 5;
+ if (settings->jpg_comp.APPn < 0)
+ settings->jpg_comp.APPn = 0;
+ if (settings->jpg_comp.APPn > 15)
+ settings->jpg_comp.APPn = 15;
+ if (settings->jpg_comp.APP_len < 0)
+ settings->jpg_comp.APP_len = 0;
+ if (settings->jpg_comp.APP_len > 60)
+ settings->jpg_comp.APP_len = 60;
+ if (settings->jpg_comp.COM_len < 0)
+ settings->jpg_comp.COM_len = 0;
+ if (settings->jpg_comp.COM_len > 60)
+ settings->jpg_comp.COM_len = 60;
+ if (err)
+ return -EINVAL;
+ return 0;
+}
+
+void
+zoran_open_init_params (struct zoran *zr)
+{
+ int i;
+
+ /* User must explicitly set a window */
+ zr->overlay_settings.is_set = 0;
+ zr->overlay_mask = NULL;
+ zr->overlay_active = ZORAN_FREE;
+
+ zr->v4l_memgrab_active = 0;
+ zr->v4l_overlay_active = 0;
+ zr->v4l_grab_frame = NO_GRAB_ACTIVE;
+ zr->v4l_grab_seq = 0;
+ zr->v4l_settings.width = 192;
+ zr->v4l_settings.height = 144;
+ zr->v4l_settings.format = &zoran_formats[7]; /* YUY2 - YUV-4:2:2 packed */
+ zr->v4l_settings.bytesperline =
+ zr->v4l_settings.width *
+ ((zr->v4l_settings.format->depth + 7) / 8);
+
+ /* DMA ring stuff for V4L */
+ zr->v4l_pend_tail = 0;
+ zr->v4l_pend_head = 0;
+ zr->v4l_sync_tail = 0;
+ zr->v4l_buffers.active = ZORAN_FREE;
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ zr->v4l_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */
+ }
+ zr->v4l_buffers.allocated = 0;
+
+ for (i = 0; i < BUZ_MAX_FRAME; i++) {
+ zr->jpg_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */
+ }
+ zr->jpg_buffers.active = ZORAN_FREE;
+ zr->jpg_buffers.allocated = 0;
+ /* Set necessary params and call zoran_check_jpg_settings to set the defaults */
+ zr->jpg_settings.decimation = 1;
+ zr->jpg_settings.jpg_comp.quality = 50; /* default compression factor 8 */
+ if (zr->card.type != BUZ)
+ zr->jpg_settings.odd_even = 1;
+ else
+ zr->jpg_settings.odd_even = 0;
+ zr->jpg_settings.jpg_comp.APPn = 0;
+ zr->jpg_settings.jpg_comp.APP_len = 0; /* No APPn marker */
+ memset(zr->jpg_settings.jpg_comp.APP_data, 0,
+ sizeof(zr->jpg_settings.jpg_comp.APP_data));
+ zr->jpg_settings.jpg_comp.COM_len = 0; /* No COM marker */
+ memset(zr->jpg_settings.jpg_comp.COM_data, 0,
+ sizeof(zr->jpg_settings.jpg_comp.COM_data));
+ zr->jpg_settings.jpg_comp.jpeg_markers =
+ V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT;
+ i = zoran_check_jpg_settings(zr, &zr->jpg_settings, 0);
+ if (i)
+ dprintk(1, KERN_ERR "%s: %s internal error\n",
+ ZR_DEVNAME(zr), __func__);
+
+ clear_interrupt_counters(zr);
+ zr->testing = 0;
+}
+
+static void __devinit
+test_interrupts (struct zoran *zr)
+{
+ DEFINE_WAIT(wait);
+ int timeout, icr;
+
+ clear_interrupt_counters(zr);
+
+ zr->testing = 1;
+ icr = btread(ZR36057_ICR);
+ btwrite(0x78000000 | ZR36057_ICR_IntPinEn, ZR36057_ICR);
+ prepare_to_wait(&zr->test_q, &wait, TASK_INTERRUPTIBLE);
+ timeout = schedule_timeout(HZ);
+ finish_wait(&zr->test_q, &wait);
+ btwrite(0, ZR36057_ICR);
+ btwrite(0x78000000, ZR36057_ISR);
+ zr->testing = 0;
+ dprintk(5, KERN_INFO "%s: Testing interrupts...\n", ZR_DEVNAME(zr));
+ if (timeout) {
+ dprintk(1, ": time spent: %d\n", 1 * HZ - timeout);
+ }
+ if (zr36067_debug > 1)
+ print_interrupts(zr);
+ btwrite(icr, ZR36057_ICR);
+}
+
+static int __devinit
+zr36057_init (struct zoran *zr)
+{
+ int j, err;
+
+ dprintk(1,
+ KERN_INFO
+ "%s: %s - initializing card[%d], zr=%p\n",
+ ZR_DEVNAME(zr), __func__, zr->id, zr);
+
+ /* default setup of all parameters which will persist between opens */
+ zr->user = 0;
+
+ init_waitqueue_head(&zr->v4l_capq);
+ init_waitqueue_head(&zr->jpg_capq);
+ init_waitqueue_head(&zr->test_q);
+ zr->jpg_buffers.allocated = 0;
+ zr->v4l_buffers.allocated = 0;
+
+ zr->vbuf_base = (void *) vidmem;
+ zr->vbuf_width = 0;
+ zr->vbuf_height = 0;
+ zr->vbuf_depth = 0;
+ zr->vbuf_bytesperline = 0;
+
+ /* Avoid nonsense settings from user for default input/norm */
+ if (default_norm < 0 || default_norm > 2)
+ default_norm = 0;
+ if (default_norm == 0) {
+ zr->norm = V4L2_STD_PAL;
+ zr->timing = zr->card.tvn[0];
+ } else if (default_norm == 1) {
+ zr->norm = V4L2_STD_NTSC;
+ zr->timing = zr->card.tvn[1];
+ } else {
+ zr->norm = V4L2_STD_SECAM;
+ zr->timing = zr->card.tvn[2];
+ }
+ if (zr->timing == NULL) {
+ dprintk(1,
+ KERN_WARNING
+ "%s: %s - default TV standard not supported by hardware. PAL will be used.\n",
+ ZR_DEVNAME(zr), __func__);
+ zr->norm = V4L2_STD_PAL;
+ zr->timing = zr->card.tvn[0];
+ }
+
+ if (default_input > zr->card.inputs-1) {
+ dprintk(1,
+ KERN_WARNING
+ "%s: default_input value %d out of range (0-%d)\n",
+ ZR_DEVNAME(zr), default_input, zr->card.inputs-1);
+ default_input = 0;
+ }
+ zr->input = default_input;
+
+ /* default setup (will be repeated at every open) */
+ zoran_open_init_params(zr);
+
+ /* allocate memory *before* doing anything to the hardware
+ * in case allocation fails */
+ zr->stat_com = kzalloc(BUZ_NUM_STAT_COM * 4, GFP_KERNEL);
+ zr->video_dev = video_device_alloc();
+ if (!zr->stat_com || !zr->video_dev) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - kmalloc (STAT_COM) failed\n",
+ ZR_DEVNAME(zr), __func__);
+ err = -ENOMEM;
+ goto exit_free;
+ }
+ for (j = 0; j < BUZ_NUM_STAT_COM; j++) {
+ zr->stat_com[j] = cpu_to_le32(1); /* mark as unavailable to zr36057 */
+ }
+
+ /*
+ * Now add the template and register the device unit.
+ */
+ memcpy(zr->video_dev, &zoran_template, sizeof(zoran_template));
+ zr->video_dev->parent = &zr->pci_dev->dev;
+ strcpy(zr->video_dev->name, ZR_DEVNAME(zr));
+ err = video_register_device(zr->video_dev, VFL_TYPE_GRABBER, video_nr[zr->id]);
+ if (err < 0)
+ goto exit_free;
+ video_set_drvdata(zr->video_dev, zr);
+
+ zoran_init_hardware(zr);
+ if (zr36067_debug > 2)
+ detect_guest_activity(zr);
+ test_interrupts(zr);
+ if (!pass_through) {
+ decoder_call(zr, video, s_stream, 0);
+ encoder_call(zr, video, s_routing, 2, 0, 0);
+ }
+
+ zr->zoran_proc = NULL;
+ zr->initialized = 1;
+ return 0;
+
+exit_free:
+ kfree(zr->stat_com);
+ kfree(zr->video_dev);
+ return err;
+}
+
+static void __devexit zoran_remove(struct pci_dev *pdev)
+{
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
+ struct zoran *zr = to_zoran(v4l2_dev);
+
+ if (!zr->initialized)
+ goto exit_free;
+
+ /* unregister videocodec bus */
+ if (zr->codec) {
+ struct videocodec_master *master = zr->codec->master_data;
+
+ videocodec_detach(zr->codec);
+ kfree(master);
+ }
+ if (zr->vfe) {
+ struct videocodec_master *master = zr->vfe->master_data;
+
+ videocodec_detach(zr->vfe);
+ kfree(master);
+ }
+
+ /* unregister i2c bus */
+ zoran_unregister_i2c(zr);
+ /* disable PCI bus-mastering */
+ zoran_set_pci_master(zr, 0);
+ /* put chip into reset */
+ btwrite(0, ZR36057_SPGPPCR);
+ free_irq(zr->pci_dev->irq, zr);
+ /* unmap and free memory */
+ kfree(zr->stat_com);
+ zoran_proc_cleanup(zr);
+ iounmap(zr->zr36057_mem);
+ pci_disable_device(zr->pci_dev);
+ video_unregister_device(zr->video_dev);
+exit_free:
+ v4l2_device_unregister(&zr->v4l2_dev);
+ kfree(zr);
+}
+
+void
+zoran_vdev_release (struct video_device *vdev)
+{
+ kfree(vdev);
+}
+
+static struct videocodec_master * __devinit
+zoran_setup_videocodec (struct zoran *zr,
+ int type)
+{
+ struct videocodec_master *m = NULL;
+
+ m = kmalloc(sizeof(struct videocodec_master), GFP_KERNEL);
+ if (!m) {
+ dprintk(1, KERN_ERR "%s: %s - no memory\n",
+ ZR_DEVNAME(zr), __func__);
+ return m;
+ }
+
+ /* magic and type are unused for master struct. Makes sense only at
+ codec structs.
+ In the past, .type were initialized to the old V4L1 .hardware
+ value, as VID_HARDWARE_ZR36067
+ */
+ m->magic = 0L;
+ m->type = 0;
+
+ m->flags = CODEC_FLAG_ENCODER | CODEC_FLAG_DECODER;
+ strlcpy(m->name, ZR_DEVNAME(zr), sizeof(m->name));
+ m->data = zr;
+
+ switch (type)
+ {
+ case CODEC_TYPE_ZR36060:
+ m->readreg = zr36060_read;
+ m->writereg = zr36060_write;
+ m->flags |= CODEC_FLAG_JPEG | CODEC_FLAG_VFE;
+ break;
+ case CODEC_TYPE_ZR36050:
+ m->readreg = zr36050_read;
+ m->writereg = zr36050_write;
+ m->flags |= CODEC_FLAG_JPEG;
+ break;
+ case CODEC_TYPE_ZR36016:
+ m->readreg = zr36016_read;
+ m->writereg = zr36016_write;
+ m->flags |= CODEC_FLAG_VFE;
+ break;
+ }
+
+ return m;
+}
+
+static void zoran_subdev_notify(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+ struct zoran *zr = to_zoran(sd->v4l2_dev);
+
+ /* Bt819 needs to reset its FIFO buffer using #FRST pin and
+ LML33 card uses GPIO(7) for that. */
+ if (cmd == BT819_FIFO_RESET_LOW)
+ GPIO(zr, 7, 0);
+ else if (cmd == BT819_FIFO_RESET_HIGH)
+ GPIO(zr, 7, 1);
+}
+
+/*
+ * Scan for a Buz card (actually for the PCI controller ZR36057),
+ * request the irq and map the io memory
+ */
+static int __devinit zoran_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ unsigned char latency, need_latency;
+ struct zoran *zr;
+ int result;
+ struct videocodec_master *master_vfe = NULL;
+ struct videocodec_master *master_codec = NULL;
+ int card_num;
+ char *codec_name, *vfe_name;
+ unsigned int nr;
+
+
+ nr = zoran_num++;
+ if (nr >= BUZ_MAX) {
+ dprintk(1, KERN_ERR "%s: driver limited to %d card(s) maximum\n",
+ ZORAN_NAME, BUZ_MAX);
+ return -ENOENT;
+ }
+
+ zr = kzalloc(sizeof(struct zoran), GFP_KERNEL);
+ if (!zr) {
+ dprintk(1, KERN_ERR "%s: %s - kzalloc failed\n",
+ ZORAN_NAME, __func__);
+ return -ENOMEM;
+ }
+ zr->v4l2_dev.notify = zoran_subdev_notify;
+ if (v4l2_device_register(&pdev->dev, &zr->v4l2_dev))
+ goto zr_free_mem;
+ zr->pci_dev = pdev;
+ zr->id = nr;
+ snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), "MJPEG[%u]", zr->id);
+ spin_lock_init(&zr->spinlock);
+ mutex_init(&zr->resource_lock);
+ mutex_init(&zr->other_lock);
+ if (pci_enable_device(pdev))
+ goto zr_unreg;
+ zr->revision = zr->pci_dev->revision;
+
+ dprintk(1,
+ KERN_INFO
+ "%s: Zoran ZR360%c7 (rev %d), irq: %d, memory: 0x%08llx\n",
+ ZR_DEVNAME(zr), zr->revision < 2 ? '5' : '6', zr->revision,
+ zr->pci_dev->irq, (uint64_t)pci_resource_start(zr->pci_dev, 0));
+ if (zr->revision >= 2) {
+ dprintk(1,
+ KERN_INFO
+ "%s: Subsystem vendor=0x%04x id=0x%04x\n",
+ ZR_DEVNAME(zr), zr->pci_dev->subsystem_vendor,
+ zr->pci_dev->subsystem_device);
+ }
+
+ /* Use auto-detected card type? */
+ if (card[nr] == -1) {
+ if (zr->revision < 2) {
+ dprintk(1,
+ KERN_ERR
+ "%s: No card type specified, please use the card=X module parameter\n",
+ ZR_DEVNAME(zr));
+ dprintk(1,
+ KERN_ERR
+ "%s: It is not possible to auto-detect ZR36057 based cards\n",
+ ZR_DEVNAME(zr));
+ goto zr_unreg;
+ }
+
+ card_num = ent->driver_data;
+ if (card_num >= NUM_CARDS) {
+ dprintk(1,
+ KERN_ERR
+ "%s: Unknown card, try specifying card=X module parameter\n",
+ ZR_DEVNAME(zr));
+ goto zr_unreg;
+ }
+ dprintk(3,
+ KERN_DEBUG
+ "%s: %s() - card %s detected\n",
+ ZR_DEVNAME(zr), __func__, zoran_cards[card_num].name);
+ } else {
+ card_num = card[nr];
+ if (card_num >= NUM_CARDS || card_num < 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: User specified card type %d out of range (0 .. %d)\n",
+ ZR_DEVNAME(zr), card_num, NUM_CARDS - 1);
+ goto zr_unreg;
+ }
+ }
+
+ /* even though we make this a non pointer and thus
+ * theoretically allow for making changes to this struct
+ * on a per-individual card basis at runtime, this is
+ * strongly discouraged. This structure is intended to
+ * keep general card information, no settings or anything */
+ zr->card = zoran_cards[card_num];
+ snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)),
+ "%s[%u]", zr->card.name, zr->id);
+
+ zr->zr36057_mem = pci_ioremap_bar(zr->pci_dev, 0);
+ if (!zr->zr36057_mem) {
+ dprintk(1, KERN_ERR "%s: %s() - ioremap failed\n",
+ ZR_DEVNAME(zr), __func__);
+ goto zr_unreg;
+ }
+
+ result = request_irq(zr->pci_dev->irq, zoran_irq,
+ IRQF_SHARED | IRQF_DISABLED, ZR_DEVNAME(zr), zr);
+ if (result < 0) {
+ if (result == -EINVAL) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - bad irq number or handler\n",
+ ZR_DEVNAME(zr), __func__);
+ } else if (result == -EBUSY) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - IRQ %d busy, change your PnP config in BIOS\n",
+ ZR_DEVNAME(zr), __func__, zr->pci_dev->irq);
+ } else {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - can't assign irq, error code %d\n",
+ ZR_DEVNAME(zr), __func__, result);
+ }
+ goto zr_unmap;
+ }
+
+ /* set PCI latency timer */
+ pci_read_config_byte(zr->pci_dev, PCI_LATENCY_TIMER,
+ &latency);
+ need_latency = zr->revision > 1 ? 32 : 48;
+ if (latency != need_latency) {
+ dprintk(2, KERN_INFO "%s: Changing PCI latency from %d to %d\n",
+ ZR_DEVNAME(zr), latency, need_latency);
+ pci_write_config_byte(zr->pci_dev, PCI_LATENCY_TIMER,
+ need_latency);
+ }
+
+ zr36057_restart(zr);
+ /* i2c */
+ dprintk(2, KERN_INFO "%s: Initializing i2c bus...\n",
+ ZR_DEVNAME(zr));
+
+ if (zoran_register_i2c(zr) < 0) {
+ dprintk(1, KERN_ERR "%s: %s - can't initialize i2c bus\n",
+ ZR_DEVNAME(zr), __func__);
+ goto zr_free_irq;
+ }
+
+ zr->decoder = v4l2_i2c_new_subdev(&zr->v4l2_dev,
+ &zr->i2c_adapter, zr->card.i2c_decoder,
+ 0, zr->card.addrs_decoder);
+
+ if (zr->card.i2c_encoder)
+ zr->encoder = v4l2_i2c_new_subdev(&zr->v4l2_dev,
+ &zr->i2c_adapter, zr->card.i2c_encoder,
+ 0, zr->card.addrs_encoder);
+
+ dprintk(2,
+ KERN_INFO "%s: Initializing videocodec bus...\n",
+ ZR_DEVNAME(zr));
+
+ if (zr->card.video_codec) {
+ codec_name = codecid_to_modulename(zr->card.video_codec);
+ if (codec_name) {
+ result = request_module(codec_name);
+ if (result) {
+ dprintk(1,
+ KERN_ERR
+ "%s: failed to load modules %s: %d\n",
+ ZR_DEVNAME(zr), codec_name, result);
+ }
+ }
+ }
+ if (zr->card.video_vfe) {
+ vfe_name = codecid_to_modulename(zr->card.video_vfe);
+ if (vfe_name) {
+ result = request_module(vfe_name);
+ if (result < 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: failed to load modules %s: %d\n",
+ ZR_DEVNAME(zr), vfe_name, result);
+ }
+ }
+ }
+
+ /* reset JPEG codec */
+ jpeg_codec_sleep(zr, 1);
+ jpeg_codec_reset(zr);
+ /* video bus enabled */
+ /* display codec revision */
+ if (zr->card.video_codec != 0) {
+ master_codec = zoran_setup_videocodec(zr, zr->card.video_codec);
+ if (!master_codec)
+ goto zr_unreg_i2c;
+ zr->codec = videocodec_attach(master_codec);
+ if (!zr->codec) {
+ dprintk(1, KERN_ERR "%s: %s - no codec found\n",
+ ZR_DEVNAME(zr), __func__);
+ goto zr_free_codec;
+ }
+ if (zr->codec->type != zr->card.video_codec) {
+ dprintk(1, KERN_ERR "%s: %s - wrong codec\n",
+ ZR_DEVNAME(zr), __func__);
+ goto zr_detach_codec;
+ }
+ }
+ if (zr->card.video_vfe != 0) {
+ master_vfe = zoran_setup_videocodec(zr, zr->card.video_vfe);
+ if (!master_vfe)
+ goto zr_detach_codec;
+ zr->vfe = videocodec_attach(master_vfe);
+ if (!zr->vfe) {
+ dprintk(1, KERN_ERR "%s: %s - no VFE found\n",
+ ZR_DEVNAME(zr), __func__);
+ goto zr_free_vfe;
+ }
+ if (zr->vfe->type != zr->card.video_vfe) {
+ dprintk(1, KERN_ERR "%s: %s = wrong VFE\n",
+ ZR_DEVNAME(zr), __func__);
+ goto zr_detach_vfe;
+ }
+ }
+
+ /* take care of Natoma chipset and a revision 1 zr36057 */
+ if ((pci_pci_problems & PCIPCI_NATOMA) && zr->revision <= 1) {
+ zr->jpg_buffers.need_contiguous = 1;
+ dprintk(1, KERN_INFO
+ "%s: ZR36057/Natoma bug, max. buffer size is 128K\n",
+ ZR_DEVNAME(zr));
+ }
+
+ if (zr36057_init(zr) < 0)
+ goto zr_detach_vfe;
+
+ zoran_proc_init(zr);
+
+ return 0;
+
+zr_detach_vfe:
+ videocodec_detach(zr->vfe);
+zr_free_vfe:
+ kfree(master_vfe);
+zr_detach_codec:
+ videocodec_detach(zr->codec);
+zr_free_codec:
+ kfree(master_codec);
+zr_unreg_i2c:
+ zoran_unregister_i2c(zr);
+zr_free_irq:
+ btwrite(0, ZR36057_SPGPPCR);
+ free_irq(zr->pci_dev->irq, zr);
+zr_unmap:
+ iounmap(zr->zr36057_mem);
+zr_unreg:
+ v4l2_device_unregister(&zr->v4l2_dev);
+zr_free_mem:
+ kfree(zr);
+
+ return -ENODEV;
+}
+
+static struct pci_driver zoran_driver = {
+ .name = "zr36067",
+ .id_table = zr36067_pci_tbl,
+ .probe = zoran_probe,
+ .remove = __devexit_p(zoran_remove),
+};
+
+static int __init zoran_init(void)
+{
+ int res;
+
+ printk(KERN_INFO "Zoran MJPEG board driver version %s\n",
+ ZORAN_VERSION);
+
+ /* check the parameters we have been given, adjust if necessary */
+ if (v4l_nbufs < 2)
+ v4l_nbufs = 2;
+ if (v4l_nbufs > VIDEO_MAX_FRAME)
+ v4l_nbufs = VIDEO_MAX_FRAME;
+ /* The user specfies the in KB, we want them in byte
+ * (and page aligned) */
+ v4l_bufsize = PAGE_ALIGN(v4l_bufsize * 1024);
+ if (v4l_bufsize < 32768)
+ v4l_bufsize = 32768;
+ /* 2 MB is arbitrary but sufficient for the maximum possible images */
+ if (v4l_bufsize > 2048 * 1024)
+ v4l_bufsize = 2048 * 1024;
+ if (jpg_nbufs < 4)
+ jpg_nbufs = 4;
+ if (jpg_nbufs > BUZ_MAX_FRAME)
+ jpg_nbufs = BUZ_MAX_FRAME;
+ jpg_bufsize = PAGE_ALIGN(jpg_bufsize * 1024);
+ if (jpg_bufsize < 8192)
+ jpg_bufsize = 8192;
+ if (jpg_bufsize > (512 * 1024))
+ jpg_bufsize = 512 * 1024;
+ /* Use parameter for vidmem or try to find a video card */
+ if (vidmem) {
+ dprintk(1,
+ KERN_INFO
+ "%s: Using supplied video memory base address @ 0x%lx\n",
+ ZORAN_NAME, vidmem);
+ }
+
+ /* some mainboards might not do PCI-PCI data transfer well */
+ if (pci_pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL|PCIPCI_ALIMAGIK)) {
+ dprintk(1,
+ KERN_WARNING
+ "%s: chipset does not support reliable PCI-PCI DMA\n",
+ ZORAN_NAME);
+ }
+
+ res = pci_register_driver(&zoran_driver);
+ if (res) {
+ dprintk(1,
+ KERN_ERR
+ "%s: Unable to register ZR36057 driver\n",
+ ZORAN_NAME);
+ return res;
+ }
+
+ return 0;
+}
+
+static void __exit zoran_exit(void)
+{
+ pci_unregister_driver(&zoran_driver);
+}
+
+module_init(zoran_init);
+module_exit(zoran_exit);
diff --git a/drivers/media/video/zoran/zoran_card.h b/drivers/media/video/zoran/zoran_card.h
new file mode 100644
index 00000000..4936fead
--- /dev/null
+++ b/drivers/media/video/zoran/zoran_card.h
@@ -0,0 +1,54 @@
+/*
+ * Zoran zr36057/zr36067 PCI controller driver, for the
+ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
+ * Media Labs LML33/LML33R10.
+ *
+ * This part handles card-specific data and detection
+ *
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ *
+ * Currently maintained by:
+ * Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * Laurent Pinchart <laurent.pinchart@skynet.be>
+ * Mailinglist <mjpeg-users@lists.sf.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ZORAN_CARD_H__
+#define __ZORAN_CARD_H__
+
+extern int zr36067_debug;
+
+#define dprintk(num, format, args...) \
+ do { \
+ if (zr36067_debug >= num) \
+ printk(format, ##args); \
+ } while (0)
+
+/* Anybody who uses more than four? */
+#define BUZ_MAX 4
+
+extern struct video_device zoran_template;
+
+extern int zoran_check_jpg_settings(struct zoran *zr,
+ struct zoran_jpg_settings *settings,
+ int try);
+extern void zoran_open_init_params(struct zoran *zr);
+extern void zoran_vdev_release(struct video_device *vdev);
+
+void zr36016_write(struct videocodec *codec, u16 reg, u32 val);
+
+#endif /* __ZORAN_CARD_H__ */
diff --git a/drivers/media/video/zoran/zoran_device.c b/drivers/media/video/zoran/zoran_device.c
new file mode 100644
index 00000000..e86173bd
--- /dev/null
+++ b/drivers/media/video/zoran/zoran_device.c
@@ -0,0 +1,1642 @@
+/*
+ * Zoran zr36057/zr36067 PCI controller driver, for the
+ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
+ * Media Labs LML33/LML33R10.
+ *
+ * This part handles device access (PCI/I2C/codec/...)
+ *
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ *
+ * Currently maintained by:
+ * Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * Laurent Pinchart <laurent.pinchart@skynet.be>
+ * Mailinglist <mjpeg-users@lists.sf.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <linux/spinlock.h>
+#include <linux/sem.h>
+
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+
+#include "videocodec.h"
+#include "zoran.h"
+#include "zoran_device.h"
+#include "zoran_card.h"
+
+#define IRQ_MASK ( ZR36057_ISR_GIRQ0 | \
+ ZR36057_ISR_GIRQ1 | \
+ ZR36057_ISR_JPEGRepIRQ )
+
+static bool lml33dpath; /* default = 0
+ * 1 will use digital path in capture
+ * mode instead of analog. It can be
+ * used for picture adjustments using
+ * tool like xawtv while watching image
+ * on TV monitor connected to the output.
+ * However, due to absence of 75 Ohm
+ * load on Bt819 input, there will be
+ * some image imperfections */
+
+module_param(lml33dpath, bool, 0644);
+MODULE_PARM_DESC(lml33dpath,
+ "Use digital path capture mode (on LML33 cards)");
+
+static void
+zr36057_init_vfe (struct zoran *zr);
+
+/*
+ * General Purpose I/O and Guest bus access
+ */
+
+/*
+ * This is a bit tricky. When a board lacks a GPIO function, the corresponding
+ * GPIO bit number in the card_info structure is set to 0.
+ */
+
+void
+GPIO (struct zoran *zr,
+ int bit,
+ unsigned int value)
+{
+ u32 reg;
+ u32 mask;
+
+ /* Make sure the bit number is legal
+ * A bit number of -1 (lacking) gives a mask of 0,
+ * making it harmless */
+ mask = (1 << (24 + bit)) & 0xff000000;
+ reg = btread(ZR36057_GPPGCR1) & ~mask;
+ if (value) {
+ reg |= mask;
+ }
+ btwrite(reg, ZR36057_GPPGCR1);
+ udelay(1);
+}
+
+/*
+ * Wait til post office is no longer busy
+ */
+
+int
+post_office_wait (struct zoran *zr)
+{
+ u32 por;
+
+// while (((por = btread(ZR36057_POR)) & (ZR36057_POR_POPen | ZR36057_POR_POTime)) == ZR36057_POR_POPen) {
+ while ((por = btread(ZR36057_POR)) & ZR36057_POR_POPen) {
+ /* wait for something to happen */
+ }
+ if ((por & ZR36057_POR_POTime) && !zr->card.gws_not_connected) {
+ /* In LML33/BUZ \GWS line is not connected, so it has always timeout set */
+ dprintk(1, KERN_INFO "%s: pop timeout %08x\n", ZR_DEVNAME(zr),
+ por);
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+post_office_write (struct zoran *zr,
+ unsigned int guest,
+ unsigned int reg,
+ unsigned int value)
+{
+ u32 por;
+
+ por =
+ ZR36057_POR_PODir | ZR36057_POR_POTime | ((guest & 7) << 20) |
+ ((reg & 7) << 16) | (value & 0xFF);
+ btwrite(por, ZR36057_POR);
+
+ return post_office_wait(zr);
+}
+
+int
+post_office_read (struct zoran *zr,
+ unsigned int guest,
+ unsigned int reg)
+{
+ u32 por;
+
+ por = ZR36057_POR_POTime | ((guest & 7) << 20) | ((reg & 7) << 16);
+ btwrite(por, ZR36057_POR);
+ if (post_office_wait(zr) < 0) {
+ return -1;
+ }
+
+ return btread(ZR36057_POR) & 0xFF;
+}
+
+/*
+ * detect guests
+ */
+
+static void
+dump_guests (struct zoran *zr)
+{
+ if (zr36067_debug > 2) {
+ int i, guest[8];
+
+ for (i = 1; i < 8; i++) { // Don't read jpeg codec here
+ guest[i] = post_office_read(zr, i, 0);
+ }
+
+ printk(KERN_INFO "%s: Guests:", ZR_DEVNAME(zr));
+
+ for (i = 1; i < 8; i++) {
+ printk(" 0x%02x", guest[i]);
+ }
+ printk("\n");
+ }
+}
+
+static inline unsigned long
+get_time (void)
+{
+ struct timeval tv;
+
+ do_gettimeofday(&tv);
+ return (1000000 * tv.tv_sec + tv.tv_usec);
+}
+
+void
+detect_guest_activity (struct zoran *zr)
+{
+ int timeout, i, j, res, guest[8], guest0[8], change[8][3];
+ unsigned long t0, t1;
+
+ dump_guests(zr);
+ printk(KERN_INFO "%s: Detecting guests activity, please wait...\n",
+ ZR_DEVNAME(zr));
+ for (i = 1; i < 8; i++) { // Don't read jpeg codec here
+ guest0[i] = guest[i] = post_office_read(zr, i, 0);
+ }
+
+ timeout = 0;
+ j = 0;
+ t0 = get_time();
+ while (timeout < 10000) {
+ udelay(10);
+ timeout++;
+ for (i = 1; (i < 8) && (j < 8); i++) {
+ res = post_office_read(zr, i, 0);
+ if (res != guest[i]) {
+ t1 = get_time();
+ change[j][0] = (t1 - t0);
+ t0 = t1;
+ change[j][1] = i;
+ change[j][2] = res;
+ j++;
+ guest[i] = res;
+ }
+ }
+ if (j >= 8)
+ break;
+ }
+ printk(KERN_INFO "%s: Guests:", ZR_DEVNAME(zr));
+
+ for (i = 1; i < 8; i++) {
+ printk(" 0x%02x", guest0[i]);
+ }
+ printk("\n");
+ if (j == 0) {
+ printk(KERN_INFO "%s: No activity detected.\n", ZR_DEVNAME(zr));
+ return;
+ }
+ for (i = 0; i < j; i++) {
+ printk(KERN_INFO "%s: %6d: %d => 0x%02x\n", ZR_DEVNAME(zr),
+ change[i][0], change[i][1], change[i][2]);
+ }
+}
+
+/*
+ * JPEG Codec access
+ */
+
+void
+jpeg_codec_sleep (struct zoran *zr,
+ int sleep)
+{
+ GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_SLEEP], !sleep);
+ if (!sleep) {
+ dprintk(3,
+ KERN_DEBUG
+ "%s: jpeg_codec_sleep() - wake GPIO=0x%08x\n",
+ ZR_DEVNAME(zr), btread(ZR36057_GPPGCR1));
+ udelay(500);
+ } else {
+ dprintk(3,
+ KERN_DEBUG
+ "%s: jpeg_codec_sleep() - sleep GPIO=0x%08x\n",
+ ZR_DEVNAME(zr), btread(ZR36057_GPPGCR1));
+ udelay(2);
+ }
+}
+
+int
+jpeg_codec_reset (struct zoran *zr)
+{
+ /* Take the codec out of sleep */
+ jpeg_codec_sleep(zr, 0);
+
+ if (zr->card.gpcs[GPCS_JPEG_RESET] != 0xff) {
+ post_office_write(zr, zr->card.gpcs[GPCS_JPEG_RESET], 0,
+ 0);
+ udelay(2);
+ } else {
+ GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_RESET], 0);
+ udelay(2);
+ GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_RESET], 1);
+ udelay(2);
+ }
+
+ return 0;
+}
+
+/*
+ * Set the registers for the size we have specified. Don't bother
+ * trying to understand this without the ZR36057 manual in front of
+ * you [AC].
+ *
+ * PS: The manual is free for download in .pdf format from
+ * www.zoran.com - nicely done those folks.
+ */
+
+static void
+zr36057_adjust_vfe (struct zoran *zr,
+ enum zoran_codec_mode mode)
+{
+ u32 reg;
+
+ switch (mode) {
+ case BUZ_MODE_MOTION_DECOMPRESS:
+ btand(~ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR);
+ reg = btread(ZR36057_VFEHCR);
+ if ((reg & (1 << 10)) && zr->card.type != LML33R10) {
+ reg += ((1 << 10) | 1);
+ }
+ btwrite(reg, ZR36057_VFEHCR);
+ break;
+ case BUZ_MODE_MOTION_COMPRESS:
+ case BUZ_MODE_IDLE:
+ default:
+ if ((zr->norm & V4L2_STD_NTSC) ||
+ (zr->card.type == LML33R10 &&
+ (zr->norm & V4L2_STD_PAL)))
+ btand(~ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR);
+ else
+ btor(ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR);
+ reg = btread(ZR36057_VFEHCR);
+ if (!(reg & (1 << 10)) && zr->card.type != LML33R10) {
+ reg -= ((1 << 10) | 1);
+ }
+ btwrite(reg, ZR36057_VFEHCR);
+ break;
+ }
+}
+
+/*
+ * set geometry
+ */
+
+static void
+zr36057_set_vfe (struct zoran *zr,
+ int video_width,
+ int video_height,
+ const struct zoran_format *format)
+{
+ struct tvnorm *tvn;
+ unsigned HStart, HEnd, VStart, VEnd;
+ unsigned DispMode;
+ unsigned VidWinWid, VidWinHt;
+ unsigned hcrop1, hcrop2, vcrop1, vcrop2;
+ unsigned Wa, We, Ha, He;
+ unsigned X, Y, HorDcm, VerDcm;
+ u32 reg;
+ unsigned mask_line_size;
+
+ tvn = zr->timing;
+
+ Wa = tvn->Wa;
+ Ha = tvn->Ha;
+
+ dprintk(2, KERN_INFO "%s: set_vfe() - width = %d, height = %d\n",
+ ZR_DEVNAME(zr), video_width, video_height);
+
+ if (video_width < BUZ_MIN_WIDTH ||
+ video_height < BUZ_MIN_HEIGHT ||
+ video_width > Wa || video_height > Ha) {
+ dprintk(1, KERN_ERR "%s: set_vfe: w=%d h=%d not valid\n",
+ ZR_DEVNAME(zr), video_width, video_height);
+ return;
+ }
+
+ /**** zr36057 ****/
+
+ /* horizontal */
+ VidWinWid = video_width;
+ X = DIV_ROUND_UP(VidWinWid * 64, tvn->Wa);
+ We = (VidWinWid * 64) / X;
+ HorDcm = 64 - X;
+ hcrop1 = 2 * ((tvn->Wa - We) / 4);
+ hcrop2 = tvn->Wa - We - hcrop1;
+ HStart = tvn->HStart ? tvn->HStart : 1;
+ /* (Ronald) Original comment:
+ * "| 1 Doesn't have any effect, tested on both a DC10 and a DC10+"
+ * this is false. It inverses chroma values on the LML33R10 (so Cr
+ * suddenly is shown as Cb and reverse, really cool effect if you
+ * want to see blue faces, not useful otherwise). So don't use |1.
+ * However, the DC10 has '0' as HStart, but does need |1, so we
+ * use a dirty check...
+ */
+ HEnd = HStart + tvn->Wa - 1;
+ HStart += hcrop1;
+ HEnd -= hcrop2;
+ reg = ((HStart & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HStart)
+ | ((HEnd & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HEnd);
+ if (zr->card.vfe_pol.hsync_pol)
+ reg |= ZR36057_VFEHCR_HSPol;
+ btwrite(reg, ZR36057_VFEHCR);
+
+ /* Vertical */
+ DispMode = !(video_height > BUZ_MAX_HEIGHT / 2);
+ VidWinHt = DispMode ? video_height : video_height / 2;
+ Y = DIV_ROUND_UP(VidWinHt * 64 * 2, tvn->Ha);
+ He = (VidWinHt * 64) / Y;
+ VerDcm = 64 - Y;
+ vcrop1 = (tvn->Ha / 2 - He) / 2;
+ vcrop2 = tvn->Ha / 2 - He - vcrop1;
+ VStart = tvn->VStart;
+ VEnd = VStart + tvn->Ha / 2; // - 1; FIXME SnapShot times out with -1 in 768*576 on the DC10 - LP
+ VStart += vcrop1;
+ VEnd -= vcrop2;
+ reg = ((VStart & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VStart)
+ | ((VEnd & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VEnd);
+ if (zr->card.vfe_pol.vsync_pol)
+ reg |= ZR36057_VFEVCR_VSPol;
+ btwrite(reg, ZR36057_VFEVCR);
+
+ /* scaler and pixel format */
+ reg = 0;
+ reg |= (HorDcm << ZR36057_VFESPFR_HorDcm);
+ reg |= (VerDcm << ZR36057_VFESPFR_VerDcm);
+ reg |= (DispMode << ZR36057_VFESPFR_DispMode);
+ /* RJ: I don't know, why the following has to be the opposite
+ * of the corresponding ZR36060 setting, but only this way
+ * we get the correct colors when uncompressing to the screen */
+ //reg |= ZR36057_VFESPFR_VCLKPol; /**/
+ /* RJ: Don't know if that is needed for NTSC also */
+ if (!(zr->norm & V4L2_STD_NTSC))
+ reg |= ZR36057_VFESPFR_ExtFl; // NEEDED!!!!!!! Wolfgang
+ reg |= ZR36057_VFESPFR_TopField;
+ if (HorDcm >= 48) {
+ reg |= 3 << ZR36057_VFESPFR_HFilter; /* 5 tap filter */
+ } else if (HorDcm >= 32) {
+ reg |= 2 << ZR36057_VFESPFR_HFilter; /* 4 tap filter */
+ } else if (HorDcm >= 16) {
+ reg |= 1 << ZR36057_VFESPFR_HFilter; /* 3 tap filter */
+ }
+ reg |= format->vfespfr;
+ btwrite(reg, ZR36057_VFESPFR);
+
+ /* display configuration */
+ reg = (16 << ZR36057_VDCR_MinPix)
+ | (VidWinHt << ZR36057_VDCR_VidWinHt)
+ | (VidWinWid << ZR36057_VDCR_VidWinWid);
+ if (pci_pci_problems & PCIPCI_TRITON)
+ // || zr->revision < 1) // Revision 1 has also Triton support
+ reg &= ~ZR36057_VDCR_Triton;
+ else
+ reg |= ZR36057_VDCR_Triton;
+ btwrite(reg, ZR36057_VDCR);
+
+ /* (Ronald) don't write this if overlay_mask = NULL */
+ if (zr->overlay_mask) {
+ /* Write overlay clipping mask data, but don't enable overlay clipping */
+ /* RJ: since this makes only sense on the screen, we use
+ * zr->overlay_settings.width instead of video_width */
+
+ mask_line_size = (BUZ_MAX_WIDTH + 31) / 32;
+ reg = virt_to_bus(zr->overlay_mask);
+ btwrite(reg, ZR36057_MMTR);
+ reg = virt_to_bus(zr->overlay_mask + mask_line_size);
+ btwrite(reg, ZR36057_MMBR);
+ reg =
+ mask_line_size - (zr->overlay_settings.width +
+ 31) / 32;
+ if (DispMode == 0)
+ reg += mask_line_size;
+ reg <<= ZR36057_OCR_MaskStride;
+ btwrite(reg, ZR36057_OCR);
+ }
+
+ zr36057_adjust_vfe(zr, zr->codec_mode);
+}
+
+/*
+ * Switch overlay on or off
+ */
+
+void
+zr36057_overlay (struct zoran *zr,
+ int on)
+{
+ u32 reg;
+
+ if (on) {
+ /* do the necessary settings ... */
+ btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); /* switch it off first */
+
+ zr36057_set_vfe(zr,
+ zr->overlay_settings.width,
+ zr->overlay_settings.height,
+ zr->overlay_settings.format);
+
+ /* Start and length of each line MUST be 4-byte aligned.
+ * This should be already checked before the call to this routine.
+ * All error messages are internal driver checking only! */
+
+ /* video display top and bottom registers */
+ reg = (long) zr->vbuf_base +
+ zr->overlay_settings.x *
+ ((zr->overlay_settings.format->depth + 7) / 8) +
+ zr->overlay_settings.y *
+ zr->vbuf_bytesperline;
+ btwrite(reg, ZR36057_VDTR);
+ if (reg & 3)
+ dprintk(1,
+ KERN_ERR
+ "%s: zr36057_overlay() - video_address not aligned\n",
+ ZR_DEVNAME(zr));
+ if (zr->overlay_settings.height > BUZ_MAX_HEIGHT / 2)
+ reg += zr->vbuf_bytesperline;
+ btwrite(reg, ZR36057_VDBR);
+
+ /* video stride, status, and frame grab register */
+ reg = zr->vbuf_bytesperline -
+ zr->overlay_settings.width *
+ ((zr->overlay_settings.format->depth + 7) / 8);
+ if (zr->overlay_settings.height > BUZ_MAX_HEIGHT / 2)
+ reg += zr->vbuf_bytesperline;
+ if (reg & 3)
+ dprintk(1,
+ KERN_ERR
+ "%s: zr36057_overlay() - video_stride not aligned\n",
+ ZR_DEVNAME(zr));
+ reg = (reg << ZR36057_VSSFGR_DispStride);
+ reg |= ZR36057_VSSFGR_VidOvf; /* clear overflow status */
+ btwrite(reg, ZR36057_VSSFGR);
+
+ /* Set overlay clipping */
+ if (zr->overlay_settings.clipcount > 0)
+ btor(ZR36057_OCR_OvlEnable, ZR36057_OCR);
+
+ /* ... and switch it on */
+ btor(ZR36057_VDCR_VidEn, ZR36057_VDCR);
+ } else {
+ /* Switch it off */
+ btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR);
+ }
+}
+
+/*
+ * The overlay mask has one bit for each pixel on a scan line,
+ * and the maximum window size is BUZ_MAX_WIDTH * BUZ_MAX_HEIGHT pixels.
+ */
+
+void write_overlay_mask(struct zoran_fh *fh, struct v4l2_clip *vp, int count)
+{
+ struct zoran *zr = fh->zr;
+ unsigned mask_line_size = (BUZ_MAX_WIDTH + 31) / 32;
+ u32 *mask;
+ int x, y, width, height;
+ unsigned i, j, k;
+ u32 reg;
+
+ /* fill mask with one bits */
+ memset(fh->overlay_mask, ~0, mask_line_size * 4 * BUZ_MAX_HEIGHT);
+ reg = 0;
+
+ for (i = 0; i < count; ++i) {
+ /* pick up local copy of clip */
+ x = vp[i].c.left;
+ y = vp[i].c.top;
+ width = vp[i].c.width;
+ height = vp[i].c.height;
+
+ /* trim clips that extend beyond the window */
+ if (x < 0) {
+ width += x;
+ x = 0;
+ }
+ if (y < 0) {
+ height += y;
+ y = 0;
+ }
+ if (x + width > fh->overlay_settings.width) {
+ width = fh->overlay_settings.width - x;
+ }
+ if (y + height > fh->overlay_settings.height) {
+ height = fh->overlay_settings.height - y;
+ }
+
+ /* ignore degenerate clips */
+ if (height <= 0) {
+ continue;
+ }
+ if (width <= 0) {
+ continue;
+ }
+
+ /* apply clip for each scan line */
+ for (j = 0; j < height; ++j) {
+ /* reset bit for each pixel */
+ /* this can be optimized later if need be */
+ mask = fh->overlay_mask + (y + j) * mask_line_size;
+ for (k = 0; k < width; ++k) {
+ mask[(x + k) / 32] &=
+ ~((u32) 1 << (x + k) % 32);
+ }
+ }
+ }
+}
+
+/* Enable/Disable uncompressed memory grabbing of the 36057 */
+
+void
+zr36057_set_memgrab (struct zoran *zr,
+ int mode)
+{
+ if (mode) {
+ /* We only check SnapShot and not FrameGrab here. SnapShot==1
+ * means a capture is already in progress, but FrameGrab==1
+ * doesn't necessary mean that. It's more correct to say a 1
+ * to 0 transition indicates a capture completed. If a
+ * capture is pending when capturing is tuned off, FrameGrab
+ * will be stuck at 1 until capturing is turned back on.
+ */
+ if (btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_SnapShot)
+ dprintk(1,
+ KERN_WARNING
+ "%s: zr36057_set_memgrab(1) with SnapShot on!?\n",
+ ZR_DEVNAME(zr));
+
+ /* switch on VSync interrupts */
+ btwrite(IRQ_MASK, ZR36057_ISR); // Clear Interrupts
+ btor(zr->card.vsync_int, ZR36057_ICR); // SW
+
+ /* enable SnapShot */
+ btor(ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR);
+
+ /* Set zr36057 video front end and enable video */
+ zr36057_set_vfe(zr, zr->v4l_settings.width,
+ zr->v4l_settings.height,
+ zr->v4l_settings.format);
+
+ zr->v4l_memgrab_active = 1;
+ } else {
+ /* switch off VSync interrupts */
+ btand(~zr->card.vsync_int, ZR36057_ICR); // SW
+
+ zr->v4l_memgrab_active = 0;
+ zr->v4l_grab_frame = NO_GRAB_ACTIVE;
+
+ /* reenable grabbing to screen if it was running */
+ if (zr->v4l_overlay_active) {
+ zr36057_overlay(zr, 1);
+ } else {
+ btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR);
+ btand(~ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR);
+ }
+ }
+}
+
+int
+wait_grab_pending (struct zoran *zr)
+{
+ unsigned long flags;
+
+ /* wait until all pending grabs are finished */
+
+ if (!zr->v4l_memgrab_active)
+ return 0;
+
+ wait_event_interruptible(zr->v4l_capq,
+ (zr->v4l_pend_tail == zr->v4l_pend_head));
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+ zr36057_set_memgrab(zr, 0);
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+
+ return 0;
+}
+
+/*****************************************************************************
+ * *
+ * Set up the Buz-specific MJPEG part *
+ * *
+ *****************************************************************************/
+
+static inline void
+set_frame (struct zoran *zr,
+ int val)
+{
+ GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_FRAME], val);
+}
+
+static void
+set_videobus_dir (struct zoran *zr,
+ int val)
+{
+ switch (zr->card.type) {
+ case LML33:
+ case LML33R10:
+ if (lml33dpath == 0)
+ GPIO(zr, 5, val);
+ else
+ GPIO(zr, 5, 1);
+ break;
+ default:
+ GPIO(zr, zr->card.gpio[ZR_GPIO_VID_DIR],
+ zr->card.gpio_pol[ZR_GPIO_VID_DIR] ? !val : val);
+ break;
+ }
+}
+
+static void
+init_jpeg_queue (struct zoran *zr)
+{
+ int i;
+
+ /* re-initialize DMA ring stuff */
+ zr->jpg_que_head = 0;
+ zr->jpg_dma_head = 0;
+ zr->jpg_dma_tail = 0;
+ zr->jpg_que_tail = 0;
+ zr->jpg_seq_num = 0;
+ zr->JPEG_error = 0;
+ zr->num_errors = 0;
+ zr->jpg_err_seq = 0;
+ zr->jpg_err_shift = 0;
+ zr->jpg_queued_num = 0;
+ for (i = 0; i < zr->jpg_buffers.num_buffers; i++) {
+ zr->jpg_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */
+ }
+ for (i = 0; i < BUZ_NUM_STAT_COM; i++) {
+ zr->stat_com[i] = cpu_to_le32(1); /* mark as unavailable to zr36057 */
+ }
+}
+
+static void
+zr36057_set_jpg (struct zoran *zr,
+ enum zoran_codec_mode mode)
+{
+ struct tvnorm *tvn;
+ u32 reg;
+
+ tvn = zr->timing;
+
+ /* assert P_Reset, disable code transfer, deassert Active */
+ btwrite(0, ZR36057_JPC);
+
+ /* MJPEG compression mode */
+ switch (mode) {
+
+ case BUZ_MODE_MOTION_COMPRESS:
+ default:
+ reg = ZR36057_JMC_MJPGCmpMode;
+ break;
+
+ case BUZ_MODE_MOTION_DECOMPRESS:
+ reg = ZR36057_JMC_MJPGExpMode;
+ reg |= ZR36057_JMC_SyncMstr;
+ /* RJ: The following is experimental - improves the output to screen */
+ //if(zr->jpg_settings.VFIFO_FB) reg |= ZR36057_JMC_VFIFO_FB; // No, it doesn't. SM
+ break;
+
+ case BUZ_MODE_STILL_COMPRESS:
+ reg = ZR36057_JMC_JPGCmpMode;
+ break;
+
+ case BUZ_MODE_STILL_DECOMPRESS:
+ reg = ZR36057_JMC_JPGExpMode;
+ break;
+
+ }
+ reg |= ZR36057_JMC_JPG;
+ if (zr->jpg_settings.field_per_buff == 1)
+ reg |= ZR36057_JMC_Fld_per_buff;
+ btwrite(reg, ZR36057_JMC);
+
+ /* vertical */
+ btor(ZR36057_VFEVCR_VSPol, ZR36057_VFEVCR);
+ reg = (6 << ZR36057_VSP_VsyncSize) |
+ (tvn->Ht << ZR36057_VSP_FrmTot);
+ btwrite(reg, ZR36057_VSP);
+ reg = ((zr->jpg_settings.img_y + tvn->VStart) << ZR36057_FVAP_NAY) |
+ (zr->jpg_settings.img_height << ZR36057_FVAP_PAY);
+ btwrite(reg, ZR36057_FVAP);
+
+ /* horizontal */
+ if (zr->card.vfe_pol.hsync_pol)
+ btor(ZR36057_VFEHCR_HSPol, ZR36057_VFEHCR);
+ else
+ btand(~ZR36057_VFEHCR_HSPol, ZR36057_VFEHCR);
+ reg = ((tvn->HSyncStart) << ZR36057_HSP_HsyncStart) |
+ (tvn->Wt << ZR36057_HSP_LineTot);
+ btwrite(reg, ZR36057_HSP);
+ reg = ((zr->jpg_settings.img_x +
+ tvn->HStart + 4) << ZR36057_FHAP_NAX) |
+ (zr->jpg_settings.img_width << ZR36057_FHAP_PAX);
+ btwrite(reg, ZR36057_FHAP);
+
+ /* field process parameters */
+ if (zr->jpg_settings.odd_even)
+ reg = ZR36057_FPP_Odd_Even;
+ else
+ reg = 0;
+
+ btwrite(reg, ZR36057_FPP);
+
+ /* Set proper VCLK Polarity, else colors will be wrong during playback */
+ //btor(ZR36057_VFESPFR_VCLKPol, ZR36057_VFESPFR);
+
+ /* code base address */
+ reg = virt_to_bus(zr->stat_com);
+ btwrite(reg, ZR36057_JCBA);
+
+ /* FIFO threshold (FIFO is 160. double words) */
+ /* NOTE: decimal values here */
+ switch (mode) {
+
+ case BUZ_MODE_STILL_COMPRESS:
+ case BUZ_MODE_MOTION_COMPRESS:
+ if (zr->card.type != BUZ)
+ reg = 140;
+ else
+ reg = 60;
+ break;
+
+ case BUZ_MODE_STILL_DECOMPRESS:
+ case BUZ_MODE_MOTION_DECOMPRESS:
+ reg = 20;
+ break;
+
+ default:
+ reg = 80;
+ break;
+
+ }
+ btwrite(reg, ZR36057_JCFT);
+ zr36057_adjust_vfe(zr, mode);
+
+}
+
+void
+print_interrupts (struct zoran *zr)
+{
+ int res, noerr = 0;
+
+ printk(KERN_INFO "%s: interrupts received:", ZR_DEVNAME(zr));
+ if ((res = zr->field_counter) < -1 || res > 1) {
+ printk(" FD:%d", res);
+ }
+ if ((res = zr->intr_counter_GIRQ1) != 0) {
+ printk(" GIRQ1:%d", res);
+ noerr++;
+ }
+ if ((res = zr->intr_counter_GIRQ0) != 0) {
+ printk(" GIRQ0:%d", res);
+ noerr++;
+ }
+ if ((res = zr->intr_counter_CodRepIRQ) != 0) {
+ printk(" CodRepIRQ:%d", res);
+ noerr++;
+ }
+ if ((res = zr->intr_counter_JPEGRepIRQ) != 0) {
+ printk(" JPEGRepIRQ:%d", res);
+ noerr++;
+ }
+ if (zr->JPEG_max_missed) {
+ printk(" JPEG delays: max=%d min=%d", zr->JPEG_max_missed,
+ zr->JPEG_min_missed);
+ }
+ if (zr->END_event_missed) {
+ printk(" ENDs missed: %d", zr->END_event_missed);
+ }
+ //if (zr->jpg_queued_num) {
+ printk(" queue_state=%ld/%ld/%ld/%ld", zr->jpg_que_tail,
+ zr->jpg_dma_tail, zr->jpg_dma_head, zr->jpg_que_head);
+ //}
+ if (!noerr) {
+ printk(": no interrupts detected.");
+ }
+ printk("\n");
+}
+
+void
+clear_interrupt_counters (struct zoran *zr)
+{
+ zr->intr_counter_GIRQ1 = 0;
+ zr->intr_counter_GIRQ0 = 0;
+ zr->intr_counter_CodRepIRQ = 0;
+ zr->intr_counter_JPEGRepIRQ = 0;
+ zr->field_counter = 0;
+ zr->IRQ1_in = 0;
+ zr->IRQ1_out = 0;
+ zr->JPEG_in = 0;
+ zr->JPEG_out = 0;
+ zr->JPEG_0 = 0;
+ zr->JPEG_1 = 0;
+ zr->END_event_missed = 0;
+ zr->JPEG_missed = 0;
+ zr->JPEG_max_missed = 0;
+ zr->JPEG_min_missed = 0x7fffffff;
+}
+
+static u32
+count_reset_interrupt (struct zoran *zr)
+{
+ u32 isr;
+
+ if ((isr = btread(ZR36057_ISR) & 0x78000000)) {
+ if (isr & ZR36057_ISR_GIRQ1) {
+ btwrite(ZR36057_ISR_GIRQ1, ZR36057_ISR);
+ zr->intr_counter_GIRQ1++;
+ }
+ if (isr & ZR36057_ISR_GIRQ0) {
+ btwrite(ZR36057_ISR_GIRQ0, ZR36057_ISR);
+ zr->intr_counter_GIRQ0++;
+ }
+ if (isr & ZR36057_ISR_CodRepIRQ) {
+ btwrite(ZR36057_ISR_CodRepIRQ, ZR36057_ISR);
+ zr->intr_counter_CodRepIRQ++;
+ }
+ if (isr & ZR36057_ISR_JPEGRepIRQ) {
+ btwrite(ZR36057_ISR_JPEGRepIRQ, ZR36057_ISR);
+ zr->intr_counter_JPEGRepIRQ++;
+ }
+ }
+ return isr;
+}
+
+void
+jpeg_start (struct zoran *zr)
+{
+ int reg;
+
+ zr->frame_num = 0;
+
+ /* deassert P_reset, disable code transfer, deassert Active */
+ btwrite(ZR36057_JPC_P_Reset, ZR36057_JPC);
+ /* stop flushing the internal code buffer */
+ btand(~ZR36057_MCTCR_CFlush, ZR36057_MCTCR);
+ /* enable code transfer */
+ btor(ZR36057_JPC_CodTrnsEn, ZR36057_JPC);
+
+ /* clear IRQs */
+ btwrite(IRQ_MASK, ZR36057_ISR);
+ /* enable the JPEG IRQs */
+ btwrite(zr->card.jpeg_int |
+ ZR36057_ICR_JPEGRepIRQ |
+ ZR36057_ICR_IntPinEn,
+ ZR36057_ICR);
+
+ set_frame(zr, 0); // \FRAME
+
+ /* set the JPEG codec guest ID */
+ reg = (zr->card.gpcs[1] << ZR36057_JCGI_JPEGuestID) |
+ (0 << ZR36057_JCGI_JPEGuestReg);
+ btwrite(reg, ZR36057_JCGI);
+
+ if (zr->card.video_vfe == CODEC_TYPE_ZR36016 &&
+ zr->card.video_codec == CODEC_TYPE_ZR36050) {
+ /* Enable processing on the ZR36016 */
+ if (zr->vfe)
+ zr36016_write(zr->vfe, 0, 1);
+
+ /* load the address of the GO register in the ZR36050 latch */
+ post_office_write(zr, 0, 0, 0);
+ }
+
+ /* assert Active */
+ btor(ZR36057_JPC_Active, ZR36057_JPC);
+
+ /* enable the Go generation */
+ btor(ZR36057_JMC_Go_en, ZR36057_JMC);
+ udelay(30);
+
+ set_frame(zr, 1); // /FRAME
+
+ dprintk(3, KERN_DEBUG "%s: jpeg_start\n", ZR_DEVNAME(zr));
+}
+
+void
+zr36057_enable_jpg (struct zoran *zr,
+ enum zoran_codec_mode mode)
+{
+ struct vfe_settings cap;
+ int field_size =
+ zr->jpg_buffers.buffer_size / zr->jpg_settings.field_per_buff;
+
+ zr->codec_mode = mode;
+
+ cap.x = zr->jpg_settings.img_x;
+ cap.y = zr->jpg_settings.img_y;
+ cap.width = zr->jpg_settings.img_width;
+ cap.height = zr->jpg_settings.img_height;
+ cap.decimation =
+ zr->jpg_settings.HorDcm | (zr->jpg_settings.VerDcm << 8);
+ cap.quality = zr->jpg_settings.jpg_comp.quality;
+
+ switch (mode) {
+
+ case BUZ_MODE_MOTION_COMPRESS: {
+ struct jpeg_app_marker app;
+ struct jpeg_com_marker com;
+
+ /* In motion compress mode, the decoder output must be enabled, and
+ * the video bus direction set to input.
+ */
+ set_videobus_dir(zr, 0);
+ decoder_call(zr, video, s_stream, 1);
+ encoder_call(zr, video, s_routing, 0, 0, 0);
+
+ /* Take the JPEG codec and the VFE out of sleep */
+ jpeg_codec_sleep(zr, 0);
+
+ /* set JPEG app/com marker */
+ app.appn = zr->jpg_settings.jpg_comp.APPn;
+ app.len = zr->jpg_settings.jpg_comp.APP_len;
+ memcpy(app.data, zr->jpg_settings.jpg_comp.APP_data, 60);
+ zr->codec->control(zr->codec, CODEC_S_JPEG_APP_DATA,
+ sizeof(struct jpeg_app_marker), &app);
+
+ com.len = zr->jpg_settings.jpg_comp.COM_len;
+ memcpy(com.data, zr->jpg_settings.jpg_comp.COM_data, 60);
+ zr->codec->control(zr->codec, CODEC_S_JPEG_COM_DATA,
+ sizeof(struct jpeg_com_marker), &com);
+
+ /* Setup the JPEG codec */
+ zr->codec->control(zr->codec, CODEC_S_JPEG_TDS_BYTE,
+ sizeof(int), &field_size);
+ zr->codec->set_video(zr->codec, zr->timing, &cap,
+ &zr->card.vfe_pol);
+ zr->codec->set_mode(zr->codec, CODEC_DO_COMPRESSION);
+
+ /* Setup the VFE */
+ if (zr->vfe) {
+ zr->vfe->control(zr->vfe, CODEC_S_JPEG_TDS_BYTE,
+ sizeof(int), &field_size);
+ zr->vfe->set_video(zr->vfe, zr->timing, &cap,
+ &zr->card.vfe_pol);
+ zr->vfe->set_mode(zr->vfe, CODEC_DO_COMPRESSION);
+ }
+
+ init_jpeg_queue(zr);
+ zr36057_set_jpg(zr, mode); // \P_Reset, ... Video param, FIFO
+
+ clear_interrupt_counters(zr);
+ dprintk(2, KERN_INFO "%s: enable_jpg(MOTION_COMPRESS)\n",
+ ZR_DEVNAME(zr));
+ break;
+ }
+
+ case BUZ_MODE_MOTION_DECOMPRESS:
+ /* In motion decompression mode, the decoder output must be disabled, and
+ * the video bus direction set to output.
+ */
+ decoder_call(zr, video, s_stream, 0);
+ set_videobus_dir(zr, 1);
+ encoder_call(zr, video, s_routing, 1, 0, 0);
+
+ /* Take the JPEG codec and the VFE out of sleep */
+ jpeg_codec_sleep(zr, 0);
+ /* Setup the VFE */
+ if (zr->vfe) {
+ zr->vfe->set_video(zr->vfe, zr->timing, &cap,
+ &zr->card.vfe_pol);
+ zr->vfe->set_mode(zr->vfe, CODEC_DO_EXPANSION);
+ }
+ /* Setup the JPEG codec */
+ zr->codec->set_video(zr->codec, zr->timing, &cap,
+ &zr->card.vfe_pol);
+ zr->codec->set_mode(zr->codec, CODEC_DO_EXPANSION);
+
+ init_jpeg_queue(zr);
+ zr36057_set_jpg(zr, mode); // \P_Reset, ... Video param, FIFO
+
+ clear_interrupt_counters(zr);
+ dprintk(2, KERN_INFO "%s: enable_jpg(MOTION_DECOMPRESS)\n",
+ ZR_DEVNAME(zr));
+ break;
+
+ case BUZ_MODE_IDLE:
+ default:
+ /* shut down processing */
+ btand(~(zr->card.jpeg_int | ZR36057_ICR_JPEGRepIRQ),
+ ZR36057_ICR);
+ btwrite(zr->card.jpeg_int | ZR36057_ICR_JPEGRepIRQ,
+ ZR36057_ISR);
+ btand(~ZR36057_JMC_Go_en, ZR36057_JMC); // \Go_en
+
+ msleep(50);
+
+ set_videobus_dir(zr, 0);
+ set_frame(zr, 1); // /FRAME
+ btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR); // /CFlush
+ btwrite(0, ZR36057_JPC); // \P_Reset,\CodTrnsEn,\Active
+ btand(~ZR36057_JMC_VFIFO_FB, ZR36057_JMC);
+ btand(~ZR36057_JMC_SyncMstr, ZR36057_JMC);
+ jpeg_codec_reset(zr);
+ jpeg_codec_sleep(zr, 1);
+ zr36057_adjust_vfe(zr, mode);
+
+ decoder_call(zr, video, s_stream, 1);
+ encoder_call(zr, video, s_routing, 0, 0, 0);
+
+ dprintk(2, KERN_INFO "%s: enable_jpg(IDLE)\n", ZR_DEVNAME(zr));
+ break;
+
+ }
+}
+
+/* when this is called the spinlock must be held */
+void
+zoran_feed_stat_com (struct zoran *zr)
+{
+ /* move frames from pending queue to DMA */
+
+ int frame, i, max_stat_com;
+
+ max_stat_com =
+ (zr->jpg_settings.TmpDcm ==
+ 1) ? BUZ_NUM_STAT_COM : (BUZ_NUM_STAT_COM >> 1);
+
+ while ((zr->jpg_dma_head - zr->jpg_dma_tail) < max_stat_com &&
+ zr->jpg_dma_head < zr->jpg_que_head) {
+
+ frame = zr->jpg_pend[zr->jpg_dma_head & BUZ_MASK_FRAME];
+ if (zr->jpg_settings.TmpDcm == 1) {
+ /* fill 1 stat_com entry */
+ i = (zr->jpg_dma_head -
+ zr->jpg_err_shift) & BUZ_MASK_STAT_COM;
+ if (!(zr->stat_com[i] & cpu_to_le32(1)))
+ break;
+ zr->stat_com[i] =
+ cpu_to_le32(zr->jpg_buffers.buffer[frame].jpg.frag_tab_bus);
+ } else {
+ /* fill 2 stat_com entries */
+ i = ((zr->jpg_dma_head -
+ zr->jpg_err_shift) & 1) * 2;
+ if (!(zr->stat_com[i] & cpu_to_le32(1)))
+ break;
+ zr->stat_com[i] =
+ cpu_to_le32(zr->jpg_buffers.buffer[frame].jpg.frag_tab_bus);
+ zr->stat_com[i + 1] =
+ cpu_to_le32(zr->jpg_buffers.buffer[frame].jpg.frag_tab_bus);
+ }
+ zr->jpg_buffers.buffer[frame].state = BUZ_STATE_DMA;
+ zr->jpg_dma_head++;
+
+ }
+ if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS)
+ zr->jpg_queued_num++;
+}
+
+/* when this is called the spinlock must be held */
+static void
+zoran_reap_stat_com (struct zoran *zr)
+{
+ /* move frames from DMA queue to done queue */
+
+ int i;
+ u32 stat_com;
+ unsigned int seq;
+ unsigned int dif;
+ struct zoran_buffer *buffer;
+ int frame;
+
+ /* In motion decompress we don't have a hardware frame counter,
+ * we just count the interrupts here */
+
+ if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) {
+ zr->jpg_seq_num++;
+ }
+ while (zr->jpg_dma_tail < zr->jpg_dma_head) {
+ if (zr->jpg_settings.TmpDcm == 1)
+ i = (zr->jpg_dma_tail -
+ zr->jpg_err_shift) & BUZ_MASK_STAT_COM;
+ else
+ i = ((zr->jpg_dma_tail -
+ zr->jpg_err_shift) & 1) * 2 + 1;
+
+ stat_com = le32_to_cpu(zr->stat_com[i]);
+
+ if ((stat_com & 1) == 0) {
+ return;
+ }
+ frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME];
+ buffer = &zr->jpg_buffers.buffer[frame];
+ do_gettimeofday(&buffer->bs.timestamp);
+
+ if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) {
+ buffer->bs.length = (stat_com & 0x7fffff) >> 1;
+
+ /* update sequence number with the help of the counter in stat_com */
+
+ seq = ((stat_com >> 24) + zr->jpg_err_seq) & 0xff;
+ dif = (seq - zr->jpg_seq_num) & 0xff;
+ zr->jpg_seq_num += dif;
+ } else {
+ buffer->bs.length = 0;
+ }
+ buffer->bs.seq =
+ zr->jpg_settings.TmpDcm ==
+ 2 ? (zr->jpg_seq_num >> 1) : zr->jpg_seq_num;
+ buffer->state = BUZ_STATE_DONE;
+
+ zr->jpg_dma_tail++;
+ }
+}
+
+static void zoran_restart(struct zoran *zr)
+{
+ /* Now the stat_comm buffer is ready for restart */
+ unsigned int status = 0;
+ int mode;
+
+ if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) {
+ decoder_call(zr, video, g_input_status, &status);
+ mode = CODEC_DO_COMPRESSION;
+ } else {
+ status = V4L2_IN_ST_NO_SIGNAL;
+ mode = CODEC_DO_EXPANSION;
+ }
+ if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS ||
+ !(status & V4L2_IN_ST_NO_SIGNAL)) {
+ /********** RESTART code *************/
+ jpeg_codec_reset(zr);
+ zr->codec->set_mode(zr->codec, mode);
+ zr36057_set_jpg(zr, zr->codec_mode);
+ jpeg_start(zr);
+
+ if (zr->num_errors <= 8)
+ dprintk(2, KERN_INFO "%s: Restart\n",
+ ZR_DEVNAME(zr));
+
+ zr->JPEG_missed = 0;
+ zr->JPEG_error = 2;
+ /********** End RESTART code ***********/
+ }
+}
+
+static void
+error_handler (struct zoran *zr,
+ u32 astat,
+ u32 stat)
+{
+ int i;
+
+ /* This is JPEG error handling part */
+ if (zr->codec_mode != BUZ_MODE_MOTION_COMPRESS &&
+ zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS) {
+ return;
+ }
+
+ if ((stat & 1) == 0 &&
+ zr->codec_mode == BUZ_MODE_MOTION_COMPRESS &&
+ zr->jpg_dma_tail - zr->jpg_que_tail >= zr->jpg_buffers.num_buffers) {
+ /* No free buffers... */
+ zoran_reap_stat_com(zr);
+ zoran_feed_stat_com(zr);
+ wake_up_interruptible(&zr->jpg_capq);
+ zr->JPEG_missed = 0;
+ return;
+ }
+
+ if (zr->JPEG_error == 1) {
+ zoran_restart(zr);
+ return;
+ }
+
+ /*
+ * First entry: error just happened during normal operation
+ *
+ * In BUZ_MODE_MOTION_COMPRESS:
+ *
+ * Possible glitch in TV signal. In this case we should
+ * stop the codec and wait for good quality signal before
+ * restarting it to avoid further problems
+ *
+ * In BUZ_MODE_MOTION_DECOMPRESS:
+ *
+ * Bad JPEG frame: we have to mark it as processed (codec crashed
+ * and was not able to do it itself), and to remove it from queue.
+ */
+ btand(~ZR36057_JMC_Go_en, ZR36057_JMC);
+ udelay(1);
+ stat = stat | (post_office_read(zr, 7, 0) & 3) << 8;
+ btwrite(0, ZR36057_JPC);
+ btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR);
+ jpeg_codec_reset(zr);
+ jpeg_codec_sleep(zr, 1);
+ zr->JPEG_error = 1;
+ zr->num_errors++;
+
+ /* Report error */
+ if (zr36067_debug > 1 && zr->num_errors <= 8) {
+ long frame;
+ int j;
+
+ frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME];
+ printk(KERN_ERR
+ "%s: JPEG error stat=0x%08x(0x%08x) queue_state=%ld/%ld/%ld/%ld seq=%ld frame=%ld. Codec stopped. ",
+ ZR_DEVNAME(zr), stat, zr->last_isr,
+ zr->jpg_que_tail, zr->jpg_dma_tail,
+ zr->jpg_dma_head, zr->jpg_que_head,
+ zr->jpg_seq_num, frame);
+ printk(KERN_INFO "stat_com frames:");
+ for (j = 0; j < BUZ_NUM_STAT_COM; j++) {
+ for (i = 0; i < zr->jpg_buffers.num_buffers; i++) {
+ if (le32_to_cpu(zr->stat_com[j]) == zr->jpg_buffers.buffer[i].jpg.frag_tab_bus)
+ printk(KERN_CONT "% d->%d", j, i);
+ }
+ }
+ printk(KERN_CONT "\n");
+ }
+ /* Find an entry in stat_com and rotate contents */
+ if (zr->jpg_settings.TmpDcm == 1)
+ i = (zr->jpg_dma_tail - zr->jpg_err_shift) & BUZ_MASK_STAT_COM;
+ else
+ i = ((zr->jpg_dma_tail - zr->jpg_err_shift) & 1) * 2;
+ if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) {
+ /* Mimic zr36067 operation */
+ zr->stat_com[i] |= cpu_to_le32(1);
+ if (zr->jpg_settings.TmpDcm != 1)
+ zr->stat_com[i + 1] |= cpu_to_le32(1);
+ /* Refill */
+ zoran_reap_stat_com(zr);
+ zoran_feed_stat_com(zr);
+ wake_up_interruptible(&zr->jpg_capq);
+ /* Find an entry in stat_com again after refill */
+ if (zr->jpg_settings.TmpDcm == 1)
+ i = (zr->jpg_dma_tail - zr->jpg_err_shift) & BUZ_MASK_STAT_COM;
+ else
+ i = ((zr->jpg_dma_tail - zr->jpg_err_shift) & 1) * 2;
+ }
+ if (i) {
+ /* Rotate stat_comm entries to make current entry first */
+ int j;
+ __le32 bus_addr[BUZ_NUM_STAT_COM];
+
+ /* Here we are copying the stat_com array, which
+ * is already in little endian format, so
+ * no endian conversions here
+ */
+ memcpy(bus_addr, zr->stat_com, sizeof(bus_addr));
+
+ for (j = 0; j < BUZ_NUM_STAT_COM; j++)
+ zr->stat_com[j] = bus_addr[(i + j) & BUZ_MASK_STAT_COM];
+
+ zr->jpg_err_shift += i;
+ zr->jpg_err_shift &= BUZ_MASK_STAT_COM;
+ }
+ if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS)
+ zr->jpg_err_seq = zr->jpg_seq_num; /* + 1; */
+ zoran_restart(zr);
+}
+
+irqreturn_t
+zoran_irq (int irq,
+ void *dev_id)
+{
+ u32 stat, astat;
+ int count;
+ struct zoran *zr;
+ unsigned long flags;
+
+ zr = dev_id;
+ count = 0;
+
+ if (zr->testing) {
+ /* Testing interrupts */
+ spin_lock_irqsave(&zr->spinlock, flags);
+ while ((stat = count_reset_interrupt(zr))) {
+ if (count++ > 100) {
+ btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR);
+ dprintk(1,
+ KERN_ERR
+ "%s: IRQ lockup while testing, isr=0x%08x, cleared int mask\n",
+ ZR_DEVNAME(zr), stat);
+ wake_up_interruptible(&zr->test_q);
+ }
+ }
+ zr->last_isr = stat;
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+ return IRQ_HANDLED;
+ }
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+ while (1) {
+ /* get/clear interrupt status bits */
+ stat = count_reset_interrupt(zr);
+ astat = stat & IRQ_MASK;
+ if (!astat) {
+ break;
+ }
+ dprintk(4,
+ KERN_DEBUG
+ "zoran_irq: astat: 0x%08x, mask: 0x%08x\n",
+ astat, btread(ZR36057_ICR));
+ if (astat & zr->card.vsync_int) { // SW
+
+ if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS ||
+ zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) {
+ /* count missed interrupts */
+ zr->JPEG_missed++;
+ }
+ //post_office_read(zr,1,0);
+ /* Interrupts may still happen when
+ * zr->v4l_memgrab_active is switched off.
+ * We simply ignore them */
+
+ if (zr->v4l_memgrab_active) {
+ /* A lot more checks should be here ... */
+ if ((btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_SnapShot) == 0)
+ dprintk(1,
+ KERN_WARNING
+ "%s: BuzIRQ with SnapShot off ???\n",
+ ZR_DEVNAME(zr));
+
+ if (zr->v4l_grab_frame != NO_GRAB_ACTIVE) {
+ /* There is a grab on a frame going on, check if it has finished */
+ if ((btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_FrameGrab) == 0) {
+ /* it is finished, notify the user */
+
+ zr->v4l_buffers.buffer[zr->v4l_grab_frame].state = BUZ_STATE_DONE;
+ zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.seq = zr->v4l_grab_seq;
+ do_gettimeofday(&zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.timestamp);
+ zr->v4l_grab_frame = NO_GRAB_ACTIVE;
+ zr->v4l_pend_tail++;
+ }
+ }
+
+ if (zr->v4l_grab_frame == NO_GRAB_ACTIVE)
+ wake_up_interruptible(&zr->v4l_capq);
+
+ /* Check if there is another grab queued */
+
+ if (zr->v4l_grab_frame == NO_GRAB_ACTIVE &&
+ zr->v4l_pend_tail != zr->v4l_pend_head) {
+ int frame = zr->v4l_pend[zr->v4l_pend_tail & V4L_MASK_FRAME];
+ u32 reg;
+
+ zr->v4l_grab_frame = frame;
+
+ /* Set zr36057 video front end and enable video */
+
+ /* Buffer address */
+
+ reg = zr->v4l_buffers.buffer[frame].v4l.fbuffer_bus;
+ btwrite(reg, ZR36057_VDTR);
+ if (zr->v4l_settings.height > BUZ_MAX_HEIGHT / 2)
+ reg += zr->v4l_settings.bytesperline;
+ btwrite(reg, ZR36057_VDBR);
+
+ /* video stride, status, and frame grab register */
+ reg = 0;
+ if (zr->v4l_settings.height > BUZ_MAX_HEIGHT / 2)
+ reg += zr->v4l_settings.bytesperline;
+ reg = (reg << ZR36057_VSSFGR_DispStride);
+ reg |= ZR36057_VSSFGR_VidOvf;
+ reg |= ZR36057_VSSFGR_SnapShot;
+ reg |= ZR36057_VSSFGR_FrameGrab;
+ btwrite(reg, ZR36057_VSSFGR);
+
+ btor(ZR36057_VDCR_VidEn,
+ ZR36057_VDCR);
+ }
+ }
+
+ /* even if we don't grab, we do want to increment
+ * the sequence counter to see lost frames */
+ zr->v4l_grab_seq++;
+ }
+#if (IRQ_MASK & ZR36057_ISR_CodRepIRQ)
+ if (astat & ZR36057_ISR_CodRepIRQ) {
+ zr->intr_counter_CodRepIRQ++;
+ IDEBUG(printk(KERN_DEBUG "%s: ZR36057_ISR_CodRepIRQ\n",
+ ZR_DEVNAME(zr)));
+ btand(~ZR36057_ICR_CodRepIRQ, ZR36057_ICR);
+ }
+#endif /* (IRQ_MASK & ZR36057_ISR_CodRepIRQ) */
+
+#if (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ)
+ if ((astat & ZR36057_ISR_JPEGRepIRQ) &&
+ (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS ||
+ zr->codec_mode == BUZ_MODE_MOTION_COMPRESS)) {
+ if (zr36067_debug > 1 && (!zr->frame_num || zr->JPEG_error)) {
+ char sv[BUZ_NUM_STAT_COM + 1];
+ int i;
+
+ printk(KERN_INFO
+ "%s: first frame ready: state=0x%08x odd_even=%d field_per_buff=%d delay=%d\n",
+ ZR_DEVNAME(zr), stat,
+ zr->jpg_settings.odd_even,
+ zr->jpg_settings.field_per_buff,
+ zr->JPEG_missed);
+
+ for (i = 0; i < BUZ_NUM_STAT_COM; i++)
+ sv[i] = le32_to_cpu(zr->stat_com[i]) & 1 ? '1' : '0';
+ sv[BUZ_NUM_STAT_COM] = 0;
+ printk(KERN_INFO
+ "%s: stat_com=%s queue_state=%ld/%ld/%ld/%ld\n",
+ ZR_DEVNAME(zr), sv,
+ zr->jpg_que_tail,
+ zr->jpg_dma_tail,
+ zr->jpg_dma_head,
+ zr->jpg_que_head);
+ } else {
+ /* Get statistics */
+ if (zr->JPEG_missed > zr->JPEG_max_missed)
+ zr->JPEG_max_missed = zr->JPEG_missed;
+ if (zr->JPEG_missed < zr->JPEG_min_missed)
+ zr->JPEG_min_missed = zr->JPEG_missed;
+ }
+
+ if (zr36067_debug > 2 && zr->frame_num < 6) {
+ int i;
+
+ printk(KERN_INFO "%s: seq=%ld stat_com:",
+ ZR_DEVNAME(zr), zr->jpg_seq_num);
+ for (i = 0; i < 4; i++) {
+ printk(KERN_CONT " %08x",
+ le32_to_cpu(zr->stat_com[i]));
+ }
+ printk(KERN_CONT "\n");
+ }
+ zr->frame_num++;
+ zr->JPEG_missed = 0;
+ zr->JPEG_error = 0;
+ zoran_reap_stat_com(zr);
+ zoran_feed_stat_com(zr);
+ wake_up_interruptible(&zr->jpg_capq);
+ }
+#endif /* (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ) */
+
+ /* DATERR, too many fields missed, error processing */
+ if ((astat & zr->card.jpeg_int) ||
+ zr->JPEG_missed > 25 ||
+ zr->JPEG_error == 1 ||
+ ((zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) &&
+ (zr->frame_num && (zr->JPEG_missed > zr->jpg_settings.field_per_buff)))) {
+ error_handler(zr, astat, stat);
+ }
+
+ count++;
+ if (count > 10) {
+ dprintk(2, KERN_WARNING "%s: irq loop %d\n",
+ ZR_DEVNAME(zr), count);
+ if (count > 20) {
+ btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR);
+ dprintk(2,
+ KERN_ERR
+ "%s: IRQ lockup, cleared int mask\n",
+ ZR_DEVNAME(zr));
+ break;
+ }
+ }
+ zr->last_isr = stat;
+ }
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+
+ return IRQ_HANDLED;
+}
+
+void
+zoran_set_pci_master (struct zoran *zr,
+ int set_master)
+{
+ if (set_master) {
+ pci_set_master(zr->pci_dev);
+ } else {
+ u16 command;
+
+ pci_read_config_word(zr->pci_dev, PCI_COMMAND, &command);
+ command &= ~PCI_COMMAND_MASTER;
+ pci_write_config_word(zr->pci_dev, PCI_COMMAND, command);
+ }
+}
+
+void
+zoran_init_hardware (struct zoran *zr)
+{
+ /* Enable bus-mastering */
+ zoran_set_pci_master(zr, 1);
+
+ /* Initialize the board */
+ if (zr->card.init) {
+ zr->card.init(zr);
+ }
+
+ decoder_call(zr, core, init, 0);
+ decoder_call(zr, core, s_std, zr->norm);
+ decoder_call(zr, video, s_routing,
+ zr->card.input[zr->input].muxsel, 0, 0);
+
+ encoder_call(zr, core, init, 0);
+ encoder_call(zr, video, s_std_output, zr->norm);
+ encoder_call(zr, video, s_routing, 0, 0, 0);
+
+ /* toggle JPEG codec sleep to sync PLL */
+ jpeg_codec_sleep(zr, 1);
+ jpeg_codec_sleep(zr, 0);
+
+ /* set individual interrupt enables (without GIRQ1)
+ * but don't global enable until zoran_open() */
+
+ //btwrite(IRQ_MASK & ~ZR36057_ISR_GIRQ1, ZR36057_ICR); // SW
+ // It looks like using only JPEGRepIRQEn is not always reliable,
+ // may be when JPEG codec crashes it won't generate IRQ? So,
+ /*CP*/ // btwrite(IRQ_MASK, ZR36057_ICR); // Enable Vsync interrupts too. SM WHY ? LP
+ zr36057_init_vfe(zr);
+
+ zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
+
+ btwrite(IRQ_MASK, ZR36057_ISR); // Clears interrupts
+}
+
+void
+zr36057_restart (struct zoran *zr)
+{
+ btwrite(0, ZR36057_SPGPPCR);
+ mdelay(1);
+ btor(ZR36057_SPGPPCR_SoftReset, ZR36057_SPGPPCR);
+ mdelay(1);
+
+ /* assert P_Reset */
+ btwrite(0, ZR36057_JPC);
+ /* set up GPIO direction - all output */
+ btwrite(ZR36057_SPGPPCR_SoftReset | 0, ZR36057_SPGPPCR);
+
+ /* set up GPIO pins and guest bus timing */
+ btwrite((0x81 << 24) | 0x8888, ZR36057_GPPGCR1);
+}
+
+/*
+ * initialize video front end
+ */
+
+static void
+zr36057_init_vfe (struct zoran *zr)
+{
+ u32 reg;
+
+ reg = btread(ZR36057_VFESPFR);
+ reg |= ZR36057_VFESPFR_LittleEndian;
+ reg &= ~ZR36057_VFESPFR_VCLKPol;
+ reg |= ZR36057_VFESPFR_ExtFl;
+ reg |= ZR36057_VFESPFR_TopField;
+ btwrite(reg, ZR36057_VFESPFR);
+ reg = btread(ZR36057_VDCR);
+ if (pci_pci_problems & PCIPCI_TRITON)
+ // || zr->revision < 1) // Revision 1 has also Triton support
+ reg &= ~ZR36057_VDCR_Triton;
+ else
+ reg |= ZR36057_VDCR_Triton;
+ btwrite(reg, ZR36057_VDCR);
+}
diff --git a/drivers/media/video/zoran/zoran_device.h b/drivers/media/video/zoran/zoran_device.h
new file mode 100644
index 00000000..07f2c23f
--- /dev/null
+++ b/drivers/media/video/zoran/zoran_device.h
@@ -0,0 +1,95 @@
+/*
+ * Zoran zr36057/zr36067 PCI controller driver, for the
+ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
+ * Media Labs LML33/LML33R10.
+ *
+ * This part handles card-specific data and detection
+ *
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ *
+ * Currently maintained by:
+ * Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * Laurent Pinchart <laurent.pinchart@skynet.be>
+ * Mailinglist <mjpeg-users@lists.sf.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ZORAN_DEVICE_H__
+#define __ZORAN_DEVICE_H__
+
+/* general purpose I/O */
+extern void GPIO(struct zoran *zr,
+ int bit,
+ unsigned int value);
+
+/* codec (or actually: guest bus) access */
+extern int post_office_wait(struct zoran *zr);
+extern int post_office_write(struct zoran *zr,
+ unsigned guest,
+ unsigned reg,
+ unsigned value);
+extern int post_office_read(struct zoran *zr,
+ unsigned guest,
+ unsigned reg);
+
+extern void detect_guest_activity(struct zoran *zr);
+
+extern void jpeg_codec_sleep(struct zoran *zr,
+ int sleep);
+extern int jpeg_codec_reset(struct zoran *zr);
+
+/* zr360x7 access to raw capture */
+extern void zr36057_overlay(struct zoran *zr,
+ int on);
+extern void write_overlay_mask(struct zoran_fh *fh,
+ struct v4l2_clip *vp,
+ int count);
+extern void zr36057_set_memgrab(struct zoran *zr,
+ int mode);
+extern int wait_grab_pending(struct zoran *zr);
+
+/* interrupts */
+extern void print_interrupts(struct zoran *zr);
+extern void clear_interrupt_counters(struct zoran *zr);
+extern irqreturn_t zoran_irq(int irq, void *dev_id);
+
+/* JPEG codec access */
+extern void jpeg_start(struct zoran *zr);
+extern void zr36057_enable_jpg(struct zoran *zr,
+ enum zoran_codec_mode mode);
+extern void zoran_feed_stat_com(struct zoran *zr);
+
+/* general */
+extern void zoran_set_pci_master(struct zoran *zr,
+ int set_master);
+extern void zoran_init_hardware(struct zoran *zr);
+extern void zr36057_restart(struct zoran *zr);
+
+extern const struct zoran_format zoran_formats[];
+
+extern int v4l_nbufs;
+extern int v4l_bufsize;
+extern int jpg_nbufs;
+extern int jpg_bufsize;
+extern int pass_through;
+
+/* i2c */
+#define decoder_call(zr, o, f, args...) \
+ v4l2_subdev_call(zr->decoder, o, f, ##args)
+#define encoder_call(zr, o, f, args...) \
+ v4l2_subdev_call(zr->encoder, o, f, ##args)
+
+#endif /* __ZORAN_DEVICE_H__ */
diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c
new file mode 100644
index 00000000..4c09ab78
--- /dev/null
+++ b/drivers/media/video/zoran/zoran_driver.c
@@ -0,0 +1,3076 @@
+/*
+ * Zoran zr36057/zr36067 PCI controller driver, for the
+ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
+ * Media Labs LML33/LML33R10.
+ *
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ *
+ * Changes for BUZ by Wolfgang Scherr <scherr@net4you.net>
+ *
+ * Changes for DC10/DC30 by Laurent Pinchart <laurent.pinchart@skynet.be>
+ *
+ * Changes for LML33R10 by Maxim Yevtyushkin <max@linuxmedialabs.com>
+ *
+ * Changes for videodev2/v4l2 by Ronald Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * Based on
+ *
+ * Miro DC10 driver
+ * Copyright (C) 1999 Wolfgang Scherr <scherr@net4you.net>
+ *
+ * Iomega Buz driver version 1.0
+ * Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de>
+ *
+ * buz.0.0.3
+ * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+ *
+ * bttv - Bt848 frame grabber driver
+ * Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
+ * & Marcus Metzler (mocm@thp.uni-koeln.de)
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+
+#include <linux/spinlock.h>
+
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include "videocodec.h"
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/proc_fs.h>
+
+#include <linux/mutex.h>
+#include "zoran.h"
+#include "zoran_device.h"
+#include "zoran_card.h"
+
+
+const struct zoran_format zoran_formats[] = {
+ {
+ .name = "15-bit RGB LE",
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 15,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ .vfespfr = ZR36057_VFESPFR_RGB555|ZR36057_VFESPFR_ErrDif|
+ ZR36057_VFESPFR_LittleEndian,
+ }, {
+ .name = "15-bit RGB BE",
+ .fourcc = V4L2_PIX_FMT_RGB555X,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 15,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ .vfespfr = ZR36057_VFESPFR_RGB555|ZR36057_VFESPFR_ErrDif,
+ }, {
+ .name = "16-bit RGB LE",
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 16,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ .vfespfr = ZR36057_VFESPFR_RGB565|ZR36057_VFESPFR_ErrDif|
+ ZR36057_VFESPFR_LittleEndian,
+ }, {
+ .name = "16-bit RGB BE",
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 16,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ .vfespfr = ZR36057_VFESPFR_RGB565|ZR36057_VFESPFR_ErrDif,
+ }, {
+ .name = "24-bit RGB",
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 24,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ .vfespfr = ZR36057_VFESPFR_RGB888|ZR36057_VFESPFR_Pack24,
+ }, {
+ .name = "32-bit RGB LE",
+ .fourcc = V4L2_PIX_FMT_BGR32,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 32,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ .vfespfr = ZR36057_VFESPFR_RGB888|ZR36057_VFESPFR_LittleEndian,
+ }, {
+ .name = "32-bit RGB BE",
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 32,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ .vfespfr = ZR36057_VFESPFR_RGB888,
+ }, {
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ .depth = 16,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ .vfespfr = ZR36057_VFESPFR_YUV422,
+ }, {
+ .name = "4:2:2, packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ .depth = 16,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ .vfespfr = ZR36057_VFESPFR_YUV422|ZR36057_VFESPFR_LittleEndian,
+ }, {
+ .name = "Hardware-encoded Motion-JPEG",
+ .fourcc = V4L2_PIX_FMT_MJPEG,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ .depth = 0,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_PLAYBACK |
+ ZORAN_FORMAT_COMPRESSED,
+ }
+};
+#define NUM_FORMATS ARRAY_SIZE(zoran_formats)
+
+ /* small helper function for calculating buffersizes for v4l2
+ * we calculate the nearest higher power-of-two, which
+ * will be the recommended buffersize */
+static __u32
+zoran_v4l2_calc_bufsize (struct zoran_jpg_settings *settings)
+{
+ __u8 div = settings->VerDcm * settings->HorDcm * settings->TmpDcm;
+ __u32 num = (1024 * 512) / (div);
+ __u32 result = 2;
+
+ num--;
+ while (num) {
+ num >>= 1;
+ result <<= 1;
+ }
+
+ if (result > jpg_bufsize)
+ return jpg_bufsize;
+ if (result < 8192)
+ return 8192;
+ return result;
+}
+
+/* forward references */
+static void v4l_fbuffer_free(struct zoran_fh *fh);
+static void jpg_fbuffer_free(struct zoran_fh *fh);
+
+/* Set mapping mode */
+static void map_mode_raw(struct zoran_fh *fh)
+{
+ fh->map_mode = ZORAN_MAP_MODE_RAW;
+ fh->buffers.buffer_size = v4l_bufsize;
+ fh->buffers.num_buffers = v4l_nbufs;
+}
+static void map_mode_jpg(struct zoran_fh *fh, int play)
+{
+ fh->map_mode = play ? ZORAN_MAP_MODE_JPG_PLAY : ZORAN_MAP_MODE_JPG_REC;
+ fh->buffers.buffer_size = jpg_bufsize;
+ fh->buffers.num_buffers = jpg_nbufs;
+}
+static inline const char *mode_name(enum zoran_map_mode mode)
+{
+ return mode == ZORAN_MAP_MODE_RAW ? "V4L" : "JPG";
+}
+
+/*
+ * Allocate the V4L grab buffers
+ *
+ * These have to be pysically contiguous.
+ */
+
+static int v4l_fbuffer_alloc(struct zoran_fh *fh)
+{
+ struct zoran *zr = fh->zr;
+ int i, off;
+ unsigned char *mem;
+
+ for (i = 0; i < fh->buffers.num_buffers; i++) {
+ if (fh->buffers.buffer[i].v4l.fbuffer)
+ dprintk(2,
+ KERN_WARNING
+ "%s: %s - buffer %d already allocated!?\n",
+ ZR_DEVNAME(zr), __func__, i);
+
+ //udelay(20);
+ mem = kmalloc(fh->buffers.buffer_size,
+ GFP_KERNEL | __GFP_NOWARN);
+ if (!mem) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - kmalloc for V4L buf %d failed\n",
+ ZR_DEVNAME(zr), __func__, i);
+ v4l_fbuffer_free(fh);
+ return -ENOBUFS;
+ }
+ fh->buffers.buffer[i].v4l.fbuffer = mem;
+ fh->buffers.buffer[i].v4l.fbuffer_phys = virt_to_phys(mem);
+ fh->buffers.buffer[i].v4l.fbuffer_bus = virt_to_bus(mem);
+ for (off = 0; off < fh->buffers.buffer_size;
+ off += PAGE_SIZE)
+ SetPageReserved(virt_to_page(mem + off));
+ dprintk(4,
+ KERN_INFO
+ "%s: %s - V4L frame %d mem 0x%lx (bus: 0x%llx)\n",
+ ZR_DEVNAME(zr), __func__, i, (unsigned long) mem,
+ (unsigned long long)virt_to_bus(mem));
+ }
+
+ fh->buffers.allocated = 1;
+
+ return 0;
+}
+
+/* free the V4L grab buffers */
+static void v4l_fbuffer_free(struct zoran_fh *fh)
+{
+ struct zoran *zr = fh->zr;
+ int i, off;
+ unsigned char *mem;
+
+ dprintk(4, KERN_INFO "%s: %s\n", ZR_DEVNAME(zr), __func__);
+
+ for (i = 0; i < fh->buffers.num_buffers; i++) {
+ if (!fh->buffers.buffer[i].v4l.fbuffer)
+ continue;
+
+ mem = fh->buffers.buffer[i].v4l.fbuffer;
+ for (off = 0; off < fh->buffers.buffer_size;
+ off += PAGE_SIZE)
+ ClearPageReserved(virt_to_page(mem + off));
+ kfree(fh->buffers.buffer[i].v4l.fbuffer);
+ fh->buffers.buffer[i].v4l.fbuffer = NULL;
+ }
+
+ fh->buffers.allocated = 0;
+}
+
+/*
+ * Allocate the MJPEG grab buffers.
+ *
+ * If a Natoma chipset is present and this is a revision 1 zr36057,
+ * each MJPEG buffer needs to be physically contiguous.
+ * (RJ: This statement is from Dave Perks' original driver,
+ * I could never check it because I have a zr36067)
+ *
+ * RJ: The contents grab buffers needs never be accessed in the driver.
+ * Therefore there is no need to allocate them with vmalloc in order
+ * to get a contiguous virtual memory space.
+ * I don't understand why many other drivers first allocate them with
+ * vmalloc (which uses internally also get_zeroed_page, but delivers you
+ * virtual addresses) and then again have to make a lot of efforts
+ * to get the physical address.
+ *
+ * Ben Capper:
+ * On big-endian architectures (such as ppc) some extra steps
+ * are needed. When reading and writing to the stat_com array
+ * and fragment buffers, the device expects to see little-
+ * endian values. The use of cpu_to_le32() and le32_to_cpu()
+ * in this function (and one or two others in zoran_device.c)
+ * ensure that these values are always stored in little-endian
+ * form, regardless of architecture. The zr36057 does Very Bad
+ * Things on big endian architectures if the stat_com array
+ * and fragment buffers are not little-endian.
+ */
+
+static int jpg_fbuffer_alloc(struct zoran_fh *fh)
+{
+ struct zoran *zr = fh->zr;
+ int i, j, off;
+ u8 *mem;
+
+ for (i = 0; i < fh->buffers.num_buffers; i++) {
+ if (fh->buffers.buffer[i].jpg.frag_tab)
+ dprintk(2,
+ KERN_WARNING
+ "%s: %s - buffer %d already allocated!?\n",
+ ZR_DEVNAME(zr), __func__, i);
+
+ /* Allocate fragment table for this buffer */
+
+ mem = (void *)get_zeroed_page(GFP_KERNEL);
+ if (!mem) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - get_zeroed_page (frag_tab) failed for buffer %d\n",
+ ZR_DEVNAME(zr), __func__, i);
+ jpg_fbuffer_free(fh);
+ return -ENOBUFS;
+ }
+ fh->buffers.buffer[i].jpg.frag_tab = (__le32 *)mem;
+ fh->buffers.buffer[i].jpg.frag_tab_bus = virt_to_bus(mem);
+
+ if (fh->buffers.need_contiguous) {
+ mem = kmalloc(fh->buffers.buffer_size, GFP_KERNEL);
+ if (mem == NULL) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - kmalloc failed for buffer %d\n",
+ ZR_DEVNAME(zr), __func__, i);
+ jpg_fbuffer_free(fh);
+ return -ENOBUFS;
+ }
+ fh->buffers.buffer[i].jpg.frag_tab[0] =
+ cpu_to_le32(virt_to_bus(mem));
+ fh->buffers.buffer[i].jpg.frag_tab[1] =
+ cpu_to_le32((fh->buffers.buffer_size >> 1) | 1);
+ for (off = 0; off < fh->buffers.buffer_size; off += PAGE_SIZE)
+ SetPageReserved(virt_to_page(mem + off));
+ } else {
+ /* jpg_bufsize is already page aligned */
+ for (j = 0; j < fh->buffers.buffer_size / PAGE_SIZE; j++) {
+ mem = (void *)get_zeroed_page(GFP_KERNEL);
+ if (mem == NULL) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - get_zeroed_page failed for buffer %d\n",
+ ZR_DEVNAME(zr), __func__, i);
+ jpg_fbuffer_free(fh);
+ return -ENOBUFS;
+ }
+
+ fh->buffers.buffer[i].jpg.frag_tab[2 * j] =
+ cpu_to_le32(virt_to_bus(mem));
+ fh->buffers.buffer[i].jpg.frag_tab[2 * j + 1] =
+ cpu_to_le32((PAGE_SIZE >> 2) << 1);
+ SetPageReserved(virt_to_page(mem));
+ }
+
+ fh->buffers.buffer[i].jpg.frag_tab[2 * j - 1] |= cpu_to_le32(1);
+ }
+ }
+
+ dprintk(4,
+ KERN_DEBUG "%s: %s - %d KB allocated\n",
+ ZR_DEVNAME(zr), __func__,
+ (fh->buffers.num_buffers * fh->buffers.buffer_size) >> 10);
+
+ fh->buffers.allocated = 1;
+
+ return 0;
+}
+
+/* free the MJPEG grab buffers */
+static void jpg_fbuffer_free(struct zoran_fh *fh)
+{
+ struct zoran *zr = fh->zr;
+ int i, j, off;
+ unsigned char *mem;
+ __le32 frag_tab;
+ struct zoran_buffer *buffer;
+
+ dprintk(4, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
+
+ for (i = 0, buffer = &fh->buffers.buffer[0];
+ i < fh->buffers.num_buffers; i++, buffer++) {
+ if (!buffer->jpg.frag_tab)
+ continue;
+
+ if (fh->buffers.need_contiguous) {
+ frag_tab = buffer->jpg.frag_tab[0];
+
+ if (frag_tab) {
+ mem = bus_to_virt(le32_to_cpu(frag_tab));
+ for (off = 0; off < fh->buffers.buffer_size; off += PAGE_SIZE)
+ ClearPageReserved(virt_to_page(mem + off));
+ kfree(mem);
+ buffer->jpg.frag_tab[0] = 0;
+ buffer->jpg.frag_tab[1] = 0;
+ }
+ } else {
+ for (j = 0; j < fh->buffers.buffer_size / PAGE_SIZE; j++) {
+ frag_tab = buffer->jpg.frag_tab[2 * j];
+
+ if (!frag_tab)
+ break;
+ ClearPageReserved(virt_to_page(bus_to_virt(le32_to_cpu(frag_tab))));
+ free_page((unsigned long)bus_to_virt(le32_to_cpu(frag_tab)));
+ buffer->jpg.frag_tab[2 * j] = 0;
+ buffer->jpg.frag_tab[2 * j + 1] = 0;
+ }
+ }
+
+ free_page((unsigned long)buffer->jpg.frag_tab);
+ buffer->jpg.frag_tab = NULL;
+ }
+
+ fh->buffers.allocated = 0;
+}
+
+/*
+ * V4L Buffer grabbing
+ */
+
+static int
+zoran_v4l_set_format (struct zoran_fh *fh,
+ int width,
+ int height,
+ const struct zoran_format *format)
+{
+ struct zoran *zr = fh->zr;
+ int bpp;
+
+ /* Check size and format of the grab wanted */
+
+ if (height < BUZ_MIN_HEIGHT || width < BUZ_MIN_WIDTH ||
+ height > BUZ_MAX_HEIGHT || width > BUZ_MAX_WIDTH) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - wrong frame size (%dx%d)\n",
+ ZR_DEVNAME(zr), __func__, width, height);
+ return -EINVAL;
+ }
+
+ bpp = (format->depth + 7) / 8;
+
+ /* Check against available buffer size */
+ if (height * width * bpp > fh->buffers.buffer_size) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - video buffer size (%d kB) is too small\n",
+ ZR_DEVNAME(zr), __func__, fh->buffers.buffer_size >> 10);
+ return -EINVAL;
+ }
+
+ /* The video front end needs 4-byte alinged line sizes */
+
+ if ((bpp == 2 && (width & 1)) || (bpp == 3 && (width & 3))) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - wrong frame alignment\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+
+ fh->v4l_settings.width = width;
+ fh->v4l_settings.height = height;
+ fh->v4l_settings.format = format;
+ fh->v4l_settings.bytesperline = bpp * fh->v4l_settings.width;
+
+ return 0;
+}
+
+static int zoran_v4l_queue_frame(struct zoran_fh *fh, int num)
+{
+ struct zoran *zr = fh->zr;
+ unsigned long flags;
+ int res = 0;
+
+ if (!fh->buffers.allocated) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - buffers not yet allocated\n",
+ ZR_DEVNAME(zr), __func__);
+ res = -ENOMEM;
+ }
+
+ /* No grabbing outside the buffer range! */
+ if (num >= fh->buffers.num_buffers || num < 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - buffer %d is out of range\n",
+ ZR_DEVNAME(zr), __func__, num);
+ res = -EINVAL;
+ }
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+
+ if (fh->buffers.active == ZORAN_FREE) {
+ if (zr->v4l_buffers.active == ZORAN_FREE) {
+ zr->v4l_buffers = fh->buffers;
+ fh->buffers.active = ZORAN_ACTIVE;
+ } else {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - another session is already capturing\n",
+ ZR_DEVNAME(zr), __func__);
+ res = -EBUSY;
+ }
+ }
+
+ /* make sure a grab isn't going on currently with this buffer */
+ if (!res) {
+ switch (zr->v4l_buffers.buffer[num].state) {
+ default:
+ case BUZ_STATE_PEND:
+ if (zr->v4l_buffers.active == ZORAN_FREE) {
+ fh->buffers.active = ZORAN_FREE;
+ zr->v4l_buffers.allocated = 0;
+ }
+ res = -EBUSY; /* what are you doing? */
+ break;
+ case BUZ_STATE_DONE:
+ dprintk(2,
+ KERN_WARNING
+ "%s: %s - queueing buffer %d in state DONE!?\n",
+ ZR_DEVNAME(zr), __func__, num);
+ case BUZ_STATE_USER:
+ /* since there is at least one unused buffer there's room for at least
+ * one more pend[] entry */
+ zr->v4l_pend[zr->v4l_pend_head++ & V4L_MASK_FRAME] = num;
+ zr->v4l_buffers.buffer[num].state = BUZ_STATE_PEND;
+ zr->v4l_buffers.buffer[num].bs.length =
+ fh->v4l_settings.bytesperline *
+ zr->v4l_settings.height;
+ fh->buffers.buffer[num] = zr->v4l_buffers.buffer[num];
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+
+ if (!res && zr->v4l_buffers.active == ZORAN_FREE)
+ zr->v4l_buffers.active = fh->buffers.active;
+
+ return res;
+}
+
+/*
+ * Sync on a V4L buffer
+ */
+
+static int v4l_sync(struct zoran_fh *fh, int frame)
+{
+ struct zoran *zr = fh->zr;
+ unsigned long flags;
+
+ if (fh->buffers.active == ZORAN_FREE) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - no grab active for this session\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+
+ /* check passed-in frame number */
+ if (frame >= fh->buffers.num_buffers || frame < 0) {
+ dprintk(1,
+ KERN_ERR "%s: %s - frame %d is invalid\n",
+ ZR_DEVNAME(zr), __func__, frame);
+ return -EINVAL;
+ }
+
+ /* Check if is buffer was queued at all */
+ if (zr->v4l_buffers.buffer[frame].state == BUZ_STATE_USER) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - attempt to sync on a buffer which was not queued?\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EPROTO;
+ }
+
+ /* wait on this buffer to get ready */
+ if (!wait_event_interruptible_timeout(zr->v4l_capq,
+ (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_PEND), 10*HZ))
+ return -ETIME;
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+
+ /* buffer should now be in BUZ_STATE_DONE */
+ if (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_DONE)
+ dprintk(2,
+ KERN_ERR "%s: %s - internal state error\n",
+ ZR_DEVNAME(zr), __func__);
+
+ zr->v4l_buffers.buffer[frame].state = BUZ_STATE_USER;
+ fh->buffers.buffer[frame] = zr->v4l_buffers.buffer[frame];
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+
+ /* Check if streaming capture has finished */
+ if (zr->v4l_pend_tail == zr->v4l_pend_head) {
+ zr36057_set_memgrab(zr, 0);
+ if (zr->v4l_buffers.active == ZORAN_ACTIVE) {
+ fh->buffers.active = zr->v4l_buffers.active = ZORAN_FREE;
+ zr->v4l_buffers.allocated = 0;
+ }
+ }
+
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+
+ return 0;
+}
+
+/*
+ * Queue a MJPEG buffer for capture/playback
+ */
+
+static int zoran_jpg_queue_frame(struct zoran_fh *fh, int num,
+ enum zoran_codec_mode mode)
+{
+ struct zoran *zr = fh->zr;
+ unsigned long flags;
+ int res = 0;
+
+ /* Check if buffers are allocated */
+ if (!fh->buffers.allocated) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - buffers not yet allocated\n",
+ ZR_DEVNAME(zr), __func__);
+ return -ENOMEM;
+ }
+
+ /* No grabbing outside the buffer range! */
+ if (num >= fh->buffers.num_buffers || num < 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - buffer %d out of range\n",
+ ZR_DEVNAME(zr), __func__, num);
+ return -EINVAL;
+ }
+
+ /* what is the codec mode right now? */
+ if (zr->codec_mode == BUZ_MODE_IDLE) {
+ zr->jpg_settings = fh->jpg_settings;
+ } else if (zr->codec_mode != mode) {
+ /* wrong codec mode active - invalid */
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - codec in wrong mode\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+
+ if (fh->buffers.active == ZORAN_FREE) {
+ if (zr->jpg_buffers.active == ZORAN_FREE) {
+ zr->jpg_buffers = fh->buffers;
+ fh->buffers.active = ZORAN_ACTIVE;
+ } else {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - another session is already capturing\n",
+ ZR_DEVNAME(zr), __func__);
+ res = -EBUSY;
+ }
+ }
+
+ if (!res && zr->codec_mode == BUZ_MODE_IDLE) {
+ /* Ok load up the jpeg codec */
+ zr36057_enable_jpg(zr, mode);
+ }
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+
+ if (!res) {
+ switch (zr->jpg_buffers.buffer[num].state) {
+ case BUZ_STATE_DONE:
+ dprintk(2,
+ KERN_WARNING
+ "%s: %s - queing frame in BUZ_STATE_DONE state!?\n",
+ ZR_DEVNAME(zr), __func__);
+ case BUZ_STATE_USER:
+ /* since there is at least one unused buffer there's room for at
+ *least one more pend[] entry */
+ zr->jpg_pend[zr->jpg_que_head++ & BUZ_MASK_FRAME] = num;
+ zr->jpg_buffers.buffer[num].state = BUZ_STATE_PEND;
+ fh->buffers.buffer[num] = zr->jpg_buffers.buffer[num];
+ zoran_feed_stat_com(zr);
+ break;
+ default:
+ case BUZ_STATE_DMA:
+ case BUZ_STATE_PEND:
+ if (zr->jpg_buffers.active == ZORAN_FREE) {
+ fh->buffers.active = ZORAN_FREE;
+ zr->jpg_buffers.allocated = 0;
+ }
+ res = -EBUSY; /* what are you doing? */
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+
+ if (!res && zr->jpg_buffers.active == ZORAN_FREE)
+ zr->jpg_buffers.active = fh->buffers.active;
+
+ return res;
+}
+
+static int jpg_qbuf(struct zoran_fh *fh, int frame, enum zoran_codec_mode mode)
+{
+ struct zoran *zr = fh->zr;
+ int res = 0;
+
+ /* Does the user want to stop streaming? */
+ if (frame < 0) {
+ if (zr->codec_mode == mode) {
+ if (fh->buffers.active == ZORAN_FREE) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s(-1) - session not active\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+ fh->buffers.active = zr->jpg_buffers.active = ZORAN_FREE;
+ zr->jpg_buffers.allocated = 0;
+ zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
+ return 0;
+ } else {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - stop streaming but not in streaming mode\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+ }
+
+ if ((res = zoran_jpg_queue_frame(fh, frame, mode)))
+ return res;
+
+ /* Start the jpeg codec when the first frame is queued */
+ if (!res && zr->jpg_que_head == 1)
+ jpeg_start(zr);
+
+ return res;
+}
+
+/*
+ * Sync on a MJPEG buffer
+ */
+
+static int jpg_sync(struct zoran_fh *fh, struct zoran_sync *bs)
+{
+ struct zoran *zr = fh->zr;
+ unsigned long flags;
+ int frame;
+
+ if (fh->buffers.active == ZORAN_FREE) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - capture is not currently active\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+ if (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS &&
+ zr->codec_mode != BUZ_MODE_MOTION_COMPRESS) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - codec not in streaming mode\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+ if (!wait_event_interruptible_timeout(zr->jpg_capq,
+ (zr->jpg_que_tail != zr->jpg_dma_tail ||
+ zr->jpg_dma_tail == zr->jpg_dma_head),
+ 10*HZ)) {
+ int isr;
+
+ btand(~ZR36057_JMC_Go_en, ZR36057_JMC);
+ udelay(1);
+ zr->codec->control(zr->codec, CODEC_G_STATUS,
+ sizeof(isr), &isr);
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - timeout: codec isr=0x%02x\n",
+ ZR_DEVNAME(zr), __func__, isr);
+
+ return -ETIME;
+
+ }
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+
+ if (zr->jpg_dma_tail != zr->jpg_dma_head)
+ frame = zr->jpg_pend[zr->jpg_que_tail++ & BUZ_MASK_FRAME];
+ else
+ frame = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME];
+
+ /* buffer should now be in BUZ_STATE_DONE */
+ if (zr->jpg_buffers.buffer[frame].state != BUZ_STATE_DONE)
+ dprintk(2,
+ KERN_ERR "%s: %s - internal state error\n",
+ ZR_DEVNAME(zr), __func__);
+
+ *bs = zr->jpg_buffers.buffer[frame].bs;
+ bs->frame = frame;
+ zr->jpg_buffers.buffer[frame].state = BUZ_STATE_USER;
+ fh->buffers.buffer[frame] = zr->jpg_buffers.buffer[frame];
+
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+
+ return 0;
+}
+
+static void zoran_open_init_session(struct zoran_fh *fh)
+{
+ int i;
+ struct zoran *zr = fh->zr;
+
+ /* Per default, map the V4L Buffers */
+ map_mode_raw(fh);
+
+ /* take over the card's current settings */
+ fh->overlay_settings = zr->overlay_settings;
+ fh->overlay_settings.is_set = 0;
+ fh->overlay_settings.format = zr->overlay_settings.format;
+ fh->overlay_active = ZORAN_FREE;
+
+ /* v4l settings */
+ fh->v4l_settings = zr->v4l_settings;
+ /* jpg settings */
+ fh->jpg_settings = zr->jpg_settings;
+
+ /* buffers */
+ memset(&fh->buffers, 0, sizeof(fh->buffers));
+ for (i = 0; i < MAX_FRAME; i++) {
+ fh->buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */
+ fh->buffers.buffer[i].bs.frame = i;
+ }
+ fh->buffers.allocated = 0;
+ fh->buffers.active = ZORAN_FREE;
+}
+
+static void zoran_close_end_session(struct zoran_fh *fh)
+{
+ struct zoran *zr = fh->zr;
+
+ /* overlay */
+ if (fh->overlay_active != ZORAN_FREE) {
+ fh->overlay_active = zr->overlay_active = ZORAN_FREE;
+ zr->v4l_overlay_active = 0;
+ if (!zr->v4l_memgrab_active)
+ zr36057_overlay(zr, 0);
+ zr->overlay_mask = NULL;
+ }
+
+ if (fh->map_mode == ZORAN_MAP_MODE_RAW) {
+ /* v4l capture */
+ if (fh->buffers.active != ZORAN_FREE) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+ zr36057_set_memgrab(zr, 0);
+ zr->v4l_buffers.allocated = 0;
+ zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE;
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+ }
+
+ /* v4l buffers */
+ if (fh->buffers.allocated)
+ v4l_fbuffer_free(fh);
+ } else {
+ /* jpg capture */
+ if (fh->buffers.active != ZORAN_FREE) {
+ zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
+ zr->jpg_buffers.allocated = 0;
+ zr->jpg_buffers.active = fh->buffers.active = ZORAN_FREE;
+ }
+
+ /* jpg buffers */
+ if (fh->buffers.allocated)
+ jpg_fbuffer_free(fh);
+ }
+}
+
+/*
+ * Open a zoran card. Right now the flags stuff is just playing
+ */
+
+static int zoran_open(struct file *file)
+{
+ struct zoran *zr = video_drvdata(file);
+ struct zoran_fh *fh;
+ int res, first_open = 0;
+
+ dprintk(2, KERN_INFO "%s: %s(%s, pid=[%d]), users(-)=%d\n",
+ ZR_DEVNAME(zr), __func__, current->comm, task_pid_nr(current), zr->user + 1);
+
+ mutex_lock(&zr->other_lock);
+
+ if (zr->user >= 2048) {
+ dprintk(1, KERN_ERR "%s: too many users (%d) on device\n",
+ ZR_DEVNAME(zr), zr->user);
+ res = -EBUSY;
+ goto fail_unlock;
+ }
+
+ /* now, create the open()-specific file_ops struct */
+ fh = kzalloc(sizeof(struct zoran_fh), GFP_KERNEL);
+ if (!fh) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - allocation of zoran_fh failed\n",
+ ZR_DEVNAME(zr), __func__);
+ res = -ENOMEM;
+ goto fail_unlock;
+ }
+ /* used to be BUZ_MAX_WIDTH/HEIGHT, but that gives overflows
+ * on norm-change! */
+ fh->overlay_mask =
+ kmalloc(((768 + 31) / 32) * 576 * 4, GFP_KERNEL);
+ if (!fh->overlay_mask) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - allocation of overlay_mask failed\n",
+ ZR_DEVNAME(zr), __func__);
+ res = -ENOMEM;
+ goto fail_fh;
+ }
+
+ if (zr->user++ == 0)
+ first_open = 1;
+
+ /*mutex_unlock(&zr->resource_lock);*/
+
+ /* default setup - TODO: look at flags */
+ if (first_open) { /* First device open */
+ zr36057_restart(zr);
+ zoran_open_init_params(zr);
+ zoran_init_hardware(zr);
+
+ btor(ZR36057_ICR_IntPinEn, ZR36057_ICR);
+ }
+
+ /* set file_ops stuff */
+ file->private_data = fh;
+ fh->zr = zr;
+ zoran_open_init_session(fh);
+ mutex_unlock(&zr->other_lock);
+
+ return 0;
+
+fail_fh:
+ kfree(fh);
+fail_unlock:
+ mutex_unlock(&zr->other_lock);
+
+ dprintk(2, KERN_INFO "%s: open failed (%d), users(-)=%d\n",
+ ZR_DEVNAME(zr), res, zr->user);
+
+ return res;
+}
+
+static int
+zoran_close(struct file *file)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+
+ dprintk(2, KERN_INFO "%s: %s(%s, pid=[%d]), users(+)=%d\n",
+ ZR_DEVNAME(zr), __func__, current->comm, task_pid_nr(current), zr->user - 1);
+
+ /* kernel locks (fs/device.c), so don't do that ourselves
+ * (prevents deadlocks) */
+ mutex_lock(&zr->other_lock);
+
+ zoran_close_end_session(fh);
+
+ if (zr->user-- == 1) { /* Last process */
+ /* Clean up JPEG process */
+ wake_up_interruptible(&zr->jpg_capq);
+ zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
+ zr->jpg_buffers.allocated = 0;
+ zr->jpg_buffers.active = ZORAN_FREE;
+
+ /* disable interrupts */
+ btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR);
+
+ if (zr36067_debug > 1)
+ print_interrupts(zr);
+
+ /* Overlay off */
+ zr->v4l_overlay_active = 0;
+ zr36057_overlay(zr, 0);
+ zr->overlay_mask = NULL;
+
+ /* capture off */
+ wake_up_interruptible(&zr->v4l_capq);
+ zr36057_set_memgrab(zr, 0);
+ zr->v4l_buffers.allocated = 0;
+ zr->v4l_buffers.active = ZORAN_FREE;
+ zoran_set_pci_master(zr, 0);
+
+ if (!pass_through) { /* Switch to color bar */
+ decoder_call(zr, video, s_stream, 0);
+ encoder_call(zr, video, s_routing, 2, 0, 0);
+ }
+ }
+ mutex_unlock(&zr->other_lock);
+
+ file->private_data = NULL;
+ kfree(fh->overlay_mask);
+ kfree(fh);
+
+ dprintk(4, KERN_INFO "%s: %s done\n", ZR_DEVNAME(zr), __func__);
+
+ return 0;
+}
+
+
+static ssize_t
+zoran_read (struct file *file,
+ char __user *data,
+ size_t count,
+ loff_t *ppos)
+{
+ /* we simply don't support read() (yet)... */
+
+ return -EINVAL;
+}
+
+static ssize_t
+zoran_write (struct file *file,
+ const char __user *data,
+ size_t count,
+ loff_t *ppos)
+{
+ /* ...and the same goes for write() */
+
+ return -EINVAL;
+}
+
+static int setup_fbuffer(struct zoran_fh *fh,
+ void *base,
+ const struct zoran_format *fmt,
+ int width,
+ int height,
+ int bytesperline)
+{
+ struct zoran *zr = fh->zr;
+
+ /* (Ronald) v4l/v4l2 guidelines */
+ if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ /* Don't allow frame buffer overlay if PCI or AGP is buggy, or on
+ ALi Magik (that needs very low latency while the card needs a
+ higher value always) */
+
+ if (pci_pci_problems & (PCIPCI_FAIL | PCIAGP_FAIL | PCIPCI_ALIMAGIK))
+ return -ENXIO;
+
+ /* we need a bytesperline value, even if not given */
+ if (!bytesperline)
+ bytesperline = width * ((fmt->depth + 7) & ~7) / 8;
+
+#if 0
+ if (zr->overlay_active) {
+ /* dzjee... stupid users... don't even bother to turn off
+ * overlay before changing the memory location...
+ * normally, we would return errors here. However, one of
+ * the tools that does this is... xawtv! and since xawtv
+ * is used by +/- 99% of the users, we'd rather be user-
+ * friendly and silently do as if nothing went wrong */
+ dprintk(3,
+ KERN_ERR
+ "%s: %s - forced overlay turnoff because framebuffer changed\n",
+ ZR_DEVNAME(zr), __func__);
+ zr36057_overlay(zr, 0);
+ }
+#endif
+
+ if (!(fmt->flags & ZORAN_FORMAT_OVERLAY)) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - no valid overlay format given\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+ if (height <= 0 || width <= 0 || bytesperline <= 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - invalid height/width/bpl value (%d|%d|%d)\n",
+ ZR_DEVNAME(zr), __func__, width, height, bytesperline);
+ return -EINVAL;
+ }
+ if (bytesperline & 3) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - bytesperline (%d) must be 4-byte aligned\n",
+ ZR_DEVNAME(zr), __func__, bytesperline);
+ return -EINVAL;
+ }
+
+ zr->vbuf_base = (void *) ((unsigned long) base & ~3);
+ zr->vbuf_height = height;
+ zr->vbuf_width = width;
+ zr->vbuf_depth = fmt->depth;
+ zr->overlay_settings.format = fmt;
+ zr->vbuf_bytesperline = bytesperline;
+
+ /* The user should set new window parameters */
+ zr->overlay_settings.is_set = 0;
+
+ return 0;
+}
+
+
+static int setup_window(struct zoran_fh *fh, int x, int y, int width, int height,
+ struct v4l2_clip __user *clips, int clipcount, void __user *bitmap)
+{
+ struct zoran *zr = fh->zr;
+ struct v4l2_clip *vcp = NULL;
+ int on, end;
+
+
+ if (!zr->vbuf_base) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - frame buffer has to be set first\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+
+ if (!fh->overlay_settings.format) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - no overlay format set\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * The video front end needs 4-byte alinged line sizes, we correct that
+ * silently here if necessary
+ */
+ if (zr->vbuf_depth == 15 || zr->vbuf_depth == 16) {
+ end = (x + width) & ~1; /* round down */
+ x = (x + 1) & ~1; /* round up */
+ width = end - x;
+ }
+
+ if (zr->vbuf_depth == 24) {
+ end = (x + width) & ~3; /* round down */
+ x = (x + 3) & ~3; /* round up */
+ width = end - x;
+ }
+
+ if (width > BUZ_MAX_WIDTH)
+ width = BUZ_MAX_WIDTH;
+ if (height > BUZ_MAX_HEIGHT)
+ height = BUZ_MAX_HEIGHT;
+
+ /* Check for invalid parameters */
+ if (width < BUZ_MIN_WIDTH || height < BUZ_MIN_HEIGHT ||
+ width > BUZ_MAX_WIDTH || height > BUZ_MAX_HEIGHT) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - width = %d or height = %d invalid\n",
+ ZR_DEVNAME(zr), __func__, width, height);
+ return -EINVAL;
+ }
+
+ fh->overlay_settings.x = x;
+ fh->overlay_settings.y = y;
+ fh->overlay_settings.width = width;
+ fh->overlay_settings.height = height;
+ fh->overlay_settings.clipcount = clipcount;
+
+ /*
+ * If an overlay is running, we have to switch it off
+ * and switch it on again in order to get the new settings in effect.
+ *
+ * We also want to avoid that the overlay mask is written
+ * when an overlay is running.
+ */
+
+ on = zr->v4l_overlay_active && !zr->v4l_memgrab_active &&
+ zr->overlay_active != ZORAN_FREE &&
+ fh->overlay_active != ZORAN_FREE;
+ if (on)
+ zr36057_overlay(zr, 0);
+
+ /*
+ * Write the overlay mask if clips are wanted.
+ * We prefer a bitmap.
+ */
+ if (bitmap) {
+ /* fake value - it just means we want clips */
+ fh->overlay_settings.clipcount = 1;
+
+ if (copy_from_user(fh->overlay_mask, bitmap,
+ (width * height + 7) / 8)) {
+ return -EFAULT;
+ }
+ } else if (clipcount > 0) {
+ /* write our own bitmap from the clips */
+ vcp = vmalloc(sizeof(struct v4l2_clip) * (clipcount + 4));
+ if (vcp == NULL) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - Alloc of clip mask failed\n",
+ ZR_DEVNAME(zr), __func__);
+ return -ENOMEM;
+ }
+ if (copy_from_user
+ (vcp, clips, sizeof(struct v4l2_clip) * clipcount)) {
+ vfree(vcp);
+ return -EFAULT;
+ }
+ write_overlay_mask(fh, vcp, clipcount);
+ vfree(vcp);
+ }
+
+ fh->overlay_settings.is_set = 1;
+ if (fh->overlay_active != ZORAN_FREE &&
+ zr->overlay_active != ZORAN_FREE)
+ zr->overlay_settings = fh->overlay_settings;
+
+ if (on)
+ zr36057_overlay(zr, 1);
+
+ /* Make sure the changes come into effect */
+ return wait_grab_pending(zr);
+}
+
+static int setup_overlay(struct zoran_fh *fh, int on)
+{
+ struct zoran *zr = fh->zr;
+
+ /* If there is nothing to do, return immediately */
+ if ((on && fh->overlay_active != ZORAN_FREE) ||
+ (!on && fh->overlay_active == ZORAN_FREE))
+ return 0;
+
+ /* check whether we're touching someone else's overlay */
+ if (on && zr->overlay_active != ZORAN_FREE &&
+ fh->overlay_active == ZORAN_FREE) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - overlay is already active for another session\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EBUSY;
+ }
+ if (!on && zr->overlay_active != ZORAN_FREE &&
+ fh->overlay_active == ZORAN_FREE) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - you cannot cancel someone else's session\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EPERM;
+ }
+
+ if (on == 0) {
+ zr->overlay_active = fh->overlay_active = ZORAN_FREE;
+ zr->v4l_overlay_active = 0;
+ /* When a grab is running, the video simply
+ * won't be switched on any more */
+ if (!zr->v4l_memgrab_active)
+ zr36057_overlay(zr, 0);
+ zr->overlay_mask = NULL;
+ } else {
+ if (!zr->vbuf_base || !fh->overlay_settings.is_set) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - buffer or window not set\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+ if (!fh->overlay_settings.format) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - no overlay format set\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+ zr->overlay_active = fh->overlay_active = ZORAN_LOCKED;
+ zr->v4l_overlay_active = 1;
+ zr->overlay_mask = fh->overlay_mask;
+ zr->overlay_settings = fh->overlay_settings;
+ if (!zr->v4l_memgrab_active)
+ zr36057_overlay(zr, 1);
+ /* When a grab is running, the video will be
+ * switched on when grab is finished */
+ }
+
+ /* Make sure the changes come into effect */
+ return wait_grab_pending(zr);
+}
+
+/* get the status of a buffer in the clients buffer queue */
+static int zoran_v4l2_buffer_status(struct zoran_fh *fh,
+ struct v4l2_buffer *buf, int num)
+{
+ struct zoran *zr = fh->zr;
+ unsigned long flags;
+
+ buf->flags = V4L2_BUF_FLAG_MAPPED;
+
+ switch (fh->map_mode) {
+ case ZORAN_MAP_MODE_RAW:
+ /* check range */
+ if (num < 0 || num >= fh->buffers.num_buffers ||
+ !fh->buffers.allocated) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - wrong number or buffers not allocated\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+ dprintk(3,
+ KERN_DEBUG
+ "%s: %s() - raw active=%c, buffer %d: state=%c, map=%c\n",
+ ZR_DEVNAME(zr), __func__,
+ "FAL"[fh->buffers.active], num,
+ "UPMD"[zr->v4l_buffers.buffer[num].state],
+ fh->buffers.buffer[num].map ? 'Y' : 'N');
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+
+ buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf->length = fh->buffers.buffer_size;
+
+ /* get buffer */
+ buf->bytesused = fh->buffers.buffer[num].bs.length;
+ if (fh->buffers.buffer[num].state == BUZ_STATE_DONE ||
+ fh->buffers.buffer[num].state == BUZ_STATE_USER) {
+ buf->sequence = fh->buffers.buffer[num].bs.seq;
+ buf->flags |= V4L2_BUF_FLAG_DONE;
+ buf->timestamp = fh->buffers.buffer[num].bs.timestamp;
+ } else {
+ buf->flags |= V4L2_BUF_FLAG_QUEUED;
+ }
+
+ if (fh->v4l_settings.height <= BUZ_MAX_HEIGHT / 2)
+ buf->field = V4L2_FIELD_TOP;
+ else
+ buf->field = V4L2_FIELD_INTERLACED;
+
+ break;
+
+ case ZORAN_MAP_MODE_JPG_REC:
+ case ZORAN_MAP_MODE_JPG_PLAY:
+
+ /* check range */
+ if (num < 0 || num >= fh->buffers.num_buffers ||
+ !fh->buffers.allocated) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - wrong number or buffers not allocated\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+
+ buf->type = (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) ?
+ V4L2_BUF_TYPE_VIDEO_CAPTURE :
+ V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ buf->length = fh->buffers.buffer_size;
+
+ /* these variables are only written after frame has been captured */
+ if (fh->buffers.buffer[num].state == BUZ_STATE_DONE ||
+ fh->buffers.buffer[num].state == BUZ_STATE_USER) {
+ buf->sequence = fh->buffers.buffer[num].bs.seq;
+ buf->timestamp = fh->buffers.buffer[num].bs.timestamp;
+ buf->bytesused = fh->buffers.buffer[num].bs.length;
+ buf->flags |= V4L2_BUF_FLAG_DONE;
+ } else {
+ buf->flags |= V4L2_BUF_FLAG_QUEUED;
+ }
+
+ /* which fields are these? */
+ if (fh->jpg_settings.TmpDcm != 1)
+ buf->field = fh->jpg_settings.odd_even ?
+ V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM;
+ else
+ buf->field = fh->jpg_settings.odd_even ?
+ V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT;
+
+ break;
+
+ default:
+
+ dprintk(5,
+ KERN_ERR
+ "%s: %s - invalid buffer type|map_mode (%d|%d)\n",
+ ZR_DEVNAME(zr), __func__, buf->type, fh->map_mode);
+ return -EINVAL;
+ }
+
+ buf->memory = V4L2_MEMORY_MMAP;
+ buf->index = num;
+ buf->m.offset = buf->length * num;
+
+ return 0;
+}
+
+static int
+zoran_set_norm (struct zoran *zr,
+ v4l2_std_id norm)
+{
+ int on;
+
+ if (zr->v4l_buffers.active != ZORAN_FREE ||
+ zr->jpg_buffers.active != ZORAN_FREE) {
+ dprintk(1,
+ KERN_WARNING
+ "%s: %s called while in playback/capture mode\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EBUSY;
+ }
+
+ if (!(norm & zr->card.norms)) {
+ dprintk(1,
+ KERN_ERR "%s: %s - unsupported norm %llx\n",
+ ZR_DEVNAME(zr), __func__, norm);
+ return -EINVAL;
+ }
+
+ if (norm == V4L2_STD_ALL) {
+ unsigned int status = 0;
+ v4l2_std_id std = 0;
+
+ decoder_call(zr, video, querystd, &std);
+ decoder_call(zr, core, s_std, std);
+
+ /* let changes come into effect */
+ ssleep(2);
+
+ decoder_call(zr, video, g_input_status, &status);
+ if (status & V4L2_IN_ST_NO_SIGNAL) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - no norm detected\n",
+ ZR_DEVNAME(zr), __func__);
+ /* reset norm */
+ decoder_call(zr, core, s_std, zr->norm);
+ return -EIO;
+ }
+
+ norm = std;
+ }
+ if (norm & V4L2_STD_SECAM)
+ zr->timing = zr->card.tvn[2];
+ else if (norm & V4L2_STD_NTSC)
+ zr->timing = zr->card.tvn[1];
+ else
+ zr->timing = zr->card.tvn[0];
+
+ /* We switch overlay off and on since a change in the
+ * norm needs different VFE settings */
+ on = zr->overlay_active && !zr->v4l_memgrab_active;
+ if (on)
+ zr36057_overlay(zr, 0);
+
+ decoder_call(zr, core, s_std, norm);
+ encoder_call(zr, video, s_std_output, norm);
+
+ if (on)
+ zr36057_overlay(zr, 1);
+
+ /* Make sure the changes come into effect */
+ zr->norm = norm;
+
+ return 0;
+}
+
+static int
+zoran_set_input (struct zoran *zr,
+ int input)
+{
+ if (input == zr->input) {
+ return 0;
+ }
+
+ if (zr->v4l_buffers.active != ZORAN_FREE ||
+ zr->jpg_buffers.active != ZORAN_FREE) {
+ dprintk(1,
+ KERN_WARNING
+ "%s: %s called while in playback/capture mode\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EBUSY;
+ }
+
+ if (input < 0 || input >= zr->card.inputs) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - unnsupported input %d\n",
+ ZR_DEVNAME(zr), __func__, input);
+ return -EINVAL;
+ }
+
+ zr->input = input;
+
+ decoder_call(zr, video, s_routing,
+ zr->card.input[input].muxsel, 0, 0);
+
+ return 0;
+}
+
+/*
+ * ioctl routine
+ */
+
+static int zoran_querycap(struct file *file, void *__fh, struct v4l2_capability *cap)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ memset(cap, 0, sizeof(*cap));
+ strncpy(cap->card, ZR_DEVNAME(zr), sizeof(cap->card)-1);
+ strncpy(cap->driver, "zoran", sizeof(cap->driver)-1);
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s",
+ pci_name(zr->pci_dev));
+ cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OVERLAY;
+ return 0;
+}
+
+static int zoran_enum_fmt(struct zoran *zr, struct v4l2_fmtdesc *fmt, int flag)
+{
+ unsigned int num, i;
+
+ for (num = i = 0; i < NUM_FORMATS; i++) {
+ if (zoran_formats[i].flags & flag && num++ == fmt->index) {
+ strncpy(fmt->description, zoran_formats[i].name,
+ sizeof(fmt->description) - 1);
+ /* fmt struct pre-zeroed, so adding '\0' not needed */
+ fmt->pixelformat = zoran_formats[i].fourcc;
+ if (zoran_formats[i].flags & ZORAN_FORMAT_COMPRESSED)
+ fmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int zoran_enum_fmt_vid_cap(struct file *file, void *__fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ return zoran_enum_fmt(zr, f, ZORAN_FORMAT_CAPTURE);
+}
+
+static int zoran_enum_fmt_vid_out(struct file *file, void *__fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ return zoran_enum_fmt(zr, f, ZORAN_FORMAT_PLAYBACK);
+}
+
+static int zoran_enum_fmt_vid_overlay(struct file *file, void *__fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ return zoran_enum_fmt(zr, f, ZORAN_FORMAT_OVERLAY);
+}
+
+static int zoran_g_fmt_vid_out(struct file *file, void *__fh,
+ struct v4l2_format *fmt)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ mutex_lock(&zr->resource_lock);
+
+ fmt->fmt.pix.width = fh->jpg_settings.img_width / fh->jpg_settings.HorDcm;
+ fmt->fmt.pix.height = fh->jpg_settings.img_height * 2 /
+ (fh->jpg_settings.VerDcm * fh->jpg_settings.TmpDcm);
+ fmt->fmt.pix.sizeimage = zoran_v4l2_calc_bufsize(&fh->jpg_settings);
+ fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
+ if (fh->jpg_settings.TmpDcm == 1)
+ fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
+ V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT);
+ else
+ fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
+ V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
+ fmt->fmt.pix.bytesperline = 0;
+ fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+ mutex_unlock(&zr->resource_lock);
+ return 0;
+}
+
+static int zoran_g_fmt_vid_cap(struct file *file, void *__fh,
+ struct v4l2_format *fmt)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ if (fh->map_mode != ZORAN_MAP_MODE_RAW)
+ return zoran_g_fmt_vid_out(file, fh, fmt);
+
+ mutex_lock(&zr->resource_lock);
+ fmt->fmt.pix.width = fh->v4l_settings.width;
+ fmt->fmt.pix.height = fh->v4l_settings.height;
+ fmt->fmt.pix.sizeimage = fh->v4l_settings.bytesperline *
+ fh->v4l_settings.height;
+ fmt->fmt.pix.pixelformat = fh->v4l_settings.format->fourcc;
+ fmt->fmt.pix.colorspace = fh->v4l_settings.format->colorspace;
+ fmt->fmt.pix.bytesperline = fh->v4l_settings.bytesperline;
+ if (BUZ_MAX_HEIGHT < (fh->v4l_settings.height * 2))
+ fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
+ else
+ fmt->fmt.pix.field = V4L2_FIELD_TOP;
+ mutex_unlock(&zr->resource_lock);
+ return 0;
+}
+
+static int zoran_g_fmt_vid_overlay(struct file *file, void *__fh,
+ struct v4l2_format *fmt)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ mutex_lock(&zr->resource_lock);
+
+ fmt->fmt.win.w.left = fh->overlay_settings.x;
+ fmt->fmt.win.w.top = fh->overlay_settings.y;
+ fmt->fmt.win.w.width = fh->overlay_settings.width;
+ fmt->fmt.win.w.height = fh->overlay_settings.height;
+ if (fh->overlay_settings.width * 2 > BUZ_MAX_HEIGHT)
+ fmt->fmt.win.field = V4L2_FIELD_INTERLACED;
+ else
+ fmt->fmt.win.field = V4L2_FIELD_TOP;
+
+ mutex_unlock(&zr->resource_lock);
+ return 0;
+}
+
+static int zoran_try_fmt_vid_overlay(struct file *file, void *__fh,
+ struct v4l2_format *fmt)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ mutex_lock(&zr->resource_lock);
+
+ if (fmt->fmt.win.w.width > BUZ_MAX_WIDTH)
+ fmt->fmt.win.w.width = BUZ_MAX_WIDTH;
+ if (fmt->fmt.win.w.width < BUZ_MIN_WIDTH)
+ fmt->fmt.win.w.width = BUZ_MIN_WIDTH;
+ if (fmt->fmt.win.w.height > BUZ_MAX_HEIGHT)
+ fmt->fmt.win.w.height = BUZ_MAX_HEIGHT;
+ if (fmt->fmt.win.w.height < BUZ_MIN_HEIGHT)
+ fmt->fmt.win.w.height = BUZ_MIN_HEIGHT;
+
+ mutex_unlock(&zr->resource_lock);
+ return 0;
+}
+
+static int zoran_try_fmt_vid_out(struct file *file, void *__fh,
+ struct v4l2_format *fmt)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ struct zoran_jpg_settings settings;
+ int res = 0;
+
+ if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG)
+ return -EINVAL;
+
+ mutex_lock(&zr->resource_lock);
+ settings = fh->jpg_settings;
+
+ /* we actually need to set 'real' parameters now */
+ if ((fmt->fmt.pix.height * 2) > BUZ_MAX_HEIGHT)
+ settings.TmpDcm = 1;
+ else
+ settings.TmpDcm = 2;
+ settings.decimation = 0;
+ if (fmt->fmt.pix.height <= fh->jpg_settings.img_height / 2)
+ settings.VerDcm = 2;
+ else
+ settings.VerDcm = 1;
+ if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 4)
+ settings.HorDcm = 4;
+ else if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 2)
+ settings.HorDcm = 2;
+ else
+ settings.HorDcm = 1;
+ if (settings.TmpDcm == 1)
+ settings.field_per_buff = 2;
+ else
+ settings.field_per_buff = 1;
+
+ if (settings.HorDcm > 1) {
+ settings.img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0;
+ settings.img_width = (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH;
+ } else {
+ settings.img_x = 0;
+ settings.img_width = BUZ_MAX_WIDTH;
+ }
+
+ /* check */
+ res = zoran_check_jpg_settings(zr, &settings, 1);
+ if (res)
+ goto tryfmt_unlock_and_return;
+
+ /* tell the user what we actually did */
+ fmt->fmt.pix.width = settings.img_width / settings.HorDcm;
+ fmt->fmt.pix.height = settings.img_height * 2 /
+ (settings.TmpDcm * settings.VerDcm);
+ if (settings.TmpDcm == 1)
+ fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
+ V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT);
+ else
+ fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
+ V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
+
+ fmt->fmt.pix.sizeimage = zoran_v4l2_calc_bufsize(&settings);
+ fmt->fmt.pix.bytesperline = 0;
+ fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+tryfmt_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+ return res;
+}
+
+static int zoran_try_fmt_vid_cap(struct file *file, void *__fh,
+ struct v4l2_format *fmt)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int bpp;
+ int i;
+
+ if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG)
+ return zoran_try_fmt_vid_out(file, fh, fmt);
+
+ mutex_lock(&zr->resource_lock);
+
+ for (i = 0; i < NUM_FORMATS; i++)
+ if (zoran_formats[i].fourcc == fmt->fmt.pix.pixelformat)
+ break;
+
+ if (i == NUM_FORMATS) {
+ mutex_unlock(&zr->resource_lock);
+ return -EINVAL;
+ }
+
+ bpp = DIV_ROUND_UP(zoran_formats[i].depth, 8);
+ v4l_bound_align_image(
+ &fmt->fmt.pix.width, BUZ_MIN_WIDTH, BUZ_MAX_WIDTH, bpp == 2 ? 1 : 2,
+ &fmt->fmt.pix.height, BUZ_MIN_HEIGHT, BUZ_MAX_HEIGHT, 0, 0);
+ mutex_unlock(&zr->resource_lock);
+
+ return 0;
+}
+
+static int zoran_s_fmt_vid_overlay(struct file *file, void *__fh,
+ struct v4l2_format *fmt)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int res;
+
+ dprintk(3, "x=%d, y=%d, w=%d, h=%d, cnt=%d, map=0x%p\n",
+ fmt->fmt.win.w.left, fmt->fmt.win.w.top,
+ fmt->fmt.win.w.width,
+ fmt->fmt.win.w.height,
+ fmt->fmt.win.clipcount,
+ fmt->fmt.win.bitmap);
+ mutex_lock(&zr->resource_lock);
+ res = setup_window(fh, fmt->fmt.win.w.left, fmt->fmt.win.w.top,
+ fmt->fmt.win.w.width, fmt->fmt.win.w.height,
+ (struct v4l2_clip __user *)fmt->fmt.win.clips,
+ fmt->fmt.win.clipcount, fmt->fmt.win.bitmap);
+ mutex_unlock(&zr->resource_lock);
+ return res;
+}
+
+static int zoran_s_fmt_vid_out(struct file *file, void *__fh,
+ struct v4l2_format *fmt)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ __le32 printformat = __cpu_to_le32(fmt->fmt.pix.pixelformat);
+ struct zoran_jpg_settings settings;
+ int res = 0;
+
+ dprintk(3, "size=%dx%d, fmt=0x%x (%4.4s)\n",
+ fmt->fmt.pix.width, fmt->fmt.pix.height,
+ fmt->fmt.pix.pixelformat,
+ (char *) &printformat);
+ if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG)
+ return -EINVAL;
+
+ mutex_lock(&zr->resource_lock);
+
+ if (fh->buffers.allocated) {
+ dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - cannot change capture mode\n",
+ ZR_DEVNAME(zr));
+ res = -EBUSY;
+ goto sfmtjpg_unlock_and_return;
+ }
+
+ settings = fh->jpg_settings;
+
+ /* we actually need to set 'real' parameters now */
+ if (fmt->fmt.pix.height * 2 > BUZ_MAX_HEIGHT)
+ settings.TmpDcm = 1;
+ else
+ settings.TmpDcm = 2;
+ settings.decimation = 0;
+ if (fmt->fmt.pix.height <= fh->jpg_settings.img_height / 2)
+ settings.VerDcm = 2;
+ else
+ settings.VerDcm = 1;
+ if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 4)
+ settings.HorDcm = 4;
+ else if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 2)
+ settings.HorDcm = 2;
+ else
+ settings.HorDcm = 1;
+ if (settings.TmpDcm == 1)
+ settings.field_per_buff = 2;
+ else
+ settings.field_per_buff = 1;
+
+ if (settings.HorDcm > 1) {
+ settings.img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0;
+ settings.img_width = (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH;
+ } else {
+ settings.img_x = 0;
+ settings.img_width = BUZ_MAX_WIDTH;
+ }
+
+ /* check */
+ res = zoran_check_jpg_settings(zr, &settings, 0);
+ if (res)
+ goto sfmtjpg_unlock_and_return;
+
+ /* it's ok, so set them */
+ fh->jpg_settings = settings;
+
+ map_mode_jpg(fh, fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ fh->buffers.buffer_size = zoran_v4l2_calc_bufsize(&fh->jpg_settings);
+
+ /* tell the user what we actually did */
+ fmt->fmt.pix.width = settings.img_width / settings.HorDcm;
+ fmt->fmt.pix.height = settings.img_height * 2 /
+ (settings.TmpDcm * settings.VerDcm);
+ if (settings.TmpDcm == 1)
+ fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
+ V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT);
+ else
+ fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
+ V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
+ fmt->fmt.pix.bytesperline = 0;
+ fmt->fmt.pix.sizeimage = fh->buffers.buffer_size;
+ fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+sfmtjpg_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+ return res;
+}
+
+static int zoran_s_fmt_vid_cap(struct file *file, void *__fh,
+ struct v4l2_format *fmt)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int i;
+ int res = 0;
+
+ if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG)
+ return zoran_s_fmt_vid_out(file, fh, fmt);
+
+ for (i = 0; i < NUM_FORMATS; i++)
+ if (fmt->fmt.pix.pixelformat == zoran_formats[i].fourcc)
+ break;
+ if (i == NUM_FORMATS) {
+ dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - unknown/unsupported format 0x%x\n",
+ ZR_DEVNAME(zr), fmt->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ mutex_lock(&zr->resource_lock);
+
+ if ((fh->map_mode != ZORAN_MAP_MODE_RAW && fh->buffers.allocated) ||
+ fh->buffers.active != ZORAN_FREE) {
+ dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - cannot change capture mode\n",
+ ZR_DEVNAME(zr));
+ res = -EBUSY;
+ goto sfmtv4l_unlock_and_return;
+ }
+ if (fmt->fmt.pix.height > BUZ_MAX_HEIGHT)
+ fmt->fmt.pix.height = BUZ_MAX_HEIGHT;
+ if (fmt->fmt.pix.width > BUZ_MAX_WIDTH)
+ fmt->fmt.pix.width = BUZ_MAX_WIDTH;
+
+ map_mode_raw(fh);
+
+ res = zoran_v4l_set_format(fh, fmt->fmt.pix.width, fmt->fmt.pix.height,
+ &zoran_formats[i]);
+ if (res)
+ goto sfmtv4l_unlock_and_return;
+
+ /* tell the user the results/missing stuff */
+ fmt->fmt.pix.bytesperline = fh->v4l_settings.bytesperline;
+ fmt->fmt.pix.sizeimage = fh->v4l_settings.height * fh->v4l_settings.bytesperline;
+ fmt->fmt.pix.colorspace = fh->v4l_settings.format->colorspace;
+ if (BUZ_MAX_HEIGHT < (fh->v4l_settings.height * 2))
+ fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
+ else
+ fmt->fmt.pix.field = V4L2_FIELD_TOP;
+
+sfmtv4l_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+ return res;
+}
+
+static int zoran_g_fbuf(struct file *file, void *__fh,
+ struct v4l2_framebuffer *fb)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ memset(fb, 0, sizeof(*fb));
+ mutex_lock(&zr->resource_lock);
+ fb->base = zr->vbuf_base;
+ fb->fmt.width = zr->vbuf_width;
+ fb->fmt.height = zr->vbuf_height;
+ if (zr->overlay_settings.format)
+ fb->fmt.pixelformat = fh->overlay_settings.format->fourcc;
+ fb->fmt.bytesperline = zr->vbuf_bytesperline;
+ mutex_unlock(&zr->resource_lock);
+ fb->fmt.colorspace = V4L2_COLORSPACE_SRGB;
+ fb->fmt.field = V4L2_FIELD_INTERLACED;
+ fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+
+ return 0;
+}
+
+static int zoran_s_fbuf(struct file *file, void *__fh,
+ struct v4l2_framebuffer *fb)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int i, res = 0;
+ __le32 printformat = __cpu_to_le32(fb->fmt.pixelformat);
+
+ for (i = 0; i < NUM_FORMATS; i++)
+ if (zoran_formats[i].fourcc == fb->fmt.pixelformat)
+ break;
+ if (i == NUM_FORMATS) {
+ dprintk(1, KERN_ERR "%s: VIDIOC_S_FBUF - format=0x%x (%4.4s) not allowed\n",
+ ZR_DEVNAME(zr), fb->fmt.pixelformat,
+ (char *)&printformat);
+ return -EINVAL;
+ }
+
+ mutex_lock(&zr->resource_lock);
+ res = setup_fbuffer(fh, fb->base, &zoran_formats[i], fb->fmt.width,
+ fb->fmt.height, fb->fmt.bytesperline);
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+}
+
+static int zoran_overlay(struct file *file, void *__fh, unsigned int on)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int res;
+
+ mutex_lock(&zr->resource_lock);
+ res = setup_overlay(fh, on);
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+}
+
+static int zoran_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type);
+
+static int zoran_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffers *req)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int res = 0;
+
+ if (req->memory != V4L2_MEMORY_MMAP) {
+ dprintk(2,
+ KERN_ERR
+ "%s: only MEMORY_MMAP capture is supported, not %d\n",
+ ZR_DEVNAME(zr), req->memory);
+ return -EINVAL;
+ }
+
+ if (req->count == 0)
+ return zoran_streamoff(file, fh, req->type);
+
+ mutex_lock(&zr->resource_lock);
+ if (fh->buffers.allocated) {
+ dprintk(2,
+ KERN_ERR
+ "%s: VIDIOC_REQBUFS - buffers already allocated\n",
+ ZR_DEVNAME(zr));
+ res = -EBUSY;
+ goto v4l2reqbuf_unlock_and_return;
+ }
+
+ if (fh->map_mode == ZORAN_MAP_MODE_RAW &&
+ req->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ /* control user input */
+ if (req->count < 2)
+ req->count = 2;
+ if (req->count > v4l_nbufs)
+ req->count = v4l_nbufs;
+
+ /* The next mmap will map the V4L buffers */
+ map_mode_raw(fh);
+ fh->buffers.num_buffers = req->count;
+
+ if (v4l_fbuffer_alloc(fh)) {
+ res = -ENOMEM;
+ goto v4l2reqbuf_unlock_and_return;
+ }
+ } else if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC ||
+ fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) {
+ /* we need to calculate size ourselves now */
+ if (req->count < 4)
+ req->count = 4;
+ if (req->count > jpg_nbufs)
+ req->count = jpg_nbufs;
+
+ /* The next mmap will map the MJPEG buffers */
+ map_mode_jpg(fh, req->type == V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ fh->buffers.num_buffers = req->count;
+ fh->buffers.buffer_size = zoran_v4l2_calc_bufsize(&fh->jpg_settings);
+
+ if (jpg_fbuffer_alloc(fh)) {
+ res = -ENOMEM;
+ goto v4l2reqbuf_unlock_and_return;
+ }
+ } else {
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_REQBUFS - unknown type %d\n",
+ ZR_DEVNAME(zr), req->type);
+ res = -EINVAL;
+ goto v4l2reqbuf_unlock_and_return;
+ }
+v4l2reqbuf_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+}
+
+static int zoran_querybuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int res;
+
+ mutex_lock(&zr->resource_lock);
+ res = zoran_v4l2_buffer_status(fh, buf, buf->index);
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+}
+
+static int zoran_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int res = 0, codec_mode, buf_type;
+
+ mutex_lock(&zr->resource_lock);
+
+ switch (fh->map_mode) {
+ case ZORAN_MAP_MODE_RAW:
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ dprintk(1, KERN_ERR
+ "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
+ ZR_DEVNAME(zr), buf->type, fh->map_mode);
+ res = -EINVAL;
+ goto qbuf_unlock_and_return;
+ }
+
+ res = zoran_v4l_queue_frame(fh, buf->index);
+ if (res)
+ goto qbuf_unlock_and_return;
+ if (!zr->v4l_memgrab_active && fh->buffers.active == ZORAN_LOCKED)
+ zr36057_set_memgrab(zr, 1);
+ break;
+
+ case ZORAN_MAP_MODE_JPG_REC:
+ case ZORAN_MAP_MODE_JPG_PLAY:
+ if (fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) {
+ buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ codec_mode = BUZ_MODE_MOTION_DECOMPRESS;
+ } else {
+ buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ codec_mode = BUZ_MODE_MOTION_COMPRESS;
+ }
+
+ if (buf->type != buf_type) {
+ dprintk(1, KERN_ERR
+ "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
+ ZR_DEVNAME(zr), buf->type, fh->map_mode);
+ res = -EINVAL;
+ goto qbuf_unlock_and_return;
+ }
+
+ res = zoran_jpg_queue_frame(fh, buf->index, codec_mode);
+ if (res != 0)
+ goto qbuf_unlock_and_return;
+ if (zr->codec_mode == BUZ_MODE_IDLE &&
+ fh->buffers.active == ZORAN_LOCKED)
+ zr36057_enable_jpg(zr, codec_mode);
+
+ break;
+
+ default:
+ dprintk(1, KERN_ERR
+ "%s: VIDIOC_QBUF - unsupported type %d\n",
+ ZR_DEVNAME(zr), buf->type);
+ res = -EINVAL;
+ break;
+ }
+qbuf_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+}
+
+static int zoran_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int res = 0, buf_type, num = -1; /* compiler borks here (?) */
+
+ mutex_lock(&zr->resource_lock);
+
+ switch (fh->map_mode) {
+ case ZORAN_MAP_MODE_RAW:
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ dprintk(1, KERN_ERR
+ "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
+ ZR_DEVNAME(zr), buf->type, fh->map_mode);
+ res = -EINVAL;
+ goto dqbuf_unlock_and_return;
+ }
+
+ num = zr->v4l_pend[zr->v4l_sync_tail & V4L_MASK_FRAME];
+ if (file->f_flags & O_NONBLOCK &&
+ zr->v4l_buffers.buffer[num].state != BUZ_STATE_DONE) {
+ res = -EAGAIN;
+ goto dqbuf_unlock_and_return;
+ }
+ res = v4l_sync(fh, num);
+ if (res)
+ goto dqbuf_unlock_and_return;
+ zr->v4l_sync_tail++;
+ res = zoran_v4l2_buffer_status(fh, buf, num);
+ break;
+
+ case ZORAN_MAP_MODE_JPG_REC:
+ case ZORAN_MAP_MODE_JPG_PLAY:
+ {
+ struct zoran_sync bs;
+
+ if (fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY)
+ buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ else
+ buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ if (buf->type != buf_type) {
+ dprintk(1, KERN_ERR
+ "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
+ ZR_DEVNAME(zr), buf->type, fh->map_mode);
+ res = -EINVAL;
+ goto dqbuf_unlock_and_return;
+ }
+
+ num = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME];
+
+ if (file->f_flags & O_NONBLOCK &&
+ zr->jpg_buffers.buffer[num].state != BUZ_STATE_DONE) {
+ res = -EAGAIN;
+ goto dqbuf_unlock_and_return;
+ }
+ bs.frame = 0; /* suppress compiler warning */
+ res = jpg_sync(fh, &bs);
+ if (res)
+ goto dqbuf_unlock_and_return;
+ res = zoran_v4l2_buffer_status(fh, buf, bs.frame);
+ break;
+ }
+
+ default:
+ dprintk(1, KERN_ERR
+ "%s: VIDIOC_DQBUF - unsupported type %d\n",
+ ZR_DEVNAME(zr), buf->type);
+ res = -EINVAL;
+ break;
+ }
+dqbuf_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+}
+
+static int zoran_streamon(struct file *file, void *__fh, enum v4l2_buf_type type)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int res = 0;
+
+ mutex_lock(&zr->resource_lock);
+
+ switch (fh->map_mode) {
+ case ZORAN_MAP_MODE_RAW: /* raw capture */
+ if (zr->v4l_buffers.active != ZORAN_ACTIVE ||
+ fh->buffers.active != ZORAN_ACTIVE) {
+ res = -EBUSY;
+ goto strmon_unlock_and_return;
+ }
+
+ zr->v4l_buffers.active = fh->buffers.active = ZORAN_LOCKED;
+ zr->v4l_settings = fh->v4l_settings;
+
+ zr->v4l_sync_tail = zr->v4l_pend_tail;
+ if (!zr->v4l_memgrab_active &&
+ zr->v4l_pend_head != zr->v4l_pend_tail) {
+ zr36057_set_memgrab(zr, 1);
+ }
+ break;
+
+ case ZORAN_MAP_MODE_JPG_REC:
+ case ZORAN_MAP_MODE_JPG_PLAY:
+ /* what is the codec mode right now? */
+ if (zr->jpg_buffers.active != ZORAN_ACTIVE ||
+ fh->buffers.active != ZORAN_ACTIVE) {
+ res = -EBUSY;
+ goto strmon_unlock_and_return;
+ }
+
+ zr->jpg_buffers.active = fh->buffers.active = ZORAN_LOCKED;
+
+ if (zr->jpg_que_head != zr->jpg_que_tail) {
+ /* Start the jpeg codec when the first frame is queued */
+ jpeg_start(zr);
+ }
+ break;
+
+ default:
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_STREAMON - invalid map mode %d\n",
+ ZR_DEVNAME(zr), fh->map_mode);
+ res = -EINVAL;
+ break;
+ }
+strmon_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+}
+
+static int zoran_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int i, res = 0;
+ unsigned long flags;
+
+ mutex_lock(&zr->resource_lock);
+
+ switch (fh->map_mode) {
+ case ZORAN_MAP_MODE_RAW: /* raw capture */
+ if (fh->buffers.active == ZORAN_FREE &&
+ zr->v4l_buffers.active != ZORAN_FREE) {
+ res = -EPERM; /* stay off other's settings! */
+ goto strmoff_unlock_and_return;
+ }
+ if (zr->v4l_buffers.active == ZORAN_FREE)
+ goto strmoff_unlock_and_return;
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+ /* unload capture */
+ if (zr->v4l_memgrab_active) {
+
+ zr36057_set_memgrab(zr, 0);
+ }
+
+ for (i = 0; i < fh->buffers.num_buffers; i++)
+ zr->v4l_buffers.buffer[i].state = BUZ_STATE_USER;
+ fh->buffers = zr->v4l_buffers;
+
+ zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE;
+
+ zr->v4l_grab_seq = 0;
+ zr->v4l_pend_head = zr->v4l_pend_tail = 0;
+ zr->v4l_sync_tail = 0;
+
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+
+ break;
+
+ case ZORAN_MAP_MODE_JPG_REC:
+ case ZORAN_MAP_MODE_JPG_PLAY:
+ if (fh->buffers.active == ZORAN_FREE &&
+ zr->jpg_buffers.active != ZORAN_FREE) {
+ res = -EPERM; /* stay off other's settings! */
+ goto strmoff_unlock_and_return;
+ }
+ if (zr->jpg_buffers.active == ZORAN_FREE)
+ goto strmoff_unlock_and_return;
+
+ res = jpg_qbuf(fh, -1,
+ (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) ?
+ BUZ_MODE_MOTION_COMPRESS :
+ BUZ_MODE_MOTION_DECOMPRESS);
+ if (res)
+ goto strmoff_unlock_and_return;
+ break;
+ default:
+ dprintk(1, KERN_ERR
+ "%s: VIDIOC_STREAMOFF - invalid map mode %d\n",
+ ZR_DEVNAME(zr), fh->map_mode);
+ res = -EINVAL;
+ break;
+ }
+strmoff_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+}
+
+static int zoran_queryctrl(struct file *file, void *__fh,
+ struct v4l2_queryctrl *ctrl)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ /* we only support hue/saturation/contrast/brightness */
+ if (ctrl->id < V4L2_CID_BRIGHTNESS ||
+ ctrl->id > V4L2_CID_HUE)
+ return -EINVAL;
+
+ decoder_call(zr, core, queryctrl, ctrl);
+
+ return 0;
+}
+
+static int zoran_g_ctrl(struct file *file, void *__fh, struct v4l2_control *ctrl)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ /* we only support hue/saturation/contrast/brightness */
+ if (ctrl->id < V4L2_CID_BRIGHTNESS ||
+ ctrl->id > V4L2_CID_HUE)
+ return -EINVAL;
+
+ mutex_lock(&zr->resource_lock);
+ decoder_call(zr, core, g_ctrl, ctrl);
+ mutex_unlock(&zr->resource_lock);
+
+ return 0;
+}
+
+static int zoran_s_ctrl(struct file *file, void *__fh, struct v4l2_control *ctrl)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ /* we only support hue/saturation/contrast/brightness */
+ if (ctrl->id < V4L2_CID_BRIGHTNESS ||
+ ctrl->id > V4L2_CID_HUE)
+ return -EINVAL;
+
+ mutex_lock(&zr->resource_lock);
+ decoder_call(zr, core, s_ctrl, ctrl);
+ mutex_unlock(&zr->resource_lock);
+
+ return 0;
+}
+
+static int zoran_g_std(struct file *file, void *__fh, v4l2_std_id *std)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ mutex_lock(&zr->resource_lock);
+ *std = zr->norm;
+ mutex_unlock(&zr->resource_lock);
+ return 0;
+}
+
+static int zoran_s_std(struct file *file, void *__fh, v4l2_std_id *std)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int res = 0;
+
+ mutex_lock(&zr->resource_lock);
+ res = zoran_set_norm(zr, *std);
+ if (res)
+ goto sstd_unlock_and_return;
+
+ res = wait_grab_pending(zr);
+sstd_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+ return res;
+}
+
+static int zoran_enum_input(struct file *file, void *__fh,
+ struct v4l2_input *inp)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ if (inp->index >= zr->card.inputs)
+ return -EINVAL;
+
+ strncpy(inp->name, zr->card.input[inp->index].name,
+ sizeof(inp->name) - 1);
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ inp->std = V4L2_STD_ALL;
+
+ /* Get status of video decoder */
+ mutex_lock(&zr->resource_lock);
+ decoder_call(zr, video, g_input_status, &inp->status);
+ mutex_unlock(&zr->resource_lock);
+ return 0;
+}
+
+static int zoran_g_input(struct file *file, void *__fh, unsigned int *input)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ mutex_lock(&zr->resource_lock);
+ *input = zr->input;
+ mutex_unlock(&zr->resource_lock);
+
+ return 0;
+}
+
+static int zoran_s_input(struct file *file, void *__fh, unsigned int input)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int res;
+
+ mutex_lock(&zr->resource_lock);
+ res = zoran_set_input(zr, input);
+ if (res)
+ goto sinput_unlock_and_return;
+
+ /* Make sure the changes come into effect */
+ res = wait_grab_pending(zr);
+sinput_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+ return res;
+}
+
+static int zoran_enum_output(struct file *file, void *__fh,
+ struct v4l2_output *outp)
+{
+ if (outp->index != 0)
+ return -EINVAL;
+
+ outp->index = 0;
+ outp->type = V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY;
+ strncpy(outp->name, "Autodetect", sizeof(outp->name)-1);
+
+ return 0;
+}
+
+static int zoran_g_output(struct file *file, void *__fh, unsigned int *output)
+{
+ *output = 0;
+
+ return 0;
+}
+
+static int zoran_s_output(struct file *file, void *__fh, unsigned int output)
+{
+ if (output != 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+/* cropping (sub-frame capture) */
+static int zoran_cropcap(struct file *file, void *__fh,
+ struct v4l2_cropcap *cropcap)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int type = cropcap->type, res = 0;
+
+ memset(cropcap, 0, sizeof(*cropcap));
+ cropcap->type = type;
+
+ mutex_lock(&zr->resource_lock);
+
+ if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ fh->map_mode == ZORAN_MAP_MODE_RAW)) {
+ dprintk(1, KERN_ERR
+ "%s: VIDIOC_CROPCAP - subcapture only supported for compressed capture\n",
+ ZR_DEVNAME(zr));
+ res = -EINVAL;
+ goto cropcap_unlock_and_return;
+ }
+
+ cropcap->bounds.top = cropcap->bounds.left = 0;
+ cropcap->bounds.width = BUZ_MAX_WIDTH;
+ cropcap->bounds.height = BUZ_MAX_HEIGHT;
+ cropcap->defrect.top = cropcap->defrect.left = 0;
+ cropcap->defrect.width = BUZ_MIN_WIDTH;
+ cropcap->defrect.height = BUZ_MIN_HEIGHT;
+cropcap_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+ return res;
+}
+
+static int zoran_g_crop(struct file *file, void *__fh, struct v4l2_crop *crop)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int type = crop->type, res = 0;
+
+ memset(crop, 0, sizeof(*crop));
+ crop->type = type;
+
+ mutex_lock(&zr->resource_lock);
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ fh->map_mode == ZORAN_MAP_MODE_RAW)) {
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_G_CROP - subcapture only supported for compressed capture\n",
+ ZR_DEVNAME(zr));
+ res = -EINVAL;
+ goto gcrop_unlock_and_return;
+ }
+
+ crop->c.top = fh->jpg_settings.img_y;
+ crop->c.left = fh->jpg_settings.img_x;
+ crop->c.width = fh->jpg_settings.img_width;
+ crop->c.height = fh->jpg_settings.img_height;
+
+gcrop_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+}
+
+static int zoran_s_crop(struct file *file, void *__fh, struct v4l2_crop *crop)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int res = 0;
+ struct zoran_jpg_settings settings;
+
+ settings = fh->jpg_settings;
+
+ mutex_lock(&zr->resource_lock);
+
+ if (fh->buffers.allocated) {
+ dprintk(1, KERN_ERR
+ "%s: VIDIOC_S_CROP - cannot change settings while active\n",
+ ZR_DEVNAME(zr));
+ res = -EBUSY;
+ goto scrop_unlock_and_return;
+ }
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ fh->map_mode == ZORAN_MAP_MODE_RAW)) {
+ dprintk(1, KERN_ERR
+ "%s: VIDIOC_G_CROP - subcapture only supported for compressed capture\n",
+ ZR_DEVNAME(zr));
+ res = -EINVAL;
+ goto scrop_unlock_and_return;
+ }
+
+ /* move into a form that we understand */
+ settings.img_x = crop->c.left;
+ settings.img_y = crop->c.top;
+ settings.img_width = crop->c.width;
+ settings.img_height = crop->c.height;
+
+ /* check validity */
+ res = zoran_check_jpg_settings(zr, &settings, 0);
+ if (res)
+ goto scrop_unlock_and_return;
+
+ /* accept */
+ fh->jpg_settings = settings;
+
+scrop_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+ return res;
+}
+
+static int zoran_g_jpegcomp(struct file *file, void *__fh,
+ struct v4l2_jpegcompression *params)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ memset(params, 0, sizeof(*params));
+
+ mutex_lock(&zr->resource_lock);
+
+ params->quality = fh->jpg_settings.jpg_comp.quality;
+ params->APPn = fh->jpg_settings.jpg_comp.APPn;
+ memcpy(params->APP_data,
+ fh->jpg_settings.jpg_comp.APP_data,
+ fh->jpg_settings.jpg_comp.APP_len);
+ params->APP_len = fh->jpg_settings.jpg_comp.APP_len;
+ memcpy(params->COM_data,
+ fh->jpg_settings.jpg_comp.COM_data,
+ fh->jpg_settings.jpg_comp.COM_len);
+ params->COM_len = fh->jpg_settings.jpg_comp.COM_len;
+ params->jpeg_markers =
+ fh->jpg_settings.jpg_comp.jpeg_markers;
+
+ mutex_unlock(&zr->resource_lock);
+
+ return 0;
+}
+
+static int zoran_s_jpegcomp(struct file *file, void *__fh,
+ struct v4l2_jpegcompression *params)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int res = 0;
+ struct zoran_jpg_settings settings;
+
+ settings = fh->jpg_settings;
+
+ settings.jpg_comp = *params;
+
+ mutex_lock(&zr->resource_lock);
+
+ if (fh->buffers.active != ZORAN_FREE) {
+ dprintk(1, KERN_WARNING
+ "%s: VIDIOC_S_JPEGCOMP called while in playback/capture mode\n",
+ ZR_DEVNAME(zr));
+ res = -EBUSY;
+ goto sjpegc_unlock_and_return;
+ }
+
+ res = zoran_check_jpg_settings(zr, &settings, 0);
+ if (res)
+ goto sjpegc_unlock_and_return;
+ if (!fh->buffers.allocated)
+ fh->buffers.buffer_size =
+ zoran_v4l2_calc_bufsize(&fh->jpg_settings);
+ fh->jpg_settings.jpg_comp = *params = settings.jpg_comp;
+sjpegc_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+}
+
+static unsigned int
+zoran_poll (struct file *file,
+ poll_table *wait)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ int res = 0, frame;
+ unsigned long flags;
+
+ /* we should check whether buffers are ready to be synced on
+ * (w/o waits - O_NONBLOCK) here
+ * if ready for read (sync), return POLLIN|POLLRDNORM,
+ * if ready for write (sync), return POLLOUT|POLLWRNORM,
+ * if error, return POLLERR,
+ * if no buffers queued or so, return POLLNVAL
+ */
+
+ mutex_lock(&zr->resource_lock);
+
+ switch (fh->map_mode) {
+ case ZORAN_MAP_MODE_RAW:
+ poll_wait(file, &zr->v4l_capq, wait);
+ frame = zr->v4l_pend[zr->v4l_sync_tail & V4L_MASK_FRAME];
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+ dprintk(3,
+ KERN_DEBUG
+ "%s: %s() raw - active=%c, sync_tail=%lu/%c, pend_tail=%lu, pend_head=%lu\n",
+ ZR_DEVNAME(zr), __func__,
+ "FAL"[fh->buffers.active], zr->v4l_sync_tail,
+ "UPMD"[zr->v4l_buffers.buffer[frame].state],
+ zr->v4l_pend_tail, zr->v4l_pend_head);
+ /* Process is the one capturing? */
+ if (fh->buffers.active != ZORAN_FREE &&
+ /* Buffer ready to DQBUF? */
+ zr->v4l_buffers.buffer[frame].state == BUZ_STATE_DONE)
+ res = POLLIN | POLLRDNORM;
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+
+ break;
+
+ case ZORAN_MAP_MODE_JPG_REC:
+ case ZORAN_MAP_MODE_JPG_PLAY:
+ poll_wait(file, &zr->jpg_capq, wait);
+ frame = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME];
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+ dprintk(3,
+ KERN_DEBUG
+ "%s: %s() jpg - active=%c, que_tail=%lu/%c, que_head=%lu, dma=%lu/%lu\n",
+ ZR_DEVNAME(zr), __func__,
+ "FAL"[fh->buffers.active], zr->jpg_que_tail,
+ "UPMD"[zr->jpg_buffers.buffer[frame].state],
+ zr->jpg_que_head, zr->jpg_dma_tail, zr->jpg_dma_head);
+ if (fh->buffers.active != ZORAN_FREE &&
+ zr->jpg_buffers.buffer[frame].state == BUZ_STATE_DONE) {
+ if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC)
+ res = POLLIN | POLLRDNORM;
+ else
+ res = POLLOUT | POLLWRNORM;
+ }
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+
+ break;
+
+ default:
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - internal error, unknown map_mode=%d\n",
+ ZR_DEVNAME(zr), __func__, fh->map_mode);
+ res = POLLNVAL;
+ }
+
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+}
+
+
+/*
+ * This maps the buffers to user space.
+ *
+ * Depending on the state of fh->map_mode
+ * the V4L or the MJPEG buffers are mapped
+ * per buffer or all together
+ *
+ * Note that we need to connect to some
+ * unmap signal event to unmap the de-allocate
+ * the buffer accordingly (zoran_vm_close())
+ */
+
+static void
+zoran_vm_open (struct vm_area_struct *vma)
+{
+ struct zoran_mapping *map = vma->vm_private_data;
+
+ map->count++;
+}
+
+static void
+zoran_vm_close (struct vm_area_struct *vma)
+{
+ struct zoran_mapping *map = vma->vm_private_data;
+ struct zoran_fh *fh = map->file->private_data;
+ struct zoran *zr = fh->zr;
+ int i;
+
+ if (--map->count > 0)
+ return;
+
+ dprintk(3, KERN_INFO "%s: %s - munmap(%s)\n", ZR_DEVNAME(zr),
+ __func__, mode_name(fh->map_mode));
+
+ for (i = 0; i < fh->buffers.num_buffers; i++) {
+ if (fh->buffers.buffer[i].map == map)
+ fh->buffers.buffer[i].map = NULL;
+ }
+ kfree(map);
+
+ /* Any buffers still mapped? */
+ for (i = 0; i < fh->buffers.num_buffers; i++)
+ if (fh->buffers.buffer[i].map)
+ return;
+
+ dprintk(3, KERN_INFO "%s: %s - free %s buffers\n", ZR_DEVNAME(zr),
+ __func__, mode_name(fh->map_mode));
+
+ mutex_lock(&zr->resource_lock);
+
+ if (fh->map_mode == ZORAN_MAP_MODE_RAW) {
+ if (fh->buffers.active != ZORAN_FREE) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+ zr36057_set_memgrab(zr, 0);
+ zr->v4l_buffers.allocated = 0;
+ zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE;
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+ }
+ v4l_fbuffer_free(fh);
+ } else {
+ if (fh->buffers.active != ZORAN_FREE) {
+ jpg_qbuf(fh, -1, zr->codec_mode);
+ zr->jpg_buffers.allocated = 0;
+ zr->jpg_buffers.active = fh->buffers.active = ZORAN_FREE;
+ }
+ jpg_fbuffer_free(fh);
+ }
+
+ mutex_unlock(&zr->resource_lock);
+}
+
+static const struct vm_operations_struct zoran_vm_ops = {
+ .open = zoran_vm_open,
+ .close = zoran_vm_close,
+};
+
+static int
+zoran_mmap (struct file *file,
+ struct vm_area_struct *vma)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ unsigned long size = (vma->vm_end - vma->vm_start);
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ int i, j;
+ unsigned long page, start = vma->vm_start, todo, pos, fraglen;
+ int first, last;
+ struct zoran_mapping *map;
+ int res = 0;
+
+ dprintk(3,
+ KERN_INFO "%s: %s(%s) of 0x%08lx-0x%08lx (size=%lu)\n",
+ ZR_DEVNAME(zr), __func__,
+ mode_name(fh->map_mode), vma->vm_start, vma->vm_end, size);
+
+ if (!(vma->vm_flags & VM_SHARED) || !(vma->vm_flags & VM_READ) ||
+ !(vma->vm_flags & VM_WRITE)) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - no MAP_SHARED/PROT_{READ,WRITE} given\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&zr->resource_lock);
+
+ if (!fh->buffers.allocated) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s(%s) - buffers not yet allocated\n",
+ ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode));
+ res = -ENOMEM;
+ goto mmap_unlock_and_return;
+ }
+
+ first = offset / fh->buffers.buffer_size;
+ last = first - 1 + size / fh->buffers.buffer_size;
+ if (offset % fh->buffers.buffer_size != 0 ||
+ size % fh->buffers.buffer_size != 0 || first < 0 ||
+ last < 0 || first >= fh->buffers.num_buffers ||
+ last >= fh->buffers.buffer_size) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s(%s) - offset=%lu or size=%lu invalid for bufsize=%d and numbufs=%d\n",
+ ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode), offset, size,
+ fh->buffers.buffer_size,
+ fh->buffers.num_buffers);
+ res = -EINVAL;
+ goto mmap_unlock_and_return;
+ }
+
+ /* Check if any buffers are already mapped */
+ for (i = first; i <= last; i++) {
+ if (fh->buffers.buffer[i].map) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s(%s) - buffer %d already mapped\n",
+ ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode), i);
+ res = -EBUSY;
+ goto mmap_unlock_and_return;
+ }
+ }
+
+ /* map these buffers */
+ map = kmalloc(sizeof(struct zoran_mapping), GFP_KERNEL);
+ if (!map) {
+ res = -ENOMEM;
+ goto mmap_unlock_and_return;
+ }
+ map->file = file;
+ map->count = 1;
+
+ vma->vm_ops = &zoran_vm_ops;
+ vma->vm_flags |= VM_DONTEXPAND;
+ vma->vm_private_data = map;
+
+ if (fh->map_mode == ZORAN_MAP_MODE_RAW) {
+ for (i = first; i <= last; i++) {
+ todo = size;
+ if (todo > fh->buffers.buffer_size)
+ todo = fh->buffers.buffer_size;
+ page = fh->buffers.buffer[i].v4l.fbuffer_phys;
+ if (remap_pfn_range(vma, start, page >> PAGE_SHIFT,
+ todo, PAGE_SHARED)) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s(V4L) - remap_pfn_range failed\n",
+ ZR_DEVNAME(zr), __func__);
+ res = -EAGAIN;
+ goto mmap_unlock_and_return;
+ }
+ size -= todo;
+ start += todo;
+ fh->buffers.buffer[i].map = map;
+ if (size == 0)
+ break;
+ }
+ } else {
+ for (i = first; i <= last; i++) {
+ for (j = 0;
+ j < fh->buffers.buffer_size / PAGE_SIZE;
+ j++) {
+ fraglen =
+ (le32_to_cpu(fh->buffers.buffer[i].jpg.
+ frag_tab[2 * j + 1]) & ~1) << 1;
+ todo = size;
+ if (todo > fraglen)
+ todo = fraglen;
+ pos =
+ le32_to_cpu(fh->buffers.
+ buffer[i].jpg.frag_tab[2 * j]);
+ /* should just be pos on i386 */
+ page = virt_to_phys(bus_to_virt(pos))
+ >> PAGE_SHIFT;
+ if (remap_pfn_range(vma, start, page,
+ todo, PAGE_SHARED)) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s(V4L) - remap_pfn_range failed\n",
+ ZR_DEVNAME(zr), __func__);
+ res = -EAGAIN;
+ goto mmap_unlock_and_return;
+ }
+ size -= todo;
+ start += todo;
+ if (size == 0)
+ break;
+ if (le32_to_cpu(fh->buffers.buffer[i].jpg.
+ frag_tab[2 * j + 1]) & 1)
+ break; /* was last fragment */
+ }
+ fh->buffers.buffer[i].map = map;
+ if (size == 0)
+ break;
+
+ }
+ }
+
+mmap_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+}
+
+static const struct v4l2_ioctl_ops zoran_ioctl_ops = {
+ .vidioc_querycap = zoran_querycap,
+ .vidioc_cropcap = zoran_cropcap,
+ .vidioc_s_crop = zoran_s_crop,
+ .vidioc_g_crop = zoran_g_crop,
+ .vidioc_enum_input = zoran_enum_input,
+ .vidioc_g_input = zoran_g_input,
+ .vidioc_s_input = zoran_s_input,
+ .vidioc_enum_output = zoran_enum_output,
+ .vidioc_g_output = zoran_g_output,
+ .vidioc_s_output = zoran_s_output,
+ .vidioc_g_fbuf = zoran_g_fbuf,
+ .vidioc_s_fbuf = zoran_s_fbuf,
+ .vidioc_g_std = zoran_g_std,
+ .vidioc_s_std = zoran_s_std,
+ .vidioc_g_jpegcomp = zoran_g_jpegcomp,
+ .vidioc_s_jpegcomp = zoran_s_jpegcomp,
+ .vidioc_overlay = zoran_overlay,
+ .vidioc_reqbufs = zoran_reqbufs,
+ .vidioc_querybuf = zoran_querybuf,
+ .vidioc_qbuf = zoran_qbuf,
+ .vidioc_dqbuf = zoran_dqbuf,
+ .vidioc_streamon = zoran_streamon,
+ .vidioc_streamoff = zoran_streamoff,
+ .vidioc_enum_fmt_vid_cap = zoran_enum_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_out = zoran_enum_fmt_vid_out,
+ .vidioc_enum_fmt_vid_overlay = zoran_enum_fmt_vid_overlay,
+ .vidioc_g_fmt_vid_cap = zoran_g_fmt_vid_cap,
+ .vidioc_g_fmt_vid_out = zoran_g_fmt_vid_out,
+ .vidioc_g_fmt_vid_overlay = zoran_g_fmt_vid_overlay,
+ .vidioc_s_fmt_vid_cap = zoran_s_fmt_vid_cap,
+ .vidioc_s_fmt_vid_out = zoran_s_fmt_vid_out,
+ .vidioc_s_fmt_vid_overlay = zoran_s_fmt_vid_overlay,
+ .vidioc_try_fmt_vid_cap = zoran_try_fmt_vid_cap,
+ .vidioc_try_fmt_vid_out = zoran_try_fmt_vid_out,
+ .vidioc_try_fmt_vid_overlay = zoran_try_fmt_vid_overlay,
+ .vidioc_queryctrl = zoran_queryctrl,
+ .vidioc_s_ctrl = zoran_s_ctrl,
+ .vidioc_g_ctrl = zoran_g_ctrl,
+};
+
+/* please use zr->resource_lock consistently and kill this wrapper */
+static long zoran_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ int ret;
+
+ mutex_lock(&zr->other_lock);
+ ret = video_ioctl2(file, cmd, arg);
+ mutex_unlock(&zr->other_lock);
+
+ return ret;
+}
+
+static const struct v4l2_file_operations zoran_fops = {
+ .owner = THIS_MODULE,
+ .open = zoran_open,
+ .release = zoran_close,
+ .unlocked_ioctl = zoran_ioctl,
+ .read = zoran_read,
+ .write = zoran_write,
+ .mmap = zoran_mmap,
+ .poll = zoran_poll,
+};
+
+struct video_device zoran_template __devinitdata = {
+ .name = ZORAN_NAME,
+ .fops = &zoran_fops,
+ .ioctl_ops = &zoran_ioctl_ops,
+ .release = &zoran_vdev_release,
+ .tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM,
+};
+
diff --git a/drivers/media/video/zoran/zoran_procfs.c b/drivers/media/video/zoran/zoran_procfs.c
new file mode 100644
index 00000000..f1423b77
--- /dev/null
+++ b/drivers/media/video/zoran/zoran_procfs.c
@@ -0,0 +1,225 @@
+/*
+ * Zoran zr36057/zr36067 PCI controller driver, for the
+ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
+ * Media Labs LML33/LML33R10.
+ *
+ * This part handles the procFS entries (/proc/ZORAN[%d])
+ *
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ *
+ * Currently maintained by:
+ * Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * Laurent Pinchart <laurent.pinchart@skynet.be>
+ * Mailinglist <mjpeg-users@lists.sf.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/videodev2.h>
+#include <linux/spinlock.h>
+#include <linux/sem.h>
+#include <linux/seq_file.h>
+
+#include <linux/ctype.h>
+#include <linux/poll.h>
+#include <asm/io.h>
+
+#include "videocodec.h"
+#include "zoran.h"
+#include "zoran_procfs.h"
+#include "zoran_card.h"
+
+#ifdef CONFIG_PROC_FS
+struct procfs_params_zr36067 {
+ char *name;
+ short reg;
+ u32 mask;
+ short bit;
+};
+
+static const struct procfs_params_zr36067 zr67[] = {
+ {"HSPol", 0x000, 1, 30},
+ {"HStart", 0x000, 0x3ff, 10},
+ {"HEnd", 0x000, 0x3ff, 0},
+
+ {"VSPol", 0x004, 1, 30},
+ {"VStart", 0x004, 0x3ff, 10},
+ {"VEnd", 0x004, 0x3ff, 0},
+
+ {"ExtFl", 0x008, 1, 26},
+ {"TopField", 0x008, 1, 25},
+ {"VCLKPol", 0x008, 1, 24},
+ {"DupFld", 0x008, 1, 20},
+ {"LittleEndian", 0x008, 1, 0},
+
+ {"HsyncStart", 0x10c, 0xffff, 16},
+ {"LineTot", 0x10c, 0xffff, 0},
+
+ {"NAX", 0x110, 0xffff, 16},
+ {"PAX", 0x110, 0xffff, 0},
+
+ {"NAY", 0x114, 0xffff, 16},
+ {"PAY", 0x114, 0xffff, 0},
+
+ /* {"",,,}, */
+
+ {NULL, 0, 0, 0},
+};
+
+static void
+setparam (struct zoran *zr,
+ char *name,
+ char *sval)
+{
+ int i = 0, reg0, reg, val;
+
+ while (zr67[i].name != NULL) {
+ if (!strncmp(name, zr67[i].name, strlen(zr67[i].name))) {
+ reg = reg0 = btread(zr67[i].reg);
+ reg &= ~(zr67[i].mask << zr67[i].bit);
+ if (!isdigit(sval[0]))
+ break;
+ val = simple_strtoul(sval, NULL, 0);
+ if ((val & ~zr67[i].mask))
+ break;
+ reg |= (val & zr67[i].mask) << zr67[i].bit;
+ dprintk(4,
+ KERN_INFO
+ "%s: setparam: setting ZR36067 register 0x%03x: 0x%08x=>0x%08x %s=%d\n",
+ ZR_DEVNAME(zr), zr67[i].reg, reg0, reg,
+ zr67[i].name, val);
+ btwrite(reg, zr67[i].reg);
+ break;
+ }
+ i++;
+ }
+}
+
+static int zoran_show(struct seq_file *p, void *v)
+{
+ struct zoran *zr = p->private;
+ int i;
+
+ seq_printf(p, "ZR36067 registers:\n");
+ for (i = 0; i < 0x130; i += 16)
+ seq_printf(p, "%03X %08X %08X %08X %08X \n", i,
+ btread(i), btread(i+4), btread(i+8), btread(i+12));
+ return 0;
+}
+
+static int zoran_open(struct inode *inode, struct file *file)
+{
+ struct zoran *data = PDE(inode)->data;
+ return single_open(file, zoran_show, data);
+}
+
+static ssize_t zoran_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct zoran *zr = PDE(file->f_path.dentry->d_inode)->data;
+ char *string, *sp;
+ char *line, *ldelim, *varname, *svar, *tdelim;
+
+ if (count > 32768) /* Stupidity filter */
+ return -EINVAL;
+
+ string = sp = vmalloc(count + 1);
+ if (!string) {
+ dprintk(1,
+ KERN_ERR
+ "%s: write_proc: can not allocate memory\n",
+ ZR_DEVNAME(zr));
+ return -ENOMEM;
+ }
+ if (copy_from_user(string, buffer, count)) {
+ vfree (string);
+ return -EFAULT;
+ }
+ string[count] = 0;
+ dprintk(4, KERN_INFO "%s: write_proc: name=%s count=%zu zr=%p\n",
+ ZR_DEVNAME(zr), file->f_path.dentry->d_name.name, count, zr);
+ ldelim = " \t\n";
+ tdelim = "=";
+ line = strpbrk(sp, ldelim);
+ while (line) {
+ *line = 0;
+ svar = strpbrk(sp, tdelim);
+ if (svar) {
+ *svar = 0;
+ varname = sp;
+ svar++;
+ setparam(zr, varname, svar);
+ }
+ sp = line + 1;
+ line = strpbrk(sp, ldelim);
+ }
+ vfree(string);
+
+ return count;
+}
+
+static const struct file_operations zoran_operations = {
+ .owner = THIS_MODULE,
+ .open = zoran_open,
+ .read = seq_read,
+ .write = zoran_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+#endif
+
+int
+zoran_proc_init (struct zoran *zr)
+{
+#ifdef CONFIG_PROC_FS
+ char name[8];
+
+ snprintf(name, 7, "zoran%d", zr->id);
+ zr->zoran_proc = proc_create_data(name, 0, NULL, &zoran_operations, zr);
+ if (zr->zoran_proc != NULL) {
+ dprintk(2,
+ KERN_INFO
+ "%s: procfs entry /proc/%s allocated. data=%p\n",
+ ZR_DEVNAME(zr), name, zr->zoran_proc->data);
+ } else {
+ dprintk(1, KERN_ERR "%s: Unable to initialise /proc/%s\n",
+ ZR_DEVNAME(zr), name);
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+void
+zoran_proc_cleanup (struct zoran *zr)
+{
+#ifdef CONFIG_PROC_FS
+ char name[8];
+
+ snprintf(name, 7, "zoran%d", zr->id);
+ if (zr->zoran_proc)
+ remove_proc_entry(name, NULL);
+ zr->zoran_proc = NULL;
+#endif
+}
diff --git a/drivers/media/video/zoran/zoran_procfs.h b/drivers/media/video/zoran/zoran_procfs.h
new file mode 100644
index 00000000..f2d5b1ba
--- /dev/null
+++ b/drivers/media/video/zoran/zoran_procfs.h
@@ -0,0 +1,36 @@
+/*
+ * Zoran zr36057/zr36067 PCI controller driver, for the
+ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
+ * Media Labs LML33/LML33R10.
+ *
+ * This part handles card-specific data and detection
+ *
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ *
+ * Currently maintained by:
+ * Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * Laurent Pinchart <laurent.pinchart@skynet.be>
+ * Mailinglist <mjpeg-users@lists.sf.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ZORAN_PROCFS_H__
+#define __ZORAN_PROCFS_H__
+
+extern int zoran_proc_init(struct zoran *zr);
+extern void zoran_proc_cleanup(struct zoran *zr);
+
+#endif /* __ZORAN_PROCFS_H__ */
diff --git a/drivers/media/video/zoran/zr36016.c b/drivers/media/video/zoran/zr36016.c
new file mode 100644
index 00000000..21c088ea
--- /dev/null
+++ b/drivers/media/video/zoran/zr36016.c
@@ -0,0 +1,524 @@
+/*
+ * Zoran ZR36016 basic configuration functions
+ *
+ * Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at>
+ *
+ * $Id: zr36016.c,v 1.1.2.14 2003/08/20 19:46:55 rbultje Exp $
+ *
+ * ------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+#define ZR016_VERSION "v0.7"
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#include <linux/types.h>
+#include <linux/wait.h>
+
+/* I/O commands, error codes */
+#include <asm/io.h>
+
+/* v4l API */
+
+/* headerfile of this module */
+#include"zr36016.h"
+
+/* codec io API */
+#include"videocodec.h"
+
+/* it doesn't make sense to have more than 20 or so,
+ just to prevent some unwanted loops */
+#define MAX_CODECS 20
+
+/* amount of chips attached via this driver */
+static int zr36016_codecs;
+
+/* debugging is available via module parameter */
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-4)");
+
+#define dprintk(num, format, args...) \
+ do { \
+ if (debug >= num) \
+ printk(format, ##args); \
+ } while (0)
+
+/* =========================================================================
+ Local hardware I/O functions:
+
+ read/write via codec layer (registers are located in the master device)
+ ========================================================================= */
+
+/* read and write functions */
+static u8
+zr36016_read (struct zr36016 *ptr,
+ u16 reg)
+{
+ u8 value = 0;
+
+ // just in case something is wrong...
+ if (ptr->codec->master_data->readreg)
+ value =
+ (ptr->codec->master_data->
+ readreg(ptr->codec, reg)) & 0xFF;
+ else
+ dprintk(1,
+ KERN_ERR "%s: invalid I/O setup, nothing read!\n",
+ ptr->name);
+
+ dprintk(4, "%s: reading from 0x%04x: %02x\n", ptr->name, reg,
+ value);
+
+ return value;
+}
+
+static void
+zr36016_write (struct zr36016 *ptr,
+ u16 reg,
+ u8 value)
+{
+ dprintk(4, "%s: writing 0x%02x to 0x%04x\n", ptr->name, value,
+ reg);
+
+ // just in case something is wrong...
+ if (ptr->codec->master_data->writereg) {
+ ptr->codec->master_data->writereg(ptr->codec, reg, value);
+ } else
+ dprintk(1,
+ KERN_ERR
+ "%s: invalid I/O setup, nothing written!\n",
+ ptr->name);
+}
+
+/* indirect read and write functions */
+/* the 016 supports auto-addr-increment, but
+ * writing it all time cost not much and is safer... */
+static u8
+zr36016_readi (struct zr36016 *ptr,
+ u16 reg)
+{
+ u8 value = 0;
+
+ // just in case something is wrong...
+ if ((ptr->codec->master_data->writereg) &&
+ (ptr->codec->master_data->readreg)) {
+ ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F); // ADDR
+ value = (ptr->codec->master_data->readreg(ptr->codec, ZR016_IDATA)) & 0xFF; // DATA
+ } else
+ dprintk(1,
+ KERN_ERR
+ "%s: invalid I/O setup, nothing read (i)!\n",
+ ptr->name);
+
+ dprintk(4, "%s: reading indirect from 0x%04x: %02x\n", ptr->name,
+ reg, value);
+ return value;
+}
+
+static void
+zr36016_writei (struct zr36016 *ptr,
+ u16 reg,
+ u8 value)
+{
+ dprintk(4, "%s: writing indirect 0x%02x to 0x%04x\n", ptr->name,
+ value, reg);
+
+ // just in case something is wrong...
+ if (ptr->codec->master_data->writereg) {
+ ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F); // ADDR
+ ptr->codec->master_data->writereg(ptr->codec, ZR016_IDATA, value & 0x0FF); // DATA
+ } else
+ dprintk(1,
+ KERN_ERR
+ "%s: invalid I/O setup, nothing written (i)!\n",
+ ptr->name);
+}
+
+/* =========================================================================
+ Local helper function:
+
+ version read
+ ========================================================================= */
+
+/* version kept in datastructure */
+static u8
+zr36016_read_version (struct zr36016 *ptr)
+{
+ ptr->version = zr36016_read(ptr, 0) >> 4;
+ return ptr->version;
+}
+
+/* =========================================================================
+ Local helper function:
+
+ basic test of "connectivity", writes/reads to/from PAX-Lo register
+ ========================================================================= */
+
+static int
+zr36016_basic_test (struct zr36016 *ptr)
+{
+ if (debug) {
+ int i;
+ zr36016_writei(ptr, ZR016I_PAX_LO, 0x55);
+ dprintk(1, KERN_INFO "%s: registers: ", ptr->name);
+ for (i = 0; i <= 0x0b; i++)
+ dprintk(1, "%02x ", zr36016_readi(ptr, i));
+ dprintk(1, "\n");
+ }
+ // for testing just write 0, then the default value to a register and read
+ // it back in both cases
+ zr36016_writei(ptr, ZR016I_PAX_LO, 0x00);
+ if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: attach failed, can't connect to vfe processor!\n",
+ ptr->name);
+ return -ENXIO;
+ }
+ zr36016_writei(ptr, ZR016I_PAX_LO, 0x0d0);
+ if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0d0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: attach failed, can't connect to vfe processor!\n",
+ ptr->name);
+ return -ENXIO;
+ }
+ // we allow version numbers from 0-3, should be enough, though
+ zr36016_read_version(ptr);
+ if (ptr->version & 0x0c) {
+ dprintk(1,
+ KERN_ERR
+ "%s: attach failed, suspicious version %d found...\n",
+ ptr->name, ptr->version);
+ return -ENXIO;
+ }
+
+ return 0; /* looks good! */
+}
+
+/* =========================================================================
+ Local helper function:
+
+ simple loop for pushing the init datasets - NO USE --
+ ========================================================================= */
+
+#if 0
+static int zr36016_pushit (struct zr36016 *ptr,
+ u16 startreg,
+ u16 len,
+ const char *data)
+{
+ int i=0;
+
+ dprintk(4, "%s: write data block to 0x%04x (len=%d)\n",
+ ptr->name, startreg,len);
+ while (i<len) {
+ zr36016_writei(ptr, startreg++, data[i++]);
+ }
+
+ return i;
+}
+#endif
+
+/* =========================================================================
+ Basic datasets & init:
+
+ //TODO//
+ ========================================================================= */
+
+// needed offset values PAL NTSC SECAM
+static const int zr016_xoff[] = { 20, 20, 20 };
+static const int zr016_yoff[] = { 8, 9, 7 };
+
+static void
+zr36016_init (struct zr36016 *ptr)
+{
+ // stop any processing
+ zr36016_write(ptr, ZR016_GOSTOP, 0);
+
+ // mode setup (yuv422 in and out, compression/expansuon due to mode)
+ zr36016_write(ptr, ZR016_MODE,
+ ZR016_YUV422 | ZR016_YUV422_YUV422 |
+ (ptr->mode == CODEC_DO_COMPRESSION ?
+ ZR016_COMPRESSION : ZR016_EXPANSION));
+
+ // misc setup
+ zr36016_writei(ptr, ZR016I_SETUP1,
+ (ptr->xdec ? (ZR016_HRFL | ZR016_HORZ) : 0) |
+ (ptr->ydec ? ZR016_VERT : 0) | ZR016_CNTI);
+ zr36016_writei(ptr, ZR016I_SETUP2, ZR016_CCIR);
+
+ // Window setup
+ // (no extra offset for now, norm defines offset, default width height)
+ zr36016_writei(ptr, ZR016I_PAX_HI, ptr->width >> 8);
+ zr36016_writei(ptr, ZR016I_PAX_LO, ptr->width & 0xFF);
+ zr36016_writei(ptr, ZR016I_PAY_HI, ptr->height >> 8);
+ zr36016_writei(ptr, ZR016I_PAY_LO, ptr->height & 0xFF);
+ zr36016_writei(ptr, ZR016I_NAX_HI, ptr->xoff >> 8);
+ zr36016_writei(ptr, ZR016I_NAX_LO, ptr->xoff & 0xFF);
+ zr36016_writei(ptr, ZR016I_NAY_HI, ptr->yoff >> 8);
+ zr36016_writei(ptr, ZR016I_NAY_LO, ptr->yoff & 0xFF);
+
+ /* shall we continue now, please? */
+ zr36016_write(ptr, ZR016_GOSTOP, 1);
+}
+
+/* =========================================================================
+ CODEC API FUNCTIONS
+
+ this functions are accessed by the master via the API structure
+ ========================================================================= */
+
+/* set compression/expansion mode and launches codec -
+ this should be the last call from the master before starting processing */
+static int
+zr36016_set_mode (struct videocodec *codec,
+ int mode)
+{
+ struct zr36016 *ptr = (struct zr36016 *) codec->data;
+
+ dprintk(2, "%s: set_mode %d call\n", ptr->name, mode);
+
+ if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION))
+ return -EINVAL;
+
+ ptr->mode = mode;
+ zr36016_init(ptr);
+
+ return 0;
+}
+
+/* set picture size */
+static int
+zr36016_set_video (struct videocodec *codec,
+ struct tvnorm *norm,
+ struct vfe_settings *cap,
+ struct vfe_polarity *pol)
+{
+ struct zr36016 *ptr = (struct zr36016 *) codec->data;
+
+ dprintk(2, "%s: set_video %d.%d, %d/%d-%dx%d (0x%x) call\n",
+ ptr->name, norm->HStart, norm->VStart,
+ cap->x, cap->y, cap->width, cap->height,
+ cap->decimation);
+
+ /* if () return -EINVAL;
+ * trust the master driver that it knows what it does - so
+ * we allow invalid startx/y for now ... */
+ ptr->width = cap->width;
+ ptr->height = cap->height;
+ /* (Ronald) This is ugly. zoran_device.c, line 387
+ * already mentions what happens if HStart is even
+ * (blue faces, etc., cr/cb inversed). There's probably
+ * some good reason why HStart is 0 instead of 1, so I'm
+ * leaving it to this for now, but really... This can be
+ * done a lot simpler */
+ ptr->xoff = (norm->HStart ? norm->HStart : 1) + cap->x;
+ /* Something to note here (I don't understand it), setting
+ * VStart too high will cause the codec to 'not work'. I
+ * really don't get it. values of 16 (VStart) already break
+ * it here. Just '0' seems to work. More testing needed! */
+ ptr->yoff = norm->VStart + cap->y;
+ /* (Ronald) dzjeeh, can't this thing do hor_decimation = 4? */
+ ptr->xdec = ((cap->decimation & 0xff) == 1) ? 0 : 1;
+ ptr->ydec = (((cap->decimation >> 8) & 0xff) == 1) ? 0 : 1;
+
+ return 0;
+}
+
+/* additional control functions */
+static int
+zr36016_control (struct videocodec *codec,
+ int type,
+ int size,
+ void *data)
+{
+ struct zr36016 *ptr = (struct zr36016 *) codec->data;
+ int *ival = (int *) data;
+
+ dprintk(2, "%s: control %d call with %d byte\n", ptr->name, type,
+ size);
+
+ switch (type) {
+ case CODEC_G_STATUS: /* get last status - we don't know it ... */
+ if (size != sizeof(int))
+ return -EFAULT;
+ *ival = 0;
+ break;
+
+ case CODEC_G_CODEC_MODE:
+ if (size != sizeof(int))
+ return -EFAULT;
+ *ival = 0;
+ break;
+
+ case CODEC_S_CODEC_MODE:
+ if (size != sizeof(int))
+ return -EFAULT;
+ if (*ival != 0)
+ return -EINVAL;
+ /* not needed, do nothing */
+ return 0;
+
+ case CODEC_G_VFE:
+ case CODEC_S_VFE:
+ return 0;
+
+ case CODEC_S_MMAP:
+ /* not available, give an error */
+ return -ENXIO;
+
+ default:
+ return -EINVAL;
+ }
+
+ return size;
+}
+
+/* =========================================================================
+ Exit and unregister function:
+
+ Deinitializes Zoran's JPEG processor
+ ========================================================================= */
+
+static int
+zr36016_unset (struct videocodec *codec)
+{
+ struct zr36016 *ptr = codec->data;
+
+ if (ptr) {
+ /* do wee need some codec deinit here, too ???? */
+
+ dprintk(1, "%s: finished codec #%d\n", ptr->name,
+ ptr->num);
+ kfree(ptr);
+ codec->data = NULL;
+
+ zr36016_codecs--;
+ return 0;
+ }
+
+ return -EFAULT;
+}
+
+/* =========================================================================
+ Setup and registry function:
+
+ Initializes Zoran's JPEG processor
+
+ Also sets pixel size, average code size, mode (compr./decompr.)
+ (the given size is determined by the processor with the video interface)
+ ========================================================================= */
+
+static int
+zr36016_setup (struct videocodec *codec)
+{
+ struct zr36016 *ptr;
+ int res;
+
+ dprintk(2, "zr36016: initializing VFE subsystem #%d.\n",
+ zr36016_codecs);
+
+ if (zr36016_codecs == MAX_CODECS) {
+ dprintk(1,
+ KERN_ERR "zr36016: Can't attach more codecs!\n");
+ return -ENOSPC;
+ }
+ //mem structure init
+ codec->data = ptr = kzalloc(sizeof(struct zr36016), GFP_KERNEL);
+ if (NULL == ptr) {
+ dprintk(1, KERN_ERR "zr36016: Can't get enough memory!\n");
+ return -ENOMEM;
+ }
+
+ snprintf(ptr->name, sizeof(ptr->name), "zr36016[%d]",
+ zr36016_codecs);
+ ptr->num = zr36016_codecs++;
+ ptr->codec = codec;
+
+ //testing
+ res = zr36016_basic_test(ptr);
+ if (res < 0) {
+ zr36016_unset(codec);
+ return res;
+ }
+ //final setup
+ ptr->mode = CODEC_DO_COMPRESSION;
+ ptr->width = 768;
+ ptr->height = 288;
+ ptr->xdec = 1;
+ ptr->ydec = 0;
+ zr36016_init(ptr);
+
+ dprintk(1, KERN_INFO "%s: codec v%d attached and running\n",
+ ptr->name, ptr->version);
+
+ return 0;
+}
+
+static const struct videocodec zr36016_codec = {
+ .owner = THIS_MODULE,
+ .name = "zr36016",
+ .magic = 0L, // magic not used
+ .flags =
+ CODEC_FLAG_HARDWARE | CODEC_FLAG_VFE | CODEC_FLAG_ENCODER |
+ CODEC_FLAG_DECODER,
+ .type = CODEC_TYPE_ZR36016,
+ .setup = zr36016_setup, // functionality
+ .unset = zr36016_unset,
+ .set_mode = zr36016_set_mode,
+ .set_video = zr36016_set_video,
+ .control = zr36016_control,
+ // others are not used
+};
+
+/* =========================================================================
+ HOOK IN DRIVER AS KERNEL MODULE
+ ========================================================================= */
+
+static int __init
+zr36016_init_module (void)
+{
+ //dprintk(1, "ZR36016 driver %s\n",ZR016_VERSION);
+ zr36016_codecs = 0;
+ return videocodec_register(&zr36016_codec);
+}
+
+static void __exit
+zr36016_cleanup_module (void)
+{
+ if (zr36016_codecs) {
+ dprintk(1,
+ "zr36016: something's wrong - %d codecs left somehow.\n",
+ zr36016_codecs);
+ }
+ videocodec_unregister(&zr36016_codec);
+}
+
+module_init(zr36016_init_module);
+module_exit(zr36016_cleanup_module);
+
+MODULE_AUTHOR("Wolfgang Scherr <scherr@net4you.at>");
+MODULE_DESCRIPTION("Driver module for ZR36016 video frontends "
+ ZR016_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/zoran/zr36016.h b/drivers/media/video/zoran/zr36016.h
new file mode 100644
index 00000000..8c79229f
--- /dev/null
+++ b/drivers/media/video/zoran/zr36016.h
@@ -0,0 +1,111 @@
+/*
+ * Zoran ZR36016 basic configuration functions - header file
+ *
+ * Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at>
+ *
+ * $Id: zr36016.h,v 1.1.2.3 2003/01/14 21:18:07 rbultje Exp $
+ *
+ * ------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+#ifndef ZR36016_H
+#define ZR36016_H
+
+/* data stored for each zoran jpeg codec chip */
+struct zr36016 {
+ char name[32];
+ int num;
+ /* io datastructure */
+ struct videocodec *codec;
+ // coder status
+ __u8 version;
+ // actual coder setup
+ int mode;
+
+ __u16 xoff;
+ __u16 yoff;
+ __u16 width;
+ __u16 height;
+ __u16 xdec;
+ __u16 ydec;
+};
+
+/* direct register addresses */
+#define ZR016_GOSTOP 0x00
+#define ZR016_MODE 0x01
+#define ZR016_IADDR 0x02
+#define ZR016_IDATA 0x03
+
+/* indirect register addresses */
+#define ZR016I_SETUP1 0x00
+#define ZR016I_SETUP2 0x01
+#define ZR016I_NAX_LO 0x02
+#define ZR016I_NAX_HI 0x03
+#define ZR016I_PAX_LO 0x04
+#define ZR016I_PAX_HI 0x05
+#define ZR016I_NAY_LO 0x06
+#define ZR016I_NAY_HI 0x07
+#define ZR016I_PAY_LO 0x08
+#define ZR016I_PAY_HI 0x09
+#define ZR016I_NOL_LO 0x0a
+#define ZR016I_NOL_HI 0x0b
+
+/* possible values for mode register */
+#define ZR016_RGB444_YUV444 0x00
+#define ZR016_RGB444_YUV422 0x01
+#define ZR016_RGB444_YUV411 0x02
+#define ZR016_RGB444_Y400 0x03
+#define ZR016_RGB444_RGB444 0x04
+#define ZR016_YUV444_YUV444 0x08
+#define ZR016_YUV444_YUV422 0x09
+#define ZR016_YUV444_YUV411 0x0a
+#define ZR016_YUV444_Y400 0x0b
+#define ZR016_YUV444_RGB444 0x0c
+#define ZR016_YUV422_YUV422 0x11
+#define ZR016_YUV422_YUV411 0x12
+#define ZR016_YUV422_Y400 0x13
+#define ZR016_YUV411_YUV411 0x16
+#define ZR016_YUV411_Y400 0x17
+#define ZR016_4444_4444 0x19
+#define ZR016_100_100 0x1b
+
+#define ZR016_RGB444 0x00
+#define ZR016_YUV444 0x20
+#define ZR016_YUV422 0x40
+
+#define ZR016_COMPRESSION 0x80
+#define ZR016_EXPANSION 0x80
+
+/* possible values for setup 1 register */
+#define ZR016_CKRT 0x80
+#define ZR016_VERT 0x40
+#define ZR016_HORZ 0x20
+#define ZR016_HRFL 0x10
+#define ZR016_DSFL 0x08
+#define ZR016_SBFL 0x04
+#define ZR016_RSTR 0x02
+#define ZR016_CNTI 0x01
+
+/* possible values for setup 2 register */
+#define ZR016_SYEN 0x40
+#define ZR016_CCIR 0x04
+#define ZR016_SIGN 0x02
+#define ZR016_YMCS 0x01
+
+#endif /*fndef ZR36016_H */
diff --git a/drivers/media/video/zoran/zr36050.c b/drivers/media/video/zoran/zr36050.c
new file mode 100644
index 00000000..e1985609
--- /dev/null
+++ b/drivers/media/video/zoran/zr36050.c
@@ -0,0 +1,900 @@
+/*
+ * Zoran ZR36050 basic configuration functions
+ *
+ * Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at>
+ *
+ * $Id: zr36050.c,v 1.1.2.11 2003/08/03 14:54:53 rbultje Exp $
+ *
+ * ------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+#define ZR050_VERSION "v0.7.1"
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#include <linux/types.h>
+#include <linux/wait.h>
+
+/* I/O commands, error codes */
+#include <asm/io.h>
+
+/* headerfile of this module */
+#include "zr36050.h"
+
+/* codec io API */
+#include "videocodec.h"
+
+/* it doesn't make sense to have more than 20 or so,
+ just to prevent some unwanted loops */
+#define MAX_CODECS 20
+
+/* amount of chips attached via this driver */
+static int zr36050_codecs;
+
+/* debugging is available via module parameter */
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-4)");
+
+#define dprintk(num, format, args...) \
+ do { \
+ if (debug >= num) \
+ printk(format, ##args); \
+ } while (0)
+
+/* =========================================================================
+ Local hardware I/O functions:
+
+ read/write via codec layer (registers are located in the master device)
+ ========================================================================= */
+
+/* read and write functions */
+static u8
+zr36050_read (struct zr36050 *ptr,
+ u16 reg)
+{
+ u8 value = 0;
+
+ // just in case something is wrong...
+ if (ptr->codec->master_data->readreg)
+ value = (ptr->codec->master_data->readreg(ptr->codec,
+ reg)) & 0xFF;
+ else
+ dprintk(1,
+ KERN_ERR "%s: invalid I/O setup, nothing read!\n",
+ ptr->name);
+
+ dprintk(4, "%s: reading from 0x%04x: %02x\n", ptr->name, reg,
+ value);
+
+ return value;
+}
+
+static void
+zr36050_write (struct zr36050 *ptr,
+ u16 reg,
+ u8 value)
+{
+ dprintk(4, "%s: writing 0x%02x to 0x%04x\n", ptr->name, value,
+ reg);
+
+ // just in case something is wrong...
+ if (ptr->codec->master_data->writereg)
+ ptr->codec->master_data->writereg(ptr->codec, reg, value);
+ else
+ dprintk(1,
+ KERN_ERR
+ "%s: invalid I/O setup, nothing written!\n",
+ ptr->name);
+}
+
+/* =========================================================================
+ Local helper function:
+
+ status read
+ ========================================================================= */
+
+/* status is kept in datastructure */
+static u8
+zr36050_read_status1 (struct zr36050 *ptr)
+{
+ ptr->status1 = zr36050_read(ptr, ZR050_STATUS_1);
+
+ zr36050_read(ptr, 0);
+ return ptr->status1;
+}
+
+/* =========================================================================
+ Local helper function:
+
+ scale factor read
+ ========================================================================= */
+
+/* scale factor is kept in datastructure */
+static u16
+zr36050_read_scalefactor (struct zr36050 *ptr)
+{
+ ptr->scalefact = (zr36050_read(ptr, ZR050_SF_HI) << 8) |
+ (zr36050_read(ptr, ZR050_SF_LO) & 0xFF);
+
+ /* leave 0 selected for an eventually GO from master */
+ zr36050_read(ptr, 0);
+ return ptr->scalefact;
+}
+
+/* =========================================================================
+ Local helper function:
+
+ wait if codec is ready to proceed (end of processing) or time is over
+ ========================================================================= */
+
+static void
+zr36050_wait_end (struct zr36050 *ptr)
+{
+ int i = 0;
+
+ while (!(zr36050_read_status1(ptr) & 0x4)) {
+ udelay(1);
+ if (i++ > 200000) { // 200ms, there is for sure something wrong!!!
+ dprintk(1,
+ "%s: timeout at wait_end (last status: 0x%02x)\n",
+ ptr->name, ptr->status1);
+ break;
+ }
+ }
+}
+
+/* =========================================================================
+ Local helper function:
+
+ basic test of "connectivity", writes/reads to/from memory the SOF marker
+ ========================================================================= */
+
+static int
+zr36050_basic_test (struct zr36050 *ptr)
+{
+ zr36050_write(ptr, ZR050_SOF_IDX, 0x00);
+ zr36050_write(ptr, ZR050_SOF_IDX + 1, 0x00);
+ if ((zr36050_read(ptr, ZR050_SOF_IDX) |
+ zr36050_read(ptr, ZR050_SOF_IDX + 1)) != 0x0000) {
+ dprintk(1,
+ KERN_ERR
+ "%s: attach failed, can't connect to jpeg processor!\n",
+ ptr->name);
+ return -ENXIO;
+ }
+ zr36050_write(ptr, ZR050_SOF_IDX, 0xff);
+ zr36050_write(ptr, ZR050_SOF_IDX + 1, 0xc0);
+ if (((zr36050_read(ptr, ZR050_SOF_IDX) << 8) |
+ zr36050_read(ptr, ZR050_SOF_IDX + 1)) != 0xffc0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: attach failed, can't connect to jpeg processor!\n",
+ ptr->name);
+ return -ENXIO;
+ }
+
+ zr36050_wait_end(ptr);
+ if ((ptr->status1 & 0x4) == 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: attach failed, jpeg processor failed (end flag)!\n",
+ ptr->name);
+ return -EBUSY;
+ }
+
+ return 0; /* looks good! */
+}
+
+/* =========================================================================
+ Local helper function:
+
+ simple loop for pushing the init datasets
+ ========================================================================= */
+
+static int
+zr36050_pushit (struct zr36050 *ptr,
+ u16 startreg,
+ u16 len,
+ const char *data)
+{
+ int i = 0;
+
+ dprintk(4, "%s: write data block to 0x%04x (len=%d)\n", ptr->name,
+ startreg, len);
+ while (i < len) {
+ zr36050_write(ptr, startreg++, data[i++]);
+ }
+
+ return i;
+}
+
+/* =========================================================================
+ Basic datasets:
+
+ jpeg baseline setup data (you find it on lots places in internet, or just
+ extract it from any regular .jpg image...)
+
+ Could be variable, but until it's not needed it they are just fixed to save
+ memory. Otherwise expand zr36050 structure with arrays, push the values to
+ it and initialize from there, as e.g. the linux zr36057/60 driver does it.
+ ========================================================================= */
+
+static const char zr36050_dqt[0x86] = {
+ 0xff, 0xdb, //Marker: DQT
+ 0x00, 0x84, //Length: 2*65+2
+ 0x00, //Pq,Tq first table
+ 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e,
+ 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28,
+ 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25,
+ 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33,
+ 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44,
+ 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57,
+ 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71,
+ 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63,
+ 0x01, //Pq,Tq second table
+ 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a,
+ 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63
+};
+
+static const char zr36050_dht[0x1a4] = {
+ 0xff, 0xc4, //Marker: DHT
+ 0x01, 0xa2, //Length: 2*AC, 2*DC
+ 0x00, //DC first table
+ 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
+ 0x01, //DC second table
+ 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
+ 0x10, //AC first table
+ 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03,
+ 0x05, 0x05, 0x04, 0x04, 0x00, 0x00,
+ 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11,
+ 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61,
+ 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1,
+ 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24,
+ 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17,
+ 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34,
+ 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44,
+ 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56,
+ 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66,
+ 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+ 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+ 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
+ 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8,
+ 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9,
+ 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8,
+ 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9,
+ 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
+ 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
+ 0xF8, 0xF9, 0xFA,
+ 0x11, //AC second table
+ 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04,
+ 0x07, 0x05, 0x04, 0x04, 0x00, 0x01,
+ 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04,
+ 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
+ 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
+ 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62,
+ 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25,
+ 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A,
+ 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44,
+ 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56,
+ 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66,
+ 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+ 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
+ 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
+ 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8,
+ 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
+ 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8,
+ 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
+ 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
+ 0xF9, 0xFA
+};
+
+/* jpeg baseline setup, this is just fixed in this driver (YUV pictures) */
+#define NO_OF_COMPONENTS 0x3 //Y,U,V
+#define BASELINE_PRECISION 0x8 //MCU size (?)
+static const char zr36050_tq[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's QT
+static const char zr36050_td[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's DC
+static const char zr36050_ta[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's AC
+
+/* horizontal 422 decimation setup (maybe we support 411 or so later, too) */
+static const char zr36050_decimation_h[8] = { 2, 1, 1, 0, 0, 0, 0, 0 };
+static const char zr36050_decimation_v[8] = { 1, 1, 1, 0, 0, 0, 0, 0 };
+
+/* =========================================================================
+ Local helper functions:
+
+ calculation and setup of parameter-dependent JPEG baseline segments
+ (needed for compression only)
+ ========================================================================= */
+
+/* ------------------------------------------------------------------------- */
+
+/* SOF (start of frame) segment depends on width, height and sampling ratio
+ of each color component */
+
+static int
+zr36050_set_sof (struct zr36050 *ptr)
+{
+ char sof_data[34]; // max. size of register set
+ int i;
+
+ dprintk(3, "%s: write SOF (%dx%d, %d components)\n", ptr->name,
+ ptr->width, ptr->height, NO_OF_COMPONENTS);
+ sof_data[0] = 0xff;
+ sof_data[1] = 0xc0;
+ sof_data[2] = 0x00;
+ sof_data[3] = (3 * NO_OF_COMPONENTS) + 8;
+ sof_data[4] = BASELINE_PRECISION; // only '8' possible with zr36050
+ sof_data[5] = (ptr->height) >> 8;
+ sof_data[6] = (ptr->height) & 0xff;
+ sof_data[7] = (ptr->width) >> 8;
+ sof_data[8] = (ptr->width) & 0xff;
+ sof_data[9] = NO_OF_COMPONENTS;
+ for (i = 0; i < NO_OF_COMPONENTS; i++) {
+ sof_data[10 + (i * 3)] = i; // index identifier
+ sof_data[11 + (i * 3)] = (ptr->h_samp_ratio[i] << 4) | (ptr->v_samp_ratio[i]); // sampling ratios
+ sof_data[12 + (i * 3)] = zr36050_tq[i]; // Q table selection
+ }
+ return zr36050_pushit(ptr, ZR050_SOF_IDX,
+ (3 * NO_OF_COMPONENTS) + 10, sof_data);
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* SOS (start of scan) segment depends on the used scan components
+ of each color component */
+
+static int
+zr36050_set_sos (struct zr36050 *ptr)
+{
+ char sos_data[16]; // max. size of register set
+ int i;
+
+ dprintk(3, "%s: write SOS\n", ptr->name);
+ sos_data[0] = 0xff;
+ sos_data[1] = 0xda;
+ sos_data[2] = 0x00;
+ sos_data[3] = 2 + 1 + (2 * NO_OF_COMPONENTS) + 3;
+ sos_data[4] = NO_OF_COMPONENTS;
+ for (i = 0; i < NO_OF_COMPONENTS; i++) {
+ sos_data[5 + (i * 2)] = i; // index
+ sos_data[6 + (i * 2)] = (zr36050_td[i] << 4) | zr36050_ta[i]; // AC/DC tbl.sel.
+ }
+ sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 2] = 00; // scan start
+ sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 3] = 0x3F;
+ sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 4] = 00;
+ return zr36050_pushit(ptr, ZR050_SOS1_IDX,
+ 4 + 1 + (2 * NO_OF_COMPONENTS) + 3,
+ sos_data);
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* DRI (define restart interval) */
+
+static int
+zr36050_set_dri (struct zr36050 *ptr)
+{
+ char dri_data[6]; // max. size of register set
+
+ dprintk(3, "%s: write DRI\n", ptr->name);
+ dri_data[0] = 0xff;
+ dri_data[1] = 0xdd;
+ dri_data[2] = 0x00;
+ dri_data[3] = 0x04;
+ dri_data[4] = ptr->dri >> 8;
+ dri_data[5] = ptr->dri & 0xff;
+ return zr36050_pushit(ptr, ZR050_DRI_IDX, 6, dri_data);
+}
+
+/* =========================================================================
+ Setup function:
+
+ Setup compression/decompression of Zoran's JPEG processor
+ ( see also zoran 36050 manual )
+
+ ... sorry for the spaghetti code ...
+ ========================================================================= */
+static void
+zr36050_init (struct zr36050 *ptr)
+{
+ int sum = 0;
+ long bitcnt, tmp;
+
+ if (ptr->mode == CODEC_DO_COMPRESSION) {
+ dprintk(2, "%s: COMPRESSION SETUP\n", ptr->name);
+
+ /* 050 communicates with 057 in master mode */
+ zr36050_write(ptr, ZR050_HARDWARE, ZR050_HW_MSTR);
+
+ /* encoding table preload for compression */
+ zr36050_write(ptr, ZR050_MODE,
+ ZR050_MO_COMP | ZR050_MO_TLM);
+ zr36050_write(ptr, ZR050_OPTIONS, 0);
+
+ /* disable all IRQs */
+ zr36050_write(ptr, ZR050_INT_REQ_0, 0);
+ zr36050_write(ptr, ZR050_INT_REQ_1, 3); // low 2 bits always 1
+
+ /* volume control settings */
+ /*zr36050_write(ptr, ZR050_MBCV, ptr->max_block_vol);*/
+ zr36050_write(ptr, ZR050_SF_HI, ptr->scalefact >> 8);
+ zr36050_write(ptr, ZR050_SF_LO, ptr->scalefact & 0xff);
+
+ zr36050_write(ptr, ZR050_AF_HI, 0xff);
+ zr36050_write(ptr, ZR050_AF_M, 0xff);
+ zr36050_write(ptr, ZR050_AF_LO, 0xff);
+
+ /* setup the variable jpeg tables */
+ sum += zr36050_set_sof(ptr);
+ sum += zr36050_set_sos(ptr);
+ sum += zr36050_set_dri(ptr);
+
+ /* setup the fixed jpeg tables - maybe variable, though -
+ * (see table init section above) */
+ dprintk(3, "%s: write DQT, DHT, APP\n", ptr->name);
+ sum += zr36050_pushit(ptr, ZR050_DQT_IDX,
+ sizeof(zr36050_dqt), zr36050_dqt);
+ sum += zr36050_pushit(ptr, ZR050_DHT_IDX,
+ sizeof(zr36050_dht), zr36050_dht);
+ zr36050_write(ptr, ZR050_APP_IDX, 0xff);
+ zr36050_write(ptr, ZR050_APP_IDX + 1, 0xe0 + ptr->app.appn);
+ zr36050_write(ptr, ZR050_APP_IDX + 2, 0x00);
+ zr36050_write(ptr, ZR050_APP_IDX + 3, ptr->app.len + 2);
+ sum += zr36050_pushit(ptr, ZR050_APP_IDX + 4, 60,
+ ptr->app.data) + 4;
+ zr36050_write(ptr, ZR050_COM_IDX, 0xff);
+ zr36050_write(ptr, ZR050_COM_IDX + 1, 0xfe);
+ zr36050_write(ptr, ZR050_COM_IDX + 2, 0x00);
+ zr36050_write(ptr, ZR050_COM_IDX + 3, ptr->com.len + 2);
+ sum += zr36050_pushit(ptr, ZR050_COM_IDX + 4, 60,
+ ptr->com.data) + 4;
+
+ /* do the internal huffman table preload */
+ zr36050_write(ptr, ZR050_MARKERS_EN, ZR050_ME_DHTI);
+
+ zr36050_write(ptr, ZR050_GO, 1); // launch codec
+ zr36050_wait_end(ptr);
+ dprintk(2, "%s: Status after table preload: 0x%02x\n",
+ ptr->name, ptr->status1);
+
+ if ((ptr->status1 & 0x4) == 0) {
+ dprintk(1, KERN_ERR "%s: init aborted!\n",
+ ptr->name);
+ return; // something is wrong, its timed out!!!!
+ }
+
+ /* setup misc. data for compression (target code sizes) */
+
+ /* size of compressed code to reach without header data */
+ sum = ptr->real_code_vol - sum;
+ bitcnt = sum << 3; /* need the size in bits */
+
+ tmp = bitcnt >> 16;
+ dprintk(3,
+ "%s: code: csize=%d, tot=%d, bit=%ld, highbits=%ld\n",
+ ptr->name, sum, ptr->real_code_vol, bitcnt, tmp);
+ zr36050_write(ptr, ZR050_TCV_NET_HI, tmp >> 8);
+ zr36050_write(ptr, ZR050_TCV_NET_MH, tmp & 0xff);
+ tmp = bitcnt & 0xffff;
+ zr36050_write(ptr, ZR050_TCV_NET_ML, tmp >> 8);
+ zr36050_write(ptr, ZR050_TCV_NET_LO, tmp & 0xff);
+
+ bitcnt -= bitcnt >> 7; // bits without stuffing
+ bitcnt -= ((bitcnt * 5) >> 6); // bits without eob
+
+ tmp = bitcnt >> 16;
+ dprintk(3, "%s: code: nettobit=%ld, highnettobits=%ld\n",
+ ptr->name, bitcnt, tmp);
+ zr36050_write(ptr, ZR050_TCV_DATA_HI, tmp >> 8);
+ zr36050_write(ptr, ZR050_TCV_DATA_MH, tmp & 0xff);
+ tmp = bitcnt & 0xffff;
+ zr36050_write(ptr, ZR050_TCV_DATA_ML, tmp >> 8);
+ zr36050_write(ptr, ZR050_TCV_DATA_LO, tmp & 0xff);
+
+ /* compression setup with or without bitrate control */
+ zr36050_write(ptr, ZR050_MODE,
+ ZR050_MO_COMP | ZR050_MO_PASS2 |
+ (ptr->bitrate_ctrl ? ZR050_MO_BRC : 0));
+
+ /* this headers seem to deliver "valid AVI" jpeg frames */
+ zr36050_write(ptr, ZR050_MARKERS_EN,
+ ZR050_ME_DQT | ZR050_ME_DHT |
+ ((ptr->app.len > 0) ? ZR050_ME_APP : 0) |
+ ((ptr->com.len > 0) ? ZR050_ME_COM : 0));
+ } else {
+ dprintk(2, "%s: EXPANSION SETUP\n", ptr->name);
+
+ /* 050 communicates with 055 in master mode */
+ zr36050_write(ptr, ZR050_HARDWARE,
+ ZR050_HW_MSTR | ZR050_HW_CFIS_2_CLK);
+
+ /* encoding table preload */
+ zr36050_write(ptr, ZR050_MODE, ZR050_MO_TLM);
+
+ /* disable all IRQs */
+ zr36050_write(ptr, ZR050_INT_REQ_0, 0);
+ zr36050_write(ptr, ZR050_INT_REQ_1, 3); // low 2 bits always 1
+
+ dprintk(3, "%s: write DHT\n", ptr->name);
+ zr36050_pushit(ptr, ZR050_DHT_IDX, sizeof(zr36050_dht),
+ zr36050_dht);
+
+ /* do the internal huffman table preload */
+ zr36050_write(ptr, ZR050_MARKERS_EN, ZR050_ME_DHTI);
+
+ zr36050_write(ptr, ZR050_GO, 1); // launch codec
+ zr36050_wait_end(ptr);
+ dprintk(2, "%s: Status after table preload: 0x%02x\n",
+ ptr->name, ptr->status1);
+
+ if ((ptr->status1 & 0x4) == 0) {
+ dprintk(1, KERN_ERR "%s: init aborted!\n",
+ ptr->name);
+ return; // something is wrong, its timed out!!!!
+ }
+
+ /* setup misc. data for expansion */
+ zr36050_write(ptr, ZR050_MODE, 0);
+ zr36050_write(ptr, ZR050_MARKERS_EN, 0);
+ }
+
+ /* adr on selected, to allow GO from master */
+ zr36050_read(ptr, 0);
+}
+
+/* =========================================================================
+ CODEC API FUNCTIONS
+
+ this functions are accessed by the master via the API structure
+ ========================================================================= */
+
+/* set compression/expansion mode and launches codec -
+ this should be the last call from the master before starting processing */
+static int
+zr36050_set_mode (struct videocodec *codec,
+ int mode)
+{
+ struct zr36050 *ptr = (struct zr36050 *) codec->data;
+
+ dprintk(2, "%s: set_mode %d call\n", ptr->name, mode);
+
+ if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION))
+ return -EINVAL;
+
+ ptr->mode = mode;
+ zr36050_init(ptr);
+
+ return 0;
+}
+
+/* set picture size (norm is ignored as the codec doesn't know about it) */
+static int
+zr36050_set_video (struct videocodec *codec,
+ struct tvnorm *norm,
+ struct vfe_settings *cap,
+ struct vfe_polarity *pol)
+{
+ struct zr36050 *ptr = (struct zr36050 *) codec->data;
+ int size;
+
+ dprintk(2, "%s: set_video %d.%d, %d/%d-%dx%d (0x%x) q%d call\n",
+ ptr->name, norm->HStart, norm->VStart,
+ cap->x, cap->y, cap->width, cap->height,
+ cap->decimation, cap->quality);
+ /* if () return -EINVAL;
+ * trust the master driver that it knows what it does - so
+ * we allow invalid startx/y and norm for now ... */
+ ptr->width = cap->width / (cap->decimation & 0xff);
+ ptr->height = cap->height / ((cap->decimation >> 8) & 0xff);
+
+ /* (KM) JPEG quality */
+ size = ptr->width * ptr->height;
+ size *= 16; /* size in bits */
+ /* apply quality setting */
+ size = size * cap->quality / 200;
+
+ /* Minimum: 1kb */
+ if (size < 8192)
+ size = 8192;
+ /* Maximum: 7/8 of code buffer */
+ if (size > ptr->total_code_vol * 7)
+ size = ptr->total_code_vol * 7;
+
+ ptr->real_code_vol = size >> 3; /* in bytes */
+
+ /* Set max_block_vol here (previously in zr36050_init, moved
+ * here for consistency with zr36060 code */
+ zr36050_write(ptr, ZR050_MBCV, ptr->max_block_vol);
+
+ return 0;
+}
+
+/* additional control functions */
+static int
+zr36050_control (struct videocodec *codec,
+ int type,
+ int size,
+ void *data)
+{
+ struct zr36050 *ptr = (struct zr36050 *) codec->data;
+ int *ival = (int *) data;
+
+ dprintk(2, "%s: control %d call with %d byte\n", ptr->name, type,
+ size);
+
+ switch (type) {
+ case CODEC_G_STATUS: /* get last status */
+ if (size != sizeof(int))
+ return -EFAULT;
+ zr36050_read_status1(ptr);
+ *ival = ptr->status1;
+ break;
+
+ case CODEC_G_CODEC_MODE:
+ if (size != sizeof(int))
+ return -EFAULT;
+ *ival = CODEC_MODE_BJPG;
+ break;
+
+ case CODEC_S_CODEC_MODE:
+ if (size != sizeof(int))
+ return -EFAULT;
+ if (*ival != CODEC_MODE_BJPG)
+ return -EINVAL;
+ /* not needed, do nothing */
+ return 0;
+
+ case CODEC_G_VFE:
+ case CODEC_S_VFE:
+ /* not needed, do nothing */
+ return 0;
+
+ case CODEC_S_MMAP:
+ /* not available, give an error */
+ return -ENXIO;
+
+ case CODEC_G_JPEG_TDS_BYTE: /* get target volume in byte */
+ if (size != sizeof(int))
+ return -EFAULT;
+ *ival = ptr->total_code_vol;
+ break;
+
+ case CODEC_S_JPEG_TDS_BYTE: /* get target volume in byte */
+ if (size != sizeof(int))
+ return -EFAULT;
+ ptr->total_code_vol = *ival;
+ /* (Kieran Morrissey)
+ * code copied from zr36060.c to ensure proper bitrate */
+ ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3;
+ break;
+
+ case CODEC_G_JPEG_SCALE: /* get scaling factor */
+ if (size != sizeof(int))
+ return -EFAULT;
+ *ival = zr36050_read_scalefactor(ptr);
+ break;
+
+ case CODEC_S_JPEG_SCALE: /* set scaling factor */
+ if (size != sizeof(int))
+ return -EFAULT;
+ ptr->scalefact = *ival;
+ break;
+
+ case CODEC_G_JPEG_APP_DATA: { /* get appn marker data */
+ struct jpeg_app_marker *app = data;
+
+ if (size != sizeof(struct jpeg_app_marker))
+ return -EFAULT;
+
+ *app = ptr->app;
+ break;
+ }
+
+ case CODEC_S_JPEG_APP_DATA: { /* set appn marker data */
+ struct jpeg_app_marker *app = data;
+
+ if (size != sizeof(struct jpeg_app_marker))
+ return -EFAULT;
+
+ ptr->app = *app;
+ break;
+ }
+
+ case CODEC_G_JPEG_COM_DATA: { /* get comment marker data */
+ struct jpeg_com_marker *com = data;
+
+ if (size != sizeof(struct jpeg_com_marker))
+ return -EFAULT;
+
+ *com = ptr->com;
+ break;
+ }
+
+ case CODEC_S_JPEG_COM_DATA: { /* set comment marker data */
+ struct jpeg_com_marker *com = data;
+
+ if (size != sizeof(struct jpeg_com_marker))
+ return -EFAULT;
+
+ ptr->com = *com;
+ break;
+ }
+
+ default:
+ return -EINVAL;
+ }
+
+ return size;
+}
+
+/* =========================================================================
+ Exit and unregister function:
+
+ Deinitializes Zoran's JPEG processor
+ ========================================================================= */
+
+static int
+zr36050_unset (struct videocodec *codec)
+{
+ struct zr36050 *ptr = codec->data;
+
+ if (ptr) {
+ /* do wee need some codec deinit here, too ???? */
+
+ dprintk(1, "%s: finished codec #%d\n", ptr->name,
+ ptr->num);
+ kfree(ptr);
+ codec->data = NULL;
+
+ zr36050_codecs--;
+ return 0;
+ }
+
+ return -EFAULT;
+}
+
+/* =========================================================================
+ Setup and registry function:
+
+ Initializes Zoran's JPEG processor
+
+ Also sets pixel size, average code size, mode (compr./decompr.)
+ (the given size is determined by the processor with the video interface)
+ ========================================================================= */
+
+static int
+zr36050_setup (struct videocodec *codec)
+{
+ struct zr36050 *ptr;
+ int res;
+
+ dprintk(2, "zr36050: initializing MJPEG subsystem #%d.\n",
+ zr36050_codecs);
+
+ if (zr36050_codecs == MAX_CODECS) {
+ dprintk(1,
+ KERN_ERR "zr36050: Can't attach more codecs!\n");
+ return -ENOSPC;
+ }
+ //mem structure init
+ codec->data = ptr = kzalloc(sizeof(struct zr36050), GFP_KERNEL);
+ if (NULL == ptr) {
+ dprintk(1, KERN_ERR "zr36050: Can't get enough memory!\n");
+ return -ENOMEM;
+ }
+
+ snprintf(ptr->name, sizeof(ptr->name), "zr36050[%d]",
+ zr36050_codecs);
+ ptr->num = zr36050_codecs++;
+ ptr->codec = codec;
+
+ //testing
+ res = zr36050_basic_test(ptr);
+ if (res < 0) {
+ zr36050_unset(codec);
+ return res;
+ }
+ //final setup
+ memcpy(ptr->h_samp_ratio, zr36050_decimation_h, 8);
+ memcpy(ptr->v_samp_ratio, zr36050_decimation_v, 8);
+
+ ptr->bitrate_ctrl = 0; /* 0 or 1 - fixed file size flag
+ * (what is the difference?) */
+ ptr->mode = CODEC_DO_COMPRESSION;
+ ptr->width = 384;
+ ptr->height = 288;
+ ptr->total_code_vol = 16000;
+ ptr->max_block_vol = 240;
+ ptr->scalefact = 0x100;
+ ptr->dri = 1;
+
+ /* no app/com marker by default */
+ ptr->app.appn = 0;
+ ptr->app.len = 0;
+ ptr->com.len = 0;
+
+ zr36050_init(ptr);
+
+ dprintk(1, KERN_INFO "%s: codec attached and running\n",
+ ptr->name);
+
+ return 0;
+}
+
+static const struct videocodec zr36050_codec = {
+ .owner = THIS_MODULE,
+ .name = "zr36050",
+ .magic = 0L, // magic not used
+ .flags =
+ CODEC_FLAG_JPEG | CODEC_FLAG_HARDWARE | CODEC_FLAG_ENCODER |
+ CODEC_FLAG_DECODER,
+ .type = CODEC_TYPE_ZR36050,
+ .setup = zr36050_setup, // functionality
+ .unset = zr36050_unset,
+ .set_mode = zr36050_set_mode,
+ .set_video = zr36050_set_video,
+ .control = zr36050_control,
+ // others are not used
+};
+
+/* =========================================================================
+ HOOK IN DRIVER AS KERNEL MODULE
+ ========================================================================= */
+
+static int __init
+zr36050_init_module (void)
+{
+ //dprintk(1, "ZR36050 driver %s\n",ZR050_VERSION);
+ zr36050_codecs = 0;
+ return videocodec_register(&zr36050_codec);
+}
+
+static void __exit
+zr36050_cleanup_module (void)
+{
+ if (zr36050_codecs) {
+ dprintk(1,
+ "zr36050: something's wrong - %d codecs left somehow.\n",
+ zr36050_codecs);
+ }
+ videocodec_unregister(&zr36050_codec);
+}
+
+module_init(zr36050_init_module);
+module_exit(zr36050_cleanup_module);
+
+MODULE_AUTHOR("Wolfgang Scherr <scherr@net4you.at>");
+MODULE_DESCRIPTION("Driver module for ZR36050 jpeg processors "
+ ZR050_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/zoran/zr36050.h b/drivers/media/video/zoran/zr36050.h
new file mode 100644
index 00000000..9f52f0cd
--- /dev/null
+++ b/drivers/media/video/zoran/zr36050.h
@@ -0,0 +1,184 @@
+/*
+ * Zoran ZR36050 basic configuration functions - header file
+ *
+ * Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at>
+ *
+ * $Id: zr36050.h,v 1.1.2.2 2003/01/14 21:18:22 rbultje Exp $
+ *
+ * ------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+#ifndef ZR36050_H
+#define ZR36050_H
+
+#include "videocodec.h"
+
+/* data stored for each zoran jpeg codec chip */
+struct zr36050 {
+ char name[32];
+ int num;
+ /* io datastructure */
+ struct videocodec *codec;
+ // last coder status
+ __u8 status1;
+ // actual coder setup
+ int mode;
+
+ __u16 width;
+ __u16 height;
+
+ __u16 bitrate_ctrl;
+
+ __u32 total_code_vol;
+ __u32 real_code_vol;
+ __u16 max_block_vol;
+
+ __u8 h_samp_ratio[8];
+ __u8 v_samp_ratio[8];
+ __u16 scalefact;
+ __u16 dri;
+
+ /* com/app marker */
+ struct jpeg_com_marker com;
+ struct jpeg_app_marker app;
+};
+
+/* zr36050 register addresses */
+#define ZR050_GO 0x000
+#define ZR050_HARDWARE 0x002
+#define ZR050_MODE 0x003
+#define ZR050_OPTIONS 0x004
+#define ZR050_MBCV 0x005
+#define ZR050_MARKERS_EN 0x006
+#define ZR050_INT_REQ_0 0x007
+#define ZR050_INT_REQ_1 0x008
+#define ZR050_TCV_NET_HI 0x009
+#define ZR050_TCV_NET_MH 0x00a
+#define ZR050_TCV_NET_ML 0x00b
+#define ZR050_TCV_NET_LO 0x00c
+#define ZR050_TCV_DATA_HI 0x00d
+#define ZR050_TCV_DATA_MH 0x00e
+#define ZR050_TCV_DATA_ML 0x00f
+#define ZR050_TCV_DATA_LO 0x010
+#define ZR050_SF_HI 0x011
+#define ZR050_SF_LO 0x012
+#define ZR050_AF_HI 0x013
+#define ZR050_AF_M 0x014
+#define ZR050_AF_LO 0x015
+#define ZR050_ACV_HI 0x016
+#define ZR050_ACV_MH 0x017
+#define ZR050_ACV_ML 0x018
+#define ZR050_ACV_LO 0x019
+#define ZR050_ACT_HI 0x01a
+#define ZR050_ACT_MH 0x01b
+#define ZR050_ACT_ML 0x01c
+#define ZR050_ACT_LO 0x01d
+#define ZR050_ACV_TRUN_HI 0x01e
+#define ZR050_ACV_TRUN_MH 0x01f
+#define ZR050_ACV_TRUN_ML 0x020
+#define ZR050_ACV_TRUN_LO 0x021
+#define ZR050_STATUS_0 0x02e
+#define ZR050_STATUS_1 0x02f
+
+#define ZR050_SOF_IDX 0x040
+#define ZR050_SOS1_IDX 0x07a
+#define ZR050_SOS2_IDX 0x08a
+#define ZR050_SOS3_IDX 0x09a
+#define ZR050_SOS4_IDX 0x0aa
+#define ZR050_DRI_IDX 0x0c0
+#define ZR050_DNL_IDX 0x0c6
+#define ZR050_DQT_IDX 0x0cc
+#define ZR050_DHT_IDX 0x1d4
+#define ZR050_APP_IDX 0x380
+#define ZR050_COM_IDX 0x3c0
+
+/* zr36050 hardware register bits */
+
+#define ZR050_HW_BSWD 0x80
+#define ZR050_HW_MSTR 0x40
+#define ZR050_HW_DMA 0x20
+#define ZR050_HW_CFIS_1_CLK 0x00
+#define ZR050_HW_CFIS_2_CLK 0x04
+#define ZR050_HW_CFIS_3_CLK 0x08
+#define ZR050_HW_CFIS_4_CLK 0x0C
+#define ZR050_HW_CFIS_5_CLK 0x10
+#define ZR050_HW_CFIS_6_CLK 0x14
+#define ZR050_HW_CFIS_7_CLK 0x18
+#define ZR050_HW_CFIS_8_CLK 0x1C
+#define ZR050_HW_BELE 0x01
+
+/* zr36050 mode register bits */
+
+#define ZR050_MO_COMP 0x80
+#define ZR050_MO_COMP 0x80
+#define ZR050_MO_ATP 0x40
+#define ZR050_MO_PASS2 0x20
+#define ZR050_MO_TLM 0x10
+#define ZR050_MO_DCONLY 0x08
+#define ZR050_MO_BRC 0x04
+
+#define ZR050_MO_ATP 0x40
+#define ZR050_MO_PASS2 0x20
+#define ZR050_MO_TLM 0x10
+#define ZR050_MO_DCONLY 0x08
+
+/* zr36050 option register bits */
+
+#define ZR050_OP_NSCN_1 0x00
+#define ZR050_OP_NSCN_2 0x20
+#define ZR050_OP_NSCN_3 0x40
+#define ZR050_OP_NSCN_4 0x60
+#define ZR050_OP_NSCN_5 0x80
+#define ZR050_OP_NSCN_6 0xA0
+#define ZR050_OP_NSCN_7 0xC0
+#define ZR050_OP_NSCN_8 0xE0
+#define ZR050_OP_OVF 0x10
+
+
+/* zr36050 markers-enable register bits */
+
+#define ZR050_ME_APP 0x80
+#define ZR050_ME_COM 0x40
+#define ZR050_ME_DRI 0x20
+#define ZR050_ME_DQT 0x10
+#define ZR050_ME_DHT 0x08
+#define ZR050_ME_DNL 0x04
+#define ZR050_ME_DQTI 0x02
+#define ZR050_ME_DHTI 0x01
+
+/* zr36050 status0/1 register bit masks */
+
+#define ZR050_ST_RST_MASK 0x20
+#define ZR050_ST_SOF_MASK 0x02
+#define ZR050_ST_SOS_MASK 0x02
+#define ZR050_ST_DATRDY_MASK 0x80
+#define ZR050_ST_MRKDET_MASK 0x40
+#define ZR050_ST_RFM_MASK 0x10
+#define ZR050_ST_RFD_MASK 0x08
+#define ZR050_ST_END_MASK 0x04
+#define ZR050_ST_TCVOVF_MASK 0x02
+#define ZR050_ST_DATOVF_MASK 0x01
+
+/* pixel component idx */
+
+#define ZR050_Y_COMPONENT 0
+#define ZR050_U_COMPONENT 1
+#define ZR050_V_COMPONENT 2
+
+#endif /*fndef ZR36050_H */
diff --git a/drivers/media/video/zoran/zr36057.h b/drivers/media/video/zoran/zr36057.h
new file mode 100644
index 00000000..54c9362a
--- /dev/null
+++ b/drivers/media/video/zoran/zr36057.h
@@ -0,0 +1,168 @@
+/*
+ * zr36057.h - zr36057 register offsets
+ *
+ * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _ZR36057_H_
+#define _ZR36057_H_
+
+
+/* Zoran ZR36057 registers */
+
+#define ZR36057_VFEHCR 0x000 /* Video Front End, Horizontal Configuration Register */
+#define ZR36057_VFEHCR_HSPol (1<<30)
+#define ZR36057_VFEHCR_HStart 10
+#define ZR36057_VFEHCR_HEnd 0
+#define ZR36057_VFEHCR_Hmask 0x3ff
+
+#define ZR36057_VFEVCR 0x004 /* Video Front End, Vertical Configuration Register */
+#define ZR36057_VFEVCR_VSPol (1<<30)
+#define ZR36057_VFEVCR_VStart 10
+#define ZR36057_VFEVCR_VEnd 0
+#define ZR36057_VFEVCR_Vmask 0x3ff
+
+#define ZR36057_VFESPFR 0x008 /* Video Front End, Scaler and Pixel Format Register */
+#define ZR36057_VFESPFR_ExtFl (1<<26)
+#define ZR36057_VFESPFR_TopField (1<<25)
+#define ZR36057_VFESPFR_VCLKPol (1<<24)
+#define ZR36057_VFESPFR_HFilter 21
+#define ZR36057_VFESPFR_HorDcm 14
+#define ZR36057_VFESPFR_VerDcm 8
+#define ZR36057_VFESPFR_DispMode 6
+#define ZR36057_VFESPFR_YUV422 (0<<3)
+#define ZR36057_VFESPFR_RGB888 (1<<3)
+#define ZR36057_VFESPFR_RGB565 (2<<3)
+#define ZR36057_VFESPFR_RGB555 (3<<3)
+#define ZR36057_VFESPFR_ErrDif (1<<2)
+#define ZR36057_VFESPFR_Pack24 (1<<1)
+#define ZR36057_VFESPFR_LittleEndian (1<<0)
+
+#define ZR36057_VDTR 0x00c /* Video Display "Top" Register */
+
+#define ZR36057_VDBR 0x010 /* Video Display "Bottom" Register */
+
+#define ZR36057_VSSFGR 0x014 /* Video Stride, Status, and Frame Grab Register */
+#define ZR36057_VSSFGR_DispStride 16
+#define ZR36057_VSSFGR_VidOvf (1<<8)
+#define ZR36057_VSSFGR_SnapShot (1<<1)
+#define ZR36057_VSSFGR_FrameGrab (1<<0)
+
+#define ZR36057_VDCR 0x018 /* Video Display Configuration Register */
+#define ZR36057_VDCR_VidEn (1<<31)
+#define ZR36057_VDCR_MinPix 24
+#define ZR36057_VDCR_Triton (1<<24)
+#define ZR36057_VDCR_VidWinHt 12
+#define ZR36057_VDCR_VidWinWid 0
+
+#define ZR36057_MMTR 0x01c /* Masking Map "Top" Register */
+
+#define ZR36057_MMBR 0x020 /* Masking Map "Bottom" Register */
+
+#define ZR36057_OCR 0x024 /* Overlay Control Register */
+#define ZR36057_OCR_OvlEnable (1 << 15)
+#define ZR36057_OCR_MaskStride 0
+
+#define ZR36057_SPGPPCR 0x028 /* System, PCI, and General Purpose Pins Control Register */
+#define ZR36057_SPGPPCR_SoftReset (1<<24)
+
+#define ZR36057_GPPGCR1 0x02c /* General Purpose Pins and GuestBus Control Register (1) */
+
+#define ZR36057_MCSAR 0x030 /* MPEG Code Source Address Register */
+
+#define ZR36057_MCTCR 0x034 /* MPEG Code Transfer Control Register */
+#define ZR36057_MCTCR_CodTime (1 << 30)
+#define ZR36057_MCTCR_CEmpty (1 << 29)
+#define ZR36057_MCTCR_CFlush (1 << 28)
+#define ZR36057_MCTCR_CodGuestID 20
+#define ZR36057_MCTCR_CodGuestReg 16
+
+#define ZR36057_MCMPR 0x038 /* MPEG Code Memory Pointer Register */
+
+#define ZR36057_ISR 0x03c /* Interrupt Status Register */
+#define ZR36057_ISR_GIRQ1 (1<<30)
+#define ZR36057_ISR_GIRQ0 (1<<29)
+#define ZR36057_ISR_CodRepIRQ (1<<28)
+#define ZR36057_ISR_JPEGRepIRQ (1<<27)
+
+#define ZR36057_ICR 0x040 /* Interrupt Control Register */
+#define ZR36057_ICR_GIRQ1 (1<<30)
+#define ZR36057_ICR_GIRQ0 (1<<29)
+#define ZR36057_ICR_CodRepIRQ (1<<28)
+#define ZR36057_ICR_JPEGRepIRQ (1<<27)
+#define ZR36057_ICR_IntPinEn (1<<24)
+
+#define ZR36057_I2CBR 0x044 /* I2C Bus Register */
+#define ZR36057_I2CBR_SDA (1<<1)
+#define ZR36057_I2CBR_SCL (1<<0)
+
+#define ZR36057_JMC 0x100 /* JPEG Mode and Control */
+#define ZR36057_JMC_JPG (1 << 31)
+#define ZR36057_JMC_JPGExpMode (0 << 29)
+#define ZR36057_JMC_JPGCmpMode (1 << 29)
+#define ZR36057_JMC_MJPGExpMode (2 << 29)
+#define ZR36057_JMC_MJPGCmpMode (3 << 29)
+#define ZR36057_JMC_RTBUSY_FB (1 << 6)
+#define ZR36057_JMC_Go_en (1 << 5)
+#define ZR36057_JMC_SyncMstr (1 << 4)
+#define ZR36057_JMC_Fld_per_buff (1 << 3)
+#define ZR36057_JMC_VFIFO_FB (1 << 2)
+#define ZR36057_JMC_CFIFO_FB (1 << 1)
+#define ZR36057_JMC_Stll_LitEndian (1 << 0)
+
+#define ZR36057_JPC 0x104 /* JPEG Process Control */
+#define ZR36057_JPC_P_Reset (1 << 7)
+#define ZR36057_JPC_CodTrnsEn (1 << 5)
+#define ZR36057_JPC_Active (1 << 0)
+
+#define ZR36057_VSP 0x108 /* Vertical Sync Parameters */
+#define ZR36057_VSP_VsyncSize 16
+#define ZR36057_VSP_FrmTot 0
+
+#define ZR36057_HSP 0x10c /* Horizontal Sync Parameters */
+#define ZR36057_HSP_HsyncStart 16
+#define ZR36057_HSP_LineTot 0
+
+#define ZR36057_FHAP 0x110 /* Field Horizontal Active Portion */
+#define ZR36057_FHAP_NAX 16
+#define ZR36057_FHAP_PAX 0
+
+#define ZR36057_FVAP 0x114 /* Field Vertical Active Portion */
+#define ZR36057_FVAP_NAY 16
+#define ZR36057_FVAP_PAY 0
+
+#define ZR36057_FPP 0x118 /* Field Process Parameters */
+#define ZR36057_FPP_Odd_Even (1 << 0)
+
+#define ZR36057_JCBA 0x11c /* JPEG Code Base Address */
+
+#define ZR36057_JCFT 0x120 /* JPEG Code FIFO Threshold */
+
+#define ZR36057_JCGI 0x124 /* JPEG Codec Guest ID */
+#define ZR36057_JCGI_JPEGuestID 4
+#define ZR36057_JCGI_JPEGuestReg 0
+
+#define ZR36057_GCR2 0x12c /* GuestBus Control Register (2) */
+
+#define ZR36057_POR 0x200 /* Post Office Register */
+#define ZR36057_POR_POPen (1<<25)
+#define ZR36057_POR_POTime (1<<24)
+#define ZR36057_POR_PODir (1<<23)
+
+#define ZR36057_STR 0x300 /* "Still" Transfer Register */
+
+#endif
diff --git a/drivers/media/video/zoran/zr36060.c b/drivers/media/video/zoran/zr36060.c
new file mode 100644
index 00000000..f08546fe
--- /dev/null
+++ b/drivers/media/video/zoran/zr36060.c
@@ -0,0 +1,1010 @@
+/*
+ * Zoran ZR36060 basic configuration functions
+ *
+ * Copyright (C) 2002 Laurent Pinchart <laurent.pinchart@skynet.be>
+ *
+ * $Id: zr36060.c,v 1.1.2.22 2003/05/06 09:35:36 rbultje Exp $
+ *
+ * ------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+#define ZR060_VERSION "v0.7"
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#include <linux/types.h>
+#include <linux/wait.h>
+
+/* I/O commands, error codes */
+#include <asm/io.h>
+
+/* headerfile of this module */
+#include "zr36060.h"
+
+/* codec io API */
+#include "videocodec.h"
+
+/* it doesn't make sense to have more than 20 or so,
+ just to prevent some unwanted loops */
+#define MAX_CODECS 20
+
+/* amount of chips attached via this driver */
+static int zr36060_codecs;
+
+static bool low_bitrate;
+module_param(low_bitrate, bool, 0);
+MODULE_PARM_DESC(low_bitrate, "Buz compatibility option, halves bitrate");
+
+/* debugging is available via module parameter */
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-4)");
+
+#define dprintk(num, format, args...) \
+ do { \
+ if (debug >= num) \
+ printk(format, ##args); \
+ } while (0)
+
+/* =========================================================================
+ Local hardware I/O functions:
+
+ read/write via codec layer (registers are located in the master device)
+ ========================================================================= */
+
+/* read and write functions */
+static u8
+zr36060_read (struct zr36060 *ptr,
+ u16 reg)
+{
+ u8 value = 0;
+
+ // just in case something is wrong...
+ if (ptr->codec->master_data->readreg)
+ value = (ptr->codec->master_data->readreg(ptr->codec,
+ reg)) & 0xff;
+ else
+ dprintk(1,
+ KERN_ERR "%s: invalid I/O setup, nothing read!\n",
+ ptr->name);
+
+ //dprintk(4, "%s: reading from 0x%04x: %02x\n",ptr->name,reg,value);
+
+ return value;
+}
+
+static void
+zr36060_write(struct zr36060 *ptr,
+ u16 reg,
+ u8 value)
+{
+ //dprintk(4, "%s: writing 0x%02x to 0x%04x\n",ptr->name,value,reg);
+ dprintk(4, "0x%02x @0x%04x\n", value, reg);
+
+ // just in case something is wrong...
+ if (ptr->codec->master_data->writereg)
+ ptr->codec->master_data->writereg(ptr->codec, reg, value);
+ else
+ dprintk(1,
+ KERN_ERR
+ "%s: invalid I/O setup, nothing written!\n",
+ ptr->name);
+}
+
+/* =========================================================================
+ Local helper function:
+
+ status read
+ ========================================================================= */
+
+/* status is kept in datastructure */
+static u8
+zr36060_read_status (struct zr36060 *ptr)
+{
+ ptr->status = zr36060_read(ptr, ZR060_CFSR);
+
+ zr36060_read(ptr, 0);
+ return ptr->status;
+}
+
+/* =========================================================================
+ Local helper function:
+
+ scale factor read
+ ========================================================================= */
+
+/* scale factor is kept in datastructure */
+static u16
+zr36060_read_scalefactor (struct zr36060 *ptr)
+{
+ ptr->scalefact = (zr36060_read(ptr, ZR060_SF_HI) << 8) |
+ (zr36060_read(ptr, ZR060_SF_LO) & 0xFF);
+
+ /* leave 0 selected for an eventually GO from master */
+ zr36060_read(ptr, 0);
+ return ptr->scalefact;
+}
+
+/* =========================================================================
+ Local helper function:
+
+ wait if codec is ready to proceed (end of processing) or time is over
+ ========================================================================= */
+
+static void
+zr36060_wait_end (struct zr36060 *ptr)
+{
+ int i = 0;
+
+ while (zr36060_read_status(ptr) & ZR060_CFSR_Busy) {
+ udelay(1);
+ if (i++ > 200000) { // 200ms, there is for sure something wrong!!!
+ dprintk(1,
+ "%s: timeout at wait_end (last status: 0x%02x)\n",
+ ptr->name, ptr->status);
+ break;
+ }
+ }
+}
+
+/* =========================================================================
+ Local helper function:
+
+ basic test of "connectivity", writes/reads to/from memory the SOF marker
+ ========================================================================= */
+
+static int
+zr36060_basic_test (struct zr36060 *ptr)
+{
+ if ((zr36060_read(ptr, ZR060_IDR_DEV) != 0x33) &&
+ (zr36060_read(ptr, ZR060_IDR_REV) != 0x01)) {
+ dprintk(1,
+ KERN_ERR
+ "%s: attach failed, can't connect to jpeg processor!\n",
+ ptr->name);
+ return -ENXIO;
+ }
+
+ zr36060_wait_end(ptr);
+ if (ptr->status & ZR060_CFSR_Busy) {
+ dprintk(1,
+ KERN_ERR
+ "%s: attach failed, jpeg processor failed (end flag)!\n",
+ ptr->name);
+ return -EBUSY;
+ }
+
+ return 0; /* looks good! */
+}
+
+/* =========================================================================
+ Local helper function:
+
+ simple loop for pushing the init datasets
+ ========================================================================= */
+
+static int
+zr36060_pushit (struct zr36060 *ptr,
+ u16 startreg,
+ u16 len,
+ const char *data)
+{
+ int i = 0;
+
+ dprintk(4, "%s: write data block to 0x%04x (len=%d)\n", ptr->name,
+ startreg, len);
+ while (i < len) {
+ zr36060_write(ptr, startreg++, data[i++]);
+ }
+
+ return i;
+}
+
+/* =========================================================================
+ Basic datasets:
+
+ jpeg baseline setup data (you find it on lots places in internet, or just
+ extract it from any regular .jpg image...)
+
+ Could be variable, but until it's not needed it they are just fixed to save
+ memory. Otherwise expand zr36060 structure with arrays, push the values to
+ it and initialize from there, as e.g. the linux zr36057/60 driver does it.
+ ========================================================================= */
+
+static const char zr36060_dqt[0x86] = {
+ 0xff, 0xdb, //Marker: DQT
+ 0x00, 0x84, //Length: 2*65+2
+ 0x00, //Pq,Tq first table
+ 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e,
+ 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28,
+ 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25,
+ 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33,
+ 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44,
+ 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57,
+ 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71,
+ 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63,
+ 0x01, //Pq,Tq second table
+ 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a,
+ 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63
+};
+
+static const char zr36060_dht[0x1a4] = {
+ 0xff, 0xc4, //Marker: DHT
+ 0x01, 0xa2, //Length: 2*AC, 2*DC
+ 0x00, //DC first table
+ 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
+ 0x01, //DC second table
+ 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
+ 0x10, //AC first table
+ 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03,
+ 0x05, 0x05, 0x04, 0x04, 0x00, 0x00,
+ 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11,
+ 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61,
+ 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1,
+ 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24,
+ 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17,
+ 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34,
+ 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44,
+ 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56,
+ 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66,
+ 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+ 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+ 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
+ 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8,
+ 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9,
+ 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8,
+ 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9,
+ 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
+ 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
+ 0xF8, 0xF9, 0xFA,
+ 0x11, //AC second table
+ 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04,
+ 0x07, 0x05, 0x04, 0x04, 0x00, 0x01,
+ 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04,
+ 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
+ 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
+ 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62,
+ 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25,
+ 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A,
+ 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44,
+ 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56,
+ 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66,
+ 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+ 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
+ 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
+ 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8,
+ 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
+ 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8,
+ 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
+ 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
+ 0xF9, 0xFA
+};
+
+/* jpeg baseline setup, this is just fixed in this driver (YUV pictures) */
+#define NO_OF_COMPONENTS 0x3 //Y,U,V
+#define BASELINE_PRECISION 0x8 //MCU size (?)
+static const char zr36060_tq[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's QT
+static const char zr36060_td[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's DC
+static const char zr36060_ta[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's AC
+
+/* horizontal 422 decimation setup (maybe we support 411 or so later, too) */
+static const char zr36060_decimation_h[8] = { 2, 1, 1, 0, 0, 0, 0, 0 };
+static const char zr36060_decimation_v[8] = { 1, 1, 1, 0, 0, 0, 0, 0 };
+
+/* =========================================================================
+ Local helper functions:
+
+ calculation and setup of parameter-dependent JPEG baseline segments
+ (needed for compression only)
+ ========================================================================= */
+
+/* ------------------------------------------------------------------------- */
+
+/* SOF (start of frame) segment depends on width, height and sampling ratio
+ of each color component */
+
+static int
+zr36060_set_sof (struct zr36060 *ptr)
+{
+ char sof_data[34]; // max. size of register set
+ int i;
+
+ dprintk(3, "%s: write SOF (%dx%d, %d components)\n", ptr->name,
+ ptr->width, ptr->height, NO_OF_COMPONENTS);
+ sof_data[0] = 0xff;
+ sof_data[1] = 0xc0;
+ sof_data[2] = 0x00;
+ sof_data[3] = (3 * NO_OF_COMPONENTS) + 8;
+ sof_data[4] = BASELINE_PRECISION; // only '8' possible with zr36060
+ sof_data[5] = (ptr->height) >> 8;
+ sof_data[6] = (ptr->height) & 0xff;
+ sof_data[7] = (ptr->width) >> 8;
+ sof_data[8] = (ptr->width) & 0xff;
+ sof_data[9] = NO_OF_COMPONENTS;
+ for (i = 0; i < NO_OF_COMPONENTS; i++) {
+ sof_data[10 + (i * 3)] = i; // index identifier
+ sof_data[11 + (i * 3)] = (ptr->h_samp_ratio[i] << 4) |
+ (ptr->v_samp_ratio[i]); // sampling ratios
+ sof_data[12 + (i * 3)] = zr36060_tq[i]; // Q table selection
+ }
+ return zr36060_pushit(ptr, ZR060_SOF_IDX,
+ (3 * NO_OF_COMPONENTS) + 10, sof_data);
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* SOS (start of scan) segment depends on the used scan components
+ of each color component */
+
+static int
+zr36060_set_sos (struct zr36060 *ptr)
+{
+ char sos_data[16]; // max. size of register set
+ int i;
+
+ dprintk(3, "%s: write SOS\n", ptr->name);
+ sos_data[0] = 0xff;
+ sos_data[1] = 0xda;
+ sos_data[2] = 0x00;
+ sos_data[3] = 2 + 1 + (2 * NO_OF_COMPONENTS) + 3;
+ sos_data[4] = NO_OF_COMPONENTS;
+ for (i = 0; i < NO_OF_COMPONENTS; i++) {
+ sos_data[5 + (i * 2)] = i; // index
+ sos_data[6 + (i * 2)] = (zr36060_td[i] << 4) |
+ zr36060_ta[i]; // AC/DC tbl.sel.
+ }
+ sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 2] = 00; // scan start
+ sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 3] = 0x3f;
+ sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 4] = 00;
+ return zr36060_pushit(ptr, ZR060_SOS_IDX,
+ 4 + 1 + (2 * NO_OF_COMPONENTS) + 3,
+ sos_data);
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* DRI (define restart interval) */
+
+static int
+zr36060_set_dri (struct zr36060 *ptr)
+{
+ char dri_data[6]; // max. size of register set
+
+ dprintk(3, "%s: write DRI\n", ptr->name);
+ dri_data[0] = 0xff;
+ dri_data[1] = 0xdd;
+ dri_data[2] = 0x00;
+ dri_data[3] = 0x04;
+ dri_data[4] = (ptr->dri) >> 8;
+ dri_data[5] = (ptr->dri) & 0xff;
+ return zr36060_pushit(ptr, ZR060_DRI_IDX, 6, dri_data);
+}
+
+/* =========================================================================
+ Setup function:
+
+ Setup compression/decompression of Zoran's JPEG processor
+ ( see also zoran 36060 manual )
+
+ ... sorry for the spaghetti code ...
+ ========================================================================= */
+static void
+zr36060_init (struct zr36060 *ptr)
+{
+ int sum = 0;
+ long bitcnt, tmp;
+
+ if (ptr->mode == CODEC_DO_COMPRESSION) {
+ dprintk(2, "%s: COMPRESSION SETUP\n", ptr->name);
+
+ zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SyncRst);
+
+ /* 060 communicates with 067 in master mode */
+ zr36060_write(ptr, ZR060_CIR, ZR060_CIR_CodeMstr);
+
+ /* Compression with or without variable scale factor */
+ /*FIXME: What about ptr->bitrate_ctrl? */
+ zr36060_write(ptr, ZR060_CMR,
+ ZR060_CMR_Comp | ZR060_CMR_Pass2 |
+ ZR060_CMR_BRB);
+
+ /* Must be zero */
+ zr36060_write(ptr, ZR060_MBZ, 0x00);
+ zr36060_write(ptr, ZR060_TCR_HI, 0x00);
+ zr36060_write(ptr, ZR060_TCR_LO, 0x00);
+
+ /* Disable all IRQs - no DataErr means autoreset */
+ zr36060_write(ptr, ZR060_IMR, 0);
+
+ /* volume control settings */
+ zr36060_write(ptr, ZR060_SF_HI, ptr->scalefact >> 8);
+ zr36060_write(ptr, ZR060_SF_LO, ptr->scalefact & 0xff);
+
+ zr36060_write(ptr, ZR060_AF_HI, 0xff);
+ zr36060_write(ptr, ZR060_AF_M, 0xff);
+ zr36060_write(ptr, ZR060_AF_LO, 0xff);
+
+ /* setup the variable jpeg tables */
+ sum += zr36060_set_sof(ptr);
+ sum += zr36060_set_sos(ptr);
+ sum += zr36060_set_dri(ptr);
+
+ /* setup the fixed jpeg tables - maybe variable, though -
+ * (see table init section above) */
+ sum +=
+ zr36060_pushit(ptr, ZR060_DQT_IDX, sizeof(zr36060_dqt),
+ zr36060_dqt);
+ sum +=
+ zr36060_pushit(ptr, ZR060_DHT_IDX, sizeof(zr36060_dht),
+ zr36060_dht);
+ zr36060_write(ptr, ZR060_APP_IDX, 0xff);
+ zr36060_write(ptr, ZR060_APP_IDX + 1, 0xe0 + ptr->app.appn);
+ zr36060_write(ptr, ZR060_APP_IDX + 2, 0x00);
+ zr36060_write(ptr, ZR060_APP_IDX + 3, ptr->app.len + 2);
+ sum += zr36060_pushit(ptr, ZR060_APP_IDX + 4, 60,
+ ptr->app.data) + 4;
+ zr36060_write(ptr, ZR060_COM_IDX, 0xff);
+ zr36060_write(ptr, ZR060_COM_IDX + 1, 0xfe);
+ zr36060_write(ptr, ZR060_COM_IDX + 2, 0x00);
+ zr36060_write(ptr, ZR060_COM_IDX + 3, ptr->com.len + 2);
+ sum += zr36060_pushit(ptr, ZR060_COM_IDX + 4, 60,
+ ptr->com.data) + 4;
+
+ /* setup misc. data for compression (target code sizes) */
+
+ /* size of compressed code to reach without header data */
+ sum = ptr->real_code_vol - sum;
+ bitcnt = sum << 3; /* need the size in bits */
+
+ tmp = bitcnt >> 16;
+ dprintk(3,
+ "%s: code: csize=%d, tot=%d, bit=%ld, highbits=%ld\n",
+ ptr->name, sum, ptr->real_code_vol, bitcnt, tmp);
+ zr36060_write(ptr, ZR060_TCV_NET_HI, tmp >> 8);
+ zr36060_write(ptr, ZR060_TCV_NET_MH, tmp & 0xff);
+ tmp = bitcnt & 0xffff;
+ zr36060_write(ptr, ZR060_TCV_NET_ML, tmp >> 8);
+ zr36060_write(ptr, ZR060_TCV_NET_LO, tmp & 0xff);
+
+ bitcnt -= bitcnt >> 7; // bits without stuffing
+ bitcnt -= ((bitcnt * 5) >> 6); // bits without eob
+
+ tmp = bitcnt >> 16;
+ dprintk(3, "%s: code: nettobit=%ld, highnettobits=%ld\n",
+ ptr->name, bitcnt, tmp);
+ zr36060_write(ptr, ZR060_TCV_DATA_HI, tmp >> 8);
+ zr36060_write(ptr, ZR060_TCV_DATA_MH, tmp & 0xff);
+ tmp = bitcnt & 0xffff;
+ zr36060_write(ptr, ZR060_TCV_DATA_ML, tmp >> 8);
+ zr36060_write(ptr, ZR060_TCV_DATA_LO, tmp & 0xff);
+
+ /* JPEG markers to be included in the compressed stream */
+ zr36060_write(ptr, ZR060_MER,
+ ZR060_MER_DQT | ZR060_MER_DHT |
+ ((ptr->com.len > 0) ? ZR060_MER_Com : 0) |
+ ((ptr->app.len > 0) ? ZR060_MER_App : 0));
+
+ /* Setup the Video Frontend */
+ /* Limit pixel range to 16..235 as per CCIR-601 */
+ zr36060_write(ptr, ZR060_VCR, ZR060_VCR_Range);
+
+ } else {
+ dprintk(2, "%s: EXPANSION SETUP\n", ptr->name);
+
+ zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SyncRst);
+
+ /* 060 communicates with 067 in master mode */
+ zr36060_write(ptr, ZR060_CIR, ZR060_CIR_CodeMstr);
+
+ /* Decompression */
+ zr36060_write(ptr, ZR060_CMR, 0);
+
+ /* Must be zero */
+ zr36060_write(ptr, ZR060_MBZ, 0x00);
+ zr36060_write(ptr, ZR060_TCR_HI, 0x00);
+ zr36060_write(ptr, ZR060_TCR_LO, 0x00);
+
+ /* Disable all IRQs - no DataErr means autoreset */
+ zr36060_write(ptr, ZR060_IMR, 0);
+
+ /* setup misc. data for expansion */
+ zr36060_write(ptr, ZR060_MER, 0);
+
+ /* setup the fixed jpeg tables - maybe variable, though -
+ * (see table init section above) */
+ zr36060_pushit(ptr, ZR060_DHT_IDX, sizeof(zr36060_dht),
+ zr36060_dht);
+
+ /* Setup the Video Frontend */
+ //zr36060_write(ptr, ZR060_VCR, ZR060_VCR_FIExt);
+ //this doesn't seem right and doesn't work...
+ zr36060_write(ptr, ZR060_VCR, ZR060_VCR_Range);
+ }
+
+ /* Load the tables */
+ zr36060_write(ptr, ZR060_LOAD,
+ ZR060_LOAD_SyncRst | ZR060_LOAD_Load);
+ zr36060_wait_end(ptr);
+ dprintk(2, "%s: Status after table preload: 0x%02x\n", ptr->name,
+ ptr->status);
+
+ if (ptr->status & ZR060_CFSR_Busy) {
+ dprintk(1, KERN_ERR "%s: init aborted!\n", ptr->name);
+ return; // something is wrong, its timed out!!!!
+ }
+}
+
+/* =========================================================================
+ CODEC API FUNCTIONS
+
+ this functions are accessed by the master via the API structure
+ ========================================================================= */
+
+/* set compression/expansion mode and launches codec -
+ this should be the last call from the master before starting processing */
+static int
+zr36060_set_mode (struct videocodec *codec,
+ int mode)
+{
+ struct zr36060 *ptr = (struct zr36060 *) codec->data;
+
+ dprintk(2, "%s: set_mode %d call\n", ptr->name, mode);
+
+ if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION))
+ return -EINVAL;
+
+ ptr->mode = mode;
+ zr36060_init(ptr);
+
+ return 0;
+}
+
+/* set picture size (norm is ignored as the codec doesn't know about it) */
+static int
+zr36060_set_video (struct videocodec *codec,
+ struct tvnorm *norm,
+ struct vfe_settings *cap,
+ struct vfe_polarity *pol)
+{
+ struct zr36060 *ptr = (struct zr36060 *) codec->data;
+ u32 reg;
+ int size;
+
+ dprintk(2, "%s: set_video %d/%d-%dx%d (%%%d) call\n", ptr->name,
+ cap->x, cap->y, cap->width, cap->height, cap->decimation);
+
+ /* if () return -EINVAL;
+ * trust the master driver that it knows what it does - so
+ * we allow invalid startx/y and norm for now ... */
+ ptr->width = cap->width / (cap->decimation & 0xff);
+ ptr->height = cap->height / (cap->decimation >> 8);
+
+ zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SyncRst);
+
+ /* Note that VSPol/HSPol bits in zr36060 have the opposite
+ * meaning of their zr360x7 counterparts with the same names
+ * N.b. for VSPol this is only true if FIVEdge = 0 (default,
+ * left unchanged here - in accordance with datasheet).
+ */
+ reg = (!pol->vsync_pol ? ZR060_VPR_VSPol : 0)
+ | (!pol->hsync_pol ? ZR060_VPR_HSPol : 0)
+ | (pol->field_pol ? ZR060_VPR_FIPol : 0)
+ | (pol->blank_pol ? ZR060_VPR_BLPol : 0)
+ | (pol->subimg_pol ? ZR060_VPR_SImgPol : 0)
+ | (pol->poe_pol ? ZR060_VPR_PoePol : 0)
+ | (pol->pvalid_pol ? ZR060_VPR_PValPol : 0)
+ | (pol->vclk_pol ? ZR060_VPR_VCLKPol : 0);
+ zr36060_write(ptr, ZR060_VPR, reg);
+
+ reg = 0;
+ switch (cap->decimation & 0xff) {
+ default:
+ case 1:
+ break;
+
+ case 2:
+ reg |= ZR060_SR_HScale2;
+ break;
+
+ case 4:
+ reg |= ZR060_SR_HScale4;
+ break;
+ }
+
+ switch (cap->decimation >> 8) {
+ default:
+ case 1:
+ break;
+
+ case 2:
+ reg |= ZR060_SR_VScale;
+ break;
+ }
+ zr36060_write(ptr, ZR060_SR, reg);
+
+ zr36060_write(ptr, ZR060_BCR_Y, 0x00);
+ zr36060_write(ptr, ZR060_BCR_U, 0x80);
+ zr36060_write(ptr, ZR060_BCR_V, 0x80);
+
+ /* sync generator */
+
+ reg = norm->Ht - 1; /* Vtotal */
+ zr36060_write(ptr, ZR060_SGR_VTOTAL_HI, (reg >> 8) & 0xff);
+ zr36060_write(ptr, ZR060_SGR_VTOTAL_LO, (reg >> 0) & 0xff);
+
+ reg = norm->Wt - 1; /* Htotal */
+ zr36060_write(ptr, ZR060_SGR_HTOTAL_HI, (reg >> 8) & 0xff);
+ zr36060_write(ptr, ZR060_SGR_HTOTAL_LO, (reg >> 0) & 0xff);
+
+ reg = 6 - 1; /* VsyncSize */
+ zr36060_write(ptr, ZR060_SGR_VSYNC, reg);
+
+ //reg = 30 - 1; /* HsyncSize */
+///*CP*/ reg = (zr->params.norm == 1 ? 57 : 68);
+ reg = 68;
+ zr36060_write(ptr, ZR060_SGR_HSYNC, reg);
+
+ reg = norm->VStart - 1; /* BVstart */
+ zr36060_write(ptr, ZR060_SGR_BVSTART, reg);
+
+ reg += norm->Ha / 2; /* BVend */
+ zr36060_write(ptr, ZR060_SGR_BVEND_HI, (reg >> 8) & 0xff);
+ zr36060_write(ptr, ZR060_SGR_BVEND_LO, (reg >> 0) & 0xff);
+
+ reg = norm->HStart - 1; /* BHstart */
+ zr36060_write(ptr, ZR060_SGR_BHSTART, reg);
+
+ reg += norm->Wa; /* BHend */
+ zr36060_write(ptr, ZR060_SGR_BHEND_HI, (reg >> 8) & 0xff);
+ zr36060_write(ptr, ZR060_SGR_BHEND_LO, (reg >> 0) & 0xff);
+
+ /* active area */
+ reg = cap->y + norm->VStart; /* Vstart */
+ zr36060_write(ptr, ZR060_AAR_VSTART_HI, (reg >> 8) & 0xff);
+ zr36060_write(ptr, ZR060_AAR_VSTART_LO, (reg >> 0) & 0xff);
+
+ reg += cap->height; /* Vend */
+ zr36060_write(ptr, ZR060_AAR_VEND_HI, (reg >> 8) & 0xff);
+ zr36060_write(ptr, ZR060_AAR_VEND_LO, (reg >> 0) & 0xff);
+
+ reg = cap->x + norm->HStart; /* Hstart */
+ zr36060_write(ptr, ZR060_AAR_HSTART_HI, (reg >> 8) & 0xff);
+ zr36060_write(ptr, ZR060_AAR_HSTART_LO, (reg >> 0) & 0xff);
+
+ reg += cap->width; /* Hend */
+ zr36060_write(ptr, ZR060_AAR_HEND_HI, (reg >> 8) & 0xff);
+ zr36060_write(ptr, ZR060_AAR_HEND_LO, (reg >> 0) & 0xff);
+
+ /* subimage area */
+ reg = norm->VStart - 4; /* SVstart */
+ zr36060_write(ptr, ZR060_SWR_VSTART_HI, (reg >> 8) & 0xff);
+ zr36060_write(ptr, ZR060_SWR_VSTART_LO, (reg >> 0) & 0xff);
+
+ reg += norm->Ha / 2 + 8; /* SVend */
+ zr36060_write(ptr, ZR060_SWR_VEND_HI, (reg >> 8) & 0xff);
+ zr36060_write(ptr, ZR060_SWR_VEND_LO, (reg >> 0) & 0xff);
+
+ reg = norm->HStart /*+ 64 */ - 4; /* SHstart */
+ zr36060_write(ptr, ZR060_SWR_HSTART_HI, (reg >> 8) & 0xff);
+ zr36060_write(ptr, ZR060_SWR_HSTART_LO, (reg >> 0) & 0xff);
+
+ reg += norm->Wa + 8; /* SHend */
+ zr36060_write(ptr, ZR060_SWR_HEND_HI, (reg >> 8) & 0xff);
+ zr36060_write(ptr, ZR060_SWR_HEND_LO, (reg >> 0) & 0xff);
+
+ size = ptr->width * ptr->height;
+ /* Target compressed field size in bits: */
+ size = size * 16; /* uncompressed size in bits */
+ /* (Ronald) by default, quality = 100 is a compression
+ * ratio 1:2. Setting low_bitrate (insmod option) sets
+ * it to 1:4 (instead of 1:2, zr36060 max) as limit because the
+ * buz can't handle more at decimation=1... Use low_bitrate if
+ * you have a Buz, unless you know what you're doing */
+ size = size * cap->quality / (low_bitrate ? 400 : 200);
+ /* Lower limit (arbitrary, 1 KB) */
+ if (size < 8192)
+ size = 8192;
+ /* Upper limit: 7/8 of the code buffers */
+ if (size > ptr->total_code_vol * 7)
+ size = ptr->total_code_vol * 7;
+
+ ptr->real_code_vol = size >> 3; /* in bytes */
+
+ /* the MBCVR is the *maximum* block volume, according to the
+ * JPEG ISO specs, this shouldn't be used, since that allows
+ * for the best encoding quality. So set it to it's max value */
+ reg = ptr->max_block_vol;
+ zr36060_write(ptr, ZR060_MBCVR, reg);
+
+ return 0;
+}
+
+/* additional control functions */
+static int
+zr36060_control (struct videocodec *codec,
+ int type,
+ int size,
+ void *data)
+{
+ struct zr36060 *ptr = (struct zr36060 *) codec->data;
+ int *ival = (int *) data;
+
+ dprintk(2, "%s: control %d call with %d byte\n", ptr->name, type,
+ size);
+
+ switch (type) {
+ case CODEC_G_STATUS: /* get last status */
+ if (size != sizeof(int))
+ return -EFAULT;
+ zr36060_read_status(ptr);
+ *ival = ptr->status;
+ break;
+
+ case CODEC_G_CODEC_MODE:
+ if (size != sizeof(int))
+ return -EFAULT;
+ *ival = CODEC_MODE_BJPG;
+ break;
+
+ case CODEC_S_CODEC_MODE:
+ if (size != sizeof(int))
+ return -EFAULT;
+ if (*ival != CODEC_MODE_BJPG)
+ return -EINVAL;
+ /* not needed, do nothing */
+ return 0;
+
+ case CODEC_G_VFE:
+ case CODEC_S_VFE:
+ /* not needed, do nothing */
+ return 0;
+
+ case CODEC_S_MMAP:
+ /* not available, give an error */
+ return -ENXIO;
+
+ case CODEC_G_JPEG_TDS_BYTE: /* get target volume in byte */
+ if (size != sizeof(int))
+ return -EFAULT;
+ *ival = ptr->total_code_vol;
+ break;
+
+ case CODEC_S_JPEG_TDS_BYTE: /* get target volume in byte */
+ if (size != sizeof(int))
+ return -EFAULT;
+ ptr->total_code_vol = *ival;
+ ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3;
+ break;
+
+ case CODEC_G_JPEG_SCALE: /* get scaling factor */
+ if (size != sizeof(int))
+ return -EFAULT;
+ *ival = zr36060_read_scalefactor(ptr);
+ break;
+
+ case CODEC_S_JPEG_SCALE: /* set scaling factor */
+ if (size != sizeof(int))
+ return -EFAULT;
+ ptr->scalefact = *ival;
+ break;
+
+ case CODEC_G_JPEG_APP_DATA: { /* get appn marker data */
+ struct jpeg_app_marker *app = data;
+
+ if (size != sizeof(struct jpeg_app_marker))
+ return -EFAULT;
+
+ *app = ptr->app;
+ break;
+ }
+
+ case CODEC_S_JPEG_APP_DATA: { /* set appn marker data */
+ struct jpeg_app_marker *app = data;
+
+ if (size != sizeof(struct jpeg_app_marker))
+ return -EFAULT;
+
+ ptr->app = *app;
+ break;
+ }
+
+ case CODEC_G_JPEG_COM_DATA: { /* get comment marker data */
+ struct jpeg_com_marker *com = data;
+
+ if (size != sizeof(struct jpeg_com_marker))
+ return -EFAULT;
+
+ *com = ptr->com;
+ break;
+ }
+
+ case CODEC_S_JPEG_COM_DATA: { /* set comment marker data */
+ struct jpeg_com_marker *com = data;
+
+ if (size != sizeof(struct jpeg_com_marker))
+ return -EFAULT;
+
+ ptr->com = *com;
+ break;
+ }
+
+ default:
+ return -EINVAL;
+ }
+
+ return size;
+}
+
+/* =========================================================================
+ Exit and unregister function:
+
+ Deinitializes Zoran's JPEG processor
+ ========================================================================= */
+
+static int
+zr36060_unset (struct videocodec *codec)
+{
+ struct zr36060 *ptr = codec->data;
+
+ if (ptr) {
+ /* do wee need some codec deinit here, too ???? */
+
+ dprintk(1, "%s: finished codec #%d\n", ptr->name,
+ ptr->num);
+ kfree(ptr);
+ codec->data = NULL;
+
+ zr36060_codecs--;
+ return 0;
+ }
+
+ return -EFAULT;
+}
+
+/* =========================================================================
+ Setup and registry function:
+
+ Initializes Zoran's JPEG processor
+
+ Also sets pixel size, average code size, mode (compr./decompr.)
+ (the given size is determined by the processor with the video interface)
+ ========================================================================= */
+
+static int
+zr36060_setup (struct videocodec *codec)
+{
+ struct zr36060 *ptr;
+ int res;
+
+ dprintk(2, "zr36060: initializing MJPEG subsystem #%d.\n",
+ zr36060_codecs);
+
+ if (zr36060_codecs == MAX_CODECS) {
+ dprintk(1,
+ KERN_ERR "zr36060: Can't attach more codecs!\n");
+ return -ENOSPC;
+ }
+ //mem structure init
+ codec->data = ptr = kzalloc(sizeof(struct zr36060), GFP_KERNEL);
+ if (NULL == ptr) {
+ dprintk(1, KERN_ERR "zr36060: Can't get enough memory!\n");
+ return -ENOMEM;
+ }
+
+ snprintf(ptr->name, sizeof(ptr->name), "zr36060[%d]",
+ zr36060_codecs);
+ ptr->num = zr36060_codecs++;
+ ptr->codec = codec;
+
+ //testing
+ res = zr36060_basic_test(ptr);
+ if (res < 0) {
+ zr36060_unset(codec);
+ return res;
+ }
+ //final setup
+ memcpy(ptr->h_samp_ratio, zr36060_decimation_h, 8);
+ memcpy(ptr->v_samp_ratio, zr36060_decimation_v, 8);
+
+ ptr->bitrate_ctrl = 0; /* 0 or 1 - fixed file size flag
+ * (what is the difference?) */
+ ptr->mode = CODEC_DO_COMPRESSION;
+ ptr->width = 384;
+ ptr->height = 288;
+ ptr->total_code_vol = 16000; /* CHECKME */
+ ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3;
+ ptr->max_block_vol = 240; /* CHECKME, was 120 is 240 */
+ ptr->scalefact = 0x100;
+ ptr->dri = 1; /* CHECKME, was 8 is 1 */
+
+ /* by default, no COM or APP markers - app should set those */
+ ptr->com.len = 0;
+ ptr->app.appn = 0;
+ ptr->app.len = 0;
+
+ zr36060_init(ptr);
+
+ dprintk(1, KERN_INFO "%s: codec attached and running\n",
+ ptr->name);
+
+ return 0;
+}
+
+static const struct videocodec zr36060_codec = {
+ .owner = THIS_MODULE,
+ .name = "zr36060",
+ .magic = 0L, // magic not used
+ .flags =
+ CODEC_FLAG_JPEG | CODEC_FLAG_HARDWARE | CODEC_FLAG_ENCODER |
+ CODEC_FLAG_DECODER | CODEC_FLAG_VFE,
+ .type = CODEC_TYPE_ZR36060,
+ .setup = zr36060_setup, // functionality
+ .unset = zr36060_unset,
+ .set_mode = zr36060_set_mode,
+ .set_video = zr36060_set_video,
+ .control = zr36060_control,
+ // others are not used
+};
+
+/* =========================================================================
+ HOOK IN DRIVER AS KERNEL MODULE
+ ========================================================================= */
+
+static int __init
+zr36060_init_module (void)
+{
+ //dprintk(1, "zr36060 driver %s\n",ZR060_VERSION);
+ zr36060_codecs = 0;
+ return videocodec_register(&zr36060_codec);
+}
+
+static void __exit
+zr36060_cleanup_module (void)
+{
+ if (zr36060_codecs) {
+ dprintk(1,
+ "zr36060: something's wrong - %d codecs left somehow.\n",
+ zr36060_codecs);
+ }
+
+ /* however, we can't just stay alive */
+ videocodec_unregister(&zr36060_codec);
+}
+
+module_init(zr36060_init_module);
+module_exit(zr36060_cleanup_module);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@skynet.be>");
+MODULE_DESCRIPTION("Driver module for ZR36060 jpeg processors "
+ ZR060_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/zoran/zr36060.h b/drivers/media/video/zoran/zr36060.h
new file mode 100644
index 00000000..914ffa4a
--- /dev/null
+++ b/drivers/media/video/zoran/zr36060.h
@@ -0,0 +1,220 @@
+/*
+ * Zoran ZR36060 basic configuration functions - header file
+ *
+ * Copyright (C) 2002 Laurent Pinchart <laurent.pinchart@skynet.be>
+ *
+ * $Id: zr36060.h,v 1.1.1.1.2.3 2003/01/14 21:18:47 rbultje Exp $
+ *
+ * ------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+#ifndef ZR36060_H
+#define ZR36060_H
+
+#include "videocodec.h"
+
+/* data stored for each zoran jpeg codec chip */
+struct zr36060 {
+ char name[32];
+ int num;
+ /* io datastructure */
+ struct videocodec *codec;
+ // last coder status
+ __u8 status;
+ // actual coder setup
+ int mode;
+
+ __u16 width;
+ __u16 height;
+
+ __u16 bitrate_ctrl;
+
+ __u32 total_code_vol;
+ __u32 real_code_vol;
+ __u16 max_block_vol;
+
+ __u8 h_samp_ratio[8];
+ __u8 v_samp_ratio[8];
+ __u16 scalefact;
+ __u16 dri;
+
+ /* app/com marker data */
+ struct jpeg_app_marker app;
+ struct jpeg_com_marker com;
+};
+
+/* ZR36060 register addresses */
+#define ZR060_LOAD 0x000
+#define ZR060_CFSR 0x001
+#define ZR060_CIR 0x002
+#define ZR060_CMR 0x003
+#define ZR060_MBZ 0x004
+#define ZR060_MBCVR 0x005
+#define ZR060_MER 0x006
+#define ZR060_IMR 0x007
+#define ZR060_ISR 0x008
+#define ZR060_TCV_NET_HI 0x009
+#define ZR060_TCV_NET_MH 0x00a
+#define ZR060_TCV_NET_ML 0x00b
+#define ZR060_TCV_NET_LO 0x00c
+#define ZR060_TCV_DATA_HI 0x00d
+#define ZR060_TCV_DATA_MH 0x00e
+#define ZR060_TCV_DATA_ML 0x00f
+#define ZR060_TCV_DATA_LO 0x010
+#define ZR060_SF_HI 0x011
+#define ZR060_SF_LO 0x012
+#define ZR060_AF_HI 0x013
+#define ZR060_AF_M 0x014
+#define ZR060_AF_LO 0x015
+#define ZR060_ACV_HI 0x016
+#define ZR060_ACV_MH 0x017
+#define ZR060_ACV_ML 0x018
+#define ZR060_ACV_LO 0x019
+#define ZR060_ACT_HI 0x01a
+#define ZR060_ACT_MH 0x01b
+#define ZR060_ACT_ML 0x01c
+#define ZR060_ACT_LO 0x01d
+#define ZR060_ACV_TRUN_HI 0x01e
+#define ZR060_ACV_TRUN_MH 0x01f
+#define ZR060_ACV_TRUN_ML 0x020
+#define ZR060_ACV_TRUN_LO 0x021
+#define ZR060_IDR_DEV 0x022
+#define ZR060_IDR_REV 0x023
+#define ZR060_TCR_HI 0x024
+#define ZR060_TCR_LO 0x025
+#define ZR060_VCR 0x030
+#define ZR060_VPR 0x031
+#define ZR060_SR 0x032
+#define ZR060_BCR_Y 0x033
+#define ZR060_BCR_U 0x034
+#define ZR060_BCR_V 0x035
+#define ZR060_SGR_VTOTAL_HI 0x036
+#define ZR060_SGR_VTOTAL_LO 0x037
+#define ZR060_SGR_HTOTAL_HI 0x038
+#define ZR060_SGR_HTOTAL_LO 0x039
+#define ZR060_SGR_VSYNC 0x03a
+#define ZR060_SGR_HSYNC 0x03b
+#define ZR060_SGR_BVSTART 0x03c
+#define ZR060_SGR_BHSTART 0x03d
+#define ZR060_SGR_BVEND_HI 0x03e
+#define ZR060_SGR_BVEND_LO 0x03f
+#define ZR060_SGR_BHEND_HI 0x040
+#define ZR060_SGR_BHEND_LO 0x041
+#define ZR060_AAR_VSTART_HI 0x042
+#define ZR060_AAR_VSTART_LO 0x043
+#define ZR060_AAR_VEND_HI 0x044
+#define ZR060_AAR_VEND_LO 0x045
+#define ZR060_AAR_HSTART_HI 0x046
+#define ZR060_AAR_HSTART_LO 0x047
+#define ZR060_AAR_HEND_HI 0x048
+#define ZR060_AAR_HEND_LO 0x049
+#define ZR060_SWR_VSTART_HI 0x04a
+#define ZR060_SWR_VSTART_LO 0x04b
+#define ZR060_SWR_VEND_HI 0x04c
+#define ZR060_SWR_VEND_LO 0x04d
+#define ZR060_SWR_HSTART_HI 0x04e
+#define ZR060_SWR_HSTART_LO 0x04f
+#define ZR060_SWR_HEND_HI 0x050
+#define ZR060_SWR_HEND_LO 0x051
+
+#define ZR060_SOF_IDX 0x060
+#define ZR060_SOS_IDX 0x07a
+#define ZR060_DRI_IDX 0x0c0
+#define ZR060_DQT_IDX 0x0cc
+#define ZR060_DHT_IDX 0x1d4
+#define ZR060_APP_IDX 0x380
+#define ZR060_COM_IDX 0x3c0
+
+/* ZR36060 LOAD register bits */
+
+#define ZR060_LOAD_Load (1 << 7)
+#define ZR060_LOAD_SyncRst (1 << 0)
+
+/* ZR36060 Code FIFO Status register bits */
+
+#define ZR060_CFSR_Busy (1 << 7)
+#define ZR060_CFSR_CBusy (1 << 2)
+#define ZR060_CFSR_CFIFO (3 << 0)
+
+/* ZR36060 Code Interface register */
+
+#define ZR060_CIR_Code16 (1 << 7)
+#define ZR060_CIR_Endian (1 << 6)
+#define ZR060_CIR_CFIS (1 << 2)
+#define ZR060_CIR_CodeMstr (1 << 0)
+
+/* ZR36060 Codec Mode register */
+
+#define ZR060_CMR_Comp (1 << 7)
+#define ZR060_CMR_ATP (1 << 6)
+#define ZR060_CMR_Pass2 (1 << 5)
+#define ZR060_CMR_TLM (1 << 4)
+#define ZR060_CMR_BRB (1 << 2)
+#define ZR060_CMR_FSF (1 << 1)
+
+/* ZR36060 Markers Enable register */
+
+#define ZR060_MER_App (1 << 7)
+#define ZR060_MER_Com (1 << 6)
+#define ZR060_MER_DRI (1 << 5)
+#define ZR060_MER_DQT (1 << 4)
+#define ZR060_MER_DHT (1 << 3)
+
+/* ZR36060 Interrupt Mask register */
+
+#define ZR060_IMR_EOAV (1 << 3)
+#define ZR060_IMR_EOI (1 << 2)
+#define ZR060_IMR_End (1 << 1)
+#define ZR060_IMR_DataErr (1 << 0)
+
+/* ZR36060 Interrupt Status register */
+
+#define ZR060_ISR_ProCnt (3 << 6)
+#define ZR060_ISR_EOAV (1 << 3)
+#define ZR060_ISR_EOI (1 << 2)
+#define ZR060_ISR_End (1 << 1)
+#define ZR060_ISR_DataErr (1 << 0)
+
+/* ZR36060 Video Control register */
+
+#define ZR060_VCR_Video8 (1 << 7)
+#define ZR060_VCR_Range (1 << 6)
+#define ZR060_VCR_FIDet (1 << 3)
+#define ZR060_VCR_FIVedge (1 << 2)
+#define ZR060_VCR_FIExt (1 << 1)
+#define ZR060_VCR_SyncMstr (1 << 0)
+
+/* ZR36060 Video Polarity register */
+
+#define ZR060_VPR_VCLKPol (1 << 7)
+#define ZR060_VPR_PValPol (1 << 6)
+#define ZR060_VPR_PoePol (1 << 5)
+#define ZR060_VPR_SImgPol (1 << 4)
+#define ZR060_VPR_BLPol (1 << 3)
+#define ZR060_VPR_FIPol (1 << 2)
+#define ZR060_VPR_HSPol (1 << 1)
+#define ZR060_VPR_VSPol (1 << 0)
+
+/* ZR36060 Scaling register */
+
+#define ZR060_SR_VScale (1 << 2)
+#define ZR060_SR_HScale2 (1 << 0)
+#define ZR060_SR_HScale4 (2 << 0)
+
+#endif /*fndef ZR36060_H */
diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c
new file mode 100644
index 00000000..cd2e39fc
--- /dev/null
+++ b/drivers/media/video/zr364xx.c
@@ -0,0 +1,1703 @@
+/*
+ * Zoran 364xx based USB webcam module version 0.73
+ *
+ * Allows you to use your USB webcam with V4L2 applications
+ * This is still in heavy developpement !
+ *
+ * Copyright (C) 2004 Antoine Jacquet <royale@zerezo.com>
+ * http://royale.zerezo.com/zr364xx/
+ *
+ * Heavily inspired by usb-skeleton.c, vicam.c, cpia.c and spca50x.c drivers
+ * V4L2 version inspired by meye.c driver
+ *
+ * Some video buffer code by Lamarque based on s2255drv.c and vivi.c drivers.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/highmem.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf-vmalloc.h>
+
+
+/* Version Information */
+#define DRIVER_VERSION "0.7.4"
+#define DRIVER_AUTHOR "Antoine Jacquet, http://royale.zerezo.com/"
+#define DRIVER_DESC "Zoran 364xx"
+
+
+/* Camera */
+#define FRAMES 1
+#define MAX_FRAME_SIZE 200000
+#define BUFFER_SIZE 0x1000
+#define CTRL_TIMEOUT 500
+
+#define ZR364XX_DEF_BUFS 4
+#define ZR364XX_READ_IDLE 0
+#define ZR364XX_READ_FRAME 1
+
+/* Debug macro */
+#define DBG(fmt, args...) \
+ do { \
+ if (debug) { \
+ printk(KERN_INFO KBUILD_MODNAME " " fmt, ##args); \
+ } \
+ } while (0)
+
+/*#define FULL_DEBUG 1*/
+#ifdef FULL_DEBUG
+#define _DBG DBG
+#else
+#define _DBG(fmt, args...)
+#endif
+
+/* Init methods, need to find nicer names for these
+ * the exact names of the chipsets would be the best if someone finds it */
+#define METHOD0 0
+#define METHOD1 1
+#define METHOD2 2
+#define METHOD3 3
+
+
+/* Module parameters */
+static int debug;
+static int mode;
+
+
+/* Module parameters interface */
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level");
+module_param(mode, int, 0644);
+MODULE_PARM_DESC(mode, "0 = 320x240, 1 = 160x120, 2 = 640x480");
+
+
+/* Devices supported by this driver
+ * .driver_info contains the init method used by the camera */
+static struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x08ca, 0x0109), .driver_info = METHOD0 },
+ {USB_DEVICE(0x041e, 0x4024), .driver_info = METHOD0 },
+ {USB_DEVICE(0x0d64, 0x0108), .driver_info = METHOD0 },
+ {USB_DEVICE(0x0546, 0x3187), .driver_info = METHOD0 },
+ {USB_DEVICE(0x0d64, 0x3108), .driver_info = METHOD0 },
+ {USB_DEVICE(0x0595, 0x4343), .driver_info = METHOD0 },
+ {USB_DEVICE(0x0bb0, 0x500d), .driver_info = METHOD0 },
+ {USB_DEVICE(0x0feb, 0x2004), .driver_info = METHOD0 },
+ {USB_DEVICE(0x055f, 0xb500), .driver_info = METHOD0 },
+ {USB_DEVICE(0x08ca, 0x2062), .driver_info = METHOD2 },
+ {USB_DEVICE(0x052b, 0x1a18), .driver_info = METHOD1 },
+ {USB_DEVICE(0x04c8, 0x0729), .driver_info = METHOD0 },
+ {USB_DEVICE(0x04f2, 0xa208), .driver_info = METHOD0 },
+ {USB_DEVICE(0x0784, 0x0040), .driver_info = METHOD1 },
+ {USB_DEVICE(0x06d6, 0x0034), .driver_info = METHOD0 },
+ {USB_DEVICE(0x0a17, 0x0062), .driver_info = METHOD2 },
+ {USB_DEVICE(0x06d6, 0x003b), .driver_info = METHOD0 },
+ {USB_DEVICE(0x0a17, 0x004e), .driver_info = METHOD2 },
+ {USB_DEVICE(0x041e, 0x405d), .driver_info = METHOD2 },
+ {USB_DEVICE(0x08ca, 0x2102), .driver_info = METHOD3 },
+ {USB_DEVICE(0x06d6, 0x003d), .driver_info = METHOD0 },
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+struct zr364xx_mode {
+ u32 color; /* output video color format */
+ u32 brightness; /* brightness */
+};
+
+/* frame structure */
+struct zr364xx_framei {
+ unsigned long ulState; /* ulState:ZR364XX_READ_IDLE,
+ ZR364XX_READ_FRAME */
+ void *lpvbits; /* image data */
+ unsigned long cur_size; /* current data copied to it */
+};
+
+/* image buffer structure */
+struct zr364xx_bufferi {
+ unsigned long dwFrames; /* number of frames in buffer */
+ struct zr364xx_framei frame[FRAMES]; /* array of FRAME structures */
+};
+
+struct zr364xx_dmaqueue {
+ struct list_head active;
+ struct zr364xx_camera *cam;
+};
+
+struct zr364xx_pipeinfo {
+ u32 transfer_size;
+ u8 *transfer_buffer;
+ u32 state;
+ void *stream_urb;
+ void *cam; /* back pointer to zr364xx_camera struct */
+ u32 err_count;
+ u32 idx;
+};
+
+struct zr364xx_fmt {
+ char *name;
+ u32 fourcc;
+ int depth;
+};
+
+/* image formats. */
+static const struct zr364xx_fmt formats[] = {
+ {
+ .name = "JPG",
+ .fourcc = V4L2_PIX_FMT_JPEG,
+ .depth = 24
+ }
+};
+
+/* Camera stuff */
+struct zr364xx_camera {
+ struct usb_device *udev; /* save off the usb device pointer */
+ struct usb_interface *interface;/* the interface for this device */
+ struct video_device *vdev; /* v4l video device */
+ int nb;
+ struct zr364xx_bufferi buffer;
+ int skip;
+ int width;
+ int height;
+ int method;
+ struct mutex lock;
+ struct mutex open_lock;
+ int users;
+
+ spinlock_t slock;
+ struct zr364xx_dmaqueue vidq;
+ int resources;
+ int last_frame;
+ int cur_frame;
+ unsigned long frame_count;
+ int b_acquire;
+ struct zr364xx_pipeinfo pipe[1];
+
+ u8 read_endpoint;
+
+ const struct zr364xx_fmt *fmt;
+ struct videobuf_queue vb_vidq;
+ enum v4l2_buf_type type;
+ struct zr364xx_mode mode;
+};
+
+/* buffer for one video frame */
+struct zr364xx_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+ const struct zr364xx_fmt *fmt;
+};
+
+/* function used to send initialisation commands to the camera */
+static int send_control_msg(struct usb_device *udev, u8 request, u16 value,
+ u16 index, unsigned char *cp, u16 size)
+{
+ int status;
+
+ unsigned char *transfer_buffer = kmalloc(size, GFP_KERNEL);
+ if (!transfer_buffer) {
+ dev_err(&udev->dev, "kmalloc(%d) failed\n", size);
+ return -ENOMEM;
+ }
+
+ memcpy(transfer_buffer, cp, size);
+
+ status = usb_control_msg(udev,
+ usb_sndctrlpipe(udev, 0),
+ request,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE, value, index,
+ transfer_buffer, size, CTRL_TIMEOUT);
+
+ kfree(transfer_buffer);
+
+ if (status < 0)
+ dev_err(&udev->dev,
+ "Failed sending control message, error %d.\n", status);
+
+ return status;
+}
+
+
+/* Control messages sent to the camera to initialize it
+ * and launch the capture */
+typedef struct {
+ unsigned int value;
+ unsigned int size;
+ unsigned char *bytes;
+} message;
+
+/* method 0 */
+static unsigned char m0d1[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+static unsigned char m0d2[] = { 0, 0, 0, 0, 0, 0 };
+static unsigned char m0d3[] = { 0, 0 };
+static message m0[] = {
+ {0x1f30, 0, NULL},
+ {0xd000, 0, NULL},
+ {0x3370, sizeof(m0d1), m0d1},
+ {0x2000, 0, NULL},
+ {0x2f0f, 0, NULL},
+ {0x2610, sizeof(m0d2), m0d2},
+ {0xe107, 0, NULL},
+ {0x2502, 0, NULL},
+ {0x1f70, 0, NULL},
+ {0xd000, 0, NULL},
+ {0x9a01, sizeof(m0d3), m0d3},
+ {-1, -1, NULL}
+};
+
+/* method 1 */
+static unsigned char m1d1[] = { 0xff, 0xff };
+static unsigned char m1d2[] = { 0x00, 0x00 };
+static message m1[] = {
+ {0x1f30, 0, NULL},
+ {0xd000, 0, NULL},
+ {0xf000, 0, NULL},
+ {0x2000, 0, NULL},
+ {0x2f0f, 0, NULL},
+ {0x2650, 0, NULL},
+ {0xe107, 0, NULL},
+ {0x2502, sizeof(m1d1), m1d1},
+ {0x1f70, 0, NULL},
+ {0xd000, 0, NULL},
+ {0xd000, 0, NULL},
+ {0xd000, 0, NULL},
+ {0x9a01, sizeof(m1d2), m1d2},
+ {-1, -1, NULL}
+};
+
+/* method 2 */
+static unsigned char m2d1[] = { 0xff, 0xff };
+static message m2[] = {
+ {0x1f30, 0, NULL},
+ {0xf000, 0, NULL},
+ {0x2000, 0, NULL},
+ {0x2f0f, 0, NULL},
+ {0x2650, 0, NULL},
+ {0xe107, 0, NULL},
+ {0x2502, sizeof(m2d1), m2d1},
+ {0x1f70, 0, NULL},
+ {-1, -1, NULL}
+};
+
+/* init table */
+static message *init[4] = { m0, m1, m2, m2 };
+
+
+/* JPEG static data in header (Huffman table, etc) */
+static unsigned char header1[] = {
+ 0xFF, 0xD8,
+ /*
+ 0xFF, 0xE0, 0x00, 0x10, 'J', 'F', 'I', 'F',
+ 0x00, 0x01, 0x01, 0x00, 0x33, 0x8A, 0x00, 0x00, 0x33, 0x88,
+ */
+ 0xFF, 0xDB, 0x00, 0x84
+};
+static unsigned char header2[] = {
+ 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
+ 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02,
+ 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01,
+ 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06,
+ 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1,
+ 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33,
+ 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25,
+ 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54,
+ 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A,
+ 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94,
+ 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6,
+ 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8,
+ 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA,
+ 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2,
+ 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3,
+ 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xC4, 0x00, 0x1F,
+ 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5,
+ 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05,
+ 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11,
+ 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
+ 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1,
+ 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16,
+ 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27,
+ 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44,
+ 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A,
+ 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84,
+ 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96,
+ 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8,
+ 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA,
+ 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3,
+ 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5,
+ 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
+ 0xF8, 0xF9, 0xFA, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0xF0, 0x01,
+ 0x40, 0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01,
+ 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11,
+ 0x00, 0x3F, 0x00
+};
+static unsigned char header3;
+
+/* ------------------------------------------------------------------
+ Videobuf operations
+ ------------------------------------------------------------------*/
+
+static int buffer_setup(struct videobuf_queue *vq, unsigned int *count,
+ unsigned int *size)
+{
+ struct zr364xx_camera *cam = vq->priv_data;
+
+ *size = cam->width * cam->height * (cam->fmt->depth >> 3);
+
+ if (*count == 0)
+ *count = ZR364XX_DEF_BUFS;
+
+ if (*size * *count > ZR364XX_DEF_BUFS * 1024 * 1024)
+ *count = (ZR364XX_DEF_BUFS * 1024 * 1024) / *size;
+
+ return 0;
+}
+
+static void free_buffer(struct videobuf_queue *vq, struct zr364xx_buffer *buf)
+{
+ _DBG("%s\n", __func__);
+
+ if (in_interrupt())
+ BUG();
+
+ videobuf_vmalloc_free(&buf->vb);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct zr364xx_camera *cam = vq->priv_data;
+ struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer,
+ vb);
+ int rc;
+
+ DBG("%s, field=%d, fmt name = %s\n", __func__, field, cam->fmt != NULL ?
+ cam->fmt->name : "");
+ if (cam->fmt == NULL)
+ return -EINVAL;
+
+ buf->vb.size = cam->width * cam->height * (cam->fmt->depth >> 3);
+
+ if (buf->vb.baddr != 0 && buf->vb.bsize < buf->vb.size) {
+ DBG("invalid buffer prepare\n");
+ return -EINVAL;
+ }
+
+ buf->fmt = cam->fmt;
+ buf->vb.width = cam->width;
+ buf->vb.height = cam->height;
+ buf->vb.field = field;
+
+ if (buf->vb.state == VIDEOBUF_NEEDS_INIT) {
+ rc = videobuf_iolock(vq, &buf->vb, NULL);
+ if (rc < 0)
+ goto fail;
+ }
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+fail:
+ free_buffer(vq, buf);
+ return rc;
+}
+
+static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer,
+ vb);
+ struct zr364xx_camera *cam = vq->priv_data;
+
+ _DBG("%s\n", __func__);
+
+ buf->vb.state = VIDEOBUF_QUEUED;
+ list_add_tail(&buf->vb.queue, &cam->vidq.active);
+}
+
+static void buffer_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer,
+ vb);
+
+ _DBG("%s\n", __func__);
+ free_buffer(vq, buf);
+}
+
+static struct videobuf_queue_ops zr364xx_video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/********************/
+/* V4L2 integration */
+/********************/
+static int zr364xx_vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type);
+
+static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t count,
+ loff_t * ppos)
+{
+ struct zr364xx_camera *cam = video_drvdata(file);
+
+ _DBG("%s\n", __func__);
+
+ if (!buf)
+ return -EINVAL;
+
+ if (!count)
+ return -EINVAL;
+
+ if (cam->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ zr364xx_vidioc_streamon(file, cam, cam->type) == 0) {
+ DBG("%s: reading %d bytes at pos %d.\n", __func__, (int) count,
+ (int) *ppos);
+
+ /* NoMan Sux ! */
+ return videobuf_read_one(&cam->vb_vidq, buf, count, ppos,
+ file->f_flags & O_NONBLOCK);
+ }
+
+ return 0;
+}
+
+/* video buffer vmalloc implementation based partly on VIVI driver which is
+ * Copyright (c) 2006 by
+ * Mauro Carvalho Chehab <mchehab--a.t--infradead.org>
+ * Ted Walther <ted--a.t--enumera.com>
+ * John Sokol <sokol--a.t--videotechnology.com>
+ * http://v4l.videotechnology.com/
+ *
+ */
+static void zr364xx_fillbuff(struct zr364xx_camera *cam,
+ struct zr364xx_buffer *buf,
+ int jpgsize)
+{
+ int pos = 0;
+ struct timeval ts;
+ const char *tmpbuf;
+ char *vbuf = videobuf_to_vmalloc(&buf->vb);
+ unsigned long last_frame;
+ struct zr364xx_framei *frm;
+
+ if (!vbuf)
+ return;
+
+ last_frame = cam->last_frame;
+ if (last_frame != -1) {
+ frm = &cam->buffer.frame[last_frame];
+ tmpbuf = (const char *)cam->buffer.frame[last_frame].lpvbits;
+ switch (buf->fmt->fourcc) {
+ case V4L2_PIX_FMT_JPEG:
+ buf->vb.size = jpgsize;
+ memcpy(vbuf, tmpbuf, buf->vb.size);
+ break;
+ default:
+ printk(KERN_DEBUG KBUILD_MODNAME ": unknown format?\n");
+ }
+ cam->last_frame = -1;
+ } else {
+ printk(KERN_ERR KBUILD_MODNAME ": =======no frame\n");
+ return;
+ }
+ DBG("%s: Buffer 0x%08lx size= %d\n", __func__,
+ (unsigned long)vbuf, pos);
+ /* tell v4l buffer was filled */
+
+ buf->vb.field_count = cam->frame_count * 2;
+ do_gettimeofday(&ts);
+ buf->vb.ts = ts;
+ buf->vb.state = VIDEOBUF_DONE;
+}
+
+static int zr364xx_got_frame(struct zr364xx_camera *cam, int jpgsize)
+{
+ struct zr364xx_dmaqueue *dma_q = &cam->vidq;
+ struct zr364xx_buffer *buf;
+ unsigned long flags = 0;
+ int rc = 0;
+
+ DBG("wakeup: %p\n", &dma_q);
+ spin_lock_irqsave(&cam->slock, flags);
+
+ if (list_empty(&dma_q->active)) {
+ DBG("No active queue to serve\n");
+ rc = -1;
+ goto unlock;
+ }
+ buf = list_entry(dma_q->active.next,
+ struct zr364xx_buffer, vb.queue);
+
+ if (!waitqueue_active(&buf->vb.done)) {
+ /* no one active */
+ rc = -1;
+ goto unlock;
+ }
+ list_del(&buf->vb.queue);
+ do_gettimeofday(&buf->vb.ts);
+ DBG("[%p/%d] wakeup\n", buf, buf->vb.i);
+ zr364xx_fillbuff(cam, buf, jpgsize);
+ wake_up(&buf->vb.done);
+ DBG("wakeup [buf/i] [%p/%d]\n", buf, buf->vb.i);
+unlock:
+ spin_unlock_irqrestore(&cam->slock, flags);
+ return rc;
+}
+
+/* this function moves the usb stream read pipe data
+ * into the system buffers.
+ * returns 0 on success, EAGAIN if more data to process (call this
+ * function again).
+ */
+static int zr364xx_read_video_callback(struct zr364xx_camera *cam,
+ struct zr364xx_pipeinfo *pipe_info,
+ struct urb *purb)
+{
+ unsigned char *pdest;
+ unsigned char *psrc;
+ s32 idx = -1;
+ struct zr364xx_framei *frm;
+ int i = 0;
+ unsigned char *ptr = NULL;
+
+ _DBG("buffer to user\n");
+ idx = cam->cur_frame;
+ frm = &cam->buffer.frame[idx];
+
+ /* swap bytes if camera needs it */
+ if (cam->method == METHOD0) {
+ u16 *buf = (u16 *)pipe_info->transfer_buffer;
+ for (i = 0; i < purb->actual_length/2; i++)
+ swab16s(buf + i);
+ }
+
+ /* search done. now find out if should be acquiring */
+ if (!cam->b_acquire) {
+ /* we found a frame, but this channel is turned off */
+ frm->ulState = ZR364XX_READ_IDLE;
+ return -EINVAL;
+ }
+
+ psrc = (u8 *)pipe_info->transfer_buffer;
+ ptr = pdest = frm->lpvbits;
+
+ if (frm->ulState == ZR364XX_READ_IDLE) {
+ frm->ulState = ZR364XX_READ_FRAME;
+ frm->cur_size = 0;
+
+ _DBG("jpeg header, ");
+ memcpy(ptr, header1, sizeof(header1));
+ ptr += sizeof(header1);
+ header3 = 0;
+ memcpy(ptr, &header3, 1);
+ ptr++;
+ memcpy(ptr, psrc, 64);
+ ptr += 64;
+ header3 = 1;
+ memcpy(ptr, &header3, 1);
+ ptr++;
+ memcpy(ptr, psrc + 64, 64);
+ ptr += 64;
+ memcpy(ptr, header2, sizeof(header2));
+ ptr += sizeof(header2);
+ memcpy(ptr, psrc + 128,
+ purb->actual_length - 128);
+ ptr += purb->actual_length - 128;
+ _DBG("header : %d %d %d %d %d %d %d %d %d\n",
+ psrc[0], psrc[1], psrc[2],
+ psrc[3], psrc[4], psrc[5],
+ psrc[6], psrc[7], psrc[8]);
+ frm->cur_size = ptr - pdest;
+ } else {
+ if (frm->cur_size + purb->actual_length > MAX_FRAME_SIZE) {
+ dev_info(&cam->udev->dev,
+ "%s: buffer (%d bytes) too small to hold "
+ "frame data. Discarding frame data.\n",
+ __func__, MAX_FRAME_SIZE);
+ } else {
+ pdest += frm->cur_size;
+ memcpy(pdest, psrc, purb->actual_length);
+ frm->cur_size += purb->actual_length;
+ }
+ }
+ /*_DBG("cur_size %lu urb size %d\n", frm->cur_size,
+ purb->actual_length);*/
+
+ if (purb->actual_length < pipe_info->transfer_size) {
+ _DBG("****************Buffer[%d]full*************\n", idx);
+ cam->last_frame = cam->cur_frame;
+ cam->cur_frame++;
+ /* end of system frame ring buffer, start at zero */
+ if (cam->cur_frame == cam->buffer.dwFrames)
+ cam->cur_frame = 0;
+
+ /* frame ready */
+ /* go back to find the JPEG EOI marker */
+ ptr = pdest = frm->lpvbits;
+ ptr += frm->cur_size - 2;
+ while (ptr > pdest) {
+ if (*ptr == 0xFF && *(ptr + 1) == 0xD9
+ && *(ptr + 2) == 0xFF)
+ break;
+ ptr--;
+ }
+ if (ptr == pdest)
+ DBG("No EOI marker\n");
+
+ /* Sometimes there is junk data in the middle of the picture,
+ * we want to skip this bogus frames */
+ while (ptr > pdest) {
+ if (*ptr == 0xFF && *(ptr + 1) == 0xFF
+ && *(ptr + 2) == 0xFF)
+ break;
+ ptr--;
+ }
+ if (ptr != pdest) {
+ DBG("Bogus frame ? %d\n", ++(cam->nb));
+ } else if (cam->b_acquire) {
+ /* we skip the 2 first frames which are usually buggy */
+ if (cam->skip)
+ cam->skip--;
+ else {
+ _DBG("jpeg(%lu): %d %d %d %d %d %d %d %d\n",
+ frm->cur_size,
+ pdest[0], pdest[1], pdest[2], pdest[3],
+ pdest[4], pdest[5], pdest[6], pdest[7]);
+
+ zr364xx_got_frame(cam, frm->cur_size);
+ }
+ }
+ cam->frame_count++;
+ frm->ulState = ZR364XX_READ_IDLE;
+ frm->cur_size = 0;
+ }
+ /* done successfully */
+ return 0;
+}
+
+static int res_get(struct zr364xx_camera *cam)
+{
+ /* is it free? */
+ mutex_lock(&cam->lock);
+ if (cam->resources) {
+ /* no, someone else uses it */
+ mutex_unlock(&cam->lock);
+ return 0;
+ }
+ /* it's free, grab it */
+ cam->resources = 1;
+ _DBG("res: get\n");
+ mutex_unlock(&cam->lock);
+ return 1;
+}
+
+static inline int res_check(struct zr364xx_camera *cam)
+{
+ return cam->resources;
+}
+
+static void res_free(struct zr364xx_camera *cam)
+{
+ mutex_lock(&cam->lock);
+ cam->resources = 0;
+ mutex_unlock(&cam->lock);
+ _DBG("res: put\n");
+}
+
+static int zr364xx_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct zr364xx_camera *cam = video_drvdata(file);
+
+ strlcpy(cap->driver, DRIVER_DESC, sizeof(cap->driver));
+ strlcpy(cap->card, cam->udev->product, sizeof(cap->card));
+ strlcpy(cap->bus_info, dev_name(&cam->udev->dev),
+ sizeof(cap->bus_info));
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+static int zr364xx_vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
+ strcpy(i->name, DRIVER_DESC " Camera");
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ return 0;
+}
+
+static int zr364xx_vidioc_g_input(struct file *file, void *priv,
+ unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int zr364xx_vidioc_s_input(struct file *file, void *priv,
+ unsigned int i)
+{
+ if (i != 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int zr364xx_vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ struct zr364xx_camera *cam;
+
+ if (file == NULL)
+ return -ENODEV;
+ cam = video_drvdata(file);
+
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ c->type = V4L2_CTRL_TYPE_INTEGER;
+ strcpy(c->name, "Brightness");
+ c->minimum = 0;
+ c->maximum = 127;
+ c->step = 1;
+ c->default_value = cam->mode.brightness;
+ c->flags = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int zr364xx_vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *c)
+{
+ struct zr364xx_camera *cam;
+ int temp;
+
+ if (file == NULL)
+ return -ENODEV;
+ cam = video_drvdata(file);
+
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ cam->mode.brightness = c->value;
+ /* hardware brightness */
+ mutex_lock(&cam->lock);
+ send_control_msg(cam->udev, 1, 0x2001, 0, NULL, 0);
+ temp = (0x60 << 8) + 127 - cam->mode.brightness;
+ send_control_msg(cam->udev, 1, temp, 0, NULL, 0);
+ mutex_unlock(&cam->lock);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int zr364xx_vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *c)
+{
+ struct zr364xx_camera *cam;
+
+ if (file == NULL)
+ return -ENODEV;
+ cam = video_drvdata(file);
+
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ c->value = cam->mode.brightness;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int zr364xx_vidioc_enum_fmt_vid_cap(struct file *file,
+ void *priv, struct v4l2_fmtdesc *f)
+{
+ if (f->index > 0)
+ return -EINVAL;
+ f->flags = V4L2_FMT_FLAG_COMPRESSED;
+ strcpy(f->description, formats[0].name);
+ f->pixelformat = formats[0].fourcc;
+ return 0;
+}
+
+static char *decode_fourcc(__u32 pixelformat, char *buf)
+{
+ buf[0] = pixelformat & 0xff;
+ buf[1] = (pixelformat >> 8) & 0xff;
+ buf[2] = (pixelformat >> 16) & 0xff;
+ buf[3] = (pixelformat >> 24) & 0xff;
+ buf[4] = '\0';
+ return buf;
+}
+
+static int zr364xx_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct zr364xx_camera *cam = video_drvdata(file);
+ char pixelformat_name[5];
+
+ if (cam == NULL)
+ return -ENODEV;
+
+ if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) {
+ DBG("%s: unsupported pixelformat V4L2_PIX_FMT_%s\n", __func__,
+ decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name));
+ return -EINVAL;
+ }
+
+ if (!(f->fmt.pix.width == 160 && f->fmt.pix.height == 120) &&
+ !(f->fmt.pix.width == 640 && f->fmt.pix.height == 480)) {
+ f->fmt.pix.width = 320;
+ f->fmt.pix.height = 240;
+ }
+
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+ f->fmt.pix.colorspace = 0;
+ f->fmt.pix.priv = 0;
+ DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__,
+ decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name),
+ f->fmt.pix.field);
+ return 0;
+}
+
+static int zr364xx_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct zr364xx_camera *cam;
+
+ if (file == NULL)
+ return -ENODEV;
+ cam = video_drvdata(file);
+
+ f->fmt.pix.pixelformat = formats[0].fourcc;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.width = cam->width;
+ f->fmt.pix.height = cam->height;
+ f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+ f->fmt.pix.colorspace = 0;
+ f->fmt.pix.priv = 0;
+ return 0;
+}
+
+static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct zr364xx_camera *cam = video_drvdata(file);
+ struct videobuf_queue *q = &cam->vb_vidq;
+ char pixelformat_name[5];
+ int ret = zr364xx_vidioc_try_fmt_vid_cap(file, cam, f);
+ int i;
+
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&q->vb_lock);
+
+ if (videobuf_queue_is_busy(&cam->vb_vidq)) {
+ DBG("%s queue busy\n", __func__);
+ ret = -EBUSY;
+ goto out;
+ }
+
+ if (res_check(cam)) {
+ DBG("%s can't change format after started\n", __func__);
+ ret = -EBUSY;
+ goto out;
+ }
+
+ cam->width = f->fmt.pix.width;
+ cam->height = f->fmt.pix.height;
+ dev_info(&cam->udev->dev, "%s: %dx%d mode selected\n", __func__,
+ cam->width, cam->height);
+ f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+ f->fmt.pix.colorspace = 0;
+ f->fmt.pix.priv = 0;
+ cam->vb_vidq.field = f->fmt.pix.field;
+ cam->mode.color = V4L2_PIX_FMT_JPEG;
+
+ if (f->fmt.pix.width == 160 && f->fmt.pix.height == 120)
+ mode = 1;
+ else if (f->fmt.pix.width == 640 && f->fmt.pix.height == 480)
+ mode = 2;
+ else
+ mode = 0;
+
+ m0d1[0] = mode;
+ m1[2].value = 0xf000 + mode;
+ m2[1].value = 0xf000 + mode;
+
+ /* special case for METHOD3, the modes are different */
+ if (cam->method == METHOD3) {
+ switch (mode) {
+ case 1:
+ m2[1].value = 0xf000 + 4;
+ break;
+ case 2:
+ m2[1].value = 0xf000 + 0;
+ break;
+ default:
+ m2[1].value = 0xf000 + 1;
+ break;
+ }
+ }
+
+ header2[437] = cam->height / 256;
+ header2[438] = cam->height % 256;
+ header2[439] = cam->width / 256;
+ header2[440] = cam->width % 256;
+
+ for (i = 0; init[cam->method][i].size != -1; i++) {
+ ret =
+ send_control_msg(cam->udev, 1, init[cam->method][i].value,
+ 0, init[cam->method][i].bytes,
+ init[cam->method][i].size);
+ if (ret < 0) {
+ dev_err(&cam->udev->dev,
+ "error during resolution change sequence: %d\n", i);
+ goto out;
+ }
+ }
+
+ /* Added some delay here, since opening/closing the camera quickly,
+ * like Ekiga does during its startup, can crash the webcam
+ */
+ mdelay(100);
+ cam->skip = 2;
+ ret = 0;
+
+out:
+ mutex_unlock(&q->vb_lock);
+
+ DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__,
+ decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name),
+ f->fmt.pix.field);
+ return ret;
+}
+
+static int zr364xx_vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ int rc;
+ struct zr364xx_camera *cam = video_drvdata(file);
+ rc = videobuf_reqbufs(&cam->vb_vidq, p);
+ return rc;
+}
+
+static int zr364xx_vidioc_querybuf(struct file *file,
+ void *priv,
+ struct v4l2_buffer *p)
+{
+ int rc;
+ struct zr364xx_camera *cam = video_drvdata(file);
+ rc = videobuf_querybuf(&cam->vb_vidq, p);
+ return rc;
+}
+
+static int zr364xx_vidioc_qbuf(struct file *file,
+ void *priv,
+ struct v4l2_buffer *p)
+{
+ int rc;
+ struct zr364xx_camera *cam = video_drvdata(file);
+ _DBG("%s\n", __func__);
+ rc = videobuf_qbuf(&cam->vb_vidq, p);
+ return rc;
+}
+
+static int zr364xx_vidioc_dqbuf(struct file *file,
+ void *priv,
+ struct v4l2_buffer *p)
+{
+ int rc;
+ struct zr364xx_camera *cam = video_drvdata(file);
+ _DBG("%s\n", __func__);
+ rc = videobuf_dqbuf(&cam->vb_vidq, p, file->f_flags & O_NONBLOCK);
+ return rc;
+}
+
+static void read_pipe_completion(struct urb *purb)
+{
+ struct zr364xx_pipeinfo *pipe_info;
+ struct zr364xx_camera *cam;
+ int pipe;
+
+ pipe_info = purb->context;
+ _DBG("%s %p, status %d\n", __func__, purb, purb->status);
+ if (pipe_info == NULL) {
+ printk(KERN_ERR KBUILD_MODNAME ": no context!\n");
+ return;
+ }
+
+ cam = pipe_info->cam;
+ if (cam == NULL) {
+ printk(KERN_ERR KBUILD_MODNAME ": no context!\n");
+ return;
+ }
+
+ /* if shutting down, do not resubmit, exit immediately */
+ if (purb->status == -ESHUTDOWN) {
+ DBG("%s, err shutdown\n", __func__);
+ pipe_info->err_count++;
+ return;
+ }
+
+ if (pipe_info->state == 0) {
+ DBG("exiting USB pipe\n");
+ return;
+ }
+
+ if (purb->actual_length < 0 ||
+ purb->actual_length > pipe_info->transfer_size) {
+ dev_err(&cam->udev->dev, "wrong number of bytes\n");
+ return;
+ }
+
+ if (purb->status == 0)
+ zr364xx_read_video_callback(cam, pipe_info, purb);
+ else {
+ pipe_info->err_count++;
+ DBG("%s: failed URB %d\n", __func__, purb->status);
+ }
+
+ pipe = usb_rcvbulkpipe(cam->udev, cam->read_endpoint);
+
+ /* reuse urb */
+ usb_fill_bulk_urb(pipe_info->stream_urb, cam->udev,
+ pipe,
+ pipe_info->transfer_buffer,
+ pipe_info->transfer_size,
+ read_pipe_completion, pipe_info);
+
+ if (pipe_info->state != 0) {
+ purb->status = usb_submit_urb(pipe_info->stream_urb,
+ GFP_ATOMIC);
+
+ if (purb->status)
+ dev_err(&cam->udev->dev,
+ "error submitting urb (error=%i)\n",
+ purb->status);
+ } else
+ DBG("read pipe complete state 0\n");
+}
+
+static int zr364xx_start_readpipe(struct zr364xx_camera *cam)
+{
+ int pipe;
+ int retval;
+ struct zr364xx_pipeinfo *pipe_info = cam->pipe;
+ pipe = usb_rcvbulkpipe(cam->udev, cam->read_endpoint);
+ DBG("%s: start pipe IN x%x\n", __func__, cam->read_endpoint);
+
+ pipe_info->state = 1;
+ pipe_info->err_count = 0;
+ pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!pipe_info->stream_urb) {
+ dev_err(&cam->udev->dev, "ReadStream: Unable to alloc URB\n");
+ return -ENOMEM;
+ }
+ /* transfer buffer allocated in board_init */
+ usb_fill_bulk_urb(pipe_info->stream_urb, cam->udev,
+ pipe,
+ pipe_info->transfer_buffer,
+ pipe_info->transfer_size,
+ read_pipe_completion, pipe_info);
+
+ DBG("submitting URB %p\n", pipe_info->stream_urb);
+ retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL);
+ if (retval) {
+ printk(KERN_ERR KBUILD_MODNAME ": start read pipe failed\n");
+ return retval;
+ }
+
+ return 0;
+}
+
+static void zr364xx_stop_readpipe(struct zr364xx_camera *cam)
+{
+ struct zr364xx_pipeinfo *pipe_info;
+
+ if (cam == NULL) {
+ printk(KERN_ERR KBUILD_MODNAME ": invalid device\n");
+ return;
+ }
+ DBG("stop read pipe\n");
+ pipe_info = cam->pipe;
+ if (pipe_info) {
+ if (pipe_info->state != 0)
+ pipe_info->state = 0;
+
+ if (pipe_info->stream_urb) {
+ /* cancel urb */
+ usb_kill_urb(pipe_info->stream_urb);
+ usb_free_urb(pipe_info->stream_urb);
+ pipe_info->stream_urb = NULL;
+ }
+ }
+ return;
+}
+
+/* starts acquisition process */
+static int zr364xx_start_acquire(struct zr364xx_camera *cam)
+{
+ int j;
+
+ DBG("start acquire\n");
+
+ cam->last_frame = -1;
+ cam->cur_frame = 0;
+ for (j = 0; j < FRAMES; j++) {
+ cam->buffer.frame[j].ulState = ZR364XX_READ_IDLE;
+ cam->buffer.frame[j].cur_size = 0;
+ }
+ cam->b_acquire = 1;
+ return 0;
+}
+
+static inline int zr364xx_stop_acquire(struct zr364xx_camera *cam)
+{
+ cam->b_acquire = 0;
+ return 0;
+}
+
+static int zr364xx_vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct zr364xx_camera *cam = video_drvdata(file);
+ int j;
+ int res;
+
+ DBG("%s\n", __func__);
+
+ if (cam->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ dev_err(&cam->udev->dev, "invalid fh type0\n");
+ return -EINVAL;
+ }
+ if (cam->type != type) {
+ dev_err(&cam->udev->dev, "invalid fh type1\n");
+ return -EINVAL;
+ }
+
+ if (!res_get(cam)) {
+ dev_err(&cam->udev->dev, "stream busy\n");
+ return -EBUSY;
+ }
+
+ cam->last_frame = -1;
+ cam->cur_frame = 0;
+ cam->frame_count = 0;
+ for (j = 0; j < FRAMES; j++) {
+ cam->buffer.frame[j].ulState = ZR364XX_READ_IDLE;
+ cam->buffer.frame[j].cur_size = 0;
+ }
+ res = videobuf_streamon(&cam->vb_vidq);
+ if (res == 0) {
+ zr364xx_start_acquire(cam);
+ } else {
+ res_free(cam);
+ }
+ return res;
+}
+
+static int zr364xx_vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ int res;
+ struct zr364xx_camera *cam = video_drvdata(file);
+
+ DBG("%s\n", __func__);
+ if (cam->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ dev_err(&cam->udev->dev, "invalid fh type0\n");
+ return -EINVAL;
+ }
+ if (cam->type != type) {
+ dev_err(&cam->udev->dev, "invalid fh type1\n");
+ return -EINVAL;
+ }
+ zr364xx_stop_acquire(cam);
+ res = videobuf_streamoff(&cam->vb_vidq);
+ if (res < 0)
+ return res;
+ res_free(cam);
+ return 0;
+}
+
+
+/* open the camera */
+static int zr364xx_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct zr364xx_camera *cam = video_drvdata(file);
+ struct usb_device *udev = cam->udev;
+ int i, err;
+
+ DBG("%s\n", __func__);
+
+ mutex_lock(&cam->open_lock);
+
+ if (cam->users) {
+ err = -EBUSY;
+ goto out;
+ }
+
+ for (i = 0; init[cam->method][i].size != -1; i++) {
+ err =
+ send_control_msg(udev, 1, init[cam->method][i].value,
+ 0, init[cam->method][i].bytes,
+ init[cam->method][i].size);
+ if (err < 0) {
+ dev_err(&cam->udev->dev,
+ "error during open sequence: %d\n", i);
+ goto out;
+ }
+ }
+
+ cam->skip = 2;
+ cam->users++;
+ file->private_data = vdev;
+ cam->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cam->fmt = formats;
+
+ videobuf_queue_vmalloc_init(&cam->vb_vidq, &zr364xx_video_qops,
+ NULL, &cam->slock,
+ cam->type,
+ V4L2_FIELD_NONE,
+ sizeof(struct zr364xx_buffer), cam, NULL);
+
+ /* Added some delay here, since opening/closing the camera quickly,
+ * like Ekiga does during its startup, can crash the webcam
+ */
+ mdelay(100);
+ err = 0;
+
+out:
+ mutex_unlock(&cam->open_lock);
+ DBG("%s: %d\n", __func__, err);
+ return err;
+}
+
+static void zr364xx_destroy(struct zr364xx_camera *cam)
+{
+ unsigned long i;
+
+ if (!cam) {
+ printk(KERN_ERR KBUILD_MODNAME ", %s: no device\n", __func__);
+ return;
+ }
+ mutex_lock(&cam->open_lock);
+ if (cam->vdev)
+ video_unregister_device(cam->vdev);
+ cam->vdev = NULL;
+
+ /* stops the read pipe if it is running */
+ if (cam->b_acquire)
+ zr364xx_stop_acquire(cam);
+
+ zr364xx_stop_readpipe(cam);
+
+ /* release sys buffers */
+ for (i = 0; i < FRAMES; i++) {
+ if (cam->buffer.frame[i].lpvbits) {
+ DBG("vfree %p\n", cam->buffer.frame[i].lpvbits);
+ vfree(cam->buffer.frame[i].lpvbits);
+ }
+ cam->buffer.frame[i].lpvbits = NULL;
+ }
+
+ /* release transfer buffer */
+ kfree(cam->pipe->transfer_buffer);
+ cam->pipe->transfer_buffer = NULL;
+ mutex_unlock(&cam->open_lock);
+ kfree(cam);
+ cam = NULL;
+}
+
+/* release the camera */
+static int zr364xx_release(struct file *file)
+{
+ struct zr364xx_camera *cam;
+ struct usb_device *udev;
+ int i, err;
+
+ DBG("%s\n", __func__);
+ cam = video_drvdata(file);
+
+ if (!cam)
+ return -ENODEV;
+
+ mutex_lock(&cam->open_lock);
+ udev = cam->udev;
+
+ /* turn off stream */
+ if (res_check(cam)) {
+ if (cam->b_acquire)
+ zr364xx_stop_acquire(cam);
+ videobuf_streamoff(&cam->vb_vidq);
+ res_free(cam);
+ }
+
+ cam->users--;
+ file->private_data = NULL;
+
+ for (i = 0; i < 2; i++) {
+ err =
+ send_control_msg(udev, 1, init[cam->method][i].value,
+ 0, init[cam->method][i].bytes,
+ init[cam->method][i].size);
+ if (err < 0) {
+ dev_err(&udev->dev, "error during release sequence\n");
+ goto out;
+ }
+ }
+
+ /* Added some delay here, since opening/closing the camera quickly,
+ * like Ekiga does during its startup, can crash the webcam
+ */
+ mdelay(100);
+ err = 0;
+
+out:
+ mutex_unlock(&cam->open_lock);
+
+ return err;
+}
+
+
+static int zr364xx_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct zr364xx_camera *cam = video_drvdata(file);
+ int ret;
+
+ if (cam == NULL) {
+ DBG("%s: cam == NULL\n", __func__);
+ return -ENODEV;
+ }
+ DBG("mmap called, vma=0x%08lx\n", (unsigned long)vma);
+
+ ret = videobuf_mmap_mapper(&cam->vb_vidq, vma);
+
+ DBG("vma start=0x%08lx, size=%ld, ret=%d\n",
+ (unsigned long)vma->vm_start,
+ (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret);
+ return ret;
+}
+
+static unsigned int zr364xx_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct zr364xx_camera *cam = video_drvdata(file);
+ struct videobuf_queue *q = &cam->vb_vidq;
+ _DBG("%s\n", __func__);
+
+ if (cam->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return POLLERR;
+
+ return videobuf_poll_stream(file, q, wait);
+}
+
+static const struct v4l2_file_operations zr364xx_fops = {
+ .owner = THIS_MODULE,
+ .open = zr364xx_open,
+ .release = zr364xx_release,
+ .read = zr364xx_read,
+ .mmap = zr364xx_mmap,
+ .ioctl = video_ioctl2,
+ .poll = zr364xx_poll,
+};
+
+static const struct v4l2_ioctl_ops zr364xx_ioctl_ops = {
+ .vidioc_querycap = zr364xx_vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = zr364xx_vidioc_enum_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = zr364xx_vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = zr364xx_vidioc_s_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = zr364xx_vidioc_g_fmt_vid_cap,
+ .vidioc_enum_input = zr364xx_vidioc_enum_input,
+ .vidioc_g_input = zr364xx_vidioc_g_input,
+ .vidioc_s_input = zr364xx_vidioc_s_input,
+ .vidioc_streamon = zr364xx_vidioc_streamon,
+ .vidioc_streamoff = zr364xx_vidioc_streamoff,
+ .vidioc_queryctrl = zr364xx_vidioc_queryctrl,
+ .vidioc_g_ctrl = zr364xx_vidioc_g_ctrl,
+ .vidioc_s_ctrl = zr364xx_vidioc_s_ctrl,
+ .vidioc_reqbufs = zr364xx_vidioc_reqbufs,
+ .vidioc_querybuf = zr364xx_vidioc_querybuf,
+ .vidioc_qbuf = zr364xx_vidioc_qbuf,
+ .vidioc_dqbuf = zr364xx_vidioc_dqbuf,
+};
+
+static struct video_device zr364xx_template = {
+ .name = DRIVER_DESC,
+ .fops = &zr364xx_fops,
+ .ioctl_ops = &zr364xx_ioctl_ops,
+ .release = video_device_release,
+};
+
+
+
+/*******************/
+/* USB integration */
+/*******************/
+static int zr364xx_board_init(struct zr364xx_camera *cam)
+{
+ struct zr364xx_pipeinfo *pipe = cam->pipe;
+ unsigned long i;
+
+ DBG("board init: %p\n", cam);
+ memset(pipe, 0, sizeof(*pipe));
+ pipe->cam = cam;
+ pipe->transfer_size = BUFFER_SIZE;
+
+ pipe->transfer_buffer = kzalloc(pipe->transfer_size,
+ GFP_KERNEL);
+ if (pipe->transfer_buffer == NULL) {
+ DBG("out of memory!\n");
+ return -ENOMEM;
+ }
+
+ cam->b_acquire = 0;
+ cam->frame_count = 0;
+
+ /*** start create system buffers ***/
+ for (i = 0; i < FRAMES; i++) {
+ /* always allocate maximum size for system buffers */
+ cam->buffer.frame[i].lpvbits = vmalloc(MAX_FRAME_SIZE);
+
+ DBG("valloc %p, idx %lu, pdata %p\n",
+ &cam->buffer.frame[i], i,
+ cam->buffer.frame[i].lpvbits);
+ if (cam->buffer.frame[i].lpvbits == NULL) {
+ printk(KERN_INFO KBUILD_MODNAME ": out of memory. "
+ "Using less frames\n");
+ break;
+ }
+ }
+
+ if (i == 0) {
+ printk(KERN_INFO KBUILD_MODNAME ": out of memory. Aborting\n");
+ kfree(cam->pipe->transfer_buffer);
+ cam->pipe->transfer_buffer = NULL;
+ return -ENOMEM;
+ } else
+ cam->buffer.dwFrames = i;
+
+ /* make sure internal states are set */
+ for (i = 0; i < FRAMES; i++) {
+ cam->buffer.frame[i].ulState = ZR364XX_READ_IDLE;
+ cam->buffer.frame[i].cur_size = 0;
+ }
+
+ cam->cur_frame = 0;
+ cam->last_frame = -1;
+ /*** end create system buffers ***/
+
+ /* start read pipe */
+ zr364xx_start_readpipe(cam);
+ DBG(": board initialized\n");
+ return 0;
+}
+
+static int zr364xx_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct zr364xx_camera *cam = NULL;
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int err;
+ int i;
+
+ DBG("probing...\n");
+
+ dev_info(&intf->dev, DRIVER_DESC " compatible webcam plugged\n");
+ dev_info(&intf->dev, "model %04x:%04x detected\n",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct));
+
+ cam = kzalloc(sizeof(struct zr364xx_camera), GFP_KERNEL);
+ if (cam == NULL) {
+ dev_err(&udev->dev, "cam: out of memory !\n");
+ return -ENOMEM;
+ }
+ /* save the init method used by this camera */
+ cam->method = id->driver_info;
+
+ cam->vdev = video_device_alloc();
+ if (cam->vdev == NULL) {
+ dev_err(&udev->dev, "cam->vdev: out of memory !\n");
+ kfree(cam);
+ cam = NULL;
+ return -ENOMEM;
+ }
+ memcpy(cam->vdev, &zr364xx_template, sizeof(zr364xx_template));
+ cam->vdev->parent = &intf->dev;
+ video_set_drvdata(cam->vdev, cam);
+ if (debug)
+ cam->vdev->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
+
+ cam->udev = udev;
+
+ switch (mode) {
+ case 1:
+ dev_info(&udev->dev, "160x120 mode selected\n");
+ cam->width = 160;
+ cam->height = 120;
+ break;
+ case 2:
+ dev_info(&udev->dev, "640x480 mode selected\n");
+ cam->width = 640;
+ cam->height = 480;
+ break;
+ default:
+ dev_info(&udev->dev, "320x240 mode selected\n");
+ cam->width = 320;
+ cam->height = 240;
+ break;
+ }
+
+ m0d1[0] = mode;
+ m1[2].value = 0xf000 + mode;
+ m2[1].value = 0xf000 + mode;
+
+ /* special case for METHOD3, the modes are different */
+ if (cam->method == METHOD3) {
+ switch (mode) {
+ case 1:
+ m2[1].value = 0xf000 + 4;
+ break;
+ case 2:
+ m2[1].value = 0xf000 + 0;
+ break;
+ default:
+ m2[1].value = 0xf000 + 1;
+ break;
+ }
+ }
+
+ header2[437] = cam->height / 256;
+ header2[438] = cam->height % 256;
+ header2[439] = cam->width / 256;
+ header2[440] = cam->width % 256;
+
+ cam->users = 0;
+ cam->nb = 0;
+ cam->mode.brightness = 64;
+ mutex_init(&cam->lock);
+ mutex_init(&cam->open_lock);
+
+ DBG("dev: %p, udev %p interface %p\n", cam, cam->udev, intf);
+
+ /* set up the endpoint information */
+ iface_desc = intf->cur_altsetting;
+ DBG("num endpoints %d\n", iface_desc->desc.bNumEndpoints);
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+ if (!cam->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) {
+ /* we found the bulk in endpoint */
+ cam->read_endpoint = endpoint->bEndpointAddress;
+ }
+ }
+
+ if (!cam->read_endpoint) {
+ dev_err(&intf->dev, "Could not find bulk-in endpoint\n");
+ video_device_release(cam->vdev);
+ kfree(cam);
+ cam = NULL;
+ return -ENOMEM;
+ }
+
+ /* v4l */
+ INIT_LIST_HEAD(&cam->vidq.active);
+ cam->vidq.cam = cam;
+ err = video_register_device(cam->vdev, VFL_TYPE_GRABBER, -1);
+ if (err) {
+ dev_err(&udev->dev, "video_register_device failed\n");
+ video_device_release(cam->vdev);
+ kfree(cam);
+ cam = NULL;
+ return err;
+ }
+
+ usb_set_intfdata(intf, cam);
+
+ /* load zr364xx board specific */
+ err = zr364xx_board_init(cam);
+ if (err) {
+ spin_lock_init(&cam->slock);
+ return err;
+ }
+
+ spin_lock_init(&cam->slock);
+
+ dev_info(&udev->dev, DRIVER_DESC " controlling device %s\n",
+ video_device_node_name(cam->vdev));
+ return 0;
+}
+
+
+static void zr364xx_disconnect(struct usb_interface *intf)
+{
+ struct zr364xx_camera *cam = usb_get_intfdata(intf);
+ videobuf_mmap_free(&cam->vb_vidq);
+ usb_set_intfdata(intf, NULL);
+ dev_info(&intf->dev, DRIVER_DESC " webcam unplugged\n");
+ zr364xx_destroy(cam);
+}
+
+
+
+/**********************/
+/* Module integration */
+/**********************/
+
+static struct usb_driver zr364xx_driver = {
+ .name = "zr364xx",
+ .probe = zr364xx_probe,
+ .disconnect = zr364xx_disconnect,
+ .id_table = device_table
+};
+
+module_usb_driver(zr364xx_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);